ASoC: dapm: add code to configure dai link parameters
authorNikesh Oswal <nikesh@opensource.wolfsonmicro.com>
Mon, 2 Feb 2015 17:06:44 +0000 (17:06 +0000)
committerMark Brown <broonie@kernel.org>
Tue, 17 Mar 2015 22:54:54 +0000 (22:54 +0000)
dai-link params for codec-codec links were fixed. The fixed
link between codec and another chip which may be another codec,
baseband, bluetooth codec etc may require run time configuaration
changes. This change provides an optional alsa control to select
one of the params from a list of params.

Signed-off-by: Nikesh Oswal <nikesh@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
include/sound/soc-dapm.h
include/sound/soc.h
sound/soc/soc-core.c
sound/soc/soc-dapm.c

index 8d7416e46861744d990f4c0dbb2d1f72fdd9407b..eda881402dda4156d5c2d3dddd05823dfee42c27 100644 (file)
@@ -378,6 +378,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
 void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card);
 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);
 
@@ -531,6 +532,8 @@ struct snd_soc_dapm_widget {
        void *priv;                             /* widget specific data */
        struct regulator *regulator;            /* attached regulator */
        const struct snd_soc_pcm_stream *params; /* params for dai links */
+       unsigned int num_params; /* number of params for dai links */
+       unsigned int params_select; /* currently selected param for dai link */
 
        /* dapm control */
        int reg;                                /* negative reg = no direct dapm */
index 0d1ade19562857c7b65157636ac37b2f0222e202..4636a058372bc8fcc93619477b03d77c8faa3b35 100644 (file)
@@ -941,6 +941,7 @@ struct snd_soc_dai_link {
        int be_id;      /* optional ID for machine driver BE identification */
 
        const struct snd_soc_pcm_stream *params;
+       unsigned int num_params;
 
        unsigned int dai_fmt;           /* format to set on init */
 
index 30579ca5bacb985c7dfa9b9ad30f58cee45d6e1a..700ac2ffe69685a4826d85ef44fe9d14d3c7fba0 100644 (file)
@@ -1245,7 +1245,8 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
        capture_w = cpu_dai->capture_widget;
        if (play_w && capture_w) {
                ret = snd_soc_dapm_new_pcm(card, dai_link->params,
-                                          capture_w, play_w);
+                                          dai_link->num_params, capture_w,
+                                          play_w);
                if (ret != 0) {
                        dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
                                play_w->name, capture_w->name, ret);
@@ -1257,7 +1258,8 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
        capture_w = codec_dai->capture_widget;
        if (play_w && capture_w) {
                ret = snd_soc_dapm_new_pcm(card, dai_link->params,
-                                          capture_w, play_w);
+                                          dai_link->num_params, capture_w,
+                                          play_w);
                if (ret != 0) {
                        dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
                                play_w->name, capture_w->name, ret);
index b6f88202b8c99a41c3e941bb8a8a2603e45985a8..6828b4ed5447998b6d144ff6ccaf87fe515b2a3b 100644 (file)
@@ -853,6 +853,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.
@@ -2719,6 +2749,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 +3226,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,8 +3318,39 @@ 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)
 {
@@ -3294,14 +3358,61 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
        struct snd_soc_dapm_widget *w;
        size_t len;
        char *link_name;
-       int ret;
+       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;
 
        len = strlen(source->name) + strlen(sink->name) + 2;
        link_name = devm_kzalloc(card->dev, len, GFP_KERNEL);
-       if (!link_name)
-               return -ENOMEM;
+       if (!link_name) {
+               ret = -ENOMEM;
+               goto outfree_w_param;
+       }
        snprintf(link_name, len, "%s-%s", source->name, sink->name);
 
+       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);
+                       len = strlen("Anonymous Configuration ") + 3;
+                       w_param_text[count] =
+                               devm_kzalloc(card->dev, len, GFP_KERNEL);
+                       if (!w_param_text[count]) {
+                               ret = -ENOMEM;
+                               goto outfree_link_name;
+                       }
+                       snprintf(w_param_text[count], len,
+                               "Anonymous Configuration %d", count);
+               } 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;
        template.id = snd_soc_dapm_dai_link;
@@ -3309,6 +3420,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 +3451,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,