[alsa-devel] [PATCH] ASoC: Automatically set TDM if needed on pxa-ssp and allow frame width override when using TDM.

pHilipp Zabel philipp.zabel at gmail.com
Wed Jun 10 14:05:29 CEST 2009


On Wed, Jun 10, 2009 at 1:44 PM, Paul Shen<boshen9 at gmail.com> wrote:
> 2009/6/10 Daniel Ribeiro <drwyrm at gmail.com>:
>> * Extend set_tdm_slot to allow the user to arbitrarily set the frame
>>  width and active TX/RX slots.
>> * Reset SSCR0_EDSS and SSCR0_DSS on pxa_ssp_set_dai_fmt.
>> * Makes SSCR0_MOD optional.
>> * Automatically sets network mode when needed if set_tdm_slot was
>>  never called.
>> * Clears SSCR1_RWOT case SSCR0_MOD is set.
>> * Updates magician.c and wm9081.c for the new set_tdm_slot()
>>
>> (Patch is based on Mark's for-2.6.32 branch)
>>
>> Signed-off-by: Daniel Ribeiro <drwyrm at gmail.com>
>>
>> ---
>>  include/sound/soc-dai.h   |    5 +
>>  sound/soc/codecs/wm9081.c |    4 -
>>  sound/soc/pxa/magician.c  |    2
>>  sound/soc/pxa/pxa-ssp.c   |  119 ++++++++++++++++++++++++++++------------------
>>  sound/soc/soc-core.c      |    9 ++-
>>  5 files changed, 85 insertions(+), 54 deletions(-)
>>
>> diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
>> index 352d7ee..f96cc36 100644
>> --- a/include/sound/soc-dai.h
>> +++ b/include/sound/soc-dai.h
>> @@ -106,7 +106,7 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
>>  int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
>>
>>  int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
>> -       unsigned int mask, int slots);
>> +       unsigned int tx_mask, unsigned int rx_mask, int slots, int frame_width);
>>
>>  int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
>>
>> @@ -140,7 +140,8 @@ struct snd_soc_dai_ops {
>>         */
>>        int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
>>        int (*set_tdm_slot)(struct snd_soc_dai *dai,
>> -               unsigned int mask, int slots);
>> +               unsigned int tx_mask, unsigned int rx_mask,
>> +               int slots, int frame_width);
>>        int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
>>
>>        /*
>> diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
>> index 86fc57e..85c720a 100644
>> --- a/sound/soc/codecs/wm9081.c
>> +++ b/sound/soc/codecs/wm9081.c
>> @@ -1207,7 +1207,7 @@ static int wm9081_set_sysclk(struct snd_soc_dai *codec_dai,
>>  }
>>
>>  static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
>> -                              unsigned int mask, int slots)
>> +       unsigned int tx_mask, unsigned int rx_mask, int slots, int frame_width)
>>  {
>>        struct snd_soc_codec *codec = dai->codec;
>>        unsigned int aif1 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_1);
>> @@ -1219,7 +1219,7 @@ static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
>>
>>        aif1 |= (slots - 1) << WM9081_AIFDAC_TDM_MODE_SHIFT;
>>
>> -       switch (mask) {
>> +       switch (tx_mask) {
>>        case 1:
>>                break;
>>        case 2:
>> diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
>> index c89a3cd..2345869 100644
>> --- a/sound/soc/pxa/magician.c
>> +++ b/sound/soc/pxa/magician.c
>> @@ -188,7 +188,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
>>        if (ret < 0)
>>                return ret;
>>
>> -       ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 1);
>> +       ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 1, 1, 16);
>>        if (ret < 0)
>>                return ret;
>>
>> diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
>> index 46d14f3..f0931e7 100644
>> --- a/sound/soc/pxa/pxa-ssp.c
>> +++ b/sound/soc/pxa/pxa-ssp.c
>> @@ -375,21 +375,34 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
>>  * Set the active slots in TDM/Network mode
>>  */
>>  static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
>> -       unsigned int mask, int slots)
>> +       unsigned int tx_mask, unsigned int rx_mask, int slots, int frame_width)
>>  {
>>        struct ssp_priv *priv = cpu_dai->private_data;
>>        struct ssp_device *ssp = priv->dev.ssp;
>>        u32 sscr0;
>>
>> -       sscr0 = ssp_read_reg(ssp, SSCR0) & ~SSCR0_SlotsPerFrm(7);
>> +       sscr0 = ssp_read_reg(ssp, SSCR0);
>> +       sscr0 &= ~(SSCR0_MOD | SSCR0_SlotsPerFrm(7) | SSCR0_EDSS | SSCR0_DSS);
>> +
>> +       /* set frame width */
>> +       if (frame_width > 16)
>> +               sscr0 |= SSCR0_EDSS | SSCR0_DataSize(frame_width - 16);
>> +       else
>> +               sscr0 |= SSCR0_DataSize(frame_width);
>> +
>> +       if (slots > 1) {
>> +               /* enable network mode */
>> +               sscr0 |= SSCR0_MOD;
>>
> Seems the frame_width has no  such relations with sampe width, the
> sample width is defined on runtime by the audio files.
> Make a example sample with  paly a often used 16bit sample width
> stereo aduio file.
>
> When you play 16bit audio sample with  2*16 bit frame ,
>         frame   --|____________|------------------|_____________|---
>         data      --<======>-<======>-<======>-
>         here the datasize should be SSCR0_DataSize(16), the frame size is 32
>
> also you can play 16bit audio sample with 2*32 frame,
>         frame
> --|_________________________|------------------------------------|____
>         data     --<======>-------------------<======>-------------------<==
>         here the datasize is also DataSize(16), but the frame size is 64.
>
> Do I misunderstand you?

Hmm right, the real frame width is DataSize * slots. So maybe
       /* set data size */
       data_size = frame_width/slots;
       if (data_size > 16)
               sscr0 |= SSCR0_EDSS | SSCR0_DataSize(data_size - 16);
       else
               sscr0 |= SSCR0_DataSize(data_size);
would be more appropriate. Or have that parameter be sample_width
instead, as Mark initially proposed.

>> -       /* set number of active slots */
>> -       sscr0 |= SSCR0_SlotsPerFrm(slots);
>> +               /* set number of active slots */
>> +               sscr0 |= SSCR0_SlotsPerFrm(slots);
>> +
>> +               /* set active slot mask */
>> +               ssp_write_reg(ssp, SSTSA, tx_mask);
>> +               ssp_write_reg(ssp, SSRSA, rx_mask);
>> +       }
>>        ssp_write_reg(ssp, SSCR0, sscr0);
>>
>> -       /* set active slot mask */
>> -       ssp_write_reg(ssp, SSTSA, mask);
>> -       ssp_write_reg(ssp, SSRSA, mask);
>>        return 0;
>>  }
>>
>> @@ -439,8 +452,8 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
>>        }
>>
>>        /* reset port settings */
>> -       sscr0 = ssp_read_reg(ssp, SSCR0) &
>> -               (SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
>> +       sscr0 = ssp_read_reg(ssp, SSCR0) & (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD |
>> +                       SSCR0_ACS | SSCR0_EDSS | SSCR0_DSS);
>>        sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7);
>>        sspsp = 0;
>>
>> @@ -487,7 +500,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
>>        case SND_SOC_DAIFMT_DSP_A:
>>                sspsp |= SSPSP_FSRT;
>>        case SND_SOC_DAIFMT_DSP_B:
>> -               sscr0 |= SSCR0_MOD | SSCR0_PSP;
>> +               sscr0 |= SSCR0_PSP;
>>                sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
>>
>>                switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
>> @@ -537,48 +550,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 frame_width;
>> +
>> +       /* check if the user explicitly set a frame_width */
>> +       sscr0 = ssp_read_reg(ssp, SSCR0);
>> +
>> +       if (sscr0 & (SSCR0_EDSS | SSCR0_DSS))
>> +               frame_width = (sscr0 & SSCR0_DSS) +
>> +                       (sscr0 & SSCR0_EDSS ? 17 : 1);
>> +       else
>> +               frame_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, frame_width > 16,
>>                        substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
>>
>>        /* we can only change the settings if the port is not in use */
>> -       if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE)
>> +       if (sscr0 & SSCR0_SSE)
>>                return 0;
>>
>> -       /* clear selected SSP bits */
>> -       sscr0 = ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS);
>> -       ssp_write_reg(ssp, SSCR0, sscr0);
>> -
>> -       /* bit size */
>> -       sscr0 = ssp_read_reg(ssp, SSCR0);
>> -       switch (params_format(params)) {
>> -       case SNDRV_PCM_FORMAT_S16_LE:
>> +       /* FIXME: What this is for? */
>>  #ifdef CONFIG_PXA3xx
>> -               if (cpu_is_pxa3xx())
>> -                       sscr0 |= SSCR0_FPCKE;
>> +       if (width == 16 && cpu_is_pxa3xx())
>> +               sscr0 |= SSCR0_FPCKE;
>>  #endif
>> -               sscr0 |= SSCR0_DataSize(16);
>> -               break;
>> -       case SNDRV_PCM_FORMAT_S24_LE:
>> -               sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8));
>> -               break;
>> -       case SNDRV_PCM_FORMAT_S32_LE:
>> -               sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16));
>> -               break;
>> +
>> +       if (!(sscr0 & (SSCR0_EDSS | SSCR0_DSS))) {
>> +               /* Frame width not set yet, we are not using network mode */
>> +               if (frame_width > 16)
>> +                       sscr0 |= SSCR0_EDSS | SSCR0_DataSize(frame_width - 16);
>> +               else
>> +                       sscr0 |= SSCR0_DataSize(frame_width);
>> +
>> +               if (frame_width > 32) {
>> +                       /*
>> +                        * Network mode is needed to support this frame_width
>> +                        * We assume that the wire is not networked and setup
>> +                        * a "fake" network mode here.
>> +                        */
>> +                       int slots = frame_width / 32;
>> +
>> +                       sscr0 |= SSCR0_MOD;
>> +                       sscr0 |= SSCR0_SlotsPerFrm(slots);
>> +
>> +                       /*
>> +                        * Set active slots. Only set an active TX slot
>> +                        * if we are going to use it.
>> +                        */
>> +                       ssp_write_reg(ssp, SSRSA, slots - 1);
>> +                       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>> +                               ssp_write_reg(ssp, SSTSA, slots - 1);
>> +               }
>>        }
>> +
>> +       /* If SSCR0_MOD is set we can't use SSCR1_RWOT */
>> +       if (sscr0 & SSCR0_MOD) {
>> +               sscr1 = ssp_read_reg(ssp, SSCR1);
>> +               ssp_write_reg(ssp, SSCR1, sscr1 & ~SSCR1_RWOT);
>> +       }
>> +
>>        ssp_write_reg(ssp, SSCR0, sscr0);
>>
>>        switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
>> @@ -625,14 +660,6 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
>>                break;
>>        }
>>
>> -       /* When we use a network mode, we always require TDM slots
>> -        * - complain loudly and fail if they've not been set up yet.
>> -        */
>> -       if ((sscr0 & SSCR0_MOD) && !ttsa) {
>> -               dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n");
>> -               return -EINVAL;
>> -       }
>> -
>>        dump_registers(ssp);
>>
>>        return 0;
>> diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
>> index e1a920c..69becf2 100644
>> --- a/sound/soc/soc-core.c
>> +++ b/sound/soc/soc-core.c
>> @@ -2133,17 +2133,20 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
>>  /**
>>  * snd_soc_dai_set_tdm_slot - configure DAI TDM.
>>  * @dai: DAI
>> - * @mask: DAI specific mask representing used slots.
>> + * @tx_mask: bitmask representing active TX slots.
>> + * @rx_mask: bitmask representing active RX slots.
>>  * @slots: Number of slots in use.
>> + * @frame_width: Width in bits for each slot.
>>  *
>>  * Configures a DAI for TDM operation. Both mask and slots are codec and DAI
>>  * specific.
>>  */
>>  int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
>> -       unsigned int mask, int slots)
>> +       unsigned int tx_mask, unsigned int rx_mask, int slots, int frame_width)
>>  {
>>        if (dai->ops && dai->ops->set_tdm_slot)
>> -               return dai->ops->set_tdm_slot(dai, mask, slots);
>> +               return dai->ops->set_tdm_slot(dai, tx_mask, rx_mask,
>> +                               slots, frame_width);
>>        else
>>                return -EINVAL;
>>  }
>>
>> --
>> Daniel Ribeiro
>>
>
>
>
> --
> Paul Shen
>

regards
Philipp


More information about the Alsa-devel mailing list