Home | History | Annotate | Line # | Download | only in ic
ac97.c revision 1.11
      1 /*      $NetBSD: ac97.c,v 1.11 2000/06/06 17:25:52 soren 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', 'K', 'M', 0),	"Asahi Kasei AK4540"	},
    277 #if 0
    278 	{ AC97_CODEC_ID('C', 'R', 'Y', 0),	"Crystal CS4297"	},
    279 #endif
    280 	{ AC97_CODEC_ID('C', 'R', 'Y', 3),	"Crystal CS4297"	},
    281 	{ AC97_CODEC_ID('C', 'R', 'Y', 19),	"Crystal CS4297A"	},
    282 	{ 0x83847600,				"SigmaTel STAC????"	},
    283 	{ 0x83847604,				"SigmaTel STAC9701/3/4/5" },
    284 	{ 0x83847605,				"SigmaTel STAC9704" 	},
    285 	{ 0x83847608,				"SigmaTel STAC9708" 	},
    286 	{ 0x83847609,				"SigmaTel STAC9721" 	},
    287 	{ 0,					NULL			}
    288 };
    289 
    290 static char *ac97enhancement[] = {
    291 	"no 3D stereo",
    292 	"Analog Devices Phat Stereo",
    293 	"Creative"
    294 	"National Semi 3D",
    295 	"Yamaha Ymersion",
    296 	"BBE 3D",
    297 	"Crystal Semi 3D"
    298 	"Qsound QXpander",
    299 	"Spatializer 3D",
    300 	"SRS 3D",
    301 	"Platform Tech 3D",
    302 	"AKM 3D",
    303 	"Aureal",
    304 	"AZTECH 3D",
    305 	"Binaura 3D",
    306 	"ESS Technology",
    307 	"Harman International VMAx",
    308 	"Nvidea 3D",
    309 	"Philips Incredible Sound",
    310 	"Texas Instruments' 3D",
    311 	"VLSI Technology 3D",
    312 	"TriTech 3D",
    313 	"Realtek 3D",
    314 	"Samsung 3D",
    315 	"Wolfson Microelectronics 3D",
    316 	"Delta Integration 3D",
    317 	"SigmaTel 3D",
    318 	"Unknown 3D",
    319 	"Rockwell 3D",
    320 	"Unknown 3D",
    321 	"Unknown 3D",
    322 	"Unknown 3D",
    323 };
    324 
    325 static char *ac97feature[] = {
    326 	"mic channel",
    327 	"reserved",
    328 	"tone",
    329 	"simulated stereo",
    330 	"headphone",
    331 	"bass boost",
    332 	"18 bit DAC",
    333 	"20 bit DAC",
    334 	"18 bit ADC",
    335 	"20 bit ADC"
    336 };
    337 
    338 
    339 int ac97_str_equal __P((char *, char *));
    340 void ac97_setup_source_info __P((struct ac97_softc *));
    341 
    342 /* #define AC97_DEBUG 10 */
    343 
    344 #ifdef AUDIO_DEBUG
    345 #define DPRINTF(x)	if (ac97debug) printf x
    346 #define DPRINTFN(n,x)	if (ac97debug>(n)) printf x
    347 #ifdef AC97_DEBUG
    348 int	ac97debug = AC97_DEBUG;
    349 #else
    350 int	ac97debug = 0;
    351 #endif
    352 #else
    353 #define DPRINTF(x)
    354 #define DPRINTFN(n,x)
    355 #endif
    356 
    357 
    358 int
    359 ac97_str_equal(a, b)
    360 	char *a, *b;
    361 {
    362 	return ((a == b) || (a && b && (!strcmp(a, b))));
    363 }
    364 
    365 void
    366 ac97_setup_source_info(as)
    367 	struct ac97_softc *as;
    368 {
    369 	int idx, ouridx;
    370 	struct ac97_source_info *si, *si2;
    371 
    372 	for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
    373 		si = &as->source_info[ouridx];
    374 
    375 		bcopy(&source_info[idx], si, sizeof(*si));
    376 
    377 		switch (si->type) {
    378 		case AUDIO_MIXER_CLASS:
    379 		        si->mixer_class = ouridx;
    380 			ouridx++;
    381 			break;
    382 		case AUDIO_MIXER_VALUE:
    383 			/* Todo - Test to see if it works */
    384 			ouridx++;
    385 
    386 			/* Add an entry for mute, if necessary */
    387 			if (si->mute) {
    388 				si = &as->source_info[ouridx];
    389 				bcopy(&source_info[idx], si, sizeof(*si));
    390 				si->qualifier = AudioNmute;
    391 				si->type = AUDIO_MIXER_ENUM;
    392 				si->info = &ac97_on_off;
    393 				si->info_size = sizeof(ac97_on_off);
    394 				si->bits = 1;
    395 				si->ofs = 15;
    396 				si->mute = 0;
    397 				si->polarity = 0;
    398 				ouridx++;
    399 			}
    400 			break;
    401 		case AUDIO_MIXER_ENUM:
    402 			/* Todo - Test to see if it works */
    403 			ouridx++;
    404 			break;
    405 		default:
    406 			printf ("ac97: shouldn't get here\n");
    407 			break;
    408 		}
    409 	}
    410 
    411 	as->num_source_info = ouridx;
    412 
    413 	for (idx = 0; idx < as->num_source_info; idx++) {
    414 		int idx2, previdx;
    415 
    416 		si = &as->source_info[idx];
    417 
    418 		/* Find mixer class */
    419 		for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
    420 			si2 = &as->source_info[idx2];
    421 
    422 			if (si2->type == AUDIO_MIXER_CLASS &&
    423 			    ac97_str_equal(si->class,
    424 					   si2->class)) {
    425 				si->mixer_class = idx2;
    426 			}
    427 		}
    428 
    429 
    430 		/* Setup prev and next pointers */
    431 		if (si->prev != 0)
    432 			continue;
    433 
    434 		if (si->qualifier)
    435 			continue;
    436 
    437 		si->prev = AUDIO_MIXER_LAST;
    438 		previdx = idx;
    439 
    440 		for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
    441 			if (idx2 == idx)
    442 				continue;
    443 
    444 			si2 = &as->source_info[idx2];
    445 
    446 			if (!si2->prev &&
    447 			    ac97_str_equal(si->class, si2->class) &&
    448 			    ac97_str_equal(si->device, si2->device)) {
    449 				as->source_info[previdx].next = idx2;
    450 				as->source_info[idx2].prev = previdx;
    451 
    452 				previdx = idx2;
    453 			}
    454 		}
    455 
    456 		as->source_info[previdx].next = AUDIO_MIXER_LAST;
    457 	}
    458 }
    459 
    460 int
    461 ac97_attach(hostIf)
    462 	struct ac97_host_if *hostIf;
    463 {
    464 	struct ac97_softc *as;
    465 	struct device *sc_dev = (struct device *)hostIf->arg;
    466 	int error, i, j;
    467 	u_int16_t id1, id2, caps;
    468 	u_int32_t id;
    469 	mixer_ctrl_t ctl;
    470 
    471 	as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_WAITOK);
    472 
    473 	if (as == NULL)
    474 		return (ENOMEM);
    475 
    476 	as->codecIf.vtbl = &ac97civ;
    477 	as->hostIf = hostIf;
    478 
    479 	if ((error = hostIf->attach(hostIf->arg, &as->codecIf))) {
    480 		free (as, M_DEVBUF);
    481 		return (error);
    482 	}
    483 
    484 	hostIf->reset(hostIf->arg);
    485 
    486 	hostIf->write(hostIf->arg, AC97_REG_POWER, 0);
    487 	hostIf->write(hostIf->arg, AC97_REG_RESET, 0);
    488 
    489 	if ((error = hostIf->read(hostIf->arg, AC97_REG_VENDOR_ID1, &id1)))
    490 		return (error);
    491 
    492 	if ((error = hostIf->read(hostIf->arg, AC97_REG_VENDOR_ID2, &id2)))
    493 		return (error);
    494 
    495 	if ((error = hostIf->read(hostIf->arg, AC97_REG_RESET, &caps)))
    496 		return (error);
    497 
    498 	id = (id1 << 16) | id2;
    499 
    500 	printf("%s: ", sc_dev->dv_xname);
    501 
    502 	for (i = 0; ; i++) {
    503 		if (ac97codecid[i].id == 0) {
    504 			char pnp[4];
    505 
    506 			AC97_GET_CODEC_ID(id, pnp);
    507 #define ISASCII(c) ((c) >= ' ' && (c) < 0x7f)
    508 			if (ISASCII(pnp[0]) && ISASCII(pnp[1]) &&
    509 			    ISASCII(pnp[2]))
    510 				printf("%c%c%c%d", pnp[0], pnp[1], pnp[2],
    511 				    pnp[3]);
    512 			else
    513 				printf("unknown (0x%8x)", id);
    514 			break;
    515 		}
    516 		if (ac97codecid[i].id == id) {
    517 			printf("%s", ac97codecid[i].name);
    518 			break;
    519 		}
    520 	}
    521 	printf(" codec; ");
    522 	for (i = j = 0; i < 10; i++) {
    523 		if (caps & (1 << i)) {
    524 			printf("%s%s", j? ", " : "", ac97feature[i]);
    525 			j++;
    526 		}
    527 	}
    528 
    529 	printf("%s%s\n", j? ", " : "", ac97enhancement[(caps >> 10) & 0x1f]);
    530 
    531 	ac97_setup_source_info(as);
    532 
    533 	/* Just enable the DAC and master volumes by default */
    534 	memset(&ctl, 0, sizeof(ctl));
    535 
    536 	ctl.type = AUDIO_MIXER_ENUM;
    537 	ctl.un.ord = 0;  /* off */
    538 	ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCoutputs,
    539 					   AudioNmaster, AudioNmute);
    540 	ac97_mixer_set_port(&as->codecIf, &ctl);
    541 	ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCinputs,
    542 					   AudioNdac, AudioNmute);
    543 
    544 	ac97_mixer_set_port(&as->codecIf, &ctl);
    545 	ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCrecord,
    546 					   AudioNvolume, AudioNmute);
    547 	ac97_mixer_set_port(&as->codecIf, &ctl);
    548 
    549 	ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCrecord,
    550 					   AudioNsource, NULL);
    551 	ctl.type = AUDIO_MIXER_ENUM;
    552 	ctl.un.ord = 0;
    553 	ac97_mixer_set_port(&as->codecIf, &ctl);
    554 
    555 	return (0);
    556 }
    557 
    558 
    559 int
    560 ac97_query_devinfo(codec_if, dip)
    561 	struct ac97_codec_if *codec_if;
    562 	mixer_devinfo_t *dip;
    563 {
    564 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    565 
    566 	if (dip->index < as->num_source_info) {
    567 		struct ac97_source_info *si = &as->source_info[dip->index];
    568 		char *name;
    569 
    570 		dip->type = si->type;
    571 		dip->mixer_class = si->mixer_class;
    572 		dip->prev = si->prev;
    573 		dip->next = si->next;
    574 
    575 		if (si->qualifier)
    576 			name = si->qualifier;
    577 		else if (si->device)
    578 			name = si->device;
    579 		else if (si->class)
    580 			name = si->class;
    581 		else
    582 			name = 0;
    583 
    584 		if (name)
    585 			strcpy(dip->label.name, name);
    586 
    587 		bcopy(si->info, &dip->un, si->info_size);
    588 		return (0);
    589 	}
    590 
    591 	return (ENXIO);
    592 }
    593 
    594 
    595 
    596 int
    597 ac97_mixer_set_port(codec_if, cp)
    598 	struct ac97_codec_if *codec_if;
    599 	mixer_ctrl_t *cp;
    600 {
    601 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    602 	struct ac97_source_info *si = &as->source_info[cp->dev];
    603 	u_int16_t mask;
    604 	u_int16_t val, newval;
    605 	int error;
    606 
    607 	if (cp->dev < 0 || cp->dev >= as->num_source_info)
    608 		return (EINVAL);
    609 
    610 	if (cp->type != si->type)
    611 		return (EINVAL);
    612 
    613 	error = as->hostIf->read(as->hostIf->arg, si->reg, &val);
    614 	if (error)
    615 		return (error);
    616 
    617 	DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
    618 
    619 	mask = (1 << si->bits) - 1;
    620 
    621 	switch (cp->type) {
    622 	case AUDIO_MIXER_ENUM:
    623 		if (cp->un.ord > mask || cp->un.ord < 0)
    624 			return (EINVAL);
    625 
    626 		newval = (cp->un.ord << si->ofs);
    627 		if (si->reg == AC97_REG_RECORD_SELECT) {
    628 			newval |= (newval << (8 + si->ofs));
    629 			mask |= (mask << 8);
    630 		}
    631 		break;
    632 	case AUDIO_MIXER_VALUE:
    633 	{
    634 		struct audio_mixer_value *value = si->info;
    635 		u_int16_t  l, r;
    636 
    637 		if ((cp->un.value.num_channels <= 0) ||
    638 		    (cp->un.value.num_channels > value->num_channels))
    639 			return (EINVAL);
    640 
    641 		if (cp->un.value.num_channels == 1) {
    642 			l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
    643 		} else {
    644 			l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
    645 			r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
    646 		}
    647 
    648 		if (!si->polarity) {
    649 			l = 255 - l;
    650 			r = 255 - r;
    651 		}
    652 
    653 		l = l >> (8 - si->bits);
    654 		r = r >> (8 - si->bits);
    655 
    656 		newval = ((l & mask) << si->ofs);
    657 		if (value->num_channels == 2) {
    658 			newval |= ((r & mask) << (si->ofs + 8));
    659 			mask |= (mask << 8);
    660 		}
    661 
    662 		break;
    663 	}
    664 	default:
    665 		return (EINVAL);
    666 	}
    667 
    668 	mask = mask << si->ofs;
    669 	error = as->hostIf->write(as->hostIf->arg, si->reg, (val & ~mask) | newval);
    670 	if (error)
    671 		return (error);
    672 
    673 	return (0);
    674 }
    675 
    676 int
    677 ac97_get_portnum_by_name(codec_if, class, device, qualifier)
    678 	struct ac97_codec_if *codec_if;
    679 	char *class, *device, *qualifier;
    680 {
    681 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    682 	int idx;
    683 
    684 	for (idx = 0; idx < as->num_source_info; idx++) {
    685 		struct ac97_source_info *si = &as->source_info[idx];
    686 		if (ac97_str_equal(class, si->class) &&
    687 		    ac97_str_equal(device, si->device) &&
    688 		    ac97_str_equal(qualifier, si->qualifier))
    689 			return (idx);
    690 	}
    691 
    692 	return (-1);
    693 }
    694 
    695 int
    696 ac97_mixer_get_port(codec_if, cp)
    697 	struct ac97_codec_if *codec_if;
    698 	mixer_ctrl_t *cp;
    699 {
    700 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    701 	struct ac97_source_info *si = &as->source_info[cp->dev];
    702 	u_int16_t mask;
    703 	u_int16_t val;
    704 	int error;
    705 
    706 	if (cp->dev < 0 || cp->dev >= as->num_source_info)
    707 		return (EINVAL);
    708 
    709 	if (cp->type != si->type)
    710 		return (EINVAL);
    711 
    712 	error = as->hostIf->read(as->hostIf->arg, si->reg, &val);
    713 	if (error)
    714 		return (error);
    715 
    716 	DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
    717 
    718 	mask = (1 << si->bits) - 1;
    719 
    720 	switch (cp->type) {
    721 	case AUDIO_MIXER_ENUM:
    722 		cp->un.ord = (val >> si->ofs) & mask;
    723 		DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs, mask, cp->un.ord));
    724 		break;
    725 	case AUDIO_MIXER_VALUE:
    726 	{
    727 		struct audio_mixer_value *value = si->info;
    728 		u_int16_t  l, r;
    729 
    730 		if ((cp->un.value.num_channels <= 0) ||
    731 		    (cp->un.value.num_channels > value->num_channels))
    732 			return (EINVAL);
    733 
    734 		if (value->num_channels == 1) {
    735 			l = r = (val >> si->ofs) & mask;
    736 		} else {
    737 			l = (val >> si->ofs) & mask;
    738 			r = (val >> (si->ofs + 8)) & mask;
    739 		}
    740 
    741 		l = (l << (8 - si->bits));
    742 		r = (r << (8 - si->bits));
    743 		if (!si->polarity) {
    744 			l = 255 - l;
    745 			r = 255 - r;
    746 		}
    747 
    748 		/* The EAP driver averages l and r for stereo
    749 		   channels that are requested in MONO mode. Does this
    750 		   make sense? */
    751 		if (cp->un.value.num_channels == 1) {
    752 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
    753 		} else if (cp->un.value.num_channels == 2) {
    754 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
    755 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
    756 		}
    757 
    758 		break;
    759 	}
    760 	default:
    761 		return (EINVAL);
    762 	}
    763 
    764 	return (0);
    765 }
    766 
    767