#include "radeonfb.h" #include #include /* * Currently, only PowerMac do D2 state */ #define CONFIG_RADEON_HAS_D2 CONFIG_PPC_PMAC #ifdef CONFIG_RADEON_HAS_D2 /* * On PowerMac, we assume any mobility chip based machine does D2 */ #ifdef CONFIG_PPC_PMAC static inline int radeon_suspend_to_d2(struct radeonfb_info *rinfo, u32 state) { return rinfo->is_mobility; } #else static inline int radeon_suspend_to_d2(struct radeonfb_info *rinfo, u32 state) { return 0; } #endif #endif /* CONFIG_RADEON_HAS_D2 */ /* * Radeon M6, M7 and M9 Power Management code. This code currently * only supports the mobile chips in D2 mode, that is typically what * is used on Apple laptops, it's based from some informations provided * by ATI along with hours of tracing of MacOS drivers. * * New version of this code almost totally rewritten by ATI, many thanks * for their support. */ void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo) { u32 sclk_cntl; u32 mclk_cntl; u32 sclk_more_cntl; u32 vclk_ecp_cntl; u32 pixclks_cntl; /* Mobility chips only, untested on M9+/M10/11 */ if (!rinfo->is_mobility) return; if (rinfo->family > CHIP_FAMILY_RV250) return; /* Force Core Clocks */ sclk_cntl = INPLL( pllSCLK_CNTL_M6); sclk_cntl |= SCLK_CNTL_M6__FORCE_CP| SCLK_CNTL_M6__FORCE_HDP| SCLK_CNTL_M6__FORCE_DISP1| SCLK_CNTL_M6__FORCE_DISP2| SCLK_CNTL_M6__FORCE_TOP| SCLK_CNTL_M6__FORCE_E2| SCLK_CNTL_M6__FORCE_SE| SCLK_CNTL_M6__FORCE_IDCT| SCLK_CNTL_M6__FORCE_VIP| SCLK_CNTL_M6__FORCE_RE| SCLK_CNTL_M6__FORCE_PB| SCLK_CNTL_M6__FORCE_TAM| SCLK_CNTL_M6__FORCE_TDM| SCLK_CNTL_M6__FORCE_RB| SCLK_CNTL_M6__FORCE_TV_SCLK| SCLK_CNTL_M6__FORCE_SUBPIC| SCLK_CNTL_M6__FORCE_OV0; OUTPLL( pllSCLK_CNTL_M6, sclk_cntl); sclk_more_cntl = INPLL(pllSCLK_MORE_CNTL); sclk_more_cntl |= SCLK_MORE_CNTL__FORCE_DISPREGS| SCLK_MORE_CNTL__FORCE_MC_GUI| SCLK_MORE_CNTL__FORCE_MC_HOST; OUTPLL(pllSCLK_MORE_CNTL, sclk_more_cntl); /* Force Display clocks */ vclk_ecp_cntl = INPLL( pllVCLK_ECP_CNTL); vclk_ecp_cntl &= ~( VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb); OUTPLL( pllVCLK_ECP_CNTL, vclk_ecp_cntl); pixclks_cntl = INPLL( pllPIXCLKS_CNTL); pixclks_cntl &= ~( PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb | PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb| PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb | PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb| PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb| PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb| PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb); OUTPLL( pllPIXCLKS_CNTL, pixclks_cntl); /* Force Memory Clocks */ mclk_cntl = INPLL( pllMCLK_CNTL_M6); mclk_cntl &= ~( MCLK_CNTL_M6__FORCE_MCLKA | MCLK_CNTL_M6__FORCE_MCLKB | MCLK_CNTL_M6__FORCE_YCLKA | MCLK_CNTL_M6__FORCE_YCLKB ); OUTPLL( pllMCLK_CNTL_M6, mclk_cntl); } void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo) { u32 clk_pwrmgt_cntl; u32 sclk_cntl; u32 sclk_more_cntl; u32 clk_pin_cntl; u32 pixclks_cntl; u32 vclk_ecp_cntl; u32 mclk_cntl; u32 mclk_misc; /* Mobility chips only, untested on M9+/M10/11 */ if (!rinfo->is_mobility) return; if (rinfo->family > CHIP_FAMILY_RV250) return; /* Set Latencies */ clk_pwrmgt_cntl = INPLL( pllCLK_PWRMGT_CNTL_M6); clk_pwrmgt_cntl &= ~( CLK_PWRMGT_CNTL_M6__ENGINE_DYNCLK_MODE_MASK| CLK_PWRMGT_CNTL_M6__ACTIVE_HILO_LAT_MASK| CLK_PWRMGT_CNTL_M6__DISP_DYN_STOP_LAT_MASK| CLK_PWRMGT_CNTL_M6__DYN_STOP_MODE_MASK); /* Mode 1 */ clk_pwrmgt_cntl = CLK_PWRMGT_CNTL_M6__MC_CH_MODE| CLK_PWRMGT_CNTL_M6__ENGINE_DYNCLK_MODE | (1<save_regs[0] = INPLL(PLL_PWRMGT_CNTL); rinfo->save_regs[1] = INPLL(CLK_PWRMGT_CNTL); rinfo->save_regs[2] = INPLL(MCLK_CNTL); rinfo->save_regs[3] = INPLL(SCLK_CNTL); rinfo->save_regs[4] = INPLL(CLK_PIN_CNTL); rinfo->save_regs[5] = INPLL(VCLK_ECP_CNTL); rinfo->save_regs[6] = INPLL(PIXCLKS_CNTL); rinfo->save_regs[7] = INPLL(MCLK_MISC); rinfo->save_regs[8] = INPLL(P2PLL_CNTL); rinfo->save_regs[9] = INREG(DISP_MISC_CNTL); rinfo->save_regs[10] = INREG(DISP_PWR_MAN); rinfo->save_regs[11] = INREG(LVDS_GEN_CNTL); rinfo->save_regs[12] = INREG(LVDS_PLL_CNTL); rinfo->save_regs[13] = INREG(TV_DAC_CNTL); rinfo->save_regs[14] = INREG(BUS_CNTL1); rinfo->save_regs[15] = INREG(CRTC_OFFSET_CNTL); rinfo->save_regs[16] = INREG(AGP_CNTL); rinfo->save_regs[17] = (INREG(CRTC_GEN_CNTL) & 0xfdffffff) | 0x04000000; rinfo->save_regs[18] = (INREG(CRTC2_GEN_CNTL) & 0xfdffffff) | 0x04000000; rinfo->save_regs[19] = INREG(GPIOPAD_A); rinfo->save_regs[20] = INREG(GPIOPAD_EN); rinfo->save_regs[21] = INREG(GPIOPAD_MASK); rinfo->save_regs[22] = INREG(ZV_LCDPAD_A); rinfo->save_regs[23] = INREG(ZV_LCDPAD_EN); rinfo->save_regs[24] = INREG(ZV_LCDPAD_MASK); rinfo->save_regs[25] = INREG(GPIO_VGA_DDC); rinfo->save_regs[26] = INREG(GPIO_DVI_DDC); rinfo->save_regs[27] = INREG(GPIO_MONID); rinfo->save_regs[28] = INREG(GPIO_CRT2_DDC); rinfo->save_regs[29] = INREG(SURFACE_CNTL); rinfo->save_regs[30] = INREG(MC_FB_LOCATION); rinfo->save_regs[31] = INREG(DISPLAY_BASE_ADDR); rinfo->save_regs[32] = INREG(MC_AGP_LOCATION); rinfo->save_regs[33] = INREG(CRTC2_DISPLAY_BASE_ADDR); } static void radeon_pm_restore_regs(struct radeonfb_info *rinfo) { OUTPLL(P2PLL_CNTL, rinfo->save_regs[8] & 0xFFFFFFFE); /* First */ OUTPLL(PLL_PWRMGT_CNTL, rinfo->save_regs[0]); OUTPLL(CLK_PWRMGT_CNTL, rinfo->save_regs[1]); OUTPLL(MCLK_CNTL, rinfo->save_regs[2]); OUTPLL(SCLK_CNTL, rinfo->save_regs[3]); OUTPLL(CLK_PIN_CNTL, rinfo->save_regs[4]); OUTPLL(VCLK_ECP_CNTL, rinfo->save_regs[5]); OUTPLL(PIXCLKS_CNTL, rinfo->save_regs[6]); OUTPLL(MCLK_MISC, rinfo->save_regs[7]); OUTREG(SURFACE_CNTL, rinfo->save_regs[29]); OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]); OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]); OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]); OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]); OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]); OUTREG(DISP_PWR_MAN, rinfo->save_regs[10]); OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11]); OUTREG(LVDS_PLL_CNTL,rinfo->save_regs[12]); OUTREG(TV_DAC_CNTL, rinfo->save_regs[13]); OUTREG(BUS_CNTL1, rinfo->save_regs[14]); OUTREG(CRTC_OFFSET_CNTL, rinfo->save_regs[15]); OUTREG(AGP_CNTL, rinfo->save_regs[16]); OUTREG(CRTC_GEN_CNTL, rinfo->save_regs[17]); OUTREG(CRTC2_GEN_CNTL, rinfo->save_regs[18]); // wait VBL before that one ? OUTPLL(P2PLL_CNTL, rinfo->save_regs[8]); OUTREG(GPIOPAD_A, rinfo->save_regs[19]); OUTREG(GPIOPAD_EN, rinfo->save_regs[20]); OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]); OUTREG(ZV_LCDPAD_A, rinfo->save_regs[22]); OUTREG(ZV_LCDPAD_EN, rinfo->save_regs[23]); OUTREG(ZV_LCDPAD_MASK, rinfo->save_regs[24]); OUTREG(GPIO_VGA_DDC, rinfo->save_regs[25]); OUTREG(GPIO_DVI_DDC, rinfo->save_regs[26]); OUTREG(GPIO_MONID, rinfo->save_regs[27]); OUTREG(GPIO_CRT2_DDC, rinfo->save_regs[28]); } static void radeon_pm_disable_iopad(struct radeonfb_info *rinfo) { OUTREG(GPIOPAD_MASK, 0x0001ffff); OUTREG(GPIOPAD_EN, 0x00000400); OUTREG(GPIOPAD_A, 0x00000000); OUTREG(ZV_LCDPAD_MASK, 0x00000000); OUTREG(ZV_LCDPAD_EN, 0x00000000); OUTREG(ZV_LCDPAD_A, 0x00000000); OUTREG(GPIO_VGA_DDC, 0x00030000); OUTREG(GPIO_DVI_DDC, 0x00000000); OUTREG(GPIO_MONID, 0x00030000); OUTREG(GPIO_CRT2_DDC, 0x00000000); } static void radeon_pm_program_v2clk(struct radeonfb_info *rinfo) { /* we use __INPLL and _OUTPLL and do the locking ourselves... */ unsigned long flags; spin_lock_irqsave(&rinfo->reg_lock, flags); /* Set v2clk to 65MHz */ __OUTPLL(pllPIXCLKS_CNTL, __INPLL(rinfo, pllPIXCLKS_CNTL) & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK); __OUTPLL(pllP2PLL_REF_DIV, 0x0000000c); __OUTPLL(pllP2PLL_CNTL, 0x0000bf00); __OUTPLL(pllP2PLL_DIV_0, 0x00020074 | P2PLL_DIV_0__P2PLL_ATOMIC_UPDATE_W); __OUTPLL(pllP2PLL_CNTL, __INPLL(rinfo, pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_SLEEP); mdelay(1); __OUTPLL(pllP2PLL_CNTL, __INPLL(rinfo, pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_RESET); mdelay( 1); __OUTPLL(pllPIXCLKS_CNTL, (__INPLL(rinfo, pllPIXCLKS_CNTL) & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK) | (0x03 << PIXCLKS_CNTL__PIX2CLK_SRC_SEL__SHIFT)); mdelay( 1); spin_unlock_irqrestore(&rinfo->reg_lock, flags); } static void radeon_pm_low_current(struct radeonfb_info *rinfo) { u32 reg; reg = INREG(BUS_CNTL1); reg &= ~BUS_CNTL1_MOBILE_PLATFORM_SEL_MASK; reg |= BUS_CNTL1_AGPCLK_VALID | (1<is_mobility && rinfo->family <= CHIP_FAMILY_RV250) { radeon_pm_program_mode_reg(rinfo, 0x2000, 1); radeon_pm_program_mode_reg(rinfo, 0x2001, 1); radeon_pm_program_mode_reg(rinfo, 0x2002, 1); radeon_pm_program_mode_reg(rinfo, 0x0132, 1); radeon_pm_program_mode_reg(rinfo, 0x0032, 1); } OUTREG( MEM_SDRAM_MODE_REG, INREG( MEM_SDRAM_MODE_REG) | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); // Init Complete OUTREG( MEM_REFRESH_CNTL, memRefreshCntl); OUTREG( CRTC_GEN_CNTL, crtcGenCntl); OUTREG( CRTC2_GEN_CNTL, crtcGenCntl2); OUTREG( FP_GEN_CNTL, fp_gen_cntl); OUTREG( FP2_GEN_CNTL, fp2_gen_cntl); OUTREG( CRTC_MORE_CNTL, crtc_more_cntl); mdelay( 15); } static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend) { u16 pwr_cmd; u32 tmp; if (!rinfo->pm_reg) return; /* Set the chip into appropriate suspend mode (we use D2, * D3 would require a compete re-initialization of the chip, * including PCI config registers, clocks, AGP conf, ...) */ if (suspend) { printk(KERN_DEBUG "radeonfb: switching to D2 state...\n"); /* Disable dynamic power management of clocks for the * duration of the suspend/resume process */ radeon_pm_disable_dynamic_mode(rinfo); /* Save some registers */ radeon_pm_save_regs(rinfo); /* Prepare mobility chips for suspend. Only do that on <= RV250 chips that * have been tested */ if (rinfo->is_mobility && rinfo->family <= CHIP_FAMILY_RV250) { /* Program V2CLK */ radeon_pm_program_v2clk(rinfo); /* Disable IO PADs */ radeon_pm_disable_iopad(rinfo); /* Set low current */ radeon_pm_low_current(rinfo); /* Prepare chip for power management */ radeon_pm_setup_for_suspend(rinfo); /* Reset the MDLL */ /* because both INPLL and OUTPLL take the same lock, that's why. */ tmp = INPLL( pllMDLL_CKO) | MDLL_CKO__MCKOA_RESET | MDLL_CKO__MCKOB_RESET; OUTPLL( pllMDLL_CKO, tmp ); } /* Switch PCI power managment to D2. */ for (;;) { pci_read_config_word( rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, &pwr_cmd); if (pwr_cmd & 2) break; pci_write_config_word( rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, (pwr_cmd & ~PCI_PM_CTRL_STATE_MASK) | 2); mdelay(500); } } else { printk(KERN_DEBUG "radeonfb: switching to D0 state...\n"); /* Switch back PCI powermanagment to D0 */ mdelay(200); pci_write_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, 0); mdelay(500); /* Reset the SDRAM controller */ radeon_pm_full_reset_sdram(rinfo); /* Restore some registers */ radeon_pm_restore_regs(rinfo); radeon_pm_enable_dynamic_mode(rinfo); } } int radeonfb_pci_suspend(struct pci_dev *pdev, u32 state) { struct fb_info *info = pci_get_drvdata(pdev); struct radeonfb_info *rinfo = info->par; /* We don't do anything but D2, for now we return 0, but * we may want to change that. How do we know if the BIOS * can properly take care of D3 ? Also, with swsusp, we * know we'll be rebooted, ... */ printk(KERN_DEBUG "radeonfb: suspending to state: %d...\n", state); acquire_console_sem(); /* Userland should do this but doesn't... bridge gets suspended * too late. Unfortunately, that works only when AGP is built-in, * not for a module. */ #ifdef CONFIG_AGP agp_enable(0); #endif fb_set_suspend(info, 1); if (!(info->flags & FBINFO_HWACCEL_DISABLED)) { /* Make sure engine is reset */ radeon_engine_idle(); radeonfb_engine_reset(rinfo); radeon_engine_idle(); } /* Blank display and LCD */ radeonfb_blank(VESA_POWERDOWN, info); /* Sleep */ rinfo->asleep = 1; rinfo->lock_blank = 1; /* Suspend the chip to D2 state when supported */ #ifdef CONFIG_RADEON_HAS_D2 if (radeon_suspend_to_d2(rinfo, state)) radeon_set_suspend(rinfo, 1); #endif /* CONFIG_RADEON_HAS_D2 */ release_console_sem(); pdev->dev.power_state = state; return 0; } int radeonfb_pci_resume(struct pci_dev *pdev) { struct fb_info *info = pci_get_drvdata(pdev); struct radeonfb_info *rinfo = info->par; if (pdev->dev.power_state == 0) return 0; acquire_console_sem(); /* Wakeup chip */ #ifdef CONFIG_RADEON_HAS_D2 if (radeon_suspend_to_d2(rinfo, 0)) radeon_set_suspend(rinfo, 0); #endif /* CONFIG_RADEON_HAS_D2 */ rinfo->asleep = 0; /* Restore display & engine */ radeonfb_set_par(info); fb_pan_display(info, &info->var); fb_set_cmap(&info->cmap, info); /* Refresh */ fb_set_suspend(info, 0); /* Unblank */ rinfo->lock_blank = 0; radeonfb_blank(0, info); release_console_sem(); pdev->dev.power_state = 0; printk(KERN_DEBUG "radeonfb: resumed !\n"); return 0; } #endif /* CONFIG_PM */