aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndres Salomon <dilinger@queued.net>2008-04-28 05:15:27 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-28 11:58:39 -0400
commitf694e53bd0db69557ee8e0db2d1602818ff173b0 (patch)
treed904b492105055a2adf00c60fa0324c0905b3bbe
parentaec40532c4d1183fa1ec415bb7dae08e19fc6b01 (diff)
lxfb: add power management functionality
This adds the ability to suspend/resume the lxfb driver, which includes: - Register and palette saving code; registers are stored in lxfb_par. A few MSR values are saved as well. - lx_powerup and lx_powerdown functions which restore/save registers and enable/disable graphic engines. - lxfb_suspend/lxfb_resume Originally based on a patch by Jordan Crouse. [akpm@linux-foundation.org: be conventional, save an ifdef] Signed-off-by: Andres Salomon <dilinger@debian.org> Cc: "Antonino A. Daplas" <adaplas@pol.net> Cc: Jordan Crouse <jordan.crouse@amd.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/video/geode/lxfb.h45
-rw-r--r--drivers/video/geode/lxfb_core.c41
-rw-r--r--drivers/video/geode/lxfb_ops.c263
3 files changed, 340 insertions, 9 deletions
diff --git a/drivers/video/geode/lxfb.h b/drivers/video/geode/lxfb.h
index b3fbc56ccbd7..3b9416f4ee20 100644
--- a/drivers/video/geode/lxfb.h
+++ b/drivers/video/geode/lxfb.h
@@ -3,6 +3,16 @@
3 3
4#include <linux/fb.h> 4#include <linux/fb.h>
5 5
6#define GP_REG_COUNT (0x7c / 4)
7#define DC_REG_COUNT (0xf0 / 4)
8#define VP_REG_COUNT (0x158 / 8)
9#define FP_REG_COUNT (0x60 / 8)
10
11#define DC_PAL_COUNT 0x104
12#define DC_HFILT_COUNT 0x100
13#define DC_VFILT_COUNT 0x100
14#define VP_COEFF_SIZE 0x1000
15
6#define OUTPUT_CRT 0x01 16#define OUTPUT_CRT 0x01
7#define OUTPUT_PANEL 0x02 17#define OUTPUT_PANEL 0x02
8 18
@@ -12,6 +22,27 @@ struct lxfb_par {
12 void __iomem *gp_regs; 22 void __iomem *gp_regs;
13 void __iomem *dc_regs; 23 void __iomem *dc_regs;
14 void __iomem *vp_regs; 24 void __iomem *vp_regs;
25#ifdef CONFIG_PM
26 int powered_down;
27
28 /* register state, for power mgmt functionality */
29 struct {
30 uint64_t padsel;
31 uint64_t dotpll;
32 uint64_t dfglcfg;
33 uint64_t dcspare;
34 } msr;
35
36 uint32_t gp[GP_REG_COUNT];
37 uint32_t dc[DC_REG_COUNT];
38 uint64_t vp[VP_REG_COUNT];
39 uint64_t fp[FP_REG_COUNT];
40
41 uint32_t pal[DC_PAL_COUNT];
42 uint32_t hcoeff[DC_HFILT_COUNT * 2];
43 uint32_t vcoeff[DC_VFILT_COUNT];
44 uint32_t vp_coeff[VP_COEFF_SIZE / 4];
45#endif
15}; 46};
16 47
17static inline unsigned int lx_get_pitch(unsigned int xres, int bpp) 48static inline unsigned int lx_get_pitch(unsigned int xres, int bpp)
@@ -27,6 +58,11 @@ int lx_blank_display(struct fb_info *, int);
27void lx_set_palette_reg(struct fb_info *, unsigned int, unsigned int, 58void lx_set_palette_reg(struct fb_info *, unsigned int, unsigned int,
28 unsigned int, unsigned int); 59 unsigned int, unsigned int);
29 60
61#ifdef CONFIG_PM
62int lx_powerdown(struct fb_info *info);
63int lx_powerup(struct fb_info *info);
64#endif
65
30 66
31/* Graphics Processor registers (table 6-29 from the data book) */ 67/* Graphics Processor registers (table 6-29 from the data book) */
32enum gp_registers { 68enum gp_registers {
@@ -182,6 +218,9 @@ enum dc_registers {
182#define DC_DV_CTL_DV_LINE_SIZE_2K (1 << 10) 218#define DC_DV_CTL_DV_LINE_SIZE_2K (1 << 10)
183#define DC_DV_CTL_DV_LINE_SIZE_4K (1 << 11) 219#define DC_DV_CTL_DV_LINE_SIZE_4K (1 << 11)
184#define DC_DV_CTL_DV_LINE_SIZE_8K ((1 << 10) | (1 << 11)) 220#define DC_DV_CTL_DV_LINE_SIZE_8K ((1 << 10) | (1 << 11))
221#define DC_DV_CTL_CLEAR_DV_RAM (1 << 0)
222
223#define DC_IRQ_FILT_CTL_H_FILT_SEL (1 << 10)
185 224
186#define DC_CLR_KEY_CLR_KEY_EN (1 << 24) 225#define DC_CLR_KEY_CLR_KEY_EN (1 << 24)
187 226
@@ -267,6 +306,8 @@ enum vp_registers {
267 VP_A2YE, 306 VP_A2YE,
268 307
269 VP_A3YE, /* 0x150 */ 308 VP_A3YE, /* 0x150 */
309
310 VP_VCR = 0x1000, /* 0x1000 - 0x1fff */
270}; 311};
271 312
272#define VP_VCFG_VID_EN (1 << 0) 313#define VP_VCFG_VID_EN (1 << 0)
@@ -319,6 +360,10 @@ enum fp_registers {
319#define FP_PT2_SCRC (1 << 27) /* shfclk free */ 360#define FP_PT2_SCRC (1 << 27) /* shfclk free */
320 361
321#define FP_PM_P (1 << 24) /* panel power ctl */ 362#define FP_PM_P (1 << 24) /* panel power ctl */
363#define FP_PM_PANEL_PWR_UP (1 << 3) /* r/o */
364#define FP_PM_PANEL_PWR_DOWN (1 << 2) /* r/o */
365#define FP_PM_PANEL_OFF (1 << 1) /* r/o */
366#define FP_PM_PANEL_ON (1 << 0) /* r/o */
322 367
323#define FP_DFC_BC ((1 << 4) | (1 << 5) | (1 << 6)) 368#define FP_DFC_BC ((1 << 4) | (1 << 5) | (1 << 6))
324 369
diff --git a/drivers/video/geode/lxfb_core.c b/drivers/video/geode/lxfb_core.c
index 19eabef1077a..b565882eee37 100644
--- a/drivers/video/geode/lxfb_core.c
+++ b/drivers/video/geode/lxfb_core.c
@@ -428,6 +428,45 @@ static struct fb_info * __init lxfb_init_fbinfo(struct device *dev)
428 return info; 428 return info;
429} 429}
430 430
431#ifdef CONFIG_PM
432static int lxfb_suspend(struct pci_dev *pdev, pm_message_t state)
433{
434 struct fb_info *info = pci_get_drvdata(pdev);
435
436 if (state.event == PM_EVENT_SUSPEND) {
437 acquire_console_sem();
438 lx_powerdown(info);
439 fb_set_suspend(info, 1);
440 release_console_sem();
441 }
442
443 /* there's no point in setting PCI states; we emulate PCI, so
444 * we don't end up getting power savings anyways */
445
446 return 0;
447}
448
449static int lxfb_resume(struct pci_dev *pdev)
450{
451 struct fb_info *info = pci_get_drvdata(pdev);
452 int ret;
453
454 acquire_console_sem();
455 ret = lx_powerup(info);
456 if (ret) {
457 printk(KERN_ERR "lxfb: power up failed!\n");
458 return ret;
459 }
460
461 fb_set_suspend(info, 0);
462 release_console_sem();
463 return 0;
464}
465#else
466#define lxfb_suspend NULL
467#define lxfb_resume NULL
468#endif
469
431static int __init lxfb_probe(struct pci_dev *pdev, 470static int __init lxfb_probe(struct pci_dev *pdev,
432 const struct pci_device_id *id) 471 const struct pci_device_id *id)
433{ 472{
@@ -553,6 +592,8 @@ static struct pci_driver lxfb_driver = {
553 .id_table = lxfb_id_table, 592 .id_table = lxfb_id_table,
554 .probe = lxfb_probe, 593 .probe = lxfb_probe,
555 .remove = lxfb_remove, 594 .remove = lxfb_remove,
595 .suspend = lxfb_suspend,
596 .resume = lxfb_resume,
556}; 597};
557 598
558#ifndef MODULE 599#ifndef MODULE
diff --git a/drivers/video/geode/lxfb_ops.c b/drivers/video/geode/lxfb_ops.c
index a68def88c92b..7abc439101ce 100644
--- a/drivers/video/geode/lxfb_ops.c
+++ b/drivers/video/geode/lxfb_ops.c
@@ -232,8 +232,8 @@ static void lx_graphics_disable(struct fb_info *info)
232 val = read_dc(par, DC_CLR_KEY); 232 val = read_dc(par, DC_CLR_KEY);
233 write_dc(par, DC_CLR_KEY, val & ~DC_CLR_KEY_CLR_KEY_EN); 233 write_dc(par, DC_CLR_KEY, val & ~DC_CLR_KEY_CLR_KEY_EN);
234 234
235 /* We don't actually blank the panel, due to the long latency 235 /* turn off the panel */
236 involved with bringing it back */ 236 write_fp(par, FP_PM, read_fp(par, FP_PM) & ~FP_PM_P);
237 237
238 val = read_vp(par, VP_MISC) | VP_MISC_DACPWRDN; 238 val = read_vp(par, VP_MISC) | VP_MISC_DACPWRDN;
239 write_vp(par, VP_MISC, val); 239 write_vp(par, VP_MISC, val);
@@ -321,13 +321,8 @@ static void lx_graphics_enable(struct fb_info *info)
321 } 321 }
322 322
323 /* Turn the panel on (if it isn't already) */ 323 /* Turn the panel on (if it isn't already) */
324 324 if (par->output & OUTPUT_PANEL)
325 if (par->output & OUTPUT_PANEL) { 325 write_fp(par, FP_PM, read_fp(par, FP_PM) | FP_PM_P);
326 temp = read_fp(par, FP_PM);
327
328 if (!(temp & 0x09))
329 write_fp(par, FP_PM, temp | FP_PM_P);
330 }
331} 326}
332 327
333unsigned int lx_framebuffer_size(void) 328unsigned int lx_framebuffer_size(void)
@@ -575,3 +570,253 @@ int lx_blank_display(struct fb_info *info, int blank_mode)
575 570
576 return 0; 571 return 0;
577} 572}
573
574#ifdef CONFIG_PM
575
576static void lx_save_regs(struct lxfb_par *par)
577{
578 uint32_t filt;
579 int i;
580
581 /* wait for the BLT engine to stop being busy */
582 do {
583 i = read_gp(par, GP_BLT_STATUS);
584 } while ((i & GP_BLT_STATUS_PB) || !(i & GP_BLT_STATUS_CE));
585
586 /* save MSRs */
587 rdmsrl(MSR_LX_MSR_PADSEL, par->msr.padsel);
588 rdmsrl(MSR_GLCP_DOTPLL, par->msr.dotpll);
589 rdmsrl(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg);
590 rdmsrl(MSR_LX_SPARE_MSR, par->msr.dcspare);
591
592 write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
593
594 /* save registers */
595 memcpy(par->gp, par->gp_regs, sizeof(par->gp));
596 memcpy(par->dc, par->dc_regs, sizeof(par->dc));
597 memcpy(par->vp, par->vp_regs, sizeof(par->vp));
598 memcpy(par->fp, par->vp_regs + VP_FP_START, sizeof(par->fp));
599
600 /* save the palette */
601 write_dc(par, DC_PAL_ADDRESS, 0);
602 for (i = 0; i < ARRAY_SIZE(par->pal); i++)
603 par->pal[i] = read_dc(par, DC_PAL_DATA);
604
605 /* save the horizontal filter coefficients */
606 filt = par->dc[DC_IRQ_FILT_CTL] | DC_IRQ_FILT_CTL_H_FILT_SEL;
607 for (i = 0; i < ARRAY_SIZE(par->hcoeff); i += 2) {
608 write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
609 par->hcoeff[i] = read_dc(par, DC_FILT_COEFF1);
610 par->hcoeff[i + 1] = read_dc(par, DC_FILT_COEFF2);
611 }
612
613 /* save the vertical filter coefficients */
614 filt &= ~DC_IRQ_FILT_CTL_H_FILT_SEL;
615 for (i = 0; i < ARRAY_SIZE(par->vcoeff); i++) {
616 write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
617 par->vcoeff[i] = read_dc(par, DC_FILT_COEFF1);
618 }
619
620 /* save video coeff ram */
621 memcpy(par->vp_coeff, par->vp_regs + VP_VCR, sizeof(par->vp_coeff));
622}
623
624static void lx_restore_gfx_proc(struct lxfb_par *par)
625{
626 int i;
627
628 /* a bunch of registers require GP_RASTER_MODE to be set first */
629 write_gp(par, GP_RASTER_MODE, par->gp[GP_RASTER_MODE]);
630
631 for (i = 0; i < ARRAY_SIZE(par->gp); i++) {
632 switch (i) {
633 case GP_RASTER_MODE:
634 case GP_VECTOR_MODE:
635 case GP_BLT_MODE:
636 case GP_BLT_STATUS:
637 case GP_HST_SRC:
638 /* FIXME: restore LUT data */
639 case GP_LUT_INDEX:
640 case GP_LUT_DATA:
641 /* don't restore these registers */
642 break;
643
644 default:
645 write_gp(par, i, par->gp[i]);
646 }
647 }
648}
649
650static void lx_restore_display_ctlr(struct lxfb_par *par)
651{
652 uint32_t filt;
653 int i;
654
655 wrmsrl(MSR_LX_SPARE_MSR, par->msr.dcspare);
656
657 for (i = 0; i < ARRAY_SIZE(par->dc); i++) {
658 switch (i) {
659 case DC_UNLOCK:
660 /* unlock the DC; runs first */
661 write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
662 break;
663
664 case DC_GENERAL_CFG:
665 case DC_DISPLAY_CFG:
666 /* disable all while restoring */
667 write_dc(par, i, 0);
668 break;
669
670 case DC_DV_CTL:
671 /* set all ram to dirty */
672 write_dc(par, i, par->dc[i] | DC_DV_CTL_CLEAR_DV_RAM);
673
674 case DC_RSVD_1:
675 case DC_RSVD_2:
676 case DC_RSVD_3:
677 case DC_LINE_CNT:
678 case DC_PAL_ADDRESS:
679 case DC_PAL_DATA:
680 case DC_DFIFO_DIAG:
681 case DC_CFIFO_DIAG:
682 case DC_FILT_COEFF1:
683 case DC_FILT_COEFF2:
684 case DC_RSVD_4:
685 case DC_RSVD_5:
686 /* don't restore these registers */
687 break;
688
689 default:
690 write_dc(par, i, par->dc[i]);
691 }
692 }
693
694 /* restore the palette */
695 write_dc(par, DC_PAL_ADDRESS, 0);
696 for (i = 0; i < ARRAY_SIZE(par->pal); i++)
697 write_dc(par, DC_PAL_DATA, par->pal[i]);
698
699 /* restore the horizontal filter coefficients */
700 filt = par->dc[DC_IRQ_FILT_CTL] | DC_IRQ_FILT_CTL_H_FILT_SEL;
701 for (i = 0; i < ARRAY_SIZE(par->hcoeff); i += 2) {
702 write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
703 write_dc(par, DC_FILT_COEFF1, par->hcoeff[i]);
704 write_dc(par, DC_FILT_COEFF2, par->hcoeff[i + 1]);
705 }
706
707 /* restore the vertical filter coefficients */
708 filt &= ~DC_IRQ_FILT_CTL_H_FILT_SEL;
709 for (i = 0; i < ARRAY_SIZE(par->vcoeff); i++) {
710 write_dc(par, DC_IRQ_FILT_CTL, (filt & 0xffffff00) | i);
711 write_dc(par, DC_FILT_COEFF1, par->vcoeff[i]);
712 }
713}
714
715static void lx_restore_video_proc(struct lxfb_par *par)
716{
717 int i;
718
719 wrmsrl(MSR_LX_GLD_MSR_CONFIG, par->msr.dfglcfg);
720 wrmsrl(MSR_LX_MSR_PADSEL, par->msr.padsel);
721
722 for (i = 0; i < ARRAY_SIZE(par->vp); i++) {
723 switch (i) {
724 case VP_VCFG:
725 case VP_DCFG:
726 case VP_PAR:
727 case VP_PDR:
728 case VP_CCS:
729 case VP_RSVD_0:
730 /* case VP_VDC: */ /* why should this not be restored? */
731 case VP_RSVD_1:
732 case VP_CRC32:
733 /* don't restore these registers */
734 break;
735
736 default:
737 write_vp(par, i, par->vp[i]);
738 }
739 }
740
741 /* restore video coeff ram */
742 memcpy(par->vp_regs + VP_VCR, par->vp_coeff, sizeof(par->vp_coeff));
743}
744
745static void lx_restore_regs(struct lxfb_par *par)
746{
747 int i;
748
749 lx_set_dotpll((u32) (par->msr.dotpll >> 32));
750 lx_restore_gfx_proc(par);
751 lx_restore_display_ctlr(par);
752 lx_restore_video_proc(par);
753
754 /* Flat Panel */
755 for (i = 0; i < ARRAY_SIZE(par->fp); i++) {
756 switch (i) {
757 case FP_PM:
758 case FP_RSVD_0:
759 case FP_RSVD_1:
760 case FP_RSVD_2:
761 case FP_RSVD_3:
762 case FP_RSVD_4:
763 /* don't restore these registers */
764 break;
765
766 default:
767 write_fp(par, i, par->fp[i]);
768 }
769 }
770
771 /* control the panel */
772 if (par->fp[FP_PM] & FP_PM_P) {
773 /* power on the panel if not already power{ed,ing} on */
774 if (!(read_fp(par, FP_PM) &
775 (FP_PM_PANEL_ON|FP_PM_PANEL_PWR_UP)))
776 write_fp(par, FP_PM, par->fp[FP_PM]);
777 } else {
778 /* power down the panel if not already power{ed,ing} down */
779 if (!(read_fp(par, FP_PM) &
780 (FP_PM_PANEL_OFF|FP_PM_PANEL_PWR_DOWN)))
781 write_fp(par, FP_PM, par->fp[FP_PM]);
782 }
783
784 /* turn everything on */
785 write_vp(par, VP_VCFG, par->vp[VP_VCFG]);
786 write_vp(par, VP_DCFG, par->vp[VP_DCFG]);
787 write_dc(par, DC_DISPLAY_CFG, par->dc[DC_DISPLAY_CFG]);
788 /* do this last; it will enable the FIFO load */
789 write_dc(par, DC_GENERAL_CFG, par->dc[DC_GENERAL_CFG]);
790
791 /* lock the door behind us */
792 write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
793}
794
795int lx_powerdown(struct fb_info *info)
796{
797 struct lxfb_par *par = info->par;
798
799 if (par->powered_down)
800 return 0;
801
802 lx_save_regs(par);
803 lx_graphics_disable(info);
804
805 par->powered_down = 1;
806 return 0;
807}
808
809int lx_powerup(struct fb_info *info)
810{
811 struct lxfb_par *par = info->par;
812
813 if (!par->powered_down)
814 return 0;
815
816 lx_restore_regs(par);
817
818 par->powered_down = 0;
819 return 0;
820}
821
822#endif