diff mbox series

[RFC,1/4] alsa: make hw_params negotiation infrastructure 'bclk_ratio aware'

Message ID 20190304165955.21696-1-TheSven73@gmail.com (mailing list archive)
State New, archived
Headers show
Series [RFC,1/4] alsa: make hw_params negotiation infrastructure 'bclk_ratio aware' | expand

Commit Message

Sven Van Asbroeck March 4, 2019, 4:59 p.m. UTC
Negotiation seems to work ok, but bclk_ratio is exposed to
userspace via snd_pcm_hw_params, which is not acceptable.

Constrain bclk_ratio by:
- cpu   dai capabilities && rules
- codec dai capabilities && rules
- minimum bclk_ratio is sample_width * channels

In hw_params_choose(), pick the smallest supported bclk_ratio,
which should correspond to the most efficient solution.

If cpu and codec dais do not specify or constrain supported
bclk_ratios, alsa will pick sample_width * channels.

Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
---
 include/sound/pcm.h         | 11 +++++++++++
 include/sound/soc.h         |  2 ++
 include/uapi/sound/asound.h |  5 +++--
 sound/core/pcm_native.c     | 34 +++++++++++++++++++++++++++++++++-
 sound/soc/soc-pcm.c         |  8 ++++++++
 5 files changed, 57 insertions(+), 3 deletions(-)

Comments

Takashi Sakamoto March 5, 2019, 4:42 a.m. UTC | #1
Hi,

On Mon, Mar 04, 2019 at 11:59:52AM -0500, Sven Van Asbroeck wrote:
> Negotiation seems to work ok, but bclk_ratio is exposed to
> userspace via snd_pcm_hw_params, which is not acceptable.
> 
> Constrain bclk_ratio by:
> - cpu   dai capabilities && rules
> - codec dai capabilities && rules
> - minimum bclk_ratio is sample_width * channels
> 
> In hw_params_choose(), pick the smallest supported bclk_ratio,
> which should correspond to the most efficient solution.
> 
> If cpu and codec dais do not specify or constrain supported
> bclk_ratios, alsa will pick sample_width * channels.
> 
> Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
> ---
>  include/sound/pcm.h         | 11 +++++++++++
>  include/sound/soc.h         |  2 ++
>  include/uapi/sound/asound.h |  5 +++--
>  sound/core/pcm_native.c     | 34 +++++++++++++++++++++++++++++++++-
>  sound/soc/soc-pcm.c         |  8 ++++++++
>  5 files changed, 57 insertions(+), 3 deletions(-)

In UAPI of Linux sound subsystem, sample formats are represented
in enumerators prefixed with 'SNDRV_PCM_FORMAT_'. In the enumerators,
sample bits and padding bits are represented:

              S8 S16 S20 S24 S32 S18_3 S20_3 S24_3
sample-bits:   8  16  20  24  32  18    20    24 (=width)
padding-bits:  0   0  12   8   0   6    12     0
total bits:    8  16  32  32  32  24    24    24 (=physical_width)
 
When indicating a certain bit ratio of BCLK/WS from userspace,
applications use correct combination of the above format (=physical_width)
and the number of samples per audio data frame (=channels).

All of drivers can add constraints and rules for runtime of PCM substream
in its .open callback. As a result, applications can see available
combination of the format and the channels and choose correct combination.

In my opinion, your issue is a lack of the constraints and rules in
relevant drivers; perhaps in tda998x driver. Or core functionality of
ALSA SoC part has a lack of consideration about the above design of
ALSA PCM core and PCM interface. If a certain combination of CPU-dai and
CODEC-dai requires to ignore the above somehow, it should be done in
interaction between drivers for CPU-dai/CODEC-dai. In short, your issue
should not be exported to userspace.

I note that for your 'hypothetical' cpu dai[1], 20x2/20x2 mode
(physical_width=20, channels=2) is not available from userspace application.
We have no sample format suitable to it.


Anyway, when posting to change UAPI of Linux sound subsystem, it's
better to describe the reason fot new stuffs; what they're designed for,
and the reason to require them. Wnen posting in a result of series of
discussion done previously, it's better to add any reference to it. The
change effects all of userspace stuffs, regardless of whether they exist
or doesn't.  Furthermore the change has forced application developers to
take care of codes newly added. Additionally, it's better to note actual
example of applications with the added code. Unless, no one can judge
that the added code is enough abstracted for an application to handle
various type of hardware by the same code.

[1] https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/146062.html


Regards

Takashi Sakamoto
Russell King (Oracle) March 5, 2019, 9:35 a.m. UTC | #2
On Tue, Mar 05, 2019 at 01:42:32PM +0900, Takashi Sakamoto wrote:
> Hi,
> 
> On Mon, Mar 04, 2019 at 11:59:52AM -0500, Sven Van Asbroeck wrote:
> > Negotiation seems to work ok, but bclk_ratio is exposed to
> > userspace via snd_pcm_hw_params, which is not acceptable.
> > 
> > Constrain bclk_ratio by:
> > - cpu   dai capabilities && rules
> > - codec dai capabilities && rules
> > - minimum bclk_ratio is sample_width * channels
> > 
> > In hw_params_choose(), pick the smallest supported bclk_ratio,
> > which should correspond to the most efficient solution.
> > 
> > If cpu and codec dais do not specify or constrain supported
> > bclk_ratios, alsa will pick sample_width * channels.
> > 
> > Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
> > ---
> >  include/sound/pcm.h         | 11 +++++++++++
> >  include/sound/soc.h         |  2 ++
> >  include/uapi/sound/asound.h |  5 +++--
> >  sound/core/pcm_native.c     | 34 +++++++++++++++++++++++++++++++++-
> >  sound/soc/soc-pcm.c         |  8 ++++++++
> >  5 files changed, 57 insertions(+), 3 deletions(-)
> 
> In UAPI of Linux sound subsystem, sample formats are represented
> in enumerators prefixed with 'SNDRV_PCM_FORMAT_'. In the enumerators,
> sample bits and padding bits are represented:
> 
>               S8 S16 S20 S24 S32 S18_3 S20_3 S24_3
> sample-bits:   8  16  20  24  32  18    20    24 (=width)
> padding-bits:  0   0  12   8   0   6    12     0
> total bits:    8  16  32  32  32  24    24    24 (=physical_width)
>  
> When indicating a certain bit ratio of BCLK/WS from userspace,
> applications use correct combination of the above format (=physical_width)
> and the number of samples per audio data frame (=channels).

You seem to be conflating the in-memory format with the on-wire format.
These are two entirely different things.  Your table above clearly shows
the in-memory format, not the on-wire format.
Sven Van Asbroeck March 5, 2019, 2:23 p.m. UTC | #3
Hi Takashi,

On Mon, Mar 4, 2019 at 11:42 PM Takashi Sakamoto
<o-takashi@sakamocchi.jp> wrote:
>
> Anyway, when posting to change UAPI of Linux sound subsystem, it's
> better to describe the reason fot new stuffs; what they're designed for,
> and the reason to require them. Wnen posting in a result of series of
> discussion done previously, it's better to add any reference to it.

My apologies for not linking in previous discussions, I'll certainly make
sure to link in the future.

The original problem was that the fsl_ssi did not appear to work with the
tda998x hdmi codec. This codec seems to be 'unique' in the sense that it
needs to know the number of clocks per frame on the wire, i.e. the bclk_ratio,
which no-one in alsa is providing or using at the moment.

I first made the error of conflating the physical width and the bclk_ratio,
and posted this patch - Russell King quickly pointed out my error:
https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/145916.html

