162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Frame buffer device for IBM GXT4500P/6500P and GXT4000P/6000P 462306a36Sopenharmony_ci * display adaptors 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2006 Paul Mackerras, IBM Corp. <paulus@samba.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/aperture.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/fb.h> 1362306a36Sopenharmony_ci#include <linux/console.h> 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci#include <linux/pci_ids.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/string.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define PCI_DEVICE_ID_IBM_GXT4500P 0x21c 2062306a36Sopenharmony_ci#define PCI_DEVICE_ID_IBM_GXT6500P 0x21b 2162306a36Sopenharmony_ci#define PCI_DEVICE_ID_IBM_GXT4000P 0x16e 2262306a36Sopenharmony_ci#define PCI_DEVICE_ID_IBM_GXT6000P 0x170 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* GXT4500P registers */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Registers in PCI config space */ 2762306a36Sopenharmony_ci#define CFG_ENDIAN0 0x40 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Misc control/status registers */ 3062306a36Sopenharmony_ci#define STATUS 0x1000 3162306a36Sopenharmony_ci#define CTRL_REG0 0x1004 3262306a36Sopenharmony_ci#define CR0_HALT_DMA 0x4 3362306a36Sopenharmony_ci#define CR0_RASTER_RESET 0x8 3462306a36Sopenharmony_ci#define CR0_GEOM_RESET 0x10 3562306a36Sopenharmony_ci#define CR0_MEM_CTRLER_RESET 0x20 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Framebuffer control registers */ 3862306a36Sopenharmony_ci#define FB_AB_CTRL 0x1100 3962306a36Sopenharmony_ci#define FB_CD_CTRL 0x1104 4062306a36Sopenharmony_ci#define FB_WID_CTRL 0x1108 4162306a36Sopenharmony_ci#define FB_Z_CTRL 0x110c 4262306a36Sopenharmony_ci#define FB_VGA_CTRL 0x1110 4362306a36Sopenharmony_ci#define REFRESH_AB_CTRL 0x1114 4462306a36Sopenharmony_ci#define REFRESH_CD_CTRL 0x1118 4562306a36Sopenharmony_ci#define FB_OVL_CTRL 0x111c 4662306a36Sopenharmony_ci#define FB_CTRL_TYPE 0x80000000 4762306a36Sopenharmony_ci#define FB_CTRL_WIDTH_MASK 0x007f0000 4862306a36Sopenharmony_ci#define FB_CTRL_WIDTH_SHIFT 16 4962306a36Sopenharmony_ci#define FB_CTRL_START_SEG_MASK 0x00003fff 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define REFRESH_START 0x1098 5262306a36Sopenharmony_ci#define REFRESH_SIZE 0x109c 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* "Direct" framebuffer access registers */ 5562306a36Sopenharmony_ci#define DFA_FB_A 0x11e0 5662306a36Sopenharmony_ci#define DFA_FB_B 0x11e4 5762306a36Sopenharmony_ci#define DFA_FB_C 0x11e8 5862306a36Sopenharmony_ci#define DFA_FB_D 0x11ec 5962306a36Sopenharmony_ci#define DFA_FB_ENABLE 0x80000000 6062306a36Sopenharmony_ci#define DFA_FB_BASE_MASK 0x03f00000 6162306a36Sopenharmony_ci#define DFA_FB_STRIDE_1k 0x00000000 6262306a36Sopenharmony_ci#define DFA_FB_STRIDE_2k 0x00000010 6362306a36Sopenharmony_ci#define DFA_FB_STRIDE_4k 0x00000020 6462306a36Sopenharmony_ci#define DFA_PIX_8BIT 0x00000000 6562306a36Sopenharmony_ci#define DFA_PIX_16BIT_565 0x00000001 6662306a36Sopenharmony_ci#define DFA_PIX_16BIT_1555 0x00000002 6762306a36Sopenharmony_ci#define DFA_PIX_24BIT 0x00000004 6862306a36Sopenharmony_ci#define DFA_PIX_32BIT 0x00000005 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* maps DFA_PIX_* to pixel size in bytes */ 7162306a36Sopenharmony_cistatic const unsigned char pixsize[] = { 7262306a36Sopenharmony_ci 1, 2, 2, 2, 4, 4 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* Display timing generator registers */ 7662306a36Sopenharmony_ci#define DTG_CONTROL 0x1900 7762306a36Sopenharmony_ci#define DTG_CTL_SCREEN_REFRESH 2 7862306a36Sopenharmony_ci#define DTG_CTL_ENABLE 1 7962306a36Sopenharmony_ci#define DTG_HORIZ_EXTENT 0x1904 8062306a36Sopenharmony_ci#define DTG_HORIZ_DISPLAY 0x1908 8162306a36Sopenharmony_ci#define DTG_HSYNC_START 0x190c 8262306a36Sopenharmony_ci#define DTG_HSYNC_END 0x1910 8362306a36Sopenharmony_ci#define DTG_HSYNC_END_COMP 0x1914 8462306a36Sopenharmony_ci#define DTG_VERT_EXTENT 0x1918 8562306a36Sopenharmony_ci#define DTG_VERT_DISPLAY 0x191c 8662306a36Sopenharmony_ci#define DTG_VSYNC_START 0x1920 8762306a36Sopenharmony_ci#define DTG_VSYNC_END 0x1924 8862306a36Sopenharmony_ci#define DTG_VERT_SHORT 0x1928 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* PLL/RAMDAC registers */ 9162306a36Sopenharmony_ci#define DISP_CTL 0x402c 9262306a36Sopenharmony_ci#define DISP_CTL_OFF 2 9362306a36Sopenharmony_ci#define SYNC_CTL 0x4034 9462306a36Sopenharmony_ci#define SYNC_CTL_SYNC_ON_RGB 1 9562306a36Sopenharmony_ci#define SYNC_CTL_SYNC_OFF 2 9662306a36Sopenharmony_ci#define SYNC_CTL_HSYNC_INV 8 9762306a36Sopenharmony_ci#define SYNC_CTL_VSYNC_INV 0x10 9862306a36Sopenharmony_ci#define SYNC_CTL_HSYNC_OFF 0x20 9962306a36Sopenharmony_ci#define SYNC_CTL_VSYNC_OFF 0x40 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define PLL_M 0x4040 10262306a36Sopenharmony_ci#define PLL_N 0x4044 10362306a36Sopenharmony_ci#define PLL_POSTDIV 0x4048 10462306a36Sopenharmony_ci#define PLL_C 0x404c 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* Hardware cursor */ 10762306a36Sopenharmony_ci#define CURSOR_X 0x4078 10862306a36Sopenharmony_ci#define CURSOR_Y 0x407c 10962306a36Sopenharmony_ci#define CURSOR_HOTSPOT 0x4080 11062306a36Sopenharmony_ci#define CURSOR_MODE 0x4084 11162306a36Sopenharmony_ci#define CURSOR_MODE_OFF 0 11262306a36Sopenharmony_ci#define CURSOR_MODE_4BPP 1 11362306a36Sopenharmony_ci#define CURSOR_PIXMAP 0x5000 11462306a36Sopenharmony_ci#define CURSOR_CMAP 0x7400 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* Window attribute table */ 11762306a36Sopenharmony_ci#define WAT_FMT 0x4100 11862306a36Sopenharmony_ci#define WAT_FMT_24BIT 0 11962306a36Sopenharmony_ci#define WAT_FMT_16BIT_565 1 12062306a36Sopenharmony_ci#define WAT_FMT_16BIT_1555 2 12162306a36Sopenharmony_ci#define WAT_FMT_32BIT 3 /* 0 vs. 3 is a guess */ 12262306a36Sopenharmony_ci#define WAT_FMT_8BIT_332 9 12362306a36Sopenharmony_ci#define WAT_FMT_8BIT 0xa 12462306a36Sopenharmony_ci#define WAT_FMT_NO_CMAP 4 /* ORd in to other values */ 12562306a36Sopenharmony_ci#define WAT_CMAP_OFFSET 0x4104 /* 4-bit value gets << 6 */ 12662306a36Sopenharmony_ci#define WAT_CTRL 0x4108 12762306a36Sopenharmony_ci#define WAT_CTRL_SEL_B 1 /* select B buffer if 1 */ 12862306a36Sopenharmony_ci#define WAT_CTRL_NO_INC 2 12962306a36Sopenharmony_ci#define WAT_GAMMA_CTRL 0x410c 13062306a36Sopenharmony_ci#define WAT_GAMMA_DISABLE 1 /* disables gamma cmap */ 13162306a36Sopenharmony_ci#define WAT_OVL_CTRL 0x430c /* controls overlay */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* Indexed by DFA_PIX_* values */ 13462306a36Sopenharmony_cistatic const unsigned char watfmt[] = { 13562306a36Sopenharmony_ci WAT_FMT_8BIT, WAT_FMT_16BIT_565, WAT_FMT_16BIT_1555, 0, 13662306a36Sopenharmony_ci WAT_FMT_24BIT, WAT_FMT_32BIT 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* Colormap array; 1k entries of 4 bytes each */ 14062306a36Sopenharmony_ci#define CMAP 0x6000 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#define readreg(par, reg) readl((par)->regs + (reg)) 14362306a36Sopenharmony_ci#define writereg(par, reg, val) writel((val), (par)->regs + (reg)) 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistruct gxt4500_par { 14662306a36Sopenharmony_ci void __iomem *regs; 14762306a36Sopenharmony_ci int wc_cookie; 14862306a36Sopenharmony_ci int pixfmt; /* pixel format, see DFA_PIX_* values */ 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* PLL parameters */ 15162306a36Sopenharmony_ci int refclk_ps; /* ref clock period in picoseconds */ 15262306a36Sopenharmony_ci int pll_m; /* ref clock divisor */ 15362306a36Sopenharmony_ci int pll_n; /* VCO divisor */ 15462306a36Sopenharmony_ci int pll_pd1; /* first post-divisor */ 15562306a36Sopenharmony_ci int pll_pd2; /* second post-divisor */ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci u32 pseudo_palette[16]; /* used in color blits */ 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* mode requested by user */ 16162306a36Sopenharmony_cistatic char *mode_option; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* default mode: 1280x1024 @ 60 Hz, 8 bpp */ 16462306a36Sopenharmony_cistatic const struct fb_videomode defaultmode = { 16562306a36Sopenharmony_ci .refresh = 60, 16662306a36Sopenharmony_ci .xres = 1280, 16762306a36Sopenharmony_ci .yres = 1024, 16862306a36Sopenharmony_ci .pixclock = 9295, 16962306a36Sopenharmony_ci .left_margin = 248, 17062306a36Sopenharmony_ci .right_margin = 48, 17162306a36Sopenharmony_ci .upper_margin = 38, 17262306a36Sopenharmony_ci .lower_margin = 1, 17362306a36Sopenharmony_ci .hsync_len = 112, 17462306a36Sopenharmony_ci .vsync_len = 3, 17562306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED 17662306a36Sopenharmony_ci}; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* List of supported cards */ 17962306a36Sopenharmony_cienum gxt_cards { 18062306a36Sopenharmony_ci GXT4500P, 18162306a36Sopenharmony_ci GXT6500P, 18262306a36Sopenharmony_ci GXT4000P, 18362306a36Sopenharmony_ci GXT6000P 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* Card-specific information */ 18762306a36Sopenharmony_cistatic const struct cardinfo { 18862306a36Sopenharmony_ci int refclk_ps; /* period of PLL reference clock in ps */ 18962306a36Sopenharmony_ci const char *cardname; 19062306a36Sopenharmony_ci} cardinfo[] = { 19162306a36Sopenharmony_ci [GXT4500P] = { .refclk_ps = 9259, .cardname = "IBM GXT4500P" }, 19262306a36Sopenharmony_ci [GXT6500P] = { .refclk_ps = 9259, .cardname = "IBM GXT6500P" }, 19362306a36Sopenharmony_ci [GXT4000P] = { .refclk_ps = 40000, .cardname = "IBM GXT4000P" }, 19462306a36Sopenharmony_ci [GXT6000P] = { .refclk_ps = 40000, .cardname = "IBM GXT6000P" }, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* 19862306a36Sopenharmony_ci * The refclk and VCO dividers appear to use a linear feedback shift 19962306a36Sopenharmony_ci * register, which gets reloaded when it reaches a terminal value, at 20062306a36Sopenharmony_ci * which point the divider output is toggled. Thus one can obtain 20162306a36Sopenharmony_ci * whatever divisor is required by putting the appropriate value into 20262306a36Sopenharmony_ci * the reload register. For a divisor of N, one puts the value from 20362306a36Sopenharmony_ci * the LFSR sequence that comes N-1 places before the terminal value 20462306a36Sopenharmony_ci * into the reload register. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic const unsigned char mdivtab[] = { 20862306a36Sopenharmony_ci/* 1 */ 0x3f, 0x00, 0x20, 0x10, 0x28, 0x14, 0x2a, 0x15, 0x0a, 20962306a36Sopenharmony_ci/* 10 */ 0x25, 0x32, 0x19, 0x0c, 0x26, 0x13, 0x09, 0x04, 0x22, 0x11, 21062306a36Sopenharmony_ci/* 20 */ 0x08, 0x24, 0x12, 0x29, 0x34, 0x1a, 0x2d, 0x36, 0x1b, 0x0d, 21162306a36Sopenharmony_ci/* 30 */ 0x06, 0x23, 0x31, 0x38, 0x1c, 0x2e, 0x17, 0x0b, 0x05, 0x02, 21262306a36Sopenharmony_ci/* 40 */ 0x21, 0x30, 0x18, 0x2c, 0x16, 0x2b, 0x35, 0x3a, 0x1d, 0x0e, 21362306a36Sopenharmony_ci/* 50 */ 0x27, 0x33, 0x39, 0x3c, 0x1e, 0x2f, 0x37, 0x3b, 0x3d, 0x3e, 21462306a36Sopenharmony_ci/* 60 */ 0x1f, 0x0f, 0x07, 0x03, 0x01, 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic const unsigned char ndivtab[] = { 21862306a36Sopenharmony_ci/* 2 */ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0x78, 0xbc, 0x5e, 21962306a36Sopenharmony_ci/* 10 */ 0x2f, 0x17, 0x0b, 0x85, 0xc2, 0xe1, 0x70, 0x38, 0x9c, 0x4e, 22062306a36Sopenharmony_ci/* 20 */ 0xa7, 0xd3, 0xe9, 0xf4, 0xfa, 0xfd, 0xfe, 0x7f, 0xbf, 0xdf, 22162306a36Sopenharmony_ci/* 30 */ 0xef, 0x77, 0x3b, 0x1d, 0x8e, 0xc7, 0xe3, 0x71, 0xb8, 0xdc, 22262306a36Sopenharmony_ci/* 40 */ 0x6e, 0xb7, 0x5b, 0x2d, 0x16, 0x8b, 0xc5, 0xe2, 0xf1, 0xf8, 22362306a36Sopenharmony_ci/* 50 */ 0xfc, 0x7e, 0x3f, 0x9f, 0xcf, 0x67, 0xb3, 0xd9, 0x6c, 0xb6, 22462306a36Sopenharmony_ci/* 60 */ 0xdb, 0x6d, 0x36, 0x9b, 0x4d, 0x26, 0x13, 0x89, 0xc4, 0x62, 22562306a36Sopenharmony_ci/* 70 */ 0xb1, 0xd8, 0xec, 0xf6, 0xfb, 0x7d, 0xbe, 0x5f, 0xaf, 0x57, 22662306a36Sopenharmony_ci/* 80 */ 0x2b, 0x95, 0x4a, 0x25, 0x92, 0x49, 0xa4, 0x52, 0x29, 0x94, 22762306a36Sopenharmony_ci/* 90 */ 0xca, 0x65, 0xb2, 0x59, 0x2c, 0x96, 0xcb, 0xe5, 0xf2, 0x79, 22862306a36Sopenharmony_ci/* 100 */ 0x3c, 0x1e, 0x0f, 0x07, 0x83, 0x41, 0x20, 0x90, 0x48, 0x24, 22962306a36Sopenharmony_ci/* 110 */ 0x12, 0x09, 0x84, 0x42, 0xa1, 0x50, 0x28, 0x14, 0x8a, 0x45, 23062306a36Sopenharmony_ci/* 120 */ 0xa2, 0xd1, 0xe8, 0x74, 0xba, 0xdd, 0xee, 0xf7, 0x7b, 0x3d, 23162306a36Sopenharmony_ci/* 130 */ 0x9e, 0x4f, 0x27, 0x93, 0xc9, 0xe4, 0x72, 0x39, 0x1c, 0x0e, 23262306a36Sopenharmony_ci/* 140 */ 0x87, 0xc3, 0x61, 0x30, 0x18, 0x8c, 0xc6, 0x63, 0x31, 0x98, 23362306a36Sopenharmony_ci/* 150 */ 0xcc, 0xe6, 0x73, 0xb9, 0x5c, 0x2e, 0x97, 0x4b, 0xa5, 0xd2, 23462306a36Sopenharmony_ci/* 160 */ 0x69, 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int calc_pll(int period_ps, struct gxt4500_par *par) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci int m, n, pdiv1, pdiv2, postdiv; 24062306a36Sopenharmony_ci int pll_period, best_error, t, intf; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* only deal with range 5MHz - 300MHz */ 24362306a36Sopenharmony_ci if (period_ps < 3333 || period_ps > 200000) 24462306a36Sopenharmony_ci return -1; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci best_error = 1000000; 24762306a36Sopenharmony_ci for (pdiv1 = 1; pdiv1 <= 8; ++pdiv1) { 24862306a36Sopenharmony_ci for (pdiv2 = 1; pdiv2 <= pdiv1; ++pdiv2) { 24962306a36Sopenharmony_ci postdiv = pdiv1 * pdiv2; 25062306a36Sopenharmony_ci pll_period = DIV_ROUND_UP(period_ps, postdiv); 25162306a36Sopenharmony_ci /* keep pll in range 350..600 MHz */ 25262306a36Sopenharmony_ci if (pll_period < 1666 || pll_period > 2857) 25362306a36Sopenharmony_ci continue; 25462306a36Sopenharmony_ci for (m = 1; m <= 64; ++m) { 25562306a36Sopenharmony_ci intf = m * par->refclk_ps; 25662306a36Sopenharmony_ci if (intf > 500000) 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci n = intf * postdiv / period_ps; 25962306a36Sopenharmony_ci if (n < 3 || n > 160) 26062306a36Sopenharmony_ci continue; 26162306a36Sopenharmony_ci t = par->refclk_ps * m * postdiv / n; 26262306a36Sopenharmony_ci t -= period_ps; 26362306a36Sopenharmony_ci if (t >= 0 && t < best_error) { 26462306a36Sopenharmony_ci par->pll_m = m; 26562306a36Sopenharmony_ci par->pll_n = n; 26662306a36Sopenharmony_ci par->pll_pd1 = pdiv1; 26762306a36Sopenharmony_ci par->pll_pd2 = pdiv2; 26862306a36Sopenharmony_ci best_error = t; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci if (best_error == 1000000) 27462306a36Sopenharmony_ci return -1; 27562306a36Sopenharmony_ci return 0; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int calc_pixclock(struct gxt4500_par *par) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci return par->refclk_ps * par->pll_m * par->pll_pd1 * par->pll_pd2 28162306a36Sopenharmony_ci / par->pll_n; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int gxt4500_var_to_par(struct fb_var_screeninfo *var, 28562306a36Sopenharmony_ci struct gxt4500_par *par) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci if (var->xres + var->xoffset > var->xres_virtual || 28862306a36Sopenharmony_ci var->yres + var->yoffset > var->yres_virtual || 28962306a36Sopenharmony_ci var->xres_virtual > 4096) 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci if ((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (calc_pll(var->pixclock, par) < 0) 29562306a36Sopenharmony_ci return -EINVAL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci switch (var->bits_per_pixel) { 29862306a36Sopenharmony_ci case 32: 29962306a36Sopenharmony_ci if (var->transp.length) 30062306a36Sopenharmony_ci par->pixfmt = DFA_PIX_32BIT; 30162306a36Sopenharmony_ci else 30262306a36Sopenharmony_ci par->pixfmt = DFA_PIX_24BIT; 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci case 24: 30562306a36Sopenharmony_ci par->pixfmt = DFA_PIX_24BIT; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci case 16: 30862306a36Sopenharmony_ci if (var->green.length == 5) 30962306a36Sopenharmony_ci par->pixfmt = DFA_PIX_16BIT_1555; 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci par->pixfmt = DFA_PIX_16BIT_565; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case 8: 31462306a36Sopenharmony_ci par->pixfmt = DFA_PIX_8BIT; 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci default: 31762306a36Sopenharmony_ci return -EINVAL; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic const struct fb_bitfield eightbits = {0, 8}; 32462306a36Sopenharmony_cistatic const struct fb_bitfield nobits = {0, 0}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void gxt4500_unpack_pixfmt(struct fb_var_screeninfo *var, 32762306a36Sopenharmony_ci int pixfmt) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci var->bits_per_pixel = pixsize[pixfmt] * 8; 33062306a36Sopenharmony_ci var->red = eightbits; 33162306a36Sopenharmony_ci var->green = eightbits; 33262306a36Sopenharmony_ci var->blue = eightbits; 33362306a36Sopenharmony_ci var->transp = nobits; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci switch (pixfmt) { 33662306a36Sopenharmony_ci case DFA_PIX_16BIT_565: 33762306a36Sopenharmony_ci var->red.length = 5; 33862306a36Sopenharmony_ci var->green.length = 6; 33962306a36Sopenharmony_ci var->blue.length = 5; 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci case DFA_PIX_16BIT_1555: 34262306a36Sopenharmony_ci var->red.length = 5; 34362306a36Sopenharmony_ci var->green.length = 5; 34462306a36Sopenharmony_ci var->blue.length = 5; 34562306a36Sopenharmony_ci var->transp.length = 1; 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci case DFA_PIX_32BIT: 34862306a36Sopenharmony_ci var->transp.length = 8; 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci if (pixfmt != DFA_PIX_8BIT) { 35262306a36Sopenharmony_ci var->blue.offset = 0; 35362306a36Sopenharmony_ci var->green.offset = var->blue.length; 35462306a36Sopenharmony_ci var->red.offset = var->green.offset + var->green.length; 35562306a36Sopenharmony_ci if (var->transp.length) 35662306a36Sopenharmony_ci var->transp.offset = 35762306a36Sopenharmony_ci var->red.offset + var->red.length; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int gxt4500_check_var(struct fb_var_screeninfo *var, 36262306a36Sopenharmony_ci struct fb_info *info) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct gxt4500_par par; 36562306a36Sopenharmony_ci int err; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci par = *(struct gxt4500_par *)info->par; 36862306a36Sopenharmony_ci err = gxt4500_var_to_par(var, &par); 36962306a36Sopenharmony_ci if (!err) { 37062306a36Sopenharmony_ci var->pixclock = calc_pixclock(&par); 37162306a36Sopenharmony_ci gxt4500_unpack_pixfmt(var, par.pixfmt); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci return err; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int gxt4500_set_par(struct fb_info *info) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct gxt4500_par *par = info->par; 37962306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 38062306a36Sopenharmony_ci int err; 38162306a36Sopenharmony_ci u32 ctrlreg, tmp; 38262306a36Sopenharmony_ci unsigned int dfa_ctl, pixfmt, stride; 38362306a36Sopenharmony_ci unsigned int wid_tiles, i; 38462306a36Sopenharmony_ci unsigned int prefetch_pix, htot; 38562306a36Sopenharmony_ci struct gxt4500_par save_par; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci save_par = *par; 38862306a36Sopenharmony_ci err = gxt4500_var_to_par(var, par); 38962306a36Sopenharmony_ci if (err) { 39062306a36Sopenharmony_ci *par = save_par; 39162306a36Sopenharmony_ci return err; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* turn off DTG for now */ 39562306a36Sopenharmony_ci ctrlreg = readreg(par, DTG_CONTROL); 39662306a36Sopenharmony_ci ctrlreg &= ~(DTG_CTL_ENABLE | DTG_CTL_SCREEN_REFRESH); 39762306a36Sopenharmony_ci writereg(par, DTG_CONTROL, ctrlreg); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* set PLL registers */ 40062306a36Sopenharmony_ci tmp = readreg(par, PLL_C) & ~0x7f; 40162306a36Sopenharmony_ci if (par->pll_n < 38) 40262306a36Sopenharmony_ci tmp |= 0x29; 40362306a36Sopenharmony_ci if (par->pll_n < 69) 40462306a36Sopenharmony_ci tmp |= 0x35; 40562306a36Sopenharmony_ci else if (par->pll_n < 100) 40662306a36Sopenharmony_ci tmp |= 0x76; 40762306a36Sopenharmony_ci else 40862306a36Sopenharmony_ci tmp |= 0x7e; 40962306a36Sopenharmony_ci writereg(par, PLL_C, tmp); 41062306a36Sopenharmony_ci writereg(par, PLL_M, mdivtab[par->pll_m - 1]); 41162306a36Sopenharmony_ci writereg(par, PLL_N, ndivtab[par->pll_n - 2]); 41262306a36Sopenharmony_ci tmp = ((8 - par->pll_pd2) << 3) | (8 - par->pll_pd1); 41362306a36Sopenharmony_ci if (par->pll_pd1 == 8 || par->pll_pd2 == 8) { 41462306a36Sopenharmony_ci /* work around erratum */ 41562306a36Sopenharmony_ci writereg(par, PLL_POSTDIV, tmp | 0x9); 41662306a36Sopenharmony_ci udelay(1); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci writereg(par, PLL_POSTDIV, tmp); 41962306a36Sopenharmony_ci msleep(20); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* turn off hardware cursor */ 42262306a36Sopenharmony_ci writereg(par, CURSOR_MODE, CURSOR_MODE_OFF); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* reset raster engine */ 42562306a36Sopenharmony_ci writereg(par, CTRL_REG0, CR0_RASTER_RESET | (CR0_RASTER_RESET << 16)); 42662306a36Sopenharmony_ci udelay(10); 42762306a36Sopenharmony_ci writereg(par, CTRL_REG0, CR0_RASTER_RESET << 16); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* set display timing generator registers */ 43062306a36Sopenharmony_ci htot = var->xres + var->left_margin + var->right_margin + 43162306a36Sopenharmony_ci var->hsync_len; 43262306a36Sopenharmony_ci writereg(par, DTG_HORIZ_EXTENT, htot - 1); 43362306a36Sopenharmony_ci writereg(par, DTG_HORIZ_DISPLAY, var->xres - 1); 43462306a36Sopenharmony_ci writereg(par, DTG_HSYNC_START, var->xres + var->right_margin - 1); 43562306a36Sopenharmony_ci writereg(par, DTG_HSYNC_END, 43662306a36Sopenharmony_ci var->xres + var->right_margin + var->hsync_len - 1); 43762306a36Sopenharmony_ci writereg(par, DTG_HSYNC_END_COMP, 43862306a36Sopenharmony_ci var->xres + var->right_margin + var->hsync_len - 1); 43962306a36Sopenharmony_ci writereg(par, DTG_VERT_EXTENT, 44062306a36Sopenharmony_ci var->yres + var->upper_margin + var->lower_margin + 44162306a36Sopenharmony_ci var->vsync_len - 1); 44262306a36Sopenharmony_ci writereg(par, DTG_VERT_DISPLAY, var->yres - 1); 44362306a36Sopenharmony_ci writereg(par, DTG_VSYNC_START, var->yres + var->lower_margin - 1); 44462306a36Sopenharmony_ci writereg(par, DTG_VSYNC_END, 44562306a36Sopenharmony_ci var->yres + var->lower_margin + var->vsync_len - 1); 44662306a36Sopenharmony_ci prefetch_pix = 3300000 / var->pixclock; 44762306a36Sopenharmony_ci if (prefetch_pix >= htot) 44862306a36Sopenharmony_ci prefetch_pix = htot - 1; 44962306a36Sopenharmony_ci writereg(par, DTG_VERT_SHORT, htot - prefetch_pix - 1); 45062306a36Sopenharmony_ci ctrlreg |= DTG_CTL_ENABLE | DTG_CTL_SCREEN_REFRESH; 45162306a36Sopenharmony_ci writereg(par, DTG_CONTROL, ctrlreg); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* calculate stride in DFA aperture */ 45462306a36Sopenharmony_ci if (var->xres_virtual > 2048) { 45562306a36Sopenharmony_ci stride = 4096; 45662306a36Sopenharmony_ci dfa_ctl = DFA_FB_STRIDE_4k; 45762306a36Sopenharmony_ci } else if (var->xres_virtual > 1024) { 45862306a36Sopenharmony_ci stride = 2048; 45962306a36Sopenharmony_ci dfa_ctl = DFA_FB_STRIDE_2k; 46062306a36Sopenharmony_ci } else { 46162306a36Sopenharmony_ci stride = 1024; 46262306a36Sopenharmony_ci dfa_ctl = DFA_FB_STRIDE_1k; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* Set up framebuffer definition */ 46662306a36Sopenharmony_ci wid_tiles = (var->xres_virtual + 63) >> 6; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* XXX add proper FB allocation here someday */ 46962306a36Sopenharmony_ci writereg(par, FB_AB_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0); 47062306a36Sopenharmony_ci writereg(par, REFRESH_AB_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0); 47162306a36Sopenharmony_ci writereg(par, FB_CD_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0); 47262306a36Sopenharmony_ci writereg(par, REFRESH_CD_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0); 47362306a36Sopenharmony_ci writereg(par, REFRESH_START, (var->xoffset << 16) | var->yoffset); 47462306a36Sopenharmony_ci writereg(par, REFRESH_SIZE, (var->xres << 16) | var->yres); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* Set up framebuffer access by CPU */ 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci pixfmt = par->pixfmt; 47962306a36Sopenharmony_ci dfa_ctl |= DFA_FB_ENABLE | pixfmt; 48062306a36Sopenharmony_ci writereg(par, DFA_FB_A, dfa_ctl); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* 48362306a36Sopenharmony_ci * Set up window attribute table. 48462306a36Sopenharmony_ci * We set all WAT entries the same so it doesn't matter what the 48562306a36Sopenharmony_ci * window ID (WID) plane contains. 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_ci for (i = 0; i < 32; ++i) { 48862306a36Sopenharmony_ci writereg(par, WAT_FMT + (i << 4), watfmt[pixfmt]); 48962306a36Sopenharmony_ci writereg(par, WAT_CMAP_OFFSET + (i << 4), 0); 49062306a36Sopenharmony_ci writereg(par, WAT_CTRL + (i << 4), 0); 49162306a36Sopenharmony_ci writereg(par, WAT_GAMMA_CTRL + (i << 4), WAT_GAMMA_DISABLE); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Set sync polarity etc. */ 49562306a36Sopenharmony_ci ctrlreg = readreg(par, SYNC_CTL) & 49662306a36Sopenharmony_ci ~(SYNC_CTL_SYNC_ON_RGB | SYNC_CTL_HSYNC_INV | 49762306a36Sopenharmony_ci SYNC_CTL_VSYNC_INV); 49862306a36Sopenharmony_ci if (var->sync & FB_SYNC_ON_GREEN) 49962306a36Sopenharmony_ci ctrlreg |= SYNC_CTL_SYNC_ON_RGB; 50062306a36Sopenharmony_ci if (!(var->sync & FB_SYNC_HOR_HIGH_ACT)) 50162306a36Sopenharmony_ci ctrlreg |= SYNC_CTL_HSYNC_INV; 50262306a36Sopenharmony_ci if (!(var->sync & FB_SYNC_VERT_HIGH_ACT)) 50362306a36Sopenharmony_ci ctrlreg |= SYNC_CTL_VSYNC_INV; 50462306a36Sopenharmony_ci writereg(par, SYNC_CTL, ctrlreg); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci info->fix.line_length = stride * pixsize[pixfmt]; 50762306a36Sopenharmony_ci info->fix.visual = (pixfmt == DFA_PIX_8BIT)? FB_VISUAL_PSEUDOCOLOR: 50862306a36Sopenharmony_ci FB_VISUAL_DIRECTCOLOR; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int gxt4500_setcolreg(unsigned int reg, unsigned int red, 51462306a36Sopenharmony_ci unsigned int green, unsigned int blue, 51562306a36Sopenharmony_ci unsigned int transp, struct fb_info *info) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci u32 cmap_entry; 51862306a36Sopenharmony_ci struct gxt4500_par *par = info->par; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (reg > 1023) 52162306a36Sopenharmony_ci return 1; 52262306a36Sopenharmony_ci cmap_entry = ((transp & 0xff00) << 16) | ((red & 0xff00) << 8) | 52362306a36Sopenharmony_ci (green & 0xff00) | (blue >> 8); 52462306a36Sopenharmony_ci writereg(par, CMAP + reg * 4, cmap_entry); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (reg < 16 && par->pixfmt != DFA_PIX_8BIT) { 52762306a36Sopenharmony_ci u32 *pal = info->pseudo_palette; 52862306a36Sopenharmony_ci u32 val = reg; 52962306a36Sopenharmony_ci switch (par->pixfmt) { 53062306a36Sopenharmony_ci case DFA_PIX_16BIT_565: 53162306a36Sopenharmony_ci val |= (reg << 11) | (reg << 5); 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci case DFA_PIX_16BIT_1555: 53462306a36Sopenharmony_ci val |= (reg << 10) | (reg << 5); 53562306a36Sopenharmony_ci break; 53662306a36Sopenharmony_ci case DFA_PIX_32BIT: 53762306a36Sopenharmony_ci val |= (reg << 24); 53862306a36Sopenharmony_ci fallthrough; 53962306a36Sopenharmony_ci case DFA_PIX_24BIT: 54062306a36Sopenharmony_ci val |= (reg << 16) | (reg << 8); 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci pal[reg] = val; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return 0; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic int gxt4500_pan_display(struct fb_var_screeninfo *var, 55062306a36Sopenharmony_ci struct fb_info *info) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct gxt4500_par *par = info->par; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (var->xoffset & 7) 55562306a36Sopenharmony_ci return -EINVAL; 55662306a36Sopenharmony_ci if (var->xoffset + info->var.xres > info->var.xres_virtual || 55762306a36Sopenharmony_ci var->yoffset + info->var.yres > info->var.yres_virtual) 55862306a36Sopenharmony_ci return -EINVAL; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci writereg(par, REFRESH_START, (var->xoffset << 16) | var->yoffset); 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int gxt4500_blank(int blank, struct fb_info *info) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct gxt4500_par *par = info->par; 56762306a36Sopenharmony_ci int ctrl, dctl; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ctrl = readreg(par, SYNC_CTL); 57062306a36Sopenharmony_ci ctrl &= ~(SYNC_CTL_SYNC_OFF | SYNC_CTL_HSYNC_OFF | SYNC_CTL_VSYNC_OFF); 57162306a36Sopenharmony_ci dctl = readreg(par, DISP_CTL); 57262306a36Sopenharmony_ci dctl |= DISP_CTL_OFF; 57362306a36Sopenharmony_ci switch (blank) { 57462306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 57562306a36Sopenharmony_ci dctl &= ~DISP_CTL_OFF; 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 57862306a36Sopenharmony_ci ctrl |= SYNC_CTL_SYNC_OFF; 57962306a36Sopenharmony_ci break; 58062306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 58162306a36Sopenharmony_ci ctrl |= SYNC_CTL_HSYNC_OFF; 58262306a36Sopenharmony_ci break; 58362306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 58462306a36Sopenharmony_ci ctrl |= SYNC_CTL_VSYNC_OFF; 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci default: ; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci writereg(par, SYNC_CTL, ctrl); 58962306a36Sopenharmony_ci writereg(par, DISP_CTL, dctl); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic const struct fb_fix_screeninfo gxt4500_fix = { 59562306a36Sopenharmony_ci .id = "IBM GXT4500P", 59662306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 59762306a36Sopenharmony_ci .visual = FB_VISUAL_PSEUDOCOLOR, 59862306a36Sopenharmony_ci .xpanstep = 8, 59962306a36Sopenharmony_ci .ypanstep = 1, 60062306a36Sopenharmony_ci .mmio_len = 0x20000, 60162306a36Sopenharmony_ci}; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic const struct fb_ops gxt4500_ops = { 60462306a36Sopenharmony_ci .owner = THIS_MODULE, 60562306a36Sopenharmony_ci FB_DEFAULT_IOMEM_OPS, 60662306a36Sopenharmony_ci .fb_check_var = gxt4500_check_var, 60762306a36Sopenharmony_ci .fb_set_par = gxt4500_set_par, 60862306a36Sopenharmony_ci .fb_setcolreg = gxt4500_setcolreg, 60962306a36Sopenharmony_ci .fb_pan_display = gxt4500_pan_display, 61062306a36Sopenharmony_ci .fb_blank = gxt4500_blank, 61162306a36Sopenharmony_ci}; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci/* PCI functions */ 61462306a36Sopenharmony_cistatic int gxt4500_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci int err; 61762306a36Sopenharmony_ci unsigned long reg_phys, fb_phys; 61862306a36Sopenharmony_ci struct gxt4500_par *par; 61962306a36Sopenharmony_ci struct fb_info *info; 62062306a36Sopenharmony_ci struct fb_var_screeninfo var; 62162306a36Sopenharmony_ci enum gxt_cards cardtype; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci err = aperture_remove_conflicting_pci_devices(pdev, "gxt4500fb"); 62462306a36Sopenharmony_ci if (err) 62562306a36Sopenharmony_ci return err; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci err = pci_enable_device(pdev); 62862306a36Sopenharmony_ci if (err) { 62962306a36Sopenharmony_ci dev_err(&pdev->dev, "gxt4500: cannot enable PCI device: %d\n", 63062306a36Sopenharmony_ci err); 63162306a36Sopenharmony_ci return err; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci reg_phys = pci_resource_start(pdev, 0); 63562306a36Sopenharmony_ci if (!request_mem_region(reg_phys, pci_resource_len(pdev, 0), 63662306a36Sopenharmony_ci "gxt4500 regs")) { 63762306a36Sopenharmony_ci dev_err(&pdev->dev, "gxt4500: cannot get registers\n"); 63862306a36Sopenharmony_ci goto err_nodev; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci fb_phys = pci_resource_start(pdev, 1); 64262306a36Sopenharmony_ci if (!request_mem_region(fb_phys, pci_resource_len(pdev, 1), 64362306a36Sopenharmony_ci "gxt4500 FB")) { 64462306a36Sopenharmony_ci dev_err(&pdev->dev, "gxt4500: cannot get framebuffer\n"); 64562306a36Sopenharmony_ci goto err_free_regs; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct gxt4500_par), &pdev->dev); 64962306a36Sopenharmony_ci if (!info) 65062306a36Sopenharmony_ci goto err_free_fb; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci par = info->par; 65362306a36Sopenharmony_ci cardtype = ent->driver_data; 65462306a36Sopenharmony_ci par->refclk_ps = cardinfo[cardtype].refclk_ps; 65562306a36Sopenharmony_ci info->fix = gxt4500_fix; 65662306a36Sopenharmony_ci strscpy(info->fix.id, cardinfo[cardtype].cardname, 65762306a36Sopenharmony_ci sizeof(info->fix.id)); 65862306a36Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci info->fix.mmio_start = reg_phys; 66162306a36Sopenharmony_ci par->regs = pci_ioremap_bar(pdev, 0); 66262306a36Sopenharmony_ci if (!par->regs) { 66362306a36Sopenharmony_ci dev_err(&pdev->dev, "gxt4500: cannot map registers\n"); 66462306a36Sopenharmony_ci goto err_free_all; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci info->fix.smem_start = fb_phys; 66862306a36Sopenharmony_ci info->fix.smem_len = pci_resource_len(pdev, 1); 66962306a36Sopenharmony_ci info->screen_base = pci_ioremap_wc_bar(pdev, 1); 67062306a36Sopenharmony_ci if (!info->screen_base) { 67162306a36Sopenharmony_ci dev_err(&pdev->dev, "gxt4500: cannot map framebuffer\n"); 67262306a36Sopenharmony_ci goto err_unmap_regs; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci pci_set_drvdata(pdev, info); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci par->wc_cookie = arch_phys_wc_add(info->fix.smem_start, 67862306a36Sopenharmony_ci info->fix.smem_len); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 68162306a36Sopenharmony_ci /* Set byte-swapping for DFA aperture for all pixel sizes */ 68262306a36Sopenharmony_ci pci_write_config_dword(pdev, CFG_ENDIAN0, 0x333300); 68362306a36Sopenharmony_ci#else /* __LITTLE_ENDIAN */ 68462306a36Sopenharmony_ci /* not sure what this means but fgl23 driver does that */ 68562306a36Sopenharmony_ci pci_write_config_dword(pdev, CFG_ENDIAN0, 0x2300); 68662306a36Sopenharmony_ci/* pci_write_config_dword(pdev, CFG_ENDIAN0 + 4, 0x400000);*/ 68762306a36Sopenharmony_ci pci_write_config_dword(pdev, CFG_ENDIAN0 + 8, 0x98530000); 68862306a36Sopenharmony_ci#endif 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci info->fbops = &gxt4500_ops; 69162306a36Sopenharmony_ci info->flags = FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci err = fb_alloc_cmap(&info->cmap, 256, 0); 69462306a36Sopenharmony_ci if (err) { 69562306a36Sopenharmony_ci dev_err(&pdev->dev, "gxt4500: cannot allocate cmap\n"); 69662306a36Sopenharmony_ci goto err_unmap_all; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci gxt4500_blank(FB_BLANK_UNBLANK, info); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (!fb_find_mode(&var, info, mode_option, NULL, 0, &defaultmode, 8)) { 70262306a36Sopenharmony_ci dev_err(&pdev->dev, "gxt4500: cannot find valid video mode\n"); 70362306a36Sopenharmony_ci goto err_free_cmap; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci info->var = var; 70662306a36Sopenharmony_ci if (gxt4500_set_par(info)) { 70762306a36Sopenharmony_ci printk(KERN_ERR "gxt4500: cannot set video mode\n"); 70862306a36Sopenharmony_ci goto err_free_cmap; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (register_framebuffer(info) < 0) { 71262306a36Sopenharmony_ci dev_err(&pdev->dev, "gxt4500: cannot register framebuffer\n"); 71362306a36Sopenharmony_ci goto err_free_cmap; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci fb_info(info, "%s frame buffer device\n", info->fix.id); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci err_free_cmap: 72062306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 72162306a36Sopenharmony_ci err_unmap_all: 72262306a36Sopenharmony_ci iounmap(info->screen_base); 72362306a36Sopenharmony_ci err_unmap_regs: 72462306a36Sopenharmony_ci iounmap(par->regs); 72562306a36Sopenharmony_ci err_free_all: 72662306a36Sopenharmony_ci framebuffer_release(info); 72762306a36Sopenharmony_ci err_free_fb: 72862306a36Sopenharmony_ci release_mem_region(fb_phys, pci_resource_len(pdev, 1)); 72962306a36Sopenharmony_ci err_free_regs: 73062306a36Sopenharmony_ci release_mem_region(reg_phys, pci_resource_len(pdev, 0)); 73162306a36Sopenharmony_ci err_nodev: 73262306a36Sopenharmony_ci return -ENODEV; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic void gxt4500_remove(struct pci_dev *pdev) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct fb_info *info = pci_get_drvdata(pdev); 73862306a36Sopenharmony_ci struct gxt4500_par *par; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (!info) 74162306a36Sopenharmony_ci return; 74262306a36Sopenharmony_ci par = info->par; 74362306a36Sopenharmony_ci unregister_framebuffer(info); 74462306a36Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 74562306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 74662306a36Sopenharmony_ci iounmap(par->regs); 74762306a36Sopenharmony_ci iounmap(info->screen_base); 74862306a36Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 0), 74962306a36Sopenharmony_ci pci_resource_len(pdev, 0)); 75062306a36Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 1), 75162306a36Sopenharmony_ci pci_resource_len(pdev, 1)); 75262306a36Sopenharmony_ci framebuffer_release(info); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci/* supported chipsets */ 75662306a36Sopenharmony_cistatic const struct pci_device_id gxt4500_pci_tbl[] = { 75762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_GXT4500P), 75862306a36Sopenharmony_ci .driver_data = GXT4500P }, 75962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_GXT6500P), 76062306a36Sopenharmony_ci .driver_data = GXT6500P }, 76162306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_GXT4000P), 76262306a36Sopenharmony_ci .driver_data = GXT4000P }, 76362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_GXT6000P), 76462306a36Sopenharmony_ci .driver_data = GXT6000P }, 76562306a36Sopenharmony_ci { 0 } 76662306a36Sopenharmony_ci}; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, gxt4500_pci_tbl); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic struct pci_driver gxt4500_driver = { 77162306a36Sopenharmony_ci .name = "gxt4500", 77262306a36Sopenharmony_ci .id_table = gxt4500_pci_tbl, 77362306a36Sopenharmony_ci .probe = gxt4500_probe, 77462306a36Sopenharmony_ci .remove = gxt4500_remove, 77562306a36Sopenharmony_ci}; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic int gxt4500_init(void) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci if (fb_modesetting_disabled("gxt4500")) 78062306a36Sopenharmony_ci return -ENODEV; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci#ifndef MODULE 78362306a36Sopenharmony_ci if (fb_get_options("gxt4500", &mode_option)) 78462306a36Sopenharmony_ci return -ENODEV; 78562306a36Sopenharmony_ci#endif 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return pci_register_driver(&gxt4500_driver); 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_cimodule_init(gxt4500_init); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic void __exit gxt4500_exit(void) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci pci_unregister_driver(&gxt4500_driver); 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_cimodule_exit(gxt4500_exit); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ciMODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); 79862306a36Sopenharmony_ciMODULE_DESCRIPTION("FBDev driver for IBM GXT4500P/6500P and GXT4000P/6000P"); 79962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 80062306a36Sopenharmony_cimodule_param(mode_option, charp, 0); 80162306a36Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\""); 802