xref: /haiku/src/add-ons/accelerants/radeon/ProposeDisplayMode.c (revision cda5b8808fd0262f0fac472f6cfa809f846a83cf)
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 { { 147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */
66 
67 { { 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) */
68 { { 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) */
69 { { 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) */
70 { { 122600, 1400, 1488, 1640, 1880, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1400, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1400X1050) */
71 { { 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) */
72 { { 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) */
73 { { 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) */
74 { { 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) */
75 { { 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) */
76 { { 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) */
77 // widescreen resolutions, 16:10
78 { { 31300, 800, 848, 928, 1008, 500, 501, 504, 518, T_POSITIVE_SYNC}, B_CMAP8, 800, 500, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X500) */
79 { { 52800, 1024, 1072, 1176, 1328, 640, 641, 644, 663, T_POSITIVE_SYNC}, B_CMAP8, 1024, 640, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X640) */
80 { { 80135, 1280, 1344, 1480, 1680, 768, 769, 772, 795, T_POSITIVE_SYNC}, B_CMAP8, 1280, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X768) */
81 { { 83500, 1280, 1344, 1480, 1680, 800, 801, 804, 828, T_POSITIVE_SYNC}, B_CMAP8, 1280, 800, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X800) */
82 { { 106500, 1440, 1520, 1672, 1904, 900, 901, 904, 932, T_POSITIVE_SYNC}, B_CMAP8, 1440, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1440X900) */
83 { { 147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */
84 /* 16:10 panel mode; 2.304M pixels */
85 { { 160000, 1920, 2010, 2060, 2110, 1200, 1202, 1208, 1235, T_POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */
86 // widescreen resolutions, 16:9
87 { { 74520, 1280, 1368, 1424, 1656, 720, 724, 730, 750, T_POSITIVE_SYNC}, B_CMAP8, 1280, 720, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X720) */
88 };
89 
90 
91 // convert Be colour space to Radeon data type
92 // returns true, if supported colour space
93 //	space - Be colour space
94 //	format - (out) Radeon data type
95 //	bpp - (out) bytes per pixel
96 bool Radeon_GetFormat( int space, int *format, int *bpp )
97 {
98 	switch( space ) {
99     /*case 4:  format = 1; bytpp = 0; break;*/
100     case B_CMAP8:  *format = 2; *bpp = 1; break;
101     case B_RGB15_LITTLE: *format = 3; *bpp = 2; break;      /*  555 */
102     case B_RGB16_LITTLE: *format = 4; *bpp = 2; break;      /*  565 */
103     case B_RGB24_LITTLE: *format = 5; *bpp = 3; break;      /*  RGB */
104     case B_RGB32_LITTLE: *format = 6; *bpp = 4; break;      /* xRGB */
105     default:
106 		SHOW_ERROR( 1, "Unsupported color space (%d)", space );
107 		return false;
108     }
109 
110     return true;
111 }
112 
113 
114 // macros to convert between register values and pixels
115 #define H_DISPLAY_2REG( a ) ((a) / 8 - 1)
116 #define H_DISPLAY_2PIX( a ) (((a) + 1) * 8)
117 #define H_TOTAL_2REG( a ) ((a) / 8 - 1)
118 #define H_TOTAL_2PIX( a ) (((a) + 1) * 8)
119 #define H_SSTART_2REG( a ) ((a) - 8 + h_sync_fudge)
120 #define H_SSTART_2PIX( a ) ((a) + 8 - h_sync_fudge)
121 #define H_SWID_2REG( a ) ((a) / 8)
122 #define H_SWID_2PIX( a ) ((a) * 8)
123 
124 #define V_2REG( a ) ((a) - 1)
125 #define V_2PIX( a ) ((a) + 1)
126 
127 /*
128 	Validate a target display mode is both
129 		a) a valid display mode for this device and
130 		b) falls between the contraints imposed by "low" and "high"
131 
132 	If the mode is not (or cannot) be made valid for this device, return B_ERROR.
133 	If a valid mode can be constructed, but it does not fall within the limits,
134 	 return B_BAD_VALUE.
135 	If the mode is both valid AND falls within the limits, return B_OK.
136 */
137 static status_t
138 Radeon_ProposeDisplayMode(shared_info *si, crtc_info *crtc,
139 	general_pll_info *pll, display_mode *target,
140 	const display_mode *low, const display_mode *high)
141 {
142 	status_t result = B_OK;
143 
144 	uint64 target_refresh;
145 	bool want_same_width, want_same_height;
146 	int format, bpp;
147 	uint32 row_bytes;
148 	int eff_virtual_width;
149 	fp_info *flatpanel = &si->flatpanels[crtc->flatpanel_port];
150 
151 	SHOW_FLOW( 4, "CRTC %d, DVI %d", (crtc == &si->crtc[0]) ? 0 : 1, crtc->flatpanel_port );
152 	SHOW_FLOW( 4, "X %d, virtX %d", target->timing.h_display,  target->virtual_width);
153 	SHOW_FLOW( 4, "fpRes %dx%d", flatpanel->panel_xres,  flatpanel->panel_yres);
154 
155 	if (target->timing.h_total * target->timing.v_total == 0)
156 		return B_BAD_VALUE;
157 
158 	// save refresh rate - we want to leave this (artifical) value untouched
159 	// don't use floating point, we are in kernel mode
160 	target_refresh =
161 		(((uint64)target->timing.pixel_clock * 1000) << FIX_SHIFT) /
162 		((uint64)target->timing.h_total * target->timing.v_total);
163 
164 	want_same_width = target->timing.h_display == target->virtual_width;
165 	want_same_height = target->timing.v_display == target->virtual_height;
166 
167 	if( !Radeon_GetFormat( target->space, &format, &bpp ))
168 		return B_ERROR;
169 
170 	// for flat panels, check maximum resolution;
171 	// all the other tricks (like fixed resolution and resulting scaling)
172 	// are done automagically by set_display_mode
173     if( (crtc->chosen_displays & (dd_lvds | dd_dvi)) != 0 ) {
174 		if( target->timing.h_display > flatpanel->panel_xres )
175 			target->timing.h_display = flatpanel->panel_xres;
176 
177 		if(	target->timing.v_display > flatpanel->panel_yres )
178 			target->timing.v_display = flatpanel->panel_yres;
179 	}
180 
181 	// for secondary flat panels there is no RMX unit for
182 	// scaling up lower resolutions.  Until we can do centered timings
183 	// we need to disable the screen unless it is the native resolution.
184 	// if the DVI input has a scaler we would need to know about it somehow...
185     if( (crtc->chosen_displays & dd_dvi_ext) != 0 ) {
186 		SHOW_FLOW0( 4, "external (secondary) DVI cannot support non-native resolutions" );
187 		if( ( target->timing.h_display != flatpanel->panel_xres ) ||
188 			( target->timing.v_display != flatpanel->panel_yres ) )
189 			return B_ERROR;
190 	}
191 
192 /*
193 	// the TV-Out encoder can "only" handle up to 1024x768
194 	if( (head->chosen_displays & (dd_ctv | dd_stv)) != 0 ) {
195 		if( target->timing.h_display > 1024 )
196 			target->timing.h_display = 1024;
197 
198 		if(	target->timing.v_display > 768 )
199 			target->timing.v_display = 768;
200 	}
201 */
202 
203 	// validate horizontal timings
204 	{
205 		int h_sync_fudge, h_display, h_sync_start, h_sync_wid, h_total;
206 
207 		h_display = target->timing.h_display;
208 		h_sync_fudge = Radeon_GetHSyncFudge( crtc, format );
209 		h_sync_start = target->timing.h_sync_start;
210 		h_sync_wid = target->timing.h_sync_end - target->timing.h_sync_start;
211 		h_total = target->timing.h_total;
212 
213 		// make sure, display is not too small
214 		// (I reckon Radeon doesn't care, but your monitor probably does)
215 		if( h_display < 320 )
216 			h_display = 320;
217 		// apply hardware restrictions
218 		// as h_display is the smallest register, it's always possible
219 		// to adjust other values to keep them in supported range
220 		if( h_display > H_DISPLAY_2PIX( RADEON_CRTC_H_DISP >> RADEON_CRTC_H_DISP_SHIFT ) )
221 			h_display = H_DISPLAY_2PIX( RADEON_CRTC_H_DISP >> RADEON_CRTC_H_DISP_SHIFT );
222 		// round properly
223 		h_display = H_DISPLAY_2PIX( H_DISPLAY_2REG( h_display ));
224 
225 		// ensure minimum time before sync
226 		if( h_sync_start < h_display + 2*8 )
227 			h_sync_start = h_display + 2*8;
228 		// sync has wider range than display are, so we won't collide there,
229 		// but total width has same range as sync start, so leave some space
230 		if( h_sync_start > H_SSTART_2PIX( RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX ) - 4*8 )
231 			h_sync_start = H_SSTART_2PIX( RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX ) - 4*8;
232 
233 		// ensure minimum sync length
234 		if( h_sync_wid < H_SWID_2PIX( 3 ))
235 			h_sync_wid = H_SWID_2PIX( 3 );
236 		// allowed range is quite small, so make sure sync isn't too long
237 		if( h_sync_wid > H_SWID_2PIX( RADEON_CRTC_H_SYNC_WID >> RADEON_CRTC_H_SYNC_WID_SHIFT ) )
238 			h_sync_wid = H_SWID_2PIX( RADEON_CRTC_H_SYNC_WID >> RADEON_CRTC_H_SYNC_WID_SHIFT );
239 		// round properly
240 		h_sync_wid = H_SWID_2PIX( H_SWID_2REG( h_sync_wid ));
241 
242 		// last but not least adapt total width
243 		// "+7" is needed for rounding up: sync_start isn't rounded, but h_total is
244 		if( h_total < h_sync_start + h_sync_wid + 1*8 + 7 )
245 			h_total = h_sync_start + h_sync_wid + 1*8 + 7;
246 		// we may get a too long total width; this can only happen
247 		// because sync is too long, so truncate sync accordingly
248 		if( h_total > H_TOTAL_2PIX( RADEON_CRTC_H_TOTAL ) ) {
249 			h_total = H_TOTAL_2PIX( RADEON_CRTC_H_TOTAL );
250 			h_sync_wid = min( h_sync_wid, h_total - h_sync_start );
251 			h_sync_wid = H_SWID_2PIX( H_SWID_2REG( h_sync_wid ));
252 		}
253 		// round properly
254 		h_total = H_TOTAL_2PIX( H_TOTAL_2REG( h_total ));
255 
256 		target->timing.h_display = h_display;
257 		target->timing.h_sync_start = h_sync_start;
258 		target->timing.h_sync_end = h_sync_start + h_sync_wid;
259 		target->timing.h_total = h_total;
260 	}
261 
262 	// did we fall out of one of the limits?
263 	if( target->timing.h_display < low->timing.h_display ||
264 		target->timing.h_display > high->timing.h_display ||
265 		target->timing.h_sync_start < low->timing.h_sync_start ||
266 		target->timing.h_sync_start > high->timing.h_sync_start ||
267 		target->timing.h_sync_end < low->timing.h_sync_end ||
268 		target->timing.h_sync_end > high->timing.h_sync_end ||
269 		target->timing.h_total < low->timing.h_total ||
270 		target->timing.h_total > high->timing.h_total)
271 	{
272 		SHOW_FLOW0( 4, "out of horizontal limits" );
273 		result = B_BAD_VALUE;
274 	}
275 
276 	// validate vertical timings
277 	{
278 		int v_display, v_sync_start, v_sync_wid, v_total;
279 
280 		v_display = target->timing.v_display;
281 		v_sync_start = target->timing.v_sync_start;
282 		v_sync_wid = target->timing.v_sync_end - target->timing.v_sync_start;
283 		v_total = target->timing.v_total;
284 
285 		// apply a reasonable minimal height to make monitor happy
286 		if( v_display < 200 )
287 			v_display = 200;
288 		// apply limits but make sure we have enough lines left for blank and sync
289 		if( v_display > V_2PIX(RADEON_CRTC_V_DISP >> RADEON_CRTC_V_DISP_SHIFT) - 5)
290 			v_display = V_2PIX(RADEON_CRTC_V_DISP >> RADEON_CRTC_V_DISP_SHIFT) - 5;
291 
292 		// leave at least one line before sync
293 		// (some flat panel have zero gap here; probably, this leads to
294 		// the infamous bright line at top of screen)
295 		if( v_sync_start < v_display + 1 )
296 			v_sync_start = v_display + 1;
297 		// apply hardware limit and leave some lines for sync
298 		if( v_sync_start > V_2PIX(RADEON_CRTC_V_SYNC_STRT) - 4)
299 			v_sync_start = V_2PIX(RADEON_CRTC_V_SYNC_STRT) - 4;
300 
301 		// don't make sync too short
302 		if( v_sync_wid < 2 )
303 			v_sync_wid = 2;
304 		// sync width is quite restricted
305 		if( v_sync_wid > (RADEON_CRTC_V_SYNC_WID >> RADEON_CRTC_V_SYNC_WID_SHIFT))
306 			v_sync_wid = (RADEON_CRTC_V_SYNC_WID >> RADEON_CRTC_V_SYNC_WID_SHIFT);
307 
308 		// leave a gap of at least 1 line
309 		if( v_total < v_sync_start + v_sync_wid + 1 )
310 			v_total = v_sync_start + v_sync_wid + 1;
311 		// if too long, truncate it and adapt sync len
312 		if( v_total > V_2PIX( RADEON_CRTC_V_TOTAL ) ) {
313 			v_total = V_2PIX( RADEON_CRTC_V_TOTAL );
314 			v_sync_wid = min( v_sync_wid, v_total - v_sync_start - 4 );
315 		}
316 
317 		target->timing.v_display = v_display;
318 		target->timing.v_sync_start = v_sync_start;
319 		target->timing.v_sync_end = v_sync_start + v_sync_wid;
320 		target->timing.v_total = v_total;
321 	}
322 
323 	// did we fall out of one of the limits?
324 	if(	target->timing.v_display < low->timing.v_display ||
325 		target->timing.v_display > high->timing.v_display ||
326 		target->timing.v_sync_start < low->timing.v_sync_start ||
327 		target->timing.v_sync_start > high->timing.h_sync_start ||
328 		target->timing.v_sync_end < low->timing.v_sync_end ||
329 		target->timing.v_sync_end > high->timing.v_sync_end ||
330 		target->timing.v_total < low->timing.v_total ||
331 		target->timing.v_total > high->timing.v_total )
332 	{
333 		SHOW_FLOW0( 4, "out of vertical limits" );
334 		result = B_BAD_VALUE;
335 	}
336 
337 	// restore whished refresh rate
338 	target->timing.pixel_clock =
339 		((uint64)target_refresh / 1000 * target->timing.h_total * target->timing.v_total + FIX_SCALE / 2)
340 		>> FIX_SHIFT;
341 
342 	// apply PLL restrictions
343 	if( target->timing.pixel_clock / 10 > pll->max_pll_freq ||
344 		target->timing.pixel_clock / 10 * 12 < pll->min_pll_freq )
345 	{
346 		SHOW_ERROR( 4, "pixel_clock (%ld) out of range (%d, %d)", target->timing.pixel_clock,
347 			pll->max_pll_freq * 10, pll->min_pll_freq / 12 );
348 		return B_ERROR;
349 	}
350 
351 	// make sure virtual_size > visible_size
352 	// additionally, restore virtual_size == visible_size if it was so on entry
353 	if ((target->timing.h_display > target->virtual_width) || want_same_width)
354 		target->virtual_width = target->timing.h_display;
355 	if ((target->timing.v_display > target->virtual_height) || want_same_height)
356 		target->virtual_height = target->timing.v_display;
357 
358 	// TBD: limit is taken from XFree86
359 	// this is probably a CRTC limit; don't know about the accelerator limit (if any)
360 	// h_display can be at most 512*8, so we don't risk h_virtual < h_display
361 	// after applying this restriction
362 	if (target->virtual_width > 1024*8)
363 		target->virtual_width = 1024*8;
364 
365 	if (target->virtual_width < low->virtual_width ||
366 		target->virtual_width > high->virtual_width )
367 	{
368 		SHOW_FLOW0( 4, "out of virtual horizontal limits" );
369 		result = B_BAD_VALUE;
370 	}
371 
372 	// we may have to use a larger virtual width -
373 	// take care of that when calculating memory consumption
374 	eff_virtual_width = Radeon_RoundVWidth( target->virtual_height, bpp );
375 
376 	// calculate rowbytes after we've nailed the virtual width
377 	row_bytes = eff_virtual_width * bpp;
378 
379 	// if we haven't enough memory, reduce virtual height
380 	// (some programs create back buffers by asking for a huge
381 	// virtual screen; they actually want to know what is possible
382 	// to adjust the number of back buffers according to amount
383 	// of graphics memory)
384 
385 	// careful about additionally required memory:
386 	// 1024 bytes are needed for hardware cursor
387 	if ((row_bytes * target->virtual_height) > si->memory[mt_local].size - 1024 )
388 		target->virtual_height = (si->memory[mt_local].size - 1024) / row_bytes;
389 
390 	// make sure we haven't shrunk virtual height too much
391 	if (target->virtual_height < target->timing.v_display) {
392 		SHOW_ERROR( 4, "not enough memory for this mode (could show only %d of %d lines)",
393 			target->virtual_height, target->timing.v_display );
394 		return B_ERROR;
395 	}
396 
397 	if (target->virtual_height < low->virtual_height ||
398 		target->virtual_height > high->virtual_height )
399 	{
400 		SHOW_FLOW0( 4, "out of virtual vertical limits" );
401 		result = B_BAD_VALUE;
402 	}
403 
404 	// we ignore flags - in the sample driver, they did the same,
405 	// so why bother?
406 	return result;
407 }
408 
409 // public function: return number of display modes returned by get_mode_list
410 uint32 ACCELERANT_MODE_COUNT( void )
411 {
412 	return ai->si->mode_count;
413 }
414 
415 // public function: get list of standard display modes
416 //	dm - modes are copied to here (to be allocated by caller)
417 status_t GET_MODE_LIST( display_mode *dm )
418 {
419 	memcpy( dm, ai->mode_list, ai->si->mode_count * sizeof(display_mode) );
420 
421 	return B_OK;
422 }
423 
424 
425 static const color_space spaces[4] = {
426 	B_CMAP8, B_RGB15_LITTLE, B_RGB16_LITTLE, B_RGB32_LITTLE
427 };
428 
429 // if given mode is possible on this card, add it to standard mode list
430 //	mode - mode to add (colourspace is ignored but replaced
431 //	       by each officially supported colour space in turn)
432 //	ignore_timing - don't care if timing has to be modified to make mode valid
433 //	                (used for fp modes - we just want their resolution)
434 static void checkAndAddMode( accelerator_info *ai, const display_mode *mode, bool ignore_timing )
435 {
436 	shared_info *si = ai->si;
437 	uint i;
438 	display_mode low, high;
439 	uint32 pix_clk_range;
440 	display_mode *dst;
441 
442 	if( ignore_timing ) {
443 		// for fp modes: don't add mode if its resolution is already in official mode list
444 		for( i = 0; i < si->mode_count; ++i ) {
445 			if( ai->mode_list[i].timing.h_display == mode->timing.h_display &&
446 				ai->mode_list[i].timing.v_display == mode->timing.v_display &&
447 				ai->mode_list[i].virtual_width == mode->virtual_width &&
448 				ai->mode_list[i].virtual_height == mode->virtual_height )
449 				return;
450 		}
451 	}
452 
453 	// set ranges for acceptable values
454 	low = high = *mode;
455 
456 	// range is 6.25% of default clock: arbitrarily picked
457 	pix_clk_range = low.timing.pixel_clock >> 5;
458 	low.timing.pixel_clock -= pix_clk_range;
459 	high.timing.pixel_clock += pix_clk_range;
460 
461 	if( ignore_timing ) {
462 		low.timing.h_total = 0;
463 		low.timing.h_sync_start = 0;
464 		low.timing.h_sync_end = 0;
465 		low.timing.v_total = 0;
466 		low.timing.v_sync_start = 0;
467 		low.timing.v_sync_end = 0;
468 		high.timing.h_total = 0xffff;
469 		high.timing.h_sync_start = 0xffff;
470 		high.timing.h_sync_end = 0xffff;
471 		high.timing.v_total = 0xffff;
472 		high.timing.v_sync_start = 0xffff;
473 		high.timing.v_sync_end = 0xffff;
474 	}
475 
476 	dst = &ai->mode_list[si->mode_count];
477 
478 	// iterator through all colour spaces
479 	for( i = 0; i < (sizeof(spaces) / sizeof(color_space)); i++ ) {
480 		// check whether first port can handle it
481 		*dst = *mode;
482 		dst->space = low.space = high.space = spaces[i];
483 
484 		if( Radeon_ProposeDisplayMode( si, &si->crtc[0],
485 			&si->pll, dst, &low, &high ) == B_OK )
486 		{
487 			si->mode_count++;
488 			++dst;
489 
490 		} else {
491 			// it can't, so try second port
492 			*dst = *mode;
493 			dst->space = spaces[i];
494 
495 			if( Radeon_ProposeDisplayMode( si, &si->crtc[1],
496 				&si->pll, dst, &low, &high ) == B_OK )
497 			{
498 				si->mode_count++;
499 				++dst;
500 
501 			} else
502 				SHOW_FLOW( 4, "%ld, %ld not supported", dst->virtual_width, dst->virtual_height );
503 		}
504 	}
505 }
506 
507 
508 // add display mode including span mode variations to offical list
509 static void checkAndAddMultiMode( accelerator_info *ai, const display_mode *mode,
510 	bool ignore_timing )
511 {
512 	display_mode wide_mode;
513 
514 	SHOW_FLOW( 4, "%ld, %ld", mode->virtual_width, mode->virtual_height );
515 
516 	// plain mode
517 	checkAndAddMode( ai, mode, ignore_timing );
518 
519 	// double width mode
520 	wide_mode = *mode;
521 	wide_mode.virtual_width *= 2;
522 	wide_mode.flags |= B_SCROLL;
523 	checkAndAddMode( ai, &wide_mode, ignore_timing );
524 
525 	// double height mode
526 	wide_mode = *mode;
527 	wide_mode.virtual_height *= 2;
528 	wide_mode.flags |= B_SCROLL;
529 	checkAndAddMode( ai, &wide_mode, ignore_timing );
530 }
531 
532 // add display mode of flat panel to official list
533 static void addFPMode( accelerator_info *ai )
534 {
535 	shared_info *si = ai->si;
536 
537 	fp_info *fp_info = &si->flatpanels[0];	//todo fix the hardcoding what about ext dvi?
538 
539     if( (ai->vc->connected_displays & (dd_dvi | dd_dvi_ext | dd_lvds)) != 0 ) {
540     	display_mode mode;
541     	SHOW_FLOW0( 2, "" );
542 		mode.virtual_width = mode.timing.h_display = fp_info->panel_xres;
543 		mode.virtual_height = mode.timing.v_display = fp_info->panel_yres;
544 
545 		mode.timing.h_total = mode.timing.h_display + fp_info->h_blank;
546 		mode.timing.h_sync_start = mode.timing.h_display + fp_info->h_over_plus;
547 		mode.timing.h_sync_end = mode.timing.h_sync_start + fp_info->h_sync_width;
548 		mode.timing.v_total = mode.timing.v_display + fp_info->v_blank;
549 		mode.timing.v_sync_start = mode.timing.v_display + fp_info->v_over_plus;
550 		mode.timing.v_sync_end = mode.timing.v_sync_start + fp_info->v_sync_width;
551 
552 		mode.timing.pixel_clock = fp_info->dot_clock;
553 
554 		// if we have no pixel clock, assume 60 Hz
555 		// (as we don't program PLL in this case, it doesn't matter
556 		// if it's wrong, we just want this resolution in the mode list)
557 		if( mode.timing.pixel_clock == 0 ) {
558 			// devide by 1000 as clock is in kHz
559 			mode.timing.pixel_clock =
560 				((uint32)mode.timing.h_total * mode.timing.v_total * 60) / 1000;
561 		}
562 
563 		mode.flags = MODE_FLAGS;
564 		mode.h_display_start = 0;
565 		mode.v_display_start = 0;
566 
567 		SHOW_FLOW( 2, "H: %4d %4d %4d %4d (v=%4d)",
568 			mode.timing.h_display, mode.timing.h_sync_start,
569 			mode.timing.h_sync_end, mode.timing.h_total, mode.virtual_width );
570 		SHOW_FLOW( 2, "V: %4d %4d %4d %4d (h=%4d)",
571 			mode.timing.v_display, mode.timing.v_sync_start,
572 			mode.timing.v_sync_end, mode.timing.v_total, mode.virtual_height );
573 		SHOW_FLOW( 2, "clk: %ld", mode.timing.pixel_clock );
574 
575 		// flat panels seem to have strange timings;
576 		// as we ignore user-supplied timing for FPs anyway,
577 		// the mode can (and usually has to) be modified to be
578 		// used for normal CRTs
579 		checkAndAddMultiMode( ai, &mode, true );
580 	}
581 }
582 
583 // create list of officially supported modes
584 status_t Radeon_CreateModeList( shared_info *si )
585 {
586 	size_t max_size;
587 	uint i;
588 	uint max_num_modes;
589 
590 	// maximum number of official modes:
591 	// (predefined-modes + fp-modes) * number-of-colour-spaces * number-of-(non)-span-modes
592 	max_num_modes = ((sizeof( base_mode_list ) / sizeof( base_mode_list[0] ) + 1) * 4 * 3);
593 
594 	max_size = (max_num_modes * sizeof(display_mode) + (B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1);
595 
596 	si->mode_list_area = create_area("Radeon accelerant mode info",
597 		(void **)&ai->mode_list, B_ANY_ADDRESS,
598 		max_size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
599 
600 	if( si->mode_list_area < B_OK )
601 		return si->mode_list_area;
602 
603 	si->mode_count = 0;
604 
605 	// check standard modes
606 	for( i = 0; i < sizeof( base_mode_list ) / sizeof( base_mode_list[0] ); i++ )
607 		checkAndAddMultiMode( ai, &base_mode_list[i], false );
608 
609 	// plus fp mode
610 	addFPMode( ai );
611 
612 	// as we've created the list ourself, we don't clone it
613 	ai->mode_list_area = si->mode_list_area;
614 
615 	return B_OK;
616 }
617 
618 
619 //! public function: wraps for internal propose_display_mode
620 status_t
621 PROPOSE_DISPLAY_MODE(display_mode *target, const display_mode *low,
622 	const display_mode *high)
623 {
624 	virtual_card *vc = ai->vc;
625 	shared_info *si = ai->si;
626 	status_t result1, result2;
627 	bool isTunneled;
628 	status_t result;
629 	display_mode tmp_target;
630 
631 	// check whether we got a tunneled settings command
632 	result = Radeon_CheckMultiMonTunnel( vc, target, low, high, &isTunneled );
633 	if( isTunneled )
634 		return result;
635 
636 	// check how many heads are needed by target mode
637 	tmp_target = *target;
638 	Radeon_DetectMultiMode( vc, &tmp_target );
639 
640 	// before checking multi-monitor mode, we must define a monitor signal routing
641 	// TBD: this may be called a bit too frequently if someone scans available modes
642 	// via successive Propose_Display_Mode; though this doesn't do any _real_ harm
643 	// it leads to annoying distortions on screen!!
644 	Radeon_DetectDisplays( ai);
645 	Radeon_SetupDefaultMonitorRouting(
646 		ai, Radeon_DifferentPorts( &tmp_target ), vc->use_laptop_panel );
647 
648 	// transform to multi-screen mode first
649 	Radeon_DetectMultiMode( vc, target );
650 	Radeon_VerifyMultiMode( vc, si, target );
651 
652 	SHOW_FLOW0( 2, "wished:" );
653 	SHOW_FLOW( 2, "H: %4d %4d %4d %4d (v=%4d)",
654 		target->timing.h_display, target->timing.h_sync_start,
655 		target->timing.h_sync_end, target->timing.h_total, target->virtual_width );
656 	SHOW_FLOW( 2, "V: %4d %4d %4d %4d (h=%4d)",
657 		target->timing.v_display, target->timing.v_sync_start,
658 		target->timing.v_sync_end, target->timing.v_total, target->virtual_height );
659 	SHOW_FLOW( 2, "clk: %ld", target->timing.pixel_clock );
660 
661 	// we must assure that each ProposeMode call doesn't tweak the mode in
662 	// a way that it cannot be handled by the other port anymore
663 	result1 = Radeon_ProposeDisplayMode( si, &si->crtc[0],
664 		&si->pll, target, low, high );
665 
666 	if( result1 == B_ERROR )
667 		return B_ERROR;
668 
669 	if( Radeon_NeedsSecondPort( target )) {
670 		// if both ports are used, make sure both can handle mode
671 		result2 = Radeon_ProposeDisplayMode( si, &si->crtc[1],
672 			&si->pll, target, low, high );
673 
674 		if( result2 == B_ERROR )
675 			return B_ERROR;
676 	} else {
677 		result2 = B_OK;
678 	}
679 
680 	SHOW_INFO0( 2, "got:" );
681 	SHOW_INFO( 2, "H: %4d %4d %4d %4d (v=%4d)",
682 		target->timing.h_display, target->timing.h_sync_start,
683 		target->timing.h_sync_end, target->timing.h_total, target->virtual_width );
684 	SHOW_INFO( 2, "V: %4d %4d %4d %4d (h=%4d)",
685 		target->timing.v_display, target->timing.v_sync_start,
686 		target->timing.v_sync_end, target->timing.v_total, target->virtual_height );
687 	SHOW_INFO( 2, "clk: %ld", target->timing.pixel_clock );
688 
689 	Radeon_HideMultiMode( vc, target );
690 
691 	if( result1 == B_OK && result2 == B_OK )
692 		return B_OK;
693 	else
694 		return B_BAD_VALUE;
695 }
696