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