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-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

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

Cache object: 9a662eca73f9b193461761aff311c159


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