The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/allwinner/a33_codec.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
    5  * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  * $FreeBSD$
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/bus.h>
   37 #include <sys/kernel.h>
   38 #include <sys/lock.h>
   39 #include <sys/module.h>
   40 #include <sys/mutex.h>
   41 #include <sys/rman.h>
   42 #include <sys/resource.h>
   43 #include <machine/bus.h>
   44 #include <sys/gpio.h>
   45 
   46 #include <dev/ofw/ofw_bus.h>
   47 #include <dev/ofw/ofw_bus_subr.h>
   48 
   49 #include <dev/extres/clk/clk.h>
   50 #include <dev/extres/hwreset/hwreset.h>
   51 
   52 #include <dev/gpio/gpiobusvar.h>
   53 
   54 #include "opt_snd.h"
   55 #include <dev/sound/pcm/sound.h>
   56 #include <dev/sound/fdt/audio_dai.h>
   57 #include "audio_dai_if.h"
   58 
   59 #define SYSCLK_CTL              0x00c
   60 #define  AIF1CLK_ENA                    (1 << 11)
   61 #define  AIF1CLK_SRC_MASK               (3 << 8)
   62 #define  AIF1CLK_SRC_PLL                (2 << 8)
   63 #define  SYSCLK_ENA                     (1 << 3)
   64 #define  SYSCLK_SRC                     (1 << 0)
   65 
   66 #define MOD_CLK_ENA             0x010
   67 #define MOD_RST_CTL             0x014
   68 #define MOD_AIF1                        (1 << 15)
   69 #define MOD_ADC                         (1 << 3)
   70 #define MOD_DAC                         (1 << 2)
   71 
   72 #define SYS_SR_CTRL             0x018
   73 #define  AIF1_FS_MASK           (0xf << 12)
   74 #define   AIF_FS_48KHZ          (8 << 12)
   75 
   76 #define AIF1CLK_CTRL            0x040
   77 #define  AIF1_MSTR_MOD          (1 << 15)
   78 #define  AIF1_BCLK_INV          (1 << 14)
   79 #define  AIF1_LRCK_INV          (1 << 13)
   80 #define  AIF1_BCLK_DIV_MASK     (0xf << 9)
   81 #define   AIF1_BCLK_DIV_16      (6 << 9)
   82 #define  AIF1_LRCK_DIV_MASK     (7 << 6)
   83 #define   AIF1_LRCK_DIV_16      (0 << 6)
   84 #define   AIF1_LRCK_DIV_64      (2 << 6)
   85 #define  AIF1_WORD_SIZ_MASK     (3 << 4)
   86 #define   AIF1_WORD_SIZ_16      (1 << 4)
   87 #define  AIF1_DATA_FMT_MASK     (3 << 2)
   88 #define   AIF1_DATA_FMT_I2S     (0 << 2)
   89 #define   AIF1_DATA_FMT_LJ      (1 << 2)
   90 #define   AIF1_DATA_FMT_RJ      (2 << 2)
   91 #define   AIF1_DATA_FMT_DSP     (3 << 2)
   92 
   93 #define AIF1_ADCDAT_CTRL        0x044
   94 #define         AIF1_ADC0L_ENA          (1 << 15)
   95 #define         AIF1_ADC0R_ENA          (1 << 14)
   96 
   97 #define AIF1_DACDAT_CTRL        0x048
   98 #define  AIF1_DAC0L_ENA         (1 << 15)
   99 #define  AIF1_DAC0R_ENA         (1 << 14)
  100 
  101 #define AIF1_MXR_SRC            0x04c
  102 #define         AIF1L_MXR_SRC_MASK      (0xf << 12)
  103 #define         AIF1L_MXR_SRC_AIF1      (0x8 << 12)
  104 #define         AIF1L_MXR_SRC_ADC       (0x2 << 12)
  105 #define         AIF1R_MXR_SRC_MASK      (0xf << 8)
  106 #define         AIF1R_MXR_SRC_AIF1      (0x8 << 8)
  107 #define         AIF1R_MXR_SRC_ADC       (0x2 << 8)
  108 
  109 #define ADC_DIG_CTRL            0x100
  110 #define  ADC_DIG_CTRL_ENAD      (1 << 15)
  111 
  112 #define HMIC_CTRL1              0x110
  113 #define  HMIC_CTRL1_N_MASK              (0xf << 8)
  114 #define  HMIC_CTRL1_N(n)                (((n) & 0xf) << 8)
  115 #define  HMIC_CTRL1_JACK_IN_IRQ_EN      (1 << 4)
  116 #define  HMIC_CTRL1_JACK_OUT_IRQ_EN     (1 << 3)
  117 #define  HMIC_CTRL1_MIC_DET_IRQ_EN      (1 << 0)
  118 
  119 #define HMIC_CTRL2              0x114
  120 #define  HMIC_CTRL2_MDATA_THRES __BITS(12,8)
  121 
  122 #define HMIC_STS                0x118
  123 #define  HMIC_STS_MIC_PRESENT   (1 << 6)
  124 #define  HMIC_STS_JACK_DET_OIRQ (1 << 4)
  125 #define  HMIC_STS_JACK_DET_IIRQ (1 << 3)
  126 #define  HMIC_STS_MIC_DET_ST    (1 << 0)
  127 
  128 #define DAC_DIG_CTRL            0x120
  129 #define  DAC_DIG_CTRL_ENDA      (1 << 15)
  130 
  131 #define DAC_MXR_SRC             0x130
  132 #define  DACL_MXR_SRC_MASK              (0xf << 12)
  133 #define   DACL_MXR_SRC_AIF1_DAC0L (0x8 << 12)
  134 #define  DACR_MXR_SRC_MASK              (0xf << 8)
  135 #define   DACR_MXR_SRC_AIF1_DAC0R (0x8 << 8)
  136 
  137 static struct ofw_compat_data compat_data[] = {
  138         { "allwinner,sun8i-a33-codec",  1},
  139         { NULL,                         0 }
  140 };
  141 
  142 static struct resource_spec sun8i_codec_spec[] = {
  143         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
  144         { SYS_RES_IRQ,          0,      RF_ACTIVE | RF_SHAREABLE },
  145         { -1, 0 }
  146 };
  147 
  148 struct sun8i_codec_softc {
  149         device_t        dev;
  150         struct resource *res[2];
  151         struct mtx      mtx;
  152         clk_t           clk_gate;
  153         clk_t           clk_mod;
  154         void *          intrhand;
  155 };
  156 
  157 #define CODEC_LOCK(sc)                  mtx_lock(&(sc)->mtx)
  158 #define CODEC_UNLOCK(sc)                mtx_unlock(&(sc)->mtx)
  159 #define CODEC_READ(sc, reg)             bus_read_4((sc)->res[0], (reg))
  160 #define CODEC_WRITE(sc, reg, val)       bus_write_4((sc)->res[0], (reg), (val))
  161 
  162 static int sun8i_codec_probe(device_t dev);
  163 static int sun8i_codec_attach(device_t dev);
  164 static int sun8i_codec_detach(device_t dev);
  165 
  166 static int
  167 sun8i_codec_probe(device_t dev)
  168 {
  169         if (!ofw_bus_status_okay(dev))
  170                 return (ENXIO);
  171 
  172         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
  173                 return (ENXIO);
  174 
  175         device_set_desc(dev, "Allwinner Codec");
  176         return (BUS_PROBE_DEFAULT);
  177 }
  178 
  179 static int
  180 sun8i_codec_attach(device_t dev)
  181 {
  182         struct sun8i_codec_softc *sc;
  183         int error;
  184         uint32_t val;
  185         struct gpiobus_pin *pa_pin;
  186         phandle_t node;
  187 
  188         sc = device_get_softc(dev);
  189         sc->dev = dev;
  190         node = ofw_bus_get_node(dev);
  191 
  192         mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
  193 
  194         if (bus_alloc_resources(dev, sun8i_codec_spec, sc->res) != 0) {
  195                 device_printf(dev, "cannot allocate resources for device\n");
  196                 error = ENXIO;
  197                 goto fail;
  198         }
  199 
  200         error = clk_get_by_ofw_name(dev, 0, "mod", &sc->clk_mod);
  201         if (error != 0) {
  202                 device_printf(dev, "cannot get \"mod\" clock\n");
  203                 goto fail;
  204         }
  205 
  206         error = clk_get_by_ofw_name(dev, 0, "bus", &sc->clk_gate);
  207         if (error != 0) {
  208                 device_printf(dev, "cannot get \"bus\" clock\n");
  209                 goto fail;
  210         }
  211 
  212         error = clk_enable(sc->clk_gate);
  213         if (error != 0) {
  214                 device_printf(dev, "cannot enable \"bus\" clock\n");
  215                 goto fail;
  216         }
  217 
  218         /* Enable clocks */
  219         val = CODEC_READ(sc, SYSCLK_CTL);
  220         val |= AIF1CLK_ENA;
  221         val &= ~AIF1CLK_SRC_MASK;
  222         val |= AIF1CLK_SRC_PLL;
  223         val |= SYSCLK_ENA;
  224         val &= ~SYSCLK_SRC;
  225         CODEC_WRITE(sc, SYSCLK_CTL, val);
  226         CODEC_WRITE(sc, MOD_CLK_ENA, MOD_AIF1 | MOD_ADC | MOD_DAC);
  227         CODEC_WRITE(sc, MOD_RST_CTL, MOD_AIF1 | MOD_ADC | MOD_DAC);
  228 
  229         /* Enable digital parts */
  230         CODEC_WRITE(sc, DAC_DIG_CTRL, DAC_DIG_CTRL_ENDA);
  231         CODEC_WRITE(sc, ADC_DIG_CTRL, ADC_DIG_CTRL_ENAD);
  232 
  233         /* Set AIF1 to 48 kHz */
  234         val = CODEC_READ(sc, SYS_SR_CTRL);
  235         val &= ~AIF1_FS_MASK;
  236         val |= AIF_FS_48KHZ;
  237         CODEC_WRITE(sc, SYS_SR_CTRL, val);
  238 
  239         /* Set AIF1 to 16-bit */
  240         val = CODEC_READ(sc, AIF1CLK_CTRL);
  241         val &= ~AIF1_WORD_SIZ_MASK;
  242         val |= AIF1_WORD_SIZ_16;
  243         CODEC_WRITE(sc, AIF1CLK_CTRL, val);
  244 
  245         /* Enable AIF1 DAC timelot 0 */
  246         val = CODEC_READ(sc, AIF1_DACDAT_CTRL);
  247         val |= AIF1_DAC0L_ENA;
  248         val |= AIF1_DAC0R_ENA;
  249         CODEC_WRITE(sc, AIF1_DACDAT_CTRL, val);
  250 
  251         /* Enable AIF1 ADC timelot 0 */
  252         val = CODEC_READ(sc, AIF1_ADCDAT_CTRL);
  253         val |= AIF1_ADC0L_ENA;
  254         val |= AIF1_ADC0R_ENA;
  255         CODEC_WRITE(sc, AIF1_ADCDAT_CTRL, val);
  256 
  257         /* DAC mixer source select */
  258         val = CODEC_READ(sc, DAC_MXR_SRC);
  259         val &= ~DACL_MXR_SRC_MASK;
  260         val |= DACL_MXR_SRC_AIF1_DAC0L;
  261         val &= ~DACR_MXR_SRC_MASK;
  262         val |= DACR_MXR_SRC_AIF1_DAC0R;
  263         CODEC_WRITE(sc, DAC_MXR_SRC, val);
  264 
  265         /* ADC mixer source select */
  266         val = CODEC_READ(sc, AIF1_MXR_SRC);
  267         val &= ~AIF1L_MXR_SRC_MASK;
  268         val |= AIF1L_MXR_SRC_ADC;
  269         val &= ~AIF1R_MXR_SRC_MASK;
  270         val |= AIF1R_MXR_SRC_ADC;
  271         CODEC_WRITE(sc, AIF1_MXR_SRC, val);
  272 
  273         /* Enable PA power */
  274         /* Unmute PA */
  275         if (gpio_pin_get_by_ofw_property(dev, node, "allwinner,pa-gpios",
  276             &pa_pin) == 0) {
  277                 error = gpio_pin_set_active(pa_pin, 1);
  278                 if (error != 0)
  279                         device_printf(dev, "failed to unmute PA\n");
  280         }
  281 
  282         OF_device_register_xref(OF_xref_from_node(node), dev);
  283 
  284         return (0);
  285 
  286 fail:
  287         sun8i_codec_detach(dev);
  288         return (error);
  289 }
  290 
  291 static int
  292 sun8i_codec_detach(device_t dev)
  293 {
  294         struct sun8i_codec_softc *sc;
  295 
  296         sc = device_get_softc(dev);
  297 
  298         if (sc->clk_gate)
  299                 clk_release(sc->clk_gate);
  300 
  301         if (sc->clk_mod)
  302                 clk_release(sc->clk_mod);
  303 
  304         if (sc->intrhand != NULL)
  305                 bus_teardown_intr(sc->dev, sc->res[1], sc->intrhand);
  306 
  307         bus_release_resources(dev, sun8i_codec_spec, sc->res);
  308         mtx_destroy(&sc->mtx);
  309 
  310         return (0);
  311 }
  312 
  313 static int
  314 sun8i_codec_dai_init(device_t dev, uint32_t format)
  315 {
  316         struct sun8i_codec_softc *sc;
  317         int fmt, pol, clk;
  318         uint32_t val;
  319 
  320         sc = device_get_softc(dev);
  321 
  322         fmt = AUDIO_DAI_FORMAT_FORMAT(format);
  323         pol = AUDIO_DAI_FORMAT_POLARITY(format);
  324         clk = AUDIO_DAI_FORMAT_CLOCK(format);
  325 
  326         val = CODEC_READ(sc, AIF1CLK_CTRL);
  327 
  328         val &= ~AIF1_DATA_FMT_MASK;
  329         switch (fmt) {
  330         case AUDIO_DAI_FORMAT_I2S:
  331                 val |= AIF1_DATA_FMT_I2S;
  332                 break;
  333         case AUDIO_DAI_FORMAT_RJ:
  334                 val |= AIF1_DATA_FMT_RJ;
  335                 break;
  336         case AUDIO_DAI_FORMAT_LJ:
  337                 val |= AIF1_DATA_FMT_LJ;
  338                 break;
  339         case AUDIO_DAI_FORMAT_DSPA:
  340         case AUDIO_DAI_FORMAT_DSPB:
  341                 val |= AIF1_DATA_FMT_DSP;
  342                 break;
  343         default:
  344                 return EINVAL;
  345         }
  346 
  347         val &= ~(AIF1_BCLK_INV|AIF1_LRCK_INV);
  348         /* Codec LRCK polarity is inverted (datasheet is wrong) */
  349         if (!AUDIO_DAI_POLARITY_INVERTED_FRAME(pol))
  350                 val |= AIF1_LRCK_INV;
  351         if (AUDIO_DAI_POLARITY_INVERTED_BCLK(pol))
  352                 val |= AIF1_BCLK_INV;
  353 
  354         switch (clk) {
  355         case AUDIO_DAI_CLOCK_CBM_CFM:
  356                 val &= ~AIF1_MSTR_MOD;  /* codec is master */
  357                 break;
  358         case AUDIO_DAI_CLOCK_CBS_CFS:
  359                 val |= AIF1_MSTR_MOD;   /* codec is slave */
  360                 break;
  361         default:
  362                 return EINVAL;
  363         }
  364 
  365         val &= ~AIF1_LRCK_DIV_MASK;
  366         val |= AIF1_LRCK_DIV_64;
  367 
  368         val &= ~AIF1_BCLK_DIV_MASK;
  369         val |= AIF1_BCLK_DIV_16;
  370 
  371         CODEC_WRITE(sc, AIF1CLK_CTRL, val);
  372 
  373         return (0);
  374 }
  375 
  376 static int
  377 sun8i_codec_dai_trigger(device_t dev, int go, int pcm_dir)
  378 {
  379 
  380         return (0);
  381 }
  382 
  383 static int
  384 sun8i_codec_dai_setup_mixer(device_t dev, device_t pcmdev)
  385 {
  386 
  387         /* Do nothing for now */
  388         return (0);
  389 }
  390 
  391 
  392 static device_method_t sun8i_codec_methods[] = {
  393         /* Device interface */
  394         DEVMETHOD(device_probe,         sun8i_codec_probe),
  395         DEVMETHOD(device_attach,        sun8i_codec_attach),
  396         DEVMETHOD(device_detach,        sun8i_codec_detach),
  397 
  398         DEVMETHOD(audio_dai_init,       sun8i_codec_dai_init),
  399         DEVMETHOD(audio_dai_setup_mixer,        sun8i_codec_dai_setup_mixer),
  400         DEVMETHOD(audio_dai_trigger,    sun8i_codec_dai_trigger),
  401 
  402         DEVMETHOD_END
  403 };
  404 
  405 static driver_t sun8i_codec_driver = {
  406         "sun8icodec",
  407         sun8i_codec_methods,
  408         sizeof(struct sun8i_codec_softc),
  409 };
  410 
  411 DRIVER_MODULE(sun8i_codec, simplebus, sun8i_codec_driver, 0, 0);
  412 SIMPLEBUS_PNP_INFO(compat_data);

Cache object: 4288d61b8179c13e46e72985dd6ffca1


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.