Commit 994aad251acab32a5d40d4a9501dc3e736562b6d

Authored by Volker Braun
Committed by Linus Torvalds
1 parent 256154fbc3

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

Showing 3 changed files with 145 additions and 25 deletions Side-by-side Diff

drivers/video/aty/radeon_base.c
... ... @@ -266,6 +266,8 @@
266 266 #ifdef CONFIG_MTRR
267 267 static int nomtrr = 0;
268 268 #endif
  269 +static int force_sleep;
  270 +static int ignore_devlist;
269 271  
270 272 /*
271 273 * prototypes
272 274  
... ... @@ -2327,9 +2329,9 @@
2327 2329 /* -2 is special: means ON on mobility chips and do not
2328 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 2333 } else
2332   - radeonfb_pm_init(rinfo, default_dynclk);
  2334 + radeonfb_pm_init(rinfo, default_dynclk, ignore_devlist, force_sleep);
2333 2335  
2334 2336 pci_set_drvdata(pdev, info);
2335 2337  
... ... @@ -2477,6 +2479,12 @@
2477 2479 force_measure_pll = 1;
2478 2480 } else if (!strncmp(this_opt, "ignore_edid", 11)) {
2479 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 2488 } else
2481 2489 mode_option = this_opt;
2482 2490 }
... ... @@ -2532,4 +2540,10 @@
2532 2540 MODULE_PARM_DESC(panel_yres, "int: set panel yres");
2533 2541 module_param(mode_option, charp, 0);
2534 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
drivers/video/aty/radeon_pm.c
... ... @@ -27,6 +27,99 @@
27 27  
28 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 123 static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo)
31 124 {
32 125 u32 tmp;
33 126  
34 127  
... ... @@ -852,18 +945,26 @@
852 945 /* because both INPLL and OUTPLL take the same lock, that's why. */
853 946 tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND;
854 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,
861   - (INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK)
862   - | (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT)); // 440BX
863   - } else {
864   - OUTREG(BUS_CNTL1, INREG(BUS_CNTL1));
865   - OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000);
  949 + /* BUS_CNTL1__MOBILE_PLATORM_SEL setting is northbridge chipset
  950 + * and radeon chip dependent. Thus we only enable it on Mac for
  951 + * now (until we get more info on how to compute the correct
  952 + * value for various X86 bridges).
  953 + */
  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 969 OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL)
869 970 & ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN));
... ... @@ -2713,7 +2814,7 @@
2713 2814  
2714 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 2819 /* Find PM registers in config space if any*/
2719 2820 rinfo->pm_reg = pci_find_capability(rinfo->pdev, PCI_CAP_ID_PM);
2720 2821  
... ... @@ -2729,22 +2830,13 @@
2729 2830 }
2730 2831  
2731 2832 #if defined(CONFIG_PM)
  2833 +#if defined(CONFIG_PPC_PMAC)
2732 2834 /* Check if we can power manage on suspend/resume. We can do
2733 2835 * D2 on M6, M7 and M9, and we can resume from D3 cold a few other
2734 2836 * "Mac" cards, but that's all. We need more infos about what the
2735 2837 * BIOS does tho. Right now, all this PM stuff is pmac-only for that
2736 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 2840 if (machine_is(powermac) && rinfo->of_node) {
2749 2841 if (rinfo->is_mobility && rinfo->pm_reg &&
2750 2842 rinfo->family <= CHIP_FAMILY_RV250)
... ... @@ -2790,6 +2882,18 @@
2790 2882 }
2791 2883 #endif /* defined(CONFIG_PPC_PMAC) */
2792 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 2899 void radeonfb_pm_exit(struct radeonfb_info *rinfo)
drivers/video/aty/radeonfb.h
... ... @@ -273,6 +273,8 @@
273 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 278 struct radeonfb_info {
277 279 struct fb_info *info;
278 280  
... ... @@ -338,7 +340,7 @@
338 340 int dynclk;
339 341 int no_schedule;
340 342 enum radeon_pm_mode pm_mode;
341   - void (*reinit_func)(struct radeonfb_info *rinfo);
  343 + reinit_function_ptr reinit_func;
342 344  
343 345 /* Lock on register access */
344 346 spinlock_t reg_lock;
... ... @@ -600,7 +602,7 @@
600 602 /* PM Functions */
601 603 extern int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state);
602 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 606 extern void radeonfb_pm_exit(struct radeonfb_info *rinfo);
605 607  
606 608 /* Monitor probe functions */