From: Koji Matsuoka <koji.matsuoka...@renesas.com>

Signed-off-by: Koji Matsuoka <koji.matsuoka...@renesas.com>
Signed-off-by: Geert Uytterhoeven <geert+rene...@glider.be>
---
 drivers/gpu/drm/bridge/dw-hdmi.c | 158 ++++++++++++++++++++++++++++++---------
 include/drm/bridge/dw_hdmi.h     |   9 +++
 2 files changed, 131 insertions(+), 36 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index 1cfff2f..55e73e8 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -1,6 +1,7 @@
 /*
  * DesignWare High-Definition Multimedia Interface (HDMI) driver
  *
+ * Copyright (C) 2015 Renesas Electronics Corporation
  * Copyright (C) 2013-2015 Mentor Graphics Inc.
  * Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
  * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski at gmx.de>
@@ -21,6 +22,7 @@
 #include <linux/of_device.h>
 #include <linux/spinlock.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_of.h>
 #include <drm/drmP.h>
 #include <drm/drm_atomic_helper.h>
@@ -143,6 +145,7 @@ struct dw_hdmi {
        struct device *dev;
        struct clk *isfr_clk;
        struct clk *iahb_clk;
+
        struct dw_hdmi_i2c *i2c;
 
        struct hdmi_data_info hdmi_data;
@@ -174,6 +177,11 @@ struct dw_hdmi {
        unsigned int audio_cts;
        unsigned int audio_n;
        bool audio_enable;
+       int ratio;
+       bool interlaced;
+       bool isfr_use;
+       bool iahb_use;
+       int num;
 
        void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
        u8 (*read)(struct dw_hdmi *hdmi, int offset);
@@ -1008,6 +1016,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, 
unsigned char prep,
        const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
        const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
        const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
+       const struct dw_hdmi_multi_div *multi_div = pdata->multi_div;
 
        if (prep)
                return -EINVAL;
@@ -1043,6 +1052,13 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, 
unsigned char prep,
                    phy_config->mpixelclock)
                        break;
 
+       if (hdmi->dev_type == RCAR_HDMI) {
+               for (; multi_div->mpixelclock != (~0UL); multi_div++)
+                       if (hdmi->hdmi_data.video_mode.mpixelclock <=
+                           multi_div->mpixelclock)
+                               break;
+       }
+
        if (mpll_config->mpixelclock == ~0UL ||
            curr_ctrl->mpixelclock == ~0UL ||
            phy_config->mpixelclock == ~0UL) {
@@ -1051,6 +1067,13 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, 
unsigned char prep,
                return -EINVAL;
        }
 
+       if ((multi_div->mpixelclock == ~0UL) &&
+               (hdmi->dev_type == RCAR_HDMI)) {
+               dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
+                       hdmi->hdmi_data.video_mode.mpixelclock);
+               return -EINVAL;
+       }
+
        /* Enable csc path */
        if (cscon)
                val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH;
@@ -1077,21 +1100,27 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, 
unsigned char prep,
        hdmi_phy_test_clear(hdmi, 0);
 
        hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].cpce, 0x06);
-       hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].gmp, 0x15);
+       if (hdmi->dev_type != RCAR_HDMI)
+               hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].gmp, 0x15);
 
        /* CURRCTRL */
        hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[res_idx], 0x10);
 
-       hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);  /* PLLPHBYCTRL */
-       hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
-
-       hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19);  /* TXTERM */
-       hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */
-       hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */
+       if (hdmi->dev_type == RCAR_HDMI)
+               hdmi_phy_i2c_write(hdmi, multi_div->multi[res_idx], 0x11);
 
-       /* REMOVE CLK TERM */
-       hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
+       if (hdmi->dev_type != RCAR_HDMI) {
+               hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);  /* PLLPHBYCTRL */
+               hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
 
+               hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19);  /* TXTERM */
+               hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr,
+                                                0x09); /* CKSYMTXCTRL */
+               hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr,
+                                                0x0E); /* VLEVCTRL */
+               /* REMOVE CLK TERM */
+               hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
+       }
        dw_hdmi_phy_enable_powerdown(hdmi, false);
 
        /* toggle TMDS enable */
