📄 aty128fb.c
字号:
* Functions to read from/write to the mmio registers * - endian conversions may possibly be avoided by * using the other register aperture. TODO. */static inline u32 _aty_ld_le32(volatile unsigned int regindex, const struct aty128fb_par *par){ return readl (par->regbase + regindex);}static inline void _aty_st_le32(volatile unsigned int regindex, u32 val, const struct aty128fb_par *par){ writel (val, par->regbase + regindex);}static inline u8 _aty_ld_8(unsigned int regindex, const struct aty128fb_par *par){ return readb (par->regbase + regindex);}static inline void _aty_st_8(unsigned int regindex, u8 val, const struct aty128fb_par *par){ writeb (val, par->regbase + regindex);}#define aty_ld_le32(regindex) _aty_ld_le32(regindex, par)#define aty_st_le32(regindex, val) _aty_st_le32(regindex, val, par)#define aty_ld_8(regindex) _aty_ld_8(regindex, par)#define aty_st_8(regindex, val) _aty_st_8(regindex, val, par) /* * Functions to read from/write to the pll registers */#define aty_ld_pll(pll_index) _aty_ld_pll(pll_index, par)#define aty_st_pll(pll_index, val) _aty_st_pll(pll_index, val, par)static u32 _aty_ld_pll(unsigned int pll_index, const struct aty128fb_par *par){ aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x3F); return aty_ld_le32(CLOCK_CNTL_DATA);} static void _aty_st_pll(unsigned int pll_index, u32 val, const struct aty128fb_par *par){ aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x3F) | PLL_WR_EN); aty_st_le32(CLOCK_CNTL_DATA, val);}/* return true when the PLL has completed an atomic update */static int aty_pll_readupdate(const struct aty128fb_par *par){ return !(aty_ld_pll(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R);}static void aty_pll_wait_readupdate(const struct aty128fb_par *par){ unsigned long timeout = jiffies + HZ/100; // should be more than enough int reset = 1; while (time_before(jiffies, timeout)) if (aty_pll_readupdate(par)) { reset = 0; break; } if (reset) /* reset engine?? */ printk(KERN_DEBUG "aty128fb: PLL write timeout!\n");}/* tell PLL to update */static void aty_pll_writeupdate(const struct aty128fb_par *par){ aty_pll_wait_readupdate(par); aty_st_pll(PPLL_REF_DIV, aty_ld_pll(PPLL_REF_DIV) | PPLL_ATOMIC_UPDATE_W);}/* write to the scratch register to test r/w functionality */static int __init register_test(const struct aty128fb_par *par){ u32 val; int flag = 0; val = aty_ld_le32(BIOS_0_SCRATCH); aty_st_le32(BIOS_0_SCRATCH, 0x55555555); if (aty_ld_le32(BIOS_0_SCRATCH) == 0x55555555) { aty_st_le32(BIOS_0_SCRATCH, 0xAAAAAAAA); if (aty_ld_le32(BIOS_0_SCRATCH) == 0xAAAAAAAA) flag = 1; } aty_st_le32(BIOS_0_SCRATCH, val); // restore value return flag;}/* * Accelerator engine functions */static void do_wait_for_fifo(u16 entries, struct aty128fb_par *par){ int i; for (;;) { for (i = 0; i < 2000000; i++) { par->fifo_slots = aty_ld_le32(GUI_STAT) & 0x0fff; if (par->fifo_slots >= entries) return; } aty128_reset_engine(par); }}static void wait_for_idle(struct aty128fb_par *par){ int i; do_wait_for_fifo(64, par); for (;;) { for (i = 0; i < 2000000; i++) { if (!(aty_ld_le32(GUI_STAT) & (1 << 31))) { aty128_flush_pixel_cache(par); par->blitter_may_be_busy = 0; return; } } aty128_reset_engine(par); }}static void wait_for_fifo(u16 entries, struct aty128fb_par *par){ if (par->fifo_slots < entries) do_wait_for_fifo(64, par); par->fifo_slots -= entries;}static void aty128_flush_pixel_cache(const struct aty128fb_par *par){ int i; u32 tmp; tmp = aty_ld_le32(PC_NGUI_CTLSTAT); tmp &= ~(0x00ff); tmp |= 0x00ff; aty_st_le32(PC_NGUI_CTLSTAT, tmp); for (i = 0; i < 2000000; i++) if (!(aty_ld_le32(PC_NGUI_CTLSTAT) & PC_BUSY)) break;}static void aty128_reset_engine(const struct aty128fb_par *par){ u32 gen_reset_cntl, clock_cntl_index, mclk_cntl; aty128_flush_pixel_cache(par); clock_cntl_index = aty_ld_le32(CLOCK_CNTL_INDEX); mclk_cntl = aty_ld_pll(MCLK_CNTL); aty_st_pll(MCLK_CNTL, mclk_cntl | 0x00030000); gen_reset_cntl = aty_ld_le32(GEN_RESET_CNTL); aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl | SOFT_RESET_GUI); aty_ld_le32(GEN_RESET_CNTL); aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl & ~(SOFT_RESET_GUI)); aty_ld_le32(GEN_RESET_CNTL); aty_st_pll(MCLK_CNTL, mclk_cntl); aty_st_le32(CLOCK_CNTL_INDEX, clock_cntl_index); aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl); /* use old pio mode */ aty_st_le32(PM4_BUFFER_CNTL, PM4_BUFFER_CNTL_NONPM4); DBG("engine reset");}static void aty128_init_engine(struct aty128fb_par *par){ u32 pitch_value; wait_for_idle(par); /* 3D scaler not spoken here */ wait_for_fifo(1, par); aty_st_le32(SCALE_3D_CNTL, 0x00000000); aty128_reset_engine(par); pitch_value = par->crtc.pitch; if (par->crtc.bpp == 24) { pitch_value = pitch_value * 3; } wait_for_fifo(4, par); /* setup engine offset registers */ aty_st_le32(DEFAULT_OFFSET, 0x00000000); /* setup engine pitch registers */ aty_st_le32(DEFAULT_PITCH, pitch_value); /* set the default scissor register to max dimensions */ aty_st_le32(DEFAULT_SC_BOTTOM_RIGHT, (0x1FFF << 16) | 0x1FFF); /* set the drawing controls registers */ aty_st_le32(DP_GUI_MASTER_CNTL, GMC_SRC_PITCH_OFFSET_DEFAULT | GMC_DST_PITCH_OFFSET_DEFAULT | GMC_SRC_CLIP_DEFAULT | GMC_DST_CLIP_DEFAULT | GMC_BRUSH_SOLIDCOLOR | (depth_to_dst(par->crtc.depth) << 8) | GMC_SRC_DSTCOLOR | GMC_BYTE_ORDER_MSB_TO_LSB | GMC_DP_CONVERSION_TEMP_6500 | ROP3_PATCOPY | GMC_DP_SRC_RECT | GMC_3D_FCN_EN_CLR | GMC_DST_CLR_CMP_FCN_CLEAR | GMC_AUX_CLIP_CLEAR | GMC_WRITE_MASK_SET); wait_for_fifo(8, par); /* clear the line drawing registers */ aty_st_le32(DST_BRES_ERR, 0); aty_st_le32(DST_BRES_INC, 0); aty_st_le32(DST_BRES_DEC, 0); /* set brush color registers */ aty_st_le32(DP_BRUSH_FRGD_CLR, 0xFFFFFFFF); /* white */ aty_st_le32(DP_BRUSH_BKGD_CLR, 0x00000000); /* black */ /* set source color registers */ aty_st_le32(DP_SRC_FRGD_CLR, 0xFFFFFFFF); /* white */ aty_st_le32(DP_SRC_BKGD_CLR, 0x00000000); /* black */ /* default write mask */ aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF); /* Wait for all the writes to be completed before returning */ wait_for_idle(par);}/* convert depth values to their register representation */static u32 depth_to_dst(u32 depth){ if (depth <= 8) return DST_8BPP; else if (depth <= 15) return DST_15BPP; else if (depth == 16) return DST_16BPP; else if (depth <= 24) return DST_24BPP; else if (depth <= 32) return DST_32BPP; return -EINVAL;}/* * PLL informations retreival */#ifndef __sparc__static void __iomem * __init aty128_map_ROM(const struct aty128fb_par *par, struct pci_dev *dev){ u16 dptr; u8 rom_type; void __iomem *bios; size_t rom_size; /* Fix from ATI for problem with Rage128 hardware not leaving ROM enabled */ unsigned int temp; temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG); temp &= 0x00ffffffu; temp |= 0x04 << 24; aty_st_le32(RAGE128_MPP_TB_CONFIG, temp); temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG); bios = pci_map_rom(dev, &rom_size); if (!bios) { printk(KERN_ERR "aty128fb: ROM failed to map\n"); return NULL; } /* Very simple test to make sure it appeared */ if (BIOS_IN16(0) != 0xaa55) { printk(KERN_DEBUG "aty128fb: Invalid ROM signature %x should " " be 0xaa55\n", BIOS_IN16(0)); goto failed; } /* Look for the PCI data to check the ROM type */ dptr = BIOS_IN16(0x18); /* Check the PCI data signature. If it's wrong, we still assume a normal x86 ROM * for now, until I've verified this works everywhere. The goal here is more * to phase out Open Firmware images. * * Currently, we only look at the first PCI data, we could iteratre and deal with * them all, and we should use fb_bios_start relative to start of image and not * relative start of ROM, but so far, I never found a dual-image ATI card * * typedef struct { * u32 signature; + 0x00 * u16 vendor; + 0x04 * u16 device; + 0x06 * u16 reserved_1; + 0x08 * u16 dlen; + 0x0a * u8 drevision; + 0x0c * u8 class_hi; + 0x0d * u16 class_lo; + 0x0e * u16 ilen; + 0x10 * u16 irevision; + 0x12 * u8 type; + 0x14 * u8 indicator; + 0x15 * u16 reserved_2; + 0x16 * } pci_data_t; */ if (BIOS_IN32(dptr) != (('R' << 24) | ('I' << 16) | ('C' << 8) | 'P')) { printk(KERN_WARNING "aty128fb: PCI DATA signature in ROM incorrect: %08x\n", BIOS_IN32(dptr)); goto anyway; } rom_type = BIOS_IN8(dptr + 0x14); switch(rom_type) { case 0: printk(KERN_INFO "aty128fb: Found Intel x86 BIOS ROM Image\n"); break; case 1: printk(KERN_INFO "aty128fb: Found Open Firmware ROM Image\n"); goto failed; case 2: printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n"); goto failed; default: printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n", rom_type); goto failed; } anyway: return bios; failed: pci_unmap_rom(dev, bios); return NULL;}static void __init aty128_get_pllinfo(struct aty128fb_par *par, unsigned char __iomem *bios){ unsigned int bios_hdr; unsigned int bios_pll; bios_hdr = BIOS_IN16(0x48); bios_pll = BIOS_IN16(bios_hdr + 0x30); par->constants.ppll_max = BIOS_IN32(bios_pll + 0x16); par->constants.ppll_min = BIOS_IN32(bios_pll + 0x12); par->constants.xclk = BIOS_IN16(bios_pll + 0x08); par->constants.ref_divider = BIOS_IN16(bios_pll + 0x10); par->constants.ref_clk = BIOS_IN16(bios_pll + 0x0e); DBG("ppll_max %d ppll_min %d xclk %d ref_divider %d ref clock %d\n", par->constants.ppll_max, par->constants.ppll_min, par->constants.xclk, par->constants.ref_divider, par->constants.ref_clk);} #ifdef CONFIG_X86static void __iomem * __devinit aty128_find_mem_vbios(struct aty128fb_par *par){ /* I simplified this code as we used to miss the signatures in * a lot of case. It's now closer to XFree, we just don't check * for signatures at all... Something better will have to be done * if we end up having conflicts */ u32 segstart; unsigned char __iomem *rom_base = NULL; for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) { rom_base = ioremap(segstart, 0x10000); if (rom_base == NULL) return NULL; if (readb(rom_base) == 0x55 && readb(rom_base + 1) == 0xaa) break; iounmap(rom_base); rom_base = NULL; } return rom_base;}#endif#endif /* ndef(__sparc__) *//* fill in known card constants if pll_block is not available */static void __init aty128_timings(struct aty128fb_par *par){#ifdef CONFIG_PPC_OF /* instead of a table lookup, assume OF has properly * setup the PLL registers and use their values * to set the XCLK values and reference divider values */ u32 x_mpll_ref_fb_div; u32 xclk_cntl; u32 Nx, M; unsigned PostDivSet[] = { 0, 1, 2, 4, 8, 3, 6, 12 };#endif if (!par->constants.ref_clk) par->constants.ref_clk = 2950;#ifdef CONFIG_PPC_OF x_mpll_ref_fb_div = aty_ld_pll(X_MPLL_REF_FB_DIV); xclk_cntl = aty_ld_pll(XCLK_CNTL) & 0x7; Nx = (x_mpll_ref_fb_div & 0x00ff00) >> 8; M = x_mpll_ref_fb_div & 0x0000ff; par->constants.xclk = round_div((2 * Nx * par->constants.ref_clk), (M * PostDivSet[xclk_cntl])); par->constants.ref_divider = aty_ld_pll(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;#endif if (!par->constants.ref_divider) { par->constants.ref_divider = 0x3b; aty_st_pll(X_MPLL_REF_FB_DIV, 0x004c4c1e); aty_pll_writeupdate(par); } aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider); aty_pll_writeupdate(par); /* from documentation */ if (!par->constants.ppll_min) par->constants.ppll_min = 12500; if (!par->constants.ppll_max) par->constants.ppll_max = 25000; /* 23000 on some cards? */ if (!par->constants.xclk) par->constants.xclk = 0x1d4d; /* same as mclk */ par->constants.fifo_width = 128; par->constants.fifo_depth = 32; switch (aty_ld_le32(MEM_CNTL) & 0x3) { case 0: par->mem = &sdr_128; break; case 1: par->mem = &sdr_sgram; break; case 2: par->mem = &ddr_sgram; break; default: par->mem = &sdr_sgram; }}/* * CRTC programming *//* Program the CRTC registers */static void aty128_set_crtc(const struct aty128_crtc *crtc,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -