1987e5972SCameron Grant /* 2987e5972SCameron Grant * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3987e5972SCameron Grant * All rights reserved. 4987e5972SCameron Grant * 5987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 6987e5972SCameron Grant * modification, are permitted provided that the following conditions 7987e5972SCameron Grant * are met: 8987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 9987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 10987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 11987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 12987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 13987e5972SCameron Grant * 14987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24987e5972SCameron Grant * SUCH DAMAGE. 25987e5972SCameron Grant */ 26987e5972SCameron Grant 27ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 28ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h> 29f9eb1409SOrion Hodson #include <dev/sound/pcm/ac97_patch.h> 30987e5972SCameron Grant 310f55ac6cSCameron Grant #include "mixer_if.h" 32987e5972SCameron Grant 3367b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 3467b1dce3SCameron Grant 350f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 36987e5972SCameron Grant 3779bb7d52SCameron Grant struct ac97mixtable_entry { 3896524a52SOrion Hodson int reg:8; /* register index */ 3996524a52SOrion Hodson /* reg < 0 if inverted polarity */ 4096524a52SOrion Hodson unsigned bits:4; /* width of control field */ 4196524a52SOrion Hodson unsigned ofs:4; /* offset (only if stereo=0) */ 4296524a52SOrion Hodson unsigned stereo:1; /* set for stereo controls */ 4396524a52SOrion Hodson unsigned mute:1; /* bit15 is MUTE */ 4496524a52SOrion Hodson unsigned recidx:4; /* index in rec mux */ 4596524a52SOrion Hodson unsigned mask:1; /* use only masked bits */ 4696524a52SOrion Hodson unsigned enable:1; /* entry is enabled */ 4779bb7d52SCameron Grant }; 4879bb7d52SCameron Grant 4966ef8af5SCameron Grant #define AC97_NAMELEN 16 5066ef8af5SCameron Grant struct ac97_info { 5166ef8af5SCameron Grant kobj_t methods; 5266ef8af5SCameron Grant device_t dev; 5366ef8af5SCameron Grant void *devinfo; 5419921b23SOrion Hodson u_int32_t id; 5566ef8af5SCameron Grant unsigned count, caps, se, extcaps, extid, extstat, noext:1; 5679bb7d52SCameron Grant u_int32_t flags; 5766ef8af5SCameron Grant struct ac97mixtable_entry mix[32]; 5866ef8af5SCameron Grant char name[AC97_NAMELEN]; 5900acb133SCameron Grant struct mtx *lock; 6066ef8af5SCameron Grant }; 6166ef8af5SCameron Grant 6219921b23SOrion Hodson struct ac97_vendorid { 6319921b23SOrion Hodson u_int32_t id; 6419921b23SOrion Hodson const char *name; 6519921b23SOrion Hodson }; 6619921b23SOrion Hodson 67987e5972SCameron Grant struct ac97_codecid { 6819921b23SOrion Hodson u_int32_t id; 6919921b23SOrion Hodson u_int8_t stepmask; 7019921b23SOrion Hodson u_int8_t noext:1; 71987e5972SCameron Grant char *name; 72f9eb1409SOrion Hodson ac97_patch patch; 73987e5972SCameron Grant }; 74987e5972SCameron Grant 75987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = { 7696524a52SOrion Hodson /* [offset] reg bits of st mu re mk en */ 77341f16ccSCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 78108082c4SOrion Hodson [SOUND_MIXER_MONITOR] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 }, 79341f16ccSCameron Grant [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 80341f16ccSCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 81341f16ccSCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 82341f16ccSCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 83341f16ccSCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 84341f16ccSCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 85341f16ccSCameron Grant [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 8696524a52SOrion Hodson [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 1, 1 }, 8796524a52SOrion Hodson /* use igain for the mic 20dB boost */ 8896524a52SOrion Hodson [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 }, 89341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 90341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 91341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 92341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 93987e5972SCameron Grant }; 94987e5972SCameron Grant 9519921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = { 9619921b23SOrion Hodson { 0x41445300, "Analog Devices" }, 9719921b23SOrion Hodson { 0x414b4d00, "Asahi Kasei" }, 9819921b23SOrion Hodson { 0x414c4300, "Realtek" }, 9919921b23SOrion Hodson { 0x414c4700, "Avance Logic" }, 10019921b23SOrion Hodson { 0x43525900, "Cirrus Logic" }, 10119921b23SOrion Hodson { 0x434d4900, "C-Media Electronics" }, 10219921b23SOrion Hodson { 0x43585400, "Conexant" }, 10361a0da1dSOrion Hodson { 0x454d4300, "eMicro" }, 10419921b23SOrion Hodson { 0x45838300, "ESS Technology" }, 10519921b23SOrion Hodson { 0x49434500, "ICEnsemble" }, 10619921b23SOrion Hodson { 0x4e534300, "National Semiconductor" }, 10719921b23SOrion Hodson { 0x50534300, "Philips Semiconductor" }, 10819921b23SOrion Hodson { 0x83847600, "SigmaTel" }, 10919921b23SOrion Hodson { 0x53494c00, "Silicon Laboratory" }, 11019921b23SOrion Hodson { 0x54524100, "TriTech" }, 11119921b23SOrion Hodson { 0x56494100, "VIA Technologies" }, 11219921b23SOrion Hodson { 0x574d4c00, "Wolfson" }, 11319921b23SOrion Hodson { 0x594d4800, "Yamaha" }, 11419921b23SOrion Hodson { 0x00000000, NULL } 11519921b23SOrion Hodson }; 11619921b23SOrion Hodson 117987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 11819921b23SOrion Hodson { 0x41445303, 0x00, 0, "AD1819", 0 }, 11919921b23SOrion Hodson { 0x41445340, 0x00, 0, "AD1881", 0 }, 12019921b23SOrion Hodson { 0x41445348, 0x00, 0, "AD1881A", 0 }, 12119921b23SOrion Hodson { 0x41445360, 0x00, 0, "AD1885", 0 }, 12219921b23SOrion Hodson { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 123ba548c64SOrion Hodson { 0x41445362, 0x00, 0, "AD1887", 0 }, 124ba548c64SOrion Hodson { 0x41445363, 0x00, 0, "AD1886A", 0 }, 125ba548c64SOrion Hodson { 0x41445370, 0x00, 0, "AD1980", 0 }, 126ba548c64SOrion Hodson { 0x41445372, 0x00, 0, "AD1981A", 0 }, 127ba548c64SOrion Hodson { 0x41445374, 0x00, 0, "AD1981B", 0 }, 128ba548c64SOrion Hodson { 0x41445375, 0x00, 0, "AD1985", 0 }, 12919921b23SOrion Hodson { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 13019921b23SOrion Hodson { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 13119921b23SOrion Hodson { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 13219921b23SOrion Hodson { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 1339963235aSOrion Hodson { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 13419921b23SOrion Hodson { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 13519921b23SOrion Hodson { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 13619921b23SOrion Hodson { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 13719921b23SOrion Hodson { 0x43525900, 0x07, 0, "CS4297", 0 }, 13819921b23SOrion Hodson { 0x43525910, 0x07, 0, "CS4297A", 0 }, 13919921b23SOrion Hodson { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 14019921b23SOrion Hodson { 0x43525930, 0x07, 0, "CS4299", 0 }, 14119921b23SOrion Hodson { 0x43525940, 0x07, 0, "CS4201", 0 }, 142b2a0f525SOrion Hodson { 0x43525958, 0x07, 0, "CS4205", 0 }, 14319921b23SOrion Hodson { 0x43525960, 0x07, 0, "CS4291A", 0 }, 14419921b23SOrion Hodson { 0x434d4961, 0x00, 0, "CMI9739", 0 }, 14519921b23SOrion Hodson { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 14619921b23SOrion Hodson { 0x43585429, 0x00, 0, "CX20468", 0 }, 14761a0da1dSOrion Hodson { 0x454d4323, 0x00, 0, "EM28023", 0 }, 14861a0da1dSOrion Hodson { 0x454d4328, 0x00, 0, "EM28028", 0 }, 14919921b23SOrion Hodson { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 15019921b23SOrion Hodson { 0x49434501, 0x00, 0, "ICE1230", 0 }, 15119921b23SOrion Hodson { 0x49434511, 0x00, 0, "ICE1232", 0 }, 15219921b23SOrion Hodson { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 15319921b23SOrion Hodson { 0x49434551, 0x00, 0, "VT1616", 0 }, /* Via badged ICE */ 15419921b23SOrion Hodson { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 15519921b23SOrion Hodson { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 15619921b23SOrion Hodson { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 15719921b23SOrion Hodson { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 15819921b23SOrion Hodson { 0x4e534331, 0x00, 0, "LM4549", 0 }, /* (?) */ 15919921b23SOrion Hodson { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 16019921b23SOrion Hodson { 0x4e534350, 0x00, 0, "LM4550", 0 }, 16119921b23SOrion Hodson { 0x50534301, 0x00, 0, "UCB1510", 0 }, 16219921b23SOrion Hodson { 0x50534304, 0x00, 0, "UCB1400", 0 }, 16319921b23SOrion Hodson { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 16419921b23SOrion Hodson { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 16519921b23SOrion Hodson { 0x83847605, 0x00, 0, "STAC9704", 0 }, 16619921b23SOrion Hodson { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 16719921b23SOrion Hodson { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 16819921b23SOrion Hodson { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 16919921b23SOrion Hodson { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 17019921b23SOrion Hodson { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 17119921b23SOrion Hodson { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 17219921b23SOrion Hodson { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 17319921b23SOrion Hodson { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 17419921b23SOrion Hodson { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 17519921b23SOrion Hodson { 0x53494c22, 0x00, 0, "Si3036", 0 }, 17619921b23SOrion Hodson { 0x53494c23, 0x00, 0, "Si3038", 0 }, 17719921b23SOrion Hodson { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 17819921b23SOrion Hodson { 0x54524106, 0x00, 0, "TR28026", 0 }, 17919921b23SOrion Hodson { 0x54524108, 0x00, 0, "TR28028", 0 }, 18019921b23SOrion Hodson { 0x54524123, 0x00, 0, "TR28602", 0 }, 18119921b23SOrion Hodson { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 18219921b23SOrion Hodson { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 18319921b23SOrion Hodson { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 18419921b23SOrion Hodson { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 18519921b23SOrion Hodson { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 18619921b23SOrion Hodson { 0x594d4800, 0x00, 0, "YMF743", 0 }, 18719921b23SOrion Hodson { 0x594d4802, 0x00, 0, "YMF752", 0 }, 18819921b23SOrion Hodson { 0x594d4803, 0x00, 0, "YMF753", 0 }, 18919921b23SOrion Hodson { 0, 0, 0, NULL, 0 } 190987e5972SCameron Grant }; 191987e5972SCameron Grant 192987e5972SCameron Grant static char *ac97enhancement[] = { 19304553e63SCameron Grant "no 3D Stereo Enhancement", 194987e5972SCameron Grant "Analog Devices Phat Stereo", 195987e5972SCameron Grant "Creative Stereo Enhancement", 196987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 197987e5972SCameron Grant "Yamaha Ymersion", 198987e5972SCameron Grant "BBE 3D Stereo Enhancement", 199987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 200987e5972SCameron Grant "Qsound QXpander", 201987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 202987e5972SCameron Grant "SRS 3D Stereo Enhancement", 203987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 204987e5972SCameron Grant "AKM 3D Audio", 205987e5972SCameron Grant "Aureal Stereo Enhancement", 206987e5972SCameron Grant "Aztech 3D Enhancement", 207987e5972SCameron Grant "Binaura 3D Audio Enhancement", 208987e5972SCameron Grant "ESS Technology Stereo Enhancement", 209987e5972SCameron Grant "Harman International VMAx", 210987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 211987e5972SCameron Grant "Philips Incredible Sound", 212987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 213987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 214987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 215987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 216987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 217987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 218987e5972SCameron Grant "Delta Integration 3D Enhancement", 219987e5972SCameron Grant "SigmaTel 3D Enhancement", 220987e5972SCameron Grant "Reserved 27", 221987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 222987e5972SCameron Grant "Reserved 29", 223987e5972SCameron Grant "Reserved 30", 224987e5972SCameron Grant "Reserved 31" 225987e5972SCameron Grant }; 226987e5972SCameron Grant 227987e5972SCameron Grant static char *ac97feature[] = { 228987e5972SCameron Grant "mic channel", 229987e5972SCameron Grant "reserved", 230987e5972SCameron Grant "tone", 231987e5972SCameron Grant "simulated stereo", 232987e5972SCameron Grant "headphone", 233987e5972SCameron Grant "bass boost", 234987e5972SCameron Grant "18 bit DAC", 235987e5972SCameron Grant "20 bit DAC", 236987e5972SCameron Grant "18 bit ADC", 237987e5972SCameron Grant "20 bit ADC" 238987e5972SCameron Grant }; 239987e5972SCameron Grant 24039004e69SCameron Grant static char *ac97extfeature[] = { 24139004e69SCameron Grant "variable rate PCM", 24239004e69SCameron Grant "double rate PCM", 24339004e69SCameron Grant "reserved 1", 24439004e69SCameron Grant "variable rate mic", 24539004e69SCameron Grant "reserved 2", 24639004e69SCameron Grant "reserved 3", 24739004e69SCameron Grant "center DAC", 24839004e69SCameron Grant "surround DAC", 24939004e69SCameron Grant "LFE DAC", 25039004e69SCameron Grant "AMAP", 25139004e69SCameron Grant "reserved 4", 25239004e69SCameron Grant "reserved 5", 25339004e69SCameron Grant "reserved 6", 25439004e69SCameron Grant "reserved 7", 25539004e69SCameron Grant }; 25639004e69SCameron Grant 257f9eb1409SOrion Hodson u_int16_t 258f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg) 25939004e69SCameron Grant { 2600f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 26139004e69SCameron Grant } 26239004e69SCameron Grant 263f9eb1409SOrion Hodson void 264f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 26539004e69SCameron Grant { 2660f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 26739004e69SCameron Grant } 26839004e69SCameron Grant 269c6d4b83aSOrion Hodson static void 270c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec) 271c6d4b83aSOrion Hodson { 272c6d4b83aSOrion Hodson u_int32_t i, ps; 273f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RESET, 0); 274c6d4b83aSOrion Hodson for (i = 0; i < 500; i++) { 275f9eb1409SOrion Hodson ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 276c6d4b83aSOrion Hodson if (ps == AC97_POWER_STATUS) 277c6d4b83aSOrion Hodson return; 278c6d4b83aSOrion Hodson DELAY(1000); 279c6d4b83aSOrion Hodson } 280a825c6e5SOrion Hodson device_printf(codec->dev, "AC97 reset timed out.\n"); 281c6d4b83aSOrion Hodson } 282c6d4b83aSOrion Hodson 28339004e69SCameron Grant int 28439004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 28539004e69SCameron Grant { 28639004e69SCameron Grant u_int16_t v; 28739004e69SCameron Grant 28839004e69SCameron Grant switch(which) { 28939004e69SCameron Grant case AC97_REGEXT_FDACRATE: 29039004e69SCameron Grant case AC97_REGEXT_SDACRATE: 29139004e69SCameron Grant case AC97_REGEXT_LDACRATE: 29239004e69SCameron Grant case AC97_REGEXT_LADCRATE: 29339004e69SCameron Grant case AC97_REGEXT_MADCRATE: 29439004e69SCameron Grant break; 29539004e69SCameron Grant 29639004e69SCameron Grant default: 29739004e69SCameron Grant return -1; 29839004e69SCameron Grant } 29939004e69SCameron Grant 30066ef8af5SCameron Grant snd_mtxlock(codec->lock); 30139004e69SCameron Grant if (rate != 0) { 30239004e69SCameron Grant v = rate; 30339004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 30439004e69SCameron Grant v >>= 1; 305f9eb1409SOrion Hodson ac97_wrcd(codec, which, v); 30639004e69SCameron Grant } 307f9eb1409SOrion Hodson v = ac97_rdcd(codec, which); 30839004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 30939004e69SCameron Grant v <<= 1; 31066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 31139004e69SCameron Grant return v; 31239004e69SCameron Grant } 31339004e69SCameron Grant 31439004e69SCameron Grant int 31539004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 31639004e69SCameron Grant { 31739004e69SCameron Grant mode &= AC97_EXTCAPS; 318647fbfebSOrion Hodson if ((mode & ~codec->extcaps) != 0) { 319647fbfebSOrion Hodson device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 320647fbfebSOrion Hodson mode); 32139004e69SCameron Grant return -1; 322647fbfebSOrion Hodson } 32366ef8af5SCameron Grant snd_mtxlock(codec->lock); 324f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 325f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 32666ef8af5SCameron Grant snd_mtxunlock(codec->lock); 32739004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 32839004e69SCameron Grant } 32939004e69SCameron Grant 3309ec437a3SCameron Grant u_int16_t 3319ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 3329ec437a3SCameron Grant { 3339ec437a3SCameron Grant return codec->extstat; 3349ec437a3SCameron Grant } 3359ec437a3SCameron Grant 3369ec437a3SCameron Grant u_int16_t 3379ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 3389ec437a3SCameron Grant { 3399ec437a3SCameron Grant return codec->extcaps; 3409ec437a3SCameron Grant } 3419ec437a3SCameron Grant 3425d91ad67SCameron Grant u_int16_t 3435d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec) 3445d91ad67SCameron Grant { 3455d91ad67SCameron Grant return codec->caps; 3465d91ad67SCameron Grant } 3475d91ad67SCameron Grant 348987e5972SCameron Grant static int 349987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 350987e5972SCameron Grant { 351987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 352341f16ccSCameron Grant 353987e5972SCameron Grant if (e->recidx > 0) { 354987e5972SCameron Grant int val = e->recidx - 1; 355987e5972SCameron Grant val |= val << 8; 35666ef8af5SCameron Grant snd_mtxlock(codec->lock); 357f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RECSEL, val); 35866ef8af5SCameron Grant snd_mtxunlock(codec->lock); 359987e5972SCameron Grant return 0; 36039004e69SCameron Grant } else 36139004e69SCameron Grant return -1; 362987e5972SCameron Grant } 363987e5972SCameron Grant 364987e5972SCameron Grant static int 365987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 366987e5972SCameron Grant { 367987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 368341f16ccSCameron Grant 369341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 37096524a52SOrion Hodson int mask, max, val, reg; 37196524a52SOrion Hodson 37296524a52SOrion Hodson reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */ 37396524a52SOrion Hodson max = (1 << e->bits) - 1; /* actual range */ 37496524a52SOrion Hodson mask = (max << 8) | max; /* bits of interest */ 375987e5972SCameron Grant 37639004e69SCameron Grant if (!e->stereo) 37739004e69SCameron Grant right = left; 37896524a52SOrion Hodson 37996524a52SOrion Hodson /* 38096524a52SOrion Hodson * Invert the range if the polarity requires so, 38196524a52SOrion Hodson * then scale to 0..max-1 to compute the value to 38296524a52SOrion Hodson * write into the codec, and scale back to 0..100 38396524a52SOrion Hodson * for the return value. 38496524a52SOrion Hodson */ 385987e5972SCameron Grant if (e->reg > 0) { 386987e5972SCameron Grant left = 100 - left; 387987e5972SCameron Grant right = 100 - right; 388987e5972SCameron Grant } 389987e5972SCameron Grant 390987e5972SCameron Grant left = (left * max) / 100; 391987e5972SCameron Grant right = (right * max) / 100; 392987e5972SCameron Grant 393987e5972SCameron Grant val = (left << 8) | right; 394987e5972SCameron Grant 395987e5972SCameron Grant left = (left * 100) / max; 396987e5972SCameron Grant right = (right * 100) / max; 397987e5972SCameron Grant 398987e5972SCameron Grant if (e->reg > 0) { 399987e5972SCameron Grant left = 100 - left; 400987e5972SCameron Grant right = 100 - right; 401987e5972SCameron Grant } 402987e5972SCameron Grant 40396524a52SOrion Hodson /* 40496524a52SOrion Hodson * For mono controls, trim val and mask, also taking 40596524a52SOrion Hodson * care of e->ofs (offset of control field). 40696524a52SOrion Hodson */ 40796524a52SOrion Hodson if (e->ofs) { 408987e5972SCameron Grant val &= max; 409987e5972SCameron Grant val <<= e->ofs; 41096524a52SOrion Hodson mask = (max << e->ofs); 41196524a52SOrion Hodson } 41296524a52SOrion Hodson 41396524a52SOrion Hodson /* 41496524a52SOrion Hodson * If we have a mute bit, add it to the mask and 41596524a52SOrion Hodson * update val and set mute if both channels require a 41696524a52SOrion Hodson * zero volume. 41796524a52SOrion Hodson */ 41896524a52SOrion Hodson if (e->mute == 1) { 41996524a52SOrion Hodson mask |= AC97_MUTE; 42096524a52SOrion Hodson if (left == 0 && right == 0) 42196524a52SOrion Hodson val = AC97_MUTE; 42296524a52SOrion Hodson } 42396524a52SOrion Hodson 42496524a52SOrion Hodson /* 42596524a52SOrion Hodson * If the mask bit is set, do not alter the other bits. 42696524a52SOrion Hodson */ 42796524a52SOrion Hodson snd_mtxlock(codec->lock); 428987e5972SCameron Grant if (e->mask) { 429f9eb1409SOrion Hodson int cur = ac97_rdcd(codec, e->reg); 43096524a52SOrion Hodson val |= cur & ~(mask); 431987e5972SCameron Grant } 432f9eb1409SOrion Hodson ac97_wrcd(codec, reg, val); 43366ef8af5SCameron Grant snd_mtxunlock(codec->lock); 434987e5972SCameron Grant return left | (right << 8); 435341f16ccSCameron Grant } else { 436341f16ccSCameron Grant /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 43739004e69SCameron Grant return -1; 438987e5972SCameron Grant } 439341f16ccSCameron Grant } 440987e5972SCameron Grant 441987e5972SCameron Grant #if 0 442987e5972SCameron Grant static int 443987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel) 444987e5972SCameron Grant { 445987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 446987e5972SCameron Grant if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 447987e5972SCameron Grant int max, val, volume; 448987e5972SCameron Grant 449987e5972SCameron Grant max = (1 << e->bits) - 1; 450f9eb1409SOrion Hodson val = ac97_rdcd(code, e->reg); 45139004e69SCameron Grant if (val == AC97_MUTE && e->mute == 1) 45239004e69SCameron Grant volume = 0; 453987e5972SCameron Grant else { 454987e5972SCameron Grant if (e->stereo == 0) val >>= e->ofs; 455987e5972SCameron Grant val &= max; 456987e5972SCameron Grant volume = (val * 100) / max; 457987e5972SCameron Grant if (e->reg > 0) volume = 100 - volume; 458987e5972SCameron Grant } 459987e5972SCameron Grant return volume; 46039004e69SCameron Grant } else 46139004e69SCameron Grant return -1; 462987e5972SCameron Grant } 463987e5972SCameron Grant #endif 464987e5972SCameron Grant 465108082c4SOrion Hodson static void 466108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec) 467108082c4SOrion Hodson { 468108082c4SOrion Hodson /* Determine what AUXOUT really means, it can be: 469108082c4SOrion Hodson * 470108082c4SOrion Hodson * 1. Headphone out. 471108082c4SOrion Hodson * 2. 4-Channel Out 472108082c4SOrion Hodson * 3. True line level out (effectively master volume). 473108082c4SOrion Hodson * 474108082c4SOrion Hodson * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 475108082c4SOrion Hodson */ 476108082c4SOrion Hodson if (codec->caps & AC97_CAP_HEADPHONE) { 477108082c4SOrion Hodson /* XXX We should probably check the AUX_OUT initial value. 478108082c4SOrion Hodson * Leave AC97_MIX_AUXOUT - SOUND_MIXER_MONITOR relationship */ 479108082c4SOrion Hodson return; 480108082c4SOrion Hodson } else if (codec->extcaps & AC97_EXTCAP_SDAC && 481f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 482108082c4SOrion Hodson /* 4-Channel Out, add an additional gain setting. */ 483108082c4SOrion Hodson codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR]; 484108082c4SOrion Hodson } else { 485108082c4SOrion Hodson /* Master volume is/maybe fixed in h/w, not sufficiently 486108082c4SOrion Hodson * clear in spec to blat SOUND_MIXER_MASTER. */ 487108082c4SOrion Hodson codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR]; 488108082c4SOrion Hodson } 489108082c4SOrion Hodson /* Blat monitor, inappropriate label if we get here */ 490108082c4SOrion Hodson bzero(&codec->mix[SOUND_MIXER_MONITOR], 491108082c4SOrion Hodson sizeof(codec->mix[SOUND_MIXER_MONITOR])); 492108082c4SOrion Hodson } 493108082c4SOrion Hodson 49419921b23SOrion Hodson static const char* 49519921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 49619921b23SOrion Hodson { 49719921b23SOrion Hodson if (cname == NULL) { 49819921b23SOrion Hodson sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 49919921b23SOrion Hodson return buf; 50019921b23SOrion Hodson } 50119921b23SOrion Hodson 50219921b23SOrion Hodson if (vname == NULL) vname = "Unknown"; 50319921b23SOrion Hodson 50419921b23SOrion Hodson if (bootverbose) { 50519921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 50619921b23SOrion Hodson } else { 50719921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec", vname, cname); 50819921b23SOrion Hodson } 50919921b23SOrion Hodson return buf; 51019921b23SOrion Hodson } 51119921b23SOrion Hodson 512987e5972SCameron Grant static unsigned 51339004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 514987e5972SCameron Grant { 515f9eb1409SOrion Hodson ac97_patch codec_patch; 51619921b23SOrion Hodson const char *cname, *vname; 51719921b23SOrion Hodson char desc[80]; 51819921b23SOrion Hodson u_int8_t model, step; 519341f16ccSCameron Grant unsigned i, j, k, old; 520987e5972SCameron Grant u_int32_t id; 521987e5972SCameron Grant 52266ef8af5SCameron Grant snd_mtxlock(codec->lock); 5230f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 524cd2c103aSCameron Grant if (codec->count == 0) { 52504553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 52666ef8af5SCameron Grant snd_mtxunlock(codec->lock); 52704553e63SCameron Grant return ENODEV; 52804553e63SCameron Grant } 5299ec437a3SCameron Grant 530f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 531c6d4b83aSOrion Hodson ac97_reset(codec); 532f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 533987e5972SCameron Grant 534f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REG_RESET); 535987e5972SCameron Grant codec->caps = i & 0x03ff; 536987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 537987e5972SCameron Grant 538f9eb1409SOrion Hodson id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 539e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 540e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 54166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 542e620d959SCameron Grant return ENODEV; 543e620d959SCameron Grant } 5446b4b88f7SCameron Grant 54519921b23SOrion Hodson codec->id = id; 546cd2c103aSCameron Grant codec->noext = 0; 547f9eb1409SOrion Hodson codec_patch = NULL; 54819921b23SOrion Hodson 54919921b23SOrion Hodson cname = NULL; 55019921b23SOrion Hodson model = step = 0; 551cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 55219921b23SOrion Hodson u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 55319921b23SOrion Hodson if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 554cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 555f9eb1409SOrion Hodson codec_patch = ac97codecid[i].patch; 55619921b23SOrion Hodson cname = ac97codecid[i].name; 55719921b23SOrion Hodson model = (id & modelmask) & 0xff; 55819921b23SOrion Hodson step = (id & ~modelmask) & 0xff; 55919921b23SOrion Hodson break; 56019921b23SOrion Hodson } 56119921b23SOrion Hodson } 56219921b23SOrion Hodson 56319921b23SOrion Hodson vname = NULL; 56419921b23SOrion Hodson for (i = 0; ac97vendorid[i].id; i++) { 56519921b23SOrion Hodson if (ac97vendorid[i].id == (id & 0xffffff00)) { 56619921b23SOrion Hodson vname = ac97vendorid[i].name; 56719921b23SOrion Hodson break; 568cd2c103aSCameron Grant } 569cd2c103aSCameron Grant } 5706b4b88f7SCameron Grant 571cd2c103aSCameron Grant codec->extcaps = 0; 572cd2c103aSCameron Grant codec->extid = 0; 573cd2c103aSCameron Grant codec->extstat = 0; 5746a6ee5bbSCameron Grant if (!codec->noext) { 575f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REGEXT_ID); 5766a6ee5bbSCameron Grant if (i != 0xffff) { 57739004e69SCameron Grant codec->extcaps = i & 0x3fff; 57839004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 579f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 5806b4b88f7SCameron Grant } 5816a6ee5bbSCameron Grant } 582987e5972SCameron Grant 583341f16ccSCameron Grant for (i = 0; i < 32; i++) { 584108082c4SOrion Hodson codec->mix[i] = ac97mixtable_default[i]; 585108082c4SOrion Hodson } 586108082c4SOrion Hodson ac97_fix_auxout(codec); 587f9eb1409SOrion Hodson if (codec_patch) 588f9eb1409SOrion Hodson codec_patch(codec); 589108082c4SOrion Hodson 590108082c4SOrion Hodson for (i = 0; i < 32; i++) { 59133c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 59233c878f0SCameron Grant if (k && (codec->mix[i].reg > 0)) { 593f9eb1409SOrion Hodson old = ac97_rdcd(codec, codec->mix[i].reg); 594f9eb1409SOrion Hodson ac97_wrcd(codec, codec->mix[i].reg, 0x3f); 595f9eb1409SOrion Hodson j = ac97_rdcd(codec, codec->mix[i].reg); 596f9eb1409SOrion Hodson ac97_wrcd(codec, codec->mix[i].reg, old); 5976a6ee5bbSCameron Grant codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 598341f16ccSCameron Grant for (k = 1; j & (1 << k); k++); 599341f16ccSCameron Grant codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 600341f16ccSCameron Grant } 601341f16ccSCameron Grant /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 602341f16ccSCameron Grant } 603987e5972SCameron Grant 60419921b23SOrion Hodson device_printf(codec->dev, "<%s>\n", 60519921b23SOrion Hodson ac97_hw_desc(codec->id, vname, cname, desc)); 606a825c6e5SOrion Hodson 607987e5972SCameron Grant if (bootverbose) { 60819921b23SOrion Hodson device_printf(codec->dev, "Codec features "); 60939004e69SCameron Grant for (i = j = 0; i < 10; i++) 61039004e69SCameron Grant if (codec->caps & (1 << i)) 61139004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 61239004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 613987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 61439004e69SCameron Grant 61539004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 61619921b23SOrion Hodson device_printf(codec->dev, "%s codec", 61719921b23SOrion Hodson codec->extid? "Secondary" : "Primary"); 61839004e69SCameron Grant if (codec->extcaps) 61939004e69SCameron Grant printf(" extended features "); 62039004e69SCameron Grant for (i = j = 0; i < 14; i++) 62139004e69SCameron Grant if (codec->extcaps & (1 << i)) 62239004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 62339004e69SCameron Grant printf("\n"); 62439004e69SCameron Grant } 625987e5972SCameron Grant } 626987e5972SCameron Grant 627f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 62803a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 62966ef8af5SCameron Grant snd_mtxunlock(codec->lock); 630987e5972SCameron Grant return 0; 631987e5972SCameron Grant } 632987e5972SCameron Grant 6339ec437a3SCameron Grant static unsigned 6349ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 6359ec437a3SCameron Grant { 63666ef8af5SCameron Grant snd_mtxlock(codec->lock); 6370f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 6389ec437a3SCameron Grant if (codec->count == 0) { 6399ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 64066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 6419ec437a3SCameron Grant return ENODEV; 6429ec437a3SCameron Grant } 6439ec437a3SCameron Grant 644f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 645c6d4b83aSOrion Hodson ac97_reset(codec); 646f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 6479ec437a3SCameron Grant 6489ec437a3SCameron Grant if (!codec->noext) { 649f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 650f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 651b60e55dbSGuido van Rooij != codec->extstat) 6529ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 653b60e55dbSGuido van Rooij codec->extstat, 654f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_REGEXT_STAT) & 655b60e55dbSGuido van Rooij AC97_EXTCAPS); 6569ec437a3SCameron Grant } 6579ec437a3SCameron Grant 658f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 6599ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 66066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 6619ec437a3SCameron Grant return 0; 6629ec437a3SCameron Grant } 6639ec437a3SCameron Grant 664987e5972SCameron Grant struct ac97_info * 6650f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 666987e5972SCameron Grant { 667987e5972SCameron Grant struct ac97_info *codec; 668987e5972SCameron Grant 6690f55ac6cSCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 6700f55ac6cSCameron Grant if (codec == NULL) 6710f55ac6cSCameron Grant return NULL; 6720f55ac6cSCameron Grant 67366ef8af5SCameron Grant snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 674489c22ebSJohn Baldwin codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 675a163d034SWarner Losh codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 6760f55ac6cSCameron Grant if (codec->methods == NULL) { 67766ef8af5SCameron Grant snd_mtxlock(codec->lock); 67866ef8af5SCameron Grant snd_mtxfree(codec->lock); 6790f55ac6cSCameron Grant free(codec, M_AC97); 6800f55ac6cSCameron Grant return NULL; 681987e5972SCameron Grant } 6820f55ac6cSCameron Grant 6830f55ac6cSCameron Grant codec->dev = dev; 6840f55ac6cSCameron Grant codec->devinfo = devinfo; 68579bb7d52SCameron Grant codec->flags = 0; 686987e5972SCameron Grant return codec; 687987e5972SCameron Grant } 688987e5972SCameron Grant 68933dbf14aSCameron Grant void 69033dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 69133dbf14aSCameron Grant { 69266ef8af5SCameron Grant snd_mtxlock(codec->lock); 6930f55ac6cSCameron Grant if (codec->methods != NULL) 6940f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 69566ef8af5SCameron Grant snd_mtxfree(codec->lock); 6960f55ac6cSCameron Grant free(codec, M_AC97); 69733dbf14aSCameron Grant } 69833dbf14aSCameron Grant 69979bb7d52SCameron Grant void 70079bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 70179bb7d52SCameron Grant { 70279bb7d52SCameron Grant codec->flags = val; 70379bb7d52SCameron Grant } 70479bb7d52SCameron Grant 70579bb7d52SCameron Grant u_int32_t 70679bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 70779bb7d52SCameron Grant { 70879bb7d52SCameron Grant return codec->flags; 70979bb7d52SCameron Grant } 71079bb7d52SCameron Grant 7110f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 7120f55ac6cSCameron Grant 713987e5972SCameron Grant static int 71466ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 715987e5972SCameron Grant { 716987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 717341f16ccSCameron Grant u_int32_t i, mask; 718341f16ccSCameron Grant 71939004e69SCameron Grant if (codec == NULL) 72039004e69SCameron Grant return -1; 721341f16ccSCameron Grant 722e620d959SCameron Grant if (ac97_initmixer(codec)) 723e620d959SCameron Grant return -1; 724341f16ccSCameron Grant 725341f16ccSCameron Grant mask = 0; 726341f16ccSCameron Grant for (i = 0; i < 32; i++) 727341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 728341f16ccSCameron Grant mix_setdevs(m, mask); 729341f16ccSCameron Grant 730341f16ccSCameron Grant mask = 0; 731341f16ccSCameron Grant for (i = 0; i < 32; i++) 732341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 733341f16ccSCameron Grant mix_setrecdevs(m, mask); 734987e5972SCameron Grant return 0; 735987e5972SCameron Grant } 736987e5972SCameron Grant 737987e5972SCameron Grant static int 73866ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 73933dbf14aSCameron Grant { 74033dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 741341f16ccSCameron Grant 74233dbf14aSCameron Grant if (codec == NULL) 74333dbf14aSCameron Grant return -1; 74433dbf14aSCameron Grant /* 74533dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 74633dbf14aSCameron Grant return -1; 74733dbf14aSCameron Grant */ 74833dbf14aSCameron Grant ac97_destroy(codec); 74933dbf14aSCameron Grant return 0; 75033dbf14aSCameron Grant } 75133dbf14aSCameron Grant 75233dbf14aSCameron Grant static int 75366ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 7549ec437a3SCameron Grant { 7559ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 7569ec437a3SCameron Grant 7579ec437a3SCameron Grant if (codec == NULL) 7589ec437a3SCameron Grant return -1; 7599ec437a3SCameron Grant return ac97_reinitmixer(codec); 7609ec437a3SCameron Grant } 7619ec437a3SCameron Grant 7629ec437a3SCameron Grant static int 76366ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 764987e5972SCameron Grant { 765987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 766341f16ccSCameron Grant 76739004e69SCameron Grant if (codec == NULL) 76839004e69SCameron Grant return -1; 769987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 770987e5972SCameron Grant } 771987e5972SCameron Grant 772987e5972SCameron Grant static int 77366ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 774987e5972SCameron Grant { 775987e5972SCameron Grant int i; 776987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 777341f16ccSCameron Grant 77839004e69SCameron Grant if (codec == NULL) 77939004e69SCameron Grant return -1; 780987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 78139004e69SCameron Grant if ((src & (1 << i)) != 0) 78239004e69SCameron Grant break; 783987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 784987e5972SCameron Grant } 785987e5972SCameron Grant 7860f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 7870f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 7880f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 7890f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 7900f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 7910f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 7920f55ac6cSCameron Grant { 0, 0 } 793987e5972SCameron Grant }; 7940f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 7950f55ac6cSCameron Grant 7960f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 7970f55ac6cSCameron Grant 7980f55ac6cSCameron Grant kobj_class_t 7990f55ac6cSCameron Grant ac97_getmixerclass(void) 8000f55ac6cSCameron Grant { 8010f55ac6cSCameron Grant return &ac97mixer_class; 8020f55ac6cSCameron Grant } 8030f55ac6cSCameron Grant 804987e5972SCameron Grant 805