linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/4] Add RK3229 HDMI support
@ 2016-01-07  8:53 Yakir Yang
  2016-01-07  8:57 ` [RFC PATCH v2 1/4] drm: dw-hdmi: make it easy to recovery the platform data for platform driver Yakir Yang
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Yakir Yang @ 2016-01-07  8:53 UTC (permalink / raw)
  To: Mark Yao, Heiko Stuebner, Russell King, Philipp Zabel, Andy Yan
  Cc: David Airlie, Rob Herring, Kumar Gala, Zheng Yang, dri-devel,
	devicetree, linux-kernel, linux-rockchip, linux-arm-kernel,
	Yakir Yang


RK3229 have integrated an DesignedWare HDMI controller and an INNO HDMI phy,
so we can still reuse the dw-hdmi driver for RK3229 HDMI controller, but
we need to create an separate driver for RK3229 HDMI PHY.

This series is based on Mark Yao's drm-next branch
[https://github.com/markyzq/kernel-drm-rockchip/tree/drm-rockchip-next-2015-12-28]

After picking my "Add RK3229 vop support" series, HDMI monitor could light up
normally on RK3229 SDK board.


Changes in v2:
- Split some dw-hdmi driver changes into separate patches [01/04] & [02/04]

Yakir Yang (4):
  drm: dw-hdmi: make it easy to recovery the platform data for platform
    driver
  drm: dw-hdmi: passing the "plat_data" when calling platform mode_valid
  drm: rockchip: hdmi: add RK3229 HDMI support
  dt-bindings: add document for rk3229-hdmi

 .../bindings/display/rockchip/dw_hdmi-rockchip.txt |   4 +-
 drivers/gpu/drm/bridge/dw-hdmi.c                   |  32 +-
 drivers/gpu/drm/imx/dw_hdmi-imx.c                  |  17 +-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c        | 376 +++++++++++++++++++--
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h        | 137 ++++++++
 include/drm/bridge/dw_hdmi.h                       |   5 +-
 6 files changed, 529 insertions(+), 42 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h

-- 
2.1.2



^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 1/4] drm: dw-hdmi: make it easy to recovery the platform data for platform driver
  2016-01-07  8:53 [RFC PATCH v2 0/4] Add RK3229 HDMI support Yakir Yang
@ 2016-01-07  8:57 ` Yakir Yang
  2016-01-07  9:00 ` [RFC PATCH v2 2/4] drm: dw-hdmi: passing the "plat_data" when calling platform mode_valid Yakir Yang
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 11+ messages in thread
From: Yakir Yang @ 2016-01-07  8:57 UTC (permalink / raw)
  To: Russell King, Philipp Zabel, Andy Yan, Mark Yao, Heiko Stuebner
  Cc: David Airlie, Rob Herring, Kumar Gala, Zheng Yang, dri-devel,
	devicetree, linux-kernel, linux-rockchip, linux-arm-kernel,
	Yakir Yang

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v2: None

 drivers/gpu/drm/imx/dw_hdmi-imx.c           | 7 ++++---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 7 ++++---
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c
index 063825f..0968610 100644
--- a/drivers/gpu/drm/imx/dw_hdmi-imx.c
+++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c
@@ -26,6 +26,7 @@ struct imx_hdmi {
 	struct device *dev;
 	struct drm_encoder encoder;
 	struct regmap *regmap;
+	struct dw_hdmi_plat_data plat_data;
 };
 
 static const struct dw_hdmi_mpll_config imx_mpll_cfg[] = {
@@ -204,7 +205,6 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
 			    void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-	const struct dw_hdmi_plat_data *plat_data;
 	const struct of_device_id *match;
 	struct drm_device *drm = data;
 	struct drm_encoder *encoder;
@@ -221,7 +221,7 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
 		return -ENOMEM;
 
 	match = of_match_node(dw_hdmi_imx_dt_ids, pdev->dev.of_node);
-	plat_data = match->data;
+	hdmi->plat_data = *(const struct dw_hdmi_plat_data *)match->data;
 	hdmi->dev = &pdev->dev;
 	encoder = &hdmi->encoder;
 
@@ -253,7 +253,8 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
 	drm_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs,
 			 DRM_MODE_ENCODER_TMDS, NULL);
 
-	return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
+	return dw_hdmi_bind(dev, master, data, encoder, iores, irq,
+			    &hdmi->plat_data);
 }
 
 static void dw_hdmi_imx_unbind(struct device *dev, struct device *master,
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index c65ce8c..d5816b3 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -28,6 +28,7 @@ struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
 	struct drm_encoder encoder;
+	struct dw_hdmi_plat_data plat_data;
 };
 
 #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
@@ -242,7 +243,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 				 void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-	const struct dw_hdmi_plat_data *plat_data;
 	const struct of_device_id *match;
 	struct drm_device *drm = data;
 	struct drm_encoder *encoder;
@@ -259,7 +259,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return -ENOMEM;
 
 	match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
-	plat_data = match->data;
+	hdmi->plat_data = *(const struct dw_hdmi_plat_data *)match->data;
 	hdmi->dev = &pdev->dev;
 	encoder = &hdmi->encoder;
 
@@ -293,7 +293,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
 			 DRM_MODE_ENCODER_TMDS, NULL);
 
-	return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
+	return dw_hdmi_bind(dev, master, data, encoder, iores, irq,
+			    &hdmi->plat_data);
 }
 
 static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
-- 
2.1.2



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 2/4] drm: dw-hdmi: passing the "plat_data" when calling platform mode_valid
  2016-01-07  8:53 [RFC PATCH v2 0/4] Add RK3229 HDMI support Yakir Yang
  2016-01-07  8:57 ` [RFC PATCH v2 1/4] drm: dw-hdmi: make it easy to recovery the platform data for platform driver Yakir Yang
@ 2016-01-07  9:00 ` Yakir Yang
  2016-01-07  9:02 ` [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support Yakir Yang
  2016-01-07  9:05 ` [RFC PATCH v2 4/4] dt-bindings: add document for rk3229-hdmi Yakir Yang
  3 siblings, 0 replies; 11+ messages in thread
From: Yakir Yang @ 2016-01-07  9:00 UTC (permalink / raw)
  To: Russell King, Philipp Zabel, Andy Yan, Mark Yao, Heiko Stuebner
  Cc: David Airlie, Rob Herring, Kumar Gala, Zheng Yang, dri-devel,
	devicetree, linux-kernel, linux-rockchip, linux-arm-kernel,
	Yakir Yang

Make it easy for platform driver could recovery the private data that
would help to valid the display mode.

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v2: None

 drivers/gpu/drm/bridge/dw-hdmi.c            |  5 +++--
 drivers/gpu/drm/imx/dw_hdmi-imx.c           | 10 ++++++----
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c |  2 +-
 include/drm/bridge/dw_hdmi.h                |  2 +-
 4 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index 6fbec99..5ad72ec 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -1476,14 +1476,15 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector,
 {
 	struct dw_hdmi *hdmi = container_of(connector,
 					   struct dw_hdmi, connector);
+	const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
 	enum drm_mode_status mode_status = MODE_OK;
 
 	/* We don't support double-clocked modes */
 	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
 		return MODE_BAD;
 
-	if (hdmi->plat_data->mode_valid)
-		mode_status = hdmi->plat_data->mode_valid(connector, mode);
+	if (plat_data->mode_valid)
+		mode_status = plat_data->mode_valid(plat_data, mode);
 
 	return mode_status;
 }
diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c
index 0968610..8b0a7da 100644
--- a/drivers/gpu/drm/imx/dw_hdmi-imx.c
+++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c
@@ -150,8 +150,9 @@ static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = {
 	.destroy = drm_encoder_cleanup,
 };
 
-static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con,
-						  struct drm_display_mode *mode)
+static enum drm_mode_status
+imx6q_hdmi_mode_valid(const struct dw_hdmi_plat_data *plat_data,
+		      struct drm_display_mode *mode)
 {
 	if (mode->clock < 13500)
 		return MODE_CLOCK_LOW;
@@ -162,8 +163,9 @@ static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con,
 	return MODE_OK;
 }
 
-static enum drm_mode_status imx6dl_hdmi_mode_valid(struct drm_connector *con,
-						   struct drm_display_mode *mode)
+static enum drm_mode_status
+imx6dl_hdmi_mode_valid(const struct dw_hdmi_plat_data *plat_data,
+		       struct drm_display_mode *mode)
 {
 	if (mode->clock < 13500)
 		return MODE_CLOCK_LOW;
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index d5816b3..8164823 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -156,7 +156,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 }
 
 static enum drm_mode_status
-dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
+dw_hdmi_rockchip_mode_valid(const struct dw_hdmi_plat_data *plat_data,
 			    struct drm_display_mode *mode)
 {
 	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index bae79f3..f8dec64 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -52,7 +52,7 @@ struct dw_hdmi_plat_data {
 	const struct dw_hdmi_mpll_config *mpll_cfg;
 	const struct dw_hdmi_curr_ctrl *cur_ctr;
 	const struct dw_hdmi_phy_config *phy_config;
-	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+	enum drm_mode_status (*mode_valid)(const struct dw_hdmi_plat_data *pd,
 					   struct drm_display_mode *mode);
 };
 
-- 
2.1.2



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support
  2016-01-07  8:53 [RFC PATCH v2 0/4] Add RK3229 HDMI support Yakir Yang
  2016-01-07  8:57 ` [RFC PATCH v2 1/4] drm: dw-hdmi: make it easy to recovery the platform data for platform driver Yakir Yang
  2016-01-07  9:00 ` [RFC PATCH v2 2/4] drm: dw-hdmi: passing the "plat_data" when calling platform mode_valid Yakir Yang
@ 2016-01-07  9:02 ` Yakir Yang
  2016-01-07 10:04   ` Philipp Zabel
  2016-01-07  9:05 ` [RFC PATCH v2 4/4] dt-bindings: add document for rk3229-hdmi Yakir Yang
  3 siblings, 1 reply; 11+ messages in thread
From: Yakir Yang @ 2016-01-07  9:02 UTC (permalink / raw)
  To: Mark Yao, Heiko Stuebner, Russell King, Philipp Zabel, Andy Yan
  Cc: David Airlie, Rob Herring, Kumar Gala, Zheng Yang, dri-devel,
	devicetree, linux-kernel, linux-rockchip, linux-arm-kernel,
	Yakir Yang

RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
the max output resolution is 4K.

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v2:
- Split some dw-hdmi driver changes into separate patches [01/04] & [02/04]

 drivers/gpu/drm/bridge/dw-hdmi.c            |  27 +-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 367 ++++++++++++++++++++++++++--
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h | 137 +++++++++++
 include/drm/bridge/dw_hdmi.h                |   3 +
 4 files changed, 507 insertions(+), 27 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h

diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index 5ad72ec..5e03d83 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -735,10 +735,12 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 {
 	unsigned res_idx;
 	u8 val, msec;
+	int ret;
 	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
 	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;
+	int mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
 
 	if (prep)
 		return -EINVAL;
@@ -758,27 +760,38 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 		return -EINVAL;
 	}
 
+	if (hdmi->plat_data->extphy_config) {
+		/* gen2 tx power off */
+		dw_hdmi_phy_gen2_txpwron(hdmi, 0);
+		dw_hdmi_phy_gen2_pddq(hdmi, 1);
+
+		ret = hdmi->plat_data->extphy_config(hdmi->plat_data, res_idx,
+						     mpixelclock);
+		/* gen2 tx power on */
+		dw_hdmi_phy_gen2_txpwron(hdmi, 1);
+		dw_hdmi_phy_gen2_pddq(hdmi, 0);
+
+		return ret;
+	}
+
 	/* PLL/MPLL Cfg - always match on final entry */
 	for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
-		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    mpll_config->mpixelclock)
+		if (mpixelclock <= mpll_config->mpixelclock)
 			break;
 
 	for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
-		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    curr_ctrl->mpixelclock)
+		if (mpixelclock <= curr_ctrl->mpixelclock)
 			break;
 
 	for (; phy_config->mpixelclock != ~0UL; phy_config++)
-		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    phy_config->mpixelclock)
+		if (mpixelclock <= phy_config->mpixelclock)
 			break;
 
 	if (mpll_config->mpixelclock == ~0UL ||
 	    curr_ctrl->mpixelclock == ~0UL ||
 	    phy_config->mpixelclock == ~0UL) {
 		dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
-			hdmi->hdmi_data.video_mode.mpixelclock);
+			mpixelclock);
 		return -EINVAL;
 	}
 
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 8164823..24fffaa 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -7,6 +7,7 @@
  * (at your option) any later version.
  */
 
+#include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/syscon.h>
@@ -21,18 +22,134 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_vop.h"
 
-#define GRF_SOC_CON6                    0x025c
-#define HDMI_SEL_VOP_LIT                (1 << 4)
+#include "dw_hdmi-rockchip.h"
 
 struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
 	struct drm_encoder encoder;
 	struct dw_hdmi_plat_data plat_data;
+
+	void __iomem *extphy_regbase;
+	struct clk *extphy_pclk;
 };
 
 #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
 
+static const struct extphy_config_tab rockchip_extphy_cfg[] = {
+	{ .mpixelclock = 165000000,
+	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 4,
+	  .data0_level = 4, 4, 4,
+	},
+
+	{ .mpixelclock = 225000000,
+	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 6,
+	  .data0_level = 6, 6, 6,
+	},
+
+	{ .mpixelclock = 340000000,
+	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 6,
+	  .data0_level = 10, 10, 10,
+	},
+
+	{ .mpixelclock = 594000000,
+	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 7,
+	  .data0_level = 10, 10, 10,
+	},
+
+	{ .mpixelclock = ~0UL},
+};
+
+static const struct extphy_pll_config_tab rockchip_extphy_pll_cfg[] = {
+	{
+		.mpixelclock = 27000000, .param = {
+			{ .pll_nd = 1, .pll_nf = 45,
+			  .tmsd_divider_a = 3, 1, 1,
+			  .pclk_divider_a = 1, 3, 3, 4,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+			{ .pll_nd = 1, .pll_nf = 45,
+			  .tmsd_divider_a = 0, 3, 3,
+			  .pclk_divider_a = 1, 3, 3, 4,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+		},
+	}, {
+		.mpixelclock = 59400000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 3, 1, 1,
+			  .pclk_divider_a = 1, 3, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 1, 1, 1,
+			  .pclk_divider_a = 1, 3, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+		},
+	}, {
+		.mpixelclock = 74250000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 1, 1, 1,
+			  .pclk_divider_a = 1, 2, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+			{ .pll_nd = 4, .pll_nf = 495,
+			  .tmsd_divider_a = 1, 2, 2,
+			  .pclk_divider_a = 1, 3, 3, 4,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
+			},
+		},
+	}, {
+		.mpixelclock = 148500000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 1, 0, 0,
+			  .pclk_divider_a = 1, 2, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
+			},
+			{ .pll_nd = 4, .pll_nf = 495,
+			  .tmsd_divider_a = 0, 2, 2,
+			  .pclk_divider_a = 1, 3, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
+			},
+		},
+	}, {
+		.mpixelclock = 297000000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 0, 0, 0,
+			  .pclk_divider_a = 1, 0, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
+			},
+			{ .pll_nd = 4, .pll_nf = 495,
+			  .tmsd_divider_a = 1, 2, 0,
+			  .pclk_divider_a = 1, 3, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
+			},
+		},
+	}, {
+		.mpixelclock = 594000000, .param = {
+			{ .pll_nd = 1, .pll_nf = 99,
+			  .tmsd_divider_a = 0, 2, 0,
+			  .pclk_divider_a = 1, 0, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
+			},
+		}
+	}, {
+		.mpixelclock = ~0UL,
+	}
+};
+
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
 		27000000, {
@@ -142,9 +259,164 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 	{ ~0UL,	     0x0000, 0x0000, 0x0000}
 };
 
+static inline void hdmi_extphy_write(struct rockchip_hdmi *hdmi,
+				     unsigned short data, unsigned char addr)
+{
+	writel_relaxed(data, hdmi->extphy_regbase + (addr) * 0x04);
+}
+
+static inline unsigned int hdmi_extphy_read(struct rockchip_hdmi *hdmi,
+					    unsigned char addr)
+{
+	return readl_relaxed(hdmi->extphy_regbase + (addr) * 0x04);
+}
+
+static int rockchip_extphy_config(const struct dw_hdmi_plat_data *plat_data,
+				  int res, int pixelclock)
+{
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(plat_data);
+	const struct extphy_pll_config_tab *mpll = rockchip_extphy_pll_cfg;
+	const struct extphy_config_tab *ctrl = rockchip_extphy_cfg;
+	const struct extphy_pll_config_param *param;
+	unsigned long timeout;
+	int i, stat;
+
+	if (res >= DW_HDMI_RES_MAX) {
+		dev_err(hdmi->dev, "Extphy can't support res %d\n", res);
+		return -EINVAL;
+	}
+
+	/* Find out the extphy MPLL configure parameters */
+	for (i = 0; mpll[i].mpixelclock != ~0UL; i++)
+		if (pixelclock == mpll[i].mpixelclock)
+			break;
+	if (mpll[i].mpixelclock == ~0UL) {
+		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
+		return -EINVAL;
+	}
+	param = &mpll[i].param[res];
+
+	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
+		     RK3229_PLL_POWER_DOWN | RK3229_PLL_PDATA_DEN);
+
+	/*
+	 * Configure external HDMI PHY PLL registers.
+	 */
+	stat = ((param->pll_nf >> 1) & EXT_PHY_PLL_FB_BIT8_MASK) |
+	       ((param->vco_div_5 & 1) << 5) |
+	       (param->pll_nd & EXT_PHY_PLL_PRE_DIVIDER_MASK);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_PRE_DIVIDER);
+
+	hdmi_extphy_write(hdmi, param->pll_nf, EXT_PHY_PLL_FB_DIVIDER);
+
+	stat = (param->pclk_divider_a & EXT_PHY_PCLK_DIVIDERA_MASK) |
+	       ((param->pclk_divider_b & 3) << 5);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER1);
+
+	stat = (param->pclk_divider_d & EXT_PHY_PCLK_DIVIDERD_MASK) |
+	       ((param->pclk_divider_c & 3) << 5);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER2);
+
+	stat = ((param->tmsd_divider_c & 3) << 4) |
+	       ((param->tmsd_divider_a & 3) << 2) |
+	       (param->tmsd_divider_b & 3);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_TMDSCLK_DIVIDER);
+
+	hdmi_extphy_write(hdmi, param->ppll_nf, EXT_PHY_PPLL_FB_DIVIDER);
+
+	if (param->ppll_no == 1) {
+		hdmi_extphy_write(hdmi, 0, EXT_PHY_PPLL_POST_DIVIDER);
+
+		stat = 0x20 | param->ppll_nd;
+		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
+	} else {
+		stat = ((param->ppll_no / 2) - 1) << 4;
+		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_POST_DIVIDER);
+
+		stat = 0xe0 | param->ppll_nd;
+		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
+	}
+
+
+	/* Find out the external HDMI PHY driver configure parameters */
+	for (i = 0; ctrl[i].mpixelclock != ~0UL; i++)
+		if (pixelclock <= ctrl[i].mpixelclock)
+			break;
+	if (ctrl[i].mpixelclock == ~0UL) {
+		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
+		return -EINVAL;
+	}
+
+	/*
+	 * Configure the external HDMI PHY driver registers.
+	 */
+	if (ctrl[i].slopeboost) {
+		hdmi_extphy_write(hdmi, 0xff, EXT_PHY_SIGNAL_CTRL);
+
+		stat = (ctrl[i].slopeboost - 1) & 3;
+		stat = (stat << 6) | (stat << 4) | (stat << 2) | stat;
+		hdmi_extphy_write(hdmi, stat, EXT_PHY_SLOPEBOOST);
+	} else
+		hdmi_extphy_write(hdmi, 0x0f, EXT_PHY_SIGNAL_CTRL);
+
+	stat = ctrl[i].pre_emphasis & 3;
+	stat = (stat << 4) | (stat << 2) | stat;
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_PREEMPHASIS);
+
+	stat = ((ctrl[i].clk_level & 0xf) << 4) | (ctrl[i].data2_level & 0xf);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL1);
+
+	stat = ((ctrl[i].data1_level & 0xf) << 4) | (ctrl[i].data0_level & 0xf);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL2);
+
+	hdmi_extphy_write(hdmi, 0x22, 0xf3);
+
+	stat = clk_get_rate(hdmi->extphy_pclk) / 100000;
+	hdmi_extphy_write(hdmi, ((stat >> 8) & 0xff) | 0x80, EXT_PHY_TERM_CAL);
+	hdmi_extphy_write(hdmi,  stat & 0xff, EXT_PHY_TERM_CAL_DIV_L);
+
+	if (pixelclock > 340000000)
+		stat = EXT_PHY_AUTO_R100_OHMS;
+	else if (pixelclock > 200000000)
+		stat = EXT_PHY_AUTO_R50_OHMS;
+	else
+		stat = EXT_PHY_AUTO_ROPEN_CIRCUIT;
+	hdmi_extphy_write(hdmi, stat | 0x20, EXT_PHY_TERM_RESIS_AUTO);
+	hdmi_extphy_write(hdmi, (stat >> 8) & 0xff, EXT_PHY_TERM_CAL);
+
+	stat = (pixelclock > 200000000) ? 0 : 0x11;
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_BW);
+	hdmi_extphy_write(hdmi, 0x27, EXT_PHY_PPLL_BW);
+
+	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_POWER_UP);
+
+	/* Detect whether PLL is lock or not */
+	timeout = jiffies + msecs_to_jiffies(100);
+	while (!time_after(jiffies, timeout)) {
+		usleep_range(1000, 2000);
+		stat = hdmi_extphy_read(hdmi, EXT_PHY_PPLL_POST_DIVIDER);
+		if (stat & EXT_PHY_PPLL_LOCK_STATUS_MASK)
+			break;
+	}
+
+	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_PDATA_EN);
+
+	if ((stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) == 0) {
+		dev_err(hdmi->dev, "EXT PHY PLL not locked\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
 static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 {
 	struct device_node *np = hdmi->dev->of_node;
+	struct platform_device *pdev;
+	struct resource *iores;
+	int ret;
+
+	pdev = container_of(hdmi->dev, struct platform_device, dev);
 
 	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 	if (IS_ERR(hdmi->regmap)) {
@@ -152,6 +424,37 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->regmap);
 	}
 
+	if (hdmi->plat_data.dev_type == RK3229_HDMI) {
+		iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		if (!iores)
+			return -ENXIO;
+
+		hdmi->extphy_regbase = devm_ioremap_resource(hdmi->dev, iores);
+		if (IS_ERR(hdmi->extphy_regbase)) {
+			dev_err(hdmi->dev, "failed to map extphy regbase\n");
+			return PTR_ERR(hdmi->extphy_regbase);
+		}
+
+		hdmi->extphy_pclk = devm_clk_get(hdmi->dev, "extphy");
+		if (IS_ERR(hdmi->extphy_pclk)) {
+			dev_err(hdmi->dev, "failed to get extphy clock\n");
+			return PTR_ERR(hdmi->extphy_pclk);
+		}
+
+		ret = clk_prepare_enable(hdmi->extphy_pclk);
+		if (ret) {
+			dev_err(hdmi->dev, "failed to enable extphy clk: %d\n",
+				ret);
+			return ret;
+		}
+
+		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON6,
+			     RK3229_IO_3V_DOMAIN);
+
+		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
+			     RK3229_DDC_MASK_EN);
+	}
+
 	return 0;
 }
 
@@ -159,17 +462,23 @@ static enum drm_mode_status
 dw_hdmi_rockchip_mode_valid(const struct dw_hdmi_plat_data *plat_data,
 			    struct drm_display_mode *mode)
 {
-	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
 	int pclk = mode->clock * 1000;
 	bool valid = false;
 	int i;
 
-	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
-		if (pclk == mpll_cfg[i].mpixelclock) {
-			valid = true;
-			break;
-		}
-	}
+	if (plat_data->dev_type == RK3288_HDMI)
+		for (i = 0; rockchip_mpll_cfg[i].mpixelclock != ~0UL; i++)
+			if (pclk == rockchip_mpll_cfg[i].mpixelclock) {
+				valid = true;
+				break;
+			}
+
+	if (plat_data->dev_type == RK3229_HDMI)
+		for (i = 0; rockchip_extphy_pll_cfg[i].mpixelclock != ~0UL; i++)
+			if (pclk == rockchip_extphy_pll_cfg[i].mpixelclock) {
+				valid = true;
+				break;
+			}
 
 	return (valid) ? MODE_OK : MODE_BAD;
 }
@@ -199,21 +508,30 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	int out_mode = ROCKCHIP_OUT_MODE_AAAA;
 	u32 val;
 	int mux;
 
+	if (hdmi->plat_data.dev_type == RK3229_HDMI)
+		out_mode = ROCKCHIP_OUT_MODE_P888;
+
 	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
-				      ROCKCHIP_OUT_MODE_AAAA);
+				      out_mode);
 
-	mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
-	if (mux)
-		val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16);
-	else
-		val = HDMI_SEL_VOP_LIT << 16;
+	if (hdmi->plat_data.dev_type == RK3288_HDMI) {
+		mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node,
+						      encoder);
+		if (mux)
+			val = RK3288_HDMI_SEL_VOP_LIT |
+			      (RK3288_HDMI_SEL_VOP_LIT << 16);
+		else
+			val = RK3288_HDMI_SEL_VOP_LIT << 16;
 
-	regmap_write(hdmi->regmap, GRF_SOC_CON6, val);
-	dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
-		(mux) ? "LIT" : "BIG");
+		regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON6, val);
+
+		dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
+			(mux) ? "LIT" : "BIG");
+	}
 }
 
 static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
