ac97.c revision 70324
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 70324 2000-12-24 03:33:21Z 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 codec->extcaps = 0; 324 codec->extid = 0; 325 codec->extstat = 0; 326 if (!codec->noext) { 327 i = rdcd(codec, AC97_REGEXT_ID); 328 if (i != 0xffff) { 329 codec->extcaps = i & 0x3fff; 330 codec->extid = (i & 0xc000) >> 14; 331 codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 332 } 333 } 334 335 for (i = 0; i < 32; i++) { 336 k = codec->noext? codec->mix[i].enable : 1; 337 if (k && (codec->mix[i].reg > 0)) { 338 old = rdcd(codec, codec->mix[i].reg); 339 wrcd(codec, codec->mix[i].reg, 0x3f); 340 j = rdcd(codec, codec->mix[i].reg); 341 wrcd(codec, codec->mix[i].reg, old); 342 codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 343 for (k = 1; j & (1 << k); k++); 344 codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 345 } 346 /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 347 } 348 349 if (bootverbose) { 350 device_printf(codec->dev, "ac97 codec id 0x%08x", id); 351 if (codec->name) 352 printf(" (%s)", codec->name); 353 printf("\n"); 354 device_printf(codec->dev, "ac97 codec features "); 355 for (i = j = 0; i < 10; i++) 356 if (codec->caps & (1 << i)) 357 printf("%s%s", j++? ", " : "", ac97feature[i]); 358 printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 359 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 360 361 if (codec->extcaps != 0 || codec->extid) { 362 device_printf(codec->dev, "ac97 %s codec", 363 codec->extid? "secondary" : "primary"); 364 if (codec->extcaps) 365 printf(" extended features "); 366 for (i = j = 0; i < 14; i++) 367 if (codec->extcaps & (1 << i)) 368 printf("%s%s", j++? ", " : "", ac97extfeature[i]); 369 printf("\n"); 370 } 371 } 372 373 if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 374 device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 375 return 0; 376} 377 378static unsigned 379ac97_reinitmixer(struct ac97_info *codec) 380{ 381 unsigned i; 382 383 codec->count = AC97_INIT(codec->methods, codec->devinfo); 384 if (codec->count == 0) { 385 device_printf(codec->dev, "ac97 codec init failed\n"); 386 return ENODEV; 387 } 388 389 wrcd(codec, AC97_REG_POWER, 0); 390 wrcd(codec, AC97_REG_RESET, 0); 391 DELAY(100000); 392 i = rdcd(codec, AC97_REG_RESET); 393 394 if (!codec->noext) { 395 wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 396 if (rdcd(codec, AC97_REGEXT_STAT) != codec->extstat) 397 device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 398 codec->extstat, rdcd(codec, AC97_REGEXT_STAT)); 399 } 400 401 if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 402 device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 403 return 0; 404} 405 406struct ac97_info * 407ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 408{ 409 struct ac97_info *codec; 410 411 codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 412 if (codec == NULL) 413 return NULL; 414 415 codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 416 if (codec->methods == NULL) { 417 free(codec, M_AC97); 418 return NULL; 419 } 420 421 codec->dev = dev; 422 codec->devinfo = devinfo; 423 return codec; 424} 425 426void 427ac97_destroy(struct ac97_info *codec) 428{ 429 if (codec->methods != NULL) 430 kobj_delete(codec->methods, M_AC97); 431 free(codec, M_AC97); 432} 433 434/* -------------------------------------------------------------------- */ 435 436static int 437ac97mix_init(snd_mixer *m) 438{ 439 struct ac97_info *codec = mix_getdevinfo(m); 440 u_int32_t i, mask; 441 442 if (codec == NULL) 443 return -1; 444 445 if (ac97_initmixer(codec)) 446 return -1; 447 448 mask = 0; 449 for (i = 0; i < 32; i++) 450 mask |= codec->mix[i].enable? 1 << i : 0; 451 mix_setdevs(m, mask); 452 453 mask = 0; 454 for (i = 0; i < 32; i++) 455 mask |= codec->mix[i].recidx? 1 << i : 0; 456 mix_setrecdevs(m, mask); 457 return 0; 458} 459 460static int 461ac97mix_uninit(snd_mixer *m) 462{ 463 struct ac97_info *codec = mix_getdevinfo(m); 464 465 if (codec == NULL) 466 return -1; 467 /* 468 if (ac97_uninitmixer(codec)) 469 return -1; 470 */ 471 ac97_destroy(codec); 472 return 0; 473} 474 475static int 476ac97mix_reinit(snd_mixer *m) 477{ 478 struct ac97_info *codec = mix_getdevinfo(m); 479 480 if (codec == NULL) 481 return -1; 482 return ac97_reinitmixer(codec); 483} 484 485static int 486ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 487{ 488 struct ac97_info *codec = mix_getdevinfo(m); 489 490 if (codec == NULL) 491 return -1; 492 return ac97_setmixer(codec, dev, left, right); 493} 494 495static int 496ac97mix_setrecsrc(snd_mixer *m, u_int32_t src) 497{ 498 int i; 499 struct ac97_info *codec = mix_getdevinfo(m); 500 501 if (codec == NULL) 502 return -1; 503 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 504 if ((src & (1 << i)) != 0) 505 break; 506 return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 507} 508 509static kobj_method_t ac97mixer_methods[] = { 510 KOBJMETHOD(mixer_init, ac97mix_init), 511 KOBJMETHOD(mixer_uninit, ac97mix_uninit), 512 KOBJMETHOD(mixer_reinit, ac97mix_reinit), 513 KOBJMETHOD(mixer_set, ac97mix_set), 514 KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 515 { 0, 0 } 516}; 517MIXER_DECLARE(ac97mixer); 518 519/* -------------------------------------------------------------------- */ 520 521kobj_class_t 522ac97_getmixerclass(void) 523{ 524 return &ac97mixer_class; 525} 526 527 528