summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sound/soc-dai.h4
-rw-r--r--include/sound/soc-dapm.h4
-rw-r--r--sound/soc/soc-core.c11
-rw-r--r--sound/soc/soc-dapm.c135
4 files changed, 146 insertions, 8 deletions
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 2413acc54883..adb07fcd712c 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -17,6 +17,7 @@
#include <linux/list.h>
struct snd_pcm_substream;
+struct snd_soc_dapm_widget;
/*
* DAI hardware audio formats.
@@ -238,6 +239,9 @@ struct snd_soc_dai {
unsigned char pop_wait:1;
unsigned char probed:1;
+ struct snd_soc_dapm_widget *playback_widget;
+ struct snd_soc_dapm_widget *capture_widget;
+
/* DAI DMA data */
void *playback_dma_data;
void *capture_dma_data;
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 91eb8126c00e..e46107fffeb4 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -355,6 +355,9 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget,
int num);
+int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dai *dai);
+int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
/* dapm path setup */
int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm);
@@ -425,6 +428,7 @@ enum snd_soc_dapm_type {
snd_soc_dapm_aif_in, /* audio interface input */
snd_soc_dapm_aif_out, /* audio interface output */
snd_soc_dapm_siggen, /* signal generator */
+ snd_soc_dapm_dai, /* link to DAI structure */
};
/*
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 77d230cba61a..32ca75e20024 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1010,6 +1010,7 @@ static int soc_probe_codec(struct snd_soc_card *card,
{
int ret = 0;
const struct snd_soc_codec_driver *driver = codec->driver;
+ struct snd_soc_dai *dai;
codec->card = card;
codec->dapm.card = card;
@@ -1024,6 +1025,14 @@ static int soc_probe_codec(struct snd_soc_card *card,
snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
driver->num_dapm_widgets);
+ /* Create DAPM widgets for each DAI stream */
+ list_for_each_entry(dai, &dai_list, list) {
+ if (dai->dev != codec->dev)
+ continue;
+
+ snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
+ }
+
codec->dapm.idle_bias_off = driver->idle_bias_off;
if (driver->probe) {
@@ -1500,6 +1509,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
}
}
+ snd_soc_dapm_link_dai_widgets(card);
+
if (card->controls)
snd_soc_add_card_controls(card, card->controls, card->num_controls);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 97915eb711cc..a4707d0fdf3d 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -52,6 +52,7 @@ static int dapm_up_seq[] = {
[snd_soc_dapm_supply] = 1,
[snd_soc_dapm_regulator_supply] = 1,
[snd_soc_dapm_micbias] = 2,
+ [snd_soc_dapm_dai] = 3,
[snd_soc_dapm_aif_in] = 3,
[snd_soc_dapm_aif_out] = 3,
[snd_soc_dapm_mic] = 4,
@@ -86,6 +87,7 @@ static int dapm_down_seq[] = {
[snd_soc_dapm_value_mux] = 9,
[snd_soc_dapm_aif_in] = 10,
[snd_soc_dapm_aif_out] = 10,
+ [snd_soc_dapm_dai] = 10,
[snd_soc_dapm_regulator_supply] = 11,
[snd_soc_dapm_supply] = 11,
[snd_soc_dapm_post] = 12,
@@ -365,6 +367,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_dai:
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_spk:
@@ -522,17 +525,17 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
* for widgets so cut the prefix off
* the front of the widget name.
*/
- snprintf(path->long_name, name_len, "%s %s",
- w->name + prefix_len,
+ 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(path->long_name, name_len, "%s",
- w->kcontrol_news[i].name);
+ snprintf((char *)path->long_name, name_len,
+ "%s", w->kcontrol_news[i].name);
break;
}
- path->long_name[name_len - 1] = '\0';
+ ((char *)path->long_name)[name_len - 1] = '\0';
path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],
wlist, path->long_name,
@@ -566,7 +569,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
struct snd_soc_dapm_widget_list *wlist;
int shared, wlistentries;
size_t wlistsize;
- char *name;
+ const char *name;
if (w->num_kcontrols != 1) {
dev_err(dapm->dev,
@@ -702,6 +705,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
switch (widget->id) {
case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_dai:
if (widget->active) {
widget->outputs = snd_soc_dapm_suspend_check(widget);
return widget->outputs;
@@ -773,6 +777,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
switch (widget->id) {
case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_dai:
if (widget->active) {
widget->inputs = snd_soc_dapm_suspend_check(widget);
return widget->inputs;
@@ -892,6 +897,13 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
return out != 0 && in != 0;
}
+static int dapm_dai_check_power(struct snd_soc_dapm_widget *w)
+{
+ DAPM_UPDATE_STAT(w, power_checks);
+
+ return w->active;
+}
+
/* Check to see if an ADC has power */
static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
{
@@ -2049,6 +2061,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_dai:
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
@@ -2732,10 +2745,10 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
return NULL;
}
if (dapm->codec && dapm->codec->name_prefix)
- snprintf(w->name, name_len, "%s %s",
+ snprintf((char *)w->name, name_len, "%s %s",
dapm->codec->name_prefix, widget->name);
else
- snprintf(w->name, name_len, "%s", widget->name);
+ snprintf((char *)w->name, name_len, "%s", widget->name);
switch (w->id) {
case snd_soc_dapm_switch:
@@ -2771,6 +2784,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_regulator_supply:
w->power_check = dapm_supply_check_power;
break;
+ case snd_soc_dapm_dai:
+ w->power_check = dapm_dai_check_power;
+ break;
default:
w->power_check = dapm_always_on_check_power;
break;
@@ -2822,6 +2838,109 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
+int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_dapm_widget template;
+ struct snd_soc_dapm_widget *w;
+
+ WARN_ON(dapm->dev != dai->dev);
+
+ memset(&template, 0, sizeof(template));
+ template.reg = SND_SOC_NOPM;
+
+ if (dai->driver->playback.stream_name) {
+ template.id = snd_soc_dapm_dai;
+ template.name = dai->driver->playback.stream_name;
+ template.sname = dai->driver->playback.stream_name;
+
+ dev_dbg(dai->dev, "adding %s widget\n",
+ template.name);
+
+ w = snd_soc_dapm_new_control(dapm, &template);
+ if (!w) {
+ dev_err(dapm->dev, "Failed to create %s widget\n",
+ dai->driver->playback.stream_name);
+ }
+
+ w->priv = dai;
+ dai->playback_widget = w;
+ }
+
+ if (dai->driver->capture.stream_name) {
+ template.id = snd_soc_dapm_dai;
+ template.name = dai->driver->capture.stream_name;
+ template.sname = dai->driver->capture.stream_name;
+
+ dev_dbg(dai->dev, "adding %s widget\n",
+ template.name);
+
+ w = snd_soc_dapm_new_control(dapm, &template);
+ if (!w) {
+ dev_err(dapm->dev, "Failed to create %s widget\n",
+ dai->driver->capture.stream_name);
+ }
+
+ w->priv = dai;
+ dai->capture_widget = w;
+ }
+
+ return 0;
+}
+
+int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
+{
+ struct snd_soc_dapm_widget *dai_w, *w;
+ struct snd_soc_dai *dai;
+ struct snd_soc_dapm_route r;
+
+ memset(&r, 0, sizeof(r));
+
+ /* For each DAI widget... */
+ list_for_each_entry(dai_w, &card->widgets, list) {
+ if (dai_w->id != snd_soc_dapm_dai)
+ continue;
+
+ dai = dai_w->priv;
+
+ /* ...find all widgets with the same stream and link them */
+ list_for_each_entry(w, &card->widgets, list) {
+ if (w->dapm != dai_w->dapm)
+ continue;
+
+ if (w->id == snd_soc_dapm_dai)
+ continue;
+
+ if (!w->sname)
+ continue;
+
+ if (dai->driver->playback.stream_name &&
+ strstr(w->sname,
+ dai->driver->playback.stream_name)) {
+ r.source = dai->playback_widget->name;
+ r.sink = w->name;
+ dev_dbg(dai->dev, "%s -> %s\n",
+ r.source, r.sink);
+
+ snd_soc_dapm_add_route(w->dapm, &r);
+ }
+
+ if (dai->driver->capture.stream_name &&
+ strstr(w->sname,
+ dai->driver->capture.stream_name)) {
+ r.source = w->name;
+ r.sink = dai->capture_widget->name;
+ dev_dbg(dai->dev, "%s -> %s\n",
+ r.source, r.sink);
+
+ snd_soc_dapm_add_route(w->dapm, &r);
+ }
+ }
+ }
+
+ return 0;
+}
+
static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
int stream, struct snd_soc_dai *dai,
int event)