@@ -223,7 +541,7 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun
 	.disable    = dw_hdmi_rockchip_encoder_disable,
 };
 
-static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
+static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
 	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
@@ -231,9 +549,18 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
 	.dev_type   = RK3288_HDMI,
 };
 
+static const struct dw_hdmi_plat_data rk3229_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.extphy_config = rockchip_extphy_config,
+	.dev_type   = RK3229_HDMI,
+};
+
 static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3288-dw-hdmi",
-	  .data = &rockchip_hdmi_drv_data
+	  .data = &rk3288_hdmi_drv_data
+	},
+	{ .compatible = "rockchip,rk3229-dw-hdmi",
+	  .data = &rk3229_hdmi_drv_data
 	},
 	{},
 };
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
new file mode 100644
index 0000000..f7ec733
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
@@ -0,0 +1,137 @@
+#ifndef __DW_HDMI_ROCKCHIP__
+#define __DW_HDMI_ROCKCHIP__
+
+struct extphy_config_tab {
+	u32 mpixelclock;
+	int pre_emphasis;
+	int slopeboost;
+	int clk_level;
+	int data0_level;
+	int data1_level;
+	int data2_level;
+};
+
+struct extphy_pll_config_tab {
+	unsigned long mpixelclock;
+	struct extphy_pll_config_param {
+		u8	pll_nd;
+		u16	pll_nf;
+		u8	tmsd_divider_a;
+		u8	tmsd_divider_b;
+		u8	tmsd_divider_c;
+		u8	pclk_divider_a;
+		u8	pclk_divider_b;
+		u8	pclk_divider_c;
+		u8	pclk_divider_d;
+		u8	vco_div_5;
+		u8	ppll_nd;
+		u8	ppll_nf;
+		u8	ppll_no;
+	} param[DW_HDMI_RES_MAX];
+};
+
+#define RK3288_GRF_SOC_CON6			0x025c
+#define RK3288_HDMI_SEL_VOP_LIT			(1 << 4)
+
+#define RK3229_GRF_SOC_CON6			0x0418
+#define RK3229_IO_3V_DOMAIN			((7 << 4) | (7 << (4 + 16)))
+
+#define RK3229_GRF_SOC_CON2			0x0408
+#define RK3229_DDC_MASK_EN			((3 << 13) | (3 << (13 + 16)))
+
+#define RK3229_PLL_POWER_DOWN			(BIT(12) | BIT(12 + 16))
+#define RK3229_PLL_POWER_UP			BIT(12 + 16)
+#define RK3229_PLL_PDATA_DEN			BIT(11 + 16)
+#define RK3229_PLL_PDATA_EN			(BIT(11) | BIT(11 + 16))
+
+/* PHY Defined for RK322X */
+#define EXT_PHY_CONTROL				0
+#define EXT_PHY_ANALOG_RESET_MASK		0x80
+#define EXT_PHY_DIGITAL_RESET_MASK		0x40
+#define EXT_PHY_PCLK_INVERT_MASK		0x08
+#define EXT_PHY_PREPCLK_INVERT_MASK		0x04
+#define EXT_PHY_TMDSCLK_INVERT_MASK		0x02
+#define EXT_PHY_SRC_SELECT_MASK			0x01
+
+#define EXT_PHY_TERM_CAL			0x03
+#define EXT_PHY_TERM_CAL_EN_MASK		0x80
+#define EXT_PHY_TERM_CAL_DIV_H_MASK		0x7f
+
+#define EXT_PHY_TERM_CAL_DIV_L			0x04
+
+#define EXT_PHY_PLL_PRE_DIVIDER			0xe2
+#define EXT_PHY_PLL_FB_BIT8_MASK		0x80
+#define EXT_PHY_PLL_PCLK_DIV5_EN_MASK		0x20
+#define EXT_PHY_PLL_PRE_DIVIDER_MASK		0x1f
+
+#define EXT_PHY_PLL_FB_DIVIDER			0xe3
+
+#define EXT_PHY_PCLK_DIVIDER1			0xe4
+#define EXT_PHY_PCLK_DIVIDERB_MASK		0x60
+#define EXT_PHY_PCLK_DIVIDERA_MASK		0x1f
+
+#define EXT_PHY_PCLK_DIVIDER2			0xe5
+#define EXT_PHY_PCLK_DIVIDERC_MASK		0x60
+#define EXT_PHY_PCLK_DIVIDERD_MASK		0x1f
+
+#define EXT_PHY_TMDSCLK_DIVIDER			0xe6
+#define EXT_PHY_TMDSCLK_DIVIDERC_MASK		0x30
+#define EXT_PHY_TMDSCLK_DIVIDERA_MASK		0x0c
+#define EXT_PHY_TMDSCLK_DIVIDERB_MASK		0x03
+
+#define EXT_PHY_PLL_BW				0xe7
+
+#define EXT_PHY_PPLL_PRE_DIVIDER		0xe9
+#define EXT_PHY_PPLL_ENABLE_MASK		0xc0
+#define EXT_PHY_PPLL_PRE_DIVIDER_MASK		0x1f
+
+#define EXT_PHY_PPLL_FB_DIVIDER			0xea
+
+#define EXT_PHY_PPLL_POST_DIVIDER		0xeb
+#define EXT_PHY_PPLL_FB_DIVIDER_BIT8_MASK	0x80
+#define EXT_PHY_PPLL_POST_DIVIDER_MASK		0x30
+#define EXT_PHY_PPLL_LOCK_STATUS_MASK		0x01
+
+#define EXT_PHY_PPLL_BW				0xec
+
+#define EXT_PHY_SIGNAL_CTRL			0xee
+#define EXT_PHY_TRANSITION_CLK_EN_MASK		0x80
+#define EXT_PHY_TRANSITION_D0_EN_MASK		0x40
+#define EXT_PHY_TRANSITION_D1_EN_MASK		0x20
+#define EXT_PHY_TRANSITION_D2_EN_MASK		0x10
+#define EXT_PHY_LEVEL_CLK_EN_MASK		0x08
+#define EXT_PHY_LEVEL_D0_EN_MASK		0x04
+#define EXT_PHY_LEVEL_D1_EN_MASK		0x02
+#define EXT_PHY_LEVEL_D2_EN_MASK		0x01
+
+#define EXT_PHY_SLOPEBOOST			0xef
+#define EXT_PHY_SLOPEBOOST_CLK_MASK		0x03
+#define EXT_PHY_SLOPEBOOST_D0_MASK		0x0c
+#define EXT_PHY_SLOPEBOOST_D1_MASK		0x30
+#define EXT_PHY_SLOPEBOOST_D2_MASK		0xc0
+
+#define EXT_PHY_PREEMPHASIS			0xf0
+#define EXT_PHY_PREEMPHASIS_D0_MASK		0x03
+#define EXT_PHY_PREEMPHASIS_D1_MASK		0x0c
+#define EXT_PHY_PREEMPHASIS_D2_MASK		0x30
+
+#define EXT_PHY_LEVEL1				0xf1
+#define EXT_PHY_LEVEL_CLK_MASK			0xf0
+#define EXT_PHY_LEVEL_D2_MASK			0x0f
+
+#define EXT_PHY_LEVEL2				0xf2
+#define EXT_PHY_LEVEL_D1_MASK			0xf0
+#define EXT_PHY_LEVEL_D0_MASK			0x0f
+
+#define EXT_PHY_TERM_RESIS_AUTO			0xf4
+#define EXT_PHY_AUTO_R50_OHMS			0
+#define EXT_PHY_AUTO_R75_OHMS			(1 << 2)
+#define EXT_PHY_AUTO_R100_OHMS			(2 << 2)
+#define EXT_PHY_AUTO_ROPEN_CIRCUIT		(3 << 2)
+
+#define EXT_PHY_TERM_RESIS_MANUAL_CLK		0xfb
+#define EXT_PHY_TERM_RESIS_MANUAL_D2		0xfc
+#define EXT_PHY_TERM_RESIS_MANUAL_D1		0xfd
+#define EXT_PHY_TERM_RESIS_MANUAL_D0		0xfe
+
+#endif /* __DW_HDMI_ROCKCHIP__ */
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index f8dec64..4e63158 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -24,6 +24,7 @@ enum {
 enum dw_hdmi_devtype {
 	IMX6Q_HDMI,
 	IMX6DL_HDMI,
+	RK3229_HDMI,
 	RK3288_HDMI,
 };
 
@@ -54,6 +55,8 @@ struct dw_hdmi_plat_data {
 	const struct dw_hdmi_phy_config *phy_config;
 	enum drm_mode_status (*mode_valid)(const struct dw_hdmi_plat_data *pd,
 					   struct drm_display_mode *mode);
+	int (*extphy_config)(const struct dw_hdmi_plat_data *plat_data,
+			     int res_idx, int pixelclock);
 };
 
 void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);
