aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-dapm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r--sound/soc/soc-dapm.c182
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}
474EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm); 474EXPORT_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 */
480struct 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}
484EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec);
485
486static void dapm_reset(struct snd_soc_card *card) 476static 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 */
847static 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
3314static 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
3324static 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
3288int snd_soc_dapm_new_pcm(struct snd_soc_card *card, 3344int 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
3456outfree_w:
3457 devm_kfree(card->dev, w);
3458outfree_kcontrol_news:
3459 devm_kfree(card->dev, (void *)template.kcontrol_news);
3460outfree_private_value:
3461 devm_kfree(card->dev, (void *)private_value);
3462outfree_link_name:
3463 devm_kfree(card->dev, link_name);
3464outfree_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
3330int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, 3472int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,