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

Cache object: 9b642751de5be6c68bd1dfe8b03b0ed5


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