xref: /haiku/src/add-ons/accelerants/radeon/dpms.c (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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->panel_pwr_delay * 1000 );
70 		OUTREGP( regs, RADEON_LVDS_GEN_CNTL, RADEON_LVDS_ON, ~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->is_igp )
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, 0,	~(RADEON_LVDS_BLON | RADEON_LVDS_ON) );
85 
86 		if( ai->si->is_mobility || ai->si->is_igp )
87 			Radeon_OUTPLL( ai->regs, ai->si->asic, RADEON_PIXCLKS_CNTL, old_pixclks_cntl );
88 
89 		break; }
90 	}
91 }
92 
93 
94 // set DPMS state of DVI port
95 static void Radeon_SetDPMS_DVI( accelerator_info *ai, int mode )
96 {
97 	vuint8 *regs = ai->regs;
98 
99 	// it seems that DPMS doesn't work on DVI, so we disable FP completely
100 	// (according to specs this is the official way to handle DVI though DPMS
101 	// *should* be supported as well)
102 	switch( mode ) {
103 	case B_DPMS_ON:
104 		OUTREGP( regs, RADEON_FP_GEN_CNTL, RADEON_FP_FPON | RADEON_FP_TMDS_EN,
105 			~(RADEON_FP_FPON | RADEON_FP_TMDS_EN));
106 		break;
107 	case B_DPMS_STAND_BY:
108 	case B_DPMS_SUSPEND:
109 	case B_DPMS_OFF:
110 		OUTREGP( regs, RADEON_FP_GEN_CNTL, 0, ~RADEON_FP_FPON | RADEON_FP_TMDS_EN );
111 		break;
112 	}
113 }
114 
115 
116 // set DPMS state of external DVI port
117 static void Radeon_SetDPMS_FP2( accelerator_info *ai, int mode )
118 {
119 	vuint8 *regs = ai->regs;
120 
121 	// it seems that DPMS doesn't work on DVI, so we disable FP completely
122 	// (according to specs this is the official way to handle DVI though DPMS
123 	// *should* be supported as well)
124 	switch( mode ) {
125 	case B_DPMS_ON:
126 		OUTREGP( regs, RADEON_FP2_GEN_CNTL, 0, ~RADEON_FP2_BLANK_EN);
127 		OUTREGP( regs, RADEON_FP2_GEN_CNTL, RADEON_FP2_FPON, ~RADEON_FP2_FPON);
128 		if (ai->si->asic >= rt_r200) {
129 			OUTREGP( regs, RADEON_FP2_GEN_CNTL, RADEON_FP2_DV0_EN, ~RADEON_FP2_DV0_EN);
130 		}
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, RADEON_FP2_BLANK_EN, ~RADEON_FP2_BLANK_EN );
136 		OUTREGP( regs, RADEON_FP2_GEN_CNTL, 0, ~RADEON_FP2_FPON);
137 		if (ai->si->asic >= rt_r200) {
138 			OUTREGP( regs, RADEON_FP2_GEN_CNTL, 0, ~RADEON_FP2_DV0_EN);
139 		}
140 		break;
141 	}
142 }
143 
144 
145 // set DPMS mode for CRT DAC.
146 // warning: the CRTC-DAC only obbeys this setting if
147 // connected to CRTC1, else it collides with TV-DAC
148 static void Radeon_SetDPMS_CRT( accelerator_info *ai, int mode )
149 {
150 	vuint8 *regs = ai->regs;
151 
152 	switch( mode ) {
153 	case B_DPMS_ON:
154 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL, 0, ~RADEON_CRTC_DISPLAY_DIS );
155 		break;
156 
157 	case B_DPMS_STAND_BY:
158 	case B_DPMS_SUSPEND:
159 	case B_DPMS_OFF:
160 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL,
161 			RADEON_CRTC_DISPLAY_DIS, ~RADEON_CRTC_DISPLAY_DIS );
162 		break;
163 	}
164 }
165 
166 
167 // set DPMS mode for TV-DAC in CRT mode
168 // warning: if the CRT-DAC is connected to CRTC2, it is
169 // affected by this setting too
170 static void Radeon_SetDPMS_TVCRT( accelerator_info *ai, int mode )
171 {
172 	vuint8 *regs = ai->regs;
173 
174 	switch( mode ) {
175 	case B_DPMS_ON:
176 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, 0,
177 			~(RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS | RADEON_CRTC2_HSYNC_DIS) );
178 		break;
179 	case B_DPMS_STAND_BY:
180 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, (RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_HSYNC_DIS),
181 			~(RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS | RADEON_CRTC2_HSYNC_DIS) );
182 		break;
183 	case B_DPMS_SUSPEND:
184 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, (RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS),
185 			~(RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS | RADEON_CRTC2_HSYNC_DIS) );
186 		break;
187 	case B_DPMS_OFF:
188 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, (RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS | RADEON_CRTC2_HSYNC_DIS),
189 			~(RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS | RADEON_CRTC2_HSYNC_DIS) );
190 		break;
191 	}
192 }
193 
194 
195 // set DPMS mode for first CRTC
196 static void Radeon_SetDPMS_CRTC1( accelerator_info *ai, int mode )
197 {
198 	vuint8 *regs = ai->regs;
199 
200 	uint32 mask = ~(RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_VSYNC_DIS | RADEON_CRTC_HSYNC_DIS);
201 
202 	switch( mode ) {
203 	case B_DPMS_ON:
204 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL, 0, mask );
205 		break;
206 	case B_DPMS_STAND_BY:
207 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL,
208 			(RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_HSYNC_DIS), mask );
209 		break;
210 	case B_DPMS_SUSPEND:
211 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL,
212 			(RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_VSYNC_DIS), mask );
213 		break;
214 	case B_DPMS_OFF:
215 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL,
216 			(RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_VSYNC_DIS | RADEON_CRTC_HSYNC_DIS), mask );
217 		break;
218 	}
219 
220 	// disable/enable memory requests and cursor
221 	switch( mode ) {
222 	case B_DPMS_ON:
223 		/* Screen: On; HSync: On, VSync: On */
224 		OUTREGP( regs, RADEON_CRTC_GEN_CNTL, 0, ~RADEON_CRTC_DISP_REQ_EN_B );
225 		Radeon_ShowCursor( ai, 0 );
226 		break;
227 	case B_DPMS_STAND_BY:
228 	case B_DPMS_SUSPEND:
229 	case B_DPMS_OFF:
230 		OUTREGP( regs, RADEON_CRTC_GEN_CNTL, RADEON_CRTC_DISP_REQ_EN_B,
231 			~(RADEON_CRTC_DISP_REQ_EN_B | RADEON_CRTC_CUR_EN) );
232 		break;
233 	}
234 }
235 
236 
237 // set DPMS mode of second CRTC
238 static void Radeon_SetDPMS_CRTC2( accelerator_info *ai, int mode )
239 {
240 	vuint8 *regs = ai->regs;
241 
242 	int mask = ~(RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS | RADEON_CRTC2_HSYNC_DIS);
243 
244 	switch( mode ) {
245 	case B_DPMS_ON:
246 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, 0, mask );
247 		break;
248 	case B_DPMS_STAND_BY:
249 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, (RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_HSYNC_DIS), mask );
250 		break;
251 	case B_DPMS_SUSPEND:
252 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, (RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS), mask);
253 		break;
254 	case B_DPMS_OFF:
255 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL,
256 			(RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS | RADEON_CRTC2_HSYNC_DIS), mask);
257 		break;
258 	}
259 
260 	switch( mode ) {
261 	case B_DPMS_ON:
262 		/* Screen: On; HSync: On, VSync: On */
263 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, 0, ~RADEON_CRTC2_DISP_REQ_EN_B );
264 		Radeon_ShowCursor( ai, 1 );
265 		break;
266 	case B_DPMS_STAND_BY:
267 	case B_DPMS_SUSPEND:
268 	case B_DPMS_OFF:
269 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_DISP_REQ_EN_B,
270 			~(RADEON_CRTC2_DISP_REQ_EN_B | RADEON_CRTC2_CUR_EN) );
271 		break;
272 	}
273 }
274 
275 
276 // set DPMS mode of TV-out
277 static void Radeon_SetDPMS_TVOUT( accelerator_info *ai, int mode )
278 {
279 	// we set to gain either to 0 for blank or 1 for normal operation
280 	if( IS_INTERNAL_TV_OUT( ai->si->tv_chip )) {
281 		OUTREG( ai->regs, RADEON_TV_LINEAR_GAIN_SETTINGS,
282 			mode == B_DPMS_ON ? 0x01000100 : 0 );
283 	} else {
284 		Radeon_VIPWrite( ai, ai->si->theatre_channel, RADEON_TV_LINEAR_GAIN_SETTINGS,
285 			mode == B_DPMS_ON ? 0x01000100 : 0 );
286 	}
287 }
288 
289 // set DPMS mode of one port
290 // engine lock is assumed to be hold
291 status_t Radeon_SetDPMS( accelerator_info *ai, int crtc_idx, int mode )
292 {
293 	crtc_info *crtc = &ai->si->crtc[crtc_idx];
294 
295 	// test validity of mode once and for all
296 	switch( mode ) {
297 	case B_DPMS_ON:
298 	case B_DPMS_STAND_BY:
299 	case B_DPMS_SUSPEND:
300 	case B_DPMS_OFF:
301 		break;
302 	default:
303 		return B_BAD_VALUE;
304 	}
305 
306 	if( crtc_idx == 0 )
307 		Radeon_SetDPMS_CRTC1( ai, mode );
308 	else
309 		Radeon_SetDPMS_CRTC2( ai, mode );
310 
311 	// possible ASIC bug: if CRT-DAC is connected to CRTC1, it obbeys
312 	// RADEON_CRTC_DISPLAY_DIS; if it is connected to CRTC2, to
313 	// RADEON_CRTC2_DISP_DIS - i.e. it follows the CRTC;
314 	// but the TV-DAC always listens to RADEON_CRTC2_DISP_DIS, independant
315 	// of the CRTC it gets its signal from;
316 	// this is a guarantee that two virtual cards will collide!
317 	if( crtc_idx == 0 || 1/* && (crtc->active_displays & dd_crt) != 0 */)
318 		Radeon_SetDPMS_CRT( ai, mode );
319 
320 	if( crtc_idx == 1 || (crtc->active_displays & (dd_tv_crt | dd_ctv | dd_stv)) != 0 )
321 		Radeon_SetDPMS_TVCRT( ai, mode );
322 
323 	// TV-Out ignores DPMS completely, including the blank-screen trick
324 	if( (crtc->active_displays & (dd_ctv | dd_stv)) != 0 )
325 		Radeon_SetDPMS_TVOUT( ai, mode );
326 
327 	if( (crtc->active_displays & dd_lvds) != 0 )
328 		Radeon_SetDPMS_LVDS( ai, mode );
329 
330 	if( (crtc->active_displays & dd_dvi) != 0 )
331 		Radeon_SetDPMS_DVI( ai, mode );
332 
333 	if( (crtc->active_displays & dd_dvi_ext) != 0 )
334 		Radeon_SetDPMS_FP2( ai, mode );
335 
336 	return B_OK;
337 }
338 
339 
340 // get DPMS mode of first port
341 static uint32 Radeon_GetDPMS_CRTC1( accelerator_info *di )
342 {
343 	uint32 tmp;
344 
345 	tmp = INREG( di->regs, RADEON_CRTC_EXT_CNTL );
346 
347 	if( (tmp & RADEON_CRTC_DISPLAY_DIS) == 0 )
348 		return B_DPMS_ON;
349 
350 	if( (tmp & RADEON_CRTC_VSYNC_DIS) == 0 )
351 		return B_DPMS_STAND_BY;
352 
353 	if( (tmp & RADEON_CRTC_HSYNC_DIS) == 0 )
354 		return B_DPMS_SUSPEND;
355 
356 	return B_DPMS_OFF;
357 }
358 
359 
360 // get DPMS mode of second port
361 static uint32 Radeon_GetDPMS_CRTC2( accelerator_info *di )
362 {
363 	uint32 tmp;
364 
365 	tmp = INREG( di->regs, RADEON_CRTC2_GEN_CNTL );
366 
367 	if( (tmp & RADEON_CRTC2_DISP_DIS) == 0 )
368 		return B_DPMS_ON;
369 
370 	if( (tmp & RADEON_CRTC2_VSYNC_DIS) == 0 )
371 		return B_DPMS_STAND_BY;
372 
373 	if( (tmp & RADEON_CRTC2_HSYNC_DIS) == 0 )
374 		return B_DPMS_SUSPEND;
375 
376 	return B_DPMS_OFF;
377 }
378 
379 
380 // get DPMS mode of one port
381 uint32 Radeon_GetDPMS( accelerator_info *ai, int crtc_idx )
382 {
383 	if( crtc_idx == 0 )
384 		return Radeon_GetDPMS_CRTC1( ai );
385 	else
386 		return Radeon_GetDPMS_CRTC2( ai );
387 }
388