📄 gxt4500.c
字号:
/* * Frame buffer device for IBM GXT4500P and GXT6000P display adaptors * * Copyright (C) 2006 Paul Mackerras, IBM Corp. <paulus@samba.org> */#include <linux/kernel.h>#include <linux/module.h>#include <linux/fb.h>#include <linux/console.h>#include <linux/pci.h>#include <linux/pci_ids.h>#include <linux/delay.h>#include <linux/string.h>#define PCI_DEVICE_ID_IBM_GXT4500P 0x21c#define PCI_DEVICE_ID_IBM_GXT6000P 0x170/* GXT4500P registers *//* Registers in PCI config space */#define CFG_ENDIAN0 0x40/* Misc control/status registers */#define STATUS 0x1000#define CTRL_REG0 0x1004#define CR0_HALT_DMA 0x4#define CR0_RASTER_RESET 0x8#define CR0_GEOM_RESET 0x10#define CR0_MEM_CTRLER_RESET 0x20/* Framebuffer control registers */#define FB_AB_CTRL 0x1100#define FB_CD_CTRL 0x1104#define FB_WID_CTRL 0x1108#define FB_Z_CTRL 0x110c#define FB_VGA_CTRL 0x1110#define REFRESH_AB_CTRL 0x1114#define REFRESH_CD_CTRL 0x1118#define FB_OVL_CTRL 0x111c#define FB_CTRL_TYPE 0x80000000#define FB_CTRL_WIDTH_MASK 0x007f0000#define FB_CTRL_WIDTH_SHIFT 16#define FB_CTRL_START_SEG_MASK 0x00003fff#define REFRESH_START 0x1098#define REFRESH_SIZE 0x109c/* "Direct" framebuffer access registers */#define DFA_FB_A 0x11e0#define DFA_FB_B 0x11e4#define DFA_FB_C 0x11e8#define DFA_FB_D 0x11ec#define DFA_FB_ENABLE 0x80000000#define DFA_FB_BASE_MASK 0x03f00000#define DFA_FB_STRIDE_1k 0x00000000#define DFA_FB_STRIDE_2k 0x00000010#define DFA_FB_STRIDE_4k 0x00000020#define DFA_PIX_8BIT 0x00000000#define DFA_PIX_16BIT_565 0x00000001#define DFA_PIX_16BIT_1555 0x00000002#define DFA_PIX_24BIT 0x00000004#define DFA_PIX_32BIT 0x00000005/* maps DFA_PIX_* to pixel size in bytes */static const unsigned char pixsize[] = { 1, 2, 2, 2, 4, 4};/* Display timing generator registers */#define DTG_CONTROL 0x1900#define DTG_CTL_SCREEN_REFRESH 2#define DTG_CTL_ENABLE 1#define DTG_HORIZ_EXTENT 0x1904#define DTG_HORIZ_DISPLAY 0x1908#define DTG_HSYNC_START 0x190c#define DTG_HSYNC_END 0x1910#define DTG_HSYNC_END_COMP 0x1914#define DTG_VERT_EXTENT 0x1918#define DTG_VERT_DISPLAY 0x191c#define DTG_VSYNC_START 0x1920#define DTG_VSYNC_END 0x1924#define DTG_VERT_SHORT 0x1928/* PLL/RAMDAC registers */#define DISP_CTL 0x402c#define DISP_CTL_OFF 2#define SYNC_CTL 0x4034#define SYNC_CTL_SYNC_ON_RGB 1#define SYNC_CTL_SYNC_OFF 2#define SYNC_CTL_HSYNC_INV 8#define SYNC_CTL_VSYNC_INV 0x10#define SYNC_CTL_HSYNC_OFF 0x20#define SYNC_CTL_VSYNC_OFF 0x40#define PLL_M 0x4040#define PLL_N 0x4044#define PLL_POSTDIV 0x4048#define PLL_C 0x404c/* Hardware cursor */#define CURSOR_X 0x4078#define CURSOR_Y 0x407c#define CURSOR_HOTSPOT 0x4080#define CURSOR_MODE 0x4084#define CURSOR_MODE_OFF 0#define CURSOR_MODE_4BPP 1#define CURSOR_PIXMAP 0x5000#define CURSOR_CMAP 0x7400/* Window attribute table */#define WAT_FMT 0x4100#define WAT_FMT_24BIT 0#define WAT_FMT_16BIT_565 1#define WAT_FMT_16BIT_1555 2#define WAT_FMT_32BIT 3 /* 0 vs. 3 is a guess */#define WAT_FMT_8BIT_332 9#define WAT_FMT_8BIT 0xa#define WAT_FMT_NO_CMAP 4 /* ORd in to other values */#define WAT_CMAP_OFFSET 0x4104 /* 4-bit value gets << 6 */#define WAT_CTRL 0x4108#define WAT_CTRL_SEL_B 1 /* select B buffer if 1 */#define WAT_CTRL_NO_INC 2#define WAT_GAMMA_CTRL 0x410c#define WAT_GAMMA_DISABLE 1 /* disables gamma cmap */#define WAT_OVL_CTRL 0x430c /* controls overlay *//* Indexed by DFA_PIX_* values */static const unsigned char watfmt[] = { WAT_FMT_8BIT, WAT_FMT_16BIT_565, WAT_FMT_16BIT_1555, 0, WAT_FMT_24BIT, WAT_FMT_32BIT};/* Colormap array; 1k entries of 4 bytes each */#define CMAP 0x6000#define readreg(par, reg) readl((par)->regs + (reg))#define writereg(par, reg, val) writel((val), (par)->regs + (reg))struct gxt4500_par { void __iomem *regs; int pixfmt; /* pixel format, see DFA_PIX_* values */ /* PLL parameters */ int refclk_ps; /* ref clock period in picoseconds */ int pll_m; /* ref clock divisor */ int pll_n; /* VCO divisor */ int pll_pd1; /* first post-divisor */ int pll_pd2; /* second post-divisor */ u32 pseudo_palette[16]; /* used in color blits */};/* mode requested by user */static char *mode_option;/* default mode: 1280x1024 @ 60 Hz, 8 bpp */static const struct fb_videomode defaultmode __devinitdata = { .refresh = 60, .xres = 1280, .yres = 1024, .pixclock = 9295, .left_margin = 248, .right_margin = 48, .upper_margin = 38, .lower_margin = 1, .hsync_len = 112, .vsync_len = 3, .vmode = FB_VMODE_NONINTERLACED};/* List of supported cards */enum gxt_cards { GXT4500P, GXT6000P};/* Card-specific information */static const struct cardinfo { int refclk_ps; /* period of PLL reference clock in ps */ const char *cardname;} cardinfo[] = { [GXT4500P] = { .refclk_ps = 9259, .cardname = "IBM GXT4500P" }, [GXT6000P] = { .refclk_ps = 40000, .cardname = "IBM GXT6000P" },};/* * The refclk and VCO dividers appear to use a linear feedback shift * register, which gets reloaded when it reaches a terminal value, at * which point the divider output is toggled. Thus one can obtain * whatever divisor is required by putting the appropriate value into * the reload register. For a divisor of N, one puts the value from * the LFSR sequence that comes N-1 places before the terminal value * into the reload register. */static const unsigned char mdivtab[] = {/* 1 */ 0x3f, 0x00, 0x20, 0x10, 0x28, 0x14, 0x2a, 0x15, 0x0a,/* 10 */ 0x25, 0x32, 0x19, 0x0c, 0x26, 0x13, 0x09, 0x04, 0x22, 0x11,/* 20 */ 0x08, 0x24, 0x12, 0x29, 0x34, 0x1a, 0x2d, 0x36, 0x1b, 0x0d,/* 30 */ 0x06, 0x23, 0x31, 0x38, 0x1c, 0x2e, 0x17, 0x0b, 0x05, 0x02,/* 40 */ 0x21, 0x30, 0x18, 0x2c, 0x16, 0x2b, 0x35, 0x3a, 0x1d, 0x0e,/* 50 */ 0x27, 0x33, 0x39, 0x3c, 0x1e, 0x2f, 0x37, 0x3b, 0x3d, 0x3e,/* 60 */ 0x1f, 0x0f, 0x07, 0x03, 0x01,};static const unsigned char ndivtab[] = {/* 2 */ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0x78, 0xbc, 0x5e,/* 10 */ 0x2f, 0x17, 0x0b, 0x85, 0xc2, 0xe1, 0x70, 0x38, 0x9c, 0x4e,/* 20 */ 0xa7, 0xd3, 0xe9, 0xf4, 0xfa, 0xfd, 0xfe, 0x7f, 0xbf, 0xdf,/* 30 */ 0xef, 0x77, 0x3b, 0x1d, 0x8e, 0xc7, 0xe3, 0x71, 0xb8, 0xdc,/* 40 */ 0x6e, 0xb7, 0x5b, 0x2d, 0x16, 0x8b, 0xc5, 0xe2, 0xf1, 0xf8,/* 50 */ 0xfc, 0x7e, 0x3f, 0x9f, 0xcf, 0x67, 0xb3, 0xd9, 0x6c, 0xb6,/* 60 */ 0xdb, 0x6d, 0x36, 0x9b, 0x4d, 0x26, 0x13, 0x89, 0xc4, 0x62,/* 70 */ 0xb1, 0xd8, 0xec, 0xf6, 0xfb, 0x7d, 0xbe, 0x5f, 0xaf, 0x57,/* 80 */ 0x2b, 0x95, 0x4a, 0x25, 0x92, 0x49, 0xa4, 0x52, 0x29, 0x94,/* 90 */ 0xca, 0x65, 0xb2, 0x59, 0x2c, 0x96, 0xcb, 0xe5, 0xf2, 0x79,/* 100 */ 0x3c, 0x1e, 0x0f, 0x07, 0x83, 0x41, 0x20, 0x90, 0x48, 0x24,/* 110 */ 0x12, 0x09, 0x84, 0x42, 0xa1, 0x50, 0x28, 0x14, 0x8a, 0x45,/* 120 */ 0xa2, 0xd1, 0xe8, 0x74, 0xba, 0xdd, 0xee, 0xf7, 0x7b, 0x3d,/* 130 */ 0x9e, 0x4f, 0x27, 0x93, 0xc9, 0xe4, 0x72, 0x39, 0x1c, 0x0e,/* 140 */ 0x87, 0xc3, 0x61, 0x30, 0x18, 0x8c, 0xc6, 0x63, 0x31, 0x98,/* 150 */ 0xcc, 0xe6, 0x73, 0xb9, 0x5c, 0x2e, 0x97, 0x4b, 0xa5, 0xd2,/* 160 */ 0x69,};static int calc_pll(int period_ps, struct gxt4500_par *par){ int m, n, pdiv1, pdiv2, postdiv; int pll_period, best_error, t, intf; /* only deal with range 5MHz - 300MHz */ if (period_ps < 3333 || period_ps > 200000) return -1; best_error = 1000000; for (pdiv1 = 1; pdiv1 <= 8; ++pdiv1) { for (pdiv2 = 1; pdiv2 <= pdiv1; ++pdiv2) { postdiv = pdiv1 * pdiv2; pll_period = DIV_ROUND_UP(period_ps, postdiv); /* keep pll in range 350..600 MHz */ if (pll_period < 1666 || pll_period > 2857) continue; for (m = 1; m <= 64; ++m) { intf = m * par->refclk_ps; if (intf > 500000) break; n = intf * postdiv / period_ps; if (n < 3 || n > 160) continue; t = par->refclk_ps * m * postdiv / n; t -= period_ps; if (t >= 0 && t < best_error) { par->pll_m = m; par->pll_n = n; par->pll_pd1 = pdiv1; par->pll_pd2 = pdiv2; best_error = t; } } } } if (best_error == 1000000) return -1; return 0;}static int calc_pixclock(struct gxt4500_par *par){ return par->refclk_ps * par->pll_m * par->pll_pd1 * par->pll_pd2 / par->pll_n;}static int gxt4500_var_to_par(struct fb_var_screeninfo *var, struct gxt4500_par *par){ if (var->xres + var->xoffset > var->xres_virtual || var->yres + var->yoffset > var->yres_virtual || var->xres_virtual > 4096) return -EINVAL; if ((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) return -EINVAL; if (calc_pll(var->pixclock, par) < 0) return -EINVAL; switch (var->bits_per_pixel) { case 32: if (var->transp.length) par->pixfmt = DFA_PIX_32BIT; else par->pixfmt = DFA_PIX_24BIT; break; case 24: par->pixfmt = DFA_PIX_24BIT; break; case 16: if (var->green.length == 5) par->pixfmt = DFA_PIX_16BIT_1555; else par->pixfmt = DFA_PIX_16BIT_565; break; case 8: par->pixfmt = DFA_PIX_8BIT; break; default: return -EINVAL; } return 0;}static const struct fb_bitfield eightbits = {0, 8};static const struct fb_bitfield nobits = {0, 0};static void gxt4500_unpack_pixfmt(struct fb_var_screeninfo *var, int pixfmt){ var->bits_per_pixel = pixsize[pixfmt] * 8; var->red = eightbits; var->green = eightbits; var->blue = eightbits; var->transp = nobits; switch (pixfmt) { case DFA_PIX_16BIT_565: var->red.length = 5; var->green.length = 6; var->blue.length = 5; break; case DFA_PIX_16BIT_1555: var->red.length = 5; var->green.length = 5; var->blue.length = 5; var->transp.length = 1; break; case DFA_PIX_32BIT: var->transp.length = 8; break; } if (pixfmt != DFA_PIX_8BIT) { var->green.offset = var->red.length; var->blue.offset = var->green.offset + var->green.length; if (var->transp.length) var->transp.offset = var->blue.offset + var->blue.length; }}static int gxt4500_check_var(struct fb_var_screeninfo *var, struct fb_info *info){ struct gxt4500_par par; int err; par = *(struct gxt4500_par *)info->par; err = gxt4500_var_to_par(var, &par); if (!err) { var->pixclock = calc_pixclock(&par); gxt4500_unpack_pixfmt(var, par.pixfmt); } return err;}static int gxt4500_set_par(struct fb_info *info){ struct gxt4500_par *par = info->par; struct fb_var_screeninfo *var = &info->var; int err; u32 ctrlreg, tmp; unsigned int dfa_ctl, pixfmt, stride; unsigned int wid_tiles, i; unsigned int prefetch_pix, htot; struct gxt4500_par save_par; save_par = *par; err = gxt4500_var_to_par(var, par); if (err) { *par = save_par; return err; } /* turn off DTG for now */ ctrlreg = readreg(par, DTG_CONTROL); ctrlreg &= ~(DTG_CTL_ENABLE | DTG_CTL_SCREEN_REFRESH); writereg(par, DTG_CONTROL, ctrlreg);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -