Instead of having a work item that never stops (which really should be a kthread), with a dedicated workqueue to not upset anyone else, use a delayed work. A bunch of changes:
- We can throw out all the custom wakeup and requeue logic and state tracking. If we schedule the work with a 0 delay it'll get scheduled immediately. - Persistent state (frame & draw_status_timeout) need to be moved out of the work. - diff is bigger than the changes, biggest chunk is reindenting the work fn because it lost its while loop. Lots of code deleting as consequence all over. Specifically we can delete the drm_driver.release code now! v2: Review from Hans: - Use mod_delayed_work in the plane update path to make sure we do actually schedule immediately). In the worker we still want queue_delayed_work, which won't modify the timeout when the work is already scheduled. Which is exactly what we want if the work races with a plane update. - Switch to system_long_wq, Hans says on usb2 a plane upload can take 80 ms. Reviewed-by: Hans de Goede <hdego...@redhat.com> Signed-off-by: Daniel Vetter <daniel.vet...@intel.com> Cc: Hans de Goede <hdego...@redhat.com> Cc: "Noralf Trønnes" <nor...@tronnes.org> --- drivers/gpu/drm/tiny/gm12u320.c | 171 +++++++++++++------------------- 1 file changed, 68 insertions(+), 103 deletions(-) diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index 9627cbc3ec64..6f0ea2827d62 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -89,13 +89,12 @@ 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; + struct delayed_work work; struct mutex lock; struct drm_framebuffer *fb; struct drm_rect rect; + int frame; + int draw_status_timeout; } fb_update; }; @@ -183,19 +182,9 @@ static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320) data_block_footer, DATA_BLOCK_FOOTER_SIZE); } - gm12u320->fb_update.workq = create_singlethread_workqueue(DRIVER_NAME); - if (!gm12u320->fb_update.workq) - return -ENOMEM; - return 0; } -static void gm12u320_usb_free(struct gm12u320_device *gm12u320) -{ - if (gm12u320->fb_update.workq) - destroy_workqueue(gm12u320->fb_update.workq); -} - static int gm12u320_misc_request(struct gm12u320_device *gm12u320, u8 req_a, u8 req_b, u8 arg_a, u8 arg_b, u8 arg_c, u8 arg_d) @@ -338,80 +327,77 @@ static void gm12u320_copy_fb_to_blocks(struct gm12u320_device *gm12u320) static void gm12u320_fb_update_work(struct work_struct *work) { struct gm12u320_device *gm12u320 = - container_of(work, struct gm12u320_device, fb_update.work); - int draw_status_timeout = FIRST_FRAME_TIMEOUT; + container_of(to_delayed_work(work), struct gm12u320_device, + fb_update.work); int block, block_size, len; - int frame = 0; int ret = 0; - while (gm12u320->fb_update.run) { - gm12u320_copy_fb_to_blocks(gm12u320); - - for (block = 0; block < GM12U320_BLOCK_COUNT; block++) { - if (block == GM12U320_BLOCK_COUNT - 1) - block_size = DATA_LAST_BLOCK_SIZE; - else - block_size = DATA_BLOCK_SIZE; - - /* Send data command to device */ - memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE); - gm12u320->cmd_buf[8] = block_size & 0xff; - gm12u320->cmd_buf[9] = block_size >> 8; - gm12u320->cmd_buf[20] = 0xfc - block * 4; - gm12u320->cmd_buf[21] = block | (frame << 7); - - ret = usb_bulk_msg(gm12u320->udev, - usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), - gm12u320->cmd_buf, CMD_SIZE, &len, - CMD_TIMEOUT); - if (ret || len != CMD_SIZE) - goto err; - - /* Send data block to device */ - ret = usb_bulk_msg(gm12u320->udev, - usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), - gm12u320->data_buf[block], block_size, - &len, DATA_TIMEOUT); - if (ret || len != block_size) - goto err; - - /* Read status */ - ret = usb_bulk_msg(gm12u320->udev, - usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT), - gm12u320->cmd_buf, READ_STATUS_SIZE, &len, - CMD_TIMEOUT); - if (ret || len != READ_STATUS_SIZE) - goto err; - } + gm12u320_copy_fb_to_blocks(gm12u320); + + for (block = 0; block < GM12U320_BLOCK_COUNT; block++) { + if (block == GM12U320_BLOCK_COUNT - 1) + block_size = DATA_LAST_BLOCK_SIZE; + else + block_size = DATA_BLOCK_SIZE; + + /* Send data command to device */ + memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE); + gm12u320->cmd_buf[8] = block_size & 0xff; + gm12u320->cmd_buf[9] = block_size >> 8; + gm12u320->cmd_buf[20] = 0xfc - block * 4; + gm12u320->cmd_buf[21] = + block | (gm12u320->fb_update.frame << 7); - /* Send draw command to device */ - memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE); ret = usb_bulk_msg(gm12u320->udev, usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), - gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT); + gm12u320->cmd_buf, CMD_SIZE, &len, + CMD_TIMEOUT); if (ret || len != CMD_SIZE) goto err; + /* Send data block to device */ + ret = usb_bulk_msg(gm12u320->udev, + usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), + gm12u320->data_buf[block], block_size, + &len, DATA_TIMEOUT); + if (ret || len != block_size) + goto err; + /* Read status */ ret = usb_bulk_msg(gm12u320->udev, usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT), gm12u320->cmd_buf, READ_STATUS_SIZE, &len, - draw_status_timeout); + CMD_TIMEOUT); if (ret || len != READ_STATUS_SIZE) goto err; - - 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.run || - gm12u320->fb_update.fb != NULL, - IDLE_TIMEOUT); } + + /* Send draw command to device */ + memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE); + ret = usb_bulk_msg(gm12u320->udev, + usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), + gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT); + if (ret || len != CMD_SIZE) + goto err; + + /* Read status */ + ret = usb_bulk_msg(gm12u320->udev, + usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT), + gm12u320->cmd_buf, READ_STATUS_SIZE, &len, + gm12u320->fb_update.draw_status_timeout); + if (ret || len != READ_STATUS_SIZE) + goto err; + + gm12u320->fb_update.draw_status_timeout = CMD_TIMEOUT; + gm12u320->fb_update.frame = !gm12u320->fb_update.frame; + + /* + * We must draw a frame every 2s otherwise the projector + * switches back to showing its logo. + */ + queue_delayed_work(system_long_wq, &gm12u320->fb_update.work, + IDLE_TIMEOUT); + return; err: /* Do not log errors caused by module unload or device unplug */ @@ -446,36 +432,24 @@ static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb, mutex_unlock(&gm12u320->fb_update.lock); if (wakeup) - wake_up(&gm12u320->fb_update.waitq); + mod_delayed_work(system_long_wq, &gm12u320->fb_update.work, 0); if (old_fb) drm_framebuffer_put(old_fb); } -static void gm12u320_start_fb_update(struct gm12u320_device *gm12u320) -{ - mutex_lock(&gm12u320->fb_update.lock); - gm12u320->fb_update.run = true; - mutex_unlock(&gm12u320->fb_update.lock); - - queue_work(gm12u320->fb_update.workq, &gm12u320->fb_update.work); -} - static void gm12u320_stop_fb_update(struct gm12u320_device *gm12u320) { - mutex_lock(&gm12u320->fb_update.lock); - gm12u320->fb_update.run = false; - mutex_unlock(&gm12u320->fb_update.lock); + struct drm_framebuffer *old_fb; - wake_up(&gm12u320->fb_update.waitq); - cancel_work_sync(&gm12u320->fb_update.work); + cancel_delayed_work_sync(&gm12u320->fb_update.work); mutex_lock(&gm12u320->fb_update.lock); - if (gm12u320->fb_update.fb) { - drm_framebuffer_put(gm12u320->fb_update.fb); - gm12u320->fb_update.fb = NULL; - } + old_fb = gm12u320->fb_update.fb; + gm12u320->fb_update.fb = NULL; mutex_unlock(&gm12u320->fb_update.lock); + + drm_framebuffer_put(old_fb); } static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320) @@ -583,11 +557,11 @@ static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) { - struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private; struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT }; + struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private; + gm12u320->fb_update.draw_status_timeout = FIRST_FRAME_TIMEOUT; gm12u320_fb_mark_dirty(plane_state->fb, &rect); - gm12u320_start_fb_update(gm12u320); } static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -622,13 +596,6 @@ static const uint64_t gm12u320_pipe_modifiers[] = { DRM_FORMAT_MOD_INVALID }; -static void gm12u320_driver_release(struct drm_device *dev) -{ - struct gm12u320_device *gm12u320 = dev->dev_private; - - gm12u320_usb_free(gm12u320); -} - DEFINE_DRM_GEM_FOPS(gm12u320_fops); static struct drm_driver gm12u320_drm_driver = { @@ -640,7 +607,6 @@ static struct drm_driver gm12u320_drm_driver = { .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, - .release = gm12u320_driver_release, .fops = &gm12u320_fops, DRM_GEM_SHMEM_DRIVER_OPS, }; @@ -670,9 +636,8 @@ static int gm12u320_usb_probe(struct usb_interface *interface, return -ENOMEM; gm12u320->udev = interface_to_usbdev(interface); - INIT_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work); + INIT_DELAYED_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work); mutex_init(&gm12u320->fb_update.lock); - init_waitqueue_head(&gm12u320->fb_update.waitq); dev = &gm12u320->dev; ret = devm_drm_dev_init(&interface->dev, dev, &gm12u320_drm_driver); -- 2.24.1 _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel