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/dev/ic/am7930.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 /*      $NetBSD: am7930.c,v 1.60 2020/09/12 05:19:16 isaki Exp $        */
    2 
    3 /*
    4  * Copyright (c) 1995 Rolf Grossmann
    5  * All rights reserved.
    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  * 3. All advertising materials mentioning features or use of this software
   16  *    must display the following acknowledgement:
   17  *      This product includes software developed by Rolf Grossmann.
   18  * 4. The name of the author may not be used to endorse or promote products
   19  *    derived from this software without specific prior written permission
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   31  */
   32 
   33 /*
   34  * Front-end attachment independent layer for AMD 79c30
   35  * audio driver.  No ISDN support.
   36  */
   37 
   38 #include <sys/cdefs.h>
   39 __KERNEL_RCSID(0, "$NetBSD: am7930.c,v 1.60 2020/09/12 05:19:16 isaki Exp $");
   40 
   41 #include "audio.h"
   42 #if NAUDIO > 0
   43 
   44 #include <sys/param.h>
   45 #include <sys/systm.h>
   46 #include <sys/atomic.h>
   47 #include <sys/errno.h>
   48 #include <sys/ioctl.h>
   49 #include <sys/device.h>
   50 #include <sys/proc.h>
   51 
   52 #include <sys/bus.h>
   53 #include <sys/cpu.h>
   54 
   55 #include <sys/audioio.h>
   56 #include <dev/audio/audio_if.h>
   57 #include <dev/audio/mulaw.h>
   58 
   59 #include <dev/ic/am7930reg.h>
   60 #include <dev/ic/am7930var.h>
   61 
   62 #ifdef AUDIO_DEBUG
   63 int     am7930debug = 0;
   64 #define DPRINTF(x)      if (am7930debug) printf x
   65 #else
   66 #define DPRINTF(x)
   67 #endif
   68 
   69 
   70 /* The following tables stolen from former (4.4Lite's) sys/sparc/bsd_audio.c */
   71 
   72 /*
   73  * gx, gr & stg gains.  this table must contain 256 elements with
   74  * the 0th being "infinity" (the magic value 9008).  The remaining
   75  * elements match sun's gain curve (but with higher resolution):
   76  * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
   77  */
   78 static const uint16_t gx_coeff[256] = {
   79         0x9008, 0x8e7c, 0x8e51, 0x8e45, 0x8d42, 0x8d3b, 0x8c36, 0x8c33,
   80         0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
   81         0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
   82         0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
   83         0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
   84         0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
   85         0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
   86         0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
   87         0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
   88         0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
   89         0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
   90         0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
   91         0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
   92         0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
   93         0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
   94         0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
   95         0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
   96         0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
   97         0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
   98         0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
   99         0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
  100         0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
  101         0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
  102         0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
  103         0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
  104         0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
  105         0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
  106         0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
  107         0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
  108         0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
  109         0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
  110         0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
  111 };
  112 
  113 /*
  114  * second stage play gain.
  115  */
  116 static const uint16_t ger_coeff[] = {
  117         0x431f, /* 5. dB */
  118         0x331f, /* 5.5 dB */
  119         0x40dd, /* 6. dB */
  120         0x11dd, /* 6.5 dB */
  121         0x440f, /* 7. dB */
  122         0x411f, /* 7.5 dB */
  123         0x311f, /* 8. dB */
  124         0x5520, /* 8.5 dB */
  125         0x10dd, /* 9. dB */
  126         0x4211, /* 9.5 dB */
  127         0x410f, /* 10. dB */
  128         0x111f, /* 10.5 dB */
  129         0x600b, /* 11. dB */
  130         0x00dd, /* 11.5 dB */
  131         0x4210, /* 12. dB */
  132         0x110f, /* 13. dB */
  133         0x7200, /* 14. dB */
  134         0x2110, /* 15. dB */
  135         0x2200, /* 15.9 dB */
  136         0x000b, /* 16.9 dB */
  137         0x000f  /* 18. dB */
  138 #define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
  139 };
  140 
  141 static const struct audio_format am7930_format = {
  142         .mode           = AUMODE_PLAY | AUMODE_RECORD,
  143         .encoding       = AUDIO_ENCODING_ULAW,
  144         .validbits      = 8,
  145         .precision      = 8,
  146         .channels       = 1,
  147         .channel_mask   = AUFMT_MONAURAL,
  148         .frequency_type = 1,
  149         .frequency      = { 8000 },
  150 };
  151 
  152 /*
  153  * Indirect access functions.
  154  */
  155 
  156 static void
  157 am7930_iwrite(struct am7930_softc *sc, int reg, uint8_t val)
  158 {
  159 
  160         AM7930_DWRITE(sc, AM7930_DREG_CR, reg);
  161         AM7930_DWRITE(sc, AM7930_DREG_DR, val);
  162 }
  163 
  164 static uint8_t
  165 am7930_iread(struct am7930_softc *sc, int reg)
  166 {
  167 
  168         AM7930_DWRITE(sc, AM7930_DREG_CR, reg);
  169         return AM7930_DREAD(sc, AM7930_DREG_DR);
  170 }
  171 
  172 static void
  173 am7930_iwrite16(struct am7930_softc *sc, int reg, uint16_t val)
  174 {
  175 
  176         AM7930_DWRITE(sc, AM7930_DREG_CR, reg);
  177         AM7930_DWRITE(sc, AM7930_DREG_DR, val);
  178         AM7930_DWRITE(sc, AM7930_DREG_DR, val >> 8);
  179 }
  180 
  181 static uint16_t __unused
  182 am7930_iread16(struct am7930_softc *sc, int reg)
  183 {
  184         uint lo, hi;
  185 
  186         AM7930_DWRITE(sc, AM7930_DREG_CR, reg);
  187         lo = AM7930_DREAD(sc, AM7930_DREG_DR);
  188         hi = AM7930_DREAD(sc, AM7930_DREG_DR);
  189         return (hi << 8) | lo;
  190 }
  191 
  192 #define AM7930_IWRITE(sc,r,v)   am7930_iwrite(sc,r,v)
  193 #define AM7930_IREAD(sc,r)      am7930_iread(sc,r)
  194 #define AM7930_IWRITE16(sc,r,v) am7930_iwrite16(sc,r,v)
  195 #define AM7930_IREAD16(sc,r)    am7930_iread16(sc,r)
  196 
  197 /*
  198  * Reset chip and set boot-time softc defaults.
  199  */
  200 void
  201 am7930_init(struct am7930_softc *sc, int flag)
  202 {
  203 
  204         DPRINTF(("%s\n", __func__));
  205 
  206         /* set boot defaults */
  207         sc->sc_rlevel = 128;
  208         sc->sc_plevel = 128;
  209         sc->sc_mlevel = 0;
  210         sc->sc_out_port = AUDIOAMD_SPEAKER_VOL;
  211         sc->sc_mic_mute = 0;
  212 
  213         memset(&sc->sc_p, 0, sizeof(sc->sc_p));
  214         memset(&sc->sc_r, 0, sizeof(sc->sc_r));
  215 
  216         /* disable sample interrupts */
  217         AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR4, 0);
  218 
  219         /* initialise voice and data, and disable interrupts */
  220         AM7930_IWRITE(sc, AM7930_IREG_INIT,
  221                 AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
  222 
  223         if (flag == AUDIOAMD_DMA_MODE) {
  224 
  225                 /* configure PP for serial (SBP) mode */
  226                 AM7930_IWRITE(sc, AM7930_IREG_PP_PPCR1, AM7930_PPCR1_SBP);
  227 
  228                 /*
  229                  * Initialise the MUX unit - route the MAP to the PP
  230                  */
  231                 AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR1,
  232                         (AM7930_MCRCHAN_BA << 4) | AM7930_MCRCHAN_BD);
  233                 AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR2, AM7930_MCRCHAN_NC);
  234                 AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR3, AM7930_MCRCHAN_NC);
  235 
  236                 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
  237         } else {
  238 
  239                 /*
  240                  * Initialize the MUX unit.  We use MCR3 to route the MAP
  241                  * through channel Bb.  MCR1 and MCR2 are unused.
  242                  * Setting the INT enable bit in MCR4 will generate an
  243                  * interrupt on each converted audio sample.
  244                  */
  245                 AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR1, 0);
  246                 AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR2, 0);
  247                 AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR3,
  248                         (AM7930_MCRCHAN_BB << 4) | AM7930_MCRCHAN_BA);
  249                 AM7930_IWRITE(sc, AM7930_IREG_MUX_MCR4,
  250                         AM7930_MCR4_INT_ENABLE);
  251 
  252                 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SOFTSERIAL);
  253         }
  254 
  255         mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
  256 
  257         sc->sc_sicookie = softint_establish(SOFTINT_SERIAL, &am7930_swintr, sc);
  258         if (sc->sc_sicookie == NULL) {
  259                 aprint_error_dev(sc->sc_dev,
  260                     "cannot establish software interrupt\n");
  261                 return;
  262         }
  263 }
  264 
  265 int
  266 am7930_query_format(void *addr, audio_format_query_t *afp)
  267 {
  268 
  269         return audio_query_format(&am7930_format, 1, afp);
  270 }
  271 
  272 int
  273 am7930_set_format(void *addr, int setmode,
  274         const audio_params_t *play, const audio_params_t *rec,
  275         audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
  276 {
  277 
  278         if ((setmode & AUMODE_PLAY) != 0) {
  279                 pfil->codec = audio_internal_to_mulaw;
  280         }
  281         if ((setmode & AUMODE_RECORD) != 0) {
  282                 rfil->codec = audio_mulaw_to_internal;
  283         }
  284 
  285         return 0;
  286 }
  287 
  288 int
  289 am7930_commit_settings(void *addr)
  290 {
  291         struct am7930_softc *sc;
  292         uint16_t ger, gr, gx, stgr;
  293         uint8_t mmr2, mmr3;
  294         int level;
  295 
  296         DPRINTF(("%s\n", __func__));
  297         sc = addr;
  298         gx = gx_coeff[sc->sc_rlevel];
  299         stgr = gx_coeff[sc->sc_mlevel];
  300 
  301         level = (sc->sc_plevel * (256 + NGER)) >> 8;
  302         if (level >= 256) {
  303                 ger = ger_coeff[level - 256];
  304                 gr = gx_coeff[255];
  305         } else {
  306                 ger = ger_coeff[0];
  307                 gr = gx_coeff[level];
  308         }
  309 
  310         mutex_enter(&sc->sc_intr_lock);
  311 
  312         mmr2 = AM7930_IREAD(sc, AM7930_IREG_MAP_MMR2);
  313         if (sc->sc_out_port == AUDIOAMD_SPEAKER_VOL)
  314                 mmr2 |= AM7930_MMR2_LS;
  315         else
  316                 mmr2 &= ~AM7930_MMR2_LS;
  317         AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR2, mmr2);
  318 
  319         mmr3 = AM7930_IREAD(sc, AM7930_IREG_MAP_MMR3);
  320         if (sc->sc_mic_mute)
  321                 mmr3 |= AM7930_MMR3_MUTE;
  322         else
  323                 mmr3 &= ~AM7930_MMR3_MUTE;
  324         AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR3, mmr3);
  325 
  326         AM7930_IWRITE(sc, AM7930_IREG_MAP_MMR1,
  327                 AM7930_MMR1_GX | AM7930_MMR1_GER |
  328                 AM7930_MMR1_GR | AM7930_MMR1_STG);
  329 
  330         AM7930_IWRITE16(sc, AM7930_IREG_MAP_GX, gx);
  331         AM7930_IWRITE16(sc, AM7930_IREG_MAP_STG, stgr);
  332         AM7930_IWRITE16(sc, AM7930_IREG_MAP_GR, gr);
  333         AM7930_IWRITE16(sc, AM7930_IREG_MAP_GER, ger);
  334 
  335         mutex_exit(&sc->sc_intr_lock);
  336 
  337         return 0;
  338 }
  339 
  340 int
  341 am7930_trigger_output(void *addr, void *start, void *end, int blksize,
  342     void (*intr)(void *), void *arg, const audio_params_t *params)
  343 {
  344         struct am7930_softc *sc;
  345 
  346         DPRINTF(("%s: blksize=%d %p(%p)\n", __func__, blksize, intr, arg));
  347         sc = addr;
  348         sc->sc_p.intr = intr;
  349         sc->sc_p.arg = arg;
  350         sc->sc_p.start = start;
  351         sc->sc_p.end = end;
  352         sc->sc_p.blksize = blksize;
  353         sc->sc_p.data = sc->sc_p.start;
  354         sc->sc_p.blkend = sc->sc_p.start + sc->sc_p.blksize;
  355 
  356         /* Start if either play or rec start. */
  357         if (sc->sc_r.intr == NULL) {
  358                 AM7930_IWRITE(sc, AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
  359                 DPRINTF(("%s: started intrs.\n", __func__));
  360         }
  361         return 0;
  362 }
  363 
  364 int
  365 am7930_trigger_input(void *addr, void *start, void *end, int blksize,
  366     void (*intr)(void *), void *arg, const audio_params_t *params)
  367 {
  368         struct am7930_softc *sc;
  369 
  370         DPRINTF(("%s: blksize=%d %p(%p)\n", __func__, blksize, intr, arg));
  371         sc = addr;
  372         sc->sc_r.intr = intr;
  373         sc->sc_r.arg = arg;
  374         sc->sc_r.start = start;
  375         sc->sc_r.end = end;
  376         sc->sc_r.blksize = blksize;
  377         sc->sc_r.data = sc->sc_r.start;
  378         sc->sc_r.blkend = sc->sc_r.start + sc->sc_r.blksize;
  379 
  380         /* Start if either play or rec start. */
  381         if (sc->sc_p.intr == NULL) {
  382                 AM7930_IWRITE(sc, AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
  383                 DPRINTF(("%s: started intrs.\n", __func__));
  384         }
  385         return 0;
  386 }
  387 
  388 int
  389 am7930_halt_output(void *addr)
  390 {
  391         struct am7930_softc *sc;
  392 
  393         sc = addr;
  394         sc->sc_p.intr = NULL;
  395         /* Halt if both of play and rec halt. */
  396         if (sc->sc_r.intr == NULL) {
  397                 AM7930_IWRITE(sc, AM7930_IREG_INIT,
  398                     AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
  399         }
  400         return 0;
  401 }
  402 
  403 int
  404 am7930_halt_input(void *addr)
  405 {
  406         struct am7930_softc *sc;
  407 
  408         sc = addr;
  409         sc->sc_r.intr = NULL;
  410         /* Halt if both of play and rec halt. */
  411         if (sc->sc_p.intr == NULL) {
  412                 AM7930_IWRITE(sc, AM7930_IREG_INIT,
  413                     AM7930_INIT_PMS_ACTIVE | AM7930_INIT_INT_DISABLE);
  414         }
  415         return 0;
  416 }
  417 
  418 int
  419 am7930_hwintr(void *arg)
  420 {
  421         struct am7930_softc *sc;
  422         int k __unused;
  423 
  424         sc = arg;
  425 
  426         /*
  427          * This hwintr is called as pseudo-DMA.  So don't acquire intr_lock.
  428          */
  429 
  430         /* clear interrupt */
  431         k = AM7930_DREAD(sc, AM7930_DREG_IR);
  432 #if !defined(__vax__)
  433         /* On vax, interrupt is not shared, this shouldn't happen */
  434         if ((k & (AM7930_IR_DTTHRSH | AM7930_IR_DRTHRSH | AM7930_IR_DSRI |
  435             AM7930_IR_DERI | AM7930_IR_BBUFF)) == 0) {
  436                 return 0;
  437         }
  438 #endif
  439 
  440         /* receive incoming data */
  441         if (sc->sc_r.intr) {
  442                 *sc->sc_r.data++ = AM7930_DREAD(sc, AM7930_DREG_BBRB);
  443                 if (sc->sc_r.data == sc->sc_r.blkend) {
  444                         if (sc->sc_r.blkend == sc->sc_r.end) {
  445                                 sc->sc_r.data = sc->sc_r.start;
  446                                 sc->sc_r.blkend = sc->sc_r.start;
  447                         }
  448                         sc->sc_r.blkend += sc->sc_r.blksize;
  449                         atomic_store_relaxed(&sc->sc_r.intr_pending, 1);
  450                         softint_schedule(sc->sc_sicookie);
  451                 }
  452         }
  453 
  454         /* send outgoing data */
  455         if (sc->sc_p.intr) {
  456                 AM7930_DWRITE(sc, AM7930_DREG_BBTB, *sc->sc_p.data++);
  457                 if (sc->sc_p.data == sc->sc_p.blkend) {
  458                         if (sc->sc_p.blkend == sc->sc_p.end) {
  459                                 sc->sc_p.data = sc->sc_p.start;
  460                                 sc->sc_p.blkend = sc->sc_p.start;
  461                         }
  462                         sc->sc_p.blkend += sc->sc_p.blksize;
  463                         atomic_store_relaxed(&sc->sc_p.intr_pending, 1);
  464                         softint_schedule(sc->sc_sicookie);
  465                 }
  466         }
  467 
  468         sc->sc_intrcnt.ev_count++;
  469         return 1;
  470 }
  471 
  472 void
  473 am7930_swintr(void *cookie)
  474 {
  475         struct am7930_softc *sc = cookie;
  476 
  477         mutex_enter(&sc->sc_intr_lock);
  478         if (atomic_cas_uint(&sc->sc_r.intr_pending, 1, 0) == 1) {
  479                 (*sc->sc_r.intr)(sc->sc_r.arg);
  480         }
  481         if (atomic_cas_uint(&sc->sc_p.intr_pending, 1, 0) == 1) {
  482                 (*sc->sc_p.intr)(sc->sc_p.arg);
  483         }
  484         mutex_exit(&sc->sc_intr_lock);
  485 }
  486 
  487 
  488 /*
  489  * XXX chip is full-duplex, but really attach-dependent.
  490  * For now we know of no half-duplex attachments.
  491  */
  492 int
  493 am7930_get_props(void *addr)
  494 {
  495 
  496         return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
  497             AUDIO_PROP_FULLDUPLEX;
  498 }
  499 
  500 /*
  501  * Attach-dependent channel set/query
  502  */
  503 int
  504 am7930_set_port(void *addr, mixer_ctrl_t *cp)
  505 {
  506         struct am7930_softc *sc;
  507 
  508         DPRINTF(("%s: port=%d\n", __func__, cp->dev));
  509         sc = addr;
  510         if (cp->dev == AUDIOAMD_RECORD_SOURCE ||
  511                 cp->dev == AUDIOAMD_MONITOR_OUTPUT ||
  512                 cp->dev == AUDIOAMD_MIC_MUTE) {
  513                 if (cp->type != AUDIO_MIXER_ENUM)
  514                         return EINVAL;
  515         } else if (cp->type != AUDIO_MIXER_VALUE ||
  516             cp->un.value.num_channels != 1) {
  517                 return EINVAL;
  518         }
  519 
  520         switch(cp->dev) {
  521             case AUDIOAMD_MIC_VOL:
  522                     sc->sc_rlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
  523                     break;
  524             case AUDIOAMD_SPEAKER_VOL:
  525             case AUDIOAMD_HEADPHONES_VOL:
  526                     sc->sc_plevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
  527                     break;
  528             case AUDIOAMD_MONITOR_VOL:
  529                     sc->sc_mlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
  530                     break;
  531             case AUDIOAMD_RECORD_SOURCE:
  532                     if (cp->un.ord != AUDIOAMD_MIC_VOL)
  533                             return EINVAL;
  534                     break;
  535             case AUDIOAMD_MIC_MUTE:
  536                     sc->sc_mic_mute = cp->un.ord;
  537                     break;
  538             case AUDIOAMD_MONITOR_OUTPUT:
  539                     if (cp->un.ord != AUDIOAMD_SPEAKER_VOL &&
  540                         cp->un.ord != AUDIOAMD_HEADPHONES_VOL)
  541                             return EINVAL;
  542                         sc->sc_out_port = cp->un.ord;
  543                     break;
  544             default:
  545                     return EINVAL;
  546                     /* NOTREACHED */
  547         }
  548         return 0;
  549 }
  550 
  551 int
  552 am7930_get_port(void *addr, mixer_ctrl_t *cp)
  553 {
  554         struct am7930_softc *sc;
  555 
  556         DPRINTF(("%s: port=%d\n", __func__, cp->dev));
  557         sc = addr;
  558         if (cp->dev == AUDIOAMD_RECORD_SOURCE ||
  559                 cp->dev == AUDIOAMD_MONITOR_OUTPUT ||
  560                 cp->dev == AUDIOAMD_MIC_MUTE) {
  561                 if (cp->type != AUDIO_MIXER_ENUM)
  562                         return EINVAL;
  563         } else if (cp->type != AUDIO_MIXER_VALUE ||
  564                 cp->un.value.num_channels != 1) {
  565                 return EINVAL;
  566         }
  567 
  568         switch(cp->dev) {
  569             case AUDIOAMD_MIC_VOL:
  570                     cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_rlevel;
  571                     break;
  572             case AUDIOAMD_SPEAKER_VOL:
  573             case AUDIOAMD_HEADPHONES_VOL:
  574                     cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_plevel;
  575                     break;
  576             case AUDIOAMD_MONITOR_VOL:
  577                     cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_mlevel;
  578                     break;
  579             case AUDIOAMD_RECORD_SOURCE:
  580                     cp->un.ord = AUDIOAMD_MIC_VOL;
  581                     break;
  582             case AUDIOAMD_MIC_MUTE:
  583                     cp->un.ord = sc->sc_mic_mute;
  584                     break;
  585             case AUDIOAMD_MONITOR_OUTPUT:
  586                     cp->un.ord = sc->sc_out_port;
  587                     break;
  588             default:
  589                     return EINVAL;
  590                     /* NOTREACHED */
  591         }
  592         return 0;
  593 }
  594 
  595 
  596 /*
  597  * Define mixer control facilities.
  598  */
  599 int
  600 am7930_query_devinfo(void *addr, mixer_devinfo_t *dip)
  601 {
  602 
  603         DPRINTF(("%s\n", __func__));
  604 
  605         switch(dip->index) {
  606         case AUDIOAMD_MIC_VOL:
  607                 dip->type = AUDIO_MIXER_VALUE;
  608                 dip->mixer_class = AUDIOAMD_INPUT_CLASS;
  609                 dip->prev =  AUDIO_MIXER_LAST;
  610                 dip->next = AUDIOAMD_MIC_MUTE;
  611                 strcpy(dip->label.name, AudioNmicrophone);
  612                 dip->un.v.num_channels = 1;
  613                 strcpy(dip->un.v.units.name, AudioNvolume);
  614                 break;
  615         case AUDIOAMD_SPEAKER_VOL:
  616                 dip->type = AUDIO_MIXER_VALUE;
  617                 dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
  618                 dip->prev = dip->next = AUDIO_MIXER_LAST;
  619                 strcpy(dip->label.name, AudioNspeaker);
  620                 dip->un.v.num_channels = 1;
  621                 strcpy(dip->un.v.units.name, AudioNvolume);
  622                 break;
  623         case AUDIOAMD_HEADPHONES_VOL:
  624                 dip->type = AUDIO_MIXER_VALUE;
  625                 dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
  626                 dip->prev = dip->next = AUDIO_MIXER_LAST;
  627                 strcpy(dip->label.name, AudioNheadphone);
  628                 dip->un.v.num_channels = 1;
  629                 strcpy(dip->un.v.units.name, AudioNvolume);
  630                 break;
  631         case AUDIOAMD_MONITOR_VOL:
  632                 dip->type = AUDIO_MIXER_VALUE;
  633                 dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
  634                 dip->prev = dip->next = AUDIO_MIXER_LAST;
  635                 strcpy(dip->label.name, AudioNmonitor);
  636                 dip->un.v.num_channels = 1;
  637                 strcpy(dip->un.v.units.name, AudioNvolume);
  638                 break;
  639         case AUDIOAMD_RECORD_SOURCE:
  640                 dip->type = AUDIO_MIXER_ENUM;
  641                 dip->mixer_class = AUDIOAMD_RECORD_CLASS;
  642                 dip->next = dip->prev = AUDIO_MIXER_LAST;
  643                 strcpy(dip->label.name, AudioNsource);
  644                 dip->un.e.num_mem = 1;
  645                 strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
  646                 dip->un.e.member[0].ord = AUDIOAMD_MIC_VOL;
  647                 break;
  648         case AUDIOAMD_MONITOR_OUTPUT:
  649                 dip->type = AUDIO_MIXER_ENUM;
  650                 dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
  651                 dip->next = dip->prev = AUDIO_MIXER_LAST;
  652                 strcpy(dip->label.name, AudioNoutput);
  653                 dip->un.e.num_mem = 2;
  654                 strcpy(dip->un.e.member[0].label.name, AudioNspeaker);
  655                 dip->un.e.member[0].ord = AUDIOAMD_SPEAKER_VOL;
  656                 strcpy(dip->un.e.member[1].label.name, AudioNheadphone);
  657                 dip->un.e.member[1].ord = AUDIOAMD_HEADPHONES_VOL;
  658                 break;
  659         case AUDIOAMD_MIC_MUTE:
  660                 dip->type = AUDIO_MIXER_ENUM;
  661                 dip->mixer_class = AUDIOAMD_INPUT_CLASS;
  662                 dip->prev =  AUDIOAMD_MIC_VOL;
  663                 dip->next = AUDIO_MIXER_LAST;
  664                 strcpy(dip->label.name, AudioNmute);
  665                 dip->un.e.num_mem = 2;
  666                 strcpy(dip->un.e.member[0].label.name, AudioNoff);
  667                 dip->un.e.member[0].ord = 0;
  668                 strcpy(dip->un.e.member[1].label.name, AudioNon);
  669                 dip->un.e.member[1].ord = 1;
  670                 break;
  671         case AUDIOAMD_INPUT_CLASS:
  672                 dip->type = AUDIO_MIXER_CLASS;
  673                 dip->mixer_class = AUDIOAMD_INPUT_CLASS;
  674                 dip->next = dip->prev = AUDIO_MIXER_LAST;
  675                 strcpy(dip->label.name, AudioCinputs);
  676                 break;
  677         case AUDIOAMD_OUTPUT_CLASS:
  678                 dip->type = AUDIO_MIXER_CLASS;
  679                 dip->mixer_class = AUDIOAMD_OUTPUT_CLASS;
  680                 dip->next = dip->prev = AUDIO_MIXER_LAST;
  681                 strcpy(dip->label.name, AudioCoutputs);
  682                 break;
  683         case AUDIOAMD_RECORD_CLASS:
  684                 dip->type = AUDIO_MIXER_CLASS;
  685                 dip->mixer_class = AUDIOAMD_RECORD_CLASS;
  686                 dip->next = dip->prev = AUDIO_MIXER_LAST;
  687                 strcpy(dip->label.name, AudioCrecord);
  688                 break;
  689         case AUDIOAMD_MONITOR_CLASS:
  690                 dip->type = AUDIO_MIXER_CLASS;
  691                 dip->mixer_class = AUDIOAMD_MONITOR_CLASS;
  692                 dip->next = dip->prev = AUDIO_MIXER_LAST;
  693                 strcpy(dip->label.name, AudioCmonitor);
  694                 break;
  695         default:
  696                 return ENXIO;
  697                 /*NOTREACHED*/
  698         }
  699 
  700         DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
  701 
  702         return 0;
  703 }
  704 
  705 void
  706 am7930_get_locks(void *addr, kmutex_t **intr, kmutex_t **thread)
  707 {
  708         struct am7930_softc *sc;
  709 
  710         sc = addr;
  711         *intr = &sc->sc_intr_lock;
  712         *thread = &sc->sc_lock;
  713 }
  714 
  715 #endif  /* NAUDIO */

Cache object: 16b376010129a9c8d9ad76ea552e9bd1


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