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> 29987e5972SCameron Grant 300f55ac6cSCameron Grant #include "mixer_if.h" 31987e5972SCameron Grant 3267b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 3367b1dce3SCameron Grant 340f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 35987e5972SCameron Grant 3679bb7d52SCameron Grant struct ac97mixtable_entry { 3779bb7d52SCameron Grant int reg:8; 3879bb7d52SCameron Grant unsigned bits:4; 3979bb7d52SCameron Grant unsigned ofs:4; 4079bb7d52SCameron Grant unsigned stereo:1; 4179bb7d52SCameron Grant unsigned mute:1; 4279bb7d52SCameron Grant unsigned recidx:4; 4379bb7d52SCameron Grant unsigned mask:1; 4479bb7d52SCameron Grant unsigned enable:1; 4579bb7d52SCameron Grant }; 4679bb7d52SCameron Grant 4766ef8af5SCameron Grant #define AC97_NAMELEN 16 4866ef8af5SCameron Grant struct ac97_info { 4966ef8af5SCameron Grant kobj_t methods; 5066ef8af5SCameron Grant device_t dev; 5166ef8af5SCameron Grant void *devinfo; 5266ef8af5SCameron Grant char *id; 5366ef8af5SCameron Grant char rev; 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]; 5866ef8af5SCameron Grant void *lock; 5966ef8af5SCameron Grant }; 6066ef8af5SCameron Grant 61987e5972SCameron Grant struct ac97_codecid { 626b4b88f7SCameron Grant u_int32_t id, noext:1; 63987e5972SCameron Grant char *name; 64987e5972SCameron Grant }; 65987e5972SCameron Grant 66987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = { 67341f16ccSCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 68341f16ccSCameron Grant [SOUND_MIXER_MONITOR] = { AC97_MIX_PHONES, 5, 0, 1, 1, 0, 0, 0 }, 69341f16ccSCameron Grant [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 70341f16ccSCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 71341f16ccSCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 72341f16ccSCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 73341f16ccSCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 74341f16ccSCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 75341f16ccSCameron Grant [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 76341f16ccSCameron Grant [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0, 1 }, 77341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 78341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 79341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 80341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 81987e5972SCameron Grant }; 82987e5972SCameron Grant 83987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 8473770282SCameron Grant { 0x41445303, 0, "Analog Devices AD1819" }, 8573770282SCameron Grant { 0x41445340, 0, "Analog Devices AD1881" }, 8673770282SCameron Grant { 0x41445348, 0, "Analog Devices AD1881A" }, 8773770282SCameron Grant { 0x41445360, 0, "Analog Devices AD1885" }, 8873770282SCameron Grant { 0x414b4d00, 1, "Asahi Kasei AK4540" }, 8973770282SCameron Grant { 0x414b4d01, 1, "Asahi Kasei AK4542" }, 9073770282SCameron Grant { 0x414b4d02, 1, "Asahi Kasei AK4543" }, 9173770282SCameron Grant { 0x414c4710, 0, "Avance Logic ALC200/200P" }, 926b4b88f7SCameron Grant { 0x43525900, 0, "Cirrus Logic CS4297" }, 9373770282SCameron Grant { 0x43525903, 0, "Cirrus Logic CS4297" }, 9473770282SCameron Grant { 0x43525913, 0, "Cirrus Logic CS4297A" }, 9573770282SCameron Grant { 0x43525914, 0, "Cirrus Logic CS4297B" }, 9673770282SCameron Grant { 0x43525923, 0, "Cirrus Logic CS4294C" }, 9773770282SCameron Grant { 0x4352592b, 0, "Cirrus Logic CS4298C" }, 9873770282SCameron Grant { 0x43525931, 0, "Cirrus Logic CS4299A" }, 9973770282SCameron Grant { 0x43525933, 0, "Cirrus Logic CS4299C" }, 10073770282SCameron Grant { 0x43525934, 0, "Cirrus Logic CS4299D" }, 10173770282SCameron Grant { 0x43525941, 0, "Cirrus Logic CS4201A" }, 10273770282SCameron Grant { 0x43525951, 0, "Cirrus Logic CS4205A" }, 10373770282SCameron Grant { 0x43525961, 0, "Cirrus Logic CS4291A" }, 10473770282SCameron Grant { 0x45838308, 0, "ESS Technology ES1921" }, 10573770282SCameron Grant { 0x49434511, 0, "ICEnsemble ICE1232" }, 10673770282SCameron Grant { 0x4e534331, 0, "National Semiconductor LM4549" }, 10773770282SCameron Grant { 0x83847600, 0, "SigmaTel STAC9700/9783/9784" }, 10873770282SCameron Grant { 0x83847604, 0, "SigmaTel STAC9701/9703/9704/9705" }, 1096b4b88f7SCameron Grant { 0x83847605, 0, "SigmaTel STAC9704" }, 11073770282SCameron Grant { 0x83847608, 0, "SigmaTel STAC9708/9711" }, 11173770282SCameron Grant { 0x83847609, 0, "SigmaTel STAC9721/9723" }, 11273770282SCameron Grant { 0x83847644, 0, "SigmaTel STAC9744" }, 11373770282SCameron Grant { 0x83847656, 0, "SigmaTel STAC9756/9757" }, 11473770282SCameron Grant { 0x53494c22, 0, "Silicon Laboratory Si3036" }, 11573770282SCameron Grant { 0x53494c23, 0, "Silicon Laboratory Si3038" }, 11673770282SCameron Grant { 0x54524103, 0, "TriTech TR?????" }, 11773770282SCameron Grant { 0x54524106, 0, "TriTech TR28026" }, 11873770282SCameron Grant { 0x54524108, 0, "TriTech TR28028" }, 11973770282SCameron Grant { 0x54524123, 0, "TriTech TR28602" }, 12073770282SCameron Grant { 0x574d4c00, 0, "Wolfson WM9701A" }, 12173770282SCameron Grant { 0x574d4c03, 0, "Wolfson WM9703/9704" }, 12273770282SCameron Grant { 0x574d4c04, 0, "Wolfson WM9704 (quad)" }, 1236b4b88f7SCameron Grant { 0, 0, NULL } 124987e5972SCameron Grant }; 125987e5972SCameron Grant 126987e5972SCameron Grant static char *ac97enhancement[] = { 12704553e63SCameron Grant "no 3D Stereo Enhancement", 128987e5972SCameron Grant "Analog Devices Phat Stereo", 129987e5972SCameron Grant "Creative Stereo Enhancement", 130987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 131987e5972SCameron Grant "Yamaha Ymersion", 132987e5972SCameron Grant "BBE 3D Stereo Enhancement", 133987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 134987e5972SCameron Grant "Qsound QXpander", 135987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 136987e5972SCameron Grant "SRS 3D Stereo Enhancement", 137987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 138987e5972SCameron Grant "AKM 3D Audio", 139987e5972SCameron Grant "Aureal Stereo Enhancement", 140987e5972SCameron Grant "Aztech 3D Enhancement", 141987e5972SCameron Grant "Binaura 3D Audio Enhancement", 142987e5972SCameron Grant "ESS Technology Stereo Enhancement", 143987e5972SCameron Grant "Harman International VMAx", 144987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 145987e5972SCameron Grant "Philips Incredible Sound", 146987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 147987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 148987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 149987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 150987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 151987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 152987e5972SCameron Grant "Delta Integration 3D Enhancement", 153987e5972SCameron Grant "SigmaTel 3D Enhancement", 154987e5972SCameron Grant "Reserved 27", 155987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 156987e5972SCameron Grant "Reserved 29", 157987e5972SCameron Grant "Reserved 30", 158987e5972SCameron Grant "Reserved 31" 159987e5972SCameron Grant }; 160987e5972SCameron Grant 161987e5972SCameron Grant static char *ac97feature[] = { 162987e5972SCameron Grant "mic channel", 163987e5972SCameron Grant "reserved", 164987e5972SCameron Grant "tone", 165987e5972SCameron Grant "simulated stereo", 166987e5972SCameron Grant "headphone", 167987e5972SCameron Grant "bass boost", 168987e5972SCameron Grant "18 bit DAC", 169987e5972SCameron Grant "20 bit DAC", 170987e5972SCameron Grant "18 bit ADC", 171987e5972SCameron Grant "20 bit ADC" 172987e5972SCameron Grant }; 173987e5972SCameron Grant 17439004e69SCameron Grant static char *ac97extfeature[] = { 17539004e69SCameron Grant "variable rate PCM", 17639004e69SCameron Grant "double rate PCM", 17739004e69SCameron Grant "reserved 1", 17839004e69SCameron Grant "variable rate mic", 17939004e69SCameron Grant "reserved 2", 18039004e69SCameron Grant "reserved 3", 18139004e69SCameron Grant "center DAC", 18239004e69SCameron Grant "surround DAC", 18339004e69SCameron Grant "LFE DAC", 18439004e69SCameron Grant "AMAP", 18539004e69SCameron Grant "reserved 4", 18639004e69SCameron Grant "reserved 5", 18739004e69SCameron Grant "reserved 6", 18839004e69SCameron Grant "reserved 7", 18939004e69SCameron Grant }; 19039004e69SCameron Grant 19139004e69SCameron Grant static u_int16_t 19239004e69SCameron Grant rdcd(struct ac97_info *codec, int reg) 19339004e69SCameron Grant { 1940f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 19539004e69SCameron Grant } 19639004e69SCameron Grant 19739004e69SCameron Grant static void 19839004e69SCameron Grant wrcd(struct ac97_info *codec, int reg, u_int16_t val) 19939004e69SCameron Grant { 2000f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 20139004e69SCameron Grant } 20239004e69SCameron Grant 20339004e69SCameron Grant int 20439004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 20539004e69SCameron Grant { 20639004e69SCameron Grant u_int16_t v; 20739004e69SCameron Grant 20839004e69SCameron Grant switch(which) { 20939004e69SCameron Grant case AC97_REGEXT_FDACRATE: 21039004e69SCameron Grant case AC97_REGEXT_SDACRATE: 21139004e69SCameron Grant case AC97_REGEXT_LDACRATE: 21239004e69SCameron Grant case AC97_REGEXT_LADCRATE: 21339004e69SCameron Grant case AC97_REGEXT_MADCRATE: 21439004e69SCameron Grant break; 21539004e69SCameron Grant 21639004e69SCameron Grant default: 21739004e69SCameron Grant return -1; 21839004e69SCameron Grant } 21939004e69SCameron Grant 22066ef8af5SCameron Grant snd_mtxlock(codec->lock); 22139004e69SCameron Grant if (rate != 0) { 22239004e69SCameron Grant v = rate; 22339004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 22439004e69SCameron Grant v >>= 1; 22539004e69SCameron Grant wrcd(codec, which, v); 22639004e69SCameron Grant } 22739004e69SCameron Grant v = rdcd(codec, which); 22839004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 22939004e69SCameron Grant v <<= 1; 23066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 23139004e69SCameron Grant return v; 23239004e69SCameron Grant } 23339004e69SCameron Grant 23439004e69SCameron Grant int 23539004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 23639004e69SCameron Grant { 23739004e69SCameron Grant mode &= AC97_EXTCAPS; 238647fbfebSOrion Hodson if ((mode & ~codec->extcaps) != 0) { 239647fbfebSOrion Hodson device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 240647fbfebSOrion Hodson mode); 24139004e69SCameron Grant return -1; 242647fbfebSOrion Hodson } 24366ef8af5SCameron Grant snd_mtxlock(codec->lock); 24439004e69SCameron Grant wrcd(codec, AC97_REGEXT_STAT, mode); 24539004e69SCameron Grant codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 24666ef8af5SCameron Grant snd_mtxunlock(codec->lock); 24739004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 24839004e69SCameron Grant } 24939004e69SCameron Grant 2509ec437a3SCameron Grant u_int16_t 2519ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 2529ec437a3SCameron Grant { 2539ec437a3SCameron Grant return codec->extstat; 2549ec437a3SCameron Grant } 2559ec437a3SCameron Grant 2569ec437a3SCameron Grant u_int16_t 2579ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 2589ec437a3SCameron Grant { 2599ec437a3SCameron Grant return codec->extcaps; 2609ec437a3SCameron Grant } 2619ec437a3SCameron Grant 2625d91ad67SCameron Grant u_int16_t 2635d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec) 2645d91ad67SCameron Grant { 2655d91ad67SCameron Grant return codec->caps; 2665d91ad67SCameron Grant } 2675d91ad67SCameron Grant 268987e5972SCameron Grant static int 269987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 270987e5972SCameron Grant { 271987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 272341f16ccSCameron Grant 273987e5972SCameron Grant if (e->recidx > 0) { 274987e5972SCameron Grant int val = e->recidx - 1; 275987e5972SCameron Grant val |= val << 8; 27666ef8af5SCameron Grant snd_mtxlock(codec->lock); 27739004e69SCameron Grant wrcd(codec, AC97_REG_RECSEL, val); 27866ef8af5SCameron Grant snd_mtxunlock(codec->lock); 279987e5972SCameron Grant return 0; 28039004e69SCameron Grant } else 28139004e69SCameron Grant return -1; 282987e5972SCameron Grant } 283987e5972SCameron Grant 284987e5972SCameron Grant static int 285987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 286987e5972SCameron Grant { 287987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 288341f16ccSCameron Grant 289341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 290e479a8afSCameron Grant int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 291987e5972SCameron Grant 29239004e69SCameron Grant if (!e->stereo) 29339004e69SCameron Grant right = left; 294987e5972SCameron Grant if (e->reg > 0) { 295987e5972SCameron Grant left = 100 - left; 296987e5972SCameron Grant right = 100 - right; 297987e5972SCameron Grant } 298987e5972SCameron Grant 299987e5972SCameron Grant max = (1 << e->bits) - 1; 300987e5972SCameron Grant left = (left * max) / 100; 301987e5972SCameron Grant right = (right * max) / 100; 302987e5972SCameron Grant 303987e5972SCameron Grant val = (left << 8) | right; 304987e5972SCameron Grant 305987e5972SCameron Grant left = (left * 100) / max; 306987e5972SCameron Grant right = (right * 100) / max; 307987e5972SCameron Grant 308987e5972SCameron Grant if (e->reg > 0) { 309987e5972SCameron Grant left = 100 - left; 310987e5972SCameron Grant right = 100 - right; 311987e5972SCameron Grant } 312987e5972SCameron Grant 313987e5972SCameron Grant if (!e->stereo) { 314987e5972SCameron Grant val &= max; 315987e5972SCameron Grant val <<= e->ofs; 316987e5972SCameron Grant if (e->mask) { 31739004e69SCameron Grant int cur = rdcd(codec, e->reg); 318987e5972SCameron Grant val |= cur & ~(max << e->ofs); 319987e5972SCameron Grant } 320987e5972SCameron Grant } 32139004e69SCameron Grant if (left == 0 && right == 0 && e->mute == 1) 32239004e69SCameron Grant val = AC97_MUTE; 32366ef8af5SCameron Grant snd_mtxlock(codec->lock); 32439004e69SCameron Grant wrcd(codec, reg, val); 32566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 326987e5972SCameron Grant return left | (right << 8); 327341f16ccSCameron Grant } else { 328341f16ccSCameron Grant /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 32939004e69SCameron Grant return -1; 330987e5972SCameron Grant } 331341f16ccSCameron Grant } 332987e5972SCameron Grant 333987e5972SCameron Grant #if 0 334987e5972SCameron Grant static int 335987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel) 336987e5972SCameron Grant { 337987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 338987e5972SCameron Grant if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 339987e5972SCameron Grant int max, val, volume; 340987e5972SCameron Grant 341987e5972SCameron Grant max = (1 << e->bits) - 1; 34239004e69SCameron Grant val = rdcd(code, e->reg); 34339004e69SCameron Grant if (val == AC97_MUTE && e->mute == 1) 34439004e69SCameron Grant volume = 0; 345987e5972SCameron Grant else { 346987e5972SCameron Grant if (e->stereo == 0) val >>= e->ofs; 347987e5972SCameron Grant val &= max; 348987e5972SCameron Grant volume = (val * 100) / max; 349987e5972SCameron Grant if (e->reg > 0) volume = 100 - volume; 350987e5972SCameron Grant } 351987e5972SCameron Grant return volume; 35239004e69SCameron Grant } else 35339004e69SCameron Grant return -1; 354987e5972SCameron Grant } 355987e5972SCameron Grant #endif 356987e5972SCameron Grant 357987e5972SCameron Grant static unsigned 35839004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 359987e5972SCameron Grant { 360341f16ccSCameron Grant unsigned i, j, k, old; 361987e5972SCameron Grant u_int32_t id; 362987e5972SCameron Grant 36366ef8af5SCameron Grant snd_mtxlock(codec->lock); 36439004e69SCameron Grant for (i = 0; i < 32; i++) 36539004e69SCameron Grant codec->mix[i] = ac97mixtable_default[i]; 366987e5972SCameron Grant 3670f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 368cd2c103aSCameron Grant if (codec->count == 0) { 36904553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 37066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 37104553e63SCameron Grant return ENODEV; 37204553e63SCameron Grant } 3739ec437a3SCameron Grant 37479bb7d52SCameron Grant wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 37539004e69SCameron Grant wrcd(codec, AC97_REG_RESET, 0); 376e620d959SCameron Grant DELAY(100000); 37779bb7d52SCameron Grant wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 378987e5972SCameron Grant 37939004e69SCameron Grant i = rdcd(codec, AC97_REG_RESET); 380987e5972SCameron Grant codec->caps = i & 0x03ff; 381987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 382987e5972SCameron Grant 3836b4b88f7SCameron Grant id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2); 3846b4b88f7SCameron Grant codec->rev = id & 0x000000ff; 385e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 386e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 38766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 388e620d959SCameron Grant return ENODEV; 389e620d959SCameron Grant } 3906b4b88f7SCameron Grant 391cd2c103aSCameron Grant codec->noext = 0; 39266ef8af5SCameron Grant codec->id = NULL; 393cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 394cd2c103aSCameron Grant if (ac97codecid[i].id == id) { 39566ef8af5SCameron Grant codec->id = ac97codecid[i].name; 396cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 397cd2c103aSCameron Grant } 398cd2c103aSCameron Grant } 3996b4b88f7SCameron Grant 400cd2c103aSCameron Grant codec->extcaps = 0; 401cd2c103aSCameron Grant codec->extid = 0; 402cd2c103aSCameron Grant codec->extstat = 0; 4036a6ee5bbSCameron Grant if (!codec->noext) { 40439004e69SCameron Grant i = rdcd(codec, AC97_REGEXT_ID); 4056a6ee5bbSCameron Grant if (i != 0xffff) { 40639004e69SCameron Grant codec->extcaps = i & 0x3fff; 40739004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 40839004e69SCameron Grant codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 4096b4b88f7SCameron Grant } 4106a6ee5bbSCameron Grant } 411987e5972SCameron Grant 412341f16ccSCameron Grant for (i = 0; i < 32; i++) { 41333c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 41433c878f0SCameron Grant if (k && (codec->mix[i].reg > 0)) { 415341f16ccSCameron Grant old = rdcd(codec, codec->mix[i].reg); 416341f16ccSCameron Grant wrcd(codec, codec->mix[i].reg, 0x3f); 417341f16ccSCameron Grant j = rdcd(codec, codec->mix[i].reg); 418341f16ccSCameron Grant wrcd(codec, codec->mix[i].reg, old); 4196a6ee5bbSCameron Grant codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 420341f16ccSCameron Grant for (k = 1; j & (1 << k); k++); 421341f16ccSCameron Grant codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 422341f16ccSCameron Grant } 423341f16ccSCameron Grant /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 424341f16ccSCameron Grant } 425987e5972SCameron Grant 426987e5972SCameron Grant if (bootverbose) { 427e620d959SCameron Grant device_printf(codec->dev, "ac97 codec id 0x%08x", id); 42866ef8af5SCameron Grant if (codec->id) 42966ef8af5SCameron Grant printf(" (%s)", codec->id); 43003a00905SCameron Grant printf("\n"); 43103a00905SCameron Grant device_printf(codec->dev, "ac97 codec features "); 43239004e69SCameron Grant for (i = j = 0; i < 10; i++) 43339004e69SCameron Grant if (codec->caps & (1 << i)) 43439004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 43539004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 436987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 43739004e69SCameron Grant 43839004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 43939004e69SCameron Grant device_printf(codec->dev, "ac97 %s codec", 44039004e69SCameron Grant codec->extid? "secondary" : "primary"); 44139004e69SCameron Grant if (codec->extcaps) 44239004e69SCameron Grant printf(" extended features "); 44339004e69SCameron Grant for (i = j = 0; i < 14; i++) 44439004e69SCameron Grant if (codec->extcaps & (1 << i)) 44539004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 44639004e69SCameron Grant printf("\n"); 44739004e69SCameron Grant } 448987e5972SCameron Grant } 449987e5972SCameron Grant 45039004e69SCameron Grant if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 45103a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 45266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 453987e5972SCameron Grant return 0; 454987e5972SCameron Grant } 455987e5972SCameron Grant 4569ec437a3SCameron Grant static unsigned 4579ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 4589ec437a3SCameron Grant { 4599ec437a3SCameron Grant unsigned i; 4609ec437a3SCameron Grant 46166ef8af5SCameron Grant snd_mtxlock(codec->lock); 4620f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 4639ec437a3SCameron Grant if (codec->count == 0) { 4649ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 46566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 4669ec437a3SCameron Grant return ENODEV; 4679ec437a3SCameron Grant } 4689ec437a3SCameron Grant 46979bb7d52SCameron Grant wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 4709ec437a3SCameron Grant wrcd(codec, AC97_REG_RESET, 0); 4719ec437a3SCameron Grant DELAY(100000); 47279bb7d52SCameron Grant wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 4739ec437a3SCameron Grant i = rdcd(codec, AC97_REG_RESET); 4749ec437a3SCameron Grant 4759ec437a3SCameron Grant if (!codec->noext) { 4769ec437a3SCameron Grant wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 477b60e55dbSGuido van Rooij if ((rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 478b60e55dbSGuido van Rooij != codec->extstat) 4799ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 480b60e55dbSGuido van Rooij codec->extstat, 481b60e55dbSGuido van Rooij rdcd(codec, AC97_REGEXT_STAT) & 482b60e55dbSGuido van Rooij AC97_EXTCAPS); 4839ec437a3SCameron Grant } 4849ec437a3SCameron Grant 4859ec437a3SCameron Grant if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 4869ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 48766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 4889ec437a3SCameron Grant return 0; 4899ec437a3SCameron Grant } 4909ec437a3SCameron Grant 491987e5972SCameron Grant struct ac97_info * 4920f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 493987e5972SCameron Grant { 494987e5972SCameron Grant struct ac97_info *codec; 495987e5972SCameron Grant 4960f55ac6cSCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 4970f55ac6cSCameron Grant if (codec == NULL) 4980f55ac6cSCameron Grant return NULL; 4990f55ac6cSCameron Grant 50066ef8af5SCameron Grant snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 50166ef8af5SCameron Grant codec->lock = snd_mtxcreate(codec->name); 5020f55ac6cSCameron Grant codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 5030f55ac6cSCameron Grant if (codec->methods == NULL) { 50466ef8af5SCameron Grant snd_mtxlock(codec->lock); 50566ef8af5SCameron Grant snd_mtxfree(codec->lock); 5060f55ac6cSCameron Grant free(codec, M_AC97); 5070f55ac6cSCameron Grant return NULL; 508987e5972SCameron Grant } 5090f55ac6cSCameron Grant 5100f55ac6cSCameron Grant codec->dev = dev; 5110f55ac6cSCameron Grant codec->devinfo = devinfo; 51279bb7d52SCameron Grant codec->flags = 0; 513987e5972SCameron Grant return codec; 514987e5972SCameron Grant } 515987e5972SCameron Grant 51633dbf14aSCameron Grant void 51733dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 51833dbf14aSCameron Grant { 51966ef8af5SCameron Grant snd_mtxlock(codec->lock); 5200f55ac6cSCameron Grant if (codec->methods != NULL) 5210f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 52266ef8af5SCameron Grant snd_mtxfree(codec->lock); 5230f55ac6cSCameron Grant free(codec, M_AC97); 52433dbf14aSCameron Grant } 52533dbf14aSCameron Grant 52679bb7d52SCameron Grant void 52779bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 52879bb7d52SCameron Grant { 52979bb7d52SCameron Grant codec->flags = val; 53079bb7d52SCameron Grant } 53179bb7d52SCameron Grant 53279bb7d52SCameron Grant u_int32_t 53379bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 53479bb7d52SCameron Grant { 53579bb7d52SCameron Grant return codec->flags; 53679bb7d52SCameron Grant } 53779bb7d52SCameron Grant 5380f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 5390f55ac6cSCameron Grant 540987e5972SCameron Grant static int 54166ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 542987e5972SCameron Grant { 543987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 544341f16ccSCameron Grant u_int32_t i, mask; 545341f16ccSCameron Grant 54639004e69SCameron Grant if (codec == NULL) 54739004e69SCameron Grant return -1; 548341f16ccSCameron Grant 549e620d959SCameron Grant if (ac97_initmixer(codec)) 550e620d959SCameron Grant return -1; 551341f16ccSCameron Grant 552341f16ccSCameron Grant mask = 0; 553341f16ccSCameron Grant for (i = 0; i < 32; i++) 554341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 555341f16ccSCameron Grant mix_setdevs(m, mask); 556341f16ccSCameron Grant 557341f16ccSCameron Grant mask = 0; 558341f16ccSCameron Grant for (i = 0; i < 32; i++) 559341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 560341f16ccSCameron Grant mix_setrecdevs(m, mask); 561987e5972SCameron Grant return 0; 562987e5972SCameron Grant } 563987e5972SCameron Grant 564987e5972SCameron Grant static int 56566ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 56633dbf14aSCameron Grant { 56733dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 568341f16ccSCameron Grant 56933dbf14aSCameron Grant if (codec == NULL) 57033dbf14aSCameron Grant return -1; 57133dbf14aSCameron Grant /* 57233dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 57333dbf14aSCameron Grant return -1; 57433dbf14aSCameron Grant */ 57533dbf14aSCameron Grant ac97_destroy(codec); 57633dbf14aSCameron Grant return 0; 57733dbf14aSCameron Grant } 57833dbf14aSCameron Grant 57933dbf14aSCameron Grant static int 58066ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 5819ec437a3SCameron Grant { 5829ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 5839ec437a3SCameron Grant 5849ec437a3SCameron Grant if (codec == NULL) 5859ec437a3SCameron Grant return -1; 5869ec437a3SCameron Grant return ac97_reinitmixer(codec); 5879ec437a3SCameron Grant } 5889ec437a3SCameron Grant 5899ec437a3SCameron Grant static int 59066ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 591987e5972SCameron Grant { 592987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 593341f16ccSCameron Grant 59439004e69SCameron Grant if (codec == NULL) 59539004e69SCameron Grant return -1; 596987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 597987e5972SCameron Grant } 598987e5972SCameron Grant 599987e5972SCameron Grant static int 60066ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 601987e5972SCameron Grant { 602987e5972SCameron Grant int i; 603987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 604341f16ccSCameron Grant 60539004e69SCameron Grant if (codec == NULL) 60639004e69SCameron Grant return -1; 607987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 60839004e69SCameron Grant if ((src & (1 << i)) != 0) 60939004e69SCameron Grant break; 610987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 611987e5972SCameron Grant } 612987e5972SCameron Grant 6130f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 6140f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 6150f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 6160f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 6170f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 6180f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 6190f55ac6cSCameron Grant { 0, 0 } 620987e5972SCameron Grant }; 6210f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 6220f55ac6cSCameron Grant 6230f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 6240f55ac6cSCameron Grant 6250f55ac6cSCameron Grant kobj_class_t 6260f55ac6cSCameron Grant ac97_getmixerclass(void) 6270f55ac6cSCameron Grant { 6280f55ac6cSCameron Grant return &ac97mixer_class; 6290f55ac6cSCameron Grant } 6300f55ac6cSCameron Grant 631987e5972SCameron Grant 632