mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-03 23:58:05 +00:00
[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>
This commit is contained in:
parent
256154fbc3
commit
994aad251a
3 changed files with 145 additions and 25 deletions
|
@ -266,6 +266,8 @@ static int force_measure_pll = 0;
|
||||||
#ifdef CONFIG_MTRR
|
#ifdef CONFIG_MTRR
|
||||||
static int nomtrr = 0;
|
static int nomtrr = 0;
|
||||||
#endif
|
#endif
|
||||||
|
static int force_sleep;
|
||||||
|
static int ignore_devlist;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prototypes
|
* prototypes
|
||||||
|
@ -2327,9 +2329,9 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
|
||||||
/* -2 is special: means ON on mobility chips and do not
|
/* -2 is special: means ON on mobility chips and do not
|
||||||
* change on others
|
* change on others
|
||||||
*/
|
*/
|
||||||
radeonfb_pm_init(rinfo, rinfo->is_mobility ? 1 : -1);
|
radeonfb_pm_init(rinfo, rinfo->is_mobility ? 1 : -1, ignore_devlist, force_sleep);
|
||||||
} else
|
} else
|
||||||
radeonfb_pm_init(rinfo, default_dynclk);
|
radeonfb_pm_init(rinfo, default_dynclk, ignore_devlist, force_sleep);
|
||||||
|
|
||||||
pci_set_drvdata(pdev, info);
|
pci_set_drvdata(pdev, info);
|
||||||
|
|
||||||
|
@ -2477,6 +2479,12 @@ static int __init radeonfb_setup (char *options)
|
||||||
force_measure_pll = 1;
|
force_measure_pll = 1;
|
||||||
} else if (!strncmp(this_opt, "ignore_edid", 11)) {
|
} else if (!strncmp(this_opt, "ignore_edid", 11)) {
|
||||||
ignore_edid = 1;
|
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, "ignore_devlist", 14)) {
|
||||||
|
ignore_devlist = 1;
|
||||||
|
#endif
|
||||||
} else
|
} else
|
||||||
mode_option = this_opt;
|
mode_option = this_opt;
|
||||||
}
|
}
|
||||||
|
@ -2532,3 +2540,9 @@ module_param(panel_yres, int, 0);
|
||||||
MODULE_PARM_DESC(panel_yres, "int: set panel yres");
|
MODULE_PARM_DESC(panel_yres, "int: set panel yres");
|
||||||
module_param(mode_option, charp, 0);
|
module_param(mode_option, charp, 0);
|
||||||
MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
|
MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
|
||||||
|
#if defined(CONFIG_PM) && defined(CONFIG_X86)
|
||||||
|
module_param(force_sleep, bool, 0);
|
||||||
|
MODULE_PARM_DESC(force_sleep, "bool: force D2 sleep mode on all hardware");
|
||||||
|
module_param(ignore_devlist, bool, 0);
|
||||||
|
MODULE_PARM_DESC(ignore_devlist, "bool: ignore workarounds for bugs in specific laptops");
|
||||||
|
#endif
|
||||||
|
|
|
@ -27,6 +27,99 @@
|
||||||
|
|
||||||
#include "ati_ids.h"
|
#include "ati_ids.h"
|
||||||
|
|
||||||
|
static void radeon_reinitialize_M10(struct radeonfb_info *rinfo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Workarounds for bugs in PC laptops:
|
||||||
|
* - enable D2 sleep in some IBM Thinkpads
|
||||||
|
* - special case for Samsung P35
|
||||||
|
*
|
||||||
|
* Whitelist by subsystem vendor/device because
|
||||||
|
* its the subsystem vendor's fault!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(CONFIG_PM) && defined(CONFIG_X86)
|
||||||
|
struct radeon_device_id {
|
||||||
|
const char *ident; /* (arbitrary) Name */
|
||||||
|
const unsigned short subsystem_vendor; /* Subsystem Vendor ID */
|
||||||
|
const unsigned short subsystem_device; /* Subsystem Device ID */
|
||||||
|
const enum radeon_pm_mode pm_mode_modifier; /* modify pm_mode */
|
||||||
|
const reinit_function_ptr new_reinit_func; /* changed reinit_func */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BUGFIX(model, sv, sd, pm, fn) { \
|
||||||
|
.ident = model, \
|
||||||
|
.subsystem_vendor = sv, \
|
||||||
|
.subsystem_device = sd, \
|
||||||
|
.pm_mode_modifier = pm, \
|
||||||
|
.new_reinit_func = fn \
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct radeon_device_id radeon_workaround_list[] = {
|
||||||
|
BUGFIX("IBM Thinkpad R32",
|
||||||
|
PCI_VENDOR_ID_IBM, 0x1905,
|
||||||
|
radeon_pm_d2, NULL),
|
||||||
|
BUGFIX("IBM Thinkpad R40",
|
||||||
|
PCI_VENDOR_ID_IBM, 0x0526,
|
||||||
|
radeon_pm_d2, NULL),
|
||||||
|
BUGFIX("IBM Thinkpad R40",
|
||||||
|
PCI_VENDOR_ID_IBM, 0x0527,
|
||||||
|
radeon_pm_d2, NULL),
|
||||||
|
BUGFIX("IBM Thinkpad R50/R51/T40/T41",
|
||||||
|
PCI_VENDOR_ID_IBM, 0x0531,
|
||||||
|
radeon_pm_d2, NULL),
|
||||||
|
BUGFIX("IBM Thinkpad R51/T40/T41/T42",
|
||||||
|
PCI_VENDOR_ID_IBM, 0x0530,
|
||||||
|
radeon_pm_d2, NULL),
|
||||||
|
BUGFIX("IBM Thinkpad T30",
|
||||||
|
PCI_VENDOR_ID_IBM, 0x0517,
|
||||||
|
radeon_pm_d2, NULL),
|
||||||
|
BUGFIX("IBM Thinkpad T40p",
|
||||||
|
PCI_VENDOR_ID_IBM, 0x054d,
|
||||||
|
radeon_pm_d2, NULL),
|
||||||
|
BUGFIX("IBM Thinkpad T42",
|
||||||
|
PCI_VENDOR_ID_IBM, 0x0550,
|
||||||
|
radeon_pm_d2, NULL),
|
||||||
|
BUGFIX("IBM Thinkpad X31/X32",
|
||||||
|
PCI_VENDOR_ID_IBM, 0x052f,
|
||||||
|
radeon_pm_d2, NULL),
|
||||||
|
BUGFIX("Samsung P35",
|
||||||
|
PCI_VENDOR_ID_SAMSUNG, 0xc00c,
|
||||||
|
radeon_pm_off, radeon_reinitialize_M10),
|
||||||
|
{ .ident = NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static int radeon_apply_workarounds(struct radeonfb_info *rinfo)
|
||||||
|
{
|
||||||
|
struct radeon_device_id *id;
|
||||||
|
|
||||||
|
for (id = radeon_workaround_list; id->ident != NULL; id++ )
|
||||||
|
if ((id->subsystem_vendor == rinfo->pdev->subsystem_vendor ) &&
|
||||||
|
(id->subsystem_device == rinfo->pdev->subsystem_device )) {
|
||||||
|
|
||||||
|
/* we found a device that requires workaround */
|
||||||
|
printk(KERN_DEBUG "radeonfb: %s detected"
|
||||||
|
", enabling workaround\n", id->ident);
|
||||||
|
|
||||||
|
rinfo->pm_mode |= id->pm_mode_modifier;
|
||||||
|
|
||||||
|
if (id->new_reinit_func != NULL)
|
||||||
|
rinfo->reinit_func = id->new_reinit_func;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0; /* not found */
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* defined(CONFIG_PM) && defined(CONFIG_X86) */
|
||||||
|
static inline int radeon_apply_workarounds(struct radeonfb_info *rinfo)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* defined(CONFIG_PM) && defined(CONFIG_X86) */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo)
|
static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo)
|
||||||
{
|
{
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
@ -853,10 +946,16 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo)
|
||||||
tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND;
|
tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND;
|
||||||
OUTPLL( pllMCLK_MISC, tmp);
|
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
|
||||||
|
if (machine_is(powermac)) {
|
||||||
/* AGP PLL control */
|
/* AGP PLL control */
|
||||||
if (rinfo->family <= CHIP_FAMILY_RV280) {
|
if (rinfo->family <= CHIP_FAMILY_RV280) {
|
||||||
OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID);
|
OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID);
|
||||||
|
|
||||||
OUTREG(BUS_CNTL1,
|
OUTREG(BUS_CNTL1,
|
||||||
(INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK)
|
(INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK)
|
||||||
| (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT)); // 440BX
|
| (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT)); // 440BX
|
||||||
|
@ -864,6 +963,8 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo)
|
||||||
OUTREG(BUS_CNTL1, INREG(BUS_CNTL1));
|
OUTREG(BUS_CNTL1, INREG(BUS_CNTL1));
|
||||||
OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000);
|
OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL)
|
OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL)
|
||||||
& ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN));
|
& ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN));
|
||||||
|
@ -2713,7 +2814,7 @@ static void radeonfb_early_resume(void *data)
|
||||||
|
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk)
|
void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlist, int force_sleep)
|
||||||
{
|
{
|
||||||
/* Find PM registers in config space if any*/
|
/* Find PM registers in config space if any*/
|
||||||
rinfo->pm_reg = pci_find_capability(rinfo->pdev, PCI_CAP_ID_PM);
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_PM)
|
#if defined(CONFIG_PM)
|
||||||
|
#if defined(CONFIG_PPC_PMAC)
|
||||||
/* Check if we can power manage on suspend/resume. We can do
|
/* Check if we can power manage on suspend/resume. We can do
|
||||||
* D2 on M6, M7 and M9, and we can resume from D3 cold a few other
|
* D2 on M6, M7 and M9, and we can resume from D3 cold a few other
|
||||||
* "Mac" cards, but that's all. We need more infos about what the
|
* "Mac" cards, but that's all. We need more infos about what the
|
||||||
* BIOS does tho. Right now, all this PM stuff is pmac-only for that
|
* BIOS does tho. Right now, all this PM stuff is pmac-only for that
|
||||||
* reason. --BenH
|
* reason. --BenH
|
||||||
*/
|
*/
|
||||||
/* Special case for Samsung P35 laptops
|
|
||||||
*/
|
|
||||||
if ((rinfo->pdev->vendor == PCI_VENDOR_ID_ATI) &&
|
|
||||||
(rinfo->pdev->device == PCI_CHIP_RV350_NP) &&
|
|
||||||
(rinfo->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG) &&
|
|
||||||
(rinfo->pdev->subsystem_device == 0xc00c)) {
|
|
||||||
rinfo->reinit_func = radeon_reinitialize_M10;
|
|
||||||
rinfo->pm_mode |= radeon_pm_off;
|
|
||||||
}
|
|
||||||
#if defined(CONFIG_PPC_PMAC)
|
|
||||||
if (machine_is(powermac) && rinfo->of_node) {
|
if (machine_is(powermac) && rinfo->of_node) {
|
||||||
if (rinfo->is_mobility && rinfo->pm_reg &&
|
if (rinfo->is_mobility && rinfo->pm_reg &&
|
||||||
rinfo->family <= CHIP_FAMILY_RV250)
|
rinfo->family <= CHIP_FAMILY_RV250)
|
||||||
|
@ -2790,6 +2882,18 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk)
|
||||||
}
|
}
|
||||||
#endif /* defined(CONFIG_PPC_PMAC) */
|
#endif /* defined(CONFIG_PPC_PMAC) */
|
||||||
#endif /* defined(CONFIG_PM) */
|
#endif /* defined(CONFIG_PM) */
|
||||||
|
|
||||||
|
if (ignore_devlist)
|
||||||
|
printk(KERN_DEBUG
|
||||||
|
"radeonfb: skipping test for device workarounds\n");
|
||||||
|
else
|
||||||
|
radeon_apply_workarounds(rinfo);
|
||||||
|
|
||||||
|
if (force_sleep) {
|
||||||
|
printk(KERN_DEBUG
|
||||||
|
"radeonfb: forcefully enabling D2 sleep mode\n");
|
||||||
|
rinfo->pm_mode |= radeon_pm_d2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void radeonfb_pm_exit(struct radeonfb_info *rinfo)
|
void radeonfb_pm_exit(struct radeonfb_info *rinfo)
|
||||||
|
|
|
@ -273,6 +273,8 @@ enum radeon_pm_mode {
|
||||||
radeon_pm_off = 0x00000002, /* Can resume from D3 cold */
|
radeon_pm_off = 0x00000002, /* Can resume from D3 cold */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef void (*reinit_function_ptr)(struct radeonfb_info *rinfo);
|
||||||
|
|
||||||
struct radeonfb_info {
|
struct radeonfb_info {
|
||||||
struct fb_info *info;
|
struct fb_info *info;
|
||||||
|
|
||||||
|
@ -338,7 +340,7 @@ struct radeonfb_info {
|
||||||
int dynclk;
|
int dynclk;
|
||||||
int no_schedule;
|
int no_schedule;
|
||||||
enum radeon_pm_mode pm_mode;
|
enum radeon_pm_mode pm_mode;
|
||||||
void (*reinit_func)(struct radeonfb_info *rinfo);
|
reinit_function_ptr reinit_func;
|
||||||
|
|
||||||
/* Lock on register access */
|
/* Lock on register access */
|
||||||
spinlock_t reg_lock;
|
spinlock_t reg_lock;
|
||||||
|
@ -600,7 +602,7 @@ extern int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn, u8
|
||||||
/* PM Functions */
|
/* PM Functions */
|
||||||
extern int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state);
|
extern int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state);
|
||||||
extern int radeonfb_pci_resume(struct pci_dev *pdev);
|
extern int radeonfb_pci_resume(struct pci_dev *pdev);
|
||||||
extern void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk);
|
extern void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlist, int force_sleep);
|
||||||
extern void radeonfb_pm_exit(struct radeonfb_info *rinfo);
|
extern void radeonfb_pm_exit(struct radeonfb_info *rinfo);
|
||||||
|
|
||||||
/* Monitor probe functions */
|
/* Monitor probe functions */
|
||||||
|
|
Loading…
Reference in a new issue