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 | |
| 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')
| -rw-r--r-- | drivers/video/geode/lxfb.h | 45 | ||||
| -rw-r--r-- | drivers/video/geode/lxfb_core.c | 41 | ||||
| -rw-r--r-- | drivers/video/geode/lxfb_ops.c | 263 |
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 | ||
| 17 | static inline unsigned int lx_get_pitch(unsigned int xres, int bpp) | 48 | static inline unsigned int lx_get_pitch(unsigned int xres, int bpp) |
| @@ -27,6 +58,11 @@ int lx_blank_display(struct fb_info *, int); | |||
| 27 | void lx_set_palette_reg(struct fb_info *, unsigned int, unsigned int, | 58 | void 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 | ||
| 62 | int lx_powerdown(struct fb_info *info); | ||
| 63 | int 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) */ |
| 32 | enum gp_registers { | 68 | enum 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 | ||
| 432 | static 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 | |||
| 449 | static 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 | |||
| 431 | static int __init lxfb_probe(struct pci_dev *pdev, | 470 | static 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 | ||
| 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 | ||
