diff options
-rw-r--r-- | include/sound/soc-dapm.h | 3 | ||||
-rw-r--r-- | include/sound/soc.h | 1 | ||||
-rw-r--r-- | sound/soc/soc-core.c | 6 | ||||
-rw-r--r-- | sound/soc/soc-dapm.c | 164 |
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); | |||
378 | void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card); | 378 | void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card); |
379 | int snd_soc_dapm_new_pcm(struct snd_soc_card *card, | 379 | int 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 */ | ||
857 | static 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 | ||
3321 | static 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 | |||
3331 | static 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 | |||
3288 | int snd_soc_dapm_new_pcm(struct snd_soc_card *card, | 3351 | int 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 | |||
3466 | outfree_w: | ||
3467 | devm_kfree(card->dev, w); | ||
3468 | outfree_kcontrol_news: | ||
3469 | devm_kfree(card->dev, (void *)template.kcontrol_news); | ||
3470 | outfree_private_value: | ||
3471 | devm_kfree(card->dev, (void *)private_value); | ||
3472 | outfree_link_name: | ||
3473 | devm_kfree(card->dev, link_name); | ||
3474 | outfree_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 | ||
3330 | int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, | 3482 | int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, |