This then led to the following discussions:
https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/thread.html#145985
https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/thread.html#145947

Most of the discussion is about a mechanism to convey bclk_ratio from
the frame master
to the tda998x. We don't yet seem to have a consensus on how to do this best.

My rfc patch was only intended to provoke discussion, and to allow
people to experiment
with a flawed solution that would allow the alsa core to negotiate
bclk_ratio between
components. The uapi change is a serious flaw. bclk_ratio negotiation should be
invisible to userspace. But I cannot see a way to accomplish this.

Sven
Jaroslav Kysela March 6, 2019, 3:53 p.m. UTC | #4
Dne 05. 03. 19 v 15:23 Sven Van Asbroeck napsal(a):
> Hi Takashi,
> 
> On Mon, Mar 4, 2019 at 11:42 PM Takashi Sakamoto
> <o-takashi@sakamocchi.jp> wrote:
>>
>> Anyway, when posting to change UAPI of Linux sound subsystem, it's
>> better to describe the reason fot new stuffs; what they're designed for,
>> and the reason to require them. Wnen posting in a result of series of
>> discussion done previously, it's better to add any reference to it.
> 
> My apologies for not linking in previous discussions, I'll certainly make
> sure to link in the future.
> 
> The original problem was that the fsl_ssi did not appear to work with the
> tda998x hdmi codec. This codec seems to be 'unique' in the sense that it
> needs to know the number of clocks per frame on the wire, i.e. the bclk_ratio,
> which no-one in alsa is providing or using at the moment.
> 
> I first made the error of conflating the physical width and the bclk_ratio,
> and posted this patch - Russell King quickly pointed out my error:
> https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/145916.html
> 
> This then led to the following discussions:
> https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/thread.html#145985
> https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/thread.html#145947
> 
> Most of the discussion is about a mechanism to convey bclk_ratio from
> the frame master
> to the tda998x. We don't yet seem to have a consensus on how to do this best.
> 
> My rfc patch was only intended to provoke discussion, and to allow
> people to experiment
> with a flawed solution that would allow the alsa core to negotiate
> bclk_ratio between
> components. The uapi change is a serious flaw. bclk_ratio negotiation should be
> invisible to userspace. But I cannot see a way to accomplish this.

I think that this might be resolved using double constraint rules in the
affected driver:

    snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
                        bclk_format_constraint, substream,
                                  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
    snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
                        bclk_channels_constraint, substream,
                                  SNDRV_PCM_HW_PARAM_FORMAT, -1);

And refine the format bitmask / channel interval according your bclk
constraint. The physical sample width can be obtained in both
bclk_format_constraint / bclk_channels_constraint function so all input
values are known. The final bclk value can be obtained like in your
proposed code:

    params_channels(params) * params_width(params)

					Jaroslav
Takashi Sakamoto March 8, 2019, 4:10 a.m. UTC | #5
On Tue, Mar 05, 2019 at 09:23:46AM -0500, Sven Van Asbroeck wrote:
> The original problem was that the fsl_ssi did not appear to work with the
> tda998x hdmi codec. This codec seems to be 'unique' in the sense that it
> needs to know the number of clocks per frame on the wire, i.e. the bclk_ratio,
> which no-one in alsa is providing or using at the moment.
> 
> I first made the error of conflating the physical width and the bclk_ratio,
> and posted this patch - Russell King quickly pointed out my error:
> https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/145916.html
> 
> This then led to the following discussions:
> https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/thread.html#145985
> https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/thread.html#145947
> 
> Most of the discussion is about a mechanism to convey bclk_ratio from
> the frame master
> to the tda998x. We don't yet seem to have a consensus on how to do this best.
> 
> My rfc patch was only intended to provoke discussion, and to allow
> people to experiment
> with a flawed solution that would allow the alsa core to negotiate
> bclk_ratio between
> components. The uapi change is a serious flaw. bclk_ratio negotiation should be
> invisible to userspace. But I cannot see a way to accomplish this.

In your case:

+---+            +-----+
|CPU| <- wire -> |CODEC|
|DAI|            | DAI |
+---+            +-----+

So that:

CPU-DAI = fsl_ssi
CODEC-DAI = tda998x
wire = I2S

In I2S:
 - SCK-line = serial clock
 - WS-line = word select
 - SD-line = serial data 

In general I2S communication:
 - 2 samples are transferred in a phase of WS

In my opinion:
 - 'the number of clocks per frame on the wire' (=you need)
   = the number of phases of SCK per phase of WS

In expectation of ALSA PCM interface for hardware for usual device:
 - half number of phases of SCK per phase of WC
   = physical_width of sample
   = bytes per sample
 - the number of phases of SCK per phase of WC
   = physical_width * channels (=2)
   = bytes per frame
 - the ratio between phases of SCK and phases of WC per second
   = rate

Here, usual device is expected to give FIFO for DMA and I2S bus,
synchronized to SCK without any conversion. CODED-DAI also has
FIFO to process signals.

In my opinion, if both of fsl_ssi and tda988x have enough constraints
and rules for runtime of PCM substream about the physical_width, channels
and rate, things look to work well. Perhaps, tda998x driver should have a
condition statement for its role of 'frame master' to add the constraints
and rules, to describe its quirk.

It's helpful to describe the 'unique'ness of tda998x hdmi codec for
the detail.


Regards

Takashi Sakamoto (not maintainer of this subsystem)
Russell King (Oracle) March 8, 2019, 12:59 p.m. UTC | #6
On Fri, Mar 08, 2019 at 01:10:57PM +0900, Takashi Sakamoto wrote:
> On Tue, Mar 05, 2019 at 09:23:46AM -0500, Sven Van Asbroeck wrote:
> > The original problem was that the fsl_ssi did not appear to work with the
> > tda998x hdmi codec. This codec seems to be 'unique' in the sense that it
> > needs to know the number of clocks per frame on the wire, i.e. the bclk_ratio,
> > which no-one in alsa is providing or using at the moment.
> > 
> > I first made the error of conflating the physical width and the bclk_ratio,
> > and posted this patch - Russell King quickly pointed out my error:
> > https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/145916.html
> > 
> > This then led to the following discussions:
> > https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/thread.html#145985
> > https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/thread.html#145947
> > 
> > Most of the discussion is about a mechanism to convey bclk_ratio from
> > the frame master
> > to the tda998x. We don't yet seem to have a consensus on how to do this best.
> > 
> > My rfc patch was only intended to provoke discussion, and to allow
> > people to experiment
> > with a flawed solution that would allow the alsa core to negotiate
> > bclk_ratio between
> > components. The uapi change is a serious flaw. bclk_ratio negotiation should be
> > invisible to userspace. But I cannot see a way to accomplish this.
> 
> In your case:
> 
> +---+            +-----+
> |CPU| <- wire -> |CODEC|
> |DAI|            | DAI |
> +---+            +-----+
> 
> So that:
> 
> CPU-DAI = fsl_ssi
> CODEC-DAI = tda998x
> wire = I2S
> 
> In I2S:
>  - SCK-line = serial clock
>  - WS-line = word select
>  - SD-line = serial data 
> 
> In general I2S communication:
>  - 2 samples are transferred in a phase of WS
> 
> In my opinion:
>  - 'the number of clocks per frame on the wire' (=you need)
>    = the number of phases of SCK per phase of WS
> 
> In expectation of ALSA PCM interface for hardware for usual device:
>  - half number of phases of SCK per phase of WC
>    = physical_width of sample
>    = bytes per sample

They are not the same thing.

Let's take SNDRV_PCM_FORMAT_S16_LE.  The in-memory layout of this format
is two 16-bit samples next to each other in a single 32-bit word.  Their
width is 16, their physical_width is 16, and bytes per sample is 2.

A CPU DAI can do one of two things:

1) it can generate exactly 16 SCK clock cycles per sample before WS
   changes state, leading to a total of 32 SCK clock cycles per
   frame.

2) it can generate more than 16 SCK clock cycles per sample.

Both are entirely legal and permissable under the I2S specification.
Both look the same in memory.

The ALSA format specification (SNDRV_PCM_FORMAT_*) does not specify
which of these two is used on the wire - it only specifies the in-
memory format.

If it were to specify the on-wire format, then we'd have to multiply
the number of in-memory formats by the number of on-wire formats.
These are (at least): AC'97, SPDIF, I2S(Philips), I2S(Left justified),
I2S(Right justified), two different DSP formats, and PDM.  Then for
at least the tree I2S modes, there are the number of SCK clocks per
sample which can be anything from the number of bits in the sample
up to an undefined limit.

What this means is that multiplying the number of in-memory formats
by the number of unboundable bus-specific formats leads to an
unboundable quantity of formats.

This is why ASoC has the ability to set the bclk (bit clock, SCK)
to sample rate ratio - in other words, the number of clocks to
completely transmit the samples for the number of channels on the
link - bit clock rate = sample rate * bclk_ratio.
Russell King (Oracle) March 8, 2019, 1:37 p.m. UTC | #7
On Fri, Mar 08, 2019 at 12:59:16PM +0000, Russell King - ARM Linux admin wrote:
> On Fri, Mar 08, 2019 at 01:10:57PM +0900, Takashi Sakamoto wrote:
> > On Tue, Mar 05, 2019 at 09:23:46AM -0500, Sven Van Asbroeck wrote:
> > > The original problem was that the fsl_ssi did not appear to work with the
> > > tda998x hdmi codec. This codec seems to be 'unique' in the sense that it
> > > needs to know the number of clocks per frame on the wire, i.e. the bclk_ratio,
> > > which no-one in alsa is providing or using at the moment.
> > > 
> > > I first made the error of conflating the physical width and the bclk_ratio,
> > > and posted this patch - Russell King quickly pointed out my error:
> > > https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/145916.html
> > > 
> > > This then led to the following discussions:
> > > https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/thread.html#145985
> > > https://mailman.alsa-project.org/pipermail/alsa-devel/2019-February/thread.html#145947
> > > 
> > > Most of the discussion is about a mechanism to convey bclk_ratio from
> > > the frame master
> > > to the tda998x. We don't yet seem to have a consensus on how to do this best.
> > > 
> > > My rfc patch was only intended to provoke discussion, and to allow
> > > people to experiment
> > > with a flawed solution that would allow the alsa core to negotiate
> > > bclk_ratio between
> > > components. The uapi change is a serious flaw. bclk_ratio negotiation should be
> > > invisible to userspace. But I cannot see a way to accomplish this.
> > 
> > In your case:
> > 
> > +---+            +-----+
> > |CPU| <- wire -> |CODEC|
> > |DAI|            | DAI |
> > +---+            +-----+
> > 
> > So that:
> > 
> > CPU-DAI = fsl_ssi
> > CODEC-DAI = tda998x
> > wire = I2S
> > 
> > In I2S:
> >  - SCK-line = serial clock
> >  - WS-line = word select
> >  - SD-line = serial data 
> > 
> > In general I2S communication:
> >  - 2 samples are transferred in a phase of WS
> > 
> > In my opinion:
> >  - 'the number of clocks per frame on the wire' (=you need)
> >    = the number of phases of SCK per phase of WS
> > 
> > In expectation of ALSA PCM interface for hardware for usual device:
> >  - half number of phases of SCK per phase of WC
> >    = physical_width of sample
> >    = bytes per sample
> 
> They are not the same thing.
> 
> Let's take SNDRV_PCM_FORMAT_S16_LE.  The in-memory layout of this format
> is two 16-bit samples next to each other in a single 32-bit word.  Their
> width is 16, their physical_width is 16, and bytes per sample is 2.
> 
> A CPU DAI can do one of two things:
> 
> 1) it can generate exactly 16 SCK clock cycles per sample before WS
>    changes state, leading to a total of 32 SCK clock cycles per
>    frame.
> 
> 2) it can generate more than 16 SCK clock cycles per sample.
> 
> Both are entirely legal and permissable under the I2S specification.
> Both look the same in memory.
> 
> The ALSA format specification (SNDRV_PCM_FORMAT_*) does not specify
> which of these two is used on the wire - it only specifies the in-
> memory format.
> 
> If it were to specify the on-wire format, then we'd have to multiply
> the number of in-memory formats by the number of on-wire formats.
> These are (at least): AC'97, SPDIF, I2S(Philips), I2S(Left justified),
> I2S(Right justified), two different DSP formats, and PDM.  Then for
> at least the tree I2S modes, there are the number of SCK clocks per
> sample which can be anything from the number of bits in the sample
> up to an undefined limit.
> 
> What this means is that multiplying the number of in-memory formats
> by the number of unboundable bus-specific formats leads to an
> unboundable quantity of formats.

I missed stating another point in that.  Let's say that we were to have
definitions such as:

SNDRV_PCM_FORMAT_S16_LE_I2S16
SNDRV_PCM_FORMAT_S16_LE_I2S32
SNDRV_PCM_FORMAT_S16_LE_SPDIF

We have a CPU interface that can simultaneously produce both
SNDRV_PCM_FORMAT_S16_LE_I2S32 and SNDRV_PCM_FORMAT_S16_LE_SPDIF.
However, the ALSA format negotiation is such that only _one_ format
can be negotiated between user space and kernel space.  So, one of
those has to be selected.  So, despite the hardware being perfectly
capable of producing both streams, combining the on-wire formats
with the in-memory formats means that we can't support simultaneous
operation, despite the hardware being perfectly capable.

Another point to make there is that in the general case, if a codec
supports reception of SNDRV_PCM_FORMAT_S16_LE_I2S16, then it supports
reception of SNDRV_PCM_FORMAT_S16_LE_I2S32 - the I2S bus specification
explicitly allows for more clocks than there are sample bits, and
states what happens to the extra bits.

The exception to that is when the receiving device uses the bit clock
(aka SCK) for some other purpose, such as TDA998x devices, where a
driver for such a device needs to know the ratio between the bus bit
clock and the sample rate.  As previously illustrated, ASoC has partial
support for this.
Takashi Sakamoto March 8, 2019, 2:29 p.m. UTC | #8
Hi,

