From invalid-sha-id-22080c28e92f466d396bfe2be Mon Sep 17 00:00:00 2001
From: Urja Rannikko <urjaman@gmail.com>
Date: Wed, 22 Aug 2018 18:36:40 +0000
Subject: [PATCH] drm: dw_hdmi-rockchip: better clock selection logic and
 dts-based rate list

This contains traces of the following commits from the ChromeOS 3.14
tree, which improve RF/EMI performance and detach the clock selection
logic from the HDMI PHY configurations, plus support for configuring
the allowed clock rates via device tree as they are dependent on
PLL configuration and maybe even the PCB layout and other hardware things,
eg. interference to wifi or such (EMI).

Rates that were allowed previous to this patch are added as the fallback
list if no dts configuration exists.

CHROMIUM: drm: rockchip/dw_hdmi-rockchip: Adjust rockchip_mpll_cfg for 146.25
CHROMIUM: drm: rockchip/dw_hdmi-rockchip: expand the informal mpll config
CHROMIUM: drm: rockchip/dw_hdmi-rockchip: add slop to more tables
CHROMIUM: drm: rockchip/dw_hdmi-rockchip: redo rockchip hdmi to allow slop
CHROMIUM: drm: rockchip/dw_hdmi-rockchip: Use auto-generated tables
CHROMIUM: drm: rockchip/dw_hdmi-rockchip: Fixup the clock to be what we expect
CHROMIUM: drm/rockchip: hdmi: adjust cklvl & txlvl for RF/EMI
CHROMIUM: drm: rockchip/dw_hdmi-rockchip: Set cur_ctr to 0 always
CHROMIUM: drm: rockchip/dw_hdmi-rockchip: Decrease slop

Signed-off-by: Urja Rannikko <urjaman@gmail.com>
Signed-off-by: Maya Matuszczyk <maccraft123mc@gmail.com>

---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 271 +++++++++++++-------
 1 file changed, 174 insertions(+), 97 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index c14f88893868..cb7a302497a5 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -80,8 +80,13 @@ struct rockchip_hdmi {
 	struct regulator *avdd_0v9;
 	struct regulator *avdd_1v8;
 	struct phy *phy;
+	u32* rates;
+	u32 rates_cnt;
 };
 
+#define CLK_SLOP(clk)          ((clk) / 1000)
+#define CLK_PLUS_SLOP(clk)     ((clk) + CLK_SLOP(clk))
+
 static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
 {
 	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
@@ -89,118 +94,132 @@ static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
 	return container_of(rkencoder, struct rockchip_hdmi, encoder);
 }
 
+/* These were the rates allowed by the driver before rates list in device tree,
+ * so keep them around as a fallback */
+static const u32 dw_hdmi_fallback_rates[] = {
+	27000000,
+	36000000,
+	40000000,
+	54000000,
+	65000000,
+	66000000,
+	74250000,
+	83500000,
+	106500000,
+	108000000,
+	146250000,
+	148500000
+};
+
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
-		27000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+		30666000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40f3, 0x0000 },
 		},
