1/*
2	Copyright (c) 2002-2005, Thomas Kurschel
3
4
5	Part of Radeon accelerant
6
7	CRTC programming
8*/
9
10#include "radeon_accelerant.h"
11#include "mmio.h"
12#include "crtc_regs.h"
13#include "GlobalData.h"
14#include "set_mode.h"
15#include "generic.h"
16
17
18// hammer CRTC registers
19void Radeon_ProgramCRTCRegisters( accelerator_info *ai, int crtc_idx,
20	crtc_regs *values )
21{
22	vuint8 *regs = ai->regs;
23
24	SHOW_FLOW0( 2, "" );
25
26	if( crtc_idx == 0 ) {
27		OUTREGP( regs, RADEON_CRTC_GEN_CNTL, values->crtc_gen_cntl,
28			RADEON_CRTC_EXT_DISP_EN );
29
30		OUTREG( regs, RADEON_CRTC_H_TOTAL_DISP, values->crtc_h_total_disp );
31		OUTREG( regs, RADEON_CRTC_H_SYNC_STRT_WID, values->crtc_h_sync_strt_wid );
32		OUTREG( regs, RADEON_CRTC_V_TOTAL_DISP, values->crtc_v_total_disp );
33		OUTREG( regs, RADEON_CRTC_V_SYNC_STRT_WID, values->crtc_v_sync_strt_wid );
34		OUTREG( regs, RADEON_CRTC_OFFSET_CNTL, values->crtc_offset_cntl );
35		OUTREG( regs, RADEON_CRTC_PITCH, values->crtc_pitch );
36
37	} else {
38		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, values->crtc_gen_cntl,
39			RADEON_CRTC2_VSYNC_DIS |
40			RADEON_CRTC2_HSYNC_DIS |
41			RADEON_CRTC2_DISP_DIS |
42			RADEON_CRTC2_CRT2_ON );
43
44		OUTREG( regs, RADEON_CRTC2_H_TOTAL_DISP, values->crtc_h_total_disp );
45		OUTREG( regs, RADEON_CRTC2_H_SYNC_STRT_WID, values->crtc_h_sync_strt_wid );
46		OUTREG( regs, RADEON_CRTC2_V_TOTAL_DISP, values->crtc_v_total_disp );
47		OUTREG( regs, RADEON_CRTC2_V_SYNC_STRT_WID, values->crtc_v_sync_strt_wid );
48		OUTREG( regs, RADEON_CRTC2_OFFSET_CNTL, values->crtc_offset_cntl );
49		OUTREG( regs, RADEON_CRTC2_PITCH, values->crtc_pitch );
50	}
51}
52
53
54// get required hsync delay depending on bit depth and output device
55uint16 Radeon_GetHSyncFudge( crtc_info *crtc, int datatype )
56{
57	static int hsync_fudge_default[] = { 0x00, 0x12, 0x09, 0x09, 0x06, 0x05 };
58	static int hsync_fudge_fp[]      = { 0x02, 0x02, 0x00, 0x00, 0x05, 0x05 };
59
60	// there is an sync delay which depends on colour-depth and output device
61	if( (crtc->chosen_displays & (dd_dvi | dd_dvi_ext | dd_lvds )) != 0 )
62		return hsync_fudge_fp[datatype - 1];
63    else
64    	return hsync_fudge_default[datatype - 1];
65}
66
67
68// calculate CRTC register content
69void Radeon_CalcCRTCRegisters( accelerator_info *ai, crtc_info *crtc,
70	display_mode *mode, crtc_regs *values )
71{
72	virtual_card *vc = ai->vc;
73    int    hsync_start;
74    int    hsync_wid;
75    int    hsync_fudge;
76    int    vsync_wid;
77
78	hsync_fudge = Radeon_GetHSyncFudge( crtc, vc->datatype );
79
80	if( crtc->crtc_idx == 0 ) {
81		// here, we should set interlace/double scan mode
82		// but we don't support them (anyone missing them?)
83	    values->crtc_gen_cntl =
84			RADEON_CRTC_EN
85			| (vc->datatype << 8);
86
87	} else {
88		values->crtc_gen_cntl = RADEON_CRTC2_EN
89			| (vc->datatype << 8)
90			| (0/*doublescan*/ ? RADEON_CRTC2_DBL_SCAN_EN	: 0)
91			| ((mode->timing.flags & B_TIMING_INTERLACED)
92				? RADEON_CRTC2_INTERLACE_EN	: 0);
93	}
94
95    values->crtc_h_total_disp =
96    	((mode->timing.h_total / 8 - 1) & RADEON_CRTC_H_TOTAL)
97		| (((mode->timing.h_display / 8 - 1) << RADEON_CRTC_H_DISP_SHIFT) & RADEON_CRTC_H_DISP);
98
99    hsync_wid = (mode->timing.h_sync_end - mode->timing.h_sync_start) / 8;
100
101    hsync_start = mode->timing.h_sync_start - 8 + hsync_fudge;
102
103    values->crtc_h_sync_strt_wid =
104    	(hsync_start & (RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX))
105		| (hsync_wid << RADEON_CRTC_H_SYNC_WID_SHIFT)
106		| ((mode->flags & B_POSITIVE_HSYNC) == 0 ? RADEON_CRTC_H_SYNC_POL : 0);
107
108   	values->crtc_v_total_disp =
109    	((mode->timing.v_total - 1) & RADEON_CRTC_V_TOTAL)
110		| (((mode->timing.v_display - 1) << RADEON_CRTC_V_DISP_SHIFT) & RADEON_CRTC_V_DISP);
111
112    vsync_wid = mode->timing.v_sync_end - mode->timing.v_sync_start;
113
114	values->crtc_v_sync_strt_wid =
115    	((mode->timing.v_sync_start - 1) & RADEON_CRTC_V_SYNC_STRT)
116		| (vsync_wid << RADEON_CRTC_V_SYNC_WID_SHIFT)
117		| ((mode->flags & B_POSITIVE_VSYNC) == 0
118		    ? RADEON_CRTC_V_SYNC_POL : 0);
119
120    values->crtc_offset_cntl = 0;
121
122	values->crtc_pitch = Radeon_RoundVWidth( mode->virtual_width, vc->bpp ) / 8;
123
124	SHOW_FLOW( 2, "crtc_pitch=%ld", values->crtc_pitch );
125
126    values->crtc_pitch |= values->crtc_pitch << 16;
127}
128
129
130// update shown are of one port
131static void moveOneDisplay( accelerator_info *ai, crtc_info *crtc )
132{
133	virtual_card *vc = ai->vc;
134	uint32 offset;
135
136	offset = (vc->mode.v_display_start + crtc->rel_y) * vc->pitch +
137		(vc->mode.h_display_start + crtc->rel_x) * vc->bpp +
138		vc->fb_offset;
139
140	SHOW_FLOW( 3, "Setting address %x on port %d",
141		offset,	crtc->crtc_idx );
142
143	OUTREG( ai->regs, crtc->crtc_idx == 0 ? RADEON_CRTC_OFFSET : RADEON_CRTC2_OFFSET, offset );
144}
145
146// internal function: pan display
147// engine lock should be hold
148status_t Radeon_MoveDisplay( accelerator_info *ai, uint16 h_display_start, uint16 v_display_start )
149{
150	virtual_card *vc = ai->vc;
151
152	SHOW_FLOW( 4, "h_display_start=%ld, v_display_start=%ld",
153		h_display_start, v_display_start );
154
155	if( h_display_start + vc->eff_width > vc->mode.virtual_width ||
156		v_display_start + vc->eff_height > vc->mode.virtual_height )
157		return B_ERROR;
158
159	// this is needed both for get_mode_info and for scrolling of virtual screens
160	vc->mode.h_display_start = h_display_start & ~7;
161	vc->mode.v_display_start = v_display_start;
162
163	// do it
164	if( vc->used_crtc[0] )
165		moveOneDisplay( ai, &ai->si->crtc[0] );
166	if( vc->used_crtc[1] )
167		moveOneDisplay( ai, &ai->si->crtc[1] );
168
169	// overlay position must be adjusted
170	Radeon_UpdateOverlay( ai );
171
172	return B_OK;
173}
174
175// public function: pan display
176status_t MOVE_DISPLAY( uint16 h_display_start, uint16 v_display_start )
177{
178	shared_info *si = ai->si;
179	status_t result;
180
181	ACQUIRE_BEN( si->engine.lock );
182
183	// TBD: we should probably lock card first; in this case, we must
184	//      split this function into locking and worker part, as this
185	//      function is used internally as well
186	result = Radeon_MoveDisplay( ai, h_display_start, v_display_start );
187
188	RELEASE_BEN( si->engine.lock );
189
190	return result;
191}
192