On Fri, Mar 08, 2019 at 12:59:16PM +0000, Russell King - ARM Linux admin wrote:
> > In your case:
> > 
> > +---+            +-----+
> > |CPU| <- wire -> |CODEC|
> > |DAI|            | DAI |
> > +---+            +-----+
> > 
> > So that:
> > 
> > CPU-DAI = fsl_ssi
> > CODEC-DAI = tda998x
> > wire = I2S
> > 
> > In I2S:
> >  - SCK-line = serial clock
> >  - WS-line = word select
> >  - SD-line = serial data 
> > 
> > In general I2S communication:
> >  - 2 samples are transferred in a phase of WS
> > 
> > In my opinion:
> >  - 'the number of clocks per frame on the wire' (=you need)
> >    = the number of phases of SCK per phase of WS
> > 
> > In expectation of ALSA PCM interface for hardware for usual device:
> >  - half number of phases of SCK per phase of WC
> >    = physical_width of sample
> >    = bytes per sample
> 
> They are not the same thing.
> 
> Let's take SNDRV_PCM_FORMAT_S16_LE.  The in-memory layout of this format
> is two 16-bit samples next to each other in a single 32-bit word.  Their
> width is 16, their physical_width is 16, and bytes per sample is 2.
> 
> A CPU DAI can do one of two things:
> 
> 1) it can generate exactly 16 SCK clock cycles per sample before WS
>    changes state, leading to a total of 32 SCK clock cycles per
>    frame.
> 
> 2) it can generate more than 16 SCK clock cycles per sample.
>
> Both are entirely legal and permissable under the I2S specification.
> Both look the same in memory.
> 
> The ALSA format specification (SNDRV_PCM_FORMAT_*) does not specify
> which of these two is used on the wire - it only specifies the in-
> memory format.
> 
> If it were to specify the on-wire format, then we'd have to multiply
> the number of in-memory formats by the number of on-wire formats.
> These are (at least): AC'97, SPDIF, I2S(Philips), I2S(Left justified),
> I2S(Right justified), two different DSP formats, and PDM.  Then for
> at least the tree I2S modes, there are the number of SCK clocks per
> sample which can be anything from the number of bits in the sample
> up to an undefined limit.
> 
> What this means is that multiplying the number of in-memory formats
> by the number of unboundable bus-specific formats leads to an
> unboundable quantity of formats.
> 
> This is why ASoC has the ability to set the bclk (bit clock, SCK)
> to sample rate ratio - in other words, the number of clocks to
> completely transmit the samples for the number of channels on the
> link - bit clock rate = sample rate * bclk_ratio.

Hm. In ALSA PCM core, the combination of 'rate_num' and 'rate_den' is
another representation of sampling rate[1], and drivers can have
constraints and rules for these two parameters of runtime of PCM
substream. Once core functionalities and drivers in ALSA SoC part have
common specifications about actual value of these two parameters, the
issue could be solved, in my opinion.
(But I need time to consider it further in this weekend...)


However, I don't have clear view of your case 2) yet. In that case,
how drivers calculate return value in 'struct snd_pcm_ops.pointer'
callback? It should be frame count, but WS seems not to be available
for 16 bit sample because the number of SCK clock cycles per sample is
larger than 16.
(If WS clock works as I expected, SCK clock cycles per sample include
expression of padding bits added to 16 bit sample.)


[1] https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git/tree/include/sound/pcm.h#n380

Thanks

Takashi Sakamoto
Russell King (Oracle) March 8, 2019, 2:55 p.m. UTC | #9
On Fri, Mar 08, 2019 at 11:29:46PM +0900, Takashi Sakamoto wrote:
> Hi,
> 
> On Fri, Mar 08, 2019 at 12:59:16PM +0000, Russell King - ARM Linux admin wrote:
> > > In your case:
> > > 
> > > +---+            +-----+
> > > |CPU| <- wire -> |CODEC|
> > > |DAI|            | DAI |
> > > +---+            +-----+
> > > 
> > > So that:
> > > 
> > > CPU-DAI = fsl_ssi
> > > CODEC-DAI = tda998x
> > > wire = I2S
> > > 
> > > In I2S:
> > >  - SCK-line = serial clock
> > >  - WS-line = word select
> > >  - SD-line = serial data 
> > > 
> > > In general I2S communication:
> > >  - 2 samples are transferred in a phase of WS
> > > 
> > > In my opinion:
> > >  - 'the number of clocks per frame on the wire' (=you need)
> > >    = the number of phases of SCK per phase of WS
> > > 
> > > In expectation of ALSA PCM interface for hardware for usual device:
> > >  - half number of phases of SCK per phase of WC
> > >    = physical_width of sample
> > >    = bytes per sample
> > 
> > They are not the same thing.
> > 
> > Let's take SNDRV_PCM_FORMAT_S16_LE.  The in-memory layout of this format
> > is two 16-bit samples next to each other in a single 32-bit word.  Their
> > width is 16, their physical_width is 16, and bytes per sample is 2.
> > 
> > A CPU DAI can do one of two things:
> > 
> > 1) it can generate exactly 16 SCK clock cycles per sample before WS
> >    changes state, leading to a total of 32 SCK clock cycles per
> >    frame.
> > 
> > 2) it can generate more than 16 SCK clock cycles per sample.
> >
> > Both are entirely legal and permissable under the I2S specification.
> > Both look the same in memory.
> > 
> > The ALSA format specification (SNDRV_PCM_FORMAT_*) does not specify
> > which of these two is used on the wire - it only specifies the in-
> > memory format.
> > 
> > If it were to specify the on-wire format, then we'd have to multiply
> > the number of in-memory formats by the number of on-wire formats.
> > These are (at least): AC'97, SPDIF, I2S(Philips), I2S(Left justified),
> > I2S(Right justified), two different DSP formats, and PDM.  Then for
> > at least the tree I2S modes, there are the number of SCK clocks per
> > sample which can be anything from the number of bits in the sample
> > up to an undefined limit.
> > 
> > What this means is that multiplying the number of in-memory formats
> > by the number of unboundable bus-specific formats leads to an
> > unboundable quantity of formats.
> > 
> > This is why ASoC has the ability to set the bclk (bit clock, SCK)
> > to sample rate ratio - in other words, the number of clocks to
> > completely transmit the samples for the number of channels on the
> > link - bit clock rate = sample rate * bclk_ratio.
> 
> Hm. In ALSA PCM core, the combination of 'rate_num' and 'rate_den' is
> another representation of sampling rate[1], and drivers can have
> constraints and rules for these two parameters of runtime of PCM
> substream. Once core functionalities and drivers in ALSA SoC part have
> common specifications about actual value of these two parameters, the
> issue could be solved, in my opinion.
> (But I need time to consider it further in this weekend...)

rate_num and rate_den describe the sampling rate in fractional
notation, as described by the ALSA tracepoint documentation:

``rate_num``
        Read-only. This value represents numerator of sampling rate in fraction
        notation. Basically, when a parameter of SNDRV_PCM_HW_PARAM_RATE was
        decided as a single value, this value is also calculated according to
        it. Else, zero. But this behaviour depends on implementations in driver
        side.
``rate_den``
        Read-only. This value represents denominator of sampling rate in
        fraction notation. Basically, when a parameter of
        SNDRV_PCM_HW_PARAM_RATE was decided as a single value, this value is
        also calculated according to it. Else, zero. But this behaviour depends
        on implementations in driver side.

and is also described in the header files as:

        unsigned int rate_num;          /* R: rate numerator */
        unsigned int rate_den;          /* R: rate denominator */

params_rate() returns the rate from the interval negotiated with
userspace, and is described in the ALSA header files as:

#define SNDRV_PCM_HW_PARAM_RATE         11      /* Approx rate */

This only describes the sample rate, it does not describe anything to do
with bit clocks or their ratio to the sample rate.

> However, I don't have clear view of your case 2) yet. In that case,
> how drivers calculate return value in 'struct snd_pcm_ops.pointer'
> callback?  It should be frame count, but WS seems not to be available
> for 16 bit sample because the number of SCK clock cycles per sample is
> larger than 16.
> (If WS clock works as I expected, SCK clock cycles per sample include
> expression of padding bits added to 16 bit sample.)

