EPD refreshes are extremely slow; they take anywhere between hundreds of milliseconds and several seconds. To avoid blocking userspace, perform these refreshes on a separate thread. The thread will also take care of initializing the display before first use and clearing it when the CRTC is disabled. Signed-off-by: Samuel Holland <samuel@xxxxxxxxxxxx> --- drivers/gpu/drm/rockchip/rockchip_ebc.c | 82 ++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c index 5f9502313657..ebe60d5e011a 100644 --- a/drivers/gpu/drm/rockchip/rockchip_ebc.c +++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c @@ -6,6 +6,7 @@ #include <linux/clk.h> #include <linux/completion.h> #include <linux/irq.h> +#include <linux/kthread.h> #include <linux/module.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> @@ -135,9 +136,15 @@ struct rockchip_ebc { struct drm_plane plane; struct regmap *regmap; struct regulator_bulk_data supplies[EBC_NUM_SUPPLIES]; + struct task_struct *refresh_thread; u32 dsp_start; + bool reset_complete; }; +static bool skip_reset; +module_param(skip_reset, bool, 0444); +MODULE_PARM_DESC(skip_reset, "skip the initial display reset"); + DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops); static const struct drm_driver rockchip_ebc_drm_driver = { @@ -172,6 +179,42 @@ to_ebc_crtc_state(struct drm_crtc_state *crtc_state) return container_of(crtc_state, struct ebc_crtc_state, base); } +static int rockchip_ebc_refresh_thread(void *data) +{ + struct rockchip_ebc *ebc = data; + + while (!kthread_should_stop()) { + /* + * LUTs use both the old and the new pixel values as inputs. + * However, the initial contents of the display are unknown. + * The special RESET waveform will initialize the display to + * known contents (white) regardless of its current contents. + */ + if (!ebc->reset_complete) { + ebc->reset_complete = true; + drm_dbg(&ebc->drm, "display reset\n"); + } + + while (!kthread_should_park()) { + drm_dbg(&ebc->drm, "display update\n"); + + set_current_state(TASK_IDLE); + schedule(); + __set_current_state(TASK_RUNNING); + } + + /* + * Clear the display before disabling the CRTC. Use the + * highest-quality waveform to minimize visible artifacts. + */ + drm_dbg(&ebc->drm, "display clear\n"); + + kthread_parkme(); + } + + return 0; +} + static inline struct rockchip_ebc *crtc_to_ebc(struct drm_crtc *crtc) { return container_of(crtc, struct rockchip_ebc, crtc); @@ -296,11 +339,23 @@ static void rockchip_ebc_crtc_atomic_flush(struct drm_crtc *crtc, static void rockchip_ebc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) { + struct rockchip_ebc *ebc = crtc_to_ebc(crtc); + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (crtc_state->mode_changed) + kthread_unpark(ebc->refresh_thread); } static void rockchip_ebc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) { + struct rockchip_ebc *ebc = crtc_to_ebc(crtc); + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (crtc_state->mode_changed) + kthread_park(ebc->refresh_thread); } static const struct drm_crtc_helper_funcs rockchip_ebc_crtc_helper_funcs = { @@ -408,6 +463,14 @@ static int rockchip_ebc_plane_atomic_check(struct drm_plane *plane, static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { + struct rockchip_ebc *ebc = plane_to_ebc(plane); + struct drm_plane_state *plane_state; + + plane_state = drm_atomic_get_new_plane_state(state, plane); + if (!plane_state->crtc) + return; + + wake_up_process(ebc->refresh_thread); } static const struct drm_plane_helper_funcs rockchip_ebc_plane_helper_funcs = { @@ -673,6 +736,7 @@ static int rockchip_ebc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ebc); init_completion(&ebc->display_end); + ebc->reset_complete = skip_reset; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -716,12 +780,26 @@ static int rockchip_ebc_probe(struct platform_device *pdev) return ret; } + ebc->refresh_thread = kthread_create(rockchip_ebc_refresh_thread, + ebc, "ebc-refresh/%s", + dev_name(dev)); + if (IS_ERR(ebc->refresh_thread)) { + ret = dev_err_probe(dev, PTR_ERR(ebc->refresh_thread), + "Failed to start refresh thread\n"); + goto err_disable_pm; + } + + kthread_park(ebc->refresh_thread); + sched_set_fifo(ebc->refresh_thread); + ret = rockchip_ebc_drm_init(ebc); if (ret) - goto err_disable_pm; + goto err_stop_kthread; return 0; +err_stop_kthread: + kthread_stop(ebc->refresh_thread); err_disable_pm: pm_runtime_disable(dev); if (!pm_runtime_status_suspended(dev)) @@ -738,6 +816,8 @@ static int rockchip_ebc_remove(struct platform_device *pdev) drm_dev_unregister(&ebc->drm); drm_atomic_helper_shutdown(&ebc->drm); + kthread_stop(ebc->refresh_thread); + pm_runtime_disable(dev); if (!pm_runtime_status_suspended(dev)) rockchip_ebc_runtime_suspend(dev); -- 2.35.1