summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gm12u320_drv.c1
-rw-r--r--gm12u320_drv.h4
-rw-r--r--gm12u320_main.c78
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;
}