1/*
2	Copyright (c) 2002-2004, Thomas Kurschel
3
4
5	Part of Radeon accelerant
6
7	Flat panel support
8*/
9
10#include "radeon_accelerant.h"
11#include "mmio.h"
12#include "fp_regs.h"
13#include "memcntrl_regs.h"
14#include "utils.h"
15#include "set_mode.h"
16#include "pll_regs.h"
17#include "pll_access.h"
18
19
20void Radeon_ReadRMXRegisters(
21	accelerator_info *ai, fp_regs *values )
22{
23	vuint8 *regs = ai->regs;
24
25	values->fp_horz_stretch = INREG( regs, RADEON_FP_HORZ_STRETCH );
26	values->fp_vert_stretch = INREG( regs, RADEON_FP_VERT_STRETCH );
27}
28
29void Radeon_CalcRMXRegisters(
30	fp_info *flatpanel, display_mode *mode, bool use_rmx, fp_regs *values )
31{
32	uint xres = mode->timing.h_display;
33	uint yres = mode->timing.v_display;
34	uint64 Hratio, Vratio;
35
36	if( !use_rmx ) {
37		// disable RMX unit if requested
38		values->fp_horz_stretch &=
39			~(RADEON_HORZ_STRETCH_BLEND |
40			  RADEON_HORZ_STRETCH_ENABLE);
41
42		values->fp_vert_stretch &=
43			~(RADEON_VERT_STRETCH_ENABLE |
44			  RADEON_VERT_STRETCH_BLEND);
45
46		return;
47	}
48
49	// RMX unit can only upscale, not downscale
50	if( xres > flatpanel->panel_xres )
51		xres = flatpanel->panel_xres;
52	if( yres > flatpanel->panel_yres )
53		yres = flatpanel->panel_yres;
54
55	Hratio = FIX_SCALE * (uint32)xres / flatpanel->panel_xres;
56	Vratio = FIX_SCALE * (uint32)yres / flatpanel->panel_yres;
57
58	// save it for overlay unit (overlays must be vertically scaled manually)
59	flatpanel->h_ratio = Hratio;
60	flatpanel->v_ratio = Vratio;
61
62	values->fp_horz_stretch = flatpanel->panel_xres << RADEON_HORZ_PANEL_SIZE_SHIFT;
63
64	if( Hratio == FIX_SCALE ) {
65		values->fp_horz_stretch &=
66			~(RADEON_HORZ_STRETCH_BLEND |
67			  RADEON_HORZ_STRETCH_ENABLE);
68	} else {
69		uint32 stretch;
70
71		stretch = (uint32)((Hratio * RADEON_HORZ_STRETCH_RATIO_MAX +
72			FIX_SCALE / 2) >> FIX_SHIFT) & RADEON_HORZ_STRETCH_RATIO_MASK;
73
74		values->fp_horz_stretch = stretch
75			| (values->fp_horz_stretch & (RADEON_HORZ_PANEL_SIZE |
76				RADEON_HORZ_FP_LOOP_STRETCH |
77				RADEON_HORZ_AUTO_RATIO_INC));
78		values->fp_horz_stretch |=
79			RADEON_HORZ_STRETCH_BLEND |
80			RADEON_HORZ_STRETCH_ENABLE;
81	}
82	values->fp_horz_stretch &= ~RADEON_HORZ_AUTO_RATIO;
83
84	values->fp_vert_stretch = flatpanel->panel_yres << RADEON_VERT_PANEL_SIZE_SHIFT;
85
86	if( Vratio == FIX_SCALE ) {
87		values->fp_vert_stretch &=
88			~(RADEON_VERT_STRETCH_ENABLE |
89			  RADEON_VERT_STRETCH_BLEND);
90	} else {
91		uint32 stretch;
92
93		stretch = (uint32)((Vratio * RADEON_VERT_STRETCH_RATIO_MAX +
94			FIX_SCALE / 2) >> FIX_SHIFT) & RADEON_VERT_STRETCH_RATIO_MASK;
95
96		values->fp_vert_stretch = stretch
97			| (values->fp_vert_stretch & (RADEON_VERT_PANEL_SIZE |
98				RADEON_VERT_STRETCH_RESERVED));
99		values->fp_vert_stretch |=
100			RADEON_VERT_STRETCH_ENABLE |
101			RADEON_VERT_STRETCH_BLEND;
102	}
103	values->fp_vert_stretch &= ~RADEON_VERT_AUTO_RATIO_EN;
104}
105
106// write RMX registers
107void Radeon_ProgramRMXRegisters(
108	accelerator_info *ai, fp_regs *values )
109{
110	vuint8 *regs = ai->regs;
111	SHOW_FLOW0( 2, "" );
112	OUTREG( regs, RADEON_FP_HORZ_STRETCH, values->fp_horz_stretch );
113	OUTREG( regs, RADEON_FP_VERT_STRETCH, values->fp_vert_stretch );
114}
115
116
117void Radeon_ReadFPRegisters(
118	accelerator_info *ai, fp_regs *values )
119{
120	vuint8 *regs = ai->regs;
121
122	values->fp_gen_cntl = INREG( regs, RADEON_FP_GEN_CNTL );
123	values->fp2_gen_cntl = INREG( regs, RADEON_FP2_GEN_CNTL );
124	values->lvds_gen_cntl = INREG( regs, RADEON_LVDS_GEN_CNTL );
125	values->tmds_pll_cntl = INREG( regs, RADEON_TMDS_PLL_CNTL );
126	values->tmds_trans_cntl = INREG( regs, RADEON_TMDS_TRANSMITTER_CNTL );
127	values->fp_h_sync_strt_wid = INREG( regs, RADEON_FP_H_SYNC_STRT_WID );
128	values->fp_v_sync_strt_wid = INREG( regs, RADEON_FP_V_SYNC_STRT_WID );
129	values->fp2_h_sync_strt_wid = INREG( regs, RADEON_FP_H2_SYNC_STRT_WID );
130	values->fp2_v_sync_strt_wid = INREG( regs, RADEON_FP_V2_SYNC_STRT_WID );
131	values->bios_4_scratch =  INREG( regs, RADEON_BIOS_4_SCRATCH );
132	values->bios_5_scratch =  INREG( regs, RADEON_BIOS_5_SCRATCH );
133	values->bios_6_scratch =  INREG( regs, RADEON_BIOS_6_SCRATCH );
134
135	if (ai->si->asic == rt_rv280) {
136		// bit 22 of TMDS_PLL_CNTL is read-back inverted
137		values->tmds_pll_cntl ^= (1 << 22);
138	}
139
140	SHOW_FLOW( 2, "before: fp_gen_cntl=%08lx, horz=%08lx, vert=%08lx, lvds_gen_cntl=%08lx",
141    		values->fp_gen_cntl, values->fp_horz_stretch, values->fp_vert_stretch,
142    		values->lvds_gen_cntl );
143}
144
145// calculcate flat panel crtc registers;
146// must be called after normal CRTC registers are determined
147void Radeon_CalcFPRegisters(
148	accelerator_info *ai, crtc_info *crtc,
149	fp_info *fp_port, crtc_regs *crtc_values, fp_regs *values )
150{
151	int i;
152	uint32 tmp = values->tmds_pll_cntl & 0xfffff;
153
154	// setup synchronization position
155	// (most values are ignored according to fp_gen_cntl, but at least polarity
156	//  and pixel precise horizontal sync position are always used)
157	if( fp_port->is_fp2 ) {
158		SHOW_FLOW0( 2, "is_fp2" );
159		values->fp2_h_sync_strt_wid = crtc_values->crtc_h_sync_strt_wid;
160		values->fp2_v_sync_strt_wid = crtc_values->crtc_v_sync_strt_wid;
161	} else {
162		SHOW_FLOW0( 2, "fp1" );
163		values->fp_h_sync_strt_wid = crtc_values->crtc_h_sync_strt_wid;
164		values->fp_v_sync_strt_wid = crtc_values->crtc_v_sync_strt_wid;
165	}
166
167	if( fp_port->is_fp2 ) {
168		// should retain POST values (esp bit 28)
169		values->fp2_gen_cntl &= (0xFFFF0000);
170
171	} else {
172		// setup magic CRTC shadowing
173		values->fp_gen_cntl &=
174			~(RADEON_FP_RMX_HVSYNC_CONTROL_EN |
175			  RADEON_FP_DFP_SYNC_SEL |
176			  RADEON_FP_CRT_SYNC_SEL |
177			  RADEON_FP_CRTC_LOCK_8DOT |
178			  RADEON_FP_USE_SHADOW_EN |
179			  RADEON_FP_CRTC_USE_SHADOW_VEND |
180			  RADEON_FP_CRT_SYNC_ALT);
181		values->fp_gen_cntl |=
182			RADEON_FP_CRTC_DONT_SHADOW_VPAR |
183			RADEON_FP_CRTC_DONT_SHADOW_HEND;
184	}
185
186	for (i = 0; i < 4; i++) {
187		if (ai->si->tmds_pll[i].freq == 0)
188			break;
189		if ((uint32)(fp_port->dot_clock) < ai->si->tmds_pll[i].freq) {
190			tmp = ai->si->tmds_pll[i].value ;
191			break;
192		}
193	}
194
195	if (IS_R300_VARIANT || (ai->si->asic == rt_rv280)) {
196		if (tmp & 0xfff00000) {
197			values->tmds_pll_cntl = tmp;
198		} else {
199			values->tmds_pll_cntl = ai->si->tmds_pll_cntl & 0xfff00000;
200			values->tmds_pll_cntl |= tmp;
201		}
202	} else {
203		values->tmds_pll_cntl = tmp;
204	}
205
206	values->tmds_trans_cntl = ai->si->tmds_transmitter_cntl
207		& ~(RADEON_TMDS_TRANSMITTER_PLLRST);
208
209	if (IS_R300_VARIANT || (ai->si->asic == rt_r200) || (ai->si->num_crtc == 1))
210		values->tmds_trans_cntl &= ~(RADEON_TMDS_TRANSMITTER_PLLEN);
211	else // weird, RV chips got this bit reversed?
212		values->tmds_trans_cntl |= (RADEON_TMDS_TRANSMITTER_PLLEN);
213
214
215	// enable proper transmitter
216	if( (crtc->chosen_displays & dd_lvds) != 0 ) {
217		// using LVDS means there cannot be a DVI monitor
218		SHOW_FLOW0( 2, "lvds" );
219		values->lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_BLON);
220		values->fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN);
221
222	} else if( !fp_port->is_fp2 ) {
223		// DVI on internal transmitter
224		SHOW_FLOW0( 2, "DVI INT" );
225		values->fp_gen_cntl |= RADEON_FP_FPON | RADEON_FP_TMDS_EN;
226		// enabling 8 bit data may be dangerous; BIOS should have taken care of that
227		values->fp_gen_cntl |= RADEON_FP_PANEL_FORMAT;
228
229	} else {
230		// DVI on external transmitter
231		SHOW_FLOW0( 2, "DVI EXT" );
232		values->fp2_gen_cntl |= RADEON_FP2_FPON | RADEON_FP_PANEL_FORMAT;
233		values->fp2_gen_cntl &= ~RADEON_FP2_BLANK_EN;
234
235		//hack in missing bits test...
236		//values->fp2_gen_cntl |= (1 << 22) | (1 << 28);
237
238		if( ai->si->asic >= rt_r200 )
239			values->fp2_gen_cntl |= RADEON_FP2_DV0_EN;
240	}
241
242    SHOW_FLOW( 2, "after: fp_gen_cntl=%08lx, fp2_gen_cntl=%08lx, horz=%08lx, vert=%08lx, lvds_gen_cntl=%08lx",
243    	values->fp_gen_cntl, values->fp2_gen_cntl, values->fp_horz_stretch, values->fp_vert_stretch,
244    	values->lvds_gen_cntl );
245}
246
247
248// write flat panel registers
249void Radeon_ProgramFPRegisters(
250	accelerator_info *ai, crtc_info *crtc,
251	fp_info *fp_port, fp_regs *values )
252{
253	shared_info *si = ai->si;
254	vuint8 *regs = ai->regs;
255
256	SHOW_FLOW0( 2, "" );
257
258	OUTREGP( regs, RADEON_FP_GEN_CNTL, values->fp_gen_cntl, RADEON_FP_SEL_CRTC2 );
259
260	if( fp_port->is_fp2 ) {
261		SHOW_FLOW0( 2, "is_fp2" );
262		OUTREGP( regs, RADEON_FP2_GEN_CNTL, values->fp2_gen_cntl,
263			~(RADEON_FP2_SOURCE_SEL_MASK | RADEON_FP2_SRC_SEL_MASK));
264		OUTREGP( regs, RADEON_FP2_GEN_CNTL, values->fp2_gen_cntl,
265			RADEON_FP2_SOURCE_SEL_CRTC2 | RADEON_FP2_SRC_SEL_CRTC2 );
266		OUTREG( regs, RADEON_FP_H2_SYNC_STRT_WID, values->fp2_h_sync_strt_wid );
267		OUTREG( regs, RADEON_FP_V2_SYNC_STRT_WID, values->fp2_v_sync_strt_wid );
268	} else {
269		SHOW_FLOW0( 2, "is_fp1" );
270		OUTREG( regs, RADEON_FP_H_SYNC_STRT_WID, values->fp_h_sync_strt_wid );
271		OUTREG( regs, RADEON_FP_V_SYNC_STRT_WID, values->fp_v_sync_strt_wid );
272	}
273
274	// workaround for old AIW Radeon having display buffer underflow
275	// in conjunction with DVI
276	if( si->asic == rt_r100 ) {
277		OUTREG( regs, RADEON_GRPH_BUFFER_CNTL,
278			INREG( regs, RADEON_GRPH_BUFFER_CNTL) & ~0x7f0000);
279	}
280
281	if ( ai->si->is_mobility ) {
282		OUTREG( regs, RADEON_BIOS_4_SCRATCH, values->bios_4_scratch);
283		OUTREG( regs, RADEON_BIOS_5_SCRATCH, values->bios_5_scratch);
284		OUTREG( regs, RADEON_BIOS_6_SCRATCH, values->bios_6_scratch);
285    }
286
287	if( (crtc->chosen_displays & dd_lvds) != 0 ) {
288
289		//OUTREGP( regs, RADEON_LVDS_GEN_CNTL, values->lvds_gen_cntl,
290		//	RADEON_LVDS_ON | RADEON_LVDS_BLON );
291
292		uint32 old_pixclks_cntl;
293		uint32 tmp;
294
295		old_pixclks_cntl = Radeon_INPLL( ai->regs, ai->si->asic, RADEON_PIXCLKS_CNTL);
296
297		// ASIC bug: when LVDS_ON is reset, LVDS_ALWAYS_ON must be zero
298		if( ai->si->is_mobility || ai->si->is_igp )
299		{
300			if (!(values->lvds_gen_cntl & RADEON_LVDS_ON)) {
301				Radeon_OUTPLLP( ai->regs, ai->si->asic, RADEON_PIXCLKS_CNTL, 0, ~RADEON_PIXCLK_LVDS_ALWAYS_ONb );
302			}
303		}
304
305		// get current state of LCD
306		tmp = INREG( regs, RADEON_LVDS_GEN_CNTL);
307
308		// if LCD is on, and previous state was on, just write the state directly.
309		if (( tmp & ( RADEON_LVDS_ON | RADEON_LVDS_BLON )) ==
310			( values->lvds_gen_cntl & ( RADEON_LVDS_ON | RADEON_LVDS_BLON ))) {
311			OUTREG( regs, RADEON_LVDS_GEN_CNTL, values->lvds_gen_cntl );
312		} else {
313			if ( values->lvds_gen_cntl & ( RADEON_LVDS_ON | RADEON_LVDS_BLON )) {
314				snooze( ai->si->panel_pwr_delay * 1000 );
315				OUTREG( regs, RADEON_LVDS_GEN_CNTL, values->lvds_gen_cntl );
316			} else {
317
318				//turn on backlight, wait for stable before turning on data ???
319				OUTREG( regs, RADEON_LVDS_GEN_CNTL,	values->lvds_gen_cntl | RADEON_LVDS_BLON );
320				snooze( ai->si->panel_pwr_delay * 1000 );
321				OUTREG( regs, RADEON_LVDS_GEN_CNTL, values->lvds_gen_cntl );
322			}
323		}
324
325		if( ai->si->is_mobility || ai->si->is_igp ) {
326			if (!(values->lvds_gen_cntl & RADEON_LVDS_ON)) {
327				Radeon_OUTPLL( ai->regs, ai->si->asic, RADEON_PIXCLKS_CNTL, old_pixclks_cntl );
328			}
329		}
330	}
331}
332