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