|
|
|
@ -330,6 +330,11 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
|
|
|
|
|
case snd_soc_dapm_mixer_named_ctl:
|
|
|
|
|
mc = (struct soc_mixer_control *)kcontrol->private_value;
|
|
|
|
|
|
|
|
|
|
if (mc->autodisable && snd_soc_volsw_is_stereo(mc))
|
|
|
|
|
dev_warn(widget->dapm->dev,
|
|
|
|
|
"ASoC: Unsupported stereo autodisable control '%s'\n",
|
|
|
|
|
ctrl_name);
|
|
|
|
|
|
|
|
|
|
if (mc->autodisable) {
|
|
|
|
|
struct snd_soc_dapm_widget template;
|
|
|
|
|
|
|
|
|
@ -723,7 +728,8 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set up initial codec paths */
|
|
|
|
|
static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
|
|
|
|
|
static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
|
|
|
|
|
int nth_path)
|
|
|
|
|
{
|
|
|
|
|
struct soc_mixer_control *mc = (struct soc_mixer_control *)
|
|
|
|
|
p->sink->kcontrol_news[i].private_value;
|
|
|
|
@ -736,7 +742,25 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
|
|
|
|
|
|
|
|
|
|
if (reg != SND_SOC_NOPM) {
|
|
|
|
|
soc_dapm_read(p->sink->dapm, reg, &val);
|
|
|
|
|
val = (val >> shift) & mask;
|
|
|
|
|
/*
|
|
|
|
|
* The nth_path argument allows this function to know
|
|
|
|
|
* which path of a kcontrol it is setting the initial
|
|
|
|
|
* status for. Ideally this would support any number
|
|
|
|
|
* of paths and channels. But since kcontrols only come
|
|
|
|
|
* in mono and stereo variants, we are limited to 2
|
|
|
|
|
* channels.
|
|
|
|
|
*
|
|
|
|
|
* The following code assumes for stereo controls the
|
|
|
|
|
* first path is the left channel, and all remaining
|
|
|
|
|
* paths are the right channel.
|
|
|
|
|
*/
|
|
|
|
|
if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) {
|
|
|
|
|
if (reg != mc->rreg)
|
|
|
|
|
soc_dapm_read(p->sink->dapm, mc->rreg, &val);
|
|
|
|
|
val = (val >> mc->rshift) & mask;
|
|
|
|
|
} else {
|
|
|
|
|
val = (val >> shift) & mask;
|
|
|
|
|
}
|
|
|
|
|
if (invert)
|
|
|
|
|
val = max - val;
|
|
|
|
|
p->connect = !!val;
|
|
|
|
@ -749,13 +773,13 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
|
|
|
|
|
static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
|
|
|
|
|
struct snd_soc_dapm_path *path, const char *control_name)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int i, nth_path = 0;
|
|
|
|
|
|
|
|
|
|
/* search for mixer kcontrol */
|
|
|
|
|
for (i = 0; i < path->sink->num_kcontrols; i++) {
|
|
|
|
|
if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
|
|
|
|
|
path->name = path->sink->kcontrol_news[i].name;
|
|
|
|
|
dapm_set_mixer_path_status(path, i);
|
|
|
|
|
dapm_set_mixer_path_status(path, i, nth_path++);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1626,6 +1650,15 @@ static void dapm_widget_update(struct snd_soc_card *card)
|
|
|
|
|
dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n",
|
|
|
|
|
w->name, ret);
|
|
|
|
|
|
|
|
|
|
if (update->has_second_set) {
|
|
|
|
|
ret = soc_dapm_update_bits(w->dapm, update->reg2,
|
|
|
|
|
update->mask2, update->val2);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
dev_err(w->dapm->dev,
|
|
|
|
|
"ASoC: %s DAPM update failed: %d\n",
|
|
|
|
|
w->name, ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (wi = 0; wi < wlist->num_widgets; wi++) {
|
|
|
|
|
w = wlist->widgets[wi];
|
|
|
|
|
|
|
|
|
@ -2177,7 +2210,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
|
|
|
|
|
|
|
|
|
|
/* test and update the power status of a mixer or switch widget */
|
|
|
|
|
static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
|
|
|
|
|
struct snd_kcontrol *kcontrol, int connect)
|
|
|
|
|
struct snd_kcontrol *kcontrol,
|
|
|
|
|
int connect, int rconnect)
|
|
|
|
|
{
|
|
|
|
|
struct snd_soc_dapm_path *path;
|
|
|
|
|
int found = 0;
|
|
|
|
@ -2186,8 +2220,33 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
|
|
|
|
|
|
|
|
|
|
/* find dapm widget path assoc with kcontrol */
|
|
|
|
|
dapm_kcontrol_for_each_path(path, kcontrol) {
|
|
|
|
|
/*
|
|
|
|
|
* Ideally this function should support any number of
|
|
|
|
|
* paths and channels. But since kcontrols only come
|
|
|
|
|
* in mono and stereo variants, we are limited to 2
|
|
|
|
|
* channels.
|
|
|
|
|
*
|
|
|
|
|
* The following code assumes for stereo controls the
|
|
|
|
|
* first path (when 'found == 0') is the left channel,
|
|
|
|
|
* and all remaining paths (when 'found == 1') are the
|
|
|
|
|
* right channel.
|
|
|
|
|
*
|
|
|
|
|
* A stereo control is signified by a valid 'rconnect'
|
|
|
|
|
* value, either 0 for unconnected, or >= 0 for connected.
|
|
|
|
|
* This is chosen instead of using snd_soc_volsw_is_stereo,
|
|
|
|
|
* so that the behavior of snd_soc_dapm_mixer_update_power
|
|
|
|
|
* doesn't change even when the kcontrol passed in is
|
|
|
|
|
* stereo.
|
|
|
|
|
*
|
|
|
|
|
* It passes 'connect' as the path connect status for
|
|
|
|
|
* the left channel, and 'rconnect' for the right
|
|
|
|
|
* channel.
|
|
|
|
|
*/
|
|
|
|
|
if (found && rconnect >= 0)
|
|
|
|
|
soc_dapm_connect_path(path, rconnect, "mixer update");
|
|
|
|
|
else
|
|
|
|
|
soc_dapm_connect_path(path, connect, "mixer update");
|
|
|
|
|
found = 1;
|
|
|
|
|
soc_dapm_connect_path(path, connect, "mixer update");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
@ -2205,7 +2264,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
|
|
|
|
|
|
|
|
|
|
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
|
|
|
|
|
card->update = update;
|
|
|
|
|
ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
|
|
|
|
|
ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1);
|
|
|
|
|
card->update = NULL;
|
|
|
|
|
mutex_unlock(&card->dapm_mutex);
|
|
|
|
|
if (ret > 0)
|
|
|
|
@ -3030,22 +3089,28 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
|
|
|
|
|
int reg = mc->reg;
|
|
|
|
|
unsigned int shift = mc->shift;
|
|
|
|
|
int max = mc->max;
|
|
|
|
|
unsigned int width = fls(max);
|
|
|
|
|
unsigned int mask = (1 << fls(max)) - 1;
|
|
|
|
|
unsigned int invert = mc->invert;
|
|
|
|
|
unsigned int val;
|
|
|
|
|
unsigned int reg_val, val, rval = 0;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
if (snd_soc_volsw_is_stereo(mc))
|
|
|
|
|
dev_warn(dapm->dev,
|
|
|
|
|
"ASoC: Control '%s' is stereo, which is not supported\n",
|
|
|
|
|
kcontrol->id.name);
|
|
|
|
|
|
|
|
|
|
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
|
|
|
|
|
if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) {
|
|
|
|
|
ret = soc_dapm_read(dapm, reg, &val);
|
|
|
|
|
val = (val >> shift) & mask;
|
|
|
|
|
ret = soc_dapm_read(dapm, reg, ®_val);
|
|
|
|
|
val = (reg_val >> shift) & mask;
|
|
|
|
|
|
|
|
|
|
if (ret == 0 && reg != mc->rreg)
|
|
|
|
|
ret = soc_dapm_read(dapm, mc->rreg, ®_val);
|
|
|
|
|
|
|
|
|
|
if (snd_soc_volsw_is_stereo(mc))
|
|
|
|
|
rval = (reg_val >> mc->rshift) & mask;
|
|
|
|
|
} else {
|
|
|
|
|
val = dapm_kcontrol_get_value(kcontrol);
|
|
|
|
|
reg_val = dapm_kcontrol_get_value(kcontrol);
|
|
|
|
|
val = reg_val & mask;
|
|
|
|
|
|
|
|
|
|
if (snd_soc_volsw_is_stereo(mc))
|
|
|
|
|
rval = (reg_val >> width) & mask;
|
|
|
|
|
}
|
|
|
|
|
mutex_unlock(&card->dapm_mutex);
|
|
|
|
|
|
|
|
|
@ -3057,6 +3122,13 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
|
|
|
|
|
else
|
|
|
|
|
ucontrol->value.integer.value[0] = val;
|
|
|
|
|
|
|
|
|
|
if (snd_soc_volsw_is_stereo(mc)) {
|
|
|
|
|
if (invert)
|
|
|
|
|
ucontrol->value.integer.value[1] = max - rval;
|
|
|
|
|
else
|
|
|
|
|
ucontrol->value.integer.value[1] = rval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
|
|
|
|
@ -3080,46 +3152,66 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
|
|
|
|
|
int reg = mc->reg;
|
|
|
|
|
unsigned int shift = mc->shift;
|
|
|
|
|
int max = mc->max;
|
|
|
|
|
unsigned int mask = (1 << fls(max)) - 1;
|
|
|
|
|
unsigned int width = fls(max);
|
|
|
|
|
unsigned int mask = (1 << width) - 1;
|
|
|
|
|
unsigned int invert = mc->invert;
|
|
|
|
|
unsigned int val;
|
|
|
|
|
int connect, change, reg_change = 0;
|
|
|
|
|
struct snd_soc_dapm_update update;
|
|
|
|
|
unsigned int val, rval = 0;
|
|
|
|
|
int connect, rconnect = -1, change, reg_change = 0;
|
|
|
|
|
struct snd_soc_dapm_update update = { NULL };
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
if (snd_soc_volsw_is_stereo(mc))
|
|
|
|
|
dev_warn(dapm->dev,
|
|
|
|
|
"ASoC: Control '%s' is stereo, which is not supported\n",
|
|
|
|
|
kcontrol->id.name);
|
|
|
|
|
|
|
|
|
|
val = (ucontrol->value.integer.value[0] & mask);
|
|
|
|
|
connect = !!val;
|
|
|
|
|
|
|
|
|
|
if (invert)
|
|
|
|
|
val = max - val;
|
|
|
|
|
|
|
|
|
|
if (snd_soc_volsw_is_stereo(mc)) {
|
|
|
|
|
rval = (ucontrol->value.integer.value[1] & mask);
|
|
|
|
|
rconnect = !!rval;
|
|
|
|
|
if (invert)
|
|
|
|
|
rval = max - rval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
|
|
|
|
|
|
|
|
|
|
change = dapm_kcontrol_set_value(kcontrol, val);
|
|
|
|
|
/* This assumes field width < (bits in unsigned int / 2) */
|
|
|
|
|
if (width > sizeof(unsigned int) * 8 / 2)
|
|
|
|
|
dev_warn(dapm->dev,
|
|
|
|
|
"ASoC: control %s field width limit exceeded\n",
|
|
|
|
|
kcontrol->id.name);
|
|
|
|
|
change = dapm_kcontrol_set_value(kcontrol, val | (rval << width));
|
|
|
|
|
|
|
|
|
|
if (reg != SND_SOC_NOPM) {
|
|
|
|
|
mask = mask << shift;
|
|
|
|
|
val = val << shift;
|
|
|
|
|
rval = rval << mc->rshift;
|
|
|
|
|
|
|
|
|
|
reg_change = soc_dapm_test_bits(dapm, reg, mask, val);
|
|
|
|
|
reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val);
|
|
|
|
|
|
|
|
|
|
if (snd_soc_volsw_is_stereo(mc))
|
|
|
|
|
reg_change |= soc_dapm_test_bits(dapm, mc->rreg,
|
|
|
|
|
mask << mc->rshift,
|
|
|
|
|
rval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (change || reg_change) {
|
|
|
|
|
if (reg_change) {
|
|
|
|
|
if (snd_soc_volsw_is_stereo(mc)) {
|
|
|
|
|
update.has_second_set = true;
|
|
|
|
|
update.reg2 = mc->rreg;
|
|
|
|
|
update.mask2 = mask << mc->rshift;
|
|
|
|
|
update.val2 = rval;
|
|
|
|
|
}
|
|
|
|
|
update.kcontrol = kcontrol;
|
|
|
|
|
update.reg = reg;
|
|
|
|
|
update.mask = mask;
|
|
|
|
|
update.mask = mask << shift;
|
|
|
|
|
update.val = val;
|
|
|
|
|
card->update = &update;
|
|
|
|
|
}
|
|
|
|
|
change |= reg_change;
|
|
|
|
|
|
|
|
|
|
ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
|
|
|
|
|
ret = soc_dapm_mixer_update_power(card, kcontrol, connect,
|
|
|
|
|
rconnect);
|
|
|
|
|
|
|
|
|
|
card->update = NULL;
|
|
|
|
|
}
|
|
|
|
@ -3192,7 +3284,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
|
|
|
|
|
unsigned int *item = ucontrol->value.enumerated.item;
|
|
|
|
|
unsigned int val, change, reg_change = 0;
|
|
|
|
|
unsigned int mask;
|
|
|
|
|
struct snd_soc_dapm_update update;
|
|
|
|
|
struct snd_soc_dapm_update update = { NULL };
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
if (item[0] >= e->items)
|
|
|
|
|