Commit c8e22658 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Greg Kroah-Hartman

drm: adv7511: Fix nested sleep when reading EDID

commit a5241289 upstream.

The EDID read code waits for the read completion interrupt to occur
using wait_event_interruptible(). The condition passed to the macro
reads I2C registers. This results in sleeping with the task state set
to TASK_INTERRUPTIBLE, triggering a WARN_ON() introduced in commit
8eb23b9f ("sched: Debug nested sleeps").

Fix this by reworking the EDID read code. Instead of checking whether
the read is complete through I2C reads, handle the interrupt registers
in the interrupt handler and update a new edid_read flag accordingly. As
a side effect both the IRQ and polling code paths now process the
interrupt sources through the same code path, simplifying the code.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 66908fde
...@@ -33,6 +33,7 @@ struct adv7511 { ...@@ -33,6 +33,7 @@ struct adv7511 {
unsigned int current_edid_segment; unsigned int current_edid_segment;
uint8_t edid_buf[256]; uint8_t edid_buf[256];
bool edid_read;
wait_queue_head_t wq; wait_queue_head_t wq;
struct drm_encoder *encoder; struct drm_encoder *encoder;
...@@ -379,69 +380,71 @@ static bool adv7511_hpd(struct adv7511 *adv7511) ...@@ -379,69 +380,71 @@ static bool adv7511_hpd(struct adv7511 *adv7511)
return false; return false;
} }
static irqreturn_t adv7511_irq_handler(int irq, void *devid) static int adv7511_irq_process(struct adv7511 *adv7511)
{
struct adv7511 *adv7511 = devid;
if (adv7511_hpd(adv7511))
drm_helper_hpd_irq_event(adv7511->encoder->dev);
wake_up_all(&adv7511->wq);
return IRQ_HANDLED;
}
static unsigned int adv7511_is_interrupt_pending(struct adv7511 *adv7511,
unsigned int irq)
{ {
unsigned int irq0, irq1; unsigned int irq0, irq1;
unsigned int pending;
int ret; int ret;
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)
return 0; return ret;
ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(1), &irq1); ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(1), &irq1);
if (ret < 0) if (ret < 0)
return 0; return ret;
regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0);
regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1);
if (irq0 & ADV7511_INT0_HDP)
drm_helper_hpd_irq_event(adv7511->encoder->dev);
if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) {
adv7511->edid_read = true;
pending = (irq1 << 8) | irq0; if (adv7511->i2c_main->irq)
wake_up_all(&adv7511->wq);
}
return pending & irq; return 0;
} }
static int adv7511_wait_for_interrupt(struct adv7511 *adv7511, int irq, static irqreturn_t adv7511_irq_handler(int irq, void *devid)
int timeout) {
struct adv7511 *adv7511 = devid;
int ret;
ret = adv7511_irq_process(adv7511);
return ret < 0 ? IRQ_NONE : IRQ_HANDLED;
}
/* -----------------------------------------------------------------------------
* EDID retrieval
*/
static int adv7511_wait_for_edid(struct adv7511 *adv7511, int timeout)
{ {
unsigned int pending;
int ret; int ret;
if (adv7511->i2c_main->irq) { if (adv7511->i2c_main->irq) {
ret = wait_event_interruptible_timeout(adv7511->wq, ret = wait_event_interruptible_timeout(adv7511->wq,
adv7511_is_interrupt_pending(adv7511, irq), adv7511->edid_read, msecs_to_jiffies(timeout));
msecs_to_jiffies(timeout));
if (ret <= 0)
return 0;
pending = adv7511_is_interrupt_pending(adv7511, irq);
} else { } else {
if (timeout < 25) for (; timeout > 0; timeout -= 25) {
timeout = 25; ret = adv7511_irq_process(adv7511);
do { if (ret < 0)
pending = adv7511_is_interrupt_pending(adv7511, irq); break;
if (pending)
if (adv7511->edid_read)
break; break;
msleep(25); msleep(25);
timeout -= 25; }
} while (timeout >= 25);
} }
return pending; return adv7511->edid_read ? 0 : -EIO;
} }
/* -----------------------------------------------------------------------------
* EDID retrieval
*/
static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block, static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
size_t len) size_t len)
{ {
...@@ -463,21 +466,14 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block, ...@@ -463,21 +466,14 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
return ret; return ret;
if (status != 2) { if (status != 2) {
adv7511->edid_read = false;
regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT, regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT,
block); block);
ret = adv7511_wait_for_interrupt(adv7511, ret = adv7511_wait_for_edid(adv7511, 200);
ADV7511_INT0_EDID_READY | if (ret < 0)
(ADV7511_INT1_DDC_ERROR << 8), 200); return ret;
if (!(ret & ADV7511_INT0_EDID_READY))
return -EIO;
} }
regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
ADV7511_INT0_EDID_READY);
regmap_write(adv7511->regmap, ADV7511_REG_INT(1),
ADV7511_INT1_DDC_ERROR);
/* Break this apart, hopefully more I2C controllers will /* Break this apart, hopefully more I2C controllers will
* support 64 byte transfers than 256 byte transfers * support 64 byte transfers than 256 byte transfers
*/ */
......
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