1 /* 2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <dev/sound/pcm/sound.h> 30 #include <dev/sound/pcm/ac97.h> 31 32 #define AC97_MUTE 0x8000 33 34 #define AC97_REG_RESET 0x00 35 #define AC97_MIX_MASTER 0x02 36 #define AC97_MIX_PHONES 0x04 37 #define AC97_MIX_MONO 0x06 38 #define AC97_MIX_TONE 0x08 39 #define AC97_MIX_BEEP 0x0a 40 #define AC97_MIX_PHONE 0x0c 41 #define AC97_MIX_MIC 0x0e 42 #define AC97_MIX_LINE 0x10 43 #define AC97_MIX_CD 0x12 44 #define AC97_MIX_VIDEO 0x14 45 #define AC97_MIX_AUX 0x16 46 #define AC97_MIX_PCM 0x18 47 #define AC97_REG_RECSEL 0x1a 48 #define AC97_MIX_RGAIN 0x1c 49 #define AC97_MIX_MGAIN 0x1e 50 #define AC97_REG_GEN 0x20 51 #define AC97_REG_3D 0x22 52 #define AC97_REG_POWER 0x26 53 #define AC97_REG_ID1 0x7c 54 #define AC97_REG_ID2 0x7e 55 56 struct ac97mixtable_entry { 57 int reg:8; 58 unsigned bits:4; 59 unsigned ofs:4; 60 unsigned stereo:1; 61 unsigned mute:1; 62 unsigned recidx:4; 63 unsigned mask:1; 64 }; 65 66 struct ac97_info { 67 device_t dev; 68 ac97_read *read; 69 ac97_write *write; 70 void *devinfo; 71 char id[4]; 72 char rev; 73 unsigned caps, se; 74 struct ac97mixtable_entry mix[32]; 75 }; 76 77 struct ac97_codecid { 78 u_int32_t id; 79 char *name; 80 }; 81 82 static const struct ac97mixtable_entry ac97mixtable_default[32] = { 83 [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0 }, 84 [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1 }, 85 [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1 }, 86 [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0 }, 87 [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0 }, 88 [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0 }, 89 [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0 }, 90 [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0 }, 91 [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0 }, 92 [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0 }, 93 [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0 } 94 }; 95 96 static const unsigned ac97mixdevs = 97 SOUND_MASK_VOLUME | 98 SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | 99 SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 | 100 SOUND_MASK_VIDEO | SOUND_MASK_RECLEV; 101 102 static const unsigned ac97recdevs = 103 SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC | 104 SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO; 105 106 static struct ac97_codecid ac97codecid[] = { 107 { 0x414B4D00, "Asahi Kasei AK4540" }, 108 { 0x43525900, "Cirrus Logic CS4297" }, 109 { 0x83847600, "SigmaTel STAC????" }, 110 { 0x83847604, "SigmaTel STAC9701/3/4/5" }, 111 { 0x83847605, "SigmaTel STAC9704" }, 112 { 0x83847608, "SigmaTel STAC9708" }, 113 { 0x83847609, "SigmaTel STAC9721" }, 114 { 0, NULL } 115 }; 116 117 static char *ac97enhancement[] = { 118 "", 119 "Analog Devices Phat Stereo", 120 "Creative Stereo Enhancement", 121 "National Semi 3D Stereo Enhancement", 122 "Yamaha Ymersion", 123 "BBE 3D Stereo Enhancement", 124 "Crystal Semi 3D Stereo Enhancement", 125 "Qsound QXpander", 126 "Spatializer 3D Stereo Enhancement", 127 "SRS 3D Stereo Enhancement", 128 "Platform Tech 3D Stereo Enhancement", 129 "AKM 3D Audio", 130 "Aureal Stereo Enhancement", 131 "Aztech 3D Enhancement", 132 "Binaura 3D Audio Enhancement", 133 "ESS Technology Stereo Enhancement", 134 "Harman International VMAx", 135 "Nvidea 3D Stereo Enhancement", 136 "Philips Incredible Sound", 137 "Texas Instruments 3D Stereo Enhancement", 138 "VLSI Technology 3D Stereo Enhancement", 139 "TriTech 3D Stereo Enhancement", 140 "Realtek 3D Stereo Enhancement", 141 "Samsung 3D Stereo Enhancement", 142 "Wolfson Microelectronics 3D Enhancement", 143 "Delta Integration 3D Enhancement", 144 "SigmaTel 3D Enhancement", 145 "Reserved 27", 146 "Rockwell 3D Stereo Enhancement", 147 "Reserved 29", 148 "Reserved 30", 149 "Reserved 31" 150 }; 151 152 static char *ac97feature[] = { 153 "mic channel", 154 "reserved", 155 "tone", 156 "simulated stereo", 157 "headphone", 158 "bass boost", 159 "18 bit DAC", 160 "20 bit DAC", 161 "18 bit ADC", 162 "20 bit ADC" 163 }; 164 165 static int 166 ac97_setrecsrc(struct ac97_info *codec, int channel) 167 { 168 struct ac97mixtable_entry *e = &codec->mix[channel]; 169 if (e->recidx > 0) { 170 int val = e->recidx - 1; 171 val |= val << 8; 172 codec->write(codec->devinfo, AC97_REG_RECSEL, val); 173 return 0; 174 } else return -1; 175 } 176 177 static int 178 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 179 { 180 struct ac97mixtable_entry *e = &codec->mix[channel]; 181 if (e->reg != 0) { 182 int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 183 184 if (!e->stereo) right = left; 185 if (e->reg > 0) { 186 left = 100 - left; 187 right = 100 - right; 188 } 189 190 max = (1 << e->bits) - 1; 191 left = (left * max) / 100; 192 right = (right * max) / 100; 193 194 val = (left << 8) | right; 195 196 left = (left * 100) / max; 197 right = (right * 100) / max; 198 199 if (e->reg > 0) { 200 left = 100 - left; 201 right = 100 - right; 202 } 203 204 if (!e->stereo) { 205 val &= max; 206 val <<= e->ofs; 207 if (e->mask) { 208 int cur = codec->read(codec->devinfo, e->reg); 209 val |= cur & ~(max << e->ofs); 210 } 211 } 212 if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE; 213 codec->write(codec->devinfo, reg, val); 214 return left | (right << 8); 215 } else return -1; 216 } 217 218 #if 0 219 static int 220 ac97_getmixer(struct ac97_info *codec, int channel) 221 { 222 struct ac97mixtable_entry *e = &codec->mix[channel]; 223 if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 224 int max, val, volume; 225 226 max = (1 << e->bits) - 1; 227 val = codec->read(codec->devinfo, e->reg); 228 if (val == AC97_MUTE && e->mute == 1) volume = 0; 229 else { 230 if (e->stereo == 0) val >>= e->ofs; 231 val &= max; 232 volume = (val * 100) / max; 233 if (e->reg > 0) volume = 100 - volume; 234 } 235 return volume; 236 } else return -1; 237 } 238 #endif 239 240 static unsigned 241 ac97_init(struct ac97_info *codec) 242 { 243 unsigned i, j; 244 u_int32_t id; 245 246 for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i]; 247 248 codec->write(codec->devinfo, AC97_REG_POWER, 0); 249 codec->write(codec->devinfo, AC97_REG_RESET, 0); 250 DELAY(10000); 251 252 i = codec->read(codec->devinfo, AC97_REG_RESET); 253 codec->caps = i & 0x03ff; 254 codec->se = (i & 0x7c00) >> 10; 255 256 id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) | 257 codec->read(codec->devinfo, AC97_REG_ID2); 258 codec->rev = id & 0x000000ff; 259 260 codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20); 261 if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20) 262 codec->mix[SOUND_MIXER_VOLUME].bits++; 263 codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00); 264 265 if (bootverbose) { 266 device_printf(codec->dev, "ac97 codec id 0x%8x", id); 267 for (i = 0; ac97codecid[i].id; i++) { 268 if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name); 269 } 270 printf("\n"); 271 device_printf(codec->dev, "ac97 codec features "); 272 for (i = j = 0; i < 10; i++) { 273 if (codec->caps & (1 << i)) { 274 printf("%s%s", j? ", " : "", ac97feature[i]); 275 j++; 276 } 277 } 278 printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 279 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 280 } 281 282 if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0) 283 device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 284 return 0; 285 } 286 287 struct ac97_info * 288 ac97_create(device_t dev, void *devinfo, ac97_read *rd, ac97_write *wr) 289 { 290 struct ac97_info *codec; 291 292 codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT); 293 if (codec != NULL) { 294 codec->dev = dev; 295 codec->read = rd; 296 codec->write = wr; 297 codec->devinfo = devinfo; 298 } 299 return codec; 300 } 301 302 static int 303 ac97mix_init(snd_mixer *m) 304 { 305 struct ac97_info *codec = mix_getdevinfo(m); 306 if (codec == NULL) return -1; 307 ac97_init(codec); 308 mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0)); 309 mix_setrecdevs(m, ac97recdevs); 310 return 0; 311 } 312 313 static int 314 ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 315 { 316 struct ac97_info *codec = mix_getdevinfo(m); 317 if (codec == NULL) return -1; 318 return ac97_setmixer(codec, dev, left, right); 319 } 320 321 static int 322 ac97mix_setrecsrc(snd_mixer *m, u_int32_t src) 323 { 324 int i; 325 struct ac97_info *codec = mix_getdevinfo(m); 326 if (codec == NULL) return -1; 327 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 328 if ((src & (1 << i)) != 0) break; 329 return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 330 } 331 332 snd_mixer ac97_mixer = { 333 "AC97 mixer", 334 ac97mix_init, 335 ac97mix_set, 336 ac97mix_setrecsrc, 337 }; 338 339