aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2015-05-01 12:02:43 -0400
committerMark Brown <broonie@kernel.org>2015-05-06 12:31:02 -0400
commitd714f97c5b8c4c5da56b89a7289acb3f12ef7abb (patch)
tree814ea8f11899bff80e966bb64dfc394742d61de1
parent92fa12426741d52b39ec92ad77c9843d3fc2b3d6 (diff)
ASoC: dapm: Add demux support
A demux is conceptually similar to a mux. Where a mux has multiple input and one output and selects one of the inputs to be connected to the output, the demux has one input and multiple outputs and selects one of the outputs to which the input gets connected. This similarity makes it straight forward to support them in DAPM using the existing mux support, we only need to swap sinks and sources when initially setting up the paths. The only slightly tricky part is that there can only be one control per path. Since mixers/muxes are at the sink of a path and a demux is at the source and both types want a control it is not possible to directly connect a demux output to a mixer/mux input. The patch adds some sanity checks to make sure that this does not happen. Drivers who want to model hardware which directly connects a demux output to a mixer/mux input can do this by inserting a dummy widget between the two. E.g.: { "Dummy", "Demux Control", "Demux" }, { "Mixer", "Mixer Control", "Dummy" }, Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--include/sound/soc-dapm.h5
-rw-r--r--sound/soc/soc-dapm.c112
2 files changed, 102 insertions, 15 deletions
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 70216d20e897..96c5e0ec81d1 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -107,6 +107,10 @@ struct device;
107{ .id = snd_soc_dapm_mux, .name = wname, \ 107{ .id = snd_soc_dapm_mux, .name = wname, \
108 SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ 108 SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
109 .kcontrol_news = wcontrols, .num_kcontrols = 1} 109 .kcontrol_news = wcontrols, .num_kcontrols = 1}
110#define SND_SOC_DAPM_DEMUX(wname, wreg, wshift, winvert, wcontrols) \
111{ .id = snd_soc_dapm_demux, .name = wname, \
112 SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
113 .kcontrol_news = wcontrols, .num_kcontrols = 1}
110 114
111/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ 115/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
112#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\ 116#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
@@ -452,6 +456,7 @@ enum snd_soc_dapm_type {
452 snd_soc_dapm_input = 0, /* input pin */ 456 snd_soc_dapm_input = 0, /* input pin */
453 snd_soc_dapm_output, /* output pin */ 457 snd_soc_dapm_output, /* output pin */
454 snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ 458 snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */
459 snd_soc_dapm_demux, /* connects the input to one of multiple outputs */
455 snd_soc_dapm_mixer, /* mixes several analog signals together */ 460 snd_soc_dapm_mixer, /* mixes several analog signals together */
456 snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */ 461 snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */
457 snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */ 462 snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 5c159f4f8097..a2e5f2278caa 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -70,6 +70,7 @@ static int dapm_up_seq[] = {
70 [snd_soc_dapm_aif_out] = 4, 70 [snd_soc_dapm_aif_out] = 4,
71 [snd_soc_dapm_mic] = 5, 71 [snd_soc_dapm_mic] = 5,
72 [snd_soc_dapm_mux] = 6, 72 [snd_soc_dapm_mux] = 6,
73 [snd_soc_dapm_demux] = 6,
73 [snd_soc_dapm_dac] = 7, 74 [snd_soc_dapm_dac] = 7,
74 [snd_soc_dapm_switch] = 8, 75 [snd_soc_dapm_switch] = 8,
75 [snd_soc_dapm_mixer] = 8, 76 [snd_soc_dapm_mixer] = 8,
@@ -100,6 +101,7 @@ static int dapm_down_seq[] = {
100 [snd_soc_dapm_mic] = 7, 101 [snd_soc_dapm_mic] = 7,
101 [snd_soc_dapm_micbias] = 8, 102 [snd_soc_dapm_micbias] = 8,
102 [snd_soc_dapm_mux] = 9, 103 [snd_soc_dapm_mux] = 9,
104 [snd_soc_dapm_demux] = 9,
103 [snd_soc_dapm_aif_in] = 10, 105 [snd_soc_dapm_aif_in] = 10,
104 [snd_soc_dapm_aif_out] = 10, 106 [snd_soc_dapm_aif_out] = 10,
105 [snd_soc_dapm_dai_in] = 10, 107 [snd_soc_dapm_dai_in] = 10,
@@ -356,6 +358,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
356 } 358 }
357 } 359 }
358 break; 360 break;
361 case snd_soc_dapm_demux:
359 case snd_soc_dapm_mux: 362 case snd_soc_dapm_mux:
360 e = (struct soc_enum *)kcontrol->private_value; 363 e = (struct soc_enum *)kcontrol->private_value;
361 364
@@ -639,9 +642,10 @@ out:
639 642
640/* connect mux widget to its interconnecting audio paths */ 643/* connect mux widget to its interconnecting audio paths */
641static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, 644static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
642 struct snd_soc_dapm_path *path, const char *control_name) 645 struct snd_soc_dapm_path *path, const char *control_name,
646 struct snd_soc_dapm_widget *w)
643{ 647{
644 const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0]; 648 const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0];
645 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 649 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
646 unsigned int val, item; 650 unsigned int val, item;
647 int i; 651 int i;
@@ -781,6 +785,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
781 wname_in_long_name = false; 785 wname_in_long_name = false;
782 kcname_in_long_name = true; 786 kcname_in_long_name = true;
783 break; 787 break;
788 case snd_soc_dapm_demux:
784 case snd_soc_dapm_mux: 789 case snd_soc_dapm_mux:
785 wname_in_long_name = true; 790 wname_in_long_name = true;
786 kcname_in_long_name = false; 791 kcname_in_long_name = false;
@@ -886,17 +891,32 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
886{ 891{
887 struct snd_soc_dapm_context *dapm = w->dapm; 892 struct snd_soc_dapm_context *dapm = w->dapm;
888 struct snd_soc_dapm_path *path; 893 struct snd_soc_dapm_path *path;
894 struct list_head *paths;
895 const char *type;
889 int ret; 896 int ret;
890 897
898 switch (w->id) {
899 case snd_soc_dapm_mux:
900 paths = &w->sources;
901 type = "mux";
902 break;
903 case snd_soc_dapm_demux:
904 paths = &w->sinks;
905 type = "demux";
906 break;
907 default:
908 return -EINVAL;
909 }
910
891 if (w->num_kcontrols != 1) { 911 if (w->num_kcontrols != 1) {
892 dev_err(dapm->dev, 912 dev_err(dapm->dev,
893 "ASoC: mux %s has incorrect number of controls\n", 913 "ASoC: %s %s has incorrect number of controls\n", type,
894 w->name); 914 w->name);
895 return -EINVAL; 915 return -EINVAL;
896 } 916 }
897 917
898 if (list_empty(&w->sources)) { 918 if (list_empty(paths)) {
899 dev_err(dapm->dev, "ASoC: mux %s has no paths\n", w->name); 919 dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name);
900 return -EINVAL; 920 return -EINVAL;
901 } 921 }
902 922
@@ -904,9 +924,16 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
904 if (ret < 0) 924 if (ret < 0)
905 return ret; 925 return ret;
906 926
907 list_for_each_entry(path, &w->sources, list_sink) { 927 if (w->id == snd_soc_dapm_mux) {
908 if (path->name) 928 list_for_each_entry(path, &w->sources, list_sink) {
909 dapm_kcontrol_add_path(w->kcontrols[0], path); 929 if (path->name)
930 dapm_kcontrol_add_path(w->kcontrols[0], path);
931 }
932 } else {
933 list_for_each_entry(path, &w->sinks, list_source) {
934 if (path->name)
935 dapm_kcontrol_add_path(w->kcontrols[0], path);
936 }
910 } 937 }
911 938
912 return 0; 939 return 0;
@@ -2414,6 +2441,50 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
2414 } 2441 }
2415} 2442}
2416 2443
2444static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm,
2445 struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink,
2446 const char *control)
2447{
2448 bool dynamic_source = false;
2449 bool dynamic_sink = false;
2450
2451 if (!control)
2452 return 0;
2453
2454 switch (source->id) {
2455 case snd_soc_dapm_demux:
2456 dynamic_source = true;
2457 break;
2458 default:
2459 break;
2460 }
2461
2462 switch (sink->id) {
2463 case snd_soc_dapm_mux:
2464 case snd_soc_dapm_switch:
2465 case snd_soc_dapm_mixer:
2466 case snd_soc_dapm_mixer_named_ctl:
2467 dynamic_sink = true;
2468 break;
2469 default:
2470 break;
2471 }
2472
2473 if (dynamic_source && dynamic_sink) {
2474 dev_err(dapm->dev,
2475 "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n",
2476 source->name, control, sink->name);
2477 return -EINVAL;
2478 } else if (!dynamic_source && !dynamic_sink) {
2479 dev_err(dapm->dev,
2480 "Control not supported for path %s -> [%s] -> %s\n",
2481 source->name, control, sink->name);
2482 return -EINVAL;
2483 }
2484
2485 return 0;
2486}
2487
2417static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, 2488static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
2418 struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, 2489 struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
2419 const char *control, 2490 const char *control,
@@ -2444,6 +2515,10 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
2444 return -EINVAL; 2515 return -EINVAL;
2445 } 2516 }
2446 2517
2518 ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control);
2519 if (ret)
2520 return ret;
2521
2447 path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); 2522 path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
2448 if (!path) 2523 if (!path)
2449 return -ENOMEM; 2524 return -ENOMEM;
@@ -2463,10 +2538,19 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
2463 if (control == NULL) { 2538 if (control == NULL) {
2464 path->connect = 1; 2539 path->connect = 1;
2465 } else { 2540 } else {
2466 /* connect dynamic paths */ 2541 switch (wsource->id) {
2542 case snd_soc_dapm_demux:
2543 ret = dapm_connect_mux(dapm, path, control, wsource);
2544 if (ret)
2545 goto err;
2546 break;
2547 default:
2548 break;
2549 }
2550
2467 switch (wsink->id) { 2551 switch (wsink->id) {
2468 case snd_soc_dapm_mux: 2552 case snd_soc_dapm_mux:
2469 ret = dapm_connect_mux(dapm, path, control); 2553 ret = dapm_connect_mux(dapm, path, control, wsink);
2470 if (ret != 0) 2554 if (ret != 0)
2471 goto err; 2555 goto err;
2472 break; 2556 break;
@@ -2478,11 +2562,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
2478 goto err; 2562 goto err;
2479 break; 2563 break;
2480 default: 2564 default:
2481 dev_err(dapm->dev, 2565 break;
2482 "Control not supported for path %s -> [%s] -> %s\n",
2483 wsource->name, control, wsink->name);
2484 ret = -EINVAL;
2485 goto err;
2486 } 2566 }
2487 } 2567 }
2488 2568
@@ -2815,6 +2895,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
2815 dapm_new_mixer(w); 2895 dapm_new_mixer(w);
2816 break; 2896 break;
2817 case snd_soc_dapm_mux: 2897 case snd_soc_dapm_mux:
2898 case snd_soc_dapm_demux:
2818 dapm_new_mux(w); 2899 dapm_new_mux(w);
2819 break; 2900 break;
2820 case snd_soc_dapm_pga: 2901 case snd_soc_dapm_pga:
@@ -3219,6 +3300,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
3219 w->power_check = dapm_always_on_check_power; 3300 w->power_check = dapm_always_on_check_power;
3220 break; 3301 break;
3221 case snd_soc_dapm_mux: 3302 case snd_soc_dapm_mux:
3303 case snd_soc_dapm_demux:
3222 case snd_soc_dapm_switch: 3304 case snd_soc_dapm_switch:
3223 case snd_soc_dapm_mixer: 3305 case snd_soc_dapm_mixer:
3224 case snd_soc_dapm_mixer_named_ctl: 3306 case snd_soc_dapm_mixer_named_ctl: