From a80ca5f87f047acea57ace9f9365794f99894285 Mon Sep 17 00:00:00 2001 From: Liu Ying <victor.liu@nxp.com> Date: Wed, 13 Mar 2019 13:36:55 +0800 Subject: [PATCH] MLK-21028-3 drm/imx: dpu-kms: Use a work queue of it's own for nonblock commits In order to prevent stall from happening during the system suspend operations, the DPU KMS driver should use a freezable and unbound work queue for nonblock commits to make sure all work items are drained in the freeze phase of the suspend operations. So, let's hook up a commit function of our own onto ->atomic_commit to replace the original one drm_atomic_helper_commit() which uses the system_unbound_wq(unfreezable). The new function is almost a copy of drm_atomic_helper_commit() expect for the work queue replacement. Note that other drivers which use drm_atomic_helper_commit() may have the same problem during system suspend. The right fix should be at the DRM atomic core. However, being conservative for now, it is okay to fix the issue for this driver only. Signed-off-by: Liu Ying <victor.liu@nxp.com> --- drivers/gpu/drm/imx/dpu/dpu-kms.c | 84 ++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/imx/dpu/dpu-kms.c b/drivers/gpu/drm/imx/dpu/dpu-kms.c index 9371904fdf1747..289dc82069c833 100644 --- a/drivers/gpu/drm/imx/dpu/dpu-kms.c +++ b/drivers/gpu/drm/imx/dpu/dpu-kms.c @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 NXP + * Copyright 2017-2019 NXP * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -772,9 +772,89 @@ static int dpu_drm_atomic_check(struct drm_device *dev, return ret; } +static void dpu_drm_commit_tail(struct drm_atomic_state *old_state) +{ + struct drm_device *dev = old_state->dev; + + drm_atomic_helper_wait_for_fences(dev, old_state, false); + + drm_atomic_helper_wait_for_dependencies(old_state); + + drm_atomic_helper_commit_tail(old_state); + + drm_atomic_helper_commit_cleanup_done(old_state); + + drm_atomic_state_put(old_state); +} + +static void dpu_drm_commit_work(struct work_struct *work) +{ + struct drm_atomic_state *state = container_of(work, + struct drm_atomic_state, + commit_work); + dpu_drm_commit_tail(state); +} + +/* + * This is almost a copy of drm_atomic_helper_commit(). + * For nonblock commits, we queue the work on a freezable and unbound work queue + * of our own instead of system_unbound_wq to make sure work items on the work + * queue are drained in the freeze phase of the system suspend operations. + */ +static int dpu_drm_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool nonblock) +{ + struct imx_drm_device *imxdrm = dev->dev_private; + int ret; + + if (state->async_update) { + ret = drm_atomic_helper_prepare_planes(dev, state); + if (ret) + return ret; + + drm_atomic_helper_async_commit(dev, state); + drm_atomic_helper_cleanup_planes(dev, state); + + return 0; + } + + ret = drm_atomic_helper_setup_commit(state, nonblock); + if (ret) + return ret; + + INIT_WORK(&state->commit_work, dpu_drm_commit_work); + + ret = drm_atomic_helper_prepare_planes(dev, state); + if (ret) + return ret; + + if (!nonblock) { + ret = drm_atomic_helper_wait_for_fences(dev, state, true); + if (ret) + goto err; + } + + ret = drm_atomic_helper_swap_state(state, true); + if (ret) + goto err; + + drm_atomic_state_get(state); + if (nonblock) + queue_work(imxdrm->dpu_nonblock_commit_wq, &state->commit_work); + else + dpu_drm_commit_tail(state); + + return 0; + +err: + drm_atomic_helper_cleanup_planes(dev, state); + return ret; +} + const struct drm_mode_config_funcs dpu_drm_mode_config_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = dpu_drm_output_poll_changed, .atomic_check = dpu_drm_atomic_check, - .atomic_commit = drm_atomic_helper_commit, + .atomic_commit = dpu_drm_atomic_commit, }; -- GitLab