Home | History | Annotate | Line # | Download | only in ic
ac97.c revision 1.31
      1 /*      $NetBSD: ac97.c,v 1.31 2002/10/09 12:06:17 kent Exp $ */
      2 /*	$OpenBSD: ac97.c,v 1.8 2000/07/19 09:01:35 csapuntz Exp $	*/
      3 
      4 /*
      5  * Copyright (c) 1999, 2000 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  * 3. The name of the author may not be used to endorse or promote
     18  *    products derived from this software without specific prior written
     19  *    permission.
     20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
     21  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
     24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
     26  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
     27  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
     30  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
     31  * DAMAGE
     32  */
     33 
     34 /* Partially inspired by FreeBSD's sys/dev/pcm/ac97.c. It came with
     35    the following copyright */
     36 
     37 /*
     38  * Copyright (c) 1999 Cameron Grant <gandalf (at) vilnya.demon.co.uk>
     39  * All rights reserved.
     40  *
     41  * Redistribution and use in source and binary forms, with or without
     42  * modification, are permitted provided that the following conditions
     43  * are met:
     44  * 1. Redistributions of source code must retain the above copyright
     45  *    notice, this list of conditions and the following disclaimer.
     46  * 2. Redistributions in binary form must reproduce the above copyright
     47  *    notice, this list of conditions and the following disclaimer in the
     48  *    documentation and/or other materials provided with the distribution.
     49  *
     50  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     53  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     60  * SUCH DAMAGE.
     61  *
     62  * $FreeBSD$
     63  */
     64 
     65 #include <sys/cdefs.h>
     66 __KERNEL_RCSID(0, "$NetBSD: ac97.c,v 1.31 2002/10/09 12:06:17 kent Exp $");
     67 
     68 #include <sys/param.h>
     69 #include <sys/systm.h>
     70 #include <sys/kernel.h>
     71 #include <sys/malloc.h>
     72 #include <sys/device.h>
     73 
     74 #include <sys/audioio.h>
     75 #include <dev/audio_if.h>
     76 
     77 #include <dev/ic/ac97reg.h>
     78 #include <dev/ic/ac97var.h>
     79 
     80 static const struct audio_mixer_enum ac97_on_off = { 2,
     81 					       { { { AudioNoff } , 0 },
     82 					         { { AudioNon }  , 1 } }};
     83 
     84 
     85 static const struct audio_mixer_enum ac97_mic_select = { 2,
     86 					       { { { AudioNmicrophone "0" },
     87 						   0 },
     88 					         { { AudioNmicrophone "1" },
     89 						   1 } }};
     90 
     91 static const struct audio_mixer_enum ac97_mono_select = { 2,
     92 					       { { { AudioNmixerout },
     93 						   0 },
     94 					         { { AudioNmicrophone },
     95 						   1 } }};
     96 
     97 static const struct audio_mixer_enum ac97_source = { 8,
     98 					       { { { AudioNmicrophone } , 0 },
     99 						 { { AudioNcd }, 1 },
    100 						 { { "video" }, 2 },
    101 						 { { AudioNaux }, 3 },
    102 						 { { AudioNline }, 4 },
    103 						 { { AudioNmixerout }, 5 },
    104 						 { { AudioNmixerout AudioNmono }, 6 },
    105 						 { { "phone" }, 7 }}};
    106 
    107 /*
    108  * Due to different values for each source that uses these structures,
    109  * the ac97_query_devinfo function sets delta in mixer_devinfo_t using
    110  * ac97_source_info.bits.
    111  */
    112 static const struct audio_mixer_value ac97_volume_stereo = { { AudioNvolume },
    113 						       2 };
    114 
    115 static const struct audio_mixer_value ac97_volume_mono = { { AudioNvolume },
    116 						     1 };
    117 
    118 #define WRAP(a)  &a, sizeof(a)
    119 
    120 const struct ac97_source_info {
    121 	const char *class;
    122 	const char *device;
    123 	const char *qualifier;
    124 	int  type;
    125 
    126 	const void *info;
    127 	int  info_size;
    128 
    129 	u_int8_t  reg;
    130 	u_int16_t default_value;
    131 	u_int8_t  bits:3;
    132 	u_int8_t  ofs:4;
    133 	u_int8_t  mute:1;
    134 	u_int8_t  polarity:1;   /* Does 0 == MAX or MIN */
    135 
    136 	int  prev;
    137 	int  next;
    138 	int  mixer_class;
    139 } source_info[] = {
    140 	{ AudioCinputs ,            NULL,           NULL,    AUDIO_MIXER_CLASS,
    141 	},
    142 	{ AudioCoutputs,            NULL,           NULL,    AUDIO_MIXER_CLASS,
    143 	},
    144 	{ AudioCrecord ,            NULL,           NULL,    AUDIO_MIXER_CLASS,
    145 	},
    146 	/* Stereo master volume*/
    147 	{ AudioCoutputs,     AudioNmaster,        NULL,    AUDIO_MIXER_VALUE,
    148 	  WRAP(ac97_volume_stereo),
    149 	  AC97_REG_MASTER_VOLUME, 0x8000, 5, 0, 1,
    150 	},
    151 	/* Mono volume */
    152 	{ AudioCoutputs,       AudioNmono,        NULL,    AUDIO_MIXER_VALUE,
    153 	  WRAP(ac97_volume_mono),
    154 	  AC97_REG_MASTER_VOLUME_MONO, 0x8000, 6, 0, 1,
    155 	},
    156 	{ AudioCoutputs,       AudioNmono,AudioNsource,   AUDIO_MIXER_ENUM,
    157 	  WRAP(ac97_mono_select),
    158 	  AC97_REG_GP, 0x0000, 1, 9, 0,
    159 	},
    160 	/* Headphone volume */
    161 	{ AudioCoutputs,  AudioNheadphone,        NULL,    AUDIO_MIXER_VALUE,
    162 	  WRAP(ac97_volume_stereo),
    163 	  AC97_REG_HEADPHONE_VOLUME, 0x8000, 6, 0, 1,
    164 	},
    165 	/* Tone */
    166 	{ AudioCoutputs,           "tone",        NULL,    AUDIO_MIXER_VALUE,
    167 	  WRAP(ac97_volume_stereo),
    168 	  AC97_REG_MASTER_TONE, 0x0f0f, 4, 0, 0,
    169 	},
    170 	/* PC Beep Volume */
    171 	{ AudioCinputs,     AudioNspeaker,        NULL,    AUDIO_MIXER_VALUE,
    172 	  WRAP(ac97_volume_mono),
    173 	  AC97_REG_PCBEEP_VOLUME, 0x0000, 4, 1, 1,
    174 	},
    175 	/* Phone */
    176 	{ AudioCinputs,           "phone",        NULL,    AUDIO_MIXER_VALUE,
    177 	  WRAP(ac97_volume_mono),
    178 	  AC97_REG_PHONE_VOLUME, 0x8008, 5, 0, 1,
    179 	},
    180 	/* Mic Volume */
    181 	{ AudioCinputs,  AudioNmicrophone,        NULL,    AUDIO_MIXER_VALUE,
    182 	  WRAP(ac97_volume_mono),
    183 	  AC97_REG_MIC_VOLUME, 0x8008, 5, 0, 1,
    184 	},
    185 	{ AudioCinputs,  AudioNmicrophone, AudioNpreamp,   AUDIO_MIXER_ENUM,
    186 	  WRAP(ac97_on_off),
    187 	  AC97_REG_MIC_VOLUME, 0x8008, 1, 6, 0,
    188 	},
    189 	{ AudioCinputs,  AudioNmicrophone, AudioNsource,   AUDIO_MIXER_ENUM,
    190 	  WRAP(ac97_mic_select),
    191 	  AC97_REG_GP, 0x0000, 1, 8, 0,
    192 	},
    193 	/* Line in Volume */
    194 	{ AudioCinputs,        AudioNline,        NULL,    AUDIO_MIXER_VALUE,
    195 	  WRAP(ac97_volume_stereo),
    196 	  AC97_REG_LINEIN_VOLUME, 0x8808, 5, 0, 1,
    197 	},
    198 	/* CD Volume */
    199 	{ AudioCinputs,          AudioNcd,        NULL,    AUDIO_MIXER_VALUE,
    200 	  WRAP(ac97_volume_stereo),
    201 	  AC97_REG_CD_VOLUME, 0x8808, 5, 0, 1,
    202 	},
    203 	/* Video Volume */
    204 	{ AudioCinputs,           "video",        NULL,    AUDIO_MIXER_VALUE,
    205 	  WRAP(ac97_volume_stereo),
    206 	  AC97_REG_VIDEO_VOLUME, 0x8808, 5, 0, 1,
    207 	},
    208 	/* AUX volume */
    209 	{ AudioCinputs,         AudioNaux,        NULL,    AUDIO_MIXER_VALUE,
    210 	  WRAP(ac97_volume_stereo),
    211 	  AC97_REG_AUX_VOLUME, 0x8808, 5, 0, 1,
    212 	},
    213 	/* PCM out volume */
    214 	{ AudioCinputs,         AudioNdac,        NULL,    AUDIO_MIXER_VALUE,
    215 	  WRAP(ac97_volume_stereo),
    216 	  AC97_REG_PCMOUT_VOLUME, 0x8808, 5, 0, 1,
    217 	},
    218 	/* Record Source - some logic for this is hard coded - see below */
    219 	{ AudioCrecord,      AudioNsource,        NULL,    AUDIO_MIXER_ENUM,
    220 	  WRAP(ac97_source),
    221 	  AC97_REG_RECORD_SELECT, 0x0000, 3, 0, 0,
    222 	},
    223 	/* Record Gain */
    224 	{ AudioCrecord,      AudioNvolume,        NULL,    AUDIO_MIXER_VALUE,
    225 	  WRAP(ac97_volume_stereo),
    226 	  AC97_REG_RECORD_GAIN, 0x8000, 4, 0, 1,
    227 	},
    228 	/* Record Gain mic */
    229 	{ AudioCrecord,  AudioNmicrophone,        NULL,    AUDIO_MIXER_VALUE,
    230 	  WRAP(ac97_volume_mono),
    231 	  AC97_REG_RECORD_GAIN_MIC, 0x8000, 4, 0, 1, 1,
    232 	},
    233 	/* */
    234 	{ AudioCoutputs,   AudioNloudness,        NULL,    AUDIO_MIXER_ENUM,
    235 	  WRAP(ac97_on_off),
    236 	  AC97_REG_GP, 0x0000, 1, 12, 0,
    237 	},
    238 	{ AudioCoutputs,    AudioNspatial,        NULL,    AUDIO_MIXER_ENUM,
    239 	  WRAP(ac97_on_off),
    240 	  AC97_REG_GP, 0x0000, 1, 13, 0,
    241 	},
    242 	{ AudioCoutputs,    AudioNspatial,    "center",    AUDIO_MIXER_VALUE,
    243 	  WRAP(ac97_volume_mono),
    244 	  AC97_REG_3D_CONTROL, 0x0000, 4, 8, 0, 1,
    245 	},
    246 	{ AudioCoutputs,    AudioNspatial,     "depth",    AUDIO_MIXER_VALUE,
    247 	  WRAP(ac97_volume_mono),
    248 	  AC97_REG_3D_CONTROL, 0x0000, 4, 0, 0, 1,
    249 	},
    250 
    251 	/* Missing features: Simulated Stereo, POP, Loopback mode */
    252 } ;
    253 
    254 #define SOURCE_INFO_SIZE (sizeof(source_info)/sizeof(source_info[0]))
    255 
    256 /*
    257  * Check out http://developer.intel.com/pc-supp/platform/ac97/ for
    258  * information on AC-97
    259  */
    260 
    261 struct ac97_softc {
    262 	/* ac97_codec_if must be at the first of ac97_softc. */
    263 	struct ac97_codec_if codec_if;
    264 
    265 	struct ac97_host_if *host_if;
    266 
    267 	struct ac97_source_info source_info[2 * SOURCE_INFO_SIZE];
    268 	int num_source_info;
    269 
    270 	enum ac97_host_flags host_flags;
    271 	unsigned int ac97_clock; /* usually 48000 */
    272 #define AC97_STANDARD_CLOCK	48000U
    273 	u_int16_t caps;		/* -> AC97_REG_RESET */
    274 	u_int16_t ext_id;	/* -> AC97_REG_EXT_AUDIO_ID */
    275 	u_int16_t shadow_reg[128];
    276 };
    277 
    278 int ac97_mixer_get_port __P((struct ac97_codec_if *self, mixer_ctrl_t *cp));
    279 int ac97_mixer_set_port __P((struct ac97_codec_if *self, mixer_ctrl_t *));
    280 int ac97_query_devinfo __P((struct ac97_codec_if *self, mixer_devinfo_t *));
    281 int ac97_get_portnum_by_name __P((struct ac97_codec_if *, char *, char *,
    282 				  char *));
    283 void ac97_restore_shadow __P((struct ac97_codec_if *self));
    284 int ac97_set_rate(struct ac97_codec_if *codec_if, int target, u_long *rate);
    285 void ac97_set_clock(struct ac97_codec_if *codec_if, unsigned int clock);
    286 u_int16_t ac97_get_extcaps(struct ac97_codec_if *codec_if);
    287 
    288 struct ac97_codec_if_vtbl ac97civ = {
    289 	ac97_mixer_get_port,
    290 	ac97_mixer_set_port,
    291 	ac97_query_devinfo,
    292 	ac97_get_portnum_by_name,
    293 	ac97_restore_shadow,
    294 	ac97_get_extcaps,
    295 	ac97_set_rate,
    296 	ac97_set_clock,
    297 };
    298 
    299 static const struct ac97_codecid {
    300 	u_int32_t id;
    301 	u_int32_t mask;
    302 	const char *name;
    303 } ac97codecid[] = {
    304 	{ AC97_CODEC_ID('A', 'D', 'S', 3),
    305 	  0xffffffff,			"Analog Devices AD1819B" },
    306 	{ AC97_CODEC_ID('A', 'D', 'S', 64),
    307 	  0xffffffff,			"Analog Devices AD1881" },
    308 	{ AC97_CODEC_ID('A', 'D', 'S', 72),
    309 	  0xffffffff,			"Analog Devices AD1881A" },
    310 	{ AC97_CODEC_ID('A', 'D', 'S', 96),
    311 	  0xffffffff,			"Analog Devices AD1885" },
    312 	{ AC97_CODEC_ID('A', 'D', 'S', 99),
    313 	  0xffffffff,			"Analog Devices AD1886A" },
    314 
    315 	{ AC97_CODEC_ID('A', 'K', 'M', 0),
    316 	  0xffffffff,			"Asahi Kasei AK4540"	},
    317 	{ AC97_CODEC_ID('A', 'K', 'M', 2),
    318 	  0xffffffff,			"Asahi Kasei AK4543"	},
    319 
    320 	{ AC97_CODEC_ID('A', 'L', 'G', 0x10),
    321 	  0xffffffff,			"Advance Logic ALC200"	},
    322 	{ AC97_CODEC_ID('A', 'L', 'G', 0x20),
    323 	  0xffffffff,			"Advance Logic ALC650"	},
    324 	{ AC97_CODEC_ID('A', 'L', 'G', 0x30),
    325 	  0xffffffff,			"Advance Logic ALC101"	},
    326 	{ AC97_CODEC_ID('A', 'L', 'G', 0x40),
    327 	  0xffffffff,			"Advance Logic ALC202"	},
    328 	{ AC97_CODEC_ID('A', 'L', 'G', 0x50),
    329 	  0xffffffff,			"Advance Logic ALC250"	},
    330 
    331 	/* Cirrus Logic, Crystal series:
    332 	 *  'C' 'R' 'Y' 0x0[0-7]  - CS4297
    333 	 *              0x1[0-7]  - CS4297A
    334 	 *              0x2[0-7]  - CS4298
    335 	 *              0x2[8-f]  - CS4294
    336 	 *              0x3[0-7]  - CS4299
    337 	 *              0x4[8-f]  - CS4201
    338 	 *              0x5[8-f]  - CS4205
    339 	 *              0x6[0-7]  - CS4291
    340 	 *              0x7[0-7]  - CS4202
    341 	 * Datasheets:
    342 	 *	http://www.cirrus.com/pubs/cs4297A-5.pdf?DocumentID=593
    343 	 *	http://www.cirrus.com/pubs/cs4294.pdf?DocumentID=32
    344 	 *	http://www.cirrus.com/pubs/cs4299-5.pdf?DocumentID=594
    345 	 *	http://www.cirrus.com/pubs/cs4201-2.pdf?DocumentID=492
    346 	 *	http://www.cirrus.com/pubs/cs4205-2.pdf?DocumentID=492
    347 	 *	http://www.cirrus.com/pubs/cs4202-1.pdf?DocumentID=852
    348 	 */
    349 	{ AC97_CODEC_ID('C', 'R', 'Y', 0x00),
    350 	  0xfffffff8,			"Crystal CS4297",	},
    351 	{ AC97_CODEC_ID('C', 'R', 'Y', 0x10),
    352 	  0xfffffff8,			"Crystal CS4297A",	},
    353 	{ AC97_CODEC_ID('C', 'R', 'Y', 0x20),
    354 	  0xfffffff8,			"Crystal CS4298",	},
    355 	{ AC97_CODEC_ID('C', 'R', 'Y', 0x28),
    356 	  0xfffffff8,			"Crystal CS4294",	},
    357 	{ AC97_CODEC_ID('C', 'R', 'Y', 0x30),
    358 	  0xfffffff8,			"Crystal CS4299",	},
    359 	{ AC97_CODEC_ID('C', 'R', 'Y', 0x48),
    360 	  0xfffffff8,			"Crystal CS4201",	},
    361 	{ AC97_CODEC_ID('C', 'R', 'Y', 0x58),
    362 	  0xfffffff8,			"Crystal CS4205",	},
    363 	{ AC97_CODEC_ID('C', 'R', 'Y', 0x60),
    364 	  0xfffffff8,			"Crystal CS4291",	},
    365 	{ AC97_CODEC_ID('C', 'R', 'Y', 0x70),
    366 	  0xfffffff8,			"Crystal CS4202",	},
    367 
    368 	{ AC97_CODEC_ID('N', 'S', 'C', 49),
    369 	  0xffffffff,			"National Semiconductor LM4549", },
    370 	{ AC97_CODEC_ID('S', 'I', 'L', 34),
    371 	  0xffffffff,			"Silicon Laboratory Si3036", },
    372 	{ AC97_CODEC_ID('S', 'I', 'L', 35),
    373 	  0xffffffff,			"Silicon Laboratory Si3038", },
    374 	{ AC97_CODEC_ID('T', 'R', 'A', 2),
    375 	  0xffffffff,			"TriTech TR28022",	},
    376 	{ AC97_CODEC_ID('T', 'R', 'A', 3),
    377 	  0xffffffff,			"TriTech TR28023",	},
    378 	{ AC97_CODEC_ID('T', 'R', 'A', 6),
    379 	  0xffffffff,			"TriTech TR28026",	},
    380 	{ AC97_CODEC_ID('T', 'R', 'A', 8),
    381 	  0xffffffff,			"TriTech TR28028",	},
    382 	{ AC97_CODEC_ID('T', 'R', 'A', 35),
    383 	  0xffffffff,			"TriTech TR28602",	},
    384 	{ AC97_CODEC_ID('W', 'M', 'L', 0),
    385 	  0xffffffff,			"Wolfson WM9704",	},
    386 	{ AC97_CODEC_ID('W', 'M', 'L', 3),
    387 	  0xffffffff,			"Wolfson WM9707",	},
    388 	{ AC97_CODEC_ID('Y', 'M', 'H', 0),
    389 	  0xffffffff,			"Yamaha YMF743-S",	},
    390 	{ AC97_CODEC_ID('Y', 'M', 'H', 3),
    391 	  0xffffffff,			"Yamaha YMF753-S",	},
    392 	{ 0x45838308, 0xffffffff,	"ESS Technology ES1921", },
    393 	{ 0x83847600, 0xffffffff,	"SigmaTel STAC9700",	},
    394 	{ 0x83847604, 0xffffffff,	"SigmaTel STAC9701/3/4/5", },
    395 	{ 0x83847605, 0xffffffff,	"SigmaTel STAC9704",	},
    396 	{ 0x83847608, 0xffffffff,	"SigmaTel STAC9708",	},
    397 	{ 0x83847609, 0xffffffff,	"SigmaTel STAC9721/23",	},
    398 	{ 0x83847644, 0xffffffff,	"SigmaTel STAC9744/45",	},
    399 	{ 0x83847684, 0xffffffff,	"SigmaTel STAC9783/84",	},
    400 	{ 0,
    401 	  0,			NULL,			}
    402 };
    403 
    404 static const char * const ac97enhancement[] = {
    405 	"no 3D stereo",
    406 	"Analog Devices Phat Stereo",
    407 	"Creative",
    408 	"National Semi 3D",
    409 	"Yamaha Ymersion",
    410 	"BBE 3D",
    411 	"Crystal Semi 3D",
    412 	"Qsound QXpander",
    413 	"Spatializer 3D",
    414 	"SRS 3D",
    415 	"Platform Tech 3D",
    416 	"AKM 3D",
    417 	"Aureal",
    418 	"AZTECH 3D",
    419 	"Binaura 3D",
    420 	"ESS Technology",
    421 	"Harman International VMAx",
    422 	"Nvidea 3D",
    423 	"Philips Incredible Sound",
    424 	"Texas Instruments' 3D",
    425 	"VLSI Technology 3D",
    426 	"TriTech 3D",
    427 	"Realtek 3D",
    428 	"Samsung 3D",
    429 	"Wolfson Microelectronics 3D",
    430 	"Delta Integration 3D",
    431 	"SigmaTel 3D",
    432 	"Unknown 3D",
    433 	"Rockwell 3D",
    434 	"Unknown 3D",
    435 	"Unknown 3D",
    436 	"Unknown 3D",
    437 };
    438 
    439 static const char * const ac97feature[] = {
    440 	"dedicated mic channel",
    441 	"reserved",
    442 	"tone",
    443 	"simulated stereo",
    444 	"headphone",
    445 	"bass boost",
    446 	"18 bit DAC",
    447 	"20 bit DAC",
    448 	"18 bit ADC",
    449 	"20 bit ADC"
    450 };
    451 
    452 
    453 int ac97_str_equal __P((const char *, const char *));
    454 void ac97_setup_source_info __P((struct ac97_softc *));
    455 void ac97_read __P((struct ac97_softc *, u_int8_t, u_int16_t *));
    456 void ac97_setup_defaults __P((struct ac97_softc *));
    457 int ac97_write __P((struct ac97_softc *, u_int8_t, u_int16_t));
    458 
    459 /* #define AC97_DEBUG 10 */
    460 
    461 #ifdef AUDIO_DEBUG
    462 #define DPRINTF(x)	if (ac97debug) printf x
    463 #define DPRINTFN(n,x)	if (ac97debug>(n)) printf x
    464 #ifdef AC97_DEBUG
    465 int	ac97debug = AC97_DEBUG;
    466 #else
    467 int	ac97debug = 0;
    468 #endif
    469 #else
    470 #define DPRINTF(x)
    471 #define DPRINTFN(n,x)
    472 #endif
    473 
    474 void
    475 ac97_read(as, reg, val)
    476 	struct ac97_softc *as;
    477 	u_int8_t reg;
    478 	u_int16_t *val;
    479 {
    480 	int error;
    481 
    482 	if (as->host_flags & AC97_HOST_DONT_READ &&
    483 	    (reg != AC97_REG_VENDOR_ID1 && reg != AC97_REG_VENDOR_ID2 &&
    484 	     reg != AC97_REG_RESET)) {
    485 		*val = as->shadow_reg[reg >> 1];
    486 		return;
    487 	}
    488 
    489 	if ((error = as->host_if->read(as->host_if->arg, reg, val))) {
    490 		*val = as->shadow_reg[reg >> 1];
    491 	}
    492 }
    493 
    494 int
    495 ac97_write(as, reg, val)
    496 	struct ac97_softc *as;
    497 	u_int8_t reg;
    498 	u_int16_t val;
    499 {
    500 
    501 	as->shadow_reg[reg >> 1] = val;
    502 
    503 	return (as->host_if->write(as->host_if->arg, reg, val));
    504 }
    505 
    506 void
    507 ac97_setup_defaults(as)
    508 	struct ac97_softc *as;
    509 {
    510 	int idx;
    511 	const struct ac97_source_info *si;
    512 
    513 	memset(as->shadow_reg, 0, sizeof(as->shadow_reg));
    514 
    515 	for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
    516 		si = &source_info[idx];
    517 		ac97_write(as, si->reg, si->default_value);
    518 	}
    519 }
    520 
    521 void
    522 ac97_restore_shadow(self)
    523 	struct ac97_codec_if *self;
    524 {
    525 	struct ac97_softc *as = (struct ac97_softc *) self;
    526 	int idx;
    527 	const struct ac97_source_info *si;
    528 
    529 	for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
    530 		si = &source_info[idx];
    531 		ac97_write(as, si->reg, as->shadow_reg[si->reg >> 1]);
    532 	}
    533 }
    534 
    535 int
    536 ac97_str_equal(a, b)
    537 	const char *a, *b;
    538 {
    539 	return ((a == b) || (a && b && (!strcmp(a, b))));
    540 }
    541 
    542 void
    543 ac97_setup_source_info(as)
    544 	struct ac97_softc *as;
    545 {
    546 	int idx, ouridx;
    547 	struct ac97_source_info *si, *si2;
    548 
    549 	for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
    550 		si = &as->source_info[ouridx];
    551 
    552 		memcpy(si, &source_info[idx], sizeof(*si));
    553 
    554 		switch (si->type) {
    555 		case AUDIO_MIXER_CLASS:
    556 		        si->mixer_class = ouridx;
    557 			ouridx++;
    558 			break;
    559 		case AUDIO_MIXER_VALUE:
    560 			/* Todo - Test to see if it works */
    561 			ouridx++;
    562 
    563 			/* Add an entry for mute, if necessary */
    564 			if (si->mute) {
    565 				si = &as->source_info[ouridx];
    566 				memcpy(si, &source_info[idx], sizeof(*si));
    567 				si->qualifier = AudioNmute;
    568 				si->type = AUDIO_MIXER_ENUM;
    569 				si->info = &ac97_on_off;
    570 				si->info_size = sizeof(ac97_on_off);
    571 				si->bits = 1;
    572 				si->ofs = 15;
    573 				si->mute = 0;
    574 				si->polarity = 0;
    575 				ouridx++;
    576 			}
    577 			break;
    578 		case AUDIO_MIXER_ENUM:
    579 			/* Todo - Test to see if it works */
    580 			ouridx++;
    581 			break;
    582 		default:
    583 			printf ("ac97: shouldn't get here\n");
    584 			break;
    585 		}
    586 	}
    587 
    588 	as->num_source_info = ouridx;
    589 
    590 	for (idx = 0; idx < as->num_source_info; idx++) {
    591 		int idx2, previdx;
    592 
    593 		si = &as->source_info[idx];
    594 
    595 		/* Find mixer class */
    596 		for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
    597 			si2 = &as->source_info[idx2];
    598 
    599 			if (si2->type == AUDIO_MIXER_CLASS &&
    600 			    ac97_str_equal(si->class,
    601 					   si2->class)) {
    602 				si->mixer_class = idx2;
    603 			}
    604 		}
    605 
    606 
    607 		/* Setup prev and next pointers */
    608 		if (si->prev != 0)
    609 			continue;
    610 
    611 		if (si->qualifier)
    612 			continue;
    613 
    614 		si->prev = AUDIO_MIXER_LAST;
    615 		previdx = idx;
    616 
    617 		for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
    618 			if (idx2 == idx)
    619 				continue;
    620 
    621 			si2 = &as->source_info[idx2];
    622 
    623 			if (!si2->prev &&
    624 			    ac97_str_equal(si->class, si2->class) &&
    625 			    ac97_str_equal(si->device, si2->device)) {
    626 				as->source_info[previdx].next = idx2;
    627 				as->source_info[idx2].prev = previdx;
    628 
    629 				previdx = idx2;
    630 			}
    631 		}
    632 
    633 		as->source_info[previdx].next = AUDIO_MIXER_LAST;
    634 	}
    635 }
    636 
    637 int
    638 ac97_attach(host_if)
    639 	struct ac97_host_if *host_if;
    640 {
    641 	struct ac97_softc *as;
    642 	struct device *sc_dev = (struct device *)host_if->arg;
    643 	int error, i, j;
    644 	u_int32_t id;
    645 	u_int16_t id1, id2;
    646 	u_int16_t extstat, rate;
    647 	mixer_ctrl_t ctl;
    648 	const char *delim;
    649 
    650 	as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_WAITOK|M_ZERO);
    651 
    652 	if (as == NULL)
    653 		return (ENOMEM);
    654 
    655 	as->codec_if.vtbl = &ac97civ;
    656 	as->host_if = host_if;
    657 
    658 	if ((error = host_if->attach(host_if->arg, &as->codec_if))) {
    659 		free (as, M_DEVBUF);
    660 		return (error);
    661 	}
    662 
    663 	host_if->reset(host_if->arg);
    664 
    665 	host_if->write(host_if->arg, AC97_REG_POWER, 0);
    666 	host_if->write(host_if->arg, AC97_REG_RESET, 0);
    667 
    668 	if (host_if->flags)
    669 		as->host_flags = host_if->flags(host_if->arg);
    670 
    671 	ac97_setup_defaults(as);
    672 	ac97_read(as, AC97_REG_VENDOR_ID1, &id1);
    673 	ac97_read(as, AC97_REG_VENDOR_ID2, &id2);
    674 	ac97_read(as, AC97_REG_RESET, &as->caps);
    675 
    676 	id = (id1 << 16) | id2;
    677 
    678 	printf("%s: ", sc_dev->dv_xname);
    679 
    680 	for (i = 0; ; i++) {
    681 		if (ac97codecid[i].id == 0) {
    682 			char pnp[4];
    683 
    684 			AC97_GET_CODEC_ID(id, pnp);
    685 #define ISASCII(c) ((c) >= ' ' && (c) < 0x7f)
    686 			if (ISASCII(pnp[0]) && ISASCII(pnp[1]) &&
    687 			    ISASCII(pnp[2]))
    688 				printf("%c%c%c%d", pnp[0], pnp[1], pnp[2],
    689 				    pnp[3]);
    690 			else
    691 				printf("unknown (0x%08x)", id);
    692 			break;
    693 		}
    694 		if (ac97codecid[i].id == (id & ac97codecid[i].mask)) {
    695 			printf("%s", ac97codecid[i].name);
    696 			break;
    697 		}
    698 	}
    699 	printf(" codec; ");
    700 	for (i = j = 0; i < 10; i++) {
    701 		if (as->caps & (1 << i)) {
    702 			printf("%s%s", j? ", " : "", ac97feature[i]);
    703 			j++;
    704 		}
    705 	}
    706 	printf("%s%s\n", j ? ", " : "",
    707 	       ac97enhancement[(as->caps >> 10) & 0x1f]);
    708 
    709 	as->ac97_clock = AC97_STANDARD_CLOCK;
    710 	ac97_read(as, AC97_REG_EXT_AUDIO_ID, &as->ext_id);
    711 	if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_DRA
    712 			  | AC97_EXT_AUDIO_SPDIF | AC97_EXT_AUDIO_VRM
    713 			  | AC97_EXT_AUDIO_CDAC | AC97_EXT_AUDIO_SDAC
    714 			  | AC97_EXT_AUDIO_LDAC)) {
    715 		printf("%s:", sc_dev->dv_xname);
    716 		delim = "";
    717 
    718 		if (as->ext_id & AC97_EXT_AUDIO_VRA) {
    719 			printf("%s variable rate audio", delim);
    720 			delim = ",";
    721 		}
    722 		if (as->ext_id & AC97_EXT_AUDIO_DRA) {
    723 			printf("%s double rate output", delim);
    724 			delim = ",";
    725 		}
    726 		if (as->ext_id & AC97_EXT_AUDIO_SPDIF) {
    727 			printf("%s S/PDIF", delim);
    728 			delim = ",";
    729 		}
    730 		if (as->ext_id & AC97_EXT_AUDIO_VRM) {
    731 			printf("%s variable rate dedicated mic", delim);
    732 			delim = ",";
    733 		}
    734 		if (as->ext_id & AC97_EXT_AUDIO_CDAC) {
    735 			printf("%s center DAC", delim);
    736 			delim = ",";
    737 		}
    738 		if (as->ext_id & AC97_EXT_AUDIO_SDAC) {
    739 			printf("%s surround DAC", delim);
    740 			delim = ",";
    741 		}
    742 		if (as->ext_id & AC97_EXT_AUDIO_LDAC) {
    743 			printf("%s LFE DAC", delim);
    744 		}
    745 		printf("\n");
    746 
    747 		/* If VRA and/or VRM capablities, enable them. */
    748 		if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_VRM)) {
    749 			ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &extstat);
    750 			if (as->ext_id & AC97_EXT_AUDIO_VRA) {
    751 				extstat |= AC97_EXT_AUDIO_VRA;
    752 			}
    753 			if (as->ext_id & AC97_EXT_AUDIO_VRM) {
    754 				extstat |= AC97_EXT_AUDIO_VRM;
    755 			}
    756 			ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, extstat);
    757 
    758 			/* so it claims to do variable rate, let's make sure */
    759 			ac97_write(as, AC97_REG_PCM_FRONT_DAC_RATE, 44100);
    760 			ac97_read(as, AC97_REG_PCM_FRONT_DAC_RATE, &rate);
    761 			if (rate != 44100) {
    762 				/* We can't believe ext_id */
    763 				as->ext_id = 0;
    764 				printf("%s: Ignore these capabilities.\n",
    765 				       sc_dev->dv_xname);
    766 			}
    767 		}
    768 	}
    769 
    770 	ac97_setup_source_info(as);
    771 
    772 	/* Just enable the DAC and master volumes by default */
    773 	memset(&ctl, 0, sizeof(ctl));
    774 
    775 	ctl.type = AUDIO_MIXER_ENUM;
    776 	ctl.un.ord = 0;  /* off */
    777 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
    778 					   AudioNmaster, AudioNmute);
    779 	ac97_mixer_set_port(&as->codec_if, &ctl);
    780 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCinputs,
    781 					   AudioNdac, AudioNmute);
    782 
    783 	ac97_mixer_set_port(&as->codec_if, &ctl);
    784 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
    785 					   AudioNvolume, AudioNmute);
    786 	ac97_mixer_set_port(&as->codec_if, &ctl);
    787 
    788 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
    789 					   AudioNsource, NULL);
    790 	ctl.type = AUDIO_MIXER_ENUM;
    791 	ctl.un.ord = 0;
    792 	ac97_mixer_set_port(&as->codec_if, &ctl);
    793 
    794 	return (0);
    795 }
    796 
    797 
    798 int
    799 ac97_query_devinfo(codec_if, dip)
    800 	struct ac97_codec_if *codec_if;
    801 	mixer_devinfo_t *dip;
    802 {
    803 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    804 
    805 	if (dip->index < as->num_source_info) {
    806 		struct ac97_source_info *si = &as->source_info[dip->index];
    807 		const char *name;
    808 
    809 		dip->type = si->type;
    810 		dip->mixer_class = si->mixer_class;
    811 		dip->prev = si->prev;
    812 		dip->next = si->next;
    813 
    814 		if (si->qualifier)
    815 			name = si->qualifier;
    816 		else if (si->device)
    817 			name = si->device;
    818 		else if (si->class)
    819 			name = si->class;
    820 		else
    821 			name = 0;
    822 
    823 		if (name)
    824 			strcpy(dip->label.name, name);
    825 
    826 		memcpy(&dip->un, si->info, si->info_size);
    827 
    828 		/* Set the delta for volume sources */
    829 		if (dip->type == AUDIO_MIXER_VALUE)
    830 			dip->un.v.delta = 1 << (8 - si->bits);
    831 
    832 		return (0);
    833 	}
    834 
    835 	return (ENXIO);
    836 }
    837 
    838 
    839 
    840 int
    841 ac97_mixer_set_port(codec_if, cp)
    842 	struct ac97_codec_if *codec_if;
    843 	mixer_ctrl_t *cp;
    844 {
    845 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    846 	struct ac97_source_info *si = &as->source_info[cp->dev];
    847 	u_int16_t mask;
    848 	u_int16_t val, newval;
    849 	int error;
    850 
    851 	if (cp->dev < 0 || cp->dev >= as->num_source_info)
    852 		return (EINVAL);
    853 
    854 	if (cp->type != si->type)
    855 		return (EINVAL);
    856 
    857 	ac97_read(as, si->reg, &val);
    858 
    859 	DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
    860 
    861 	mask = (1 << si->bits) - 1;
    862 
    863 	switch (cp->type) {
    864 	case AUDIO_MIXER_ENUM:
    865 		if (cp->un.ord > mask || cp->un.ord < 0)
    866 			return (EINVAL);
    867 
    868 		newval = (cp->un.ord << si->ofs);
    869 		if (si->reg == AC97_REG_RECORD_SELECT) {
    870 			newval |= (newval << (8 + si->ofs));
    871 			mask |= (mask << 8);
    872 		}
    873 		break;
    874 	case AUDIO_MIXER_VALUE:
    875 	{
    876 		const struct audio_mixer_value *value = si->info;
    877 		u_int16_t  l, r;
    878 
    879 		if ((cp->un.value.num_channels <= 0) ||
    880 		    (cp->un.value.num_channels > value->num_channels))
    881 			return (EINVAL);
    882 
    883 		if (cp->un.value.num_channels == 1) {
    884 			l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
    885 		} else {
    886 			if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
    887 				l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
    888 				r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
    889 			} else {	/* left/right is reversed here */
    890 				r = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
    891 				l = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
    892 			}
    893 
    894 		}
    895 
    896 		if (!si->polarity) {
    897 			l = 255 - l;
    898 			r = 255 - r;
    899 		}
    900 
    901 		l = l >> (8 - si->bits);
    902 		r = r >> (8 - si->bits);
    903 
    904 		newval = ((l & mask) << si->ofs);
    905 		if (value->num_channels == 2) {
    906 			newval |= ((r & mask) << (si->ofs + 8));
    907 			mask |= (mask << 8);
    908 		}
    909 
    910 		break;
    911 	}
    912 	default:
    913 		return (EINVAL);
    914 	}
    915 
    916 	mask = mask << si->ofs;
    917 	error = ac97_write(as, si->reg, (val & ~mask) | newval);
    918 	if (error)
    919 		return (error);
    920 
    921 	return (0);
    922 }
    923 
    924 int
    925 ac97_get_portnum_by_name(codec_if, class, device, qualifier)
    926 	struct ac97_codec_if *codec_if;
    927 	char *class, *device, *qualifier;
    928 {
    929 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    930 	int idx;
    931 
    932 	for (idx = 0; idx < as->num_source_info; idx++) {
    933 		struct ac97_source_info *si = &as->source_info[idx];
    934 		if (ac97_str_equal(class, si->class) &&
    935 		    ac97_str_equal(device, si->device) &&
    936 		    ac97_str_equal(qualifier, si->qualifier))
    937 			return (idx);
    938 	}
    939 
    940 	return (-1);
    941 }
    942 
    943 int
    944 ac97_mixer_get_port(codec_if, cp)
    945 	struct ac97_codec_if *codec_if;
    946 	mixer_ctrl_t *cp;
    947 {
    948 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    949 	struct ac97_source_info *si = &as->source_info[cp->dev];
    950 	u_int16_t mask;
    951 	u_int16_t val;
    952 
    953 	if (cp->dev < 0 || cp->dev >= as->num_source_info)
    954 		return (EINVAL);
    955 
    956 	if (cp->type != si->type)
    957 		return (EINVAL);
    958 
    959 	ac97_read(as, si->reg, &val);
    960 
    961 	DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
    962 
    963 	mask = (1 << si->bits) - 1;
    964 
    965 	switch (cp->type) {
    966 	case AUDIO_MIXER_ENUM:
    967 		cp->un.ord = (val >> si->ofs) & mask;
    968 		DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs, mask, cp->un.ord));
    969 		break;
    970 	case AUDIO_MIXER_VALUE:
    971 	{
    972 		const struct audio_mixer_value *value = si->info;
    973 		u_int16_t  l, r;
    974 
    975 		if ((cp->un.value.num_channels <= 0) ||
    976 		    (cp->un.value.num_channels > value->num_channels))
    977 			return (EINVAL);
    978 
    979 		if (value->num_channels == 1) {
    980 			l = r = (val >> si->ofs) & mask;
    981 		} else {
    982 			if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
    983 				l = (val >> si->ofs) & mask;
    984 				r = (val >> (si->ofs + 8)) & mask;
    985 			} else {	/* host has reversed channels */
    986 				r = (val >> si->ofs) & mask;
    987 				l = (val >> (si->ofs + 8)) & mask;
    988 			}
    989 		}
    990 
    991 		l = (l << (8 - si->bits));
    992 		r = (r << (8 - si->bits));
    993 		if (!si->polarity) {
    994 			l = 255 - l;
    995 			r = 255 - r;
    996 		}
    997 
    998 		/* The EAP driver averages l and r for stereo
    999 		   channels that are requested in MONO mode. Does this
   1000 		   make sense? */
   1001 		if (cp->un.value.num_channels == 1) {
   1002 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
   1003 		} else if (cp->un.value.num_channels == 2) {
   1004 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
   1005 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
   1006 		}
   1007 
   1008 		break;
   1009 	}
   1010 	default:
   1011 		return (EINVAL);
   1012 	}
   1013 
   1014 	return (0);
   1015 }
   1016 
   1017 
   1018 int
   1019 ac97_set_rate(struct ac97_codec_if *codec_if, int target, u_long *rate)
   1020 {
   1021 	struct ac97_softc *as;
   1022 	u_long value;
   1023 	u_int16_t ext_stat;
   1024 	u_int16_t actual;
   1025 	u_int16_t power;
   1026 	u_int16_t power_bit;
   1027 
   1028 	as = (struct ac97_softc *)codec_if;
   1029 	if (target == AC97_REG_PCM_MIC_ADC_RATE) {
   1030 		if (!(as->ext_id & AC97_EXT_AUDIO_VRM)) {
   1031 			*rate = AC97_SINGLE_RATE;
   1032 			return 0;
   1033 		}
   1034 	} else {
   1035 		if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
   1036 			*rate = AC97_SINGLE_RATE;
   1037 			return 0;
   1038 		}
   1039 	}
   1040 	value = *rate * AC97_STANDARD_CLOCK / as->ac97_clock;
   1041 	ext_stat = 0;
   1042 	/*
   1043 	 * PCM_FRONT_DAC_RATE/PCM_SURR_DAC_RATE/PCM_LFE_DAC_RATE
   1044 	 *	Check VRA, DRA
   1045 	 * PCM_LR_ADC_RATE
   1046 	 *	Check VRA
   1047 	 * PCM_MIC_ADC_RATE
   1048 	 *	Check VRM
   1049 	 */
   1050 	switch (target) {
   1051 	case AC97_REG_PCM_FRONT_DAC_RATE:
   1052 	case AC97_REG_PCM_SURR_DAC_RATE:
   1053 	case AC97_REG_PCM_LFE_DAC_RATE:
   1054 		power_bit = AC97_POWER_OUT;
   1055 		if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
   1056 			*rate = AC97_SINGLE_RATE;
   1057 			return 0;
   1058 		}
   1059 		if (as->ext_id & AC97_EXT_AUDIO_DRA) {
   1060 			ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &ext_stat);
   1061 			if (value > 0x1ffff) {
   1062 				return EINVAL;
   1063 			} else if (value > 0xffff) {
   1064 				/* Enable DRA */
   1065 				ext_stat |= AC97_EXT_AUDIO_DRA;
   1066 				ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, ext_stat);
   1067 				value /= 2;
   1068 			} else {
   1069 				/* Disable DRA */
   1070 				ext_stat &= ~AC97_EXT_AUDIO_DRA;
   1071 				ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, ext_stat);
   1072 			}
   1073 		} else {
   1074 			if (value > 0xffff)
   1075 				return EINVAL;
   1076 		}
   1077 		break;
   1078 	case AC97_REG_PCM_LR_ADC_RATE:
   1079 		power_bit = AC97_POWER_IN;
   1080 		if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
   1081 			*rate = AC97_SINGLE_RATE;
   1082 			return 0;
   1083 		}
   1084 		if (value > 0xffff)
   1085 			return EINVAL;
   1086 		break;
   1087 	case AC97_REG_PCM_MIC_ADC_RATE:
   1088 		power_bit = AC97_POWER_IN;
   1089 		if (!(as->ext_id & AC97_EXT_AUDIO_VRM)) {
   1090 			*rate = AC97_SINGLE_RATE;
   1091 			return 0;
   1092 		}
   1093 		if (value > 0xffff)
   1094 			return EINVAL;
   1095 		break;
   1096 	default:
   1097 		printf("%s: Unknown register: 0x%x\n", __func__, target);
   1098 		return EINVAL;
   1099 	}
   1100 
   1101 	ac97_read(as, AC97_REG_POWER, &power);
   1102 	ac97_write(as, AC97_REG_POWER, power | power_bit);
   1103 
   1104 	ac97_write(as, target, (u_int16_t)value);
   1105 	ac97_read(as, target, &actual);
   1106 	actual = (u_int32_t)actual * as->ac97_clock / AC97_STANDARD_CLOCK;
   1107 
   1108 	ac97_write(as, AC97_REG_POWER, power);
   1109 	if (ext_stat & AC97_EXT_AUDIO_DRA) {
   1110 		*rate = actual * 2;
   1111 	} else {
   1112 		*rate = actual;
   1113 	}
   1114 	return 0;
   1115 }
   1116 
   1117 void
   1118 ac97_set_clock(struct ac97_codec_if *codec_if, unsigned int clock)
   1119 {
   1120 	struct ac97_softc *as;
   1121 
   1122 	as = (struct ac97_softc *)codec_if;
   1123 	as->ac97_clock = clock;
   1124 }
   1125 
   1126 u_int16_t
   1127 ac97_get_extcaps(struct ac97_codec_if *codec_if)
   1128 {
   1129 	struct ac97_softc *as;
   1130 
   1131 	as = (struct ac97_softc *)codec_if;
   1132 	return as->ext_id;
   1133 }
   1134