Commit 9b190610 authored by Boris Brezillon's avatar Boris Brezillon

drm: atmel-hlcdc: support asynchronous atomic commit operations

drm_atomic_helper_commit() does not support asynchronous commits.
Replace it by a specific commit function supporting these kind of requests.
Signed-off-by: default avatarBoris Brezillon <boris.brezillon@free-electrons.com>
Tested-by: default avatarNicolas Ferre <nicolas.ferre@atmel.com>
parent 1a7b37ca
...@@ -427,11 +427,102 @@ static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev) ...@@ -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 = { static const struct drm_mode_config_funcs mode_config_funcs = {
.fb_create = atmel_hlcdc_fb_create, .fb_create = atmel_hlcdc_fb_create,
.output_poll_changed = atmel_hlcdc_fb_output_poll_changed, .output_poll_changed = atmel_hlcdc_fb_output_poll_changed,
.atomic_check = drm_atomic_helper_check, .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) 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) ...@@ -509,6 +600,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
if (!dc->wq) if (!dc->wq)
return -ENOMEM; return -ENOMEM;
init_waitqueue_head(&dc->commit.wait);
dc->desc = match->data; dc->desc = match->data;
dc->hlcdc = dev_get_drvdata(dev->dev->parent); dc->hlcdc = dev_get_drvdata(dev->dev->parent);
dev->dev_private = dc; dev->dev_private = dc;
......
...@@ -128,6 +128,7 @@ struct atmel_hlcdc_planes { ...@@ -128,6 +128,7 @@ struct atmel_hlcdc_planes {
* @planes: instantiated planes * @planes: instantiated planes
* @layers: active HLCDC layer * @layers: active HLCDC layer
* @wq: display controller workqueue * @wq: display controller workqueue
* @commit: used for async commit handling
*/ */
struct atmel_hlcdc_dc { struct atmel_hlcdc_dc {
const struct atmel_hlcdc_dc_desc *desc; const struct atmel_hlcdc_dc_desc *desc;
...@@ -137,6 +138,10 @@ struct atmel_hlcdc_dc { ...@@ -137,6 +138,10 @@ struct atmel_hlcdc_dc {
struct atmel_hlcdc_planes *planes; struct atmel_hlcdc_planes *planes;
struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS]; struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
struct workqueue_struct *wq; struct workqueue_struct *wq;
struct {
wait_queue_head_t wait;
bool pending;
} commit;
}; };
extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats; extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats;
......
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