📄 pm2fb.c
字号:
/* * Permedia2 framebuffer driver. * * 2.5/2.6 driver: * Copyright (c) 2003 Jim Hague (jim.hague@acm.org) * * based on 2.4 driver: * Copyright (c) 1998-2000 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) * Copyright (c) 1999 Jakub Jelinek (jakub@redhat.com) * * and additional input from James Simmon's port of Hannu Mallat's tdfx * driver. * * I have a Creative Graphics Blaster Exxtreme card - pm2fb on x86. I * have no access to other pm2fb implementations. Sparc (and thus * hopefully other big-endian) devices now work, thanks to a lot of * testing work by Ron Murray. I have no access to CVision hardware, * and therefore for now I am omitting the CVision code. * * Multiple boards support has been on the TODO list for ages. * Don't expect this to change. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. * * */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/init.h>#include <linux/pci.h>#ifdef CONFIG_MTRR#include <asm/mtrr.h>#endif#include <video/permedia2.h>#include <video/cvisionppc.h>#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)#error "The endianness of the target host has not been defined."#endif#if !defined(CONFIG_PCI)#error "Only generic PCI cards supported."#endif#undef PM2FB_MASTER_DEBUG#ifdef PM2FB_MASTER_DEBUG#define DPRINTK(a, b...) \ printk(KERN_DEBUG "pm2fb: %s: " a, __func__ , ## b)#else#define DPRINTK(a, b...)#endif#define PM2_PIXMAP_SIZE (1600 * 4)/* * Driver data */static int hwcursor = 1;static char *mode_option __devinitdata;/* * The XFree GLINT driver will (I think to implement hardware cursor * support on TVP4010 and similar where there is no RAMDAC - see * comment in set_video) always request +ve sync regardless of what * the mode requires. This screws me because I have a Sun * fixed-frequency monitor which absolutely has to have -ve sync. So * these flags allow the user to specify that requests for +ve sync * should be silently turned in -ve sync. */static int lowhsync;static int lowvsync;static int noaccel __devinitdata;/* mtrr option */#ifdef CONFIG_MTRRstatic int nomtrr __devinitdata;#endif/* * The hardware state of the graphics card that isn't part of the * screeninfo. */struct pm2fb_par{ pm2type_t type; /* Board type */ unsigned char __iomem *v_regs;/* virtual address of p_regs */ u32 memclock; /* memclock */ u32 video; /* video flags before blanking */ u32 mem_config; /* MemConfig reg at probe */ u32 mem_control; /* MemControl reg at probe */ u32 boot_address; /* BootAddress reg at probe */ u32 palette[16]; int mtrr_handle;};/* * Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo * if we don't use modedb. */static struct fb_fix_screeninfo pm2fb_fix __devinitdata = { .id = "", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, .xpanstep = 1, .ypanstep = 1, .ywrapstep = 0, .accel = FB_ACCEL_3DLABS_PERMEDIA2,};/* * Default video mode. In case the modedb doesn't work. */static struct fb_var_screeninfo pm2fb_var __devinitdata = { /* "640x480, 8 bpp @ 60 Hz */ .xres = 640, .yres = 480, .xres_virtual = 640, .yres_virtual = 480, .bits_per_pixel = 8, .red = {0, 8, 0}, .blue = {0, 8, 0}, .green = {0, 8, 0}, .activate = FB_ACTIVATE_NOW, .height = -1, .width = -1, .accel_flags = 0, .pixclock = 39721, .left_margin = 40, .right_margin = 24, .upper_margin = 32, .lower_margin = 11, .hsync_len = 96, .vsync_len = 2, .vmode = FB_VMODE_NONINTERLACED};/* * Utility functions */static inline u32 pm2_RD(struct pm2fb_par *p, s32 off){ return fb_readl(p->v_regs + off);}static inline void pm2_WR(struct pm2fb_par *p, s32 off, u32 v){ fb_writel(v, p->v_regs + off);}static inline u32 pm2_RDAC_RD(struct pm2fb_par *p, s32 idx){ pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx); mb(); return pm2_RD(p, PM2R_RD_INDEXED_DATA);}static inline u32 pm2v_RDAC_RD(struct pm2fb_par *p, s32 idx){ pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); mb(); return pm2_RD(p, PM2VR_RD_INDEXED_DATA);}static inline void pm2_RDAC_WR(struct pm2fb_par *p, s32 idx, u32 v){ pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx); wmb(); pm2_WR(p, PM2R_RD_INDEXED_DATA, v); wmb();}static inline void pm2v_RDAC_WR(struct pm2fb_par *p, s32 idx, u32 v){ pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); wmb(); pm2_WR(p, PM2VR_RD_INDEXED_DATA, v); wmb();}#ifdef CONFIG_FB_PM2_FIFO_DISCONNECT#define WAIT_FIFO(p, a)#elsestatic inline void WAIT_FIFO(struct pm2fb_par *p, u32 a){ while (pm2_RD(p, PM2R_IN_FIFO_SPACE) < a) cpu_relax();}#endif/* * partial products for the supported horizontal resolutions. */#define PACKPP(p0, p1, p2) (((p2) << 6) | ((p1) << 3) | (p0))static const struct { u16 width; u16 pp;} pp_table[] = { { 32, PACKPP(1, 0, 0) }, { 64, PACKPP(1, 1, 0) }, { 96, PACKPP(1, 1, 1) }, { 128, PACKPP(2, 1, 1) }, { 160, PACKPP(2, 2, 1) }, { 192, PACKPP(2, 2, 2) }, { 224, PACKPP(3, 2, 1) }, { 256, PACKPP(3, 2, 2) }, { 288, PACKPP(3, 3, 1) }, { 320, PACKPP(3, 3, 2) }, { 384, PACKPP(3, 3, 3) }, { 416, PACKPP(4, 3, 1) }, { 448, PACKPP(4, 3, 2) }, { 512, PACKPP(4, 3, 3) }, { 544, PACKPP(4, 4, 1) }, { 576, PACKPP(4, 4, 2) }, { 640, PACKPP(4, 4, 3) }, { 768, PACKPP(4, 4, 4) }, { 800, PACKPP(5, 4, 1) }, { 832, PACKPP(5, 4, 2) }, { 896, PACKPP(5, 4, 3) }, { 1024, PACKPP(5, 4, 4) }, { 1056, PACKPP(5, 5, 1) }, { 1088, PACKPP(5, 5, 2) }, { 1152, PACKPP(5, 5, 3) }, { 1280, PACKPP(5, 5, 4) }, { 1536, PACKPP(5, 5, 5) }, { 1568, PACKPP(6, 5, 1) }, { 1600, PACKPP(6, 5, 2) }, { 1664, PACKPP(6, 5, 3) }, { 1792, PACKPP(6, 5, 4) }, { 2048, PACKPP(6, 5, 5) }, { 0, 0 } };static u32 partprod(u32 xres){ int i; for (i = 0; pp_table[i].width && pp_table[i].width != xres; i++) ; if (pp_table[i].width == 0) DPRINTK("invalid width %u\n", xres); return pp_table[i].pp;}static u32 to3264(u32 timing, int bpp, int is64){ switch (bpp) { case 24: timing *= 3; case 8: timing >>= 1; case 16: timing >>= 1; case 32: break; } if (is64) timing >>= 1; return timing;}static void pm2_mnp(u32 clk, unsigned char *mm, unsigned char *nn, unsigned char *pp){ unsigned char m; unsigned char n; unsigned char p; u32 f; s32 curr; s32 delta = 100000; *mm = *nn = *pp = 0; for (n = 2; n < 15; n++) { for (m = 2; m; m++) { f = PM2_REFERENCE_CLOCK * m / n; if (f >= 150000 && f <= 300000) { for (p = 0; p < 5; p++, f >>= 1) { curr = (clk > f) ? clk - f : f - clk; if (curr < delta) { delta = curr; *mm = m; *nn = n; *pp = p; } } } } }}static void pm2v_mnp(u32 clk, unsigned char *mm, unsigned char *nn, unsigned char *pp){ unsigned char m; unsigned char n; unsigned char p; u32 f; s32 delta = 1000; *mm = *nn = *pp = 0; for (m = 1; m < 128; m++) { for (n = 2 * m + 1; n; n++) { for (p = 0; p < 2; p++) { f = (PM2_REFERENCE_CLOCK >> (p + 1)) * n / m; if (clk > f - delta && clk < f + delta) { delta = (clk > f) ? clk - f : f - clk; *mm = m; *nn = n; *pp = p; } } } }}static void clear_palette(struct pm2fb_par *p){ int i = 256; WAIT_FIFO(p, 1); pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, 0); wmb(); while (i--) { WAIT_FIFO(p, 3); pm2_WR(p, PM2R_RD_PALETTE_DATA, 0); pm2_WR(p, PM2R_RD_PALETTE_DATA, 0); pm2_WR(p, PM2R_RD_PALETTE_DATA, 0); }}static void reset_card(struct pm2fb_par *p){ if (p->type == PM2_TYPE_PERMEDIA2V) pm2_WR(p, PM2VR_RD_INDEX_HIGH, 0); pm2_WR(p, PM2R_RESET_STATUS, 0); mb(); while (pm2_RD(p, PM2R_RESET_STATUS) & PM2F_BEING_RESET) cpu_relax(); mb();#ifdef CONFIG_FB_PM2_FIFO_DISCONNECT DPRINTK("FIFO disconnect enabled\n"); pm2_WR(p, PM2R_FIFO_DISCON, 1); mb();#endif /* Restore stashed memory config information from probe */ WAIT_FIFO(p, 3); pm2_WR(p, PM2R_MEM_CONTROL, p->mem_control); pm2_WR(p, PM2R_BOOT_ADDRESS, p->boot_address); wmb(); pm2_WR(p, PM2R_MEM_CONFIG, p->mem_config);}static void reset_config(struct pm2fb_par *p){ WAIT_FIFO(p, 53); pm2_WR(p, PM2R_CHIP_CONFIG, pm2_RD(p, PM2R_CHIP_CONFIG) & ~(PM2F_VGA_ENABLE | PM2F_VGA_FIXED)); pm2_WR(p, PM2R_BYPASS_WRITE_MASK, ~(0L)); pm2_WR(p, PM2R_FRAMEBUFFER_WRITE_MASK, ~(0L)); pm2_WR(p, PM2R_FIFO_CONTROL, 0); pm2_WR(p, PM2R_APERTURE_ONE, 0); pm2_WR(p, PM2R_APERTURE_TWO, 0); pm2_WR(p, PM2R_RASTERIZER_MODE, 0); pm2_WR(p, PM2R_DELTA_MODE, PM2F_DELTA_ORDER_RGB); pm2_WR(p, PM2R_LB_READ_FORMAT, 0); pm2_WR(p, PM2R_LB_WRITE_FORMAT, 0); pm2_WR(p, PM2R_LB_READ_MODE, 0); pm2_WR(p, PM2R_LB_SOURCE_OFFSET, 0); pm2_WR(p, PM2R_FB_SOURCE_OFFSET, 0); pm2_WR(p, PM2R_FB_PIXEL_OFFSET, 0); pm2_WR(p, PM2R_FB_WINDOW_BASE, 0); pm2_WR(p, PM2R_LB_WINDOW_BASE, 0); pm2_WR(p, PM2R_FB_SOFT_WRITE_MASK, ~(0L)); pm2_WR(p, PM2R_FB_HARD_WRITE_MASK, ~(0L)); pm2_WR(p, PM2R_FB_READ_PIXEL, 0); pm2_WR(p, PM2R_DITHER_MODE, 0); pm2_WR(p, PM2R_AREA_STIPPLE_MODE, 0); pm2_WR(p, PM2R_DEPTH_MODE, 0); pm2_WR(p, PM2R_STENCIL_MODE, 0); pm2_WR(p, PM2R_TEXTURE_ADDRESS_MODE, 0); pm2_WR(p, PM2R_TEXTURE_READ_MODE, 0); pm2_WR(p, PM2R_TEXEL_LUT_MODE, 0); pm2_WR(p, PM2R_YUV_MODE, 0); pm2_WR(p, PM2R_COLOR_DDA_MODE, 0); pm2_WR(p, PM2R_TEXTURE_COLOR_MODE, 0); pm2_WR(p, PM2R_FOG_MODE, 0); pm2_WR(p, PM2R_ALPHA_BLEND_MODE, 0); pm2_WR(p, PM2R_LOGICAL_OP_MODE, 0); pm2_WR(p, PM2R_STATISTICS_MODE, 0); pm2_WR(p, PM2R_SCISSOR_MODE, 0); pm2_WR(p, PM2R_FILTER_MODE, PM2F_SYNCHRONIZATION); pm2_WR(p, PM2R_RD_PIXEL_MASK, 0xff); switch (p->type) { case PM2_TYPE_PERMEDIA2: pm2_RDAC_WR(p, PM2I_RD_MODE_CONTROL, 0); /* no overlay */ pm2_RDAC_WR(p, PM2I_RD_CURSOR_CONTROL, 0); pm2_RDAC_WR(p, PM2I_RD_MISC_CONTROL, PM2F_RD_PALETTE_WIDTH_8); pm2_RDAC_WR(p, PM2I_RD_COLOR_KEY_CONTROL, 0); pm2_RDAC_WR(p, PM2I_RD_OVERLAY_KEY, 0); pm2_RDAC_WR(p, PM2I_RD_RED_KEY, 0); pm2_RDAC_WR(p, PM2I_RD_GREEN_KEY, 0); pm2_RDAC_WR(p, PM2I_RD_BLUE_KEY, 0); break; case PM2_TYPE_PERMEDIA2V: pm2v_RDAC_WR(p, PM2VI_RD_MISC_CONTROL, 1); /* 8bit */ break; }}static void set_aperture(struct pm2fb_par *p, u32 depth){ /* * The hardware is little-endian. When used in big-endian * hosts, the on-chip aperture settings are used where * possible to translate from host to card byte order. */ WAIT_FIFO(p, 2);#ifdef __LITTLE_ENDIAN pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_STANDARD);#else switch (depth) { case 24: /* RGB->BGR */ /* * We can't use the aperture to translate host to * card byte order here, so we switch to BGR mode * in pm2fb_set_par(). */ case 8: /* B->B */ pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_STANDARD); break; case 16: /* HL->LH */ pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_HALFWORDSWAP); break; case 32: /* RGBA->ABGR */ pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_BYTESWAP); break; }#endif /* We don't use aperture two, so this may be superflous */ pm2_WR(p, PM2R_APERTURE_TWO, PM2F_APERTURE_STANDARD);}static void set_color(struct pm2fb_par *p, unsigned char regno, unsigned char r, unsigned char g, unsigned char b){ WAIT_FIFO(p, 4); pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, regno); wmb(); pm2_WR(p, PM2R_RD_PALETTE_DATA, r); wmb(); pm2_WR(p, PM2R_RD_PALETTE_DATA, g); wmb(); pm2_WR(p, PM2R_RD_PALETTE_DATA, b);}static void set_memclock(struct pm2fb_par *par, u32 clk){ int i; unsigned char m, n, p; switch (par->type) { case PM2_TYPE_PERMEDIA2V: pm2v_mnp(clk/2, &m, &n, &p); WAIT_FIFO(par, 12); pm2_WR(par, PM2VR_RD_INDEX_HIGH, PM2VI_RD_MCLK_CONTROL >> 8); pm2v_RDAC_WR(par, PM2VI_RD_MCLK_CONTROL, 0); pm2v_RDAC_WR(par, PM2VI_RD_MCLK_PRESCALE, m); pm2v_RDAC_WR(par, PM2VI_RD_MCLK_FEEDBACK, n); pm2v_RDAC_WR(par, PM2VI_RD_MCLK_POSTSCALE, p); pm2v_RDAC_WR(par, PM2VI_RD_MCLK_CONTROL, 1); rmb();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -