xref: /haiku/src/add-ons/accelerants/radeon_hd/display.cpp (revision a085e81e62d7a860f809b4fb7c7bf5654c396985)
1 /*
2  * Copyright 2006-2013, 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  *	Bill Randle, billr@neocat.org
8  */
9 
10 /*
11  * It's dangerous to go alone, take this!
12  *	framebuffer -> crtc -> encoder -> transmitter -> connector -> monitor
13  */
14 
15 
16 #include "display.h"
17 
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include "accelerant.h"
22 #include "accelerant_protos.h"
23 #include "bios.h"
24 #include "connector.h"
25 #include "displayport.h"
26 #include "encoder.h"
27 
28 
29 #define TRACE_DISPLAY
30 #ifdef TRACE_DISPLAY
31 extern "C" void _sPrintf(const char* format, ...);
32 #   define TRACE(x...) _sPrintf("radeon_hd: " x)
33 #else
34 #   define TRACE(x...) ;
35 #endif
36 
37 #define ERROR(x...) _sPrintf("radeon_hd: " x)
38 
39 
40 /*! Populate regs with device dependant register locations */
41 status_t
42 init_registers(register_info* regs, uint8 crtcID)
43 {
44 	memset(regs, 0, sizeof(register_info));
45 
46 	radeon_shared_info &info = *gInfo->shared_info;
47 
48 	if (info.chipsetID >= RADEON_CEDAR) {
49 		// Evergreen
50 		uint32 offset = 0;
51 
52 		switch (crtcID) {
53 			case 0:
54 				offset = EVERGREEN_CRTC0_REGISTER_OFFSET;
55 				regs->vgaControl = AVIVO_D1VGA_CONTROL;
56 				break;
57 			case 1:
58 				offset = EVERGREEN_CRTC1_REGISTER_OFFSET;
59 				regs->vgaControl = AVIVO_D2VGA_CONTROL;
60 				break;
61 			case 2:
62 				offset = EVERGREEN_CRTC2_REGISTER_OFFSET;
63 				regs->vgaControl = EVERGREEN_D3VGA_CONTROL;
64 				break;
65 			case 3:
66 				offset = EVERGREEN_CRTC3_REGISTER_OFFSET;
67 				regs->vgaControl = EVERGREEN_D4VGA_CONTROL;
68 				break;
69 			case 4:
70 				offset = EVERGREEN_CRTC4_REGISTER_OFFSET;
71 				regs->vgaControl = EVERGREEN_D5VGA_CONTROL;
72 				break;
73 			case 5:
74 				offset = EVERGREEN_CRTC5_REGISTER_OFFSET;
75 				regs->vgaControl = EVERGREEN_D6VGA_CONTROL;
76 				break;
77 			default:
78 				ERROR("%s: Unknown CRTC %" B_PRIu32 "\n",
79 					__func__, crtcID);
80 				return B_ERROR;
81 		}
82 
83 		regs->crtcOffset = offset;
84 
85 		regs->grphEnable = EVERGREEN_GRPH_ENABLE + offset;
86 		regs->grphControl = EVERGREEN_GRPH_CONTROL + offset;
87 		regs->grphSwapControl = EVERGREEN_GRPH_SWAP_CONTROL + offset;
88 
89 		regs->grphPrimarySurfaceAddr
90 			= EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + offset;
91 		regs->grphSecondarySurfaceAddr
92 			= EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + offset;
93 		regs->grphPrimarySurfaceAddrHigh
94 			= EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + offset;
95 		regs->grphSecondarySurfaceAddrHigh
96 			= EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + offset;
97 
98 		regs->grphPitch = EVERGREEN_GRPH_PITCH + offset;
99 		regs->grphSurfaceOffsetX
100 			= EVERGREEN_GRPH_SURFACE_OFFSET_X + offset;
101 		regs->grphSurfaceOffsetY
102 			= EVERGREEN_GRPH_SURFACE_OFFSET_Y + offset;
103 		regs->grphXStart = EVERGREEN_GRPH_X_START + offset;
104 		regs->grphYStart = EVERGREEN_GRPH_Y_START + offset;
105 		regs->grphXEnd = EVERGREEN_GRPH_X_END + offset;
106 		regs->grphYEnd = EVERGREEN_GRPH_Y_END + offset;
107 		regs->modeDesktopHeight = EVERGREEN_DESKTOP_HEIGHT + offset;
108 		regs->modeDataFormat = EVERGREEN_DATA_FORMAT + offset;
109 		regs->viewportStart = EVERGREEN_VIEWPORT_START + offset;
110 		regs->viewportSize = EVERGREEN_VIEWPORT_SIZE + offset;
111 
112 	} else if (info.chipsetID >= RADEON_RV770) {
113 		// R700 series
114 		uint32 offset = 0;
115 
116 		switch (crtcID) {
117 			case 0:
118 				offset = R700_CRTC0_REGISTER_OFFSET;
119 				regs->vgaControl = AVIVO_D1VGA_CONTROL;
120 				regs->grphPrimarySurfaceAddrHigh
121 					= R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH;
122 				break;
123 			case 1:
124 				offset = R700_CRTC1_REGISTER_OFFSET;
125 				regs->vgaControl = AVIVO_D2VGA_CONTROL;
126 				regs->grphPrimarySurfaceAddrHigh
127 					= R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH;
128 				break;
129 			default:
130 				ERROR("%s: Unknown CRTC %" B_PRIu32 "\n",
131 					__func__, crtcID);
132 				return B_ERROR;
133 		}
134 
135 		regs->crtcOffset = offset;
136 
137 		regs->grphEnable = AVIVO_D1GRPH_ENABLE + offset;
138 		regs->grphControl = AVIVO_D1GRPH_CONTROL + offset;
139 		regs->grphSwapControl = AVIVO_D1GRPH_SWAP_CNTL + offset;
140 
141 		regs->grphPrimarySurfaceAddr
142 			= R700_D1GRPH_PRIMARY_SURFACE_ADDRESS + offset;
143 		regs->grphSecondarySurfaceAddr
144 			= R700_D1GRPH_SECONDARY_SURFACE_ADDRESS + offset;
145 
146 		regs->grphPitch = AVIVO_D1GRPH_PITCH + offset;
147 		regs->grphSurfaceOffsetX = AVIVO_D1GRPH_SURFACE_OFFSET_X + offset;
148 		regs->grphSurfaceOffsetY = AVIVO_D1GRPH_SURFACE_OFFSET_Y + offset;
149 		regs->grphXStart = AVIVO_D1GRPH_X_START + offset;
150 		regs->grphYStart = AVIVO_D1GRPH_Y_START + offset;
151 		regs->grphXEnd = AVIVO_D1GRPH_X_END + offset;
152 		regs->grphYEnd = AVIVO_D1GRPH_Y_END + offset;
153 
154 		regs->modeDesktopHeight = AVIVO_D1MODE_DESKTOP_HEIGHT + offset;
155 		regs->modeDataFormat = AVIVO_D1MODE_DATA_FORMAT + offset;
156 		regs->viewportStart = AVIVO_D1MODE_VIEWPORT_START + offset;
157 		regs->viewportSize = AVIVO_D1MODE_VIEWPORT_SIZE + offset;
158 
159 	} else if (info.chipsetID >= RADEON_RS600) {
160 		// Avivo+
161 		uint32 offset = 0;
162 
163 		switch (crtcID) {
164 			case 0:
165 				offset = R600_CRTC0_REGISTER_OFFSET;
166 				regs->vgaControl = AVIVO_D1VGA_CONTROL;
167 				break;
168 			case 1:
169 				offset = R600_CRTC1_REGISTER_OFFSET;
170 				regs->vgaControl = AVIVO_D2VGA_CONTROL;
171 				break;
172 			default:
173 				ERROR("%s: Unknown CRTC %" B_PRIu32 "\n",
174 					__func__, crtcID);
175 				return B_ERROR;
176 		}
177 
178 		regs->crtcOffset = offset;
179 
180 		regs->grphEnable = AVIVO_D1GRPH_ENABLE + offset;
181 		regs->grphControl = AVIVO_D1GRPH_CONTROL + offset;
182 		regs->grphSwapControl = AVIVO_D1GRPH_SWAP_CNTL + offset;
183 
184 		regs->grphPrimarySurfaceAddr
185 			= AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS + offset;
186 		regs->grphSecondarySurfaceAddr
187 			= AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS + offset;
188 
189 		// Surface Address high only used on r700 and higher
190 		regs->grphPrimarySurfaceAddrHigh = 0xDEAD;
191 		regs->grphSecondarySurfaceAddrHigh = 0xDEAD;
192 
193 		regs->grphPitch = AVIVO_D1GRPH_PITCH + offset;
194 		regs->grphSurfaceOffsetX = AVIVO_D1GRPH_SURFACE_OFFSET_X + offset;
195 		regs->grphSurfaceOffsetY = AVIVO_D1GRPH_SURFACE_OFFSET_Y + offset;
196 		regs->grphXStart = AVIVO_D1GRPH_X_START + offset;
197 		regs->grphYStart = AVIVO_D1GRPH_Y_START + offset;
198 		regs->grphXEnd = AVIVO_D1GRPH_X_END + offset;
199 		regs->grphYEnd = AVIVO_D1GRPH_Y_END + offset;
200 
201 		regs->modeDesktopHeight = AVIVO_D1MODE_DESKTOP_HEIGHT + offset;
202 		regs->modeDataFormat = AVIVO_D1MODE_DATA_FORMAT + offset;
203 		regs->viewportStart = AVIVO_D1MODE_VIEWPORT_START + offset;
204 		regs->viewportSize = AVIVO_D1MODE_VIEWPORT_SIZE + offset;
205 	} else {
206 		// this really shouldn't happen unless a driver PCIID chipset is wrong
207 		TRACE("%s, unknown Radeon chipset: %s\n", __func__,
208 			info.chipsetName);
209 		return B_ERROR;
210 	}
211 
212 	TRACE("%s, registers for ATI chipset %s crt #%d loaded\n", __func__,
213 		info.chipsetName, crtcID);
214 
215 	return B_OK;
216 }
217 
218 
219 status_t
220 detect_crt_ranges(uint32 crtid)
221 {
222 	edid1_info* edid = &gDisplay[crtid]->edidData;
223 
224 	// Scan each display EDID description for monitor ranges
225 	for (uint32 index = 0; index < EDID1_NUM_DETAILED_MONITOR_DESC; index++) {
226 
227 		edid1_detailed_monitor* monitor
228 			= &edid->detailed_monitor[index];
229 
230 		if (monitor->monitor_desc_type == EDID1_MONITOR_RANGES) {
231 			edid1_monitor_range range = monitor->data.monitor_range;
232 			gDisplay[crtid]->vfreqMin = range.min_v;   /* in Hz */
233 			gDisplay[crtid]->vfreqMax = range.max_v;
234 			gDisplay[crtid]->hfreqMin = range.min_h;   /* in kHz */
235 			gDisplay[crtid]->hfreqMax = range.max_h;
236 			return B_OK;
237 		}
238 	}
239 
240 	return B_ERROR;
241 }
242 
243 
244 status_t
245 detect_displays()
246 {
247 	// reset known displays
248 	for (uint32 id = 0; id < MAX_DISPLAY; id++) {
249 		gDisplay[id]->attached = false;
250 		gDisplay[id]->powered = false;
251 		gDisplay[id]->foundRanges = false;
252 	}
253 
254 	uint32 displayIndex = 0;
255 	for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
256 		if (gConnector[id]->valid == false)
257 			continue;
258 		if (displayIndex >= MAX_DISPLAY)
259 			continue;
260 
261 		if (gConnector[id]->type == VIDEO_CONNECTOR_9DIN) {
262 			TRACE("%s: connector(%" B_PRIu32 "): Skipping 9DIN connector "
263 				"(not yet supported)\n", __func__, id);
264 			continue;
265 		}
266 
267 		if (gConnector[id]->type == VIDEO_CONNECTOR_DP) {
268 			TRACE("%s: connector(%" B_PRIu32 "): Checking DP.\n", __func__, id);
269 
270 			edid1_info* edid = &gDisplay[displayIndex]->edidData;
271 			gDisplay[displayIndex]->attached
272 				= ddc2_dp_read_edid1(id, edid);
273 
274 			if (gDisplay[displayIndex]->attached) {
275 				TRACE("%s: connector(%" B_PRIu32 "): Found DisplayPort EDID!\n",
276 					__func__);
277 			}
278 		}
279 		// TODO: Handle external DP brides - ??
280 		#if 0
281 		if (gConnector[id]->encoderExternal.isDPBridge == true) {
282 			// If this is a DisplayPort Bridge, setup ddc on bus
283 			// TRAVIS (LVDS) or NUTMEG (VGA)
284 			TRACE("%s: is bridge, performing bridge DDC setup\n", __func__);
285 			encoder_external_setup(id, 23860,
286 				EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP);
287 			gDisplay[displayIndex]->attached = true;
288 
289 			// TODO: DDC Router switching for DisplayPort (and others?)
290 		}
291 		#endif
292 		if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) {
293 			// If plain (non-DP) laptop LVDS, read mode info from AtomBIOS
294 			//TRACE("%s: non-DP laptop LVDS detected\n", __func__);
295 			gDisplay[displayIndex]->attached = connector_read_mode_lvds(id,
296 				&gDisplay[displayIndex]->preferredMode);
297 			if (gDisplay[displayIndex]->attached) {
298 				TRACE("%s: connector(%" B_PRIu32 "): found LVDS preferred "
299 					"mode\n", __func__, id);
300 			}
301 		}
302 
303 		// If no display found yet, try more standard detection methods
304 		if (gDisplay[displayIndex]->attached == false) {
305 			TRACE("%s: connector(%" B_PRIu32 "): bit-banging ddc for EDID.\n",
306 				__func__, id);
307 
308 			// Lets try bit-banging edid from connector
309 			gDisplay[displayIndex]->attached
310 				= connector_read_edid(id, &gDisplay[displayIndex]->edidData);
311 
312 			// Found EDID data?
313 			if (gDisplay[displayIndex]->attached) {
314 				TRACE("%s: connector(%" B_PRIu32 "): found EDID data.\n",
315 					__func__, id);
316 
317 				if (gConnector[id]->type == VIDEO_CONNECTOR_DVII
318 					|| gConnector[id]->type == VIDEO_CONNECTOR_HDMIB) {
319 					// These connectors can share gpio pins for data
320 					// communication between digital and analog encoders
321 					// (DVI-I is most common)
322 					edid1_info* edid = &gDisplay[displayIndex]->edidData;
323 
324 					bool analogEncoder
325 						= gConnector[id]->encoder.type == VIDEO_ENCODER_TVDAC
326 						|| gConnector[id]->encoder.type == VIDEO_ENCODER_DAC;
327 					bool digitalEncoder
328 						= gConnector[id]->encoder.type == VIDEO_ENCODER_TMDS;
329 
330 					bool digitalEdid = edid->display.input_type ? true : false;
331 
332 					if (digitalEdid && analogEncoder) {
333 						// Digital EDID + analog encoder? Lets try a load test
334 						gDisplay[displayIndex]->attached
335 							= encoder_analog_load_detect(id);
336 					} else if (!digitalEdid && digitalEncoder) {
337 						// non-digital EDID + digital encoder? Nope.
338 						gDisplay[displayIndex]->attached = false;
339 					}
340 
341 					// Else... everything aligns as it should and attached = 1
342 				}
343 			}
344 		}
345 
346 		if (gDisplay[displayIndex]->attached != true) {
347 			// Nothing interesting here, move along
348 			continue;
349 		}
350 
351 		// We found a valid / attached display
352 
353 		gDisplay[displayIndex]->connectorIndex = id;
354 			// Populate physical connector index from gConnector
355 
356 		init_registers(gDisplay[displayIndex]->regs, displayIndex);
357 
358 		if (gDisplay[displayIndex]->preferredMode.virtual_width > 0) {
359 			// Found a single preferred mode
360 			gDisplay[displayIndex]->foundRanges = false;
361 		} else {
362 			// Use edid data and pull ranges
363 			if (detect_crt_ranges(displayIndex) == B_OK)
364 				gDisplay[displayIndex]->foundRanges = true;
365 		}
366 
367 		displayIndex++;
368 	}
369 
370 	// fallback if no attached monitors were found
371 	if (displayIndex == 0) {
372 		// This is a hack, however as we don't support HPD just yet,
373 		// it tries to prevent a "no displays" situation.
374 		ERROR("%s: ERROR: 0 attached monitors were found on display connectors."
375 			" Injecting first connector as a last resort.\n", __func__);
376 		for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
377 			// skip TV DAC connectors as likely fallback isn't for TV
378 			if (gConnector[id]->encoder.type == VIDEO_ENCODER_TVDAC)
379 				continue;
380 			gDisplay[0]->attached = true;
381 			gDisplay[0]->connectorIndex = id;
382 			init_registers(gDisplay[0]->regs, 0);
383 			if (detect_crt_ranges(0) == B_OK)
384 				gDisplay[0]->foundRanges = true;
385 			break;
386 		}
387 	}
388 
389 	// Initial boot state is the first two crtc's powered
390 	if (gDisplay[0]->attached == true)
391 		gDisplay[0]->powered = true;
392 	if (gDisplay[1]->attached == true)
393 		gDisplay[1]->powered = true;
394 
395 	return B_OK;
396 }
397 
398 
399 void
400 debug_displays()
401 {
402 	TRACE("Currently detected monitors===============\n");
403 	for (uint32 id = 0; id < MAX_DISPLAY; id++) {
404 		ERROR("Display #%" B_PRIu32 " attached = %s\n",
405 			id, gDisplay[id]->attached ? "true" : "false");
406 
407 		uint32 connectorIndex = gDisplay[id]->connectorIndex;
408 
409 		if (gDisplay[id]->attached) {
410 			uint32 connectorType = gConnector[connectorIndex]->type;
411 			uint32 encoderType = gConnector[connectorIndex]->encoder.type;
412 			ERROR(" + connector ID:   %" B_PRIu32 "\n", connectorIndex);
413 			ERROR(" + connector type: %s\n", get_connector_name(connectorType));
414 			ERROR(" + encoder type:   %s\n", get_encoder_name(encoderType));
415 			ERROR(" + limits: Vert Min/Max: %" B_PRIu32 "/%" B_PRIu32"\n",
416 				gDisplay[id]->vfreqMin, gDisplay[id]->vfreqMax);
417 			ERROR(" + limits: Horz Min/Max: %" B_PRIu32 "/%" B_PRIu32"\n",
418 				gDisplay[id]->hfreqMin, gDisplay[id]->hfreqMax);
419 		}
420 	}
421 	TRACE("==========================================\n");
422 }
423 
424 
425 uint32
426 display_get_encoder_mode(uint32 connectorIndex)
427 {
428 	// Is external DisplayPort Bridge?
429 	if (gConnector[connectorIndex]->encoderExternal.valid == true
430 		&& gConnector[connectorIndex]->encoderExternal.isDPBridge == true) {
431 		return ATOM_ENCODER_MODE_DP;
432 	}
433 
434 	// DVO Encoders (should be bridges)
435 	switch (gConnector[connectorIndex]->encoder.objectID) {
436 		case ENCODER_OBJECT_ID_INTERNAL_DVO1:
437 		case ENCODER_OBJECT_ID_INTERNAL_DDI:
438 		case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
439 			return ATOM_ENCODER_MODE_DVO;
440 	}
441 
442 	// Find crtc for connector so we can identify source of edid data
443 	int32 crtc = -1;
444 	for (int32 id = 0; id < MAX_DISPLAY; id++) {
445 		if (gDisplay[id]->connectorIndex == connectorIndex) {
446 			crtc = id;
447 			break;
448 		}
449 	}
450 	bool edidDigital = false;
451 	if (crtc == -1) {
452 		ERROR("%s: BUG: executed on connector without crtc!\n", __func__);
453 	} else {
454 		edid1_info* edid = &gDisplay[crtc]->edidData;
455 		edidDigital = edid->display.input_type ? true : false;
456 	}
457 
458 	// Normal encoder situations
459 	switch (gConnector[connectorIndex]->type) {
460 		case VIDEO_CONNECTOR_DVII:
461 		case VIDEO_CONNECTOR_HDMIB: /* HDMI-B is DL-DVI; analog works fine */
462 			// TODO: if audio detected on edid and DCE4, ATOM_ENCODER_MODE_DVI
463 			//        if audio detected on edid not DCE4, ATOM_ENCODER_MODE_HDMI
464 			if (edidDigital)
465 				return ATOM_ENCODER_MODE_DVI;
466 			else
467 				return ATOM_ENCODER_MODE_CRT;
468 			break;
469 		case VIDEO_CONNECTOR_DVID:
470 		case VIDEO_CONNECTOR_HDMIA:
471 		default:
472 			// TODO: if audio detected on edid and DCE4, ATOM_ENCODER_MODE_DVI
473 			//        if audio detected on edid not DCE4, ATOM_ENCODER_MODE_HDMI
474 			return ATOM_ENCODER_MODE_DVI;
475 		case VIDEO_CONNECTOR_LVDS:
476 			return ATOM_ENCODER_MODE_LVDS;
477 		case VIDEO_CONNECTOR_DP:
478 			// dig_connector = radeon_connector->con_priv;
479 			// if ((dig_connector->dp_sink_type
480 			//	== CONNECTOR_OBJECT_ID_DISPLAYPORT)
481 			// 	|| (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
482 			// 	return ATOM_ENCODER_MODE_DP;
483 			// }
484 			// TODO: if audio detected on edid and DCE4, ATOM_ENCODER_MODE_DVI
485 			//        if audio detected on edid not DCE4, ATOM_ENCODER_MODE_HDMI
486 			return ATOM_ENCODER_MODE_DP;
487 		case VIDEO_CONNECTOR_EDP:
488 			return ATOM_ENCODER_MODE_DP;
489 		case VIDEO_CONNECTOR_DVIA:
490 		case VIDEO_CONNECTOR_VGA:
491 			return ATOM_ENCODER_MODE_CRT;
492 		case VIDEO_CONNECTOR_COMPOSITE:
493 		case VIDEO_CONNECTOR_SVIDEO:
494 		case VIDEO_CONNECTOR_9DIN:
495 			return ATOM_ENCODER_MODE_TV;
496 	}
497 }
498 
499 
500 void
501 display_crtc_lock(uint8 crtcID, int command)
502 {
503 	TRACE("%s\n", __func__);
504 
505 	ENABLE_CRTC_PS_ALLOCATION args;
506 	int index
507 		= GetIndexIntoMasterTable(COMMAND, UpdateCRTC_DoubleBufferRegisters);
508 
509 	memset(&args, 0, sizeof(args));
510 
511 	args.ucCRTC = crtcID;
512 	args.ucEnable = command;
513 
514 	atom_execute_table(gAtomContext, index, (uint32*)&args);
515 }
516 
517 
518 void
519 display_crtc_blank(uint8 crtcID, int command)
520 {
521 	TRACE("%s\n", __func__);
522 
523 	BLANK_CRTC_PS_ALLOCATION args;
524 	int index = GetIndexIntoMasterTable(COMMAND, BlankCRTC);
525 
526 	memset(&args, 0, sizeof(args));
527 
528 	args.ucCRTC = crtcID;
529 	args.ucBlanking = command;
530 
531 	args.usBlackColorRCr = 0;
532 	args.usBlackColorGY = 0;
533 	args.usBlackColorBCb = 0;
534 
535 	atom_execute_table(gAtomContext, index, (uint32*)&args);
536 }
537 
538 
539 void
540 display_crtc_scale(uint8 crtcID, display_mode* mode)
541 {
542 	TRACE("%s\n", __func__);
543 	ENABLE_SCALER_PS_ALLOCATION args;
544 	int index = GetIndexIntoMasterTable(COMMAND, EnableScaler);
545 
546 	memset(&args, 0, sizeof(args));
547 
548 	args.ucScaler = crtcID;
549 	args.ucEnable = ATOM_SCALER_DISABLE;
550 
551 	atom_execute_table(gAtomContext, index, (uint32*)&args);
552 }
553 
554 
555 void
556 display_crtc_dpms(uint8 crtcID, int mode)
557 {
558 	radeon_shared_info &info = *gInfo->shared_info;
559 
560 	switch (mode) {
561 		case B_DPMS_ON:
562 			TRACE("%s: crtc %" B_PRIu8 " dpms powerup\n", __func__, crtcID);
563 			if (gDisplay[crtcID]->attached == false)
564 				return;
565 			display_crtc_power(crtcID, ATOM_ENABLE);
566 			gDisplay[crtcID]->powered = true;
567 			if (info.dceMajor >= 3)
568 				display_crtc_memreq(crtcID, ATOM_ENABLE);
569 			display_crtc_blank(crtcID, ATOM_BLANKING_OFF);
570 			break;
571 		case B_DPMS_STAND_BY:
572 		case B_DPMS_SUSPEND:
573 		case B_DPMS_OFF:
574 			TRACE("%s: crtc %" B_PRIu8 " dpms powerdown\n", __func__, crtcID);
575 			if (gDisplay[crtcID]->attached == false)
576 				return;
577 			if (gDisplay[crtcID]->powered == true)
578 				display_crtc_blank(crtcID, ATOM_BLANKING);
579 			if (info.dceMajor >= 3)
580 				display_crtc_memreq(crtcID, ATOM_DISABLE);
581 			display_crtc_power(crtcID, ATOM_DISABLE);
582 			gDisplay[crtcID]->powered = false;
583 	}
584 }
585 
586 
587 void
588 display_dce45_crtc_load_lut(uint8 crtcID)
589 {
590 	radeon_shared_info &info = *gInfo->shared_info;
591 	register_info* regs = gDisplay[crtcID]->regs;
592 
593 	TRACE("%s: crtcID %" B_PRIu8 "\n", __func__, crtcID);
594 
595 	uint16* r = info.color_data;
596 	uint16* g = r + 256;
597 	uint16* b = r + 512;
598 
599 	if (info.dceMajor >= 5) {
600 		Write32(OUT, NI_INPUT_CSC_CONTROL + regs->crtcOffset,
601 		   (NI_INPUT_CSC_GRPH_MODE(NI_INPUT_CSC_BYPASS) |
602 		   NI_INPUT_CSC_OVL_MODE(NI_INPUT_CSC_BYPASS)));
603 		Write32(OUT, NI_PRESCALE_GRPH_CONTROL + regs->crtcOffset,
604 			NI_GRPH_PRESCALE_BYPASS);
605 		Write32(OUT, NI_PRESCALE_OVL_CONTROL + regs->crtcOffset,
606 			NI_OVL_PRESCALE_BYPASS);
607 		Write32(OUT, NI_INPUT_GAMMA_CONTROL + regs->crtcOffset,
608 			(NI_GRPH_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT) |
609 			NI_OVL_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT)));
610 	}
611 
612 	Write32(OUT, EVERGREEN_DC_LUT_CONTROL + regs->crtcOffset, 0);
613 
614 	Write32(OUT, EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + regs->crtcOffset, 0);
615 	Write32(OUT, EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + regs->crtcOffset, 0);
616 	Write32(OUT, EVERGREEN_DC_LUT_BLACK_OFFSET_RED + regs->crtcOffset, 0);
617 
618 	Write32(OUT, EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + regs->crtcOffset, 0xffff);
619 	Write32(OUT, EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + regs->crtcOffset, 0xffff);
620 	Write32(OUT, EVERGREEN_DC_LUT_WHITE_OFFSET_RED + regs->crtcOffset, 0xffff);
621 
622 	Write32(OUT, EVERGREEN_DC_LUT_RW_MODE, 0);
623 	Write32(OUT, EVERGREEN_DC_LUT_WRITE_EN_MASK, 0x00000007);
624 
625 	Write32(OUT, EVERGREEN_DC_LUT_RW_INDEX, 0);
626 	for (int i = 0; i < 256; i++) {
627 		Write32(OUT, EVERGREEN_DC_LUT_30_COLOR + regs->crtcOffset,
628 			 (r[i] << 20) |
629 			 (g[i] << 10) |
630 			 (b[i] << 0));
631 	}
632 
633 	if (info.dceMajor >= 5) {
634 		Write32(OUT, NI_DEGAMMA_CONTROL + regs->crtcOffset,
635 		   (NI_GRPH_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
636 		   NI_OVL_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
637 		   NI_ICON_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
638 		   NI_CURSOR_DEGAMMA_MODE(NI_DEGAMMA_BYPASS)));
639 		Write32(OUT, NI_GAMUT_REMAP_CONTROL + regs->crtcOffset,
640 			(NI_GRPH_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS) |
641 			NI_OVL_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS)));
642 		Write32(OUT, NI_REGAMMA_CONTROL + regs->crtcOffset,
643 			(NI_GRPH_REGAMMA_MODE(NI_REGAMMA_BYPASS) |
644 			NI_OVL_REGAMMA_MODE(NI_REGAMMA_BYPASS)));
645 		Write32(OUT, NI_OUTPUT_CSC_CONTROL + regs->crtcOffset,
646 			(NI_OUTPUT_CSC_GRPH_MODE(NI_OUTPUT_CSC_BYPASS) |
647 			NI_OUTPUT_CSC_OVL_MODE(NI_OUTPUT_CSC_BYPASS)));
648 		/* XXX match this to the depth of the crtc fmt block, move to modeset? */
649 		Write32(OUT, 0x6940 + regs->crtcOffset, 0);
650 	}
651 }
652 
653 
654 void
655 display_avivo_crtc_load_lut(uint8 crtcID)
656 {
657 	radeon_shared_info &info = *gInfo->shared_info;
658 	register_info* regs = gDisplay[crtcID]->regs;
659 
660 	TRACE("%s: crtcID %" B_PRIu8 "\n", __func__, crtcID);
661 
662 	uint16* r = info.color_data;
663 	uint16* g = r + 256;
664 	uint16* b = r + 512;
665 
666 	Write32(OUT, AVIVO_DC_LUTA_CONTROL + regs->crtcOffset, 0);
667 
668 	Write32(OUT, AVIVO_DC_LUTA_BLACK_OFFSET_BLUE + regs->crtcOffset, 0);
669 	Write32(OUT, AVIVO_DC_LUTA_BLACK_OFFSET_GREEN + regs->crtcOffset, 0);
670 	Write32(OUT, AVIVO_DC_LUTA_BLACK_OFFSET_RED + regs->crtcOffset, 0);
671 
672 	Write32(OUT, AVIVO_DC_LUTA_WHITE_OFFSET_BLUE + regs->crtcOffset, 0xffff);
673 	Write32(OUT, AVIVO_DC_LUTA_WHITE_OFFSET_GREEN + regs->crtcOffset, 0xffff);
674 	Write32(OUT, AVIVO_DC_LUTA_WHITE_OFFSET_RED + regs->crtcOffset, 0xffff);
675 
676 	Write32(OUT, AVIVO_DC_LUT_RW_SELECT, crtcID);
677 	Write32(OUT, AVIVO_DC_LUT_RW_MODE, 0);
678 	Write32(OUT, AVIVO_DC_LUT_WRITE_EN_MASK, 0x0000003f);
679 
680 	Write32(OUT, AVIVO_DC_LUT_RW_INDEX, 0);
681 	for (int i = 0; i < 256; i++) {
682 		Write32(OUT, AVIVO_DC_LUT_30_COLOR,
683 			 (r[i] << 20) |
684 			 (g[i] << 10) |
685 			 (b[i] << 0));
686 	}
687 
688 	Write32(OUT, AVIVO_D1GRPH_LUT_SEL + regs->crtcOffset, crtcID);
689 }
690 
691 
692 void
693 display_crtc_fb_set(uint8 crtcID, display_mode* mode)
694 {
695 	radeon_shared_info &info = *gInfo->shared_info;
696 	register_info* regs = gDisplay[crtcID]->regs;
697 
698 	uint16* r = info.color_data;
699 	uint16* g = r + 256;
700 	uint16* b = r + 512;
701 
702 	uint32 fbSwap;
703 	if (info.dceMajor >= 4)
704 		fbSwap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_NONE);
705 	else
706 		fbSwap = R600_D1GRPH_SWAP_ENDIAN_NONE;
707 
708 	uint32 fbFormat;
709 
710 	uint32 bytesPerPixel;
711 	uint32 bitsPerPixel;
712 
713 	switch (mode->space) {
714 		case B_CMAP8:
715 			bytesPerPixel = 1;
716 			bitsPerPixel = 8;
717 			if (info.dceMajor >= 4) {
718 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_8BPP)
719 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_INDEXED));
720 			} else {
721 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_8BPP
722 					| AVIVO_D1GRPH_CONTROL_8BPP_INDEXED;
723 			}
724 			// TODO: copy system color map into shared info
725 			break;
726 		case B_RGB15_LITTLE:
727 			bytesPerPixel = 2;
728 			bitsPerPixel = 15;
729 			if (info.dceMajor >= 4) {
730 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP)
731 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB1555));
732 			} else {
733 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP
734 					| AVIVO_D1GRPH_CONTROL_16BPP_ARGB1555;
735 			}
736 			break;
737 		case B_RGB16_LITTLE:
738 			bytesPerPixel = 2;
739 			bitsPerPixel = 16;
740 
741 			if (info.dceMajor >= 4) {
742 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP)
743 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB565));
744 				#ifdef __POWERPC__
745 				fbSwap
746 					= EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16);
747 				#endif
748 			} else {
749 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP
750 					| AVIVO_D1GRPH_CONTROL_16BPP_RGB565;
751 				#ifdef __POWERPC__
752 				fbSwap = R600_D1GRPH_SWAP_ENDIAN_16BIT;
753 				#endif
754 			}
755 
756 			{
757 				// default gamma table
758 				uint16 gamma = 0;
759 				for (int i = 0; i < 256; i++) {
760 					r[i] = gamma;
761 					g[i] = gamma;
762 					b[i] = gamma;
763 					gamma += 4;
764 				}
765 			}
766 			break;
767 		case B_RGB24_LITTLE:
768 		case B_RGB32_LITTLE:
769 		default:
770 			bytesPerPixel = 4;
771 			bitsPerPixel = 32;
772 			if (info.dceMajor >= 4) {
773 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP)
774 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB8888));
775 				#ifdef __POWERPC__
776 				fbSwap
777 					= EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN32);
778 				#endif
779 			} else {
780 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_32BPP
781 					| AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888;
782 				#ifdef __POWERPC__
783 				fbSwap = R600_D1GRPH_SWAP_ENDIAN_32BIT;
784 				#endif
785 			}
786 
787 			{
788 				// default gamma table
789 				uint16 gamma = 0;
790 				for (int i = 0; i < 256; i++) {
791 					r[i] = gamma;
792 					g[i] = gamma;
793 					b[i] = gamma;
794 					gamma += 4;
795 				}
796 			}
797 			break;
798 	}
799 
800 	Write32(OUT, regs->vgaControl, 0);
801 
802 	uint64 fbAddress = gInfo->fb.vramStart;
803 
804 	TRACE("%s: Framebuffer at: 0x%" B_PRIX64 "\n", __func__, fbAddress);
805 
806 	if (info.chipsetID >= RADEON_RV770) {
807 		TRACE("%s: Set SurfaceAddress High: 0x%" B_PRIX32 "\n",
808 			__func__, (fbAddress >> 32) & 0xf);
809 
810 		Write32(OUT, regs->grphPrimarySurfaceAddrHigh,
811 			(fbAddress >> 32) & 0xf);
812 		Write32(OUT, regs->grphSecondarySurfaceAddrHigh,
813 			(fbAddress >> 32) & 0xf);
814 	}
815 
816 	TRACE("%s: Set SurfaceAddress: 0x%" B_PRIX64 "\n",
817 		__func__, (fbAddress & 0xFFFFFFFF));
818 
819 	Write32(OUT, regs->grphPrimarySurfaceAddr, (fbAddress & 0xFFFFFFFF));
820 	Write32(OUT, regs->grphSecondarySurfaceAddr, (fbAddress & 0xFFFFFFFF));
821 
822 	if (info.chipsetID >= RADEON_R600) {
823 		Write32(CRT, regs->grphControl, fbFormat);
824 		Write32(CRT, regs->grphSwapControl, fbSwap);
825 	}
826 
827 	// Align our framebuffer width
828 	uint32 widthAligned = mode->virtual_width;
829 	uint32 pitchMask = 0;
830 
831 	// assume micro-linear/macro-linear mode (i.e., not tiled)
832 	switch (bytesPerPixel) {
833 		case 1:
834 			pitchMask = 63;
835 			break;
836 		case 2:
837 			pitchMask = 31;
838 			break;
839 		case 3:
840 		case 4:
841 			pitchMask = 31;
842 			break;
843 	}
844 	widthAligned += pitchMask;
845 	widthAligned &= ~pitchMask;
846 
847 	TRACE("%s: fb: %" B_PRIu32 "x%" B_PRIu32 " (%" B_PRIu32 " bpp)\n", __func__,
848 		mode->virtual_width, mode->virtual_height, bitsPerPixel);
849 	TRACE("%s: fb pitch: %" B_PRIu32 " \n", __func__,
850 		widthAligned);
851 	TRACE("%s: fb width aligned: %" B_PRIu32 "\n", __func__,
852 		widthAligned);
853 
854 	Write32(CRT, regs->grphSurfaceOffsetX, 0);
855 	Write32(CRT, regs->grphSurfaceOffsetY, 0);
856 	Write32(CRT, regs->grphXStart, 0);
857 	Write32(CRT, regs->grphYStart, 0);
858 	Write32(CRT, regs->grphXEnd, mode->virtual_width);
859 	Write32(CRT, regs->grphYEnd, mode->virtual_height);
860 	Write32(CRT, regs->grphPitch, widthAligned);
861 
862 	Write32(CRT, regs->grphEnable, 1);
863 		// Enable Frame buffer
864 
865 	Write32(CRT, regs->modeDesktopHeight, mode->virtual_height);
866 
867 	uint32 viewportWidth = mode->timing.h_display;
868 	uint32 viewportHeight = (mode->timing.v_display + 1) & ~1;
869 
870 	Write32(CRT, regs->viewportStart, 0);
871 	Write32(CRT, regs->viewportSize,
872 		(viewportWidth << 16) | viewportHeight);
873 
874 	// Pageflip setup
875 	if (info.dceMajor >= 4) {
876 		uint32 tmp
877 			= Read32(OUT, EVERGREEN_GRPH_FLIP_CONTROL + regs->crtcOffset);
878 		tmp &= ~EVERGREEN_GRPH_SURFACE_UPDATE_H_RETRACE_EN;
879 		Write32(OUT, EVERGREEN_GRPH_FLIP_CONTROL + regs->crtcOffset, tmp);
880 
881 		Write32(OUT, EVERGREEN_MASTER_UPDATE_MODE + regs->crtcOffset, 0);
882 			// Pageflip to happen anywhere in vblank
883 		display_dce45_crtc_load_lut(crtcID);
884 	} else {
885 		uint32 tmp = Read32(OUT, AVIVO_D1GRPH_FLIP_CONTROL + regs->crtcOffset);
886 		tmp &= ~AVIVO_D1GRPH_SURFACE_UPDATE_H_RETRACE_EN;
887 		Write32(OUT, AVIVO_D1GRPH_FLIP_CONTROL + regs->crtcOffset, tmp);
888 
889 		Write32(OUT, AVIVO_D1MODE_MASTER_UPDATE_MODE + regs->crtcOffset, 0);
890 			// Pageflip to happen anywhere in vblank
891 		display_avivo_crtc_load_lut(crtcID);
892 	}
893 
894 	// update shared info
895 	gInfo->shared_info->bytes_per_row = widthAligned * bytesPerPixel;
896 	gInfo->shared_info->current_mode = *mode;
897 	gInfo->shared_info->bits_per_pixel = bitsPerPixel;
898 }
899 
900 
901 void
902 display_crtc_set(uint8 crtcID, display_mode* mode)
903 {
904 	display_timing& displayTiming = mode->timing;
905 
906 	TRACE("%s called to do %dx%d\n",
907 		__func__, displayTiming.h_display, displayTiming.v_display);
908 
909 	SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION args;
910 	int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_Timing);
911 	uint16 misc = 0;
912 
913 	memset(&args, 0, sizeof(args));
914 
915 	args.usH_Total = B_HOST_TO_LENDIAN_INT16(displayTiming.h_total);
916 	args.usH_Disp = B_HOST_TO_LENDIAN_INT16(displayTiming.h_display);
917 	args.usH_SyncStart = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_start);
918 	args.usH_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_end
919 		- displayTiming.h_sync_start);
920 
921 	args.usV_Total = B_HOST_TO_LENDIAN_INT16(displayTiming.v_total);
922 	args.usV_Disp = B_HOST_TO_LENDIAN_INT16(displayTiming.v_display);
923 	args.usV_SyncStart = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_start);
924 	args.usV_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_end
925 		- displayTiming.v_sync_start);
926 
927 	args.ucOverscanRight = 0;
928 	args.ucOverscanLeft = 0;
929 	args.ucOverscanBottom = 0;
930 	args.ucOverscanTop = 0;
931 
932 	if ((displayTiming.flags & B_POSITIVE_HSYNC) == 0)
933 		misc |= ATOM_HSYNC_POLARITY;
934 	if ((displayTiming.flags & B_POSITIVE_VSYNC) == 0)
935 		misc |= ATOM_VSYNC_POLARITY;
936 
937 	args.susModeMiscInfo.usAccess = B_HOST_TO_LENDIAN_INT16(misc);
938 	args.ucCRTC = crtcID;
939 
940 	atom_execute_table(gAtomContext, index, (uint32*)&args);
941 }
942 
943 
944 void
945 display_crtc_set_dtd(uint8 crtcID, display_mode* mode)
946 {
947 	display_timing& displayTiming = mode->timing;
948 
949 	TRACE("%s called to do %dx%d\n", __func__,
950 		displayTiming.h_display, displayTiming.v_display);
951 
952 	SET_CRTC_USING_DTD_TIMING_PARAMETERS args;
953 	int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_UsingDTDTiming);
954 	uint16 misc = 0;
955 
956 	memset(&args, 0, sizeof(args));
957 
958 	// Note: the code below assumes H & V borders are both zero
959 	uint16 blankStart
960 		= MIN(displayTiming.h_sync_start, displayTiming.h_display);
961 	uint16 blankEnd
962 		= MAX(displayTiming.h_sync_end, displayTiming.h_total);
963 	args.usH_Size = B_HOST_TO_LENDIAN_INT16(displayTiming.h_display);
964 	args.usH_Blanking_Time = B_HOST_TO_LENDIAN_INT16(blankEnd - blankStart);
965 
966 	blankStart = MIN(displayTiming.v_sync_start, displayTiming.v_display);
967 	blankEnd = MAX(displayTiming.v_sync_end, displayTiming.v_total);
968 	args.usV_Size = B_HOST_TO_LENDIAN_INT16(displayTiming.v_display);
969 	args.usV_Blanking_Time = B_HOST_TO_LENDIAN_INT16(blankEnd - blankStart);
970 
971 	args.usH_SyncOffset = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_start
972 		- displayTiming.h_display);
973 	args.usH_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_end
974 		- displayTiming.h_sync_start);
975 
976 	args.usV_SyncOffset = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_start
977 		- displayTiming.v_display);
978 	args.usV_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_end
979 		- displayTiming.v_sync_start);
980 
981 	args.ucH_Border = 0;
982 	args.ucV_Border = 0;
983 
984 	if ((displayTiming.flags & B_POSITIVE_HSYNC) == 0)
985 		misc |= ATOM_HSYNC_POLARITY;
986 	if ((displayTiming.flags & B_POSITIVE_VSYNC) == 0)
987 		misc |= ATOM_VSYNC_POLARITY;
988 
989 	args.susModeMiscInfo.usAccess = B_HOST_TO_LENDIAN_INT16(misc);
990 	args.ucCRTC = crtcID;
991 
992 	atom_execute_table(gAtomContext, index, (uint32*)&args);
993 }
994 
995 
996 void
997 display_crtc_ss(pll_info* pll, int command)
998 {
999 	TRACE("%s\n", __func__);
1000 	radeon_shared_info &info = *gInfo->shared_info;
1001 
1002 	int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL);
1003 
1004 	union enableSS {
1005 		ENABLE_LVDS_SS_PARAMETERS lvds_ss;
1006 		ENABLE_LVDS_SS_PARAMETERS_V2 lvds_ss_2;
1007 		ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION v1;
1008 		ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 v2;
1009 		ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 v3;
1010 	};
1011 
1012 	union enableSS args;
1013 	memset(&args, 0, sizeof(args));
1014 
1015 	if (info.dceMajor >= 5) {
1016 		args.v3.usSpreadSpectrumAmountFrac = B_HOST_TO_LENDIAN_INT16(0);
1017 		args.v3.ucSpreadSpectrumType
1018 			= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
1019 		switch (pll->id) {
1020 			case ATOM_PPLL1:
1021 				args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P1PLL;
1022 				args.v3.usSpreadSpectrumAmount
1023 					= B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
1024 				args.v3.usSpreadSpectrumStep
1025 					= B_HOST_TO_LENDIAN_INT16(pll->ssStep);
1026 				break;
1027 			case ATOM_PPLL2:
1028 				args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P2PLL;
1029 				args.v3.usSpreadSpectrumAmount
1030 					= B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
1031 				args.v3.usSpreadSpectrumStep
1032 					= B_HOST_TO_LENDIAN_INT16(pll->ssStep);
1033 				break;
1034 			case ATOM_DCPLL:
1035 				args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_DCPLL;
1036 				args.v3.usSpreadSpectrumAmount = B_HOST_TO_LENDIAN_INT16(0);
1037 				args.v3.usSpreadSpectrumStep = B_HOST_TO_LENDIAN_INT16(0);
1038 				break;
1039 			default:
1040 				ERROR("%s: BUG: Invalid PLL ID!\n", __func__);
1041 				return;
1042 		}
1043 		if (pll->ssPercentage == 0
1044 			|| ((pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0)) {
1045 			command = ATOM_DISABLE;
1046 		}
1047 		args.v3.ucEnable = command;
1048 	} else if (info.dceMajor >= 4) {
1049 		args.v2.usSpreadSpectrumPercentage
1050 			= B_HOST_TO_LENDIAN_INT16(pll->ssPercentage);
1051 		args.v2.ucSpreadSpectrumType
1052 			= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
1053 		switch (pll->id) {
1054 			case ATOM_PPLL1:
1055 				args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_P1PLL;
1056 				args.v2.usSpreadSpectrumAmount
1057 					= B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
1058 				args.v2.usSpreadSpectrumStep
1059 					= B_HOST_TO_LENDIAN_INT16(pll->ssStep);
1060 				break;
1061 			case ATOM_PPLL2:
1062 				args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P2PLL;
1063 				args.v2.usSpreadSpectrumAmount
1064 					= B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
1065 				args.v2.usSpreadSpectrumStep
1066 					= B_HOST_TO_LENDIAN_INT16(pll->ssStep);
1067 				break;
1068 			case ATOM_DCPLL:
1069 				args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_DCPLL;
1070 				args.v2.usSpreadSpectrumAmount = B_HOST_TO_LENDIAN_INT16(0);
1071 				args.v2.usSpreadSpectrumStep = B_HOST_TO_LENDIAN_INT16(0);
1072 				break;
1073 			default:
1074 				ERROR("%s: BUG: Invalid PLL ID!\n", __func__);
1075 				return;
1076 		}
1077 		if (pll->ssPercentage == 0
1078 			|| ((pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0)
1079 			|| (info.chipsetFlags & CHIP_APU) != 0 ) {
1080 			command = ATOM_DISABLE;
1081 		}
1082 		args.v2.ucEnable = command;
1083 	} else if (info.dceMajor >= 3) {
1084 		args.v1.usSpreadSpectrumPercentage
1085 			= B_HOST_TO_LENDIAN_INT16(pll->ssPercentage);
1086 		args.v1.ucSpreadSpectrumType
1087 			= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
1088 		args.v1.ucSpreadSpectrumStep = pll->ssStep;
1089 		args.v1.ucSpreadSpectrumDelay = pll->ssDelay;
1090 		args.v1.ucSpreadSpectrumRange = pll->ssRange;
1091 		args.v1.ucPpll = pll->id;
1092 		args.v1.ucEnable = command;
1093 	} else if (info.dceMajor >= 2) {
1094 		if ((command == ATOM_DISABLE) || (pll->ssPercentage == 0)
1095 			|| (pll->ssType & ATOM_EXTERNAL_SS_MASK)) {
1096 			radeon_gpu_ss_control(pll, false);
1097 			return;
1098 		}
1099 		args.lvds_ss_2.usSpreadSpectrumPercentage
1100 			= B_HOST_TO_LENDIAN_INT16(pll->ssPercentage);
1101 		args.lvds_ss_2.ucSpreadSpectrumType
1102 			= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
1103 		args.lvds_ss_2.ucSpreadSpectrumStep = pll->ssStep;
1104 		args.lvds_ss_2.ucSpreadSpectrumDelay = pll->ssDelay;
1105 		args.lvds_ss_2.ucSpreadSpectrumRange = pll->ssRange;
1106 		args.lvds_ss_2.ucEnable = command;
1107 	} else {
1108 		ERROR("%s: TODO: Old card SS control\n", __func__);
1109 		return;
1110 	}
1111 
1112 	atom_execute_table(gAtomContext, index, (uint32*)&args);
1113 }
1114 
1115 
1116 void
1117 display_crtc_power(uint8 crtcID, int command)
1118 {
1119 	TRACE("%s\n", __func__);
1120 	int index = GetIndexIntoMasterTable(COMMAND, EnableCRTC);
1121 	ENABLE_CRTC_PS_ALLOCATION args;
1122 
1123 	memset(&args, 0, sizeof(args));
1124 
1125 	args.ucCRTC = crtcID;
1126 	args.ucEnable = command;
1127 
1128 	atom_execute_table(gAtomContext, index, (uint32*)&args);
1129 }
1130 
1131 
1132 void
1133 display_crtc_memreq(uint8 crtcID, int command)
1134 {
1135 	TRACE("%s\n", __func__);
1136 	int index = GetIndexIntoMasterTable(COMMAND, EnableCRTCMemReq);
1137 	ENABLE_CRTC_PS_ALLOCATION args;
1138 
1139 	memset(&args, 0, sizeof(args));
1140 
1141 	args.ucCRTC = crtcID;
1142 	args.ucEnable = command;
1143 
1144 	atom_execute_table(gAtomContext, index, (uint32*)&args);
1145 }
1146