snd_pcm_ops.pointer has nothing at all to do with what is happening on
the I2S bus.  It has nothing to do with the WS nor SCK.  Please see the
documentation in Documentation/sound/kernel-api, which states:

  pointer callback
  ~~~~~~~~~~~~~~~

  ::

    static snd_pcm_uframes_t snd_xxx_pointer(struct snd_pcm_substream *substream)

  This callback is called when the PCM middle layer inquires the current
  hardware position on the buffer. The position must be returned in
  frames, ranging from 0 to ``buffer_size - 1``.

  This is called usually from the buffer-update routine in the pcm
  middle layer, which is invoked when :c:func:`snd_pcm_period_elapsed()`
  is called in the interrupt routine. Then the pcm middle layer updates
  the position and calculates the available space, and wakes up the
  sleeping poll threads, etc.

This is the position within the memory buffer in frames (hence the
return type) where the hardware has read or written.  It does *not*
take account of any FIFO that may be between the DMA accessing the
memory buffer and the samples appearing on the wire to the codec.
ALSA models that effective delay using the "fifo_size" member of
struct snd_pcm_hardware.

ALSA defines frames as (also from the documentation):

  In the ALSA world, ``1 frame = channels \* samples-size``.
Mark Brown March 8, 2019, 5:22 p.m. UTC | #10
On Fri, Mar 08, 2019 at 12:59:16PM +0000, Russell King - ARM Linux admin wrote:
> On Fri, Mar 08, 2019 at 01:10:57PM +0900, Takashi Sakamoto wrote:

> > In expectation of ALSA PCM interface for hardware for usual device:
> >  - half number of phases of SCK per phase of WC
> >    = physical_width of sample
> >    = bytes per sample

> They are not the same thing.

> Let's take SNDRV_PCM_FORMAT_S16_LE.  The in-memory layout of this format
> is two 16-bit samples next to each other in a single 32-bit word.  Their
> width is 16, their physical_width is 16, and bytes per sample is 2.

> A CPU DAI can do one of two things:

> 1) it can generate exactly 16 SCK clock cycles per sample before WS
>    changes state, leading to a total of 32 SCK clock cycles per
>    frame.

> 2) it can generate more than 16 SCK clock cycles per sample.

> Both are entirely legal and permissable under the I2S specification.
> Both look the same in memory.

> The ALSA format specification (SNDRV_PCM_FORMAT_*) does not specify
> which of these two is used on the wire - it only specifies the in-
> memory format.

Everything Russell is saying here is correct.  The actual
ABI only affects the in memory format, userspace really shouldn't care
what's going on on the wire.  However we don't have separate
infrastructure for what goes on the wire and 90% of the time you can
just translate the memory layout into a wire layout which works so we're
conflating the two in a lot of places internally which is confusing and
fragile.
Jaroslav Kysela March 8, 2019, 7:54 p.m. UTC | #11
Dne 08. 03. 19 v 18:22 Mark Brown napsal(a):
> On Fri, Mar 08, 2019 at 12:59:16PM +0000, Russell King - ARM Linux admin wrote:
>> On Fri, Mar 08, 2019 at 01:10:57PM +0900, Takashi Sakamoto wrote:
> 
>>> In expectation of ALSA PCM interface for hardware for usual device:
>>>  - half number of phases of SCK per phase of WC
>>>    = physical_width of sample
>>>    = bytes per sample
> 
>> They are not the same thing.
> 
>> Let's take SNDRV_PCM_FORMAT_S16_LE.  The in-memory layout of this format
>> is two 16-bit samples next to each other in a single 32-bit word.  Their
>> width is 16, their physical_width is 16, and bytes per sample is 2.
> 
>> A CPU DAI can do one of two things:
> 
>> 1) it can generate exactly 16 SCK clock cycles per sample before WS
>>    changes state, leading to a total of 32 SCK clock cycles per
>>    frame.
> 
>> 2) it can generate more than 16 SCK clock cycles per sample.
> 
>> Both are entirely legal and permissable under the I2S specification.
>> Both look the same in memory.
> 
>> The ALSA format specification (SNDRV_PCM_FORMAT_*) does not specify
>> which of these two is used on the wire - it only specifies the in-
>> memory format.
> 
> Everything Russell is saying here is correct.  The actual
> ABI only affects the in memory format, userspace really shouldn't care
> what's going on on the wire.  However we don't have separate
> infrastructure for what goes on the wire and 90% of the time you can
> just translate the memory layout into a wire layout which works so we're
> conflating the two in a lot of places internally which is confusing and
> fragile.

I agree. We just need a library which will:

1) gather the information from hardware drivers
  - a simple description of the bclk constrains
2) create right constraints (hw_params rules) for the ALSA PCM API
3) return the selected bclk when hw_params are installed

The description of the bclk constraints from the hardware driver might
be min/max or a list of allowed wire format bit width * channels,
eventually define the wire formats (bitmask) and use them in this
library. I can imagine that all of those bclk contraints descriptions
(or any future, if there are such requirement) can be implemented in
this library.

The library should not extend hw_params (new interval), but it should
work as a separate layer - use new structures / functions etc.

					Jaroslav
Sven Van Asbroeck March 8, 2019, 8:07 p.m. UTC | #12
On Fri, Mar 8, 2019 at 2:54 PM Jaroslav Kysela <perex@perex.cz> wrote:
>
> I agree. We just need a library which will:
>
> 1) gather the information from hardware drivers
>   - a simple description of the bclk constrains
> 2) create right constraints (hw_params rules) for the ALSA PCM API
> 3) return the selected bclk when hw_params are installed
>

Yes, that's what the RFC patch I posted attempts to do.
But it extends hw_params, which is clearly wrong.

>
> The library should not extend hw_params (new interval), but it should
> work as a separate layer - use new structures / functions etc.
>

This, I could not work out how to approach :)
Pierre-Louis Bossart March 8, 2019, 8:49 p.m. UTC | #13
>> I agree. We just need a library which will:
>>
>> 1) gather the information from hardware drivers
>>    - a simple description of the bclk constrains
>> 2) create right constraints (hw_params rules) for the ALSA PCM API
>> 3) return the selected bclk when hw_params are installed
>>
> Yes, that's what the RFC patch I posted attempts to do.
> But it extends hw_params, which is clearly wrong.
>
>> The library should not extend hw_params (new interval), but it should
>> work as a separate layer - use new structures / functions etc.
>>
> This, I could not work out how to approach :)

I am not sure I fully understand the ask but wanted to point out that 
for ASoC topology-based solutions the bclk rate is typically passed as a 
parameter from userspace (w/ a request_firmware and topology parsing) 
and might be forwarded over IPC to a DSP. On some Intel platforms which 
can't support 32x fs that is typically how we represent a bclk ratio 
multiple of 25. the kernel has no idea of the relationship between the 
representation of the stream in memory and the final bit clock, only the 
DSP which programs the hardware registers knows about the latter.

It's really quite typical that the DAI is programmed for a fixed 
configuration and the DSP takes care of the conversions. The kernel only 
deals with stream triggers and power management without know all the 
internal details of the audio graph.
Mark Brown March 8, 2019, 9:13 p.m. UTC | #14
On Fri, Mar 08, 2019 at 02:49:48PM -0600, Pierre-Louis Bossart wrote:

> I am not sure I fully understand the ask but wanted to point out that for
> ASoC topology-based solutions the bclk rate is typically passed as a
> parameter from userspace (w/ a request_firmware and topology parsing) and

You mean for x86 systems :)  Well, big DSP really.  It's not really
topology related.

> might be forwarded over IPC to a DSP. On some Intel platforms which can't
> support 32x fs that is typically how we represent a bclk ratio multiple of
> 25. the kernel has no idea of the relationship between the representation of
> the stream in memory and the final bit clock, only the DSP which programs
> the hardware registers knows about the latter.

