aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernie Thompson <bernie@plugable.com>2010-02-15 06:46:26 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2010-03-03 16:43:00 -0800
commit3e8f3d6fa672896096068296658a769649186150 (patch)
treedb79b94adff407e3ac9f6c9a17ec78de1060f97b
parent530f43a8a69d9fa10ec6d30652244bb5e2a29694 (diff)
Staging: udlfb: Support for fbdev mmap clients (defio)
Add support for fbdev mmap clients who don't send damage ioctls Because DisplayLink devices are out on the other end of usb, their "framebuffer" is just normal system memory. So memory mapped writes don't automatically trigger anything. So up to this point, standard fbdev clients who rely on mmap() will get an unchanging screen. This patch makes udlfb a client of Jaya Kumar's defio framework - which sets up page fault triggers, and those faults are accumulated and sent to udlfb on a defferred basis, to process as damage notifications for the framebuffer. Because this involves more overhead than a notification directly from the application (e.g. just passing on X DAMAGE extension notifications), a sysfs attribute is provided by udlfb to control defio support. /sys/class/graphics/fb*/use_defio - writing a "0" to this file before calling mmap() causes defio to not be initialized - instead udlfb will rely on getting damage notifications directly through the damage ioctl. There are unsolved rendering problems with defio (horizontal dead regions on framebuffer, that accumulate over time) which still needs a fix. Signed-off-by: Bernie Thompson <bernie@plugable.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/staging/udlfb/udlfb.c124
1 files changed, 121 insertions, 3 deletions
diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c
index 22f3dc310ff..939b8faba0f 100644
--- a/drivers/staging/udlfb/udlfb.c
+++ b/drivers/staging/udlfb/udlfb.c
@@ -63,6 +63,11 @@ static int dlfb_submit_urb(struct dlfb_data *dev, struct urb * urb, size_t len);
static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size);
static void dlfb_free_urb_list(struct dlfb_data *dev);
+/* other symbols with dependents */
+#ifdef CONFIG_FB_DEFERRED_IO
+static struct fb_deferred_io dlfb_defio;
+#endif
+
/*
* Inserts a specific DisplayLink controller command into the provided
* buffer.
@@ -717,9 +722,59 @@ dlfb_ops_setcolreg(unsigned regno, unsigned red, unsigned green,
return err;
}
+/*
+ * It's common for several clients to have framebuffer open simultaneously.
+ * e.g. both fbcon and X. Makes things interesting.
+ */
+static int dlfb_ops_open(struct fb_info *info, int user)
+{
+ struct dlfb_data *dev = info->par;
+
+/* if (user == 0)
+ * We could special case kernel mode clients (fbcon) here
+ */
+
+ mutex_lock(&dev->fb_open_lock);
+
+ dev->fb_count++;
+
+#ifdef CONFIG_FB_DEFERRED_IO
+ if ((atomic_read(&dev->use_defio)) && (info->fbdefio == NULL)) {
+ /* enable defio */
+ info->fbdefio = &dlfb_defio;
+ fb_deferred_io_init(info);
+ }
+#endif
+
+ dl_notice("open /dev/fb%d user=%d fb_info=%p count=%d\n",
+ info->node, user, info, dev->fb_count);
+
+ mutex_unlock(&dev->fb_open_lock);
+
+ return 0;
+}
+
static int dlfb_ops_release(struct fb_info *info, int user)
{
- struct dlfb_data *dev_info = info->par;
+ struct dlfb_data *dev = info->par;
+
+ mutex_lock(&dev->fb_open_lock);
+
+ dev->fb_count--;
+
+#ifdef CONFIG_FB_DEFERRED_IO
+ if ((dev->fb_count == 0) && (info->fbdefio)) {
+ fb_deferred_io_cleanup(info);
+ info->fbdefio = NULL;
+ info->fbops->fb_mmap = dlfb_ops_mmap;
+ }
+#endif
+
+ dl_notice("release /dev/fb%d user=%d count=%d\n",
+ info->node, user, dev->fb_count);
+
+ mutex_unlock(&dev->fb_open_lock);
+
return 0;
}
@@ -735,6 +790,8 @@ static void dlfb_delete(struct kref *kref)
if (dev->backing_buffer)
vfree(dev->backing_buffer);
+ mutex_destroy(&dev->fb_open_lock);
+
kfree(dev);
}
@@ -853,6 +910,7 @@ static struct fb_ops dlfb_ops = {
.fb_imageblit = dlfb_ops_imageblit,
.fb_mmap = dlfb_ops_mmap,
.fb_ioctl = dlfb_ops_ioctl,
+ .fb_open = dlfb_ops_open,
.fb_release = dlfb_ops_release,
.fb_blank = dlfb_ops_blank,
.fb_check_var = dlfb_ops_check_var,
@@ -1068,6 +1126,68 @@ static struct device_attribute fb_device_attrs[] = {
__ATTR_RW(use_defio),
};
+#ifdef CONFIG_FB_DEFERRED_IO
+static void dlfb_dpy_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ struct page *cur;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct dlfb_data *dev = info->par;
+ struct urb *urb;
+ char *cmd;
+ cycles_t start_cycles, end_cycles;
+ int bytes_sent = 0;
+ int bytes_identical = 0;
+ int bytes_rendered = 0;
+ int fault_count = 0;
+
+ if (!atomic_read(&dev->use_defio))
+ return;
+
+ if (!atomic_read(&dev->usb_active))
+ return;
+
+ start_cycles = get_cycles();
+
+ urb = dlfb_get_urb(dev);
+ if (!urb)
+ return;
+ cmd = urb->transfer_buffer;
+
+ /* walk the written page list and render each to device */
+ list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+ dlfb_render_hline(dev, &urb, (char *) info->fix.smem_start,
+ &cmd, cur->index << PAGE_SHIFT,
+ PAGE_SIZE, &bytes_identical, &bytes_sent);
+ bytes_rendered += PAGE_SIZE;
+ fault_count++;
+ }
+
+ if (cmd > (char *) urb->transfer_buffer) {
+ /* Send partial buffer remaining before exiting */
+ int len = cmd - (char *) urb->transfer_buffer;
+ dlfb_submit_urb(dev, urb, len);
+ bytes_sent += len;
+ } else
+ dlfb_urb_completion(urb);
+
+ atomic_add(fault_count, &dev->defio_fault_count);
+ atomic_add(bytes_sent, &dev->bytes_sent);
+ atomic_add(bytes_identical, &dev->bytes_identical);
+ atomic_add(bytes_rendered, &dev->bytes_rendered);
+ end_cycles = get_cycles();
+ atomic_add(((unsigned int) ((end_cycles - start_cycles)
+ >> 10)), /* Kcycles */
+ &dev->cpu_kcycles_used);
+}
+
+static struct fb_deferred_io dlfb_defio = {
+ .delay = 5,
+ .deferred_io = dlfb_dpy_deferred_io,
+};
+
+#endif
+
/*
* This is necessary before we can communicate with the display controller.
*/
@@ -1193,11 +1313,9 @@ static int dlfb_usb_probe(struct usb_interface *interface,
/* ready to begin using device */
-/*
#ifdef CONFIG_FB_DEFERRED_IO
atomic_set(&dev->use_defio, 1);
#endif
-*/
atomic_set(&dev->usb_active, 1);
dlfb_select_std_channel(dev);