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 | |
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>
-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 | ||