1/*
2	Copyright (c) 2002-2004, Thomas Kurschel
3
4
5	Part of Radeon accelerant
6
7	Multi-monitor management
8*/
9
10#include "radeon_accelerant.h"
11#include "generic.h"
12#include "GlobalData.h"
13
14
15// transform official mode to internal, multi-screen mode enhanced mode
16void Radeon_DetectMultiMode( virtual_card *vc, display_mode *mode )
17{
18	(void)vc;
19
20	mode->timing.flags &= ~RADEON_MODE_MASK;
21
22	// combine mode is used if virtual area is twice as visible area
23	// and if scrolling is enabled; if combining is impossible, use
24	// cloning instead
25	if( (mode->flags & B_SCROLL) == 0 )
26		return;
27
28	SHOW_FLOW0( 3, "possibly combine mode" );
29
30	// remove scroll flag - we don't need it anymore
31	mode->flags &= ~B_SCROLL;
32
33	mode->timing.flags &= ~RADEON_MODE_POSITION_MASK;
34
35	if( mode->virtual_width == 2 * mode->timing.h_display ) {
36		SHOW_FLOW0( 2, "horizontal combine mode" );
37		mode->timing.flags |= RADEON_MODE_POSITION_HORIZONTAL;
38		mode->timing.flags &= ~RADEON_MODE_MASK;
39		mode->timing.flags |= RADEON_MODE_COMBINE;
40	} else if( mode->virtual_height == 2 * mode->timing.v_display ) {
41		SHOW_FLOW0( 2, "vertical combine mode" );
42		mode->timing.flags |= RADEON_MODE_POSITION_VERTICAL;
43		mode->timing.flags &= ~RADEON_MODE_MASK;
44		mode->timing.flags |= RADEON_MODE_COMBINE;
45	} else {
46		// ups, this isn't really a combine mode - restore flags
47		SHOW_FLOW0( 2, "wasn't really a combine mode" );
48		mode->timing.flags &= ~RADEON_MODE_MASK;
49		mode->flags |= B_SCROLL;
50	}
51}
52
53// make sure selected multi-screen mode is valid; adapt it if needed
54void Radeon_VerifyMultiMode( virtual_card *vc, shared_info *si, display_mode *mode )
55{
56	// if there is no second port or no second monitor connected,
57	// fall back to standard mode
58	int num_usable_crtcs = vc->assigned_crtc[0] && si->crtc[0].chosen_displays != dd_none;
59
60	if( si->num_crtc > 1 )
61		num_usable_crtcs += vc->assigned_crtc[1] && si->crtc[1].chosen_displays != dd_none;
62
63	if( num_usable_crtcs < 2 ) {
64		SHOW_FLOW0( 2, "only one monitor - disabling any multi-mon mode" );
65		// restore flags if combine mode is selected
66		if( (mode->timing.flags & RADEON_MODE_MASK) == RADEON_MODE_COMBINE )
67			mode->flags |= B_SCROLL;
68
69		mode->timing.flags &= ~RADEON_MODE_MASK;
70		mode->timing.flags |= RADEON_MODE_STANDARD;
71	}
72}
73
74// transform internal, multi-screen enabled display mode
75// to official mode
76void Radeon_HideMultiMode( virtual_card *vc, display_mode *mode )
77{
78	(void) vc;
79
80	// restore flags for combine mode
81	if( (mode->timing.flags & RADEON_MODE_MASK) == RADEON_MODE_COMBINE )
82		mode->flags |= B_SCROLL;
83}
84
85
86// initialize multi-screen mode dependant variables
87void Radeon_InitMultiModeVars(
88	accelerator_info *ai, display_mode *mode )
89{
90	virtual_card *vc = ai->vc;
91	shared_info *si = ai->si;
92	uint32 x, y;
93
94	// setup single-screen mode
95	vc->eff_width = mode->timing.h_display;
96	vc->eff_height = mode->timing.v_display;
97
98	if( vc->used_crtc[0] ) {
99		si->crtc[0].rel_x = 0;
100		si->crtc[0].rel_y = 0;
101	}
102
103	if( vc->used_crtc[1] ) {
104		si->crtc[1].rel_x = 0;
105		si->crtc[1].rel_y = 0;
106	}
107
108	switch( mode->timing.flags & RADEON_MODE_MASK ) {
109	case RADEON_MODE_COMBINE:
110		// detect where second screen must be located and
111		// adapt total visible area accordingly
112		if( (mode->timing.flags & RADEON_MODE_POSITION_MASK) == RADEON_MODE_POSITION_HORIZONTAL ) {
113			vc->eff_width = 2 * mode->timing.h_display;
114			x = mode->timing.h_display;
115			y = 0;
116		} else {
117			vc->eff_height = 2 * mode->timing.v_display;
118			x = 0;
119			y = mode->timing.v_display;
120		}
121
122		SHOW_FLOW( 3, "relative position of second screen: %d, %d", x, y );
123
124		// set relative offset
125		if( !vc->swap_displays ) {
126			si->crtc[1].rel_x = x;
127			si->crtc[1].rel_y = y;
128		} else {
129			si->crtc[0].rel_x = x;
130			si->crtc[0].rel_y = y;
131		}
132		break;
133
134	default:
135		// else, ports are independant but show the same
136		break;
137	}
138}
139
140
141// mapping of internal TV standard code to public TV standard code
142static const uint32 private2be[] = {
143	0, 1, 3, 4, 103, 3/* PAL SCART - no public id, so I use PAL BDGHI */, 102 };
144
145// check and execute tunnel settings command
146status_t Radeon_CheckMultiMonTunnel( virtual_card *vc, display_mode *mode,
147	const display_mode *low, const display_mode *high, bool *isTunneled )
148{
149	if( (mode->timing.flags & RADEON_MODE_MULTIMON_REQUEST) != 0 &&
150		(mode->timing.flags & RADEON_MODE_MULTIMON_REPLY) == 0 )
151	{
152		mode->timing.flags &= ~RADEON_MODE_MULTIMON_REQUEST;
153		mode->timing.flags |= RADEON_MODE_MULTIMON_REPLY;
154
155		// still process request, just in case someone set this flag
156		// combination by mistake
157
158		// TBD: disabled to shorten syslog
159		*isTunneled = true;
160		return B_OK;
161	}
162
163	// check magic params
164	if( mode->space != 0 || low->space != 0 || high->space != 0
165		|| low->virtual_width != 0xffff || low->virtual_height != 0xffff
166		|| high->virtual_width != 0 || high->virtual_height != 0
167		|| mode->timing.pixel_clock != 0
168		|| low->timing.pixel_clock != 'TKTK' || high->timing.pixel_clock != 'KTKT' )
169	{
170		*isTunneled = false;
171		return B_OK;
172	}
173
174	*isTunneled = true;
175
176	/*SHOW_FLOW( 1, "tunnel access code=%d, command=%d",
177		mode->h_display_start, mode->v_display_start );*/
178
179	switch( mode->h_display_start ) {
180	case ms_swap:
181		switch( mode->v_display_start ) {
182		case 0:
183			mode->timing.flags = vc->swap_displays;
184			return B_OK;
185
186		case 1:
187			vc->swap_displays = mode->timing.flags != 0;
188			vc->enforce_mode_change = true;
189			// write settings instantly
190			Radeon_WriteSettings( vc );
191			return B_OK;
192		}
193		break;
194
195	case ms_use_laptop_panel:
196		// we must refuse this setting if there is no laptop panel;
197		// else, the preferences dialog would show this (useless) option
198		if( (vc->connected_displays & dd_lvds) == 0 )
199			return B_ERROR;
200
201		switch( mode->v_display_start ) {
202		case 0:
203			mode->timing.flags = vc->use_laptop_panel;
204			//SHOW_FLOW( 1, "get use_laptop_panel settings (%d)", mode->timing.flags );
205			return B_OK;
206
207		case 1:
208			vc->use_laptop_panel = mode->timing.flags != 0;
209			//SHOW_FLOW( 1, "set use_laptop_panel settings (%d)", vc->use_laptop_panel );
210			vc->enforce_mode_change = true;
211			Radeon_WriteSettings( vc );
212			return B_OK;
213		}
214		break;
215
216	case ms_tv_standard:
217		switch( mode->v_display_start ) {
218		case 0:
219			mode->timing.flags = private2be[vc->tv_standard];
220			/*SHOW_FLOW( 1, "read tv_standard (internal %d, public %d)",
221				vc->tv_standard, mode->timing.flags );*/
222			return B_OK;
223
224		case 1:
225			switch( mode->timing.flags ) {
226			case 0: vc->tv_standard = ts_off; break;
227			case 1:	vc->tv_standard = ts_ntsc; break;
228			case 2: break; // ntsc j
229			case 3: vc->tv_standard = ts_pal_bdghi; break;
230			case 4: vc->tv_standard = ts_pal_m; break;
231			case 5: break; // pal n
232			case 6: break; // secam - I reckon not supported by hardware
233			case 101: break; // ntsc 443
234			case 102: vc->tv_standard = ts_pal_60; break;
235			case 103: vc->tv_standard = ts_pal_nc; break;
236			}
237
238			SHOW_FLOW( 1, "set tv_standard (internal %d, public %d)",
239				vc->tv_standard, mode->timing.flags );
240
241			vc->enforce_mode_change = true;
242			Radeon_WriteSettings( vc );
243			return B_OK;
244
245		case 2: {
246			uint32 idx = mode->timing.flags;
247
248			// we limit it explicetely to NTSC and PAL as all other
249			// modes are not fully implemented
250			if( idx < sizeof( private2be ) / sizeof( private2be[0] ) &&
251				idx < 3 ) {
252				mode->timing.flags = private2be[idx];
253				return B_OK;
254			} else
255				return B_ERROR;
256			}
257		}
258	}
259
260	return B_ERROR;
261}
262
263
264// return true if both ports must be programmed
265bool Radeon_NeedsSecondPort( display_mode *mode )
266{
267	switch( mode->timing.flags & RADEON_MODE_MASK ) {
268	case RADEON_MODE_COMBINE:
269		return true;
270	default:
271		return false;
272	}
273}
274
275
276// return number of ports showing differents parts of frame buffer
277bool Radeon_DifferentPorts( display_mode *mode )
278{
279	switch( mode->timing.flags & RADEON_MODE_MASK ) {
280	case RADEON_MODE_COMBINE:
281		return 2;
282	default:
283		return 1;
284	}
285}
286