-- 
2.1.2



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 4/4] dt-bindings: add document for rk3229-hdmi
  2016-01-07  8:53 [RFC PATCH v2 0/4] Add RK3229 HDMI support Yakir Yang
                   ` (2 preceding siblings ...)
  2016-01-07  9:02 ` [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support Yakir Yang
@ 2016-01-07  9:05 ` Yakir Yang
  2016-01-11  2:06   ` Rob Herring
  3 siblings, 1 reply; 11+ messages in thread
From: Yakir Yang @ 2016-01-07  9:05 UTC (permalink / raw)
  To: Mark Yao, Heiko Stuebner, Russell King, Philipp Zabel, Andy Yan
  Cc: David Airlie, Rob Herring, Kumar Gala, Zheng Yang, dri-devel,
	devicetree, linux-kernel, linux-rockchip, linux-arm-kernel,
	Yakir Yang

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v2: None

 .../devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt         | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
index 668091f..1cdc627 100644
--- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
@@ -2,7 +2,7 @@ Rockchip specific extensions to the Synopsys Designware HDMI
 ================================
 
 Required properties:
-- compatible: "rockchip,rk3288-dw-hdmi";
+- compatible: "rockchip,rk3288-dw-hdmi", "rockchip,rk3229-dw-hdmi";
 - reg: Physical base address and length of the controller's registers.
 - clocks: phandle to hdmi iahb and isfr clocks.
 - clock-names: should be "iahb" "isfr"
@@ -15,8 +15,10 @@ Required properties:
   rk3288 platform
 
 Optional properties
+- reg: Physical base address and length of the external HDMI PHY's registers.
 - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
 - clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
+		       phandle to the external HDMI PHY clock, name should be "extphy"
 
 Example:
 hdmi: hdmi@ff980000 {
-- 
2.1.2



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support
  2016-01-07  9:02 ` [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support Yakir Yang
@ 2016-01-07 10:04   ` Philipp Zabel
  2016-01-07 10:15     ` Yakir Yang
  0 siblings, 1 reply; 11+ messages in thread
From: Philipp Zabel @ 2016-01-07 10:04 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Mark Yao, Heiko Stuebner, Russell King, Andy Yan, David Airlie,
	Rob Herring, Kumar Gala, Zheng Yang, dri-devel, devicetree,
	linux-kernel, linux-rockchip, linux-arm-kernel

Am Donnerstag, den 07.01.2016, 17:02 +0800 schrieb Yakir Yang:
> RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
> the max output resolution is 4K.
> 
> Signed-off-by: Yakir Yang <ykk@rock-chips.com>

It sounds like the INNO HDMI2.0 phy is not necessarily specific to
RK3229 but might also appear in other SoCs? If so, I think this should
be implemented in a separate phy driver and be used by dw_hdmi-rockchip.

regards
Philipp

> ---
> Changes in v2:
> - Split some dw-hdmi driver changes into separate patches [01/04] & [02/04]
> 
>  drivers/gpu/drm/bridge/dw-hdmi.c            |  27 +-
>  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 367 ++++++++++++++++++++++++++--
>  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h | 137 +++++++++++
>  include/drm/bridge/dw_hdmi.h                |   3 +
>  4 files changed, 507 insertions(+), 27 deletions(-)
>  create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
> 
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
> index 5ad72ec..5e03d83 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.c
> @@ -735,10 +735,12 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>  {
>  	unsigned res_idx;
>  	u8 val, msec;
> +	int ret;
>  	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
>  	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;
> +	int mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
>  
>  	if (prep)
>  		return -EINVAL;
> @@ -758,27 +760,38 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>  		return -EINVAL;
>  	}
>  
> +	if (hdmi->plat_data->extphy_config) {
> +		/* gen2 tx power off */
> +		dw_hdmi_phy_gen2_txpwron(hdmi, 0);
> +		dw_hdmi_phy_gen2_pddq(hdmi, 1);
> +
> +		ret = hdmi->plat_data->extphy_config(hdmi->plat_data, res_idx,
> +						     mpixelclock);
> +		/* gen2 tx power on */
> +		dw_hdmi_phy_gen2_txpwron(hdmi, 1);
> +		dw_hdmi_phy_gen2_pddq(hdmi, 0);
> +
> +		return ret;
> +	}
> +
>  	/* PLL/MPLL Cfg - always match on final entry */
>  	for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
> -		    mpll_config->mpixelclock)
> +		if (mpixelclock <= mpll_config->mpixelclock)
>  			break;
>  
>  	for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
> -		    curr_ctrl->mpixelclock)
> +		if (mpixelclock <= curr_ctrl->mpixelclock)
>  			break;
>  
>  	for (; phy_config->mpixelclock != ~0UL; phy_config++)
> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
> -		    phy_config->mpixelclock)
> +		if (mpixelclock <= phy_config->mpixelclock)
>  			break;
>  
>  	if (mpll_config->mpixelclock == ~0UL ||
>  	    curr_ctrl->mpixelclock == ~0UL ||
>  	    phy_config->mpixelclock == ~0UL) {
>  		dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
> -			hdmi->hdmi_data.video_mode.mpixelclock);
> +			mpixelclock);
>  		return -EINVAL;
>  	}
>  
> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> index 8164823..24fffaa 100644
> --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> @@ -7,6 +7,7 @@
>   * (at your option) any later version.
>   */
>  
> +#include <linux/clk.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
>  #include <linux/mfd/syscon.h>
> @@ -21,18 +22,134 @@
>  #include "rockchip_drm_drv.h"
>  #include "rockchip_drm_vop.h"
>  
> -#define GRF_SOC_CON6                    0x025c
> -#define HDMI_SEL_VOP_LIT                (1 << 4)
> +#include "dw_hdmi-rockchip.h"
>  
>  struct rockchip_hdmi {
>  	struct device *dev;
>  	struct regmap *regmap;
>  	struct drm_encoder encoder;
>  	struct dw_hdmi_plat_data plat_data;
> +
> +	void __iomem *extphy_regbase;
> +	struct clk *extphy_pclk;
>  };
>  
>  #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
>  
> +static const struct extphy_config_tab rockchip_extphy_cfg[] = {
> +	{ .mpixelclock = 165000000,
> +	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 4,
> +	  .data0_level = 4, 4, 4,
> +	},
> +
> +	{ .mpixelclock = 225000000,
> +	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 6,
> +	  .data0_level = 6, 6, 6,
> +	},
> +
> +	{ .mpixelclock = 340000000,
> +	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 6,
> +	  .data0_level = 10, 10, 10,
> +	},
> +
> +	{ .mpixelclock = 594000000,
> +	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 7,
> +	  .data0_level = 10, 10, 10,
> +	},
> +
> +	{ .mpixelclock = ~0UL},
> +};
> +
> +static const struct extphy_pll_config_tab rockchip_extphy_pll_cfg[] = {
> +	{
> +		.mpixelclock = 27000000, .param = {
> +			{ .pll_nd = 1, .pll_nf = 45,
> +			  .tmsd_divider_a = 3, 1, 1,
> +			  .pclk_divider_a = 1, 3, 3, 4,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +			{ .pll_nd = 1, .pll_nf = 45,
> +			  .tmsd_divider_a = 0, 3, 3,
> +			  .pclk_divider_a = 1, 3, 3, 4,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 59400000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 3, 1, 1,
> +			  .pclk_divider_a = 1, 3, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 1, 1, 1,
> +			  .pclk_divider_a = 1, 3, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 74250000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 1, 1, 1,
> +			  .pclk_divider_a = 1, 2, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +			{ .pll_nd = 4, .pll_nf = 495,
> +			  .tmsd_divider_a = 1, 2, 2,
> +			  .pclk_divider_a = 1, 3, 3, 4,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 148500000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 1, 0, 0,
> +			  .pclk_divider_a = 1, 2, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
> +			},
> +			{ .pll_nd = 4, .pll_nf = 495,
> +			  .tmsd_divider_a = 0, 2, 2,
> +			  .pclk_divider_a = 1, 3, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 297000000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 0, 0, 0,
> +			  .pclk_divider_a = 1, 0, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
> +			},
> +			{ .pll_nd = 4, .pll_nf = 495,
> +			  .tmsd_divider_a = 1, 2, 0,
> +			  .pclk_divider_a = 1, 3, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 594000000, .param = {
> +			{ .pll_nd = 1, .pll_nf = 99,
> +			  .tmsd_divider_a = 0, 2, 0,
> +			  .pclk_divider_a = 1, 0, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
> +			},
> +		}
> +	}, {
> +		.mpixelclock = ~0UL,
> +	}
> +};
> +
>  static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
>  	{
>  		27000000, {
> @@ -142,9 +259,164 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
>  	{ ~0UL,	     0x0000, 0x0000, 0x0000}
>  };
>  
> +static inline void hdmi_extphy_write(struct rockchip_hdmi *hdmi,
> +				     unsigned short data, unsigned char addr)
> +{
> +	writel_relaxed(data, hdmi->extphy_regbase + (addr) * 0x04);
> +}
> +
> +static inline unsigned int hdmi_extphy_read(struct rockchip_hdmi *hdmi,
> +					    unsigned char addr)
> +{
> +	return readl_relaxed(hdmi->extphy_regbase + (addr) * 0x04);
> +}
> +
> +static int rockchip_extphy_config(const struct dw_hdmi_plat_data *plat_data,
> +				  int res, int pixelclock)
> +{
> +	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(plat_data);
> +	const struct extphy_pll_config_tab *mpll = rockchip_extphy_pll_cfg;
> +	const struct extphy_config_tab *ctrl = rockchip_extphy_cfg;
> +	const struct extphy_pll_config_param *param;
> +	unsigned long timeout;
> +	int i, stat;
> +
> +	if (res >= DW_HDMI_RES_MAX) {
> +		dev_err(hdmi->dev, "Extphy can't support res %d\n", res);
> +		return -EINVAL;
> +	}
> +
> +	/* Find out the extphy MPLL configure parameters */
> +	for (i = 0; mpll[i].mpixelclock != ~0UL; i++)
> +		if (pixelclock == mpll[i].mpixelclock)
> +			break;
> +	if (mpll[i].mpixelclock == ~0UL) {
> +		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
> +		return -EINVAL;
> +	}
> +	param = &mpll[i].param[res];
> +
> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
> +		     RK3229_PLL_POWER_DOWN | RK3229_PLL_PDATA_DEN);
> +
> +	/*
> +	 * Configure external HDMI PHY PLL registers.
> +	 */
> +	stat = ((param->pll_nf >> 1) & EXT_PHY_PLL_FB_BIT8_MASK) |
> +	       ((param->vco_div_5 & 1) << 5) |
> +	       (param->pll_nd & EXT_PHY_PLL_PRE_DIVIDER_MASK);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_PRE_DIVIDER);
> +
> +	hdmi_extphy_write(hdmi, param->pll_nf, EXT_PHY_PLL_FB_DIVIDER);
> +
> +	stat = (param->pclk_divider_a & EXT_PHY_PCLK_DIVIDERA_MASK) |
> +	       ((param->pclk_divider_b & 3) << 5);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER1);
> +
> +	stat = (param->pclk_divider_d & EXT_PHY_PCLK_DIVIDERD_MASK) |
> +	       ((param->pclk_divider_c & 3) << 5);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER2);
> +
> +	stat = ((param->tmsd_divider_c & 3) << 4) |
> +	       ((param->tmsd_divider_a & 3) << 2) |
> +	       (param->tmsd_divider_b & 3);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_TMDSCLK_DIVIDER);
> +
> +	hdmi_extphy_write(hdmi, param->ppll_nf, EXT_PHY_PPLL_FB_DIVIDER);
> +
> +	if (param->ppll_no == 1) {
> +		hdmi_extphy_write(hdmi, 0, EXT_PHY_PPLL_POST_DIVIDER);
> +
> +		stat = 0x20 | param->ppll_nd;
> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
> +	} else {
> +		stat = ((param->ppll_no / 2) - 1) << 4;
> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_POST_DIVIDER);
> +
> +		stat = 0xe0 | param->ppll_nd;
> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
> +	}
> +
> +
> +	/* Find out the external HDMI PHY driver configure parameters */
> +	for (i = 0; ctrl[i].mpixelclock != ~0UL; i++)
> +		if (pixelclock <= ctrl[i].mpixelclock)
> +			break;
> +	if (ctrl[i].mpixelclock == ~0UL) {
> +		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Configure the external HDMI PHY driver registers.
> +	 */
> +	if (ctrl[i].slopeboost) {
> +		hdmi_extphy_write(hdmi, 0xff, EXT_PHY_SIGNAL_CTRL);
> +
> +		stat = (ctrl[i].slopeboost - 1) & 3;
> +		stat = (stat << 6) | (stat << 4) | (stat << 2) | stat;
> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_SLOPEBOOST);
> +	} else
> +		hdmi_extphy_write(hdmi, 0x0f, EXT_PHY_SIGNAL_CTRL);
> +
> +	stat = ctrl[i].pre_emphasis & 3;
> +	stat = (stat << 4) | (stat << 2) | stat;
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PREEMPHASIS);
> +
> +	stat = ((ctrl[i].clk_level & 0xf) << 4) | (ctrl[i].data2_level & 0xf);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL1);
> +
> +	stat = ((ctrl[i].data1_level & 0xf) << 4) | (ctrl[i].data0_level & 0xf);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL2);
> +
> +	hdmi_extphy_write(hdmi, 0x22, 0xf3);
> +
> +	stat = clk_get_rate(hdmi->extphy_pclk) / 100000;
> +	hdmi_extphy_write(hdmi, ((stat >> 8) & 0xff) | 0x80, EXT_PHY_TERM_CAL);
> +	hdmi_extphy_write(hdmi,  stat & 0xff, EXT_PHY_TERM_CAL_DIV_L);
> +
> +	if (pixelclock > 340000000)
> +		stat = EXT_PHY_AUTO_R100_OHMS;
> +	else if (pixelclock > 200000000)
> +		stat = EXT_PHY_AUTO_R50_OHMS;
> +	else
> +		stat = EXT_PHY_AUTO_ROPEN_CIRCUIT;
> +	hdmi_extphy_write(hdmi, stat | 0x20, EXT_PHY_TERM_RESIS_AUTO);
> +	hdmi_extphy_write(hdmi, (stat >> 8) & 0xff, EXT_PHY_TERM_CAL);
> +
> +	stat = (pixelclock > 200000000) ? 0 : 0x11;
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_BW);
> +	hdmi_extphy_write(hdmi, 0x27, EXT_PHY_PPLL_BW);
> +
> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_POWER_UP);
> +
> +	/* Detect whether PLL is lock or not */
> +	timeout = jiffies + msecs_to_jiffies(100);
> +	while (!time_after(jiffies, timeout)) {
> +		usleep_range(1000, 2000);
> +		stat = hdmi_extphy_read(hdmi, EXT_PHY_PPLL_POST_DIVIDER);
> +		if (stat & EXT_PHY_PPLL_LOCK_STATUS_MASK)
> +			break;
> +	}
> +
> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_PDATA_EN);
> +
> +	if ((stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) == 0) {
> +		dev_err(hdmi->dev, "EXT PHY PLL not locked\n");
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
>  static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
>  {
>  	struct device_node *np = hdmi->dev->of_node;
> +	struct platform_device *pdev;
> +	struct resource *iores;
> +	int ret;
> +
> +	pdev = container_of(hdmi->dev, struct platform_device, dev);
>  
>  	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
>  	if (IS_ERR(hdmi->regmap)) {
> @@ -152,6 +424,37 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
>  		return PTR_ERR(hdmi->regmap);
>  	}
>  
> +	if (hdmi->plat_data.dev_type == RK3229_HDMI) {
> +		iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +		if (!iores)
> +			return -ENXIO;
> +
> +		hdmi->extphy_regbase = devm_ioremap_resource(hdmi->dev, iores);
> +		if (IS_ERR(hdmi->extphy_regbase)) {
> +			dev_err(hdmi->dev, "failed to map extphy regbase\n");
> +			return PTR_ERR(hdmi->extphy_regbase);
> +		}
> +
> +		hdmi->extphy_pclk = devm_clk_get(hdmi->dev, "extphy");
> +		if (IS_ERR(hdmi->extphy_pclk)) {
> +			dev_err(hdmi->dev, "failed to get extphy clock\n");
> +			return PTR_ERR(hdmi->extphy_pclk);
> +		}
> +
> +		ret = clk_prepare_enable(hdmi->extphy_pclk);
> +		if (ret) {
> +			dev_err(hdmi->dev, "failed to enable extphy clk: %d\n",
> +				ret);
> +			return ret;
> +		}
> +
> +		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON6,
> +			     RK3229_IO_3V_DOMAIN);
> +
> +		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
> +			     RK3229_DDC_MASK_EN);
> +	}
> +
>  	return 0;
>  }
>  
> @@ -159,17 +462,23 @@ static enum drm_mode_status
>  dw_hdmi_rockchip_mode_valid(const struct dw_hdmi_plat_data *plat_data,
>  			    struct drm_display_mode *mode)
>  {
> -	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
>  	int pclk = mode->clock * 1000;
>  	bool valid = false;
>  	int i;
>  
> -	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
> -		if (pclk == mpll_cfg[i].mpixelclock) {
> -			valid = true;
> -			break;
> -		}
> -	}
> +	if (plat_data->dev_type == RK3288_HDMI)
> +		for (i = 0; rockchip_mpll_cfg[i].mpixelclock != ~0UL; i++)
> +			if (pclk == rockchip_mpll_cfg[i].mpixelclock) {
> +				valid = true;
> +				break;
> +			}
> +
> +	if (plat_data->dev_type == RK3229_HDMI)
> +		for (i = 0; rockchip_extphy_pll_cfg[i].mpixelclock != ~0UL; i++)
> +			if (pclk == rockchip_extphy_pll_cfg[i].mpixelclock) {
> +				valid = true;
> +				break;
> +			}
>  
>  	return (valid) ? MODE_OK : MODE_BAD;
>  }
> @@ -199,21 +508,30 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
>  static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
>  {
>  	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
> +	int out_mode = ROCKCHIP_OUT_MODE_AAAA;
>  	u32 val;
>  	int mux;
>  
> +	if (hdmi->plat_data.dev_type == RK3229_HDMI)
> +		out_mode = ROCKCHIP_OUT_MODE_P888;
> +
>  	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
> -				      ROCKCHIP_OUT_MODE_AAAA);
> +				      out_mode);
>  
> -	mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
> -	if (mux)
> -		val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16);
> -	else
> -		val = HDMI_SEL_VOP_LIT << 16;
> +	if (hdmi->plat_data.dev_type == RK3288_HDMI) {
> +		mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node,
> +						      encoder);
> +		if (mux)
> +			val = RK3288_HDMI_SEL_VOP_LIT |
> +			      (RK3288_HDMI_SEL_VOP_LIT << 16);
> +		else
> +			val = RK3288_HDMI_SEL_VOP_LIT << 16;
>  
> -	regmap_write(hdmi->regmap, GRF_SOC_CON6, val);
> -	dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
> -		(mux) ? "LIT" : "BIG");
> +		regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON6, val);
> +
> +		dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
> +			(mux) ? "LIT" : "BIG");
> +	}
>  }
>  
>  static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
> @@ -223,7 +541,7 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun
>  	.disable    = dw_hdmi_rockchip_encoder_disable,
>  };
>  
> -static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
> +static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
>  	.mode_valid = dw_hdmi_rockchip_mode_valid,
>  	.mpll_cfg   = rockchip_mpll_cfg,
>  	.cur_ctr    = rockchip_cur_ctr,
> @@ -231,9 +549,18 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
>  	.dev_type   = RK3288_HDMI,
>  };
>  
> +static const struct dw_hdmi_plat_data rk3229_hdmi_drv_data = {
> +	.mode_valid = dw_hdmi_rockchip_mode_valid,
> +	.extphy_config = rockchip_extphy_config,
> +	.dev_type   = RK3229_HDMI,
> +};
> +
>  static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
>  	{ .compatible = "rockchip,rk3288-dw-hdmi",
> -	  .data = &rockchip_hdmi_drv_data
> +	  .data = &rk3288_hdmi_drv_data
> +	},
> +	{ .compatible = "rockchip,rk3229-dw-hdmi",
> +	  .data = &rk3229_hdmi_drv_data
>  	},
>  	{},
>  };
> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
> new file mode 100644
> index 0000000..f7ec733
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
> @@ -0,0 +1,137 @@
> +#ifndef __DW_HDMI_ROCKCHIP__
> +#define __DW_HDMI_ROCKCHIP__
> +
> +struct extphy_config_tab {
> +	u32 mpixelclock;
> +	int pre_emphasis;
> +	int slopeboost;
> +	int clk_level;
> +	int data0_level;
> +	int data1_level;
> +	int data2_level;
> +};
> +
> +struct extphy_pll_config_tab {
> +	unsigned long mpixelclock;
> +	struct extphy_pll_config_param {
> +		u8	pll_nd;
> +		u16	pll_nf;
> +		u8	tmsd_divider_a;
> +		u8	tmsd_divider_b;
> +		u8	tmsd_divider_c;
> +		u8	pclk_divider_a;
> +		u8	pclk_divider_b;
> +		u8	pclk_divider_c;
> +		u8	pclk_divider_d;
> +		u8	vco_div_5;
> +		u8	ppll_nd;
> +		u8	ppll_nf;
> +		u8	ppll_no;
> +	} param[DW_HDMI_RES_MAX];
> +};
> +
> +#define RK3288_GRF_SOC_CON6			0x025c
> +#define RK3288_HDMI_SEL_VOP_LIT			(1 << 4)
> +
> +#define RK3229_GRF_SOC_CON6			0x0418
> +#define RK3229_IO_3V_DOMAIN			((7 << 4) | (7 << (4 + 16)))
> +
> +#define RK3229_GRF_SOC_CON2			0x0408
> +#define RK3229_DDC_MASK_EN			((3 << 13) | (3 << (13 + 16)))
> +
> +#define RK3229_PLL_POWER_DOWN			(BIT(12) | BIT(12 + 16))
> +#define RK3229_PLL_POWER_UP			BIT(12 + 16)
> +#define RK3229_PLL_PDATA_DEN			BIT(11 + 16)
> +#define RK3229_PLL_PDATA_EN			(BIT(11) | BIT(11 + 16))
> +
> +/* PHY Defined for RK322X */
> +#define EXT_PHY_CONTROL				0
> +#define EXT_PHY_ANALOG_RESET_MASK		0x80
> +#define EXT_PHY_DIGITAL_RESET_MASK		0x40
> +#define EXT_PHY_PCLK_INVERT_MASK		0x08
> +#define EXT_PHY_PREPCLK_INVERT_MASK		0x04
> +#define EXT_PHY_TMDSCLK_INVERT_MASK		0x02
> +#define EXT_PHY_SRC_SELECT_MASK			0x01
> +
> +#define EXT_PHY_TERM_CAL			0x03
> +#define EXT_PHY_TERM_CAL_EN_MASK		0x80
> +#define EXT_PHY_TERM_CAL_DIV_H_MASK		0x7f
> +
> +#define EXT_PHY_TERM_CAL_DIV_L			0x04
> +
> +#define EXT_PHY_PLL_PRE_DIVIDER			0xe2
> +#define EXT_PHY_PLL_FB_BIT8_MASK		0x80
> +#define EXT_PHY_PLL_PCLK_DIV5_EN_MASK		0x20
> +#define EXT_PHY_PLL_PRE_DIVIDER_MASK		0x1f
> +
> +#define EXT_PHY_PLL_FB_DIVIDER			0xe3
> +
> +#define EXT_PHY_PCLK_DIVIDER1			0xe4
> +#define EXT_PHY_PCLK_DIVIDERB_MASK		0x60
> +#define EXT_PHY_PCLK_DIVIDERA_MASK		0x1f
> +
> +#define EXT_PHY_PCLK_DIVIDER2			0xe5
> +#define EXT_PHY_PCLK_DIVIDERC_MASK		0x60
> +#define EXT_PHY_PCLK_DIVIDERD_MASK		0x1f
> +
> +#define EXT_PHY_TMDSCLK_DIVIDER			0xe6
> +#define EXT_PHY_TMDSCLK_DIVIDERC_MASK		0x30
> +#define EXT_PHY_TMDSCLK_DIVIDERA_MASK		0x0c
> +#define EXT_PHY_TMDSCLK_DIVIDERB_MASK		0x03
> +
> +#define EXT_PHY_PLL_BW				0xe7
> +
> +#define EXT_PHY_PPLL_PRE_DIVIDER		0xe9
> +#define EXT_PHY_PPLL_ENABLE_MASK		0xc0
> +#define EXT_PHY_PPLL_PRE_DIVIDER_MASK		0x1f
> +
> +#define EXT_PHY_PPLL_FB_DIVIDER			0xea
> +
> +#define EXT_PHY_PPLL_POST_DIVIDER		0xeb
> +#define EXT_PHY_PPLL_FB_DIVIDER_BIT8_MASK	0x80
> +#define EXT_PHY_PPLL_POST_DIVIDER_MASK		0x30
> +#define EXT_PHY_PPLL_LOCK_STATUS_MASK		0x01
> +
> +#define EXT_PHY_PPLL_BW				0xec
> +
> +#define EXT_PHY_SIGNAL_CTRL			0xee
> +#define EXT_PHY_TRANSITION_CLK_EN_MASK		0x80
> +#define EXT_PHY_TRANSITION_D0_EN_MASK		0x40
> +#define EXT_PHY_TRANSITION_D1_EN_MASK		0x20
> +#define EXT_PHY_TRANSITION_D2_EN_MASK		0x10
> +#define EXT_PHY_LEVEL_CLK_EN_MASK		0x08
> +#define EXT_PHY_LEVEL_D0_EN_MASK		0x04
> +#define EXT_PHY_LEVEL_D1_EN_MASK		0x02
> +#define EXT_PHY_LEVEL_D2_EN_MASK		0x01
> +
> +#define EXT_PHY_SLOPEBOOST			0xef
> +#define EXT_PHY_SLOPEBOOST_CLK_MASK		0x03
> +#define EXT_PHY_SLOPEBOOST_D0_MASK		0x0c
> +#define EXT_PHY_SLOPEBOOST_D1_MASK		0x30
> +#define EXT_PHY_SLOPEBOOST_D2_MASK		0xc0
> +
> +#define EXT_PHY_PREEMPHASIS			0xf0
> +#define EXT_PHY_PREEMPHASIS_D0_MASK		0x03
> +#define EXT_PHY_PREEMPHASIS_D1_MASK		0x0c
> +#define EXT_PHY_PREEMPHASIS_D2_MASK		0x30
> +
> +#define EXT_PHY_LEVEL1				0xf1
> +#define EXT_PHY_LEVEL_CLK_MASK			0xf0
> +#define EXT_PHY_LEVEL_D2_MASK			0x0f
> +
> +#define EXT_PHY_LEVEL2				0xf2
> +#define EXT_PHY_LEVEL_D1_MASK			0xf0
> +#define EXT_PHY_LEVEL_D0_MASK			0x0f
> +
> +#define EXT_PHY_TERM_RESIS_AUTO			0xf4
> +#define EXT_PHY_AUTO_R50_OHMS			0
> +#define EXT_PHY_AUTO_R75_OHMS			(1 << 2)
> +#define EXT_PHY_AUTO_R100_OHMS			(2 << 2)
> +#define EXT_PHY_AUTO_ROPEN_CIRCUIT		(3 << 2)
> +
> +#define EXT_PHY_TERM_RESIS_MANUAL_CLK		0xfb
> +#define EXT_PHY_TERM_RESIS_MANUAL_D2		0xfc
> +#define EXT_PHY_TERM_RESIS_MANUAL_D1		0xfd
> +#define EXT_PHY_TERM_RESIS_MANUAL_D0		0xfe
> +
> +#endif /* __DW_HDMI_ROCKCHIP__ */
> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
> index f8dec64..4e63158 100644
> --- a/include/drm/bridge/dw_hdmi.h
> +++ b/include/drm/bridge/dw_hdmi.h
> @@ -24,6 +24,7 @@ enum {
>  enum dw_hdmi_devtype {
>  	IMX6Q_HDMI,
>  	IMX6DL_HDMI,
> +	RK3229_HDMI,
>  	RK3288_HDMI,
>  };
>  
> @@ -54,6 +55,8 @@ struct dw_hdmi_plat_data {
>  	const struct dw_hdmi_phy_config *phy_config;
>  	enum drm_mode_status (*mode_valid)(const struct dw_hdmi_plat_data *pd,
>  					   struct drm_display_mode *mode);
> +	int (*extphy_config)(const struct dw_hdmi_plat_data *plat_data,
> +			     int res_idx, int pixelclock);
>  };
>  
>  void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support
  2016-01-07 10:04   ` Philipp Zabel
@ 2016-01-07 10:15     ` Yakir Yang
  2016-01-07 16:50       ` Philipp Zabel
  0 siblings, 1 reply; 11+ messages in thread
From: Yakir Yang @ 2016-01-07 10:15 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Mark Yao, Heiko Stuebner, Russell King, Andy Yan, David Airlie,
	Rob Herring, Kumar Gala, Zheng Yang, dri-devel, devicetree,
	linux-kernel, linux-rockchip, linux-arm-kernel

Hi Philipp,

Thanks for your fast respond :)

On 01/07/2016 06:04 PM, Philipp Zabel wrote:
> Am Donnerstag, den 07.01.2016, 17:02 +0800 schrieb Yakir Yang:
>> RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
>> the max output resolution is 4K.
>>
>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> It sounds like the INNO HDMI2.0 phy is not necessarily specific to
> RK3229 but might also appear in other SoCs? If so, I think this should
> be implemented in a separate phy driver and be used by dw_hdmi-rockchip.

Do you mean I should create a new phy driver that place in "driver/phy" 
directly ?

I have think about this idea, and it would make things much clean. But 
INNO PHY
driver need the target pixel clock in drm_display_mode, I didn't find a 
good way
to pass this variable to separate phy driver. Do you have some idea ?

Thanks,
- Yakir

> regards
> Philipp
>
>> ---
>> Changes in v2:
>> - Split some dw-hdmi driver changes into separate patches [01/04] & [02/04]
>>
>>   drivers/gpu/drm/bridge/dw-hdmi.c            |  27 +-
>>   drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 367 ++++++++++++++++++++++++++--
>>   drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h | 137 +++++++++++
>>   include/drm/bridge/dw_hdmi.h                |   3 +
>>   4 files changed, 507 insertions(+), 27 deletions(-)
>>   create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
>>
>> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
>> index 5ad72ec..5e03d83 100644
>> --- a/drivers/gpu/drm/bridge/dw-hdmi.c
>> +++ b/drivers/gpu/drm/bridge/dw-hdmi.c
>> @@ -735,10 +735,12 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>>   {
>>   	unsigned res_idx;
>>   	u8 val, msec;
>> +	int ret;
>>   	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
>>   	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;
>> +	int mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
>>   
>>   	if (prep)
>>   		return -EINVAL;
>> @@ -758,27 +760,38 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>>   		return -EINVAL;
>>   	}
>>   
>> +	if (hdmi->plat_data->extphy_config) {
>> +		/* gen2 tx power off */
>> +		dw_hdmi_phy_gen2_txpwron(hdmi, 0);
>> +		dw_hdmi_phy_gen2_pddq(hdmi, 1);
>> +
>> +		ret = hdmi->plat_data->extphy_config(hdmi->plat_data, res_idx,
>> +						     mpixelclock);
>> +		/* gen2 tx power on */
>> +		dw_hdmi_phy_gen2_txpwron(hdmi, 1);
>> +		dw_hdmi_phy_gen2_pddq(hdmi, 0);
>> +
>> +		return ret;
>> +	}
>> +
>>   	/* PLL/MPLL Cfg - always match on final entry */
>>   	for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
>> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
>> -		    mpll_config->mpixelclock)
>> +		if (mpixelclock <= mpll_config->mpixelclock)
>>   			break;
>>   
>>   	for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
>> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
>> -		    curr_ctrl->mpixelclock)
>> +		if (mpixelclock <= curr_ctrl->mpixelclock)
>>   			break;
>>   
>>   	for (; phy_config->mpixelclock != ~0UL; phy_config++)
>> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
>> -		    phy_config->mpixelclock)
>> +		if (mpixelclock <= phy_config->mpixelclock)
>>   			break;
>>   
>>   	if (mpll_config->mpixelclock == ~0UL ||
>>   	    curr_ctrl->mpixelclock == ~0UL ||
>>   	    phy_config->mpixelclock == ~0UL) {
>>   		dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
>> -			hdmi->hdmi_data.video_mode.mpixelclock);
>> +			mpixelclock);
>>   		return -EINVAL;
>>   	}
>>   
>> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
>> index 8164823..24fffaa 100644
>> --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
>> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
>> @@ -7,6 +7,7 @@
>>    * (at your option) any later version.
>>    */
>>   
>> +#include <linux/clk.h>
>>   #include <linux/module.h>
>>   #include <linux/platform_device.h>
>>   #include <linux/mfd/syscon.h>
>> @@ -21,18 +22,134 @@
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_vop.h"
>>   
>> -#define GRF_SOC_CON6                    0x025c
>> -#define HDMI_SEL_VOP_LIT                (1 << 4)
>> +#include "dw_hdmi-rockchip.h"
>>   
>>   struct rockchip_hdmi {
>>   	struct device *dev;
>>   	struct regmap *regmap;
>>   	struct drm_encoder encoder;
>>   	struct dw_hdmi_plat_data plat_data;
>> +
>> +	void __iomem *extphy_regbase;
>> +	struct clk *extphy_pclk;
>>   };
>>   
>>   #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
>>   
>> +static const struct extphy_config_tab rockchip_extphy_cfg[] = {
>> +	{ .mpixelclock = 165000000,
>> +	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 4,
>> +	  .data0_level = 4, 4, 4,
>> +	},
>> +
>> +	{ .mpixelclock = 225000000,
>> +	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 6,
>> +	  .data0_level = 6, 6, 6,
>> +	},
>> +
>> +	{ .mpixelclock = 340000000,
>> +	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 6,
>> +	  .data0_level = 10, 10, 10,
>> +	},
>> +
>> +	{ .mpixelclock = 594000000,
>> +	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 7,
>> +	  .data0_level = 10, 10, 10,
>> +	},
>> +
>> +	{ .mpixelclock = ~0UL},
>> +};
>> +
>> +static const struct extphy_pll_config_tab rockchip_extphy_pll_cfg[] = {
>> +	{
>> +		.mpixelclock = 27000000, .param = {
>> +			{ .pll_nd = 1, .pll_nf = 45,
>> +			  .tmsd_divider_a = 3, 1, 1,
>> +			  .pclk_divider_a = 1, 3, 3, 4,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
>> +			},
>> +			{ .pll_nd = 1, .pll_nf = 45,
>> +			  .tmsd_divider_a = 0, 3, 3,
>> +			  .pclk_divider_a = 1, 3, 3, 4,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
>> +			},
>> +		},
>> +	}, {
>> +		.mpixelclock = 59400000, .param = {
>> +			{ .pll_nd = 2, .pll_nf = 99,
>> +			  .tmsd_divider_a = 3, 1, 1,
>> +			  .pclk_divider_a = 1, 3, 2, 2,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
>> +			},
>> +			{ .pll_nd = 2, .pll_nf = 99,
>> +			  .tmsd_divider_a = 1, 1, 1,
>> +			  .pclk_divider_a = 1, 3, 2, 2,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
>> +			},
>> +		},
>> +	}, {
>> +		.mpixelclock = 74250000, .param = {
>> +			{ .pll_nd = 2, .pll_nf = 99,
>> +			  .tmsd_divider_a = 1, 1, 1,
>> +			  .pclk_divider_a = 1, 2, 2, 2,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
>> +			},
>> +			{ .pll_nd = 4, .pll_nf = 495,
>> +			  .tmsd_divider_a = 1, 2, 2,
>> +			  .pclk_divider_a = 1, 3, 3, 4,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
>> +			},
>> +		},
>> +	}, {
>> +		.mpixelclock = 148500000, .param = {
>> +			{ .pll_nd = 2, .pll_nf = 99,
>> +			  .tmsd_divider_a = 1, 0, 0,
>> +			  .pclk_divider_a = 1, 2, 1, 1,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
>> +			},
>> +			{ .pll_nd = 4, .pll_nf = 495,
>> +			  .tmsd_divider_a = 0, 2, 2,
>> +			  .pclk_divider_a = 1, 3, 2, 2,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
>> +			},
>> +		},
>> +	}, {
>> +		.mpixelclock = 297000000, .param = {
>> +			{ .pll_nd = 2, .pll_nf = 99,
>> +			  .tmsd_divider_a = 0, 0, 0,
>> +			  .pclk_divider_a = 1, 0, 1, 1,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
>> +			},
>> +			{ .pll_nd = 4, .pll_nf = 495,
>> +			  .tmsd_divider_a = 1, 2, 0,
>> +			  .pclk_divider_a = 1, 3, 1, 1,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
>> +			},
>> +		},
>> +	}, {
>> +		.mpixelclock = 594000000, .param = {
>> +			{ .pll_nd = 1, .pll_nf = 99,
>> +			  .tmsd_divider_a = 0, 2, 0,
>> +			  .pclk_divider_a = 1, 0, 1, 1,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
>> +			},
>> +		}
>> +	}, {
>> +		.mpixelclock = ~0UL,
>> +	}
>> +};
>> +
>>   static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
>>   	{
>>   		27000000, {
>> @@ -142,9 +259,164 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
>>   	{ ~0UL,	     0x0000, 0x0000, 0x0000}
>>   };
>>   
>> +static inline void hdmi_extphy_write(struct rockchip_hdmi *hdmi,
>> +				     unsigned short data, unsigned char addr)
>> +{
>> +	writel_relaxed(data, hdmi->extphy_regbase + (addr) * 0x04);
>> +}
>> +
>> +static inline unsigned int hdmi_extphy_read(struct rockchip_hdmi *hdmi,
>> +					    unsigned char addr)
>> +{
>> +	return readl_relaxed(hdmi->extphy_regbase + (addr) * 0x04);
>> +}
>> +
>> +static int rockchip_extphy_config(const struct dw_hdmi_plat_data *plat_data,
>> +				  int res, int pixelclock)
>> +{
>> +	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(plat_data);
>> +	const struct extphy_pll_config_tab *mpll = rockchip_extphy_pll_cfg;
>> +	const struct extphy_config_tab *ctrl = rockchip_extphy_cfg;
>> +	const struct extphy_pll_config_param *param;
>> +	unsigned long timeout;
>> +	int i, stat;
>> +
>> +	if (res >= DW_HDMI_RES_MAX) {
>> +		dev_err(hdmi->dev, "Extphy can't support res %d\n", res);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Find out the extphy MPLL configure parameters */
>> +	for (i = 0; mpll[i].mpixelclock != ~0UL; i++)
>> +		if (pixelclock == mpll[i].mpixelclock)
>> +			break;
>> +	if (mpll[i].mpixelclock == ~0UL) {
>> +		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
>> +		return -EINVAL;
>> +	}
>> +	param = &mpll[i].param[res];
>> +
>> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
>> +		     RK3229_PLL_POWER_DOWN | RK3229_PLL_PDATA_DEN);
>> +
>> +	/*
>> +	 * Configure external HDMI PHY PLL registers.
>> +	 */
>> +	stat = ((param->pll_nf >> 1) & EXT_PHY_PLL_FB_BIT8_MASK) |
>> +	       ((param->vco_div_5 & 1) << 5) |
>> +	       (param->pll_nd & EXT_PHY_PLL_PRE_DIVIDER_MASK);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_PRE_DIVIDER);
>> +
>> +	hdmi_extphy_write(hdmi, param->pll_nf, EXT_PHY_PLL_FB_DIVIDER);
>> +
>> +	stat = (param->pclk_divider_a & EXT_PHY_PCLK_DIVIDERA_MASK) |
>> +	       ((param->pclk_divider_b & 3) << 5);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER1);
>> +
>> +	stat = (param->pclk_divider_d & EXT_PHY_PCLK_DIVIDERD_MASK) |
>> +	       ((param->pclk_divider_c & 3) << 5);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER2);
>> +
>> +	stat = ((param->tmsd_divider_c & 3) << 4) |
>> +	       ((param->tmsd_divider_a & 3) << 2) |
>> +	       (param->tmsd_divider_b & 3);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_TMDSCLK_DIVIDER);
>> +
>> +	hdmi_extphy_write(hdmi, param->ppll_nf, EXT_PHY_PPLL_FB_DIVIDER);
>> +
>> +	if (param->ppll_no == 1) {
>> +		hdmi_extphy_write(hdmi, 0, EXT_PHY_PPLL_POST_DIVIDER);
>> +
>> +		stat = 0x20 | param->ppll_nd;
>> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
>> +	} else {
>> +		stat = ((param->ppll_no / 2) - 1) << 4;
>> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_POST_DIVIDER);
>> +
>> +		stat = 0xe0 | param->ppll_nd;
>> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
>> +	}
>> +
>> +
>> +	/* Find out the external HDMI PHY driver configure parameters */
>> +	for (i = 0; ctrl[i].mpixelclock != ~0UL; i++)
>> +		if (pixelclock <= ctrl[i].mpixelclock)
>> +			break;
>> +	if (ctrl[i].mpixelclock == ~0UL) {
>> +		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/*
>> +	 * Configure the external HDMI PHY driver registers.
>> +	 */
>> +	if (ctrl[i].slopeboost) {
>> +		hdmi_extphy_write(hdmi, 0xff, EXT_PHY_SIGNAL_CTRL);
>> +
>> +		stat = (ctrl[i].slopeboost - 1) & 3;
>> +		stat = (stat << 6) | (stat << 4) | (stat << 2) | stat;
>> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_SLOPEBOOST);
>> +	} else
>> +		hdmi_extphy_write(hdmi, 0x0f, EXT_PHY_SIGNAL_CTRL);
>> +
>> +	stat = ctrl[i].pre_emphasis & 3;
>> +	stat = (stat << 4) | (stat << 2) | stat;
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PREEMPHASIS);
>> +
>> +	stat = ((ctrl[i].clk_level & 0xf) << 4) | (ctrl[i].data2_level & 0xf);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL1);
>> +
>> +	stat = ((ctrl[i].data1_level & 0xf) << 4) | (ctrl[i].data0_level & 0xf);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL2);
>> +
>> +	hdmi_extphy_write(hdmi, 0x22, 0xf3);
>> +
>> +	stat = clk_get_rate(hdmi->extphy_pclk) / 100000;
>> +	hdmi_extphy_write(hdmi, ((stat >> 8) & 0xff) | 0x80, EXT_PHY_TERM_CAL);
>> +	hdmi_extphy_write(hdmi,  stat & 0xff, EXT_PHY_TERM_CAL_DIV_L);
>> +
>> +	if (pixelclock > 340000000)
>> +		stat = EXT_PHY_AUTO_R100_OHMS;
>> +	else if (pixelclock > 200000000)
>> +		stat = EXT_PHY_AUTO_R50_OHMS;
>> +	else
>> +		stat = EXT_PHY_AUTO_ROPEN_CIRCUIT;
>> +	hdmi_extphy_write(hdmi, stat | 0x20, EXT_PHY_TERM_RESIS_AUTO);
>> +	hdmi_extphy_write(hdmi, (stat >> 8) & 0xff, EXT_PHY_TERM_CAL);
>> +
>> +	stat = (pixelclock > 200000000) ? 0 : 0x11;
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_BW);
>> +	hdmi_extphy_write(hdmi, 0x27, EXT_PHY_PPLL_BW);
>> +
>> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_POWER_UP);
>> +
>> +	/* Detect whether PLL is lock or not */
>> +	timeout = jiffies + msecs_to_jiffies(100);
>> +	while (!time_after(jiffies, timeout)) {
>> +		usleep_range(1000, 2000);
>> +		stat = hdmi_extphy_read(hdmi, EXT_PHY_PPLL_POST_DIVIDER);
>> +		if (stat & EXT_PHY_PPLL_LOCK_STATUS_MASK)
>> +			break;
>> +	}
>> +
>> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_PDATA_EN);
>> +
>> +	if ((stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) == 0) {
>> +		dev_err(hdmi->dev, "EXT PHY PLL not locked\n");
>> +		return -EBUSY;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>>   static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
>>   {
>>   	struct device_node *np = hdmi->dev->of_node;
>> +	struct platform_device *pdev;
>> +	struct resource *iores;
>> +	int ret;
>> +
>> +	pdev = container_of(hdmi->dev, struct platform_device, dev);
>>   
>>   	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
>>   	if (IS_ERR(hdmi->regmap)) {
>> @@ -152,6 +424,37 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
>>   		return PTR_ERR(hdmi->regmap);
>>   	}
>>   
>> +	if (hdmi->plat_data.dev_type == RK3229_HDMI) {
>> +		iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +		if (!iores)
>> +			return -ENXIO;
>> +
>> +		hdmi->extphy_regbase = devm_ioremap_resource(hdmi->dev, iores);
>> +		if (IS_ERR(hdmi->extphy_regbase)) {
>> +			dev_err(hdmi->dev, "failed to map extphy regbase\n");
>> +			return PTR_ERR(hdmi->extphy_regbase);
>> +		}
>> +
>> +		hdmi->extphy_pclk = devm_clk_get(hdmi->dev, "extphy");
>> +		if (IS_ERR(hdmi->extphy_pclk)) {
>> +			dev_err(hdmi->dev, "failed to get extphy clock\n");
>> +			return PTR_ERR(hdmi->extphy_pclk);
>> +		}
>> +
>> +		ret = clk_prepare_enable(hdmi->extphy_pclk);
>> +		if (ret) {
>> +			dev_err(hdmi->dev, "failed to enable extphy clk: %d\n",
>> +				ret);
>> +			return ret;
>> +		}
>> +
>> +		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON6,
>> +			     RK3229_IO_3V_DOMAIN);
>> +
>> +		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
>> +			     RK3229_DDC_MASK_EN);
>> +	}
>> +
>>   	return 0;
>>   }
>>   
>> @@ -159,17 +462,23 @@ static enum drm_mode_status
>>   dw_hdmi_rockchip_mode_valid(const struct dw_hdmi_plat_data *plat_data,
>>   			    struct drm_display_mode *mode)
>>   {
>> -	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
>>   	int pclk = mode->clock * 1000;
>>   	bool valid = false;
>>   	int i;
>>   
>> -	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
>> -		if (pclk == mpll_cfg[i].mpixelclock) {
>> -			valid = true;
>> -			break;
>> -		}
>> -	}
>> +	if (plat_data->dev_type == RK3288_HDMI)
>> +		for (i = 0; rockchip_mpll_cfg[i].mpixelclock != ~0UL; i++)
>> +			if (pclk == rockchip_mpll_cfg[i].mpixelclock) {
>> +				valid = true;
>> +				break;
>> +			}
>> +
>> +	if (plat_data->dev_type == RK3229_HDMI)
>> +		for (i = 0; rockchip_extphy_pll_cfg[i].mpixelclock != ~0UL; i++)
>> +			if (pclk == rockchip_extphy_pll_cfg[i].mpixelclock) {
>> +				valid = true;
>> +				break;
>> +			}
>>   
>>   	return (valid) ? MODE_OK : MODE_BAD;
>>   }
>> @@ -199,21 +508,30 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
>>   static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
>>   {
>>   	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
>> +	int out_mode = ROCKCHIP_OUT_MODE_AAAA;
>>   	u32 val;
>>   	int mux;
>>   
>> +	if (hdmi->plat_data.dev_type == RK3229_HDMI)
>> +		out_mode = ROCKCHIP_OUT_MODE_P888;
>> +
>>   	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
>> -				      ROCKCHIP_OUT_MODE_AAAA);
>> +				      out_mode);
>>   
>> -	mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
>> -	if (mux)
>> -		val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16);
>> -	else
>> -		val = HDMI_SEL_VOP_LIT << 16;
>> +	if (hdmi->plat_data.dev_type == RK3288_HDMI) {
>> +		mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node,
>> +						      encoder);
>> +		if (mux)
>> +			val = RK3288_HDMI_SEL_VOP_LIT |
>> +			      (RK3288_HDMI_SEL_VOP_LIT << 16);
>> +		else
>> +			val = RK3288_HDMI_SEL_VOP_LIT << 16;
>>   
>> -	regmap_write(hdmi->regmap, GRF_SOC_CON6, val);
>> -	dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
>> -		(mux) ? "LIT" : "BIG");
>> +		regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON6, val);
>> +
>> +		dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
>> +			(mux) ? "LIT" : "BIG");
>> +	}
>>   }
>>   
>>   static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
>> @@ -223,7 +541,7 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun
>>   	.disable    = dw_hdmi_rockchip_encoder_disable,
>>   };
>>   
>> -static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
>> +static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
>>   	.mode_valid = dw_hdmi_rockchip_mode_valid,
>>   	.mpll_cfg   = rockchip_mpll_cfg,
>>   	.cur_ctr    = rockchip_cur_ctr,
>> @@ -231,9 +549,18 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
>>   	.dev_type   = RK3288_HDMI,
>>   };
>>   
>> +static const struct dw_hdmi_plat_data rk3229_hdmi_drv_data = {
>> +	.mode_valid = dw_hdmi_rockchip_mode_valid,
>> +	.extphy_config = rockchip_extphy_config,
>> +	.dev_type   = RK3229_HDMI,
>> +};
>> +
>>   static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
>>   	{ .compatible = "rockchip,rk3288-dw-hdmi",
>> -	  .data = &rockchip_hdmi_drv_data
>> +	  .data = &rk3288_hdmi_drv_data
>> +	},
>> +	{ .compatible = "rockchip,rk3229-dw-hdmi",
>> +	  .data = &rk3229_hdmi_drv_data
>>   	},
>>   	{},
>>   };
>> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
>> new file mode 100644
>> index 0000000..f7ec733
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
>> @@ -0,0 +1,137 @@
>> +#ifndef __DW_HDMI_ROCKCHIP__
>> +#define __DW_HDMI_ROCKCHIP__
>> +
>> +struct extphy_config_tab {
>> +	u32 mpixelclock;
>> +	int pre_emphasis;
>> +	int slopeboost;
>> +	int clk_level;
>> +	int data0_level;
>> +	int data1_level;
>> +	int data2_level;
>> +};
>> +
>> +struct extphy_pll_config_tab {
>> +	unsigned long mpixelclock;
>> +	struct extphy_pll_config_param {
>> +		u8	pll_nd;
>> +		u16	pll_nf;
>> +		u8	tmsd_divider_a;
>> +		u8	tmsd_divider_b;
>> +		u8	tmsd_divider_c;
>> +		u8	pclk_divider_a;
>> +		u8	pclk_divider_b;
>> +		u8	pclk_divider_c;
>> +		u8	pclk_divider_d;
>> +		u8	vco_div_5;
>> +		u8	ppll_nd;
>> +		u8	ppll_nf;
>> +		u8	ppll_no;
>> +	} param[DW_HDMI_RES_MAX];
>> +};
>> +
>> +#define RK3288_GRF_SOC_CON6			0x025c
>> +#define RK3288_HDMI_SEL_VOP_LIT			(1 << 4)
>> +
>> +#define RK3229_GRF_SOC_CON6			0x0418
>> +#define RK3229_IO_3V_DOMAIN			((7 << 4) | (7 << (4 + 16)))
>> +
>> +#define RK3229_GRF_SOC_CON2			0x0408
>> +#define RK3229_DDC_MASK_EN			((3 << 13) | (3 << (13 + 16)))
>> +
>> +#define RK3229_PLL_POWER_DOWN			(BIT(12) | BIT(12 + 16))
>> +#define RK3229_PLL_POWER_UP			BIT(12 + 16)
>> +#define RK3229_PLL_PDATA_DEN			BIT(11 + 16)
>> +#define RK3229_PLL_PDATA_EN			(BIT(11) | BIT(11 + 16))
>> +
>> +/* PHY Defined for RK322X */
>> +#define EXT_PHY_CONTROL				0
>> +#define EXT_PHY_ANALOG_RESET_MASK		0x80
>> +#define EXT_PHY_DIGITAL_RESET_MASK		0x40
>> +#define EXT_PHY_PCLK_INVERT_MASK		0x08
>> +#define EXT_PHY_PREPCLK_INVERT_MASK		0x04
>> +#define EXT_PHY_TMDSCLK_INVERT_MASK		0x02
>> +#define EXT_PHY_SRC_SELECT_MASK			0x01
>> +
>> +#define EXT_PHY_TERM_CAL			0x03
>> +#define EXT_PHY_TERM_CAL_EN_MASK		0x80
>> +#define EXT_PHY_TERM_CAL_DIV_H_MASK		0x7f
>> +
>> +#define EXT_PHY_TERM_CAL_DIV_L			0x04
>> +
>> +#define EXT_PHY_PLL_PRE_DIVIDER			0xe2
>> +#define EXT_PHY_PLL_FB_BIT8_MASK		0x80
>> +#define EXT_PHY_PLL_PCLK_DIV5_EN_MASK		0x20
>> +#define EXT_PHY_PLL_PRE_DIVIDER_MASK		0x1f
>> +
>> +#define EXT_PHY_PLL_FB_DIVIDER			0xe3
>> +
>> +#define EXT_PHY_PCLK_DIVIDER1			0xe4
>> +#define EXT_PHY_PCLK_DIVIDERB_MASK		0x60
>> +#define EXT_PHY_PCLK_DIVIDERA_MASK		0x1f
>> +
>> +#define EXT_PHY_PCLK_DIVIDER2			0xe5
>> +#define EXT_PHY_PCLK_DIVIDERC_MASK		0x60
>> +#define EXT_PHY_PCLK_DIVIDERD_MASK		0x1f
>> +
>> +#define EXT_PHY_TMDSCLK_DIVIDER			0xe6
>> +#define EXT_PHY_TMDSCLK_DIVIDERC_MASK		0x30
>> +#define EXT_PHY_TMDSCLK_DIVIDERA_MASK		0x0c
>> +#define EXT_PHY_TMDSCLK_DIVIDERB_MASK		0x03
>> +
>> +#define EXT_PHY_PLL_BW				0xe7
>> +
>> +#define EXT_PHY_PPLL_PRE_DIVIDER		0xe9
>> +#define EXT_PHY_PPLL_ENABLE_MASK		0xc0
>> +#define EXT_PHY_PPLL_PRE_DIVIDER_MASK		0x1f
>> +
>> +#define EXT_PHY_PPLL_FB_DIVIDER			0xea
>> +
>> +#define EXT_PHY_PPLL_POST_DIVIDER		0xeb
>> +#define EXT_PHY_PPLL_FB_DIVIDER_BIT8_MASK	0x80
>> +#define EXT_PHY_PPLL_POST_DIVIDER_MASK		0x30
>> +#define EXT_PHY_PPLL_LOCK_STATUS_MASK		0x01
>> +
>> +#define EXT_PHY_PPLL_BW				0xec
>> +
>> +#define EXT_PHY_SIGNAL_CTRL			0xee
>> +#define EXT_PHY_TRANSITION_CLK_EN_MASK		0x80
>> +#define EXT_PHY_TRANSITION_D0_EN_MASK		0x40
>> +#define EXT_PHY_TRANSITION_D1_EN_MASK		0x20
>> +#define EXT_PHY_TRANSITION_D2_EN_MASK		0x10
>> +#define EXT_PHY_LEVEL_CLK_EN_MASK		0x08
>> +#define EXT_PHY_LEVEL_D0_EN_MASK		0x04
>> +#define EXT_PHY_LEVEL_D1_EN_MASK		0x02
>> +#define EXT_PHY_LEVEL_D2_EN_MASK		0x01
>> +
>> +#define EXT_PHY_SLOPEBOOST			0xef
>> +#define EXT_PHY_SLOPEBOOST_CLK_MASK		0x03
>> +#define EXT_PHY_SLOPEBOOST_D0_MASK		0x0c
>> +#define EXT_PHY_SLOPEBOOST_D1_MASK		0x30
>> +#define EXT_PHY_SLOPEBOOST_D2_MASK		0xc0
>> +
>> +#define EXT_PHY_PREEMPHASIS			0xf0
>> +#define EXT_PHY_PREEMPHASIS_D0_MASK		0x03
>> +#define EXT_PHY_PREEMPHASIS_D1_MASK		0x0c
>> +#define EXT_PHY_PREEMPHASIS_D2_MASK		0x30
>> +
>> +#define EXT_PHY_LEVEL1				0xf1
>> +#define EXT_PHY_LEVEL_CLK_MASK			0xf0
>> +#define EXT_PHY_LEVEL_D2_MASK			0x0f
>> +
>> +#define EXT_PHY_LEVEL2				0xf2
>> +#define EXT_PHY_LEVEL_D1_MASK			0xf0
>> +#define EXT_PHY_LEVEL_D0_MASK			0x0f
>> +
>> +#define EXT_PHY_TERM_RESIS_AUTO			0xf4
>> +#define EXT_PHY_AUTO_R50_OHMS			0
>> +#define EXT_PHY_AUTO_R75_OHMS			(1 << 2)
>> +#define EXT_PHY_AUTO_R100_OHMS			(2 << 2)
>> +#define EXT_PHY_AUTO_ROPEN_CIRCUIT		(3 << 2)
>> +
>> +#define EXT_PHY_TERM_RESIS_MANUAL_CLK		0xfb
>> +#define EXT_PHY_TERM_RESIS_MANUAL_D2		0xfc
>> +#define EXT_PHY_TERM_RESIS_MANUAL_D1		0xfd
>> +#define EXT_PHY_TERM_RESIS_MANUAL_D0		0xfe
>> +
>> +#endif /* __DW_HDMI_ROCKCHIP__ */
>> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
>> index f8dec64..4e63158 100644
>> --- a/include/drm/bridge/dw_hdmi.h
>> +++ b/include/drm/bridge/dw_hdmi.h
>> @@ -24,6 +24,7 @@ enum {
>>   enum dw_hdmi_devtype {
>>   	IMX6Q_HDMI,
>>   	IMX6DL_HDMI,
>> +	RK3229_HDMI,
>>   	RK3288_HDMI,
>>   };
>>   
>> @@ -54,6 +55,8 @@ struct dw_hdmi_plat_data {
>>   	const struct dw_hdmi_phy_config *phy_config;
>>   	enum drm_mode_status (*mode_valid)(const struct dw_hdmi_plat_data *pd,
>>   					   struct drm_display_mode *mode);
>> +	int (*extphy_config)(const struct dw_hdmi_plat_data *plat_data,
>> +			     int res_idx, int pixelclock);
>>   };
>>   
>>   void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);
>
>
>
>



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support
  2016-01-07 10:15     ` Yakir Yang
@ 2016-01-07 16:50       ` Philipp Zabel
  2016-01-08  9:13         ` Yakir Yang
  0 siblings, 1 reply; 11+ messages in thread
From: Philipp Zabel @ 2016-01-07 16:50 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Mark Yao, Heiko Stuebner, Russell King, Andy Yan, David Airlie,
	Rob Herring, Kumar Gala, Zheng Yang, dri-devel, devicetree,
	linux-kernel, linux-rockchip, linux-arm-kernel

Hi Yakir,

Am Donnerstag, den 07.01.2016, 18:15 +0800 schrieb Yakir Yang:
> Hi Philipp,
> 
> Thanks for your fast respond :)
> 
> On 01/07/2016 06:04 PM, Philipp Zabel wrote:
> > Am Donnerstag, den 07.01.2016, 17:02 +0800 schrieb Yakir Yang:
> >> RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
> >> the max output resolution is 4K.
> >>
> >> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> > It sounds like the INNO HDMI2.0 phy is not necessarily specific to
> > RK3229 but might also appear in other SoCs? If so, I think this should
> > be implemented in a separate phy driver and be used by dw_hdmi-rockchip.
> 
> Do you mean I should create a new phy driver that place in "driver/phy" 
> directly ?

Possibly, yes. The exynos video phys are already there. I have kept the
mediatek dsi/hdmi phys together with the DRM driver, but I suppose I
could move them there, too.

> I have think about this idea, and it would make things much clean. But 
> INNO PHY
> driver need the target pixel clock in drm_display_mode, I didn't find a 
> good way
> to pass this variable to separate phy driver. Do you have some idea ?

We'd need to extend the PHY API for this. For the mediatek phys we have
side-stepped the issue by wiring up the PLL output to the common clock
framework.
I expect besides the pixel clock frequency, it might also be necessary
to inform the PHY about cycles per pixel for deep color modes.

regards
Philipp


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support
  2016-01-07 16:50       ` Philipp Zabel
