Modify the exynos HDMI driver to support Exynos7 HDMI 1.4.
* Add phy configs for Exynos7.
* Exynos7 has a different clock structure for HDMI,
  so introduce the new clocks.
* Add sysreg support to enable HDMI SYSREG on Exynos7.
* Exynos7 based boards need a DCDC_EN and LS_EN pins
  for powering up HDMI. Add support for that too.

Signed-off-by: Ajay Kumar <ajaykumar...@samsung.com>
---
 .../devicetree/bindings/video/exynos_hdmi.txt      |   21 +-
 drivers/gpu/drm/exynos/exynos_hdmi.c               |  252 ++++++++++++++++----
 drivers/gpu/drm/exynos/regs-hdmi.h                 |    4 +
 3 files changed, 231 insertions(+), 46 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/exynos_hdmi.txt 
b/Documentation/devicetree/bindings/video/exynos_hdmi.txt
index 1fd8cf9..bb22a60 100644
--- a/Documentation/devicetree/bindings/video/exynos_hdmi.txt
+++ b/Documentation/devicetree/bindings/video/exynos_hdmi.txt
@@ -6,6 +6,7 @@ Required properties:
        2) "samsung,exynos4210-hdmi"
        3) "samsung,exynos4212-hdmi"
        4) "samsung,exynos5420-hdmi"
+       5) "samsung,exynos7-hdmi"
 - reg: physical base address of the hdmi and length of memory mapped
        region.
 - interrupts: interrupt number to the cpu.
@@ -15,21 +16,33 @@ Required properties:
        c) optional flags and pull up/down.
 - clocks: list of clock IDs from SoC clock driver.
        a) hdmi: Gate of HDMI IP bus clock.
-       b) sclk_hdmi: Gate of HDMI special clock.
-       c) sclk_pixel: Pixel special clock, one of the two possible inputs of
+       HDMI clocks necessary for non exynos7:
+        b) sclk_hdmi: Gate of HDMI special clock.
+        c) sclk_pixel: Pixel special clock, one of the two possible inputs of
                HDMI clock mux.
-       d) sclk_hdmiphy: HDMI PHY clock output, one of two possible inputs of
+        d) sclk_hdmiphy: HDMI PHY clock output, one of two possible inputs of
                HDMI clock mux.
-       e) mout_hdmi: It is required by the driver to switch between the 2
+        e) mout_hdmi: It is required by the driver to switch between the 2
                parents i.e. sclk_pixel and sclk_hdmiphy. If hdmiphy is stable
                after configuration, parent is set to sclk_hdmiphy else
                sclk_pixel.
+       HDMI clocks necessary for Exynos7:
+        b) pclk_hdmiphy: Gate to HDMIPHY clock.
+        c) hdmi_pixel: Gate clock of MUX output for I_PIXEL_CLK.
+        d) hdmi_tmds: Gate clock of MUX output for I_TMDS_CLK.
 - clock-names: aliases as per driver requirements for above clock IDs:
        "hdmi", "sclk_hdmi", "sclk_pixel", "sclk_hdmiphy" and "mout_hdmi".
 - ddc: phandle to the hdmi ddc node
 - phy: phandle to the hdmi phy node
 - samsung,syscon-phandle: phandle for system controller node for PMU.
 
