xref: /haiku/src/add-ons/accelerants/radeon_hd/mode.cpp (revision d3ff06683af390a4c2e83b69177e0a2eb76679bc)
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 status_t
43 create_mode_list(void)
44 {
45 	// TODO: multi-monitor?  for now we use VESA and not gDisplay edid
46 
47 	const color_space kRadeonHDSpaces[] = {B_RGB32_LITTLE, B_RGB24_LITTLE,
48 		B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
49 
50 	gInfo->mode_list_area = create_display_modes("radeon HD modes",
51 		gInfo->shared_info->has_edid ? &gInfo->shared_info->edid_info : NULL,
52 		NULL, 0, kRadeonHDSpaces,
53 		sizeof(kRadeonHDSpaces) / sizeof(kRadeonHDSpaces[0]),
54 		is_mode_supported, &gInfo->mode_list, &gInfo->shared_info->mode_count);
55 	if (gInfo->mode_list_area < B_OK)
56 		return gInfo->mode_list_area;
57 
58 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
59 
60 	return B_OK;
61 }
62 
63 
64 //	#pragma mark -
65 
66 
67 uint32
68 radeon_accelerant_mode_count(void)
69 {
70 	TRACE("%s\n", __func__);
71 
72 	return gInfo->shared_info->mode_count;
73 }
74 
75 
76 status_t
77 radeon_get_mode_list(display_mode* modeList)
78 {
79 	TRACE("%s\n", __func__);
80 	memcpy(modeList, gInfo->mode_list,
81 		gInfo->shared_info->mode_count * sizeof(display_mode));
82 	return B_OK;
83 }
84 
85 
86 status_t
87 radeon_get_edid_info(void* info, size_t size, uint32* edid_version)
88 {
89 	// TODO: multi-monitor?  for now we use VESA edid
90 
91 	TRACE("%s\n", __func__);
92 	if (!gInfo->shared_info->has_edid)
93 		return B_ERROR;
94 	if (size < sizeof(struct edid1_info))
95 		return B_BUFFER_OVERFLOW;
96 
97 	memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info));
98 		// VESA
99 	//memcpy(info, &gDisplay[0]->edid_info, sizeof(struct edid1_info));
100 		// BitBanged display 0
101 
102 	*edid_version = EDID_VERSION_1;
103 
104 	return B_OK;
105 }
106 
107 
108 uint32
109 radeon_dpms_capabilities(void)
110 {
111 	// These should be pretty universally supported on Radeon HD cards
112 	return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
113 }
114 
115 
116 uint32
117 radeon_dpms_mode(void)
118 {
119 	// TODO: this really isn't a good long-term solution
120 	// we may need to look at the encoder dpms scratch registers
121 	return gInfo->dpms_mode;
122 }
123 
124 
125 void
126 radeon_dpms_set(int mode)
127 {
128 	radeon_shared_info &info = *gInfo->shared_info;
129 
130 	switch (mode) {
131 		case B_DPMS_ON:
132 			TRACE("%s: ON\n", __func__);
133 			for (uint8 id = 0; id < MAX_DISPLAY; id++) {
134 				if (gDisplay[id]->active == false)
135 					continue;
136 				encoder_output_lock(true);
137 				encoder_dpms_set(id, mode);
138 				encoder_output_lock(false);
139 				display_crtc_lock(id, ATOM_ENABLE);
140 				display_crtc_power(id, ATOM_ENABLE);
141 				if (info.dceMajor >= 3)
142 					display_crtc_memreq(id, ATOM_ENABLE);
143 				display_crtc_blank(id, ATOM_BLANKING_OFF);
144 				display_crtc_lock(id, ATOM_DISABLE);
145 			}
146 			break;
147 		case B_DPMS_STAND_BY:
148 		case B_DPMS_SUSPEND:
149 		case B_DPMS_OFF:
150 			TRACE("%s: OFF\n", __func__);
151 			for (uint8 id = 0; id < MAX_DISPLAY; id++) {
152 				if (gDisplay[id]->active == false)
153 					continue;
154 				display_crtc_lock(id, ATOM_ENABLE);
155 				display_crtc_blank(id, ATOM_BLANKING);
156 				if (info.dceMajor >= 3)
157 					display_crtc_memreq(id, ATOM_DISABLE);
158 				display_crtc_power(id, ATOM_DISABLE);
159 				display_crtc_lock(id, ATOM_DISABLE);
160 				encoder_output_lock(true);
161 				encoder_dpms_set(id, mode);
162 				encoder_output_lock(false);
163 			}
164 			break;
165 	}
166 	gInfo->dpms_mode = mode;
167 }
168 
169 
170 status_t
171 radeon_set_display_mode(display_mode* mode)
172 {
173 	radeon_shared_info &info = *gInfo->shared_info;
174 
175 	// Set mode on each display
176 	for (uint8 id = 0; id < MAX_DISPLAY; id++) {
177 		if (gDisplay[id]->active == false)
178 			continue;
179 
180 		uint16 connectorIndex = gDisplay[id]->connectorIndex;
181 
182 		// Determine DP lanes if DP
183 		if (connector_is_dp(connectorIndex))
184 			gDPInfo[connectorIndex]->laneCount
185 				= dp_get_lane_count(connectorIndex, mode);
186 
187 		// *** encoder prep
188 		encoder_output_lock(true);
189 		encoder_dpms_set(id, B_DPMS_OFF);
190 		encoder_assign_crtc(id);
191 
192 		// *** CRT controler prep
193 		display_crtc_lock(id, ATOM_ENABLE);
194 		display_crtc_blank(id, ATOM_BLANKING);
195 		if (info.dceMajor >= 3)
196 			display_crtc_memreq(id, ATOM_DISABLE);
197 		display_crtc_power(id, ATOM_DISABLE);
198 
199 		// *** CRT controler mode set
200 		// TODO: program SS
201 		pll_set(ATOM_PPLL1, mode->timing.pixel_clock, id);
202 			// TODO: check if ATOM_PPLL1 is used and use ATOM_PPLL2 if so
203 		display_crtc_set_dtd(id, mode);
204 
205 		display_crtc_fb_set(id, mode);
206 		// atombios_overscan_setup
207 		display_crtc_scale(id, mode);
208 
209 		// *** encoder mode set
210 		encoder_mode_set(id, mode->timing.pixel_clock);
211 
212 		// *** CRT controler commit
213 		display_crtc_power(id, ATOM_ENABLE);
214 		if (info.dceMajor >= 3)
215 			display_crtc_memreq(id, ATOM_ENABLE);
216 		display_crtc_blank(id, ATOM_BLANKING_OFF);
217 		display_crtc_lock(id, ATOM_DISABLE);
218 
219 		// *** encoder commit
220 
221 		// handle DisplayPort link training
222 		if (connector_is_dp(connectorIndex)) {
223 			if (info.dceMajor >= 4)
224 				encoder_dig_setup(connectorIndex,
225 					ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
226 
227 			dp_link_train(id, mode);
228 
229 			if (info.dceMajor >= 4)
230 				encoder_dig_setup(connectorIndex,
231 					ATOM_ENCODER_CMD_DP_VIDEO_ON, 0);
232 		}
233 
234 		encoder_dpms_set(id, B_DPMS_ON);
235 		encoder_output_lock(false);
236 	}
237 
238 	// for debugging
239 	TRACE("D1CRTC_STATUS        Value: 0x%X\n", Read32(CRT, D1CRTC_STATUS));
240 	TRACE("D2CRTC_STATUS        Value: 0x%X\n", Read32(CRT, D2CRTC_STATUS));
241 	TRACE("D1CRTC_CONTROL       Value: 0x%X\n", Read32(CRT, D1CRTC_CONTROL));
242 	TRACE("D2CRTC_CONTROL       Value: 0x%X\n", Read32(CRT, D2CRTC_CONTROL));
243 	TRACE("D1GRPH_ENABLE        Value: 0x%X\n",
244 		Read32(CRT, AVIVO_D1GRPH_ENABLE));
245 	TRACE("D2GRPH_ENABLE        Value: 0x%X\n",
246 		Read32(CRT, AVIVO_D2GRPH_ENABLE));
247 	TRACE("D1SCL_ENABLE         Value: 0x%X\n",
248 		Read32(CRT, AVIVO_D1SCL_SCALER_ENABLE));
249 	TRACE("D2SCL_ENABLE         Value: 0x%X\n",
250 		Read32(CRT, AVIVO_D2SCL_SCALER_ENABLE));
251 	TRACE("D1CRTC_BLANK_CONTROL Value: 0x%X\n",
252 		Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
253 	TRACE("D2CRTC_BLANK_CONTROL Value: 0x%X\n",
254 		Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
255 
256 	return B_OK;
257 }
258 
259 
260 status_t
261 radeon_get_display_mode(display_mode* _currentMode)
262 {
263 	TRACE("%s\n", __func__);
264 
265 	*_currentMode = gInfo->shared_info->current_mode;
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]->found_ranges) {
327 
328 		// validate horizontal frequency range
329 		uint32 hfreq = mode->timing.pixel_clock / mode->timing.h_total;
330 		if (hfreq > gDisplay[crtid]->hfreq_max + 1
331 			|| hfreq < gDisplay[crtid]->hfreq_min - 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]->vfreq_max + 1
340 			|| vfreq < gDisplay[crtid]->vfreq_min - 1) {
341 			//TRACE("!!! mode below falls outside of vfreq range!\n");
342 			sane = false;
343 		}
344 	}
345 
346 	TRACE("MODE: %d ; %d %d %d %d ; %d %d %d %d is %s\n",
347 		mode->timing.pixel_clock, mode->timing.h_display,
348 		mode->timing.h_sync_start, mode->timing.h_sync_end,
349 		mode->timing.h_total, mode->timing.v_display,
350 		mode->timing.v_sync_start, mode->timing.v_sync_end,
351 		mode->timing.v_total,
352 		sane ? "OK." : "BAD, out of range!");
353 
354 	return sane;
355 }
356 
357 
358 /*
359  * A quick sanity check of the provided display_mode
360  */
361 status_t
362 is_mode_sane(display_mode* mode)
363 {
364 	// horizontal timing
365 	// validate h_sync_start is less then h_sync_end
366 	if (mode->timing.h_sync_start > mode->timing.h_sync_end) {
367 		TRACE("%s: ERROR: (%dx%d) "
368 			"received h_sync_start greater then h_sync_end!\n",
369 			__func__, mode->timing.h_display, mode->timing.v_display);
370 		return B_ERROR;
371 	}
372 	// validate h_total is greater then h_display
373 	if (mode->timing.h_total < mode->timing.h_display) {
374 		TRACE("%s: ERROR: (%dx%d) "
375 			"received h_total greater then h_display!\n",
376 			__func__, mode->timing.h_display, mode->timing.v_display);
377 		return B_ERROR;
378 	}
379 
380 	// vertical timing
381 	// validate v_start is less then v_end
382 	if (mode->timing.v_sync_start > mode->timing.v_sync_end) {
383 		TRACE("%s: ERROR: (%dx%d) "
384 			"received v_sync_start greater then v_sync_end!\n",
385 			__func__, mode->timing.h_display, mode->timing.v_display);
386 		return B_ERROR;
387 	}
388 	// validate v_total is greater then v_display
389 	if (mode->timing.v_total < mode->timing.v_display) {
390 		TRACE("%s: ERROR: (%dx%d) "
391 			"received v_total greater then v_display!\n",
392 			__func__, mode->timing.h_display, mode->timing.v_display);
393 		return B_ERROR;
394 	}
395 
396 	// calculate refresh rate for given timings to whole int (in Hz)
397 	int refresh = mode->timing.pixel_clock * 1000
398 		/ (mode->timing.h_total * mode->timing.v_total);
399 
400 	if (refresh < 30 || refresh > 250) {
401 		TRACE("%s: ERROR: (%dx%d) "
402 			"refresh rate of %dHz is unlikely for any kind of monitor!\n",
403 			__func__, mode->timing.h_display, mode->timing.v_display, refresh);
404 		return B_ERROR;
405 	}
406 
407 	return B_OK;
408 }
409 
410 
411 uint32
412 get_mode_bpp(display_mode* mode)
413 {
414 	// Get bitsPerPixel for given mode
415 
416 	switch (mode->space) {
417 		case B_CMAP8:
418 			return 8;
419 		case B_RGB15_LITTLE:
420 			return 15;
421 		case B_RGB16_LITTLE:
422 			return 16;
423 		case B_RGB24_LITTLE:
424 		case B_RGB32_LITTLE:
425 			return 32;
426 	}
427 	ERROR("%s: Unknown colorspace for mode, guessing 32 bits per pixel\n",
428 		__func__);
429 	return 32;
430 }
431