Home | History | Annotate | Line # | Download | only in ic
ac97.c revision 1.32
      1 /*      $NetBSD: ac97.c,v 1.32 2002/10/11 02:32:23 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.32 2002/10/11 02:32:23 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 			/* restore the default value */
    768 			ac97_write(as, AC97_REG_PCM_FRONT_DAC_RATE,
    769 				   AC97_SINGLE_RATE);
    770 		}
    771 	}
    772 
    773 	ac97_setup_source_info(as);
    774 
    775 	/* Just enable the DAC and master volumes by default */
    776 	memset(&ctl, 0, sizeof(ctl));
    777 
    778 	ctl.type = AUDIO_MIXER_ENUM;
    779 	ctl.un.ord = 0;  /* off */
    780 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
    781 					   AudioNmaster, AudioNmute);
    782 	ac97_mixer_set_port(&as->codec_if, &ctl);
    783 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCinputs,
    784 					   AudioNdac, AudioNmute);
    785 
    786 	ac97_mixer_set_port(&as->codec_if, &ctl);
    787 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
    788 					   AudioNvolume, AudioNmute);
    789 	ac97_mixer_set_port(&as->codec_if, &ctl);
    790 
    791 	ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
    792 					   AudioNsource, NULL);
    793 	ctl.type = AUDIO_MIXER_ENUM;
    794 	ctl.un.ord = 0;
    795 	ac97_mixer_set_port(&as->codec_if, &ctl);
    796 
    797 	return (0);
    798 }
    799 
    800 
    801 int
    802 ac97_query_devinfo(codec_if, dip)
    803 	struct ac97_codec_if *codec_if;
    804 	mixer_devinfo_t *dip;
    805 {
    806 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    807 
    808 	if (dip->index < as->num_source_info) {
    809 		struct ac97_source_info *si = &as->source_info[dip->index];
    810 		const char *name;
    811 
    812 		dip->type = si->type;
    813 		dip->mixer_class = si->mixer_class;
    814 		dip->prev = si->prev;
    815 		dip->next = si->next;
    816 
    817 		if (si->qualifier)
    818 			name = si->qualifier;
    819 		else if (si->device)
    820 			name = si->device;
    821 		else if (si->class)
    822 			name = si->class;
    823 		else
    824 			name = 0;
    825 
    826 		if (name)
    827 			strcpy(dip->label.name, name);
    828 
    829 		memcpy(&dip->un, si->info, si->info_size);
    830 
    831 		/* Set the delta for volume sources */
    832 		if (dip->type == AUDIO_MIXER_VALUE)
    833 			dip->un.v.delta = 1 << (8 - si->bits);
    834 
    835 		return (0);
    836 	}
    837 
    838 	return (ENXIO);
    839 }
    840 
    841 
    842 
    843 int
    844 ac97_mixer_set_port(codec_if, cp)
    845 	struct ac97_codec_if *codec_if;
    846 	mixer_ctrl_t *cp;
    847 {
    848 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    849 	struct ac97_source_info *si = &as->source_info[cp->dev];
    850 	u_int16_t mask;
    851 	u_int16_t val, newval;
    852 	int error;
    853 
    854 	if (cp->dev < 0 || cp->dev >= as->num_source_info)
    855 		return (EINVAL);
    856 
    857 	if (cp->type != si->type)
    858 		return (EINVAL);
    859 
    860 	ac97_read(as, si->reg, &val);
    861 
    862 	DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
    863 
    864 	mask = (1 << si->bits) - 1;
    865 
    866 	switch (cp->type) {
    867 	case AUDIO_MIXER_ENUM:
    868 		if (cp->un.ord > mask || cp->un.ord < 0)
    869 			return (EINVAL);
    870 
    871 		newval = (cp->un.ord << si->ofs);
    872 		if (si->reg == AC97_REG_RECORD_SELECT) {
    873 			newval |= (newval << (8 + si->ofs));
    874 			mask |= (mask << 8);
    875 		}
    876 		break;
    877 	case AUDIO_MIXER_VALUE:
    878 	{
    879 		const struct audio_mixer_value *value = si->info;
    880 		u_int16_t  l, r;
    881 
    882 		if ((cp->un.value.num_channels <= 0) ||
    883 		    (cp->un.value.num_channels > value->num_channels))
    884 			return (EINVAL);
    885 
    886 		if (cp->un.value.num_channels == 1) {
    887 			l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
    888 		} else {
    889 			if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
    890 				l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
    891 				r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
    892 			} else {	/* left/right is reversed here */
    893 				r = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
    894 				l = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
    895 			}
    896 
    897 		}
    898 
    899 		if (!si->polarity) {
    900 			l = 255 - l;
    901 			r = 255 - r;
    902 		}
    903 
    904 		l = l >> (8 - si->bits);
    905 		r = r >> (8 - si->bits);
    906 
    907 		newval = ((l & mask) << si->ofs);
    908 		if (value->num_channels == 2) {
    909 			newval |= ((r & mask) << (si->ofs + 8));
    910 			mask |= (mask << 8);
    911 		}
    912 
    913 		break;
    914 	}
    915 	default:
    916 		return (EINVAL);
    917 	}
    918 
    919 	mask = mask << si->ofs;
    920 	error = ac97_write(as, si->reg, (val & ~mask) | newval);
    921 	if (error)
    922 		return (error);
    923 
    924 	return (0);
    925 }
    926 
    927 int
    928 ac97_get_portnum_by_name(codec_if, class, device, qualifier)
    929 	struct ac97_codec_if *codec_if;
    930 	char *class, *device, *qualifier;
    931 {
    932 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    933 	int idx;
    934 
    935 	for (idx = 0; idx < as->num_source_info; idx++) {
    936 		struct ac97_source_info *si = &as->source_info[idx];
    937 		if (ac97_str_equal(class, si->class) &&
    938 		    ac97_str_equal(device, si->device) &&
    939 		    ac97_str_equal(qualifier, si->qualifier))
    940 			return (idx);
    941 	}
    942 
    943 	return (-1);
    944 }
    945 
    946 int
    947 ac97_mixer_get_port(codec_if, cp)
    948 	struct ac97_codec_if *codec_if;
    949 	mixer_ctrl_t *cp;
    950 {
    951 	struct ac97_softc *as = (struct ac97_softc *)codec_if;
    952 	struct ac97_source_info *si = &as->source_info[cp->dev];
    953 	u_int16_t mask;
    954 	u_int16_t val;
    955 
    956 	if (cp->dev < 0 || cp->dev >= as->num_source_info)
    957 		return (EINVAL);
    958 
    959 	if (cp->type != si->type)
    960 		return (EINVAL);
    961 
    962 	ac97_read(as, si->reg, &val);
    963 
    964 	DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
    965 
    966 	mask = (1 << si->bits) - 1;
    967 
    968 	switch (cp->type) {
    969 	case AUDIO_MIXER_ENUM:
    970 		cp->un.ord = (val >> si->ofs) & mask;
    971 		DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs, mask, cp->un.ord));
    972 		break;
    973 	case AUDIO_MIXER_VALUE:
    974 	{
    975 		const struct audio_mixer_value *value = si->info;
    976 		u_int16_t  l, r;
    977 
    978 		if ((cp->un.value.num_channels <= 0) ||
    979 		    (cp->un.value.num_channels > value->num_channels))
    980 			return (EINVAL);
    981 
    982 		if (value->num_channels == 1) {
    983 			l = r = (val >> si->ofs) & mask;
    984 		} else {
    985 			if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
    986 				l = (val >> si->ofs) & mask;
    987 				r = (val >> (si->ofs + 8)) & mask;
    988 			} else {	/* host has reversed channels */
    989 				r = (val >> si->ofs) & mask;
    990 				l = (val >> (si->ofs + 8)) & mask;
    991 			}
    992 		}
    993 
    994 		l = (l << (8 - si->bits));
    995 		r = (r << (8 - si->bits));
    996 		if (!si->polarity) {
    997 			l = 255 - l;
    998 			r = 255 - r;
    999 		}
   1000 
   1001 		/* The EAP driver averages l and r for stereo
   1002 		   channels that are requested in MONO mode. Does this
   1003 		   make sense? */
   1004 		if (cp->un.value.num_channels == 1) {
   1005 			cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
   1006 		} else if (cp->un.value.num_channels == 2) {
   1007 			cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
   1008 			cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
   1009 		}
   1010 
   1011 		break;
   1012 	}
   1013 	default:
   1014 		return (EINVAL);
   1015 	}
   1016 
   1017 	return (0);
   1018 }
   1019 
   1020 
   1021 int
   1022 ac97_set_rate(struct ac97_codec_if *codec_if, int target, u_long *rate)
   1023 {
   1024 	struct ac97_softc *as;
   1025 	u_long value;
   1026 	u_int16_t ext_stat;
   1027 	u_int16_t actual;
   1028 	u_int16_t power;
   1029 	u_int16_t power_bit;
   1030 
   1031 	as = (struct ac97_softc *)codec_if;
   1032 	if (target == AC97_REG_PCM_MIC_ADC_RATE) {
   1033 		if (!(as->ext_id & AC97_EXT_AUDIO_VRM)) {
   1034 			*rate = AC97_SINGLE_RATE;
   1035 			return 0;
   1036 		}
   1037 	} else {
   1038 		if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
   1039 			*rate = AC97_SINGLE_RATE;
   1040 			return 0;
   1041 		}
   1042 	}
   1043 	value = *rate * AC97_STANDARD_CLOCK / as->ac97_clock;
   1044 	ext_stat = 0;
   1045 	/*
   1046 	 * PCM_FRONT_DAC_RATE/PCM_SURR_DAC_RATE/PCM_LFE_DAC_RATE
   1047 	 *	Check VRA, DRA
   1048 	 * PCM_LR_ADC_RATE
   1049 	 *	Check VRA
   1050 	 * PCM_MIC_ADC_RATE
   1051 	 *	Check VRM
   1052 	 */
   1053 	switch (target) {
   1054 	case AC97_REG_PCM_FRONT_DAC_RATE:
   1055 	case AC97_REG_PCM_SURR_DAC_RATE:
   1056 	case AC97_REG_PCM_LFE_DAC_RATE:
   1057 		power_bit = AC97_POWER_OUT;
   1058 		if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
   1059 			*rate = AC97_SINGLE_RATE;
   1060 			return 0;
   1061 		}
   1062 		if (as->ext_id & AC97_EXT_AUDIO_DRA) {
   1063 			ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &ext_stat);
   1064 			if (value > 0x1ffff) {
   1065 				return EINVAL;
   1066 			} else if (value > 0xffff) {
   1067 				/* Enable DRA */
   1068 				ext_stat |= AC97_EXT_AUDIO_DRA;
   1069 				ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, ext_stat);
   1070 				value /= 2;
   1071 			} else {
   1072 				/* Disable DRA */
   1073 				ext_stat &= ~AC97_EXT_AUDIO_DRA;
   1074 				ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, ext_stat);
   1075 			}
   1076 		} else {
   1077 			if (value > 0xffff)
   1078 				return EINVAL;
   1079 		}
   1080 		break;
   1081 	case AC97_REG_PCM_LR_ADC_RATE:
   1082 		power_bit = AC97_POWER_IN;
   1083 		if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
   1084 			*rate = AC97_SINGLE_RATE;
   1085 			return 0;
   1086 		}
   1087 		if (value > 0xffff)
   1088 			return EINVAL;
   1089 		break;
   1090 	case AC97_REG_PCM_MIC_ADC_RATE:
   1091 		power_bit = AC97_POWER_IN;
   1092 		if (!(as->ext_id & AC97_EXT_AUDIO_VRM)) {
   1093 			*rate = AC97_SINGLE_RATE;
   1094 			return 0;
   1095 		}
   1096 		if (value > 0xffff)
   1097 			return EINVAL;
   1098 		break;
   1099 	default:
   1100 		printf("%s: Unknown register: 0x%x\n", __func__, target);
   1101 		return EINVAL;
   1102 	}
   1103 
   1104 	ac97_read(as, AC97_REG_POWER, &power);
   1105 	ac97_write(as, AC97_REG_POWER, power | power_bit);
   1106 
   1107 	ac97_write(as, target, (u_int16_t)value);
   1108 	ac97_read(as, target, &actual);
   1109 	actual = (u_int32_t)actual * as->ac97_clock / AC97_STANDARD_CLOCK;
   1110 
   1111 	ac97_write(as, AC97_REG_POWER, power);
   1112 	if (ext_stat & AC97_EXT_AUDIO_DRA) {
   1113 		*rate = actual * 2;
   1114 	} else {
   1115 		*rate = actual;
   1116 	}
   1117 	return 0;
   1118 }
   1119 
   1120 void
   1121 ac97_set_clock(struct ac97_codec_if *codec_if, unsigned int clock)
   1122 {
   1123 	struct ac97_softc *as;
   1124 
   1125 	as = (struct ac97_softc *)codec_if;
   1126 	as->ac97_clock = clock;
   1127 }
   1128 
   1129 u_int16_t
   1130 ac97_get_extcaps(struct ac97_codec_if *codec_if)
   1131 {
   1132 	struct ac97_softc *as;
   1133 
   1134 	as = (struct ac97_softc *)codec_if;
   1135 	return as->ext_id;
   1136 }
   1137