Commit | Line | Data |
---|---|---|
0867b421 | 1 | /************************************************************************** |
de64ac92 | 2 | * Copyright (c) 2009-2011, Intel Corporation. |
0867b421 | 3 | * All Rights Reserved. |
de64ac92 | 4 | * |
0867b421 AC |
5 | * Permission is hereby granted, free of charge, to any person obtaining a |
6 | * copy of this software and associated documentation files (the "Software"), | |
7 | * to deal in the Software without restriction, including without limitation | |
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
9 | * and/or sell copies of the Software, and to permit persons to whom the | |
10 | * Software is furnished to do so, subject to the following conditions: | |
11 | * | |
12 | * The above copyright notice and this permission notice (including the next | |
13 | * paragraph) shall be included in all copies or substantial portions of the | |
14 | * Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
22 | * SOFTWARE. | |
23 | * | |
24 | * Authors: | |
25 | * Benjamin Defnet <benjamin.r.defnet@intel.com> | |
26 | * Rajesh Poornachandran <rajesh.poornachandran@intel.com> | |
c3460fd3 AC |
27 | * Massively reworked |
28 | * Alan Cox <alan@linux.intel.com> | |
0867b421 AC |
29 | */ |
30 | #include "psb_powermgmt.h" | |
31 | #include "psb_drv.h" | |
c3460fd3 | 32 | #include "psb_reg.h" |
0867b421 | 33 | #include "psb_intel_reg.h" |
a897854c AC |
34 | #include "mdfld_output.h" |
35 | #include "mdfld_dsi_output.h" | |
0867b421 AC |
36 | #include <linux/mutex.h> |
37 | #include <linux/pm_runtime.h> | |
a897854c AC |
38 | #include <asm/intel_scu_ipc.h> |
39 | ||
40 | /* IPC message and command defines used to enable/disable mipi panel voltages */ | |
41 | #define IPC_MSG_PANEL_ON_OFF 0xE9 | |
42 | #define IPC_CMD_PANEL_ON 1 | |
43 | #define IPC_CMD_PANEL_OFF 0 | |
0867b421 | 44 | |
0867b421 | 45 | static struct mutex power_mutex; |
0867b421 | 46 | |
c3460fd3 AC |
47 | /** |
48 | * gma_power_init - initialise power manager | |
49 | * @dev: our device | |
0867b421 | 50 | * |
c3460fd3 | 51 | * Set up for power management tracking of our hardware. |
0867b421 | 52 | */ |
c3460fd3 | 53 | void gma_power_init(struct drm_device *dev) |
0867b421 | 54 | { |
c3460fd3 | 55 | struct drm_psb_private *dev_priv = dev->dev_private; |
0867b421 | 56 | |
a897854c AC |
57 | /* FIXME: need to sort out fetching apm_reg for both platforms ?? */ |
58 | ||
0867b421 AC |
59 | dev_priv->apm_base = dev_priv->apm_reg & 0xffff; |
60 | dev_priv->ospm_base &= 0xffff; | |
61 | ||
c3460fd3 AC |
62 | dev_priv->display_power = true; /* We start active */ |
63 | dev_priv->display_count = 0; /* Currently no users */ | |
64 | dev_priv->suspended = false; /* And not suspended */ | |
0867b421 | 65 | mutex_init(&power_mutex); |
c3460fd3 | 66 | |
bcc70a64 | 67 | if (!IS_MRST(dev) && !IS_MFLD(dev)) { |
c3460fd3 AC |
68 | /* FIXME: wants further review */ |
69 | u32 gating = PSB_RSGX32(PSB_CR_CLKGATECTL); | |
70 | /* Disable 2D clock gating */ | |
71 | gating &= ~3; | |
72 | gating |= 1; | |
73 | PSB_WSGX32(gating, PSB_CR_CLKGATECTL); | |
74 | PSB_RSGX32(PSB_CR_CLKGATECTL); | |
75 | } | |
0867b421 AC |
76 | } |
77 | ||
c3460fd3 AC |
78 | /** |
79 | * gma_power_uninit - end power manager | |
80 | * @dev: device to end for | |
0867b421 | 81 | * |
c3460fd3 | 82 | * Undo the effects of gma_power_init |
0867b421 | 83 | */ |
c3460fd3 | 84 | void gma_power_uninit(struct drm_device *dev) |
0867b421 AC |
85 | { |
86 | mutex_destroy(&power_mutex); | |
c3460fd3 AC |
87 | pm_runtime_disable(&dev->pdev->dev); |
88 | pm_runtime_set_suspended(&dev->pdev->dev); | |
0867b421 AC |
89 | } |
90 | ||
91 | ||
c3460fd3 AC |
92 | /** |
93 | * save_display_registers - save registers lost on suspend | |
94 | * @dev: our DRM device | |
0867b421 | 95 | * |
c3460fd3 AC |
96 | * Save the state we need in order to be able to restore the interface |
97 | * upon resume from suspend | |
0867b421 AC |
98 | */ |
99 | static int save_display_registers(struct drm_device *dev) | |
100 | { | |
101 | struct drm_psb_private *dev_priv = dev->dev_private; | |
c3460fd3 AC |
102 | struct drm_crtc *crtc; |
103 | struct drm_connector *connector; | |
0867b421 AC |
104 | |
105 | /* Display arbitration control + watermarks */ | |
106 | dev_priv->saveDSPARB = PSB_RVDC32(DSPARB); | |
107 | dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1); | |
108 | dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2); | |
109 | dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3); | |
110 | dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4); | |
111 | dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5); | |
112 | dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6); | |
113 | dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); | |
114 | ||
c3460fd3 | 115 | /* Save crtc and output state */ |
0867b421 AC |
116 | mutex_lock(&dev->mode_config.mutex); |
117 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | |
c3460fd3 | 118 | if (drm_helper_crtc_in_use(crtc)) |
0867b421 | 119 | crtc->funcs->save(crtc); |
0867b421 | 120 | } |
5352161f | 121 | |
c3460fd3 | 122 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
0867b421 | 123 | connector->funcs->save(connector); |
0867b421 | 124 | |
c3460fd3 | 125 | mutex_unlock(&dev->mode_config.mutex); |
0867b421 AC |
126 | return 0; |
127 | } | |
128 | ||
c3460fd3 AC |
129 | /** |
130 | * restore_display_registers - restore lost register state | |
131 | * @dev: our DRM device | |
0867b421 | 132 | * |
c3460fd3 | 133 | * Restore register state that was lost during suspend and resume. |
0867b421 AC |
134 | */ |
135 | static int restore_display_registers(struct drm_device *dev) | |
136 | { | |
137 | struct drm_psb_private *dev_priv = dev->dev_private; | |
c3460fd3 AC |
138 | struct drm_crtc *crtc; |
139 | struct drm_connector *connector; | |
0867b421 AC |
140 | |
141 | /* Display arbitration + watermarks */ | |
142 | PSB_WVDC32(dev_priv->saveDSPARB, DSPARB); | |
143 | PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1); | |
144 | PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2); | |
145 | PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3); | |
146 | PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4); | |
147 | PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5); | |
148 | PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6); | |
149 | PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT); | |
150 | ||
151 | /*make sure VGA plane is off. it initializes to on after reset!*/ | |
152 | PSB_WVDC32(0x80000000, VGACNTRL); | |
153 | ||
154 | mutex_lock(&dev->mode_config.mutex); | |
c3460fd3 AC |
155 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
156 | if (drm_helper_crtc_in_use(crtc)) | |
0867b421 | 157 | crtc->funcs->restore(crtc); |
0867b421 | 158 | |
c3460fd3 AC |
159 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
160 | connector->funcs->restore(connector); | |
0867b421 | 161 | |
c3460fd3 | 162 | mutex_unlock(&dev->mode_config.mutex); |
0867b421 AC |
163 | return 0; |
164 | } | |
c3460fd3 | 165 | |
a897854c AC |
166 | /** |
167 | * mdfld_save_display_registers - save registers for pipe | |
168 | * @dev: our device | |
169 | * @pipe: pipe to save | |
170 | * | |
171 | * Save the pipe state of the device before we power it off. Keep everything | |
172 | * we need to put it back again | |
173 | */ | |
174 | static int mdfld_save_display_registers(struct drm_device *dev, int pipe) | |
175 | { | |
176 | struct drm_psb_private *dev_priv = dev->dev_private; | |
177 | int i; | |
178 | ||
179 | /* register */ | |
180 | u32 dpll_reg = MRST_DPLL_A; | |
181 | u32 fp_reg = MRST_FPA0; | |
182 | u32 pipeconf_reg = PIPEACONF; | |
183 | u32 htot_reg = HTOTAL_A; | |
184 | u32 hblank_reg = HBLANK_A; | |
185 | u32 hsync_reg = HSYNC_A; | |
186 | u32 vtot_reg = VTOTAL_A; | |
187 | u32 vblank_reg = VBLANK_A; | |
188 | u32 vsync_reg = VSYNC_A; | |
189 | u32 pipesrc_reg = PIPEASRC; | |
190 | u32 dspstride_reg = DSPASTRIDE; | |
191 | u32 dsplinoff_reg = DSPALINOFF; | |
192 | u32 dsptileoff_reg = DSPATILEOFF; | |
193 | u32 dspsize_reg = DSPASIZE; | |
194 | u32 dsppos_reg = DSPAPOS; | |
195 | u32 dspsurf_reg = DSPASURF; | |
196 | u32 mipi_reg = MIPI; | |
197 | u32 dspcntr_reg = DSPACNTR; | |
198 | u32 dspstatus_reg = PIPEASTAT; | |
199 | u32 palette_reg = PALETTE_A; | |
200 | ||
201 | /* pointer to values */ | |
202 | u32 *dpll_val = &dev_priv->saveDPLL_A; | |
203 | u32 *fp_val = &dev_priv->saveFPA0; | |
204 | u32 *pipeconf_val = &dev_priv->savePIPEACONF; | |
205 | u32 *htot_val = &dev_priv->saveHTOTAL_A; | |
206 | u32 *hblank_val = &dev_priv->saveHBLANK_A; | |
207 | u32 *hsync_val = &dev_priv->saveHSYNC_A; | |
208 | u32 *vtot_val = &dev_priv->saveVTOTAL_A; | |
209 | u32 *vblank_val = &dev_priv->saveVBLANK_A; | |
210 | u32 *vsync_val = &dev_priv->saveVSYNC_A; | |
211 | u32 *pipesrc_val = &dev_priv->savePIPEASRC; | |
212 | u32 *dspstride_val = &dev_priv->saveDSPASTRIDE; | |
213 | u32 *dsplinoff_val = &dev_priv->saveDSPALINOFF; | |
214 | u32 *dsptileoff_val = &dev_priv->saveDSPATILEOFF; | |
215 | u32 *dspsize_val = &dev_priv->saveDSPASIZE; | |
216 | u32 *dsppos_val = &dev_priv->saveDSPAPOS; | |
217 | u32 *dspsurf_val = &dev_priv->saveDSPASURF; | |
218 | u32 *mipi_val = &dev_priv->saveMIPI; | |
219 | u32 *dspcntr_val = &dev_priv->saveDSPACNTR; | |
220 | u32 *dspstatus_val = &dev_priv->saveDSPASTATUS; | |
221 | u32 *palette_val = dev_priv->save_palette_a; | |
222 | ||
223 | switch (pipe) { | |
224 | case 0: | |
225 | break; | |
226 | case 1: | |
227 | /* register */ | |
228 | dpll_reg = MDFLD_DPLL_B; | |
229 | fp_reg = MDFLD_DPLL_DIV0; | |
230 | pipeconf_reg = PIPEBCONF; | |
231 | htot_reg = HTOTAL_B; | |
232 | hblank_reg = HBLANK_B; | |
233 | hsync_reg = HSYNC_B; | |
234 | vtot_reg = VTOTAL_B; | |
235 | vblank_reg = VBLANK_B; | |
236 | vsync_reg = VSYNC_B; | |
237 | pipesrc_reg = PIPEBSRC; | |
238 | dspstride_reg = DSPBSTRIDE; | |
239 | dsplinoff_reg = DSPBLINOFF; | |
240 | dsptileoff_reg = DSPBTILEOFF; | |
241 | dspsize_reg = DSPBSIZE; | |
242 | dsppos_reg = DSPBPOS; | |
243 | dspsurf_reg = DSPBSURF; | |
244 | dspcntr_reg = DSPBCNTR; | |
245 | dspstatus_reg = PIPEBSTAT; | |
246 | palette_reg = PALETTE_B; | |
247 | ||
248 | /* values */ | |
249 | dpll_val = &dev_priv->saveDPLL_B; | |
250 | fp_val = &dev_priv->saveFPB0; | |
251 | pipeconf_val = &dev_priv->savePIPEBCONF; | |
252 | htot_val = &dev_priv->saveHTOTAL_B; | |
253 | hblank_val = &dev_priv->saveHBLANK_B; | |
254 | hsync_val = &dev_priv->saveHSYNC_B; | |
255 | vtot_val = &dev_priv->saveVTOTAL_B; | |
256 | vblank_val = &dev_priv->saveVBLANK_B; | |
257 | vsync_val = &dev_priv->saveVSYNC_B; | |
258 | pipesrc_val = &dev_priv->savePIPEBSRC; | |
259 | dspstride_val = &dev_priv->saveDSPBSTRIDE; | |
260 | dsplinoff_val = &dev_priv->saveDSPBLINOFF; | |
261 | dsptileoff_val = &dev_priv->saveDSPBTILEOFF; | |
262 | dspsize_val = &dev_priv->saveDSPBSIZE; | |
263 | dsppos_val = &dev_priv->saveDSPBPOS; | |
264 | dspsurf_val = &dev_priv->saveDSPBSURF; | |
265 | dspcntr_val = &dev_priv->saveDSPBCNTR; | |
266 | dspstatus_val = &dev_priv->saveDSPBSTATUS; | |
267 | palette_val = dev_priv->save_palette_b; | |
268 | break; | |
269 | case 2: | |
270 | /* register */ | |
271 | pipeconf_reg = PIPECCONF; | |
272 | htot_reg = HTOTAL_C; | |
273 | hblank_reg = HBLANK_C; | |
274 | hsync_reg = HSYNC_C; | |
275 | vtot_reg = VTOTAL_C; | |
276 | vblank_reg = VBLANK_C; | |
277 | vsync_reg = VSYNC_C; | |
278 | pipesrc_reg = PIPECSRC; | |
279 | dspstride_reg = DSPCSTRIDE; | |
280 | dsplinoff_reg = DSPCLINOFF; | |
281 | dsptileoff_reg = DSPCTILEOFF; | |
282 | dspsize_reg = DSPCSIZE; | |
283 | dsppos_reg = DSPCPOS; | |
284 | dspsurf_reg = DSPCSURF; | |
285 | mipi_reg = MIPI_C; | |
286 | dspcntr_reg = DSPCCNTR; | |
287 | dspstatus_reg = PIPECSTAT; | |
288 | palette_reg = PALETTE_C; | |
289 | ||
290 | /* pointer to values */ | |
291 | pipeconf_val = &dev_priv->savePIPECCONF; | |
292 | htot_val = &dev_priv->saveHTOTAL_C; | |
293 | hblank_val = &dev_priv->saveHBLANK_C; | |
294 | hsync_val = &dev_priv->saveHSYNC_C; | |
295 | vtot_val = &dev_priv->saveVTOTAL_C; | |
296 | vblank_val = &dev_priv->saveVBLANK_C; | |
297 | vsync_val = &dev_priv->saveVSYNC_C; | |
298 | pipesrc_val = &dev_priv->savePIPECSRC; | |
299 | dspstride_val = &dev_priv->saveDSPCSTRIDE; | |
300 | dsplinoff_val = &dev_priv->saveDSPCLINOFF; | |
301 | dsptileoff_val = &dev_priv->saveDSPCTILEOFF; | |
302 | dspsize_val = &dev_priv->saveDSPCSIZE; | |
303 | dsppos_val = &dev_priv->saveDSPCPOS; | |
304 | dspsurf_val = &dev_priv->saveDSPCSURF; | |
305 | mipi_val = &dev_priv->saveMIPI_C; | |
306 | dspcntr_val = &dev_priv->saveDSPCCNTR; | |
307 | dspstatus_val = &dev_priv->saveDSPCSTATUS; | |
308 | palette_val = dev_priv->save_palette_c; | |
309 | break; | |
310 | default: | |
311 | DRM_ERROR("%s, invalid pipe number.\n", __func__); | |
312 | return -EINVAL; | |
313 | } | |
314 | ||
315 | /* Pipe & plane A info */ | |
316 | *dpll_val = PSB_RVDC32(dpll_reg); | |
317 | *fp_val = PSB_RVDC32(fp_reg); | |
318 | *pipeconf_val = PSB_RVDC32(pipeconf_reg); | |
319 | *htot_val = PSB_RVDC32(htot_reg); | |
320 | *hblank_val = PSB_RVDC32(hblank_reg); | |
321 | *hsync_val = PSB_RVDC32(hsync_reg); | |
322 | *vtot_val = PSB_RVDC32(vtot_reg); | |
323 | *vblank_val = PSB_RVDC32(vblank_reg); | |
324 | *vsync_val = PSB_RVDC32(vsync_reg); | |
325 | *pipesrc_val = PSB_RVDC32(pipesrc_reg); | |
326 | *dspstride_val = PSB_RVDC32(dspstride_reg); | |
327 | *dsplinoff_val = PSB_RVDC32(dsplinoff_reg); | |
328 | *dsptileoff_val = PSB_RVDC32(dsptileoff_reg); | |
329 | *dspsize_val = PSB_RVDC32(dspsize_reg); | |
330 | *dsppos_val = PSB_RVDC32(dsppos_reg); | |
331 | *dspsurf_val = PSB_RVDC32(dspsurf_reg); | |
332 | *dspcntr_val = PSB_RVDC32(dspcntr_reg); | |
333 | *dspstatus_val = PSB_RVDC32(dspstatus_reg); | |
334 | ||
335 | /*save palette (gamma) */ | |
336 | for (i = 0; i < 256; i++) | |
337 | palette_val[i] = PSB_RVDC32(palette_reg + (i<<2)); | |
338 | ||
339 | if (pipe == 1) { | |
340 | dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); | |
341 | dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); | |
342 | dev_priv->saveHDMIPHYMISCCTL = PSB_RVDC32(HDMIPHYMISCCTL); | |
343 | dev_priv->saveHDMIB_CONTROL = PSB_RVDC32(HDMIB_CONTROL); | |
344 | return 0; | |
345 | } | |
346 | *mipi_val = PSB_RVDC32(mipi_reg); | |
347 | return 0; | |
348 | } | |
349 | ||
350 | /** | |
351 | * mdfld_save_cursor_overlay_registers - save cursor overlay info | |
352 | * @dev: our device | |
353 | * | |
354 | * Save the cursor and overlay register state | |
355 | */ | |
356 | static int mdfld_save_cursor_overlay_registers(struct drm_device *dev) | |
357 | { | |
358 | struct drm_psb_private *dev_priv = dev->dev_private; | |
359 | ||
360 | /* Save cursor regs */ | |
361 | dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); | |
362 | dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); | |
363 | dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); | |
364 | ||
365 | dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR); | |
366 | dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE); | |
367 | dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS); | |
368 | ||
369 | dev_priv->saveDSPCCURSOR_CTRL = PSB_RVDC32(CURCCNTR); | |
370 | dev_priv->saveDSPCCURSOR_BASE = PSB_RVDC32(CURCBASE); | |
371 | dev_priv->saveDSPCCURSOR_POS = PSB_RVDC32(CURCPOS); | |
372 | ||
373 | /* HW overlay */ | |
374 | dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD); | |
375 | dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); | |
376 | dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); | |
377 | dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); | |
378 | dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); | |
379 | dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); | |
380 | dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); | |
381 | ||
382 | dev_priv->saveOV_OVADD_C = PSB_RVDC32(OV_OVADD + OV_C_OFFSET); | |
383 | dev_priv->saveOV_OGAMC0_C = PSB_RVDC32(OV_OGAMC0 + OV_C_OFFSET); | |
384 | dev_priv->saveOV_OGAMC1_C = PSB_RVDC32(OV_OGAMC1 + OV_C_OFFSET); | |
385 | dev_priv->saveOV_OGAMC2_C = PSB_RVDC32(OV_OGAMC2 + OV_C_OFFSET); | |
386 | dev_priv->saveOV_OGAMC3_C = PSB_RVDC32(OV_OGAMC3 + OV_C_OFFSET); | |
387 | dev_priv->saveOV_OGAMC4_C = PSB_RVDC32(OV_OGAMC4 + OV_C_OFFSET); | |
388 | dev_priv->saveOV_OGAMC5_C = PSB_RVDC32(OV_OGAMC5 + OV_C_OFFSET); | |
389 | ||
390 | return 0; | |
391 | } | |
392 | /* | |
393 | * mdfld_restore_display_registers - restore the state of a pipe | |
394 | * @dev: our device | |
395 | * @pipe: the pipe to restore | |
396 | * | |
397 | * Restore the state of a pipe to that which was saved by the register save | |
398 | * functions. | |
399 | */ | |
400 | static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) | |
401 | { | |
402 | /* To get panel out of ULPS mode */ | |
403 | struct drm_psb_private *dev_priv = dev->dev_private; | |
404 | struct mdfld_dsi_config *dsi_config = NULL; | |
405 | u32 i = 0; | |
406 | u32 dpll = 0; | |
407 | u32 timeout = 0; | |
408 | u32 reg_offset = 0; | |
409 | ||
410 | /* register */ | |
411 | u32 dpll_reg = MRST_DPLL_A; | |
412 | u32 fp_reg = MRST_FPA0; | |
413 | u32 pipeconf_reg = PIPEACONF; | |
414 | u32 htot_reg = HTOTAL_A; | |
415 | u32 hblank_reg = HBLANK_A; | |
416 | u32 hsync_reg = HSYNC_A; | |
417 | u32 vtot_reg = VTOTAL_A; | |
418 | u32 vblank_reg = VBLANK_A; | |
419 | u32 vsync_reg = VSYNC_A; | |
420 | u32 pipesrc_reg = PIPEASRC; | |
421 | u32 dspstride_reg = DSPASTRIDE; | |
422 | u32 dsplinoff_reg = DSPALINOFF; | |
423 | u32 dsptileoff_reg = DSPATILEOFF; | |
424 | u32 dspsize_reg = DSPASIZE; | |
425 | u32 dsppos_reg = DSPAPOS; | |
426 | u32 dspsurf_reg = DSPASURF; | |
427 | u32 dspstatus_reg = PIPEASTAT; | |
428 | u32 mipi_reg = MIPI; | |
429 | u32 dspcntr_reg = DSPACNTR; | |
430 | u32 palette_reg = PALETTE_A; | |
431 | ||
432 | /* values */ | |
433 | u32 dpll_val = dev_priv->saveDPLL_A & ~DPLL_VCO_ENABLE; | |
434 | u32 fp_val = dev_priv->saveFPA0; | |
435 | u32 pipeconf_val = dev_priv->savePIPEACONF; | |
436 | u32 htot_val = dev_priv->saveHTOTAL_A; | |
437 | u32 hblank_val = dev_priv->saveHBLANK_A; | |
438 | u32 hsync_val = dev_priv->saveHSYNC_A; | |
439 | u32 vtot_val = dev_priv->saveVTOTAL_A; | |
440 | u32 vblank_val = dev_priv->saveVBLANK_A; | |
441 | u32 vsync_val = dev_priv->saveVSYNC_A; | |
442 | u32 pipesrc_val = dev_priv->savePIPEASRC; | |
443 | u32 dspstride_val = dev_priv->saveDSPASTRIDE; | |
444 | u32 dsplinoff_val = dev_priv->saveDSPALINOFF; | |
445 | u32 dsptileoff_val = dev_priv->saveDSPATILEOFF; | |
446 | u32 dspsize_val = dev_priv->saveDSPASIZE; | |
447 | u32 dsppos_val = dev_priv->saveDSPAPOS; | |
448 | u32 dspsurf_val = dev_priv->saveDSPASURF; | |
449 | u32 dspstatus_val = dev_priv->saveDSPASTATUS; | |
450 | u32 mipi_val = dev_priv->saveMIPI; | |
451 | u32 dspcntr_val = dev_priv->saveDSPACNTR; | |
452 | u32 *palette_val = dev_priv->save_palette_a; | |
453 | ||
454 | switch (pipe) { | |
455 | case 0: | |
456 | dsi_config = dev_priv->dsi_configs[0]; | |
457 | break; | |
458 | case 1: | |
459 | /* register */ | |
460 | dpll_reg = MDFLD_DPLL_B; | |
461 | fp_reg = MDFLD_DPLL_DIV0; | |
462 | pipeconf_reg = PIPEBCONF; | |
463 | htot_reg = HTOTAL_B; | |
464 | hblank_reg = HBLANK_B; | |
465 | hsync_reg = HSYNC_B; | |
466 | vtot_reg = VTOTAL_B; | |
467 | vblank_reg = VBLANK_B; | |
468 | vsync_reg = VSYNC_B; | |
469 | pipesrc_reg = PIPEBSRC; | |
470 | dspstride_reg = DSPBSTRIDE; | |
471 | dsplinoff_reg = DSPBLINOFF; | |
472 | dsptileoff_reg = DSPBTILEOFF; | |
473 | dspsize_reg = DSPBSIZE; | |
474 | dsppos_reg = DSPBPOS; | |
475 | dspsurf_reg = DSPBSURF; | |
476 | dspcntr_reg = DSPBCNTR; | |
477 | palette_reg = PALETTE_B; | |
478 | dspstatus_reg = PIPEBSTAT; | |
479 | ||
480 | /* values */ | |
481 | dpll_val = dev_priv->saveDPLL_B & ~DPLL_VCO_ENABLE; | |
482 | fp_val = dev_priv->saveFPB0; | |
483 | pipeconf_val = dev_priv->savePIPEBCONF; | |
484 | htot_val = dev_priv->saveHTOTAL_B; | |
485 | hblank_val = dev_priv->saveHBLANK_B; | |
486 | hsync_val = dev_priv->saveHSYNC_B; | |
487 | vtot_val = dev_priv->saveVTOTAL_B; | |
488 | vblank_val = dev_priv->saveVBLANK_B; | |
489 | vsync_val = dev_priv->saveVSYNC_B; | |
490 | pipesrc_val = dev_priv->savePIPEBSRC; | |
491 | dspstride_val = dev_priv->saveDSPBSTRIDE; | |
492 | dsplinoff_val = dev_priv->saveDSPBLINOFF; | |
493 | dsptileoff_val = dev_priv->saveDSPBTILEOFF; | |
494 | dspsize_val = dev_priv->saveDSPBSIZE; | |
495 | dsppos_val = dev_priv->saveDSPBPOS; | |
496 | dspsurf_val = dev_priv->saveDSPBSURF; | |
497 | dspcntr_val = dev_priv->saveDSPBCNTR; | |
498 | dspstatus_val = dev_priv->saveDSPBSTATUS; | |
499 | palette_val = dev_priv->save_palette_b; | |
500 | break; | |
501 | case 2: | |
502 | reg_offset = MIPIC_REG_OFFSET; | |
503 | ||
504 | /* register */ | |
505 | pipeconf_reg = PIPECCONF; | |
506 | htot_reg = HTOTAL_C; | |
507 | hblank_reg = HBLANK_C; | |
508 | hsync_reg = HSYNC_C; | |
509 | vtot_reg = VTOTAL_C; | |
510 | vblank_reg = VBLANK_C; | |
511 | vsync_reg = VSYNC_C; | |
512 | pipesrc_reg = PIPECSRC; | |
513 | dspstride_reg = DSPCSTRIDE; | |
514 | dsplinoff_reg = DSPCLINOFF; | |
515 | dsptileoff_reg = DSPCTILEOFF; | |
516 | dspsize_reg = DSPCSIZE; | |
517 | dsppos_reg = DSPCPOS; | |
518 | dspsurf_reg = DSPCSURF; | |
519 | mipi_reg = MIPI_C; | |
520 | dspcntr_reg = DSPCCNTR; | |
521 | palette_reg = PALETTE_C; | |
522 | dspstatus_reg = PIPECSTAT; | |
523 | ||
524 | /* values */ | |
525 | pipeconf_val = dev_priv->savePIPECCONF; | |
526 | htot_val = dev_priv->saveHTOTAL_C; | |
527 | hblank_val = dev_priv->saveHBLANK_C; | |
528 | hsync_val = dev_priv->saveHSYNC_C; | |
529 | vtot_val = dev_priv->saveVTOTAL_C; | |
530 | vblank_val = dev_priv->saveVBLANK_C; | |
531 | vsync_val = dev_priv->saveVSYNC_C; | |
532 | pipesrc_val = dev_priv->savePIPECSRC; | |
533 | dspstride_val = dev_priv->saveDSPCSTRIDE; | |
534 | dsplinoff_val = dev_priv->saveDSPCLINOFF; | |
535 | dsptileoff_val = dev_priv->saveDSPCTILEOFF; | |
536 | dspsize_val = dev_priv->saveDSPCSIZE; | |
537 | dsppos_val = dev_priv->saveDSPCPOS; | |
538 | dspsurf_val = dev_priv->saveDSPCSURF; | |
539 | dspstatus_val = dev_priv->saveDSPCSTATUS; | |
540 | mipi_val = dev_priv->saveMIPI_C; | |
541 | dspcntr_val = dev_priv->saveDSPCCNTR; | |
542 | palette_val = dev_priv->save_palette_c; | |
543 | ||
544 | dsi_config = dev_priv->dsi_configs[1]; | |
545 | break; | |
546 | default: | |
547 | DRM_ERROR("%s, invalid pipe number.\n", __func__); | |
548 | return -EINVAL; | |
549 | } | |
550 | ||
551 | /* Make sure VGA plane is off. it initializes to on after reset!*/ | |
552 | PSB_WVDC32(0x80000000, VGACNTRL); | |
553 | if (pipe == 1) { | |
554 | PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, dpll_reg); | |
555 | PSB_RVDC32(dpll_reg); | |
556 | ||
557 | PSB_WVDC32(fp_val, fp_reg); | |
558 | } else { | |
559 | dpll = PSB_RVDC32(dpll_reg); | |
560 | ||
561 | if (!(dpll & DPLL_VCO_ENABLE)) { | |
562 | ||
563 | /* When ungating power of DPLL, needs to wait 0.5us before enable the VCO */ | |
564 | if (dpll & MDFLD_PWR_GATE_EN) { | |
565 | dpll &= ~MDFLD_PWR_GATE_EN; | |
566 | PSB_WVDC32(dpll, dpll_reg); | |
567 | udelay(500); /* FIXME: 1 ? */ | |
568 | } | |
569 | ||
570 | PSB_WVDC32(fp_val, fp_reg); | |
571 | PSB_WVDC32(dpll_val, dpll_reg); | |
572 | /* FIXME_MDFLD PO - change 500 to 1 after PO */ | |
573 | udelay(500); | |
574 | ||
575 | dpll_val |= DPLL_VCO_ENABLE; | |
576 | PSB_WVDC32(dpll_val, dpll_reg); | |
577 | PSB_RVDC32(dpll_reg); | |
578 | ||
579 | /* wait for DSI PLL to lock */ | |
580 | while ((timeout < 20000) && !(PSB_RVDC32(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { | |
581 | udelay(150); | |
582 | timeout++; | |
583 | } | |
584 | ||
585 | if (timeout == 20000) { | |
586 | DRM_ERROR("%s, can't lock DSIPLL.\n", | |
587 | __func__); | |
588 | return -EINVAL; | |
589 | } | |
590 | } | |
591 | } | |
592 | /* Restore mode */ | |
593 | PSB_WVDC32(htot_val, htot_reg); | |
594 | PSB_WVDC32(hblank_val, hblank_reg); | |
595 | PSB_WVDC32(hsync_val, hsync_reg); | |
596 | PSB_WVDC32(vtot_val, vtot_reg); | |
597 | PSB_WVDC32(vblank_val, vblank_reg); | |
598 | PSB_WVDC32(vsync_val, vsync_reg); | |
599 | PSB_WVDC32(pipesrc_val, pipesrc_reg); | |
600 | PSB_WVDC32(dspstatus_val, dspstatus_reg); | |
601 | ||
602 | /* Set up the plane */ | |
603 | PSB_WVDC32(dspstride_val, dspstride_reg); | |
604 | PSB_WVDC32(dsplinoff_val, dsplinoff_reg); | |
605 | PSB_WVDC32(dsptileoff_val, dsptileoff_reg); | |
606 | PSB_WVDC32(dspsize_val, dspsize_reg); | |
607 | PSB_WVDC32(dsppos_val, dsppos_reg); | |
608 | PSB_WVDC32(dspsurf_val, dspsurf_reg); | |
609 | ||
610 | if (pipe == 1) { | |
611 | PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL); | |
612 | PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); | |
613 | PSB_WVDC32(dev_priv->saveHDMIPHYMISCCTL, HDMIPHYMISCCTL); | |
614 | PSB_WVDC32(dev_priv->saveHDMIB_CONTROL, HDMIB_CONTROL); | |
615 | ||
616 | } else { | |
617 | /* Set up pipe related registers */ | |
618 | PSB_WVDC32(mipi_val, mipi_reg); | |
619 | /* Setup MIPI adapter + MIPI IP registers */ | |
620 | mdfld_dsi_controller_init(dsi_config, pipe); | |
621 | msleep(20); | |
622 | } | |
623 | /* Enable the plane */ | |
624 | PSB_WVDC32(dspcntr_val, dspcntr_reg); | |
625 | msleep(20); | |
626 | /* Enable the pipe */ | |
627 | PSB_WVDC32(pipeconf_val, pipeconf_reg); | |
628 | ||
629 | for (i = 0; i < 256; i++) | |
630 | PSB_WVDC32(palette_val[i], palette_reg + (i<<2)); | |
631 | if (pipe == 1) | |
632 | return 0; | |
633 | if (IS_MFLD(dev) && !mdfld_panel_dpi(dev)) | |
634 | mdfld_enable_te(dev, pipe); | |
635 | return 0; | |
636 | } | |
637 | ||
638 | /** | |
639 | * mdfld_restore_cursor_overlay_registers - restore cursor | |
640 | * @dev: our device | |
641 | * | |
642 | * Restore the cursor and overlay state that was saved earlier | |
643 | */ | |
644 | static int mdfld_restore_cursor_overlay_registers(struct drm_device *dev) | |
645 | { | |
646 | struct drm_psb_private *dev_priv = dev->dev_private; | |
647 | ||
648 | /* Enable Cursor A */ | |
649 | PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR); | |
650 | PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS); | |
651 | PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE); | |
652 | ||
653 | PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR); | |
654 | PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS); | |
655 | PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE); | |
656 | ||
657 | PSB_WVDC32(dev_priv->saveDSPCCURSOR_CTRL, CURCCNTR); | |
658 | PSB_WVDC32(dev_priv->saveDSPCCURSOR_POS, CURCPOS); | |
659 | PSB_WVDC32(dev_priv->saveDSPCCURSOR_BASE, CURCBASE); | |
660 | ||
661 | /* Restore HW overlay */ | |
662 | PSB_WVDC32(dev_priv->saveOV_OVADD, OV_OVADD); | |
663 | PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0); | |
664 | PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1); | |
665 | PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2); | |
666 | PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3); | |
667 | PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4); | |
668 | PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5); | |
669 | ||
670 | PSB_WVDC32(dev_priv->saveOV_OVADD_C, OV_OVADD + OV_C_OFFSET); | |
671 | PSB_WVDC32(dev_priv->saveOV_OGAMC0_C, OV_OGAMC0 + OV_C_OFFSET); | |
672 | PSB_WVDC32(dev_priv->saveOV_OGAMC1_C, OV_OGAMC1 + OV_C_OFFSET); | |
673 | PSB_WVDC32(dev_priv->saveOV_OGAMC2_C, OV_OGAMC2 + OV_C_OFFSET); | |
674 | PSB_WVDC32(dev_priv->saveOV_OGAMC3_C, OV_OGAMC3 + OV_C_OFFSET); | |
675 | PSB_WVDC32(dev_priv->saveOV_OGAMC4_C, OV_OGAMC4 + OV_C_OFFSET); | |
676 | PSB_WVDC32(dev_priv->saveOV_OGAMC5_C, OV_OGAMC5 + OV_C_OFFSET); | |
677 | ||
678 | return 0; | |
679 | } | |
680 | ||
c3460fd3 AC |
681 | /** |
682 | * power_down - power down the display island | |
683 | * @dev: our DRM device | |
0867b421 | 684 | * |
c3460fd3 | 685 | * Power down the display interface of our device |
0867b421 | 686 | */ |
c3460fd3 | 687 | static void power_down(struct drm_device *dev) |
0867b421 AC |
688 | { |
689 | struct drm_psb_private *dev_priv = dev->dev_private; | |
c3460fd3 AC |
690 | u32 pwr_mask ; |
691 | u32 pwr_sts; | |
0867b421 | 692 | |
c3460fd3 AC |
693 | if (IS_MRST(dev)) { |
694 | pwr_mask = PSB_PWRGT_DISPLAY_MASK; | |
695 | outl(pwr_mask, dev_priv->ospm_base + PSB_PM_SSC); | |
0867b421 | 696 | |
c3460fd3 AC |
697 | while (true) { |
698 | pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); | |
699 | if ((pwr_sts & pwr_mask) == pwr_mask) | |
700 | break; | |
701 | else | |
702 | udelay(10); | |
703 | } | |
704 | dev_priv->display_power = false; | |
705 | } | |
706 | } | |
707 | ||
708 | ||
709 | /** | |
710 | * gma_suspend_display - suspend the display logic | |
711 | * @dev: our DRM device | |
712 | * | |
713 | * Suspend the display logic of the graphics interface | |
a897854c AC |
714 | * |
715 | * FIXME: This ought to be replaced by a dev_priv-> ops interface | |
716 | * where the various platforms register their save/restore methods | |
717 | * and keep them in their own support files. | |
c3460fd3 AC |
718 | */ |
719 | static void gma_suspend_display(struct drm_device *dev) | |
720 | { | |
721 | struct drm_psb_private *dev_priv = dev->dev_private; | |
722 | int pp_stat; | |
723 | ||
724 | if (dev_priv->suspended) | |
0867b421 AC |
725 | return; |
726 | ||
a897854c AC |
727 | if (IS_MFLD(dev)) { |
728 | /* FIXME: We need to shut down panels here if using them | |
729 | and once the right bits are merged */ | |
730 | mdfld_save_cursor_overlay_registers(dev); | |
731 | mdfld_save_display_registers(dev, 0); | |
732 | mdfld_save_display_registers(dev, 0); | |
733 | mdfld_save_display_registers(dev, 2); | |
734 | mdfld_save_display_registers(dev, 1); | |
735 | mdfld_disable_crtc(dev, 0); | |
736 | mdfld_disable_crtc(dev, 2); | |
737 | mdfld_disable_crtc(dev, 1); | |
0867b421 | 738 | } else { |
a897854c AC |
739 | save_display_registers(dev); |
740 | ||
741 | if (dev_priv->iLVDS_enable) { | |
742 | /*shutdown the panel*/ | |
743 | PSB_WVDC32(0, PP_CONTROL); | |
744 | ||
745 | do { | |
746 | pp_stat = PSB_RVDC32(PP_STATUS); | |
747 | } while (pp_stat & 0x80000000); | |
748 | ||
749 | /* Turn off the plane */ | |
750 | PSB_WVDC32(0x58000000, DSPACNTR); | |
751 | PSB_WVDC32(0, DSPASURF);/*trigger the plane disable*/ | |
752 | /* Wait ~4 ticks */ | |
753 | msleep(4); | |
754 | ||
755 | /* Turn off pipe */ | |
756 | PSB_WVDC32(0x0, PIPEACONF); | |
757 | /* Wait ~8 ticks */ | |
758 | msleep(8); | |
759 | ||
760 | /* Turn off PLLs */ | |
761 | PSB_WVDC32(0, MRST_DPLL_A); | |
762 | } else { | |
763 | PSB_WVDC32(DPI_SHUT_DOWN, DPI_CONTROL_REG); | |
764 | PSB_WVDC32(0x0, PIPEACONF); | |
765 | PSB_WVDC32(0x2faf0000, BLC_PWM_CTL); | |
766 | while (REG_READ(0x70008) & 0x40000000) | |
767 | cpu_relax(); | |
768 | while ((PSB_RVDC32(GEN_FIFO_STAT_REG) & DPI_FIFO_EMPTY) | |
769 | != DPI_FIFO_EMPTY) | |
770 | cpu_relax(); | |
771 | PSB_WVDC32(0, DEVICE_READY_REG); | |
772 | /* Turn off panel power */ | |
773 | #ifdef CONFIG_X86_MRST | |
774 | intel_scu_ipc_simple_command(IPC_MSG_PANEL_ON_OFF, | |
775 | IPC_CMD_PANEL_OFF); | |
776 | #endif | |
777 | } | |
0867b421 | 778 | } |
c3460fd3 | 779 | power_down(dev); |
0867b421 AC |
780 | } |
781 | ||
782 | /* | |
c3460fd3 | 783 | * power_up |
0867b421 | 784 | * |
c3460fd3 | 785 | * Description: Restore power to the specified island(s) (powergating) |
0867b421 | 786 | */ |
c3460fd3 AC |
787 | static void power_up(struct drm_device *dev) |
788 | { | |
789 | struct drm_psb_private *dev_priv = dev->dev_private; | |
790 | u32 pwr_mask = PSB_PWRGT_DISPLAY_MASK; | |
791 | u32 pwr_sts, pwr_cnt; | |
792 | ||
793 | if (IS_MRST(dev)) { | |
794 | pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC); | |
795 | pwr_cnt &= ~pwr_mask; | |
796 | outl(pwr_cnt, (dev_priv->ospm_base + PSB_PM_SSC)); | |
797 | ||
798 | while (true) { | |
799 | pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); | |
800 | if ((pwr_sts & pwr_mask) == 0) | |
801 | break; | |
802 | else | |
803 | udelay(10); | |
804 | } | |
805 | } | |
806 | dev_priv->suspended = false; | |
807 | dev_priv->display_power = true; | |
808 | } | |
809 | ||
810 | /** | |
811 | * gma_resume_display - resume display side logic | |
812 | * | |
813 | * Resume the display hardware restoring state and enabling | |
814 | * as necessary. | |
815 | */ | |
816 | static void gma_resume_display(struct pci_dev *pdev) | |
0867b421 AC |
817 | { |
818 | struct drm_device *dev = pci_get_drvdata(pdev); | |
819 | struct drm_psb_private *dev_priv = dev->dev_private; | |
0867b421 | 820 | |
c3460fd3 | 821 | if (dev_priv->suspended == false) |
0867b421 AC |
822 | return; |
823 | ||
824 | /* turn on the display power island */ | |
c3460fd3 | 825 | power_up(dev); |
0867b421 | 826 | |
6a62730c | 827 | PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); |
0867b421 | 828 | pci_write_config_word(pdev, PSB_GMCH_CTRL, |
6a62730c | 829 | dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); |
0867b421 AC |
830 | |
831 | /* Don't reinitialize the GTT as it is unnecessary. The gtt is | |
832 | * stored in memory so it will automatically be restored. All | |
833 | * we need to do is restore the PGETBL_CTL which we already do | |
834 | * above. | |
835 | */ | |
836 | /*psb_gtt_init(dev_priv->pg, 1);*/ | |
a897854c AC |
837 | if (IS_MFLD(dev)) { |
838 | mdfld_restore_display_registers(dev, 1); | |
839 | mdfld_restore_display_registers(dev, 0); | |
840 | mdfld_restore_display_registers(dev, 2); | |
841 | mdfld_restore_cursor_overlay_registers(dev); | |
842 | } else if (IS_MRST(dev)) { | |
843 | if (!dev_priv->iLVDS_enable) { | |
844 | #ifdef CONFIG_X86_MRST | |
845 | intel_scu_ipc_simple_command(IPC_MSG_PANEL_ON_OFF, | |
846 | IPC_CMD_PANEL_ON); | |
847 | /* FIXME: can we avoid this delay ? */ | |
848 | msleep(2000); /* wait 2 seconds */ | |
849 | #endif | |
850 | } | |
851 | } | |
0867b421 AC |
852 | restore_display_registers(dev); |
853 | } | |
854 | ||
c3460fd3 AC |
855 | /** |
856 | * gma_suspend_pci - suspend PCI side | |
857 | * @pdev: PCI device | |
0867b421 | 858 | * |
c3460fd3 | 859 | * Perform the suspend processing on our PCI device state |
0867b421 | 860 | */ |
c3460fd3 | 861 | static void gma_suspend_pci(struct pci_dev *pdev) |
0867b421 AC |
862 | { |
863 | struct drm_device *dev = pci_get_drvdata(pdev); | |
864 | struct drm_psb_private *dev_priv = dev->dev_private; | |
865 | int bsm, vbt; | |
866 | ||
c3460fd3 | 867 | if (dev_priv->suspended) |
0867b421 AC |
868 | return; |
869 | ||
0867b421 AC |
870 | pci_save_state(pdev); |
871 | pci_read_config_dword(pdev, 0x5C, &bsm); | |
872 | dev_priv->saveBSM = bsm; | |
873 | pci_read_config_dword(pdev, 0xFC, &vbt); | |
874 | dev_priv->saveVBT = vbt; | |
875 | pci_read_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, &dev_priv->msi_addr); | |
876 | pci_read_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, &dev_priv->msi_data); | |
877 | ||
878 | pci_disable_device(pdev); | |
879 | pci_set_power_state(pdev, PCI_D3hot); | |
880 | ||
c3460fd3 | 881 | dev_priv->suspended = true; |
0867b421 AC |
882 | } |
883 | ||
c3460fd3 AC |
884 | /** |
885 | * gma_resume_pci - resume helper | |
886 | * @dev: our PCI device | |
0867b421 | 887 | * |
c3460fd3 AC |
888 | * Perform the resume processing on our PCI device state - rewrite |
889 | * register state and re-enable the PCI device | |
0867b421 | 890 | */ |
c3460fd3 | 891 | static bool gma_resume_pci(struct pci_dev *pdev) |
0867b421 AC |
892 | { |
893 | struct drm_device *dev = pci_get_drvdata(pdev); | |
894 | struct drm_psb_private *dev_priv = dev->dev_private; | |
c3460fd3 | 895 | int ret; |
0867b421 | 896 | |
c3460fd3 | 897 | if (!dev_priv->suspended) |
0867b421 AC |
898 | return true; |
899 | ||
0867b421 AC |
900 | pci_set_power_state(pdev, PCI_D0); |
901 | pci_restore_state(pdev); | |
902 | pci_write_config_dword(pdev, 0x5c, dev_priv->saveBSM); | |
903 | pci_write_config_dword(pdev, 0xFC, dev_priv->saveVBT); | |
904 | /* retoring MSI address and data in PCIx space */ | |
905 | pci_write_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, dev_priv->msi_addr); | |
906 | pci_write_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, dev_priv->msi_data); | |
907 | ret = pci_enable_device(pdev); | |
908 | ||
909 | if (ret != 0) | |
c3460fd3 | 910 | dev_err(&pdev->dev, "pci_enable failed: %d\n", ret); |
0867b421 | 911 | else |
c3460fd3 AC |
912 | dev_priv->suspended = false; |
913 | return !dev_priv->suspended; | |
0867b421 AC |
914 | } |
915 | ||
c3460fd3 AC |
916 | /** |
917 | * gma_power_suspend - bus callback for suspend | |
918 | * @pdev: our PCI device | |
919 | * @state: suspend type | |
0867b421 | 920 | * |
c3460fd3 AC |
921 | * Called back by the PCI layer during a suspend of the system. We |
922 | * perform the necessary shut down steps and save enough state that | |
923 | * we can undo this when resume is called. | |
0867b421 | 924 | */ |
c3460fd3 | 925 | int gma_power_suspend(struct pci_dev *pdev, pm_message_t state) |
0867b421 | 926 | { |
c3460fd3 AC |
927 | struct drm_device *dev = pci_get_drvdata(pdev); |
928 | struct drm_psb_private *dev_priv = dev->dev_private; | |
0867b421 | 929 | |
c3460fd3 AC |
930 | mutex_lock(&power_mutex); |
931 | if (!dev_priv->suspended) { | |
932 | if (dev_priv->display_count) { | |
933 | mutex_unlock(&power_mutex); | |
934 | return -EBUSY; | |
0867b421 | 935 | } |
c3460fd3 AC |
936 | psb_irq_uninstall(dev); |
937 | gma_suspend_display(dev); | |
938 | gma_suspend_pci(pdev); | |
0867b421 | 939 | } |
c3460fd3 | 940 | mutex_unlock(&power_mutex); |
0867b421 AC |
941 | return 0; |
942 | } | |
943 | ||
944 | ||
c3460fd3 AC |
945 | /** |
946 | * gma_power_resume - resume power | |
947 | * @pdev: PCI device | |
0867b421 | 948 | * |
c3460fd3 | 949 | * Resume the PCI side of the graphics and then the displays |
0867b421 | 950 | */ |
c3460fd3 | 951 | int gma_power_resume(struct pci_dev *pdev) |
0867b421 | 952 | { |
c3460fd3 | 953 | struct drm_device *dev = pci_get_drvdata(pdev); |
0867b421 | 954 | |
c3460fd3 AC |
955 | mutex_lock(&power_mutex); |
956 | gma_resume_pci(pdev); | |
957 | gma_resume_display(pdev); | |
958 | psb_irq_preinstall(dev); | |
959 | psb_irq_postinstall(dev); | |
960 | mutex_unlock(&power_mutex); | |
961 | return 0; | |
0867b421 AC |
962 | } |
963 | ||
964 | ||
c3460fd3 AC |
965 | |
966 | /** | |
967 | * gma_power_is_on - returne true if power is on | |
968 | * @dev: our DRM device | |
0867b421 | 969 | * |
c3460fd3 | 970 | * Returns true if the display island power is on at this moment |
0867b421 | 971 | */ |
c3460fd3 | 972 | bool gma_power_is_on(struct drm_device *dev) |
0867b421 | 973 | { |
c3460fd3 AC |
974 | struct drm_psb_private *dev_priv = dev->dev_private; |
975 | return dev_priv->display_power; | |
0867b421 AC |
976 | } |
977 | ||
c3460fd3 AC |
978 | |
979 | /** | |
980 | * gma_power_begin - begin requiring power | |
981 | * @dev: our DRM device | |
982 | * @force_on: true to force power on | |
0867b421 | 983 | * |
c3460fd3 AC |
984 | * Begin an action that requires the display power island is enabled. |
985 | * We refcount the islands. | |
5352161f | 986 | * |
c3460fd3 | 987 | * FIXME: locking |
0867b421 | 988 | */ |
c3460fd3 | 989 | bool gma_power_begin(struct drm_device *dev, bool force_on) |
0867b421 | 990 | { |
c3460fd3 AC |
991 | struct drm_psb_private *dev_priv = dev->dev_private; |
992 | int ret; | |
0867b421 | 993 | |
c3460fd3 AC |
994 | /* Power already on ? */ |
995 | if (dev_priv->display_power) { | |
996 | dev_priv->display_count++; | |
997 | pm_runtime_get(&dev->pdev->dev); | |
998 | return true; | |
0867b421 | 999 | } |
c3460fd3 AC |
1000 | if (force_on == false) |
1001 | return false; | |
1002 | ||
1003 | /* Ok power up needed */ | |
1004 | ret = gma_resume_pci(dev->pdev); | |
1005 | if (ret == 0) { | |
1006 | psb_irq_preinstall(dev); | |
1007 | psb_irq_postinstall(dev); | |
1008 | pm_runtime_get(&dev->pdev->dev); | |
1009 | dev_priv->display_count++; | |
1010 | return true; | |
0867b421 | 1011 | } |
c3460fd3 | 1012 | return false; |
0867b421 AC |
1013 | } |
1014 | ||
1015 | ||
c3460fd3 AC |
1016 | /** |
1017 | * gma_power_end - end use of power | |
1018 | * @dev: Our DRM device | |
0867b421 | 1019 | * |
c3460fd3 AC |
1020 | * Indicate that one of our gma_power_begin() requested periods when |
1021 | * the diplay island power is needed has completed. | |
0867b421 | 1022 | */ |
c3460fd3 | 1023 | void gma_power_end(struct drm_device *dev) |
0867b421 | 1024 | { |
c3460fd3 AC |
1025 | struct drm_psb_private *dev_priv = dev->dev_private; |
1026 | dev_priv->display_count--; | |
1027 | WARN_ON(dev_priv->display_count < 0); | |
1028 | pm_runtime_put(&dev->pdev->dev); | |
0867b421 AC |
1029 | } |
1030 | ||
1031 | int psb_runtime_suspend(struct device *dev) | |
1032 | { | |
c3460fd3 AC |
1033 | static pm_message_t dummy; |
1034 | return gma_power_suspend(to_pci_dev(dev), dummy); | |
0867b421 AC |
1035 | } |
1036 | ||
1037 | int psb_runtime_resume(struct device *dev) | |
1038 | { | |
1039 | return 0; | |
1040 | } | |
1041 | ||
1042 | int psb_runtime_idle(struct device *dev) | |
1043 | { | |
c3460fd3 AC |
1044 | struct drm_device *drmdev = pci_get_drvdata(to_pci_dev(dev)); |
1045 | struct drm_psb_private *dev_priv = drmdev->dev_private; | |
1046 | if (dev_priv->display_count) | |
0867b421 | 1047 | return 0; |
c3460fd3 AC |
1048 | else |
1049 | return 1; | |
0867b421 AC |
1050 | } |
1051 |