This patch implements the power on/off sequence
(and its dependant functions) of HDMI exynos5250
as provided by the hardware team.

Signed-off-by: Shirish S <s.shirish at samsung.com>
---
 drivers/gpu/drm/exynos/exynos_hdmi.c |  137 +++++++++++++++++++++++++++++-----
 drivers/gpu/drm/exynos/regs-hdmi.h   |   15 ++++
 2 files changed, 133 insertions(+), 19 deletions(-)
 mode change 100644 => 100755 drivers/gpu/drm/exynos/exynos_hdmi.c
 mode change 100644 => 100755 drivers/gpu/drm/exynos/regs-hdmi.h

diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c 
b/drivers/gpu/drm/exynos/exynos_hdmi.c
old mode 100644
new mode 100755
index 12fdf55..ee000f7
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -180,6 +180,7 @@ struct hdmi_context {

        void __iomem                    *regs;
        int                             irq;
+       void __iomem                    *phy_pow_ctrl_reg;

        struct i2c_client               *ddc_port;
        struct i2c_client               *hdmiphy_port;
@@ -387,6 +388,33 @@ static inline void hdmi_reg_writemask(struct hdmi_context 
*hdata,
        writel(value, hdata->regs + reg_id);
 }

+
+static inline void hdmi_phy_pow_ctrl_reg_writemask(struct hdmi_context *hdata,
+                                u32 value, u32 mask)
+{
+       u32 old = readl(hdata->phy_pow_ctrl_reg);
+       value = (value & mask) | (old & ~mask);
+       writel(value, hdata->phy_pow_ctrl_reg);
+}
+
+static int hdmiphy_reg_writeb(struct hdmi_context *hdata,
+                       u32 reg_offset, u8 value)
+{
+       if (hdata->hdmiphy_port) {
+               u8 buffer[2];
+               int ret;
+
+               buffer[0] = reg_offset;
+               buffer[1] = value;
+
+               ret = i2c_master_send(hdata->hdmiphy_port, buffer, 2);
+               if (ret == 2)
+                       return 0;
+               return ret;
+       } else
+               return 0;
+}
+
 static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix)
 {
 #define DUMPREG(reg_id) \
@@ -1386,19 +1414,14 @@ static void hdmi_mode_apply(struct hdmi_context *hdata)

 static void hdmiphy_conf_reset(struct hdmi_context *hdata)
 {
-       u8 buffer[2];
        u32 reg;

        clk_disable_unprepare(hdata->res.sclk_hdmi);
        clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_pixel);
        clk_prepare_enable(hdata->res.sclk_hdmi);

-       /* operation mode */
-       buffer[0] = 0x1f;
-       buffer[1] = 0x00;
-
-       if (hdata->hdmiphy_port)
-               i2c_master_send(hdata->hdmiphy_port, buffer, 2);
+       hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+                                               HDMI_PHY_ENABLE_MODE_SET);

        if (hdata->type == HDMI_TYPE13)
                reg = HDMI_V13_PHY_RSTOUT;
@@ -1414,16 +1437,42 @@ 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_TYPE13)
+               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_TYPE13)
+               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)
@@ -1476,6 +1525,14 @@ static void hdmiphy_conf_apply(struct hdmi_context 
*hdata)
                DRM_ERROR("failed to read hdmiphy config\n");
                return;
        }
+       usleep_range(10000, 12000);
+
+       ret = hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+                                               HDMI_PHY_DISABLE_MODE_SET);
+       if (ret < 0) {
+               DRM_ERROR("failed to enable hdmiphy\n");
+               return;
+       }

        for (i = 0; i < ret; i++)
                DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
@@ -1767,10 +1824,10 @@ static void hdmi_poweron(struct exynos_drm_display 
*display)
        if (regulator_bulk_enable(res->regul_count, res->regul_bulk))
                DRM_DEBUG_KMS("failed to enable regulator bulk\n");

