Module Name: src Committed By: bouyer Date: Sun Apr 1 21:19:18 UTC 2018
Modified Files: src/sys/arch/arm/sunxi: files.sunxi sun4i_a10_ccu.c sunxi_ccu.c sunxi_ccu.h sunxi_ccu_fractional.c Added Files: src/sys/arch/arm/sunxi: sunxi_ccu_display.c Log Message: Add a round_rate() callback for the sunxi clock domain. Add a sunxi_ccu_display.c file with helpers for setting up display engine clocks. for fractional clocks, rename frac_en to div_en, I got the logic inverted. Adjust tcon0-ch0, tcon0-ch1, tcon1-ch0 and tcon1-ch1 definitions to automatically select a parent. tcon0 hardcoded to pll3 and tcon1 to pll7. Define a round_rate() callback for these clocks, as well as fractional clocks. Hardcode debe clocks parent to pll5. To generate a diff of this commit: cvs rdiff -u -r1.46 -r1.47 src/sys/arch/arm/sunxi/files.sunxi cvs rdiff -u -r1.7 -r1.8 src/sys/arch/arm/sunxi/sun4i_a10_ccu.c cvs rdiff -u -r1.8 -r1.9 src/sys/arch/arm/sunxi/sunxi_ccu.c cvs rdiff -u -r1.16 -r1.17 src/sys/arch/arm/sunxi/sunxi_ccu.h cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/sunxi/sunxi_ccu_display.c cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/sunxi/sunxi_ccu_fractional.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/sunxi/files.sunxi diff -u src/sys/arch/arm/sunxi/files.sunxi:1.46 src/sys/arch/arm/sunxi/files.sunxi:1.47 --- src/sys/arch/arm/sunxi/files.sunxi:1.46 Sun Apr 1 04:35:04 2018 +++ src/sys/arch/arm/sunxi/files.sunxi Sun Apr 1 21:19:17 2018 @@ -1,4 +1,4 @@ -# $NetBSD: files.sunxi,v 1.46 2018/04/01 04:35:04 ryo Exp $ +# $NetBSD: files.sunxi,v 1.47 2018/04/01 21:19:17 bouyer Exp $ # # Configuration info for Allwinner sunxi family SoCs # @@ -17,6 +17,7 @@ file arch/arm/sunxi/sunxi_ccu_nm.c sunx file arch/arm/sunxi/sunxi_ccu_nkmp.c sunxi_ccu file arch/arm/sunxi/sunxi_ccu_phase.c sunxi_ccu file arch/arm/sunxi/sunxi_ccu_prediv.c sunxi_ccu +file arch/arm/sunxi/sunxi_ccu_display.c sunxi_ccu # CCU (A10/A20) device sun4ia10ccu: sunxi_ccu Index: src/sys/arch/arm/sunxi/sun4i_a10_ccu.c diff -u src/sys/arch/arm/sunxi/sun4i_a10_ccu.c:1.7 src/sys/arch/arm/sunxi/sun4i_a10_ccu.c:1.8 --- src/sys/arch/arm/sunxi/sun4i_a10_ccu.c:1.7 Mon Mar 19 16:18:30 2018 +++ src/sys/arch/arm/sunxi/sun4i_a10_ccu.c Sun Apr 1 21:19:17 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sun4i_a10_ccu.c,v 1.7 2018/03/19 16:18:30 bouyer Exp $ */ +/* $NetBSD: sun4i_a10_ccu.c,v 1.8 2018/04/01 21:19:17 bouyer Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -28,7 +28,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: sun4i_a10_ccu.c,v 1.7 2018/03/19 16:18:30 bouyer Exp $"); +__KERNEL_RCSID(1, "$NetBSD: sun4i_a10_ccu.c,v 1.8 2018/04/01 21:19:17 bouyer Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -116,10 +116,7 @@ static const char *apb1_parents[] = { "o static const char *mod_parents[] = { "osc24m", "pll_periph", "pll_ddr_other" }; static const char *sata_parents[] = { "pll6_periph_sata", "external" }; static const char *de_parents[] = { "pll_video0", "pll_video1", "pll_ddr_other" }; -static const char *lcd0_parents[] = { "pll_video0", "pll_video1", "pll_video0x2" }; -static const char *lcd1_parents[] = { "pll_video0", "pll_video1", "pll_video0x2", "pll_video1x2" }; -static const char *lcd0ch1c2[] = { "tcon0-ch1-clk2" }; -static const char *lcd1ch1c2[] = { "tcon1-ch1-clk2" }; +static const char *lcd_parents[] = { "pll_video0", "pll_video1", "pll_video0x2", "pll_video1x2" }; static const struct sunxi_ccu_nkmp_tbl sun4i_a10_pll1_table[] = { { 1008000000, 21, 1, 0, 0 }, @@ -139,6 +136,25 @@ static const struct sunxi_ccu_nkmp_tbl s { 0 } }; +/* + * some special cases + * hardcode lcd0 (tcon0) to pll3 and lcd1 (tcon1) to pll7. + * compute pll rate based on desired pixel clock + */ + +static int sun4i_a10_ccu_lcd0ch0_set_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, u_int); +static int sun4i_a10_ccu_lcd1ch0_set_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, u_int); +static u_int sun4i_a10_ccu_lcd0ch0_round_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, u_int); +static u_int sun4i_a10_ccu_lcd1ch0_round_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, u_int); +static int sun4i_a10_ccu_lcd0ch1_set_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, u_int); +static int sun4i_a10_ccu_lcd1ch1_set_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, u_int); + static struct sunxi_ccu_clk sun4i_a10_ccu_clks[] = { SUNXI_CCU_GATE(A10_CLK_HOSC, "osc24m", "hosc", OSC24M_CFG_REG, 0), @@ -302,7 +318,7 @@ static struct sunxi_ccu_clk sun4i_a10_cc __BITS(7,0), /* m */ 9, /* m_min */ 127, /* m_max */ - __BIT(15), /* frac_en */ + __BIT(15), /* div_en */ __BIT(14), /* frac_sel */ 270000000, 297000000, /* frac values */ 8, /* prediv */ @@ -313,7 +329,7 @@ static struct sunxi_ccu_clk sun4i_a10_cc __BITS(7,0), /* m */ 9, /* m_min */ 127, /* m_max */ - __BIT(15), /* frac_en */ + __BIT(15), /* div_en */ __BIT(14), /* frac_sel */ 270000000, 297000000, /* frac values */ 8, /* prediv */ @@ -354,49 +370,73 @@ static struct sunxi_ccu_clk sun4i_a10_cc __BIT(31), /* enable */ 0 /* flags */ ), - SUNXI_CCU_DIV_GATE(A10_CLK_TCON0_CH0, "tcon0-ch0", lcd0_parents, - LCD0CH0_CFG_REG, /* reg */ - 0, /* div */ - __BITS(25,24), /* sel */ - __BIT(31), /* enable */ - SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ - ), - SUNXI_CCU_DIV_GATE(A10_CLK_TCON1_CH0, "tcon1-ch0", lcd1_parents, - LCD1CH0_CFG_REG, /* reg */ - 0, /* div */ - __BITS(25,24), /* sel */ - __BIT(31), /* enable */ - SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ - ), - SUNXI_CCU_DIV_GATE(A10_CLK_TCON0_CH1_SCLK2, "tcon0-ch1-clk2", lcd1_parents, - LCD0CH1_CFG_REG, /* reg */ - __BITS(3,0), /* div */ - __BITS(25,24), /* sel */ - __BIT(31), /* enable */ - SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ - ), - SUNXI_CCU_DIV_GATE(A10_CLK_TCON0_CH1, "tcon0-ch1", lcd0ch1c2, - LCD0CH1_CFG_REG, /* reg */ - __BIT(11), /* div */ - 0, /* sel */ - __BIT(15), /* enable */ - SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ - ), - SUNXI_CCU_DIV_GATE(A10_CLK_TCON1_CH1_SCLK2, "tcon1-ch1-clk2", lcd1_parents, - LCD1CH1_CFG_REG, /* reg */ - __BITS(3,0), /* div */ - __BITS(25,24), /* sel */ - __BIT(31), /* enable */ - SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ - ), - SUNXI_CCU_DIV_GATE(A10_CLK_TCON1_CH1, "tcon1-ch1", lcd1ch1c2, - LCD1CH1_CFG_REG, /* reg */ - __BIT(11), /* div */ - 0, /* sel */ - __BIT(15), /* enable */ - SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ - ), - SUNXI_CCU_DIV_GATE(A10_CLK_HDMI, "hdmi-mod", lcd1_parents, + [A10_CLK_TCON0_CH0] = { + .type = SUNXI_CCU_DIV, + .base.name = "tcon0-ch0", + .u.div.reg = LCD0CH0_CFG_REG, + .u.div.parents = lcd_parents, + .u.div.nparents = __arraycount(lcd_parents), + .u.div.div = 0, + .u.div.sel = __BITS(25,24), + .u.div.enable = __BIT(31), + .u.div.flags = 0, + .enable = sunxi_ccu_div_enable, + .get_rate = sunxi_ccu_div_get_rate, + .set_rate = sun4i_a10_ccu_lcd0ch0_set_rate, + .round_rate = sun4i_a10_ccu_lcd0ch0_round_rate, + .set_parent = sunxi_ccu_div_set_parent, + .get_parent = sunxi_ccu_div_get_parent, + }, + [A10_CLK_TCON1_CH0] = { + .type = SUNXI_CCU_DIV, + .base.name = "tcon1-ch0", + .u.div.reg = LCD1CH0_CFG_REG, + .u.div.parents = lcd_parents, + .u.div.nparents = __arraycount(lcd_parents), + .u.div.div = 0, + .u.div.sel = __BITS(25,24), + .u.div.enable = __BIT(31), + .u.div.flags = 0, + .enable = sunxi_ccu_div_enable, + .get_rate = sunxi_ccu_div_get_rate, + .set_rate = sun4i_a10_ccu_lcd1ch0_set_rate, + .round_rate = sun4i_a10_ccu_lcd1ch0_round_rate, + .set_parent = sunxi_ccu_div_set_parent, + .get_parent = sunxi_ccu_div_get_parent, + }, + [A10_CLK_TCON0_CH1] = { + .type = SUNXI_CCU_DIV, + .base.name = "tcon0-ch1", + .u.div.reg = LCD0CH1_CFG_REG, + .u.div.parents = lcd_parents, + .u.div.nparents = __arraycount(lcd_parents), + .u.div.div = __BITS(3,0), + .u.div.sel = __BITS(25,24), + .u.div.enable = __BIT(15) | __BIT(31), + .u.div.flags = 0, + .enable = sunxi_ccu_div_enable, + .get_rate = sunxi_ccu_div_get_rate, + .set_rate = sun4i_a10_ccu_lcd0ch1_set_rate, + .set_parent = sunxi_ccu_div_set_parent, + .get_parent = sunxi_ccu_div_get_parent, + }, + [A10_CLK_TCON1_CH1] = { + .type = SUNXI_CCU_DIV, + .base.name = "tcon1-ch1", + .u.div.reg = LCD1CH1_CFG_REG, + .u.div.parents = lcd_parents, + .u.div.nparents = __arraycount(lcd_parents), + .u.div.div = __BITS(3,0), + .u.div.sel = __BITS(25,24), + .u.div.enable = __BIT(15) | __BIT(31), + .u.div.flags = 0, + .enable = sunxi_ccu_div_enable, + .get_rate = sunxi_ccu_div_get_rate, + .set_rate = sun4i_a10_ccu_lcd1ch1_set_rate, + .set_parent = sunxi_ccu_div_set_parent, + .get_parent = sunxi_ccu_div_get_parent, + }, + SUNXI_CCU_DIV_GATE(A10_CLK_HDMI, "hdmi-mod", lcd_parents, HDMI_CLOCK_CFG_REG, /* reg */ __BITS(3,0), /* div */ __BITS(25,24), /* sel */ @@ -571,6 +611,177 @@ static struct sunxi_ccu_clk sun4i_a10_cc USBPHY_CFG_REG, 8), }; +/* + * some special cases + * hardcode lcd0 (tcon0) to pll3 and lcd1 (tcon1) to pll7. + * compute pll rate based on desired pixel clock + */ + +static int +sun4i_a10_ccu_lcd0ch0_set_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk * clk, u_int rate) +{ + int error; + error = sunxi_ccu_lcdxch0_set_rate(sc, clk, + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO0], + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO0_2X], + rate); + return error; +} + +static int +sun4i_a10_ccu_lcd1ch0_set_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk * clk, u_int rate) +{ + return sunxi_ccu_lcdxch0_set_rate(sc, clk, + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO1], + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO1_2X], + rate); +} + +static u_int +sun4i_a10_ccu_lcd0ch0_round_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk * clk, u_int rate) +{ + return sunxi_ccu_lcdxch0_round_rate(sc, clk, + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO0], + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO0_2X], + rate); +} + +static u_int +sun4i_a10_ccu_lcd1ch0_round_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk * clk, u_int rate) +{ + return sunxi_ccu_lcdxch0_round_rate(sc, clk, + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO1], + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO1_2X], + rate); +} + +static int +sun4i_a10_ccu_lcd0ch1_set_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk * clk, u_int rate) +{ + return sunxi_ccu_lcdxch1_set_rate(sc, clk, + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO0], + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO0_2X], + rate); +} + +static int +sun4i_a10_ccu_lcd1ch1_set_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk * clk, u_int rate) +{ + return sunxi_ccu_lcdxch1_set_rate(sc, clk, + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO1], + &sun4i_a10_ccu_clks[A10_CLK_PLL_VIDEO1_2X], + rate); +} + +#if 0 +static int +sun4i_a10_ccu_lcdxch0_set_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk * clk, u_int rate, int unit) +{ + int parent_index; + struct clk *clkp; + int error; + + parent_index = (unit == 0) ? A10_CLK_PLL_VIDEO0 : A10_CLK_PLL_VIDEO1; + clkp = &sun4i_a10_ccu_clks[parent_index].base; + error = clk_set_rate(clkp, rate); + if (error) { + error = clk_set_rate(clkp, rate / 2); + if (error != 0) + return error; + parent_index = + (unit == 0) ? A10_CLK_PLL_VIDEO0_2X : A10_CLK_PLL_VIDEO1_2X; + clkp = &sun4i_a10_ccu_clks[parent_index].base; + } + error = clk_set_parent(&clk->base, clkp); + KASSERT(error == 0); + return error; +} + +static u_int +sun4i_a10_ccu_lcdxch0_round_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk * clk, u_int try_rate, int unit) +{ + int parent_index; + struct clk *clkp; + int diff, diff_x2; + int rate, rate_x2; + + parent_index = (unit == 0) ? A10_CLK_PLL_VIDEO0 : A10_CLK_PLL_VIDEO1; + clkp = &sun4i_a10_ccu_clks[parent_index].base; + rate = clk_round_rate(clkp, try_rate); + diff = abs(try_rate - rate); + + rate_x2 = (clk_round_rate(clkp, try_rate / 2) * 2); + diff_x2 = abs(try_rate - rate_x2); + + if (diff_x2 < diff) + return rate_x2; + return rate; +} + +static void +sun4i_a10_tcon_calc_pll(int f_ref, int f_out, int *pm, int *pn, int *pd) +{ + int best = INT_MAX; + for (int d = 1; d <= 2 && best != 0; d++) { + for (int m = 1; m <= 16 && best != 0; m++) { + for (int n = 9; n <= 127 && best != 0; n++) { + int f_cur = (n * f_ref * d) / m; + int diff = abs(f_out - f_cur); + if (diff < best) { + best = diff; + *pm = m; + *pn = n; + *pd = d; + if (diff == 0) + return; + } + } + } + } +} + +static int +sun4i_a10_ccu_lcdxch1_set_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk *clk, u_int rate, int unit) +{ + int parent_index; + struct clk *clkp, *pllclk; + int error; + int n = 0, m = 0, d = 0; + + parent_index = (unit == 0) ? A10_CLK_PLL_VIDEO0 : A10_CLK_PLL_VIDEO1; + clkp = &sun4i_a10_ccu_clks[parent_index].base; + pllclk = clkp; + + sun4i_a10_tcon_calc_pll(3000000, rate, &m, &n, &d); + + if (n == 0 || m == 0 || d == 0) + return ERANGE; + + if (d == 2) { + parent_index = + (unit == 0) ? A10_CLK_PLL_VIDEO0_2X : A10_CLK_PLL_VIDEO1_2X; + clkp = &sun4i_a10_ccu_clks[parent_index].base; + } + + error = clk_set_rate(pllclk, 3000000 * n); + KASSERT(error == 0); + error = clk_set_parent(&clk->base, clkp); + KASSERT(error == 0); + error = sunxi_ccu_div_set_rate(sc, clk, rate); + KASSERT(error == 0); + return error; +} +#endif + static int sun4i_a10_ccu_match(device_t parent, cfdata_t cf, void *aux) { @@ -579,12 +790,15 @@ sun4i_a10_ccu_match(device_t parent, cfd return of_match_compat_data(faa->faa_phandle, compat_data); } +static struct sunxi_ccu_softc *sc0; static void sun4i_a10_ccu_attach(device_t parent, device_t self, void *aux) { struct sunxi_ccu_softc * const sc = device_private(self); struct fdt_attach_args * const faa = aux; enum sun4i_a10_ccu_type type; + struct clk *clk, *clkp; + int error; sc->sc_dev = self; sc->sc_phandle = faa->faa_phandle; @@ -611,6 +825,23 @@ sun4i_a10_ccu_attach(device_t parent, de aprint_normal(": A20 CCU\n"); break; } + /* hardcode debe clocks parent to PLL5 */ + clkp = &sun4i_a10_ccu_clks[A10_CLK_PLL_DDR_BASE].base; + clk = &sun4i_a10_ccu_clks[A10_CLK_DE_BE0].base; + error = clk_set_parent(clk, clkp); + KASSERT(error == 0); + clk = &sun4i_a10_ccu_clks[A10_CLK_DE_BE1].base; + error = clk_set_parent(clk, clkp); + KASSERT(error == 0); + (void)error; sunxi_ccu_print(sc); + sc0 = sc; +} + +void sun4i_ccu_print(void); +void +sun4i_ccu_print(void) +{ + sunxi_ccu_print(sc0); } Index: src/sys/arch/arm/sunxi/sunxi_ccu.c diff -u src/sys/arch/arm/sunxi/sunxi_ccu.c:1.8 src/sys/arch/arm/sunxi/sunxi_ccu.c:1.9 --- src/sys/arch/arm/sunxi/sunxi_ccu.c:1.8 Mon Mar 19 16:18:30 2018 +++ src/sys/arch/arm/sunxi/sunxi_ccu.c Sun Apr 1 21:19:17 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_ccu.c,v 1.8 2018/03/19 16:18:30 bouyer Exp $ */ +/* $NetBSD: sunxi_ccu.c,v 1.9 2018/04/01 21:19:17 bouyer Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -31,7 +31,7 @@ #include "opt_fdt_arm.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu.c,v 1.8 2018/03/19 16:18:30 bouyer Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu.c,v 1.9 2018/04/01 21:19:17 bouyer Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -184,6 +184,28 @@ sunxi_ccu_clock_set_rate(void *priv, str return ENXIO; } +static u_int +sunxi_ccu_clock_round_rate(void *priv, struct clk *clkp, u_int rate) +{ + struct sunxi_ccu_softc * const sc = priv; + struct sunxi_ccu_clk *clk = (struct sunxi_ccu_clk *)clkp; + struct clk *clkp_parent; + + if (clkp->flags & CLK_SET_RATE_PARENT) { + clkp_parent = clk_get_parent(clkp); + if (clkp_parent == NULL) { + aprint_error("%s: no parent for %s\n", __func__, clk->base.name); + return 0; + } + return clk_round_rate(clkp_parent, rate); + } + + if (clk->round_rate) + return clk->round_rate(sc, clk, rate); + + return 0; +} + static int sunxi_ccu_clock_enable(void *priv, struct clk *clkp) { @@ -259,6 +281,7 @@ static const struct clk_funcs sunxi_ccu_ .put = sunxi_ccu_clock_put, .get_rate = sunxi_ccu_clock_get_rate, .set_rate = sunxi_ccu_clock_set_rate, + .round_rate = sunxi_ccu_clock_round_rate, .enable = sunxi_ccu_clock_enable, .disable = sunxi_ccu_clock_disable, .set_parent = sunxi_ccu_clock_set_parent, Index: src/sys/arch/arm/sunxi/sunxi_ccu.h diff -u src/sys/arch/arm/sunxi/sunxi_ccu.h:1.16 src/sys/arch/arm/sunxi/sunxi_ccu.h:1.17 --- src/sys/arch/arm/sunxi/sunxi_ccu.h:1.16 Mon Mar 19 16:18:30 2018 +++ src/sys/arch/arm/sunxi/sunxi_ccu.h Sun Apr 1 21:19:17 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_ccu.h,v 1.16 2018/03/19 16:18:30 bouyer Exp $ */ +/* $NetBSD: sunxi_ccu.h,v 1.17 2018/04/01 21:19:17 bouyer Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -247,6 +247,18 @@ const char *sunxi_ccu_div_get_parent(str .get_parent = sunxi_ccu_div_get_parent, \ } +/* special case of the div model for display clocks */ +int sunxi_ccu_lcdxch0_set_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, struct sunxi_ccu_clk *, + struct sunxi_ccu_clk *, u_int); +u_int sunxi_ccu_lcdxch0_round_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, struct sunxi_ccu_clk *, + struct sunxi_ccu_clk *, u_int); + +int sunxi_ccu_lcdxch1_set_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk *clk, struct sunxi_ccu_clk *pclk, + struct sunxi_ccu_clk *pclk_x2, u_int); + struct sunxi_ccu_prediv { bus_size_t reg; const char **parents; @@ -349,7 +361,7 @@ struct sunxi_ccu_fractional { uint32_t m; uint32_t m_min; uint32_t m_max; - uint32_t frac_en; + uint32_t div_en; uint32_t frac_sel; uint32_t frac[2]; uint32_t prediv; @@ -362,11 +374,13 @@ u_int sunxi_ccu_fractional_get_rate(stru struct sunxi_ccu_clk *); int sunxi_ccu_fractional_set_rate(struct sunxi_ccu_softc *, struct sunxi_ccu_clk *, u_int); +u_int sunxi_ccu_fractional_round_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, u_int); const char *sunxi_ccu_fractional_get_parent(struct sunxi_ccu_softc *, struct sunxi_ccu_clk *); #define SUNXI_CCU_FRACTIONAL(_id, _name, _parent, _reg, _m, _m_min, _m_max, \ - _frac_en, _frac_sel, _frac0, _frac1, _prediv, _enable) \ + _div_en, _frac_sel, _frac0, _frac1, _prediv, _enable) \ [_id] = { \ .type = SUNXI_CCU_FRACTIONAL, \ .base.name = (_name), \ @@ -376,7 +390,7 @@ const char *sunxi_ccu_fractional_get_par .u.fractional.m_min = (_m_min), \ .u.fractional.m_max = (_m_max), \ .u.fractional.prediv = (_prediv), \ - .u.fractional.frac_en = (_frac_en), \ + .u.fractional.div_en = (_div_en), \ .u.fractional.frac_sel = (_frac_sel), \ .u.fractional.frac[0] = (_frac0), \ .u.fractional.frac[1] = (_frac1), \ @@ -384,6 +398,7 @@ const char *sunxi_ccu_fractional_get_par .enable = sunxi_ccu_fractional_enable, \ .get_rate = sunxi_ccu_fractional_get_rate, \ .set_rate = sunxi_ccu_fractional_set_rate, \ + .round_rate = sunxi_ccu_fractional_round_rate, \ .get_parent = sunxi_ccu_fractional_get_parent, \ } @@ -407,6 +422,8 @@ struct sunxi_ccu_clk { struct sunxi_ccu_clk *); int (*set_rate)(struct sunxi_ccu_softc *, struct sunxi_ccu_clk *, u_int); + u_int (*round_rate)(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, u_int); const char * (*get_parent)(struct sunxi_ccu_softc *, struct sunxi_ccu_clk *); int (*set_parent)(struct sunxi_ccu_softc *, Index: src/sys/arch/arm/sunxi/sunxi_ccu_fractional.c diff -u src/sys/arch/arm/sunxi/sunxi_ccu_fractional.c:1.1 src/sys/arch/arm/sunxi/sunxi_ccu_fractional.c:1.2 --- src/sys/arch/arm/sunxi/sunxi_ccu_fractional.c:1.1 Mon Mar 19 16:18:30 2018 +++ src/sys/arch/arm/sunxi/sunxi_ccu_fractional.c Sun Apr 1 21:19:17 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_ccu_fractional.c,v 1.1 2018/03/19 16:18:30 bouyer Exp $ */ +/* $NetBSD: sunxi_ccu_fractional.c,v 1.2 2018/04/01 21:19:17 bouyer Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_fractional.c,v 1.1 2018/03/19 16:18:30 bouyer Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_fractional.c,v 1.2 2018/04/01 21:19:17 bouyer Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -86,7 +86,7 @@ sunxi_ccu_fractional_get_rate(struct sun if (fractional->enable && !(val & fractional->enable)) return 0; - if (val & fractional->frac_en) { + if ((val & fractional->div_en) == 0) { int sel = __SHIFTOUT(val, fractional->frac_sel); return fractional->frac[sel]; } @@ -122,14 +122,14 @@ sunxi_ccu_fractional_set_rate(struct sun val = CCU_READ(sc, fractional->reg); for (i = 0; i < __arraycount(fractional->frac); i++) { if (fractional->frac[i] == new_rate) { - val |= fractional->frac_en; + val &= ~fractional->div_en; val &= ~fractional->frac_sel; val |= __SHIFTIN(i, fractional->frac_sel); CCU_WRITE(sc, fractional->reg, val); return 0; } } - val &= ~fractional->frac_en; + val |= fractional->div_en; best_rate = 0; best_diff = INT_MAX; @@ -141,6 +141,8 @@ sunxi_ccu_fractional_set_rate(struct sun best_diff = diff; best_rate = rate; best_m = m; + if (diff == 0) + break; } } @@ -154,6 +156,52 @@ sunxi_ccu_fractional_set_rate(struct sun return 0; } +u_int +sunxi_ccu_fractional_round_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk *clk, u_int try_rate) +{ + struct sunxi_ccu_fractional *fractional = &clk->u.fractional; + struct clk *clkp, *clkp_parent; + u_int parent_rate, best_rate; + u_int m, rate; + int best_diff; + int i; + + clkp = &clk->base; + clkp_parent = clk_get_parent(clkp); + if (clkp_parent == NULL) + return 0; + + parent_rate = clk_get_rate(clkp_parent); + if (parent_rate == 0) + return 0; + + if (fractional->prediv > 0) + parent_rate = parent_rate / fractional->prediv; + + for (i = 0; i < __arraycount(fractional->frac); i++) { + if (fractional->frac[i] == try_rate) { + return try_rate; + } + } + + best_rate = 0; + best_diff = INT_MAX; + + for (m = fractional->m_min; m <= fractional->m_max; m++) { + rate = parent_rate * m; + const int diff = abs(try_rate - rate); + if (diff < best_diff) { + best_diff = diff; + best_rate = rate; + if (diff == 0) + break; + } + } + + return best_rate; +} + const char * sunxi_ccu_fractional_get_parent(struct sunxi_ccu_softc *sc, struct sunxi_ccu_clk *clk) Added files: Index: src/sys/arch/arm/sunxi/sunxi_ccu_display.c diff -u /dev/null src/sys/arch/arm/sunxi/sunxi_ccu_display.c:1.1 --- /dev/null Sun Apr 1 21:19:18 2018 +++ src/sys/arch/arm/sunxi/sunxi_ccu_display.c Sun Apr 1 21:19:17 2018 @@ -0,0 +1,128 @@ +/* $NetBSD: sunxi_ccu_display.c,v 1.1 2018/04/01 21:19:17 bouyer Exp $ */ + +/*- + * Copyright (c) 2018 Manuel Bouyer <bou...@antioche.eu.org> + * All rights reserved. + * + * Copyright (c) 2014 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: sunxi_ccu_display.c,v 1.1 2018/04/01 21:19:17 bouyer Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> + +#include <dev/clk/clk_backend.h> + +#include <arm/sunxi/sunxi_ccu.h> + +int +sunxi_ccu_lcdxch0_set_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk *clk, struct sunxi_ccu_clk *pllclk, + struct sunxi_ccu_clk *pllclk_x2, u_int new_rate) +{ + struct clk *clkp; + int error; + + clkp = &pllclk->base; + error = clk_set_rate(clkp, new_rate); + if (error) { + error = clk_set_rate(clkp, new_rate / 2); + if (error != 0) + return error; + clkp = &pllclk_x2->base; + } + error = clk_set_parent(&clk->base, clkp); + KASSERT(error == 0); + return error; +} + +u_int +sunxi_ccu_lcdxch0_round_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk * clk, struct sunxi_ccu_clk *pllclk, + struct sunxi_ccu_clk *pllclk_x2, u_int try_rate) +{ + struct clk *clkp; + int diff, diff_x2; + int rate, rate_x2; + + clkp = &pllclk->base; + rate = clk_round_rate(clkp, try_rate); + diff = abs(try_rate - rate); + + rate_x2 = (clk_round_rate(clkp, try_rate / 2) * 2); + diff_x2 = abs(try_rate - rate_x2); + + if (diff_x2 < diff) + return rate_x2; + return rate; +} + +int +sunxi_ccu_lcdxch1_set_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk * clk, struct sunxi_ccu_clk *pllclk, + struct sunxi_ccu_clk *pllclk_x2, u_int new_rate) +{ + struct clk *clkp, *pllclkp; + int best_diff; + int parent_rate, best_parent_rate; + uint32_t best_m, best_d; + int error; + + pllclkp = clkp = &pllclk->base; + best_diff = INT_MAX; + best_m = best_d = 0; + for (uint32_t d = 1; d <= 2 && best_diff != 0; d++) { + for (uint32_t m = 1; m <= 16 && best_diff != 0; m++) { + int rate, diff; + parent_rate = clk_round_rate(pllclkp, + new_rate * m / d); + if (parent_rate == 0) + continue; + rate = parent_rate * d / m; + diff = abs(rate - new_rate); + if (diff < best_diff) { + best_diff = diff; + best_m = m; + best_d = d; + best_parent_rate = parent_rate; + } + } + } + if (best_m == 0) + return ERANGE; + + if (best_d == 2) + clkp = &pllclk_x2->base; + + error = clk_set_rate(pllclkp, best_parent_rate); + KASSERT(error == 0); + error = clk_set_parent(&clk->base, clkp); + KASSERT(error == 0); + error = sunxi_ccu_div_set_rate(sc, clk, new_rate); + KASSERT(error == 0); + return error; +}