-	}, {
-		36000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		36800000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		40000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		46000000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		54000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		61333000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		65000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		73600000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		66000000, {
-			{ 0x013e, 0x0003},
-			{ 0x217e, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		92000000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		74250000, {
-			{ 0x0072, 0x0001},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		122666000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		83500000, {
-			{ 0x0072, 0x0001},
+	},  {
+		147200000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		108000000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		184000000, {
+			{ 0x0051, 0x0002 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		106500000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		226666000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		146250000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		272000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
-		148500000, {
-			{ 0x0051, 0x0003},
-			{ 0x214c, 0x0003},
-			{ 0x4064, 0x0003}
+	},  {
+		340000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
+	},  {
+		600000000, {
+			{ 0x1a40, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
+		},
+	},  {
 		~0UL, {
-			{ 0x00a0, 0x000a },
-			{ 0x2001, 0x000f },
-			{ 0x4002, 0x000f },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
 		},
 	}
 };
 
 static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
-	/*      pixelclk    bpp8    bpp10   bpp12 */
+	/*      pixelclk      bpp8    bpp10   bpp12 */
 	{
-		40000000,  { 0x0018, 0x0018, 0x0018 },
-	}, {
-		65000000,  { 0x0028, 0x0028, 0x0028 },
-	}, {
-		66000000,  { 0x0038, 0x0038, 0x0038 },
-	}, {
-		74250000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		83500000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		146250000, { 0x0038, 0x0038, 0x0038 },
+		600000000,  { 0x0000, 0x0000, 0x0000 },
 	}, {
-		148500000, { 0x0000, 0x0038, 0x0038 },
-	}, {
-		~0UL,      { 0x0000, 0x0000, 0x0000},
+		~0UL,       { 0x0000, 0x0000, 0x0000},
 	}
 };
 
 static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
-	/*pixelclk   symbol   term   vlev*/
-	{ 74250000,  0x8009, 0x0004, 0x0272},
-	{ 148500000, 0x802b, 0x0004, 0x028d},
-	{ 297000000, 0x8039, 0x0005, 0x028d},
-	{ ~0UL,	     0x0000, 0x0000, 0x0000}
+	/*pixelclk		    symbol  term    vlev*/
+	{ CLK_PLUS_SLOP(74250000),  0x8009, 0x0004, 0x0272},
+	{ CLK_PLUS_SLOP(165000000), 0x802b, 0x0004, 0x0209},
+	{ CLK_PLUS_SLOP(297000000), 0x8039, 0x0005, 0x028d},
+	{ ~0UL,                     0x0000, 0x0000, 0x0000}
 };
 
 static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 {
 	struct device_node *np = hdmi->dev->of_node;
+	int rates_cnt;
 
 	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 	if (IS_ERR(hdmi->regmap)) {
@@ -237,27 +256,55 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 	if (IS_ERR(hdmi->avdd_1v8))
 		return PTR_ERR(hdmi->avdd_1v8);
 
+	if ((rates_cnt = of_property_count_u32_elems(np, "rockchip,hdmi-rates-hz")) > 0) {
+		int rv;
+		u32 *rates = devm_kmalloc_array(hdmi->dev, rates_cnt, sizeof(u32), GFP_KERNEL);
+		if (!rates)
+			return -ENOMEM;
+		rv = of_property_read_u32_array(np, "rockchip,hdmi-rates-hz", rates, rates_cnt);
+		if (rv)
+			return rv;
+		hdmi->rates = rates;
+		hdmi->rates_cnt = rates_cnt;
+	} else {
+		rates_cnt = ARRAY_SIZE(dw_hdmi_fallback_rates);
+		hdmi->rates = devm_kmalloc_array(hdmi->dev, rates_cnt, sizeof(u32), GFP_KERNEL);
+		if (!hdmi->rates)
+			return -ENOMEM;
+		memcpy(hdmi->rates, dw_hdmi_fallback_rates, rates_cnt * sizeof(u32));
+		hdmi->rates_cnt = rates_cnt;
+	}
+
+
 	return 0;
 }
 
 static enum drm_mode_status
-dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
-			    const struct drm_display_info *info,
+dw_hdmi_rockchip_encoder_mode_valid(struct drm_encoder *encoder,
 			    const struct drm_display_mode *mode)
 {
-	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 	int pclk = mode->clock * 1000;
-	bool valid = false;
+	int num_rates = hdmi->rates_cnt;
 	int i;
 
-	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
-		if (pclk == mpll_cfg[i].mpixelclock) {
-			valid = true;
-			break;
-		}
+	/*
+	 * Pixel clocks we support are always < 2GHz and so fit in an
+	 * int.  We should make sure source rate does too so we don't get
+	 * overflow when we multiply by 1000.
+	 */
+	if (mode->clock > INT_MAX / 1000)
+		return MODE_BAD;
+
+	for (i = 0; i < num_rates; i++) {
+		int slop = CLK_SLOP(pclk);
+
+		if ((pclk >= hdmi->rates[i] - slop) &&
+		    (pclk <= hdmi->rates[i] + slop))
+			return MODE_OK;
 	}
 
-	return (valid) ? MODE_OK : MODE_BAD;
+	return MODE_BAD;
 }
 
 static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
@@ -269,7 +316,39 @@ dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
 				    const struct drm_display_mode *mode,
 				    struct drm_display_mode *adj_mode)
 {
-	return true;
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	int pclk = adj_mode->clock * 1000;
+	int best_diff = INT_MAX;
+	int best_clock = 0;
+	int slop;
+	int i;
+
+	/* Pick the best clock */
+	for (i = 0; i < hdmi->rates_cnt; i++) {
+		int diff = hdmi->rates[i] - pclk;
+
+		if (diff < 0)
+			diff = -diff;
+		if (diff < best_diff) {
+			best_diff = diff;
+			best_clock = hdmi->rates[i];
+
+			/* Bail early if we're exact */
+			if (best_diff == 0)
+				return true;
+		}
+	}
+
+	/* Double check that it's OK */
+	slop = CLK_SLOP(pclk);
+	if ((pclk >= best_clock - slop) && (pclk <= best_clock + slop)) {
+		adj_mode->clock = DIV_ROUND_UP(best_clock, 1000);
+		return true;
+	}
+
+	/* Shoudn't be here; we should have said rate wasn't valid */
+	dev_warn(hdmi->dev, "tried to set invalid rate %d\n", adj_mode->clock);
+	return false;
 }
 
 static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
@@ -325,6 +404,7 @@ dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
 }
 
 static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
+	.mode_valid = dw_hdmi_rockchip_encoder_mode_valid,
 	.mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
 	.mode_set   = dw_hdmi_rockchip_encoder_mode_set,
 	.enable     = dw_hdmi_rockchip_encoder_enable,
@@ -425,7 +505,6 @@ static struct rockchip_hdmi_chip_data rk3228_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg = rockchip_mpll_cfg,
 	.cur_ctr = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -442,7 +521,6 @@ static struct rockchip_hdmi_chip_data rk3288_chip_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,
 	.phy_config = rockchip_phy_config,
@@ -462,7 +540,6 @@ static struct rockchip_hdmi_chip_data rk3328_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg = rockchip_mpll_cfg,
 	.cur_ctr = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -480,7 +557,6 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -493,7 +569,6 @@ static struct rockchip_hdmi_chip_data rk3568_chip_data = {
 };
 
 static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
-	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
@@ -623,6 +698,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	return 0;
 
 err_bind:
+	devm_kfree(hdmi->dev, hdmi->rates);
 	drm_encoder_cleanup(encoder);
 	clk_disable_unprepare(hdmi->ref_clk);
 err_clk:
@@ -638,6 +714,7 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 {
 	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
 
+	devm_kfree(hdmi->dev, hdmi->rates);
 	dw_hdmi_unbind(hdmi->hdmi);
 	clk_disable_unprepare(hdmi->ref_clk);
 
-- 
2.36.1