@ 2016-01-08  9:13         ` Yakir Yang
  0 siblings, 0 replies; 11+ messages in thread
From: Yakir Yang @ 2016-01-08  9:13 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Mark Yao, Heiko Stuebner, Russell King, Andy Yan, David Airlie,
	Rob Herring, Kumar Gala, Zheng Yang, dri-devel, devicetree,
	linux-kernel, linux-rockchip, linux-arm-kernel

Hi Philipp,

On 01/08/2016 12:50 AM, Philipp Zabel wrote:
> Hi Yakir,
>
> Am Donnerstag, den 07.01.2016, 18:15 +0800 schrieb Yakir Yang:
>> Hi Philipp,
>>
>> Thanks for your fast respond :)
>>
>> On 01/07/2016 06:04 PM, Philipp Zabel wrote:
>>> Am Donnerstag, den 07.01.2016, 17:02 +0800 schrieb Yakir Yang:
>>>> RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
>>>> the max output resolution is 4K.
>>>>
>>>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>>> It sounds like the INNO HDMI2.0 phy is not necessarily specific to
>>> RK3229 but might also appear in other SoCs? If so, I think this should
>>> be implemented in a separate phy driver and be used by dw_hdmi-rockchip.
>> Do you mean I should create a new phy driver that place in "driver/phy"
>> directly ?
> Possibly, yes. The exynos video phys are already there. I have kept the
> mediatek dsi/hdmi phys together with the DRM driver, but I suppose I
> could move them there, too.
>
>> I have think about this idea, and it would make things much clean. But
>> INNO PHY
>> driver need the target pixel clock in drm_display_mode, I didn't find a
>> good way
>> to pass this variable to separate phy driver. Do you have some idea ?
> We'd need to extend the PHY API for this. For the mediatek phys we have
> side-stepped the issue by wiring up the PLL output to the common clock
> framework.

Wow, I have look at your "drm/mediatek: Add HDMI support" patch, it's 
great to
registers a common clock framework.

> I expect besides the pixel clock frequency, it might also be necessary
> to inform the PHY about cycles per pixel for deep color modes.

INNO PHY didn't need the color depth directly, driver could get the 
input pixel
clock rate, and then hdmi core driver could set TMDS rate though common
clock framework.

Anyway it's great material, I would update the new version out.

Thanks a lot
- Yakir

> regards
> Philipp
>
>
>
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH v2 4/4] dt-bindings: add document for rk3229-hdmi
  2016-01-07  9:05 ` [RFC PATCH v2 4/4] dt-bindings: add document for rk3229-hdmi Yakir Yang
