|
@@ -504,17 +504,27 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/* create new dapm mixer control */
|
|
|
-static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
|
|
|
+/*
|
|
|
+ * Determine if a kcontrol is shared. If it is, look it up. If it isn't,
|
|
|
+ * create it. Either way, add the widget into the control's widget list
|
|
|
+ */
|
|
|
+static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
|
|
|
+ int kci, struct snd_soc_dapm_path *path)
|
|
|
{
|
|
|
struct snd_soc_dapm_context *dapm = w->dapm;
|
|
|
- int i, ret = 0;
|
|
|
- size_t name_len, prefix_len;
|
|
|
- struct snd_soc_dapm_path *path;
|
|
|
struct snd_card *card = dapm->card->snd_card;
|
|
|
const char *prefix;
|
|
|
+ size_t prefix_len;
|
|
|
+ int shared;
|
|
|
+ struct snd_kcontrol *kcontrol;
|
|
|
struct snd_soc_dapm_widget_list *wlist;
|
|
|
+ int wlistentries;
|
|
|
size_t wlistsize;
|
|
|
+ bool wname_in_long_name, kcname_in_long_name;
|
|
|
+ size_t name_len;
|
|
|
+ char *long_name;
|
|
|
+ const char *name;
|
|
|
+ int ret;
|
|
|
|
|
|
if (dapm->codec)
|
|
|
prefix = dapm->codec->name_prefix;
|
|
@@ -526,103 +536,141 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
|
|
|
else
|
|
|
prefix_len = 0;
|
|
|
|
|
|
- /* add kcontrol */
|
|
|
- for (i = 0; i < w->num_kcontrols; i++) {
|
|
|
+ shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci],
|
|
|
+ &kcontrol);
|
|
|
|
|
|
- /* match name */
|
|
|
- list_for_each_entry(path, &w->sources, list_sink) {
|
|
|
+ if (kcontrol) {
|
|
|
+ wlist = kcontrol->private_data;
|
|
|
+ wlistentries = wlist->num_widgets + 1;
|
|
|
+ } else {
|
|
|
+ wlist = NULL;
|
|
|
+ wlistentries = 1;
|
|
|
+ }
|
|
|
|
|
|
- /* mixer/mux paths name must match control name */
|
|
|
- if (path->name != (char *)w->kcontrol_news[i].name)
|
|
|
- continue;
|
|
|
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
|
|
|
+ wlistentries * sizeof(struct snd_soc_dapm_widget *);
|
|
|
+ wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
|
|
|
+ if (wlist == NULL) {
|
|
|
+ dev_err(dapm->dev, "ASoC: can't allocate widget list for %s\n",
|
|
|
+ w->name);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ wlist->num_widgets = wlistentries;
|
|
|
+ wlist->widgets[wlistentries - 1] = w;
|
|
|
|
|
|
- if (w->kcontrols[i]) {
|
|
|
- path->kcontrol = w->kcontrols[i];
|
|
|
- continue;
|
|
|
+ if (!kcontrol) {
|
|
|
+ if (shared) {
|
|
|
+ wname_in_long_name = false;
|
|
|
+ kcname_in_long_name = true;
|
|
|
+ } else {
|
|
|
+ switch (w->id) {
|
|
|
+ case snd_soc_dapm_switch:
|
|
|
+ case snd_soc_dapm_mixer:
|
|
|
+ wname_in_long_name = true;
|
|
|
+ kcname_in_long_name = true;
|
|
|
+ break;
|
|
|
+ case snd_soc_dapm_mixer_named_ctl:
|
|
|
+ wname_in_long_name = false;
|
|
|
+ kcname_in_long_name = true;
|
|
|
+ break;
|
|
|
+ case snd_soc_dapm_mux:
|
|
|
+ case snd_soc_dapm_virt_mux:
|
|
|
+ case snd_soc_dapm_value_mux:
|
|
|
+ wname_in_long_name = true;
|
|
|
+ kcname_in_long_name = false;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ kfree(wlist);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ if (wname_in_long_name && kcname_in_long_name) {
|
|
|
+ name_len = strlen(w->name) - prefix_len + 1 +
|
|
|
+ strlen(w->kcontrol_news[kci].name) + 1;
|
|
|
|
|
|
- wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
|
|
|
- sizeof(struct snd_soc_dapm_widget *),
|
|
|
- wlist = kzalloc(wlistsize, GFP_KERNEL);
|
|
|
- if (wlist == NULL) {
|
|
|
- dev_err(dapm->dev,
|
|
|
- "ASoC: can't allocate widget list for %s\n",
|
|
|
- w->name);
|
|
|
+ long_name = kmalloc(name_len, GFP_KERNEL);
|
|
|
+ if (long_name == NULL) {
|
|
|
+ kfree(wlist);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
- wlist->num_widgets = 1;
|
|
|
- wlist->widgets[0] = w;
|
|
|
-
|
|
|
- /* 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.
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The control will get a prefix from the control
|
|
|
+ * creation process but we're also using the same
|
|
|
+ * prefix for widgets so cut the prefix off the
|
|
|
+ * front of the widget name.
|
|
|
*/
|
|
|
- name_len = strlen(w->kcontrol_news[i].name) + 1;
|
|
|
- if (w->id != snd_soc_dapm_mixer_named_ctl)
|
|
|
- name_len += 1 + strlen(w->name);
|
|
|
+ snprintf(long_name, name_len, "%s %s",
|
|
|
+ w->name + prefix_len,
|
|
|
+ w->kcontrol_news[kci].name);
|
|
|
+ long_name[name_len - 1] = '\0';
|
|
|
+
|
|
|
+ name = long_name;
|
|
|
+ } else if (wname_in_long_name) {
|
|
|
+ long_name = NULL;
|
|
|
+ name = w->name + prefix_len;
|
|
|
+ } else {
|
|
|
+ long_name = NULL;
|
|
|
+ name = w->kcontrol_news[kci].name;
|
|
|
+ }
|
|
|
|
|
|
- path->long_name = kmalloc(name_len, GFP_KERNEL);
|
|
|
+ kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], wlist, name,
|
|
|
+ prefix);
|
|
|
+ ret = snd_ctl_add(card, kcontrol);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(dapm->dev,
|
|
|
+ "ASoC: failed to add widget %s dapm kcontrol %s: %d\n",
|
|
|
+ w->name, name, ret);
|
|
|
+ kfree(wlist);
|
|
|
+ kfree(long_name);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
|
|
|
- if (path->long_name == NULL) {
|
|
|
- kfree(wlist);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
+ path->long_name = long_name;
|
|
|
+ }
|
|
|
|
|
|
- switch (w->id) {
|
|
|
- default:
|
|
|
- /* The control will get a prefix from
|
|
|
- * the control creation process but
|
|
|
- * we're also using the same prefix
|
|
|
- * for widgets so cut the prefix off
|
|
|
- * the front of the widget name.
|
|
|
- */
|
|
|
- snprintf((char *)path->long_name, name_len,
|
|
|
- "%s %s", w->name + prefix_len,
|
|
|
- w->kcontrol_news[i].name);
|
|
|
- break;
|
|
|
- case snd_soc_dapm_mixer_named_ctl:
|
|
|
- snprintf((char *)path->long_name, name_len,
|
|
|
- "%s", w->kcontrol_news[i].name);
|
|
|
- break;
|
|
|
- }
|
|
|
+ kcontrol->private_data = wlist;
|
|
|
+ w->kcontrols[kci] = kcontrol;
|
|
|
+ path->kcontrol = kcontrol;
|
|
|
|
|
|
- ((char *)path->long_name)[name_len - 1] = '\0';
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],
|
|
|
- wlist, path->long_name,
|
|
|
- prefix);
|
|
|
- ret = snd_ctl_add(card, path->kcontrol);
|
|
|
- if (ret < 0) {
|
|
|
- dev_err(dapm->dev, "ASoC: failed to add widget"
|
|
|
- " %s dapm kcontrol %s: %d\n",
|
|
|
- w->name, path->long_name, ret);
|
|
|
- kfree(wlist);
|
|
|
- kfree(path->long_name);
|
|
|
- path->long_name = NULL;
|
|
|
- return ret;
|
|
|
+/* create new dapm mixer control */
|
|
|
+static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
|
|
|
+{
|
|
|
+ int i, ret;
|
|
|
+ struct snd_soc_dapm_path *path;
|
|
|
+
|
|
|
+ /* add kcontrol */
|
|
|
+ for (i = 0; i < w->num_kcontrols; i++) {
|
|
|
+ /* match name */
|
|
|
+ list_for_each_entry(path, &w->sources, list_sink) {
|
|
|
+ /* mixer/mux paths name must match control name */
|
|
|
+ if (path->name != (char *)w->kcontrol_news[i].name)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (w->kcontrols[i]) {
|
|
|
+ path->kcontrol = w->kcontrols[i];
|
|
|
+ continue;
|
|
|
}
|
|
|
- w->kcontrols[i] = path->kcontrol;
|
|
|
+
|
|
|
+ ret = dapm_create_or_share_mixmux_kcontrol(w, i, path);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
}
|
|
|
}
|
|
|
- return ret;
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/* create new dapm mux control */
|
|
|
static int dapm_new_mux(struct snd_soc_dapm_widget *w)
|
|
|
{
|
|
|
struct snd_soc_dapm_context *dapm = w->dapm;
|
|
|
- struct snd_soc_dapm_path *path = NULL;
|
|
|
- struct snd_kcontrol *kcontrol;
|
|
|
- struct snd_card *card = dapm->card->snd_card;
|
|
|
- const char *prefix;
|
|
|
- size_t prefix_len;
|
|
|
+ struct snd_soc_dapm_path *path;
|
|
|
int ret;
|
|
|
- struct snd_soc_dapm_widget_list *wlist;
|
|
|
- int shared, wlistentries;
|
|
|
- size_t wlistsize;
|
|
|
- const char *name;
|
|
|
|
|
|
if (w->num_kcontrols != 1) {
|
|
|
dev_err(dapm->dev,
|
|
@@ -631,65 +679,19 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[0],
|
|
|
- &kcontrol);
|
|
|
- if (kcontrol) {
|
|
|
- wlist = kcontrol->private_data;
|
|
|
- wlistentries = wlist->num_widgets + 1;
|
|
|
- } else {
|
|
|
- wlist = NULL;
|
|
|
- wlistentries = 1;
|
|
|
- }
|
|
|
- wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
|
|
|
- wlistentries * sizeof(struct snd_soc_dapm_widget *),
|
|
|
- wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
|
|
|
- if (wlist == NULL) {
|
|
|
- dev_err(dapm->dev,
|
|
|
- "ASoC: can't allocate widget list for %s\n", w->name);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
- wlist->num_widgets = wlistentries;
|
|
|
- wlist->widgets[wlistentries - 1] = w;
|
|
|
-
|
|
|
- if (!kcontrol) {
|
|
|
- if (dapm->codec)
|
|
|
- prefix = dapm->codec->name_prefix;
|
|
|
- else
|
|
|
- prefix = NULL;
|
|
|
-
|
|
|
- if (shared) {
|
|
|
- name = w->kcontrol_news[0].name;
|
|
|
- prefix_len = 0;
|
|
|
- } else {
|
|
|
- name = w->name;
|
|
|
- if (prefix)
|
|
|
- prefix_len = strlen(prefix) + 1;
|
|
|
- else
|
|
|
- prefix_len = 0;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * The control will get a prefix from the control creation
|
|
|
- * process but we're also using the same prefix for widgets so
|
|
|
- * cut the prefix off the front of the widget name.
|
|
|
- */
|
|
|
- kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist,
|
|
|
- name + prefix_len, prefix);
|
|
|
- ret = snd_ctl_add(card, kcontrol);
|
|
|
- if (ret < 0) {
|
|
|
- dev_err(dapm->dev, "ASoC: failed to add kcontrol %s: %d\n",
|
|
|
- w->name, ret);
|
|
|
- kfree(wlist);
|
|
|
- return ret;
|
|
|
- }
|
|
|
+ path = list_first_entry(&w->sources, struct snd_soc_dapm_path,
|
|
|
+ list_sink);
|
|
|
+ if (!path) {
|
|
|
+ dev_err(dapm->dev, "ASoC: mux %s has no paths\n", w->name);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- kcontrol->private_data = wlist;
|
|
|
-
|
|
|
- w->kcontrols[0] = kcontrol;
|
|
|
+ ret = dapm_create_or_share_mixmux_kcontrol(w, 0, path);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
|
|
|
list_for_each_entry(path, &w->sources, list_sink)
|
|
|
- path->kcontrol = kcontrol;
|
|
|
+ path->kcontrol = w->kcontrols[0];
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -705,14 +707,33 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)
|
|
|
}
|
|
|
|
|
|
/* reset 'walked' bit for each dapm path */
|
|
|
-static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm)
|
|
|
+static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm,
|
|
|
+ struct list_head *sink)
|
|
|
{
|
|
|
struct snd_soc_dapm_path *p;
|
|
|
|
|
|
- list_for_each_entry(p, &dapm->card->paths, list)
|
|
|
- p->walked = 0;
|
|
|
+ list_for_each_entry(p, sink, list_source) {
|
|
|
+ if (p->walked) {
|
|
|
+ p->walked = 0;
|
|
|
+ dapm_clear_walk_output(dapm, &p->sink->sinks);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm,
|
|
|
+ struct list_head *source)
|
|
|
+{
|
|
|
+ struct snd_soc_dapm_path *p;
|
|
|
+
|
|
|
+ list_for_each_entry(p, source, list_sink) {
|
|
|
+ if (p->walked) {
|
|
|
+ p->walked = 0;
|
|
|
+ dapm_clear_walk_input(dapm, &p->source->sources);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/* We implement power down on suspend by checking the power state of
|
|
|
* the ALSA card - when we are suspending the ALSA state for the card
|
|
|
* is set to D3.
|
|
@@ -995,13 +1016,17 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
|
|
|
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
|
|
|
dapm_reset(card);
|
|
|
|
|
|
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
|
paths = is_connected_output_ep(dai->playback_widget, list);
|
|
|
- else
|
|
|
+ dapm_clear_walk_output(&card->dapm,
|
|
|
+ &dai->playback_widget->sinks);
|
|
|
+ } else {
|
|
|
paths = is_connected_input_ep(dai->capture_widget, list);
|
|
|
+ dapm_clear_walk_input(&card->dapm,
|
|
|
+ &dai->capture_widget->sources);
|
|
|
+ }
|
|
|
|
|
|
trace_snd_soc_dapm_connected(paths, stream);
|
|
|
- dapm_clear_walk(&card->dapm);
|
|
|
mutex_unlock(&card->dapm_mutex);
|
|
|
|
|
|
return paths;
|
|
@@ -1104,9 +1129,9 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
|
|
|
DAPM_UPDATE_STAT(w, power_checks);
|
|
|
|
|
|
in = is_connected_input_ep(w, NULL);
|
|
|
- dapm_clear_walk(w->dapm);
|
|
|
+ dapm_clear_walk_input(w->dapm, &w->sources);
|
|
|
out = is_connected_output_ep(w, NULL);
|
|
|
- dapm_clear_walk(w->dapm);
|
|
|
+ dapm_clear_walk_output(w->dapm, &w->sinks);
|
|
|
return out != 0 && in != 0;
|
|
|
}
|
|
|
|
|
@@ -1129,7 +1154,7 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
|
|
|
|
|
|
if (w->active) {
|
|
|
in = is_connected_input_ep(w, NULL);
|
|
|
- dapm_clear_walk(w->dapm);
|
|
|
+ dapm_clear_walk_input(w->dapm, &w->sources);
|
|
|
return in != 0;
|
|
|
} else {
|
|
|
return dapm_generic_check_power(w);
|
|
@@ -1145,7 +1170,7 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
|
|
|
|
|
|
if (w->active) {
|
|
|
out = is_connected_output_ep(w, NULL);
|
|
|
- dapm_clear_walk(w->dapm);
|
|
|
+ dapm_clear_walk_output(w->dapm, &w->sinks);
|
|
|
return out != 0;
|
|
|
} else {
|
|
|
return dapm_generic_check_power(w);
|
|
@@ -1177,8 +1202,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
- dapm_clear_walk(w->dapm);
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1759,9 +1782,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
|
|
|
return -ENOMEM;
|
|
|
|
|
|
in = is_connected_input_ep(w, NULL);
|
|
|
- dapm_clear_walk(w->dapm);
|
|
|
+ dapm_clear_walk_input(w->dapm, &w->sources);
|
|
|
out = is_connected_output_ep(w, NULL);
|
|
|
- dapm_clear_walk(w->dapm);
|
|
|
+ dapm_clear_walk_output(w->dapm, &w->sinks);
|
|
|
|
|
|
ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",
|
|
|
w->name, w->power ? "On" : "Off",
|