diff options
Diffstat (limited to 'drivers/video/fbdev/udlfb.c')
| -rw-r--r-- | drivers/video/fbdev/udlfb.c | 171 |
1 files changed, 67 insertions, 104 deletions
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c index b9cdd02c1000..ccede85df1e1 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c @@ -23,10 +23,12 @@ #include <linux/vmalloc.h> #include <linux/slab.h> #include <linux/delay.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <video/udlfb.h> #include "edid.h" +#define OUT_EP_NUM 1 /* The endpoint number we will use */ + static const struct fb_fix_screeninfo dlfb_fix = { .id = "udlfb", .type = FB_TYPE_PACKED_PIXELS, @@ -37,7 +39,7 @@ static const struct fb_fix_screeninfo dlfb_fix = { .accel = FB_ACCEL_NONE, }; -static const u32 udlfb_info_flags = FBINFO_DEFAULT | FBINFO_READS_FAST | +static const u32 udlfb_info_flags = FBINFO_READS_FAST | FBINFO_VIRTFB | FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR; @@ -326,6 +328,11 @@ 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; + if (info->fbdefio) + return fb_deferred_io_mmap(info, vma); + + vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) return -EINVAL; if (size > info->fix.smem_len) @@ -367,7 +374,7 @@ static int dlfb_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) const unsigned long *back = (const unsigned long *) bback; const unsigned long *front = (const unsigned long *) *bfront; const int width = *width_bytes / sizeof(unsigned long); - int identical = width; + int identical; int start = width; int end = width; @@ -711,78 +718,14 @@ static void dlfb_offload_damage(struct dlfb_data *dlfb, int x, int y, int width, } /* - * 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; - struct dlfb_data *dlfb = info->par; - u32 offset = (u32) *ppos; - - result = fb_sys_write(info, buf, count, ppos); - - if (result > 0) { - int start = max((int)(offset / info->fix.line_length), 0); - int lines = min((u32)((result / info->fix.line_length) + 1), - (u32)info->var.yres); - - dlfb_handle_damage(dlfb, 0, start, info->var.xres, - lines); - } - - 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) -{ - - struct dlfb_data *dlfb = info->par; - - sys_copyarea(info, area); - - dlfb_offload_damage(dlfb, area->dx, area->dy, - area->width, area->height); -} - -static void dlfb_ops_imageblit(struct fb_info *info, - const struct fb_image *image) -{ - struct dlfb_data *dlfb = info->par; - - sys_imageblit(info, image); - - dlfb_offload_damage(dlfb, image->dx, image->dy, - image->width, image->height); -} - -static void dlfb_ops_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) -{ - struct dlfb_data *dlfb = info->par; - - sys_fillrect(info, rect); - - dlfb_offload_damage(dlfb, rect->dx, rect->dy, rect->width, - rect->height); -} - -/* * 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) +static void dlfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist) { - struct page *cur; - struct fb_deferred_io *fbdefio = info->fbdefio; + struct fb_deferred_io_pageref *pageref; struct dlfb_data *dlfb = info->par; struct urb *urb; char *cmd; @@ -808,11 +751,10 @@ static void dlfb_dpy_deferred_io(struct fb_info *info, cmd = urb->transfer_buffer; /* walk the written page list and render each to device */ - list_for_each_entry(cur, &fbdefio->pagelist, lru) { - + list_for_each_entry(pageref, pagereflist, list) { if (dlfb_render_hline(dlfb, &urb, (char *) info->fix.smem_start, - &cmd, cur->index << PAGE_SHIFT, - PAGE_SIZE, &bytes_identical, &bytes_sent)) + &cmd, pageref->offset, PAGE_SIZE, + &bytes_identical, &bytes_sent)) goto error; bytes_rendered += PAGE_SIZE; } @@ -980,6 +922,7 @@ static int dlfb_ops_open(struct fb_info *info, int user) if (fbdefio) { fbdefio->delay = DL_DEFIO_WRITE_DELAY; + fbdefio->sort_pagereflist = true; fbdefio->deferred_io = dlfb_dpy_deferred_io; } @@ -1005,7 +948,7 @@ static void dlfb_ops_destroy(struct fb_info *info) fb_dealloc_cmap(&info->cmap); if (info->monspecs.modedb) fb_destroy_modedb(info->monspecs.modedb); - vfree(info->screen_base); + vfree(info->screen_buffer); fb_destroy_modelist(&info->modelist); @@ -1119,7 +1062,7 @@ static int dlfb_ops_set_par(struct fb_info *info) /* paint greenscreen */ - pix_framebuffer = (u16 *) info->screen_base; + pix_framebuffer = (u16 *)info->screen_buffer; for (i = 0; i < info->fix.smem_len / 2; i++) pix_framebuffer[i] = 0x37e6; } @@ -1183,14 +1126,31 @@ static int dlfb_ops_blank(int blank_mode, struct fb_info *info) return 0; } +static void dlfb_ops_damage_range(struct fb_info *info, off_t off, size_t len) +{ + struct dlfb_data *dlfb = info->par; + int start = max((int)(off / info->fix.line_length), 0); + int lines = min((u32)((len / info->fix.line_length) + 1), (u32)info->var.yres); + + dlfb_handle_damage(dlfb, 0, start, info->var.xres, lines); +} + +static void dlfb_ops_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height) +{ + struct dlfb_data *dlfb = info->par; + + dlfb_offload_damage(dlfb, x, y, width, height); +} + +FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(dlfb_ops, + dlfb_ops_damage_range, + dlfb_ops_damage_area) + static const struct fb_ops dlfb_ops = { .owner = THIS_MODULE, - .fb_read = fb_sys_read, - .fb_write = dlfb_ops_write, + __FB_DEFAULT_DEFERRED_OPS_RDWR(dlfb_ops), .fb_setcolreg = dlfb_ops_setcolreg, - .fb_fillrect = dlfb_ops_fillrect, - .fb_copyarea = dlfb_ops_copyarea, - .fb_imageblit = dlfb_ops_imageblit, + __FB_DEFAULT_DEFERRED_OPS_DRAW(dlfb_ops), .fb_mmap = dlfb_ops_mmap, .fb_ioctl = dlfb_ops_ioctl, .fb_open = dlfb_ops_open, @@ -1218,7 +1178,7 @@ static void dlfb_deferred_vfree(struct dlfb_data *dlfb, void *mem) static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info, u32 new_len) { u32 old_len = info->fix.smem_len; - const void *old_fb = (const void __force *)info->screen_base; + const void *old_fb = info->screen_buffer; unsigned char *new_fb; unsigned char *new_back = NULL; @@ -1235,12 +1195,12 @@ static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info } memset(new_fb, 0xff, new_len); - if (info->screen_base) { + if (info->screen_buffer) { memcpy(new_fb, old_fb, old_len); - dlfb_deferred_vfree(dlfb, (void __force *)info->screen_base); + dlfb_deferred_vfree(dlfb, info->screen_buffer); } - info->screen_base = (char __iomem *)new_fb; + info->screen_buffer = new_fb; info->fix.smem_len = new_len; info->fix.smem_start = (unsigned long) new_fb; info->flags = udlfb_info_flags; @@ -1426,7 +1386,7 @@ static ssize_t metrics_bytes_rendered_show(struct device *fbdev, struct device_attribute *a, char *buf) { struct fb_info *fb_info = dev_get_drvdata(fbdev); struct dlfb_data *dlfb = fb_info->par; - return snprintf(buf, PAGE_SIZE, "%u\n", + return sysfs_emit(buf, "%u\n", atomic_read(&dlfb->bytes_rendered)); } @@ -1434,7 +1394,7 @@ static ssize_t metrics_bytes_identical_show(struct device *fbdev, struct device_attribute *a, char *buf) { struct fb_info *fb_info = dev_get_drvdata(fbdev); struct dlfb_data *dlfb = fb_info->par; - return snprintf(buf, PAGE_SIZE, "%u\n", + return sysfs_emit(buf, "%u\n", atomic_read(&dlfb->bytes_identical)); } @@ -1442,7 +1402,7 @@ static ssize_t metrics_bytes_sent_show(struct device *fbdev, struct device_attribute *a, char *buf) { struct fb_info *fb_info = dev_get_drvdata(fbdev); struct dlfb_data *dlfb = fb_info->par; - return snprintf(buf, PAGE_SIZE, "%u\n", + return sysfs_emit(buf, "%u\n", atomic_read(&dlfb->bytes_sent)); } @@ -1450,13 +1410,13 @@ static ssize_t metrics_cpu_kcycles_used_show(struct device *fbdev, struct device_attribute *a, char *buf) { struct fb_info *fb_info = dev_get_drvdata(fbdev); struct dlfb_data *dlfb = fb_info->par; - return snprintf(buf, PAGE_SIZE, "%u\n", + return sysfs_emit(buf, "%u\n", atomic_read(&dlfb->cpu_kcycles_used)); } static ssize_t edid_show( struct file *filp, - struct kobject *kobj, struct bin_attribute *a, + struct kobject *kobj, const struct bin_attribute *a, char *buf, loff_t off, size_t count) { struct device *fbdev = kobj_to_dev(kobj); struct fb_info *fb_info = dev_get_drvdata(fbdev); @@ -1478,7 +1438,7 @@ static ssize_t edid_show( static ssize_t edid_store( struct file *filp, - struct kobject *kobj, struct bin_attribute *a, + struct kobject *kobj, const struct bin_attribute *a, char *src, loff_t src_off, size_t src_size) { struct device *fbdev = kobj_to_dev(kobj); struct fb_info *fb_info = dev_get_drvdata(fbdev); @@ -1540,24 +1500,16 @@ static const struct device_attribute fb_device_attrs[] = { static int dlfb_select_std_channel(struct dlfb_data *dlfb) { int ret; - void *buf; static const u8 set_def_chn[] = { 0x57, 0xCD, 0xDC, 0xA7, 0x1C, 0x88, 0x5E, 0x15, 0x60, 0xFE, 0xC6, 0x97, 0x16, 0x3D, 0x47, 0xF2 }; - buf = kmemdup(set_def_chn, sizeof(set_def_chn), GFP_KERNEL); - - if (!buf) - return -ENOMEM; - - ret = usb_control_msg(dlfb->udev, usb_sndctrlpipe(dlfb->udev, 0), - NR_USB_REQUEST_CHANNEL, + ret = usb_control_msg_send(dlfb->udev, 0, NR_USB_REQUEST_CHANNEL, (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0, - buf, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT); - - kfree(buf); + &set_def_chn, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT, + GFP_KERNEL); return ret; } @@ -1649,8 +1601,9 @@ static int dlfb_usb_probe(struct usb_interface *intf, const struct device_attribute *attr; struct dlfb_data *dlfb; struct fb_info *info; - int retval = -ENOMEM; + int retval; struct usb_device *usbdev = interface_to_usbdev(intf); + static u8 out_ep[] = {OUT_EP_NUM + USB_DIR_OUT, 0}; /* usb initialization */ dlfb = kzalloc(sizeof(*dlfb), GFP_KERNEL); @@ -1664,6 +1617,12 @@ static int dlfb_usb_probe(struct usb_interface *intf, dlfb->udev = usb_get_dev(usbdev); usb_set_intfdata(intf, dlfb); + if (!usb_check_bulk_endpoints(intf, out_ep)) { + dev_err(&intf->dev, "Invalid DisplayLink device!\n"); + retval = -EINVAL; + goto error; + } + dev_dbg(&intf->dev, "console enable=%d\n", console); dev_dbg(&intf->dev, "fb_defio enable=%d\n", fb_defio); dev_dbg(&intf->dev, "shadow enable=%d\n", shadow); @@ -1673,6 +1632,7 @@ static int dlfb_usb_probe(struct usb_interface *intf, if (!dlfb_parse_vendor_descriptor(dlfb, intf)) { dev_err(&intf->dev, "firmware not recognized, incompatible device?\n"); + retval = -ENODEV; goto error; } @@ -1686,8 +1646,10 @@ static int dlfb_usb_probe(struct usb_interface *intf, /* allocates framebuffer driver structure, not framebuffer memory */ info = framebuffer_alloc(0, &dlfb->udev->dev); - if (!info) + if (!info) { + retval = -ENOMEM; goto error; + } dlfb->info = info; info->par = dlfb; @@ -1916,7 +1878,8 @@ retry: } /* urb->transfer_buffer_length set to actual before submit */ - usb_fill_bulk_urb(urb, dlfb->udev, usb_sndbulkpipe(dlfb->udev, 1), + usb_fill_bulk_urb(urb, dlfb->udev, + usb_sndbulkpipe(dlfb->udev, OUT_EP_NUM), buf, size, dlfb_urb_completion, unode); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
