xref: /haiku/src/add-ons/accelerants/nvidia/ProposeDisplayMode.c (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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...
23 static 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
100 static 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
124 static 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. */
226 status_t
227 PROPOSE_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 */
561 uint32
562 ACCELERANT_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 */
571 status_t
572 GET_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 
581 static 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 */
620 status_t
621 create_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