diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2015-06-06 16:41:09 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2015-09-15 11:19:48 -0400 |
commit | 0fc6f44d9683c61678da4b0eebc89e8fa624de39 (patch) | |
tree | bf0e5d5e975a34c36e352d48609e0a1df7e871c8 | |
parent | f84a97d4804a09240372dc7b195f9d6162152228 (diff) |
drm/i2c: tda998x: re-implement "Fix EDID read timeout on HDMI connect"
Commit 6833d26ef823 ("drm: tda998x: Fix EDID read timeout on HDMI
connect") used a weak scheme to try and delay reading EDID on a HDMI
connect event. It is weak because delaying the notification of a
hotplug event does not stop userspace from trying to read the EDID
within the 100ms delay.
The solution provided here solves this issue:
* When a HDMI connection event is detected, mark a blocking flag for
EDID reads, and start a timer for the delay.
* If an EDID read is attempted, and the blocking flag is set, wait
for the blocking flag to clear.
* When the timer expires, clear the blocking flag and wake any thread
waiting for the EDID read.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | drivers/gpu/drm/i2c/tda998x_drv.c | 80 |
1 files changed, 68 insertions, 12 deletions
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index ad3ce3479edf..a53696fd8938 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c | |||
@@ -34,7 +34,6 @@ struct tda998x_priv { | |||
34 | struct i2c_client *cec; | 34 | struct i2c_client *cec; |
35 | struct i2c_client *hdmi; | 35 | struct i2c_client *hdmi; |
36 | struct mutex mutex; | 36 | struct mutex mutex; |
37 | struct delayed_work dwork; | ||
38 | uint16_t rev; | 37 | uint16_t rev; |
39 | uint8_t current_page; | 38 | uint8_t current_page; |
40 | int dpms; | 39 | int dpms; |
@@ -47,6 +46,11 @@ struct tda998x_priv { | |||
47 | wait_queue_head_t wq_edid; | 46 | wait_queue_head_t wq_edid; |
48 | volatile int wq_edid_wait; | 47 | volatile int wq_edid_wait; |
49 | struct drm_encoder *encoder; | 48 | struct drm_encoder *encoder; |
49 | |||
50 | struct work_struct detect_work; | ||
51 | struct timer_list edid_delay_timer; | ||
52 | wait_queue_head_t edid_delay_waitq; | ||
53 | bool edid_delay_active; | ||
50 | }; | 54 | }; |
51 | 55 | ||
52 | #define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv) | 56 | #define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv) |
@@ -551,15 +555,50 @@ tda998x_reset(struct tda998x_priv *priv) | |||
551 | reg_write(priv, REG_MUX_VP_VIP_OUT, 0x24); | 555 | reg_write(priv, REG_MUX_VP_VIP_OUT, 0x24); |
552 | } | 556 | } |
553 | 557 | ||
554 | /* handle HDMI connect/disconnect */ | 558 | /* |
555 | static void tda998x_hpd(struct work_struct *work) | 559 | * The TDA998x has a problem when trying to read the EDID close to a |
560 | * HPD assertion: it needs a delay of 100ms to avoid timing out while | ||
561 | * trying to read EDID data. | ||
562 | * | ||
563 | * However, tda998x_encoder_get_modes() may be called at any moment | ||
564 | * after tda998x_encoder_detect() indicates that we are connected, so | ||
565 | * we need to delay probing modes in tda998x_encoder_get_modes() after | ||
566 | * we have seen a HPD inactive->active transition. This code implements | ||
567 | * that delay. | ||
568 | */ | ||
569 | static void tda998x_edid_delay_done(unsigned long data) | ||
570 | { | ||
571 | struct tda998x_priv *priv = (struct tda998x_priv *)data; | ||
572 | |||
573 | priv->edid_delay_active = false; | ||
574 | wake_up(&priv->edid_delay_waitq); | ||
575 | schedule_work(&priv->detect_work); | ||
576 | } | ||
577 | |||
578 | static void tda998x_edid_delay_start(struct tda998x_priv *priv) | ||
579 | { | ||
580 | priv->edid_delay_active = true; | ||
581 | mod_timer(&priv->edid_delay_timer, jiffies + HZ/10); | ||
582 | } | ||
583 | |||
584 | static int tda998x_edid_delay_wait(struct tda998x_priv *priv) | ||
585 | { | ||
586 | return wait_event_killable(priv->edid_delay_waitq, !priv->edid_delay_active); | ||
587 | } | ||
588 | |||
589 | /* | ||
590 | * We need to run the KMS hotplug event helper outside of our threaded | ||
591 | * interrupt routine as this can call back into our get_modes method, | ||
592 | * which will want to make use of interrupts. | ||
593 | */ | ||
594 | static void tda998x_detect_work(struct work_struct *work) | ||
556 | { | 595 | { |
557 | struct delayed_work *dwork = to_delayed_work(work); | ||
558 | struct tda998x_priv *priv = | 596 | struct tda998x_priv *priv = |
559 | container_of(dwork, struct tda998x_priv, dwork); | 597 | container_of(work, struct tda998x_priv, detect_work); |
598 | struct drm_device *dev = priv->encoder->dev; | ||
560 | 599 | ||
561 | if (priv->encoder->dev) | 600 | if (dev) |
562 | drm_kms_helper_hotplug_event(priv->encoder->dev); | 601 | drm_kms_helper_hotplug_event(dev); |
563 | } | 602 | } |
564 | 603 | ||
565 | /* | 604 | /* |
@@ -585,7 +624,11 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data) | |||
585 | wake_up(&priv->wq_edid); | 624 | wake_up(&priv->wq_edid); |
586 | handled = true; | 625 | handled = true; |
587 | } else if (cec != 0) { /* HPD change */ | 626 | } else if (cec != 0) { /* HPD change */ |
588 | schedule_delayed_work(&priv->dwork, HZ/10); | 627 | if (lvl & CEC_RXSHPDLEV_HPD) |
628 | tda998x_edid_delay_start(priv); | ||
629 | else | ||
630 | schedule_work(&priv->detect_work); | ||
631 | |||
589 | handled = true; | 632 | handled = true; |
590 | } | 633 | } |
591 | return IRQ_RETVAL(handled); | 634 | return IRQ_RETVAL(handled); |
@@ -1103,6 +1146,14 @@ tda998x_encoder_get_modes(struct tda998x_priv *priv, | |||
1103 | struct edid *edid; | 1146 | struct edid *edid; |
1104 | int n; | 1147 | int n; |
1105 | 1148 | ||
1149 | /* | ||
1150 | * If we get killed while waiting for the HPD timeout, return | ||
1151 | * no modes found: we are not in a restartable path, so we | ||
1152 | * can't handle signals gracefully. | ||
1153 | */ | ||
1154 | if (tda998x_edid_delay_wait(priv)) | ||
1155 | return 0; | ||
1156 | |||
1106 | if (priv->rev == TDA19988) | 1157 | if (priv->rev == TDA19988) |
1107 | reg_clear(priv, REG_TX4, TX4_PD_RAM); | 1158 | reg_clear(priv, REG_TX4, TX4_PD_RAM); |
1108 | 1159 | ||
@@ -1149,10 +1200,12 @@ static void tda998x_destroy(struct tda998x_priv *priv) | |||
1149 | /* disable all IRQs and free the IRQ handler */ | 1200 | /* disable all IRQs and free the IRQ handler */ |
1150 | cec_write(priv, REG_CEC_RXSHPDINTENA, 0); | 1201 | cec_write(priv, REG_CEC_RXSHPDINTENA, 0); |
1151 | reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); | 1202 | reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); |
1152 | if (priv->hdmi->irq) { | 1203 | |
1204 | if (priv->hdmi->irq) | ||
1153 | free_irq(priv->hdmi->irq, priv); | 1205 | free_irq(priv->hdmi->irq, priv); |
1154 | cancel_delayed_work_sync(&priv->dwork); | 1206 | |
1155 | } | 1207 | del_timer_sync(&priv->edid_delay_timer); |
1208 | cancel_work_sync(&priv->detect_work); | ||
1156 | 1209 | ||
1157 | i2c_unregister_device(priv->cec); | 1210 | i2c_unregister_device(priv->cec); |
1158 | } | 1211 | } |
@@ -1253,6 +1306,10 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) | |||
1253 | priv->dpms = DRM_MODE_DPMS_OFF; | 1306 | priv->dpms = DRM_MODE_DPMS_OFF; |
1254 | 1307 | ||
1255 | mutex_init(&priv->mutex); /* protect the page access */ | 1308 | mutex_init(&priv->mutex); /* protect the page access */ |
1309 | init_waitqueue_head(&priv->edid_delay_waitq); | ||
1310 | setup_timer(&priv->edid_delay_timer, tda998x_edid_delay_done, | ||
1311 | (unsigned long)priv); | ||
1312 | INIT_WORK(&priv->detect_work, tda998x_detect_work); | ||
1256 | 1313 | ||
1257 | /* wake up the device: */ | 1314 | /* wake up the device: */ |
1258 | cec_write(priv, REG_CEC_ENAMODS, | 1315 | cec_write(priv, REG_CEC_ENAMODS, |
@@ -1311,7 +1368,6 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) | |||
1311 | 1368 | ||
1312 | /* init read EDID waitqueue and HDP work */ | 1369 | /* init read EDID waitqueue and HDP work */ |
1313 | init_waitqueue_head(&priv->wq_edid); | 1370 | init_waitqueue_head(&priv->wq_edid); |
1314 | INIT_DELAYED_WORK(&priv->dwork, tda998x_hpd); | ||
1315 | 1371 | ||
1316 | /* clear pending interrupts */ | 1372 | /* clear pending interrupts */ |
1317 | reg_read(priv, REG_INT_FLAGS_0); | 1373 | reg_read(priv, REG_INT_FLAGS_0); |