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  * 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 #include <dev/sound/pcm/sound.h>
   28 #include <dev/sound/pcm/ac97.h>
   29 #include <dev/sound/pcm/ac97_patch.h>
   30 
   31 #include "mixer_if.h"
   32 
   33 SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/ac97.c,v 1.51.2.2 2005/05/01 14:31:06 scottl Exp $");
   34 
   35 MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
   36 
   37 struct ac97mixtable_entry {
   38         int      reg:8;         /* register index               */
   39                                 /* reg < 0 if inverted polarity */
   40         unsigned bits:4;        /* width of control field       */
   41         unsigned ofs:4;         /* offset (only if stereo=0)    */
   42         unsigned stereo:1;      /* set for stereo controls      */
   43         unsigned mute:1;        /* bit15 is MUTE                */
   44         unsigned recidx:4;      /* index in rec mux             */
   45         unsigned mask:1;        /* use only masked bits         */
   46         unsigned enable:1;      /* entry is enabled             */
   47 };
   48 
   49 #define AC97_NAMELEN    16
   50 struct ac97_info {
   51         kobj_t methods;
   52         device_t dev;
   53         void *devinfo;
   54         u_int32_t id;
   55         unsigned count, caps, se, extcaps, extid, extstat, noext:1;
   56         u_int32_t flags;
   57         struct ac97mixtable_entry mix[32];
   58         char name[AC97_NAMELEN];
   59         struct mtx *lock;
   60 };
   61 
   62 struct ac97_vendorid {
   63         u_int32_t   id;
   64         const char *name;
   65 };
   66 
   67 struct ac97_codecid {
   68         u_int32_t  id;
   69         u_int8_t   stepmask;
   70         u_int8_t   noext:1;
   71         char      *name;
   72         ac97_patch patch;
   73 };
   74 
   75 static const struct ac97mixtable_entry ac97mixtable_default[32] = {
   76     /*  [offset]                        reg          bits of st mu re mk en */
   77         [SOUND_MIXER_VOLUME]    = { AC97_MIX_MASTER,    5, 0, 1, 1, 6, 0, 1 },
   78         [SOUND_MIXER_OGAIN]     = { AC97_MIX_AUXOUT,    5, 0, 1, 1, 0, 0, 0 },
   79         [SOUND_MIXER_PHONEOUT]  = { AC97_MIX_MONO,      5, 0, 0, 1, 7, 0, 0 },
   80         [SOUND_MIXER_BASS]      = { AC97_MIX_TONE,      4, 8, 0, 0, 0, 1, 0 },
   81         [SOUND_MIXER_TREBLE]    = { AC97_MIX_TONE,      4, 0, 0, 0, 0, 1, 0 },
   82         [SOUND_MIXER_PCM]       = { AC97_MIX_PCM,       5, 0, 1, 1, 0, 0, 1 },
   83         [SOUND_MIXER_SPEAKER]   = { AC97_MIX_BEEP,      4, 1, 0, 1, 0, 0, 0 },
   84         [SOUND_MIXER_LINE]      = { AC97_MIX_LINE,      5, 0, 1, 1, 5, 0, 1 },
   85         [SOUND_MIXER_PHONEIN]   = { AC97_MIX_PHONE,     5, 0, 0, 1, 8, 0, 0 },
   86         [SOUND_MIXER_MIC]       = { AC97_MIX_MIC,       5, 0, 0, 1, 1, 1, 1 },
   87 #if 0
   88         /* use igain for the mic 20dB boost */
   89         [SOUND_MIXER_IGAIN]     = { -AC97_MIX_MIC,      1, 6, 0, 0, 0, 1, 1 },
   90 #endif
   91         [SOUND_MIXER_CD]        = { AC97_MIX_CD,        5, 0, 1, 1, 2, 0, 1 },
   92         [SOUND_MIXER_LINE1]     = { AC97_MIX_AUX,       5, 0, 1, 1, 4, 0, 0 },
   93         [SOUND_MIXER_VIDEO]     = { AC97_MIX_VIDEO,     5, 0, 1, 1, 3, 0, 0 },
   94         [SOUND_MIXER_RECLEV]    = { -AC97_MIX_RGAIN,    4, 0, 1, 1, 0, 0, 1 }
   95 };
   96 
   97 static const struct ac97_vendorid ac97vendorid[] = {
   98         { 0x41445300, "Analog Devices" },
   99         { 0x414b4d00, "Asahi Kasei" },
  100         { 0x414c4300, "Realtek" },
  101         { 0x414c4700, "Avance Logic" },
  102         { 0x43525900, "Cirrus Logic" },
  103         { 0x434d4900, "C-Media Electronics" },
  104         { 0x43585400, "Conexant" },
  105         { 0x44543000, "Diamond Technology" },
  106         { 0x454d4300, "eMicro" },
  107         { 0x45838300, "ESS Technology" },
  108         { 0x48525300, "Intersil" },
  109         { 0x49434500, "ICEnsemble" },
  110         { 0x49544500, "ITE, Inc." },
  111         { 0x4e534300, "National Semiconductor" },
  112         { 0x50534300, "Philips Semiconductor" },
  113         { 0x83847600, "SigmaTel" },
  114         { 0x53494c00, "Silicon Laboratories" },
  115         { 0x54524100, "TriTech" },
  116         { 0x54584e00, "Texas Instruments" },
  117         { 0x56494100, "VIA Technologies" },
  118         { 0x57454300, "Winbond" },
  119         { 0x574d4c00, "Wolfson" },
  120         { 0x594d4800, "Yamaha" },
  121         { 0x01408300, "Creative" },
  122         { 0x00000000, NULL }
  123 };
  124 
  125 static struct ac97_codecid ac97codecid[] = {
  126         { 0x41445303, 0x00, 0, "AD1819",        0 },
  127         { 0x41445340, 0x00, 0, "AD1881",        0 },
  128         { 0x41445348, 0x00, 0, "AD1881A",       0 },
  129         { 0x41445360, 0x00, 0, "AD1885",        0 },
  130         { 0x41445361, 0x00, 0, "AD1886",        ad1886_patch },
  131         { 0x41445362, 0x00, 0, "AD1887",        0 },
  132         { 0x41445363, 0x00, 0, "AD1886A",       0 },
  133         { 0x41445368, 0x00, 0, "AD1888",        ad198x_patch },
  134         { 0x41445370, 0x00, 0, "AD1980",        ad198x_patch },
  135         { 0x41445372, 0x00, 0, "AD1981A",       0 },
  136         { 0x41445374, 0x00, 0, "AD1981B",       0 },
  137         { 0x41445375, 0x00, 0, "AD1985",        ad198x_patch },
  138         { 0x414b4d00, 0x00, 1, "AK4540",        0 },
  139         { 0x414b4d01, 0x00, 1, "AK4542",        0 },
  140         { 0x414b4d02, 0x00, 1, "AK4543",        0 },
  141         { 0x414b4d06, 0x00, 0, "AK4544A",       0 },
  142         { 0x454b4d07, 0x00, 0, "AK4545",        0 },
  143         { 0x414c4320, 0x0f, 0, "ALC100",        0 },
  144         { 0x414c4730, 0x0f, 0, "ALC101",        0 },
  145         { 0x414c4710, 0x0f, 0, "ALC200",        0 },
  146         { 0x414c4740, 0x0f, 0, "ALC202",        0 },
  147         { 0x414c4720, 0x0f, 0, "ALC650",        0 },
  148         { 0x414c4760, 0x0f, 0, "ALC655",        0 },
  149         { 0x414c4780, 0x0f, 0, "ALC658",        0 },
  150         { 0x414c4790, 0x0f, 0, "ALC850",        0 },
  151         { 0x43525900, 0x07, 0, "CS4297",        0 },
  152         { 0x43525910, 0x07, 0, "CS4297A",       0 },
  153         { 0x43525920, 0x07, 0, "CS4294/98",     0 },
  154         { 0x4352592d, 0x07, 0, "CS4294",        0 },
  155         { 0x43525930, 0x07, 0, "CS4299",        0 },
  156         { 0x43525940, 0x07, 0, "CS4201",        0 },
  157         { 0x43525958, 0x07, 0, "CS4205",        0 },
  158         { 0x43525960, 0x07, 0, "CS4291A",       0 },
  159         { 0x434d4961, 0x00, 0, "CMI9739",       0 },
  160         { 0x434d4941, 0x00, 0, "CMI9738",       0 },
  161         { 0x43585421, 0x00, 0, "HSD11246",      0 },
  162         { 0x43585428, 0x07, 0, "CX20468",       0 },
  163         { 0x44543000, 0x00, 0, "DT0398",        0 },
  164         { 0x454d4323, 0x00, 0, "EM28023",       0 },
  165         { 0x454d4328, 0x00, 0, "EM28028",       0 },
  166         { 0x45838308, 0x00, 0, "ES1988",        0 }, /* Formerly ES1921(?) */
  167         { 0x48525300, 0x00, 0, "HMP9701",       0 },
  168         { 0x49434501, 0x00, 0, "ICE1230",       0 },
  169         { 0x49434511, 0x00, 0, "ICE1232",       0 },
  170         { 0x49434514, 0x00, 0, "ICE1232A",      0 },
  171         { 0x49434551, 0x03, 0, "VT1616",        0 }, /* Via badged ICE */
  172         { 0x49544520, 0x00, 0, "ITE2226E",      0 },
  173         { 0x49544560, 0x07, 0, "ITE2646E",      0 }, /* XXX: patch needed */
  174         { 0x4e534340, 0x00, 0, "LM4540",        0 }, /* Spec blank on revid */
  175         { 0x4e534343, 0x00, 0, "LM4543",        0 }, /* Ditto */
  176         { 0x4e534346, 0x00, 0, "LM4546A",       0 },
  177         { 0x4e534348, 0x00, 0, "LM4548A",       0 },
  178         { 0x4e534331, 0x00, 0, "LM4549",        0 },
  179         { 0x4e534349, 0x00, 0, "LM4549A",       0 },
  180         { 0x4e534350, 0x00, 0, "LM4550",        0 },
  181         { 0x50534301, 0x00, 0, "UCB1510",       0 },
  182         { 0x50534304, 0x00, 0, "UCB1400",       0 },
  183         { 0x83847600, 0x00, 0, "STAC9700/83/84",        0 },
  184         { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
  185         { 0x83847605, 0x00, 0, "STAC9704",      0 },
  186         { 0x83847608, 0x00, 0, "STAC9708/11",   0 },
  187         { 0x83847609, 0x00, 0, "STAC9721/23",   0 },
  188         { 0x83847644, 0x00, 0, "STAC9744/45",   0 },
  189         { 0x83847650, 0x00, 0, "STAC9750/51",   0 },
  190         { 0x83847652, 0x00, 0, "STAC9752/53",   0 },
  191         { 0x83847656, 0x00, 0, "STAC9756/57",   0 },
  192         { 0x83847658, 0x00, 0, "STAC9758/59",   0 },
  193         { 0x83847660, 0x00, 0, "STAC9760/61",   0 }, /* Extrapolated */
  194         { 0x83847662, 0x00, 0, "STAC9762/63",   0 }, /* Extrapolated */
  195         { 0x53494c22, 0x00, 0, "Si3036",        0 },
  196         { 0x53494c23, 0x00, 0, "Si3038",        0 },
  197         { 0x54524103, 0x00, 0, "TR28023",       0 }, /* Extrapolated */
  198         { 0x54524106, 0x00, 0, "TR28026",       0 },
  199         { 0x54524108, 0x00, 0, "TR28028",       0 },
  200         { 0x54524123, 0x00, 0, "TR28602",       0 },
  201         { 0x54524e03, 0x07, 0, "TLV320AIC27",   0 },
  202         { 0x54584e20, 0x00, 0, "TLC320AD90",    0 },
  203         { 0x56494161, 0x00, 0, "VIA1612A",      0 },
  204         { 0x574d4c00, 0x00, 0, "WM9701A",       0 },
  205         { 0x574d4c03, 0x00, 0, "WM9703/4/7/8",  0 },
  206         { 0x574d4c04, 0x00, 0, "WM9704Q",       0 },
  207         { 0x574d4c05, 0x00, 0, "WM9705/10",     0 },
  208         { 0x574d4d09, 0x00, 0, "WM9709",        0 },
  209         { 0x574d4c12, 0x00, 0, "WM9711/12",     0 }, /* XXX: patch needed */
  210         { 0x57454301, 0x00, 0, "W83971D",       0 },
  211         { 0x594d4800, 0x00, 0, "YMF743",        0 },
  212         { 0x594d4802, 0x00, 0, "YMF752",        0 },
  213         { 0x594d4803, 0x00, 0, "YMF753",        0 },
  214         { 0x01408384, 0x00, 0, "EV1938",        0 },
  215         { 0, 0, 0, NULL, 0 }
  216 };
  217 
  218 static char *ac97enhancement[] = {
  219         "no 3D Stereo Enhancement",
  220         "Analog Devices Phat Stereo",
  221         "Creative Stereo Enhancement",
  222         "National Semi 3D Stereo Enhancement",
  223         "Yamaha Ymersion",
  224         "BBE 3D Stereo Enhancement",
  225         "Crystal Semi 3D Stereo Enhancement",
  226         "Qsound QXpander",
  227         "Spatializer 3D Stereo Enhancement",
  228         "SRS 3D Stereo Enhancement",
  229         "Platform Tech 3D Stereo Enhancement",
  230         "AKM 3D Audio",
  231         "Aureal Stereo Enhancement",
  232         "Aztech 3D Enhancement",
  233         "Binaura 3D Audio Enhancement",
  234         "ESS Technology Stereo Enhancement",
  235         "Harman International VMAx",
  236         "Nvidea 3D Stereo Enhancement",
  237         "Philips Incredible Sound",
  238         "Texas Instruments 3D Stereo Enhancement",
  239         "VLSI Technology 3D Stereo Enhancement",
  240         "TriTech 3D Stereo Enhancement",
  241         "Realtek 3D Stereo Enhancement",
  242         "Samsung 3D Stereo Enhancement",
  243         "Wolfson Microelectronics 3D Enhancement",
  244         "Delta Integration 3D Enhancement",
  245         "SigmaTel 3D Enhancement",
  246         "Reserved 27",
  247         "Rockwell 3D Stereo Enhancement",
  248         "Reserved 29",
  249         "Reserved 30",
  250         "Reserved 31"
  251 };
  252 
  253 static char *ac97feature[] = {
  254         "mic channel",
  255         "reserved",
  256         "tone",
  257         "simulated stereo",
  258         "headphone",
  259         "bass boost",
  260         "18 bit DAC",
  261         "20 bit DAC",
  262         "18 bit ADC",
  263         "20 bit ADC"
  264 };
  265 
  266 static char *ac97extfeature[] = {
  267         "variable rate PCM",
  268         "double rate PCM",
  269         "reserved 1",
  270         "variable rate mic",
  271         "reserved 2",
  272         "reserved 3",
  273         "center DAC",
  274         "surround DAC",
  275         "LFE DAC",
  276         "AMAP",
  277         "reserved 4",
  278         "reserved 5",
  279         "reserved 6",
  280         "reserved 7",
  281 };
  282 
  283 u_int16_t
  284 ac97_rdcd(struct ac97_info *codec, int reg)
  285 {
  286         return AC97_READ(codec->methods, codec->devinfo, reg);
  287 }
  288 
  289 void
  290 ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
  291 {
  292         AC97_WRITE(codec->methods, codec->devinfo, reg, val);
  293 }
  294 
  295 static void
  296 ac97_reset(struct ac97_info *codec)
  297 {
  298         u_int32_t i, ps;
  299         ac97_wrcd(codec, AC97_REG_RESET, 0);
  300         for (i = 0; i < 500; i++) {
  301                 ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
  302                 if (ps == AC97_POWER_STATUS)
  303                         return;
  304                 DELAY(1000);
  305         }
  306         device_printf(codec->dev, "AC97 reset timed out.\n");
  307 }
  308 
  309 int
  310 ac97_setrate(struct ac97_info *codec, int which, int rate)
  311 {
  312         u_int16_t v;
  313 
  314         switch(which) {
  315         case AC97_REGEXT_FDACRATE:
  316         case AC97_REGEXT_SDACRATE:
  317         case AC97_REGEXT_LDACRATE:
  318         case AC97_REGEXT_LADCRATE:
  319         case AC97_REGEXT_MADCRATE:
  320                 break;
  321 
  322         default:
  323                 return -1;
  324         }
  325 
  326         snd_mtxlock(codec->lock);
  327         if (rate != 0) {
  328                 v = rate;
  329                 if (codec->extstat & AC97_EXTCAP_DRA)
  330                         v >>= 1;
  331                 ac97_wrcd(codec, which, v);
  332         }
  333         v = ac97_rdcd(codec, which);
  334         if (codec->extstat & AC97_EXTCAP_DRA)
  335                 v <<= 1;
  336         snd_mtxunlock(codec->lock);
  337         return v;
  338 }
  339 
  340 int
  341 ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
  342 {
  343         mode &= AC97_EXTCAPS;
  344         if ((mode & ~codec->extcaps) != 0) {
  345                 device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
  346                               mode);
  347                 return -1;
  348         }
  349         snd_mtxlock(codec->lock);
  350         ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
  351         codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
  352         snd_mtxunlock(codec->lock);
  353         return (mode == codec->extstat)? 0 : -1;
  354 }
  355 
  356 u_int16_t
  357 ac97_getextmode(struct ac97_info *codec)
  358 {
  359         return codec->extstat;
  360 }
  361 
  362 u_int16_t
  363 ac97_getextcaps(struct ac97_info *codec)
  364 {
  365         return codec->extcaps;
  366 }
  367 
  368 u_int16_t
  369 ac97_getcaps(struct ac97_info *codec)
  370 {
  371         return codec->caps;
  372 }
  373 
  374 static int
  375 ac97_setrecsrc(struct ac97_info *codec, int channel)
  376 {
  377         struct ac97mixtable_entry *e = &codec->mix[channel];
  378 
  379         if (e->recidx > 0) {
  380                 int val = e->recidx - 1;
  381                 val |= val << 8;
  382                 snd_mtxlock(codec->lock);
  383                 ac97_wrcd(codec, AC97_REG_RECSEL, val);
  384                 snd_mtxunlock(codec->lock);
  385                 return 0;
  386         } else
  387                 return -1;
  388 }
  389 
  390 static int
  391 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
  392 {
  393         struct ac97mixtable_entry *e = &codec->mix[channel];
  394 
  395         if (e->reg && e->enable && e->bits) {
  396                 int mask, max, val, reg;
  397 
  398                 reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register    */
  399                 max = (1 << e->bits) - 1;               /* actual range     */
  400                 mask = (max << 8) | max;                /* bits of interest */
  401 
  402                 if (!e->stereo)
  403                         right = left;
  404 
  405                 /*
  406                  * Invert the range if the polarity requires so,
  407                  * then scale to 0..max-1 to compute the value to
  408                  * write into the codec, and scale back to 0..100
  409                  * for the return value.
  410                  */
  411                 if (e->reg > 0) {
  412                         left = 100 - left;
  413                         right = 100 - right;
  414                 }
  415 
  416                 left = (left * max) / 100;
  417                 right = (right * max) / 100;
  418 
  419                 val = (left << 8) | right;
  420 
  421                 left = (left * 100) / max;
  422                 right = (right * 100) / max;
  423 
  424                 if (e->reg > 0) {
  425                         left = 100 - left;
  426                         right = 100 - right;
  427                 }
  428 
  429                 /*
  430                  * For mono controls, trim val and mask, also taking
  431                  * care of e->ofs (offset of control field).
  432                  */
  433                 if (e->ofs) {
  434                         val &= max;
  435                         val <<= e->ofs;
  436                         mask = (max << e->ofs);
  437                 }
  438 
  439                 /*
  440                  * If we have a mute bit, add it to the mask and
  441                  * update val and set mute if both channels require a
  442                  * zero volume.
  443                  */
  444                 if (e->mute == 1) {
  445                         mask |= AC97_MUTE;
  446                         if (left == 0 && right == 0)
  447                                 val = AC97_MUTE;
  448                 }
  449 
  450                 /*
  451                  * If the mask bit is set, do not alter the other bits.
  452                  */
  453                 snd_mtxlock(codec->lock);
  454                 if (e->mask) {
  455                         int cur = ac97_rdcd(codec, e->reg);
  456                         val |= cur & ~(mask);
  457                 }
  458                 ac97_wrcd(codec, reg, val);
  459                 snd_mtxunlock(codec->lock);
  460                 return left | (right << 8);
  461         } else {
  462                 /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
  463                 return -1;
  464         }
  465 }
  466 
  467 static void
  468 ac97_fix_auxout(struct ac97_info *codec)
  469 {
  470         int keep_ogain;
  471 
  472         /*
  473          * By default, The ac97 aux_out register (0x04) corresponds to OSS's
  474          * OGAIN setting.
  475          *
  476          * We first check whether aux_out is a valid register.  If not
  477          * we may not want to keep ogain.
  478          */
  479         keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
  480 
  481         /*
  482          * Determine what AUX_OUT really means, it can be:
  483          *
  484          * 1. Headphone out.
  485          * 2. 4-Channel Out
  486          * 3. True line level out (effectively master volume).
  487          *
  488          * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
  489          */
  490         if (codec->extcaps & AC97_EXTCAP_SDAC &&
  491             ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
  492                 codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
  493                 keep_ogain = 1;
  494         }
  495 
  496         if (keep_ogain == 0) {
  497                 bzero(&codec->mix[SOUND_MIXER_OGAIN],
  498                       sizeof(codec->mix[SOUND_MIXER_OGAIN]));
  499         }
  500 }
  501 
  502 static void
  503 ac97_fix_tone(struct ac97_info *codec)
  504 {
  505         /* Hide treble and bass if they don't exist */
  506         if ((codec->caps & AC97_CAP_TONE) == 0) {
  507                 bzero(&codec->mix[SOUND_MIXER_BASS],
  508                       sizeof(codec->mix[SOUND_MIXER_BASS]));
  509                 bzero(&codec->mix[SOUND_MIXER_TREBLE],
  510                       sizeof(codec->mix[SOUND_MIXER_TREBLE]));
  511         }
  512 }
  513 
  514 static const char*
  515 ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
  516 {
  517         if (cname == NULL) {
  518                 sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
  519                 return buf;
  520         }
  521 
  522         if (vname == NULL) vname = "Unknown";
  523 
  524         if (bootverbose) {
  525                 sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
  526         } else {
  527                 sprintf(buf, "%s %s AC97 Codec", vname, cname);
  528         }
  529         return buf;
  530 }
  531 
  532 static unsigned
  533 ac97_initmixer(struct ac97_info *codec)
  534 {
  535         ac97_patch codec_patch;
  536         const char *cname, *vname;
  537         char desc[80];
  538         u_int8_t model, step;
  539         unsigned i, j, k, old;
  540         u_int32_t id;
  541 
  542         snd_mtxlock(codec->lock);
  543         codec->count = AC97_INIT(codec->methods, codec->devinfo);
  544         if (codec->count == 0) {
  545                 device_printf(codec->dev, "ac97 codec init failed\n");
  546                 snd_mtxunlock(codec->lock);
  547                 return ENODEV;
  548         }
  549 
  550         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
  551         ac97_reset(codec);
  552         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
  553 
  554         i = ac97_rdcd(codec, AC97_REG_RESET);
  555         codec->caps = i & 0x03ff;
  556         codec->se =  (i & 0x7c00) >> 10;
  557 
  558         id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
  559         if (id == 0 || id == 0xffffffff) {
  560                 device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
  561                 snd_mtxunlock(codec->lock);
  562                 return ENODEV;
  563         }
  564 
  565         codec->id = id;
  566         codec->noext = 0;
  567         codec_patch = NULL;
  568 
  569         cname = NULL;
  570         model = step = 0;
  571         for (i = 0; ac97codecid[i].id; i++) {
  572                 u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
  573                 if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
  574                         codec->noext = ac97codecid[i].noext;
  575                         codec_patch = ac97codecid[i].patch;
  576                         cname = ac97codecid[i].name;
  577                         model = (id & modelmask) & 0xff;
  578                         step = (id & ~modelmask) & 0xff;
  579                         break;
  580                 }
  581         }
  582 
  583         vname = NULL;
  584         for (i = 0; ac97vendorid[i].id; i++) {
  585                 if (ac97vendorid[i].id == (id & 0xffffff00)) {
  586                         vname = ac97vendorid[i].name;
  587                         break;
  588                 }
  589         }
  590 
  591         codec->extcaps = 0;
  592         codec->extid = 0;
  593         codec->extstat = 0;
  594         if (!codec->noext) {
  595                 i = ac97_rdcd(codec, AC97_REGEXT_ID);
  596                 if (i != 0xffff) {
  597                         codec->extcaps = i & 0x3fff;
  598                         codec->extid =  (i & 0xc000) >> 14;
  599                         codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
  600                 }
  601         }
  602 
  603         for (i = 0; i < 32; i++) {
  604                 codec->mix[i] = ac97mixtable_default[i];
  605         }
  606         ac97_fix_auxout(codec);
  607         ac97_fix_tone(codec);
  608         if (codec_patch)
  609                 codec_patch(codec);
  610 
  611         for (i = 0; i < 32; i++) {
  612                 k = codec->noext? codec->mix[i].enable : 1;
  613                 if (k && (codec->mix[i].reg > 0)) {
  614                         old = ac97_rdcd(codec, codec->mix[i].reg);
  615                         ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
  616                         j = ac97_rdcd(codec, codec->mix[i].reg);
  617                         ac97_wrcd(codec, codec->mix[i].reg, old);
  618                         codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
  619                         for (k = 1; j & (1 << k); k++);
  620                         codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
  621                 }
  622                 /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
  623         }
  624 
  625         device_printf(codec->dev, "<%s>\n",
  626                       ac97_hw_desc(codec->id, vname, cname, desc));
  627 
  628         if (bootverbose) {
  629                 device_printf(codec->dev, "Codec features ");
  630                 for (i = j = 0; i < 10; i++)
  631                         if (codec->caps & (1 << i))
  632                                 printf("%s%s", j++? ", " : "", ac97feature[i]);
  633                 printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
  634                 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
  635 
  636                 if (codec->extcaps != 0 || codec->extid) {
  637                         device_printf(codec->dev, "%s codec",
  638                                       codec->extid? "Secondary" : "Primary");
  639                         if (codec->extcaps)
  640                                 printf(" extended features ");
  641                         for (i = j = 0; i < 14; i++)
  642                                 if (codec->extcaps & (1 << i))
  643                                         printf("%s%s", j++? ", " : "", ac97extfeature[i]);
  644                         printf("\n");
  645                 }
  646         }
  647 
  648         if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
  649                 device_printf(codec->dev, "ac97 codec reports dac not ready\n");
  650         snd_mtxunlock(codec->lock);
  651         return 0;
  652 }
  653 
  654 static unsigned
  655 ac97_reinitmixer(struct ac97_info *codec)
  656 {
  657         snd_mtxlock(codec->lock);
  658         codec->count = AC97_INIT(codec->methods, codec->devinfo);
  659         if (codec->count == 0) {
  660                 device_printf(codec->dev, "ac97 codec init failed\n");
  661                 snd_mtxunlock(codec->lock);
  662                 return ENODEV;
  663         }
  664 
  665         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
  666         ac97_reset(codec);
  667         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
  668 
  669         if (!codec->noext) {
  670                 ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
  671                 if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
  672                     != codec->extstat)
  673                         device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
  674                                       codec->extstat,
  675                                       ac97_rdcd(codec, AC97_REGEXT_STAT) &
  676                                       AC97_EXTCAPS);
  677         }
  678 
  679         if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
  680                 device_printf(codec->dev, "ac97 codec reports dac not ready\n");
  681         snd_mtxunlock(codec->lock);
  682         return 0;
  683 }
  684 
  685 struct ac97_info *
  686 ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
  687 {
  688         struct ac97_info *codec;
  689 
  690         codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
  691         if (codec == NULL)
  692                 return NULL;
  693 
  694         snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
  695         codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
  696         codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
  697         if (codec->methods == NULL) {
  698                 snd_mtxlock(codec->lock);
  699                 snd_mtxfree(codec->lock);
  700                 free(codec, M_AC97);
  701                 return NULL;
  702         }
  703 
  704         codec->dev = dev;
  705         codec->devinfo = devinfo;
  706         codec->flags = 0;
  707         return codec;
  708 }
  709 
  710 void
  711 ac97_destroy(struct ac97_info *codec)
  712 {
  713         snd_mtxlock(codec->lock);
  714         if (codec->methods != NULL)
  715                 kobj_delete(codec->methods, M_AC97);
  716         snd_mtxfree(codec->lock);
  717         free(codec, M_AC97);
  718 }
  719 
  720 void
  721 ac97_setflags(struct ac97_info *codec, u_int32_t val)
  722 {
  723         codec->flags = val;
  724 }
  725 
  726 u_int32_t
  727 ac97_getflags(struct ac97_info *codec)
  728 {
  729         return codec->flags;
  730 }
  731 
  732 /* -------------------------------------------------------------------- */
  733 
  734 static int
  735 ac97mix_init(struct snd_mixer *m)
  736 {
  737         struct ac97_info *codec = mix_getdevinfo(m);
  738         u_int32_t i, mask;
  739 
  740         if (codec == NULL)
  741                 return -1;
  742 
  743         if (ac97_initmixer(codec))
  744                 return -1;
  745 
  746         mask = 0;
  747         for (i = 0; i < 32; i++)
  748                 mask |= codec->mix[i].enable? 1 << i : 0;
  749         mix_setdevs(m, mask);
  750 
  751         mask = 0;
  752         for (i = 0; i < 32; i++)
  753                 mask |= codec->mix[i].recidx? 1 << i : 0;
  754         mix_setrecdevs(m, mask);
  755         return 0;
  756 }
  757 
  758 static int
  759 ac97mix_uninit(struct snd_mixer *m)
  760 {
  761         struct ac97_info *codec = mix_getdevinfo(m);
  762 
  763         if (codec == NULL)
  764                 return -1;
  765         /*
  766         if (ac97_uninitmixer(codec))
  767                 return -1;
  768         */
  769         ac97_destroy(codec);
  770         return 0;
  771 }
  772 
  773 static int
  774 ac97mix_reinit(struct snd_mixer *m)
  775 {
  776         struct ac97_info *codec = mix_getdevinfo(m);
  777 
  778         if (codec == NULL)
  779                 return -1;
  780         return ac97_reinitmixer(codec);
  781 }
  782 
  783 static int
  784 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
  785 {
  786         struct ac97_info *codec = mix_getdevinfo(m);
  787 
  788         if (codec == NULL)
  789                 return -1;
  790         return ac97_setmixer(codec, dev, left, right);
  791 }
  792 
  793 static int
  794 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
  795 {
  796         int i;
  797         struct ac97_info *codec = mix_getdevinfo(m);
  798 
  799         if (codec == NULL)
  800                 return -1;
  801         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
  802                 if ((src & (1 << i)) != 0)
  803                         break;
  804         return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
  805 }
  806 
  807 static kobj_method_t ac97mixer_methods[] = {
  808         KOBJMETHOD(mixer_init,          ac97mix_init),
  809         KOBJMETHOD(mixer_uninit,        ac97mix_uninit),
  810         KOBJMETHOD(mixer_reinit,        ac97mix_reinit),
  811         KOBJMETHOD(mixer_set,           ac97mix_set),
  812         KOBJMETHOD(mixer_setrecsrc,     ac97mix_setrecsrc),
  813         { 0, 0 }
  814 };
  815 MIXER_DECLARE(ac97mixer);
  816 
  817 /* -------------------------------------------------------------------- */
  818 
  819 kobj_class_t
  820 ac97_getmixerclass(void)
  821 {
  822         return &ac97mixer_class;
  823 }
  824 
  825 

Cache object: 5d785de42d21609ea56ff49dc10f88ce


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