[Sound-open-firmware] Loading coefficients for Audio IP

Pierre-Louis Bossart pierre-louis.bossart at linux.intel.com
Wed Dec 19 17:26:36 CET 2018


Hi Daniel,

> What is the standard ALSA way of loading tables of coefficients for
> various audio IPs?
>
> We have IPs like PDM or ASRC which need large table of coefficients
> depending on configuration. This tables can reach up to 120K in size.
>
> Creating this kind of tables as arrays of integers in kernel doesn't seem
> to be a common used solution and we will end up with large files that
> contains only numbers.
>
> Another approach that we are thinking of is to create and compile this
> tables in userspace as raw binary files and then loading them in kernel
> using the firmware interface.

I think there are two separate parts in your question (not trying to 
pontificate but split variables).

1. how do we download binary tables that are related to hardware 
interfaces or firmware modules?

In that case the "coefficients" don't depend on use cases but are set in 
stone either at build time or when the device is flashed. They never 
vary once the boot starts. You absolutely do not want these coefficients 
in the kernel, and I can think of two ways of dealing with this case

1.a. request_firmware(). If you are using the topology framework, there 
may be a way of adding tokens so that the file names for the coefficient 
doesn't have to be a constant string in the kernel.

1.b adding binary data as part of the topology file. I vaguely recall 
discussing this with Liam, not sure if it's feasible.

1.c a new custom interface to add modules to the firmware. Liam worked 
on that part to add new code, but it could be used for coefficients 
since the symbol address resolution would be identical.


2. how do we download binary data related to processing/algorithms, 
which can vary depending on use cases and possibly user interaction?

That part is more complicated and I don't have a turn-key solution, I 
only started looking into this last week and you'll find below all my 
raw notes (all based on publicly-available information).

The main issue here is that ALSA BYTE controls are limited to 512 bytes, 
which doesn't cut it for most applications. There were two successive 
changes, one to add "extended controls" and a second to use TLVs. I 
personally missed that second step and wasn't aware that they were used 
by Intel in the Skylake driver. After looking in more details at all the 
online information I am really not hot on this direction. There are a 
couple of issues with TLVs

2.a The API was abused big time. The "T" in TLV should have been a 
standard one, but it turns out everyone just used whatever they wanted.

2.b. the API isn't a good fit for the type of data being handled. the 
"L" in TLV is supposed to indicate the length of the binary data, but 
the API can also pass a length information. I find this really 
confusing. Such binary data should be handled in an 'atomic' way, all or 
none.

2.c and fundamentally I don't see what TLVs bring over regular extended 
controls. If the driver ignores the length passed by the application and 
only uses the encoded length, then it'd just be equivalent to use a 
custom encoding and an extended control.

In the notes from the last two or three ALSA miniconfs there are 
mentions of looking into other directions such as hwdep:. I must admit I 
am not familiar with this interface so there's some learning needed on 
my side. Patrick Lai from Qualcomm also mentioned a number of times the 
need to support 'calibration' files and I wonder what the need really is.

I am also wondering if there is a need for reads of large binary data. I 
can see cases where you could extract state information, but this could 
be abused if people used it for probes or data logging, which is really 
streaming and can be handled e.g. with the compressed API. And you would 
probably need a 'freeze/snapshot' command in the first place otherwise 
the state could be inconsistent between the initial part of the read and 
the last.

At any rate, for SOF I don't feel we have to comply with any legacy, but 
rather we should try and help the ALSA community do the right thing with 
a solution that is agreed upon and will be acceptable for a number of 
years. Restarting with controversial concepts doesn't seem like a good 
direction to me.

Most likely something that will happen in 2019 though. Happy Holidays y'all.

Regards

-Pierre


Raw notes on ALSA binary control support:


2014-05-02
d98812082c87
ASoC: add SND_SOC_BYTES_EXT

     we need _EXT version for SND_SOC_BYTES so that DSPs can use this to 
pass data
     for DSP modules

     Signed-off-by: Vinod Koul <vinod.koul at intel.com>

2014-07-15
7523a271682fc
     ASoC: core: add a helper for extended byte controls using TLV

     ALSA supports arbitrary length TLVs for each kcontrol that can be used
     to pass metadata about the control (e.g. volumes, enum 
information). The
     same transport mechanism is now used for arbitrary length data by
     defining a new helper.

     Signed-off-by: Omair Mohammed Abdullah <omair.m.abdullah at intel.com>
     Signed-off-by: Vinod Koul <vinod.koul at intel.com>

