aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/geode
diff options
context:
space:
mode:
authorAndres Salomon <dilinger@queued.net>2008-04-28 05:15:02 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-28 11:58:36 -0400
commit46fb6f110dfc3fc99f44cf701f66ea3e790b6a81 (patch)
treef1fd3abed10ac35bed20670bb47b83c592293d07 /drivers/video/geode
parentd1b4cc3ec5f8ddbac57ada58cbab36f5a0be38eb (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')
-rw-r--r--drivers/video/geode/Makefile2
-rw-r--r--drivers/video/geode/gxfb.h72
-rw-r--r--drivers/video/geode/gxfb_core.c57
-rw-r--r--drivers/video/geode/suspend_gx.c267
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
5obj-$(CONFIG_FB_GEODE_LX) += lxfb.o 5obj-$(CONFIG_FB_GEODE_LX) += lxfb.o
6 6
7gx1fb-objs := gx1fb_core.o display_gx1.o video_cs5530.o 7gx1fb-objs := gx1fb_core.o display_gx1.o video_cs5530.o
8gxfb-objs := gxfb_core.o display_gx.o video_gx.o 8gxfb-objs := gxfb_core.o display_gx.o video_gx.o suspend_gx.o
9lxfb-objs := lxfb_core.o lxfb_ops.o 9lxfb-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
16struct gxfb_par { 23struct 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
22unsigned int gx_frame_buffer_size(void); 46unsigned int gx_frame_buffer_size(void);
@@ -29,6 +53,43 @@ void gx_set_dclk_frequency(struct fb_info *info);
29void gx_configure_display(struct fb_info *info); 53void gx_configure_display(struct fb_info *info);
30int gx_blank_display(struct fb_info *info, int blank_mode); 54int gx_blank_display(struct fb_info *info, int blank_mode);
31 55
56#ifdef CONFIG_PM
57int gx_powerdown(struct fb_info *info);
58int gx_powerup(struct fb_info *info);
59#endif
60
61
62/* Graphics Processor registers (table 6-23 from the data book) */
63enum 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) */
34enum dc_registers { 95enum dc_registers {
@@ -238,6 +299,16 @@ enum fp_registers {
238 299
239/* register access functions */ 300/* register access functions */
240 301
302static inline uint32_t read_gp(struct gxfb_par *par, int reg)
303{
304 return readl(par->gp_regs + 4*reg);
305}
306
307static inline void write_gp(struct gxfb_par *par, int reg, uint32_t val)
308{
309 writel(val, par->gp_regs + 4*reg);
310}
311
241static inline uint32_t read_dc(struct gxfb_par *par, int reg) 312static 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
252static inline uint32_t read_vp(struct gxfb_par *par, int reg) 322static 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
309static 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
326static 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
298static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) 344static 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
20static 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
47static 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
70static 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
88static 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
138static 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
175static 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
191static 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
213static 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
239int 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
253int 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