[BACK]Return to ac97.c CVS log [TXT][DIR] Up to [local] / sys / dev / ic

Annotation of sys/dev/ic/ac97.c, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: ac97.c,v 1.63 2007/07/27 01:48:04 ian Exp $   */
        !             2:
        !             3: /*
        !             4:  * Copyright (c) 1999, 2000 Constantine Sapuntzakis
        !             5:  *
        !             6:  * Author:     Constantine Sapuntzakis <csapuntz@stanford.edu>
        !             7:  *
        !             8:  * Redistribution and use in source and binary forms, with or without
        !             9:  * modification, are permitted provided that the following conditions
        !            10:  * are met:
        !            11:  * 1. Redistributions of source code must retain the above copyright
        !            12:  *    notice, this list of conditions and the following disclaimer.
        !            13:  * 2. Redistributions in binary form must reproduce the above copyright
        !            14:  *    notice, this list of conditions and the following disclaimer in the
        !            15:  *    documentation and/or other materials provided with the distribution.
        !            16:  * 3. The name of the author may not be used to endorse or promote
        !            17:  *    products derived from this software without specific prior written
        !            18:  *    permission.
        !            19:  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
        !            20:  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
        !            21:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            22:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
        !            23:  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
        !            24:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
        !            25:  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
        !            26:  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
        !            27:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
        !            28:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
        !            29:  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
        !            30:  * DAMAGE.  */
        !            31:
        !            32: /* Partially inspired by FreeBSD's sys/dev/pcm/ac97.c. It came with
        !            33:    the following copyright */
        !            34:
        !            35: /*
        !            36:  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
        !            37:  * All rights reserved.
        !            38:  *
        !            39:  * Redistribution and use in source and binary forms, with or without
        !            40:  * modification, are permitted provided that the following conditions
        !            41:  * are met:
        !            42:  * 1. Redistributions of source code must retain the above copyright
        !            43:  *    notice, this list of conditions and the following disclaimer.
        !            44:  * 2. Redistributions in binary form must reproduce the above copyright
        !            45:  *    notice, this list of conditions and the following disclaimer in the
        !            46:  *    documentation and/or other materials provided with the distribution.
        !            47:  *
        !            48:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
        !            49:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            50:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            51:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
        !            52:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            53:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            54:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            55:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            56:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            57:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            58:  * SUCH DAMAGE.
        !            59:  *
        !            60:  * $FreeBSD$
        !            61:  */
        !            62:
        !            63: #include <sys/param.h>
        !            64: #include <sys/systm.h>
        !            65: #include <sys/kernel.h>
        !            66: #include <sys/malloc.h>
        !            67: #include <sys/device.h>
        !            68:
        !            69: #include <sys/audioio.h>
        !            70: #include <dev/audio_if.h>
        !            71: #include <dev/ic/ac97.h>
        !            72:
        !            73: const struct audio_mixer_enum ac97_on_off = {
        !            74:        2,
        !            75:        { { { AudioNoff } , 0 },
        !            76:        { { AudioNon }  , 1 } }
        !            77: };
        !            78:
        !            79: const struct audio_mixer_enum ac97_mic_select = {
        !            80:        2,
        !            81:        { { { AudioNmicrophone "0" }, 0 },
        !            82:        { { AudioNmicrophone "1" }, 1 } }
        !            83: };
        !            84:
        !            85: const struct audio_mixer_enum ac97_mono_select = {
        !            86:        2,
        !            87:        { { { AudioNmixerout }, 0 },
        !            88:        { { AudioNmicrophone }, 1 } }
        !            89: };
        !            90:
        !            91: const struct audio_mixer_enum ac97_source = {
        !            92:        8,
        !            93:        { { { AudioNmicrophone } , 0 },
        !            94:        { { AudioNcd }, 1 },
        !            95:        { { "video" }, 2 },
        !            96:        { { AudioNaux }, 3 },
        !            97:        { { AudioNline }, 4 },
        !            98:        { { AudioNmixerout }, 5 },
        !            99:        { { AudioNmixerout AudioNmono }, 6 },
        !           100:        { { "phone" }, 7 }}
        !           101: };
        !           102:
        !           103: /*
        !           104:  * Due to different values for each source that uses these structures,
        !           105:  * the ac97_query_devinfo function sets delta in mixer_devinfo_t using
        !           106:  * ac97_source_info.bits.
        !           107:  */
        !           108: const struct audio_mixer_value ac97_volume_stereo = {
        !           109:        { AudioNvolume },
        !           110:        2
        !           111: };
        !           112:
        !           113: const struct audio_mixer_value ac97_volume_mono = {
        !           114:        { AudioNvolume },
        !           115:        1
        !           116: };
        !           117:
        !           118: #define WRAP(a)  &a, sizeof(a)
        !           119:
        !           120: const struct ac97_source_info {
        !           121:        char *class;
        !           122:        char *device;
        !           123:        char *qualifier;
        !           124:        int  type;
        !           125:
        !           126:        const void *info;
        !           127:        int16_t info_size;
        !           128:
        !           129:        u_int8_t  reg;
        !           130:        u_int8_t  bits:3;
        !           131:        u_int8_t  ofs:4;
        !           132:        u_int8_t  mute:1;
        !           133:        u_int8_t  polarity:1;           /* Does 0 == MAX or MIN */
        !           134:        u_int16_t default_value;
        !           135:
        !           136:        int16_t  prev;
        !           137:        int16_t  next;
        !           138:        int16_t  mixer_class;
        !           139: } source_info[] = {
        !           140:        {
        !           141:                AudioCinputs,   NULL,           NULL,   AUDIO_MIXER_CLASS,
        !           142:        }, {
        !           143:                AudioCoutputs,  NULL,           NULL,   AUDIO_MIXER_CLASS,
        !           144:        }, {
        !           145:                AudioCrecord,   NULL,           NULL,   AUDIO_MIXER_CLASS,
        !           146:        }, {
        !           147:                /* Stereo master volume*/
        !           148:                AudioCoutputs,  AudioNmaster,   NULL,   AUDIO_MIXER_VALUE,
        !           149:                WRAP(ac97_volume_stereo),
        !           150:                AC97_REG_MASTER_VOLUME, 5, 0, 1, 0, 0x8000
        !           151:        }, {
        !           152:                /* Mono volume */
        !           153:                AudioCoutputs,  AudioNmono,     NULL,   AUDIO_MIXER_VALUE,
        !           154:                WRAP(ac97_volume_mono),
        !           155:                AC97_REG_MASTER_VOLUME_MONO, 6, 0, 1, 0, 0x8000
        !           156:        }, {
        !           157:                AudioCoutputs,  AudioNmono, AudioNsource, AUDIO_MIXER_ENUM,
        !           158:                WRAP(ac97_mono_select),
        !           159:                AC97_REG_GP, 1, 9, 0, 0, 0x0000
        !           160:        }, {
        !           161:                /* Headphone volume */
        !           162:                AudioCoutputs,  AudioNheadphone, NULL,  AUDIO_MIXER_VALUE,
        !           163:                WRAP(ac97_volume_stereo),
        !           164:                AC97_REG_HEADPHONE_VOLUME, 6, 0, 1, 0, 0x8000
        !           165:        }, {
        !           166:                AudioCoutputs,  AudioNbass,     NULL,   AUDIO_MIXER_VALUE,
        !           167:                WRAP(ac97_volume_mono),
        !           168:                AC97_REG_MASTER_TONE, 4, 8, 0, 0, 0x0f0f
        !           169:        }, {
        !           170:                AudioCoutputs,  AudioNtreble,   NULL,   AUDIO_MIXER_VALUE,
        !           171:                WRAP(ac97_volume_mono),
        !           172:                AC97_REG_MASTER_TONE, 4, 0, 0, 0, 0x0f0f
        !           173:        }, {
        !           174:                /* PC Beep Volume */
        !           175:                AudioCinputs,   AudioNspeaker,  NULL,   AUDIO_MIXER_VALUE,
        !           176:                WRAP(ac97_volume_mono),
        !           177:                AC97_REG_PCBEEP_VOLUME, 4, 1, 1, 0, 0x0000
        !           178:        }, {
        !           179:                /* Phone */
        !           180:                AudioCinputs,   "phone",        NULL,   AUDIO_MIXER_VALUE,
        !           181:                WRAP(ac97_volume_mono),
        !           182:                AC97_REG_PHONE_VOLUME, 5, 0, 1, 0, 0x8008
        !           183:        }, {
        !           184:                /* Mic Volume */
        !           185:                AudioCinputs,   AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
        !           186:                WRAP(ac97_volume_mono),
        !           187:                AC97_REG_MIC_VOLUME, 5, 0, 1, 0, 0x8008
        !           188:        }, {
        !           189:                AudioCinputs,   AudioNmicrophone, AudioNpreamp, AUDIO_MIXER_ENUM,
        !           190:                WRAP(ac97_on_off),
        !           191:                AC97_REG_MIC_VOLUME, 1, 6, 0, 0, 0x8008
        !           192:        }, {
        !           193:                AudioCinputs,   AudioNmicrophone, AudioNsource, AUDIO_MIXER_ENUM,
        !           194:                WRAP(ac97_mic_select),
        !           195:                AC97_REG_GP, 1, 8, 0, 0x0000
        !           196:        }, {
        !           197:                /* Line in Volume */
        !           198:                AudioCinputs,   AudioNline,     NULL,   AUDIO_MIXER_VALUE,
        !           199:                WRAP(ac97_volume_stereo),
        !           200:                AC97_REG_LINEIN_VOLUME, 5, 0, 1, 0, 0x8808
        !           201:        }, {
        !           202:                /* CD Volume */
        !           203:                AudioCinputs,   AudioNcd,       NULL,   AUDIO_MIXER_VALUE,
        !           204:                WRAP(ac97_volume_stereo),
        !           205:                AC97_REG_CD_VOLUME, 5, 0, 1, 0, 0x8808
        !           206:        }, {
        !           207:                /* Video Volume */
        !           208:                AudioCinputs,   "video",        NULL,   AUDIO_MIXER_VALUE,
        !           209:                WRAP(ac97_volume_stereo),
        !           210:                AC97_REG_VIDEO_VOLUME, 5, 0, 1, 0, 0x8808
        !           211:        }, {
        !           212:                /* AUX volume */
        !           213:                AudioCinputs,   AudioNaux,      NULL,   AUDIO_MIXER_VALUE,
        !           214:                WRAP(ac97_volume_stereo),
        !           215:                AC97_REG_AUX_VOLUME, 5, 0, 1, 0, 0x8808
        !           216:        }, {
        !           217:                /* PCM out volume */
        !           218:                AudioCinputs,   AudioNdac,      NULL,   AUDIO_MIXER_VALUE,
        !           219:                WRAP(ac97_volume_stereo),
        !           220:                AC97_REG_PCMOUT_VOLUME, 5, 0, 1, 0, 0x8808
        !           221:        }, {
        !           222:                /* Record Source - some logic for this is hard coded - see below */
        !           223:                AudioCrecord,   AudioNsource,   NULL,   AUDIO_MIXER_ENUM,
        !           224:                WRAP(ac97_source),
        !           225:                AC97_REG_RECORD_SELECT, 3, 0, 0, 0, 0x0000
        !           226:        }, {
        !           227:                /* Record Gain */
        !           228:                AudioCrecord,   AudioNvolume,   NULL,   AUDIO_MIXER_VALUE,
        !           229:                WRAP(ac97_volume_stereo),
        !           230:                AC97_REG_RECORD_GAIN, 4, 0, 1, 0, 0x8000
        !           231:        }, {
        !           232:                /* Record Gain mic */
        !           233:                AudioCrecord,   AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
        !           234:                WRAP(ac97_volume_mono),
        !           235:                AC97_REG_RECORD_GAIN_MIC, 4, 0, 1, 1, 0x8000
        !           236:        }, {
        !           237:                /* */
        !           238:                AudioCoutputs,  AudioNloudness, NULL,   AUDIO_MIXER_ENUM,
        !           239:                WRAP(ac97_on_off),
        !           240:                AC97_REG_GP, 1, 12, 0, 0, 0x0000
        !           241:        }, {
        !           242:                AudioCoutputs,  AudioNspatial,  NULL,   AUDIO_MIXER_ENUM,
        !           243:                WRAP(ac97_on_off),
        !           244:                AC97_REG_GP, 1, 13, 0, 0, 0x0000
        !           245:        }, {
        !           246:                AudioCoutputs,  AudioNspatial,  AudioNcenter,AUDIO_MIXER_VALUE,
        !           247:                WRAP(ac97_volume_mono),
        !           248:                AC97_REG_3D_CONTROL, 4, 8, 0, 1, 0x0000
        !           249:        }, {
        !           250:                AudioCoutputs,  AudioNspatial,  AudioNdepth, AUDIO_MIXER_VALUE,
        !           251:                WRAP(ac97_volume_mono),
        !           252:                AC97_REG_3D_CONTROL, 4, 0, 0, 1, 0x0000
        !           253:        }, {
        !           254:                /* Surround volume */
        !           255:                AudioCoutputs,  AudioNsurround, NULL,   AUDIO_MIXER_VALUE,
        !           256:                WRAP(ac97_volume_stereo),
        !           257:                AC97_REG_SURROUND_VOLUME, 6, 0, 1, 0, 0x8080
        !           258:        }, {
        !           259:                /* Center volume */
        !           260:                AudioCoutputs,  AudioNcenter,   NULL,   AUDIO_MIXER_VALUE,
        !           261:                WRAP(ac97_volume_mono),
        !           262:                AC97_REG_CENTER_LFE_VOLUME, 6, 0, 1, 0, 0x8080
        !           263:        }, {
        !           264:                /* LFE volume */
        !           265:                AudioCoutputs,  AudioNlfe,      NULL,   AUDIO_MIXER_VALUE,
        !           266:                WRAP(ac97_volume_mono),
        !           267:                AC97_REG_CENTER_LFE_VOLUME, 6, 8, 1, 0, 0x8080
        !           268:        }, {
        !           269:                /* External Amp */
        !           270:                AudioCoutputs,  AudioNextamp,   NULL,   AUDIO_MIXER_ENUM,
        !           271:                WRAP(ac97_on_off),
        !           272:                AC97_REG_POWER, 1, 15, 0, 0, 0x0000
        !           273:        }
        !           274:
        !           275:        /* Missing features: Simulated Stereo, POP, Loopback mode */
        !           276: };
        !           277:
        !           278: #define SOURCE_INFO_SIZE (sizeof(source_info)/sizeof(source_info[0]))
        !           279:
        !           280: /*
        !           281:  * Check out http://www.intel.com/technology/computing/audio/index.htm
        !           282:  * for information on AC-97
        !           283:  */
        !           284:
        !           285: struct ac97_softc {
        !           286:        struct ac97_codec_if codec_if;
        !           287:        struct ac97_host_if *host_if;
        !           288:        struct ac97_source_info source_info[2 * SOURCE_INFO_SIZE];
        !           289:        int num_source_info;
        !           290:        enum ac97_host_flags host_flags;
        !           291:        u_int16_t caps, ext_id;
        !           292:        u_int16_t shadow_reg[128];
        !           293: };
        !           294:
        !           295: int    ac97_mixer_get_port(struct ac97_codec_if *, mixer_ctrl_t *);
        !           296: int    ac97_mixer_set_port(struct ac97_codec_if *, mixer_ctrl_t *);
        !           297: int    ac97_query_devinfo(struct ac97_codec_if *, mixer_devinfo_t *);
        !           298: int    ac97_get_portnum_by_name(struct ac97_codec_if *, char *, char *,
        !           299:            char *);
        !           300: void   ac97_restore_shadow(struct ac97_codec_if *);
        !           301:
        !           302: void   ac97_ad1886_init(struct ac97_softc *);
        !           303: void   ac97_ad198x_init(struct ac97_softc *);
        !           304: void   ac97_alc655_init(struct ac97_softc *);
        !           305: void   ac97_cx20468_init(struct ac97_softc *);
        !           306:
        !           307: struct ac97_codec_if_vtbl ac97civ = {
        !           308:        ac97_mixer_get_port,
        !           309:        ac97_mixer_set_port,
        !           310:        ac97_query_devinfo,
        !           311:        ac97_get_portnum_by_name,
        !           312:        ac97_restore_shadow
        !           313: };
        !           314:
        !           315: const struct ac97_codecid {
        !           316:        u_int8_t id;
        !           317:        u_int8_t mask;
        !           318:        u_int8_t rev;
        !           319:        u_int8_t shift; /* no use yet */
        !           320:        char * const name;
        !           321:        void (*init)(struct ac97_softc *);
        !           322: }  ac97_ad[] = {
        !           323:        { 0x03, 0xff, 0, 0,     "AD1819" },
        !           324:        { 0x40, 0xff, 0, 0,     "AD1881" },
        !           325:        { 0x48, 0xff, 0, 0,     "AD1881A" },
        !           326:        { 0x60, 0xff, 0, 0,     "AD1885" },
        !           327:        { 0x61, 0xff, 0, 0,     "AD1886",       ac97_ad1886_init },
        !           328:        { 0x63, 0xff, 0, 0,     "AD1886A" },
        !           329:        { 0x68, 0xff, 0, 0,     "AD1888",       ac97_ad198x_init },
        !           330:        { 0x70, 0xff, 0, 0,     "AD1980" },
        !           331:        { 0x72, 0xff, 0, 0,     "AD1981A" },
        !           332:        { 0x74, 0xff, 0, 0,     "AD1981B" },
        !           333:        { 0x75, 0xff, 0, 0,     "AD1985",       ac97_ad198x_init },
        !           334: }, ac97_ak[] = {
        !           335:        { 0x00, 0xfe, 1, 0,     "AK4540" },
        !           336:        { 0x01, 0xfe, 1, 0,     "AK4540" },
        !           337:        { 0x02, 0xff, 0, 0,     "AK4543" },
        !           338:        { 0x05, 0xff, 0, 0,     "AK4544" },
        !           339:        { 0x06, 0xff, 0, 0,     "AK4544A" },
        !           340:        { 0x07, 0xff, 0, 0,     "AK4545" },
        !           341: }, ac97_av[] = {
        !           342:        { 0x10, 0xff, 0, 0,     "ALC200" },
        !           343:        { 0x20, 0xff, 0, 0,     "ALC650" },
        !           344:        { 0x21, 0xff, 0, 0,     "ALC650D" },
        !           345:        { 0x22, 0xff, 0, 0,     "ALC650E" },
        !           346:        { 0x23, 0xff, 0, 0,     "ALC650F" },
        !           347:        { 0x30, 0xff, 0, 0,     "ALC101" },
        !           348:        { 0x40, 0xff, 0, 0,     "ALC202" },
        !           349:        { 0x50, 0xff, 0, 0,     "ALC250" },
        !           350:        { 0x52, 0xff, 0, 0,     "ALC250A?" },
        !           351:        { 0x60, 0xf0, 0xf, 0,   "ALC655",       ac97_alc655_init },
        !           352:        { 0x70, 0xf0, 0xf, 0,   "ALC203" },
        !           353:        { 0x80, 0xf0, 0xf, 0,   "ALC658",       ac97_alc655_init },
        !           354:        { 0x90, 0xf0, 0xf, 0,   "ALC850" },
        !           355: }, ac97_rl[] = {
        !           356:        { 0x00, 0xf0, 0xf, 0,   "RL5306" },
        !           357:        { 0x10, 0xf0, 0xf, 0,   "RL5382" },
        !           358:        { 0x20, 0xf0, 0xf, 0,   "RL5383" },
        !           359: }, ac97_cm[] = {
        !           360:        { 0x41, 0xff, 0, 0,     "CMI9738" },
        !           361:        { 0x61, 0xff, 0, 0,     "CMI9739" },
        !           362:        { 0x78, 0xff, 0, 0,     "CMI9761A" },
        !           363:        { 0x82, 0xff, 0, 0,     "CMI9761B" },
        !           364:        { 0x83, 0xff, 0, 0,     "CMI9761A+" },
        !           365: }, ac97_cr[] = {
        !           366:        { 0x84, 0xff, 0, 0,     "EV1938" },
        !           367: }, ac97_cs[] = {
        !           368:        { 0x00, 0xf8, 7, 0,     "CS4297" },
        !           369:        { 0x10, 0xf8, 7, 0,     "CS4297A" },
        !           370:        { 0x20, 0xf8, 7, 0,     "CS4298" },
        !           371:        { 0x28, 0xf8, 7, 0,     "CS4294" },
        !           372:        { 0x30, 0xf8, 7, 0,     "CS4299" },
        !           373:        { 0x48, 0xf8, 7, 0,     "CS4201" },
        !           374:        { 0x58, 0xf8, 7, 0,     "CS4205" },
        !           375:        { 0x60, 0xf8, 7, 0,     "CS4291" },
        !           376:        { 0x70, 0xf8, 7, 0,     "CS4202" },
        !           377: }, ac97_cx[] = {
        !           378:        { 0x21, 0xff, 0, 0,     "HSD11246" },
        !           379:        { 0x28, 0xf8, 7, 0,     "CX20468",      ac97_cx20468_init },
        !           380:        { 0x30, 0xff, 0, 0,     "CXT48", },
        !           381:        { 0x42, 0xff, 0, 0,     "CXT66", },
        !           382: }, ac97_dt[] = {
        !           383:        { 0x00, 0xff, 0, 0,     "DT0398" },
        !           384: }, ac97_em[] = {
        !           385:        { 0x23, 0xff, 0, 0,     "EM28023" },
        !           386:        { 0x28, 0xff, 0, 0,     "EM28028" },
        !           387: }, ac97_es[] = {
        !           388:        { 0x08, 0xff, 0, 0,     "ES1921" },
        !           389: }, ac97_is[] = {
        !           390:        { 0x00, 0xff, 0, 0,     "HMP9701" },
        !           391: }, ac97_ic[] = {
        !           392:        { 0x01, 0xff, 0, 0,     "ICE1230" },
        !           393:        { 0x11, 0xff, 0, 0,     "ICE1232" },
        !           394:        { 0x14, 0xff, 0, 0,     "ICE1232A" },
        !           395:        { 0x51, 0xff, 0, 0,     "VIA VT1616" },
        !           396:        { 0x52, 0xff, 0, 0,     "VIA VT1616i" },
        !           397: }, ac97_it[] = {
        !           398:        { 0x20, 0xff, 0, 0,     "ITE2226E" },
        !           399:        { 0x60, 0xff, 0, 0,     "ITE2646E" },
        !           400: }, ac97_ns[] = {
        !           401:        { 0x00, 0xff, 0, 0,     "LM454[03568]" },
        !           402:        { 0x31, 0xff, 0, 0,     "LM4549" },
        !           403:        { 0x40, 0xff, 0, 0,     "LM4540" },
        !           404:        { 0x43, 0xff, 0, 0,     "LM4543" },
        !           405:        { 0x46, 0xff, 0, 0,     "LM4546A" },
        !           406:        { 0x48, 0xff, 0, 0,     "LM4548A" },
        !           407:        { 0x49, 0xff, 0, 0,     "LM4549A" },
        !           408:        { 0x50, 0xff, 0, 0,     "LM4550" },
        !           409: }, ac97_ps[] = {
        !           410:        { 0x01, 0xff, 0, 0,     "UCB1510" },
        !           411:        { 0x04, 0xff, 0, 0,     "UCB1400" },
        !           412: }, ac97_sl[] = {
        !           413:        { 0x20, 0xe0, 0, 0,     "Si3036/38" },
        !           414: }, ac97_st[] = {
        !           415:        { 0x00, 0xff, 0, 0,     "STAC9700" },
        !           416:        { 0x04, 0xff, 0, 0,     "STAC970[135]" },
        !           417:        { 0x05, 0xff, 0, 0,     "STAC9704" },
        !           418:        { 0x08, 0xff, 0, 0,     "STAC9708/11" },
        !           419:        { 0x09, 0xff, 0, 0,     "STAC9721/23" },
        !           420:        { 0x44, 0xff, 0, 0,     "STAC9744/45" },
        !           421:        { 0x50, 0xff, 0, 0,     "STAC9750/51" },
        !           422:        { 0x52, 0xff, 0, 0,     "STAC9752/53" },
        !           423:        { 0x56, 0xff, 0, 0,     "STAC9756/57" },
        !           424:        { 0x58, 0xff, 0, 0,     "STAC9758/59" },
        !           425:        { 0x60, 0xff, 0, 0,     "STAC9760/61" },
        !           426:        { 0x62, 0xff, 0, 0,     "STAC9762/63" },
        !           427:        { 0x66, 0xff, 0, 0,     "STAC9766/67" },
        !           428:        { 0x84, 0xff, 0, 0,     "STAC9784/85" },
        !           429: }, ac97_vi[] = {
        !           430:        { 0x61, 0xff, 0, 0,     "VT1612A" },
        !           431: }, ac97_tt[] = {
        !           432:        { 0x02, 0xff, 0, 0,     "TR28022" },
        !           433:        { 0x03, 0xff, 0, 0,     "TR28023" },
        !           434:        { 0x06, 0xff, 0, 0,     "TR28026" },
        !           435:        { 0x08, 0xff, 0, 0,     "TR28028" },
        !           436:        { 0x23, 0xff, 0, 0,     "TR28602" },
        !           437: }, ac97_ti[] = {
        !           438:        { 0x20, 0xff, 0, 0,     "TLC320AD9xC" },
        !           439: }, ac97_wb[] = {
        !           440:        { 0x01, 0xff, 0, 0,     "W83971D" },
        !           441: }, ac97_wo[] = {
        !           442:        { 0x00, 0xff, 0, 0,     "WM9701A" },
        !           443:        { 0x03, 0xff, 0, 0,     "WM9704M/Q-0" }, /* & WM9703 */
        !           444:        { 0x04, 0xff, 0, 0,     "WM9704M/Q-1" },
        !           445:        { 0x05, 0xff, 0, 0,     "WM9705/10" },
        !           446:        { 0x09, 0xff, 0, 0,     "WM9709" },
        !           447:        { 0x12, 0xff, 0, 0,     "WM9711/12" },
        !           448: }, ac97_ym[] = {
        !           449:        { 0x00, 0xff, 0, 0,     "YMF743-S" },
        !           450:        { 0x02, 0xff, 0, 0,     "YMF752-S" },
        !           451:        { 0x03, 0xff, 0, 0,     "YMF753-S" },
        !           452: };
        !           453:
        !           454: #define        cl(n)   n, sizeof(n)/sizeof(n[0])
        !           455: const struct ac97_vendorid {
        !           456:        u_int32_t id;
        !           457:        char * const name;
        !           458:        const struct ac97_codecid * const codecs;
        !           459:        u_int8_t num;
        !           460: } ac97_vendors[] = {
        !           461:        { 0x01408300, "Creative",               cl(ac97_cr) },
        !           462:        { 0x41445300, "Analog Devices",         cl(ac97_ad) },
        !           463:        { 0x414b4D00, "Asahi Kasei",            cl(ac97_ak) },
        !           464:        { 0x414c4300, "Realtek",                cl(ac97_rl) },
        !           465:        { 0x414c4700, "Avance Logic",           cl(ac97_av) },
        !           466:        { 0x434d4900, "C-Media Electronics",    cl(ac97_cm) },
        !           467:        { 0x43525900, "Cirrus Logic",           cl(ac97_cs) },
        !           468:        { 0x43585400, "Conexant",               cl(ac97_cx) },
        !           469:        { 0x44543000, "Diamond Technology",     cl(ac97_dt) },
        !           470:        { 0x454d4300, "eMicro",                 cl(ac97_em) },
        !           471:        { 0x45838300, "ESS Technology",         cl(ac97_es) },
        !           472:        { 0x48525300, "Intersil",               cl(ac97_is) },
        !           473:        { 0x49434500, "ICEnsemble",             cl(ac97_ic) },
        !           474:        { 0x49544500, "ITE, Inc.",              cl(ac97_it) },
        !           475:        { 0x4e534300, "National Semiconductor", cl(ac97_ns) },
        !           476:        { 0x50534300, "Philips Semiconductor",  cl(ac97_ps) },
        !           477:        { 0x53494c00, "Silicon Laboratory",     cl(ac97_sl) },
        !           478:        { 0x54524100, "TriTech Microelectronics", cl(ac97_tt) },
        !           479:        { 0x54584e00, "Texas Instruments",      cl(ac97_ti) },
        !           480:        { 0x56494100, "VIA Technologies",       cl(ac97_vi) },
        !           481:        { 0x57454300, "Winbond",                cl(ac97_wb) },
        !           482:        { 0x574d4c00, "Wolfson",                cl(ac97_wo) },
        !           483:        { 0x594d4800, "Yamaha",                 cl(ac97_ym) },
        !           484:        { 0x83847600, "SigmaTel",               cl(ac97_st) },
        !           485: };
        !           486: #undef cl
        !           487:
        !           488: const char * const ac97enhancement[] = {
        !           489:        "No 3D Stereo",
        !           490:        "Analog Devices Phat Stereo",
        !           491:        "Creative",
        !           492:        "National Semi 3D",
        !           493:        "Yamaha Ymersion",
        !           494:        "BBE 3D",
        !           495:        "Crystal Semi 3D",
        !           496:        "Qsound QXpander",
        !           497:        "Spatializer 3D",
        !           498:        "SRS 3D",
        !           499:        "Platform Tech 3D",
        !           500:        "AKM 3D",
        !           501:        "Aureal",
        !           502:        "AZTECH 3D",
        !           503:        "Binaura 3D",
        !           504:        "ESS Technology",
        !           505:        "Harman International VMAx",
        !           506:        "Nvidea 3D",
        !           507:        "Philips Incredible Sound",
        !           508:        "Texas Instruments 3D",
        !           509:        "VLSI Technology 3D",
        !           510:        "TriTech 3D",
        !           511:        "Realtek 3D",
        !           512:        "Samsung 3D",
        !           513:        "Wolfson Microelectronics 3D",
        !           514:        "Delta Integration 3D",
        !           515:        "SigmaTel 3D",
        !           516:        "KS Waves 3D",
        !           517:        "Rockwell 3D",
        !           518:        "Unknown 3D",
        !           519:        "Unknown 3D",
        !           520:        "Unknown 3D"
        !           521: };
        !           522:
        !           523: const char * const ac97feature[] = {
        !           524:        "mic channel",
        !           525:        "reserved",
        !           526:        "tone",
        !           527:        "simulated stereo",
        !           528:        "headphone",
        !           529:        "bass boost",
        !           530:        "18 bit DAC",
        !           531:        "20 bit DAC",
        !           532:        "18 bit ADC",
        !           533:        "20 bit ADC"
        !           534: };
        !           535:
        !           536:
        !           537: int    ac97_str_equal(const char *, const char *);
        !           538: void   ac97_setup_source_info(struct ac97_softc *);
        !           539: void   ac97_setup_defaults(struct ac97_softc *);
        !           540: int    ac97_read(struct ac97_softc *, u_int8_t, u_int16_t *);
        !           541: int    ac97_write(struct ac97_softc *, u_int8_t, u_int16_t);
        !           542:
        !           543:
        !           544: #ifdef AUDIO_DEBUG
        !           545: #define DPRINTF(x)     if (ac97debug) printf x
        !           546: #define DPRINTFN(n,x)  if (ac97debug>(n)) printf x
        !           547: #ifdef AC97_DEBUG
        !           548: int    ac97debug = 1;
        !           549: #else
        !           550: int    ac97debug = 0;
        !           551: #endif
        !           552: #else
        !           553: #define DPRINTF(x)
        !           554: #define DPRINTFN(n,x)
        !           555: #endif
        !           556:
        !           557: int
        !           558: ac97_read(struct ac97_softc *as, u_int8_t reg, u_int16_t *val)
        !           559: {
        !           560:        int error;
        !           561:
        !           562:        if (((as->host_flags & AC97_HOST_DONT_READ) &&
        !           563:            (reg != AC97_REG_VENDOR_ID1 && reg != AC97_REG_VENDOR_ID2 &&
        !           564:            reg != AC97_REG_RESET)) ||
        !           565:            (as->host_flags & AC97_HOST_DONT_READANY)) {
        !           566:                *val = as->shadow_reg[reg >> 1];
        !           567:                return (0);
        !           568:        }
        !           569:
        !           570:        if ((error = as->host_if->read(as->host_if->arg, reg, val)))
        !           571:                *val = as->shadow_reg[reg >> 1];
        !           572:        return (error);
        !           573: }
        !           574:
        !           575: int
        !           576: ac97_write(struct ac97_softc *as, u_int8_t reg, u_int16_t val)
        !           577: {
        !           578:        as->shadow_reg[reg >> 1] = val;
        !           579:        return (as->host_if->write(as->host_if->arg, reg, val));
        !           580: }
        !           581:
        !           582: void
        !           583: ac97_setup_defaults(struct ac97_softc *as)
        !           584: {
        !           585:        int idx;
        !           586:
        !           587:        bzero(as->shadow_reg, sizeof(as->shadow_reg));
        !           588:
        !           589:        for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
        !           590:                const struct ac97_source_info *si = &source_info[idx];
        !           591:
        !           592:                ac97_write(as, si->reg, si->default_value);
        !           593:        }
        !           594: }
        !           595:
        !           596: void
        !           597: ac97_restore_shadow(struct ac97_codec_if *self)
        !           598: {
        !           599:        struct ac97_softc *as = (struct ac97_softc *)self;
        !           600:        int idx;
        !           601:
        !           602:        for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
        !           603:                const struct ac97_source_info *si = &source_info[idx];
        !           604:
        !           605:                ac97_write(as, si->reg, as->shadow_reg[si->reg >> 1]);
        !           606:        }
        !           607: }
        !           608:
        !           609: int
        !           610: ac97_str_equal(const char *a, const char *b)
        !           611: {
        !           612:        return ((a == b) || (a && b && (!strcmp(a, b))));
        !           613: }
        !           614:
        !           615: void
        !           616: ac97_setup_source_info(struct ac97_softc *as)
        !           617: {
        !           618:        struct ac97_source_info *si, *si2;
        !           619:        int idx, ouridx;
        !           620:
        !           621:        for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
        !           622:                si = &as->source_info[ouridx];
        !           623:
        !           624:                bcopy(&source_info[idx], si, sizeof(*si));
        !           625:
        !           626:                switch (si->type) {
        !           627:                case AUDIO_MIXER_CLASS:
        !           628:                        si->mixer_class = ouridx;
        !           629:                        ouridx++;
        !           630:                        break;
        !           631:                case AUDIO_MIXER_VALUE:
        !           632:                        /* Todo - Test to see if it works */
        !           633:                        ouridx++;
        !           634:
        !           635:                        /* Add an entry for mute, if necessary */
        !           636:                        if (si->mute) {
        !           637:                                si = &as->source_info[ouridx];
        !           638:                                bcopy(&source_info[idx], si, sizeof(*si));
        !           639:                                si->qualifier = AudioNmute;
        !           640:                                si->type = AUDIO_MIXER_ENUM;
        !           641:                                si->info = &ac97_on_off;
        !           642:                                si->info_size = sizeof(ac97_on_off);
        !           643:                                si->bits = 1;
        !           644:                                si->ofs = 15;
        !           645:                                si->mute = 0;
        !           646:                                si->polarity = 0;
        !           647:                                ouridx++;
        !           648:                        }
        !           649:                        break;
        !           650:                case AUDIO_MIXER_ENUM:
        !           651:                        /* Todo - Test to see if it works */
        !           652:                        ouridx++;
        !           653:                        break;
        !           654:                default:
        !           655:                        printf ("ac97: shouldn't get here\n");
        !           656:                        break;
        !           657:                }
        !           658:        }
        !           659:
        !           660:        as->num_source_info = ouridx;
        !           661:
        !           662:        for (idx = 0; idx < as->num_source_info; idx++) {
        !           663:                int idx2, previdx;
        !           664:
        !           665:                si = &as->source_info[idx];
        !           666:
        !           667:                /* Find mixer class */
        !           668:                for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
        !           669:                        si2 = &as->source_info[idx2];
        !           670:
        !           671:                        if (si2->type == AUDIO_MIXER_CLASS &&
        !           672:                            ac97_str_equal(si->class, si2->class)) {
        !           673:                                si->mixer_class = idx2;
        !           674:                        }
        !           675:                }
        !           676:
        !           677:
        !           678:                /* Setup prev and next pointers */
        !           679:                if (si->prev != 0 || si->qualifier)
        !           680:                        continue;
        !           681:
        !           682:                si->prev = AUDIO_MIXER_LAST;
        !           683:                previdx = idx;
        !           684:
        !           685:                for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
        !           686:                        if (idx2 == idx)
        !           687:                                continue;
        !           688:
        !           689:                        si2 = &as->source_info[idx2];
        !           690:
        !           691:                        if (!si2->prev &&
        !           692:                            ac97_str_equal(si->class, si2->class) &&
        !           693:                            ac97_str_equal(si->device, si2->device)) {
        !           694:                                as->source_info[previdx].next = idx2;
        !           695:                                as->source_info[idx2].prev = previdx;
        !           696:
        !           697:                                previdx = idx2;
        !           698:                        }
        !           699:                }
        !           700:
        !           701:                as->source_info[previdx].next = AUDIO_MIXER_LAST;
        !           702:        }
        !           703: }
        !           704:
        !           705: int
        !           706: ac97_attach(struct ac97_host_if *host_if)
        !           707: {
        !           708:        struct ac97_softc *as;
        !           709:        u_int16_t id1, id2;
        !           710:        u_int32_t id;
        !           711:        mixer_ctrl_t ctl;
        !           712:        int error, i;
        !           713:        void (*initfunc)(struct ac97_softc *);
        !           714:
        !           715:        initfunc = NULL;
        !           716:
        !           717:        if (!(as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_NOWAIT)))
        !           718:                return (ENOMEM);
        !           719:
        !           720:        bzero(as, sizeof(*as));
        !           721:
        !           722:        as->codec_if.vtbl = &ac97civ;
        !           723:        as->host_if = host_if;
        !           724:
        !           725:        if ((error = host_if->attach(host_if->arg, &as->codec_if))) {
        !           726:                free(as, M_DEVBUF);
        !           727:                return (error);
        !           728:        }
        !           729:
        !           730:        host_if->reset(host_if->arg);
        !           731:        DELAY(1000);
        !           732:
        !           733:        host_if->write(host_if->arg, AC97_REG_POWER, 0);
        !           734:        host_if->write(host_if->arg, AC97_REG_RESET, 0);
        !           735:        DELAY(10000);
        !           736:
        !           737:        if (host_if->flags)
        !           738:                as->host_flags = host_if->flags(host_if->arg);
        !           739:
        !           740:        ac97_setup_defaults(as);
        !           741:        ac97_read(as, AC97_REG_VENDOR_ID1, &id1);
        !           742:        ac97_read(as, AC97_REG_VENDOR_ID2, &id2);
        !           743:        ac97_read(as, AC97_REG_RESET, &as->caps);
        !           744:
        !           745:        id = (id1 << 16) | id2;
        !           746:        if (id) {
        !           747:                register const struct ac97_vendorid *vendor;
        !           748:                register const struct ac97_codecid *codec;
        !           749:
        !           750:                printf("ac97: codec id 0x%08x", id);
        !           751:                for (vendor = &ac97_vendors[sizeof(ac97_vendors) /
        !           752:                     sizeof(ac97_vendors[0]) - 1];
        !           753:                     vendor >= ac97_vendors; vendor--) {
        !           754:                        if (vendor->id == (id & AC97_VENDOR_ID_MASK)) {
        !           755:                                printf(" (%s", vendor->name);
        !           756:                                for (codec = &vendor->codecs[vendor->num-1];
        !           757:                                     codec >= vendor->codecs; codec--) {
        !           758:                                        if (codec->id == (id & codec->mask))
        !           759:                                                break;
        !           760:                                }
        !           761:                                if (codec >= vendor->codecs && codec->mask) {
        !           762:                                        printf(" %s", codec->name);
        !           763:                                        initfunc = codec->init;
        !           764:                                } else
        !           765:                                        printf(" <%02x>", id & 0xff);
        !           766:                                if (codec >= vendor->codecs && codec->rev)
        !           767:                                        printf(" rev %d", id & codec->rev);
        !           768:                                printf(")");
        !           769:                                break;
        !           770:                        }
        !           771:                }
        !           772:                printf("\n");
        !           773:        } else
        !           774:                printf("ac97: codec id not read\n");
        !           775:
        !           776:        if (as->caps) {
        !           777:                printf("ac97: codec features ");
        !           778:                for (i = 0; i < 10; i++) {
        !           779:                        if (as->caps & (1 << i))
        !           780:                                printf("%s, ", ac97feature[i]);
        !           781:                }
        !           782:                printf("%s\n",
        !           783:                    ac97enhancement[AC97_CAPS_ENHANCEMENT(as->caps)]);
        !           784:        }
        !           785:
        !           786:        ac97_read(as, AC97_REG_EXT_AUDIO_ID, &as->ext_id);
        !           787:        if (as->ext_id)
        !           788:                DPRINTF(("ac97: ext id %b\n", as->ext_id,
        !           789:                    AC97_EXT_AUDIO_BITS));
        !           790:        if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_VRM)) {
        !           791:                ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &id1);
        !           792:                if (as->ext_id & AC97_EXT_AUDIO_VRA)
        !           793:                        id1 |= AC97_EXT_AUDIO_VRA;
        !           794:                if (as->ext_id & AC97_EXT_AUDIO_VRM)
        !           795:                        id1 |= AC97_EXT_AUDIO_VRM;
        !           796:                ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, id1);
        !           797:        }
        !           798:
        !           799:        ac97_setup_source_info(as);
        !           800:
        !           801:        /* use initfunc for specific device */
        !           802:        if (initfunc != NULL)
        !           803:                initfunc(as);
        !           804:
        !           805:        /* Just enable the DAC and master volumes by default */
        !           806:        bzero(&ctl, sizeof(ctl));
        !           807:
        !           808:        ctl.type = AUDIO_MIXER_ENUM;
        !           809:        ctl.un.ord = 0;  /* off */
        !           810:        ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
        !           811:            AudioNmaster, AudioNmute);
        !           812:        ac97_mixer_set_port(&as->codec_if, &ctl);
        !           813:
        !           814:        ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCinputs,
        !           815:            AudioNdac, AudioNmute);
        !           816:        ac97_mixer_set_port(&as->codec_if, &ctl);
        !           817:
        !           818:        ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
        !           819:            AudioNvolume, AudioNmute);
        !           820:        ac97_mixer_set_port(&as->codec_if, &ctl);
        !           821:
        !           822:        ctl.type = AUDIO_MIXER_ENUM;
        !           823:        ctl.un.ord = 0;
        !           824:        ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
        !           825:            AudioNsource, NULL);
        !           826:        ac97_mixer_set_port(&as->codec_if, &ctl);
        !           827:
        !           828:        return (0);
        !           829: }
        !           830:
        !           831: int
        !           832: ac97_query_devinfo(struct ac97_codec_if *codec_if, mixer_devinfo_t *dip)
        !           833: {
        !           834:        struct ac97_softc *as = (struct ac97_softc *)codec_if;
        !           835:
        !           836:        if (dip->index < as->num_source_info) {
        !           837:                struct ac97_source_info *si = &as->source_info[dip->index];
        !           838:                const char *name;
        !           839:
        !           840:                dip->type = si->type;
        !           841:                dip->mixer_class = si->mixer_class;
        !           842:                dip->prev = si->prev;
        !           843:                dip->next = si->next;
        !           844:
        !           845:                if (si->qualifier)
        !           846:                        name = si->qualifier;
        !           847:                else if (si->device)
        !           848:                        name = si->device;
        !           849:                else if (si->class)
        !           850:                        name = si->class;
        !           851:
        !           852:                if (name)
        !           853:                        strlcpy(dip->label.name, name, sizeof dip->label.name);
        !           854:
        !           855:                bcopy(si->info, &dip->un, si->info_size);
        !           856:
        !           857:                /* Set the delta for volume sources */
        !           858:                if (dip->type == AUDIO_MIXER_VALUE)
        !           859:                        dip->un.v.delta = 1 << (8 - si->bits);
        !           860:
        !           861:                return (0);
        !           862:        }
        !           863:
        !           864:        return (ENXIO);
        !           865: }
        !           866:
        !           867: int
        !           868: ac97_mixer_set_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
        !           869: {
        !           870:        struct ac97_softc *as = (struct ac97_softc *)codec_if;
        !           871:        struct ac97_source_info *si = &as->source_info[cp->dev];
        !           872:        u_int16_t mask;
        !           873:        u_int16_t val, newval;
        !           874:        int error;
        !           875:
        !           876:        if (cp->dev < 0 || cp->dev >= as->num_source_info ||
        !           877:            cp->type != si->type)
        !           878:                return (EINVAL);
        !           879:
        !           880:        ac97_read(as, si->reg, &val);
        !           881:
        !           882:        DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
        !           883:
        !           884:        mask = (1 << si->bits) - 1;
        !           885:
        !           886:        switch (cp->type) {
        !           887:        case AUDIO_MIXER_ENUM:
        !           888:                if (cp->un.ord > mask || cp->un.ord < 0)
        !           889:                        return (EINVAL);
        !           890:
        !           891:                newval = (cp->un.ord << si->ofs);
        !           892:                if (si->reg == AC97_REG_RECORD_SELECT) {
        !           893:                        newval |= (newval << (8 + si->ofs));
        !           894:                        mask |= (mask << 8);
        !           895:                }
        !           896:
        !           897:                if (si->mute) {
        !           898:                        newval |= newval << 8;
        !           899:                        mask |= mask << 8;
        !           900:                }
        !           901:
        !           902:                break;
        !           903:        case AUDIO_MIXER_VALUE:
        !           904:        {
        !           905:                const struct audio_mixer_value *value = si->info;
        !           906:                u_int16_t  l, r;
        !           907:
        !           908:                if (cp->un.value.num_channels <= 0 ||
        !           909:                    cp->un.value.num_channels > value->num_channels)
        !           910:                        return (EINVAL);
        !           911:
        !           912:                if (cp->un.value.num_channels == 1) {
        !           913:                        l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
        !           914:                } else {
        !           915:                        if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
        !           916:                                l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
        !           917:                                r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
        !           918:                        } else {
        !           919:                                r = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
        !           920:                                l = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
        !           921:                        }
        !           922:                }
        !           923:
        !           924:                if (!si->polarity) {
        !           925:                        l = 255 - l;
        !           926:                        r = 255 - r;
        !           927:                }
        !           928:
        !           929:                l >>= 8 - si->bits;
        !           930:                r >>= 8 - si->bits;
        !           931:
        !           932:                newval = ((l & mask) << si->ofs);
        !           933:                if (value->num_channels == 2) {
        !           934:                        newval |= ((r & mask) << (si->ofs + 8));
        !           935:                        mask |= (mask << 8);
        !           936:                }
        !           937:
        !           938:                break;
        !           939:        }
        !           940:        default:
        !           941:                return (EINVAL);
        !           942:        }
        !           943:
        !           944:        mask = mask << si->ofs;
        !           945:        error = ac97_write(as, si->reg, (val & ~mask) | newval);
        !           946:        if (error)
        !           947:                return (error);
        !           948:
        !           949:        return (0);
        !           950: }
        !           951:
        !           952: int
        !           953: ac97_get_portnum_by_name(struct ac97_codec_if *codec_if, char *class,
        !           954:     char *device, char *qualifier)
        !           955: {
        !           956:        struct ac97_softc *as = (struct ac97_softc *)codec_if;
        !           957:        int idx;
        !           958:
        !           959:        for (idx = 0; idx < as->num_source_info; idx++) {
        !           960:                struct ac97_source_info *si = &as->source_info[idx];
        !           961:                if (ac97_str_equal(class, si->class) &&
        !           962:                    ac97_str_equal(device, si->device) &&
        !           963:                    ac97_str_equal(qualifier, si->qualifier))
        !           964:                        return (idx);
        !           965:        }
        !           966:
        !           967:        return (-1);
        !           968: }
        !           969:
        !           970: int
        !           971: ac97_mixer_get_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
        !           972: {
        !           973:        struct ac97_softc *as = (struct ac97_softc *)codec_if;
        !           974:        struct ac97_source_info *si = &as->source_info[cp->dev];
        !           975:        u_int16_t mask;
        !           976:        u_int16_t val;
        !           977:
        !           978:        if (cp->dev < 0 || cp->dev >= as->num_source_info ||
        !           979:            cp->type != si->type)
        !           980:                return (EINVAL);
        !           981:
        !           982:        ac97_read(as, si->reg, &val);
        !           983:
        !           984:        DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
        !           985:
        !           986:        mask = (1 << si->bits) - 1;
        !           987:
        !           988:        switch (cp->type) {
        !           989:        case AUDIO_MIXER_ENUM:
        !           990:                cp->un.ord = (val >> si->ofs) & mask;
        !           991:                DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs,
        !           992:                    mask, cp->un.ord));
        !           993:                break;
        !           994:        case AUDIO_MIXER_VALUE:
        !           995:        {
        !           996:                const struct audio_mixer_value *value = si->info;
        !           997:                u_int16_t  l, r;
        !           998:
        !           999:                if ((cp->un.value.num_channels <= 0) ||
        !          1000:                    (cp->un.value.num_channels > value->num_channels))
        !          1001:                        return (EINVAL);
        !          1002:
        !          1003:                if (value->num_channels == 1)
        !          1004:                        l = r = (val >> si->ofs) & mask;
        !          1005:                else {
        !          1006:                        if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
        !          1007:                                l = (val >> si->ofs) & mask;
        !          1008:                                r = (val >> (si->ofs + 8)) & mask;
        !          1009:                        } else {
        !          1010:                                r = (val >> si->ofs) & mask;
        !          1011:                                l = (val >> (si->ofs + 8)) & mask;
        !          1012:                        }
        !          1013:                }
        !          1014:
        !          1015:                l <<= 8 - si->bits;
        !          1016:                r <<= 8 - si->bits;
        !          1017:                if (!si->polarity) {
        !          1018:                        l = 255 - l;
        !          1019:                        r = 255 - r;
        !          1020:                }
        !          1021:
        !          1022:                /*
        !          1023:                 * The EAP driver averages l and r for stereo
        !          1024:                 * channels that are requested in MONO mode. Does this
        !          1025:                 * make sense?
        !          1026:                 */
        !          1027:                if (cp->un.value.num_channels == 1) {
        !          1028:                        cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
        !          1029:                } else if (cp->un.value.num_channels == 2) {
        !          1030:                        cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
        !          1031:                        cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
        !          1032:                }
        !          1033:
        !          1034:                break;
        !          1035:        }
        !          1036:        default:
        !          1037:                return (EINVAL);
        !          1038:        }
        !          1039:
        !          1040:        return (0);
        !          1041: }
        !          1042:
        !          1043: int
        !          1044: ac97_set_rate(struct ac97_codec_if *codec_if, struct audio_params *p,
        !          1045:     int mode)
        !          1046: {
        !          1047:        struct ac97_softc *as = (struct ac97_softc *)codec_if;
        !          1048:        u_int16_t reg, val, regval, id = 0;
        !          1049:
        !          1050:        DPRINTFN(5, ("set_rate(%lu) ", p->sample_rate));
        !          1051:
        !          1052:        if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
        !          1053:                p->sample_rate = AC97_SINGLERATE;
        !          1054:                return (0);
        !          1055:        }
        !          1056:
        !          1057:        if (p->sample_rate > 0xffff) {
        !          1058:                if (mode != AUMODE_PLAY)
        !          1059:                        return (EINVAL);
        !          1060:                if (!(as->ext_id & AC97_EXT_AUDIO_DRA))
        !          1061:                        return (EINVAL);
        !          1062:                if (ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &id))
        !          1063:                        return (EIO);
        !          1064:                id |= AC97_EXT_AUDIO_DRA;
        !          1065:                if (ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, id))
        !          1066:                        return (EIO);
        !          1067:                p->sample_rate /= 2;
        !          1068:        }
        !          1069:
        !          1070:        /* i guess it's better w/o clicks and squeecks when changing the rate */
        !          1071:        if (ac97_read(as, AC97_REG_POWER, &val) ||
        !          1072:            ac97_write(as, AC97_REG_POWER, val |
        !          1073:              (mode == AUMODE_PLAY? AC97_POWER_OUT : AC97_POWER_IN)))
        !          1074:                return (EIO);
        !          1075:
        !          1076:        reg = mode == AUMODE_PLAY ?
        !          1077:            AC97_REG_FRONT_DAC_RATE : AC97_REG_PCM_ADC_RATE;
        !          1078:
        !          1079:        if (ac97_write(as, reg, (u_int16_t) p->sample_rate) ||
        !          1080:            ac97_read(as, reg, &regval))
        !          1081:                return (EIO);
        !          1082:        p->sample_rate = regval;
        !          1083:        if (id & AC97_EXT_AUDIO_DRA)
        !          1084:                p->sample_rate *= 2;
        !          1085:
        !          1086:        DPRINTFN(5, (" %lu\n", regval));
        !          1087:
        !          1088:        if (ac97_write(as, AC97_REG_POWER, val))
        !          1089:                return (EIO);
        !          1090:
        !          1091:        return (0);
        !          1092: }
        !          1093:
        !          1094: /*
        !          1095:  * Codec-dependent initialization
        !          1096:  */
        !          1097:
        !          1098: #define AC97_AD1886_JACK_SENSE 0x72
        !          1099:
        !          1100: void
        !          1101: ac97_ad1886_init(struct ac97_softc *as)
        !          1102: {
        !          1103:        ac97_write(as, AC97_AD1886_JACK_SENSE, 0x0010);
        !          1104: }
        !          1105:
        !          1106: void
        !          1107: ac97_ad198x_init(struct ac97_softc *as)
        !          1108: {
        !          1109:        int i;
        !          1110:        u_int16_t misc;
        !          1111:
        !          1112:        ac97_read(as, AC97_AD_REG_MISC, &misc);
        !          1113:        ac97_write(as, AC97_AD_REG_MISC,
        !          1114:            misc|AC97_AD_MISC_HPSEL|AC97_AD_MISC_LOSEL);
        !          1115:
        !          1116:        for (i = 0; i < as->num_source_info; i++) {
        !          1117:                if (as->source_info[i].reg == AC97_REG_SURROUND_VOLUME)
        !          1118:                        as->source_info[i].reg = AC97_REG_MASTER_VOLUME;
        !          1119:                else if (as->source_info[i].reg == AC97_REG_MASTER_VOLUME) {
        !          1120:                        as->source_info[i].reg = AC97_REG_SURROUND_VOLUME;
        !          1121:                        if (as->source_info[i].type == AUDIO_MIXER_ENUM) {
        !          1122:                                as->source_info[i].mute = 1;
        !          1123:                                as->source_info[i].ofs = 7;
        !          1124:                        }
        !          1125:                }
        !          1126:        }
        !          1127: }
        !          1128:
        !          1129: void
        !          1130: ac97_alc655_init(struct ac97_softc *as)
        !          1131: {
        !          1132:        u_int16_t misc;
        !          1133:
        !          1134:        ac97_read(as, AC97_AV_REG_MISC, &misc);
        !          1135:        if (as->host_flags & AC97_HOST_DONT_ENABLE_SPDIF) {
        !          1136:                misc &= ~AC97_AV_MISC_SPDIFEN;
        !          1137:        } else  {
        !          1138:                misc |= AC97_AV_MISC_SPDIFEN;
        !          1139:        }
        !          1140:        misc &= ~AC97_AV_MISC_VREFDIS;
        !          1141:        ac97_write(as, AC97_AV_REG_MISC, misc);
        !          1142:
        !          1143:        ac97_write(as, AC97_AV_REG_MULTICH, AC97_AV_MULTICH_MAGIC);
        !          1144: }
        !          1145:
        !          1146: void
        !          1147: ac97_cx20468_init(struct ac97_softc *as)
        !          1148: {
        !          1149:        u_int16_t misc;
        !          1150:
        !          1151:        ac97_read(as, AC97_CX_REG_MISC, &misc);
        !          1152:        ac97_write(as, AC97_CX_REG_MISC, misc &
        !          1153:            ~(AC97_CX_SPDIFEN | AC97_CX_COPYRIGHT | AC97_CX_MASK));
        !          1154: }

CVSweb