Module Name: src Committed By: martin Date: Sat Nov 16 16:48:26 UTC 2019
Modified Files: src/sys/arch/arm/dts [netbsd-9]: rk3399-rockpro64.dts src/sys/arch/arm/rockchip [netbsd-9]: files.rockchip rk3399_cru.c rk_cru.h rk_cru_composite.c rk_i2c.c src/sys/arch/evbarm/conf [netbsd-9]: GENERIC64 src/sys/conf [netbsd-9]: files src/sys/dev/fdt [netbsd-9]: ausoc.c fdt_clock.c fdtvar.h src/sys/dev/ic [netbsd-9]: dw_hdmi.c dw_hdmi.h Added Files: src/sys/arch/arm/rockchip [netbsd-9]: rk_drm.c rk_drm.h rk_dwhdmi.c rk_fb.c rk_i2s.c rk_vop.c src/sys/dev/ic [netbsd-9]: dw_hdmi_phy.c Log Message: Pull up following revision(s) (requested by jmcneill in ticket #427): sys/dev/ic/dw_hdmi_phy.c: revision 1.2 sys/dev/ic/dw_hdmi.c: revision 1.4 sys/dev/fdt/ausoc.c: revision 1.5 sys/dev/ic/dw_hdmi.h: revision 1.2 sys/dev/ic/dw_hdmi.h: revision 1.3 sys/dev/ic/dw_hdmi.h: revision 1.4 sys/conf/files: revision 1.1242 sys/dev/fdt/fdtvar.h: revision 1.57 sys/arch/arm/rockchip/rk3399_cru.c: revision 1.11 sys/arch/arm/rockchip/rk3399_cru.c: revision 1.12 sys/arch/arm/rockchip/rk3399_cru.c: revision 1.13 sys/arch/evbarm/conf/GENERIC64: revision 1.110 sys/arch/arm/rockchip/rk_drm.c: revision 1.1 sys/arch/arm/rockchip/rk_drm.c: revision 1.2 sys/arch/evbarm/conf/GENERIC64: revision 1.112 sys/arch/arm/rockchip/rk_dwhdmi.c: revision 1.1 sys/dev/fdt/fdt_clock.c: revision 1.10 sys/arch/evbarm/conf/GENERIC64: revision 1.113 sys/arch/arm/rockchip/rk_dwhdmi.c: revision 1.2 sys/arch/arm/rockchip/rk_drm.h: revision 1.1 sys/arch/arm/rockchip/rk_dwhdmi.c: revision 1.3 sys/arch/arm/rockchip/rk_fb.c: revision 1.1 sys/arch/arm/dts/rk3399-rockpro64.dts: revision 1.9 sys/arch/arm/rockchip/rk_vop.c: revision 1.1 sys/arch/arm/rockchip/rk_vop.c: revision 1.2 sys/arch/arm/rockchip/rk_i2c.c: revision 1.6 sys/arch/arm/rockchip/rk_cru.h: revision 1.6 sys/arch/arm/rockchip/rk_cru.h: revision 1.7 sys/arch/arm/rockchip/rk_cru_composite.c: revision 1.4 sys/arch/arm/rockchip/rk_cru_composite.c: revision 1.5 sys/arch/arm/rockchip/files.rockchip: revision 1.21 sys/arch/arm/rockchip/rk_i2s.c: revision 1.1 sys/arch/arm/rockchip/files.rockchip: revision 1.22 sys/dev/ic/dw_hdmi.c: revision 1.2 sys/dev/ic/dw_hdmi_phy.c: revision 1.1 sys/dev/ic/dw_hdmi.c: revision 1.3 Support reads of more than 32 bytes in a single xfer. Add support for internal DesignWare HDMI PHYs Add fdtbus_clock_enable and fdtbus_clock_enable_index shortcuts Add HDMI and VOP clocks WIP display driver for Rockchip RK3399 Add (commented out) Rockchip display support Select the correct MPLL and PHY settings for the requested pixel clock Force DCLK_VOP0/1 dividers to 1 and select closest match when setting PLL rates. Fix typo in phy config table Fix a few swapped fields Remove debug output Enable Rockchip display support Set sysclk rate at set_format time, so the link set_format callback can read the new sysclk Add I2S audio input support. Add software volume controls. Add support for I2S clocks. Add driver for Rockchip I2S/PCM controller. Enable HDMI audio on ROCKPro64 Add rki2s Add audio support To generate a diff of this commit: cvs rdiff -u -r1.7 -r1.7.2.1 src/sys/arch/arm/dts/rk3399-rockpro64.dts cvs rdiff -u -r1.19 -r1.19.2.1 src/sys/arch/arm/rockchip/files.rockchip cvs rdiff -u -r1.8 -r1.8.4.1 src/sys/arch/arm/rockchip/rk3399_cru.c cvs rdiff -u -r1.4 -r1.4.4.1 src/sys/arch/arm/rockchip/rk_cru.h cvs rdiff -u -r1.3 -r1.3.8.1 src/sys/arch/arm/rockchip/rk_cru_composite.c cvs rdiff -u -r0 -r1.2.2.2 src/sys/arch/arm/rockchip/rk_drm.c \ src/sys/arch/arm/rockchip/rk_vop.c cvs rdiff -u -r0 -r1.1.2.2 src/sys/arch/arm/rockchip/rk_drm.h \ src/sys/arch/arm/rockchip/rk_fb.c src/sys/arch/arm/rockchip/rk_i2s.c cvs rdiff -u -r0 -r1.3.2.2 src/sys/arch/arm/rockchip/rk_dwhdmi.c cvs rdiff -u -r1.4.6.1 -r1.4.6.2 src/sys/arch/arm/rockchip/rk_i2c.c cvs rdiff -u -r1.103.2.2 -r1.103.2.3 src/sys/arch/evbarm/conf/GENERIC64 cvs rdiff -u -r1.1237.2.1 -r1.1237.2.2 src/sys/conf/files cvs rdiff -u -r1.4 -r1.4.2.1 src/sys/dev/fdt/ausoc.c cvs rdiff -u -r1.8 -r1.8.4.1 src/sys/dev/fdt/fdt_clock.c cvs rdiff -u -r1.52.2.1 -r1.52.2.2 src/sys/dev/fdt/fdtvar.h cvs rdiff -u -r1.1 -r1.1.6.1 src/sys/dev/ic/dw_hdmi.c \ src/sys/dev/ic/dw_hdmi.h cvs rdiff -u -r0 -r1.2.2.2 src/sys/dev/ic/dw_hdmi_phy.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/dts/rk3399-rockpro64.dts diff -u src/sys/arch/arm/dts/rk3399-rockpro64.dts:1.7 src/sys/arch/arm/dts/rk3399-rockpro64.dts:1.7.2.1 --- src/sys/arch/arm/dts/rk3399-rockpro64.dts:1.7 Sun Jul 28 10:03:56 2019 +++ src/sys/arch/arm/dts/rk3399-rockpro64.dts Sat Nov 16 16:48:26 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: rk3399-rockpro64.dts,v 1.7 2019/07/28 10:03:56 jmcneill Exp $ */ +/* $NetBSD: rk3399-rockpro64.dts,v 1.7.2.1 2019/11/16 16:48:26 martin Exp $ */ /*- * Copyright (c) 2019 Jared McNeill <jmcne...@invisible.ca> @@ -62,6 +62,10 @@ }; }; +&hdmi_sound { + status = "okay"; +}; + &pwm1 { status = "okay"; }; Index: src/sys/arch/arm/rockchip/files.rockchip diff -u src/sys/arch/arm/rockchip/files.rockchip:1.19 src/sys/arch/arm/rockchip/files.rockchip:1.19.2.1 --- src/sys/arch/arm/rockchip/files.rockchip:1.19 Wed May 1 10:41:33 2019 +++ src/sys/arch/arm/rockchip/files.rockchip Sat Nov 16 16:48:25 2019 @@ -1,4 +1,4 @@ -# $NetBSD: files.rockchip,v 1.19 2019/05/01 10:41:33 jmcneill Exp $ +# $NetBSD: files.rockchip,v 1.19.2.1 2019/11/16 16:48:25 martin Exp $ # # Configuration info for Rockchip family SoCs # @@ -78,6 +78,31 @@ device rkpwm: pwm attach rkpwm at fdt with rk_pwm file arch/arm/rockchip/rk_pwm.c rk_pwm +# DRM master +define rkfbbus { } +device rkdrm: drmkms, ddc_read_edid, rkfbbus +attach rkdrm at fdt with rk_drm +file arch/arm/rockchip/rk_drm.c rk_drm + +# DRM framebuffer console +device rkfb: rkfbbus, drmfb, wsemuldisplaydev +attach rkfb at rkfbbus with rk_fb +file arch/arm/rockchip/rk_fb.c rk_fb + +# Visual Output Processor +device rkvop: drmkms +attach rkvop at fdt with rk_vop +file arch/arm/rockchip/rk_vop.c rk_vop + +# HDMI TX (Designware based) +attach dwhdmi at fdt with rk_dwhdmi +file arch/arm/rockchip/rk_dwhdmi.c rk_dwhdmi + +# I2S/PCM controller +device rki2s +attach rki2s at fdt with rk_i2s +file arch/arm/rockchip/rk_i2s.c rk_i2s + # SOC parameters defflag opt_soc.h SOC_ROCKCHIP defflag opt_soc.h SOC_RK3328: SOC_ROCKCHIP Index: src/sys/arch/arm/rockchip/rk3399_cru.c diff -u src/sys/arch/arm/rockchip/rk3399_cru.c:1.8 src/sys/arch/arm/rockchip/rk3399_cru.c:1.8.4.1 --- src/sys/arch/arm/rockchip/rk3399_cru.c:1.8 Sun Jun 9 16:14:53 2019 +++ src/sys/arch/arm/rockchip/rk3399_cru.c Sat Nov 16 16:48:25 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: rk3399_cru.c,v 1.8 2019/06/09 16:14:53 jmcneill Exp $ */ +/* $NetBSD: rk3399_cru.c,v 1.8.4.1 2019/11/16 16:48:25 martin Exp $ */ /*- * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> @@ -28,7 +28,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: rk3399_cru.c,v 1.8 2019/06/09 16:14:53 jmcneill Exp $"); +__KERNEL_RCSID(1, "$NetBSD: rk3399_cru.c,v 1.8.4.1 2019/11/16 16:48:25 martin Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -271,20 +271,21 @@ rk3399_cru_pll_set_rate(struct rk_cru_so struct rk_cru_pll *pll = &clk->u.pll; const struct rk_cru_pll_rate *pll_rate = NULL; uint32_t val; - int retry; + int retry, best_diff; KASSERT(clk->type == RK_CRU_PLL); if (pll->rates == NULL || rate == 0) return EIO; - for (int i = 0; i < pll->nrates; i++) - if (pll->rates[i].rate == rate) { + best_diff = INT_MAX; + for (int i = 0; i < pll->nrates; i++) { + const int diff = (int)rate - (int)pll->rates[i].rate; + if (abs(diff) < best_diff) { pll_rate = &pll->rates[i]; - break; + best_diff = abs(diff); } - if (pll_rate == NULL) - return EINVAL; + } val = __SHIFTIN(PLL_WORK_MODE_SLOW, PLL_WORK_MODE) | (PLL_WORK_MODE << 16); CRU_WRITE(sc, pll->con_base + PLL_CON3, val); @@ -348,13 +349,23 @@ static const char * armclkb_parents[] = static const char * mux_clk_tsadc_parents[] = { "xin24m", "xin32k" }; static const char * mux_pll_src_cpll_gpll_parents[] = { "cpll", "gpll" }; static const char * mux_pll_src_cpll_gpll_npll_parents[] = { "cpll", "gpll", "npll" }; +static const char * mux_pll_src_cpll_gpll_ppll_parents[] = { "cpll", "gpll", "npll" }; static const char * mux_pll_src_cpll_gpll_upll_parents[] = { "cpll", "gpll", "upll" }; static const char * mux_pll_src_cpll_gpll_npll_24m_parents[] = { "cpll", "gpll", "npll", "xin24m" }; static const char * mux_pll_src_cpll_gpll_npll_ppll_upll_24m_parents[] = { "cpll", "gpll", "npll", "ppll", "upll", "xin24m" }; +static const char * mux_pll_src_vpll_cpll_gpll_parents[] = { "vpll", "cpll", "gpll" }; +static const char * mux_pll_src_vpll_cpll_gpll_npll_parents[] = { "vpll", "cpll", "gpll", "npll" }; static const char * mux_aclk_perilp0_parents[] = { "cpll_aclk_perilp0_src", "gpll_aclk_perilp0_src" }; static const char * mux_hclk_perilp1_parents[] = { "cpll_hclk_perilp1_src", "gpll_hclk_perilp1_src" }; static const char * mux_aclk_perihp_parents[] = { "cpll_aclk_perihp_src", "gpll_aclk_perihp_src" }; static const char * mux_aclk_cci_parents[] = { "cpll_aclk_cci_src", "gpll_aclk_cci_src", "npll_aclk_cci_src", "vpll_aclk_cci_src" }; +static const char * mux_dclk_vop0_parents[] = { "dclk_vop0_div", "dclk_vop0_frac" }; +static const char * mux_dclk_vop1_parents[] = { "dclk_vop1_div", "dclk_vop1_frac" }; +static const char * mux_i2s0_parents[] = { "clk_i2s0_div", "clk_i2s0_frac", "clkin_i2s", "xin12m" }; +static const char * mux_i2s1_parents[] = { "clk_i2s1_div", "clk_i2s1_frac", "clkin_i2s", "xin12m" }; +static const char * mux_i2s2_parents[] = { "clk_i2s2_div", "clk_i2s2_frac", "clkin_i2s", "xin12m" }; +static const char * mux_i2sch_parents[] = { "clk_i2s0", "clk_i2s1", "clk_i2s2" }; +static const char * mux_i2sout_parents[] = { "clk_i2sout_src", "xin12m" }; static const char * mux_uart0_parents[] = { "clk_uart0_div", "clk_uart0_frac", "xin24m" }; static const char * mux_uart1_parents[] = { "clk_uart1_div", "clk_uart1_frac", "xin24m" }; static const char * mux_uart2_parents[] = { "clk_uart2_div", "clk_uart2_frac", "xin24m" }; @@ -403,7 +414,7 @@ static struct rk_cru_clk rk3399_cru_clks __BIT(31), /* lock_mask */ pll_rates), RK3399_PLL(RK3399_PLL_VPLL, "vpll", pll_parents, - PLL_CON(43), /* con_base */ + PLL_CON(48), /* con_base */ PLL_CON(51), /* mode_reg */ __BIT(8), /* mode_mask */ __BIT(31), /* lock_mask */ @@ -796,18 +807,196 @@ static struct rk_cru_clk rk3399_cru_clks __BIT(1), /* gate_mask */ RK_COMPOSITE_ROUND_DOWN), RK_GATE(RK3399_PCLK_TSADC, "pclk_tsadc", "pclk_perilp1", CLKGATE_CON(22), 13), + + /* VOP0 */ + RK_COMPOSITE(RK3399_ACLK_VOP0_PRE, "aclk_vop0_pre", mux_pll_src_vpll_cpll_gpll_npll_parents, + CLKSEL_CON(47), /* muxdiv_reg */ + __BITS(7,6), /* mux_mask */ + __BITS(4,0), /* div_mask */ + CLKGATE_CON(10), /* gate_reg */ + __BIT(8), /* gate_mask */ + 0), + RK_COMPOSITE_NOMUX(0, "hclk_vop0_pre", "aclk_vop0_pre", + CLKSEL_CON(47), /* div_reg */ + __BITS(12,8), /* div_mask */ + CLKGATE_CON(10), /* gate_reg */ + __BIT(9), /* gate_mask */ + 0), + RK_COMPOSITE(RK3399_DCLK_VOP0_DIV, "dclk_vop0_div", mux_pll_src_vpll_cpll_gpll_parents, + CLKSEL_CON(49), /* muxdiv_reg */ + __BITS(9,8), /* mux_mask */ + __BITS(7,0), /* div_mask */ + CLKGATE_CON(10), /* gate_reg */ + __BIT(12), /* gate_mask */ + RK_COMPOSITE_SET_RATE_PARENT), + RK_GATE(RK3399_ACLK_VOP0, "aclk_vop0", "aclk_vop0_pre", CLKGATE_CON(28), 3), + RK_GATE(RK3399_HCLK_VOP0, "hclk_vop0", "hclk_vop0_pre", CLKGATE_CON(28), 2), + RK_MUX(RK3399_DCLK_VOP0, "dclk_vop0", mux_dclk_vop0_parents, CLKSEL_CON(49), __BIT(11)), + + /* VOP1 */ + RK_COMPOSITE(RK3399_ACLK_VOP1_PRE, "aclk_vop1_pre", mux_pll_src_vpll_cpll_gpll_npll_parents, + CLKSEL_CON(48), /* muxdiv_reg */ + __BITS(7,6), /* mux_mask */ + __BITS(4,0), /* div_mask */ + CLKGATE_CON(10), /* gate_reg */ + __BIT(10), /* gate_mask */ + 0), + RK_COMPOSITE_NOMUX(0, "hclk_vop1_pre", "aclk_vop1_pre", + CLKSEL_CON(48), /* div_reg */ + __BITS(12,8), /* div_mask */ + CLKGATE_CON(10), /* gate_reg */ + __BIT(11), /* gate_mask */ + 0), + RK_COMPOSITE(RK3399_DCLK_VOP1_DIV, "dclk_vop1_div", mux_pll_src_vpll_cpll_gpll_parents, + CLKSEL_CON(50), /* muxdiv_reg */ + __BITS(9,8), /* mux_mask */ + __BITS(7,0), /* div_mask */ + CLKGATE_CON(10), /* gate_reg */ + __BIT(13), /* gate_mask */ + RK_COMPOSITE_SET_RATE_PARENT), + RK_GATE(RK3399_ACLK_VOP1, "aclk_vop1", "aclk_vop1_pre", CLKGATE_CON(28), 7), + RK_GATE(RK3399_HCLK_VOP1, "hclk_vop1", "hclk_vop1_pre", CLKGATE_CON(28), 6), + RK_MUX(RK3399_DCLK_VOP1, "dclk_vop1", mux_dclk_vop1_parents, CLKSEL_CON(50), __BIT(11)), + + /* VIO */ + RK_COMPOSITE(RK3399_ACLK_VIO, "aclk_vio", mux_pll_src_cpll_gpll_ppll_parents, + CLKSEL_CON(42), /* muxdiv_reg */ + __BITS(7,6), /* mux_mask */ + __BITS(4,0), /* div_mask */ + CLKGATE_CON(11), /* gate_reg */ + __BIT(0), /* gate_mask */ + 0), + RK_COMPOSITE_NOMUX(RK3399_PCLK_VIO, "pclk_vio", "aclk_vio", + CLKSEL_CON(43), /* div_reg */ + __BITS(4,0), /* div_mask */ + CLKGATE_CON(11), /* gate_reg */ + __BIT(1), /* gate_mask */ + 0), + RK_GATE(RK3399_PCLK_VIO_GRF, "pclk_vio_grf", "pclk_vio", CLKGATE_CON(29), 12), + + /* HDMI */ + RK_COMPOSITE(RK3399_ACLK_HDCP, "aclk_hdcp", mux_pll_src_cpll_gpll_ppll_parents, + CLKSEL_CON(42), /* muxdiv_reg */ + __BITS(15,14), /* mux_mask */ + __BITS(12,8), /* div_mask */ + CLKGATE_CON(11), /* gate_reg */ + __BIT(12), /* gate_mask */ + 0), + RK_COMPOSITE_NOMUX(RK3399_PCLK_HDCP, "pclk_hdcp", "aclk_hdcp", + CLKSEL_CON(43), /* div_reg */ + __BITS(14,10), /* div_mask */ + CLKGATE_CON(11), /* gate_reg */ + __BIT(10), /* gate_mask */ + 0), + RK_COMPOSITE(RK3399_SCLK_HDMI_CEC, "clk_hdmi_cec", pll_parents, + CLKSEL_CON(45), /* muxdiv_reg */ + __BIT(15), /* mux_mask */ + __BITS(9,0), /* div_mask */ + CLKGATE_CON(11), /* gate_reg */ + __BIT(7), /* gate_mask */ + 0), + RK_GATE(RK3399_PCLK_HDMI_CTRL, "pclk_hdmi_ctrl", "pclk_hdcp", CLKGATE_CON(29), 6), + RK_GATE(RK3399_SCLK_HDMI_SFR, "clk_hdmi_sfr", "xin24m", CLKGATE_CON(11), 6), + + /* I2S2 */ + RK_COMPOSITE(0, "clk_i2s0_div", mux_pll_src_cpll_gpll_parents, + CLKSEL_CON(28), /* muxdiv_reg */ + __BIT(7), /* mux_mask */ + __BITS(6,0), /* div_mask */ + CLKGATE_CON(8), /* gate_reg */ + __BIT(3), /* gate_mask */ + 0), + RK_COMPOSITE(0, "clk_i2s1_div", mux_pll_src_cpll_gpll_parents, + CLKSEL_CON(29), /* muxdiv_reg */ + __BIT(7), /* mux_mask */ + __BITS(6,0), /* div_mask */ + CLKGATE_CON(8), /* gate_reg */ + __BIT(6), /* gate_mask */ + 0), + RK_COMPOSITE(0, "clk_i2s2_div", mux_pll_src_cpll_gpll_parents, + CLKSEL_CON(30), /* muxdiv_reg */ + __BIT(7), /* mux_mask */ + __BITS(6,0), /* div_mask */ + CLKGATE_CON(8), /* gate_reg */ + __BIT(9), /* gate_mask */ + 0), + RK_COMPOSITE_FRAC(0, "clk_i2s0_frac", "clk_i2s0_div", + CLKSEL_CON(96), /* frac_reg */ + 0), + RK_COMPOSITE_FRAC(0, "clk_i2s1_frac", "clk_i2s1_div", + CLKSEL_CON(97), /* frac_reg */ + 0), + RK_COMPOSITE_FRAC(0, "clk_i2s2_frac", "clk_i2s2_div", + CLKSEL_CON(98), /* frac_reg */ + 0), + RK_MUX(0, "clk_i2s0_mux", mux_i2s0_parents, CLKSEL_CON(28), __BITS(9,8)), + RK_MUX(0, "clk_i2s1_mux", mux_i2s1_parents, CLKSEL_CON(29), __BITS(9,8)), + RK_MUX(0, "clk_i2s2_mux", mux_i2s2_parents, CLKSEL_CON(30), __BITS(9,8)), + RK_GATE(RK3399_SCLK_I2S0_8CH, "clk_i2s0", "clk_i2s0_mux", CLKGATE_CON(8), 5), + RK_GATE(RK3399_SCLK_I2S1_8CH, "clk_i2s1", "clk_i2s1_mux", CLKGATE_CON(8), 8), + RK_GATE(RK3399_SCLK_I2S2_8CH, "clk_i2s2", "clk_i2s2_mux", CLKGATE_CON(8), 11), + RK_GATE(RK3399_HCLK_I2S0_8CH, "hclk_i2s0", "hclk_perilp1", CLKGATE_CON(34), 0), + RK_GATE(RK3399_HCLK_I2S1_8CH, "hclk_i2s1", "hclk_perilp1", CLKGATE_CON(34), 1), + RK_GATE(RK3399_HCLK_I2S2_8CH, "hclk_i2s2", "hclk_perilp1", CLKGATE_CON(34), 2), + RK_MUX(0, "clk_i2sout_src", mux_i2sch_parents, CLKSEL_CON(31), __BITS(1,0)), + RK_COMPOSITE(RK3399_SCLK_I2S_8CH_OUT, "clk_i2sout", mux_i2sout_parents, + CLKSEL_CON(31), /* muxdiv_reg */ + __BIT(2), /* mux_mask */ + 0, /* div_mask */ + CLKGATE_CON(8), /* gate_reg */ + __BIT(12), /* gate_mask */ + RK_COMPOSITE_SET_RATE_PARENT), +}; + +static const struct rk3399_init_param { + const char *clk; + const char *parent; +} rk3399_init_params[] = { + { .clk = "clk_i2s0_mux", .parent = "clk_i2s0_frac" }, + { .clk = "clk_i2s1_mux", .parent = "clk_i2s1_frac" }, + { .clk = "clk_i2s2_mux", .parent = "clk_i2s2_frac" }, }; static void rk3399_cru_init(struct rk_cru_softc *sc) { - struct rk_cru_clk *clk; + struct rk_cru_clk *clk, *pclk; + uint32_t write_mask, write_val; + int error; + u_int n; /* * Force an update of BPLL to bring it out of slow mode. */ clk = rk_cru_clock_find(sc, "armclkb"); clk_set_rate(&clk->base, clk_get_rate(&clk->base)); + + /* + * Set DCLK_VOP0 and DCLK_VOP1 dividers to 1. + */ + write_mask = __BITS(7,0) << 16; + write_val = 0; + CRU_WRITE(sc, CLKSEL_CON(49), write_mask | write_val); + CRU_WRITE(sc, CLKSEL_CON(50), write_mask | write_val); + + /* + * Set defaults + */ + for (n = 0; n < __arraycount(rk3399_init_params); n++) { + const struct rk3399_init_param *param = &rk3399_init_params[n]; + clk = rk_cru_clock_find(sc, param->clk); + KASSERTMSG(clk != NULL, "couldn't find clock %s", param->clk); + if (param->parent != NULL) { + pclk = rk_cru_clock_find(sc, param->parent); + KASSERTMSG(pclk != NULL, "couldn't find clock %s", param->parent); + error = clk_set_parent(&clk->base, &pclk->base); + if (error != 0) { + aprint_error_dev(sc->sc_dev, "couldn't set %s parent to %s: %d\n", + param->clk, param->parent, error); + continue; + } + } + } } static int Index: src/sys/arch/arm/rockchip/rk_cru.h diff -u src/sys/arch/arm/rockchip/rk_cru.h:1.4 src/sys/arch/arm/rockchip/rk_cru.h:1.4.4.1 --- src/sys/arch/arm/rockchip/rk_cru.h:1.4 Sat Sep 1 19:35:53 2018 +++ src/sys/arch/arm/rockchip/rk_cru.h Sat Nov 16 16:48:25 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: rk_cru.h,v 1.4 2018/09/01 19:35:53 jmcneill Exp $ */ +/* $NetBSD: rk_cru.h,v 1.4.4.1 2019/11/16 16:48:25 martin Exp $ */ /*- * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> @@ -200,10 +200,13 @@ struct rk_cru_composite { uint32_t div_mask; bus_size_t gate_reg; uint32_t gate_mask; + bus_size_t frac_reg; const char **parents; u_int nparents; u_int flags; #define RK_COMPOSITE_ROUND_DOWN 0x01 +#define RK_COMPOSITE_SET_RATE_PARENT 0x02 +#define RK_COMPOSITE_FRACDIV 0x04 }; int rk_cru_composite_enable(struct rk_cru_softc *, struct rk_cru_clk *, int); @@ -212,7 +215,7 @@ int rk_cru_composite_set_rate(struct rk_ const char *rk_cru_composite_get_parent(struct rk_cru_softc *, struct rk_cru_clk *); int rk_cru_composite_set_parent(struct rk_cru_softc *, struct rk_cru_clk *, const char *); -#define RK_COMPOSITE(_id, _name, _parents, _muxdiv_reg, _mux_mask, _div_mask, _gate_reg, _gate_mask, _flags) \ +#define _RK_COMPOSITE_INIT(_id, _name, _parents, _muxdiv_reg, _mux_mask, _div_mask, _gate_reg, _gate_mask, _frac_reg, _flags) \ { \ .id = (_id), \ .type = RK_CRU_COMPOSITE, \ @@ -225,6 +228,7 @@ int rk_cru_composite_set_parent(struct r .u.composite.div_mask = (_div_mask), \ .u.composite.gate_reg = (_gate_reg), \ .u.composite.gate_mask = (_gate_mask), \ + .u.composite.frac_reg = (_frac_reg), \ .u.composite.flags = (_flags), \ .enable = rk_cru_composite_enable, \ .get_rate = rk_cru_composite_get_rate, \ @@ -233,14 +237,20 @@ int rk_cru_composite_set_parent(struct r .set_parent = rk_cru_composite_set_parent, \ } +#define RK_COMPOSITE(_id, _name, _parents, _muxdiv_reg, _mux_mask, _div_mask, _gate_reg, _gate_mask, _flags) \ + _RK_COMPOSITE_INIT(_id, _name, _parents, _muxdiv_reg, _mux_mask, _div_mask, _gate_reg, _gate_mask, 0, _flags) + #define RK_COMPOSITE_NOMUX(_id, _name, _parent, _div_reg, _div_mask, _gate_reg, _gate_mask, _flags) \ - RK_COMPOSITE(_id, _name, (const char *[]){ _parent }, _div_reg, 0, _div_mask, _gate_reg, _gate_mask, _flags) + _RK_COMPOSITE_INIT(_id, _name, (const char *[]){ _parent }, _div_reg, 0, _div_mask, _gate_reg, _gate_mask, 0, _flags) #define RK_COMPOSITE_NOGATE(_id, _name, _parents, _muxdiv_reg, _mux_mask, _div_mask, _flags) \ - RK_COMPOSITE(_id, _name, _parents, _muxdiv_reg, _mux_mask, _div_mask, 0, 0, _flags) + _RK_COMPOSITE_INIT(_id, _name, _parents, _muxdiv_reg, _mux_mask, _div_mask, 0, 0, 0, _flags) + +#define RK_COMPOSITE_FRAC(_id, _name, _parent, _frac_reg, _flags) \ + _RK_COMPOSITE_INIT(_id, _name, (const char *[]){ _parent }, 0, 0, 0, 0, 0, _frac_reg, (_flags) | RK_COMPOSITE_FRACDIV) #define RK_DIV(_id, _name, _parent, _div_reg, _div_mask, _flags) \ - RK_COMPOSITE(_id, _name, (const char *[]){ _parent }, _div_reg, 0, _div_mask, 0, 0, _flags) + _RK_COMPOSITE_INIT(_id, _name, (const char *[]){ _parent }, _div_reg, 0, _div_mask, 0, 0, 0, _flags) /* Gate clocks */ Index: src/sys/arch/arm/rockchip/rk_cru_composite.c diff -u src/sys/arch/arm/rockchip/rk_cru_composite.c:1.3 src/sys/arch/arm/rockchip/rk_cru_composite.c:1.3.8.1 --- src/sys/arch/arm/rockchip/rk_cru_composite.c:1.3 Tue Jun 19 01:24:17 2018 +++ src/sys/arch/arm/rockchip/rk_cru_composite.c Sat Nov 16 16:48:25 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: rk_cru_composite.c,v 1.3 2018/06/19 01:24:17 jmcneill Exp $ */ +/* $NetBSD: rk_cru_composite.c,v 1.3.8.1 2019/11/16 16:48:25 martin Exp $ */ /*- * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: rk_cru_composite.c,v 1.3 2018/06/19 01:24:17 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rk_cru_composite.c,v 1.3.8.1 2019/11/16 16:48:25 martin Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -75,10 +75,55 @@ rk_cru_composite_get_rate(struct rk_cru_ if (prate == 0) return 0; - const uint32_t val = CRU_READ(sc, composite->muxdiv_reg); - const u_int div = __SHIFTOUT(val, composite->div_mask) + 1; + if (composite->flags & RK_COMPOSITE_FRACDIV) { + const uint32_t val = CRU_READ(sc, composite->frac_reg); + const u_int num = (val >> 16) & 0xffff; + const u_int den = val & 0xffff; - return prate / div; + return (u_int)((uint64_t)prate * num / den); + } else { + const uint32_t val = CRU_READ(sc, composite->muxdiv_reg); + const u_int div = __SHIFTOUT(val, composite->div_mask) + 1; + + return prate / div; + } +} + +static u_int +rk_cru_composite_get_frac_div(u_int n, u_int d) +{ + u_int tmp; + + while (d > 0) { + tmp = d; + d = n % d; + n = tmp; + } + + return n; +} + +static int +rk_cru_composite_set_rate_frac(struct rk_cru_softc *sc, + struct rk_cru_clk *clk, u_int rate) +{ + struct rk_cru_composite *composite = &clk->u.composite; + struct clk *clk_parent; + + clk_parent = clk_get_parent(&clk->base); + if (clk_parent == NULL) + return ENXIO; + + const u_int prate = clk_get_rate(clk_parent); + const u_int v = rk_cru_composite_get_frac_div(prate, rate); + const u_int num = (prate / v) & 0xffff; + const u_int den = (rate / v) & 0xffff; + if (prate / num * den != rate) + return EINVAL; + + CRU_WRITE(sc, composite->frac_reg, (den << 16) | num); + + return 0; } int @@ -92,6 +137,17 @@ rk_cru_composite_set_rate(struct rk_cru_ KASSERT(clk->type == RK_CRU_COMPOSITE); + if (composite->flags & RK_COMPOSITE_SET_RATE_PARENT) { + clk_parent = clk_get_parent(&clk->base); + if (clk_parent == NULL) + return ENXIO; + return clk_set_rate(clk_parent, rate); + } + + if (composite->flags & RK_COMPOSITE_FRACDIV) { + return rk_cru_composite_set_rate_frac(sc, clk, rate); + } + best_div = 0; best_mux = 0; best_diff = INT_MAX; Index: src/sys/arch/arm/rockchip/rk_i2c.c diff -u src/sys/arch/arm/rockchip/rk_i2c.c:1.4.6.1 src/sys/arch/arm/rockchip/rk_i2c.c:1.4.6.2 --- src/sys/arch/arm/rockchip/rk_i2c.c:1.4.6.1 Tue Sep 24 02:50:36 2019 +++ src/sys/arch/arm/rockchip/rk_i2c.c Sat Nov 16 16:48:25 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: rk_i2c.c,v 1.4.6.1 2019/09/24 02:50:36 martin Exp $ */ +/* $NetBSD: rk_i2c.c,v 1.4.6.2 2019/11/16 16:48:25 martin Exp $ */ /*- * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> @@ -28,7 +28,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: rk_i2c.c,v 1.4.6.1 2019/09/24 02:50:36 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rk_i2c.c,v 1.4.6.2 2019/11/16 16:48:25 martin Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -284,7 +284,7 @@ rk_i2c_write(struct rk_i2c_softc *sc, i2 static int rk_i2c_read(struct rk_i2c_softc *sc, i2c_addr_t addr, const uint8_t *cmd, size_t cmdlen, uint8_t *buf, - size_t buflen, int flags, bool send_start) + size_t buflen, int flags, bool send_start, bool last_ack) { uint32_t rxdata[8]; uint32_t con, mrxaddr, mrxraddr; @@ -296,24 +296,27 @@ rk_i2c_read(struct rk_i2c_softc *sc, i2c if (cmdlen > 3) return EINVAL; - mode = RKI2C_CON_I2C_MODE_RTX; - con = RKI2C_CON_I2C_EN | RKI2C_CON_ACK | __SHIFTIN(mode, RKI2C_CON_I2C_MODE); + mode = send_start ? RKI2C_CON_I2C_MODE_RTX : RKI2C_CON_I2C_MODE_RX; + con = RKI2C_CON_I2C_EN | __SHIFTIN(mode, RKI2C_CON_I2C_MODE); WR4(sc, RKI2C_CON, con); if (send_start && (error = rk_i2c_start(sc)) != 0) return error; - mrxaddr = __SHIFTIN((addr << 1) | 1, RKI2C_MRXADDR_SADDR) | - RKI2C_MRXADDR_ADDLVLD; - WR4(sc, RKI2C_MRXADDR, mrxaddr); - for (n = 0, mrxraddr = 0; n < cmdlen; n++) { - mrxraddr |= cmd[n] << (n * 8); - mrxraddr |= (RKI2C_MRXRADDR_ADDLVLD << n); + if (send_start) { + mrxaddr = __SHIFTIN((addr << 1) | 1, RKI2C_MRXADDR_SADDR) | + RKI2C_MRXADDR_ADDLVLD; + WR4(sc, RKI2C_MRXADDR, mrxaddr); + for (n = 0, mrxraddr = 0; n < cmdlen; n++) { + mrxraddr |= cmd[n] << (n * 8); + mrxraddr |= (RKI2C_MRXRADDR_ADDLVLD << n); + } + WR4(sc, RKI2C_MRXRADDR, mrxraddr); } - WR4(sc, RKI2C_MRXRADDR, mrxraddr); - /* Acknowledge last byte read */ - con |= RKI2C_CON_ACK; + if (last_ack) { + con |= RKI2C_CON_ACK; + } WR4(sc, RKI2C_CON, con); /* Receive data. Slave address goes in the lower 8 bits of MRXADDR */ @@ -321,8 +324,14 @@ rk_i2c_read(struct rk_i2c_softc *sc, i2c if ((error = rk_i2c_wait(sc, RKI2C_IPD_MBRFIPD)) != 0) return error; +#if 0 bus_space_read_region_4(sc->sc_bst, sc->sc_bsh, RKI2C_RXDATA(0), rxdata, howmany(buflen, 4)); +#else + for (n = 0; n < roundup(buflen, 4); n += 4) + rxdata[n/4] = RD4(sc, RKI2C_RXDATA(n/4)); +#endif + memcpy(buf, rxdata, buflen); return 0; @@ -339,7 +348,19 @@ rk_i2c_exec(void *priv, i2c_op_t op, i2c KASSERT(mutex_owned(&sc->sc_lock)); if (I2C_OP_READ_P(op)) { - error = rk_i2c_read(sc, addr, cmdbuf, cmdlen, buf, buflen, flags, send_start); + uint8_t *databuf = buf; + while (buflen > 0) { + const size_t datalen = uimin(buflen, 32); + const bool last_ack = datalen == buflen; + error = rk_i2c_read(sc, addr, cmdbuf, cmdlen, databuf, datalen, flags, send_start, last_ack); + if (error != 0) + break; + databuf += datalen; + buflen -= datalen; + send_start = false; + cmdbuf = NULL; + cmdlen = 0; + } } else { error = rk_i2c_write(sc, addr, cmdbuf, cmdlen, buf, buflen, flags, send_start); } Index: src/sys/arch/evbarm/conf/GENERIC64 diff -u src/sys/arch/evbarm/conf/GENERIC64:1.103.2.2 src/sys/arch/evbarm/conf/GENERIC64:1.103.2.3 --- src/sys/arch/evbarm/conf/GENERIC64:1.103.2.2 Thu Oct 3 17:14:47 2019 +++ src/sys/arch/evbarm/conf/GENERIC64 Sat Nov 16 16:48:25 2019 @@ -1,5 +1,5 @@ # -# $NetBSD: GENERIC64,v 1.103.2.2 2019/10/03 17:14:47 martin Exp $ +# $NetBSD: GENERIC64,v 1.103.2.3 2019/11/16 16:48:25 martin Exp $ # # GENERIC ARM (aarch64) kernel # @@ -340,7 +340,7 @@ options I2C_MAX_ADDR=0xfff bsciic* at fdt? # Broadcom BCM283x Serial Control dwiic* at fdt? # Designware I2C dwiic* at acpi? -rkiic* at fdt? # Rockchip I2C +rkiic* at fdt? pass 4 # Rockchip I2C sunxirsb* at fdt? pass 4 # Allwinner RSB sunxitwi* at fdt? # Allwinner TWI tegrai2c* at fdt? pass 4 # NVIDIA Tegra I2C @@ -412,6 +412,7 @@ options HDAUDIOVERBOSE options HDAUDIO_ENABLE_HDMI options HDAUDIO_ENABLE_DISPLAYPORT ausoc* at fdt? # Simple SoC audio card +rki2s* at fdt? # Rockchip I2S/PCM sunxicodec* at fdt? # Allwinner audio codec sun8icodec* at fdt? # Allwinner audio codec (sun8i/sun50i) h3codec* at fdt? # Allwinner H3 audio codec (analog part) @@ -429,6 +430,9 @@ hdmicec* at hdmicecbus? anxedp* at iic? # Analogix eDP TX dispcon* at fdt? # Display connector devices dwhdmi* at fdt? # Designware HDMI TX +rkdrm* at fdt? pass 5 # Rockchip DRM master +rkfb* at rkdrm? # Rockchip DRM framebuffer +rkvop* at fdt? # Rockchip Visual Output Processor sunxide2bus* at fdt? pass 4 # Allwinner DE2 bus sunxidrm* at fdt? pass 5 # Allwinner Display Pipeline sunxifb* at sunxidrm? # Allwinner DRM framebuffer Index: src/sys/conf/files diff -u src/sys/conf/files:1.1237.2.1 src/sys/conf/files:1.1237.2.2 --- src/sys/conf/files:1.1237.2.1 Tue Sep 3 07:47:59 2019 +++ src/sys/conf/files Sat Nov 16 16:48:25 2019 @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.1237.2.1 2019/09/03 07:47:59 martin Exp $ +# $NetBSD: files,v 1.1237.2.2 2019/11/16 16:48:25 martin Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 version 20171118 @@ -1489,6 +1489,7 @@ attach ipmi at ipmibus # Designware HDMI TX device dwhdmi: edid, videomode, drmkms, drmkms_i2c file dev/ic/dw_hdmi.c dwhdmi +file dev/ic/dw_hdmi_phy.c dwhdmi # # File systems Index: src/sys/dev/fdt/ausoc.c diff -u src/sys/dev/fdt/ausoc.c:1.4 src/sys/dev/fdt/ausoc.c:1.4.2.1 --- src/sys/dev/fdt/ausoc.c:1.4 Wed May 8 13:40:18 2019 +++ src/sys/dev/fdt/ausoc.c Sat Nov 16 16:48:25 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: ausoc.c,v 1.4 2019/05/08 13:40:18 isaki Exp $ */ +/* $NetBSD: ausoc.c,v 1.4.2.1 2019/11/16 16:48:25 martin Exp $ */ /*- * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ausoc.c,v 1.4 2019/05/08 13:40:18 isaki Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ausoc.c,v 1.4.2.1 2019/11/16 16:48:25 martin Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -121,8 +121,22 @@ ausoc_set_format(void *priv, int setmode audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) { struct ausoc_link * const link = priv; + const audio_params_t *params = (setmode & AUMODE_PLAY) != 0 ? + play : rec; int error; + if (link->link_mclk_fs) { + const u_int rate = params->sample_rate * link->link_mclk_fs; + error = audio_dai_set_sysclk(link->link_codec, rate, + AUDIO_DAI_CLOCK_IN); + if (error) + return error; + error = audio_dai_set_sysclk(link->link_cpu, rate, + AUDIO_DAI_CLOCK_OUT); + if (error) + return error; + } + error = audio_dai_mi_set_format(link->link_cpu, setmode, play, rec, pfil, rfil); if (error) @@ -246,20 +260,8 @@ ausoc_trigger_output(void *priv, void *s void (*intr)(void *), void *intrarg, const audio_params_t *params) { struct ausoc_link * const link = priv; - u_int n, rate; int error; - - if (link->link_mclk_fs) { - rate = params->sample_rate * link->link_mclk_fs; - error = audio_dai_set_sysclk(link->link_codec, rate, - AUDIO_DAI_CLOCK_IN); - if (error) - goto failed; - error = audio_dai_set_sysclk(link->link_cpu, rate, - AUDIO_DAI_CLOCK_OUT); - if (error) - goto failed; - } + u_int n; for (n = 0; n < link->link_naux; n++) { error = audio_dai_trigger(link->link_aux[n], start, end, @@ -285,20 +287,8 @@ ausoc_trigger_input(void *priv, void *st void (*intr)(void *), void *intrarg, const audio_params_t *params) { struct ausoc_link * const link = priv; - u_int n, rate; int error; - - if (link->link_mclk_fs) { - rate = params->sample_rate * link->link_mclk_fs; - error = audio_dai_set_sysclk(link->link_codec, rate, - AUDIO_DAI_CLOCK_IN); - if (error) - goto failed; - error = audio_dai_set_sysclk(link->link_cpu, rate, - AUDIO_DAI_CLOCK_OUT); - if (error) - goto failed; - } + u_int n; for (n = 0; n < link->link_naux; n++) { error = audio_dai_trigger(link->link_aux[n], start, end, Index: src/sys/dev/fdt/fdt_clock.c diff -u src/sys/dev/fdt/fdt_clock.c:1.8 src/sys/dev/fdt/fdt_clock.c:1.8.4.1 --- src/sys/dev/fdt/fdt_clock.c:1.8 Wed Feb 27 16:56:00 2019 +++ src/sys/dev/fdt/fdt_clock.c Sat Nov 16 16:48:25 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: fdt_clock.c,v 1.8 2019/02/27 16:56:00 jakllsch Exp $ */ +/* $NetBSD: fdt_clock.c,v 1.8.4.1 2019/11/16 16:48:25 martin Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: fdt_clock.c,v 1.8 2019/02/27 16:56:00 jakllsch Exp $"); +__KERNEL_RCSID(0, "$NetBSD: fdt_clock.c,v 1.8.4.1 2019/11/16 16:48:25 martin Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -161,6 +161,30 @@ fdtbus_clock_get(int phandle, const char return fdtbus_clock_get_prop(phandle, clkname, "clock-names"); } +int +fdtbus_clock_enable(int phandle, const char *clkname, bool required) +{ + struct clk *clk; + + clk = fdtbus_clock_get(phandle, clkname); + if (clk == NULL) + return required ? ENOENT : 0; + + return clk_enable(clk); +} + +int +fdtbus_clock_enable_index(int phandle, u_int index, bool required) +{ + struct clk *clk; + + clk = fdtbus_clock_get_index(phandle, index); + if (clk == NULL) + return required ? ENOENT : 0; + + return clk_enable(clk); +} + /* * Search the DT for a clock by "clock-output-names" property. * Index: src/sys/dev/fdt/fdtvar.h diff -u src/sys/dev/fdt/fdtvar.h:1.52.2.1 src/sys/dev/fdt/fdtvar.h:1.52.2.2 --- src/sys/dev/fdt/fdtvar.h:1.52.2.1 Thu Oct 3 17:23:11 2019 +++ src/sys/dev/fdt/fdtvar.h Sat Nov 16 16:48:25 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: fdtvar.h,v 1.52.2.1 2019/10/03 17:23:11 martin Exp $ */ +/* $NetBSD: fdtvar.h,v 1.52.2.2 2019/11/16 16:48:25 martin Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -345,6 +345,8 @@ struct clk * fdtbus_clock_get(int, const struct clk * fdtbus_clock_get_index(int, u_int); struct clk * fdtbus_clock_byname(const char *); void fdtbus_clock_assign(int); +int fdtbus_clock_enable(int, const char *, bool); +int fdtbus_clock_enable_index(int, u_int, bool); struct fdtbus_reset *fdtbus_reset_get(int, const char *); struct fdtbus_reset *fdtbus_reset_get_index(int, u_int); Index: src/sys/dev/ic/dw_hdmi.c diff -u src/sys/dev/ic/dw_hdmi.c:1.1 src/sys/dev/ic/dw_hdmi.c:1.1.6.1 --- src/sys/dev/ic/dw_hdmi.c:1.1 Wed Jan 30 01:19:49 2019 +++ src/sys/dev/ic/dw_hdmi.c Sat Nov 16 16:48:25 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: dw_hdmi.c,v 1.1 2019/01/30 01:19:49 jmcneill Exp $ */ +/* $NetBSD: dw_hdmi.c,v 1.1.6.1 2019/11/16 16:48:25 martin Exp $ */ /*- * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.1 2019/01/30 01:19:49 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.1.6.1 2019/11/16 16:48:25 martin Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -45,11 +45,19 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v #include <dev/videomode/videomode.h> #include <dev/videomode/edidvar.h> +#include <dev/audio/audio_dai.h> + #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> +#define HDMI_DESIGN_ID 0x0000 +#define HDMI_REVISION_ID 0x0001 +#define HDMI_CONFIG0_ID 0x0004 +#define HDMI_CONFIG0_ID_AUDI2S __BIT(4) +#define HDMI_CONFIG2_ID 0x0006 + #define HDMI_IH_I2CM_STAT0 0x0105 #define HDMI_IH_I2CM_STAT0_DONE __BIT(1) #define HDMI_IH_I2CM_STAT0_ERROR __BIT(0) @@ -127,6 +135,49 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v #define HDMI_FC_CH1PREAM_DEFAULT 0x16 #define HDMI_FC_CH2PREAM 0x1016 #define HDMI_FC_CH2PREAM_DEFAULT 0x21 +#define HDMI_FC_AUDCONF0 0x1025 +#define HDMI_FC_AUDCONF1 0x1026 +#define HDMI_FC_AUDCONF2 0x1027 +#define HDMI_FC_AUDCONF3 0x1028 + +#define HDMI_PHY_CONF0 0x3000 +#define HDMI_PHY_CONF0_PDZ __BIT(7) +#define HDMI_PHY_CONF0_ENTMDS __BIT(6) +#define HDMI_PHY_CONF0_SVSRET __BIT(5) +#define HDMI_PHY_CONF0_PDDQ __BIT(4) +#define HDMI_PHY_CONF0_TXPWRON __BIT(3) +#define HDMI_PHY_CONF0_ENHPDRXSENSE __BIT(2) +#define HDMI_PHY_CONF0_SELDATAENPOL __BIT(1) +#define HDMI_PHY_CONF0_SELDIPIF __BIT(0) +#define HDMI_PHY_STAT0 0x3004 +#define HDMI_PHY_STAT0_RX_SENSE_3 __BIT(7) +#define HDMI_PHY_STAT0_RX_SENSE_2 __BIT(6) +#define HDMI_PHY_STAT0_RX_SENSE_1 __BIT(5) +#define HDMI_PHY_STAT0_RX_SENSE_0 __BIT(4) +#define HDMI_PHY_STAT0_HPD __BIT(1) +#define HDMI_PHY_STAT0_TX_PHY_LOCK __BIT(0) + +#define HDMI_AUD_CONF0 0x3100 +#define HDMI_AUD_CONF0_SW_AUDIO_FIFO_RST __BIT(7) +#define HDMI_AUD_CONF0_I2S_SELECT __BIT(5) +#define HDMI_AUD_CONF0_I2S_IN_EN __BITS(3,0) +#define HDMI_AUD_CONF1 0x3101 +#define HDMI_AUD_CONF1_I2S_WIDTH __BITS(4,0) +#define HDMI_AUD_INT 0x3102 +#define HDMI_AUD_CONF2 0x3103 +#define HDMI_AUD_CONF2_INSERT_PCUV __BIT(2) +#define HDMI_AUD_CONF2_NLPCM __BIT(1) +#define HDMI_AUD_CONF2_HBR __BIT(0) +#define HDMI_AUD_INT1 0x3104 + +#define HDMI_AUD_N1 0x3200 +#define HDMI_AUD_N2 0x3201 +#define HDMI_AUD_N3 0x3202 +#define HDMI_AUD_CTS1 0x3203 +#define HDMI_AUD_CTS2 0x3204 +#define HDMI_AUD_CTS3 0x3205 +#define HDMI_AUD_INPUTCLKFS 0x3206 +#define HDMI_AUD_INPUTCLKFS_IFSFACTOR __BITS(2,0) #define HDMI_MC_CLKDIS 0x4001 #define HDMI_MC_CLKDIS_HDCPCLK_DISABLE __BIT(6) @@ -143,6 +194,8 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v #define HDMI_MC_SWRSTZREQ_PIXELSWRST_REQ __BIT(0) #define HDMI_MC_FLOWCTRL 0x4004 #define HDMI_MC_PHYRSTZ 0x4005 +#define HDMI_MC_PHYRSTZ_ASSERT __BIT(0) +#define HDMI_MC_PHYRSTZ_DEASSERT 0 #define HDMI_MC_LOCKONCLOCK 0x4006 #define HDMI_MC_HEACPHY_RST 0x4007 @@ -187,6 +240,16 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v #define HDMI_I2CM_SOFTRSTZ_I2C_SOFTRST __BIT(0) #define HDMI_I2CM_SEGPTR 0x7e0a +enum dwhdmi_dai_mixer_ctrl { + DWHDMI_DAI_OUTPUT_CLASS, + DWHDMI_DAI_INPUT_CLASS, + + DWHDMI_DAI_OUTPUT_MASTER_VOLUME, + DWHDMI_DAI_INPUT_DAC_VOLUME, + + DWHDMI_DAI_MIXER_CTRL_LAST +}; + static int dwhdmi_ddc_acquire_bus(void *priv, int flags) { @@ -410,9 +473,8 @@ dwhdmi_fc_init(struct dwhdmi_softc *sc, static void dwhdmi_mc_init(struct dwhdmi_softc *sc) { - struct dwhdmi_connector *dwhdmi_connector = &sc->sc_connector; uint8_t val; - u_int n; + u_int n, iter; /* Bypass colour space converter */ dwhdmi_write(sc, HDMI_MC_FLOWCTRL, 0); @@ -422,16 +484,16 @@ dwhdmi_mc_init(struct dwhdmi_softc *sc) HDMI_MC_CLKDIS_CECCLK_DISABLE | HDMI_MC_CLKDIS_CSCCLK_DISABLE | HDMI_MC_CLKDIS_PREPCLK_DISABLE; - if (!dwhdmi_connector->monitor_audio) - val |= HDMI_MC_CLKDIS_AUDCLK_DISABLE; dwhdmi_write(sc, HDMI_MC_CLKDIS, val); /* Soft reset TMDS */ val = 0xff & ~HDMI_MC_SWRSTZREQ_TMDSSWRST_REQ; dwhdmi_write(sc, HDMI_MC_SWRSTZREQ, val); + iter = sc->sc_version == 0x130a ? 4 : 1; + val = dwhdmi_read(sc, HDMI_FC_INVIDCONF); - for (n = 0; n < 4; n++) + for (n = 0; n < iter; n++) dwhdmi_write(sc, HDMI_FC_INVIDCONF, val); } @@ -442,6 +504,59 @@ dwhdmi_mc_disable(struct dwhdmi_softc *s dwhdmi_write(sc, HDMI_MC_CLKDIS, 0xff); } +static void +dwhdmi_audio_init(struct dwhdmi_softc *sc) +{ + uint8_t val; + u_int n; + + /* The following values are for 48 kHz */ + switch (sc->sc_curmode.clock) { + case 25170: + n = 6864; + break; + case 74170: + n = 11648; + break; + case 148350: + n = 5824; + break; + default: + n = 6144; + break; + } + + /* Use automatic CTS generation */ + dwhdmi_write(sc, HDMI_AUD_CTS1, 0); + dwhdmi_write(sc, HDMI_AUD_CTS2, 0); + dwhdmi_write(sc, HDMI_AUD_CTS3, 0); + + /* Set N factor for audio clock regeneration */ + dwhdmi_write(sc, HDMI_AUD_N1, n & 0xff); + dwhdmi_write(sc, HDMI_AUD_N2, (n >> 8) & 0xff); + dwhdmi_write(sc, HDMI_AUD_N3, (n >> 16) & 0xff); + + val = dwhdmi_read(sc, HDMI_AUD_CONF0); + val |= HDMI_AUD_CONF0_I2S_SELECT; /* XXX i2s mode */ + val &= ~HDMI_AUD_CONF0_I2S_IN_EN; + val |= __SHIFTIN(1, HDMI_AUD_CONF0_I2S_IN_EN); /* XXX 2ch */ + dwhdmi_write(sc, HDMI_AUD_CONF0, val); + + val = __SHIFTIN(16, HDMI_AUD_CONF1_I2S_WIDTH); + dwhdmi_write(sc, HDMI_AUD_CONF1, val); + + dwhdmi_write(sc, HDMI_AUD_INPUTCLKFS, 4); /* XXX 64 FS */ + + dwhdmi_write(sc, HDMI_FC_AUDCONF0, 1 << 4); /* XXX 2ch */ + dwhdmi_write(sc, HDMI_FC_AUDCONF1, 0); + dwhdmi_write(sc, HDMI_FC_AUDCONF2, 0); + dwhdmi_write(sc, HDMI_FC_AUDCONF3, 0); + + val = dwhdmi_read(sc, HDMI_MC_CLKDIS); + val &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE; + dwhdmi_write(sc, HDMI_MC_CLKDIS, val); +} + static enum drm_connector_status dwhdmi_connector_detect(struct drm_connector *connector, bool force) { @@ -479,7 +594,7 @@ dwhdmi_connector_get_modes(struct drm_co memset(edid, 0, sizeof(edid)); for (block = 0; block < 4; block++) { - error = ddc_read_edid_block(&sc->sc_ic, + error = ddc_read_edid_block(sc->sc_ic, &edid[block * EDID_LENGTH], EDID_LENGTH, block); if (error != 0) break; @@ -569,6 +684,9 @@ dwhdmi_bridge_enable(struct drm_bridge * dwhdmi_tx_init(sc); dwhdmi_mc_init(sc); + + if (sc->sc_connector.monitor_audio) + dwhdmi_audio_init(sc); } static void @@ -621,10 +739,136 @@ static const struct drm_bridge_funcs dwh .mode_fixup = dwhdmi_bridge_mode_fixup, }; +static int +dwhdmi_dai_set_format(audio_dai_tag_t dai, u_int format) +{ + return 0; +} + +static int +dwhdmi_dai_add_device(audio_dai_tag_t dai, audio_dai_tag_t aux) +{ + /* Not supported */ + return 0; +} + +static void +dwhdmi_audio_swvol_codec(audio_filter_arg_t *arg) +{ + struct dwhdmi_softc * const sc = arg->context; + const aint_t *src; + aint_t *dst; + u_int sample_count; + u_int i; + + src = arg->src; + dst = arg->dst; + sample_count = arg->count * arg->srcfmt->channels; + for (i = 0; i < sample_count; i++) { + aint2_t v = (aint2_t)(*src++); + v = v * sc->sc_swvol / 255; + *dst++ = (aint_t)v; + } +} + +static int +dwhdmi_audio_set_format(void *priv, int setmode, + const audio_params_t *play, const audio_params_t *rec, + audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) +{ + struct dwhdmi_softc * const sc = priv; + + pfil->codec = dwhdmi_audio_swvol_codec; + pfil->context = sc; + + return 0; +} + +static int +dwhdmi_audio_set_port(void *priv, mixer_ctrl_t *mc) +{ + struct dwhdmi_softc * const sc = priv; + + switch (mc->dev) { + case DWHDMI_DAI_OUTPUT_MASTER_VOLUME: + case DWHDMI_DAI_INPUT_DAC_VOLUME: + sc->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + return 0; + default: + return ENXIO; + } +} + +static int +dwhdmi_audio_get_port(void *priv, mixer_ctrl_t *mc) +{ + struct dwhdmi_softc * const sc = priv; + + switch (mc->dev) { + case DWHDMI_DAI_OUTPUT_MASTER_VOLUME: + case DWHDMI_DAI_INPUT_DAC_VOLUME: + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->sc_swvol; + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->sc_swvol; + return 0; + default: + return ENXIO; + } +} + +static int +dwhdmi_audio_query_devinfo(void *priv, mixer_devinfo_t *di) +{ + switch (di->index) { + case DWHDMI_DAI_OUTPUT_CLASS: + di->mixer_class = di->index; + strcpy(di->label.name, AudioCoutputs); + di->type = AUDIO_MIXER_CLASS; + di->next = di->prev = AUDIO_MIXER_LAST; + return 0; + + case DWHDMI_DAI_INPUT_CLASS: + di->mixer_class = di->index; + strcpy(di->label.name, AudioCinputs); + di->type = AUDIO_MIXER_CLASS; + di->next = di->prev = AUDIO_MIXER_LAST; + return 0; + + case DWHDMI_DAI_OUTPUT_MASTER_VOLUME: + di->mixer_class = DWHDMI_DAI_OUTPUT_CLASS; + strcpy(di->label.name, AudioNmaster); + di->un.v.delta = 1; + di->un.v.num_channels = 2; + strcpy(di->un.v.units.name, AudioNvolume); + di->type = AUDIO_MIXER_VALUE; + di->next = di->prev = AUDIO_MIXER_LAST; + return 0; + + case DWHDMI_DAI_INPUT_DAC_VOLUME: + di->mixer_class = DWHDMI_DAI_INPUT_CLASS; + strcpy(di->label.name, AudioNdac); + di->un.v.delta = 1; + di->un.v.num_channels = 2; + strcpy(di->un.v.units.name, AudioNvolume); + di->type = AUDIO_MIXER_VALUE; + di->next = di->prev = AUDIO_MIXER_LAST; + return 0; + + default: + return ENXIO; + } +} + +static const struct audio_hw_if dwhdmi_dai_hw_if = { + .set_format = dwhdmi_audio_set_format, + .set_port = dwhdmi_audio_set_port, + .get_port = dwhdmi_audio_get_port, + .query_devinfo = dwhdmi_audio_query_devinfo, +}; + int dwhdmi_attach(struct dwhdmi_softc *sc) { - struct i2c_controller *ic = &sc->sc_ic; + uint8_t val; if (sc->sc_reg_width != 1 && sc->sc_reg_width != 4) { aprint_error_dev(sc->sc_dev, "unsupported register width %d\n", sc->sc_reg_width); @@ -633,10 +877,48 @@ dwhdmi_attach(struct dwhdmi_softc *sc) mutex_init(&sc->sc_ic_lock, MUTEX_DEFAULT, IPL_NONE); - ic->ic_cookie = sc; - ic->ic_acquire_bus = dwhdmi_ddc_acquire_bus; - ic->ic_release_bus = dwhdmi_ddc_release_bus; - ic->ic_exec = dwhdmi_ddc_exec; + sc->sc_version = dwhdmi_read(sc, HDMI_DESIGN_ID); + sc->sc_version <<= 8; + sc->sc_version |= dwhdmi_read(sc, HDMI_REVISION_ID); + + sc->sc_phytype = dwhdmi_read(sc, HDMI_CONFIG2_ID); + + aprint_normal_dev(sc->sc_dev, "version %x.%03x, phytype 0x%02x\n", + sc->sc_version >> 12, sc->sc_version & 0xfff, + sc->sc_phytype); + + sc->sc_swvol = 255; + + /* + * If a DDC i2c bus tag is provided by the caller, use it. Otherwise, + * use the I2C master built-in to DWC HDMI. + */ + if (sc->sc_ic == NULL) { + struct i2c_controller *ic = &sc->sc_ic_builtin; + ic->ic_cookie = sc; + ic->ic_acquire_bus = dwhdmi_ddc_acquire_bus; + ic->ic_release_bus = dwhdmi_ddc_release_bus; + ic->ic_exec = dwhdmi_ddc_exec; + sc->sc_ic = ic; + } + + /* + * Enable HPD on internal PHY + */ + if ((sc->sc_flags & DWHDMI_USE_INTERNAL_PHY) != 0) { + val = dwhdmi_read(sc, HDMI_PHY_CONF0); + val |= HDMI_PHY_CONF0_ENHPDRXSENSE; + dwhdmi_write(sc, HDMI_PHY_CONF0, val); + } + + /* + * Initialize audio DAI + */ + sc->sc_dai.dai_set_format = dwhdmi_dai_set_format; + sc->sc_dai.dai_add_device = dwhdmi_dai_add_device; + sc->sc_dai.dai_hw_if = &dwhdmi_dai_hw_if; + sc->sc_dai.dai_dev = sc->sc_dev; + sc->sc_dai.dai_priv = sc; return 0; } Index: src/sys/dev/ic/dw_hdmi.h diff -u src/sys/dev/ic/dw_hdmi.h:1.1 src/sys/dev/ic/dw_hdmi.h:1.1.6.1 --- src/sys/dev/ic/dw_hdmi.h:1.1 Wed Jan 30 01:19:49 2019 +++ src/sys/dev/ic/dw_hdmi.h Sat Nov 16 16:48:25 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: dw_hdmi.h,v 1.1 2019/01/30 01:19:49 jmcneill Exp $ */ +/* $NetBSD: dw_hdmi.h,v 1.1.6.1 2019/11/16 16:48:25 martin Exp $ */ /*- * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca> @@ -32,6 +32,8 @@ #include <dev/i2c/i2cvar.h> #include <dev/i2c/ddcreg.h> +#include <dev/audio/audio_dai.h> + #include <drm/drmP.h> struct dwhdmi_softc; @@ -44,20 +46,46 @@ struct dwhdmi_connector { bool monitor_audio; }; +struct dwhdmi_phy_config { + u_int pixel_clock; + uint32_t sym; + uint32_t term; + uint32_t vlev; +}; + +struct dwhdmi_mpll_config { + u_int pixel_clock; + uint32_t cpce; + uint32_t gmp; + uint32_t curr; +}; + struct dwhdmi_softc { device_t sc_dev; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; u_int sc_reg_width; + u_int sc_flags; +#define DWHDMI_USE_INTERNAL_PHY __BIT(0) - struct i2c_controller sc_ic; + u_int sc_phytype; + u_int sc_version; + + i2c_tag_t sc_ic; kmutex_t sc_ic_lock; + struct i2c_controller sc_ic_builtin; + + struct audio_dai_device sc_dai; + uint8_t sc_swvol; struct dwhdmi_connector sc_connector; struct drm_bridge sc_bridge; struct drm_display_mode sc_curmode; + const struct dwhdmi_mpll_config *sc_mpll_config; + const struct dwhdmi_phy_config *sc_phy_config; + enum drm_connector_status (*sc_detect)(struct dwhdmi_softc *, bool); void (*sc_enable)(struct dwhdmi_softc *); void (*sc_disable)(struct dwhdmi_softc *); @@ -74,4 +102,11 @@ int dwhdmi_bind(struct dwhdmi_softc *, uint8_t dwhdmi_read(struct dwhdmi_softc *, bus_size_t); void dwhdmi_write(struct dwhdmi_softc *, bus_size_t, uint8_t); +enum drm_connector_status dwhdmi_phy_detect(struct dwhdmi_softc *, bool); +void dwhdmi_phy_enable(struct dwhdmi_softc *); +void dwhdmi_phy_disable(struct dwhdmi_softc *); +void dwhdmi_phy_mode_set(struct dwhdmi_softc *, + struct drm_display_mode *, + struct drm_display_mode *); + #endif /* !_DEV_IC_DWHDMI_H */ Added files: Index: src/sys/arch/arm/rockchip/rk_drm.c diff -u /dev/null src/sys/arch/arm/rockchip/rk_drm.c:1.2.2.2 --- /dev/null Sat Nov 16 16:48:26 2019 +++ src/sys/arch/arm/rockchip/rk_drm.c Sat Nov 16 16:48:25 2019 @@ -0,0 +1,512 @@ +/* $NetBSD: rk_drm.c,v 1.2.2.2 2019/11/16 16:48:25 martin Exp $ */ + +/*- + * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: rk_drm.c,v 1.2.2.2 2019/11/16 16:48:25 martin Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/intr.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> + +#include <uvm/uvm_extern.h> +#include <uvm/uvm_object.h> +#include <uvm/uvm_device.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/fdt/fdt_port.h> + +#include <arm/rockchip/rk_drm.h> + +#define RK_DRM_MAX_WIDTH 3840 +#define RK_DRM_MAX_HEIGHT 2160 + +static TAILQ_HEAD(, rk_drm_ports) rk_drm_ports = + TAILQ_HEAD_INITIALIZER(rk_drm_ports); + +static const char * const compatible[] = { + "rockchip,display-subsystem", + NULL +}; + +static const char * fb_compatible[] = { + "simple-framebuffer", + NULL +}; + +static int rk_drm_match(device_t, cfdata_t, void *); +static void rk_drm_attach(device_t, device_t, void *); + +static void rk_drm_init(device_t); +static vmem_t *rk_drm_alloc_cma_pool(struct drm_device *, size_t); + +static int rk_drm_set_busid(struct drm_device *, struct drm_master *); + +static uint32_t rk_drm_get_vblank_counter(struct drm_device *, unsigned int); +static int rk_drm_enable_vblank(struct drm_device *, unsigned int); +static void rk_drm_disable_vblank(struct drm_device *, unsigned int); + +static int rk_drm_load(struct drm_device *, unsigned long); +static int rk_drm_unload(struct drm_device *); + +static struct drm_driver rk_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, + .dev_priv_size = 0, + .load = rk_drm_load, + .unload = rk_drm_unload, + + .gem_free_object = drm_gem_cma_free_object, + .mmap_object = drm_gem_or_legacy_mmap_object, + .gem_uvm_ops = &drm_gem_cma_uvm_ops, + + .dumb_create = drm_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_dumb_destroy, + + .get_vblank_counter = rk_drm_get_vblank_counter, + .enable_vblank = rk_drm_enable_vblank, + .disable_vblank = rk_drm_disable_vblank, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + + .set_busid = rk_drm_set_busid, +}; + +CFATTACH_DECL_NEW(rk_drm, sizeof(struct rk_drm_softc), + rk_drm_match, rk_drm_attach, NULL, NULL); + +static int +rk_drm_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +rk_drm_attach(device_t parent, device_t self, void *aux) +{ + struct rk_drm_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + struct drm_driver * const driver = &rk_drm_driver; + prop_dictionary_t dict = device_properties(self); + bool is_disabled; + + sc->sc_dev = self; + sc->sc_dmat = faa->faa_dmat; + sc->sc_bst = faa->faa_bst; + sc->sc_phandle = faa->faa_phandle; + + aprint_naive("\n"); + + if (prop_dictionary_get_bool(dict, "disabled", &is_disabled) && is_disabled) { + aprint_normal(": (disabled)\n"); + return; + } + + aprint_normal("\n"); + + sc->sc_ddev = drm_dev_alloc(driver, sc->sc_dev); + if (sc->sc_ddev == NULL) { + aprint_error_dev(self, "couldn't allocate DRM device\n"); + return; + } + sc->sc_ddev->dev_private = sc; + sc->sc_ddev->bst = sc->sc_bst; + sc->sc_ddev->bus_dmat = sc->sc_dmat; + sc->sc_ddev->dmat = sc->sc_ddev->bus_dmat; + sc->sc_ddev->dmat_subregion_p = false; + + fdt_remove_bycompat(fb_compatible); + + config_defer(self, rk_drm_init); +} + +static void +rk_drm_init(device_t dev) +{ + struct rk_drm_softc * const sc = device_private(dev); + struct drm_driver * const driver = &rk_drm_driver; + int error; + + error = -drm_dev_register(sc->sc_ddev, 0); + if (error) { + drm_dev_unref(sc->sc_ddev); + aprint_error_dev(dev, "couldn't register DRM device: %d\n", + error); + return; + } + + aprint_normal_dev(dev, "initialized %s %d.%d.%d %s on minor %d\n", + driver->name, driver->major, driver->minor, driver->patchlevel, + driver->date, sc->sc_ddev->primary->index); +} + +static vmem_t * +rk_drm_alloc_cma_pool(struct drm_device *ddev, size_t cma_size) +{ + struct rk_drm_softc * const sc = rk_drm_private(ddev); + bus_dma_segment_t segs[1]; + int nsegs; + int error; + + error = bus_dmamem_alloc(sc->sc_dmat, cma_size, PAGE_SIZE, 0, + segs, 1, &nsegs, BUS_DMA_NOWAIT); + if (error) { + aprint_error_dev(sc->sc_dev, "couldn't allocate CMA pool\n"); + return NULL; + } + + return vmem_create("rkdrm", segs[0].ds_addr, segs[0].ds_len, + PAGE_SIZE, NULL, NULL, NULL, 0, VM_SLEEP, IPL_NONE); +} + +static int +rk_drm_set_busid(struct drm_device *ddev, struct drm_master *master) +{ + struct rk_drm_softc * const sc = rk_drm_private(ddev); + char id[32]; + + snprintf(id, sizeof(id), "platform:rk:%u", device_unit(sc->sc_dev)); + + master->unique = kzalloc(strlen(id) + 1, GFP_KERNEL); + if (master->unique == NULL) + return -ENOMEM; + strcpy(master->unique, id); + master->unique_len = strlen(master->unique); + + return 0; +} + +static int +rk_drm_fb_create_handle(struct drm_framebuffer *fb, + struct drm_file *file, unsigned int *handle) +{ + struct rk_drm_framebuffer *sfb = to_rk_drm_framebuffer(fb); + + return drm_gem_handle_create(file, &sfb->obj->base, handle); +} + +static void +rk_drm_fb_destroy(struct drm_framebuffer *fb) +{ + struct rk_drm_framebuffer *sfb = to_rk_drm_framebuffer(fb); + + drm_framebuffer_cleanup(fb); + drm_gem_object_unreference_unlocked(&sfb->obj->base); + kmem_free(sfb, sizeof(*sfb)); +} + +static const struct drm_framebuffer_funcs rk_drm_framebuffer_funcs = { + .create_handle = rk_drm_fb_create_handle, + .destroy = rk_drm_fb_destroy, +}; + +static struct drm_framebuffer * +rk_drm_fb_create(struct drm_device *ddev, struct drm_file *file, + struct drm_mode_fb_cmd2 *cmd) +{ + struct rk_drm_framebuffer *fb; + struct drm_gem_object *gem_obj; + int error; + + if (cmd->flags) + return NULL; + + gem_obj = drm_gem_object_lookup(ddev, file, cmd->handles[0]); + if (gem_obj == NULL) + return NULL; + + fb = kmem_zalloc(sizeof(*fb), KM_SLEEP); + fb->obj = to_drm_gem_cma_obj(gem_obj); + fb->base.pitches[0] = cmd->pitches[0]; + fb->base.pitches[1] = cmd->pitches[1]; + fb->base.pitches[2] = cmd->pitches[2]; + fb->base.offsets[0] = cmd->offsets[0]; + fb->base.offsets[1] = cmd->offsets[2]; + fb->base.offsets[2] = cmd->offsets[1]; + fb->base.width = cmd->width; + fb->base.height = cmd->height; + fb->base.pixel_format = cmd->pixel_format; + fb->base.bits_per_pixel = drm_format_plane_cpp(fb->base.pixel_format, 0) * 8; + + switch (fb->base.pixel_format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + fb->base.depth = 32; + break; + default: + break; + } + + error = drm_framebuffer_init(ddev, &fb->base, &rk_drm_framebuffer_funcs); + if (error != 0) + goto dealloc; + + return &fb->base; + +dealloc: + drm_framebuffer_cleanup(&fb->base); + kmem_free(fb, sizeof(*fb)); + drm_gem_object_unreference_unlocked(gem_obj); + + return NULL; +} + +static struct drm_mode_config_funcs rk_drm_mode_config_funcs = { + .fb_create = rk_drm_fb_create, +}; + +static int +rk_drm_fb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) +{ + struct rk_drm_softc * const sc = rk_drm_private(helper->dev); + struct drm_device *ddev = helper->dev; + struct rk_drm_framebuffer *sfb = to_rk_drm_framebuffer(helper->fb); + struct drm_framebuffer *fb = helper->fb; + struct rk_drmfb_attach_args sfa; + size_t cma_size; + int error; + + const u_int width = sizes->surface_width; + const u_int height = sizes->surface_height; + const u_int pitch = width * (32 / 8); + + const size_t size = roundup(height * pitch, PAGE_SIZE); + + /* Reserve enough memory for the FB console plus a 4K plane, rounded to 1MB */ + cma_size = size; + cma_size += (RK_DRM_MAX_WIDTH * RK_DRM_MAX_HEIGHT * 4); + cma_size = roundup(cma_size, 1024 * 1024); + sc->sc_ddev->cma_pool = rk_drm_alloc_cma_pool(sc->sc_ddev, cma_size); + if (sc->sc_ddev->cma_pool != NULL) + aprint_normal_dev(sc->sc_dev, "reserved %u MB DRAM for CMA\n", + (u_int)(cma_size / (1024 * 1024))); + + sfb->obj = drm_gem_cma_create(ddev, size); + if (sfb->obj == NULL) { + DRM_ERROR("failed to allocate memory for framebuffer\n"); + return -ENOMEM; + } + + fb->pitches[0] = pitch; + fb->offsets[0] = 0; + fb->width = width; + fb->height = height; + fb->pixel_format = DRM_FORMAT_XRGB8888; + drm_fb_get_bpp_depth(fb->pixel_format, &fb->depth, &fb->bits_per_pixel); + + error = drm_framebuffer_init(ddev, fb, &rk_drm_framebuffer_funcs); + if (error != 0) { + DRM_ERROR("failed to initialize framebuffer\n"); + return error; + } + + memset(&sfa, 0, sizeof(sfa)); + sfa.sfa_drm_dev = ddev; + sfa.sfa_fb_helper = helper; + sfa.sfa_fb_sizes = *sizes; + sfa.sfa_fb_bst = sc->sc_bst; + sfa.sfa_fb_dmat = sc->sc_dmat; + sfa.sfa_fb_linebytes = helper->fb->pitches[0]; + + helper->fbdev = config_found_ia(ddev->dev, "rkfbbus", &sfa, NULL); + if (helper->fbdev == NULL) { + DRM_ERROR("unable to attach framebuffer\n"); + return -ENXIO; + } + + return 0; +} + +static struct drm_fb_helper_funcs rk_drm_fb_helper_funcs = { + .fb_probe = rk_drm_fb_probe, +}; + +static int +rk_drm_load(struct drm_device *ddev, unsigned long flags) +{ + struct rk_drm_softc * const sc = rk_drm_private(ddev); + struct rk_drm_ports *sport; + struct rk_drm_fbdev *fbdev; + struct fdt_endpoint *ep; + const u_int *data; + int datalen, error, num_crtc, ep_index; + + drm_mode_config_init(ddev); + ddev->mode_config.min_width = 0; + ddev->mode_config.min_height = 0; + ddev->mode_config.max_width = RK_DRM_MAX_WIDTH; + ddev->mode_config.max_height = RK_DRM_MAX_HEIGHT; + ddev->mode_config.funcs = &rk_drm_mode_config_funcs; + + num_crtc = 0; + data = fdtbus_get_prop(sc->sc_phandle, "ports", &datalen); + while (datalen >= 4) { + const int crtc_phandle = fdtbus_get_phandle_from_native(be32dec(data)); + + TAILQ_FOREACH(sport, &rk_drm_ports, entries) + if (sport->phandle == crtc_phandle && sport->ddev == NULL) { + sport->ddev = ddev; + for (ep_index = 0; (ep = fdt_endpoint_get_from_index(sport->port, 0, ep_index)) != NULL; ep_index++) { + error = fdt_endpoint_activate_direct(ep, true); + if (error != 0) + aprint_debug_dev(sc->sc_dev, + "failed to activate endpoint %d: %d\n", + ep_index, error); + } + num_crtc++; + } + + datalen -= 4; + data++; + } + + if (num_crtc == 0) { + aprint_error_dev(sc->sc_dev, "no display interface ports configured\n"); + return ENXIO; + } + + fbdev = kmem_zalloc(sizeof(*fbdev), KM_SLEEP); + + drm_fb_helper_prepare(ddev, &fbdev->helper, &rk_drm_fb_helper_funcs); + + error = drm_fb_helper_init(ddev, &fbdev->helper, num_crtc, num_crtc); + if (error) + goto drmerr; + + fbdev->helper.fb = kmem_zalloc(sizeof(struct rk_drm_framebuffer), KM_SLEEP); + + drm_fb_helper_single_add_all_connectors(&fbdev->helper); + + drm_helper_disable_unused_functions(ddev); + + drm_fb_helper_initial_config(&fbdev->helper, 32); + + /* XXX */ + ddev->irq_enabled = true; + drm_vblank_init(ddev, num_crtc); + + return 0; + +drmerr: + drm_mode_config_cleanup(ddev); + kmem_free(fbdev, sizeof(*fbdev)); + + return error; +} + +static uint32_t +rk_drm_get_vblank_counter(struct drm_device *ddev, unsigned int crtc) +{ + struct rk_drm_softc * const sc = rk_drm_private(ddev); + + if (crtc >= __arraycount(sc->sc_vbl)) + return 0; + + if (sc->sc_vbl[crtc].get_vblank_counter == NULL) + return 0; + + return sc->sc_vbl[crtc].get_vblank_counter(sc->sc_vbl[crtc].priv); +} + +static int +rk_drm_enable_vblank(struct drm_device *ddev, unsigned int crtc) +{ + struct rk_drm_softc * const sc = rk_drm_private(ddev); + + if (crtc >= __arraycount(sc->sc_vbl)) + return 0; + + if (sc->sc_vbl[crtc].enable_vblank == NULL) + return 0; + + sc->sc_vbl[crtc].enable_vblank(sc->sc_vbl[crtc].priv); + + return 0; +} + +static void +rk_drm_disable_vblank(struct drm_device *ddev, unsigned int crtc) +{ + struct rk_drm_softc * const sc = rk_drm_private(ddev); + + if (crtc >= __arraycount(sc->sc_vbl)) + return; + + if (sc->sc_vbl[crtc].disable_vblank == NULL) + return; + + sc->sc_vbl[crtc].disable_vblank(sc->sc_vbl[crtc].priv); +} + +static int +rk_drm_unload(struct drm_device *ddev) +{ + drm_mode_config_cleanup(ddev); + + return 0; +} + +int +rk_drm_register_port(int phandle, struct fdt_device_ports *port) +{ + struct rk_drm_ports *sport; + + sport = kmem_zalloc(sizeof(*sport), KM_SLEEP); + sport->phandle = phandle; + sport->port = port; + sport->ddev = NULL; + TAILQ_INSERT_TAIL(&rk_drm_ports, sport, entries); + + return 0; +} + +struct drm_device * +rk_drm_port_device(struct fdt_device_ports *port) +{ + struct rk_drm_ports *sport; + + TAILQ_FOREACH(sport, &rk_drm_ports, entries) + if (sport->port == port) + return sport->ddev; + + return NULL; +} Index: src/sys/arch/arm/rockchip/rk_vop.c diff -u /dev/null src/sys/arch/arm/rockchip/rk_vop.c:1.2.2.2 --- /dev/null Sat Nov 16 16:48:26 2019 +++ src/sys/arch/arm/rockchip/rk_vop.c Sat Nov 16 16:48:25 2019 @@ -0,0 +1,659 @@ +/* $NetBSD: rk_vop.c,v 1.2.2.2 2019/11/16 16:48:25 martin Exp $ */ + +/*- + * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: rk_vop.c,v 1.2.2.2 2019/11/16 16:48:25 martin Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/intr.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/sysctl.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/fdt/fdt_port.h> + +#include <arm/rockchip/rk_drm.h> + +#define VOP_REG_CFG_DONE 0x0000 +#define REG_LOAD_EN __BIT(0) +#define VOP_SYS_CTRL 0x0008 +#define VOP_STANDBY_EN __BIT(22) +#define MIPI_OUT_EN __BIT(15) +#define EDP_OUT_EN __BIT(14) +#define HDMI_OUT_EN __BIT(13) +#define RGB_OUT_EN __BIT(12) +#define VOP_DSP_CTRL0 0x0010 +#define DSP_OUT_MODE __BITS(3,0) +#define DSP_OUT_MODE_RGB888 0 +#define DSP_OUT_MODE_RGBaaa 15 +#define VOP_DSP_CTRL1 0x0014 +#define VOP_WIN0_CTRL 0x0030 +#define WIN0_LB_MODE __BITS(7,5) +#define WIN0_LB_MODE_RGB_3840X2 2 +#define WIN0_LB_MODE_RGB_2560X4 3 +#define WIN0_LB_MODE_RGB_1920X5 4 +#define WIN0_LB_MODE_RGB_1280X8 5 +#define WIN0_DATA_FMT __BITS(3,1) +#define WIN0_DATA_FMT_ARGB888 0 +#define WIN0_EN __BIT(0) +#define VOP_WIN0_COLOR_KEY 0x0038 +#define VOP_WIN0_VIR 0x003c +#define WIN0_VIR_STRIDE __BITS(13,0) +#define VOP_WIN0_YRGB_MST 0x0040 +#define VOP_WIN0_ACT_INFO 0x0048 +#define WIN0_ACT_HEIGHT __BITS(28,16) +#define WIN0_ACT_WIDTH __BITS(12,0) +#define VOP_WIN0_DSP_INFO 0x004c +#define WIN0_DSP_HEIGHT __BITS(27,16) +#define WIN0_DSP_WIDTH __BITS(11,0) +#define VOP_WIN0_DSP_ST 0x0050 +#define WIN0_DSP_YST __BITS(28,16) +#define WIN0_DSP_XST __BITS(12,0) +#define VOP_POST_DSP_HACT_INFO 0x0170 +#define DSP_HACT_ST_POST __BITS(28,16) +#define DSP_HACT_END_POST __BITS(12,0) +#define VOP_POST_DSP_VACT_INFO 0x0174 +#define DSP_VACT_ST_POST __BITS(28,16) +#define DSP_VACT_END_POST __BITS(12,0) +#define VOP_DSP_HTOTAL_HS_END 0x0188 +#define DSP_HS_END __BITS(28,16) +#define DSP_HTOTAL __BITS(12,0) +#define VOP_DSP_HACT_ST_END 0x018c +#define DSP_HACT_ST __BITS(28,16) +#define DSP_HACT_END __BITS(12,0) +#define VOP_DSP_VTOTAL_VS_END 0x0190 +#define DSP_VS_END __BITS(28,16) +#define DSP_VTOTAL __BITS(12,0) +#define VOP_DSP_VACT_ST_END 0x0194 +#define DSP_VACT_ST __BITS(28,16) +#define DSP_VACT_END __BITS(12,0) + +/* + * Polarity fields are in different locations depending on SoC and output type, + * but always in the same order. + */ +#define DSP_DCLK_POL __BIT(3) +#define DSP_DEN_POL __BIT(2) +#define DSP_VSYNC_POL __BIT(1) +#define DSP_HSYNC_POL __BIT(0) + +enum vop_ep_type { + VOP_EP_MIPI, + VOP_EP_EDP, + VOP_EP_HDMI, + VOP_EP_MIPI1, + VOP_EP_DP, + VOP_NEP +}; + +struct rk_vop_softc; +struct rk_vop_config; + +struct rk_vop_crtc { + struct drm_crtc base; + struct rk_vop_softc *sc; +}; + +struct rk_vop_encoder { + struct drm_encoder base; + struct rk_vop_softc *sc; + enum vop_ep_type ep_type; +}; + +struct rk_vop_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + int sc_phandle; + + struct clk *sc_dclk; + + struct rk_vop_crtc sc_crtc; + struct rk_vop_encoder sc_encoder[VOP_NEP]; + + struct fdt_device_ports sc_ports; + + struct rk_vop_config *sc_conf; +}; + +#define to_rk_vop_crtc(x) container_of(x, struct rk_vop_crtc, base) +#define to_rk_vop_encoder(x) container_of(x, struct rk_vop_encoder, base) + +#define RD4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define WR4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) + +struct rk_vop_config { + const char *descr; + u_int out_mode; + void (*init)(struct rk_vop_softc *); + void (*set_polarity)(struct rk_vop_softc *, + enum vop_ep_type, uint32_t); +}; + +#define RK3399_VOP_MIPI_POL __BITS(31,28) +#define RK3399_VOP_EDP_POL __BITS(27,24) +#define RK3399_VOP_HDMI_POL __BITS(23,20) +#define RK3399_VOP_DP_POL __BITS(19,16) + +#define RK3399_VOP_SYS_CTRL_ENABLE __BIT(11) + +static void +rk3399_vop_set_polarity(struct rk_vop_softc *sc, enum vop_ep_type ep_type, uint32_t pol) +{ + uint32_t mask, val; + + switch (ep_type) { + case VOP_EP_MIPI: + case VOP_EP_MIPI1: + mask = RK3399_VOP_MIPI_POL; + break; + case VOP_EP_EDP: + mask = RK3399_VOP_EDP_POL; + break; + case VOP_EP_HDMI: + mask = RK3399_VOP_HDMI_POL; + break; + case VOP_EP_DP: + mask = RK3399_VOP_DP_POL; + break; + default: + return; + } + + val = RD4(sc, VOP_DSP_CTRL1); + val &= ~mask; + val |= __SHIFTIN(pol, mask); + WR4(sc, VOP_DSP_CTRL1, val); +} + +static void +rk3399_vop_init(struct rk_vop_softc *sc) +{ + uint32_t val; + + val = RD4(sc, VOP_SYS_CTRL); + val |= RK3399_VOP_SYS_CTRL_ENABLE; + WR4(sc, VOP_SYS_CTRL, val); +} + +static const struct rk_vop_config rk3399_vop_lit_config = { + .descr = "RK3399 VOPL", + .out_mode = DSP_OUT_MODE_RGB888, + .init = rk3399_vop_init, + .set_polarity = rk3399_vop_set_polarity, +}; + +static const struct rk_vop_config rk3399_vop_big_config = { + .descr = "RK3399 VOPB", + .out_mode = DSP_OUT_MODE_RGBaaa, + .init = rk3399_vop_init, + .set_polarity = rk3399_vop_set_polarity, +}; + +static const struct of_compat_data compat_data[] = { + { "rockchip,rk3399-vop-big", (uintptr_t)&rk3399_vop_big_config }, + { "rockchip,rk3399-vop-lit", (uintptr_t)&rk3399_vop_lit_config }, + { NULL } +}; + +static int +rk_vop_mode_do_set_base(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y, int atomic) +{ + struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc); + struct rk_vop_softc * const sc = mixer_crtc->sc; + struct rk_drm_framebuffer *sfb = atomic? + to_rk_drm_framebuffer(fb) : + to_rk_drm_framebuffer(crtc->primary->fb); + + uint64_t paddr = (uint64_t)sfb->obj->dmamap->dm_segs[0].ds_addr; + + KASSERT((paddr & ~0xffffffff) == 0); + + /* Framebuffer start address */ + WR4(sc, VOP_WIN0_YRGB_MST, (uint32_t)paddr); + + return 0; +} + +static void +rk_vop_destroy(struct drm_crtc *crtc) +{ + drm_crtc_cleanup(crtc); +} + +static const struct drm_crtc_funcs rk_vop_crtc_funcs = { + .set_config = drm_crtc_helper_set_config, + .destroy = rk_vop_destroy, +}; + +static void +rk_vop_dpms(struct drm_crtc *crtc, int mode) +{ +} + +static bool +rk_vop_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static int +rk_vop_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc); + struct rk_vop_softc * const sc = mixer_crtc->sc; + uint32_t val; + u_int lb_mode; + int error; + + const u_int hactive = adjusted_mode->hdisplay; + const u_int hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start; + const u_int hback_porch = adjusted_mode->htotal - adjusted_mode->hsync_end; + + const u_int vactive = adjusted_mode->vdisplay; + const u_int vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start; + const u_int vback_porch = adjusted_mode->vtotal - adjusted_mode->vsync_end; + + error = clk_set_rate(sc->sc_dclk, adjusted_mode->clock * 1000); + if (error != 0) + DRM_ERROR("couldn't set pixel clock: %d\n", error); + + val = __SHIFTIN(hactive - 1, WIN0_ACT_WIDTH) | + __SHIFTIN(vactive - 1, WIN0_ACT_HEIGHT); + WR4(sc, VOP_WIN0_ACT_INFO, val); + + val = __SHIFTIN(hactive - 1, WIN0_DSP_WIDTH) | + __SHIFTIN(vactive - 1, WIN0_DSP_HEIGHT); + WR4(sc, VOP_WIN0_DSP_INFO, val); + + val = __SHIFTIN(hsync_len + hback_porch, WIN0_DSP_XST) | + __SHIFTIN(vsync_len + vback_porch, WIN0_DSP_YST); + WR4(sc, VOP_WIN0_DSP_ST, val); + + WR4(sc, VOP_WIN0_COLOR_KEY, 0); + + val = __SHIFTIN(hactive, WIN0_VIR_STRIDE); + WR4(sc, VOP_WIN0_VIR, val); + + if (adjusted_mode->hdisplay > 2560) + lb_mode = WIN0_LB_MODE_RGB_3840X2; + else if (adjusted_mode->hdisplay > 1920) + lb_mode = WIN0_LB_MODE_RGB_2560X4; + else if (adjusted_mode->hdisplay > 1280) + lb_mode = WIN0_LB_MODE_RGB_1920X5; + else + lb_mode = WIN0_LB_MODE_RGB_1280X8; + + val = __SHIFTIN(lb_mode, WIN0_LB_MODE) | + __SHIFTIN(WIN0_DATA_FMT_ARGB888, WIN0_DATA_FMT) | + WIN0_EN; + WR4(sc, VOP_WIN0_CTRL, val); + + rk_vop_mode_do_set_base(crtc, old_fb, x, y, 0); + + return 0; +} + +static int +rk_vop_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc); + struct rk_vop_softc * const sc = mixer_crtc->sc; + + rk_vop_mode_do_set_base(crtc, old_fb, x, y, 0); + + /* Commit settings */ + WR4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN); + + return 0; +} + +static int +rk_vop_mode_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y, enum mode_set_atomic state) +{ + struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc); + struct rk_vop_softc * const sc = mixer_crtc->sc; + + rk_vop_mode_do_set_base(crtc, fb, x, y, 1); + + /* Commit settings */ + WR4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN); + + return 0; +} + +static void +rk_vop_disable(struct drm_crtc *crtc) +{ +} + +static void +rk_vop_prepare(struct drm_crtc *crtc) +{ +} + +static void +rk_vop_commit(struct drm_crtc *crtc) +{ + struct rk_vop_crtc *mixer_crtc = to_rk_vop_crtc(crtc); + struct rk_vop_softc * const sc = mixer_crtc->sc; + + /* Commit settings */ + WR4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN); +} + +static const struct drm_crtc_helper_funcs rk_vop_crtc_helper_funcs = { + .dpms = rk_vop_dpms, + .mode_fixup = rk_vop_mode_fixup, + .mode_set = rk_vop_mode_set, + .mode_set_base = rk_vop_mode_set_base, + .mode_set_base_atomic = rk_vop_mode_set_base_atomic, + .disable = rk_vop_disable, + .prepare = rk_vop_prepare, + .commit = rk_vop_commit, +}; + +static void +rk_vop_encoder_destroy(struct drm_encoder *encoder) +{ +} + +static const struct drm_encoder_funcs rk_vop_encoder_funcs = { + .destroy = rk_vop_encoder_destroy, +}; + +static void +rk_vop_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +} + +static bool +rk_vop_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void +rk_vop_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) +{ + struct rk_vop_encoder *rkencoder = to_rk_vop_encoder(encoder); + struct rk_vop_softc * const sc = rkencoder->sc; + uint32_t val; + u_int pol; + + const u_int hactive = adjusted_mode->hdisplay; + const u_int hfront_porch = adjusted_mode->hsync_start - adjusted_mode->hdisplay; + const u_int hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start; + const u_int hback_porch = adjusted_mode->htotal - adjusted_mode->hsync_end; + + const u_int vactive = adjusted_mode->vdisplay; + const u_int vfront_porch = adjusted_mode->vsync_start - adjusted_mode->vdisplay; + const u_int vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start; + const u_int vback_porch = adjusted_mode->vtotal - adjusted_mode->vsync_end; + + pol = DSP_DCLK_POL; + if ((adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) != 0) + pol |= DSP_HSYNC_POL; + if ((adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) != 0) + pol |= DSP_VSYNC_POL; + sc->sc_conf->set_polarity(sc, rkencoder->ep_type, pol); + + val = RD4(sc, VOP_SYS_CTRL); + val &= ~VOP_STANDBY_EN; + val &= ~(MIPI_OUT_EN|EDP_OUT_EN|HDMI_OUT_EN|RGB_OUT_EN); + switch (rkencoder->ep_type) { + case VOP_EP_MIPI: + case VOP_EP_MIPI1: + val |= MIPI_OUT_EN; + break; + case VOP_EP_EDP: + case VOP_EP_DP: + val |= EDP_OUT_EN; + break; + case VOP_EP_HDMI: + val |= HDMI_OUT_EN; + break; + default: + break; + } + WR4(sc, VOP_SYS_CTRL, val); + + val = RD4(sc, VOP_DSP_CTRL0); + val &= ~DSP_OUT_MODE; + val |= __SHIFTIN(sc->sc_conf->out_mode, DSP_OUT_MODE); + WR4(sc, VOP_DSP_CTRL0, val); + + val = __SHIFTIN(hsync_len + hback_porch, DSP_HACT_ST_POST) | + __SHIFTIN(hsync_len + hback_porch + hactive, DSP_HACT_END_POST); + WR4(sc, VOP_POST_DSP_HACT_INFO, val); + + val = __SHIFTIN(hsync_len + hback_porch, DSP_HACT_ST) | + __SHIFTIN(hsync_len + hback_porch + hactive, DSP_HACT_END); + WR4(sc, VOP_DSP_HACT_ST_END, val); + + val = __SHIFTIN(hsync_len, DSP_HTOTAL) | + __SHIFTIN(hsync_len + hback_porch + hactive + hfront_porch, DSP_HS_END); + WR4(sc, VOP_DSP_HTOTAL_HS_END, val); + + val = __SHIFTIN(vsync_len + vback_porch, DSP_VACT_ST_POST) | + __SHIFTIN(vsync_len + vback_porch + vactive, DSP_VACT_END_POST); + WR4(sc, VOP_POST_DSP_VACT_INFO, val); + + val = __SHIFTIN(vsync_len + vback_porch, DSP_VACT_ST) | + __SHIFTIN(vsync_len + vback_porch + vactive, DSP_VACT_END); + WR4(sc, VOP_DSP_VACT_ST_END, val); + + val = __SHIFTIN(vsync_len, DSP_VTOTAL) | + __SHIFTIN(vsync_len + vback_porch + vactive + vfront_porch, DSP_VS_END); + WR4(sc, VOP_DSP_VTOTAL_VS_END, val); +} + +static void +rk_vop_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void +rk_vop_encoder_commit(struct drm_encoder *encoder) +{ + struct rk_vop_encoder *rkencoder = to_rk_vop_encoder(encoder); + struct rk_vop_softc * const sc = rkencoder->sc; + + /* Commit settings */ + WR4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN); +} + +static const struct drm_encoder_helper_funcs rk_vop_encoder_helper_funcs = { + .dpms = rk_vop_encoder_dpms, + .mode_fixup = rk_vop_encoder_mode_fixup, + .prepare = rk_vop_encoder_prepare, + .commit = rk_vop_encoder_commit, + .mode_set = rk_vop_encoder_mode_set, +}; + +static int +rk_vop_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate) +{ + struct rk_vop_softc * const sc = device_private(dev); + struct drm_device *ddev; + u_int encoder_type; + + if (!activate) + return EINVAL; + + ddev = rk_drm_port_device(&sc->sc_ports); + if (ddev == NULL) { + DRM_ERROR("couldn't find DRM device\n"); + return ENXIO; + } + + if (sc->sc_crtc.sc == NULL) { + sc->sc_crtc.sc = sc; + + drm_crtc_init(ddev, &sc->sc_crtc.base, &rk_vop_crtc_funcs); + drm_crtc_helper_add(&sc->sc_crtc.base, &rk_vop_crtc_helper_funcs); + + aprint_debug_dev(dev, "using CRTC %d for %s\n", + drm_crtc_index(&sc->sc_crtc.base), sc->sc_conf->descr); + } + + const u_int ep_index = fdt_endpoint_index(ep); + if (ep_index >= VOP_NEP) { + DRM_ERROR("endpoint index %d out of range\n", ep_index); + return ENXIO; + } + + switch (ep_index) { + case VOP_EP_MIPI: + case VOP_EP_MIPI1: + encoder_type = DRM_MODE_ENCODER_DSI; + break; + case VOP_EP_HDMI: + case VOP_EP_EDP: + case VOP_EP_DP: + encoder_type = DRM_MODE_ENCODER_TMDS; + break; + } + + sc->sc_encoder[ep_index].sc = sc; + sc->sc_encoder[ep_index].ep_type = ep_index; + sc->sc_encoder[ep_index].base.possible_crtcs = 1 << drm_crtc_index(&sc->sc_crtc.base); + drm_encoder_init(ddev, &sc->sc_encoder[ep_index].base, &rk_vop_encoder_funcs, + encoder_type); + drm_encoder_helper_add(&sc->sc_encoder[ep_index].base, &rk_vop_encoder_helper_funcs); + + return fdt_endpoint_activate(ep, activate); +} + +static void * +rk_vop_ep_get_data(device_t dev, struct fdt_endpoint *ep) +{ + struct rk_vop_softc * const sc = device_private(dev); + const u_int ep_index = fdt_endpoint_index(ep); + + if (ep_index >= VOP_NEP) + return NULL; + + if (sc->sc_encoder[ep_index].sc == NULL) + return NULL; + + return &sc->sc_encoder[ep_index].base; +} + +static int +rk_vop_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_match_compat_data(faa->faa_phandle, compat_data); +} + +static void +rk_vop_attach(device_t parent, device_t self, void *aux) +{ + struct rk_vop_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + const char * const reset_names[] = { "axi", "ahb", "dclk" }; + const char * const clock_names[] = { "aclk_vop", "hclk_vop" }; + struct fdtbus_reset *rst; + bus_addr_t addr; + bus_size_t size; + u_int n; + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + + fdtbus_clock_assign(phandle); + + for (n = 0; n < __arraycount(reset_names); n++) { + rst = fdtbus_reset_get(phandle, reset_names[n]); + if (rst == NULL || fdtbus_reset_deassert(rst) != 0) { + aprint_error(": couldn't de-assert reset %s\n", reset_names[n]); + return; + } + } + for (n = 0; n < __arraycount(clock_names); n++) { + if (fdtbus_clock_enable(phandle, clock_names[n], true) != 0) { + aprint_error(": couldn't enable clock %s\n", clock_names[n]); + return; + } + } + sc->sc_dclk = fdtbus_clock_get(phandle, "dclk_vop"); + if (sc->sc_dclk == NULL || clk_enable(sc->sc_dclk) != 0) { + aprint_error(": couldn't enable clock %s\n", "dclk_vop"); + return; + } + + sc->sc_dev = self; + sc->sc_bst = faa->faa_bst; + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + sc->sc_phandle = faa->faa_phandle; + sc->sc_conf = (void *)of_search_compatible(phandle, compat_data)->data; + + aprint_naive("\n"); + aprint_normal(": %s\n", sc->sc_conf->descr); + + if (sc->sc_conf->init != NULL) + sc->sc_conf->init(sc); + + sc->sc_ports.dp_ep_activate = rk_vop_ep_activate; + sc->sc_ports.dp_ep_get_data = rk_vop_ep_get_data; + fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_ENCODER); + + const int port_phandle = of_find_firstchild_byname(phandle, "port"); + if (port_phandle > 0) + rk_drm_register_port(port_phandle, &sc->sc_ports); +} + +CFATTACH_DECL_NEW(rk_vop, sizeof(struct rk_vop_softc), + rk_vop_match, rk_vop_attach, NULL, NULL); Index: src/sys/arch/arm/rockchip/rk_drm.h diff -u /dev/null src/sys/arch/arm/rockchip/rk_drm.h:1.1.2.2 --- /dev/null Sat Nov 16 16:48:26 2019 +++ src/sys/arch/arm/rockchip/rk_drm.h Sat Nov 16 16:48:25 2019 @@ -0,0 +1,99 @@ +/* $NetBSD: rk_drm.h,v 1.1.2.2 2019/11/16 16:48:25 martin Exp $ */ + +/*- + * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _ARM_RK_DRM_H +#define _ARM_RK_DRM_H + +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_cma_helper.h> + +#define DRIVER_AUTHOR "Jared McNeill" + +#define DRIVER_NAME "rk" +#define DRIVER_DESC "Rockchip Display Subsystem" +#define DRIVER_DATE "20191109" + +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +struct rk_framebuffer; + +#define RK_DRM_MAX_CRTC 2 + +struct rk_drm_vblank { + void *priv; + void (*enable_vblank)(void *); + void (*disable_vblank)(void *); + uint32_t (*get_vblank_counter)(void *); +}; + +struct rk_drm_softc { + device_t sc_dev; + struct drm_device *sc_ddev; + + bus_space_tag_t sc_bst; + bus_dma_tag_t sc_dmat; + + int sc_phandle; + + struct rk_drm_vblank sc_vbl[RK_DRM_MAX_CRTC]; +}; + +struct rk_drm_framebuffer { + struct drm_framebuffer base; + struct drm_gem_cma_object *obj; +}; + +struct rk_drm_ports { + int phandle; + struct fdt_device_ports *port; + struct drm_device *ddev; + TAILQ_ENTRY(rk_drm_ports) entries; +}; + +struct rk_drm_fbdev { + struct drm_fb_helper helper; +}; + +struct rk_drmfb_attach_args { + struct drm_device *sfa_drm_dev; + struct drm_fb_helper *sfa_fb_helper; + struct drm_fb_helper_surface_size sfa_fb_sizes; + bus_space_tag_t sfa_fb_bst; + bus_dma_tag_t sfa_fb_dmat; + uint32_t sfa_fb_linebytes; +}; + +#define rk_drm_private(ddev) (ddev)->dev_private +#define to_rk_drm_framebuffer(x) container_of(x, struct rk_drm_framebuffer, base) + +int rk_drm_register_port(int, struct fdt_device_ports *); +struct drm_device *rk_drm_port_device(struct fdt_device_ports *); + +#endif /* _ARM_RK_DRM_H */ Index: src/sys/arch/arm/rockchip/rk_fb.c diff -u /dev/null src/sys/arch/arm/rockchip/rk_fb.c:1.1.2.2 --- /dev/null Sat Nov 16 16:48:26 2019 +++ src/sys/arch/arm/rockchip/rk_fb.c Sat Nov 16 16:48:25 2019 @@ -0,0 +1,160 @@ +/* $NetBSD: rk_fb.c,v 1.1.2.2 2019/11/16 16:48:25 martin Exp $ */ + +/*- + * Copyright (c) 2015-2019 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_wsdisplay_compat.h" + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: rk_fb.c,v 1.1.2.2 2019/11/16 16:48:25 martin Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> + +#include <dev/fdt/fdtvar.h> + +#include <drm/drmP.h> +#include <drm/drmfb.h> + +#include <arm/rockchip/rk_drm.h> + +static int rk_fb_match(device_t, cfdata_t, void *); +static void rk_fb_attach(device_t, device_t, void *); + +static bool rk_fb_shutdown(device_t, int); + +struct rk_fb_softc { + struct drmfb_softc sc_drmfb; + device_t sc_dev; + struct rk_drm_framebuffer *sc_fb; + struct rk_drmfb_attach_args sc_sfa; +}; + +static paddr_t rk_fb_mmapfb(struct drmfb_softc *, off_t, int); +static int rk_fb_ioctl(struct drmfb_softc *, u_long, void *, int, + lwp_t *); + +static const struct drmfb_params rkfb_drmfb_params = { + .dp_mmapfb = rk_fb_mmapfb, + .dp_ioctl = rk_fb_ioctl, + +}; + +CFATTACH_DECL_NEW(rk_fb, sizeof(struct rk_fb_softc), + rk_fb_match, rk_fb_attach, NULL, NULL); + +static int +rk_fb_match(device_t parent, cfdata_t cf, void *aux) +{ + return 1; +} + +static void +rk_fb_attach(device_t parent, device_t self, void *aux) +{ + struct rk_fb_softc * const sc = device_private(self); + struct rk_drmfb_attach_args * const sfa = aux; + int error; + + sc->sc_dev = self; + sc->sc_sfa = *sfa; + sc->sc_fb = to_rk_drm_framebuffer(sfa->sfa_fb_helper->fb); + + aprint_naive("\n"); + aprint_normal("\n"); + +#ifdef WSDISPLAY_MULTICONS + prop_dictionary_t dict = device_properties(self); + const bool is_console = true; + prop_dictionary_set_bool(dict, "is_console", is_console); +#endif + + const struct drmfb_attach_args da = { + .da_dev = self, + .da_fb_helper = sfa->sfa_fb_helper, + .da_fb_sizes = &sfa->sfa_fb_sizes, + .da_fb_vaddr = sc->sc_fb->obj->vaddr, + .da_fb_linebytes = sfa->sfa_fb_linebytes, + .da_params = &rkfb_drmfb_params, + }; + + error = drmfb_attach(&sc->sc_drmfb, &da); + if (error) { + aprint_error_dev(self, "failed to attach drmfb: %d\n", error); + return; + } + + pmf_device_register1(self, NULL, NULL, rk_fb_shutdown); +} + +static bool +rk_fb_shutdown(device_t self, int flags) +{ + struct rk_fb_softc * const sc = device_private(self); + + return drmfb_shutdown(&sc->sc_drmfb, flags); +} + +static paddr_t +rk_fb_mmapfb(struct drmfb_softc *sc, off_t off, int prot) +{ + struct rk_fb_softc * const tfb_sc = (struct rk_fb_softc *)sc; + struct drm_gem_cma_object *obj = tfb_sc->sc_fb->obj; + + KASSERT(off >= 0); + KASSERT(off < obj->dmasize); + + return bus_dmamem_mmap(obj->dmat, obj->dmasegs, 1, off, prot, + BUS_DMA_PREFETCHABLE); +} + +static int +rk_fb_ioctl(struct drmfb_softc *sc, u_long cmd, void *data, int flag, + lwp_t *l) +{ + struct wsdisplayio_bus_id *busid; + struct wsdisplayio_fbinfo *fbi; + struct rasops_info *ri = &sc->sc_genfb.vd.active->scr_ri; + int error; + + switch (cmd) { + case WSDISPLAYIO_GET_BUSID: + busid = data; + busid->bus_type = WSDISPLAYIO_BUS_SOC; + return 0; + case WSDISPLAYIO_GTYPE: + *(u_int *)data = WSDISPLAY_TYPE_GENFB; + return 0; + case WSDISPLAYIO_GET_FBINFO: + fbi = data; + error = wsdisplayio_get_fbinfo(ri, fbi); + fbi->fbi_flags |= WSFB_VRAM_IS_RAM; + return error; + default: + return EPASSTHROUGH; + } +} Index: src/sys/arch/arm/rockchip/rk_i2s.c diff -u /dev/null src/sys/arch/arm/rockchip/rk_i2s.c:1.1.2.2 --- /dev/null Sat Nov 16 16:48:26 2019 +++ src/sys/arch/arm/rockchip/rk_i2s.c Sat Nov 16 16:48:25 2019 @@ -0,0 +1,638 @@ +/* $NetBSD: rk_i2s.c,v 1.1.2.2 2019/11/16 16:48:25 martin Exp $ */ + +/*- + * Copyright (c) 2019 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: rk_i2s.c,v 1.1.2.2 2019/11/16 16:48:25 martin Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/cpu.h> +#include <sys/device.h> +#include <sys/kmem.h> + +#include <sys/audioio.h> +#include <dev/audio/audio_if.h> +#include <dev/audio/linear.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/fdt/syscon.h> + +#define RK_I2S_FIFO_DEPTH 32 +#define RK_I2S_SAMPLE_RATE 48000 + +#define I2S_TXCR 0x00 +#define TXCR_RCNT __BITS(22,17) +#define TXCR_TCSR __BITS(16,15) +#define TXCR_HWT __BIT(14) +#define TXCR_SJM __BIT(12) +#define TXCR_FBM __BIT(11) +#define TXCR_IBM __BITS(10,9) +#define TXCR_PBM __BITS(8,7) +#define TXCR_TFS __BIT(5) +#define TXCR_VDW __BITS(4,0) +#define I2S_RXCR 0x04 +#define RXCR_RCSR __BITS(16,15) +#define RXCR_HWT __BIT(14) +#define RXCR_SJM __BIT(12) +#define RXCR_FBM __BIT(11) +#define RXCR_IBM __BITS(10,9) +#define RXCR_PBM __BITS(8,7) +#define RXCR_TFS __BIT(5) +#define RXCR_VDW __BITS(4,0) +#define I2S_CKR 0x08 +#define CKR_TRCM __BITS(29,28) +#define CKR_MSS __BIT(27) +#define CKR_CKP __BIT(26) +#define CKR_RLP __BIT(25) +#define CKR_TLP __BIT(24) +#define CKR_MDIV __BITS(23,16) +#define CKR_RSD __BITS(15,8) +#define CKR_TSD __BITS(7,0) +#define I2S_TXFIFOLR 0x0c +#define TXFIFOLR_TFL(n) __BITS((n) * 6 + 5, (n) * 6) +#define I2S_DMACR 0x10 +#define DMACR_RDE __BIT(24) +#define DMACR_RDL __BITS(20,16) +#define DMACR_TDE __BIT(8) +#define DMACR_TDL __BITS(4,0) +#define I2S_INTCR 0x14 +#define INTCR_RFT __BITS(24,20) +#define INTCR_RXOIC __BIT(18) +#define INTCR_RXOIE __BIT(17) +#define INTCR_RXFIE __BIT(16) +#define INTCR_TFT __BITS(8,4) +#define INTCR_TXUIC __BIT(2) +#define INTCR_TXUIE __BIT(1) +#define INTCR_TXEIE __BIT(0) +#define I2S_INTSR 0x18 +#define INTSR_RXOI __BIT(17) +#define INTSR_RXFI __BIT(16) +#define INTSR_TXUI __BIT(1) +#define INTSR_TXEI __BIT(0) +#define I2S_XFER 0x1c +#define XFER_RXS __BIT(1) +#define XFER_TXS __BIT(0) +#define I2S_CLR 0x20 +#define CLR_RXC __BIT(1) +#define CLR_TXC __BIT(0) +#define I2S_TXDR 0x24 +#define I2S_RXDR 0x28 +#define I2S_RXFIFOLR 0x2c +#define RXFIFOLR_RFL(n) __BITS((n) * 6 + 5, (n) * 6) + +struct rk_i2s_config { + bus_size_t oe_reg; + u_int oe_mask; + u_int oe_val; +}; + +static const struct rk_i2s_config rk3399_i2s_config = { + .oe_reg = 0x0e220, + .oe_mask = __BITS(13,11), + .oe_val = 0x7, +}; + +static const struct of_compat_data compat_data[] = { + { "rockchip,rk3399-i2s", (uintptr_t)&rk3399_i2s_config }, + { NULL } +}; + +struct rk_i2s_softc; + +struct rk_i2s_chan { + uint32_t *ch_start; + uint32_t *ch_end; + uint32_t *ch_cur; + + int ch_blksize; + int ch_resid; + + void (*ch_intr)(void *); + void *ch_intrarg; +}; + +struct rk_i2s_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + int sc_phandle; + struct clk *sc_clk; + struct syscon *sc_grf; + const struct rk_i2s_config *sc_conf; + + kmutex_t sc_lock; + kmutex_t sc_intr_lock; + + struct audio_format sc_format; + + struct rk_i2s_chan sc_pchan; + struct rk_i2s_chan sc_rchan; + + u_int sc_active; + + struct audio_dai_device sc_dai; +}; + +#define RD4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define WR4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) + +static int +rk_i2s_query_format(void *priv, audio_format_query_t *afp) +{ + struct rk_i2s_softc * const sc = priv; + + return audio_query_format(&sc->sc_format, 1, afp); +} + +static int +rk_i2s_set_format(void *priv, int setmode, + const audio_params_t *play, const audio_params_t *rec, + audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) +{ + struct rk_i2s_softc * const sc = priv; + uint32_t ckr, txcr, rxcr; + + ckr = RD4(sc, I2S_CKR); + if ((ckr & CKR_MSS) == 0) { + const u_int mclk_rate = clk_get_rate(sc->sc_clk); + device_printf(sc->sc_dev, "%s: sysclk rate %u Hz\n", __func__, mclk_rate); + const u_int bclk_rate = 2 * 32 * RK_I2S_SAMPLE_RATE; + const u_int bclk_div = mclk_rate / bclk_rate; + const u_int lrck_div = bclk_rate / RK_I2S_SAMPLE_RATE; + + ckr &= ~CKR_MDIV; + ckr |= __SHIFTIN(bclk_div - 1, CKR_MDIV); + ckr &= ~CKR_TSD; + ckr |= __SHIFTIN(lrck_div - 1, CKR_TSD); + ckr &= ~CKR_RSD; + ckr |= __SHIFTIN(lrck_div - 1, CKR_RSD); + } + + ckr &= ~CKR_TRCM; + ckr |= __SHIFTIN(0, CKR_TRCM); + WR4(sc, I2S_CKR, ckr); + + if (play && (setmode & AUMODE_PLAY) != 0) { + if (play->channels & 1) + return EINVAL; + txcr = RD4(sc, I2S_TXCR); + txcr &= ~TXCR_VDW; + txcr |= __SHIFTIN(play->validbits - 1, TXCR_VDW); + txcr &= ~TXCR_TCSR; + txcr |= __SHIFTIN(play->channels / 2 - 1, TXCR_TCSR); + WR4(sc, I2S_TXCR, txcr); + } + + if (rec && (setmode & AUMODE_RECORD) != 0) { + if (rec->channels & 1) + return EINVAL; + rxcr = RD4(sc, I2S_RXCR); + rxcr &= ~RXCR_VDW; + rxcr |= __SHIFTIN(rec->validbits - 1, RXCR_VDW); + rxcr &= ~RXCR_RCSR; + rxcr |= __SHIFTIN(rec->channels / 2 - 1, RXCR_RCSR); + WR4(sc, I2S_RXCR, rxcr); + } + + return 0; +} + +static int +rk_i2s_get_props(void *priv) +{ + + return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE | + AUDIO_PROP_FULLDUPLEX; +} + +static int +rk_i2s_round_blocksize(void *priv, int bs, int mode, + const audio_params_t *params) +{ + bs &= ~3; + if (bs == 0) + bs = 4; + return bs; +} + +static void * +rk_i2s_allocm(void *priv, int dir, size_t size) +{ + return kmem_zalloc(size, KM_SLEEP); +} + +static void +rk_i2s_freem(void *priv, void *addr, size_t size) +{ + kmem_free(addr, size); +} + +static int +rk_i2s_trigger_output(void *priv, void *start, void *end, int blksize, + void (*intr)(void *), void *intrarg, const audio_params_t *params) +{ + struct rk_i2s_softc * const sc = priv; + struct rk_i2s_chan *ch = &sc->sc_pchan; + uint32_t val; + + if (sc->sc_active == 0) { + val = RD4(sc, I2S_XFER); + val |= (XFER_TXS | XFER_RXS); + WR4(sc, I2S_XFER, val); + } + + sc->sc_active |= XFER_TXS; + + val = RD4(sc, I2S_INTCR); + val |= INTCR_TXEIE; + val &= ~INTCR_TFT; + val |= __SHIFTIN(RK_I2S_FIFO_DEPTH / 2, INTCR_TFT); + WR4(sc, I2S_INTCR, val); + + ch->ch_intr = intr; + ch->ch_intrarg = intrarg; + ch->ch_start = ch->ch_cur = start; + ch->ch_end = end; + ch->ch_blksize = blksize; + ch->ch_resid = blksize; + + return 0; +} + +static int +rk_i2s_trigger_input(void *priv, void *start, void *end, int blksize, + void (*intr)(void *), void *intrarg, const audio_params_t *params) +{ + return EIO; +} + +static int +rk_i2s_halt_output(void *priv) +{ + struct rk_i2s_softc * const sc = priv; + struct rk_i2s_chan *ch = &sc->sc_pchan; + uint32_t val; + + sc->sc_active &= ~XFER_TXS; + if (sc->sc_active == 0) { + val = RD4(sc, I2S_XFER); + val &= ~(XFER_TXS|XFER_RXS); + WR4(sc, I2S_XFER, val); + } + + val = RD4(sc, I2S_INTCR); + val &= ~INTCR_TXEIE; + WR4(sc, I2S_INTCR, val); + + val = RD4(sc, I2S_CLR); + val |= CLR_TXC; + WR4(sc, I2S_CLR, val); + + while ((RD4(sc, I2S_CLR) & CLR_TXC) != 0) + delay(1); + + ch->ch_intr = NULL; + ch->ch_intrarg = NULL; + + return 0; +} + +static int +rk_i2s_halt_input(void *priv) +{ + struct rk_i2s_softc * const sc = priv; + struct rk_i2s_chan *ch = &sc->sc_rchan; + uint32_t val; + + sc->sc_active &= ~XFER_RXS; + if (sc->sc_active == 0) { + val = RD4(sc, I2S_XFER); + val &= ~(XFER_TXS|XFER_RXS); + WR4(sc, I2S_XFER, val); + } + + val = RD4(sc, I2S_INTCR); + val &= ~INTCR_RXFIE; + WR4(sc, I2S_INTCR, val); + + ch->ch_intr = NULL; + ch->ch_intrarg = NULL; + + return 0; +} + +static void +rk_i2s_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread) +{ + struct rk_i2s_softc * const sc = priv; + + *intr = &sc->sc_intr_lock; + *thread = &sc->sc_lock; +} + +static const struct audio_hw_if rk_i2s_hw_if = { + .query_format = rk_i2s_query_format, + .set_format = rk_i2s_set_format, + .get_props = rk_i2s_get_props, + .round_blocksize = rk_i2s_round_blocksize, + .allocm = rk_i2s_allocm, + .freem = rk_i2s_freem, + .trigger_output = rk_i2s_trigger_output, + .trigger_input = rk_i2s_trigger_input, + .halt_output = rk_i2s_halt_output, + .halt_input = rk_i2s_halt_input, + .get_locks = rk_i2s_get_locks, +}; + +static int +rk_i2s_intr(void *priv) +{ + struct rk_i2s_softc * const sc = priv; + struct rk_i2s_chan * const pch = &sc->sc_pchan; +#if notyet + struct rk_i2s_chan * const rch = &sc->sc_rchan; +#endif + uint32_t sr, val; + int fifolr; + + mutex_enter(&sc->sc_intr_lock); + + sr = RD4(sc, I2S_INTSR); + + if ((sr & INTSR_RXFI) != 0) { +#if notyet + val = RD4(sc, I2S_RXFIFOLR); + fifolr = __SHIFTOUT(val, RXFIFOLR_RFL(0)); + while (fifolr > 0) { + *rch->ch_data = RD4(sc, I2S_RXDR); + rch->ch_data++; + rch->ch_resid -= 4; + if (rch->ch_resid == 0) + rch->ch_intr(rch->ch_intrarg); + --fifolr; + } +#endif + } + + if ((sr & INTSR_TXEI) != 0) { + val = RD4(sc, I2S_TXFIFOLR); + fifolr = __SHIFTOUT(val, TXFIFOLR_TFL(0)); + fifolr = uimin(fifolr, RK_I2S_FIFO_DEPTH); + while (fifolr < RK_I2S_FIFO_DEPTH - 1) { + WR4(sc, I2S_TXDR, *pch->ch_cur); + pch->ch_cur++; + if (pch->ch_cur == pch->ch_end) + pch->ch_cur = pch->ch_start; + pch->ch_resid -= 4; + if (pch->ch_resid == 0) { + pch->ch_intr(pch->ch_intrarg); + pch->ch_resid = pch->ch_blksize; + } + ++fifolr; + } + } + + mutex_exit(&sc->sc_intr_lock); + + return 0; +} + +static int +rk_i2s_dai_set_sysclk(audio_dai_tag_t dai, u_int rate, int dir) +{ + struct rk_i2s_softc * const sc = audio_dai_private(dai); + int error; + + device_printf(sc->sc_dev, "set sysclk %u Hz\n", rate); + error = clk_set_rate(sc->sc_clk, rate); + if (error != 0) { + device_printf(sc->sc_dev, "failed to set sysclk to %u Hz: %d\n", + rate, error); + return error; + } + + return 0; +} + +static int +rk_i2s_dai_set_format(audio_dai_tag_t dai, u_int format) +{ + struct rk_i2s_softc * const sc = audio_dai_private(dai); + uint32_t txcr, rxcr, ckr; + + const u_int fmt = __SHIFTOUT(format, AUDIO_DAI_FORMAT_MASK); + const u_int pol = __SHIFTOUT(format, AUDIO_DAI_POLARITY_MASK); + const u_int clk = __SHIFTOUT(format, AUDIO_DAI_CLOCK_MASK); + + txcr = RD4(sc, I2S_TXCR); + rxcr = RD4(sc, I2S_RXCR); + ckr = RD4(sc, I2S_CKR); + + txcr &= ~(TXCR_IBM|TXCR_PBM|TXCR_TFS); + rxcr &= ~(RXCR_IBM|RXCR_PBM|RXCR_TFS); + switch (fmt) { + case AUDIO_DAI_FORMAT_I2S: + txcr |= __SHIFTIN(0, TXCR_IBM); + rxcr |= __SHIFTIN(0, RXCR_IBM); + break; + case AUDIO_DAI_FORMAT_LJ: + txcr |= __SHIFTIN(1, TXCR_IBM); + rxcr |= __SHIFTIN(1, RXCR_IBM); + break; + case AUDIO_DAI_FORMAT_RJ: + txcr |= __SHIFTIN(2, TXCR_IBM); + rxcr |= __SHIFTIN(2, RXCR_IBM); + break; + case AUDIO_DAI_FORMAT_DSPA: + txcr |= __SHIFTIN(0, TXCR_PBM); + txcr |= TXCR_TFS; + rxcr |= __SHIFTIN(0, RXCR_PBM); + txcr |= RXCR_TFS; + break; + case AUDIO_DAI_FORMAT_DSPB: + txcr |= __SHIFTIN(1, TXCR_PBM); + txcr |= TXCR_TFS; + rxcr |= __SHIFTIN(1, RXCR_PBM); + txcr |= RXCR_TFS; + break; + default: + return EINVAL; + } + + WR4(sc, I2S_TXCR, txcr); + WR4(sc, I2S_RXCR, rxcr); + + switch (pol) { + case AUDIO_DAI_POLARITY_IB_NF: + ckr |= CKR_CKP; + break; + case AUDIO_DAI_POLARITY_NB_NF: + ckr &= ~CKR_CKP; + break; + default: + return EINVAL; + } + + switch (clk) { + case AUDIO_DAI_CLOCK_CBM_CFM: + ckr |= CKR_MSS; /* sclk input */ + break; + case AUDIO_DAI_CLOCK_CBS_CFS: + ckr &= ~CKR_MSS; /* sclk output */ + break; + default: + return EINVAL; + } + + WR4(sc, I2S_CKR, ckr); + + return 0; +} + +static audio_dai_tag_t +rk_i2s_dai_get_tag(device_t dev, const void *data, size_t len) +{ + struct rk_i2s_softc * const sc = device_private(dev); + + if (len != 4) + return NULL; + + return &sc->sc_dai; +} + +static struct fdtbus_dai_controller_func rk_i2s_dai_funcs = { + .get_tag = rk_i2s_dai_get_tag +}; + +static int +rk_i2s_clock_init(struct rk_i2s_softc *sc) +{ + const int phandle = sc->sc_phandle; + int error; + + sc->sc_clk = fdtbus_clock_get(phandle, "i2s_clk"); + if (sc->sc_clk == NULL) { + aprint_error(": couldn't find i2s_clk clock\n"); + return ENXIO; + } + error = clk_enable(sc->sc_clk); + if (error != 0) { + aprint_error(": couldn't enable i2s_clk clock: %d\n", error); + return error; + } + + /* Enable bus clock */ + if (fdtbus_clock_enable(phandle, "i2s_hclk", true) != 0) { + aprint_error(": couldn't enable i2s_hclk clock: %d\n", error); + return error; + } + + return 0; +} + +static int +rk_i2s_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_match_compat_data(faa->faa_phandle, compat_data); +} + +static void +rk_i2s_attach(device_t parent, device_t self, void *aux) +{ + struct rk_i2s_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + char intrstr[128]; + bus_addr_t addr; + bus_size_t size; + uint32_t val; + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error(": couldn't decode interrupt\n"); + return; + } + + sc->sc_dev = self; + sc->sc_phandle = phandle; + sc->sc_bst = faa->faa_bst; + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); + + sc->sc_conf = (void *)of_search_compatible(phandle, compat_data)->data; + sc->sc_grf = fdtbus_syscon_acquire(phandle, "rockchip,grf"); + if (sc->sc_grf != NULL && sc->sc_conf->oe_mask != 0) { + syscon_lock(sc->sc_grf); + val = __SHIFTIN(sc->sc_conf->oe_val, sc->sc_conf->oe_mask); + val |= (sc->sc_conf->oe_mask << 16); + syscon_write_4(sc->sc_grf, sc->sc_conf->oe_reg, val); + syscon_unlock(sc->sc_grf); + } + + if (rk_i2s_clock_init(sc) != 0) + return; + + aprint_naive("\n"); + aprint_normal(": I2S/PCM controller\n"); + + if (fdtbus_intr_establish(phandle, 0, IPL_AUDIO, FDT_INTR_MPSAFE, rk_i2s_intr, sc) == NULL) { + aprint_error_dev(self, "couldn't establish interrupt on %s\n", intrstr); + return; + } + aprint_normal_dev(self, "interrupting on %s\n", intrstr); + + sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD; + sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE; + sc->sc_format.validbits = 16; + sc->sc_format.precision = 16; + sc->sc_format.channels = 2; + sc->sc_format.channel_mask = AUFMT_STEREO; + sc->sc_format.frequency_type = 1; + sc->sc_format.frequency[0] = RK_I2S_SAMPLE_RATE; + + sc->sc_dai.dai_set_sysclk = rk_i2s_dai_set_sysclk; + sc->sc_dai.dai_set_format = rk_i2s_dai_set_format; + sc->sc_dai.dai_hw_if = &rk_i2s_hw_if; + sc->sc_dai.dai_dev = self; + sc->sc_dai.dai_priv = sc; + fdtbus_register_dai_controller(self, phandle, &rk_i2s_dai_funcs); +} + +CFATTACH_DECL_NEW(rk_i2s, sizeof(struct rk_i2s_softc), + rk_i2s_match, rk_i2s_attach, NULL, NULL); Index: src/sys/arch/arm/rockchip/rk_dwhdmi.c diff -u /dev/null src/sys/arch/arm/rockchip/rk_dwhdmi.c:1.3.2.2 --- /dev/null Sat Nov 16 16:48:26 2019 +++ src/sys/arch/arm/rockchip/rk_dwhdmi.c Sat Nov 16 16:48:25 2019 @@ -0,0 +1,310 @@ +/* $NetBSD: rk_dwhdmi.c,v 1.3.2.2 2019/11/16 16:48:25 martin Exp $ */ + +/*- + * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: rk_dwhdmi.c,v 1.3.2.2 2019/11/16 16:48:25 martin Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/intr.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> + +#include <drm/drmP.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/fdt/fdt_port.h> +#include <dev/fdt/syscon.h> + +#include <dev/ic/dw_hdmi.h> + +#define RK3399_GRF_SOC_CON20 0x6250 +#define HDMI_LCDC_SEL __BIT(6) + +static const struct dwhdmi_mpll_config rk_dwhdmi_mpll_config[] = { + { 40000, 0x00b3, 0x0000, 0x0018 }, + { 65000, 0x0072, 0x0001, 0x0028 }, + { 66000, 0x013e, 0x0003, 0x0038 }, + { 83500, 0x0072, 0x0001, 0x0028 }, + { 146250, 0x0051, 0x0002, 0x0038 }, + { 148500, 0x0051, 0x0003, 0x0000 }, + { 272000, 0x0040, 0x0003, 0x0000 }, + { 340000, 0x0040, 0x0003, 0x0000 }, + { 0, 0x0051, 0x0003, 0x0000 }, +}; + +static const struct dwhdmi_phy_config rk_dwhdmi_phy_config[] = { + { 74250, 0x8009, 0x0004, 0x0272 }, + { 148500, 0x802b, 0x0004, 0x028d }, + { 297000, 0x8039, 0x0005, 0x028d }, + { 594000, 0x8039, 0x0000, 0x019d }, + { 0, 0x0000, 0x0000, 0x0000 } +}; + +enum { + DWHDMI_PORT_INPUT = 0, + DWHDMI_PORT_OUTPUT = 1, +}; + +static const char * const compatible[] = { + "rockchip,rk3399-dw-hdmi", + NULL +}; + +struct rk_dwhdmi_softc { + struct dwhdmi_softc sc_base; + int sc_phandle; + struct clk *sc_clk_vpll; + + struct fdt_device_ports sc_ports; + struct drm_display_mode sc_curmode; + struct syscon *sc_grf; + + bool sc_activated; +}; + +#define to_rk_dwhdmi_softc(x) container_of(x, struct rk_dwhdmi_softc, sc_base) + +static void +rk_dwhdmi_select_input(struct rk_dwhdmi_softc *sc, u_int crtc_index) +{ + const uint32_t write_mask = HDMI_LCDC_SEL << 16; + const uint32_t write_val = crtc_index == 0 ? HDMI_LCDC_SEL : 0; + + syscon_lock(sc->sc_grf); + syscon_write_4(sc->sc_grf, RK3399_GRF_SOC_CON20, write_mask | write_val); + syscon_unlock(sc->sc_grf); +} + +static int +rk_dwhdmi_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate) +{ + struct rk_dwhdmi_softc * const sc = device_private(dev); + struct fdt_endpoint *in_ep = fdt_endpoint_remote(ep); + struct fdt_endpoint *out_ep, *out_rep; + struct drm_encoder *encoder; + struct drm_bridge *bridge; + int error; + + if (!activate) + return EINVAL; + + if (fdt_endpoint_port_index(ep) != DWHDMI_PORT_INPUT) + return EINVAL; + + switch (fdt_endpoint_type(in_ep)) { + case EP_DRM_ENCODER: + encoder = fdt_endpoint_get_data(in_ep); + break; + case EP_DRM_BRIDGE: + bridge = fdt_endpoint_get_data(in_ep); + encoder = bridge->encoder; + break; + default: + encoder = NULL; + break; + } + + if (encoder == NULL) + return EINVAL; + + if (sc->sc_activated == false) { + error = dwhdmi_bind(&sc->sc_base, encoder); + if (error != 0) + return error; + sc->sc_activated = true; + } + + out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, DWHDMI_PORT_OUTPUT, 0); + if (out_ep != NULL) { + /* Ignore downstream connectors, we have our own. */ + out_rep = fdt_endpoint_remote(out_ep); + if (out_rep != NULL && fdt_endpoint_type(out_rep) == EP_DRM_CONNECTOR) + return 0; + + error = fdt_endpoint_activate(out_ep, activate); + if (error != 0) + return error; + } + + return 0; +} + +static void * +rk_dwhdmi_ep_get_data(device_t dev, struct fdt_endpoint *ep) +{ + struct rk_dwhdmi_softc * const sc = device_private(dev); + + return &sc->sc_base.sc_bridge; +} + +static void +rk_dwhdmi_enable(struct dwhdmi_softc *dsc) +{ + struct rk_dwhdmi_softc * const sc = to_rk_dwhdmi_softc(dsc); + + const u_int crtc_index = drm_crtc_index(dsc->sc_bridge.encoder->crtc); + + rk_dwhdmi_select_input(sc, crtc_index); + + dwhdmi_phy_enable(dsc); +} + +static void +rk_dwhdmi_mode_set(struct dwhdmi_softc *dsc, + struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) +{ + struct rk_dwhdmi_softc * const sc = to_rk_dwhdmi_softc(dsc); + int error; + + if (sc->sc_clk_vpll != NULL) { + error = clk_set_rate(sc->sc_clk_vpll, adjusted_mode->clock * 1000); + if (error != 0) + device_printf(dsc->sc_dev, "couldn't set pixel clock to %u Hz: %d\n", + adjusted_mode->clock * 1000, error); + } + + dwhdmi_phy_mode_set(dsc, mode, adjusted_mode); +} + +static audio_dai_tag_t +rk_dwhdmi_dai_get_tag(device_t dev, const void *data, size_t len) +{ + struct rk_dwhdmi_softc * const sc = device_private(dev); + + if (len != 4) + return NULL; + + return &sc->sc_base.sc_dai; +} + +static struct fdtbus_dai_controller_func rk_dwhdmi_dai_funcs = { + .get_tag = rk_dwhdmi_dai_get_tag +}; + +static int +rk_dwhdmi_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +rk_dwhdmi_attach(device_t parent, device_t self, void *aux) +{ + struct rk_dwhdmi_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + bus_addr_t addr; + bus_size_t size; + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + + /* Required */ + if (fdtbus_clock_enable(phandle, "iahb", true) != 0) { + aprint_error(": couldn't enable iahb clock\n"); + return; + } + + /* Required */ + if (fdtbus_clock_enable(phandle, "isfr", true) != 0) { + aprint_error(": couldn't enable isfr clock\n"); + return; + } + + /* Optional */ + sc->sc_clk_vpll = fdtbus_clock_get(phandle, "vpll"); + if (sc->sc_clk_vpll != NULL && clk_enable(sc->sc_clk_vpll) != 0) { + aprint_error(": couldn't enable vpll clock\n"); + return; + } + + /* Optional */ + if (fdtbus_clock_enable(phandle, "grf", false) != 0) { + aprint_error(": couldn't enable grf clock\n"); + return; + } + + /* Optional */ + if (fdtbus_clock_enable(phandle, "cec", false) != 0) { + aprint_error(": couldn't enable cec clock\n"); + return; + } + + sc->sc_base.sc_dev = self; + if (of_getprop_uint32(phandle, "reg-io-width", &sc->sc_base.sc_reg_width) != 0) + sc->sc_base.sc_reg_width = 4; + sc->sc_base.sc_bst = faa->faa_bst; + if (bus_space_map(sc->sc_base.sc_bst, addr, size, 0, &sc->sc_base.sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + sc->sc_phandle = faa->faa_phandle; + sc->sc_grf = fdtbus_syscon_acquire(phandle, "rockchip,grf"); + if (sc->sc_grf == NULL) { + aprint_error(": couldn't get grf syscon\n"); + return; + } + + aprint_naive("\n"); + aprint_normal(": HDMI TX\n"); + + sc->sc_base.sc_ic = fdtbus_i2c_acquire(phandle, "ddc-i2c-bus"); + if (of_hasprop(phandle, "ddc-i2c-bus") && sc->sc_base.sc_ic == NULL) { + aprint_error_dev(self, "couldn't find external I2C master\n"); + return; + } + + sc->sc_base.sc_flags |= DWHDMI_USE_INTERNAL_PHY; + sc->sc_base.sc_detect = dwhdmi_phy_detect; + sc->sc_base.sc_enable = rk_dwhdmi_enable; + sc->sc_base.sc_disable = dwhdmi_phy_disable; + sc->sc_base.sc_mode_set = rk_dwhdmi_mode_set; + sc->sc_base.sc_mpll_config = rk_dwhdmi_mpll_config; + sc->sc_base.sc_phy_config = rk_dwhdmi_phy_config; + + if (dwhdmi_attach(&sc->sc_base) != 0) { + aprint_error_dev(self, "failed to attach driver\n"); + return; + } + + sc->sc_ports.dp_ep_activate = rk_dwhdmi_ep_activate; + sc->sc_ports.dp_ep_get_data = rk_dwhdmi_ep_get_data; + fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_BRIDGE); + + fdtbus_register_dai_controller(self, phandle, &rk_dwhdmi_dai_funcs); +} + +CFATTACH_DECL_NEW(rk_dwhdmi, sizeof(struct rk_dwhdmi_softc), + rk_dwhdmi_match, rk_dwhdmi_attach, NULL, NULL); Index: src/sys/dev/ic/dw_hdmi_phy.c diff -u /dev/null src/sys/dev/ic/dw_hdmi_phy.c:1.2.2.2 --- /dev/null Sat Nov 16 16:48:26 2019 +++ src/sys/dev/ic/dw_hdmi_phy.c Sat Nov 16 16:48:25 2019 @@ -0,0 +1,401 @@ +/* $NetBSD: dw_hdmi_phy.c,v 1.2.2.2 2019/11/16 16:48:25 martin Exp $ */ + +/*- + * Copyright (c) 2015 Oleksandr Tymoshenko <go...@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: dw_hdmi_phy.c,v 1.2.2.2 2019/11/16 16:48:25 martin Exp $"); + +#include <sys/param.h> + +#include <drm/drmP.h> + +#include <dev/ic/dw_hdmi.h> + +#define HDMI_IH_PHY_STAT0 0x0104 +#define HDMI_IH_PHY_STAT0_HPD (1 << 0) +#define HDMI_IH_I2CMPHY_STAT0 0x0108 +#define HDMI_IH_I2CMPHY_STAT0_DONE (1 << 1) +#define HDMI_IH_I2CMPHY_STAT0_ERROR (1 << 0) + +#define HDMI_PHY_CONF0 0x3000 +#define HDMI_PHY_CONF0_PDZ_MASK 0x80 +#define HDMI_PHY_CONF0_PDZ_OFFSET 7 +#define HDMI_PHY_CONF0_ENTMDS_MASK 0x40 +#define HDMI_PHY_CONF0_ENTMDS_OFFSET 6 +#define HDMI_PHY_CONF0_SVSRET_MASK 0x20 +#define HDMI_PHY_CONF0_SVSRET_OFFSET 5 +#define HDMI_PHY_CONF0_GEN2_PDDQ_MASK 0x10 +#define HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET 4 +#define HDMI_PHY_CONF0_GEN2_TXPWRON_MASK 0x8 +#define HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET 3 +#define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK 0x4 +#define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET 2 +#define HDMI_PHY_CONF0_SELDATAENPOL_MASK 0x2 +#define HDMI_PHY_CONF0_SELDATAENPOL_OFFSET 1 +#define HDMI_PHY_CONF0_SELDIPIF_MASK 0x1 +#define HDMI_PHY_CONF0_SELDIPIF_OFFSET 0 +#define HDMI_PHY_TST0 0x3001 +#define HDMI_PHY_TST0_TSTCLR_MASK 0x20 +#define HDMI_PHY_TST0_TSTCLR_OFFSET 5 +#define HDMI_PHY_TST0_TSTEN_MASK 0x10 +#define HDMI_PHY_TST0_TSTEN_OFFSET 4 +#define HDMI_PHY_TST0_TSTCLK_MASK 0x1 +#define HDMI_PHY_TST0_TSTCLK_OFFSET 0 +#define HDMI_PHY_TST1 0x3002 +#define HDMI_PHY_TST2 0x3003 +#define HDMI_PHY_STAT0 0x3004 +#define HDMI_PHY_STAT0_RX_SENSE3 0x80 +#define HDMI_PHY_STAT0_RX_SENSE2 0x40 +#define HDMI_PHY_STAT0_RX_SENSE1 0x20 +#define HDMI_PHY_STAT0_RX_SENSE0 0x10 +#define HDMI_PHY_STAT0_RX_SENSE 0xf0 +#define HDMI_PHY_STAT0_HPD 0x02 +#define HDMI_PHY_TX_PHY_LOCK 0x01 +#define HDMI_PHY_INT0 0x3005 +#define HDMI_PHY_MASK0 0x3006 +#define HDMI_PHY_POL0 0x3007 +#define HDMI_PHY_POL0_HPD 0x02 + +/* HDMI Master PHY Registers */ +#define HDMI_PHY_I2CM_SLAVE_ADDR 0x3020 +#define HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 0x69 +#define HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY 0x49 +#define HDMI_PHY_I2CM_ADDRESS_ADDR 0x3021 +#define HDMI_PHY_I2CM_DATAO_1_ADDR 0x3022 +#define HDMI_PHY_I2CM_DATAO_0_ADDR 0x3023 +#define HDMI_PHY_I2CM_DATAI_1_ADDR 0x3024 +#define HDMI_PHY_I2CM_DATAI_0_ADDR 0x3025 +#define HDMI_PHY_I2CM_OPERATION_ADDR 0x3026 +#define HDMI_PHY_I2CM_OPERATION_ADDR_WRITE 0x10 +#define HDMI_PHY_I2CM_OPERATION_ADDR_READ 0x1 +#define HDMI_PHY_I2CM_INT_ADDR 0x3027 +#define HDMI_PHY_I2CM_CTLINT_ADDR 0x3028 +#define HDMI_PHY_I2CM_DIV_ADDR 0x3029 +#define HDMI_PHY_I2CM_SOFTRSTZ_ADDR 0x302a +#define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR 0x302b +#define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR 0x302c +#define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR 0x302d +#define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR 0x302e +#define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR 0x302f +#define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR 0x3030 +#define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR 0x3031 +#define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR 0x3032 + +#define HDMI_MC_FLOWCTRL 0x4004 +#define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK 0x1 +#define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH 0x1 +#define HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS 0x0 +#define HDMI_MC_PHYRSTZ 0x4005 +#define HDMI_MC_PHYRSTZ_ASSERT 0x0 +#define HDMI_MC_PHYRSTZ_DEASSERT 0x1 +#define HDMI_MC_HEACPHY_RST 0x4007 +#define HDMI_MC_HEACPHY_RST_ASSERT 0x1 +#define HDMI_MC_HEACPHY_RST_DEASSERT 0x0 + +/* HDMI PHY register with access through I2C */ +#define HDMI_PHY_I2C_CKCALCTRL 0x5 +#define CKCALCTRL_OVERRIDE (1 << 15) +#define HDMI_PHY_I2C_CPCE_CTRL 0x6 +#define CPCE_CTRL_45_25 ((3 << 7) | (3 << 5)) +#define CPCE_CTRL_92_50 ((2 << 7) | (2 << 5)) +#define CPCE_CTRL_185 ((1 << 7) | (1 << 5)) +#define CPCE_CTRL_370 ((0 << 7) | (0 << 5)) +#define HDMI_PHY_I2C_CKSYMTXCTRL 0x9 +#define CKSYMTXCTRL_OVERRIDE (1 << 15) +#define CKSYMTXCTRL_TX_SYMON (1 << 3) +#define CKSYMTXCTRL_TX_TRAON (1 << 2) +#define CKSYMTXCTRL_TX_TRBON (1 << 1) +#define CKSYMTXCTRL_TX_CK_SYMON (1 << 0) +#define HDMI_PHY_I2C_VLEVCTRL 0x0E +#define HDMI_PHY_I2C_CURRCTRL 0x10 +#define HDMI_PHY_I2C_PLLPHBYCTRL 0x13 +#define VLEVCTRL_TX_LVL(x) ((x) << 5) +#define VLEVCTRL_CK_LVL(x) (x) +#define HDMI_PHY_I2C_GMPCTRL 0x15 +#define GMPCTRL_45_25 0x00 +#define GMPCTRL_92_50 0x05 +#define GMPCTRL_185 0x0a +#define GMPCTRL_370 0x0f +#define HDMI_PHY_I2C_MSM_CTRL 0x17 +#define MSM_CTRL_FB_CLK (0x3 << 1) +#define HDMI_PHY_I2C_TXTERM 0x19 +#define TXTERM_133 0x5 + +static void +dwhdmi_phy_wait_i2c_done(struct dwhdmi_softc *sc, int msec) +{ + uint8_t val; + + val = dwhdmi_read(sc, HDMI_IH_I2CMPHY_STAT0) & + (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR); + while (val == 0) { + delay(1000); + msec -= 10; + if (msec <= 0) + return; + val = dwhdmi_read(sc, HDMI_IH_I2CMPHY_STAT0) & + (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR); + } +} + +static void +dwhdmi_phy_i2c_write(struct dwhdmi_softc *sc, unsigned short data, + unsigned char addr) +{ + + /* clear DONE and ERROR flags */ + dwhdmi_write(sc, HDMI_IH_I2CMPHY_STAT0, + HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR); + dwhdmi_write(sc, HDMI_PHY_I2CM_ADDRESS_ADDR, addr); + dwhdmi_write(sc, HDMI_PHY_I2CM_DATAO_1_ADDR, ((data >> 8) & 0xff)); + dwhdmi_write(sc, HDMI_PHY_I2CM_DATAO_0_ADDR, ((data >> 0) & 0xff)); + dwhdmi_write(sc, HDMI_PHY_I2CM_OPERATION_ADDR, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE); + dwhdmi_phy_wait_i2c_done(sc, 1000); +} + +static void +dwhdmi_phy_enable_power(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_PDZ_MASK; + reg |= (enable << HDMI_PHY_CONF0_PDZ_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_enable_tmds(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_ENTMDS_MASK; + reg |= (enable << HDMI_PHY_CONF0_ENTMDS_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_gen2_pddq(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK; + reg |= (enable << HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_gen2_txpwron(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK; + reg |= (enable << HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_sel_data_en_pol(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_SELDATAENPOL_MASK; + reg |= (enable << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_sel_interface_control(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_SELDIPIF_MASK; + reg |= (enable << HDMI_PHY_CONF0_SELDIPIF_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static void +dwhdmi_phy_enable_svsret(struct dwhdmi_softc *sc, uint8_t enable) +{ + uint8_t reg; + + reg = dwhdmi_read(sc, HDMI_PHY_CONF0); + reg &= ~HDMI_PHY_CONF0_SVSRET_MASK; + reg |= (enable << HDMI_PHY_CONF0_SVSRET_OFFSET); + dwhdmi_write(sc, HDMI_PHY_CONF0, reg); +} + +static inline void +dwhdmi_phy_test_clear(struct dwhdmi_softc *sc, unsigned char bit) +{ + uint8_t val; + + val = dwhdmi_read(sc, HDMI_PHY_TST0); + val &= ~HDMI_PHY_TST0_TSTCLR_MASK; + val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) & + HDMI_PHY_TST0_TSTCLR_MASK; + dwhdmi_write(sc, HDMI_PHY_TST0, val); +} + +static int +dwhdmi_phy_configure(struct dwhdmi_softc *sc, struct drm_display_mode *mode) +{ + const struct dwhdmi_mpll_config *mpll_conf; + const struct dwhdmi_phy_config *phy_conf; + uint8_t val; + uint8_t msec; + + dwhdmi_write(sc, HDMI_MC_FLOWCTRL, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS); + + /* gen2 tx power off */ + dwhdmi_phy_gen2_txpwron(sc, 0); + + /* gen2 pddq */ + dwhdmi_phy_gen2_pddq(sc, 1); + + /* PHY reset */ + dwhdmi_write(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_DEASSERT); + dwhdmi_write(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_ASSERT); + + dwhdmi_write(sc, HDMI_MC_HEACPHY_RST, HDMI_MC_HEACPHY_RST_ASSERT); + + dwhdmi_phy_test_clear(sc, 1); + dwhdmi_write(sc, HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2); + dwhdmi_phy_test_clear(sc, 0); + + /* + * Following initialization are for 8bit per color case + */ + + /* + * PLL/MPLL config + */ + for (mpll_conf = &sc->sc_mpll_config[0]; mpll_conf->pixel_clock != 0; mpll_conf++) + if (mode->clock <= mpll_conf->pixel_clock) + break; + + dwhdmi_phy_i2c_write(sc, mpll_conf->cpce, HDMI_PHY_I2C_CPCE_CTRL); + dwhdmi_phy_i2c_write(sc, mpll_conf->gmp, HDMI_PHY_I2C_GMPCTRL); + dwhdmi_phy_i2c_write(sc, mpll_conf->curr, HDMI_PHY_I2C_CURRCTRL); + + for (phy_conf = &sc->sc_phy_config[0]; phy_conf->pixel_clock != 0; phy_conf++) + if (mode->clock <= phy_conf->pixel_clock) + break; + + dwhdmi_phy_i2c_write(sc, 0x0000, HDMI_PHY_I2C_PLLPHBYCTRL); + dwhdmi_phy_i2c_write(sc, MSM_CTRL_FB_CLK, HDMI_PHY_I2C_MSM_CTRL); + + dwhdmi_phy_i2c_write(sc, phy_conf->term, HDMI_PHY_I2C_TXTERM); + dwhdmi_phy_i2c_write(sc, phy_conf->sym, HDMI_PHY_I2C_CKSYMTXCTRL); + dwhdmi_phy_i2c_write(sc, phy_conf->vlev, HDMI_PHY_I2C_VLEVCTRL); + + /* REMOVE CLK TERM */ + dwhdmi_phy_i2c_write(sc, CKCALCTRL_OVERRIDE, HDMI_PHY_I2C_CKCALCTRL); + + dwhdmi_phy_enable_power(sc, 1); + + /* toggle TMDS enable */ + dwhdmi_phy_enable_tmds(sc, 0); + dwhdmi_phy_enable_tmds(sc, 1); + + /* gen2 tx power on */ + dwhdmi_phy_gen2_txpwron(sc, 1); + dwhdmi_phy_gen2_pddq(sc, 0); + + switch (sc->sc_phytype) { + case 0xb2: /* MHL PHY HEAC */ + case 0xc2: /* MHL PHY */ + case 0xf3: /* HDMI 2.0 TX PHY */ + dwhdmi_phy_enable_svsret(sc, 1); + break; + } + + /*Wait for PHY PLL lock */ + msec = 4; + val = dwhdmi_read(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; + while (val == 0) { + delay(1000); + if (msec-- == 0) { + device_printf(sc->sc_dev, "PHY PLL not locked\n"); + return (-1); + } + val = dwhdmi_read(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; + } + + return (0); +} + +static void +dwhdmi_phy_init(struct dwhdmi_softc *sc, struct drm_display_mode *mode) +{ + int i; + + /* HDMI Phy spec says to do the phy initialization sequence twice */ + for (i = 0 ; i < 2 ; i++) { + dwhdmi_phy_sel_data_en_pol(sc, 1); + dwhdmi_phy_sel_interface_control(sc, 0); + dwhdmi_phy_enable_tmds(sc, 0); + dwhdmi_phy_enable_power(sc, 0); + + /* Enable CSC */ + dwhdmi_phy_configure(sc, mode); + } +} + +enum drm_connector_status +dwhdmi_phy_detect(struct dwhdmi_softc *sc, bool force) +{ + uint8_t val; + + val = dwhdmi_read(sc, HDMI_PHY_STAT0); + + return ((val & HDMI_PHY_STAT0_HPD) != 0) ? + connector_status_connected : + connector_status_disconnected; +} + +void +dwhdmi_phy_enable(struct dwhdmi_softc *sc) +{ +} + +void +dwhdmi_phy_disable(struct dwhdmi_softc *sc) +{ +} + +void +dwhdmi_phy_mode_set(struct dwhdmi_softc *sc, + struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) +{ + dwhdmi_phy_init(sc, adjusted_mode); +}