aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikesh Oswal <nikesh@opensource.wolfsonmicro.com>2015-02-02 12:06:44 -0500
committerMark Brown <broonie@kernel.org>2015-03-17 18:54:54 -0400
commitc66150824b8a809a502fd833fa9b18082cd89a39 (patch)
tree312992852d07ce536b600cdf9da91a067c31d1c9
parentc517d838eb7d07bbe9507871fab3931deccff539 (diff)
ASoC: dapm: add code to configure dai link parameters
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>
-rw-r--r--include/sound/soc-dapm.h3
-rw-r--r--include/sound/soc.h1
-rw-r--r--sound/soc/soc-core.c6
-rw-r--r--sound/soc/soc-dapm.c164
4 files changed, 166 insertions, 8 deletions
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 8d7416e46861..eda881402dda 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -378,6 +378,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
378void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card); 378void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card);
379int snd_soc_dapm_new_pcm(struct snd_soc_card *card, 379int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
380 const struct snd_soc_pcm_stream *params, 380 const struct snd_soc_pcm_stream *params,
381 unsigned int num_params,
381 struct snd_soc_dapm_widget *source, 382 struct snd_soc_dapm_widget *source,
382 struct snd_soc_dapm_widget *sink); 383 struct snd_soc_dapm_widget *sink);
383 384
@@ -531,6 +532,8 @@ struct snd_soc_dapm_widget {
531 void *priv; /* widget specific data */ 532 void *priv; /* widget specific data */
532 struct regulator *regulator; /* attached regulator */ 533 struct regulator *regulator; /* attached regulator */
533 const struct snd_soc_pcm_stream *params; /* params for dai links */ 534 const struct snd_soc_pcm_stream *params; /* params for dai links */
535 unsigned int num_params; /* number of params for dai links */
536 unsigned int params_select; /* currently selected param for dai link */
534 537
535 /* dapm control */ 538 /* dapm control */
536 int reg; /* negative reg = no direct dapm */ 539 int reg; /* negative reg = no direct dapm */
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 0d1ade195628..4636a058372b 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -941,6 +941,7 @@ struct snd_soc_dai_link {
941 int be_id; /* optional ID for machine driver BE identification */ 941 int be_id; /* optional ID for machine driver BE identification */
942 942
943 const struct snd_soc_pcm_stream *params; 943 const struct snd_soc_pcm_stream *params;
944 unsigned int num_params;
944 945
945 unsigned int dai_fmt; /* format to set on init */ 946 unsigned int dai_fmt; /* format to set on init */
946 947
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 30579ca5bacb..700ac2ffe696 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1245,7 +1245,8 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
1245 capture_w = cpu_dai->capture_widget; 1245 capture_w = cpu_dai->capture_widget;
1246 if (play_w && capture_w) { 1246 if (play_w && capture_w) {
1247 ret = snd_soc_dapm_new_pcm(card, dai_link->params, 1247 ret = snd_soc_dapm_new_pcm(card, dai_link->params,
1248 capture_w, play_w); 1248 dai_link->num_params, capture_w,
1249 play_w);
1249 if (ret != 0) { 1250 if (ret != 0) {
1250 dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", 1251 dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
1251 play_w->name, capture_w->name, ret); 1252 play_w->name, capture_w->name, ret);
@@ -1257,7 +1258,8 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
1257 capture_w = codec_dai->capture_widget; 1258 capture_w = codec_dai->capture_widget;
1258 if (play_w && capture_w) { 1259 if (play_w && capture_w) {
1259 ret = snd_soc_dapm_new_pcm(card, dai_link->params, 1260 ret = snd_soc_dapm_new_pcm(card, dai_link->params,
1260 capture_w, play_w); 1261 dai_link->num_params, capture_w,
1262 play_w);
1261 if (ret != 0) { 1263 if (ret != 0) {
1262 dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", 1264 dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
1263 play_w->name, capture_w->name, ret); 1265 play_w->name, capture_w->name, ret);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index b6f88202b8c9..6828b4ed5447 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -853,6 +853,36 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)
853 return 0; 853 return 0;
854} 854}
855 855
856/* create new dapm dai link control */
857static int dapm_new_dai_link(struct snd_soc_dapm_widget *w)
858{
859 int i, ret;
860 struct snd_kcontrol *kcontrol;
861 struct snd_soc_dapm_context *dapm = w->dapm;
862 struct snd_card *card = dapm->card->snd_card;
863
864 /* create control for links with > 1 config */
865 if (w->num_params <= 1)
866 return 0;
867
868 /* add kcontrol */
869 for (i = 0; i < w->num_kcontrols; i++) {
870 kcontrol = snd_soc_cnew(&w->kcontrol_news[i], w,
871 w->name, NULL);
872 ret = snd_ctl_add(card, kcontrol);
873 if (ret < 0) {
874 dev_err(dapm->dev,
875 "ASoC: failed to add widget %s dapm kcontrol %s: %d\n",
876 w->name, w->kcontrol_news[i].name, ret);
877 return ret;
878 }
879 kcontrol->private_data = w;
880 w->kcontrols[i] = kcontrol;
881 }
882
883 return 0;
884}
885
856/* We implement power down on suspend by checking the power state of 886/* We implement power down on suspend by checking the power state of
857 * the ALSA card - when we are suspending the ALSA state for the card 887 * the ALSA card - when we are suspending the ALSA state for the card
858 * is set to D3. 888 * is set to D3.
@@ -2719,6 +2749,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
2719 case snd_soc_dapm_out_drv: 2749 case snd_soc_dapm_out_drv:
2720 dapm_new_pga(w); 2750 dapm_new_pga(w);
2721 break; 2751 break;
2752 case snd_soc_dapm_dai_link:
2753 dapm_new_dai_link(w);
2754 break;
2722 default: 2755 default:
2723 break; 2756 break;
2724 } 2757 }
@@ -3193,7 +3226,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
3193{ 3226{
3194 struct snd_soc_dapm_path *source_p, *sink_p; 3227 struct snd_soc_dapm_path *source_p, *sink_p;
3195 struct snd_soc_dai *source, *sink; 3228 struct snd_soc_dai *source, *sink;
3196 const struct snd_soc_pcm_stream *config = w->params; 3229 const struct snd_soc_pcm_stream *config = w->params + w->params_select;
3197 struct snd_pcm_substream substream; 3230 struct snd_pcm_substream substream;
3198 struct snd_pcm_hw_params *params = NULL; 3231 struct snd_pcm_hw_params *params = NULL;
3199 u64 fmt; 3232 u64 fmt;
@@ -3285,8 +3318,39 @@ out:
3285 return ret; 3318 return ret;
3286} 3319}
3287 3320
3321static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol,
3322 struct snd_ctl_elem_value *ucontrol)
3323{
3324 struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
3325
3326 ucontrol->value.integer.value[0] = w->params_select;
3327
3328 return 0;
3329}
3330
3331static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol,
3332 struct snd_ctl_elem_value *ucontrol)
3333{
3334 struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
3335
3336 /* Can't change the config when widget is already powered */
3337 if (w->power)
3338 return -EBUSY;
3339
3340 if (ucontrol->value.integer.value[0] == w->params_select)
3341 return 0;
3342
3343 if (ucontrol->value.integer.value[0] >= w->num_params)
3344 return -EINVAL;
3345
3346 w->params_select = ucontrol->value.integer.value[0];
3347
3348 return 0;
3349}
3350
3288int snd_soc_dapm_new_pcm(struct snd_soc_card *card, 3351int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
3289 const struct snd_soc_pcm_stream *params, 3352 const struct snd_soc_pcm_stream *params,
3353 unsigned int num_params,
3290 struct snd_soc_dapm_widget *source, 3354 struct snd_soc_dapm_widget *source,
3291 struct snd_soc_dapm_widget *sink) 3355 struct snd_soc_dapm_widget *sink)
3292{ 3356{
@@ -3294,14 +3358,61 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
3294 struct snd_soc_dapm_widget *w; 3358 struct snd_soc_dapm_widget *w;
3295 size_t len; 3359 size_t len;
3296 char *link_name; 3360 char *link_name;
3297 int ret; 3361 int ret, count;
3362 unsigned long private_value;
3363 const char **w_param_text;
3364 struct soc_enum w_param_enum[] = {
3365 SOC_ENUM_SINGLE(0, 0, 0, NULL),
3366 };
3367 struct snd_kcontrol_new kcontrol_dai_link[] = {
3368 SOC_ENUM_EXT(NULL, w_param_enum[0],
3369 snd_soc_dapm_dai_link_get,
3370 snd_soc_dapm_dai_link_put),
3371 };
3372 const struct snd_soc_pcm_stream *config = params;
3373
3374 w_param_text = devm_kcalloc(card->dev, num_params,
3375 sizeof(char *), GFP_KERNEL);
3376 if (!w_param_text)
3377 return -ENOMEM;
3298 3378
3299 len = strlen(source->name) + strlen(sink->name) + 2; 3379 len = strlen(source->name) + strlen(sink->name) + 2;
3300 link_name = devm_kzalloc(card->dev, len, GFP_KERNEL); 3380 link_name = devm_kzalloc(card->dev, len, GFP_KERNEL);
3301 if (!link_name) 3381 if (!link_name) {
3302 return -ENOMEM; 3382 ret = -ENOMEM;
3383 goto outfree_w_param;
3384 }
3303 snprintf(link_name, len, "%s-%s", source->name, sink->name); 3385 snprintf(link_name, len, "%s-%s", source->name, sink->name);
3304 3386
3387 for (count = 0 ; count < num_params; count++) {
3388 if (!config->stream_name) {
3389 dev_warn(card->dapm.dev,
3390 "ASoC: anonymous config %d for dai link %s\n",
3391 count, link_name);
3392 len = strlen("Anonymous Configuration ") + 3;
3393 w_param_text[count] =
3394 devm_kzalloc(card->dev, len, GFP_KERNEL);
3395 if (!w_param_text[count]) {
3396 ret = -ENOMEM;
3397 goto outfree_link_name;
3398 }
3399 snprintf(w_param_text[count], len,
3400 "Anonymous Configuration %d", count);
3401 } else {
3402 w_param_text[count] = devm_kmemdup(card->dev,
3403 config->stream_name,
3404 strlen(config->stream_name) + 1,
3405 GFP_KERNEL);
3406 if (!w_param_text[count]) {
3407 ret = -ENOMEM;
3408 goto outfree_link_name;
3409 }
3410 }
3411 config++;
3412 }
3413 w_param_enum[0].items = num_params;
3414 w_param_enum[0].texts = w_param_text;
3415
3305 memset(&template, 0, sizeof(template)); 3416 memset(&template, 0, sizeof(template));
3306 template.reg = SND_SOC_NOPM; 3417 template.reg = SND_SOC_NOPM;
3307 template.id = snd_soc_dapm_dai_link; 3418 template.id = snd_soc_dapm_dai_link;
@@ -3309,6 +3420,30 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
3309 template.event = snd_soc_dai_link_event; 3420 template.event = snd_soc_dai_link_event;
3310 template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | 3421 template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
3311 SND_SOC_DAPM_PRE_PMD; 3422 SND_SOC_DAPM_PRE_PMD;
3423 template.num_kcontrols = 1;
3424 /* duplicate w_param_enum on heap so that memory persists */
3425 private_value =
3426 (unsigned long) devm_kmemdup(card->dev,
3427 (void *)(kcontrol_dai_link[0].private_value),
3428 sizeof(struct soc_enum), GFP_KERNEL);
3429 if (!private_value) {
3430 dev_err(card->dev, "ASoC: Failed to create control for %s widget\n",
3431 link_name);
3432 ret = -ENOMEM;
3433 goto outfree_link_name;
3434 }
3435 kcontrol_dai_link[0].private_value = private_value;
3436 /* duplicate kcontrol_dai_link on heap so that memory persists */
3437 template.kcontrol_news =
3438 devm_kmemdup(card->dev, &kcontrol_dai_link[0],
3439 sizeof(struct snd_kcontrol_new),
3440 GFP_KERNEL);
3441 if (!template.kcontrol_news) {
3442 dev_err(card->dev, "ASoC: Failed to create control for %s widget\n",
3443 link_name);
3444 ret = -ENOMEM;
3445 goto outfree_private_value;
3446 }
3312 3447
3313 dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); 3448 dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name);
3314 3449
@@ -3316,15 +3451,32 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
3316 if (!w) { 3451 if (!w) {
3317 dev_err(card->dev, "ASoC: Failed to create %s widget\n", 3452 dev_err(card->dev, "ASoC: Failed to create %s widget\n",
3318 link_name); 3453 link_name);
3319 return -ENOMEM; 3454 ret = -ENOMEM;
3455 goto outfree_kcontrol_news;
3320 } 3456 }
3321 3457
3322 w->params = params; 3458 w->params = params;
3459 w->num_params = num_params;
3323 3460
3324 ret = snd_soc_dapm_add_path(&card->dapm, source, w, NULL, NULL); 3461 ret = snd_soc_dapm_add_path(&card->dapm, source, w, NULL, NULL);
3325 if (ret) 3462 if (ret)
3326 return ret; 3463 goto outfree_w;
3327 return snd_soc_dapm_add_path(&card->dapm, w, sink, NULL, NULL); 3464 return snd_soc_dapm_add_path(&card->dapm, w, sink, NULL, NULL);
3465
3466outfree_w:
3467 devm_kfree(card->dev, w);
3468outfree_kcontrol_news:
3469 devm_kfree(card->dev, (void *)template.kcontrol_news);
3470outfree_private_value:
3471 devm_kfree(card->dev, (void *)private_value);
3472outfree_link_name:
3473 devm_kfree(card->dev, link_name);
3474outfree_w_param:
3475 for (count = 0 ; count < num_params; count++)
3476 devm_kfree(card->dev, (void *)w_param_text[count]);
3477 devm_kfree(card->dev, w_param_text);
3478
3479 return ret;
3328} 3480}
3329 3481
3330int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, 3482int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,