Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F161063873
D5050.id12642.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
48 KB
Referenced Files
None
Subscribers
None
D5050.id12642.diff
View Options
Index: sys/arm/allwinner/a10_clk.h
===================================================================
--- sys/arm/allwinner/a10_clk.h
+++ sys/arm/allwinner/a10_clk.h
@@ -106,10 +106,14 @@
#define CCM_GMAC_CLK_EXT_RGMII 0x1
#define CCM_GMAC_CLK_RGMII 0x2
+/* APB0_GATING */
+#define CCM_APB0_GATING_ADDA (1 << 0)
+
/* AHB_GATING_REG0 */
#define CCM_AHB_GATING_USB0 (1 << 0)
#define CCM_AHB_GATING_EHCI0 (1 << 1)
#define CCM_AHB_GATING_EHCI1 (1 << 3)
+#define CCM_AHB_GATING_DMA (1 << 6)
#define CCM_AHB_GATING_SDMMC0 (1 << 8)
#define CCM_AHB_GATING_EMAC (1 << 17)
#define CCM_AHB_GATING_SATA (1 << 25)
@@ -132,6 +136,11 @@
#define CCM_PLL_CFG_FACTOR_K_SHIFT 4
#define CCM_PLL_CFG_FACTOR_M 0x3
+#define CCM_PLL2_CFG_PREDIV 0x1f
+#define CCM_PLL2_CFG_PREDIV_SHIFT 0
+#define CCM_PLL2_CFG_POSTDIV 0x3c000000
+#define CCM_PLL2_CFG_POSTDIV_SHIFT 26
+
#define CCM_PLL6_CFG_SATA_CLKEN (1U << 14)
#define CCM_SD_CLK_SRC_SEL 0x3000000
@@ -146,6 +155,8 @@
#define CCM_SD_CLK_OPHASE_CTR_SHIFT 8
#define CCM_SD_CLK_DIV_RATIO_M 0xf
+#define CCM_AUDIO_CODEC_ENABLE (1U << 31)
+
#define CCM_CLK_REF_FREQ 24000000U
int a10_clk_usb_activate(void);
@@ -155,5 +166,7 @@
int a10_clk_ahci_activate(void);
int a10_clk_mmc_activate(int);
int a10_clk_mmc_cfg(int, int);
+int a10_clk_dmac_activate(void);
+int a10_clk_codec_activate(unsigned int);
#endif /* _A10_CLK_H_ */
Index: sys/arm/allwinner/a10_clk.c
===================================================================
--- sys/arm/allwinner/a10_clk.c
+++ sys/arm/allwinner/a10_clk.c
@@ -255,6 +255,50 @@
return ((CCM_CLK_REF_FREQ * n * k) / 2);
}
+static int
+a10_clk_pll2_set_rate(unsigned int freq)
+{
+ struct a10_ccm_softc *sc;
+ uint32_t reg_value;
+ unsigned int prediv, postdiv, n;
+
+ /*
+ * Audio Codec needs PLL2 to be either 24576000 Hz or 22579200 Hz
+ */
+ sc = a10_ccm_sc;
+
+ reg_value = ccm_read_4(sc, CCM_PLL2_CFG);
+ reg_value &= ~(CCM_PLL2_CFG_PREDIV | CCM_PLL2_CFG_POSTDIV |
+ CCM_PLL_CFG_FACTOR_N);
+
+ prediv = 21;
+ postdiv = 4;
+
+ switch (freq) {
+ case 24576000:
+ n = 86;
+ reg_value |= CCM_PLL_CFG_ENABLE;
+ break;
+ case 22579200:
+ n = 79;
+ reg_value |= CCM_PLL_CFG_ENABLE;
+ break;
+ case 0:
+ n = 1;
+ reg_value &= ~CCM_PLL_CFG_ENABLE;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ reg_value |= (prediv << CCM_PLL2_CFG_PREDIV_SHIFT);
+ reg_value |= (postdiv << CCM_PLL2_CFG_POSTDIV_SHIFT);
+ reg_value |= (n << CCM_PLL_CFG_FACTOR_N_SHIFT);
+ ccm_write_4(sc, CCM_PLL2_CFG, reg_value);
+
+ return (0);
+}
+
int
a10_clk_ahci_activate(void)
{
@@ -347,3 +391,46 @@
return (0);
}
+
+int
+a10_clk_dmac_activate(void)
+{
+ struct a10_ccm_softc *sc;
+ uint32_t reg_value;
+
+ sc = a10_ccm_sc;
+ if (sc == NULL)
+ return (ENXIO);
+
+ /* Gating AHB clock for DMA controller */
+ reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
+ reg_value |= CCM_AHB_GATING_DMA;
+ ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
+
+ return (0);
+}
+
+int
+a10_clk_codec_activate(unsigned int freq)
+{
+ struct a10_ccm_softc *sc;
+ uint32_t reg_value;
+
+ sc = a10_ccm_sc;
+ if (sc == NULL)
+ return (ENXIO);
+
+ a10_clk_pll2_set_rate(freq);
+
+ /* Gating APB clock for ADDA */
+ reg_value = ccm_read_4(sc, CCM_APB0_GATING);
+ reg_value |= CCM_APB0_GATING_ADDA;
+ ccm_write_4(sc, CCM_APB0_GATING, reg_value);
+
+ /* Enable audio codec clock */
+ reg_value = ccm_read_4(sc, CCM_AUDIO_CODEC_CLK);
+ reg_value |= CCM_AUDIO_CODEC_ENABLE;
+ ccm_write_4(sc, CCM_AUDIO_CODEC_CLK, reg_value);
+
+ return (0);
+}
Index: sys/arm/allwinner/a10_codec.c
===================================================================
--- /dev/null
+++ sys/arm/allwinner/a10_codec.c
@@ -0,0 +1,848 @@
+/*-
+ * Copyright (c) 2014-2016 Jared D. McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner A10/A20 Audio Codec
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/chip.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "sunxi_dma_if.h"
+#include "mixer_if.h"
+#include "gpio_if.h"
+
+#include "a10_clk.h"
+
+#define TX_TRIG_LEVEL 0xf
+#define RX_TRIG_LEVEL 0x7
+#define DRQ_CLR_CNT 0x3
+
+#define AC_DAC_DPC 0x00
+#define DAC_DPC_EN_DA 0x80000000
+#define AC_DAC_FIFOC 0x04
+#define DAC_FIFOC_FS_SHIFT 29
+#define DAC_FIFOC_FS_MASK (7U << DAC_FIFOC_FS_SHIFT)
+#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_SHIFT 24
+#define DAC_FIFOC_FIFO_MODE_MASK (3U << DAC_FIFOC_FIFO_MODE_SHIFT)
+#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_SHIFT 21
+#define DAC_FIFOC_DRQ_CLR_CNT_MASK (3U << DAC_FIFOC_DRQ_CLR_CNT_SHIFT)
+#define DAC_FIFOC_TX_TRIG_LEVEL_SHIFT 8
+#define DAC_FIFOC_TX_TRIG_LEVEL_MASK (0x7f << DAC_FIFOC_TX_TRIG_LEVEL_SHIFT)
+#define DAC_FIFOC_MONO_EN (1U << 6)
+#define DAC_FIFOC_TX_BITS (1U << 5)
+#define DAC_FIFOC_DRQ_EN (1U << 4)
+#define DAC_FIFOC_FIFO_FLUSH (1U << 0)
+#define AC_DAC_FIFOS 0x08
+#define AC_DAC_TXDATA 0x0c
+#define AC_DAC_ACTL 0x10
+#define DAC_ACTL_DACAREN (1U << 31)
+#define DAC_ACTL_DACALEN (1U << 30)
+#define DAC_ACTL_MIXEN (1U << 29)
+#define DAC_ACTL_DACPAS (1U << 8)
+#define DAC_ACTL_PAMUTE (1U << 6)
+#define DAC_ACTL_PAVOL_SHIFT 0
+#define DAC_ACTL_PAVOL_MASK (0x3f << DAC_ACTL_PAVOL_SHIFT)
+#define AC_ADC_FIFOC 0x1c
+#define ADC_FIFOC_FS_SHIFT 29
+#define ADC_FIFOC_FS_MASK (7U << ADC_FIFOC_FS_SHIFT)
+#define ADC_FS_48KHZ 0
+#define ADC_FIFOC_EN_AD (1U << 28)
+#define ADC_FIFOC_RX_FIFO_MODE (1U << 24)
+#define ADC_FIFOC_RX_TRIG_LEVEL_SHIFT 8
+#define ADC_FIFOC_RX_TRIG_LEVEL_MASK (0x1f << ADC_FIFOC_RX_TRIG_LEVEL_SHIFT)
+#define ADC_FIFOC_MONO_EN (1U << 7)
+#define ADC_FIFOC_RX_BITS (1U << 6)
+#define ADC_FIFOC_DRQ_EN (1U << 4)
+#define ADC_FIFOC_FIFO_FLUSH (1U << 1)
+#define AC_ADC_FIFOS 0x20
+#define AC_ADC_RXDATA 0x24
+#define AC_ADC_ACTL 0x28
+#define ADC_ACTL_ADCREN (1U << 31)
+#define ADC_ACTL_ADCLEN (1U << 30)
+#define ADC_ACTL_PREG1EN (1U << 29)
+#define ADC_ACTL_PREG2EN (1U << 28)
+#define ADC_ACTL_VMICEN (1U << 27)
+#define ADC_ACTL_ADCG_SHIFT 20
+#define ADC_ACTL_ADCG_MASK (7U << ADC_ACTL_ADCG_SHIFT)
+#define ADC_ACTL_ADCIS_SHIFT 17
+#define ADC_ACTL_ADCIS_MASK (7U << ADC_ACTL_ADCIS_SHIFT)
+#define ADC_IS_LINEIN 0
+#define ADC_IS_FMIN 1
+#define ADC_IS_MIC1 2
+#define ADC_IS_MIC2 3
+#define ADC_IS_MIC1_L_MIC2_R 4
+#define ADC_IS_MIC1_LR_MIC2_LR 5
+#define ADC_IS_OMIX 6
+#define ADC_IS_LINEIN_L_MIC1_R 7
+#define ADC_ACTL_LNRDF (1U << 16)
+#define ADC_ACTL_LNPREG_SHIFT 13
+#define ADC_ACTL_LNPREG_MASK (7U << ADC_ACTL_LNPREG_SHIFT)
+#define ADC_ACTL_PA_EN (1U << 4)
+#define ADC_ACTL_DDE (1U << 3)
+#define AC_DAC_CNT 0x30
+#define AC_ADC_CNT 0x34
+
+static uint32_t a10codec_fmt[] = {
+ SND_FORMAT(AFMT_S16_LE, 1, 0),
+ SND_FORMAT(AFMT_S16_LE, 2, 0),
+ 0
+};
+
+static struct pcmchan_caps a10codec_pcaps = { 8000, 192000, a10codec_fmt, 0 };
+static struct pcmchan_caps a10codec_rcaps = { 8000, 48000, a10codec_fmt, 0 };
+
+struct a10codec_info;
+
+struct a10codec_chinfo {
+ struct snd_dbuf *buffer;
+ struct pcm_channel *channel;
+ struct a10codec_info *parent;
+ bus_dmamap_t dmamap;
+ void *dmaaddr;
+ bus_addr_t physaddr;
+ bus_size_t fifo;
+ device_t dmac;
+ void *dmachan;
+
+ int dir;
+ int run;
+ uint32_t pos;
+ uint32_t format;
+ uint32_t blocksize;
+ uint32_t speed;
+};
+
+struct a10codec_info {
+ device_t dev;
+ struct resource *res;
+ struct resource *irq;
+ struct mtx *lock;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ bus_dma_tag_t dmat;
+ unsigned dmasize;
+ void *ih;
+
+ unsigned drqtype_codec;
+ unsigned drqtype_sdram;
+
+ struct a10codec_chinfo play;
+ struct a10codec_chinfo rec;
+};
+
+#define CODEC_READ(sc, reg) \
+ bus_space_read_4((sc)->bst, (sc)->bsh, (reg))
+#define CODEC_WRITE(sc, reg, val) \
+ bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val))
+
+/*
+ * Mixer interface
+ */
+
+static int
+a10codec_mixer_init(struct snd_mixer *m)
+{
+ struct a10codec_info *sc = mix_getdevinfo(m);
+ pcell_t prop[4];
+ phandle_t node;
+ device_t gpio;
+ uint32_t val;
+ ssize_t len;
+ int pin;
+
+ mix_setdevs(m, SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_RECLEV);
+ mix_setrecdevs(m, SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC);
+
+ /* Unmute input source to PA */
+ val = CODEC_READ(sc, AC_DAC_ACTL);
+ val |= DAC_ACTL_PAMUTE;
+ CODEC_WRITE(sc, AC_DAC_ACTL, val);
+
+ /* Enable PA */
+ val = CODEC_READ(sc, AC_ADC_ACTL);
+ val |= ADC_ACTL_PA_EN;
+ CODEC_WRITE(sc, AC_ADC_ACTL, val);
+
+ /* Unmute PA */
+ node = ofw_bus_get_node(sc->dev);
+ len = OF_getencprop(node, "pamute-gpio", prop, sizeof(prop));
+ if (len > 0 && (len / sizeof(prop[0])) == 4) {
+ gpio = OF_device_from_xref(prop[0]);
+ if (gpio != NULL) {
+ pin = prop[1] * 32 + prop[2];
+ GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT);
+ GPIO_PIN_SET(gpio, pin, GPIO_PIN_LOW);
+ }
+ }
+
+ return (0);
+}
+
+static const struct a10codec_mixer {
+ unsigned reg;
+ unsigned mask;
+ unsigned shift;
+} a10codec_mixers[SOUND_MIXER_NRDEVICES] = {
+ [SOUND_MIXER_VOLUME] = { AC_DAC_ACTL, DAC_ACTL_PAVOL_MASK,
+ DAC_ACTL_PAVOL_SHIFT },
+ [SOUND_MIXER_LINE] = { AC_ADC_ACTL, ADC_ACTL_LNPREG_MASK,
+ ADC_ACTL_LNPREG_SHIFT },
+ [SOUND_MIXER_RECLEV] = { AC_ADC_ACTL, ADC_ACTL_ADCG_MASK,
+ ADC_ACTL_ADCG_SHIFT },
+};
+
+static int
+a10codec_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left,
+ unsigned right)
+{
+ struct a10codec_info *sc = mix_getdevinfo(m);
+ uint32_t val;
+ unsigned nvol, max;
+
+ max = a10codec_mixers[dev].mask >> a10codec_mixers[dev].shift;
+ nvol = (left * max) / 100;
+
+ val = CODEC_READ(sc, a10codec_mixers[dev].reg);
+ val &= ~a10codec_mixers[dev].mask;
+ val |= (nvol << a10codec_mixers[dev].shift);
+ CODEC_WRITE(sc, a10codec_mixers[dev].reg, val);
+
+ left = right = (left * 100) / max;
+ return (left | (right << 8));
+}
+
+static uint32_t
+a10codec_mixer_setrecsrc(struct snd_mixer *m, uint32_t src)
+{
+ struct a10codec_info *sc = mix_getdevinfo(m);
+ uint32_t val;
+
+ val = CODEC_READ(sc, AC_ADC_ACTL);
+
+ switch (src) {
+ case SOUND_MASK_LINE: /* line-in */
+ val &= ~ADC_ACTL_ADCIS_MASK;
+ val |= (ADC_IS_LINEIN << ADC_ACTL_ADCIS_SHIFT);
+ break;
+ case SOUND_MASK_MIC: /* MIC1 */
+ val &= ~ADC_ACTL_ADCIS_MASK;
+ val |= (ADC_IS_MIC1 << ADC_ACTL_ADCIS_SHIFT);
+ break;
+ case SOUND_MASK_LINE1: /* MIC2 */
+ val &= ~ADC_ACTL_ADCIS_MASK;
+ val |= (ADC_IS_MIC2 << ADC_ACTL_ADCIS_SHIFT);
+ break;
+ default:
+ break;
+ }
+
+ CODEC_WRITE(sc, AC_ADC_ACTL, val);
+
+ switch ((val & ADC_ACTL_ADCIS_MASK) >> ADC_ACTL_ADCIS_SHIFT) {
+ case ADC_IS_LINEIN:
+ return (SOUND_MASK_LINE);
+ case ADC_IS_MIC1:
+ return (SOUND_MASK_MIC);
+ case ADC_IS_MIC2:
+ return (SOUND_MASK_LINE1);
+ default:
+ return (0);
+ }
+}
+
+static kobj_method_t a10codec_mixer_methods[] = {
+ KOBJMETHOD(mixer_init, a10codec_mixer_init),
+ KOBJMETHOD(mixer_set, a10codec_mixer_set),
+ KOBJMETHOD(mixer_setrecsrc, a10codec_mixer_setrecsrc),
+ KOBJMETHOD_END
+};
+MIXER_DECLARE(a10codec_mixer);
+
+
+/*
+ * Channel interface
+ */
+
+static void
+a10codec_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct a10codec_chinfo *ch = arg;
+
+ if (error != 0)
+ return;
+
+ ch->physaddr = segs[0].ds_addr;
+}
+
+static void
+a10codec_transfer(struct a10codec_chinfo *ch)
+{
+ bus_addr_t src, dst;
+ int error;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ src = ch->physaddr + ch->pos;
+ dst = ch->fifo;
+ } else {
+ src = ch->fifo;
+ dst = ch->physaddr + ch->pos;
+ }
+
+ error = SUNXI_DMA_TRANSFER(ch->dmac, ch->dmachan, src, dst,
+ ch->blocksize);
+ if (error) {
+ ch->run = 0;
+ device_printf(ch->parent->dev, "DMA transfer failed: %d\n", error);
+ }
+}
+
+static void
+a10codec_dmaconfig(struct a10codec_chinfo *ch)
+{
+ struct a10codec_info *sc = ch->parent;
+ struct sunxi_dma_config conf;
+
+ memset(&conf, 0, sizeof(conf));
+ conf.src_width = conf.dst_width = 16;
+ conf.src_burst_len = conf.dst_burst_len = 4;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ conf.dst_noincr = true;
+ conf.src_drqtype = sc->drqtype_sdram;
+ conf.dst_drqtype = sc->drqtype_codec;
+ } else {
+ conf.src_noincr = true;
+ conf.src_drqtype = sc->drqtype_codec;
+ conf.dst_drqtype = sc->drqtype_sdram;
+ }
+
+ SUNXI_DMA_SET_CONFIG(ch->dmac, ch->dmachan, &conf);
+}
+
+static void
+a10codec_dmaintr(void *priv)
+{
+ struct a10codec_chinfo *ch = priv;
+ unsigned bufsize;
+
+ bufsize = sndbuf_getsize(ch->buffer);
+
+ ch->pos += ch->blocksize;
+ if (ch->pos >= bufsize)
+ ch->pos -= bufsize;
+
+ if (ch->run) {
+ chn_intr(ch->channel);
+ a10codec_transfer(ch);
+ }
+}
+
+static unsigned
+a10codec_fs(struct a10codec_chinfo *ch)
+{
+ switch (ch->speed) {
+ case 48000:
+ return (DAC_FS_48KHZ);
+ case 24000:
+ return (DAC_FS_24KHZ);
+ case 12000:
+ return (DAC_FS_12KHZ);
+ case 192000:
+ return (DAC_FS_192KHZ);
+ case 32000:
+ return (DAC_FS_32KHZ);
+ case 16000:
+ return (DAC_FS_16KHZ);
+ case 8000:
+ return (DAC_FS_8KHZ);
+ case 96000:
+ return (DAC_FS_96KHZ);
+ default:
+ return (DAC_FS_48KHZ);
+ }
+}
+
+static void
+a10codec_start(struct a10codec_chinfo *ch)
+{
+ struct a10codec_info *sc = ch->parent;
+ uint32_t val;
+
+ ch->pos = 0;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ /* Flush DAC FIFO */
+ CODEC_WRITE(sc, AC_DAC_FIFOC, DAC_FIFOC_FIFO_FLUSH);
+
+ /* Clear DAC FIFO status */
+ CODEC_WRITE(sc, AC_DAC_FIFOS, CODEC_READ(sc, AC_DAC_FIFOS));
+
+ /* Enable DAC analog left/right channels and output mixer */
+ val = CODEC_READ(sc, AC_DAC_ACTL);
+ val |= DAC_ACTL_DACAREN;
+ val |= DAC_ACTL_DACALEN;
+ val |= DAC_ACTL_DACPAS;
+ CODEC_WRITE(sc, AC_DAC_ACTL, val);
+
+ /* Configure DAC DMA channel */
+ a10codec_dmaconfig(ch);
+
+ /* Configure DAC FIFO */
+ CODEC_WRITE(sc, AC_DAC_FIFOC,
+ (AFMT_CHANNEL(ch->format) == 1 ? DAC_FIFOC_MONO_EN : 0) |
+ (a10codec_fs(ch) << DAC_FIFOC_FS_SHIFT) |
+ (FIFO_MODE_16_15_0 << DAC_FIFOC_FIFO_MODE_SHIFT) |
+ (DRQ_CLR_CNT << DAC_FIFOC_DRQ_CLR_CNT_SHIFT) |
+ (TX_TRIG_LEVEL << DAC_FIFOC_TX_TRIG_LEVEL_SHIFT));
+
+ /* Enable DAC DRQ */
+ val = CODEC_READ(sc, AC_DAC_FIFOC);
+ val |= DAC_FIFOC_DRQ_EN;
+ CODEC_WRITE(sc, AC_DAC_FIFOC, val);
+ } else {
+ /* Flush ADC FIFO */
+ CODEC_WRITE(sc, AC_ADC_FIFOC, ADC_FIFOC_FIFO_FLUSH);
+
+ /* Clear ADC FIFO status */
+ CODEC_WRITE(sc, AC_ADC_FIFOS, CODEC_READ(sc, AC_ADC_FIFOS));
+
+ /* Enable ADC analog left/right channels */
+ val = CODEC_READ(sc, AC_ADC_ACTL);
+ val |= ADC_ACTL_ADCREN;
+ val |= ADC_ACTL_ADCLEN;
+ CODEC_WRITE(sc, AC_ADC_ACTL, val);
+
+ /* Configure ADC DMA channel */
+ a10codec_dmaconfig(ch);
+
+ /* Configure ADC FIFO */
+ CODEC_WRITE(sc, AC_ADC_FIFOC,
+ ADC_FIFOC_EN_AD |
+ ADC_FIFOC_RX_FIFO_MODE |
+ (AFMT_CHANNEL(ch->format) == 1 ? ADC_FIFOC_MONO_EN : 0) |
+ (a10codec_fs(ch) << ADC_FIFOC_FS_SHIFT) |
+ (RX_TRIG_LEVEL << ADC_FIFOC_RX_TRIG_LEVEL_SHIFT));
+
+ /* Enable ADC DRQ */
+ val = CODEC_READ(sc, AC_ADC_FIFOC);
+ val |= ADC_FIFOC_DRQ_EN;
+ CODEC_WRITE(sc, AC_ADC_FIFOC, val);
+ }
+
+ /* Start DMA transfer */
+ a10codec_transfer(ch);
+}
+
+static void
+a10codec_stop(struct a10codec_chinfo *ch)
+{
+ struct a10codec_info *sc = ch->parent;
+ uint32_t val;
+
+ /* Disable DMA channel */
+ SUNXI_DMA_HALT(ch->dmac, ch->dmachan);
+
+ if (ch->dir == PCMDIR_PLAY) {
+ /* Disable DAC analog left/right channels and output mixer */
+ val = CODEC_READ(sc, AC_DAC_ACTL);
+ val &= ~DAC_ACTL_DACAREN;
+ val &= ~DAC_ACTL_DACALEN;
+ val &= ~DAC_ACTL_DACPAS;
+ CODEC_WRITE(sc, AC_DAC_ACTL, val);
+
+ /* Disable DAC DRQ */
+ CODEC_WRITE(sc, AC_DAC_FIFOC, 0);
+ } else {
+ /* Disable ADC analog left/right channels */
+ val = CODEC_READ(sc, AC_ADC_ACTL);
+ val &= ~ADC_ACTL_ADCREN;
+ val &= ~ADC_ACTL_ADCLEN;
+ CODEC_WRITE(sc, AC_ADC_ACTL, val);
+
+ /* Disable ADC DRQ */
+ CODEC_WRITE(sc, AC_ADC_FIFOC, 0);
+ }
+}
+
+static void *
+a10codec_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct a10codec_info *sc = devinfo;
+ struct a10codec_chinfo *ch = dir == PCMDIR_PLAY ? &sc->play : &sc->rec;
+ int error;
+
+ ch->parent = sc;
+ ch->channel = c;
+ ch->buffer = b;
+ ch->dir = dir;
+ ch->fifo = rman_get_start(sc->res) +
+ (dir == PCMDIR_REC ? AC_ADC_RXDATA : AC_DAC_TXDATA);
+
+ ch->dmac = devclass_get_device(devclass_find("a10dmac"), 0);
+ if (ch->dmac == NULL) {
+ device_printf(sc->dev, "cannot find DMA controller\n");
+ return (NULL);
+ }
+ ch->dmachan = SUNXI_DMA_ALLOC(ch->dmac, false, a10codec_dmaintr, ch);
+ if (ch->dmachan == NULL) {
+ device_printf(sc->dev, "cannot allocate DMA channel\n");
+ return (NULL);
+ }
+
+ error = bus_dmamem_alloc(sc->dmat, &ch->dmaaddr,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &ch->dmamap);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot allocate channel buffer\n");
+ return (NULL);
+ }
+ error = bus_dmamap_load(sc->dmat, ch->dmamap, ch->dmaaddr,
+ sc->dmasize, a10codec_dmamap_cb, ch, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ device_printf(sc->dev, "cannot load DMA map\n");
+ return (NULL);
+ }
+ memset(ch->dmaaddr, 0, sc->dmasize);
+
+ if (sndbuf_setup(ch->buffer, ch->dmaaddr, sc->dmasize) != 0) {
+ device_printf(sc->dev, "cannot setup sndbuf\n");
+ return (NULL);
+ }
+
+ return (ch);
+}
+
+static int
+a10codec_chan_free(kobj_t obj, void *data)
+{
+ struct a10codec_chinfo *ch = data;
+ struct a10codec_info *sc = ch->parent;
+
+ SUNXI_DMA_FREE(ch->dmac, ch->dmachan);
+ bus_dmamap_unload(sc->dmat, ch->dmamap);
+ bus_dmamem_free(sc->dmat, ch->dmaaddr, ch->dmamap);
+
+ return (0);
+}
+
+static int
+a10codec_chan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ struct a10codec_chinfo *ch = data;
+
+ ch->format = format;
+
+ return (0);
+}
+
+static uint32_t
+a10codec_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ struct a10codec_chinfo *ch = data;
+
+ /*
+ * The codec supports full duplex operation but both DAC and ADC
+ * use the same source clock (PLL2). Limit the available speeds to
+ * those supported by a 24576000 Hz input.
+ */
+ switch (speed) {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ ch->speed = speed;
+ break;
+ case 96000:
+ case 192000:
+ /* 96 KHz / 192 KHz mode only supported for playback */
+ if (ch->dir == PCMDIR_PLAY) {
+ ch->speed = speed;
+ } else {
+ ch->speed = 48000;
+ }
+ break;
+ case 44100:
+ ch->speed = 48000;
+ break;
+ case 22050:
+ ch->speed = 24000;
+ break;
+ case 11025:
+ ch->speed = 12000;
+ break;
+ default:
+ ch->speed = 48000;
+ break;
+ }
+
+ return (ch->speed);
+}
+
+static uint32_t
+a10codec_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ struct a10codec_chinfo *ch = data;
+
+ ch->blocksize = blocksize & ~3;
+
+ return (ch->blocksize);
+}
+
+static int
+a10codec_chan_trigger(kobj_t obj, void *data, int go)
+{
+ struct a10codec_chinfo *ch = data;
+ struct a10codec_info *sc = ch->parent;
+
+ if (!PCMTRIG_COMMON(go))
+ return (0);
+
+ snd_mtxlock(sc->lock);
+ switch (go) {
+ case PCMTRIG_START:
+ ch->run = 1;
+ a10codec_start(ch);
+ break;
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ ch->run = 0;
+ a10codec_stop(ch);
+ break;
+ default:
+ break;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static uint32_t
+a10codec_chan_getptr(kobj_t obj, void *data)
+{
+ struct a10codec_chinfo *ch = data;
+
+ return (ch->pos);
+}
+
+static struct pcmchan_caps *
+a10codec_chan_getcaps(kobj_t obj, void *data)
+{
+ struct a10codec_chinfo *ch = data;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ return (&a10codec_pcaps);
+ } else {
+ return (&a10codec_rcaps);
+ }
+}
+
+static kobj_method_t a10codec_chan_methods[] = {
+ KOBJMETHOD(channel_init, a10codec_chan_init),
+ KOBJMETHOD(channel_free, a10codec_chan_free),
+ KOBJMETHOD(channel_setformat, a10codec_chan_setformat),
+ KOBJMETHOD(channel_setspeed, a10codec_chan_setspeed),
+ KOBJMETHOD(channel_setblocksize, a10codec_chan_setblocksize),
+ KOBJMETHOD(channel_trigger, a10codec_chan_trigger),
+ KOBJMETHOD(channel_getptr, a10codec_chan_getptr),
+ KOBJMETHOD(channel_getcaps, a10codec_chan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(a10codec_chan);
+
+
+/*
+ * Device interface
+ */
+
+static int
+a10codec_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-codec"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner Audio Codec");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a10codec_attach(device_t dev)
+{
+ struct a10codec_info *sc;
+ char status[SND_STATUSLEN];
+ int rid, error;
+ uint32_t val;
+
+ sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->dev = dev;
+ sc->lock = snd_mtxcreate(device_get_nameunit(dev), "a10codec softc");
+
+ rid = 0;
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->res == NULL) {
+ device_printf(dev, "unable to map memory\n");
+ error = ENXIO;
+ goto fail;
+ }
+ sc->bst = rman_get_bustag(sc->res);
+ sc->bsh = rman_get_bushandle(sc->res);
+
+ rid = 0;
+ sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irq == NULL) {
+ device_printf(dev, "cannot allocate IRQ resources\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ /* XXX DRQ types should come from FDT, but how? */
+ if (ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-codec")) {
+ sc->drqtype_codec = 19;
+ sc->drqtype_sdram = 22;
+ } else {
+ device_printf(dev, "DRQ types not known for this SoC\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc->dmasize = 131072;
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(dev),
+ 4, sc->dmasize, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ sc->dmasize, 1, /* maxsize, nsegs */
+ sc->dmasize, 0, /* maxsegsize, flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->dmat);
+ if (error != 0) {
+ device_printf(dev, "cannot create DMA tag\n");
+ goto fail;
+ }
+
+ /* Activate audio codec clock */
+ a10_clk_codec_activate(24576000);
+
+ /* Enable DAC */
+ val = CODEC_READ(sc, AC_DAC_DPC);
+ val |= DAC_DPC_EN_DA;
+ CODEC_WRITE(sc, AC_DAC_DPC, val);
+
+#ifdef notdef
+ error = snd_setup_intr(dev, sc->irq, INTR_MPSAFE, a10codec_intr, sc,
+ &sc->ih);
+ if (error != 0) {
+ device_printf(dev, "could not setup interrupt handler\n");
+ goto fail;
+ }
+#endif
+
+ if (mixer_init(dev, &a10codec_mixer_class, sc)) {
+ device_printf(dev, "mixer_init failed\n");
+ goto fail;
+ }
+
+ pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
+
+ if (pcm_register(dev, sc, 1, 1)) {
+ device_printf(dev, "pcm_register failed\n");
+ goto fail;
+ }
+
+ pcm_addchan(dev, PCMDIR_PLAY, &a10codec_chan_class, sc);
+ pcm_addchan(dev, PCMDIR_REC, &a10codec_chan_class, sc);
+
+ snprintf(status, SND_STATUSLEN, "at %s", ofw_bus_get_name(dev));
+ pcm_setstatus(dev, status);
+
+ return (0);
+
+fail:
+ if (sc->res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res);
+ if (sc->irq != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
+
+ return (error);
+}
+
+static device_method_t a10codec_pcm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a10codec_probe),
+ DEVMETHOD(device_attach, a10codec_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t a10codec_pcm_driver = {
+ "pcm",
+ a10codec_pcm_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(a10codec, simplebus, a10codec_pcm_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(a10codec, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(a10codec, 1);
Index: sys/arm/allwinner/a10_dmac.h
===================================================================
--- /dev/null
+++ sys/arm/allwinner/a10_dmac.h
@@ -0,0 +1,158 @@
+/*-
+ * Copyright (c) 2014-2016 Jared D. McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _A10_DMAC_H_
+#define _A10_DMAC_H_
+
+#define AWIN_DMA_IRQ_EN_REG 0x0000
+#define AWIN_DMA_IRQ_PEND_STA_REG 0x0004
+#define AWIN_NDMA_AUTO_GATE_REG 0x0008
+#define AWIN_NDMA_REG(n) (0x100+0x20*(n))
+#define AWIN_NDMA_CTL_REG 0x0000
+#define AWIN_NDMA_SRC_ADDR_REG 0x0004
+#define AWIN_NDMA_DEST_ADDR_REG 0x0008
+#define AWIN_NDMA_BC_REG 0x000c
+#define AWIN_DDMA_REG(n) (0x300+0x20*(n))
+#define AWIN_DDMA_CTL_REG 0x0000
+#define AWIN_DDMA_SRC_START_ADDR_REG 0x0004
+#define AWIN_DDMA_DEST_START_ADDR_REG 0x0008
+#define AWIN_DDMA_BC_REG 0x000c
+#define AWIN_DDMA_PARA_REG 0x0018
+#define AWIN_DMA_IRQ_END_MASK 0xaaaaaaaa
+#define AWIN_DMA_IRQ_HF_MASK 0x55555555
+#define AWIN_DMA_IRQ_DDMA 0xffff0000
+#define AWIN_DMA_IRQ_DDMA_END(n) (1U << (17+2*(n)))
+#define AWIN_DMA_IRQ_DDMA_HF(n) (1U << (16+2*(n)))
+#define AWIN_DMA_IRQ_NDMA 0x0000ffff
+#define AWIN_DMA_IRQ_NDMA_END(n) (1U << (1+2*(n)))
+#define AWIN_DMA_IRQ_NDMA_HF(n) (1U << (0+2*(n)))
+#define AWIN_NDMA_AUTO_GATING_DIS (1U << 16)
+#define AWIN_DMA_CTL_DST_DATA_WIDTH_SHIFT 25
+#define AWIN_DMA_CTL_DST_DATA_WIDTH_MASK (3U << AWIN_DMA_CTL_DST_DATA_WIDTH_SHIFT)
+#define AWIN_DMA_CTL_DATA_WIDTH_8 0
+#define AWIN_DMA_CTL_DATA_WIDTH_16 1
+#define AWIN_DMA_CTL_DATA_WIDTH_32 2
+#define AWIN_DMA_CTL_DST_BURST_LEN_SHIFT 23
+#define AWIN_DMA_CTL_DST_BURST_LEN_MASK (3 << AWIN_DMA_CTL_DST_BURST_LEN_SHIFT)
+#define AWIN_DMA_CTL_BURST_LEN_1 0
+#define AWIN_DMA_CTL_BURST_LEN_4 1
+#define AWIN_DMA_CTL_BURST_LEN_8 2
+#define AWIN_DMA_CTL_DST_DRQ_TYPE_SHIFT 16
+#define AWIN_DMA_CTL_DST_DRQ_TYPE_MASK (0x1f << AWIN_DMA_CTL_DST_DRQ_TYPE_SHIFT)
+#define AWIN_DMA_CTL_BC_REMAINING (1U << 15)
+#define AWIN_DMA_CTL_SRC_DATA_WIDTH_SHIFT 9
+#define AWIN_DMA_CTL_SRC_DATA_WIDTH_MASK (3U << AWIN_DMA_CTL_SRC_DATA_WIDTH_SHIFT)
+#define AWIN_DMA_CTL_SRC_BURST_LEN_SHIFT 7
+#define AWIN_DMA_CTL_SRC_BURST_LEN_MASK (3U << AWIN_DMA_CTL_SRC_BURST_LEN_SHIFT)
+#define AWIN_DMA_CTL_SRC_DRQ_TYPE_SHIFT 0
+#define AWIN_DMA_CTL_SRC_DRQ_TYPE_MASK (0x1f << AWIN_DMA_CTL_SRC_DRQ_TYPE_SHIFT)
+#define AWIN_NDMA_CTL_DMA_LOADING (1U << 31)
+#define AWIN_NDMA_CTL_DMA_CONTIN_MODE (1U << 30)
+#define AWIN_NDMA_CTL_WAIT_STATE_LOG2_SHIFT 27
+#define AWIN_NDMA_CTL_WAIT_STATE_LOG2_MASK (7U << AWIN_NDMA_CTL_WAIT_STATE_LOG2_SHIFT)
+#define AWIN_NDMA_CTL_DST_NON_SECURE (1U << 22)
+#define AWIN_NDMA_CTL_DST_ADDR_NOINCR (1U << 21)
+#define AWIN_NDMA_CTL_DRQ_IRO 0
+#define AWIN_NDMA_CTL_DRQ_IR1 1
+#define AWIN_NDMA_CTL_DRQ_SPDIF 2
+#define AWIN_NDMA_CTL_DRQ_IISO 3
+#define AWIN_NDMA_CTL_DRQ_IIS1 4
+#define AWIN_NDMA_CTL_DRQ_AC97 5
+#define AWIN_NDMA_CTL_DRQ_IIS2 6
+#define AWIN_NDMA_CTL_DRQ_UARTO 8
+#define AWIN_NDMA_CTL_DRQ_UART1 9
+#define AWIN_NDMA_CTL_DRQ_UART2 10
+#define AWIN_NDMA_CTL_DRQ_UART3 11
+#define AWIN_NDMA_CTL_DRQ_UART4 12
+#define AWIN_NDMA_CTL_DRQ_UART5 13
+#define AWIN_NDMA_CTL_DRQ_UART6 14
+#define AWIN_NDMA_CTL_DRQ_UART7 15
+#define AWIN_NDMA_CTL_DRQ_DDC 16
+#define AWIN_NDMA_CTL_DRQ_USB_EP1 17
+#define AWIN_NDMA_CTL_DRQ_CODEC 19
+#define AWIN_NDMA_CTL_DRQ_SRAM 21
+#define AWIN_NDMA_CTL_DRQ_SDRAM 22
+#define AWIN_NDMA_CTL_DRQ_TP_AD 23
+#define AWIN_NDMA_CTL_DRQ_SPI0 24
+#define AWIN_NDMA_CTL_DRQ_SPI1 25
+#define AWIN_NDMA_CTL_DRQ_SPI2 26
+#define AWIN_NDMA_CTL_DRQ_SPI3 27
+#define AWIN_NDMA_CTL_DRQ_USB_EP2 28
+#define AWIN_NDMA_CTL_DRQ_USB_EP3 29
+#define AWIN_NDMA_CTL_DRQ_USB_EP4 30
+#define AWIN_NDMA_CTL_DRQ_USB_EP5 31
+#define AWIN_NDMA_CTL_SRC_NON_SECURE (1U << 6)
+#define AWIN_NDMA_CTL_SRC_ADDR_NOINCR (1U << 5)
+#define AWIN_NDMA_BC_COUNT 0x0003ffff
+#define AWIN_DDMA_CTL_DMA_LOADING (1U << 31)
+#define AWIN_DDMA_CTL_BUSY (1U << 30)
+#define AWIN_DDMA_CTL_DMA_CONTIN_MODE (1U << 29)
+#define AWIN_DDMA_CTL_DST_NON_SECURE (1U << 28)
+#define AWIN_DDMA_CTL_DST_ADDR_MODE_SHIFT 21
+#define AWIN_DDMA_CTL_DST_ADDR_MODE_MASK (3U << AWIN_DDMA_CTL_DST_ADDR_MODE_SHIFT)
+#define AWIN_DDMA_CTL_DMA_ADDR_LINEAR 0
+#define AWIN_DDMA_CTL_DMA_ADDR_IO 1
+#define AWIN_DDMA_CTL_DMA_ADDR_HPAGE 2
+#define AWIN_DDMA_CTL_DMA_ADDR_VPAGE 3
+#define AWIN_DDMA_CTL_DST_DRQ_TYPE_SHIFT 16
+#define AWIN_DDMA_CTL_DST_DRQ_TYPE_MASK (0x1f << AWIN_DDMA_CTL_DST_DRQ_TYPE_SHIFT)
+#define AWIN_DDMA_CTL_DRQ_SRAM 0
+#define AWIN_DDMA_CTL_DRQ_SDRAM 1
+#define AWIN_DDMA_CTL_DRQ_NFC 3
+#define AWIN_DDMA_CTL_DRQ_USB0 4
+#define AWIN_DDMA_CTL_DRQ_EMAC_TX 6
+#define AWIN_DDMA_CTL_DRQ_EMAC_RX 7
+#define AWIN_DDMA_CTL_DRQ_SPI1_TX 8
+#define AWIN_DDMA_CTL_DRQ_SPI1_RX 9
+#define AWIN_DDMA_CTL_DRQ_SS_TX 10
+#define AWIN_DDMA_CTL_DRQ_SS_RX 11
+#define AWIN_DDMA_CTL_DRQ_TCON0 14
+#define AWIN_DDMA_CTL_DRQ_TCON1 15
+#define AWIN_DDMA_CTL_DRQ_MS_TX 23
+#define AWIN_DDMA_CTL_DRQ_MS_RX 23
+#define AWIN_DDMA_CTL_DRQ_HDMI_AUDIO 24
+#define AWIN_DDMA_CTL_DRQ_SPI0_TX 26
+#define AWIN_DDMA_CTL_DRQ_SPI0_RX 27
+#define AWIN_DDMA_CTL_DRQ_SPI2_TX 28
+#define AWIN_DDMA_CTL_DRQ_SPI2_RX 29
+#define AWIN_DDMA_CTL_DRQ_SPI3_TX 30
+#define AWIN_DDMA_CTL_DRQ_SPI3_RX 31
+#define AWIN_DDMA_CTL_SRC_NON_SECURE (1U << 12)
+#define AWIN_DDMA_CTL_SRC_ADDR_MODE_SHIFT 5
+#define AWIN_DDMA_CTL_SRC_ADDR_MODE_MASK (3U << AWIN_DDMA_CTL_SRC_ADDR_MODE_SHIFT)
+#define AWIN_DDMA_BC_COUNT 0x00003fff
+#define AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_SHIFT 24
+#define AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_MASK (0xff << AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_SHIFT)
+#define AWIN_DDMA_PARA_DST_WAIT_CYC_SHIFT 16
+#define AWIN_DDMA_PARA_DST_WAIT_CYC_MASK (0xff << AWIN_DDMA_PARA_DST_WAIT_CYC_SHIFT)
+#define AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_SHIFT 8
+#define AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_MASK (0xff << AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_SHIFT)
+#define AWIN_DDMA_PARA_SRC_WAIT_CYC_SHIFT 0
+#define AWIN_DDMA_PARA_SRC_WAIT_CYC_MASK (0xff << AWIN_DDMA_PARA_SRC_WAIT_CYC_SHIFT)
+
+#endif /* !_A10_DMAC_H_ */
Index: sys/arm/allwinner/a10_dmac.c
===================================================================
--- /dev/null
+++ sys/arm/allwinner/a10_dmac.c
@@ -0,0 +1,464 @@
+/*-
+ * Copyright (c) 2014-2016 Jared D. McNeill <jmcneill@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner A10/A20 DMA controller
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <machine/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "sunxi_dma_if.h"
+
+#include "a10_dmac.h"
+#include "a10_clk.h"
+
+#define NDMA_CHANNELS 8
+#define DDMA_CHANNELS 8
+
+enum a10dmac_type {
+ CH_NDMA,
+ CH_DDMA
+};
+
+struct a10dmac_softc;
+
+struct a10dmac_channel {
+ struct a10dmac_softc * ch_sc;
+ uint8_t ch_index;
+ enum a10dmac_type ch_type;
+ void (*ch_callback)(void *);
+ void * ch_callbackarg;
+ uint32_t ch_regoff;
+};
+
+struct a10dmac_softc {
+ device_t sc_dev;
+ struct resource * sc_res;
+ struct resource * sc_irq;
+ struct mtx sc_mtx;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ void * sc_ih;
+
+ struct a10dmac_channel sc_ndma_channels[NDMA_CHANNELS];
+ struct a10dmac_channel sc_ddma_channels[DDMA_CHANNELS];
+};
+
+#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))
+#define DMACH_READ(ch, reg) \
+ DMA_READ((ch)->ch_sc, (reg) + (ch)->ch_regoff)
+#define DMACH_WRITE(ch, reg, val) \
+ DMA_WRITE((ch)->ch_sc, (reg) + (ch)->ch_regoff, (val))
+
+static void a10dmac_intr(void *);
+
+static int
+a10dmac_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-dma"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Allwinner DMA controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a10dmac_attach(device_t dev)
+{
+ struct a10dmac_softc *sc;
+ unsigned int index;
+ int rid, error;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ mtx_init(&sc->sc_mtx, "a10 dmac", NULL, MTX_SPIN);
+
+ rid = 0;
+ sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_res == NULL) {
+ device_printf(dev, "unable to map memory\n");
+ error = ENXIO;
+ goto fail;
+ }
+ sc->sc_bst = rman_get_bustag(sc->sc_res);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_res);
+
+ rid = 0;
+ sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq == NULL) {
+ device_printf(dev, "cannot allocate IRQ resources\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ /* Activate DMA controller clock */
+ a10_clk_dmac_activate();
+
+ /* Disable all interrupts and clear pending status */
+ DMA_WRITE(sc, AWIN_DMA_IRQ_EN_REG, 0);
+ DMA_WRITE(sc, AWIN_DMA_IRQ_PEND_STA_REG, ~0);
+
+ /* Initialize channels */
+ for (index = 0; index < NDMA_CHANNELS; index++) {
+ sc->sc_ndma_channels[index].ch_sc = sc;
+ sc->sc_ndma_channels[index].ch_index = index;
+ sc->sc_ndma_channels[index].ch_type = CH_NDMA;
+ sc->sc_ndma_channels[index].ch_callback = NULL;
+ sc->sc_ndma_channels[index].ch_callbackarg = NULL;
+ sc->sc_ndma_channels[index].ch_regoff = AWIN_NDMA_REG(index);
+ DMACH_WRITE(&sc->sc_ndma_channels[index], AWIN_NDMA_CTL_REG, 0);
+ }
+ for (index = 0; index < DDMA_CHANNELS; index++) {
+ sc->sc_ddma_channels[index].ch_sc = sc;
+ sc->sc_ddma_channels[index].ch_index = index;
+ sc->sc_ddma_channels[index].ch_type = CH_DDMA;
+ sc->sc_ddma_channels[index].ch_callback = NULL;
+ sc->sc_ddma_channels[index].ch_callbackarg = NULL;
+ sc->sc_ddma_channels[index].ch_regoff = AWIN_DDMA_REG(index);
+ DMACH_WRITE(&sc->sc_ddma_channels[index], AWIN_DDMA_CTL_REG, 0);
+ }
+
+ error = bus_setup_intr(dev, sc->sc_irq, INTR_MPSAFE | INTR_TYPE_MISC,
+ NULL, a10dmac_intr, sc, &sc->sc_ih);
+ if (error != 0) {
+ device_printf(dev, "could not setup interrupt handler\n");
+ goto fail;
+ }
+
+ return (0);
+
+fail:
+ if (sc->sc_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_res);
+ if (sc->sc_irq != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq);
+
+ return (error);
+}
+
+static void
+a10dmac_intr(void *priv)
+{
+ struct a10dmac_softc *sc = priv;
+ uint32_t sta, bit, mask;
+ uint8_t index;
+
+ sta = DMA_READ(sc, AWIN_DMA_IRQ_PEND_STA_REG);
+ DMA_WRITE(sc, AWIN_DMA_IRQ_PEND_STA_REG, sta);
+
+ while ((bit = ffs(sta & AWIN_DMA_IRQ_END_MASK)) != 0) {
+ mask = (1U << (bit - 1));
+ sta &= ~mask;
+ index = ((bit - 1) / 2) & 7;
+ if (mask & AWIN_DMA_IRQ_NDMA) {
+ if (sc->sc_ndma_channels[index].ch_callback == NULL)
+ continue;
+ sc->sc_ndma_channels[index].ch_callback(
+ sc->sc_ndma_channels[index].ch_callbackarg);
+ } else {
+ if (sc->sc_ddma_channels[index].ch_callback == NULL)
+ continue;
+ sc->sc_ddma_channels[index].ch_callback(
+ sc->sc_ddma_channels[index].ch_callbackarg);
+ }
+ }
+}
+
+static uint32_t
+a10dmac_read_ctl(struct a10dmac_channel *ch)
+{
+ if (ch->ch_type == CH_NDMA) {
+ return (DMACH_READ(ch, AWIN_NDMA_CTL_REG));
+ } else {
+ return (DMACH_READ(ch, AWIN_DDMA_CTL_REG));
+ }
+}
+
+static void
+a10dmac_write_ctl(struct a10dmac_channel *ch, uint32_t val)
+{
+ if (ch->ch_type == CH_NDMA) {
+ DMACH_WRITE(ch, AWIN_NDMA_CTL_REG, val);
+ } else {
+ DMACH_WRITE(ch, AWIN_DDMA_CTL_REG, val);
+ }
+}
+
+static int
+a10dmac_set_config(device_t dev, void *priv, const struct sunxi_dma_config *cfg)
+{
+ struct a10dmac_channel *ch = priv;
+ uint32_t val;
+ unsigned int dst_dw, dst_bl, src_dw, src_bl;
+
+ switch (cfg->dst_width) {
+ case 8:
+ dst_dw = AWIN_DMA_CTL_DATA_WIDTH_8;
+ break;
+ case 16:
+ dst_dw = AWIN_DMA_CTL_DATA_WIDTH_16;
+ break;
+ case 32:
+ dst_dw = AWIN_DMA_CTL_DATA_WIDTH_32;
+ break;
+ default:
+ return (EINVAL);
+ }
+ switch (cfg->dst_burst_len) {
+ case 1:
+ dst_bl = AWIN_DMA_CTL_BURST_LEN_1;
+ break;
+ case 4:
+ dst_bl = AWIN_DMA_CTL_BURST_LEN_4;
+ break;
+ case 8:
+ dst_bl = AWIN_DMA_CTL_BURST_LEN_8;
+ break;
+ default:
+ return (EINVAL);
+ }
+ switch (cfg->src_width) {
+ case 8:
+ src_dw = AWIN_DMA_CTL_DATA_WIDTH_8;
+ break;
+ case 16:
+ src_dw = AWIN_DMA_CTL_DATA_WIDTH_16;
+ break;
+ case 32:
+ src_dw = AWIN_DMA_CTL_DATA_WIDTH_32;
+ break;
+ default:
+ return (EINVAL);
+ }
+ switch (cfg->src_burst_len) {
+ case 1:
+ src_bl = AWIN_DMA_CTL_BURST_LEN_1;
+ break;
+ case 4:
+ src_bl = AWIN_DMA_CTL_BURST_LEN_4;
+ break;
+ case 8:
+ src_bl = AWIN_DMA_CTL_BURST_LEN_8;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ val = (dst_dw << AWIN_DMA_CTL_DST_DATA_WIDTH_SHIFT) |
+ (dst_bl << AWIN_DMA_CTL_DST_BURST_LEN_SHIFT) |
+ (cfg->dst_drqtype << AWIN_DMA_CTL_DST_DRQ_TYPE_SHIFT) |
+ (src_dw << AWIN_DMA_CTL_SRC_DATA_WIDTH_SHIFT) |
+ (src_bl << AWIN_DMA_CTL_SRC_BURST_LEN_SHIFT) |
+ (cfg->src_drqtype << AWIN_DMA_CTL_SRC_DRQ_TYPE_SHIFT);
+ if (cfg->dst_noincr) {
+ val |= AWIN_NDMA_CTL_DST_ADDR_NOINCR;
+ }
+ if (cfg->src_noincr) {
+ val |= AWIN_NDMA_CTL_SRC_ADDR_NOINCR;
+ }
+
+ if (ch->ch_type == CH_NDMA) {
+ DMACH_WRITE(ch, AWIN_NDMA_CTL_REG, val);
+ } else {
+ DMACH_WRITE(ch, AWIN_DDMA_CTL_REG, val);
+ }
+
+ return (0);
+}
+
+static void *
+a10dmac_alloc(device_t dev, bool dedicated, void (*cb)(void *), void *cbarg)
+{
+ struct a10dmac_softc *sc = device_get_softc(dev);
+ struct a10dmac_channel *ch_list;
+ struct a10dmac_channel *ch = NULL;
+ uint32_t irqen;
+ uint8_t ch_count, index;
+
+ if (dedicated) {
+ ch_list = sc->sc_ddma_channels;
+ ch_count = DDMA_CHANNELS;
+ } else {
+ ch_list = sc->sc_ndma_channels;
+ ch_count = NDMA_CHANNELS;
+ }
+
+ mtx_lock_spin(&sc->sc_mtx);
+ for (index = 0; index < ch_count; index++) {
+ if (ch_list[index].ch_callback == NULL) {
+ ch = &ch_list[index];
+ ch->ch_callback = cb;
+ ch->ch_callbackarg = cbarg;
+
+ irqen = DMA_READ(sc, AWIN_DMA_IRQ_EN_REG);
+ if (ch->ch_type == CH_NDMA)
+ irqen |= AWIN_DMA_IRQ_NDMA_END(index);
+ else
+ irqen |= AWIN_DMA_IRQ_DDMA_END(index);
+ DMA_WRITE(sc, AWIN_DMA_IRQ_EN_REG, irqen);
+
+ break;
+ }
+ }
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ return (ch);
+}
+
+static void
+a10dmac_free(device_t dev, void *priv)
+{
+ struct a10dmac_channel *ch = priv;
+ struct a10dmac_softc *sc = ch->ch_sc;
+ uint32_t irqen, sta, cfg;
+
+ mtx_lock_spin(&sc->sc_mtx);
+
+ irqen = DMA_READ(sc, AWIN_DMA_IRQ_EN_REG);
+ cfg = a10dmac_read_ctl(ch);
+ if (ch->ch_type == CH_NDMA) {
+ sta = AWIN_DMA_IRQ_NDMA_END(ch->ch_index);
+ cfg &= ~AWIN_NDMA_CTL_DMA_LOADING;
+ } else {
+ sta = AWIN_DMA_IRQ_DDMA_END(ch->ch_index);
+ cfg &= ~AWIN_DDMA_CTL_DMA_LOADING;
+ }
+ irqen &= ~sta;
+ a10dmac_write_ctl(ch, cfg);
+ DMA_WRITE(sc, AWIN_DMA_IRQ_EN_REG, irqen);
+ DMA_WRITE(sc, AWIN_DMA_IRQ_PEND_STA_REG, sta);
+
+ ch->ch_callback = NULL;
+ ch->ch_callbackarg = NULL;
+
+ mtx_unlock_spin(&sc->sc_mtx);
+}
+
+static int
+a10dmac_transfer(device_t dev, void *priv, bus_addr_t src, bus_addr_t dst,
+ size_t nbytes)
+{
+ struct a10dmac_channel *ch = priv;
+ uint32_t cfg;
+
+ cfg = a10dmac_read_ctl(ch);
+ if (ch->ch_type == CH_NDMA) {
+ if (cfg & AWIN_NDMA_CTL_DMA_LOADING)
+ return (EBUSY);
+
+ DMACH_WRITE(ch, AWIN_NDMA_SRC_ADDR_REG, src);
+ DMACH_WRITE(ch, AWIN_NDMA_DEST_ADDR_REG, dst);
+ DMACH_WRITE(ch, AWIN_NDMA_BC_REG, nbytes);
+
+ cfg |= AWIN_NDMA_CTL_DMA_LOADING;
+ a10dmac_write_ctl(ch, cfg);
+ } else {
+ if (cfg & AWIN_DDMA_CTL_DMA_LOADING)
+ return (EBUSY);
+
+ DMACH_WRITE(ch, AWIN_DDMA_SRC_START_ADDR_REG, src);
+ DMACH_WRITE(ch, AWIN_DDMA_DEST_START_ADDR_REG, dst);
+ DMACH_WRITE(ch, AWIN_DDMA_BC_REG, nbytes);
+ DMACH_WRITE(ch, AWIN_DDMA_PARA_REG,
+ (31 << AWIN_DDMA_PARA_DST_DATA_BLK_SIZ_SHIFT) |
+ (7 << AWIN_DDMA_PARA_DST_WAIT_CYC_SHIFT) |
+ (31 << AWIN_DDMA_PARA_SRC_DATA_BLK_SIZ_SHIFT) |
+ (7 << AWIN_DDMA_PARA_SRC_WAIT_CYC_SHIFT));
+
+ cfg |= AWIN_DDMA_CTL_DMA_LOADING;
+ a10dmac_write_ctl(ch, cfg);
+ }
+
+ return (0);
+}
+
+static void
+a10dmac_halt(device_t dev, void *priv)
+{
+ struct a10dmac_channel *ch = priv;
+ uint32_t cfg;
+
+ cfg = a10dmac_read_ctl(ch);
+ if (ch->ch_type == CH_NDMA) {
+ cfg &= ~AWIN_NDMA_CTL_DMA_LOADING;
+ } else {
+ cfg &= ~AWIN_DDMA_CTL_DMA_LOADING;
+ }
+ a10dmac_write_ctl(ch, cfg);
+}
+
+static device_method_t a10dmac_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a10dmac_probe),
+ DEVMETHOD(device_attach, a10dmac_attach),
+
+ /* sunxi DMA interface */
+ DEVMETHOD(sunxi_dma_alloc, a10dmac_alloc),
+ DEVMETHOD(sunxi_dma_free, a10dmac_free),
+ DEVMETHOD(sunxi_dma_set_config, a10dmac_set_config),
+ DEVMETHOD(sunxi_dma_transfer, a10dmac_transfer),
+ DEVMETHOD(sunxi_dma_halt, a10dmac_halt),
+
+ DEVMETHOD_END
+};
+
+static driver_t a10dmac_driver = {
+ "a10dmac",
+ a10dmac_methods,
+ sizeof(struct a10dmac_softc)
+};
+
+static devclass_t a10dmac_devclass;
+
+DRIVER_MODULE(a10dmac, simplebus, a10dmac_driver, a10dmac_devclass, 0, 0);
Index: sys/arm/allwinner/files.allwinner
===================================================================
--- sys/arm/allwinner/files.allwinner
+++ sys/arm/allwinner/files.allwinner
@@ -3,7 +3,9 @@
arm/allwinner/a10_ahci.c optional ahci
arm/allwinner/a10_clk.c standard
+arm/allwinner/a10_codec.c optional sound
arm/allwinner/a10_common.c standard
+arm/allwinner/a10_dmac.c standard
arm/allwinner/a10_ehci.c optional ehci
arm/allwinner/a10_gpio.c optional gpio
arm/allwinner/a10_machdep.c standard
@@ -12,5 +14,6 @@
arm/allwinner/a10_wdog.c standard
arm/allwinner/a20/a20_cpu_cfg.c standard
arm/allwinner/if_emac.c optional emac
+arm/allwinner/sunxi_dma_if.m standard
arm/allwinner/timer.c standard
#arm/allwinner/console.c standard
Index: sys/arm/allwinner/sunxi_dma_if.m
===================================================================
--- /dev/null
+++ sys/arm/allwinner/sunxi_dma_if.m
@@ -0,0 +1,94 @@
+#-
+# Copyright (c) 2016 Jared D. McNeill <jmcneill@invisible.ca>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+#
+# $FreeBSD$
+#
+
+#include <sys/bus.h>
+
+INTERFACE sunxi_dma;
+
+HEADER {
+ #include <machine/bus.h>
+
+ struct sunxi_dma_config {
+ unsigned int dst_width;
+ unsigned int dst_burst_len;
+ unsigned int dst_drqtype;
+ bool dst_noincr;
+ unsigned int src_width;
+ unsigned int src_burst_len;
+ unsigned int src_drqtype;
+ bool src_noincr;
+ };
+
+ typedef void (*sunxi_dma_callback)(void *);
+}
+
+#
+# Allocate DMA channel
+#
+METHOD void * alloc {
+ device_t dev;
+ bool dedicated;
+ sunxi_dma_callback callback;
+ void *callback_arg;
+};
+
+#
+# Free DMA channel
+#
+METHOD void free {
+ device_t dev;
+ void *dmachan;
+};
+
+#
+# Set DMA channel configuration
+#
+METHOD int set_config {
+ device_t dev;
+ void *dmachan;
+ const struct sunxi_dma_config *cfg;
+};
+
+#
+# Start DMA channel transfer
+#
+METHOD int transfer {
+ device_t dev;
+ void *dmachan;
+ bus_addr_t src;
+ bus_addr_t dst;
+ size_t nbytes;
+};
+
+#
+# Halt DMA channel transfer
+#
+METHOD void halt {
+ device_t dev;
+ void *dmachan;
+};
Index: sys/arm/conf/A20
===================================================================
--- sys/arm/conf/A20
+++ sys/arm/conf/A20
@@ -107,6 +107,9 @@
# USB ethernet support, requires miibus
device miibus
+# Sound
+device sound
+
# Flattened Device Tree
options FDT # Configure using FDT/DTB data
makeoptions MODULES_EXTRA=dtb/allwinner
Index: sys/boot/fdt/dts/arm/cubieboard2.dts
===================================================================
--- sys/boot/fdt/dts/arm/cubieboard2.dts
+++ sys/boot/fdt/dts/arm/cubieboard2.dts
@@ -74,6 +74,11 @@
ahci: sata@01c18000 {
status = "okay";
};
+
+ codec: codec@01c22c00 {
+ status = "okay";
+ pamute-gpio = <&pio 7 15 GPIO_ACTIVE_HIGH>;
+ };
};
leds {
Index: sys/boot/fdt/dts/arm/sun7i-a20.dtsi
===================================================================
--- sys/boot/fdt/dts/arm/sun7i-a20.dtsi
+++ sys/boot/fdt/dts/arm/sun7i-a20.dtsi
@@ -156,6 +156,21 @@
#address-cells = <1>;
#size-cells = <0>;
};
+
+ dma: dma-controller@01c02000 {
+ compatible = "allwinner,sun4i-a10-dma";
+ reg = <0x01c02000 0x1000>;
+ interrupts = <27>;
+ interrupt-parent = <&GIC>;
+ };
+
+ codec: codec@01c22c00 {
+ compatible = "allwinner,sun7i-a20-codec";
+ reg = <0x01c22c00 0x40>;
+ interrupts = <30>;
+ interrupt-parent = <&GIC>;
+ status = "disabled";
+ };
};
};
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Jul 1, 6:31 AM (5 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34546057
Default Alt Text
D5050.id12642.diff (48 KB)
Attached To
Mode
D5050: Allwinner A10/A20 DMA controller driver
Attached
Detach File
Event Timeline
Log In to Comment