Home | History | Annotate | Line # | Download | only in ic
ac97.c revision 1.6
      1 /*      $NetBSD: ac97.c,v 1.6 2000/03/15 22:41:29 augustss 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 #include <dev/ic/ac97.h>
     71 
     72 #define AC97_REG_RESET                0x00
     73 #define AC97_SOUND_ENHANCEMENT(reg)   (((reg) >> 10) & 0x1f)
     74 #define AC97_REG_MASTER_VOLUME        0x02
     75 #define AC97_REG_HEADPHONE_VOLUME     0x04
     76 #define AC97_REG_MASTER_VOLUME_MONO   0x06
     77 #define AC97_REG_MASTER_TONE          0x08
     78 #define AC97_REG_PCBEEP_VOLUME        0x0a
     79 #define AC97_REG_PHONE_VOLUME         0x0c
     80 #define AC97_REG_MIC_VOLUME           0x0e
     81 #define AC97_REG_LINEIN_VOLUME        0x10
     82 #define AC97_REG_CD_VOLUME            0x12
     83 #define AC97_REG_VIDEO_VOLUME         0x14
     84 #define AC97_REG_AUX_VOLUME           0x16
     85 #define AC97_REG_PCMOUT_VOLUME        0x18
     86 #define AC97_REG_RECORD_SELECT        0x1a
     87 #define AC97_REG_RECORD_GAIN          0x1c
     88 #define AC97_REG_RECORD_GAIN_MIC      0x1e
     89 #define AC97_REG_GP                   0x20
     90 #define AC97_REG_3D_CONTROL           0x22
     91 #define AC97_REG_POWER                0x26
     92 #define AC97_REG_VENDOR_ID1           0x7c
     93 #define AC97_REG_VENDOR_ID2           0x7e
     94 
     95 static struct audio_mixer_enum ac97_on_off = { 2,
     96 					       { { { AudioNoff } , 0 },
     97 					         { { AudioNon }  , 1 } }};
     98 
     99 
    100 static struct audio_mixer_enum ac97_mic_select = { 2,
    101 					       { { { AudioNmicrophone "0" },
    102 						   0 },
    103 					         { { AudioNmicrophone "1" },
    104 						   1 } }};
    105 
    106 static struct audio_mixer_enum ac97_mono_select = { 2,
    107 					       { { { AudioNmixerout },
    108 						   0 },
    109 					         { { AudioNmicrophone },
    110 						   1 } }};
    111 
    112 static struct audio_mixer_enum ac97_source = { 8,
    113 					       { { { AudioNmicrophone } , 0 },
    114 						 { { AudioNcd }, 1 },
    115 						 { { "video" }, 2 },
    116 						 { { AudioNaux }, 3 },
    117 						 { { AudioNline }, 4 },
    118 						 { { AudioNmixerout }, 5 },
    119 						 { { AudioNmixerout AudioNmono }, 6 },
    120 						 { { "phone" }, 7 }}};
    121 
    122 static struct audio_mixer_value ac97_volume_stereo = { { AudioNvolume },
    123 						       2 };
    124 
    125 
    126 static struct audio_mixer_value ac97_volume_mono = { { AudioNvolume },
    127 						     1 };
    128 
    129 #define WRAP(a)  &a, sizeof(a)
    130 
    131 struct ac97_source_info {
    132 	char *class;
    133 	char *device;
    134 	char *qualifier;
    135 	int  type;
    136 
    137 	void *info;
    138 	int  info_size;
    139 
    140 	u_int8_t  reg;
    141 	u_int8_t  bits:3;
    142 	u_int8_t  ofs:4;
    143 	u_int8_t  mute:1;
    144 	u_int8_t  polarity:1;   /* Does 0 == MAX or MIN */
    145 
    146 	int  prev;
    147 	int  next;
    148 	int  mixer_class;
    149 } source_info[] = {
    150 	{ AudioCinputs ,            NULL,           NULL,    AUDIO_MIXER_CLASS,
    151 	},
    152 	{ AudioCoutputs,            NULL,           NULL,    AUDIO_MIXER_CLASS,
    153 	},
    154 	{ AudioCrecord ,            NULL,           NULL,    AUDIO_MIXER_CLASS,
    155 	},
    156 	/* Stereo master volume*/
    157 	{ AudioCoutputs,     AudioNmaster,        NULL,    AUDIO_MIXER_VALUE,
    158 	  WRAP(ac97_volume_stereo),
    159 	  AC97_REG_MASTER_VOLUME, 5, 0, 1,
    160 	},
    161 	/* Mono volume */
    162 	{ AudioCoutputs,       AudioNmono,        NULL,    AUDIO_MIXER_VALUE,
    163 	  WRAP(ac97_volume_mono),
    164 	  AC97_REG_MASTER_VOLUME_MONO, 6, 0, 1,
    165 	},
    166 	{ AudioCoutputs,       AudioNmono,AudioNsource,   AUDIO_MIXER_ENUM,
    167 	  WRAP(ac97_mono_select),
    168 	  AC97_REG_GP, 1, 9, 0,
    169 	},
    170 	/* Headphone volume */
    171 	{ AudioCoutputs,  AudioNheadphone,        NULL,    AUDIO_MIXER_VALUE,
    172 	  WRAP(ac97_volume_stereo),
    173 	  AC97_REG_HEADPHONE_VOLUME, 6, 0, 1,
    174 	},
    175 	/* Tone */
    176 	{ AudioCoutputs,           "tone",        NULL,    AUDIO_MIXER_VALUE,
    177 	  WRAP(ac97_volume_stereo),
    178 	  AC97_REG_MASTER_TONE, 4, 0, 0,
    179 	},
    180 	/* PC Beep Volume */
    181 	{ AudioCinputs,     AudioNspeaker,        NULL,    AUDIO_MIXER_VALUE,
    182 	  WRAP(ac97_volume_mono),
    183 	  AC97_REG_PCBEEP_VOLUME, 4, 1, 1,
    184 	},
    185 	/* Phone */
    186 	{ AudioCinputs,           "phone",        NULL,    AUDIO_MIXER_VALUE,
    187 	  WRAP(ac97_volume_mono),
    188 	  AC97_REG_PHONE_VOLUME, 5, 0, 1,
    189 	},
    190 	/* Mic Volume */
    191 	{ AudioCinputs,  AudioNmicrophone,        NULL,    AUDIO_MIXER_VALUE,
    192 	  WRAP(ac97_volume_mono),
    193 	  AC97_REG_MIC_VOLUME, 5, 0, 1,
    194 	},
    195 	{ AudioCinputs,  AudioNmicrophone, AudioNpreamp,   AUDIO_MIXER_ENUM,
    196 	  WRAP(ac97_on_off),
    197 	  AC97_REG_MIC_VOLUME, 1, 6, 0,
    198 	},
    199 	{ AudioCinputs,  AudioNmicrophone, AudioNsource,   AUDIO_MIXER_ENUM,
    200 	  WRAP(ac97_mic_select),
    201 	  AC97_REG_GP, 1, 8, 0,
    202 	},
    203 	/* Line in Volume */
    204 	{ AudioCinputs,        AudioNline,        NULL,    AUDIO_MIXER_VALUE,
    205 	  WRAP(ac97_volume_stereo),
    206 	  AC97_REG_LINEIN_VOLUME, 5, 0, 1,
    207 	},
    208 	/* CD Volume */
    209 	{ AudioCinputs,          AudioNcd,        NULL,    AUDIO_MIXER_VALUE,
    210 	  WRAP(ac97_volume_stereo),
    211 	  AC97_REG_CD_VOLUME, 5, 0, 1,
    212 	},
    213 	/* Video Volume */
    214 	{ AudioCinputs,           "video",        NULL,    AUDIO_MIXER_VALUE,
    215 	  WRAP(ac97_volume_stereo),
    216 	  AC97_REG_VIDEO_VOLUME, 5, 0, 1,
    217 	},
    218 	/* AUX volume */
    219 	{ AudioCinputs,         AudioNaux,        NULL,    AUDIO_MIXER_VALUE,
    220 	  WRAP(ac97_volume_stereo),
    221 	  AC97_REG_AUX_VOLUME, 5, 0, 1,
    222 	},
    223 	/* PCM out volume */
    224 	{ AudioCinputs,         AudioNdac,        NULL,    AUDIO_MIXER_VALUE,
    225 	  WRAP(ac97_volume_stereo),
    226 	  AC97_REG_PCMOUT_VOLUME, 5, 0, 1,
    227 	},
    228 	/* Record Source - some logic for this is hard coded - see below */
    229 	{ AudioCrecord,      AudioNsource,        NULL,    AUDIO_MIXER_ENUM,
    230 	  WRAP(ac97_source),
    231 	  AC97_REG_RECORD_SELECT, 3, 0, 0,
    232 	},
    233 	/* Record Gain */
    234 	{ AudioCrecord,      AudioNvolume,        NULL,    AUDIO_MIXER_VALUE,
    235 	  WRAP(ac97_volume_stereo),
    236 	  AC97_REG_RECORD_GAIN, 4, 0, 1,
    237 	},
    238 	/* Record Gain mic */
    239 	{ AudioCrecord,  AudioNmicrophone,        NULL,    AUDIO_MIXER_VALUE,
    240 	  WRAP(ac97_volume_mono),
    241 	  AC97_REG_RECORD_GAIN_MIC, 4, 0, 1, 1,
    242 	},
    243 	/* */
    244 	{ AudioCoutputs,   AudioNloudness,        NULL,    AUDIO_MIXER_ENUM,
    245 	  WRAP(ac97_on_off),
    246 	  AC97_REG_GP, 1, 12, 0,
    247 	},
    248 	{ AudioCoutputs,    AudioNspatial,        NULL,    AUDIO_MIXER_ENUM,
    249 	  WRAP(ac97_on_off),
    250 	  AC97_REG_GP, 1, 13, 0,
    251 	},
    252 	{ AudioCoutputs,    AudioNspatial,    "center",    AUDIO_MIXER_VALUE,
    253 	  WRAP(ac97_volume_mono),
    254 	  AC97_REG_3D_CONTROL, 4, 8, 0, 1,
    255 	},
    256 	{ AudioCoutputs,    AudioNspatial,     "depth",    AUDIO_MIXER_VALUE,
    257 	  WRAP(ac97_volume_mono),
    258 	  AC97_REG_3D_CONTROL, 4, 0, 0, 1,
    259 	},
    260 
    261 	/* Missing features: Simulated Stereo, POP, Loopback mode */
    262 } ;
    263 
    264 #define SOURCE_INFO_SIZE (sizeof(source_info)/sizeof(source_info[0]))
    265 
    266 /*
    267  * Check out http://developer.intel.com/pc-supp/platform/ac97/ for
    268  * information on AC-97
    269  */
    270 
    271 struct ac97_softc {
    272 	struct ac97_codec_if codecIf;
    273 
    274 	struct ac97_host_if *hostIf;
    275 
    276 	struct ac97_source_info source_info[2 * SOURCE_INFO_SIZE];
    277 	int num_source_info;
    278 };
    279 
    280 int ac97_mixer_get_port __P((struct ac97_codec_if *self, mixer_ctrl_t *cp));
    281 int ac97_mixer_set_port __P((struct ac97_codec_if *self, mixer_ctrl_t *));
    282 int ac97_query_devinfo __P((struct ac97_codec_if *self, mixer_devinfo_t *));
    283 int ac97_get_portnum_by_name __P((struct ac97_codec_if *, char *, char *,
    284 				  char *));
    285 
    286 struct ac97_codec_if_vtbl ac97civ = {
    287 	ac97_mixer_get_port,
    288 	ac97_mixer_set_port,
    289 	ac97_query_devinfo,
    290 	ac97_get_portnum_by_name
    291 };
    292 
    293 static struct ac97_codecid {
    294 	u_int32_t id;
    295 	char *name;
    296 } ac97codecid[] = {
    297 	{ 0x414B4D00, "Asahi Kasei AK4540" 	},
    298 	/*0x43525900, "Cirrus Logic CS4297" 	},*/
    299 	{ 0x43525903, "Cirrus Logic CS4297" 	},
    300 	{ 0x83847600, "SigmaTel STAC????" 	},
    301 	{ 0x83847604, "SigmaTel STAC9701/3/4/5" },
    302 	{ 0x83847605, "SigmaTel STAC9704" 	},
    303 	{ 0x83847608, "SigmaTel STAC9708" 	},
    304 	{ 0x83847609, "SigmaTel STAC9721" 	},
    305 	{ 0, 	      NULL			}
    306 };
    307 
    308 static char *ac97enhancement[] = {
    309 	"no 3D stereo",
    310 	"Analog Devices Phat Stereo",
    311 	"Creative"
    312 	"National Semi 3D",
    313 	"Yamaha Ymersion",
    314 	"BBE 3D",
    315 	"Crystal Semi 3D"
    316 	"Qsound QXpander",
    317 	"Spatializer 3D",
    318 	"SRS 3D",
    319 	"Platform Tech 3D",
    320 	"AKM 3D",
    321 	"Aureal",
    322 	"AZTECH 3D",
    323 	"Binaura 3D",
    324 	"ESS Technology",
    325 	"Harman International VMAx",
    326 	"Nvidea 3D",
    327 	"Philips Incredible Sound",
    328 	"Texas Instruments' 3D",
    329 	"VLSI Technology 3D",
    330 	"TriTech 3D",
    331 	"Realtek 3D",
    332 	"Samsung 3D",
    333 	"Wolfson Microelectronics 3D",
    334 	"Delta Integration 3D",
    335 	"SigmaTel 3D",
    336 	"Unknown 3D",
    337 	"Rockwell 3D",
    338 	"Unknown 3D",
    339 	"Unknown 3D",
    340 	"Unknown 3D",
    341 };
    342 
    343 static char *ac97feature[] = {
    344 	"mic channel",
    345 	"reserved",
    346 	"tone",
    347 	"simulated stereo",
    348 	"headphone",
    349 	"bass boost",
    350 	"18 bit DAC",
    351 	"20 bit DAC",
    352 	"18 bit ADC",
    353 	"20 bit ADC"
    354 };
    355 
    356 
    357 int ac97_str_equal __P((char *, char *));
    358 void ac97_setup_source_info __P((struct ac97_softc *));
    359 
    360 /* #define AC97_DEBUG 10 */
    361 
    362 #ifdef AUDIO_DEBUG
    363 #define DPRINTF(x)	if (ac97debug) printf x
    364 #define DPRINTFN(n,x)	if (ac97debug>(n)) printf x
    365 #ifdef AC97_DEBUG
    366 int	ac97debug = AC97_DEBUG;
    367 #else
    368 int	ac97debug = 0;
    369 #endif
    370 #else
    371 #define DPRINTF(x)
    372 #define DPRINTFN(n,x)
    373 #endif
    374 
    375 
    376 int
    377 ac97_str_equal(a, b)
    378 	char *a, *b;
    379 {
    380 	return ((a == b) || (a && b && (!strcmp(a, b))));
    381 }
    382 
    383 void
    384 ac97_setup_source_info(as)
    385 	struct ac97_softc *as;
    386 {
    387 	int idx, ouridx;
    388 	struct ac97_source_info *si, *si2;
    389 
    390 	for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
    391 		si = &as->source_info[ouridx];
    392 
    393 		bcopy(&source_info[idx], si, sizeof(*si));
    394 
    395 		switch (si->type) {
    396 		case AUDIO_MIXER_CLASS:
    397 		        si->mixer_class = ouridx;
    398 			ouridx++;
    399 			break;
    400 		case AUDIO_MIXER_VALUE:
    401 			/* Todo - Test to see if it works */
    402 			ouridx++;
    403 
    404 			/* Add an entry for mute, if necessary */
    405 			if (si->mute) {
    406 				si = &as->source_info[ouridx];
    407 				bcopy(&source_info[idx], si, sizeof(*si));
    408 				si->qualifier = AudioNmute;
    409 				si->type = AUDIO_MIXER_ENUM;
    410 				si->info = &ac97_on_off;
    411 				si->info_size = sizeof(ac97_on_off);
    412 				si->bits = 1;
    413 				si->ofs = 15;
    414 				si->mute = 0;
    415 				si->polarity = 0;
    416 				ouridx++;
    417 			}
    418 			break;
    419 		case AUDIO_MIXER_ENUM:
    420 			/* Todo - Test to see if it works */
    421 			ouridx++;
    422 			break;
    423 		default:
    424 			printf ("ac97: shouldn't get here\n");
    425 			break;
    426 		}
    427 	}
    428 
    429 	as->num_source_info = ouridx;
    430 
    431 	for (idx = 0; idx < as->num_source_info; idx++) {
    432 		int idx2, previdx;
    433 
    434 		si = &as->source_info[idx];
    435 
    436 		/* Find mixer class */
    437 		for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
    438 			si2 = &as->source_info[idx2];
    439 
    440 			if (si2->type == AUDIO_MIXER_CLASS &&
    441 			    ac97_str_equal(si->class,
    442 					   si2->class)) {
    443 				si->mixer_class = idx2;
    444 			}
    445 		}
    446 
    447 
    448 		/* Setup prev and next pointers */
    449 		if (si->prev != 0)
    450 			continue;
    451 
    452 		if (si->qualifier)
    453 			continue;
    454 
    455 		si->prev = AUDIO_MIXER_LAST;
    456 		previdx = idx;
    457 
    458 		for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
    459 			if (idx2 == idx)
    460 				continue;
    461 
    462 			si2 = &as->source_info[idx2];
    463 
    464 			if (!si2->prev &&
    465 			    ac97_str_equal(si->class, si2->class) &&
    466 			    ac97_str_equal(si->device, si2->device)) {
    467 				as->source_info[previdx].next = idx2;
    468 				as->source_info[idx2].prev = previdx;
    469 
    470 				previdx = idx2;
    471 			}
    472 		}
    473 
    474 		as->source_info[previdx].next = AUDIO_MIXER_LAST;
    475 	}
    476 }
    477 
    478 int
    479 ac97_attach(hostIf)
    480 	struct ac97_host_if *hostIf;
    481 {
    482 	struct ac97_softc *as;
    483 	struct device *sc_dev = (struct device *)hostIf->arg;
    484 	int error, i, j;
    485 	u_int16_t id1, id2, caps;
    486 	u_int32_t id;
    487 
    488 	as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_WAITOK);
    489 
    490 	if (as == NULL)
    491 		return (ENOMEM);
    492 
    493 	as->codecIf.vtbl = &ac97civ;
    494 	as->hostIf = hostIf;
    495 
    496 	if ((error = hostIf->attach(hostIf->arg, &as->codecIf))) {
    497 		free (as, M_DEVBUF);
    498 		return (error);
    499 	}
    500 
    501 	hostIf->reset(hostIf->arg);
    502 
    503 	hostIf->write(hostIf->arg, AC97_REG_POWER, 0);
    504 	hostIf->write(hostIf->arg, AC97_REG_RESET, 0);
    505 
    506 	if ((error = hostIf->read(hostIf->arg, AC97_REG_VENDOR_ID1, &id1)))
    507 		return (error);
    508 
    509 	if ((error = hostIf->read(hostIf->arg, AC97_REG_VENDOR_ID2, &id2)))
    510 		return (error);
    511 
    512 	if ((error = hostIf->read(hostIf->arg, AC97_REG_RESET, &caps)))
    513 		return (error);
    514 
    515 	id = (id1 << 16) | id2;
    516 
    517 	printf("%s: ", sc_dev->dv_xname);
    518 
    519 	for (i = 0; ; i++) {
    520 		if (ac97codecid[i].id == id) {
    521 			printf("%s", ac97codecid[i].name);
    522 			break;
    523 		}
    524 		if (ac97codecid[i].id == 0) {
    525 			char pnp[4];
    526 			pnp[0] = id >> 24;
    527 			pnp[1] = id >> 16;
    528 			pnp[2] = id >>  8;
    529 			pnp[3] = '\0';
    530 #define ISASCII(c) ((c) >= ' ' && (c) < 0x7f)
    531 			if (ISASCII(pnp[0]) && ISASCII(pnp[1]) &&
    532 			    ISASCII(pnp[2]))
    533 				printf("%s%d", pnp, id & 0xff);
    534 			else
    535 				printf("unknown (0x%8x)", id);
    536 			break;
    537 		}
    538 	}
    539 	printf(" codec; ");
    540 	for (i = j = 0; i < 10; i++) {
    541 		if (caps & (1 << i)) {
    542 			printf("%s%s", j? ", " : "", ac97feature[i]);
    543 			j++;
    544 		}
    545 	}
    546 
    547 	printf("%s%s\n", j? ", " : "", ac97enhancement[(caps >> 10) & 0x1f]);
    548 
    549 	ac97_setup_source_info(as);
    550 
    551 	return (0);
    552 }
    553 
    554 
    555 int
    556 ac97_query_devinfo(codec_if, dip)
    557 	struct ac97_codec_if *codec_if;
    558 	mixer_devinfo_t *dip;
    559 {
    560 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    561 
    562 	if (dip->index < as->num_source_info) {
    563 		struct ac97_source_info *si = &as->source_info[dip->index];
    564 		char *name;
    565 
    566 		dip->type = si->type;
    567 		dip->mixer_class = si->mixer_class;
    568 		dip->prev = si->prev;
    569 		dip->next = si->next;
    570 
    571 		if (si->qualifier)
    572 			name = si->qualifier;
    573 		else if (si->device)
    574 			name = si->device;
    575 		else if (si->class)
    576 			name = si->class;
    577 		else
    578 			name = 0;
    579 
    580 		if (name)
    581 			strcpy(dip->label.name, name);
    582 
    583 		bcopy(si->info, &dip->un, si->info_size);
    584 		return (0);
    585 	}
    586 
    587 	return (ENXIO);
    588 }
    589 
    590 
    591 
    592 int
    593 ac97_mixer_set_port(codec_if, cp)
    594 	struct ac97_codec_if *codec_if;
    595 	mixer_ctrl_t *cp;
    596 {
    597 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    598 	struct ac97_source_info *si = &as->source_info[cp->dev];
    599 	u_int16_t mask;
    600 	u_int16_t val, newval;
    601 	int error;
    602 
    603 	if (cp->dev < 0 || cp->dev >= as->num_source_info)
    604 		return (EINVAL);
    605 
    606 	if (cp->type != si->type)
    607 		return (EINVAL);
    608 
    609 	error = as->hostIf->read(as->hostIf->arg, si->reg, &val);
    610 	if (error)
    611 		return (error);
    612 
    613 	DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
    614 
    615 	mask = (1 << si->bits) - 1;
    616 
    617 	switch (cp->type) {
    618 	case AUDIO_MIXER_ENUM:
    619 		if (cp->un.ord > mask || cp->un.ord < 0)
    620 			return (EINVAL);
    621 
    622 		newval = (cp->un.ord << si->ofs);
    623 		if (si->reg == AC97_REG_RECORD_SELECT) {
    624 			newval |= (newval << (8 + si->ofs));
    625 			mask |= (mask << 8);
    626 		}
    627 		break;
    628 	case AUDIO_MIXER_VALUE:
    629 	{
    630 		struct audio_mixer_value *value = si->info;
    631 		u_int16_t  l, r;
    632 
    633 		if ((cp->un.value.num_channels <= 0) ||
    634 		    (cp->un.value.num_channels > value->num_channels))
    635 			return (EINVAL);
    636 
    637 		if (cp->un.value.num_channels == 1) {
    638 			l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
    639 		} else {
    640 			l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
    641 			r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
    642 		}
    643 
    644 		if (!si->polarity) {
    645 			l = 255 - l;
    646 			r = 255 - r;
    647 		}
    648 
    649 		l = l >> (8 - si->bits);
    650 		r = r >> (8 - si->bits);
    651 
    652 		newval = ((l & mask) << si->ofs);
    653 		if (value->num_channels == 2) {
    654 			newval |= ((r & mask) << (si->ofs + 8));
    655 			mask |= (mask << 8);
    656 		}
    657 
    658 		break;
    659 	}
    660 	default:
    661 		return (EINVAL);
    662 	}
    663 
    664 	mask = mask << si->ofs;
    665 	error = as->hostIf->write(as->hostIf->arg, si->reg, (val & ~mask) | newval);
    666 	if (error)
    667 		return (error);
    668 
    669 	return (0);
    670 }
    671 
    672 int
    673 ac97_get_portnum_by_name(codec_if, class, device, qualifier)
    674 	struct ac97_codec_if *codec_if;
    675 	char *class, *device, *qualifier;
    676 {
    677 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    678 	int idx;
    679 
    680 	for (idx = 0; idx < as->num_source_info; idx++) {
    681 		struct ac97_source_info *si = &as->source_info[idx];
    682 		if (ac97_str_equal(class, si->class) &&
    683 		    ac97_str_equal(device, si->device) &&
    684 		    ac97_str_equal(qualifier, si->qualifier))
    685 			return (idx);
    686 	}
    687 
    688 	return (-1);
    689 }
    690 
    691 int
    692 ac97_mixer_get_port(codec_if, cp)
    693 	struct ac97_codec_if *codec_if;
    694 	mixer_ctrl_t *cp;
    695 {
    696 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    697 	struct ac97_source_info *si = &as->source_info[cp->dev];
    698 	u_int16_t mask;
    699 	u_int16_t val;
    700 	int error;
    701 
    702 	if (cp->dev < 0 || cp->dev >= as->num_source_info)
    703 		return (EINVAL);
    704 
    705 	if (cp->type != si->type)
    706 		return (EINVAL);
    707 
    708 	error = as->hostIf->read(as->hostIf->arg, si->reg, &val);
    709 	if (error)
    710 		return (error);
    711 
    712 	DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
    713 
    714 	mask = (1 << si->bits) - 1;
    715 
    716 	switch (cp->type) {
    717 	case AUDIO_MIXER_ENUM:
    718 		cp->un.ord = (val >> si->ofs) & mask;
    719 		DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs, mask, cp->un.ord));
    720 		break;
    721 	case AUDIO_MIXER_VALUE:
    722 	{
    723 		struct audio_mixer_value *value = si->info;
    724 		u_int16_t  l, r;
    725 
    726 		if ((cp->un.value.num_channels <= 0) ||
    727 		    (cp->un.value.num_channels > value->num_channels))
    728 			return (EINVAL);
    729 
    730 		if (value->num_channels == 1) {
    731 			l = r = (val >> si->ofs) & mask;
    732 		} else {
    733 			l = (val >> si->ofs) & mask;
    734 			r = (val >> (si->ofs + 8)) & mask;
    735 		}
    736 
    737 		l = (l << (8 - si->bits));
    738 		r = (r << (8 - si->bits));
    739 		if (!si->polarity) {
    740 			l = 255 - l;
    741 			r = 255 - r;
    742 		}
    743 
    744 		/* The EAP driver averages l and r for stereo
    745 		   channels that are requested in MONO mode. Does this
    746 		   make sense? */
    747 		if (cp->un.value.num_channels == 1) {
    748 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
    749 		} else if (cp->un.value.num_channels == 2) {
    750 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
    751 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
    752 		}
    753 
    754 		break;
    755 	}
    756 	default:
    757 		return (EINVAL);
    758 	}
    759 
    760 	return (0);
    761 }
    762 
    763