vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / accelerants / radeon / ProposeDisplayMode.c
blobbbfa956a0768a3082b76a82b2aa9955c223b51a9
1 /*
2 Copyright (c) 2002-2004, Thomas Kurschel
5 Part of Radeon accelerant
7 Everything concerning getting/testing display modes
8 */
10 #include "radeon_accelerant.h"
11 #include "generic.h"
12 #include <string.h>
13 #include "GlobalData.h"
15 #include "crtc_regs.h"
16 #include "utils.h"
17 #include "set_mode.h"
19 // standard mode list
20 // all drivers contain this list - this should really be moved to
21 // something like the screen preferences panel
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))
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) */
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) */
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 */
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 */
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) */
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 */
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) */
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) */
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) */
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) */
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 )
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;
116 return true;
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)
130 #define V_2REG( a ) ((a) - 1)
131 #define V_2PIX( a ) ((a) + 1)
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"
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.
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)
148 status_t result = B_OK;
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];
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);
161 if (target->timing.h_total * target->timing.v_total == 0)
162 return B_BAD_VALUE;
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);
170 want_same_width = target->timing.h_display == target->virtual_width;
171 want_same_height = target->timing.v_display == target->virtual_height;
173 if( !Radeon_GetFormat( target->space, &format, &bpp ))
174 return B_ERROR;
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;
183 if( target->timing.v_display > flatpanel->panel_yres )
184 target->timing.v_display = flatpanel->panel_yres;
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;
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;
204 if( target->timing.v_display > 768 )
205 target->timing.v_display = 768;
209 // validate horizontal timings
211 int h_sync_fudge, h_display, h_sync_start, h_sync_wid, h_total;
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;
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 ));
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;
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 ));
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 ));
259 // round properly
260 h_total = H_TOTAL_2PIX( H_TOTAL_2REG( h_total ));
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;
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)
278 SHOW_FLOW0( 4, "out of horizontal limits" );
279 result = B_BAD_VALUE;
282 // validate vertical timings
284 int v_display, v_sync_start, v_sync_wid, v_total;
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;
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;
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;
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);
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 );
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;
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 )
339 SHOW_FLOW0( 4, "out of vertical limits" );
340 result = B_BAD_VALUE;
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;
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 )
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;
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;
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;
371 if (target->virtual_width < low->virtual_width ||
372 target->virtual_width > high->virtual_width )
374 SHOW_FLOW0( 4, "out of virtual horizontal limits" );
375 result = B_BAD_VALUE;
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 );
382 // calculate rowbytes after we've nailed the virtual width
383 row_bytes = eff_virtual_width * bpp;
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)
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;
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;
403 if (target->virtual_height < low->virtual_height ||
404 target->virtual_height > high->virtual_height )
406 SHOW_FLOW0( 4, "out of virtual vertical limits" );
407 result = B_BAD_VALUE;
410 // we ignore flags - in the sample driver, they did the same,
411 // so why bother?
412 return result;
415 // public function: return number of display modes returned by get_mode_list
416 uint32 ACCELERANT_MODE_COUNT( void )
418 return ai->si->mode_count;
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 )
425 memcpy( dm, ai->mode_list, ai->si->mode_count * sizeof(display_mode) );
427 return B_OK;
431 static const color_space spaces[4] = {
432 B_CMAP8, B_RGB15_LITTLE, B_RGB16_LITTLE, B_RGB32_LITTLE
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 )
442 shared_info *si = ai->si;
443 uint i;
444 display_mode low, high;
445 uint32 pix_clk_range;
446 display_mode *dst;
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;
459 // set ranges for acceptable values
460 low = high = *mode;
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;
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;
482 dst = &ai->mode_list[si->mode_count];
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];
490 if( Radeon_ProposeDisplayMode( si, &si->crtc[0],
491 &si->pll, dst, &low, &high ) == B_OK )
493 si->mode_count++;
494 ++dst;
496 } else {
497 // it can't, so try second port
498 *dst = *mode;
499 dst->space = spaces[i];
501 if( Radeon_ProposeDisplayMode( si, &si->crtc[1],
502 &si->pll, dst, &low, &high ) == B_OK )
504 si->mode_count++;
505 ++dst;
507 } else
508 SHOW_FLOW( 4, "%ld, %ld not supported", dst->virtual_width, dst->virtual_height );
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 )
518 display_mode wide_mode;
520 SHOW_FLOW( 4, "%ld, %ld", mode->virtual_width, mode->virtual_height );
522 // plain mode
523 checkAndAddMode( ai, mode, ignore_timing );
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 );
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 );
538 // add display mode of flat panel to official list
539 static void addFPMode( accelerator_info *ai )
541 shared_info *si = ai->si;
543 fp_info *fp_info = &si->flatpanels[0]; //todo fix the hardcoding what about ext dvi?
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;
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;
558 mode.timing.pixel_clock = fp_info->dot_clock;
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;
569 mode.flags = MODE_FLAGS;
570 mode.h_display_start = 0;
571 mode.v_display_start = 0;
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 );
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 );
589 // create list of officially supported modes
590 status_t Radeon_CreateModeList( shared_info *si )
592 size_t max_size;
593 uint i;
594 uint max_num_modes;
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);
600 max_size = (max_num_modes * sizeof(display_mode) + (B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1);
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);
606 if( si->mode_list_area < B_OK )
607 return si->mode_list_area;
609 si->mode_count = 0;
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 );
615 // plus fp mode
616 addFPMode( ai );
618 // as we've created the list ourself, we don't clone it
619 ai->mode_list_area = si->mode_list_area;
621 return B_OK;
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)
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;
637 // check whether we got a tunneled settings command
638 result = Radeon_CheckMultiMonTunnel( vc, target, low, high, &isTunneled );
639 if( isTunneled )
640 return result;
642 // check how many heads are needed by target mode
643 tmp_target = *target;
644 Radeon_DetectMultiMode( vc, &tmp_target );
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 );
654 // transform to multi-screen mode first
655 Radeon_DetectMultiMode( vc, target );
656 Radeon_VerifyMultiMode( vc, si, target );
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 );
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 );
672 if( result1 == B_ERROR )
673 return B_ERROR;
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 );
680 if( result2 == B_ERROR )
681 return B_ERROR;
682 } else {
683 result2 = B_OK;
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 );
695 Radeon_HideMultiMode( vc, target );
697 if( result1 == B_OK && result2 == B_OK )
698 return B_OK;
699 else
700 return B_BAD_VALUE;