Skip to content

Commit

Permalink
ASoC: dapm: Add demux support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Lars-Peter Clausen authored and Mark Brown committed May 6, 2015
1 parent 92fa124 commit d714f97
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 15 deletions.
5 changes: 5 additions & 0 deletions include/sound/soc-dapm.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ struct device;
{ .id = snd_soc_dapm_mux, .name = wname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
.kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_DEMUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_demux, .name = wname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
.kcontrol_news = wcontrols, .num_kcontrols = 1}

/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
Expand Down Expand Up @@ -452,6 +456,7 @@ enum snd_soc_dapm_type {
snd_soc_dapm_input = 0, /* input pin */
snd_soc_dapm_output, /* output pin */
snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */
snd_soc_dapm_demux, /* connects the input to one of multiple outputs */
snd_soc_dapm_mixer, /* mixes several analog signals together */
snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */
snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */
Expand Down
112 changes: 97 additions & 15 deletions sound/soc/soc-dapm.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ static int dapm_up_seq[] = {
[snd_soc_dapm_aif_out] = 4,
[snd_soc_dapm_mic] = 5,
[snd_soc_dapm_mux] = 6,
[snd_soc_dapm_demux] = 6,
[snd_soc_dapm_dac] = 7,
[snd_soc_dapm_switch] = 8,
[snd_soc_dapm_mixer] = 8,
Expand Down Expand Up @@ -100,6 +101,7 @@ static int dapm_down_seq[] = {
[snd_soc_dapm_mic] = 7,
[snd_soc_dapm_micbias] = 8,
[snd_soc_dapm_mux] = 9,
[snd_soc_dapm_demux] = 9,
[snd_soc_dapm_aif_in] = 10,
[snd_soc_dapm_aif_out] = 10,
[snd_soc_dapm_dai_in] = 10,
Expand Down Expand Up @@ -356,6 +358,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
}
}
break;
case snd_soc_dapm_demux:
case snd_soc_dapm_mux:
e = (struct soc_enum *)kcontrol->private_value;

Expand Down Expand Up @@ -639,9 +642,10 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,

/* connect mux widget to its interconnecting audio paths */
static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_path *path, const char *control_name)
struct snd_soc_dapm_path *path, const char *control_name,
struct snd_soc_dapm_widget *w)
{
const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0];
const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, item;
int i;
Expand Down Expand Up @@ -781,6 +785,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
wname_in_long_name = false;
kcname_in_long_name = true;
break;
case snd_soc_dapm_demux:
case snd_soc_dapm_mux:
wname_in_long_name = true;
kcname_in_long_name = false;
Expand Down Expand Up @@ -886,27 +891,49 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_dapm_path *path;
struct list_head *paths;
const char *type;
int ret;

switch (w->id) {
case snd_soc_dapm_mux:
paths = &w->sources;
type = "mux";
break;
case snd_soc_dapm_demux:
paths = &w->sinks;
type = "demux";
break;
default:
return -EINVAL;
}

if (w->num_kcontrols != 1) {
dev_err(dapm->dev,
"ASoC: mux %s has incorrect number of controls\n",
"ASoC: %s %s has incorrect number of controls\n", type,
w->name);
return -EINVAL;
}

if (list_empty(&w->sources)) {
dev_err(dapm->dev, "ASoC: mux %s has no paths\n", w->name);
if (list_empty(paths)) {
dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name);
return -EINVAL;
}

ret = dapm_create_or_share_mixmux_kcontrol(w, 0);
if (ret < 0)
return ret;

list_for_each_entry(path, &w->sources, list_sink) {
if (path->name)
dapm_kcontrol_add_path(w->kcontrols[0], path);
if (w->id == snd_soc_dapm_mux) {
list_for_each_entry(path, &w->sources, list_sink) {
if (path->name)
dapm_kcontrol_add_path(w->kcontrols[0], path);
}
} else {
list_for_each_entry(path, &w->sinks, list_source) {
if (path->name)
dapm_kcontrol_add_path(w->kcontrols[0], path);
}
}

return 0;
Expand Down Expand Up @@ -2414,6 +2441,50 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
}
}

static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink,
const char *control)
{
bool dynamic_source = false;
bool dynamic_sink = false;

if (!control)
return 0;

switch (source->id) {
case snd_soc_dapm_demux:
dynamic_source = true;
break;
default:
break;
}

switch (sink->id) {
case snd_soc_dapm_mux:
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
dynamic_sink = true;
break;
default:
break;
}

if (dynamic_source && dynamic_sink) {
dev_err(dapm->dev,
"Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n",
source->name, control, sink->name);
return -EINVAL;
} else if (!dynamic_source && !dynamic_sink) {
dev_err(dapm->dev,
"Control not supported for path %s -> [%s] -> %s\n",
source->name, control, sink->name);
return -EINVAL;
}

return 0;
}

static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
const char *control,
Expand Down Expand Up @@ -2444,6 +2515,10 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
return -EINVAL;
}

ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control);
if (ret)
return ret;

path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
if (!path)
return -ENOMEM;
Expand All @@ -2463,10 +2538,19 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
if (control == NULL) {
path->connect = 1;
} else {
/* connect dynamic paths */
switch (wsource->id) {
case snd_soc_dapm_demux:
ret = dapm_connect_mux(dapm, path, control, wsource);
if (ret)
goto err;
break;
default:
break;
}

switch (wsink->id) {
case snd_soc_dapm_mux:
ret = dapm_connect_mux(dapm, path, control);
ret = dapm_connect_mux(dapm, path, control, wsink);
if (ret != 0)
goto err;
break;
Expand All @@ -2478,11 +2562,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
goto err;
break;
default:
dev_err(dapm->dev,
"Control not supported for path %s -> [%s] -> %s\n",
wsource->name, control, wsink->name);
ret = -EINVAL;
goto err;
break;
}
}

Expand Down Expand Up @@ -2815,6 +2895,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
dapm_new_mixer(w);
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_demux:
dapm_new_mux(w);
break;
case snd_soc_dapm_pga:
Expand Down Expand Up @@ -3219,6 +3300,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
w->power_check = dapm_always_on_check_power;
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_demux:
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
Expand Down

0 comments on commit d714f97

Please sign in to comment.