@ 2016-01-11  2:06   ` Rob Herring
  2016-01-11  3:48     ` Yakir Yang
  0 siblings, 1 reply; 11+ messages in thread
From: Rob Herring @ 2016-01-11  2:06 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Mark Yao, Heiko Stuebner, Russell King, Philipp Zabel, Andy Yan,
	David Airlie, Kumar Gala, Zheng Yang, dri-devel, devicetree,
	linux-kernel, linux-rockchip, linux-arm-kernel

On Thu, Jan 07, 2016 at 05:05:11PM +0800, Yakir Yang wrote:
> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> ---
> Changes in v2: None
> 
>  .../devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt         | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
> index 668091f..1cdc627 100644
> --- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
> +++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
> @@ -2,7 +2,7 @@ Rockchip specific extensions to the Synopsys Designware HDMI
>  ================================
>  
>  Required properties:
> -- compatible: "rockchip,rk3288-dw-hdmi";
> +- compatible: "rockchip,rk3288-dw-hdmi", "rockchip,rk3229-dw-hdmi";
>  - reg: Physical base address and length of the controller's registers.
>  - clocks: phandle to hdmi iahb and isfr clocks.
>  - clock-names: should be "iahb" "isfr"
> @@ -15,8 +15,10 @@ Required properties:
>    rk3288 platform
>  
>  Optional properties
> +- reg: Physical base address and length of the external HDMI PHY's registers.

