Commit c0f2302a authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/dapm' into asoc-next

parents 3f1b8613 02866eab
...@@ -272,6 +272,16 @@ struct device; ...@@ -272,6 +272,16 @@ struct device;
/* dapm kcontrol types */ /* dapm kcontrol types */
#define SOC_DAPM_DOUBLE(xname, reg, lshift, rshift, max, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_volsw, \
.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
.private_value = SOC_DOUBLE_VALUE(reg, lshift, rshift, max, invert, 0) }
#define SOC_DAPM_DOUBLE_R(xname, lreg, rreg, shift, max, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_volsw, \
.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
.private_value = SOC_DOUBLE_R_VALUE(lreg, rreg, shift, max, invert) }
#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \ #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_volsw, \ .info = snd_soc_info_volsw, \
...@@ -615,6 +625,10 @@ struct snd_soc_dapm_update { ...@@ -615,6 +625,10 @@ struct snd_soc_dapm_update {
int reg; int reg;
int mask; int mask;
int val; int val;
int reg2;
int mask2;
int val2;
bool has_second_set;
}; };
struct snd_soc_dapm_wcache { struct snd_soc_dapm_wcache {
......
...@@ -160,7 +160,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol, ...@@ -160,7 +160,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adau *adau = snd_soc_codec_get_drvdata(codec); struct adau *adau = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct snd_soc_dapm_update update; struct snd_soc_dapm_update update = { 0 };
unsigned int stream = e->shift_l; unsigned int stream = e->shift_l;
unsigned int val, change; unsigned int val, change;
int reg; int reg;
......
...@@ -157,7 +157,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, ...@@ -157,7 +157,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
unsigned int mask = (1 << fls(max)) - 1; unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert; unsigned int invert = mc->invert;
unsigned short val; unsigned short val;
struct snd_soc_dapm_update update; struct snd_soc_dapm_update update = { 0 };
int connect, change; int connect, change;
val = (ucontrol->value.integer.value[0] & mask); val = (ucontrol->value.integer.value[0] & mask);
......
...@@ -187,7 +187,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, ...@@ -187,7 +187,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *mc = struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value; (struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, mask, shift, old; unsigned int mixer, mask, shift, old;
struct snd_soc_dapm_update update; struct snd_soc_dapm_update update = { 0 };
bool change; bool change;
mixer = mc->shift >> 8; mixer = mc->shift >> 8;
......
...@@ -231,7 +231,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, ...@@ -231,7 +231,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *mc = struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value; (struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, mask, shift, old; unsigned int mixer, mask, shift, old;
struct snd_soc_dapm_update update; struct snd_soc_dapm_update update = { 0 };
bool change; bool change;
mixer = mc->shift >> 8; mixer = mc->shift >> 8;
......
...@@ -330,6 +330,11 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, ...@@ -330,6 +330,11 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
case snd_soc_dapm_mixer_named_ctl: case snd_soc_dapm_mixer_named_ctl:
mc = (struct soc_mixer_control *)kcontrol->private_value; 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) { if (mc->autodisable) {
struct snd_soc_dapm_widget template; struct snd_soc_dapm_widget template;
...@@ -723,7 +728,8 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, ...@@ -723,7 +728,8 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
} }
/* set up initial codec paths */ /* 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 *) struct soc_mixer_control *mc = (struct soc_mixer_control *)
p->sink->kcontrol_news[i].private_value; 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) ...@@ -736,7 +742,25 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
if (reg != SND_SOC_NOPM) { if (reg != SND_SOC_NOPM) {
soc_dapm_read(p->sink->dapm, reg, &val); 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) if (invert)
val = max - val; val = max - val;
p->connect = !!val; p->connect = !!val;
...@@ -749,13 +773,13 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i) ...@@ -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, static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_path *path, const char *control_name) struct snd_soc_dapm_path *path, const char *control_name)
{ {
int i; int i, nth_path = 0;
/* search for mixer kcontrol */ /* search for mixer kcontrol */
for (i = 0; i < path->sink->num_kcontrols; i++) { for (i = 0; i < path->sink->num_kcontrols; i++) {
if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
path->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; return 0;
} }
} }
...@@ -1626,6 +1650,15 @@ static void dapm_widget_update(struct snd_soc_card *card) ...@@ -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", dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n",
w->name, ret); 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++) { for (wi = 0; wi < wlist->num_widgets; wi++) {
w = wlist->widgets[wi]; w = wlist->widgets[wi];
...@@ -2177,7 +2210,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); ...@@ -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 */ /* test and update the power status of a mixer or switch widget */
static int soc_dapm_mixer_update_power(struct snd_soc_card *card, 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; struct snd_soc_dapm_path *path;
int found = 0; int found = 0;
...@@ -2186,8 +2220,33 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, ...@@ -2186,8 +2220,33 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
/* find dapm widget path assoc with kcontrol */ /* find dapm widget path assoc with kcontrol */
dapm_kcontrol_for_each_path(path, 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; found = 1;
soc_dapm_connect_path(path, connect, "mixer update");
} }
if (found) if (found)
...@@ -2205,7 +2264,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, ...@@ -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); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
card->update = update; 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; card->update = NULL;
mutex_unlock(&card->dapm_mutex); mutex_unlock(&card->dapm_mutex);
if (ret > 0) if (ret > 0)
...@@ -3030,22 +3089,28 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, ...@@ -3030,22 +3089,28 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
int reg = mc->reg; int reg = mc->reg;
unsigned int shift = mc->shift; unsigned int shift = mc->shift;
int max = mc->max; int max = mc->max;
unsigned int width = fls(max);
unsigned int mask = (1 << fls(max)) - 1; unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert; unsigned int invert = mc->invert;
unsigned int val; unsigned int reg_val, val, rval = 0;
int ret = 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); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) { if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) {
ret = soc_dapm_read(dapm, reg, &val); ret = soc_dapm_read(dapm, reg, &reg_val);
val = (val >> shift) & mask; val = (reg_val >> shift) & mask;
if (ret == 0 && reg != mc->rreg)
ret = soc_dapm_read(dapm, mc->rreg, &reg_val);
if (snd_soc_volsw_is_stereo(mc))
rval = (reg_val >> mc->rshift) & mask;
} else { } 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); mutex_unlock(&card->dapm_mutex);
...@@ -3057,6 +3122,13 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, ...@@ -3057,6 +3122,13 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
else else
ucontrol->value.integer.value[0] = val; 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; return ret;
} }
EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
...@@ -3080,46 +3152,66 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, ...@@ -3080,46 +3152,66 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
int reg = mc->reg; int reg = mc->reg;
unsigned int shift = mc->shift; unsigned int shift = mc->shift;
int max = mc->max; 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 invert = mc->invert;
unsigned int val; unsigned int val, rval = 0;
int connect, change, reg_change = 0; int connect, rconnect = -1, change, reg_change = 0;
struct snd_soc_dapm_update update; struct snd_soc_dapm_update update = { NULL };
int ret = 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);
val = (ucontrol->value.integer.value[0] & mask); val = (ucontrol->value.integer.value[0] & mask);
connect = !!val; connect = !!val;
if (invert) if (invert)
val = max - val; 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); 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) { if (reg != SND_SOC_NOPM) {
mask = mask << shift;
val = val << shift; val = val << shift;
rval = rval << mc->rshift;
reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val);
reg_change = soc_dapm_test_bits(dapm, reg, mask, 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 (change || reg_change) {
if (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.kcontrol = kcontrol;
update.reg = reg; update.reg = reg;
update.mask = mask; update.mask = mask << shift;
update.val = val; update.val = val;
card->update = &update; card->update = &update;
} }
change |= reg_change; 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; card->update = NULL;
} }
...@@ -3192,7 +3284,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, ...@@ -3192,7 +3284,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
unsigned int *item = ucontrol->value.enumerated.item; unsigned int *item = ucontrol->value.enumerated.item;
unsigned int val, change, reg_change = 0; unsigned int val, change, reg_change = 0;
unsigned int mask; unsigned int mask;
struct snd_soc_dapm_update update; struct snd_soc_dapm_update update = { NULL };
int ret = 0; int ret = 0;
if (item[0] >= e->items) if (item[0] >= e->items)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment