drm: atmel-hlcdc: support asynchronous atomic commit operations
authorBoris Brezillon <boris.brezillon@free-electrons.com>
Sat, 10 Oct 2015 06:22:09 +0000 (08:22 +0200)
committerBoris Brezillon <boris.brezillon@free-electrons.com>
Thu, 14 Apr 2016 07:17:25 +0000 (09:17 +0200)
drm_atomic_helper_commit() does not support asynchronous commits.
Replace it by a specific commit function supporting these kind of requests.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Tested-by: Nicolas Ferre <nicolas.ferre@atmel.com>
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h

index 8ab4318e57a1b927bfd38772cf45cca6f0e3275a..67b139174e7b406ecedd6c75b132bd5307d28ec7 100644 (file)
@@ -427,11 +427,102 @@ static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
        }
 }
 
+struct atmel_hlcdc_dc_commit {
+       struct work_struct work;
+       struct drm_device *dev;
+       struct drm_atomic_state *state;
+};
+
+static void
+atmel_hlcdc_dc_atomic_complete(struct atmel_hlcdc_dc_commit *commit)
+{
+       struct drm_device *dev = commit->dev;
+       struct atmel_hlcdc_dc *dc = dev->dev_private;
+       struct drm_atomic_state *old_state = commit->state;
+
+       /* Apply the atomic update. */
+       drm_atomic_helper_commit_modeset_disables(dev, old_state);
+       drm_atomic_helper_commit_planes(dev, old_state, false);
+       drm_atomic_helper_commit_modeset_enables(dev, old_state);
+
+       drm_atomic_helper_wait_for_vblanks(dev, old_state);
+
+       drm_atomic_helper_cleanup_planes(dev, old_state);
+
+       drm_atomic_state_free(old_state);
+
+       /* Complete the commit, wake up any waiter. */
+       spin_lock(&dc->commit.wait.lock);
+       dc->commit.pending = false;
+       wake_up_all_locked(&dc->commit.wait);
+       spin_unlock(&dc->commit.wait.lock);
+
+       kfree(commit);
+}
+
+static void atmel_hlcdc_dc_atomic_work(struct work_struct *work)
+{
+       struct atmel_hlcdc_dc_commit *commit =
+               container_of(work, struct atmel_hlcdc_dc_commit, work);
+
+       atmel_hlcdc_dc_atomic_complete(commit);
+}
+
+static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev,
+                                       struct drm_atomic_state *state,
+                                       bool async)
+{
+       struct atmel_hlcdc_dc *dc = dev->dev_private;
+       struct atmel_hlcdc_dc_commit *commit;
+       int ret;
+
+       ret = drm_atomic_helper_prepare_planes(dev, state);
+       if (ret)
+               return ret;
+
+       /* Allocate the commit object. */
+       commit = kzalloc(sizeof(*commit), GFP_KERNEL);
+       if (!commit) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       INIT_WORK(&commit->work, atmel_hlcdc_dc_atomic_work);
+       commit->dev = dev;
+       commit->state = state;
+
+       spin_lock(&dc->commit.wait.lock);
+       ret = wait_event_interruptible_locked(dc->commit.wait,
+                                             !dc->commit.pending);
+       if (ret == 0)
+               dc->commit.pending = true;
+       spin_unlock(&dc->commit.wait.lock);
+
+       if (ret) {
+               kfree(commit);
+               goto error;
+       }
+
+       /* Swap the state, this is the point of no return. */
+       drm_atomic_helper_swap_state(dev, state);
+
+       if (async)
+               queue_work(dc->wq, &commit->work);
+       else
+               atmel_hlcdc_dc_atomic_complete(commit);
+
+       return 0;
+
+error:
+       drm_atomic_helper_cleanup_planes(dev, state);
+       return ret;
+}
+
 static const struct drm_mode_config_funcs mode_config_funcs = {
        .fb_create = atmel_hlcdc_fb_create,
        .output_poll_changed = atmel_hlcdc_fb_output_poll_changed,
        .atomic_check = drm_atomic_helper_check,
-       .atomic_commit = drm_atomic_helper_commit,
+       .atomic_commit = atmel_hlcdc_dc_atomic_commit,
 };
 
 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
@@ -509,6 +600,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
        if (!dc->wq)
                return -ENOMEM;
 
+       init_waitqueue_head(&dc->commit.wait);
        dc->desc = match->data;
        dc->hlcdc = dev_get_drvdata(dev->dev->parent);
        dev->dev_private = dc;
index fed517f297da1ead290a60c1ab0140467cefed01..733dd1d01b1e841eab013b96585e72fb2e38209c 100644 (file)
@@ -128,6 +128,7 @@ struct atmel_hlcdc_planes {
  * @planes: instantiated planes
  * @layers: active HLCDC layer
  * @wq: display controller workqueue
+ * @commit: used for async commit handling
  */
 struct atmel_hlcdc_dc {
        const struct atmel_hlcdc_dc_desc *desc;
@@ -137,6 +138,10 @@ struct atmel_hlcdc_dc {
        struct atmel_hlcdc_planes *planes;
        struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
        struct workqueue_struct *wq;
+       struct {
+               wait_queue_head_t wait;
+               bool pending;
+       } commit;
 };
 
 extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats;