Commit 91f9f4a3 authored by Adam Ford's avatar Adam Ford Committed by Dmitry Baryshkov

drm/bridge: adv7511: Fix Intermittent EDID failures

In the process of adding support for shared IRQ pins, a scenario
was accidentally created where adv7511_irq_process returned
prematurely causing the EDID to fail randomly.

Since the interrupt handler is broken up into two main helper functions,
update both of them to treat the helper functions as IRQ handlers. These
IRQ routines process their respective tasks as before, but if they
determine that actual work was done, mark the respective IRQ status
accordingly, and delay the check until everything has been processed.

This should guarantee the helper functions don't return prematurely
while still returning proper values of either IRQ_HANDLED or IRQ_NONE.
Reported-by: default avatarLiu Ying <victor.liu@nxp.com>
Fixes: f3d96833 ("drm/bridge: adv7511: Allow IRQ to share GPIO pins")
Signed-off-by: default avatarAdam Ford <aford173@gmail.com>
Tested-by: Liu Ying <victor.liu@nxp.com> # i.MX8MP EVK ADV7535 EDID retrieval w/o IRQ
Reviewed-by: default avatarDmitry Baryshkov <dmitry.baryshkov@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20240630221931.1650565-1-aford173@gmail.comSigned-off-by: default avatarDmitry Baryshkov <dmitry.baryshkov@linaro.org>

V3:  Remove unnecessary declaration of ret by evaluating the return
     code of regmap_read directly.

V2:  Fix uninitialized cec_status
     Cut back a little on error handling to return either IRQ_NONE or
     IRQ_HANDLED.
parent 256abd8e
...@@ -401,7 +401,7 @@ struct adv7511 { ...@@ -401,7 +401,7 @@ struct adv7511 {
#ifdef CONFIG_DRM_I2C_ADV7511_CEC #ifdef CONFIG_DRM_I2C_ADV7511_CEC
int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511); int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511);
void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1); int adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1);
#else #else
static inline int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) static inline int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511)
{ {
......
...@@ -119,7 +119,7 @@ static void adv7511_cec_rx(struct adv7511 *adv7511, int rx_buf) ...@@ -119,7 +119,7 @@ static void adv7511_cec_rx(struct adv7511 *adv7511, int rx_buf)
cec_received_msg(adv7511->cec_adap, &msg); cec_received_msg(adv7511->cec_adap, &msg);
} }
void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1) int adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
{ {
unsigned int offset = adv7511->info->reg_cec_offset; unsigned int offset = adv7511->info->reg_cec_offset;
const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY | const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY |
...@@ -131,16 +131,19 @@ void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1) ...@@ -131,16 +131,19 @@ void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
unsigned int rx_status; unsigned int rx_status;
int rx_order[3] = { -1, -1, -1 }; int rx_order[3] = { -1, -1, -1 };
int i; int i;
int irq_status = IRQ_NONE;
if (irq1 & irq_tx_mask) if (irq1 & irq_tx_mask) {
adv_cec_tx_raw_status(adv7511, irq1); adv_cec_tx_raw_status(adv7511, irq1);
irq_status = IRQ_HANDLED;
}
if (!(irq1 & irq_rx_mask)) if (!(irq1 & irq_rx_mask))
return; return irq_status;
if (regmap_read(adv7511->regmap_cec, if (regmap_read(adv7511->regmap_cec,
ADV7511_REG_CEC_RX_STATUS + offset, &rx_status)) ADV7511_REG_CEC_RX_STATUS + offset, &rx_status))
return; return irq_status;
/* /*
* ADV7511_REG_CEC_RX_STATUS[5:0] contains the reception order of RX * ADV7511_REG_CEC_RX_STATUS[5:0] contains the reception order of RX
...@@ -172,6 +175,8 @@ void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1) ...@@ -172,6 +175,8 @@ void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
adv7511_cec_rx(adv7511, rx_buf); adv7511_cec_rx(adv7511, rx_buf);
} }
return IRQ_HANDLED;
} }
static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
......
...@@ -469,6 +469,8 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) ...@@ -469,6 +469,8 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
{ {
unsigned int irq0, irq1; unsigned int irq0, irq1;
int ret; int ret;
int cec_status = IRQ_NONE;
int irq_status = IRQ_NONE;
ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0); ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0);
if (ret < 0) if (ret < 0)
...@@ -478,29 +480,31 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) ...@@ -478,29 +480,31 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
if (ret < 0) if (ret < 0)
return ret; return ret;
/* If there is no IRQ to handle, exit indicating no IRQ data */
if (!(irq0 & (ADV7511_INT0_HPD | ADV7511_INT0_EDID_READY)) &&
!(irq1 & ADV7511_INT1_DDC_ERROR))
return -ENODATA;
regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0); regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0);
regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1); regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1);
if (process_hpd && irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder) if (process_hpd && irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder) {
schedule_work(&adv7511->hpd_work); schedule_work(&adv7511->hpd_work);
irq_status = IRQ_HANDLED;
}
if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) {
adv7511->edid_read = true; adv7511->edid_read = true;
if (adv7511->i2c_main->irq) if (adv7511->i2c_main->irq)
wake_up_all(&adv7511->wq); wake_up_all(&adv7511->wq);
irq_status = IRQ_HANDLED;
} }
#ifdef CONFIG_DRM_I2C_ADV7511_CEC #ifdef CONFIG_DRM_I2C_ADV7511_CEC
adv7511_cec_irq_process(adv7511, irq1); cec_status = adv7511_cec_irq_process(adv7511, irq1);
#endif #endif
return 0; /* If there is no IRQ to handle, exit indicating no IRQ data */
if (irq_status == IRQ_HANDLED || cec_status == IRQ_HANDLED)
return IRQ_HANDLED;
return IRQ_NONE;
} }
static irqreturn_t adv7511_irq_handler(int irq, void *devid) static irqreturn_t adv7511_irq_handler(int irq, void *devid)
...@@ -509,7 +513,7 @@ static irqreturn_t adv7511_irq_handler(int irq, void *devid) ...@@ -509,7 +513,7 @@ static irqreturn_t adv7511_irq_handler(int irq, void *devid)
int ret; int ret;
ret = adv7511_irq_process(adv7511, true); ret = adv7511_irq_process(adv7511, true);
return ret < 0 ? IRQ_NONE : IRQ_HANDLED; return ret < 0 ? IRQ_NONE : ret;
} }
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment