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
31extern "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 */
41status_t
42init_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
219status_t
220detect_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
244status_t
245detect_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
404void
405debug_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
430uint32
431display_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
506void
507display_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
524void
525display_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
545void
546display_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
561void
562display_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
593void
594display_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
658void
659display_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
694void
695display_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
902void
903display_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
945void
946display_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
997void
998display_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
1112void
1113display_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
1128void
1129display_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