X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=drivers%2Fvideo%2Faty%2Fradeon_pm.c;h=9a2b0d69b0ae7ebdb8bc0dac91a686e986c27f11;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=98352af393255d44907ad92270b4f236cc3210ac;hpb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;p=linux-2.6.git diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c index 98352af39..9a2b0d69b 100644 --- a/drivers/video/aty/radeon_pm.c +++ b/drivers/video/aty/radeon_pm.c @@ -20,13 +20,109 @@ #include #ifdef CONFIG_PPC_PMAC -#include +#include #include #include #endif #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), + BUGFIX("Acer Aspire 2010", + PCI_VENDOR_ID_AI, 0x0061, + 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; @@ -62,9 +158,9 @@ static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo) OUTPLL(pllSCLK_CNTL, tmp); return; } - /* RV350 (M10) */ + /* RV350 (M10/M11) */ if (rinfo->family == CHIP_FAMILY_RV350) { - /* for RV350/M10, no delays are required. */ + /* for RV350/M10/M11, no delays are required. */ tmp = INPLL(pllSCLK_CNTL2); tmp |= (SCLK_CNTL2__R300_FORCE_TCL | SCLK_CNTL2__R300_FORCE_GA | @@ -248,7 +344,7 @@ static void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo) return; } - /* M10 */ + /* M10/M11 */ if (rinfo->family == CHIP_FAMILY_RV350) { tmp = INPLL(pllSCLK_CNTL2); tmp &= ~(SCLK_CNTL2__R300_FORCE_TCL | @@ -852,18 +948,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<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<family == CHIP_FAMILY_RV350) { u32 sdram_mode_reg = rinfo->save_regs[35]; static u32 default_mrtable[] = @@ -1167,7 +1271,7 @@ static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo) 0x21320032, 0xa1320032, 0x21320032, 0xffffffff, 0x31320032 }; - u32 *mrtable = default_mrtable; + const u32 *mrtable = default_mrtable; int i, mrtable_size = ARRAY_SIZE(default_mrtable); mdelay(30); @@ -1186,7 +1290,7 @@ static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo) if (rinfo->of_node != NULL) { int size; - mrtable = (u32 *)get_property(rinfo->of_node, "ATY,MRT", &size); + mrtable = get_property(rinfo->of_node, "ATY,MRT", &size); if (mrtable) mrtable_size = size >> 2; else @@ -1321,8 +1425,6 @@ static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo) mdelay( 15); } -#ifdef CONFIG_PPC_OF - static void radeon_pm_reset_pad_ctlr_strength(struct radeonfb_info *rinfo) { u32 tmp, tmp2; @@ -1836,6 +1938,8 @@ static void radeon_reinitialize_M10(struct radeonfb_info *rinfo) radeon_pm_m10_enable_lvds_spread_spectrum(rinfo); } +#ifdef CONFIG_PPC_OF + static void radeon_pm_m9p_reconfigure_mc(struct radeonfb_info *rinfo) { OUTREG(MC_CNTL, rinfo->save_regs[46]); @@ -2080,7 +2184,7 @@ static void radeon_reinitialize_M9P(struct radeonfb_info *rinfo) OUTREG(0x2ec, 0x6332a3f0); mdelay(17); - OUTPLL(pllPPLL_REF_DIV, rinfo->pll.ref_div);; + OUTPLL(pllPPLL_REF_DIV, rinfo->pll.ref_div); OUTPLL(pllPPLL_DIV_0, rinfo->save_regs[92]); mdelay(40); @@ -2520,25 +2624,28 @@ static int radeon_restore_pci_cfg(struct radeonfb_info *rinfo) } -int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) +int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) { struct fb_info *info = pci_get_drvdata(pdev); struct radeonfb_info *rinfo = info->par; int i; - if (state == pdev->dev.power.power_state) + if (mesg.event == pdev->dev.power.power_state.event) return 0; - printk(KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n", - pci_name(pdev), state); + printk(KERN_DEBUG "radeonfb (%s): suspending for event: %d...\n", + pci_name(pdev), mesg.event); /* For suspend-to-disk, we cheat here. We don't suspend anything and * let fbcon continue drawing until we are all set. That shouldn't * really cause any problem at this point, provided that the wakeup * code knows that any state in memory may not match the HW */ - if (state != PM_SUSPEND_MEM) + switch (mesg.event) { + case PM_EVENT_FREEZE: /* about to take snapshot */ + case PM_EVENT_PRETHAW: /* before restoring snapshot */ goto done; + } acquire_console_sem(); @@ -2605,7 +2712,7 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) release_console_sem(); done: - pdev->dev.power.power_state = state; + pdev->dev.power.power_state = mesg; return 0; } @@ -2616,7 +2723,7 @@ int radeonfb_pci_resume(struct pci_dev *pdev) struct radeonfb_info *rinfo = info->par; int rc = 0; - if (pdev->dev.power.power_state == 0) + if (pdev->dev.power.power_state.event == PM_EVENT_ON) return 0; if (rinfo->no_schedule) { @@ -2626,7 +2733,7 @@ int radeonfb_pci_resume(struct pci_dev *pdev) acquire_console_sem(); printk(KERN_DEBUG "radeonfb (%s): resuming from state: %d...\n", - pci_name(pdev), pdev->dev.power.power_state); + pci_name(pdev), pdev->dev.power.power_state.event); if (pci_enable_device(pdev)) { @@ -2637,7 +2744,7 @@ int radeonfb_pci_resume(struct pci_dev *pdev) } pci_set_master(pdev); - if (pdev->dev.power.power_state == PM_SUSPEND_MEM) { + if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { /* Wakeup chip. Check from config space if we were powered off * (todo: additionally, check CLK_PIN_CNTL too) */ @@ -2713,7 +2820,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); @@ -2728,22 +2835,25 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk) printk("radeonfb: Dynamic Clock Power Management disabled\n"); } +#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 */ -#if defined(CONFIG_PM) && defined(CONFIG_PPC_OF) - if (_machine == _MACH_Pmac && rinfo->of_node) { + if (machine_is(powermac) && rinfo->of_node) { if (rinfo->is_mobility && rinfo->pm_reg && rinfo->family <= CHIP_FAMILY_RV250) rinfo->pm_mode |= radeon_pm_d2; /* We can restart Jasper (M10 chip in albooks), BlueStone (7500 chip - * in some desktop G4s), and Via (M9+ chip on iBook G4) + * in some desktop G4s), Via (M9+ chip on iBook G4) and + * Snowy (M11 chip on iBook G4 manufactured after July 2005) */ - if (!strcmp(rinfo->of_node->name, "ATY,JasperParent")) { + if (!strcmp(rinfo->of_node->name, "ATY,JasperParent") || + !strcmp(rinfo->of_node->name, "ATY,SnowyParent")) { rinfo->reinit_func = radeon_reinitialize_M10; rinfo->pm_mode |= radeon_pm_off; } @@ -2776,12 +2886,25 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk) OUTREG(TV_DAC_CNTL, INREG(TV_DAC_CNTL) | 0x07000000); #endif } -#endif /* defined(CONFIG_PM) && defined(CONFIG_PPC_OF) */ +#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) { -#if defined(CONFIG_PM) && defined(CONFIG_PPC_OF) +#if defined(CONFIG_PM) && defined(CONFIG_PPC_PMAC) if (rinfo->pm_mode != radeon_pm_none) pmac_set_early_video_resume(NULL, NULL); #endif