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