Page MenuHomeFreeBSD

D5050.id12642.diff
No OneTemporary

D5050.id12642.diff

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

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)

Event Timeline