Merge pull request #1 from bernieplug/fbdev-next
udlfb patches for fbdev-next
This commit is contained in:
commit
2c0fad8e90
3 changed files with 105 additions and 68 deletions
|
@ -2231,6 +2231,15 @@ F: Documentation/filesystems/quota.txt
|
||||||
F: fs/quota/
|
F: fs/quota/
|
||||||
F: include/linux/quota*.h
|
F: include/linux/quota*.h
|
||||||
|
|
||||||
|
DISPLAYLINK USB 2.0 FRAMEBUFFER DRIVER (UDLFB)
|
||||||
|
M: Bernie Thompson <bernie@plugable.com>
|
||||||
|
L: linux-fbdev@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
W: http://plugable.com/category/projects/udlfb/
|
||||||
|
F: drivers/video/udlfb.c
|
||||||
|
F: include/video/udlfb.h
|
||||||
|
F: Documentation/fb/udlfb.txt
|
||||||
|
|
||||||
DISTRIBUTED LOCK MANAGER (DLM)
|
DISTRIBUTED LOCK MANAGER (DLM)
|
||||||
M: Christine Caulfield <ccaulfie@redhat.com>
|
M: Christine Caulfield <ccaulfie@redhat.com>
|
||||||
M: David Teigland <teigland@redhat.com>
|
M: David Teigland <teigland@redhat.com>
|
||||||
|
|
|
@ -72,6 +72,7 @@ MODULE_DEVICE_TABLE(usb, id_table);
|
||||||
static bool console = 1; /* Allow fbcon to open framebuffer */
|
static bool console = 1; /* Allow fbcon to open framebuffer */
|
||||||
static bool fb_defio = 1; /* Detect mmap writes using page faults */
|
static bool fb_defio = 1; /* Detect mmap writes using page faults */
|
||||||
static bool shadow = 1; /* Optionally disable shadow framebuffer */
|
static bool shadow = 1; /* Optionally disable shadow framebuffer */
|
||||||
|
static int pixel_limit; /* Optionally force a pixel resolution limit */
|
||||||
|
|
||||||
/* dlfb keeps a list of urbs for efficient bulk transfers */
|
/* dlfb keeps a list of urbs for efficient bulk transfers */
|
||||||
static void dlfb_urb_completion(struct urb *urb);
|
static void dlfb_urb_completion(struct urb *urb);
|
||||||
|
@ -918,10 +919,6 @@ static void dlfb_free(struct kref *kref)
|
||||||
{
|
{
|
||||||
struct dlfb_data *dev = container_of(kref, struct dlfb_data, 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)
|
if (dev->backing_buffer)
|
||||||
vfree(dev->backing_buffer);
|
vfree(dev->backing_buffer);
|
||||||
|
|
||||||
|
@ -940,35 +937,42 @@ static void dlfb_release_urb_work(struct work_struct *work)
|
||||||
up(&unode->dev->urbs.limit_sem);
|
up(&unode->dev->urbs.limit_sem);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dlfb_free_framebuffer_work(struct work_struct *work)
|
static void dlfb_free_framebuffer(struct dlfb_data *dev)
|
||||||
{
|
{
|
||||||
struct dlfb_data *dev = container_of(work, struct dlfb_data,
|
|
||||||
free_framebuffer_work.work);
|
|
||||||
struct fb_info *info = dev->info;
|
struct fb_info *info = dev->info;
|
||||||
int node = info->node;
|
|
||||||
|
|
||||||
unregister_framebuffer(info);
|
if (info) {
|
||||||
|
int node = info->node;
|
||||||
|
|
||||||
if (info->cmap.len != 0)
|
unregister_framebuffer(info);
|
||||||
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);
|
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);
|
||||||
|
|
||||||
dev->info = 0;
|
fb_destroy_modelist(&info->modelist);
|
||||||
|
|
||||||
/* Assume info structure is freed after this point */
|
dev->info = NULL;
|
||||||
framebuffer_release(info);
|
|
||||||
|
|
||||||
pr_warn("fb_info for /dev/fb%d has been freed\n", node);
|
/* Assume info structure is freed after this point */
|
||||||
|
framebuffer_release(info);
|
||||||
|
|
||||||
|
pr_warn("fb_info for /dev/fb%d has been freed\n", node);
|
||||||
|
}
|
||||||
|
|
||||||
/* ref taken in probe() as part of registering framebfufer */
|
/* ref taken in probe() as part of registering framebfufer */
|
||||||
kref_put(&dev->kref, dlfb_free);
|
kref_put(&dev->kref, dlfb_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dlfb_free_framebuffer_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct dlfb_data *dev = container_of(work, struct dlfb_data,
|
||||||
|
free_framebuffer_work.work);
|
||||||
|
dlfb_free_framebuffer(dev);
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Assumes caller is holding info->lock mutex (for open and release at least)
|
* Assumes caller is holding info->lock mutex (for open and release at least)
|
||||||
*/
|
*/
|
||||||
|
@ -1012,7 +1016,8 @@ static int dlfb_is_valid_mode(struct fb_videomode *mode,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("%dx%d valid mode\n", mode->xres, mode->yres);
|
pr_info("%dx%d @ %d Hz valid mode\n", mode->xres, mode->yres,
|
||||||
|
mode->refresh);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1537,7 +1542,7 @@ static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev,
|
||||||
u8 length;
|
u8 length;
|
||||||
u16 key;
|
u16 key;
|
||||||
|
|
||||||
key = *((u16 *) desc);
|
key = le16_to_cpu(*((u16 *) desc));
|
||||||
desc += sizeof(u16);
|
desc += sizeof(u16);
|
||||||
length = *desc;
|
length = *desc;
|
||||||
desc++;
|
desc++;
|
||||||
|
@ -1570,14 +1575,15 @@ success:
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dlfb_init_framebuffer_work(struct work_struct *work);
|
||||||
|
|
||||||
static int dlfb_usb_probe(struct usb_interface *interface,
|
static int dlfb_usb_probe(struct usb_interface *interface,
|
||||||
const struct usb_device_id *id)
|
const struct usb_device_id *id)
|
||||||
{
|
{
|
||||||
struct usb_device *usbdev;
|
struct usb_device *usbdev;
|
||||||
struct dlfb_data *dev = 0;
|
struct dlfb_data *dev = 0;
|
||||||
struct fb_info *info = 0;
|
|
||||||
int retval = -ENOMEM;
|
int retval = -ENOMEM;
|
||||||
int i;
|
|
||||||
|
|
||||||
/* usb initialization */
|
/* usb initialization */
|
||||||
|
|
||||||
|
@ -1589,9 +1595,7 @@ static int dlfb_usb_probe(struct usb_interface *interface,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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_init(&dev->kref); /* matching kref_put in usb .disconnect fn */
|
||||||
kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */
|
|
||||||
|
|
||||||
dev->udev = usbdev;
|
dev->udev = usbdev;
|
||||||
dev->gdev = &usbdev->dev; /* our generic struct device * */
|
dev->gdev = &usbdev->dev; /* our generic struct device * */
|
||||||
|
@ -1613,16 +1617,53 @@ static int dlfb_usb_probe(struct usb_interface *interface,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pixel_limit) {
|
||||||
|
pr_warn("DL chip limit of %d overriden"
|
||||||
|
" by module param to %d\n",
|
||||||
|
dev->sku_pixel_limit, pixel_limit);
|
||||||
|
dev->sku_pixel_limit = pixel_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
|
if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
|
||||||
retval = -ENOMEM;
|
retval = -ENOMEM;
|
||||||
pr_err("dlfb_alloc_urb_list failed\n");
|
pr_err("dlfb_alloc_urb_list failed\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */
|
||||||
|
|
||||||
/* We don't register a new USB class. Our client interface is fbdev */
|
/* We don't register a new USB class. Our client interface is fbdev */
|
||||||
|
|
||||||
|
/* Workitem keep things fast & simple during USB enumeration */
|
||||||
|
INIT_DELAYED_WORK(&dev->init_framebuffer_work,
|
||||||
|
dlfb_init_framebuffer_work);
|
||||||
|
schedule_delayed_work(&dev->init_framebuffer_work, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (dev) {
|
||||||
|
|
||||||
|
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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dlfb_init_framebuffer_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct dlfb_data *dev = container_of(work, struct dlfb_data,
|
||||||
|
init_framebuffer_work.work);
|
||||||
|
struct fb_info *info;
|
||||||
|
int retval;
|
||||||
|
int i;
|
||||||
|
|
||||||
/* allocates framebuffer driver structure, not framebuffer memory */
|
/* allocates framebuffer driver structure, not framebuffer memory */
|
||||||
info = framebuffer_alloc(0, &interface->dev);
|
info = framebuffer_alloc(0, dev->gdev);
|
||||||
if (!info) {
|
if (!info) {
|
||||||
retval = -ENOMEM;
|
retval = -ENOMEM;
|
||||||
pr_err("framebuffer_alloc failed\n");
|
pr_err("framebuffer_alloc failed\n");
|
||||||
|
@ -1668,15 +1709,13 @@ static int dlfb_usb_probe(struct usb_interface *interface,
|
||||||
for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) {
|
for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) {
|
||||||
retval = device_create_file(info->dev, &fb_device_attrs[i]);
|
retval = device_create_file(info->dev, &fb_device_attrs[i]);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
pr_err("device_create_file failed %d\n", retval);
|
pr_warn("device_create_file failed %d\n", retval);
|
||||||
goto err_del_attrs;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = device_create_bin_file(info->dev, &edid_attr);
|
retval = device_create_bin_file(info->dev, &edid_attr);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
pr_err("device_create_bin_file failed %d\n", retval);
|
pr_warn("device_create_bin_file failed %d\n", retval);
|
||||||
goto err_del_attrs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution."
|
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,
|
||||||
info->var.xres, info->var.yres,
|
info->var.xres, info->var.yres,
|
||||||
((dev->backing_buffer) ?
|
((dev->backing_buffer) ?
|
||||||
info->fix.smem_len * 2 : info->fix.smem_len) >> 10);
|
info->fix.smem_len * 2 : info->fix.smem_len) >> 10);
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
err_del_attrs:
|
|
||||||
for (i -= 1; i >= 0; i--)
|
|
||||||
device_remove_file(info->dev, &fb_device_attrs[i]);
|
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (dev) {
|
dlfb_free_framebuffer(dev);
|
||||||
|
|
||||||
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 */
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dlfb_usb_disconnect(struct usb_interface *interface)
|
static void dlfb_usb_disconnect(struct usb_interface *interface)
|
||||||
|
@ -1735,12 +1746,24 @@ static void dlfb_usb_disconnect(struct usb_interface *interface)
|
||||||
/* When non-active we'll update virtual framebuffer, but no new urbs */
|
/* When non-active we'll update virtual framebuffer, but no new urbs */
|
||||||
atomic_set(&dev->usb_active, 0);
|
atomic_set(&dev->usb_active, 0);
|
||||||
|
|
||||||
/* remove udlfb's sysfs interfaces */
|
/* this function will wait for all in-flight urbs to complete */
|
||||||
for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++)
|
dlfb_free_urb_list(dev);
|
||||||
device_remove_file(info->dev, &fb_device_attrs[i]);
|
|
||||||
device_remove_bin_file(info->dev, &edid_attr);
|
if (info) {
|
||||||
unlink_framebuffer(info);
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* it's safe to uncomment next line if your kernel
|
||||||
|
doesn't yet have this function exported */
|
||||||
|
unlink_framebuffer(info);
|
||||||
|
}
|
||||||
|
|
||||||
usb_set_intfdata(interface, NULL);
|
usb_set_intfdata(interface, NULL);
|
||||||
|
dev->udev = NULL;
|
||||||
|
dev->gdev = NULL;
|
||||||
|
|
||||||
/* if clients still have us open, will be freed on last close */
|
/* if clients still have us open, will be freed on last close */
|
||||||
if (dev->fb_count == 0)
|
if (dev->fb_count == 0)
|
||||||
|
@ -1806,12 +1829,12 @@ static void dlfb_free_urb_list(struct dlfb_data *dev)
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
pr_notice("Waiting for completes and freeing all render urbs\n");
|
pr_notice("Freeing all render urbs\n");
|
||||||
|
|
||||||
/* keep waiting and freeing, until we've got 'em all */
|
/* keep waiting and freeing, until we've got 'em all */
|
||||||
while (count--) {
|
while (count--) {
|
||||||
|
|
||||||
/* Getting interrupted means a leak, but ok at shutdown*/
|
/* Getting interrupted means a leak, but ok at disconnect */
|
||||||
ret = down_interruptible(&dev->urbs.limit_sem);
|
ret = down_interruptible(&dev->urbs.limit_sem);
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
|
@ -1833,6 +1856,7 @@ static void dlfb_free_urb_list(struct dlfb_data *dev)
|
||||||
kfree(node);
|
kfree(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev->urbs.count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size)
|
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");
|
||||||
module_param(shadow, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
|
module_param(shadow, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
|
||||||
MODULE_PARM_DESC(shadow, "Shadow vid mem. Disable to save mem but lose perf");
|
MODULE_PARM_DESC(shadow, "Shadow vid mem. Disable to save mem but lose perf");
|
||||||
|
|
||||||
|
module_param(pixel_limit, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
|
||||||
|
MODULE_PARM_DESC(pixel_limit, "Force limit on max mode (in x*y pixels)");
|
||||||
|
|
||||||
MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, "
|
MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, "
|
||||||
"Jaya Kumar <jayakumar.lkml@gmail.com>, "
|
"Jaya Kumar <jayakumar.lkml@gmail.com>, "
|
||||||
"Bernie Thompson <bernie@plugable.com>");
|
"Bernie Thompson <bernie@plugable.com>");
|
||||||
|
|
|
@ -41,6 +41,7 @@ struct dlfb_data {
|
||||||
char *backing_buffer;
|
char *backing_buffer;
|
||||||
int fb_count;
|
int fb_count;
|
||||||
bool virtualized; /* true when physical usb device not present */
|
bool virtualized; /* true when physical usb device not present */
|
||||||
|
struct delayed_work init_framebuffer_work;
|
||||||
struct delayed_work free_framebuffer_work;
|
struct delayed_work free_framebuffer_work;
|
||||||
atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */
|
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 lost_pixels; /* 1 = a render op failed. Need screen refresh */
|
||||||
|
|
Loading…
Add table
Reference in a new issue