From feaeccc19de59001b4c4dff59cecb6b2f395adf9 Mon Sep 17 00:00:00 2001 From: Algea Cao Date: Thu, 8 Aug 2019 10:29:33 +0800 Subject: [PATCH] drm: bridge: dw-hdmi: optimize edid reading process 1.change SDA high level holding time to 3us. 2.when plug in,add timer to avoid unstable state. Change-Id: Idc6faec710137ac9f8e589d75cbc1b85f7a45faf Signed-off-by: Algea Cao --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 64 ++++++++++++++++++----- drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 1 + 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index f784f8098114..2405bf9ecf0f 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -227,6 +227,10 @@ struct dw_hdmi { void __iomem *regs; bool sink_is_hdmi; bool sink_has_audio; + bool hpd_state; + + struct delayed_work work; + struct workqueue_struct *workqueue; struct mutex mutex; /* for state below and previous_mode */ enum drm_connector_force force; /* mutex-protected force state */ @@ -288,6 +292,49 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, hdmi_modb(hdmi, data << shift, mask, reg); } +static void repo_hpd_event(struct work_struct *p_work) +{ + struct dw_hdmi *hdmi = container_of(p_work, struct dw_hdmi, work.work); + + if (hdmi->bridge.dev) + drm_helper_hpd_irq_event(hdmi->bridge.dev); +#ifdef CONFIG_SWITCH + if (hdmi->hpd_state) + switch_set_state(&hdmi->switchdev, 1); + else + switch_set_state(&hdmi->switchdev, 0); +#endif +} + +static bool check_hdmi_irq(struct dw_hdmi *hdmi, int intr_stat, + int phy_int_pol) +{ + int msecs; + + /* To determine whether interrupt type is HPD */ + if (!(intr_stat & HDMI_IH_PHY_STAT0_HPD)) + return false; + + if (phy_int_pol & HDMI_PHY_HPD) { + dev_dbg(hdmi->dev, "dw hdmi plug in\n"); + msecs = 150; + hdmi->hpd_state = true; + } else { + dev_dbg(hdmi->dev, "dw hdmi plug out\n"); + msecs = 20; + hdmi->hpd_state = false; + } + mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs)); + + return true; +} + +static void init_hpd_work(struct dw_hdmi *hdmi) +{ + hdmi->workqueue = create_workqueue("hpd_queue"); + INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event); +} + static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) { /* Software reset */ @@ -308,6 +355,9 @@ static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) /* Mute DONE and ERROR interrupts */ hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE, HDMI_IH_MUTE_I2CM_STAT0); + + /* set SDA high level holding time */ + hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD); } static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi, @@ -2513,18 +2563,7 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) CEC_PHYS_ADDR_INVALID); } - if (intr_stat & HDMI_IH_PHY_STAT0_HPD) { - dev_dbg(hdmi->dev, "EVENT=%s\n", - phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout"); - if (hdmi->bridge.dev) - drm_helper_hpd_irq_event(hdmi->bridge.dev); -#ifdef CONFIG_SWITCH - if (phy_int_pol & HDMI_PHY_HPD) - switch_set_state(&hdmi->switchdev, 1); - else - switch_set_state(&hdmi->switchdev, 0); -#endif - } + check_hdmi_irq(hdmi, intr_stat, phy_int_pol); hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), @@ -2957,6 +2996,7 @@ __dw_hdmi_probe(struct platform_device *pdev, prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without", hdmi->phy.name); + init_hpd_work(hdmi); initialize_hdmi_ih_mutes(hdmi); irq = platform_get_irq(pdev, 0); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index f78d7651e45a..2a4d70bc637c 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h @@ -544,6 +544,7 @@ #define HDMI_I2CM_FS_SCL_HCNT_0_ADDR 0x7E10 #define HDMI_I2CM_FS_SCL_LCNT_1_ADDR 0x7E11 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12 +#define HDMI_I2CM_SDA_HOLD 0x7E13 enum { /* PRODUCT_ID0 field values */