Index: head/sys/mips/ingenic/jz4780_aic.c =================================================================== --- head/sys/mips/ingenic/jz4780_aic.c (revision 310775) +++ head/sys/mips/ingenic/jz4780_aic.c (revision 310776) @@ -1,754 +1,803 @@ /*- * Copyright (c) 2016 Ruslan Bukin * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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. */ /* Ingenic JZ4780 Audio Interface Controller (AIC). */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define AIC_NCHANNELS 1 struct aic_softc { device_t dev; struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; struct mtx *lock; int pos; bus_dma_tag_t dma_tag; bus_dmamap_t dma_map; bus_addr_t buf_base_phys; uint32_t *buf_base; uintptr_t aic_fifo_paddr; int dma_size; clk_t clk_aic; clk_t clk_i2s; struct aic_rate *sr; void *ih; struct xdma_channel *xchan; xdma_controller_t *xdma_tx; + int internal_codec; }; /* Channel registers */ struct sc_chinfo { struct snd_dbuf *buffer; struct pcm_channel *channel; struct sc_pcminfo *parent; /* Channel information */ uint32_t dir; uint32_t format; /* Flags */ uint32_t run; }; /* PCM device private data */ struct sc_pcminfo { device_t dev; uint32_t chnum; struct sc_chinfo chan[AIC_NCHANNELS]; struct aic_softc *sc; }; static struct resource_spec aic_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static int aic_probe(device_t dev); static int aic_attach(device_t dev); static int aic_detach(device_t dev); static int setup_xdma(struct sc_pcminfo *scp); struct aic_rate { uint32_t speed; }; static struct aic_rate rate_map[] = { - { 96000 }, + { 48000 }, /* TODO: add more frequences */ { 0 }, }; /* * Mixer interface. */ static int aicmixer_init(struct snd_mixer *m) { struct sc_pcminfo *scp; struct aic_softc *sc; int mask; scp = mix_getdevinfo(m); sc = scp->sc; if (sc == NULL) return -1; mask = SOUND_MASK_PCM; snd_mtxlock(sc->lock); pcm_setflags(scp->dev, pcm_getflags(scp->dev) | SD_F_SOFTPCMVOL); mix_setdevs(m, mask); snd_mtxunlock(sc->lock); return (0); } static int aicmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct sc_pcminfo *scp; scp = mix_getdevinfo(m); /* Here we can configure hardware volume on our DAC */ return (0); } static kobj_method_t aicmixer_methods[] = { KOBJMETHOD(mixer_init, aicmixer_init), KOBJMETHOD(mixer_set, aicmixer_set), KOBJMETHOD_END }; MIXER_DECLARE(aicmixer); /* * Channel interface. */ static void * aicchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct aic_softc *sc; scp = (struct sc_pcminfo *)devinfo; sc = scp->sc; snd_mtxlock(sc->lock); ch = &scp->chan[0]; ch->dir = dir; ch->run = 0; ch->buffer = b; ch->channel = c; ch->parent = scp; snd_mtxunlock(sc->lock); if (sndbuf_setup(ch->buffer, sc->buf_base, sc->dma_size) != 0) { device_printf(scp->dev, "Can't setup sndbuf.\n"); return NULL; } return (ch); } static int aicchan_free(kobj_t obj, void *data) { struct sc_chinfo *ch = data; struct sc_pcminfo *scp = ch->parent; struct aic_softc *sc = scp->sc; snd_mtxlock(sc->lock); /* TODO: free channel buffer */ snd_mtxunlock(sc->lock); return (0); } static int aicchan_setformat(kobj_t obj, void *data, uint32_t format) { struct sc_pcminfo *scp; struct sc_chinfo *ch; ch = data; scp = ch->parent; ch->format = format; return (0); } static uint32_t aicchan_setspeed(kobj_t obj, void *data, uint32_t speed) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct aic_rate *sr; struct aic_softc *sc; int threshold; int i; ch = data; scp = ch->parent; sc = scp->sc; sr = NULL; /* First look for equal frequency. */ for (i = 0; rate_map[i].speed != 0; i++) { if (rate_map[i].speed == speed) sr = &rate_map[i]; } /* If no match, just find nearest. */ if (sr == NULL) { for (i = 0; rate_map[i].speed != 0; i++) { sr = &rate_map[i]; threshold = sr->speed + ((rate_map[i + 1].speed != 0) ? ((rate_map[i + 1].speed - sr->speed) >> 1) : 0); if (speed < threshold) break; } } sc->sr = sr; /* Clocks can be reconfigured here. */ return (sr->speed); } static uint32_t aicchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct aic_softc *sc; ch = data; scp = ch->parent; sc = scp->sc; sndbuf_resize(ch->buffer, sc->dma_size / blocksize, blocksize); return (sndbuf_getblksz(ch->buffer)); } static int aic_intr(void *arg) { struct sc_pcminfo *scp; xdma_channel_t *xchan; struct sc_chinfo *ch; struct aic_softc *sc; xdma_config_t *conf; int bufsize; scp = arg; sc = scp->sc; ch = &scp->chan[0]; xchan = sc->xchan; conf = &xchan->conf; bufsize = sndbuf_getsize(ch->buffer); sc->pos += conf->block_len; if (sc->pos >= bufsize) sc->pos -= bufsize; if (ch->run) chn_intr(ch->channel); return (0); } static int setup_xdma(struct sc_pcminfo *scp) { struct aic_softc *sc; struct sc_chinfo *ch; int fmt; int err; ch = &scp->chan[0]; sc = scp->sc; fmt = sndbuf_getfmt(ch->buffer); KASSERT(fmt & AFMT_16BIT, ("16-bit audio supported only.")); err = xdma_prep_cyclic(sc->xchan, XDMA_MEM_TO_DEV, /* direction */ sc->buf_base_phys, /* src addr */ sc->aic_fifo_paddr, /* dst addr */ sndbuf_getblksz(ch->buffer), /* block len */ sndbuf_getblkcnt(ch->buffer), /* block num */ 2, /* src port width */ 2); /* dst port width */ if (err != 0) { device_printf(sc->dev, "Can't configure virtual channel\n"); return (-1); } xdma_begin(sc->xchan); return (0); } static int aic_start(struct sc_pcminfo *scp) { struct aic_softc *sc; int reg; sc = scp->sc; - sc->pos = 0; /* Ensure clock enabled. */ reg = READ4(sc, I2SCR); reg |= (I2SCR_ESCLK); WRITE4(sc, I2SCR, reg); setup_xdma(scp); reg = (AICCR_OSS_16 | AICCR_ISS_16); reg |= (AICCR_CHANNEL_2); reg |= (AICCR_TDMS); reg |= (AICCR_ERPL); WRITE4(sc, AICCR, reg); return (0); } static int aic_stop(struct sc_pcminfo *scp) { struct aic_softc *sc; int reg; sc = scp->sc; reg = READ4(sc, AICCR); reg &= ~(AICCR_TDMS | AICCR_ERPL); WRITE4(sc, AICCR, reg); xdma_terminate(sc->xchan); - sc->pos = 0; - - bzero(sc->buf_base, sc->dma_size); - return (0); } static int aicchan_trigger(kobj_t obj, void *data, int go) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct aic_softc *sc; ch = data; scp = ch->parent; sc = scp->sc; snd_mtxlock(sc->lock); switch (go) { case PCMTRIG_START: ch->run = 1; + sc->pos = 0; + aic_start(scp); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: ch->run = 0; aic_stop(scp); + sc->pos = 0; + + bzero(sc->buf_base, sc->dma_size); + break; } snd_mtxunlock(sc->lock); return (0); } static uint32_t aicchan_getptr(kobj_t obj, void *data) { struct sc_pcminfo *scp; struct sc_chinfo *ch; struct aic_softc *sc; ch = data; scp = ch->parent; sc = scp->sc; return (sc->pos); } static uint32_t aic_pfmt[] = { SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; -static struct pcmchan_caps aic_pcaps = {96000, 96000, aic_pfmt, 0}; +static struct pcmchan_caps aic_pcaps = {48000, 48000, aic_pfmt, 0}; static struct pcmchan_caps * aicchan_getcaps(kobj_t obj, void *data) { return (&aic_pcaps); } static kobj_method_t aicchan_methods[] = { KOBJMETHOD(channel_init, aicchan_init), KOBJMETHOD(channel_free, aicchan_free), KOBJMETHOD(channel_setformat, aicchan_setformat), KOBJMETHOD(channel_setspeed, aicchan_setspeed), KOBJMETHOD(channel_setblocksize, aicchan_setblocksize), KOBJMETHOD(channel_trigger, aicchan_trigger), KOBJMETHOD(channel_getptr, aicchan_getptr), KOBJMETHOD(channel_getcaps, aicchan_getcaps), KOBJMETHOD_END }; CHANNEL_DECLARE(aicchan); static void aic_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) { bus_addr_t *addr; if (err) return; addr = (bus_addr_t*)arg; *addr = segs[0].ds_addr; } static int aic_dma_setup(struct aic_softc *sc) { device_t dev; int err; dev = sc->dev; /* DMA buffer size. */ sc->dma_size = 131072; /* * Must use dma_size boundary as modulo feature required. * Modulo feature allows setup circular buffer. */ err = bus_dma_tag_create( bus_get_dma_tag(sc->dev), 4, sc->dma_size, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sc->dma_size, 1, /* maxsize, nsegments */ sc->dma_size, 0, /* maxsegsize, flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->dma_tag); if (err) { device_printf(dev, "cannot create bus dma tag\n"); return (-1); } err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->buf_base, BUS_DMA_WAITOK | BUS_DMA_COHERENT, &sc->dma_map); if (err) { device_printf(dev, "cannot allocate memory\n"); return (-1); } err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->buf_base, sc->dma_size, aic_dmamap_cb, &sc->buf_base_phys, BUS_DMA_WAITOK); if (err) { device_printf(dev, "cannot load DMA map\n"); return (-1); } bzero(sc->buf_base, sc->dma_size); return (0); } static int aic_configure_clocks(struct aic_softc *sc) { uint64_t aic_freq; uint64_t i2s_freq; device_t dev; int err; dev = sc->dev; err = clk_get_by_ofw_name(sc->dev, 0, "aic", &sc->clk_aic); if (err != 0) { device_printf(dev, "Can't find aic clock.\n"); return (-1); } err = clk_enable(sc->clk_aic); if (err != 0) { device_printf(dev, "Can't enable aic clock.\n"); return (-1); } err = clk_get_by_ofw_name(sc->dev, 0, "i2s", &sc->clk_i2s); if (err != 0) { device_printf(dev, "Can't find i2s clock.\n"); return (-1); } err = clk_enable(sc->clk_i2s); if (err != 0) { device_printf(dev, "Can't enable i2s clock.\n"); return (-1); } err = clk_set_freq(sc->clk_i2s, 12000000, 0); if (err != 0) { device_printf(dev, "Can't set i2s frequency.\n"); return (-1); } clk_get_freq(sc->clk_aic, &aic_freq); clk_get_freq(sc->clk_i2s, &i2s_freq); device_printf(dev, "Frequency aic %d i2s %d\n", (uint32_t)aic_freq, (uint32_t)i2s_freq); return (0); } static int aic_configure(struct aic_softc *sc) { - int internal_codec; int reg; - internal_codec = 1; - WRITE4(sc, AICFR, AICFR_RST); /* Configure AIC */ reg = 0; - if (internal_codec) { + if (sc->internal_codec) { reg |= (AICFR_ICDC); } else { reg |= (AICFR_SYNCD | AICFR_BCKD); } reg |= (AICFR_AUSEL); /* I2S/MSB-justified format. */ reg |= (AICFR_TFTH(8)); /* Transmit FIFO threshold */ reg |= (AICFR_RFTH(7)); /* Receive FIFO threshold */ WRITE4(sc, AICFR, reg); reg = READ4(sc, AICFR); reg |= (AICFR_ENB); /* Enable the controller. */ WRITE4(sc, AICFR, reg); return (0); } static int +sysctl_hw_pcm_internal_codec(SYSCTL_HANDLER_ARGS) +{ + struct sc_pcminfo *scp; + struct sc_chinfo *ch; + struct aic_softc *sc; + int error, val; + + if (arg1 == NULL) + return (EINVAL); + + scp = arg1; + sc = scp->sc; + ch = &scp->chan[0]; + + snd_mtxlock(sc->lock); + + val = sc->internal_codec; + error = sysctl_handle_int(oidp, &val, 0, req); + if (error || req->newptr == NULL) { + snd_mtxunlock(sc->lock); + return (error); + } + if (val < 0 || val > 1) { + snd_mtxunlock(sc->lock); + return (EINVAL); + } + + if (sc->internal_codec != val) { + sc->internal_codec = val; + if (ch->run) + aic_stop(scp); + aic_configure(sc); + if (ch->run) + aic_start(scp); + } + + snd_mtxunlock(sc->lock); + + return (0); +} + +static int aic_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-i2s")) return (ENXIO); device_set_desc(dev, "Audio Interface Controller"); return (BUS_PROBE_DEFAULT); } static int aic_attach(device_t dev) { char status[SND_STATUSLEN]; struct sc_pcminfo *scp; struct aic_softc *sc; int err; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); sc->dev = dev; sc->pos = 0; + sc->internal_codec = 1; /* Get xDMA controller */ sc->xdma_tx = xdma_ofw_get(sc->dev, "tx"); if (sc->xdma_tx == NULL) { device_printf(dev, "Can't find DMA controller.\n"); return (ENXIO); } /* Alloc xDMA virtual channel. */ sc->xchan = xdma_channel_alloc(sc->xdma_tx); if (sc->xchan == NULL) { device_printf(dev, "Can't alloc virtual DMA channel.\n"); return (ENXIO); } /* Setup sound subsystem */ sc->lock = snd_mtxcreate(device_get_nameunit(dev), "aic softc"); if (sc->lock == NULL) { device_printf(dev, "Can't create mtx.\n"); return (ENXIO); } if (bus_alloc_resources(dev, aic_spec, sc->res)) { device_printf(dev, "could not allocate resources for device\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); sc->aic_fifo_paddr = rman_get_start(sc->res[0]) + AICDR; /* Setup PCM. */ scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO); scp->sc = sc; scp->dev = dev; /* Setup audio buffer. */ err = aic_dma_setup(sc); if (err != 0) { device_printf(dev, "Can't setup sound buffer.\n"); return (ENXIO); } /* Setup clocks. */ err = aic_configure_clocks(sc); if (err != 0) { device_printf(dev, "Can't configure clocks.\n"); return (ENXIO); } err = aic_configure(sc); if (err != 0) { device_printf(dev, "Can't configure AIC.\n"); return (ENXIO); } pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); /* Setup interrupt handler. */ err = xdma_setup_intr(sc->xchan, aic_intr, scp, &sc->ih); if (err) { device_printf(sc->dev, "Can't setup xDMA interrupt handler.\n"); return (ENXIO); } err = pcm_register(dev, scp, 1, 0); if (err) { device_printf(dev, "Can't register pcm.\n"); return (ENXIO); } scp->chnum = 0; pcm_addchan(dev, PCMDIR_PLAY, &aicchan_class, scp); scp->chnum++; snprintf(status, SND_STATUSLEN, "at %s", ofw_bus_get_name(dev)); pcm_setstatus(dev, status); mixer_init(dev, &aicmixer_class, scp); + + /* Create device sysctl node. */ + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "internal_codec", CTLTYPE_INT | CTLFLAG_RW, + scp, 0, sysctl_hw_pcm_internal_codec, "I", + "use internal audio codec"); return (0); } static int aic_detach(device_t dev) { struct aic_softc *sc; sc = device_get_softc(dev); xdma_channel_free(sc->xchan); bus_release_resources(dev, aic_spec, sc->res); return (0); } static device_method_t aic_pcm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aic_probe), DEVMETHOD(device_attach, aic_attach), DEVMETHOD(device_detach, aic_detach), DEVMETHOD_END }; static driver_t aic_pcm_driver = { "pcm", aic_pcm_methods, PCM_SOFTC_SIZE, }; DRIVER_MODULE(aic, simplebus, aic_pcm_driver, pcm_devclass, 0, 0); MODULE_DEPEND(aic, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(aic, 1); Index: head/sys/mips/ingenic/jz4780_codec.c =================================================================== --- head/sys/mips/ingenic/jz4780_codec.c (revision 310775) +++ head/sys/mips/ingenic/jz4780_codec.c (revision 310776) @@ -1,293 +1,310 @@ /*- * Copyright (c) 2016 Ruslan Bukin * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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. */ /* Ingenic JZ4780 CODEC. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include + #include #include #define CI20_HP_PIN 13 #define CI20_HP_PORT 3 struct codec_softc { device_t dev; struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; + clk_t clk; }; static struct resource_spec codec_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static int codec_probe(device_t dev); static int codec_attach(device_t dev); static int codec_detach(device_t dev); void codec_print_registers(struct codec_softc *sc); static int codec_write(struct codec_softc *sc, uint32_t reg, uint32_t val) { uint32_t tmp; + clk_enable(sc->clk); + tmp = (reg << RGADW_RGADDR_S); tmp |= (val << RGADW_RGDIN_S); tmp |= RGADW_RGWR; WRITE4(sc, CODEC_RGADW, tmp); while(READ4(sc, CODEC_RGADW) & RGADW_RGWR) ; + clk_disable(sc->clk); + return (0); } static int codec_read(struct codec_softc *sc, uint32_t reg) { uint32_t tmp; + clk_enable(sc->clk); + tmp = (reg << RGADW_RGADDR_S); WRITE4(sc, CODEC_RGADW, tmp); tmp = READ4(sc, CODEC_RGDATA); + clk_disable(sc->clk); + return (tmp); } void codec_print_registers(struct codec_softc *sc) { printf("codec SR %x\n", codec_read(sc, SR)); printf("codec SR2 %x\n", codec_read(sc, SR2)); printf("codec MR %x\n", codec_read(sc, MR)); printf("codec AICR_DAC %x\n", codec_read(sc, AICR_DAC)); printf("codec AICR_ADC %x\n", codec_read(sc, AICR_ADC)); printf("codec CR_LO %x\n", codec_read(sc, CR_LO)); printf("codec CR_HP %x\n", codec_read(sc, CR_HP)); printf("codec CR_DMIC %x\n", codec_read(sc, CR_DMIC)); printf("codec CR_MIC1 %x\n", codec_read(sc, CR_MIC1)); printf("codec CR_MIC2 %x\n", codec_read(sc, CR_MIC2)); printf("codec CR_LI1 %x\n", codec_read(sc, CR_LI1)); printf("codec CR_LI2 %x\n", codec_read(sc, CR_LI2)); printf("codec CR_DAC %x\n", codec_read(sc, CR_DAC)); printf("codec CR_ADC %x\n", codec_read(sc, CR_ADC)); printf("codec CR_MIX %x\n", codec_read(sc, CR_MIX)); printf("codec DR_MIX %x\n", codec_read(sc, DR_MIX)); printf("codec CR_VIC %x\n", codec_read(sc, CR_VIC)); printf("codec CR_CK %x\n", codec_read(sc, CR_CK)); printf("codec FCR_DAC %x\n", codec_read(sc, FCR_DAC)); printf("codec FCR_ADC %x\n", codec_read(sc, FCR_ADC)); printf("codec CR_TIMER_MSB %x\n", codec_read(sc, CR_TIMER_MSB)); printf("codec CR_TIMER_LSB %x\n", codec_read(sc, CR_TIMER_LSB)); printf("codec ICR %x\n", codec_read(sc, ICR)); printf("codec IMR %x\n", codec_read(sc, IMR)); printf("codec IFR %x\n", codec_read(sc, IFR)); printf("codec IMR2 %x\n", codec_read(sc, IMR2)); printf("codec IFR2 %x\n", codec_read(sc, IFR2)); printf("codec GCR_HPL %x\n", codec_read(sc, GCR_HPL)); printf("codec GCR_HPR %x\n", codec_read(sc, GCR_HPR)); printf("codec GCR_LIBYL %x\n", codec_read(sc, GCR_LIBYL)); printf("codec GCR_LIBYR %x\n", codec_read(sc, GCR_LIBYR)); printf("codec GCR_DACL %x\n", codec_read(sc, GCR_DACL)); printf("codec GCR_DACR %x\n", codec_read(sc, GCR_DACR)); printf("codec GCR_MIC1 %x\n", codec_read(sc, GCR_MIC1)); printf("codec GCR_MIC2 %x\n", codec_read(sc, GCR_MIC2)); printf("codec GCR_ADCL %x\n", codec_read(sc, GCR_ADCL)); printf("codec GCR_ADCR %x\n", codec_read(sc, GCR_ADCR)); printf("codec GCR_MIXDACL %x\n", codec_read(sc, GCR_MIXDACL)); printf("codec GCR_MIXDACR %x\n", codec_read(sc, GCR_MIXDACR)); printf("codec GCR_MIXADCL %x\n", codec_read(sc, GCR_MIXADCL)); printf("codec GCR_MIXADCR %x\n", codec_read(sc, GCR_MIXADCR)); printf("codec CR_ADC_AGC %x\n", codec_read(sc, CR_ADC_AGC)); printf("codec DR_ADC_AGC %x\n", codec_read(sc, DR_ADC_AGC)); } /* * CI20 board-specific */ static int ci20_hp_unmute(struct codec_softc *sc) { device_t dev; int port; int err; int pin; pin = CI20_HP_PIN; port = CI20_HP_PORT; dev = devclass_get_device(devclass_find("gpio"), port); if (dev == NULL) return (0); err = GPIO_PIN_SETFLAGS(dev, pin, GPIO_PIN_OUTPUT); if (err != 0) { device_printf(dev, "Cannot configure GPIO pin %d on %s\n", pin, device_get_nameunit(dev)); return (err); } err = GPIO_PIN_SET(dev, pin, 0); if (err != 0) { device_printf(dev, "Cannot configure GPIO pin %d on %s\n", pin, device_get_nameunit(dev)); return (err); } return (0); } static int codec_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-codec")) return (ENXIO); device_set_desc(dev, "Ingenic JZ4780 CODEC"); return (BUS_PROBE_DEFAULT); } static int codec_attach(device_t dev) { struct codec_softc *sc; uint8_t reg; sc = device_get_softc(dev); sc->dev = dev; if (bus_alloc_resources(dev, codec_spec, sc->res)) { device_printf(dev, "could not allocate resources for device\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); + if (clk_get_by_ofw_name(dev, 0, "i2s", &sc->clk) != 0) { + device_printf(dev, "could not get i2s clock\n"); + bus_release_resources(dev, codec_spec, sc->res); + return (ENXIO); + } + /* Initialize codec. */ reg = codec_read(sc, CR_VIC); reg &= ~(VIC_SB_SLEEP | VIC_SB); codec_write(sc, CR_VIC, reg); DELAY(20000); reg = codec_read(sc, CR_DAC); reg &= ~(DAC_SB | DAC_MUTE); codec_write(sc, CR_DAC, reg); DELAY(10000); - /* I2S, 16-bit, 96 kHz. */ + /* I2S, 16-bit, 48 kHz. */ reg = codec_read(sc, AICR_DAC); reg &= ~(AICR_DAC_SB | DAC_ADWL_M); reg |= DAC_ADWL_16; reg &= ~(AUDIOIF_M); reg |= AUDIOIF_I2S; codec_write(sc, AICR_DAC, reg); DELAY(10000); - reg = FCR_DAC_96; + reg = FCR_DAC_48; codec_write(sc, FCR_DAC, reg); DELAY(10000); /* Unmute headphones. */ reg = codec_read(sc, CR_HP); reg &= ~(HP_SB | HP_MUTE); codec_write(sc, CR_HP, reg); ci20_hp_unmute(sc); return (0); } static int codec_detach(device_t dev) { struct codec_softc *sc; sc = device_get_softc(dev); bus_release_resources(dev, codec_spec, sc->res); return (0); } static device_method_t codec_methods[] = { /* Device interface */ DEVMETHOD(device_probe, codec_probe), DEVMETHOD(device_attach, codec_attach), DEVMETHOD(device_detach, codec_detach), DEVMETHOD_END }; static driver_t codec_driver = { "codec", codec_methods, sizeof(struct codec_softc), }; static devclass_t codec_devclass; DRIVER_MODULE(codec, simplebus, codec_driver, codec_devclass, 0, 0); Index: head/sys/mips/ingenic/jz4780_codec.h =================================================================== --- head/sys/mips/ingenic/jz4780_codec.h (revision 310775) +++ head/sys/mips/ingenic/jz4780_codec.h (revision 310776) @@ -1,101 +1,102 @@ /*- * Copyright (c) 2016 Ruslan Bukin * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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$ */ #define CODEC_RGADW 0x00 /* Address, data in and write command */ #define RGADW_ICRST (1 << 31) /* Reset internal CODEC */ #define RGADW_RGWR (1 << 16) /* Issue a write command to CODEC */ #define RGADW_RGADDR_S 8 /* CODEC register's address. */ #define RGADW_RGADDR_M (0x7f << RGADW_RGADDR_S) #define RGADW_RGDIN_S 0 /* CODEC register data to write */ #define RGADW_RGDIN_M (0xff << RGADW_RGDIN_S) #define CODEC_RGDATA 0x04 /* The data read out */ #define SR 0x00 /* Status Register */ #define SR2 0x01 /* Status Register 2 */ #define MR 0x07 /* Mode status register */ #define AICR_DAC 0x08 /* DAC Audio Interface Control Register */ #define DAC_ADWL_S 6 /* Audio Data Word Length for DAC path. */ #define DAC_ADWL_M (0x3 << DAC_ADWL_S) #define DAC_ADWL_16 (0 << DAC_ADWL_S) #define AICR_DAC_SB (1 << 4) /* DAC audio interface in power-down mode */ #define AUDIOIF_S 0 #define AUDIOIF_M (0x3 << AUDIOIF_S) #define AUDIOIF_I2S 0x3 /* I2S interface */ #define AUDIOIF_DSP 0x2 /* DSP interface */ #define AUDIOIF_LJ 0x1 /* Left-justified interface */ #define AUDIOIF_P 0x0 /* Parallel interface */ #define AICR_ADC 0x09 /* ADC Audio Interface Control Register */ #define CR_LO 0x0B /* Differential line-out Control Register */ #define CR_HP 0x0D /* HeadPhone Control Register */ #define HP_MUTE (1 << 7) /* no signal on headphone outputs */ #define HP_SB (1 << 4) /* power-down */ #define CR_DMIC 0x10 /* Digital Microphone register */ #define CR_MIC1 0x11 /* Microphone1 Control register */ #define CR_MIC2 0x12 /* Microphone2 Control register */ #define CR_LI1 0x13 /* Control Register for line1 inputs */ #define CR_LI2 0x14 /* Control Register for line2 inputs */ #define CR_DAC 0x17 /* DAC Control Register */ #define DAC_MUTE (1 << 7) /* puts the DAC in soft mute mode */ #define DAC_SB (1 << 4) /* power-down */ #define CR_ADC 0x18 /* ADC Control Register */ #define CR_MIX 0x19 /* Digital Mixer Control Register */ #define DR_MIX 0x1A /* Digital Mixer Data Register */ #define CR_VIC 0x1B /* Control Register for the ViC */ #define VIC_SB_SLEEP (1 << 1) /* sleep mode */ #define VIC_SB (1 << 0) /* complete power-down */ #define CR_CK 0x1C /* Clock Control Register */ #define FCR_DAC 0x1D /* DAC Frequency Control Register */ +#define FCR_DAC_48 8 /* 48 kHz. */ #define FCR_DAC_96 10 /* 96 kHz. */ #define FCR_ADC 0x20 /* ADC Frequency Control Register */ #define CR_TIMER_MSB 0x21 /* MSB of programmable counter */ #define CR_TIMER_LSB 0x22 /* LSB of programmable counter */ #define ICR 0x23 /* Interrupt Control Register */ #define IMR 0x24 /* Interrupt Mask Register */ #define IFR 0x25 /* Interrupt Flag Register */ #define IMR2 0x26 /* Interrupt Mask Register 2 */ #define IFR2 0x27 /* Interrupt Flag Register 2 */ #define GCR_HPL 0x28 /* Left channel headphone Control Gain Register */ #define GCR_HPR 0x29 /* Right channel headphone Control Gain Register */ #define GCR_LIBYL 0x2A /* Left channel bypass line Control Gain Register */ #define GCR_LIBYR 0x2B /* Right channel bypass line Control Gain Register */ #define GCR_DACL 0x2C /* Left channel DAC Gain Control Register */ #define GCR_DACR 0x2D /* Right channel DAC Gain Control Register */ #define GCR_MIC1 0x2E /* Microphone 1 Gain Control Register */ #define GCR_MIC2 0x2F /* Microphone 2 Gain Control Register */ #define GCR_ADCL 0x30 /* Left ADC Gain Control Register */ #define GCR_ADCR 0x31 /* Right ADC Gain Control Register */ #define GCR_MIXDACL 0x34 /* DAC Digital Mixer Control Register */ #define GCR_MIXDACR 0x35 /* DAC Digital Mixer Control Register */ #define GCR_MIXADCL 0x36 /* ADC Digital Mixer Control Register */ #define GCR_MIXADCR 0x37 /* ADC Digital Mixer Control Register */ #define CR_ADC_AGC 0x3A /* Automatic Gain Control Register */ #define DR_ADC_AGC 0x3B /* Automatic Gain Control Data Register */