diff --git a/drivers/gpu/drm/dvbe/Kconfig b/drivers/gpu/drm/dvbe/Kconfig
index e49df10..ca27455 100644
--- a/drivers/gpu/drm/dvbe/Kconfig
+++ b/drivers/gpu/drm/dvbe/Kconfig
@@ -27,3 +27,21 @@ config DRM_DVBE
To compile this driver as a module, choose M here: the
module will be called dvbe.
+
+config DRM_DVBE_FBDEV
+ bool "VESA BIOS Extension DRM fbdev Compatibility Layer"
+ depends on DRM_DVBE && FB
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This provides an fbdev frontend (via /dev/fbX) for the DVBE VESA
+ driver. Old userspace that depends on the fbdev API can access the
+ DVBE driver this way. It provides full backwards compatibility to the
+ old vesafb driver.
+
+ Newer userspace accesses graphics devices via the DRM API and the old
+ fbdev compatibility layer is not needed. Activate it only if you
+ really need to run old userspace programs.
+
+ If unsure, say N.
diff --git a/drivers/gpu/drm/dvbe/Makefile b/drivers/gpu/drm/dvbe/Makefile
index f6fb888..e01aaa1 100644
--- a/drivers/gpu/drm/dvbe/Makefile
+++ b/drivers/gpu/drm/dvbe/Makefile
@@ -1,4 +1,5 @@
ccflags-y := -Iinclude/drm
dvbe-y := dvbe_drv.o dvbe_main.o dvbe_mem.o dvbe_vesa.o
+dvbe-$(CONFIG_DRM_DVBE_FBDEV) += dvbe_fbdev.o
obj-$(CONFIG_DRM_DVBE) := dvbe.o
diff --git a/drivers/gpu/drm/dvbe/dvbe.h b/drivers/gpu/drm/dvbe/dvbe.h
index 68fd452..dfe7c20 100644
--- a/drivers/gpu/drm/dvbe/dvbe.h
+++ b/drivers/gpu/drm/dvbe/dvbe.h
@@ -14,6 +14,7 @@
#define DVBE_DRV_H
#include <linux/errno.h>
+#include <linux/fb.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
@@ -47,6 +48,9 @@ struct dvbe_device {
struct drm_encoder enc;
struct drm_connector conn;
struct drm_display_mode *mode;
+
+ /* fbdev */
+ void *fbdev;
};
int dvbe_drm_load(struct drm_device *ddev, unsigned long flags);@@ -95,4 +99,23 @@ int dvbe_vesa_damage(struct dvbe_device *dvbe, struct dvbe_framebuffer *fb,
unsigned int flags, unsigned int color,
struct drm_clip_rect *clips, unsigned int num);
+/* fbdev helpers */
+
+#ifdef CONFIG_DRM_DVBE_FBDEV
+
+void dvbe_fbdev_init(struct dvbe_device *dvbe);
+void dvbe_fbdev_cleanup(struct dvbe_device *dvbe);
+
+#else /* CONFIG_DRM_DVBE_FBDEV */
+
+static inline void dvbe_fbdev_init(struct dvbe_device *dvbe)
+{
+}
+
+static inline void dvbe_fbdev_cleanup(struct dvbe_device *dvbe)
+{
+}
+
+#endif /* CONFIG_DRM_DVBE_FBDEV */
+
#endif /* DVBE_DRV_H */diff --git a/drivers/gpu/drm/dvbe/dvbe_fbdev.c b/drivers/gpu/drm/dvbe/dvbe_fbdev.c
new file mode 100644
index 0000000..0e22e12
--- /dev/null
+++ b/drivers/gpu/drm/dvbe/dvbe_fbdev.c
@@ -0,0 +1,235 @@
+/*
+ * DRM VESA BIOS Extension Driver
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/*
+ * fbdev compatibility layer
+ * This provides an fbdev framebuffer device for the DVBE driver. It is
+ * based on the old vesafb.c driver:
+ * (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include "dvbe.h"
+
+struct dvbe_fbdev {
+ struct dvbe_device *dvbe;
+ u32 palette[256];
+};
+
+static int dvbe_fbdev_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ /*
+ * Set a single color register. The values supplied are
+ * already rounded down to the hardware's capabilities
+ * (according to the entries in the `var' structure). Return != 0 for
+ * invalid regno.
+ */
+
+ if (regno >= info->cmap.len)
+ return 1;
+ if (info->var.bits_per_pixel = 8)
+ return -EINVAL;
+ if (regno >= 16)
+ return 0;
+
+ switch (info->var.bits_per_pixel) {
+ case 16:
+ if (info->var.red.offset = 10) {
+ /* 1:5:5:5 */
+ ((u32*) (info->pseudo_palette))[regno] + ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) |
+ ((blue & 0xf800) >> 11);
+ } else {
+ /* 0:5:6:5 */
+ ((u32*) (info->pseudo_palette))[regno] + ((red & 0xf800) ) |
+ ((green & 0xfc00) >> 5) |
+ ((blue & 0xf800) >> 11);
+ }
+ break;
+ case 24:
+ case 32:
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ ((u32*) (info->pseudo_palette))[regno] + (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset);
+ break;
+ }
+
+ return 0;
+}
+
+static void dvbe_fbdev_free(struct dvbe_device *dvbe, struct fb_info *info)
+{
+ dev_info(dvbe->ddev->dev, "fbdev cleanup\n");
+ fb_dealloc_cmap(&info->cmap);
+ framebuffer_release(info);
+}
+
+static void dvbe_fbdev_destroy(struct fb_info *info)
+{
+ struct dvbe_fbdev *fb = info->par;
+ struct dvbe_device *dvbe = fb->dvbe;
+
+ mutex_lock(&dvbe->ddev->struct_mutex);
+ info = dvbe->fbdev;
+ dvbe->fbdev = NULL;
+ mutex_unlock(&dvbe->ddev->struct_mutex);
+
+ if (info)
+ dvbe_fbdev_free(dvbe, info);
+}
+
+static struct fb_ops dvbe_fbdev_ops = {
+ .owner = THIS_MODULE,
+ .fb_destroy = dvbe_fbdev_destroy,
+ .fb_setcolreg = dvbe_fbdev_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+void dvbe_fbdev_init(struct dvbe_device *dvbe)
+{
+ struct dvbe_fbdev *fb;
+ struct fb_info *info;
+ int ret;
+
+ info = framebuffer_alloc(sizeof(struct dvbe_fbdev), dvbe->ddev->dev);
+ if (!info) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ fb = info->par;
+ fb->dvbe = dvbe;
+ info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE;
+ info->pseudo_palette = fb->palette;
+ info->screen_base = dvbe->vbe_map;
+ info->fbops = &dvbe_fbdev_ops;
+
+ strncpy(info->fix.id, "dvbedrmfb", 15);
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.accel = FB_ACCEL_NONE;
+ info->fix.smem_start = (unsigned long)dvbe->vbe_addr;
+ info->fix.smem_len = dvbe->vbe_size;
+ info->fix.line_length = dvbe->vbe_stride;
+
+ if (dvbe->vbe_bpp = 8)
+ info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
+ else
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->var.vmode = FB_VMODE_NONINTERLACED;
+ info->var.bits_per_pixel = dvbe->vbe_bpp;
+ info->var.xres = dvbe->vbe_width;
+ info->var.yres = dvbe->vbe_height;
+ info->var.xres_virtual = info->var.xres;
+ info->var.yres_virtual = info->var.yres;
+
+ /* some dummy values for timing to make fbset happy */
+ info->var.pixclock = 10000000 / info->var.xres * 1000 / info->var.yres;
+ info->var.left_margin = (info->var.xres / 8) & 0xf8;
+ info->var.right_margin = 32;
+ info->var.upper_margin = 16;
+ info->var.lower_margin = 4;
+ info->var.hsync_len = (info->var.xres / 8) & 0xf8;
+ info->var.vsync_len = 4;
+
+ info->var.red.offset = dvbe->vbe_red_pos;
+ info->var.red.length = dvbe->vbe_red_size;
+ info->var.green.offset = dvbe->vbe_green_pos;
+ info->var.green.length = dvbe->vbe_green_size;
+ info->var.blue.offset = dvbe->vbe_blue_pos;
+ info->var.blue.length = dvbe->vbe_blue_size;
+
+ if (info->var.bits_per_pixel <= 8) {
+ info->var.red.length = info->var.bits_per_pixel;
+ info->var.green.length = info->var.bits_per_pixel;
+ info->var.blue.length = info->var.bits_per_pixel;
+ }
+
+ info->apertures = alloc_apertures(1);
+ if (!info->apertures) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+ info->apertures->ranges[0].base = (unsigned long)dvbe->vbe_addr;
+ info->apertures->ranges[0].size = dvbe->vbe_vsize;
+
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret < 0)
+ goto err_free;
+
+ /*
+ * TODO: Fix register_framebuffer() to not reset refcounts!
+ * So register_framebuffer() resets the refcnt of \info to 1. However,
+ * any other context might call remove_conflicting_framebuffers()
+ * before our call to register_framebuffer() returns.
+ * remove_conflicting_framebuffers() calls unregister_framebuffer()
+ * which then calls put_fb_info() and destroys \info by calling our
+ * fb_destroy() callback.
+ * To summarize, \info might be dead after register_framebuffer()
+ * returns so don't access it afterwards. There is _no_ reliable way to
+ * detect that so don't use \info at all now.
+ * Instead we lock all accesses around dvbe->fbdev and the first one who
+ * resets it is responsible of freeing it.
+ */
+
+ dvbe->fbdev = info;
+ ret = register_framebuffer(info);
+ if (ret < 0)
+ goto err_cmap;
+
+ mutex_lock(&dvbe->ddev->struct_mutex);
+ if (dvbe->fbdev)
+ dev_info(dvbe->ddev->dev, "fbdev frontend %s as fb%d\n",
+ info->fix.id, info->node);
+ mutex_unlock(&dvbe->ddev->struct_mutex);
+
+ return;
+
+err_cmap:
+ fb_dealloc_cmap(&info->cmap);
+err_free:
+ framebuffer_release(info);
+err_out:
+ dev_warn(dvbe->ddev->dev, "cannot create fbdev frontend (%d)\n", ret);
+}
+
+void dvbe_fbdev_cleanup(struct dvbe_device *dvbe)
+{
+ struct fb_info *info;
+
+ mutex_lock(&dvbe->ddev->struct_mutex);
+ info = dvbe->fbdev;
+ dvbe->fbdev = NULL;
+ mutex_unlock(&dvbe->ddev->struct_mutex);
+
+ if (!info)
+ return;
+
+ unregister_framebuffer(info);
+ dvbe_fbdev_free(dvbe, info);
+}diff --git a/drivers/gpu/drm/dvbe/dvbe_main.c b/drivers/gpu/drm/dvbe/dvbe_main.c
index c418310..95241a6 100644
--- a/drivers/gpu/drm/dvbe/dvbe_main.c
+++ b/drivers/gpu/drm/dvbe/dvbe_main.c
@@ -408,6 +408,7 @@ int dvbe_drm_load(struct drm_device *ddev, unsigned long flags)
if (ret)
goto err_cleanup;
+ dvbe_fbdev_init(dvbe);
return 0;
err_cleanup:
@@ -422,6 +423,7 @@ int dvbe_drm_unload(struct drm_device *ddev)
{
struct dvbe_device *dvbe = ddev->dev_private;
+ dvbe_fbdev_cleanup(dvbe);
drm_mode_config_cleanup(ddev);
dvbe_vesa_cleanup(dvbe);
kfree(dvbe);--
1.8.1.3