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 2790da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 2890da2b28SAriff Abdullah #include "opt_snd.h" 2990da2b28SAriff Abdullah #endif 3090da2b28SAriff Abdullah 31ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 32ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h> 33f9eb1409SOrion Hodson #include <dev/sound/pcm/ac97_patch.h> 34987e5972SCameron Grant 35a580b31aSAriff Abdullah #include <dev/pci/pcivar.h> 36a580b31aSAriff Abdullah 370f55ac6cSCameron Grant #include "mixer_if.h" 38987e5972SCameron Grant 3967b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 4067b1dce3SCameron Grant 410f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 42987e5972SCameron Grant 4379bb7d52SCameron Grant struct ac97mixtable_entry { 4483234531SAriff Abdullah int reg; /* register index */ 4596524a52SOrion Hodson /* reg < 0 if inverted polarity */ 4696524a52SOrion Hodson unsigned bits:4; /* width of control field */ 4796524a52SOrion Hodson unsigned ofs:4; /* offset (only if stereo=0) */ 4896524a52SOrion Hodson unsigned stereo:1; /* set for stereo controls */ 4996524a52SOrion Hodson unsigned mute:1; /* bit15 is MUTE */ 5096524a52SOrion Hodson unsigned recidx:4; /* index in rec mux */ 5196524a52SOrion Hodson unsigned mask:1; /* use only masked bits */ 5296524a52SOrion Hodson unsigned enable:1; /* entry is enabled */ 5379bb7d52SCameron Grant }; 5479bb7d52SCameron Grant 5569f6d261SAriff Abdullah #define AC97_MIXER_SIZE SOUND_MIXER_NRDEVICES 5669f6d261SAriff Abdullah 5766ef8af5SCameron Grant struct ac97_info { 5866ef8af5SCameron Grant kobj_t methods; 5966ef8af5SCameron Grant device_t dev; 6066ef8af5SCameron Grant void *devinfo; 6119921b23SOrion Hodson u_int32_t id; 62fd7390d6SAriff Abdullah u_int32_t subvendor; 6366ef8af5SCameron Grant unsigned count, caps, se, extcaps, extid, extstat, noext:1; 6479bb7d52SCameron Grant u_int32_t flags; 6569f6d261SAriff Abdullah struct ac97mixtable_entry mix[AC97_MIXER_SIZE]; 6669f6d261SAriff Abdullah char name[16]; 6700acb133SCameron Grant struct mtx *lock; 6866ef8af5SCameron Grant }; 6966ef8af5SCameron Grant 7019921b23SOrion Hodson struct ac97_vendorid { 7119921b23SOrion Hodson u_int32_t id; 7219921b23SOrion Hodson const char *name; 7319921b23SOrion Hodson }; 7419921b23SOrion Hodson 75987e5972SCameron Grant struct ac97_codecid { 7619921b23SOrion Hodson u_int32_t id; 7719921b23SOrion Hodson u_int8_t stepmask; 7819921b23SOrion Hodson u_int8_t noext:1; 79987e5972SCameron Grant char *name; 80f9eb1409SOrion Hodson ac97_patch patch; 81987e5972SCameron Grant }; 82987e5972SCameron Grant 8369f6d261SAriff Abdullah static const struct ac97mixtable_entry ac97mixtable_default[AC97_MIXER_SIZE] = { 8496524a52SOrion Hodson /* [offset] reg bits of st mu re mk en */ 85341f16ccSCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 86a52604cfSOrion Hodson [SOUND_MIXER_OGAIN] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 }, 87341f16ccSCameron Grant [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 88341f16ccSCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 89341f16ccSCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 90341f16ccSCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 91341f16ccSCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 92341f16ccSCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 93341f16ccSCameron Grant [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 9496524a52SOrion Hodson [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 1, 1 }, 9596524a52SOrion Hodson /* use igain for the mic 20dB boost */ 9696524a52SOrion Hodson [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 }, 97341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 98341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 99341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 100341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 101987e5972SCameron Grant }; 102987e5972SCameron Grant 10319921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = { 10419921b23SOrion Hodson { 0x41445300, "Analog Devices" }, 10519921b23SOrion Hodson { 0x414b4d00, "Asahi Kasei" }, 10619921b23SOrion Hodson { 0x414c4300, "Realtek" }, 10719921b23SOrion Hodson { 0x414c4700, "Avance Logic" }, 10819921b23SOrion Hodson { 0x43525900, "Cirrus Logic" }, 10919921b23SOrion Hodson { 0x434d4900, "C-Media Electronics" }, 11019921b23SOrion Hodson { 0x43585400, "Conexant" }, 1113c6b655dSMathew Kanner { 0x44543000, "Diamond Technology" }, 11261a0da1dSOrion Hodson { 0x454d4300, "eMicro" }, 11319921b23SOrion Hodson { 0x45838300, "ESS Technology" }, 1143c6b655dSMathew Kanner { 0x48525300, "Intersil" }, 11519921b23SOrion Hodson { 0x49434500, "ICEnsemble" }, 1163c6b655dSMathew Kanner { 0x49544500, "ITE, Inc." }, 11719921b23SOrion Hodson { 0x4e534300, "National Semiconductor" }, 11819921b23SOrion Hodson { 0x50534300, "Philips Semiconductor" }, 11919921b23SOrion Hodson { 0x83847600, "SigmaTel" }, 1203c6b655dSMathew Kanner { 0x53494c00, "Silicon Laboratories" }, 12119921b23SOrion Hodson { 0x54524100, "TriTech" }, 1223c6b655dSMathew Kanner { 0x54584e00, "Texas Instruments" }, 12319921b23SOrion Hodson { 0x56494100, "VIA Technologies" }, 1243c6b655dSMathew Kanner { 0x57454300, "Winbond" }, 12519921b23SOrion Hodson { 0x574d4c00, "Wolfson" }, 12619921b23SOrion Hodson { 0x594d4800, "Yamaha" }, 12786336196SAlexander Leidinger /* 12886336196SAlexander Leidinger * XXX This is a fluke, really! The real vendor 12986336196SAlexander Leidinger * should be SigmaTel, not this! This should be 13086336196SAlexander Leidinger * removed someday! 13186336196SAlexander Leidinger */ 1326f0182bdSOrion Hodson { 0x01408300, "Creative" }, 13319921b23SOrion Hodson { 0x00000000, NULL } 13419921b23SOrion Hodson }; 13519921b23SOrion Hodson 136987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 13719921b23SOrion Hodson { 0x41445303, 0x00, 0, "AD1819", 0 }, 13819921b23SOrion Hodson { 0x41445340, 0x00, 0, "AD1881", 0 }, 13919921b23SOrion Hodson { 0x41445348, 0x00, 0, "AD1881A", 0 }, 14019921b23SOrion Hodson { 0x41445360, 0x00, 0, "AD1885", 0 }, 14119921b23SOrion Hodson { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 142ba548c64SOrion Hodson { 0x41445362, 0x00, 0, "AD1887", 0 }, 143ba548c64SOrion Hodson { 0x41445363, 0x00, 0, "AD1886A", 0 }, 144c5cb8d60SScott Long { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, 145a52604cfSOrion Hodson { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, 146ba548c64SOrion Hodson { 0x41445372, 0x00, 0, "AD1981A", 0 }, 1477699548fSAriff Abdullah { 0x41445374, 0x00, 0, "AD1981B", ad1981b_patch }, 148a52604cfSOrion Hodson { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, 149e1e05d5dSAriff Abdullah { 0x41445378, 0x00, 0, "AD1986", ad198x_patch }, 15019921b23SOrion Hodson { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 15119921b23SOrion Hodson { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 15219921b23SOrion Hodson { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 1533c6b655dSMathew Kanner { 0x414b4d06, 0x00, 0, "AK4544A", 0 }, 1543c6b655dSMathew Kanner { 0x454b4d07, 0x00, 0, "AK4545", 0 }, 15519921b23SOrion Hodson { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 1569963235aSOrion Hodson { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 15719921b23SOrion Hodson { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 15819921b23SOrion Hodson { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 15919921b23SOrion Hodson { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 160b327ee51SAriff Abdullah { 0x414c4752, 0x0f, 0, "ALC250", 0 }, 161fd7390d6SAriff Abdullah { 0x414c4760, 0x0f, 0, "ALC655", alc655_patch }, 162b7994e34SPyun YongHyeon { 0x414c4770, 0x0f, 0, "ALC203", 0 }, 16394ed763dSJun Kuriyama { 0x414c4780, 0x0f, 0, "ALC658", 0 }, 1643c6b655dSMathew Kanner { 0x414c4790, 0x0f, 0, "ALC850", 0 }, 16519921b23SOrion Hodson { 0x43525900, 0x07, 0, "CS4297", 0 }, 16619921b23SOrion Hodson { 0x43525910, 0x07, 0, "CS4297A", 0 }, 16719921b23SOrion Hodson { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 168e5728f83SMIHIRA Sanpei Yoshiro { 0x4352592d, 0x07, 0, "CS4294", 0 }, 16919921b23SOrion Hodson { 0x43525930, 0x07, 0, "CS4299", 0 }, 17019921b23SOrion Hodson { 0x43525940, 0x07, 0, "CS4201", 0 }, 171b2a0f525SOrion Hodson { 0x43525958, 0x07, 0, "CS4205", 0 }, 17219921b23SOrion Hodson { 0x43525960, 0x07, 0, "CS4291A", 0 }, 173cb44f623SAlexander Leidinger { 0x434d4961, 0x00, 0, "CMI9739", cmi9739_patch }, 17419921b23SOrion Hodson { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 175cb44f623SAlexander Leidinger { 0x434d4978, 0x00, 0, "CMI9761", 0 }, 176cb44f623SAlexander Leidinger { 0x434d4982, 0x00, 0, "CMI9761", 0 }, 177cb44f623SAlexander Leidinger { 0x434d4983, 0x00, 0, "CMI9761", 0 }, 1783c6b655dSMathew Kanner { 0x43585421, 0x00, 0, "HSD11246", 0 }, 1793c6b655dSMathew Kanner { 0x43585428, 0x07, 0, "CX20468", 0 }, 18080138937SAriff Abdullah { 0x43585430, 0x00, 0, "CX20468-21", 0 }, 1813c6b655dSMathew Kanner { 0x44543000, 0x00, 0, "DT0398", 0 }, 18261a0da1dSOrion Hodson { 0x454d4323, 0x00, 0, "EM28023", 0 }, 18361a0da1dSOrion Hodson { 0x454d4328, 0x00, 0, "EM28028", 0 }, 18419921b23SOrion Hodson { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 1853c6b655dSMathew Kanner { 0x48525300, 0x00, 0, "HMP9701", 0 }, 18619921b23SOrion Hodson { 0x49434501, 0x00, 0, "ICE1230", 0 }, 18719921b23SOrion Hodson { 0x49434511, 0x00, 0, "ICE1232", 0 }, 18819921b23SOrion Hodson { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 18949fd6905SOrion Hodson { 0x49434551, 0x03, 0, "VT1616", 0 }, /* Via badged ICE */ 1903c6b655dSMathew Kanner { 0x49544520, 0x00, 0, "ITE2226E", 0 }, 1913c6b655dSMathew Kanner { 0x49544560, 0x07, 0, "ITE2646E", 0 }, /* XXX: patch needed */ 19219921b23SOrion Hodson { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 19319921b23SOrion Hodson { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 19419921b23SOrion Hodson { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 19519921b23SOrion Hodson { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 1963c6b655dSMathew Kanner { 0x4e534331, 0x00, 0, "LM4549", 0 }, 19719921b23SOrion Hodson { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 19819921b23SOrion Hodson { 0x4e534350, 0x00, 0, "LM4550", 0 }, 19919921b23SOrion Hodson { 0x50534301, 0x00, 0, "UCB1510", 0 }, 20019921b23SOrion Hodson { 0x50534304, 0x00, 0, "UCB1400", 0 }, 20119921b23SOrion Hodson { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 20219921b23SOrion Hodson { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 20319921b23SOrion Hodson { 0x83847605, 0x00, 0, "STAC9704", 0 }, 20419921b23SOrion Hodson { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 20519921b23SOrion Hodson { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 20619921b23SOrion Hodson { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 20719921b23SOrion Hodson { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 20819921b23SOrion Hodson { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 20919921b23SOrion Hodson { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 21019921b23SOrion Hodson { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 21119921b23SOrion Hodson { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 21219921b23SOrion Hodson { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 213cb44f623SAlexander Leidinger { 0x83847666, 0x00, 0, "STAC9766/67", 0 }, 21419921b23SOrion Hodson { 0x53494c22, 0x00, 0, "Si3036", 0 }, 21519921b23SOrion Hodson { 0x53494c23, 0x00, 0, "Si3038", 0 }, 21619921b23SOrion Hodson { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 21719921b23SOrion Hodson { 0x54524106, 0x00, 0, "TR28026", 0 }, 21819921b23SOrion Hodson { 0x54524108, 0x00, 0, "TR28028", 0 }, 21919921b23SOrion Hodson { 0x54524123, 0x00, 0, "TR28602", 0 }, 2203c6b655dSMathew Kanner { 0x54524e03, 0x07, 0, "TLV320AIC27", 0 }, 2213c6b655dSMathew Kanner { 0x54584e20, 0x00, 0, "TLC320AD90", 0 }, 22219921b23SOrion Hodson { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 22380138937SAriff Abdullah { 0x56494170, 0x00, 0, "VIA1617A", 0 }, 22419921b23SOrion Hodson { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 22519921b23SOrion Hodson { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 22619921b23SOrion Hodson { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 22719921b23SOrion Hodson { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 2283c6b655dSMathew Kanner { 0x574d4d09, 0x00, 0, "WM9709", 0 }, 2293c6b655dSMathew Kanner { 0x574d4c12, 0x00, 0, "WM9711/12", 0 }, /* XXX: patch needed */ 2303c6b655dSMathew Kanner { 0x57454301, 0x00, 0, "W83971D", 0 }, 23119921b23SOrion Hodson { 0x594d4800, 0x00, 0, "YMF743", 0 }, 23219921b23SOrion Hodson { 0x594d4802, 0x00, 0, "YMF752", 0 }, 23319921b23SOrion Hodson { 0x594d4803, 0x00, 0, "YMF753", 0 }, 23486336196SAlexander Leidinger /* 23586336196SAlexander Leidinger * XXX This is a fluke, really! The real codec 23686336196SAlexander Leidinger * should be STAC9704, not this! This should be 23786336196SAlexander Leidinger * removed someday! 23886336196SAlexander Leidinger */ 2396f0182bdSOrion Hodson { 0x01408384, 0x00, 0, "EV1938", 0 }, 24019921b23SOrion Hodson { 0, 0, 0, NULL, 0 } 241987e5972SCameron Grant }; 242987e5972SCameron Grant 243987e5972SCameron Grant static char *ac97enhancement[] = { 24404553e63SCameron Grant "no 3D Stereo Enhancement", 245987e5972SCameron Grant "Analog Devices Phat Stereo", 246987e5972SCameron Grant "Creative Stereo Enhancement", 247987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 248987e5972SCameron Grant "Yamaha Ymersion", 249987e5972SCameron Grant "BBE 3D Stereo Enhancement", 250987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 251987e5972SCameron Grant "Qsound QXpander", 252987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 253987e5972SCameron Grant "SRS 3D Stereo Enhancement", 254987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 255987e5972SCameron Grant "AKM 3D Audio", 256987e5972SCameron Grant "Aureal Stereo Enhancement", 257987e5972SCameron Grant "Aztech 3D Enhancement", 258987e5972SCameron Grant "Binaura 3D Audio Enhancement", 259987e5972SCameron Grant "ESS Technology Stereo Enhancement", 260987e5972SCameron Grant "Harman International VMAx", 261987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 262987e5972SCameron Grant "Philips Incredible Sound", 263987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 264987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 265987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 266987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 267987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 268987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 269987e5972SCameron Grant "Delta Integration 3D Enhancement", 270987e5972SCameron Grant "SigmaTel 3D Enhancement", 271987e5972SCameron Grant "Reserved 27", 272987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 273987e5972SCameron Grant "Reserved 29", 274987e5972SCameron Grant "Reserved 30", 275987e5972SCameron Grant "Reserved 31" 276987e5972SCameron Grant }; 277987e5972SCameron Grant 278987e5972SCameron Grant static char *ac97feature[] = { 279987e5972SCameron Grant "mic channel", 280987e5972SCameron Grant "reserved", 281987e5972SCameron Grant "tone", 282987e5972SCameron Grant "simulated stereo", 283987e5972SCameron Grant "headphone", 284987e5972SCameron Grant "bass boost", 285987e5972SCameron Grant "18 bit DAC", 286987e5972SCameron Grant "20 bit DAC", 287987e5972SCameron Grant "18 bit ADC", 288987e5972SCameron Grant "20 bit ADC" 289987e5972SCameron Grant }; 290987e5972SCameron Grant 29139004e69SCameron Grant static char *ac97extfeature[] = { 29239004e69SCameron Grant "variable rate PCM", 29339004e69SCameron Grant "double rate PCM", 29439004e69SCameron Grant "reserved 1", 29539004e69SCameron Grant "variable rate mic", 29639004e69SCameron Grant "reserved 2", 29739004e69SCameron Grant "reserved 3", 29839004e69SCameron Grant "center DAC", 29939004e69SCameron Grant "surround DAC", 30039004e69SCameron Grant "LFE DAC", 30139004e69SCameron Grant "AMAP", 30239004e69SCameron Grant "reserved 4", 30339004e69SCameron Grant "reserved 5", 30439004e69SCameron Grant "reserved 6", 30539004e69SCameron Grant "reserved 7", 30639004e69SCameron Grant }; 30739004e69SCameron Grant 308f9eb1409SOrion Hodson u_int16_t 309f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg) 31039004e69SCameron Grant { 31186336196SAlexander Leidinger if (codec->flags & AC97_F_RDCD_BUG) { 31286336196SAlexander Leidinger u_int16_t i[2], j = 100; 31386336196SAlexander Leidinger 31486336196SAlexander Leidinger i[0] = AC97_READ(codec->methods, codec->devinfo, reg); 31586336196SAlexander Leidinger i[1] = AC97_READ(codec->methods, codec->devinfo, reg); 31686336196SAlexander Leidinger while (i[0] != i[1] && j) 31786336196SAlexander Leidinger i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg); 31886336196SAlexander Leidinger #if 0 31986336196SAlexander Leidinger if (j < 100) { 32086336196SAlexander Leidinger device_printf(codec->dev, "%s(): Inconsistent register value at" 32186336196SAlexander Leidinger " 0x%08x (retry: %d)\n", __func__, reg, 100 - j); 32286336196SAlexander Leidinger } 32386336196SAlexander Leidinger #endif 32486336196SAlexander Leidinger return i[!(j & 1)]; 32586336196SAlexander Leidinger } 3260f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 32739004e69SCameron Grant } 32839004e69SCameron Grant 329f9eb1409SOrion Hodson void 330f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 33139004e69SCameron Grant { 3320f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 33339004e69SCameron Grant } 33439004e69SCameron Grant 335c6d4b83aSOrion Hodson static void 336c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec) 337c6d4b83aSOrion Hodson { 338c6d4b83aSOrion Hodson u_int32_t i, ps; 339f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RESET, 0); 340c6d4b83aSOrion Hodson for (i = 0; i < 500; i++) { 341f9eb1409SOrion Hodson ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 342c6d4b83aSOrion Hodson if (ps == AC97_POWER_STATUS) 343c6d4b83aSOrion Hodson return; 344c6d4b83aSOrion Hodson DELAY(1000); 345c6d4b83aSOrion Hodson } 346a825c6e5SOrion Hodson device_printf(codec->dev, "AC97 reset timed out.\n"); 347c6d4b83aSOrion Hodson } 348c6d4b83aSOrion Hodson 34939004e69SCameron Grant int 35039004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 35139004e69SCameron Grant { 35239004e69SCameron Grant u_int16_t v; 35339004e69SCameron Grant 35439004e69SCameron Grant switch(which) { 35539004e69SCameron Grant case AC97_REGEXT_FDACRATE: 35639004e69SCameron Grant case AC97_REGEXT_SDACRATE: 35739004e69SCameron Grant case AC97_REGEXT_LDACRATE: 35839004e69SCameron Grant case AC97_REGEXT_LADCRATE: 35939004e69SCameron Grant case AC97_REGEXT_MADCRATE: 36039004e69SCameron Grant break; 36139004e69SCameron Grant 36239004e69SCameron Grant default: 36339004e69SCameron Grant return -1; 36439004e69SCameron Grant } 36539004e69SCameron Grant 36666ef8af5SCameron Grant snd_mtxlock(codec->lock); 36739004e69SCameron Grant if (rate != 0) { 36839004e69SCameron Grant v = rate; 36939004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 37039004e69SCameron Grant v >>= 1; 371f9eb1409SOrion Hodson ac97_wrcd(codec, which, v); 37239004e69SCameron Grant } 373f9eb1409SOrion Hodson v = ac97_rdcd(codec, which); 37439004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 37539004e69SCameron Grant v <<= 1; 37666ef8af5SCameron Grant snd_mtxunlock(codec->lock); 37739004e69SCameron Grant return v; 37839004e69SCameron Grant } 37939004e69SCameron Grant 38039004e69SCameron Grant int 38139004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 38239004e69SCameron Grant { 38339004e69SCameron Grant mode &= AC97_EXTCAPS; 384647fbfebSOrion Hodson if ((mode & ~codec->extcaps) != 0) { 385647fbfebSOrion Hodson device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 386647fbfebSOrion Hodson mode); 38739004e69SCameron Grant return -1; 388647fbfebSOrion Hodson } 38966ef8af5SCameron Grant snd_mtxlock(codec->lock); 390f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 391f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 39266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 39339004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 39439004e69SCameron Grant } 39539004e69SCameron Grant 3969ec437a3SCameron Grant u_int16_t 3979ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 3989ec437a3SCameron Grant { 3999ec437a3SCameron Grant return codec->extstat; 4009ec437a3SCameron Grant } 4019ec437a3SCameron Grant 4029ec437a3SCameron Grant u_int16_t 4039ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 4049ec437a3SCameron Grant { 4059ec437a3SCameron Grant return codec->extcaps; 4069ec437a3SCameron Grant } 4079ec437a3SCameron Grant 4085d91ad67SCameron Grant u_int16_t 4095d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec) 4105d91ad67SCameron Grant { 4115d91ad67SCameron Grant return codec->caps; 4125d91ad67SCameron Grant } 4135d91ad67SCameron Grant 414fd7390d6SAriff Abdullah u_int32_t 415fd7390d6SAriff Abdullah ac97_getsubvendor(struct ac97_info *codec) 416fd7390d6SAriff Abdullah { 417fd7390d6SAriff Abdullah return codec->subvendor; 418fd7390d6SAriff Abdullah } 419fd7390d6SAriff Abdullah 420987e5972SCameron Grant static int 421987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 422987e5972SCameron Grant { 423987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 424341f16ccSCameron Grant 425987e5972SCameron Grant if (e->recidx > 0) { 426987e5972SCameron Grant int val = e->recidx - 1; 427987e5972SCameron Grant val |= val << 8; 42866ef8af5SCameron Grant snd_mtxlock(codec->lock); 429f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RECSEL, val); 43066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 431987e5972SCameron Grant return 0; 43239004e69SCameron Grant } else 43339004e69SCameron Grant return -1; 434987e5972SCameron Grant } 435987e5972SCameron Grant 436987e5972SCameron Grant static int 437987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 438987e5972SCameron Grant { 439987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 440341f16ccSCameron Grant 441341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 44296524a52SOrion Hodson int mask, max, val, reg; 44396524a52SOrion Hodson 44496524a52SOrion Hodson reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */ 44596524a52SOrion Hodson max = (1 << e->bits) - 1; /* actual range */ 44696524a52SOrion Hodson mask = (max << 8) | max; /* bits of interest */ 447987e5972SCameron Grant 44839004e69SCameron Grant if (!e->stereo) 44939004e69SCameron Grant right = left; 45096524a52SOrion Hodson 45196524a52SOrion Hodson /* 45296524a52SOrion Hodson * Invert the range if the polarity requires so, 45396524a52SOrion Hodson * then scale to 0..max-1 to compute the value to 45496524a52SOrion Hodson * write into the codec, and scale back to 0..100 45596524a52SOrion Hodson * for the return value. 45696524a52SOrion Hodson */ 457987e5972SCameron Grant if (e->reg > 0) { 458987e5972SCameron Grant left = 100 - left; 459987e5972SCameron Grant right = 100 - right; 460987e5972SCameron Grant } 461987e5972SCameron Grant 462987e5972SCameron Grant left = (left * max) / 100; 463987e5972SCameron Grant right = (right * max) / 100; 464987e5972SCameron Grant 465987e5972SCameron Grant val = (left << 8) | right; 466987e5972SCameron Grant 467987e5972SCameron Grant left = (left * 100) / max; 468987e5972SCameron Grant right = (right * 100) / max; 469987e5972SCameron Grant 470987e5972SCameron Grant if (e->reg > 0) { 471987e5972SCameron Grant left = 100 - left; 472987e5972SCameron Grant right = 100 - right; 473987e5972SCameron Grant } 474987e5972SCameron Grant 47596524a52SOrion Hodson /* 47696524a52SOrion Hodson * For mono controls, trim val and mask, also taking 47796524a52SOrion Hodson * care of e->ofs (offset of control field). 47896524a52SOrion Hodson */ 47996524a52SOrion Hodson if (e->ofs) { 480987e5972SCameron Grant val &= max; 481987e5972SCameron Grant val <<= e->ofs; 48296524a52SOrion Hodson mask = (max << e->ofs); 48396524a52SOrion Hodson } 48496524a52SOrion Hodson 48596524a52SOrion Hodson /* 48696524a52SOrion Hodson * If we have a mute bit, add it to the mask and 48796524a52SOrion Hodson * update val and set mute if both channels require a 48896524a52SOrion Hodson * zero volume. 48996524a52SOrion Hodson */ 49096524a52SOrion Hodson if (e->mute == 1) { 49196524a52SOrion Hodson mask |= AC97_MUTE; 49296524a52SOrion Hodson if (left == 0 && right == 0) 49396524a52SOrion Hodson val = AC97_MUTE; 49496524a52SOrion Hodson } 49596524a52SOrion Hodson 49696524a52SOrion Hodson /* 49796524a52SOrion Hodson * If the mask bit is set, do not alter the other bits. 49896524a52SOrion Hodson */ 49996524a52SOrion Hodson snd_mtxlock(codec->lock); 500987e5972SCameron Grant if (e->mask) { 50186336196SAlexander Leidinger int cur = ac97_rdcd(codec, reg); 50296524a52SOrion Hodson val |= cur & ~(mask); 503987e5972SCameron Grant } 504f9eb1409SOrion Hodson ac97_wrcd(codec, reg, val); 50566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 506987e5972SCameron Grant return left | (right << 8); 507341f16ccSCameron Grant } else { 50886336196SAlexander Leidinger #if 0 50986336196SAlexander Leidinger printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); 51086336196SAlexander Leidinger #endif 51139004e69SCameron Grant return -1; 512987e5972SCameron Grant } 513341f16ccSCameron Grant } 514987e5972SCameron Grant 515108082c4SOrion Hodson static void 516108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec) 517108082c4SOrion Hodson { 518cfd5696dSOrion Hodson int keep_ogain; 519cfd5696dSOrion Hodson 520a52604cfSOrion Hodson /* 521cfd5696dSOrion Hodson * By default, The ac97 aux_out register (0x04) corresponds to OSS's 522cfd5696dSOrion Hodson * OGAIN setting. 523a52604cfSOrion Hodson * 524cfd5696dSOrion Hodson * We first check whether aux_out is a valid register. If not 525cfd5696dSOrion Hodson * we may not want to keep ogain. 526a52604cfSOrion Hodson */ 527cfd5696dSOrion Hodson keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000; 528a52604cfSOrion Hodson 529a52604cfSOrion Hodson /* 530a52604cfSOrion Hodson * Determine what AUX_OUT really means, it can be: 531108082c4SOrion Hodson * 532108082c4SOrion Hodson * 1. Headphone out. 533108082c4SOrion Hodson * 2. 4-Channel Out 534108082c4SOrion Hodson * 3. True line level out (effectively master volume). 535108082c4SOrion Hodson * 536108082c4SOrion Hodson * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 537108082c4SOrion Hodson */ 538a52604cfSOrion Hodson if (codec->extcaps & AC97_EXTCAP_SDAC && 539f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 540cfd5696dSOrion Hodson codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND; 541cfd5696dSOrion Hodson keep_ogain = 1; 542cfd5696dSOrion Hodson } 543cfd5696dSOrion Hodson 544cfd5696dSOrion Hodson if (keep_ogain == 0) { 545cfd5696dSOrion Hodson bzero(&codec->mix[SOUND_MIXER_OGAIN], 546cfd5696dSOrion Hodson sizeof(codec->mix[SOUND_MIXER_OGAIN])); 547108082c4SOrion Hodson } 548a52604cfSOrion Hodson } 549a52604cfSOrion Hodson 550a52604cfSOrion Hodson static void 551a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec) 552a52604cfSOrion Hodson { 553f0c4d272SAriff Abdullah /* 554f0c4d272SAriff Abdullah * YMF chips does not indicate tone and 3D enhancement capability 555f0c4d272SAriff Abdullah * in the AC97_REG_RESET register. 556f0c4d272SAriff Abdullah */ 557f0c4d272SAriff Abdullah switch (codec->id) { 558f0c4d272SAriff Abdullah case 0x594d4800: /* YMF743 */ 559f0c4d272SAriff Abdullah case 0x594d4803: /* YMF753 */ 560f0c4d272SAriff Abdullah codec->caps |= AC97_CAP_TONE; 561f0c4d272SAriff Abdullah codec->se |= 0x04; 562f0c4d272SAriff Abdullah break; 563f0c4d272SAriff Abdullah case 0x594d4802: /* YMF752 */ 564f0c4d272SAriff Abdullah codec->se |= 0x04; 565f0c4d272SAriff Abdullah break; 566f0c4d272SAriff Abdullah default: 567f0c4d272SAriff Abdullah break; 568f0c4d272SAriff Abdullah } 569f0c4d272SAriff Abdullah 570a52604cfSOrion Hodson /* Hide treble and bass if they don't exist */ 571a52604cfSOrion Hodson if ((codec->caps & AC97_CAP_TONE) == 0) { 572a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_BASS], 573a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_BASS])); 574a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_TREBLE], 575a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_TREBLE])); 576a52604cfSOrion Hodson } 577108082c4SOrion Hodson } 578108082c4SOrion Hodson 57919921b23SOrion Hodson static const char* 58019921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 58119921b23SOrion Hodson { 58219921b23SOrion Hodson if (cname == NULL) { 58319921b23SOrion Hodson sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 58419921b23SOrion Hodson return buf; 58519921b23SOrion Hodson } 58619921b23SOrion Hodson 58719921b23SOrion Hodson if (vname == NULL) vname = "Unknown"; 58819921b23SOrion Hodson 58919921b23SOrion Hodson if (bootverbose) { 59019921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 59119921b23SOrion Hodson } else { 59219921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec", vname, cname); 59319921b23SOrion Hodson } 59419921b23SOrion Hodson return buf; 59519921b23SOrion Hodson } 59619921b23SOrion Hodson 597987e5972SCameron Grant static unsigned 59839004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 599987e5972SCameron Grant { 600f9eb1409SOrion Hodson ac97_patch codec_patch; 60119921b23SOrion Hodson const char *cname, *vname; 60219921b23SOrion Hodson char desc[80]; 60319921b23SOrion Hodson u_int8_t model, step; 60486336196SAlexander Leidinger unsigned i, j, k, bit, old; 605987e5972SCameron Grant u_int32_t id; 60686336196SAlexander Leidinger int reg; 607987e5972SCameron Grant 60866ef8af5SCameron Grant snd_mtxlock(codec->lock); 6090f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 610cd2c103aSCameron Grant if (codec->count == 0) { 61104553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 61266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 61304553e63SCameron Grant return ENODEV; 61404553e63SCameron Grant } 6159ec437a3SCameron Grant 616f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 617c6d4b83aSOrion Hodson ac97_reset(codec); 618f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 619987e5972SCameron Grant 620f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REG_RESET); 62186336196SAlexander Leidinger j = ac97_rdcd(codec, AC97_REG_RESET); 62269f6d261SAriff Abdullah k = ac97_rdcd(codec, AC97_REG_RESET); 62386336196SAlexander Leidinger /* 62486336196SAlexander Leidinger * Let see if this codec can return consistent value. 62586336196SAlexander Leidinger * If not, turn on aggressive read workaround 62686336196SAlexander Leidinger * (STAC9704 comes in mind). 62786336196SAlexander Leidinger */ 62869f6d261SAriff Abdullah if (i != j || j != k) { 62986336196SAlexander Leidinger codec->flags |= AC97_F_RDCD_BUG; 63086336196SAlexander Leidinger i = ac97_rdcd(codec, AC97_REG_RESET); 63186336196SAlexander Leidinger } 632987e5972SCameron Grant codec->caps = i & 0x03ff; 633987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 634987e5972SCameron Grant 635f9eb1409SOrion Hodson id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 636e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 637e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 63866ef8af5SCameron Grant snd_mtxunlock(codec->lock); 639e620d959SCameron Grant return ENODEV; 640e620d959SCameron Grant } 6416b4b88f7SCameron Grant 64219921b23SOrion Hodson codec->id = id; 643fd7390d6SAriff Abdullah codec->subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16; 644fd7390d6SAriff Abdullah codec->subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) & 645fd7390d6SAriff Abdullah 0x0000ffff; 646cd2c103aSCameron Grant codec->noext = 0; 647f9eb1409SOrion Hodson codec_patch = NULL; 64819921b23SOrion Hodson 64919921b23SOrion Hodson cname = NULL; 65019921b23SOrion Hodson model = step = 0; 651cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 65219921b23SOrion Hodson u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 65319921b23SOrion Hodson if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 654cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 655f9eb1409SOrion Hodson codec_patch = ac97codecid[i].patch; 65619921b23SOrion Hodson cname = ac97codecid[i].name; 65719921b23SOrion Hodson model = (id & modelmask) & 0xff; 65819921b23SOrion Hodson step = (id & ~modelmask) & 0xff; 65919921b23SOrion Hodson break; 66019921b23SOrion Hodson } 66119921b23SOrion Hodson } 66219921b23SOrion Hodson 66319921b23SOrion Hodson vname = NULL; 66419921b23SOrion Hodson for (i = 0; ac97vendorid[i].id; i++) { 66519921b23SOrion Hodson if (ac97vendorid[i].id == (id & 0xffffff00)) { 66619921b23SOrion Hodson vname = ac97vendorid[i].name; 66719921b23SOrion Hodson break; 668cd2c103aSCameron Grant } 669cd2c103aSCameron Grant } 6706b4b88f7SCameron Grant 671cd2c103aSCameron Grant codec->extcaps = 0; 672cd2c103aSCameron Grant codec->extid = 0; 673cd2c103aSCameron Grant codec->extstat = 0; 6746a6ee5bbSCameron Grant if (!codec->noext) { 675f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REGEXT_ID); 6766a6ee5bbSCameron Grant if (i != 0xffff) { 67739004e69SCameron Grant codec->extcaps = i & 0x3fff; 67839004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 679f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 6806b4b88f7SCameron Grant } 6816a6ee5bbSCameron Grant } 682987e5972SCameron Grant 68369f6d261SAriff Abdullah for (i = 0; i < AC97_MIXER_SIZE; i++) { 684108082c4SOrion Hodson codec->mix[i] = ac97mixtable_default[i]; 685108082c4SOrion Hodson } 686108082c4SOrion Hodson ac97_fix_auxout(codec); 687a52604cfSOrion Hodson ac97_fix_tone(codec); 688f9eb1409SOrion Hodson if (codec_patch) 689f9eb1409SOrion Hodson codec_patch(codec); 690108082c4SOrion Hodson 69169f6d261SAriff Abdullah for (i = 0; i < AC97_MIXER_SIZE; i++) { 69233c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 69386336196SAlexander Leidinger reg = codec->mix[i].reg; 69486336196SAlexander Leidinger if (reg < 0) 69586336196SAlexander Leidinger reg = -reg; 69686336196SAlexander Leidinger if (k && reg) { 69786336196SAlexander Leidinger j = old = ac97_rdcd(codec, reg); 69886336196SAlexander Leidinger /* 69986336196SAlexander Leidinger * Test for mute bit (except for AC97_MIX_TONE, 70086336196SAlexander Leidinger * where we simply assume it as available). 70186336196SAlexander Leidinger */ 70286336196SAlexander Leidinger if (codec->mix[i].mute) { 70386336196SAlexander Leidinger ac97_wrcd(codec, reg, j | 0x8000); 70486336196SAlexander Leidinger j = ac97_rdcd(codec, reg); 70586336196SAlexander Leidinger } else 706fe435202SAlexander Leidinger j |= 0x8000; 70786336196SAlexander Leidinger if ((j & 0x8000)) { 70886336196SAlexander Leidinger /* 70986336196SAlexander Leidinger * Test whether the control width should be 71086336196SAlexander Leidinger * 4, 5 or 6 bit. For 5bit register, we should 71186336196SAlexander Leidinger * test it whether it's really 5 or 6bit. Leave 71286336196SAlexander Leidinger * 4bit register alone, because sometimes an 71386336196SAlexander Leidinger * attempt to write past 4th bit may cause 71486336196SAlexander Leidinger * incorrect result especially for AC97_MIX_BEEP 71586336196SAlexander Leidinger * (ac97 2.3). 71686336196SAlexander Leidinger */ 71786336196SAlexander Leidinger bit = codec->mix[i].bits; 71886336196SAlexander Leidinger if (bit == 5) 71986336196SAlexander Leidinger bit++; 72086336196SAlexander Leidinger j = ((1 << bit) - 1) << codec->mix[i].ofs; 72186336196SAlexander Leidinger ac97_wrcd(codec, reg, 72286336196SAlexander Leidinger j | (codec->mix[i].mute ? 0x8000 : 0)); 72386336196SAlexander Leidinger k = ac97_rdcd(codec, reg) & j; 72486336196SAlexander Leidinger k >>= codec->mix[i].ofs; 72586336196SAlexander Leidinger if (reg == AC97_MIX_TONE && 72686336196SAlexander Leidinger ((k & 0x0001) == 0x0000)) 727fe435202SAlexander Leidinger k >>= 1; 72886336196SAlexander Leidinger for (j = 0; k >> j; j++) 72986336196SAlexander Leidinger ; 730fe435202SAlexander Leidinger if (j != 0) { 731fe435202SAlexander Leidinger #if 0 73286336196SAlexander Leidinger device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n", 73386336196SAlexander Leidinger i, k, bit, codec->mix[i].bits, j); 734fe435202SAlexander Leidinger #endif 73586336196SAlexander Leidinger codec->mix[i].enable = 1; 73686336196SAlexander Leidinger codec->mix[i].bits = j; 737eaf70083SAriff Abdullah } else if (reg == AC97_MIX_BEEP) { 738eaf70083SAriff Abdullah /* 739eaf70083SAriff Abdullah * Few codec such as CX20468-21 does 740eaf70083SAriff Abdullah * have this control register, although 741eaf70083SAriff Abdullah * the only usable part is the mute bit. 742eaf70083SAriff Abdullah */ 743eaf70083SAriff Abdullah codec->mix[i].enable = 1; 744fe435202SAlexander Leidinger } else 745fe435202SAlexander Leidinger codec->mix[i].enable = 0; 746fe435202SAlexander Leidinger } else 747fe435202SAlexander Leidinger codec->mix[i].enable = 0; 74886336196SAlexander Leidinger ac97_wrcd(codec, reg, old); 749fe435202SAlexander Leidinger } 750fe435202SAlexander Leidinger #if 0 751fe435202SAlexander Leidinger printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); 752fe435202SAlexander Leidinger #endif 753341f16ccSCameron Grant } 754987e5972SCameron Grant 75519921b23SOrion Hodson device_printf(codec->dev, "<%s>\n", 75619921b23SOrion Hodson ac97_hw_desc(codec->id, vname, cname, desc)); 757a825c6e5SOrion Hodson 758987e5972SCameron Grant if (bootverbose) { 75986336196SAlexander Leidinger if (codec->flags & AC97_F_RDCD_BUG) 76086336196SAlexander Leidinger device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n"); 76119921b23SOrion Hodson device_printf(codec->dev, "Codec features "); 76239004e69SCameron Grant for (i = j = 0; i < 10; i++) 76339004e69SCameron Grant if (codec->caps & (1 << i)) 76439004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 76539004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 766987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 76739004e69SCameron Grant 76839004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 76919921b23SOrion Hodson device_printf(codec->dev, "%s codec", 77019921b23SOrion Hodson codec->extid? "Secondary" : "Primary"); 77139004e69SCameron Grant if (codec->extcaps) 77239004e69SCameron Grant printf(" extended features "); 77339004e69SCameron Grant for (i = j = 0; i < 14; i++) 77439004e69SCameron Grant if (codec->extcaps & (1 << i)) 77539004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 77639004e69SCameron Grant printf("\n"); 77739004e69SCameron Grant } 778987e5972SCameron Grant } 779987e5972SCameron Grant 780fe435202SAlexander Leidinger i = 0; 781fe435202SAlexander Leidinger while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) { 782fe435202SAlexander Leidinger if (++i == 100) { 78303a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 784fe435202SAlexander Leidinger break; 785fe435202SAlexander Leidinger } 786fe435202SAlexander Leidinger DELAY(1000); 787fe435202SAlexander Leidinger } 788fe435202SAlexander Leidinger if (bootverbose) 789fe435202SAlexander Leidinger device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i); 79066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 791987e5972SCameron Grant return 0; 792987e5972SCameron Grant } 793987e5972SCameron Grant 7949ec437a3SCameron Grant static unsigned 7959ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 7969ec437a3SCameron Grant { 79766ef8af5SCameron Grant snd_mtxlock(codec->lock); 7980f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 7999ec437a3SCameron Grant if (codec->count == 0) { 8009ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 80166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 8029ec437a3SCameron Grant return ENODEV; 8039ec437a3SCameron Grant } 8049ec437a3SCameron Grant 805f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 806c6d4b83aSOrion Hodson ac97_reset(codec); 807f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 8089ec437a3SCameron Grant 8099ec437a3SCameron Grant if (!codec->noext) { 810f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 811f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 812b60e55dbSGuido van Rooij != codec->extstat) 8139ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 814b60e55dbSGuido van Rooij codec->extstat, 815f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_REGEXT_STAT) & 816b60e55dbSGuido van Rooij AC97_EXTCAPS); 8179ec437a3SCameron Grant } 8189ec437a3SCameron Grant 819f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 8209ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 82166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 8229ec437a3SCameron Grant return 0; 8239ec437a3SCameron Grant } 8249ec437a3SCameron Grant 825987e5972SCameron Grant struct ac97_info * 8260f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 827987e5972SCameron Grant { 828987e5972SCameron Grant struct ac97_info *codec; 82990da2b28SAriff Abdullah int i; 830987e5972SCameron Grant 831082f6383SAriff Abdullah codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO); 83269f6d261SAriff Abdullah snprintf(codec->name, sizeof(codec->name), "%s:ac97", 83369f6d261SAriff Abdullah device_get_nameunit(dev)); 834489c22ebSJohn Baldwin codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 8357699548fSAriff Abdullah codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO); 8360f55ac6cSCameron Grant codec->dev = dev; 8370f55ac6cSCameron Grant codec->devinfo = devinfo; 83879bb7d52SCameron Grant codec->flags = 0; 83990da2b28SAriff Abdullah 8407699548fSAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 84190da2b28SAriff Abdullah "eapdinv", &i) == 0 && i != 0) 8427699548fSAriff Abdullah codec->flags |= AC97_F_EAPD_INV; 84390da2b28SAriff Abdullah 84490da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 84590da2b28SAriff Abdullah "softpcmvol", &i) == 0 && i != 0) 84690da2b28SAriff Abdullah pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL); 84790da2b28SAriff Abdullah 848987e5972SCameron Grant return codec; 849987e5972SCameron Grant } 850987e5972SCameron Grant 85133dbf14aSCameron Grant void 85233dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 85333dbf14aSCameron Grant { 85466ef8af5SCameron Grant snd_mtxlock(codec->lock); 8550f55ac6cSCameron Grant if (codec->methods != NULL) 8560f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 85766ef8af5SCameron Grant snd_mtxfree(codec->lock); 8580f55ac6cSCameron Grant free(codec, M_AC97); 85933dbf14aSCameron Grant } 86033dbf14aSCameron Grant 86179bb7d52SCameron Grant void 86279bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 86379bb7d52SCameron Grant { 86479bb7d52SCameron Grant codec->flags = val; 86579bb7d52SCameron Grant } 86679bb7d52SCameron Grant 86779bb7d52SCameron Grant u_int32_t 86879bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 86979bb7d52SCameron Grant { 87079bb7d52SCameron Grant return codec->flags; 87179bb7d52SCameron Grant } 87279bb7d52SCameron Grant 8730f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 8740f55ac6cSCameron Grant 875a580b31aSAriff Abdullah static int 876a580b31aSAriff Abdullah sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS) 877a580b31aSAriff Abdullah { 878a580b31aSAriff Abdullah struct ac97_info *codec; 879a580b31aSAriff Abdullah int ea, inv, err = 0; 880a580b31aSAriff Abdullah u_int16_t val; 881a580b31aSAriff Abdullah 882a580b31aSAriff Abdullah codec = oidp->oid_arg1; 883a580b31aSAriff Abdullah if (codec == NULL || codec->id == 0 || codec->lock == NULL) 884a580b31aSAriff Abdullah return EINVAL; 885a580b31aSAriff Abdullah snd_mtxlock(codec->lock); 886a580b31aSAriff Abdullah val = ac97_rdcd(codec, AC97_REG_POWER); 887a580b31aSAriff Abdullah inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1; 888a580b31aSAriff Abdullah ea = (val >> 15) ^ inv; 889a580b31aSAriff Abdullah snd_mtxunlock(codec->lock); 890041b706bSDavid Malone err = sysctl_handle_int(oidp, &ea, 0, req); 891a580b31aSAriff Abdullah if (err == 0 && req->newptr != NULL) { 892a580b31aSAriff Abdullah if (ea != 0 && ea != 1) 893a580b31aSAriff Abdullah return EINVAL; 894a580b31aSAriff Abdullah if (ea != ((val >> 15) ^ inv)) { 895a580b31aSAriff Abdullah snd_mtxlock(codec->lock); 896a580b31aSAriff Abdullah ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000); 897a580b31aSAriff Abdullah snd_mtxunlock(codec->lock); 898a580b31aSAriff Abdullah } 899a580b31aSAriff Abdullah } 900a580b31aSAriff Abdullah return err; 901a580b31aSAriff Abdullah } 902a580b31aSAriff Abdullah 903a580b31aSAriff Abdullah static void 904a580b31aSAriff Abdullah ac97_init_sysctl(struct ac97_info *codec) 905a580b31aSAriff Abdullah { 906a580b31aSAriff Abdullah u_int16_t orig, val; 907a580b31aSAriff Abdullah 908a580b31aSAriff Abdullah if (codec == NULL || codec->dev == NULL) 909a580b31aSAriff Abdullah return; 910a580b31aSAriff Abdullah snd_mtxlock(codec->lock); 911a580b31aSAriff Abdullah orig = ac97_rdcd(codec, AC97_REG_POWER); 912a580b31aSAriff Abdullah ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000); 913a580b31aSAriff Abdullah val = ac97_rdcd(codec, AC97_REG_POWER); 914a580b31aSAriff Abdullah ac97_wrcd(codec, AC97_REG_POWER, orig); 915a580b31aSAriff Abdullah snd_mtxunlock(codec->lock); 916a580b31aSAriff Abdullah if ((val & 0x8000) == (orig & 0x8000)) 917a580b31aSAriff Abdullah return; 918a580b31aSAriff Abdullah SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev), 919a580b31aSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)), 920a580b31aSAriff Abdullah OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW, 921a580b31aSAriff Abdullah codec, sizeof(codec), sysctl_hw_snd_ac97_eapd, 922a580b31aSAriff Abdullah "I", "AC97 External Amplifier"); 923a580b31aSAriff Abdullah } 924a580b31aSAriff Abdullah 925987e5972SCameron Grant static int 92666ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 927987e5972SCameron Grant { 928987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 929341f16ccSCameron Grant u_int32_t i, mask; 930341f16ccSCameron Grant 93139004e69SCameron Grant if (codec == NULL) 93239004e69SCameron Grant return -1; 933341f16ccSCameron Grant 934e620d959SCameron Grant if (ac97_initmixer(codec)) 935e620d959SCameron Grant return -1; 936341f16ccSCameron Grant 9377699548fSAriff Abdullah switch (codec->id) { 9387699548fSAriff Abdullah case 0x41445374: /* AD1981B */ 93955431491SAriff Abdullah switch (codec->subvendor) { 94055431491SAriff Abdullah case 0x02d91014: 941fd7390d6SAriff Abdullah /* 942fd7390d6SAriff Abdullah * IBM Thinkcentre: 94355431491SAriff Abdullah * 94455431491SAriff Abdullah * Tie "ogain" and "phout" to "vol" since its 945fd7390d6SAriff Abdullah * master volume is basically useless and can't 946fd7390d6SAriff Abdullah * control anything. 947fd7390d6SAriff Abdullah */ 9487699548fSAriff Abdullah mask = 0; 9497699548fSAriff Abdullah if (codec->mix[SOUND_MIXER_OGAIN].enable) 9507699548fSAriff Abdullah mask |= SOUND_MASK_OGAIN; 9517699548fSAriff Abdullah if (codec->mix[SOUND_MIXER_PHONEOUT].enable) 9527699548fSAriff Abdullah mask |= SOUND_MASK_PHONEOUT; 9537699548fSAriff Abdullah if (codec->mix[SOUND_MIXER_VOLUME].enable) 954a580b31aSAriff Abdullah mix_setparentchild(m, SOUND_MIXER_VOLUME, 955a580b31aSAriff Abdullah mask); 9567699548fSAriff Abdullah else { 957a580b31aSAriff Abdullah mix_setparentchild(m, SOUND_MIXER_VOLUME, 958a580b31aSAriff Abdullah mask); 959a580b31aSAriff Abdullah mix_setrealdev(m, SOUND_MIXER_VOLUME, 960a580b31aSAriff Abdullah SOUND_MIXER_NONE); 9617699548fSAriff Abdullah } 96255431491SAriff Abdullah break; 96355431491SAriff Abdullah case 0x099c103c: 96455431491SAriff Abdullah /* 96555431491SAriff Abdullah * HP nx6110: 96655431491SAriff Abdullah * 96755431491SAriff Abdullah * By default, "vol" is controlling internal speakers 96855431491SAriff Abdullah * (not a master volume!) and "ogain" is controlling 96955431491SAriff Abdullah * headphone. Enable dummy "phout" so it can be 97055431491SAriff Abdullah * remapped to internal speakers and virtualize 97155431491SAriff Abdullah * "vol" to control both. 97255431491SAriff Abdullah */ 97355431491SAriff Abdullah codec->mix[SOUND_MIXER_OGAIN].enable = 1; 97455431491SAriff Abdullah codec->mix[SOUND_MIXER_PHONEOUT].enable = 1; 97555431491SAriff Abdullah mix_setrealdev(m, SOUND_MIXER_PHONEOUT, 97655431491SAriff Abdullah SOUND_MIXER_VOLUME); 97755431491SAriff Abdullah mix_setrealdev(m, SOUND_MIXER_VOLUME, 97855431491SAriff Abdullah SOUND_MIXER_NONE); 97955431491SAriff Abdullah mix_setparentchild(m, SOUND_MIXER_VOLUME, 98055431491SAriff Abdullah SOUND_MASK_OGAIN | SOUND_MASK_PHONEOUT); 98155431491SAriff Abdullah break; 98255431491SAriff Abdullah default: 98355431491SAriff Abdullah break; 984a580b31aSAriff Abdullah } 9857699548fSAriff Abdullah break; 9867699548fSAriff Abdullah case 0x434d4941: /* CMI9738 */ 9877699548fSAriff Abdullah case 0x434d4961: /* CMI9739 */ 9887699548fSAriff Abdullah case 0x434d4978: /* CMI9761 */ 9897699548fSAriff Abdullah case 0x434d4982: /* CMI9761 */ 9907699548fSAriff Abdullah case 0x434d4983: /* CMI9761 */ 9917699548fSAriff Abdullah bzero(&codec->mix[SOUND_MIXER_PCM], 9927699548fSAriff Abdullah sizeof(codec->mix[SOUND_MIXER_PCM])); 993e510f521SAriff Abdullah pcm_setflags(codec->dev, pcm_getflags(codec->dev) | 994e510f521SAriff Abdullah SD_F_SOFTPCMVOL); 9957699548fSAriff Abdullah /* XXX How about master volume ? */ 9967699548fSAriff Abdullah break; 9977699548fSAriff Abdullah default: 9987699548fSAriff Abdullah break; 9997699548fSAriff Abdullah } 10007699548fSAriff Abdullah 100190da2b28SAriff Abdullah if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL) 100290da2b28SAriff Abdullah ac97_wrcd(codec, AC97_MIX_PCM, 0); 10037699548fSAriff Abdullah #if 0 10047699548fSAriff Abdullah /* XXX For the sake of debugging purposes */ 10057699548fSAriff Abdullah mix_setparentchild(m, SOUND_MIXER_VOLUME, 10067699548fSAriff Abdullah SOUND_MASK_PCM | SOUND_MASK_CD); 10077699548fSAriff Abdullah mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); 10087699548fSAriff Abdullah ac97_wrcd(codec, AC97_MIX_MASTER, 0); 10097699548fSAriff Abdullah #endif 10107699548fSAriff Abdullah 1011341f16ccSCameron Grant mask = 0; 101269f6d261SAriff Abdullah for (i = 0; i < AC97_MIXER_SIZE; i++) 1013341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 1014341f16ccSCameron Grant mix_setdevs(m, mask); 1015341f16ccSCameron Grant 1016341f16ccSCameron Grant mask = 0; 101769f6d261SAriff Abdullah for (i = 0; i < AC97_MIXER_SIZE; i++) 1018341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 1019341f16ccSCameron Grant mix_setrecdevs(m, mask); 1020a580b31aSAriff Abdullah 1021a580b31aSAriff Abdullah ac97_init_sysctl(codec); 1022a580b31aSAriff Abdullah 1023987e5972SCameron Grant return 0; 1024987e5972SCameron Grant } 1025987e5972SCameron Grant 1026987e5972SCameron Grant static int 102766ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 102833dbf14aSCameron Grant { 102933dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 1030341f16ccSCameron Grant 103133dbf14aSCameron Grant if (codec == NULL) 103233dbf14aSCameron Grant return -1; 103333dbf14aSCameron Grant /* 103433dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 103533dbf14aSCameron Grant return -1; 103633dbf14aSCameron Grant */ 103733dbf14aSCameron Grant ac97_destroy(codec); 103833dbf14aSCameron Grant return 0; 103933dbf14aSCameron Grant } 104033dbf14aSCameron Grant 104133dbf14aSCameron Grant static int 104266ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 10439ec437a3SCameron Grant { 10449ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 10459ec437a3SCameron Grant 10469ec437a3SCameron Grant if (codec == NULL) 10479ec437a3SCameron Grant return -1; 10489ec437a3SCameron Grant return ac97_reinitmixer(codec); 10499ec437a3SCameron Grant } 10509ec437a3SCameron Grant 10519ec437a3SCameron Grant static int 105266ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 1053987e5972SCameron Grant { 1054987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 1055341f16ccSCameron Grant 105669f6d261SAriff Abdullah if (codec == NULL || dev >= AC97_MIXER_SIZE) 105739004e69SCameron Grant return -1; 1058987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 1059987e5972SCameron Grant } 1060987e5972SCameron Grant 106190da2b28SAriff Abdullah static u_int32_t 106266ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 1063987e5972SCameron Grant { 1064987e5972SCameron Grant int i; 1065987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 1066341f16ccSCameron Grant 106739004e69SCameron Grant if (codec == NULL) 106839004e69SCameron Grant return -1; 106969f6d261SAriff Abdullah for (i = 0; i < AC97_MIXER_SIZE; i++) 107039004e69SCameron Grant if ((src & (1 << i)) != 0) 107139004e69SCameron Grant break; 107290da2b28SAriff Abdullah return (ac97_setrecsrc(codec, i) == 0)? 1U << i : 0xffffffffU; 1073987e5972SCameron Grant } 1074987e5972SCameron Grant 10750f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 10760f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 10770f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 10780f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 10790f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 10800f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 108190da2b28SAriff Abdullah KOBJMETHOD_END 1082987e5972SCameron Grant }; 10830f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 10840f55ac6cSCameron Grant 10850f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 10860f55ac6cSCameron Grant 10870f55ac6cSCameron Grant kobj_class_t 10880f55ac6cSCameron Grant ac97_getmixerclass(void) 10890f55ac6cSCameron Grant { 10900f55ac6cSCameron Grant return &ac97mixer_class; 10910f55ac6cSCameron Grant } 1092