xref: /haiku/src/add-ons/accelerants/radeon_hd/mode.cpp (revision 5e96d7d537fbec23bad4ae9b4c8e7b02e769f0c6)
1 /*
2  * Copyright 2006-2013, 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 	uint8 crtcID = 0;
48 
49 	const color_space kRadeonHDSpaces[] = {B_RGB32_LITTLE, B_RGB24_LITTLE,
50 		B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
51 
52 	gInfo->mode_list_area = create_display_modes("radeon HD modes",
53 		&gDisplay[crtcID]->edidData, 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 	// TODO: multi-monitor?  we need crtcid here
73 
74 	return gInfo->shared_info->mode_count;
75 }
76 
77 
78 status_t
79 radeon_get_mode_list(display_mode* modeList)
80 {
81 	TRACE("%s\n", __func__);
82 	// TODO: multi-monitor?  we need crtcid here
83 	memcpy(modeList, gInfo->mode_list,
84 		gInfo->shared_info->mode_count * sizeof(display_mode));
85 	return B_OK;
86 }
87 
88 
89 status_t
90 radeon_get_preferred_mode(display_mode* preferredMode)
91 {
92 	TRACE("%s\n", __func__);
93 	// TODO: multi-monitor?  we need crtcid here
94 
95 	uint8_t crtc = 0;
96 
97 	if (gDisplay[crtc]->preferredMode.virtual_width > 0
98 		&& gDisplay[crtc]->preferredMode.virtual_height > 0) {
99 		TRACE("%s: preferred mode was found for display %" B_PRIu8 "\n",
100 			__func__, crtc);
101 		memcpy(preferredMode, &gDisplay[crtc]->preferredMode,
102 			sizeof(gDisplay[crtc]->preferredMode));
103 		return B_OK;
104 	}
105 
106 	return B_ERROR;
107 }
108 
109 
110 status_t
111 radeon_get_edid_info(void* info, size_t size, uint32* edid_version)
112 {
113 	// TODO: multi-monitor?  for now we use display 0
114 	uint8 crtcID = 0;
115 
116 	TRACE("%s\n", __func__);
117 	if (!gInfo->shared_info->has_edid)
118 		return B_ERROR;
119 	if (size < sizeof(struct edid1_info))
120 		return B_BUFFER_OVERFLOW;
121 
122 	//memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info));
123 		// VESA
124 	memcpy(info, &gDisplay[crtcID]->edidData, sizeof(struct edid1_info));
125 		// Display 0
126 
127 	*edid_version = EDID_VERSION_1;
128 
129 	return B_OK;
130 }
131 
132 
133 uint32
134 radeon_dpms_capabilities(void)
135 {
136 	// These should be pretty universally supported on Radeon HD cards
137 	return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
138 }
139 
140 
141 uint32
142 radeon_dpms_mode(void)
143 {
144 	// TODO: this really isn't a good long-term solution
145 	// we may need to look at the encoder dpms scratch registers
146 	return gInfo->dpms_mode;
147 }
148 
149 
150 void
151 radeon_dpms_set(uint8 id, int mode)
152 {
153 	if (mode == B_DPMS_ON) {
154 		display_crtc_dpms(id, mode);
155 		encoder_dpms_set(id, mode);
156 	} else {
157 		encoder_dpms_set(id, mode);
158 		display_crtc_dpms(id, mode);
159 	}
160 	gInfo->dpms_mode = mode;
161 }
162 
163 
164 void
165 radeon_dpms_set_hook(int mode)
166 {
167 	// TODO: multi-monitor?
168 
169 	uint8 crtcID = 0;
170 
171 	if (gDisplay[crtcID]->attached)
172 		radeon_dpms_set(crtcID, mode);
173 }
174 
175 
176 status_t
177 radeon_set_display_mode(display_mode* mode)
178 {
179 	// TODO: multi-monitor? For now we set the mode on
180 	// the first display found.
181 
182 	uint8 crtcID = 0;
183 
184 	if (gDisplay[crtcID]->attached == false)
185 		return B_ERROR;
186 
187 	// Copy this display mode into the "current mode" for the display
188 	memcpy(&gDisplay[crtcID]->currentMode, mode, sizeof(display_mode));
189 
190 	uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
191 
192 	// Determine DP lanes if DP
193 	if (connector_is_dp(connectorIndex)) {
194 		dp_info *dpInfo = &gConnector[connectorIndex]->dpInfo;
195 		dpInfo->laneCount = dp_get_lane_count(connectorIndex, mode);
196 		dpInfo->linkRate = dp_get_link_rate(connectorIndex, mode);
197 	}
198 
199 	// *** crtc and encoder prep
200 	encoder_output_lock(true);
201 	display_crtc_lock(crtcID, ATOM_ENABLE);
202 	radeon_dpms_set(crtcID, B_DPMS_OFF);
203 
204 	// *** Set up encoder -> crtc routing
205 	encoder_assign_crtc(crtcID);
206 
207 	// *** CRT controler mode set
208 	// Set up PLL for connector
209 	pll_pick(connectorIndex);
210 	pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
211 	TRACE("%s: pll %d selected for connector %" B_PRIu32 "\n", __func__,
212 		pll->id, connectorIndex);
213 	pll_set(mode, crtcID);
214 
215 	display_crtc_set_dtd(crtcID, mode);
216 
217 	display_crtc_fb_set(crtcID, mode);
218 	// atombios_overscan_setup
219 	display_crtc_scale(crtcID, mode);
220 
221 	// *** encoder mode set
222 	encoder_mode_set(crtcID);
223 
224 	// *** encoder and CRT controller commit
225 	radeon_dpms_set(crtcID, B_DPMS_ON);
226 	display_crtc_lock(crtcID, ATOM_DISABLE);
227 	encoder_output_lock(false);
228 
229 	#ifdef TRACE_MODE
230 	// for debugging
231 	debug_dp_info();
232 
233 	TRACE("D1CRTC_STATUS        Value: 0x%X\n",
234 		Read32(CRT, AVIVO_D1CRTC_STATUS));
235 	TRACE("D2CRTC_STATUS        Value: 0x%X\n",
236 		Read32(CRT, AVIVO_D2CRTC_STATUS));
237 	TRACE("D1CRTC_CONTROL       Value: 0x%X\n",
238 		Read32(CRT, AVIVO_D1CRTC_CONTROL));
239 	TRACE("D2CRTC_CONTROL       Value: 0x%X\n",
240 		Read32(CRT, AVIVO_D2CRTC_CONTROL));
241 	TRACE("D1GRPH_ENABLE        Value: 0x%X\n",
242 		Read32(CRT, AVIVO_D1GRPH_ENABLE));
243 	TRACE("D2GRPH_ENABLE        Value: 0x%X\n",
244 		Read32(CRT, AVIVO_D2GRPH_ENABLE));
245 	TRACE("D1SCL_ENABLE         Value: 0x%X\n",
246 		Read32(CRT, AVIVO_D1SCL_SCALER_ENABLE));
247 	TRACE("D2SCL_ENABLE         Value: 0x%X\n",
248 		Read32(CRT, AVIVO_D2SCL_SCALER_ENABLE));
249 	TRACE("D1CRTC_BLANK_CONTROL Value: 0x%X\n",
250 		Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
251 	TRACE("D2CRTC_BLANK_CONTROL Value: 0x%X\n",
252 		Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
253 	#endif
254 
255 	return B_OK;
256 }
257 
258 
259 status_t
260 radeon_get_display_mode(display_mode* _currentMode)
261 {
262 	TRACE("%s\n", __func__);
263 
264 	*_currentMode = gInfo->shared_info->current_mode;
265 	//*_currentMode = gDisplay[X]->currentMode;
266 	return B_OK;
267 }
268 
269 
270 status_t
271 radeon_get_frame_buffer_config(frame_buffer_config* config)
272 {
273 	TRACE("%s\n", __func__);
274 
275 	config->frame_buffer = gInfo->shared_info->frame_buffer;
276 	config->frame_buffer_dma = (uint8*)gInfo->shared_info->frame_buffer_phys;
277 
278 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
279 
280 	return B_OK;
281 }
282 
283 
284 status_t
285 radeon_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
286 {
287 	TRACE("%s\n", __func__);
288 
289 	if (_low != NULL) {
290 		// lower limit of about 48Hz vertical refresh
291 		uint32 totalClocks = (uint32)mode->timing.h_total
292 			* (uint32)mode->timing.v_total;
293 		uint32 low = (totalClocks * 48L) / 1000L;
294 
295 		if (low < PLL_MIN_DEFAULT)
296 			low = PLL_MIN_DEFAULT;
297 		else if (low > PLL_MAX_DEFAULT)
298 			return B_ERROR;
299 
300 		*_low = low;
301 	}
302 
303 	if (_high != NULL)
304 		*_high = PLL_MAX_DEFAULT;
305 
306 	//*_low = 48L;
307 	//*_high = 100 * 1000000L;
308 	return B_OK;
309 }
310 
311 
312 bool
313 is_mode_supported(display_mode* mode)
314 {
315 	bool sane = true;
316 
317 	// Validate modeline is within a sane range
318 	if (is_mode_sane(mode) != B_OK)
319 		sane = false;
320 
321 	// TODO: is_mode_supported on *which* display?
322 	uint32 crtid = 0;
323 
324 	// if we have edid info, check frequency adginst crt reported valid ranges
325 	if (gInfo->shared_info->has_edid
326 		&& gDisplay[crtid]->foundRanges) {
327 
328 		// validate horizontal frequency range
329 		uint32 hfreq = mode->timing.pixel_clock / mode->timing.h_total;
330 		if (hfreq > gDisplay[crtid]->hfreqMax + 1
331 			|| hfreq < gDisplay[crtid]->hfreqMin - 1) {
332 			//TRACE("!!! mode below falls outside of hfreq range!\n");
333 			sane = false;
334 		}
335 
336 		// validate vertical frequency range
337 		uint32 vfreq = mode->timing.pixel_clock / ((mode->timing.v_total
338 			* mode->timing.h_total) / 1000);
339 		if (vfreq > gDisplay[crtid]->vfreqMax + 1
340 			|| vfreq < gDisplay[crtid]->vfreqMin - 1) {
341 			//TRACE("!!! mode below falls outside of vfreq range!\n");
342 			sane = false;
343 		}
344 	}
345 
346 	#if 0
347 	// Lots of spam, but good for understanding what modelines are in use
348 	TRACE("MODE: %d ; %d %d %d %d ; %d %d %d %d is %s\n",
349 		mode->timing.pixel_clock, mode->timing.h_display,
350 		mode->timing.h_sync_start, mode->timing.h_sync_end,
351 		mode->timing.h_total, mode->timing.v_display,
352 		mode->timing.v_sync_start, mode->timing.v_sync_end,
353 		mode->timing.v_total,
354 		sane ? "OK." : "BAD, out of range!");
355 	#endif
356 
357 	return sane;
358 }
359 
360 
361 /*
362  * A quick sanity check of the provided display_mode
363  */
364 status_t
365 is_mode_sane(display_mode* mode)
366 {
367 	// horizontal timing
368 	// validate h_sync_start is less then h_sync_end
369 	if (mode->timing.h_sync_start > mode->timing.h_sync_end) {
370 		TRACE("%s: ERROR: (%dx%d) "
371 			"received h_sync_start greater then h_sync_end!\n",
372 			__func__, mode->timing.h_display, mode->timing.v_display);
373 		return B_ERROR;
374 	}
375 	// validate h_total is greater then h_display
376 	if (mode->timing.h_total < mode->timing.h_display) {
377 		TRACE("%s: ERROR: (%dx%d) "
378 			"received h_total greater then h_display!\n",
379 			__func__, mode->timing.h_display, mode->timing.v_display);
380 		return B_ERROR;
381 	}
382 
383 	// vertical timing
384 	// validate v_start is less then v_end
385 	if (mode->timing.v_sync_start > mode->timing.v_sync_end) {
386 		TRACE("%s: ERROR: (%dx%d) "
387 			"received v_sync_start greater then v_sync_end!\n",
388 			__func__, mode->timing.h_display, mode->timing.v_display);
389 		return B_ERROR;
390 	}
391 	// validate v_total is greater then v_display
392 	if (mode->timing.v_total < mode->timing.v_display) {
393 		TRACE("%s: ERROR: (%dx%d) "
394 			"received v_total greater then v_display!\n",
395 			__func__, mode->timing.h_display, mode->timing.v_display);
396 		return B_ERROR;
397 	}
398 
399 	// calculate refresh rate for given timings to whole int (in Hz)
400 	int refresh = mode->timing.pixel_clock * 1000
401 		/ (mode->timing.h_total * mode->timing.v_total);
402 
403 	if (refresh < 30 || refresh > 250) {
404 		TRACE("%s: ERROR: (%dx%d) "
405 			"refresh rate of %dHz is unlikely for any kind of monitor!\n",
406 			__func__, mode->timing.h_display, mode->timing.v_display, refresh);
407 		return B_ERROR;
408 	}
409 
410 	return B_OK;
411 }
412 
413 
414 uint32
415 get_mode_bpp(display_mode* mode)
416 {
417 	// Get bitsPerPixel for given mode
418 
419 	switch (mode->space) {
420 		case B_CMAP8:
421 			return 8;
422 		case B_RGB15_LITTLE:
423 			return 15;
424 		case B_RGB16_LITTLE:
425 			return 16;
426 		case B_RGB24_LITTLE:
427 		case B_RGB32_LITTLE:
428 			return 32;
429 	}
430 	ERROR("%s: Unknown colorspace for mode, guessing 32 bits per pixel\n",
431 		__func__);
432 	return 32;
433 }
434