From: Rahul Sharma Rahul.Sharma@samsung.com
Series is addressing various issues in the drm hdmi driver for exynos Soc.
Based on Inki Dae's exynos-drm-next branch.
Daniel Kurtz (1): drm/exynos: hdmi: remove unnecessary memset
Paul Taysom (1): drm/exynos: check for null pointers in error handling
Rahul Sharma (1): drm/exynos: replace hdmi reset with hdmi disable
Sean Paul (3): drm/exynos: Don't reset hdmiphy on hdmi off drm/exynos: Debounce HDMI hotplug interrupts drm/exynos: Read hpd gpio in is_connected callback
Shirish S (1): drm/exynos: add hdmiphy power on/off sequence
drivers/gpu/drm/exynos/exynos_hdmi.c | 115 ++++++++++++++++++++++------------ drivers/gpu/drm/exynos/regs-hdmi.h | 7 ++- 2 files changed, 81 insertions(+), 41 deletions(-)
From: Sean Paul seanpaul@chromium.org
This patch removes the hdmiphy reset in hdmi_poweroff. The hdmiphy reset was added to take advantage of exynos clockgating, doing it would gate the entire TV domain. Unfortunately, mixer is included in the TV domain and its vsync interrupts are stopped when TV is gated.
Signed-off-by: Sean Paul seanpaul@chromium.org Signed-off-by: Rahul Sharma Rahul.Sharma@samsung.com --- drivers/gpu/drm/exynos/exynos_hdmi.c | 5 ----- 1 file changed, 5 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index f3189af..79f98ac 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -2056,11 +2056,6 @@ static void hdmi_poweroff(struct exynos_drm_display *display) goto out; mutex_unlock(&hdata->hdmi_mutex);
- /* - * The TV power domain needs any condition of hdmiphy to turn off and - * its reset state seems to meet the condition. - */ - hdmiphy_conf_reset(hdata); hdmiphy_poweroff(hdata);
clk_disable_unprepare(res->sclk_hdmi);
From: Sean Paul seanpaul@chromium.org
This patch debounces hotplug interrupts generated by the HDMI hotplug gpio. The reason this is needed is that we get multiple (5) interrupts every time a monitor is inserted which causes us to needlessly enable and disable the IP block.
Signed-off-by: Sean Paul seanpaul@chromium.org Signed-off-by: Rahul Sharma Rahul.Sharma@samsung.com --- drivers/gpu/drm/exynos/exynos_hdmi.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 79f98ac..f6d4435 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -51,6 +51,8 @@ #define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev)) #define ctx_from_connector(c) container_of(c, struct hdmi_context, connector)
+#define HOTPLUG_DEBOUNCE_MS 1100 + /* AVI header and aspect ratio */ #define HDMI_AVI_VERSION 0x02 #define HDMI_AVI_LENGTH 0x0D @@ -189,6 +191,7 @@ struct hdmi_context {
void __iomem *regs; int irq; + struct delayed_work hotplug_work;
struct i2c_adapter *ddc_adpt; struct i2c_client *hdmiphy_port; @@ -2058,6 +2061,8 @@ static void hdmi_poweroff(struct exynos_drm_display *display)
hdmiphy_poweroff(hdata);
+ cancel_delayed_work(&hdata->hotplug_work); + clk_disable_unprepare(res->sclk_hdmi); clk_disable_unprepare(res->hdmi); /* reset pmu hdmiphy control bit to disable hdmiphy */ @@ -2108,9 +2113,11 @@ static struct exynos_drm_display hdmi_display = { .ops = &hdmi_display_ops, };
-static irqreturn_t hdmi_irq_thread(int irq, void *arg) +static void hdmi_hotplug_work_func(struct work_struct *work) { - struct hdmi_context *hdata = arg; + struct hdmi_context *hdata; + + hdata = container_of(work, struct hdmi_context, hotplug_work.work);
mutex_lock(&hdata->hdmi_mutex); hdata->hpd = 1;//gpio_get_value(hdata->hpd_gpio); @@ -2118,6 +2125,14 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
if (hdata->drm_dev) drm_helper_hpd_irq_event(hdata->drm_dev); +} + +static irqreturn_t hdmi_irq_thread(int irq, void *arg) +{ + struct hdmi_context *hdata = arg; + + mod_delayed_work(system_wq, &hdata->hotplug_work, + msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
return IRQ_HANDLED; } @@ -2315,6 +2330,8 @@ static int hdmi_probe(struct platform_device *pdev)
hdata->hpd = 1;//gpio_get_value(hdata->hpd_gpio);
+ INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func); + ret = devm_request_threaded_irq(dev, hdata->irq, NULL, hdmi_irq_thread, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, @@ -2351,6 +2368,8 @@ static int hdmi_remove(struct platform_device *pdev) struct exynos_drm_display *display = get_hdmi_display(dev); struct hdmi_context *hdata = display->ctx;
+ cancel_delayed_work_sync(&hdata->hotplug_work); + put_device(&hdata->hdmiphy_port->dev); put_device(&hdata->ddc_adpt->dev); pm_runtime_disable(&pdev->dev);
From: Paul Taysom taysom@chromium.org
Smatch error from arm build: drivers/gpu/drm/exynos/ exynos_hdmi.c:2374 hdmi_probe() error: potential NULL dereference 'hdata->hdmiphy_port'.
Added check for hdata->hdmiphy_port that it is not NULL.
Signed-off-by: Paul Taysom taysom@chromium.org Signed-off-by: Rahul Sharma Rahul.Sharma@samsung.com --- drivers/gpu/drm/exynos/exynos_hdmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index f6d4435..5ed8973 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -2356,7 +2356,8 @@ static int hdmi_probe(struct platform_device *pdev) return 0;
err_hdmiphy: - put_device(&hdata->hdmiphy_port->dev); + if (hdata->hdmiphy_port) + put_device(&hdata->hdmiphy_port->dev); err_ddc: put_device(&hdata->ddc_adpt->dev); return ret;
From: Daniel Kurtz djkurtz@chromium.org
Our resources were just zalloc'ed as part of hdata. They are already 0.
Signed-off-by: Daniel Kurtz djkurtz@chromium.org Signed-off-by: Rahul Sharma Rahul.Sharma@samsung.com --- drivers/gpu/drm/exynos/exynos_hdmi.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 5ed8973..539e603 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -2151,8 +2151,6 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
DRM_DEBUG_KMS("HDMI resource init\n");
- memset(res, 0, sizeof(*res)); - /* get clocks, power */ res->hdmi = devm_clk_get(dev, "hdmi"); if (IS_ERR(res->hdmi)) {
From: Shirish S s.shirish@samsung.com
This patch implements the power on/off sequence of HDMI PHY in exynos5420 and exynos5250 as provided by the hardware team.
This has been verified for mulitple iterations of S2R.
Signed-off-by: Shirish S s.shirish@samsung.com Signed-off-by: Rahul Sharma Rahul.Sharma@samsung.com --- drivers/gpu/drm/exynos/exynos_hdmi.c | 40 +++++++++++++++++++++++++++++----- drivers/gpu/drm/exynos/regs-hdmi.h | 7 +++++- 2 files changed, 40 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 539e603..b2cbf43 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1711,16 +1711,44 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
static void hdmiphy_poweron(struct hdmi_context *hdata) { - if (hdata->type == HDMI_TYPE14) - hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, - HDMI_PHY_POWER_OFF_EN); + if (hdata->type != HDMI_TYPE14) + return; + + DRM_DEBUG_KMS("\n"); + + /* For PHY Mode Setting */ + hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE, + HDMI_PHY_ENABLE_MODE_SET); + /* Phy Power On */ + hdmiphy_reg_writeb(hdata, HDMIPHY_POWER, + HDMI_PHY_POWER_ON); + /* For PHY Mode Setting */ + hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE, + HDMI_PHY_DISABLE_MODE_SET); + /* PHY SW Reset */ + hdmiphy_conf_reset(hdata); }
static void hdmiphy_poweroff(struct hdmi_context *hdata) { - if (hdata->type == HDMI_TYPE14) - hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, - HDMI_PHY_POWER_OFF_EN); + if (hdata->type != HDMI_TYPE14) + return; + + DRM_DEBUG_KMS("\n"); + + /* PHY SW Reset */ + hdmiphy_conf_reset(hdata); + /* For PHY Mode Setting */ + hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE, + HDMI_PHY_ENABLE_MODE_SET); + + /* PHY Power Off */ + hdmiphy_reg_writeb(hdata, HDMIPHY_POWER, + HDMI_PHY_POWER_OFF); + + /* For PHY Mode Setting */ + hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE, + HDMI_PHY_DISABLE_MODE_SET); }
static void hdmiphy_conf_apply(struct hdmi_context *hdata) diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h index 344a5db..fd4c590 100644 --- a/drivers/gpu/drm/exynos/regs-hdmi.h +++ b/drivers/gpu/drm/exynos/regs-hdmi.h @@ -579,7 +579,12 @@ #define HDMI_TG_3D HDMI_TG_BASE(0x00F0)
/* HDMI PHY Registers Offsets*/ -#define HDMIPHY_MODE_SET_DONE (0x7C >> 2) +#define HDMIPHY_POWER (0x74 >> 2) +#define HDMIPHY_MODE_SET_DONE (0x7c >> 2) + +/* HDMI PHY Values */ +#define HDMI_PHY_POWER_ON 0x80 +#define HDMI_PHY_POWER_OFF 0xff
/* HDMI PHY Values */ #define HDMI_PHY_DISABLE_MODE_SET 0x80
Hi Inki,
Please review this patch.
Regards, Rahul Sharma
On 3 April 2014 20:41, Rahul Sharma rahul.sharma@samsung.com wrote:
From: Shirish S s.shirish@samsung.com
This patch implements the power on/off sequence of HDMI PHY in exynos5420 and exynos5250 as provided by the hardware team.
This has been verified for mulitple iterations of S2R.
Signed-off-by: Shirish S s.shirish@samsung.com Signed-off-by: Rahul Sharma Rahul.Sharma@samsung.com
drivers/gpu/drm/exynos/exynos_hdmi.c | 40 +++++++++++++++++++++++++++++----- drivers/gpu/drm/exynos/regs-hdmi.h | 7 +++++- 2 files changed, 40 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 539e603..b2cbf43 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1711,16 +1711,44 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
static void hdmiphy_poweron(struct hdmi_context *hdata) {
if (hdata->type == HDMI_TYPE14)
hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
HDMI_PHY_POWER_OFF_EN);
if (hdata->type != HDMI_TYPE14)
return;
DRM_DEBUG_KMS("\n");
/* For PHY Mode Setting */
hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
HDMI_PHY_ENABLE_MODE_SET);
/* Phy Power On */
hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
HDMI_PHY_POWER_ON);
/* For PHY Mode Setting */
hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
HDMI_PHY_DISABLE_MODE_SET);
/* PHY SW Reset */
hdmiphy_conf_reset(hdata);
}
static void hdmiphy_poweroff(struct hdmi_context *hdata) {
if (hdata->type == HDMI_TYPE14)
hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
HDMI_PHY_POWER_OFF_EN);
if (hdata->type != HDMI_TYPE14)
return;
DRM_DEBUG_KMS("\n");
/* PHY SW Reset */
hdmiphy_conf_reset(hdata);
/* For PHY Mode Setting */
hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
HDMI_PHY_ENABLE_MODE_SET);
/* PHY Power Off */
hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
HDMI_PHY_POWER_OFF);
/* For PHY Mode Setting */
hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
HDMI_PHY_DISABLE_MODE_SET);
}
static void hdmiphy_conf_apply(struct hdmi_context *hdata) diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h index 344a5db..fd4c590 100644 --- a/drivers/gpu/drm/exynos/regs-hdmi.h +++ b/drivers/gpu/drm/exynos/regs-hdmi.h @@ -579,7 +579,12 @@ #define HDMI_TG_3D HDMI_TG_BASE(0x00F0)
/* HDMI PHY Registers Offsets*/ -#define HDMIPHY_MODE_SET_DONE (0x7C >> 2) +#define HDMIPHY_POWER (0x74 >> 2) +#define HDMIPHY_MODE_SET_DONE (0x7c >> 2)
+/* HDMI PHY Values */ +#define HDMI_PHY_POWER_ON 0x80 +#define HDMI_PHY_POWER_OFF 0xff
/* HDMI PHY Values */
#define HDMI_PHY_DISABLE_MODE_SET 0x80
1.7.9.5
From: Sean Paul seanpaul@chromium.org
This patch adds a gpio read of hpd during the is_connected callback. This fixes the case where hdmi is off going into suspend and the cable is plugged in while suspended. In this case, the hpd interrupt does not fire and is_connected will return false.
Signed-off-by: Sean Paul seanpaul@chromium.org Signed-off-by: Rahul Sharma Rahul.Sharma@samsung.com --- drivers/gpu/drm/exynos/exynos_hdmi.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index b2cbf43..fce2f7b 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1031,6 +1031,8 @@ static enum drm_connector_status hdmi_detect(struct drm_connector *connector, { struct hdmi_context *hdata = ctx_from_connector(connector);
+ hdata->hpd = gpio_get_value(hdata->hpd_gpio); + return hdata->hpd ? connector_status_connected : connector_status_disconnected; }
Before setting the core and timing generation registers, hdmi driver resets the whole hdmi hardware, which also resets the audio related registers.
Hdmi reset is replaced by hdmi disable which is called just before setting the core and timing registers. It also ensure that audio settings are not changed.
Signed-off-by: Rahul Sharma rahul.sharma@samsung.com Signed-off-by: Shirish S s.shirish@samsung.com --- drivers/gpu/drm/exynos/exynos_hdmi.c | 40 ++++++++++++++-------------------- 1 file changed, 16 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index fce2f7b..4c69139 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -197,6 +197,7 @@ struct hdmi_context { struct i2c_client *hdmiphy_port;
/* current hdmiphy conf regs */ + struct drm_display_mode current_mode; struct hdmi_conf_regs mode_conf;
struct hdmi_resources res; @@ -1350,20 +1351,15 @@ static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff) HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK); }
-static void hdmi_conf_reset(struct hdmi_context *hdata) +static void hdmi_start(struct hdmi_context *hdata, bool start) { - u32 reg; + u32 val = start ? HDMI_TG_EN : 0;
- if (hdata->type == HDMI_TYPE13) - reg = HDMI_V13_CORE_RSTOUT; - else - reg = HDMI_CORE_RSTOUT; + if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE) + val |= HDMI_FIELD_EN;
- /* resetting HDMI core */ - hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT); - usleep_range(10000, 12000); - hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT); - usleep_range(10000, 12000); + hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN); + hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN); }
static void hdmi_conf_init(struct hdmi_context *hdata) @@ -1500,12 +1496,7 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata) clk_prepare_enable(hdata->res.sclk_hdmi);
/* enable HDMI and timing generator */ - hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); - if (core->int_pro_mode[0]) - hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN | - HDMI_FIELD_EN); - else - hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); + hdmi_start(hdata, true); }
static void hdmi_v14_mode_apply(struct hdmi_context *hdata) @@ -1667,12 +1658,7 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata) clk_prepare_enable(hdata->res.sclk_hdmi);
/* enable HDMI and timing generator */ - hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); - if (core->int_pro_mode[0]) - hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN | - HDMI_FIELD_EN); - else - hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); + hdmi_start(hdata, true); }
static void hdmi_mode_apply(struct hdmi_context *hdata) @@ -1788,7 +1774,7 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) hdmiphy_conf_apply(hdata);
mutex_lock(&hdata->hdmi_mutex); - hdmi_conf_reset(hdata); + hdmi_start(hdata, false); hdmi_conf_init(hdata); mutex_unlock(&hdata->hdmi_mutex);
@@ -2029,6 +2015,9 @@ static void hdmi_mode_set(struct exynos_drm_display *display, m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ? "INTERLACED" : "PROGERESSIVE");
+ /* preserve mode information for later use. */ + drm_mode_copy(&hdata->current_mode, mode); + if (hdata->type == HDMI_TYPE13) hdmi_v13_mode_set(hdata, mode); else @@ -2089,6 +2078,9 @@ static void hdmi_poweroff(struct exynos_drm_display *display) goto out; mutex_unlock(&hdata->hdmi_mutex);
+ /* HDMI System Disable */ + hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN); + hdmiphy_poweroff(hdata);
cancel_delayed_work(&hdata->hotplug_work);
dri-devel@lists.freedesktop.org