diff options
| author | Volker Braun <vbraun@physics.upenn.edu> | 2006-07-30 06:04:18 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-31 16:28:45 -0400 |
| commit | 994aad251acab32a5d40d4a9501dc3e736562b6d (patch) | |
| tree | 4c816fa097d07b2c4a4f46d26023af94f9a26822 | |
| parent | 256154fbc31c25a8df4d398232acfa9d4892224c (diff) | |
[PATCH] radeonfb sleep fixes
Many IBM Thinkpad T4* models and some R* and X* with radeon video cards draw
too much power when suspended to RAM, reducing drastically the battery
lifetime. The solution is to enable suspend-to-D2 on these machines. They
are whitelisted through their subsystem vendor/device ID. This fixes
http://bugzilla.kernel.org/show_bug.cgi?id=3022
The patch introduces a framework to alter the pm_mode and reinit_func fields
of the radeonfb_info structure based on a whitelist. This should facilitate
future hardware-dependent workarounds. The workaround for the Samsung P35
that is already in the radeonfb code has been rewritten using this framework.
The behavior can be overridden with module options:
i) video=radeonfb:force_sleep=1
enable suspend-to-D2 also on non-whitelisted machines (useful for
testing new notebook models),
ii) video=radeonfb:ignore_devlist=1
Disable checking the whitelist and do not apply any workarounds.
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
| -rw-r--r-- | drivers/video/aty/radeon_base.c | 18 | ||||
| -rw-r--r-- | drivers/video/aty/radeon_pm.c | 146 | ||||
| -rw-r--r-- | drivers/video/aty/radeonfb.h | 6 |
3 files changed, 145 insertions, 25 deletions
diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 8d85fc58142e..8e3400d5dd21 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c | |||
| @@ -266,6 +266,8 @@ static int force_measure_pll = 0; | |||
| 266 | #ifdef CONFIG_MTRR | 266 | #ifdef CONFIG_MTRR |
| 267 | static int nomtrr = 0; | 267 | static int nomtrr = 0; |
| 268 | #endif | 268 | #endif |
| 269 | static int force_sleep; | ||
| 270 | static int ignore_devlist; | ||
| 269 | 271 | ||
| 270 | /* | 272 | /* |
| 271 | * prototypes | 273 | * prototypes |
| @@ -2327,9 +2329,9 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev, | |||
| 2327 | /* -2 is special: means ON on mobility chips and do not | 2329 | /* -2 is special: means ON on mobility chips and do not |
| 2328 | * change on others | 2330 | * change on others |
| 2329 | */ | 2331 | */ |
| 2330 | radeonfb_pm_init(rinfo, rinfo->is_mobility ? 1 : -1); | 2332 | radeonfb_pm_init(rinfo, rinfo->is_mobility ? 1 : -1, ignore_devlist, force_sleep); |
| 2331 | } else | 2333 | } else |
| 2332 | radeonfb_pm_init(rinfo, default_dynclk); | 2334 | radeonfb_pm_init(rinfo, default_dynclk, ignore_devlist, force_sleep); |
| 2333 | 2335 | ||
| 2334 | pci_set_drvdata(pdev, info); | 2336 | pci_set_drvdata(pdev, info); |
| 2335 | 2337 | ||
| @@ -2477,6 +2479,12 @@ static int __init radeonfb_setup (char *options) | |||
| 2477 | force_measure_pll = 1; | 2479 | force_measure_pll = 1; |
| 2478 | } else if (!strncmp(this_opt, "ignore_edid", 11)) { | 2480 | } else if (!strncmp(this_opt, "ignore_edid", 11)) { |
| 2479 | ignore_edid = 1; | 2481 | ignore_edid = 1; |
| 2482 | #if defined(CONFIG_PM) && defined(CONFIG_X86) | ||
| 2483 | } else if (!strncmp(this_opt, "force_sleep", 11)) { | ||
| 2484 | force_sleep = 1; | ||
| 2485 | } else if (!strncmp(this_opt, "ignore_devlist", 14)) { | ||
| 2486 | ignore_devlist = 1; | ||
| 2487 | #endif | ||
| 2480 | } else | 2488 | } else |
| 2481 | mode_option = this_opt; | 2489 | mode_option = this_opt; |
| 2482 | } | 2490 | } |
| @@ -2532,3 +2540,9 @@ module_param(panel_yres, int, 0); | |||
| 2532 | MODULE_PARM_DESC(panel_yres, "int: set panel yres"); | 2540 | MODULE_PARM_DESC(panel_yres, "int: set panel yres"); |
| 2533 | module_param(mode_option, charp, 0); | 2541 | module_param(mode_option, charp, 0); |
| 2534 | MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); | 2542 | MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); |
| 2543 | #if defined(CONFIG_PM) && defined(CONFIG_X86) | ||
| 2544 | module_param(force_sleep, bool, 0); | ||
| 2545 | MODULE_PARM_DESC(force_sleep, "bool: force D2 sleep mode on all hardware"); | ||
| 2546 | module_param(ignore_devlist, bool, 0); | ||
| 2547 | MODULE_PARM_DESC(ignore_devlist, "bool: ignore workarounds for bugs in specific laptops"); | ||
| 2548 | #endif | ||
diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c index c7091761cef4..f31e606a2ded 100644 --- a/drivers/video/aty/radeon_pm.c +++ b/drivers/video/aty/radeon_pm.c | |||
| @@ -27,6 +27,99 @@ | |||
| 27 | 27 | ||
| 28 | #include "ati_ids.h" | 28 | #include "ati_ids.h" |
| 29 | 29 | ||
| 30 | static void radeon_reinitialize_M10(struct radeonfb_info *rinfo); | ||
| 31 | |||
| 32 | /* | ||
| 33 | * Workarounds for bugs in PC laptops: | ||
| 34 | * - enable D2 sleep in some IBM Thinkpads | ||
| 35 | * - special case for Samsung P35 | ||
| 36 | * | ||
| 37 | * Whitelist by subsystem vendor/device because | ||
| 38 | * its the subsystem vendor's fault! | ||
| 39 | */ | ||
| 40 | |||
| 41 | #if defined(CONFIG_PM) && defined(CONFIG_X86) | ||
| 42 | struct radeon_device_id { | ||
| 43 | const char *ident; /* (arbitrary) Name */ | ||
| 44 | const unsigned short subsystem_vendor; /* Subsystem Vendor ID */ | ||
| 45 | const unsigned short subsystem_device; /* Subsystem Device ID */ | ||
| 46 | const enum radeon_pm_mode pm_mode_modifier; /* modify pm_mode */ | ||
| 47 | const reinit_function_ptr new_reinit_func; /* changed reinit_func */ | ||
| 48 | }; | ||
| 49 | |||
| 50 | #define BUGFIX(model, sv, sd, pm, fn) { \ | ||
| 51 | .ident = model, \ | ||
| 52 | .subsystem_vendor = sv, \ | ||
| 53 | .subsystem_device = sd, \ | ||
| 54 | .pm_mode_modifier = pm, \ | ||
| 55 | .new_reinit_func = fn \ | ||
| 56 | } | ||
| 57 | |||
| 58 | static struct radeon_device_id radeon_workaround_list[] = { | ||
| 59 | BUGFIX("IBM Thinkpad R32", | ||
| 60 | PCI_VENDOR_ID_IBM, 0x1905, | ||
| 61 | radeon_pm_d2, NULL), | ||
| 62 | BUGFIX("IBM Thinkpad R40", | ||
| 63 | PCI_VENDOR_ID_IBM, 0x0526, | ||
| 64 | radeon_pm_d2, NULL), | ||
| 65 | BUGFIX("IBM Thinkpad R40", | ||
| 66 | PCI_VENDOR_ID_IBM, 0x0527, | ||
| 67 | radeon_pm_d2, NULL), | ||
| 68 | BUGFIX("IBM Thinkpad R50/R51/T40/T41", | ||
| 69 | PCI_VENDOR_ID_IBM, 0x0531, | ||
| 70 | radeon_pm_d2, NULL), | ||
| 71 | BUGFIX("IBM Thinkpad R51/T40/T41/T42", | ||
| 72 | PCI_VENDOR_ID_IBM, 0x0530, | ||
| 73 | radeon_pm_d2, NULL), | ||
| 74 | BUGFIX("IBM Thinkpad T30", | ||
| 75 | PCI_VENDOR_ID_IBM, 0x0517, | ||
| 76 | radeon_pm_d2, NULL), | ||
| 77 | BUGFIX("IBM Thinkpad T40p", | ||
| 78 | PCI_VENDOR_ID_IBM, 0x054d, | ||
| 79 | radeon_pm_d2, NULL), | ||
| 80 | BUGFIX("IBM Thinkpad T42", | ||
| 81 | PCI_VENDOR_ID_IBM, 0x0550, | ||
| 82 | radeon_pm_d2, NULL), | ||
| 83 | BUGFIX("IBM Thinkpad X31/X32", | ||
| 84 | PCI_VENDOR_ID_IBM, 0x052f, | ||
| 85 | radeon_pm_d2, NULL), | ||
| 86 | BUGFIX("Samsung P35", | ||
| 87 | PCI_VENDOR_ID_SAMSUNG, 0xc00c, | ||
| 88 | radeon_pm_off, radeon_reinitialize_M10), | ||
| 89 | { .ident = NULL } | ||
| 90 | }; | ||
| 91 | |||
| 92 | static int radeon_apply_workarounds(struct radeonfb_info *rinfo) | ||
| 93 | { | ||
| 94 | struct radeon_device_id *id; | ||
| 95 | |||
| 96 | for (id = radeon_workaround_list; id->ident != NULL; id++ ) | ||
| 97 | if ((id->subsystem_vendor == rinfo->pdev->subsystem_vendor ) && | ||
| 98 | (id->subsystem_device == rinfo->pdev->subsystem_device )) { | ||
| 99 | |||
| 100 | /* we found a device that requires workaround */ | ||
| 101 | printk(KERN_DEBUG "radeonfb: %s detected" | ||
| 102 | ", enabling workaround\n", id->ident); | ||
| 103 | |||
| 104 | rinfo->pm_mode |= id->pm_mode_modifier; | ||
| 105 | |||
| 106 | if (id->new_reinit_func != NULL) | ||
| 107 | rinfo->reinit_func = id->new_reinit_func; | ||
| 108 | |||
| 109 | return 1; | ||
| 110 | } | ||
| 111 | return 0; /* not found */ | ||
| 112 | } | ||
| 113 | |||
| 114 | #else /* defined(CONFIG_PM) && defined(CONFIG_X86) */ | ||
| 115 | static inline int radeon_apply_workarounds(struct radeonfb_info *rinfo) | ||
| 116 | { | ||
| 117 | return 0; | ||
| 118 | } | ||
| 119 | #endif /* defined(CONFIG_PM) && defined(CONFIG_X86) */ | ||
| 120 | |||
| 121 | |||
| 122 | |||
| 30 | static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo) | 123 | static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo) |
| 31 | { | 124 | { |
| 32 | u32 tmp; | 125 | u32 tmp; |
| @@ -852,18 +945,26 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo) | |||
| 852 | /* because both INPLL and OUTPLL take the same lock, that's why. */ | 945 | /* because both INPLL and OUTPLL take the same lock, that's why. */ |
| 853 | tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND; | 946 | tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND; |
| 854 | OUTPLL( pllMCLK_MISC, tmp); | 947 | OUTPLL( pllMCLK_MISC, tmp); |
| 855 | |||
| 856 | /* AGP PLL control */ | ||
| 857 | if (rinfo->family <= CHIP_FAMILY_RV280) { | ||
| 858 | OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID); | ||
| 859 | 948 | ||
| 860 | OUTREG(BUS_CNTL1, | 949 | /* BUS_CNTL1__MOBILE_PLATORM_SEL setting is northbridge chipset |
| 861 | (INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK) | 950 | * and radeon chip dependent. Thus we only enable it on Mac for |
| 862 | | (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT)); // 440BX | 951 | * now (until we get more info on how to compute the correct |
| 863 | } else { | 952 | * value for various X86 bridges). |
| 864 | OUTREG(BUS_CNTL1, INREG(BUS_CNTL1)); | 953 | */ |
| 865 | OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000); | 954 | #ifdef CONFIG_PPC_PMAC |
| 955 | if (machine_is(powermac)) { | ||
| 956 | /* AGP PLL control */ | ||
| 957 | if (rinfo->family <= CHIP_FAMILY_RV280) { | ||
| 958 | OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID); | ||
| 959 | OUTREG(BUS_CNTL1, | ||
| 960 | (INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK) | ||
| 961 | | (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT)); // 440BX | ||
| 962 | } else { | ||
| 963 | OUTREG(BUS_CNTL1, INREG(BUS_CNTL1)); | ||
| 964 | OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000); | ||
| 965 | } | ||
| 866 | } | 966 | } |
| 967 | #endif | ||
| 867 | 968 | ||
| 868 | OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL) | 969 | OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL) |
| 869 | & ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN)); | 970 | & ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN)); |
| @@ -2713,7 +2814,7 @@ static void radeonfb_early_resume(void *data) | |||
| 2713 | 2814 | ||
| 2714 | #endif /* CONFIG_PM */ | 2815 | #endif /* CONFIG_PM */ |
| 2715 | 2816 | ||
| 2716 | void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk) | 2817 | void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlist, int force_sleep) |
| 2717 | { | 2818 | { |
| 2718 | /* Find PM registers in config space if any*/ | 2819 | /* Find PM registers in config space if any*/ |
| 2719 | rinfo->pm_reg = pci_find_capability(rinfo->pdev, PCI_CAP_ID_PM); | 2820 | rinfo->pm_reg = pci_find_capability(rinfo->pdev, PCI_CAP_ID_PM); |
| @@ -2729,22 +2830,13 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk) | |||
| 2729 | } | 2830 | } |
| 2730 | 2831 | ||
| 2731 | #if defined(CONFIG_PM) | 2832 | #if defined(CONFIG_PM) |
| 2833 | #if defined(CONFIG_PPC_PMAC) | ||
| 2732 | /* Check if we can power manage on suspend/resume. We can do | 2834 | /* Check if we can power manage on suspend/resume. We can do |
| 2733 | * D2 on M6, M7 and M9, and we can resume from D3 cold a few other | 2835 | * D2 on M6, M7 and M9, and we can resume from D3 cold a few other |
| 2734 | * "Mac" cards, but that's all. We need more infos about what the | 2836 | * "Mac" cards, but that's all. We need more infos about what the |
| 2735 | * BIOS does tho. Right now, all this PM stuff is pmac-only for that | 2837 | * BIOS does tho. Right now, all this PM stuff is pmac-only for that |
| 2736 | * reason. --BenH | 2838 | * reason. --BenH |
| 2737 | */ | 2839 | */ |
| 2738 | /* Special case for Samsung P35 laptops | ||
| 2739 | */ | ||
| 2740 | if ((rinfo->pdev->vendor == PCI_VENDOR_ID_ATI) && | ||
| 2741 | (rinfo->pdev->device == PCI_CHIP_RV350_NP) && | ||
| 2742 | (rinfo->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG) && | ||
| 2743 | (rinfo->pdev->subsystem_device == 0xc00c)) { | ||
| 2744 | rinfo->reinit_func = radeon_reinitialize_M10; | ||
| 2745 | rinfo->pm_mode |= radeon_pm_off; | ||
| 2746 | } | ||
| 2747 | #if defined(CONFIG_PPC_PMAC) | ||
| 2748 | if (machine_is(powermac) && rinfo->of_node) { | 2840 | if (machine_is(powermac) && rinfo->of_node) { |
| 2749 | if (rinfo->is_mobility && rinfo->pm_reg && | 2841 | if (rinfo->is_mobility && rinfo->pm_reg && |
| 2750 | rinfo->family <= CHIP_FAMILY_RV250) | 2842 | rinfo->family <= CHIP_FAMILY_RV250) |
| @@ -2790,6 +2882,18 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk) | |||
| 2790 | } | 2882 | } |
| 2791 | #endif /* defined(CONFIG_PPC_PMAC) */ | 2883 | #endif /* defined(CONFIG_PPC_PMAC) */ |
| 2792 | #endif /* defined(CONFIG_PM) */ | 2884 | #endif /* defined(CONFIG_PM) */ |
| 2885 | |||
| 2886 | if (ignore_devlist) | ||
| 2887 | printk(KERN_DEBUG | ||
| 2888 | "radeonfb: skipping test for device workarounds\n"); | ||
| 2889 | else | ||
| 2890 | radeon_apply_workarounds(rinfo); | ||
| 2891 | |||
| 2892 | if (force_sleep) { | ||
| 2893 | printk(KERN_DEBUG | ||
| 2894 | "radeonfb: forcefully enabling D2 sleep mode\n"); | ||
| 2895 | rinfo->pm_mode |= radeon_pm_d2; | ||
| 2896 | } | ||
| 2793 | } | 2897 | } |
| 2794 | 2898 | ||
| 2795 | void radeonfb_pm_exit(struct radeonfb_info *rinfo) | 2899 | void radeonfb_pm_exit(struct radeonfb_info *rinfo) |
diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h index 38657b2d10eb..d5ff224a6258 100644 --- a/drivers/video/aty/radeonfb.h +++ b/drivers/video/aty/radeonfb.h | |||
| @@ -273,6 +273,8 @@ enum radeon_pm_mode { | |||
| 273 | radeon_pm_off = 0x00000002, /* Can resume from D3 cold */ | 273 | radeon_pm_off = 0x00000002, /* Can resume from D3 cold */ |
| 274 | }; | 274 | }; |
| 275 | 275 | ||
| 276 | typedef void (*reinit_function_ptr)(struct radeonfb_info *rinfo); | ||
| 277 | |||
| 276 | struct radeonfb_info { | 278 | struct radeonfb_info { |
| 277 | struct fb_info *info; | 279 | struct fb_info *info; |
| 278 | 280 | ||
| @@ -338,7 +340,7 @@ struct radeonfb_info { | |||
| 338 | int dynclk; | 340 | int dynclk; |
| 339 | int no_schedule; | 341 | int no_schedule; |
| 340 | enum radeon_pm_mode pm_mode; | 342 | enum radeon_pm_mode pm_mode; |
| 341 | void (*reinit_func)(struct radeonfb_info *rinfo); | 343 | reinit_function_ptr reinit_func; |
| 342 | 344 | ||
| 343 | /* Lock on register access */ | 345 | /* Lock on register access */ |
| 344 | spinlock_t reg_lock; | 346 | spinlock_t reg_lock; |
| @@ -600,7 +602,7 @@ extern int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn, u8 | |||
| 600 | /* PM Functions */ | 602 | /* PM Functions */ |
| 601 | extern int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state); | 603 | extern int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state); |
| 602 | extern int radeonfb_pci_resume(struct pci_dev *pdev); | 604 | extern int radeonfb_pci_resume(struct pci_dev *pdev); |
| 603 | extern void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk); | 605 | extern void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlist, int force_sleep); |
| 604 | extern void radeonfb_pm_exit(struct radeonfb_info *rinfo); | 606 | extern void radeonfb_pm_exit(struct radeonfb_info *rinfo); |
| 605 | 607 | ||
| 606 | /* Monitor probe functions */ | 608 | /* Monitor probe functions */ |
