summaryrefslogtreecommitdiff
path: root/sound/soc/soc-dapm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r--sound/soc/soc-dapm.c390
1 files changed, 250 insertions, 140 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index a2f1da8b4646..735903a74675 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -54,14 +54,15 @@
static int dapm_up_seq[] = {
snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac,
- snd_soc_dapm_mixer, snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp,
- snd_soc_dapm_spk, snd_soc_dapm_post
+ snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_pga,
+ snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
};
+
static int dapm_down_seq[] = {
snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
- snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic,
- snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_value_mux,
- snd_soc_dapm_post
+ snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer,
+ snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias,
+ snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_post
};
static int dapm_status = 1;
@@ -101,7 +102,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
{
switch (w->id) {
case snd_soc_dapm_switch:
- case snd_soc_dapm_mixer: {
+ case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl: {
int val;
struct soc_mixer_control *mc = (struct soc_mixer_control *)
w->kcontrols[i].private_value;
@@ -323,15 +325,32 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
if (path->name != (char*)w->kcontrols[i].name)
continue;
- /* add dapm control with long name */
- name_len = 2 + strlen(w->name)
- + strlen(w->kcontrols[i].name);
+ /* add dapm control with long name.
+ * for dapm_mixer this is the concatenation of the
+ * mixer and kcontrol name.
+ * for dapm_mixer_named_ctl this is simply the
+ * kcontrol name.
+ */
+ name_len = strlen(w->kcontrols[i].name) + 1;
+ if (w->id != snd_soc_dapm_mixer_named_ctl)
+ name_len += 1 + strlen(w->name);
+
path->long_name = kmalloc(name_len, GFP_KERNEL);
+
if (path->long_name == NULL)
return -ENOMEM;
- snprintf(path->long_name, name_len, "%s %s",
- w->name, w->kcontrols[i].name);
+ switch (w->id) {
+ default:
+ snprintf(path->long_name, name_len, "%s %s",
+ w->name, w->kcontrols[i].name);
+ break;
+ case snd_soc_dapm_mixer_named_ctl:
+ snprintf(path->long_name, name_len, "%s",
+ w->kcontrols[i].name);
+ break;
+ }
+
path->long_name[name_len - 1] = '\0';
path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
@@ -503,6 +522,137 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
EXPORT_SYMBOL_GPL(dapm_reg_event);
/*
+ * Scan a single DAPM widget for a complete audio path and update the
+ * power status appropriately.
+ */
+static int dapm_power_widget(struct snd_soc_codec *codec, int event,
+ struct snd_soc_dapm_widget *w)
+{
+ int in, out, power_change, power, ret;
+
+ /* vmid - no action */
+ if (w->id == snd_soc_dapm_vmid)
+ return 0;
+
+ /* active ADC */
+ if (w->id == snd_soc_dapm_adc && w->active) {
+ in = is_connected_input_ep(w);
+ dapm_clear_walk(w->codec);
+ w->power = (in != 0) ? 1 : 0;
+ dapm_update_bits(w);
+ return 0;
+ }
+
+ /* active DAC */
+ if (w->id == snd_soc_dapm_dac && w->active) {
+ out = is_connected_output_ep(w);
+ dapm_clear_walk(w->codec);
+ w->power = (out != 0) ? 1 : 0;
+ dapm_update_bits(w);
+ return 0;
+ }
+
+ /* pre and post event widgets */
+ if (w->id == snd_soc_dapm_pre) {
+ if (!w->event)
+ return 0;
+
+ if (event == SND_SOC_DAPM_STREAM_START) {
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_PRE_PMU);
+ if (ret < 0)
+ return ret;
+ } else if (event == SND_SOC_DAPM_STREAM_STOP) {
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_PRE_PMD);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+ }
+ if (w->id == snd_soc_dapm_post) {
+ if (!w->event)
+ return 0;
+
+ if (event == SND_SOC_DAPM_STREAM_START) {
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_POST_PMU);
+ if (ret < 0)
+ return ret;
+ } else if (event == SND_SOC_DAPM_STREAM_STOP) {
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_POST_PMD);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+ }
+
+ /* all other widgets */
+ in = is_connected_input_ep(w);
+ dapm_clear_walk(w->codec);
+ out = is_connected_output_ep(w);
+ dapm_clear_walk(w->codec);
+ power = (out != 0 && in != 0) ? 1 : 0;
+ power_change = (w->power == power) ? 0 : 1;
+ w->power = power;
+
+ if (!power_change)
+ return 0;
+
+ /* call any power change event handlers */
+ if (w->event)
+ pr_debug("power %s event for %s flags %x\n",
+ w->power ? "on" : "off",
+ w->name, w->event_flags);
+
+ /* power up pre event */
+ if (power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
+ ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* power down pre event */
+ if (!power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
+ ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Lower PGA volume to reduce pops */
+ if (w->id == snd_soc_dapm_pga && !power)
+ dapm_set_pga(w, power);
+
+ dapm_update_bits(w);
+
+ /* Raise PGA volume to reduce pops */
+ if (w->id == snd_soc_dapm_pga && power)
+ dapm_set_pga(w, power);
+
+ /* power up post event */
+ if (power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_POST_PMU);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* power down post event */
+ if (!power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
+ ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
* Scan each dapm widget for complete audio path.
* A complete path is a route that has valid endpoints i.e.:-
*
@@ -514,7 +664,7 @@ EXPORT_SYMBOL_GPL(dapm_reg_event);
static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
{
struct snd_soc_dapm_widget *w;
- int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power;
+ int i, c = 1, *seq = NULL, ret = 0;
/* do we have a sequenced stream event */
if (event == SND_SOC_DAPM_STREAM_START) {
@@ -525,135 +675,20 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
seq = dapm_down_seq;
}
- for(i = 0; i < c; i++) {
+ for (i = 0; i < c; i++) {
list_for_each_entry(w, &codec->dapm_widgets, list) {
/* is widget in stream order */
if (seq && seq[i] && w->id != seq[i])
continue;
- /* vmid - no action */
- if (w->id == snd_soc_dapm_vmid)
- continue;
-
- /* active ADC */
- if (w->id == snd_soc_dapm_adc && w->active) {
- in = is_connected_input_ep(w);
- dapm_clear_walk(w->codec);
- w->power = (in != 0) ? 1 : 0;
- dapm_update_bits(w);
- continue;
- }
-
- /* active DAC */
- if (w->id == snd_soc_dapm_dac && w->active) {
- out = is_connected_output_ep(w);
- dapm_clear_walk(w->codec);
- w->power = (out != 0) ? 1 : 0;
- dapm_update_bits(w);
- continue;
- }
-
- /* pre and post event widgets */
- if (w->id == snd_soc_dapm_pre) {
- if (!w->event)
- continue;
-
- if (event == SND_SOC_DAPM_STREAM_START) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_PRE_PMU);
- if (ret < 0)
- return ret;
- } else if (event == SND_SOC_DAPM_STREAM_STOP) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_PRE_PMD);
- if (ret < 0)
- return ret;
- }
- continue;
- }
- if (w->id == snd_soc_dapm_post) {
- if (!w->event)
- continue;
-
- if (event == SND_SOC_DAPM_STREAM_START) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_POST_PMU);
- if (ret < 0)
- return ret;
- } else if (event == SND_SOC_DAPM_STREAM_STOP) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_POST_PMD);
- if (ret < 0)
- return ret;
- }
- continue;
- }
-
- /* all other widgets */
- in = is_connected_input_ep(w);
- dapm_clear_walk(w->codec);
- out = is_connected_output_ep(w);
- dapm_clear_walk(w->codec);
- power = (out != 0 && in != 0) ? 1 : 0;
- power_change = (w->power == power) ? 0: 1;
- w->power = power;
-
- if (!power_change)
- continue;
-
- /* call any power change event handlers */
- if (w->event)
- pr_debug("power %s event for %s flags %x\n",
- w->power ? "on" : "off",
- w->name, w->event_flags);
-
- /* power up pre event */
- if (power && w->event &&
- (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
- if (ret < 0)
- return ret;
- }
-
- /* power down pre event */
- if (!power && w->event &&
- (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
- if (ret < 0)
- return ret;
- }
-
- /* Lower PGA volume to reduce pops */
- if (w->id == snd_soc_dapm_pga && !power)
- dapm_set_pga(w, power);
-
- dapm_update_bits(w);
-
- /* Raise PGA volume to reduce pops */
- if (w->id == snd_soc_dapm_pga && power)
- dapm_set_pga(w, power);
-
- /* power up post event */
- if (power && w->event &&
- (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_POST_PMU);
- if (ret < 0)
- return ret;
- }
-
- /* power down post event */
- if (!power && w->event &&
- (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
- if (ret < 0)
- return ret;
- }
+ ret = dapm_power_widget(codec, event, w);
+ if (ret != 0)
+ return ret;
}
}
- return ret;
+ return 0;
}
#ifdef DEBUG
@@ -687,6 +722,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
case snd_soc_dapm_adc:
case snd_soc_dapm_pga:
case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
if (w->name) {
in = is_connected_input_ep(w);
dapm_clear_walk(w->codec);
@@ -760,6 +796,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
int found = 0;
if (widget->id != snd_soc_dapm_mixer &&
+ widget->id != snd_soc_dapm_mixer_named_ctl &&
widget->id != snd_soc_dapm_switch)
return -ENODEV;
@@ -795,7 +832,7 @@ static ssize_t dapm_widget_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct snd_soc_device *devdata = dev_get_drvdata(dev);
- struct snd_soc_codec *codec = devdata->codec;
+ struct snd_soc_codec *codec = devdata->card->codec;
struct snd_soc_dapm_widget *w;
int count = 0;
char *state = "not set";
@@ -813,6 +850,7 @@ static ssize_t dapm_widget_show(struct device *dev,
case snd_soc_dapm_adc:
case snd_soc_dapm_pga:
case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
if (w->name)
count += sprintf(buf + count, "%s: %s\n",
w->name, w->power ? "On":"Off");
@@ -876,7 +914,7 @@ static void dapm_free_widgets(struct snd_soc_codec *codec)
}
static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec,
- char *pin, int status)
+ const char *pin, int status)
{
struct snd_soc_dapm_widget *w;
@@ -991,6 +1029,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
break;
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
if (ret != 0)
goto err;
@@ -1068,6 +1107,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
switch(w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
dapm_new_mixer(codec, w);
break;
case snd_soc_dapm_mux:
@@ -1396,6 +1436,76 @@ out:
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
/**
+ * snd_soc_dapm_info_pin_switch - Info for a pin switch
+ *
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a pin switch control.
+ */
+int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_info_pin_switch);
+
+/**
+ * snd_soc_dapm_get_pin_switch - Get information for a pin switch
+ *
+ * @kcontrol: mixer control
+ * @ucontrol: Value
+ */
+int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ const char *pin = (const char *)kcontrol->private_value;
+
+ mutex_lock(&codec->mutex);
+
+ ucontrol->value.integer.value[0] =
+ snd_soc_dapm_get_pin_status(codec, pin);
+
+ mutex_unlock(&codec->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_switch);
+
+/**
+ * snd_soc_dapm_put_pin_switch - Set information for a pin switch
+ *
+ * @kcontrol: mixer control
+ * @ucontrol: Value
+ */
+int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ const char *pin = (const char *)kcontrol->private_value;
+
+ mutex_lock(&codec->mutex);
+
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_enable_pin(codec, pin);
+ else
+ snd_soc_dapm_disable_pin(codec, pin);
+
+ snd_soc_dapm_sync(codec);
+
+ mutex_unlock(&codec->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
+
+/**
* snd_soc_dapm_new_control - create new dapm control
* @codec: audio codec
* @widget: widget template
@@ -1527,8 +1637,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
enum snd_soc_bias_level level)
{
- struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_card *card = socdev->card;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
if (card->set_bias_level)
@@ -1549,7 +1659,7 @@ int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
* NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
* do any widget power switching.
*/
-int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin)
{
return snd_soc_dapm_set_pin(codec, pin, 1);
}
@@ -1564,7 +1674,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
* NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
* do any widget power switching.
*/
-int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, const char *pin)
{
return snd_soc_dapm_set_pin(codec, pin, 0);
}
@@ -1584,7 +1694,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);
* NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
* do any widget power switching.
*/
-int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, const char *pin)
{
return snd_soc_dapm_set_pin(codec, pin, 0);
}
@@ -1599,7 +1709,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin);
*
* Returns 1 for connected otherwise 0.
*/
-int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin)
{
struct snd_soc_dapm_widget *w;
@@ -1620,7 +1730,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);
*/
void snd_soc_dapm_free(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
snd_soc_dapm_sys_remove(socdev->dev);
dapm_free_widgets(codec);