+Only for Exynos7(when compatible = "samsung,exynos7-hdmi"):
+- samsung,sysreg-phandle: phandle for system controller node for SYSREG block.
+
+Optional properties:
+- dcdc-gpios: OF device-tree gpio specification for HDMI_DCDC_EN pin.
+- lsen-gpios: OF device-tree gpio specification for HDMI_LS_EN pin.
+
 Example:
 
        hdmi {
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c 
b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 229b361..1b579ea 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -67,6 +67,7 @@
 enum hdmi_type {
        HDMI_TYPE13,
        HDMI_TYPE14,
+       HDMI_TYPE14_7,
 };
 
 struct hdmi_driver_data {
@@ -82,6 +83,9 @@ struct hdmi_resources {
        struct clk                      *sclk_pixel;
        struct clk                      *sclk_hdmiphy;
        struct clk                      *mout_hdmi;
+       struct clk                      *hdmi_pixel;
+       struct clk                      *pclk_hdmiphy;
+       struct clk                      *hdmi_tmds;
        struct regulator_bulk_data      *regul_bulk;
        struct regulator                *reg_hdmi_en;
        int                             regul_count;
@@ -210,6 +214,7 @@ struct hdmi_context {
        unsigned int                    phy_conf_count;
 
        struct regmap                   *pmureg;
+       struct regmap                   *sysreg;
        enum hdmi_type                  type;
 };
 
@@ -584,6 +589,61 @@ static const struct hdmiphy_config hdmiphy_5420_configs[] 
= {
        },
 };
 
+static const struct hdmiphy_config hdmiphy_7_configs[] = {
+       {
+               .pixel_clock = 27000000,
+               .conf = {
+                       0x01, 0xD2, 0x87, 0xB0, 0x01, 0x00, 0x88, 0x02,
+                       0x4F, 0x30, 0x33, 0x65, 0x00, 0xE4, 0x24, 0x80,
+                       0x6C, 0xF2, 0x67, 0x00, 0x10, 0x0B, 0x00, 0x10,
+                       0x60, 0x0F, 0x00, 0x00, 0x08, 0x00, 0x3E, 0x00,
+               },
+       },
+       {
+               .pixel_clock = 27027000,
+               .conf = {
+                       0x01, 0xD1, 0x5A, 0xFA, 0xE4, 0x12, 0x88, 0x43,
+                       0x4F, 0x30, 0x33, 0x65, 0x00, 0xE4, 0x24, 0x80,
+                       0x6C, 0xF2, 0x67, 0x00, 0x10, 0x0F, 0x00, 0x10,
+                       0x60, 0x0F, 0x00, 0x00, 0x08, 0x00, 0x3E, 0x00,
+               },
+       },
+       {
+               .pixel_clock = 74176000,
+               .conf = {
+                       0x01, 0xD1, 0x3E, 0x35, 0xDB, 0xA2, 0x88, 0x42,
+                       0x4F, 0x30, 0x33, 0x65, 0x10, 0xA6, 0x24, 0x80,
+                       0x6C, 0xF2, 0x67, 0x00, 0x10, 0x03, 0x00, 0x10,
+                       0x60, 0x0F, 0x00, 0x00, 0x08, 0x00, 0x3E, 0x00,
+               },
+       },
+       {
+               .pixel_clock = 74250000,
+               .conf = {
+                       0x01, 0xD1, 0x3E, 0x35, 0xC0, 0x90, 0x88, 0x42,
+                       0x4F, 0x30, 0x33, 0x65, 0x10, 0xA6, 0x24, 0x80,
+                       0x6C, 0xF2, 0x67, 0x00, 0x10, 0x03, 0x00, 0x10,
+                       0x60, 0x0F, 0x00, 0x00, 0x08, 0x00, 0x3E, 0x00,
+               },
+       },
+       {
+               .pixel_clock = 148500000,
+               .conf = {
+                       0x01, 0xD1, 0x3E, 0x15, 0xC0, 0x90, 0x88, 0x42,
+                       0x4F, 0x30, 0x33, 0x65, 0x20, 0xA6, 0x24, 0x80,
+                       0x6C, 0xF2, 0x67, 0x00, 0x10, 0x01, 0x00, 0x10,
+                       0x60, 0x0F, 0x00, 0x00, 0x08, 0x00, 0x3E, 0x00,
+               },
+       },
+};
+
+static struct hdmi_driver_data exynos7_hdmi_driver_data = {
+       .type           = HDMI_TYPE14_7,
+       .phy_confs      = hdmiphy_7_configs,
+       .phy_conf_count = ARRAY_SIZE(hdmiphy_7_configs),
+       .is_apb_phy     = 1,
+};
+
 static struct hdmi_driver_data exynos5420_hdmi_driver_data = {
        .type           = HDMI_TYPE14,
        .phy_confs      = hdmiphy_5420_configs,
@@ -1355,6 +1415,9 @@ static void hdmi_start(struct hdmi_context *hdata, bool 
start)
                val |= HDMI_FIELD_EN;
 
        hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
+       if (hdata->type == HDMI_TYPE14_7)
+               hdmi_reg_writemask(hdata, HDMI_CON_0, HDMI_ENCODING_RETAIN,
+                                                       HDMI_ENCODING_RETAIN);
        hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
 }
 
@@ -1489,9 +1552,11 @@ static void hdmi_v13_mode_apply(struct hdmi_context 
*hdata)
                hdmi_regs_dump(hdata, "timing apply");
        }
 
-       clk_disable_unprepare(hdata->res.sclk_hdmi);
-       clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_hdmiphy);
-       clk_prepare_enable(hdata->res.sclk_hdmi);
+       if (hdata->type != HDMI_TYPE14_7) {
+               clk_disable_unprepare(hdata->res.sclk_hdmi);
+               clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_hdmiphy);
+               clk_prepare_enable(hdata->res.sclk_hdmi);
+       }
 
        /* enable HDMI and timing generator */
        hdmi_start(hdata, true);
