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, |