This is already required...

>  - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
>  - clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
> +		       phandle to the external HDMI PHY clock, name should be "extphy"

Does this change apply to rk3288 or just rk3229? If the latter, you 
should make that clear. The driver needs to allow for old dtb's that 
only have 1 clock.

Based on Philipp's feedback, you also need to add a phys property.

Rob

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH v2 4/4] dt-bindings: add document for rk3229-hdmi
  2016-01-11  2:06   ` Rob Herring
@ 2016-01-11  3:48     ` Yakir Yang
  0 siblings, 0 replies; 11+ messages in thread
From: Yakir Yang @ 2016-01-11  3:48 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Yao, Heiko Stuebner, Russell King, Philipp Zabel, Andy Yan,
	David Airlie, Kumar Gala, Zheng Yang, dri-devel, devicetree,
	linux-kernel, linux-rockchip, linux-arm-kernel

Hi Rob,

On 01/11/2016 10:06 AM, Rob Herring wrote:
> On Thu, Jan 07, 2016 at 05:05:11PM +0800, Yakir Yang wrote:
>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>> ---
>> Changes in v2: None
>>
>>   .../devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt         | 4 +++-
>>   1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
>> index 668091f..1cdc627 100644
>> --- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
>> +++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
>> @@ -2,7 +2,7 @@ Rockchip specific extensions to the Synopsys Designware HDMI
>>   ================================
>>   
>>   Required properties:
>> -- compatible: "rockchip,rk3288-dw-hdmi";
>> +- compatible: "rockchip,rk3288-dw-hdmi", "rockchip,rk3229-dw-hdmi";
>>   - reg: Physical base address and length of the controller's registers.
>>   - clocks: phandle to hdmi iahb and isfr clocks.
>>   - clock-names: should be "iahb" "isfr"
>> @@ -15,8 +15,10 @@ Required properties:
>>     rk3288 platform
>>   
>>   Optional properties
>> +- reg: Physical base address and length of the external HDMI PHY's registers.
> This is already required...
>

