1/*
2	Copyright 1999, Be Incorporated.   All Rights Reserved.
3	This file may be used under the terms of the Be Sample Code License.
4
5	Other authors for NV driver:
6	Mark Watson,
7	Rudolf Cornelissen 9/2002-1/2016
8*/
9
10#define MODULE_BIT 0x00400000
11
12#include "acc_std.h"
13
14#define	T_POSITIVE_SYNC	(B_POSITIVE_HSYNC | B_POSITIVE_VSYNC)
15/* mode flags will be setup as status info by PROPOSEMODE! */
16#define MODE_FLAGS 0
17#define MODE_COUNT (sizeof (mode_list) / sizeof (display_mode))
18
19/* Standard VESA modes,
20 * plus panel specific resolution modes which are internally modified during run-time depending on the requirements of the actual
21 * panel connected. The modes as listed here, should timing-wise be as compatible with analog (CRT) monitors as can be... */
22//fixme: if EDID monitor found create list via common EDID code...
23static const display_mode mode_list[] = {
24/* 4:3 modes; 307.2k pixels */
25{ { 25175, 640, 656, 752, 800, 480, 490, 492, 525, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */
26{ { 27500, 640, 672, 768, 864, 480, 488, 494, 530, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* 640X480X60Hz */
27{ { 30500, 640, 672, 768, 864, 480, 517, 523, 588, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* SVGA_640X480X60HzNI */
28{ { 31500, 640, 664, 704, 832, 480, 489, 492, 520, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(640X480X8.Z1) */
29{ { 31500, 640, 656, 720, 840, 480, 481, 484, 500, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(640X480X8.Z1) */
30{ { 36000, 640, 696, 752, 832, 480, 481, 484, 509, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(640X480X8.Z1) */
31/* 4:3 modes; 480k pixels */
32{ { 36000, 800, 824, 896, 1024, 600, 601, 603, 625, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@56Hz_(800X600) from Be, Inc. driver + XFree86 */
33{ { 38100, 800, 832, 960, 1088, 600, 602, 606, 620, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* SVGA_800X600X56HzNI */
34{ { 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) + XFree86 */
35{ { 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) + XFree86 */
36{ { 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) + XFree86 */
37{ { 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) + XFree86 */
38/* 4:3 modes; 786.432k pixels */
39{ { 65000, 1024, 1048, 1184, 1344, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X768X8.Z1) + XFree86 */
40{ { 75000, 1024, 1048, 1184, 1328, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(1024X768X8.Z1) + XFree86 */
41{ { 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) + XFree86 */
42{ { 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) + XFree86 */
43/* 4:3 modes; 995.328k pixels */
44{ { 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) */
45{ { 97800, 1152, 1216, 1344, 1552, 864, 865, 868, 900, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1152X864X8.Z1) */
46{ { 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) + XFree86 */
47{ { 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) */
48/* 5:4 modes; 1.311M pixels */
49{ { 108000, 1280, 1328, 1440, 1688, 1024, 1025, 1028, 1066, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024) from Be, Inc. driver + XFree86 */
50{ { 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) + XFree86 */
51{ { 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) + XFree86 */
52/* 4:3 panel mode; 1.47M pixels */
53{ { 122600, 1400, 1488, 1640, 1880, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1400, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1400X1050) */
54/* 4:3 modes; 1.92M pixels */
55{ { 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) + XFree86 */
56/* identical lines to above one, apart from refreshrate.. */
57{ { 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) + XFree86 */
58{ { 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) + XFree86 */
59{ { 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) + XFree86 */
60{ { 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) */
61{ { 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) + XFree86 */
62/* end identical lines. */
63/* 4:3 modes; 2.408M pixels */
64{ { 204750, 1792, 1920, 2120, 2448, 1344, 1345, 1348, 1394, B_POSITIVE_VSYNC}, B_CMAP8, 1792, 1344, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1792X1344) from Be, Inc. driver + XFree86 */
65{ { 261000, 1792, 1888, 2104, 2456, 1344, 1345, 1348, 1417, B_POSITIVE_VSYNC}, B_CMAP8, 1792, 1344, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1792X1344) from Be, Inc. driver + XFree86 */
66/* 4:3 modes; 2.584M pixels */
67{ { 218250, 1856, 1952, 2176, 2528, 1392, 1393, 1396, 1439, B_POSITIVE_VSYNC}, B_CMAP8, 1856, 1392, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1856X1392) from Be, Inc. driver + XFree86 */
68{ { 288000, 1856, 1984, 2208, 2560, 1392, 1393, 1396, 1500, B_POSITIVE_VSYNC}, B_CMAP8, 1856, 1392, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1856X1392) from Be, Inc. driver + XFree86 */
69/* 4:3 modes; 2.765M pixels */
70{ { 234000, 1920, 2048, 2256, 2600, 1440, 1441, 1444, 1500, B_POSITIVE_VSYNC}, B_CMAP8, 1920, 1440, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1440) from Be, Inc. driver + XFree86 */
71{ { 297000, 1920, 2064, 2288, 2640, 1440, 1441, 1444, 1500, B_POSITIVE_VSYNC}, B_CMAP8, 1920, 1440, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1920X1440) from Be, Inc. driver + XFree86 */
72/* 4:3 modes; 3.146M pixels */
73{ { 266950, 2048, 2200, 2424, 2800, 1536, 1537, 1540, 1589, B_POSITIVE_VSYNC}, B_CMAP8, 2048, 1536, 0, 0, MODE_FLAGS}, /* From XFree86 posting @60Hz + XFree86 */
74/* 16:10 panel mode; 400k pixels */
75{ { 31300, 800, 848, 928, 1008, 500, 501, 504, 518, T_POSITIVE_SYNC}, B_CMAP8, 800, 500, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X500) */
76/* 16:10 panel mode; 655.36k pixels */
77{ { 52800, 1024, 1072, 1176, 1328, 640, 641, 644, 663, T_POSITIVE_SYNC}, B_CMAP8, 1024, 640, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X640) */
78/* 16:10 panel-TV mode; 983.04k pixels */
79{ { 80135, 1280, 1344, 1480, 1680, 768, 769, 772, 795, T_POSITIVE_SYNC}, B_CMAP8, 1280, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X768) */
80/* 16:10 panel mode; 1.024M pixels */
81{ { 83500, 1280, 1344, 1480, 1680, 800, 801, 804, 828, T_POSITIVE_SYNC}, B_CMAP8, 1280, 800, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X800) */
82/* 16:10 panel mode; 1.296M pixels */
83{ { 106500, 1440, 1520, 1672, 1904, 900, 901, 904, 932, T_POSITIVE_SYNC}, B_CMAP8, 1440, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1440X900) */
84/* 16:10 panel mode; 1.764M pixels */
85{ { 147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */
86/* 16:10 panel mode; 2.304M pixels */
87{ { 193160, 1920, 2048, 2256, 2592, 1200, 1201, 1204, 1242, T_POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */
88//{ { 160000, 1920, 2010, 2060, 2110, 1200, 1202, 1208, 1235, T_POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */
89/* 16:9 panel mode; 1280x720 (HDTV 1280x720p) */
90{ { 74520, 1280, 1368, 1424, 1656, 720, 724, 730, 750, T_POSITIVE_SYNC}, B_CMAP8, 1280, 720, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X720) */
91/* 16:9 panel mode; 1366x768 (HDTV '1280x720p')
92   note: horizontal CRTC timing must be a multiple of 8! (hardware restriction) */
93{ { 85500, 1368, 1440, 1576, 1792, 768, 771, 774, 798, T_POSITIVE_SYNC}, B_CMAP8, 1368, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1366X768) */
94/* 16:9 panel mode; 1920x1080 (HDTV 1920x1080p) */
95{ { 148500, 1920, 2008, 2052, 2200, 1080, 1084, 1089, 1125, T_POSITIVE_SYNC}, B_CMAP8, 1920, 1080, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1080) */
96};
97
98
99// transform official mode to internal, multi-screen mode enhanced mode
100static void Haiku_DetectTranslateMultiMode(display_mode *mode)
101{
102	mode->flags &= ~DUALHEAD_BITS;
103
104	if( mode->virtual_width == 2 * mode->timing.h_display ) {
105		LOG(4, ("Haiku: horizontal combine mode\n"));
106		if (si->Haiku_switch_head)
107			mode->flags |= DUALHEAD_SWITCH;
108		else
109			mode->flags |= DUALHEAD_ON;
110	} else if( mode->virtual_height == 2 * mode->timing.v_display ) {
111		LOG(4, ("Haiku: vertical combine mode not supported\n"));
112	} else {
113		/* unfortunately Haiku's screenprefs panel does not support single head output explicitly, so we'd better
114		   activate both heads always to (probably) mimic Radeon driver behaviour (for which this app was adapted) */
115		/* note please:
116		   - this will have a big downside on hardware for which both heads have different resolution cababilities (i.e. Matrox);
117		   - also (on laptops) this will shorten battery life a bit of course.. */
118		mode->flags |= DUALHEAD_CLONE;
119	}
120}
121
122
123// check and execute tunnel settings command
124static status_t Haiku_CheckMultiMonTunnel(display_mode *mode, const display_mode *low, const display_mode *high, bool *isTunneled )
125{
126	if( (mode->timing.flags & RADEON_MODE_MULTIMON_REQUEST) != 0 &&
127		(mode->timing.flags & RADEON_MODE_MULTIMON_REPLY) == 0 )
128	{
129		mode->timing.flags &= ~RADEON_MODE_MULTIMON_REQUEST;
130		mode->timing.flags |= RADEON_MODE_MULTIMON_REPLY;
131
132		// still process request, just in case someone set this flag
133		// combination by mistake
134
135		*isTunneled = true;
136		return B_OK;
137	}
138
139	// check magic params
140	if( mode->space != 0 || low->space != 0 || high->space != 0
141		|| low->virtual_width != 0xffff || low->virtual_height != 0xffff
142		|| high->virtual_width != 0 || high->virtual_height != 0
143		|| mode->timing.pixel_clock != 0
144		|| low->timing.pixel_clock != 'TKTK' || high->timing.pixel_clock != 'KTKT' )
145	{
146		*isTunneled = false;
147		return B_OK;
148	}
149
150	*isTunneled = true;
151
152	/* enable Haiku special handling */
153	if (!si->haiku_prefs_used)
154		LOG(4, ("PROPOSEMODE: Haiku screenprefs tunnel detected.\n"));
155	si->haiku_prefs_used = true;
156
157	/* note please:
158	   Haiku ScreenPrefs does not issue a SetMode command after changing these settings (per se), but relies on
159	   driver switching outputs directly. These settings are not dependant on workspace there, and are not part of
160	   the mode in the Radeon driver. In the Matrox and nVidia drivers they are though. So we need SetMode
161	   to be issued. (yes: sometimes I switch monitors when I switch workspace.. ;-)
162	   Also the RADEON driver saves these settings to a file so it remembers these after reboots. We don't atm.
163	   If the mode as proposed by the driver would be saved by the ScreenPrefs panel, we would 'remember' it over
164	   reboots though (via app_server settings).. */
165
166	switch( mode->h_display_start ) {
167	case ms_swap:
168		switch( mode->v_display_start ) {
169		case 0:
170			if (si->Haiku_switch_head)
171				mode->timing.flags = 1;
172			else
173				mode->timing.flags = 0;
174			LOG(4, ("Haiku: tunnel access target=swap, command=get, value=%u\n", mode->timing.flags));
175			return B_OK;
176		case 1:
177			si->Haiku_switch_head = mode->timing.flags != 0;
178			LOG(4, ("Haiku: tunnel access target=swap, command=set, value=%u\n", mode->timing.flags));
179			/* Haiku's screenprefs panel expects us to directly set the mode.. (but it should do that itself) */
180			SET_DISPLAY_MODE(&si->dm);
181			return B_OK;
182		}
183		break;
184
185	case ms_use_laptop_panel:
186		LOG(4, ("Haiku: tunnel access target=usepanel, command=%s, value=%u\n",
187			(mode->v_display_start == 1) ? "set" : "get", mode->timing.flags));
188		// we refuse this setting (makes no sense for us: laptop panel is treated as normal screen)
189		return B_ERROR;
190		break;
191
192	case ms_tv_standard:
193		LOG(4, ("Haiku: tunnel access target=tvstandard, command=%s, value=%u\n",
194			(mode->v_display_start == 1) ? "set" : "get", mode->timing.flags));
195		// let's forget about this for now..
196		return B_ERROR;
197		break;
198	}
199
200	LOG(4, ("Haiku: unhandled tunnel access target=$%x, command=%u, value=%u\n",
201		mode->h_display_start, mode->v_display_start, mode->timing.flags));
202
203	return B_ERROR;
204}
205
206
207/*!
208	Check mode is between low and high limits.
209	Returns:
210	B_OK - found one
211	B_BAD_VALUE - mode can be made, but outside limits
212	B_ERROR - not possible
213*/
214/* BOUNDS WARNING:
215 * BeOS (tested R5.0.3PE) is failing BWindowScreen.SetFrameBuffer() if PROPOSEMODE
216 * returns B_BAD_VALUE. It's called by the OS with target, low and high set to
217 * have the same settings for BWindowScreen!
218 * Which means we should not return B_BAD_VALUE on anything except for deviations on:
219 * display_mode.virtual_width;
220 * display_mode.virtual_height;
221 * display_mode.timing.h_display;
222 * display_mode.timing.v_display;
223 */
224/* Note:
225 * The target mode should be modified to correspond to the mode as it can be made. */
226status_t
227PROPOSE_DISPLAY_MODE(display_mode *target, const display_mode *low, const display_mode *high)
228{
229	status_t status = B_OK;
230	float pix_clock_found, target_aspect;
231	uint8 m,n,p, bpp;
232	status_t result;
233	uint32 max_vclk, row_bytes, mem_reservation;
234	bool acc_mode;
235	double target_refresh = ((double)target->timing.pixel_clock * 1000.0)
236		/ ((double)target->timing.h_total * (double)target->timing.v_total);
237	bool want_same_width = target->timing.h_display == target->virtual_width;
238	bool want_same_height = target->timing.v_display == target->virtual_height;
239	bool isTunneled = false;
240	// check whether we got a tunneled settings command
241	result = Haiku_CheckMultiMonTunnel(target, low, high, &isTunneled);
242	if (isTunneled)
243		return result;
244
245	/* since we (might be) called by the Haiku ScreenPrefs panel, check for special modes and translate
246	   them from (for us) non-native modes to native modes (as used in DualheadSetup by Mark Watson).
247	   These 'native modes' survive system reboots by the way, at least when set using DualheadSetup. */
248
249	/* note please: apparantly Haiku Screenprefs does not save the modes as set by the driver, but as originally requested
250	   by the ScreenPrefs panel. Modifications done by the driver are therefore not saved.
251	   It would be nice if the screenprefs panel in Haiku would save the modified modeflags (after proposemode or setmode),
252	   that would also make the driver remember switched heads (now it's a temporary setting). */
253
254	if (si->haiku_prefs_used) {
255		// check how many heads are needed by target mode
256		Haiku_DetectTranslateMultiMode(target);
257	}
258
259	LOG(1, ("PROPOSEMODE: (ENTER) requested virtual_width %d, virtual_height %d\n",
260		target->virtual_width, target->virtual_height));
261
262	/*find a nearby valid timing from that given*/
263	result = head1_validate_timing(&target->timing.h_display,
264		&target->timing.h_sync_start, &target->timing.h_sync_end,
265		&target->timing.h_total, &target->timing.v_display,
266		&target->timing.v_sync_start, &target->timing.v_sync_end,
267		&target->timing.v_total);
268	if (result == B_ERROR) {
269		LOG(4, ("PROPOSEMODE: could not validate timing, aborted.\n"));
270		return result;
271	}
272
273	/* disable aspect checks for a requested TVout mode when mode is TVout capable */
274	if (!si->ps.tvout
275		|| !(BT_check_tvmode(*target) && (target->flags & TV_BITS))) {
276		/* check if all connected output devices can display the requested mode's aspect.
277		 * assuming 16:10 screens can display non-WS modes, but cannot (correctly) display 16:9 modes;
278		 * assuming  16:9 screens can display non-WS modes, and can display 16:10 modes. */
279		/* calculate display mode aspect */
280		target_aspect = (target->timing.h_display / ((float)target->timing.v_display));
281		/* NOTE:
282		 * allow 0.10 difference so 5:4 aspect panels will be able to use 4:3 aspect modes! */
283		switch (si->ps.monitors) {
284			case 0: /* no monitor found at all */
285				/* if forcing widescreen type was requested don't block mode */
286				if (target_aspect > 1.34 && !si->settings.force_ws) {
287					LOG(4, ("PROPOSEMODE: not all output devices can display widescreen modes, aborted.\n"));
288					return B_ERROR;
289				}
290				break;
291			case CRTC1_TMDS:	/* digital panel on head 1, nothing on head 2 */
292			case CRTC1_VGA:		/* analog connected screen on head 1, nothing on head 2 */
293				if (si->ps.crtc1_screen.aspect < (target_aspect - 0.10)) {
294					LOG(4, ("PROPOSEMODE: screen at crtc1 is not widescreen (enough) type, aborted.\n"));
295					return B_ERROR;
296				}
297				break;
298			case CRTC2_TMDS:	/* nothing on head 1, digital panel on head 2 */
299			case CRTC2_VGA:		/* analog connected screen on head 2, nothing on head 1 */
300				if (si->ps.crtc2_screen.aspect < (target_aspect - 0.10)) {
301					LOG(4, ("PROPOSEMODE: screen at crtc2 is not widescreen (enough) type, aborted.\n"));
302					return B_ERROR;
303				}
304				break;
305			case CRTC1_TMDS | CRTC2_TMDS:	/* digital panels on both heads */
306			case CRTC1_VGA | CRTC2_VGA:		/* analog connected screens on both heads */
307			case CRTC1_TMDS | CRTC2_VGA:	/* digital panel on head 1, analog connected screen on head 2 */
308			case CRTC1_VGA | CRTC2_TMDS:	/* analog connected screen on head 1, digital panel on head 2 */
309			default:						/* more than two screens connected (illegal setup) */
310				if ((si->ps.crtc1_screen.aspect < (target_aspect - 0.10)) ||
311					(si->ps.crtc2_screen.aspect < (target_aspect - 0.10))) {
312					LOG(4, ("PROPOSEMODE: not all connected screens are widescreen (enough) type, aborted.\n"));
313					return B_ERROR;
314				}
315				break;
316		}
317	}
318
319	/* only limit modelist if user did not explicitly block this via nv.settings
320	   (because of errors in monitor's EDID information returned) */
321	if (si->settings.check_edid) {
322		/* check if screen(s) can display the requested resolution (if we have it's EDID info)
323		   note:
324		   allowing 2 pixels more for horizontal display for the 1366 mode, since multiples of 8
325		   are required for the CRTCs horizontal timing programming) */
326		if (si->ps.crtc1_screen.have_native_edid) {
327			if ((target->timing.h_display - 2) > si->ps.crtc1_screen.timing.h_display
328				|| target->timing.v_display > si->ps.crtc1_screen.timing.v_display) {
329				LOG(4, ("PROPOSEMODE: screen at crtc1 can't display requested resolution, aborted.\n"));
330				return B_ERROR;
331			}
332		}
333		if (si->ps.crtc2_screen.have_native_edid) {
334			if ((target->timing.h_display - 2) > si->ps.crtc2_screen.timing.h_display
335				|| target->timing.v_display > si->ps.crtc2_screen.timing.v_display) {
336				LOG(4, ("PROPOSEMODE: screen at crtc2 can't display requested resolution, aborted.\n"));
337				return B_ERROR;
338			}
339		}
340	}
341
342	/* validate display vs. virtual */
343	if (target->timing.h_display > target->virtual_width || want_same_width)
344		target->virtual_width = target->timing.h_display;
345	if (target->timing.v_display > target->virtual_height || want_same_height)
346		target->virtual_height = target->timing.v_display;
347
348	/* nail virtual size and 'subsequently' calculate rowbytes */
349	result = nv_general_validate_pic_size(target, &row_bytes, &acc_mode);
350	if (result == B_ERROR) {
351		LOG(4, ("PROPOSEMODE: could not validate virtual picture size, aborted.\n"));
352		return result;
353	}
354
355	/* check if virtual_width is still within the requested limits */
356	if (target->virtual_width < low->virtual_width
357		|| target->virtual_width > high->virtual_width) {
358		status = B_BAD_VALUE;
359		LOG(4, ("PROPOSEMODE: WARNING: virtual_width deviates too much\n"));
360	}
361
362	/* check if timing found is within the requested horizontal limits */
363	if (target->timing.h_display < low->timing.h_display
364		|| target->timing.h_display > high->timing.h_display
365		|| target->timing.h_sync_start < low->timing.h_sync_start
366		|| target->timing.h_sync_start > high->timing.h_sync_start
367		|| target->timing.h_sync_end < low->timing.h_sync_end
368		|| target->timing.h_sync_end > high->timing.h_sync_end
369		|| target->timing.h_total < low->timing.h_total
370		|| target->timing.h_total > high->timing.h_total) {
371		/* BWindowScreen workaround: we accept everything except h_display deviations */
372		if (target->timing.h_display < low->timing.h_display
373			|| target->timing.h_display > high->timing.h_display)
374			status = B_BAD_VALUE;
375
376		LOG(4, ("PROPOSEMODE: WARNING: horizontal timing deviates too much\n"));
377	}
378
379	/* check if timing found is within the requested vertical limits */
380	if (target->timing.v_display < low->timing.v_display
381		|| target->timing.v_display > high->timing.v_display
382		|| target->timing.v_sync_start < low->timing.v_sync_start
383		|| target->timing.v_sync_start > high->timing.v_sync_start
384		|| target->timing.v_sync_end < low->timing.v_sync_end
385		|| target->timing.v_sync_end > high->timing.v_sync_end
386		|| target->timing.v_total < low->timing.v_total
387		|| target->timing.v_total > high->timing.v_total) {
388		/* BWindowScreen workaround: we accept everything except v_display deviations */
389		if (target->timing.v_display < low->timing.v_display
390			|| target->timing.v_display > high->timing.v_display)
391			status = B_BAD_VALUE;
392
393		LOG(4, ("PROPOSEMODE: WARNING: vertical timing deviates too much\n"));
394	}
395
396	/* adjust pixelclock for possible timing modifications done above */
397	target->timing.pixel_clock = target_refresh * ((double)target->timing.h_total)
398		* ((double)target->timing.v_total) / 1000.0;
399
400	/* Now find the nearest valid pixelclock we actually can setup for the target mode,
401	 * this also makes sure we don't generate more pixel bandwidth than the device can handle */
402	/* calculate settings, but do not actually test anything (that costs too much time!) */
403	result = head1_pix_pll_find(*target, &pix_clock_found, &m, &n, &p, 0);
404	/* update the target mode */
405	target->timing.pixel_clock = pix_clock_found * 1000;
406
407	/* note if we fell outside the limits */
408	if (target->timing.pixel_clock < low->timing.pixel_clock
409		|| target->timing.pixel_clock > high->timing.pixel_clock) {
410		/* BWindowScreen workaround: we accept deviations <= 1Mhz */
411		if (target->timing.pixel_clock < low->timing.pixel_clock - 1000
412			|| target->timing.pixel_clock > high->timing.pixel_clock + 1000)
413			status = B_BAD_VALUE;
414
415		LOG(4, ("PROPOSEMODE: WARNING: pixelclock deviates too much\n"));
416	}
417
418	mem_reservation = 0;
419	/* checkout space needed for hardcursor (if any) */
420	if (si->settings.hardcursor)
421		mem_reservation = 2048;
422
423	/* Reserve extra space as a workaround for certain bugs (see DriverInterface.h
424	 * for an explanation). */
425	if (si->ps.card_arch < NV40A)
426		mem_reservation += PRE_NV40_OFFSET;
427	else
428		mem_reservation += NV40_PLUS_OFFSET;
429
430	/* memory requirement for frame buffer */
431	if (row_bytes * target->virtual_height > si->ps.memory_size - mem_reservation) {
432		target->virtual_height = (si->ps.memory_size - mem_reservation) / row_bytes;
433	}
434	if (target->virtual_height < target->timing.v_display) {
435		LOG(4,("PROPOSEMODE: not enough memory for current mode, aborted.\n"));
436		return B_ERROR;
437	}
438
439	LOG(4,("PROPOSEMODE: validated virtual_width %d, virtual_height %d pixels\n",
440		target->virtual_width, target->virtual_height));
441
442	if (target->virtual_height < low->virtual_height
443		|| target->virtual_height > high->virtual_height) {
444		status = B_BAD_VALUE;
445		LOG(4, ("PROPOSEMODE: WARNING: virtual_height deviates too much\n"));
446	}
447
448	/* setup status flags */
449	LOG(1, ("PROPOSEMODE: initial modeflags: $%08x\n", target->flags));
450	/* preset to singlehead card without TVout, no overlay support and no hardcursor.
451	 * also advice system that app_server and acc engine may touch the framebuffer
452	 * simultaneously (fixed). */
453	target->flags &=
454		~(DUALHEAD_CAPABLE | TV_CAPABLE | B_SUPPORTS_OVERLAYS | B_HARDWARE_CURSOR | B_IO_FB_NA);
455	/* we always allow parallel access (fixed), the DAC is always in 'enhanced'
456	 * mode (fixed), and all modes support DPMS (fixed);
457	 * We support scrolling and panning in every mode, so we 'send a signal' to
458	 * BWindowScreen.CanControlFrameBuffer() by setting B_SCROLL.  */
459	/* BTW: B_PARALLEL_ACCESS in combination with a hardcursor enables
460	 * BDirectWindow windowed modes. */
461	target->flags |= (B_PARALLEL_ACCESS | B_8_BIT_DAC | B_DPMS | B_SCROLL);
462
463	/* determine the 'would be' max. pixelclock for the second DAC for the current videomode if dualhead were activated */
464	switch (target->space) {
465		case B_CMAP8:
466			max_vclk = si->ps.max_dac2_clock_8;
467			bpp = 1;
468			break;
469		case B_RGB15_LITTLE:
470		case B_RGB16_LITTLE:
471			max_vclk = si->ps.max_dac2_clock_16;
472			bpp = 2;
473			break;
474		case B_RGB24_LITTLE:
475			max_vclk = si->ps.max_dac2_clock_24;
476			bpp = 3;
477			break;
478		case B_RGB32_LITTLE:
479			max_vclk = si->ps.max_dac2_clock_32dh;
480			bpp = 4;
481			break;
482		default:
483			/* use fail-safe value */
484			max_vclk = si->ps.max_dac2_clock_32dh;
485			bpp = 4;
486			break;
487	}
488
489	/* set DUALHEAD_CAPABLE if suitable */
490	//fixme: update for independant secondary head use! (reserve fixed memory then)
491	if (si->ps.secondary_head && target->timing.pixel_clock <= (max_vclk * 1000)) {
492		switch (target->flags & DUALHEAD_BITS) {
493			case DUALHEAD_ON:
494			case DUALHEAD_SWITCH:
495				if (si->ps.memory_size - mem_reservation
496						>= row_bytes * target->virtual_height
497					&& (uint16)(row_bytes / bpp) >= target->timing.h_display * 2)
498					target->flags |= DUALHEAD_CAPABLE;
499				break;
500			case DUALHEAD_CLONE:
501				if (si->ps.memory_size - mem_reservation
502						>= row_bytes * target->virtual_height)
503					target->flags |= DUALHEAD_CAPABLE;
504				break;
505			case DUALHEAD_OFF:
506				if (si->ps.memory_size - mem_reservation
507						>= row_bytes * target->virtual_height * 2)
508					target->flags |= DUALHEAD_CAPABLE;
509				break;
510		}
511	}
512
513	/* if not dualhead capable card clear dualhead flags */
514	if (!(target->flags & DUALHEAD_CAPABLE))
515		target->flags &= ~DUALHEAD_BITS;
516
517	/* set TV_CAPABLE if suitable: pixelclock is not important (defined by TVstandard) */
518	if (si->ps.tvout && BT_check_tvmode(*target))
519		target->flags |= TV_CAPABLE;
520
521	/* if not TVout capable card clear TVout flags */
522	if (!(target->flags & TV_CAPABLE))
523		target->flags &= ~TV_BITS;
524
525	/* make sure TV head assignment is sane */
526	if (target->flags & TV_BITS) {
527		if (!si->ps.secondary_head)
528			target->flags |= TV_PRIMARY;
529		else if ((target->flags & DUALHEAD_BITS) == DUALHEAD_OFF)
530			target->flags |= TV_PRIMARY;
531	} else
532		target->flags &= ~TV_PRIMARY;
533
534	/* set HARDWARE_CURSOR mode if suitable */
535	if (si->settings.hardcursor)
536		target->flags |= B_HARDWARE_CURSOR;
537
538	/* set SUPPORTS_OVERLAYS if suitable */
539	if (si->ps.card_type <= NV40 || si->ps.card_type == NV45)
540		target->flags |= B_SUPPORTS_OVERLAYS;
541
542	LOG(1, ("PROPOSEMODE: validated modeflags: $%08x\n", target->flags));
543
544	/* overrule timing command flags to be (fixed) blank_pedestal = 0.0IRE,
545	 * progressive scan (fixed), and sync_on_green not avaible. */
546	target->timing.flags &= ~(B_BLANK_PEDESTAL | B_TIMING_INTERLACED | B_SYNC_ON_GREEN);
547	/* The HSYNC and VSYNC command flags are actually executed by the driver. */
548
549	if (status == B_OK)
550		LOG(4, ("PROPOSEMODE: completed successfully.\n"));
551	else
552		LOG(4, ("PROPOSEMODE: mode can be made, but outside given limits.\n"));
553	return status;
554}
555
556
557/*!
558	Return the number of modes this device will return from GET_MODE_LIST().
559	This is precalculated in create_mode_list (called from InitAccelerant stuff)
560*/
561uint32
562ACCELERANT_MODE_COUNT(void)
563{
564	LOG(1, ("ACCELERANT_MODE_COUNT: the modelist contains %d modes\n",si->mode_count));
565	return si->mode_count;
566}
567
568
569/*! Copy the list of guaranteed supported video modes to the location provided.
570*/
571status_t
572GET_MODE_LIST(display_mode *dm)
573{
574	LOG(1, ("GET_MODE_LIST: exporting the modelist created before.\n"));
575
576	memcpy(dm, my_mode_list, si->mode_count * sizeof(display_mode));
577	return B_OK;
578}
579
580
581static void checkAndAddMode(const display_mode *src, display_mode *dst)
582{
583	uint32 j, pix_clk_range;
584	display_mode low, high;
585	color_space spaces[4] = {B_RGB32_LITTLE, B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
586
587	/* set ranges for acceptable values */
588	low = high = *src;
589	/* range is 6.25% of default clock: arbitrarily picked */
590	pix_clk_range = low.timing.pixel_clock >> 5;
591	low.timing.pixel_clock -= pix_clk_range;
592	high.timing.pixel_clock += pix_clk_range;
593	/* 'some cards need wider virtual widths for certain modes':
594	 * Not true. They might need a wider pitch, but this is _not_ reflected in
595	 * virtual_width, but in fbc.bytes_per_row. */
596	//So disable next line:
597	//high.virtual_width = 4096;
598	/* do it once for each depth we want to support */
599	for (j = 0; j < (sizeof(spaces) / sizeof(color_space)); j++) {
600		/* set target values */
601		dst[si->mode_count] = *src;
602		/* poke the specific space */
603		dst[si->mode_count].space = low.space = high.space = spaces[j];
604		/* ask for a compatible mode */
605		/* We have to check for B_OK, because otherwise the pix_clk_range
606		 * won't be taken into account!! */
607		//So don't do this:
608		//if (PROPOSE_DISPLAY_MODE(dst, &low, &high) != B_ERROR) {
609		//Instead, do this:
610		if (PROPOSE_DISPLAY_MODE(&dst[si->mode_count], &low, &high) == B_OK) {
611			/* count it, so move on to next mode */
612			si->mode_count++;
613		}
614	}
615}
616
617
618/*! Create a list of display_modes to pass back to the caller.
619*/
620status_t
621create_mode_list(void)
622{
623	size_t max_size;
624	uint32 i;
625	const display_mode *src;
626	display_mode *dst;
627	display_mode custom_mode;
628
629	/* figure out how big the list could be (4 colorspaces, 3 virtual types per mode), and adjust up to nearest multiple of B_PAGE_SIZE */
630	max_size = (((MODE_COUNT * 4 * 3) * sizeof(display_mode)) + (B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1);
631
632	/* create an area to hold the info */
633	si->mode_area = my_mode_list_area = create_area("NV accelerant mode info",
634		(void **)&my_mode_list, B_ANY_ADDRESS, max_size, B_NO_LOCK,
635		B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
636	if (my_mode_list_area < B_OK)
637		return my_mode_list_area;
638
639	/* walk through our predefined list and see which modes fit this device */
640	src = mode_list;
641	dst = my_mode_list;
642	si->mode_count = 0;
643	for (i = 0; i < MODE_COUNT; i++) {
644		// standard mode
645		/* unfortunately Haiku's screenprefs panel does not support single head output explicitly, so we'd better
646		   activate both heads always to (probably) mimic Radeon driver behaviour (for which this app was adapted) */
647		/* note please:
648		   - this will have a big downside on hardware for which both heads have different resolution cababilities (i.e. Matrox);
649		   - also (on laptops) this will shorten battery life a bit of course.. */
650		custom_mode = *src;
651		custom_mode.flags |= DUALHEAD_CLONE;
652		checkAndAddMode(&custom_mode, dst);
653		// double width mode for Haiku ScreenPrefs panel
654		/* note please: These modes should not be added. Instead the mode.flags should be used during setting screen as these will
655		   automatically generate the needed other properties of the mode. Besides, virtual size is meant to be used for
656		   pan&scan modes (viewports), i.e. for certain games.
657		   The currently used method will fail programs that are meant to work pan@scan if the virtual width (or height) are
658		   exactly twice the view area. */
659		custom_mode = *src;
660		custom_mode.virtual_width *= 2;
661		custom_mode.flags |= DUALHEAD_ON;
662		checkAndAddMode(&custom_mode, dst);
663		// we don't support double height modes
664		/* advance to next mode */
665		src++;
666	}
667
668	return B_OK;
669}
670