[PATCH v2] drm/msm/dp: do not complete dp_aux_cmd_fifo_tx() if irq is not for aux transfer
Abhinav Kumar
quic_abhinavk at quicinc.com
Thu Dec 15 20:30:47 UTC 2022
On 12/15/2022 10:46 AM, Dmitry Baryshkov wrote:
> On 15/12/2022 20:32, Kuogee Hsieh wrote:
>> There are 3 possible interrupt sources are handled by DP controller,
>> HPDstatus, Controller state changes and Aux read/write transaction.
>> At every irq, DP controller have to check isr status of every interrupt
>> sources and service the interrupt if its isr status bits shows interrupts
>> are pending. There is potential race condition may happen at current aux
>> isr handler implementation since it is always complete
>> dp_aux_cmd_fifo_tx()
>> even irq is not for aux read or write transaction. This may cause aux
>> read
>> transaction return premature if host aux data read is in the middle of
>> waiting for sink to complete transferring data to host while irq happen.
>> This will cause host's receiving buffer contains unexpected data. This
>> patch fixes this problem by checking aux isr and return immediately at
>> aux isr handler if there are no any isr status bits set.
>>
>> Current there is a bug report regrading eDP edid corruption happen during
>> system booting up. After lengthy debugging to found that VIDEO_READY
>> interrupt was continuously firing during system booting up which cause
>> dp_aux_isr() to complete dp_aux_cmd_fifo_tx() prematurely to retrieve
>> data
>> from aux hardware buffer which is not yet contains complete data transfer
>> from sink. This cause edid corruption.
>>
>> Follows are the signature at kernel logs when problem happen,
>> EDID has corrupt header
>> panel-simple-dp-aux aux-aea0000.edp: Couldn't identify panel via EDID
>> panel-simple-dp-aux aux-aea0000.edp: error -EIO: Couldn't detect panel
>> nor find a fallback
>>
>> Changes in v2:
>> -- do complete if (ret == IRQ_HANDLED) ay dp-aux_isr()
>> -- add more commit text
>
> Usually it's a single dash.
>
>>
>> Fixes: c943b4948b58 ("drm/msm/dp: add displayPort driver support")
>>
>> Signed-off-by: Kuogee Hsieh <quic_khsieh at quicinc.com>
>
> There should be no empty lines between the tags.
>
>> Tested-by: Douglas Anderson <dianders at chromium.org>
>> Reviewed-by: Abhinav Kumar <quic_abhinavk at quicinc.com>
>> ---
>> drivers/gpu/drm/msm/dp/dp_aux.c | 87
>> +++++++++++++++++++++++++++++------------
>> 1 file changed, 63 insertions(+), 24 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_aux.c
>> b/drivers/gpu/drm/msm/dp/dp_aux.c
>> index d030a93..f31e5c1 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_aux.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_aux.c
>> @@ -162,45 +162,78 @@ static ssize_t dp_aux_cmd_fifo_rx(struct
>> dp_aux_private *aux,
>> return i;
>> }
>> -static void dp_aux_native_handler(struct dp_aux_private *aux, u32 isr)
>> +static irqreturn_t dp_aux_native_handler(struct dp_aux_private *aux,
>> u32 isr)
>> {
>> - if (isr & DP_INTR_AUX_I2C_DONE)
>> + irqreturn_t ret = IRQ_NONE;
>> +
>> + if (isr & DP_INTR_AUX_I2C_DONE) {
>> aux->aux_error_num = DP_AUX_ERR_NONE;
>> - else if (isr & DP_INTR_WRONG_ADDR)
>> + ret = IRQ_HANDLED;
>> + } else if (isr & DP_INTR_WRONG_ADDR) {
>> aux->aux_error_num = DP_AUX_ERR_ADDR;
>> - else if (isr & DP_INTR_TIMEOUT)
>> + ret = IRQ_HANDLED;
>> + } else if (isr & DP_INTR_TIMEOUT) {
>> aux->aux_error_num = DP_AUX_ERR_TOUT;
>> - if (isr & DP_INTR_NACK_DEFER)
>> + ret = IRQ_HANDLED;
>> + }
>> +
>> + if (isr & DP_INTR_NACK_DEFER) {
>> aux->aux_error_num = DP_AUX_ERR_NACK;
>> + ret = IRQ_HANDLED;
>> + }
>> +
>> if (isr & DP_INTR_AUX_ERROR) {
>> aux->aux_error_num = DP_AUX_ERR_PHY;
>> dp_catalog_aux_clear_hw_interrupts(aux->catalog);
>> + ret = IRQ_HANDLED;
>> }
>> +
>> + return ret;
>> }
>> -static void dp_aux_i2c_handler(struct dp_aux_private *aux, u32 isr)
>> +static irqreturn_t dp_aux_i2c_handler(struct dp_aux_private *aux, u32
>> isr)
>> {
>> + irqreturn_t ret = IRQ_NONE;
>> +
>> if (isr & DP_INTR_AUX_I2C_DONE) {
>> if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER))
>> aux->aux_error_num = DP_AUX_ERR_NACK;
>> else
>> aux->aux_error_num = DP_AUX_ERR_NONE;
>> - } else {
>> - if (isr & DP_INTR_WRONG_ADDR)
>> - aux->aux_error_num = DP_AUX_ERR_ADDR;
>> - else if (isr & DP_INTR_TIMEOUT)
>> - aux->aux_error_num = DP_AUX_ERR_TOUT;
>> - if (isr & DP_INTR_NACK_DEFER)
>> - aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;
>> - if (isr & DP_INTR_I2C_NACK)
>> - aux->aux_error_num = DP_AUX_ERR_NACK;
>> - if (isr & DP_INTR_I2C_DEFER)
>> - aux->aux_error_num = DP_AUX_ERR_DEFER;
>> - if (isr & DP_INTR_AUX_ERROR) {
>> - aux->aux_error_num = DP_AUX_ERR_PHY;
>> - dp_catalog_aux_clear_hw_interrupts(aux->catalog);
>> - }
>> +
>> + return IRQ_HANDLED;
>> + }
>> +
>> + if (isr & DP_INTR_WRONG_ADDR) {
>> + aux->aux_error_num = DP_AUX_ERR_ADDR;
>> + ret = IRQ_HANDLED;
>> + } else if (isr & DP_INTR_TIMEOUT) {
>> + aux->aux_error_num = DP_AUX_ERR_TOUT;
>> + ret = IRQ_HANDLED;
>> }
>> +
>> + if (isr & DP_INTR_NACK_DEFER) {
>> + aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;
>> + ret = IRQ_HANDLED;
>> + }
>> +
>> + if (isr & DP_INTR_I2C_NACK) {
>> + aux->aux_error_num = DP_AUX_ERR_NACK;
>> + ret = IRQ_HANDLED;
>> + }
>> +
>> + if (isr & DP_INTR_I2C_DEFER) {
>> + aux->aux_error_num = DP_AUX_ERR_DEFER;
>> + ret = IRQ_HANDLED;
>> + }
>> +
>> + if (isr & DP_INTR_AUX_ERROR) {
>> + aux->aux_error_num = DP_AUX_ERR_PHY;
>> + dp_catalog_aux_clear_hw_interrupts(aux->catalog);
>> + ret = IRQ_HANDLED;
>> + }
>> +
>> + return ret;
>> }
>> static void dp_aux_update_offset_and_segment(struct dp_aux_private
>> *aux,
>> @@ -413,6 +446,7 @@ void dp_aux_isr(struct drm_dp_aux *dp_aux)
>> {
>> u32 isr;
>> struct dp_aux_private *aux;
>> + irqreturn_t ret = IRQ_NONE;
>
> No need to assign a value here. It will be overwritten in both of code
> branches.
>
>> if (!dp_aux) {
>> DRM_ERROR("invalid input\n");
>> @@ -423,15 +457,20 @@ void dp_aux_isr(struct drm_dp_aux *dp_aux)
>> isr = dp_catalog_aux_get_irq(aux->catalog);
>> + /* no interrupts pending, return immediately */
>> + if (!isr)
>> + return;
>> +
>
> A separate commit please.
>
>> if (!aux->cmd_busy)
>> return;
>> if (aux->native)
>> - dp_aux_native_handler(aux, isr);
>> + ret = dp_aux_native_handler(aux, isr);
>> else
>> - dp_aux_i2c_handler(aux, isr);
>> + ret = dp_aux_i2c_handler(aux, isr);
>> - complete(&aux->comp);
>> + if (ret == IRQ_HANDLED)
>> + complete(&aux->comp);
>
> Can you just move the complete() into the individual handling functions?
> Then you won't have to return the error code from dp_aux_*_handler() at
> all. You can check `isr' in that function and call complete if there was
> any error.
>
> Also could you please describe, why is it necessary to complete()
> condition at all? Judging from your commit message the `if (!isr)
> return;' part should be enough.
>
Yes, and thats why I wrote that from a functionality standpoint, the
original v1 posted was enough to fix this issue.
But, this version is better because of the irq return value.
So, if IRQ_HANDLED Vs IRQ_NONE handling is better with this.
>> }
>> void dp_aux_reconfig(struct drm_dp_aux *dp_aux)
>
More information about the dri-devel
mailing list