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