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 struct ac97mixtable_entry { 33 int reg:8; 34 unsigned bits:4; 35 unsigned ofs:4; 36 unsigned stereo:1; 37 unsigned mute:1; 38 unsigned recidx:4; 39 unsigned mask:1; 40 }; 41 42 struct ac97_info { 43 device_t dev; 44 ac97_init *init; 45 ac97_read *read; 46 ac97_write *write; 47 void *devinfo; 48 char id[4]; 49 char rev; 50 unsigned caps, se, extcaps, extid, extstat, noext:1; 51 struct ac97mixtable_entry mix[32]; 52 }; 53 54 struct ac97_codecid { 55 u_int32_t id, noext:1; 56 char *name; 57 }; 58 59 static const struct ac97mixtable_entry ac97mixtable_default[32] = { 60 [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0 }, 61 [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1 }, 62 [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1 }, 63 [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0 }, 64 [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0 }, 65 [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0 }, 66 [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0 }, 67 [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0 }, 68 [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0 }, 69 [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0 }, 70 [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0 } 71 }; 72 73 static const unsigned ac97mixdevs = 74 SOUND_MASK_VOLUME | 75 SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | 76 SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 | 77 SOUND_MASK_VIDEO | SOUND_MASK_RECLEV; 78 79 static const unsigned ac97recdevs = 80 SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC | 81 SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO; 82 83 static struct ac97_codecid ac97codecid[] = { 84 { 0x414b4d00, 1, "Asahi Kasei AK4540 rev 0" }, 85 { 0x43525900, 0, "Cirrus Logic CS4297" }, 86 { 0x83847600, 0, "SigmaTel STAC????" }, 87 { 0x83847604, 0, "SigmaTel STAC9701/3/4/5" }, 88 { 0x83847605, 0, "SigmaTel STAC9704" }, 89 { 0x83847608, 0, "SigmaTel STAC9708" }, 90 { 0x83847609, 0, "SigmaTel STAC9721" }, 91 { 0x414b4d01, 1, "Asahi Kasei AK4540 rev 1" }, 92 { 0, 0, NULL } 93 }; 94 95 static char *ac97enhancement[] = { 96 "", 97 "Analog Devices Phat Stereo", 98 "Creative Stereo Enhancement", 99 "National Semi 3D Stereo Enhancement", 100 "Yamaha Ymersion", 101 "BBE 3D Stereo Enhancement", 102 "Crystal Semi 3D Stereo Enhancement", 103 "Qsound QXpander", 104 "Spatializer 3D Stereo Enhancement", 105 "SRS 3D Stereo Enhancement", 106 "Platform Tech 3D Stereo Enhancement", 107 "AKM 3D Audio", 108 "Aureal Stereo Enhancement", 109 "Aztech 3D Enhancement", 110 "Binaura 3D Audio Enhancement", 111 "ESS Technology Stereo Enhancement", 112 "Harman International VMAx", 113 "Nvidea 3D Stereo Enhancement", 114 "Philips Incredible Sound", 115 "Texas Instruments 3D Stereo Enhancement", 116 "VLSI Technology 3D Stereo Enhancement", 117 "TriTech 3D Stereo Enhancement", 118 "Realtek 3D Stereo Enhancement", 119 "Samsung 3D Stereo Enhancement", 120 "Wolfson Microelectronics 3D Enhancement", 121 "Delta Integration 3D Enhancement", 122 "SigmaTel 3D Enhancement", 123 "Reserved 27", 124 "Rockwell 3D Stereo Enhancement", 125 "Reserved 29", 126 "Reserved 30", 127 "Reserved 31" 128 }; 129 130 static char *ac97feature[] = { 131 "mic channel", 132 "reserved", 133 "tone", 134 "simulated stereo", 135 "headphone", 136 "bass boost", 137 "18 bit DAC", 138 "20 bit DAC", 139 "18 bit ADC", 140 "20 bit ADC" 141 }; 142 143 static char *ac97extfeature[] = { 144 "variable rate PCM", 145 "double rate PCM", 146 "reserved 1", 147 "variable rate mic", 148 "reserved 2", 149 "reserved 3", 150 "center DAC", 151 "surround DAC", 152 "LFE DAC", 153 "AMAP", 154 "reserved 4", 155 "reserved 5", 156 "reserved 6", 157 "reserved 7", 158 }; 159 160 static u_int16_t 161 rdcd(struct ac97_info *codec, int reg) 162 { 163 return codec->read(codec->devinfo, reg); 164 } 165 166 static void 167 wrcd(struct ac97_info *codec, int reg, u_int16_t val) 168 { 169 codec->write(codec->devinfo, reg, val); 170 } 171 172 int 173 ac97_setrate(struct ac97_info *codec, int which, int rate) 174 { 175 u_int16_t v; 176 177 switch(which) { 178 case AC97_REGEXT_FDACRATE: 179 case AC97_REGEXT_SDACRATE: 180 case AC97_REGEXT_LDACRATE: 181 case AC97_REGEXT_LADCRATE: 182 case AC97_REGEXT_MADCRATE: 183 break; 184 185 default: 186 return -1; 187 } 188 189 if (rate != 0) { 190 v = rate; 191 if (codec->extstat & AC97_EXTCAP_DRA) 192 v >>= 1; 193 wrcd(codec, which, v); 194 } 195 v = rdcd(codec, which); 196 if (codec->extstat & AC97_EXTCAP_DRA) 197 v <<= 1; 198 return v; 199 } 200 201 int 202 ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 203 { 204 mode &= AC97_EXTCAPS; 205 if ((mode & ~codec->extcaps) != 0) 206 return -1; 207 wrcd(codec, AC97_REGEXT_STAT, mode); 208 codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 209 return (mode == codec->extstat)? 0 : -1; 210 } 211 212 static int 213 ac97_setrecsrc(struct ac97_info *codec, int channel) 214 { 215 struct ac97mixtable_entry *e = &codec->mix[channel]; 216 if (e->recidx > 0) { 217 int val = e->recidx - 1; 218 val |= val << 8; 219 wrcd(codec, AC97_REG_RECSEL, val); 220 return 0; 221 } else 222 return -1; 223 } 224 225 static int 226 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 227 { 228 struct ac97mixtable_entry *e = &codec->mix[channel]; 229 if (e->reg != 0) { 230 int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 231 232 if (!e->stereo) 233 right = left; 234 if (e->reg > 0) { 235 left = 100 - left; 236 right = 100 - right; 237 } 238 239 max = (1 << e->bits) - 1; 240 left = (left * max) / 100; 241 right = (right * max) / 100; 242 243 val = (left << 8) | right; 244 245 left = (left * 100) / max; 246 right = (right * 100) / max; 247 248 if (e->reg > 0) { 249 left = 100 - left; 250 right = 100 - right; 251 } 252 253 if (!e->stereo) { 254 val &= max; 255 val <<= e->ofs; 256 if (e->mask) { 257 int cur = rdcd(codec, e->reg); 258 val |= cur & ~(max << e->ofs); 259 } 260 } 261 if (left == 0 && right == 0 && e->mute == 1) 262 val = AC97_MUTE; 263 wrcd(codec, reg, val); 264 return left | (right << 8); 265 } else 266 return -1; 267 } 268 269 #if 0 270 static int 271 ac97_getmixer(struct ac97_info *codec, int channel) 272 { 273 struct ac97mixtable_entry *e = &codec->mix[channel]; 274 if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 275 int max, val, volume; 276 277 max = (1 << e->bits) - 1; 278 val = rdcd(code, e->reg); 279 if (val == AC97_MUTE && e->mute == 1) 280 volume = 0; 281 else { 282 if (e->stereo == 0) val >>= e->ofs; 283 val &= max; 284 volume = (val * 100) / max; 285 if (e->reg > 0) volume = 100 - volume; 286 } 287 return volume; 288 } else 289 return -1; 290 } 291 #endif 292 293 static unsigned 294 ac97_initmixer(struct ac97_info *codec) 295 { 296 unsigned i, j; 297 u_int32_t id; 298 299 for (i = 0; i < 32; i++) 300 codec->mix[i] = ac97mixtable_default[i]; 301 302 if (codec->init) 303 codec->init(codec->devinfo); 304 wrcd(codec, AC97_REG_POWER, 0); 305 wrcd(codec, AC97_REG_RESET, 0); 306 DELAY(100000); 307 308 i = rdcd(codec, AC97_REG_RESET); 309 codec->caps = i & 0x03ff; 310 codec->se = (i & 0x7c00) >> 10; 311 312 id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2); 313 codec->rev = id & 0x000000ff; 314 if (id == 0 || id == 0xffffffff) { 315 device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 316 return ENODEV; 317 } 318 319 for (i = 0; ac97codecid[i].id; i++) 320 if (ac97codecid[i].id == id) 321 codec->noext = 1; 322 323 if (!codec->noext) { 324 i = rdcd(codec, AC97_REGEXT_ID); 325 codec->extcaps = i & 0x3fff; 326 codec->extid = (i & 0xc000) >> 14; 327 codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 328 } else { 329 codec->extcaps = 0; 330 codec->extid = 0; 331 codec->extstat = 0; 332 } 333 334 wrcd(codec, AC97_MIX_MASTER, 0x20); 335 if ((rdcd(codec, AC97_MIX_MASTER) & 0x20) == 0x20) 336 codec->mix[SOUND_MIXER_VOLUME].bits++; 337 wrcd(codec, AC97_MIX_MASTER, 0x00); 338 339 if (bootverbose) { 340 device_printf(codec->dev, "ac97 codec id 0x%08x", id); 341 for (i = 0; ac97codecid[i].id; i++) 342 if (ac97codecid[i].id == id) 343 printf(" (%s)", ac97codecid[i].name); 344 printf("\n"); 345 device_printf(codec->dev, "ac97 codec features "); 346 for (i = j = 0; i < 10; i++) 347 if (codec->caps & (1 << i)) 348 printf("%s%s", j++? ", " : "", ac97feature[i]); 349 printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 350 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 351 352 if (codec->extcaps != 0 || codec->extid) { 353 device_printf(codec->dev, "ac97 %s codec", 354 codec->extid? "secondary" : "primary"); 355 if (codec->extcaps) 356 printf(" extended features "); 357 for (i = j = 0; i < 14; i++) 358 if (codec->extcaps & (1 << i)) 359 printf("%s%s", j++? ", " : "", ac97extfeature[i]); 360 printf("\n"); 361 } 362 } 363 364 if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 365 device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 366 return 0; 367 } 368 369 struct ac97_info * 370 ac97_create(device_t dev, void *devinfo, ac97_init *init, ac97_read *rd, ac97_write *wr) 371 { 372 struct ac97_info *codec; 373 374 codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT); 375 if (codec != NULL) { 376 codec->dev = dev; 377 codec->init = init; 378 codec->read = rd; 379 codec->write = wr; 380 codec->devinfo = devinfo; 381 } 382 return codec; 383 } 384 385 static int 386 ac97mix_init(snd_mixer *m) 387 { 388 struct ac97_info *codec = mix_getdevinfo(m); 389 if (codec == NULL) 390 return -1; 391 if (ac97_initmixer(codec)) 392 return -1; 393 mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0)); 394 mix_setrecdevs(m, ac97recdevs); 395 return 0; 396 } 397 398 static int 399 ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 400 { 401 struct ac97_info *codec = mix_getdevinfo(m); 402 if (codec == NULL) 403 return -1; 404 return ac97_setmixer(codec, dev, left, right); 405 } 406 407 static int 408 ac97mix_setrecsrc(snd_mixer *m, u_int32_t src) 409 { 410 int i; 411 struct ac97_info *codec = mix_getdevinfo(m); 412 if (codec == NULL) 413 return -1; 414 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 415 if ((src & (1 << i)) != 0) 416 break; 417 return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 418 } 419 420 snd_mixer ac97_mixer = { 421 "AC97 mixer", 422 ac97mix_init, 423 ac97mix_set, 424 ac97mix_setrecsrc, 425 }; 426 427