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/sound/pcm/ac97.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  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #ifdef HAVE_KERNEL_OPTION_HEADERS
   28 #include "opt_snd.h"
   29 #endif
   30 
   31 #include <dev/sound/pcm/sound.h>
   32 #include <dev/sound/pcm/ac97.h>
   33 #include <dev/sound/pcm/ac97_patch.h>
   34 
   35 #include <dev/pci/pcivar.h>
   36 
   37 #include "mixer_if.h"
   38 
   39 SND_DECLARE_FILE("$FreeBSD: releng/9.0/sys/dev/sound/pcm/ac97.c 193640 2009-06-07 19:12:08Z ariff $");
   40 
   41 MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
   42 
   43 struct ac97mixtable_entry {
   44         int reg;                /* register index               */
   45                                 /* reg < 0 if inverted polarity */
   46         unsigned bits:4;        /* width of control field       */
   47         unsigned ofs:4;         /* offset (only if stereo=0)    */
   48         unsigned stereo:1;      /* set for stereo controls      */
   49         unsigned mute:1;        /* bit15 is MUTE                */
   50         unsigned recidx:4;      /* index in rec mux             */
   51         unsigned mask:1;        /* use only masked bits         */
   52         unsigned enable:1;      /* entry is enabled             */
   53 };
   54 
   55 #define AC97_MIXER_SIZE         SOUND_MIXER_NRDEVICES
   56 
   57 struct ac97_info {
   58         kobj_t methods;
   59         device_t dev;
   60         void *devinfo;
   61         u_int32_t id;
   62         u_int32_t subvendor;
   63         unsigned count, caps, se, extcaps, extid, extstat, noext:1;
   64         u_int32_t flags;
   65         struct ac97mixtable_entry mix[AC97_MIXER_SIZE];
   66         char name[16];
   67         struct mtx *lock;
   68 };
   69 
   70 struct ac97_vendorid {
   71         u_int32_t   id;
   72         const char *name;
   73 };
   74 
   75 struct ac97_codecid {
   76         u_int32_t  id;
   77         u_int8_t   stepmask;
   78         u_int8_t   noext:1;
   79         char      *name;
   80         ac97_patch patch;
   81 };
   82 
   83 static const struct ac97mixtable_entry ac97mixtable_default[AC97_MIXER_SIZE] = {
   84     /*  [offset]                        reg          bits of st mu re mk en */
   85         [SOUND_MIXER_VOLUME]    = { AC97_MIX_MASTER,    5, 0, 1, 1, 6, 0, 1 },
   86         [SOUND_MIXER_OGAIN]     = { AC97_MIX_AUXOUT,    5, 0, 1, 1, 0, 0, 0 },
   87         [SOUND_MIXER_PHONEOUT]  = { AC97_MIX_MONO,      5, 0, 0, 1, 7, 0, 0 },
   88         [SOUND_MIXER_BASS]      = { AC97_MIX_TONE,      4, 8, 0, 0, 0, 1, 0 },
   89         [SOUND_MIXER_TREBLE]    = { AC97_MIX_TONE,      4, 0, 0, 0, 0, 1, 0 },
   90         [SOUND_MIXER_PCM]       = { AC97_MIX_PCM,       5, 0, 1, 1, 0, 0, 1 },
   91         [SOUND_MIXER_SPEAKER]   = { AC97_MIX_BEEP,      4, 1, 0, 1, 0, 0, 0 },
   92         [SOUND_MIXER_LINE]      = { AC97_MIX_LINE,      5, 0, 1, 1, 5, 0, 1 },
   93         [SOUND_MIXER_PHONEIN]   = { AC97_MIX_PHONE,     5, 0, 0, 1, 8, 0, 0 },
   94         [SOUND_MIXER_MIC]       = { AC97_MIX_MIC,       5, 0, 0, 1, 1, 1, 1 },
   95         /* use igain for the mic 20dB boost */
   96         [SOUND_MIXER_IGAIN]     = { -AC97_MIX_MIC,      1, 6, 0, 0, 0, 1, 1 },
   97         [SOUND_MIXER_CD]        = { AC97_MIX_CD,        5, 0, 1, 1, 2, 0, 1 },
   98         [SOUND_MIXER_LINE1]     = { AC97_MIX_AUX,       5, 0, 1, 1, 4, 0, 0 },
   99         [SOUND_MIXER_VIDEO]     = { AC97_MIX_VIDEO,     5, 0, 1, 1, 3, 0, 0 },
  100         [SOUND_MIXER_RECLEV]    = { -AC97_MIX_RGAIN,    4, 0, 1, 1, 0, 0, 1 }
  101 };
  102 
  103 static const struct ac97_vendorid ac97vendorid[] = {
  104         { 0x41445300, "Analog Devices" },
  105         { 0x414b4d00, "Asahi Kasei" },
  106         { 0x414c4300, "Realtek" },
  107         { 0x414c4700, "Avance Logic" },
  108         { 0x43525900, "Cirrus Logic" },
  109         { 0x434d4900, "C-Media Electronics" },
  110         { 0x43585400, "Conexant" },
  111         { 0x44543000, "Diamond Technology" },
  112         { 0x454d4300, "eMicro" },
  113         { 0x45838300, "ESS Technology" },
  114         { 0x48525300, "Intersil" },
  115         { 0x49434500, "ICEnsemble" },
  116         { 0x49544500, "ITE, Inc." },
  117         { 0x4e534300, "National Semiconductor" },
  118         { 0x50534300, "Philips Semiconductor" },
  119         { 0x83847600, "SigmaTel" },
  120         { 0x53494c00, "Silicon Laboratories" },
  121         { 0x54524100, "TriTech" },
  122         { 0x54584e00, "Texas Instruments" },
  123         { 0x56494100, "VIA Technologies" },
  124         { 0x57454300, "Winbond" },
  125         { 0x574d4c00, "Wolfson" },
  126         { 0x594d4800, "Yamaha" },
  127         /* 
  128          * XXX This is a fluke, really! The real vendor
  129          * should be SigmaTel, not this! This should be
  130          * removed someday!
  131          */
  132         { 0x01408300, "Creative" },
  133         { 0x00000000, NULL }
  134 };
  135 
  136 static struct ac97_codecid ac97codecid[] = {
  137         { 0x41445303, 0x00, 0, "AD1819",        0 },
  138         { 0x41445340, 0x00, 0, "AD1881",        0 },
  139         { 0x41445348, 0x00, 0, "AD1881A",       0 },
  140         { 0x41445360, 0x00, 0, "AD1885",        0 },
  141         { 0x41445361, 0x00, 0, "AD1886",        ad1886_patch },
  142         { 0x41445362, 0x00, 0, "AD1887",        0 },
  143         { 0x41445363, 0x00, 0, "AD1886A",       0 },
  144         { 0x41445368, 0x00, 0, "AD1888",        ad198x_patch },
  145         { 0x41445370, 0x00, 0, "AD1980",        ad198x_patch },
  146         { 0x41445372, 0x00, 0, "AD1981A",       0 },
  147         { 0x41445374, 0x00, 0, "AD1981B",       ad1981b_patch },
  148         { 0x41445375, 0x00, 0, "AD1985",        ad198x_patch },
  149         { 0x41445378, 0x00, 0, "AD1986",        ad198x_patch },
  150         { 0x414b4d00, 0x00, 1, "AK4540",        0 },
  151         { 0x414b4d01, 0x00, 1, "AK4542",        0 },
  152         { 0x414b4d02, 0x00, 1, "AK4543",        0 },
  153         { 0x414b4d06, 0x00, 0, "AK4544A",       0 },
  154         { 0x454b4d07, 0x00, 0, "AK4545",        0 },
  155         { 0x414c4320, 0x0f, 0, "ALC100",        0 },
  156         { 0x414c4730, 0x0f, 0, "ALC101",        0 },
  157         { 0x414c4710, 0x0f, 0, "ALC200",        0 },
  158         { 0x414c4740, 0x0f, 0, "ALC202",        0 },
  159         { 0x414c4720, 0x0f, 0, "ALC650",        0 },
  160         { 0x414c4752, 0x0f, 0, "ALC250",        0 },
  161         { 0x414c4760, 0x0f, 0, "ALC655",        alc655_patch },
  162         { 0x414c4770, 0x0f, 0, "ALC203",        0 },
  163         { 0x414c4780, 0x0f, 0, "ALC658",        0 },
  164         { 0x414c4790, 0x0f, 0, "ALC850",        0 },
  165         { 0x43525900, 0x07, 0, "CS4297",        0 },
  166         { 0x43525910, 0x07, 0, "CS4297A",       0 },
  167         { 0x43525920, 0x07, 0, "CS4294/98",     0 },
  168         { 0x4352592d, 0x07, 0, "CS4294",        0 },
  169         { 0x43525930, 0x07, 0, "CS4299",        0 },
  170         { 0x43525940, 0x07, 0, "CS4201",        0 },
  171         { 0x43525958, 0x07, 0, "CS4205",        0 },
  172         { 0x43525960, 0x07, 0, "CS4291A",       0 },
  173         { 0x434d4961, 0x00, 0, "CMI9739",       cmi9739_patch },
  174         { 0x434d4941, 0x00, 0, "CMI9738",       0 },
  175         { 0x434d4978, 0x00, 0, "CMI9761",       0 },
  176         { 0x434d4982, 0x00, 0, "CMI9761",       0 },
  177         { 0x434d4983, 0x00, 0, "CMI9761",       0 },
  178         { 0x43585421, 0x00, 0, "HSD11246",      0 },
  179         { 0x43585428, 0x07, 0, "CX20468",       0 },
  180         { 0x43585430, 0x00, 0, "CX20468-21",    0 },
  181         { 0x44543000, 0x00, 0, "DT0398",        0 },
  182         { 0x454d4323, 0x00, 0, "EM28023",       0 },
  183         { 0x454d4328, 0x00, 0, "EM28028",       0 },
  184         { 0x45838308, 0x00, 0, "ES1988",        0 }, /* Formerly ES1921(?) */
  185         { 0x48525300, 0x00, 0, "HMP9701",       0 },
  186         { 0x49434501, 0x00, 0, "ICE1230",       0 },
  187         { 0x49434511, 0x00, 0, "ICE1232",       0 },
  188         { 0x49434514, 0x00, 0, "ICE1232A",      0 },
  189         { 0x49434551, 0x03, 0, "VT1616",        0 }, /* Via badged ICE */
  190         { 0x49544520, 0x00, 0, "ITE2226E",      0 },
  191         { 0x49544560, 0x07, 0, "ITE2646E",      0 }, /* XXX: patch needed */
  192         { 0x4e534340, 0x00, 0, "LM4540",        0 }, /* Spec blank on revid */
  193         { 0x4e534343, 0x00, 0, "LM4543",        0 }, /* Ditto */
  194         { 0x4e534346, 0x00, 0, "LM4546A",       0 },
  195         { 0x4e534348, 0x00, 0, "LM4548A",       0 },
  196         { 0x4e534331, 0x00, 0, "LM4549",        0 },
  197         { 0x4e534349, 0x00, 0, "LM4549A",       0 },
  198         { 0x4e534350, 0x00, 0, "LM4550",        0 },
  199         { 0x50534301, 0x00, 0, "UCB1510",       0 },
  200         { 0x50534304, 0x00, 0, "UCB1400",       0 },
  201         { 0x83847600, 0x00, 0, "STAC9700/83/84",        0 },
  202         { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
  203         { 0x83847605, 0x00, 0, "STAC9704",      0 },
  204         { 0x83847608, 0x00, 0, "STAC9708/11",   0 },
  205         { 0x83847609, 0x00, 0, "STAC9721/23",   0 },
  206         { 0x83847644, 0x00, 0, "STAC9744/45",   0 },
  207         { 0x83847650, 0x00, 0, "STAC9750/51",   0 },
  208         { 0x83847652, 0x00, 0, "STAC9752/53",   0 },
  209         { 0x83847656, 0x00, 0, "STAC9756/57",   0 },
  210         { 0x83847658, 0x00, 0, "STAC9758/59",   0 },
  211         { 0x83847660, 0x00, 0, "STAC9760/61",   0 }, /* Extrapolated */
  212         { 0x83847662, 0x00, 0, "STAC9762/63",   0 }, /* Extrapolated */
  213         { 0x83847666, 0x00, 0, "STAC9766/67",   0 },
  214         { 0x53494c22, 0x00, 0, "Si3036",        0 },
  215         { 0x53494c23, 0x00, 0, "Si3038",        0 },
  216         { 0x54524103, 0x00, 0, "TR28023",       0 }, /* Extrapolated */
  217         { 0x54524106, 0x00, 0, "TR28026",       0 },
  218         { 0x54524108, 0x00, 0, "TR28028",       0 },
  219         { 0x54524123, 0x00, 0, "TR28602",       0 },
  220         { 0x54524e03, 0x07, 0, "TLV320AIC27",   0 },
  221         { 0x54584e20, 0x00, 0, "TLC320AD90",    0 },
  222         { 0x56494161, 0x00, 0, "VIA1612A",      0 },
  223         { 0x56494170, 0x00, 0, "VIA1617A",      0 },
  224         { 0x574d4c00, 0x00, 0, "WM9701A",       0 },
  225         { 0x574d4c03, 0x00, 0, "WM9703/4/7/8",  0 },
  226         { 0x574d4c04, 0x00, 0, "WM9704Q",       0 },
  227         { 0x574d4c05, 0x00, 0, "WM9705/10",     0 },
  228         { 0x574d4d09, 0x00, 0, "WM9709",        0 },
  229         { 0x574d4c12, 0x00, 0, "WM9711/12",     0 }, /* XXX: patch needed */
  230         { 0x57454301, 0x00, 0, "W83971D",       0 },
  231         { 0x594d4800, 0x00, 0, "YMF743",        0 },
  232         { 0x594d4802, 0x00, 0, "YMF752",        0 },
  233         { 0x594d4803, 0x00, 0, "YMF753",        0 },
  234         /* 
  235          * XXX This is a fluke, really! The real codec
  236          * should be STAC9704, not this! This should be
  237          * removed someday!
  238          */
  239         { 0x01408384, 0x00, 0, "EV1938",        0 },
  240         { 0, 0, 0, NULL, 0 }
  241 };
  242 
  243 static char *ac97enhancement[] = {
  244         "no 3D Stereo Enhancement",
  245         "Analog Devices Phat Stereo",
  246         "Creative Stereo Enhancement",
  247         "National Semi 3D Stereo Enhancement",
  248         "Yamaha Ymersion",
  249         "BBE 3D Stereo Enhancement",
  250         "Crystal Semi 3D Stereo Enhancement",
  251         "Qsound QXpander",
  252         "Spatializer 3D Stereo Enhancement",
  253         "SRS 3D Stereo Enhancement",
  254         "Platform Tech 3D Stereo Enhancement",
  255         "AKM 3D Audio",
  256         "Aureal Stereo Enhancement",
  257         "Aztech 3D Enhancement",
  258         "Binaura 3D Audio Enhancement",
  259         "ESS Technology Stereo Enhancement",
  260         "Harman International VMAx",
  261         "Nvidea 3D Stereo Enhancement",
  262         "Philips Incredible Sound",
  263         "Texas Instruments 3D Stereo Enhancement",
  264         "VLSI Technology 3D Stereo Enhancement",
  265         "TriTech 3D Stereo Enhancement",
  266         "Realtek 3D Stereo Enhancement",
  267         "Samsung 3D Stereo Enhancement",
  268         "Wolfson Microelectronics 3D Enhancement",
  269         "Delta Integration 3D Enhancement",
  270         "SigmaTel 3D Enhancement",
  271         "Reserved 27",
  272         "Rockwell 3D Stereo Enhancement",
  273         "Reserved 29",
  274         "Reserved 30",
  275         "Reserved 31"
  276 };
  277 
  278 static char *ac97feature[] = {
  279         "mic channel",
  280         "reserved",
  281         "tone",
  282         "simulated stereo",
  283         "headphone",
  284         "bass boost",
  285         "18 bit DAC",
  286         "20 bit DAC",
  287         "18 bit ADC",
  288         "20 bit ADC"
  289 };
  290 
  291 static char *ac97extfeature[] = {
  292         "variable rate PCM",
  293         "double rate PCM",
  294         "reserved 1",
  295         "variable rate mic",
  296         "reserved 2",
  297         "reserved 3",
  298         "center DAC",
  299         "surround DAC",
  300         "LFE DAC",
  301         "AMAP",
  302         "reserved 4",
  303         "reserved 5",
  304         "reserved 6",
  305         "reserved 7",
  306 };
  307 
  308 u_int16_t
  309 ac97_rdcd(struct ac97_info *codec, int reg)
  310 {
  311         if (codec->flags & AC97_F_RDCD_BUG) {
  312                 u_int16_t i[2], j = 100;
  313 
  314                 i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
  315                 i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
  316                 while (i[0] != i[1] && j)
  317                         i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
  318 #if 0
  319                 if (j < 100) {
  320                         device_printf(codec->dev, "%s(): Inconsistent register value at"
  321                                         " 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
  322                 }
  323 #endif
  324                 return i[!(j & 1)];
  325         }
  326         return AC97_READ(codec->methods, codec->devinfo, reg);
  327 }
  328 
  329 void
  330 ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
  331 {
  332         AC97_WRITE(codec->methods, codec->devinfo, reg, val);
  333 }
  334 
  335 static void
  336 ac97_reset(struct ac97_info *codec)
  337 {
  338         u_int32_t i, ps;
  339         ac97_wrcd(codec, AC97_REG_RESET, 0);
  340         for (i = 0; i < 500; i++) {
  341                 ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
  342                 if (ps == AC97_POWER_STATUS)
  343                         return;
  344                 DELAY(1000);
  345         }
  346         device_printf(codec->dev, "AC97 reset timed out.\n");
  347 }
  348 
  349 int
  350 ac97_setrate(struct ac97_info *codec, int which, int rate)
  351 {
  352         u_int16_t v;
  353 
  354         switch(which) {
  355         case AC97_REGEXT_FDACRATE:
  356         case AC97_REGEXT_SDACRATE:
  357         case AC97_REGEXT_LDACRATE:
  358         case AC97_REGEXT_LADCRATE:
  359         case AC97_REGEXT_MADCRATE:
  360                 break;
  361 
  362         default:
  363                 return -1;
  364         }
  365 
  366         snd_mtxlock(codec->lock);
  367         if (rate != 0) {
  368                 v = rate;
  369                 if (codec->extstat & AC97_EXTCAP_DRA)
  370                         v >>= 1;
  371                 ac97_wrcd(codec, which, v);
  372         }
  373         v = ac97_rdcd(codec, which);
  374         if (codec->extstat & AC97_EXTCAP_DRA)
  375                 v <<= 1;
  376         snd_mtxunlock(codec->lock);
  377         return v;
  378 }
  379 
  380 int
  381 ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
  382 {
  383         mode &= AC97_EXTCAPS;
  384         if ((mode & ~codec->extcaps) != 0) {
  385                 device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
  386                               mode);
  387                 return -1;
  388         }
  389         snd_mtxlock(codec->lock);
  390         ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
  391         codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
  392         snd_mtxunlock(codec->lock);
  393         return (mode == codec->extstat)? 0 : -1;
  394 }
  395 
  396 u_int16_t
  397 ac97_getextmode(struct ac97_info *codec)
  398 {
  399         return codec->extstat;
  400 }
  401 
  402 u_int16_t
  403 ac97_getextcaps(struct ac97_info *codec)
  404 {
  405         return codec->extcaps;
  406 }
  407 
  408 u_int16_t
  409 ac97_getcaps(struct ac97_info *codec)
  410 {
  411         return codec->caps;
  412 }
  413 
  414 u_int32_t
  415 ac97_getsubvendor(struct ac97_info *codec)
  416 {
  417         return codec->subvendor;
  418 }
  419 
  420 static int
  421 ac97_setrecsrc(struct ac97_info *codec, int channel)
  422 {
  423         struct ac97mixtable_entry *e = &codec->mix[channel];
  424 
  425         if (e->recidx > 0) {
  426                 int val = e->recidx - 1;
  427                 val |= val << 8;
  428                 snd_mtxlock(codec->lock);
  429                 ac97_wrcd(codec, AC97_REG_RECSEL, val);
  430                 snd_mtxunlock(codec->lock);
  431                 return 0;
  432         } else
  433                 return -1;
  434 }
  435 
  436 static int
  437 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
  438 {
  439         struct ac97mixtable_entry *e = &codec->mix[channel];
  440 
  441         if (e->reg && e->enable && e->bits) {
  442                 int mask, max, val, reg;
  443 
  444                 reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register    */
  445                 max = (1 << e->bits) - 1;               /* actual range     */
  446                 mask = (max << 8) | max;                /* bits of interest */
  447 
  448                 if (!e->stereo)
  449                         right = left;
  450 
  451                 /*
  452                  * Invert the range if the polarity requires so,
  453                  * then scale to 0..max-1 to compute the value to
  454                  * write into the codec, and scale back to 0..100
  455                  * for the return value.
  456                  */
  457                 if (e->reg > 0) {
  458                         left = 100 - left;
  459                         right = 100 - right;
  460                 }
  461 
  462                 left = (left * max) / 100;
  463                 right = (right * max) / 100;
  464 
  465                 val = (left << 8) | right;
  466 
  467                 left = (left * 100) / max;
  468                 right = (right * 100) / max;
  469 
  470                 if (e->reg > 0) {
  471                         left = 100 - left;
  472                         right = 100 - right;
  473                 }
  474 
  475                 /*
  476                  * For mono controls, trim val and mask, also taking
  477                  * care of e->ofs (offset of control field).
  478                  */
  479                 if (e->ofs) {
  480                         val &= max;
  481                         val <<= e->ofs;
  482                         mask = (max << e->ofs);
  483                 }
  484 
  485                 /*
  486                  * If we have a mute bit, add it to the mask and
  487                  * update val and set mute if both channels require a
  488                  * zero volume.
  489                  */
  490                 if (e->mute == 1) {
  491                         mask |= AC97_MUTE;
  492                         if (left == 0 && right == 0)
  493                                 val = AC97_MUTE;
  494                 }
  495 
  496                 /*
  497                  * If the mask bit is set, do not alter the other bits.
  498                  */
  499                 snd_mtxlock(codec->lock);
  500                 if (e->mask) {
  501                         int cur = ac97_rdcd(codec, reg);
  502                         val |= cur & ~(mask);
  503                 }
  504                 ac97_wrcd(codec, reg, val);
  505                 snd_mtxunlock(codec->lock);
  506                 return left | (right << 8);
  507         } else {
  508 #if 0
  509                 printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
  510 #endif
  511                 return -1;
  512         }
  513 }
  514 
  515 static void
  516 ac97_fix_auxout(struct ac97_info *codec)
  517 {
  518         int keep_ogain;
  519 
  520         /*
  521          * By default, The ac97 aux_out register (0x04) corresponds to OSS's
  522          * OGAIN setting.
  523          *
  524          * We first check whether aux_out is a valid register.  If not
  525          * we may not want to keep ogain.
  526          */
  527         keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
  528 
  529         /*
  530          * Determine what AUX_OUT really means, it can be:
  531          *
  532          * 1. Headphone out.
  533          * 2. 4-Channel Out
  534          * 3. True line level out (effectively master volume).
  535          *
  536          * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
  537          */
  538         if (codec->extcaps & AC97_EXTCAP_SDAC &&
  539             ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
  540                 codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
  541                 keep_ogain = 1;
  542         }
  543 
  544         if (keep_ogain == 0) {
  545                 bzero(&codec->mix[SOUND_MIXER_OGAIN],
  546                       sizeof(codec->mix[SOUND_MIXER_OGAIN]));
  547         }
  548 }
  549 
  550 static void
  551 ac97_fix_tone(struct ac97_info *codec)
  552 {
  553         /*
  554          * YMF chips does not indicate tone and 3D enhancement capability
  555          * in the AC97_REG_RESET register.
  556          */
  557         switch (codec->id) {
  558         case 0x594d4800:        /* YMF743 */
  559         case 0x594d4803:        /* YMF753 */
  560                 codec->caps |= AC97_CAP_TONE;
  561                 codec->se |= 0x04;
  562                 break;
  563         case 0x594d4802:        /* YMF752 */
  564                 codec->se |= 0x04;
  565                 break;
  566         default:
  567                 break;
  568         }
  569 
  570         /* Hide treble and bass if they don't exist */
  571         if ((codec->caps & AC97_CAP_TONE) == 0) {
  572                 bzero(&codec->mix[SOUND_MIXER_BASS],
  573                       sizeof(codec->mix[SOUND_MIXER_BASS]));
  574                 bzero(&codec->mix[SOUND_MIXER_TREBLE],
  575                       sizeof(codec->mix[SOUND_MIXER_TREBLE]));
  576         }
  577 }
  578 
  579 static const char*
  580 ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
  581 {
  582         if (cname == NULL) {
  583                 sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
  584                 return buf;
  585         }
  586 
  587         if (vname == NULL) vname = "Unknown";
  588 
  589         if (bootverbose) {
  590                 sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
  591         } else {
  592                 sprintf(buf, "%s %s AC97 Codec", vname, cname);
  593         }
  594         return buf;
  595 }
  596 
  597 static unsigned
  598 ac97_initmixer(struct ac97_info *codec)
  599 {
  600         ac97_patch codec_patch;
  601         const char *cname, *vname;
  602         char desc[80];
  603         u_int8_t model, step;
  604         unsigned i, j, k, bit, old;
  605         u_int32_t id;
  606         int reg;
  607 
  608         snd_mtxlock(codec->lock);
  609         codec->count = AC97_INIT(codec->methods, codec->devinfo);
  610         if (codec->count == 0) {
  611                 device_printf(codec->dev, "ac97 codec init failed\n");
  612                 snd_mtxunlock(codec->lock);
  613                 return ENODEV;
  614         }
  615 
  616         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
  617         ac97_reset(codec);
  618         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
  619 
  620         i = ac97_rdcd(codec, AC97_REG_RESET);
  621         j = ac97_rdcd(codec, AC97_REG_RESET);
  622         k = ac97_rdcd(codec, AC97_REG_RESET);
  623         /*
  624          * Let see if this codec can return consistent value.
  625          * If not, turn on aggressive read workaround
  626          * (STAC9704 comes in mind).
  627          */
  628         if (i != j || j != k) {
  629                 codec->flags |= AC97_F_RDCD_BUG;
  630                 i = ac97_rdcd(codec, AC97_REG_RESET);
  631         }
  632         codec->caps = i & 0x03ff;
  633         codec->se =  (i & 0x7c00) >> 10;
  634 
  635         id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
  636         if (id == 0 || id == 0xffffffff) {
  637                 device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
  638                 snd_mtxunlock(codec->lock);
  639                 return ENODEV;
  640         }
  641 
  642         codec->id = id;
  643         codec->subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16;
  644         codec->subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) &
  645             0x0000ffff;
  646         codec->noext = 0;
  647         codec_patch = NULL;
  648 
  649         cname = NULL;
  650         model = step = 0;
  651         for (i = 0; ac97codecid[i].id; i++) {
  652                 u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
  653                 if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
  654                         codec->noext = ac97codecid[i].noext;
  655                         codec_patch = ac97codecid[i].patch;
  656                         cname = ac97codecid[i].name;
  657                         model = (id & modelmask) & 0xff;
  658                         step = (id & ~modelmask) & 0xff;
  659                         break;
  660                 }
  661         }
  662 
  663         vname = NULL;
  664         for (i = 0; ac97vendorid[i].id; i++) {
  665                 if (ac97vendorid[i].id == (id & 0xffffff00)) {
  666                         vname = ac97vendorid[i].name;
  667                         break;
  668                 }
  669         }
  670 
  671         codec->extcaps = 0;
  672         codec->extid = 0;
  673         codec->extstat = 0;
  674         if (!codec->noext) {
  675                 i = ac97_rdcd(codec, AC97_REGEXT_ID);
  676                 if (i != 0xffff) {
  677                         codec->extcaps = i & 0x3fff;
  678                         codec->extid =  (i & 0xc000) >> 14;
  679                         codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
  680                 }
  681         }
  682 
  683         for (i = 0; i < AC97_MIXER_SIZE; i++) {
  684                 codec->mix[i] = ac97mixtable_default[i];
  685         }
  686         ac97_fix_auxout(codec);
  687         ac97_fix_tone(codec);
  688         if (codec_patch)
  689                 codec_patch(codec);
  690 
  691         for (i = 0; i < AC97_MIXER_SIZE; i++) {
  692                 k = codec->noext? codec->mix[i].enable : 1;
  693                 reg = codec->mix[i].reg;
  694                 if (reg < 0)
  695                         reg = -reg;
  696                 if (k && reg) {
  697                         j = old = ac97_rdcd(codec, reg);
  698                         /*
  699                          * Test for mute bit (except for AC97_MIX_TONE,
  700                          * where we simply assume it as available).
  701                          */
  702                         if (codec->mix[i].mute) {
  703                                 ac97_wrcd(codec, reg, j | 0x8000);
  704                                 j = ac97_rdcd(codec, reg);
  705                         } else
  706                                 j |= 0x8000;
  707                         if ((j & 0x8000)) {
  708                                 /*
  709                                  * Test whether the control width should be
  710                                  * 4, 5 or 6 bit. For 5bit register, we should
  711                                  * test it whether it's really 5 or 6bit. Leave
  712                                  * 4bit register alone, because sometimes an
  713                                  * attempt to write past 4th bit may cause
  714                                  * incorrect result especially for AC97_MIX_BEEP
  715                                  * (ac97 2.3).
  716                                  */
  717                                 bit = codec->mix[i].bits;
  718                                 if (bit == 5)
  719                                         bit++;
  720                                 j = ((1 << bit) - 1) << codec->mix[i].ofs;
  721                                 ac97_wrcd(codec, reg,
  722                                         j | (codec->mix[i].mute ? 0x8000 : 0));
  723                                 k = ac97_rdcd(codec, reg) & j;
  724                                 k >>= codec->mix[i].ofs;
  725                                 if (reg == AC97_MIX_TONE &&
  726                                                         ((k & 0x0001) == 0x0000))
  727                                         k >>= 1;
  728                                 for (j = 0; k >> j; j++)
  729                                         ;
  730                                 if (j != 0) {
  731 #if 0
  732                                         device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n",
  733                                                 i, k, bit, codec->mix[i].bits, j);
  734 #endif
  735                                         codec->mix[i].enable = 1;
  736                                         codec->mix[i].bits = j;
  737                                 } else if (reg == AC97_MIX_BEEP) {
  738                                         /*
  739                                          * Few codec such as CX20468-21 does
  740                                          * have this control register, although
  741                                          * the only usable part is the mute bit.
  742                                          */
  743                                         codec->mix[i].enable = 1;
  744                                 } else
  745                                         codec->mix[i].enable = 0;
  746                         } else
  747                                 codec->mix[i].enable = 0;
  748                         ac97_wrcd(codec, reg, old);
  749                 }
  750 #if 0
  751                 printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits);
  752 #endif
  753         }
  754 
  755         device_printf(codec->dev, "<%s>\n",
  756                       ac97_hw_desc(codec->id, vname, cname, desc));
  757 
  758         if (bootverbose) {
  759                 if (codec->flags & AC97_F_RDCD_BUG)
  760                         device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n");
  761                 device_printf(codec->dev, "Codec features ");
  762                 for (i = j = 0; i < 10; i++)
  763                         if (codec->caps & (1 << i))
  764                                 printf("%s%s", j++? ", " : "", ac97feature[i]);
  765                 printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
  766                 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
  767 
  768                 if (codec->extcaps != 0 || codec->extid) {
  769                         device_printf(codec->dev, "%s codec",
  770                                       codec->extid? "Secondary" : "Primary");
  771                         if (codec->extcaps)
  772                                 printf(" extended features ");
  773                         for (i = j = 0; i < 14; i++)
  774                                 if (codec->extcaps & (1 << i))
  775                                         printf("%s%s", j++? ", " : "", ac97extfeature[i]);
  776                         printf("\n");
  777                 }
  778         }
  779 
  780         i = 0;
  781         while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) {
  782                 if (++i == 100) {
  783                         device_printf(codec->dev, "ac97 codec reports dac not ready\n");
  784                         break;
  785                 }
  786                 DELAY(1000);
  787         }
  788         if (bootverbose)
  789                 device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i);
  790         snd_mtxunlock(codec->lock);
  791         return 0;
  792 }
  793 
  794 static unsigned
  795 ac97_reinitmixer(struct ac97_info *codec)
  796 {
  797         snd_mtxlock(codec->lock);
  798         codec->count = AC97_INIT(codec->methods, codec->devinfo);
  799         if (codec->count == 0) {
  800                 device_printf(codec->dev, "ac97 codec init failed\n");
  801                 snd_mtxunlock(codec->lock);
  802                 return ENODEV;
  803         }
  804 
  805         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
  806         ac97_reset(codec);
  807         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
  808 
  809         if (!codec->noext) {
  810                 ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
  811                 if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
  812                     != codec->extstat)
  813                         device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
  814                                       codec->extstat,
  815                                       ac97_rdcd(codec, AC97_REGEXT_STAT) &
  816                                       AC97_EXTCAPS);
  817         }
  818 
  819         if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
  820                 device_printf(codec->dev, "ac97 codec reports dac not ready\n");
  821         snd_mtxunlock(codec->lock);
  822         return 0;
  823 }
  824 
  825 struct ac97_info *
  826 ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
  827 {
  828         struct ac97_info *codec;
  829         int i;
  830 
  831         codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO);
  832         snprintf(codec->name, sizeof(codec->name), "%s:ac97",
  833             device_get_nameunit(dev));
  834         codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
  835         codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO);
  836         codec->dev = dev;
  837         codec->devinfo = devinfo;
  838         codec->flags = 0;
  839 
  840         if (resource_int_value(device_get_name(dev), device_get_unit(dev),
  841             "eapdinv", &i) == 0 && i != 0)
  842                 codec->flags |= AC97_F_EAPD_INV;
  843 
  844         if (resource_int_value(device_get_name(dev), device_get_unit(dev),
  845             "softpcmvol", &i) == 0 && i != 0)
  846                 pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL);
  847 
  848         return codec;
  849 }
  850 
  851 void
  852 ac97_destroy(struct ac97_info *codec)
  853 {
  854         snd_mtxlock(codec->lock);
  855         if (codec->methods != NULL)
  856                 kobj_delete(codec->methods, M_AC97);
  857         snd_mtxfree(codec->lock);
  858         free(codec, M_AC97);
  859 }
  860 
  861 void
  862 ac97_setflags(struct ac97_info *codec, u_int32_t val)
  863 {
  864         codec->flags = val;
  865 }
  866 
  867 u_int32_t
  868 ac97_getflags(struct ac97_info *codec)
  869 {
  870         return codec->flags;
  871 }
  872 
  873 /* -------------------------------------------------------------------- */
  874 
  875 static int
  876 sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)
  877 {
  878         struct ac97_info *codec;
  879         int ea, inv, err = 0;
  880         u_int16_t val;
  881 
  882         codec = oidp->oid_arg1;
  883         if (codec == NULL || codec->id == 0 || codec->lock == NULL)
  884                 return EINVAL;
  885         snd_mtxlock(codec->lock);
  886         val = ac97_rdcd(codec, AC97_REG_POWER);
  887         inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1;
  888         ea = (val >> 15) ^ inv;
  889         snd_mtxunlock(codec->lock);
  890         err = sysctl_handle_int(oidp, &ea, 0, req);
  891         if (err == 0 && req->newptr != NULL) {
  892                 if (ea != 0 && ea != 1)
  893                         return EINVAL;
  894                 if (ea != ((val >> 15) ^ inv)) {
  895                         snd_mtxlock(codec->lock);
  896                         ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000);
  897                         snd_mtxunlock(codec->lock);
  898                 }
  899         }
  900         return err;
  901 }
  902 
  903 static void
  904 ac97_init_sysctl(struct ac97_info *codec)
  905 {
  906         u_int16_t orig, val;
  907 
  908         if (codec == NULL || codec->dev == NULL)
  909                 return;
  910         snd_mtxlock(codec->lock);
  911         orig = ac97_rdcd(codec, AC97_REG_POWER);
  912         ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000);
  913         val = ac97_rdcd(codec, AC97_REG_POWER);
  914         ac97_wrcd(codec, AC97_REG_POWER, orig);
  915         snd_mtxunlock(codec->lock);
  916         if ((val & 0x8000) == (orig & 0x8000))
  917                 return;
  918         SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev),
  919             SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)),
  920             OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW,
  921             codec, sizeof(codec), sysctl_hw_snd_ac97_eapd,
  922             "I", "AC97 External Amplifier");
  923 }
  924 
  925 static int
  926 ac97mix_init(struct snd_mixer *m)
  927 {
  928         struct ac97_info *codec = mix_getdevinfo(m);
  929         u_int32_t i, mask;
  930 
  931         if (codec == NULL)
  932                 return -1;
  933 
  934         if (ac97_initmixer(codec))
  935                 return -1;
  936 
  937         switch (codec->id) {
  938         case 0x41445374:        /* AD1981B */
  939                 switch (codec->subvendor) {
  940                 case 0x02d91014:
  941                         /*
  942                          * IBM Thinkcentre:
  943                          *
  944                          * Tie "ogain" and "phout" to "vol" since its
  945                          * master volume is basically useless and can't
  946                          * control anything.
  947                          */
  948                         mask = 0;
  949                         if (codec->mix[SOUND_MIXER_OGAIN].enable)
  950                                 mask |= SOUND_MASK_OGAIN;
  951                         if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
  952                                 mask |= SOUND_MASK_PHONEOUT;
  953                         if (codec->mix[SOUND_MIXER_VOLUME].enable)
  954                                 mix_setparentchild(m, SOUND_MIXER_VOLUME,
  955                                     mask);
  956                         else {
  957                                 mix_setparentchild(m, SOUND_MIXER_VOLUME,
  958                                     mask);
  959                                 mix_setrealdev(m, SOUND_MIXER_VOLUME,
  960                                     SOUND_MIXER_NONE);
  961                         }
  962                         break;
  963                 case 0x099c103c:
  964                         /*
  965                          * HP nx6110:
  966                          *
  967                          * By default, "vol" is controlling internal speakers
  968                          * (not a master volume!) and "ogain" is controlling
  969                          * headphone. Enable dummy "phout" so it can be
  970                          * remapped to internal speakers and virtualize
  971                          * "vol" to control both.
  972                          */
  973                         codec->mix[SOUND_MIXER_OGAIN].enable = 1;
  974                         codec->mix[SOUND_MIXER_PHONEOUT].enable = 1;
  975                         mix_setrealdev(m, SOUND_MIXER_PHONEOUT,
  976                             SOUND_MIXER_VOLUME);
  977                         mix_setrealdev(m, SOUND_MIXER_VOLUME,
  978                             SOUND_MIXER_NONE);
  979                         mix_setparentchild(m, SOUND_MIXER_VOLUME,
  980                             SOUND_MASK_OGAIN | SOUND_MASK_PHONEOUT);
  981                         break;
  982                 default:
  983                         break;
  984                 }
  985                 break;
  986         case 0x434d4941:        /* CMI9738 */
  987         case 0x434d4961:        /* CMI9739 */
  988         case 0x434d4978:        /* CMI9761 */
  989         case 0x434d4982:        /* CMI9761 */
  990         case 0x434d4983:        /* CMI9761 */
  991                 bzero(&codec->mix[SOUND_MIXER_PCM],
  992                     sizeof(codec->mix[SOUND_MIXER_PCM]));
  993                 pcm_setflags(codec->dev, pcm_getflags(codec->dev) |
  994                     SD_F_SOFTPCMVOL);
  995                 /* XXX How about master volume ? */
  996                 break;
  997         default:
  998                 break;
  999         }
 1000 
 1001         if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL)
 1002                 ac97_wrcd(codec, AC97_MIX_PCM, 0);
 1003 #if 0
 1004         /* XXX For the sake of debugging purposes */
 1005         mix_setparentchild(m, SOUND_MIXER_VOLUME,
 1006             SOUND_MASK_PCM | SOUND_MASK_CD);
 1007         mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
 1008         ac97_wrcd(codec, AC97_MIX_MASTER, 0);
 1009 #endif
 1010 
 1011         mask = 0;
 1012         for (i = 0; i < AC97_MIXER_SIZE; i++)
 1013                 mask |= codec->mix[i].enable? 1 << i : 0;
 1014         mix_setdevs(m, mask);
 1015 
 1016         mask = 0;
 1017         for (i = 0; i < AC97_MIXER_SIZE; i++)
 1018                 mask |= codec->mix[i].recidx? 1 << i : 0;
 1019         mix_setrecdevs(m, mask);
 1020 
 1021         ac97_init_sysctl(codec);
 1022 
 1023         return 0;
 1024 }
 1025 
 1026 static int
 1027 ac97mix_uninit(struct snd_mixer *m)
 1028 {
 1029         struct ac97_info *codec = mix_getdevinfo(m);
 1030 
 1031         if (codec == NULL)
 1032                 return -1;
 1033         /*
 1034         if (ac97_uninitmixer(codec))
 1035                 return -1;
 1036         */
 1037         ac97_destroy(codec);
 1038         return 0;
 1039 }
 1040 
 1041 static int
 1042 ac97mix_reinit(struct snd_mixer *m)
 1043 {
 1044         struct ac97_info *codec = mix_getdevinfo(m);
 1045 
 1046         if (codec == NULL)
 1047                 return -1;
 1048         return ac97_reinitmixer(codec);
 1049 }
 1050 
 1051 static int
 1052 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
 1053 {
 1054         struct ac97_info *codec = mix_getdevinfo(m);
 1055 
 1056         if (codec == NULL || dev >= AC97_MIXER_SIZE)
 1057                 return -1;
 1058         return ac97_setmixer(codec, dev, left, right);
 1059 }
 1060 
 1061 static u_int32_t
 1062 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
 1063 {
 1064         int i;
 1065         struct ac97_info *codec = mix_getdevinfo(m);
 1066 
 1067         if (codec == NULL)
 1068                 return -1;
 1069         for (i = 0; i < AC97_MIXER_SIZE; i++)
 1070                 if ((src & (1 << i)) != 0)
 1071                         break;
 1072         return (ac97_setrecsrc(codec, i) == 0)? 1U << i : 0xffffffffU;
 1073 }
 1074 
 1075 static kobj_method_t ac97mixer_methods[] = {
 1076         KOBJMETHOD(mixer_init,          ac97mix_init),
 1077         KOBJMETHOD(mixer_uninit,        ac97mix_uninit),
 1078         KOBJMETHOD(mixer_reinit,        ac97mix_reinit),
 1079         KOBJMETHOD(mixer_set,           ac97mix_set),
 1080         KOBJMETHOD(mixer_setrecsrc,     ac97mix_setrecsrc),
 1081         KOBJMETHOD_END
 1082 };
 1083 MIXER_DECLARE(ac97mixer);
 1084 
 1085 /* -------------------------------------------------------------------- */
 1086 
 1087 kobj_class_t
 1088 ac97_getmixerclass(void)
 1089 {
 1090         return &ac97mixer_class;
 1091 }

Cache object: 7fdfc43ab917ac4303f94a09cbdd7446


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