2016-11-23
50a4f98d3452
    ASoC: core: mark SND_SOC_BYTES_EXT as deprecated

     Since we have SND_SOC_BYTES_TLV control to lets devices have
     larger size data sent, we do not need SND_SOC_BYTES_EXT with 512
     byte limitation so mark it deprecated

     Signed-off-by: Vinod Koul <vinod.koul at intel.com>

[PLB] I have no idea why TLVs were marked as a better solution

2015-09-15
[alsa-devel] [PATCH] Revert "ASoC: core: mark SND_SOC_BYTES_EXT as 
deprecated"

SND_SOC_BYTES_TLV brings confusions to user land because it doesn't follow
to a protocol of ctl and tlv operation. At least, this macro and related
kernel APIs include two misunderstandings:
  - 'struct snd_ctl_elem_info.count' can also represent the length of TLV
    packet paylaod, snd_soc_bytes_info_ext() performs in this way.
  - 'struct snd_ctl_tlv.tlv' can include arbitrary data regardless of TLV
    packet structure, snd_soc_bytes_tlv_callback() performs in this way.

In a policy of kernel land development, it's quite worse to break protocols
for applications. Therefore, developers are discouraged to use these kernel
APIs.

In the first place, SND_SOC_BYTES_TLV was added to satisfy a request of
developers who need to add control elements which transfer data larger than
the size which 'struct snd_ctl_elem_value' can represent; e.g. over 512
bytes. However, as long as the size is less than the size; e.g. 512 bytes,
SND_SOC_BYTES_EXT is still available. Although there is actually the
limitation of maximum data size, it's better to use this API for stable
application interface till better alternative ways are implemented in
future.

For these reasons, this commit reverts the previous commit which lead
developers to the worse behaviour.

Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>

[PLB] Sakamoto-san was very vocal on this and provided even more details at

https://patchwork.kernel.org/patch/9304649/

While this thread is difficult to follow (turn off social media and get 
a large mug of coffee), it does go over all the known TLV problems. One 
of the main issues with TLV seems to be that if the application passes a 
large buffer of data, only the parts that can be handled at the ASoC 
level will be handled. The application has no means to know how much was 
written nor how much it gets back on a read. Maybe this could be 
improved by first querying the control size and asking the application 
to respect that max size. But to some extent letting the application 
muck with the length isn't quite right in the first place, the data 
should be used as is in an atomic way.


In latest code:

