Merge remote-tracking branches 'asoc/topic/link-param', 'asoc/topic/max98090', 'asoc...
[sfrench/cifs-2.6.git] / sound / soc / soc-dapm.c
index b6f88202b8c99a41c3e941bb8a8a2603e45985a8..defe0f0082b5e8877d6ae092d53395c6387fbaff 100644 (file)
@@ -473,16 +473,6 @@ struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm);
 
-/**
- * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol
- * @kcontrol: The kcontrol
- */
-struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol)
-{
-       return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol));
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec);
-
 static void dapm_reset(struct snd_soc_card *card)
 {
        struct snd_soc_dapm_widget *w;
@@ -853,6 +843,36 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)
        return 0;
 }
 
+/* create new dapm dai link control */
+static int dapm_new_dai_link(struct snd_soc_dapm_widget *w)
+{
+       int i, ret;
+       struct snd_kcontrol *kcontrol;
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct snd_card *card = dapm->card->snd_card;
+
+       /* create control for links with > 1 config */
+       if (w->num_params <= 1)
+               return 0;
+
+       /* add kcontrol */
+       for (i = 0; i < w->num_kcontrols; i++) {
+               kcontrol = snd_soc_cnew(&w->kcontrol_news[i], w,
+                                       w->name, NULL);
+               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, w->kcontrol_news[i].name, ret);
+                       return ret;
+               }
+               kcontrol->private_data = w;
+               w->kcontrols[i] = kcontrol;
+       }
+
+       return 0;
+}
+
 /* 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.
@@ -1898,6 +1918,9 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
 {
        struct dentry *d;
 
+       if (!parent)
+               return;
+
        dapm->debugfs_dapm = debugfs_create_dir("dapm", parent);
 
        if (!dapm->debugfs_dapm) {
@@ -2719,6 +2742,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
                case snd_soc_dapm_out_drv:
                        dapm_new_pga(w);
                        break;
+               case snd_soc_dapm_dai_link:
+                       dapm_new_dai_link(w);
+                       break;
                default:
                        break;
                }
@@ -3193,7 +3219,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
 {
        struct snd_soc_dapm_path *source_p, *sink_p;
        struct snd_soc_dai *source, *sink;
-       const struct snd_soc_pcm_stream *config = w->params;
+       const struct snd_soc_pcm_stream *config = w->params + w->params_select;
        struct snd_pcm_substream substream;
        struct snd_pcm_hw_params *params = NULL;
        u64 fmt;
@@ -3285,22 +3311,97 @@ out:
        return ret;
 }
 
+static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = w->params_select;
+
+       return 0;
+}
+
+static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+
+       /* Can't change the config when widget is already powered */
+       if (w->power)
+               return -EBUSY;
+
+       if (ucontrol->value.integer.value[0] == w->params_select)
+               return 0;
+
+       if (ucontrol->value.integer.value[0] >= w->num_params)
+               return -EINVAL;
+
+       w->params_select = ucontrol->value.integer.value[0];
+
+       return 0;
+}
+
 int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
                         const struct snd_soc_pcm_stream *params,
