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