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
Radeon_GetFormat(int space,int * format,int * bpp)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
Radeon_ProposeDisplayMode(shared_info * si,crtc_info * crtc,general_pll_info * pll,display_mode * target,const display_mode * low,const display_mode * high)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
ACCELERANT_MODE_COUNT(void)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)
GET_MODE_LIST(display_mode * dm)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)
checkAndAddMode(accelerator_info * ai,const display_mode * mode,bool ignore_timing)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
checkAndAddMultiMode(accelerator_info * ai,const display_mode * mode,bool ignore_timing)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
addFPMode(accelerator_info * ai)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
Radeon_CreateModeList(shared_info * si)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
PROPOSE_DISPLAY_MODE(display_mode * target,const display_mode * low,const display_mode * high)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