Author: jmcneill
Date: Thu Dec 29 14:00:10 2016
New Revision: 310776
URL: https://svnweb.freebsd.org/changeset/base/310776

Log:
  The JZ4780 I2S can feed either the internal audio codec or the HDMI
  transmitter, but not both at the same time. This patch:
  
   - Adds a dev.pcm.0.internal_codec sysctl node for selecting between
     internal and external codec
   - Changes playback sample rate from 96 kHz to 48 kHz for HDMI compatibility
   - Enables i2s clock on codec access
  
  Reviewed by:          br
  Differential Revision:        https://reviews.freebsd.org/D8960

Modified:
  head/sys/mips/ingenic/jz4780_aic.c
  head/sys/mips/ingenic/jz4780_codec.c
  head/sys/mips/ingenic/jz4780_codec.h

Modified: head/sys/mips/ingenic/jz4780_aic.c
==============================================================================
--- head/sys/mips/ingenic/jz4780_aic.c  Thu Dec 29 13:27:04 2016        
(r310775)
+++ head/sys/mips/ingenic/jz4780_aic.c  Thu Dec 29 14:00:10 2016        
(r310776)
@@ -81,6 +81,7 @@ struct aic_softc {
        void                    *ih;
        struct xdma_channel     *xchan;
        xdma_controller_t       *xdma_tx;
+       int                     internal_codec;
 };
 
 /* Channel registers */
@@ -120,7 +121,7 @@ struct aic_rate {
 };
 
 static struct aic_rate rate_map[] = {
-       { 96000 },
+       { 48000 },
        /* TODO: add more frequences */
        { 0 },
 };
@@ -355,7 +356,6 @@ aic_start(struct sc_pcminfo *scp)
        int reg;
 
        sc = scp->sc;
-       sc->pos = 0;
 
        /* Ensure clock enabled. */
        reg = READ4(sc, I2SCR);
@@ -387,10 +387,6 @@ aic_stop(struct sc_pcminfo *scp)
 
        xdma_terminate(sc->xchan);
 
-       sc->pos = 0;
-
-       bzero(sc->buf_base, sc->dma_size);
-
        return (0);
 }
 
@@ -411,6 +407,8 @@ aicchan_trigger(kobj_t obj, void *data, 
        case PCMTRIG_START:
                ch->run = 1;
 
+               sc->pos = 0;
+
                aic_start(scp);
 
                break;
@@ -421,6 +419,10 @@ aicchan_trigger(kobj_t obj, void *data, 
 
                aic_stop(scp);
 
+               sc->pos = 0;
+
+               bzero(sc->buf_base, sc->dma_size);
+
                break;
        }
 
@@ -448,7 +450,7 @@ static uint32_t aic_pfmt[] = {
        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)
@@ -583,16 +585,13 @@ aic_configure_clocks(struct aic_softc *s
 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);
@@ -610,6 +609,48 @@ aic_configure(struct aic_softc *sc)
 }
 
 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)
 {
 
@@ -635,6 +676,7 @@ aic_attach(device_t dev)
        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");
@@ -718,6 +760,13 @@ aic_attach(device_t dev)
 
        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);
 }
 

Modified: head/sys/mips/ingenic/jz4780_codec.c
==============================================================================
--- head/sys/mips/ingenic/jz4780_codec.c        Thu Dec 29 13:27:04 2016        
(r310775)
+++ head/sys/mips/ingenic/jz4780_codec.c        Thu Dec 29 14:00:10 2016        
(r310776)
@@ -53,6 +53,8 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/gpio/gpiobusvar.h>
 
+#include <dev/extres/clk/clk.h>
+
 #include <mips/ingenic/jz4780_common.h>
 #include <mips/ingenic/jz4780_codec.h>
 
@@ -64,6 +66,7 @@ struct codec_softc {
        struct resource         *res[1];
        bus_space_tag_t         bst;
        bus_space_handle_t      bsh;
+       clk_t                   clk;
 };
 
 static struct resource_spec codec_spec[] = {
@@ -81,6 +84,8 @@ codec_write(struct codec_softc *sc, uint
 {
        uint32_t tmp;
 
+       clk_enable(sc->clk);
+
        tmp = (reg << RGADW_RGADDR_S);
        tmp |= (val << RGADW_RGDIN_S);
        tmp |= RGADW_RGWR;
@@ -90,6 +95,8 @@ codec_write(struct codec_softc *sc, uint
        while(READ4(sc, CODEC_RGADW) & RGADW_RGWR)
                ;
 
+       clk_disable(sc->clk);
+
        return (0);
 }
 
@@ -98,11 +105,15 @@ codec_read(struct codec_softc *sc, uint3
 {
        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);
 }
 
@@ -223,6 +234,12 @@ codec_attach(device_t dev)
        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);
@@ -236,7 +253,7 @@ codec_attach(device_t dev)
 
        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;
@@ -246,7 +263,7 @@ codec_attach(device_t dev)
 
        DELAY(10000);
 
-       reg = FCR_DAC_96;
+       reg = FCR_DAC_48;
        codec_write(sc, FCR_DAC, reg);
 
        DELAY(10000);

Modified: head/sys/mips/ingenic/jz4780_codec.h
==============================================================================
--- head/sys/mips/ingenic/jz4780_codec.h        Thu Dec 29 13:27:04 2016        
(r310775)
+++ head/sys/mips/ingenic/jz4780_codec.h        Thu Dec 29 14:00:10 2016        
(r310776)
@@ -74,6 +74,7 @@
 #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 */
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to