diff options
author | Ville Syrjala <syrjala@sci.fi> | 2006-12-08 05:40:45 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-08 11:29:07 -0500 |
commit | efc08a75d3a2d449edab7d1bee312eaa591f7669 (patch) | |
tree | a22c27c69cb226dd6e1fc9dc4c82f57cd81f289e /drivers/video | |
parent | 4ec3fd71e4f5d1201cb7d2374ffdb4f328091fb4 (diff) |
[PATCH] atyfb: Improve power management
Some register were only set in aty_init() which is not called on resume. I
added the aty_resume_chip() function to reset those registers on resume.
Susped-to-ram now works on a HP Omnibook 6000 with acpi_sleep=s3_bios.
Signed-off-by: Ville Syrjala <syrjala@sci.fi>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/aty/atyfb.h | 2 | ||||
-rw-r--r-- | drivers/video/aty/atyfb_base.c | 61 | ||||
-rw-r--r-- | drivers/video/aty/mach64_ct.c | 39 |
3 files changed, 68 insertions, 34 deletions
diff --git a/drivers/video/aty/atyfb.h b/drivers/video/aty/atyfb.h index c9b35c6b0b69..f72faff33c0c 100644 --- a/drivers/video/aty/atyfb.h +++ b/drivers/video/aty/atyfb.h | |||
@@ -185,6 +185,7 @@ struct atyfb_par { | |||
185 | int mtrr_aper; | 185 | int mtrr_aper; |
186 | int mtrr_reg; | 186 | int mtrr_reg; |
187 | #endif | 187 | #endif |
188 | u32 mem_cntl; | ||
188 | }; | 189 | }; |
189 | 190 | ||
190 | /* | 191 | /* |
@@ -314,6 +315,7 @@ struct aty_pll_ops { | |||
314 | void (*set_pll) (const struct fb_info * info, const union aty_pll * pll); | 315 | void (*set_pll) (const struct fb_info * info, const union aty_pll * pll); |
315 | void (*get_pll) (const struct fb_info *info, union aty_pll * pll); | 316 | void (*get_pll) (const struct fb_info *info, union aty_pll * pll); |
316 | int (*init_pll) (const struct fb_info * info, union aty_pll * pll); | 317 | int (*init_pll) (const struct fb_info * info, union aty_pll * pll); |
318 | void (*resume_pll)(const struct fb_info *info, union aty_pll *pll); | ||
317 | }; | 319 | }; |
318 | 320 | ||
319 | extern const struct aty_pll_ops aty_pll_ati18818_1; /* ATI 18818 */ | 321 | extern const struct aty_pll_ops aty_pll_ati18818_1; /* ATI 18818 */ |
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index a9c73c257f3a..176f9b85cdbe 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c | |||
@@ -242,6 +242,7 @@ static int atyfb_sync(struct fb_info *info); | |||
242 | */ | 242 | */ |
243 | 243 | ||
244 | static int aty_init(struct fb_info *info); | 244 | static int aty_init(struct fb_info *info); |
245 | static void aty_resume_chip(struct fb_info *info); | ||
245 | #ifdef CONFIG_ATARI | 246 | #ifdef CONFIG_ATARI |
246 | static int store_video_par(char *videopar, unsigned char m64_num); | 247 | static int store_video_par(char *videopar, unsigned char m64_num); |
247 | #endif | 248 | #endif |
@@ -1971,6 +1972,7 @@ static void atyfb_palette(int enter) | |||
1971 | 1972 | ||
1972 | #if defined(CONFIG_PM) && defined(CONFIG_PCI) | 1973 | #if defined(CONFIG_PM) && defined(CONFIG_PCI) |
1973 | 1974 | ||
1975 | #ifdef CONFIG_PPC_PMAC | ||
1974 | /* Power management routines. Those are used for PowerBook sleep. | 1976 | /* Power management routines. Those are used for PowerBook sleep. |
1975 | */ | 1977 | */ |
1976 | static int aty_power_mgmt(int sleep, struct atyfb_par *par) | 1978 | static int aty_power_mgmt(int sleep, struct atyfb_par *par) |
@@ -2027,21 +2029,13 @@ static int aty_power_mgmt(int sleep, struct atyfb_par *par) | |||
2027 | 2029 | ||
2028 | return timeout ? 0 : -EIO; | 2030 | return timeout ? 0 : -EIO; |
2029 | } | 2031 | } |
2032 | #endif | ||
2030 | 2033 | ||
2031 | static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) | 2034 | static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) |
2032 | { | 2035 | { |
2033 | struct fb_info *info = pci_get_drvdata(pdev); | 2036 | struct fb_info *info = pci_get_drvdata(pdev); |
2034 | struct atyfb_par *par = (struct atyfb_par *) info->par; | 2037 | struct atyfb_par *par = (struct atyfb_par *) info->par; |
2035 | 2038 | ||
2036 | #ifndef CONFIG_PPC_PMAC | ||
2037 | /* HACK ALERT ! Once I find a proper way to say to each driver | ||
2038 | * individually what will happen with it's PCI slot, I'll change | ||
2039 | * that. On laptops, the AGP slot is just unclocked, so D2 is | ||
2040 | * expected, while on desktops, the card is powered off | ||
2041 | */ | ||
2042 | return 0; | ||
2043 | #endif /* CONFIG_PPC_PMAC */ | ||
2044 | |||
2045 | if (state.event == pdev->dev.power.power_state.event) | 2039 | if (state.event == pdev->dev.power.power_state.event) |
2046 | return 0; | 2040 | return 0; |
2047 | 2041 | ||
@@ -2059,6 +2053,7 @@ static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) | |||
2059 | par->asleep = 1; | 2053 | par->asleep = 1; |
2060 | par->lock_blank = 1; | 2054 | par->lock_blank = 1; |
2061 | 2055 | ||
2056 | #ifdef CONFIG_PPC_PMAC | ||
2062 | /* Set chip to "suspend" mode */ | 2057 | /* Set chip to "suspend" mode */ |
2063 | if (aty_power_mgmt(1, par)) { | 2058 | if (aty_power_mgmt(1, par)) { |
2064 | par->asleep = 0; | 2059 | par->asleep = 0; |
@@ -2068,6 +2063,9 @@ static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) | |||
2068 | release_console_sem(); | 2063 | release_console_sem(); |
2069 | return -EIO; | 2064 | return -EIO; |
2070 | } | 2065 | } |
2066 | #else | ||
2067 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | ||
2068 | #endif | ||
2071 | 2069 | ||
2072 | release_console_sem(); | 2070 | release_console_sem(); |
2073 | 2071 | ||
@@ -2086,8 +2084,15 @@ static int atyfb_pci_resume(struct pci_dev *pdev) | |||
2086 | 2084 | ||
2087 | acquire_console_sem(); | 2085 | acquire_console_sem(); |
2088 | 2086 | ||
2087 | #ifdef CONFIG_PPC_PMAC | ||
2089 | if (pdev->dev.power.power_state.event == 2) | 2088 | if (pdev->dev.power.power_state.event == 2) |
2090 | aty_power_mgmt(0, par); | 2089 | aty_power_mgmt(0, par); |
2090 | #else | ||
2091 | pci_set_power_state(pdev, PCI_D0); | ||
2092 | #endif | ||
2093 | |||
2094 | aty_resume_chip(info); | ||
2095 | |||
2091 | par->asleep = 0; | 2096 | par->asleep = 0; |
2092 | 2097 | ||
2093 | /* Restore display */ | 2098 | /* Restore display */ |
@@ -2339,7 +2344,6 @@ static int __devinit aty_init(struct fb_info *info) | |||
2339 | const char *ramname = NULL, *xtal; | 2344 | const char *ramname = NULL, *xtal; |
2340 | int gtb_memsize, has_var = 0; | 2345 | int gtb_memsize, has_var = 0; |
2341 | struct fb_var_screeninfo var; | 2346 | struct fb_var_screeninfo var; |
2342 | u32 i; | ||
2343 | 2347 | ||
2344 | init_waitqueue_head(&par->vblank.wait); | 2348 | init_waitqueue_head(&par->vblank.wait); |
2345 | spin_lock_init(&par->int_lock); | 2349 | spin_lock_init(&par->int_lock); |
@@ -2470,10 +2474,10 @@ static int __devinit aty_init(struct fb_info *info) | |||
2470 | if(par->pll_ops->get_pll) | 2474 | if(par->pll_ops->get_pll) |
2471 | par->pll_ops->get_pll(info, &saved_pll); | 2475 | par->pll_ops->get_pll(info, &saved_pll); |
2472 | 2476 | ||
2473 | i = aty_ld_le32(MEM_CNTL, par); | 2477 | par->mem_cntl = aty_ld_le32(MEM_CNTL, par); |
2474 | gtb_memsize = M64_HAS(GTB_DSP); | 2478 | gtb_memsize = M64_HAS(GTB_DSP); |
2475 | if (gtb_memsize) | 2479 | if (gtb_memsize) |
2476 | switch (i & 0xF) { /* 0xF used instead of MEM_SIZE_ALIAS */ | 2480 | switch (par->mem_cntl & 0xF) { /* 0xF used instead of MEM_SIZE_ALIAS */ |
2477 | case MEM_SIZE_512K: | 2481 | case MEM_SIZE_512K: |
2478 | info->fix.smem_len = 0x80000; | 2482 | info->fix.smem_len = 0x80000; |
2479 | break; | 2483 | break; |
@@ -2495,7 +2499,7 @@ static int __devinit aty_init(struct fb_info *info) | |||
2495 | default: | 2499 | default: |
2496 | info->fix.smem_len = 0x80000; | 2500 | info->fix.smem_len = 0x80000; |
2497 | } else | 2501 | } else |
2498 | switch (i & MEM_SIZE_ALIAS) { | 2502 | switch (par->mem_cntl & MEM_SIZE_ALIAS) { |
2499 | case MEM_SIZE_512K: | 2503 | case MEM_SIZE_512K: |
2500 | info->fix.smem_len = 0x80000; | 2504 | info->fix.smem_len = 0x80000; |
2501 | break; | 2505 | break; |
@@ -2525,20 +2529,20 @@ static int __devinit aty_init(struct fb_info *info) | |||
2525 | 2529 | ||
2526 | if (vram) { | 2530 | if (vram) { |
2527 | info->fix.smem_len = vram * 1024; | 2531 | info->fix.smem_len = vram * 1024; |
2528 | i = i & ~(gtb_memsize ? 0xF : MEM_SIZE_ALIAS); | 2532 | par->mem_cntl &= ~(gtb_memsize ? 0xF : MEM_SIZE_ALIAS); |
2529 | if (info->fix.smem_len <= 0x80000) | 2533 | if (info->fix.smem_len <= 0x80000) |
2530 | i |= MEM_SIZE_512K; | 2534 | par->mem_cntl |= MEM_SIZE_512K; |
2531 | else if (info->fix.smem_len <= 0x100000) | 2535 | else if (info->fix.smem_len <= 0x100000) |
2532 | i |= MEM_SIZE_1M; | 2536 | par->mem_cntl |= MEM_SIZE_1M; |
2533 | else if (info->fix.smem_len <= 0x200000) | 2537 | else if (info->fix.smem_len <= 0x200000) |
2534 | i |= gtb_memsize ? MEM_SIZE_2M_GTB : MEM_SIZE_2M; | 2538 | par->mem_cntl |= gtb_memsize ? MEM_SIZE_2M_GTB : MEM_SIZE_2M; |
2535 | else if (info->fix.smem_len <= 0x400000) | 2539 | else if (info->fix.smem_len <= 0x400000) |
2536 | i |= gtb_memsize ? MEM_SIZE_4M_GTB : MEM_SIZE_4M; | 2540 | par->mem_cntl |= gtb_memsize ? MEM_SIZE_4M_GTB : MEM_SIZE_4M; |
2537 | else if (info->fix.smem_len <= 0x600000) | 2541 | else if (info->fix.smem_len <= 0x600000) |
2538 | i |= gtb_memsize ? MEM_SIZE_6M_GTB : MEM_SIZE_6M; | 2542 | par->mem_cntl |= gtb_memsize ? MEM_SIZE_6M_GTB : MEM_SIZE_6M; |
2539 | else | 2543 | else |
2540 | i |= gtb_memsize ? MEM_SIZE_8M_GTB : MEM_SIZE_8M; | 2544 | par->mem_cntl |= gtb_memsize ? MEM_SIZE_8M_GTB : MEM_SIZE_8M; |
2541 | aty_st_le32(MEM_CNTL, i, par); | 2545 | aty_st_le32(MEM_CNTL, par->mem_cntl, par); |
2542 | } | 2546 | } |
2543 | 2547 | ||
2544 | /* | 2548 | /* |
@@ -2584,6 +2588,8 @@ static int __devinit aty_init(struct fb_info *info) | |||
2584 | #endif | 2588 | #endif |
2585 | if(par->pll_ops->init_pll) | 2589 | if(par->pll_ops->init_pll) |
2586 | par->pll_ops->init_pll(info, &par->pll); | 2590 | par->pll_ops->init_pll(info, &par->pll); |
2591 | if (par->pll_ops->resume_pll) | ||
2592 | par->pll_ops->resume_pll(info, &par->pll); | ||
2587 | 2593 | ||
2588 | /* | 2594 | /* |
2589 | * Last page of 8 MB (4 MB on ISA) aperture is MMIO, | 2595 | * Last page of 8 MB (4 MB on ISA) aperture is MMIO, |
@@ -2755,6 +2761,19 @@ aty_init_exit: | |||
2755 | return -1; | 2761 | return -1; |
2756 | } | 2762 | } |
2757 | 2763 | ||
2764 | static void aty_resume_chip(struct fb_info *info) | ||
2765 | { | ||
2766 | struct atyfb_par *par = info->par; | ||
2767 | |||
2768 | aty_st_le32(MEM_CNTL, par->mem_cntl, par); | ||
2769 | |||
2770 | if (par->pll_ops->resume_pll) | ||
2771 | par->pll_ops->resume_pll(info, &par->pll); | ||
2772 | |||
2773 | if (par->aux_start) | ||
2774 | aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, par) | BUS_APER_REG_DIS, par); | ||
2775 | } | ||
2776 | |||
2758 | #ifdef CONFIG_ATARI | 2777 | #ifdef CONFIG_ATARI |
2759 | static int __devinit store_video_par(char *video_str, unsigned char m64_num) | 2778 | static int __devinit store_video_par(char *video_str, unsigned char m64_num) |
2760 | { | 2779 | { |
diff --git a/drivers/video/aty/mach64_ct.c b/drivers/video/aty/mach64_ct.c index 10232cc2dc76..f3b487b8710b 100644 --- a/drivers/video/aty/mach64_ct.c +++ b/drivers/video/aty/mach64_ct.c | |||
@@ -398,8 +398,8 @@ static int __devinit aty_init_pll_ct(const struct fb_info *info, | |||
398 | union aty_pll *pll) | 398 | union aty_pll *pll) |
399 | { | 399 | { |
400 | struct atyfb_par *par = (struct atyfb_par *) info->par; | 400 | struct atyfb_par *par = (struct atyfb_par *) info->par; |
401 | u8 mpost_div, xpost_div, sclk_post_div_real, sclk_fb_div, spll_cntl2; | 401 | u8 mpost_div, xpost_div, sclk_post_div_real; |
402 | u32 q, i, memcntl, trp; | 402 | u32 q, memcntl, trp; |
403 | u32 dsp_config, dsp_on_off, vga_dsp_config, vga_dsp_on_off; | 403 | u32 dsp_config, dsp_on_off, vga_dsp_config, vga_dsp_on_off; |
404 | #ifdef DEBUG | 404 | #ifdef DEBUG |
405 | int pllmclk, pllsclk; | 405 | int pllmclk, pllsclk; |
@@ -575,14 +575,30 @@ static int __devinit aty_init_pll_ct(const struct fb_info *info, | |||
575 | mpost_div += (q < 32*8); | 575 | mpost_div += (q < 32*8); |
576 | } | 576 | } |
577 | sclk_post_div_real = postdividers[mpost_div]; | 577 | sclk_post_div_real = postdividers[mpost_div]; |
578 | sclk_fb_div = q * sclk_post_div_real / 8; | 578 | pll->ct.sclk_fb_div = q * sclk_post_div_real / 8; |
579 | spll_cntl2 = mpost_div << 4; | 579 | pll->ct.spll_cntl2 = mpost_div << 4; |
580 | #ifdef DEBUG | 580 | #ifdef DEBUG |
581 | pllsclk = (1000000 * 2 * sclk_fb_div) / | 581 | pllsclk = (1000000 * 2 * pll->ct.sclk_fb_div) / |
582 | (par->ref_clk_per * pll->ct.pll_ref_div); | 582 | (par->ref_clk_per * pll->ct.pll_ref_div); |
583 | printk("atyfb(%s): use sclk, pllsclk=%d MHz, sclk=mclk=%d MHz\n", | 583 | printk("atyfb(%s): use sclk, pllsclk=%d MHz, sclk=mclk=%d MHz\n", |
584 | __FUNCTION__, pllsclk, pllsclk / sclk_post_div_real); | 584 | __FUNCTION__, pllsclk, pllsclk / sclk_post_div_real); |
585 | #endif | 585 | #endif |
586 | } | ||
587 | |||
588 | /* Disable the extra precision pixel clock controls since we do not use them. */ | ||
589 | pll->ct.ext_vpll_cntl = aty_ld_pll_ct(EXT_VPLL_CNTL, par); | ||
590 | pll->ct.ext_vpll_cntl &= ~(EXT_VPLL_EN | EXT_VPLL_VGA_EN | EXT_VPLL_INSYNC); | ||
591 | |||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | static void aty_resume_pll_ct(const struct fb_info *info, | ||
596 | union aty_pll *pll) | ||
597 | { | ||
598 | struct atyfb_par *par = info->par; | ||
599 | |||
600 | if (par->mclk_per != par->xclk_per) { | ||
601 | int i; | ||
586 | /* | 602 | /* |
587 | * This disables the sclk, crashes the computer as reported: | 603 | * This disables the sclk, crashes the computer as reported: |
588 | * aty_st_pll_ct(SPLL_CNTL2, 3, info); | 604 | * aty_st_pll_ct(SPLL_CNTL2, 3, info); |
@@ -590,8 +606,8 @@ static int __devinit aty_init_pll_ct(const struct fb_info *info, | |||
590 | * So it seems the sclk must be enabled before it is used; | 606 | * So it seems the sclk must be enabled before it is used; |
591 | * so PLL_GEN_CNTL must be programmed *after* the sclk. | 607 | * so PLL_GEN_CNTL must be programmed *after* the sclk. |
592 | */ | 608 | */ |
593 | aty_st_pll_ct(SCLK_FB_DIV, sclk_fb_div, par); | 609 | aty_st_pll_ct(SCLK_FB_DIV, pll->ct.sclk_fb_div, par); |
594 | aty_st_pll_ct(SPLL_CNTL2, spll_cntl2, par); | 610 | aty_st_pll_ct(SPLL_CNTL2, pll->ct.spll_cntl2, par); |
595 | /* | 611 | /* |
596 | * The sclk has been started. However, I believe the first clock | 612 | * The sclk has been started. However, I believe the first clock |
597 | * ticks it generates are not very stable. Hope this primitive loop | 613 | * ticks it generates are not very stable. Hope this primitive loop |
@@ -605,11 +621,7 @@ static int __devinit aty_init_pll_ct(const struct fb_info *info, | |||
605 | aty_st_pll_ct(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par); | 621 | aty_st_pll_ct(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par); |
606 | aty_st_pll_ct(MCLK_FB_DIV, pll->ct.mclk_fb_div, par); | 622 | aty_st_pll_ct(MCLK_FB_DIV, pll->ct.mclk_fb_div, par); |
607 | aty_st_pll_ct(PLL_EXT_CNTL, pll->ct.pll_ext_cntl, par); | 623 | aty_st_pll_ct(PLL_EXT_CNTL, pll->ct.pll_ext_cntl, par); |
608 | /* Disable the extra precision pixel clock controls since we do not use them. */ | 624 | aty_st_pll_ct(EXT_VPLL_CNTL, pll->ct.ext_vpll_cntl, par); |
609 | aty_st_pll_ct(EXT_VPLL_CNTL, aty_ld_pll_ct(EXT_VPLL_CNTL, par) & | ||
610 | ~(EXT_VPLL_EN | EXT_VPLL_VGA_EN | EXT_VPLL_INSYNC), par); | ||
611 | |||
612 | return 0; | ||
613 | } | 625 | } |
614 | 626 | ||
615 | static int dummy(void) | 627 | static int dummy(void) |
@@ -626,5 +638,6 @@ const struct aty_pll_ops aty_pll_ct = { | |||
626 | .pll_to_var = aty_pll_to_var_ct, | 638 | .pll_to_var = aty_pll_to_var_ct, |
627 | .set_pll = aty_set_pll_ct, | 639 | .set_pll = aty_set_pll_ct, |
628 | .get_pll = aty_get_pll_ct, | 640 | .get_pll = aty_get_pll_ct, |
629 | .init_pll = aty_init_pll_ct | 641 | .init_pll = aty_init_pll_ct, |
642 | .resume_pll = aty_resume_pll_ct, | ||
630 | }; | 643 | }; |