From d46ecb9f79db82ce6bef4baafbcae29ac809ea67 Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Sun, 5 Sep 2010 16:35:04 -0700 Subject: staging: udlfb: fbdev character read and write support Restore fbdev character read and write support Enables access to the framebuffer from simple reads and writes to /dev/fb0 (or fb1, ...). Low performance, because of extra copy and because udlfb must scan every pixel for changes. But very useful for testing and simple image display, etc. Signed-off-by: Bernie Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/udlfb/udlfb.c | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'drivers/staging/udlfb/udlfb.c') diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index c7e061e5e04d..a2bd5f7d2c50 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -602,6 +602,48 @@ int dlfb_handle_damage(struct dlfb_data *dev, int x, int y, return 0; } +static ssize_t dlfb_ops_read(struct fb_info *info, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t result = -ENOSYS; + +#if defined CONFIG_FB_SYS_FOPS || defined CONFIG_FB_SYS_FOPS_MODULE + result = fb_sys_read(info, buf, count, ppos); +#endif + + return result; +} + +/* + * Path triggered by usermode clients who write to filesystem + * e.g. cat filename > /dev/fb1 + * Not used by X Windows or text-mode console. But useful for testing. + * Slow because of extra copy and we must assume all pixels dirty. + */ +static ssize_t dlfb_ops_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t result = -ENOSYS; + struct dlfb_data *dev = info->par; + u32 offset = (u32) *ppos; + +#if defined CONFIG_FB_SYS_FOPS || defined CONFIG_FB_SYS_FOPS_MODULE + + result = fb_sys_write(info, buf, count, ppos); + + if (result > 0) { + int start = max((int)(offset / info->fix.line_length) - 1, 0); + int lines = min((u32)((result / info->fix.line_length) + 1), + (u32)info->var.yres); + + dlfb_handle_damage(dev, 0, start, info->var.xres, + lines, info->screen_base); + } +#endif + + return result; +} + /* hardware has native COPY command (see libdlo), but not worth it for fbcon */ static void dlfb_ops_copyarea(struct fb_info *info, const struct fb_copyarea *area) @@ -924,6 +966,8 @@ static int dlfb_ops_blank(int blank_mode, struct fb_info *info) static struct fb_ops dlfb_ops = { .owner = THIS_MODULE, + .fb_read = dlfb_ops_read, + .fb_write = dlfb_ops_write, .fb_setcolreg = dlfb_ops_setcolreg, .fb_fillrect = dlfb_ops_fillrect, .fb_copyarea = dlfb_ops_copyarea, -- cgit v1.2.3-59-g8ed1b From 9825f70f5cf465a8c31236409456d18de07fd166 Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Sun, 5 Sep 2010 16:35:10 -0700 Subject: staging: udlfb: add DPMS support Add ability to power off monitor with standard blanking interface Used by X servers and others to manage power of dislpay Signed-off-by: Bernie Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/udlfb/udlfb.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) (limited to 'drivers/staging/udlfb/udlfb.c') diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index a2bd5f7d2c50..0e03dd3227d1 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -108,13 +108,16 @@ static char *dlfb_vidreg_unlock(char *buf) /* * On/Off for driving the DisplayLink framebuffer to the display + * 0x00 H and V sync on + * 0x01 H and V sync off (screen blank but powered) + * 0x07 DPMS powerdown (requires modeset to come back) */ static char *dlfb_enable_hvsync(char *buf, bool enable) { if (enable) return dlfb_set_register(buf, 0x1F, 0x00); else - return dlfb_set_register(buf, 0x1F, 0x01); + return dlfb_set_register(buf, 0x1F, 0x07); } static char *dlfb_set_color_depth(char *buf, u8 selection) @@ -936,30 +939,31 @@ static int dlfb_ops_set_par(struct fb_info *info) return dlfb_set_video_mode(dev, &info->var); } +/* + * In order to come back from full DPMS off, we need to set the mode again + */ static int dlfb_ops_blank(int blank_mode, struct fb_info *info) { struct dlfb_data *dev = info->par; - char *bufptr; - struct urb *urb; - urb = dlfb_get_urb(dev); - if (!urb) - return 0; - bufptr = (char *) urb->transfer_buffer; + if (blank_mode != FB_BLANK_UNBLANK) { + char *bufptr; + struct urb *urb; - /* overloading usb_active. UNBLANK can conflict with teardown */ + urb = dlfb_get_urb(dev); + if (!urb) + return 0; - bufptr = dlfb_vidreg_lock(bufptr); - if (blank_mode != FB_BLANK_UNBLANK) { - atomic_set(&dev->usb_active, 0); + bufptr = (char *) urb->transfer_buffer; + bufptr = dlfb_vidreg_lock(bufptr); bufptr = dlfb_enable_hvsync(bufptr, false); + bufptr = dlfb_vidreg_unlock(bufptr); + + dlfb_submit_urb(dev, urb, bufptr - + (char *) urb->transfer_buffer); } else { - atomic_set(&dev->usb_active, 1); - bufptr = dlfb_enable_hvsync(bufptr, true); + dlfb_set_video_mode(dev, &info->var); } - bufptr = dlfb_vidreg_unlock(bufptr); - - dlfb_submit_urb(dev, urb, bufptr - (char *) urb->transfer_buffer); return 0; } -- cgit v1.2.3-59-g8ed1b From f11f4bc027414bc7db5dde49c55b7149a627b591 Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Sun, 5 Sep 2010 16:35:15 -0700 Subject: staging: udlfb: remove metrics_misc sysfs attribute Remove metrics_misc sysfs attribute Previously returned multiple values, which goes against sysfs guidelines. Had been used for debugging to figure out which of the multiple fbdev interfaces an app was using, but log file messages are sufficient. Signed-off-by: Bernie Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/udlfb/udlfb.c | 37 ------------------------------------- drivers/staging/udlfb/udlfb.h | 6 ------ 2 files changed, 43 deletions(-) (limited to 'drivers/staging/udlfb/udlfb.c') diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index 0e03dd3227d1..b027a1e1c32b 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -661,7 +661,6 @@ static void dlfb_ops_copyarea(struct fb_info *info, dlfb_handle_damage(dev, area->dx, area->dy, area->width, area->height, info->screen_base); #endif - atomic_inc(&dev->copy_count); } @@ -679,7 +678,6 @@ static void dlfb_ops_imageblit(struct fb_info *info, #endif - atomic_inc(&dev->blit_count); } static void dlfb_ops_fillrect(struct fb_info *info, @@ -695,8 +693,6 @@ static void dlfb_ops_fillrect(struct fb_info *info, rect->height, info->screen_base); #endif - atomic_inc(&dev->fill_count); - } static void dlfb_get_edid(struct dlfb_data *dev) @@ -754,7 +750,6 @@ static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, dlfb_handle_damage(dev, area->x, area->y, area->w, area->h, info->screen_base); - atomic_inc(&dev->damage_count); } return 0; @@ -1088,29 +1083,6 @@ static ssize_t metrics_cpu_kcycles_used_show(struct device *fbdev, atomic_read(&dev->cpu_kcycles_used)); } -static ssize_t metrics_misc_show(struct device *fbdev, - struct device_attribute *a, char *buf) { - struct fb_info *fb_info = dev_get_drvdata(fbdev); - struct dlfb_data *dev = fb_info->par; - return snprintf(buf, PAGE_SIZE, - "Calls to\ndamage: %u\nblit: %u\n" - "defio faults: %u\ncopy: %u\n" - "fill: %u\n\n" - "active framebuffer clients: %d\n" - "urbs available %d(%d)\n" - "Shadow framebuffer in use? %s\n" - "Any lost pixels? %s\n", - atomic_read(&dev->damage_count), - atomic_read(&dev->blit_count), - atomic_read(&dev->defio_fault_count), - atomic_read(&dev->copy_count), - atomic_read(&dev->fill_count), - dev->fb_count, - dev->urbs.available, dev->urbs.limit_sem.count, - (dev->backing_buffer) ? "yes" : "no", - atomic_read(&dev->lost_pixels) ? "yes" : "no"); -} - static ssize_t edid_show(struct file *filp, struct kobject *kobj, struct bin_attribute *a, char *buf, loff_t off, size_t count) { @@ -1145,11 +1117,6 @@ static ssize_t metrics_reset_store(struct device *fbdev, atomic_set(&dev->bytes_identical, 0); atomic_set(&dev->bytes_sent, 0); atomic_set(&dev->cpu_kcycles_used, 0); - atomic_set(&dev->blit_count, 0); - atomic_set(&dev->copy_count, 0); - atomic_set(&dev->fill_count, 0); - atomic_set(&dev->defio_fault_count, 0); - atomic_set(&dev->damage_count, 0); return count; } @@ -1190,7 +1157,6 @@ static struct device_attribute fb_device_attrs[] = { __ATTR_RO(metrics_bytes_identical), __ATTR_RO(metrics_bytes_sent), __ATTR_RO(metrics_cpu_kcycles_used), - __ATTR_RO(metrics_misc), __ATTR(metrics_reset, S_IWUGO, NULL, metrics_reset_store), __ATTR_RW(use_defio), }; @@ -1208,7 +1174,6 @@ static void dlfb_dpy_deferred_io(struct fb_info *info, int bytes_sent = 0; int bytes_identical = 0; int bytes_rendered = 0; - int fault_count = 0; if (!atomic_read(&dev->use_defio)) return; @@ -1229,7 +1194,6 @@ static void dlfb_dpy_deferred_io(struct fb_info *info, &cmd, cur->index << PAGE_SHIFT, PAGE_SIZE, &bytes_identical, &bytes_sent); bytes_rendered += PAGE_SIZE; - fault_count++; } if (cmd > (char *) urb->transfer_buffer) { @@ -1240,7 +1204,6 @@ static void dlfb_dpy_deferred_io(struct fb_info *info, } 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); diff --git a/drivers/staging/udlfb/udlfb.h b/drivers/staging/udlfb/udlfb.h index b07a69371f1f..f32a220993d1 100644 --- a/drivers/staging/udlfb/udlfb.h +++ b/drivers/staging/udlfb/udlfb.h @@ -54,12 +54,6 @@ struct dlfb_data { atomic_t bytes_identical; /* saved effort with backbuffer comparison */ atomic_t bytes_sent; /* to usb, after compression including overhead */ atomic_t cpu_kcycles_used; /* transpired during pixel processing */ - /* interface usage metrics. Clients can call driver via several */ - atomic_t blit_count; - atomic_t copy_count; - atomic_t fill_count; - atomic_t damage_count; - atomic_t defio_fault_count; }; #define NR_USB_REQUEST_I2C_SUB_IO 0x02 -- cgit v1.2.3-59-g8ed1b From 33077b8d3042e01da61924973e372abe589ba297 Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Sun, 5 Sep 2010 16:35:19 -0700 Subject: staging: udlfb: revamp reference handling to insure successful shutdown Revamp reference handling and synchronization for unload/shutdown Udlfb is a "virtual" framebuffer device that really exists on two separate stacks: at the bottom of the framebuffer interface, and on top of USB. During unload, there's no guarantee which one will tear down first. So reference counting must be solid to handle all possibilities and not access anything once its gone. Signed-off-by: Bernie Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/udlfb/udlfb.c | 175 ++++++++++++++++++++++++------------------ drivers/staging/udlfb/udlfb.h | 20 +++-- 2 files changed, 115 insertions(+), 80 deletions(-) (limited to 'drivers/staging/udlfb/udlfb.c') diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index b027a1e1c32b..c9ac6879e933 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -25,6 +25,8 @@ #include #include #include +#include + #include "udlfb.h" @@ -300,7 +302,6 @@ static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) unsigned long size = vma->vm_end - vma->vm_start; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long page, pos; - struct dlfb_data *dev = info->par; dl_notice("MMAP: %lu %u\n", offset + size, info->fix.smem_len); @@ -785,6 +786,7 @@ dlfb_ops_setcolreg(unsigned regno, unsigned red, unsigned green, /* * It's common for several clients to have framebuffer open simultaneously. * e.g. both fbcon and X. Makes things interesting. + * Assumes caller is holding info->lock (for open and release at least) */ static int dlfb_ops_open(struct fb_info *info, int user) { @@ -794,10 +796,14 @@ static int dlfb_ops_open(struct fb_info *info, int user) * We could special case kernel mode clients (fbcon) here */ - mutex_lock(&dev->fb_open_lock); + /* If the USB device is gone, we don't accept new opens */ + if (dev->virtualized) + return -ENODEV; dev->fb_count++; + kref_get(&dev->kref); + #ifdef CONFIG_FB_DEFERRED_IO if ((atomic_read(&dev->use_defio)) && (info->fbdefio == NULL)) { /* enable defio */ @@ -809,32 +815,6 @@ static int dlfb_ops_open(struct fb_info *info, int user) 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->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; } @@ -843,25 +823,33 @@ static int dlfb_ops_release(struct fb_info *info, int user) * and all references to our device instance (dlfb_data) are released. * Every transaction must have a reference, so we know are fully spun down */ -static void dlfb_delete(struct kref *kref) +static void dlfb_free(struct kref *kref) { struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref); + /* this function will wait for all in-flight urbs to complete */ + if (dev->urbs.count > 0) + dlfb_free_urb_list(dev); + if (dev->backing_buffer) vfree(dev->backing_buffer); - mutex_destroy(&dev->fb_open_lock); + kfree(dev->edid); + + dl_warn("freeing dlfb_data %p\n", dev); kfree(dev); } -/* - * Called by fbdev as last part of unregister_framebuffer() process - * No new clients can open connections. Deallocate everything fb_info. - */ -static void dlfb_ops_destroy(struct fb_info *info) + +static void dlfb_free_framebuffer_work(struct work_struct *work) { - struct dlfb_data *dev = info->par; + struct dlfb_data *dev = container_of(work, struct dlfb_data, + free_framebuffer_work.work); + struct fb_info *info = dev->info; + int node = info->node; + + unregister_framebuffer(info); if (info->cmap.len != 0) fb_dealloc_cmap(&info->cmap); @@ -872,10 +860,45 @@ static void dlfb_ops_destroy(struct fb_info *info) fb_destroy_modelist(&info->modelist); + dev->info = 0; + + /* Assume info structure is freed after this point */ framebuffer_release(info); - /* ref taken before register_framebuffer() for dlfb_data clients */ - kref_put(&dev->kref, dlfb_delete); + dl_warn("fb_info for /dev/fb%d has been freed\n", node); + + /* ref taken in probe() as part of registering framebfufer */ + kref_put(&dev->kref, dlfb_free); +} + +/* + * Assumes caller is holding info->lock mutex (for open and release at least) + */ +static int dlfb_ops_release(struct fb_info *info, int user) +{ + struct dlfb_data *dev = info->par; + + dev->fb_count--; + + /* We can't free fb_info here - fbmem will touch it when we return */ + if (dev->virtualized && (dev->fb_count == 0)) + schedule_delayed_work(&dev->free_framebuffer_work, HZ); + +#ifdef CONFIG_FB_DEFERRED_IO + if ((dev->fb_count == 0) && (info->fbdefio)) { + fb_deferred_io_cleanup(info); + kfree(info->fbdefio); + info->fbdefio = NULL; + info->fbops->fb_mmap = dlfb_ops_mmap; + } +#endif + + dl_warn("released /dev/fb%d user=%d count=%d\n", + info->node, user, dev->fb_count); + + kref_put(&dev->kref, dlfb_free); + + return 0; } /* @@ -1244,13 +1267,12 @@ static int dlfb_usb_probe(struct usb_interface *interface, { struct usb_device *usbdev; struct dlfb_data *dev; - struct fb_info *info; + struct fb_info *info = 0; int videomemorysize; int i; unsigned char *videomemory; int retval = -ENOMEM; struct fb_var_screeninfo *var; - int registered = 0; u16 *pix_framebuffer; /* usb initialization */ @@ -1277,8 +1299,6 @@ static int dlfb_usb_probe(struct usb_interface *interface, goto error; } - mutex_init(&dev->fb_open_lock); - /* We don't register a new USB class. Our client interface is fbdev */ /* allocates framebuffer driver structure, not framebuffer memory */ @@ -1288,6 +1308,7 @@ static int dlfb_usb_probe(struct usb_interface *interface, dl_err("framebuffer_alloc failed\n"); goto error; } + dev->info = info; info->par = dev; info->pseudo_palette = dev->pseudo_palette; @@ -1343,6 +1364,9 @@ static int dlfb_usb_probe(struct usb_interface *interface, goto error; } + INIT_DELAYED_WORK(&dev->free_framebuffer_work, + dlfb_free_framebuffer_work); + /* ready to begin using device */ #ifdef CONFIG_FB_DEFERRED_IO @@ -1367,7 +1391,6 @@ static int dlfb_usb_probe(struct usb_interface *interface, dl_err("register_framebuffer failed %d\n", retval); goto error; } - registered = 1; for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) device_create_file(info->dev, &fb_device_attrs[i]); @@ -1383,15 +1406,25 @@ static int dlfb_usb_probe(struct usb_interface *interface, error: if (dev) { - if (registered) { - unregister_framebuffer(info); - dlfb_ops_destroy(info); - } else - kref_put(&dev->kref, dlfb_delete); - if (dev->urbs.count > 0) - dlfb_free_urb_list(dev); - kref_put(&dev->kref, dlfb_delete); /* last ref from kref_init */ + if (info) { + if (info->cmap.len != 0) + fb_dealloc_cmap(&info->cmap); + if (info->monspecs.modedb) + fb_destroy_modedb(info->monspecs.modedb); + if (info->screen_base) + vfree(info->screen_base); + + fb_destroy_modelist(&info->modelist); + + framebuffer_release(info); + } + + if (dev->backing_buffer) + vfree(dev->backing_buffer); + + kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ + kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ /* dev has been deallocated. Do not dereference */ } @@ -1408,27 +1441,27 @@ static void dlfb_usb_disconnect(struct usb_interface *interface) dev = usb_get_intfdata(interface); info = dev->info; - /* when non-active we'll update virtual framebuffer, but no new urbs */ - atomic_set(&dev->usb_active, 0); + dl_info("USB disconnect starting\n"); - usb_set_intfdata(interface, NULL); + /* we virtualize until all fb clients release. Then we free */ + dev->virtualized = true; + + /* When non-active we'll update virtual framebuffer, but no new urbs */ + atomic_set(&dev->usb_active, 0); + /* remove udlfb's sysfs interfaces */ for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) device_remove_file(info->dev, &fb_device_attrs[i]); - device_remove_bin_file(info->dev, &edid_attr); - /* this function will wait for all in-flight urbs to complete */ - dlfb_free_urb_list(dev); + usb_set_intfdata(interface, NULL); - if (info) { - dl_notice("Detaching /dev/fb%d\n", info->node); - unregister_framebuffer(info); - dlfb_ops_destroy(info); - } + /* if clients still have us open, will be freed on last close */ + if (dev->fb_count == 0) + schedule_delayed_work(&dev->free_framebuffer_work, 0); /* release reference taken by kref_init in probe() */ - kref_put(&dev->kref, dlfb_delete); + kref_put(&dev->kref, dlfb_free); /* consider dlfb_data freed */ @@ -1450,8 +1483,6 @@ static int __init dlfb_module_init(void) if (res) err("usb_register failed. Error number %d", res); - printk(KERN_INFO "VMODES initialized\n"); - return res; } @@ -1503,12 +1534,12 @@ static void dlfb_free_urb_list(struct dlfb_data *dev) /* keep waiting and freeing, until we've got 'em all */ while (count--) { - /* Timeout means a memory leak and/or fault */ - ret = down_timeout(&dev->urbs.limit_sem, FREE_URB_TIMEOUT); - if (ret) { - BUG_ON(ret); + + /* Getting interrupted means a leak, but ok at shutdown*/ + ret = down_interruptible(&dev->urbs.limit_sem); + if (ret) break; - } + spin_lock_irqsave(&dev->urbs.lock, flags); node = dev->urbs.list.next; /* have reserved one with sem */ @@ -1526,8 +1557,6 @@ static void dlfb_free_urb_list(struct dlfb_data *dev) kfree(node); } - kref_put(&dev->kref, dlfb_delete); - } static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) @@ -1577,8 +1606,6 @@ static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) dev->urbs.count = i; dev->urbs.available = i; - kref_get(&dev->kref); /* released in free_render_urbs() */ - dl_notice("allocated %d %d byte urbs\n", i, (int) size); return i; diff --git a/drivers/staging/udlfb/udlfb.h b/drivers/staging/udlfb/udlfb.h index f32a220993d1..7c46477af2a2 100644 --- a/drivers/staging/udlfb/udlfb.h +++ b/drivers/staging/udlfb/udlfb.h @@ -38,9 +38,9 @@ struct dlfb_data { struct urb_list urbs; struct kref kref; char *backing_buffer; - struct delayed_work deferred_work; - struct mutex fb_open_lock; int fb_count; + bool virtualized; /* true when physical usb device not present */ + struct delayed_work free_framebuffer_work; atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */ atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */ atomic_t use_defio; /* 0 = rely on ioctls and blit/copy/fill rects */ @@ -89,12 +89,20 @@ struct dlfb_data { /* remove once this gets added to sysfs.h */ #define __ATTR_RW(attr) __ATTR(attr, 0644, attr##_show, attr##_store) +/* + * udlfb is both a usb device, and a framebuffer device. + * They may exist at the same time, but during various stages + * inactivity, teardown, or "virtual" operation, only one or the + * other will exist (one will outlive the other). So we can't + * call the dev_*() macros, because we don't have a stable dev object. + */ #define dl_err(format, arg...) \ - dev_err(dev->gdev, "dlfb: " format, ## arg) + pr_err("udlfb: " format, ## arg) #define dl_warn(format, arg...) \ - dev_warn(dev->gdev, "dlfb: " format, ## arg) + pr_warning("udlfb: " format, ## arg) #define dl_notice(format, arg...) \ - dev_notice(dev->gdev, "dlfb: " format, ## arg) + pr_notice("udlfb: " format, ## arg) #define dl_info(format, arg...) \ - dev_info(dev->gdev, "dlfb: " format, ## arg) + pr_info("udlfb: " format, ## arg) + #endif -- cgit v1.2.3-59-g8ed1b From 18dffdf8913ab9e4ac7403a624879a15afa5262e Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Sun, 5 Sep 2010 16:35:23 -0700 Subject: staging: udlfb: enhance EDID and mode handling support Enhance EDID and mode table handling * Adds support for lower-end DL chips on higher-end monitors * Adds support for using fbdev standard EDID parsing and handling * Adds support for more dynamic reallocation of framebuffer, based on new EDID Queries EDID from hardware with several retries then queries DisplayLink chip for max pixel area via vendor descriptor. Uses existing fbdev mode handling service funcations to build mode list of common modes between adapter and monitor. This enables lower-end chips (e.g. DL-125) to successfully pick a compatible mode on higher end monitors (e.g. whose default mode is over 1280x1024). Reallocate framebuffer if necessary in response to new max mode. Signed-off-by: Bernie Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/udlfb/udlfb.c | 399 ++++++++++++++++++++++++++++++++---------- drivers/staging/udlfb/udlfb.h | 8 +- 2 files changed, 312 insertions(+), 95 deletions(-) (limited to 'drivers/staging/udlfb/udlfb.c') diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index c9ac6879e933..6977b71e8c3c 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -696,19 +696,32 @@ static void dlfb_ops_fillrect(struct fb_info *info, } -static void dlfb_get_edid(struct dlfb_data *dev) +static int dlfb_get_edid(struct dlfb_data *dev, char *edid, int len) { int i; int ret; - char rbuf[2]; + char *rbuf; - for (i = 0; i < sizeof(dev->edid); i++) { + rbuf = kmalloc(2, GFP_KERNEL); + if (!rbuf) + return 0; + + for (i = 0; i < len; i++) { ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), (0x02), (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, - 0); - dev->edid[i] = rbuf[1]; + HZ); + if (ret < 1) { + dl_err("Read EDID byte %d failed err %x\n", i, ret); + i--; + break; + } + edid[i] = rbuf[1]; } + + kfree(rbuf); + + return i; } static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, @@ -724,8 +737,7 @@ static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, /* TODO: Update X server to get this from sysfs instead */ if (cmd == DLFB_IOCTL_RETURN_EDID) { char *edid = (char *)arg; - dlfb_get_edid(dev); - if (copy_to_user(edid, dev->edid, sizeof(dev->edid))) + if (copy_to_user(edid, dev->edid, dev->edid_size)) return -EFAULT; return 0; } @@ -910,8 +922,13 @@ static int dlfb_is_valid_mode(struct fb_videomode *mode, { struct dlfb_data *dev = info->par; - if (mode->xres * mode->yres > dev->sku_pixel_limit) + if (mode->xres * mode->yres > dev->sku_pixel_limit) { + dl_warn("%dx%d beyond chip capabilities\n", + mode->xres, mode->yres); return 0; + } + + dl_info("%dx%d valid mode\n", mode->xres, mode->yres); return 1; } @@ -951,10 +968,27 @@ static int dlfb_ops_check_var(struct fb_var_screeninfo *var, static int dlfb_ops_set_par(struct fb_info *info) { struct dlfb_data *dev = info->par; + int result; + u16 *pix_framebuffer; + int i; dl_notice("set_par mode %dx%d\n", info->var.xres, info->var.yres); - return dlfb_set_video_mode(dev, &info->var); + result = dlfb_set_video_mode(dev, &info->var); + + if ((result == 0) && (dev->fb_count == 0)) { + + /* paint greenscreen */ + + pix_framebuffer = (u16 *) info->screen_base; + for (i = 0; i < info->fix.smem_len / 2; i++) + pix_framebuffer[i] = 0x37e6; + + dlfb_handle_damage(dev, 0, 0, info->var.xres, info->var.yres, + info->screen_base); + } + + return result; } /* @@ -1003,46 +1037,166 @@ static struct fb_ops dlfb_ops = { .fb_set_par = dlfb_ops_set_par, }; + +/* + * Assumes &info->lock held by caller + * Assumes no active clients have framebuffer open + */ +static int dlfb_realloc_framebuffer(struct dlfb_data *dev, struct fb_info *info) +{ + int retval = -ENOMEM; + int old_len = info->fix.smem_len; + int new_len; + unsigned char *old_fb = info->screen_base; + unsigned char *new_fb; + unsigned char *new_back; + + dl_warn("Reallocating framebuffer. Addresses will change!\n"); + + new_len = info->fix.line_length * info->var.yres; + + if (PAGE_ALIGN(new_len) > old_len) { + /* + * Alloc system memory for virtual framebuffer + */ + new_fb = vmalloc(new_len); + if (!new_fb) { + dl_err("Virtual framebuffer alloc failed\n"); + goto error; + } + + if (info->screen_base) { + memcpy(new_fb, old_fb, old_len); + vfree(info->screen_base); + } + + info->screen_base = new_fb; + info->fix.smem_len = PAGE_ALIGN(new_len); + info->fix.smem_start = (unsigned long) new_fb; + info->flags = udlfb_info_flags; + + /* + * Second framebuffer copy to mirror the framebuffer state + * on the physical USB device. We can function without this. + * But with imperfect damage info we may send pixels over USB + * that were, in fact, unchanged - wasting limited USB bandwidth + */ + new_back = vmalloc(new_len); + if (!new_back) + dl_info("No shadow/backing buffer allcoated\n"); + else { + if (dev->backing_buffer) + vfree(dev->backing_buffer); + dev->backing_buffer = new_back; + memset(dev->backing_buffer, 0, new_len); + } + } + + retval = 0; + +error: + return retval; +} + /* - * Calls dlfb_get_edid() to query the EDID of attached monitor via usb cmds - * Then parses EDID into three places used by various parts of fbdev: + * 1) Get EDID from hw, or use sw default + * 2) Parse into various fb_info structs + * 3) Allocate virtual framebuffer memory to back highest res mode + * + * Parses EDID into three places used by various parts of fbdev: * fb_var_screeninfo contains the timing of the monitor's preferred mode * fb_info.monspecs is full parsed EDID info, including monspecs.modedb * fb_info.modelist is a linked list of all monitor & VESA modes which work * * If EDID is not readable/valid, then modelist is all VESA modes, * monspecs is NULL, and fb_var_screeninfo is set to safe VESA mode - * Returns 0 if EDID parses successfully + * Returns 0 if successful */ -static int dlfb_parse_edid(struct dlfb_data *dev, - struct fb_var_screeninfo *var, - struct fb_info *info) +static int dlfb_setup_modes(struct dlfb_data *dev, + struct fb_info *info, + char *default_edid, size_t default_edid_size) { int i; const struct fb_videomode *default_vmode = NULL; int result = 0; + char *edid; + int tries = 3; + + if (info->dev) /* only use mutex if info has been registered */ + mutex_lock(&info->lock); + + edid = kmalloc(MAX_EDID_SIZE, GFP_KERNEL); + if (!edid) { + result = -ENOMEM; + goto error; + } fb_destroy_modelist(&info->modelist); memset(&info->monspecs, 0, sizeof(info->monspecs)); - dlfb_get_edid(dev); - fb_edid_to_monspecs(dev->edid, &info->monspecs); + /* + * Try to (re)read EDID from hardware first + * EDID data may return, but not parse as valid + * Try again a few times, in case of e.g. analog cable noise + */ + while (tries--) { + + i = dlfb_get_edid(dev, edid, MAX_EDID_SIZE); + + if (i >= MIN_EDID_SIZE) + fb_edid_to_monspecs(edid, &info->monspecs); + + if (info->monspecs.modedb_len > 0) { + dev->edid = edid; + dev->edid_size = i; + break; + } + } + /* If that fails, use a previously returned EDID if available */ + if (info->monspecs.modedb_len == 0) { + + dl_err("Unable to get valid EDID from device/display\n"); + + if (dev->edid) { + fb_edid_to_monspecs(dev->edid, &info->monspecs); + if (info->monspecs.modedb_len > 0) + dl_err("Using previously queried EDID\n"); + } + } + + /* If that fails, use the default EDID we were handed */ + if (info->monspecs.modedb_len == 0) { + if (default_edid_size >= MIN_EDID_SIZE) { + fb_edid_to_monspecs(default_edid, &info->monspecs); + if (info->monspecs.modedb_len > 0) { + memcpy(edid, default_edid, default_edid_size); + dev->edid = edid; + dev->edid_size = default_edid_size; + dl_err("Using default/backup EDID\n"); + } + } + } + + /* If we've got modes, let's pick a best default mode */ if (info->monspecs.modedb_len > 0) { for (i = 0; i < info->monspecs.modedb_len; i++) { if (dlfb_is_valid_mode(&info->monspecs.modedb[i], info)) fb_add_videomode(&info->monspecs.modedb[i], &info->modelist); + else /* if we've removed top/best mode */ + info->monspecs.misc &= ~FB_MISC_1ST_DETAIL; } default_vmode = fb_find_best_display(&info->monspecs, &info->modelist); - } else { - struct fb_videomode fb_vmode = {0}; + } - dl_err("Unable to get valid EDID from device/display\n"); - result = 1; + /* If everything else has failed, fall back to safe default mode */ + if (default_vmode == NULL) { + + struct fb_videomode fb_vmode = {0}; /* * Add the standard VESA modes to our modelist @@ -1068,8 +1222,30 @@ static int dlfb_parse_edid(struct dlfb_data *dev, &info->modelist); } - fb_videomode_to_var(var, default_vmode); - dlfb_var_color_format(var); + /* If we have good mode and no active clients*/ + if ((default_vmode != NULL) && (dev->fb_count == 0)) { + + fb_videomode_to_var(&info->var, default_vmode); + dlfb_var_color_format(&info->var); + + /* + * with mode size info, we can now alloc our framebuffer. + */ + memcpy(&info->fix, &dlfb_fix, sizeof(dlfb_fix)); + info->fix.line_length = info->var.xres * + (info->var.bits_per_pixel / 8); + + result = dlfb_realloc_framebuffer(dev, info); + + } else + result = -EINVAL; + +error: + if (edid && (dev->edid != edid)) + kfree(edid); + + if (info->dev) + mutex_unlock(&info->lock); return result; } @@ -1106,24 +1282,27 @@ static ssize_t metrics_cpu_kcycles_used_show(struct device *fbdev, atomic_read(&dev->cpu_kcycles_used)); } -static ssize_t edid_show(struct file *filp, struct kobject *kobj, - struct bin_attribute *a, +static ssize_t edid_show( + struct file *filp, + struct kobject *kobj, struct bin_attribute *a, char *buf, loff_t off, size_t count) { struct device *fbdev = container_of(kobj, struct device, kobj); struct fb_info *fb_info = dev_get_drvdata(fbdev); struct dlfb_data *dev = fb_info->par; - char *edid = &dev->edid[0]; - const size_t size = sizeof(dev->edid); - if (dlfb_parse_edid(dev, &fb_info->var, fb_info)) + if (dev->edid == NULL) return 0; - if (off >= size) + if ((off >= dev->edid_size) || (count > dev->edid_size)) return 0; - if (off + count > size) - count = size - off; - memcpy(buf, edid + off, count); + if (off + count > dev->edid_size) + count = dev->edid_size - off; + + dl_info("sysfs edid copy %p to %p, %d bytes\n", + dev->edid, buf, (int) count); + + memcpy(buf, dev->edid, count); return count; } @@ -1261,19 +1440,82 @@ static int dlfb_select_std_channel(struct dlfb_data *dev) return ret; } +static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev, + struct usb_device *usbdev) +{ + char *desc; + char *buf; + char *desc_end; + + u8 total_len = 0; + + buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL); + if (!buf) + return false; + desc = buf; + + total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */ + 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE); + if (total_len > 5) { + dl_info("vendor descriptor length:%x data:%02x %02x %02x %02x" \ + "%02x %02x %02x %02x %02x %02x %02x\n", + total_len, desc[0], + desc[1], desc[2], desc[3], desc[4], desc[5], desc[6], + desc[7], desc[8], desc[9], desc[10]); + + if ((desc[0] != total_len) || /* descriptor length */ + (desc[1] != 0x5f) || /* vendor descriptor type */ + (desc[2] != 0x01) || /* version (2 bytes) */ + (desc[3] != 0x00) || + (desc[4] != total_len - 2)) /* length after type */ + goto unrecognized; + + desc_end = desc + total_len; + desc += 5; /* the fixed header we've already parsed */ + + while (desc < desc_end) { + u8 length; + u16 key; + + key = *((u16 *) desc); + desc += sizeof(u16); + length = *desc; + desc++; + + switch (key) { + case 0x0200: { /* max_area */ + u32 max_area; + max_area = le32_to_cpu(*((u32 *)desc)); + dl_warn("DL chip limited to %d pixel modes\n", + max_area); + dev->sku_pixel_limit = max_area; + break; + } + default: + break; + } + desc += length; + } + } + + goto success; +unrecognized: + /* allow udlfb to load for now even if firmware unrecognized */ + dl_err("Unrecognized vendor firmware descriptor\n"); + +success: + kfree(buf); + return true; +} static int dlfb_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *usbdev; - struct dlfb_data *dev; + struct dlfb_data *dev = 0; struct fb_info *info = 0; - int videomemorysize; int i; - unsigned char *videomemory; int retval = -ENOMEM; - struct fb_var_screeninfo *var; - u16 *pix_framebuffer; /* usb initialization */ @@ -1287,12 +1529,25 @@ static int dlfb_usb_probe(struct usb_interface *interface, /* we need to wait for both usb and fbdev to spin down on disconnect */ kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ - kref_get(&dev->kref); /* matching kref_put in .fb_destroy function*/ + kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ dev->udev = usbdev; dev->gdev = &usbdev->dev; /* our generic struct device * */ usb_set_intfdata(interface, dev); + dl_info("%s %s - serial #%s\n", + usbdev->manufacturer, usbdev->product, usbdev->serial); + dl_info("vid_%04x&pid_%04x&rev_%04x driver's dlfb_data struct at %p\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, + usbdev->descriptor.bcdDevice, dev); + + dev->sku_pixel_limit = 2048 * 1152; /* default to maximum */ + + if (!dlfb_parse_vendor_descriptor(dev, usbdev)) { + dl_err("firmware not recognized. Assume incompatible device\n"); + goto error; + } + if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { retval = -ENOMEM; dl_err("dlfb_alloc_urb_list failed\n"); @@ -1314,50 +1569,6 @@ static int dlfb_usb_probe(struct usb_interface *interface, info->pseudo_palette = dev->pseudo_palette; info->fbops = &dlfb_ops; - var = &info->var; - - /* TODO set limit based on actual SKU detection */ - dev->sku_pixel_limit = 2048 * 1152; - - INIT_LIST_HEAD(&info->modelist); - dlfb_parse_edid(dev, var, info); - - /* - * ok, now that we've got the size info, we can alloc our framebuffer. - */ - info->fix = dlfb_fix; - info->fix.line_length = var->xres * (var->bits_per_pixel / 8); - videomemorysize = info->fix.line_length * var->yres; - - /* - * The big chunk of system memory we use as a virtual framebuffer. - * TODO: Handle fbcon cursor code calling blit in interrupt context - */ - videomemory = vmalloc(videomemorysize); - if (!videomemory) { - retval = -ENOMEM; - dl_err("Virtual framebuffer alloc failed\n"); - goto error; - } - - info->screen_base = videomemory; - info->fix.smem_len = PAGE_ALIGN(videomemorysize); - info->fix.smem_start = (unsigned long) videomemory; - info->flags = udlfb_info_flags; - - - /* - * Second framebuffer copy, mirroring the state of the framebuffer - * on the physical USB device. We can function without this. - * But with imperfect damage info we may end up sending pixels over USB - * that were, in fact, unchanged -- wasting limited USB bandwidth - */ - dev->backing_buffer = vmalloc(videomemorysize); - if (!dev->backing_buffer) - dl_warn("No shadow/backing buffer allcoated\n"); - else - memset(dev->backing_buffer, 0, videomemorysize); - retval = fb_alloc_cmap(&info->cmap, 256, 0); if (retval < 0) { dl_err("fb_alloc_cmap failed %x\n", retval); @@ -1367,6 +1578,14 @@ static int dlfb_usb_probe(struct usb_interface *interface, INIT_DELAYED_WORK(&dev->free_framebuffer_work, dlfb_free_framebuffer_work); + INIT_LIST_HEAD(&info->modelist); + + retval = dlfb_setup_modes(dev, info, NULL, 0); + if (retval != 0) { + dl_err("unable to find common mode for display and adapter\n"); + goto error; + } + /* ready to begin using device */ #ifdef CONFIG_FB_DEFERRED_IO @@ -1375,17 +1594,9 @@ static int dlfb_usb_probe(struct usb_interface *interface, atomic_set(&dev->usb_active, 1); dlfb_select_std_channel(dev); - dlfb_ops_check_var(var, info); + dlfb_ops_check_var(&info->var, info); dlfb_ops_set_par(info); - /* paint greenscreen */ - pix_framebuffer = (u16 *) videomemory; - for (i = 0; i < videomemorysize / 2; i++) - pix_framebuffer[i] = 0x37e6; - - dlfb_handle_damage(dev, 0, 0, info->var.xres, info->var.yres, - videomemory); - retval = register_framebuffer(info); if (retval < 0) { dl_err("register_framebuffer failed %d\n", retval); @@ -1397,11 +1608,11 @@ static int dlfb_usb_probe(struct usb_interface *interface, device_create_bin_file(info->dev, &edid_attr); - dl_err("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." + dl_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." " Using %dK framebuffer memory\n", info->node, - var->xres, var->yres, + info->var.xres, info->var.yres, ((dev->backing_buffer) ? - videomemorysize * 2 : videomemorysize) >> 10); + info->fix.smem_len * 2 : info->fix.smem_len) >> 10); return 0; error: diff --git a/drivers/staging/udlfb/udlfb.h b/drivers/staging/udlfb/udlfb.h index 7c46477af2a2..d28e783482de 100644 --- a/drivers/staging/udlfb/udlfb.h +++ b/drivers/staging/udlfb/udlfb.h @@ -44,7 +44,8 @@ struct dlfb_data { atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */ atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */ atomic_t use_defio; /* 0 = rely on ioctls and blit/copy/fill rects */ - char edid[128]; + char *edid; /* null until we read edid from hw or get from sysfs */ + size_t edid_size; int sku_pixel_limit; int base16; int base8; @@ -64,6 +65,11 @@ struct dlfb_data { #define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE) #define WRITES_IN_FLIGHT (4) +#define MIN_EDID_SIZE 128 +#define MAX_EDID_SIZE 128 + +#define MAX_VENDOR_DESCRIPTOR_SIZE 256 + #define GET_URB_TIMEOUT HZ #define FREE_URB_TIMEOUT (HZ*2) -- cgit v1.2.3-59-g8ed1b From 1572f91cffd7c0d64ab4e0cec71e1d2d1a3349e1 Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Sun, 5 Sep 2010 16:35:27 -0700 Subject: staging: udlfb: fix big endian rendering error Fix big endian rendering bug (affects PowerPC) Thanks to Christian Melki at Ericsson for finding and suggesting patch. Signed-off-by: Bernie Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/udlfb/udlfb.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/staging/udlfb/udlfb.c') diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index 6977b71e8c3c..bcb4a1e9e713 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -414,15 +414,14 @@ static void dlfb_compress_hline( uint8_t *cmd_pixels_count_byte = 0; const uint16_t *raw_pixel_start = 0; const uint16_t *cmd_pixel_start, *cmd_pixel_end = 0; - const uint32_t be_dev_addr = cpu_to_be32(dev_addr); prefetchw((void *) cmd); /* pull in one cache line at least */ *cmd++ = 0xAF; *cmd++ = 0x6B; - *cmd++ = (uint8_t) ((be_dev_addr >> 8) & 0xFF); - *cmd++ = (uint8_t) ((be_dev_addr >> 16) & 0xFF); - *cmd++ = (uint8_t) ((be_dev_addr >> 24) & 0xFF); + *cmd++ = (uint8_t) ((dev_addr >> 16) & 0xFF); + *cmd++ = (uint8_t) ((dev_addr >> 8) & 0xFF); + *cmd++ = (uint8_t) ((dev_addr) & 0xFF); cmd_pixels_count_byte = cmd++; /* we'll know this later */ cmd_pixel_start = pixel; -- cgit v1.2.3-59-g8ed1b From 8ef8cc4fca4a92e77c0b5aa7a852a6c3d2fba844 Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Sun, 5 Sep 2010 16:35:31 -0700 Subject: staging: udlfb: support for writing backup EDID to sysfs file Support writing default/backup edid via /sys/class/graphics/fb?/edid Writing a backup edid blob to the 'edid' file will cause udlfb to attempt to re-read the edid from hardware. If we still don't have a good edid, it will use the backup edid instead. The intent is to 1) enable user-supplied edid 2) enable udev rules to backup the last known good edid for a device, and then provide that edid as a backup for all future starts Signed-off-by: Bernie Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/udlfb/udlfb.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'drivers/staging/udlfb/udlfb.c') diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index bcb4a1e9e713..b13869bbe09d 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -1306,6 +1306,29 @@ static ssize_t edid_show( return count; } +static ssize_t edid_store( + struct file *filp, + struct kobject *kobj, struct bin_attribute *a, + char *src, loff_t src_off, size_t src_size) { + struct device *fbdev = container_of(kobj, struct device, kobj); + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + + /* We only support write of entire EDID at once, no offset*/ + if ((src_size < MIN_EDID_SIZE) || + (src_size > MAX_EDID_SIZE) || + (src_off != 0)) + return 0; + + dlfb_setup_modes(dev, fb_info, src, src_size); + + if (dev->edid && (memcmp(src, dev->edid, src_size) == 0)) { + dl_info("sysfs written EDID is new default\n"); + dlfb_ops_set_par(fb_info); + return src_size; + } else + return 0; +} static ssize_t metrics_reset_store(struct device *fbdev, struct device_attribute *attr, @@ -1348,9 +1371,10 @@ static ssize_t use_defio_store(struct device *fbdev, static struct bin_attribute edid_attr = { .attr.name = "edid", - .attr.mode = 0444, - .size = 128, + .attr.mode = 0666, + .size = MAX_EDID_SIZE, .read = edid_show, + .write = edid_store }; static struct device_attribute fb_device_attrs[] = { -- cgit v1.2.3-59-g8ed1b From d5ed54322e54d48e5fa66dff0086b79211f1cde7 Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Sun, 5 Sep 2010 16:35:39 -0700 Subject: staging: udlfb: add module options for console and fb_defio Add module options for console and fb_defio Convert fb_defio on/off switch to module option and add console option. >From the command line, pass options to modprobe modprobe udlfb defio=1 console=1 Or for permanent option, create file like /etc/modprobe.d/options with text options udlfb defio=1 console=1 Accepted options: fb_defio Make use of the fb_defio (CONFIG_FB_DEFERRED_IO) kernel module to track changed areas of the framebuffer by page faults. Standard fbdev applications that use mmap but that do not report damage, may be able to work with this enabled. Disabled by default because of overhead and other issues. console Allow fbcon to attach to udlfb provided framebuffers. This is disabled by default because fbcon will aggressively consume the first framebuffer it finds, which isn't usually what the user wants in the case of USB displays. Signed-off-by: Bernie Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/udlfb/udlfb.c | 178 +++++++++++++++++++----------------------- drivers/staging/udlfb/udlfb.h | 1 - 2 files changed, 81 insertions(+), 98 deletions(-) (limited to 'drivers/staging/udlfb/udlfb.c') diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index b13869bbe09d..80a1e0e0ab7e 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -59,19 +59,9 @@ static struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); -#ifndef CONFIG_FB_DEFERRED_IO -#warning Please set CONFIG_FB_DEFFERRED_IO option to support generic fbdev apps -#endif - -#ifndef CONFIG_FB_SYS_IMAGEBLIT -#ifndef CONFIG_FB_SYS_IMAGEBLIT_MODULE -#warning Please set CONFIG_FB_SYS_IMAGEBLIT option to support fb console -#endif -#endif - -#ifndef CONFIG_FB_MODE_HELPERS -#warning CONFIG_FB_MODE_HELPERS required. Expect build break -#endif +/* module options */ +static int console; /* Optionally allow fbcon to consume first framebuffer */ +static int fb_defio; /* Optionally enable experimental fb_defio mmap support */ /* dlfb keeps a list of urbs for efficient bulk transfers */ static void dlfb_urb_completion(struct urb *urb); @@ -695,6 +685,68 @@ static void dlfb_ops_fillrect(struct fb_info *info, } +#ifdef CONFIG_FB_DEFERRED_IO +/* + * NOTE: fb_defio.c is holding info->fbdefio.mutex + * Touching ANY framebuffer memory that triggers a page fault + * in fb_defio will cause a deadlock, when it also tries to + * grab the same mutex. + */ +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; + + if (!fb_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; + } + + 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(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); +} + +#endif + static int dlfb_get_edid(struct dlfb_data *dev, char *edid, int len) { int i; @@ -758,8 +810,6 @@ static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, if (area->y > info->var.yres) area->y = info->var.yres; - atomic_set(&dev->use_defio, 0); - dlfb_handle_damage(dev, area->x, area->y, area->w, area->h, info->screen_base); } @@ -803,9 +853,13 @@ 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 - */ + /* + * fbcon aggressively connects to first framebuffer it finds, + * preventing other clients (X) from working properly. Usually + * not what the user wants. Fail by default with option to enable. + */ + if ((user == 0) & (!console)) + return -EBUSY; /* If the USB device is gone, we don't accept new opens */ if (dev->virtualized) @@ -816,7 +870,7 @@ static int dlfb_ops_open(struct fb_info *info, int user) kref_get(&dev->kref); #ifdef CONFIG_FB_DEFERRED_IO - if ((atomic_read(&dev->use_defio)) && (info->fbdefio == NULL)) { + if (fb_defio && (info->fbdefio == NULL)) { /* enable defio */ info->fbdefio = &dlfb_defio; fb_deferred_io_init(info); @@ -1345,30 +1399,6 @@ static ssize_t metrics_reset_store(struct device *fbdev, return count; } -static ssize_t use_defio_show(struct device *fbdev, - struct device_attribute *a, char *buf) { - struct fb_info *fb_info = dev_get_drvdata(fbdev); - struct dlfb_data *dev = fb_info->par; - return snprintf(buf, PAGE_SIZE, "%d\n", - atomic_read(&dev->use_defio)); -} - -static ssize_t use_defio_store(struct device *fbdev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct fb_info *fb_info = dev_get_drvdata(fbdev); - struct dlfb_data *dev = fb_info->par; - - if (count > 0) { - if (buf[0] == '0') - atomic_set(&dev->use_defio, 0); - if (buf[0] == '1') - atomic_set(&dev->use_defio, 1); - } - return count; -} - static struct bin_attribute edid_attr = { .attr.name = "edid", .attr.mode = 0666, @@ -1383,60 +1413,9 @@ static struct device_attribute fb_device_attrs[] = { __ATTR_RO(metrics_bytes_sent), __ATTR_RO(metrics_cpu_kcycles_used), __ATTR(metrics_reset, S_IWUGO, NULL, metrics_reset_store), - __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; - - 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; - } - - 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(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, @@ -1563,6 +1542,8 @@ static int dlfb_usb_probe(struct usb_interface *interface, dl_info("vid_%04x&pid_%04x&rev_%04x driver's dlfb_data struct at %p\n", usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, usbdev->descriptor.bcdDevice, dev); + dl_info("console enable=%d\n", console); + dl_info("fb_defio enable=%d\n", fb_defio); dev->sku_pixel_limit = 2048 * 1152; /* default to maximum */ @@ -1611,9 +1592,6 @@ 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); @@ -1893,6 +1871,12 @@ static int dlfb_submit_urb(struct dlfb_data *dev, struct urb *urb, size_t len) return ret; } +module_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); +MODULE_PARM_DESC(console, "Allow fbcon to consume first framebuffer found"); + +module_param(fb_defio, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); +MODULE_PARM_DESC(fb_defio, "Enable fb_defio mmap support. *Experimental*"); + MODULE_AUTHOR("Roberto De Ioris , " "Jaya Kumar , " "Bernie Thompson "); diff --git a/drivers/staging/udlfb/udlfb.h b/drivers/staging/udlfb/udlfb.h index d28e783482de..58d2af6cebbe 100644 --- a/drivers/staging/udlfb/udlfb.h +++ b/drivers/staging/udlfb/udlfb.h @@ -43,7 +43,6 @@ struct dlfb_data { struct delayed_work free_framebuffer_work; atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */ atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */ - atomic_t use_defio; /* 0 = rely on ioctls and blit/copy/fill rects */ char *edid; /* null until we read edid from hw or get from sysfs */ size_t edid_size; int sku_pixel_limit; -- cgit v1.2.3-59-g8ed1b From 5bea1fbf942363e4114a086aefc67cdca21be7f0 Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Sun, 5 Sep 2010 18:28:29 -0700 Subject: staging: udlfb: fix incorrect fb_defio implementation for multiple framebuffers Fix fb_defio bug in udlfb for multiple framebuffers Previously when using fb_defio with multiple DisplayLink attached displays udlfb incorrectly used a shared datastructure where it needed to have one per fb_info struct. This was completely broken and caused all kinds of problems, including rendering to one framebuffer affecting others. Signed-off-by: Bernie Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/udlfb/udlfb.c | 81 ++++++++++++++++++++++++++++++------------- drivers/staging/udlfb/udlfb.h | 4 +++ 2 files changed, 60 insertions(+), 25 deletions(-) (limited to 'drivers/staging/udlfb/udlfb.c') diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index 80a1e0e0ab7e..082468ac71c4 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -70,11 +70,6 @@ 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 - /* * All DisplayLink bulk operations start with 0xAF, followed by specific code * All operations are written to buffers which then later get sent to device @@ -481,7 +476,7 @@ static void dlfb_compress_hline( * (that we can only write to, slowly, and can never read), and (optionally) * our shadow copy that tracks what's been sent to that hardware buffer. */ -static void dlfb_render_hline(struct dlfb_data *dev, struct urb **urb_ptr, +static int dlfb_render_hline(struct dlfb_data *dev, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, u32 byte_offset, u32 byte_width, int *ident_ptr, int *sent_ptr) @@ -523,11 +518,11 @@ static void dlfb_render_hline(struct dlfb_data *dev, struct urb **urb_ptr, if (cmd >= cmd_end) { int len = cmd - (u8 *) urb->transfer_buffer; if (dlfb_submit_urb(dev, urb, len)) - return; /* lost pixels is set */ + return 1; /* lost pixels is set */ *sent_ptr += len; urb = dlfb_get_urb(dev); if (!urb) - return; /* lost_pixels is set */ + return 1; /* lost_pixels is set */ *urb_ptr = urb; cmd = urb->transfer_buffer; cmd_end = &cmd[urb->transfer_buffer_length]; @@ -535,6 +530,8 @@ static void dlfb_render_hline(struct dlfb_data *dev, struct urb **urb_ptr, } *urb_buf_ptr = cmd; + + return 0; } int dlfb_handle_damage(struct dlfb_data *dev, int x, int y, @@ -571,9 +568,11 @@ int dlfb_handle_damage(struct dlfb_data *dev, int x, int y, const int line_offset = dev->info->fix.line_length * i; const int byte_offset = line_offset + (x * BPP); - dlfb_render_hline(dev, &urb, (char *) dev->info->fix.smem_start, + if (dlfb_render_hline(dev, &urb, + (char *) dev->info->fix.smem_start, &cmd, byte_offset, width * BPP, - &bytes_identical, &bytes_sent); + &bytes_identical, &bytes_sent)) + goto error; } if (cmd > (char *) urb->transfer_buffer) { @@ -584,6 +583,7 @@ int dlfb_handle_damage(struct dlfb_data *dev, int x, int y, } else dlfb_urb_completion(urb); +error: atomic_add(bytes_sent, &dev->bytes_sent); atomic_add(bytes_identical, &dev->bytes_identical); atomic_add(width*height*2, &dev->bytes_rendered); @@ -722,9 +722,10 @@ static void dlfb_dpy_deferred_io(struct fb_info *info, /* 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, + if (dlfb_render_hline(dev, &urb, (char *) info->fix.smem_start, &cmd, cur->index << PAGE_SHIFT, - PAGE_SIZE, &bytes_identical, &bytes_sent); + PAGE_SIZE, &bytes_identical, &bytes_sent)) + goto error; bytes_rendered += PAGE_SIZE; } @@ -736,6 +737,7 @@ static void dlfb_dpy_deferred_io(struct fb_info *info, } else dlfb_urb_completion(urb); +error: atomic_add(bytes_sent, &dev->bytes_sent); atomic_add(bytes_identical, &dev->bytes_identical); atomic_add(bytes_rendered, &dev->bytes_rendered); @@ -796,6 +798,16 @@ static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, /* TODO: Help propose a standard fb.h ioctl to report mmap damage */ if (cmd == DLFB_IOCTL_REPORT_DAMAGE) { + /* + * If we have a damage-aware client, turn fb_defio "off" + * To avoid perf imact of unecessary page fault handling. + * Done by resetting the delay for this fb_info to a very + * long period. Pages will become writable and stay that way. + * Reset to normal value when all clients have closed this fb. + */ + if (info->fbdefio) + info->fbdefio->delay = DL_DEFIO_WRITE_DISABLE; + area = (struct dloarea *)arg; if (area->x < 0) @@ -871,8 +883,18 @@ static int dlfb_ops_open(struct fb_info *info, int user) #ifdef CONFIG_FB_DEFERRED_IO if (fb_defio && (info->fbdefio == NULL)) { - /* enable defio */ - info->fbdefio = &dlfb_defio; + /* enable defio at last moment if not disabled by client */ + + struct fb_deferred_io *fbdefio; + + fbdefio = kmalloc(GFP_KERNEL, sizeof(struct fb_deferred_io)); + + if (fbdefio) { + fbdefio->delay = DL_DEFIO_WRITE_DELAY; + fbdefio->deferred_io = dlfb_dpy_deferred_io; + } + + info->fbdefio = fbdefio; fb_deferred_io_init(info); } #endif @@ -906,6 +928,13 @@ static void dlfb_free(struct kref *kref) kfree(dev); } +static void dlfb_release_urb_work(struct work_struct *work) +{ + struct urb_node *unode = container_of(work, struct urb_node, + release_urb_work.work); + + up(&unode->dev->urbs.limit_sem); +} static void dlfb_free_framebuffer_work(struct work_struct *work) { @@ -1415,15 +1444,6 @@ static struct device_attribute fb_device_attrs[] = { __ATTR(metrics_reset, S_IWUGO, NULL, metrics_reset_store), }; -#ifdef CONFIG_FB_DEFERRED_IO - -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. */ @@ -1730,7 +1750,14 @@ static void dlfb_urb_completion(struct urb *urb) dev->urbs.available++; spin_unlock_irqrestore(&dev->urbs.lock, flags); - up(&dev->urbs.limit_sem); + /* + * When using fb_defio, we deadlock if up() is called + * while another is waiting. So queue to another process. + */ + if (fb_defio) + schedule_delayed_work(&unode->release_urb_work, 0); + else + up(&dev->urbs.limit_sem); } static void dlfb_free_urb_list(struct dlfb_data *dev) @@ -1789,6 +1816,9 @@ static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) break; unode->dev = dev; + INIT_DELAYED_WORK(&unode->release_urb_work, + dlfb_release_urb_work); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { kfree(unode); @@ -1835,7 +1865,8 @@ static struct urb *dlfb_get_urb(struct dlfb_data *dev) ret = down_timeout(&dev->urbs.limit_sem, GET_URB_TIMEOUT); if (ret) { atomic_set(&dev->lost_pixels, 1); - dl_err("wait for urb interrupted: %x\n", ret); + dl_warn("wait for urb interrupted: %x available: %d\n", + ret, dev->urbs.available); goto error; } diff --git a/drivers/staging/udlfb/udlfb.h b/drivers/staging/udlfb/udlfb.h index 58d2af6cebbe..6f9785e9d62e 100644 --- a/drivers/staging/udlfb/udlfb.h +++ b/drivers/staging/udlfb/udlfb.h @@ -19,6 +19,7 @@ struct dloarea { struct urb_node { struct list_head entry; struct dlfb_data *dev; + struct delayed_work release_urb_work; struct urb *urb; }; @@ -87,6 +88,9 @@ struct dlfb_data { #define MIN_RAW_PIX_BYTES 2 #define MIN_RAW_CMD_BYTES (RAW_HEADER_BYTES + MIN_RAW_PIX_BYTES) +#define DL_DEFIO_WRITE_DELAY 5 /* fb_deferred_io.delay in jiffies */ +#define DL_DEFIO_WRITE_DISABLE (HZ*60) /* "disable" with long delay */ + /* remove these once align.h patch is taken into kernel */ #define DL_ALIGN_UP(x, a) ALIGN(x, a) #define DL_ALIGN_DOWN(x, a) ALIGN(x-(a-1), a) -- cgit v1.2.3-59-g8ed1b From 2685cffa9e799278cdbfa5f524dd9e548be6598d Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Sun, 5 Sep 2010 18:29:56 -0700 Subject: staging: udlfb: fix checkpatch and style Fix warnings detected by tools Signed-off-by: Bernie Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/udlfb/udlfb.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/staging/udlfb/udlfb.c') diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index 082468ac71c4..5969e848d297 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -27,7 +27,6 @@ #include #include - #include "udlfb.h" static struct fb_fix_screeninfo dlfb_fix = { @@ -256,6 +255,7 @@ static int dlfb_set_video_mode(struct dlfb_data *dev, urb = dlfb_get_urb(dev); if (!urb) return -ENOMEM; + buf = (char *) urb->transfer_buffer; /* @@ -288,13 +288,14 @@ static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long page, pos; - dl_notice("MMAP: %lu %u\n", offset + size, info->fix.smem_len); - if (offset + size > info->fix.smem_len) return -EINVAL; pos = (unsigned long)info->fix.smem_start + offset; + dl_notice("mmap() framebuffer addr:%lu size:%lu\n", + pos, size); + while (size > 0) { page = vmalloc_to_pfn((void *)pos); if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) @@ -310,7 +311,6 @@ static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ return 0; - } /* @@ -372,13 +372,13 @@ static int dlfb_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) * A single command can transmit a maximum of 256 pixels, * regardless of the compression ratio (protocol design limit). * To the hardware, 0 for a size byte means 256 - * + * * Rather than 256 pixel commands which are either rl or raw encoded, * the rlx command simply assumes alternating raw and rl spans within one cmd. * This has a slightly larger header overhead, but produces more even results. * It also processes all data (read and write) in a single pass. * Performance benchmarks of common cases show it having just slightly better - * compression than 256 pixel raw -or- rle commands, with similar CPU consumpion. + * compression than 256 pixel raw or rle commands, with similar CPU consumpion. * But for very rl friendly data, will compress not quite as well. */ static void dlfb_compress_hline( @@ -570,7 +570,7 @@ int dlfb_handle_damage(struct dlfb_data *dev, int x, int y, if (dlfb_render_hline(dev, &urb, (char *) dev->info->fix.smem_start, - &cmd, byte_offset, width * BPP, + &cmd, byte_offset, width * BPP, &bytes_identical, &bytes_sent)) goto error; } @@ -1536,8 +1536,8 @@ static int dlfb_usb_probe(struct usb_interface *interface, struct usb_device *usbdev; struct dlfb_data *dev = 0; struct fb_info *info = 0; - int i; int retval = -ENOMEM; + int i; /* usb initialization */ -- cgit v1.2.3-59-g8ed1b