> It's really quite typical that the DAI is programmed for a fixed
> configuration and the DSP takes care of the conversions. The kernel only
> deals with stream triggers and power management without know all the
> internal details of the audio graph.

I think this is more the issue with not having transitioned fully to
components which we've talked about before I think - it's related but
not quite the same thing.  In the big DSP case there's really two audio
links that aren't really connected but we're currently trying to treat
them as one since the code was designed for much smaller DSPs.
Pierre-Louis Bossart March 8, 2019, 9:54 p.m. UTC | #15
On 3/8/19 3:13 PM, Mark Brown wrote:
> On Fri, Mar 08, 2019 at 02:49:48PM -0600, Pierre-Louis Bossart wrote:
>
>> I am not sure I fully understand the ask but wanted to point out that for
>> ASoC topology-based solutions the bclk rate is typically passed as a
>> parameter from userspace (w/ a request_firmware and topology parsing) and
> You mean for x86 systems :)  Well, big DSP really.  It's not really
> topology related.

I was referring to asoc.h and the following structure. For once it's not 
an Intel-specific hack, though the topology does need a lot of love to 
be hardened and extended.

struct snd_soc_tplg_hw_config {
     __le32 size;            /* in bytes of this structure */
     __le32 id;        /* unique ID - - used to match */
     __le32 fmt;        /* SND_SOC_DAI_FORMAT_ format value */
     __u8 clock_gated;    /* SND_SOC_TPLG_DAI_CLK_GATE_ value */
     __u8 invert_bclk;    /* 1 for inverted BCLK, 0 for normal */
     __u8 invert_fsync;    /* 1 for inverted frame clock, 0 for normal */
     __u8 bclk_master;    /* SND_SOC_TPLG_BCLK_ value */
     __u8 fsync_master;    /* SND_SOC_TPLG_FSYNC_ value */
     __u8 mclk_direction;    /* SND_SOC_TPLG_MCLK_ value */
     __le16 reserved;    /* for 32bit alignment */
     __le32 mclk_rate;    /* MCLK or SYSCLK freqency in Hz */
     __le32 bclk_rate;    /* BCLK frequency in Hz */
     __le32 fsync_rate;    /* frame clock in Hz */
     __le32 tdm_slots;    /* number of TDM slots in use */
     __le32 tdm_slot_width;    /* width in bits for each slot */
     __le32 tx_slots;    /* bit mask for active Tx slots */
     __le32 rx_slots;    /* bit mask for active Rx slots */
     __le32 tx_channels;    /* number of Tx channels */
     __le32 tx_chanmap[SND_SOC_TPLG_MAX_CHAN]; /* array of slot number */
     __le32 rx_channels;    /* number of Rx channels */
     __le32 rx_chanmap[SND_SOC_TPLG_MAX_CHAN]; /* array of slot number */
} __attribute__((packed));


>
>> might be forwarded over IPC to a DSP. On some Intel platforms which can't
>> support 32x fs that is typically how we represent a bclk ratio multiple of
>> 25. the kernel has no idea of the relationship between the representation of
>> the stream in memory and the final bit clock, only the DSP which programs
>> the hardware registers knows about the latter.
>> It's really quite typical that the DAI is programmed for a fixed
>> configuration and the DSP takes care of the conversions. The kernel only
>> deals with stream triggers and power management without know all the
>> internal details of the audio graph.
> I think this is more the issue with not having transitioned fully to
> components which we've talked about before I think - it's related but
> not quite the same thing.  In the big DSP case there's really two audio
> links that aren't really connected but we're currently trying to treat
> them as one since the code was designed for much smaller DSPs.

Yes, this notion of hw_params negotiation made me think about the 
constraints propagation that Lars talked about ~2 years ago, it's not as 
simple as a helper library I am afraid.

This discussion on bclk ratio makes a lot of sense. Some codecs have 
undocumented restrictions, e.g PCM512x or some Maxim amps, and it's not 
uncommon to come up with a configuration mismatch that take time to 
debug. If at least we could have an error thrown it'd be a good thing.
Takashi Sakamoto March 11, 2019, 8:15 a.m. UTC | #16
Hi all,

On Fri, Mar 08, 2019 at 08:54:03PM +0100, Jaroslav Kysela wrote:
> Dne 08. 03. 19 v 18:22 Mark Brown napsal(a):
> > On Fri, Mar 08, 2019 at 12:59:16PM +0000, Russell King - ARM Linux admin wrote:
> >> On Fri, Mar 08, 2019 at 01:10:57PM +0900, Takashi Sakamoto wrote:
> > 
> >>> In expectation of ALSA PCM interface for hardware for usual device:
> >>>  - half number of phases of SCK per phase of WC
> >>>    = physical_width of sample
> >>>    = bytes per sample
> > 
> >> They are not the same thing.
> > 
> >> Let's take SNDRV_PCM_FORMAT_S16_LE.  The in-memory layout of this format
> >> is two 16-bit samples next to each other in a single 32-bit word.  Their
> >> width is 16, their physical_width is 16, and bytes per sample is 2.
> > 
> >> A CPU DAI can do one of two things:
> > 
> >> 1) it can generate exactly 16 SCK clock cycles per sample before WS
> >>    changes state, leading to a total of 32 SCK clock cycles per
> >>    frame.
> > 
> >> 2) it can generate more than 16 SCK clock cycles per sample.
> > 
> >> Both are entirely legal and permissable under the I2S specification.
> >> Both look the same in memory.
> > 
> >> The ALSA format specification (SNDRV_PCM_FORMAT_*) does not specify
> >> which of these two is used on the wire - it only specifies the in-
> >> memory format.
> > 
> > Everything Russell is saying here is correct.  The actual
> > ABI only affects the in memory format, userspace really shouldn't care
> > what's going on on the wire.  However we don't have separate
> > infrastructure for what goes on the wire and 90% of the time you can
> > just translate the memory layout into a wire layout which works so we're
> > conflating the two in a lot of places internally which is confusing and
> > fragile.
> 
> I agree. We just need a library which will:
> 
> 1) gather the information from hardware drivers
>   - a simple description of the bclk constrains
> 2) create right constraints (hw_params rules) for the ALSA PCM API
> 3) return the selected bclk when hw_params are installed
> 
> The description of the bclk constraints from the hardware driver might
> be min/max or a list of allowed wire format bit width * channels,
> eventually define the wire formats (bitmask) and use them in this
> library. I can imagine that all of those bclk contraints descriptions
> (or any future, if there are such requirement) can be implemented in
> this library.
> 
> The library should not extend hw_params (new interval), but it should
> work as a separate layer - use new structures / functions etc.

As another option I can suggest; usage of 'subformat' field of 'struct
snd_pcm_hw_params'.

At present, ALSA PCM interface has one entry to this field:
 - SNDRV_PCM_SUBFORMAT_STD

And no drivers use this entry.

Let's assume to add new entries to represent bclk/ws for the issued case:
 - SNDRV_PCM_SUBFORMAT_I2S_16BCLK_PER_WS
 - SNDRV_PCM_SUBFORMAT_I2S_48BCLK_PER_WS
 - SNDRV_PCM_SUBFORMAT_I2S_64BCLK_PER_WS
 - SNDRV_PCM_SUBFORMAT_I2S_128BCLK_PER_WS

Drivers can have constraints and rules for the parameter. At least, ALSA
PCM core should have an rule (if I understand correctly):
 - BCLK count of these subformats > physical_width * channel

One week point is it's not 'interval' type of parameter but 'mask'
parameter, thus we can't represent min/max/integer and so on. However,
less changes for ALSA PCM interface.

