Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ALSA/ASoC: hda: Address format selection limitations and ambiguity #4539

Closed

Conversation

crojewsk-intel
Copy link

This PR is provided for testing purposes so that no regression occurs on sof-driver side and mirrors thread present on upstream. Once tested, PR will be closed.

Patchset aims to address format selection restrictions present currently in the HDAudio library. Formats which we are concerned about are 20 and 24 valid bits per sample within 32 bit depth container. One may identify them as S20_LE and S24_LE except that those, according to comments found in include/uapi/sound/asound.h, are for LSB-aligned scenarios. HDAudio streams expect MSB-aligned data, no matter if we are speaking of HOST (SDxFMT) or LINK (PPLCxFMT) side - chapter 4.5.1 of the public HDAudio specification. In short, S20_LE and S24_LE are invalid options.

Right now, given the implementation of snd_hdac_query_supported_pcm() within sound/hda/hdac_device.c, even if a codec responds with: "I support all possible formats specified within HDAudio specification", there will be no option to open a 20/32 or 24/32 stream. The kernel will force the stream to be opened using the highest available bit depth.

After discussing subject initially with Jaroslav and Takashi, suggestion was made to utilize 'subformat' option to address the problem. The eye-opening discussion begun much earlier though, in 2019 [1].

Paired with PRs for alsa-utils [2] and alsa-lib [3].


Flow of changes:

The very first patch adds MSBITS subformat options to allow for granular
20/32, 24/32 and 32/32 format selection. The next three make sure
subformat is actually honored during runtime. Most of that code is based
on format-related API.

Follow up is upgrade to the hda stream-format interface - several functions are added to make the granular format selection simple in the HDAudio world. Core of the implementation is based on the existing snd_hdac_calc_stream_format(). The next ten patches are straightforward switch from one interface to another with cleanup of now-unsed function as a finishing touch.

Last but not least - the avs-driver, on which the problem analyzed and debugged, is updated to no longer acknowledge S24_LE as a valid format option.

Changes in v1:

  • fixed UBSAN due to missing snd_pcm_subformat_names[] entries for new subformats
  • as HDMI stream capabilities are assigned on PCM open, patch 16/17 has been updated to ignore such codecs for now. A separate patchset will take care of this case
  • params_bps() reworded to snd_pcm_hw_params_bps()
  • fixed compilation issues in sof-driver, patch 13/17

[1]: https://lore.kernel.org/alsa-devel/[email protected]/
[2]: alsa-project/alsa-utils#228
[3]: alsa-project/alsa-lib#342

Improve granularity of format selection for S32/U32 formats by adding
constants representing 20, 24 and 32 most significant bits.

To make it easy for drivers to utilize those constants, introduce
snd_pcm_subformat_width() and snd_pcm_hw_params_bps(). While the former
is self-explanatory, the latter returns the bit-per-sample value based
on format, subformat and msbits characteristics of the provided
hw_params.

Signed-off-by: Cezary Rojewski <[email protected]>
Substream value is currently hardcoded to SNDRV_PCM_SUBFORMAT_STD.
Update the constraint procedure so that subformat selection is not
ignored.

Signed-off-by: Cezary Rojewski <[email protected]>
Update mechanism for querying supported PCMs to allow for granular
format selection when container size is 32 bits. Currently always the
highest bit depth is selected, regardless of how many actual formats
codec in question supports.

Signed-off-by: Cezary Rojewski <[email protected]>
Subformat options are ignored when setting up hardware parameters and
assigning PCM stream capabilities. Account for them to allow for
granular format selection.

Signed-off-by: Cezary Rojewski <[email protected]>
Introduce a set of functions that facilite SDxFMT-related calculations
in atomic manner:

snd_hdac_format_normalize() - format converter. S20_LE, S24_LE and their
unsigned and BE friends are invalid from HDAudio perspective but still
can be specified as function argument due to compatibility reasons.

snd_hdac_stream_format_bps() - obtain just the bits-per-sample value.
Does not ignore subformat and msbits parameters.

snd_hdac_stream_format() and snd_hdac_spdif_stream_format() - obtain the
SDxFMT value given the audio format parameters. The former is stripped
away of spdif-related information. Useful for users that do not care
about them.

Signed-off-by: Cezary Rojewski <[email protected]>
To provide option for selecting different bit-per-sample than just the
maximum one, use the new format calculation mechanism.

Signed-off-by: Cezary Rojewski <[email protected]>
To provide option for selecting different bit-per-sample than just the
maximum one, use the new format calculation mechanism.

Signed-off-by: Cezary Rojewski <[email protected]>
To provide option for selecting different bit-per-sample than just the
maximum one, use the new format calculation mechanism.

Signed-off-by: Cezary Rojewski <[email protected]>
To provide option for selecting different bit-per-sample than just the
maximum one, use the new format calculation mechanism.

While at it, complete PCM stream initialization with subformat options.

Signed-off-by: Cezary Rojewski <[email protected]>
To provide option for selecting different bit-per-sample than just the
maximum one, use the new format calculation mechanism.

Signed-off-by: Cezary Rojewski <[email protected]>
To provide option for selecting different bit-per-sample than just the
maximum one, use the new format calculation mechanism.

