From: Giorgio Lando Many IBM Thinkpad T4* models and some R* and X* with radeon video cards, when suspended to RAM, draw too much power, reducing drastically the battery lifetime. The solution is to enable suspend-to-D2 on non-PPC-machines. Since this is in general not well documented, suspend-to-D2 is enabled only on machines where it is known to work. These machines are identified through their DMI strings and listed in a white-list into the patch itself. This behaviour can be overriden with module options: through these options suspend-to-D2 can be: i) enabled also on non-whitelisted machines (since the white-list is partial and can be enlarged in the time); ii) disabled on whitelisted machines, in case of negative side-effects. The module options can be passed at boot time including the following corresponding parameter in the kernel command line: i) video=radeonfb:force_sleep=1 ii) video=radeonfb:nosleep=1 Signed-off-by: Antti Andreimann Acked-by: Volker Braun Acked-by: Joel Becker Signed-off-by: Thomas De Grenier De Latour Signed-off-by: Giorgio Lando Cc: Benjamin Herrenschmidt Cc: "Antonino A. Daplas" Signed-off-by: Andrew Morton --- drivers/video/aty/radeon_base.c | 17 ++++ drivers/video/aty/radeon_pm.c | 118 +++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 2 deletions(-) diff -puN drivers/video/aty/radeon_base.c~radeonfb-powerdrain-issue-on-ibm-thinkpads-and-suspend-to-d2 drivers/video/aty/radeon_base.c --- devel/drivers/video/aty/radeon_base.c~radeonfb-powerdrain-issue-on-ibm-thinkpads-and-suspend-to-d2 2006-05-17 13:10:19.000000000 -0700 +++ devel-akpm/drivers/video/aty/radeon_base.c 2006-05-17 13:10:19.000000000 -0700 @@ -268,7 +268,10 @@ static int force_measure_pll = 0; #ifdef CONFIG_MTRR static int nomtrr = 0; #endif - +#if defined(CONFIG_PM) && defined(CONFIG_X86) +int force_sleep = 0; +int nosleep = 0; +#endif /* * prototypes */ @@ -2481,6 +2484,12 @@ static int __init radeonfb_setup (char * force_measure_pll = 1; } else if (!strncmp(this_opt, "ignore_edid", 11)) { ignore_edid = 1; +#if defined(CONFIG_PM) && defined(CONFIG_X86) + } else if (!strncmp(this_opt, "force_sleep", 11)) { + force_sleep = 1; + } else if (!strncmp(this_opt, "nosleep", 7)) { + nosleep = 1; +#endif } else mode_option = this_opt; } @@ -2536,3 +2545,9 @@ module_param(panel_yres, int, 0); MODULE_PARM_DESC(panel_yres, "int: set panel yres"); module_param(mode_option, charp, 0); MODULE_PARM_DESC(mode_option, "Specify resolution as \"x[-][@]\" "); +#if defined(CONFIG_PM) && defined(CONFIG_X86) +module_param(force_sleep, bool, 0); +MODULE_PARM_DESC(force_sleep, "bool: force D2 sleep mode on non whitelisted laptops"); +module_param(nosleep, bool, 0); +MODULE_PARM_DESC(nosleep, "bool: disable D2 sleep mode, ignoring whitelisted laptops"); +#endif diff -puN drivers/video/aty/radeon_pm.c~radeonfb-powerdrain-issue-on-ibm-thinkpads-and-suspend-to-d2 drivers/video/aty/radeon_pm.c --- devel/drivers/video/aty/radeon_pm.c~radeonfb-powerdrain-issue-on-ibm-thinkpads-and-suspend-to-d2 2006-05-17 13:10:19.000000000 -0700 +++ devel-akpm/drivers/video/aty/radeon_pm.c 2006-05-17 13:10:19.000000000 -0700 @@ -27,6 +27,95 @@ #include "ati_ids.h" +#if defined(CONFIG_PM) && defined(CONFIG_X86) +/* DMI is used to detect PC laptops known to support D2 sleep */ +#include + +/* Whitelist of PC laptops known to support D2 sleep */ +static int radeon_sleep_dmi_whitelisted(struct dmi_system_id *id) { + printk(KERN_DEBUG "radeonfb: %s detected, enabling D2 sleep\n", id->ident); + return 1; +} +#define RADEON_SLEEP_THINKPAD_DMI_MATCH_MODEL(model) { \ + .ident = "IBM ThinkPad " model, \ + .callback = radeon_sleep_dmi_whitelisted, \ + .matches = { \ + DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \ + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad " model) \ + } \ +} +#define RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE(type,name) { \ + .ident = "IBM ThinkPad " name " (" type ")", \ + .callback = radeon_sleep_dmi_whitelisted, \ + .matches = { \ + DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \ + DMI_MATCH(DMI_PRODUCT_NAME, type) \ + } \ +} +static struct dmi_system_id radeon_sleep_dmi_whitelist[] = { + // This models all had at least one positive report and no negative one + RADEON_SLEEP_THINKPAD_DMI_MATCH_MODEL("R50"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_MODEL("R51"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_MODEL("T40p"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_MODEL("T40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_MODEL("T41p"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_MODEL("T41"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_MODEL("T42"), + // Same for this ones, but it's still to confirm that the DMI string exists + RADEON_SLEEP_THINKPAD_DMI_MATCH_MODEL("T30"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_MODEL("R32"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_MODEL("X31"), + // R40 does not have the version DMI string + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2681","R40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2682","R40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2683","R40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2722","R40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2723","R40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2724","R40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2892","R40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2893","R40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2896","R40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2897","R40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2898","R40"), + RADEON_SLEEP_THINKPAD_DMI_MATCH_TYPE("2899","R40"), + { .ident = NULL } +}; + +/* Need a blacklist too because DMI matching is done by substrings search */ +#define RADEON_SLEEP_THINKPAD_DMI_UNMATCH_MODEL(model) { \ + .ident = "IBM ThinkPad " model, \ + .matches = { \ + DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \ + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad " model) \ + } \ +} +#define RADEON_SLEEP_THINKPAD_DMI_UNMATCH_TYPE(type,name) { \ + .ident = "IBM ThinkPad " name " (" type ")", \ + .matches = { \ + DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \ + DMI_MATCH(DMI_PRODUCT_NAME, type) \ + } \ +} +static struct dmi_system_id radeon_sleep_dmi_blacklist[] = { + // Excluded by lack of positive report, and possibly wrong substring match + RADEON_SLEEP_THINKPAD_DMI_UNMATCH_MODEL("R50p"), + RADEON_SLEEP_THINKPAD_DMI_UNMATCH_MODEL("R50e"), + RADEON_SLEEP_THINKPAD_DMI_UNMATCH_MODEL("R51e"), + // T42p excluded because of one negative report and no positive one + RADEON_SLEEP_THINKPAD_DMI_UNMATCH_MODEL("T42p"), + { .ident = NULL } +}; + +/* Macro for checking DMI infos against the whitelist */ +#define radeon_sleep_match_whitelist() \ + (! dmi_check_system(radeon_sleep_dmi_blacklist) \ + && dmi_check_system(radeon_sleep_dmi_whitelist)) + +/* Module parameters to ignore the whitelist */ +extern int force_sleep; +extern int nosleep; +#endif /* defined(CONFIG_PM) && defined(CONFIG_X86) */ + static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo) { u32 tmp; @@ -852,7 +941,13 @@ static void radeon_pm_setup_for_suspend( /* because both INPLL and OUTPLL take the same lock, that's why. */ tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND; OUTPLL( pllMCLK_MISC, tmp); - + + /* BUS_CNTL1__MOBILE_PLATORM_SEL setting is northbridge chipset + * and radeon chip dependent. Thus we only enable it on Mac for + * now (until we get more info on how to compute the correct + * value for various X86 bridges). + */ +#ifdef CONFIG_PPC_PMAC /* AGP PLL control */ if (rinfo->family <= CHIP_FAMILY_RV280) { OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID); @@ -864,6 +959,7 @@ static void radeon_pm_setup_for_suspend( OUTREG(BUS_CNTL1, INREG(BUS_CNTL1)); OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000); } +#endif OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL) & ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN)); @@ -2789,6 +2885,26 @@ void radeonfb_pm_init(struct radeonfb_in #endif } #endif /* defined(CONFIG_PPC_PMAC) */ + +/* The PM code also works on some PC laptops. + * We can do D2 on at least M7 and M9 on some IBM ThinkPad models. + */ +#if defined(CONFIG_X86) + if (!nosleep && (force_sleep || radeon_sleep_match_whitelist())) { + if (force_sleep) + printk(KERN_DEBUG "radeonfb: forcefully enabling D2 sleep mode\n"); + + if (rinfo->is_mobility && rinfo->pm_reg && + rinfo->family <= CHIP_FAMILY_RV250) + rinfo->pm_mode |= radeon_pm_d2; + + /* Power down TV DAC, that saves a significant amount of power, + * we'll have something better once we actually have some TVOut + * support + */ + OUTREG(TV_DAC_CNTL, INREG(TV_DAC_CNTL) | 0x07000000); + } +#endif /* defined(CONFIG_X86) */ #endif /* defined(CONFIG_PM) */ } _