Various fixups, especially for the upcomming High Definition Audio

commit.

1) sys/dev/sound/pcm/sound.h
   sys/dev/sound/pcm/channel.c
   * Be more specific: SD_F_SOFTVOL -> SD_F_SOFTPCMVOL
2) sys/dev/sound/pcm/mixer.[ch]
   * Implement
       mix_setparentchild()
       mix_setrealdev()
       mix_getparent()
       mix_getchild()
     The purpose of these functions is implement relative volume
     adjustment, such as to tie two or more mixer device into a
     single logical device. Usefull for the upcoming HDA driver
     and few AC97 codec (such as AD1981B) where the master volume
     "vol" need to be implemented using this logical manner.
3) sys/dev/sound/pcm/ac97_patch.[ch]
   * Patch for AD1981B codec to enable (automuting) headphone jack sense.
4) sys/dev/sound/pcm/ac97.c
   * Implement proper logical master volume for AD9181B codec
     through various mix_set{parentchild,realdev}(). Tie both
     "ogain" (headphone volume) and "phone" (speaker/lineout) to
     a logical "vol".
5) sys/dev/sound/pcm/usb/uaudio_pcm.c
   * ditto, for "vol" -> { "pcm" }.

MFC after:	1 month
This commit is contained in:
Ariff Abdullah 2006-09-28 17:29:00 +00:00
parent add92b34d1
commit 7699548f1b
8 changed files with 264 additions and 80 deletions

View File

@ -136,7 +136,7 @@ static struct ac97_codecid ac97codecid[] = {
{ 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 @@ ac97_fix_tone(struct ac97_info *codec)
}
}
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_initmixer(struct ac97_info *codec)
}
ac97_fix_auxout(codec);
ac97_fix_tone(codec);
ac97_fix_volume(codec);
if (codec_patch)
codec_patch(codec);
@ -758,8 +723,6 @@ ac97_initmixer(struct ac97_info *codec)
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))
@ -828,14 +791,15 @@ struct ac97_info *
ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
{
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 @@ ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
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 @@ static int
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)
@ -885,6 +855,46 @@ ac97mix_init(struct snd_mixer *m)
if (ac97_initmixer(codec))
return -1;
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].enable? 1 << i : 0;

View File

@ -46,6 +46,12 @@ void ad198x_patch(struct ac97_info* codec)
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)
{
/*

View File

@ -29,4 +29,5 @@ typedef void (*ac97_patch)(struct ac97_info*);
void ad1886_patch(struct ac97_info*);
void ad198x_patch(struct ac97_info*);
void ad1981b_patch(struct ac97_info*);
void cmi9739_patch(struct ac97_info*);

View File

@ -1417,7 +1417,7 @@ chn_buildfeeder(struct pcm_channel *c)
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;
@ -1475,7 +1475,10 @@ chn_buildfeeder(struct pcm_channel *c)
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);
/*
@ -1485,9 +1488,26 @@ chn_buildfeeder(struct pcm_channel *c)
*/
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;

View File

@ -47,6 +47,9 @@ struct snd_mixer {
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;
oss_mixer_enuminfo enuminfo;
@ -124,48 +127,94 @@ mixer_lookup(char *devname)
#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;
}
@ -274,8 +323,17 @@ void
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;
}
@ -347,6 +405,54 @@ mix_setrecdevs(struct snd_mixer *m, u_int32_t v)
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)
{
@ -381,6 +487,11 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
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;
@ -409,6 +520,30 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
++mixer_count;
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:

View File

@ -40,6 +40,10 @@ void mix_setdevs(struct snd_mixer *m, u_int32_t v);
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);
extern int mixer_count;

View File

@ -136,7 +136,7 @@ nomenclature:
#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)

View File

@ -243,12 +243,20 @@ ua_mixer_init(struct snd_mixer *m)
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);