svn commit: r230451 - head/sys/dev/sound/pci/hda
Alexander Motin
mav at FreeBSD.org
Sun Jan 22 10:24:13 UTC 2012
Author: mav
Date: Sun Jan 22 10:24:12 2012
New Revision: 230451
URL: http://svn.freebsd.org/changeset/base/230451
Log:
Complete rewrite of the snd_hda(4) volume control.
Previous code was relatively dumb. During CODEC probe it was tracing signals
and statically binding amplifier controls to the OSS mixer controls. To set
volume it just set all bound amplifier controls proportionally to mixer
level, not looking on their hierarchy and amplification levels/offsets.
New code is much smarter. It also traces signals during probe, but mostly
to find out possible amplification control rages in dB for each specific
signal. To set volume it retraces each affected signal again and sets
amplifiers controls recursively to reach desired amplification level in dB.
It would be nice to export values in dB to user, but unluckily our OSS mixer
API is too simple for that.
As result of this change:
- cascaded amplifiers will work together to reach maximal precision.
If some input has 0/+40dB preamplifier with 10dB step and -10/+10dB mixer
with 1dB step after it, new code will use both to provide 0/+40dB control
with 1dB step! We could even get -10/+50dB range there, but that is
intentionally blocked for now.
- different channels of multichannel associations on non-uniform CODECs
such as VIA VT1708S will have the same volume, not looking that control
ranges are different. It was not good when fronts were 12dB louder.
- for multiplexed recording, when we can record from only one source at
a time, we can now use recording amplifier controls to set different
volume levels for different inputs if they have no own controls of they
are less precise. If recording source change, amplifiers will be
reconfigured.
To improve out-of-the-box behavior, ignore default volume levels set by
sound(4) and use own, more reasonable: +20dB for mics, -10dB for analog
output volume and 0dB for the rest of controls. sound(4) defaults of 75%
mean absolutely random things for different controls of different CODECs
because of very different control ranges.
Together with further planned automatic recording source selection this
should allow users to get fine playback and recording without touching
mixer first.
Note that existing users should delete /var/db/mixer*-state and reboot
or trigger CODEC reconfiguration to get new default values.
MFC after: 2 months
Sponsored by: iXsystems, Inc.
Modified:
head/sys/dev/sound/pci/hda/hdaa.c
head/sys/dev/sound/pci/hda/hdaa.h
Modified: head/sys/dev/sound/pci/hda/hdaa.c
==============================================================================
--- head/sys/dev/sound/pci/hda/hdaa.c Sun Jan 22 10:16:24 2012 (r230450)
+++ head/sys/dev/sound/pci/hda/hdaa.c Sun Jan 22 10:24:12 2012 (r230451)
@@ -175,6 +175,8 @@ static const struct {
};
#define HDA_RATE_TAB_LEN (sizeof(hda_rate_tab) / sizeof(hda_rate_tab[0]))
+const static char *ossnames[] = SOUND_DEVICE_NAMES;
+
/****************************************************************************
* Function prototypes
****************************************************************************/
@@ -193,7 +195,6 @@ static void hdaa_dump_pin_config(struct
static char *
hdaa_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len)
{
- static char *ossname[] = SOUND_DEVICE_NAMES;
int i, first = 1;
bzero(buf, len);
@@ -201,7 +202,7 @@ hdaa_audio_ctl_ossmixer_mask2allname(uin
if (mask & (1 << i)) {
if (first == 0)
strlcat(buf, ", ", len);
- strlcat(buf, ossname[i], len);
+ strlcat(buf, ossnames[i], len);
first = 0;
}
}
@@ -1774,11 +1775,11 @@ hdaa_audio_ctl_ossmixer_init(struct snd_
struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m);
struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
struct hdaa_widget *w, *cw;
- struct hdaa_audio_ctl *ctl;
uint32_t mask, recmask;
- int i, j, softpcmvol;
+ int i, j;
hdaa_lock(devinfo);
+ pdevinfo->mixer = m;
/* Make sure that in case of soft volume it won't stay muted. */
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
@@ -1786,11 +1787,10 @@ hdaa_audio_ctl_ossmixer_init(struct snd_
pdevinfo->right[i] = 100;
}
- mask = 0;
- recmask = 0;
-
- /* Declate EAPD as ogain control. */
+ /* Declare volume controls assigned to this association. */
+ mask = pdevinfo->ossmask;
if (pdevinfo->playas >= 0) {
+ /* Declate EAPD as ogain control. */
for (i = devinfo->startnode; i < devinfo->endnode; i++) {
w = hdaa_widget_get(devinfo, i);
if (w == NULL || w->enable == 0)
@@ -1802,23 +1802,36 @@ hdaa_audio_ctl_ossmixer_init(struct snd_
mask |= SOUND_MASK_OGAIN;
break;
}
- }
- /* Declare volume controls assigned to this association. */
- i = 0;
- ctl = NULL;
- while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
- if (ctl->enable == 0)
- continue;
- if ((pdevinfo->playas >= 0 &&
- ctl->widget->bindas == pdevinfo->playas) ||
- (pdevinfo->recas >= 0 &&
- ctl->widget->bindas == pdevinfo->recas) ||
- (ctl->widget->bindas == -2 && pdevinfo->index == 0))
- mask |= ctl->ossmask;
+ /* Declare soft PCM volume if needed. */
+ if ((mask & SOUND_MASK_PCM) == 0 ||
+ (devinfo->quirks & HDAA_QUIRK_SOFTPCMVOL) ||
+ pdevinfo->minamp[SOUND_MIXER_PCM] ==
+ pdevinfo->maxamp[SOUND_MIXER_PCM]) {
+ mask |= SOUND_MASK_PCM;
+ pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL);
+ HDA_BOOTHVERBOSE(
+ device_printf(pdevinfo->dev,
+ "Forcing Soft PCM volume\n");
+ );
+ }
+
+ /* Declare master volume if needed. */
+ if ((mask & SOUND_MASK_VOLUME) == 0) {
+ mask |= SOUND_MASK_VOLUME;
+ mix_setparentchild(m, SOUND_MIXER_VOLUME,
+ SOUND_MASK_PCM);
+ mix_setrealdev(m, SOUND_MIXER_VOLUME,
+ SOUND_MIXER_NONE);
+ HDA_BOOTHVERBOSE(
+ device_printf(pdevinfo->dev,
+ "Forcing master volume with PCM\n");
+ );
+ }
}
/* Declare record sources available to this association. */
+ recmask = 0;
if (pdevinfo->recas >= 0) {
for (i = 0; i < 16; i++) {
if (devinfo->as[pdevinfo->recas].dacs[0][i] < 0)
@@ -1841,66 +1854,273 @@ hdaa_audio_ctl_ossmixer_init(struct snd_
}
}
- /* Declare soft PCM volume if needed. */
- if (pdevinfo->playas >= 0) {
- ctl = NULL;
- if ((mask & SOUND_MASK_PCM) == 0 ||
- (devinfo->quirks & HDAA_QUIRK_SOFTPCMVOL)) {
- softpcmvol = 1;
- mask |= SOUND_MASK_PCM;
- } else {
- softpcmvol = 0;
- i = 0;
- while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
- if (ctl->enable == 0)
- continue;
- if (ctl->widget->bindas != pdevinfo->playas &&
- (ctl->widget->bindas != -2 || pdevinfo->index != 0))
- continue;
- if (!(ctl->ossmask & SOUND_MASK_PCM))
- continue;
- if (ctl->step > 0)
- break;
- }
+ recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1;
+ mask &= (1 << SOUND_MIXER_NRDEVICES) - 1;
+ pdevinfo->ossmask = mask;
+
+ mix_setrecdevs(m, recmask);
+ mix_setdevs(m, mask);
+
+ hdaa_unlock(devinfo);
+
+ return (0);
+}
+
+/*
+ * Update amplification per pdevinfo per ossdev, calculate summary coefficient
+ * and write it to codec, update *left and *right to reflect remaining error.
+ */
+static void
+hdaa_audio_ctl_dev_set(struct hdaa_audio_ctl *ctl, int ossdev,
+ int mute, int *left, int *right)
+{
+ int i, zleft, zright, sleft, sright, smute, lval, rval;
+
+ ctl->devleft[ossdev] = *left;
+ ctl->devright[ossdev] = *right;
+ ctl->devmute[ossdev] = mute;
+ smute = sleft = sright = zleft = zright = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ sleft += ctl->devleft[i];
+ sright += ctl->devright[i];
+ smute |= ctl->devmute[i];
+ if (i == ossdev)
+ continue;
+ zleft += ctl->devleft[i];
+ zright += ctl->devright[i];
+ }
+ lval = QDB2VAL(ctl, sleft);
+ rval = QDB2VAL(ctl, sright);
+ hdaa_audio_ctl_amp_set(ctl, smute, lval, rval);
+ *left -= VAL2QDB(ctl, lval) - VAL2QDB(ctl, QDB2VAL(ctl, zleft));
+ *right -= VAL2QDB(ctl, rval) - VAL2QDB(ctl, QDB2VAL(ctl, zright));
+}
+
+/*
+ * Trace signal from source, setting volumes on the way.
+ */
+static void
+hdaa_audio_ctl_source_volume(struct hdaa_pcm_devinfo *pdevinfo,
+ int ossdev, nid_t nid, int index, int mute, int left, int right, int depth)
+{
+ struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
+ struct hdaa_widget *w, *wc;
+ struct hdaa_audio_ctl *ctl;
+ int i, j, conns = 0;
+
+ if (depth > HDA_PARSE_MAXDEPTH)
+ return;
+
+ w = hdaa_widget_get(devinfo, nid);
+ if (w == NULL || w->enable == 0)
+ return;
+
+ /* Count number of active inputs. */
+ if (depth > 0) {
+ for (j = 0; j < w->nconns; j++) {
+ if (!w->connsenable[j])
+ continue;
+ conns++;
}
+ }
- if (softpcmvol == 1 || ctl == NULL) {
- pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL);
- HDA_BOOTVERBOSE(
- device_printf(pdevinfo->dev,
- "%s Soft PCM volume\n",
- (softpcmvol == 1) ? "Forcing" : "Enabling");
- );
+ /* If this is not a first step - use input mixer.
+ Pins have common input ctl so care must be taken. */
+ if (depth > 0 && (conns == 1 ||
+ w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) {
+ ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_IN,
+ index, 1);
+ if (ctl)
+ hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right);
+ }
+
+ /* If widget has own ossdev - not traverse it.
+ It will be traversed on it's own. */
+ if (w->ossdev >= 0 && depth > 0)
+ return;
+
+ /* We must not traverse pin */
+ if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT ||
+ w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) &&
+ depth > 0)
+ return;
+
+ /*
+ * If signals mixed, we can't assign controls farther.
+ * Ignore this on depth zero. Caller must knows why.
+ */
+ if (conns > 1 &&
+ (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER ||
+ w->selconn != index))
+ return;
+
+ ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_OUT, -1, 1);
+ if (ctl)
+ hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right);
+
+ for (i = devinfo->startnode; i < devinfo->endnode; i++) {
+ wc = hdaa_widget_get(devinfo, i);
+ if (wc == NULL || wc->enable == 0)
+ continue;
+ for (j = 0; j < wc->nconns; j++) {
+ if (wc->connsenable[j] && wc->conns[j] == nid) {
+ hdaa_audio_ctl_source_volume(pdevinfo, ossdev,
+ wc->nid, j, mute, left, right, depth + 1);
+ }
}
}
+ return;
+}
- /* Declare master volume if needed. */
- if (pdevinfo->playas >= 0) {
- if ((mask & (SOUND_MASK_VOLUME | SOUND_MASK_PCM)) ==
- SOUND_MASK_PCM) {
- mask |= SOUND_MASK_VOLUME;
- mix_setparentchild(m, SOUND_MIXER_VOLUME,
- SOUND_MASK_PCM);
- mix_setrealdev(m, SOUND_MIXER_VOLUME,
- SOUND_MIXER_NONE);
- HDA_BOOTVERBOSE(
- device_printf(pdevinfo->dev,
- "Forcing master volume with PCM\n");
- );
+/*
+ * Trace signal from destination, setting volumes on the way.
+ */
+static void
+hdaa_audio_ctl_dest_volume(struct hdaa_pcm_devinfo *pdevinfo,
+ int ossdev, nid_t nid, int index, int mute, int left, int right, int depth)
+{
+ struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
+ struct hdaa_audio_as *as = devinfo->as;
+ struct hdaa_widget *w, *wc;
+ struct hdaa_audio_ctl *ctl;
+ int i, j, consumers, cleft, cright;
+
+ if (depth > HDA_PARSE_MAXDEPTH)
+ return;
+
+ w = hdaa_widget_get(devinfo, nid);
+ if (w == NULL || w->enable == 0)
+ return;
+
+ if (depth > 0) {
+ /* If this node produce output for several consumers,
+ we can't touch it. */
+ consumers = 0;
+ for (i = devinfo->startnode; i < devinfo->endnode; i++) {
+ wc = hdaa_widget_get(devinfo, i);
+ if (wc == NULL || wc->enable == 0)
+ continue;
+ for (j = 0; j < wc->nconns; j++) {
+ if (wc->connsenable[j] && wc->conns[j] == nid)
+ consumers++;
+ }
}
+ /* The only exception is if real HP redirection is configured
+ and this is a duplication point.
+ XXX: Actually exception is not completely correct.
+ XXX: Duplication point check is not perfect. */
+ if ((consumers == 2 && (w->bindas < 0 ||
+ as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir ||
+ (w->bindseqmask & (1 << 15)) == 0)) ||
+ consumers > 2)
+ return;
+
+ /* Else use it's output mixer. */
+ ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
+ HDAA_CTL_OUT, -1, 1);
+ if (ctl)
+ hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right);
}
- recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1;
- mask &= (1 << SOUND_MIXER_NRDEVICES) - 1;
+ /* We must not traverse pin */
+ if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
+ depth > 0)
+ return;
- mix_setrecdevs(m, recmask);
- mix_setdevs(m, mask);
+ for (i = 0; i < w->nconns; i++) {
+ if (w->connsenable[i] == 0)
+ continue;
+ if (index >= 0 && i != index)
+ continue;
+ cleft = left;
+ cright = right;
+ ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
+ HDAA_CTL_IN, i, 1);
+ if (ctl)
+ hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &cleft, &cright);
+ hdaa_audio_ctl_dest_volume(pdevinfo, ossdev, w->conns[i], -1,
+ mute, cleft, cright, depth + 1);
+ }
+}
- hdaa_unlock(devinfo);
+/*
+ * Set volumes for the specified pdevinfo and ossdev.
+ */
+static void
+hdaa_audio_ctl_dev_volume(struct hdaa_pcm_devinfo *pdevinfo, unsigned dev)
+{
+ struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
+ struct hdaa_widget *w, *cw;
+ uint32_t mute;
+ int lvol, rvol;
+ int i, j;
- return (0);
+ mute = 0;
+ if (pdevinfo->left[dev] == 0) {
+ mute |= HDAA_AMP_MUTE_LEFT;
+ lvol = -4000;
+ } else
+ lvol = ((pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) *
+ pdevinfo->left[dev] + 50) / 100 + pdevinfo->minamp[dev];
+ if (pdevinfo->right[dev] == 0) {
+ mute |= HDAA_AMP_MUTE_RIGHT;
+ rvol = -4000;
+ } else
+ rvol = ((pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) *
+ pdevinfo->right[dev] + 50) / 100 + pdevinfo->minamp[dev];
+ for (i = devinfo->startnode; i < devinfo->endnode; i++) {
+ w = hdaa_widget_get(devinfo, i);
+ if (w == NULL || w->enable == 0)
+ continue;
+ if (w->bindas < 0 && pdevinfo->index != 0)
+ continue;
+ if (w->bindas != pdevinfo->playas &&
+ w->bindas != pdevinfo->recas)
+ continue;
+ if (dev == SOUND_MIXER_RECLEV &&
+ w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
+ hdaa_audio_ctl_dest_volume(pdevinfo, dev,
+ w->nid, -1, mute, lvol, rvol, 0);
+ continue;
+ }
+ if (dev == SOUND_MIXER_VOLUME &&
+ w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
+ devinfo->as[w->bindas].dir == HDAA_CTL_OUT) {
+ hdaa_audio_ctl_dest_volume(pdevinfo, dev,
+ w->nid, -1, mute, lvol, rvol, 0);
+ continue;
+ }
+ if (dev == SOUND_MIXER_IGAIN &&
+ w->pflags & HDAA_ADC_MONITOR) {
+ for (j = 0; j < w->nconns; j++) {
+ if (!w->connsenable[j])
+ continue;
+ cw = hdaa_widget_get(devinfo, w->conns[j]);
+ if (cw == NULL || cw->enable == 0)
+ continue;
+ if (cw->bindas == -1)
+ continue;
+ if (cw->bindas >= 0 &&
+ devinfo->as[cw->bindas].dir != HDAA_CTL_IN)
+ continue;
+ hdaa_audio_ctl_dest_volume(pdevinfo, dev,
+ w->nid, j, mute, lvol, rvol, 0);
+ }
+ continue;
+ }
+ if (w->ossdev != dev)
+ continue;
+ hdaa_audio_ctl_source_volume(pdevinfo, dev,
+ w->nid, -1, mute, lvol, rvol, 0);
+ if (dev == SOUND_MIXER_IMIX && (w->pflags & HDAA_IMIX_AS_DST))
+ hdaa_audio_ctl_dest_volume(pdevinfo, dev,
+ w->nid, -1, mute, lvol, rvol, 0);
+ }
}
+/*
+ * OSS Mixer set method.
+ */
static int
hdaa_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev,
unsigned left, unsigned right)
@@ -1908,12 +2128,10 @@ hdaa_audio_ctl_ossmixer_set(struct snd_m
struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m);
struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
struct hdaa_widget *w;
- struct hdaa_audio_ctl *ctl;
- uint32_t mute;
- int lvol, rvol;
- int i, j;
+ int i;
hdaa_lock(devinfo);
+
/* Save new values. */
pdevinfo->left[dev] = left;
pdevinfo->right[dev] = right;
@@ -1954,39 +2172,57 @@ hdaa_audio_ctl_ossmixer_set(struct snd_m
}
/* Recalculate all controls related to this OSS device. */
- i = 0;
- while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
- if (ctl->enable == 0 ||
- !(ctl->ossmask & (1 << dev)))
+ hdaa_audio_ctl_dev_volume(pdevinfo, dev);
+
+ hdaa_unlock(devinfo);
+ return (left | (right << 8));
+}
+
+/*
+ * Set mixer settings to our own default values:
+ * +20dB for mics, -10dB for analog vol, mute for igain, 0dB for others.
+ */
+static void
+hdaa_audio_ctl_set_defaults(struct hdaa_pcm_devinfo *pdevinfo)
+{
+ int amp, vol, dev;
+
+ for (dev = 0; dev < SOUND_MIXER_NRDEVICES; dev++) {
+ if ((pdevinfo->ossmask & (1 << dev)) == 0)
continue;
- if (!((pdevinfo->playas >= 0 &&
- ctl->widget->bindas == pdevinfo->playas) ||
- (pdevinfo->recas >= 0 &&
- ctl->widget->bindas == pdevinfo->recas) ||
- ctl->widget->bindas == -2))
+
+ /* If the value was overriden, leave it as is. */
+ if (resource_int_value(device_get_name(pdevinfo->dev),
+ device_get_unit(pdevinfo->dev), ossnames[dev], &vol) == 0)
continue;
- lvol = 100;
- rvol = 100;
- for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) {
- if (ctl->ossmask & (1 << j)) {
- lvol = lvol * pdevinfo->left[j] / 100;
- rvol = rvol * pdevinfo->right[j] / 100;
- }
+ vol = -1;
+ if (dev == SOUND_MIXER_OGAIN)
+ vol = 100;
+ else if (dev == SOUND_MIXER_IGAIN)
+ vol = 0;
+ else if (dev == SOUND_MIXER_MIC ||
+ dev == SOUND_MIXER_MONITOR)
+ amp = 20 * 4; /* +20dB */
+ else if (dev == SOUND_MIXER_VOLUME && !pdevinfo->digital)
+ amp = -10 * 4; /* -10dB */
+ else
+ amp = 0;
+ if (vol < 0 &&
+ (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) <= 0) {
+ vol = 100;
+ } else if (vol < 0) {
+ vol = ((amp - pdevinfo->minamp[dev]) * 100 +
+ (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) / 2) /
+ (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]);
+ vol = imin(imax(vol, 1), 100);
}
- mute = (lvol == 0) ? HDAA_AMP_MUTE_LEFT : 0;
- mute |= (rvol == 0) ? HDAA_AMP_MUTE_RIGHT : 0;
- lvol = (lvol * ctl->step + 50) / 100;
- rvol = (rvol * ctl->step + 50) / 100;
- hdaa_audio_ctl_amp_set(ctl, mute, lvol, rvol);
+ mix_set(pdevinfo->mixer, dev, vol, vol);
}
- hdaa_unlock(devinfo);
-
- return (left | (right << 8));
}
/*
- * Commutate specified record source.
+ * Recursively commutate specified record source.
*/
static uint32_t
hdaa_audio_ctl_recsel_comm(struct hdaa_pcm_devinfo *pdevinfo, uint32_t src, nid_t nid, int depth)
@@ -2069,6 +2305,7 @@ hdaa_audio_ctl_ossmixer_setrecsrc(struct
struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
struct hdaa_widget *w;
struct hdaa_audio_as *as;
+ struct hdaa_audio_ctl *ctl;
struct hdaa_chan *ch;
int i, j;
uint32_t ret = 0xffffffff;
@@ -2097,9 +2334,47 @@ hdaa_audio_ctl_ossmixer_setrecsrc(struct
ch->io[i], 0);
}
}
+ if (ret == 0xffffffff)
+ ret = 0;
+
+ /*
+ * Some controls could be shared. Reset volumes for controls
+ * related to previously chosen devices, as they may no longer
+ * affect the signal.
+ */
+ i = 0;
+ while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
+ if (ctl->enable == 0 ||
+ !(ctl->ossmask & pdevinfo->recsrc))
+ continue;
+ if (!((pdevinfo->playas >= 0 &&
+ ctl->widget->bindas == pdevinfo->playas) ||
+ (pdevinfo->recas >= 0 &&
+ ctl->widget->bindas == pdevinfo->recas) ||
+ (pdevinfo->index == 0 &&
+ ctl->widget->bindas == -2)))
+ continue;
+ for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) {
+ if (pdevinfo->recsrc & (1 << j)) {
+ ctl->devleft[j] = 0;
+ ctl->devright[j] = 0;
+ ctl->devmute[j] = 0;
+ }
+ }
+ }
+ /*
+ * Some controls could be shared. Set volumes for controls
+ * related to devices selected both previously and now.
+ */
+ for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) {
+ if ((ret | pdevinfo->recsrc) & (1 << j))
+ hdaa_audio_ctl_dev_volume(pdevinfo, j);
+ }
+
+ pdevinfo->recsrc = ret;
hdaa_unlock(devinfo);
- return ((ret == 0xffffffff)? 0 : ret);
+ return (ret);
}
static kobj_method_t hdaa_audio_ctl_ossmixer_methods[] = {
@@ -3782,31 +4057,31 @@ hdaa_audio_disable_crossas(struct hdaa_d
}
-#define HDA_CTL_GIVE(ctl) ((ctl)->step?1:0)
-
/*
- * Find controls to control amplification for source.
+ * Find controls to control amplification for source and calculate possible
+ * amplification range.
*/
static int
hdaa_audio_ctl_source_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index,
- int ossdev, int ctlable, int depth, int need)
+ int ossdev, int ctlable, int depth, int *minamp, int *maxamp)
{
struct hdaa_widget *w, *wc;
struct hdaa_audio_ctl *ctl;
- int i, j, conns = 0, rneed;
+ int i, j, conns = 0, tminamp, tmaxamp, cminamp, cmaxamp, found = 0;
if (depth > HDA_PARSE_MAXDEPTH)
- return (need);
+ return (found);
w = hdaa_widget_get(devinfo, nid);
if (w == NULL || w->enable == 0)
- return (need);
+ return (found);
/* Count number of active inputs. */
if (depth > 0) {
for (j = 0; j < w->nconns; j++) {
- if (w->connsenable[j])
- conns++;
+ if (!w->connsenable[j])
+ continue;
+ conns++;
}
}
@@ -3817,81 +4092,96 @@ hdaa_audio_ctl_source_amp(struct hdaa_de
ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_IN,
index, 1);
if (ctl) {
- if (HDA_CTL_GIVE(ctl) & need)
- ctl->ossmask |= (1 << ossdev);
- else
- ctl->possmask |= (1 << ossdev);
- need &= ~HDA_CTL_GIVE(ctl);
+ ctl->ossmask |= (1 << ossdev);
+ found++;
+ if (*minamp == *maxamp) {
+ *minamp += MINQDB(ctl);
+ *maxamp += MAXQDB(ctl);
+ }
}
}
/* If widget has own ossdev - not traverse it.
It will be traversed on it's own. */
if (w->ossdev >= 0 && depth > 0)
- return (need);
+ return (found);
/* We must not traverse pin */
if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT ||
w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) &&
depth > 0)
- return (need);
+ return (found);
/* record that this widget exports such signal, */
w->ossmask |= (1 << ossdev);
- /* If signals mixed, we can't assign controls farther.
+ /*
+ * If signals mixed, we can't assign controls farther.
* Ignore this on depth zero. Caller must knows why.
- * Ignore this for static selectors if this input selected.
*/
- if (conns > 1)
+ if (conns > 1 &&
+ w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
ctlable = 0;
if (ctlable) {
ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_OUT, -1, 1);
if (ctl) {
- if (HDA_CTL_GIVE(ctl) & need)
- ctl->ossmask |= (1 << ossdev);
- else
- ctl->possmask |= (1 << ossdev);
- need &= ~HDA_CTL_GIVE(ctl);
+ ctl->ossmask |= (1 << ossdev);
+ found++;
+ if (*minamp == *maxamp) {
+ *minamp += MINQDB(ctl);
+ *maxamp += MAXQDB(ctl);
+ }
}
}
- rneed = 0;
+ cminamp = cmaxamp = 0;
for (i = devinfo->startnode; i < devinfo->endnode; i++) {
wc = hdaa_widget_get(devinfo, i);
if (wc == NULL || wc->enable == 0)
continue;
for (j = 0; j < wc->nconns; j++) {
if (wc->connsenable[j] && wc->conns[j] == nid) {
- rneed |= hdaa_audio_ctl_source_amp(devinfo,
- wc->nid, j, ossdev, ctlable, depth + 1, need);
+ tminamp = tmaxamp = 0;
+ found += hdaa_audio_ctl_source_amp(devinfo,
+ wc->nid, j, ossdev, ctlable, depth + 1,
+ &tminamp, &tmaxamp);
+ if (cminamp == 0 && cmaxamp == 0) {
+ cminamp = tminamp;
+ cmaxamp = tmaxamp;
+ } else if (tminamp != tmaxamp) {
+ cminamp = imax(cminamp, tminamp);
+ cmaxamp = imin(cmaxamp, tmaxamp);
+ }
}
}
}
- rneed &= need;
-
- return (rneed);
+ if (*minamp == *maxamp && cminamp < cmaxamp) {
+ *minamp += cminamp;
+ *maxamp += cmaxamp;
+ }
+ return (found);
}
/*
- * Find controls to control amplification for destination.
+ * Find controls to control amplification for destination and calculate
+ * possible amplification range.
*/
-static void
+static int
hdaa_audio_ctl_dest_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index,
- int ossdev, int depth, int need)
+ int ossdev, int depth, int *minamp, int *maxamp)
{
struct hdaa_audio_as *as = devinfo->as;
struct hdaa_widget *w, *wc;
struct hdaa_audio_ctl *ctl;
- int i, j, consumers;
+ int i, j, consumers, tminamp, tmaxamp, cminamp, cmaxamp, found = 0;
if (depth > HDA_PARSE_MAXDEPTH)
- return;
+ return (found);
w = hdaa_widget_get(devinfo, nid);
if (w == NULL || w->enable == 0)
- return;
+ return (found);
if (depth > 0) {
/* If this node produce output for several consumers,
@@ -3914,43 +4204,58 @@ hdaa_audio_ctl_dest_amp(struct hdaa_devi
as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir ||
(w->bindseqmask & (1 << 15)) == 0)) ||
consumers > 2)
- return;
+ return (found);
/* Else use it's output mixer. */
ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
HDAA_CTL_OUT, -1, 1);
if (ctl) {
- if (HDA_CTL_GIVE(ctl) & need)
- ctl->ossmask |= (1 << ossdev);
- else
- ctl->possmask |= (1 << ossdev);
- need &= ~HDA_CTL_GIVE(ctl);
+ ctl->ossmask |= (1 << ossdev);
+ found++;
+ if (*minamp == *maxamp) {
+ *minamp += MINQDB(ctl);
+ *maxamp += MAXQDB(ctl);
+ }
}
}
/* We must not traverse pin */
if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
depth > 0)
- return;
+ return (found);
+ cminamp = cmaxamp = 0;
for (i = 0; i < w->nconns; i++) {
- int tneed = need;
if (w->connsenable[i] == 0)
continue;
if (index >= 0 && i != index)
continue;
+ tminamp = tmaxamp = 0;
ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
HDAA_CTL_IN, i, 1);
if (ctl) {
- if (HDA_CTL_GIVE(ctl) & tneed)
- ctl->ossmask |= (1 << ossdev);
- else
- ctl->possmask |= (1 << ossdev);
- tneed &= ~HDA_CTL_GIVE(ctl);
- }
- hdaa_audio_ctl_dest_amp(devinfo, w->conns[i], -1, ossdev,
- depth + 1, tneed);
+ ctl->ossmask |= (1 << ossdev);
+ found++;
+ if (*minamp == *maxamp) {
+ tminamp += MINQDB(ctl);
+ tmaxamp += MAXQDB(ctl);
+ }
+ }
+ found += hdaa_audio_ctl_dest_amp(devinfo, w->conns[i], -1, ossdev,
+ depth + 1, &tminamp, &tmaxamp);
+ if (cminamp == 0 && cmaxamp == 0) {
+ cminamp = tminamp;
+ cmaxamp = tmaxamp;
+ } else if (tminamp != tmaxamp) {
+ cminamp = imax(cminamp, tminamp);
+ cmaxamp = imin(cmaxamp, tmaxamp);
+ }
+ }
+ if (*minamp == *maxamp && cminamp < cmaxamp) {
+ *minamp += cminamp;
+ *maxamp += cmaxamp;
}
+ return (found);
}
/*
@@ -4158,43 +4463,82 @@ retry:
hdaa_audio_trace_as_extra(devinfo);
}
+/*
+ * Store in pdevinfo new data about whether and how we can control signal
+ * for OSS device to/from specified widget.
+ */
+static void
+hdaa_adjust_amp(struct hdaa_widget *w, int ossdev,
+ int found, int minamp, int maxamp)
+{
+ struct hdaa_devinfo *devinfo = w->devinfo;
+ struct hdaa_pcm_devinfo *pdevinfo;
+
+ if (w->bindas >= 0)
+ pdevinfo = devinfo->as[w->bindas].pdevinfo;
+ else
+ pdevinfo = &devinfo->devs[0];
+ if (found)
+ pdevinfo->ossmask |= (1 << ossdev);
+ if (minamp == 0 && maxamp == 0)
+ return;
+ if (pdevinfo->minamp[ossdev] == 0 && pdevinfo->maxamp[ossdev] == 0) {
+ pdevinfo->minamp[ossdev] = minamp;
+ pdevinfo->maxamp[ossdev] = maxamp;
+ } else {
+ pdevinfo->minamp[ossdev] = imax(pdevinfo->minamp[ossdev], minamp);
+ pdevinfo->maxamp[ossdev] = imin(pdevinfo->maxamp[ossdev], maxamp);
+ }
+}
+
+/*
+ * Trace signals from/to all possible sources/destionstions to find possible
+ * recording sources, OSS device control ranges and to assign controls.
+ */
static void
hdaa_audio_assign_mixers(struct hdaa_devinfo *devinfo)
{
struct hdaa_audio_as *as = devinfo->as;
- struct hdaa_audio_ctl *ctl;
struct hdaa_widget *w, *cw;
- int i, j;
+ int i, j, minamp, maxamp, found;
/* Assign mixers to the tree. */
for (i = devinfo->startnode; i < devinfo->endnode; i++) {
w = hdaa_widget_get(devinfo, i);
if (w == NULL || w->enable == 0)
continue;
+ minamp = maxamp = 0;
if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT ||
w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET ||
(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
as[w->bindas].dir == HDAA_CTL_IN)) {
if (w->ossdev < 0)
continue;
- hdaa_audio_ctl_source_amp(devinfo, w->nid, -1,
- w->ossdev, 1, 0, 1);
+ found = hdaa_audio_ctl_source_amp(devinfo, w->nid, -1,
+ w->ossdev, 1, 0, &minamp, &maxamp);
+ hdaa_adjust_amp(w, w->ossdev, found, minamp, maxamp);
} else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
- hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
- SOUND_MIXER_RECLEV, 0, 1);
+ found = hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
+ SOUND_MIXER_RECLEV, 0, &minamp, &maxamp);
+ hdaa_adjust_amp(w, SOUND_MIXER_RECLEV, found, minamp, maxamp);
} else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
as[w->bindas].dir == HDAA_CTL_OUT) {
- hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
- SOUND_MIXER_VOLUME, 0, 1);
+ found = hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
+ SOUND_MIXER_VOLUME, 0, &minamp, &maxamp);
+ hdaa_adjust_amp(w, SOUND_MIXER_VOLUME, found, minamp, maxamp);
}
if (w->ossdev == SOUND_MIXER_IMIX) {
- if (hdaa_audio_ctl_source_amp(devinfo, w->nid, -1,
- w->ossdev, 1, 0, 1)) {
+ minamp = maxamp = 0;
+ found = hdaa_audio_ctl_source_amp(devinfo, w->nid, -1,
+ w->ossdev, 1, 0, &minamp, &maxamp);
+ if (minamp == maxamp) {
/* If we are unable to control input monitor
as source - try to control it as destination. */
- hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
- w->ossdev, 0, 1);
+ found += hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
+ w->ossdev, 0, &minamp, &maxamp);
+ w->pflags |= HDAA_IMIX_AS_DST;
}
+ hdaa_adjust_amp(w, w->ossdev, found, minamp, maxamp);
}
if (w->pflags & HDAA_ADC_MONITOR) {
for (j = 0; j < w->nconns; j++) {
@@ -4208,17 +4552,15 @@ hdaa_audio_assign_mixers(struct hdaa_dev
if (cw->bindas >= 0 &&
as[cw->bindas].dir != HDAA_CTL_IN)
continue;
- hdaa_audio_ctl_dest_amp(devinfo,
- w->nid, j, SOUND_MIXER_IGAIN, 0, 1);
+ minamp = maxamp = 0;
+ found = hdaa_audio_ctl_dest_amp(devinfo,
+ w->nid, j, SOUND_MIXER_IGAIN, 0,
+ &minamp, &maxamp);
+ hdaa_adjust_amp(w, SOUND_MIXER_IGAIN,
+ found, minamp, maxamp);
}
}
}
- /* Treat unrequired as possible. */
- i = 0;
- while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
- if (ctl->ossmask == 0)
- ctl->ossmask = ctl->possmask;
- }
}
static void
@@ -4671,7 +5013,7 @@ hdaa_pcmchannel_setup(struct hdaa_chan *
}
static void
-hdaa_create_pcms(struct hdaa_devinfo *devinfo)
+hdaa_prepare_pcms(struct hdaa_devinfo *devinfo)
{
struct hdaa_audio_as *as = devinfo->as;
int i, j, k, apdev = 0, ardev = 0, dpdev = 0, drdev = 0;
@@ -4726,6 +5068,7 @@ hdaa_create_pcms(struct hdaa_devinfo *de
continue;
devinfo->devs[j].playas = i;
}
+ as[i].pdevinfo = &devinfo->devs[j];
for (k = 0; k < as[i].num_chans; k++) {
devinfo->chans[as[i].chans[k]].pdevinfo =
&devinfo->devs[j];
@@ -4734,6 +5077,13 @@ hdaa_create_pcms(struct hdaa_devinfo *de
break;
}
}
+}
+
+static void
+hdaa_create_pcms(struct hdaa_devinfo *devinfo)
+{
+ int i;
+
for (i = 0; i < devinfo->num_devs; i++) {
struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i];
@@ -4782,9 +5132,15 @@ hdaa_dump_ctls(struct hdaa_pcm_devinfo *
} else {
device_printf(pdevinfo->dev, "Unknown Ctl");
}
- printf(" (OSS: %s)\n",
+ printf(" (OSS: %s)",
hdaa_audio_ctl_ossmixer_mask2allname(1 << j,
buf, sizeof(buf)));
+ if (pdevinfo->ossmask & (1 << j)) {
+ printf(": %+d/%+ddB\n",
+ pdevinfo->minamp[j] / 4,
+ pdevinfo->maxamp[j] / 4);
+ } else
+ printf("\n");
device_printf(pdevinfo->dev, " |\n");
printed = 1;
}
@@ -4797,8 +5153,8 @@ hdaa_dump_ctls(struct hdaa_pcm_devinfo *
printf("): ");
if (ctl->step > 0) {
printf("%+d/%+ddB (%d steps)%s\n",
- (0 - ctl->offset) * (ctl->size + 1) / 4,
- (ctl->step - ctl->offset) * (ctl->size + 1) / 4,
+ MINQDB(ctl) / 4,
+ MAXQDB(ctl) / 4,
ctl->step + 1,
ctl->mute?" + mute":"");
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-all
mailing list