@@ -1651,9 +1716,11 @@ static void hdmi_v14_mode_apply(struct hdmi_context 
*hdata)
                hdmi_regs_dump(hdata, "timing apply");
        }
 
-       clk_disable_unprepare(hdata->res.sclk_hdmi);
-       clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_hdmiphy);
-       clk_prepare_enable(hdata->res.sclk_hdmi);
+       if (hdata->type != HDMI_TYPE14_7) {
+               clk_disable_unprepare(hdata->res.sclk_hdmi);
+               clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_hdmiphy);
+               clk_prepare_enable(hdata->res.sclk_hdmi);
+       }
 
        /* enable HDMI and timing generator */
        hdmi_start(hdata, true);
@@ -1671,9 +1738,11 @@ static void hdmiphy_conf_reset(struct hdmi_context 
*hdata)
 {
        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);
+       if (hdata->type != HDMI_TYPE14_7) {
+               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 */
        hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
@@ -1693,7 +1762,7 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
 
 static void hdmiphy_poweron(struct hdmi_context *hdata)
 {
-       if (hdata->type != HDMI_TYPE14)
+       if ((hdata->type != HDMI_TYPE14) || (hdata->type != HDMI_TYPE14_7))
                return;
 
        DRM_DEBUG_KMS("\n");
@@ -1713,7 +1782,7 @@ static void hdmiphy_poweron(struct hdmi_context *hdata)
 
 static void hdmiphy_poweroff(struct hdmi_context *hdata)
 {
-       if (hdata->type != HDMI_TYPE14)
+       if ((hdata->type != HDMI_TYPE14) || (hdata->type != HDMI_TYPE14_7))
                return;
 
        DRM_DEBUG_KMS("\n");
@@ -2042,6 +2111,10 @@ static void hdmi_poweron(struct hdmi_context *hdata)
                return;
        }
 
+       if (hdata->type == HDMI_TYPE14_7)
+               regmap_update_bits(hdata->sysreg, SYSREG_DISP_HDMI_PHY,
+                                       SYSREG_HDMI_REFCLK_SEL, 1);
+
        hdata->powered = true;
 
        mutex_unlock(&hdata->hdmi_mutex);
@@ -2056,7 +2129,19 @@ static void hdmi_poweron(struct hdmi_context *hdata)
                        PMU_HDMI_PHY_ENABLE_BIT, 1);
 
        clk_prepare_enable(res->hdmi);
-       clk_prepare_enable(res->sclk_hdmi);
+       if (hdata->type != HDMI_TYPE14_7)
+               clk_prepare_enable(res->sclk_hdmi);
+       else
+               clk_prepare_enable(res->pclk_hdmiphy);
+
+       if (hdata->type == HDMI_TYPE14_7)
+               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
+                                       HDMI_PHY_POWER_OFF_EN);
+
+       if (hdata->type == HDMI_TYPE14_7) {
+               clk_prepare_enable(res->hdmi_pixel);
+               clk_prepare_enable(res->hdmi_tmds);
+       }
 
        hdmiphy_poweron(hdata);
        hdmi_commit(&hdata->display);
@@ -2074,13 +2159,29 @@ static void hdmi_poweroff(struct hdmi_context *hdata)
        /* HDMI System Disable */
        hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
 
+       if (hdata->type == HDMI_TYPE14_7) {
+               clk_disable_unprepare(res->hdmi_pixel);
+               clk_disable_unprepare(res->hdmi_tmds);
+       }
+
+       if (hdata->type == HDMI_TYPE14_7)
+               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
+                                       HDMI_PHY_POWER_OFF_EN);
+
        hdmiphy_poweroff(hdata);
 
        cancel_delayed_work(&hdata->hotplug_work);
 
-       clk_disable_unprepare(res->sclk_hdmi);
+       if (hdata->type != HDMI_TYPE14_7)
+               clk_disable_unprepare(res->sclk_hdmi);
+       else
+               clk_disable_unprepare(res->pclk_hdmiphy);
        clk_disable_unprepare(res->hdmi);
 
