disp: msm: sde: manage vblank refcount concurrency
Vblank refcount can reach out of sync with below case 1. event_thread triggers the vblank_enable 2. commit_thread triggers the modeset 2.a modeset resets the vblank refcount with mode_set 3. event_thread triggers the vblank_disable Event 2.a resets the vblank refcount and vblank disable request after 2.a is going to fail. This can be fixed by avoiding concurrency between mode_set call and vblank request. Change-Id: Ibb810ec90e81d63feee443f1c37dd736d5cfac0d Signed-off-by: Dhaval Patel <pdhaval@codeaurora.org>
This commit is contained in:

committed by
Gerrit - the friendly Code Review server

parent
743dc695c4
commit
44cde01fc7
@@ -365,15 +365,7 @@ static int vblank_ctrl_queue_work(struct msm_drm_private *priv,
|
|||||||
cur_work->crtc_id = crtc_id;
|
cur_work->crtc_id = crtc_id;
|
||||||
cur_work->enable = enable;
|
cur_work->enable = enable;
|
||||||
cur_work->priv = priv;
|
cur_work->priv = priv;
|
||||||
|
worker = &priv->event_thread[crtc_id].worker;
|
||||||
/* During modeset scenario, vblank request is queued to
|
|
||||||
* display thread to avoid enabling irq resulting in
|
|
||||||
* vblank refcount mismatch
|
|
||||||
*/
|
|
||||||
if (crtc->state && drm_atomic_crtc_needs_modeset(crtc->state))
|
|
||||||
worker = &priv->disp_thread[crtc_id].worker;
|
|
||||||
else
|
|
||||||
worker = &priv->event_thread[crtc_id].worker;
|
|
||||||
|
|
||||||
kthread_queue_work(worker, &cur_work->work);
|
kthread_queue_work(worker, &cur_work->work);
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -275,6 +275,7 @@ struct sde_encoder_irq {
|
|||||||
* @enc_spinlock: Virtual-Encoder-Wide Spin Lock for IRQ purposes
|
* @enc_spinlock: Virtual-Encoder-Wide Spin Lock for IRQ purposes
|
||||||
* @enable_state: Enable state tracking
|
* @enable_state: Enable state tracking
|
||||||
* @vblank_refcount: Reference count of vblank request
|
* @vblank_refcount: Reference count of vblank request
|
||||||
|
* @vblank_cached_refcount: Reference count of vblank cached request
|
||||||
* @wbirq_refcount: Reference count of wb irq request
|
* @wbirq_refcount: Reference count of wb irq request
|
||||||
* @vsync_cnt: Vsync count for the physical encoder
|
* @vsync_cnt: Vsync count for the physical encoder
|
||||||
* @underrun_cnt: Underrun count for the physical encoder
|
* @underrun_cnt: Underrun count for the physical encoder
|
||||||
@@ -324,6 +325,7 @@ struct sde_encoder_phys {
|
|||||||
enum sde_enc_enable_state enable_state;
|
enum sde_enc_enable_state enable_state;
|
||||||
struct mutex *vblank_ctl_lock;
|
struct mutex *vblank_ctl_lock;
|
||||||
atomic_t vblank_refcount;
|
atomic_t vblank_refcount;
|
||||||
|
atomic_t vblank_cached_refcount;
|
||||||
atomic_t wbirq_refcount;
|
atomic_t wbirq_refcount;
|
||||||
atomic_t vsync_cnt;
|
atomic_t vsync_cnt;
|
||||||
atomic_t underrun_cnt;
|
atomic_t underrun_cnt;
|
||||||
|
@@ -328,6 +328,7 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx(
|
|||||||
struct sde_encoder_irq *irq;
|
struct sde_encoder_irq *irq;
|
||||||
struct sde_kms *sde_kms;
|
struct sde_kms *sde_kms;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
u32 vblank_refcount;
|
||||||
|
|
||||||
if (!phys_enc->sde_kms || !phys_enc->hw_pp || !phys_enc->hw_ctl) {
|
if (!phys_enc->sde_kms || !phys_enc->hw_pp || !phys_enc->hw_ctl) {
|
||||||
SDE_ERROR("invalid args %d %d %d\n", !phys_enc->sde_kms,
|
SDE_ERROR("invalid args %d %d %d\n", !phys_enc->sde_kms,
|
||||||
@@ -342,20 +343,28 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx(
|
|||||||
|
|
||||||
sde_kms = phys_enc->sde_kms;
|
sde_kms = phys_enc->sde_kms;
|
||||||
|
|
||||||
mutex_lock(&sde_kms->vblank_ctl_global_lock);
|
mutex_lock(phys_enc->vblank_ctl_lock);
|
||||||
|
vblank_refcount = atomic_read(&phys_enc->vblank_refcount);
|
||||||
if (atomic_read(&phys_enc->vblank_refcount)) {
|
if (vblank_refcount) {
|
||||||
SDE_ERROR(
|
|
||||||
"vblank_refcount mismatch detected, try to reset %d\n",
|
|
||||||
atomic_read(&phys_enc->vblank_refcount));
|
|
||||||
ret = sde_encoder_helper_unregister_irq(phys_enc,
|
ret = sde_encoder_helper_unregister_irq(phys_enc,
|
||||||
INTR_IDX_RDPTR);
|
INTR_IDX_RDPTR);
|
||||||
if (ret)
|
if (ret)
|
||||||
SDE_ERROR(
|
SDE_ERROR(
|
||||||
"control vblank irq registration error %d\n",
|
"control vblank irq registration error %d\n",
|
||||||
ret);
|
ret);
|
||||||
|
if (vblank_refcount > 1)
|
||||||
|
SDE_ERROR(
|
||||||
|
"vblank_refcount mismatch detected, try to reset %d\n",
|
||||||
|
atomic_read(&phys_enc->vblank_refcount));
|
||||||
|
else
|
||||||
|
atomic_set(&phys_enc->vblank_cached_refcount, 1);
|
||||||
|
|
||||||
|
SDE_EVT32(DRMID(phys_enc->parent),
|
||||||
|
phys_enc->hw_pp->idx - PINGPONG_0, vblank_refcount,
|
||||||
|
atomic_read(&phys_enc->vblank_cached_refcount));
|
||||||
}
|
}
|
||||||
atomic_set(&phys_enc->vblank_refcount, 0);
|
atomic_set(&phys_enc->vblank_refcount, 0);
|
||||||
|
mutex_unlock(phys_enc->vblank_ctl_lock);
|
||||||
|
|
||||||
irq = &phys_enc->irq[INTR_IDX_CTL_START];
|
irq = &phys_enc->irq[INTR_IDX_CTL_START];
|
||||||
irq->hw_idx = phys_enc->hw_ctl->idx;
|
irq->hw_idx = phys_enc->hw_ctl->idx;
|
||||||
@@ -389,9 +398,6 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx(
|
|||||||
irq->hw_idx = phys_enc->hw_intf->idx;
|
irq->hw_idx = phys_enc->hw_intf->idx;
|
||||||
else
|
else
|
||||||
irq->hw_idx = phys_enc->hw_pp->idx;
|
irq->hw_idx = phys_enc->hw_pp->idx;
|
||||||
|
|
||||||
mutex_unlock(&sde_kms->vblank_ctl_global_lock);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sde_encoder_phys_cmd_cont_splash_mode_set(
|
static void sde_encoder_phys_cmd_cont_splash_mode_set(
|
||||||
@@ -811,7 +817,7 @@ static int sde_encoder_phys_cmd_control_vblank_irq(
|
|||||||
struct sde_encoder_phys_cmd *cmd_enc =
|
struct sde_encoder_phys_cmd *cmd_enc =
|
||||||
to_sde_encoder_phys_cmd(phys_enc);
|
to_sde_encoder_phys_cmd(phys_enc);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int refcount;
|
u32 refcount, cached_refcount;
|
||||||
struct sde_kms *sde_kms;
|
struct sde_kms *sde_kms;
|
||||||
|
|
||||||
if (!phys_enc || !phys_enc->hw_pp) {
|
if (!phys_enc || !phys_enc->hw_pp) {
|
||||||
@@ -820,17 +826,23 @@ static int sde_encoder_phys_cmd_control_vblank_irq(
|
|||||||
}
|
}
|
||||||
sde_kms = phys_enc->sde_kms;
|
sde_kms = phys_enc->sde_kms;
|
||||||
|
|
||||||
mutex_lock(&sde_kms->vblank_ctl_global_lock);
|
mutex_lock(phys_enc->vblank_ctl_lock);
|
||||||
refcount = atomic_read(&phys_enc->vblank_refcount);
|
|
||||||
|
|
||||||
/* Slave encoders don't report vblank */
|
/* Slave encoders don't report vblank */
|
||||||
if (!sde_encoder_phys_cmd_is_master(phys_enc))
|
if (!sde_encoder_phys_cmd_is_master(phys_enc))
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
|
refcount = atomic_read(&phys_enc->vblank_refcount);
|
||||||
|
cached_refcount = atomic_read(&phys_enc->vblank_cached_refcount);
|
||||||
|
|
||||||
/* protect against negative */
|
/* protect against negative */
|
||||||
if (!enable && refcount == 0) {
|
if (!enable && refcount == 0) {
|
||||||
ret = -EINVAL;
|
if (cached_refcount == 1) {
|
||||||
goto end;
|
atomic_set(&phys_enc->vblank_cached_refcount, 0);
|
||||||
|
goto end;
|
||||||
|
} else {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDE_DEBUG_CMDENC(cmd_enc, "[%pS] enable=%d/%d\n",
|
SDE_DEBUG_CMDENC(cmd_enc, "[%pS] enable=%d/%d\n",
|
||||||
@@ -850,7 +862,13 @@ static int sde_encoder_phys_cmd_control_vblank_irq(
|
|||||||
atomic_inc_return(&phys_enc->vblank_refcount);
|
atomic_inc_return(&phys_enc->vblank_refcount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enable && cached_refcount) {
|
||||||
|
atomic_inc(&phys_enc->vblank_refcount);
|
||||||
|
atomic_set(&phys_enc->vblank_cached_refcount, 0);
|
||||||
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
|
mutex_unlock(phys_enc->vblank_ctl_lock);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
SDE_ERROR_CMDENC(cmd_enc,
|
SDE_ERROR_CMDENC(cmd_enc,
|
||||||
"control vblank irq error %d, enable %d, refcount %d\n",
|
"control vblank irq error %d, enable %d, refcount %d\n",
|
||||||
@@ -860,7 +878,6 @@ end:
|
|||||||
enable, refcount, SDE_EVTLOG_ERROR);
|
enable, refcount, SDE_EVTLOG_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&sde_kms->vblank_ctl_global_lock);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2068,6 +2085,7 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init(
|
|||||||
irq->cb.func = sde_encoder_phys_cmd_wr_ptr_irq;
|
irq->cb.func = sde_encoder_phys_cmd_wr_ptr_irq;
|
||||||
|
|
||||||
atomic_set(&phys_enc->vblank_refcount, 0);
|
atomic_set(&phys_enc->vblank_refcount, 0);
|
||||||
|
atomic_set(&phys_enc->vblank_cached_refcount, 0);
|
||||||
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
|
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
|
||||||
atomic_set(&phys_enc->pending_retire_fence_cnt, 0);
|
atomic_set(&phys_enc->pending_retire_fence_cnt, 0);
|
||||||
atomic_set(&cmd_enc->pending_vblank_cnt, 0);
|
atomic_set(&cmd_enc->pending_vblank_cnt, 0);
|
||||||
|
@@ -3619,7 +3619,6 @@ static int sde_kms_hw_init(struct msm_kms *kms)
|
|||||||
dev->mode_config.max_height = sde_kms->catalog->max_display_height;
|
dev->mode_config.max_height = sde_kms->catalog->max_display_height;
|
||||||
|
|
||||||
mutex_init(&sde_kms->secure_transition_lock);
|
mutex_init(&sde_kms->secure_transition_lock);
|
||||||
mutex_init(&sde_kms->vblank_ctl_global_lock);
|
|
||||||
|
|
||||||
atomic_set(&sde_kms->detach_sec_cb, 0);
|
atomic_set(&sde_kms->detach_sec_cb, 0);
|
||||||
atomic_set(&sde_kms->detach_all_cb, 0);
|
atomic_set(&sde_kms->detach_all_cb, 0);
|
||||||
|
@@ -302,7 +302,6 @@ struct sde_kms {
|
|||||||
atomic_t detach_sec_cb;
|
atomic_t detach_sec_cb;
|
||||||
atomic_t detach_all_cb;
|
atomic_t detach_all_cb;
|
||||||
struct mutex secure_transition_lock;
|
struct mutex secure_transition_lock;
|
||||||
struct mutex vblank_ctl_global_lock;
|
|
||||||
|
|
||||||
bool first_kickoff;
|
bool first_kickoff;
|
||||||
bool qdss_enabled;
|
bool qdss_enabled;
|
||||||
|
Reference in New Issue
Block a user