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

authored by Volker Braun and committed by Linus Torvalds 994aad25 256154fb

+145 -25
+16 -2
drivers/video/aty/radeon_base.c
··· 266 #ifdef CONFIG_MTRR 267 static int nomtrr = 0; 268 #endif 269 270 /* 271 * prototypes ··· 2329 /* -2 is special: means ON on mobility chips and do not 2330 * change on others 2331 */ 2332 - radeonfb_pm_init(rinfo, rinfo->is_mobility ? 1 : -1); 2333 } else 2334 - radeonfb_pm_init(rinfo, default_dynclk); 2335 2336 pci_set_drvdata(pdev, info); 2337 ··· 2479 force_measure_pll = 1; 2480 } else if (!strncmp(this_opt, "ignore_edid", 11)) { 2481 ignore_edid = 1; 2482 } else 2483 mode_option = this_opt; 2484 } ··· 2540 MODULE_PARM_DESC(panel_yres, "int: set panel yres"); 2541 module_param(mode_option, charp, 0); 2542 MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
··· 266 #ifdef CONFIG_MTRR 267 static int nomtrr = 0; 268 #endif 269 + static int force_sleep; 270 + static int ignore_devlist; 271 272 /* 273 * prototypes ··· 2327 /* -2 is special: means ON on mobility chips and do not 2328 * change on others 2329 */ 2330 + radeonfb_pm_init(rinfo, rinfo->is_mobility ? 1 : -1, ignore_devlist, force_sleep); 2331 } else 2332 + radeonfb_pm_init(rinfo, default_dynclk, ignore_devlist, force_sleep); 2333 2334 pci_set_drvdata(pdev, info); 2335 ··· 2477 force_measure_pll = 1; 2478 } else if (!strncmp(this_opt, "ignore_edid", 11)) { 2479 ignore_edid = 1; 2480 + #if defined(CONFIG_PM) && defined(CONFIG_X86) 2481 + } else if (!strncmp(this_opt, "force_sleep", 11)) { 2482 + force_sleep = 1; 2483 + } else if (!strncmp(this_opt, "ignore_devlist", 14)) { 2484 + ignore_devlist = 1; 2485 + #endif 2486 } else 2487 mode_option = this_opt; 2488 } ··· 2532 MODULE_PARM_DESC(panel_yres, "int: set panel yres"); 2533 module_param(mode_option, charp, 0); 2534 MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); 2535 + #if defined(CONFIG_PM) && defined(CONFIG_X86) 2536 + module_param(force_sleep, bool, 0); 2537 + MODULE_PARM_DESC(force_sleep, "bool: force D2 sleep mode on all hardware"); 2538 + module_param(ignore_devlist, bool, 0); 2539 + MODULE_PARM_DESC(ignore_devlist, "bool: ignore workarounds for bugs in specific laptops"); 2540 + #endif
+125 -21
drivers/video/aty/radeon_pm.c
··· 27 28 #include "ati_ids.h" 29 30 static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo) 31 { 32 u32 tmp; ··· 945 /* because both INPLL and OUTPLL take the same lock, that's why. */ 946 tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND; 947 OUTPLL( pllMCLK_MISC, tmp); 948 - 949 - /* AGP PLL control */ 950 - if (rinfo->family <= CHIP_FAMILY_RV280) { 951 - OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID); 952 953 - OUTREG(BUS_CNTL1, 954 - (INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK) 955 - | (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT)); // 440BX 956 - } else { 957 - OUTREG(BUS_CNTL1, INREG(BUS_CNTL1)); 958 - OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000); 959 } 960 961 OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL) 962 & ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN)); ··· 2814 2815 #endif /* CONFIG_PM */ 2816 2817 - void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk) 2818 { 2819 /* Find PM registers in config space if any*/ 2820 rinfo->pm_reg = pci_find_capability(rinfo->pdev, PCI_CAP_ID_PM); ··· 2830 } 2831 2832 #if defined(CONFIG_PM) 2833 /* Check if we can power manage on suspend/resume. We can do 2834 * D2 on M6, M7 and M9, and we can resume from D3 cold a few other 2835 * "Mac" cards, but that's all. We need more infos about what the 2836 * BIOS does tho. Right now, all this PM stuff is pmac-only for that 2837 * reason. --BenH 2838 */ 2839 - /* Special case for Samsung P35 laptops 2840 - */ 2841 - if ((rinfo->pdev->vendor == PCI_VENDOR_ID_ATI) && 2842 - (rinfo->pdev->device == PCI_CHIP_RV350_NP) && 2843 - (rinfo->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG) && 2844 - (rinfo->pdev->subsystem_device == 0xc00c)) { 2845 - rinfo->reinit_func = radeon_reinitialize_M10; 2846 - rinfo->pm_mode |= radeon_pm_off; 2847 - } 2848 - #if defined(CONFIG_PPC_PMAC) 2849 if (machine_is(powermac) && rinfo->of_node) { 2850 if (rinfo->is_mobility && rinfo->pm_reg && 2851 rinfo->family <= CHIP_FAMILY_RV250) ··· 2882 } 2883 #endif /* defined(CONFIG_PPC_PMAC) */ 2884 #endif /* defined(CONFIG_PM) */ 2885 } 2886 2887 void radeonfb_pm_exit(struct radeonfb_info *rinfo)
··· 27 28 #include "ati_ids.h" 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 + 123 static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo) 124 { 125 u32 tmp; ··· 852 /* because both INPLL and OUTPLL take the same lock, that's why. */ 853 tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND; 854 OUTPLL( pllMCLK_MISC, tmp); 855 856 + /* BUS_CNTL1__MOBILE_PLATORM_SEL setting is northbridge chipset 857 + * and radeon chip dependent. Thus we only enable it on Mac for 858 + * now (until we get more info on how to compute the correct 859 + * value for various X86 bridges). 860 + */ 861 + #ifdef CONFIG_PPC_PMAC 862 + if (machine_is(powermac)) { 863 + /* AGP PLL control */ 864 + if (rinfo->family <= CHIP_FAMILY_RV280) { 865 + OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID); 866 + OUTREG(BUS_CNTL1, 867 + (INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK) 868 + | (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT)); // 440BX 869 + } else { 870 + OUTREG(BUS_CNTL1, INREG(BUS_CNTL1)); 871 + OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000); 872 + } 873 } 874 + #endif 875 876 OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL) 877 & ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN)); ··· 2713 2714 #endif /* CONFIG_PM */ 2715 2716 + void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlist, int force_sleep) 2717 { 2718 /* Find PM registers in config space if any*/ 2719 rinfo->pm_reg = pci_find_capability(rinfo->pdev, PCI_CAP_ID_PM); ··· 2729 } 2730 2731 #if defined(CONFIG_PM) 2732 + #if defined(CONFIG_PPC_PMAC) 2733 /* Check if we can power manage on suspend/resume. We can do 2734 * D2 on M6, M7 and M9, and we can resume from D3 cold a few other 2735 * "Mac" cards, but that's all. We need more infos about what the 2736 * BIOS does tho. Right now, all this PM stuff is pmac-only for that 2737 * reason. --BenH 2738 */ 2739 if (machine_is(powermac) && rinfo->of_node) { 2740 if (rinfo->is_mobility && rinfo->pm_reg && 2741 rinfo->family <= CHIP_FAMILY_RV250) ··· 2790 } 2791 #endif /* defined(CONFIG_PPC_PMAC) */ 2792 #endif /* defined(CONFIG_PM) */ 2793 + 2794 + if (ignore_devlist) 2795 + printk(KERN_DEBUG 2796 + "radeonfb: skipping test for device workarounds\n"); 2797 + else 2798 + radeon_apply_workarounds(rinfo); 2799 + 2800 + if (force_sleep) { 2801 + printk(KERN_DEBUG 2802 + "radeonfb: forcefully enabling D2 sleep mode\n"); 2803 + rinfo->pm_mode |= radeon_pm_d2; 2804 + } 2805 } 2806 2807 void radeonfb_pm_exit(struct radeonfb_info *rinfo)
+4 -2
drivers/video/aty/radeonfb.h
··· 273 radeon_pm_off = 0x00000002, /* Can resume from D3 cold */ 274 }; 275 276 struct radeonfb_info { 277 struct fb_info *info; 278 ··· 340 int dynclk; 341 int no_schedule; 342 enum radeon_pm_mode pm_mode; 343 - void (*reinit_func)(struct radeonfb_info *rinfo); 344 345 /* Lock on register access */ 346 spinlock_t reg_lock; ··· 602 /* PM Functions */ 603 extern int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state); 604 extern int radeonfb_pci_resume(struct pci_dev *pdev); 605 - extern void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk); 606 extern void radeonfb_pm_exit(struct radeonfb_info *rinfo); 607 608 /* Monitor probe functions */
··· 273 radeon_pm_off = 0x00000002, /* Can resume from D3 cold */ 274 }; 275 276 + typedef void (*reinit_function_ptr)(struct radeonfb_info *rinfo); 277 + 278 struct radeonfb_info { 279 struct fb_info *info; 280 ··· 338 int dynclk; 339 int no_schedule; 340 enum radeon_pm_mode pm_mode; 341 + reinit_function_ptr reinit_func; 342 343 /* Lock on register access */ 344 spinlock_t reg_lock; ··· 600 /* PM Functions */ 601 extern int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state); 602 extern int radeonfb_pci_resume(struct pci_dev *pdev); 603 + 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); 605 606 /* Monitor probe functions */