Home | History | Annotate | Line # | Download | only in ic
ac97.c revision 1.26
      1 /*      $NetBSD: ac97.c,v 1.26 2002/10/04 19:22:40 joda Exp $ */
      2 /*	$OpenBSD: ac97.c,v 1.8 2000/07/19 09:01:35 csapuntz Exp $	*/
      3 
      4 /*
      5  * Copyright (c) 1999, 2000 Constantine Sapuntzakis
      6  *
      7  * Author:        Constantine Sapuntzakis <csapuntz (at) stanford.edu>
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. The name of the author may not be used to endorse or promote
     18  *    products derived from this software without specific prior written
     19  *    permission.
     20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
     21  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
     24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
     26  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
     27  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
     30  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
     31  * DAMAGE
     32  */
     33 
     34 /* Partially inspired by FreeBSD's sys/dev/pcm/ac97.c. It came with
     35    the following copyright */
     36 
     37 /*
     38  * Copyright (c) 1999 Cameron Grant <gandalf (at) vilnya.demon.co.uk>
     39  * All rights reserved.
     40  *
     41  * Redistribution and use in source and binary forms, with or without
     42  * modification, are permitted provided that the following conditions
     43  * are met:
     44  * 1. Redistributions of source code must retain the above copyright
     45  *    notice, this list of conditions and the following disclaimer.
     46  * 2. Redistributions in binary form must reproduce the above copyright
     47  *    notice, this list of conditions and the following disclaimer in the
     48  *    documentation and/or other materials provided with the distribution.
     49  *
     50  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     53  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     60  * SUCH DAMAGE.
     61  *
     62  * $FreeBSD$
     63  */
     64 
     65 #include <sys/cdefs.h>
     66 __KERNEL_RCSID(0, "$NetBSD: ac97.c,v 1.26 2002/10/04 19:22:40 joda Exp $");
     67 
     68 #include <sys/param.h>
     69 #include <sys/systm.h>
     70 #include <sys/kernel.h>
     71 #include <sys/malloc.h>
     72 #include <sys/device.h>
     73 
     74 #include <sys/audioio.h>
     75 #include <dev/audio_if.h>
     76 
     77 #include <dev/ic/ac97reg.h>
     78 #include <dev/ic/ac97var.h>
     79 
     80 static const struct audio_mixer_enum ac97_on_off = { 2,
     81 					       { { { AudioNoff } , 0 },
     82 					         { { AudioNon }  , 1 } }};
     83 
     84 
     85 static const struct audio_mixer_enum ac97_mic_select = { 2,
     86 					       { { { AudioNmicrophone "0" },
     87 						   0 },
     88 					         { { AudioNmicrophone "1" },
     89 						   1 } }};
     90 
     91 static const struct audio_mixer_enum ac97_mono_select = { 2,
     92 					       { { { AudioNmixerout },
     93 						   0 },
     94 					         { { AudioNmicrophone },
     95 						   1 } }};
     96 
     97 static const struct audio_mixer_enum ac97_source = { 8,
     98 					       { { { AudioNmicrophone } , 0 },
     99 						 { { AudioNcd }, 1 },
    100 						 { { "video" }, 2 },
    101 						 { { AudioNaux }, 3 },
    102 						 { { AudioNline }, 4 },
    103 						 { { AudioNmixerout }, 5 },
    104 						 { { AudioNmixerout AudioNmono }, 6 },
    105 						 { { "phone" }, 7 }}};
    106 
    107 /*
    108  * Due to different values for each source that uses these structures,
    109  * the ac97_query_devinfo function sets delta in mixer_devinfo_t using
    110  * ac97_source_info.bits.
    111  */
    112 static const struct audio_mixer_value ac97_volume_stereo = { { AudioNvolume },
    113 						       2 };
    114 
    115 static const struct audio_mixer_value ac97_volume_mono = { { AudioNvolume },
    116 						     1 };
    117 
    118 #define WRAP(a)  &a, sizeof(a)
    119 
    120 const struct ac97_source_info {
    121 	const char *class;
    122 	const char *device;
    123 	const char *qualifier;
    124 	int  type;
    125 
    126 	const void *info;
    127 	int  info_size;
    128 
    129 	u_int8_t  reg;
    130 	u_int16_t default_value;
    131 	u_int8_t  bits:3;
    132 	u_int8_t  ofs:4;
    133 	u_int8_t  mute:1;
    134 	u_int8_t  polarity:1;   /* Does 0 == MAX or MIN */
    135 
    136 	int  prev;
    137 	int  next;
    138 	int  mixer_class;
    139 } source_info[] = {
    140 	{ AudioCinputs ,            NULL,           NULL,    AUDIO_MIXER_CLASS,
    141 	},
    142 	{ AudioCoutputs,            NULL,           NULL,    AUDIO_MIXER_CLASS,
    143 	},
    144 	{ AudioCrecord ,            NULL,           NULL,    AUDIO_MIXER_CLASS,
    145 	},
    146 	/* Stereo master volume*/
    147 	{ AudioCoutputs,     AudioNmaster,        NULL,    AUDIO_MIXER_VALUE,
    148 	  WRAP(ac97_volume_stereo),
    149 	  AC97_REG_MASTER_VOLUME, 0x8000, 5, 0, 1,
    150 	},
    151 	/* Mono volume */
    152 	{ AudioCoutputs,       AudioNmono,        NULL,    AUDIO_MIXER_VALUE,
    153 	  WRAP(ac97_volume_mono),
    154 	  AC97_REG_MASTER_VOLUME_MONO, 0x8000, 6, 0, 1,
    155 	},
    156 	{ AudioCoutputs,       AudioNmono,AudioNsource,   AUDIO_MIXER_ENUM,
    157 	  WRAP(ac97_mono_select),
    158 	  AC97_REG_GP, 0x0000, 1, 9, 0,
    159 	},
    160 	/* Headphone volume */
    161 	{ AudioCoutputs,  AudioNheadphone,        NULL,    AUDIO_MIXER_VALUE,
    162 	  WRAP(ac97_volume_stereo),
    163 	  AC97_REG_HEADPHONE_VOLUME, 0x8000, 6, 0, 1,
    164 	},
    165 	/* Tone */
    166 	{ AudioCoutputs,           "tone",        NULL,    AUDIO_MIXER_VALUE,
    167 	  WRAP(ac97_volume_stereo),
    168 	  AC97_REG_MASTER_TONE, 0x0f0f, 4, 0, 0,
    169 	},
    170 	/* PC Beep Volume */
    171 	{ AudioCinputs,     AudioNspeaker,        NULL,    AUDIO_MIXER_VALUE,
    172 	  WRAP(ac97_volume_mono),
    173 	  AC97_REG_PCBEEP_VOLUME, 0x0000, 4, 1, 1,
    174 	},
    175 	/* Phone */
    176 	{ AudioCinputs,           "phone",        NULL,    AUDIO_MIXER_VALUE,
    177 	  WRAP(ac97_volume_mono),
    178 	  AC97_REG_PHONE_VOLUME, 0x8008, 5, 0, 1,
    179 	},
    180 	/* Mic Volume */
    181 	{ AudioCinputs,  AudioNmicrophone,        NULL,    AUDIO_MIXER_VALUE,
    182 	  WRAP(ac97_volume_mono),
    183 	  AC97_REG_MIC_VOLUME, 0x8008, 5, 0, 1,
    184 	},
    185 	{ AudioCinputs,  AudioNmicrophone, AudioNpreamp,   AUDIO_MIXER_ENUM,
    186 	  WRAP(ac97_on_off),
    187 	  AC97_REG_MIC_VOLUME, 0x8008, 1, 6, 0,
    188 	},
    189 	{ AudioCinputs,  AudioNmicrophone, AudioNsource,   AUDIO_MIXER_ENUM,
    190 	  WRAP(ac97_mic_select),
    191 	  AC97_REG_GP, 0x0000, 1, 8, 0,
    192 	},
    193 	/* Line in Volume */
    194 	{ AudioCinputs,        AudioNline,        NULL,    AUDIO_MIXER_VALUE,
    195 	  WRAP(ac97_volume_stereo),
    196 	  AC97_REG_LINEIN_VOLUME, 0x8808, 5, 0, 1,
    197 	},
    198 	/* CD Volume */
    199 	{ AudioCinputs,          AudioNcd,        NULL,    AUDIO_MIXER_VALUE,
    200 	  WRAP(ac97_volume_stereo),
    201 	  AC97_REG_CD_VOLUME, 0x8808, 5, 0, 1,
    202 	},
    203 	/* Video Volume */
    204 	{ AudioCinputs,           "video",        NULL,    AUDIO_MIXER_VALUE,
    205 	  WRAP(ac97_volume_stereo),
    206 	  AC97_REG_VIDEO_VOLUME, 0x8808, 5, 0, 1,
    207 	},
    208 	/* AUX volume */
    209 	{ AudioCinputs,         AudioNaux,        NULL,    AUDIO_MIXER_VALUE,
    210 	  WRAP(ac97_volume_stereo),
    211 	  AC97_REG_AUX_VOLUME, 0x8808, 5, 0, 1,
    212 	},
    213 	/* PCM out volume */
    214 	{ AudioCinputs,         AudioNdac,        NULL,    AUDIO_MIXER_VALUE,
    215 	  WRAP(ac97_volume_stereo),
    216 	  AC97_REG_PCMOUT_VOLUME, 0x8808, 5, 0, 1,
    217 	},
    218 	/* Record Source - some logic for this is hard coded - see below */
    219 	{ AudioCrecord,      AudioNsource,        NULL,    AUDIO_MIXER_ENUM,
    220 	  WRAP(ac97_source),
    221 	  AC97_REG_RECORD_SELECT, 0x0000, 3, 0, 0,
    222 	},
    223 	/* Record Gain */
    224 	{ AudioCrecord,      AudioNvolume,        NULL,    AUDIO_MIXER_VALUE,
    225 	  WRAP(ac97_volume_stereo),
    226 	  AC97_REG_RECORD_GAIN, 0x8000, 4, 0, 1,
    227 	},
    228 	/* Record Gain mic */
    229 	{ AudioCrecord,  AudioNmicrophone,        NULL,    AUDIO_MIXER_VALUE,
    230 	  WRAP(ac97_volume_mono),
    231 	  AC97_REG_RECORD_GAIN_MIC, 0x8000, 4, 0, 1, 1,
    232 	},
    233 	/* */
    234 	{ AudioCoutputs,   AudioNloudness,        NULL,    AUDIO_MIXER_ENUM,
    235 	  WRAP(ac97_on_off),
    236 	  AC97_REG_GP, 0x0000, 1, 12, 0,
    237 	},
    238 	{ AudioCoutputs,    AudioNspatial,        NULL,    AUDIO_MIXER_ENUM,
    239 	  WRAP(ac97_on_off),
    240 	  AC97_REG_GP, 0x0000, 1, 13, 0,
    241 	},
    242 	{ AudioCoutputs,    AudioNspatial,    "center",    AUDIO_MIXER_VALUE,
    243 	  WRAP(ac97_volume_mono),
    244 	  AC97_REG_3D_CONTROL, 0x0000, 4, 8, 0, 1,
    245 	},
    246 	{ AudioCoutputs,    AudioNspatial,     "depth",    AUDIO_MIXER_VALUE,
    247 	  WRAP(ac97_volume_mono),
    248 	  AC97_REG_3D_CONTROL, 0x0000, 4, 0, 0, 1,
    249 	},
    250 
    251 	/* Missing features: Simulated Stereo, POP, Loopback mode */
    252 } ;
    253 
    254 #define SOURCE_INFO_SIZE (sizeof(source_info)/sizeof(source_info[0]))
    255 
    256 /*
    257  * Check out http://developer.intel.com/pc-supp/platform/ac97/ for
    258  * information on AC-97
    259  */
    260 
    261 struct ac97_softc {
    262 	struct ac97_codec_if codec_if;
    263 
    264 	struct ac97_host_if *host_if;
    265 
    266 	struct ac97_source_info source_info[2 * SOURCE_INFO_SIZE];
    267 	int num_source_info;
    268 
    269 	enum ac97_host_flags host_flags;
    270 
    271 	u_int16_t shadow_reg[128];
    272 };
    273 
    274 int ac97_mixer_get_port __P((struct ac97_codec_if *self, mixer_ctrl_t *cp));
    275 int ac97_mixer_set_port __P((struct ac97_codec_if *self, mixer_ctrl_t *));
    276 int ac97_query_devinfo __P((struct ac97_codec_if *self, mixer_devinfo_t *));
    277 int ac97_get_portnum_by_name __P((struct ac97_codec_if *, char *, char *,
    278 				  char *));
    279 void ac97_restore_shadow __P((struct ac97_codec_if *self));
    280 
    281 struct ac97_codec_if_vtbl ac97civ = {
    282 	ac97_mixer_get_port,
    283 	ac97_mixer_set_port,
    284 	ac97_query_devinfo,
    285 	ac97_get_portnum_by_name,
    286 	ac97_restore_shadow,
    287 };
    288 
    289 static const struct ac97_codecid {
    290 	u_int32_t id;
    291 	const char *name;
    292 } ac97codecid[] = {
    293 	{ AC97_CODEC_ID('A', 'D', 'S', 3),	"Analog Devices AD1819B" },
    294 	{ AC97_CODEC_ID('A', 'D', 'S', 64),	"Analog Devices AD1881" },
    295 	{ AC97_CODEC_ID('A', 'D', 'S', 72),	"Analog Devices AD1881A" },
    296 	{ AC97_CODEC_ID('A', 'D', 'S', 96),	"Analog Devices AD1885" },
    297 	{ AC97_CODEC_ID('A', 'D', 'S', 99),	"Analog Devices AD1886A" },
    298 	{ AC97_CODEC_ID('A', 'K', 'M', 0),	"Asahi Kasei AK4540"	},
    299 	{ AC97_CODEC_ID('A', 'K', 'M', 2),	"Asahi Kasei AK4543"	},
    300 	{ AC97_CODEC_ID('A', 'L', 'G', 16),	"Advance Logic ALC200"	},
    301 	{ AC97_CODEC_ID('A', 'L', 'G', 32),	"Advance Logic ALC650"	},
    302 	{ AC97_CODEC_ID('A', 'L', 'G', 48),	"Advance Logic ALC101"	},
    303 	{ AC97_CODEC_ID('A', 'L', 'G', 64),	"Advance Logic ALC202"	},
    304 	{ AC97_CODEC_ID('A', 'L', 'G', 80),	"Advance Logic ALC250"	},
    305 	{ AC97_CODEC_ID('C', 'R', 'Y', 0),	"Crystal CS4297"	},
    306 	{ AC97_CODEC_ID('C', 'R', 'Y', 3),	"Crystal CS4297"	},
    307 	{ AC97_CODEC_ID('C', 'R', 'Y', 19),	"Crystal CS4297A"	},
    308 	{ AC97_CODEC_ID('C', 'R', 'Y', 35),	"Crystal CS4298",	},
    309 	{ AC97_CODEC_ID('C', 'R', 'Y', 43),	"Crystal CS4294",	},
    310 	{ AC97_CODEC_ID('C', 'R', 'Y', 49),	"Crystal CS4299",	},
    311 	{ AC97_CODEC_ID('C', 'R', 'Y', 51),	"Crystal CS4298A",	},
    312 	{ AC97_CODEC_ID('C', 'R', 'Y', 52),	"Crystal CS4299",	},
    313 	{ AC97_CODEC_ID('N', 'S', 'C', 49), "National Semiconductor LM4549", },
    314 	{ AC97_CODEC_ID('S', 'I', 'L', 34),	"Silicon Laboratory Si3036", },
    315 	{ AC97_CODEC_ID('S', 'I', 'L', 35),	"Silicon Laboratory Si3038", },
    316 	{ AC97_CODEC_ID('T', 'R', 'A', 2),	"TriTech TR28022",	},
    317 	{ AC97_CODEC_ID('T', 'R', 'A', 3),	"TriTech TR28023",	},
    318 	{ AC97_CODEC_ID('T', 'R', 'A', 6),	"TriTech TR28026",	},
    319 	{ AC97_CODEC_ID('T', 'R', 'A', 8),	"TriTech TR28028",	},
    320 	{ AC97_CODEC_ID('T', 'R', 'A', 35),	"TriTech unknown",	},
    321 	{ AC97_CODEC_ID('W', 'M', 'L', 0),	"Wolfson WM9704",	},
    322 	{ AC97_CODEC_ID('W', 'M', 'L', 3),	"Wolfson WM9707",	},
    323 	{ 0x45838308,				"ESS Technology ES1921", },
    324 	{ 0x83847600,				"SigmaTel STAC9700",	},
    325 	{ 0x83847604,				"SigmaTel STAC9701/3/4/5", },
    326 	{ 0x83847605,				"SigmaTel STAC9704", 	},
    327 	{ 0x83847608,				"SigmaTel STAC9708", 	},
    328 	{ 0x83847609,				"SigmaTel STAC9721/23",	},
    329 	{ 0x83847644,				"SigmaTel STAC9744/45",	},
    330 	{ 0x83847684,				"SigmaTel STAC9783/84",	},
    331 	{ 0,					NULL,			}
    332 };
    333 
    334 static const char * const ac97enhancement[] = {
    335 	"no 3D stereo",
    336 	"Analog Devices Phat Stereo",
    337 	"Creative",
    338 	"National Semi 3D",
    339 	"Yamaha Ymersion",
    340 	"BBE 3D",
    341 	"Crystal Semi 3D",
    342 	"Qsound QXpander",
    343 	"Spatializer 3D",
    344 	"SRS 3D",
    345 	"Platform Tech 3D",
    346 	"AKM 3D",
    347 	"Aureal",
    348 	"AZTECH 3D",
    349 	"Binaura 3D",
    350 	"ESS Technology",
    351 	"Harman International VMAx",
    352 	"Nvidea 3D",
    353 	"Philips Incredible Sound",
    354 	"Texas Instruments' 3D",
    355 	"VLSI Technology 3D",
    356 	"TriTech 3D",
    357 	"Realtek 3D",
    358 	"Samsung 3D",
    359 	"Wolfson Microelectronics 3D",
    360 	"Delta Integration 3D",
    361 	"SigmaTel 3D",
    362 	"Unknown 3D",
    363 	"Rockwell 3D",
    364 	"Unknown 3D",
    365 	"Unknown 3D",
    366 	"Unknown 3D",
    367 };
    368 
    369 static const char * const ac97feature[] = {
    370 	"mic channel",
    371 	"reserved",
    372 	"tone",
    373 	"simulated stereo",
    374 	"headphone",
    375 	"bass boost",
    376 	"18 bit DAC",
    377 	"20 bit DAC",
    378 	"18 bit ADC",
    379 	"20 bit ADC"
    380 };
    381 
    382 
    383 int ac97_str_equal __P((const char *, const char *));
    384 void ac97_setup_source_info __P((struct ac97_softc *));
    385 void ac97_read __P((struct ac97_softc *, u_int8_t, u_int16_t *));
    386 void ac97_setup_defaults __P((struct ac97_softc *));
    387 int ac97_write __P((struct ac97_softc *, u_int8_t, u_int16_t));
    388 
    389 /* #define AC97_DEBUG 10 */
    390 
    391 #ifdef AUDIO_DEBUG
    392 #define DPRINTF(x)	if (ac97debug) printf x
    393 #define DPRINTFN(n,x)	if (ac97debug>(n)) printf x
    394 #ifdef AC97_DEBUG
    395 int	ac97debug = AC97_DEBUG;
    396 #else
    397 int	ac97debug = 0;
    398 #endif
    399 #else
    400 #define DPRINTF(x)
    401 #define DPRINTFN(n,x)
    402 #endif
    403 
    404 void
    405 ac97_read(as, reg, val)
    406 	struct ac97_softc *as;
    407 	u_int8_t reg;
    408 	u_int16_t *val;
    409 {
    410 	int error;
    411 
    412 	if (as->host_flags & AC97_HOST_DONT_READ &&
    413 	    (reg != AC97_REG_VENDOR_ID1 && reg != AC97_REG_VENDOR_ID2 &&
    414 	     reg != AC97_REG_RESET)) {
    415 		*val = as->shadow_reg[reg >> 1];
    416 		return;
    417 	}
    418 
    419 	if ((error = as->host_if->read(as->host_if->arg, reg, val))) {
    420 		*val = as->shadow_reg[reg >> 1];
    421 	}
    422 }
    423 
    424 int
    425 ac97_write(as, reg, val)
    426 	struct ac97_softc *as;
    427 	u_int8_t reg;
    428 	u_int16_t val;
    429 {
    430 
    431 	as->shadow_reg[reg >> 1] = val;
    432 
    433 	return (as->host_if->write(as->host_if->arg, reg, val));
    434 }
    435 
    436 void
    437 ac97_setup_defaults(as)
    438 	struct ac97_softc *as;
    439 {
    440 	int idx;
    441 	const struct ac97_source_info *si;
    442 
    443 	memset(as->shadow_reg, 0, sizeof(as->shadow_reg));
    444 
    445 	for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
    446 		si = &source_info[idx];
    447 		ac97_write(as, si->reg, si->default_value);
    448 	}
    449 }
    450 
    451 void
    452 ac97_restore_shadow(self)
    453 	struct ac97_codec_if *self;
    454 {
    455 	struct ac97_softc *as = (struct ac97_softc *) self;
    456 	int idx;
    457 	const struct ac97_source_info *si;
    458 
    459 	for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
    460 		si = &source_info[idx];
    461 		ac97_write(as, si->reg, as->shadow_reg[si->reg >> 1]);
    462 	}
    463 }
    464 
    465 int
    466 ac97_str_equal(a, b)
    467 	const char *a, *b;
    468 {
    469 	return ((a == b) || (a && b && (!strcmp(a, b))));
    470 }
    471 
    472 void
    473 ac97_setup_source_info(as)
    474 	struct ac97_softc *as;
    475 {
    476 	int idx, ouridx;
    477 	struct ac97_source_info *si, *si2;
    478 
    479 	for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
    480 		si = &as->source_info[ouridx];
    481 
    482 		memcpy(si, &source_info[idx], sizeof(*si));
    483 
    484 		switch (si->type) {
    485 		case AUDIO_MIXER_CLASS:
    486 		        si->mixer_class = ouridx;
    487 			ouridx++;
    488 			break;
    489 		case AUDIO_MIXER_VALUE:
    490 			/* Todo - Test to see if it works */
    491 			ouridx++;
    492 
    493 			/* Add an entry for mute, if necessary */
    494 			if (si->mute) {
    495 				si = &as->source_info[ouridx];
    496 				memcpy(si, &source_info[idx], sizeof(*si));
    497 				si->qualifier = AudioNmute;
    498 				si->type = AUDIO_MIXER_ENUM;
    499 				si->info = &ac97_on_off;
    500 				si->info_size = sizeof(ac97_on_off);
    501 				si->bits = 1;
    502 				si->ofs = 15;
    503 				si->mute = 0;
    504 				si->polarity = 0;
    505 				ouridx++;
    506 			}
    507 			break;
    508 		case AUDIO_MIXER_ENUM:
    509 			/* Todo - Test to see if it works */
    510 			ouridx++;
    511 			break;
    512 		default:
    513 			printf ("ac97: shouldn't get here\n");
    514 			break;
    515 		}
    516 	}
    517 
    518 	as->num_source_info = ouridx;
    519 
    520 	for (idx = 0; idx < as->num_source_info; idx++) {
    521 		int idx2, previdx;
    522 
    523 		si = &as->source_info[idx];
    524 
    525 		/* Find mixer class */
    526 		for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
    527 			si2 = &as->source_info[idx2];
    528 
    529 			if (si2->type == AUDIO_MIXER_CLASS &&
    530 			    ac97_str_equal(si->class,
    531 					   si2->class)) {
    532 				si->mixer_class = idx2;
    533 			}
    534 		}
    535 
    536 
    537 		/* Setup prev and next pointers */
    538 		if (si->prev != 0)
    539 			continue;
    540 
    541 		if (si->qualifier)
    542 			continue;
    543 
    544 		si->prev = AUDIO_MIXER_LAST;
    545 		previdx = idx;
    546 
    547 		for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
    548 			if (idx2 == idx)
    549 				continue;
    550 
    551 			si2 = &as->source_info[idx2];
    552 
    553 			if (!si2->prev &&
    554 			    ac97_str_equal(si->class, si2->class) &&
    555 			    ac97_str_equal(si->device, si2->device)) {
    556 				as->source_info[previdx].next = idx2;
    557 				as->source_info[idx2].prev = previdx;
    558 
    559 				previdx = idx2;
    560 			}
    561 		}
    562 
    563 		as->source_info[previdx].next = AUDIO_MIXER_LAST;
    564 	}
    565 }
    566 
    567 int
    568 ac97_attach(host_if)
    569 	struct ac97_host_if *host_if;
    570 {
    571 	struct ac97_softc *as;
    572 	struct device *sc_dev = (struct device *)host_if->arg;
    573 	int error, i, j;
    574 	u_int16_t id1, id2, caps;
    575 	u_int32_t id;
    576 	mixer_ctrl_t ctl;
    577 
    578 	as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_WAITOK|M_ZERO);
    579 
    580 	if (as == NULL)
    581 		return (ENOMEM);
    582 
    583 	as->codec_if.vtbl = &ac97civ;
    584 	as->host_if = host_if;
    585 
    586 	if ((error = host_if->attach(host_if->arg, &as->codec_if))) {
    587 		free (as, M_DEVBUF);
    588 		return (error);
    589 	}
    590 
    591 	host_if->reset(host_if->arg);
    592 
    593 	host_if->write(host_if->arg, AC97_REG_POWER, 0);
    594 	host_if->write(host_if->arg, AC97_REG_RESET, 0);
    595 
    596 	if (host_if->flags)
    597 		as->host_flags = host_if->flags(host_if->arg);
    598 
    599 	ac97_setup_defaults(as);
    600 	ac97_read(as, AC97_REG_VENDOR_ID1, &id1);
    601 	ac97_read(as, AC97_REG_VENDOR_ID2, &id2);
    602 	ac97_read(as, AC97_REG_RESET, &caps);
    603 
    604 	id = (id1 << 16) | id2;
    605 
    606 	printf("%s: ", sc_dev->dv_xname);
    607 
    608 	for (i = 0; ; i++) {
    609 		if (ac97codecid[i].id == 0) {
    610 			char pnp[4];
    611 
    612 			AC97_GET_CODEC_ID(id, pnp);
    613 #define ISASCII(c) ((c) >= ' ' && (c) < 0x7f)
    614 			if (ISASCII(pnp[0]) && ISASCII(pnp[1]) &&
    615 			    ISASCII(pnp[2]))
    616 				printf("%c%c%c%d", pnp[0], pnp[1], pnp[2],
    617 				    pnp[3]);
    618 			else
    619 				printf("unknown (0x%08x)", id);
    620 			break;
    621 		}
    622 		if (ac97codecid[i].id == id) {
    623 			printf("%s", ac97codecid[i].name);
    624 			break;
    625 		}
    626 	}
    627 	printf(" codec; ");
    628 	for (i = j = 0; i < 10; i++) {
    629 		if (caps & (1 << i)) {
    630 			printf("%s%s", j? ", " : "", ac97feature[i]);
    631 			j++;
    632 		}
    633 	}
    634 
    635 	printf("%s%s\n", j? ", " : "", ac97enhancement[(caps >> 10) & 0x1f]);
    636 
    637 	ac97_setup_source_info(as);
    638 
    639 	/* Just enable the DAC and master volumes by default */
    640 	memset(&ctl, 0, sizeof(ctl));
    641 
    642 	ctl.type = AUDIO_MIXER_ENUM;
    643 	ctl.un.ord = 0;  /* off */
    644 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
    645 					   AudioNmaster, AudioNmute);
    646 	ac97_mixer_set_port(&as->codec_if, &ctl);
    647 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCinputs,
    648 					   AudioNdac, AudioNmute);
    649 
    650 	ac97_mixer_set_port(&as->codec_if, &ctl);
    651 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
    652 					   AudioNvolume, AudioNmute);
    653 	ac97_mixer_set_port(&as->codec_if, &ctl);
    654 
    655 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
    656 					   AudioNsource, NULL);
    657 	ctl.type = AUDIO_MIXER_ENUM;
    658 	ctl.un.ord = 0;
    659 	ac97_mixer_set_port(&as->codec_if, &ctl);
    660 
    661 	return (0);
    662 }
    663 
    664 
    665 int
    666 ac97_query_devinfo(codec_if, dip)
    667 	struct ac97_codec_if *codec_if;
    668 	mixer_devinfo_t *dip;
    669 {
    670 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    671 
    672 	if (dip->index < as->num_source_info) {
    673 		struct ac97_source_info *si = &as->source_info[dip->index];
    674 		const char *name;
    675 
    676 		dip->type = si->type;
    677 		dip->mixer_class = si->mixer_class;
    678 		dip->prev = si->prev;
    679 		dip->next = si->next;
    680 
    681 		if (si->qualifier)
    682 			name = si->qualifier;
    683 		else if (si->device)
    684 			name = si->device;
    685 		else if (si->class)
    686 			name = si->class;
    687 		else
    688 			name = 0;
    689 
    690 		if (name)
    691 			strcpy(dip->label.name, name);
    692 
    693 		memcpy(&dip->un, si->info, si->info_size);
    694 
    695 		/* Set the delta for volume sources */
    696 		if (dip->type == AUDIO_MIXER_VALUE)
    697 			dip->un.v.delta = 1 << (8 - si->bits);
    698 
    699 		return (0);
    700 	}
    701 
    702 	return (ENXIO);
    703 }
    704 
    705 
    706 
    707 int
    708 ac97_mixer_set_port(codec_if, cp)
    709 	struct ac97_codec_if *codec_if;
    710 	mixer_ctrl_t *cp;
    711 {
    712 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    713 	struct ac97_source_info *si = &as->source_info[cp->dev];
    714 	u_int16_t mask;
    715 	u_int16_t val, newval;
    716 	int error;
    717 
    718 	if (cp->dev < 0 || cp->dev >= as->num_source_info)
    719 		return (EINVAL);
    720 
    721 	if (cp->type != si->type)
    722 		return (EINVAL);
    723 
    724 	ac97_read(as, si->reg, &val);
    725 
    726 	DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
    727 
    728 	mask = (1 << si->bits) - 1;
    729 
    730 	switch (cp->type) {
    731 	case AUDIO_MIXER_ENUM:
    732 		if (cp->un.ord > mask || cp->un.ord < 0)
    733 			return (EINVAL);
    734 
    735 		newval = (cp->un.ord << si->ofs);
    736 		if (si->reg == AC97_REG_RECORD_SELECT) {
    737 			newval |= (newval << (8 + si->ofs));
    738 			mask |= (mask << 8);
    739 		}
    740 		break;
    741 	case AUDIO_MIXER_VALUE:
    742 	{
    743 		const struct audio_mixer_value *value = si->info;
    744 		u_int16_t  l, r;
    745 
    746 		if ((cp->un.value.num_channels <= 0) ||
    747 		    (cp->un.value.num_channels > value->num_channels))
    748 			return (EINVAL);
    749 
    750 		if (cp->un.value.num_channels == 1) {
    751 			l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
    752 		} else {
    753 			if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
    754 				l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
    755 				r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
    756 			} else {	/* left/right is reversed here */
    757 				r = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
    758 				l = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
    759 			}
    760 
    761 		}
    762 
    763 		if (!si->polarity) {
    764 			l = 255 - l;
    765 			r = 255 - r;
    766 		}
    767 
    768 		l = l >> (8 - si->bits);
    769 		r = r >> (8 - si->bits);
    770 
    771 		newval = ((l & mask) << si->ofs);
    772 		if (value->num_channels == 2) {
    773 			newval |= ((r & mask) << (si->ofs + 8));
    774 			mask |= (mask << 8);
    775 		}
    776 
    777 		break;
    778 	}
    779 	default:
    780 		return (EINVAL);
    781 	}
    782 
    783 	mask = mask << si->ofs;
    784 	error = ac97_write(as, si->reg, (val & ~mask) | newval);
    785 	if (error)
    786 		return (error);
    787 
    788 	return (0);
    789 }
    790 
    791 int
    792 ac97_get_portnum_by_name(codec_if, class, device, qualifier)
    793 	struct ac97_codec_if *codec_if;
    794 	char *class, *device, *qualifier;
    795 {
    796 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    797 	int idx;
    798 
    799 	for (idx = 0; idx < as->num_source_info; idx++) {
    800 		struct ac97_source_info *si = &as->source_info[idx];
    801 		if (ac97_str_equal(class, si->class) &&
    802 		    ac97_str_equal(device, si->device) &&
    803 		    ac97_str_equal(qualifier, si->qualifier))
    804 			return (idx);
    805 	}
    806 
    807 	return (-1);
    808 }
    809 
    810 int
    811 ac97_mixer_get_port(codec_if, cp)
    812 	struct ac97_codec_if *codec_if;
    813 	mixer_ctrl_t *cp;
    814 {
    815 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    816 	struct ac97_source_info *si = &as->source_info[cp->dev];
    817 	u_int16_t mask;
    818 	u_int16_t val;
    819 
    820 	if (cp->dev < 0 || cp->dev >= as->num_source_info)
    821 		return (EINVAL);
    822 
    823 	if (cp->type != si->type)
    824 		return (EINVAL);
    825 
    826 	ac97_read(as, si->reg, &val);
    827 
    828 	DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
    829 
    830 	mask = (1 << si->bits) - 1;
    831 
    832 	switch (cp->type) {
    833 	case AUDIO_MIXER_ENUM:
    834 		cp->un.ord = (val >> si->ofs) & mask;
    835 		DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs, mask, cp->un.ord));
    836 		break;
    837 	case AUDIO_MIXER_VALUE:
    838 	{
    839 		const struct audio_mixer_value *value = si->info;
    840 		u_int16_t  l, r;
    841 
    842 		if ((cp->un.value.num_channels <= 0) ||
    843 		    (cp->un.value.num_channels > value->num_channels))
    844 			return (EINVAL);
    845 
    846 		if (value->num_channels == 1) {
    847 			l = r = (val >> si->ofs) & mask;
    848 		} else {
    849 			if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
    850 				l = (val >> si->ofs) & mask;
    851 				r = (val >> (si->ofs + 8)) & mask;
    852 			} else {	/* host has reversed channels */
    853 				r = (val >> si->ofs) & mask;
    854 				l = (val >> (si->ofs + 8)) & mask;
    855 			}
    856 		}
    857 
    858 		l = (l << (8 - si->bits));
    859 		r = (r << (8 - si->bits));
    860 		if (!si->polarity) {
    861 			l = 255 - l;
    862 			r = 255 - r;
    863 		}
    864 
    865 		/* The EAP driver averages l and r for stereo
    866 		   channels that are requested in MONO mode. Does this
    867 		   make sense? */
    868 		if (cp->un.value.num_channels == 1) {
    869 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
    870 		} else if (cp->un.value.num_channels == 2) {
    871 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
    872 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
    873 		}
    874 
    875 		break;
    876 	}
    877 	default:
    878 		return (EINVAL);
    879 	}
    880 
    881 	return (0);
    882 }
    883 
    884