📄 radeon_pm.c
字号:
/* * drivers/video/aty/radeon_pm.c * * Copyright 2003,2004 Ben. Herrenschmidt <benh@kernel.crashing.org> * Copyright 2004 Paul Mackerras <paulus@samba.org> * * This is the power management code for ATI radeon chipsets. It contains * some dynamic clock PM enable/disable code similar to what X.org does, * some D2-state (APM-style) sleep/wakeup code for use on some PowerMacs, * and the necessary bits to re-initialize from scratch a few chips found * on PowerMacs as well. The later could be extended to more platforms * provided the memory controller configuration code be made more generic, * and you can get the proper mode register commands for your RAMs. * Those things may be found in the BIOS image... */#include "radeonfb.h"#include <linux/console.h>#include <linux/agp_backend.h>#ifdef CONFIG_PPC_PMAC#include <asm/machdep.h>#include <asm/prom.h>#include <asm/pmac_feature.h>#endif#include "ati_ids.h"/* * 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)static void radeon_reinitialize_M10(struct radeonfb_info *rinfo);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; /* RV100 */ if ((rinfo->family == CHIP_FAMILY_RV100) && (!rinfo->is_mobility)) { if (rinfo->has_CRTC2) { tmp = INPLL(pllSCLK_CNTL); tmp &= ~SCLK_CNTL__DYN_STOP_LAT_MASK; tmp |= SCLK_CNTL__CP_MAX_DYN_STOP_LAT | SCLK_CNTL__FORCEON_MASK; OUTPLL(pllSCLK_CNTL, tmp); } tmp = INPLL(pllMCLK_CNTL); tmp |= (MCLK_CNTL__FORCE_MCLKA | MCLK_CNTL__FORCE_MCLKB | MCLK_CNTL__FORCE_YCLKA | MCLK_CNTL__FORCE_YCLKB | MCLK_CNTL__FORCE_AIC | MCLK_CNTL__FORCE_MC); OUTPLL(pllMCLK_CNTL, tmp); return; } /* R100 */ if (!rinfo->has_CRTC2) { tmp = INPLL(pllSCLK_CNTL); tmp |= (SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_HDP | SCLK_CNTL__FORCE_DISP1 | SCLK_CNTL__FORCE_TOP | SCLK_CNTL__FORCE_E2 | SCLK_CNTL__FORCE_SE | SCLK_CNTL__FORCE_IDCT | SCLK_CNTL__FORCE_VIP | SCLK_CNTL__FORCE_RE | SCLK_CNTL__FORCE_PB | SCLK_CNTL__FORCE_TAM | SCLK_CNTL__FORCE_TDM | SCLK_CNTL__FORCE_RB); OUTPLL(pllSCLK_CNTL, tmp); return; } /* RV350 (M10/M11) */ if (rinfo->family == CHIP_FAMILY_RV350) { /* for RV350/M10/M11, no delays are required. */ tmp = INPLL(pllSCLK_CNTL2); tmp |= (SCLK_CNTL2__R300_FORCE_TCL | SCLK_CNTL2__R300_FORCE_GA | SCLK_CNTL2__R300_FORCE_CBA); OUTPLL(pllSCLK_CNTL2, tmp); tmp = INPLL(pllSCLK_CNTL); tmp |= (SCLK_CNTL__FORCE_DISP2 | SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_HDP | SCLK_CNTL__FORCE_DISP1 | SCLK_CNTL__FORCE_TOP | SCLK_CNTL__FORCE_E2 | SCLK_CNTL__R300_FORCE_VAP | SCLK_CNTL__FORCE_IDCT | SCLK_CNTL__FORCE_VIP | SCLK_CNTL__R300_FORCE_SR | SCLK_CNTL__R300_FORCE_PX | SCLK_CNTL__R300_FORCE_TX | SCLK_CNTL__R300_FORCE_US | SCLK_CNTL__FORCE_TV_SCLK | SCLK_CNTL__R300_FORCE_SU | SCLK_CNTL__FORCE_OV0); OUTPLL(pllSCLK_CNTL, tmp); tmp = INPLL(pllSCLK_MORE_CNTL); tmp |= (SCLK_MORE_CNTL__FORCE_DISPREGS | SCLK_MORE_CNTL__FORCE_MC_GUI | SCLK_MORE_CNTL__FORCE_MC_HOST); OUTPLL(pllSCLK_MORE_CNTL, tmp); tmp = INPLL(pllMCLK_CNTL); tmp |= (MCLK_CNTL__FORCE_MCLKA | MCLK_CNTL__FORCE_MCLKB | MCLK_CNTL__FORCE_YCLKA | MCLK_CNTL__FORCE_YCLKB | MCLK_CNTL__FORCE_MC); OUTPLL(pllMCLK_CNTL, tmp); tmp = INPLL(pllVCLK_ECP_CNTL); tmp &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb | VCLK_ECP_CNTL__R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF); OUTPLL(pllVCLK_ECP_CNTL, tmp); tmp = INPLL(pllPIXCLKS_CNTL); tmp &= ~(PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb | PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb | PIXCLKS_CNTL__DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | PIXCLKS_CNTL__R300_DVOCLK_ALWAYS_ONb | PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb | PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb | PIXCLKS_CNTL__R300_PIXCLK_DVO_ALWAYS_ONb | PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb | PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb | PIXCLKS_CNTL__R300_PIXCLK_TRANS_ALWAYS_ONb | PIXCLKS_CNTL__R300_PIXCLK_TVO_ALWAYS_ONb | PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb | PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb | PIXCLKS_CNTL__R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF); OUTPLL(pllPIXCLKS_CNTL, tmp); return; } /* Default */ /* Force Core Clocks */ tmp = INPLL(pllSCLK_CNTL); tmp |= (SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_E2); /* XFree doesn't do that case, but we had this code from Apple and it * seem necessary for proper suspend/resume operations */ if (rinfo->is_mobility) { tmp |= SCLK_CNTL__FORCE_HDP| SCLK_CNTL__FORCE_DISP1| SCLK_CNTL__FORCE_DISP2| SCLK_CNTL__FORCE_TOP| SCLK_CNTL__FORCE_SE| SCLK_CNTL__FORCE_IDCT| SCLK_CNTL__FORCE_VIP| SCLK_CNTL__FORCE_PB| SCLK_CNTL__FORCE_RE| SCLK_CNTL__FORCE_TAM| SCLK_CNTL__FORCE_TDM| SCLK_CNTL__FORCE_RB| SCLK_CNTL__FORCE_TV_SCLK| SCLK_CNTL__FORCE_SUBPIC| SCLK_CNTL__FORCE_OV0; } else if (rinfo->family == CHIP_FAMILY_R300 || rinfo->family == CHIP_FAMILY_R350) { tmp |= SCLK_CNTL__FORCE_HDP | SCLK_CNTL__FORCE_DISP1 | SCLK_CNTL__FORCE_DISP2 | SCLK_CNTL__FORCE_TOP | SCLK_CNTL__FORCE_IDCT | SCLK_CNTL__FORCE_VIP; } OUTPLL(pllSCLK_CNTL, tmp); radeon_msleep(16); if (rinfo->family == CHIP_FAMILY_R300 || rinfo->family == CHIP_FAMILY_R350) { tmp = INPLL(pllSCLK_CNTL2); tmp |= SCLK_CNTL2__R300_FORCE_TCL | SCLK_CNTL2__R300_FORCE_GA | SCLK_CNTL2__R300_FORCE_CBA; OUTPLL(pllSCLK_CNTL2, tmp); radeon_msleep(16); } tmp = INPLL(pllCLK_PIN_CNTL); tmp &= ~CLK_PIN_CNTL__SCLK_DYN_START_CNTL; OUTPLL(pllCLK_PIN_CNTL, tmp); radeon_msleep(15); if (rinfo->is_IGP) { /* Weird ... X is _un_ forcing clocks here, I think it's * doing backward. Imitate it for now... */ tmp = INPLL(pllMCLK_CNTL); tmp &= ~(MCLK_CNTL__FORCE_MCLKA | MCLK_CNTL__FORCE_YCLKA); OUTPLL(pllMCLK_CNTL, tmp); radeon_msleep(16); } /* Hrm... same shit, X doesn't do that but I have to */ else if (rinfo->is_mobility) { tmp = INPLL(pllMCLK_CNTL); tmp |= (MCLK_CNTL__FORCE_MCLKA | MCLK_CNTL__FORCE_MCLKB | MCLK_CNTL__FORCE_YCLKA | MCLK_CNTL__FORCE_YCLKB); OUTPLL(pllMCLK_CNTL, tmp); radeon_msleep(16); tmp = INPLL(pllMCLK_MISC); tmp &= ~(MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT| MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT| MCLK_MISC__MC_MCLK_DYN_ENABLE| MCLK_MISC__IO_MCLK_DYN_ENABLE); OUTPLL(pllMCLK_MISC, tmp); radeon_msleep(15); } if (rinfo->is_mobility) { tmp = INPLL(pllSCLK_MORE_CNTL); tmp |= SCLK_MORE_CNTL__FORCE_DISPREGS| SCLK_MORE_CNTL__FORCE_MC_GUI| SCLK_MORE_CNTL__FORCE_MC_HOST; OUTPLL(pllSCLK_MORE_CNTL, tmp); radeon_msleep(16); } tmp = INPLL(pllPIXCLKS_CNTL); tmp &= ~(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, tmp); radeon_msleep(16); tmp = INPLL( pllVCLK_ECP_CNTL); tmp &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb); OUTPLL( pllVCLK_ECP_CNTL, tmp); radeon_msleep(16);}static void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo){ u32 tmp; /* R100 */ if (!rinfo->has_CRTC2) { tmp = INPLL(pllSCLK_CNTL); if ((INREG(CNFG_CNTL) & CFG_ATI_REV_ID_MASK) > CFG_ATI_REV_A13) tmp &= ~(SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_RB); tmp &= ~(SCLK_CNTL__FORCE_HDP | SCLK_CNTL__FORCE_DISP1 | SCLK_CNTL__FORCE_TOP | SCLK_CNTL__FORCE_SE | SCLK_CNTL__FORCE_IDCT | SCLK_CNTL__FORCE_RE | SCLK_CNTL__FORCE_PB | SCLK_CNTL__FORCE_TAM | SCLK_CNTL__FORCE_TDM); OUTPLL(pllSCLK_CNTL, tmp); return; } /* M10/M11 */ if (rinfo->family == CHIP_FAMILY_RV350) { tmp = INPLL(pllSCLK_CNTL2); tmp &= ~(SCLK_CNTL2__R300_FORCE_TCL | SCLK_CNTL2__R300_FORCE_GA | SCLK_CNTL2__R300_FORCE_CBA); tmp |= (SCLK_CNTL2__R300_TCL_MAX_DYN_STOP_LAT | SCLK_CNTL2__R300_GA_MAX_DYN_STOP_LAT | SCLK_CNTL2__R300_CBA_MAX_DYN_STOP_LAT); OUTPLL(pllSCLK_CNTL2, tmp); tmp = INPLL(pllSCLK_CNTL); tmp &= ~(SCLK_CNTL__FORCE_DISP2 | SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_HDP | SCLK_CNTL__FORCE_DISP1 | SCLK_CNTL__FORCE_TOP | SCLK_CNTL__FORCE_E2 | SCLK_CNTL__R300_FORCE_VAP | SCLK_CNTL__FORCE_IDCT | SCLK_CNTL__FORCE_VIP | SCLK_CNTL__R300_FORCE_SR | SCLK_CNTL__R300_FORCE_PX | SCLK_CNTL__R300_FORCE_TX | SCLK_CNTL__R300_FORCE_US | SCLK_CNTL__FORCE_TV_SCLK | SCLK_CNTL__R300_FORCE_SU | SCLK_CNTL__FORCE_OV0); tmp |= SCLK_CNTL__DYN_STOP_LAT_MASK; OUTPLL(pllSCLK_CNTL, tmp); tmp = INPLL(pllSCLK_MORE_CNTL); tmp &= ~SCLK_MORE_CNTL__FORCEON; tmp |= SCLK_MORE_CNTL__DISPREGS_MAX_DYN_STOP_LAT | SCLK_MORE_CNTL__MC_GUI_MAX_DYN_STOP_LAT | SCLK_MORE_CNTL__MC_HOST_MAX_DYN_STOP_LAT; OUTPLL(pllSCLK_MORE_CNTL, tmp); tmp = INPLL(pllVCLK_ECP_CNTL); tmp |= (VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb); OUTPLL(pllVCLK_ECP_CNTL, tmp); tmp = INPLL(pllPIXCLKS_CNTL); tmp |= (PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb | PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb | PIXCLKS_CNTL__DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | PIXCLKS_CNTL__R300_DVOCLK_ALWAYS_ONb | PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb | PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb | PIXCLKS_CNTL__R300_PIXCLK_DVO_ALWAYS_ONb | PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb | PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb | PIXCLKS_CNTL__R300_PIXCLK_TRANS_ALWAYS_ONb | PIXCLKS_CNTL__R300_PIXCLK_TVO_ALWAYS_ONb | PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb | PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb); OUTPLL(pllPIXCLKS_CNTL, tmp); tmp = INPLL(pllMCLK_MISC); tmp |= (MCLK_MISC__MC_MCLK_DYN_ENABLE | MCLK_MISC__IO_MCLK_DYN_ENABLE); OUTPLL(pllMCLK_MISC, tmp); tmp = INPLL(pllMCLK_CNTL); tmp |= (MCLK_CNTL__FORCE_MCLKA | MCLK_CNTL__FORCE_MCLKB); tmp &= ~(MCLK_CNTL__FORCE_YCLKA | MCLK_CNTL__FORCE_YCLKB | MCLK_CNTL__FORCE_MC); /* Some releases of vbios have set DISABLE_MC_MCLKA * and DISABLE_MC_MCLKB bits in the vbios table. Setting these * bits will cause H/W hang when reading video memory with dynamic * clocking enabled. */ if ((tmp & MCLK_CNTL__R300_DISABLE_MC_MCLKA) && (tmp & MCLK_CNTL__R300_DISABLE_MC_MCLKB)) { /* If both bits are set, then check the active channels */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -