162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HP300 Topcat framebuffer support (derived from macfb of all things) 462306a36Sopenharmony_ci * Phil Blundell <philb@gnu.org> 1998 562306a36Sopenharmony_ci * DIO-II, colour map and Catseye support by 662306a36Sopenharmony_ci * Kars de Jong <jongk@linux-m68k.org>, May 2004. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/mm.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/fb.h> 1762306a36Sopenharmony_ci#include <linux/dio.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/io.h> 2062306a36Sopenharmony_ci#include <linux/uaccess.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic struct fb_info fb_info = { 2362306a36Sopenharmony_ci .fix = { 2462306a36Sopenharmony_ci .id = "HP300 ", 2562306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 2662306a36Sopenharmony_ci .visual = FB_VISUAL_PSEUDOCOLOR, 2762306a36Sopenharmony_ci .accel = FB_ACCEL_NONE, 2862306a36Sopenharmony_ci } 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic unsigned long fb_regs; 3262306a36Sopenharmony_cistatic unsigned char fb_bitmask; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define TC_NBLANK 0x4080 3562306a36Sopenharmony_ci#define TC_WEN 0x4088 3662306a36Sopenharmony_ci#define TC_REN 0x408c 3762306a36Sopenharmony_ci#define TC_FBEN 0x4090 3862306a36Sopenharmony_ci#define TC_PRR 0x40ea 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* These defines match the X window system */ 4162306a36Sopenharmony_ci#define RR_CLEAR 0x0 4262306a36Sopenharmony_ci#define RR_COPY 0x3 4362306a36Sopenharmony_ci#define RR_NOOP 0x5 4462306a36Sopenharmony_ci#define RR_XOR 0x6 4562306a36Sopenharmony_ci#define RR_INVERT 0xa 4662306a36Sopenharmony_ci#define RR_COPYINVERTED 0xc 4762306a36Sopenharmony_ci#define RR_SET 0xf 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* blitter regs */ 5062306a36Sopenharmony_ci#define BUSY 0x4044 5162306a36Sopenharmony_ci#define WMRR 0x40ef 5262306a36Sopenharmony_ci#define SOURCE_X 0x40f2 5362306a36Sopenharmony_ci#define SOURCE_Y 0x40f6 5462306a36Sopenharmony_ci#define DEST_X 0x40fa 5562306a36Sopenharmony_ci#define DEST_Y 0x40fe 5662306a36Sopenharmony_ci#define WHEIGHT 0x4106 5762306a36Sopenharmony_ci#define WWIDTH 0x4102 5862306a36Sopenharmony_ci#define WMOVE 0x409c 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic struct fb_var_screeninfo hpfb_defined = { 6162306a36Sopenharmony_ci .red = { 6262306a36Sopenharmony_ci .length = 8, 6362306a36Sopenharmony_ci }, 6462306a36Sopenharmony_ci .green = { 6562306a36Sopenharmony_ci .length = 8, 6662306a36Sopenharmony_ci }, 6762306a36Sopenharmony_ci .blue = { 6862306a36Sopenharmony_ci .length = 8, 6962306a36Sopenharmony_ci }, 7062306a36Sopenharmony_ci .activate = FB_ACTIVATE_NOW, 7162306a36Sopenharmony_ci .height = -1, 7262306a36Sopenharmony_ci .width = -1, 7362306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int hpfb_setcolreg(unsigned regno, unsigned red, unsigned green, 7762306a36Sopenharmony_ci unsigned blue, unsigned transp, 7862306a36Sopenharmony_ci struct fb_info *info) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci /* use MSBs */ 8162306a36Sopenharmony_ci unsigned char _red =red>>8; 8262306a36Sopenharmony_ci unsigned char _green=green>>8; 8362306a36Sopenharmony_ci unsigned char _blue =blue>>8; 8462306a36Sopenharmony_ci unsigned char _regno=regno; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * Set a single color register. The values supplied are 8862306a36Sopenharmony_ci * already rounded down to the hardware's capabilities 8962306a36Sopenharmony_ci * (according to the entries in the `var' structure). Return 9062306a36Sopenharmony_ci * != 0 for invalid regno. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (regno >= info->cmap.len) 9462306a36Sopenharmony_ci return 1; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci while (in_be16(fb_regs + 0x6002) & 0x4) udelay(1); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci out_be16(fb_regs + 0x60ba, 0xff); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci out_be16(fb_regs + 0x60b2, _red); 10162306a36Sopenharmony_ci out_be16(fb_regs + 0x60b4, _green); 10262306a36Sopenharmony_ci out_be16(fb_regs + 0x60b6, _blue); 10362306a36Sopenharmony_ci out_be16(fb_regs + 0x60b8, ~_regno); 10462306a36Sopenharmony_ci out_be16(fb_regs + 0x60f0, 0xff); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci udelay(100); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci while (in_be16(fb_regs + 0x6002) & 0x4) udelay(1); 10962306a36Sopenharmony_ci out_be16(fb_regs + 0x60b2, 0); 11062306a36Sopenharmony_ci out_be16(fb_regs + 0x60b4, 0); 11162306a36Sopenharmony_ci out_be16(fb_regs + 0x60b6, 0); 11262306a36Sopenharmony_ci out_be16(fb_regs + 0x60b8, 0); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int hpfb_blank(int blank, struct fb_info *info) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci out_8(fb_regs + TC_NBLANK, (blank ? 0x00 : fb_bitmask)); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void topcat_blit(int x0, int y0, int x1, int y1, int w, int h, int rr) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci if (rr >= 0) { 12962306a36Sopenharmony_ci while (in_8(fb_regs + BUSY) & fb_bitmask) 13062306a36Sopenharmony_ci ; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci out_8(fb_regs + TC_FBEN, fb_bitmask); 13362306a36Sopenharmony_ci if (rr >= 0) { 13462306a36Sopenharmony_ci out_8(fb_regs + TC_WEN, fb_bitmask); 13562306a36Sopenharmony_ci out_8(fb_regs + WMRR, rr); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci out_be16(fb_regs + SOURCE_X, x0); 13862306a36Sopenharmony_ci out_be16(fb_regs + SOURCE_Y, y0); 13962306a36Sopenharmony_ci out_be16(fb_regs + DEST_X, x1); 14062306a36Sopenharmony_ci out_be16(fb_regs + DEST_Y, y1); 14162306a36Sopenharmony_ci out_be16(fb_regs + WWIDTH, w); 14262306a36Sopenharmony_ci out_be16(fb_regs + WHEIGHT, h); 14362306a36Sopenharmony_ci out_8(fb_regs + WMOVE, fb_bitmask); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void hpfb_copyarea(struct fb_info *info, const struct fb_copyarea *area) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci topcat_blit(area->sx, area->sy, area->dx, area->dy, area->width, area->height, RR_COPY); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void hpfb_fillrect(struct fb_info *p, const struct fb_fillrect *region) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci u8 clr; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci clr = region->color & 0xff; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci while (in_8(fb_regs + BUSY) & fb_bitmask) 15862306a36Sopenharmony_ci ; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Foreground */ 16162306a36Sopenharmony_ci out_8(fb_regs + TC_WEN, fb_bitmask & clr); 16262306a36Sopenharmony_ci out_8(fb_regs + WMRR, (region->rop == ROP_COPY ? RR_SET : RR_INVERT)); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* Background */ 16562306a36Sopenharmony_ci out_8(fb_regs + TC_WEN, fb_bitmask & ~clr); 16662306a36Sopenharmony_ci out_8(fb_regs + WMRR, (region->rop == ROP_COPY ? RR_CLEAR : RR_NOOP)); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci topcat_blit(region->dx, region->dy, region->dx, region->dy, region->width, region->height, -1); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int hpfb_sync(struct fb_info *info) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * Since we also access the framebuffer directly, we have to wait 17562306a36Sopenharmony_ci * until the block mover is finished 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci while (in_8(fb_regs + BUSY) & fb_bitmask) 17862306a36Sopenharmony_ci ; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci out_8(fb_regs + TC_WEN, fb_bitmask); 18162306a36Sopenharmony_ci out_8(fb_regs + TC_PRR, RR_COPY); 18262306a36Sopenharmony_ci out_8(fb_regs + TC_FBEN, fb_bitmask); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic const struct fb_ops hpfb_ops = { 18862306a36Sopenharmony_ci .owner = THIS_MODULE, 18962306a36Sopenharmony_ci .fb_setcolreg = hpfb_setcolreg, 19062306a36Sopenharmony_ci .fb_blank = hpfb_blank, 19162306a36Sopenharmony_ci .fb_fillrect = hpfb_fillrect, 19262306a36Sopenharmony_ci .fb_copyarea = hpfb_copyarea, 19362306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 19462306a36Sopenharmony_ci .fb_sync = hpfb_sync, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* Common to all HP framebuffers */ 19862306a36Sopenharmony_ci#define HPFB_FBWMSB 0x05 /* Frame buffer width */ 19962306a36Sopenharmony_ci#define HPFB_FBWLSB 0x07 20062306a36Sopenharmony_ci#define HPFB_FBHMSB 0x09 /* Frame buffer height */ 20162306a36Sopenharmony_ci#define HPFB_FBHLSB 0x0b 20262306a36Sopenharmony_ci#define HPFB_DWMSB 0x0d /* Display width */ 20362306a36Sopenharmony_ci#define HPFB_DWLSB 0x0f 20462306a36Sopenharmony_ci#define HPFB_DHMSB 0x11 /* Display height */ 20562306a36Sopenharmony_ci#define HPFB_DHLSB 0x13 20662306a36Sopenharmony_ci#define HPFB_NUMPLANES 0x5b /* Number of colour planes */ 20762306a36Sopenharmony_ci#define HPFB_FBOMSB 0x5d /* Frame buffer offset */ 20862306a36Sopenharmony_ci#define HPFB_FBOLSB 0x5f 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int hpfb_init_one(unsigned long phys_base, unsigned long virt_base) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci unsigned long fboff, fb_width, fb_height, fb_start; 21362306a36Sopenharmony_ci int ret; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci fb_regs = virt_base; 21662306a36Sopenharmony_ci fboff = (in_8(fb_regs + HPFB_FBOMSB) << 8) | in_8(fb_regs + HPFB_FBOLSB); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci fb_info.fix.smem_start = (in_8(fb_regs + fboff) << 16); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (phys_base >= DIOII_BASE) { 22162306a36Sopenharmony_ci fb_info.fix.smem_start += phys_base; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (DIO_SECID(fb_regs) != DIO_ID2_TOPCAT) { 22562306a36Sopenharmony_ci /* This is the magic incantation the HP X server uses to make Catseye boards work. */ 22662306a36Sopenharmony_ci while (in_be16(fb_regs+0x4800) & 1) 22762306a36Sopenharmony_ci ; 22862306a36Sopenharmony_ci out_be16(fb_regs+0x4800, 0); /* Catseye status */ 22962306a36Sopenharmony_ci out_be16(fb_regs+0x4510, 0); /* VB */ 23062306a36Sopenharmony_ci out_be16(fb_regs+0x4512, 0); /* TCNTRL */ 23162306a36Sopenharmony_ci out_be16(fb_regs+0x4514, 0); /* ACNTRL */ 23262306a36Sopenharmony_ci out_be16(fb_regs+0x4516, 0); /* PNCNTRL */ 23362306a36Sopenharmony_ci out_be16(fb_regs+0x4206, 0x90); /* RUG Command/Status */ 23462306a36Sopenharmony_ci out_be16(fb_regs+0x60a2, 0); /* Overlay Mask */ 23562306a36Sopenharmony_ci out_be16(fb_regs+0x60bc, 0); /* Ram Select */ 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* 23962306a36Sopenharmony_ci * Fill in the available video resolution 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci fb_width = (in_8(fb_regs + HPFB_FBWMSB) << 8) | in_8(fb_regs + HPFB_FBWLSB); 24262306a36Sopenharmony_ci fb_info.fix.line_length = fb_width; 24362306a36Sopenharmony_ci fb_height = (in_8(fb_regs + HPFB_FBHMSB) << 8) | in_8(fb_regs + HPFB_FBHLSB); 24462306a36Sopenharmony_ci fb_info.fix.smem_len = fb_width * fb_height; 24562306a36Sopenharmony_ci fb_start = (unsigned long)ioremap_wt(fb_info.fix.smem_start, 24662306a36Sopenharmony_ci fb_info.fix.smem_len); 24762306a36Sopenharmony_ci hpfb_defined.xres = (in_8(fb_regs + HPFB_DWMSB) << 8) | in_8(fb_regs + HPFB_DWLSB); 24862306a36Sopenharmony_ci hpfb_defined.yres = (in_8(fb_regs + HPFB_DHMSB) << 8) | in_8(fb_regs + HPFB_DHLSB); 24962306a36Sopenharmony_ci hpfb_defined.xres_virtual = hpfb_defined.xres; 25062306a36Sopenharmony_ci hpfb_defined.yres_virtual = hpfb_defined.yres; 25162306a36Sopenharmony_ci hpfb_defined.bits_per_pixel = in_8(fb_regs + HPFB_NUMPLANES); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci printk(KERN_INFO "hpfb: framebuffer at 0x%lx, mapped to 0x%lx, size %dk\n", 25462306a36Sopenharmony_ci fb_info.fix.smem_start, fb_start, fb_info.fix.smem_len/1024); 25562306a36Sopenharmony_ci printk(KERN_INFO "hpfb: mode is %dx%dx%d, linelength=%d\n", 25662306a36Sopenharmony_ci hpfb_defined.xres, hpfb_defined.yres, hpfb_defined.bits_per_pixel, fb_info.fix.line_length); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * Give the hardware a bit of a prod and work out how many bits per 26062306a36Sopenharmony_ci * pixel are supported. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci out_8(fb_regs + TC_WEN, 0xff); 26362306a36Sopenharmony_ci out_8(fb_regs + TC_PRR, RR_COPY); 26462306a36Sopenharmony_ci out_8(fb_regs + TC_FBEN, 0xff); 26562306a36Sopenharmony_ci out_8(fb_start, 0xff); 26662306a36Sopenharmony_ci fb_bitmask = in_8(fb_start); 26762306a36Sopenharmony_ci out_8(fb_start, 0); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* 27062306a36Sopenharmony_ci * Enable reading/writing of all the planes. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci out_8(fb_regs + TC_WEN, fb_bitmask); 27362306a36Sopenharmony_ci out_8(fb_regs + TC_PRR, RR_COPY); 27462306a36Sopenharmony_ci out_8(fb_regs + TC_REN, fb_bitmask); 27562306a36Sopenharmony_ci out_8(fb_regs + TC_FBEN, fb_bitmask); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* 27862306a36Sopenharmony_ci * Clear the screen. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci topcat_blit(0, 0, 0, 0, fb_width, fb_height, RR_CLEAR); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * Let there be consoles.. 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci if (DIO_SECID(fb_regs) == DIO_ID2_TOPCAT) 28662306a36Sopenharmony_ci strcat(fb_info.fix.id, "Topcat"); 28762306a36Sopenharmony_ci else 28862306a36Sopenharmony_ci strcat(fb_info.fix.id, "Catseye"); 28962306a36Sopenharmony_ci fb_info.fbops = &hpfb_ops; 29062306a36Sopenharmony_ci fb_info.var = hpfb_defined; 29162306a36Sopenharmony_ci fb_info.screen_base = (char *)fb_start; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci ret = fb_alloc_cmap(&fb_info.cmap, 1 << hpfb_defined.bits_per_pixel, 0); 29462306a36Sopenharmony_ci if (ret < 0) 29562306a36Sopenharmony_ci goto unmap_screen_base; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ret = register_framebuffer(&fb_info); 29862306a36Sopenharmony_ci if (ret < 0) 29962306a36Sopenharmony_ci goto dealloc_cmap; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci fb_info(&fb_info, "%s frame buffer device\n", fb_info.fix.id); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cidealloc_cmap: 30662306a36Sopenharmony_ci fb_dealloc_cmap(&fb_info.cmap); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ciunmap_screen_base: 30962306a36Sopenharmony_ci if (fb_info.screen_base) { 31062306a36Sopenharmony_ci iounmap(fb_info.screen_base); 31162306a36Sopenharmony_ci fb_info.screen_base = NULL; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return ret; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/* 31862306a36Sopenharmony_ci * Check that the secondary ID indicates that we have some hope of working with this 31962306a36Sopenharmony_ci * framebuffer. The catseye boards are pretty much like topcats and we can muddle through. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci#define topcat_sid_ok(x) (((x) == DIO_ID2_LRCATSEYE) || ((x) == DIO_ID2_HRCCATSEYE) \ 32362306a36Sopenharmony_ci || ((x) == DIO_ID2_HRMCATSEYE) || ((x) == DIO_ID2_TOPCAT)) 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* 32662306a36Sopenharmony_ci * Initialise the framebuffer 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_cistatic int hpfb_dio_probe(struct dio_dev *d, const struct dio_device_id *ent) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci unsigned long paddr, vaddr; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci paddr = d->resource.start; 33362306a36Sopenharmony_ci if (!request_mem_region(d->resource.start, resource_size(&d->resource), d->name)) 33462306a36Sopenharmony_ci return -EBUSY; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (d->scode >= DIOII_SCBASE) { 33762306a36Sopenharmony_ci vaddr = (unsigned long)ioremap(paddr, resource_size(&d->resource)); 33862306a36Sopenharmony_ci } else { 33962306a36Sopenharmony_ci vaddr = paddr + DIO_VIRADDRBASE; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci printk(KERN_INFO "Topcat found at DIO select code %d " 34262306a36Sopenharmony_ci "(secondary id %02x)\n", d->scode, (d->id >> 8) & 0xff); 34362306a36Sopenharmony_ci if (hpfb_init_one(paddr, vaddr)) { 34462306a36Sopenharmony_ci if (d->scode >= DIOII_SCBASE) 34562306a36Sopenharmony_ci iounmap((void *)vaddr); 34662306a36Sopenharmony_ci return -ENOMEM; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void hpfb_remove_one(struct dio_dev *d) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci unregister_framebuffer(&fb_info); 35462306a36Sopenharmony_ci if (d->scode >= DIOII_SCBASE) 35562306a36Sopenharmony_ci iounmap((void *)fb_regs); 35662306a36Sopenharmony_ci release_mem_region(d->resource.start, resource_size(&d->resource)); 35762306a36Sopenharmony_ci fb_dealloc_cmap(&fb_info.cmap); 35862306a36Sopenharmony_ci if (fb_info.screen_base) 35962306a36Sopenharmony_ci iounmap(fb_info.screen_base); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic struct dio_device_id hpfb_dio_tbl[] = { 36362306a36Sopenharmony_ci { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_LRCATSEYE) }, 36462306a36Sopenharmony_ci { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_HRCCATSEYE) }, 36562306a36Sopenharmony_ci { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_HRMCATSEYE) }, 36662306a36Sopenharmony_ci { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_TOPCAT) }, 36762306a36Sopenharmony_ci { 0 } 36862306a36Sopenharmony_ci}; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic struct dio_driver hpfb_driver = { 37162306a36Sopenharmony_ci .name = "hpfb", 37262306a36Sopenharmony_ci .id_table = hpfb_dio_tbl, 37362306a36Sopenharmony_ci .probe = hpfb_dio_probe, 37462306a36Sopenharmony_ci .remove = hpfb_remove_one, 37562306a36Sopenharmony_ci}; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int __init hpfb_init(void) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci unsigned int sid; 38062306a36Sopenharmony_ci unsigned char i; 38162306a36Sopenharmony_ci int err; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* Topcats can be on the internal IO bus or real DIO devices. 38462306a36Sopenharmony_ci * The internal variant sits at 0x560000; it has primary 38562306a36Sopenharmony_ci * and secondary ID registers just like the DIO version. 38662306a36Sopenharmony_ci * So we merge the two detection routines. 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * Perhaps this #define should be in a global header file: 38962306a36Sopenharmony_ci * I believe it's common to all internal fbs, not just topcat. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ci#define INTFBVADDR 0xf0560000 39262306a36Sopenharmony_ci#define INTFBPADDR 0x560000 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!MACH_IS_HP300) 39562306a36Sopenharmony_ci return -ENODEV; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (fb_get_options("hpfb", NULL)) 39862306a36Sopenharmony_ci return -ENODEV; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci err = dio_register_driver(&hpfb_driver); 40162306a36Sopenharmony_ci if (err) 40262306a36Sopenharmony_ci return err; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci err = copy_from_kernel_nofault(&i, (unsigned char *)INTFBVADDR + DIO_IDOFF, 1); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (!err && (i == DIO_ID_FBUFFER) && topcat_sid_ok(sid = DIO_SECID(INTFBVADDR))) { 40762306a36Sopenharmony_ci if (!request_mem_region(INTFBPADDR, DIO_DEVSIZE, "Internal Topcat")) 40862306a36Sopenharmony_ci return -EBUSY; 40962306a36Sopenharmony_ci printk(KERN_INFO "Internal Topcat found (secondary id %02x)\n", sid); 41062306a36Sopenharmony_ci if (hpfb_init_one(INTFBPADDR, INTFBVADDR)) { 41162306a36Sopenharmony_ci return -ENOMEM; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic void __exit hpfb_cleanup_module(void) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci dio_unregister_driver(&hpfb_driver); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cimodule_init(hpfb_init); 42362306a36Sopenharmony_cimodule_exit(hpfb_cleanup_module); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 426