diff options
-rw-r--r-- | MAINTAINERS | 9 | ||||
-rw-r--r-- | drivers/video/udlfb.c | 163 | ||||
-rw-r--r-- | include/video/udlfb.h | 1 |
3 files changed, 105 insertions, 68 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 9a648eb8e213..e662b8a3e3cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -2231,6 +2231,15 @@ F: Documentation/filesystems/quota.txt | |||
2231 | F: fs/quota/ | 2231 | F: fs/quota/ |
2232 | F: include/linux/quota*.h | 2232 | F: include/linux/quota*.h |
2233 | 2233 | ||
2234 | DISPLAYLINK USB 2.0 FRAMEBUFFER DRIVER (UDLFB) | ||
2235 | M: Bernie Thompson <bernie@plugable.com> | ||
2236 | L: linux-fbdev@vger.kernel.org | ||
2237 | S: Maintained | ||
2238 | W: http://plugable.com/category/projects/udlfb/ | ||
2239 | F: drivers/video/udlfb.c | ||
2240 | F: include/video/udlfb.h | ||
2241 | F: Documentation/fb/udlfb.txt | ||
2242 | |||
2234 | DISTRIBUTED LOCK MANAGER (DLM) | 2243 | DISTRIBUTED LOCK MANAGER (DLM) |
2235 | M: Christine Caulfield <ccaulfie@redhat.com> | 2244 | M: Christine Caulfield <ccaulfie@redhat.com> |
2236 | M: David Teigland <teigland@redhat.com> | 2245 | M: David Teigland <teigland@redhat.com> |
diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index a40c05ebbdc2..86c8b256e306 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c | |||
@@ -72,6 +72,7 @@ MODULE_DEVICE_TABLE(usb, id_table); | |||
72 | static bool console = 1; /* Allow fbcon to open framebuffer */ | 72 | static bool console = 1; /* Allow fbcon to open framebuffer */ |
73 | static bool fb_defio = 1; /* Detect mmap writes using page faults */ | 73 | static bool fb_defio = 1; /* Detect mmap writes using page faults */ |
74 | static bool shadow = 1; /* Optionally disable shadow framebuffer */ | 74 | static bool shadow = 1; /* Optionally disable shadow framebuffer */ |
75 | static int pixel_limit; /* Optionally force a pixel resolution limit */ | ||
75 | 76 | ||
76 | /* dlfb keeps a list of urbs for efficient bulk transfers */ | 77 | /* dlfb keeps a list of urbs for efficient bulk transfers */ |
77 | static void dlfb_urb_completion(struct urb *urb); | 78 | static void dlfb_urb_completion(struct urb *urb); |
@@ -918,10 +919,6 @@ static void dlfb_free(struct kref *kref) | |||
918 | { | 919 | { |
919 | struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref); | 920 | struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref); |
920 | 921 | ||
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) | 922 | if (dev->backing_buffer) |
926 | vfree(dev->backing_buffer); | 923 | vfree(dev->backing_buffer); |
927 | 924 | ||
@@ -940,35 +937,42 @@ static void dlfb_release_urb_work(struct work_struct *work) | |||
940 | up(&unode->dev->urbs.limit_sem); | 937 | up(&unode->dev->urbs.limit_sem); |
941 | } | 938 | } |
942 | 939 | ||
943 | static void dlfb_free_framebuffer_work(struct work_struct *work) | 940 | static void dlfb_free_framebuffer(struct dlfb_data *dev) |
944 | { | 941 | { |
945 | struct dlfb_data *dev = container_of(work, struct dlfb_data, | ||
946 | free_framebuffer_work.work); | ||
947 | struct fb_info *info = dev->info; | 942 | struct fb_info *info = dev->info; |
948 | int node = info->node; | ||
949 | 943 | ||
950 | unregister_framebuffer(info); | 944 | if (info) { |
945 | int node = info->node; | ||
951 | 946 | ||
952 | if (info->cmap.len != 0) | 947 | 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 | 948 | ||
959 | fb_destroy_modelist(&info->modelist); | 949 | if (info->cmap.len != 0) |
950 | fb_dealloc_cmap(&info->cmap); | ||
951 | if (info->monspecs.modedb) | ||
952 | fb_destroy_modedb(info->monspecs.modedb); | ||
953 | if (info->screen_base) | ||
954 | vfree(info->screen_base); | ||
960 | 955 | ||
961 | dev->info = 0; | 956 | fb_destroy_modelist(&info->modelist); |
962 | 957 | ||
963 | /* Assume info structure is freed after this point */ | 958 | dev->info = NULL; |
964 | framebuffer_release(info); | ||
965 | 959 | ||
966 | pr_warn("fb_info for /dev/fb%d has been freed\n", node); | 960 | /* Assume info structure is freed after this point */ |
961 | framebuffer_release(info); | ||
962 | |||
963 | pr_warn("fb_info for /dev/fb%d has been freed\n", node); | ||
964 | } | ||
967 | 965 | ||
968 | /* ref taken in probe() as part of registering framebfufer */ | 966 | /* ref taken in probe() as part of registering framebfufer */ |
969 | kref_put(&dev->kref, dlfb_free); | 967 | kref_put(&dev->kref, dlfb_free); |
970 | } | 968 | } |
971 | 969 | ||
970 | static void dlfb_free_framebuffer_work(struct work_struct *work) | ||
971 | { | ||
972 | struct dlfb_data *dev = container_of(work, struct dlfb_data, | ||
973 | free_framebuffer_work.work); | ||
974 | dlfb_free_framebuffer(dev); | ||
975 | } | ||
972 | /* | 976 | /* |
973 | * Assumes caller is holding info->lock mutex (for open and release at least) | 977 | * Assumes caller is holding info->lock mutex (for open and release at least) |
974 | */ | 978 | */ |
@@ -1012,7 +1016,8 @@ static int dlfb_is_valid_mode(struct fb_videomode *mode, | |||
1012 | return 0; | 1016 | return 0; |
1013 | } | 1017 | } |
1014 | 1018 | ||
1015 | pr_info("%dx%d valid mode\n", mode->xres, mode->yres); | 1019 | pr_info("%dx%d @ %d Hz valid mode\n", mode->xres, mode->yres, |
1020 | mode->refresh); | ||
1016 | 1021 | ||
1017 | return 1; | 1022 | return 1; |
1018 | } | 1023 | } |
@@ -1537,7 +1542,7 @@ static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev, | |||
1537 | u8 length; | 1542 | u8 length; |
1538 | u16 key; | 1543 | u16 key; |
1539 | 1544 | ||
1540 | key = *((u16 *) desc); | 1545 | key = le16_to_cpu(*((u16 *) desc)); |
1541 | desc += sizeof(u16); | 1546 | desc += sizeof(u16); |
1542 | length = *desc; | 1547 | length = *desc; |
1543 | desc++; | 1548 | desc++; |
@@ -1570,14 +1575,15 @@ success: | |||
1570 | kfree(buf); | 1575 | kfree(buf); |
1571 | return true; | 1576 | return true; |
1572 | } | 1577 | } |
1578 | |||
1579 | static void dlfb_init_framebuffer_work(struct work_struct *work); | ||
1580 | |||
1573 | static int dlfb_usb_probe(struct usb_interface *interface, | 1581 | static int dlfb_usb_probe(struct usb_interface *interface, |
1574 | const struct usb_device_id *id) | 1582 | const struct usb_device_id *id) |
1575 | { | 1583 | { |
1576 | struct usb_device *usbdev; | 1584 | struct usb_device *usbdev; |
1577 | struct dlfb_data *dev = 0; | 1585 | struct dlfb_data *dev = 0; |
1578 | struct fb_info *info = 0; | ||
1579 | int retval = -ENOMEM; | 1586 | int retval = -ENOMEM; |
1580 | int i; | ||
1581 | 1587 | ||
1582 | /* usb initialization */ | 1588 | /* usb initialization */ |
1583 | 1589 | ||
@@ -1589,9 +1595,7 @@ static int dlfb_usb_probe(struct usb_interface *interface, | |||
1589 | goto error; | 1595 | goto error; |
1590 | } | 1596 | } |
1591 | 1597 | ||
1592 | /* we need to wait for both usb and fbdev to spin down on disconnect */ | ||
1593 | kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ | 1598 | kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ |
1594 | kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ | ||
1595 | 1599 | ||
1596 | dev->udev = usbdev; | 1600 | dev->udev = usbdev; |
1597 | dev->gdev = &usbdev->dev; /* our generic struct device * */ | 1601 | dev->gdev = &usbdev->dev; /* our generic struct device * */ |
@@ -1613,16 +1617,53 @@ static int dlfb_usb_probe(struct usb_interface *interface, | |||
1613 | goto error; | 1617 | goto error; |
1614 | } | 1618 | } |
1615 | 1619 | ||
1620 | if (pixel_limit) { | ||
1621 | pr_warn("DL chip limit of %d overriden" | ||
1622 | " by module param to %d\n", | ||
1623 | dev->sku_pixel_limit, pixel_limit); | ||
1624 | dev->sku_pixel_limit = pixel_limit; | ||
1625 | } | ||
1626 | |||
1627 | |||
1616 | if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { | 1628 | if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { |
1617 | retval = -ENOMEM; | 1629 | retval = -ENOMEM; |
1618 | pr_err("dlfb_alloc_urb_list failed\n"); | 1630 | pr_err("dlfb_alloc_urb_list failed\n"); |
1619 | goto error; | 1631 | goto error; |
1620 | } | 1632 | } |
1621 | 1633 | ||
1634 | kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ | ||
1635 | |||
1622 | /* We don't register a new USB class. Our client interface is fbdev */ | 1636 | /* We don't register a new USB class. Our client interface is fbdev */ |
1623 | 1637 | ||
1638 | /* Workitem keep things fast & simple during USB enumeration */ | ||
1639 | INIT_DELAYED_WORK(&dev->init_framebuffer_work, | ||
1640 | dlfb_init_framebuffer_work); | ||
1641 | schedule_delayed_work(&dev->init_framebuffer_work, 0); | ||
1642 | |||
1643 | return 0; | ||
1644 | |||
1645 | error: | ||
1646 | if (dev) { | ||
1647 | |||
1648 | kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ | ||
1649 | kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ | ||
1650 | |||
1651 | /* dev has been deallocated. Do not dereference */ | ||
1652 | } | ||
1653 | |||
1654 | return retval; | ||
1655 | } | ||
1656 | |||
1657 | static void dlfb_init_framebuffer_work(struct work_struct *work) | ||
1658 | { | ||
1659 | struct dlfb_data *dev = container_of(work, struct dlfb_data, | ||
1660 | init_framebuffer_work.work); | ||
1661 | struct fb_info *info; | ||
1662 | int retval; | ||
1663 | int i; | ||
1664 | |||
1624 | /* allocates framebuffer driver structure, not framebuffer memory */ | 1665 | /* allocates framebuffer driver structure, not framebuffer memory */ |
1625 | info = framebuffer_alloc(0, &interface->dev); | 1666 | info = framebuffer_alloc(0, dev->gdev); |
1626 | if (!info) { | 1667 | if (!info) { |
1627 | retval = -ENOMEM; | 1668 | retval = -ENOMEM; |
1628 | pr_err("framebuffer_alloc failed\n"); | 1669 | pr_err("framebuffer_alloc failed\n"); |
@@ -1668,15 +1709,13 @@ static int dlfb_usb_probe(struct usb_interface *interface, | |||
1668 | for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) { | 1709 | for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) { |
1669 | retval = device_create_file(info->dev, &fb_device_attrs[i]); | 1710 | retval = device_create_file(info->dev, &fb_device_attrs[i]); |
1670 | if (retval) { | 1711 | if (retval) { |
1671 | pr_err("device_create_file failed %d\n", retval); | 1712 | pr_warn("device_create_file failed %d\n", retval); |
1672 | goto err_del_attrs; | ||
1673 | } | 1713 | } |
1674 | } | 1714 | } |
1675 | 1715 | ||
1676 | retval = device_create_bin_file(info->dev, &edid_attr); | 1716 | retval = device_create_bin_file(info->dev, &edid_attr); |
1677 | if (retval) { | 1717 | if (retval) { |
1678 | pr_err("device_create_bin_file failed %d\n", retval); | 1718 | pr_warn("device_create_bin_file failed %d\n", retval); |
1679 | goto err_del_attrs; | ||
1680 | } | 1719 | } |
1681 | 1720 | ||
1682 | pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." | 1721 | pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." |
@@ -1684,38 +1723,10 @@ static int dlfb_usb_probe(struct usb_interface *interface, | |||
1684 | info->var.xres, info->var.yres, | 1723 | info->var.xres, info->var.yres, |
1685 | ((dev->backing_buffer) ? | 1724 | ((dev->backing_buffer) ? |
1686 | info->fix.smem_len * 2 : info->fix.smem_len) >> 10); | 1725 | info->fix.smem_len * 2 : info->fix.smem_len) >> 10); |
1687 | return 0; | 1726 | return; |
1688 | |||
1689 | err_del_attrs: | ||
1690 | for (i -= 1; i >= 0; i--) | ||
1691 | device_remove_file(info->dev, &fb_device_attrs[i]); | ||
1692 | 1727 | ||
1693 | error: | 1728 | error: |
1694 | if (dev) { | 1729 | dlfb_free_framebuffer(dev); |
1695 | |||
1696 | if (info) { | ||
1697 | if (info->cmap.len != 0) | ||
1698 | fb_dealloc_cmap(&info->cmap); | ||
1699 | if (info->monspecs.modedb) | ||
1700 | fb_destroy_modedb(info->monspecs.modedb); | ||
1701 | if (info->screen_base) | ||
1702 | vfree(info->screen_base); | ||
1703 | |||
1704 | fb_destroy_modelist(&info->modelist); | ||
1705 | |||
1706 | framebuffer_release(info); | ||
1707 | } | ||
1708 | |||
1709 | if (dev->backing_buffer) | ||
1710 | vfree(dev->backing_buffer); | ||
1711 | |||
1712 | kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ | ||
1713 | kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ | ||
1714 | |||
1715 | /* dev has been deallocated. Do not dereference */ | ||
1716 | } | ||
1717 | |||
1718 | return retval; | ||
1719 | } | 1730 | } |
1720 | 1731 | ||
1721 | static void dlfb_usb_disconnect(struct usb_interface *interface) | 1732 | static void dlfb_usb_disconnect(struct usb_interface *interface) |
@@ -1735,12 +1746,24 @@ static void dlfb_usb_disconnect(struct usb_interface *interface) | |||
1735 | /* When non-active we'll update virtual framebuffer, but no new urbs */ | 1746 | /* When non-active we'll update virtual framebuffer, but no new urbs */ |
1736 | atomic_set(&dev->usb_active, 0); | 1747 | atomic_set(&dev->usb_active, 0); |
1737 | 1748 | ||
1738 | /* remove udlfb's sysfs interfaces */ | 1749 | /* this function will wait for all in-flight urbs to complete */ |
1739 | for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) | 1750 | dlfb_free_urb_list(dev); |
1740 | device_remove_file(info->dev, &fb_device_attrs[i]); | 1751 | |
1741 | device_remove_bin_file(info->dev, &edid_attr); | 1752 | if (info) { |
1742 | unlink_framebuffer(info); | 1753 | |
1754 | /* remove udlfb's sysfs interfaces */ | ||
1755 | for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) | ||
1756 | device_remove_file(info->dev, &fb_device_attrs[i]); | ||
1757 | device_remove_bin_file(info->dev, &edid_attr); | ||
1758 | |||
1759 | /* it's safe to uncomment next line if your kernel | ||
1760 | doesn't yet have this function exported */ | ||
1761 | unlink_framebuffer(info); | ||
1762 | } | ||
1763 | |||
1743 | usb_set_intfdata(interface, NULL); | 1764 | usb_set_intfdata(interface, NULL); |
1765 | dev->udev = NULL; | ||
1766 | dev->gdev = NULL; | ||
1744 | 1767 | ||
1745 | /* if clients still have us open, will be freed on last close */ | 1768 | /* if clients still have us open, will be freed on last close */ |
1746 | if (dev->fb_count == 0) | 1769 | if (dev->fb_count == 0) |
@@ -1806,12 +1829,12 @@ static void dlfb_free_urb_list(struct dlfb_data *dev) | |||
1806 | int ret; | 1829 | int ret; |
1807 | unsigned long flags; | 1830 | unsigned long flags; |
1808 | 1831 | ||
1809 | pr_notice("Waiting for completes and freeing all render urbs\n"); | 1832 | pr_notice("Freeing all render urbs\n"); |
1810 | 1833 | ||
1811 | /* keep waiting and freeing, until we've got 'em all */ | 1834 | /* keep waiting and freeing, until we've got 'em all */ |
1812 | while (count--) { | 1835 | while (count--) { |
1813 | 1836 | ||
1814 | /* Getting interrupted means a leak, but ok at shutdown*/ | 1837 | /* Getting interrupted means a leak, but ok at disconnect */ |
1815 | ret = down_interruptible(&dev->urbs.limit_sem); | 1838 | ret = down_interruptible(&dev->urbs.limit_sem); |
1816 | if (ret) | 1839 | if (ret) |
1817 | break; | 1840 | break; |
@@ -1833,6 +1856,7 @@ static void dlfb_free_urb_list(struct dlfb_data *dev) | |||
1833 | kfree(node); | 1856 | kfree(node); |
1834 | } | 1857 | } |
1835 | 1858 | ||
1859 | dev->urbs.count = 0; | ||
1836 | } | 1860 | } |
1837 | 1861 | ||
1838 | static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) | 1862 | static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) |
@@ -1948,6 +1972,9 @@ MODULE_PARM_DESC(fb_defio, "Page fault detection of mmap writes"); | |||
1948 | module_param(shadow, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); | 1972 | module_param(shadow, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); |
1949 | MODULE_PARM_DESC(shadow, "Shadow vid mem. Disable to save mem but lose perf"); | 1973 | MODULE_PARM_DESC(shadow, "Shadow vid mem. Disable to save mem but lose perf"); |
1950 | 1974 | ||
1975 | module_param(pixel_limit, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); | ||
1976 | MODULE_PARM_DESC(pixel_limit, "Force limit on max mode (in x*y pixels)"); | ||
1977 | |||
1951 | MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, " | 1978 | MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, " |
1952 | "Jaya Kumar <jayakumar.lkml@gmail.com>, " | 1979 | "Jaya Kumar <jayakumar.lkml@gmail.com>, " |
1953 | "Bernie Thompson <bernie@plugable.com>"); | 1980 | "Bernie Thompson <bernie@plugable.com>"); |
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 */ |