xref: /haiku/src/add-ons/accelerants/radeon_hd/mode.cpp (revision 4cc4f7bb1845789f95d5ebc68cbb0b859e72f6ff)
1 /*
2  * Copyright 2006-2011, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Support for i915 chipset and up based on the X driver,
6  * Copyright 2006-2007 Intel Corporation.
7  *
8  * Authors:
9  *		Axel Dörfler, axeld@pinc-software.de
10  *		Alexander von Gluck, kallisti5@unixzen.com
11  */
12 
13 
14 #include "accelerant_protos.h"
15 #include "accelerant.h"
16 #include "utility.h"
17 #include "mode.h"
18 
19 #include <stdio.h>
20 #include <string.h>
21 #include <math.h>
22 
23 #include <create_display_modes.h>
24 
25 
26 #define TRACE_MODE
27 #ifdef TRACE_MODE
28 extern "C" void _sPrintf(const char *format, ...);
29 #	define TRACE(x...) _sPrintf("radeon_hd: " x)
30 #else
31 #	define TRACE(x...) ;
32 #endif
33 
34 
35 status_t
36 create_mode_list(void)
37 {
38 	const color_space kRadeonHDSpaces[] = {B_RGB32_LITTLE, B_RGB24_LITTLE,
39 		B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
40 
41 	gInfo->mode_list_area = create_display_modes("radeon HD modes",
42 		gInfo->shared_info->has_edid ? &gInfo->shared_info->edid_info : NULL,
43 		NULL, 0, kRadeonHDSpaces,
44 		sizeof(kRadeonHDSpaces) / sizeof(kRadeonHDSpaces[0]),
45 		is_mode_supported, &gInfo->mode_list, &gInfo->shared_info->mode_count);
46 	if (gInfo->mode_list_area < B_OK)
47 		return gInfo->mode_list_area;
48 
49 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
50 
51 	return B_OK;
52 }
53 
54 
55 //	#pragma mark -
56 
57 
58 uint32
59 radeon_accelerant_mode_count(void)
60 {
61 	TRACE("%s\n", __func__);
62 
63 	return gInfo->shared_info->mode_count;
64 }
65 
66 
67 status_t
68 radeon_get_mode_list(display_mode *modeList)
69 {
70 	TRACE("%s\n", __func__);
71 	memcpy(modeList, gInfo->mode_list,
72 		gInfo->shared_info->mode_count * sizeof(display_mode));
73 	return B_OK;
74 }
75 
76 
77 status_t
78 radeon_get_edid_info(void* info, size_t size, uint32* edid_version)
79 {
80 	TRACE("%s\n", __func__);
81 	if (!gInfo->shared_info->has_edid)
82 		return B_ERROR;
83 	if (size < sizeof(struct edid1_info))
84 		return B_BUFFER_OVERFLOW;
85 
86 	memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info));
87 	*edid_version = EDID_VERSION_1;
88 
89 	return B_OK;
90 }
91 
92 
93 static void
94 get_color_space_format(const display_mode &mode, uint32 &colorMode,
95 	uint32 &bytesPerRow, uint32 &bitsPerPixel)
96 {
97 	uint32 bytesPerPixel;
98 
99 	switch (mode.space) {
100 		case B_RGB32_LITTLE:
101 			colorMode = DISPLAY_CONTROL_RGB32;
102 			bytesPerPixel = 4;
103 			bitsPerPixel = 32;
104 			break;
105 		case B_RGB16_LITTLE:
106 			colorMode = DISPLAY_CONTROL_RGB16;
107 			bytesPerPixel = 2;
108 			bitsPerPixel = 16;
109 			break;
110 		case B_RGB15_LITTLE:
111 			colorMode = DISPLAY_CONTROL_RGB15;
112 			bytesPerPixel = 2;
113 			bitsPerPixel = 15;
114 			break;
115 		case B_CMAP8:
116 		default:
117 			colorMode = DISPLAY_CONTROL_CMAP8;
118 			bytesPerPixel = 1;
119 			bitsPerPixel = 8;
120 			break;
121 	}
122 
123 	bytesPerRow = mode.virtual_width * bytesPerPixel;
124 }
125 
126 
127 // Blacks the screen out, useful for mode setting
128 static void
129 CardBlankSet(bool blank)
130 {
131 	int blackColorReg;
132 	int blankControlReg;
133 
134 	blackColorReg = D1CRTC_BLACK_COLOR;
135 	blankControlReg = D1CRTC_BLANK_CONTROL;
136 
137 	Write32(CRT, blackColorReg, 0);
138 	Write32Mask(CRT, blankControlReg, blank ? 1 << 8 : 0, 1 << 8);
139 }
140 
141 
142 static void
143 CardFBSet(display_mode *mode)
144 {
145 	uint32 colorMode;
146 	uint32 bytesPerRow;
147 	uint32 bitsPerPixel;
148 
149 	get_color_space_format(*mode, colorMode, bytesPerRow, bitsPerPixel);
150 
151 	// Disable VGA mode to enable Radeon extended registers
152 	Write32Mask(VGA, VGA_RENDER_CONTROL, 0, 0x00030000);
153 	Write32Mask(VGA, VGA_MODE_CONTROL, 0, 0x00000030);
154 	Write32Mask(VGA, VGA_HDP_CONTROL, 0x00010010, 0x00010010);
155 	Write32Mask(VGA, gRegister->vgaControl, 0, D1VGA_MODE_ENABLE
156 		| D1VGA_TIMING_SELECT | D1VGA_SYNC_POLARITY_SELECT);
157 
158 	// disable R/B swap, disable tiling, disable 16bit alpha, etc.
159 	Write32Mask(CRT, gRegister->grphEnable, 1, 0x00000001);
160 	Write32(CRT, gRegister->grphControl, 0);
161 
162 	// set color mode on video card
163 	switch (mode->space) {
164 		case B_CMAP8:
165 			Write32Mask(CRT, gRegister->grphControl,
166 				0, 0x00000703);
167 			break;
168 		case B_RGB15_LITTLE:
169 			Write32Mask(CRT, gRegister->grphControl,
170 				0x000001, 0x00000703);
171 			break;
172 		case B_RGB16_LITTLE:
173 			Write32Mask(CRT, gRegister->grphControl,
174 				0x000101, 0x00000703);
175 			break;
176 		case B_RGB24_LITTLE:
177 		case B_RGB32_LITTLE:
178 		default:
179 			Write32Mask(CRT, gRegister->grphControl,
180 				0x000002, 0x00000703);
181 			break;
182 	}
183 
184 	Write32(CRT, gRegister->grphSwapControl, 0);
185 		// only for chipsets > r600
186 		// R5xx - RS690 case is GRPH_CONTROL bit 16
187 
188 	// framebuffersize = w * h * bpp  =  fb bits / 8 = bytes needed
189 
190 	uint64_t fbAddress = gInfo->shared_info->frame_buffer_phys;
191 
192 	// Tell GPU which frame buffer address to draw from
193 	if (gInfo->shared_info->device_chipset >= (uint16)(RADEON_R700 & 0x70)) {
194 		Write32(CRT, gRegister->grphPrimarySurfaceAddrHigh,
195 			(fbAddress >> 32) & 0xf);
196 		Write32(CRT, gRegister->grphSecondarySurfaceAddrHigh,
197 			(fbAddress >> 32) & 0xf);
198 	}
199 
200 	Write32(CRT, gRegister->grphPrimarySurfaceAddr,
201 		fbAddress & 0xffffffff);
202 	Write32(CRT, gRegister->grphSecondarySurfaceAddr,
203 		fbAddress & 0xffffffff);
204 
205 	Write32(CRT, gRegister->grphPitch, bytesPerRow / 4);
206 	Write32(CRT, gRegister->grphSurfaceOffsetX, 0);
207 	Write32(CRT, gRegister->grphSurfaceOffsetY, 0);
208 	Write32(CRT, gRegister->grphXStart, 0);
209 	Write32(CRT, gRegister->grphYStart, 0);
210 	Write32(CRT, gRegister->grphXEnd, mode->virtual_width);
211 	Write32(CRT, gRegister->grphYEnd, mode->virtual_height);
212 
213 	/* D1Mode registers */
214 	Write32(CRT, gRegister->modeDesktopHeight, mode->virtual_height);
215 
216 	// update shared info
217 	gInfo->shared_info->bytes_per_row = bytesPerRow;
218 	gInfo->shared_info->current_mode = *mode;
219 	gInfo->shared_info->bits_per_pixel = bitsPerPixel;
220 }
221 
222 
223 static void
224 CardModeSet(display_mode *mode)
225 {
226 	display_timing& displayTiming = mode->timing;
227 
228 	TRACE("%s called to do %dx%d\n",
229 		__func__, displayTiming.h_display, displayTiming.v_display);
230 
231 	// enable read requests
232 	Write32Mask(CRT, gRegister->grphControl, 0, 0x01000000);
233 
234 	// *** Horizontal
235 	Write32(CRT, gRegister->crtHTotal,
236 		displayTiming.h_total - 1);
237 
238 	#if 0
239 	// determine blanking based on passed modeline
240 	uint16 blankStart = displayTiming.h_display;
241 	uint16 blankEnd = displayTiming.h_total;
242 
243 	Write32(CRT, gRegister->crtHBlank,
244 		blankStart | (blankEnd << 16));
245 	#endif
246 
247 	Write32(CRT, gRegister->crtHSync,
248 		(displayTiming.h_sync_end - displayTiming.h_sync_start) << 16);
249 
250 	// set flag for neg. H sync. M76 Register Reference Guide 2-256
251 	Write32Mask(CRT, gRegister->crtHPolarity,
252 		displayTiming.flags & B_POSITIVE_HSYNC ? 0 : 1, 0x1);
253 
254 	// *** Vertical
255 	Write32(CRT, gRegister->crtVTotal,
256 		displayTiming.v_total - 1);
257 
258 	#if 0
259 	blankStart = displayTiming.v_display;
260 	blankEnd = displayTiming.v_total;
261 
262 	Write32(CRT, gRegister->crtVBlank,
263 		blankStart | (blankEnd << 16));
264 	#endif
265 
266 	// Set Interlace if specified within mode line
267 	if (displayTiming.flags & B_TIMING_INTERLACED) {
268 		Write32(CRT, gRegister->crtInterlace, 0x1);
269 		Write32(CRT, gRegister->modeDataFormat, 0x1);
270 	} else {
271 		Write32(CRT, gRegister->crtInterlace, 0x0);
272 		Write32(CRT, gRegister->modeDataFormat, 0x0);
273 	}
274 
275 	Write32(CRT, gRegister->crtVSync,
276 		(displayTiming.v_sync_end - displayTiming.v_sync_start) << 16);
277 
278 	// set flag for neg. V sync. M76 Register Reference Guide 2-258
279 	// we don't need a mask here as this is the only param for Vertical
280 	Write32(CRT, gRegister->crtVPolarity,
281 		displayTiming.flags & B_POSITIVE_VSYNC ? 0 : 1);
282 
283 	/*	set D1CRTC_HORZ_COUNT_BY2_EN to 0;
284 		should only be set to 1 on 30bpp DVI modes
285 	*/
286 	Write32Mask(CRT, gRegister->crtCountControl, 0x0, 0x1);
287 }
288 
289 
290 static void
291 CardModeScale(display_mode *mode)
292 {
293 	Write32(CRT, gRegister->viewportSize,
294 		mode->timing.v_display | (mode->timing.h_display << 16));
295 	Write32(CRT, gRegister->viewportStart, 0);
296 
297 	// For now, no overscan support
298 	Write32(CRT, D1MODE_EXT_OVERSCAN_LEFT_RIGHT,
299 		(0 << 16) | 0); // LEFT | RIGHT
300 	Write32(CRT, D1MODE_EXT_OVERSCAN_TOP_BOTTOM,
301 		(0 << 16) | 0); // TOP | BOTTOM
302 
303 	// No scaling
304 	Write32(CRT, gRegister->sclUpdate, (1<<16));// Lock
305 	Write32(CRT, gRegister->sclEnable, 0);
306 	Write32(CRT, gRegister->sclTapControl, 0);
307 	Write32(CRT, gRegister->modeCenter, 0);
308 	Write32(CRT, gRegister->sclUpdate, 0);		// Unlock
309 
310 	#if 0
311 	// Auto scale keeping aspect ratio
312 	Write32(CRT, regOffset + D1MODE_CENTER, 1);
313 
314 	Write32(CRT, regOffset + D1SCL_UPDATE, 0);
315 	Write32(CRT, regOffset + D1SCL_FLIP_CONTROL, 0);
316 
317 	Write32(CRT, regOffset + D1SCL_ENABLE, 1);
318 	Write32(CRT, regOffset + D1SCL_HVSCALE, 0x00010001);
319 
320 	Write32(CRT, regOffset + D1SCL_TAP_CONTROL, 0x00000101);
321 
322 	Write32(CRT, regOffset + D1SCL_HFILTER, 0x00030100);
323 	Write32(CRT, regOffset + D1SCL_VFILTER, 0x00030100);
324 
325 	Write32(CRT, regOffset + D1SCL_DITHER, 0x00001010);
326 	#endif
327 }
328 
329 
330 status_t
331 radeon_set_display_mode(display_mode *mode)
332 {
333 	int crtNumber = 0;
334 
335 	init_registers(crtNumber);
336 
337 	CardBlankSet(true);
338 	CardFBSet(mode);
339 	CardModeSet(mode);
340 	CardModeScale(mode);
341 	PLLSet(0, mode->timing.pixel_clock);
342 	PLLPower(0, RHD_POWER_ON);
343 	DACPower(0, RHD_POWER_ON);
344 	CardBlankSet(false);
345 
346 	int32 crtstatus = Read32(CRT, D1CRTC_STATUS);
347 	TRACE("CRT0 Status: 0x%X\n", crtstatus);
348 
349 	return B_OK;
350 }
351 
352 
353 status_t
354 radeon_get_display_mode(display_mode *_currentMode)
355 {
356 	TRACE("%s\n", __func__);
357 
358 	*_currentMode = gInfo->shared_info->current_mode;
359 	return B_OK;
360 }
361 
362 
363 status_t
364 radeon_get_frame_buffer_config(frame_buffer_config *config)
365 {
366 	TRACE("%s\n", __func__);
367 
368 	config->frame_buffer = gInfo->shared_info->frame_buffer;
369 	config->frame_buffer_dma = (uint8 *)gInfo->shared_info->frame_buffer_phys;
370 
371 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
372 
373 	return B_OK;
374 }
375 
376 
377 status_t
378 radeon_get_pixel_clock_limits(display_mode *mode, uint32 *_low, uint32 *_high)
379 {
380 	TRACE("%s\n", __func__);
381 
382 	if (_low != NULL) {
383 		// lower limit of about 48Hz vertical refresh
384 		uint32 totalClocks = (uint32)mode->timing.h_total
385 			*(uint32)mode->timing.v_total;
386 		uint32 low = (totalClocks * 48L) / 1000L;
387 
388 		if (low < gInfo->shared_info->pll_info.min_frequency)
389 			low = gInfo->shared_info->pll_info.min_frequency;
390 		else if (low > gInfo->shared_info->pll_info.max_frequency)
391 			return B_ERROR;
392 
393 		*_low = low;
394 	}
395 
396 	if (_high != NULL)
397 		*_high = gInfo->shared_info->pll_info.max_frequency;
398 
399 	//*_low = 48L;
400 	//*_high = 100 * 1000000L;
401 	return B_OK;
402 }
403 
404 
405 bool
406 is_mode_supported(display_mode *mode)
407 {
408 	// Validate modeline is within a sane range
409 	if (is_mode_sane(mode) != B_OK)
410 		return false;
411 
412 	// TODO : Look at min and max monitor freqs and verify selected
413 	// mode is within tolerances.
414 	#if 0
415 	int crtid = 0;
416 
417 	edid1_detailed_monitor *monitor
418 		= &gInfo->shared_info->edid_info.detailed_monitor[crtid + 1];
419 	edid1_monitor_range& range = monitor->data.monitor_range;
420 
421 	TRACE("%s CRT Min/Max H %d/%d; CRT Min/Max V %d/%d\n", __func__,
422 		range.min_h, range.max_h, range.min_v, range.max_v);
423 	#endif
424 
425 	return true;
426 }
427 
428 
429 /*
430  * A quick sanity check of the provided display_mode
431  */
432 status_t
433 is_mode_sane(display_mode *mode)
434 {
435 	// horizontal timing
436 	// validate h_sync_start is less then h_sync_end
437 	if (mode->timing.h_sync_start > mode->timing.h_sync_end) {
438 		TRACE("%s: ERROR: (%dx%d) "
439 			"received h_sync_start greater then h_sync_end!\n",
440 			__func__, mode->timing.h_display, mode->timing.v_display);
441 		return B_ERROR;
442 	}
443 	// validate h_total is greater then h_display
444 	if (mode->timing.h_total < mode->timing.h_display) {
445 		TRACE("%s: ERROR: (%dx%d) "
446 			"received h_total greater then h_display!\n",
447 			__func__, mode->timing.h_display, mode->timing.v_display);
448 		return B_ERROR;
449 	}
450 
451 	// vertical timing
452 	// validate v_start is less then v_end
453 	if (mode->timing.v_sync_start > mode->timing.v_sync_end) {
454 		TRACE("%s: ERROR: (%dx%d) "
455 			"received v_sync_start greater then v_sync_end!\n",
456 			__func__, mode->timing.h_display, mode->timing.v_display);
457 		return B_ERROR;
458 	}
459 	// validate v_total is greater then v_display
460 	if (mode->timing.v_total < mode->timing.v_display) {
461 		TRACE("%s: ERROR: (%dx%d) "
462 			"received v_total greater then v_display!\n",
463 			__func__, mode->timing.h_display, mode->timing.v_display);
464 		return B_ERROR;
465 	}
466 
467 	// calculate refresh rate for given timings to whole int (in Hz)
468 	int refresh = mode->timing.pixel_clock * 1000
469 		/ (mode->timing.h_total * mode->timing.v_total);
470 
471 	if (refresh < 30 || refresh > 250) {
472 		TRACE("%s: ERROR: (%dx%d) "
473 			"refresh rate of %dHz is unlikely for any kind of monitor!\n",
474 			__func__, mode->timing.h_display, mode->timing.v_display, refresh);
475 		return B_ERROR;
476 	}
477 
478 	return B_OK;
479 }
480 
481