18c2ecf20Sopenharmony_ci/* sunxvr500.c: Sun 3DLABS XVR-500 Expert3D fb driver for sparc64 systems 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * License: GPL 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 David S. Miller (davem@davemloft.net) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/fb.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/io.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* XXX This device has a 'dev-comm' property which apparently is 178c2ecf20Sopenharmony_ci * XXX a pointer into the openfirmware's address space which is 188c2ecf20Sopenharmony_ci * XXX a shared area the kernel driver can use to keep OBP 198c2ecf20Sopenharmony_ci * XXX informed about the current resolution setting. The idea 208c2ecf20Sopenharmony_ci * XXX is that the kernel can change resolutions, and as long 218c2ecf20Sopenharmony_ci * XXX as the values in the 'dev-comm' area are accurate then 228c2ecf20Sopenharmony_ci * XXX OBP can still render text properly to the console. 238c2ecf20Sopenharmony_ci * XXX 248c2ecf20Sopenharmony_ci * XXX I'm still working out the layout of this and whether there 258c2ecf20Sopenharmony_ci * XXX are any signatures we need to look for etc. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_cistruct e3d_info { 288c2ecf20Sopenharmony_ci struct fb_info *info; 298c2ecf20Sopenharmony_ci struct pci_dev *pdev; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci spinlock_t lock; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci char __iomem *fb_base; 348c2ecf20Sopenharmony_ci unsigned long fb_base_phys; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci unsigned long fb8_buf_diff; 378c2ecf20Sopenharmony_ci unsigned long regs_base_phys; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci void __iomem *ramdac; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci struct device_node *of_node; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci unsigned int width; 448c2ecf20Sopenharmony_ci unsigned int height; 458c2ecf20Sopenharmony_ci unsigned int depth; 468c2ecf20Sopenharmony_ci unsigned int fb_size; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci u32 fb_base_reg; 498c2ecf20Sopenharmony_ci u32 fb8_0_off; 508c2ecf20Sopenharmony_ci u32 fb8_1_off; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci u32 pseudo_palette[16]; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int e3d_get_props(struct e3d_info *ep) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci ep->width = of_getintprop_default(ep->of_node, "width", 0); 588c2ecf20Sopenharmony_ci ep->height = of_getintprop_default(ep->of_node, "height", 0); 598c2ecf20Sopenharmony_ci ep->depth = of_getintprop_default(ep->of_node, "depth", 8); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (!ep->width || !ep->height) { 628c2ecf20Sopenharmony_ci printk(KERN_ERR "e3d: Critical properties missing for %s\n", 638c2ecf20Sopenharmony_ci pci_name(ep->pdev)); 648c2ecf20Sopenharmony_ci return -EINVAL; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* My XVR-500 comes up, at 1280x768 and a FB base register value of 718c2ecf20Sopenharmony_ci * 0x04000000, the following video layout register values: 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * RAMDAC_VID_WH 0x03ff04ff 748c2ecf20Sopenharmony_ci * RAMDAC_VID_CFG 0x1a0b0088 758c2ecf20Sopenharmony_ci * RAMDAC_VID_32FB_0 0x04000000 768c2ecf20Sopenharmony_ci * RAMDAC_VID_32FB_1 0x04800000 778c2ecf20Sopenharmony_ci * RAMDAC_VID_8FB_0 0x05000000 788c2ecf20Sopenharmony_ci * RAMDAC_VID_8FB_1 0x05200000 798c2ecf20Sopenharmony_ci * RAMDAC_VID_XXXFB 0x05400000 808c2ecf20Sopenharmony_ci * RAMDAC_VID_YYYFB 0x05c00000 818c2ecf20Sopenharmony_ci * RAMDAC_VID_ZZZFB 0x05e00000 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci/* Video layout registers */ 848c2ecf20Sopenharmony_ci#define RAMDAC_VID_WH 0x00000070UL /* (height-1)<<16 | (width-1) */ 858c2ecf20Sopenharmony_ci#define RAMDAC_VID_CFG 0x00000074UL /* 0x1a000088|(linesz_log2<<16) */ 868c2ecf20Sopenharmony_ci#define RAMDAC_VID_32FB_0 0x00000078UL /* PCI base 32bpp FB buffer 0 */ 878c2ecf20Sopenharmony_ci#define RAMDAC_VID_32FB_1 0x0000007cUL /* PCI base 32bpp FB buffer 1 */ 888c2ecf20Sopenharmony_ci#define RAMDAC_VID_8FB_0 0x00000080UL /* PCI base 8bpp FB buffer 0 */ 898c2ecf20Sopenharmony_ci#define RAMDAC_VID_8FB_1 0x00000084UL /* PCI base 8bpp FB buffer 1 */ 908c2ecf20Sopenharmony_ci#define RAMDAC_VID_XXXFB 0x00000088UL /* PCI base of XXX FB */ 918c2ecf20Sopenharmony_ci#define RAMDAC_VID_YYYFB 0x0000008cUL /* PCI base of YYY FB */ 928c2ecf20Sopenharmony_ci#define RAMDAC_VID_ZZZFB 0x00000090UL /* PCI base of ZZZ FB */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* CLUT registers */ 958c2ecf20Sopenharmony_ci#define RAMDAC_INDEX 0x000000bcUL 968c2ecf20Sopenharmony_ci#define RAMDAC_DATA 0x000000c0UL 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void e3d_clut_write(struct e3d_info *ep, int index, u32 val) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci void __iomem *ramdac = ep->ramdac; 1018c2ecf20Sopenharmony_ci unsigned long flags; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci spin_lock_irqsave(&ep->lock, flags); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci writel(index, ramdac + RAMDAC_INDEX); 1068c2ecf20Sopenharmony_ci writel(val, ramdac + RAMDAC_DATA); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ep->lock, flags); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int e3d_setcolreg(unsigned regno, 1128c2ecf20Sopenharmony_ci unsigned red, unsigned green, unsigned blue, 1138c2ecf20Sopenharmony_ci unsigned transp, struct fb_info *info) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct e3d_info *ep = info->par; 1168c2ecf20Sopenharmony_ci u32 red_8, green_8, blue_8; 1178c2ecf20Sopenharmony_ci u32 red_10, green_10, blue_10; 1188c2ecf20Sopenharmony_ci u32 value; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (regno >= 256) 1218c2ecf20Sopenharmony_ci return 1; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci red_8 = red >> 8; 1248c2ecf20Sopenharmony_ci green_8 = green >> 8; 1258c2ecf20Sopenharmony_ci blue_8 = blue >> 8; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci value = (blue_8 << 24) | (green_8 << 16) | (red_8 << 8); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) 1308c2ecf20Sopenharmony_ci ((u32 *)info->pseudo_palette)[regno] = value; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci red_10 = red >> 6; 1348c2ecf20Sopenharmony_ci green_10 = green >> 6; 1358c2ecf20Sopenharmony_ci blue_10 = blue >> 6; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci value = (blue_10 << 20) | (green_10 << 10) | (red_10 << 0); 1388c2ecf20Sopenharmony_ci e3d_clut_write(ep, regno, value); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* XXX This is a bit of a hack. I can't figure out exactly how the 1448c2ecf20Sopenharmony_ci * XXX two 8bpp areas of the framebuffer work. I imagine there is 1458c2ecf20Sopenharmony_ci * XXX a WID attribute somewhere else in the framebuffer which tells 1468c2ecf20Sopenharmony_ci * XXX the ramdac which of the two 8bpp framebuffer regions to take 1478c2ecf20Sopenharmony_ci * XXX the pixel from. So, for now, render into both regions to make 1488c2ecf20Sopenharmony_ci * XXX sure the pixel shows up. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_cistatic void e3d_imageblit(struct fb_info *info, const struct fb_image *image) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct e3d_info *ep = info->par; 1538c2ecf20Sopenharmony_ci unsigned long flags; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci spin_lock_irqsave(&ep->lock, flags); 1568c2ecf20Sopenharmony_ci cfb_imageblit(info, image); 1578c2ecf20Sopenharmony_ci info->screen_base += ep->fb8_buf_diff; 1588c2ecf20Sopenharmony_ci cfb_imageblit(info, image); 1598c2ecf20Sopenharmony_ci info->screen_base -= ep->fb8_buf_diff; 1608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ep->lock, flags); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void e3d_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct e3d_info *ep = info->par; 1668c2ecf20Sopenharmony_ci unsigned long flags; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci spin_lock_irqsave(&ep->lock, flags); 1698c2ecf20Sopenharmony_ci cfb_fillrect(info, rect); 1708c2ecf20Sopenharmony_ci info->screen_base += ep->fb8_buf_diff; 1718c2ecf20Sopenharmony_ci cfb_fillrect(info, rect); 1728c2ecf20Sopenharmony_ci info->screen_base -= ep->fb8_buf_diff; 1738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ep->lock, flags); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void e3d_copyarea(struct fb_info *info, const struct fb_copyarea *area) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct e3d_info *ep = info->par; 1798c2ecf20Sopenharmony_ci unsigned long flags; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci spin_lock_irqsave(&ep->lock, flags); 1828c2ecf20Sopenharmony_ci cfb_copyarea(info, area); 1838c2ecf20Sopenharmony_ci info->screen_base += ep->fb8_buf_diff; 1848c2ecf20Sopenharmony_ci cfb_copyarea(info, area); 1858c2ecf20Sopenharmony_ci info->screen_base -= ep->fb8_buf_diff; 1868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ep->lock, flags); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic const struct fb_ops e3d_ops = { 1908c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1918c2ecf20Sopenharmony_ci .fb_setcolreg = e3d_setcolreg, 1928c2ecf20Sopenharmony_ci .fb_fillrect = e3d_fillrect, 1938c2ecf20Sopenharmony_ci .fb_copyarea = e3d_copyarea, 1948c2ecf20Sopenharmony_ci .fb_imageblit = e3d_imageblit, 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int e3d_set_fbinfo(struct e3d_info *ep) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct fb_info *info = ep->info; 2008c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT; 2038c2ecf20Sopenharmony_ci info->fbops = &e3d_ops; 2048c2ecf20Sopenharmony_ci info->screen_base = ep->fb_base; 2058c2ecf20Sopenharmony_ci info->screen_size = ep->fb_size; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci info->pseudo_palette = ep->pseudo_palette; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Fill fix common fields */ 2108c2ecf20Sopenharmony_ci strlcpy(info->fix.id, "e3d", sizeof(info->fix.id)); 2118c2ecf20Sopenharmony_ci info->fix.smem_start = ep->fb_base_phys; 2128c2ecf20Sopenharmony_ci info->fix.smem_len = ep->fb_size; 2138c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 2148c2ecf20Sopenharmony_ci if (ep->depth == 32 || ep->depth == 24) 2158c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 2168c2ecf20Sopenharmony_ci else 2178c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci var->xres = ep->width; 2208c2ecf20Sopenharmony_ci var->yres = ep->height; 2218c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 2228c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 2238c2ecf20Sopenharmony_ci var->bits_per_pixel = ep->depth; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci var->red.offset = 8; 2268c2ecf20Sopenharmony_ci var->red.length = 8; 2278c2ecf20Sopenharmony_ci var->green.offset = 16; 2288c2ecf20Sopenharmony_ci var->green.length = 8; 2298c2ecf20Sopenharmony_ci var->blue.offset = 24; 2308c2ecf20Sopenharmony_ci var->blue.length = 8; 2318c2ecf20Sopenharmony_ci var->transp.offset = 0; 2328c2ecf20Sopenharmony_ci var->transp.length = 0; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (fb_alloc_cmap(&info->cmap, 256, 0)) { 2358c2ecf20Sopenharmony_ci printk(KERN_ERR "e3d: Cannot allocate color map.\n"); 2368c2ecf20Sopenharmony_ci return -ENOMEM; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int e3d_pci_register(struct pci_dev *pdev, 2438c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct device_node *of_node; 2468c2ecf20Sopenharmony_ci const char *device_type; 2478c2ecf20Sopenharmony_ci struct fb_info *info; 2488c2ecf20Sopenharmony_ci struct e3d_info *ep; 2498c2ecf20Sopenharmony_ci unsigned int line_length; 2508c2ecf20Sopenharmony_ci int err; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci of_node = pci_device_to_OF_node(pdev); 2538c2ecf20Sopenharmony_ci if (!of_node) { 2548c2ecf20Sopenharmony_ci printk(KERN_ERR "e3d: Cannot find OF node of %s\n", 2558c2ecf20Sopenharmony_ci pci_name(pdev)); 2568c2ecf20Sopenharmony_ci return -ENODEV; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci device_type = of_get_property(of_node, "device_type", NULL); 2608c2ecf20Sopenharmony_ci if (!device_type) { 2618c2ecf20Sopenharmony_ci printk(KERN_INFO "e3d: Ignoring secondary output device " 2628c2ecf20Sopenharmony_ci "at %s\n", pci_name(pdev)); 2638c2ecf20Sopenharmony_ci return -ENODEV; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 2678c2ecf20Sopenharmony_ci if (err < 0) { 2688c2ecf20Sopenharmony_ci printk(KERN_ERR "e3d: Cannot enable PCI device %s\n", 2698c2ecf20Sopenharmony_ci pci_name(pdev)); 2708c2ecf20Sopenharmony_ci goto err_out; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct e3d_info), &pdev->dev); 2748c2ecf20Sopenharmony_ci if (!info) { 2758c2ecf20Sopenharmony_ci err = -ENOMEM; 2768c2ecf20Sopenharmony_ci goto err_disable; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci ep = info->par; 2808c2ecf20Sopenharmony_ci ep->info = info; 2818c2ecf20Sopenharmony_ci ep->pdev = pdev; 2828c2ecf20Sopenharmony_ci spin_lock_init(&ep->lock); 2838c2ecf20Sopenharmony_ci ep->of_node = of_node; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Read the PCI base register of the frame buffer, which we 2868c2ecf20Sopenharmony_ci * need in order to interpret the RAMDAC_VID_*FB* values in 2878c2ecf20Sopenharmony_ci * the ramdac correctly. 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, 2908c2ecf20Sopenharmony_ci &ep->fb_base_reg); 2918c2ecf20Sopenharmony_ci ep->fb_base_reg &= PCI_BASE_ADDRESS_MEM_MASK; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ep->regs_base_phys = pci_resource_start (pdev, 1); 2948c2ecf20Sopenharmony_ci err = pci_request_region(pdev, 1, "e3d regs"); 2958c2ecf20Sopenharmony_ci if (err < 0) { 2968c2ecf20Sopenharmony_ci printk("e3d: Cannot request region 1 for %s\n", 2978c2ecf20Sopenharmony_ci pci_name(pdev)); 2988c2ecf20Sopenharmony_ci goto err_release_fb; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci ep->ramdac = ioremap(ep->regs_base_phys + 0x8000, 0x1000); 3018c2ecf20Sopenharmony_ci if (!ep->ramdac) { 3028c2ecf20Sopenharmony_ci err = -ENOMEM; 3038c2ecf20Sopenharmony_ci goto err_release_pci1; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci ep->fb8_0_off = readl(ep->ramdac + RAMDAC_VID_8FB_0); 3078c2ecf20Sopenharmony_ci ep->fb8_0_off -= ep->fb_base_reg; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ep->fb8_1_off = readl(ep->ramdac + RAMDAC_VID_8FB_1); 3108c2ecf20Sopenharmony_ci ep->fb8_1_off -= ep->fb_base_reg; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ep->fb8_buf_diff = ep->fb8_1_off - ep->fb8_0_off; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci ep->fb_base_phys = pci_resource_start (pdev, 0); 3158c2ecf20Sopenharmony_ci ep->fb_base_phys += ep->fb8_0_off; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci err = pci_request_region(pdev, 0, "e3d framebuffer"); 3188c2ecf20Sopenharmony_ci if (err < 0) { 3198c2ecf20Sopenharmony_ci printk("e3d: Cannot request region 0 for %s\n", 3208c2ecf20Sopenharmony_ci pci_name(pdev)); 3218c2ecf20Sopenharmony_ci goto err_unmap_ramdac; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci err = e3d_get_props(ep); 3258c2ecf20Sopenharmony_ci if (err) 3268c2ecf20Sopenharmony_ci goto err_release_pci0; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci line_length = (readl(ep->ramdac + RAMDAC_VID_CFG) >> 16) & 0xff; 3298c2ecf20Sopenharmony_ci line_length = 1 << line_length; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci switch (ep->depth) { 3328c2ecf20Sopenharmony_ci case 8: 3338c2ecf20Sopenharmony_ci info->fix.line_length = line_length; 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci case 16: 3368c2ecf20Sopenharmony_ci info->fix.line_length = line_length * 2; 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci case 24: 3398c2ecf20Sopenharmony_ci info->fix.line_length = line_length * 3; 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci case 32: 3428c2ecf20Sopenharmony_ci info->fix.line_length = line_length * 4; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci ep->fb_size = info->fix.line_length * ep->height; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci ep->fb_base = ioremap(ep->fb_base_phys, ep->fb_size); 3488c2ecf20Sopenharmony_ci if (!ep->fb_base) { 3498c2ecf20Sopenharmony_ci err = -ENOMEM; 3508c2ecf20Sopenharmony_ci goto err_release_pci0; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci err = e3d_set_fbinfo(ep); 3548c2ecf20Sopenharmony_ci if (err) 3558c2ecf20Sopenharmony_ci goto err_unmap_fb; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, info); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci printk("e3d: Found device at %s\n", pci_name(pdev)); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci err = register_framebuffer(info); 3628c2ecf20Sopenharmony_ci if (err < 0) { 3638c2ecf20Sopenharmony_ci printk(KERN_ERR "e3d: Could not register framebuffer %s\n", 3648c2ecf20Sopenharmony_ci pci_name(pdev)); 3658c2ecf20Sopenharmony_ci goto err_free_cmap; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cierr_free_cmap: 3718c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cierr_unmap_fb: 3748c2ecf20Sopenharmony_ci iounmap(ep->fb_base); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cierr_release_pci0: 3778c2ecf20Sopenharmony_ci pci_release_region(pdev, 0); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cierr_unmap_ramdac: 3808c2ecf20Sopenharmony_ci iounmap(ep->ramdac); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cierr_release_pci1: 3838c2ecf20Sopenharmony_ci pci_release_region(pdev, 1); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cierr_release_fb: 3868c2ecf20Sopenharmony_ci framebuffer_release(info); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cierr_disable: 3898c2ecf20Sopenharmony_ci pci_disable_device(pdev); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cierr_out: 3928c2ecf20Sopenharmony_ci return err; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic const struct pci_device_id e3d_pci_table[] = { 3968c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x7a0), }, 3978c2ecf20Sopenharmony_ci { PCI_DEVICE(0x1091, 0x7a0), }, 3988c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x7a2), }, 3998c2ecf20Sopenharmony_ci { .vendor = PCI_VENDOR_ID_3DLABS, 4008c2ecf20Sopenharmony_ci .device = PCI_ANY_ID, 4018c2ecf20Sopenharmony_ci .subvendor = PCI_VENDOR_ID_3DLABS, 4028c2ecf20Sopenharmony_ci .subdevice = 0x0108, 4038c2ecf20Sopenharmony_ci }, 4048c2ecf20Sopenharmony_ci { .vendor = PCI_VENDOR_ID_3DLABS, 4058c2ecf20Sopenharmony_ci .device = PCI_ANY_ID, 4068c2ecf20Sopenharmony_ci .subvendor = PCI_VENDOR_ID_3DLABS, 4078c2ecf20Sopenharmony_ci .subdevice = 0x0140, 4088c2ecf20Sopenharmony_ci }, 4098c2ecf20Sopenharmony_ci { .vendor = PCI_VENDOR_ID_3DLABS, 4108c2ecf20Sopenharmony_ci .device = PCI_ANY_ID, 4118c2ecf20Sopenharmony_ci .subvendor = PCI_VENDOR_ID_3DLABS, 4128c2ecf20Sopenharmony_ci .subdevice = 0x1024, 4138c2ecf20Sopenharmony_ci }, 4148c2ecf20Sopenharmony_ci { 0, } 4158c2ecf20Sopenharmony_ci}; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic struct pci_driver e3d_driver = { 4188c2ecf20Sopenharmony_ci .driver = { 4198c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 4208c2ecf20Sopenharmony_ci }, 4218c2ecf20Sopenharmony_ci .name = "e3d", 4228c2ecf20Sopenharmony_ci .id_table = e3d_pci_table, 4238c2ecf20Sopenharmony_ci .probe = e3d_pci_register, 4248c2ecf20Sopenharmony_ci}; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int __init e3d_init(void) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci if (fb_get_options("e3d", NULL)) 4298c2ecf20Sopenharmony_ci return -ENODEV; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return pci_register_driver(&e3d_driver); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_cidevice_initcall(e3d_init); 434