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