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 * 2653c5a968SPeter Wemm * $FreeBSD$ 27987e5972SCameron Grant */ 28987e5972SCameron Grant 29ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 30ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h> 31987e5972SCameron Grant 320f55ac6cSCameron Grant #include "mixer_if.h" 33987e5972SCameron Grant 340f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 35987e5972SCameron Grant 3666ef8af5SCameron Grant #define AC97_NAMELEN 16 3766ef8af5SCameron Grant struct ac97_info { 3866ef8af5SCameron Grant kobj_t methods; 3966ef8af5SCameron Grant device_t dev; 4066ef8af5SCameron Grant void *devinfo; 4166ef8af5SCameron Grant char *id; 4266ef8af5SCameron Grant char rev; 4366ef8af5SCameron Grant unsigned count, caps, se, extcaps, extid, extstat, noext:1; 4466ef8af5SCameron Grant struct ac97mixtable_entry mix[32]; 4566ef8af5SCameron Grant char name[AC97_NAMELEN]; 4666ef8af5SCameron Grant void *lock; 4766ef8af5SCameron Grant }; 4866ef8af5SCameron Grant 49987e5972SCameron Grant struct ac97_codecid { 506b4b88f7SCameron Grant u_int32_t id, noext:1; 51987e5972SCameron Grant char *name; 52987e5972SCameron Grant }; 53987e5972SCameron Grant 54987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = { 55341f16ccSCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 56341f16ccSCameron Grant [SOUND_MIXER_MONITOR] = { AC97_MIX_PHONES, 5, 0, 1, 1, 0, 0, 0 }, 57341f16ccSCameron Grant [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 58341f16ccSCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 59341f16ccSCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 60341f16ccSCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 61341f16ccSCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 62341f16ccSCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 63341f16ccSCameron Grant [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 64341f16ccSCameron Grant [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0, 1 }, 65341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 66341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 67341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 68341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 69987e5972SCameron Grant }; 70987e5972SCameron Grant 71987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 7273770282SCameron Grant { 0x41445303, 0, "Analog Devices AD1819" }, 7373770282SCameron Grant { 0x41445340, 0, "Analog Devices AD1881" }, 7473770282SCameron Grant { 0x41445348, 0, "Analog Devices AD1881A" }, 7573770282SCameron Grant { 0x41445360, 0, "Analog Devices AD1885" }, 7673770282SCameron Grant { 0x414b4d00, 1, "Asahi Kasei AK4540" }, 7773770282SCameron Grant { 0x414b4d01, 1, "Asahi Kasei AK4542" }, 7873770282SCameron Grant { 0x414b4d02, 1, "Asahi Kasei AK4543" }, 7973770282SCameron Grant { 0x414c4710, 0, "Avance Logic ALC200/200P" }, 806b4b88f7SCameron Grant { 0x43525900, 0, "Cirrus Logic CS4297" }, 8173770282SCameron Grant { 0x43525903, 0, "Cirrus Logic CS4297" }, 8273770282SCameron Grant { 0x43525913, 0, "Cirrus Logic CS4297A" }, 8373770282SCameron Grant { 0x43525914, 0, "Cirrus Logic CS4297B" }, 8473770282SCameron Grant { 0x43525923, 0, "Cirrus Logic CS4294C" }, 8573770282SCameron Grant { 0x4352592b, 0, "Cirrus Logic CS4298C" }, 8673770282SCameron Grant { 0x43525931, 0, "Cirrus Logic CS4299A" }, 8773770282SCameron Grant { 0x43525933, 0, "Cirrus Logic CS4299C" }, 8873770282SCameron Grant { 0x43525934, 0, "Cirrus Logic CS4299D" }, 8973770282SCameron Grant { 0x43525941, 0, "Cirrus Logic CS4201A" }, 9073770282SCameron Grant { 0x43525951, 0, "Cirrus Logic CS4205A" }, 9173770282SCameron Grant { 0x43525961, 0, "Cirrus Logic CS4291A" }, 9273770282SCameron Grant { 0x45838308, 0, "ESS Technology ES1921" }, 9373770282SCameron Grant { 0x49434511, 0, "ICEnsemble ICE1232" }, 9473770282SCameron Grant { 0x4e534331, 0, "National Semiconductor LM4549" }, 9573770282SCameron Grant { 0x83847600, 0, "SigmaTel STAC9700/9783/9784" }, 9673770282SCameron Grant { 0x83847604, 0, "SigmaTel STAC9701/9703/9704/9705" }, 976b4b88f7SCameron Grant { 0x83847605, 0, "SigmaTel STAC9704" }, 9873770282SCameron Grant { 0x83847608, 0, "SigmaTel STAC9708/9711" }, 9973770282SCameron Grant { 0x83847609, 0, "SigmaTel STAC9721/9723" }, 10073770282SCameron Grant { 0x83847644, 0, "SigmaTel STAC9744" }, 10173770282SCameron Grant { 0x83847656, 0, "SigmaTel STAC9756/9757" }, 10273770282SCameron Grant { 0x53494c22, 0, "Silicon Laboratory Si3036" }, 10373770282SCameron Grant { 0x53494c23, 0, "Silicon Laboratory Si3038" }, 10473770282SCameron Grant { 0x54524103, 0, "TriTech TR?????" }, 10573770282SCameron Grant { 0x54524106, 0, "TriTech TR28026" }, 10673770282SCameron Grant { 0x54524108, 0, "TriTech TR28028" }, 10773770282SCameron Grant { 0x54524123, 0, "TriTech TR28602" }, 10873770282SCameron Grant { 0x574d4c00, 0, "Wolfson WM9701A" }, 10973770282SCameron Grant { 0x574d4c03, 0, "Wolfson WM9703/9704" }, 11073770282SCameron Grant { 0x574d4c04, 0, "Wolfson WM9704 (quad)" }, 1116b4b88f7SCameron Grant { 0, 0, NULL } 112987e5972SCameron Grant }; 113987e5972SCameron Grant 114987e5972SCameron Grant static char *ac97enhancement[] = { 11504553e63SCameron Grant "no 3D Stereo Enhancement", 116987e5972SCameron Grant "Analog Devices Phat Stereo", 117987e5972SCameron Grant "Creative Stereo Enhancement", 118987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 119987e5972SCameron Grant "Yamaha Ymersion", 120987e5972SCameron Grant "BBE 3D Stereo Enhancement", 121987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 122987e5972SCameron Grant "Qsound QXpander", 123987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 124987e5972SCameron Grant "SRS 3D Stereo Enhancement", 125987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 126987e5972SCameron Grant "AKM 3D Audio", 127987e5972SCameron Grant "Aureal Stereo Enhancement", 128987e5972SCameron Grant "Aztech 3D Enhancement", 129987e5972SCameron Grant "Binaura 3D Audio Enhancement", 130987e5972SCameron Grant "ESS Technology Stereo Enhancement", 131987e5972SCameron Grant "Harman International VMAx", 132987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 133987e5972SCameron Grant "Philips Incredible Sound", 134987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 135987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 136987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 137987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 138987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 139987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 140987e5972SCameron Grant "Delta Integration 3D Enhancement", 141987e5972SCameron Grant "SigmaTel 3D Enhancement", 142987e5972SCameron Grant "Reserved 27", 143987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 144987e5972SCameron Grant "Reserved 29", 145987e5972SCameron Grant "Reserved 30", 146987e5972SCameron Grant "Reserved 31" 147987e5972SCameron Grant }; 148987e5972SCameron Grant 149987e5972SCameron Grant static char *ac97feature[] = { 150987e5972SCameron Grant "mic channel", 151987e5972SCameron Grant "reserved", 152987e5972SCameron Grant "tone", 153987e5972SCameron Grant "simulated stereo", 154987e5972SCameron Grant "headphone", 155987e5972SCameron Grant "bass boost", 156987e5972SCameron Grant "18 bit DAC", 157987e5972SCameron Grant "20 bit DAC", 158987e5972SCameron Grant "18 bit ADC", 159987e5972SCameron Grant "20 bit ADC" 160987e5972SCameron Grant }; 161987e5972SCameron Grant 16239004e69SCameron Grant static char *ac97extfeature[] = { 16339004e69SCameron Grant "variable rate PCM", 16439004e69SCameron Grant "double rate PCM", 16539004e69SCameron Grant "reserved 1", 16639004e69SCameron Grant "variable rate mic", 16739004e69SCameron Grant "reserved 2", 16839004e69SCameron Grant "reserved 3", 16939004e69SCameron Grant "center DAC", 17039004e69SCameron Grant "surround DAC", 17139004e69SCameron Grant "LFE DAC", 17239004e69SCameron Grant "AMAP", 17339004e69SCameron Grant "reserved 4", 17439004e69SCameron Grant "reserved 5", 17539004e69SCameron Grant "reserved 6", 17639004e69SCameron Grant "reserved 7", 17739004e69SCameron Grant }; 17839004e69SCameron Grant 17939004e69SCameron Grant static u_int16_t 18039004e69SCameron Grant rdcd(struct ac97_info *codec, int reg) 18139004e69SCameron Grant { 1820f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 18339004e69SCameron Grant } 18439004e69SCameron Grant 18539004e69SCameron Grant static void 18639004e69SCameron Grant wrcd(struct ac97_info *codec, int reg, u_int16_t val) 18739004e69SCameron Grant { 1880f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 18939004e69SCameron Grant } 19039004e69SCameron Grant 19139004e69SCameron Grant int 19239004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 19339004e69SCameron Grant { 19439004e69SCameron Grant u_int16_t v; 19539004e69SCameron Grant 19639004e69SCameron Grant switch(which) { 19739004e69SCameron Grant case AC97_REGEXT_FDACRATE: 19839004e69SCameron Grant case AC97_REGEXT_SDACRATE: 19939004e69SCameron Grant case AC97_REGEXT_LDACRATE: 20039004e69SCameron Grant case AC97_REGEXT_LADCRATE: 20139004e69SCameron Grant case AC97_REGEXT_MADCRATE: 20239004e69SCameron Grant break; 20339004e69SCameron Grant 20439004e69SCameron Grant default: 20539004e69SCameron Grant return -1; 20639004e69SCameron Grant } 20739004e69SCameron Grant 20866ef8af5SCameron Grant snd_mtxlock(codec->lock); 20939004e69SCameron Grant if (rate != 0) { 21039004e69SCameron Grant v = rate; 21139004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 21239004e69SCameron Grant v >>= 1; 21339004e69SCameron Grant wrcd(codec, which, v); 21439004e69SCameron Grant } 21539004e69SCameron Grant v = rdcd(codec, which); 21639004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 21739004e69SCameron Grant v <<= 1; 21866ef8af5SCameron Grant snd_mtxunlock(codec->lock); 21939004e69SCameron Grant return v; 22039004e69SCameron Grant } 22139004e69SCameron Grant 22239004e69SCameron Grant int 22339004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 22439004e69SCameron Grant { 22539004e69SCameron Grant mode &= AC97_EXTCAPS; 22639004e69SCameron Grant if ((mode & ~codec->extcaps) != 0) 22739004e69SCameron Grant return -1; 22866ef8af5SCameron Grant snd_mtxlock(codec->lock); 22939004e69SCameron Grant wrcd(codec, AC97_REGEXT_STAT, mode); 23039004e69SCameron Grant codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 23166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 23239004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 23339004e69SCameron Grant } 23439004e69SCameron Grant 2359ec437a3SCameron Grant u_int16_t 2369ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 2379ec437a3SCameron Grant { 2389ec437a3SCameron Grant return codec->extstat; 2399ec437a3SCameron Grant } 2409ec437a3SCameron Grant 2419ec437a3SCameron Grant u_int16_t 2429ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 2439ec437a3SCameron Grant { 2449ec437a3SCameron Grant return codec->extcaps; 2459ec437a3SCameron Grant } 2469ec437a3SCameron Grant 247987e5972SCameron Grant static int 248987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 249987e5972SCameron Grant { 250987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 251341f16ccSCameron Grant 252987e5972SCameron Grant if (e->recidx > 0) { 253987e5972SCameron Grant int val = e->recidx - 1; 254987e5972SCameron Grant val |= val << 8; 25566ef8af5SCameron Grant snd_mtxlock(codec->lock); 25639004e69SCameron Grant wrcd(codec, AC97_REG_RECSEL, val); 25766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 258987e5972SCameron Grant return 0; 25939004e69SCameron Grant } else 26039004e69SCameron Grant return -1; 261987e5972SCameron Grant } 262987e5972SCameron Grant 263987e5972SCameron Grant static int 264987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 265987e5972SCameron Grant { 266987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 267341f16ccSCameron Grant 268341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 269e479a8afSCameron Grant int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 270987e5972SCameron Grant 27139004e69SCameron Grant if (!e->stereo) 27239004e69SCameron Grant right = left; 273987e5972SCameron Grant if (e->reg > 0) { 274987e5972SCameron Grant left = 100 - left; 275987e5972SCameron Grant right = 100 - right; 276987e5972SCameron Grant } 277987e5972SCameron Grant 278987e5972SCameron Grant max = (1 << e->bits) - 1; 279987e5972SCameron Grant left = (left * max) / 100; 280987e5972SCameron Grant right = (right * max) / 100; 281987e5972SCameron Grant 282987e5972SCameron Grant val = (left << 8) | right; 283987e5972SCameron Grant 284987e5972SCameron Grant left = (left * 100) / max; 285987e5972SCameron Grant right = (right * 100) / max; 286987e5972SCameron Grant 287987e5972SCameron Grant if (e->reg > 0) { 288987e5972SCameron Grant left = 100 - left; 289987e5972SCameron Grant right = 100 - right; 290987e5972SCameron Grant } 291987e5972SCameron Grant 292987e5972SCameron Grant if (!e->stereo) { 293987e5972SCameron Grant val &= max; 294987e5972SCameron Grant val <<= e->ofs; 295987e5972SCameron Grant if (e->mask) { 29639004e69SCameron Grant int cur = rdcd(codec, e->reg); 297987e5972SCameron Grant val |= cur & ~(max << e->ofs); 298987e5972SCameron Grant } 299987e5972SCameron Grant } 30039004e69SCameron Grant if (left == 0 && right == 0 && e->mute == 1) 30139004e69SCameron Grant val = AC97_MUTE; 30266ef8af5SCameron Grant snd_mtxlock(codec->lock); 30339004e69SCameron Grant wrcd(codec, reg, val); 30466ef8af5SCameron Grant snd_mtxunlock(codec->lock); 305987e5972SCameron Grant return left | (right << 8); 306341f16ccSCameron Grant } else { 307341f16ccSCameron Grant /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 30839004e69SCameron Grant return -1; 309987e5972SCameron Grant } 310341f16ccSCameron Grant } 311987e5972SCameron Grant 312987e5972SCameron Grant #if 0 313987e5972SCameron Grant static int 314987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel) 315987e5972SCameron Grant { 316987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 317987e5972SCameron Grant if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 318987e5972SCameron Grant int max, val, volume; 319987e5972SCameron Grant 320987e5972SCameron Grant max = (1 << e->bits) - 1; 32139004e69SCameron Grant val = rdcd(code, e->reg); 32239004e69SCameron Grant if (val == AC97_MUTE && e->mute == 1) 32339004e69SCameron Grant volume = 0; 324987e5972SCameron Grant else { 325987e5972SCameron Grant if (e->stereo == 0) val >>= e->ofs; 326987e5972SCameron Grant val &= max; 327987e5972SCameron Grant volume = (val * 100) / max; 328987e5972SCameron Grant if (e->reg > 0) volume = 100 - volume; 329987e5972SCameron Grant } 330987e5972SCameron Grant return volume; 33139004e69SCameron Grant } else 33239004e69SCameron Grant return -1; 333987e5972SCameron Grant } 334987e5972SCameron Grant #endif 335987e5972SCameron Grant 336987e5972SCameron Grant static unsigned 33739004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 338987e5972SCameron Grant { 339341f16ccSCameron Grant unsigned i, j, k, old; 340987e5972SCameron Grant u_int32_t id; 341987e5972SCameron Grant 34266ef8af5SCameron Grant snd_mtxlock(codec->lock); 34339004e69SCameron Grant for (i = 0; i < 32; i++) 34439004e69SCameron Grant codec->mix[i] = ac97mixtable_default[i]; 345987e5972SCameron Grant 3460f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 347cd2c103aSCameron Grant if (codec->count == 0) { 34804553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 34966ef8af5SCameron Grant snd_mtxunlock(codec->lock); 35004553e63SCameron Grant return ENODEV; 35104553e63SCameron Grant } 3529ec437a3SCameron Grant 35371508289SCameron Grant wrcd(codec, AC97_REG_POWER, 0x8000); 35439004e69SCameron Grant wrcd(codec, AC97_REG_RESET, 0); 355e620d959SCameron Grant DELAY(100000); 35671508289SCameron Grant wrcd(codec, AC97_REG_POWER, 0x8000); 357987e5972SCameron Grant 35839004e69SCameron Grant i = rdcd(codec, AC97_REG_RESET); 359987e5972SCameron Grant codec->caps = i & 0x03ff; 360987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 361987e5972SCameron Grant 3626b4b88f7SCameron Grant id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2); 3636b4b88f7SCameron Grant codec->rev = id & 0x000000ff; 364e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 365e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 36666ef8af5SCameron Grant snd_mtxunlock(codec->lock); 367e620d959SCameron Grant return ENODEV; 368e620d959SCameron Grant } 3696b4b88f7SCameron Grant 370cd2c103aSCameron Grant codec->noext = 0; 37166ef8af5SCameron Grant codec->id = NULL; 372cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 373cd2c103aSCameron Grant if (ac97codecid[i].id == id) { 37466ef8af5SCameron Grant codec->id = ac97codecid[i].name; 375cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 376cd2c103aSCameron Grant } 377cd2c103aSCameron Grant } 3786b4b88f7SCameron Grant 379cd2c103aSCameron Grant codec->extcaps = 0; 380cd2c103aSCameron Grant codec->extid = 0; 381cd2c103aSCameron Grant codec->extstat = 0; 3826a6ee5bbSCameron Grant if (!codec->noext) { 38339004e69SCameron Grant i = rdcd(codec, AC97_REGEXT_ID); 3846a6ee5bbSCameron Grant if (i != 0xffff) { 38539004e69SCameron Grant codec->extcaps = i & 0x3fff; 38639004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 38739004e69SCameron Grant codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 3886b4b88f7SCameron Grant } 3896a6ee5bbSCameron Grant } 390987e5972SCameron Grant 391341f16ccSCameron Grant for (i = 0; i < 32; i++) { 39233c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 39333c878f0SCameron Grant if (k && (codec->mix[i].reg > 0)) { 394341f16ccSCameron Grant old = rdcd(codec, codec->mix[i].reg); 395341f16ccSCameron Grant wrcd(codec, codec->mix[i].reg, 0x3f); 396341f16ccSCameron Grant j = rdcd(codec, codec->mix[i].reg); 397341f16ccSCameron Grant wrcd(codec, codec->mix[i].reg, old); 3986a6ee5bbSCameron Grant codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 399341f16ccSCameron Grant for (k = 1; j & (1 << k); k++); 400341f16ccSCameron Grant codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 401341f16ccSCameron Grant } 402341f16ccSCameron Grant /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 403341f16ccSCameron Grant } 404987e5972SCameron Grant 405987e5972SCameron Grant if (bootverbose) { 406e620d959SCameron Grant device_printf(codec->dev, "ac97 codec id 0x%08x", id); 40766ef8af5SCameron Grant if (codec->id) 40866ef8af5SCameron Grant printf(" (%s)", codec->id); 40903a00905SCameron Grant printf("\n"); 41003a00905SCameron Grant device_printf(codec->dev, "ac97 codec features "); 41139004e69SCameron Grant for (i = j = 0; i < 10; i++) 41239004e69SCameron Grant if (codec->caps & (1 << i)) 41339004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 41439004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 415987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 41639004e69SCameron Grant 41739004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 41839004e69SCameron Grant device_printf(codec->dev, "ac97 %s codec", 41939004e69SCameron Grant codec->extid? "secondary" : "primary"); 42039004e69SCameron Grant if (codec->extcaps) 42139004e69SCameron Grant printf(" extended features "); 42239004e69SCameron Grant for (i = j = 0; i < 14; i++) 42339004e69SCameron Grant if (codec->extcaps & (1 << i)) 42439004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 42539004e69SCameron Grant printf("\n"); 42639004e69SCameron Grant } 427987e5972SCameron Grant } 428987e5972SCameron Grant 42939004e69SCameron Grant if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 43003a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 43166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 432987e5972SCameron Grant return 0; 433987e5972SCameron Grant } 434987e5972SCameron Grant 4359ec437a3SCameron Grant static unsigned 4369ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 4379ec437a3SCameron Grant { 4389ec437a3SCameron Grant unsigned i; 4399ec437a3SCameron Grant 44066ef8af5SCameron Grant snd_mtxlock(codec->lock); 4410f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 4429ec437a3SCameron Grant if (codec->count == 0) { 4439ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 44466ef8af5SCameron Grant snd_mtxunlock(codec->lock); 4459ec437a3SCameron Grant return ENODEV; 4469ec437a3SCameron Grant } 4479ec437a3SCameron Grant 4489ec437a3SCameron Grant wrcd(codec, AC97_REG_POWER, 0); 4499ec437a3SCameron Grant wrcd(codec, AC97_REG_RESET, 0); 4509ec437a3SCameron Grant DELAY(100000); 4519ec437a3SCameron Grant i = rdcd(codec, AC97_REG_RESET); 4529ec437a3SCameron Grant 4539ec437a3SCameron Grant if (!codec->noext) { 4549ec437a3SCameron Grant wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 4559ec437a3SCameron Grant if (rdcd(codec, AC97_REGEXT_STAT) != codec->extstat) 4569ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 4579ec437a3SCameron Grant codec->extstat, rdcd(codec, AC97_REGEXT_STAT)); 4589ec437a3SCameron Grant } 4599ec437a3SCameron Grant 4609ec437a3SCameron Grant if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 4619ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 46266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 4639ec437a3SCameron Grant return 0; 4649ec437a3SCameron Grant } 4659ec437a3SCameron Grant 466987e5972SCameron Grant struct ac97_info * 4670f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 468987e5972SCameron Grant { 469987e5972SCameron Grant struct ac97_info *codec; 470987e5972SCameron Grant 4710f55ac6cSCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 4720f55ac6cSCameron Grant if (codec == NULL) 4730f55ac6cSCameron Grant return NULL; 4740f55ac6cSCameron Grant 47566ef8af5SCameron Grant snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 47666ef8af5SCameron Grant codec->lock = snd_mtxcreate(codec->name); 4770f55ac6cSCameron Grant codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 4780f55ac6cSCameron Grant if (codec->methods == NULL) { 47966ef8af5SCameron Grant snd_mtxlock(codec->lock); 48066ef8af5SCameron Grant snd_mtxfree(codec->lock); 4810f55ac6cSCameron Grant free(codec, M_AC97); 4820f55ac6cSCameron Grant return NULL; 483987e5972SCameron Grant } 4840f55ac6cSCameron Grant 4850f55ac6cSCameron Grant codec->dev = dev; 4860f55ac6cSCameron Grant codec->devinfo = devinfo; 487987e5972SCameron Grant return codec; 488987e5972SCameron Grant } 489987e5972SCameron Grant 49033dbf14aSCameron Grant void 49133dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 49233dbf14aSCameron Grant { 49366ef8af5SCameron Grant snd_mtxlock(codec->lock); 4940f55ac6cSCameron Grant if (codec->methods != NULL) 4950f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 49666ef8af5SCameron Grant snd_mtxfree(codec->lock); 4970f55ac6cSCameron Grant free(codec, M_AC97); 49833dbf14aSCameron Grant } 49933dbf14aSCameron Grant 5000f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 5010f55ac6cSCameron Grant 502987e5972SCameron Grant static int 50366ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 504987e5972SCameron Grant { 505987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 506341f16ccSCameron Grant u_int32_t i, mask; 507341f16ccSCameron Grant 50839004e69SCameron Grant if (codec == NULL) 50939004e69SCameron Grant return -1; 510341f16ccSCameron Grant 511e620d959SCameron Grant if (ac97_initmixer(codec)) 512e620d959SCameron Grant return -1; 513341f16ccSCameron Grant 514341f16ccSCameron Grant mask = 0; 515341f16ccSCameron Grant for (i = 0; i < 32; i++) 516341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 517341f16ccSCameron Grant mix_setdevs(m, mask); 518341f16ccSCameron Grant 519341f16ccSCameron Grant mask = 0; 520341f16ccSCameron Grant for (i = 0; i < 32; i++) 521341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 522341f16ccSCameron Grant mix_setrecdevs(m, mask); 523987e5972SCameron Grant return 0; 524987e5972SCameron Grant } 525987e5972SCameron Grant 526987e5972SCameron Grant static int 52766ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 52833dbf14aSCameron Grant { 52933dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 530341f16ccSCameron Grant 53133dbf14aSCameron Grant if (codec == NULL) 53233dbf14aSCameron Grant return -1; 53333dbf14aSCameron Grant /* 53433dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 53533dbf14aSCameron Grant return -1; 53633dbf14aSCameron Grant */ 53733dbf14aSCameron Grant ac97_destroy(codec); 53833dbf14aSCameron Grant return 0; 53933dbf14aSCameron Grant } 54033dbf14aSCameron Grant 54133dbf14aSCameron Grant static int 54266ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 5439ec437a3SCameron Grant { 5449ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 5459ec437a3SCameron Grant 5469ec437a3SCameron Grant if (codec == NULL) 5479ec437a3SCameron Grant return -1; 5489ec437a3SCameron Grant return ac97_reinitmixer(codec); 5499ec437a3SCameron Grant } 5509ec437a3SCameron Grant 5519ec437a3SCameron Grant static int 55266ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 553987e5972SCameron Grant { 554987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 555341f16ccSCameron Grant 55639004e69SCameron Grant if (codec == NULL) 55739004e69SCameron Grant return -1; 558987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 559987e5972SCameron Grant } 560987e5972SCameron Grant 561987e5972SCameron Grant static int 56266ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 563987e5972SCameron Grant { 564987e5972SCameron Grant int i; 565987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 566341f16ccSCameron Grant 56739004e69SCameron Grant if (codec == NULL) 56839004e69SCameron Grant return -1; 569987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 57039004e69SCameron Grant if ((src & (1 << i)) != 0) 57139004e69SCameron Grant break; 572987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 573987e5972SCameron Grant } 574987e5972SCameron Grant 5750f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 5760f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 5770f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 5780f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 5790f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 5800f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 5810f55ac6cSCameron Grant { 0, 0 } 582987e5972SCameron Grant }; 5830f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 5840f55ac6cSCameron Grant 5850f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 5860f55ac6cSCameron Grant 5870f55ac6cSCameron Grant kobj_class_t 5880f55ac6cSCameron Grant ac97_getmixerclass(void) 5890f55ac6cSCameron Grant { 5900f55ac6cSCameron Grant return &ac97mixer_class; 5910f55ac6cSCameron Grant } 5920f55ac6cSCameron Grant 593987e5972SCameron Grant 594