aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/geode/lxfb_ops.c
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 /drivers/video/geode/lxfb_ops.c
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>
Diffstat (limited to 'drivers/video/geode/lxfb_ops.c')
-rw-r--r--drivers/video/geode/lxfb_ops.c263
1 files changed, 254 insertions, 9 deletions
diff --git a/drivers/video/geode/lxfb_ops.c b/drivers/video/geode/lxfb_ops.c
index a68def88c92..7abc439101c 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