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 #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: releng/6.2/sys/dev/sound/pcm/ac97.c 154125 2006-01-09 02:06:42Z ariff $");
   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         /* use igain for the mic 20dB boost */
   88         [SOUND_MIXER_IGAIN]     = { -AC97_MIX_MIC,      1, 6, 0, 0, 0, 1, 1 },
   89         [SOUND_MIXER_CD]        = { AC97_MIX_CD,        5, 0, 1, 1, 2, 0, 1 },
   90         [SOUND_MIXER_LINE1]     = { AC97_MIX_AUX,       5, 0, 1, 1, 4, 0, 0 },
   91         [SOUND_MIXER_VIDEO]     = { AC97_MIX_VIDEO,     5, 0, 1, 1, 3, 0, 0 },
   92         [SOUND_MIXER_RECLEV]    = { -AC97_MIX_RGAIN,    4, 0, 1, 1, 0, 0, 1 }
   93 };
   94 
   95 static const struct ac97_vendorid ac97vendorid[] = {
   96         { 0x41445300, "Analog Devices" },
   97         { 0x414b4d00, "Asahi Kasei" },
   98         { 0x414c4300, "Realtek" },
   99         { 0x414c4700, "Avance Logic" },
  100         { 0x43525900, "Cirrus Logic" },
  101         { 0x434d4900, "C-Media Electronics" },
  102         { 0x43585400, "Conexant" },
  103         { 0x44543000, "Diamond Technology" },
  104         { 0x454d4300, "eMicro" },
  105         { 0x45838300, "ESS Technology" },
  106         { 0x48525300, "Intersil" },
  107         { 0x49434500, "ICEnsemble" },
  108         { 0x49544500, "ITE, Inc." },
  109         { 0x4e534300, "National Semiconductor" },
  110         { 0x50534300, "Philips Semiconductor" },
  111         { 0x83847600, "SigmaTel" },
  112         { 0x53494c00, "Silicon Laboratories" },
  113         { 0x54524100, "TriTech" },
  114         { 0x54584e00, "Texas Instruments" },
  115         { 0x56494100, "VIA Technologies" },
  116         { 0x57454300, "Winbond" },
  117         { 0x574d4c00, "Wolfson" },
  118         { 0x594d4800, "Yamaha" },
  119         /* 
  120          * XXX This is a fluke, really! The real vendor
  121          * should be SigmaTel, not this! This should be
  122          * removed someday!
  123          */
  124         { 0x01408300, "Creative" },
  125         { 0x00000000, NULL }
  126 };
  127 
  128 static struct ac97_codecid ac97codecid[] = {
  129         { 0x41445303, 0x00, 0, "AD1819",        0 },
  130         { 0x41445340, 0x00, 0, "AD1881",        0 },
  131         { 0x41445348, 0x00, 0, "AD1881A",       0 },
  132         { 0x41445360, 0x00, 0, "AD1885",        0 },
  133         { 0x41445361, 0x00, 0, "AD1886",        ad1886_patch },
  134         { 0x41445362, 0x00, 0, "AD1887",        0 },
  135         { 0x41445363, 0x00, 0, "AD1886A",       0 },
  136         { 0x41445368, 0x00, 0, "AD1888",        ad198x_patch },
  137         { 0x41445370, 0x00, 0, "AD1980",        ad198x_patch },
  138         { 0x41445372, 0x00, 0, "AD1981A",       0 },
  139         { 0x41445374, 0x00, 0, "AD1981B",       0 },
  140         { 0x41445375, 0x00, 0, "AD1985",        ad198x_patch },
  141         { 0x41445378, 0x00, 0, "AD1986",        ad198x_patch },
  142         { 0x414b4d00, 0x00, 1, "AK4540",        0 },
  143         { 0x414b4d01, 0x00, 1, "AK4542",        0 },
  144         { 0x414b4d02, 0x00, 1, "AK4543",        0 },
  145         { 0x414b4d06, 0x00, 0, "AK4544A",       0 },
  146         { 0x454b4d07, 0x00, 0, "AK4545",        0 },
  147         { 0x414c4320, 0x0f, 0, "ALC100",        0 },
  148         { 0x414c4730, 0x0f, 0, "ALC101",        0 },
  149         { 0x414c4710, 0x0f, 0, "ALC200",        0 },
  150         { 0x414c4740, 0x0f, 0, "ALC202",        0 },
  151         { 0x414c4720, 0x0f, 0, "ALC650",        0 },
  152         { 0x414c4752, 0x0f, 0, "ALC250",        0 },
  153         { 0x414c4760, 0x0f, 0, "ALC655",        0 },
  154         { 0x414c4770, 0x0f, 0, "ALC203",        0 },
  155         { 0x414c4780, 0x0f, 0, "ALC658",        0 },
  156         { 0x414c4790, 0x0f, 0, "ALC850",        0 },
  157         { 0x43525900, 0x07, 0, "CS4297",        0 },
  158         { 0x43525910, 0x07, 0, "CS4297A",       0 },
  159         { 0x43525920, 0x07, 0, "CS4294/98",     0 },
  160         { 0x4352592d, 0x07, 0, "CS4294",        0 },
  161         { 0x43525930, 0x07, 0, "CS4299",        0 },
  162         { 0x43525940, 0x07, 0, "CS4201",        0 },
  163         { 0x43525958, 0x07, 0, "CS4205",        0 },
  164         { 0x43525960, 0x07, 0, "CS4291A",       0 },
  165         { 0x434d4961, 0x00, 0, "CMI9739",       cmi9739_patch },
  166         { 0x434d4941, 0x00, 0, "CMI9738",       0 },
  167         { 0x434d4978, 0x00, 0, "CMI9761",       0 },
  168         { 0x434d4982, 0x00, 0, "CMI9761",       0 },
  169         { 0x434d4983, 0x00, 0, "CMI9761",       0 },
  170         { 0x43585421, 0x00, 0, "HSD11246",      0 },
  171         { 0x43585428, 0x07, 0, "CX20468",       0 },
  172         { 0x43585430, 0x00, 0, "CX20468-21",    0 },
  173         { 0x44543000, 0x00, 0, "DT0398",        0 },
  174         { 0x454d4323, 0x00, 0, "EM28023",       0 },
  175         { 0x454d4328, 0x00, 0, "EM28028",       0 },
  176         { 0x45838308, 0x00, 0, "ES1988",        0 }, /* Formerly ES1921(?) */
  177         { 0x48525300, 0x00, 0, "HMP9701",       0 },
  178         { 0x49434501, 0x00, 0, "ICE1230",       0 },
  179         { 0x49434511, 0x00, 0, "ICE1232",       0 },
  180         { 0x49434514, 0x00, 0, "ICE1232A",      0 },
  181         { 0x49434551, 0x03, 0, "VT1616",        0 }, /* Via badged ICE */
  182         { 0x49544520, 0x00, 0, "ITE2226E",      0 },
  183         { 0x49544560, 0x07, 0, "ITE2646E",      0 }, /* XXX: patch needed */
  184         { 0x4e534340, 0x00, 0, "LM4540",        0 }, /* Spec blank on revid */
  185         { 0x4e534343, 0x00, 0, "LM4543",        0 }, /* Ditto */
  186         { 0x4e534346, 0x00, 0, "LM4546A",       0 },
  187         { 0x4e534348, 0x00, 0, "LM4548A",       0 },
  188         { 0x4e534331, 0x00, 0, "LM4549",        0 },
  189         { 0x4e534349, 0x00, 0, "LM4549A",       0 },
  190         { 0x4e534350, 0x00, 0, "LM4550",        0 },
  191         { 0x50534301, 0x00, 0, "UCB1510",       0 },
  192         { 0x50534304, 0x00, 0, "UCB1400",       0 },
  193         { 0x83847600, 0x00, 0, "STAC9700/83/84",        0 },
  194         { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
  195         { 0x83847605, 0x00, 0, "STAC9704",      0 },
  196         { 0x83847608, 0x00, 0, "STAC9708/11",   0 },
  197         { 0x83847609, 0x00, 0, "STAC9721/23",   0 },
  198         { 0x83847644, 0x00, 0, "STAC9744/45",   0 },
  199         { 0x83847650, 0x00, 0, "STAC9750/51",   0 },
  200         { 0x83847652, 0x00, 0, "STAC9752/53",   0 },
  201         { 0x83847656, 0x00, 0, "STAC9756/57",   0 },
  202         { 0x83847658, 0x00, 0, "STAC9758/59",   0 },
  203         { 0x83847660, 0x00, 0, "STAC9760/61",   0 }, /* Extrapolated */
  204         { 0x83847662, 0x00, 0, "STAC9762/63",   0 }, /* Extrapolated */
  205         { 0x83847666, 0x00, 0, "STAC9766/67",   0 },
  206         { 0x53494c22, 0x00, 0, "Si3036",        0 },
  207         { 0x53494c23, 0x00, 0, "Si3038",        0 },
  208         { 0x54524103, 0x00, 0, "TR28023",       0 }, /* Extrapolated */
  209         { 0x54524106, 0x00, 0, "TR28026",       0 },
  210         { 0x54524108, 0x00, 0, "TR28028",       0 },
  211         { 0x54524123, 0x00, 0, "TR28602",       0 },
  212         { 0x54524e03, 0x07, 0, "TLV320AIC27",   0 },
  213         { 0x54584e20, 0x00, 0, "TLC320AD90",    0 },
  214         { 0x56494161, 0x00, 0, "VIA1612A",      0 },
  215         { 0x56494170, 0x00, 0, "VIA1617A",      0 },
  216         { 0x574d4c00, 0x00, 0, "WM9701A",       0 },
  217         { 0x574d4c03, 0x00, 0, "WM9703/4/7/8",  0 },
  218         { 0x574d4c04, 0x00, 0, "WM9704Q",       0 },
  219         { 0x574d4c05, 0x00, 0, "WM9705/10",     0 },
  220         { 0x574d4d09, 0x00, 0, "WM9709",        0 },
  221         { 0x574d4c12, 0x00, 0, "WM9711/12",     0 }, /* XXX: patch needed */
  222         { 0x57454301, 0x00, 0, "W83971D",       0 },
  223         { 0x594d4800, 0x00, 0, "YMF743",        0 },
  224         { 0x594d4802, 0x00, 0, "YMF752",        0 },
  225         { 0x594d4803, 0x00, 0, "YMF753",        0 },
  226         /* 
  227          * XXX This is a fluke, really! The real codec
  228          * should be STAC9704, not this! This should be
  229          * removed someday!
  230          */
  231         { 0x01408384, 0x00, 0, "EV1938",        0 },
  232         { 0, 0, 0, NULL, 0 }
  233 };
  234 
  235 static char *ac97enhancement[] = {
  236         "no 3D Stereo Enhancement",
  237         "Analog Devices Phat Stereo",
  238         "Creative Stereo Enhancement",
  239         "National Semi 3D Stereo Enhancement",
  240         "Yamaha Ymersion",
  241         "BBE 3D Stereo Enhancement",
  242         "Crystal Semi 3D Stereo Enhancement",
  243         "Qsound QXpander",
  244         "Spatializer 3D Stereo Enhancement",
  245         "SRS 3D Stereo Enhancement",
  246         "Platform Tech 3D Stereo Enhancement",
  247         "AKM 3D Audio",
  248         "Aureal Stereo Enhancement",
  249         "Aztech 3D Enhancement",
  250         "Binaura 3D Audio Enhancement",
  251         "ESS Technology Stereo Enhancement",
  252         "Harman International VMAx",
  253         "Nvidea 3D Stereo Enhancement",
  254         "Philips Incredible Sound",
  255         "Texas Instruments 3D Stereo Enhancement",
  256         "VLSI Technology 3D Stereo Enhancement",
  257         "TriTech 3D Stereo Enhancement",
  258         "Realtek 3D Stereo Enhancement",
  259         "Samsung 3D Stereo Enhancement",
  260         "Wolfson Microelectronics 3D Enhancement",
  261         "Delta Integration 3D Enhancement",
  262         "SigmaTel 3D Enhancement",
  263         "Reserved 27",
  264         "Rockwell 3D Stereo Enhancement",
  265         "Reserved 29",
  266         "Reserved 30",
  267         "Reserved 31"
  268 };
  269 
  270 static char *ac97feature[] = {
  271         "mic channel",
  272         "reserved",
  273         "tone",
  274         "simulated stereo",
  275         "headphone",
  276         "bass boost",
  277         "18 bit DAC",
  278         "20 bit DAC",
  279         "18 bit ADC",
  280         "20 bit ADC"
  281 };
  282 
  283 static char *ac97extfeature[] = {
  284         "variable rate PCM",
  285         "double rate PCM",
  286         "reserved 1",
  287         "variable rate mic",
  288         "reserved 2",
  289         "reserved 3",
  290         "center DAC",
  291         "surround DAC",
  292         "LFE DAC",
  293         "AMAP",
  294         "reserved 4",
  295         "reserved 5",
  296         "reserved 6",
  297         "reserved 7",
  298 };
  299 
  300 u_int16_t
  301 ac97_rdcd(struct ac97_info *codec, int reg)
  302 {
  303         if (codec->flags & AC97_F_RDCD_BUG) {
  304                 u_int16_t i[2], j = 100;
  305 
  306                 i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
  307                 i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
  308                 while (i[0] != i[1] && j)
  309                         i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
  310 #if 0
  311                 if (j < 100) {
  312                         device_printf(codec->dev, "%s(): Inconsistent register value at"
  313                                         " 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
  314                 }
  315 #endif
  316                 return i[!(j & 1)];
  317         }
  318         return AC97_READ(codec->methods, codec->devinfo, reg);
  319 }
  320 
  321 void
  322 ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
  323 {
  324         AC97_WRITE(codec->methods, codec->devinfo, reg, val);
  325 }
  326 
  327 static void
  328 ac97_reset(struct ac97_info *codec)
  329 {
  330         u_int32_t i, ps;
  331         ac97_wrcd(codec, AC97_REG_RESET, 0);
  332         for (i = 0; i < 500; i++) {
  333                 ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
  334                 if (ps == AC97_POWER_STATUS)
  335                         return;
  336                 DELAY(1000);
  337         }
  338         device_printf(codec->dev, "AC97 reset timed out.\n");
  339 }
  340 
  341 int
  342 ac97_setrate(struct ac97_info *codec, int which, int rate)
  343 {
  344         u_int16_t v;
  345 
  346         switch(which) {
  347         case AC97_REGEXT_FDACRATE:
  348         case AC97_REGEXT_SDACRATE:
  349         case AC97_REGEXT_LDACRATE:
  350         case AC97_REGEXT_LADCRATE:
  351         case AC97_REGEXT_MADCRATE:
  352                 break;
  353 
  354         default:
  355                 return -1;
  356         }
  357 
  358         snd_mtxlock(codec->lock);
  359         if (rate != 0) {
  360                 v = rate;
  361                 if (codec->extstat & AC97_EXTCAP_DRA)
  362                         v >>= 1;
  363                 ac97_wrcd(codec, which, v);
  364         }
  365         v = ac97_rdcd(codec, which);
  366         if (codec->extstat & AC97_EXTCAP_DRA)
  367                 v <<= 1;
  368         snd_mtxunlock(codec->lock);
  369         return v;
  370 }
  371 
  372 int
  373 ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
  374 {
  375         mode &= AC97_EXTCAPS;
  376         if ((mode & ~codec->extcaps) != 0) {
  377                 device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
  378                               mode);
  379                 return -1;
  380         }
  381         snd_mtxlock(codec->lock);
  382         ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
  383         codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
  384         snd_mtxunlock(codec->lock);
  385         return (mode == codec->extstat)? 0 : -1;
  386 }
  387 
  388 u_int16_t
  389 ac97_getextmode(struct ac97_info *codec)
  390 {
  391         return codec->extstat;
  392 }
  393 
  394 u_int16_t
  395 ac97_getextcaps(struct ac97_info *codec)
  396 {
  397         return codec->extcaps;
  398 }
  399 
  400 u_int16_t
  401 ac97_getcaps(struct ac97_info *codec)
  402 {
  403         return codec->caps;
  404 }
  405 
  406 static int
  407 ac97_setrecsrc(struct ac97_info *codec, int channel)
  408 {
  409         struct ac97mixtable_entry *e = &codec->mix[channel];
  410 
  411         if (e->recidx > 0) {
  412                 int val = e->recidx - 1;
  413                 val |= val << 8;
  414                 snd_mtxlock(codec->lock);
  415                 ac97_wrcd(codec, AC97_REG_RECSEL, val);
  416                 snd_mtxunlock(codec->lock);
  417                 return 0;
  418         } else
  419                 return -1;
  420 }
  421 
  422 static int
  423 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
  424 {
  425         struct ac97mixtable_entry *e = &codec->mix[channel];
  426 
  427         if (e->reg && e->enable && e->bits) {
  428                 int mask, max, val, reg;
  429 
  430                 reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register    */
  431                 max = (1 << e->bits) - 1;               /* actual range     */
  432                 mask = (max << 8) | max;                /* bits of interest */
  433 
  434                 if (!e->stereo)
  435                         right = left;
  436 
  437                 /*
  438                  * Invert the range if the polarity requires so,
  439                  * then scale to 0..max-1 to compute the value to
  440                  * write into the codec, and scale back to 0..100
  441                  * for the return value.
  442                  */
  443                 if (e->reg > 0) {
  444                         left = 100 - left;
  445                         right = 100 - right;
  446                 }
  447 
  448                 left = (left * max) / 100;
  449                 right = (right * max) / 100;
  450 
  451                 val = (left << 8) | right;
  452 
  453                 left = (left * 100) / max;
  454                 right = (right * 100) / max;
  455 
  456                 if (e->reg > 0) {
  457                         left = 100 - left;
  458                         right = 100 - right;
  459                 }
  460 
  461                 /*
  462                  * For mono controls, trim val and mask, also taking
  463                  * care of e->ofs (offset of control field).
  464                  */
  465                 if (e->ofs) {
  466                         val &= max;
  467                         val <<= e->ofs;
  468                         mask = (max << e->ofs);
  469                 }
  470 
  471                 /*
  472                  * If we have a mute bit, add it to the mask and
  473                  * update val and set mute if both channels require a
  474                  * zero volume.
  475                  */
  476                 if (e->mute == 1) {
  477                         mask |= AC97_MUTE;
  478                         if (left == 0 && right == 0)
  479                                 val = AC97_MUTE;
  480                 }
  481 
  482                 /*
  483                  * If the mask bit is set, do not alter the other bits.
  484                  */
  485                 snd_mtxlock(codec->lock);
  486                 if (e->mask) {
  487                         int cur = ac97_rdcd(codec, reg);
  488                         val |= cur & ~(mask);
  489                 }
  490                 ac97_wrcd(codec, reg, val);
  491                 snd_mtxunlock(codec->lock);
  492                 return left | (right << 8);
  493         } else {
  494 #if 0
  495                 printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
  496 #endif
  497                 return -1;
  498         }
  499 }
  500 
  501 static void
  502 ac97_fix_auxout(struct ac97_info *codec)
  503 {
  504         int keep_ogain;
  505 
  506         /*
  507          * By default, The ac97 aux_out register (0x04) corresponds to OSS's
  508          * OGAIN setting.
  509          *
  510          * We first check whether aux_out is a valid register.  If not
  511          * we may not want to keep ogain.
  512          */
  513         keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
  514 
  515         /*
  516          * Determine what AUX_OUT really means, it can be:
  517          *
  518          * 1. Headphone out.
  519          * 2. 4-Channel Out
  520          * 3. True line level out (effectively master volume).
  521          *
  522          * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
  523          */
  524         if (codec->extcaps & AC97_EXTCAP_SDAC &&
  525             ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
  526                 codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
  527                 keep_ogain = 1;
  528         }
  529 
  530         if (keep_ogain == 0) {
  531                 bzero(&codec->mix[SOUND_MIXER_OGAIN],
  532                       sizeof(codec->mix[SOUND_MIXER_OGAIN]));
  533         }
  534 }
  535 
  536 static void
  537 ac97_fix_tone(struct ac97_info *codec)
  538 {
  539         /* Hide treble and bass if they don't exist */
  540         if ((codec->caps & AC97_CAP_TONE) == 0) {
  541                 bzero(&codec->mix[SOUND_MIXER_BASS],
  542                       sizeof(codec->mix[SOUND_MIXER_BASS]));
  543                 bzero(&codec->mix[SOUND_MIXER_TREBLE],
  544                       sizeof(codec->mix[SOUND_MIXER_TREBLE]));
  545         }
  546 }
  547 
  548 static void
  549 ac97_fix_volume(struct ac97_info *codec)
  550 {
  551         struct snddev_info *d = device_get_softc(codec->dev);
  552 
  553 #if 0
  554         /* XXX For the sake of debugging purposes */
  555         ac97_wrcd(codec, AC97_MIX_PCM, 0);
  556         bzero(&codec->mix[SOUND_MIXER_PCM],
  557                 sizeof(codec->mix[SOUND_MIXER_PCM]));
  558         codec->flags |= AC97_F_SOFTVOL;
  559         if (d)
  560                 d->flags |= SD_F_SOFTVOL;
  561         return;
  562 #endif
  563         switch (codec->id) {
  564                 case 0x434d4941:        /* CMI9738 */
  565                 case 0x434d4961:        /* CMI9739 */
  566                 case 0x434d4978:        /* CMI9761 */
  567                 case 0x434d4982:        /* CMI9761 */
  568                 case 0x434d4983:        /* CMI9761 */
  569                         ac97_wrcd(codec, AC97_MIX_PCM, 0);
  570                         break;
  571                 default:
  572                         return;
  573                         break;
  574         }
  575         bzero(&codec->mix[SOUND_MIXER_PCM],
  576                         sizeof(codec->mix[SOUND_MIXER_PCM]));
  577         codec->flags |= AC97_F_SOFTVOL;
  578         if (d)
  579                 d->flags |= SD_F_SOFTVOL;
  580 }
  581 
  582 static const char*
  583 ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
  584 {
  585         if (cname == NULL) {
  586                 sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
  587                 return buf;
  588         }
  589 
  590         if (vname == NULL) vname = "Unknown";
  591 
  592         if (bootverbose) {
  593                 sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
  594         } else {
  595                 sprintf(buf, "%s %s AC97 Codec", vname, cname);
  596         }
  597         return buf;
  598 }
  599 
  600 static unsigned
  601 ac97_initmixer(struct ac97_info *codec)
  602 {
  603         ac97_patch codec_patch;
  604         const char *cname, *vname;
  605         char desc[80];
  606         u_int8_t model, step;
  607         unsigned i, j, k, bit, old;
  608         u_int32_t id;
  609         int reg;
  610 
  611         snd_mtxlock(codec->lock);
  612         codec->count = AC97_INIT(codec->methods, codec->devinfo);
  613         if (codec->count == 0) {
  614                 device_printf(codec->dev, "ac97 codec init failed\n");
  615                 snd_mtxunlock(codec->lock);
  616                 return ENODEV;
  617         }
  618 
  619         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
  620         ac97_reset(codec);
  621         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
  622 
  623         i = ac97_rdcd(codec, AC97_REG_RESET);
  624         j = 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) {
  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->noext = 0;
  646         codec_patch = NULL;
  647 
  648         cname = NULL;
  649         model = step = 0;
  650         for (i = 0; ac97codecid[i].id; i++) {
  651                 u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
  652                 if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
  653                         codec->noext = ac97codecid[i].noext;
  654                         codec_patch = ac97codecid[i].patch;
  655                         cname = ac97codecid[i].name;
  656                         model = (id & modelmask) & 0xff;
  657                         step = (id & ~modelmask) & 0xff;
  658                         break;
  659                 }
  660         }
  661 
  662         vname = NULL;
  663         for (i = 0; ac97vendorid[i].id; i++) {
  664                 if (ac97vendorid[i].id == (id & 0xffffff00)) {
  665                         vname = ac97vendorid[i].name;
  666                         break;
  667                 }
  668         }
  669 
  670         codec->extcaps = 0;
  671         codec->extid = 0;
  672         codec->extstat = 0;
  673         if (!codec->noext) {
  674                 i = ac97_rdcd(codec, AC97_REGEXT_ID);
  675                 if (i != 0xffff) {
  676                         codec->extcaps = i & 0x3fff;
  677                         codec->extid =  (i & 0xc000) >> 14;
  678                         codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
  679                 }
  680         }
  681 
  682         for (i = 0; i < 32; i++) {
  683                 codec->mix[i] = ac97mixtable_default[i];
  684         }
  685         ac97_fix_auxout(codec);
  686         ac97_fix_tone(codec);
  687         ac97_fix_volume(codec);
  688         if (codec_patch)
  689                 codec_patch(codec);
  690 
  691         for (i = 0; i < 32; 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                 if (codec->flags & AC97_F_SOFTVOL)
  762                         device_printf(codec->dev, "Soft PCM volume\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 
  832         codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
  833         if (codec == NULL)
  834                 return NULL;
  835 
  836         snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
  837         codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
  838         codec->methods = kobj_create(cls, M_AC97, M_WAITOK);
  839         if (codec->methods == NULL) {
  840                 snd_mtxlock(codec->lock);
  841                 snd_mtxfree(codec->lock);
  842                 free(codec, M_AC97);
  843                 return NULL;
  844         }
  845 
  846         codec->dev = dev;
  847         codec->devinfo = devinfo;
  848         codec->flags = 0;
  849         return codec;
  850 }
  851 
  852 void
  853 ac97_destroy(struct ac97_info *codec)
  854 {
  855         snd_mtxlock(codec->lock);
  856         if (codec->methods != NULL)
  857                 kobj_delete(codec->methods, M_AC97);
  858         snd_mtxfree(codec->lock);
  859         free(codec, M_AC97);
  860 }
  861 
  862 void
  863 ac97_setflags(struct ac97_info *codec, u_int32_t val)
  864 {
  865         codec->flags = val;
  866 }
  867 
  868 u_int32_t
  869 ac97_getflags(struct ac97_info *codec)
  870 {
  871         return codec->flags;
  872 }
  873 
  874 /* -------------------------------------------------------------------- */
  875 
  876 static int
  877 ac97mix_init(struct snd_mixer *m)
  878 {
  879         struct ac97_info *codec = mix_getdevinfo(m);
  880         u_int32_t i, mask;
  881 
  882         if (codec == NULL)
  883                 return -1;
  884 
  885         if (ac97_initmixer(codec))
  886                 return -1;
  887 
  888         mask = 0;
  889         for (i = 0; i < 32; i++)
  890                 mask |= codec->mix[i].enable? 1 << i : 0;
  891         mix_setdevs(m, mask);
  892 
  893         mask = 0;
  894         for (i = 0; i < 32; i++)
  895                 mask |= codec->mix[i].recidx? 1 << i : 0;
  896         mix_setrecdevs(m, mask);
  897         return 0;
  898 }
  899 
  900 static int
  901 ac97mix_uninit(struct snd_mixer *m)
  902 {
  903         struct ac97_info *codec = mix_getdevinfo(m);
  904 
  905         if (codec == NULL)
  906                 return -1;
  907         /*
  908         if (ac97_uninitmixer(codec))
  909                 return -1;
  910         */
  911         ac97_destroy(codec);
  912         return 0;
  913 }
  914 
  915 static int
  916 ac97mix_reinit(struct snd_mixer *m)
  917 {
  918         struct ac97_info *codec = mix_getdevinfo(m);
  919 
  920         if (codec == NULL)
  921                 return -1;
  922         return ac97_reinitmixer(codec);
  923 }
  924 
  925 static int
  926 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
  927 {
  928         struct ac97_info *codec = mix_getdevinfo(m);
  929 
  930         if (codec == NULL)
  931                 return -1;
  932         return ac97_setmixer(codec, dev, left, right);
  933 }
  934 
  935 static int
  936 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
  937 {
  938         int i;
  939         struct ac97_info *codec = mix_getdevinfo(m);
  940 
  941         if (codec == NULL)
  942                 return -1;
  943         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
  944                 if ((src & (1 << i)) != 0)
  945                         break;
  946         return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
  947 }
  948 
  949 static kobj_method_t ac97mixer_methods[] = {
  950         KOBJMETHOD(mixer_init,          ac97mix_init),
  951         KOBJMETHOD(mixer_uninit,        ac97mix_uninit),
  952         KOBJMETHOD(mixer_reinit,        ac97mix_reinit),
  953         KOBJMETHOD(mixer_set,           ac97mix_set),
  954         KOBJMETHOD(mixer_setrecsrc,     ac97mix_setrecsrc),
  955         { 0, 0 }
  956 };
  957 MIXER_DECLARE(ac97mixer);
  958 
  959 /* -------------------------------------------------------------------- */
  960 
  961 kobj_class_t
  962 ac97_getmixerclass(void)
  963 {
  964         return &ac97mixer_class;
  965 }
  966 
  967 

Cache object: d7fa5219f12770e27643a2c42beffdfe


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