This is just an idea so it's not enough for me to consider about
relevant implementation. Any indication is welcome.


Regards

Takashi Sakamoto
Jaroslav Kysela March 11, 2019, 3:43 p.m. UTC | #17
Dne 11. 03. 19 v 9:15 Takashi Sakamoto napsal(a):
> Hi all,
> 
> On Fri, Mar 08, 2019 at 08:54:03PM +0100, Jaroslav Kysela wrote:
>> Dne 08. 03. 19 v 18:22 Mark Brown napsal(a):
>>> On Fri, Mar 08, 2019 at 12:59:16PM +0000, Russell King - ARM Linux admin wrote:
>>>> On Fri, Mar 08, 2019 at 01:10:57PM +0900, Takashi Sakamoto wrote:
>>>
>>>>> In expectation of ALSA PCM interface for hardware for usual device:
>>>>>  - half number of phases of SCK per phase of WC
>>>>>    = physical_width of sample
>>>>>    = bytes per sample
>>>
>>>> They are not the same thing.
>>>
>>>> Let's take SNDRV_PCM_FORMAT_S16_LE.  The in-memory layout of this format
>>>> is two 16-bit samples next to each other in a single 32-bit word.  Their
>>>> width is 16, their physical_width is 16, and bytes per sample is 2.
>>>
>>>> A CPU DAI can do one of two things:
>>>
>>>> 1) it can generate exactly 16 SCK clock cycles per sample before WS
>>>>    changes state, leading to a total of 32 SCK clock cycles per
>>>>    frame.
>>>
>>>> 2) it can generate more than 16 SCK clock cycles per sample.
>>>
>>>> Both are entirely legal and permissable under the I2S specification.
>>>> Both look the same in memory.
>>>
>>>> The ALSA format specification (SNDRV_PCM_FORMAT_*) does not specify
>>>> which of these two is used on the wire - it only specifies the in-
>>>> memory format.
>>>
>>> Everything Russell is saying here is correct.  The actual
>>> ABI only affects the in memory format, userspace really shouldn't care
>>> what's going on on the wire.  However we don't have separate
>>> infrastructure for what goes on the wire and 90% of the time you can
>>> just translate the memory layout into a wire layout which works so we're
>>> conflating the two in a lot of places internally which is confusing and
>>> fragile.
>>
>> I agree. We just need a library which will:
>>
>> 1) gather the information from hardware drivers
>>   - a simple description of the bclk constrains
>> 2) create right constraints (hw_params rules) for the ALSA PCM API
>> 3) return the selected bclk when hw_params are installed
>>
>> The description of the bclk constraints from the hardware driver might
>> be min/max or a list of allowed wire format bit width * channels,
>> eventually define the wire formats (bitmask) and use them in this
>> library. I can imagine that all of those bclk contraints descriptions
>> (or any future, if there are such requirement) can be implemented in
>> this library.
>>
>> The library should not extend hw_params (new interval), but it should
>> work as a separate layer - use new structures / functions etc.
> 
> As another option I can suggest; usage of 'subformat' field of 'struct
> snd_pcm_hw_params'.
> 
> At present, ALSA PCM interface has one entry to this field:
>  - SNDRV_PCM_SUBFORMAT_STD
> 
> And no drivers use this entry.
> 
> Let's assume to add new entries to represent bclk/ws for the issued case:
>  - SNDRV_PCM_SUBFORMAT_I2S_16BCLK_PER_WS
>  - SNDRV_PCM_SUBFORMAT_I2S_48BCLK_PER_WS
>  - SNDRV_PCM_SUBFORMAT_I2S_64BCLK_PER_WS
>  - SNDRV_PCM_SUBFORMAT_I2S_128BCLK_PER_WS
> 
> Drivers can have constraints and rules for the parameter. At least, ALSA
> PCM core should have an rule (if I understand correctly):
>  - BCLK count of these subformats > physical_width * channel
> 
> One week point is it's not 'interval' type of parameter but 'mask'
> parameter, thus we can't represent min/max/integer and so on. However,
> less changes for ALSA PCM interface.
> 
> This is just an idea so it's not enough for me to consider about
> relevant implementation. Any indication is welcome.

I would not use any of the "user space" ioctl API to represent the
hardware bclk requirements. The applications should know just the DMA
memory layout.

Also, think about the multiple simultaneous paths for the audio output
in the sound controller (so one DMA from the user space to the
controller, but the controller can do multiple simultaneous outputs
using different clocks combining different wire buses or so). Yes, it's
the corner case, but it's another reason to have the bclk code totally
separated from the user space ALSA's PCM API.

					Jaroslav
Mark Brown March 12, 2019, 3:03 p.m. UTC | #18
On Mon, Mar 11, 2019 at 04:43:39PM +0100, Jaroslav Kysela wrote:

> I would not use any of the "user space" ioctl API to represent the
> hardware bclk requirements. The applications should know just the DMA
> memory layout.

> Also, think about the multiple simultaneous paths for the audio output
> in the sound controller (so one DMA from the user space to the
> controller, but the controller can do multiple simultaneous outputs
> using different clocks combining different wire buses or so). Yes, it's
> the corner case, but it's another reason to have the bclk code totally
> separated from the user space ALSA's PCM API.

There's also a range of devices that either don't have visible buses at
all due to integration or which are on buses that look nothing like the
I2S/DSP mode style of bus, rendering the parameters meaningless.
Takashi Sakamoto March 13, 2019, 5:57 a.m. UTC | #19
Hi,

On Tue, Mar 12, 2019 at 03:03:00PM +0000, Mark Brown wrote:
> On Mon, Mar 11, 2019 at 04:43:39PM +0100, Jaroslav Kysela wrote:
> 
> > I would not use any of the "user space" ioctl API to represent the
> > hardware bclk requirements. The applications should know just the DMA
> > memory layout.
> 
> > Also, think about the multiple simultaneous paths for the audio output
> > in the sound controller (so one DMA from the user space to the
> > controller, but the controller can do multiple simultaneous outputs
> > using different clocks combining different wire buses or so). Yes, it's
> > the corner case, but it's another reason to have the bclk code totally
> > separated from the user space ALSA's PCM API.
> 
> There's also a range of devices that either don't have visible buses at
> all due to integration or which are on buses that look nothing like the
> I2S/DSP mode style of bus, rendering the parameters meaningless.

Indeed. That's resonable to add no changes to ALSA PCM interface. When
suggesting usage of 'rate_num' and 'rate_den', I assumed to change
ALSA SoC part internal to have constraints and rules for them, with no
changes of ALSA PCM inteface itself. I agree that the dicision of
on-wire format should not be exposed to userspace as well.


[1] https://mailman.alsa-project.org/pipermail/alsa-devel/2019-March/146261.html

Thanks

Takashi Sakamoto
diff mbox series

Patch

diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index d6bd3caf6878..71ac7e8de23d 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -56,6 +56,8 @@  struct snd_pcm_hardware {
 	unsigned int periods_min;	/* min # of periods */
 	unsigned int periods_max;	/* max # of periods */
 	size_t fifo_size;		/* fifo size in bytes */
+	unsigned int bclk_ratio_min;	/* min bclk ratio for wire format */
+	unsigned int bclk_ratio_max;	/* max bclk ratio for wire format */
 };
 
 struct snd_pcm_substream;
@@ -980,6 +982,15 @@  static inline unsigned int params_buffer_bytes(const struct snd_pcm_hw_params *p
 	return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min;
 }
 
