xref: /haiku/src/add-ons/accelerants/radeon_hd/mode.cpp (revision 98057dd02a2411868fd4c35f7a48d20acfd92c23)
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 "mode.h"
15 
16 #include <create_display_modes.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <math.h>
20 
21 #include "accelerant.h"
22 #include "accelerant_protos.h"
23 #include "bios.h"
24 #include "connector.h"
25 #include "display.h"
26 #include "displayport.h"
27 #include "encoder.h"
28 #include "pll.h"
29 #include "utility.h"
30 
31 
32 #define TRACE_MODE
33 #ifdef TRACE_MODE
34 extern "C" void _sPrintf(const char* format, ...);
35 #	define TRACE(x...) _sPrintf("radeon_hd: " x)
36 #else
37 #	define TRACE(x...) ;
38 #endif
39 
40 #define ERROR(x...) _sPrintf("radeon_hd: " x)
41 
42 
43 status_t
44 create_mode_list(void)
45 {
46 	// TODO: multi-monitor?  for now we use VESA and not gDisplay edid
47 
48 	const color_space kRadeonHDSpaces[] = {B_RGB32_LITTLE, B_RGB24_LITTLE,
49 		B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
50 
51 	gInfo->mode_list_area = create_display_modes("radeon HD modes",
52 		gInfo->shared_info->has_edid ? &gInfo->shared_info->edid_info : NULL,
53 		NULL, 0, kRadeonHDSpaces,
54 		sizeof(kRadeonHDSpaces) / sizeof(kRadeonHDSpaces[0]),
55 		is_mode_supported, &gInfo->mode_list, &gInfo->shared_info->mode_count);
56 	if (gInfo->mode_list_area < B_OK)
57 		return gInfo->mode_list_area;
58 
59 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
60 
61 	return B_OK;
62 }
63 
64 
65 //	#pragma mark -
66 
67 
68 uint32
69 radeon_accelerant_mode_count(void)
70 {
71 	TRACE("%s\n", __func__);
72 
73 	return gInfo->shared_info->mode_count;
74 }
75 
76 
77 status_t
78 radeon_get_mode_list(display_mode* modeList)
79 {
80 	TRACE("%s\n", __func__);
81 	memcpy(modeList, gInfo->mode_list,
82 		gInfo->shared_info->mode_count * sizeof(display_mode));
83 	return B_OK;
84 }
85 
86 
87 status_t
88 radeon_get_preferred_mode(display_mode* preferredMode)
89 {
90 	TRACE("%s\n", __func__);
91 
92 	// TODO: Argh!  Which display? :)
93 	uint8_t crtc = 0;
94 
95 	if (gDisplay[crtc]->preferredMode.virtual_width > 0
96 		&& gDisplay[crtc]->preferredMode.virtual_height > 0) {
97 		TRACE("%s: preferred mode was found for display %" B_PRIu8 "\n",
98 			__func__, crtc);
99 		memcpy(preferredMode, &gDisplay[crtc]->preferredMode,
100 			sizeof(gDisplay[crtc]->preferredMode));
101 		return B_OK;
102 	}
103 
104 	return B_ERROR;
105 }
106 
107 
108 status_t
109 radeon_get_edid_info(void* info, size_t size, uint32* edid_version)
110 {
111 	// TODO: multi-monitor?  for now we use VESA edid
112 
113 	TRACE("%s\n", __func__);
114 	if (!gInfo->shared_info->has_edid)
115 		return B_ERROR;
116 	if (size < sizeof(struct edid1_info))
117 		return B_BUFFER_OVERFLOW;
118 
119 	memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info));
120 		// VESA
121 	//memcpy(info, &gDisplay[0]->edid_info, sizeof(struct edid1_info));
122 		// BitBanged display 0
123 
124 	*edid_version = EDID_VERSION_1;
125 
126 	return B_OK;
127 }
128 
129 
130 uint32
131 radeon_dpms_capabilities(void)
132 {
133 	// These should be pretty universally supported on Radeon HD cards
134 	return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
135 }
136 
137 
138 uint32
139 radeon_dpms_mode(void)
140 {
141 	// TODO: this really isn't a good long-term solution
142 	// we may need to look at the encoder dpms scratch registers
143 	return gInfo->dpms_mode;
144 }
145 
146 
147 void
148 radeon_dpms_set(int mode)
149 {
150 	radeon_shared_info &info = *gInfo->shared_info;
151 
152 	switch (mode) {
153 		case B_DPMS_ON:
154 			TRACE("%s: ON\n", __func__);
155 			for (uint8 id = 0; id < MAX_DISPLAY; id++) {
156 				if (gDisplay[id]->active == false)
157 					continue;
158 				encoder_output_lock(true);
159 				encoder_dpms_set(id, mode);
160 				encoder_output_lock(false);
161 				display_crtc_lock(id, ATOM_ENABLE);
162 				display_crtc_power(id, ATOM_ENABLE);
163 				if (info.dceMajor >= 3)
164 					display_crtc_memreq(id, ATOM_ENABLE);
165 				display_crtc_blank(id, ATOM_BLANKING_OFF);
166 				display_crtc_lock(id, ATOM_DISABLE);
167 			}
168 			break;
169 		case B_DPMS_STAND_BY:
170 		case B_DPMS_SUSPEND:
171 		case B_DPMS_OFF:
172 			TRACE("%s: OFF\n", __func__);
173 			for (uint8 id = 0; id < MAX_DISPLAY; id++) {
174 				if (gDisplay[id]->active == false)
175 					continue;
176 				display_crtc_lock(id, ATOM_ENABLE);
177 				display_crtc_blank(id, ATOM_BLANKING);
178 				if (info.dceMajor >= 3)
179 					display_crtc_memreq(id, ATOM_DISABLE);
180 				display_crtc_power(id, ATOM_DISABLE);
181 				display_crtc_lock(id, ATOM_DISABLE);
182 				encoder_output_lock(true);
183 				encoder_dpms_set(id, mode);
184 				encoder_output_lock(false);
185 			}
186 			break;
187 	}
188 	gInfo->dpms_mode = mode;
189 }
190 
191 
192 status_t
193 radeon_set_display_mode(display_mode* mode)
194 {
195 	radeon_shared_info &info = *gInfo->shared_info;
196 
197 	// Set mode on each display
198 	for (uint8 id = 0; id < MAX_DISPLAY; id++) {
199 		if (gDisplay[id]->active == false)
200 			continue;
201 
202 		uint16 connectorIndex = gDisplay[id]->connectorIndex;
203 
204 		// Determine DP lanes if DP
205 		if (connector_is_dp(connectorIndex))
206 			gDPInfo[connectorIndex]->laneCount
207 				= dp_get_lane_count(connectorIndex, mode);
208 
209 		// *** encoder prep
210 		encoder_output_lock(true);
211 		encoder_dpms_set(id, B_DPMS_OFF);
212 		encoder_assign_crtc(id);
213 
214 		// *** CRT controler prep
215 		display_crtc_lock(id, ATOM_ENABLE);
216 		display_crtc_blank(id, ATOM_BLANKING);
217 		if (info.dceMajor >= 3)
218 			display_crtc_memreq(id, ATOM_DISABLE);
219 		display_crtc_power(id, ATOM_DISABLE);
220 
221 		// *** CRT controler mode set
222 		// TODO: program SS
223 		pll_set(ATOM_PPLL1, mode->timing.pixel_clock, id);
224 			// TODO: check if ATOM_PPLL1 is used and use ATOM_PPLL2 if so
225 		display_crtc_set_dtd(id, mode);
226 
227 		display_crtc_fb_set(id, mode);
228 		// atombios_overscan_setup
229 		display_crtc_scale(id, mode);
230 
231 		// *** encoder mode set
232 		encoder_mode_set(id, mode->timing.pixel_clock);
233 
234 		// *** CRT controler commit
235 		display_crtc_power(id, ATOM_ENABLE);
236 		if (info.dceMajor >= 3)
237 			display_crtc_memreq(id, ATOM_ENABLE);
238 		display_crtc_blank(id, ATOM_BLANKING_OFF);
239 		display_crtc_lock(id, ATOM_DISABLE);
240 
241 		// *** encoder commit
242 
243 		// handle DisplayPort link training
244 		if (connector_is_dp(connectorIndex)) {
245 			if (info.dceMajor >= 4)
246 				encoder_dig_setup(connectorIndex,
247 					ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
248 
249 			dp_link_train(id, mode);
250 
251 			if (info.dceMajor >= 4)
252 				encoder_dig_setup(connectorIndex,
253 					ATOM_ENCODER_CMD_DP_VIDEO_ON, 0);
254 		}
255 
256 		encoder_dpms_set(id, B_DPMS_ON);
257 		encoder_output_lock(false);
258 	}
259 
260 	// for debugging
261 	TRACE("D1CRTC_STATUS        Value: 0x%X\n", Read32(CRT, D1CRTC_STATUS));
262 	TRACE("D2CRTC_STATUS        Value: 0x%X\n", Read32(CRT, D2CRTC_STATUS));
263 	TRACE("D1CRTC_CONTROL       Value: 0x%X\n", Read32(CRT, D1CRTC_CONTROL));
264 	TRACE("D2CRTC_CONTROL       Value: 0x%X\n", Read32(CRT, D2CRTC_CONTROL));
265 	TRACE("D1GRPH_ENABLE        Value: 0x%X\n",
266 		Read32(CRT, AVIVO_D1GRPH_ENABLE));
267 	TRACE("D2GRPH_ENABLE        Value: 0x%X\n",
268 		Read32(CRT, AVIVO_D2GRPH_ENABLE));
269 	TRACE("D1SCL_ENABLE         Value: 0x%X\n",
270 		Read32(CRT, AVIVO_D1SCL_SCALER_ENABLE));
271 	TRACE("D2SCL_ENABLE         Value: 0x%X\n",
272 		Read32(CRT, AVIVO_D2SCL_SCALER_ENABLE));
273 	TRACE("D1CRTC_BLANK_CONTROL Value: 0x%X\n",
274 		Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
275 	TRACE("D2CRTC_BLANK_CONTROL Value: 0x%X\n",
276 		Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
277 
278 	return B_OK;
279 }
280 
281 
282 status_t
283 radeon_get_display_mode(display_mode* _currentMode)
284 {
285 	TRACE("%s\n", __func__);
286 
287 	*_currentMode = gInfo->shared_info->current_mode;
288 	return B_OK;
289 }
290 
291 
292 status_t
293 radeon_get_frame_buffer_config(frame_buffer_config* config)
294 {
295 	TRACE("%s\n", __func__);
296 
297 	config->frame_buffer = gInfo->shared_info->frame_buffer;
298 	config->frame_buffer_dma = (uint8*)gInfo->shared_info->frame_buffer_phys;
299 
300 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
301 
302 	return B_OK;
303 }
304 
305 
306 status_t
307 radeon_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
308 {
309 	TRACE("%s\n", __func__);
310 
311 	if (_low != NULL) {
312 		// lower limit of about 48Hz vertical refresh
313 		uint32 totalClocks = (uint32)mode->timing.h_total
314 			* (uint32)mode->timing.v_total;
315 		uint32 low = (totalClocks * 48L) / 1000L;
316 
317 		if (low < PLL_MIN_DEFAULT)
318 			low = PLL_MIN_DEFAULT;
319 		else if (low > PLL_MAX_DEFAULT)
320 			return B_ERROR;
321 
322 		*_low = low;
323 	}
324 
325 	if (_high != NULL)
326 		*_high = PLL_MAX_DEFAULT;
327 
328 	//*_low = 48L;
329 	//*_high = 100 * 1000000L;
330 	return B_OK;
331 }
332 
333 
334 bool
335 is_mode_supported(display_mode* mode)
336 {
337 	bool sane = true;
338 
339 	// Validate modeline is within a sane range
340 	if (is_mode_sane(mode) != B_OK)
341 		sane = false;
342 
343 	// TODO: is_mode_supported on *which* display?
344 	uint32 crtid = 0;
345 
346 	// if we have edid info, check frequency adginst crt reported valid ranges
347 	if (gInfo->shared_info->has_edid
348 		&& gDisplay[crtid]->found_ranges) {
349 
350 		// validate horizontal frequency range
351 		uint32 hfreq = mode->timing.pixel_clock / mode->timing.h_total;
352 		if (hfreq > gDisplay[crtid]->hfreq_max + 1
353 			|| hfreq < gDisplay[crtid]->hfreq_min - 1) {
354 			//TRACE("!!! mode below falls outside of hfreq range!\n");
355 			sane = false;
356 		}
357 
358 		// validate vertical frequency range
359 		uint32 vfreq = mode->timing.pixel_clock / ((mode->timing.v_total
360 			* mode->timing.h_total) / 1000);
361 		if (vfreq > gDisplay[crtid]->vfreq_max + 1
362 			|| vfreq < gDisplay[crtid]->vfreq_min - 1) {
363 			//TRACE("!!! mode below falls outside of vfreq range!\n");
364 			sane = false;
365 		}
366 	}
367 
368 	TRACE("MODE: %d ; %d %d %d %d ; %d %d %d %d is %s\n",
369 		mode->timing.pixel_clock, mode->timing.h_display,
370 		mode->timing.h_sync_start, mode->timing.h_sync_end,
371 		mode->timing.h_total, mode->timing.v_display,
372 		mode->timing.v_sync_start, mode->timing.v_sync_end,
373 		mode->timing.v_total,
374 		sane ? "OK." : "BAD, out of range!");
375 
376 	return sane;
377 }
378 
379 
380 /*
381  * A quick sanity check of the provided display_mode
382  */
383 status_t
384 is_mode_sane(display_mode* mode)
385 {
386 	// horizontal timing
387 	// validate h_sync_start is less then h_sync_end
388 	if (mode->timing.h_sync_start > mode->timing.h_sync_end) {
389 		TRACE("%s: ERROR: (%dx%d) "
390 			"received h_sync_start greater then h_sync_end!\n",
391 			__func__, mode->timing.h_display, mode->timing.v_display);
392 		return B_ERROR;
393 	}
394 	// validate h_total is greater then h_display
395 	if (mode->timing.h_total < mode->timing.h_display) {
396 		TRACE("%s: ERROR: (%dx%d) "
397 			"received h_total greater then h_display!\n",
398 			__func__, mode->timing.h_display, mode->timing.v_display);
399 		return B_ERROR;
400 	}
401 
402 	// vertical timing
403 	// validate v_start is less then v_end
404 	if (mode->timing.v_sync_start > mode->timing.v_sync_end) {
405 		TRACE("%s: ERROR: (%dx%d) "
406 			"received v_sync_start greater then v_sync_end!\n",
407 			__func__, mode->timing.h_display, mode->timing.v_display);
408 		return B_ERROR;
409 	}
410 	// validate v_total is greater then v_display
411 	if (mode->timing.v_total < mode->timing.v_display) {
412 		TRACE("%s: ERROR: (%dx%d) "
413 			"received v_total greater then v_display!\n",
414 			__func__, mode->timing.h_display, mode->timing.v_display);
415 		return B_ERROR;
416 	}
417 
418 	// calculate refresh rate for given timings to whole int (in Hz)
419 	int refresh = mode->timing.pixel_clock * 1000
420 		/ (mode->timing.h_total * mode->timing.v_total);
421 
422 	if (refresh < 30 || refresh > 250) {
423 		TRACE("%s: ERROR: (%dx%d) "
424 			"refresh rate of %dHz is unlikely for any kind of monitor!\n",
425 			__func__, mode->timing.h_display, mode->timing.v_display, refresh);
426 		return B_ERROR;
427 	}
428 
429 	return B_OK;
430 }
431 
432 
433 uint32
434 get_mode_bpp(display_mode* mode)
435 {
436 	// Get bitsPerPixel for given mode
437 
438 	switch (mode->space) {
439 		case B_CMAP8:
440 			return 8;
441 		case B_RGB15_LITTLE:
442 			return 15;
443 		case B_RGB16_LITTLE:
444 			return 16;
445 		case B_RGB24_LITTLE:
446 		case B_RGB32_LITTLE:
447 			return 32;
448 	}
449 	ERROR("%s: Unknown colorspace for mode, guessing 32 bits per pixel\n",
450 		__func__);
451 	return 32;
452 }
453