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
23status_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
42uint32 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
49uint32 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
57static 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
95static 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
117static 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
148static 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
170static 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
196static 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
238static 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
277static 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
291status_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
341static 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
361static 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
381uint32 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