The original reg only include the DW HDMI controller registers:
         reg = <0x200a0000 0x20000>;

But for now, I need to pass the external HDMI PHY registers:
         reg = <0x200a0000 0x20000>, <0x12030000 0x10000>;

>>   - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
>>   - clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
>> +		       phandle to the external HDMI PHY clock, name should be "extphy"
> Does this change apply to rk3288 or just rk3229? If the latter, you
> should make that clear. The driver needs to allow for old dtb's that
> only have 1 clock.

It's the latter one, driver have allowed that by judging the device type,
but I should make the document more clear.

> Based on Philipp's feedback, you also need to add a phys property.

Yes, I'm preparing the new patches that create a new separate phy
driver for external HDMI phy, there would be much difference from
this version.

Thanks for your respond,
- Yakir

>
> Rob
>
>
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2016-01-11  3:48 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-07  8:53 [RFC PATCH v2 0/4] Add RK3229 HDMI support Yakir Yang
2016-01-07  8:57 ` [RFC PATCH v2 1/4] drm: dw-hdmi: make it easy to recovery the platform data for platform driver Yakir Yang
2016-01-07  9:00 ` [RFC PATCH v2 2/4] drm: dw-hdmi: passing the "plat_data" when calling platform mode_valid Yakir Yang
2016-01-07  9:02 ` [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support Yakir Yang
2016-01-07 10:04   ` Philipp Zabel
2016-01-07 10:15     ` Yakir Yang
2016-01-07 16:50       ` Philipp Zabel
2016-01-08  9:13         ` Yakir Yang
2016-01-07  9:05 ` [RFC PATCH v2 4/4] dt-bindings: add document for rk3229-hdmi Yakir Yang
2016-01-11  2:06   ` Rob Herring
2016-01-11  3:48     ` Yakir Yang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).