xref: /haiku/src/add-ons/accelerants/radeon_hd/display.cpp (revision 9130cabf87695d0a22c62f90124726673b28821e)
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 = R600_CRTC0_REGISTER_OFFSET;
117 				regs->vgaControl = AVIVO_D1VGA_CONTROL;
118 				regs->grphPrimarySurfaceAddrHigh
119 					= D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH;
120 				break;
121 			case 1:
122 				offset = R600_CRTC1_REGISTER_OFFSET;
123 				regs->vgaControl = AVIVO_D2VGA_CONTROL;
124 				regs->grphPrimarySurfaceAddrHigh
125 					= 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 = D1GRPH_SWAP_CNTL + offset;
138 
139 		regs->grphPrimarySurfaceAddr
140 			= D1GRPH_PRIMARY_SURFACE_ADDRESS + offset;
141 		regs->grphSecondarySurfaceAddr
142 			= 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 = D1GRPH_SWAP_CNTL + offset;
181 
182 		regs->grphPrimarySurfaceAddr
183 			= D1GRPH_PRIMARY_SURFACE_ADDRESS + offset;
184 		regs->grphSecondarySurfaceAddr
185 			= 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]->edid_info;
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]->vfreq_min = range.min_v;   /* in Hz */
232 			gDisplay[crtid]->vfreq_max = range.max_v;
233 			gDisplay[crtid]->hfreq_min = range.min_h;   /* in kHz */
234 			gDisplay[crtid]->hfreq_max = 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]->active = false;
249 		gDisplay[id]->found_ranges = false;
250 	}
251 
252 	uint32 displayIndex = 0;
253 	for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
254 		if (gConnector[id]->valid == false)
255 			continue;
256 		if (displayIndex >= MAX_DISPLAY)
257 			continue;
258 
259 		// TODO: As DP aux transactions don't work yet, just use LVDS as a hack
260 		#if 0
261 		if (gConnector[id]->encoder.isDPBridge == true) {
262 			// If this is a DisplayPort Bridge, setup ddc on bus
263 			// TRAVIS (LVDS) or NUTMEG (VGA)
264 			TRACE("%s: is bridge, performing bridge DDC setup\n", __func__);
265 			encoder_external_setup(id, 23860,
266 				EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP);
267 		} else if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) {
268 		#endif
269 		if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) {
270 			// If plain (non-DP) laptop LVDS, read mode info from AtomBIOS
271 			//TRACE("%s: non-DP laptop LVDS detected\n", __func__);
272 			gDisplay[displayIndex]->active
273 				= connector_read_mode_lvds(id,
274 					&gDisplay[displayIndex]->preferredMode);
275 		}
276 
277 		if (gDisplay[displayIndex]->active == false) {
278 			TRACE("%s: bit-banging ddc for edid on connector %" B_PRIu32 "\n",
279 				__func__, id);
280 			// Lets try bit-banging edid from connector
281 			gDisplay[displayIndex]->active =
282 				connector_read_edid(id, &gDisplay[displayIndex]->edid_info);
283 
284 			if (gConnector[id]->encoder.type == VIDEO_ENCODER_TVDAC
285 				|| gConnector[id]->encoder.type == VIDEO_ENCODER_DAC) {
286 				// analog? with valid EDID? lets make sure there is load.
287 				// There is only one ddc communications path on DVI-I
288 				if (encoder_analog_load_detect(id) != true) {
289 					TRACE("%s: no analog load on EDID valid connector "
290 						"#%" B_PRIu32 "\n", __func__, id);
291 					gDisplay[displayIndex]->active = false;
292 				}
293 			}
294 		}
295 
296 		if (gDisplay[displayIndex]->active != true) {
297 			// Nothing interesting here, move along
298 			continue;
299 		}
300 
301 		// We found a valid / active display
302 
303 		gDisplay[displayIndex]->connectorIndex = id;
304 			// Populate physical connector index from gConnector
305 
306 		init_registers(gDisplay[displayIndex]->regs, displayIndex);
307 
308 		if (gDisplay[displayIndex]->preferredMode.virtual_width > 0) {
309 			// Found a single preferred mode
310 			gDisplay[displayIndex]->found_ranges = false;
311 		} else {
312 			// Use edid data and pull ranges
313 			if (detect_crt_ranges(displayIndex) == B_OK)
314 				gDisplay[displayIndex]->found_ranges = true;
315 		}
316 
317 		displayIndex++;
318 	}
319 
320 	// fallback if no active monitors were found
321 	if (displayIndex == 0) {
322 		ERROR("%s: ERROR: 0 attached monitors were found on display connectors."
323 			" Injecting first connector as a last resort.\n", __func__);
324 		for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
325 			// skip TV DAC connectors as likely fallback isn't for TV
326 			if (gConnector[id]->encoder.type == VIDEO_ENCODER_TVDAC)
327 				continue;
328 			gDisplay[0]->active = true;
329 			gDisplay[0]->connectorIndex = id;
330 			init_registers(gDisplay[0]->regs, 0);
331 			if (detect_crt_ranges(0) == B_OK)
332 				gDisplay[0]->found_ranges = true;
333 			break;
334 		}
335 	}
336 
337 	return B_OK;
338 }
339 
340 
341 void
342 debug_displays()
343 {
344 	TRACE("Currently detected monitors===============\n");
345 	for (uint32 id = 0; id < MAX_DISPLAY; id++) {
346 		ERROR("Display #%" B_PRIu32 " active = %s\n",
347 			id, gDisplay[id]->active ? "true" : "false");
348 
349 		uint32 connectorIndex = gDisplay[id]->connectorIndex;
350 
351 		if (gDisplay[id]->active) {
352 			uint32 connectorType = gConnector[connectorIndex]->type;
353 			uint32 encoderType = gConnector[connectorIndex]->encoder.type;
354 			ERROR(" + connector ID:   %" B_PRIu32 "\n", connectorIndex);
355 			ERROR(" + connector type: %s\n", get_connector_name(connectorType));
356 			ERROR(" + encoder type:   %s\n", get_encoder_name(encoderType));
357 			ERROR(" + limits: Vert Min/Max: %" B_PRIu32 "/%" B_PRIu32"\n",
358 				gDisplay[id]->vfreq_min, gDisplay[id]->vfreq_max);
359 			ERROR(" + limits: Horz Min/Max: %" B_PRIu32 "/%" B_PRIu32"\n",
360 				gDisplay[id]->hfreq_min, gDisplay[id]->hfreq_max);
361 		}
362 	}
363 	TRACE("==========================================\n");
364 }
365 
366 
367 uint32
368 display_get_encoder_mode(uint32 connectorIndex)
369 {
370 	switch (gConnector[connectorIndex]->type) {
371 		case VIDEO_CONNECTOR_DVII:
372 		case VIDEO_CONNECTOR_HDMIB: /* HDMI-B is DL-DVI; analog works fine */
373 			// TODO: if audio detected on edid and DCE4, ATOM_ENCODER_MODE_DVI
374 			//        if audio detected on edid not DCE4, ATOM_ENCODER_MODE_HDMI
375 			// if (gConnector[connectorIndex]->use_digital)
376 			//	return ATOM_ENCODER_MODE_DVI;
377 			// else
378 				return ATOM_ENCODER_MODE_CRT;
379 			break;
380 		case VIDEO_CONNECTOR_DVID:
381 		case VIDEO_CONNECTOR_HDMIA:
382 		default:
383 			// TODO: if audio detected on edid and DCE4, ATOM_ENCODER_MODE_DVI
384 			//        if audio detected on edid not DCE4, ATOM_ENCODER_MODE_HDMI
385 			return ATOM_ENCODER_MODE_DVI;
386 		case VIDEO_CONNECTOR_LVDS:
387 			return ATOM_ENCODER_MODE_LVDS;
388 		case VIDEO_CONNECTOR_DP:
389 			// dig_connector = radeon_connector->con_priv;
390 			// if ((dig_connector->dp_sink_type
391 			//	== CONNECTOR_OBJECT_ID_DISPLAYPORT)
392 			// 	|| (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
393 			// 	return ATOM_ENCODER_MODE_DP;
394 			// }
395 			// TODO: if audio detected on edid and DCE4, ATOM_ENCODER_MODE_DVI
396 			//        if audio detected on edid not DCE4, ATOM_ENCODER_MODE_HDMI
397 			return ATOM_ENCODER_MODE_DVI;
398 		case VIDEO_CONNECTOR_EDP:
399 			return ATOM_ENCODER_MODE_DP;
400 		case VIDEO_CONNECTOR_DVIA:
401 		case VIDEO_CONNECTOR_VGA:
402 			return ATOM_ENCODER_MODE_CRT;
403 		case VIDEO_CONNECTOR_COMPOSITE:
404 		case VIDEO_CONNECTOR_SVIDEO:
405 		case VIDEO_CONNECTOR_9DIN:
406 			return ATOM_ENCODER_MODE_TV;
407 	}
408 }
409 
410 
411 void
412 display_crtc_lock(uint8 crtcID, int command)
413 {
414 	TRACE("%s\n", __func__);
415 	ENABLE_CRTC_PS_ALLOCATION args;
416 	int index
417 		= GetIndexIntoMasterTable(COMMAND, UpdateCRTC_DoubleBufferRegisters);
418 
419 	memset(&args, 0, sizeof(args));
420 
421 	args.ucCRTC = crtcID;
422 	args.ucEnable = command;
423 
424 	atom_execute_table(gAtomContext, index, (uint32*)&args);
425 }
426 
427 
428 void
429 display_crtc_blank(uint8 crtcID, int command)
430 {
431 	TRACE("%s\n", __func__);
432 	BLANK_CRTC_PS_ALLOCATION args;
433 	int index = GetIndexIntoMasterTable(COMMAND, BlankCRTC);
434 
435 	memset(&args, 0, sizeof(args));
436 
437 	args.ucCRTC = crtcID;
438 	args.ucBlanking = command;
439 
440 	// DEBUG: Radeon red to know when we are blanked :)
441 	args.usBlackColorRCr = 255;
442 	args.usBlackColorGY = 0;
443 	args.usBlackColorBCb = 0;
444 
445 	atom_execute_table(gAtomContext, index, (uint32*)&args);
446 }
447 
448 
449 void
450 display_crtc_scale(uint8 crtcID, display_mode* mode)
451 {
452 	TRACE("%s\n", __func__);
453 	ENABLE_SCALER_PS_ALLOCATION args;
454 	int index = GetIndexIntoMasterTable(COMMAND, EnableScaler);
455 
456 	memset(&args, 0, sizeof(args));
457 
458 	args.ucScaler = crtcID;
459 	args.ucEnable = ATOM_SCALER_DISABLE;
460 
461 	atom_execute_table(gAtomContext, index, (uint32*)&args);
462 }
463 
464 
465 void
466 display_crtc_fb_set(uint8 crtcID, display_mode* mode)
467 {
468 	radeon_shared_info &info = *gInfo->shared_info;
469 	register_info* regs = gDisplay[crtcID]->regs;
470 
471 	uint32 fbSwap;
472 	if (info.dceMajor >= 4)
473 		fbSwap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_NONE);
474 	else
475 		fbSwap = R600_D1GRPH_SWAP_ENDIAN_NONE;
476 
477 	uint32 fbFormat;
478 
479 	uint32 bytesPerPixel;
480 	uint32 bitsPerPixel;
481 
482 	switch (mode->space) {
483 		case B_CMAP8:
484 			bytesPerPixel = 1;
485 			bitsPerPixel = 8;
486 			if (info.dceMajor >= 4) {
487 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_8BPP)
488 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_INDEXED));
489 			} else {
490 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_8BPP
491 					| AVIVO_D1GRPH_CONTROL_8BPP_INDEXED;
492 			}
493 			break;
494 		case B_RGB15_LITTLE:
495 			bytesPerPixel = 2;
496 			bitsPerPixel = 15;
497 			if (info.dceMajor >= 4) {
498 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP)
499 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB1555));
500 			} else {
501 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP
502 					| AVIVO_D1GRPH_CONTROL_16BPP_ARGB1555;
503 			}
504 			break;
505 		case B_RGB16_LITTLE:
506 			bytesPerPixel = 2;
507 			bitsPerPixel = 16;
508 
509 			if (info.dceMajor >= 4) {
510 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP)
511 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB565));
512 				#ifdef __POWERPC__
513 				fbSwap
514 					= EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16);
515 				#endif
516 			} else {
517 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP
518 					| AVIVO_D1GRPH_CONTROL_16BPP_RGB565;
519 				#ifdef __POWERPC__
520 				fbSwap = R600_D1GRPH_SWAP_ENDIAN_16BIT;
521 				#endif
522 			}
523 			break;
524 		case B_RGB24_LITTLE:
525 		case B_RGB32_LITTLE:
526 		default:
527 			bytesPerPixel = 4;
528 			bitsPerPixel = 32;
529 			if (info.dceMajor >= 4) {
530 				fbFormat = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP)
531 					| EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB8888));
532 				#ifdef __POWERPC__
533 				fbSwap
534 					= EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN32);
535 				#endif
536 			} else {
537 				fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_32BPP
538 					| AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888;
539 				#ifdef __POWERPC__
540 				fbSwap = R600_D1GRPH_SWAP_ENDIAN_32BIT;
541 				#endif
542 			}
543 			break;
544 	}
545 
546 	uint32 bytesPerRow = mode->virtual_width * bytesPerPixel;
547 
548 	Write32(OUT, regs->vgaControl, 0);
549 
550 	uint64 fbAddress = gInfo->fb.vramStart;
551 
552 	TRACE("%s: Framebuffer at: 0x%" B_PRIX64 "\n", __func__, fbAddress);
553 
554 	if (info.chipsetID >= RADEON_RV770) {
555 		TRACE("%s: Set SurfaceAddress High: 0x%" B_PRIX32 "\n",
556 			__func__, (fbAddress >> 32) & 0xf);
557 
558 		Write32(OUT, regs->grphPrimarySurfaceAddrHigh,
559 			(fbAddress >> 32) & 0xf);
560 		Write32(OUT, regs->grphSecondarySurfaceAddrHigh,
561 			(fbAddress >> 32) & 0xf);
562 	}
563 
564 	TRACE("%s: Set SurfaceAddress: 0x%" B_PRIX32 "\n",
565 		__func__, (fbAddress & 0xFFFFFFFF));
566 
567 	Write32(OUT, regs->grphPrimarySurfaceAddr, (fbAddress & 0xFFFFFFFF));
568 	Write32(OUT, regs->grphSecondarySurfaceAddr, (fbAddress & 0xFFFFFFFF));
569 
570 	if (info.chipsetID >= RADEON_R600) {
571 		Write32(CRT, regs->grphControl, fbFormat);
572 		Write32(CRT, regs->grphSwapControl, fbSwap);
573 	}
574 
575 	Write32(CRT, regs->grphSurfaceOffsetX, 0);
576 	Write32(CRT, regs->grphSurfaceOffsetY, 0);
577 	Write32(CRT, regs->grphXStart, 0);
578 	Write32(CRT, regs->grphYStart, 0);
579 	Write32(CRT, regs->grphXEnd, mode->virtual_width);
580 	Write32(CRT, regs->grphYEnd, mode->virtual_height);
581 	Write32(CRT, regs->grphPitch, (bytesPerRow / 4));
582 
583 	Write32(CRT, regs->grphEnable, 1);
584 		// Enable Frame buffer
585 
586 	Write32(CRT, regs->modeDesktopHeight, mode->virtual_height);
587 
588 	uint32 viewport_w = mode->timing.h_display;
589 	uint32 viewport_h = (mode->timing.v_display + 1) & ~1;
590 
591 	Write32(CRT, regs->viewportStart, 0);
592 	Write32(CRT, regs->viewportSize,
593 		(viewport_w << 16) | viewport_h);
594 
595 	// Pageflip setup
596 	if (info.dceMajor >= 4) {
597 		uint32 tmp
598 			= Read32(OUT, EVERGREEN_GRPH_FLIP_CONTROL + regs->crtcOffset);
599 		tmp &= ~EVERGREEN_GRPH_SURFACE_UPDATE_H_RETRACE_EN;
600 		Write32(OUT, EVERGREEN_GRPH_FLIP_CONTROL + regs->crtcOffset, tmp);
601 
602 		Write32(OUT, EVERGREEN_MASTER_UPDATE_MODE + regs->crtcOffset, 0);
603 			// Pageflip to happen anywhere in vblank
604 
605 	} else {
606 		uint32 tmp = Read32(OUT, AVIVO_D1GRPH_FLIP_CONTROL + regs->crtcOffset);
607 		tmp &= ~AVIVO_D1GRPH_SURFACE_UPDATE_H_RETRACE_EN;
608 		Write32(OUT, AVIVO_D1GRPH_FLIP_CONTROL + regs->crtcOffset, tmp);
609 
610 		Write32(OUT, AVIVO_D1MODE_MASTER_UPDATE_MODE + regs->crtcOffset, 0);
611 			// Pageflip to happen anywhere in vblank
612 	}
613 
614 	// update shared info
615 	gInfo->shared_info->bytes_per_row = bytesPerRow;
616 	gInfo->shared_info->current_mode = *mode;
617 	gInfo->shared_info->bits_per_pixel = bitsPerPixel;
618 }
619 
620 
621 void
622 display_crtc_set(uint8 crtcID, display_mode* mode)
623 {
624 	display_timing& displayTiming = mode->timing;
625 
626 	TRACE("%s called to do %dx%d\n",
627 		__func__, displayTiming.h_display, displayTiming.v_display);
628 
629 	SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION args;
630 	int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_Timing);
631 	uint16 misc = 0;
632 
633 	memset(&args, 0, sizeof(args));
634 
635 	args.usH_Total = B_HOST_TO_LENDIAN_INT16(displayTiming.h_total);
636 	args.usH_Disp = B_HOST_TO_LENDIAN_INT16(displayTiming.h_display);
637 	args.usH_SyncStart = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_start);
638 	args.usH_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_end
639 		- displayTiming.h_sync_start);
640 
641 	args.usV_Total = B_HOST_TO_LENDIAN_INT16(displayTiming.v_total);
642 	args.usV_Disp = B_HOST_TO_LENDIAN_INT16(displayTiming.v_display);
643 	args.usV_SyncStart = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_start);
644 	args.usV_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_end
645 		- displayTiming.v_sync_start);
646 
647 	args.ucOverscanRight = 0;
648 	args.ucOverscanLeft = 0;
649 	args.ucOverscanBottom = 0;
650 	args.ucOverscanTop = 0;
651 
652 	if ((displayTiming.flags & B_POSITIVE_HSYNC) == 0)
653 		misc |= ATOM_HSYNC_POLARITY;
654 	if ((displayTiming.flags & B_POSITIVE_VSYNC) == 0)
655 		misc |= ATOM_VSYNC_POLARITY;
656 
657 	args.susModeMiscInfo.usAccess = B_HOST_TO_LENDIAN_INT16(misc);
658 	args.ucCRTC = crtcID;
659 
660 	atom_execute_table(gAtomContext, index, (uint32*)&args);
661 }
662 
663 
664 void
665 display_crtc_set_dtd(uint8 crtcID, display_mode* mode)
666 {
667 	display_timing& displayTiming = mode->timing;
668 
669 	TRACE("%s called to do %dx%d\n",
670 		__func__, displayTiming.h_display, displayTiming.v_display);
671 
672 	SET_CRTC_USING_DTD_TIMING_PARAMETERS args;
673 	int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_UsingDTDTiming);
674 	uint16 misc = 0;
675 
676 	memset(&args, 0, sizeof(args));
677 
678 	uint16 blankStart
679 		= MIN(displayTiming.h_sync_start, displayTiming.h_display);
680 	uint16 blankEnd
681 		= MAX(displayTiming.h_sync_end, displayTiming.h_total);
682 	args.usH_Size = B_HOST_TO_LENDIAN_INT16(displayTiming.h_display);
683 	args.usH_Blanking_Time = B_HOST_TO_LENDIAN_INT16(blankEnd - blankStart);
684 
685 	blankStart = MIN(displayTiming.v_sync_start, displayTiming.v_display);
686 	blankEnd = MAX(displayTiming.v_sync_end, displayTiming.v_total);
687 	args.usV_Size = B_HOST_TO_LENDIAN_INT16(displayTiming.v_display);
688 	args.usV_Blanking_Time = B_HOST_TO_LENDIAN_INT16(blankEnd - blankStart);
689 
690 	args.usH_SyncOffset = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_start
691 		- displayTiming.h_display);
692 	args.usH_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.h_sync_end
693 		- displayTiming.h_sync_start);
694 
695 	args.usV_SyncOffset = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_start
696 		- displayTiming.v_display);
697 	args.usV_SyncWidth = B_HOST_TO_LENDIAN_INT16(displayTiming.v_sync_end
698 		- displayTiming.v_sync_start);
699 
700 	args.ucH_Border = 0;
701 	args.ucV_Border = 0;
702 
703 	if ((displayTiming.flags & B_POSITIVE_HSYNC) == 0)
704 		misc |= ATOM_HSYNC_POLARITY;
705 	if ((displayTiming.flags & B_POSITIVE_VSYNC) == 0)
706 		misc |= ATOM_VSYNC_POLARITY;
707 
708 	args.susModeMiscInfo.usAccess = B_HOST_TO_LENDIAN_INT16(misc);
709 	args.ucCRTC = crtcID;
710 
711 	atom_execute_table(gAtomContext, index, (uint32*)&args);
712 }
713 
714 
715 void
716 display_crtc_power(uint8 crtcID, int command)
717 {
718 	TRACE("%s\n", __func__);
719 	int index = GetIndexIntoMasterTable(COMMAND, EnableCRTC);
720 	ENABLE_CRTC_PS_ALLOCATION args;
721 
722 	memset(&args, 0, sizeof(args));
723 
724 	args.ucCRTC = crtcID;
725 	args.ucEnable = command;
726 
727 	atom_execute_table(gAtomContext, index, (uint32*)&args);
728 }
729 
730 
731 void
732 display_crtc_memreq(uint8 crtcID, int command)
733 {
734 	TRACE("%s\n", __func__);
735 	int index = GetIndexIntoMasterTable(COMMAND, EnableCRTCMemReq);
736 	ENABLE_CRTC_PS_ALLOCATION args;
737 
738 	memset(&args, 0, sizeof(args));
739 
740 	args.ucCRTC = crtcID;
741 	args.ucEnable = command;
742 
743 	atom_execute_table(gAtomContext, index, (uint32*)&args);
744 }
745