xref: /haiku/src/add-ons/accelerants/radeon/dpms.c (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 /*
2 	Copyright (c) 2002-2004, Thomas Kurschel
3 
4 
5 	Part of Radeon accelerant
6 
7 	Display Power Management (DPMS) support
8 */
9 
10 #include "radeon_accelerant.h"
11 #include "mmio.h"
12 #include "crtc_regs.h"
13 #include "fp_regs.h"
14 #include "pll_regs.h"
15 #include "pll_access.h"
16 #include "tv_out_regs.h"
17 #include "theatre_regs.h"
18 #include "GlobalData.h"
19 
20 
21 // public function: set DPMS mode
22 status_t SET_DPMS_MODE(uint32 dpms_flags)
23 {
24 	virtual_card *vc = ai->vc;
25 	status_t
26 		result1 = B_OK,
27 		result2 = B_OK;
28 
29 	if( vc->used_crtc[0] )
30 		result1 = Radeon_SetDPMS( ai, 0, dpms_flags );
31 	if( vc->used_crtc[0] )
32 		result1 = Radeon_SetDPMS( ai, 0, dpms_flags );
33 
34 	if( result1 == B_OK && result2 == B_OK )
35 		return B_OK;
36 	else
37 		return B_ERROR;
38 }
39 
40 // public function: report DPMS capabilities
41 uint32 DPMS_CAPABILITIES(void)
42 {
43 	return 	B_DPMS_ON | B_DPMS_STAND_BY  | B_DPMS_SUSPEND | B_DPMS_OFF;
44 }
45 
46 
47 // public function: get current DPMS mode
48 uint32 DPMS_MODE(void)
49 {
50 	// we just ask the primary virtual head what status it is in
51 	return Radeon_GetDPMS( ai, ai->vc->used_crtc[0] ? 0 : 1 );
52 }
53 
54 
55 // set DPMS state of LVDS port
56 static void Radeon_SetDPMS_LVDS( accelerator_info *ai, int mode )
57 {
58 	vuint8 *regs = ai->regs;
59 
60 	// for internal flat panel, switch backlight off too
61 	switch( mode ) {
62 	case B_DPMS_ON:
63 		// on my laptop, the display has problems to wake-up, this
64 		// should hopefully cure that
65 		// (you get a dark picture first that becomes brighter step by step,
66 		//  after a couple of seconds you have full brightness again)
67 		OUTREGP( regs, RADEON_LVDS_GEN_CNTL, RADEON_LVDS_BLON, ~RADEON_LVDS_BLON );
68 		//snooze( ai->si->fp_port.panel_pwr_delay * 1000 );
69 		OUTREGP( regs, RADEON_LVDS_GEN_CNTL, RADEON_LVDS_BLON | RADEON_LVDS_ON,
70 			~(RADEON_LVDS_DISPLAY_DIS | RADEON_LVDS_BLON | RADEON_LVDS_ON) );
71 		break;
72 
73 	case B_DPMS_STAND_BY:
74 	case B_DPMS_SUSPEND:
75 	case B_DPMS_OFF: {
76 		uint32 old_pixclks_cntl;
77 
78 		old_pixclks_cntl = Radeon_INPLL( ai->regs, ai->si->asic, RADEON_PIXCLKS_CNTL);
79 
80 		// ASIC bug: when LVDS_ON is reset, LVDS_ALWAYS_ON must be zero
81 		if( ai->si->is_mobility || ai->si->asic == rt_rs100 )
82 			Radeon_OUTPLLP( ai->regs, ai->si->asic, RADEON_PIXCLKS_CNTL, 0, ~RADEON_PIXCLK_LVDS_ALWAYS_ONb );
83 
84 		OUTREGP( regs, RADEON_LVDS_GEN_CNTL, RADEON_LVDS_DISPLAY_DIS,
85 			~(RADEON_LVDS_DISPLAY_DIS | RADEON_LVDS_BLON | RADEON_LVDS_ON) );
86 
87 		if( ai->si->is_mobility || ai->si->asic == rt_rs100 )
88 			Radeon_OUTPLL( ai->regs, ai->si->asic, RADEON_PIXCLKS_CNTL, old_pixclks_cntl );
89 
90 		break; }
91 	}
92 }
93 
94 
95 // set DPMS state of DVI port
96 static void Radeon_SetDPMS_DVI( accelerator_info *ai, int mode )
97 {
98 	vuint8 *regs = ai->regs;
99 
100 	// it seems that DPMS doesn't work on DVI, so we disable FP completely
101 	// (according to specs this is the official way to handle DVI though DPMS
102 	// *should* be supported as well)
103 	switch( mode ) {
104 	case B_DPMS_ON:
105 		OUTREGP( regs, RADEON_FP_GEN_CNTL, RADEON_FP_FPON | RADEON_FP_TMDS_EN,
106 			~(RADEON_FP_FPON | RADEON_FP_TMDS_EN));
107 		break;
108 	case B_DPMS_STAND_BY:
109 	case B_DPMS_SUSPEND:
110 	case B_DPMS_OFF:
111 		OUTREGP( regs, RADEON_FP_GEN_CNTL, 0, ~RADEON_FP_FPON | RADEON_FP_TMDS_EN );
112 		break;
113 	}
114 }
115 
116 
117 // set DPMS state of external DVI port
118 static void Radeon_SetDPMS_FP2( accelerator_info *ai, int mode )
119 {
120 	vuint8 *regs = ai->regs;
121 
122 	// it seems that DPMS doesn't work on DVI, so we disable FP completely
123 	// (according to specs this is the official way to handle DVI though DPMS
124 	// *should* be supported as well)
125 	switch( mode ) {
126 	case B_DPMS_ON:
127 		OUTREGP( regs, RADEON_FP2_GEN_CNTL,
128 			RADEON_FP_FPON |
129 			(ai->si->asic >= rt_r200 ? RADEON_FP2_DV0_EN : 0),
130 			~(RADEON_FP2_BLANK_EN | RADEON_FP2_BLANK_EN) );
131 		break;
132 	case B_DPMS_STAND_BY:
133 	case B_DPMS_SUSPEND:
134 	case B_DPMS_OFF:
135 		OUTREGP( regs, RADEON_FP2_GEN_CNTL, 0, ~(RADEON_FP2_BLANK_EN | RADEON_FP2_BLANK_EN) );
136 		break;
137 	}
138 }
139 
140 
141 // set DPMS mode for CRT DAC.
142 // warning: the CRTC-DAC only obbeys this setting if
143 // connected to CRTC1, else it collides with TV-DAC
144 static void Radeon_SetDPMS_CRT( accelerator_info *ai, int mode )
145 {
146 	vuint8 *regs = ai->regs;
147 
148 	switch( mode ) {
149 	case B_DPMS_ON:
150 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL, 0, ~RADEON_CRTC_DISPLAY_DIS );
151 		break;
152 
153 	case B_DPMS_STAND_BY:
154 	case B_DPMS_SUSPEND:
155 	case B_DPMS_OFF:
156 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL,
157 			RADEON_CRTC_DISPLAY_DIS, ~RADEON_CRTC_DISPLAY_DIS );
158 		break;
159 	}
160 }
161 
162 
163 // set DPMS mode for TV-DAC in CRT mode
164 // warning: if the CRT-DAC is connected to CRTC2, it is
165 // affected by this setting too
166 static void Radeon_SetDPMS_TVCRT( accelerator_info *ai, int mode )
167 {
168 	vuint8 *regs = ai->regs;
169 
170 	switch( mode ) {
171 	case B_DPMS_ON:
172 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, 0, ~RADEON_CRTC2_DISP_DIS );
173 		break;
174 
175 	case B_DPMS_STAND_BY:
176 	case B_DPMS_SUSPEND:
177 	case B_DPMS_OFF:
178 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL,
179 			RADEON_CRTC2_DISP_DIS, ~RADEON_CRTC2_DISP_DIS );
180 		break;
181 	}
182 }
183 
184 
185 // set DPMS mode for first CRTC
186 static void Radeon_SetDPMS_CRTC1( accelerator_info *ai, int mode )
187 {
188 	vuint8 *regs = ai->regs;
189 
190 	uint32 mask = RADEON_CRTC_HSYNC_DIS | RADEON_CRTC_VSYNC_DIS;
191 
192 	switch( mode ) {
193 	case B_DPMS_ON:
194 		/* Screen: On; HSync: On, VSync: On */
195 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL, 0, ~mask );
196 		break;
197 	case B_DPMS_STAND_BY:
198 		/* Screen: Off; HSync: Off, VSync: On */
199 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL,
200 			RADEON_CRTC_HSYNC_DIS, ~mask );
201 		break;
202 	case B_DPMS_SUSPEND:
203 		/* Screen: Off; HSync: On, VSync: Off */
204 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL,
205 			RADEON_CRTC_VSYNC_DIS, ~mask );
206 		break;
207 	case B_DPMS_OFF:
208 		/* Screen: Off; HSync: Off, VSync: Off */
209 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL, mask, ~mask );
210 		break;
211 	}
212 
213 	// disable/enable memory requests and cursor
214 	switch( mode ) {
215 	case B_DPMS_ON:
216 		/* Screen: On; HSync: On, VSync: On */
217 		OUTREGP( regs, RADEON_CRTC_GEN_CNTL, 0, ~RADEON_CRTC_DISP_REQ_EN_B );
218 		Radeon_ShowCursor( ai, 0 );
219 		break;
220 	case B_DPMS_STAND_BY:
221 	case B_DPMS_SUSPEND:
222 	case B_DPMS_OFF:
223 		OUTREGP( regs, RADEON_CRTC_GEN_CNTL, RADEON_CRTC_DISP_REQ_EN_B,
224 			~(RADEON_CRTC_DISP_REQ_EN_B | RADEON_CRTC_CUR_EN) );
225 		break;
226 	}
227 }
228 
229 
230 // set DPMS mode of second CRTC
231 static void Radeon_SetDPMS_CRTC2( accelerator_info *ai, int mode )
232 {
233 	vuint8 *regs = ai->regs;
234 
235 	int mask = RADEON_CRTC2_HSYNC_DIS | RADEON_CRTC2_VSYNC_DIS;
236 
237 	switch( mode ) {
238 	case B_DPMS_ON:
239 		/* Screen: On; HSync: On, VSync: On */
240 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, 0, ~mask );
241 		break;
242 	case B_DPMS_STAND_BY:
243 		/* Screen: Off; HSync: Off, VSync: On */
244 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL,
245 			RADEON_CRTC2_HSYNC_DIS, ~mask );
246 		break;
247 	case B_DPMS_SUSPEND:
248 		/* Screen: Off; HSync: On, VSync: Off */
249 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL,
250 			RADEON_CRTC2_VSYNC_DIS, ~mask );
251 		break;
252 	case B_DPMS_OFF:
253 		/* Screen: Off; HSync: Off, VSync: Off */
254 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, mask, ~mask );
255 		break;
256 	}
257 
258 	switch( mode ) {
259 	case B_DPMS_ON:
260 		/* Screen: On; HSync: On, VSync: On */
261 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, 0, ~RADEON_CRTC2_DISP_REQ_EN_B );
262 		Radeon_ShowCursor( ai, 1 );
263 		break;
264 	case B_DPMS_STAND_BY:
265 	case B_DPMS_SUSPEND:
266 	case B_DPMS_OFF:
267 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_DISP_REQ_EN_B,
268 			~(RADEON_CRTC2_DISP_REQ_EN_B | RADEON_CRTC2_CUR_EN) );
269 		break;
270 	}
271 }
272 
273 
274 // set DPMS mode of TV-out
275 static void Radeon_SetDPMS_TVOUT( accelerator_info *ai, int mode )
276 {
277 	// we set to gain either to 0 for blank or 1 for normal operation
278 	if( IS_INTERNAL_TV_OUT( ai->si->tv_chip )) {
279 		OUTREG( ai->regs, RADEON_TV_LINEAR_GAIN_SETTINGS,
280 			mode == B_DPMS_ON ? 0x01000100 : 0 );
281 	} else {
282 		Radeon_VIPWrite( ai, ai->si->theatre_channel, RADEON_TV_LINEAR_GAIN_SETTINGS,
283 			mode == B_DPMS_ON ? 0x01000100 : 0 );
284 	}
285 }
286 
287 // set DPMS mode of one port
288 // engine lock is assumed to be hold
289 status_t Radeon_SetDPMS( accelerator_info *ai, int crtc_idx, int mode )
290 {
291 	crtc_info *crtc = &ai->si->crtc[crtc_idx];
292 
293 	// test validity of mode once and for all
294 	switch( mode ) {
295 	case B_DPMS_ON:
296 	case B_DPMS_STAND_BY:
297 	case B_DPMS_SUSPEND:
298 	case B_DPMS_OFF:
299 		break;
300 	default:
301 		return B_BAD_VALUE;
302 	}
303 
304 	if( crtc_idx == 0 )
305 		Radeon_SetDPMS_CRTC1( ai, mode );
306 	else
307 		Radeon_SetDPMS_CRTC2( ai, mode );
308 
309 	// possible ASIC bug: if CRT-DAC is connected to CRTC1, it obbeys
310 	// RADEON_CRTC_DISPLAY_DIS; if it is connected to CRTC2, to
311 	// RADEON_CRTC2_DISP_DIS - i.e. it follows the CRTC;
312 	// but the TV-DAC always listens to RADEON_CRTC2_DISP_DIS, independant
313 	// of the CRTC it gets its signal from;
314 	// this is a guarantee that two virtual cards will collide!
315 	if( crtc_idx == 0 || 1/* && (crtc->active_displays & dd_crt) != 0 */)
316 		Radeon_SetDPMS_CRT( ai, mode );
317 
318 	if( crtc_idx == 1 || (crtc->active_displays & (dd_tv_crt | dd_ctv | dd_stv)) != 0 )
319 		Radeon_SetDPMS_TVCRT( ai, mode );
320 
321 	// TV-Out ignores DPMS completely, including the blank-screen trick
322 	if( (crtc->active_displays & (dd_ctv | dd_stv)) != 0 )
323 		Radeon_SetDPMS_TVOUT( ai, mode );
324 
325 	if( (crtc->active_displays & dd_lvds) != 0 )
326 		Radeon_SetDPMS_LVDS( ai, mode );
327 
328 	if( (crtc->active_displays & dd_dvi) != 0 )
329 		Radeon_SetDPMS_DVI( ai, mode );
330 
331 	if( (crtc->active_displays & dd_dvi_ext) != 0 )
332 		Radeon_SetDPMS_FP2( ai, mode );
333 
334 	return B_OK;
335 }
336 
337 
338 // get DPMS mode of first port
339 static uint32 Radeon_GetDPMS_CRTC1( accelerator_info *di )
340 {
341 	uint32 tmp;
342 
343 	tmp = INREG( di->regs, RADEON_CRTC_EXT_CNTL );
344 
345 	if( (tmp & RADEON_CRTC_DISPLAY_DIS) == 0 )
346 		return B_DPMS_ON;
347 
348 	if( (tmp & RADEON_CRTC_VSYNC_DIS) == 0 )
349 		return B_DPMS_STAND_BY;
350 
351 	if( (tmp & RADEON_CRTC_HSYNC_DIS) == 0 )
352 		return B_DPMS_SUSPEND;
353 
354 	return B_DPMS_OFF;
355 }
356 
357 
358 // get DPMS mode of second port
359 static uint32 Radeon_GetDPMS_CRTC2( accelerator_info *di )
360 {
361 	uint32 tmp;
362 
363 	tmp = INREG( di->regs, RADEON_CRTC2_GEN_CNTL );
364 
365 	if( (tmp & RADEON_CRTC2_DISP_DIS) == 0 )
366 		return B_DPMS_ON;
367 
368 	if( (tmp & RADEON_CRTC2_VSYNC_DIS) == 0 )
369 		return B_DPMS_STAND_BY;
370 
371 	if( (tmp & RADEON_CRTC2_HSYNC_DIS) == 0 )
372 		return B_DPMS_SUSPEND;
373 
374 	return B_DPMS_OFF;
375 }
376 
377 
378 // get DPMS mode of one port
379 uint32 Radeon_GetDPMS( accelerator_info *ai, int crtc_idx )
380 {
381 	if( crtc_idx == 0 )
382 		return Radeon_GetDPMS_CRTC1( ai );
383 	else
384 		return Radeon_GetDPMS_CRTC2( ai );
385 }
386