| From f0bfcc22d9822947b0ad3095e8363eab5261864c Mon Sep 17 00:00:00 2001 |
| From: Archit Taneja <architt@codeaurora.org> |
| Date: Wed, 15 Jun 2016 16:20:45 +0530 |
| Subject: drm/bridge: adv7511: Fix mutex deadlock when interrupts are disabled |
| |
| From: Archit Taneja <architt@codeaurora.org> |
| |
| commit f0bfcc22d9822947b0ad3095e8363eab5261864c upstream. |
| |
| When the adv7511 i2c client doesn't have an interrupt line, we observe a |
| deadlock on caused by trying to lock drm device's mode_config.mutex twice |
| in the same context. |
| |
| Here is the sequence that causes it: |
| |
| ioctl DRM_IOCTL_MODE_GETCONNECTOR from userspace |
| drm_mode_getconnector (acquires mode_config mutex) |
| connector->fill_modes() |
| drm_helper_probe_single_connector_modes |
| connector_funcs->get_modes |
| adv7511_encoder_get_modes |
| adv7511_get_edid_block |
| adv7511_irq_process |
| drm_helper_hpd_irq_event (acquires mode_config mutex again) |
| |
| In adv7511_irq_process, don't call drm_helper_hpd_irq_event when not |
| called from the interrupt handler. It doesn't serve any purpose there |
| anyway. |
| |
| Signed-off-by: Archit Taneja <architt@codeaurora.org> |
| Signed-off-by: Thong Ho <thong.ho.px@rvc.renesas.com> |
| Signed-off-by: Nhan Nguyen <nhan.nguyen.yb@renesas.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/gpu/drm/i2c/adv7511.c | 8 ++++---- |
| 1 file changed, 4 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/gpu/drm/i2c/adv7511.c |
| +++ b/drivers/gpu/drm/i2c/adv7511.c |
| @@ -429,7 +429,7 @@ static bool adv7511_hpd(struct adv7511 * |
| return false; |
| } |
| |
| -static int adv7511_irq_process(struct adv7511 *adv7511) |
| +static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) |
| { |
| unsigned int irq0, irq1; |
| int ret; |
| @@ -445,7 +445,7 @@ static int adv7511_irq_process(struct ad |
| regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0); |
| regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1); |
| |
| - if (irq0 & ADV7511_INT0_HDP && adv7511->encoder) |
| + if (process_hpd && irq0 & ADV7511_INT0_HDP && adv7511->encoder) |
| drm_helper_hpd_irq_event(adv7511->encoder->dev); |
| |
| if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { |
| @@ -463,7 +463,7 @@ static irqreturn_t adv7511_irq_handler(i |
| struct adv7511 *adv7511 = devid; |
| int ret; |
| |
| - ret = adv7511_irq_process(adv7511); |
| + ret = adv7511_irq_process(adv7511, true); |
| return ret < 0 ? IRQ_NONE : IRQ_HANDLED; |
| } |
| |
| @@ -480,7 +480,7 @@ static int adv7511_wait_for_edid(struct |
| adv7511->edid_read, msecs_to_jiffies(timeout)); |
| } else { |
| for (; timeout > 0; timeout -= 25) { |
| - ret = adv7511_irq_process(adv7511); |
| + ret = adv7511_irq_process(adv7511, false); |
| if (ret < 0) |
| break; |
| |