diff options
author | Bernie Thompson <bernie@plugable.com> | 2010-02-15 06:46:26 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-03-03 16:43:00 -0800 |
commit | 3e8f3d6fa672896096068296658a769649186150 (patch) | |
tree | db79b94adff407e3ac9f6c9a17ec78de1060f97b /drivers | |
parent | 530f43a8a69d9fa10ec6d30652244bb5e2a29694 (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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/staging/udlfb/udlfb.c | 124 |
1 files changed, 121 insertions, 3 deletions
diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index 22f3dc310ff2..939b8faba0ff 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); |