xref: /haiku/src/add-ons/accelerants/radeon_hd/display.cpp (revision c237c4ce593ee823d9867fd997e51e4c447f5623)
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 			|| gConnector[id]->type == VIDEO_CONNECTOR_EDP) {
269 			TRACE("%s: connector(%" B_PRIu32 "): Checking %sDP.\n", __func__, id,
270 				gConnector[id]->type == VIDEO_CONNECTOR_EDP ? "e" : "");
271 
272 			if (gConnector[id]->encoderExternal.valid == true) {
273 				// If this has a valid external encoder (dp bridge)
274 				// normally TRAVIS (LVDS) or NUTMEG (VGA)
275 				TRACE("%s: external encoder, performing bridge DDC setup\n",
276 					__func__);
277 				encoder_external_setup(id,
278 					EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP);
279 			}
280 			edid1_info* edid = &gDisplay[displayIndex]->edidData;
281 			gDisplay[displayIndex]->attached
282 				= ddc2_dp_read_edid1(id, edid);
283 
284 			// TODO: DDC Router switching for DisplayPort (and others?)
285 
286 			if (gDisplay[displayIndex]->attached) {
287 				TRACE("%s: connector(%" B_PRIu32 "): Found DisplayPort EDID!\n",
288 					__func__, id);
289 				gInfo->shared_info->has_edid = true;
290 				edid_dump(edid);
291 			}
292 		}
293 
294 
295 		if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) {
296 			display_mode preferredMode;
297 			bool lvdsInfoFound = connector_read_mode_lvds(id,
298 				&preferredMode);
299 			TRACE("%s: connector(%" B_PRIu32 "): bit-banging LVDS for EDID.\n",
300 				__func__, id);
301 
302 			gDisplay[displayIndex]->attached = connector_read_edid(id,
303 				&gDisplay[displayIndex]->edidData);
304 
305 			if (!gDisplay[displayIndex]->attached && lvdsInfoFound) {
306 				// If we didn't find ddc edid data, fallback to lvdsInfo
307 				// We have to call connector_read_mode_lvds first to
308 				// collect SS data for the lvds connector
309 				TRACE("%s: connector(%" B_PRIu32 "): using AtomBIOS LVDS_Info "
310 					"preferred mode\n", __func__, id);
311 				gDisplay[displayIndex]->attached = true;
312 				memcpy(&gDisplay[displayIndex]->preferredMode,
313 					&preferredMode, sizeof(display_mode));
314 			}
315 		}
316 
317 		// If no display found yet, try more standard detection methods
318 		if (gDisplay[displayIndex]->attached == false) {
319 			TRACE("%s: connector(%" B_PRIu32 "): bit-banging ddc for EDID.\n",
320 				__func__, id);
321 
322 			// Bit-bang edid from connector
323 			gDisplay[displayIndex]->attached = connector_read_edid(id,
324 				&gDisplay[displayIndex]->edidData);
325 
326 			// Found EDID data?
327 			if (gDisplay[displayIndex]->attached) {
328 				TRACE("%s: connector(%" B_PRIu32 "): found EDID data.\n",
329 					__func__, id);
330 
331 				if (gConnector[id]->type == VIDEO_CONNECTOR_DVII
332 					|| gConnector[id]->type == VIDEO_CONNECTOR_HDMIB) {
333 					// These connectors can share gpio pins for data
334 					// communication between digital and analog encoders
335 					// (DVI-I is most common)
336 					edid1_info* edid = &gDisplay[displayIndex]->edidData;
337 
338 					bool analogEncoder
339 						= gConnector[id]->encoder.type == VIDEO_ENCODER_TVDAC
340 						|| gConnector[id]->encoder.type == VIDEO_ENCODER_DAC;
341 					bool digitalEncoder
342 						= gConnector[id]->encoder.type == VIDEO_ENCODER_TMDS;
343 
344 					bool digitalEdid = edid->display.input_type ? true : false;
345 
346 					if (digitalEdid && analogEncoder) {
347 						// Digital EDID + analog encoder? Lets try a load test
348 						gDisplay[displayIndex]->attached
349 							= encoder_analog_load_detect(id);
350 					} else if (!digitalEdid && digitalEncoder) {
351 						// non-digital EDID + digital encoder? Nope.
352 						gDisplay[displayIndex]->attached = false;
353 					}
354 
355 					// Else... everything aligns as it should and attached = 1
356 				}
357 			}
358 		}
359 
360 		if (gDisplay[displayIndex]->attached != true) {
361 			// Nothing interesting here, move along
362 			continue;
363 		}
364 
365 		// We found a valid / attached display
366 
367 		gDisplay[displayIndex]->connectorIndex = id;
368 			// Populate physical connector index from gConnector
369 
370 		init_registers(gDisplay[displayIndex]->regs, displayIndex);
371 
372 		if (gDisplay[displayIndex]->preferredMode.virtual_width > 0) {
373 			// Found a single preferred mode
374 			gDisplay[displayIndex]->foundRanges = false;
375 		} else {
376 			// Use edid data and pull ranges
377 			if (detect_crt_ranges(displayIndex) == B_OK)
378 				gDisplay[displayIndex]->foundRanges = true;
379 		}
380 
381 		displayIndex++;
382 	}
383 
384 	// fail if no attached monitors were found
385 	if (displayIndex == 0) {
386 		// TODO: In the future we might want to accept this condition.. however
387 		// without monitor hot plugging, we're most likely going to fail to bring
388 		// up a display here.
389 		ERROR("%s: ERROR: 0 attached monitors were found on display connectors.\n",
390 			__func__);
391 		return B_ERROR;
392 	}
393 
394 	// Initial boot state is the first two crtc's powered
395 	if (gDisplay[0]->attached == true)
396 		gDisplay[0]->powered = true;
397 	if (gDisplay[1]->attached == true)
398 		gDisplay[1]->powered = true;
399 
400 	return B_OK;
401 }
402 
403 
404 void
405 debug_displays()
406 {
407 	TRACE("Currently detected monitors===============\n");
408 	for (uint32 id = 0; id < MAX_DISPLAY; id++) {
409 		ERROR("Display #%" B_PRIu32 " attached = %s\n",
410 			id, gDisplay[id]->attached ? "true" : "false");
411 
412 		uint32 connectorIndex = gDisplay[id]->connectorIndex;
413 
414 		if (gDisplay[id]->attached) {
415 			uint32 connectorType = gConnector[connectorIndex]->type;
416 			uint32 encoderType = gConnector[connectorIndex]->encoder.type;
417 			ERROR(" + connector ID:   %" B_PRIu32 "\n", connectorIndex);
418 			ERROR(" + connector type: %s\n", get_connector_name(connectorType));
419 			ERROR(" + encoder type:   %s\n", get_encoder_name(encoderType));
420 			ERROR(" + limits: Vert Min/Max: %" B_PRIu32 "/%" B_PRIu32"\n",
421 				gDisplay[id]->vfreqMin, gDisplay[id]->vfreqMax);
422 			ERROR(" + limits: Horz Min/Max: %" B_PRIu32 "/%" B_PRIu32"\n",
423 				gDisplay[id]->hfreqMin, gDisplay[id]->hfreqMax);
424 		}
425 	}
426 	TRACE("==========================================\n");
427 }
428 
429 
430 uint32
431 display_get_encoder_mode(uint32 connectorIndex)
432 {
433 	// Is external DisplayPort Bridge?
434 	if (gConnector[connectorIndex]->encoderExternal.valid == true
435 		&& gConnector[connectorIndex]->encoderExternal.isDPBridge == true) {
436 		return ATOM_ENCODER_MODE_DP;
437 	}
438 
439 	// DVO Encoders (should be bridges)
440 	switch (gConnector[connectorIndex]->encoder.objectID) {
441 		case ENCODER_OBJECT_ID_INTERNAL_DVO1:
442 		case ENCODER_OBJECT_ID_INTERNAL_DDI:
443 		case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
444 			return ATOM_ENCODER_MODE_DVO;
445 	}
446 
447 	// Find display for connector so we can identify source of edid data
448 	int32 crtcID = -1;
449 	for (int32 id = 0; id < MAX_DISPLAY; id++) {
450 		if (gDisplay[id]->connectorIndex == connectorIndex) {
451 			crtcID = id;
452 			break;
453 		}
454 	}
455 	bool edidDigital = false;
456 	if (crtcID == -1) {
457 		ERROR("%s: BUG: executed on connector without assigned display!\n",
458 			__func__);
459 	} else {
460 		edid1_info* edid = &gDisplay[crtcID]->edidData;
461 		edidDigital = edid->display.input_type ? true : false;
462 	}
463 
464 	// Normal encoder situations
465 	switch (gConnector[connectorIndex]->type) {
466 		case VIDEO_CONNECTOR_DVII:
467 		case VIDEO_CONNECTOR_HDMIB: /* HDMI-B is DL-DVI; analog works fine */
468 			// TODO: if audio detected on edid and DCE4, ATOM_ENCODER_MODE_DVI
469 			//        if audio detected on edid not DCE4, ATOM_ENCODER_MODE_HDMI
470 			if (edidDigital)
471 				return ATOM_ENCODER_MODE_DVI;
472 			else
473 				return ATOM_ENCODER_MODE_CRT;
474 			break;
475 		case VIDEO_CONNECTOR_DVID:
476 		case VIDEO_CONNECTOR_HDMIA:
477 		default:
478 			// TODO: if audio detected on edid and DCE4, ATOM_ENCODER_MODE_DVI
479 			//        if audio detected on edid not DCE4, ATOM_ENCODER_MODE_HDMI
480 			return ATOM_ENCODER_MODE_DVI;
481 		case VIDEO_CONNECTOR_LVDS:
482 			return ATOM_ENCODER_MODE_LVDS;
483 		case VIDEO_CONNECTOR_DP:
484 			// dig_connector = radeon_connector->con_priv;
485 			// if ((dig_connector->dp_sink_type
486 			//	== CONNECTOR_OBJECT_ID_DISPLAYPORT)
487 			// 	|| (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
488 			// 	return ATOM_ENCODER_MODE_DP;
489 			// }
490 			// TODO: if audio detected on edid and DCE4, ATOM_ENCODER_MODE_DVI
491 			//        if audio detected on edid not DCE4, ATOM_ENCODER_MODE_HDMI
492 			return ATOM_ENCODER_MODE_DP;
493 		case VIDEO_CONNECTOR_EDP:
494 			return ATOM_ENCODER_MODE_DP;
495 		case VIDEO_CONNECTOR_DVIA:
496 		case VIDEO_CONNECTOR_VGA:
497 			return ATOM_ENCODER_MODE_CRT;
498 		case VIDEO_CONNECTOR_COMPOSITE:
499 		case VIDEO_CONNECTOR_SVIDEO:
500 		case VIDEO_CONNECTOR_9DIN:
501 			return ATOM_ENCODER_MODE_TV;
502 	}
503 }
504 
505 
506 void
507 display_crtc_lock(uint8 crtcID, int command)
508 {
509 	TRACE("%s\n", __func__);
510 
511 	ENABLE_CRTC_PS_ALLOCATION args;
512 	int index
513 		= GetIndexIntoMasterTable(COMMAND, UpdateCRTC_DoubleBufferRegisters);
514 
515 	memset(&args, 0, sizeof(args));
516 
517 	args.ucCRTC = crtcID;
518 	args.ucEnable = command;
519 
520 	atom_execute_table(gAtomContext, index, (uint32*)&args);
521 }
522 
523 
524 void
525 display_crtc_blank(uint8 crtcID, int command)
526 {
527 	TRACE("%s\n", __func__);
528 
529 	BLANK_CRTC_PS_ALLOCATION args;
530 	int index = GetIndexIntoMasterTable(COMMAND, BlankCRTC);
531 
532 	memset(&args, 0, sizeof(args));
533 
534 	args.ucCRTC = crtcID;
535 	args.ucBlanking = command;
536 
537 	args.usBlackColorRCr = 0;
538 	args.usBlackColorGY = 0;
539 	args.usBlackColorBCb = 0;
540 
541 	atom_execute_table(gAtomContext, index, (uint32*)&args);
542 }
543 
544 
545 void
546 display_crtc_scale(uint8 crtcID, display_mode* mode)
547 {
548 	TRACE("%s\n", __func__);
549 	ENABLE_SCALER_PS_ALLOCATION args;
550 	int index = GetIndexIntoMasterTable(COMMAND, EnableScaler);
551 
552 	memset(&args, 0, sizeof(args));
553 
554 	args.ucScaler = crtcID;
555 	args.ucEnable = ATOM_SCALER_DISABLE;
556 
557 	atom_execute_table(gAtomContext, index, (uint32*)&args);
558 }
559 
560 
561 void
562 display_crtc_dpms(uint8 crtcID, int mode)
563 {
564 	radeon_shared_info &info = *gInfo->shared_info;
565 
566 	switch (mode) {
567 		case B_DPMS_ON:
568 			TRACE("%s: crtc %" B_PRIu8 " dpms powerup\n", __func__, crtcID);
569 			if (gDisplay[crtcID]->attached == false)
570 				return;
571 			display_crtc_power(crtcID, ATOM_ENABLE);
572 			gDisplay[crtcID]->powered = true;
573 			if (info.dceMajor >= 3 && info.dceMajor < 6)
574 				display_crtc_memreq(crtcID, ATOM_ENABLE);
575 			display_crtc_blank(crtcID, ATOM_BLANKING_OFF);
576 			break;
577 		case B_DPMS_STAND_BY:
578 		case B_DPMS_SUSPEND:
579 		case B_DPMS_OFF:
580 			TRACE("%s: crtc %" B_PRIu8 " dpms powerdown\n", __func__, crtcID);
581 			if (gDisplay[crtcID]->attached == false)
582 				return;
583 			if (gDisplay[crtcID]->powered == true)
584 				display_crtc_blank(crtcID, ATOM_BLANKING);
585 			if (info.dceMajor >= 3 && info.dceMajor < 6)
586 				display_crtc_memreq(crtcID, ATOM_DISABLE);
587 			display_crtc_power(crtcID, ATOM_DISABLE);
588 			gDisplay[crtcID]->powered = false;
589 	}
590 }
591 
592 
593 void
594 display_dce45_crtc_load_lut(uint8 crtcID)
595 {
596 	radeon_shared_info &info = *gInfo->shared_info;
597 	register_info* regs = gDisplay[crtcID]->regs;
598 
599 	TRACE("%s: crtcID %" B_PRIu8 "\n", __func__, crtcID);
600 
601 	uint16* r = info.color_data;
602 	uint16* g = r + 256;
603 	uint16* b = r + 512;
604 
605 	if (info.dceMajor >= 5) {
606 		Write32(OUT, NI_INPUT_CSC_CONTROL + regs->crtcOffset,
607 			NI_INPUT_CSC_GRPH_MODE(NI_INPUT_CSC_BYPASS)
608 				| NI_INPUT_CSC_OVL_MODE(NI_INPUT_CSC_BYPASS));
609 		Write32(OUT, NI_PRESCALE_GRPH_CONTROL + regs->crtcOffset,
610 			NI_GRPH_PRESCALE_BYPASS);
611 		Write32(OUT, NI_PRESCALE_OVL_CONTROL + regs->crtcOffset,
612 			NI_OVL_PRESCALE_BYPASS);
613 		Write32(OUT, NI_INPUT_GAMMA_CONTROL + regs->crtcOffset,
614 			NI_GRPH_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT) |
615 			NI_OVL_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT));
616 	}
617 
618 	Write32(OUT, EVERGREEN_DC_LUT_CONTROL + regs->crtcOffset, 0);
619 
620 	Write32(OUT, EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + regs->crtcOffset, 0);
621 	Write32(OUT, EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + regs->crtcOffset, 0);
622 	Write32(OUT, EVERGREEN_DC_LUT_BLACK_OFFSET_RED + regs->crtcOffset, 0);
623 
624 	Write32(OUT, EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + regs->crtcOffset, 0xffff);
625 	Write32(OUT, EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + regs->crtcOffset, 0xffff);
626 	Write32(OUT, EVERGREEN_DC_LUT_WHITE_OFFSET_RED + regs->crtcOffset, 0xffff);
627 
628 	Write32(OUT, EVERGREEN_DC_LUT_RW_MODE, 0);
629 	Write32(OUT, EVERGREEN_DC_LUT_WRITE_EN_MASK, 0x00000007);
630 
631 	Write32(OUT, EVERGREEN_DC_LUT_RW_INDEX, 0);
632 	for (int i = 0; i < 256; i++) {
633 		Write32(OUT, EVERGREEN_DC_LUT_30_COLOR + regs->crtcOffset,
634 			(r[i] << 20) | (g[i] << 10) | (b[i] << 0));
635 	}
636 
637 	if (info.dceMajor >= 5) {
638 		Write32(OUT, NI_DEGAMMA_CONTROL + regs->crtcOffset,
639 			(NI_GRPH_DEGAMMA_MODE(NI_DEGAMMA_BYPASS)
640 				| NI_OVL_DEGAMMA_MODE(NI_DEGAMMA_BYPASS)
641 				| NI_ICON_DEGAMMA_MODE(NI_DEGAMMA_BYPASS)
642 				| NI_CURSOR_DEGAMMA_MODE(NI_DEGAMMA_BYPASS)));
643 		Write32(OUT, NI_GAMUT_REMAP_CONTROL + regs->crtcOffset,
644 			(NI_GRPH_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS) |
645 			NI_OVL_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS)));
646 		Write32(OUT, NI_REGAMMA_CONTROL + regs->crtcOffset,
647 			(NI_GRPH_REGAMMA_MODE(NI_REGAMMA_BYPASS) |
648 			NI_OVL_REGAMMA_MODE(NI_REGAMMA_BYPASS)));
649 		Write32(OUT, NI_OUTPUT_CSC_CONTROL + regs->crtcOffset,
650 			(NI_OUTPUT_CSC_GRPH_MODE(NI_OUTPUT_CSC_BYPASS) |
651 			NI_OUTPUT_CSC_OVL_MODE(NI_OUTPUT_CSC_BYPASS)));
652 		/* XXX match this to the depth of the crtc fmt block, move to modeset? */
653 		Write32(OUT, 0x6940 + regs->crtcOffset, 0);
654 	}
655 }
656 
657 
658 void
659 display_avivo_crtc_load_lut(uint8 crtcID)
660 {
661 	radeon_shared_info &info = *gInfo->shared_info;
662 	register_info* regs = gDisplay[crtcID]->regs;
663 
664 	TRACE("%s: crtcID %" B_PRIu8 "\n", __func__, crtcID);
665 
666 	uint16* r = info.color_data;
667 	uint16* g = r + 256;
668 	uint16* b = r + 512;
669 
670 	Write32(OUT, AVIVO_DC_LUTA_CONTROL + regs->crtcOffset, 0);
671 
672 	Write32(OUT, AVIVO_DC_LUTA_BLACK_OFFSET_BLUE + regs->crtcOffset, 0);
673 	Write32(OUT, AVIVO_DC_LUTA_BLACK_OFFSET_GREEN + regs->crtcOffset, 0);
674 	Write32(OUT, AVIVO_DC_LUTA_BLACK_OFFSET_RED + regs->crtcOffset, 0);
675 
676 	Write32(OUT, AVIVO_DC_LUTA_WHITE_OFFSET_BLUE + regs->crtcOffset, 0xffff);
677 	Write32(OUT, AVIVO_DC_LUTA_WHITE_OFFSET_GREEN + regs->crtcOffset, 0xffff);
678 	Write32(OUT, AVIVO_DC_LUTA_WHITE_OFFSET_RED + regs->crtcOffset, 0xffff);
679 
680 	Write32(OUT, AVIVO_DC_LUT_RW_SELECT, crtcID);
681 	Write32(OUT, AVIVO_DC_LUT_RW_MODE, 0);
682 	Write32(OUT, AVIVO_DC_LUT_WRITE_EN_MASK, 0x0000003f);
683 
684 	Write32(OUT, AVIVO_DC_LUT_RW_INDEX, 0);
685 	for (int i = 0; i < 256; i++) {
686 		Write32(OUT, AVIVO_DC_LUT_30_COLOR,
687 			(r[i] << 20) | (g[i] << 10) | (b[i] << 0));
688 	}
689 
690 	Write32(OUT, AVIVO_D1GRPH_LUT_SEL + regs->crtcOffset, crtcID);
691 }
692 
693 
694 void
695 display_crtc_fb_set(uint8 crtcID, display_mode* mode)
696 {
697 	radeon_shared_info &info = *gInfo->shared_info;
698 	register_info* regs = gDisplay[crtcID]->regs;
699 
700 	uint16* r = info.color_data;
701 	uint16* g = r + 256;
702 	uint16* b = r + 512;
703 
704 	uint32 fbSwap;
705 	if (info.dceMajor >= 4)
706 		fbSwap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_NONE);
707 	else
708 		fbSwap = R600_D1GRPH_SWAP_ENDIAN_NONE;
709 
710 	uint32 fbFormat;
711 
712 	uint32 bytesPerPixel;
713 	uint32 bitsPerPixel;
714 
715 	switch (mode->space) {
716 		case B_CMAP8:
717 			bytesPerPixel = 1;
718 			bitsPerPixel = 8;
719 			if (info.dceMajor >= 4) {
720 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_8BPP)
721 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_INDEXED));
722 			} else {
723 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_8BPP
724 					| AVIVO_D1GRPH_CONTROL_8BPP_INDEXED;
725 			}
726 			// TODO: copy system color map into shared info
727 			break;
728 		case B_RGB15_LITTLE:
729 			bytesPerPixel = 2;
730 			bitsPerPixel = 15;
731 			if (info.dceMajor >= 4) {
732 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP)
733 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB1555));
734 			} else {
735 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP
736 					| AVIVO_D1GRPH_CONTROL_16BPP_ARGB1555;
737 			}
738 			break;
739 		case B_RGB16_LITTLE:
740 			bytesPerPixel = 2;
741 			bitsPerPixel = 16;
742 
743 			if (info.dceMajor >= 4) {
744 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP)
745 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB565));
746 				#ifdef __POWERPC__
747 				fbSwap
748 					= EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16);
749 				#endif
750 			} else {
751 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP
752 					| AVIVO_D1GRPH_CONTROL_16BPP_RGB565;
753 				#ifdef __POWERPC__
754 				fbSwap = R600_D1GRPH_SWAP_ENDIAN_16BIT;
755 				#endif
756 			}
757 
758 			{
759 				// default gamma table
760 				uint16 gamma = 0;
761 				for (int i = 0; i < 256; i++) {
762 					r[i] = gamma;
763 					g[i] = gamma;
764 					b[i] = gamma;
765 					gamma += 4;
766 				}
767 			}
768 			break;
769 		case B_RGB24_LITTLE:
770 		case B_RGB32_LITTLE:
771 		default:
772 			bytesPerPixel = 4;
773 			bitsPerPixel = 32;
774 			if (info.dceMajor >= 4) {
775 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP)
776 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB8888));
777 				#ifdef __POWERPC__
778 				fbSwap
779 					= EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN32);
780 				#endif
781 			} else {
782 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_32BPP
783 					| AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888;
784 				#ifdef __POWERPC__
785 				fbSwap = R600_D1GRPH_SWAP_ENDIAN_32BIT;
786 				#endif
787 			}
788 
789 			{
790 				// default gamma table
791 				uint16 gamma = 0;
792 				for (int i = 0; i < 256; i++) {
793 					r[i] = gamma;
794 					g[i] = gamma;
795 					b[i] = gamma;
796 					gamma += 4;
797 				}
798 			}
799 			break;
800 	}
801 
802 	Write32(OUT, regs->vgaControl, 0);
803 
804 	uint64 fbAddress = gInfo->fb.vramStart;
805 
806 	TRACE("%s: Framebuffer at: 0x%" B_PRIX64 "\n", __func__, fbAddress);
807 
808 	if (info.chipsetID >= RADEON_RV770) {
809 		TRACE("%s: Set SurfaceAddress High: 0x%" B_PRIX32 "\n",
810 			__func__, (fbAddress >> 32) & 0xf);
811 
812 		Write32(OUT, regs->grphPrimarySurfaceAddrHigh,
813 			(fbAddress >> 32) & 0xf);
814 		Write32(OUT, regs->grphSecondarySurfaceAddrHigh,
815 			(fbAddress >> 32) & 0xf);
816 	}
817 
818 	TRACE("%s: Set SurfaceAddress: 0x%" B_PRIX64 "\n",
819 		__func__, (fbAddress & 0xFFFFFFFF));
820 
821 	Write32(OUT, regs->grphPrimarySurfaceAddr, (fbAddress & 0xFFFFFFFF));
822 	Write32(OUT, regs->grphSecondarySurfaceAddr, (fbAddress & 0xFFFFFFFF));
823 
824 	if (info.chipsetID >= RADEON_R600) {
825 		Write32(CRT, regs->grphControl, fbFormat);
826 		Write32(CRT, regs->grphSwapControl, fbSwap);
827 	}
828 
829 	// TODO: Technically if chip >= RS600
830 	int largeAlign = (info.dceMajor >= 2) ? 1 : 0;
831 
832 	// Align our framebuffer width
833 	uint32 widthAligned = mode->virtual_width;
834 	uint32 pitchMask = 0;
835 
836 	switch (bytesPerPixel) {
837 		case 1:
838 			pitchMask = largeAlign ? 255 : 127;
839 			break;
840 		case 2:
841 			pitchMask = largeAlign ? 127 : 31;
842 			break;
843 		case 3:
844 		case 4:
845 			pitchMask = largeAlign ? 63 : 15;
846 			break;
847 	}
848 	widthAligned += pitchMask;
849 	widthAligned &= ~pitchMask;
850 
851 	TRACE("%s: fb: %" B_PRIu32 "x%" B_PRIu32 " (%" B_PRIu32 " bpp)\n", __func__,
852 		mode->timing.h_display, mode->timing.v_display, bitsPerPixel);
853 	TRACE("%s: fb pitch: %" B_PRIu32 " \n", __func__, widthAligned);
854 
855 	Write32(CRT, regs->grphSurfaceOffsetX, 0);
856 	Write32(CRT, regs->grphSurfaceOffsetY, 0);
857 	Write32(CRT, regs->grphXStart, 0);
858 	Write32(CRT, regs->grphYStart, 0);
859 	Write32(CRT, regs->grphXEnd, mode->virtual_width);
860 	Write32(CRT, regs->grphYEnd, mode->virtual_height);
861 	Write32(CRT, regs->grphPitch, widthAligned);
862 
863 	Write32(CRT, regs->grphEnable, 1);
864 		// Enable Frame buffer
865 
866 	Write32(CRT, regs->modeDesktopHeight, mode->virtual_height);
867 
868 	uint32 viewportWidth = mode->timing.h_display;
869 	uint32 viewportHeight = (mode->timing.v_display + 1) & ~1;
870 
871 	Write32(CRT, regs->viewportStart, 0);
872 	Write32(CRT, regs->viewportSize,
873 		(viewportWidth << 16) | viewportHeight);
874 
875 	// Pageflip setup
876 	if (info.dceMajor >= 4) {
877 		uint32 tmp
878 			= Read32(OUT, EVERGREEN_GRPH_FLIP_CONTROL + regs->crtcOffset);
879 		tmp &= ~EVERGREEN_GRPH_SURFACE_UPDATE_H_RETRACE_EN;
880 		Write32(OUT, EVERGREEN_GRPH_FLIP_CONTROL + regs->crtcOffset, tmp);
881 
882 		Write32(OUT, EVERGREEN_MASTER_UPDATE_MODE + regs->crtcOffset, 0);
883 			// Pageflip to happen anywhere in vblank
884 		display_dce45_crtc_load_lut(crtcID);
885 	} else {
886 		uint32 tmp = Read32(OUT, AVIVO_D1GRPH_FLIP_CONTROL + regs->crtcOffset);
887 		tmp &= ~AVIVO_D1GRPH_SURFACE_UPDATE_H_RETRACE_EN;
888 		Write32(OUT, AVIVO_D1GRPH_FLIP_CONTROL + regs->crtcOffset, tmp);
889 
890 		Write32(OUT, AVIVO_D1MODE_MASTER_UPDATE_MODE + regs->crtcOffset, 0);
891 			// Pageflip to happen anywhere in vblank
892 		display_avivo_crtc_load_lut(crtcID);
893 	}
894 
895 	// update shared info
896 	gInfo->shared_info->bytes_per_row = widthAligned * bytesPerPixel;
897 	gInfo->shared_info->current_mode = *mode;
898 	gInfo->shared_info->bits_per_pixel = bitsPerPixel;
899 }
900 
901 
902 void
903 display_crtc_set(uint8 crtcID, display_mode* mode)
904 {
905 	display_timing& displayTiming = mode->timing;
906 
907 	TRACE("%s called to do %dx%d\n",
908 		__func__, displayTiming.h_display, displayTiming.v_display);
909 
910 	SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION args;
911 	int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_Timing);
912 	uint16 misc = 0;
913 
914 	memset(&args, 0, sizeof(args));
915 
916 	args.usH_Total = B_HOST_TO_LENDIAN_INT16(displayTiming.h_total);
917 	args.usH_Disp = B_HOST_TO_LENDIAN_INT16(displayTiming.h_display);
918 	args.usH_SyncStart = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_start);
919 	args.usH_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_end
920 		- displayTiming.h_sync_start);
921 
922 	args.usV_Total = B_HOST_TO_LENDIAN_INT16(displayTiming.v_total);
923 	args.usV_Disp = B_HOST_TO_LENDIAN_INT16(displayTiming.v_display);
924 	args.usV_SyncStart = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_start);
925 	args.usV_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_end
926 		- displayTiming.v_sync_start);
927 
928 	args.ucOverscanRight = 0;
929 	args.ucOverscanLeft = 0;
930 	args.ucOverscanBottom = 0;
931 	args.ucOverscanTop = 0;
932 
933 	if ((displayTiming.flags & B_POSITIVE_HSYNC) == 0)
934 		misc |= ATOM_HSYNC_POLARITY;
935 	if ((displayTiming.flags & B_POSITIVE_VSYNC) == 0)
936 		misc |= ATOM_VSYNC_POLARITY;
937 
938 	args.susModeMiscInfo.usAccess = B_HOST_TO_LENDIAN_INT16(misc);
939 	args.ucCRTC = crtcID;
940 
941 	atom_execute_table(gAtomContext, index, (uint32*)&args);
942 }
943 
944 
945 void
946 display_crtc_set_dtd(uint8 crtcID, display_mode* mode)
947 {
948 	display_timing& displayTiming = mode->timing;
949 
950 	TRACE("%s called to do %dx%d\n", __func__,
951 		displayTiming.h_display, displayTiming.v_display);
952 
953 	SET_CRTC_USING_DTD_TIMING_PARAMETERS args;
954 	int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_UsingDTDTiming);
955 	uint16 misc = 0;
956 
957 	memset(&args, 0, sizeof(args));
958 
959 	// Note: the code below assumes H & V borders are both zero
960 	uint16 blankStart
961 		= MIN(displayTiming.h_sync_start, displayTiming.h_display);
962 	uint16 blankEnd
963 		= MAX(displayTiming.h_sync_end, displayTiming.h_total);
964 	args.usH_Size = B_HOST_TO_LENDIAN_INT16(displayTiming.h_display);
965 	args.usH_Blanking_Time = B_HOST_TO_LENDIAN_INT16(blankEnd - blankStart);
966 
967 	blankStart = MIN(displayTiming.v_sync_start, displayTiming.v_display);
968 	blankEnd = MAX(displayTiming.v_sync_end, displayTiming.v_total);
969 	args.usV_Size = B_HOST_TO_LENDIAN_INT16(displayTiming.v_display);
970 	args.usV_Blanking_Time = B_HOST_TO_LENDIAN_INT16(blankEnd - blankStart);
971 
972 	args.usH_SyncOffset = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_start
973 		- displayTiming.h_display);
974 	args.usH_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_end
975 		- displayTiming.h_sync_start);
976 
977 	args.usV_SyncOffset = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_start
978 		- displayTiming.v_display);
979 	args.usV_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_end
980 		- displayTiming.v_sync_start);
981 
982 	args.ucH_Border = 0;
983 	args.ucV_Border = 0;
984 
985 	if ((displayTiming.flags & B_POSITIVE_HSYNC) == 0)
986 		misc |= ATOM_HSYNC_POLARITY;
987 	if ((displayTiming.flags & B_POSITIVE_VSYNC) == 0)
988 		misc |= ATOM_VSYNC_POLARITY;
989 
990 	args.susModeMiscInfo.usAccess = B_HOST_TO_LENDIAN_INT16(misc);
991 	args.ucCRTC = crtcID;
992 
993 	atom_execute_table(gAtomContext, index, (uint32*)&args);
994 }
995 
996 
997 void
998 display_crtc_ss(pll_info* pll, int command)
999 {
1000 	TRACE("%s\n", __func__);
1001 	radeon_shared_info &info = *gInfo->shared_info;
1002 
1003 	if (command == ATOM_ENABLE) {
1004 		if (pll->ssPercentage == 0) {
1005 			TRACE("%s: ssPercentage 0, ignoring SS request\n", __func__);
1006 			return;
1007 		}
1008 		if ((pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) {
1009 			TRACE("%s: external SS, ignoring SS request\n", __func__);
1010 			return;
1011 		}
1012 	} else {
1013 		if (pll_usage_count(pll->id) > 1) {
1014 			// TODO: Check if PLL has SS enabled on any other displays, if so
1015 			// we need to also skip this function.
1016 			TRACE("%s: TODO: shared PLL detected!\n", __func__);
1017 		}
1018 	}
1019 
1020 	int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL);
1021 
1022 	union enableSS {
1023 		ENABLE_LVDS_SS_PARAMETERS lvds_ss;
1024 		ENABLE_LVDS_SS_PARAMETERS_V2 lvds_ss_2;
1025 		ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION v1;
1026 		ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 v2;
1027 		ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 v3;
1028 	};
1029 
1030 	union enableSS args;
1031 	memset(&args, 0, sizeof(args));
1032 
1033 	if (info.dceMajor >= 5) {
1034 		args.v3.usSpreadSpectrumAmountFrac = B_HOST_TO_LENDIAN_INT16(0);
1035 		args.v3.ucSpreadSpectrumType
1036 			= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
1037 		switch (pll->id) {
1038 			case ATOM_PPLL1:
1039 				args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P1PLL;
1040 				break;
1041 			case ATOM_PPLL2:
1042 				args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P2PLL;
1043 				break;
1044 			case ATOM_DCPLL:
1045 				args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_DCPLL;
1046 				break;
1047 			case ATOM_PPLL_INVALID:
1048 				return;
1049 			default:
1050 				ERROR("%s: BUG: Invalid PLL ID!\n", __func__);
1051 				return;
1052 		}
1053 		args.v3.usSpreadSpectrumAmount = B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
1054 		args.v3.usSpreadSpectrumStep = B_HOST_TO_LENDIAN_INT16(pll->ssStep);
1055 		args.v3.ucEnable = command;
1056 	} else if (info.dceMajor >= 4) {
1057 		args.v2.usSpreadSpectrumPercentage
1058 			= B_HOST_TO_LENDIAN_INT16(pll->ssPercentage);
1059 		args.v2.ucSpreadSpectrumType
1060 			= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
1061 		switch (pll->id) {
1062 			case ATOM_PPLL1:
1063 				args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_P1PLL;
1064 				break;
1065 			case ATOM_PPLL2:
1066 				args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P2PLL;
1067 				break;
1068 			case ATOM_DCPLL:
1069 				args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_DCPLL;
1070 				break;
1071 			case ATOM_PPLL_INVALID:
1072 				return;
1073 			default:
1074 				ERROR("%s: BUG: Invalid PLL ID!\n", __func__);
1075 				return;
1076 		}
1077 		args.v2.usSpreadSpectrumAmount = B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
1078 		args.v2.usSpreadSpectrumStep = B_HOST_TO_LENDIAN_INT16(pll->ssStep);
1079 		args.v2.ucEnable = command;
1080 	} else if (info.dceMajor >= 3) {
1081 		args.v1.usSpreadSpectrumPercentage
1082 			= B_HOST_TO_LENDIAN_INT16(pll->ssPercentage);
1083 		args.v1.ucSpreadSpectrumType
1084 			= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
1085 		args.v1.ucSpreadSpectrumStep = pll->ssStep;
1086 		args.v1.ucSpreadSpectrumDelay = pll->ssDelay;
1087 		args.v1.ucSpreadSpectrumRange = pll->ssRange;
1088 		args.v1.ucPpll = pll->id;
1089 		args.v1.ucEnable = command;
1090 	} else if (info.dceMajor >= 2) {
1091 		if (command == ATOM_DISABLE) {
1092 			radeon_gpu_ss_control(pll, false);
1093 			return;
1094 		}
1095 		args.lvds_ss_2.usSpreadSpectrumPercentage
1096 			= B_HOST_TO_LENDIAN_INT16(pll->ssPercentage);
1097 		args.lvds_ss_2.ucSpreadSpectrumType
1098 			= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
1099 		args.lvds_ss_2.ucSpreadSpectrumStep = pll->ssStep;
1100 		args.lvds_ss_2.ucSpreadSpectrumDelay = pll->ssDelay;
1101 		args.lvds_ss_2.ucSpreadSpectrumRange = pll->ssRange;
1102 		args.lvds_ss_2.ucEnable = command;
1103 	} else {
1104 		ERROR("%s: TODO: Old card SS control\n", __func__);
1105 		return;
1106 	}
1107 
1108 	atom_execute_table(gAtomContext, index, (uint32*)&args);
1109 }
1110 
1111 
1112 void
1113 display_crtc_power(uint8 crtcID, int command)
1114 {
1115 	TRACE("%s\n", __func__);
1116 	int index = GetIndexIntoMasterTable(COMMAND, EnableCRTC);
1117 	ENABLE_CRTC_PS_ALLOCATION args;
1118 
1119 	memset(&args, 0, sizeof(args));
1120 
1121 	args.ucCRTC = crtcID;
1122 	args.ucEnable = command;
1123 
1124 	atom_execute_table(gAtomContext, index, (uint32*)&args);
1125 }
1126 
1127 
1128 void
1129 display_crtc_memreq(uint8 crtcID, int command)
1130 {
1131 	TRACE("%s\n", __func__);
1132 	int index = GetIndexIntoMasterTable(COMMAND, EnableCRTCMemReq);
1133 	ENABLE_CRTC_PS_ALLOCATION args;
1134 
1135 	memset(&args, 0, sizeof(args));
1136 
1137 	args.ucCRTC = crtcID;
1138 	args.ucEnable = command;
1139 
1140 	atom_execute_table(gAtomContext, index, (uint32*)&args);
1141 }
1142