+       if (hdata->type == HDMI_TYPE14_7)
+               regmap_update_bits(hdata->sysreg, SYSREG_DISP_HDMI_PHY,
+                                       SYSREG_HDMI_REFCLK_SEL, 0);
+
        /* reset pmu hdmiphy control bit to disable hdmiphy */
        regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
                        PMU_HDMI_PHY_ENABLE_BIT, 0);
@@ -2121,10 +2222,12 @@ static void hdmi_dpms(struct exynos_drm_display 
*display, int mode)
                 * Below codes will try to disable Mixer and VP(if used)
                 * prior to disabling HDMI.
                 */
-               if (crtc)
-                       funcs = crtc->helper_private;
-               if (funcs && funcs->dpms)
-                       (*funcs->dpms)(crtc, mode);
+               if (hdata->type != HDMI_TYPE14_7) {
+                       if (crtc)
+                               funcs = crtc->helper_private;
+                       if (funcs && funcs->dpms)
+                               (*funcs->dpms)(crtc, mode);
+               }
 
                hdmi_poweroff(hdata);
                break;
@@ -2166,6 +2269,31 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
        return IRQ_HANDLED;
 }
 
+static int hdmi_configure_gpios(struct hdmi_context *hdata)
+{
+       struct gpio_desc *dcdc_gpio, *lsen_gpio;
+       int err;
+
+       dcdc_gpio = devm_gpiod_get_optional(hdata->dev, "dcdc", GPIOD_OUT_LOW);
+       if (IS_ERR(dcdc_gpio)) {
+               err = PTR_ERR(dcdc_gpio);
+               dev_err(hdata->dev, "failed to request GPIO: %d\n", err);
+               return err;
+       }
+
+       lsen_gpio = devm_gpiod_get_optional(hdata->dev, "lsen", GPIOD_OUT_LOW);
+       if (IS_ERR(lsen_gpio)) {
+               err = PTR_ERR(lsen_gpio);
+               dev_err(hdata->dev, "failed to request GPIO: %d\n", err);
+               return err;
+       }
+
+       gpiod_set_value(dcdc_gpio, 1);
+       gpiod_set_value(lsen_gpio, 1);
+
+       return 0;
+}
+
 static int hdmi_resources_init(struct hdmi_context *hdata)
 {
        struct device *dev = hdata->dev;
@@ -2179,6 +2307,10 @@ static int hdmi_resources_init(struct hdmi_context 
*hdata)
 
        DRM_DEBUG_KMS("HDMI resource init\n");
 
+       ret = hdmi_configure_gpios(hdata);
+       if (ret)
+               goto fail;
+
        /* get clocks, power */
        res->hdmi = devm_clk_get(dev, "hdmi");
        if (IS_ERR(res->hdmi)) {
@@ -2186,32 +2318,55 @@ static int hdmi_resources_init(struct hdmi_context 
*hdata)
                ret = PTR_ERR(res->hdmi);
                goto fail;
        }
-       res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
-       if (IS_ERR(res->sclk_hdmi)) {
-               DRM_ERROR("failed to get clock 'sclk_hdmi'\n");
-               ret = PTR_ERR(res->sclk_hdmi);
-               goto fail;
-       }
-       res->sclk_pixel = devm_clk_get(dev, "sclk_pixel");
-       if (IS_ERR(res->sclk_pixel)) {
-               DRM_ERROR("failed to get clock 'sclk_pixel'\n");
-               ret = PTR_ERR(res->sclk_pixel);
-               goto fail;
-       }
-       res->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy");
-       if (IS_ERR(res->sclk_hdmiphy)) {
-               DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n");
-               ret = PTR_ERR(res->sclk_hdmiphy);
-               goto fail;
-       }
-       res->mout_hdmi = devm_clk_get(dev, "mout_hdmi");
-       if (IS_ERR(res->mout_hdmi)) {
-               DRM_ERROR("failed to get clock 'mout_hdmi'\n");
-               ret = PTR_ERR(res->mout_hdmi);
-               goto fail;
-       }
 
-       clk_set_parent(res->mout_hdmi, res->sclk_pixel);
+       if (hdata->type != HDMI_TYPE14_7) {
+               res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
+               if (IS_ERR(res->sclk_hdmi)) {
+                       DRM_ERROR("failed to get clock 'sclk_hdmi'\n");
+                       ret = PTR_ERR(res->sclk_hdmi);
+                       goto fail;
+               }
+               res->sclk_pixel = devm_clk_get(dev, "sclk_pixel");
+               if (IS_ERR(res->sclk_pixel)) {
+                       DRM_ERROR("failed to get clock 'sclk_pixel'\n");
+                       ret = PTR_ERR(res->sclk_pixel);
+                       goto fail;
+               }
+               res->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy");
+               if (IS_ERR(res->sclk_hdmiphy)) {
+                       DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n");
+                       ret = PTR_ERR(res->sclk_hdmiphy);
+                       goto fail;
+               }
+               res->mout_hdmi = devm_clk_get(dev, "mout_hdmi");
+               if (IS_ERR(res->mout_hdmi)) {
+                       DRM_ERROR("failed to get clock 'mout_hdmi'\n");
+                       ret = PTR_ERR(res->mout_hdmi);
+                       goto fail;
+               }
+
+               clk_set_parent(res->mout_hdmi, res->sclk_pixel);
+       } else {
+
+               res->pclk_hdmiphy = clk_get(dev, "pclk_hdmiphy");
+               if (IS_ERR_OR_NULL(res->pclk_hdmiphy)) {
+                       DRM_ERROR("failed to get clock 'pclk_hdmiphy'\n");
+                       ret = PTR_ERR(res->pclk_hdmiphy);
+                       goto fail;
+               }
+               res->hdmi_pixel = clk_get(dev, "hdmi_pixel");
+               if (IS_ERR_OR_NULL(res->hdmi_pixel)) {
+                       DRM_ERROR("failed to get clock 'hdmi_pixel'\n");
+                       ret = PTR_ERR(res->hdmi_pixel);
+                       goto fail;
+               }
+               res->hdmi_tmds = clk_get(dev, "hdmi_tmds");
+               if (IS_ERR_OR_NULL(res->hdmi_tmds)) {
+                       DRM_ERROR("failed to get clock 'hdmi_tmds'\n");
+                       ret = PTR_ERR(res->hdmi_tmds);
+                       goto fail;
+               }
+       }
 
        res->regul_bulk = devm_kzalloc(dev, ARRAY_SIZE(supply) *
                sizeof(res->regul_bulk[0]), GFP_KERNEL);
@@ -2288,6 +2443,9 @@ static struct of_device_id hdmi_match_types[] = {
                .compatible = "samsung,exynos5420-hdmi",
                .data = &exynos5420_hdmi_driver_data,
        }, {
+               .compatible = "samsung,exynos7-hdmi",
+               .data = &exynos7_hdmi_driver_data,
+       }, {
                /* end node */
        }
 };
