1409f1731Sshadow/*
2e02e12deSAxel Dörfler	Copyright (c) 2002-2004, Thomas Kurschel
3409f1731Sshadow
4409f1731Sshadow
5409f1731Sshadow	Part of Radeon accelerant
6409f1731Sshadow
7409f1731Sshadow	Multi-monitor management
8409f1731Sshadow*/
9409f1731Sshadow
10409f1731Sshadow#include "radeon_accelerant.h"
11409f1731Sshadow#include "generic.h"
12409f1731Sshadow#include "GlobalData.h"
13409f1731Sshadow
14409f1731Sshadow
15409f1731Sshadow// transform official mode to internal, multi-screen mode enhanced mode
16409f1731Sshadowvoid Radeon_DetectMultiMode( virtual_card *vc, display_mode *mode )
17409f1731Sshadow{
18e02e12deSAxel Dörfler	(void)vc;
19409f1731Sshadow
20e02e12deSAxel Dörfler	mode->timing.flags &= ~RADEON_MODE_MASK;
21409f1731Sshadow
22409f1731Sshadow	// combine mode is used if virtual area is twice as visible area
23409f1731Sshadow	// and if scrolling is enabled; if combining is impossible, use
24409f1731Sshadow	// cloning instead
25e02e12deSAxel Dörfler	if( (mode->flags & B_SCROLL) == 0 )
26409f1731Sshadow		return;
27409f1731Sshadow
28409f1731Sshadow	SHOW_FLOW0( 3, "possibly combine mode" );
29409f1731Sshadow
30409f1731Sshadow	// remove scroll flag - we don't need it anymore
31409f1731Sshadow	mode->flags &= ~B_SCROLL;
32409f1731Sshadow
33409f1731Sshadow	mode->timing.flags &= ~RADEON_MODE_POSITION_MASK;
34409f1731Sshadow
35409f1731Sshadow	if( mode->virtual_width == 2 * mode->timing.h_display ) {
36e02e12deSAxel Dörfler		SHOW_FLOW0( 2, "horizontal combine mode" );
37409f1731Sshadow		mode->timing.flags |= RADEON_MODE_POSITION_HORIZONTAL;
38409f1731Sshadow		mode->timing.flags &= ~RADEON_MODE_MASK;
39409f1731Sshadow		mode->timing.flags |= RADEON_MODE_COMBINE;
40409f1731Sshadow	} else if( mode->virtual_height == 2 * mode->timing.v_display ) {
41e02e12deSAxel Dörfler		SHOW_FLOW0( 2, "vertical combine mode" );
42409f1731Sshadow		mode->timing.flags |= RADEON_MODE_POSITION_VERTICAL;
43409f1731Sshadow		mode->timing.flags &= ~RADEON_MODE_MASK;
44409f1731Sshadow		mode->timing.flags |= RADEON_MODE_COMBINE;
45409f1731Sshadow	} else {
46409f1731Sshadow		// ups, this isn't really a combine mode - restore flags
47e02e12deSAxel Dörfler		SHOW_FLOW0( 2, "wasn't really a combine mode" );
48409f1731Sshadow		mode->timing.flags &= ~RADEON_MODE_MASK;
492a37e4c1Sshadow		mode->flags |= B_SCROLL;
50409f1731Sshadow	}
51409f1731Sshadow}
52409f1731Sshadow
53409f1731Sshadow// make sure selected multi-screen mode is valid; adapt it if needed
54409f1731Sshadowvoid Radeon_VerifyMultiMode( virtual_card *vc, shared_info *si, display_mode *mode )
55409f1731Sshadow{
56409f1731Sshadow	// if there is no second port or no second monitor connected,
57409f1731Sshadow	// fall back to standard mode
58e02e12deSAxel Dörfler	int num_usable_crtcs = vc->assigned_crtc[0] && si->crtc[0].chosen_displays != dd_none;
59e02e12deSAxel Dörfler
60e02e12deSAxel Dörfler	if( si->num_crtc > 1 )
61e02e12deSAxel Dörfler		num_usable_crtcs += vc->assigned_crtc[1] && si->crtc[1].chosen_displays != dd_none;
62e02e12deSAxel Dörfler
63e02e12deSAxel Dörfler	if( num_usable_crtcs < 2 ) {
64e02e12deSAxel Dörfler		SHOW_FLOW0( 2, "only one monitor - disabling any multi-mon mode" );
65409f1731Sshadow		// restore flags if combine mode is selected
66409f1731Sshadow		if( (mode->timing.flags & RADEON_MODE_MASK) == RADEON_MODE_COMBINE )
67409f1731Sshadow			mode->flags |= B_SCROLL;
68409f1731Sshadow
69409f1731Sshadow		mode->timing.flags &= ~RADEON_MODE_MASK;
70409f1731Sshadow		mode->timing.flags |= RADEON_MODE_STANDARD;
71409f1731Sshadow	}
72409f1731Sshadow}
73409f1731Sshadow
74409f1731Sshadow// transform internal, multi-screen enabled display mode
75409f1731Sshadow// to official mode
76409f1731Sshadowvoid Radeon_HideMultiMode( virtual_card *vc, display_mode *mode )
77409f1731Sshadow{
782a37e4c1Sshadow	(void) vc;
792a37e4c1Sshadow
80409f1731Sshadow	// restore flags for combine mode
81409f1731Sshadow	if( (mode->timing.flags & RADEON_MODE_MASK) == RADEON_MODE_COMBINE )
82409f1731Sshadow		mode->flags |= B_SCROLL;
83409f1731Sshadow}
84409f1731Sshadow
85409f1731Sshadow
86409f1731Sshadow// initialize multi-screen mode dependant variables
87e02e12deSAxel Dörflervoid Radeon_InitMultiModeVars(
88e02e12deSAxel Dörfler	accelerator_info *ai, display_mode *mode )
89409f1731Sshadow{
90e02e12deSAxel Dörfler	virtual_card *vc = ai->vc;
91e02e12deSAxel Dörfler	shared_info *si = ai->si;
92409f1731Sshadow	uint32 x, y;
93409f1731Sshadow
94409f1731Sshadow	// setup single-screen mode
95409f1731Sshadow	vc->eff_width = mode->timing.h_display;
96409f1731Sshadow	vc->eff_height = mode->timing.v_display;
97e02e12deSAxel Dörfler
98e02e12deSAxel Dörfler	if( vc->used_crtc[0] ) {
99e02e12deSAxel Dörfler		si->crtc[0].rel_x = 0;
100e02e12deSAxel Dörfler		si->crtc[0].rel_y = 0;
101e02e12deSAxel Dörfler	}
102e02e12deSAxel Dörfler
103e02e12deSAxel Dörfler	if( vc->used_crtc[1] ) {
104e02e12deSAxel Dörfler		si->crtc[1].rel_x = 0;
105e02e12deSAxel Dörfler		si->crtc[1].rel_y = 0;
106e02e12deSAxel Dörfler	}
107409f1731Sshadow
108409f1731Sshadow	switch( mode->timing.flags & RADEON_MODE_MASK ) {
109409f1731Sshadow	case RADEON_MODE_COMBINE:
110409f1731Sshadow		// detect where second screen must be located and
111409f1731Sshadow		// adapt total visible area accordingly
112409f1731Sshadow		if( (mode->timing.flags & RADEON_MODE_POSITION_MASK) == RADEON_MODE_POSITION_HORIZONTAL ) {
113409f1731Sshadow			vc->eff_width = 2 * mode->timing.h_display;
114409f1731Sshadow			x = mode->timing.h_display;
115409f1731Sshadow			y = 0;
116409f1731Sshadow		} else {
117409f1731Sshadow			vc->eff_height = 2 * mode->timing.v_display;
118409f1731Sshadow			x = 0;
119409f1731Sshadow			y = mode->timing.v_display;
120409f1731Sshadow		}
121409f1731Sshadow
122409f1731Sshadow		SHOW_FLOW( 3, "relative position of second screen: %d, %d", x, y );
123409f1731Sshadow
124409f1731Sshadow		// set relative offset
1252a37e4c1Sshadow		if( !vc->swap_displays ) {
126e02e12deSAxel Dörfler			si->crtc[1].rel_x = x;
127e02e12deSAxel Dörfler			si->crtc[1].rel_y = y;
128409f1731Sshadow		} else {
129e02e12deSAxel Dörfler			si->crtc[0].rel_x = x;
130e02e12deSAxel Dörfler			si->crtc[0].rel_y = y;
131409f1731Sshadow		}
132409f1731Sshadow		break;
133409f1731Sshadow
134e02e12deSAxel Dörfler	default:
135e02e12deSAxel Dörfler		// else, ports are independant but show the same
136409f1731Sshadow		break;
137409f1731Sshadow	}
138409f1731Sshadow}
139409f1731Sshadow
140409f1731Sshadow
141e02e12deSAxel Dörfler// mapping of internal TV standard code to public TV standard code
142e02e12deSAxel Dörflerstatic const uint32 private2be[] = {
143e02e12deSAxel Dörfler	0, 1, 3, 4, 103, 3/* PAL SCART - no public id, so I use PAL BDGHI */, 102 };
144e02e12deSAxel Dörfler
145409f1731Sshadow// check and execute tunnel settings command
146409f1731Sshadowstatus_t Radeon_CheckMultiMonTunnel( virtual_card *vc, display_mode *mode,
147409f1731Sshadow	const display_mode *low, const display_mode *high, bool *isTunneled )
148409f1731Sshadow{
149409f1731Sshadow	if( (mode->timing.flags & RADEON_MODE_MULTIMON_REQUEST) != 0 &&
150409f1731Sshadow		(mode->timing.flags & RADEON_MODE_MULTIMON_REPLY) == 0 )
151409f1731Sshadow	{
152409f1731Sshadow		mode->timing.flags &= ~RADEON_MODE_MULTIMON_REQUEST;
153409f1731Sshadow		mode->timing.flags |= RADEON_MODE_MULTIMON_REPLY;
154409f1731Sshadow
155409f1731Sshadow		// still process request, just in case someone set this flag
156409f1731Sshadow		// combination by mistake
157409f1731Sshadow
158409f1731Sshadow		// TBD: disabled to shorten syslog
159409f1731Sshadow		*isTunneled = true;
160409f1731Sshadow		return B_OK;
161409f1731Sshadow	}
162409f1731Sshadow
163409f1731Sshadow	// check magic params
164409f1731Sshadow	if( mode->space != 0 || low->space != 0 || high->space != 0
165409f1731Sshadow		|| low->virtual_width != 0xffff || low->virtual_height != 0xffff
166409f1731Sshadow		|| high->virtual_width != 0 || high->virtual_height != 0
167409f1731Sshadow		|| mode->timing.pixel_clock != 0
168409f1731Sshadow		|| low->timing.pixel_clock != 'TKTK' || high->timing.pixel_clock != 'KTKT' )
169409f1731Sshadow	{
170409f1731Sshadow		*isTunneled = false;
171409f1731Sshadow		return B_OK;
172409f1731Sshadow	}
173409f1731Sshadow
174409f1731Sshadow	*isTunneled = true;
175409f1731Sshadow
176e02e12deSAxel Dörfler	/*SHOW_FLOW( 1, "tunnel access code=%d, command=%d",
177e02e12deSAxel Dörfler		mode->h_display_start, mode->v_display_start );*/
178e02e12deSAxel Dörfler
179409f1731Sshadow	switch( mode->h_display_start ) {
180409f1731Sshadow	case ms_swap:
181e02e12deSAxel Dörfler		switch( mode->v_display_start ) {
182e02e12deSAxel Dörfler		case 0:
1832a37e4c1Sshadow			mode->timing.flags = vc->swap_displays;
184e02e12deSAxel Dörfler			return B_OK;
185409f1731Sshadow
186e02e12deSAxel Dörfler		case 1:
187e02e12deSAxel Dörfler			vc->swap_displays = mode->timing.flags != 0;
188e02e12deSAxel Dörfler			vc->enforce_mode_change = true;
189e02e12deSAxel Dörfler			// write settings instantly
190e02e12deSAxel Dörfler			Radeon_WriteSettings( vc );
191e02e12deSAxel Dörfler			return B_OK;
192e02e12deSAxel Dörfler		}
193e02e12deSAxel Dörfler		break;
194e02e12deSAxel Dörfler
195e02e12deSAxel Dörfler	case ms_use_laptop_panel:
196e02e12deSAxel Dörfler		// we must refuse this setting if there is no laptop panel;
197e02e12deSAxel Dörfler		// else, the preferences dialog would show this (useless) option
198e02e12deSAxel Dörfler		if( (vc->connected_displays & dd_lvds) == 0 )
199e02e12deSAxel Dörfler			return B_ERROR;
200e02e12deSAxel Dörfler
201e02e12deSAxel Dörfler		switch( mode->v_display_start ) {
202e02e12deSAxel Dörfler		case 0:
203e02e12deSAxel Dörfler			mode->timing.flags = vc->use_laptop_panel;
204e02e12deSAxel Dörfler			//SHOW_FLOW( 1, "get use_laptop_panel settings (%d)", mode->timing.flags );
205e02e12deSAxel Dörfler			return B_OK;
206e02e12deSAxel Dörfler
207e02e12deSAxel Dörfler		case 1:
208e02e12deSAxel Dörfler			vc->use_laptop_panel = mode->timing.flags != 0;
209e02e12deSAxel Dörfler			//SHOW_FLOW( 1, "set use_laptop_panel settings (%d)", vc->use_laptop_panel );
210e02e12deSAxel Dörfler			vc->enforce_mode_change = true;
211e02e12deSAxel Dörfler			Radeon_WriteSettings( vc );
212e02e12deSAxel Dörfler			return B_OK;
213e02e12deSAxel Dörfler		}
214e02e12deSAxel Dörfler		break;
215409f1731Sshadow
216e02e12deSAxel Dörfler	case ms_tv_standard:
217e02e12deSAxel Dörfler		switch( mode->v_display_start ) {
218e02e12deSAxel Dörfler		case 0:
219e02e12deSAxel Dörfler			mode->timing.flags = private2be[vc->tv_standard];
220e02e12deSAxel Dörfler			/*SHOW_FLOW( 1, "read tv_standard (internal %d, public %d)",
221e02e12deSAxel Dörfler				vc->tv_standard, mode->timing.flags );*/
222e02e12deSAxel Dörfler			return B_OK;
223409f1731Sshadow
224e02e12deSAxel Dörfler		case 1:
225e02e12deSAxel Dörfler			switch( mode->timing.flags ) {
226e02e12deSAxel Dörfler			case 0: vc->tv_standard = ts_off; break;
227e02e12deSAxel Dörfler			case 1:	vc->tv_standard = ts_ntsc; break;
228e02e12deSAxel Dörfler			case 2: break; // ntsc j
229e02e12deSAxel Dörfler			case 3: vc->tv_standard = ts_pal_bdghi; break;
230e02e12deSAxel Dörfler			case 4: vc->tv_standard = ts_pal_m; break;
231e02e12deSAxel Dörfler			case 5: break; // pal n
232e02e12deSAxel Dörfler			case 6: break; // secam - I reckon not supported by hardware
233e02e12deSAxel Dörfler			case 101: break; // ntsc 443
234e02e12deSAxel Dörfler			case 102: vc->tv_standard = ts_pal_60; break;
235e02e12deSAxel Dörfler			case 103: vc->tv_standard = ts_pal_nc; break;
236e02e12deSAxel Dörfler			}
237e02e12deSAxel Dörfler
238e02e12deSAxel Dörfler			SHOW_FLOW( 1, "set tv_standard (internal %d, public %d)",
239e02e12deSAxel Dörfler				vc->tv_standard, mode->timing.flags );
240409f1731Sshadow
241e02e12deSAxel Dörfler			vc->enforce_mode_change = true;
242e02e12deSAxel Dörfler			Radeon_WriteSettings( vc );
243e02e12deSAxel Dörfler			return B_OK;
244e02e12deSAxel Dörfler
245e02e12deSAxel Dörfler		case 2: {
246e02e12deSAxel Dörfler			uint32 idx = mode->timing.flags;
247e02e12deSAxel Dörfler
248e02e12deSAxel Dörfler			// we limit it explicetely to NTSC and PAL as all other
249e02e12deSAxel Dörfler			// modes are not fully implemented
250e02e12deSAxel Dörfler			if( idx < sizeof( private2be ) / sizeof( private2be[0] ) &&
251e02e12deSAxel Dörfler				idx < 3 ) {
252e02e12deSAxel Dörfler				mode->timing.flags = private2be[idx];
253e02e12deSAxel Dörfler				return B_OK;
254e02e12deSAxel Dörfler			} else
255e02e12deSAxel Dörfler				return B_ERROR;
256e02e12deSAxel Dörfler			}
257e02e12deSAxel Dörfler		}
258409f1731Sshadow	}
259e02e12deSAxel Dörfler
260e02e12deSAxel Dörfler	return B_ERROR;
261409f1731Sshadow}
262409f1731Sshadow
263409f1731Sshadow
264409f1731Sshadow// return true if both ports must be programmed
265409f1731Sshadowbool Radeon_NeedsSecondPort( display_mode *mode )
266409f1731Sshadow{
267409f1731Sshadow	switch( mode->timing.flags & RADEON_MODE_MASK ) {
268409f1731Sshadow	case RADEON_MODE_COMBINE:
269409f1731Sshadow		return true;
270409f1731Sshadow	default:
271409f1731Sshadow		return false;
272409f1731Sshadow	}
273409f1731Sshadow}
274409f1731Sshadow
275e02e12deSAxel Dörfler
276409f1731Sshadow// return number of ports showing differents parts of frame buffer
277409f1731Sshadowbool Radeon_DifferentPorts( display_mode *mode )
278409f1731Sshadow{
279409f1731Sshadow	switch( mode->timing.flags & RADEON_MODE_MASK ) {
280409f1731Sshadow	case RADEON_MODE_COMBINE:
281409f1731Sshadow		return 2;
282409f1731Sshadow	default:
283409f1731Sshadow		return 1;
284409f1731Sshadow	}
285409f1731Sshadow}
286