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
27static 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
102bool 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*/
143static status_t
144Radeon_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
416uint32 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)
423status_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
431static 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)
440static 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
515static 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
539static 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
590status_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
626status_t
627PROPOSE_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