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