diff options
author | Archit Taneja <architt@codeaurora.org> | 2016-06-15 12:50:45 +0200 |
---|---|---|
committer | Archit Taneja <architt@codeaurora.org> | 2016-07-13 10:54:36 +0200 |
commit | f0bfcc22d9822947b0ad3095e8363eab5261864c (patch) | |
tree | 2ab24e01aa051c3f481c043c8bca6de26507ae94 /drivers/gpu/drm/bridge/adv7511 | |
parent | drm/i2c: adv7511: Move to bridge folder (diff) | |
download | linux-f0bfcc22d9822947b0ad3095e8363eab5261864c.tar.xz linux-f0bfcc22d9822947b0ad3095e8363eab5261864c.zip |
drm/bridge: adv7511: Fix mutex deadlock when interrupts are disabled
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>
Diffstat (limited to 'drivers/gpu/drm/bridge/adv7511')
-rw-r--r-- | drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 8 |
1 files changed, 4 insertions, 4 deletions
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index c2642f99865d..1fff0ab29b7a 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -427,7 +427,7 @@ static bool adv7511_hpd(struct adv7511 *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; @@ -443,7 +443,7 @@ static int adv7511_irq_process(struct adv7511 *adv7511) regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0); regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1); - if (irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder) + if (process_hpd && irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder) drm_helper_hpd_irq_event(adv7511->connector.dev); if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { @@ -461,7 +461,7 @@ static irqreturn_t adv7511_irq_handler(int irq, void *devid) struct adv7511 *adv7511 = devid; int ret; - ret = adv7511_irq_process(adv7511); + ret = adv7511_irq_process(adv7511, true); return ret < 0 ? IRQ_NONE : IRQ_HANDLED; } @@ -478,7 +478,7 @@ static int adv7511_wait_for_edid(struct adv7511 *adv7511, int timeout) 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; |