Message ID | 20240630221931.1650565-1-aford173@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [V3] drm/bridge: adv7511: Fix Intermittent EDID failures | expand |
On Sun, Jun 30, 2024 at 05:19:31PM GMT, Adam Ford wrote: > 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: Liu Ying <victor.liu@nxp.com> > Fixes: f3d9683346d6 ("drm/bridge: adv7511: Allow IRQ to share GPIO pins") > Signed-off-by: Adam Ford <aford173@gmail.com> > Tested-by: Liu Ying <victor.liu@nxp.com> # i.MX8MP EVK ADV7535 EDID retrieval w/o IRQ > Reviewed-by: Dmitry 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. > Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
On Sun, Jun 30, 2024 at 5:19 PM Adam Ford <aford173@gmail.com> wrote: > > 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: Liu Ying <victor.liu@nxp.com> > Fixes: f3d9683346d6 ("drm/bridge: adv7511: Allow IRQ to share GPIO pins") > Signed-off-by: Adam Ford <aford173@gmail.com> > Tested-by: Liu Ying <victor.liu@nxp.com> # i.MX8MP EVK ADV7535 EDID retrieval w/o IRQ > Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> Gentle nudge on this. I was hoping it could be merged in the 6.10 window since it fixes a known regression, but I know it's summer in the northern hemisphere, and everyone is busy. adam > --- > 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. > > diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h > index ea271f62b214..ec0b7f3d889c 100644 > --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h > +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h > @@ -401,7 +401,7 @@ struct adv7511 { > > #ifdef CONFIG_DRM_I2C_ADV7511_CEC > 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 > static inline int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) > { > diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c > index 44451a9658a3..2e9c88a2b5ed 100644 > --- a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c > +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c > @@ -119,7 +119,7 @@ static void adv7511_cec_rx(struct adv7511 *adv7511, int rx_buf) > 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; > 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) > unsigned int rx_status; > int rx_order[3] = { -1, -1, -1 }; > int i; > + int irq_status = IRQ_NONE; > > - if (irq1 & irq_tx_mask) > + if (irq1 & irq_tx_mask) { > adv_cec_tx_raw_status(adv7511, irq1); > + irq_status = IRQ_HANDLED; > + } > > if (!(irq1 & irq_rx_mask)) > - return; > + return irq_status; > > if (regmap_read(adv7511->regmap_cec, > 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 > @@ -172,6 +175,8 @@ void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1) > > adv7511_cec_rx(adv7511, rx_buf); > } > + > + return IRQ_HANDLED; > } > > static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) > diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c > index 66ccb61e2a66..c8d2c4a157b2 100644 > --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c > +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c > @@ -469,6 +469,8 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) > { > unsigned int irq0, irq1; > int ret; > + int cec_status = IRQ_NONE; > + int irq_status = IRQ_NONE; > > ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0); > if (ret < 0) > @@ -478,29 +480,31 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) > if (ret < 0) > 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(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); > + irq_status = IRQ_HANDLED; > + } > > if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { > adv7511->edid_read = true; > > if (adv7511->i2c_main->irq) > wake_up_all(&adv7511->wq); > + irq_status = IRQ_HANDLED; > } > > #ifdef CONFIG_DRM_I2C_ADV7511_CEC > - adv7511_cec_irq_process(adv7511, irq1); > + cec_status = adv7511_cec_irq_process(adv7511, irq1); > #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) > @@ -509,7 +513,7 @@ static irqreturn_t adv7511_irq_handler(int irq, void *devid) > int ret; > > ret = adv7511_irq_process(adv7511, true); > - return ret < 0 ? IRQ_NONE : IRQ_HANDLED; > + return ret < 0 ? IRQ_NONE : ret; > } > > /* ----------------------------------------------------------------------------- > -- > 2.43.0 >
On Sun, 30 Jun 2024 17:19:31 -0500, Adam Ford wrote: > 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. > > [...] Applied to drm-misc-fixes, thanks! [1/1] drm/bridge: adv7511: Fix Intermittent EDID failures commit: 91f9f4a37124044089debb02a3965c59b5b10c21 Best regards,
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index ea271f62b214..ec0b7f3d889c 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -401,7 +401,7 @@ struct adv7511 { #ifdef CONFIG_DRM_I2C_ADV7511_CEC 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 static inline int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) { diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c index 44451a9658a3..2e9c88a2b5ed 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c @@ -119,7 +119,7 @@ static void adv7511_cec_rx(struct adv7511 *adv7511, int rx_buf) 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; 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) unsigned int rx_status; int rx_order[3] = { -1, -1, -1 }; int i; + int irq_status = IRQ_NONE; - if (irq1 & irq_tx_mask) + if (irq1 & irq_tx_mask) { adv_cec_tx_raw_status(adv7511, irq1); + irq_status = IRQ_HANDLED; + } if (!(irq1 & irq_rx_mask)) - return; + return irq_status; if (regmap_read(adv7511->regmap_cec, 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 @@ -172,6 +175,8 @@ void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1) adv7511_cec_rx(adv7511, rx_buf); } + + return IRQ_HANDLED; } static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 66ccb61e2a66..c8d2c4a157b2 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -469,6 +469,8 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) { unsigned int irq0, irq1; int ret; + int cec_status = IRQ_NONE; + int irq_status = IRQ_NONE; ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0); if (ret < 0) @@ -478,29 +480,31 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) if (ret < 0) 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(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); + irq_status = IRQ_HANDLED; + } if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { adv7511->edid_read = true; if (adv7511->i2c_main->irq) wake_up_all(&adv7511->wq); + irq_status = IRQ_HANDLED; } #ifdef CONFIG_DRM_I2C_ADV7511_CEC - adv7511_cec_irq_process(adv7511, irq1); + cec_status = adv7511_cec_irq_process(adv7511, irq1); #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) @@ -509,7 +513,7 @@ static irqreturn_t adv7511_irq_handler(int irq, void *devid) int ret; ret = adv7511_irq_process(adv7511, true); - return ret < 0 ? IRQ_NONE : IRQ_HANDLED; + return ret < 0 ? IRQ_NONE : ret; } /* -----------------------------------------------------------------------------