| From 516b99618f40e7add41e7cbb0a326ab37971d510 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 18 Apr 2022 14:56:28 -0700 |
| Subject: drm/msm/dp: stop event kernel thread when DP unbind |
| |
| From: Kuogee Hsieh <quic_khsieh@quicinc.com> |
| |
| [ Upstream commit 570d3e5d28db7a94557fa179167a9fb8642fb8a1 ] |
| |
| Current DP driver implementation, event thread is kept running |
| after DP display is unbind. This patch fix this problem by disabling |
| DP irq and stop event thread to exit gracefully at dp_display_unbind(). |
| |
| Changes in v2: |
| -- start event thread at dp_display_bind() |
| |
| Changes in v3: |
| -- disable all HDP interrupts at unbind |
| -- replace dp_hpd_event_setup() with dp_hpd_event_thread_start() |
| -- replace dp_hpd_event_stop() with dp_hpd_event_thread_stop() |
| -- move init_waitqueue_head(&dp->event_q) to probe() |
| -- move spin_lock_init(&dp->event_lock) to probe() |
| |
| Changes in v4: |
| -- relocate both dp_display_bind() and dp_display_unbind() to bottom of file |
| |
| Changes in v5: |
| -- cancel relocation of both dp_display_bind() and dp_display_unbind() |
| |
| Changes in v6: |
| -- move empty event q to dp_event_thread_start() |
| |
| Changes in v7: |
| -- call ktheread_stop() directly instead of dp_hpd_event_thread_stop() function |
| |
| Changes in v8: |
| -- return error immediately if audio registration failed. |
| |
| Changes in v9: |
| -- return error immediately if event thread create failed. |
| |
| Changes in v10: |
| -- delete extra DRM_ERROR("failed to create DP event thread\n"); |
| |
| Fixes: 8ede2ecc3e5e ("drm/msm/dp: Add DP compliance tests on Snapdragon Chipsets") |
| Signed-off-by: Kuogee Hsieh <quic_khsieh@quicinc.com> |
| Reported-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> |
| Reviewed-by: Stephen Boyd <swboyd@chromium.org> |
| Patchwork: https://patchwork.freedesktop.org/patch/482399/ |
| Link: https://lore.kernel.org/r/1650318988-17580-1-git-send-email-quic_khsieh@quicinc.com |
| [DB: fixed Fixes tag] |
| Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/gpu/drm/msm/dp/dp_display.c | 39 +++++++++++++++++++++++------ |
| 1 file changed, 31 insertions(+), 8 deletions(-) |
| |
| diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c |
| index 6cd6934c8c9f..36caf3d5a9f9 100644 |
| --- a/drivers/gpu/drm/msm/dp/dp_display.c |
| +++ b/drivers/gpu/drm/msm/dp/dp_display.c |
| @@ -111,6 +111,7 @@ struct dp_display_private { |
| u32 hpd_state; |
| u32 event_pndx; |
| u32 event_gndx; |
| + struct task_struct *ev_tsk; |
| struct dp_event event_list[DP_EVENT_Q_MAX]; |
| spinlock_t event_lock; |
| |
| @@ -194,6 +195,8 @@ void dp_display_signal_audio_complete(struct msm_dp *dp_display) |
| complete_all(&dp->audio_comp); |
| } |
| |
| +static int dp_hpd_event_thread_start(struct dp_display_private *dp_priv); |
| + |
| static int dp_display_bind(struct device *dev, struct device *master, |
| void *data) |
| { |
| @@ -234,9 +237,18 @@ static int dp_display_bind(struct device *dev, struct device *master, |
| } |
| |
| rc = dp_register_audio_driver(dev, dp->audio); |
| - if (rc) |
| + if (rc) { |
| DRM_ERROR("Audio registration Dp failed\n"); |
| + goto end; |
| + } |
| |
| + rc = dp_hpd_event_thread_start(dp); |
| + if (rc) { |
| + DRM_ERROR("Event thread create failed\n"); |
| + goto end; |
| + } |
| + |
| + return 0; |
| end: |
| return rc; |
| } |
| @@ -255,6 +267,11 @@ static void dp_display_unbind(struct device *dev, struct device *master, |
| return; |
| } |
| |
| + /* disable all HPD interrupts */ |
| + dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_INT_MASK, false); |
| + |
| + kthread_stop(dp->ev_tsk); |
| + |
| dp_power_client_deinit(dp->power); |
| dp_aux_unregister(dp->aux); |
| priv->dp = NULL; |
| @@ -981,7 +998,7 @@ static int hpd_event_thread(void *data) |
| |
| dp_priv = (struct dp_display_private *)data; |
| |
| - while (1) { |
| + while (!kthread_should_stop()) { |
| if (timeout_mode) { |
| wait_event_timeout(dp_priv->event_q, |
| (dp_priv->event_pndx == dp_priv->event_gndx), |
| @@ -1062,12 +1079,17 @@ static int hpd_event_thread(void *data) |
| return 0; |
| } |
| |
| -static void dp_hpd_event_setup(struct dp_display_private *dp_priv) |
| +static int dp_hpd_event_thread_start(struct dp_display_private *dp_priv) |
| { |
| - init_waitqueue_head(&dp_priv->event_q); |
| - spin_lock_init(&dp_priv->event_lock); |
| + /* set event q to empty */ |
| + dp_priv->event_gndx = 0; |
| + dp_priv->event_pndx = 0; |
| |
| - kthread_run(hpd_event_thread, dp_priv, "dp_hpd_handler"); |
| + dp_priv->ev_tsk = kthread_run(hpd_event_thread, dp_priv, "dp_hpd_handler"); |
| + if (IS_ERR(dp_priv->ev_tsk)) |
| + return PTR_ERR(dp_priv->ev_tsk); |
| + |
| + return 0; |
| } |
| |
| static irqreturn_t dp_display_irq_handler(int irq, void *dev_id) |
| @@ -1167,8 +1189,11 @@ static int dp_display_probe(struct platform_device *pdev) |
| return -EPROBE_DEFER; |
| } |
| |
| + /* setup event q */ |
| mutex_init(&dp->event_mutex); |
| g_dp_display = &dp->dp_display; |
| + init_waitqueue_head(&dp->event_q); |
| + spin_lock_init(&dp->event_lock); |
| |
| /* Store DP audio handle inside DP display */ |
| g_dp_display->dp_audio = dp->audio; |
| @@ -1308,8 +1333,6 @@ void msm_dp_irq_postinstall(struct msm_dp *dp_display) |
| |
| dp = container_of(dp_display, struct dp_display_private, dp_display); |
| |
| - dp_hpd_event_setup(dp); |
| - |
| dp_add_event(dp, EV_HPD_INIT_SETUP, 0, 100); |
| } |
| |
| -- |
| 2.35.1 |
| |