Merge branch 'topic/dapm' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into asoc-sunxi
This commit is contained in:
commit
a1ca97ba44
|
@ -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 {
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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);
|
||||||
|
|
||||||
/* 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,
|
||||||
|
|
||||||
/* 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,
|
||||||
|
|
||||||
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,
|
||||||
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, ®_val);
|
||||||
val = (val >> shift) & mask;
|
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 {
|
} 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,
|
||||||
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,
|
||||||
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, 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 (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,
|
||||||
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)
|
||||||
|
|
Loading…
Reference in New Issue