xref: /haiku/src/add-ons/accelerants/radeon/crtc.c (revision 2a37e4c1cf59b445e309a3880ceed82d25a0d107)
1409f1731Sshadow303 /*
2409f1731Sshadow303 	Copyright (c) 2002, Thomas Kurschel
3409f1731Sshadow303 
4409f1731Sshadow303 
5409f1731Sshadow303 	Part of Radeon accelerant
6409f1731Sshadow303 
7409f1731Sshadow303 	CRTC programming
8409f1731Sshadow303 */
9409f1731Sshadow303 
10409f1731Sshadow303 #include "radeon_accelerant.h"
11409f1731Sshadow303 #include "mmio.h"
12409f1731Sshadow303 #include "crtc_regs.h"
13409f1731Sshadow303 #include "dac_regs.h"
14*2a37e4c1Sshadow303 #include "GlobalData.h"
15*2a37e4c1Sshadow303 
16*2a37e4c1Sshadow303 
17*2a37e4c1Sshadow303 // read old CRTC register content
18*2a37e4c1Sshadow303 void Radeon_ReadCRTCRegisters( accelerator_info *ai, physical_head *head,
19*2a37e4c1Sshadow303 	port_regs *values )
20*2a37e4c1Sshadow303 {
21*2a37e4c1Sshadow303 	vuint8 *regs = ai->regs;
22*2a37e4c1Sshadow303 
23*2a37e4c1Sshadow303 	// only CRTC_EXT_CNTL is programmed by someone else (namely the monitor
24*2a37e4c1Sshadow303 	// router); if more registers are affected, you must read them here too!
25*2a37e4c1Sshadow303 	if( !head->is_crtc2 ) {
26*2a37e4c1Sshadow303 		values->crtc_ext_cntl = INREG( regs, RADEON_CRTC_EXT_CNTL );
27*2a37e4c1Sshadow303 	}
28*2a37e4c1Sshadow303 }
29*2a37e4c1Sshadow303 
30409f1731Sshadow303 
31409f1731Sshadow303 // hammer CRTC registers
32*2a37e4c1Sshadow303 void Radeon_ProgramCRTCRegisters( accelerator_info *ai, physical_head *head,
33409f1731Sshadow303 	port_regs *values )
34409f1731Sshadow303 {
35409f1731Sshadow303 	vuint8 *regs = ai->regs;
36409f1731Sshadow303 
37409f1731Sshadow303 	SHOW_FLOW0( 2, "" );
38409f1731Sshadow303 
39*2a37e4c1Sshadow303 	if( head->is_crtc2 ) {
40409f1731Sshadow303 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, values->crtc_gen_cntl,
41409f1731Sshadow303 			RADEON_CRTC2_VSYNC_DIS |
42409f1731Sshadow303 			RADEON_CRTC2_HSYNC_DIS |
43409f1731Sshadow303 			RADEON_CRTC2_DISP_DIS );
44409f1731Sshadow303 
45409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC2_H_TOTAL_DISP, values->crtc_h_total_disp );
46409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC2_H_SYNC_STRT_WID, values->crtc_h_sync_strt_wid );
47409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC2_V_TOTAL_DISP, values->crtc_v_total_disp );
48409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC2_V_SYNC_STRT_WID, values->crtc_v_sync_strt_wid );
49409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC2_OFFSET_CNTL, values->crtc_offset_cntl );
50409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC2_PITCH, values->crtc_pitch );
51409f1731Sshadow303 
52409f1731Sshadow303 	} else {
53409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC_GEN_CNTL, values->crtc_gen_cntl );
54409f1731Sshadow303 
55409f1731Sshadow303 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL, values->crtc_ext_cntl,
56409f1731Sshadow303 			RADEON_CRTC_VSYNC_DIS |
57409f1731Sshadow303 			RADEON_CRTC_HSYNC_DIS |
58*2a37e4c1Sshadow303 			RADEON_CRTC_DISPLAY_DIS |
59*2a37e4c1Sshadow303 			RADEON_CRTC_CRT_ON );
60409f1731Sshadow303 
61409f1731Sshadow303 		OUTREGP( regs, RADEON_DAC_CNTL, values->dac_cntl,
62*2a37e4c1Sshadow303 			RADEON_DAC_RANGE_CNTL_MASK | RADEON_DAC_BLANKING );
63409f1731Sshadow303 
64409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC_H_TOTAL_DISP, values->crtc_h_total_disp );
65409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC_H_SYNC_STRT_WID, values->crtc_h_sync_strt_wid );
66409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC_V_TOTAL_DISP, values->crtc_v_total_disp );
67409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC_V_SYNC_STRT_WID, values->crtc_v_sync_strt_wid );
68409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC_OFFSET_CNTL, values->crtc_offset_cntl );
69409f1731Sshadow303 		OUTREG( regs, RADEON_CRTC_PITCH, values->crtc_pitch );
70409f1731Sshadow303 	}
71409f1731Sshadow303 }
72409f1731Sshadow303 
73409f1731Sshadow303 
74409f1731Sshadow303 // get required hsync delay depending on bit depth and output device
75*2a37e4c1Sshadow303 uint16 Radeon_GetHSyncFudge( physical_head *head, int datatype )
76409f1731Sshadow303 {
77409f1731Sshadow303 	static int hsync_fudge_default[] = { 0x00, 0x12, 0x09, 0x09, 0x06, 0x05 };
78409f1731Sshadow303 	static int hsync_fudge_fp[]      = { 0x02, 0x02, 0x00, 0x00, 0x05, 0x05 };
79409f1731Sshadow303 
80409f1731Sshadow303 	// there is an sync delay which depends on colour-depth and output device
81*2a37e4c1Sshadow303 	if( (head->chosen_displays & (dd_dvi | dd_dvi_ext | dd_lvds )) != 0 )
82409f1731Sshadow303 		return hsync_fudge_fp[datatype - 1];
83409f1731Sshadow303     else
84409f1731Sshadow303     	return hsync_fudge_default[datatype - 1];
85409f1731Sshadow303 }
86409f1731Sshadow303 
87409f1731Sshadow303 
88409f1731Sshadow303 // calculate CRTC register content
89*2a37e4c1Sshadow303 void Radeon_CalcCRTCRegisters( accelerator_info *ai, physical_head *head,
90409f1731Sshadow303 	display_mode *mode, port_regs *values )
91409f1731Sshadow303 {
92409f1731Sshadow303 	virtual_card *vc = ai->vc;
93409f1731Sshadow303     int    hsync_start;
94409f1731Sshadow303     int    hsync_wid;
95409f1731Sshadow303     int    hsync_fudge;
96409f1731Sshadow303     int    vsync_wid;
97409f1731Sshadow303 
98*2a37e4c1Sshadow303 	hsync_fudge = Radeon_GetHSyncFudge( head, vc->datatype );
99409f1731Sshadow303 
100*2a37e4c1Sshadow303 	if( head->is_crtc2 ) {
101409f1731Sshadow303 		values->crtc_gen_cntl = (RADEON_CRTC2_EN
102409f1731Sshadow303 			| RADEON_CRTC2_CRT2_ON
103409f1731Sshadow303 			| (vc->datatype << 8)
104409f1731Sshadow303 			| (0/*doublescan*/ ? RADEON_CRTC2_DBL_SCAN_EN	: 0)
105409f1731Sshadow303 			| ((mode->timing.flags & B_TIMING_INTERLACED)
106409f1731Sshadow303 				? RADEON_CRTC2_INTERLACE_EN	: 0));
107409f1731Sshadow303 	} else {
108409f1731Sshadow303 		// here, we should set interlace/double scan mode
109409f1731Sshadow303 		// but we don't support them (anyone missing them?)
110409f1731Sshadow303 	    values->crtc_gen_cntl = (RADEON_CRTC_EXT_DISP_EN
111409f1731Sshadow303 			| RADEON_CRTC_EN
112409f1731Sshadow303 			| (vc->datatype << 8));
113409f1731Sshadow303 
114409f1731Sshadow303 		values->crtc_ext_cntl =
115409f1731Sshadow303 	    	RADEON_VGA_ATI_LINEAR |
116*2a37e4c1Sshadow303 			RADEON_XCRT_CNT_EN;
117409f1731Sshadow303 
118409f1731Sshadow303 		values->dac_cntl = RADEON_DAC_MASK_ALL
119409f1731Sshadow303 			| RADEON_DAC_VGA_ADR_EN
120409f1731Sshadow303 			| RADEON_DAC_8BIT_EN;
121409f1731Sshadow303 	}
122409f1731Sshadow303 
123409f1731Sshadow303     values->crtc_h_total_disp =
124409f1731Sshadow303     	((mode->timing.h_total / 8 - 1) & RADEON_CRTC_H_TOTAL)
125409f1731Sshadow303 		| (((mode->timing.h_display / 8 - 1) << RADEON_CRTC_H_DISP_SHIFT) & RADEON_CRTC_H_DISP);
126409f1731Sshadow303 
127409f1731Sshadow303     hsync_wid = (mode->timing.h_sync_end - mode->timing.h_sync_start) / 8;
128409f1731Sshadow303 
129409f1731Sshadow303     hsync_start = mode->timing.h_sync_start - 8 + hsync_fudge;
130409f1731Sshadow303 
131409f1731Sshadow303     values->crtc_h_sync_strt_wid =
132409f1731Sshadow303     	(hsync_start & (RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX))
133409f1731Sshadow303 		| (hsync_wid << RADEON_CRTC_H_SYNC_WID_SHIFT)
134409f1731Sshadow303 		| ((mode->flags & B_POSITIVE_HSYNC) == 0 ? RADEON_CRTC_H_SYNC_POL : 0);
135409f1731Sshadow303 
136409f1731Sshadow303    	values->crtc_v_total_disp =
137409f1731Sshadow303     	((mode->timing.v_total - 1) & RADEON_CRTC_V_TOTAL)
138409f1731Sshadow303 		| (((mode->timing.v_display - 1) << RADEON_CRTC_V_DISP_SHIFT) & RADEON_CRTC_V_DISP);
139409f1731Sshadow303 
140409f1731Sshadow303     vsync_wid = mode->timing.v_sync_end - mode->timing.v_sync_start;
141409f1731Sshadow303 
142409f1731Sshadow303 	values->crtc_v_sync_strt_wid =
143409f1731Sshadow303     	((mode->timing.v_sync_start - 1) & RADEON_CRTC_V_SYNC_STRT)
144409f1731Sshadow303 		| (vsync_wid << RADEON_CRTC_V_SYNC_WID_SHIFT)
145409f1731Sshadow303 		| ((mode->flags & B_POSITIVE_VSYNC) == 0
146409f1731Sshadow303 		    ? RADEON_CRTC_V_SYNC_POL : 0);
147409f1731Sshadow303 
148409f1731Sshadow303     values->crtc_offset_cntl = 0;
149409f1731Sshadow303 
150409f1731Sshadow303 	values->crtc_pitch = Radeon_RoundVWidth( mode->virtual_width, vc->bpp ) / 8;
151409f1731Sshadow303 
152409f1731Sshadow303 	SHOW_FLOW( 2, "crtc_pitch=%ld", values->crtc_pitch );
153409f1731Sshadow303 
154409f1731Sshadow303     values->crtc_pitch |= values->crtc_pitch << 16;
155409f1731Sshadow303 }
156*2a37e4c1Sshadow303 
157*2a37e4c1Sshadow303 
158*2a37e4c1Sshadow303 // update shown are of one port
159*2a37e4c1Sshadow303 static void moveOneDisplay( accelerator_info *ai, virtual_head *virtual_head )
160*2a37e4c1Sshadow303 {
161*2a37e4c1Sshadow303 	virtual_card *vc = ai->vc;
162*2a37e4c1Sshadow303 	uint32 offset;
163*2a37e4c1Sshadow303 
164*2a37e4c1Sshadow303 	offset = (vc->mode.v_display_start + virtual_head->rel_y) * vc->pitch +
165*2a37e4c1Sshadow303 		(vc->mode.h_display_start + virtual_head->rel_x) * vc->bpp +
166*2a37e4c1Sshadow303 		vc->fb_offset;
167*2a37e4c1Sshadow303 
168*2a37e4c1Sshadow303 	SHOW_FLOW( 3, "Setting address %x on port %d",
169*2a37e4c1Sshadow303 		offset,	virtual_head->physical_head );
170*2a37e4c1Sshadow303 
171*2a37e4c1Sshadow303 	OUTREG( ai->regs, virtual_head->physical_head ? RADEON_CRTC2_OFFSET : RADEON_CRTC_OFFSET, offset );
172*2a37e4c1Sshadow303 }
173*2a37e4c1Sshadow303 
174*2a37e4c1Sshadow303 // internal function: pan display
175*2a37e4c1Sshadow303 // engine lock should be hold
176*2a37e4c1Sshadow303 status_t Radeon_MoveDisplay( accelerator_info *ai, uint16 h_display_start, uint16 v_display_start )
177*2a37e4c1Sshadow303 {
178*2a37e4c1Sshadow303 	virtual_card *vc = ai->vc;
179*2a37e4c1Sshadow303 
180*2a37e4c1Sshadow303 	SHOW_FLOW( 4, "h_display_start=%ld, v_display_start=%ld",
181*2a37e4c1Sshadow303 		h_display_start, v_display_start );
182*2a37e4c1Sshadow303 
183*2a37e4c1Sshadow303 	if( h_display_start + vc->eff_width > vc->mode.virtual_width ||
184*2a37e4c1Sshadow303 		v_display_start + vc->eff_height > vc->mode.virtual_height )
185*2a37e4c1Sshadow303 		return B_ERROR;
186*2a37e4c1Sshadow303 
187*2a37e4c1Sshadow303 	// this is needed both for get_mode_info and for scrolling of virtual screens
188*2a37e4c1Sshadow303 	vc->mode.h_display_start = h_display_start & ~7;
189*2a37e4c1Sshadow303 	vc->mode.v_display_start = v_display_start;
190*2a37e4c1Sshadow303 
191*2a37e4c1Sshadow303 	// do it
192*2a37e4c1Sshadow303 	moveOneDisplay( ai, &vc->heads[0] );
193*2a37e4c1Sshadow303 
194*2a37e4c1Sshadow303 	if( vc->independant_heads > 1 )
195*2a37e4c1Sshadow303 		moveOneDisplay( ai, &vc->heads[1] );
196*2a37e4c1Sshadow303 
197*2a37e4c1Sshadow303 	// overlay position must be adjusted
198*2a37e4c1Sshadow303 	Radeon_UpdateOverlay( ai );
199*2a37e4c1Sshadow303 
200*2a37e4c1Sshadow303 	return B_OK;
201*2a37e4c1Sshadow303 }
202*2a37e4c1Sshadow303 
203*2a37e4c1Sshadow303 // public function: pan display
204*2a37e4c1Sshadow303 status_t MOVE_DISPLAY( uint16 h_display_start, uint16 v_display_start )
205*2a37e4c1Sshadow303 {
206*2a37e4c1Sshadow303 	shared_info *si = ai->si;
207*2a37e4c1Sshadow303 	status_t result;
208*2a37e4c1Sshadow303 
209*2a37e4c1Sshadow303 	ACQUIRE_BEN( si->engine.lock );
210*2a37e4c1Sshadow303 
211*2a37e4c1Sshadow303 	// TBD: we should probably lock card first; in this case, we must
212*2a37e4c1Sshadow303 	//      split this function into locking and worker part, as this
213*2a37e4c1Sshadow303 	//      function is used internally as well
214*2a37e4c1Sshadow303 	result = Radeon_MoveDisplay( ai, h_display_start, v_display_start );
215*2a37e4c1Sshadow303 
216*2a37e4c1Sshadow303 	RELEASE_BEN( si->engine.lock );
217*2a37e4c1Sshadow303 
218*2a37e4c1Sshadow303 	return result;
219*2a37e4c1Sshadow303 }
220