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

The HDMI driver in the R-Car Gen3 uses dw_hdmi driver.

Signed-off-by: Koji Matsuoka <koji.matsuoka.xm at renesas.com>
[geert: Select DRM_DW_HDMI on non-r8a7795 to fix shmobile_defconfig build]
Signed-off-by: Geert Uytterhoeven <geert+renesas at glider.be>
---
 drivers/gpu/drm/rcar-du/Kconfig           |   2 +
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c |   9 +-
 drivers/gpu/drm/rcar-du/rcar_du_encoder.h |   6 +-
 drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c | 225 +++++++++++++++++++++++++++---
 drivers/gpu/drm/rcar-du/rcar_du_kms.c     |   6 +-
 5 files changed, 227 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index 7fc3ca5..d7c7ffa 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -15,6 +15,8 @@ config DRM_RCAR_DU
 config DRM_RCAR_HDMI
        bool "R-Car DU HDMI Encoder Support"
        depends on DRM_RCAR_DU
+       depends on OF
+       select DRM_DW_HDMI
        help
          Enable support for external HDMI encoders.

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c 
b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index 1b16297..5d64893 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -1,7 +1,7 @@
 /*
  * rcar_du_encoder.c  --  R-Car Display Unit Encoder
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart at ideasonboard.com)
  *
@@ -118,7 +118,8 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
                         enum rcar_du_encoder_type type,
                         enum rcar_du_output output,
                         struct device_node *enc_node,
-                        struct device_node *con_node)
+                        struct device_node *con_node,
+                        const char *device_name)
 {
        struct rcar_du_encoder *renc;
        struct drm_encoder *encoder;
@@ -162,8 +163,12 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
                break;
        }

+       renc->device_name = device_name;
+
        if (type == RCAR_DU_ENCODER_HDMI) {
                ret = rcar_du_hdmienc_init(rcdu, renc, enc_node);
+               if (of_device_is_compatible(enc_node, "rockchip,rcar-dw-hdmi"))
+                       goto done;
                if (ret < 0)
                        goto done;
        } else {
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h 
b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
index dde523a..51a4664 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_encoder.h  --  R-Car Display Unit Encoder
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart at ideasonboard.com)
  *
@@ -33,6 +33,7 @@ struct rcar_du_encoder {
        enum rcar_du_output output;
        struct rcar_du_hdmienc *hdmi;
        struct rcar_du_lvdsenc *lvds;
+       const char *device_name;
 };

 #define to_rcar_encoder(e) \
@@ -55,6 +56,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
                         enum rcar_du_encoder_type type,
                         enum rcar_du_output output,
                         struct device_node *enc_node,
-                        struct device_node *con_node);
+                        struct device_node *con_node,
+                        const char *device_name);

 #endif /* __RCAR_DU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c 
b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
index 15d553a..b8b89a0 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
@@ -1,7 +1,7 @@
 /*
  * R-Car Display Unit HDMI Encoder
  *
- * Copyright (C) 2014 Renesas Electronics Corporation
+ * Copyright (C) 2014-2015 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart at ideasonboard.com)
  *
@@ -13,10 +13,13 @@

 #include <linux/slab.h>

+#include <drm/bridge/dw_hdmi.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>

+#include <linux/of_platform.h>
+
 #include "rcar_du_drv.h"
 #include "rcar_du_encoder.h"
 #include "rcar_du_hdmienc.h"
@@ -24,7 +27,9 @@

 struct rcar_du_hdmienc {
        struct rcar_du_encoder *renc;
+       struct device *dev;
        bool enabled;
+       unsigned int index;
 };

 #define to_rcar_hdmienc(e)     (to_rcar_encoder(e)->hdmi)
@@ -32,6 +37,10 @@ struct rcar_du_hdmienc {
 static void rcar_du_hdmienc_disable(struct drm_encoder *encoder)
 {
        struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+       const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs;
+
+       if ((bfuncs) && (bfuncs->post_disable))
+               bfuncs->post_disable(encoder->bridge);

        if (hdmienc->renc->lvds)
                rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
@@ -43,10 +52,13 @@ static void rcar_du_hdmienc_disable(struct drm_encoder 
*encoder)
 static void rcar_du_hdmienc_enable(struct drm_encoder *encoder)
 {
        struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+       const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs;

        if (hdmienc->renc->lvds)
                rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
                                       true);
+       if ((bfuncs) && (bfuncs->enable))
+               bfuncs->enable(encoder->bridge);

        hdmienc->enabled = true;
 }
@@ -56,13 +68,19 @@ static int rcar_du_hdmienc_atomic_check(struct drm_encoder 
*encoder,
                                        struct drm_connector_state *conn_state)
 {
        struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+       const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs;
        struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+       const struct drm_display_mode *mode = &crtc_state->mode;
+       int ret = 0;

        if (hdmienc->renc->lvds)
                rcar_du_lvdsenc_atomic_check(hdmienc->renc->lvds,
                                             adjusted_mode);

-       return 0;
+       if ((bfuncs) && (bfuncs->mode_fixup))
+               ret = bfuncs->mode_fixup(encoder->bridge, mode,
+                                adjusted_mode) ? 0 : -EINVAL;
+       return ret;
 }

 static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder,
@@ -70,6 +88,10 @@ static void rcar_du_hdmienc_mode_set(struct drm_encoder 
*encoder,
                                     struct drm_display_mode *adjusted_mode)
 {
        struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+       const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs;
+
+       if ((bfuncs) && (bfuncs->mode_set))
+               bfuncs->mode_set(encoder->bridge, mode, adjusted_mode);

        rcar_du_crtc_route_output(encoder->crtc, hdmienc->renc->output);
 }
@@ -95,24 +117,187 @@ static const struct drm_encoder_funcs encoder_funcs = {
        .destroy = rcar_du_hdmienc_cleanup,
 };

+static const struct dw_hdmi_mpll_config rcar_du_hdmienc_mpll_cfg[] = {
+       {
+               44900000, {
+                       { 0x0003, 0x0000},
+                       { 0x0003, 0x0000},
+                       { 0x0003, 0x0000}
+               },
+       }, {
+               90000000, {
+                       { 0x0002, 0x0000},
+                       { 0x0002, 0x0000},
+                       { 0x0002, 0x0000}
+               },
+       }, {
+               182750000, {
+                       { 0x0001, 0x0000},
+                       { 0x0001, 0x0000},
+                       { 0x0001, 0x0000}
+               },
+       }, {
+               297000000, {
+                       { 0x0000, 0x0000},
+                       { 0x0000, 0x0000},
+                       { 0x0000, 0x0000}
+               },
+       }, {
+               ~0UL, {
+                       { 0xFFFF, 0xFFFF },
+                       { 0xFFFF, 0xFFFF },
+                       { 0xFFFF, 0xFFFF },
+               },
+       }
+};
+static const struct dw_hdmi_curr_ctrl rcar_du_hdmienc_cur_ctr[] = {
+       /*      pixelclk    bpp8    bpp10   bpp12 */
+       {
+               35500000,  { 0x0344, 0x0000, 0x0000 },
+       }, {
+               44900000,  { 0x0285, 0x0000, 0x0000 },
+       }, {
+               71000000,  { 0x1184, 0x0000, 0x0000 },
+       }, {
+               90000000,  { 0x1144, 0x0000, 0x0000 },
+       }, {
+               140250000, { 0x20c4, 0x0000, 0x0000 },
+       }, {
+               182750000, { 0x2084, 0x0000, 0x0000 },
+       }, {
+               297000000, { 0x0084, 0x0000, 0x0000 },
+       }, {
+               ~0UL,      { 0x0000, 0x0000, 0x0000 },
+       }
+};
+
+static const struct dw_hdmi_multi_div rcar_du_hdmienc_multi_div[] = {
+       /*      pixelclk    bpp8    bpp10   bpp12 */
+       {
+               35500000,  { 0x0328, 0x0000, 0x0000 },
+       }, {
+               44900000,  { 0x0128, 0x0000, 0x0000 },
+       }, {
+               71000000,  { 0x0314, 0x0000, 0x0000 },
+       }, {
+               90000000,  { 0x0114, 0x0000, 0x0000 },
+       }, {
+               140250000, { 0x030a, 0x0000, 0x0000 },
+       }, {
+               182750000, { 0x010a, 0x0000, 0x0000 },
+       }, {
+               281250000, { 0x0305, 0x0000, 0x0000 },
+       }, {
+               297000000, { 0x0105, 0x0000, 0x0000 },
+       }, {
+               ~0UL,      { 0x0000, 0x0000, 0x0000 },
+       }
+};
+
+static const struct dw_hdmi_phy_config rcar_du_hdmienc_phy_config[] = {
+       /*pixelclk   symbol   term   vlev*/
+       { 74250000,  0x8009, 0x0004, 0x0272},
+       { 148500000, 0x802b, 0x0004, 0x028d},
+       { 297000000, 0x8039, 0x0005, 0x028d},
+       { ~0UL,      0x0000, 0x0000, 0x0000}
+};
+
+static enum drm_mode_status
+rcar_du_hdmienc_mode_valid(struct drm_connector *connector,
+                           struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static const struct dw_hdmi_plat_data rcar_du_hdmienc_hdmi0_drv_data = {
+       .mode_valid = rcar_du_hdmienc_mode_valid,
+       .mpll_cfg   = rcar_du_hdmienc_mpll_cfg,
+       .cur_ctr    = rcar_du_hdmienc_cur_ctr,
+       .multi_div  = rcar_du_hdmienc_multi_div,
+       .phy_config = rcar_du_hdmienc_phy_config,
+       .dev_type   = RCAR_HDMI,
+       .index      = 0,
+};
+
+static const struct dw_hdmi_plat_data rcar_du_hdmienc_hdmi1_drv_data = {
+       .mode_valid = rcar_du_hdmienc_mode_valid,
+       .mpll_cfg   = rcar_du_hdmienc_mpll_cfg,
+       .cur_ctr    = rcar_du_hdmienc_cur_ctr,
+       .multi_div  = rcar_du_hdmienc_multi_div,
+       .phy_config = rcar_du_hdmienc_phy_config,
+       .dev_type   = RCAR_HDMI,
+       .index      = 1,
+};
+
+static const struct of_device_id rcar_du_hdmienc_dt_ids[] = {
+       {
+               .data = &rcar_du_hdmienc_hdmi0_drv_data
+       },
+       {
+               .data = &rcar_du_hdmienc_hdmi1_drv_data
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rcar_du_hdmienc_dt_ids);
+
 int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
                         struct rcar_du_encoder *renc, struct device_node *np)
 {
        struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
-       struct drm_bridge *bridge;
        struct rcar_du_hdmienc *hdmienc;
-       int ret;
+       struct resource *iores;
+       struct platform_device *pdev;
+       const struct dw_hdmi_plat_data *plat_data;
+       int ret, irq;
+       bool dw_hdmi_use = false;
+       struct drm_bridge *bridge = NULL;

        hdmienc = devm_kzalloc(rcdu->dev, sizeof(*hdmienc), GFP_KERNEL);
        if (hdmienc == NULL)
                return -ENOMEM;

-       /* Locate the DRM bridge from the HDMI encoder DT node. */
-       bridge = of_drm_find_bridge(np);
-       if (!bridge) {
-               dev_dbg(rcdu->dev, "can't get bridge for %s, deferring probe\n",
-                       of_node_full_name(np));
-               return -EPROBE_DEFER;
+       if (strcmp(renc->device_name, "rockchip,rcar-dw-hdmi") == 0)
+               dw_hdmi_use = true;
+
+       if (dw_hdmi_use) {
+               if (renc->output == RCAR_DU_OUTPUT_HDMI0)
+                       hdmienc->index = 0;
+               else if (renc->output == RCAR_DU_OUTPUT_HDMI1)
+                       hdmienc->index = 1;
+               else
+                       return -EINVAL;
+
+               np = of_parse_phandle(rcdu->dev->of_node, "hdmi",
+                                                hdmienc->index);
+               if (!np) {
+                       dev_err(rcdu->dev, "hdmi node not found\n");
+                       return -ENXIO;
+               }
+
+               pdev = of_find_device_by_node(np);
+               of_node_put(np);
+               if (!pdev)
+                       return -ENXIO;
+
+               plat_data = rcar_du_hdmienc_dt_ids[hdmienc->index].data;
+               hdmienc->dev = &pdev->dev;
+
+               irq = platform_get_irq(pdev, 0);
+               if (irq < 0)
+                       return irq;
+
+               iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               if (!iores)
+                       return -ENXIO;
+
+       } else {
+               /* Locate the DRM bridge from the HDMI encoder DT node. */
+               bridge = of_drm_find_bridge(np);
+               if (!bridge) {
+                       dev_dbg(rcdu->dev, "can't get bridge for %s, deferring 
probe\n",
+                               of_node_full_name(np));
+                       return -EPROBE_DEFER;
+               }
        }

        ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
@@ -126,13 +311,21 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
        hdmienc->renc = renc;

        /* Link the bridge to the encoder. */
-       bridge->encoder = encoder;
-       encoder->bridge = bridge;
+       if (bridge) {
+               bridge->encoder = encoder;
+               encoder->bridge = bridge;
+       }

-       ret = drm_bridge_attach(rcdu->ddev, bridge);
-       if (ret) {
-               drm_encoder_cleanup(encoder);
-               return ret;
+       if (dw_hdmi_use)
+               ret = dw_hdmi_bind(rcdu->dev, NULL, rcdu->ddev, encoder,
+                               iores, irq, plat_data);
+
+       if (bridge) {
+               ret = drm_bridge_attach(rcdu->ddev, bridge);
+               if (ret) {
+                       drm_encoder_cleanup(encoder);
+                       return ret;
+               }
        }

        return 0;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c 
b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index e70a4f3..baac8c9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -362,6 +362,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device 
*rcdu,
        } encoders[] = {
                { "adi,adv7123", RCAR_DU_ENCODER_VGA },
                { "adi,adv7511w", RCAR_DU_ENCODER_HDMI },
+               { "rockchip,rcar-dw-hdmi", RCAR_DU_ENCODER_HDMI },
                { "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS },
        };

@@ -372,6 +373,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device 
*rcdu,
        struct device_node *entity_ep_node;
        struct device_node *entity;
        int ret;
+       const char *enc_name = NULL;

        /*
         * Locate the connected entity and infer its type from the number of
@@ -423,6 +425,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device 
*rcdu,
                        if (of_device_is_compatible(encoder,
                                                    encoders[i].compatible)) {
                                enc_type = encoders[i].type;
+                               enc_name = encoders[i].compatible;
                                break;
                        }
                }
@@ -443,7 +446,8 @@ static int rcar_du_encoders_init_one(struct rcar_du_device 
*rcdu,
                connector = entity;
        }

-       ret = rcar_du_encoder_init(rcdu, enc_type, output, encoder, connector);
+       ret = rcar_du_encoder_init(rcdu, enc_type, output, encoder, connector,
+                                       enc_name);
        of_node_put(encoder);
        of_node_put(connector);

-- 
2.7.4

Reply via email to