From af316dd973de68f1ce6439106df94e3821d240c1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 6 Jul 2015 17:18:30 +0200 Subject: Fix disconnect handling Signed-off-by: Hans de Goede --- gm12u320_drv.c | 1 + gm12u320_drv.h | 4 +++ gm12u320_main.c | 78 ++++++++++++++++++++++++++++++++++++--------------------- 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/gm12u320_drv.c b/gm12u320_drv.c index 1469f56..bf9d9a7 100644 --- a/gm12u320_drv.c +++ b/gm12u320_drv.c @@ -107,6 +107,7 @@ static void gm12u320_usb_disconnect(struct usb_interface *interface) drm_kms_helper_poll_disable(dev); drm_connector_unplug_all(dev); gm12u320_fbdev_unplug(dev); + gm12u320_stop_fb_update(dev); drm_unplug_dev(dev); } diff --git a/gm12u320_drv.h b/gm12u320_drv.h index 4af1feb..76620e8 100644 --- a/gm12u320_drv.h +++ b/gm12u320_drv.h @@ -52,6 +52,7 @@ struct gm12u320_device { unsigned char *cmd_buf; unsigned char *data_buf[GM12U320_BLOCK_COUNT]; struct { + bool run; struct workqueue_struct *workq; struct work_struct work; wait_queue_head_t waitq; @@ -122,4 +123,7 @@ int gm12u320_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); void gm12u320_fb_mark_dirty(struct gm12u320_framebuffer *fb, int x1, int x2, int y1, int y2); +void gm12u320_start_fb_update(struct drm_device *dev); +void gm12u320_stop_fb_update(struct drm_device *dev); + #endif diff --git a/gm12u320_main.c b/gm12u320_main.c index 011444e..e51fcd3 100644 --- a/gm12u320_main.c +++ b/gm12u320_main.c @@ -281,12 +281,12 @@ end_cpu_access: fb->obj->base.size, DMA_FROM_DEVICE); } -static int gm12u320_frame_ready(struct gm12u320_device *gm12u320) +static int gm12u320_fb_update_ready(struct gm12u320_device *gm12u320) { int ret; spin_lock(&gm12u320->fb_update.lock); - ret = gm12u320->fb_update.fb != NULL; + ret = !gm12u320->fb_update.run || gm12u320->fb_update.fb != NULL; spin_unlock(&gm12u320->fb_update.lock); return ret; @@ -302,15 +302,7 @@ static void gm12u320_fb_update_work(struct work_struct *work) int frame = 0; int ret = 0; - while (1) { - /* - * We must draw a frame every 2s otherwise the projector - * switches back to showing its logo. - */ - wait_event_timeout(gm12u320->fb_update.waitq, - gm12u320_frame_ready(gm12u320), - IDLE_TIMEOUT); - + while (gm12u320->fb_update.run) { spin_lock(&gm12u320->fb_update.lock); fb = gm12u320->fb_update.fb; x1 = gm12u320->fb_update.x1; @@ -380,14 +372,52 @@ static void gm12u320_fb_update_work(struct work_struct *work) draw_status_timeout = CMD_TIMEOUT; frame = !frame; + + /* + * We must draw a frame every 2s otherwise the projector + * switches back to showing its logo. + */ + wait_event_timeout(gm12u320->fb_update.waitq, + gm12u320_fb_update_ready(gm12u320), + IDLE_TIMEOUT); } + return; err: /* Do not log errors caused by module unload or device unplug */ - if (ret != -ENOENT && ret != -ECONNRESET && ret != -ESHUTDOWN && - ret != -ENODEV) + if (ret != -ECONNRESET && ret != -ESHUTDOWN) dev_err(&gm12u320->udev->dev, "Frame update error: %d\n", ret); } +void gm12u320_start_fb_update(struct drm_device *dev) +{ + struct gm12u320_device *gm12u320 = dev->dev_private; + + spin_lock(&gm12u320->fb_update.lock); + gm12u320->fb_update.run = true; + spin_unlock(&gm12u320->fb_update.lock); + + queue_work(gm12u320->fb_update.workq, &gm12u320->fb_update.work); +} + +void gm12u320_stop_fb_update(struct drm_device *dev) +{ + struct gm12u320_device *gm12u320 = dev->dev_private; + + spin_lock(&gm12u320->fb_update.lock); + gm12u320->fb_update.run = false; + spin_unlock(&gm12u320->fb_update.lock); + + wake_up(&gm12u320->fb_update.waitq); + cancel_work_sync(&gm12u320->fb_update.work); + + spin_lock(&gm12u320->fb_update.lock); + if (gm12u320->fb_update.fb) { + drm_framebuffer_unreference(&gm12u320->fb_update.fb->base); + gm12u320->fb_update.fb = NULL; + } + spin_unlock(&gm12u320->fb_update.lock); +} + int gm12u320_driver_load(struct drm_device *dev, unsigned long flags) { struct usb_device *udev = (void*)flags; @@ -413,16 +443,16 @@ int gm12u320_driver_load(struct drm_device *dev, unsigned long flags) if (ret) goto err; - ret = gm12u320_usb_alloc(gm12u320); - if (ret) - goto err; - gm12u320->fb_update.workq = create_singlethread_workqueue(DRIVER_NAME); if (!gm12u320->fb_update.workq) { ret = -ENOMEM; goto err; } + ret = gm12u320_usb_alloc(gm12u320); + if (ret) + goto err; + DRM_DEBUG("\n"); ret = gm12u320_modeset_init(dev); if (ret) @@ -436,7 +466,7 @@ int gm12u320_driver_load(struct drm_device *dev, unsigned long flags) if (ret) goto err_fb; - queue_work(gm12u320->fb_update.workq, &gm12u320->fb_update.work); + gm12u320_start_fb_update(dev); return 0; err_fb: @@ -452,20 +482,12 @@ int gm12u320_driver_unload(struct drm_device *dev) { struct gm12u320_device *gm12u320 = dev->dev_private; - /* Wake up fb_update_work, so that it sees the disconnect and exits */ - wake_up(&gm12u320->fb_update.waitq); - cancel_work_sync(&gm12u320->fb_update.work); - destroy_workqueue(gm12u320->fb_update.workq); - gm12u320_usb_free(gm12u320); - - spin_lock(&gm12u320->fb_update.lock); - if (gm12u320->fb_update.fb) - drm_framebuffer_unreference(&gm12u320->fb_update.fb->base); - spin_unlock(&gm12u320->fb_update.lock); drm_vblank_cleanup(dev); gm12u320_fbdev_cleanup(dev); gm12u320_modeset_cleanup(dev); + gm12u320_usb_free(gm12u320); + destroy_workqueue(gm12u320->fb_update.workq); kfree(gm12u320); return 0; } -- cgit v1.2.3