1098ca2bdSWarner Losh /*- 23f225978SCameron Grant * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 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" }, 1053c6b655dSMathew Kanner { 0x44543000, "Diamond Technology" }, 10661a0da1dSOrion Hodson { 0x454d4300, "eMicro" }, 10719921b23SOrion Hodson { 0x45838300, "ESS Technology" }, 1083c6b655dSMathew Kanner { 0x48525300, "Intersil" }, 10919921b23SOrion Hodson { 0x49434500, "ICEnsemble" }, 1103c6b655dSMathew Kanner { 0x49544500, "ITE, Inc." }, 11119921b23SOrion Hodson { 0x4e534300, "National Semiconductor" }, 11219921b23SOrion Hodson { 0x50534300, "Philips Semiconductor" }, 11319921b23SOrion Hodson { 0x83847600, "SigmaTel" }, 1143c6b655dSMathew Kanner { 0x53494c00, "Silicon Laboratories" }, 11519921b23SOrion Hodson { 0x54524100, "TriTech" }, 1163c6b655dSMathew Kanner { 0x54584e00, "Texas Instruments" }, 11719921b23SOrion Hodson { 0x56494100, "VIA Technologies" }, 1183c6b655dSMathew Kanner { 0x57454300, "Winbond" }, 11919921b23SOrion Hodson { 0x574d4c00, "Wolfson" }, 12019921b23SOrion Hodson { 0x594d4800, "Yamaha" }, 1216f0182bdSOrion Hodson { 0x01408300, "Creative" }, 12219921b23SOrion Hodson { 0x00000000, NULL } 12319921b23SOrion Hodson }; 12419921b23SOrion Hodson 125987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 12619921b23SOrion Hodson { 0x41445303, 0x00, 0, "AD1819", 0 }, 12719921b23SOrion Hodson { 0x41445340, 0x00, 0, "AD1881", 0 }, 12819921b23SOrion Hodson { 0x41445348, 0x00, 0, "AD1881A", 0 }, 12919921b23SOrion Hodson { 0x41445360, 0x00, 0, "AD1885", 0 }, 13019921b23SOrion Hodson { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 131ba548c64SOrion Hodson { 0x41445362, 0x00, 0, "AD1887", 0 }, 132ba548c64SOrion Hodson { 0x41445363, 0x00, 0, "AD1886A", 0 }, 133c5cb8d60SScott Long { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, 134a52604cfSOrion Hodson { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, 135ba548c64SOrion Hodson { 0x41445372, 0x00, 0, "AD1981A", 0 }, 136ba548c64SOrion Hodson { 0x41445374, 0x00, 0, "AD1981B", 0 }, 137a52604cfSOrion Hodson { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, 13819921b23SOrion Hodson { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 13919921b23SOrion Hodson { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 14019921b23SOrion Hodson { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 1413c6b655dSMathew Kanner { 0x414b4d06, 0x00, 0, "AK4544A", 0 }, 1423c6b655dSMathew Kanner { 0x454b4d07, 0x00, 0, "AK4545", 0 }, 14319921b23SOrion Hodson { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 1449963235aSOrion Hodson { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 14519921b23SOrion Hodson { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 14619921b23SOrion Hodson { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 14719921b23SOrion Hodson { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 1485197cdc1SDag-Erling Smørgrav { 0x414c4760, 0x0f, 0, "ALC655", 0 }, 14994ed763dSJun Kuriyama { 0x414c4780, 0x0f, 0, "ALC658", 0 }, 1503c6b655dSMathew Kanner { 0x414c4790, 0x0f, 0, "ALC850", 0 }, 15119921b23SOrion Hodson { 0x43525900, 0x07, 0, "CS4297", 0 }, 15219921b23SOrion Hodson { 0x43525910, 0x07, 0, "CS4297A", 0 }, 15319921b23SOrion Hodson { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 154e5728f83SMIHIRA Sanpei Yoshiro { 0x4352592d, 0x07, 0, "CS4294", 0 }, 15519921b23SOrion Hodson { 0x43525930, 0x07, 0, "CS4299", 0 }, 15619921b23SOrion Hodson { 0x43525940, 0x07, 0, "CS4201", 0 }, 157b2a0f525SOrion Hodson { 0x43525958, 0x07, 0, "CS4205", 0 }, 15819921b23SOrion Hodson { 0x43525960, 0x07, 0, "CS4291A", 0 }, 15919921b23SOrion Hodson { 0x434d4961, 0x00, 0, "CMI9739", 0 }, 16019921b23SOrion Hodson { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 1613c6b655dSMathew Kanner { 0x43585421, 0x00, 0, "HSD11246", 0 }, 1623c6b655dSMathew Kanner { 0x43585428, 0x07, 0, "CX20468", 0 }, 1633c6b655dSMathew Kanner { 0x44543000, 0x00, 0, "DT0398", 0 }, 16461a0da1dSOrion Hodson { 0x454d4323, 0x00, 0, "EM28023", 0 }, 16561a0da1dSOrion Hodson { 0x454d4328, 0x00, 0, "EM28028", 0 }, 16619921b23SOrion Hodson { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 1673c6b655dSMathew Kanner { 0x48525300, 0x00, 0, "HMP9701", 0 }, 16819921b23SOrion Hodson { 0x49434501, 0x00, 0, "ICE1230", 0 }, 16919921b23SOrion Hodson { 0x49434511, 0x00, 0, "ICE1232", 0 }, 17019921b23SOrion Hodson { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 17149fd6905SOrion Hodson { 0x49434551, 0x03, 0, "VT1616", 0 }, /* Via badged ICE */ 1723c6b655dSMathew Kanner { 0x49544520, 0x00, 0, "ITE2226E", 0 }, 1733c6b655dSMathew Kanner { 0x49544560, 0x07, 0, "ITE2646E", 0 }, /* XXX: patch needed */ 17419921b23SOrion Hodson { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 17519921b23SOrion Hodson { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 17619921b23SOrion Hodson { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 17719921b23SOrion Hodson { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 1783c6b655dSMathew Kanner { 0x4e534331, 0x00, 0, "LM4549", 0 }, 17919921b23SOrion Hodson { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 18019921b23SOrion Hodson { 0x4e534350, 0x00, 0, "LM4550", 0 }, 18119921b23SOrion Hodson { 0x50534301, 0x00, 0, "UCB1510", 0 }, 18219921b23SOrion Hodson { 0x50534304, 0x00, 0, "UCB1400", 0 }, 18319921b23SOrion Hodson { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 18419921b23SOrion Hodson { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 18519921b23SOrion Hodson { 0x83847605, 0x00, 0, "STAC9704", 0 }, 18619921b23SOrion Hodson { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 18719921b23SOrion Hodson { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 18819921b23SOrion Hodson { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 18919921b23SOrion Hodson { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 19019921b23SOrion Hodson { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 19119921b23SOrion Hodson { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 19219921b23SOrion Hodson { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 19319921b23SOrion Hodson { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 19419921b23SOrion Hodson { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 19519921b23SOrion Hodson { 0x53494c22, 0x00, 0, "Si3036", 0 }, 19619921b23SOrion Hodson { 0x53494c23, 0x00, 0, "Si3038", 0 }, 19719921b23SOrion Hodson { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 19819921b23SOrion Hodson { 0x54524106, 0x00, 0, "TR28026", 0 }, 19919921b23SOrion Hodson { 0x54524108, 0x00, 0, "TR28028", 0 }, 20019921b23SOrion Hodson { 0x54524123, 0x00, 0, "TR28602", 0 }, 2013c6b655dSMathew Kanner { 0x54524e03, 0x07, 0, "TLV320AIC27", 0 }, 2023c6b655dSMathew Kanner { 0x54584e20, 0x00, 0, "TLC320AD90", 0 }, 20319921b23SOrion Hodson { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 20419921b23SOrion Hodson { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 20519921b23SOrion Hodson { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 20619921b23SOrion Hodson { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 20719921b23SOrion Hodson { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 2083c6b655dSMathew Kanner { 0x574d4d09, 0x00, 0, "WM9709", 0 }, 2093c6b655dSMathew Kanner { 0x574d4c12, 0x00, 0, "WM9711/12", 0 }, /* XXX: patch needed */ 2103c6b655dSMathew Kanner { 0x57454301, 0x00, 0, "W83971D", 0 }, 21119921b23SOrion Hodson { 0x594d4800, 0x00, 0, "YMF743", 0 }, 21219921b23SOrion Hodson { 0x594d4802, 0x00, 0, "YMF752", 0 }, 21319921b23SOrion Hodson { 0x594d4803, 0x00, 0, "YMF753", 0 }, 2146f0182bdSOrion Hodson { 0x01408384, 0x00, 0, "EV1938", 0 }, 21519921b23SOrion Hodson { 0, 0, 0, NULL, 0 } 216987e5972SCameron Grant }; 217987e5972SCameron Grant 218987e5972SCameron Grant static char *ac97enhancement[] = { 21904553e63SCameron Grant "no 3D Stereo Enhancement", 220987e5972SCameron Grant "Analog Devices Phat Stereo", 221987e5972SCameron Grant "Creative Stereo Enhancement", 222987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 223987e5972SCameron Grant "Yamaha Ymersion", 224987e5972SCameron Grant "BBE 3D Stereo Enhancement", 225987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 226987e5972SCameron Grant "Qsound QXpander", 227987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 228987e5972SCameron Grant "SRS 3D Stereo Enhancement", 229987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 230987e5972SCameron Grant "AKM 3D Audio", 231987e5972SCameron Grant "Aureal Stereo Enhancement", 232987e5972SCameron Grant "Aztech 3D Enhancement", 233987e5972SCameron Grant "Binaura 3D Audio Enhancement", 234987e5972SCameron Grant "ESS Technology Stereo Enhancement", 235987e5972SCameron Grant "Harman International VMAx", 236987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 237987e5972SCameron Grant "Philips Incredible Sound", 238987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 239987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 240987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 241987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 242987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 243987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 244987e5972SCameron Grant "Delta Integration 3D Enhancement", 245987e5972SCameron Grant "SigmaTel 3D Enhancement", 246987e5972SCameron Grant "Reserved 27", 247987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 248987e5972SCameron Grant "Reserved 29", 249987e5972SCameron Grant "Reserved 30", 250987e5972SCameron Grant "Reserved 31" 251987e5972SCameron Grant }; 252987e5972SCameron Grant 253987e5972SCameron Grant static char *ac97feature[] = { 254987e5972SCameron Grant "mic channel", 255987e5972SCameron Grant "reserved", 256987e5972SCameron Grant "tone", 257987e5972SCameron Grant "simulated stereo", 258987e5972SCameron Grant "headphone", 259987e5972SCameron Grant "bass boost", 260987e5972SCameron Grant "18 bit DAC", 261987e5972SCameron Grant "20 bit DAC", 262987e5972SCameron Grant "18 bit ADC", 263987e5972SCameron Grant "20 bit ADC" 264987e5972SCameron Grant }; 265987e5972SCameron Grant 26639004e69SCameron Grant static char *ac97extfeature[] = { 26739004e69SCameron Grant "variable rate PCM", 26839004e69SCameron Grant "double rate PCM", 26939004e69SCameron Grant "reserved 1", 27039004e69SCameron Grant "variable rate mic", 27139004e69SCameron Grant "reserved 2", 27239004e69SCameron Grant "reserved 3", 27339004e69SCameron Grant "center DAC", 27439004e69SCameron Grant "surround DAC", 27539004e69SCameron Grant "LFE DAC", 27639004e69SCameron Grant "AMAP", 27739004e69SCameron Grant "reserved 4", 27839004e69SCameron Grant "reserved 5", 27939004e69SCameron Grant "reserved 6", 28039004e69SCameron Grant "reserved 7", 28139004e69SCameron Grant }; 28239004e69SCameron Grant 283f9eb1409SOrion Hodson u_int16_t 284f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg) 28539004e69SCameron Grant { 2860f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 28739004e69SCameron Grant } 28839004e69SCameron Grant 289f9eb1409SOrion Hodson void 290f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 29139004e69SCameron Grant { 2920f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 29339004e69SCameron Grant } 29439004e69SCameron Grant 295c6d4b83aSOrion Hodson static void 296c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec) 297c6d4b83aSOrion Hodson { 298c6d4b83aSOrion Hodson u_int32_t i, ps; 299f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RESET, 0); 300c6d4b83aSOrion Hodson for (i = 0; i < 500; i++) { 301f9eb1409SOrion Hodson ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 302c6d4b83aSOrion Hodson if (ps == AC97_POWER_STATUS) 303c6d4b83aSOrion Hodson return; 304c6d4b83aSOrion Hodson DELAY(1000); 305c6d4b83aSOrion Hodson } 306a825c6e5SOrion Hodson device_printf(codec->dev, "AC97 reset timed out.\n"); 307c6d4b83aSOrion Hodson } 308c6d4b83aSOrion Hodson 30939004e69SCameron Grant int 31039004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 31139004e69SCameron Grant { 31239004e69SCameron Grant u_int16_t v; 31339004e69SCameron Grant 31439004e69SCameron Grant switch(which) { 31539004e69SCameron Grant case AC97_REGEXT_FDACRATE: 31639004e69SCameron Grant case AC97_REGEXT_SDACRATE: 31739004e69SCameron Grant case AC97_REGEXT_LDACRATE: 31839004e69SCameron Grant case AC97_REGEXT_LADCRATE: 31939004e69SCameron Grant case AC97_REGEXT_MADCRATE: 32039004e69SCameron Grant break; 32139004e69SCameron Grant 32239004e69SCameron Grant default: 32339004e69SCameron Grant return -1; 32439004e69SCameron Grant } 32539004e69SCameron Grant 32666ef8af5SCameron Grant snd_mtxlock(codec->lock); 32739004e69SCameron Grant if (rate != 0) { 32839004e69SCameron Grant v = rate; 32939004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 33039004e69SCameron Grant v >>= 1; 331f9eb1409SOrion Hodson ac97_wrcd(codec, which, v); 33239004e69SCameron Grant } 333f9eb1409SOrion Hodson v = ac97_rdcd(codec, which); 33439004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 33539004e69SCameron Grant v <<= 1; 33666ef8af5SCameron Grant snd_mtxunlock(codec->lock); 33739004e69SCameron Grant return v; 33839004e69SCameron Grant } 33939004e69SCameron Grant 34039004e69SCameron Grant int 34139004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 34239004e69SCameron Grant { 34339004e69SCameron Grant mode &= AC97_EXTCAPS; 344647fbfebSOrion Hodson if ((mode & ~codec->extcaps) != 0) { 345647fbfebSOrion Hodson device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 346647fbfebSOrion Hodson mode); 34739004e69SCameron Grant return -1; 348647fbfebSOrion Hodson } 34966ef8af5SCameron Grant snd_mtxlock(codec->lock); 350f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 351f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 35266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 35339004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 35439004e69SCameron Grant } 35539004e69SCameron Grant 3569ec437a3SCameron Grant u_int16_t 3579ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 3589ec437a3SCameron Grant { 3599ec437a3SCameron Grant return codec->extstat; 3609ec437a3SCameron Grant } 3619ec437a3SCameron Grant 3629ec437a3SCameron Grant u_int16_t 3639ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 3649ec437a3SCameron Grant { 3659ec437a3SCameron Grant return codec->extcaps; 3669ec437a3SCameron Grant } 3679ec437a3SCameron Grant 3685d91ad67SCameron Grant u_int16_t 3695d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec) 3705d91ad67SCameron Grant { 3715d91ad67SCameron Grant return codec->caps; 3725d91ad67SCameron Grant } 3735d91ad67SCameron Grant 374987e5972SCameron Grant static int 375987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 376987e5972SCameron Grant { 377987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 378341f16ccSCameron Grant 379987e5972SCameron Grant if (e->recidx > 0) { 380987e5972SCameron Grant int val = e->recidx - 1; 381987e5972SCameron Grant val |= val << 8; 38266ef8af5SCameron Grant snd_mtxlock(codec->lock); 383f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RECSEL, val); 38466ef8af5SCameron Grant snd_mtxunlock(codec->lock); 385987e5972SCameron Grant return 0; 38639004e69SCameron Grant } else 38739004e69SCameron Grant return -1; 388987e5972SCameron Grant } 389987e5972SCameron Grant 390987e5972SCameron Grant static int 391987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 392987e5972SCameron Grant { 393987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 394341f16ccSCameron Grant 395341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 39696524a52SOrion Hodson int mask, max, val, reg; 39796524a52SOrion Hodson 39896524a52SOrion Hodson reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */ 39996524a52SOrion Hodson max = (1 << e->bits) - 1; /* actual range */ 40096524a52SOrion Hodson mask = (max << 8) | max; /* bits of interest */ 401987e5972SCameron Grant 40239004e69SCameron Grant if (!e->stereo) 40339004e69SCameron Grant right = left; 40496524a52SOrion Hodson 40596524a52SOrion Hodson /* 40696524a52SOrion Hodson * Invert the range if the polarity requires so, 40796524a52SOrion Hodson * then scale to 0..max-1 to compute the value to 40896524a52SOrion Hodson * write into the codec, and scale back to 0..100 40996524a52SOrion Hodson * for the return value. 41096524a52SOrion Hodson */ 411987e5972SCameron Grant if (e->reg > 0) { 412987e5972SCameron Grant left = 100 - left; 413987e5972SCameron Grant right = 100 - right; 414987e5972SCameron Grant } 415987e5972SCameron Grant 416987e5972SCameron Grant left = (left * max) / 100; 417987e5972SCameron Grant right = (right * max) / 100; 418987e5972SCameron Grant 419987e5972SCameron Grant val = (left << 8) | right; 420987e5972SCameron Grant 421987e5972SCameron Grant left = (left * 100) / max; 422987e5972SCameron Grant right = (right * 100) / max; 423987e5972SCameron Grant 424987e5972SCameron Grant if (e->reg > 0) { 425987e5972SCameron Grant left = 100 - left; 426987e5972SCameron Grant right = 100 - right; 427987e5972SCameron Grant } 428987e5972SCameron Grant 42996524a52SOrion Hodson /* 43096524a52SOrion Hodson * For mono controls, trim val and mask, also taking 43196524a52SOrion Hodson * care of e->ofs (offset of control field). 43296524a52SOrion Hodson */ 43396524a52SOrion Hodson if (e->ofs) { 434987e5972SCameron Grant val &= max; 435987e5972SCameron Grant val <<= e->ofs; 43696524a52SOrion Hodson mask = (max << e->ofs); 43796524a52SOrion Hodson } 43896524a52SOrion Hodson 43996524a52SOrion Hodson /* 44096524a52SOrion Hodson * If we have a mute bit, add it to the mask and 44196524a52SOrion Hodson * update val and set mute if both channels require a 44296524a52SOrion Hodson * zero volume. 44396524a52SOrion Hodson */ 44496524a52SOrion Hodson if (e->mute == 1) { 44596524a52SOrion Hodson mask |= AC97_MUTE; 44696524a52SOrion Hodson if (left == 0 && right == 0) 44796524a52SOrion Hodson val = AC97_MUTE; 44896524a52SOrion Hodson } 44996524a52SOrion Hodson 45096524a52SOrion Hodson /* 45196524a52SOrion Hodson * If the mask bit is set, do not alter the other bits. 45296524a52SOrion Hodson */ 45396524a52SOrion Hodson snd_mtxlock(codec->lock); 454987e5972SCameron Grant if (e->mask) { 455f9eb1409SOrion Hodson int cur = ac97_rdcd(codec, e->reg); 45696524a52SOrion Hodson val |= cur & ~(mask); 457987e5972SCameron Grant } 458f9eb1409SOrion Hodson ac97_wrcd(codec, reg, val); 45966ef8af5SCameron Grant snd_mtxunlock(codec->lock); 460987e5972SCameron Grant return left | (right << 8); 461341f16ccSCameron Grant } else { 462341f16ccSCameron Grant /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 46339004e69SCameron Grant return -1; 464987e5972SCameron Grant } 465341f16ccSCameron Grant } 466987e5972SCameron Grant 467108082c4SOrion Hodson static void 468108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec) 469108082c4SOrion Hodson { 470cfd5696dSOrion Hodson int keep_ogain; 471cfd5696dSOrion Hodson 472a52604cfSOrion Hodson /* 473cfd5696dSOrion Hodson * By default, The ac97 aux_out register (0x04) corresponds to OSS's 474cfd5696dSOrion Hodson * OGAIN setting. 475a52604cfSOrion Hodson * 476cfd5696dSOrion Hodson * We first check whether aux_out is a valid register. If not 477cfd5696dSOrion Hodson * we may not want to keep ogain. 478a52604cfSOrion Hodson */ 479cfd5696dSOrion Hodson keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000; 480a52604cfSOrion Hodson 481a52604cfSOrion Hodson /* 482a52604cfSOrion Hodson * Determine what AUX_OUT really means, it can be: 483108082c4SOrion Hodson * 484108082c4SOrion Hodson * 1. Headphone out. 485108082c4SOrion Hodson * 2. 4-Channel Out 486108082c4SOrion Hodson * 3. True line level out (effectively master volume). 487108082c4SOrion Hodson * 488108082c4SOrion Hodson * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 489108082c4SOrion Hodson */ 490a52604cfSOrion Hodson if (codec->extcaps & AC97_EXTCAP_SDAC && 491f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 492cfd5696dSOrion Hodson codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND; 493cfd5696dSOrion Hodson keep_ogain = 1; 494cfd5696dSOrion Hodson } 495cfd5696dSOrion Hodson 496cfd5696dSOrion Hodson if (keep_ogain == 0) { 497cfd5696dSOrion Hodson bzero(&codec->mix[SOUND_MIXER_OGAIN], 498cfd5696dSOrion Hodson sizeof(codec->mix[SOUND_MIXER_OGAIN])); 499108082c4SOrion Hodson } 500a52604cfSOrion Hodson } 501a52604cfSOrion Hodson 502a52604cfSOrion Hodson static void 503a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec) 504a52604cfSOrion Hodson { 505a52604cfSOrion Hodson /* Hide treble and bass if they don't exist */ 506a52604cfSOrion Hodson if ((codec->caps & AC97_CAP_TONE) == 0) { 507a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_BASS], 508a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_BASS])); 509a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_TREBLE], 510a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_TREBLE])); 511a52604cfSOrion Hodson } 512108082c4SOrion Hodson } 513108082c4SOrion Hodson 51419921b23SOrion Hodson static const char* 51519921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 51619921b23SOrion Hodson { 51719921b23SOrion Hodson if (cname == NULL) { 51819921b23SOrion Hodson sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 51919921b23SOrion Hodson return buf; 52019921b23SOrion Hodson } 52119921b23SOrion Hodson 52219921b23SOrion Hodson if (vname == NULL) vname = "Unknown"; 52319921b23SOrion Hodson 52419921b23SOrion Hodson if (bootverbose) { 52519921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 52619921b23SOrion Hodson } else { 52719921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec", vname, cname); 52819921b23SOrion Hodson } 52919921b23SOrion Hodson return buf; 53019921b23SOrion Hodson } 53119921b23SOrion Hodson 532987e5972SCameron Grant static unsigned 53339004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 534987e5972SCameron Grant { 535f9eb1409SOrion Hodson ac97_patch codec_patch; 53619921b23SOrion Hodson const char *cname, *vname; 53719921b23SOrion Hodson char desc[80]; 53819921b23SOrion Hodson u_int8_t model, step; 539341f16ccSCameron Grant unsigned i, j, k, old; 540987e5972SCameron Grant u_int32_t id; 541987e5972SCameron Grant 54266ef8af5SCameron Grant snd_mtxlock(codec->lock); 5430f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 544cd2c103aSCameron Grant if (codec->count == 0) { 54504553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 54666ef8af5SCameron Grant snd_mtxunlock(codec->lock); 54704553e63SCameron Grant return ENODEV; 54804553e63SCameron Grant } 5499ec437a3SCameron Grant 550f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 551c6d4b83aSOrion Hodson ac97_reset(codec); 552f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 553987e5972SCameron Grant 554f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REG_RESET); 555987e5972SCameron Grant codec->caps = i & 0x03ff; 556987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 557987e5972SCameron Grant 558f9eb1409SOrion Hodson id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 559e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 560e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 56166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 562e620d959SCameron Grant return ENODEV; 563e620d959SCameron Grant } 5646b4b88f7SCameron Grant 56519921b23SOrion Hodson codec->id = id; 566cd2c103aSCameron Grant codec->noext = 0; 567f9eb1409SOrion Hodson codec_patch = NULL; 56819921b23SOrion Hodson 56919921b23SOrion Hodson cname = NULL; 57019921b23SOrion Hodson model = step = 0; 571cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 57219921b23SOrion Hodson u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 57319921b23SOrion Hodson if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 574cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 575f9eb1409SOrion Hodson codec_patch = ac97codecid[i].patch; 57619921b23SOrion Hodson cname = ac97codecid[i].name; 57719921b23SOrion Hodson model = (id & modelmask) & 0xff; 57819921b23SOrion Hodson step = (id & ~modelmask) & 0xff; 57919921b23SOrion Hodson break; 58019921b23SOrion Hodson } 58119921b23SOrion Hodson } 58219921b23SOrion Hodson 58319921b23SOrion Hodson vname = NULL; 58419921b23SOrion Hodson for (i = 0; ac97vendorid[i].id; i++) { 58519921b23SOrion Hodson if (ac97vendorid[i].id == (id & 0xffffff00)) { 58619921b23SOrion Hodson vname = ac97vendorid[i].name; 58719921b23SOrion Hodson break; 588cd2c103aSCameron Grant } 589cd2c103aSCameron Grant } 5906b4b88f7SCameron Grant 591cd2c103aSCameron Grant codec->extcaps = 0; 592cd2c103aSCameron Grant codec->extid = 0; 593cd2c103aSCameron Grant codec->extstat = 0; 5946a6ee5bbSCameron Grant if (!codec->noext) { 595f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REGEXT_ID); 5966a6ee5bbSCameron Grant if (i != 0xffff) { 59739004e69SCameron Grant codec->extcaps = i & 0x3fff; 59839004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 599f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 6006b4b88f7SCameron Grant } 6016a6ee5bbSCameron Grant } 602987e5972SCameron Grant 603341f16ccSCameron Grant for (i = 0; i < 32; i++) { 604108082c4SOrion Hodson codec->mix[i] = ac97mixtable_default[i]; 605108082c4SOrion Hodson } 606108082c4SOrion Hodson ac97_fix_auxout(codec); 607a52604cfSOrion Hodson ac97_fix_tone(codec); 608f9eb1409SOrion Hodson if (codec_patch) 609f9eb1409SOrion Hodson codec_patch(codec); 610108082c4SOrion Hodson 611108082c4SOrion Hodson for (i = 0; i < 32; i++) { 61233c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 61333c878f0SCameron Grant if (k && (codec->mix[i].reg > 0)) { 614f9eb1409SOrion Hodson old = ac97_rdcd(codec, codec->mix[i].reg); 615f9eb1409SOrion Hodson ac97_wrcd(codec, codec->mix[i].reg, 0x3f); 616f9eb1409SOrion Hodson j = ac97_rdcd(codec, codec->mix[i].reg); 617f9eb1409SOrion Hodson ac97_wrcd(codec, codec->mix[i].reg, old); 6186a6ee5bbSCameron Grant codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 619341f16ccSCameron Grant for (k = 1; j & (1 << k); k++); 620341f16ccSCameron Grant codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 621341f16ccSCameron Grant } 622341f16ccSCameron Grant /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 623341f16ccSCameron Grant } 624987e5972SCameron Grant 62519921b23SOrion Hodson device_printf(codec->dev, "<%s>\n", 62619921b23SOrion Hodson ac97_hw_desc(codec->id, vname, cname, desc)); 627a825c6e5SOrion Hodson 628987e5972SCameron Grant if (bootverbose) { 62919921b23SOrion Hodson device_printf(codec->dev, "Codec features "); 63039004e69SCameron Grant for (i = j = 0; i < 10; i++) 63139004e69SCameron Grant if (codec->caps & (1 << i)) 63239004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 63339004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 634987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 63539004e69SCameron Grant 63639004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 63719921b23SOrion Hodson device_printf(codec->dev, "%s codec", 63819921b23SOrion Hodson codec->extid? "Secondary" : "Primary"); 63939004e69SCameron Grant if (codec->extcaps) 64039004e69SCameron Grant printf(" extended features "); 64139004e69SCameron Grant for (i = j = 0; i < 14; i++) 64239004e69SCameron Grant if (codec->extcaps & (1 << i)) 64339004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 64439004e69SCameron Grant printf("\n"); 64539004e69SCameron Grant } 646987e5972SCameron Grant } 647987e5972SCameron Grant 648f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 64903a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 65066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 651987e5972SCameron Grant return 0; 652987e5972SCameron Grant } 653987e5972SCameron Grant 6549ec437a3SCameron Grant static unsigned 6559ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 6569ec437a3SCameron Grant { 65766ef8af5SCameron Grant snd_mtxlock(codec->lock); 6580f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 6599ec437a3SCameron Grant if (codec->count == 0) { 6609ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 66166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 6629ec437a3SCameron Grant return ENODEV; 6639ec437a3SCameron Grant } 6649ec437a3SCameron Grant 665f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 666c6d4b83aSOrion Hodson ac97_reset(codec); 667f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 6689ec437a3SCameron Grant 6699ec437a3SCameron Grant if (!codec->noext) { 670f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 671f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 672b60e55dbSGuido van Rooij != codec->extstat) 6739ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 674b60e55dbSGuido van Rooij codec->extstat, 675f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_REGEXT_STAT) & 676b60e55dbSGuido van Rooij AC97_EXTCAPS); 6779ec437a3SCameron Grant } 6789ec437a3SCameron Grant 679f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 6809ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 68166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 6829ec437a3SCameron Grant return 0; 6839ec437a3SCameron Grant } 6849ec437a3SCameron Grant 685987e5972SCameron Grant struct ac97_info * 6860f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 687987e5972SCameron Grant { 688987e5972SCameron Grant struct ac97_info *codec; 689987e5972SCameron Grant 6900f55ac6cSCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 6910f55ac6cSCameron Grant if (codec == NULL) 6920f55ac6cSCameron Grant return NULL; 6930f55ac6cSCameron Grant 69466ef8af5SCameron Grant snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 695489c22ebSJohn Baldwin codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 696a163d034SWarner Losh codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 6970f55ac6cSCameron Grant if (codec->methods == NULL) { 69866ef8af5SCameron Grant snd_mtxlock(codec->lock); 69966ef8af5SCameron Grant snd_mtxfree(codec->lock); 7000f55ac6cSCameron Grant free(codec, M_AC97); 7010f55ac6cSCameron Grant return NULL; 702987e5972SCameron Grant } 7030f55ac6cSCameron Grant 7040f55ac6cSCameron Grant codec->dev = dev; 7050f55ac6cSCameron Grant codec->devinfo = devinfo; 70679bb7d52SCameron Grant codec->flags = 0; 707987e5972SCameron Grant return codec; 708987e5972SCameron Grant } 709987e5972SCameron Grant 71033dbf14aSCameron Grant void 71133dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 71233dbf14aSCameron Grant { 71366ef8af5SCameron Grant snd_mtxlock(codec->lock); 7140f55ac6cSCameron Grant if (codec->methods != NULL) 7150f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 71666ef8af5SCameron Grant snd_mtxfree(codec->lock); 7170f55ac6cSCameron Grant free(codec, M_AC97); 71833dbf14aSCameron Grant } 71933dbf14aSCameron Grant 72079bb7d52SCameron Grant void 72179bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 72279bb7d52SCameron Grant { 72379bb7d52SCameron Grant codec->flags = val; 72479bb7d52SCameron Grant } 72579bb7d52SCameron Grant 72679bb7d52SCameron Grant u_int32_t 72779bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 72879bb7d52SCameron Grant { 72979bb7d52SCameron Grant return codec->flags; 73079bb7d52SCameron Grant } 73179bb7d52SCameron Grant 7320f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 7330f55ac6cSCameron Grant 734987e5972SCameron Grant static int 73566ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 736987e5972SCameron Grant { 737987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 738341f16ccSCameron Grant u_int32_t i, mask; 739341f16ccSCameron Grant 74039004e69SCameron Grant if (codec == NULL) 74139004e69SCameron Grant return -1; 742341f16ccSCameron Grant 743e620d959SCameron Grant if (ac97_initmixer(codec)) 744e620d959SCameron Grant return -1; 745341f16ccSCameron Grant 746341f16ccSCameron Grant mask = 0; 747341f16ccSCameron Grant for (i = 0; i < 32; i++) 748341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 749341f16ccSCameron Grant mix_setdevs(m, mask); 750341f16ccSCameron Grant 751341f16ccSCameron Grant mask = 0; 752341f16ccSCameron Grant for (i = 0; i < 32; i++) 753341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 754341f16ccSCameron Grant mix_setrecdevs(m, mask); 755987e5972SCameron Grant return 0; 756987e5972SCameron Grant } 757987e5972SCameron Grant 758987e5972SCameron Grant static int 75966ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 76033dbf14aSCameron Grant { 76133dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 762341f16ccSCameron Grant 76333dbf14aSCameron Grant if (codec == NULL) 76433dbf14aSCameron Grant return -1; 76533dbf14aSCameron Grant /* 76633dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 76733dbf14aSCameron Grant return -1; 76833dbf14aSCameron Grant */ 76933dbf14aSCameron Grant ac97_destroy(codec); 77033dbf14aSCameron Grant return 0; 77133dbf14aSCameron Grant } 77233dbf14aSCameron Grant 77333dbf14aSCameron Grant static int 77466ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 7759ec437a3SCameron Grant { 7769ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 7779ec437a3SCameron Grant 7789ec437a3SCameron Grant if (codec == NULL) 7799ec437a3SCameron Grant return -1; 7809ec437a3SCameron Grant return ac97_reinitmixer(codec); 7819ec437a3SCameron Grant } 7829ec437a3SCameron Grant 7839ec437a3SCameron Grant static int 78466ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 785987e5972SCameron Grant { 786987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 787341f16ccSCameron Grant 78839004e69SCameron Grant if (codec == NULL) 78939004e69SCameron Grant return -1; 790987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 791987e5972SCameron Grant } 792987e5972SCameron Grant 793987e5972SCameron Grant static int 79466ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 795987e5972SCameron Grant { 796987e5972SCameron Grant int i; 797987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 798341f16ccSCameron Grant 79939004e69SCameron Grant if (codec == NULL) 80039004e69SCameron Grant return -1; 801987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 80239004e69SCameron Grant if ((src & (1 << i)) != 0) 80339004e69SCameron Grant break; 804987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 805987e5972SCameron Grant } 806987e5972SCameron Grant 8070f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 8080f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 8090f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 8100f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 8110f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 8120f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 8130f55ac6cSCameron Grant { 0, 0 } 814987e5972SCameron Grant }; 8150f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 8160f55ac6cSCameron Grant 8170f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 8180f55ac6cSCameron Grant 8190f55ac6cSCameron Grant kobj_class_t 8200f55ac6cSCameron Grant ac97_getmixerclass(void) 8210f55ac6cSCameron Grant { 8220f55ac6cSCameron Grant return &ac97mixer_class; 8230f55ac6cSCameron Grant } 8240f55ac6cSCameron Grant 825987e5972SCameron Grant 826