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