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 | |
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>
-rw-r--r-- | drivers/video/geode/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/geode/gxfb.h | 72 | ||||
-rw-r--r-- | drivers/video/geode/gxfb_core.c | 57 | ||||
-rw-r--r-- | drivers/video/geode/suspend_gx.c | 267 |
4 files changed, 396 insertions, 2 deletions
diff --git a/drivers/video/geode/Makefile b/drivers/video/geode/Makefile index 957304b45fba..5c98da126883 100644 --- a/drivers/video/geode/Makefile +++ b/drivers/video/geode/Makefile | |||
@@ -5,5 +5,5 @@ obj-$(CONFIG_FB_GEODE_GX) += gxfb.o | |||
5 | obj-$(CONFIG_FB_GEODE_LX) += lxfb.o | 5 | obj-$(CONFIG_FB_GEODE_LX) += lxfb.o |
6 | 6 | ||
7 | gx1fb-objs := gx1fb_core.o display_gx1.o video_cs5530.o | 7 | gx1fb-objs := gx1fb_core.o display_gx1.o video_cs5530.o |
8 | gxfb-objs := gxfb_core.o display_gx.o video_gx.o | 8 | gxfb-objs := gxfb_core.o display_gx.o video_gx.o suspend_gx.o |
9 | lxfb-objs := lxfb_core.o lxfb_ops.o | 9 | lxfb-objs := lxfb_core.o lxfb_ops.o |
diff --git a/drivers/video/geode/gxfb.h b/drivers/video/geode/gxfb.h index b8db7f82646c..16a96f8fd8c5 100644 --- a/drivers/video/geode/gxfb.h +++ b/drivers/video/geode/gxfb.h | |||
@@ -13,10 +13,34 @@ | |||
13 | 13 | ||
14 | #include <linux/io.h> | 14 | #include <linux/io.h> |
15 | 15 | ||
16 | #define GP_REG_COUNT (0x50 / 4) | ||
17 | #define DC_REG_COUNT (0x90 / 4) | ||
18 | #define VP_REG_COUNT (0x138 / 8) | ||
19 | #define FP_REG_COUNT (0x68 / 8) | ||
20 | |||
21 | #define DC_PAL_COUNT 0x104 | ||
22 | |||
16 | struct gxfb_par { | 23 | struct gxfb_par { |
17 | int enable_crt; | 24 | int enable_crt; |
18 | void __iomem *dc_regs; | 25 | void __iomem *dc_regs; |
19 | void __iomem *vid_regs; | 26 | void __iomem *vid_regs; |
27 | void __iomem *gp_regs; | ||
28 | #ifdef CONFIG_PM | ||
29 | int powered_down; | ||
30 | |||
31 | /* register state, for power management functionality */ | ||
32 | struct { | ||
33 | uint64_t padsel; | ||
34 | uint64_t dotpll; | ||
35 | } msr; | ||
36 | |||
37 | uint32_t gp[GP_REG_COUNT]; | ||
38 | uint32_t dc[DC_REG_COUNT]; | ||
39 | uint64_t vp[VP_REG_COUNT]; | ||
40 | uint64_t fp[FP_REG_COUNT]; | ||
41 | |||
42 | uint32_t pal[DC_PAL_COUNT]; | ||
43 | #endif | ||
20 | }; | 44 | }; |
21 | 45 | ||
22 | unsigned int gx_frame_buffer_size(void); | 46 | unsigned int gx_frame_buffer_size(void); |
@@ -29,6 +53,43 @@ void gx_set_dclk_frequency(struct fb_info *info); | |||
29 | void gx_configure_display(struct fb_info *info); | 53 | void gx_configure_display(struct fb_info *info); |
30 | int gx_blank_display(struct fb_info *info, int blank_mode); | 54 | int gx_blank_display(struct fb_info *info, int blank_mode); |
31 | 55 | ||
56 | #ifdef CONFIG_PM | ||
57 | int gx_powerdown(struct fb_info *info); | ||
58 | int gx_powerup(struct fb_info *info); | ||
59 | #endif | ||
60 | |||
61 | |||
62 | /* Graphics Processor registers (table 6-23 from the data book) */ | ||
63 | enum gp_registers { | ||
64 | GP_DST_OFFSET = 0, | ||
65 | GP_SRC_OFFSET, | ||
66 | GP_STRIDE, | ||
67 | GP_WID_HEIGHT, | ||
68 | |||
69 | GP_SRC_COLOR_FG, | ||
70 | GP_SRC_COLOR_BG, | ||
71 | GP_PAT_COLOR_0, | ||
72 | GP_PAT_COLOR_1, | ||
73 | |||
74 | GP_PAT_COLOR_2, | ||
75 | GP_PAT_COLOR_3, | ||
76 | GP_PAT_COLOR_4, | ||
77 | GP_PAT_COLOR_5, | ||
78 | |||
79 | GP_PAT_DATA_0, | ||
80 | GP_PAT_DATA_1, | ||
81 | GP_RASTER_MODE, | ||
82 | GP_VECTOR_MODE, | ||
83 | |||
84 | GP_BLT_MODE, | ||
85 | GP_BLT_STATUS, | ||
86 | GP_HST_SRC, | ||
87 | GP_BASE_OFFSET, /* 0x4c */ | ||
88 | }; | ||
89 | |||
90 | #define GP_BLT_STATUS_BLT_PENDING (1 << 2) | ||
91 | #define GP_BLT_STATUS_BLT_BUSY (1 << 0) | ||
92 | |||
32 | 93 | ||
33 | /* Display Controller registers (table 6-38 from the data book) */ | 94 | /* Display Controller registers (table 6-38 from the data book) */ |
34 | enum dc_registers { | 95 | enum dc_registers { |
@@ -238,6 +299,16 @@ enum fp_registers { | |||
238 | 299 | ||
239 | /* register access functions */ | 300 | /* register access functions */ |
240 | 301 | ||
302 | static inline uint32_t read_gp(struct gxfb_par *par, int reg) | ||
303 | { | ||
304 | return readl(par->gp_regs + 4*reg); | ||
305 | } | ||
306 | |||
307 | static inline void write_gp(struct gxfb_par *par, int reg, uint32_t val) | ||
308 | { | ||
309 | writel(val, par->gp_regs + 4*reg); | ||
310 | } | ||
311 | |||
241 | static inline uint32_t read_dc(struct gxfb_par *par, int reg) | 312 | static inline uint32_t read_dc(struct gxfb_par *par, int reg) |
242 | { | 313 | { |
243 | return readl(par->dc_regs + 4*reg); | 314 | return readl(par->dc_regs + 4*reg); |
@@ -248,7 +319,6 @@ static inline void write_dc(struct gxfb_par *par, int reg, uint32_t val) | |||
248 | writel(val, par->dc_regs + 4*reg); | 319 | writel(val, par->dc_regs + 4*reg); |
249 | } | 320 | } |
250 | 321 | ||
251 | |||
252 | static inline uint32_t read_vp(struct gxfb_par *par, int reg) | 322 | static inline uint32_t read_vp(struct gxfb_par *par, int reg) |
253 | { | 323 | { |
254 | return readl(par->vid_regs + 8*reg); | 324 | return readl(par->vid_regs + 8*reg); |
diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c index fc56b8fc1a8b..151d964c025a 100644 --- a/drivers/video/geode/gxfb_core.c +++ b/drivers/video/geode/gxfb_core.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
29 | #include <linux/delay.h> | 29 | #include <linux/delay.h> |
30 | #include <linux/fb.h> | 30 | #include <linux/fb.h> |
31 | #include <linux/console.h> | ||
31 | #include <linux/init.h> | 32 | #include <linux/init.h> |
32 | #include <linux/pci.h> | 33 | #include <linux/pci.h> |
33 | #include <asm/geode.h> | 34 | #include <asm/geode.h> |
@@ -222,6 +223,15 @@ static int __init gxfb_map_video_memory(struct fb_info *info, struct pci_dev *de | |||
222 | if (!par->dc_regs) | 223 | if (!par->dc_regs) |
223 | return -ENOMEM; | 224 | return -ENOMEM; |
224 | 225 | ||
226 | ret = pci_request_region(dev, 1, "gxfb (graphics processor)"); | ||
227 | if (ret < 0) | ||
228 | return ret; | ||
229 | par->gp_regs = ioremap(pci_resource_start(dev, 1), | ||
230 | pci_resource_len(dev, 1)); | ||
231 | |||
232 | if (!par->gp_regs) | ||
233 | return -ENOMEM; | ||
234 | |||
225 | ret = pci_request_region(dev, 0, "gxfb (framebuffer)"); | 235 | ret = pci_request_region(dev, 0, "gxfb (framebuffer)"); |
226 | if (ret < 0) | 236 | if (ret < 0) |
227 | return ret; | 237 | return ret; |
@@ -295,6 +305,42 @@ static struct fb_info * __init gxfb_init_fbinfo(struct device *dev) | |||
295 | return info; | 305 | return info; |
296 | } | 306 | } |
297 | 307 | ||
308 | #ifdef CONFIG_PM | ||
309 | static int gxfb_suspend(struct pci_dev *pdev, pm_message_t state) | ||
310 | { | ||
311 | struct fb_info *info = pci_get_drvdata(pdev); | ||
312 | |||
313 | if (state.event == PM_EVENT_SUSPEND) { | ||
314 | acquire_console_sem(); | ||
315 | gx_powerdown(info); | ||
316 | fb_set_suspend(info, 1); | ||
317 | release_console_sem(); | ||
318 | } | ||
319 | |||
320 | /* there's no point in setting PCI states; we emulate PCI, so | ||
321 | * we don't end up getting power savings anyways */ | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static int gxfb_resume(struct pci_dev *pdev) | ||
327 | { | ||
328 | struct fb_info *info = pci_get_drvdata(pdev); | ||
329 | int ret; | ||
330 | |||
331 | acquire_console_sem(); | ||
332 | ret = gx_powerup(info); | ||
333 | if (ret) { | ||
334 | printk(KERN_ERR "gxfb: power up failed!\n"); | ||
335 | return ret; | ||
336 | } | ||
337 | |||
338 | fb_set_suspend(info, 0); | ||
339 | release_console_sem(); | ||
340 | return 0; | ||
341 | } | ||
342 | #endif | ||
343 | |||
298 | static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) | 344 | static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
299 | { | 345 | { |
300 | struct gxfb_par *par; | 346 | struct gxfb_par *par; |
@@ -357,6 +403,10 @@ static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *i | |||
357 | iounmap(par->dc_regs); | 403 | iounmap(par->dc_regs); |
358 | pci_release_region(pdev, 2); | 404 | pci_release_region(pdev, 2); |
359 | } | 405 | } |
406 | if (par->gp_regs) { | ||
407 | iounmap(par->gp_regs); | ||
408 | pci_release_region(pdev, 1); | ||
409 | } | ||
360 | 410 | ||
361 | if (info) | 411 | if (info) |
362 | framebuffer_release(info); | 412 | framebuffer_release(info); |
@@ -379,6 +429,9 @@ static void gxfb_remove(struct pci_dev *pdev) | |||
379 | iounmap(par->dc_regs); | 429 | iounmap(par->dc_regs); |
380 | pci_release_region(pdev, 2); | 430 | pci_release_region(pdev, 2); |
381 | 431 | ||
432 | iounmap(par->gp_regs); | ||
433 | pci_release_region(pdev, 1); | ||
434 | |||
382 | pci_set_drvdata(pdev, NULL); | 435 | pci_set_drvdata(pdev, NULL); |
383 | 436 | ||
384 | framebuffer_release(info); | 437 | framebuffer_release(info); |
@@ -396,6 +449,10 @@ static struct pci_driver gxfb_driver = { | |||
396 | .id_table = gxfb_id_table, | 449 | .id_table = gxfb_id_table, |
397 | .probe = gxfb_probe, | 450 | .probe = gxfb_probe, |
398 | .remove = gxfb_remove, | 451 | .remove = gxfb_remove, |
452 | #ifdef CONFIG_PM | ||
453 | .suspend = gxfb_suspend, | ||
454 | .resume = gxfb_resume, | ||
455 | #endif | ||
399 | }; | 456 | }; |
400 | 457 | ||
401 | #ifndef MODULE | 458 | #ifndef MODULE |
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 | ||