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/a64/sun50i_a64_acodec.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 
   45 #include <dev/ofw/ofw_bus.h>
   46 #include <dev/ofw/ofw_bus_subr.h>
   47 
   48 #include <dev/extres/clk/clk.h>
   49 #include <dev/extres/hwreset/hwreset.h>
   50 #include <dev/extres/regulator/regulator.h>
   51 
   52 #include "syscon_if.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 #include "mixer_if.h"
   59 
   60 #define A64_PR_CFG              0x00
   61 #define  A64_AC_PR_RST          (1 << 28)
   62 #define  A64_AC_PR_RW           (1 << 24)
   63 #define  A64_AC_PR_ADDR_MASK    (0x1f << 16)
   64 #define  A64_AC_PR_ADDR(n)      (((n) & 0x1f) << 16)
   65 #define  A64_ACDA_PR_WDAT_MASK  (0xff << 8)
   66 #define  A64_ACDA_PR_WDAT(n)    (((n) & 0xff) << 8)
   67 #define  A64_ACDA_PR_RDAT(n)    ((n) & 0xff)
   68 
   69 #define A64_HP_CTRL             0x00
   70 #define  A64_HPPA_EN            (1 << 6)
   71 #define  A64_HPVOL_MASK         0x3f
   72 #define  A64_HPVOL(n)           ((n) & 0x3f)
   73 #define A64_OL_MIX_CTRL         0x01
   74 #define  A64_LMIXMUTE_LDAC      (1 << 1)
   75 #define A64_OR_MIX_CTRL         0x02
   76 #define  A64_RMIXMUTE_RDAC      (1 << 1)
   77 #define A64_LINEOUT_CTRL0       0x05
   78 #define  A64_LINEOUT_LEFT_EN    (1 << 7)
   79 #define  A64_LINEOUT_RIGHT_EN   (1 << 6)
   80 #define  A64_LINEOUT_EN         (A64_LINEOUT_LEFT_EN|A64_LINEOUT_RIGHT_EN)
   81 #define A64_LINEOUT_CTRL1       0x06
   82 #define  A64_LINEOUT_VOL        __BITS(4,0)
   83 #define A64_MIC1_CTRL           0x07
   84 #define  A64_MIC1G              __BITS(6,4)
   85 #define  A64_MIC1AMPEN          (1 << 3)
   86 #define  A64_MIC1BOOST          __BITS(2,0)
   87 #define A64_MIC2_CTRL           0x08
   88 #define  A64_MIC2_SEL           (1 << 7)
   89 #define  A64_MIC2G_MASK         (7 << 4)
   90 #define  A64_MIC2G(n)           (((n) & 7) << 4)
   91 #define  A64_MIC2AMPEN          (1 << 3)
   92 #define  A64_MIC2BOOST_MASK     (7 << 0)
   93 #define  A64_MIC2BOOST(n)       (((n) & 7) << 0)
   94 #define A64_LINEIN_CTRL         0x09
   95 #define  A64_LINEING            __BITS(6,4)
   96 #define A64_MIX_DAC_CTRL        0x0a
   97 #define  A64_DACAREN            (1 << 7)
   98 #define  A64_DACALEN            (1 << 6)
   99 #define  A64_RMIXEN             (1 << 5)
  100 #define  A64_LMIXEN             (1 << 4)
  101 #define  A64_RHPPAMUTE          (1 << 3)
  102 #define  A64_LHPPAMUTE          (1 << 2)
  103 #define  A64_RHPIS              (1 << 1)
  104 #define  A64_LHPIS              (1 << 0)
  105 #define A64_L_ADCMIX_SRC        0x0b
  106 #define A64_R_ADCMIX_SRC        0x0c
  107 #define  A64_ADCMIX_SRC_MIC1    (1 << 6)
  108 #define  A64_ADCMIX_SRC_MIC2    (1 << 5)
  109 #define  A64_ADCMIX_SRC_LINEIN  (1 << 2)
  110 #define  A64_ADCMIX_SRC_OMIXER  (1 << 1)
  111 #define A64_ADC_CTRL            0x0d
  112 #define  A64_ADCREN             (1 << 7)
  113 #define  A64_ADCLEN             (1 << 6)
  114 #define  A64_ADCG               __BITS(2,0)
  115 #define A64_JACK_MIC_CTRL       0x1d
  116 #define  A64_JACKDETEN          (1 << 7)
  117 #define  A64_INNERRESEN         (1 << 6)
  118 #define  A64_HMICBIASEN         (1 << 5)
  119 #define  A64_AUTOPLEN           (1 << 1)
  120 
  121 #define A64CODEC_MIXER_DEVS     ((1 << SOUND_MIXER_VOLUME) | \
  122         (1 << SOUND_MIXER_MIC))
  123 
  124 static struct ofw_compat_data compat_data[] = {
  125         { "allwinner,sun50i-a64-codec-analog",  1},
  126         { NULL,                                 0 }
  127 };
  128 
  129 struct a64codec_softc {
  130         device_t        dev;
  131         struct resource *res;
  132         struct mtx      mtx;
  133         u_int   regaddr;        /* address for the sysctl */
  134 };
  135 
  136 #define A64CODEC_LOCK(sc)               mtx_lock(&(sc)->mtx)
  137 #define A64CODEC_UNLOCK(sc)             mtx_unlock(&(sc)->mtx)
  138 #define A64CODEC_READ(sc, reg)          bus_read_4((sc)->res, (reg))
  139 #define A64CODEC_WRITE(sc, reg, val)    bus_write_4((sc)->res, (reg), (val))
  140 
  141 static int a64codec_probe(device_t dev);
  142 static int a64codec_attach(device_t dev);
  143 static int a64codec_detach(device_t dev);
  144 
  145 static u_int
  146 a64_acodec_pr_read(struct a64codec_softc *sc, u_int addr)
  147 {
  148         uint32_t val;
  149 
  150         /* Read current value */
  151         val = A64CODEC_READ(sc, A64_PR_CFG);
  152 
  153         /* De-assert reset */
  154         val |= A64_AC_PR_RST;
  155         A64CODEC_WRITE(sc, A64_PR_CFG, val);
  156 
  157         /* Read mode */
  158         val &= ~A64_AC_PR_RW;
  159         A64CODEC_WRITE(sc, A64_PR_CFG, val);
  160 
  161         /* Set address */
  162         val &= ~A64_AC_PR_ADDR_MASK;
  163         val |= A64_AC_PR_ADDR(addr);
  164         A64CODEC_WRITE(sc, A64_PR_CFG, val);
  165 
  166         /* Read data */
  167         val = A64CODEC_READ(sc, A64_PR_CFG);
  168         return A64_ACDA_PR_RDAT(val);
  169 }
  170 
  171 static void
  172 a64_acodec_pr_write(struct a64codec_softc *sc, u_int addr, u_int data)
  173 {
  174         uint32_t val;
  175 
  176         /* Read current value */
  177         val = A64CODEC_READ(sc, A64_PR_CFG);
  178 
  179         /* De-assert reset */
  180         val |= A64_AC_PR_RST;
  181         A64CODEC_WRITE(sc, A64_PR_CFG, val);
  182 
  183         /* Set address */
  184         val &= ~A64_AC_PR_ADDR_MASK;
  185         val |= A64_AC_PR_ADDR(addr);
  186         A64CODEC_WRITE(sc, A64_PR_CFG, val);
  187 
  188         /* Write data */
  189         val &= ~A64_ACDA_PR_WDAT_MASK;
  190         val |= A64_ACDA_PR_WDAT(data);
  191         A64CODEC_WRITE(sc, A64_PR_CFG, val);
  192 
  193         /* Write mode */
  194         val |= A64_AC_PR_RW;
  195         A64CODEC_WRITE(sc, A64_PR_CFG, val);
  196 
  197         /* Clear write mode */
  198         val &= ~A64_AC_PR_RW;
  199         A64CODEC_WRITE(sc, A64_PR_CFG, val);
  200 }
  201 
  202 static void
  203 a64_acodec_pr_set_clear(struct a64codec_softc *sc, u_int addr, u_int set, u_int clr)
  204 {
  205         u_int old, new;
  206 
  207         old = a64_acodec_pr_read(sc, addr);
  208         new = set | (old & ~clr);
  209         a64_acodec_pr_write(sc, addr, new);
  210 }
  211 
  212 static int
  213 a64codec_probe(device_t dev)
  214 {
  215         if (!ofw_bus_status_okay(dev))
  216                 return (ENXIO);
  217 
  218         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
  219                 return (ENXIO);
  220 
  221         device_set_desc(dev, "Allwinner A64 Analog Codec");
  222         return (BUS_PROBE_DEFAULT);
  223 }
  224 
  225 static int
  226 a64codec_attach(device_t dev)
  227 {
  228         struct a64codec_softc *sc;
  229         int error, rid;
  230         phandle_t node;
  231         regulator_t reg;
  232 
  233         sc = device_get_softc(dev);
  234         sc->dev = dev;
  235 
  236         mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
  237 
  238         rid = 0;
  239         sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
  240         if (!sc->res) {
  241                 device_printf(dev, "cannot allocate resource for device\n");
  242                 error = ENXIO;
  243                 goto fail;
  244         }
  245 
  246         if (regulator_get_by_ofw_property(dev, 0, "cpvdd-supply", &reg) == 0) {
  247                 error = regulator_enable(reg);
  248                 if (error != 0) {
  249                         device_printf(dev, "cannot enable PHY regulator\n");
  250                         goto fail;
  251                 }
  252         }
  253 
  254         /* Right & Left Headphone PA enable */
  255         a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
  256             A64_HPPA_EN, 0);
  257 
  258         /* Microphone BIAS enable */
  259         a64_acodec_pr_set_clear(sc, A64_JACK_MIC_CTRL,
  260             A64_HMICBIASEN | A64_INNERRESEN, 0);
  261 
  262         /* Unmute DAC to output mixer */
  263         a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL,
  264             A64_LMIXMUTE_LDAC, 0);
  265         a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL,
  266             A64_RMIXMUTE_RDAC, 0);
  267 
  268         /* For now we work only with headphones */
  269         a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0,
  270             0, A64_LINEOUT_EN);
  271         a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
  272             A64_HPPA_EN, 0);
  273 
  274         u_int val = a64_acodec_pr_read(sc, A64_HP_CTRL);
  275         val &= ~(0x3f);
  276         val |= 0x25;
  277         a64_acodec_pr_write(sc, A64_HP_CTRL, val);
  278 
  279         a64_acodec_pr_set_clear(sc, A64_MIC2_CTRL,
  280             A64_MIC2AMPEN | A64_MIC2_SEL | A64_MIC2G(0x3) | A64_MIC2BOOST(0x4),
  281             A64_MIC2G_MASK | A64_MIC2BOOST_MASK);
  282 
  283         a64_acodec_pr_write(sc, A64_L_ADCMIX_SRC,
  284             A64_ADCMIX_SRC_MIC2);
  285         a64_acodec_pr_write(sc, A64_R_ADCMIX_SRC,
  286             A64_ADCMIX_SRC_MIC2);
  287 
  288         /* Max out MIC2 gain */
  289         val = a64_acodec_pr_read(sc, A64_MIC2_CTRL);
  290         val &= ~(0x7);
  291         val |= (0x7);
  292         val &= ~(7 << 4);
  293         val |= (7 << 4);
  294         a64_acodec_pr_write(sc, A64_MIC2_CTRL, val);
  295 
  296         node = ofw_bus_get_node(dev);
  297         OF_device_register_xref(OF_xref_from_node(node), dev);
  298 
  299         return (0);
  300 
  301 fail:
  302         a64codec_detach(dev);
  303         return (error);
  304 }
  305 
  306 static int
  307 a64codec_detach(device_t dev)
  308 {
  309         struct a64codec_softc *sc;
  310 
  311         sc = device_get_softc(dev);
  312 
  313         if (sc->res)
  314                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res);
  315         mtx_destroy(&sc->mtx);
  316 
  317         return (0);
  318 }
  319 
  320 static int
  321 a64codec_mixer_init(struct snd_mixer *m)
  322 {
  323 
  324         mix_setdevs(m, A64CODEC_MIXER_DEVS);
  325 
  326         return (0);
  327 }
  328 
  329 static int
  330 a64codec_mixer_uninit(struct snd_mixer *m)
  331 {
  332 
  333         return (0);
  334 }
  335 
  336 static int
  337 a64codec_mixer_reinit(struct snd_mixer *m)
  338 {
  339 
  340         return (0);
  341 }
  342 
  343 static int
  344 a64codec_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
  345 {
  346         struct a64codec_softc *sc;
  347         struct mtx *mixer_lock;
  348         uint8_t do_unlock;
  349         u_int val;
  350 
  351         sc = device_get_softc(mix_getdevinfo(m));
  352         mixer_lock = mixer_get_lock(m);
  353 
  354         if (mtx_owned(mixer_lock)) {
  355                 do_unlock = 0;
  356         } else {
  357                 do_unlock = 1;
  358                 mtx_lock(mixer_lock);
  359         }
  360 
  361         right = left;
  362 
  363         A64CODEC_LOCK(sc);
  364         switch(dev) {
  365         case SOUND_MIXER_VOLUME:
  366                 val = a64_acodec_pr_read(sc, A64_HP_CTRL);
  367                 val &= ~(A64_HPVOL_MASK);
  368                 val |= A64_HPVOL(left * 63 / 100);
  369                 a64_acodec_pr_write(sc, A64_HP_CTRL, val);
  370                 break;
  371 
  372         case SOUND_MIXER_MIC:
  373                 val = a64_acodec_pr_read(sc, A64_MIC2_CTRL);
  374                 val &= ~(A64_MIC2BOOST_MASK);
  375                 val |= A64_MIC2BOOST(left * 7 / 100);
  376                 a64_acodec_pr_write(sc, A64_MIC2_CTRL, val);
  377                 break;
  378         default:
  379                 break;
  380         }
  381         A64CODEC_UNLOCK(sc);
  382 
  383         if (do_unlock) {
  384                 mtx_unlock(mixer_lock);
  385         }
  386 
  387         return (left | (right << 8));
  388 }
  389 
  390 static unsigned
  391 a64codec_mixer_setrecsrc(struct snd_mixer *m, unsigned src)
  392 {
  393 
  394         return (0);
  395 }
  396 
  397 static kobj_method_t a64codec_mixer_methods[] = {
  398         KOBJMETHOD(mixer_init,          a64codec_mixer_init),
  399         KOBJMETHOD(mixer_uninit,        a64codec_mixer_uninit),
  400         KOBJMETHOD(mixer_reinit,        a64codec_mixer_reinit),
  401         KOBJMETHOD(mixer_set,           a64codec_mixer_set),
  402         KOBJMETHOD(mixer_setrecsrc,     a64codec_mixer_setrecsrc),
  403         KOBJMETHOD_END
  404 };
  405 
  406 MIXER_DECLARE(a64codec_mixer);
  407 
  408 static int
  409 a64codec_dai_init(device_t dev, uint32_t format)
  410 {
  411 
  412         return (0);
  413 }
  414 
  415 static int
  416 a64codec_dai_trigger(device_t dev, int go, int pcm_dir)
  417 {
  418         struct a64codec_softc   *sc = device_get_softc(dev);
  419 
  420         if ((pcm_dir != PCMDIR_PLAY) && (pcm_dir != PCMDIR_REC))
  421                 return (EINVAL);
  422 
  423         switch (go) {
  424         case PCMTRIG_START:
  425                 if (pcm_dir == PCMDIR_PLAY) {
  426                         /* Enable DAC analog l/r channels, HP PA, and output mixer */
  427                         a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL,
  428                             A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN |
  429                             A64_RHPPAMUTE | A64_LHPPAMUTE, 0);
  430                 }
  431                 else if (pcm_dir == PCMDIR_REC) {
  432                         /* Enable ADC analog l/r channels */
  433                         a64_acodec_pr_set_clear(sc, A64_ADC_CTRL,
  434                             A64_ADCREN | A64_ADCLEN, 0);
  435                 }
  436                 break;
  437 
  438         case PCMTRIG_STOP:
  439         case PCMTRIG_ABORT:
  440                 if (pcm_dir == PCMDIR_PLAY) {
  441                         /* Disable DAC analog l/r channels, HP PA, and output mixer */
  442                         a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL,
  443                             0, A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN |
  444                             A64_RHPPAMUTE | A64_LHPPAMUTE);
  445                 }
  446                 else if (pcm_dir == PCMDIR_REC) {
  447                         /* Disable ADC analog l/r channels */
  448                         a64_acodec_pr_set_clear(sc, A64_ADC_CTRL,
  449                             0, A64_ADCREN | A64_ADCLEN);
  450                 }
  451                 break;
  452         }
  453 
  454         return (0);
  455 }
  456 
  457 static int
  458 a64codec_dai_setup_mixer(device_t dev, device_t pcmdev)
  459 {
  460 
  461         mixer_init(pcmdev, &a64codec_mixer_class, dev);
  462 
  463         return (0);
  464 }
  465 
  466 static device_method_t a64codec_methods[] = {
  467         /* Device interface */
  468         DEVMETHOD(device_probe,         a64codec_probe),
  469         DEVMETHOD(device_attach,        a64codec_attach),
  470         DEVMETHOD(device_detach,        a64codec_detach),
  471 
  472         DEVMETHOD(audio_dai_init,       a64codec_dai_init),
  473         DEVMETHOD(audio_dai_setup_mixer,        a64codec_dai_setup_mixer),
  474         DEVMETHOD(audio_dai_trigger,    a64codec_dai_trigger),
  475 
  476         DEVMETHOD_END
  477 };
  478 
  479 static driver_t a64codec_driver = {
  480         "a64codec",
  481         a64codec_methods,
  482         sizeof(struct a64codec_softc),
  483 };
  484 
  485 DRIVER_MODULE(a64codec, simplebus, a64codec_driver, 0, 0);
  486 SIMPLEBUS_PNP_INFO(compat_data);

Cache object: e624ffaa7ff351b4511b349bd743ff23


[ 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.