From aeb42ff3a2d04e19ff87b1e537e0d84d63132c11 Mon Sep 17 00:00:00 2001 From: Urja Rannikko Date: Wed, 22 Aug 2018 18:36:05 +0000 Subject: [PATCH] RK3288 HDMI clock hacks combined This is only for my patching convenience, contains these: clk: rockchip: improve rk3288 pll rates for better hdmi output dt-bindings: clock: rk3288-cru: Add property to dedicate NPLL for VOPx drivers: clk-rk3288: support for dedicating NPLL to a VOP dt-bindings: display/rockchip: dw_hdmi: Add property for HDMI frequency list drm: dw_hdmi-rockchip: better clock selection logic and dts-based rate list dts: rk3288: support for dedicating npll to a vop dts: rk3288-veyron-chromebook: dedicate npll to VOP0/HDMI + HDMI rates Signed-off-by: Urja Rannikko --- .../bindings/clock/rockchip,rk3288-cru.txt | 3 + .../display/rockchip/dw_hdmi-rockchip.txt | 1 + .../boot/dts/rk3288-veyron-chromebook.dtsi | 67 +++++ arch/arm/boot/dts/rk3288.dtsi | 6 +- drivers/clk/rockchip/clk-rk3288.c | 98 +++++-- drivers/clk/rockchip/clk.h | 3 + drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 269 ++++++++++++------ 7 files changed, 335 insertions(+), 112 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/rockchip,rk3288-cru.txt b/Documentation/devicetree/bindings/clock/rockchip,rk3288-cru.txt index 8cb47c39ba53..20724584e0a4 100644 --- a/Documentation/devicetree/bindings/clock/rockchip,rk3288-cru.txt +++ b/Documentation/devicetree/bindings/clock/rockchip,rk3288-cru.txt @@ -16,6 +16,9 @@ Optional Properties: - rockchip,grf: phandle to the syscon managing the "general register files" If missing pll rates are not changeable, due to the missing pll lock status. +- rockchip,npll-for-vop: u32 0 or 1, dedicates NPLL to a VOP output unit for + more frequency flexibility for the selected VOP output at a cost of + flexibility for other devices. Disabled if not present or -1. Each clock is assigned an identifier and client nodes can use this identifier to specify the clock which they consume. All available clocks are defined as diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt index adc94fc3c9f8..edeacbbc4f26 100644 --- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt +++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt @@ -34,6 +34,7 @@ Optional properties - clock-names: May contain "cec" as defined in dw_hdmi.txt. - clock-names: May contain "grf", power for grf io. - clock-names: May contain "vpll", external clock for some hdmi phy. +- rockchip,hdmi-rates-hz: List of allowed HDMI frequencies in Hz. Example: diff --git a/arch/arm/boot/dts/rk3288-veyron-chromebook.dtsi b/arch/arm/boot/dts/rk3288-veyron-chromebook.dtsi index b16d570ff029..70ed0b2a9549 100644 --- a/arch/arm/boot/dts/rk3288-veyron-chromebook.dtsi +++ b/arch/arm/boot/dts/rk3288-veyron-chromebook.dtsi @@ -163,6 +163,25 @@ }; }; +&cru { + /* Dedicate NPLL for VOP0 / VOP_BIG for HDMI. */ + rockchip,npll-for-vop = <0>; + /* The first assigned clocks are DCLK_VOP0 and DCLK_VOP1 */ + assigned-clock-parents = <&cru PLL_NPLL>, <&cru PLL_GPLL>; +}; + +/* Delete the nodes that allow non-desirable VOP - connector links. That + * is the eDP cannot use vopb and HDMI cannot use vopl. */ + +/delete-node/ &edp_in_vopb; +/delete-node/ &vopb_out_edp; +/delete-node/ &hdmi_in_vopl; +/delete-node/ &vopl_out_hdmi; + +/* Delete the 500 Mhz GPU opp since that cannot be easily made + * without NPLL. */ +/delete-node/ &{/gpu-opp-table/opp@500000000}; + &edp { status = "okay"; @@ -186,6 +205,54 @@ status = "okay"; }; +&hdmi { + /* These depend on NPLL being dedicated to HDMI use. */ + rockchip,hdmi-rates-hz = < + 25176471 /* for 25.175 MHz, 0.006% off */ + 25200000 + 27000000 + 28320000 + 30240000 + 31500000 + 32000000 + 33750000 + 36000000 + 40000000 + 49500000 + 50000000 + 54000000 + 57290323 /* for 57.284 MHz, .011 % off */ + 65000000 + 68250000 + 71000000 + 72000000 + 73250000 + 74250000 + 74437500 /* for 74.44 MHz, .003% off */ + 75000000 + 78750000 + 78800000 + 79500000 + 83500000 + 85500000 + 88750000 + 97750000 + 101000000 + 106500000 + 108000000 + 115500000 + 118666667 /* for 118.68 MHz, .011% off */ + 119000000 + 121714286 /* for 121.75 MHz, .029% off */ + 135000000 + 136800000 /* for 136.75 MHz, .037% off */ + 146250000 + 148500000 + 154000000 + 162000000 >; +}; + + &gpio_keys { pinctrl-0 = <&pwr_key_l &ap_lid_int_l>; lid { diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index d7e49d29ace5..2106ddfdaf06 100644 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -840,12 +840,14 @@ rockchip,grf = <&grf>; #clock-cells = <1>; #reset-cells = <1>; - assigned-clocks = <&cru PLL_GPLL>, <&cru PLL_CPLL>, + assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>, + <&cru PLL_GPLL>, <&cru PLL_CPLL>, <&cru PLL_NPLL>, <&cru ACLK_CPU>, <&cru HCLK_CPU>, <&cru PCLK_CPU>, <&cru ACLK_PERI>, <&cru HCLK_PERI>, <&cru PCLK_PERI>; - assigned-clock-rates = <594000000>, <400000000>, + assigned-clock-rates = <0>, <0>, + <594000000>, <400000000>, <500000000>, <300000000>, <150000000>, <75000000>, <300000000>, <150000000>, diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index 450de24a1b42..7adf85e43078 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -83,22 +83,43 @@ static struct rockchip_pll_rate_table rk3288_pll_rates[] = { RK3066_PLL_RATE( 768000000, 1, 64, 2), RK3066_PLL_RATE( 742500000, 8, 495, 2), RK3066_PLL_RATE( 696000000, 1, 58, 2), + RK3066_PLL_RATE_NB(621000000, 1, 207, 8, 1), RK3066_PLL_RATE( 600000000, 1, 50, 2), RK3066_PLL_RATE_NB(594000000, 1, 198, 8, 1), RK3066_PLL_RATE( 552000000, 1, 46, 2), RK3066_PLL_RATE( 504000000, 1, 84, 4), RK3066_PLL_RATE( 500000000, 3, 125, 2), RK3066_PLL_RATE( 456000000, 1, 76, 4), + RK3066_PLL_RATE( 428000000, 1, 107, 6), RK3066_PLL_RATE( 408000000, 1, 68, 4), RK3066_PLL_RATE( 400000000, 3, 100, 2), + RK3066_PLL_RATE_NB( 394000000, 1, 197, 12, 1), RK3066_PLL_RATE( 384000000, 2, 128, 4), RK3066_PLL_RATE( 360000000, 1, 60, 4), + RK3066_PLL_RATE_NB( 356000000, 1, 178, 12, 1), + RK3066_PLL_RATE_NB( 324000000, 1, 189, 14, 1), RK3066_PLL_RATE( 312000000, 1, 52, 4), - RK3066_PLL_RATE( 300000000, 1, 50, 4), - RK3066_PLL_RATE( 297000000, 2, 198, 8), + RK3066_PLL_RATE_NB( 308000000, 1, 154, 12, 1), + RK3066_PLL_RATE_NB( 303000000, 1, 202, 16, 1), + RK3066_PLL_RATE( 300000000, 1, 75, 6), + RK3066_PLL_RATE_NB( 297750000, 2, 397, 16, 1), + RK3066_PLL_RATE_NB( 293250000, 2, 391, 16, 1), + RK3066_PLL_RATE_NB( 292500000, 1, 195, 16, 1), + RK3066_PLL_RATE( 273600000, 1, 114, 10), + RK3066_PLL_RATE_NB( 273000000, 1, 182, 16, 1), + RK3066_PLL_RATE_NB( 270000000, 1, 180, 16, 1), + RK3066_PLL_RATE_NB( 266250000, 2, 355, 16, 1), + RK3066_PLL_RATE_NB( 256500000, 1, 171, 16, 1), RK3066_PLL_RATE( 252000000, 1, 84, 8), - RK3066_PLL_RATE( 216000000, 1, 72, 8), - RK3066_PLL_RATE( 148500000, 2, 99, 8), + RK3066_PLL_RATE_NB( 250500000, 1, 167, 16, 1), + RK3066_PLL_RATE_NB( 243428571, 1, 142, 14, 1), + RK3066_PLL_RATE( 238000000, 1, 119, 12), + RK3066_PLL_RATE_NB( 219750000, 2, 293, 16, 1), + RK3066_PLL_RATE_NB( 216000000, 1, 144, 16, 1), + RK3066_PLL_RATE_NB( 213000000, 1, 142, 16, 1), + RK3066_PLL_RATE( 195428571, 1, 114, 14), + RK3066_PLL_RATE( 160000000, 1, 80, 12), + RK3066_PLL_RATE( 157500000, 1, 105, 16), RK3066_PLL_RATE( 126000000, 1, 84, 16), RK3066_PLL_RATE( 48000000, 1, 64, 32), { /* sentinel */ }, @@ -177,10 +198,14 @@ PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr" }; PNAME(mux_aclk_cpu_src_p) = { "cpll_aclk_cpu", "gpll_aclk_cpu" }; PNAME(mux_pll_src_cpll_gpll_p) = { "cpll", "gpll" }; -PNAME(mux_pll_src_npll_cpll_gpll_p) = { "npll", "cpll", "gpll" }; -PNAME(mux_pll_src_cpll_gpll_npll_p) = { "cpll", "gpll", "npll" }; +PNAME_ED(mux_pll_src_npll_cpll_gpll_p) = { "npll", "cpll", "gpll" }; + +PNAME_ED(mux_pll_src_cgn_pll_nonvop_p) = { "cpll", "gpll", "npll" }; +PNAME_ED(mux_pll_src_cgn_pll_vop0_p) = { "cpll", "gpll", "npll" }; +PNAME_ED(mux_pll_src_cgn_pll_vop1_p) = { "cpll", "gpll", "npll" }; + PNAME(mux_pll_src_cpll_gpll_usb480m_p) = { "cpll", "gpll", "usbphy480m_src" }; -PNAME(mux_pll_src_cpll_gll_usb_npll_p) = { "cpll", "gpll", "usbphy480m_src", "npll" }; +PNAME_ED(mux_pll_src_cpll_gll_usb_npll_p) = { "cpll", "gpll", "usbphy480m_src", "npll" }; PNAME(mux_mmc_src_p) = { "cpll", "gpll", "xin24m", "xin24m" }; PNAME(mux_i2s_pre_p) = { "i2s_src", "i2s_frac", "ext_i2s", "xin12m" }; @@ -426,24 +451,24 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { RK3288_CLKSEL_CON(30), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(3), 4, GFLAGS), - COMPOSITE(DCLK_VOP0, "dclk_vop0", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(DCLK_VOP0, "dclk_vop0", mux_pll_src_cgn_pll_vop0_p, 0, RK3288_CLKSEL_CON(27), 0, 2, MFLAGS, 8, 8, DFLAGS, RK3288_CLKGATE_CON(3), 1, GFLAGS), - COMPOSITE(DCLK_VOP1, "dclk_vop1", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(DCLK_VOP1, "dclk_vop1", mux_pll_src_cgn_pll_vop1_p, 0, RK3288_CLKSEL_CON(29), 6, 2, MFLAGS, 8, 8, DFLAGS, RK3288_CLKGATE_CON(3), 3, GFLAGS), COMPOSITE_NODIV(SCLK_EDP_24M, "sclk_edp_24m", mux_edp_24m_p, 0, RK3288_CLKSEL_CON(28), 15, 1, MFLAGS, RK3288_CLKGATE_CON(3), 12, GFLAGS), - COMPOSITE(SCLK_EDP, "sclk_edp", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_EDP, "sclk_edp", mux_pll_src_cgn_pll_nonvop_p, 0, RK3288_CLKSEL_CON(28), 6, 2, MFLAGS, 0, 6, DFLAGS, RK3288_CLKGATE_CON(3), 13, GFLAGS), - COMPOSITE(SCLK_ISP, "sclk_isp", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_ISP, "sclk_isp", mux_pll_src_cgn_pll_nonvop_p, 0, RK3288_CLKSEL_CON(6), 6, 2, MFLAGS, 0, 6, DFLAGS, RK3288_CLKGATE_CON(3), 14, GFLAGS), - COMPOSITE(SCLK_ISP_JPE, "sclk_isp_jpe", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_ISP_JPE, "sclk_isp_jpe", mux_pll_src_cgn_pll_nonvop_p, 0, RK3288_CLKSEL_CON(6), 14, 2, MFLAGS, 8, 6, DFLAGS, RK3288_CLKGATE_CON(3), 15, GFLAGS), @@ -452,16 +477,16 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { GATE(SCLK_HDMI_CEC, "sclk_hdmi_cec", "xin32k", 0, RK3288_CLKGATE_CON(5), 11, GFLAGS), - COMPOSITE(ACLK_HEVC, "aclk_hevc", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(ACLK_HEVC, "aclk_hevc", mux_pll_src_cgn_pll_nonvop_p, 0, RK3288_CLKSEL_CON(39), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(13), 13, GFLAGS), DIV(HCLK_HEVC, "hclk_hevc", "aclk_hevc", 0, RK3288_CLKSEL_CON(40), 12, 2, DFLAGS), - COMPOSITE(SCLK_HEVC_CABAC, "sclk_hevc_cabac", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_HEVC_CABAC, "sclk_hevc_cabac", mux_pll_src_cgn_pll_nonvop_p, 0, RK3288_CLKSEL_CON(42), 6, 2, MFLAGS, 0, 5, DFLAGS, RK3288_CLKGATE_CON(13), 14, GFLAGS), - COMPOSITE(SCLK_HEVC_CORE, "sclk_hevc_core", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_HEVC_CORE, "sclk_hevc_core", mux_pll_src_cgn_pll_nonvop_p, 0, RK3288_CLKSEL_CON(42), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(13), 15, GFLAGS), @@ -535,7 +560,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { COMPOSITE(0, "sclk_tspout", mux_tspout_p, 0, RK3288_CLKSEL_CON(35), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(4), 11, GFLAGS), - COMPOSITE(0, "sclk_tsp", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(0, "sclk_tsp", mux_pll_src_cgn_pll_nonvop_p, 0, RK3288_CLKSEL_CON(35), 6, 2, MFLAGS, 0, 5, DFLAGS, RK3288_CLKGATE_CON(4), 10, GFLAGS), @@ -895,6 +920,7 @@ static void __init rk3288_clk_init(struct device_node *np) { struct rockchip_clk_provider *ctx; struct clk *clk; + s32 npll_vop = -1; rk3288_cru_base = of_iomap(np, 0); if (!rk3288_cru_base) { @@ -902,6 +928,46 @@ static void __init rk3288_clk_init(struct device_node *np) return; } + if (!of_property_read_s32(np, "rockchip,npll-for-vop", &npll_vop)) { + if ((npll_vop < -1) || (npll_vop > 1)) { + pr_warn("%s: invalid VOP to dedicate NPLL to: %d\n", + __func__, npll_vop); + } else if (npll_vop >= 0) { + unsigned int vop_clk_id; + const char ** npll_names; + const char ** non_npll_names; + int i; + + /* Firstly, not-VOP needs to not use npll */ + mux_pll_src_npll_cpll_gpll_p[0] = "dummy_npll"; + mux_pll_src_cgn_pll_nonvop_p[2] = "dummy_npll"; + mux_pll_src_cpll_gll_usb_npll_p[3] = "dummy_npll"; + + /* Then the npll VOP needs to only use npll, and the other one not use npll. */ + if (npll_vop) { + vop_clk_id = DCLK_VOP1; + npll_names = mux_pll_src_cgn_pll_vop1_p; + non_npll_names = mux_pll_src_cgn_pll_vop0_p; + } else { + vop_clk_id = DCLK_VOP0; + npll_names = mux_pll_src_cgn_pll_vop0_p; + non_npll_names = mux_pll_src_cgn_pll_vop1_p; + } + npll_names[0] = "dummy_cpll"; + npll_names[1] = "dummy_gpll"; + non_npll_names[2] = "dummy_npll"; + + /* Lastly the npll-dedicated-VOP needs to be able to control npll. */ + for (i = 0; i < ARRAY_SIZE(rk3288_clk_branches); i++) { + if (rk3288_clk_branches[i].id == vop_clk_id) { + rk3288_clk_branches[i].flags |= CLK_SET_RATE_PARENT; + break; + } + } + pr_debug("%s: npll dedicated for VOP %d\n", __func__, npll_vop); + } + } + ctx = rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS); if (IS_ERR(ctx)) { pr_err("%s: rockchip clk init failed\n", __func__); diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index ef601dded32c..8e2f70d62f03 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -343,6 +343,9 @@ struct clk *rockchip_clk_register_muxgrf(const char *name, #define PNAME(x) static const char *const x[] __initconst +/* For when you want to be able to modify the pointers. */ +#define PNAME_ED(x) static const char * x[] __initdata + enum rockchip_clk_branch_type { branch_composite, branch_mux, diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 11309a2a4e43..740b0aeea796 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -49,122 +49,141 @@ struct rockchip_hdmi { struct clk *vpll_clk; struct clk *grf_clk; struct dw_hdmi *hdmi; + 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)) { @@ -192,26 +211,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 = { @@ -227,7 +275,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, @@ -280,6 +360,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, @@ -294,7 +375,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, @@ -308,7 +388,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, @@ -387,6 +466,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); } @@ -399,6 +479,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); } -- 2.18.0