diff options
author | Daniel Solomon <daniels@nvidia.com> | 2014-01-29 12:00:01 -0800 |
---|---|---|
committer | Chao Xu <cxu@nvidia.com> | 2014-01-31 11:27:47 -0800 |
commit | c94e9e220296cab5b5e473f2bf4bdbc13e7d3ed9 (patch) | |
tree | a0c96f72591c3150faf5f84ba64d944f4c395906 /drivers | |
parent | 6d58c15ffd0d80275d76ced5502753335a064c5f (diff) |
video: tegra: dp: Serialize DPAUX accesses
This change serializes DPAUX accesses to prevent
system hangs if DP waits on an interrupt to be
notified that a DPAUX transaction has completed
and a DP IRQ_EVENT occurs at the same time.
Bug 1440921
Change-Id: Id6c90ab18386eb25984eb7acafb207507e06ee01
Signed-off-by: Daniel Solomon <daniels@nvidia.com>
Reviewed-on: http://git-master/r/361297
Reviewed-by: Chao Xu <cxu@nvidia.com>
Tested-by: Chao Xu <cxu@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/tegra/dc/dp.c | 36 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dp.h | 2 |
2 files changed, 28 insertions, 10 deletions
diff --git a/drivers/video/tegra/dc/dp.c b/drivers/video/tegra/dc/dp.c index 1db0ac0484fd..f8f7a8658d6a 100644 --- a/drivers/video/tegra/dc/dp.c +++ b/drivers/video/tegra/dc/dp.c @@ -188,13 +188,15 @@ fail: return -EINVAL; } -static int tegra_dc_dpaux_write_chunk(struct tegra_dc_dp_data *dp, u32 cmd, - u32 addr, u8 *data, u32 *size, u32 *aux_stat) +static int tegra_dc_dpaux_write_chunk_locked(struct tegra_dc_dp_data *dp, + u32 cmd, u32 addr, u8 *data, u32 *size, u32 *aux_stat) { int err = 0; u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES; u32 defer_retries = DP_AUX_DEFER_MAX_TRIES; + WARN_ON(!mutex_is_locked(&dp->dpaux_lock)); + switch (cmd) { case DPAUX_DP_AUXCTL_CMD_I2CWR: case DPAUX_DP_AUXCTL_CMD_MOTWR: @@ -301,11 +303,12 @@ tegra_dc_dpaux_write(struct tegra_dc_dp_data *dp, u32 cmd, u32 addr, u32 finished = 0; int ret = 0; + mutex_lock(&dp->dpaux_lock); do { cur_size = *size - finished; if (cur_size >= DP_AUX_MAX_BYTES) cur_size = DP_AUX_MAX_BYTES - 1; - ret = tegra_dc_dpaux_write_chunk(dp, cmd, addr, + ret = tegra_dc_dpaux_write_chunk_locked(dp, cmd, addr, data, &cur_size, aux_stat); finished += cur_size; @@ -315,18 +318,21 @@ tegra_dc_dpaux_write(struct tegra_dc_dp_data *dp, u32 cmd, u32 addr, if (ret) break; } while (*size >= finished); + mutex_unlock(&dp->dpaux_lock); *size = finished; return ret; } -static int tegra_dc_dpaux_read_chunk(struct tegra_dc_dp_data *dp, u32 cmd, - u32 addr, u8 *data, u32 *size, u32 *aux_stat) +static int tegra_dc_dpaux_read_chunk_locked(struct tegra_dc_dp_data *dp, + u32 cmd, u32 addr, u8 *data, u32 *size, u32 *aux_stat) { int err = 0; u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES; u32 defer_retries = DP_AUX_DEFER_MAX_TRIES; + WARN_ON(!mutex_is_locked(&dp->dpaux_lock)); + switch (cmd) { case DPAUX_DP_AUXCTL_CMD_I2CRD: case DPAUX_DP_AUXCTL_CMD_I2CREQWSTAT: @@ -448,6 +454,7 @@ int tegra_dc_dpaux_read(struct tegra_dc_dp_data *dp, u32 cmd, u32 addr, return -EINVAL; } + mutex_lock(&dp->dpaux_lock); do { cur_size = *size - finished; if (cur_size >= DP_AUX_MAX_BYTES) @@ -455,7 +462,7 @@ int tegra_dc_dpaux_read(struct tegra_dc_dp_data *dp, u32 cmd, u32 addr, else cur_size -= 1; - ret = tegra_dc_dpaux_read_chunk(dp, cmd, addr, + ret = tegra_dc_dpaux_read_chunk_locked(dp, cmd, addr, data, &cur_size, aux_stat); if (ret) @@ -467,6 +474,7 @@ int tegra_dc_dpaux_read(struct tegra_dc_dp_data *dp, u32 cmd, u32 addr, finished += cur_size; } while (*size > finished); + mutex_unlock(&dp->dpaux_lock); *size = finished; return ret; @@ -490,6 +498,7 @@ static int tegra_dc_i2c_read(struct tegra_dc_dp_data *dp, u32 i2c_addr, return -EINVAL; } + mutex_lock(&dp->dpaux_lock); do { cur_size = *size - finished; if (cur_size >= DP_AUX_MAX_BYTES) @@ -498,10 +507,10 @@ static int tegra_dc_i2c_read(struct tegra_dc_dp_data *dp, u32 i2c_addr, cur_size -= 1; len = 0; - CHECK_RET(tegra_dc_dpaux_write_chunk(dp, + CHECK_RET(tegra_dc_dpaux_write_chunk_locked(dp, DPAUX_DP_AUXCTL_CMD_I2CWR, i2c_addr, &iaddr, &len, aux_stat)); - CHECK_RET(tegra_dc_dpaux_read_chunk(dp, + CHECK_RET(tegra_dc_dpaux_read_chunk_locked(dp, DPAUX_DP_AUXCTL_CMD_I2CRD, i2c_addr, data, &cur_size, aux_stat)); @@ -509,6 +518,7 @@ static int tegra_dc_i2c_read(struct tegra_dc_dp_data *dp, u32 i2c_addr, data += cur_size; finished += cur_size; } while (*size > finished); + mutex_unlock(&dp->dpaux_lock); *size = finished; return ret; @@ -521,8 +531,10 @@ static int tegra_dc_dp_dpcd_read(struct tegra_dc_dp_data *dp, u32 cmd, u32 status = 0; int ret; - ret = tegra_dc_dpaux_read_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, + mutex_lock(&dp->dpaux_lock); + ret = tegra_dc_dpaux_read_chunk_locked(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, cmd, data_ptr, &size, &status); + mutex_unlock(&dp->dpaux_lock); if (ret) dev_err(&dp->dc->ndev->dev, "dp: Failed to read DPCD data. CMD 0x%x, Status 0x%x\n", @@ -579,8 +591,10 @@ static int tegra_dc_dp_dpcd_write(struct tegra_dc_dp_data *dp, u32 cmd, u32 status = 0; int ret; - ret = tegra_dc_dpaux_write_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXWR, + mutex_lock(&dp->dpaux_lock); + ret = tegra_dc_dpaux_write_chunk_locked(dp, DPAUX_DP_AUXCTL_CMD_AUXWR, cmd, &data, &size, &status); + mutex_unlock(&dp->dpaux_lock); if (ret) dev_err(&dp->dc->ndev->dev, "dp: Failed to write DPCD data. CMD 0x%x, Status 0x%x\n", @@ -1414,6 +1428,8 @@ static int tegra_dc_dp_init(struct tegra_dc *dc) init_completion(&dp->hpd_plug); init_completion(&dp->aux_tx); + mutex_init(&dp->dpaux_lock); + tegra_dc_set_outdata(dc, dp); tegra_dc_dp_debug_create(dp); diff --git a/drivers/video/tegra/dc/dp.h b/drivers/video/tegra/dc/dp.h index c28adbf18a87..0d069ae2ce43 100644 --- a/drivers/video/tegra/dc/dp.h +++ b/drivers/video/tegra/dc/dp.h @@ -250,6 +250,8 @@ struct tegra_dc_dp_data { struct completion aux_tx; struct tegra_dp_out *pdata; + + struct mutex dpaux_lock; }; static inline u32 tegra_dp_wait_aux_training(struct tegra_dc_dp_data *dp, |