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