From: Biju Das <[email protected]>

On RZ/G3E SMARC EVK using PSCI, s2ram powers down the SoC. Testing ADV7535
IRQ configured as edge-triggered interrupt on RZ/G3E SMARC EVK shows that
it is missing HPD IRQ during system resume, as the status change occurs
before the IRQ/pincontrol resume. Once the status bit is set, there won't
be any further IRQ unless the status bit is cleared.

Clear any pending HPD IRQs before powering on the ADV7535 device to
deliver HPD interrupts after resume().

Signed-off-by: Biju Das <[email protected]>
---
 drivers/gpu/drm/bridge/adv7511/adv7511.h     |  1 +
 drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 32 ++++++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h 
b/drivers/gpu/drm/bridge/adv7511/adv7511.h
index 8be7266fd4f4..03aa23836ca4 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511.h
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h
@@ -393,6 +393,7 @@ struct adv7511 {
        bool cec_enabled_adap;
        struct clk *cec_clk;
        u32 cec_clk_freq;
+       bool suspended;
 };
 
 static inline struct adv7511 *bridge_to_adv7511(struct drm_bridge *bridge)
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c 
b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index b9be86541307..8d9467187d7c 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -790,6 +790,25 @@ static void adv7511_bridge_atomic_enable(struct drm_bridge 
*bridge,
        struct drm_connector_state *conn_state;
        struct drm_crtc_state *crtc_state;
 
+       if (adv->i2c_main->irq && adv->suspended) {
+               unsigned int irq;
+
+               /*
+                * If ADV7511 IRQ is configured as edge triggered interrupt, it
+                * will miss the IRQ during system resume as the status change
+                * occurs before IRQ/pincontrol resume. Once the status bit is
+                * set there won't be any further IRQ unless the status bit is
+                * cleared. So, clear the IRQ status bit for further delivery
+                * of HPD IRQ.
+                */
+               regmap_read(adv->regmap, ADV7511_REG_INT(0), &irq);
+               if (irq & ADV7511_INT0_HPD)
+                       regmap_write(adv->regmap, ADV7511_REG_INT(0),
+                                    ADV7511_INT0_HPD);
+
+               adv->suspended = false;
+       }
+
        adv7511_power_on(adv);
 
        connector = drm_atomic_get_new_connector_for_encoder(state, 
bridge->encoder);
@@ -1407,6 +1426,16 @@ static void adv7511_remove(struct i2c_client *i2c)
        i2c_unregister_device(adv7511->i2c_edid);
 }
 
+static int adv7511_suspend(struct device *dev)
+{
+       struct i2c_client *i2c = to_i2c_client(dev);
+       struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
+
+       adv7511->suspended = true;
+
+       return 0;
+}
+
 static const struct adv7511_chip_info adv7511_chip_info = {
        .type = ADV7511,
        .name = "ADV7511",
@@ -1439,6 +1468,8 @@ static const struct adv7511_chip_info adv7535_chip_info = 
{
        .hpd_override_enable = true,
 };
 
+static DEFINE_SIMPLE_DEV_PM_OPS(adv7511_pm_ops, adv7511_suspend, NULL);
+
 static const struct i2c_device_id adv7511_i2c_ids[] = {
        { "adv7511", (kernel_ulong_t)&adv7511_chip_info },
        { "adv7511w", (kernel_ulong_t)&adv7511_chip_info },
@@ -1467,6 +1498,7 @@ static struct i2c_driver adv7511_driver = {
        .driver = {
                .name = "adv7511",
                .of_match_table = adv7511_of_ids,
+               .pm = pm_sleep_ptr(&adv7511_pm_ops),
        },
        .id_table = adv7511_i2c_ids,
        .probe = adv7511_probe,
-- 
2.43.0

Reply via email to