Index: src/sys/dev/sound/pcm/ac97.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/ac97.c,v retrieving revision 1.53.2.3 diff -u -r1.53.2.3 ac97.c --- src/sys/dev/sound/pcm/ac97.c 9 Jan 2006 02:06:42 -0000 1.53.2.3 +++ src/sys/dev/sound/pcm/ac97.c 27 Sep 2006 16:34:16 -0000 @@ -136,7 +136,7 @@ { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, { 0x41445372, 0x00, 0, "AD1981A", 0 }, - { 0x41445374, 0x00, 0, "AD1981B", 0 }, + { 0x41445374, 0x00, 0, "AD1981B", ad1981b_patch }, { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, { 0x41445378, 0x00, 0, "AD1986", ad198x_patch }, { 0x414b4d00, 0x00, 1, "AK4540", 0 }, @@ -545,40 +545,6 @@ } } -static void -ac97_fix_volume(struct ac97_info *codec) -{ - struct snddev_info *d = device_get_softc(codec->dev); - -#if 0 - /* XXX For the sake of debugging purposes */ - ac97_wrcd(codec, AC97_MIX_PCM, 0); - bzero(&codec->mix[SOUND_MIXER_PCM], - sizeof(codec->mix[SOUND_MIXER_PCM])); - codec->flags |= AC97_F_SOFTVOL; - if (d) - d->flags |= SD_F_SOFTVOL; - return; -#endif - switch (codec->id) { - case 0x434d4941: /* CMI9738 */ - case 0x434d4961: /* CMI9739 */ - case 0x434d4978: /* CMI9761 */ - case 0x434d4982: /* CMI9761 */ - case 0x434d4983: /* CMI9761 */ - ac97_wrcd(codec, AC97_MIX_PCM, 0); - break; - default: - return; - break; - } - bzero(&codec->mix[SOUND_MIXER_PCM], - sizeof(codec->mix[SOUND_MIXER_PCM])); - codec->flags |= AC97_F_SOFTVOL; - if (d) - d->flags |= SD_F_SOFTVOL; -} - static const char* ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) { @@ -684,7 +650,6 @@ } ac97_fix_auxout(codec); ac97_fix_tone(codec); - ac97_fix_volume(codec); if (codec_patch) codec_patch(codec); @@ -758,8 +723,6 @@ if (bootverbose) { if (codec->flags & AC97_F_RDCD_BUG) device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n"); - if (codec->flags & AC97_F_SOFTVOL) - device_printf(codec->dev, "Soft PCM volume\n"); device_printf(codec->dev, "Codec features "); for (i = j = 0; i < 10; i++) if (codec->caps & (1 << i)) @@ -827,15 +790,16 @@ struct ac97_info * ac97_create(device_t dev, void *devinfo, kobj_class_t cls) { - struct ac97_info *codec; + struct ac97_info *codec; + int eapd_inv; - codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); + codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT | M_ZERO); if (codec == NULL) return NULL; snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); - codec->methods = kobj_create(cls, M_AC97, M_WAITOK); + codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO); if (codec->methods == NULL) { snd_mtxlock(codec->lock); snd_mtxfree(codec->lock); @@ -846,6 +810,11 @@ codec->dev = dev; codec->devinfo = devinfo; codec->flags = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "ac97_eapd_inv", &eapd_inv) == 0) { + if (eapd_inv != 0) + codec->flags |= AC97_F_EAPD_INV; + } return codec; } @@ -877,6 +846,7 @@ ac97mix_init(struct snd_mixer *m) { struct ac97_info *codec = mix_getdevinfo(m); + struct snddev_info *d; u_int32_t i, mask; if (codec == NULL) @@ -890,6 +860,46 @@ mask |= codec->mix[i].enable? 1 << i : 0; mix_setdevs(m, mask); + switch (codec->id) { + case 0x41445374: /* AD1981B */ + mask = 0; + if (codec->mix[SOUND_MIXER_OGAIN].enable) + mask |= SOUND_MASK_OGAIN; + if (codec->mix[SOUND_MIXER_PHONEOUT].enable) + mask |= SOUND_MASK_PHONEOUT; + /* Tie ogain/phone to master volume */ + if (codec->mix[SOUND_MIXER_VOLUME].enable) + mix_setparentchild(m, SOUND_MIXER_VOLUME, mask); + else { + mix_setparentchild(m, SOUND_MIXER_VOLUME, mask); + mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); + } + break; + case 0x434d4941: /* CMI9738 */ + case 0x434d4961: /* CMI9739 */ + case 0x434d4978: /* CMI9761 */ + case 0x434d4982: /* CMI9761 */ + case 0x434d4983: /* CMI9761 */ + ac97_wrcd(codec, AC97_MIX_PCM, 0); + bzero(&codec->mix[SOUND_MIXER_PCM], + sizeof(codec->mix[SOUND_MIXER_PCM])); + d = device_get_softc(codec->dev); + if (d != NULL) + d->flags |= SD_F_SOFTPCMVOL; + /* XXX How about master volume ? */ + break; + default: + break; + } + +#if 0 + /* XXX For the sake of debugging purposes */ + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM | SOUND_MASK_CD); + mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); + ac97_wrcd(codec, AC97_MIX_MASTER, 0); +#endif + mask = 0; for (i = 0; i < 32; i++) mask |= codec->mix[i].recidx? 1 << i : 0; Index: src/sys/dev/sound/pcm/ac97_patch.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/ac97_patch.c,v retrieving revision 1.3.2.1 diff -u -r1.3.2.1 ac97_patch.c --- src/sys/dev/sound/pcm/ac97_patch.c 30 Dec 2005 19:55:54 -0000 1.3.2.1 +++ src/sys/dev/sound/pcm/ac97_patch.c 27 Sep 2006 16:34:16 -0000 @@ -46,6 +46,12 @@ ac97_wrcd(codec, 0x76, ac97_rdcd(codec, 0x76) | 0x0420); } +void ad1981b_patch(struct ac97_info* codec) +{ + ac97_wrcd(codec, AC97_AD_JACK_SPDIF, + ac97_rdcd(codec, AC97_AD_JACK_SPDIF) | 0x0800); +} + void cmi9739_patch(struct ac97_info* codec) { /* Index: src/sys/dev/sound/pcm/ac97_patch.h =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/ac97_patch.h,v retrieving revision 1.3.2.1 diff -u -r1.3.2.1 ac97_patch.h --- src/sys/dev/sound/pcm/ac97_patch.h 30 Dec 2005 19:55:54 -0000 1.3.2.1 +++ src/sys/dev/sound/pcm/ac97_patch.h 27 Sep 2006 16:34:16 -0000 @@ -29,4 +29,5 @@ void ad1886_patch(struct ac97_info*); void ad198x_patch(struct ac97_info*); +void ad1981b_patch(struct ac97_info*); void cmi9739_patch(struct ac97_info*); Index: src/sys/dev/sound/pcm/channel.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/channel.c,v retrieving revision 1.99.2.4 diff -u -r1.99.2.4 channel.c --- src/sys/dev/sound/pcm/channel.c 4 Apr 2006 17:37:51 -0000 1.99.2.4 +++ src/sys/dev/sound/pcm/channel.c 27 Sep 2006 16:34:16 -0000 @@ -1346,7 +1346,7 @@ c->feederflags &= ~(1 << FEEDER_VOLUME); if (c->direction == PCMDIR_PLAY && !(c->flags & CHN_F_VIRTUAL) && - c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTVOL) && + c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTPCMVOL) && c->parentsnddev->mixer_dev) c->feederflags |= 1 << FEEDER_VOLUME; flags = c->feederflags; @@ -1404,7 +1404,10 @@ sndbuf_setfmt(c->bufhard, hwfmt); if ((flags & (1 << FEEDER_VOLUME))) { - int vol = 100 | (100 << 8); + uint32_t parent = SOUND_MIXER_NONE; + int vol, left, right; + + vol = 100 | (100 << 8); CHN_UNLOCK(c); /* @@ -1414,9 +1417,26 @@ */ if (mixer_ioctl(c->parentsnddev->mixer_dev, MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&vol, -1, NULL) != 0) - device_printf(c->dev, "Soft Volume: Failed to read default value\n"); + device_printf(c->dev, "Soft PCM Volume: Failed to read default value\n"); + left = vol & 0x7f; + right = (vol >> 8) & 0x7f; + if (c->parentsnddev != NULL && + c->parentsnddev->mixer_dev != NULL && + c->parentsnddev->mixer_dev->si_drv1 != NULL) + parent = mix_getparent( + c->parentsnddev->mixer_dev->si_drv1, + SOUND_MIXER_PCM); + if (parent != SOUND_MIXER_NONE) { + vol = 100 | (100 << 8); + if (mixer_ioctl(c->parentsnddev->mixer_dev, + MIXER_READ(parent), + (caddr_t)&vol, -1, NULL) != 0) + device_printf(c->dev, "Soft Volume: Failed to read parent default value\n"); + left = (left * (vol & 0x7f)) / 100; + right = (right * ((vol >> 8) & 0x7f)) / 100; + } CHN_LOCK(c); - chn_setvolume(c, vol & 0x7f, (vol >> 8) & 0x7f); + chn_setvolume(c, left, right); } return 0; Index: src/sys/dev/sound/pcm/mixer.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/mixer.c,v retrieving revision 1.43.2.4 diff -u -r1.43.2.4 mixer.c --- src/sys/dev/sound/pcm/mixer.c 4 Apr 2006 17:43:48 -0000 1.43.2.4 +++ src/sys/dev/sound/pcm/mixer.c 27 Sep 2006 16:34:16 -0000 @@ -47,6 +47,9 @@ u_int32_t recdevs; u_int32_t recsrc; u_int16_t level[32]; + u_int32_t parent[32]; + u_int32_t child[32]; + u_int32_t realdev[32]; char name[MIXER_NAMELEN]; struct mtx *lock; }; @@ -112,48 +115,94 @@ #endif static int -mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev) +mixer_set_softpcmvol(struct snd_mixer *mixer, struct snddev_info *d, + unsigned left, unsigned right) +{ + struct snddev_channel *sce; + struct pcm_channel *ch; +#ifdef USING_MUTEX + int locked = (mixer->lock && mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0; + + if (locked) + snd_mtxunlock(mixer->lock); +#endif + SLIST_FOREACH(sce, &d->channels, link) { + ch = sce->channel; + CHN_LOCK(ch); + if (ch->direction == PCMDIR_PLAY && + (ch->feederflags & (1 << FEEDER_VOLUME))) + chn_setvolume(ch, left, right); + CHN_UNLOCK(ch); + } +#ifdef USING_MUTEX + if (locked) + snd_mtxlock(mixer->lock); +#endif + return 0; +} + +static int +mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev) { struct snddev_info *d; - unsigned l, r; - int v; + unsigned l, r, tl, tr; + u_int32_t parent = SOUND_MIXER_NONE, child = 0; + u_int32_t realdev; + int i; - if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev)))) + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || + (0 == (m->devs & (1 << dev)))) return -1; l = min((lev & 0x00ff), 100); r = min(((lev & 0xff00) >> 8), 100); + realdev = m->realdev[dev]; - d = device_get_softc(mixer->dev); - if (dev == SOUND_MIXER_PCM && d && - (d->flags & SD_F_SOFTVOL)) { - struct snddev_channel *sce; - struct pcm_channel *ch; -#ifdef USING_MUTEX - int locked = (mixer->lock && mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0; + d = device_get_softc(m->dev); + if (d == NULL) + return -1; - if (locked) - snd_mtxunlock(mixer->lock); -#endif - SLIST_FOREACH(sce, &d->channels, link) { - ch = sce->channel; - CHN_LOCK(ch); - if (ch->direction == PCMDIR_PLAY && - (ch->feederflags & (1 << FEEDER_VOLUME))) - chn_setvolume(ch, l, r); - CHN_UNLOCK(ch); + /* TODO: recursive handling */ + parent = m->parent[dev]; + if (parent >= SOUND_MIXER_NRDEVICES) + parent = SOUND_MIXER_NONE; + if (parent == SOUND_MIXER_NONE) + child = m->child[dev]; + + if (parent != SOUND_MIXER_NONE) { + tl = (l * (m->level[parent] & 0x00ff)) / 100; + tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100; + if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + mixer_set_softpcmvol(m, d, tl, tr); + else if (realdev != SOUND_MIXER_NONE && + MIXER_SET(m, realdev, tl, tr) < 0) + return -1; + } else if (child != 0) { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(child & (1 << i)) || m->parent[i] != dev) + continue; + realdev = m->realdev[i]; + tl = (l * (m->level[i] & 0x00ff)) / 100; + tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; + if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + mixer_set_softpcmvol(m, d, tl, tr); + else if (realdev != SOUND_MIXER_NONE) + MIXER_SET(m, realdev, tl, tr); } -#ifdef USING_MUTEX - if (locked) - snd_mtxlock(mixer->lock); -#endif + realdev = m->realdev[dev]; + if (realdev != SOUND_MIXER_NONE && + MIXER_SET(m, realdev, l, r) < 0) + return -1; } else { - v = MIXER_SET(mixer, dev, l, r); - if (v < 0) + if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + mixer_set_softpcmvol(m, d, l, r); + else if (realdev != SOUND_MIXER_NONE && + MIXER_SET(m, realdev, l, r) < 0) return -1; } - mixer->level[dev] = l | (r << 8); + m->level[dev] = l | (r << 8); + return 0; } @@ -185,8 +234,17 @@ mix_setdevs(struct snd_mixer *m, u_int32_t v) { struct snddev_info *d = device_get_softc(m->dev); - if (d && (d->flags & SD_F_SOFTVOL)) + int i; + + if (m == NULL) + return; + if (d != NULL && (d->flags & SD_F_SOFTPCMVOL)) v |= SOUND_MASK_PCM; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (m->parent[i] < SOUND_MIXER_NRDEVICES) + v |= 1 << m->parent[i]; + v |= m->child[i]; + } m->devs = v; } @@ -196,6 +254,54 @@ m->recdevs = v; } +void +mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs) +{ + u_int32_t mask = 0; + int i; + + if (m == NULL || parent >= SOUND_MIXER_NRDEVICES) + return; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (i == parent) + continue; + if (childs & (1 << i)) { + mask |= 1 << i; + if (m->parent[i] < SOUND_MIXER_NRDEVICES) + m->child[m->parent[i]] &= ~(1 << i); + m->parent[i] = parent; + m->child[i] = 0; + } + } + mask &= ~(1 << parent); + m->child[parent] = mask; +} + +void +mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev) +{ + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || + !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES)) + return; + m->realdev[dev] = realdev; +} + +u_int32_t +mix_getparent(struct snd_mixer *m, u_int32_t dev) +{ + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) + return SOUND_MIXER_NONE; + return m->parent[dev]; +} + +u_int32_t +mix_getchild(struct snd_mixer *m, u_int32_t dev) +{ + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) + return 0; + return m->child[dev]; +} + u_int32_t mix_getdevs(struct snd_mixer *m) { @@ -230,6 +336,11 @@ m->devinfo = devinfo; m->busy = 0; m->dev = dev; + for (i = 0; i < 32; i++) { + m->parent[i] = SOUND_MIXER_NONE; + m->child[i] = 0; + m->realdev[i] = i; + } if (MIXER_INIT(m)) goto bad; @@ -256,6 +367,30 @@ snddev = device_get_softc(dev); snddev->mixer_dev = pdev; + if (bootverbose) { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(m->devs & (1 << i))) + continue; + if (m->realdev[i] != i) { + device_printf(dev, "Mixer \"%s\" -> \"%s\":", + snd_mixernames[i], + (m->realdev[i] < SOUND_MIXER_NRDEVICES) ? + snd_mixernames[m->realdev[i]] : "none"); + } else { + device_printf(dev, "Mixer \"%s\":", + snd_mixernames[i]); + } + if (m->parent[i] < SOUND_MIXER_NRDEVICES) + printf(" parent=\"%s\"", + snd_mixernames[m->parent[i]]); + if (m->child[i] != 0) + printf(" child=0x%08x", m->child[i]); + printf("\n"); + } + if (snddev->flags & SD_F_SOFTPCMVOL) + device_printf(dev, "Soft PCM mixer ENABLED\n"); + } + return 0; bad: Index: src/sys/dev/sound/pcm/mixer.h =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/mixer.h,v retrieving revision 1.14 diff -u -r1.14 mixer.h --- src/sys/dev/sound/pcm/mixer.h 6 Jan 2005 01:43:21 -0000 1.14 +++ src/sys/dev/sound/pcm/mixer.h 27 Sep 2006 16:34:16 -0000 @@ -39,6 +39,10 @@ void mix_setrecdevs(struct snd_mixer *m, u_int32_t v); u_int32_t mix_getdevs(struct snd_mixer *m); u_int32_t mix_getrecdevs(struct snd_mixer *m); +void mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs); +void mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev); +u_int32_t mix_getparent(struct snd_mixer *m, u_int32_t dev); +u_int32_t mix_getchild(struct snd_mixer *m, u_int32_t dev); void *mix_getdevinfo(struct snd_mixer *m); /* Index: src/sys/dev/sound/pcm/sound.h =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/sound.h,v retrieving revision 1.63.2.2 diff -u -r1.63.2.2 sound.h --- src/sys/dev/sound/pcm/sound.h 4 Apr 2006 17:43:48 -0000 1.63.2.2 +++ src/sys/dev/sound/pcm/sound.h 27 Sep 2006 16:34:16 -0000 @@ -135,7 +135,7 @@ #define SD_F_SIMPLEX 0x00000001 #define SD_F_AUTOVCHAN 0x00000002 -#define SD_F_SOFTVOL 0x00000004 +#define SD_F_SOFTPCMVOL 0x00000004 #define SD_F_PRIO_RD 0x10000000 #define SD_F_PRIO_WR 0x20000000 #define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR) Index: src/sys/dev/sound/usb/uaudio_pcm.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/usb/uaudio_pcm.c,v retrieving revision 1.15.2.1 diff -u -r1.15.2.1 uaudio_pcm.c --- src/sys/dev/sound/usb/uaudio_pcm.c 30 Dec 2005 19:55:54 -0000 1.15.2.1 +++ src/sys/dev/sound/usb/uaudio_pcm.c 27 Sep 2006 16:34:16 -0000 @@ -243,12 +243,20 @@ d = device_get_softc(ua->sc_dev); mask = uaudio_query_mix_info(pa_dev); - if (d && !(mask & SOUND_MIXER_PCM)) { - /* - * Emulate missing pcm mixer controller - * through FEEDER_VOLUME - */ - d->flags |= SD_F_SOFTVOL; + if (d != NULL) { + if (!(mask & SOUND_MASK_PCM)) { + /* + * Emulate missing pcm mixer controller + * through FEEDER_VOLUME + */ + d->flags |= SD_F_SOFTPCMVOL; + } + if (!(mask & SOUND_MASK_VOLUME)) { + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM); + mix_setrealdev(m, SOUND_MIXER_VOLUME, + SOUND_MIXER_NONE); + } } mix_setdevs(m, mask);