xref: /haiku/src/add-ons/accelerants/radeon/ProposeDisplayMode.c (revision 24159a0c7d6d6dcba9f2a0c1a7c08d2c8167f21b)
1 /*
2 	Copyright (c) 2002-2004, Thomas Kurschel
3 
4 
5 	Part of Radeon accelerant
6 
7 	Everything concerning getting/testing display modes
8 */
9 
10 #include "radeon_accelerant.h"
11 #include "generic.h"
12 #include <string.h>
13 #include "GlobalData.h"
14 
15 #include "crtc_regs.h"
16 #include "utils.h"
17 #include "set_mode.h"
18 
19 // standard mode list
20 // all drivers contain this list - this should really be moved to
21 // something like the screen preferences panel
22 
23 #define	T_POSITIVE_SYNC	(B_POSITIVE_HSYNC | B_POSITIVE_VSYNC)
24 #define MODE_FLAGS	(B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS | B_DPMS | B_SUPPORTS_OVERLAYS)
25 //#define MODE_COUNT (sizeof (mode_list) / sizeof (display_mode))
26 
27 static const display_mode base_mode_list[] = {
28 // test for PAL
29 //{ { 25175, 640, 656, 752, 816, 480, 490, 492, 625, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */
30 // test for NTSC
31 //{ { 43956, 800, 824, 952, 992, 600, 632, 635, 740, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X600X8.Z1) */
32 
33 { { 25175, 640, 656, 752, 800, 480, 490, 492, 525, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */
34 { { 27500, 640, 672, 768, 864, 480, 488, 494, 530, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* 640X480X60Hz */
35 { { 30500, 640, 672, 768, 864, 480, 517, 523, 588, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* SVGA_640X480X60HzNI */
36 { { 31500, 640, 664, 704, 832, 480, 489, 492, 520, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(640X480X8.Z1) */
37 { { 31500, 640, 656, 720, 840, 480, 481, 484, 500, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(640X480X8.Z1) */
38 { { 36000, 640, 696, 752, 832, 480, 481, 484, 509, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(640X480X8.Z1) */
39 { { 25175, 640, 656, 752, 800, 400, 412, 414, 449, B_POSITIVE_VSYNC}, B_CMAP8, 640, 400, 0, 0, MODE_FLAGS}, /* 640x400 - www.epanorama.net/documents/pc/vga_timing.html) */
40 { { 25175, 640, 656, 752, 800, 350, 387, 389, 449, B_POSITIVE_HSYNC}, B_CMAP8, 640, 350, 0, 0, MODE_FLAGS}, /* 640x350 - www.epanorama.net/documents/pc/vga_timing.html) */
41 
42 // NTSC non-isometric resolution (isometric resolution is 640x480)
43 { { 26720, 720, 736, 808, 896, 480, 481, 484, 497, B_POSITIVE_VSYNC}, B_CMAP8, 720, 480, 0, 0, MODE_FLAGS},	/* 720x480@60Hz according to GMTF */
44 
45 // PAL resolutions
46 { { 26570, 720, 736, 808, 896, 576, 577, 580, 593, B_POSITIVE_VSYNC}, B_CMAP8, 720, 576, 0, 0, MODE_FLAGS},	/* 720x576@50Hz according to GMTF */
47 { { 28460, 768, 784, 864, 960, 576, 577, 580, 593, B_POSITIVE_VSYNC}, B_CMAP8, 768, 576, 0, 0, MODE_FLAGS},	/* 768x576@50Hz according to GMTF */
48 
49 { { 38100, 800, 832, 960, 1088, 600, 602, 606, 620, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* SVGA_800X600X56HzNI */
50 { { 40000, 800, 840, 968, 1056, 600, 601, 605, 628, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X600X8.Z1) */
51 { { 49500, 800, 816, 896, 1056, 600, 601, 604, 625, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(800X600X8.Z1) */
52 { { 50000, 800, 856, 976, 1040, 600, 637, 643, 666, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(800X600X8.Z1) */
53 { { 56250, 800, 832, 896, 1048, 600, 601, 604, 631, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(800X600X8.Z1) */
54 { { 65000, 1024, 1048, 1184, 1344, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X768X8.Z1) */
55 { { 75000, 1024, 1048, 1184, 1328, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(1024X768X8.Z1) */
56 { { 78750, 1024, 1040, 1136, 1312, 768, 769, 772, 800, T_POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1024X768X8.Z1) */
57 { { 94500, 1024, 1072, 1168, 1376, 768, 769, 772, 808, T_POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1024X768X8.Z1) */
58 { { 94200, 1152, 1184, 1280, 1472, 864, 865, 868, 914, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1152X864X8.Z1) */
59 { { 108000, 1152, 1216, 1344, 1600, 864, 865, 868, 900, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1152X864X8.Z1) */
60 { { 121500, 1152, 1216, 1344, 1568, 864, 865, 868, 911, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1152X864X8.Z1) */
61 
62 { { 108000, 1280, 1376, 1488, 1800, 960, 961, 964, 1000, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X960X8.Z1) - not in Be's list */
63 { { 148500, 1280, 1344, 1504, 1728, 960, 961, 964, 1011, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1280X960X8.Z1) - not in Be's list */
64 
65 { { 108000, 1280, 1328, 1440, 1688, 1024, 1025, 1028, 1066, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024X8.Z1) */
66 { { 135000, 1280, 1296, 1440, 1688, 1024, 1025, 1028, 1066, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1280X1024X8.Z1) */
67 { { 157500, 1280, 1344, 1504, 1728, 1024, 1025, 1028, 1072, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1280X1024X8.Z1) */
68 { { 162000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1600X1200X8.Z1) */
69 { { 175500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@65Hz_(1600X1200X8.Z1) */
70 { { 189000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1600X1200X8.Z1) */
71 { { 202500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1600X1200X8.Z1) */
72 { { 216000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@80Hz_(1600X1200X8.Z1) */
73 { { 229500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}  /* Vesa_Monitor_@85Hz_(1600X1200X8.Z1) */
74 };
75 
76 
77 // convert Be colour space to Radeon data type
78 // returns true, if supported colour space
79 //	space - Be colour space
80 //	format - (out) Radeon data type
81 //	bpp - (out) bytes per pixel
82 bool Radeon_GetFormat( int space, int *format, int *bpp )
83 {
84 	switch( space ) {
85     /*case 4:  format = 1; bytpp = 0; break;*/
86     case B_CMAP8:  *format = 2; *bpp = 1; break;
87     case B_RGB15_LITTLE: *format = 3; *bpp = 2; break;      /*  555 */
88     case B_RGB16_LITTLE: *format = 4; *bpp = 2; break;      /*  565 */
89     case B_RGB24_LITTLE: *format = 5; *bpp = 3; break;      /*  RGB */
90     case B_RGB32_LITTLE: *format = 6; *bpp = 4; break;      /* xRGB */
91     default:
92 		SHOW_ERROR( 1, "Unsupported color space (%d)", space );
93 		return false;
94     }
95 
96     return true;
97 }
98 
99 
100 // macros to convert between register values and pixels
101 #define H_DISPLAY_2REG( a ) ((a) / 8 - 1)
102 #define H_DISPLAY_2PIX( a ) (((a) + 1) * 8)
103 #define H_TOTAL_2REG( a ) ((a) / 8 - 1)
104 #define H_TOTAL_2PIX( a ) (((a) + 1) * 8)
105 #define H_SSTART_2REG( a ) ((a) - 8 + h_sync_fudge)
106 #define H_SSTART_2PIX( a ) ((a) + 8 - h_sync_fudge)
107 #define H_SWID_2REG( a ) ((a) / 8)
108 #define H_SWID_2PIX( a ) ((a) * 8)
109 
110 #define V_2REG( a ) ((a) - 1)
111 #define V_2PIX( a ) ((a) + 1)
112 
113 /*
114 	Validate a target display mode is both
115 		a) a valid display mode for this device and
116 		b) falls between the contraints imposed by "low" and "high"
117 
118 	If the mode is not (or cannot) be made valid for this device, return B_ERROR.
119 	If a valid mode can be constructed, but it does not fall within the limits,
120 	 return B_BAD_VALUE.
121 	If the mode is both valid AND falls within the limits, return B_OK.
122 */
123 status_t Radeon_ProposeDisplayMode(
124 	shared_info *si, crtc_info *crtc,
125 	general_pll_info *pll, display_mode *target,
126 	const display_mode *low, const display_mode *high )
127 {
128 	status_t result = B_OK;
129 
130 	uint64 target_refresh;
131 	bool want_same_width, want_same_height;
132 	int format, bpp;
133 	uint32 row_bytes;
134 	int eff_virtual_width;
135 	fp_info *flatpanel = &si->flatpanels[crtc->flatpanel_port];
136 
137 	// save refresh rate - we want to leave this (artifical) value untouched
138 	// don't use floating point, we are in kernel mode
139 	target_refresh =
140 		(((uint64)target->timing.pixel_clock * 1000) << FIX_SHIFT) /
141 		((uint64)target->timing.h_total * target->timing.v_total);
142 
143 	want_same_width = target->timing.h_display == target->virtual_width;
144 	want_same_height = target->timing.v_display == target->virtual_height;
145 
146 	if( !Radeon_GetFormat( target->space, &format, &bpp ))
147 		return B_ERROR;
148 
149 	// for flat panels, check maximum resolution;
150 	// all the other tricks (like fixed resolution and resulting scaling)
151 	// are done automagically by set_display_mode
152     if( (crtc->chosen_displays & (dd_lvds | dd_dvi | dd_dvi_ext)) != 0 ) {
153 		if( target->timing.h_display > flatpanel->panel_xres )
154 			target->timing.h_display = flatpanel->panel_xres;
155 
156 		if(	target->timing.v_display > flatpanel->panel_yres )
157 			target->timing.v_display = flatpanel->panel_yres;
158 	}
159 
160 /*
161 	// the TV-Out encoder can "only" handle up to 1024x768
162 	if( (head->chosen_displays & (dd_ctv | dd_stv)) != 0 ) {
163 		if( target->timing.h_display > 1024 )
164 			target->timing.h_display = 1024;
165 
166 		if(	target->timing.v_display > 768 )
167 			target->timing.v_display = 768;
168 	}
169 */
170 
171 	// validate horizontal timings
172 	{
173 		int h_sync_fudge, h_display, h_sync_start, h_sync_wid, h_total;
174 
175 		h_display = target->timing.h_display;
176 		h_sync_fudge = Radeon_GetHSyncFudge( crtc, format );
177 		h_sync_start = target->timing.h_sync_start;
178 		h_sync_wid = target->timing.h_sync_end - target->timing.h_sync_start;
179 		h_total = target->timing.h_total;
180 
181 		// make sure, display is not too small
182 		// (I reckon Radeon doesn't care, but your monitor probably does)
183 		if( h_display < 320 )
184 			h_display = 320;
185 		// apply hardware restrictions
186 		// as h_display is the smallest register, it's always possible
187 		// to adjust other values to keep them in supported range
188 		if( h_display > H_DISPLAY_2PIX( RADEON_CRTC_H_DISP >> RADEON_CRTC_H_DISP_SHIFT ) )
189 			h_display = H_DISPLAY_2PIX( RADEON_CRTC_H_DISP >> RADEON_CRTC_H_DISP_SHIFT );
190 		// round properly
191 		h_display = H_DISPLAY_2PIX( H_DISPLAY_2REG( h_display ));
192 
193 		// ensure minimum time before sync
194 		if( h_sync_start < h_display + 2*8 )
195 			h_sync_start = h_display + 2*8;
196 		// sync has wider range than display are, so we won't collide there,
197 		// but total width has same range as sync start, so leave some space
198 		if( h_sync_start > H_SSTART_2PIX( RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX ) - 4*8 )
199 			h_sync_start = H_SSTART_2PIX( RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX ) - 4*8;
200 
201 		// ensure minimum sync length
202 		if( h_sync_wid < H_SWID_2PIX( 3 ))
203 			h_sync_wid = H_SWID_2PIX( 3 );
204 		// allowed range is quite small, so make sure sync isn't too long
205 		if( h_sync_wid > H_SWID_2PIX( RADEON_CRTC_H_SYNC_WID >> RADEON_CRTC_H_SYNC_WID_SHIFT ) )
206 			h_sync_wid = H_SWID_2PIX( RADEON_CRTC_H_SYNC_WID >> RADEON_CRTC_H_SYNC_WID_SHIFT );
207 		// round properly
208 		h_sync_wid = H_SWID_2PIX( H_SWID_2REG( h_sync_wid ));
209 
210 		// last but not least adapt total width
211 		// "+7" is needed for rounding up: sync_start isn't rounded, but h_total is
212 		if( h_total < h_sync_start + h_sync_wid + 1*8 + 7 )
213 			h_total = h_sync_start + h_sync_wid + 1*8 + 7;
214 		// we may get a too long total width; this can only happen
215 		// because sync is too long, so truncate sync accordingly
216 		if( h_total > H_TOTAL_2PIX( RADEON_CRTC_H_TOTAL ) ) {
217 			h_total = H_TOTAL_2PIX( RADEON_CRTC_H_TOTAL );
218 			h_sync_wid = min( h_sync_wid, h_total - h_sync_start );
219 			h_sync_wid = H_SWID_2PIX( H_SWID_2REG( h_sync_wid ));
220 		}
221 		// round properly
222 		h_total = H_TOTAL_2PIX( H_TOTAL_2REG( h_total ));
223 
224 		target->timing.h_display = h_display;
225 		target->timing.h_sync_start = h_sync_start;
226 		target->timing.h_sync_end = h_sync_start + h_sync_wid;
227 		target->timing.h_total = h_total;
228 	}
229 
230 	// did we fall out of one of the limits?
231 	if( target->timing.h_display < low->timing.h_display ||
232 		target->timing.h_display > high->timing.h_display ||
233 		target->timing.h_sync_start < low->timing.h_sync_start ||
234 		target->timing.h_sync_start > high->timing.h_sync_start ||
235 		target->timing.h_sync_end < low->timing.h_sync_end ||
236 		target->timing.h_sync_end > high->timing.h_sync_end ||
237 		target->timing.h_total < low->timing.h_total ||
238 		target->timing.h_total > high->timing.h_total)
239 	{
240 		SHOW_FLOW0( 4, "out of horizontal limits" );
241 		result = B_BAD_VALUE;
242 	}
243 
244 	// validate vertical timings
245 	{
246 		int v_display, v_sync_start, v_sync_wid, v_total;
247 
248 		v_display = target->timing.v_display;
249 		v_sync_start = target->timing.v_sync_start;
250 		v_sync_wid = target->timing.v_sync_end - target->timing.v_sync_start;
251 		v_total = target->timing.v_total;
252 
253 		// apply a reasonable minimal height to make monitor happy
254 		if( v_display < 200 )
255 			v_display = 200;
256 		// apply limits but make sure we have enough lines left for blank and sync
257 		if( v_display > V_2PIX(RADEON_CRTC_V_DISP >> RADEON_CRTC_V_DISP_SHIFT) - 5)
258 			v_display = V_2PIX(RADEON_CRTC_V_DISP >> RADEON_CRTC_V_DISP_SHIFT) - 5;
259 
260 		// leave at least one line before sync
261 		// (some flat panel have zero gap here; probably, this leads to
262 		// the infamous bright line at top of screen)
263 		if( v_sync_start < v_display + 1 )
264 			v_sync_start = v_display + 1;
265 		// apply hardware limit and leave some lines for sync
266 		if( v_sync_start > V_2PIX(RADEON_CRTC_V_SYNC_STRT) - 4)
267 			v_sync_start = V_2PIX(RADEON_CRTC_V_SYNC_STRT) - 4;
268 
269 		// don't make sync too short
270 		if( v_sync_wid < 2 )
271 			v_sync_wid = 2;
272 		// sync width is quite restricted
273 		if( v_sync_wid > (RADEON_CRTC_V_SYNC_WID >> RADEON_CRTC_V_SYNC_WID_SHIFT))
274 			v_sync_wid = (RADEON_CRTC_V_SYNC_WID >> RADEON_CRTC_V_SYNC_WID_SHIFT);
275 
276 		// leave a gap of at least 1 line
277 		if( v_total < v_sync_start + v_sync_wid + 1 )
278 			v_total = v_sync_start + v_sync_wid + 1;
279 		// if too long, truncate it and adapt sync len
280 		if( v_total > V_2PIX( RADEON_CRTC_V_TOTAL ) ) {
281 			v_total = V_2PIX( RADEON_CRTC_V_TOTAL );
282 			v_sync_wid = min( v_sync_wid, v_total - v_sync_start - 4 );
283 		}
284 
285 		target->timing.v_display = v_display;
286 		target->timing.v_sync_start = v_sync_start;
287 		target->timing.v_sync_end = v_sync_start + v_sync_wid;
288 		target->timing.v_total = v_total;
289 	}
290 
291 	// did we fall out of one of the limits?
292 	if(	target->timing.v_display < low->timing.v_display ||
293 		target->timing.v_display > high->timing.v_display ||
294 		target->timing.v_sync_start < low->timing.v_sync_start ||
295 		target->timing.v_sync_start > high->timing.h_sync_start ||
296 		target->timing.v_sync_end < low->timing.v_sync_end ||
297 		target->timing.v_sync_end > high->timing.v_sync_end ||
298 		target->timing.v_total < low->timing.v_total ||
299 		target->timing.v_total > high->timing.v_total )
300 	{
301 		SHOW_FLOW0( 4, "out of vertical limits" );
302 		result = B_BAD_VALUE;
303 	}
304 
305 	// restore whished refresh rate
306 	target->timing.pixel_clock =
307 		((uint64)target_refresh / 1000 * target->timing.h_total * target->timing.v_total + FIX_SCALE / 2)
308 		>> FIX_SHIFT;
309 
310 	// apply PLL restrictions
311 	if( target->timing.pixel_clock / 10 > pll->max_pll_freq ||
312 		target->timing.pixel_clock / 10 * 12 < pll->min_pll_freq )
313 	{
314 		SHOW_ERROR( 2, "pixel_clock (%ld) out of range (%d, %d)", target->timing.pixel_clock,
315 			pll->max_pll_freq * 10, pll->min_pll_freq / 12 );
316 		return B_ERROR;
317 	}
318 
319 	// make sure virtual_size > visible_size
320 	// additionally, restore virtual_size == visible_size if it was so on entry
321 	if ((target->timing.h_display > target->virtual_width) || want_same_width)
322 		target->virtual_width = target->timing.h_display;
323 	if ((target->timing.v_display > target->virtual_height) || want_same_height)
324 		target->virtual_height = target->timing.v_display;
325 
326 	// TBD: limit is taken from XFree86
327 	// this is probably a CRTC limit; don't know about the accelerator limit (if any)
328 	// h_display can be at most 512*8, so we don't risk h_virtual < h_display
329 	// after applying this restriction
330 	if (target->virtual_width > 1024*8)
331 		target->virtual_width = 1024*8;
332 
333 	if (target->virtual_width < low->virtual_width ||
334 		target->virtual_width > high->virtual_width )
335 	{
336 		SHOW_FLOW0( 4, "out of virtual horizontal limits" );
337 		result = B_BAD_VALUE;
338 	}
339 
340 	// we may have to use a larger virtual width -
341 	// take care of that when calculating memory consumption
342 	eff_virtual_width = Radeon_RoundVWidth( target->virtual_height, bpp );
343 
344 	// calculate rowbytes after we've nailed the virtual width
345 	row_bytes = eff_virtual_width * bpp;
346 
347 	// if we haven't enough memory, reduce virtual height
348 	// (some programs create back buffers by asking for a huge
349 	// virtual screen; they actually want to know what is possible
350 	// to adjust the number of back buffers according to amount
351 	// of graphics memory)
352 
353 	// careful about additionally required memory:
354 	// 1024 bytes are needed for hardware cursor
355 	if ((row_bytes * target->virtual_height) > si->memory[mt_local].size - 1024 )
356 		target->virtual_height = (si->memory[mt_local].size - 1024) / row_bytes;
357 
358 	// make sure we haven't shrunk virtual height too much
359 	if (target->virtual_height < target->timing.v_display) {
360 		SHOW_ERROR( 2, "not enough memory for this mode (could show only %d of %d lines)",
361 			target->virtual_height, target->timing.v_display );
362 		return B_ERROR;
363 	}
364 
365 	if (target->virtual_height < low->virtual_height ||
366 		target->virtual_height > high->virtual_height )
367 	{
368 		SHOW_FLOW0( 4, "out of virtual vertical limits" );
369 		result = B_BAD_VALUE;
370 	}
371 
372 	// we ignore flags - in the sample driver, they did the same,
373 	// so why bother?
374 	return result;
375 }
376 
377 // public function: return number of display modes returned by get_mode_list
378 uint32 ACCELERANT_MODE_COUNT( void )
379 {
380 	return ai->si->mode_count;
381 }
382 
383 // public function: get list of standard display modes
384 //	dm - modes are copied to here (to be allocated by caller)
385 status_t GET_MODE_LIST( display_mode *dm )
386 {
387 	memcpy( dm, ai->mode_list, ai->si->mode_count * sizeof(display_mode) );
388 
389 	return B_OK;
390 }
391 
392 
393 static const color_space spaces[4] = {
394 	B_CMAP8, B_RGB15_LITTLE, B_RGB16_LITTLE, B_RGB32_LITTLE
395 };
396 
397 // if given mode is possible on this card, add it to standard mode list
398 //	mode - mode to add (colourspace is ignored but replaced
399 //	       by each officially supported colour space in turn)
400 //	ignore_timing - don't care if timing has to be modified to make mode valid
401 //	                (used for fp modes - we just want their resolution)
402 static void checkAndAddMode( accelerator_info *ai, const display_mode *mode, bool ignore_timing )
403 {
404 	shared_info *si = ai->si;
405 	uint i;
406 	display_mode low, high;
407 	uint32 pix_clk_range;
408 	display_mode *dst;
409 
410 	if( ignore_timing ) {
411 		// for fp modes: don't add mode if its resolution is already in official mode list
412 		for( i = 0; i < si->mode_count; ++i ) {
413 			if( ai->mode_list[i].timing.h_display == mode->timing.h_display &&
414 				ai->mode_list[i].timing.v_display == mode->timing.v_display &&
415 				ai->mode_list[i].virtual_width == mode->virtual_width &&
416 				ai->mode_list[i].virtual_height == mode->virtual_height )
417 				return;
418 		}
419 	}
420 
421 	// set ranges for acceptable values
422 	low = high = *mode;
423 
424 	// range is 6.25% of default clock: arbitrarily picked
425 	pix_clk_range = low.timing.pixel_clock >> 5;
426 	low.timing.pixel_clock -= pix_clk_range;
427 	high.timing.pixel_clock += pix_clk_range;
428 
429 	if( ignore_timing ) {
430 		low.timing.h_total = 0;
431 		low.timing.h_sync_start = 0;
432 		low.timing.h_sync_end = 0;
433 		low.timing.v_total = 0;
434 		low.timing.v_sync_start = 0;
435 		low.timing.v_sync_end = 0;
436 		high.timing.h_total = 0xffff;
437 		high.timing.h_sync_start = 0xffff;
438 		high.timing.h_sync_end = 0xffff;
439 		high.timing.v_total = 0xffff;
440 		high.timing.v_sync_start = 0xffff;
441 		high.timing.v_sync_end = 0xffff;
442 	}
443 
444 	dst = &ai->mode_list[si->mode_count];
445 
446 	// iterator through all colour spaces
447 	for( i = 0; i < (sizeof(spaces) / sizeof(color_space)); i++ ) {
448 		// check whether first port can handle it
449 		*dst = *mode;
450 		dst->space = low.space = high.space = spaces[i];
451 
452 		if( Radeon_ProposeDisplayMode( si, &si->crtc[0],
453 			&si->pll, dst, &low, &high ) == B_OK )
454 		{
455 			si->mode_count++;
456 			++dst;
457 
458 		} else {
459 			// it can't, so try second port
460 			*dst = *mode;
461 			dst->space = spaces[i];
462 
463 			if( Radeon_ProposeDisplayMode( si, &si->crtc[1],
464 				&si->pll, dst, &low, &high ) == B_OK )
465 			{
466 				si->mode_count++;
467 				++dst;
468 
469 			} else
470 				SHOW_FLOW( 4, "%ld, %ld not supported", dst->virtual_width, dst->virtual_height );
471 		}
472 	}
473 }
474 
475 
476 // add display mode including span mode variations to offical list
477 static void checkAndAddMultiMode( accelerator_info *ai, const display_mode *mode,
478 	bool ignore_timing )
479 {
480 	display_mode wide_mode;
481 
482 	SHOW_FLOW( 4, "%ld, %ld", mode->virtual_width, mode->virtual_height );
483 
484 	// plain mode
485 	checkAndAddMode( ai, mode, ignore_timing );
486 
487 	// double width mode
488 	wide_mode = *mode;
489 	wide_mode.virtual_width *= 2;
490 	wide_mode.flags |= B_SCROLL;
491 	checkAndAddMode( ai, &wide_mode, ignore_timing );
492 
493 	// double height mode
494 	wide_mode = *mode;
495 	wide_mode.virtual_height *= 2;
496 	wide_mode.flags |= B_SCROLL;
497 	checkAndAddMode( ai, &wide_mode, ignore_timing );
498 }
499 
500 // add display mode of flat panel to official list
501 static void addFPMode( accelerator_info *ai )
502 {
503 	shared_info *si = ai->si;
504 
505 	fp_info *fp_info = &si->flatpanels[0];
506 
507     if( (ai->vc->connected_displays & (dd_dvi | dd_lvds)) != 0 ) {
508     	display_mode mode;
509 
510 		mode.virtual_width = mode.timing.h_display = fp_info->panel_xres;
511 		mode.virtual_height = mode.timing.v_display = fp_info->panel_yres;
512 
513 		mode.timing.h_total = mode.timing.h_display + fp_info->h_blank;
514 		mode.timing.h_sync_start = mode.timing.h_display + fp_info->h_over_plus;
515 		mode.timing.h_sync_end = mode.timing.h_sync_start + fp_info->h_sync_width;
516 		mode.timing.v_total = mode.timing.v_display + fp_info->v_blank;
517 		mode.timing.v_sync_start = mode.timing.v_display + fp_info->v_over_plus;
518 		mode.timing.v_sync_end = mode.timing.v_sync_start + fp_info->v_sync_width;
519 
520 		mode.timing.pixel_clock = fp_info->dot_clock;
521 
522 		// if we have no pixel clock, assume 60 Hz
523 		// (as we don't program PLL in this case, it doesn't matter
524 		// if it's wrong, we just want this resolution in the mode list)
525 		if( mode.timing.pixel_clock == 0 ) {
526 			// devide by 1000 as clock is in kHz
527 			mode.timing.pixel_clock =
528 				((uint32)mode.timing.h_total * mode.timing.v_total * 60) / 1000;
529 		}
530 
531 		mode.flags = MODE_FLAGS;
532 		mode.h_display_start = 0;
533 		mode.v_display_start = 0;
534 
535 		SHOW_FLOW( 2, "H: %4d %4d %4d %4d (v=%4d)",
536 			mode.timing.h_display, mode.timing.h_sync_start,
537 			mode.timing.h_sync_end, mode.timing.h_total, mode.virtual_width );
538 		SHOW_FLOW( 2, "V: %4d %4d %4d %4d (h=%4d)",
539 			mode.timing.v_display, mode.timing.v_sync_start,
540 			mode.timing.v_sync_end, mode.timing.v_total, mode.virtual_height );
541 		SHOW_FLOW( 2, "clk: %ld", mode.timing.pixel_clock );
542 
543 		// flat panels seem to have strange timings;
544 		// as we ignore user-supplied timing for FPs anyway,
545 		// the mode can (and usually has to) be modified to be
546 		// used for normal CRTs
547 		checkAndAddMultiMode( ai, &mode, true );
548 	}
549 }
550 
551 // create list of officially supported modes
552 status_t Radeon_CreateModeList( shared_info *si )
553 {
554 	size_t max_size;
555 	uint i;
556 	uint max_num_modes;
557 
558 	// maximum number of official modes:
559 	// (predefined-modes + fp-modes) * number-of-colour-spaces * number-of-(non)-span-modes
560 	max_num_modes = ((sizeof( base_mode_list ) / sizeof( base_mode_list[0] ) + 1) * 4 * 3);
561 
562 	max_size = (max_num_modes * sizeof(display_mode) + (B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1);
563 
564 	si->mode_list_area = create_area("Radeon accelerant mode info",
565 		(void **)&ai->mode_list, B_ANY_ADDRESS,
566 		max_size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
567 
568 	if( si->mode_list_area < B_OK )
569 		return si->mode_list_area;
570 
571 	si->mode_count = 0;
572 
573 	// check standard modes
574 	for( i = 0; i < sizeof( base_mode_list ) / sizeof( base_mode_list[0] ); i++ )
575 		checkAndAddMultiMode( ai, &base_mode_list[i], false );
576 
577 	// plus fp mode
578 	addFPMode( ai );
579 
580 	// as we've created the list ourself, we don't clone it
581 	ai->mode_list_area = si->mode_list_area;
582 
583 	return B_OK;
584 }
585 
586 
587 // cleanup official display mode list
588 void Radeon_DisposeModeList( shared_info *si )
589 {
590 	delete_area( si->mode_list_area );
591 }
592 
593 
594 // public function: wraps for internal propose_display_mode
595 status_t PROPOSE_DISPLAY_MODE( display_mode *target, const display_mode *low,
596 	const display_mode *high )
597 {
598 	virtual_card *vc = ai->vc;
599 	shared_info *si = ai->si;
600 	status_t result1, result2;
601 	bool isTunneled;
602 	status_t result;
603 	display_mode tmp_target;
604 
605 	// check whether we got a tunneled settings command
606 	result = Radeon_CheckMultiMonTunnel( vc, target, low, high, &isTunneled );
607 	if( isTunneled )
608 		return result;
609 
610 	// check how many heads are needed by target mode
611 	tmp_target = *target;
612 	Radeon_DetectMultiMode( vc, &tmp_target );
613 
614 	// before checking multi-monitor mode, we must define a monitor signal routing
615 	// TBD: this may be called a bit too frequently if someone scans available modes
616 	// via successive Propose_Display_Mode; though this doesn't do any _real_ harm
617 	// it leads to annoying distortions on screen!!
618 	Radeon_DetectDisplays( ai);
619 	Radeon_SetupDefaultMonitorRouting(
620 		ai, Radeon_DifferentPorts( &tmp_target ), vc->use_laptop_panel );
621 
622 	// transform to multi-screen mode first
623 	Radeon_DetectMultiMode( vc, target );
624 	Radeon_VerifyMultiMode( vc, si, target );
625 
626 	SHOW_FLOW0( 3, "wished:" );
627 	SHOW_FLOW( 3, "H: %4d %4d %4d %4d (v=%4d)",
628 		target->timing.h_display, target->timing.h_sync_start,
629 		target->timing.h_sync_end, target->timing.h_total, target->virtual_width );
630 	SHOW_FLOW( 3, "V: %4d %4d %4d %4d (h=%4d)",
631 		target->timing.v_display, target->timing.v_sync_start,
632 		target->timing.v_sync_end, target->timing.v_total, target->virtual_height );
633 	SHOW_FLOW( 3, "clk: %ld", target->timing.pixel_clock );
634 
635 	// we must assure that each ProposeMode call doesn't tweak the mode in
636 	// a way that it cannot be handled by the other port anymore
637 	result1 = Radeon_ProposeDisplayMode( si, &si->crtc[0],
638 		&si->pll, target, low, high );
639 
640 	if( result1 == B_ERROR )
641 		return B_ERROR;
642 
643 	if( Radeon_NeedsSecondPort( target )) {
644 		// if both ports are used, make sure both can handle mode
645 		result2 = Radeon_ProposeDisplayMode( si, &si->crtc[1],
646 			&si->pll, target, low, high );
647 
648 		if( result2 == B_ERROR )
649 			return B_ERROR;
650 	} else {
651 		result2 = B_OK;
652 	}
653 
654 	SHOW_INFO0( 4, "got:" );
655 	SHOW_INFO( 4, "H: %4d %4d %4d %4d (v=%4d)",
656 		target->timing.h_display, target->timing.h_sync_start,
657 		target->timing.h_sync_end, target->timing.h_total, target->virtual_width );
658 	SHOW_INFO( 4, "V: %4d %4d %4d %4d (h=%4d)",
659 		target->timing.v_display, target->timing.v_sync_start,
660 		target->timing.v_sync_end, target->timing.v_total, target->virtual_height );
661 	SHOW_INFO( 4, "clk: %ld", target->timing.pixel_clock );
662 
663 	Radeon_HideMultiMode( vc, target );
664 
665 	if( result1 == B_OK && result2 == B_OK )
666 		return B_OK;
667 	else
668 		return B_BAD_VALUE;
669 }
670