Module Name: src
Committed By: snj
Date: Wed Aug 9 05:49:50 UTC 2017
Modified Files:
src/sys/arch/arm/sunxi [netbsd-8]: files.sunxi sun8i_h3_ccu.c
sunxi_ccu.h sunxi_ccu_nkmp.c
src/sys/arch/evbarm/conf [netbsd-8]: SUNXI
Added Files:
src/sys/arch/arm/sunxi [netbsd-8]: sun6i_dma.c sun8i_h3_codec.c
sunxi_codec.c sunxi_codec.h
Log Message:
Pull up following revision(s) (requested by jmcneill in ticket #200):
sys/arch/arm/sunxi/files.sunxi: 1.15-1.16
sys/arch/arm/sunxi/sun6i_dma.c: 1.1-1.2
sys/arch/arm/sunxi/sun8i_h3_ccu.c: 1.9-1.10
sys/arch/arm/sunxi/sun8i_h3_codec.c: 1.1-1.2
sys/arch/arm/sunxi/sunxi_ccu.h: 1.8
sys/arch/arm/sunxi/sunxi_ccu_nkmp.c: 1.5
sys/arch/arm/sunxi/sunxi_codec.c: 1.1
sys/arch/arm/sunxi/sunxi_codec.h: 1.1
sys/arch/evbarm/conf/SUNXI: 1.20-1.21
Add DMA controller driver for sun6i and later family SoCs.
--
Enable sun6idma
--
add DMA gate
--
Fix burst field encoding, and add a helper function to dump registers
from ddb.
--
Add support for H3 audio PLL and digital audio part.
--
Add support for Allwinner H3 audio codec.
--
Enable H3 audio support
--
h3_codec_pr_write: clear write mode bit after setting it; fixes an issue with
output being mutex when skipping tracks in mpg123
To generate a diff of this commit:
cvs rdiff -u -r1.12.4.4 -r1.12.4.5 src/sys/arch/arm/sunxi/files.sunxi
cvs rdiff -u -r0 -r1.2.2.2 src/sys/arch/arm/sunxi/sun6i_dma.c \
src/sys/arch/arm/sunxi/sun8i_h3_codec.c
cvs rdiff -u -r1.8.4.3 -r1.8.4.4 src/sys/arch/arm/sunxi/sun8i_h3_ccu.c
cvs rdiff -u -r1.7.4.3 -r1.7.4.4 src/sys/arch/arm/sunxi/sunxi_ccu.h
cvs rdiff -u -r1.4.4.2 -r1.4.4.3 src/sys/arch/arm/sunxi/sunxi_ccu_nkmp.c
cvs rdiff -u -r0 -r1.1.2.2 src/sys/arch/arm/sunxi/sunxi_codec.c \
src/sys/arch/arm/sunxi/sunxi_codec.h
cvs rdiff -u -r1.17.4.4 -r1.17.4.5 src/sys/arch/evbarm/conf/SUNXI
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.12.4.4 src/sys/arch/arm/sunxi/files.sunxi:1.12.4.5
--- src/sys/arch/arm/sunxi/files.sunxi:1.12.4.4 Wed Jul 26 07:32:06 2017
+++ src/sys/arch/arm/sunxi/files.sunxi Wed Aug 9 05:49:50 2017
@@ -1,4 +1,4 @@
-# $NetBSD: files.sunxi,v 1.12.4.4 2017/07/26 07:32:06 martin Exp $
+# $NetBSD: files.sunxi,v 1.12.4.5 2017/08/09 05:49:50 snj Exp $
#
# Configuration info for Allwinner sunxi family SoCs
#
@@ -107,6 +107,21 @@ device sunxiwdt: sysmon_wdog
attach sunxiwdt at fdt with sunxi_wdt
file arch/arm/sunxi/sunxi_wdt.c sunxi_wdt
+# DMA controller
+device sun6idma
+attach sun6idma at fdt with sun6i_dma
+file arch/arm/sunxi/sun6i_dma.c sun6i_dma
+
+# Audio codec
+device sunxicodec: audiobus, auconv, mulaw, aurateconv
+attach sunxicodec at fdt with sunxi_codec
+file arch/arm/sunxi/sunxi_codec.c sunxi_codec
+
+# Audio codec (analog part)
+device h3codec
+attach h3codec at fdt with h3_codec
+file arch/arm/sunxi/sun8i_h3_codec.c h3_codec needs-flag
+
# SOC parameters
defflag opt_soc.h SOC_SUNXI
defflag opt_soc.h SOC_SUN8I: SOC_SUNXI
Index: src/sys/arch/arm/sunxi/sun8i_h3_ccu.c
diff -u src/sys/arch/arm/sunxi/sun8i_h3_ccu.c:1.8.4.3 src/sys/arch/arm/sunxi/sun8i_h3_ccu.c:1.8.4.4
--- src/sys/arch/arm/sunxi/sun8i_h3_ccu.c:1.8.4.3 Tue Jul 25 02:03:16 2017
+++ src/sys/arch/arm/sunxi/sun8i_h3_ccu.c Wed Aug 9 05:49:50 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: sun8i_h3_ccu.c,v 1.8.4.3 2017/07/25 02:03:16 snj Exp $ */
+/* $NetBSD: sun8i_h3_ccu.c,v 1.8.4.4 2017/08/09 05:49:50 snj Exp $ */
/*-
* Copyright (c) 2017 Jared McNeill <[email protected]>
@@ -29,7 +29,7 @@
#include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: sun8i_h3_ccu.c,v 1.8.4.3 2017/07/25 02:03:16 snj Exp $");
+__KERNEL_RCSID(1, "$NetBSD: sun8i_h3_ccu.c,v 1.8.4.4 2017/08/09 05:49:50 snj Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -41,6 +41,7 @@ __KERNEL_RCSID(1, "$NetBSD: sun8i_h3_ccu
#include <arm/sunxi/sunxi_ccu.h>
#include <arm/sunxi/sun8i_h3_ccu.h>
+#define PLL_AUDIO_CTRL_REG 0x008
#define PLL_PERIPH0_CTRL_REG 0x028
#define AHB1_APB1_CFG_REG 0x054
#define APB2_CFG_REG 0x058
@@ -55,6 +56,7 @@ __KERNEL_RCSID(1, "$NetBSD: sun8i_h3_ccu
#define SDMMC2_CLK_REG 0x090
#define USBPHY_CFG_REG 0x0cc
#define MBUS_RST_REG 0x0fc
+#define AC_DIG_CLK_REG 0x140
#define BUS_SOFT_RST_REG0 0x2c0
#define BUS_SOFT_RST_REG1 0x2c4
#define BUS_SOFT_RST_REG2 0x2c8
@@ -141,6 +143,11 @@ static const char *apb1_parents[] = { "a
static const char *apb2_parents[] = { "losc", "hosc", "pll_periph0" };
static const char *mod_parents[] = { "hosc", "pll_periph0", "pll_periph1" };
+static const struct sunxi_ccu_nkmp_tbl sunx8_h3_ac_dig_table[] = {
+ { 24576000, 13, 0, 0, 13 },
+ { 0 }
+};
+
static struct sunxi_ccu_clk sun8i_h3_ccu_clks[] = {
SUNXI_CCU_NKMP(H3_CLK_PLL_PERIPH0, "pll_periph0", "hosc",
PLL_PERIPH0_CTRL_REG, /* reg */
@@ -151,6 +158,17 @@ static struct sunxi_ccu_clk sun8i_h3_ccu
__BIT(31), /* enable */
SUNXI_CCU_NKMP_DIVIDE_BY_TWO),
+ SUNXI_CCU_NKMP_TABLE(H3_CLK_PLL_AUDIO_BASE, "pll_audio", "hosc",
+ PLL_AUDIO_CTRL_REG, /* reg */
+ __BITS(14,8), /* n */
+ 0, /* k */
+ __BITS(4,0), /* m */
+ __BITS(19,16), /* p */
+ __BIT(31), /* enable */
+ __BIT(28), /* lock */
+ sunx8_h3_ac_dig_table, /* table */
+ 0),
+
SUNXI_CCU_PREDIV(H3_CLK_AHB1, "ahb1", ahb1_parents,
AHB1_APB1_CFG_REG, /* reg */
__BITS(7,6), /* prediv */
@@ -203,6 +221,11 @@ static struct sunxi_ccu_clk sun8i_h3_ccu
SUNXI_CCU_PHASE(H3_CLK_MMC2_OUTPUT, "mmc2_output", "mmc2",
SDMMC2_CLK_REG, __BITS(10,8)),
+ SUNXI_CCU_GATE(H3_CLK_AC_DIG, "ac_dig", "pll_audio",
+ AC_DIG_CLK_REG, 31),
+
+ SUNXI_CCU_GATE(H3_CLK_BUS_DMA, "bus-dma", "ahb1",
+ BUS_CLK_GATING_REG0, 6),
SUNXI_CCU_GATE(H3_CLK_BUS_MMC0, "bus-mmc0", "ahb1",
BUS_CLK_GATING_REG0, 8),
SUNXI_CCU_GATE(H3_CLK_BUS_MMC1, "bus-mmc1", "ahb1",
@@ -230,6 +253,8 @@ static struct sunxi_ccu_clk sun8i_h3_ccu
SUNXI_CCU_GATE(H3_CLK_BUS_OHCI3, "bus-ohci3", "ahb2",
BUS_CLK_GATING_REG0, 31),
+ SUNXI_CCU_GATE(H3_CLK_BUS_CODEC, "bus-codec", "apb1",
+ BUS_CLK_GATING_REG2, 0),
SUNXI_CCU_GATE(H3_CLK_BUS_PIO, "bus-pio", "apb1",
BUS_CLK_GATING_REG2, 5),
Index: src/sys/arch/arm/sunxi/sunxi_ccu.h
diff -u src/sys/arch/arm/sunxi/sunxi_ccu.h:1.7.4.3 src/sys/arch/arm/sunxi/sunxi_ccu.h:1.7.4.4
--- src/sys/arch/arm/sunxi/sunxi_ccu.h:1.7.4.3 Tue Jul 25 02:03:16 2017
+++ src/sys/arch/arm/sunxi/sunxi_ccu.h Wed Aug 9 05:49:50 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_ccu.h,v 1.7.4.3 2017/07/25 02:03:16 snj Exp $ */
+/* $NetBSD: sunxi_ccu.h,v 1.7.4.4 2017/08/09 05:49:50 snj Exp $ */
/*-
* Copyright (c) 2017 Jared McNeill <[email protected]>
@@ -79,6 +79,7 @@ const char *sunxi_ccu_gate_get_parent(st
[_id] = { \
.type = SUNXI_CCU_GATE, \
.base.name = (_name), \
+ .base.flags = CLK_SET_RATE_PARENT, \
.u.gate.parent = (_pname), \
.u.gate.reg = (_reg), \
.u.gate.mask = __BIT(_bit), \
@@ -86,6 +87,14 @@ const char *sunxi_ccu_gate_get_parent(st
.get_parent = sunxi_ccu_gate_get_parent, \
}
+struct sunxi_ccu_nkmp_tbl {
+ u_int rate;
+ uint32_t n;
+ uint32_t k;
+ uint32_t m;
+ uint32_t p;
+};
+
struct sunxi_ccu_nkmp {
bus_size_t reg;
const char *parent;
@@ -96,6 +105,7 @@ struct sunxi_ccu_nkmp {
uint32_t lock;
uint32_t enable;
uint32_t flags;
+ const struct sunxi_ccu_nkmp_tbl *table;
#define SUNXI_CCU_NKMP_DIVIDE_BY_TWO __BIT(0)
#define SUNXI_CCU_NKMP_FACTOR_N_EXACT __BIT(1)
};
@@ -109,8 +119,8 @@ int sunxi_ccu_nkmp_set_rate(struct sunxi
const char *sunxi_ccu_nkmp_get_parent(struct sunxi_ccu_softc *,
struct sunxi_ccu_clk *);
-#define SUNXI_CCU_NKMP(_id, _name, _parent, _reg, _n, _k, _m, \
- _p, _enable, _flags) \
+#define SUNXI_CCU_NKMP_TABLE(_id, _name, _parent, _reg, _n, _k, _m, \
+ _p, _enable, _lock, _tbl, _flags) \
[_id] = { \
.type = SUNXI_CCU_NKMP, \
.base.name = (_name), \
@@ -122,12 +132,20 @@ const char *sunxi_ccu_nkmp_get_parent(st
.u.nkmp.p = (_p), \
.u.nkmp.enable = (_enable), \
.u.nkmp.flags = (_flags), \
+ .u.nkmp.lock = (_lock), \
+ .u.nkmp.table = (_tbl), \
.enable = sunxi_ccu_nkmp_enable, \
.get_rate = sunxi_ccu_nkmp_get_rate, \
.set_rate = sunxi_ccu_nkmp_set_rate, \
.get_parent = sunxi_ccu_nkmp_get_parent, \
}
+#define SUNXI_CCU_NKMP(_id, _name, _parent, _reg, _n, _k, _m, \
+ _p, _enable, _flags) \
+ SUNXI_CCU_NKMP_TABLE(_id, _name, _parent, _reg, _n, _k, _m, \
+ _p, _enable, 0, NULL, _flags)
+
+
struct sunxi_ccu_nm {
bus_size_t reg;
const char **parents;
Index: src/sys/arch/arm/sunxi/sunxi_ccu_nkmp.c
diff -u src/sys/arch/arm/sunxi/sunxi_ccu_nkmp.c:1.4.4.2 src/sys/arch/arm/sunxi/sunxi_ccu_nkmp.c:1.4.4.3
--- src/sys/arch/arm/sunxi/sunxi_ccu_nkmp.c:1.4.4.2 Tue Jul 18 19:13:08 2017
+++ src/sys/arch/arm/sunxi/sunxi_ccu_nkmp.c Wed Aug 9 05:49:50 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_ccu_nkmp.c,v 1.4.4.2 2017/07/18 19:13:08 snj Exp $ */
+/* $NetBSD: sunxi_ccu_nkmp.c,v 1.4.4.3 2017/08/09 05:49:50 snj Exp $ */
/*-
* Copyright (c) 2017 Jared McNeill <[email protected]>
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_nkmp.c,v 1.4.4.2 2017/07/18 19:13:08 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_nkmp.c,v 1.4.4.3 2017/08/09 05:49:50 snj Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -42,6 +42,7 @@ sunxi_ccu_nkmp_enable(struct sunxi_ccu_s
{
struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp;
uint32_t val;
+ int retry;
KASSERT(clk->type == SUNXI_CCU_NKMP);
@@ -55,6 +56,17 @@ sunxi_ccu_nkmp_enable(struct sunxi_ccu_s
val &= ~nkmp->enable;
CCU_WRITE(sc, nkmp->reg, val);
+ if (enable && nkmp->lock) {
+ for (retry = 1000; retry > 0; retry--) {
+ val = CCU_READ(sc, nkmp->reg);
+ if (val & nkmp->lock)
+ break;
+ delay(100);
+ }
+ if (retry == 0)
+ return ETIMEDOUT;
+ }
+
return 0;
}
@@ -115,7 +127,41 @@ int
sunxi_ccu_nkmp_set_rate(struct sunxi_ccu_softc *sc,
struct sunxi_ccu_clk *clk, u_int rate)
{
- return EIO;
+ struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp;
+ const struct sunxi_ccu_nkmp_tbl *tab;
+ uint32_t val;
+
+ KASSERT(clk->type == SUNXI_CCU_NKMP);
+
+ if (nkmp->table == NULL || rate == 0)
+ return EIO;
+
+ for (tab = nkmp->table; tab->rate > 0; tab++)
+ if (tab->rate == rate)
+ break;
+ if (tab->rate == 0)
+ return EINVAL;
+
+ val = CCU_READ(sc, nkmp->reg);
+ if (nkmp->n) {
+ val &= ~nkmp->n;
+ val |= __SHIFTIN(tab->n, nkmp->n);
+ }
+ if (nkmp->k) {
+ val &= ~nkmp->k;
+ val |= __SHIFTIN(tab->k, nkmp->k);
+ }
+ if (nkmp->m) {
+ val &= ~nkmp->m;
+ val |= __SHIFTIN(tab->m, nkmp->m);
+ }
+ if (nkmp->p) {
+ val &= ~nkmp->p;
+ val |= __SHIFTIN(tab->p, nkmp->p);
+ }
+ CCU_WRITE(sc, nkmp->reg, val);
+
+ return 0;
}
const char *
Index: src/sys/arch/evbarm/conf/SUNXI
diff -u src/sys/arch/evbarm/conf/SUNXI:1.17.4.4 src/sys/arch/evbarm/conf/SUNXI:1.17.4.5
--- src/sys/arch/evbarm/conf/SUNXI:1.17.4.4 Wed Jul 26 07:32:06 2017
+++ src/sys/arch/evbarm/conf/SUNXI Wed Aug 9 05:49:50 2017
@@ -1,5 +1,5 @@
#
-# $NetBSD: SUNXI,v 1.17.4.4 2017/07/26 07:32:06 martin Exp $
+# $NetBSD: SUNXI,v 1.17.4.5 2017/08/09 05:49:50 snj Exp $
#
# Allwinner sunxi family
#
@@ -89,6 +89,7 @@ armgic0 at gic?
# Memory controller
# DMA controller
+sun6idma* at fdt? # DMA controller
# Clock and Reset controller
@@ -114,6 +115,12 @@ iic* at i2cbus?
# RTC
sunxirtc* at fdt? # RTC
+# Audio
+sunxicodec* at fdt? # Audio codec
+h3codec* at fdt? # H3 audio codec (analog part)
+audio* at audiobus?
+spkr* at audio?
+
# SDMMC
sunximmc* at fdt? # SDMMC
sdmmc* at sunximmc?
Added files:
Index: src/sys/arch/arm/sunxi/sun6i_dma.c
diff -u /dev/null src/sys/arch/arm/sunxi/sun6i_dma.c:1.2.2.2
--- /dev/null Wed Aug 9 05:49:50 2017
+++ src/sys/arch/arm/sunxi/sun6i_dma.c Wed Aug 9 05:49:50 2017
@@ -0,0 +1,453 @@
+/* $NetBSD: sun6i_dma.c,v 1.2.2.2 2017/08/09 05:49:50 snj Exp $ */
+
+/*-
+ * Copyright (c) 2014-2017 Jared 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 "opt_ddb.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: sun6i_dma.c,v 1.2.2.2 2017/08/09 05:49:50 snj Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/bitops.h>
+#include <sys/kmem.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#define DMA_IRQ_EN_REG0_REG 0x0000
+#define DMA_IRQ_EN_REG1_REG 0x0004
+#define DMA_IRQ_EN_REG0_QUEUE_IRQ_EN(n) __BIT(n * 4 + 2)
+#define DMA_IRQ_EN_REG0_PKG_IRQ_EN(n) __BIT(n * 4 + 1)
+#define DMA_IRQ_EN_REG0_HLAF_IRQ_EN(n) __BIT(n * 4 + 0)
+#define DMA_IRQ_EN_REG1_QUEUE_IRQ_EN(n) __BIT((n - 8) * 4 + 2)
+#define DMA_IRQ_EN_REG1_PKG_IRQ_EN(n) __BIT((n - 8) * 4 + 1)
+#define DMA_IRQ_EN_REG1_HLAF_IRQ_EN(n) __BIT((n - 8) * 4 + 0)
+#define DMA_IRQ_PEND_REG0_REG 0x0010
+#define DMA_IRQ_PEND_REG1_REG 0x0014
+#define DMA_IRQ_QUEUE_MASK 0x4444444444444444ULL
+#define DMA_IRQ_PKG_MASK 0x2222222222222222ULL
+#define DMA_IRQ_HF_MASK 0x1111111111111111ULL
+#define DMA_STA_REG 0x0030
+#define DMA_EN_REG(n) (0x0100 + (n) * 0x40 + 0x00)
+#define DMA_EN_EN __BIT(0)
+#define DMA_PAU_REG(n) (0x0100 + (n) * 0x40 + 0x04)
+#define DMA_PAU_PAUSE __BIT(0)
+#define DMA_START_ADDR_REG(n) (0x0100 + (n) * 0x40 + 0x08)
+#define DMA_CFG_REG(n) (0x0100 + (n) * 0x40 + 0x0C)
+#define DMA_CFG_DEST_DATA_WIDTH __BITS(26,25)
+#define DMA_CFG_DATA_WIDTH(n) ((n) >> 4)
+#define DMA_CFG_DEST_BST_LEN __BITS(24,23)
+#define DMA_CFG_BST_LEN(n) ((n) == 1 ? 0 : (((n) >> 3) + 1))
+#define DMA_CFG_DEST_ADDR_MODE __BITS(22,21)
+#define DMA_CFG_ADDR_MODE_LINEAR 0
+#define DMA_CFG_ADDR_MODE_IO 1
+#define DMA_CFG_DEST_DRQ_TYPE __BITS(20,16)
+#define DMA_CFG_DRQ_TYPE_SDRAM 1
+#define DMA_CFG_SRC_DATA_WIDTH __BITS(10,9)
+#define DMA_CFG_SRC_BST_LEN __BITS(8,7)
+#define DMA_CFG_SRC_ADDR_MODE __BITS(6,5)
+#define DMA_CFG_SRC_DRQ_TYPE __BITS(4,0)
+#define DMA_CUR_SRC_REG(n) (0x0100 + (n) * 0x40 + 0x10)
+#define DMA_CUR_DEST_REG(n) (0x0100 + (n) * 0x40 + 0x14)
+#define DMA_BCNT_LEFT_REG(n) (0x0100 + (n) * 0x40 + 0x18)
+#define DMA_PARA_REG(n) (0x0100 + (n) * 0x40 + 0x1C)
+#define DMA_PARA_DATA_BLK_SIZE __BITS(15,8)
+#define DMA_PARA_WAIT_CYC __BITS(7,0)
+
+struct sun6idma_desc {
+ uint32_t dma_config;
+ uint32_t dma_srcaddr;
+ uint32_t dma_dstaddr;
+ uint32_t dma_bcnt;
+ uint32_t dma_para;
+ uint32_t dma_next;
+#define DMA_NULL 0xfffff800
+};
+
+static const struct of_compat_data compat_data[] = {
+ { "allwinner,sun6i-a31-dma", 16 },
+ { "allwinner,sun8i-a83t-dma", 8 },
+ { "allwinner,sun8i-h3-dma", 12 },
+ { NULL }
+};
+
+struct sun6idma_channel {
+ uint8_t ch_index;
+ void (*ch_callback)(void *);
+ void *ch_callbackarg;
+ u_int ch_portid;
+
+ bus_dma_segment_t ch_dmasegs[1];
+ bus_dmamap_t ch_dmamap;
+ void *ch_dmadesc;
+ bus_size_t ch_dmadesclen;
+};
+
+struct sun6idma_softc {
+ device_t sc_dev;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ bus_dma_tag_t sc_dmat;
+ int sc_phandle;
+ void *sc_ih;
+
+ kmutex_t sc_lock;
+
+ struct sun6idma_channel *sc_chan;
+ u_int sc_nchan;
+};
+
+#define DMA_READ(sc, reg) \
+ bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define DMA_WRITE(sc, reg, val) \
+ bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+static void *
+sun6idma_acquire(device_t dev, const void *data, size_t len,
+ void (*cb)(void *), void *cbarg)
+{
+ struct sun6idma_softc *sc = device_private(dev);
+ struct sun6idma_channel *ch = NULL;
+ uint32_t irqen;
+ uint8_t index;
+
+ if (len != 4)
+ return NULL;
+
+ const u_int portid = be32dec(data);
+ if (portid > __SHIFTOUT_MASK(DMA_CFG_SRC_DRQ_TYPE))
+ return NULL;
+
+ mutex_enter(&sc->sc_lock);
+
+ for (index = 0; index < sc->sc_nchan; index++) {
+ if (sc->sc_chan[index].ch_callback == NULL) {
+ ch = &sc->sc_chan[index];
+ ch->ch_callback = cb;
+ ch->ch_callbackarg = cbarg;
+ ch->ch_portid = portid;
+
+ irqen = DMA_READ(sc, index < 8 ?
+ DMA_IRQ_EN_REG0_REG :
+ DMA_IRQ_EN_REG1_REG);
+ irqen |= (index < 8 ?
+ DMA_IRQ_EN_REG0_PKG_IRQ_EN(index) :
+ DMA_IRQ_EN_REG1_PKG_IRQ_EN(index));
+ DMA_WRITE(sc, index < 8 ?
+ DMA_IRQ_EN_REG0_REG :
+ DMA_IRQ_EN_REG1_REG, irqen);
+
+ break;
+ }
+ }
+
+ mutex_exit(&sc->sc_lock);
+
+ return ch;
+}
+
+static void
+sun6idma_release(device_t dev, void *priv)
+{
+ struct sun6idma_softc *sc = device_private(dev);
+ struct sun6idma_channel *ch = priv;
+ uint32_t irqen;
+ uint8_t index = ch->ch_index;
+
+ mutex_enter(&sc->sc_lock);
+
+ irqen = DMA_READ(sc, index < 8 ?
+ DMA_IRQ_EN_REG0_REG :
+ DMA_IRQ_EN_REG1_REG);
+ irqen &= ~(index < 8 ?
+ DMA_IRQ_EN_REG0_PKG_IRQ_EN(index) :
+ DMA_IRQ_EN_REG1_PKG_IRQ_EN(index));
+ DMA_WRITE(sc, index < 8 ?
+ DMA_IRQ_EN_REG0_REG :
+ DMA_IRQ_EN_REG1_REG, irqen);
+
+ ch->ch_callback = NULL;
+ ch->ch_callbackarg = NULL;
+
+ mutex_exit(&sc->sc_lock);
+}
+
+static int
+sun6idma_transfer(device_t dev, void *priv, struct fdtbus_dma_req *req)
+{
+ struct sun6idma_softc *sc = device_private(dev);
+ struct sun6idma_channel *ch = priv;
+ struct sun6idma_desc *desc = ch->ch_dmadesc;
+ uint32_t src, dst, len, cfg, mem_cfg, dev_cfg;
+ uint32_t mem_width, dev_width, mem_burst, dev_burst;
+
+ if (req->dreq_nsegs != 1)
+ return EINVAL;
+
+ mem_width = DMA_CFG_DATA_WIDTH(req->dreq_mem_opt.opt_bus_width);
+ dev_width = DMA_CFG_DATA_WIDTH(req->dreq_dev_opt.opt_bus_width);
+ mem_burst = DMA_CFG_BST_LEN(req->dreq_mem_opt.opt_burst_len);
+ dev_burst = DMA_CFG_BST_LEN(req->dreq_dev_opt.opt_burst_len);
+
+ mem_cfg = __SHIFTIN(mem_width, DMA_CFG_SRC_DATA_WIDTH) |
+ __SHIFTIN(mem_burst, DMA_CFG_SRC_BST_LEN) |
+ __SHIFTIN(DMA_CFG_ADDR_MODE_LINEAR, DMA_CFG_SRC_ADDR_MODE) |
+ __SHIFTIN(DMA_CFG_DRQ_TYPE_SDRAM, DMA_CFG_SRC_DRQ_TYPE);
+ dev_cfg = __SHIFTIN(dev_width, DMA_CFG_SRC_DATA_WIDTH) |
+ __SHIFTIN(dev_burst, DMA_CFG_SRC_BST_LEN) |
+ __SHIFTIN(DMA_CFG_ADDR_MODE_IO, DMA_CFG_SRC_ADDR_MODE) |
+ __SHIFTIN(ch->ch_portid, DMA_CFG_SRC_DRQ_TYPE);
+
+ if (req->dreq_dir == FDT_DMA_READ) {
+ src = req->dreq_dev_phys;
+ dst = req->dreq_segs[0].ds_addr;
+ cfg = mem_cfg << 16 | dev_cfg;
+ } else {
+ src = req->dreq_segs[0].ds_addr;
+ dst = req->dreq_dev_phys;
+ cfg = dev_cfg << 16 | mem_cfg;
+ }
+ len = req->dreq_segs[0].ds_len;
+
+ desc->dma_config = htole32(cfg);
+ desc->dma_srcaddr = htole32(src);
+ desc->dma_dstaddr = htole32(dst);
+ desc->dma_bcnt = htole32(len);
+ desc->dma_para = htole32(0);
+ desc->dma_next = htole32(DMA_NULL);
+
+ bus_dmamap_sync(sc->sc_dmat, ch->ch_dmamap, 0, ch->ch_dmadesclen,
+ BUS_DMASYNC_PREWRITE);
+
+ DMA_WRITE(sc, DMA_START_ADDR_REG(ch->ch_index),
+ ch->ch_dmamap->dm_segs[0].ds_addr);
+ DMA_WRITE(sc, DMA_EN_REG(ch->ch_index), DMA_EN_EN);
+
+ if ((DMA_READ(sc, DMA_EN_REG(ch->ch_index)) & DMA_EN_EN) == 0) {
+ aprint_error_dev(sc->sc_dev,
+ "DMA Channel %u failed to start\n", ch->ch_index);
+ return EIO;
+ }
+
+ return 0;
+}
+
+static void
+sun6idma_halt(device_t dev, void *priv)
+{
+ struct sun6idma_softc *sc = device_private(dev);
+ struct sun6idma_channel *ch = priv;
+
+ DMA_WRITE(sc, DMA_EN_REG(ch->ch_index), 0);
+}
+
+static const struct fdtbus_dma_controller_func sun6idma_funcs = {
+ .acquire = sun6idma_acquire,
+ .release = sun6idma_release,
+ .transfer = sun6idma_transfer,
+ .halt = sun6idma_halt
+};
+
+static int
+sun6idma_intr(void *priv)
+{
+ struct sun6idma_softc *sc = priv;
+ uint32_t pend0, pend1, bit;
+ uint64_t pend, mask;
+ uint8_t index;
+
+ pend0 = DMA_READ(sc, DMA_IRQ_PEND_REG0_REG);
+ pend1 = DMA_READ(sc, DMA_IRQ_PEND_REG1_REG);
+ if (!pend0 && !pend1)
+ return 0;
+
+ DMA_WRITE(sc, DMA_IRQ_PEND_REG0_REG, pend0);
+ DMA_WRITE(sc, DMA_IRQ_PEND_REG1_REG, pend1);
+
+ pend = pend0 | ((uint64_t)pend1 << 32);
+
+ while ((bit = ffs64(pend & DMA_IRQ_PKG_MASK)) != 0) {
+ mask = __BIT(bit - 1);
+ pend &= ~mask;
+ index = (bit - 1) / 4;
+
+ if (sc->sc_chan[index].ch_callback == NULL)
+ continue;
+ sc->sc_chan[index].ch_callback(
+ sc->sc_chan[index].ch_callbackarg);
+ }
+
+ return 1;
+}
+
+static int
+sun6idma_match(device_t parent, cfdata_t cf, void *aux)
+{
+ struct fdt_attach_args * const faa = aux;
+
+ return of_match_compat_data(faa->faa_phandle, compat_data);
+}
+
+static void
+sun6idma_attach(device_t parent, device_t self, void *aux)
+{
+ struct sun6idma_softc * const sc = device_private(self);
+ struct fdt_attach_args * const faa = aux;
+ const int phandle = faa->faa_phandle;
+ const size_t desclen = sizeof(struct sun6idma_desc);
+ struct fdtbus_reset *rst;
+ struct clk *clk;
+ char intrstr[128];
+ bus_addr_t addr;
+ bus_size_t size;
+ int error, nsegs;
+ u_int index;
+
+ if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+ aprint_error(": couldn't get registers\n");
+ return;
+ }
+
+ if ((clk = fdtbus_clock_get_index(phandle, 0)) == NULL ||
+ clk_enable(clk) != 0) {
+ aprint_error(": couldn't enable clock\n");
+ return;
+ }
+ if ((rst = fdtbus_reset_get_index(phandle, 0)) == NULL ||
+ fdtbus_reset_deassert(rst) != 0) {
+ aprint_error(": couldn't de-assert reset\n");
+ return;
+ }
+
+ sc->sc_dev = self;
+ sc->sc_phandle = phandle;
+ sc->sc_dmat = faa->faa_dmat;
+ sc->sc_bst = faa->faa_bst;
+ if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
+ aprint_error(": couldn't map registers\n");
+ return;
+ }
+ mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SCHED);
+
+ if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
+ aprint_error(": failed to decode interrupt\n");
+ return;
+ }
+
+ sc->sc_nchan = of_search_compatible(phandle, compat_data)->data;
+ sc->sc_chan = kmem_alloc(sizeof(*sc->sc_chan) * sc->sc_nchan, KM_SLEEP);
+
+ aprint_naive("\n");
+ aprint_normal(": DMA controller (%u channels)\n", sc->sc_nchan);
+
+ DMA_WRITE(sc, DMA_IRQ_EN_REG0_REG, 0);
+ DMA_WRITE(sc, DMA_IRQ_EN_REG1_REG, 0);
+ DMA_WRITE(sc, DMA_IRQ_PEND_REG0_REG, ~0);
+ DMA_WRITE(sc, DMA_IRQ_PEND_REG1_REG, ~0);
+
+ for (index = 0; index < sc->sc_nchan; index++) {
+ struct sun6idma_channel *ch = &sc->sc_chan[index];
+ ch->ch_index = index;
+ ch->ch_callback = NULL;
+ ch->ch_callbackarg = NULL;
+ ch->ch_dmadesclen = desclen;
+
+ error = bus_dmamem_alloc(sc->sc_dmat, desclen, 0, 0,
+ ch->ch_dmasegs, 1, &nsegs, BUS_DMA_WAITOK);
+ if (error)
+ panic("bus_dmamem_alloc failed: %d", error);
+ error = bus_dmamem_map(sc->sc_dmat, ch->ch_dmasegs, nsegs,
+ desclen, &ch->ch_dmadesc, BUS_DMA_WAITOK);
+ if (error)
+ panic("bus_dmamem_map failed: %d", error);
+ error = bus_dmamap_create(sc->sc_dmat, desclen, 1, desclen, 0,
+ BUS_DMA_WAITOK, &ch->ch_dmamap);
+ if (error)
+ panic("bus_dmamap_create failed: %d", error);
+ error = bus_dmamap_load(sc->sc_dmat, ch->ch_dmamap,
+ ch->ch_dmadesc, desclen, NULL, BUS_DMA_WAITOK);
+ if (error)
+ panic("bus_dmamap_load failed: %d", error);
+
+ DMA_WRITE(sc, DMA_EN_REG(index), 0);
+ }
+
+ sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_SCHED, FDT_INTR_MPSAFE,
+ sun6idma_intr, sc);
+ if (sc->sc_ih == NULL) {
+ aprint_error_dev(sc->sc_dev,
+ "couldn't establish interrupt on %s\n", intrstr);
+ return;
+ }
+ aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr);
+
+ fdtbus_register_dma_controller(self, phandle, &sun6idma_funcs);
+}
+
+CFATTACH_DECL_NEW(sun6i_dma, sizeof(struct sun6idma_softc),
+ sun6idma_match, sun6idma_attach, NULL, NULL);
+
+#ifdef DDB
+void sun6idma_dump(void);
+
+void
+sun6idma_dump(void)
+{
+ struct sun6idma_softc *sc;
+ device_t dev;
+ u_int index;
+
+ dev = device_find_by_driver_unit("sun6idma", 0);
+ if (dev == NULL)
+ return;
+ sc = device_private(dev);
+
+ device_printf(dev, "DMA_IRQ_EN_REG0_REG: %08x\n", DMA_READ(sc, DMA_IRQ_EN_REG0_REG));
+ device_printf(dev, "DMA_IRQ_EN_REG1_REG: %08x\n", DMA_READ(sc, DMA_IRQ_EN_REG1_REG));
+ device_printf(dev, "DMA_IRQ_PEND_REG0_REG: %08x\n", DMA_READ(sc, DMA_IRQ_PEND_REG0_REG));
+ device_printf(dev, "DMA_IRQ_PEND_REG1_REG: %08x\n", DMA_READ(sc, DMA_IRQ_PEND_REG1_REG));
+ device_printf(dev, "DMA_STA_REG: %08x\n", DMA_READ(sc, DMA_STA_REG));
+
+ for (index = 0; index < sc->sc_nchan; index++) {
+ struct sun6idma_channel *ch = &sc->sc_chan[index];
+ if (ch->ch_callback == NULL)
+ continue;
+ device_printf(dev, " %2d: DMA_EN_REG: %08x\n", index, DMA_READ(sc, DMA_EN_REG(index)));
+ device_printf(dev, " %2d: DMA_PAU_REG: %08x\n", index, DMA_READ(sc, DMA_PAU_REG(index)));
+ device_printf(dev, " %2d: DMA_START_ADDR_REG: %08x\n", index, DMA_READ(sc, DMA_START_ADDR_REG(index)));
+ device_printf(dev, " %2d: DMA_CFG_REG: %08x\n", index, DMA_READ(sc, DMA_CFG_REG(index)));
+ device_printf(dev, " %2d: DMA_CUR_SRC_REG: %08x\n", index, DMA_READ(sc, DMA_CUR_SRC_REG(index)));
+ device_printf(dev, " %2d: DMA_CUR_DEST_REG: %08x\n", index, DMA_READ(sc, DMA_CUR_DEST_REG(index)));
+ device_printf(dev, " %2d: DMA_BCNT_LEFT_REG: %08x\n", index, DMA_READ(sc, DMA_BCNT_LEFT_REG(index)));
+ device_printf(dev, " %2d: DMA_PARA_REG: %08x\n", index, DMA_READ(sc, DMA_PARA_REG(index)));
+ }
+}
+#endif
Index: src/sys/arch/arm/sunxi/sun8i_h3_codec.c
diff -u /dev/null src/sys/arch/arm/sunxi/sun8i_h3_codec.c:1.2.2.2
--- /dev/null Wed Aug 9 05:49:50 2017
+++ src/sys/arch/arm/sunxi/sun8i_h3_codec.c Wed Aug 9 05:49:50 2017
@@ -0,0 +1,475 @@
+/* $NetBSD: sun8i_h3_codec.c,v 1.2.2.2 2017/08/09 05:49:50 snj Exp $ */
+
+/*-
+ * Copyright (c) 2014-2017 Jared 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: sun8i_h3_codec.c,v 1.2.2.2 2017/08/09 05:49:50 snj Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/device.h>
+#include <sys/kmem.h>
+#include <sys/bitops.h>
+
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+
+#include <arm/sunxi/sunxi_codec.h>
+
+#define H3_PR_CFG 0x00
+#define H3_AC_PR_RW __BIT(24)
+#define H3_AC_PR_RST __BIT(18)
+#define H3_AC_PR_ADDR __BITS(20,16)
+#define H3_ACDA_PR_WDAT __BITS(15,8)
+#define H3_ACDA_PR_RDAT __BITS(7,0)
+
+#define H3_LOMIXSC 0x01
+#define H3_LOMIXSC_LDAC __BIT(1)
+#define H3_ROMIXSC 0x02
+#define H3_ROMIXSC_RDAC __BIT(1)
+#define H3_DAC_PA_SRC 0x03
+#define H3_DACAREN __BIT(7)
+#define H3_DACALEN __BIT(6)
+#define H3_RMIXEN __BIT(5)
+#define H3_LMIXEN __BIT(4)
+#define H3_LINEIN_GCTR 0x05
+#define H3_LINEING __BITS(6,4)
+#define H3_MIC_GCTR 0x06
+#define H3_MIC1_GAIN __BITS(6,4)
+#define H3_MIC2_GAIN __BITS(2,0)
+#define H3_PAEN_CTR 0x07
+#define H3_LINEOUTEN __BIT(7)
+#define H3_LINEOUT_VOLC 0x09
+#define H3_LINEOUTVOL __BITS(7,3)
+#define H3_MIC2G_LINEOUT_CTR 0x0a
+#define H3_LINEOUT_LSEL __BIT(3)
+#define H3_LINEOUT_RSEL __BIT(2)
+#define H3_LADCMIXSC 0x0c
+#define H3_RADCMIXSC 0x0d
+#define H3_ADCMIXSC_MIC1 __BIT(6)
+#define H3_ADCMIXSC_MIC2 __BIT(5)
+#define H3_ADCMIXSC_LINEIN __BIT(2)
+#define H3_ADCMIXSC_OMIXER __BITS(1,0)
+#define H3_ADC_AP_EN 0x0f
+#define H3_ADCREN __BIT(7)
+#define H3_ADCLEN __BIT(6)
+#define H3_ADCG __BITS(2,0)
+
+struct h3_codec_softc {
+ device_t sc_dev;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ int sc_phandle;
+};
+
+enum h3_codec_mixer_ctrl {
+ H3_CODEC_OUTPUT_CLASS,
+ H3_CODEC_INPUT_CLASS,
+ H3_CODEC_RECORD_CLASS,
+
+ H3_CODEC_OUTPUT_MASTER_VOLUME,
+ H3_CODEC_INPUT_DAC_VOLUME,
+ H3_CODEC_INPUT_LINEIN_VOLUME,
+ H3_CODEC_INPUT_MIC1_VOLUME,
+ H3_CODEC_INPUT_MIC2_VOLUME,
+ H3_CODEC_RECORD_AGC_VOLUME,
+ H3_CODEC_RECORD_SOURCE,
+
+ H3_CODEC_MIXER_CTRL_LAST
+};
+
+static const struct h3_codec_mixer {
+ const char * name;
+ enum h3_codec_mixer_ctrl mixer_class;
+ u_int reg;
+ u_int mask;
+} h3_codec_mixers[H3_CODEC_MIXER_CTRL_LAST] = {
+ [H3_CODEC_OUTPUT_MASTER_VOLUME] = { AudioNmaster,
+ H3_CODEC_OUTPUT_CLASS, H3_LINEOUT_VOLC, H3_LINEOUTVOL },
+ [H3_CODEC_INPUT_DAC_VOLUME] = { AudioNdac,
+ H3_CODEC_INPUT_CLASS, H3_LINEOUT_VOLC, H3_LINEOUTVOL },
+ [H3_CODEC_INPUT_LINEIN_VOLUME] = { AudioNline,
+ H3_CODEC_INPUT_CLASS, H3_LINEIN_GCTR, H3_LINEING },
+ [H3_CODEC_INPUT_MIC1_VOLUME] = { "mic1",
+ H3_CODEC_INPUT_CLASS, H3_MIC_GCTR, H3_MIC1_GAIN },
+ [H3_CODEC_INPUT_MIC2_VOLUME] = { "mic2",
+ H3_CODEC_INPUT_CLASS, H3_MIC_GCTR, H3_MIC2_GAIN },
+ [H3_CODEC_RECORD_AGC_VOLUME] = { AudioNagc,
+ H3_CODEC_RECORD_CLASS, H3_ADC_AP_EN, H3_ADCG },
+};
+
+#define RD4(sc, reg) \
+ bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define WR4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+static struct h3_codec_softc *
+h3_codec_find(int phandle)
+{
+ struct h3_codec_softc *csc;
+ device_t dev;
+
+ dev = device_find_by_driver_unit("h3codec", 0);
+ if (dev == NULL)
+ return NULL;
+ csc = device_private(dev);
+ if (csc->sc_phandle != phandle)
+ return NULL;
+
+ return csc;
+}
+
+static u_int
+h3_codec_pr_read(struct h3_codec_softc *csc, u_int addr)
+{
+ uint32_t val;
+
+ /* Read current value */
+ val = RD4(csc, H3_PR_CFG);
+
+ /* De-assert reset */
+ val |= H3_AC_PR_RST;
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Read mode */
+ val &= ~H3_AC_PR_RW;
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Set address */
+ val &= ~H3_AC_PR_ADDR;
+ val |= __SHIFTIN(addr, H3_AC_PR_ADDR);
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Read data */
+ return __SHIFTOUT(RD4(csc, H3_PR_CFG), H3_ACDA_PR_RDAT);
+}
+
+static void
+h3_codec_pr_write(struct h3_codec_softc *csc, u_int addr, u_int data)
+{
+ uint32_t val;
+
+ /* Read current value */
+ val = RD4(csc, H3_PR_CFG);
+
+ /* De-assert reset */
+ val |= H3_AC_PR_RST;
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Set address */
+ val &= ~H3_AC_PR_ADDR;
+ val |= __SHIFTIN(addr, H3_AC_PR_ADDR);
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Write data */
+ val &= ~H3_ACDA_PR_WDAT;
+ val |= __SHIFTIN(data, H3_ACDA_PR_WDAT);
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Write mode */
+ val |= H3_AC_PR_RW;
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Clear write mode */
+ val &= ~H3_AC_PR_RW;
+ WR4(csc, H3_PR_CFG, val);
+}
+
+static void
+h3_codec_pr_set_clear(struct h3_codec_softc *csc, u_int addr, u_int set, u_int clr)
+{
+ u_int old, new;
+
+ old = h3_codec_pr_read(csc, addr);
+ new = set | (old & ~clr);
+ h3_codec_pr_write(csc, addr, new);
+}
+
+static int
+h3_codec_init(struct sunxi_codec_softc *sc)
+{
+ struct h3_codec_softc *csc;
+ int phandle;
+
+ /* Lookup the codec analog controls phandle */
+ phandle = fdtbus_get_phandle(sc->sc_phandle,
+ "allwinner,codec-analog-controls");
+ if (phandle < 0) {
+ aprint_error_dev(sc->sc_dev,
+ "missing allwinner,codec-analog-controls property\n");
+ return ENXIO;
+ }
+
+ /* Find a matching h3codec instance */
+ sc->sc_codec_priv = h3_codec_find(phandle);
+ if (sc->sc_codec_priv == NULL) {
+ aprint_error_dev(sc->sc_dev, "couldn't find codec analog controls\n");
+ return ENOENT;
+ }
+ csc = sc->sc_codec_priv;
+
+ /* Right & Left LINEOUT enable */
+ h3_codec_pr_set_clear(csc, H3_PAEN_CTR, H3_LINEOUTEN, 0);
+ h3_codec_pr_set_clear(csc, H3_MIC2G_LINEOUT_CTR,
+ H3_LINEOUT_LSEL | H3_LINEOUT_RSEL, 0);
+
+ return 0;
+}
+
+static void
+h3_codec_mute(struct sunxi_codec_softc *sc, int mute, u_int mode)
+{
+ struct h3_codec_softc * const csc = sc->sc_codec_priv;
+
+ if (mode == AUMODE_PLAY) {
+ if (mute) {
+ /* Mute DAC l/r channels to output mixer */
+ h3_codec_pr_set_clear(csc, H3_LOMIXSC,
+ 0, H3_LOMIXSC_LDAC);
+ h3_codec_pr_set_clear(csc, H3_ROMIXSC,
+ 0, H3_ROMIXSC_RDAC);
+ /* Disable DAC analog l/r channels and output mixer */
+ h3_codec_pr_set_clear(csc, H3_DAC_PA_SRC,
+ 0, H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN);
+ } else {
+ /* Enable DAC analog l/r channels and output mixer */
+ h3_codec_pr_set_clear(csc, H3_DAC_PA_SRC,
+ H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN, 0);
+ /* Unmute DAC l/r channels to output mixer */
+ h3_codec_pr_set_clear(csc, H3_LOMIXSC, H3_LOMIXSC_LDAC, 0);
+ h3_codec_pr_set_clear(csc, H3_ROMIXSC, H3_ROMIXSC_RDAC, 0);
+ }
+ } else {
+ if (mute) {
+ /* Disable ADC analog l/r channels */
+ h3_codec_pr_set_clear(csc, H3_ADC_AP_EN,
+ 0, H3_ADCREN | H3_ADCLEN);
+ } else {
+ /* Enable ADC analog l/r channels */
+ h3_codec_pr_set_clear(csc, H3_ADC_AP_EN,
+ H3_ADCREN | H3_ADCLEN, 0);
+ }
+ }
+}
+
+static int
+h3_codec_set_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
+{
+ struct h3_codec_softc * const csc = sc->sc_codec_priv;
+ const struct h3_codec_mixer *mix;
+ u_int val, shift;
+ int nvol;
+
+ switch (mc->dev) {
+ case H3_CODEC_OUTPUT_MASTER_VOLUME:
+ case H3_CODEC_INPUT_DAC_VOLUME:
+ case H3_CODEC_INPUT_LINEIN_VOLUME:
+ case H3_CODEC_INPUT_MIC1_VOLUME:
+ case H3_CODEC_INPUT_MIC2_VOLUME:
+ case H3_CODEC_RECORD_AGC_VOLUME:
+ mix = &h3_codec_mixers[mc->dev];
+ val = h3_codec_pr_read(csc, mix->reg);
+ shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
+ nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> shift;
+ val &= ~mix->mask;
+ val |= __SHIFTIN(nvol, mix->mask);
+ h3_codec_pr_write(csc, mix->reg, val);
+ return 0;
+
+ case H3_CODEC_RECORD_SOURCE:
+ h3_codec_pr_write(csc, H3_LADCMIXSC, mc->un.mask);
+ h3_codec_pr_write(csc, H3_RADCMIXSC, mc->un.mask);
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+static int
+h3_codec_get_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
+{
+ struct h3_codec_softc * const csc = sc->sc_codec_priv;
+ const struct h3_codec_mixer *mix;
+ u_int val, shift;
+ int nvol;
+
+ switch (mc->dev) {
+ case H3_CODEC_OUTPUT_MASTER_VOLUME:
+ case H3_CODEC_INPUT_DAC_VOLUME:
+ case H3_CODEC_INPUT_LINEIN_VOLUME:
+ case H3_CODEC_INPUT_MIC1_VOLUME:
+ case H3_CODEC_INPUT_MIC2_VOLUME:
+ case H3_CODEC_RECORD_AGC_VOLUME:
+ mix = &h3_codec_mixers[mc->dev];
+ val = h3_codec_pr_read(csc, mix->reg);
+ shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
+ nvol = __SHIFTOUT(val, mix->mask) << shift;
+ mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol;
+ mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol;
+ return 0;
+
+ case H3_CODEC_RECORD_SOURCE:
+ mc->un.mask =
+ h3_codec_pr_read(csc, H3_LADCMIXSC) |
+ h3_codec_pr_read(csc, H3_RADCMIXSC);
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+static int
+h3_codec_query_devinfo(struct sunxi_codec_softc *sc, mixer_devinfo_t *di)
+{
+ const struct h3_codec_mixer *mix;
+
+ switch (di->index) {
+ case H3_CODEC_OUTPUT_CLASS:
+ di->mixer_class = di->index;
+ strcpy(di->label.name, AudioCoutputs);
+ di->type = AUDIO_MIXER_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ return 0;
+
+ case H3_CODEC_INPUT_CLASS:
+ di->mixer_class = di->index;
+ strcpy(di->label.name, AudioCinputs);
+ di->type = AUDIO_MIXER_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ return 0;
+
+ case H3_CODEC_RECORD_CLASS:
+ di->mixer_class = di->index;
+ strcpy(di->label.name, AudioCrecord);
+ di->type = AUDIO_MIXER_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ return 0;
+
+ case H3_CODEC_OUTPUT_MASTER_VOLUME:
+ case H3_CODEC_INPUT_DAC_VOLUME:
+ case H3_CODEC_INPUT_LINEIN_VOLUME:
+ case H3_CODEC_INPUT_MIC1_VOLUME:
+ case H3_CODEC_INPUT_MIC2_VOLUME:
+ case H3_CODEC_RECORD_AGC_VOLUME:
+ mix = &h3_codec_mixers[di->index];
+ di->mixer_class = mix->mixer_class;
+ strcpy(di->label.name, mix->name);
+ di->un.v.delta =
+ 256 / (__SHIFTOUT_MASK(mix->mask) + 1);
+ di->type = AUDIO_MIXER_VALUE;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ di->un.v.num_channels = 2;
+ strcpy(di->un.v.units.name, AudioNvolume);
+ return 0;
+
+ case H3_CODEC_RECORD_SOURCE:
+ di->mixer_class = H3_CODEC_RECORD_CLASS;
+ strcpy(di->label.name, AudioNsource);
+ di->type = AUDIO_MIXER_SET;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ di->un.s.num_mem = 4;
+ strcpy(di->un.s.member[0].label.name, AudioNline);
+ di->un.s.member[0].mask = H3_ADCMIXSC_LINEIN;
+ strcpy(di->un.s.member[1].label.name, "mic1");
+ di->un.s.member[1].mask = H3_ADCMIXSC_MIC1;
+ strcpy(di->un.s.member[2].label.name, "mic2");
+ di->un.s.member[2].mask = H3_ADCMIXSC_MIC2;
+ strcpy(di->un.s.member[3].label.name, AudioNdac);
+ di->un.s.member[3].mask = H3_ADCMIXSC_OMIXER;
+ return 0;
+
+ }
+
+ return ENXIO;
+}
+
+const struct sunxi_codec_conf sun8i_h3_codecconf = {
+ .name = "H3 Audio Codec",
+
+ .init = h3_codec_init,
+ .mute = h3_codec_mute,
+ .set_port = h3_codec_set_port,
+ .get_port = h3_codec_get_port,
+ .query_devinfo = h3_codec_query_devinfo,
+
+ .DPC = 0x00,
+ .DAC_FIFOC = 0x04,
+ .DAC_FIFOS = 0x08,
+ .DAC_TXDATA = 0x20,
+ .ADC_FIFOC = 0x10,
+ .ADC_FIFOS = 0x14,
+ .ADC_RXDATA = 0x18,
+ .DAC_CNT = 0x40,
+ .ADC_CNT = 0x44,
+};
+
+/*
+ * Device glue, only here to claim resources on behalf of the sunxi_codec driver.
+ */
+
+static const char * compatible[] = {
+ "allwinner,sun8i-h3-codec-analog",
+ NULL
+};
+
+static int
+h3_codec_match(device_t parent, cfdata_t cf, void *aux)
+{
+ struct fdt_attach_args * const faa = aux;
+
+ return of_match_compatible(faa->faa_phandle, compatible);
+}
+
+static void
+h3_codec_attach(device_t parent, device_t self, void *aux)
+{
+ struct h3_codec_softc * const sc = device_private(self);
+ struct fdt_attach_args * const faa = aux;
+ const int phandle = faa->faa_phandle;
+ bus_addr_t addr;
+ bus_size_t size;
+
+ sc->sc_dev = self;
+ if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+ aprint_error(": couldn't get registers\n");
+ return;
+ }
+ sc->sc_bst = faa->faa_bst;
+ if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
+ aprint_error(": couldn't map registers\n");
+ return;
+ }
+
+ sc->sc_phandle = phandle;
+
+ aprint_naive("\n");
+ aprint_normal(": H3 Audio Codec (analog part)\n");
+}
+
+CFATTACH_DECL_NEW(h3_codec, sizeof(struct h3_codec_softc),
+ h3_codec_match, h3_codec_attach, NULL, NULL);
Index: src/sys/arch/arm/sunxi/sunxi_codec.c
diff -u /dev/null src/sys/arch/arm/sunxi/sunxi_codec.c:1.1.2.2
--- /dev/null Wed Aug 9 05:49:50 2017
+++ src/sys/arch/arm/sunxi/sunxi_codec.c Wed Aug 9 05:49:50 2017
@@ -0,0 +1,751 @@
+/* $NetBSD: sunxi_codec.c,v 1.1.2.2 2017/08/09 05:49:50 snj Exp $ */
+
+/*-
+ * Copyright (c) 2014-2017 Jared 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 "opt_ddb.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: sunxi_codec.c,v 1.1.2.2 2017/08/09 05:49:50 snj Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/device.h>
+#include <sys/kmem.h>
+#include <sys/gpio.h>
+
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+#include <dev/auconv.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#include <arm/sunxi/sunxi_codec.h>
+
+#define TX_TRIG_LEVEL 0xf
+#define RX_TRIG_LEVEL 0x7
+#define DRQ_CLR_CNT 0x3
+
+#define AC_DAC_DPC(_sc) ((_sc)->sc_cfg->DPC)
+#define DAC_DPC_EN_DA 0x80000000
+#define AC_DAC_FIFOC(_sc) ((_sc)->sc_cfg->DAC_FIFOC)
+#define DAC_FIFOC_FS __BITS(31,29)
+#define DAC_FS_48KHZ 0
+#define DAC_FS_32KHZ 1
+#define DAC_FS_24KHZ 2
+#define DAC_FS_16KHZ 3
+#define DAC_FS_12KHZ 4
+#define DAC_FS_8KHZ 5
+#define DAC_FS_192KHZ 6
+#define DAC_FS_96KHZ 7
+#define DAC_FIFOC_FIFO_MODE __BITS(25,24)
+#define FIFO_MODE_24_31_8 0
+#define FIFO_MODE_16_31_16 0
+#define FIFO_MODE_16_15_0 1
+#define DAC_FIFOC_DRQ_CLR_CNT __BITS(22,21)
+#define DAC_FIFOC_TX_TRIG_LEVEL __BITS(14,8)
+#define DAC_FIFOC_MONO_EN __BIT(6)
+#define DAC_FIFOC_TX_BITS __BIT(5)
+#define DAC_FIFOC_DRQ_EN __BIT(4)
+#define DAC_FIFOC_FIFO_FLUSH __BIT(0)
+#define AC_DAC_FIFOS(_sc) ((_sc)->sc_cfg->DAC_FIFOS)
+#define AC_DAC_TXDATA(_sc) ((_sc)->sc_cfg->DAC_TXDATA)
+#define AC_ADC_FIFOC(_sc) ((_sc)->sc_cfg->ADC_FIFOC)
+#define ADC_FIFOC_FS __BITS(31,29)
+#define ADC_FS_48KHZ 0
+#define ADC_FIFOC_EN_AD __BIT(28)
+#define ADC_FIFOC_RX_FIFO_MODE __BIT(24)
+#define ADC_FIFOC_RX_TRIG_LEVEL __BITS(12,8)
+#define ADC_FIFOC_MONO_EN __BIT(7)
+#define ADC_FIFOC_RX_BITS __BIT(6)
+#define ADC_FIFOC_DRQ_EN __BIT(4)
+#define ADC_FIFOC_FIFO_FLUSH __BIT(0)
+#define AC_ADC_FIFOS(_sc) ((_sc)->sc_cfg->ADC_FIFOS)
+#define AC_ADC_RXDATA(_sc) ((_sc)->sc_cfg->ADC_RXDATA)
+#define AC_DAC_CNT(_sc) ((_sc)->sc_cfg->DAC_CNT)
+#define AC_ADC_CNT(_sc) ((_sc)->sc_cfg->ADC_CNT)
+
+static const struct of_compat_data compat_data[] = {
+ H3_CODEC_COMPATDATA,
+ { NULL }
+};
+
+#define CODEC_READ(sc, reg) \
+ bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define CODEC_WRITE(sc, reg, val) \
+ bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+static int
+sunxi_codec_allocdma(struct sunxi_codec_softc *sc, size_t size,
+ size_t align, struct sunxi_codec_dma *dma)
+{
+ int error;
+
+ dma->dma_size = size;
+ error = bus_dmamem_alloc(sc->sc_dmat, dma->dma_size, align, 0,
+ dma->dma_segs, 1, &dma->dma_nsegs, BUS_DMA_WAITOK);
+ if (error)
+ return error;
+
+ error = bus_dmamem_map(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs,
+ dma->dma_size, &dma->dma_addr, BUS_DMA_WAITOK | BUS_DMA_COHERENT);
+ if (error)
+ goto free;
+
+ error = bus_dmamap_create(sc->sc_dmat, dma->dma_size, dma->dma_nsegs,
+ dma->dma_size, 0, BUS_DMA_WAITOK, &dma->dma_map);
+ if (error)
+ goto unmap;
+
+ error = bus_dmamap_load(sc->sc_dmat, dma->dma_map, dma->dma_addr,
+ dma->dma_size, NULL, BUS_DMA_WAITOK);
+ if (error)
+ goto destroy;
+
+ return 0;
+
+destroy:
+ bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
+unmap:
+ bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size);
+free:
+ bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs);
+
+ return error;
+}
+
+static void
+sunxi_codec_freedma(struct sunxi_codec_softc *sc, struct sunxi_codec_dma *dma)
+{
+ bus_dmamap_unload(sc->sc_dmat, dma->dma_map);
+ bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
+ bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size);
+ bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs);
+}
+
+static int
+sunxi_codec_transfer(struct sunxi_codec_chan *ch)
+{
+ bus_dma_segment_t seg;
+
+ seg.ds_addr = ch->ch_cur_phys;
+ seg.ds_len = ch->ch_blksize;
+ ch->ch_req.dreq_segs = &seg;
+ ch->ch_req.dreq_nsegs = 1;
+
+ return fdtbus_dma_transfer(ch->ch_dma, &ch->ch_req);
+}
+
+static int
+sunxi_codec_open(void *priv, int flags)
+{
+ return 0;
+}
+
+static void
+sunxi_codec_close(void *priv)
+{
+}
+
+static int
+sunxi_codec_drain(void *priv)
+{
+ struct sunxi_codec_softc * const sc = priv;
+ uint32_t val;
+
+ val = CODEC_READ(sc, AC_DAC_FIFOC(sc));
+ CODEC_WRITE(sc, AC_DAC_FIFOC(sc), val | DAC_FIFOC_FIFO_FLUSH);
+
+ val = CODEC_READ(sc, AC_ADC_FIFOC(sc));
+ CODEC_WRITE(sc, AC_ADC_FIFOC(sc), val | ADC_FIFOC_FIFO_FLUSH);
+
+ return 0;
+}
+
+static int
+sunxi_codec_query_encoding(void *priv, struct audio_encoding *ae)
+{
+ struct sunxi_codec_softc * const sc = priv;
+
+ return auconv_query_encoding(sc->sc_encodings, ae);
+}
+
+static int
+sunxi_codec_set_params(void *priv, int setmode, int usemode,
+ audio_params_t *play, audio_params_t *rec,
+ stream_filter_list_t *pfil, stream_filter_list_t *rfil)
+{
+ struct sunxi_codec_softc * const sc = priv;
+ int index;
+
+ if (play && (setmode & AUMODE_PLAY)) {
+ index = auconv_set_converter(&sc->sc_format, 1,
+ AUMODE_PLAY, play, true, pfil);
+ if (index < 0)
+ return EINVAL;
+ sc->sc_pchan.ch_params = pfil->req_size > 0 ?
+ pfil->filters[0].param : *play;
+ }
+ if (rec && (setmode & AUMODE_RECORD)) {
+ index = auconv_set_converter(&sc->sc_format, 1,
+ AUMODE_RECORD, rec, true, rfil);
+ if (index < 0)
+ return EINVAL;
+ sc->sc_rchan.ch_params = rfil->req_size > 0 ?
+ rfil->filters[0].param : *rec;
+ }
+
+ return 0;
+}
+
+static int
+sunxi_codec_set_port(void *priv, mixer_ctrl_t *mc)
+{
+ struct sunxi_codec_softc * const sc = priv;
+
+ return sc->sc_cfg->set_port(sc, mc);
+}
+
+static int
+sunxi_codec_get_port(void *priv, mixer_ctrl_t *mc)
+{
+ struct sunxi_codec_softc * const sc = priv;
+
+ return sc->sc_cfg->get_port(sc, mc);
+}
+
+static int
+sunxi_codec_query_devinfo(void *priv, mixer_devinfo_t *di)
+{
+ struct sunxi_codec_softc * const sc = priv;
+
+ return sc->sc_cfg->query_devinfo(sc, di);
+}
+
+static void *
+sunxi_codec_allocm(void *priv, int dir, size_t size)
+{
+ struct sunxi_codec_softc * const sc = priv;
+ struct sunxi_codec_dma *dma;
+ int error;
+
+ dma = kmem_alloc(sizeof(*dma), KM_SLEEP);
+
+ error = sunxi_codec_allocdma(sc, size, 16, dma);
+ if (error) {
+ kmem_free(dma, sizeof(*dma));
+ device_printf(sc->sc_dev, "couldn't allocate DMA memory (%d)\n",
+ error);
+ return NULL;
+ }
+
+ LIST_INSERT_HEAD(&sc->sc_dmalist, dma, dma_list);
+
+ return dma->dma_addr;
+}
+
+static void
+sunxi_codec_freem(void *priv, void *addr, size_t size)
+{
+ struct sunxi_codec_softc * const sc = priv;
+ struct sunxi_codec_dma *dma;
+
+ LIST_FOREACH(dma, &sc->sc_dmalist, dma_list)
+ if (dma->dma_addr == addr) {
+ sunxi_codec_freedma(sc, dma);
+ LIST_REMOVE(dma, dma_list);
+ kmem_free(dma, sizeof(*dma));
+ break;
+ }
+}
+
+static paddr_t
+sunxi_codec_mappage(void *priv, void *addr, off_t off, int prot)
+{
+ struct sunxi_codec_softc * const sc = priv;
+ struct sunxi_codec_dma *dma;
+
+ if (off < 0)
+ return -1;
+
+ LIST_FOREACH(dma, &sc->sc_dmalist, dma_list)
+ if (dma->dma_addr == addr) {
+ return bus_dmamem_mmap(sc->sc_dmat, dma->dma_segs,
+ dma->dma_nsegs, off, prot, BUS_DMA_WAITOK);
+ }
+
+ return -1;
+}
+
+static int
+sunxi_codec_getdev(void *priv, struct audio_device *adev)
+{
+ struct sunxi_codec_softc * const sc = priv;
+
+ snprintf(adev->name, sizeof(adev->name), "Allwinner");
+ snprintf(adev->version, sizeof(adev->version), "%s",
+ sc->sc_cfg->name);
+ snprintf(adev->config, sizeof(adev->config), "sunxicodec");
+
+ return 0;
+}
+
+static int
+sunxi_codec_get_props(void *priv)
+{
+ return AUDIO_PROP_PLAYBACK|AUDIO_PROP_CAPTURE|
+ AUDIO_PROP_INDEPENDENT|AUDIO_PROP_MMAP|
+ AUDIO_PROP_FULLDUPLEX;
+}
+
+static int
+sunxi_codec_round_blocksize(void *priv, int bs, int mode,
+ const audio_params_t *params)
+{
+ bs &= ~3;
+ if (bs == 0)
+ bs = 4;
+ return bs;
+}
+
+static size_t
+sunxi_codec_round_buffersize(void *priv, int dir, size_t bufsize)
+{
+ return bufsize;
+}
+
+static int
+sunxi_codec_trigger_output(void *priv, void *start, void *end, int blksize,
+ void (*intr)(void *), void *intrarg, const audio_params_t *params)
+{
+ struct sunxi_codec_softc * const sc = priv;
+ struct sunxi_codec_chan *ch = &sc->sc_pchan;
+ struct sunxi_codec_dma *dma;
+ bus_addr_t pstart;
+ bus_size_t psize;
+ uint32_t val;
+ int error;
+
+ pstart = 0;
+ psize = (uintptr_t)end - (uintptr_t)start;
+
+ LIST_FOREACH(dma, &sc->sc_dmalist, dma_list)
+ if (dma->dma_addr == start) {
+ pstart = dma->dma_map->dm_segs[0].ds_addr;
+ break;
+ }
+ if (pstart == 0) {
+ device_printf(sc->sc_dev, "bad addr %p\n", start);
+ return EINVAL;
+ }
+
+ ch->ch_intr = intr;
+ ch->ch_intrarg = intrarg;
+ ch->ch_start_phys = ch->ch_cur_phys = pstart;
+ ch->ch_end_phys = pstart + psize;
+ ch->ch_blksize = blksize;
+
+ /* Flush DAC FIFO */
+ val = CODEC_READ(sc, AC_DAC_FIFOC(sc));
+ CODEC_WRITE(sc, AC_DAC_FIFOC(sc), val | DAC_FIFOC_FIFO_FLUSH);
+
+ /* Clear DAC FIFO status */
+ val = CODEC_READ(sc, AC_DAC_FIFOS(sc));
+ CODEC_WRITE(sc, AC_DAC_FIFOS(sc), val);
+
+ /* Unmute output */
+ if (sc->sc_cfg->mute)
+ sc->sc_cfg->mute(sc, 0, ch->ch_mode);
+
+ /* Configure DAC FIFO */
+ CODEC_WRITE(sc, AC_DAC_FIFOC(sc),
+ __SHIFTIN(DAC_FS_48KHZ, DAC_FIFOC_FS) |
+ __SHIFTIN(FIFO_MODE_16_15_0, DAC_FIFOC_FIFO_MODE) |
+ __SHIFTIN(DRQ_CLR_CNT, DAC_FIFOC_DRQ_CLR_CNT) |
+ __SHIFTIN(TX_TRIG_LEVEL, DAC_FIFOC_TX_TRIG_LEVEL));
+
+ /* Enable DAC DRQ */
+ val = CODEC_READ(sc, AC_DAC_FIFOC(sc));
+ CODEC_WRITE(sc, AC_DAC_FIFOC(sc), val | DAC_FIFOC_DRQ_EN);
+
+ /* Start DMA transfer */
+ error = sunxi_codec_transfer(ch);
+ if (error != 0) {
+ aprint_error_dev(sc->sc_dev,
+ "failed to start DMA transfer: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int
+sunxi_codec_trigger_input(void *priv, void *start, void *end, int blksize,
+ void (*intr)(void *), void *intrarg, const audio_params_t *params)
+{
+ struct sunxi_codec_softc * const sc = priv;
+ struct sunxi_codec_chan *ch = &sc->sc_rchan;
+ struct sunxi_codec_dma *dma;
+ bus_addr_t pstart;
+ bus_size_t psize;
+ uint32_t val;
+ int error;
+
+ pstart = 0;
+ psize = (uintptr_t)end - (uintptr_t)start;
+
+ LIST_FOREACH(dma, &sc->sc_dmalist, dma_list)
+ if (dma->dma_addr == start) {
+ pstart = dma->dma_map->dm_segs[0].ds_addr;
+ break;
+ }
+ if (pstart == 0) {
+ device_printf(sc->sc_dev, "bad addr %p\n", start);
+ return EINVAL;
+ }
+
+ ch->ch_intr = intr;
+ ch->ch_intrarg = intrarg;
+ ch->ch_start_phys = ch->ch_cur_phys = pstart;
+ ch->ch_end_phys = pstart + psize;
+ ch->ch_blksize = blksize;
+
+ /* Flush ADC FIFO */
+ val = CODEC_READ(sc, AC_ADC_FIFOC(sc));
+ CODEC_WRITE(sc, AC_ADC_FIFOC(sc), val | ADC_FIFOC_FIFO_FLUSH);
+
+ /* Clear ADC FIFO status */
+ val = CODEC_READ(sc, AC_ADC_FIFOS(sc));
+ CODEC_WRITE(sc, AC_ADC_FIFOS(sc), val);
+
+ /* Unmute input */
+ if (sc->sc_cfg->mute)
+ sc->sc_cfg->mute(sc, 0, ch->ch_mode);
+
+ /* Configure ADC FIFO */
+ CODEC_WRITE(sc, AC_ADC_FIFOC(sc),
+ __SHIFTIN(ADC_FS_48KHZ, ADC_FIFOC_FS) |
+ __SHIFTIN(RX_TRIG_LEVEL, ADC_FIFOC_RX_TRIG_LEVEL) |
+ ADC_FIFOC_EN_AD | ADC_FIFOC_RX_FIFO_MODE);
+
+ /* Enable ADC DRQ */
+ val = CODEC_READ(sc, AC_ADC_FIFOC(sc));
+ CODEC_WRITE(sc, AC_ADC_FIFOC(sc), val | ADC_FIFOC_DRQ_EN);
+
+ /* Start DMA transfer */
+ error = sunxi_codec_transfer(ch);
+ if (error != 0) {
+ aprint_error_dev(sc->sc_dev,
+ "failed to start DMA transfer: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int
+sunxi_codec_halt_output(void *priv)
+{
+ struct sunxi_codec_softc * const sc = priv;
+ struct sunxi_codec_chan *ch = &sc->sc_pchan;
+ uint32_t val;
+
+ /* Disable DMA channel */
+ fdtbus_dma_halt(ch->ch_dma);
+
+ /* Mute output */
+ if (sc->sc_cfg->mute)
+ sc->sc_cfg->mute(sc, 1, ch->ch_mode);
+
+ /* Disable DAC DRQ */
+ val = CODEC_READ(sc, AC_DAC_FIFOC(sc));
+ CODEC_WRITE(sc, AC_DAC_FIFOC(sc), val & ~DAC_FIFOC_DRQ_EN);
+
+ ch->ch_intr = NULL;
+ ch->ch_intrarg = NULL;
+
+ return 0;
+}
+
+static int
+sunxi_codec_halt_input(void *priv)
+{
+ struct sunxi_codec_softc * const sc = priv;
+ struct sunxi_codec_chan *ch = &sc->sc_rchan;
+ uint32_t val;
+
+ /* Disable DMA channel */
+ fdtbus_dma_halt(ch->ch_dma);
+
+ /* Mute output */
+ if (sc->sc_cfg->mute)
+ sc->sc_cfg->mute(sc, 1, ch->ch_mode);
+
+ /* Disable ADC DRQ */
+ val = CODEC_READ(sc, AC_ADC_FIFOC(sc));
+ CODEC_WRITE(sc, AC_ADC_FIFOC(sc), val & ~ADC_FIFOC_DRQ_EN);
+
+ return 0;
+}
+
+static void
+sunxi_codec_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread)
+{
+ struct sunxi_codec_softc * const sc = priv;
+
+ *intr = &sc->sc_intr_lock;
+ *thread = &sc->sc_lock;
+}
+
+static const struct audio_hw_if sunxi_codec_hw_if = {
+ .open = sunxi_codec_open,
+ .close = sunxi_codec_close,
+ .drain = sunxi_codec_drain,
+ .query_encoding = sunxi_codec_query_encoding,
+ .set_params = sunxi_codec_set_params,
+ .allocm = sunxi_codec_allocm,
+ .freem = sunxi_codec_freem,
+ .mappage = sunxi_codec_mappage,
+ .getdev = sunxi_codec_getdev,
+ .set_port = sunxi_codec_set_port,
+ .get_port = sunxi_codec_get_port,
+ .query_devinfo = sunxi_codec_query_devinfo,
+ .get_props = sunxi_codec_get_props,
+ .round_blocksize = sunxi_codec_round_blocksize,
+ .round_buffersize = sunxi_codec_round_buffersize,
+ .trigger_output = sunxi_codec_trigger_output,
+ .trigger_input = sunxi_codec_trigger_input,
+ .halt_output = sunxi_codec_halt_output,
+ .halt_input = sunxi_codec_halt_input,
+ .get_locks = sunxi_codec_get_locks,
+};
+
+static void
+sunxi_codec_dmaintr(void *priv)
+{
+ struct sunxi_codec_chan * const ch = priv;
+
+ ch->ch_cur_phys += ch->ch_blksize;
+ if (ch->ch_cur_phys >= ch->ch_end_phys)
+ ch->ch_cur_phys = ch->ch_start_phys;
+
+ if (ch->ch_intr) {
+ ch->ch_intr(ch->ch_intrarg);
+ sunxi_codec_transfer(ch);
+ }
+}
+
+static int
+sunxi_codec_chan_init(struct sunxi_codec_softc *sc,
+ struct sunxi_codec_chan *ch, u_int mode, const char *dmaname)
+{
+ ch->ch_sc = sc;
+ ch->ch_mode = mode;
+ ch->ch_dma = fdtbus_dma_get(sc->sc_phandle, dmaname, sunxi_codec_dmaintr, ch);
+ if (ch->ch_dma == NULL) {
+ aprint_error(": couldn't get dma channel \"%s\"\n", dmaname);
+ return ENXIO;
+ }
+
+ if (mode == AUMODE_PLAY) {
+ ch->ch_req.dreq_dir = FDT_DMA_WRITE;
+ ch->ch_req.dreq_dev_phys =
+ sc->sc_baseaddr + AC_DAC_TXDATA(sc);
+ } else {
+ ch->ch_req.dreq_dir = FDT_DMA_READ;
+ ch->ch_req.dreq_dev_phys =
+ sc->sc_baseaddr + AC_ADC_RXDATA(sc);
+ }
+ ch->ch_req.dreq_mem_opt.opt_bus_width = 16;
+ ch->ch_req.dreq_mem_opt.opt_burst_len = 4;
+ ch->ch_req.dreq_dev_opt.opt_bus_width = 16;
+ ch->ch_req.dreq_dev_opt.opt_burst_len = 4;
+
+ return 0;
+}
+
+static int
+sunxi_codec_clock_init(int phandle)
+{
+ struct fdtbus_reset *rst;
+ struct clk *clk;
+ int error;
+
+ /* Set codec clock to 24.576MHz, suitable for 48 kHz sampling rates */
+ clk = fdtbus_clock_get(phandle, "codec");
+ if (clk == NULL) {
+ aprint_error(": couldn't find codec clock\n");
+ return ENXIO;
+ }
+ error = clk_set_rate(clk, 24576000);
+ if (error != 0) {
+ aprint_error(": couldn't set codec clock rate: %d\n", error);
+ return error;
+ }
+ error = clk_enable(clk);
+ if (error != 0) {
+ aprint_error(": couldn't enable codec clock: %d\n", error);
+ return error;
+ }
+
+ /* Enable APB clock */
+ clk = fdtbus_clock_get(phandle, "apb");
+ if (clk == NULL) {
+ aprint_error(": couldn't find apb clock\n");
+ return ENXIO;
+ }
+ error = clk_enable(clk);
+ if (error != 0) {
+ aprint_error(": couldn't enable apb clock: %d\n", error);
+ return error;
+ }
+
+ /* De-assert reset */
+ rst = fdtbus_reset_get_index(phandle, 0);
+ if (rst == NULL) {
+ aprint_error(": couldn't find reset\n");
+ return ENXIO;
+ }
+ error = fdtbus_reset_deassert(rst);
+ if (error != 0) {
+ aprint_error(": couldn't de-assert reset: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int
+sunxi_codec_match(device_t parent, cfdata_t cf, void *aux)
+{
+ struct fdt_attach_args * const faa = aux;
+
+ return of_match_compat_data(faa->faa_phandle, compat_data);
+}
+
+static void
+sunxi_codec_attach(device_t parent, device_t self, void *aux)
+{
+ struct sunxi_codec_softc * const sc = device_private(self);
+ struct fdt_attach_args * const faa = aux;
+ const int phandle = faa->faa_phandle;
+ bus_addr_t addr;
+ bus_size_t size;
+ uint32_t val;
+ int error;
+
+ if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+ aprint_error(": couldn't get registers\n");
+ return;
+ }
+
+ if (sunxi_codec_clock_init(phandle) != 0)
+ return;
+
+ sc->sc_dev = self;
+ sc->sc_phandle = phandle;
+ sc->sc_baseaddr = addr;
+ sc->sc_bst = faa->faa_bst;
+ if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
+ aprint_error(": couldn't map registers\n");
+ return;
+ }
+ sc->sc_dmat = faa->faa_dmat;
+ LIST_INIT(&sc->sc_dmalist);
+ sc->sc_cfg = (void *)of_search_compatible(phandle, compat_data)->data;
+ mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
+ mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
+
+ if (sunxi_codec_chan_init(sc, &sc->sc_pchan, AUMODE_PLAY, "tx") != 0 ||
+ sunxi_codec_chan_init(sc, &sc->sc_rchan, AUMODE_RECORD, "rx") != 0) {
+ aprint_error(": couldn't setup channels\n");
+ return;
+ }
+
+ /* Optional PA mute GPIO */
+ sc->sc_pin_pa = fdtbus_gpio_acquire(phandle, "allwinner,pa-gpios", GPIO_PIN_OUTPUT);
+ if (sc->sc_pin_pa != NULL)
+ fdtbus_gpio_write(sc->sc_pin_pa, 1);
+
+ aprint_naive("\n");
+ aprint_normal(": %s\n", sc->sc_cfg->name);
+
+ /* Enable DAC */
+ val = CODEC_READ(sc, AC_DAC_DPC(sc));
+ val |= DAC_DPC_EN_DA;
+ CODEC_WRITE(sc, AC_DAC_DPC(sc), val);
+
+ /* Initialize codec */
+ if (sc->sc_cfg->init(sc) != 0) {
+ aprint_error_dev(self, "couldn't initialize codec\n");
+ return;
+ }
+
+ sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD;
+ sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE;
+ sc->sc_format.validbits = 16;
+ sc->sc_format.precision = 16;
+ sc->sc_format.channels = 2;
+ sc->sc_format.channel_mask = AUFMT_STEREO;
+ sc->sc_format.frequency_type = 0;
+ sc->sc_format.frequency[0] = sc->sc_format.frequency[1] = 48000;
+
+ error = auconv_create_encodings(&sc->sc_format, 1, &sc->sc_encodings);
+ if (error) {
+ aprint_error_dev(self, "couldn't create encodings\n");
+ return;
+ }
+
+ audio_attach_mi(&sunxi_codec_hw_if, sc, self);
+}
+
+CFATTACH_DECL_NEW(sunxi_codec, sizeof(struct sunxi_codec_softc),
+ sunxi_codec_match, sunxi_codec_attach, NULL, NULL);
+
+#ifdef DDB
+void sunxicodec_dump(void);
+
+void
+sunxicodec_dump(void)
+{
+ struct sunxi_codec_softc *sc;
+ device_t dev;
+
+ dev = device_find_by_driver_unit("sunxicodec", 0);
+ if (dev == NULL)
+ return;
+ sc = device_private(dev);
+
+ device_printf(dev, "AC_DAC_DPC: %08x\n", CODEC_READ(sc, AC_DAC_DPC(sc)));
+ device_printf(dev, "AC_DAC_FIFOC: %08x\n", CODEC_READ(sc, AC_DAC_FIFOC(sc)));
+ device_printf(dev, "AC_DAC_FIFOS: %08x\n", CODEC_READ(sc, AC_DAC_FIFOS(sc)));
+ device_printf(dev, "AC_ADC_FIFOC: %08x\n", CODEC_READ(sc, AC_ADC_FIFOC(sc)));
+ device_printf(dev, "AC_ADC_FIFOS: %08x\n", CODEC_READ(sc, AC_ADC_FIFOS(sc)));
+ device_printf(dev, "AC_DAC_CNT: %08x\n", CODEC_READ(sc, AC_DAC_CNT(sc)));
+ device_printf(dev, "AC_ADC_CNT: %08x\n", CODEC_READ(sc, AC_ADC_CNT(sc)));
+}
+#endif
Index: src/sys/arch/arm/sunxi/sunxi_codec.h
diff -u /dev/null src/sys/arch/arm/sunxi/sunxi_codec.h:1.1.2.2
--- /dev/null Wed Aug 9 05:49:50 2017
+++ src/sys/arch/arm/sunxi/sunxi_codec.h Wed Aug 9 05:49:50 2017
@@ -0,0 +1,129 @@
+/* $NetBSD: sunxi_codec.h,v 1.1.2.2 2017/08/09 05:49:50 snj Exp $ */
+
+/*-
+ * Copyright (c) 2014-2017 Jared 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.
+ */
+
+#ifndef _ARM_SUNXI_CODEC_H
+#define _ARM_SUNXI_CODEC_H
+
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+#include <dev/auconv.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#include "h3_codec.h"
+
+struct sunxi_codec_softc;
+
+struct sunxi_codec_conf {
+ const char *name;
+
+ /* initialize codec */
+ int (*init)(struct sunxi_codec_softc *);
+ /* toggle DAC/ADC mute */
+ void (*mute)(struct sunxi_codec_softc *, int, u_int);
+ /* mixer controls */
+ int (*set_port)(struct sunxi_codec_softc *,
+ mixer_ctrl_t *);
+ int (*get_port)(struct sunxi_codec_softc *,
+ mixer_ctrl_t *);
+ int (*query_devinfo)(struct sunxi_codec_softc *,
+ mixer_devinfo_t *);
+
+ /* register map */
+ bus_size_t DPC,
+ DAC_FIFOC,
+ DAC_FIFOS,
+ DAC_TXDATA,
+ ADC_FIFOC,
+ ADC_FIFOS,
+ ADC_RXDATA,
+ DAC_CNT,
+ ADC_CNT;
+};
+
+struct sunxi_codec_chan {
+ struct sunxi_codec_softc *ch_sc;
+ u_int ch_mode;
+
+ struct fdtbus_dma *ch_dma;
+ struct fdtbus_dma_req ch_req;
+
+ audio_params_t ch_params;
+
+ bus_addr_t ch_start_phys;
+ bus_addr_t ch_end_phys;
+ bus_addr_t ch_cur_phys;
+ int ch_blksize;
+
+ void (*ch_intr)(void *);
+ void *ch_intrarg;
+};
+
+struct sunxi_codec_dma {
+ LIST_ENTRY(sunxi_codec_dma) dma_list;
+ bus_dmamap_t dma_map;
+ void *dma_addr;
+ size_t dma_size;
+ bus_dma_segment_t dma_segs[1];
+ int dma_nsegs;
+};
+
+struct sunxi_codec_softc {
+ device_t sc_dev;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ bus_dma_tag_t sc_dmat;
+ int sc_phandle;
+ bus_addr_t sc_baseaddr;
+
+ struct sunxi_codec_conf *sc_cfg;
+ void *sc_codec_priv;
+
+ struct fdtbus_gpio_pin *sc_pin_pa;
+
+ LIST_HEAD(, sunxi_codec_dma) sc_dmalist;
+
+ kmutex_t sc_lock;
+ kmutex_t sc_intr_lock;
+
+ struct audio_format sc_format;
+ struct audio_encoding_set *sc_encodings;
+
+ struct sunxi_codec_chan sc_pchan;
+ struct sunxi_codec_chan sc_rchan;
+};
+
+#if NH3_CODEC > 0
+extern const struct sunxi_codec_conf sun8i_h3_codecconf;
+#define H3_CODEC_COMPATDATA \
+ { "allwinner,sun8i-h3-codec", (uintptr_t)&sun8i_h3_codecconf }
+#else
+#define H3_CODEC_COMPATDATA
+#endif
+
+#endif /* !_ARM_SUNXI_CODEC_H */