diff options
author | Andres Salomon <dilinger@queued.net> | 2008-04-28 05:15:02 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-28 11:58:36 -0400 |
commit | 46fb6f110dfc3fc99f44cf701f66ea3e790b6a81 (patch) | |
tree | f1fd3abed10ac35bed20670bb47b83c592293d07 /drivers/video/geode/suspend_gx.c | |
parent | d1b4cc3ec5f8ddbac57ada58cbab36f5a0be38eb (diff) |
gxfb: add power management functionality
This adds the ability to suspend/resume the gxfb driver, which includes:
- The addition of a Graphics Processor register table in gxfb.h, and
associated GP handling.
- Register and palette saving code; registers are stored in gxfb_par.
A few MSR values are saved as well.
- gx_powerup and gx_powerdown functions which restore/save registers and
enable/disable graphic engines.
- gxfb_suspend/gxfb_resume
Originally based on a patch by Jordan Crouse.
Signed-off-by: Andres Salomon <dilinger@debian.org>
Cc: Jordan Crouse <jordan.crouse@amd.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/geode/suspend_gx.c')
-rw-r--r-- | drivers/video/geode/suspend_gx.c | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/drivers/video/geode/suspend_gx.c b/drivers/video/geode/suspend_gx.c new file mode 100644 index 000000000000..9aff32ef8bb6 --- /dev/null +++ b/drivers/video/geode/suspend_gx.c | |||
@@ -0,0 +1,267 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Advanced Micro Devices, Inc. | ||
3 | * Copyright (C) 2008 Andres Salomon <dilinger@debian.org> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | */ | ||
10 | #include <linux/fb.h> | ||
11 | #include <asm/io.h> | ||
12 | #include <asm/msr.h> | ||
13 | #include <asm/geode.h> | ||
14 | #include <asm/delay.h> | ||
15 | |||
16 | #include "gxfb.h" | ||
17 | |||
18 | #ifdef CONFIG_PM | ||
19 | |||
20 | static void gx_save_regs(struct gxfb_par *par) | ||
21 | { | ||
22 | int i; | ||
23 | |||
24 | /* wait for the BLT engine to stop being busy */ | ||
25 | do { | ||
26 | i = read_gp(par, GP_BLT_STATUS); | ||
27 | } while (i & (GP_BLT_STATUS_BLT_PENDING | GP_BLT_STATUS_BLT_BUSY)); | ||
28 | |||
29 | /* save MSRs */ | ||
30 | rdmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel); | ||
31 | rdmsrl(MSR_GLCP_DOTPLL, par->msr.dotpll); | ||
32 | |||
33 | write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); | ||
34 | |||
35 | /* save registers */ | ||
36 | memcpy(par->gp, par->gp_regs, sizeof(par->gp)); | ||
37 | memcpy(par->dc, par->dc_regs, sizeof(par->dc)); | ||
38 | memcpy(par->vp, par->vid_regs, sizeof(par->vp)); | ||
39 | memcpy(par->fp, par->vid_regs + VP_FP_START, sizeof(par->fp)); | ||
40 | |||
41 | /* save the palette */ | ||
42 | write_dc(par, DC_PAL_ADDRESS, 0); | ||
43 | for (i = 0; i < ARRAY_SIZE(par->pal); i++) | ||
44 | par->pal[i] = read_dc(par, DC_PAL_DATA); | ||
45 | } | ||
46 | |||
47 | static void gx_set_dotpll(uint32_t dotpll_hi) | ||
48 | { | ||
49 | uint32_t dotpll_lo; | ||
50 | int i; | ||
51 | |||
52 | rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo); | ||
53 | dotpll_lo |= MSR_GLCP_DOTPLL_DOTRESET; | ||
54 | dotpll_lo &= ~MSR_GLCP_DOTPLL_BYPASS; | ||
55 | wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi); | ||
56 | |||
57 | /* wait for the PLL to lock */ | ||
58 | for (i = 0; i < 200; i++) { | ||
59 | rdmsrl(MSR_GLCP_DOTPLL, dotpll_lo); | ||
60 | if (dotpll_lo & MSR_GLCP_DOTPLL_LOCK) | ||
61 | break; | ||
62 | udelay(1); | ||
63 | } | ||
64 | |||
65 | /* PLL set, unlock */ | ||
66 | dotpll_lo &= ~MSR_GLCP_DOTPLL_DOTRESET; | ||
67 | wrmsr(MSR_GLCP_DOTPLL, dotpll_lo, dotpll_hi); | ||
68 | } | ||
69 | |||
70 | static void gx_restore_gfx_proc(struct gxfb_par *par) | ||
71 | { | ||
72 | int i; | ||
73 | |||
74 | for (i = 0; i < ARRAY_SIZE(par->gp); i++) { | ||
75 | switch (i) { | ||
76 | case GP_VECTOR_MODE: | ||
77 | case GP_BLT_MODE: | ||
78 | case GP_BLT_STATUS: | ||
79 | case GP_HST_SRC: | ||
80 | /* don't restore these registers */ | ||
81 | break; | ||
82 | default: | ||
83 | write_gp(par, i, par->gp[i]); | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | |||
88 | static void gx_restore_display_ctlr(struct gxfb_par *par) | ||
89 | { | ||
90 | int i; | ||
91 | |||
92 | for (i = 0; i < ARRAY_SIZE(par->dc); i++) { | ||
93 | switch (i) { | ||
94 | case DC_UNLOCK: | ||
95 | /* unlock the DC; runs first */ | ||
96 | write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); | ||
97 | break; | ||
98 | |||
99 | case DC_GENERAL_CFG: | ||
100 | /* write without the enables */ | ||
101 | write_dc(par, i, par->dc[i] & ~(DC_GENERAL_CFG_VIDE | | ||
102 | DC_GENERAL_CFG_ICNE | | ||
103 | DC_GENERAL_CFG_CURE | | ||
104 | DC_GENERAL_CFG_DFLE)); | ||
105 | break; | ||
106 | |||
107 | case DC_DISPLAY_CFG: | ||
108 | /* write without the enables */ | ||
109 | write_dc(par, i, par->dc[i] & ~(DC_DISPLAY_CFG_VDEN | | ||
110 | DC_DISPLAY_CFG_GDEN | | ||
111 | DC_DISPLAY_CFG_TGEN)); | ||
112 | break; | ||
113 | |||
114 | case DC_RSVD_0: | ||
115 | case DC_RSVD_1: | ||
116 | case DC_RSVD_2: | ||
117 | case DC_RSVD_3: | ||
118 | case DC_RSVD_4: | ||
119 | case DC_LINE_CNT: | ||
120 | case DC_PAL_ADDRESS: | ||
121 | case DC_PAL_DATA: | ||
122 | case DC_DFIFO_DIAG: | ||
123 | case DC_CFIFO_DIAG: | ||
124 | case DC_RSVD_5: | ||
125 | /* don't restore these registers */ | ||
126 | break; | ||
127 | default: | ||
128 | write_dc(par, i, par->dc[i]); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | /* restore the palette */ | ||
133 | write_dc(par, DC_PAL_ADDRESS, 0); | ||
134 | for (i = 0; i < ARRAY_SIZE(par->pal); i++) | ||
135 | write_dc(par, DC_PAL_DATA, par->pal[i]); | ||
136 | } | ||
137 | |||
138 | static void gx_restore_video_proc(struct gxfb_par *par) | ||
139 | { | ||
140 | int i; | ||
141 | |||
142 | wrmsrl(MSR_GX_MSR_PADSEL, par->msr.padsel); | ||
143 | |||
144 | for (i = 0; i < ARRAY_SIZE(par->vp); i++) { | ||
145 | switch (i) { | ||
146 | case VP_VCFG: | ||
147 | /* don't enable video yet */ | ||
148 | write_vp(par, i, par->vp[i] & ~VP_VCFG_VID_EN); | ||
149 | break; | ||
150 | |||
151 | case VP_DCFG: | ||
152 | /* don't enable CRT yet */ | ||
153 | write_vp(par, i, par->vp[i] & | ||
154 | ~(VP_DCFG_DAC_BL_EN | VP_DCFG_VSYNC_EN | | ||
155 | VP_DCFG_HSYNC_EN | VP_DCFG_CRT_EN)); | ||
156 | break; | ||
157 | |||
158 | case VP_GAR: | ||
159 | case VP_GDR: | ||
160 | case VP_RSVD_0: | ||
161 | case VP_RSVD_1: | ||
162 | case VP_RSVD_2: | ||
163 | case VP_RSVD_3: | ||
164 | case VP_CRC32: | ||
165 | case VP_AWT: | ||
166 | case VP_VTM: | ||
167 | /* don't restore these registers */ | ||
168 | break; | ||
169 | default: | ||
170 | write_vp(par, i, par->vp[i]); | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | |||
175 | static void gx_restore_regs(struct gxfb_par *par) | ||
176 | { | ||
177 | int i; | ||
178 | |||
179 | gx_set_dotpll((uint32_t) (par->msr.dotpll >> 32)); | ||
180 | gx_restore_gfx_proc(par); | ||
181 | gx_restore_display_ctlr(par); | ||
182 | gx_restore_video_proc(par); | ||
183 | |||
184 | /* Flat Panel */ | ||
185 | for (i = 0; i < ARRAY_SIZE(par->fp); i++) { | ||
186 | if (i != FP_PM && i != FP_RSVD_0) | ||
187 | write_fp(par, i, par->fp[i]); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | static void gx_disable_graphics(struct gxfb_par *par) | ||
192 | { | ||
193 | /* shut down the engine */ | ||
194 | write_vp(par, VP_VCFG, par->vp[VP_VCFG] & ~VP_VCFG_VID_EN); | ||
195 | write_vp(par, VP_DCFG, par->vp[VP_DCFG] & ~(VP_DCFG_DAC_BL_EN | | ||
196 | VP_DCFG_VSYNC_EN | VP_DCFG_HSYNC_EN | VP_DCFG_CRT_EN)); | ||
197 | |||
198 | /* turn off the flat panel */ | ||
199 | write_fp(par, FP_PM, par->fp[FP_PM] & ~FP_PM_P); | ||
200 | |||
201 | |||
202 | /* turn off display */ | ||
203 | write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); | ||
204 | write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG] & | ||
205 | ~(DC_GENERAL_CFG_VIDE | DC_GENERAL_CFG_ICNE | | ||
206 | DC_GENERAL_CFG_CURE | DC_GENERAL_CFG_DFLE)); | ||
207 | write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG] & | ||
208 | ~(DC_DISPLAY_CFG_VDEN | DC_DISPLAY_CFG_GDEN | | ||
209 | DC_DISPLAY_CFG_TGEN)); | ||
210 | write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK); | ||
211 | } | ||
212 | |||
213 | static void gx_enable_graphics(struct gxfb_par *par) | ||
214 | { | ||
215 | uint32_t fp; | ||
216 | |||
217 | fp = read_fp(par, FP_PM); | ||
218 | if (par->fp[FP_PM] & FP_PM_P) { | ||
219 | /* power on the panel if not already power{ed,ing} on */ | ||
220 | if (!(fp & (FP_PM_PANEL_ON|FP_PM_PANEL_PWR_UP))) | ||
221 | write_fp(par, FP_PM, par->fp[FP_PM]); | ||
222 | } else { | ||
223 | /* power down the panel if not already power{ed,ing} down */ | ||
224 | if (!(fp & (FP_PM_PANEL_OFF|FP_PM_PANEL_PWR_DOWN))) | ||
225 | write_fp(par, FP_PM, par->fp[FP_PM]); | ||
226 | } | ||
227 | |||
228 | /* turn everything on */ | ||
229 | write_vp(par, VP_VCFG, par->vp[VP_VCFG]); | ||
230 | write_vp(par, VP_DCFG, par->vp[VP_DCFG]); | ||
231 | write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG]); | ||
232 | /* do this last; it will enable the FIFO load */ | ||
233 | write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG]); | ||
234 | |||
235 | /* lock the door behind us */ | ||
236 | write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK); | ||
237 | } | ||
238 | |||
239 | int gx_powerdown(struct fb_info *info) | ||
240 | { | ||
241 | struct gxfb_par *par = info->par; | ||
242 | |||
243 | if (par->powered_down) | ||
244 | return 0; | ||
245 | |||
246 | gx_save_regs(par); | ||
247 | gx_disable_graphics(par); | ||
248 | |||
249 | par->powered_down = 1; | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | int gx_powerup(struct fb_info *info) | ||
254 | { | ||
255 | struct gxfb_par *par = info->par; | ||
256 | |||
257 | if (!par->powered_down) | ||
258 | return 0; | ||
259 | |||
260 | gx_restore_regs(par); | ||
261 | gx_enable_graphics(par); | ||
262 | |||
263 | par->powered_down = 0; | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | #endif | ||