|
@@ -27,6 +27,99 @@
|
|
|
|
|
|
#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)
|
|
|
{
|
|
|
u32 tmp;
|
|
@@ -852,18 +945,26 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo)
|
|
|
/* 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);
|
|
|
-
|
|
|
- /* AGP PLL control */
|
|
|
- if (rinfo->family <= CHIP_FAMILY_RV280) {
|
|
|
- OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID);
|
|
|
|
|
|
- OUTREG(BUS_CNTL1,
|
|
|
- (INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK)
|
|
|
- | (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT)); // 440BX
|
|
|
- } else {
|
|
|
- OUTREG(BUS_CNTL1, INREG(BUS_CNTL1));
|
|
|
- OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000);
|
|
|
+ /* 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 */
|
|
|
+ if (rinfo->family <= CHIP_FAMILY_RV280) {
|
|
|
+ OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID);
|
|
|
+ OUTREG(BUS_CNTL1,
|
|
|
+ (INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK)
|
|
|
+ | (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT)); // 440BX
|
|
|
+ } else {
|
|
|
+ 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));
|
|
@@ -2713,7 +2814,7 @@ static void radeonfb_early_resume(void *data)
|
|
|
|
|
|
#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*/
|
|
|
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_PPC_PMAC)
|
|
|
/* 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
|
|
|
* "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
|
|
|
* 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 (rinfo->is_mobility && rinfo->pm_reg &&
|
|
|
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_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)
|