+                        unsigned int num_params,
                         struct snd_soc_dapm_widget *source,
                         struct snd_soc_dapm_widget *sink)
 {
        struct snd_soc_dapm_widget template;
        struct snd_soc_dapm_widget *w;
-       size_t len;
        char *link_name;
-       int ret;
-
-       len = strlen(source->name) + strlen(sink->name) + 2;
-       link_name = devm_kzalloc(card->dev, len, GFP_KERNEL);
-       if (!link_name)
+       int ret, count;
+       unsigned long private_value;
+       const char **w_param_text;
+       struct soc_enum w_param_enum[] = {
+               SOC_ENUM_SINGLE(0, 0, 0, NULL),
+       };
+       struct snd_kcontrol_new kcontrol_dai_link[] = {
+               SOC_ENUM_EXT(NULL, w_param_enum[0],
+                            snd_soc_dapm_dai_link_get,
+                            snd_soc_dapm_dai_link_put),
+       };
+       const struct snd_soc_pcm_stream *config = params;
+
+       w_param_text = devm_kcalloc(card->dev, num_params,
+                                       sizeof(char *), GFP_KERNEL);
+       if (!w_param_text)
                return -ENOMEM;
-       snprintf(link_name, len, "%s-%s", source->name, sink->name);
+
+       link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s",
+                                  source->name, sink->name);
+       if (!link_name) {
+               ret = -ENOMEM;
+               goto outfree_w_param;
+       }
+
+       for (count = 0 ; count < num_params; count++) {
+               if (!config->stream_name) {
+                       dev_warn(card->dapm.dev,
+                               "ASoC: anonymous config %d for dai link %s\n",
+                               count, link_name);
+                       w_param_text[count] =
+                               devm_kasprintf(card->dev, GFP_KERNEL,
+                                              "Anonymous Configuration %d",
+                                              count);
+                       if (!w_param_text[count]) {
+                               ret = -ENOMEM;
+                               goto outfree_link_name;
+                       }
+               } else {
+                       w_param_text[count] = devm_kmemdup(card->dev,
+                                               config->stream_name,
+                                               strlen(config->stream_name) + 1,
+                                               GFP_KERNEL);
+                       if (!w_param_text[count]) {
+                               ret = -ENOMEM;
+                               goto outfree_link_name;
+                       }
+               }
+               config++;
+       }
+       w_param_enum[0].items = num_params;
+       w_param_enum[0].texts = w_param_text;
 
        memset(&template, 0, sizeof(template));
        template.reg = SND_SOC_NOPM;
@@ -3309,6 +3410,30 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
        template.event = snd_soc_dai_link_event;
        template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
                SND_SOC_DAPM_PRE_PMD;
+       template.num_kcontrols = 1;
+       /* duplicate w_param_enum on heap so that memory persists */
+       private_value =
+               (unsigned long) devm_kmemdup(card->dev,
+                       (void *)(kcontrol_dai_link[0].private_value),
+                       sizeof(struct soc_enum), GFP_KERNEL);
+       if (!private_value) {
+               dev_err(card->dev, "ASoC: Failed to create control for %s widget\n",
+                       link_name);
+               ret = -ENOMEM;
+               goto outfree_link_name;
+       }
+       kcontrol_dai_link[0].private_value = private_value;
+       /* duplicate kcontrol_dai_link on heap so that memory persists */
+       template.kcontrol_news =
+                               devm_kmemdup(card->dev, &kcontrol_dai_link[0],
+                                       sizeof(struct snd_kcontrol_new),
+                                       GFP_KERNEL);
+       if (!template.kcontrol_news) {
+               dev_err(card->dev, "ASoC: Failed to create control for %s widget\n",
+                       link_name);
+               ret = -ENOMEM;
+               goto outfree_private_value;
+       }
 
        dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name);
 
@@ -3316,15 +3441,32 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
        if (!w) {
                dev_err(card->dev, "ASoC: Failed to create %s widget\n",
                        link_name);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto outfree_kcontrol_news;
        }
 
        w->params = params;
+       w->num_params = num_params;
 
        ret = snd_soc_dapm_add_path(&card->dapm, source, w, NULL, NULL);
        if (ret)
-               return ret;
+               goto outfree_w;
        return snd_soc_dapm_add_path(&card->dapm, w, sink, NULL, NULL);
+
+outfree_w:
+       devm_kfree(card->dev, w);
+outfree_kcontrol_news:
+       devm_kfree(card->dev, (void *)template.kcontrol_news);
+outfree_private_value:
+       devm_kfree(card->dev, (void *)private_value);
+outfree_link_name:
+       devm_kfree(card->dev, link_name);
+outfree_w_param:
+       for (count = 0 ; count < num_params; count++)
+               devm_kfree(card->dev, (void *)w_param_text[count]);
+       devm_kfree(card->dev, w_param_text);
+
+       return ret;
 }
 
 int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,