Annotation of sys/dev/ic/ac97.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ac97.c,v 1.63 2007/07/27 01:48:04 ian Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 1999, 2000 Constantine Sapuntzakis
! 5: *
! 6: * Author: Constantine Sapuntzakis <csapuntz@stanford.edu>
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: * 3. The name of the author may not be used to endorse or promote
! 17: * products derived from this software without specific prior written
! 18: * permission.
! 19: * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
! 20: * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
! 21: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 22: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
! 23: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 24: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
! 25: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
! 26: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
! 27: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 28: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
! 29: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
! 30: * DAMAGE. */
! 31:
! 32: /* Partially inspired by FreeBSD's sys/dev/pcm/ac97.c. It came with
! 33: the following copyright */
! 34:
! 35: /*
! 36: * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
! 37: * All rights reserved.
! 38: *
! 39: * Redistribution and use in source and binary forms, with or without
! 40: * modification, are permitted provided that the following conditions
! 41: * are met:
! 42: * 1. Redistributions of source code must retain the above copyright
! 43: * notice, this list of conditions and the following disclaimer.
! 44: * 2. Redistributions in binary form must reproduce the above copyright
! 45: * notice, this list of conditions and the following disclaimer in the
! 46: * documentation and/or other materials provided with the distribution.
! 47: *
! 48: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 49: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 50: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 51: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 52: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 53: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 54: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 55: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 56: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 57: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 58: * SUCH DAMAGE.
! 59: *
! 60: * $FreeBSD$
! 61: */
! 62:
! 63: #include <sys/param.h>
! 64: #include <sys/systm.h>
! 65: #include <sys/kernel.h>
! 66: #include <sys/malloc.h>
! 67: #include <sys/device.h>
! 68:
! 69: #include <sys/audioio.h>
! 70: #include <dev/audio_if.h>
! 71: #include <dev/ic/ac97.h>
! 72:
! 73: const struct audio_mixer_enum ac97_on_off = {
! 74: 2,
! 75: { { { AudioNoff } , 0 },
! 76: { { AudioNon } , 1 } }
! 77: };
! 78:
! 79: const struct audio_mixer_enum ac97_mic_select = {
! 80: 2,
! 81: { { { AudioNmicrophone "0" }, 0 },
! 82: { { AudioNmicrophone "1" }, 1 } }
! 83: };
! 84:
! 85: const struct audio_mixer_enum ac97_mono_select = {
! 86: 2,
! 87: { { { AudioNmixerout }, 0 },
! 88: { { AudioNmicrophone }, 1 } }
! 89: };
! 90:
! 91: const struct audio_mixer_enum ac97_source = {
! 92: 8,
! 93: { { { AudioNmicrophone } , 0 },
! 94: { { AudioNcd }, 1 },
! 95: { { "video" }, 2 },
! 96: { { AudioNaux }, 3 },
! 97: { { AudioNline }, 4 },
! 98: { { AudioNmixerout }, 5 },
! 99: { { AudioNmixerout AudioNmono }, 6 },
! 100: { { "phone" }, 7 }}
! 101: };
! 102:
! 103: /*
! 104: * Due to different values for each source that uses these structures,
! 105: * the ac97_query_devinfo function sets delta in mixer_devinfo_t using
! 106: * ac97_source_info.bits.
! 107: */
! 108: const struct audio_mixer_value ac97_volume_stereo = {
! 109: { AudioNvolume },
! 110: 2
! 111: };
! 112:
! 113: const struct audio_mixer_value ac97_volume_mono = {
! 114: { AudioNvolume },
! 115: 1
! 116: };
! 117:
! 118: #define WRAP(a) &a, sizeof(a)
! 119:
! 120: const struct ac97_source_info {
! 121: char *class;
! 122: char *device;
! 123: char *qualifier;
! 124: int type;
! 125:
! 126: const void *info;
! 127: int16_t info_size;
! 128:
! 129: u_int8_t reg;
! 130: u_int8_t bits:3;
! 131: u_int8_t ofs:4;
! 132: u_int8_t mute:1;
! 133: u_int8_t polarity:1; /* Does 0 == MAX or MIN */
! 134: u_int16_t default_value;
! 135:
! 136: int16_t prev;
! 137: int16_t next;
! 138: int16_t mixer_class;
! 139: } source_info[] = {
! 140: {
! 141: AudioCinputs, NULL, NULL, AUDIO_MIXER_CLASS,
! 142: }, {
! 143: AudioCoutputs, NULL, NULL, AUDIO_MIXER_CLASS,
! 144: }, {
! 145: AudioCrecord, NULL, NULL, AUDIO_MIXER_CLASS,
! 146: }, {
! 147: /* Stereo master volume*/
! 148: AudioCoutputs, AudioNmaster, NULL, AUDIO_MIXER_VALUE,
! 149: WRAP(ac97_volume_stereo),
! 150: AC97_REG_MASTER_VOLUME, 5, 0, 1, 0, 0x8000
! 151: }, {
! 152: /* Mono volume */
! 153: AudioCoutputs, AudioNmono, NULL, AUDIO_MIXER_VALUE,
! 154: WRAP(ac97_volume_mono),
! 155: AC97_REG_MASTER_VOLUME_MONO, 6, 0, 1, 0, 0x8000
! 156: }, {
! 157: AudioCoutputs, AudioNmono, AudioNsource, AUDIO_MIXER_ENUM,
! 158: WRAP(ac97_mono_select),
! 159: AC97_REG_GP, 1, 9, 0, 0, 0x0000
! 160: }, {
! 161: /* Headphone volume */
! 162: AudioCoutputs, AudioNheadphone, NULL, AUDIO_MIXER_VALUE,
! 163: WRAP(ac97_volume_stereo),
! 164: AC97_REG_HEADPHONE_VOLUME, 6, 0, 1, 0, 0x8000
! 165: }, {
! 166: AudioCoutputs, AudioNbass, NULL, AUDIO_MIXER_VALUE,
! 167: WRAP(ac97_volume_mono),
! 168: AC97_REG_MASTER_TONE, 4, 8, 0, 0, 0x0f0f
! 169: }, {
! 170: AudioCoutputs, AudioNtreble, NULL, AUDIO_MIXER_VALUE,
! 171: WRAP(ac97_volume_mono),
! 172: AC97_REG_MASTER_TONE, 4, 0, 0, 0, 0x0f0f
! 173: }, {
! 174: /* PC Beep Volume */
! 175: AudioCinputs, AudioNspeaker, NULL, AUDIO_MIXER_VALUE,
! 176: WRAP(ac97_volume_mono),
! 177: AC97_REG_PCBEEP_VOLUME, 4, 1, 1, 0, 0x0000
! 178: }, {
! 179: /* Phone */
! 180: AudioCinputs, "phone", NULL, AUDIO_MIXER_VALUE,
! 181: WRAP(ac97_volume_mono),
! 182: AC97_REG_PHONE_VOLUME, 5, 0, 1, 0, 0x8008
! 183: }, {
! 184: /* Mic Volume */
! 185: AudioCinputs, AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
! 186: WRAP(ac97_volume_mono),
! 187: AC97_REG_MIC_VOLUME, 5, 0, 1, 0, 0x8008
! 188: }, {
! 189: AudioCinputs, AudioNmicrophone, AudioNpreamp, AUDIO_MIXER_ENUM,
! 190: WRAP(ac97_on_off),
! 191: AC97_REG_MIC_VOLUME, 1, 6, 0, 0, 0x8008
! 192: }, {
! 193: AudioCinputs, AudioNmicrophone, AudioNsource, AUDIO_MIXER_ENUM,
! 194: WRAP(ac97_mic_select),
! 195: AC97_REG_GP, 1, 8, 0, 0x0000
! 196: }, {
! 197: /* Line in Volume */
! 198: AudioCinputs, AudioNline, NULL, AUDIO_MIXER_VALUE,
! 199: WRAP(ac97_volume_stereo),
! 200: AC97_REG_LINEIN_VOLUME, 5, 0, 1, 0, 0x8808
! 201: }, {
! 202: /* CD Volume */
! 203: AudioCinputs, AudioNcd, NULL, AUDIO_MIXER_VALUE,
! 204: WRAP(ac97_volume_stereo),
! 205: AC97_REG_CD_VOLUME, 5, 0, 1, 0, 0x8808
! 206: }, {
! 207: /* Video Volume */
! 208: AudioCinputs, "video", NULL, AUDIO_MIXER_VALUE,
! 209: WRAP(ac97_volume_stereo),
! 210: AC97_REG_VIDEO_VOLUME, 5, 0, 1, 0, 0x8808
! 211: }, {
! 212: /* AUX volume */
! 213: AudioCinputs, AudioNaux, NULL, AUDIO_MIXER_VALUE,
! 214: WRAP(ac97_volume_stereo),
! 215: AC97_REG_AUX_VOLUME, 5, 0, 1, 0, 0x8808
! 216: }, {
! 217: /* PCM out volume */
! 218: AudioCinputs, AudioNdac, NULL, AUDIO_MIXER_VALUE,
! 219: WRAP(ac97_volume_stereo),
! 220: AC97_REG_PCMOUT_VOLUME, 5, 0, 1, 0, 0x8808
! 221: }, {
! 222: /* Record Source - some logic for this is hard coded - see below */
! 223: AudioCrecord, AudioNsource, NULL, AUDIO_MIXER_ENUM,
! 224: WRAP(ac97_source),
! 225: AC97_REG_RECORD_SELECT, 3, 0, 0, 0, 0x0000
! 226: }, {
! 227: /* Record Gain */
! 228: AudioCrecord, AudioNvolume, NULL, AUDIO_MIXER_VALUE,
! 229: WRAP(ac97_volume_stereo),
! 230: AC97_REG_RECORD_GAIN, 4, 0, 1, 0, 0x8000
! 231: }, {
! 232: /* Record Gain mic */
! 233: AudioCrecord, AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
! 234: WRAP(ac97_volume_mono),
! 235: AC97_REG_RECORD_GAIN_MIC, 4, 0, 1, 1, 0x8000
! 236: }, {
! 237: /* */
! 238: AudioCoutputs, AudioNloudness, NULL, AUDIO_MIXER_ENUM,
! 239: WRAP(ac97_on_off),
! 240: AC97_REG_GP, 1, 12, 0, 0, 0x0000
! 241: }, {
! 242: AudioCoutputs, AudioNspatial, NULL, AUDIO_MIXER_ENUM,
! 243: WRAP(ac97_on_off),
! 244: AC97_REG_GP, 1, 13, 0, 0, 0x0000
! 245: }, {
! 246: AudioCoutputs, AudioNspatial, AudioNcenter,AUDIO_MIXER_VALUE,
! 247: WRAP(ac97_volume_mono),
! 248: AC97_REG_3D_CONTROL, 4, 8, 0, 1, 0x0000
! 249: }, {
! 250: AudioCoutputs, AudioNspatial, AudioNdepth, AUDIO_MIXER_VALUE,
! 251: WRAP(ac97_volume_mono),
! 252: AC97_REG_3D_CONTROL, 4, 0, 0, 1, 0x0000
! 253: }, {
! 254: /* Surround volume */
! 255: AudioCoutputs, AudioNsurround, NULL, AUDIO_MIXER_VALUE,
! 256: WRAP(ac97_volume_stereo),
! 257: AC97_REG_SURROUND_VOLUME, 6, 0, 1, 0, 0x8080
! 258: }, {
! 259: /* Center volume */
! 260: AudioCoutputs, AudioNcenter, NULL, AUDIO_MIXER_VALUE,
! 261: WRAP(ac97_volume_mono),
! 262: AC97_REG_CENTER_LFE_VOLUME, 6, 0, 1, 0, 0x8080
! 263: }, {
! 264: /* LFE volume */
! 265: AudioCoutputs, AudioNlfe, NULL, AUDIO_MIXER_VALUE,
! 266: WRAP(ac97_volume_mono),
! 267: AC97_REG_CENTER_LFE_VOLUME, 6, 8, 1, 0, 0x8080
! 268: }, {
! 269: /* External Amp */
! 270: AudioCoutputs, AudioNextamp, NULL, AUDIO_MIXER_ENUM,
! 271: WRAP(ac97_on_off),
! 272: AC97_REG_POWER, 1, 15, 0, 0, 0x0000
! 273: }
! 274:
! 275: /* Missing features: Simulated Stereo, POP, Loopback mode */
! 276: };
! 277:
! 278: #define SOURCE_INFO_SIZE (sizeof(source_info)/sizeof(source_info[0]))
! 279:
! 280: /*
! 281: * Check out http://www.intel.com/technology/computing/audio/index.htm
! 282: * for information on AC-97
! 283: */
! 284:
! 285: struct ac97_softc {
! 286: struct ac97_codec_if codec_if;
! 287: struct ac97_host_if *host_if;
! 288: struct ac97_source_info source_info[2 * SOURCE_INFO_SIZE];
! 289: int num_source_info;
! 290: enum ac97_host_flags host_flags;
! 291: u_int16_t caps, ext_id;
! 292: u_int16_t shadow_reg[128];
! 293: };
! 294:
! 295: int ac97_mixer_get_port(struct ac97_codec_if *, mixer_ctrl_t *);
! 296: int ac97_mixer_set_port(struct ac97_codec_if *, mixer_ctrl_t *);
! 297: int ac97_query_devinfo(struct ac97_codec_if *, mixer_devinfo_t *);
! 298: int ac97_get_portnum_by_name(struct ac97_codec_if *, char *, char *,
! 299: char *);
! 300: void ac97_restore_shadow(struct ac97_codec_if *);
! 301:
! 302: void ac97_ad1886_init(struct ac97_softc *);
! 303: void ac97_ad198x_init(struct ac97_softc *);
! 304: void ac97_alc655_init(struct ac97_softc *);
! 305: void ac97_cx20468_init(struct ac97_softc *);
! 306:
! 307: struct ac97_codec_if_vtbl ac97civ = {
! 308: ac97_mixer_get_port,
! 309: ac97_mixer_set_port,
! 310: ac97_query_devinfo,
! 311: ac97_get_portnum_by_name,
! 312: ac97_restore_shadow
! 313: };
! 314:
! 315: const struct ac97_codecid {
! 316: u_int8_t id;
! 317: u_int8_t mask;
! 318: u_int8_t rev;
! 319: u_int8_t shift; /* no use yet */
! 320: char * const name;
! 321: void (*init)(struct ac97_softc *);
! 322: } ac97_ad[] = {
! 323: { 0x03, 0xff, 0, 0, "AD1819" },
! 324: { 0x40, 0xff, 0, 0, "AD1881" },
! 325: { 0x48, 0xff, 0, 0, "AD1881A" },
! 326: { 0x60, 0xff, 0, 0, "AD1885" },
! 327: { 0x61, 0xff, 0, 0, "AD1886", ac97_ad1886_init },
! 328: { 0x63, 0xff, 0, 0, "AD1886A" },
! 329: { 0x68, 0xff, 0, 0, "AD1888", ac97_ad198x_init },
! 330: { 0x70, 0xff, 0, 0, "AD1980" },
! 331: { 0x72, 0xff, 0, 0, "AD1981A" },
! 332: { 0x74, 0xff, 0, 0, "AD1981B" },
! 333: { 0x75, 0xff, 0, 0, "AD1985", ac97_ad198x_init },
! 334: }, ac97_ak[] = {
! 335: { 0x00, 0xfe, 1, 0, "AK4540" },
! 336: { 0x01, 0xfe, 1, 0, "AK4540" },
! 337: { 0x02, 0xff, 0, 0, "AK4543" },
! 338: { 0x05, 0xff, 0, 0, "AK4544" },
! 339: { 0x06, 0xff, 0, 0, "AK4544A" },
! 340: { 0x07, 0xff, 0, 0, "AK4545" },
! 341: }, ac97_av[] = {
! 342: { 0x10, 0xff, 0, 0, "ALC200" },
! 343: { 0x20, 0xff, 0, 0, "ALC650" },
! 344: { 0x21, 0xff, 0, 0, "ALC650D" },
! 345: { 0x22, 0xff, 0, 0, "ALC650E" },
! 346: { 0x23, 0xff, 0, 0, "ALC650F" },
! 347: { 0x30, 0xff, 0, 0, "ALC101" },
! 348: { 0x40, 0xff, 0, 0, "ALC202" },
! 349: { 0x50, 0xff, 0, 0, "ALC250" },
! 350: { 0x52, 0xff, 0, 0, "ALC250A?" },
! 351: { 0x60, 0xf0, 0xf, 0, "ALC655", ac97_alc655_init },
! 352: { 0x70, 0xf0, 0xf, 0, "ALC203" },
! 353: { 0x80, 0xf0, 0xf, 0, "ALC658", ac97_alc655_init },
! 354: { 0x90, 0xf0, 0xf, 0, "ALC850" },
! 355: }, ac97_rl[] = {
! 356: { 0x00, 0xf0, 0xf, 0, "RL5306" },
! 357: { 0x10, 0xf0, 0xf, 0, "RL5382" },
! 358: { 0x20, 0xf0, 0xf, 0, "RL5383" },
! 359: }, ac97_cm[] = {
! 360: { 0x41, 0xff, 0, 0, "CMI9738" },
! 361: { 0x61, 0xff, 0, 0, "CMI9739" },
! 362: { 0x78, 0xff, 0, 0, "CMI9761A" },
! 363: { 0x82, 0xff, 0, 0, "CMI9761B" },
! 364: { 0x83, 0xff, 0, 0, "CMI9761A+" },
! 365: }, ac97_cr[] = {
! 366: { 0x84, 0xff, 0, 0, "EV1938" },
! 367: }, ac97_cs[] = {
! 368: { 0x00, 0xf8, 7, 0, "CS4297" },
! 369: { 0x10, 0xf8, 7, 0, "CS4297A" },
! 370: { 0x20, 0xf8, 7, 0, "CS4298" },
! 371: { 0x28, 0xf8, 7, 0, "CS4294" },
! 372: { 0x30, 0xf8, 7, 0, "CS4299" },
! 373: { 0x48, 0xf8, 7, 0, "CS4201" },
! 374: { 0x58, 0xf8, 7, 0, "CS4205" },
! 375: { 0x60, 0xf8, 7, 0, "CS4291" },
! 376: { 0x70, 0xf8, 7, 0, "CS4202" },
! 377: }, ac97_cx[] = {
! 378: { 0x21, 0xff, 0, 0, "HSD11246" },
! 379: { 0x28, 0xf8, 7, 0, "CX20468", ac97_cx20468_init },
! 380: { 0x30, 0xff, 0, 0, "CXT48", },
! 381: { 0x42, 0xff, 0, 0, "CXT66", },
! 382: }, ac97_dt[] = {
! 383: { 0x00, 0xff, 0, 0, "DT0398" },
! 384: }, ac97_em[] = {
! 385: { 0x23, 0xff, 0, 0, "EM28023" },
! 386: { 0x28, 0xff, 0, 0, "EM28028" },
! 387: }, ac97_es[] = {
! 388: { 0x08, 0xff, 0, 0, "ES1921" },
! 389: }, ac97_is[] = {
! 390: { 0x00, 0xff, 0, 0, "HMP9701" },
! 391: }, ac97_ic[] = {
! 392: { 0x01, 0xff, 0, 0, "ICE1230" },
! 393: { 0x11, 0xff, 0, 0, "ICE1232" },
! 394: { 0x14, 0xff, 0, 0, "ICE1232A" },
! 395: { 0x51, 0xff, 0, 0, "VIA VT1616" },
! 396: { 0x52, 0xff, 0, 0, "VIA VT1616i" },
! 397: }, ac97_it[] = {
! 398: { 0x20, 0xff, 0, 0, "ITE2226E" },
! 399: { 0x60, 0xff, 0, 0, "ITE2646E" },
! 400: }, ac97_ns[] = {
! 401: { 0x00, 0xff, 0, 0, "LM454[03568]" },
! 402: { 0x31, 0xff, 0, 0, "LM4549" },
! 403: { 0x40, 0xff, 0, 0, "LM4540" },
! 404: { 0x43, 0xff, 0, 0, "LM4543" },
! 405: { 0x46, 0xff, 0, 0, "LM4546A" },
! 406: { 0x48, 0xff, 0, 0, "LM4548A" },
! 407: { 0x49, 0xff, 0, 0, "LM4549A" },
! 408: { 0x50, 0xff, 0, 0, "LM4550" },
! 409: }, ac97_ps[] = {
! 410: { 0x01, 0xff, 0, 0, "UCB1510" },
! 411: { 0x04, 0xff, 0, 0, "UCB1400" },
! 412: }, ac97_sl[] = {
! 413: { 0x20, 0xe0, 0, 0, "Si3036/38" },
! 414: }, ac97_st[] = {
! 415: { 0x00, 0xff, 0, 0, "STAC9700" },
! 416: { 0x04, 0xff, 0, 0, "STAC970[135]" },
! 417: { 0x05, 0xff, 0, 0, "STAC9704" },
! 418: { 0x08, 0xff, 0, 0, "STAC9708/11" },
! 419: { 0x09, 0xff, 0, 0, "STAC9721/23" },
! 420: { 0x44, 0xff, 0, 0, "STAC9744/45" },
! 421: { 0x50, 0xff, 0, 0, "STAC9750/51" },
! 422: { 0x52, 0xff, 0, 0, "STAC9752/53" },
! 423: { 0x56, 0xff, 0, 0, "STAC9756/57" },
! 424: { 0x58, 0xff, 0, 0, "STAC9758/59" },
! 425: { 0x60, 0xff, 0, 0, "STAC9760/61" },
! 426: { 0x62, 0xff, 0, 0, "STAC9762/63" },
! 427: { 0x66, 0xff, 0, 0, "STAC9766/67" },
! 428: { 0x84, 0xff, 0, 0, "STAC9784/85" },
! 429: }, ac97_vi[] = {
! 430: { 0x61, 0xff, 0, 0, "VT1612A" },
! 431: }, ac97_tt[] = {
! 432: { 0x02, 0xff, 0, 0, "TR28022" },
! 433: { 0x03, 0xff, 0, 0, "TR28023" },
! 434: { 0x06, 0xff, 0, 0, "TR28026" },
! 435: { 0x08, 0xff, 0, 0, "TR28028" },
! 436: { 0x23, 0xff, 0, 0, "TR28602" },
! 437: }, ac97_ti[] = {
! 438: { 0x20, 0xff, 0, 0, "TLC320AD9xC" },
! 439: }, ac97_wb[] = {
! 440: { 0x01, 0xff, 0, 0, "W83971D" },
! 441: }, ac97_wo[] = {
! 442: { 0x00, 0xff, 0, 0, "WM9701A" },
! 443: { 0x03, 0xff, 0, 0, "WM9704M/Q-0" }, /* & WM9703 */
! 444: { 0x04, 0xff, 0, 0, "WM9704M/Q-1" },
! 445: { 0x05, 0xff, 0, 0, "WM9705/10" },
! 446: { 0x09, 0xff, 0, 0, "WM9709" },
! 447: { 0x12, 0xff, 0, 0, "WM9711/12" },
! 448: }, ac97_ym[] = {
! 449: { 0x00, 0xff, 0, 0, "YMF743-S" },
! 450: { 0x02, 0xff, 0, 0, "YMF752-S" },
! 451: { 0x03, 0xff, 0, 0, "YMF753-S" },
! 452: };
! 453:
! 454: #define cl(n) n, sizeof(n)/sizeof(n[0])
! 455: const struct ac97_vendorid {
! 456: u_int32_t id;
! 457: char * const name;
! 458: const struct ac97_codecid * const codecs;
! 459: u_int8_t num;
! 460: } ac97_vendors[] = {
! 461: { 0x01408300, "Creative", cl(ac97_cr) },
! 462: { 0x41445300, "Analog Devices", cl(ac97_ad) },
! 463: { 0x414b4D00, "Asahi Kasei", cl(ac97_ak) },
! 464: { 0x414c4300, "Realtek", cl(ac97_rl) },
! 465: { 0x414c4700, "Avance Logic", cl(ac97_av) },
! 466: { 0x434d4900, "C-Media Electronics", cl(ac97_cm) },
! 467: { 0x43525900, "Cirrus Logic", cl(ac97_cs) },
! 468: { 0x43585400, "Conexant", cl(ac97_cx) },
! 469: { 0x44543000, "Diamond Technology", cl(ac97_dt) },
! 470: { 0x454d4300, "eMicro", cl(ac97_em) },
! 471: { 0x45838300, "ESS Technology", cl(ac97_es) },
! 472: { 0x48525300, "Intersil", cl(ac97_is) },
! 473: { 0x49434500, "ICEnsemble", cl(ac97_ic) },
! 474: { 0x49544500, "ITE, Inc.", cl(ac97_it) },
! 475: { 0x4e534300, "National Semiconductor", cl(ac97_ns) },
! 476: { 0x50534300, "Philips Semiconductor", cl(ac97_ps) },
! 477: { 0x53494c00, "Silicon Laboratory", cl(ac97_sl) },
! 478: { 0x54524100, "TriTech Microelectronics", cl(ac97_tt) },
! 479: { 0x54584e00, "Texas Instruments", cl(ac97_ti) },
! 480: { 0x56494100, "VIA Technologies", cl(ac97_vi) },
! 481: { 0x57454300, "Winbond", cl(ac97_wb) },
! 482: { 0x574d4c00, "Wolfson", cl(ac97_wo) },
! 483: { 0x594d4800, "Yamaha", cl(ac97_ym) },
! 484: { 0x83847600, "SigmaTel", cl(ac97_st) },
! 485: };
! 486: #undef cl
! 487:
! 488: const char * const ac97enhancement[] = {
! 489: "No 3D Stereo",
! 490: "Analog Devices Phat Stereo",
! 491: "Creative",
! 492: "National Semi 3D",
! 493: "Yamaha Ymersion",
! 494: "BBE 3D",
! 495: "Crystal Semi 3D",
! 496: "Qsound QXpander",
! 497: "Spatializer 3D",
! 498: "SRS 3D",
! 499: "Platform Tech 3D",
! 500: "AKM 3D",
! 501: "Aureal",
! 502: "AZTECH 3D",
! 503: "Binaura 3D",
! 504: "ESS Technology",
! 505: "Harman International VMAx",
! 506: "Nvidea 3D",
! 507: "Philips Incredible Sound",
! 508: "Texas Instruments 3D",
! 509: "VLSI Technology 3D",
! 510: "TriTech 3D",
! 511: "Realtek 3D",
! 512: "Samsung 3D",
! 513: "Wolfson Microelectronics 3D",
! 514: "Delta Integration 3D",
! 515: "SigmaTel 3D",
! 516: "KS Waves 3D",
! 517: "Rockwell 3D",
! 518: "Unknown 3D",
! 519: "Unknown 3D",
! 520: "Unknown 3D"
! 521: };
! 522:
! 523: const char * const ac97feature[] = {
! 524: "mic channel",
! 525: "reserved",
! 526: "tone",
! 527: "simulated stereo",
! 528: "headphone",
! 529: "bass boost",
! 530: "18 bit DAC",
! 531: "20 bit DAC",
! 532: "18 bit ADC",
! 533: "20 bit ADC"
! 534: };
! 535:
! 536:
! 537: int ac97_str_equal(const char *, const char *);
! 538: void ac97_setup_source_info(struct ac97_softc *);
! 539: void ac97_setup_defaults(struct ac97_softc *);
! 540: int ac97_read(struct ac97_softc *, u_int8_t, u_int16_t *);
! 541: int ac97_write(struct ac97_softc *, u_int8_t, u_int16_t);
! 542:
! 543:
! 544: #ifdef AUDIO_DEBUG
! 545: #define DPRINTF(x) if (ac97debug) printf x
! 546: #define DPRINTFN(n,x) if (ac97debug>(n)) printf x
! 547: #ifdef AC97_DEBUG
! 548: int ac97debug = 1;
! 549: #else
! 550: int ac97debug = 0;
! 551: #endif
! 552: #else
! 553: #define DPRINTF(x)
! 554: #define DPRINTFN(n,x)
! 555: #endif
! 556:
! 557: int
! 558: ac97_read(struct ac97_softc *as, u_int8_t reg, u_int16_t *val)
! 559: {
! 560: int error;
! 561:
! 562: if (((as->host_flags & AC97_HOST_DONT_READ) &&
! 563: (reg != AC97_REG_VENDOR_ID1 && reg != AC97_REG_VENDOR_ID2 &&
! 564: reg != AC97_REG_RESET)) ||
! 565: (as->host_flags & AC97_HOST_DONT_READANY)) {
! 566: *val = as->shadow_reg[reg >> 1];
! 567: return (0);
! 568: }
! 569:
! 570: if ((error = as->host_if->read(as->host_if->arg, reg, val)))
! 571: *val = as->shadow_reg[reg >> 1];
! 572: return (error);
! 573: }
! 574:
! 575: int
! 576: ac97_write(struct ac97_softc *as, u_int8_t reg, u_int16_t val)
! 577: {
! 578: as->shadow_reg[reg >> 1] = val;
! 579: return (as->host_if->write(as->host_if->arg, reg, val));
! 580: }
! 581:
! 582: void
! 583: ac97_setup_defaults(struct ac97_softc *as)
! 584: {
! 585: int idx;
! 586:
! 587: bzero(as->shadow_reg, sizeof(as->shadow_reg));
! 588:
! 589: for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
! 590: const struct ac97_source_info *si = &source_info[idx];
! 591:
! 592: ac97_write(as, si->reg, si->default_value);
! 593: }
! 594: }
! 595:
! 596: void
! 597: ac97_restore_shadow(struct ac97_codec_if *self)
! 598: {
! 599: struct ac97_softc *as = (struct ac97_softc *)self;
! 600: int idx;
! 601:
! 602: for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
! 603: const struct ac97_source_info *si = &source_info[idx];
! 604:
! 605: ac97_write(as, si->reg, as->shadow_reg[si->reg >> 1]);
! 606: }
! 607: }
! 608:
! 609: int
! 610: ac97_str_equal(const char *a, const char *b)
! 611: {
! 612: return ((a == b) || (a && b && (!strcmp(a, b))));
! 613: }
! 614:
! 615: void
! 616: ac97_setup_source_info(struct ac97_softc *as)
! 617: {
! 618: struct ac97_source_info *si, *si2;
! 619: int idx, ouridx;
! 620:
! 621: for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
! 622: si = &as->source_info[ouridx];
! 623:
! 624: bcopy(&source_info[idx], si, sizeof(*si));
! 625:
! 626: switch (si->type) {
! 627: case AUDIO_MIXER_CLASS:
! 628: si->mixer_class = ouridx;
! 629: ouridx++;
! 630: break;
! 631: case AUDIO_MIXER_VALUE:
! 632: /* Todo - Test to see if it works */
! 633: ouridx++;
! 634:
! 635: /* Add an entry for mute, if necessary */
! 636: if (si->mute) {
! 637: si = &as->source_info[ouridx];
! 638: bcopy(&source_info[idx], si, sizeof(*si));
! 639: si->qualifier = AudioNmute;
! 640: si->type = AUDIO_MIXER_ENUM;
! 641: si->info = &ac97_on_off;
! 642: si->info_size = sizeof(ac97_on_off);
! 643: si->bits = 1;
! 644: si->ofs = 15;
! 645: si->mute = 0;
! 646: si->polarity = 0;
! 647: ouridx++;
! 648: }
! 649: break;
! 650: case AUDIO_MIXER_ENUM:
! 651: /* Todo - Test to see if it works */
! 652: ouridx++;
! 653: break;
! 654: default:
! 655: printf ("ac97: shouldn't get here\n");
! 656: break;
! 657: }
! 658: }
! 659:
! 660: as->num_source_info = ouridx;
! 661:
! 662: for (idx = 0; idx < as->num_source_info; idx++) {
! 663: int idx2, previdx;
! 664:
! 665: si = &as->source_info[idx];
! 666:
! 667: /* Find mixer class */
! 668: for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
! 669: si2 = &as->source_info[idx2];
! 670:
! 671: if (si2->type == AUDIO_MIXER_CLASS &&
! 672: ac97_str_equal(si->class, si2->class)) {
! 673: si->mixer_class = idx2;
! 674: }
! 675: }
! 676:
! 677:
! 678: /* Setup prev and next pointers */
! 679: if (si->prev != 0 || si->qualifier)
! 680: continue;
! 681:
! 682: si->prev = AUDIO_MIXER_LAST;
! 683: previdx = idx;
! 684:
! 685: for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
! 686: if (idx2 == idx)
! 687: continue;
! 688:
! 689: si2 = &as->source_info[idx2];
! 690:
! 691: if (!si2->prev &&
! 692: ac97_str_equal(si->class, si2->class) &&
! 693: ac97_str_equal(si->device, si2->device)) {
! 694: as->source_info[previdx].next = idx2;
! 695: as->source_info[idx2].prev = previdx;
! 696:
! 697: previdx = idx2;
! 698: }
! 699: }
! 700:
! 701: as->source_info[previdx].next = AUDIO_MIXER_LAST;
! 702: }
! 703: }
! 704:
! 705: int
! 706: ac97_attach(struct ac97_host_if *host_if)
! 707: {
! 708: struct ac97_softc *as;
! 709: u_int16_t id1, id2;
! 710: u_int32_t id;
! 711: mixer_ctrl_t ctl;
! 712: int error, i;
! 713: void (*initfunc)(struct ac97_softc *);
! 714:
! 715: initfunc = NULL;
! 716:
! 717: if (!(as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_NOWAIT)))
! 718: return (ENOMEM);
! 719:
! 720: bzero(as, sizeof(*as));
! 721:
! 722: as->codec_if.vtbl = &ac97civ;
! 723: as->host_if = host_if;
! 724:
! 725: if ((error = host_if->attach(host_if->arg, &as->codec_if))) {
! 726: free(as, M_DEVBUF);
! 727: return (error);
! 728: }
! 729:
! 730: host_if->reset(host_if->arg);
! 731: DELAY(1000);
! 732:
! 733: host_if->write(host_if->arg, AC97_REG_POWER, 0);
! 734: host_if->write(host_if->arg, AC97_REG_RESET, 0);
! 735: DELAY(10000);
! 736:
! 737: if (host_if->flags)
! 738: as->host_flags = host_if->flags(host_if->arg);
! 739:
! 740: ac97_setup_defaults(as);
! 741: ac97_read(as, AC97_REG_VENDOR_ID1, &id1);
! 742: ac97_read(as, AC97_REG_VENDOR_ID2, &id2);
! 743: ac97_read(as, AC97_REG_RESET, &as->caps);
! 744:
! 745: id = (id1 << 16) | id2;
! 746: if (id) {
! 747: register const struct ac97_vendorid *vendor;
! 748: register const struct ac97_codecid *codec;
! 749:
! 750: printf("ac97: codec id 0x%08x", id);
! 751: for (vendor = &ac97_vendors[sizeof(ac97_vendors) /
! 752: sizeof(ac97_vendors[0]) - 1];
! 753: vendor >= ac97_vendors; vendor--) {
! 754: if (vendor->id == (id & AC97_VENDOR_ID_MASK)) {
! 755: printf(" (%s", vendor->name);
! 756: for (codec = &vendor->codecs[vendor->num-1];
! 757: codec >= vendor->codecs; codec--) {
! 758: if (codec->id == (id & codec->mask))
! 759: break;
! 760: }
! 761: if (codec >= vendor->codecs && codec->mask) {
! 762: printf(" %s", codec->name);
! 763: initfunc = codec->init;
! 764: } else
! 765: printf(" <%02x>", id & 0xff);
! 766: if (codec >= vendor->codecs && codec->rev)
! 767: printf(" rev %d", id & codec->rev);
! 768: printf(")");
! 769: break;
! 770: }
! 771: }
! 772: printf("\n");
! 773: } else
! 774: printf("ac97: codec id not read\n");
! 775:
! 776: if (as->caps) {
! 777: printf("ac97: codec features ");
! 778: for (i = 0; i < 10; i++) {
! 779: if (as->caps & (1 << i))
! 780: printf("%s, ", ac97feature[i]);
! 781: }
! 782: printf("%s\n",
! 783: ac97enhancement[AC97_CAPS_ENHANCEMENT(as->caps)]);
! 784: }
! 785:
! 786: ac97_read(as, AC97_REG_EXT_AUDIO_ID, &as->ext_id);
! 787: if (as->ext_id)
! 788: DPRINTF(("ac97: ext id %b\n", as->ext_id,
! 789: AC97_EXT_AUDIO_BITS));
! 790: if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_VRM)) {
! 791: ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &id1);
! 792: if (as->ext_id & AC97_EXT_AUDIO_VRA)
! 793: id1 |= AC97_EXT_AUDIO_VRA;
! 794: if (as->ext_id & AC97_EXT_AUDIO_VRM)
! 795: id1 |= AC97_EXT_AUDIO_VRM;
! 796: ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, id1);
! 797: }
! 798:
! 799: ac97_setup_source_info(as);
! 800:
! 801: /* use initfunc for specific device */
! 802: if (initfunc != NULL)
! 803: initfunc(as);
! 804:
! 805: /* Just enable the DAC and master volumes by default */
! 806: bzero(&ctl, sizeof(ctl));
! 807:
! 808: ctl.type = AUDIO_MIXER_ENUM;
! 809: ctl.un.ord = 0; /* off */
! 810: ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
! 811: AudioNmaster, AudioNmute);
! 812: ac97_mixer_set_port(&as->codec_if, &ctl);
! 813:
! 814: ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCinputs,
! 815: AudioNdac, AudioNmute);
! 816: ac97_mixer_set_port(&as->codec_if, &ctl);
! 817:
! 818: ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
! 819: AudioNvolume, AudioNmute);
! 820: ac97_mixer_set_port(&as->codec_if, &ctl);
! 821:
! 822: ctl.type = AUDIO_MIXER_ENUM;
! 823: ctl.un.ord = 0;
! 824: ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
! 825: AudioNsource, NULL);
! 826: ac97_mixer_set_port(&as->codec_if, &ctl);
! 827:
! 828: return (0);
! 829: }
! 830:
! 831: int
! 832: ac97_query_devinfo(struct ac97_codec_if *codec_if, mixer_devinfo_t *dip)
! 833: {
! 834: struct ac97_softc *as = (struct ac97_softc *)codec_if;
! 835:
! 836: if (dip->index < as->num_source_info) {
! 837: struct ac97_source_info *si = &as->source_info[dip->index];
! 838: const char *name;
! 839:
! 840: dip->type = si->type;
! 841: dip->mixer_class = si->mixer_class;
! 842: dip->prev = si->prev;
! 843: dip->next = si->next;
! 844:
! 845: if (si->qualifier)
! 846: name = si->qualifier;
! 847: else if (si->device)
! 848: name = si->device;
! 849: else if (si->class)
! 850: name = si->class;
! 851:
! 852: if (name)
! 853: strlcpy(dip->label.name, name, sizeof dip->label.name);
! 854:
! 855: bcopy(si->info, &dip->un, si->info_size);
! 856:
! 857: /* Set the delta for volume sources */
! 858: if (dip->type == AUDIO_MIXER_VALUE)
! 859: dip->un.v.delta = 1 << (8 - si->bits);
! 860:
! 861: return (0);
! 862: }
! 863:
! 864: return (ENXIO);
! 865: }
! 866:
! 867: int
! 868: ac97_mixer_set_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
! 869: {
! 870: struct ac97_softc *as = (struct ac97_softc *)codec_if;
! 871: struct ac97_source_info *si = &as->source_info[cp->dev];
! 872: u_int16_t mask;
! 873: u_int16_t val, newval;
! 874: int error;
! 875:
! 876: if (cp->dev < 0 || cp->dev >= as->num_source_info ||
! 877: cp->type != si->type)
! 878: return (EINVAL);
! 879:
! 880: ac97_read(as, si->reg, &val);
! 881:
! 882: DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
! 883:
! 884: mask = (1 << si->bits) - 1;
! 885:
! 886: switch (cp->type) {
! 887: case AUDIO_MIXER_ENUM:
! 888: if (cp->un.ord > mask || cp->un.ord < 0)
! 889: return (EINVAL);
! 890:
! 891: newval = (cp->un.ord << si->ofs);
! 892: if (si->reg == AC97_REG_RECORD_SELECT) {
! 893: newval |= (newval << (8 + si->ofs));
! 894: mask |= (mask << 8);
! 895: }
! 896:
! 897: if (si->mute) {
! 898: newval |= newval << 8;
! 899: mask |= mask << 8;
! 900: }
! 901:
! 902: break;
! 903: case AUDIO_MIXER_VALUE:
! 904: {
! 905: const struct audio_mixer_value *value = si->info;
! 906: u_int16_t l, r;
! 907:
! 908: if (cp->un.value.num_channels <= 0 ||
! 909: cp->un.value.num_channels > value->num_channels)
! 910: return (EINVAL);
! 911:
! 912: if (cp->un.value.num_channels == 1) {
! 913: l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
! 914: } else {
! 915: if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
! 916: l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
! 917: r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
! 918: } else {
! 919: r = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
! 920: l = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
! 921: }
! 922: }
! 923:
! 924: if (!si->polarity) {
! 925: l = 255 - l;
! 926: r = 255 - r;
! 927: }
! 928:
! 929: l >>= 8 - si->bits;
! 930: r >>= 8 - si->bits;
! 931:
! 932: newval = ((l & mask) << si->ofs);
! 933: if (value->num_channels == 2) {
! 934: newval |= ((r & mask) << (si->ofs + 8));
! 935: mask |= (mask << 8);
! 936: }
! 937:
! 938: break;
! 939: }
! 940: default:
! 941: return (EINVAL);
! 942: }
! 943:
! 944: mask = mask << si->ofs;
! 945: error = ac97_write(as, si->reg, (val & ~mask) | newval);
! 946: if (error)
! 947: return (error);
! 948:
! 949: return (0);
! 950: }
! 951:
! 952: int
! 953: ac97_get_portnum_by_name(struct ac97_codec_if *codec_if, char *class,
! 954: char *device, char *qualifier)
! 955: {
! 956: struct ac97_softc *as = (struct ac97_softc *)codec_if;
! 957: int idx;
! 958:
! 959: for (idx = 0; idx < as->num_source_info; idx++) {
! 960: struct ac97_source_info *si = &as->source_info[idx];
! 961: if (ac97_str_equal(class, si->class) &&
! 962: ac97_str_equal(device, si->device) &&
! 963: ac97_str_equal(qualifier, si->qualifier))
! 964: return (idx);
! 965: }
! 966:
! 967: return (-1);
! 968: }
! 969:
! 970: int
! 971: ac97_mixer_get_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
! 972: {
! 973: struct ac97_softc *as = (struct ac97_softc *)codec_if;
! 974: struct ac97_source_info *si = &as->source_info[cp->dev];
! 975: u_int16_t mask;
! 976: u_int16_t val;
! 977:
! 978: if (cp->dev < 0 || cp->dev >= as->num_source_info ||
! 979: cp->type != si->type)
! 980: return (EINVAL);
! 981:
! 982: ac97_read(as, si->reg, &val);
! 983:
! 984: DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
! 985:
! 986: mask = (1 << si->bits) - 1;
! 987:
! 988: switch (cp->type) {
! 989: case AUDIO_MIXER_ENUM:
! 990: cp->un.ord = (val >> si->ofs) & mask;
! 991: DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs,
! 992: mask, cp->un.ord));
! 993: break;
! 994: case AUDIO_MIXER_VALUE:
! 995: {
! 996: const struct audio_mixer_value *value = si->info;
! 997: u_int16_t l, r;
! 998:
! 999: if ((cp->un.value.num_channels <= 0) ||
! 1000: (cp->un.value.num_channels > value->num_channels))
! 1001: return (EINVAL);
! 1002:
! 1003: if (value->num_channels == 1)
! 1004: l = r = (val >> si->ofs) & mask;
! 1005: else {
! 1006: if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
! 1007: l = (val >> si->ofs) & mask;
! 1008: r = (val >> (si->ofs + 8)) & mask;
! 1009: } else {
! 1010: r = (val >> si->ofs) & mask;
! 1011: l = (val >> (si->ofs + 8)) & mask;
! 1012: }
! 1013: }
! 1014:
! 1015: l <<= 8 - si->bits;
! 1016: r <<= 8 - si->bits;
! 1017: if (!si->polarity) {
! 1018: l = 255 - l;
! 1019: r = 255 - r;
! 1020: }
! 1021:
! 1022: /*
! 1023: * The EAP driver averages l and r for stereo
! 1024: * channels that are requested in MONO mode. Does this
! 1025: * make sense?
! 1026: */
! 1027: if (cp->un.value.num_channels == 1) {
! 1028: cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
! 1029: } else if (cp->un.value.num_channels == 2) {
! 1030: cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
! 1031: cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
! 1032: }
! 1033:
! 1034: break;
! 1035: }
! 1036: default:
! 1037: return (EINVAL);
! 1038: }
! 1039:
! 1040: return (0);
! 1041: }
! 1042:
! 1043: int
! 1044: ac97_set_rate(struct ac97_codec_if *codec_if, struct audio_params *p,
! 1045: int mode)
! 1046: {
! 1047: struct ac97_softc *as = (struct ac97_softc *)codec_if;
! 1048: u_int16_t reg, val, regval, id = 0;
! 1049:
! 1050: DPRINTFN(5, ("set_rate(%lu) ", p->sample_rate));
! 1051:
! 1052: if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
! 1053: p->sample_rate = AC97_SINGLERATE;
! 1054: return (0);
! 1055: }
! 1056:
! 1057: if (p->sample_rate > 0xffff) {
! 1058: if (mode != AUMODE_PLAY)
! 1059: return (EINVAL);
! 1060: if (!(as->ext_id & AC97_EXT_AUDIO_DRA))
! 1061: return (EINVAL);
! 1062: if (ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &id))
! 1063: return (EIO);
! 1064: id |= AC97_EXT_AUDIO_DRA;
! 1065: if (ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, id))
! 1066: return (EIO);
! 1067: p->sample_rate /= 2;
! 1068: }
! 1069:
! 1070: /* i guess it's better w/o clicks and squeecks when changing the rate */
! 1071: if (ac97_read(as, AC97_REG_POWER, &val) ||
! 1072: ac97_write(as, AC97_REG_POWER, val |
! 1073: (mode == AUMODE_PLAY? AC97_POWER_OUT : AC97_POWER_IN)))
! 1074: return (EIO);
! 1075:
! 1076: reg = mode == AUMODE_PLAY ?
! 1077: AC97_REG_FRONT_DAC_RATE : AC97_REG_PCM_ADC_RATE;
! 1078:
! 1079: if (ac97_write(as, reg, (u_int16_t) p->sample_rate) ||
! 1080: ac97_read(as, reg, ®val))
! 1081: return (EIO);
! 1082: p->sample_rate = regval;
! 1083: if (id & AC97_EXT_AUDIO_DRA)
! 1084: p->sample_rate *= 2;
! 1085:
! 1086: DPRINTFN(5, (" %lu\n", regval));
! 1087:
! 1088: if (ac97_write(as, AC97_REG_POWER, val))
! 1089: return (EIO);
! 1090:
! 1091: return (0);
! 1092: }
! 1093:
! 1094: /*
! 1095: * Codec-dependent initialization
! 1096: */
! 1097:
! 1098: #define AC97_AD1886_JACK_SENSE 0x72
! 1099:
! 1100: void
! 1101: ac97_ad1886_init(struct ac97_softc *as)
! 1102: {
! 1103: ac97_write(as, AC97_AD1886_JACK_SENSE, 0x0010);
! 1104: }
! 1105:
! 1106: void
! 1107: ac97_ad198x_init(struct ac97_softc *as)
! 1108: {
! 1109: int i;
! 1110: u_int16_t misc;
! 1111:
! 1112: ac97_read(as, AC97_AD_REG_MISC, &misc);
! 1113: ac97_write(as, AC97_AD_REG_MISC,
! 1114: misc|AC97_AD_MISC_HPSEL|AC97_AD_MISC_LOSEL);
! 1115:
! 1116: for (i = 0; i < as->num_source_info; i++) {
! 1117: if (as->source_info[i].reg == AC97_REG_SURROUND_VOLUME)
! 1118: as->source_info[i].reg = AC97_REG_MASTER_VOLUME;
! 1119: else if (as->source_info[i].reg == AC97_REG_MASTER_VOLUME) {
! 1120: as->source_info[i].reg = AC97_REG_SURROUND_VOLUME;
! 1121: if (as->source_info[i].type == AUDIO_MIXER_ENUM) {
! 1122: as->source_info[i].mute = 1;
! 1123: as->source_info[i].ofs = 7;
! 1124: }
! 1125: }
! 1126: }
! 1127: }
! 1128:
! 1129: void
! 1130: ac97_alc655_init(struct ac97_softc *as)
! 1131: {
! 1132: u_int16_t misc;
! 1133:
! 1134: ac97_read(as, AC97_AV_REG_MISC, &misc);
! 1135: if (as->host_flags & AC97_HOST_DONT_ENABLE_SPDIF) {
! 1136: misc &= ~AC97_AV_MISC_SPDIFEN;
! 1137: } else {
! 1138: misc |= AC97_AV_MISC_SPDIFEN;
! 1139: }
! 1140: misc &= ~AC97_AV_MISC_VREFDIS;
! 1141: ac97_write(as, AC97_AV_REG_MISC, misc);
! 1142:
! 1143: ac97_write(as, AC97_AV_REG_MULTICH, AC97_AV_MULTICH_MAGIC);
! 1144: }
! 1145:
! 1146: void
! 1147: ac97_cx20468_init(struct ac97_softc *as)
! 1148: {
! 1149: u_int16_t misc;
! 1150:
! 1151: ac97_read(as, AC97_CX_REG_MISC, &misc);
! 1152: ac97_write(as, AC97_CX_REG_MISC, misc &
! 1153: ~(AC97_CX_SPDIFEN | AC97_CX_COPYRIGHT | AC97_CX_MASK));
! 1154: }
CVSweb