162306a36Sopenharmony_ci/* sunxvr500.c: Sun 3DLABS XVR-500 Expert3D fb driver for sparc64 systems 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * License: GPL 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 David S. Miller (davem@davemloft.net) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/aperture.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/fb.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <asm/io.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* XXX This device has a 'dev-comm' property which apparently is 1862306a36Sopenharmony_ci * XXX a pointer into the openfirmware's address space which is 1962306a36Sopenharmony_ci * XXX a shared area the kernel driver can use to keep OBP 2062306a36Sopenharmony_ci * XXX informed about the current resolution setting. The idea 2162306a36Sopenharmony_ci * XXX is that the kernel can change resolutions, and as long 2262306a36Sopenharmony_ci * XXX as the values in the 'dev-comm' area are accurate then 2362306a36Sopenharmony_ci * XXX OBP can still render text properly to the console. 2462306a36Sopenharmony_ci * XXX 2562306a36Sopenharmony_ci * XXX I'm still working out the layout of this and whether there 2662306a36Sopenharmony_ci * XXX are any signatures we need to look for etc. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistruct e3d_info { 2962306a36Sopenharmony_ci struct fb_info *info; 3062306a36Sopenharmony_ci struct pci_dev *pdev; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci spinlock_t lock; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci char __iomem *fb_base; 3562306a36Sopenharmony_ci unsigned long fb_base_phys; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci unsigned long fb8_buf_diff; 3862306a36Sopenharmony_ci unsigned long regs_base_phys; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci void __iomem *ramdac; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci struct device_node *of_node; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci unsigned int width; 4562306a36Sopenharmony_ci unsigned int height; 4662306a36Sopenharmony_ci unsigned int depth; 4762306a36Sopenharmony_ci unsigned int fb_size; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci u32 fb_base_reg; 5062306a36Sopenharmony_ci u32 fb8_0_off; 5162306a36Sopenharmony_ci u32 fb8_1_off; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci u32 pseudo_palette[16]; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int e3d_get_props(struct e3d_info *ep) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci ep->width = of_getintprop_default(ep->of_node, "width", 0); 5962306a36Sopenharmony_ci ep->height = of_getintprop_default(ep->of_node, "height", 0); 6062306a36Sopenharmony_ci ep->depth = of_getintprop_default(ep->of_node, "depth", 8); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (!ep->width || !ep->height) { 6362306a36Sopenharmony_ci printk(KERN_ERR "e3d: Critical properties missing for %s\n", 6462306a36Sopenharmony_ci pci_name(ep->pdev)); 6562306a36Sopenharmony_ci return -EINVAL; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* My XVR-500 comes up, at 1280x768 and a FB base register value of 7262306a36Sopenharmony_ci * 0x04000000, the following video layout register values: 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * RAMDAC_VID_WH 0x03ff04ff 7562306a36Sopenharmony_ci * RAMDAC_VID_CFG 0x1a0b0088 7662306a36Sopenharmony_ci * RAMDAC_VID_32FB_0 0x04000000 7762306a36Sopenharmony_ci * RAMDAC_VID_32FB_1 0x04800000 7862306a36Sopenharmony_ci * RAMDAC_VID_8FB_0 0x05000000 7962306a36Sopenharmony_ci * RAMDAC_VID_8FB_1 0x05200000 8062306a36Sopenharmony_ci * RAMDAC_VID_XXXFB 0x05400000 8162306a36Sopenharmony_ci * RAMDAC_VID_YYYFB 0x05c00000 8262306a36Sopenharmony_ci * RAMDAC_VID_ZZZFB 0x05e00000 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci/* Video layout registers */ 8562306a36Sopenharmony_ci#define RAMDAC_VID_WH 0x00000070UL /* (height-1)<<16 | (width-1) */ 8662306a36Sopenharmony_ci#define RAMDAC_VID_CFG 0x00000074UL /* 0x1a000088|(linesz_log2<<16) */ 8762306a36Sopenharmony_ci#define RAMDAC_VID_32FB_0 0x00000078UL /* PCI base 32bpp FB buffer 0 */ 8862306a36Sopenharmony_ci#define RAMDAC_VID_32FB_1 0x0000007cUL /* PCI base 32bpp FB buffer 1 */ 8962306a36Sopenharmony_ci#define RAMDAC_VID_8FB_0 0x00000080UL /* PCI base 8bpp FB buffer 0 */ 9062306a36Sopenharmony_ci#define RAMDAC_VID_8FB_1 0x00000084UL /* PCI base 8bpp FB buffer 1 */ 9162306a36Sopenharmony_ci#define RAMDAC_VID_XXXFB 0x00000088UL /* PCI base of XXX FB */ 9262306a36Sopenharmony_ci#define RAMDAC_VID_YYYFB 0x0000008cUL /* PCI base of YYY FB */ 9362306a36Sopenharmony_ci#define RAMDAC_VID_ZZZFB 0x00000090UL /* PCI base of ZZZ FB */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* CLUT registers */ 9662306a36Sopenharmony_ci#define RAMDAC_INDEX 0x000000bcUL 9762306a36Sopenharmony_ci#define RAMDAC_DATA 0x000000c0UL 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void e3d_clut_write(struct e3d_info *ep, int index, u32 val) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci void __iomem *ramdac = ep->ramdac; 10262306a36Sopenharmony_ci unsigned long flags; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci spin_lock_irqsave(&ep->lock, flags); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci writel(index, ramdac + RAMDAC_INDEX); 10762306a36Sopenharmony_ci writel(val, ramdac + RAMDAC_DATA); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci spin_unlock_irqrestore(&ep->lock, flags); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int e3d_setcolreg(unsigned regno, 11362306a36Sopenharmony_ci unsigned red, unsigned green, unsigned blue, 11462306a36Sopenharmony_ci unsigned transp, struct fb_info *info) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct e3d_info *ep = info->par; 11762306a36Sopenharmony_ci u32 red_8, green_8, blue_8; 11862306a36Sopenharmony_ci u32 red_10, green_10, blue_10; 11962306a36Sopenharmony_ci u32 value; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (regno >= 256) 12262306a36Sopenharmony_ci return 1; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci red_8 = red >> 8; 12562306a36Sopenharmony_ci green_8 = green >> 8; 12662306a36Sopenharmony_ci blue_8 = blue >> 8; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci value = (blue_8 << 24) | (green_8 << 16) | (red_8 << 8); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) 13162306a36Sopenharmony_ci ((u32 *)info->pseudo_palette)[regno] = value; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci red_10 = red >> 6; 13562306a36Sopenharmony_ci green_10 = green >> 6; 13662306a36Sopenharmony_ci blue_10 = blue >> 6; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci value = (blue_10 << 20) | (green_10 << 10) | (red_10 << 0); 13962306a36Sopenharmony_ci e3d_clut_write(ep, regno, value); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* XXX This is a bit of a hack. I can't figure out exactly how the 14562306a36Sopenharmony_ci * XXX two 8bpp areas of the framebuffer work. I imagine there is 14662306a36Sopenharmony_ci * XXX a WID attribute somewhere else in the framebuffer which tells 14762306a36Sopenharmony_ci * XXX the ramdac which of the two 8bpp framebuffer regions to take 14862306a36Sopenharmony_ci * XXX the pixel from. So, for now, render into both regions to make 14962306a36Sopenharmony_ci * XXX sure the pixel shows up. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic void e3d_imageblit(struct fb_info *info, const struct fb_image *image) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct e3d_info *ep = info->par; 15462306a36Sopenharmony_ci unsigned long flags; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci spin_lock_irqsave(&ep->lock, flags); 15762306a36Sopenharmony_ci cfb_imageblit(info, image); 15862306a36Sopenharmony_ci info->screen_base += ep->fb8_buf_diff; 15962306a36Sopenharmony_ci cfb_imageblit(info, image); 16062306a36Sopenharmony_ci info->screen_base -= ep->fb8_buf_diff; 16162306a36Sopenharmony_ci spin_unlock_irqrestore(&ep->lock, flags); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void e3d_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct e3d_info *ep = info->par; 16762306a36Sopenharmony_ci unsigned long flags; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci spin_lock_irqsave(&ep->lock, flags); 17062306a36Sopenharmony_ci cfb_fillrect(info, rect); 17162306a36Sopenharmony_ci info->screen_base += ep->fb8_buf_diff; 17262306a36Sopenharmony_ci cfb_fillrect(info, rect); 17362306a36Sopenharmony_ci info->screen_base -= ep->fb8_buf_diff; 17462306a36Sopenharmony_ci spin_unlock_irqrestore(&ep->lock, flags); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void e3d_copyarea(struct fb_info *info, const struct fb_copyarea *area) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct e3d_info *ep = info->par; 18062306a36Sopenharmony_ci unsigned long flags; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci spin_lock_irqsave(&ep->lock, flags); 18362306a36Sopenharmony_ci cfb_copyarea(info, area); 18462306a36Sopenharmony_ci info->screen_base += ep->fb8_buf_diff; 18562306a36Sopenharmony_ci cfb_copyarea(info, area); 18662306a36Sopenharmony_ci info->screen_base -= ep->fb8_buf_diff; 18762306a36Sopenharmony_ci spin_unlock_irqrestore(&ep->lock, flags); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic const struct fb_ops e3d_ops = { 19162306a36Sopenharmony_ci .owner = THIS_MODULE, 19262306a36Sopenharmony_ci .fb_setcolreg = e3d_setcolreg, 19362306a36Sopenharmony_ci .fb_fillrect = e3d_fillrect, 19462306a36Sopenharmony_ci .fb_copyarea = e3d_copyarea, 19562306a36Sopenharmony_ci .fb_imageblit = e3d_imageblit, 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int e3d_set_fbinfo(struct e3d_info *ep) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct fb_info *info = ep->info; 20162306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci info->fbops = &e3d_ops; 20462306a36Sopenharmony_ci info->screen_base = ep->fb_base; 20562306a36Sopenharmony_ci info->screen_size = ep->fb_size; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci info->pseudo_palette = ep->pseudo_palette; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Fill fix common fields */ 21062306a36Sopenharmony_ci strscpy(info->fix.id, "e3d", sizeof(info->fix.id)); 21162306a36Sopenharmony_ci info->fix.smem_start = ep->fb_base_phys; 21262306a36Sopenharmony_ci info->fix.smem_len = ep->fb_size; 21362306a36Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 21462306a36Sopenharmony_ci if (ep->depth == 32 || ep->depth == 24) 21562306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 21662306a36Sopenharmony_ci else 21762306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci var->xres = ep->width; 22062306a36Sopenharmony_ci var->yres = ep->height; 22162306a36Sopenharmony_ci var->xres_virtual = var->xres; 22262306a36Sopenharmony_ci var->yres_virtual = var->yres; 22362306a36Sopenharmony_ci var->bits_per_pixel = ep->depth; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci var->red.offset = 8; 22662306a36Sopenharmony_ci var->red.length = 8; 22762306a36Sopenharmony_ci var->green.offset = 16; 22862306a36Sopenharmony_ci var->green.length = 8; 22962306a36Sopenharmony_ci var->blue.offset = 24; 23062306a36Sopenharmony_ci var->blue.length = 8; 23162306a36Sopenharmony_ci var->transp.offset = 0; 23262306a36Sopenharmony_ci var->transp.length = 0; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (fb_alloc_cmap(&info->cmap, 256, 0)) { 23562306a36Sopenharmony_ci printk(KERN_ERR "e3d: Cannot allocate color map.\n"); 23662306a36Sopenharmony_ci return -ENOMEM; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic int e3d_pci_register(struct pci_dev *pdev, 24362306a36Sopenharmony_ci const struct pci_device_id *ent) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct device_node *of_node; 24662306a36Sopenharmony_ci const char *device_type; 24762306a36Sopenharmony_ci struct fb_info *info; 24862306a36Sopenharmony_ci struct e3d_info *ep; 24962306a36Sopenharmony_ci unsigned int line_length; 25062306a36Sopenharmony_ci int err; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci err = aperture_remove_conflicting_pci_devices(pdev, "e3dfb"); 25362306a36Sopenharmony_ci if (err) 25462306a36Sopenharmony_ci return err; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci of_node = pci_device_to_OF_node(pdev); 25762306a36Sopenharmony_ci if (!of_node) { 25862306a36Sopenharmony_ci printk(KERN_ERR "e3d: Cannot find OF node of %s\n", 25962306a36Sopenharmony_ci pci_name(pdev)); 26062306a36Sopenharmony_ci return -ENODEV; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci device_type = of_get_property(of_node, "device_type", NULL); 26462306a36Sopenharmony_ci if (!device_type) { 26562306a36Sopenharmony_ci printk(KERN_INFO "e3d: Ignoring secondary output device " 26662306a36Sopenharmony_ci "at %s\n", pci_name(pdev)); 26762306a36Sopenharmony_ci return -ENODEV; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci err = pci_enable_device(pdev); 27162306a36Sopenharmony_ci if (err < 0) { 27262306a36Sopenharmony_ci printk(KERN_ERR "e3d: Cannot enable PCI device %s\n", 27362306a36Sopenharmony_ci pci_name(pdev)); 27462306a36Sopenharmony_ci goto err_out; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct e3d_info), &pdev->dev); 27862306a36Sopenharmony_ci if (!info) { 27962306a36Sopenharmony_ci err = -ENOMEM; 28062306a36Sopenharmony_ci goto err_disable; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci ep = info->par; 28462306a36Sopenharmony_ci ep->info = info; 28562306a36Sopenharmony_ci ep->pdev = pdev; 28662306a36Sopenharmony_ci spin_lock_init(&ep->lock); 28762306a36Sopenharmony_ci ep->of_node = of_node; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Read the PCI base register of the frame buffer, which we 29062306a36Sopenharmony_ci * need in order to interpret the RAMDAC_VID_*FB* values in 29162306a36Sopenharmony_ci * the ramdac correctly. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, 29462306a36Sopenharmony_ci &ep->fb_base_reg); 29562306a36Sopenharmony_ci ep->fb_base_reg &= PCI_BASE_ADDRESS_MEM_MASK; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ep->regs_base_phys = pci_resource_start (pdev, 1); 29862306a36Sopenharmony_ci err = pci_request_region(pdev, 1, "e3d regs"); 29962306a36Sopenharmony_ci if (err < 0) { 30062306a36Sopenharmony_ci printk("e3d: Cannot request region 1 for %s\n", 30162306a36Sopenharmony_ci pci_name(pdev)); 30262306a36Sopenharmony_ci goto err_release_fb; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci ep->ramdac = ioremap(ep->regs_base_phys + 0x8000, 0x1000); 30562306a36Sopenharmony_ci if (!ep->ramdac) { 30662306a36Sopenharmony_ci err = -ENOMEM; 30762306a36Sopenharmony_ci goto err_release_pci1; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ep->fb8_0_off = readl(ep->ramdac + RAMDAC_VID_8FB_0); 31162306a36Sopenharmony_ci ep->fb8_0_off -= ep->fb_base_reg; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci ep->fb8_1_off = readl(ep->ramdac + RAMDAC_VID_8FB_1); 31462306a36Sopenharmony_ci ep->fb8_1_off -= ep->fb_base_reg; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ep->fb8_buf_diff = ep->fb8_1_off - ep->fb8_0_off; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ep->fb_base_phys = pci_resource_start (pdev, 0); 31962306a36Sopenharmony_ci ep->fb_base_phys += ep->fb8_0_off; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci err = pci_request_region(pdev, 0, "e3d framebuffer"); 32262306a36Sopenharmony_ci if (err < 0) { 32362306a36Sopenharmony_ci printk("e3d: Cannot request region 0 for %s\n", 32462306a36Sopenharmony_ci pci_name(pdev)); 32562306a36Sopenharmony_ci goto err_unmap_ramdac; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci err = e3d_get_props(ep); 32962306a36Sopenharmony_ci if (err) 33062306a36Sopenharmony_ci goto err_release_pci0; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci line_length = (readl(ep->ramdac + RAMDAC_VID_CFG) >> 16) & 0xff; 33362306a36Sopenharmony_ci line_length = 1 << line_length; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci switch (ep->depth) { 33662306a36Sopenharmony_ci case 8: 33762306a36Sopenharmony_ci info->fix.line_length = line_length; 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci case 16: 34062306a36Sopenharmony_ci info->fix.line_length = line_length * 2; 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci case 24: 34362306a36Sopenharmony_ci info->fix.line_length = line_length * 3; 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci case 32: 34662306a36Sopenharmony_ci info->fix.line_length = line_length * 4; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci ep->fb_size = info->fix.line_length * ep->height; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci ep->fb_base = ioremap(ep->fb_base_phys, ep->fb_size); 35262306a36Sopenharmony_ci if (!ep->fb_base) { 35362306a36Sopenharmony_ci err = -ENOMEM; 35462306a36Sopenharmony_ci goto err_release_pci0; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci err = e3d_set_fbinfo(ep); 35862306a36Sopenharmony_ci if (err) 35962306a36Sopenharmony_ci goto err_unmap_fb; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci pci_set_drvdata(pdev, info); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci printk("e3d: Found device at %s\n", pci_name(pdev)); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci err = register_framebuffer(info); 36662306a36Sopenharmony_ci if (err < 0) { 36762306a36Sopenharmony_ci printk(KERN_ERR "e3d: Could not register framebuffer %s\n", 36862306a36Sopenharmony_ci pci_name(pdev)); 36962306a36Sopenharmony_ci goto err_free_cmap; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cierr_free_cmap: 37562306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cierr_unmap_fb: 37862306a36Sopenharmony_ci iounmap(ep->fb_base); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cierr_release_pci0: 38162306a36Sopenharmony_ci pci_release_region(pdev, 0); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cierr_unmap_ramdac: 38462306a36Sopenharmony_ci iounmap(ep->ramdac); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cierr_release_pci1: 38762306a36Sopenharmony_ci pci_release_region(pdev, 1); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cierr_release_fb: 39062306a36Sopenharmony_ci framebuffer_release(info); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cierr_disable: 39362306a36Sopenharmony_ci pci_disable_device(pdev); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cierr_out: 39662306a36Sopenharmony_ci return err; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic const struct pci_device_id e3d_pci_table[] = { 40062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x7a0), }, 40162306a36Sopenharmony_ci { PCI_DEVICE(0x1091, 0x7a0), }, 40262306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x7a2), }, 40362306a36Sopenharmony_ci { .vendor = PCI_VENDOR_ID_3DLABS, 40462306a36Sopenharmony_ci .device = PCI_ANY_ID, 40562306a36Sopenharmony_ci .subvendor = PCI_VENDOR_ID_3DLABS, 40662306a36Sopenharmony_ci .subdevice = 0x0108, 40762306a36Sopenharmony_ci }, 40862306a36Sopenharmony_ci { .vendor = PCI_VENDOR_ID_3DLABS, 40962306a36Sopenharmony_ci .device = PCI_ANY_ID, 41062306a36Sopenharmony_ci .subvendor = PCI_VENDOR_ID_3DLABS, 41162306a36Sopenharmony_ci .subdevice = 0x0140, 41262306a36Sopenharmony_ci }, 41362306a36Sopenharmony_ci { .vendor = PCI_VENDOR_ID_3DLABS, 41462306a36Sopenharmony_ci .device = PCI_ANY_ID, 41562306a36Sopenharmony_ci .subvendor = PCI_VENDOR_ID_3DLABS, 41662306a36Sopenharmony_ci .subdevice = 0x1024, 41762306a36Sopenharmony_ci }, 41862306a36Sopenharmony_ci { 0, } 41962306a36Sopenharmony_ci}; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic struct pci_driver e3d_driver = { 42262306a36Sopenharmony_ci .driver = { 42362306a36Sopenharmony_ci .suppress_bind_attrs = true, 42462306a36Sopenharmony_ci }, 42562306a36Sopenharmony_ci .name = "e3d", 42662306a36Sopenharmony_ci .id_table = e3d_pci_table, 42762306a36Sopenharmony_ci .probe = e3d_pci_register, 42862306a36Sopenharmony_ci}; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int __init e3d_init(void) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci if (fb_modesetting_disabled("e3d")) 43362306a36Sopenharmony_ci return -ENODEV; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (fb_get_options("e3d", NULL)) 43662306a36Sopenharmony_ci return -ENODEV; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return pci_register_driver(&e3d_driver); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_cidevice_initcall(e3d_init); 441