@@ -2474,6 +2632,16 @@ out_get_phy_port:
                goto err_hdmiphy;
        }
 
+       if (hdata->type == HDMI_TYPE14_7) {
+               hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
+                       "samsung,sysreg-phandle");
+               if (IS_ERR(hdata->sysreg)) {
+                       DRM_ERROR("sysreg regmap lookup failed.\n");
+                       ret = -EPROBE_DEFER;
+                       goto err_hdmiphy;
+               }
+       }
+
        pm_runtime_enable(dev);
 
        ret = component_add(&pdev->dev, &hdmi_component_ops);
diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h 
b/drivers/gpu/drm/exynos/regs-hdmi.h
index 3f35ac6..d6c84e0 100644
--- a/drivers/gpu/drm/exynos/regs-hdmi.h
+++ b/drivers/gpu/drm/exynos/regs-hdmi.h
@@ -133,6 +133,7 @@
 
 /* HDMI_CON_0 */
 #define HDMI_BLUE_SCR_EN               (1 << 5)
+#define HDMI_ENCODING_RETAIN           (1 << 4)
 #define HDMI_ASP_EN                    (1 << 2)
 #define HDMI_ASP_DIS                   (0 << 2)
 #define HDMI_ASP_MASK                  (1 << 2)
@@ -594,4 +595,7 @@
 #define PMU_HDMI_PHY_CONTROL           0x700
 #define PMU_HDMI_PHY_ENABLE_BIT                BIT(0)
 
+#define SYSREG_DISP_HDMI_PHY           0x838
+#define SYSREG_HDMI_REFCLK_SEL         BIT(0)
+
 #endif /* SAMSUNG_REGS_HDMI_H */
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to