diff options
-rw-r--r-- | drivers/video/udlfb.c | 146 | ||||
-rw-r--r-- | include/video/udlfb.h | 1 |
2 files changed, 81 insertions, 66 deletions
diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index 04aea205f021..cbf030d0dfc4 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c | |||
@@ -918,10 +918,6 @@ static void dlfb_free(struct kref *kref) | |||
918 | { | 918 | { |
919 | struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref); | 919 | struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref); |
920 | 920 | ||
921 | /* this function will wait for all in-flight urbs to complete */ | ||
922 | if (dev->urbs.count > 0) | ||
923 | dlfb_free_urb_list(dev); | ||
924 | |||
925 | if (dev->backing_buffer) | 921 | if (dev->backing_buffer) |
926 | vfree(dev->backing_buffer); | 922 | vfree(dev->backing_buffer); |
927 | 923 | ||
@@ -940,35 +936,42 @@ static void dlfb_release_urb_work(struct work_struct *work) | |||
940 | up(&unode->dev->urbs.limit_sem); | 936 | up(&unode->dev->urbs.limit_sem); |
941 | } | 937 | } |
942 | 938 | ||
943 | static void dlfb_free_framebuffer_work(struct work_struct *work) | 939 | static void dlfb_free_framebuffer(struct dlfb_data *dev) |
944 | { | 940 | { |
945 | struct dlfb_data *dev = container_of(work, struct dlfb_data, | ||
946 | free_framebuffer_work.work); | ||
947 | struct fb_info *info = dev->info; | 941 | struct fb_info *info = dev->info; |
948 | int node = info->node; | ||
949 | 942 | ||
950 | unregister_framebuffer(info); | 943 | if (info) { |
944 | int node = info->node; | ||
951 | 945 | ||
952 | if (info->cmap.len != 0) | 946 | unregister_framebuffer(info); |
953 | fb_dealloc_cmap(&info->cmap); | ||
954 | if (info->monspecs.modedb) | ||
955 | fb_destroy_modedb(info->monspecs.modedb); | ||
956 | if (info->screen_base) | ||
957 | vfree(info->screen_base); | ||
958 | 947 | ||
959 | fb_destroy_modelist(&info->modelist); | 948 | if (info->cmap.len != 0) |
949 | fb_dealloc_cmap(&info->cmap); | ||
950 | if (info->monspecs.modedb) | ||
951 | fb_destroy_modedb(info->monspecs.modedb); | ||
952 | if (info->screen_base) | ||
953 | vfree(info->screen_base); | ||
954 | |||
955 | fb_destroy_modelist(&info->modelist); | ||
960 | 956 | ||
961 | dev->info = 0; | 957 | dev->info = NULL; |
962 | 958 | ||
963 | /* Assume info structure is freed after this point */ | 959 | /* Assume info structure is freed after this point */ |
964 | framebuffer_release(info); | 960 | framebuffer_release(info); |
965 | 961 | ||
966 | pr_warn("fb_info for /dev/fb%d has been freed\n", node); | 962 | pr_warn("fb_info for /dev/fb%d has been freed\n", node); |
963 | } | ||
967 | 964 | ||
968 | /* ref taken in probe() as part of registering framebfufer */ | 965 | /* ref taken in probe() as part of registering framebfufer */ |
969 | kref_put(&dev->kref, dlfb_free); | 966 | kref_put(&dev->kref, dlfb_free); |
970 | } | 967 | } |
971 | 968 | ||
969 | static void dlfb_free_framebuffer_work(struct work_struct *work) | ||
970 | { | ||
971 | struct dlfb_data *dev = container_of(work, struct dlfb_data, | ||
972 | free_framebuffer_work.work); | ||
973 | dlfb_free_framebuffer(dev); | ||
974 | } | ||
972 | /* | 975 | /* |
973 | * Assumes caller is holding info->lock mutex (for open and release at least) | 976 | * Assumes caller is holding info->lock mutex (for open and release at least) |
974 | */ | 977 | */ |
@@ -1571,14 +1574,15 @@ success: | |||
1571 | kfree(buf); | 1574 | kfree(buf); |
1572 | return true; | 1575 | return true; |
1573 | } | 1576 | } |
1577 | |||
1578 | static void dlfb_init_framebuffer_work(struct work_struct *work); | ||
1579 | |||
1574 | static int dlfb_usb_probe(struct usb_interface *interface, | 1580 | static int dlfb_usb_probe(struct usb_interface *interface, |
1575 | const struct usb_device_id *id) | 1581 | const struct usb_device_id *id) |
1576 | { | 1582 | { |
1577 | struct usb_device *usbdev; | 1583 | struct usb_device *usbdev; |
1578 | struct dlfb_data *dev = 0; | 1584 | struct dlfb_data *dev = 0; |
1579 | struct fb_info *info = 0; | ||
1580 | int retval = -ENOMEM; | 1585 | int retval = -ENOMEM; |
1581 | int i; | ||
1582 | 1586 | ||
1583 | /* usb initialization */ | 1587 | /* usb initialization */ |
1584 | 1588 | ||
@@ -1590,9 +1594,7 @@ static int dlfb_usb_probe(struct usb_interface *interface, | |||
1590 | goto error; | 1594 | goto error; |
1591 | } | 1595 | } |
1592 | 1596 | ||
1593 | /* we need to wait for both usb and fbdev to spin down on disconnect */ | ||
1594 | kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ | 1597 | kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ |
1595 | kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ | ||
1596 | 1598 | ||
1597 | dev->udev = usbdev; | 1599 | dev->udev = usbdev; |
1598 | dev->gdev = &usbdev->dev; /* our generic struct device * */ | 1600 | dev->gdev = &usbdev->dev; /* our generic struct device * */ |
@@ -1620,10 +1622,39 @@ static int dlfb_usb_probe(struct usb_interface *interface, | |||
1620 | goto error; | 1622 | goto error; |
1621 | } | 1623 | } |
1622 | 1624 | ||
1625 | kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ | ||
1626 | |||
1623 | /* We don't register a new USB class. Our client interface is fbdev */ | 1627 | /* We don't register a new USB class. Our client interface is fbdev */ |
1624 | 1628 | ||
1629 | /* Workitem keep things fast & simple during USB enumeration */ | ||
1630 | INIT_DELAYED_WORK(&dev->init_framebuffer_work, | ||
1631 | dlfb_init_framebuffer_work); | ||
1632 | schedule_delayed_work(&dev->init_framebuffer_work, 0); | ||
1633 | |||
1634 | return 0; | ||
1635 | |||
1636 | error: | ||
1637 | if (dev) { | ||
1638 | |||
1639 | kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ | ||
1640 | kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ | ||
1641 | |||
1642 | /* dev has been deallocated. Do not dereference */ | ||
1643 | } | ||
1644 | |||
1645 | return retval; | ||
1646 | } | ||
1647 | |||
1648 | static void dlfb_init_framebuffer_work(struct work_struct *work) | ||
1649 | { | ||
1650 | struct dlfb_data *dev = container_of(work, struct dlfb_data, | ||
1651 | init_framebuffer_work.work); | ||
1652 | struct fb_info *info; | ||
1653 | int retval; | ||
1654 | int i; | ||
1655 | |||
1625 | /* allocates framebuffer driver structure, not framebuffer memory */ | 1656 | /* allocates framebuffer driver structure, not framebuffer memory */ |
1626 | info = framebuffer_alloc(0, &interface->dev); | 1657 | info = framebuffer_alloc(0, dev->gdev); |
1627 | if (!info) { | 1658 | if (!info) { |
1628 | retval = -ENOMEM; | 1659 | retval = -ENOMEM; |
1629 | pr_err("framebuffer_alloc failed\n"); | 1660 | pr_err("framebuffer_alloc failed\n"); |
@@ -1669,15 +1700,13 @@ static int dlfb_usb_probe(struct usb_interface *interface, | |||
1669 | for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) { | 1700 | for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) { |
1670 | retval = device_create_file(info->dev, &fb_device_attrs[i]); | 1701 | retval = device_create_file(info->dev, &fb_device_attrs[i]); |
1671 | if (retval) { | 1702 | if (retval) { |
1672 | pr_err("device_create_file failed %d\n", retval); | 1703 | pr_warn("device_create_file failed %d\n", retval); |
1673 | goto err_del_attrs; | ||
1674 | } | 1704 | } |
1675 | } | 1705 | } |
1676 | 1706 | ||
1677 | retval = device_create_bin_file(info->dev, &edid_attr); | 1707 | retval = device_create_bin_file(info->dev, &edid_attr); |
1678 | if (retval) { | 1708 | if (retval) { |
1679 | pr_err("device_create_bin_file failed %d\n", retval); | 1709 | pr_warn("device_create_bin_file failed %d\n", retval); |
1680 | goto err_del_attrs; | ||
1681 | } | 1710 | } |
1682 | 1711 | ||
1683 | pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." | 1712 | pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." |
@@ -1685,38 +1714,10 @@ static int dlfb_usb_probe(struct usb_interface *interface, | |||
1685 | info->var.xres, info->var.yres, | 1714 | info->var.xres, info->var.yres, |
1686 | ((dev->backing_buffer) ? | 1715 | ((dev->backing_buffer) ? |
1687 | info->fix.smem_len * 2 : info->fix.smem_len) >> 10); | 1716 | info->fix.smem_len * 2 : info->fix.smem_len) >> 10); |
1688 | return 0; | 1717 | return; |
1689 | |||
1690 | err_del_attrs: | ||
1691 | for (i -= 1; i >= 0; i--) | ||
1692 | device_remove_file(info->dev, &fb_device_attrs[i]); | ||
1693 | 1718 | ||
1694 | error: | 1719 | error: |
1695 | if (dev) { | 1720 | dlfb_free_framebuffer(dev); |
1696 | |||
1697 | if (info) { | ||
1698 | if (info->cmap.len != 0) | ||
1699 | fb_dealloc_cmap(&info->cmap); | ||
1700 | if (info->monspecs.modedb) | ||
1701 | fb_destroy_modedb(info->monspecs.modedb); | ||
1702 | if (info->screen_base) | ||
1703 | vfree(info->screen_base); | ||
1704 | |||
1705 | fb_destroy_modelist(&info->modelist); | ||
1706 | |||
1707 | framebuffer_release(info); | ||
1708 | } | ||
1709 | |||
1710 | if (dev->backing_buffer) | ||
1711 | vfree(dev->backing_buffer); | ||
1712 | |||
1713 | kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ | ||
1714 | kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ | ||
1715 | |||
1716 | /* dev has been deallocated. Do not dereference */ | ||
1717 | } | ||
1718 | |||
1719 | return retval; | ||
1720 | } | 1721 | } |
1721 | 1722 | ||
1722 | static void dlfb_usb_disconnect(struct usb_interface *interface) | 1723 | static void dlfb_usb_disconnect(struct usb_interface *interface) |
@@ -1736,12 +1737,24 @@ static void dlfb_usb_disconnect(struct usb_interface *interface) | |||
1736 | /* When non-active we'll update virtual framebuffer, but no new urbs */ | 1737 | /* When non-active we'll update virtual framebuffer, but no new urbs */ |
1737 | atomic_set(&dev->usb_active, 0); | 1738 | atomic_set(&dev->usb_active, 0); |
1738 | 1739 | ||
1739 | /* remove udlfb's sysfs interfaces */ | 1740 | /* this function will wait for all in-flight urbs to complete */ |
1740 | for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) | 1741 | dlfb_free_urb_list(dev); |
1741 | device_remove_file(info->dev, &fb_device_attrs[i]); | 1742 | |
1742 | device_remove_bin_file(info->dev, &edid_attr); | 1743 | if (info) { |
1743 | unlink_framebuffer(info); | 1744 | |
1745 | /* remove udlfb's sysfs interfaces */ | ||
1746 | for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) | ||
1747 | device_remove_file(info->dev, &fb_device_attrs[i]); | ||
1748 | device_remove_bin_file(info->dev, &edid_attr); | ||
1749 | |||
1750 | /* it's safe to uncomment next line if your kernel | ||
1751 | doesn't yet have this function exported */ | ||
1752 | unlink_framebuffer(info); | ||
1753 | } | ||
1754 | |||
1744 | usb_set_intfdata(interface, NULL); | 1755 | usb_set_intfdata(interface, NULL); |
1756 | dev->udev = NULL; | ||
1757 | dev->gdev = NULL; | ||
1745 | 1758 | ||
1746 | /* if clients still have us open, will be freed on last close */ | 1759 | /* if clients still have us open, will be freed on last close */ |
1747 | if (dev->fb_count == 0) | 1760 | if (dev->fb_count == 0) |
@@ -1807,12 +1820,12 @@ static void dlfb_free_urb_list(struct dlfb_data *dev) | |||
1807 | int ret; | 1820 | int ret; |
1808 | unsigned long flags; | 1821 | unsigned long flags; |
1809 | 1822 | ||
1810 | pr_notice("Waiting for completes and freeing all render urbs\n"); | 1823 | pr_notice("Freeing all render urbs\n"); |
1811 | 1824 | ||
1812 | /* keep waiting and freeing, until we've got 'em all */ | 1825 | /* keep waiting and freeing, until we've got 'em all */ |
1813 | while (count--) { | 1826 | while (count--) { |
1814 | 1827 | ||
1815 | /* Getting interrupted means a leak, but ok at shutdown*/ | 1828 | /* Getting interrupted means a leak, but ok at disconnect */ |
1816 | ret = down_interruptible(&dev->urbs.limit_sem); | 1829 | ret = down_interruptible(&dev->urbs.limit_sem); |
1817 | if (ret) | 1830 | if (ret) |
1818 | break; | 1831 | break; |
@@ -1834,6 +1847,7 @@ static void dlfb_free_urb_list(struct dlfb_data *dev) | |||
1834 | kfree(node); | 1847 | kfree(node); |
1835 | } | 1848 | } |
1836 | 1849 | ||
1850 | dev->urbs.count = 0; | ||
1837 | } | 1851 | } |
1838 | 1852 | ||
1839 | static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) | 1853 | static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) |
diff --git a/include/video/udlfb.h b/include/video/udlfb.h index c41f308c9636..f9466fa54ba4 100644 --- a/include/video/udlfb.h +++ b/include/video/udlfb.h | |||
@@ -41,6 +41,7 @@ struct dlfb_data { | |||
41 | char *backing_buffer; | 41 | char *backing_buffer; |
42 | int fb_count; | 42 | int fb_count; |
43 | bool virtualized; /* true when physical usb device not present */ | 43 | bool virtualized; /* true when physical usb device not present */ |
44 | struct delayed_work init_framebuffer_work; | ||
44 | struct delayed_work free_framebuffer_work; | 45 | struct delayed_work free_framebuffer_work; |
45 | atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */ | 46 | atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */ |
46 | atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */ | 47 | atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */ |