Module Name: src Committed By: jmcneill Date: Sat Sep 6 22:48:19 UTC 2014
Modified Files: src/sys/arch/arm/allwinner: awin_ac.c Log Message: capture support To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.11 src/sys/arch/arm/allwinner/awin_ac.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/allwinner/awin_ac.c diff -u src/sys/arch/arm/allwinner/awin_ac.c:1.10 src/sys/arch/arm/allwinner/awin_ac.c:1.11 --- src/sys/arch/arm/allwinner/awin_ac.c:1.10 Sat Sep 6 20:54:53 2014 +++ src/sys/arch/arm/allwinner/awin_ac.c Sat Sep 6 22:48:19 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: awin_ac.c,v 1.10 2014/09/06 20:54:53 jmcneill Exp $ */ +/* $NetBSD: awin_ac.c,v 1.11 2014/09/06 22:48:19 jmcneill Exp $ */ /*- * Copyright (c) 2014 Jared D. McNeill <jmcne...@invisible.ca> @@ -30,7 +30,7 @@ #include "opt_ddb.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: awin_ac.c,v 1.10 2014/09/06 20:54:53 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: awin_ac.c,v 1.11 2014/09/06 22:48:19 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -47,6 +47,7 @@ __KERNEL_RCSID(0, "$NetBSD: awin_ac.c,v #include <arm/allwinner/awin_var.h> #define AWINAC_TX_TRIG_LEVEL 0xf +#define AWINAC_RX_TRIG_LEVEL 0x7 #define AWINAC_DRQ_CLR_CNT 0x3 #define AWINAC_INIT_VOL 0x3b @@ -114,10 +115,46 @@ __KERNEL_RCSID(0, "$NetBSD: awin_ac.c,v #define DAC_ACTL_PAVOL __BITS(5,0) #define AC_DAC_TUNE 0x14 #define AC_ADC_FIFOC 0x1c +#define ADC_FIFOC_FS __BITS(31,29) +#define ADC_FS_48KHZ 0 +#define ADC_FS_32KHZ 1 +#define ADC_FS_24KHZ 2 +#define ADC_FS_16KHZ 3 +#define ADC_FS_12KHZ 4 +#define DAC_FS_8KHZ 5 +#define ADC_FIFOC_EN_AD __BIT(28) +#define ADC_FIFOC_RX_FIFO_MODE __BIT(24) +#define ADC_FIFOC_RX_TRIG_LEVEL __BITS(12,8) +#define ADC_FIFOC_MONO_EN __BIT(7) +#define ADC_FIFOC_RX_SAMPLE_BITS __BIT(6) +#define ADC_FIFOC_DRQ_EN __BIT(4) +#define ADC_FIFOC_IRQ_EN __BIT(3) +#define ADC_FIFOC_OVERRUN_IRQ_EN __BIT(2) +#define ADC_FIFOC_FIFO_FLUSH __BIT(1) #define AC_ADC_FIFOS 0x20 +#define ADC_FIFOS_RXA __BIT(23) +#define ADC_FIFOS_RXA_CNT __BITS(13,8) +#define ADC_FIFOS_RXA_INT __BIT(3) +#define ADC_FIFOS_RXO_INT __BIT(1) #define AC_ADC_RXDATA 0x24 #define AC_ADC_ACTL 0x28 +#define ADC_ACTL_ADCREN __BIT(31) +#define ADC_ACTL_ADCLEN __BIT(30) +#define ADC_ACTL_PREG1EN __BIT(29) +#define ADC_ACTL_PREG2EN __BIT(28) +#define ADC_ACTL_VMICEN __BIT(27) +#define ADC_ACTL_ADCG __BITS(22,20) +#define ADC_ACTL_ADCIS __BITS(19,17) +#define ADC_ACTL_LNRDF __BIT(16) +#define ADC_ACTL_LNPREG __BIT(15) +#define ADC_ACTL_LHPOUTN __BIT(11) +#define ADC_ACTL_RHPOUTN __BIT(10) +#define ADC_ACTL_DITHER __BIT(8) +#define ADC_ACTL_DITHER_CLK_SELECT __BITS(7,6) #define ADC_ACTL_PA_EN __BIT(4) +#define ADC_ACTL_DDE __BIT(3) +#define ADC_ACTL_COMPTEN __BIT(2) +#define ADC_ACTL_PTDBS __BITS(1,0) #define AC_DAC_CNT 0x30 #define AC_ADC_CNT 0x34 #define AC_DAC_CAL 0x38 @@ -149,9 +186,6 @@ struct awinac_softc { struct audio_encoding_set *sc_encodings; audio_params_t sc_pparam; - - void * sc_ih; - struct awin_dma_channel *sc_pdma; void (*sc_pint)(void *); void *sc_pintarg; @@ -160,6 +194,15 @@ struct awinac_softc { bus_addr_t sc_pcur; int sc_pblksize; + audio_params_t sc_rparam; + struct awin_dma_channel *sc_rdma; + void (*sc_rint)(void *); + void *sc_rintarg; + bus_addr_t sc_rstart; + bus_addr_t sc_rend; + bus_addr_t sc_rcur; + int sc_rblksize; + struct awin_gpio_pindata sc_pactrl_gpio; bool sc_has_pactrl_gpio; }; @@ -176,6 +219,8 @@ static void awinac_freedma(struct awinac static void awinac_pint(void *); static int awinac_play(struct awinac_softc *); +static void awinac_rint(void *); +static int awinac_rec(struct awinac_softc *); #if defined(DDB) void awinac_dump_regs(void); @@ -313,7 +358,12 @@ awinac_attach(device_t parent, device_t sc->sc_pdma = awin_dma_alloc(AWIN_DMA_TYPE_NDMA, awinac_pint, sc); if (sc->sc_pdma == NULL) { - aprint_error_dev(self, "couldn't allocate DMA channel\n"); + aprint_error_dev(self, "couldn't allocate play DMA channel\n"); + return; + } + sc->sc_rdma = awin_dma_alloc(AWIN_DMA_TYPE_NDMA, awinac_rint, sc); + if (sc->sc_rdma == NULL) { + aprint_error_dev(self, "couldn't allocate rec DMA channel\n"); return; } @@ -386,7 +436,14 @@ awinac_init(struct awinac_softc *sc) val &= ~DAC_FIFOC_FIFO_OVERRUN_IRQ_EN; AC_WRITE(sc, AC_DAC_FIFOC, val); + val = AC_READ(sc, AC_ADC_FIFOC); + val &= ~ADC_FIFOC_DRQ_EN; + val &= ~ADC_FIFOC_IRQ_EN; + val &= ~ADC_FIFOC_OVERRUN_IRQ_EN; + AC_WRITE(sc, AC_ADC_FIFOC, val); + AC_WRITE(sc, AC_DAC_FIFOS, AC_READ(sc, AC_DAC_FIFOS)); + AC_WRITE(sc, AC_ADC_FIFOS, AC_READ(sc, AC_ADC_FIFOS)); } static int @@ -474,15 +531,61 @@ awinac_play(struct awinac_softc *sc) return 0; } +static void +awinac_rint(void *priv) +{ + struct awinac_softc *sc = priv; + + mutex_enter(&sc->sc_intr_lock); + if (sc->sc_rint == NULL) { + mutex_exit(&sc->sc_intr_lock); + return; + } + sc->sc_rint(sc->sc_rintarg); + mutex_exit(&sc->sc_intr_lock); + + awinac_rec(sc); +} + +static int +awinac_rec(struct awinac_softc *sc) +{ + int error; + + error = awin_dma_transfer(sc->sc_rdma, + AWIN_CORE_PBASE + AWIN_AC_OFFSET + AC_ADC_RXDATA, sc->sc_rcur, + sc->sc_rblksize); + if (error) { + device_printf(sc->sc_dev, "failed to transfer DMA; error %d\n", + error); + return error; + } + + sc->sc_rcur += sc->sc_rblksize; + if (sc->sc_rcur >= sc->sc_rend) + sc->sc_rcur = sc->sc_rstart; + + return 0; +} + static int awinac_open(void *priv, int flags) { + struct awinac_softc *sc = priv; + + if (sc->sc_has_pactrl_gpio) + awin_gpio_pindata_write(&sc->sc_pactrl_gpio, 1); + return 0; } static void awinac_close(void *priv) { + struct awinac_softc *sc = priv; + + if (sc->sc_has_pactrl_gpio) + awin_gpio_pindata_write(&sc->sc_pactrl_gpio, 0); } static int @@ -495,6 +598,10 @@ awinac_drain(void *priv) val |= DAC_FIFOC_FIFO_FLUSH; AC_WRITE(sc, AC_DAC_FIFOC, val); + val = AC_READ(sc, AC_ADC_FIFOC); + val |= ADC_FIFOC_FIFO_FLUSH; + AC_WRITE(sc, AC_ADC_FIFOC, val); + return 0; } @@ -523,6 +630,15 @@ awinac_set_params(void *priv, int setmod pfil->filters[0].param : *play; } + if (rec && (setmode & AUMODE_RECORD)) { + index = auconv_set_converter(&sc->sc_format, 1, + AUMODE_RECORD, rec, true, rfil); + if (index < 0) + return EINVAL; + sc->sc_rparam = rfil->req_size > 0 ? + rfil->filters[0].param : + *rec; + } return 0; } @@ -537,6 +653,10 @@ awinac_commit_settings(void *priv) return EINVAL; if (sc->sc_pparam.validbits != 16 && sc->sc_pparam.validbits != 24) return EINVAL; + if (sc->sc_rparam.sample_rate != 48000) + return EINVAL; + if (sc->sc_rparam.validbits != 16 && sc->sc_rparam.validbits != 24) + return EINVAL; val = AC_READ(sc, AC_DAC_FIFOC); val &= ~DAC_FIFOC_FIR_VER; @@ -558,6 +678,21 @@ awinac_commit_settings(void *priv) } AC_WRITE(sc, AC_DAC_FIFOC, val); + val = AC_READ(sc, AC_ADC_FIFOC); + val |= ADC_FIFOC_EN_AD; + val |= ADC_FIFOC_RX_FIFO_MODE; + val &= ~ADC_FIFOC_FS; + val |= __SHIFTIN(ADC_FS_48KHZ, ADC_FIFOC_FS); + val &= ~ADC_FIFOC_RX_TRIG_LEVEL; + val |= __SHIFTIN(AWINAC_RX_TRIG_LEVEL, ADC_FIFOC_RX_TRIG_LEVEL); + val &= ~ADC_FIFOC_MONO_EN; + if (sc->sc_rparam.validbits == 16) { + val &= ~ADC_FIFOC_RX_SAMPLE_BITS; + } else { + val |= ADC_FIFOC_RX_SAMPLE_BITS; + } + AC_WRITE(sc, AC_ADC_FIFOC, val); + return 0; } @@ -569,9 +704,6 @@ awinac_halt_output(void *priv) awin_dma_halt(sc->sc_pdma); - if (sc->sc_has_pactrl_gpio) - awin_gpio_pindata_write(&sc->sc_pactrl_gpio, 0); - val = AC_READ(sc, AC_DAC_ACTL); val &= ~DAC_ACTL_DACAREN; val &= ~DAC_ACTL_DACALEN; @@ -591,7 +723,24 @@ awinac_halt_output(void *priv) static int awinac_halt_input(void *priv) { - return EINVAL; + struct awinac_softc *sc = priv; + uint32_t val; + + awin_dma_halt(sc->sc_rdma); + + val = AC_READ(sc, AC_ADC_ACTL); + val &= ~ADC_ACTL_ADCREN; + val &= ~ADC_ACTL_ADCLEN; + AC_WRITE(sc, AC_ADC_ACTL, val); + + val = AC_READ(sc, AC_ADC_FIFOC); + val &= ~ADC_FIFOC_DRQ_EN; + AC_WRITE(sc, AC_ADC_FIFOC, val); + + sc->sc_rint = NULL; + sc->sc_rintarg = NULL; + + return 0; } static int @@ -744,7 +893,8 @@ static int awinac_get_props(void *priv) { return AUDIO_PROP_PLAYBACK|AUDIO_PROP_CAPTURE| - AUDIO_PROP_INDEPENDENT|AUDIO_PROP_MMAP; + AUDIO_PROP_INDEPENDENT|AUDIO_PROP_MMAP| + AUDIO_PROP_FULLDUPLEX; } static int @@ -801,9 +951,6 @@ awinac_trigger_output(void *priv, void * sc->sc_pend = sc->sc_pstart + psize; sc->sc_pblksize = blksize; - if (sc->sc_has_pactrl_gpio) - awin_gpio_pindata_write(&sc->sc_pactrl_gpio, 1); - val = AC_READ(sc, AC_DAC_FIFOC); val |= DAC_FIFOC_DRQ_EN; AC_WRITE(sc, AC_DAC_FIFOC, val); @@ -836,7 +983,68 @@ static int awinac_trigger_input(void *priv, void *start, void *end, int blksize, void (*intr)(void *), void *intrarg, const audio_params_t *params) { - return EINVAL; + struct awinac_softc *sc = priv; + struct awinac_dma *dma; + bus_addr_t rstart; + bus_size_t rsize; + uint32_t val, dmacfg; + int error; + + rstart = 0; + rsize = (uintptr_t)end - (uintptr_t)start; + + LIST_FOREACH(dma, &sc->sc_dmalist, dma_list) { + if (dma->dma_addr == start) { + rstart = dma->dma_map->dm_segs[0].ds_addr; + break; + } + } + if (rstart == 0) { + device_printf(sc->sc_dev, "bad addr %p\n", start); + return EINVAL; + } + + val = AC_READ(sc, AC_ADC_FIFOC); + val |= ADC_FIFOC_FIFO_FLUSH; + AC_WRITE(sc, AC_ADC_FIFOC, val); + + val = AC_READ(sc, AC_ADC_ACTL); + val |= ADC_ACTL_ADCREN; + val |= ADC_ACTL_ADCLEN; + AC_WRITE(sc, AC_ADC_ACTL, val); + + sc->sc_rint = intr; + sc->sc_rintarg = intrarg; + sc->sc_rstart = sc->sc_rcur = rstart; + sc->sc_rend = sc->sc_rstart + rsize; + sc->sc_rblksize = blksize; + + val = AC_READ(sc, AC_ADC_FIFOC); + val |= ADC_FIFOC_DRQ_EN; + AC_WRITE(sc, AC_ADC_FIFOC, val); + + dmacfg = 0; + dmacfg |= __SHIFTIN(AWIN_DMA_CTL_DATA_WIDTH_16, + AWIN_DMA_CTL_DST_DATA_WIDTH); + dmacfg |= __SHIFTIN(AWIN_DMA_CTL_BURST_LEN_4, + AWIN_DMA_CTL_DST_BURST_LEN); + dmacfg |= __SHIFTIN(AWIN_DMA_CTL_DATA_WIDTH_16, + AWIN_DMA_CTL_SRC_DATA_WIDTH); + dmacfg |= __SHIFTIN(AWIN_DMA_CTL_BURST_LEN_4, + AWIN_DMA_CTL_SRC_BURST_LEN); + dmacfg |= AWIN_DMA_CTL_BC_REMAINING; + dmacfg |= AWIN_NDMA_CTL_SRC_ADDR_NOINCR; + dmacfg |= __SHIFTIN(AWIN_NDMA_CTL_DRQ_SDRAM, + AWIN_DMA_CTL_DST_DRQ_TYPE); + dmacfg |= __SHIFTIN(AWIN_NDMA_CTL_DRQ_CODEC, + AWIN_DMA_CTL_SRC_DRQ_TYPE); + awin_dma_set_config(sc->sc_pdma, dmacfg); + + error = awinac_rec(sc); + if (error) + awinac_halt_input(sc); + + return error; } static void