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