[PATCH] video: fbdev: add HP Visualize FX driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This adds a framebuffer driver for HP's visualize series of
cards. The aim is to support all FX2 - FX10 types but currently only
FX5 is tested as i don't have any other card.

Currently no mmap of video memory is supported as i haven't figured
out how to access VRAM directly.

Signed-off-by: Sven Schnelle <svens@xxxxxxxxxxxxxx>
---
 drivers/video/fbdev/Kconfig       |  14 +
 drivers/video/fbdev/Makefile      |   2 +-
 drivers/video/fbdev/visualizefx.c | 602 ++++++++++++++++++++++++++++++
 3 files changed, 617 insertions(+), 1 deletion(-)
 create mode 100644 drivers/video/fbdev/visualizefx.c

diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 6ed5e608dd04..ee963f755047 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -566,6 +566,20 @@ config FB_STI
 
 	  It is safe to enable this option, so you should probably say "Y".
 
+config FB_VISUALIZEFX
+	tristate "HP Visualize FX support"
+	depends on FB && PCI && PARISC
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	select RATIONAL
+	  help
+	    Frame buffer driver for the HP Visualize FX cards. These cards are
+	    commonly found in PA-RISC workstations. Currently only FX5 has been
+	    tested.
+
+	    Say Y if you have such a card.
+
 config FB_MAC
 	bool "Generic Macintosh display support"
 	depends on (FB = y) && MAC
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
index 477b9624b703..3ef26907a3a4 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -129,6 +129,6 @@ obj-$(CONFIG_FB_MX3)		  += mx3fb.o
 obj-$(CONFIG_FB_DA8XX)		  += da8xx-fb.o
 obj-$(CONFIG_FB_SSD1307)	  += ssd1307fb.o
 obj-$(CONFIG_FB_SIMPLE)           += simplefb.o
-
+obj-$(CONFIG_FB_VISUALIZEFX)	  += visualizefx.o
 # the test framebuffer is last
 obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