git grep SND_SOC_BYTES_EXT
include/sound/soc.h: * SND_SOC_BYTES_EXT is deprecated, please USE 
SND_SOC_BYTES_TLV instead
include/sound/soc.h:#define SND_SOC_BYTES_EXT(xname, xcount, 
xhandler_get, xhandler_put) \
sound/soc/codecs/da7218.c:      SND_SOC_BYTES_EXT("Sidetone BiQuad 
Coefficients",
sound/soc/codecs/da7218.c:      SND_SOC_BYTES_EXT("BiQuad Coefficients",
sound/soc/codecs/nau8810.c:     SND_SOC_BYTES_EXT("EQ Parameters", 10,
sound/soc/codecs/nau8822.c:     SND_SOC_BYTES_EXT("EQ Parameters", 10,
sound/soc/codecs/nau8825.c:     SND_SOC_BYTES_EXT("BIQ Coefficients", 20,
sound/soc/codecs/wm5102.c:SND_SOC_BYTES_EXT("Output Compensation 
Coefficient", 2,
sound/soc/intel/haswell/sst-haswell-pcm.c: SND_SOC_BYTES_EXT("Waves Set 
Param", WAVES_PARAM_COUNT,

git grep SND_SOC_BYTES_TLV
include/sound/soc.h: * SND_SOC_BYTES_EXT is deprecated, please USE 
SND_SOC_BYTES_TLV instead
include/sound/soc.h:#define SND_SOC_BYTES_TLV(xname, xcount, 
xhandler_get, xhandler_put) \

[PLB]: There is no clear mention in the kernel code that TLVs are 
actually used

git grep SKL_CONTROL_TYPE_BYTE_TLV
include/uapi/sound/skl-tplg-interface.h:#define 
SKL_CONTROL_TYPE_BYTE_TLV       0x100
sound/soc/intel/skylake/skl-topology.c: {SKL_CONTROL_TYPE_BYTE_TLV, 
skl_tplg_tlv_control_get,

static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol,
             unsigned int __user *data, unsigned int size)
{
     struct soc_bytes_ext *sb =
             (struct soc_bytes_ext *)kcontrol->private_value;
     struct skl_algo_data *bc = (struct skl_algo_data *)sb->dobj.private;
     struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
     struct skl_module_cfg *mconfig = w->priv;
     struct skl *skl = get_skl_ctx(w->dapm->dev);

     if (w->power)
         skl_get_module_params(skl->skl_sst, (u32 *)bc->params,
                       bc->size, bc->param_id, mconfig);

     /* decrement size for TLV header */
     size -= 2 * sizeof(u32);

     /* check size as we don't want to send kernel data */
     if (size > bc->max)
         size = bc->max;

     if (bc->params) {
         if (copy_to_user(data, &bc->param_id, sizeof(u32)))
             return -EFAULT;
         if (copy_to_user(data + 1, &size, sizeof(u32)))
             return -EFAULT;
         if (copy_to_user(data + 2, bc->params, size))
             return -EFAULT;
     }

     return 0;
}

static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
             const unsigned int __user *data, unsigned int size)
{
     struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
     struct skl_module_cfg *mconfig = w->priv;
     struct soc_bytes_ext *sb =
             (struct soc_bytes_ext *)kcontrol->private_value;
     struct skl_algo_data *ac = (struct skl_algo_data *)sb->dobj.private;
     struct skl *skl = get_skl_ctx(w->dapm->dev);

     if (ac->params) {
         if (size > ac->max)
             return -EINVAL;

         ac->size = size;
         /*
          * if the param_is is of type Vendor, firmware expects actual
          * parameter id and size from the control.
          */
         if (ac->param_id == SKL_PARAM_VENDOR_ID) {
             if (copy_from_user(ac->params, data, size))
                 return -EFAULT;
         } else {
             if (copy_from_user(ac->params,
                        data + 2, size))
                 return -EFAULT;
         }

         if (w->power)
             return skl_set_module_params(skl->skl_sst,
                         (u32 *)ac->params, ac->size,
                         ac->param_id, mconfig);
     }

     return 0;
}

[PLB] So Skylake does appear to use an implicit TLV, but the TLV 
information is not propagated as is to firmware. Custom methods are used 
to pass the data in chunks.

https://www.alsa-project.org/main/index.php/Miniconf_2017
TLV API for binary controls
Picking up the discussion from last year.
Problem: Can't figure out what kind of TLV control it is
It's a hack to use TLV for binary controls, probably worth reconsidering.
Only users are Cirrus and Intel. Only for controls with more than 512 
bytes for Cirrus.
Tidy up existing one? Add a flag saying that it's binary nonsense - 
disagreement.
Add TLV headers in the data? Probably breaks Intel who have headers that 
look like they mean something.
One goal for using TLV was to get data through existing API clients.
Takashi would prefer to add a new API for this.
Indirect pointers sound complicated.
Maybe use file based interface (ioctl takes a fd).
Sounds like new ioctls, Charles volunteers to implement.


https://www.alsa-project.org/main/index.php/Miniconf_2018
Byte controls

     Already covered, Sakamoto-san’s new elem API probably can have no 
limit if desired
     Vinod: it’s easier for usespace to handle a single userspace interface
     Charles: Cirrus has few large controls
     Having to extend every single UCM implementation is a pain
     tiwai: controls need to be readable at any time
     Calibration loading
         QC currently has a calibration mechanism parallel to standard 
ALSA one. QC code available on codeaurora. Want to re-do this with 
upstream constructs.
         2 methods aware of upstream currently
             Pass calibration through byte control from user space
             Go through request firmware
         Discussion converging towards hwdep
         Vinod: fudge hwdep in alsa-lib
         Charles: happy to investigate what the hwdep solution would 
look like from cirrus PoV
         Patrick: need to provide different calibration data depending 
on sample rate.
         UCM: Problems with Android but otherwise good







More information about the Sound-open-firmware mailing list