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 32987e5972SCameron Grant #define AC97_MUTE 0x8000 33987e5972SCameron Grant 34987e5972SCameron Grant #define AC97_REG_RESET 0x00 35987e5972SCameron Grant #define AC97_MIX_MASTER 0x02 36987e5972SCameron Grant #define AC97_MIX_PHONES 0x04 37987e5972SCameron Grant #define AC97_MIX_MONO 0x06 38987e5972SCameron Grant #define AC97_MIX_TONE 0x08 39987e5972SCameron Grant #define AC97_MIX_BEEP 0x0a 40987e5972SCameron Grant #define AC97_MIX_PHONE 0x0c 41987e5972SCameron Grant #define AC97_MIX_MIC 0x0e 42987e5972SCameron Grant #define AC97_MIX_LINE 0x10 43987e5972SCameron Grant #define AC97_MIX_CD 0x12 44987e5972SCameron Grant #define AC97_MIX_VIDEO 0x14 45987e5972SCameron Grant #define AC97_MIX_AUX 0x16 46987e5972SCameron Grant #define AC97_MIX_PCM 0x18 47987e5972SCameron Grant #define AC97_REG_RECSEL 0x1a 48987e5972SCameron Grant #define AC97_MIX_RGAIN 0x1c 49987e5972SCameron Grant #define AC97_MIX_MGAIN 0x1e 50987e5972SCameron Grant #define AC97_REG_GEN 0x20 51987e5972SCameron Grant #define AC97_REG_3D 0x22 52987e5972SCameron Grant #define AC97_REG_POWER 0x26 53987e5972SCameron Grant #define AC97_REG_ID1 0x7c 54987e5972SCameron Grant #define AC97_REG_ID2 0x7e 55987e5972SCameron Grant 56987e5972SCameron Grant struct ac97mixtable_entry { 57987e5972SCameron Grant int reg:8; 58987e5972SCameron Grant unsigned bits:4; 59987e5972SCameron Grant unsigned ofs:4; 60987e5972SCameron Grant unsigned stereo:1; 61987e5972SCameron Grant unsigned mute:1; 62987e5972SCameron Grant unsigned recidx:4; 63987e5972SCameron Grant unsigned mask:1; 64987e5972SCameron Grant }; 65987e5972SCameron Grant 66987e5972SCameron Grant struct ac97_info { 6703a00905SCameron Grant device_t dev; 68987e5972SCameron Grant ac97_read *read; 69987e5972SCameron Grant ac97_write *write; 70987e5972SCameron Grant void *devinfo; 71987e5972SCameron Grant char id[4]; 72987e5972SCameron Grant char rev; 73987e5972SCameron Grant unsigned caps, se; 74987e5972SCameron Grant struct ac97mixtable_entry mix[32]; 75987e5972SCameron Grant }; 76987e5972SCameron Grant 77987e5972SCameron Grant struct ac97_codecid { 78987e5972SCameron Grant u_int32_t id; 79987e5972SCameron Grant char *name; 80987e5972SCameron Grant }; 81987e5972SCameron Grant 82987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = { 83987e5972SCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0 }, 84987e5972SCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1 }, 85987e5972SCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1 }, 86987e5972SCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0 }, 87987e5972SCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0 }, 88987e5972SCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0 }, 89987e5972SCameron Grant [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0 }, 90987e5972SCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0 }, 91987e5972SCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0 }, 92987e5972SCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0 }, 93987e5972SCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0 } 94987e5972SCameron Grant }; 95987e5972SCameron Grant 96987e5972SCameron Grant static const unsigned ac97mixdevs = 97987e5972SCameron Grant SOUND_MASK_VOLUME | 98987e5972SCameron Grant SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | 99987e5972SCameron Grant SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 | 100987e5972SCameron Grant SOUND_MASK_VIDEO | SOUND_MASK_RECLEV; 101987e5972SCameron Grant 102987e5972SCameron Grant static const unsigned ac97recdevs = 103987e5972SCameron Grant SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC | 104987e5972SCameron Grant SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO; 105987e5972SCameron Grant 106987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 107987e5972SCameron Grant { 0x414B4D00, "Asahi Kasei AK4540" }, 108987e5972SCameron Grant { 0x43525900, "Cirrus Logic CS4297" }, 109987e5972SCameron Grant { 0x83847600, "SigmaTel STAC????" }, 110987e5972SCameron Grant { 0x83847604, "SigmaTel STAC9701/3/4/5" }, 111987e5972SCameron Grant { 0x83847605, "SigmaTel STAC9704" }, 112987e5972SCameron Grant { 0x83847608, "SigmaTel STAC9708" }, 113987e5972SCameron Grant { 0x83847609, "SigmaTel STAC9721" }, 114987e5972SCameron Grant { 0, NULL } 115987e5972SCameron Grant }; 116987e5972SCameron Grant 117987e5972SCameron Grant static char *ac97enhancement[] = { 118987e5972SCameron Grant "", 119987e5972SCameron Grant "Analog Devices Phat Stereo", 120987e5972SCameron Grant "Creative Stereo Enhancement", 121987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 122987e5972SCameron Grant "Yamaha Ymersion", 123987e5972SCameron Grant "BBE 3D Stereo Enhancement", 124987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 125987e5972SCameron Grant "Qsound QXpander", 126987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 127987e5972SCameron Grant "SRS 3D Stereo Enhancement", 128987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 129987e5972SCameron Grant "AKM 3D Audio", 130987e5972SCameron Grant "Aureal Stereo Enhancement", 131987e5972SCameron Grant "Aztech 3D Enhancement", 132987e5972SCameron Grant "Binaura 3D Audio Enhancement", 133987e5972SCameron Grant "ESS Technology Stereo Enhancement", 134987e5972SCameron Grant "Harman International VMAx", 135987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 136987e5972SCameron Grant "Philips Incredible Sound", 137987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 138987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 139987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 140987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 141987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 142987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 143987e5972SCameron Grant "Delta Integration 3D Enhancement", 144987e5972SCameron Grant "SigmaTel 3D Enhancement", 145987e5972SCameron Grant "Reserved 27", 146987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 147987e5972SCameron Grant "Reserved 29", 148987e5972SCameron Grant "Reserved 30", 149987e5972SCameron Grant "Reserved 31" 150987e5972SCameron Grant }; 151987e5972SCameron Grant 152987e5972SCameron Grant static char *ac97feature[] = { 153987e5972SCameron Grant "mic channel", 154987e5972SCameron Grant "reserved", 155987e5972SCameron Grant "tone", 156987e5972SCameron Grant "simulated stereo", 157987e5972SCameron Grant "headphone", 158987e5972SCameron Grant "bass boost", 159987e5972SCameron Grant "18 bit DAC", 160987e5972SCameron Grant "20 bit DAC", 161987e5972SCameron Grant "18 bit ADC", 162987e5972SCameron Grant "20 bit ADC" 163987e5972SCameron Grant }; 164987e5972SCameron Grant 165987e5972SCameron Grant static int 166987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 167987e5972SCameron Grant { 168987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 169987e5972SCameron Grant if (e->recidx > 0) { 170987e5972SCameron Grant int val = e->recidx - 1; 171987e5972SCameron Grant val |= val << 8; 172987e5972SCameron Grant codec->write(codec->devinfo, AC97_REG_RECSEL, val); 173987e5972SCameron Grant return 0; 174987e5972SCameron Grant } else return -1; 175987e5972SCameron Grant } 176987e5972SCameron Grant 177987e5972SCameron Grant static int 178987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 179987e5972SCameron Grant { 180987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 181987e5972SCameron Grant if (e->reg != 0) { 182e479a8afSCameron Grant int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 183987e5972SCameron Grant 184987e5972SCameron Grant if (!e->stereo) right = left; 185987e5972SCameron Grant if (e->reg > 0) { 186987e5972SCameron Grant left = 100 - left; 187987e5972SCameron Grant right = 100 - right; 188987e5972SCameron Grant } 189987e5972SCameron Grant 190987e5972SCameron Grant max = (1 << e->bits) - 1; 191987e5972SCameron Grant left = (left * max) / 100; 192987e5972SCameron Grant right = (right * max) / 100; 193987e5972SCameron Grant 194987e5972SCameron Grant val = (left << 8) | right; 195987e5972SCameron Grant 196987e5972SCameron Grant left = (left * 100) / max; 197987e5972SCameron Grant right = (right * 100) / max; 198987e5972SCameron Grant 199987e5972SCameron Grant if (e->reg > 0) { 200987e5972SCameron Grant left = 100 - left; 201987e5972SCameron Grant right = 100 - right; 202987e5972SCameron Grant } 203987e5972SCameron Grant 204987e5972SCameron Grant if (!e->stereo) { 205987e5972SCameron Grant val &= max; 206987e5972SCameron Grant val <<= e->ofs; 207987e5972SCameron Grant if (e->mask) { 208987e5972SCameron Grant int cur = codec->read(codec->devinfo, e->reg); 209987e5972SCameron Grant val |= cur & ~(max << e->ofs); 210987e5972SCameron Grant } 211987e5972SCameron Grant } 212987e5972SCameron Grant if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE; 213e479a8afSCameron Grant codec->write(codec->devinfo, reg, val); 214987e5972SCameron Grant return left | (right << 8); 215987e5972SCameron Grant } else return -1; 216987e5972SCameron Grant } 217987e5972SCameron Grant 218987e5972SCameron Grant #if 0 219987e5972SCameron Grant static int 220987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel) 221987e5972SCameron Grant { 222987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 223987e5972SCameron Grant if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 224987e5972SCameron Grant int max, val, volume; 225987e5972SCameron Grant 226987e5972SCameron Grant max = (1 << e->bits) - 1; 227987e5972SCameron Grant val = codec->read(codec->devinfo, e->reg); 228987e5972SCameron Grant if (val == AC97_MUTE && e->mute == 1) volume = 0; 229987e5972SCameron Grant else { 230987e5972SCameron Grant if (e->stereo == 0) val >>= e->ofs; 231987e5972SCameron Grant val &= max; 232987e5972SCameron Grant volume = (val * 100) / max; 233987e5972SCameron Grant if (e->reg > 0) volume = 100 - volume; 234987e5972SCameron Grant } 235987e5972SCameron Grant return volume; 236987e5972SCameron Grant } else return -1; 237987e5972SCameron Grant } 238987e5972SCameron Grant #endif 239987e5972SCameron Grant 240987e5972SCameron Grant static unsigned 241987e5972SCameron Grant ac97_init(struct ac97_info *codec) 242987e5972SCameron Grant { 243987e5972SCameron Grant unsigned i, j; 244987e5972SCameron Grant u_int32_t id; 245987e5972SCameron Grant 246987e5972SCameron Grant for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i]; 247987e5972SCameron Grant 248987e5972SCameron Grant codec->write(codec->devinfo, AC97_REG_POWER, 0); 249987e5972SCameron Grant codec->write(codec->devinfo, AC97_REG_RESET, 0); 250987e5972SCameron Grant DELAY(10000); 251987e5972SCameron Grant 252987e5972SCameron Grant i = codec->read(codec->devinfo, AC97_REG_RESET); 253987e5972SCameron Grant codec->caps = i & 0x03ff; 254987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 255987e5972SCameron Grant 256987e5972SCameron Grant id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) | 257987e5972SCameron Grant codec->read(codec->devinfo, AC97_REG_ID2); 258987e5972SCameron Grant codec->rev = id & 0x000000ff; 259987e5972SCameron Grant 260987e5972SCameron Grant codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20); 261987e5972SCameron Grant if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20) 262987e5972SCameron Grant codec->mix[SOUND_MIXER_VOLUME].bits++; 263987e5972SCameron Grant codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00); 264987e5972SCameron Grant 265987e5972SCameron Grant if (bootverbose) { 26603a00905SCameron Grant device_printf(codec->dev, "ac97 codec id 0x%8x", id); 267987e5972SCameron Grant for (i = 0; ac97codecid[i].id; i++) { 268987e5972SCameron Grant if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name); 269987e5972SCameron Grant } 27003a00905SCameron Grant printf("\n"); 27103a00905SCameron Grant device_printf(codec->dev, "ac97 codec features "); 272987e5972SCameron Grant for (i = j = 0; i < 10; i++) { 273987e5972SCameron Grant if (codec->caps & (1 << i)) { 274987e5972SCameron Grant printf("%s%s", j? ", " : "", ac97feature[i]); 275987e5972SCameron Grant j++; 276987e5972SCameron Grant } 277987e5972SCameron Grant } 278987e5972SCameron Grant printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 279987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 280987e5972SCameron Grant } 281987e5972SCameron Grant 282987e5972SCameron Grant if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0) 28303a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 284987e5972SCameron Grant return 0; 285987e5972SCameron Grant } 286987e5972SCameron Grant 287987e5972SCameron Grant struct ac97_info * 28803a00905SCameron Grant ac97_create(device_t dev, void *devinfo, ac97_read *rd, ac97_write *wr) 289987e5972SCameron Grant { 290987e5972SCameron Grant struct ac97_info *codec; 291987e5972SCameron Grant 292987e5972SCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT); 293987e5972SCameron Grant if (codec != NULL) { 29403a00905SCameron Grant codec->dev = dev; 295987e5972SCameron Grant codec->read = rd; 296987e5972SCameron Grant codec->write = wr; 297987e5972SCameron Grant codec->devinfo = devinfo; 298987e5972SCameron Grant } 299987e5972SCameron Grant return codec; 300987e5972SCameron Grant } 301987e5972SCameron Grant 302987e5972SCameron Grant static int 303987e5972SCameron Grant ac97mix_init(snd_mixer *m) 304987e5972SCameron Grant { 305987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 306987e5972SCameron Grant if (codec == NULL) return -1; 307987e5972SCameron Grant ac97_init(codec); 308987e5972SCameron Grant mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0)); 309987e5972SCameron Grant mix_setrecdevs(m, ac97recdevs); 310987e5972SCameron Grant return 0; 311987e5972SCameron Grant } 312987e5972SCameron Grant 313987e5972SCameron Grant static int 314987e5972SCameron Grant ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 315987e5972SCameron Grant { 316987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 317987e5972SCameron Grant if (codec == NULL) return -1; 318987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 319987e5972SCameron Grant } 320987e5972SCameron Grant 321987e5972SCameron Grant static int 322987e5972SCameron Grant ac97mix_setrecsrc(snd_mixer *m, u_int32_t src) 323987e5972SCameron Grant { 324987e5972SCameron Grant int i; 325987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 326987e5972SCameron Grant if (codec == NULL) return -1; 327987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 328987e5972SCameron Grant if ((src & (1 << i)) != 0) break; 329987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 330987e5972SCameron Grant } 331987e5972SCameron Grant 332987e5972SCameron Grant snd_mixer ac97_mixer = { 333987e5972SCameron Grant "AC97 mixer", 334987e5972SCameron Grant ac97mix_init, 335987e5972SCameron Grant ac97mix_set, 336987e5972SCameron Grant ac97mix_setrecsrc, 337987e5972SCameron Grant }; 338987e5972SCameron Grant 339