ASoC: SOF: control: override volume info callback
authorJaska Uimonen <jaska.uimonen@linux.intel.com>
Wed, 11 Nov 2020 17:31:05 +0000 (19:31 +0200)
committerMark Brown <broonie@kernel.org>
Fri, 20 Nov 2020 13:49:05 +0000 (13:49 +0000)
ASoC dapm controls currently don't support more than 2 channels. This is
a problem for SOF-based devices where individual volume control cannot
be provided on the 4 DMIC input path.

If we want to provide controls for more than 2 channels, this patch
suggests a simple solution based on an override of the info callback.
For example, in the case with 4 channel DMIC PGAs, a sof_info callback
would be used. Mono and stereo cases will keep using the existing dapm
info callback.

A longer-term solution would be to remove the limits to 2 channels in
ASoC/DAPM/topology. This is a topic Intel is currently looking into,
e.g. by removing the use of 'reg' and 'rreg' fields and use arrays
instead. Such changes will be rather intrusive and touch multiple codec
and platform drivers. Removing restrictions is the right thing to do,
but this will need to be done in steps with lots of validation.

Signed-off-by: Jaska Uimonen <jaska.uimonen@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/20201111173105.1927466-1-kai.vehmanen@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/control.c
sound/soc/sof/sof-audio.h
sound/soc/sof/topology.c

index 056c86ad5a4791cca689bfb84bdd90cb26013b99..a5dd728c580a9374ef10b1a754900ccd39380dc8 100644 (file)
@@ -114,6 +114,28 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+int snd_sof_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_sof_control *scontrol = sm->dobj.private;
+       unsigned int channels = scontrol->num_channels;
+       int platform_max;
+
+       if (!sm->platform_max)
+               sm->platform_max = sm->max;
+       platform_max = sm->platform_max;
+
+       if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       else
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+       uinfo->count = channels;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = platform_max - sm->min;
+       return 0;
+}
+
 int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
                       struct snd_ctl_elem_value *ucontrol)
 {
index 9f645a2e5a6ccf21e497cd2cedebacdc960d7650..ddcfd46617f8ea5a0a69db34aac83eb667578de3 100644 (file)
@@ -124,6 +124,8 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
                       struct snd_ctl_elem_value *ucontrol);
 int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
                       struct snd_ctl_elem_value *ucontrol);
+int snd_sof_volume_info(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_info *ucontrol);
 int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
                       struct snd_ctl_elem_value *ucontrol);
 int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
index 430d274722aea8a68c40e19387c7b4d6aec9e204..b6b32a7a91f876b01db656167eedb25cf96551aa 100644 (file)
@@ -1041,6 +1041,15 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
                goto out;
        }
 
+       /*
+        * If control has more than 2 channels we need to override the info. This is because even if
+        * ASoC layer has defined topology's max channel count to SND_SOC_TPLG_MAX_CHAN = 8, the
+        * pre-defined dapm control types (and related functions) creating the actual control
+        * restrict the channels only to mono or stereo.
+        */
+       if (le32_to_cpu(mc->num_channels) > 2)
+               kc->info = snd_sof_volume_info;
+
        /* init the volume get/put data */
        scontrol->size = struct_size(scontrol->control_data, chanv,
                                     le32_to_cpu(mc->num_channels));