ac97.c revision 53512
1299244Sjilles/* 2299244Sjilles * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3299244Sjilles * All rights reserved. 4299244Sjilles * 5299244Sjilles * Redistribution and use in source and binary forms, with or without 6299244Sjilles * modification, are permitted provided that the following conditions 7299244Sjilles * are met: 8299244Sjilles * 1. Redistributions of source code must retain the above copyright 9299244Sjilles * notice, this list of conditions and the following disclaimer. 10299244Sjilles * 2. Redistributions in binary form must reproduce the above copyright 11299244Sjilles * notice, this list of conditions and the following disclaimer in the 12299244Sjilles * documentation and/or other materials provided with the distribution. 13299244Sjilles * 14299244Sjilles * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15299244Sjilles * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16299244Sjilles * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17299244Sjilles * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18299244Sjilles * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19299244Sjilles * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20299244Sjilles * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21299244Sjilles * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22299244Sjilles * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23299244Sjilles * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24299244Sjilles * SUCH DAMAGE. 25299244Sjilles * 26299244Sjilles * $FreeBSD: head/sys/dev/sound/pcm/ac97.c 53512 1999-11-21 17:15:12Z cg $ 27299244Sjilles */ 28299244Sjilles 29299244Sjilles#include <dev/sound/pcm/sound.h> 30299244Sjilles#include <dev/sound/pcm/ac97.h> 31299244Sjilles 32299244Sjilles#define AC97_MUTE 0x8000 33299244Sjilles 34299244Sjilles#define AC97_REG_RESET 0x00 35299244Sjilles#define AC97_MIX_MASTER 0x02 36299244Sjilles#define AC97_MIX_PHONES 0x04 37299244Sjilles#define AC97_MIX_MONO 0x06 38299244Sjilles#define AC97_MIX_TONE 0x08 39299244Sjilles#define AC97_MIX_BEEP 0x0a 40299244Sjilles#define AC97_MIX_PHONE 0x0c 41299244Sjilles#define AC97_MIX_MIC 0x0e 42299244Sjilles#define AC97_MIX_LINE 0x10 43299244Sjilles#define AC97_MIX_CD 0x12 44299244Sjilles#define AC97_MIX_VIDEO 0x14 45299244Sjilles#define AC97_MIX_AUX 0x16 46299244Sjilles#define AC97_MIX_PCM 0x18 47299244Sjilles#define AC97_REG_RECSEL 0x1a 48299244Sjilles#define AC97_MIX_RGAIN 0x1c 49299244Sjilles#define AC97_MIX_MGAIN 0x1e 50299244Sjilles#define AC97_REG_GEN 0x20 51299244Sjilles#define AC97_REG_3D 0x22 52299244Sjilles#define AC97_REG_POWER 0x26 53299244Sjilles#define AC97_REG_ID1 0x7c 54299244Sjilles#define AC97_REG_ID2 0x7e 55299244Sjilles 56299244Sjillesstruct ac97mixtable_entry { 57299244Sjilles int reg:8; 58299244Sjilles unsigned bits:4; 59299244Sjilles unsigned ofs:4; 60299244Sjilles unsigned stereo:1; 61299244Sjilles unsigned mute:1; 62299244Sjilles unsigned recidx:4; 63299244Sjilles unsigned mask:1; 64299244Sjilles}; 65299244Sjilles 66299244Sjillesstruct ac97_info { 67301765Sjilles ac97_read *read; 68301765Sjilles ac97_write *write; 69301765Sjilles void *devinfo; 70301765Sjilles char id[4]; 71301765Sjilles char rev; 72301765Sjilles unsigned caps, se; 73299244Sjilles struct ac97mixtable_entry mix[32]; 74299244Sjilles}; 75299244Sjilles 76299244Sjillesstruct ac97_codecid { 77299244Sjilles u_int32_t id; 78299244Sjilles char *name; 79299244Sjilles}; 80299244Sjilles 81299244Sjillesstatic const struct ac97mixtable_entry ac97mixtable_default[32] = { 82299244Sjilles [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0 }, 83299244Sjilles [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1 }, 84299244Sjilles [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1 }, 85299244Sjilles [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0 }, 86299244Sjilles [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0 }, 87299244Sjilles [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0 }, 88299244Sjilles [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0 }, 89299244Sjilles [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0 }, 90299244Sjilles [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0 }, 91299244Sjilles [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0 }, 92299244Sjilles [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0 } 93299244Sjilles}; 94299244Sjilles 95299244Sjillesstatic const unsigned ac97mixdevs = 96299244Sjilles SOUND_MASK_VOLUME | 97299244Sjilles SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | 98299244Sjilles SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 | 99299244Sjilles SOUND_MASK_VIDEO | SOUND_MASK_RECLEV; 100299244Sjilles 101299244Sjillesstatic const unsigned ac97recdevs = 102299244Sjilles SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC | 103299244Sjilles SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO; 104299244Sjilles 105299244Sjillesstatic struct ac97_codecid ac97codecid[] = { 106299244Sjilles { 0x414B4D00, "Asahi Kasei AK4540" }, 107299244Sjilles { 0x43525900, "Cirrus Logic CS4297" }, 108299244Sjilles { 0x83847600, "SigmaTel STAC????" }, 109299244Sjilles { 0x83847604, "SigmaTel STAC9701/3/4/5" }, 110299244Sjilles { 0x83847605, "SigmaTel STAC9704" }, 111299244Sjilles { 0x83847608, "SigmaTel STAC9708" }, 112299244Sjilles { 0x83847609, "SigmaTel STAC9721" }, 113299244Sjilles { 0, NULL } 114299244Sjilles}; 115299244Sjilles 116299244Sjillesstatic char *ac97enhancement[] = { 117299244Sjilles "", 118299244Sjilles "Analog Devices Phat Stereo", 119299244Sjilles "Creative Stereo Enhancement", 120299244Sjilles "National Semi 3D Stereo Enhancement", 121299244Sjilles "Yamaha Ymersion", 122299244Sjilles "BBE 3D Stereo Enhancement", 123299244Sjilles "Crystal Semi 3D Stereo Enhancement", 124299244Sjilles "Qsound QXpander", 125299244Sjilles "Spatializer 3D Stereo Enhancement", 126299244Sjilles "SRS 3D Stereo Enhancement", 127299244Sjilles "Platform Tech 3D Stereo Enhancement", 128299244Sjilles "AKM 3D Audio", 129299244Sjilles "Aureal Stereo Enhancement", 130299244Sjilles "Aztech 3D Enhancement", 131299244Sjilles "Binaura 3D Audio Enhancement", 132299244Sjilles "ESS Technology Stereo Enhancement", 133299244Sjilles "Harman International VMAx", 134299244Sjilles "Nvidea 3D Stereo Enhancement", 135299244Sjilles "Philips Incredible Sound", 136299244Sjilles "Texas Instruments 3D Stereo Enhancement", 137299244Sjilles "VLSI Technology 3D Stereo Enhancement", 138299244Sjilles "TriTech 3D Stereo Enhancement", 139299244Sjilles "Realtek 3D Stereo Enhancement", 140299244Sjilles "Samsung 3D Stereo Enhancement", 141299244Sjilles "Wolfson Microelectronics 3D Enhancement", 142299244Sjilles "Delta Integration 3D Enhancement", 143299244Sjilles "SigmaTel 3D Enhancement", 144299244Sjilles "Reserved 27", 145299244Sjilles "Rockwell 3D Stereo Enhancement", 146299244Sjilles "Reserved 29", 147299244Sjilles "Reserved 30", 148299244Sjilles "Reserved 31" 149299244Sjilles}; 150299244Sjilles 151299244Sjillesstatic char *ac97feature[] = { 152299244Sjilles "mic channel", 153299244Sjilles "reserved", 154299244Sjilles "tone", 155299244Sjilles "simulated stereo", 156299244Sjilles "headphone", 157299244Sjilles "bass boost", 158299244Sjilles "18 bit DAC", 159299244Sjilles "20 bit DAC", 160299244Sjilles "18 bit ADC", 161299244Sjilles "20 bit ADC" 162299244Sjilles}; 163299244Sjilles 164299244Sjillesstatic int 165299244Sjillesac97_setrecsrc(struct ac97_info *codec, int channel) 166299244Sjilles{ 167299244Sjilles struct ac97mixtable_entry *e = &codec->mix[channel]; 168299244Sjilles if (e->recidx > 0) { 169299244Sjilles int val = e->recidx - 1; 170299244Sjilles val |= val << 8; 171299244Sjilles codec->write(codec->devinfo, AC97_REG_RECSEL, val); 172299244Sjilles return 0; 173299244Sjilles } else return -1; 174299244Sjilles} 175299244Sjilles 176299244Sjillesstatic int 177299244Sjillesac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 178299244Sjilles{ 179299244Sjilles struct ac97mixtable_entry *e = &codec->mix[channel]; 180299244Sjilles if (e->reg != 0) { 181299244Sjilles int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 182299244Sjilles 183299244Sjilles if (!e->stereo) right = left; 184299244Sjilles if (e->reg > 0) { 185299244Sjilles left = 100 - left; 186299244Sjilles right = 100 - right; 187299244Sjilles } 188299244Sjilles 189299244Sjilles max = (1 << e->bits) - 1; 190299244Sjilles left = (left * max) / 100; 191299244Sjilles right = (right * max) / 100; 192299244Sjilles 193299244Sjilles val = (left << 8) | right; 194299244Sjilles 195299244Sjilles left = (left * 100) / max; 196299244Sjilles right = (right * 100) / max; 197299244Sjilles 198299244Sjilles if (e->reg > 0) { 199299244Sjilles left = 100 - left; 200299244Sjilles right = 100 - right; 201299244Sjilles } 202299244Sjilles 203299244Sjilles if (!e->stereo) { 204299244Sjilles val &= max; 205299244Sjilles val <<= e->ofs; 206299244Sjilles if (e->mask) { 207299244Sjilles int cur = codec->read(codec->devinfo, e->reg); 208299244Sjilles val |= cur & ~(max << e->ofs); 209299244Sjilles } 210299244Sjilles } 211299244Sjilles if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE; 212299244Sjilles codec->write(codec->devinfo, reg, val); 213299244Sjilles return left | (right << 8); 214299244Sjilles } else return -1; 215299244Sjilles} 216299244Sjilles 217299244Sjilles#if 0 218299244Sjillesstatic int 219299244Sjillesac97_getmixer(struct ac97_info *codec, int channel) 220299244Sjilles{ 221299244Sjilles struct ac97mixtable_entry *e = &codec->mix[channel]; 222299244Sjilles if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 223299244Sjilles int max, val, volume; 224299244Sjilles 225299244Sjilles max = (1 << e->bits) - 1; 226299244Sjilles val = codec->read(codec->devinfo, e->reg); 227299244Sjilles if (val == AC97_MUTE && e->mute == 1) volume = 0; 228299244Sjilles else { 229299244Sjilles if (e->stereo == 0) val >>= e->ofs; 230299244Sjilles val &= max; 231299244Sjilles volume = (val * 100) / max; 232299244Sjilles if (e->reg > 0) volume = 100 - volume; 233299244Sjilles } 234299244Sjilles return volume; 235299244Sjilles } else return -1; 236299244Sjilles} 237299244Sjilles#endif 238299244Sjilles 239299244Sjillesstatic unsigned 240299244Sjillesac97_init(struct ac97_info *codec) 241299244Sjilles{ 242299244Sjilles unsigned i, j; 243299244Sjilles u_int32_t id; 244299244Sjilles 245299244Sjilles for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i]; 246299244Sjilles 247299244Sjilles codec->write(codec->devinfo, AC97_REG_POWER, 0); 248299244Sjilles codec->write(codec->devinfo, AC97_REG_RESET, 0); 249299244Sjilles DELAY(10000); 250299244Sjilles 251299244Sjilles i = codec->read(codec->devinfo, AC97_REG_RESET); 252299244Sjilles codec->caps = i & 0x03ff; 253299244Sjilles codec->se = (i & 0x7c00) >> 10; 254299244Sjilles 255299244Sjilles id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) | 256299244Sjilles codec->read(codec->devinfo, AC97_REG_ID2); 257299244Sjilles codec->rev = id & 0x000000ff; 258299244Sjilles 259299244Sjilles codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20); 260299244Sjilles if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20) 261299244Sjilles codec->mix[SOUND_MIXER_VOLUME].bits++; 262299244Sjilles codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00); 263299244Sjilles 264299244Sjilles if (bootverbose) { 265299244Sjilles printf("ac97: codec id 0x%8x", id); 266299244Sjilles for (i = 0; ac97codecid[i].id; i++) { 267299244Sjilles if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name); 268299244Sjilles } 269299244Sjilles printf("\nac97: codec features "); 270299244Sjilles for (i = j = 0; i < 10; i++) { 271299244Sjilles if (codec->caps & (1 << i)) { 272299244Sjilles printf("%s%s", j? ", " : "", ac97feature[i]); 273299244Sjilles j++; 274299244Sjilles } 275299244Sjilles } 276299244Sjilles printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 277299244Sjilles printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 278299244Sjilles } 279299244Sjilles 280299244Sjilles if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0) 281299244Sjilles printf("ac97: dac not ready\n"); 282299244Sjilles return 0; 283299244Sjilles} 284299244Sjilles 285299244Sjillesstruct ac97_info * 286299244Sjillesac97_create(void *devinfo, ac97_read *rd, ac97_write *wr) 287299244Sjilles{ 288299244Sjilles struct ac97_info *codec; 289299244Sjilles 290299244Sjilles codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT); 291299244Sjilles if (codec != NULL) { 292299244Sjilles codec->read = rd; 293299244Sjilles codec->write = wr; 294299244Sjilles codec->devinfo = devinfo; 295299244Sjilles } 296299244Sjilles return codec; 297299244Sjilles} 298299244Sjilles 299299244Sjillesstatic int 300299244Sjillesac97mix_init(snd_mixer *m) 301299244Sjilles{ 302299244Sjilles struct ac97_info *codec = mix_getdevinfo(m); 303299244Sjilles if (codec == NULL) return -1; 304299244Sjilles ac97_init(codec); 305299244Sjilles mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0)); 306299244Sjilles mix_setrecdevs(m, ac97recdevs); 307299244Sjilles return 0; 308299244Sjilles} 309299244Sjilles 310299244Sjillesstatic int 311299244Sjillesac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 312299244Sjilles{ 313299244Sjilles struct ac97_info *codec = mix_getdevinfo(m); 314299244Sjilles if (codec == NULL) return -1; 315299244Sjilles return ac97_setmixer(codec, dev, left, right); 316301765Sjilles} 317299244Sjilles 318299244Sjillesstatic int 319299244Sjillesac97mix_setrecsrc(snd_mixer *m, u_int32_t src) 320299244Sjilles{ 321299244Sjilles int i; 322299244Sjilles struct ac97_info *codec = mix_getdevinfo(m); 323299244Sjilles if (codec == NULL) return -1; 324299244Sjilles for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 325299244Sjilles if ((src & (1 << i)) != 0) break; 326299244Sjilles return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 327299244Sjilles} 328299244Sjilles 329299244Sjillessnd_mixer ac97_mixer = { 330299244Sjilles "AC97 mixer", 331299244Sjilles ac97mix_init, 332299244Sjilles ac97mix_set, 333299244Sjilles ac97mix_setrecsrc, 334299244Sjilles}; 335299244Sjilles 336299244Sjilles