diff options
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 182 |
1 files changed, 162 insertions, 20 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b6f88202b8c9..defe0f0082b5 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -473,16 +473,6 @@ struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( | |||
473 | } | 473 | } |
474 | EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm); | 474 | EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm); |
475 | 475 | ||
476 | /** | ||
477 | * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol | ||
478 | * @kcontrol: The kcontrol | ||
479 | */ | ||
480 | struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) | ||
481 | { | ||
482 | return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol)); | ||
483 | } | ||
484 | EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); | ||
485 | |||
486 | static void dapm_reset(struct snd_soc_card *card) | 476 | static void dapm_reset(struct snd_soc_card *card) |
487 | { | 477 | { |
488 | struct snd_soc_dapm_widget *w; | 478 | struct snd_soc_dapm_widget *w; |
@@ -853,6 +843,36 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w) | |||
853 | return 0; | 843 | return 0; |
854 | } | 844 | } |
855 | 845 | ||
846 | /* create new dapm dai link control */ | ||
847 | static int dapm_new_dai_link(struct snd_soc_dapm_widget *w) | ||
848 | { | ||
849 | int i, ret; | ||
850 | struct snd_kcontrol *kcontrol; | ||
851 | struct snd_soc_dapm_context *dapm = w->dapm; | ||
852 | struct snd_card *card = dapm->card->snd_card; | ||
853 | |||
854 | /* create control for links with > 1 config */ | ||
855 | if (w->num_params <= 1) | ||
856 | return 0; | ||
857 | |||
858 | /* add kcontrol */ | ||
859 | for (i = 0; i < w->num_kcontrols; i++) { | ||
860 | kcontrol = snd_soc_cnew(&w->kcontrol_news[i], w, | ||
861 | w->name, NULL); | ||
862 | ret = snd_ctl_add(card, kcontrol); | ||
863 | if (ret < 0) { | ||
864 | dev_err(dapm->dev, | ||
865 | "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", | ||
866 | w->name, w->kcontrol_news[i].name, ret); | ||
867 | return ret; | ||
868 | } | ||
869 | kcontrol->private_data = w; | ||
870 | w->kcontrols[i] = kcontrol; | ||
871 | } | ||
872 | |||
873 | return 0; | ||
874 | } | ||
875 | |||
856 | /* We implement power down on suspend by checking the power state of | 876 | /* 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 | 877 | * the ALSA card - when we are suspending the ALSA state for the card |
858 | * is set to D3. | 878 | * is set to D3. |
@@ -1898,6 +1918,9 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, | |||
1898 | { | 1918 | { |
1899 | struct dentry *d; | 1919 | struct dentry *d; |
1900 | 1920 | ||
1921 | if (!parent) | ||
1922 | return; | ||
1923 | |||
1901 | dapm->debugfs_dapm = debugfs_create_dir("dapm", parent); | 1924 | dapm->debugfs_dapm = debugfs_create_dir("dapm", parent); |
1902 | 1925 | ||
1903 | if (!dapm->debugfs_dapm) { | 1926 | if (!dapm->debugfs_dapm) { |
@@ -2719,6 +2742,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) | |||
2719 | case snd_soc_dapm_out_drv: | 2742 | case snd_soc_dapm_out_drv: |
2720 | dapm_new_pga(w); | 2743 | dapm_new_pga(w); |
2721 | break; | 2744 | break; |
2745 | case snd_soc_dapm_dai_link: | ||
2746 | dapm_new_dai_link(w); | ||
2747 | break; | ||
2722 | default: | 2748 | default: |
2723 | break; | 2749 | break; |
2724 | } | 2750 | } |
@@ -3193,7 +3219,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, | |||
3193 | { | 3219 | { |
3194 | struct snd_soc_dapm_path *source_p, *sink_p; | 3220 | struct snd_soc_dapm_path *source_p, *sink_p; |
3195 | struct snd_soc_dai *source, *sink; | 3221 | struct snd_soc_dai *source, *sink; |
3196 | const struct snd_soc_pcm_stream *config = w->params; | 3222 | const struct snd_soc_pcm_stream *config = w->params + w->params_select; |
3197 | struct snd_pcm_substream substream; | 3223 | struct snd_pcm_substream substream; |
3198 | struct snd_pcm_hw_params *params = NULL; | 3224 | struct snd_pcm_hw_params *params = NULL; |
3199 | u64 fmt; | 3225 | u64 fmt; |
@@ -3285,22 +3311,97 @@ out: | |||
3285 | return ret; | 3311 | return ret; |
3286 | } | 3312 | } |
3287 | 3313 | ||
3314 | static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol, | ||
3315 | struct snd_ctl_elem_value *ucontrol) | ||
3316 | { | ||
3317 | struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); | ||
3318 | |||
3319 | ucontrol->value.integer.value[0] = w->params_select; | ||
3320 | |||
3321 | return 0; | ||
3322 | } | ||
3323 | |||
3324 | static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, | ||
3325 | struct snd_ctl_elem_value *ucontrol) | ||
3326 | { | ||
3327 | struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); | ||
3328 | |||
3329 | /* Can't change the config when widget is already powered */ | ||
3330 | if (w->power) | ||
3331 | return -EBUSY; | ||
3332 | |||
3333 | if (ucontrol->value.integer.value[0] == w->params_select) | ||
3334 | return 0; | ||
3335 | |||
3336 | if (ucontrol->value.integer.value[0] >= w->num_params) | ||
3337 | return -EINVAL; | ||
3338 | |||
3339 | w->params_select = ucontrol->value.integer.value[0]; | ||
3340 | |||
3341 | return 0; | ||
3342 | } | ||
3343 | |||
3288 | int snd_soc_dapm_new_pcm(struct snd_soc_card *card, | 3344 | int snd_soc_dapm_new_pcm(struct snd_soc_card *card, |
3289 | const struct snd_soc_pcm_stream *params, | 3345 | const struct snd_soc_pcm_stream *params, |
3346 | unsigned int num_params, | ||
3290 | struct snd_soc_dapm_widget *source, | 3347 | struct snd_soc_dapm_widget *source, |
3291 | struct snd_soc_dapm_widget *sink) | 3348 | struct snd_soc_dapm_widget *sink) |
3292 | { | 3349 | { |
3293 | struct snd_soc_dapm_widget template; | 3350 | struct snd_soc_dapm_widget template; |
3294 | struct snd_soc_dapm_widget *w; | 3351 | struct snd_soc_dapm_widget *w; |
3295 | size_t len; | ||
3296 | char *link_name; | 3352 | char *link_name; |
3297 | int ret; | 3353 | int ret, count; |
3298 | 3354 | unsigned long private_value; | |
3299 | len = strlen(source->name) + strlen(sink->name) + 2; | 3355 | const char **w_param_text; |
3300 | link_name = devm_kzalloc(card->dev, len, GFP_KERNEL); | 3356 | struct soc_enum w_param_enum[] = { |
3301 | if (!link_name) | 3357 | SOC_ENUM_SINGLE(0, 0, 0, NULL), |
3358 | }; | ||
3359 | struct snd_kcontrol_new kcontrol_dai_link[] = { | ||
3360 | SOC_ENUM_EXT(NULL, w_param_enum[0], | ||
3361 | snd_soc_dapm_dai_link_get, | ||
3362 | snd_soc_dapm_dai_link_put), | ||
3363 | }; | ||
3364 | const struct snd_soc_pcm_stream *config = params; | ||
3365 | |||
3366 | w_param_text = devm_kcalloc(card->dev, num_params, | ||
3367 | sizeof(char *), GFP_KERNEL); | ||
3368 | if (!w_param_text) | ||
3302 | return -ENOMEM; | 3369 | return -ENOMEM; |
3303 | snprintf(link_name, len, "%s-%s", source->name, sink->name); | 3370 | |
3371 | link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", | ||
3372 | source->name, sink->name); | ||
3373 | if (!link_name) { | ||
3374 | ret = -ENOMEM; | ||
3375 | goto outfree_w_param; | ||
3376 | } | ||
3377 | |||
3378 | for (count = 0 ; count < num_params; count++) { | ||
3379 | if (!config->stream_name) { | ||
3380 | dev_warn(card->dapm.dev, | ||
3381 | "ASoC: anonymous config %d for dai link %s\n", | ||
3382 | count, link_name); | ||
3383 | w_param_text[count] = | ||
3384 | devm_kasprintf(card->dev, GFP_KERNEL, | ||
3385 | "Anonymous Configuration %d", | ||
3386 | count); | ||
3387 | if (!w_param_text[count]) { | ||
3388 | ret = -ENOMEM; | ||
3389 | goto outfree_link_name; | ||
3390 | } | ||
3391 | } else { | ||
3392 | w_param_text[count] = devm_kmemdup(card->dev, | ||
3393 | config->stream_name, | ||
3394 | strlen(config->stream_name) + 1, | ||
3395 | GFP_KERNEL); | ||
3396 | if (!w_param_text[count]) { | ||
3397 | ret = -ENOMEM; | ||
3398 | goto outfree_link_name; | ||
3399 | } | ||
3400 | } | ||
3401 | config++; | ||
3402 | } | ||
3403 | w_param_enum[0].items = num_params; | ||
3404 | w_param_enum[0].texts = w_param_text; | ||
3304 | 3405 | ||
3305 | memset(&template, 0, sizeof(template)); | 3406 | memset(&template, 0, sizeof(template)); |
3306 | template.reg = SND_SOC_NOPM; | 3407 | template.reg = SND_SOC_NOPM; |
@@ -3309,6 +3410,30 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, | |||
3309 | template.event = snd_soc_dai_link_event; | 3410 | template.event = snd_soc_dai_link_event; |
3310 | template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | 3411 | template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | |
3311 | SND_SOC_DAPM_PRE_PMD; | 3412 | SND_SOC_DAPM_PRE_PMD; |
3413 | template.num_kcontrols = 1; | ||
3414 | /* duplicate w_param_enum on heap so that memory persists */ | ||
3415 | private_value = | ||
3416 | (unsigned long) devm_kmemdup(card->dev, | ||
3417 | (void *)(kcontrol_dai_link[0].private_value), | ||
3418 | sizeof(struct soc_enum), GFP_KERNEL); | ||
3419 | if (!private_value) { | ||
3420 | dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", | ||
3421 | link_name); | ||
3422 | ret = -ENOMEM; | ||
3423 | goto outfree_link_name; | ||
3424 | } | ||
3425 | kcontrol_dai_link[0].private_value = private_value; | ||
3426 | /* duplicate kcontrol_dai_link on heap so that memory persists */ | ||
3427 | template.kcontrol_news = | ||
3428 | devm_kmemdup(card->dev, &kcontrol_dai_link[0], | ||
3429 | sizeof(struct snd_kcontrol_new), | ||
3430 | GFP_KERNEL); | ||
3431 | if (!template.kcontrol_news) { | ||
3432 | dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", | ||
3433 | link_name); | ||
3434 | ret = -ENOMEM; | ||
3435 | goto outfree_private_value; | ||
3436 | } | ||
3312 | 3437 | ||
3313 | dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); | 3438 | dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); |
3314 | 3439 | ||
@@ -3316,15 +3441,32 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, | |||
3316 | if (!w) { | 3441 | if (!w) { |
3317 | dev_err(card->dev, "ASoC: Failed to create %s widget\n", | 3442 | dev_err(card->dev, "ASoC: Failed to create %s widget\n", |
3318 | link_name); | 3443 | link_name); |
3319 | return -ENOMEM; | 3444 | ret = -ENOMEM; |
3445 | goto outfree_kcontrol_news; | ||
3320 | } | 3446 | } |
3321 | 3447 | ||
3322 | w->params = params; | 3448 | w->params = params; |
3449 | w->num_params = num_params; | ||
3323 | 3450 | ||
3324 | ret = snd_soc_dapm_add_path(&card->dapm, source, w, NULL, NULL); | 3451 | ret = snd_soc_dapm_add_path(&card->dapm, source, w, NULL, NULL); |
3325 | if (ret) | 3452 | if (ret) |
3326 | return ret; | 3453 | goto outfree_w; |
3327 | return snd_soc_dapm_add_path(&card->dapm, w, sink, NULL, NULL); | 3454 | return snd_soc_dapm_add_path(&card->dapm, w, sink, NULL, NULL); |
3455 | |||
3456 | outfree_w: | ||
3457 | devm_kfree(card->dev, w); | ||
3458 | outfree_kcontrol_news: | ||
3459 | devm_kfree(card->dev, (void *)template.kcontrol_news); | ||
3460 | outfree_private_value: | ||
3461 | devm_kfree(card->dev, (void *)private_value); | ||
3462 | outfree_link_name: | ||
3463 | devm_kfree(card->dev, link_name); | ||
3464 | outfree_w_param: | ||
3465 | for (count = 0 ; count < num_params; count++) | ||
3466 | devm_kfree(card->dev, (void *)w_param_text[count]); | ||
3467 | devm_kfree(card->dev, w_param_text); | ||
3468 | |||
3469 | return ret; | ||
3328 | } | 3470 | } |
3329 | 3471 | ||
3330 | int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, | 3472 | int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, |