📄 radeon_base.c
字号:
}static int radeonfb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg, struct fb_info *info){ struct radeonfb_info *rinfo = info->par; unsigned int tmp; u32 value = 0; int rc; switch (cmd) { /* * TODO: set mirror accordingly for non-Mobility chipsets with 2 CRTC's * and do something better using 2nd CRTC instead of just hackish * routing to second output */ case FBIO_RADEON_SET_MIRROR: if (!rinfo->is_mobility) return -EINVAL; rc = get_user(value, (__u32 __user *)arg); if (rc) return rc; radeon_fifo_wait(2); if (value & 0x01) { tmp = INREG(LVDS_GEN_CNTL); tmp |= (LVDS_ON | LVDS_BLON); } else { tmp = INREG(LVDS_GEN_CNTL); tmp &= ~(LVDS_ON | LVDS_BLON); } OUTREG(LVDS_GEN_CNTL, tmp); if (value & 0x02) { tmp = INREG(CRTC_EXT_CNTL); tmp |= CRTC_CRT_ON; mirror = 1; } else { tmp = INREG(CRTC_EXT_CNTL); tmp &= ~CRTC_CRT_ON; mirror = 0; } OUTREG(CRTC_EXT_CNTL, tmp); break; case FBIO_RADEON_GET_MIRROR: if (!rinfo->is_mobility) return -EINVAL; tmp = INREG(LVDS_GEN_CNTL); if ((LVDS_ON | LVDS_BLON) & tmp) value |= 0x01; tmp = INREG(CRTC_EXT_CNTL); if (CRTC_CRT_ON & tmp) value |= 0x02; return put_user(value, (__u32 __user *)arg); default: return -EINVAL; } return -EINVAL;}int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch){ u32 val; u32 tmp_pix_clks; int unblank = 0; if (rinfo->lock_blank) return 0; radeon_engine_idle(); val = INREG(CRTC_EXT_CNTL); val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS | CRTC_VSYNC_DIS); switch (blank) { case FB_BLANK_VSYNC_SUSPEND: val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS); break; case FB_BLANK_HSYNC_SUSPEND: val |= (CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS); break; case FB_BLANK_POWERDOWN: val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | CRTC_HSYNC_DIS); break; case FB_BLANK_NORMAL: val |= CRTC_DISPLAY_DIS; break; case FB_BLANK_UNBLANK: default: unblank = 1; } OUTREG(CRTC_EXT_CNTL, val); switch (rinfo->mon1_type) { case MT_DFP: if (unblank) OUTREGP(FP_GEN_CNTL, (FP_FPON | FP_TMDS_EN), ~(FP_FPON | FP_TMDS_EN)); else { if (mode_switch || blank == FB_BLANK_NORMAL) break; OUTREGP(FP_GEN_CNTL, 0, ~(FP_FPON | FP_TMDS_EN)); } break; case MT_LCD: del_timer_sync(&rinfo->lvds_timer); val = INREG(LVDS_GEN_CNTL); if (unblank) { u32 target_val = (val & ~LVDS_DISPLAY_DIS) | LVDS_BLON | LVDS_ON | LVDS_EN | (rinfo->init_state.lvds_gen_cntl & (LVDS_DIGON | LVDS_BL_MOD_EN)); if ((val ^ target_val) == LVDS_DISPLAY_DIS) OUTREG(LVDS_GEN_CNTL, target_val); else if ((val ^ target_val) != 0) { OUTREG(LVDS_GEN_CNTL, target_val & ~(LVDS_ON | LVDS_BL_MOD_EN)); rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; rinfo->init_state.lvds_gen_cntl |= target_val & LVDS_STATE_MASK; if (mode_switch) { radeon_msleep(rinfo->panel_info.pwr_delay); OUTREG(LVDS_GEN_CNTL, target_val); } else { rinfo->pending_lvds_gen_cntl = target_val; mod_timer(&rinfo->lvds_timer, jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); } } } else { val |= LVDS_DISPLAY_DIS; OUTREG(LVDS_GEN_CNTL, val); /* We don't do a full switch-off on a simple mode switch */ if (mode_switch || blank == FB_BLANK_NORMAL) break; /* Asic bug, when turning off LVDS_ON, we have to make sure * RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off */ tmp_pix_clks = INPLL(PIXCLKS_CNTL); if (rinfo->is_mobility || rinfo->is_IGP) OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb); val &= ~(LVDS_BL_MOD_EN); OUTREG(LVDS_GEN_CNTL, val); udelay(100); val &= ~(LVDS_ON | LVDS_EN); OUTREG(LVDS_GEN_CNTL, val); val &= ~LVDS_DIGON; rinfo->pending_lvds_gen_cntl = val; mod_timer(&rinfo->lvds_timer, jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; rinfo->init_state.lvds_gen_cntl |= val & LVDS_STATE_MASK; if (rinfo->is_mobility || rinfo->is_IGP) OUTPLL(PIXCLKS_CNTL, tmp_pix_clks); } break; case MT_CRT: // todo: powerdown DAC default: break; } /* let fbcon do a soft blank for us */ return (blank == FB_BLANK_NORMAL) ? -EINVAL : 0;}static int radeonfb_blank (int blank, struct fb_info *info){ struct radeonfb_info *rinfo = info->par; if (rinfo->asleep) return 0; return radeon_screen_blank(rinfo, blank, 0);}static int radeonfb_setcolreg (unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info){ struct radeonfb_info *rinfo = info->par; u32 pindex; unsigned int i; if (regno > 255) return 1; red >>= 8; green >>= 8; blue >>= 8; rinfo->palette[regno].red = red; rinfo->palette[regno].green = green; rinfo->palette[regno].blue = blue; /* default */ pindex = regno; if (!rinfo->asleep) { u32 dac_cntl2, vclk_cntl = 0; radeon_fifo_wait(9); if (rinfo->is_mobility) { vclk_cntl = INPLL(VCLK_ECP_CNTL); OUTPLL(VCLK_ECP_CNTL, vclk_cntl & ~PIXCLK_DAC_ALWAYS_ONb); } /* Make sure we are on first palette */ if (rinfo->has_CRTC2) { dac_cntl2 = INREG(DAC_CNTL2); dac_cntl2 &= ~DAC2_PALETTE_ACCESS_CNTL; OUTREG(DAC_CNTL2, dac_cntl2); } if (rinfo->bpp == 16) { pindex = regno * 8; if (rinfo->depth == 16 && regno > 63) return 1; if (rinfo->depth == 15 && regno > 31) return 1; /* For 565, the green component is mixed one order below */ if (rinfo->depth == 16) { OUTREG(PALETTE_INDEX, pindex>>1); OUTREG(PALETTE_DATA, (rinfo->palette[regno>>1].red << 16) | (green << 8) | (rinfo->palette[regno>>1].blue)); green = rinfo->palette[regno<<1].green; } } if (rinfo->depth != 16 || regno < 32) { OUTREG(PALETTE_INDEX, pindex); OUTREG(PALETTE_DATA, (red << 16) | (green << 8) | blue); } if (rinfo->is_mobility) OUTPLL(VCLK_ECP_CNTL, vclk_cntl); } if (regno < 16) { u32 *pal = info->pseudo_palette; switch (rinfo->depth) { case 15: pal[regno] = (regno << 10) | (regno << 5) | regno; break; case 16: pal[regno] = (regno << 11) | (regno << 5) | regno; break; case 24: pal[regno] = (regno << 16) | (regno << 8) | regno; break; case 32: i = (regno << 8) | regno; pal[regno] = (i << 16) | i; break; } } return 0;}void radeon_save_state (struct radeonfb_info *rinfo, struct radeon_regs *save){ /* CRTC regs */ save->crtc_gen_cntl = INREG(CRTC_GEN_CNTL); save->crtc_ext_cntl = INREG(CRTC_EXT_CNTL); save->crtc_more_cntl = INREG(CRTC_MORE_CNTL); save->dac_cntl = INREG(DAC_CNTL); save->crtc_h_total_disp = INREG(CRTC_H_TOTAL_DISP); save->crtc_h_sync_strt_wid = INREG(CRTC_H_SYNC_STRT_WID); save->crtc_v_total_disp = INREG(CRTC_V_TOTAL_DISP); save->crtc_v_sync_strt_wid = INREG(CRTC_V_SYNC_STRT_WID); save->crtc_pitch = INREG(CRTC_PITCH); save->surface_cntl = INREG(SURFACE_CNTL); /* FP regs */ save->fp_crtc_h_total_disp = INREG(FP_CRTC_H_TOTAL_DISP); save->fp_crtc_v_total_disp = INREG(FP_CRTC_V_TOTAL_DISP); save->fp_gen_cntl = INREG(FP_GEN_CNTL); save->fp_h_sync_strt_wid = INREG(FP_H_SYNC_STRT_WID); save->fp_horz_stretch = INREG(FP_HORZ_STRETCH); save->fp_v_sync_strt_wid = INREG(FP_V_SYNC_STRT_WID); save->fp_vert_stretch = INREG(FP_VERT_STRETCH); save->lvds_gen_cntl = INREG(LVDS_GEN_CNTL); save->lvds_pll_cntl = INREG(LVDS_PLL_CNTL); save->tmds_crc = INREG(TMDS_CRC); save->tmds_transmitter_cntl = INREG(TMDS_TRANSMITTER_CNTL); save->vclk_ecp_cntl = INPLL(VCLK_ECP_CNTL); /* PLL regs */ save->clk_cntl_index = INREG(CLOCK_CNTL_INDEX) & ~0x3f; save->ppll_div_3 = INPLL(PPLL_DIV_3); save->ppll_ref_div = INPLL(PPLL_REF_DIV);}static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs *mode){ int i; radeon_fifo_wait(20); /* Workaround from XFree */ if (rinfo->is_mobility) { /* A temporal workaround for the occational blanking on certain laptop * panels. This appears to related to the PLL divider registers * (fail to lock?). It occurs even when all dividers are the same * with their old settings. In this case we really don't need to * fiddle with PLL registers. By doing this we can avoid the blanking * problem with some panels. */ if ((mode->ppll_ref_div == (INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK)) && (mode->ppll_div_3 == (INPLL(PPLL_DIV_3) & (PPLL_POST3_DIV_MASK | PPLL_FB3_DIV_MASK)))) { /* We still have to force a switch to selected PPLL div thanks to * an XFree86 driver bug which will switch it away in some cases * even when using UseFDev */ OUTREGP(CLOCK_CNTL_INDEX, mode->clk_cntl_index & PPLL_DIV_SEL_MASK, ~PPLL_DIV_SEL_MASK); return; } } /* Swich VCKL clock input to CPUCLK so it stays fed while PPLL updates*/ OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_CPUCLK, ~VCLK_SRC_SEL_MASK); /* Reset PPLL & enable atomic update */ OUTPLLP(PPLL_CNTL, PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN, ~(PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN)); /* Switch to selected PPLL divider */ OUTREGP(CLOCK_CNTL_INDEX, mode->clk_cntl_index & PPLL_DIV_SEL_MASK, ~PPLL_DIV_SEL_MASK); /* Set PPLL ref. div */ if (rinfo->family == CHIP_FAMILY_R300 || rinfo->family == CHIP_FAMILY_RS300 || rinfo->family == CHIP_FAMILY_R350 || rinfo->family == CHIP_FAMILY_RV350) { if (mode->ppll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) { /* When restoring console mode, use saved PPLL_REF_DIV * setting. */ OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, 0); } else { /* R300 uses ref_div_acc field as real ref divider */ OUTPLLP(PPLL_REF_DIV, (mode->ppll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), ~R300_PPLL_REF_DIV_ACC_MASK); } } else OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, ~PPLL_REF_DIV_MASK); /* Set PPLL divider 3 & post divider*/ OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_FB3_DIV_MASK); OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_POST3_DIV_MASK); /* Write update */ while (INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R) ; OUTPLLP(PPLL_REF_DIV, PPLL_ATOMIC_UPDATE_W, ~PPLL_ATOMIC_UPDATE_W); /* Wait read update complete */ /* FIXME: Certain revisions of R300 can't recover here. Not sure of the cause yet, but this workaround will mask the problem for now. Other chips usually will pass at the very first test, so the workaround shouldn't have any effect on them. */ for (i = 0; (i < 10000 && INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R); i++) ; OUTPLL(HTOTAL_CNTL, 0); /* Clear reset & atomic update */ OUTPLLP(PPLL_CNTL, 0, ~(PPLL_RESET | PPLL_SLEEP | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN)); /* We may want some locking ... oh well */ radeon_msleep(5); /* Switch back VCLK source to PPLL */ OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_PPLLCLK, ~VCLK_SRC_SEL_MASK);}/* * Timer function for delayed LVDS panel power up/down */static void radeon_lvds_timer_func(unsigned long data){ struct radeonfb_info *rinfo = (struct radeonfb_info *)data; radeon_engine_idle(); OUTREG(LVDS_GEN_CNTL, rinfo->pending_lvds_gen_cntl);}/* * Apply a video mode. This will apply the whole register set, including * the PLL registers, to the card */void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode, int regs_only){ int i; int primary_mon = PRIMARY_MONITOR(rinfo); if (nomodeset) return; if (!regs_only) radeon_screen_blank(rinfo, FB_BLANK_NORMAL, 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -