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