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