diff options
Diffstat (limited to 'drivers/video/fbdev/smscufx.c')
| -rw-r--r-- | drivers/video/fbdev/smscufx.c | 244 |
1 files changed, 116 insertions, 128 deletions
diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c index 22b606af0a87..5f0dd01fd834 100644 --- a/drivers/video/fbdev/smscufx.c +++ b/drivers/video/fbdev/smscufx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * smscufx.c -- Framebuffer driver for SMSC UFX USB controller * @@ -6,10 +7,6 @@ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> * - * This file is subject to the terms and conditions of the GNU General Public - * License v2. See the file COPYING in the main directory of this archive for - * more details. - * * Based on udlfb, with work from Florian Echtler, Henrik Bjerregaard Pedersen, * and others. * @@ -100,7 +97,6 @@ struct ufx_data { struct kref kref; 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 */ u8 *edid; /* null until we read edid from hw or get from sysfs */ @@ -118,7 +114,7 @@ static struct fb_fix_screeninfo ufx_fix = { .accel = FB_ACCEL_NONE, }; -static const u32 smscufx_info_flags = FBINFO_DEFAULT | FBINFO_READS_FAST | +static const u32 smscufx_info_flags = FBINFO_READS_FAST | FBINFO_VIRTFB | FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR; @@ -140,6 +136,8 @@ static int ufx_submit_urb(struct ufx_data *dev, struct urb * urb, size_t len); static int ufx_alloc_urb_list(struct ufx_data *dev, int count, size_t size); static void ufx_free_urb_list(struct ufx_data *dev); +static DEFINE_MUTEX(disconnect_mutex); + /* reads a control register */ static int ufx_reg_read(struct ufx_data *dev, u32 index, u32 *data) { @@ -782,6 +780,11 @@ static int ufx_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) @@ -893,74 +896,14 @@ static int ufx_handle_damage(struct ufx_data *dev, int x, int y, return 0; } -/* 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 ufx_ops_write(struct fb_info *info, const char __user *buf, - size_t count, loff_t *ppos) -{ - ssize_t result; - struct ufx_data *dev = 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); - - ufx_handle_damage(dev, 0, start, info->var.xres, lines); - } - - return result; -} - -static void ufx_ops_copyarea(struct fb_info *info, - const struct fb_copyarea *area) -{ - - struct ufx_data *dev = info->par; - - sys_copyarea(info, area); - - ufx_handle_damage(dev, area->dx, area->dy, - area->width, area->height); -} - -static void ufx_ops_imageblit(struct fb_info *info, - const struct fb_image *image) -{ - struct ufx_data *dev = info->par; - - sys_imageblit(info, image); - - ufx_handle_damage(dev, image->dx, image->dy, - image->width, image->height); -} - -static void ufx_ops_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) -{ - struct ufx_data *dev = info->par; - - sys_fillrect(info, rect); - - ufx_handle_damage(dev, 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 ufx_dpy_deferred_io(struct fb_info *info, - struct list_head *pagelist) +static void ufx_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist) { - struct page *cur; - struct fb_deferred_io *fbdefio = info->fbdefio; struct ufx_data *dev = info->par; + struct fb_deferred_io_pageref *pageref; if (!fb_defio) return; @@ -969,12 +912,12 @@ static void ufx_dpy_deferred_io(struct fb_info *info, return; /* 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) { /* create a rectangle of full screen width that encloses the * entire dirty framebuffer page */ const int x = 0; const int width = dev->info->var.xres; - const int y = (cur->index << PAGE_SHIFT) / (width * 2); + const int y = pageref->offset / (width * 2); int height = (PAGE_SIZE / (width * 2)) + 1; height = min(height, (int)(dev->info->var.yres - y)); @@ -1073,9 +1016,13 @@ static int ufx_ops_open(struct fb_info *info, int user) if (user == 0 && !console) return -EBUSY; + mutex_lock(&disconnect_mutex); + /* If the USB device is gone, we don't accept new opens */ - if (dev->virtualized) + if (dev->virtualized) { + mutex_unlock(&disconnect_mutex); return -ENODEV; + } dev->fb_count++; @@ -1099,6 +1046,8 @@ static int ufx_ops_open(struct fb_info *info, int user) pr_debug("open /dev/fb%d user=%d fb_info=%p count=%d", info->node, user, info, dev->fb_count); + mutex_unlock(&disconnect_mutex); + return 0; } @@ -1111,15 +1060,24 @@ static void ufx_free(struct kref *kref) { struct ufx_data *dev = container_of(kref, struct ufx_data, kref); - /* this function will wait for all in-flight urbs to complete */ - if (dev->urbs.count > 0) - ufx_free_urb_list(dev); + kfree(dev); +} - pr_debug("freeing ufx_data %p", dev); +static void ufx_ops_destory(struct fb_info *info) +{ + struct ufx_data *dev = info->par; + int node = info->node; - kfree(dev); + /* Assume info structure is freed after this point */ + framebuffer_release(info); + + pr_debug("fb_info for /dev/fb%d has been freed", node); + + /* release reference taken by kref_init in probe() */ + kref_put(&dev->kref, ufx_free); } + static void ufx_release_urb_work(struct work_struct *work) { struct urb_node *unode = container_of(work, struct urb_node, @@ -1128,30 +1086,20 @@ static void ufx_release_urb_work(struct work_struct *work) up(&unode->dev->urbs.limit_sem); } -static void ufx_free_framebuffer_work(struct work_struct *work) +static void ufx_free_framebuffer(struct ufx_data *dev) { - struct ufx_data *dev = container_of(work, struct ufx_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); if (info->monspecs.modedb) fb_destroy_modedb(info->monspecs.modedb); - vfree(info->screen_base); + vfree(info->screen_buffer); fb_destroy_modelist(&info->modelist); dev->info = NULL; - /* Assume info structure is freed after this point */ - framebuffer_release(info); - - pr_debug("fb_info for /dev/fb%d has been freed", node); - /* ref taken in probe() as part of registering framebfufer */ kref_put(&dev->kref, ufx_free); } @@ -1163,17 +1111,18 @@ static int ufx_ops_release(struct fb_info *info, int user) { struct ufx_data *dev = info->par; + mutex_lock(&disconnect_mutex); + 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); + ufx_free_framebuffer(dev); if ((dev->fb_count == 0) && (info->fbdefio)) { fb_deferred_io_cleanup(info); kfree(info->fbdefio); info->fbdefio = NULL; - info->fbops->fb_mmap = ufx_ops_mmap; } pr_debug("released /dev/fb%d user=%d count=%d", @@ -1181,6 +1130,8 @@ static int ufx_ops_release(struct fb_info *info, int user) kref_put(&dev->kref, ufx_free); + mutex_unlock(&disconnect_mutex); + return 0; } @@ -1250,7 +1201,7 @@ static int ufx_ops_set_par(struct fb_info *info) if ((result == 0) && (dev->fb_count == 0)) { /* 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; @@ -1272,14 +1223,31 @@ static int ufx_ops_blank(int blank_mode, struct fb_info *info) return 0; } -static struct fb_ops ufx_ops = { +static void ufx_ops_damage_range(struct fb_info *info, off_t off, size_t len) +{ + struct ufx_data *dev = 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); + + ufx_handle_damage(dev, 0, start, info->var.xres, lines); +} + +static void ufx_ops_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height) +{ + struct ufx_data *dev = info->par; + + ufx_handle_damage(dev, x, y, width, height); +} + +FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(ufx_ops, + ufx_ops_damage_range, + ufx_ops_damage_area) + +static const struct fb_ops ufx_ops = { .owner = THIS_MODULE, - .fb_read = fb_sys_read, - .fb_write = ufx_ops_write, + __FB_DEFAULT_DEFERRED_OPS_RDWR(ufx_ops), .fb_setcolreg = ufx_ops_setcolreg, - .fb_fillrect = ufx_ops_fillrect, - .fb_copyarea = ufx_ops_copyarea, - .fb_imageblit = ufx_ops_imageblit, + __FB_DEFAULT_DEFERRED_OPS_DRAW(ufx_ops), .fb_mmap = ufx_ops_mmap, .fb_ioctl = ufx_ops_ioctl, .fb_open = ufx_ops_open, @@ -1287,6 +1255,7 @@ static struct fb_ops ufx_ops = { .fb_blank = ufx_ops_blank, .fb_check_var = ufx_ops_check_var, .fb_set_par = ufx_ops_set_par, + .fb_destroy = ufx_ops_destory, }; /* Assumes &info->lock held by caller @@ -1295,7 +1264,7 @@ static int ufx_realloc_framebuffer(struct ufx_data *dev, struct fb_info *info) { int old_len = info->fix.smem_len; int new_len; - unsigned char *old_fb = info->screen_base; + unsigned char *old_fb = info->screen_buffer; unsigned char *new_fb; pr_debug("Reallocating framebuffer. Addresses will change!"); @@ -1310,12 +1279,12 @@ static int ufx_realloc_framebuffer(struct ufx_data *dev, struct fb_info *info) if (!new_fb) return -ENOMEM; - if (info->screen_base) { + if (info->screen_buffer) { memcpy(new_fb, old_fb, old_len); - vfree(info->screen_base); + vfree(info->screen_buffer); } - info->screen_base = new_fb; + info->screen_buffer = new_fb; info->fix.smem_len = PAGE_ALIGN(new_len); info->fix.smem_start = (unsigned long) new_fb; info->flags = smscufx_info_flags; @@ -1323,7 +1292,7 @@ static int ufx_realloc_framebuffer(struct ufx_data *dev, struct fb_info *info) return 0; } -/* sets up I2C Controller for 100 Kbps, std. speed, 7-bit addr, master, +/* sets up DDC channel for 100 Kbps, std. speed, 7-bit addr, controller mode, * restart enabled, but no start byte, enable controller */ static int ufx_i2c_init(struct ufx_data *dev) { @@ -1352,7 +1321,7 @@ static int ufx_i2c_init(struct ufx_data *dev) /* 7-bit (not 10-bit) addressing */ tmp &= ~(0x10); - /* enable restart conditions and master mode */ + /* enable restart conditions and controller mode */ tmp |= 0x21; status = ufx_reg_write(dev, 0x1000, tmp); @@ -1488,7 +1457,7 @@ static int ufx_setup_modes(struct ufx_data *dev, struct fb_info *info, u8 *edid; int i, result = 0, tries = 3; - if (info->dev) /* only use mutex if info has been registered */ + if (refcount_read(&info->count)) /* only use mutex if info has been registered */ mutex_lock(&info->lock); edid = kmalloc(EDID_LENGTH, GFP_KERNEL); @@ -1602,7 +1571,7 @@ error: if (edid && (dev->edid != edid)) kfree(edid); - if (info->dev) + if (refcount_read(&info->count)) mutex_unlock(&info->lock); return result; @@ -1614,7 +1583,7 @@ static int ufx_usb_probe(struct usb_interface *interface, struct usb_device *usbdev; struct ufx_data *dev; struct fb_info *info; - int retval; + int retval = -ENOMEM; u32 id_rev, fpga_rev; /* usb initialization */ @@ -1646,7 +1615,7 @@ static int ufx_usb_probe(struct usb_interface *interface, if (!ufx_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { dev_err(dev->gdev, "ufx_alloc_urb_list failed\n"); - goto e_nomem; + goto put_ref; } /* We don't register a new USB class. Our client interface is fbdev */ @@ -1655,13 +1624,14 @@ static int ufx_usb_probe(struct usb_interface *interface, info = framebuffer_alloc(0, &usbdev->dev); if (!info) { dev_err(dev->gdev, "framebuffer_alloc failed\n"); - goto e_nomem; + goto free_urb_list; } dev->info = info; info->par = dev; info->pseudo_palette = dev->pseudo_palette; info->fbops = &ufx_ops; + INIT_LIST_HEAD(&info->modelist); retval = fb_alloc_cmap(&info->cmap, 256, 0); if (retval < 0) { @@ -1669,11 +1639,6 @@ static int ufx_usb_probe(struct usb_interface *interface, goto destroy_modedb; } - INIT_DELAYED_WORK(&dev->free_framebuffer_work, - ufx_free_framebuffer_work); - - INIT_LIST_HEAD(&info->modelist); - retval = ufx_reg_read(dev, 0x3000, &id_rev); check_warn_goto_error(retval, "error %d reading 0x3000 register from device", retval); dev_dbg(dev->gdev, "ID_REV register value 0x%08x", id_rev); @@ -1703,22 +1668,34 @@ static int ufx_usb_probe(struct usb_interface *interface, check_warn_goto_error(retval, "unable to find common mode for display and adapter"); retval = ufx_reg_set_bits(dev, 0x4000, 0x00000001); - check_warn_goto_error(retval, "error %d enabling graphics engine", retval); + if (retval < 0) { + dev_err(dev->gdev, "error %d enabling graphics engine", retval); + goto setup_modes; + } /* ready to begin using device */ atomic_set(&dev->usb_active, 1); dev_dbg(dev->gdev, "checking var"); retval = ufx_ops_check_var(&info->var, info); - check_warn_goto_error(retval, "error %d ufx_ops_check_var", retval); + if (retval < 0) { + dev_err(dev->gdev, "error %d ufx_ops_check_var", retval); + goto reset_active; + } dev_dbg(dev->gdev, "setting par"); retval = ufx_ops_set_par(info); - check_warn_goto_error(retval, "error %d ufx_ops_set_par", retval); + if (retval < 0) { + dev_err(dev->gdev, "error %d ufx_ops_set_par", retval); + goto reset_active; + } dev_dbg(dev->gdev, "registering framebuffer"); retval = register_framebuffer(info); - check_warn_goto_error(retval, "error %d register_framebuffer", retval); + if (retval < 0) { + dev_err(dev->gdev, "error %d register_framebuffer", retval); + goto reset_active; + } dev_info(dev->gdev, "SMSC UDX USB device /dev/fb%d attached. %dx%d resolution." " Using %dK framebuffer memory\n", info->node, @@ -1726,28 +1703,34 @@ static int ufx_usb_probe(struct usb_interface *interface, return 0; +reset_active: + atomic_set(&dev->usb_active, 0); +setup_modes: + fb_destroy_modedb(info->monspecs.modedb); + vfree(info->screen_buffer); + fb_destroy_modelist(&info->modelist); error: fb_dealloc_cmap(&info->cmap); destroy_modedb: - fb_destroy_modedb(info->monspecs.modedb); - vfree(info->screen_base); - fb_destroy_modelist(&info->modelist); framebuffer_release(info); +free_urb_list: + if (dev->urbs.count > 0) + ufx_free_urb_list(dev); put_ref: kref_put(&dev->kref, ufx_free); /* ref for framebuffer */ kref_put(&dev->kref, ufx_free); /* last ref from kref_init */ return retval; - -e_nomem: - retval = -ENOMEM; - goto put_ref; } static void ufx_usb_disconnect(struct usb_interface *interface) { struct ufx_data *dev; + struct fb_info *info; + + mutex_lock(&disconnect_mutex); dev = usb_get_intfdata(interface); + info = dev->info; pr_debug("USB disconnect starting\n"); @@ -1761,12 +1744,17 @@ static void ufx_usb_disconnect(struct usb_interface *interface) /* 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); + ufx_free_framebuffer(dev); - /* release reference taken by kref_init in probe() */ - kref_put(&dev->kref, ufx_free); + /* this function will wait for all in-flight urbs to complete */ + if (dev->urbs.count > 0) + ufx_free_urb_list(dev); + + pr_debug("freeing ufx_data %p", dev); + + unregister_framebuffer(info); - /* consider ufx_data freed */ + mutex_unlock(&disconnect_mutex); } static struct usb_driver ufx_driver = { |
