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;
+}

Reply via email to