Commit 8f2cb937 authored by Daniel Vetter's avatar Daniel Vetter

drm/gm12u320: Simplify upload work

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: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@intel.com>
Cc: Hans de Goede <hdegoede@redhat.com>
Cc: "Noralf Trønnes" <noralf@tronnes.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20200323144950.3018436-46-daniel.vetter@ffwll.ch
parent 7ef64ed1
...@@ -89,13 +89,12 @@ struct gm12u320_device { ...@@ -89,13 +89,12 @@ struct gm12u320_device {
unsigned char *cmd_buf; unsigned char *cmd_buf;
unsigned char *data_buf[GM12U320_BLOCK_COUNT]; unsigned char *data_buf[GM12U320_BLOCK_COUNT];
struct { struct {
bool run; struct delayed_work work;
struct workqueue_struct *workq;
struct work_struct work;
wait_queue_head_t waitq;
struct mutex lock; struct mutex lock;
struct drm_framebuffer *fb; struct drm_framebuffer *fb;
struct drm_rect rect; struct drm_rect rect;
int frame;
int draw_status_timeout;
} fb_update; } fb_update;
}; };
...@@ -183,19 +182,9 @@ static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320) ...@@ -183,19 +182,9 @@ static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320)
data_block_footer, DATA_BLOCK_FOOTER_SIZE); 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; 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, static int gm12u320_misc_request(struct gm12u320_device *gm12u320,
u8 req_a, u8 req_b, u8 req_a, u8 req_b,
u8 arg_a, u8 arg_b, u8 arg_c, u8 arg_d) 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) ...@@ -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) static void gm12u320_fb_update_work(struct work_struct *work)
{ {
struct gm12u320_device *gm12u320 = struct gm12u320_device *gm12u320 =
container_of(work, struct gm12u320_device, fb_update.work); container_of(to_delayed_work(work), struct gm12u320_device,
int draw_status_timeout = FIRST_FRAME_TIMEOUT; fb_update.work);
int block, block_size, len; int block, block_size, len;
int frame = 0;
int ret = 0; int ret = 0;
while (gm12u320->fb_update.run) { gm12u320_copy_fb_to_blocks(gm12u320);
gm12u320_copy_fb_to_blocks(gm12u320);
for (block = 0; block < GM12U320_BLOCK_COUNT; block++) {
for (block = 0; block < GM12U320_BLOCK_COUNT; block++) { if (block == GM12U320_BLOCK_COUNT - 1)
if (block == GM12U320_BLOCK_COUNT - 1) block_size = DATA_LAST_BLOCK_SIZE;
block_size = DATA_LAST_BLOCK_SIZE; else
else block_size = DATA_BLOCK_SIZE;
block_size = DATA_BLOCK_SIZE;
/* Send data command to device */
/* Send data command to device */ memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE);
memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE); gm12u320->cmd_buf[8] = block_size & 0xff;
gm12u320->cmd_buf[8] = block_size & 0xff; gm12u320->cmd_buf[9] = block_size >> 8;
gm12u320->cmd_buf[9] = block_size >> 8; gm12u320->cmd_buf[20] = 0xfc - block * 4;
gm12u320->cmd_buf[20] = 0xfc - block * 4; gm12u320->cmd_buf[21] =
gm12u320->cmd_buf[21] = block | (frame << 7); block | (gm12u320->fb_update.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;
}
/* Send draw command to device */
memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE);
ret = usb_bulk_msg(gm12u320->udev, ret = usb_bulk_msg(gm12u320->udev,
usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), 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) if (ret || len != CMD_SIZE)
goto err; 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 */ /* Read status */
ret = usb_bulk_msg(gm12u320->udev, ret = usb_bulk_msg(gm12u320->udev,
usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT), usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT),
gm12u320->cmd_buf, READ_STATUS_SIZE, &len, gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
draw_status_timeout); CMD_TIMEOUT);
if (ret || len != READ_STATUS_SIZE) if (ret || len != READ_STATUS_SIZE)
goto err; 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; return;
err: err:
/* Do not log errors caused by module unload or device unplug */ /* 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, ...@@ -446,36 +432,24 @@ static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb,
mutex_unlock(&gm12u320->fb_update.lock); mutex_unlock(&gm12u320->fb_update.lock);
if (wakeup) if (wakeup)
wake_up(&gm12u320->fb_update.waitq); mod_delayed_work(system_long_wq, &gm12u320->fb_update.work, 0);
if (old_fb) if (old_fb)
drm_framebuffer_put(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) static void gm12u320_stop_fb_update(struct gm12u320_device *gm12u320)
{ {
mutex_lock(&gm12u320->fb_update.lock); struct drm_framebuffer *old_fb;
gm12u320->fb_update.run = false;
mutex_unlock(&gm12u320->fb_update.lock);
wake_up(&gm12u320->fb_update.waitq); cancel_delayed_work_sync(&gm12u320->fb_update.work);
cancel_work_sync(&gm12u320->fb_update.work);
mutex_lock(&gm12u320->fb_update.lock); mutex_lock(&gm12u320->fb_update.lock);
if (gm12u320->fb_update.fb) { old_fb = gm12u320->fb_update.fb;
drm_framebuffer_put(gm12u320->fb_update.fb); gm12u320->fb_update.fb = NULL;
gm12u320->fb_update.fb = NULL;
}
mutex_unlock(&gm12u320->fb_update.lock); mutex_unlock(&gm12u320->fb_update.lock);
drm_framebuffer_put(old_fb);
} }
static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320) static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320)
...@@ -583,11 +557,11 @@ static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe, ...@@ -583,11 +557,11 @@ static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state, struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_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 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_fb_mark_dirty(plane_state->fb, &rect);
gm12u320_start_fb_update(gm12u320);
} }
static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe) static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe)
...@@ -622,13 +596,6 @@ static const uint64_t gm12u320_pipe_modifiers[] = { ...@@ -622,13 +596,6 @@ static const uint64_t gm12u320_pipe_modifiers[] = {
DRM_FORMAT_MOD_INVALID 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); DEFINE_DRM_GEM_FOPS(gm12u320_fops);
static struct drm_driver gm12u320_drm_driver = { static struct drm_driver gm12u320_drm_driver = {
...@@ -640,7 +607,6 @@ static struct drm_driver gm12u320_drm_driver = { ...@@ -640,7 +607,6 @@ static struct drm_driver gm12u320_drm_driver = {
.major = DRIVER_MAJOR, .major = DRIVER_MAJOR,
.minor = DRIVER_MINOR, .minor = DRIVER_MINOR,
.release = gm12u320_driver_release,
.fops = &gm12u320_fops, .fops = &gm12u320_fops,
DRM_GEM_SHMEM_DRIVER_OPS, DRM_GEM_SHMEM_DRIVER_OPS,
}; };
...@@ -670,9 +636,8 @@ static int gm12u320_usb_probe(struct usb_interface *interface, ...@@ -670,9 +636,8 @@ static int gm12u320_usb_probe(struct usb_interface *interface,
return -ENOMEM; return -ENOMEM;
gm12u320->udev = interface_to_usbdev(interface); 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); mutex_init(&gm12u320->fb_update.lock);
init_waitqueue_head(&gm12u320->fb_update.waitq);
dev = &gm12u320->dev; dev = &gm12u320->dev;
ret = devm_drm_dev_init(&interface->dev, dev, &gm12u320_drm_driver); ret = devm_drm_dev_init(&interface->dev, dev, &gm12u320_drm_driver);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment