xref: /haiku/src/add-ons/accelerants/radeon_hd/pll.cpp (revision 0044a8c39ab5721051b6279506d1a8c511e20453)
1 /*
2  * Copyright 2006-2011, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *	  Alexander von Gluck, kallisti5@unixzen.com
7  */
8 
9 
10 #include "accelerant_protos.h"
11 #include "accelerant.h"
12 #include "bios.h"
13 #include "display.h"
14 #include "utility.h"
15 #include "pll.h"
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <math.h>
21 
22 
23 #define TRACE_PLL
24 #ifdef TRACE_PLL
25 extern "C" void _sPrintf(const char *format, ...);
26 #   define TRACE(x...) _sPrintf("radeon_hd: " x)
27 #else
28 #   define TRACE(x...) ;
29 #endif
30 
31 #define ERROR(x...) _sPrintf("radeon_hd: " x)
32 
33 
34 union firmware_info {
35 	ATOM_FIRMWARE_INFO info;
36 	ATOM_FIRMWARE_INFO_V1_2 info_12;
37 	ATOM_FIRMWARE_INFO_V1_3 info_13;
38 	ATOM_FIRMWARE_INFO_V1_4 info_14;
39 	ATOM_FIRMWARE_INFO_V2_1 info_21;
40 	ATOM_FIRMWARE_INFO_V2_2 info_22;
41 };
42 
43 
44 status_t
45 pll_limit_probe(pll_info *pll)
46 {
47 	int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
48 	uint8 tableMajor;
49 	uint8 tableMinor;
50 	uint16 tableOffset;
51 
52 	if (atom_parse_data_header(gAtomContext, index, NULL,
53 		&tableMajor, &tableMinor, &tableOffset) != B_OK) {
54 		ERROR("%s: Couldn't parse data header\n", __func__);
55 		return B_ERROR;
56 	}
57 
58 	union firmware_info *firmwareInfo
59 		= (union firmware_info *)(gAtomContext->bios + tableOffset);
60 
61 	/* pixel clock limits */
62 	pll->referenceFreq
63 		= B_LENDIAN_TO_HOST_INT16(firmwareInfo->info.usReferenceClock) * 10;
64 
65 	if (tableMinor < 2) {
66 		pll->pllOutMin
67 			= B_LENDIAN_TO_HOST_INT16(
68 				firmwareInfo->info.usMinPixelClockPLL_Output) * 10;
69 	} else {
70 		pll->pllOutMin
71 			= B_LENDIAN_TO_HOST_INT32(
72 				firmwareInfo->info_12.ulMinPixelClockPLL_Output) * 10;
73 	}
74 
75 	pll->pllOutMax
76 		= B_LENDIAN_TO_HOST_INT32(
77 			firmwareInfo->info.ulMaxPixelClockPLL_Output) * 10;
78 
79 	if (tableMinor >= 4) {
80 		pll->lcdPllOutMin
81 			= B_LENDIAN_TO_HOST_INT16(
82 				firmwareInfo->info_14.usLcdMinPixelClockPLL_Output) * 1000;
83 
84 		if (pll->lcdPllOutMin == 0)
85 			pll->lcdPllOutMin = pll->pllOutMin;
86 
87 		pll->lcdPllOutMax
88 			= B_LENDIAN_TO_HOST_INT16(
89 				firmwareInfo->info_14.usLcdMaxPixelClockPLL_Output) * 1000;
90 
91 		if (pll->lcdPllOutMax == 0)
92 			pll->lcdPllOutMax = pll->pllOutMax;
93 
94 	} else {
95 		pll->lcdPllOutMin = pll->pllOutMin;
96 		pll->lcdPllOutMax = pll->pllOutMax;
97 	}
98 
99 	if (pll->pllOutMin == 0) {
100 		pll->pllOutMin = 64800 * 10;
101 			// Avivo+ limit
102 	}
103 
104 	pll->minPostDiv = POST_DIV_MIN;
105 	pll->maxPostDiv = POST_DIV_LIMIT;
106 	pll->minRefDiv = REF_DIV_MIN;
107 	pll->maxRefDiv = REF_DIV_LIMIT;
108 	pll->minFeedbackDiv = FB_DIV_MIN;
109 	pll->maxFeedbackDiv = FB_DIV_LIMIT;
110 
111 	pll->pllInMin = B_LENDIAN_TO_HOST_INT16(
112 		firmwareInfo->info.usMinPixelClockPLL_Input) * 10;
113 	pll->pllInMax = B_LENDIAN_TO_HOST_INT16(
114 		firmwareInfo->info.usMaxPixelClockPLL_Input) * 10;
115 
116 	TRACE("%s: referenceFreq: %" B_PRIu16 "; pllOutMin: %" B_PRIu16 "; "
117 		" pllOutMax: %" B_PRIu16 "; pllInMin: %" B_PRIu16 ";"
118 		"pllInMax: %" B_PRIu16 "\n", __func__, pll->referenceFreq,
119 		pll->pllOutMin, pll->pllOutMax, pll->pllInMin, pll->pllInMax);
120 
121 	return B_OK;
122 }
123 
124 
125 void
126 pll_compute_post_divider(pll_info *pll)
127 {
128 	if ((pll->flags & PLL_USE_POST_DIV) != 0) {
129 		TRACE("%s: using AtomBIOS post divider\n", __func__);
130 		return;
131 	}
132 
133 	uint32 vco;
134 	if ((pll->flags & PLL_PREFER_MINM_OVER_MAXP) != 0) {
135 		if ((pll->flags & PLL_IS_LCD) != 0)
136 			vco = pll->lcdPllOutMin;
137 		else
138 			vco = pll->pllOutMin;
139 	} else {
140 		if ((pll->flags & PLL_IS_LCD) != 0)
141 			vco = pll->lcdPllOutMax;
142 		else
143 			vco = pll->pllOutMin;
144 	}
145 
146 	TRACE("%s: vco = %" B_PRIu32 "\n", __func__, vco);
147 
148 	uint32 postDivider = vco / pll->pixelClock;
149 	uint32 tmp = vco % pll->pixelClock;
150 
151 	if ((pll->flags & PLL_PREFER_MINM_OVER_MAXP) != 0) {
152 		if (tmp)
153 			postDivider++;
154 	} else {
155 		if (!tmp)
156 			postDivider--;
157 	}
158 
159 	if (postDivider > pll->maxPostDiv)
160 		postDivider = pll->maxPostDiv;
161 	else if (postDivider < pll->minPostDiv)
162 		postDivider = pll->minPostDiv;
163 
164 	pll->postDiv = postDivider;
165 	TRACE("%s: postDiv = %" B_PRIu32 "\n", __func__, postDivider);
166 }
167 
168 
169 status_t
170 pll_compute(pll_info *pll)
171 {
172 	pll_compute_post_divider(pll);
173 
174 	uint32 targetClock = pll->pixelClock;
175 
176 	pll->feedbackDiv = 0;
177 	pll->feedbackDivFrac = 0;
178 	uint32 referenceFrequency = pll->referenceFreq;
179 
180 	if ((pll->flags & PLL_USE_REF_DIV) != 0) {
181 		TRACE("%s: using AtomBIOS reference divider\n", __func__);
182 		return B_OK;
183 	} else {
184 		pll->referenceDiv = pll->minRefDiv;
185 	}
186 
187 	if ((pll->flags & PLL_USE_FRAC_FB_DIV) != 0) {
188 		TRACE("%s: using AtomBIOS fractional feedback divider\n", __func__);
189 
190 		uint32 tmp = pll->postDiv * pll->referenceDiv;
191 		tmp *= targetClock;
192 		pll->feedbackDiv = tmp / pll->referenceFreq;
193 		pll->feedbackDivFrac = tmp % pll->referenceFreq;
194 
195 		if (pll->feedbackDiv > pll->maxFeedbackDiv)
196 			pll->feedbackDiv = pll->maxFeedbackDiv;
197 		else if (pll->feedbackDiv < pll->minFeedbackDiv)
198 			pll->feedbackDiv = pll->minFeedbackDiv;
199 
200 		pll->feedbackDivFrac
201 			= (100 * pll->feedbackDivFrac) / pll->referenceFreq;
202 
203 		if (pll->feedbackDivFrac >= 5) {
204 			pll->feedbackDivFrac -= 5;
205 			pll->feedbackDivFrac /= 10;
206 			pll->feedbackDivFrac++;
207 		}
208 		if (pll->feedbackDivFrac >= 10) {
209 			pll->feedbackDiv++;
210 			pll->feedbackDivFrac = 0;
211 		}
212 	} else {
213 		TRACE("%s: performing fractional feedback calculations\n", __func__);
214 
215 		while (pll->referenceDiv <= pll->maxRefDiv) {
216 			// get feedback divider
217 			uint32 retroEncabulator = pll->postDiv * pll->referenceDiv;
218 
219 			retroEncabulator *= targetClock;
220 			pll->feedbackDiv = retroEncabulator / referenceFrequency;
221 			pll->feedbackDivFrac
222 				= retroEncabulator % referenceFrequency;
223 
224 			if (pll->feedbackDiv > pll->maxFeedbackDiv)
225 				pll->feedbackDiv = pll->maxFeedbackDiv;
226 			else if (pll->feedbackDiv < pll->minFeedbackDiv)
227 				pll->feedbackDiv = pll->minFeedbackDiv;
228 
229 			if (pll->feedbackDivFrac >= (referenceFrequency / 2))
230 				pll->feedbackDiv++;
231 
232 			pll->feedbackDivFrac = 0;
233 
234 			if (pll->referenceDiv == 0
235 				|| pll->postDiv == 0
236 				|| targetClock == 0) {
237 				TRACE("%s: Caught division by zero!\n", __func__);
238 				TRACE("%s: referenceDiv %" B_PRIu32 "\n",
239 					__func__, pll->referenceDiv);
240 				TRACE("%s: postDiv      %" B_PRIu32 "\n",
241 					__func__, pll->postDiv);
242 				TRACE("%s: targetClock  %" B_PRIu32 "\n",
243 					__func__, targetClock);
244 				return B_ERROR;
245 			}
246 			uint32 tmp = (referenceFrequency * pll->feedbackDiv)
247 				/ (pll->postDiv * pll->referenceDiv);
248 			tmp = (tmp * 1000) / targetClock;
249 
250 			if (tmp > (1000 + (MAX_TOLERANCE / 10)))
251 				pll->referenceDiv++;
252 			else if (tmp >= (1000 - (MAX_TOLERANCE / 10)))
253 				break;
254 			else
255 				pll->referenceDiv++;
256 		}
257 	}
258 
259 	if (pll->referenceDiv == 0 || pll->postDiv == 0) {
260 		TRACE("%s: Caught division by zero of post or reference divider\n",
261 			__func__);
262 		return B_ERROR;
263 	}
264 
265 	uint32 calculatedClock
266 		= ((referenceFrequency * pll->feedbackDiv * 10)
267 		+ (referenceFrequency * pll->feedbackDivFrac))
268 		/ (pll->referenceDiv * pll->postDiv * 10);
269 
270 	TRACE("%s: pixel clock: %" B_PRIu32 " gives:"
271 		" feedbackDivider = %" B_PRIu32 ".%" B_PRIu32
272 		"; referenceDivider = %" B_PRIu32 "; postDivider = %" B_PRIu32 "\n",
273 		__func__, pll->pixelClock, pll->feedbackDiv, pll->feedbackDivFrac,
274 		pll->referenceDiv, pll->postDiv);
275 
276 	if (pll->pixelClock != calculatedClock) {
277 		TRACE("%s: pixel clock %" B_PRIu32 " was changed to %" B_PRIu32 "\n",
278 			__func__, pll->pixelClock, calculatedClock);
279 		pll->pixelClock = calculatedClock;
280 	}
281 
282 	return B_OK;
283 }
284 
285 
286 union adjust_pixel_clock {
287 	ADJUST_DISPLAY_PLL_PS_ALLOCATION v1;
288 	ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 v3;
289 };
290 
291 
292 void
293 pll_setup_flags(pll_info *pll, uint8 crtcID)
294 {
295 	radeon_shared_info &info = *gInfo->shared_info;
296 	uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
297 	uint32 encoderFlags = gConnector[connectorIndex]->encoder.flags;
298 
299 	if ((info.dceMajor >= 3 && info.dceMinor >= 2)
300 		&& pll->pixelClock > 200000) {
301 		pll->flags |= PLL_PREFER_HIGH_FB_DIV;
302 	} else
303 		pll->flags |= PLL_PREFER_LOW_REF_DIV;
304 
305 
306 	if (info.device_chipset < RADEON_R700)
307 		pll->flags |= PLL_PREFER_MINM_OVER_MAXP;
308 
309 
310 	if ((encoderFlags & ATOM_DEVICE_LCD_SUPPORT) != 0) {
311 		pll->flags |= PLL_IS_LCD;
312 
313 		// TODO: Spread Spectrum PLL
314 		// use reference divider for spread spectrum
315 		if (0) { // SS enabled
316 			if (0) { // if we have a SS reference divider
317 				pll->flags |= PLL_USE_REF_DIV;
318 				//pll->reference_div = ss->refdiv;
319 				pll->flags |= PLL_USE_FRAC_FB_DIV;
320 			}
321 		}
322 	}
323 
324 	if ((encoderFlags & ATOM_DEVICE_TV_SUPPORT) != 0)
325 		pll->flags |= PLL_PREFER_CLOSEST_LOWER;
326 }
327 
328 
329 status_t
330 pll_adjust(pll_info *pll, uint8 crtcID)
331 {
332 	// TODO: PLL flags
333 	radeon_shared_info &info = *gInfo->shared_info;
334 
335 	uint32 pixelClock = pll->pixelClock;
336 		// original as pixel_clock will be adjusted
337 
338 	uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
339 	uint32 encoderID = gConnector[connectorIndex]->encoder.objectID;
340 	uint32 encoderMode = display_get_encoder_mode(connectorIndex);
341 
342 	if (info.device_chipset >= (RADEON_R600 | 0x20)) {
343 		union adjust_pixel_clock args;
344 
345 		uint8 tableMajor;
346 		uint8 tableMinor;
347 
348 		int index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll);
349 
350 		if (atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor)
351 			!= B_OK) {
352 			return B_ERROR;
353 		}
354 
355 		memset(&args, 0, sizeof(args));
356 		switch (tableMajor) {
357 			case 1:
358 				switch (tableMinor) {
359 					case 1:
360 					case 2:
361 						args.v1.usPixelClock
362 							= B_HOST_TO_LENDIAN_INT16(pixelClock / 10);
363 						args.v1.ucTransmitterID = encoderID;
364 						args.v1.ucEncodeMode = encoderMode;
365 						// TODO: SS and SS % > 0
366 						if (0) {
367 							args.v1.ucConfig
368 								|= ADJUST_DISPLAY_CONFIG_SS_ENABLE;
369 						}
370 
371 						atom_execute_table(gAtomContext, index, (uint32*)&args);
372 						// get returned adjusted clock
373 						pll->pixelClock
374 							= B_LENDIAN_TO_HOST_INT16(args.v1.usPixelClock);
375 						pll->pixelClock *= 10;
376 						break;
377 					case 3:
378 						args.v3.sInput.usPixelClock
379 							= B_HOST_TO_LENDIAN_INT16(pixelClock / 10);
380 						args.v3.sInput.ucTransmitterID = encoderID;
381 						args.v3.sInput.ucEncodeMode = encoderMode;
382 						args.v3.sInput.ucDispPllConfig = 0;
383 						// TODO: SS and SS % > 0
384 						if (0) {
385 							args.v3.sInput.ucDispPllConfig
386 								|= DISPPLL_CONFIG_SS_ENABLE;
387 						}
388 						// TODO: if ATOM_DEVICE_DFP_SUPPORT
389 						// TODO: display port DP
390 
391 						// TODO: is DP?
392 						args.v3.sInput.ucExtTransmitterID = 0;
393 
394 						atom_execute_table(gAtomContext, index, (uint32*)&args);
395 						// get returned adjusted clock
396 						pll->pixelClock
397 							= B_LENDIAN_TO_HOST_INT32(
398 								args.v3.sOutput.ulDispPllFreq);
399 						pll->pixelClock *= 10;
400 							// convert to kHz for storage
401 
402 						if (args.v3.sOutput.ucRefDiv) {
403 							pll->flags |= PLL_USE_FRAC_FB_DIV;
404 							pll->flags |= PLL_USE_REF_DIV;
405 							pll->referenceDiv = args.v3.sOutput.ucRefDiv;
406 						}
407 						if (args.v3.sOutput.ucPostDiv) {
408 							pll->flags |= PLL_USE_FRAC_FB_DIV;
409 							pll->flags |= PLL_USE_POST_DIV;
410 							pll->postDiv = args.v3.sOutput.ucPostDiv;
411 						}
412 						break;
413 					default:
414 						TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8
415 							" unknown\n", __func__, tableMajor, tableMinor);
416 						return B_ERROR;
417 				}
418 				break;
419 			default:
420 				TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8
421 					" unknown\n", __func__, tableMajor, tableMinor);
422 				return B_ERROR;
423 		}
424 	}
425 
426 	TRACE("%s: was: %" B_PRIu32 ", now: %" B_PRIu32 "\n", __func__,
427 		pixelClock, pll->pixelClock);
428 
429 	return B_OK;
430 }
431 
432 
433 union set_pixel_clock {
434 	SET_PIXEL_CLOCK_PS_ALLOCATION base;
435 	PIXEL_CLOCK_PARAMETERS v1;
436 	PIXEL_CLOCK_PARAMETERS_V2 v2;
437 	PIXEL_CLOCK_PARAMETERS_V3 v3;
438 	PIXEL_CLOCK_PARAMETERS_V5 v5;
439 	PIXEL_CLOCK_PARAMETERS_V6 v6;
440 };
441 
442 
443 status_t
444 pll_set(uint8 pllID, uint32 pixelClock, uint8 crtcID)
445 {
446 	uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
447 	pll_info *pll = &gConnector[connectorIndex]->encoder.pll;
448 
449 	pll->pixelClock = pixelClock;
450 	pll->id = pllID;
451 
452 	pll_setup_flags(pll, crtcID);
453 		// set up any special flags
454 	pll_adjust(pll, crtcID);
455 		// get any needed clock adjustments, set reference/post dividers
456 	pll_compute(pll);
457 		// compute dividers
458 
459 	int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
460 	union set_pixel_clock args;
461 	memset(&args, 0, sizeof(args));
462 
463 	uint8 tableMajor;
464 	uint8 tableMinor;
465 
466 	atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor);
467 
468 	uint32 bitsPerChannel = 8;
469 		// TODO: Digital Depth, EDID 1.4+ on digital displays
470 		// isn't in Haiku edid common code?
471 
472 	switch (tableMinor) {
473 		case 1:
474 			args.v1.usPixelClock
475 				= B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10);
476 			args.v1.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv);
477 			args.v1.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv);
478 			args.v1.ucFracFbDiv = pll->feedbackDivFrac;
479 			args.v1.ucPostDiv = pll->postDiv;
480 			args.v1.ucPpll = pll->id;
481 			args.v1.ucCRTC = crtcID;
482 			args.v1.ucRefDivSrc = 1;
483 			break;
484 		case 2:
485 			args.v2.usPixelClock
486 				= B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10);
487 			args.v2.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv);
488 			args.v2.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv);
489 			args.v2.ucFracFbDiv = pll->feedbackDivFrac;
490 			args.v2.ucPostDiv = pll->postDiv;
491 			args.v2.ucPpll = pll->id;
492 			args.v2.ucCRTC = crtcID;
493 			args.v2.ucRefDivSrc = 1;
494 			break;
495 		case 3:
496 			args.v3.usPixelClock
497 				= B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10);
498 			args.v3.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv);
499 			args.v3.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv);
500 			args.v3.ucFracFbDiv = pll->feedbackDivFrac;
501 			args.v3.ucPostDiv = pll->postDiv;
502 			args.v3.ucPpll = pll->id;
503 			args.v3.ucMiscInfo = (pll->id << 2);
504 			// if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
505 			// 	args.v3.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC;
506 			args.v3.ucTransmitterId
507 				= gConnector[connectorIndex]->encoder.objectID;
508 			args.v3.ucEncoderMode = display_get_encoder_mode(connectorIndex);
509 			break;
510 		case 5:
511 			args.v5.ucCRTC = crtcID;
512 			args.v5.usPixelClock
513 				= B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10);
514 			args.v5.ucRefDiv = pll->referenceDiv;
515 			args.v5.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv);
516 			args.v5.ulFbDivDecFrac
517 				= B_HOST_TO_LENDIAN_INT32(pll->feedbackDivFrac * 100000);
518 			args.v5.ucPostDiv = pll->postDiv;
519 			args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */
520 			// if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
521 			//	args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC;
522 			switch (bitsPerChannel) {
523 				case 8:
524 				default:
525 					args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP;
526 					break;
527 				case 10:
528 					args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP;
529 					break;
530 			}
531 			args.v5.ucTransmitterID
532 				= gConnector[connectorIndex]->encoder.objectID;
533 			args.v5.ucEncoderMode
534 				= display_get_encoder_mode(connectorIndex);
535 			args.v5.ucPpll = pllID;
536 			break;
537 		case 6:
538 			args.v6.ulDispEngClkFreq
539 				= B_HOST_TO_LENDIAN_INT32(crtcID << 24 | pll->pixelClock / 10);
540 			args.v6.ucRefDiv = pll->referenceDiv;
541 			args.v6.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv);
542 			args.v6.ulFbDivDecFrac
543 				= B_HOST_TO_LENDIAN_INT32(pll->feedbackDivFrac * 100000);
544 			args.v6.ucPostDiv = pll->postDiv;
545 			args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */
546 			// if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
547 			//	args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC;
548 			switch (bitsPerChannel) {
549 				case 8:
550 				default:
551 					args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP;
552 					break;
553 				case 10:
554 					args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP;
555 					break;
556 				case 12:
557 					args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP;
558 					break;
559 				case 16:
560 					args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP;
561 					break;
562 			}
563 			args.v6.ucTransmitterID
564 				= gConnector[connectorIndex]->encoder.objectID;
565 			args.v6.ucEncoderMode = display_get_encoder_mode(connectorIndex);
566 			args.v6.ucPpll = pllID;
567 			break;
568 		default:
569 			TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 " TODO\n",
570 				__func__, tableMajor, tableMinor);
571 			return B_ERROR;
572 	}
573 
574 	TRACE("%s: set adjusted pixel clock %" B_PRIu32 " (was %" B_PRIu32 ")\n",
575 		__func__, pll->pixelClock, pixelClock);
576 
577 	return atom_execute_table(gAtomContext, index, (uint32*)&args);
578 }
579