Module Name: src
Committed By: martin
Date: Mon Nov 18 19:31:00 UTC 2019
Modified Files:
src/sys/arch/arm/dts [netbsd-9]: sun50i-a64-pine64-plus.dts
sun50i-a64-pine64.dts sun50i-a64-sopine-baseboard.dts
sun50i-a64.dtsi
src/sys/arch/arm/sunxi [netbsd-9]: sun50i_a64_ccu.c sunxi_ccu_div.c
sunxi_dwhdmi.c sunxi_i2s.c
Log Message:
Pull up following revision(s) (requested by jmcneill in ticket #437):
sys/arch/arm/dts/sun50i-a64-sopine-baseboard.dts: revision 1.2
sys/arch/arm/sunxi/sunxi_ccu_div.c: revision 1.6
sys/arch/arm/dts/sun50i-a64.dtsi: revision 1.13
sys/arch/arm/dts/sun50i-a64-pine64.dts: revision 1.2
sys/arch/arm/sunxi/sunxi_dwhdmi.c: revision 1.4
sys/arch/arm/dts/sun50i-a64-pine64-plus.dts: revision 1.3
sys/arch/arm/sunxi/sunxi_i2s.c: revision 1.7
sys/arch/arm/sunxi/sun50i_a64_ccu.c: revision 1.14
Add support for A64 I2S clocks.
Add A64 HDMI audio support.
Enable HDMI audio support on Pine64, Pine64+, and Pine64 LTS boards.
To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.2.10.1 src/sys/arch/arm/dts/sun50i-a64-pine64-plus.dts
cvs rdiff -u -r1.1 -r1.1.8.1 src/sys/arch/arm/dts/sun50i-a64-pine64.dts \
src/sys/arch/arm/dts/sun50i-a64-sopine-baseboard.dts
cvs rdiff -u -r1.11.2.1 -r1.11.2.2 src/sys/arch/arm/dts/sun50i-a64.dtsi
cvs rdiff -u -r1.13 -r1.13.2.1 src/sys/arch/arm/sunxi/sun50i_a64_ccu.c
cvs rdiff -u -r1.5 -r1.5.6.1 src/sys/arch/arm/sunxi/sunxi_ccu_div.c
cvs rdiff -u -r1.3 -r1.3.6.1 src/sys/arch/arm/sunxi/sunxi_dwhdmi.c
cvs rdiff -u -r1.6 -r1.6.2.1 src/sys/arch/arm/sunxi/sunxi_i2s.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/sun50i-a64-pine64-plus.dts
diff -u src/sys/arch/arm/dts/sun50i-a64-pine64-plus.dts:1.2 src/sys/arch/arm/dts/sun50i-a64-pine64-plus.dts:1.2.10.1
--- src/sys/arch/arm/dts/sun50i-a64-pine64-plus.dts:1.2 Sat Sep 9 12:05:28 2017
+++ src/sys/arch/arm/dts/sun50i-a64-pine64-plus.dts Mon Nov 18 19:31:00 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sun50i-a64-pine64-plus.dts,v 1.2 2017/09/09 12:05:28 jmcneill Exp $ */
+/* $NetBSD: sun50i-a64-pine64-plus.dts,v 1.2.10.1 2019/11/18 19:31:00 martin Exp $ */
/*-
* Copyright (c) 2017 Jared McNeill <[email protected]>
@@ -49,3 +49,11 @@
&ohci0 {
status = "okay";
};
+
+&i2s2 {
+ status = "okay";
+};
+
+&sound_hdmi {
+ status = "okay";
+};
Index: src/sys/arch/arm/dts/sun50i-a64-pine64.dts
diff -u src/sys/arch/arm/dts/sun50i-a64-pine64.dts:1.1 src/sys/arch/arm/dts/sun50i-a64-pine64.dts:1.1.8.1
--- src/sys/arch/arm/dts/sun50i-a64-pine64.dts:1.1 Sun Feb 18 12:20:25 2018
+++ src/sys/arch/arm/dts/sun50i-a64-pine64.dts Mon Nov 18 19:31:00 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sun50i-a64-pine64.dts,v 1.1 2018/02/18 12:20:25 jmcneill Exp $ */
+/* $NetBSD: sun50i-a64-pine64.dts,v 1.1.8.1 2019/11/18 19:31:00 martin Exp $ */
/*-
* Copyright (c) 2018 Jared McNeill <[email protected]>
@@ -28,3 +28,11 @@
#include "../../../external/gpl2/dts/dist/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts"
#include "sun50i-a64.dtsi"
+
+&i2s2 {
+ status = "okay";
+};
+
+&sound_hdmi {
+ status = "okay";
+};
Index: src/sys/arch/arm/dts/sun50i-a64-sopine-baseboard.dts
diff -u src/sys/arch/arm/dts/sun50i-a64-sopine-baseboard.dts:1.1 src/sys/arch/arm/dts/sun50i-a64-sopine-baseboard.dts:1.1.8.1
--- src/sys/arch/arm/dts/sun50i-a64-sopine-baseboard.dts:1.1 Thu Jun 7 00:51:41 2018
+++ src/sys/arch/arm/dts/sun50i-a64-sopine-baseboard.dts Mon Nov 18 19:31:00 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sun50i-a64-sopine-baseboard.dts,v 1.1 2018/06/07 00:51:41 jmcneill Exp $ */
+/* $NetBSD: sun50i-a64-sopine-baseboard.dts,v 1.1.8.1 2019/11/18 19:31:00 martin Exp $ */
/*-
* Copyright (c) 2018 Jared McNeill <[email protected]>
@@ -28,3 +28,11 @@
#include "../../../external/gpl2/dts/dist/arch/arm64/boot/dts/allwinner/sun50i-a64-sopine-baseboard.dts"
#include "sun50i-a64.dtsi"
+
+&i2s2 {
+ status = "okay";
+};
+
+&sound_hdmi {
+ status = "okay";
+};
Index: src/sys/arch/arm/dts/sun50i-a64.dtsi
diff -u src/sys/arch/arm/dts/sun50i-a64.dtsi:1.11.2.1 src/sys/arch/arm/dts/sun50i-a64.dtsi:1.11.2.2
--- src/sys/arch/arm/dts/sun50i-a64.dtsi:1.11.2.1 Sun Nov 10 13:14:41 2019
+++ src/sys/arch/arm/dts/sun50i-a64.dtsi Mon Nov 18 19:31:00 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sun50i-a64.dtsi,v 1.11.2.1 2019/11/10 13:14:41 martin Exp $ */
+/* $NetBSD: sun50i-a64.dtsi,v 1.11.2.2 2019/11/18 19:31:00 martin Exp $ */
/*-
* Copyright (c) 2017 Jared McNeill <[email protected]>
@@ -59,6 +59,36 @@
<GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
};
+
+ i2s2: i2s@1c22800 {
+ #sound-dai-cells = <0>;
+ compatible = "allwinner,sun50i-a64-i2s",
+ "allwinner,sun8i-h3-i2s";
+ reg = <0x01c22800 0x400>;
+ interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2S2>, <&ccu CLK_I2S2>;
+ clock-names = "apb", "mod";
+ resets = <&ccu RST_BUS_I2S2>;
+ dma-names = "tx";
+ dmas = <&dma 27>;
+ status = "disabled";
+ };
+
+ sound_hdmi: sound-hdmi {
+ compatible = "simple-audio-card";
+ simple-audio-card,name = "hdmi-audio";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,mclk-fs = <256>;
+ status = "disabled";
+
+ simple-audio-card,cpu {
+ sound-dai = <&i2s2>;
+ };
+
+ simple-audio-card,codec {
+ sound-dai = <&hdmi>;
+ };
+ };
};
&cpu0 {
Index: src/sys/arch/arm/sunxi/sun50i_a64_ccu.c
diff -u src/sys/arch/arm/sunxi/sun50i_a64_ccu.c:1.13 src/sys/arch/arm/sunxi/sun50i_a64_ccu.c:1.13.2.1
--- src/sys/arch/arm/sunxi/sun50i_a64_ccu.c:1.13 Mon Jul 1 21:06:47 2019
+++ src/sys/arch/arm/sunxi/sun50i_a64_ccu.c Mon Nov 18 19:31:00 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sun50i_a64_ccu.c,v 1.13 2019/07/01 21:06:47 jmcneill Exp $ */
+/* $NetBSD: sun50i_a64_ccu.c,v 1.13.2.1 2019/11/18 19:31:00 martin Exp $ */
/*-
* Copyright (c) 2017 Jared McNeill <[email protected]>
@@ -28,7 +28,7 @@
#include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: sun50i_a64_ccu.c,v 1.13 2019/07/01 21:06:47 jmcneill Exp $");
+__KERNEL_RCSID(1, "$NetBSD: sun50i_a64_ccu.c,v 1.13.2.1 2019/11/18 19:31:00 martin Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -60,6 +60,9 @@ __KERNEL_RCSID(1, "$NetBSD: sun50i_a64_c
#define SDMMC0_CLK_REG 0x088
#define SDMMC1_CLK_REG 0x08c
#define SDMMC2_CLK_REG 0x090
+#define I2SPCM0_CLK_REG 0x0b0
+#define I2SPCM1_CLK_REG 0x0b4
+#define I2SPCM2_CLK_REG 0x0b8
#define USBPHY_CFG_REG 0x0cc
#define DRAM_CFG_REG 0x0f4
#define MBUS_RST_REG 0x0fc
@@ -154,6 +157,7 @@ static const char *mmc_parents[] = { "ho
static const char *ths_parents[] = { "hosc", NULL, NULL, NULL };
static const char *de_parents[] = { "pll_periph0_2x", "pll_de" };
static const char *hdmi_parents[] = { "pll_video0", "pll_video1" };
+static const char *i2s_parents[] = { "pll_audio_8x", "pll_audio_4x", "pll_audio_2x", "pll_audio" };
static const char *tcon1_parents[] = { "pll_video0", NULL, "pll_video1", NULL };
static const char *gpu_parents[] = { "pll_gpu" };
@@ -407,6 +411,26 @@ static struct sunxi_ccu_clk sun50i_a64_c
SUNXI_CCU_GATE(A64_CLK_HDMI_DDC, "hdmi-ddc", "hosc",
HDMI_SLOW_CLK_REG, 31),
+ SUNXI_CCU_DIV_GATE(A64_CLK_I2S0, "i2s0", i2s_parents,
+ I2SPCM0_CLK_REG, /* reg */
+ 0, /* div */
+ __BITS(17,16), /* sel */
+ __BIT(31), /* enable */
+ 0),
+ SUNXI_CCU_DIV_GATE(A64_CLK_I2S1, "i2s1", i2s_parents,
+ I2SPCM1_CLK_REG, /* reg */
+ 0, /* div */
+ __BITS(17,16), /* sel */
+ __BIT(31), /* enable */
+ 0),
+ SUNXI_CCU_DIV_GATE(A64_CLK_I2S2, "i2s2", i2s_parents,
+ I2SPCM2_CLK_REG, /* reg */
+ 0, /* div */
+ __BITS(17,16), /* sel */
+ __BIT(31), /* enable */
+ 0),
+
+
SUNXI_CCU_DIV_GATE(A64_CLK_TCON1, "tcon1", tcon1_parents,
TCON1_CLK_REG, /* reg */
__BITS(3,0), /* div */
Index: src/sys/arch/arm/sunxi/sunxi_ccu_div.c
diff -u src/sys/arch/arm/sunxi/sunxi_ccu_div.c:1.5 src/sys/arch/arm/sunxi/sunxi_ccu_div.c:1.5.6.1
--- src/sys/arch/arm/sunxi/sunxi_ccu_div.c:1.5 Mon Mar 19 16:19:17 2018
+++ src/sys/arch/arm/sunxi/sunxi_ccu_div.c Mon Nov 18 19:31:00 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_ccu_div.c,v 1.5 2018/03/19 16:19:17 bouyer Exp $ */
+/* $NetBSD: sunxi_ccu_div.c,v 1.5.6.1 2019/11/18 19:31:00 martin Exp $ */
/*-
* Copyright (c) 2017 Jared McNeill <[email protected]>
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_div.c,v 1.5 2018/03/19 16:19:17 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_div.c,v 1.5.6.1 2019/11/18 19:31:00 martin Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -98,6 +98,38 @@ sunxi_ccu_div_get_rate(struct sunxi_ccu_
return rate / ratio;
}
+static int
+sunxi_ccu_div_select_parent(struct sunxi_ccu_softc *sc,
+ struct sunxi_ccu_clk *clk, u_int new_rate)
+{
+ struct sunxi_ccu_div *div = &clk->u.div;
+ struct sunxi_ccu_clk *clk_parent;
+ struct clk *best_parent;
+ u_int index, best_diff;
+ const char *pname;
+
+ best_parent = NULL;
+ best_diff = ~0u;
+ for (index = 0; index < div->nparents; index++) {
+ pname = div->parents[index];
+ if (pname == NULL)
+ continue;
+ clk_parent = sunxi_ccu_clock_find(sc, pname);
+ if (clk_parent == NULL)
+ continue;
+ const u_int rate = clk_get_rate(&clk_parent->base);
+ const u_int diff = abs((int)rate - (int)new_rate);
+ if (diff < best_diff) {
+ best_diff = diff;
+ best_parent = &clk_parent->base;
+ }
+ }
+ if (best_diff == ~0u)
+ return EINVAL;
+
+ return clk_set_parent(&clk->base, best_parent);
+}
+
int
sunxi_ccu_div_set_rate(struct sunxi_ccu_softc *sc,
struct sunxi_ccu_clk *clk, u_int new_rate)
@@ -119,7 +151,7 @@ sunxi_ccu_div_set_rate(struct sunxi_ccu_
if ((div->flags & SUNXI_CCU_DIV_SET_RATE_PARENT) != 0)
return clk_set_rate(clkp_parent, new_rate);
else
- return ENXIO;
+ return sunxi_ccu_div_select_parent(sc, clk, new_rate);
}
val = CCU_READ(sc, div->reg);
Index: src/sys/arch/arm/sunxi/sunxi_dwhdmi.c
diff -u src/sys/arch/arm/sunxi/sunxi_dwhdmi.c:1.3 src/sys/arch/arm/sunxi/sunxi_dwhdmi.c:1.3.6.1
--- src/sys/arch/arm/sunxi/sunxi_dwhdmi.c:1.3 Sat Feb 2 17:35:16 2019
+++ src/sys/arch/arm/sunxi/sunxi_dwhdmi.c Mon Nov 18 19:31:00 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_dwhdmi.c,v 1.3 2019/02/02 17:35:16 jmcneill Exp $ */
+/* $NetBSD: sunxi_dwhdmi.c,v 1.3.6.1 2019/11/18 19:31:00 martin Exp $ */
/*-
* Copyright (c) 2019 Jared D. McNeill <[email protected]>
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_dwhdmi.c,v 1.3 2019/02/02 17:35:16 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_dwhdmi.c,v 1.3.6.1 2019/11/18 19:31:00 martin Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -197,6 +197,21 @@ sunxi_dwhdmi_mode_set(struct dwhdmi_soft
sc->sc_curmode = *adjusted_mode;
}
+static audio_dai_tag_t
+sunxi_dwhdmi_dai_get_tag(device_t dev, const void *data, size_t len)
+{
+ struct sunxi_dwhdmi_softc * const sc = device_private(dev);
+
+ if (len != 4)
+ return NULL;
+
+ return &sc->sc_base.sc_dai;
+}
+
+static struct fdtbus_dai_controller_func sunxi_dwhdmi_dai_funcs = {
+ .get_tag = sunxi_dwhdmi_dai_get_tag
+};
+
static int
sunxi_dwhdmi_match(device_t parent, cfdata_t cf, void *aux)
{
@@ -263,6 +278,8 @@ sunxi_dwhdmi_attach(device_t parent, dev
sc->sc_ports.dp_ep_activate = sunxi_dwhdmi_ep_activate;
sc->sc_ports.dp_ep_get_data = sunxi_dwhdmi_ep_get_data;
fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_BRIDGE);
+
+ fdtbus_register_dai_controller(self, phandle, &sunxi_dwhdmi_dai_funcs);
}
CFATTACH_DECL_NEW(sunxi_dwhdmi, sizeof(struct sunxi_dwhdmi_softc),
Index: src/sys/arch/arm/sunxi/sunxi_i2s.c
diff -u src/sys/arch/arm/sunxi/sunxi_i2s.c:1.6 src/sys/arch/arm/sunxi/sunxi_i2s.c:1.6.2.1
--- src/sys/arch/arm/sunxi/sunxi_i2s.c:1.6 Sat Jun 8 08:02:37 2019
+++ src/sys/arch/arm/sunxi/sunxi_i2s.c Mon Nov 18 19:31:00 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_i2s.c,v 1.6 2019/06/08 08:02:37 isaki Exp $ */
+/* $NetBSD: sunxi_i2s.c,v 1.6.2.1 2019/11/18 19:31:00 martin Exp $ */
/*-
* Copyright (c) 2018 Jared McNeill <[email protected]>
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_i2s.c,v 1.6 2019/06/08 08:02:37 isaki Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_i2s.c,v 1.6.2.1 2019/11/18 19:31:00 martin Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -42,16 +42,24 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_i2s.c,
#include <dev/fdt/fdtvar.h>
-#define SUNXI_I2S_CLK_RATE 24576000
+#define SUNXI_I2S_CLK_RATE 24576000
+#define SUNXI_I2S_SAMPLE_RATE 48000
#define DA_CTL 0x00
+#define DA_CTL_BCLK_OUT __BIT(18) /* sun8i */
+#define DA_CLK_LRCK_OUT __BIT(17) /* sun8i */
#define DA_CTL_SDO_EN __BIT(8)
-#define DA_CTL_MS __BIT(5)
-#define DA_CTL_PCM __BIT(4)
+#define DA_CTL_MS __BIT(5) /* sun4i */
+#define DA_CTL_PCM __BIT(4) /* sun4i */
+#define DA_CTL_MODE_SEL __BITS(5,4) /* sun8i */
+#define DA_CTL_MODE_SEL_PCM 0
+#define DA_CTL_MODE_SEL_LJ 1
+#define DA_CTL_MODE_SEL_RJ 2
#define DA_CTL_TXEN __BIT(2)
#define DA_CTL_RXEN __BIT(1)
#define DA_CTL_GEN __BIT(0)
#define DA_FAT0 0x04
+#define DA_FAT0_LRCK_PERIOD __BITS(17,8) /* sun8i */
#define DA_FAT0_LRCP __BIT(7)
#define DA_LRCP_NORMAL 0
#define DA_LRCP_INVERTED 1
@@ -79,20 +87,34 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_i2s.c,
#define DA_INT_RX_DRQ __BIT(3)
#define DA_TXFIFO 0x20
#define DA_CLKD 0x24
-#define DA_CLKD_MCLKO_EN __BIT(7)
-#define DA_CLKD_BCLKDIV __BITS(6,4)
+#define DA_CLKD_MCLKO_EN_SUN8I __BIT(8)
+#define DA_CLKD_MCLKO_EN_SUN4I __BIT(7)
+#define DA_CLKD_BCLKDIV_SUN8I __BITS(7,4)
+#define DA_CLKD_BCLKDIV_SUN4I __BITS(6,4)
#define DA_CLKD_BCLKDIV_8 3
#define DA_CLKD_BCLKDIV_16 5
#define DA_CLKD_MCLKDIV __BITS(3,0)
#define DA_CLKD_MCLKDIV_1 0
#define DA_TXCNT 0x28
#define DA_RXCNT 0x2c
+#define DA_CHCFG 0x30 /* sun8i */
+#define DA_CHCFG_TX_SLOT_HIZ __BIT(9)
+#define DA_CHCFG_TXN_STATE __BIT(8)
+#define DA_CHCFG_RX_SLOT_NUM __BITS(6,4)
+#define DA_CHCFG_TX_SLOT_NUM __BITS(2,0)
+#define DA_CHSEL_OFFSET __BITS(13,12) /* sun8i */
#define DA_CHSEL_EN __BITS(11,4)
#define DA_CHSEL_SEL __BITS(2,0)
+enum sunxi_i2s_type {
+ SUNXI_I2S_SUN4I,
+ SUNXI_I2S_SUN8I,
+};
+
struct sunxi_i2s_config {
const char *name;
+ enum sunxi_i2s_type type;
bus_size_t txchsel;
bus_size_t txchmap;
bus_size_t rxchsel;
@@ -101,15 +123,27 @@ struct sunxi_i2s_config {
static const struct sunxi_i2s_config sun50i_a64_codec_config = {
.name = "Audio Codec (digital part)",
+ .type = SUNXI_I2S_SUN4I,
.txchsel = 0x30,
.txchmap = 0x34,
.rxchsel = 0x38,
.rxchmap = 0x3c,
};
+static const struct sunxi_i2s_config sun8i_h3_config = {
+ .name = "I2S/PCM controller",
+ .type = SUNXI_I2S_SUN8I,
+ .txchsel = 0x34,
+ .txchmap = 0x44,
+ .rxchsel = 0x54,
+ .rxchmap = 0x58,
+};
+
static const struct of_compat_data compat_data[] = {
{ "allwinner,sun50i-a64-codec-i2s",
(uintptr_t)&sun50i_a64_codec_config },
+ { "allwinner,sun8i-h3-i2s",
+ (uintptr_t)&sun8i_h3_config },
{ NULL }
};
@@ -150,6 +184,7 @@ struct sunxi_i2s_softc {
bus_dma_tag_t sc_dmat;
int sc_phandle;
bus_addr_t sc_baseaddr;
+ struct clk *sc_clk;
struct sunxi_i2s_config *sc_cfg;
@@ -166,11 +201,63 @@ struct sunxi_i2s_softc {
struct audio_dai_device sc_dai;
};
+#define I2S_TYPE(sc) ((sc)->sc_cfg->type)
+
#define I2S_READ(sc, reg) \
bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
#define I2S_WRITE(sc, reg, val) \
bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+static const u_int sun4i_i2s_bclk_divmap[] = {
+ [0] = 2,
+ [1] = 4,
+ [2] = 6,
+ [3] = 8,
+ [4] = 12,
+ [5] = 16,
+};
+
+static const u_int sun4i_i2s_mclk_divmap[] = {
+ [0] = 1,
+ [1] = 2,
+ [2] = 4,
+ [3] = 6,
+ [4] = 8,
+ [5] = 12,
+ [6] = 16,
+ [7] = 24,
+};
+
+static const u_int sun8i_i2s_divmap[] = {
+ [1] = 1,
+ [2] = 2,
+ [3] = 4,
+ [4] = 6,
+ [5] = 8,
+ [6] = 12,
+ [7] = 16,
+ [8] = 24,
+ [9] = 32,
+ [10] = 48,
+ [11] = 64,
+ [12] = 96,
+ [13] = 128,
+ [14] = 176,
+ [15] = 192,
+};
+
+static u_int
+sunxi_i2s_div_to_regval(const u_int *divmap, u_int divmaplen, u_int div)
+{
+ u_int n;
+
+ for (n = 0; n < divmaplen; n++)
+ if (divmap[n] == div)
+ return n;
+
+ return -1;
+}
+
static int
sunxi_i2s_allocdma(struct sunxi_i2s_softc *sc, size_t size,
size_t align, struct sunxi_i2s_dma *dma)
@@ -246,13 +333,6 @@ sunxi_i2s_set_format(void *priv, int set
audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
{
- if ((setmode & AUMODE_PLAY)) {
- pfil->codec = audio_internal_to_linear32;
- }
- if ((setmode & AUMODE_RECORD)) {
- rfil->codec = audio_linear32_to_internal;
- }
-
return 0;
}
@@ -296,9 +376,17 @@ sunxi_i2s_freem(void *priv, void *addr,
static int
sunxi_i2s_get_props(void *priv)
{
+ struct sunxi_i2s_softc * const sc = priv;
+ int props = 0;
- return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
- AUDIO_PROP_FULLDUPLEX | AUDIO_PROP_INDEPENDENT;
+ if (sc->sc_pchan.ch_dma != NULL)
+ props |= AUDIO_PROP_PLAYBACK;
+ if (sc->sc_rchan.ch_dma != NULL)
+ props |= AUDIO_PROP_CAPTURE;
+ if (sc->sc_pchan.ch_dma != NULL && sc->sc_rchan.ch_dma != NULL)
+ props |= AUDIO_PROP_FULLDUPLEX;
+
+ return props;
}
static int
@@ -323,6 +411,9 @@ sunxi_i2s_trigger_output(void *priv, voi
uint32_t val;
int error;
+ if (ch->ch_dma == NULL)
+ return EIO;
+
pstart = 0;
psize = (uintptr_t)end - (uintptr_t)start;
@@ -381,6 +472,9 @@ sunxi_i2s_trigger_input(void *priv, void
uint32_t val;
int error;
+ if (ch->ch_dma == NULL)
+ return EIO;
+
pstart = 0;
psize = (uintptr_t)end - (uintptr_t)start;
@@ -434,6 +528,9 @@ sunxi_i2s_halt_output(void *priv)
struct sunxi_i2s_chan *ch = &sc->sc_pchan;
uint32_t val;
+ if (ch->ch_dma == NULL)
+ return EIO;
+
/* Disable DMA channel */
fdtbus_dma_halt(ch->ch_dma);
@@ -458,6 +555,9 @@ sunxi_i2s_halt_input(void *priv)
struct sunxi_i2s_chan *ch = &sc->sc_rchan;
uint32_t val;
+ if (ch->ch_dma == NULL)
+ return EIO;
+
/* Disable DMA channel */
fdtbus_dma_halt(ch->ch_dma);
@@ -520,10 +620,8 @@ sunxi_i2s_chan_init(struct sunxi_i2s_sof
ch->ch_sc = sc;
ch->ch_mode = mode;
ch->ch_dma = fdtbus_dma_get(sc->sc_phandle, dmaname, sunxi_i2s_dmaintr, ch);
- if (ch->ch_dma == NULL) {
- aprint_error(": couldn't get dma channel \"%s\"\n", dmaname);
+ if (ch->ch_dma == NULL)
return ENXIO;
- }
if (mode == AUMODE_PLAY) {
ch->ch_req.dreq_dir = FDT_DMA_WRITE;
@@ -534,9 +632,9 @@ sunxi_i2s_chan_init(struct sunxi_i2s_sof
ch->ch_req.dreq_dev_phys =
sc->sc_baseaddr + DA_RXFIFO;
}
- ch->ch_req.dreq_mem_opt.opt_bus_width = 32;
+ ch->ch_req.dreq_mem_opt.opt_bus_width = 16;
ch->ch_req.dreq_mem_opt.opt_burst_len = 8;
- ch->ch_req.dreq_dev_opt.opt_bus_width = 32;
+ ch->ch_req.dreq_dev_opt.opt_bus_width = 16;
ch->ch_req.dreq_dev_opt.opt_burst_len = 8;
return 0;
@@ -546,14 +644,56 @@ static int
sunxi_i2s_dai_set_sysclk(audio_dai_tag_t dai, u_int rate, int dir)
{
struct sunxi_i2s_softc * const sc = audio_dai_private(dai);
+ int bclk_val, mclk_val;
uint32_t val;
+ int error;
+
+ error = clk_set_rate(sc->sc_clk, SUNXI_I2S_CLK_RATE);
+ if (error != 0) {
+ aprint_error_dev(sc->sc_dev,
+ "couldn't set mod clock rate to %u Hz: %d\n", SUNXI_I2S_CLK_RATE, error);
+ return error;
+ }
+ error = clk_enable(sc->sc_clk);
+ if (error != 0) {
+ aprint_error_dev(sc->sc_dev,
+ "couldn't enable mod clock: %d\n", error);
+ return error;
+ }
- /* XXX */
+ const u_int bclk_prate = I2S_TYPE(sc) == SUNXI_I2S_SUN4I ? rate : SUNXI_I2S_CLK_RATE;
- val = DA_CLKD_MCLKO_EN;
- val |= __SHIFTIN(DA_CLKD_BCLKDIV_8, DA_CLKD_BCLKDIV);
- val |= __SHIFTIN(DA_CLKD_MCLKDIV_1, DA_CLKD_MCLKDIV);
+ const u_int bclk_div = bclk_prate / (2 * 32 * SUNXI_I2S_SAMPLE_RATE);
+ const u_int mclk_div = SUNXI_I2S_CLK_RATE / rate;
+ if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) {
+ bclk_val = sunxi_i2s_div_to_regval(sun4i_i2s_bclk_divmap,
+ __arraycount(sun4i_i2s_bclk_divmap), bclk_div);
+ mclk_val = sunxi_i2s_div_to_regval(sun4i_i2s_mclk_divmap,
+ __arraycount(sun4i_i2s_mclk_divmap), mclk_div);
+ } else {
+ bclk_val = sunxi_i2s_div_to_regval(sun8i_i2s_divmap,
+ __arraycount(sun8i_i2s_divmap), bclk_div);
+ mclk_val = sunxi_i2s_div_to_regval(sun8i_i2s_divmap,
+ __arraycount(sun8i_i2s_divmap), mclk_div);
+ }
+ if (bclk_val == -1 || mclk_val == -1) {
+ aprint_error_dev(sc->sc_dev, "couldn't configure bclk/mclk dividers\n");
+ return EIO;
+ }
+
+ val = I2S_READ(sc, DA_CLKD);
+ if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) {
+ val |= DA_CLKD_MCLKO_EN_SUN4I;
+ val &= ~DA_CLKD_BCLKDIV_SUN4I;
+ val |= __SHIFTIN(bclk_val, DA_CLKD_BCLKDIV_SUN4I);
+ } else {
+ val |= DA_CLKD_MCLKO_EN_SUN8I;
+ val &= ~DA_CLKD_BCLKDIV_SUN8I;
+ val |= __SHIFTIN(bclk_val, DA_CLKD_BCLKDIV_SUN8I);
+ }
+ val &= ~DA_CLKD_MCLKDIV;
+ val |= __SHIFTIN(mclk_val, DA_CLKD_MCLKDIV);
I2S_WRITE(sc, DA_CLKD, val);
return 0;
@@ -563,7 +703,8 @@ static int
sunxi_i2s_dai_set_format(audio_dai_tag_t dai, u_int format)
{
struct sunxi_i2s_softc * const sc = audio_dai_private(dai);
- uint32_t ctl, fat0;
+ uint32_t ctl, fat0, chsel;
+ u_int offset;
const u_int fmt = __SHIFTOUT(format, AUDIO_DAI_FORMAT_MASK);
const u_int pol = __SHIFTOUT(format, AUDIO_DAI_POLARITY_MASK);
@@ -572,40 +713,97 @@ sunxi_i2s_dai_set_format(audio_dai_tag_t
ctl = I2S_READ(sc, DA_CTL);
fat0 = I2S_READ(sc, DA_FAT0);
- fat0 &= ~DA_FAT0_FMT;
- switch (fmt) {
- case AUDIO_DAI_FORMAT_I2S:
- fat0 |= __SHIFTIN(DA_FMT_I2S, DA_FAT0_FMT);
- break;
- case AUDIO_DAI_FORMAT_RJ:
- fat0 |= __SHIFTIN(DA_FMT_RJ, DA_FAT0_FMT);
- break;
- case AUDIO_DAI_FORMAT_LJ:
- fat0 |= __SHIFTIN(DA_FMT_LJ, DA_FAT0_FMT);
- break;
- default:
- return EINVAL;
+ if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) {
+ fat0 &= ~DA_FAT0_FMT;
+ switch (fmt) {
+ case AUDIO_DAI_FORMAT_I2S:
+ fat0 |= __SHIFTIN(DA_FMT_I2S, DA_FAT0_FMT);
+ break;
+ case AUDIO_DAI_FORMAT_RJ:
+ fat0 |= __SHIFTIN(DA_FMT_RJ, DA_FAT0_FMT);
+ break;
+ case AUDIO_DAI_FORMAT_LJ:
+ fat0 |= __SHIFTIN(DA_FMT_LJ, DA_FAT0_FMT);
+ break;
+ default:
+ return EINVAL;
+ }
+ ctl &= ~DA_CTL_PCM;
+ } else {
+ ctl &= ~DA_CTL_MODE_SEL;
+ switch (fmt) {
+ case AUDIO_DAI_FORMAT_I2S:
+ ctl |= __SHIFTIN(DA_CTL_MODE_SEL_LJ, DA_CTL_MODE_SEL);
+ offset = 1;
+ break;
+ case AUDIO_DAI_FORMAT_LJ:
+ ctl |= __SHIFTIN(DA_CTL_MODE_SEL_LJ, DA_CTL_MODE_SEL);
+ offset = 0;
+ break;
+ case AUDIO_DAI_FORMAT_RJ:
+ ctl |= __SHIFTIN(DA_CTL_MODE_SEL_RJ, DA_CTL_MODE_SEL);
+ offset = 0;
+ break;
+ case AUDIO_DAI_FORMAT_DSPA:
+ ctl |= __SHIFTIN(DA_CTL_MODE_SEL_PCM, DA_CTL_MODE_SEL);
+ offset = 1;
+ break;
+ case AUDIO_DAI_FORMAT_DSPB:
+ ctl |= __SHIFTIN(DA_CTL_MODE_SEL_PCM, DA_CTL_MODE_SEL);
+ offset = 0;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ chsel = I2S_READ(sc, sc->sc_cfg->txchsel);
+ chsel &= ~DA_CHSEL_OFFSET;
+ chsel |= __SHIFTIN(offset, DA_CHSEL_OFFSET);
+ I2S_WRITE(sc, sc->sc_cfg->txchsel, chsel);
+
+ chsel = I2S_READ(sc, sc->sc_cfg->rxchsel);
+ chsel &= ~DA_CHSEL_OFFSET;
+ chsel |= __SHIFTIN(offset, DA_CHSEL_OFFSET);
+ I2S_WRITE(sc, sc->sc_cfg->rxchsel, chsel);
}
fat0 &= ~(DA_FAT0_LRCP|DA_FAT0_BCP);
- if (AUDIO_DAI_POLARITY_B(pol))
- fat0 |= __SHIFTIN(DA_BCP_INVERTED, DA_FAT0_BCP);
- if (AUDIO_DAI_POLARITY_F(pol))
- fat0 |= __SHIFTIN(DA_LRCP_INVERTED, DA_FAT0_LRCP);
+ if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) {
+ if (AUDIO_DAI_POLARITY_B(pol))
+ fat0 |= __SHIFTIN(DA_BCP_INVERTED, DA_FAT0_BCP);
+ if (AUDIO_DAI_POLARITY_F(pol))
+ fat0 |= __SHIFTIN(DA_LRCP_INVERTED, DA_FAT0_LRCP);
+ } else {
+ if (AUDIO_DAI_POLARITY_B(pol))
+ fat0 |= __SHIFTIN(DA_BCP_INVERTED, DA_FAT0_BCP);
+ if (!AUDIO_DAI_POLARITY_F(pol))
+ fat0 |= __SHIFTIN(DA_LRCP_INVERTED, DA_FAT0_LRCP);
+
+ fat0 &= ~DA_FAT0_LRCK_PERIOD;
+ fat0 |= __SHIFTIN(32 - 1, DA_FAT0_LRCK_PERIOD);
+ }
switch (clk) {
case AUDIO_DAI_CLOCK_CBM_CFM:
- ctl |= DA_CTL_MS; /* codec is master */
+ if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) {
+ ctl |= DA_CTL_MS; /* codec is master */
+ } else {
+ ctl &= ~DA_CTL_BCLK_OUT;
+ ctl &= ~DA_CLK_LRCK_OUT;
+ }
break;
case AUDIO_DAI_CLOCK_CBS_CFS:
- ctl &= ~DA_CTL_MS; /* codec is slave */
+ if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) {
+ ctl &= ~DA_CTL_MS; /* codec is slave */
+ } else {
+ ctl |= DA_CTL_BCLK_OUT;
+ ctl |= DA_CLK_LRCK_OUT;
+ }
break;
default:
return EINVAL;
}
- ctl &= ~DA_CTL_PCM;
-
I2S_WRITE(sc, DA_CTL, ctl);
I2S_WRITE(sc, DA_FAT0, fat0);
@@ -628,28 +826,18 @@ static struct fdtbus_dai_controller_func
};
static int
-sunxi_i2s_clock_init(int phandle)
+sunxi_i2s_clock_init(struct sunxi_i2s_softc *sc)
{
+ const int phandle = sc->sc_phandle;
struct fdtbus_reset *rst;
struct clk *clk;
int error;
- /* Set module clock to 24.576MHz, suitable for 48 kHz sampling rates */
- clk = fdtbus_clock_get(phandle, "mod");
- if (clk == NULL) {
+ sc->sc_clk = fdtbus_clock_get(phandle, "mod");
+ if (sc->sc_clk == NULL) {
aprint_error(": couldn't find mod clock\n");
return ENXIO;
}
- error = clk_set_rate(clk, SUNXI_I2S_CLK_RATE);
- if (error != 0) {
- aprint_error(": couldn't set mod clock rate: %d\n", error);
- return error;
- }
- error = clk_enable(clk);
- if (error != 0) {
- aprint_error(": couldn't enable mod clock: %d\n", error);
- return error;
- }
/* Enable APB clock */
clk = fdtbus_clock_get(phandle, "apb");
@@ -664,7 +852,7 @@ sunxi_i2s_clock_init(int phandle)
}
/* De-assert reset */
- rst = fdtbus_reset_get(phandle, "rst");
+ rst = fdtbus_reset_get_index(phandle, 0);
if (rst == NULL) {
aprint_error(": couldn't find reset\n");
return ENXIO;
@@ -701,9 +889,6 @@ sunxi_i2s_attach(device_t parent, device
return;
}
- if (sunxi_i2s_clock_init(phandle) != 0)
- return;
-
sc->sc_dev = self;
sc->sc_phandle = phandle;
sc->sc_baseaddr = addr;
@@ -718,8 +903,13 @@ sunxi_i2s_attach(device_t parent, device
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
- if (sunxi_i2s_chan_init(sc, &sc->sc_pchan, AUMODE_PLAY, "tx") != 0 ||
- sunxi_i2s_chan_init(sc, &sc->sc_rchan, AUMODE_RECORD, "rx") != 0) {
+ if (sunxi_i2s_clock_init(sc) != 0)
+ return;
+
+ /* At least one of these needs to succeed */
+ sunxi_i2s_chan_init(sc, &sc->sc_pchan, AUMODE_PLAY, "tx");
+ sunxi_i2s_chan_init(sc, &sc->sc_rchan, AUMODE_RECORD, "rx");
+ if (sc->sc_pchan.ch_dma == NULL && sc->sc_rchan.ch_dma == NULL) {
aprint_error(": couldn't setup channels\n");
return;
}
@@ -740,24 +930,45 @@ sunxi_i2s_attach(device_t parent, device
I2S_WRITE(sc, DA_RXCNT, 0);
/* Enable */
- I2S_WRITE(sc, DA_CTL, DA_CTL_GEN | DA_CTL_SDO_EN);
+ val = I2S_READ(sc, DA_CTL);
+ val |= DA_CTL_GEN;
+ I2S_WRITE(sc, DA_CTL, val);
+ val |= DA_CTL_SDO_EN;
+ I2S_WRITE(sc, DA_CTL, val);
/* Setup channels */
I2S_WRITE(sc, sc->sc_cfg->txchmap, 0x76543210);
- I2S_WRITE(sc, sc->sc_cfg->txchsel, __SHIFTIN(1, DA_CHSEL_SEL) |
- __SHIFTIN(3, DA_CHSEL_EN));
+ val = I2S_READ(sc, sc->sc_cfg->txchsel);
+ val &= ~DA_CHSEL_EN;
+ val |= __SHIFTIN(3, DA_CHSEL_EN);
+ val &= ~DA_CHSEL_SEL;
+ val |= __SHIFTIN(1, DA_CHSEL_SEL);
+ I2S_WRITE(sc, sc->sc_cfg->txchsel, val);
I2S_WRITE(sc, sc->sc_cfg->rxchmap, 0x76543210);
- I2S_WRITE(sc, sc->sc_cfg->rxchsel, __SHIFTIN(1, DA_CHSEL_SEL) |
- __SHIFTIN(3, DA_CHSEL_EN));
+ val = I2S_READ(sc, sc->sc_cfg->rxchsel);
+ val &= ~DA_CHSEL_EN;
+ val |= __SHIFTIN(3, DA_CHSEL_EN);
+ val &= ~DA_CHSEL_SEL;
+ val |= __SHIFTIN(1, DA_CHSEL_SEL);
+ I2S_WRITE(sc, sc->sc_cfg->rxchsel, val);
+
+ if (I2S_TYPE(sc) == SUNXI_I2S_SUN8I) {
+ val = I2S_READ(sc, DA_CHCFG);
+ val &= ~DA_CHCFG_TX_SLOT_NUM;
+ val |= __SHIFTIN(1, DA_CHCFG_TX_SLOT_NUM);
+ val &= ~DA_CHCFG_RX_SLOT_NUM;
+ val |= __SHIFTIN(1, DA_CHCFG_RX_SLOT_NUM);
+ I2S_WRITE(sc, DA_CHCFG, val);
+ }
sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD;
sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE;
- sc->sc_format.validbits = 32;
- sc->sc_format.precision = 32;
+ 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] = 48000;
+ sc->sc_format.frequency[0] = SUNXI_I2S_SAMPLE_RATE;
sc->sc_dai.dai_set_sysclk = sunxi_i2s_dai_set_sysclk;
sc->sc_dai.dai_set_format = sunxi_i2s_dai_set_format;