Signed-off-by: Cezary Rojewski <[email protected]>
To provide option for selecting different bit-per-sample than just the
maximum one, use the new format calculation mechanism.

Signed-off-by: Cezary Rojewski <[email protected]>
To provide option for selecting different bit-per-sample than just the
maximum one, use the new format calculation mechanism.

Signed-off-by: Cezary Rojewski <[email protected]>
To provide option for selecting different bit-per-sample than just the
maximum one, use the new format calculation mechanism.

Signed-off-by: Cezary Rojewski <[email protected]>
There are no users of the function.

Signed-off-by: Cezary Rojewski <[email protected]>
Eliminate all occurrences of S24_LE within the HDAudio related pcm code,
both HOST and LINK side. Replace those with MSBITS subformats to allow
for granular selection when S32_LE is the format of choice.

Signed-off-by: Cezary Rojewski <[email protected]>
To not expose more than in fact is supported by the codec, update CPU
DAI initialization procedure to rely on codec capabilities instead of
hardcoding them. This includes subformat which is currently ignored.

As capabilities for HDMI streams are initialized on PCM open, leave it
as is for now.

Signed-off-by: Cezary Rojewski <[email protected]>
format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
params_format(params),
params_physical_width(params),
0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there will be a minor conflict with 255f5af, but easy enough to resolve.

@crojewsk-intel
Copy link
Author

I see some timeouts in suspend/resume scenarios and even a panic in check-kmod-load-unload-after-playback case on GML but I fail to find a connection between my patchset and the negative results.

@plbossart
Copy link
Member

Copy link

@RanderWang RanderWang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also notice the mismatch of S24_LE and HDA spec and we do some effort to deal with it in SOF FW. I am glad to remove S24_LE from pcm capability and will make life easier for FW developer.

Copy link
Collaborator

@ujfalusi ujfalusi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@crojewsk-intel, can you explain why the existing msbits support is not good enough when paired with S32_LE?
S32_LE + msbits=24 == 24 bits audio in 32bit
S32_LE + msbits=20 == 20 bits audio in 32bit
S32_LE + msbits=0/32 == 32 bits audio

@@ -2634,8 +2636,7 @@ static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
if (err < 0)
return err;

err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT,
PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD));
err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, hw->subformats);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

snd_pcm_hw_constraint_mask64() ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack.

@@ -2614,6 +2614,8 @@ static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
int err;
unsigned int mask = 0;

/* All PCMs support at least the default STD subformat. */
hw->subformats |= PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no definition of SNDRV_PCM_SUBFORMAT_STD that I can find, but it looks to me that it is there to not fail when setting the mask?
SNDRV_PCM_SUBFORMAT_STD does not cover any supported subformat, it is just BIT(0) and to keep snd_pcm_hw_constraint_mask() not to fail few lines down?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

STD is the default and expected to be always supported by any device. So, when adding new flags I had to OR it here to avoid breaking audio for most of the world.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the upstream discussion, the SNDRV_PCM_SUBFORMAT_STD tells that the whole physical width is used?
But how that maps with S24_LE for example?

I would expect that drivers would set explicit subformats if they care and they might say that I'm not supporting STD and here you override that statement.

if (!hw->subformats)
	hw->subformats = PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD);

might be what should be done here?

sound/hda/hdac_device.c Show resolved Hide resolved
@@ -848,8 +849,9 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
*ratesp = rates;
}

if (formatsp || bpsp) {
if (formatsp || subformatsp || bpsp) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commit title is a bit misleading as this patch will not make the code to honor subformat, but it will also querry, set it up based on the hardware.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honoring a variable from snd_hdac_query_supported_pcm() point of view is: let it be queryable just like format and bsp are.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me 'honoring' means that based on the subformat do the right thing, take subformat into account.
"Extend supported format query with subformat support", perhaps better?

sound/hda/hdac_device.c Show resolved Hide resolved
*
* Return: The format bitset or zero if invalid.
*/
unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bps,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'spdif' in function name implies that it is for SPDIF format type, but it is not..

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to how things are right now. The goal for the split is different - most users call stream-format calculation routine with '0' (ignore) as last parameter. Having two variants - one for those that do not care about spdif bits and one for those that do care - makes things clear.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is to be used in place of snd_hdac_calc_stream_format()? It is doing the same thing at the end.

snd_hdac_calc_stream_format(params_rate(params), params_channels(params), params_format(params), maxbps, 0);
snd_hdac_spdif_stream_format(params_channels(params), snd_pcm_hw_params_bps(params), params_rate(params), 0);

ctls);
bps = snd_hdac_stream_format_bps(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps);

format_val = snd_hdac_spdif_stream_format(runtime->channels, bps, runtime->rate, ctls);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the commit message trying to tell. Is it valid to select smaller bps than what required for the format?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the case even now - set msbits/sigbits to 24 and select format=S16_LE.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand how this achieves the goal set by the commit message. It is switching to a new set of function calls to get the format_val, right?
No change in functionality, or am I missing something?

