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