+/**
+ * params_bclk_ratio - Get the bclk_ratio from the hw params
+ * @p: hw params
+ */
+static inline unsigned int params_bclk_ratio(const struct snd_pcm_hw_params *p)
+{
+	return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_BCLK_RATIO)->min;
+}
+
 int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v);
 int snd_interval_list(struct snd_interval *i, unsigned int count,
 		      const unsigned int *list, unsigned int mask);
diff --git a/include/sound/soc.h b/include/sound/soc.h
index e665f111b0d2..96d669423688 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -732,6 +732,8 @@  struct snd_soc_pcm_stream {
 	unsigned int channels_min;	/* min channels */
 	unsigned int channels_max;	/* max channels */
 	unsigned int sig_bits;		/* number of bits of content */
+	unsigned int bclk_ratio_min;	/* min bclk ratio for wire format */
+	unsigned int bclk_ratio_max;	/* max bclk ratio for wire format */
 };
 
 /* SoC audio ops */
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 404d4b9ffe76..c3ea94eaaa77 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -371,8 +371,9 @@  typedef int snd_pcm_hw_param_t;
 #define	SNDRV_PCM_HW_PARAM_BUFFER_SIZE	17	/* Size of buffer in frames */
 #define	SNDRV_PCM_HW_PARAM_BUFFER_BYTES	18	/* Size of buffer in bytes */
 #define	SNDRV_PCM_HW_PARAM_TICK_TIME	19	/* Approx tick duration in us */
+#define SNDRV_PCM_HW_PARAM_BCLK_RATIO	20	/* bclk_ratio for wire format */
 #define	SNDRV_PCM_HW_PARAM_FIRST_INTERVAL	SNDRV_PCM_HW_PARAM_SAMPLE_BITS
-#define	SNDRV_PCM_HW_PARAM_LAST_INTERVAL	SNDRV_PCM_HW_PARAM_TICK_TIME
+#define	SNDRV_PCM_HW_PARAM_LAST_INTERVAL	SNDRV_PCM_HW_PARAM_BCLK_RATIO
 
 #define SNDRV_PCM_HW_PARAMS_NORESAMPLE	(1<<0)	/* avoid rate resampling */
 #define SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER	(1<<1)	/* export buffer */
@@ -399,7 +400,7 @@  struct snd_pcm_hw_params {
 	struct snd_mask mres[5];	/* reserved masks */
 	struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL -
 				        SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
-	struct snd_interval ires[9];	/* reserved intervals */
+	struct snd_interval ires[8];	/* reserved intervals */
 	unsigned int rmask;		/* W: requested masks */
 	unsigned int cmask;		/* R: changed masks */
 	unsigned int info;		/* R: Info flags for returned setup */
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 818dff1de545..23dbe43a6691 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -516,6 +516,7 @@  int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
 		      struct snd_pcm_hw_params *params)
 {
 	int err;
+	struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_BCLK_RATIO);
 
 	params->info = 0;
 	params->fifo_size = 0;
@@ -525,6 +526,12 @@  int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
 		params->rate_num = 0;
 		params->rate_den = 0;
 	}
+	/*
+	 * if left zero (not empty), assume userspace is oblivious, and
+	 * completely flexible
+	 */
+	if (snd_interval_single(r) && snd_interval_min(r) == 0)
+		snd_interval_any(r);
 
 	err = constrain_mask_params(substream, params);
 	if (err < 0)
@@ -610,7 +617,8 @@  static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
  * Choose one configuration from configuration space defined by @params.
  * The configuration chosen is that obtained fixing in this order:
  * first access, first format, first subformat, min channels,
- * min rate, min period time, max buffer size, min tick time
+ * min rate, min period time, max buffer size, min tick time,
+ * min bclk_ratio
  *
  * Return: Zero if successful, or a negative error code on failure.
  */
@@ -626,6 +634,7 @@  static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
 		SNDRV_PCM_HW_PARAM_PERIOD_TIME,
 		SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
 		SNDRV_PCM_HW_PARAM_TICK_TIME,
+		SNDRV_PCM_HW_PARAM_BCLK_RATIO,
 		-1
 	};
 	const int *v;
@@ -2100,6 +2109,18 @@  static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params,
 	return snd_mask_refine(mask, &m);
 }
 
+static int snd_pcm_hw_rule_bclk_ratio(struct snd_pcm_hw_params *params,
+				  struct snd_pcm_hw_rule *rule)
+{
+	struct snd_interval i;
+	struct snd_interval *ratios = hw_param_interval(params, SNDRV_PCM_HW_PARAM_BCLK_RATIO);
+	snd_interval_any(&i);
+	i.openmax = 1;
+	i.min = params_channels(params) * params_width(params);
+	i.integer = 1;
+	return snd_interval_refine(ratios, &i);
+}
+
 static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
 				       struct snd_pcm_hw_rule *rule)
 {
@@ -2180,12 +2201,18 @@  int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
 	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES));
 	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS));
 	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS));
+	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BCLK_RATIO));
 
 	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
 				   snd_pcm_hw_rule_format, NULL,
 				   SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
 	if (err < 0)
 		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BCLK_RATIO,
+				   snd_pcm_hw_rule_bclk_ratio, NULL,
+				   SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (err < 0)
+		return err;
 	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 
 				  snd_pcm_hw_rule_sample_bits, NULL,
 				  SNDRV_PCM_HW_PARAM_FORMAT, 
@@ -2341,6 +2368,11 @@  int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
 	if (err < 0)
 		return err;
 
+	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BCLK_RATIO,
+					   hw->bclk_ratio_min, hw->bclk_ratio_max);
+	if (err < 0)
+		return err;
+
 	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 
 				  snd_pcm_hw_rule_buffer_bytes_max, substream,
 				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 03f36e534050..747026595634 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -381,6 +381,7 @@  static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
 	struct snd_soc_pcm_stream *cpu_stream;
 	unsigned int chan_min = 0, chan_max = UINT_MAX;
 	unsigned int rate_min = 0, rate_max = UINT_MAX;
+	unsigned int bclk_ratio_min = 0, bclk_ratio_max = UINT_MAX;
 	unsigned int rates = UINT_MAX;
 	u64 formats = ULLONG_MAX;
 	int i;
@@ -413,6 +414,8 @@  static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
 			codec_stream = &codec_dai_drv->capture;
 		chan_min = max(chan_min, codec_stream->channels_min);
 		chan_max = min(chan_max, codec_stream->channels_max);
+		bclk_ratio_min = max(bclk_ratio_min, codec_stream->bclk_ratio_min);
+		bclk_ratio_max = min_not_zero(bclk_ratio_max, codec_stream->bclk_ratio_max);
 		rate_min = max(rate_min, codec_stream->rate_min);
 		rate_max = min_not_zero(rate_max, codec_stream->rate_max);
 		formats &= codec_stream->formats;
@@ -443,6 +446,11 @@  static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
 	hw->rate_min = max(hw->rate_min, rate_min);
 	hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max);
 	hw->rate_max = min_not_zero(hw->rate_max, rate_max);
+
+	hw->bclk_ratio_min = max(hw->bclk_ratio_min, cpu_stream->bclk_ratio_min);
+	hw->bclk_ratio_min = max(hw->bclk_ratio_min, bclk_ratio_min);
+	hw->bclk_ratio_max = min_not_zero(hw->bclk_ratio_max, cpu_stream->bclk_ratio_max);
+	hw->bclk_ratio_max = min_not_zero(hw->bclk_ratio_max, bclk_ratio_max);
 }
 
 static int soc_pcm_components_close(struct snd_pcm_substream *substream,