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