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