-       clk_prepare_enable(res->hdmiphy);
-       clk_prepare_enable(res->hdmi);
-       clk_prepare_enable(res->sclk_hdmi);
+       hdmi_phy_pow_ctrl_reg_writemask(hdata, PMU_HDMI_PHY_ENABLE,
+               PMU_HDMI_PHY_CONTROL_MASK);

+       /* HDMI PHY Enable and Power-On */
        hdmiphy_poweron(hdata);
        hdmi_commit(display);
 }
@@ -1790,11 +1847,16 @@ static void hdmi_poweroff(struct exynos_drm_display 
*display)
         * its reset state seems to meet the condition.
         */
        hdmiphy_conf_reset(hdata);
+
+       /* HDMI System Disable */
+       hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
+
        hdmiphy_poweroff(hdata);

-       clk_disable_unprepare(res->sclk_hdmi);
-       clk_disable_unprepare(res->hdmi);
-       clk_disable_unprepare(res->hdmiphy);
+       /* HDMI PHY Disable */
+       hdmi_phy_pow_ctrl_reg_writemask(hdata, PMU_HDMI_PHY_DISABLE,
+               PMU_HDMI_PHY_CONTROL_MASK);
+
        regulator_bulk_disable(res->regul_count, res->regul_bulk);

        pm_runtime_put_sync(hdata->dev);
@@ -1947,6 +2009,36 @@ err_data:
        return NULL;
 }

+static int drm_hdmi_dt_parse_phy_pow_control(struct hdmi_context *hdata)
+{
+       struct device_node *phy_pow_ctrl_node;
+       u32 buf[2];
+       int ret = 0;
+
+       phy_pow_ctrl_node = of_find_node_by_name(NULL, "phy-power-control");
+       if (!phy_pow_ctrl_node)
+               return 0;
+
+       /* reg property holds two informations: addr of pmu register, size */
+       if (of_property_read_u32_array(phy_pow_ctrl_node, "reg",
+                       (u32 *)&buf, 2)) {
+               DRM_ERROR("faild to get phy power control reg\n");
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       hdata->phy_pow_ctrl_reg = devm_ioremap(hdata->dev, buf[0],  buf[1]);
+       if (!hdata->phy_pow_ctrl_reg) {
+               DRM_ERROR("failed to ioremap phy pmu reg\n");
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+fail:
+       of_node_put(phy_pow_ctrl_node);
+       return ret;
+}
+
 static struct of_device_id hdmi_match_types[] = {
        {
                .compatible = "samsung,exynos5-hdmi",
@@ -2010,6 +2102,13 @@ static int hdmi_bind(struct device *dev, struct device 
*master, void *data)
                return ret;
        }

+       /* map hdmiphy power control reg */
+       ret = drm_hdmi_dt_parse_phy_pow_control(hdata);
+       if (ret) {
+               DRM_ERROR("failed to map phy power control registers\n");
+               return ret;
+       }
+
        /* DDC i2c driver */
        ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
        if (!ddc_node) {
diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h 
b/drivers/gpu/drm/exynos/regs-hdmi.h
old mode 100644
new mode 100755
index ef1b3eb..e686fe9
--- a/drivers/gpu/drm/exynos/regs-hdmi.h
+++ b/drivers/gpu/drm/exynos/regs-hdmi.h
@@ -578,4 +578,19 @@
 #define HDMI_TG_VACT_ST4_H             HDMI_TG_BASE(0x0074)
 #define HDMI_TG_3D                     HDMI_TG_BASE(0x00F0)

+/* HDMI PHY Registers Offsets*/
+
+#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
+#define HDMI_PHY_DISABLE_MODE_SET      0x80
+#define HDMI_PHY_ENABLE_MODE_SET       0x00
+
+#define PMU_HDMI_PHY_CONTROL_MASK      (1 << 0)
+#define PMU_HDMI_PHY_ENABLE            (1)
+#define PMU_HDMI_PHY_DISABLE           (0)
+
 #endif /* SAMSUNG_REGS_HDMI_H */
-- 
1.7.10.4

Reply via email to