X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fvideo%2Faty%2Fradeon_pm.c;h=459fda622ad76bcbdcae2ff46c20b2074fb85c83;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=498dc4e520cc439a1df17d9b364f5decb6541811;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c index 498dc4e52..459fda622 100644 --- a/drivers/video/aty/radeon_pm.c +++ b/drivers/video/aty/radeon_pm.c @@ -20,14 +20,32 @@ #include #ifdef CONFIG_PPC_PMAC -#include +#include #include #include #endif +/* For detecting supported PC laptops */ +#ifdef CONFIG_X86 +#include +#endif + #include "ati_ids.h" -void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo) +#ifdef CONFIG_X86 +/* This array holds a list of supported PC laptops. + * Currently only few IBM models are tested. + * If you want to experiment, use dmidecode to find out + * vendor and product codes for Your laptop. + */ +static struct dmi_system_id __devinitdata radeonfb_dmi_table[] = { +#include "radeon_pm_whitelist.h" +}; + +extern int radeon_force_sleep; +#endif + +static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo) { u32 tmp; @@ -62,9 +80,9 @@ 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 | @@ -229,7 +247,7 @@ void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo) radeon_msleep(16); } -void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo) +static void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo) { u32 tmp; @@ -248,7 +266,7 @@ 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,7 +870,14 @@ 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); - + + /* 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 +889,7 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo) 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)); @@ -1155,7 +1181,7 @@ static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo) OUTREG( CRTC_GEN_CNTL, (crtcGenCntl | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B) ); OUTREG( CRTC2_GEN_CNTL, (crtcGenCntl2 | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B) ); - /* This is the code for the Aluminium PowerBooks M10 */ + /* This is the code for the Aluminium PowerBooks M10 / iBooks M11 */ if (rinfo->family == CHIP_FAMILY_RV350) { u32 sdram_mode_reg = rinfo->save_regs[35]; static u32 default_mrtable[] = @@ -1321,8 +1347,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; @@ -1373,7 +1397,9 @@ static void radeon_pm_start_mclk_sclk(struct radeonfb_info *rinfo) /* Reconfigure SPLL charge pump, VCO gain, duty cycle */ tmp = INPLL(pllSPLL_CNTL); OUTREG8(CLOCK_CNTL_INDEX, pllSPLL_CNTL + PLL_WR_EN); + radeon_pll_errata_after_index(rinfo); OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff); + radeon_pll_errata_after_data(rinfo); /* Set SPLL feedback divider */ tmp = INPLL(pllM_SPLL_REF_FB_DIV); @@ -1406,7 +1432,9 @@ static void radeon_pm_start_mclk_sclk(struct radeonfb_info *rinfo) /* Reconfigure MPLL charge pump, VCO gain, duty cycle */ tmp = INPLL(pllMPLL_CNTL); OUTREG8(CLOCK_CNTL_INDEX, pllMPLL_CNTL + PLL_WR_EN); + radeon_pll_errata_after_index(rinfo); OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff); + radeon_pll_errata_after_data(rinfo); /* Set MPLL feedback divider */ tmp = INPLL(pllM_SPLL_REF_FB_DIV); @@ -1525,7 +1553,9 @@ static void radeon_pm_restore_pixel_pll(struct radeonfb_info *rinfo) u32 tmp; OUTREG8(CLOCK_CNTL_INDEX, pllHTOTAL_CNTL + PLL_WR_EN); + radeon_pll_errata_after_index(rinfo); OUTREG8(CLOCK_CNTL_DATA, 0); + radeon_pll_errata_after_data(rinfo); tmp = INPLL(pllVCLK_ECP_CNTL); OUTPLL(pllVCLK_ECP_CNTL, tmp | 0x80); @@ -1541,11 +1571,9 @@ static void radeon_pm_restore_pixel_pll(struct radeonfb_info *rinfo) */ tmp = INPLL(pllPPLL_CNTL); OUTREG8(CLOCK_CNTL_INDEX, pllSPLL_CNTL + PLL_WR_EN); + radeon_pll_errata_after_index(rinfo); OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff); - - /* Not sure what was intended here ... */ - tmp = INREG(CLOCK_CNTL_INDEX); - OUTREG(CLOCK_CNTL_INDEX, tmp); + radeon_pll_errata_after_data(rinfo); /* Restore our "reference" PPLL divider set by firmware * according to proper spread spectrum calculations @@ -1570,6 +1598,8 @@ static void radeon_pm_restore_pixel_pll(struct radeonfb_info *rinfo) /* Switch pixel clock to firmware default div 0 */ OUTREG8(CLOCK_CNTL_INDEX+1, 0); + radeon_pll_errata_after_index(rinfo); + radeon_pll_errata_after_data(rinfo); } static void radeon_pm_m10_reconfigure_mc(struct radeonfb_info *rinfo) @@ -1830,6 +1860,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]); @@ -2074,7 +2106,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); @@ -2157,7 +2189,9 @@ static void radeon_reinitialize_QW(struct radeonfb_info *rinfo) tmp = INPLL(MPLL_CNTL); OUTREG8(CLOCK_CNTL_INDEX, MPLL_CNTL + PLL_WR_EN); + radeon_pll_errata_after_index(rinfo); OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff); + radeon_pll_errata_after_data(rinfo); tmp = INPLL(M_SPLL_REF_FB_DIV); OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0x5900); @@ -2178,7 +2212,9 @@ static void radeon_reinitialize_QW(struct radeonfb_info *rinfo) tmp = INPLL(SPLL_CNTL); OUTREG8(CLOCK_CNTL_INDEX, SPLL_CNTL + PLL_WR_EN); + radeon_pll_errata_after_index(rinfo); OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff); + radeon_pll_errata_after_data(rinfo); tmp = INPLL(M_SPLL_REF_FB_DIV); OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0x780000); @@ -2306,7 +2342,9 @@ static void radeon_reinitialize_QW(struct radeonfb_info *rinfo) OUTREG(CRTC_H_SYNC_STRT_WID, 0x008e0580); OUTREG(CRTC_H_TOTAL_DISP, 0x009f00d2); OUTREG8(CLOCK_CNTL_INDEX, HTOTAL_CNTL + PLL_WR_EN); + radeon_pll_errata_after_index(rinfo); OUTREG8(CLOCK_CNTL_DATA, 0); + radeon_pll_errata_after_data(rinfo); OUTREG(CRTC_V_SYNC_STRT_WID, 0x00830403); OUTREG(CRTC_V_TOTAL_DISP, 0x03ff0429); OUTREG(FP_CRTC_H_TOTAL_DISP, 0x009f0033); @@ -2328,10 +2366,15 @@ static void radeon_reinitialize_QW(struct radeonfb_info *rinfo) INPLL(PPLL_REF_DIV); OUTREG8(CLOCK_CNTL_INDEX, PPLL_CNTL + PLL_WR_EN); + radeon_pll_errata_after_index(rinfo); OUTREG8(CLOCK_CNTL_DATA + 1, 0xbc); + radeon_pll_errata_after_data(rinfo); tmp = INREG(CLOCK_CNTL_INDEX); + radeon_pll_errata_after_index(rinfo); OUTREG(CLOCK_CNTL_INDEX, tmp & 0xff); + radeon_pll_errata_after_index(rinfo); + radeon_pll_errata_after_data(rinfo); OUTPLL(PPLL_DIV_0, 0x48090); @@ -2400,7 +2443,8 @@ static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend) * including PCI config registers, clocks, AGP conf, ...) */ if (suspend) { - printk(KERN_DEBUG "radeonfb: switching to D2 state...\n"); + printk(KERN_DEBUG "radeonfb (%s): switching to D2 state...\n", + pci_name(rinfo->pdev)); /* Disable dynamic power management of clocks for the * duration of the suspend/resume process @@ -2453,7 +2497,8 @@ static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend) mdelay(500); } } else { - printk(KERN_DEBUG "radeonfb: switching to D0 state...\n"); + printk(KERN_DEBUG "radeonfb (%s): switching to D0 state...\n", + pci_name(rinfo->pdev)); /* Switch back PCI powermanagment to D0 */ mdelay(200); @@ -2501,31 +2546,25 @@ static int radeon_restore_pci_cfg(struct radeonfb_info *rinfo) } -static/*extern*/ int susdisking = 0; - -int radeonfb_pci_suspend(struct pci_dev *pdev, u32 state) +int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) { struct fb_info *info = pci_get_drvdata(pdev); struct radeonfb_info *rinfo = info->par; int i; - if (state == pdev->dev.power.power_state) + if (state.event == pdev->dev.power.power_state.event) return 0; printk(KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n", - pci_name(pdev), state); + pci_name(pdev), state.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) - goto done; - if (susdisking) { - printk("suspending to disk but state = %d\n", state); + if (state.event == PM_EVENT_FREEZE) goto done; - } acquire_console_sem(); @@ -2546,6 +2585,14 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, u32 state) rinfo->lock_blank = 1; del_timer_sync(&rinfo->lvds_timer); +#ifdef CONFIG_PPC_PMAC + /* On powermac, we have hooks to properly suspend/resume AGP now, + * use them here. We'll ultimately need some generic support here, + * but the generic code isn't quite ready for that yet + */ + pmac_suspend_agp_for_card(pdev); +#endif /* CONFIG_PPC_PMAC */ + /* If we support wakeup from poweroff, we save all regs we can including cfg * space */ @@ -2569,12 +2616,11 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, u32 state) OUTREG(LVDS_PLL_CNTL, (INREG(LVDS_PLL_CNTL) & ~30000) | 0x20000); mdelay(20); OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_DIGON)); - - // FIXME: Use PCI layer - for (i = 0; i < 64; ++i) - pci_read_config_dword(rinfo->pdev, i * 4, - &rinfo->cfg_save[i]); } + // FIXME: Use PCI layer + for (i = 0; i < 64; ++i) + pci_read_config_dword(pdev, i * 4, &rinfo->cfg_save[i]); + pci_disable_device(pdev); } /* If we support D2, we go to it (should be fixed later with a flag forcing * D3 only for some laptops) @@ -2596,7 +2642,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) { @@ -2606,7 +2652,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)) { @@ -2617,7 +2663,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) */ @@ -2657,13 +2703,22 @@ int radeonfb_pci_resume(struct pci_dev *pdev) rinfo->lock_blank = 0; radeon_screen_blank(rinfo, FB_BLANK_UNBLANK, 1); +#ifdef CONFIG_PPC_PMAC + /* On powermac, we have hooks to properly suspend/resume AGP now, + * use them here. We'll ultimately need some generic support here, + * but the generic code isn't quite ready for that yet + */ + pmac_resume_agp_for_card(pdev); +#endif /* CONFIG_PPC_PMAC */ + + /* Check status of dynclk */ if (rinfo->dynclk == 1) radeon_pm_enable_dynamic_mode(rinfo); else if (rinfo->dynclk == 0) radeon_pm_disable_dynamic_mode(rinfo); - pdev->dev.power.power_state = 0; + pdev->dev.power.power_state = PMSG_ON; bail: release_console_sem(); @@ -2699,22 +2754,34 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk) printk("radeonfb: Dynamic Clock Power Management disabled\n"); } +#if defined(CONFIG_PM) /* 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) { + /* 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) 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; } @@ -2727,8 +2794,6 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk) if (!strcmp(rinfo->of_node->name, "ATY,ViaParent")) { rinfo->reinit_func = radeon_reinitialize_M9P; rinfo->pm_mode |= radeon_pm_off; - /* Workaround not used for now */ - rinfo->m9p_workaround = 1; } /* If any of the above is set, we assume the machine can sleep/resume. @@ -2749,12 +2814,36 @@ 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) */ + +/* The PM code also works on some PC laptops. + * Only a few models are actually tested so Your mileage may vary. + * We can do D2 on at least M7 and M9 on some IBM ThinkPad T41 models. + */ +#if defined(CONFIG_PM) && defined(CONFIG_X86) + if (radeon_force_sleep || dmi_check_system(radeonfb_dmi_table)) { + if (radeon_force_sleep) + printk("radeonfb: forcefully enabling sleep mode\n"); + else + printk("radeonfb: enabling 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_PM) && defined(CONFIG_X86) */ } 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