From 6c86916e81fa18394d9b57b4af44f9948e100e96 Mon Sep 17 00:00:00 2001 From: "Miouyouyou (Myy)" Date: Sat, 29 Sep 2018 03:02:10 +0200 Subject: [PATCH 6/6] drm: dw_hdmi-rockchip: better clock selection logic and dts-based rate list This patch is taken from Urja Rannikko ( @urjaman ) patchset here : https://github.com/urjaman/arch-c201/blob/master/linux-c201/0020-RK3288-HDMI-clock-hacks-combined.patch https://www.spinics.net/lists/arm-kernel/msg673156.html The original description was : 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 https://www.spinics.net/lists/arm-kernel/msg673163.html This is the patch that takes into account the new property "rockchip,hdmi-rates-hz" that allows the definition of the HDMI frequencies in the DTS file. This also change a lot of HDMI frequencies definition, so that *will* require some extensive testing. Still, if it works fine, this should make tinkering the HDMI frequencies easier, in case you have a very special HDMI screen. Signed-off-by: Miouyouyou (Myy) --- diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index cdc304d4c..91c7dc07b 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -73,122 +73,141 @@ struct rockchip_hdmi { struct clk *grf_clk; struct dw_hdmi *hdmi; struct phy *phy; + u32* rates; + u32 rates_cnt; }; +#define CLK_SLOP(clk) ((clk) / 1000) +#define CLK_PLUS_SLOP(clk) ((clk) + CLK_SLOP(clk)) + #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) +/* 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 }, + }, + }, { + 36800000, { + { 0x00b3, 0x0000 }, + { 0x2153, 0x0000 }, + { 0x40a2, 0x0001 }, }, - }, { - 36000000, { - { 0x00b3, 0x0000}, - { 0x2153, 0x0000}, - { 0x40f3, 0x0000} + }, { + 46000000, { + { 0x00b3, 0x0000 }, + { 0x2142, 0x0001 }, + { 0x40a2, 0x0001 }, }, - }, { - 40000000, { - { 0x00b3, 0x0000}, - { 0x2153, 0x0000}, - { 0x40f3, 0x0000} + }, { + 61333000, { + { 0x0072, 0x0001 }, + { 0x2142, 0x0001 }, + { 0x40a2, 0x0001 }, }, - }, { - 54000000, { - { 0x0072, 0x0001}, - { 0x2142, 0x0001}, - { 0x40a2, 0x0001}, + }, { + 73600000, { + { 0x0072, 0x0001 }, + { 0x2142, 0x0001 }, + { 0x4061, 0x0002 }, }, - }, { - 65000000, { - { 0x0072, 0x0001}, - { 0x2142, 0x0001}, - { 0x40a2, 0x0001}, + }, { + 92000000, { + { 0x0072, 0x0001 }, + { 0x2145, 0x0002 }, + { 0x4061, 0x0002 }, }, - }, { - 66000000, { - { 0x013e, 0x0003}, - { 0x217e, 0x0002}, - { 0x4061, 0x0002} + }, { + 122666000, { + { 0x0051, 0x0002 }, + { 0x2145, 0x0002 }, + { 0x4061, 0x0002 }, }, - }, { - 74250000, { - { 0x0072, 0x0001}, - { 0x2145, 0x0002}, - { 0x4061, 0x0002} + }, { + 147200000, { + { 0x0051, 0x0002 }, + { 0x2145, 0x0002 }, + { 0x4064, 0x0003 }, }, - }, { - 83500000, { - { 0x0072, 0x0001}, + }, { + 184000000, { + { 0x0051, 0x0002 }, + { 0x214c, 0x0003 }, + { 0x4064, 0x0003 }, }, - }, { - 108000000, { - { 0x0051, 0x0002}, - { 0x2145, 0x0002}, - { 0x4061, 0x0002} + }, { + 226666000, { + { 0x0040, 0x0003 }, + { 0x214c, 0x0003 }, + { 0x4064, 0x0003 }, }, - }, { - 106500000, { - { 0x0051, 0x0002}, - { 0x2145, 0x0002}, - { 0x4061, 0x0002} + }, { + 272000000, { + { 0x0040, 0x0003 }, + { 0x214c, 0x0003 }, + { 0x5a64, 0x0003 }, }, - }, { - 146250000, { - { 0x0051, 0x0002}, - { 0x2145, 0x0002}, - { 0x4061, 0x0002} + }, { + 340000000, { + { 0x0040, 0x0003 }, + { 0x3b4c, 0x0003 }, + { 0x5a64, 0x0003 }, }, - }, { - 148500000, { - { 0x0051, 0x0003}, - { 0x214c, 0x0003}, - { 0x4064, 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 }, - }, { - 148500000, { 0x0000, 0x0038, 0x0038 }, - }, { - ~0UL, { 0x0000, 0x0000, 0x0000}, - } + 600000000, { 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} + { 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)) { @@ -216,26 +235,55 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) return PTR_ERR(hdmi->grf_clk); } + 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 drm_connector *connector, +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 const struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = { @@ -251,7 +299,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, @@ -307,6 +387,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, @@ -406,7 +487,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, @@ -423,7 +503,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, @@ -443,7 +522,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, @@ -460,7 +538,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, @@ -558,6 +635,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, */ if (IS_ERR(hdmi->hdmi)) { ret = PTR_ERR(hdmi->hdmi); + devm_kfree(hdmi->dev, hdmi->rates); drm_encoder_cleanup(encoder); clk_disable_unprepare(hdmi->vpll_clk); } @@ -570,6 +648,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->vpll_clk); }