[U-Boot] [PATCH v2 4/5] rockchip: video: rk3399: add HDMI TX support on the RK3399

Philipp Tomsich philipp.tomsich at theobroma-systems.com
Fri Apr 28 16:34:00 UTC 2017


This commit enables the RK3399 HDMI TX, which is very similar to the
one found on the RK3288. The differences between the two SoCs (mainly
the input VOP selection) is abstracted away through the driverdata.

Note that the I2C communication for reading the EDID works well with
the default settings, but does not with the alternate settings used on
the RK3288... so this configuration aspect also is part of the
driverdata.

Having some sort of DTS-based configuration for the regulator
dependencies would be nice for the future, but for now we simply use
lists of regulator names (also via driverdata) that we probe.

Signed-off-by: Philipp Tomsich <philipp.tomsich at theobroma-systems.com>
---

Changes in v2:
- removed DEBUG from the patch (as was done in our production branches,
  but missing from the patch-prep branch)
- updated SJG's comment (with a TODO for the RK3288) to reflect the
  new code structure

 arch/arm/include/asm/arch-rockchip/grf_rk3399.h |   5 +
 drivers/video/rockchip/rk_hdmi.c                | 164 +++++++++++++++++++-----
 2 files changed, 134 insertions(+), 35 deletions(-)