@@ -1102,7 +1131,8 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, 
unsigned char prep,
        dw_hdmi_phy_gen2_txpwron(hdmi, 1);
        dw_hdmi_phy_gen2_pddq(hdmi, 0);
 
-       if (hdmi->dev_type == RK3288_HDMI)
+       if ((hdmi->dev_type == RK3288_HDMI) ||
+               (hdmi->dev_type == RCAR_HDMI))
                dw_hdmi_phy_enable_spare(hdmi, 1);
 
        /*Wait for PHY PLL lock */
@@ -1791,6 +1821,16 @@ static const struct drm_connector_funcs 
dw_hdmi_atomic_connector_funcs = {
        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
+static struct drm_connector_funcs dw_hdmi_rcar_connector_funcs = {
+       .dpms = drm_atomic_helper_connector_dpms,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .detect = dw_hdmi_connector_detect,
+       .destroy = dw_hdmi_connector_destroy,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
 static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs 
= {
        .get_modes = dw_hdmi_connector_get_modes,
        .mode_valid = dw_hdmi_connector_mode_valid,
@@ -1940,16 +1980,23 @@ static int dw_hdmi_register(struct drm_device *drm, 
struct dw_hdmi *hdmi)
        encoder->bridge = bridge;
        hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
 
+       if (hdmi->interlaced)
+               hdmi->connector.interlace_allowed = true;
+
        drm_connector_helper_add(&hdmi->connector,
                                 &dw_hdmi_connector_helper_funcs);
 
        if (drm_core_check_feature(drm, DRIVER_ATOMIC))
                drm_connector_init(drm, &hdmi->connector,
-                                  &dw_hdmi_atomic_connector_funcs,
+                                  hdmi->dev_type == RCAR_HDMI ?
+                                       &dw_hdmi_rcar_connector_funcs :
+                                       &dw_hdmi_atomic_connector_funcs,
                                   DRM_MODE_CONNECTOR_HDMIA);
        else
                drm_connector_init(drm, &hdmi->connector,
-                                  &dw_hdmi_connector_funcs,
+                                  hdmi->dev_type == RCAR_HDMI ?
+                                       &dw_hdmi_rcar_connector_funcs :
+                                       &dw_hdmi_connector_funcs,
                                   DRM_MODE_CONNECTOR_HDMIA);
 
        drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
@@ -2006,6 +2053,11 @@ int dw_hdmi_bind(struct device *dev, struct device 
*master,
                return -EINVAL;
        }
 
+       if (of_property_read_u32(np, "interlaced", &val) == 0)
+               hdmi->interlaced = val;
+       else
+               hdmi->interlaced = false;
+
        ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
        if (ddc_node) {
                hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
@@ -2023,30 +2075,59 @@ int dw_hdmi_bind(struct device *dev, struct device 
*master,
        if (IS_ERR(hdmi->regs))
                return PTR_ERR(hdmi->regs);
 
-       hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
-       if (IS_ERR(hdmi->isfr_clk)) {
-               ret = PTR_ERR(hdmi->isfr_clk);
-               dev_err(hdmi->dev, "Unable to get HDMI isfr clk: %d\n", ret);
-               return ret;
-       }
+       if (of_property_read_u32(np, "clock-isfr", &val) == 0)
+               hdmi->isfr_use = val;
+       else
+               hdmi->isfr_use = true;
 
-       ret = clk_prepare_enable(hdmi->isfr_clk);
-       if (ret) {
-               dev_err(hdmi->dev, "Cannot enable HDMI isfr clock: %d\n", ret);
-               return ret;
-       }
+       if (hdmi->isfr_use) {
+               if (of_property_read_u32(np, "hdmi-num", &val) == 0)
+                       hdmi->num = val;
+               else
+                       hdmi->num = -1;
+
+               if (hdmi->num > 1) {
+                       char name[7];
+
+                       sprintf(name, "isfr.%u", hdmi->plat_data->index);
+                       hdmi->isfr_clk = devm_clk_get(hdmi->dev, name);
+               } else
+                       hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
+               if (IS_ERR(hdmi->isfr_clk)) {
+                       ret = PTR_ERR(hdmi->isfr_clk);
+                       dev_err(hdmi->dev,
+                                "Unable to get HDMI isfr clk: %d\n", ret);
+                       return ret;
+               }
 
-       hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
-       if (IS_ERR(hdmi->iahb_clk)) {
-               ret = PTR_ERR(hdmi->iahb_clk);
-               dev_err(hdmi->dev, "Unable to get HDMI iahb clk: %d\n", ret);
-               goto err_isfr;
+               ret = clk_prepare_enable(hdmi->isfr_clk);
+               if (ret) {
+                       dev_err(hdmi->dev,
+                                "Cannot enable HDMI isfr clock: %d\n", ret);
+                       return ret;
+               }
        }
 
-       ret = clk_prepare_enable(hdmi->iahb_clk);
-       if (ret) {
-               dev_err(hdmi->dev, "Cannot enable HDMI iahb clock: %d\n", ret);
-               goto err_isfr;
+       if (of_property_read_u32(np, "clock-iahb", &val) == 0)
+               hdmi->iahb_use = val;
+       else
+               hdmi->iahb_use = true;
+
+       if (hdmi->iahb_use) {
+               hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
+               if (IS_ERR(hdmi->iahb_clk)) {
+                       ret = PTR_ERR(hdmi->iahb_clk);
+                       dev_err(hdmi->dev,
+                                "Unable to get HDMI iahb clk: %d\n", ret);
+                       goto err_isfr;
+               }
+
+               ret = clk_prepare_enable(hdmi->iahb_clk);
+               if (ret) {
+                       dev_err(hdmi->dev,
+                                "Cannot enable HDMI iahb clock: %d\n", ret);
+                       goto err_isfr;
+               }
        }
 
        /* Product and revision IDs */
@@ -2130,9 +2211,11 @@ err_iahb:
        if (hdmi->i2c)
                i2c_del_adapter(&hdmi->i2c->adap);
 
-       clk_disable_unprepare(hdmi->iahb_clk);
+       if (hdmi->iahb_use)
+               clk_disable_unprepare(hdmi->iahb_clk);
 err_isfr:
-       clk_disable_unprepare(hdmi->isfr_clk);
+       if (hdmi->isfr_use)
+               clk_disable_unprepare(hdmi->isfr_clk);
 
        return ret;
 }
@@ -2151,8 +2234,11 @@ void dw_hdmi_unbind(struct device *dev, struct device 
*master, void *data)
        hdmi->connector.funcs->destroy(&hdmi->connector);
        hdmi->encoder->funcs->destroy(hdmi->encoder);
 
-       clk_disable_unprepare(hdmi->iahb_clk);
-       clk_disable_unprepare(hdmi->isfr_clk);
+       if (hdmi->iahb_use)
+               clk_disable_unprepare(hdmi->iahb_clk);
+
+       if (hdmi->isfr_use)
+               clk_disable_unprepare(hdmi->isfr_clk);
 
        if (hdmi->i2c)
                i2c_del_adapter(&hdmi->i2c->adap);
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index bae79f3..a620cab 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2015 Renesas Electronics Corporation
  * Copyright (C) 2011 Freescale Semiconductor, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -25,6 +26,7 @@ enum dw_hdmi_devtype {
        IMX6Q_HDMI,
        IMX6DL_HDMI,
        RK3288_HDMI,
+       RCAR_HDMI,
 };
 
 struct dw_hdmi_mpll_config {
@@ -40,6 +42,11 @@ struct dw_hdmi_curr_ctrl {
        u16 curr[DW_HDMI_RES_MAX];
 };
 
+struct dw_hdmi_multi_div {
+       unsigned long mpixelclock;
+       u16 multi[DW_HDMI_RES_MAX];
+};
+
 struct dw_hdmi_phy_config {
        unsigned long mpixelclock;
        u16 sym_ctr;    /*clock symbol and transmitter control*/
@@ -51,9 +58,11 @@ struct dw_hdmi_plat_data {
        enum dw_hdmi_devtype dev_type;
        const struct dw_hdmi_mpll_config *mpll_cfg;
        const struct dw_hdmi_curr_ctrl *cur_ctr;
+       const struct dw_hdmi_multi_div *multi_div;
        const struct dw_hdmi_phy_config *phy_config;
        enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
                                           struct drm_display_mode *mode);
+       u32 index;
 };
 
 void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);
-- 
2.7.4

Reply via email to