format = snd_hdac_calc_stream_format(params_rate(hparams),
params_channels(hparams), params_format(hparams),
dai->driver->playback.sig_bits, 0);
bps = snd_hdac_stream_format_bps(params_format(hparams), SNDRV_PCM_SUBFORMAT_STD,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why with the fixed subformat and not the params_subformat() ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto as for hda_controller.c case.

@@ -138,8 +139,8 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params)
stream = stream_to_hdac_ext_stream(hstream);
snd_hdac_ext_stream_decouple(bus, stream, true);

format_val = snd_hdac_calc_stream_format(params->s_freq,
params->ch, params->format, params->host_bps, 0);
bps = snd_hdac_stream_format_bps(params->format, SNDRV_PCM_SUBFORMAT_STD, params->host_bps);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well. I beginning to think that I'm missing something fundamental now.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

},
.capture = {
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would not just this change could resolve the unsupported S24_LE advertised to userspace?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such change alone is insufficient. It blocks usage of S24_LE yes, but simultaneously does not allow for selecting 20, 24 or 32 as valid bit depth.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is blocking the use of msbits?
Yes, we can only say for example that msbits=32 and have no information that the setup actually supports msbits=24, right?

I mean, should the SNDRV_PCM_SUBFMTBIT_MSBITS_24 be more specific, like SNDRV_PCM_SUBFMTBIT_24_MSBITS_IN_32?
If we really want it to be part of this struct snd_soc_pcm_stream or should we have this as refine information for user space that with S32_LE it could use 20, 24 MSB or 24, 32 MSB and let it deal with it?

@crojewsk-intel
Copy link
Author

@crojewsk-intel, can you explain why the existing msbits support is not good enough when paired with S32_LE? S32_LE + msbits=24 == 24 bits audio in 32bit S32_LE + msbits=20 == 20 bits audio in 32bit S32_LE + msbits=0/32 == 32 bits audio

With all due respect, I'd like to avoid duplicating the upstream discussion. The exact same question has been asked by Pierre [1]. If you feel more answers are needed, host the questions there.
In short, msbits/sigbits is a field that represents a single value, not a mask. A device should be able to tell it supports 20/24/32 valid bit depth within 4-byte wide container back to the user space.

[1]: https://lore.kernel.org/alsa-devel/[email protected]/T/#m66d83b7a47cd1277a4081416b3ec75b4c70ac93d

* Return: The number of bits per sample based on the format,
* subformat and msbits the specified hw params has.
*/
int snd_pcm_hw_params_bps(const struct snd_pcm_hw_params *p)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to update the params_width() so all drivers will get to support the subformat out of box?

@@ -2614,6 +2614,8 @@ static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
int err;
unsigned int mask = 0;

/* All PCMs support at least the default STD subformat. */
hw->subformats |= PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the upstream discussion, the SNDRV_PCM_SUBFORMAT_STD tells that the whole physical width is used?
But how that maps with S24_LE for example?

I would expect that drivers would set explicit subformats if they care and they might say that I'm not supporting STD and here you override that statement.

if (!hw->subformats)
	hw->subformats = PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD);

might be what should be done here?

@@ -848,8 +849,9 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
*ratesp = rates;
}

if (formatsp || bpsp) {
if (formatsp || subformatsp || bpsp) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me 'honoring' means that based on the subformat do the right thing, take subformat into account.
"Extend supported format query with subformat support", perhaps better?

*
* Return: The format bitset or zero if invalid.
*/
unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bps,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is to be used in place of snd_hdac_calc_stream_format()? It is doing the same thing at the end.

snd_hdac_calc_stream_format(params_rate(params), params_channels(params), params_format(params), maxbps, 0);
snd_hdac_spdif_stream_format(params_channels(params), snd_pcm_hw_params_bps(params), params_rate(params), 0);

ctls);
bps = snd_hdac_stream_format_bps(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps);

format_val = snd_hdac_spdif_stream_format(runtime->channels, bps, runtime->rate, ctls);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand how this achieves the goal set by the commit message. It is switching to a new set of function calls to get the format_val, right?
No change in functionality, or am I missing something?

runtime->format,
hinfo->maxbps,
ctls);
bps = snd_hdac_stream_format_bps(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, so this is basically a no functional change to switch function calls to get the format_val?

params_format(params),
maxbps,
0);
format_val = snd_hdac_stream_format(params_channels(params), bps, params_rate(params));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

group the snd_hdac_stream_format_bps() call here, before calling snd_hdac_stream_format()?
or the other way around

},
.capture = {
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is blocking the use of msbits?
Yes, we can only say for example that msbits=32 and have no information that the setup actually supports msbits=24, right?

I mean, should the SNDRV_PCM_SUBFMTBIT_MSBITS_24 be more specific, like SNDRV_PCM_SUBFMTBIT_24_MSBITS_IN_32?
If we really want it to be part of this struct snd_soc_pcm_stream or should we have this as refine information for user space that with S32_LE it could use 20, 24 MSB or 24, 32 MSB and let it deal with it?

@plbossart
Copy link
Member

The thread has now moved upstream and there's another alternate proposal from @perexg, closing this PR

@plbossart plbossart closed this Sep 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants