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