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

Cache object: 91030be3e41b4666346d8918c582f5db


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