xref: /haiku/src/add-ons/accelerants/radeon/dpms.c (revision 67bce78b48ed6d01b5a8eef89f5694c372b7e0a1)
1 /*
2 	Copyright (c) 2002, 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 "GlobalData.h"
15 
16 // these static functions are moved to end of file to
17 // make sure gcc doesn't inline them - we prefer size and not
18 // speed for this file
19 static status_t Radeon_SetDPMS_CRTC1( accelerator_info *di, int mode );
20 static status_t Radeon_SetDPMS_CRTC2( accelerator_info *di, int mode );
21 static uint32 Radeon_GetDPMS_CRTC1( accelerator_info *di );
22 static uint32 Radeon_GetDPMS_CRTC2( accelerator_info *di );
23 
24 status_t SET_DPMS_MODE(uint32 dpms_flags);
25 uint32 DPMS_CAPABILITIES(void);
26 uint32 DPMS_MODE(void);
27 
28 
29 // public function: set DPMS mode
30 status_t SET_DPMS_MODE(uint32 dpms_flags)
31 {
32 	virtual_card *vc = ai->vc;
33 	status_t result1, result2;
34 
35 	result1 = Radeon_SetDPMS( ai, &vc->ports[0], dpms_flags );
36 
37 	if( vc->independant_ports > 1 )
38 		result2 = Radeon_SetDPMS( ai, &vc->ports[1], dpms_flags );
39 	else
40 		result2 = B_OK;
41 
42 	if( result1 == B_OK && result2 == B_OK )
43 		return B_OK;
44 	else
45 		return B_ERROR;
46 }
47 
48 // public function: report DPMS capabilities
49 uint32 DPMS_CAPABILITIES(void)
50 {
51 	return 	B_DPMS_ON | B_DPMS_STAND_BY  | B_DPMS_SUSPEND | B_DPMS_OFF;
52 }
53 
54 
55 // public function: get current DPMS mode
56 uint32 DPMS_MODE(void)
57 {
58 	// we just ask the primary port what status it is in
59 	return Radeon_GetDPMS( ai, &ai->vc->ports[0] );
60 }
61 
62 
63 // set DPMS mode of one port
64 status_t Radeon_SetDPMS( accelerator_info *ai, virtual_port *port, int mode )
65 {
66 	// if we have a laptop panel
67 	// and we have a second screen connected
68 	// and they both show the same content,
69 	// then switch the laptop display always off
70 	if( ai->si->ports[port->physical_port].disp_type == dt_lvds &&
71 		ai->vc->independant_ports > 1 &&
72 		ai->vc->different_ports == 1 )
73 	{
74 		mode = B_DPMS_OFF;
75 	}
76 
77 	if( port->is_crtc2 )
78 		return Radeon_SetDPMS_CRTC2( ai, mode );
79 	else
80 		return Radeon_SetDPMS_CRTC1( ai, mode );
81 }
82 
83 
84 // get DPMS mode of one port
85 uint32 Radeon_GetDPMS( accelerator_info *ai, virtual_port *port )
86 {
87 	if( port->is_crtc2 )
88 		return Radeon_GetDPMS_CRTC2( ai );
89 	else
90 		return Radeon_GetDPMS_CRTC1( ai );
91 }
92 
93 
94 // set DPMS mode for first port
95 status_t Radeon_SetDPMS_CRTC1( accelerator_info *ai, int mode )
96 {
97 	vuint8 *regs = ai->regs;
98 	shared_info *si = ai->si;
99 
100 	int mask = RADEON_CRTC_DISPLAY_DIS
101 		| RADEON_CRTC_HSYNC_DIS
102 		| RADEON_CRTC_VSYNC_DIS;
103 
104 	switch( mode ) {
105 	case B_DPMS_ON:
106 		/* Screen: On; HSync: On, VSync: On */
107 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL, 0, ~mask );
108 		break;
109 	case B_DPMS_STAND_BY:
110 		/* Screen: Off; HSync: Off, VSync: On */
111 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL,
112 			RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_HSYNC_DIS, ~mask );
113 		break;
114 	case B_DPMS_SUSPEND:
115 		/* Screen: Off; HSync: On, VSync: Off */
116 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL,
117 			RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_VSYNC_DIS, ~mask );
118 		break;
119 	case B_DPMS_OFF:
120 		/* Screen: Off; HSync: Off, VSync: Off */
121 		OUTREGP( regs, RADEON_CRTC_EXT_CNTL, mask, ~mask );
122 		break;
123 	default:
124 		return B_BAD_VALUE;
125 	}
126 
127 	// if this is a flat panel, switch off backlight too
128 	if( si->ports[0].disp_type == dt_dvi_1 || si->ports[0].disp_type == dt_lvds ) {
129 		switch( mode ) {
130 		case B_DPMS_ON:
131 			// on my laptop, the display has problems to wake-up, this
132 			// should hopefully cure that
133 			// (you get a dark picture first that becomes brighter step by step,
134 			//  after a couple of seconds you have full brightness again)
135 			OUTREGP( regs, RADEON_LVDS_GEN_CNTL, RADEON_LVDS_BLON, ~RADEON_LVDS_BLON );
136 			//snooze( ai->si->fp_port.panel_pwr_delay * 1000 );
137 			OUTREGP( regs, RADEON_LVDS_GEN_CNTL, RADEON_LVDS_BLON | RADEON_LVDS_ON,
138 				~(RADEON_LVDS_DISPLAY_DIS | RADEON_LVDS_BLON | RADEON_LVDS_ON) );
139 			break;
140 		case B_DPMS_STAND_BY:
141 		case B_DPMS_SUSPEND:
142 		case B_DPMS_OFF:
143 			OUTREGP( regs, RADEON_LVDS_GEN_CNTL, RADEON_LVDS_DISPLAY_DIS,
144 				~(RADEON_LVDS_DISPLAY_DIS | RADEON_LVDS_BLON | RADEON_LVDS_ON) );
145 			break;
146 		}
147 	}
148 
149 	// it seems that DPMS doesn't work on DVI, so we disable FP completely
150 	// (according to specs this is the official way to handle DVI though DPMS
151 	// *should* be supported as well)
152 	if( si->ports[0].disp_type == dt_dvi_1 ) {
153 		switch( mode ) {
154 		case B_DPMS_ON:
155 			OUTREGP( regs, RADEON_FP_GEN_CNTL, RADEON_FP_FPON, ~RADEON_FP_FPON );
156 			break;
157 		case B_DPMS_STAND_BY:
158 		case B_DPMS_SUSPEND:
159 		case B_DPMS_OFF:
160 			OUTREGP( regs, RADEON_FP_GEN_CNTL, 0, ~RADEON_FP_FPON );
161 			break;
162 		}
163 	}
164 
165 	return B_OK;
166 }
167 
168 
169 // set DPMS mode of second port
170 status_t Radeon_SetDPMS_CRTC2( accelerator_info *di, int mode )
171 {
172 	vuint8 *regs = di->regs;
173 
174 	int mask = RADEON_CRTC2_DISP_DIS
175 		| RADEON_CRTC2_HSYNC_DIS
176 		| RADEON_CRTC2_VSYNC_DIS;
177 
178 	switch( mode ) {
179 	case B_DPMS_ON:
180 		/* Screen: On; HSync: On, VSync: On */
181 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, 0, ~mask );
182 		break;
183 	case B_DPMS_STAND_BY:
184 		/* Screen: Off; HSync: Off, VSync: On */
185 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL,
186 			RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_HSYNC_DIS, ~mask );
187 		break;
188 	case B_DPMS_SUSPEND:
189 		/* Screen: Off; HSync: On, VSync: Off */
190 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL,
191 			RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS, ~mask );
192 		break;
193 	case B_DPMS_OFF:
194 		/* Screen: Off; HSync: Off, VSync: Off */
195 		OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, mask, ~mask );
196 		break;
197 	default:
198 		return B_BAD_VALUE;
199 	}
200 
201 	return B_OK;
202 }
203 
204 
205 // get DPMS mode of first port
206 uint32 Radeon_GetDPMS_CRTC1( accelerator_info *di )
207 {
208 	uint32 tmp;
209 
210 	tmp = INREG( di->regs, RADEON_CRTC_EXT_CNTL );
211 
212 	if( (tmp & RADEON_CRTC_DISPLAY_DIS) == 0 )
213 		return B_DPMS_ON;
214 
215 	if( (tmp & RADEON_CRTC_VSYNC_DIS) == 0 )
216 		return B_DPMS_STAND_BY;
217 
218 	if( (tmp & RADEON_CRTC_HSYNC_DIS) == 0 )
219 		return B_DPMS_SUSPEND;
220 
221 	return B_DPMS_OFF;
222 }
223 
224 
225 // get DPMS mode of second port
226 uint32 Radeon_GetDPMS_CRTC2( accelerator_info *di )
227 {
228 	uint32 tmp;
229 
230 	tmp = INREG( di->regs, RADEON_CRTC2_GEN_CNTL );
231 
232 	if( (tmp & RADEON_CRTC2_DISP_DIS) == 0 )
233 		return B_DPMS_ON;
234 
235 	if( (tmp & RADEON_CRTC2_VSYNC_DIS) == 0 )
236 		return B_DPMS_STAND_BY;
237 
238 	if( (tmp & RADEON_CRTC2_HSYNC_DIS) == 0 )
239 		return B_DPMS_SUSPEND;
240 
241 	return B_DPMS_OFF;
242 }
243