1ba9b7163SAndrew Turner /*- 2ba9b7163SAndrew Turner * Copyright (c) 2014-2016 Jared D. McNeill <jmcneill@invisible.ca> 3ba9b7163SAndrew Turner * All rights reserved. 4ba9b7163SAndrew Turner * 5ba9b7163SAndrew Turner * Redistribution and use in source and binary forms, with or without 6ba9b7163SAndrew Turner * modification, are permitted provided that the following conditions 7ba9b7163SAndrew Turner * are met: 8ba9b7163SAndrew Turner * 1. Redistributions of source code must retain the above copyright 9ba9b7163SAndrew Turner * notice, this list of conditions and the following disclaimer. 10ba9b7163SAndrew Turner * 2. Redistributions in binary form must reproduce the above copyright 11ba9b7163SAndrew Turner * notice, this list of conditions and the following disclaimer in the 12ba9b7163SAndrew Turner * documentation and/or other materials provided with the distribution. 13ba9b7163SAndrew Turner * 14ba9b7163SAndrew Turner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15ba9b7163SAndrew Turner * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16ba9b7163SAndrew Turner * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17ba9b7163SAndrew Turner * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18ba9b7163SAndrew Turner * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19ba9b7163SAndrew Turner * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20ba9b7163SAndrew Turner * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21ba9b7163SAndrew Turner * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22ba9b7163SAndrew Turner * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23ba9b7163SAndrew Turner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24ba9b7163SAndrew Turner * SUCH DAMAGE. 25ba9b7163SAndrew Turner * 26ba9b7163SAndrew Turner * $FreeBSD$ 27ba9b7163SAndrew Turner */ 28ba9b7163SAndrew Turner 29ba9b7163SAndrew Turner /* 30*16025c35SJared McNeill * Allwinner A10/A20 and H3 Audio Codec 31ba9b7163SAndrew Turner */ 32ba9b7163SAndrew Turner 33ba9b7163SAndrew Turner #include <sys/cdefs.h> 34ba9b7163SAndrew Turner __FBSDID("$FreeBSD$"); 35ba9b7163SAndrew Turner 36ba9b7163SAndrew Turner #include <sys/param.h> 37ba9b7163SAndrew Turner #include <sys/systm.h> 38ba9b7163SAndrew Turner #include <sys/bus.h> 39ba9b7163SAndrew Turner #include <sys/rman.h> 40ba9b7163SAndrew Turner #include <sys/condvar.h> 41ba9b7163SAndrew Turner #include <sys/kernel.h> 42ba9b7163SAndrew Turner #include <sys/module.h> 43ba9b7163SAndrew Turner #include <sys/gpio.h> 44ba9b7163SAndrew Turner 45ba9b7163SAndrew Turner #include <machine/bus.h> 46ba9b7163SAndrew Turner 47ba9b7163SAndrew Turner #include <dev/sound/pcm/sound.h> 48ba9b7163SAndrew Turner #include <dev/sound/chip.h> 49ba9b7163SAndrew Turner 50ba9b7163SAndrew Turner #include <dev/ofw/ofw_bus.h> 51ba9b7163SAndrew Turner #include <dev/ofw/ofw_bus_subr.h> 52ba9b7163SAndrew Turner 53*16025c35SJared McNeill #include <dev/gpio/gpiobusvar.h> 54*16025c35SJared McNeill 556a05f063SJared McNeill #include <dev/extres/clk/clk.h> 56*16025c35SJared McNeill #include <dev/extres/hwreset/hwreset.h> 57ba9b7163SAndrew Turner 58ba9b7163SAndrew Turner #include "sunxi_dma_if.h" 59ba9b7163SAndrew Turner #include "mixer_if.h" 60*16025c35SJared McNeill 61*16025c35SJared McNeill struct a10codec_info; 62*16025c35SJared McNeill 63*16025c35SJared McNeill struct a10codec_config { 64*16025c35SJared McNeill /* mixer class */ 65*16025c35SJared McNeill struct kobj_class *mixer_class; 66*16025c35SJared McNeill 67*16025c35SJared McNeill /* toggle DAC/ADC mute */ 68*16025c35SJared McNeill void (*mute)(struct a10codec_info *, int, int); 69*16025c35SJared McNeill 70*16025c35SJared McNeill /* DRQ types */ 71*16025c35SJared McNeill u_int drqtype_codec; 72*16025c35SJared McNeill u_int drqtype_sdram; 73*16025c35SJared McNeill 74*16025c35SJared McNeill /* register map */ 75*16025c35SJared McNeill bus_size_t DPC, 76*16025c35SJared McNeill DAC_FIFOC, 77*16025c35SJared McNeill DAC_FIFOS, 78*16025c35SJared McNeill DAC_TXDATA, 79*16025c35SJared McNeill ADC_FIFOC, 80*16025c35SJared McNeill ADC_FIFOS, 81*16025c35SJared McNeill ADC_RXDATA, 82*16025c35SJared McNeill DAC_CNT, 83*16025c35SJared McNeill ADC_CNT; 84*16025c35SJared McNeill }; 85ba9b7163SAndrew Turner 86ba9b7163SAndrew Turner #define TX_TRIG_LEVEL 0xf 87ba9b7163SAndrew Turner #define RX_TRIG_LEVEL 0x7 88ba9b7163SAndrew Turner #define DRQ_CLR_CNT 0x3 89ba9b7163SAndrew Turner 90*16025c35SJared McNeill #define AC_DAC_DPC(_sc) ((_sc)->cfg->DPC) 91ba9b7163SAndrew Turner #define DAC_DPC_EN_DA 0x80000000 92*16025c35SJared McNeill #define AC_DAC_FIFOC(_sc) ((_sc)->cfg->DAC_FIFOC) 93ba9b7163SAndrew Turner #define DAC_FIFOC_FS_SHIFT 29 94ba9b7163SAndrew Turner #define DAC_FIFOC_FS_MASK (7U << DAC_FIFOC_FS_SHIFT) 95ba9b7163SAndrew Turner #define DAC_FS_48KHZ 0 96ba9b7163SAndrew Turner #define DAC_FS_32KHZ 1 97ba9b7163SAndrew Turner #define DAC_FS_24KHZ 2 98ba9b7163SAndrew Turner #define DAC_FS_16KHZ 3 99ba9b7163SAndrew Turner #define DAC_FS_12KHZ 4 100ba9b7163SAndrew Turner #define DAC_FS_8KHZ 5 101ba9b7163SAndrew Turner #define DAC_FS_192KHZ 6 102ba9b7163SAndrew Turner #define DAC_FS_96KHZ 7 103ba9b7163SAndrew Turner #define DAC_FIFOC_FIFO_MODE_SHIFT 24 104ba9b7163SAndrew Turner #define DAC_FIFOC_FIFO_MODE_MASK (3U << DAC_FIFOC_FIFO_MODE_SHIFT) 105ba9b7163SAndrew Turner #define FIFO_MODE_24_31_8 0 106ba9b7163SAndrew Turner #define FIFO_MODE_16_31_16 0 107ba9b7163SAndrew Turner #define FIFO_MODE_16_15_0 1 108ba9b7163SAndrew Turner #define DAC_FIFOC_DRQ_CLR_CNT_SHIFT 21 109ba9b7163SAndrew Turner #define DAC_FIFOC_DRQ_CLR_CNT_MASK (3U << DAC_FIFOC_DRQ_CLR_CNT_SHIFT) 110ba9b7163SAndrew Turner #define DAC_FIFOC_TX_TRIG_LEVEL_SHIFT 8 111ba9b7163SAndrew Turner #define DAC_FIFOC_TX_TRIG_LEVEL_MASK (0x7f << DAC_FIFOC_TX_TRIG_LEVEL_SHIFT) 112ba9b7163SAndrew Turner #define DAC_FIFOC_MONO_EN (1U << 6) 113ba9b7163SAndrew Turner #define DAC_FIFOC_TX_BITS (1U << 5) 114ba9b7163SAndrew Turner #define DAC_FIFOC_DRQ_EN (1U << 4) 115ba9b7163SAndrew Turner #define DAC_FIFOC_FIFO_FLUSH (1U << 0) 116*16025c35SJared McNeill #define AC_DAC_FIFOS(_sc) ((_sc)->cfg->DAC_FIFOS) 117*16025c35SJared McNeill #define AC_DAC_TXDATA(_sc) ((_sc)->cfg->DAC_TXDATA) 118*16025c35SJared McNeill #define AC_ADC_FIFOC(_sc) ((_sc)->cfg->ADC_FIFOC) 119ba9b7163SAndrew Turner #define ADC_FIFOC_FS_SHIFT 29 120ba9b7163SAndrew Turner #define ADC_FIFOC_FS_MASK (7U << ADC_FIFOC_FS_SHIFT) 121ba9b7163SAndrew Turner #define ADC_FS_48KHZ 0 122ba9b7163SAndrew Turner #define ADC_FIFOC_EN_AD (1U << 28) 123ba9b7163SAndrew Turner #define ADC_FIFOC_RX_FIFO_MODE (1U << 24) 124ba9b7163SAndrew Turner #define ADC_FIFOC_RX_TRIG_LEVEL_SHIFT 8 125ba9b7163SAndrew Turner #define ADC_FIFOC_RX_TRIG_LEVEL_MASK (0x1f << ADC_FIFOC_RX_TRIG_LEVEL_SHIFT) 126ba9b7163SAndrew Turner #define ADC_FIFOC_MONO_EN (1U << 7) 127ba9b7163SAndrew Turner #define ADC_FIFOC_RX_BITS (1U << 6) 128ba9b7163SAndrew Turner #define ADC_FIFOC_DRQ_EN (1U << 4) 129ba9b7163SAndrew Turner #define ADC_FIFOC_FIFO_FLUSH (1U << 1) 130*16025c35SJared McNeill #define AC_ADC_FIFOS(_sc) ((_sc)->cfg->ADC_FIFOS) 131*16025c35SJared McNeill #define AC_ADC_RXDATA(_sc) ((_sc)->cfg->ADC_RXDATA) 132*16025c35SJared McNeill #define AC_DAC_CNT(_sc) ((_sc)->cfg->DAC_CNT) 133*16025c35SJared McNeill #define AC_ADC_CNT(_sc) ((_sc)->cfg->ADC_CNT) 134ba9b7163SAndrew Turner 135ba9b7163SAndrew Turner static uint32_t a10codec_fmt[] = { 136ba9b7163SAndrew Turner SND_FORMAT(AFMT_S16_LE, 1, 0), 137ba9b7163SAndrew Turner SND_FORMAT(AFMT_S16_LE, 2, 0), 138ba9b7163SAndrew Turner 0 139ba9b7163SAndrew Turner }; 140ba9b7163SAndrew Turner 141ba9b7163SAndrew Turner static struct pcmchan_caps a10codec_pcaps = { 8000, 192000, a10codec_fmt, 0 }; 142ba9b7163SAndrew Turner static struct pcmchan_caps a10codec_rcaps = { 8000, 48000, a10codec_fmt, 0 }; 143ba9b7163SAndrew Turner 144ba9b7163SAndrew Turner struct a10codec_info; 145ba9b7163SAndrew Turner 146ba9b7163SAndrew Turner struct a10codec_chinfo { 147ba9b7163SAndrew Turner struct snd_dbuf *buffer; 148ba9b7163SAndrew Turner struct pcm_channel *channel; 149ba9b7163SAndrew Turner struct a10codec_info *parent; 150ba9b7163SAndrew Turner bus_dmamap_t dmamap; 151ba9b7163SAndrew Turner void *dmaaddr; 152ba9b7163SAndrew Turner bus_addr_t physaddr; 153ba9b7163SAndrew Turner bus_size_t fifo; 154ba9b7163SAndrew Turner device_t dmac; 155ba9b7163SAndrew Turner void *dmachan; 156ba9b7163SAndrew Turner 157ba9b7163SAndrew Turner int dir; 158ba9b7163SAndrew Turner int run; 159ba9b7163SAndrew Turner uint32_t pos; 160ba9b7163SAndrew Turner uint32_t format; 161ba9b7163SAndrew Turner uint32_t blocksize; 162ba9b7163SAndrew Turner uint32_t speed; 163ba9b7163SAndrew Turner }; 164ba9b7163SAndrew Turner 165ba9b7163SAndrew Turner struct a10codec_info { 166ba9b7163SAndrew Turner device_t dev; 167*16025c35SJared McNeill struct resource *res[3]; 168ba9b7163SAndrew Turner struct mtx *lock; 169ba9b7163SAndrew Turner bus_dma_tag_t dmat; 170ba9b7163SAndrew Turner unsigned dmasize; 171ba9b7163SAndrew Turner void *ih; 172ba9b7163SAndrew Turner 173*16025c35SJared McNeill struct a10codec_config *cfg; 174ba9b7163SAndrew Turner 175ba9b7163SAndrew Turner struct a10codec_chinfo play; 176ba9b7163SAndrew Turner struct a10codec_chinfo rec; 177ba9b7163SAndrew Turner }; 178ba9b7163SAndrew Turner 179ba9b7163SAndrew Turner static struct resource_spec a10codec_spec[] = { 180ba9b7163SAndrew Turner { SYS_RES_MEMORY, 0, RF_ACTIVE }, 181*16025c35SJared McNeill { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_OPTIONAL }, 182ba9b7163SAndrew Turner { SYS_RES_IRQ, 0, RF_ACTIVE }, 183ba9b7163SAndrew Turner { -1, 0 } 184ba9b7163SAndrew Turner }; 185ba9b7163SAndrew Turner 186ba9b7163SAndrew Turner #define CODEC_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) 187ba9b7163SAndrew Turner #define CODEC_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) 188ba9b7163SAndrew Turner 189ba9b7163SAndrew Turner /* 190*16025c35SJared McNeill * A10/A20 mixer interface 191ba9b7163SAndrew Turner */ 192ba9b7163SAndrew Turner 193*16025c35SJared McNeill #define A10_DAC_ACTL 0x10 194*16025c35SJared McNeill #define A10_DACAREN (1U << 31) 195*16025c35SJared McNeill #define A10_DACALEN (1U << 30) 196*16025c35SJared McNeill #define A10_MIXEN (1U << 29) 197*16025c35SJared McNeill #define A10_DACPAS (1U << 8) 198*16025c35SJared McNeill #define A10_PAMUTE (1U << 6) 199*16025c35SJared McNeill #define A10_PAVOL_SHIFT 0 200*16025c35SJared McNeill #define A10_PAVOL_MASK (0x3f << A10_PAVOL_SHIFT) 201*16025c35SJared McNeill #define A10_ADC_ACTL 0x28 202*16025c35SJared McNeill #define A10_ADCREN (1U << 31) 203*16025c35SJared McNeill #define A10_ADCLEN (1U << 30) 204*16025c35SJared McNeill #define A10_PREG1EN (1U << 29) 205*16025c35SJared McNeill #define A10_PREG2EN (1U << 28) 206*16025c35SJared McNeill #define A10_VMICEN (1U << 27) 207*16025c35SJared McNeill #define A10_ADCG_SHIFT 20 208*16025c35SJared McNeill #define A10_ADCG_MASK (7U << A10_ADCG_SHIFT) 209*16025c35SJared McNeill #define A10_ADCIS_SHIFT 17 210*16025c35SJared McNeill #define A10_ADCIS_MASK (7U << A10_ADCIS_SHIFT) 211*16025c35SJared McNeill #define A10_ADC_IS_LINEIN 0 212*16025c35SJared McNeill #define A10_ADC_IS_FMIN 1 213*16025c35SJared McNeill #define A10_ADC_IS_MIC1 2 214*16025c35SJared McNeill #define A10_ADC_IS_MIC2 3 215*16025c35SJared McNeill #define A10_ADC_IS_MIC1_L_MIC2_R 4 216*16025c35SJared McNeill #define A10_ADC_IS_MIC1_LR_MIC2_LR 5 217*16025c35SJared McNeill #define A10_ADC_IS_OMIX 6 218*16025c35SJared McNeill #define A10_ADC_IS_LINEIN_L_MIC1_R 7 219*16025c35SJared McNeill #define A10_LNRDF (1U << 16) 220*16025c35SJared McNeill #define A10_LNPREG_SHIFT 13 221*16025c35SJared McNeill #define A10_LNPREG_MASK (7U << A10_LNPREG_SHIFT) 222*16025c35SJared McNeill #define A10_PA_EN (1U << 4) 223*16025c35SJared McNeill #define A10_DDE (1U << 3) 224*16025c35SJared McNeill 225ba9b7163SAndrew Turner static int 226*16025c35SJared McNeill a10_mixer_init(struct snd_mixer *m) 227ba9b7163SAndrew Turner { 228ba9b7163SAndrew Turner struct a10codec_info *sc = mix_getdevinfo(m); 229ba9b7163SAndrew Turner uint32_t val; 230ba9b7163SAndrew Turner 231ba9b7163SAndrew Turner mix_setdevs(m, SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_RECLEV); 232ba9b7163SAndrew Turner mix_setrecdevs(m, SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC); 233ba9b7163SAndrew Turner 234ba9b7163SAndrew Turner /* Unmute input source to PA */ 235*16025c35SJared McNeill val = CODEC_READ(sc, A10_DAC_ACTL); 236*16025c35SJared McNeill val |= A10_PAMUTE; 237*16025c35SJared McNeill CODEC_WRITE(sc, A10_DAC_ACTL, val); 238ba9b7163SAndrew Turner 239ba9b7163SAndrew Turner /* Enable PA */ 240*16025c35SJared McNeill val = CODEC_READ(sc, A10_ADC_ACTL); 241*16025c35SJared McNeill val |= A10_PA_EN; 242*16025c35SJared McNeill CODEC_WRITE(sc, A10_ADC_ACTL, val); 243ba9b7163SAndrew Turner 244ba9b7163SAndrew Turner return (0); 245ba9b7163SAndrew Turner } 246ba9b7163SAndrew Turner 247*16025c35SJared McNeill static const struct a10_mixer { 248ba9b7163SAndrew Turner unsigned reg; 249ba9b7163SAndrew Turner unsigned mask; 250ba9b7163SAndrew Turner unsigned shift; 251*16025c35SJared McNeill } a10_mixers[SOUND_MIXER_NRDEVICES] = { 252*16025c35SJared McNeill [SOUND_MIXER_VOLUME] = { A10_DAC_ACTL, A10_PAVOL_MASK, 253*16025c35SJared McNeill A10_PAVOL_SHIFT }, 254*16025c35SJared McNeill [SOUND_MIXER_LINE] = { A10_ADC_ACTL, A10_LNPREG_MASK, 255*16025c35SJared McNeill A10_LNPREG_SHIFT }, 256*16025c35SJared McNeill [SOUND_MIXER_RECLEV] = { A10_ADC_ACTL, A10_ADCG_MASK, 257*16025c35SJared McNeill A10_ADCG_SHIFT }, 258ba9b7163SAndrew Turner }; 259ba9b7163SAndrew Turner 260ba9b7163SAndrew Turner static int 261*16025c35SJared McNeill a10_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, 262ba9b7163SAndrew Turner unsigned right) 263ba9b7163SAndrew Turner { 264ba9b7163SAndrew Turner struct a10codec_info *sc = mix_getdevinfo(m); 265ba9b7163SAndrew Turner uint32_t val; 266ba9b7163SAndrew Turner unsigned nvol, max; 267ba9b7163SAndrew Turner 268*16025c35SJared McNeill max = a10_mixers[dev].mask >> a10_mixers[dev].shift; 269ba9b7163SAndrew Turner nvol = (left * max) / 100; 270ba9b7163SAndrew Turner 271*16025c35SJared McNeill val = CODEC_READ(sc, a10_mixers[dev].reg); 272*16025c35SJared McNeill val &= ~a10_mixers[dev].mask; 273*16025c35SJared McNeill val |= (nvol << a10_mixers[dev].shift); 274*16025c35SJared McNeill CODEC_WRITE(sc, a10_mixers[dev].reg, val); 275ba9b7163SAndrew Turner 276ba9b7163SAndrew Turner left = right = (left * 100) / max; 277ba9b7163SAndrew Turner return (left | (right << 8)); 278ba9b7163SAndrew Turner } 279ba9b7163SAndrew Turner 280ba9b7163SAndrew Turner static uint32_t 281*16025c35SJared McNeill a10_mixer_setrecsrc(struct snd_mixer *m, uint32_t src) 282ba9b7163SAndrew Turner { 283ba9b7163SAndrew Turner struct a10codec_info *sc = mix_getdevinfo(m); 284ba9b7163SAndrew Turner uint32_t val; 285ba9b7163SAndrew Turner 286*16025c35SJared McNeill val = CODEC_READ(sc, A10_ADC_ACTL); 287ba9b7163SAndrew Turner 288ba9b7163SAndrew Turner switch (src) { 289ba9b7163SAndrew Turner case SOUND_MASK_LINE: /* line-in */ 290*16025c35SJared McNeill val &= ~A10_ADCIS_MASK; 291*16025c35SJared McNeill val |= (A10_ADC_IS_LINEIN << A10_ADCIS_SHIFT); 292ba9b7163SAndrew Turner break; 293ba9b7163SAndrew Turner case SOUND_MASK_MIC: /* MIC1 */ 294*16025c35SJared McNeill val &= ~A10_ADCIS_MASK; 295*16025c35SJared McNeill val |= (A10_ADC_IS_MIC1 << A10_ADCIS_SHIFT); 296ba9b7163SAndrew Turner break; 297ba9b7163SAndrew Turner case SOUND_MASK_LINE1: /* MIC2 */ 298*16025c35SJared McNeill val &= ~A10_ADCIS_MASK; 299*16025c35SJared McNeill val |= (A10_ADC_IS_MIC2 << A10_ADCIS_SHIFT); 300ba9b7163SAndrew Turner break; 301ba9b7163SAndrew Turner default: 302ba9b7163SAndrew Turner break; 303ba9b7163SAndrew Turner } 304ba9b7163SAndrew Turner 305*16025c35SJared McNeill CODEC_WRITE(sc, A10_ADC_ACTL, val); 306ba9b7163SAndrew Turner 307*16025c35SJared McNeill switch ((val & A10_ADCIS_MASK) >> A10_ADCIS_SHIFT) { 308*16025c35SJared McNeill case A10_ADC_IS_LINEIN: 309ba9b7163SAndrew Turner return (SOUND_MASK_LINE); 310*16025c35SJared McNeill case A10_ADC_IS_MIC1: 311ba9b7163SAndrew Turner return (SOUND_MASK_MIC); 312*16025c35SJared McNeill case A10_ADC_IS_MIC2: 313ba9b7163SAndrew Turner return (SOUND_MASK_LINE1); 314ba9b7163SAndrew Turner default: 315ba9b7163SAndrew Turner return (0); 316ba9b7163SAndrew Turner } 317ba9b7163SAndrew Turner } 318ba9b7163SAndrew Turner 319*16025c35SJared McNeill static void 320*16025c35SJared McNeill a10_mute(struct a10codec_info *sc, int mute, int dir) 321*16025c35SJared McNeill { 322*16025c35SJared McNeill uint32_t val; 323*16025c35SJared McNeill 324*16025c35SJared McNeill if (dir == PCMDIR_PLAY) { 325*16025c35SJared McNeill val = CODEC_READ(sc, A10_DAC_ACTL); 326*16025c35SJared McNeill if (mute) { 327*16025c35SJared McNeill /* Disable DAC analog l/r channels and output mixer */ 328*16025c35SJared McNeill val &= ~A10_DACAREN; 329*16025c35SJared McNeill val &= ~A10_DACALEN; 330*16025c35SJared McNeill val &= ~A10_DACPAS; 331*16025c35SJared McNeill } else { 332*16025c35SJared McNeill /* Enable DAC analog l/r channels and output mixer */ 333*16025c35SJared McNeill val |= A10_DACAREN; 334*16025c35SJared McNeill val |= A10_DACALEN; 335*16025c35SJared McNeill val |= A10_DACPAS; 336*16025c35SJared McNeill } 337*16025c35SJared McNeill CODEC_WRITE(sc, A10_DAC_ACTL, val); 338*16025c35SJared McNeill } else { 339*16025c35SJared McNeill val = CODEC_READ(sc, A10_ADC_ACTL); 340*16025c35SJared McNeill if (mute) { 341*16025c35SJared McNeill /* Disable ADC analog l/r channels, MIC1 preamp, 342*16025c35SJared McNeill * and VMIC pin voltage 343*16025c35SJared McNeill */ 344*16025c35SJared McNeill val &= ~A10_ADCREN; 345*16025c35SJared McNeill val &= ~A10_ADCLEN; 346*16025c35SJared McNeill val &= ~A10_PREG1EN; 347*16025c35SJared McNeill val &= ~A10_VMICEN; 348*16025c35SJared McNeill } else { 349*16025c35SJared McNeill /* Enable ADC analog l/r channels, MIC1 preamp, 350*16025c35SJared McNeill * and VMIC pin voltage 351*16025c35SJared McNeill */ 352*16025c35SJared McNeill val |= A10_ADCREN; 353*16025c35SJared McNeill val |= A10_ADCLEN; 354*16025c35SJared McNeill val |= A10_PREG1EN; 355*16025c35SJared McNeill val |= A10_VMICEN; 356*16025c35SJared McNeill } 357*16025c35SJared McNeill CODEC_WRITE(sc, A10_ADC_ACTL, val); 358*16025c35SJared McNeill } 359*16025c35SJared McNeill } 360*16025c35SJared McNeill 361*16025c35SJared McNeill static kobj_method_t a10_mixer_methods[] = { 362*16025c35SJared McNeill KOBJMETHOD(mixer_init, a10_mixer_init), 363*16025c35SJared McNeill KOBJMETHOD(mixer_set, a10_mixer_set), 364*16025c35SJared McNeill KOBJMETHOD(mixer_setrecsrc, a10_mixer_setrecsrc), 365ba9b7163SAndrew Turner KOBJMETHOD_END 366ba9b7163SAndrew Turner }; 367*16025c35SJared McNeill MIXER_DECLARE(a10_mixer); 368*16025c35SJared McNeill 369*16025c35SJared McNeill 370*16025c35SJared McNeill /* 371*16025c35SJared McNeill * H3 mixer interface 372*16025c35SJared McNeill */ 373*16025c35SJared McNeill 374*16025c35SJared McNeill #define H3_PR_CFG 0x00 375*16025c35SJared McNeill #define H3_AC_PR_RST (1 << 18) 376*16025c35SJared McNeill #define H3_AC_PR_RW (1 << 24) 377*16025c35SJared McNeill #define H3_AC_PR_ADDR_SHIFT 16 378*16025c35SJared McNeill #define H3_AC_PR_ADDR_MASK (0x1f << H3_AC_PR_ADDR_SHIFT) 379*16025c35SJared McNeill #define H3_ACDA_PR_WDAT_SHIFT 8 380*16025c35SJared McNeill #define H3_ACDA_PR_WDAT_MASK (0xff << H3_ACDA_PR_WDAT_SHIFT) 381*16025c35SJared McNeill #define H3_ACDA_PR_RDAT_SHIFT 0 382*16025c35SJared McNeill #define H3_ACDA_PR_RDAT_MASK (0xff << H3_ACDA_PR_RDAT_SHIFT) 383*16025c35SJared McNeill 384*16025c35SJared McNeill #define H3_LOMIXSC 0x01 385*16025c35SJared McNeill #define H3_LOMIXSC_LDAC (1 << 1) 386*16025c35SJared McNeill #define H3_ROMIXSC 0x02 387*16025c35SJared McNeill #define H3_ROMIXSC_RDAC (1 << 1) 388*16025c35SJared McNeill #define H3_DAC_PA_SRC 0x03 389*16025c35SJared McNeill #define H3_DACAREN (1 << 7) 390*16025c35SJared McNeill #define H3_DACALEN (1 << 6) 391*16025c35SJared McNeill #define H3_RMIXEN (1 << 5) 392*16025c35SJared McNeill #define H3_LMIXEN (1 << 4) 393*16025c35SJared McNeill #define H3_LINEIN_GCTR 0x05 394*16025c35SJared McNeill #define H3_LINEING_SHIFT 4 395*16025c35SJared McNeill #define H3_LINEING_MASK (0x7 << H3_LINEING_SHIFT) 396*16025c35SJared McNeill #define H3_MIC_GCTR 0x06 397*16025c35SJared McNeill #define H3_MIC1_GAIN_SHIFT 4 398*16025c35SJared McNeill #define H3_MIC1_GAIN_MASK (0x7 << H3_MIC1_GAIN_SHIFT) 399*16025c35SJared McNeill #define H3_MIC2_GAIN_SHIFT 0 400*16025c35SJared McNeill #define H3_MIC2_GAIN_MASK (0x7 << H3_MIC2_GAIN_SHIFT) 401*16025c35SJared McNeill #define H3_PAEN_CTR 0x07 402*16025c35SJared McNeill #define H3_LINEOUTEN (1 << 7) 403*16025c35SJared McNeill #define H3_LINEOUT_VOLC 0x09 404*16025c35SJared McNeill #define H3_LINEOUTVOL_SHIFT 3 405*16025c35SJared McNeill #define H3_LINEOUTVOL_MASK (0x1f << H3_LINEOUTVOL_SHIFT) 406*16025c35SJared McNeill #define H3_MIC2G_LINEOUT_CTR 0x0a 407*16025c35SJared McNeill #define H3_LINEOUT_LSEL (1 << 3) 408*16025c35SJared McNeill #define H3_LINEOUT_RSEL (1 << 2) 409*16025c35SJared McNeill #define H3_LADCMIXSC 0x0c 410*16025c35SJared McNeill #define H3_RADCMIXSC 0x0d 411*16025c35SJared McNeill #define H3_ADCMIXSC_MIC1 (1 << 6) 412*16025c35SJared McNeill #define H3_ADCMIXSC_MIC2 (1 << 5) 413*16025c35SJared McNeill #define H3_ADCMIXSC_LINEIN (1 << 2) 414*16025c35SJared McNeill #define H3_ADCMIXSC_OMIXER (3 << 0) 415*16025c35SJared McNeill #define H3_ADC_AP_EN 0x0f 416*16025c35SJared McNeill #define H3_ADCREN (1 << 7) 417*16025c35SJared McNeill #define H3_ADCLEN (1 << 6) 418*16025c35SJared McNeill #define H3_ADCG_SHIFT 0 419*16025c35SJared McNeill #define H3_ADCG_MASK (0x7 << H3_ADCG_SHIFT) 420*16025c35SJared McNeill 421*16025c35SJared McNeill static u_int 422*16025c35SJared McNeill h3_pr_read(struct a10codec_info *sc, u_int addr) 423*16025c35SJared McNeill { 424*16025c35SJared McNeill uint32_t val; 425*16025c35SJared McNeill 426*16025c35SJared McNeill /* Read current value */ 427*16025c35SJared McNeill val = bus_read_4(sc->res[1], H3_PR_CFG); 428*16025c35SJared McNeill 429*16025c35SJared McNeill /* De-assert reset */ 430*16025c35SJared McNeill val |= H3_AC_PR_RST; 431*16025c35SJared McNeill bus_write_4(sc->res[1], H3_PR_CFG, val); 432*16025c35SJared McNeill 433*16025c35SJared McNeill /* Read mode */ 434*16025c35SJared McNeill val &= ~H3_AC_PR_RW; 435*16025c35SJared McNeill bus_write_4(sc->res[1], H3_PR_CFG, val); 436*16025c35SJared McNeill 437*16025c35SJared McNeill /* Set address */ 438*16025c35SJared McNeill val &= ~H3_AC_PR_ADDR_MASK; 439*16025c35SJared McNeill val |= (addr << H3_AC_PR_ADDR_SHIFT); 440*16025c35SJared McNeill bus_write_4(sc->res[1], H3_PR_CFG, val); 441*16025c35SJared McNeill 442*16025c35SJared McNeill /* Read data */ 443*16025c35SJared McNeill return (bus_read_4(sc->res[1], H3_PR_CFG) & H3_ACDA_PR_RDAT_MASK); 444*16025c35SJared McNeill } 445*16025c35SJared McNeill 446*16025c35SJared McNeill static void 447*16025c35SJared McNeill h3_pr_write(struct a10codec_info *sc, u_int addr, u_int data) 448*16025c35SJared McNeill { 449*16025c35SJared McNeill uint32_t val; 450*16025c35SJared McNeill 451*16025c35SJared McNeill /* Read current value */ 452*16025c35SJared McNeill val = bus_read_4(sc->res[1], H3_PR_CFG); 453*16025c35SJared McNeill 454*16025c35SJared McNeill /* De-assert reset */ 455*16025c35SJared McNeill val |= H3_AC_PR_RST; 456*16025c35SJared McNeill bus_write_4(sc->res[1], H3_PR_CFG, val); 457*16025c35SJared McNeill 458*16025c35SJared McNeill /* Set address */ 459*16025c35SJared McNeill val &= ~H3_AC_PR_ADDR_MASK; 460*16025c35SJared McNeill val |= (addr << H3_AC_PR_ADDR_SHIFT); 461*16025c35SJared McNeill bus_write_4(sc->res[1], H3_PR_CFG, val); 462*16025c35SJared McNeill 463*16025c35SJared McNeill /* Write data */ 464*16025c35SJared McNeill val &= ~H3_ACDA_PR_WDAT_MASK; 465*16025c35SJared McNeill val |= (data << H3_ACDA_PR_WDAT_SHIFT); 466*16025c35SJared McNeill bus_write_4(sc->res[1], H3_PR_CFG, val); 467*16025c35SJared McNeill 468*16025c35SJared McNeill /* Write mode */ 469*16025c35SJared McNeill val |= H3_AC_PR_RW; 470*16025c35SJared McNeill bus_write_4(sc->res[1], H3_PR_CFG, val); 471*16025c35SJared McNeill } 472*16025c35SJared McNeill 473*16025c35SJared McNeill static void 474*16025c35SJared McNeill h3_pr_set_clear(struct a10codec_info *sc, u_int addr, u_int set, u_int clr) 475*16025c35SJared McNeill { 476*16025c35SJared McNeill u_int old, new; 477*16025c35SJared McNeill 478*16025c35SJared McNeill old = h3_pr_read(sc, addr); 479*16025c35SJared McNeill new = set | (old & ~clr); 480*16025c35SJared McNeill h3_pr_write(sc, addr, new); 481*16025c35SJared McNeill } 482*16025c35SJared McNeill 483*16025c35SJared McNeill static int 484*16025c35SJared McNeill h3_mixer_init(struct snd_mixer *m) 485*16025c35SJared McNeill { 486*16025c35SJared McNeill struct a10codec_info *sc = mix_getdevinfo(m); 487*16025c35SJared McNeill 488*16025c35SJared McNeill mix_setdevs(m, SOUND_MASK_PCM | SOUND_MASK_VOLUME | SOUND_MASK_RECLEV | 489*16025c35SJared McNeill SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_LINE1); 490*16025c35SJared McNeill mix_setrecdevs(m, SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_LINE1 | 491*16025c35SJared McNeill SOUND_MASK_IMIX); 492*16025c35SJared McNeill 493*16025c35SJared McNeill pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL); 494*16025c35SJared McNeill 495*16025c35SJared McNeill /* Right & Left LINEOUT enable */ 496*16025c35SJared McNeill h3_pr_set_clear(sc, H3_PAEN_CTR, H3_LINEOUTEN, 0); 497*16025c35SJared McNeill h3_pr_set_clear(sc, H3_MIC2G_LINEOUT_CTR, 498*16025c35SJared McNeill H3_LINEOUT_LSEL | H3_LINEOUT_RSEL, 0); 499*16025c35SJared McNeill 500*16025c35SJared McNeill return (0); 501*16025c35SJared McNeill } 502*16025c35SJared McNeill 503*16025c35SJared McNeill static const struct h3_mixer { 504*16025c35SJared McNeill unsigned reg; 505*16025c35SJared McNeill unsigned mask; 506*16025c35SJared McNeill unsigned shift; 507*16025c35SJared McNeill } h3_mixers[SOUND_MIXER_NRDEVICES] = { 508*16025c35SJared McNeill [SOUND_MIXER_VOLUME] = { H3_LINEOUT_VOLC, H3_LINEOUTVOL_MASK, 509*16025c35SJared McNeill H3_LINEOUTVOL_SHIFT }, 510*16025c35SJared McNeill [SOUND_MIXER_RECLEV] = { H3_ADC_AP_EN, H3_ADCG_MASK, 511*16025c35SJared McNeill H3_ADCG_SHIFT }, 512*16025c35SJared McNeill [SOUND_MIXER_LINE] = { H3_LINEIN_GCTR, H3_LINEING_MASK, 513*16025c35SJared McNeill H3_LINEING_SHIFT }, 514*16025c35SJared McNeill [SOUND_MIXER_MIC] = { H3_MIC_GCTR, H3_MIC1_GAIN_MASK, 515*16025c35SJared McNeill H3_MIC1_GAIN_SHIFT }, 516*16025c35SJared McNeill [SOUND_MIXER_LINE1] = { H3_MIC_GCTR, H3_MIC2_GAIN_MASK, 517*16025c35SJared McNeill H3_MIC2_GAIN_SHIFT }, 518*16025c35SJared McNeill }; 519*16025c35SJared McNeill 520*16025c35SJared McNeill static int 521*16025c35SJared McNeill h3_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, 522*16025c35SJared McNeill unsigned right) 523*16025c35SJared McNeill { 524*16025c35SJared McNeill struct a10codec_info *sc = mix_getdevinfo(m); 525*16025c35SJared McNeill unsigned nvol, max; 526*16025c35SJared McNeill 527*16025c35SJared McNeill max = h3_mixers[dev].mask >> h3_mixers[dev].shift; 528*16025c35SJared McNeill nvol = (left * max) / 100; 529*16025c35SJared McNeill 530*16025c35SJared McNeill h3_pr_set_clear(sc, h3_mixers[dev].reg, 531*16025c35SJared McNeill nvol << h3_mixers[dev].shift, h3_mixers[dev].mask); 532*16025c35SJared McNeill 533*16025c35SJared McNeill left = right = (left * 100) / max; 534*16025c35SJared McNeill return (left | (right << 8)); 535*16025c35SJared McNeill } 536*16025c35SJared McNeill 537*16025c35SJared McNeill static uint32_t 538*16025c35SJared McNeill h3_mixer_setrecsrc(struct snd_mixer *m, uint32_t src) 539*16025c35SJared McNeill { 540*16025c35SJared McNeill struct a10codec_info *sc = mix_getdevinfo(m); 541*16025c35SJared McNeill uint32_t val; 542*16025c35SJared McNeill 543*16025c35SJared McNeill val = 0; 544*16025c35SJared McNeill src &= (SOUND_MASK_LINE | SOUND_MASK_MIC | 545*16025c35SJared McNeill SOUND_MASK_LINE1 | SOUND_MASK_IMIX); 546*16025c35SJared McNeill 547*16025c35SJared McNeill if ((src & SOUND_MASK_LINE) != 0) /* line-in */ 548*16025c35SJared McNeill val |= H3_ADCMIXSC_LINEIN; 549*16025c35SJared McNeill if ((src & SOUND_MASK_MIC) != 0) /* MIC1 */ 550*16025c35SJared McNeill val |= H3_ADCMIXSC_MIC1; 551*16025c35SJared McNeill if ((src & SOUND_MASK_LINE1) != 0) /* MIC2 */ 552*16025c35SJared McNeill val |= H3_ADCMIXSC_MIC2; 553*16025c35SJared McNeill if ((src & SOUND_MASK_IMIX) != 0) /* l/r output mixer */ 554*16025c35SJared McNeill val |= H3_ADCMIXSC_OMIXER; 555*16025c35SJared McNeill 556*16025c35SJared McNeill h3_pr_write(sc, H3_LADCMIXSC, val); 557*16025c35SJared McNeill h3_pr_write(sc, H3_RADCMIXSC, val); 558*16025c35SJared McNeill 559*16025c35SJared McNeill return (src); 560*16025c35SJared McNeill } 561*16025c35SJared McNeill 562*16025c35SJared McNeill static void 563*16025c35SJared McNeill h3_mute(struct a10codec_info *sc, int mute, int dir) 564*16025c35SJared McNeill { 565*16025c35SJared McNeill if (dir == PCMDIR_PLAY) { 566*16025c35SJared McNeill if (mute) { 567*16025c35SJared McNeill /* Mute DAC l/r channels to output mixer */ 568*16025c35SJared McNeill h3_pr_set_clear(sc, H3_LOMIXSC, 0, H3_LOMIXSC_LDAC); 569*16025c35SJared McNeill h3_pr_set_clear(sc, H3_ROMIXSC, 0, H3_ROMIXSC_RDAC); 570*16025c35SJared McNeill /* Disable DAC analog l/r channels and output mixer */ 571*16025c35SJared McNeill h3_pr_set_clear(sc, H3_DAC_PA_SRC, 572*16025c35SJared McNeill 0, H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN); 573*16025c35SJared McNeill } else { 574*16025c35SJared McNeill /* Enable DAC analog l/r channels and output mixer */ 575*16025c35SJared McNeill h3_pr_set_clear(sc, H3_DAC_PA_SRC, 576*16025c35SJared McNeill H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN, 0); 577*16025c35SJared McNeill /* Unmute DAC l/r channels to output mixer */ 578*16025c35SJared McNeill h3_pr_set_clear(sc, H3_LOMIXSC, H3_LOMIXSC_LDAC, 0); 579*16025c35SJared McNeill h3_pr_set_clear(sc, H3_ROMIXSC, H3_ROMIXSC_RDAC, 0); 580*16025c35SJared McNeill } 581*16025c35SJared McNeill } else { 582*16025c35SJared McNeill if (mute) { 583*16025c35SJared McNeill /* Disable ADC analog l/r channels */ 584*16025c35SJared McNeill h3_pr_set_clear(sc, H3_ADC_AP_EN, 585*16025c35SJared McNeill 0, H3_ADCREN | H3_ADCLEN); 586*16025c35SJared McNeill } else { 587*16025c35SJared McNeill /* Enable ADC analog l/r channels */ 588*16025c35SJared McNeill h3_pr_set_clear(sc, H3_ADC_AP_EN, 589*16025c35SJared McNeill H3_ADCREN | H3_ADCLEN, 0); 590*16025c35SJared McNeill } 591*16025c35SJared McNeill } 592*16025c35SJared McNeill } 593*16025c35SJared McNeill 594*16025c35SJared McNeill static kobj_method_t h3_mixer_methods[] = { 595*16025c35SJared McNeill KOBJMETHOD(mixer_init, h3_mixer_init), 596*16025c35SJared McNeill KOBJMETHOD(mixer_set, h3_mixer_set), 597*16025c35SJared McNeill KOBJMETHOD(mixer_setrecsrc, h3_mixer_setrecsrc), 598*16025c35SJared McNeill KOBJMETHOD_END 599*16025c35SJared McNeill }; 600*16025c35SJared McNeill MIXER_DECLARE(h3_mixer); 601ba9b7163SAndrew Turner 602ba9b7163SAndrew Turner 603ba9b7163SAndrew Turner /* 604ba9b7163SAndrew Turner * Channel interface 605ba9b7163SAndrew Turner */ 606ba9b7163SAndrew Turner 607ba9b7163SAndrew Turner static void 608ba9b7163SAndrew Turner a10codec_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 609ba9b7163SAndrew Turner { 610ba9b7163SAndrew Turner struct a10codec_chinfo *ch = arg; 611ba9b7163SAndrew Turner 612ba9b7163SAndrew Turner if (error != 0) 613ba9b7163SAndrew Turner return; 614ba9b7163SAndrew Turner 615ba9b7163SAndrew Turner ch->physaddr = segs[0].ds_addr; 616ba9b7163SAndrew Turner } 617ba9b7163SAndrew Turner 618ba9b7163SAndrew Turner static void 619ba9b7163SAndrew Turner a10codec_transfer(struct a10codec_chinfo *ch) 620ba9b7163SAndrew Turner { 621ba9b7163SAndrew Turner bus_addr_t src, dst; 622ba9b7163SAndrew Turner int error; 623ba9b7163SAndrew Turner 624ba9b7163SAndrew Turner if (ch->dir == PCMDIR_PLAY) { 625ba9b7163SAndrew Turner src = ch->physaddr + ch->pos; 626ba9b7163SAndrew Turner dst = ch->fifo; 627ba9b7163SAndrew Turner } else { 628ba9b7163SAndrew Turner src = ch->fifo; 629ba9b7163SAndrew Turner dst = ch->physaddr + ch->pos; 630ba9b7163SAndrew Turner } 631ba9b7163SAndrew Turner 632ba9b7163SAndrew Turner error = SUNXI_DMA_TRANSFER(ch->dmac, ch->dmachan, src, dst, 633ba9b7163SAndrew Turner ch->blocksize); 634ba9b7163SAndrew Turner if (error) { 635ba9b7163SAndrew Turner ch->run = 0; 636ba9b7163SAndrew Turner device_printf(ch->parent->dev, "DMA transfer failed: %d\n", 637ba9b7163SAndrew Turner error); 638ba9b7163SAndrew Turner } 639ba9b7163SAndrew Turner } 640ba9b7163SAndrew Turner 641ba9b7163SAndrew Turner static void 642ba9b7163SAndrew Turner a10codec_dmaconfig(struct a10codec_chinfo *ch) 643ba9b7163SAndrew Turner { 644ba9b7163SAndrew Turner struct a10codec_info *sc = ch->parent; 645ba9b7163SAndrew Turner struct sunxi_dma_config conf; 646ba9b7163SAndrew Turner 647ba9b7163SAndrew Turner memset(&conf, 0, sizeof(conf)); 648ba9b7163SAndrew Turner conf.src_width = conf.dst_width = 16; 649ba9b7163SAndrew Turner conf.src_burst_len = conf.dst_burst_len = 4; 650ba9b7163SAndrew Turner 651ba9b7163SAndrew Turner if (ch->dir == PCMDIR_PLAY) { 652ba9b7163SAndrew Turner conf.dst_noincr = true; 653*16025c35SJared McNeill conf.src_drqtype = sc->cfg->drqtype_sdram; 654*16025c35SJared McNeill conf.dst_drqtype = sc->cfg->drqtype_codec; 655ba9b7163SAndrew Turner } else { 656ba9b7163SAndrew Turner conf.src_noincr = true; 657*16025c35SJared McNeill conf.src_drqtype = sc->cfg->drqtype_codec; 658*16025c35SJared McNeill conf.dst_drqtype = sc->cfg->drqtype_sdram; 659ba9b7163SAndrew Turner } 660ba9b7163SAndrew Turner 661ba9b7163SAndrew Turner SUNXI_DMA_SET_CONFIG(ch->dmac, ch->dmachan, &conf); 662ba9b7163SAndrew Turner } 663ba9b7163SAndrew Turner 664ba9b7163SAndrew Turner static void 665ba9b7163SAndrew Turner a10codec_dmaintr(void *priv) 666ba9b7163SAndrew Turner { 667ba9b7163SAndrew Turner struct a10codec_chinfo *ch = priv; 668ba9b7163SAndrew Turner unsigned bufsize; 669ba9b7163SAndrew Turner 670ba9b7163SAndrew Turner bufsize = sndbuf_getsize(ch->buffer); 671ba9b7163SAndrew Turner 672ba9b7163SAndrew Turner ch->pos += ch->blocksize; 673ba9b7163SAndrew Turner if (ch->pos >= bufsize) 674ba9b7163SAndrew Turner ch->pos -= bufsize; 675ba9b7163SAndrew Turner 676ba9b7163SAndrew Turner if (ch->run) { 677ba9b7163SAndrew Turner chn_intr(ch->channel); 678ba9b7163SAndrew Turner a10codec_transfer(ch); 679ba9b7163SAndrew Turner } 680ba9b7163SAndrew Turner } 681ba9b7163SAndrew Turner 682ba9b7163SAndrew Turner static unsigned 683ba9b7163SAndrew Turner a10codec_fs(struct a10codec_chinfo *ch) 684ba9b7163SAndrew Turner { 685ba9b7163SAndrew Turner switch (ch->speed) { 686ba9b7163SAndrew Turner case 48000: 687ba9b7163SAndrew Turner return (DAC_FS_48KHZ); 688ba9b7163SAndrew Turner case 24000: 689ba9b7163SAndrew Turner return (DAC_FS_24KHZ); 690ba9b7163SAndrew Turner case 12000: 691ba9b7163SAndrew Turner return (DAC_FS_12KHZ); 692ba9b7163SAndrew Turner case 192000: 693ba9b7163SAndrew Turner return (DAC_FS_192KHZ); 694ba9b7163SAndrew Turner case 32000: 695ba9b7163SAndrew Turner return (DAC_FS_32KHZ); 696ba9b7163SAndrew Turner case 16000: 697ba9b7163SAndrew Turner return (DAC_FS_16KHZ); 698ba9b7163SAndrew Turner case 8000: 699ba9b7163SAndrew Turner return (DAC_FS_8KHZ); 700ba9b7163SAndrew Turner case 96000: 701ba9b7163SAndrew Turner return (DAC_FS_96KHZ); 702ba9b7163SAndrew Turner default: 703ba9b7163SAndrew Turner return (DAC_FS_48KHZ); 704ba9b7163SAndrew Turner } 705ba9b7163SAndrew Turner } 706ba9b7163SAndrew Turner 707ba9b7163SAndrew Turner static void 708ba9b7163SAndrew Turner a10codec_start(struct a10codec_chinfo *ch) 709ba9b7163SAndrew Turner { 710ba9b7163SAndrew Turner struct a10codec_info *sc = ch->parent; 711ba9b7163SAndrew Turner uint32_t val; 712ba9b7163SAndrew Turner 713ba9b7163SAndrew Turner ch->pos = 0; 714ba9b7163SAndrew Turner 715ba9b7163SAndrew Turner if (ch->dir == PCMDIR_PLAY) { 716ba9b7163SAndrew Turner /* Flush DAC FIFO */ 717*16025c35SJared McNeill CODEC_WRITE(sc, AC_DAC_FIFOC(sc), DAC_FIFOC_FIFO_FLUSH); 718ba9b7163SAndrew Turner 719ba9b7163SAndrew Turner /* Clear DAC FIFO status */ 720*16025c35SJared McNeill CODEC_WRITE(sc, AC_DAC_FIFOS(sc), 721*16025c35SJared McNeill CODEC_READ(sc, AC_DAC_FIFOS(sc))); 722ba9b7163SAndrew Turner 723*16025c35SJared McNeill /* Unmute output */ 724*16025c35SJared McNeill sc->cfg->mute(sc, 0, ch->dir); 725ba9b7163SAndrew Turner 726ba9b7163SAndrew Turner /* Configure DAC DMA channel */ 727ba9b7163SAndrew Turner a10codec_dmaconfig(ch); 728ba9b7163SAndrew Turner 729ba9b7163SAndrew Turner /* Configure DAC FIFO */ 730*16025c35SJared McNeill CODEC_WRITE(sc, AC_DAC_FIFOC(sc), 731ba9b7163SAndrew Turner (AFMT_CHANNEL(ch->format) == 1 ? DAC_FIFOC_MONO_EN : 0) | 732ba9b7163SAndrew Turner (a10codec_fs(ch) << DAC_FIFOC_FS_SHIFT) | 733ba9b7163SAndrew Turner (FIFO_MODE_16_15_0 << DAC_FIFOC_FIFO_MODE_SHIFT) | 734ba9b7163SAndrew Turner (DRQ_CLR_CNT << DAC_FIFOC_DRQ_CLR_CNT_SHIFT) | 735ba9b7163SAndrew Turner (TX_TRIG_LEVEL << DAC_FIFOC_TX_TRIG_LEVEL_SHIFT)); 736ba9b7163SAndrew Turner 737ba9b7163SAndrew Turner /* Enable DAC DRQ */ 738*16025c35SJared McNeill val = CODEC_READ(sc, AC_DAC_FIFOC(sc)); 739ba9b7163SAndrew Turner val |= DAC_FIFOC_DRQ_EN; 740*16025c35SJared McNeill CODEC_WRITE(sc, AC_DAC_FIFOC(sc), val); 741ba9b7163SAndrew Turner } else { 742ba9b7163SAndrew Turner /* Flush ADC FIFO */ 743*16025c35SJared McNeill CODEC_WRITE(sc, AC_ADC_FIFOC(sc), ADC_FIFOC_FIFO_FLUSH); 744ba9b7163SAndrew Turner 745ba9b7163SAndrew Turner /* Clear ADC FIFO status */ 746*16025c35SJared McNeill CODEC_WRITE(sc, AC_ADC_FIFOS(sc), 747*16025c35SJared McNeill CODEC_READ(sc, AC_ADC_FIFOS(sc))); 748ba9b7163SAndrew Turner 749*16025c35SJared McNeill /* Unmute input */ 750*16025c35SJared McNeill sc->cfg->mute(sc, 0, ch->dir); 751ba9b7163SAndrew Turner 752ba9b7163SAndrew Turner /* Configure ADC DMA channel */ 753ba9b7163SAndrew Turner a10codec_dmaconfig(ch); 754ba9b7163SAndrew Turner 755ba9b7163SAndrew Turner /* Configure ADC FIFO */ 756*16025c35SJared McNeill CODEC_WRITE(sc, AC_ADC_FIFOC(sc), 757ba9b7163SAndrew Turner ADC_FIFOC_EN_AD | 758ba9b7163SAndrew Turner ADC_FIFOC_RX_FIFO_MODE | 759ba9b7163SAndrew Turner (AFMT_CHANNEL(ch->format) == 1 ? ADC_FIFOC_MONO_EN : 0) | 760ba9b7163SAndrew Turner (a10codec_fs(ch) << ADC_FIFOC_FS_SHIFT) | 761ba9b7163SAndrew Turner (RX_TRIG_LEVEL << ADC_FIFOC_RX_TRIG_LEVEL_SHIFT)); 762ba9b7163SAndrew Turner 763ba9b7163SAndrew Turner /* Enable ADC DRQ */ 764*16025c35SJared McNeill val = CODEC_READ(sc, AC_ADC_FIFOC(sc)); 765ba9b7163SAndrew Turner val |= ADC_FIFOC_DRQ_EN; 766*16025c35SJared McNeill CODEC_WRITE(sc, AC_ADC_FIFOC(sc), val); 767ba9b7163SAndrew Turner } 768ba9b7163SAndrew Turner 769ba9b7163SAndrew Turner /* Start DMA transfer */ 770ba9b7163SAndrew Turner a10codec_transfer(ch); 771ba9b7163SAndrew Turner } 772ba9b7163SAndrew Turner 773ba9b7163SAndrew Turner static void 774ba9b7163SAndrew Turner a10codec_stop(struct a10codec_chinfo *ch) 775ba9b7163SAndrew Turner { 776ba9b7163SAndrew Turner struct a10codec_info *sc = ch->parent; 777ba9b7163SAndrew Turner 778ba9b7163SAndrew Turner /* Disable DMA channel */ 779ba9b7163SAndrew Turner SUNXI_DMA_HALT(ch->dmac, ch->dmachan); 780ba9b7163SAndrew Turner 781*16025c35SJared McNeill sc->cfg->mute(sc, 1, ch->dir); 782*16025c35SJared McNeill 783ba9b7163SAndrew Turner if (ch->dir == PCMDIR_PLAY) { 784ba9b7163SAndrew Turner /* Disable DAC DRQ */ 785*16025c35SJared McNeill CODEC_WRITE(sc, AC_DAC_FIFOC(sc), 0); 786ba9b7163SAndrew Turner } else { 787ba9b7163SAndrew Turner /* Disable ADC DRQ */ 788*16025c35SJared McNeill CODEC_WRITE(sc, AC_ADC_FIFOC(sc), 0); 789ba9b7163SAndrew Turner } 790ba9b7163SAndrew Turner } 791ba9b7163SAndrew Turner 792ba9b7163SAndrew Turner static void * 793ba9b7163SAndrew Turner a10codec_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 794ba9b7163SAndrew Turner struct pcm_channel *c, int dir) 795ba9b7163SAndrew Turner { 796ba9b7163SAndrew Turner struct a10codec_info *sc = devinfo; 797ba9b7163SAndrew Turner struct a10codec_chinfo *ch = dir == PCMDIR_PLAY ? &sc->play : &sc->rec; 798*16025c35SJared McNeill phandle_t xref; 799*16025c35SJared McNeill pcell_t *cells; 800*16025c35SJared McNeill int ncells, error; 801*16025c35SJared McNeill 802*16025c35SJared McNeill error = ofw_bus_parse_xref_list_alloc(ofw_bus_get_node(sc->dev), 803*16025c35SJared McNeill "dmas", "#dma-cells", dir == PCMDIR_PLAY ? 1 : 0, 804*16025c35SJared McNeill &xref, &ncells, &cells); 805*16025c35SJared McNeill if (error != 0) { 806*16025c35SJared McNeill device_printf(sc->dev, "cannot parse 'dmas' property\n"); 807*16025c35SJared McNeill return (NULL); 808*16025c35SJared McNeill } 809*16025c35SJared McNeill OF_prop_free(cells); 810ba9b7163SAndrew Turner 811ba9b7163SAndrew Turner ch->parent = sc; 812ba9b7163SAndrew Turner ch->channel = c; 813ba9b7163SAndrew Turner ch->buffer = b; 814ba9b7163SAndrew Turner ch->dir = dir; 815ba9b7163SAndrew Turner ch->fifo = rman_get_start(sc->res[0]) + 816*16025c35SJared McNeill (dir == PCMDIR_REC ? AC_ADC_RXDATA(sc) : AC_DAC_TXDATA(sc)); 817ba9b7163SAndrew Turner 818*16025c35SJared McNeill ch->dmac = OF_device_from_xref(xref); 819ba9b7163SAndrew Turner if (ch->dmac == NULL) { 820ba9b7163SAndrew Turner device_printf(sc->dev, "cannot find DMA controller\n"); 821*16025c35SJared McNeill device_printf(sc->dev, "xref = 0x%x\n", (u_int)xref); 822ba9b7163SAndrew Turner return (NULL); 823ba9b7163SAndrew Turner } 824ba9b7163SAndrew Turner ch->dmachan = SUNXI_DMA_ALLOC(ch->dmac, false, a10codec_dmaintr, ch); 825ba9b7163SAndrew Turner if (ch->dmachan == NULL) { 826ba9b7163SAndrew Turner device_printf(sc->dev, "cannot allocate DMA channel\n"); 827ba9b7163SAndrew Turner return (NULL); 828ba9b7163SAndrew Turner } 829ba9b7163SAndrew Turner 830ba9b7163SAndrew Turner error = bus_dmamem_alloc(sc->dmat, &ch->dmaaddr, 831ba9b7163SAndrew Turner BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &ch->dmamap); 832ba9b7163SAndrew Turner if (error != 0) { 833ba9b7163SAndrew Turner device_printf(sc->dev, "cannot allocate channel buffer\n"); 834ba9b7163SAndrew Turner return (NULL); 835ba9b7163SAndrew Turner } 836ba9b7163SAndrew Turner error = bus_dmamap_load(sc->dmat, ch->dmamap, ch->dmaaddr, 837ba9b7163SAndrew Turner sc->dmasize, a10codec_dmamap_cb, ch, BUS_DMA_NOWAIT); 838ba9b7163SAndrew Turner if (error != 0) { 839ba9b7163SAndrew Turner device_printf(sc->dev, "cannot load DMA map\n"); 840ba9b7163SAndrew Turner return (NULL); 841ba9b7163SAndrew Turner } 842ba9b7163SAndrew Turner memset(ch->dmaaddr, 0, sc->dmasize); 843ba9b7163SAndrew Turner 844ba9b7163SAndrew Turner if (sndbuf_setup(ch->buffer, ch->dmaaddr, sc->dmasize) != 0) { 845ba9b7163SAndrew Turner device_printf(sc->dev, "cannot setup sndbuf\n"); 846ba9b7163SAndrew Turner return (NULL); 847ba9b7163SAndrew Turner } 848ba9b7163SAndrew Turner 849ba9b7163SAndrew Turner return (ch); 850ba9b7163SAndrew Turner } 851ba9b7163SAndrew Turner 852ba9b7163SAndrew Turner static int 853ba9b7163SAndrew Turner a10codec_chan_free(kobj_t obj, void *data) 854ba9b7163SAndrew Turner { 855ba9b7163SAndrew Turner struct a10codec_chinfo *ch = data; 856ba9b7163SAndrew Turner struct a10codec_info *sc = ch->parent; 857ba9b7163SAndrew Turner 858ba9b7163SAndrew Turner SUNXI_DMA_FREE(ch->dmac, ch->dmachan); 859ba9b7163SAndrew Turner bus_dmamap_unload(sc->dmat, ch->dmamap); 860ba9b7163SAndrew Turner bus_dmamem_free(sc->dmat, ch->dmaaddr, ch->dmamap); 861ba9b7163SAndrew Turner 862ba9b7163SAndrew Turner return (0); 863ba9b7163SAndrew Turner } 864ba9b7163SAndrew Turner 865ba9b7163SAndrew Turner static int 866ba9b7163SAndrew Turner a10codec_chan_setformat(kobj_t obj, void *data, uint32_t format) 867ba9b7163SAndrew Turner { 868ba9b7163SAndrew Turner struct a10codec_chinfo *ch = data; 869ba9b7163SAndrew Turner 870ba9b7163SAndrew Turner ch->format = format; 871ba9b7163SAndrew Turner 872ba9b7163SAndrew Turner return (0); 873ba9b7163SAndrew Turner } 874ba9b7163SAndrew Turner 875ba9b7163SAndrew Turner static uint32_t 876ba9b7163SAndrew Turner a10codec_chan_setspeed(kobj_t obj, void *data, uint32_t speed) 877ba9b7163SAndrew Turner { 878ba9b7163SAndrew Turner struct a10codec_chinfo *ch = data; 879ba9b7163SAndrew Turner 880ba9b7163SAndrew Turner /* 881ba9b7163SAndrew Turner * The codec supports full duplex operation but both DAC and ADC 882ba9b7163SAndrew Turner * use the same source clock (PLL2). Limit the available speeds to 883ba9b7163SAndrew Turner * those supported by a 24576000 Hz input. 884ba9b7163SAndrew Turner */ 885ba9b7163SAndrew Turner switch (speed) { 886ba9b7163SAndrew Turner case 8000: 887ba9b7163SAndrew Turner case 12000: 888ba9b7163SAndrew Turner case 16000: 889ba9b7163SAndrew Turner case 24000: 890ba9b7163SAndrew Turner case 32000: 891ba9b7163SAndrew Turner case 48000: 892ba9b7163SAndrew Turner ch->speed = speed; 893ba9b7163SAndrew Turner break; 894ba9b7163SAndrew Turner case 96000: 895ba9b7163SAndrew Turner case 192000: 896ba9b7163SAndrew Turner /* 96 KHz / 192 KHz mode only supported for playback */ 897ba9b7163SAndrew Turner if (ch->dir == PCMDIR_PLAY) { 898ba9b7163SAndrew Turner ch->speed = speed; 899ba9b7163SAndrew Turner } else { 900ba9b7163SAndrew Turner ch->speed = 48000; 901ba9b7163SAndrew Turner } 902ba9b7163SAndrew Turner break; 903ba9b7163SAndrew Turner case 44100: 904ba9b7163SAndrew Turner ch->speed = 48000; 905ba9b7163SAndrew Turner break; 906ba9b7163SAndrew Turner case 22050: 907ba9b7163SAndrew Turner ch->speed = 24000; 908ba9b7163SAndrew Turner break; 909ba9b7163SAndrew Turner case 11025: 910ba9b7163SAndrew Turner ch->speed = 12000; 911ba9b7163SAndrew Turner break; 912ba9b7163SAndrew Turner default: 913ba9b7163SAndrew Turner ch->speed = 48000; 914ba9b7163SAndrew Turner break; 915ba9b7163SAndrew Turner } 916ba9b7163SAndrew Turner 917ba9b7163SAndrew Turner return (ch->speed); 918ba9b7163SAndrew Turner } 919ba9b7163SAndrew Turner 920ba9b7163SAndrew Turner static uint32_t 921ba9b7163SAndrew Turner a10codec_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) 922ba9b7163SAndrew Turner { 923ba9b7163SAndrew Turner struct a10codec_chinfo *ch = data; 924ba9b7163SAndrew Turner 925ba9b7163SAndrew Turner ch->blocksize = blocksize & ~3; 926ba9b7163SAndrew Turner 927ba9b7163SAndrew Turner return (ch->blocksize); 928ba9b7163SAndrew Turner } 929ba9b7163SAndrew Turner 930ba9b7163SAndrew Turner static int 931ba9b7163SAndrew Turner a10codec_chan_trigger(kobj_t obj, void *data, int go) 932ba9b7163SAndrew Turner { 933ba9b7163SAndrew Turner struct a10codec_chinfo *ch = data; 934ba9b7163SAndrew Turner struct a10codec_info *sc = ch->parent; 935ba9b7163SAndrew Turner 936ba9b7163SAndrew Turner if (!PCMTRIG_COMMON(go)) 937ba9b7163SAndrew Turner return (0); 938ba9b7163SAndrew Turner 939ba9b7163SAndrew Turner snd_mtxlock(sc->lock); 940ba9b7163SAndrew Turner switch (go) { 941ba9b7163SAndrew Turner case PCMTRIG_START: 942ba9b7163SAndrew Turner ch->run = 1; 943ba9b7163SAndrew Turner a10codec_start(ch); 944ba9b7163SAndrew Turner break; 945ba9b7163SAndrew Turner case PCMTRIG_STOP: 946ba9b7163SAndrew Turner case PCMTRIG_ABORT: 947ba9b7163SAndrew Turner ch->run = 0; 948ba9b7163SAndrew Turner a10codec_stop(ch); 949ba9b7163SAndrew Turner break; 950ba9b7163SAndrew Turner default: 951ba9b7163SAndrew Turner break; 952ba9b7163SAndrew Turner } 953ba9b7163SAndrew Turner snd_mtxunlock(sc->lock); 954ba9b7163SAndrew Turner 955ba9b7163SAndrew Turner return (0); 956ba9b7163SAndrew Turner } 957ba9b7163SAndrew Turner 958ba9b7163SAndrew Turner static uint32_t 959ba9b7163SAndrew Turner a10codec_chan_getptr(kobj_t obj, void *data) 960ba9b7163SAndrew Turner { 961ba9b7163SAndrew Turner struct a10codec_chinfo *ch = data; 962ba9b7163SAndrew Turner 963ba9b7163SAndrew Turner return (ch->pos); 964ba9b7163SAndrew Turner } 965ba9b7163SAndrew Turner 966ba9b7163SAndrew Turner static struct pcmchan_caps * 967ba9b7163SAndrew Turner a10codec_chan_getcaps(kobj_t obj, void *data) 968ba9b7163SAndrew Turner { 969ba9b7163SAndrew Turner struct a10codec_chinfo *ch = data; 970ba9b7163SAndrew Turner 971ba9b7163SAndrew Turner if (ch->dir == PCMDIR_PLAY) { 972ba9b7163SAndrew Turner return (&a10codec_pcaps); 973ba9b7163SAndrew Turner } else { 974ba9b7163SAndrew Turner return (&a10codec_rcaps); 975ba9b7163SAndrew Turner } 976ba9b7163SAndrew Turner } 977ba9b7163SAndrew Turner 978ba9b7163SAndrew Turner static kobj_method_t a10codec_chan_methods[] = { 979ba9b7163SAndrew Turner KOBJMETHOD(channel_init, a10codec_chan_init), 980ba9b7163SAndrew Turner KOBJMETHOD(channel_free, a10codec_chan_free), 981ba9b7163SAndrew Turner KOBJMETHOD(channel_setformat, a10codec_chan_setformat), 982ba9b7163SAndrew Turner KOBJMETHOD(channel_setspeed, a10codec_chan_setspeed), 983ba9b7163SAndrew Turner KOBJMETHOD(channel_setblocksize, a10codec_chan_setblocksize), 984ba9b7163SAndrew Turner KOBJMETHOD(channel_trigger, a10codec_chan_trigger), 985ba9b7163SAndrew Turner KOBJMETHOD(channel_getptr, a10codec_chan_getptr), 986ba9b7163SAndrew Turner KOBJMETHOD(channel_getcaps, a10codec_chan_getcaps), 987ba9b7163SAndrew Turner KOBJMETHOD_END 988ba9b7163SAndrew Turner }; 989ba9b7163SAndrew Turner CHANNEL_DECLARE(a10codec_chan); 990ba9b7163SAndrew Turner 991ba9b7163SAndrew Turner 992ba9b7163SAndrew Turner /* 993ba9b7163SAndrew Turner * Device interface 994ba9b7163SAndrew Turner */ 995ba9b7163SAndrew Turner 996*16025c35SJared McNeill static const struct a10codec_config a10_config = { 997*16025c35SJared McNeill .mixer_class = &a10_mixer_class, 998*16025c35SJared McNeill .mute = a10_mute, 999*16025c35SJared McNeill .drqtype_codec = 19, 1000*16025c35SJared McNeill .drqtype_sdram = 22, 1001*16025c35SJared McNeill .DPC = 0x00, 1002*16025c35SJared McNeill .DAC_FIFOC = 0x04, 1003*16025c35SJared McNeill .DAC_FIFOS = 0x08, 1004*16025c35SJared McNeill .DAC_TXDATA = 0x0c, 1005*16025c35SJared McNeill .ADC_FIFOC = 0x1c, 1006*16025c35SJared McNeill .ADC_FIFOS = 0x20, 1007*16025c35SJared McNeill .ADC_RXDATA = 0x24, 1008*16025c35SJared McNeill .DAC_CNT = 0x30, 1009*16025c35SJared McNeill .ADC_CNT = 0x34, 1010*16025c35SJared McNeill }; 1011*16025c35SJared McNeill 1012*16025c35SJared McNeill static const struct a10codec_config h3_config = { 1013*16025c35SJared McNeill .mixer_class = &h3_mixer_class, 1014*16025c35SJared McNeill .mute = h3_mute, 1015*16025c35SJared McNeill .drqtype_codec = 15, 1016*16025c35SJared McNeill .drqtype_sdram = 1, 1017*16025c35SJared McNeill .DPC = 0x00, 1018*16025c35SJared McNeill .DAC_FIFOC = 0x04, 1019*16025c35SJared McNeill .DAC_FIFOS = 0x08, 1020*16025c35SJared McNeill .DAC_TXDATA = 0x20, 1021*16025c35SJared McNeill .ADC_FIFOC = 0x10, 1022*16025c35SJared McNeill .ADC_FIFOS = 0x14, 1023*16025c35SJared McNeill .ADC_RXDATA = 0x18, 1024*16025c35SJared McNeill .DAC_CNT = 0x40, 1025*16025c35SJared McNeill .ADC_CNT = 0x44, 1026*16025c35SJared McNeill }; 1027*16025c35SJared McNeill 1028356c50adSEmmanuel Vadot static struct ofw_compat_data compat_data[] = { 1029*16025c35SJared McNeill { "allwinner,sun4i-a10-codec", (uintptr_t)&a10_config }, 1030*16025c35SJared McNeill { "allwinner,sun7i-a20-codec", (uintptr_t)&a10_config }, 1031*16025c35SJared McNeill { "allwinner,sun8i-h3-codec", (uintptr_t)&h3_config }, 1032*16025c35SJared McNeill { NULL, 0 } 1033356c50adSEmmanuel Vadot }; 1034356c50adSEmmanuel Vadot 1035ba9b7163SAndrew Turner static int 1036ba9b7163SAndrew Turner a10codec_probe(device_t dev) 1037ba9b7163SAndrew Turner { 1038ba9b7163SAndrew Turner if (!ofw_bus_status_okay(dev)) 1039ba9b7163SAndrew Turner return (ENXIO); 1040ba9b7163SAndrew Turner 1041356c50adSEmmanuel Vadot if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 1042ba9b7163SAndrew Turner return (ENXIO); 1043ba9b7163SAndrew Turner 1044ba9b7163SAndrew Turner device_set_desc(dev, "Allwinner Audio Codec"); 1045ba9b7163SAndrew Turner return (BUS_PROBE_DEFAULT); 1046ba9b7163SAndrew Turner } 1047ba9b7163SAndrew Turner 1048ba9b7163SAndrew Turner static int 1049ba9b7163SAndrew Turner a10codec_attach(device_t dev) 1050ba9b7163SAndrew Turner { 1051ba9b7163SAndrew Turner struct a10codec_info *sc; 1052ba9b7163SAndrew Turner char status[SND_STATUSLEN]; 1053*16025c35SJared McNeill struct gpiobus_pin *pa_pin; 1054*16025c35SJared McNeill phandle_t node; 1055*16025c35SJared McNeill clk_t clk_bus, clk_codec; 1056*16025c35SJared McNeill hwreset_t rst; 1057ba9b7163SAndrew Turner uint32_t val; 1058ba9b7163SAndrew Turner int error; 1059ba9b7163SAndrew Turner 1060*16025c35SJared McNeill node = ofw_bus_get_node(dev); 1061*16025c35SJared McNeill 1062ba9b7163SAndrew Turner sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 1063*16025c35SJared McNeill sc->cfg = (void *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; 1064ba9b7163SAndrew Turner sc->dev = dev; 1065ba9b7163SAndrew Turner sc->lock = snd_mtxcreate(device_get_nameunit(dev), "a10codec softc"); 1066ba9b7163SAndrew Turner 1067ba9b7163SAndrew Turner if (bus_alloc_resources(dev, a10codec_spec, sc->res)) { 1068ba9b7163SAndrew Turner device_printf(dev, "cannot allocate resources for device\n"); 1069ba9b7163SAndrew Turner error = ENXIO; 1070ba9b7163SAndrew Turner goto fail; 1071ba9b7163SAndrew Turner } 1072ba9b7163SAndrew Turner 1073ba9b7163SAndrew Turner sc->dmasize = 131072; 1074ba9b7163SAndrew Turner error = bus_dma_tag_create( 1075ba9b7163SAndrew Turner bus_get_dma_tag(dev), 1076ba9b7163SAndrew Turner 4, sc->dmasize, /* alignment, boundary */ 1077ba9b7163SAndrew Turner BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1078ba9b7163SAndrew Turner BUS_SPACE_MAXADDR, /* highaddr */ 1079ba9b7163SAndrew Turner NULL, NULL, /* filter, filterarg */ 1080ba9b7163SAndrew Turner sc->dmasize, 1, /* maxsize, nsegs */ 1081ba9b7163SAndrew Turner sc->dmasize, 0, /* maxsegsize, flags */ 1082ba9b7163SAndrew Turner NULL, NULL, /* lockfunc, lockarg */ 1083ba9b7163SAndrew Turner &sc->dmat); 1084ba9b7163SAndrew Turner if (error != 0) { 1085ba9b7163SAndrew Turner device_printf(dev, "cannot create DMA tag\n"); 1086ba9b7163SAndrew Turner goto fail; 1087ba9b7163SAndrew Turner } 1088ba9b7163SAndrew Turner 10896a05f063SJared McNeill /* Get clocks */ 1090*16025c35SJared McNeill if (clk_get_by_ofw_name(dev, 0, "apb", &clk_bus) != 0 && 1091*16025c35SJared McNeill clk_get_by_ofw_name(dev, 0, "ahb", &clk_bus) != 0) { 1092*16025c35SJared McNeill device_printf(dev, "cannot find bus clock\n"); 10936a05f063SJared McNeill goto fail; 10946a05f063SJared McNeill } 1095*16025c35SJared McNeill if (clk_get_by_ofw_name(dev, 0, "codec", &clk_codec) != 0) { 10966a05f063SJared McNeill device_printf(dev, "cannot find codec clock\n"); 10976a05f063SJared McNeill goto fail; 10986a05f063SJared McNeill } 10996a05f063SJared McNeill 1100*16025c35SJared McNeill /* Gating bus clock for codec */ 1101*16025c35SJared McNeill if (clk_enable(clk_bus) != 0) { 1102*16025c35SJared McNeill device_printf(dev, "cannot enable bus clock\n"); 11036a05f063SJared McNeill goto fail; 11046a05f063SJared McNeill } 1105ba9b7163SAndrew Turner /* Activate audio codec clock. According to the A10 and A20 user 1106ba9b7163SAndrew Turner * manuals, Audio_pll can be either 24.576MHz or 22.5792MHz. Most 1107ba9b7163SAndrew Turner * audio sampling rates require an 24.576MHz input clock with the 1108ba9b7163SAndrew Turner * exception of 44.1kHz, 22.05kHz, and 11.025kHz. Unfortunately, 1109ba9b7163SAndrew Turner * both capture and playback use the same clock source so to 1110ba9b7163SAndrew Turner * safely support independent full duplex operation, we use a fixed 1111ba9b7163SAndrew Turner * 24.576MHz clock source and don't advertise native support for 1112ba9b7163SAndrew Turner * the three sampling rates that require a 22.5792MHz input. 1113ba9b7163SAndrew Turner */ 11146a05f063SJared McNeill error = clk_set_freq(clk_codec, 24576000, CLK_SET_ROUND_DOWN); 11156a05f063SJared McNeill if (error != 0) { 11166a05f063SJared McNeill device_printf(dev, "cannot set codec clock frequency\n"); 11176a05f063SJared McNeill goto fail; 11186a05f063SJared McNeill } 11196a05f063SJared McNeill /* Enable audio codec clock */ 11206a05f063SJared McNeill error = clk_enable(clk_codec); 11216a05f063SJared McNeill if (error != 0) { 11226a05f063SJared McNeill device_printf(dev, "cannot enable codec clock\n"); 11236a05f063SJared McNeill goto fail; 11246a05f063SJared McNeill } 1125ba9b7163SAndrew Turner 1126*16025c35SJared McNeill /* De-assert hwreset */ 1127*16025c35SJared McNeill if (hwreset_get_by_ofw_name(dev, 0, "apb", &rst) == 0 || 1128*16025c35SJared McNeill hwreset_get_by_ofw_name(dev, 0, "ahb", &rst) == 0) { 1129*16025c35SJared McNeill error = hwreset_deassert(rst); 1130*16025c35SJared McNeill if (error != 0) { 1131*16025c35SJared McNeill device_printf(dev, "cannot de-assert reset\n"); 1132*16025c35SJared McNeill goto fail; 1133*16025c35SJared McNeill } 1134*16025c35SJared McNeill } 1135*16025c35SJared McNeill 1136ba9b7163SAndrew Turner /* Enable DAC */ 1137*16025c35SJared McNeill val = CODEC_READ(sc, AC_DAC_DPC(sc)); 1138ba9b7163SAndrew Turner val |= DAC_DPC_EN_DA; 1139*16025c35SJared McNeill CODEC_WRITE(sc, AC_DAC_DPC(sc), val); 1140ba9b7163SAndrew Turner 1141ba9b7163SAndrew Turner #ifdef notdef 1142ba9b7163SAndrew Turner error = snd_setup_intr(dev, sc->irq, INTR_MPSAFE, a10codec_intr, sc, 1143ba9b7163SAndrew Turner &sc->ih); 1144ba9b7163SAndrew Turner if (error != 0) { 1145ba9b7163SAndrew Turner device_printf(dev, "could not setup interrupt handler\n"); 1146ba9b7163SAndrew Turner goto fail; 1147ba9b7163SAndrew Turner } 1148ba9b7163SAndrew Turner #endif 1149ba9b7163SAndrew Turner 1150*16025c35SJared McNeill if (mixer_init(dev, sc->cfg->mixer_class, sc)) { 1151ba9b7163SAndrew Turner device_printf(dev, "mixer_init failed\n"); 1152ba9b7163SAndrew Turner goto fail; 1153ba9b7163SAndrew Turner } 1154ba9b7163SAndrew Turner 1155*16025c35SJared McNeill /* Unmute PA */ 1156*16025c35SJared McNeill if (gpio_pin_get_by_ofw_property(dev, node, "allwinner,pa-gpios", 1157*16025c35SJared McNeill &pa_pin) == 0) { 1158*16025c35SJared McNeill error = gpio_pin_set_active(pa_pin, 1); 1159*16025c35SJared McNeill if (error != 0) 1160*16025c35SJared McNeill device_printf(dev, "failed to unmute PA\n"); 1161*16025c35SJared McNeill } 1162*16025c35SJared McNeill 1163ba9b7163SAndrew Turner pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); 1164ba9b7163SAndrew Turner 1165ba9b7163SAndrew Turner if (pcm_register(dev, sc, 1, 1)) { 1166ba9b7163SAndrew Turner device_printf(dev, "pcm_register failed\n"); 1167ba9b7163SAndrew Turner goto fail; 1168ba9b7163SAndrew Turner } 1169ba9b7163SAndrew Turner 1170ba9b7163SAndrew Turner pcm_addchan(dev, PCMDIR_PLAY, &a10codec_chan_class, sc); 1171ba9b7163SAndrew Turner pcm_addchan(dev, PCMDIR_REC, &a10codec_chan_class, sc); 1172ba9b7163SAndrew Turner 1173ba9b7163SAndrew Turner snprintf(status, SND_STATUSLEN, "at %s", ofw_bus_get_name(dev)); 1174ba9b7163SAndrew Turner pcm_setstatus(dev, status); 1175ba9b7163SAndrew Turner 1176ba9b7163SAndrew Turner return (0); 1177ba9b7163SAndrew Turner 1178ba9b7163SAndrew Turner fail: 1179ba9b7163SAndrew Turner bus_release_resources(dev, a10codec_spec, sc->res); 1180ba9b7163SAndrew Turner snd_mtxfree(sc->lock); 1181ba9b7163SAndrew Turner free(sc, M_DEVBUF); 1182ba9b7163SAndrew Turner 1183*16025c35SJared McNeill return (ENXIO); 1184ba9b7163SAndrew Turner } 1185ba9b7163SAndrew Turner 1186ba9b7163SAndrew Turner static device_method_t a10codec_pcm_methods[] = { 1187ba9b7163SAndrew Turner /* Device interface */ 1188ba9b7163SAndrew Turner DEVMETHOD(device_probe, a10codec_probe), 1189ba9b7163SAndrew Turner DEVMETHOD(device_attach, a10codec_attach), 1190ba9b7163SAndrew Turner 1191ba9b7163SAndrew Turner DEVMETHOD_END 1192ba9b7163SAndrew Turner }; 1193ba9b7163SAndrew Turner 1194ba9b7163SAndrew Turner static driver_t a10codec_pcm_driver = { 1195ba9b7163SAndrew Turner "pcm", 1196ba9b7163SAndrew Turner a10codec_pcm_methods, 1197ba9b7163SAndrew Turner PCM_SOFTC_SIZE, 1198ba9b7163SAndrew Turner }; 1199ba9b7163SAndrew Turner 1200ba9b7163SAndrew Turner DRIVER_MODULE(a10codec, simplebus, a10codec_pcm_driver, pcm_devclass, 0, 0); 1201ba9b7163SAndrew Turner MODULE_DEPEND(a10codec, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 1202ba9b7163SAndrew Turner MODULE_VERSION(a10codec, 1); 1203