1987e5972SCameron Grant /* 2987e5972SCameron Grant * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3987e5972SCameron Grant * All rights reserved. 4987e5972SCameron Grant * 5987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 6987e5972SCameron Grant * modification, are permitted provided that the following conditions 7987e5972SCameron Grant * are met: 8987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 9987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 10987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 11987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 12987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 13987e5972SCameron Grant * 14987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24987e5972SCameron Grant * SUCH DAMAGE. 25987e5972SCameron Grant */ 26987e5972SCameron Grant 27ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 28ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h> 29f9eb1409SOrion Hodson #include <dev/sound/pcm/ac97_patch.h> 30987e5972SCameron Grant 310f55ac6cSCameron Grant #include "mixer_if.h" 32987e5972SCameron Grant 3367b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 3467b1dce3SCameron Grant 350f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 36987e5972SCameron Grant 3779bb7d52SCameron Grant struct ac97mixtable_entry { 3879bb7d52SCameron Grant int reg:8; 3979bb7d52SCameron Grant unsigned bits:4; 4079bb7d52SCameron Grant unsigned ofs:4; 4179bb7d52SCameron Grant unsigned stereo:1; 4279bb7d52SCameron Grant unsigned mute:1; 4379bb7d52SCameron Grant unsigned recidx:4; 4479bb7d52SCameron Grant unsigned mask:1; 4579bb7d52SCameron Grant unsigned enable:1; 4679bb7d52SCameron Grant }; 4779bb7d52SCameron Grant 4866ef8af5SCameron Grant #define AC97_NAMELEN 16 4966ef8af5SCameron Grant struct ac97_info { 5066ef8af5SCameron Grant kobj_t methods; 5166ef8af5SCameron Grant device_t dev; 5266ef8af5SCameron Grant void *devinfo; 5319921b23SOrion Hodson u_int32_t id; 5466ef8af5SCameron Grant unsigned count, caps, se, extcaps, extid, extstat, noext:1; 5579bb7d52SCameron Grant u_int32_t flags; 5666ef8af5SCameron Grant struct ac97mixtable_entry mix[32]; 5766ef8af5SCameron Grant char name[AC97_NAMELEN]; 5800acb133SCameron Grant struct mtx *lock; 5966ef8af5SCameron Grant }; 6066ef8af5SCameron Grant 6119921b23SOrion Hodson struct ac97_vendorid { 6219921b23SOrion Hodson u_int32_t id; 6319921b23SOrion Hodson const char *name; 6419921b23SOrion Hodson }; 6519921b23SOrion Hodson 66987e5972SCameron Grant struct ac97_codecid { 6719921b23SOrion Hodson u_int32_t id; 6819921b23SOrion Hodson u_int8_t stepmask; 6919921b23SOrion Hodson u_int8_t noext:1; 70987e5972SCameron Grant char *name; 71f9eb1409SOrion Hodson ac97_patch patch; 72987e5972SCameron Grant }; 73987e5972SCameron Grant 74987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = { 75341f16ccSCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 76108082c4SOrion Hodson [SOUND_MIXER_MONITOR] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 }, 77341f16ccSCameron Grant [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 78341f16ccSCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 79341f16ccSCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 80341f16ccSCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 81341f16ccSCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 82341f16ccSCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 83341f16ccSCameron Grant [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 84341f16ccSCameron Grant [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0, 1 }, 85341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 86341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 87341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 88341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 89987e5972SCameron Grant }; 90987e5972SCameron Grant 9119921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = { 9219921b23SOrion Hodson { 0x41445300, "Analog Devices" }, 9319921b23SOrion Hodson { 0x414b4d00, "Asahi Kasei" }, 9419921b23SOrion Hodson { 0x414c4300, "Realtek" }, 9519921b23SOrion Hodson { 0x414c4700, "Avance Logic" }, 9619921b23SOrion Hodson { 0x43525900, "Cirrus Logic" }, 9719921b23SOrion Hodson { 0x434d4900, "C-Media Electronics" }, 9819921b23SOrion Hodson { 0x43585400, "Conexant" }, 9919921b23SOrion Hodson { 0x45838300, "ESS Technology" }, 10019921b23SOrion Hodson { 0x49434500, "ICEnsemble" }, 10119921b23SOrion Hodson { 0x4e534300, "National Semiconductor" }, 10219921b23SOrion Hodson { 0x50534300, "Philips Semiconductor" }, 10319921b23SOrion Hodson { 0x83847600, "SigmaTel" }, 10419921b23SOrion Hodson { 0x53494c00, "Silicon Laboratory" }, 10519921b23SOrion Hodson { 0x54524100, "TriTech" }, 10619921b23SOrion Hodson { 0x56494100, "VIA Technologies" }, 10719921b23SOrion Hodson { 0x574d4c00, "Wolfson" }, 10819921b23SOrion Hodson { 0x594d4800, "Yamaha" }, 10919921b23SOrion Hodson { 0x00000000, NULL } 11019921b23SOrion Hodson }; 11119921b23SOrion Hodson 112987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 11319921b23SOrion Hodson { 0x41445303, 0x00, 0, "AD1819", 0 }, 11419921b23SOrion Hodson { 0x41445340, 0x00, 0, "AD1881", 0 }, 11519921b23SOrion Hodson { 0x41445348, 0x00, 0, "AD1881A", 0 }, 11619921b23SOrion Hodson { 0x41445360, 0x00, 0, "AD1885", 0 }, 11719921b23SOrion Hodson { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 11819921b23SOrion Hodson { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 11919921b23SOrion Hodson { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 12019921b23SOrion Hodson { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 12119921b23SOrion Hodson { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 12219921b23SOrion Hodson { 0x414c4320, 0x0f, 0, "ALC101", 0 }, 12319921b23SOrion Hodson { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 12419921b23SOrion Hodson { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 12519921b23SOrion Hodson { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 12619921b23SOrion Hodson { 0x43525900, 0x07, 0, "CS4297", 0 }, 12719921b23SOrion Hodson { 0x43525910, 0x07, 0, "CS4297A", 0 }, 12819921b23SOrion Hodson { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 12919921b23SOrion Hodson { 0x43525930, 0x07, 0, "CS4299", 0 }, 13019921b23SOrion Hodson { 0x43525940, 0x07, 0, "CS4201", 0 }, 13119921b23SOrion Hodson { 0x43525950, 0x07, 0, "CS4205", 0 }, 13219921b23SOrion Hodson { 0x43525960, 0x07, 0, "CS4291A", 0 }, 13319921b23SOrion Hodson { 0x434d4961, 0x00, 0, "CMI9739", 0 }, 13419921b23SOrion Hodson { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 13519921b23SOrion Hodson { 0x43585429, 0x00, 0, "CX20468", 0 }, 13619921b23SOrion Hodson { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 13719921b23SOrion Hodson { 0x49434501, 0x00, 0, "ICE1230", 0 }, 13819921b23SOrion Hodson { 0x49434511, 0x00, 0, "ICE1232", 0 }, 13919921b23SOrion Hodson { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 14019921b23SOrion Hodson { 0x49434551, 0x00, 0, "VT1616", 0 }, /* Via badged ICE */ 14119921b23SOrion Hodson { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 14219921b23SOrion Hodson { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 14319921b23SOrion Hodson { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 14419921b23SOrion Hodson { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 14519921b23SOrion Hodson { 0x4e534331, 0x00, 0, "LM4549", 0 }, /* (?) */ 14619921b23SOrion Hodson { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 14719921b23SOrion Hodson { 0x4e534350, 0x00, 0, "LM4550", 0 }, 14819921b23SOrion Hodson { 0x50534301, 0x00, 0, "UCB1510", 0 }, 14919921b23SOrion Hodson { 0x50534304, 0x00, 0, "UCB1400", 0 }, 15019921b23SOrion Hodson { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 15119921b23SOrion Hodson { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 15219921b23SOrion Hodson { 0x83847605, 0x00, 0, "STAC9704", 0 }, 15319921b23SOrion Hodson { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 15419921b23SOrion Hodson { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 15519921b23SOrion Hodson { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 15619921b23SOrion Hodson { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 15719921b23SOrion Hodson { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 15819921b23SOrion Hodson { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 15919921b23SOrion Hodson { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 16019921b23SOrion Hodson { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 16119921b23SOrion Hodson { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 16219921b23SOrion Hodson { 0x53494c22, 0x00, 0, "Si3036", 0 }, 16319921b23SOrion Hodson { 0x53494c23, 0x00, 0, "Si3038", 0 }, 16419921b23SOrion Hodson { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 16519921b23SOrion Hodson { 0x54524106, 0x00, 0, "TR28026", 0 }, 16619921b23SOrion Hodson { 0x54524108, 0x00, 0, "TR28028", 0 }, 16719921b23SOrion Hodson { 0x54524123, 0x00, 0, "TR28602", 0 }, 16819921b23SOrion Hodson { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 16919921b23SOrion Hodson { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 17019921b23SOrion Hodson { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 17119921b23SOrion Hodson { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 17219921b23SOrion Hodson { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 17319921b23SOrion Hodson { 0x594d4800, 0x00, 0, "YMF743", 0 }, 17419921b23SOrion Hodson { 0x594d4802, 0x00, 0, "YMF752", 0 }, 17519921b23SOrion Hodson { 0x594d4803, 0x00, 0, "YMF753", 0 }, 17619921b23SOrion Hodson { 0, 0, 0, NULL, 0 } 177987e5972SCameron Grant }; 178987e5972SCameron Grant 179987e5972SCameron Grant static char *ac97enhancement[] = { 18004553e63SCameron Grant "no 3D Stereo Enhancement", 181987e5972SCameron Grant "Analog Devices Phat Stereo", 182987e5972SCameron Grant "Creative Stereo Enhancement", 183987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 184987e5972SCameron Grant "Yamaha Ymersion", 185987e5972SCameron Grant "BBE 3D Stereo Enhancement", 186987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 187987e5972SCameron Grant "Qsound QXpander", 188987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 189987e5972SCameron Grant "SRS 3D Stereo Enhancement", 190987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 191987e5972SCameron Grant "AKM 3D Audio", 192987e5972SCameron Grant "Aureal Stereo Enhancement", 193987e5972SCameron Grant "Aztech 3D Enhancement", 194987e5972SCameron Grant "Binaura 3D Audio Enhancement", 195987e5972SCameron Grant "ESS Technology Stereo Enhancement", 196987e5972SCameron Grant "Harman International VMAx", 197987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 198987e5972SCameron Grant "Philips Incredible Sound", 199987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 200987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 201987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 202987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 203987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 204987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 205987e5972SCameron Grant "Delta Integration 3D Enhancement", 206987e5972SCameron Grant "SigmaTel 3D Enhancement", 207987e5972SCameron Grant "Reserved 27", 208987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 209987e5972SCameron Grant "Reserved 29", 210987e5972SCameron Grant "Reserved 30", 211987e5972SCameron Grant "Reserved 31" 212987e5972SCameron Grant }; 213987e5972SCameron Grant 214987e5972SCameron Grant static char *ac97feature[] = { 215987e5972SCameron Grant "mic channel", 216987e5972SCameron Grant "reserved", 217987e5972SCameron Grant "tone", 218987e5972SCameron Grant "simulated stereo", 219987e5972SCameron Grant "headphone", 220987e5972SCameron Grant "bass boost", 221987e5972SCameron Grant "18 bit DAC", 222987e5972SCameron Grant "20 bit DAC", 223987e5972SCameron Grant "18 bit ADC", 224987e5972SCameron Grant "20 bit ADC" 225987e5972SCameron Grant }; 226987e5972SCameron Grant 22739004e69SCameron Grant static char *ac97extfeature[] = { 22839004e69SCameron Grant "variable rate PCM", 22939004e69SCameron Grant "double rate PCM", 23039004e69SCameron Grant "reserved 1", 23139004e69SCameron Grant "variable rate mic", 23239004e69SCameron Grant "reserved 2", 23339004e69SCameron Grant "reserved 3", 23439004e69SCameron Grant "center DAC", 23539004e69SCameron Grant "surround DAC", 23639004e69SCameron Grant "LFE DAC", 23739004e69SCameron Grant "AMAP", 23839004e69SCameron Grant "reserved 4", 23939004e69SCameron Grant "reserved 5", 24039004e69SCameron Grant "reserved 6", 24139004e69SCameron Grant "reserved 7", 24239004e69SCameron Grant }; 24339004e69SCameron Grant 244f9eb1409SOrion Hodson u_int16_t 245f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg) 24639004e69SCameron Grant { 2470f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 24839004e69SCameron Grant } 24939004e69SCameron Grant 250f9eb1409SOrion Hodson void 251f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 25239004e69SCameron Grant { 2530f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 25439004e69SCameron Grant } 25539004e69SCameron Grant 256c6d4b83aSOrion Hodson static void 257c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec) 258c6d4b83aSOrion Hodson { 259c6d4b83aSOrion Hodson u_int32_t i, ps; 260f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RESET, 0); 261c6d4b83aSOrion Hodson for (i = 0; i < 500; i++) { 262f9eb1409SOrion Hodson ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 263c6d4b83aSOrion Hodson if (ps == AC97_POWER_STATUS) 264c6d4b83aSOrion Hodson return; 265c6d4b83aSOrion Hodson DELAY(1000); 266c6d4b83aSOrion Hodson } 267a825c6e5SOrion Hodson device_printf(codec->dev, "AC97 reset timed out.\n"); 268c6d4b83aSOrion Hodson } 269c6d4b83aSOrion Hodson 27039004e69SCameron Grant int 27139004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 27239004e69SCameron Grant { 27339004e69SCameron Grant u_int16_t v; 27439004e69SCameron Grant 27539004e69SCameron Grant switch(which) { 27639004e69SCameron Grant case AC97_REGEXT_FDACRATE: 27739004e69SCameron Grant case AC97_REGEXT_SDACRATE: 27839004e69SCameron Grant case AC97_REGEXT_LDACRATE: 27939004e69SCameron Grant case AC97_REGEXT_LADCRATE: 28039004e69SCameron Grant case AC97_REGEXT_MADCRATE: 28139004e69SCameron Grant break; 28239004e69SCameron Grant 28339004e69SCameron Grant default: 28439004e69SCameron Grant return -1; 28539004e69SCameron Grant } 28639004e69SCameron Grant 28766ef8af5SCameron Grant snd_mtxlock(codec->lock); 28839004e69SCameron Grant if (rate != 0) { 28939004e69SCameron Grant v = rate; 29039004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 29139004e69SCameron Grant v >>= 1; 292f9eb1409SOrion Hodson ac97_wrcd(codec, which, v); 29339004e69SCameron Grant } 294f9eb1409SOrion Hodson v = ac97_rdcd(codec, which); 29539004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 29639004e69SCameron Grant v <<= 1; 29766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 29839004e69SCameron Grant return v; 29939004e69SCameron Grant } 30039004e69SCameron Grant 30139004e69SCameron Grant int 30239004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 30339004e69SCameron Grant { 30439004e69SCameron Grant mode &= AC97_EXTCAPS; 305647fbfebSOrion Hodson if ((mode & ~codec->extcaps) != 0) { 306647fbfebSOrion Hodson device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 307647fbfebSOrion Hodson mode); 30839004e69SCameron Grant return -1; 309647fbfebSOrion Hodson } 31066ef8af5SCameron Grant snd_mtxlock(codec->lock); 311f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 312f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 31366ef8af5SCameron Grant snd_mtxunlock(codec->lock); 31439004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 31539004e69SCameron Grant } 31639004e69SCameron Grant 3179ec437a3SCameron Grant u_int16_t 3189ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 3199ec437a3SCameron Grant { 3209ec437a3SCameron Grant return codec->extstat; 3219ec437a3SCameron Grant } 3229ec437a3SCameron Grant 3239ec437a3SCameron Grant u_int16_t 3249ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 3259ec437a3SCameron Grant { 3269ec437a3SCameron Grant return codec->extcaps; 3279ec437a3SCameron Grant } 3289ec437a3SCameron Grant 3295d91ad67SCameron Grant u_int16_t 3305d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec) 3315d91ad67SCameron Grant { 3325d91ad67SCameron Grant return codec->caps; 3335d91ad67SCameron Grant } 3345d91ad67SCameron Grant 335987e5972SCameron Grant static int 336987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 337987e5972SCameron Grant { 338987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 339341f16ccSCameron Grant 340987e5972SCameron Grant if (e->recidx > 0) { 341987e5972SCameron Grant int val = e->recidx - 1; 342987e5972SCameron Grant val |= val << 8; 34366ef8af5SCameron Grant snd_mtxlock(codec->lock); 344f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RECSEL, val); 34566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 346987e5972SCameron Grant return 0; 34739004e69SCameron Grant } else 34839004e69SCameron Grant return -1; 349987e5972SCameron Grant } 350987e5972SCameron Grant 351987e5972SCameron Grant static int 352987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 353987e5972SCameron Grant { 354987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 355341f16ccSCameron Grant 356341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 357e479a8afSCameron Grant int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 358987e5972SCameron Grant 35939004e69SCameron Grant if (!e->stereo) 36039004e69SCameron Grant right = left; 361987e5972SCameron Grant if (e->reg > 0) { 362987e5972SCameron Grant left = 100 - left; 363987e5972SCameron Grant right = 100 - right; 364987e5972SCameron Grant } 365987e5972SCameron Grant 366987e5972SCameron Grant max = (1 << e->bits) - 1; 367987e5972SCameron Grant left = (left * max) / 100; 368987e5972SCameron Grant right = (right * max) / 100; 369987e5972SCameron Grant 370987e5972SCameron Grant val = (left << 8) | right; 371987e5972SCameron Grant 372987e5972SCameron Grant left = (left * 100) / max; 373987e5972SCameron Grant right = (right * 100) / max; 374987e5972SCameron Grant 375987e5972SCameron Grant if (e->reg > 0) { 376987e5972SCameron Grant left = 100 - left; 377987e5972SCameron Grant right = 100 - right; 378987e5972SCameron Grant } 379987e5972SCameron Grant 380987e5972SCameron Grant if (!e->stereo) { 381987e5972SCameron Grant val &= max; 382987e5972SCameron Grant val <<= e->ofs; 383987e5972SCameron Grant if (e->mask) { 384f9eb1409SOrion Hodson int cur = ac97_rdcd(codec, e->reg); 385987e5972SCameron Grant val |= cur & ~(max << e->ofs); 386987e5972SCameron Grant } 387987e5972SCameron Grant } 38839004e69SCameron Grant if (left == 0 && right == 0 && e->mute == 1) 38939004e69SCameron Grant val = AC97_MUTE; 39066ef8af5SCameron Grant snd_mtxlock(codec->lock); 391f9eb1409SOrion Hodson ac97_wrcd(codec, reg, val); 39266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 393987e5972SCameron Grant return left | (right << 8); 394341f16ccSCameron Grant } else { 395341f16ccSCameron Grant /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 39639004e69SCameron Grant return -1; 397987e5972SCameron Grant } 398341f16ccSCameron Grant } 399987e5972SCameron Grant 400987e5972SCameron Grant #if 0 401987e5972SCameron Grant static int 402987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel) 403987e5972SCameron Grant { 404987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 405987e5972SCameron Grant if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 406987e5972SCameron Grant int max, val, volume; 407987e5972SCameron Grant 408987e5972SCameron Grant max = (1 << e->bits) - 1; 409f9eb1409SOrion Hodson val = ac97_rdcd(code, e->reg); 41039004e69SCameron Grant if (val == AC97_MUTE && e->mute == 1) 41139004e69SCameron Grant volume = 0; 412987e5972SCameron Grant else { 413987e5972SCameron Grant if (e->stereo == 0) val >>= e->ofs; 414987e5972SCameron Grant val &= max; 415987e5972SCameron Grant volume = (val * 100) / max; 416987e5972SCameron Grant if (e->reg > 0) volume = 100 - volume; 417987e5972SCameron Grant } 418987e5972SCameron Grant return volume; 41939004e69SCameron Grant } else 42039004e69SCameron Grant return -1; 421987e5972SCameron Grant } 422987e5972SCameron Grant #endif 423987e5972SCameron Grant 424108082c4SOrion Hodson static void 425108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec) 426108082c4SOrion Hodson { 427108082c4SOrion Hodson /* Determine what AUXOUT really means, it can be: 428108082c4SOrion Hodson * 429108082c4SOrion Hodson * 1. Headphone out. 430108082c4SOrion Hodson * 2. 4-Channel Out 431108082c4SOrion Hodson * 3. True line level out (effectively master volume). 432108082c4SOrion Hodson * 433108082c4SOrion Hodson * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 434108082c4SOrion Hodson */ 435108082c4SOrion Hodson if (codec->caps & AC97_CAP_HEADPHONE) { 436108082c4SOrion Hodson /* XXX We should probably check the AUX_OUT initial value. 437108082c4SOrion Hodson * Leave AC97_MIX_AUXOUT - SOUND_MIXER_MONITOR relationship */ 438108082c4SOrion Hodson return; 439108082c4SOrion Hodson } else if (codec->extcaps & AC97_EXTCAP_SDAC && 440f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 441108082c4SOrion Hodson /* 4-Channel Out, add an additional gain setting. */ 442108082c4SOrion Hodson codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR]; 443108082c4SOrion Hodson } else { 444108082c4SOrion Hodson /* Master volume is/maybe fixed in h/w, not sufficiently 445108082c4SOrion Hodson * clear in spec to blat SOUND_MIXER_MASTER. */ 446108082c4SOrion Hodson codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR]; 447108082c4SOrion Hodson } 448108082c4SOrion Hodson /* Blat monitor, inappropriate label if we get here */ 449108082c4SOrion Hodson bzero(&codec->mix[SOUND_MIXER_MONITOR], 450108082c4SOrion Hodson sizeof(codec->mix[SOUND_MIXER_MONITOR])); 451108082c4SOrion Hodson } 452108082c4SOrion Hodson 45319921b23SOrion Hodson static const char* 45419921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 45519921b23SOrion Hodson { 45619921b23SOrion Hodson if (cname == NULL) { 45719921b23SOrion Hodson sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 45819921b23SOrion Hodson return buf; 45919921b23SOrion Hodson } 46019921b23SOrion Hodson 46119921b23SOrion Hodson if (vname == NULL) vname = "Unknown"; 46219921b23SOrion Hodson 46319921b23SOrion Hodson if (bootverbose) { 46419921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 46519921b23SOrion Hodson } else { 46619921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec", vname, cname); 46719921b23SOrion Hodson } 46819921b23SOrion Hodson return buf; 46919921b23SOrion Hodson } 47019921b23SOrion Hodson 471987e5972SCameron Grant static unsigned 47239004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 473987e5972SCameron Grant { 474f9eb1409SOrion Hodson ac97_patch codec_patch; 47519921b23SOrion Hodson const char *cname, *vname; 47619921b23SOrion Hodson char desc[80]; 47719921b23SOrion Hodson u_int8_t model, step; 478341f16ccSCameron Grant unsigned i, j, k, old; 479987e5972SCameron Grant u_int32_t id; 480987e5972SCameron Grant 48166ef8af5SCameron Grant snd_mtxlock(codec->lock); 4820f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 483cd2c103aSCameron Grant if (codec->count == 0) { 48404553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 48566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 48604553e63SCameron Grant return ENODEV; 48704553e63SCameron Grant } 4889ec437a3SCameron Grant 489f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 490c6d4b83aSOrion Hodson ac97_reset(codec); 491f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 492987e5972SCameron Grant 493f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REG_RESET); 494987e5972SCameron Grant codec->caps = i & 0x03ff; 495987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 496987e5972SCameron Grant 497f9eb1409SOrion Hodson id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 498e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 499e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 50066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 501e620d959SCameron Grant return ENODEV; 502e620d959SCameron Grant } 5036b4b88f7SCameron Grant 50419921b23SOrion Hodson codec->id = id; 505cd2c103aSCameron Grant codec->noext = 0; 506f9eb1409SOrion Hodson codec_patch = NULL; 50719921b23SOrion Hodson 50819921b23SOrion Hodson cname = NULL; 50919921b23SOrion Hodson model = step = 0; 510cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 51119921b23SOrion Hodson u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 51219921b23SOrion Hodson if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 513cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 514f9eb1409SOrion Hodson codec_patch = ac97codecid[i].patch; 51519921b23SOrion Hodson cname = ac97codecid[i].name; 51619921b23SOrion Hodson model = (id & modelmask) & 0xff; 51719921b23SOrion Hodson step = (id & ~modelmask) & 0xff; 51819921b23SOrion Hodson break; 51919921b23SOrion Hodson } 52019921b23SOrion Hodson } 52119921b23SOrion Hodson 52219921b23SOrion Hodson vname = NULL; 52319921b23SOrion Hodson for (i = 0; ac97vendorid[i].id; i++) { 52419921b23SOrion Hodson if (ac97vendorid[i].id == (id & 0xffffff00)) { 52519921b23SOrion Hodson vname = ac97vendorid[i].name; 52619921b23SOrion Hodson break; 527cd2c103aSCameron Grant } 528cd2c103aSCameron Grant } 5296b4b88f7SCameron Grant 530cd2c103aSCameron Grant codec->extcaps = 0; 531cd2c103aSCameron Grant codec->extid = 0; 532cd2c103aSCameron Grant codec->extstat = 0; 5336a6ee5bbSCameron Grant if (!codec->noext) { 534f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REGEXT_ID); 5356a6ee5bbSCameron Grant if (i != 0xffff) { 53639004e69SCameron Grant codec->extcaps = i & 0x3fff; 53739004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 538f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 5396b4b88f7SCameron Grant } 5406a6ee5bbSCameron Grant } 541987e5972SCameron Grant 542341f16ccSCameron Grant for (i = 0; i < 32; i++) { 543108082c4SOrion Hodson codec->mix[i] = ac97mixtable_default[i]; 544108082c4SOrion Hodson } 545108082c4SOrion Hodson ac97_fix_auxout(codec); 546f9eb1409SOrion Hodson if (codec_patch) 547f9eb1409SOrion Hodson codec_patch(codec); 548108082c4SOrion Hodson 549108082c4SOrion Hodson for (i = 0; i < 32; i++) { 55033c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 55133c878f0SCameron Grant if (k && (codec->mix[i].reg > 0)) { 552f9eb1409SOrion Hodson old = ac97_rdcd(codec, codec->mix[i].reg); 553f9eb1409SOrion Hodson ac97_wrcd(codec, codec->mix[i].reg, 0x3f); 554f9eb1409SOrion Hodson j = ac97_rdcd(codec, codec->mix[i].reg); 555f9eb1409SOrion Hodson ac97_wrcd(codec, codec->mix[i].reg, old); 5566a6ee5bbSCameron Grant codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 557341f16ccSCameron Grant for (k = 1; j & (1 << k); k++); 558341f16ccSCameron Grant codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 559341f16ccSCameron Grant } 560341f16ccSCameron Grant /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 561341f16ccSCameron Grant } 562987e5972SCameron Grant 56319921b23SOrion Hodson device_printf(codec->dev, "<%s>\n", 56419921b23SOrion Hodson ac97_hw_desc(codec->id, vname, cname, desc)); 565a825c6e5SOrion Hodson 566987e5972SCameron Grant if (bootverbose) { 56719921b23SOrion Hodson device_printf(codec->dev, "Codec features "); 56839004e69SCameron Grant for (i = j = 0; i < 10; i++) 56939004e69SCameron Grant if (codec->caps & (1 << i)) 57039004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 57139004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 572987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 57339004e69SCameron Grant 57439004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 57519921b23SOrion Hodson device_printf(codec->dev, "%s codec", 57619921b23SOrion Hodson codec->extid? "Secondary" : "Primary"); 57739004e69SCameron Grant if (codec->extcaps) 57839004e69SCameron Grant printf(" extended features "); 57939004e69SCameron Grant for (i = j = 0; i < 14; i++) 58039004e69SCameron Grant if (codec->extcaps & (1 << i)) 58139004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 58239004e69SCameron Grant printf("\n"); 58339004e69SCameron Grant } 584987e5972SCameron Grant } 585987e5972SCameron Grant 586f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 58703a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 58866ef8af5SCameron Grant snd_mtxunlock(codec->lock); 589987e5972SCameron Grant return 0; 590987e5972SCameron Grant } 591987e5972SCameron Grant 5929ec437a3SCameron Grant static unsigned 5939ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 5949ec437a3SCameron Grant { 59566ef8af5SCameron Grant snd_mtxlock(codec->lock); 5960f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 5979ec437a3SCameron Grant if (codec->count == 0) { 5989ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 59966ef8af5SCameron Grant snd_mtxunlock(codec->lock); 6009ec437a3SCameron Grant return ENODEV; 6019ec437a3SCameron Grant } 6029ec437a3SCameron Grant 603f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 604c6d4b83aSOrion Hodson ac97_reset(codec); 605f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 6069ec437a3SCameron Grant 6079ec437a3SCameron Grant if (!codec->noext) { 608f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 609f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 610b60e55dbSGuido van Rooij != codec->extstat) 6119ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 612b60e55dbSGuido van Rooij codec->extstat, 613f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_REGEXT_STAT) & 614b60e55dbSGuido van Rooij AC97_EXTCAPS); 6159ec437a3SCameron Grant } 6169ec437a3SCameron Grant 617f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 6189ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 61966ef8af5SCameron Grant snd_mtxunlock(codec->lock); 6209ec437a3SCameron Grant return 0; 6219ec437a3SCameron Grant } 6229ec437a3SCameron Grant 623987e5972SCameron Grant struct ac97_info * 6240f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 625987e5972SCameron Grant { 626987e5972SCameron Grant struct ac97_info *codec; 627987e5972SCameron Grant 6280f55ac6cSCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 6290f55ac6cSCameron Grant if (codec == NULL) 6300f55ac6cSCameron Grant return NULL; 6310f55ac6cSCameron Grant 63266ef8af5SCameron Grant snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 633489c22ebSJohn Baldwin codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 634a163d034SWarner Losh codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 6350f55ac6cSCameron Grant if (codec->methods == NULL) { 63666ef8af5SCameron Grant snd_mtxlock(codec->lock); 63766ef8af5SCameron Grant snd_mtxfree(codec->lock); 6380f55ac6cSCameron Grant free(codec, M_AC97); 6390f55ac6cSCameron Grant return NULL; 640987e5972SCameron Grant } 6410f55ac6cSCameron Grant 6420f55ac6cSCameron Grant codec->dev = dev; 6430f55ac6cSCameron Grant codec->devinfo = devinfo; 64479bb7d52SCameron Grant codec->flags = 0; 645987e5972SCameron Grant return codec; 646987e5972SCameron Grant } 647987e5972SCameron Grant 64833dbf14aSCameron Grant void 64933dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 65033dbf14aSCameron Grant { 65166ef8af5SCameron Grant snd_mtxlock(codec->lock); 6520f55ac6cSCameron Grant if (codec->methods != NULL) 6530f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 65466ef8af5SCameron Grant snd_mtxfree(codec->lock); 6550f55ac6cSCameron Grant free(codec, M_AC97); 65633dbf14aSCameron Grant } 65733dbf14aSCameron Grant 65879bb7d52SCameron Grant void 65979bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 66079bb7d52SCameron Grant { 66179bb7d52SCameron Grant codec->flags = val; 66279bb7d52SCameron Grant } 66379bb7d52SCameron Grant 66479bb7d52SCameron Grant u_int32_t 66579bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 66679bb7d52SCameron Grant { 66779bb7d52SCameron Grant return codec->flags; 66879bb7d52SCameron Grant } 66979bb7d52SCameron Grant 6700f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 6710f55ac6cSCameron Grant 672987e5972SCameron Grant static int 67366ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 674987e5972SCameron Grant { 675987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 676341f16ccSCameron Grant u_int32_t i, mask; 677341f16ccSCameron Grant 67839004e69SCameron Grant if (codec == NULL) 67939004e69SCameron Grant return -1; 680341f16ccSCameron Grant 681e620d959SCameron Grant if (ac97_initmixer(codec)) 682e620d959SCameron Grant return -1; 683341f16ccSCameron Grant 684341f16ccSCameron Grant mask = 0; 685341f16ccSCameron Grant for (i = 0; i < 32; i++) 686341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 687341f16ccSCameron Grant mix_setdevs(m, mask); 688341f16ccSCameron Grant 689341f16ccSCameron Grant mask = 0; 690341f16ccSCameron Grant for (i = 0; i < 32; i++) 691341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 692341f16ccSCameron Grant mix_setrecdevs(m, mask); 693987e5972SCameron Grant return 0; 694987e5972SCameron Grant } 695987e5972SCameron Grant 696987e5972SCameron Grant static int 69766ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 69833dbf14aSCameron Grant { 69933dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 700341f16ccSCameron Grant 70133dbf14aSCameron Grant if (codec == NULL) 70233dbf14aSCameron Grant return -1; 70333dbf14aSCameron Grant /* 70433dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 70533dbf14aSCameron Grant return -1; 70633dbf14aSCameron Grant */ 70733dbf14aSCameron Grant ac97_destroy(codec); 70833dbf14aSCameron Grant return 0; 70933dbf14aSCameron Grant } 71033dbf14aSCameron Grant 71133dbf14aSCameron Grant static int 71266ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 7139ec437a3SCameron Grant { 7149ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 7159ec437a3SCameron Grant 7169ec437a3SCameron Grant if (codec == NULL) 7179ec437a3SCameron Grant return -1; 7189ec437a3SCameron Grant return ac97_reinitmixer(codec); 7199ec437a3SCameron Grant } 7209ec437a3SCameron Grant 7219ec437a3SCameron Grant static int 72266ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 723987e5972SCameron Grant { 724987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 725341f16ccSCameron Grant 72639004e69SCameron Grant if (codec == NULL) 72739004e69SCameron Grant return -1; 728987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 729987e5972SCameron Grant } 730987e5972SCameron Grant 731987e5972SCameron Grant static int 73266ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 733987e5972SCameron Grant { 734987e5972SCameron Grant int i; 735987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 736341f16ccSCameron Grant 73739004e69SCameron Grant if (codec == NULL) 73839004e69SCameron Grant return -1; 739987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 74039004e69SCameron Grant if ((src & (1 << i)) != 0) 74139004e69SCameron Grant break; 742987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 743987e5972SCameron Grant } 744987e5972SCameron Grant 7450f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 7460f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 7470f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 7480f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 7490f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 7500f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 7510f55ac6cSCameron Grant { 0, 0 } 752987e5972SCameron Grant }; 7530f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 7540f55ac6cSCameron Grant 7550f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 7560f55ac6cSCameron Grant 7570f55ac6cSCameron Grant kobj_class_t 7580f55ac6cSCameron Grant ac97_getmixerclass(void) 7590f55ac6cSCameron Grant { 7600f55ac6cSCameron Grant return &ac97mixer_class; 7610f55ac6cSCameron Grant } 7620f55ac6cSCameron Grant 763987e5972SCameron Grant 764