[alsa-devel] [PATCH 2/3] ASoC: pxa-ssp.c, Automatically set TDM when needed
Daniel Mack
daniel at caiaq.de
Wed Aug 12 20:17:24 CEST 2009
Ok, I spent some time in debugging this again, and here are my
findings.
Again, to summarize what this is all about - what I need the code to
produce is the following output:
- syncronous LRCLK
- 64fs on I2SCLK
- I2S data on the first 16bits of each LRCLK edge
-> http://caiaq.de/download/tmp/2.png
So I applied all three patches and added the following call to my board
support code:
snd_soc_dai_set_tdm_slot(cpu_dai, 3, 3, 2, 16);
Which translates into "give me 2 slots with 16 bits each, and distribute
them over the timeline as a 0-1-0-1 pattern". Which is exactly what I
need, right?
But unfortunately, the code didn't make the SSP port do that. I ended up
having a asynchronous clock and the data somewhere where I wouldn't have
expected them: http://caiaq.de/download/tmp/1.png
I did some minor modifications to the patches 2/3 and 3/3 which made it
work for me eventually - find them inline.
Not sure whether I got the theory about the API right, and of course I
didn't test on any other hardware than the one I have.
On Thu, Aug 06, 2009 at 03:55:18PM +0100, Mark Brown wrote:
> * Automatically sets TDM mode for frame_width larger than 32 bits, if
> the user doesn't setup the TDM slots with set_tdm_slot().
> * Reset SSCR0_EDSS and SSCR0_DSS on pxa_ssp_set_dai_fmt.
> * Makes SSCR0_MOD optional.
> * Clears SSCR1_RWOT case SSCR0_MOD is set.
>
> Signed-off-by: Daniel Ribeiro <drwyrm at gmail.com>
> Signed-off-by: Mark Brown <broonie at opensource.wolfsonmicro.com>
> ---
> sound/soc/pxa/pxa-ssp.c | 92 +++++++++++++++++++++++++++--------------------
> 1 files changed, 53 insertions(+), 39 deletions(-)
>
> diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
> index 5b9ed64..d60492e 100644
> --- a/sound/soc/pxa/pxa-ssp.c
> +++ b/sound/soc/pxa/pxa-ssp.c
[...]
> @@ -532,48 +532,70 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
> struct ssp_priv *priv = cpu_dai->private_data;
> struct ssp_device *ssp = priv->dev.ssp;
> int chn = params_channels(params);
> - u32 sscr0;
> - u32 sspsp;
> + u32 sscr0, sscr1, sspsp;
> int width = snd_pcm_format_physical_width(params_format(params));
> - int ttsa = ssp_read_reg(ssp, SSTSA) & 0xf;
> + int slot_width, frame_width = 0;
> +
> + /* check if the user explicitly set a slot_width */
> + sscr0 = ssp_read_reg(ssp, SSCR0);
> +
> + if (sscr0 & (SSCR0_EDSS | SSCR0_DSS))
> + slot_width = (sscr0 & SSCR0_DSS) +
> + (sscr0 & SSCR0_EDSS ? 17 : 1);
> + else
> + frame_width = slot_width = width * chn;
>
> /* generate correct DMA params */
> if (cpu_dai->dma_data)
> kfree(cpu_dai->dma_data);
>
> - /* Network mode with one active slot (ttsa == 1) can be used
> - * to force 16-bit frame width on the wire (for S16_LE), even
> - * with two channels. Use 16-bit DMA transfers for this case.
> - */
> - cpu_dai->dma_data = ssp_get_dma_params(ssp,
> - ((chn == 2) && (ttsa != 1)) || (width == 32),
> + cpu_dai->dma_data = ssp_get_dma_params(ssp, slot_width > 16,
> substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
The condition for the width4 parameter needs to be
(slot_width * slots) > 16 for me. With that parameter set to 0, audio
plays at half speed on my system.
And in patch 3/3,
> diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
> index d60492e..aac85fe 100644
> --- a/sound/soc/pxa/pxa-ssp.c
> +++ b/sound/soc/pxa/pxa-ssp.c
[...]
> @@ -561,6 +543,59 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
> sscr0 |= SSCR0_FPCKE;
> #endif
>
> + sspsp = ssp_read_reg(ssp, SSPSP);
> + switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_I2S:
> + /*
> + * I2S and LEFT_J are stereo only, we have to send data for
> + * both channels.
> + */
> + if (chn == 1)
> + frame_width *= 2;
> +
> + /*
> + * If the user did not use network mode, we assume the codec
> + * is I2S compliant.
> + */
> + if (frame_width > 0) {
> + sspsp |= SSPSP_SFRMWDTH(frame_width / 2);
> + sspsp |= SSPSP_FSRT;
> + } else {
> + /*
> + * Otherwise we assume that it is a single TDM slot, and
> + * the user is abusing set_tdm_slot to support an
> + * out of spec codec.
> + */
> + int slots = ((sscr0 & SSCR0_SlotsPerFrm(8)) >> 24) + 1;
> +
> + /* PXA2XX doesn't support DMYSTOP > 3 */
> + if (slot_width != (width * 2) && !cpu_is_pxa3xx())
> + return -EINVAL;
> +
> + sspsp |= SSPSP_DMYSTRT(1);
> + sspsp |= SSPSP_DMYSTOP(
> + (slot_width * slots) / 2 - width - 1);
> + sspsp |= SSPSP_SFRMWDTH((slot_width * slots) / 2);
These two calculations are wrong my case. What works here is
sspsp |= SSPSP_DMYSTOP(
(slot_width * slots * chn) / 2 - width - 1);
sspsp |= SSPSP_SFRMWDTH((slot_width * slots * chn) / 2);
... which is another multiplication by factor 2. But I'm not sure if I
got the wrong variable with value 2 :)
I then end up having the following register contents:
SSCR0 0xa10003ff
SSCR1 0x00601d08
SSPSP 0x31a00084
SSACD 0x00000060
SSTSA 0x00000003
SSRSA 0x00000003
Daniel
More information about the Alsa-devel
mailing list