diff options
author | Andres Salomon <dilinger@queued.net> | 2008-04-28 05:15:27 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-28 11:58:39 -0400 |
commit | f694e53bd0db69557ee8e0db2d1602818ff173b0 (patch) | |
tree | d904b492105055a2adf00c60fa0324c0905b3bbe /drivers/video/geode/lxfb_ops.c | |
parent | aec40532c4d1183fa1ec415bb7dae08e19fc6b01 (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.c | 263 |
1 files changed, 254 insertions, 9 deletions
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 | ||
333 | unsigned int lx_framebuffer_size(void) | 328 | unsigned 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 | |||
576 | static 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 | |||
624 | static 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 | |||
650 | static 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 | |||
715 | static 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 | |||
745 | static 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 | |||
795 | int 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 | |||
809 | int 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 | ||