diff --git a/drivers/video/fbdev/visualizefx.c b/drivers/video/fbdev/visualizefx.c
new file mode 100644
index 000000000000..9318e07be1aa
--- /dev/null
+++ b/drivers/video/fbdev/visualizefx.c
@@ -0,0 +1,602 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Framebuffer driver for Visualize FX cards commonly found in PA-RISC machines
+ *
+ * Copyright (c) 2021 Sven Schnelle <svens@xxxxxxxxxxxxxx>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/rational.h>
+
+#define VISFX_VRAM_ENDIANESS_WRITE	0xa4303c
+#define VISFX_VRAM_ENDIANESS_READ	0xaa0408
+#define VISFX_VRAM_ENDIANESS_BIG	0xe4e4e4e4
+#define VISFX_VRAM_ENDIANESS_LITTLE	0x1b1b1b1b
+
+#define VISFX_STATUS			0x641400
+
+#define VISFX_COLOR_MASK		0x800018
+#define VISFX_COLOR_INDEX		0x800020
+#define VISFX_COLOR_VALUE		0x800024
+
+#define VISFX_SYNC_POLARITY		0x800044
+#define VISFX_SYNC_VISIBLE_SIZE		0x80005c
+#define VISFX_SYNC_HORIZ_CONF		0x800060
+#define VISFX_SYNC_VERT_CONF		0x800068
+#define VISFX_SYNC_MASTER_PLL		0x8000a0
+#define VISFX_SYNC_PLL_STATUS		0x8000b8
+
+#define VISFX_VRAM_WRITE_MODE		0xa00808
+#define VISFX_VRAM_MASK			0xa0082c
+#define VISFX_FGCOLOR			0xa0083c
+#define VISFX_BGCOLOR			0xa00844
+#define VISFX_WRITE_MASK		0xa0084c
+#define VISFX_VRAM_WRITE_DATA_INCRX	0xa60000
+#define VISFX_VRAM_WRITE_DATA_INCRY	0xa68000
+#define VISFX_SCREEN_SIZE		0xac1054
+#define VISFX_VRAM_WRITE_DEST		0xac1000
+
+#define VISFX_START			0xb3c000
+#define VISFX_SIZE			0xb3c808
+#define VISFX_HEIGHT			0xb3c008
+#define VISFX_DST			0xb3cc00
+
+#define VISFX_DFP_ENABLE		0x10000
+#define VISFX_HSYNC_POSITIVE		0x40000
+#define VISFX_VSYNC_POSITIVE		0x80000
+
+#define VISFX_SYNC_PLL_BASE		49383 /* 20.25MHz in ps */
+
+#define VISFX_CURSOR_POS		0x400000
+#define VISFX_CURSOR_INDEX		0x400004
+#define VISFX_CURSOR_DATA		0x400008
+#define VISFX_CURSOR_COLOR		0x400010
+#define VISFX_CURSOR_ENABLE		0x80000000
+
+#define VISFX_VRAM_WRITE_MODE_BITMAP	0x02000000
+#define VISFX_VRAM_WRITE_MODE_COLOR	0x050004c0
+#define VISFX_VRAM_WRITE_MODE_FILL	0x05000080
+
+#define VISFX_FB_LENGTH			0x01000000
+#define VISFX_FB_OFFSET			0x01000000
+#define NR_PALETTE 256
+
+struct visfx_par {
+	u32 pseudo_palette[256];
+	unsigned long debug_reg;
+	void __iomem *reg_base;
+	unsigned long reg_size;
+	int open_count;
+};
+
+static u32 visfx_readl(struct fb_info *info, int reg)
+{
+	struct visfx_par *par = info->par;
+
+	return le32_to_cpu(readl(par->reg_base + reg));
+}
+
+static void visfx_writel(struct fb_info *info, int reg, u32 val)
+{
+	struct visfx_par *par = info->par;
+
+	return writel(cpu_to_le32(val), par->reg_base + reg);
+}
+
+static void visfx_write_vram(struct fb_info *info, int reg, u32 val)
+{
+	struct visfx_par *par = info->par;
+
+	return writel(val, par->reg_base + reg);
+}
+
+static void visfx_bmove_wait(struct fb_info *info)
+{
+	while (visfx_readl(info, VISFX_STATUS));
+}
+
+static ssize_t visfx_sysfs_show_reg(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct fb_info *info = pci_get_drvdata(container_of(dev, struct pci_dev, dev));
+	struct visfx_par *par = info->par;
+
+	return sprintf(buf, "%08x\n", visfx_readl(info, par->debug_reg));
+}
+
+static ssize_t visfx_sysfs_store_reg(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct fb_info *info = pci_get_drvdata(container_of(dev, struct pci_dev, dev));
+	struct visfx_par *par = info->par;
+	unsigned long data;
+	char *p;
+
+	p = strchr(buf, '=');
+	if (p)
+		*p = '\0';
+
+	if (kstrtoul(buf, 16, &par->debug_reg))
+		return -EINVAL;
+
+	if (par->debug_reg > par->reg_size)
+		return -EINVAL;
+
+	if (p) {
+		if (kstrtoul(p+1, 16, &data))
+			return -EINVAL;
+		visfx_writel(info, par->debug_reg, data);
+	}
+	return count;
+}
+
+static DEVICE_ATTR(reg, 0600, visfx_sysfs_show_reg, visfx_sysfs_store_reg);
+
+static void visfx_set_vram_addr(struct fb_info *info, int x, int y)
+{
+	visfx_writel(info, VISFX_VRAM_WRITE_DEST, (y << 16) | x);
+}
+
+static void visfx_set_bmove_color(struct fb_info *info, int fg, int bg)
+{
+	visfx_writel(info, VISFX_BGCOLOR, 0x01010101 * bg);
+	visfx_writel(info, VISFX_FGCOLOR, 0x01010101 * fg);
+}
+
+static void visfx_imageblit_mono(struct fb_info *info, const char *data, int dx, int dy,
+				 int width, int height, int fg_color, int bg_color)
+{
+	int _width, x, y;
+	u32 tmp;
+
+	visfx_set_bmove_color(info, fg_color, bg_color);
+	visfx_writel(info, VISFX_VRAM_WRITE_MODE, VISFX_VRAM_WRITE_MODE_COLOR);
+	visfx_writel(info, VISFX_VRAM_MASK, 0xffffffff);
+
+	for (x = 0, _width = width; _width > 0; _width -= 32, x += 4) {
+		visfx_set_vram_addr(info, dx + x * 8, dy);
+		if (_width >= 32) {
+			for (y = 0; y < height; y++) {
+				memcpy(&tmp, &data[y * (width / 8) + x], 4);
+				visfx_write_vram(info, VISFX_VRAM_WRITE_DATA_INCRY, tmp);
+			}
+		} else {
+			visfx_writel(info, VISFX_VRAM_MASK, GENMASK(31, 31 - _width + 1));
+			for (y = 0; y < height; y++) {
+				tmp = 0;
+				memcpy(&tmp, &data[y * (width / 8) + x], ((_width-1)/8)+1);
+				visfx_write_vram(info, VISFX_VRAM_WRITE_DATA_INCRY, tmp);
+			}
+		}
+	}
+}
+
+static void visfx_setup_unknown(struct fb_info *info)
+{
+	visfx_writel(info, 0xb08044, 0x1b);
+	visfx_writel(info, 0xb08048, 0x1b);
+	visfx_writel(info, 0x920860, 0xe4);
+	visfx_writel(info, 0xa00818, 0);
+	visfx_writel(info, 0xa00404, 0);
+	visfx_writel(info, 0x921110, 0);
+	visfx_writel(info, 0x9211d8, 0);
+	visfx_writel(info, 0xa0086c, 0);
+	visfx_writel(info, 0x921114, 0);
+	visfx_writel(info, 0xac1050, 0);
+	visfx_writel(info, 0xa00858, 0xb0);
+
+	visfx_writel(info, VISFX_VRAM_WRITE_MODE, VISFX_VRAM_WRITE_MODE_BITMAP);
+	visfx_writel(info, VISFX_WRITE_MASK, 0xffffffff);
+	visfx_writel(info, VISFX_VRAM_MASK, 0xffffffff);
+#ifdef __BIG_ENDIAN
+	visfx_writel(info, VISFX_VRAM_ENDIANESS_READ, VISFX_VRAM_ENDIANESS_BIG);
+	visfx_writel(info, VISFX_VRAM_ENDIANESS_WRITE, VISFX_VRAM_ENDIANESS_BIG);
+#else
+	visfx_writel(info, VISFX_VRAM_ENDIANESS_READ, VISFX_VRAM_ENDIANESS_LITTLE);
+	visfx_writel(info, VISFX_VRAM_ENDIANESS_WRITE, VISFX_VRAM_ENDIANESS_LITTLE);
+#endif
+}
+
+static void visfx_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+	int x, y;
+
+	visfx_bmove_wait(info);
+	visfx_writel(info, VISFX_WRITE_MASK, 0xffffffff);
+
+	switch (image->depth) {
+	case 1:
+		visfx_imageblit_mono(info, image->data, image->dx, image->dy,
+				     image->width, image->height,
+				     image->fg_color, image->bg_color);
+		break;
+	case 8:
+		visfx_writel(info, VISFX_VRAM_WRITE_MODE, VISFX_VRAM_WRITE_MODE_BITMAP);
+
+		for (y = 0; y < image->height; y++) {
+			u32 data = 0;
+			int pos = 0;
+
+			visfx_writel(info, VISFX_WRITE_MASK, 0xffffffff);
+			visfx_set_vram_addr(info, image->dx, image->dy + y);
+
+			for (x = 0; x < image->width; x++) {
+				pos = x & 3;
+				data |= ((u8 *)image->data)[y * image->height + x] << (pos * 8);
+				if (pos == 3) {
+					visfx_write_vram(info, VISFX_VRAM_WRITE_DATA_INCRX, data);
+					data = 0;
+				}
+			}
+
+			if (x && pos != 3) {
+				visfx_write_vram(info, VISFX_WRITE_MASK, (1 << ((pos+1) * 8))-1);
+				visfx_write_vram(info, VISFX_VRAM_WRITE_DATA_INCRX, data);
+			}
+		}
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void visfx_fillrect(struct fb_info *info, const struct fb_fillrect *fr)
+{
+	visfx_bmove_wait(info);
+	visfx_writel(info, VISFX_WRITE_MASK, 0xffffffff);
+	visfx_writel(info, VISFX_VRAM_WRITE_MODE, VISFX_VRAM_WRITE_MODE_FILL);
+	visfx_set_bmove_color(info, fr->color, 0);
+	visfx_writel(info, VISFX_START, (fr->dx << 16) | fr->dy);
+	visfx_writel(info, VISFX_SIZE, (fr->width << 16) | fr->height);
+}
+
+static u32 visfx_cmap_entry(struct fb_cmap *cmap, int color)
+{
+	return (((cmap->blue[color] & 0xff)) |
+		((cmap->green[color] & 0xff) << 8) |
+		(cmap->red[color] & 0xff) << 16);
+}
+
+static int visfx_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+	int i;
+
+	visfx_writel(info, VISFX_COLOR_INDEX, cmap->start);
+
+	for (i = 0; i < cmap->len; i++)
+		visfx_writel(info, VISFX_COLOR_VALUE, visfx_cmap_entry(cmap, i));
+
+	visfx_writel(info, VISFX_COLOR_MASK, 0xff);
+	visfx_writel(info, 0x80004c, 0xc);
+	visfx_writel(info, 0x800000, 0);
+	return 0;
+}
+
+static void visfx_get_video_mode(struct fb_info *info)
+{
+	struct fb_var_screeninfo *var = &info->var;
+	unsigned long n, d;
+	u32 tmp;
+
+	tmp = visfx_readl(info, VISFX_SYNC_VISIBLE_SIZE);
+	var->xres = (tmp & 0xffff) + 1;
+	var->yres = (tmp >> 16) + 1;
+
+	tmp = visfx_readl(info, VISFX_SYNC_MASTER_PLL);
+	n = (tmp & 0xff) + 1;
+	d = ((tmp >> 8) & 0xff) + 1;
+	var->pixclock = (VISFX_SYNC_PLL_BASE / d) * n;
+
+	tmp = visfx_readl(info, VISFX_SYNC_HORIZ_CONF);
+	var->left_margin = ((tmp >> 20) & 0x1ff) + 1;
+	var->hsync_len = (((tmp >> 12) & 0xff) + 1) * 4;
+	var->right_margin = (tmp & 0x1ff) + 1;
+
+	tmp = visfx_readl(info, VISFX_SYNC_VERT_CONF);
+	var->upper_margin = ((tmp >> 16) & 0xff) + 1;
+	var->vsync_len = ((tmp >> 8) & 0xff) + 1;
+	var->lower_margin = (tmp & 0xff) + 1;
+
+	tmp = visfx_readl(info, VISFX_SYNC_POLARITY);
+	if (tmp & VISFX_HSYNC_POSITIVE)
+		var->sync |= FB_SYNC_HOR_HIGH_ACT;
+	if (tmp & VISFX_VSYNC_POSITIVE)
+		var->sync |= FB_SYNC_VERT_HIGH_ACT;
+
+	var->red.length = 8;
+	var->green.length = 8;
+	var->blue.length = 8;
+	var->bits_per_pixel = 8;
+	var->grayscale = 0;
+	var->xres_virtual = var->xres;
+	var->yres_virtual = var->yres;
+	info->screen_size = 2048 * var->yres;
+}
+
+static void visfx_set_pll(struct fb_info *info, unsigned long clock)
+{
+	unsigned long n, d, tmp;
+
+	rational_best_approximation(clock, VISFX_SYNC_PLL_BASE, 0x3f, 0x3f, &n, &d);
+	tmp = (((d * 4) - 1) << 8) | ((n * 4) - 1);
+	visfx_writel(info, VISFX_SYNC_MASTER_PLL, 0x520000 | tmp);
+	while (visfx_readl(info, VISFX_SYNC_PLL_STATUS) & 0xffffff)
+		udelay(10);
+	visfx_writel(info, VISFX_SYNC_MASTER_PLL, 0x530000 | tmp);
+	while (visfx_readl(info, VISFX_SYNC_PLL_STATUS) & 0xffffff)
+		udelay(10);
+}
+
+static int visfx_set_par(struct fb_info *info)
+{
+	u32 xres, yres, hbp, hsw, hfp, vbp, vsw, vfp, tmp;
+	struct fb_var_screeninfo *var = &info->var;
+
+
+	xres = var->xres;
+	yres = var->yres;
+	hsw = var->hsync_len / 4 - 1;
+	hfp = var->right_margin - 1;
+	hbp = var->left_margin - 1;
+	vsw = var->vsync_len - 1;
+	vfp = var->lower_margin - 1;
+	vbp = var->upper_margin - 1;
+
+	visfx_set_pll(info, var->pixclock);
+	visfx_writel(info, VISFX_SYNC_VISIBLE_SIZE, ((yres - 1) << 16) | (xres - 1));
+	visfx_writel(info, VISFX_SYNC_HORIZ_CONF, (hbp << 20) | (hsw << 12) | (0xc << 8) | hfp);
+	visfx_writel(info, VISFX_SYNC_VERT_CONF, (vbp << 16) | (vsw << 8) | vfp);
+	visfx_writel(info, VISFX_SCREEN_SIZE, (xres << 16) | yres);
+
+	tmp = VISFX_DFP_ENABLE;
+	if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+		tmp |= VISFX_HSYNC_POSITIVE;
+	if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+		tmp |= VISFX_VSYNC_POSITIVE;
+	visfx_writel(info, VISFX_SYNC_POLARITY, tmp);
+
+	visfx_writel(info, VISFX_VRAM_WRITE_MODE, VISFX_VRAM_WRITE_MODE_BITMAP);
+
+	visfx_get_video_mode(info);
+	return 0;
+}
+
+static int visfx_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	if (var->pixclock > VISFX_SYNC_PLL_BASE ||
+	    var->left_margin > 512 ||
+	    var->right_margin > 512 ||
+	    var->hsync_len > 512 ||
+	    var->lower_margin > 256 ||
+	    var->upper_margin > 256 ||
+	    var->vsync_len > 256 ||
+		var->xres > 2048 ||
+		var->yres > 2048)
+		return -EINVAL;
+	return 0;
+}
+
+static void visfx_update_cursor_image_line(struct fb_info *info,
+					   struct fb_cursor *cursor, int y)
+{
+	unsigned int x, bytecnt;
+	u32 data[2] = { 0 };
+	u8 d, m;
+
+	bytecnt = cursor->image.width / 8;
+
+	for (x = 0; x < bytecnt && x < 8; x++) {
+		m = cursor->mask[y * bytecnt + x];
+		d = cursor->image.data[y * bytecnt + x];
+
+		if (cursor->rop == ROP_XOR)
+			((u8 *)data)[x] = d ^ m;
+		else
+			((u8 *)data)[x] = d & m;
+	}
+
+	visfx_writel(info, VISFX_CURSOR_DATA, data[0]);
+	visfx_writel(info, VISFX_CURSOR_DATA, data[1]);
+}
+
+static void visfx_update_cursor_image(struct fb_info *info,
+				      struct fb_cursor *cursor)
+{
+	int y, height = cursor->image.height;
+
+	if (height > 128)
+		height = 128;
+
+	visfx_writel(info, VISFX_CURSOR_INDEX, 0);
+	for (y = 0; y < height; y++)
+		visfx_update_cursor_image_line(info, cursor, y);
+
+	for (; y < 256; y++)
+		visfx_writel(info, VISFX_CURSOR_DATA, 0);
+}
+
+static int visfx_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+	u32 tmp;
+
+	if (cursor->set & (FB_CUR_SETIMAGE|FB_CUR_SETSHAPE))
+		visfx_update_cursor_image(info, cursor);
+
+	if (cursor->set & FB_CUR_SETCMAP) {
+		tmp = visfx_cmap_entry(&info->cmap, cursor->image.fg_color);
+		visfx_writel(info, VISFX_CURSOR_COLOR, tmp);
+	}
+
+	tmp = (cursor->image.dx << 16) | (cursor->image.dy & 0xffff);
+	if (cursor->enable)
+		tmp |= VISFX_CURSOR_ENABLE;
+	visfx_writel(info, VISFX_CURSOR_POS, tmp);
+	return 0;
+}
+
+static int visfx_open(struct fb_info *info, int user)
+{
+	struct visfx_par *par = info->par;
+
+	if (user && par->open_count++ == 0)
+		visfx_writel(info, VISFX_VRAM_WRITE_MODE, VISFX_VRAM_WRITE_MODE_BITMAP);
+
+	return 0;
+}
+
+static int visfx_release(struct fb_info *info, int user)
+{
+	struct visfx_par *par = info->par;
+
+	if (user)
+		par->open_count--;
+
+	return 0;
+}
+
+static const struct fb_ops visfx_ops = {
+	.owner		= THIS_MODULE,
+	.fb_open	= visfx_open,
+	.fb_release	= visfx_release,
+	.fb_setcmap	= visfx_setcmap,
+	.fb_fillrect	= visfx_fillrect,
+	.fb_imageblit	= visfx_imageblit,
+	.fb_set_par	= visfx_set_par,
+	.fb_check_var	= visfx_check_var,
+	.fb_cursor	= visfx_cursor,
+};
+
+static struct fb_fix_screeninfo visfx_fix = {
+	.type = FB_TYPE_PACKED_PIXELS,
+	.visual = FB_VISUAL_PSEUDOCOLOR,
+	.id = "Visualize FX",
+};
+
+static int visfx_probe(struct pci_dev *pdev,
+		       const struct pci_device_id *ent)
+{
+	struct visfx_par *par;
+	struct fb_info *info;
+	int ret;
+
+	info = framebuffer_alloc(sizeof(struct visfx_par), &pdev->dev);
+	if (!info)
+		return -ENOMEM;
+
+	par = info->par;
+
+	ret = pci_enable_device(pdev);
+	if (ret)
+		goto err_out_free;
+
+	ret = pci_request_regions(pdev, KBUILD_MODNAME);
+	if (ret)
+		goto err_out_disable;
+
+	par->reg_size = pci_resource_len(pdev, 0);
+	par->reg_base = pci_ioremap_bar(pdev, 0);
+	par->open_count = 0;
+
+	if (!par->reg_base) {
+		ret = -ENOMEM;
+		goto err_out_release;
+	}
+
+	pci_set_drvdata(pdev, info);
+
+	info->fbops = &visfx_ops;
+	info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_IMAGEBLIT;
+	info->fix = visfx_fix;
+	info->pseudo_palette = par->pseudo_palette;
+	info->fix.smem_start = pci_resource_start(pdev, 0) + VISFX_FB_OFFSET;
+	info->fix.smem_len = VISFX_FB_LENGTH;
+	info->screen_base = par->reg_base + VISFX_FB_OFFSET;
+	info->fix.type = FB_TYPE_PACKED_PIXELS;
+	info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+	info->fix.line_length = 2048;
+	info->fix.accel = FB_ACCEL_NONE;
+
+	visfx_setup_unknown(info);
+	visfx_get_video_mode(info);
+	info->var.accel_flags = info->flags;
+
+	ret = device_create_file(&pdev->dev, &dev_attr_reg);
+	if (ret)
+		goto err_out_iounmap;
+
+	ret = fb_alloc_cmap(&info->cmap, NR_PALETTE, 0);
+	if (ret)
+		goto err_out_remove;
+
+	ret = register_framebuffer(info);
+	if (ret)
+		goto err_out_dealloc_cmap;
+	return 0;
+
+err_out_dealloc_cmap:
+	fb_dealloc_cmap(&info->cmap);
+err_out_remove:
+	device_remove_file(&pdev->dev, &dev_attr_reg);
+err_out_iounmap:
+	pci_iounmap(pdev, par->reg_base);
+err_out_release:
+	pci_release_regions(pdev);
+err_out_disable:
+	pci_disable_device(pdev);
+err_out_free:
+	framebuffer_release(info);
+	return ret;
+}
+
+static void __exit visfx_remove(struct pci_dev *pdev)
+{
+	struct fb_info *info = pci_get_drvdata(pdev);
+	struct visfx_par *par = info->par;
+
+	device_remove_file(&pdev->dev, &dev_attr_reg);
+	unregister_framebuffer(info);
+	pci_iounmap(pdev, par->reg_base);
+	framebuffer_release(info);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+}
+
+static const struct pci_device_id visfx_pci_tbl[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_HP, 0x1008) },
+	{ 0 },
+};
+MODULE_DEVICE_TABLE(pci, visfx_pci_tbl);
+
+static struct pci_driver visfx_driver = {
+	.name      = KBUILD_MODNAME,
+	.id_table  = visfx_pci_tbl,
+	.probe     = visfx_probe,
+	.remove    = visfx_remove,
+};
+
+static int __init visfx_init(void)
+{
+	return pci_register_driver(&visfx_driver);
+}
+module_init(visfx_init);
+
+static void __exit visfx_exit(void)
+{
+	pci_unregister_driver(&visfx_driver);
+}
+module_exit(visfx_exit);
+
+MODULE_AUTHOR("Sven Schnelle <svens@xxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Framebuffer driver for HP Visualize FX cards");
+MODULE_LICENSE("GPL");
-- 
2.33.0




[Index of Archives]     [Linux SoC]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux