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 }, 78a52604cfSOrion Hodson [SOUND_MIXER_OGAIN] = { 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 }, 876faa2f6dSJohn Baldwin #if 0 8896524a52SOrion Hodson /* use igain for the mic 20dB boost */ 8996524a52SOrion Hodson [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 }, 906faa2f6dSJohn Baldwin #endif 91341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 92341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 93341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 94341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 95987e5972SCameron Grant }; 96987e5972SCameron Grant 9719921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = { 9819921b23SOrion Hodson { 0x41445300, "Analog Devices" }, 9919921b23SOrion Hodson { 0x414b4d00, "Asahi Kasei" }, 10019921b23SOrion Hodson { 0x414c4300, "Realtek" }, 10119921b23SOrion Hodson { 0x414c4700, "Avance Logic" }, 10219921b23SOrion Hodson { 0x43525900, "Cirrus Logic" }, 10319921b23SOrion Hodson { 0x434d4900, "C-Media Electronics" }, 10419921b23SOrion Hodson { 0x43585400, "Conexant" }, 10561a0da1dSOrion Hodson { 0x454d4300, "eMicro" }, 10619921b23SOrion Hodson { 0x45838300, "ESS Technology" }, 10719921b23SOrion Hodson { 0x49434500, "ICEnsemble" }, 10819921b23SOrion Hodson { 0x4e534300, "National Semiconductor" }, 10919921b23SOrion Hodson { 0x50534300, "Philips Semiconductor" }, 11019921b23SOrion Hodson { 0x83847600, "SigmaTel" }, 11119921b23SOrion Hodson { 0x53494c00, "Silicon Laboratory" }, 11219921b23SOrion Hodson { 0x54524100, "TriTech" }, 11319921b23SOrion Hodson { 0x56494100, "VIA Technologies" }, 11419921b23SOrion Hodson { 0x574d4c00, "Wolfson" }, 11519921b23SOrion Hodson { 0x594d4800, "Yamaha" }, 11619921b23SOrion Hodson { 0x00000000, NULL } 11719921b23SOrion Hodson }; 11819921b23SOrion Hodson 119987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 12019921b23SOrion Hodson { 0x41445303, 0x00, 0, "AD1819", 0 }, 12119921b23SOrion Hodson { 0x41445340, 0x00, 0, "AD1881", 0 }, 12219921b23SOrion Hodson { 0x41445348, 0x00, 0, "AD1881A", 0 }, 12319921b23SOrion Hodson { 0x41445360, 0x00, 0, "AD1885", 0 }, 12419921b23SOrion Hodson { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 125ba548c64SOrion Hodson { 0x41445362, 0x00, 0, "AD1887", 0 }, 126ba548c64SOrion Hodson { 0x41445363, 0x00, 0, "AD1886A", 0 }, 127a52604cfSOrion Hodson { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, 128ba548c64SOrion Hodson { 0x41445372, 0x00, 0, "AD1981A", 0 }, 129ba548c64SOrion Hodson { 0x41445374, 0x00, 0, "AD1981B", 0 }, 130a52604cfSOrion Hodson { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, 13119921b23SOrion Hodson { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 13219921b23SOrion Hodson { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 13319921b23SOrion Hodson { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 13419921b23SOrion Hodson { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 1359963235aSOrion Hodson { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 13619921b23SOrion Hodson { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 13719921b23SOrion Hodson { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 13819921b23SOrion Hodson { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 13919921b23SOrion Hodson { 0x43525900, 0x07, 0, "CS4297", 0 }, 14019921b23SOrion Hodson { 0x43525910, 0x07, 0, "CS4297A", 0 }, 14119921b23SOrion Hodson { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 14219921b23SOrion Hodson { 0x43525930, 0x07, 0, "CS4299", 0 }, 14319921b23SOrion Hodson { 0x43525940, 0x07, 0, "CS4201", 0 }, 144b2a0f525SOrion Hodson { 0x43525958, 0x07, 0, "CS4205", 0 }, 14519921b23SOrion Hodson { 0x43525960, 0x07, 0, "CS4291A", 0 }, 14619921b23SOrion Hodson { 0x434d4961, 0x00, 0, "CMI9739", 0 }, 14719921b23SOrion Hodson { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 14819921b23SOrion Hodson { 0x43585429, 0x00, 0, "CX20468", 0 }, 14961a0da1dSOrion Hodson { 0x454d4323, 0x00, 0, "EM28023", 0 }, 15061a0da1dSOrion Hodson { 0x454d4328, 0x00, 0, "EM28028", 0 }, 15119921b23SOrion Hodson { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 15219921b23SOrion Hodson { 0x49434501, 0x00, 0, "ICE1230", 0 }, 15319921b23SOrion Hodson { 0x49434511, 0x00, 0, "ICE1232", 0 }, 15419921b23SOrion Hodson { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 15549fd6905SOrion Hodson { 0x49434551, 0x03, 0, "VT1616", 0 }, /* Via badged ICE */ 15619921b23SOrion Hodson { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 15719921b23SOrion Hodson { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 15819921b23SOrion Hodson { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 15919921b23SOrion Hodson { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 16019921b23SOrion Hodson { 0x4e534331, 0x00, 0, "LM4549", 0 }, /* (?) */ 16119921b23SOrion Hodson { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 16219921b23SOrion Hodson { 0x4e534350, 0x00, 0, "LM4550", 0 }, 16319921b23SOrion Hodson { 0x50534301, 0x00, 0, "UCB1510", 0 }, 16419921b23SOrion Hodson { 0x50534304, 0x00, 0, "UCB1400", 0 }, 16519921b23SOrion Hodson { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 16619921b23SOrion Hodson { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 16719921b23SOrion Hodson { 0x83847605, 0x00, 0, "STAC9704", 0 }, 16819921b23SOrion Hodson { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 16919921b23SOrion Hodson { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 17019921b23SOrion Hodson { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 17119921b23SOrion Hodson { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 17219921b23SOrion Hodson { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 17319921b23SOrion Hodson { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 17419921b23SOrion Hodson { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 17519921b23SOrion Hodson { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 17619921b23SOrion Hodson { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 17719921b23SOrion Hodson { 0x53494c22, 0x00, 0, "Si3036", 0 }, 17819921b23SOrion Hodson { 0x53494c23, 0x00, 0, "Si3038", 0 }, 17919921b23SOrion Hodson { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 18019921b23SOrion Hodson { 0x54524106, 0x00, 0, "TR28026", 0 }, 18119921b23SOrion Hodson { 0x54524108, 0x00, 0, "TR28028", 0 }, 18219921b23SOrion Hodson { 0x54524123, 0x00, 0, "TR28602", 0 }, 18319921b23SOrion Hodson { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 18419921b23SOrion Hodson { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 18519921b23SOrion Hodson { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 18619921b23SOrion Hodson { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 18719921b23SOrion Hodson { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 18819921b23SOrion Hodson { 0x594d4800, 0x00, 0, "YMF743", 0 }, 18919921b23SOrion Hodson { 0x594d4802, 0x00, 0, "YMF752", 0 }, 19019921b23SOrion Hodson { 0x594d4803, 0x00, 0, "YMF753", 0 }, 19119921b23SOrion Hodson { 0, 0, 0, NULL, 0 } 192987e5972SCameron Grant }; 193987e5972SCameron Grant 194987e5972SCameron Grant static char *ac97enhancement[] = { 19504553e63SCameron Grant "no 3D Stereo Enhancement", 196987e5972SCameron Grant "Analog Devices Phat Stereo", 197987e5972SCameron Grant "Creative Stereo Enhancement", 198987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 199987e5972SCameron Grant "Yamaha Ymersion", 200987e5972SCameron Grant "BBE 3D Stereo Enhancement", 201987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 202987e5972SCameron Grant "Qsound QXpander", 203987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 204987e5972SCameron Grant "SRS 3D Stereo Enhancement", 205987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 206987e5972SCameron Grant "AKM 3D Audio", 207987e5972SCameron Grant "Aureal Stereo Enhancement", 208987e5972SCameron Grant "Aztech 3D Enhancement", 209987e5972SCameron Grant "Binaura 3D Audio Enhancement", 210987e5972SCameron Grant "ESS Technology Stereo Enhancement", 211987e5972SCameron Grant "Harman International VMAx", 212987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 213987e5972SCameron Grant "Philips Incredible Sound", 214987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 215987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 216987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 217987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 218987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 219987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 220987e5972SCameron Grant "Delta Integration 3D Enhancement", 221987e5972SCameron Grant "SigmaTel 3D Enhancement", 222987e5972SCameron Grant "Reserved 27", 223987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 224987e5972SCameron Grant "Reserved 29", 225987e5972SCameron Grant "Reserved 30", 226987e5972SCameron Grant "Reserved 31" 227987e5972SCameron Grant }; 228987e5972SCameron Grant 229987e5972SCameron Grant static char *ac97feature[] = { 230987e5972SCameron Grant "mic channel", 231987e5972SCameron Grant "reserved", 232987e5972SCameron Grant "tone", 233987e5972SCameron Grant "simulated stereo", 234987e5972SCameron Grant "headphone", 235987e5972SCameron Grant "bass boost", 236987e5972SCameron Grant "18 bit DAC", 237987e5972SCameron Grant "20 bit DAC", 238987e5972SCameron Grant "18 bit ADC", 239987e5972SCameron Grant "20 bit ADC" 240987e5972SCameron Grant }; 241987e5972SCameron Grant 24239004e69SCameron Grant static char *ac97extfeature[] = { 24339004e69SCameron Grant "variable rate PCM", 24439004e69SCameron Grant "double rate PCM", 24539004e69SCameron Grant "reserved 1", 24639004e69SCameron Grant "variable rate mic", 24739004e69SCameron Grant "reserved 2", 24839004e69SCameron Grant "reserved 3", 24939004e69SCameron Grant "center DAC", 25039004e69SCameron Grant "surround DAC", 25139004e69SCameron Grant "LFE DAC", 25239004e69SCameron Grant "AMAP", 25339004e69SCameron Grant "reserved 4", 25439004e69SCameron Grant "reserved 5", 25539004e69SCameron Grant "reserved 6", 25639004e69SCameron Grant "reserved 7", 25739004e69SCameron Grant }; 25839004e69SCameron Grant 259f9eb1409SOrion Hodson u_int16_t 260f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg) 26139004e69SCameron Grant { 2620f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 26339004e69SCameron Grant } 26439004e69SCameron Grant 265f9eb1409SOrion Hodson void 266f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 26739004e69SCameron Grant { 2680f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 26939004e69SCameron Grant } 27039004e69SCameron Grant 271c6d4b83aSOrion Hodson static void 272c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec) 273c6d4b83aSOrion Hodson { 274c6d4b83aSOrion Hodson u_int32_t i, ps; 275f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RESET, 0); 276c6d4b83aSOrion Hodson for (i = 0; i < 500; i++) { 277f9eb1409SOrion Hodson ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 278c6d4b83aSOrion Hodson if (ps == AC97_POWER_STATUS) 279c6d4b83aSOrion Hodson return; 280c6d4b83aSOrion Hodson DELAY(1000); 281c6d4b83aSOrion Hodson } 282a825c6e5SOrion Hodson device_printf(codec->dev, "AC97 reset timed out.\n"); 283c6d4b83aSOrion Hodson } 284c6d4b83aSOrion Hodson 28539004e69SCameron Grant int 28639004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 28739004e69SCameron Grant { 28839004e69SCameron Grant u_int16_t v; 28939004e69SCameron Grant 29039004e69SCameron Grant switch(which) { 29139004e69SCameron Grant case AC97_REGEXT_FDACRATE: 29239004e69SCameron Grant case AC97_REGEXT_SDACRATE: 29339004e69SCameron Grant case AC97_REGEXT_LDACRATE: 29439004e69SCameron Grant case AC97_REGEXT_LADCRATE: 29539004e69SCameron Grant case AC97_REGEXT_MADCRATE: 29639004e69SCameron Grant break; 29739004e69SCameron Grant 29839004e69SCameron Grant default: 29939004e69SCameron Grant return -1; 30039004e69SCameron Grant } 30139004e69SCameron Grant 30266ef8af5SCameron Grant snd_mtxlock(codec->lock); 30339004e69SCameron Grant if (rate != 0) { 30439004e69SCameron Grant v = rate; 30539004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 30639004e69SCameron Grant v >>= 1; 307f9eb1409SOrion Hodson ac97_wrcd(codec, which, v); 30839004e69SCameron Grant } 309f9eb1409SOrion Hodson v = ac97_rdcd(codec, which); 31039004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 31139004e69SCameron Grant v <<= 1; 31266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 31339004e69SCameron Grant return v; 31439004e69SCameron Grant } 31539004e69SCameron Grant 31639004e69SCameron Grant int 31739004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 31839004e69SCameron Grant { 31939004e69SCameron Grant mode &= AC97_EXTCAPS; 320647fbfebSOrion Hodson if ((mode & ~codec->extcaps) != 0) { 321647fbfebSOrion Hodson device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 322647fbfebSOrion Hodson mode); 32339004e69SCameron Grant return -1; 324647fbfebSOrion Hodson } 32566ef8af5SCameron Grant snd_mtxlock(codec->lock); 326f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 327f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 32866ef8af5SCameron Grant snd_mtxunlock(codec->lock); 32939004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 33039004e69SCameron Grant } 33139004e69SCameron Grant 3329ec437a3SCameron Grant u_int16_t 3339ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 3349ec437a3SCameron Grant { 3359ec437a3SCameron Grant return codec->extstat; 3369ec437a3SCameron Grant } 3379ec437a3SCameron Grant 3389ec437a3SCameron Grant u_int16_t 3399ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 3409ec437a3SCameron Grant { 3419ec437a3SCameron Grant return codec->extcaps; 3429ec437a3SCameron Grant } 3439ec437a3SCameron Grant 3445d91ad67SCameron Grant u_int16_t 3455d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec) 3465d91ad67SCameron Grant { 3475d91ad67SCameron Grant return codec->caps; 3485d91ad67SCameron Grant } 3495d91ad67SCameron Grant 350987e5972SCameron Grant static int 351987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 352987e5972SCameron Grant { 353987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 354341f16ccSCameron Grant 355987e5972SCameron Grant if (e->recidx > 0) { 356987e5972SCameron Grant int val = e->recidx - 1; 357987e5972SCameron Grant val |= val << 8; 35866ef8af5SCameron Grant snd_mtxlock(codec->lock); 359f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RECSEL, val); 36066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 361987e5972SCameron Grant return 0; 36239004e69SCameron Grant } else 36339004e69SCameron Grant return -1; 364987e5972SCameron Grant } 365987e5972SCameron Grant 366987e5972SCameron Grant static int 367987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 368987e5972SCameron Grant { 369987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 370341f16ccSCameron Grant 371341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 37296524a52SOrion Hodson int mask, max, val, reg; 37396524a52SOrion Hodson 37496524a52SOrion Hodson reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */ 37596524a52SOrion Hodson max = (1 << e->bits) - 1; /* actual range */ 37696524a52SOrion Hodson mask = (max << 8) | max; /* bits of interest */ 377987e5972SCameron Grant 37839004e69SCameron Grant if (!e->stereo) 37939004e69SCameron Grant right = left; 38096524a52SOrion Hodson 38196524a52SOrion Hodson /* 38296524a52SOrion Hodson * Invert the range if the polarity requires so, 38396524a52SOrion Hodson * then scale to 0..max-1 to compute the value to 38496524a52SOrion Hodson * write into the codec, and scale back to 0..100 38596524a52SOrion Hodson * for the return value. 38696524a52SOrion Hodson */ 387987e5972SCameron Grant if (e->reg > 0) { 388987e5972SCameron Grant left = 100 - left; 389987e5972SCameron Grant right = 100 - right; 390987e5972SCameron Grant } 391987e5972SCameron Grant 392987e5972SCameron Grant left = (left * max) / 100; 393987e5972SCameron Grant right = (right * max) / 100; 394987e5972SCameron Grant 395987e5972SCameron Grant val = (left << 8) | right; 396987e5972SCameron Grant 397987e5972SCameron Grant left = (left * 100) / max; 398987e5972SCameron Grant right = (right * 100) / max; 399987e5972SCameron Grant 400987e5972SCameron Grant if (e->reg > 0) { 401987e5972SCameron Grant left = 100 - left; 402987e5972SCameron Grant right = 100 - right; 403987e5972SCameron Grant } 404987e5972SCameron Grant 40596524a52SOrion Hodson /* 40696524a52SOrion Hodson * For mono controls, trim val and mask, also taking 40796524a52SOrion Hodson * care of e->ofs (offset of control field). 40896524a52SOrion Hodson */ 40996524a52SOrion Hodson if (e->ofs) { 410987e5972SCameron Grant val &= max; 411987e5972SCameron Grant val <<= e->ofs; 41296524a52SOrion Hodson mask = (max << e->ofs); 41396524a52SOrion Hodson } 41496524a52SOrion Hodson 41596524a52SOrion Hodson /* 41696524a52SOrion Hodson * If we have a mute bit, add it to the mask and 41796524a52SOrion Hodson * update val and set mute if both channels require a 41896524a52SOrion Hodson * zero volume. 41996524a52SOrion Hodson */ 42096524a52SOrion Hodson if (e->mute == 1) { 42196524a52SOrion Hodson mask |= AC97_MUTE; 42296524a52SOrion Hodson if (left == 0 && right == 0) 42396524a52SOrion Hodson val = AC97_MUTE; 42496524a52SOrion Hodson } 42596524a52SOrion Hodson 42696524a52SOrion Hodson /* 42796524a52SOrion Hodson * If the mask bit is set, do not alter the other bits. 42896524a52SOrion Hodson */ 42996524a52SOrion Hodson snd_mtxlock(codec->lock); 430987e5972SCameron Grant if (e->mask) { 431f9eb1409SOrion Hodson int cur = ac97_rdcd(codec, e->reg); 43296524a52SOrion Hodson val |= cur & ~(mask); 433987e5972SCameron Grant } 434f9eb1409SOrion Hodson ac97_wrcd(codec, reg, val); 43566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 436987e5972SCameron Grant return left | (right << 8); 437341f16ccSCameron Grant } else { 438341f16ccSCameron Grant /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 43939004e69SCameron Grant return -1; 440987e5972SCameron Grant } 441341f16ccSCameron Grant } 442987e5972SCameron Grant 443108082c4SOrion Hodson static void 444108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec) 445108082c4SOrion Hodson { 446a52604cfSOrion Hodson /* 447a52604cfSOrion Hodson * Determine if AUX_OUT is a valid control. 448a52604cfSOrion Hodson * 449a52604cfSOrion Hodson * Control will read zero if not valid after a reset, other gain 450a52604cfSOrion Hodson * controls read muted (0x8000). 451a52604cfSOrion Hodson */ 452a52604cfSOrion Hodson if (ac97_rdcd(codec, AC97_MIX_AUXOUT) == 0) { 453a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_OGAIN], 454a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_OGAIN])); 455a52604cfSOrion Hodson } 456a52604cfSOrion Hodson 457a52604cfSOrion Hodson /* 458a52604cfSOrion Hodson * Determine what AUX_OUT really means, it can be: 459108082c4SOrion Hodson * 460108082c4SOrion Hodson * 1. Headphone out. 461108082c4SOrion Hodson * 2. 4-Channel Out 462108082c4SOrion Hodson * 3. True line level out (effectively master volume). 463108082c4SOrion Hodson * 464108082c4SOrion Hodson * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 465108082c4SOrion Hodson */ 466a52604cfSOrion Hodson if (codec->extcaps & AC97_EXTCAP_SDAC && 467f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 468a52604cfSOrion Hodson codec->mix[SOUND_MIXER_VOLUME].reg = AC97_MIXEXT_SURROUND; 469a52604cfSOrion Hodson } else if (codec->caps & AC97_CAP_HEADPHONE) { 470a52604cfSOrion Hodson /* Headphone out present/selected AUX_OUT is effectively 471a52604cfSOrion Hodson * master volume control. */ 472a52604cfSOrion Hodson struct ac97mixtable_entry tmp = codec->mix[SOUND_MIXER_VOLUME]; 473a52604cfSOrion Hodson codec->mix[SOUND_MIXER_VOLUME] = codec->mix[SOUND_MIXER_OGAIN]; 474a52604cfSOrion Hodson codec->mix[SOUND_MIXER_OGAIN] = tmp; 475108082c4SOrion Hodson } 476a52604cfSOrion Hodson } 477a52604cfSOrion Hodson 478a52604cfSOrion Hodson static void 479a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec) 480a52604cfSOrion Hodson { 481a52604cfSOrion Hodson /* Hide treble and bass if they don't exist */ 482a52604cfSOrion Hodson if ((codec->caps & AC97_CAP_TONE) == 0) { 483a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_BASS], 484a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_BASS])); 485a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_TREBLE], 486a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_TREBLE])); 487a52604cfSOrion Hodson } 488108082c4SOrion Hodson } 489108082c4SOrion Hodson 49019921b23SOrion Hodson static const char* 49119921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 49219921b23SOrion Hodson { 49319921b23SOrion Hodson if (cname == NULL) { 49419921b23SOrion Hodson sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 49519921b23SOrion Hodson return buf; 49619921b23SOrion Hodson } 49719921b23SOrion Hodson 49819921b23SOrion Hodson if (vname == NULL) vname = "Unknown"; 49919921b23SOrion Hodson 50019921b23SOrion Hodson if (bootverbose) { 50119921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 50219921b23SOrion Hodson } else { 50319921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec", vname, cname); 50419921b23SOrion Hodson } 50519921b23SOrion Hodson return buf; 50619921b23SOrion Hodson } 50719921b23SOrion Hodson 508987e5972SCameron Grant static unsigned 50939004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 510987e5972SCameron Grant { 511f9eb1409SOrion Hodson ac97_patch codec_patch; 51219921b23SOrion Hodson const char *cname, *vname; 51319921b23SOrion Hodson char desc[80]; 51419921b23SOrion Hodson u_int8_t model, step; 515341f16ccSCameron Grant unsigned i, j, k, old; 516987e5972SCameron Grant u_int32_t id; 517987e5972SCameron Grant 51866ef8af5SCameron Grant snd_mtxlock(codec->lock); 5190f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 520cd2c103aSCameron Grant if (codec->count == 0) { 52104553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 52266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 52304553e63SCameron Grant return ENODEV; 52404553e63SCameron Grant } 5259ec437a3SCameron Grant 526f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 527c6d4b83aSOrion Hodson ac97_reset(codec); 528f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 529987e5972SCameron Grant 530f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REG_RESET); 531987e5972SCameron Grant codec->caps = i & 0x03ff; 532987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 533987e5972SCameron Grant 534f9eb1409SOrion Hodson id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 535e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 536e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 53766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 538e620d959SCameron Grant return ENODEV; 539e620d959SCameron Grant } 5406b4b88f7SCameron Grant 54119921b23SOrion Hodson codec->id = id; 542cd2c103aSCameron Grant codec->noext = 0; 543f9eb1409SOrion Hodson codec_patch = NULL; 54419921b23SOrion Hodson 54519921b23SOrion Hodson cname = NULL; 54619921b23SOrion Hodson model = step = 0; 547cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 54819921b23SOrion Hodson u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 54919921b23SOrion Hodson if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 550cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 551f9eb1409SOrion Hodson codec_patch = ac97codecid[i].patch; 55219921b23SOrion Hodson cname = ac97codecid[i].name; 55319921b23SOrion Hodson model = (id & modelmask) & 0xff; 55419921b23SOrion Hodson step = (id & ~modelmask) & 0xff; 55519921b23SOrion Hodson break; 55619921b23SOrion Hodson } 55719921b23SOrion Hodson } 55819921b23SOrion Hodson 55919921b23SOrion Hodson vname = NULL; 56019921b23SOrion Hodson for (i = 0; ac97vendorid[i].id; i++) { 56119921b23SOrion Hodson if (ac97vendorid[i].id == (id & 0xffffff00)) { 56219921b23SOrion Hodson vname = ac97vendorid[i].name; 56319921b23SOrion Hodson break; 564cd2c103aSCameron Grant } 565cd2c103aSCameron Grant } 5666b4b88f7SCameron Grant 567cd2c103aSCameron Grant codec->extcaps = 0; 568cd2c103aSCameron Grant codec->extid = 0; 569cd2c103aSCameron Grant codec->extstat = 0; 5706a6ee5bbSCameron Grant if (!codec->noext) { 571f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REGEXT_ID); 5726a6ee5bbSCameron Grant if (i != 0xffff) { 57339004e69SCameron Grant codec->extcaps = i & 0x3fff; 57439004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 575f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 5766b4b88f7SCameron Grant } 5776a6ee5bbSCameron Grant } 578987e5972SCameron Grant 579341f16ccSCameron Grant for (i = 0; i < 32; i++) { 580108082c4SOrion Hodson codec->mix[i] = ac97mixtable_default[i]; 581108082c4SOrion Hodson } 582108082c4SOrion Hodson ac97_fix_auxout(codec); 583a52604cfSOrion Hodson ac97_fix_tone(codec); 584f9eb1409SOrion Hodson if (codec_patch) 585f9eb1409SOrion Hodson codec_patch(codec); 586108082c4SOrion Hodson 587108082c4SOrion Hodson for (i = 0; i < 32; i++) { 58833c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 58933c878f0SCameron Grant if (k && (codec->mix[i].reg > 0)) { 590f9eb1409SOrion Hodson old = ac97_rdcd(codec, codec->mix[i].reg); 591f9eb1409SOrion Hodson ac97_wrcd(codec, codec->mix[i].reg, 0x3f); 592f9eb1409SOrion Hodson j = ac97_rdcd(codec, codec->mix[i].reg); 593f9eb1409SOrion Hodson ac97_wrcd(codec, codec->mix[i].reg, old); 5946a6ee5bbSCameron Grant codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 595341f16ccSCameron Grant for (k = 1; j & (1 << k); k++); 596341f16ccSCameron Grant codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 597341f16ccSCameron Grant } 598341f16ccSCameron Grant /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 599341f16ccSCameron Grant } 600987e5972SCameron Grant 60119921b23SOrion Hodson device_printf(codec->dev, "<%s>\n", 60219921b23SOrion Hodson ac97_hw_desc(codec->id, vname, cname, desc)); 603a825c6e5SOrion Hodson 604987e5972SCameron Grant if (bootverbose) { 60519921b23SOrion Hodson device_printf(codec->dev, "Codec features "); 60639004e69SCameron Grant for (i = j = 0; i < 10; i++) 60739004e69SCameron Grant if (codec->caps & (1 << i)) 60839004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 60939004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 610987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 61139004e69SCameron Grant 61239004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 61319921b23SOrion Hodson device_printf(codec->dev, "%s codec", 61419921b23SOrion Hodson codec->extid? "Secondary" : "Primary"); 61539004e69SCameron Grant if (codec->extcaps) 61639004e69SCameron Grant printf(" extended features "); 61739004e69SCameron Grant for (i = j = 0; i < 14; i++) 61839004e69SCameron Grant if (codec->extcaps & (1 << i)) 61939004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 62039004e69SCameron Grant printf("\n"); 62139004e69SCameron Grant } 622987e5972SCameron Grant } 623987e5972SCameron Grant 624f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 62503a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 62666ef8af5SCameron Grant snd_mtxunlock(codec->lock); 627987e5972SCameron Grant return 0; 628987e5972SCameron Grant } 629987e5972SCameron Grant 6309ec437a3SCameron Grant static unsigned 6319ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 6329ec437a3SCameron Grant { 63366ef8af5SCameron Grant snd_mtxlock(codec->lock); 6340f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 6359ec437a3SCameron Grant if (codec->count == 0) { 6369ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 63766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 6389ec437a3SCameron Grant return ENODEV; 6399ec437a3SCameron Grant } 6409ec437a3SCameron Grant 641f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 642c6d4b83aSOrion Hodson ac97_reset(codec); 643f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 6449ec437a3SCameron Grant 6459ec437a3SCameron Grant if (!codec->noext) { 646f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 647f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 648b60e55dbSGuido van Rooij != codec->extstat) 6499ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 650b60e55dbSGuido van Rooij codec->extstat, 651f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_REGEXT_STAT) & 652b60e55dbSGuido van Rooij AC97_EXTCAPS); 6539ec437a3SCameron Grant } 6549ec437a3SCameron Grant 655f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 6569ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 65766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 6589ec437a3SCameron Grant return 0; 6599ec437a3SCameron Grant } 6609ec437a3SCameron Grant 661987e5972SCameron Grant struct ac97_info * 6620f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 663987e5972SCameron Grant { 664987e5972SCameron Grant struct ac97_info *codec; 665987e5972SCameron Grant 6660f55ac6cSCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 6670f55ac6cSCameron Grant if (codec == NULL) 6680f55ac6cSCameron Grant return NULL; 6690f55ac6cSCameron Grant 67066ef8af5SCameron Grant snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 671489c22ebSJohn Baldwin codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 672a163d034SWarner Losh codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 6730f55ac6cSCameron Grant if (codec->methods == NULL) { 67466ef8af5SCameron Grant snd_mtxlock(codec->lock); 67566ef8af5SCameron Grant snd_mtxfree(codec->lock); 6760f55ac6cSCameron Grant free(codec, M_AC97); 6770f55ac6cSCameron Grant return NULL; 678987e5972SCameron Grant } 6790f55ac6cSCameron Grant 6800f55ac6cSCameron Grant codec->dev = dev; 6810f55ac6cSCameron Grant codec->devinfo = devinfo; 68279bb7d52SCameron Grant codec->flags = 0; 683987e5972SCameron Grant return codec; 684987e5972SCameron Grant } 685987e5972SCameron Grant 68633dbf14aSCameron Grant void 68733dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 68833dbf14aSCameron Grant { 68966ef8af5SCameron Grant snd_mtxlock(codec->lock); 6900f55ac6cSCameron Grant if (codec->methods != NULL) 6910f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 69266ef8af5SCameron Grant snd_mtxfree(codec->lock); 6930f55ac6cSCameron Grant free(codec, M_AC97); 69433dbf14aSCameron Grant } 69533dbf14aSCameron Grant 69679bb7d52SCameron Grant void 69779bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 69879bb7d52SCameron Grant { 69979bb7d52SCameron Grant codec->flags = val; 70079bb7d52SCameron Grant } 70179bb7d52SCameron Grant 70279bb7d52SCameron Grant u_int32_t 70379bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 70479bb7d52SCameron Grant { 70579bb7d52SCameron Grant return codec->flags; 70679bb7d52SCameron Grant } 70779bb7d52SCameron Grant 7080f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 7090f55ac6cSCameron Grant 710987e5972SCameron Grant static int 71166ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 712987e5972SCameron Grant { 713987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 714341f16ccSCameron Grant u_int32_t i, mask; 715341f16ccSCameron Grant 71639004e69SCameron Grant if (codec == NULL) 71739004e69SCameron Grant return -1; 718341f16ccSCameron Grant 719e620d959SCameron Grant if (ac97_initmixer(codec)) 720e620d959SCameron Grant return -1; 721341f16ccSCameron Grant 722341f16ccSCameron Grant mask = 0; 723341f16ccSCameron Grant for (i = 0; i < 32; i++) 724341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 725341f16ccSCameron Grant mix_setdevs(m, mask); 726341f16ccSCameron Grant 727341f16ccSCameron Grant mask = 0; 728341f16ccSCameron Grant for (i = 0; i < 32; i++) 729341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 730341f16ccSCameron Grant mix_setrecdevs(m, mask); 731987e5972SCameron Grant return 0; 732987e5972SCameron Grant } 733987e5972SCameron Grant 734987e5972SCameron Grant static int 73566ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 73633dbf14aSCameron Grant { 73733dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 738341f16ccSCameron Grant 73933dbf14aSCameron Grant if (codec == NULL) 74033dbf14aSCameron Grant return -1; 74133dbf14aSCameron Grant /* 74233dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 74333dbf14aSCameron Grant return -1; 74433dbf14aSCameron Grant */ 74533dbf14aSCameron Grant ac97_destroy(codec); 74633dbf14aSCameron Grant return 0; 74733dbf14aSCameron Grant } 74833dbf14aSCameron Grant 74933dbf14aSCameron Grant static int 75066ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 7519ec437a3SCameron Grant { 7529ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 7539ec437a3SCameron Grant 7549ec437a3SCameron Grant if (codec == NULL) 7559ec437a3SCameron Grant return -1; 7569ec437a3SCameron Grant return ac97_reinitmixer(codec); 7579ec437a3SCameron Grant } 7589ec437a3SCameron Grant 7599ec437a3SCameron Grant static int 76066ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 761987e5972SCameron Grant { 762987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 763341f16ccSCameron Grant 76439004e69SCameron Grant if (codec == NULL) 76539004e69SCameron Grant return -1; 766987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 767987e5972SCameron Grant } 768987e5972SCameron Grant 769987e5972SCameron Grant static int 77066ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 771987e5972SCameron Grant { 772987e5972SCameron Grant int i; 773987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 774341f16ccSCameron Grant 77539004e69SCameron Grant if (codec == NULL) 77639004e69SCameron Grant return -1; 777987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 77839004e69SCameron Grant if ((src & (1 << i)) != 0) 77939004e69SCameron Grant break; 780987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 781987e5972SCameron Grant } 782987e5972SCameron Grant 7830f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 7840f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 7850f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 7860f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 7870f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 7880f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 7890f55ac6cSCameron Grant { 0, 0 } 790987e5972SCameron Grant }; 7910f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 7920f55ac6cSCameron Grant 7930f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 7940f55ac6cSCameron Grant 7950f55ac6cSCameron Grant kobj_class_t 7960f55ac6cSCameron Grant ac97_getmixerclass(void) 7970f55ac6cSCameron Grant { 7980f55ac6cSCameron Grant return &ac97mixer_class; 7990f55ac6cSCameron Grant } 8000f55ac6cSCameron Grant 801987e5972SCameron Grant 802