diff --git a/arch/arm/include/asm/arch-rockchip/grf_rk3399.h b/arch/arm/include/asm/arch-rockchip/grf_rk3399.h
index 22d8d97..ddd558e 100644
--- a/arch/arm/include/asm/arch-rockchip/grf_rk3399.h
+++ b/arch/arm/include/asm/arch-rockchip/grf_rk3399.h
@@ -462,6 +462,11 @@ enum {
 	GRF_UART_DBG_SEL_MASK	= 3 << GRF_UART_DBG_SEL_SHIFT,
 	GRF_UART_DBG_SEL_C	= 2,
 
+	/* GRF_SOC_CON20 */
+	GRF_RK3399_HDMI_VOP_SEL_MASK = 1 << 6,
+	GRF_RK3399_HDMI_VOP_SEL_B = 0 << 6,
+	GRF_RK3399_HDMI_VOP_SEL_L = 1 << 6,
+
 	/*  PMUGRF_GPIO0A_IOMUX */
 	PMUGRF_GPIO0A6_SEL_SHIFT        = 12,
 	PMUGRF_GPIO0A6_SEL_MASK = 3 << PMUGRF_GPIO0A6_SEL_SHIFT,
diff --git a/drivers/video/rockchip/rk_hdmi.c b/drivers/video/rockchip/rk_hdmi.c
index db07588..5d11ab8 100644
--- a/drivers/video/rockchip/rk_hdmi.c
+++ b/drivers/video/rockchip/rk_hdmi.c
@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
  * Copyright (c) 2015 Google, Inc
  * Copyright 2014 Rockchip Inc.
  *
@@ -16,12 +17,25 @@
 #include <asm/gpio.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
+#include <asm/arch/hardware.h>
 #include <asm/arch/grf_rk3288.h>
+#include <asm/arch/grf_rk3399.h>
 #include <power/regulator.h>
 
 struct rk_hdmi_priv {
 	struct dw_hdmi hdmi;
-	struct rk3288_grf *grf;
+	void *grf;
+};
+
+struct rkhdmi_driverdata {
+	/* configuration */
+	u8 i2c_clk_high;
+	u8 i2c_clk_low;
+	const char * const *regulator_names;
+	u32 regulator_names_cnt;
+	/* setters/getters */
+	int (*set_input_vop)(struct udevice *dev);
+	int (*clk_config)(struct udevice *dev);
 };
 
 static const struct hdmi_phy_config rockchip_phy_config[] = {
@@ -65,6 +79,60 @@ static const struct hdmi_mpll_config rockchip_mpll_cfg[] = {
 	}
 };
 
+static int rk3288_set_input_vop(struct udevice *dev)
+{
+	struct rk_hdmi_priv *priv = dev_get_priv(dev);
+	struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
+	int vop_id = uc_plat->source_id;
+	struct rk3288_grf *grf = priv->grf;
+
+	/* hdmi source select hdmi controller */
+	rk_setreg(&grf->soc_con6, 1 << 15);
+
+	/* hdmi data from vop id */
+	rk_clrsetreg(&grf->soc_con6, 1 << 4, (vop_id == 1) ? (1 << 4) : 0);
+
+	return 0;
+}
+
+static int rk3399_set_input_vop(struct udevice *dev)
+{
+	struct rk_hdmi_priv *priv = dev_get_priv(dev);
+	struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
+	int vop_id = uc_plat->source_id;
+	struct rk3399_grf_regs *grf = priv->grf;
+
+	/* hdmi data from vop id */
+	rk_clrsetreg(&grf->soc_con20, GRF_RK3399_HDMI_VOP_SEL_MASK,
+		     (vop_id == 1) ? GRF_RK3399_HDMI_VOP_SEL_L : 0);
+
+	return 0;
+}
+
+static int rk3288_clk_config(struct udevice *dev)
+{
+	struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
+	struct clk clk;
+	int ret;
+
+	/*
+	 * Configure the maximum clock to permit whatever resolution the
+	 * monitor wants
+	 */
+	ret = clk_get_by_index(uc_plat->src_dev, 0, &clk);
+	if (ret >= 0) {
+		ret = clk_set_rate(&clk, 384000000);
+		clk_free(&clk);
+	}
+	if (ret < 0) {
+		debug("%s: Failed to set clock in source device '%s': ret=%d\n",
+		      __func__, uc_plat->src_dev->name, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 static int rk_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size)
 {
 	struct rk_hdmi_priv *priv = dev_get_priv(dev);
@@ -83,20 +151,16 @@ static int rk_hdmi_enable(struct udevice *dev, int panel_bpp,
 static int rk_hdmi_ofdata_to_platdata(struct udevice *dev)
 {
 	struct rk_hdmi_priv *priv = dev_get_priv(dev);
+	struct rkhdmi_driverdata *data =
+		(struct rkhdmi_driverdata *)dev_get_driver_data(dev);
 	struct dw_hdmi *hdmi = &priv->hdmi;
 
 	hdmi->ioaddr = (ulong)dev_get_addr(dev);
 	hdmi->mpll_cfg = rockchip_mpll_cfg;
 	hdmi->phy_cfg = rockchip_phy_config;
-	hdmi->i2c_clk_high = 0x7a;
-	hdmi->i2c_clk_low = 0x8d;
+	hdmi->i2c_clk_high = data->i2c_clk_high;
+	hdmi->i2c_clk_low = data->i2c_clk_low;
 
-	/*
-	 * TODO(sjg at chromium.org): The above values don't work - these ones
-	 * work better, but generate lots of errors in the data.
-	 */
-	hdmi->i2c_clk_high = 0x0d;
-	hdmi->i2c_clk_low = 0x0d;
 	hdmi->reg_io_width = 4;
 	hdmi->phy_set = dw_hdmi_phy_cfg;
 
@@ -107,13 +171,15 @@ static int rk_hdmi_ofdata_to_platdata(struct udevice *dev)
 
 static int rk_hdmi_probe(struct udevice *dev)
 {
-	struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
+	struct rkhdmi_driverdata *data =
+		(struct rkhdmi_driverdata *)dev_get_driver_data(dev);
 	struct rk_hdmi_priv *priv = dev_get_priv(dev);
 	struct dw_hdmi *hdmi = &priv->hdmi;
 	struct udevice *reg;
 	struct clk clk;
+	const char *regulator_name;
 	int ret;
-	int vop_id = uc_plat->source_id;
+	int i;
 
 	ret = clk_get_by_index(dev, 0, &clk);
 	if (ret >= 0) {
@@ -125,33 +191,27 @@ static int rk_hdmi_probe(struct udevice *dev)
 		return ret;
 	}
 
-	/*
-	 * Configure the maximum clock to permit whatever resolution the
-	 * monitor wants
-	 */
-	ret = clk_get_by_index(uc_plat->src_dev, 0, &clk);
-	if (ret >= 0) {
-		ret = clk_set_rate(&clk, 384000000);
-		clk_free(&clk);
-	}
-	if (ret < 0) {
-		debug("%s: Failed to set clock in source device '%s': ret=%d\n",
-		      __func__, uc_plat->src_dev->name, ret);
-		return ret;
+	if (data->clk_config) {
+		ret = data->clk_config(dev);
+		if (ret < 0)
+			return ret;
 	}
 
-	ret = regulator_get_by_platname("vcc50_hdmi", &reg);
-	if (!ret)
-		ret = regulator_set_enable(reg, true);
-	if (ret)
-		debug("%s: Cannot set regulator vcc50_hdmi\n", __func__);
+	for (i = 0; i < data->regulator_names_cnt; ++i) {
+		regulator_name = data->regulator_names[i];
+		debug("%s: probing regulator '%s'\n", __func__, regulator_name);
 
-	/* hdmi source select hdmi controller */
-	rk_setreg(&priv->grf->soc_con6, 1 << 15);
+		ret = regulator_autoset_by_name(regulator_name, &reg);
+		if (!ret)
+			ret = regulator_set_enable(reg, true);
+	}
 
-	/* hdmi data from vop id */
-	rk_clrsetreg(&priv->grf->soc_con6, 1 << 4,
-		     (vop_id == 1) ? (1 << 4) : 0);
+	if (!data->set_input_vop) {
+		debug("%s: data->set_input_vop not set\n", __func__);
+		return -1;
+	}
+
+	data->set_input_vop(dev);
 
 	ret = dw_hdmi_phy_wait_for_hpd(hdmi);
 	if (ret < 0) {
@@ -170,8 +230,42 @@ static const struct dm_display_ops rk_hdmi_ops = {
 	.enable = rk_hdmi_enable,
 };
 
+static const char * const rk3288_regulator_names[] = {
+	"vcc50_hdmi",
+};
+
+static const struct rkhdmi_driverdata rk3288_driverdata = {
+	/*
+	 * TODO(sjg at chromium.org): The values used for the RK3399
+	 * (before refactoring: "above values") don't work - these
+	 * ones work better, but generate lots of errors in the data.
+	 */
+	.i2c_clk_high = 0x0d,
+	.i2c_clk_low = 0x0d,
+	.regulator_names = rk3288_regulator_names,
+	.regulator_names_cnt = ARRAY_SIZE(rk3288_regulator_names),
+	.set_input_vop = rk3288_set_input_vop,
+	.clk_config = rk3288_clk_config,
+};
+
+static const char * const rk3399_regulator_names[] = {
+	"vcc1v8_hdmi",
+	"vcc0v9_hdmi"
+};
+
+static const struct rkhdmi_driverdata rk3399_driverdata = {
+	.i2c_clk_high = 0x7a,
+	.i2c_clk_low = 0x8d,
+	.regulator_names = rk3399_regulator_names,
+	.regulator_names_cnt = ARRAY_SIZE(rk3399_regulator_names),
+	.set_input_vop = rk3399_set_input_vop,
+};
+
 static const struct udevice_id rk_hdmi_ids[] = {
-	{ .compatible = "rockchip,rk3288-dw-hdmi" },
+	{ .compatible = "rockchip,rk3288-dw-hdmi",
+	  .data = (ulong)&rk3288_driverdata },
+	{ .compatible = "rockchip,rk3399-dw-hdmi",
+	  .data = (ulong)&rk3399_driverdata },
 	{ }
 };
 
-- 
1.9.1



More information about the U-Boot mailing list