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 <[email protected]>
@@ -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 <[email protected]>
@@ -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 <[email protected]>
@@ -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 <[email protected]>
@@ -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 <[email protected]>
+ * All rights reserved.
+ *
+ * Copyright (c) 2014 Jared D. McNeill <[email protected]>
+ * 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;
+}