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