Troy,

I tried this patch on DM644x EVM and following are my observations:

1. Playback works fine.
2. Record has some issues. Recorded stream has Loud volume on
the right side, and on the left, volume is very low.
3. Loopback (arecord -f cd | aplay -f cd) has some issues.
Audio is bit noisy here. I think if arecord issues are fixed,
this may go away.

On DM355, though arecord and aplay work fine independently. But
once loopback is run, arecord records data only on one side.
Loopback is noisy with intermittent overrun/underrun messages both
on DM355 and DM644x EVMs.

Regards, Sudhakar

> -----Original Message-----
> From: davinci-linux-open-source-
> [email protected] [mailto:davinci-linux-
> [email protected]] On Behalf
> Of Troy Kisky
> Sent: Wednesday, May 20, 2009 5:18 AM
> To: [email protected]
> Subject: [PATCH] RFC: ARM: DaVinci: ASoc use iram to buffer sound
> 
> Use the sram(iram) to avoid underrun on audio.
> I will clean this up after someone says it
> works for them.
> 
> Signed-off-by: Troy Kisky <[email protected]>
> ---
>  include/sound/soc-dai.h         |    2 +
>  sound/soc/davinci/davinci-i2s.c |  336 ++++++++++++++++---------
>  sound/soc/davinci/davinci-pcm.c |  516 ++++++++++++++++++++++++++++++-
> --------
>  sound/soc/davinci/davinci-pcm.h |    3 +-
>  4 files changed, 618 insertions(+), 239 deletions(-)
> 
> diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
> index 1367647..fe9bfa3 100644
> --- a/include/sound/soc-dai.h
> +++ b/include/sound/soc-dai.h
> @@ -137,6 +137,7 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai
> *dai, int mute);
>   * This structure covers the clocking, formating and ALSA operations
> for each
>   * interface a
>   */
> +/* ASoC DAI ops */
>  struct snd_soc_dai_ops {
>       /*
>        * DAI clocking configuration, all optional.
> @@ -162,6 +163,7 @@ struct snd_soc_dai_ops {
>        * Called by soc-core to minimise any pops.
>        */
>       int (*digital_mute)(struct snd_soc_dai *dai, int mute);
> +     int (*inform_channel_order)(struct snd_soc_dai *dai, int
> right_first);
> 
>       /*
>        * ALSA PCM audio operations - all optional.
> diff --git a/sound/soc/davinci/davinci-i2s.c
> b/sound/soc/davinci/davinci-i2s.c
> index b1ea52f..725a019 100644
> --- a/sound/soc/davinci/davinci-i2s.c
> +++ b/sound/soc/davinci/davinci-i2s.c
> @@ -63,6 +63,7 @@
>  #define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5)
>  #define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8)
>  #define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16)
> +#define DAVINCI_MCBSP_RCR_RFIG               (1 << 18)
>  #define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21)
> 
>  #define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5)
> @@ -85,14 +86,6 @@
>  #define DAVINCI_MCBSP_PCR_FSRM               (1 << 10)
>  #define DAVINCI_MCBSP_PCR_FSXM               (1 << 11)
> 
> -#define MOD_REG_BIT(val, mask, set) do { \
> -     if (set) { \
> -             val |= mask; \
> -     } else { \
> -             val &= ~mask; \
> -     } \
> -} while (0)
> -
>  enum {
>       DAVINCI_MCBSP_WORD_8 = 0,
>       DAVINCI_MCBSP_WORD_12,
> @@ -112,8 +105,13 @@ static struct davinci_pcm_dma_params
> davinci_i2s_pcm_in = {
> 
>  struct davinci_mcbsp_dev {
>       void __iomem                    *base;
> +#define MOD_DSP_A    0
> +#define MOD_DSP_B    1
> +     int                             mode;
> +     u32                             pcr;
>       struct clk                      *clk;
>       struct davinci_pcm_dma_params   *dma_params[2];
> +     struct snd_soc_dai *codec_dai;
>  };
> 
>  static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev
> *dev,
> @@ -127,96 +125,100 @@ static inline u32 davinci_mcbsp_read_reg(struct
> davinci_mcbsp_dev *dev, int reg)
>       return __raw_readl(dev->base + reg);
>  }
> 
> -static void davinci_mcbsp_start(struct snd_pcm_substream *substream)
> +static void toggle_clock(struct davinci_mcbsp_dev *dev, int playback)
> +{
> +     u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP :
> DAVINCI_MCBSP_PCR_CLKRP;
> +     /* The clock needs to toggle to complete reset.
> +      * So, fake it by toggling the clk polarity.
> +      */
> +     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr ^ m);
> +     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr);
> +}
> +
> +static void davinci_mcbsp_start(struct davinci_mcbsp_dev *dev,
> +             struct snd_pcm_substream *substream)
>  {
>       struct snd_soc_pcm_runtime *rtd = substream->private_data;
> -     struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
>       struct snd_soc_device *socdev = rtd->socdev;
>       struct snd_soc_platform *platform = socdev->card->platform;
> -     u32 w;
> -     int ret;
> -
> -     /* Start the sample generator and enable transmitter/receiver */
> -     w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> -     MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
> -     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
> +     int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> +     u32 spcr;
> +     u32 mask = playback ? DAVINCI_MCBSP_SPCR_XRST :
> DAVINCI_MCBSP_SPCR_RRST;
> +     spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> +     if (spcr & mask) {
> +             /* start off disabled */
> +             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
> +                             spcr & ~mask);
> +             toggle_clock(dev, playback);
> +     }
> +     if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM |
> +                     DAVINCI_MCBSP_PCR_CLKXM | DAVINCI_MCBSP_PCR_CLKRM))
{
> +             /* Start the sample generator */
> +             spcr |= DAVINCI_MCBSP_SPCR_GRST;
> +             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
> +     }
> 
> -     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +     if (playback) {
>               /* Stop the DMA to avoid data loss */
>               /* while the transmitter is out of reset to handle XSYNCERR
> */
>               if (platform->pcm_ops->trigger) {
> -                     ret = platform->pcm_ops->trigger(substream,
> +                     int ret = platform->pcm_ops->trigger(substream,
>                               SNDRV_PCM_TRIGGER_STOP);
>                       if (ret < 0)
>                               printk(KERN_DEBUG "Playback DMA stop
failed\n");
>               }
> 
>               /* Enable the transmitter */
> -             w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> -             MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
> -             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
> +             spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> +             spcr |= DAVINCI_MCBSP_SPCR_XRST;
> +             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
> 
>               /* wait for any unexpected frame sync error to occur */
>               udelay(100);
> 
>               /* Disable the transmitter to clear any outstanding XSYNCERR
> */
> -             w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> -             MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
> -             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
> +             spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> +             spcr &= ~DAVINCI_MCBSP_SPCR_XRST;
> +             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
> +             toggle_clock(dev, playback);
> 
>               /* Restart the DMA */
>               if (platform->pcm_ops->trigger) {
> -                     ret = platform->pcm_ops->trigger(substream,
> +                     int ret = platform->pcm_ops->trigger(substream,
>                               SNDRV_PCM_TRIGGER_START);
>                       if (ret < 0)
>                               printk(KERN_DEBUG "Playback DMA start
failed\n");
>               }
> -             /* Enable the transmitter */
> -             w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> -             MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
> -             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
> -
> -     } else {
> -
> -             /* Enable the reciever */
> -             w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> -             MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
> -             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
>       }
> 
> +     /* Enable transmitter or receiver */
> +     spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> +     spcr |= mask;
> 
> -     /* Start frame sync */
> -     w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> -     MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_FRST, 1);
> -     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
> +     if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM))
> {
> +             /* Start frame sync */
> +             spcr |= DAVINCI_MCBSP_SPCR_FRST;
> +     }
> +     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
>  }
> 
> -static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
> +static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int
> playback)
>  {
> -     struct snd_soc_pcm_runtime *rtd = substream->private_data;
> -     struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
> -     u32 w;
> +     u32 spcr;
> 
>       /* Reset transmitter/receiver and sample rate/frame sync
> generators */
> -     w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> -     MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST |
> -                    DAVINCI_MCBSP_SPCR_FRST, 0);
> -     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> -             MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
> -     else
> -             MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 0);
> -     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
> +     spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> +     spcr &= ~(DAVINCI_MCBSP_SPCR_GRST | DAVINCI_MCBSP_SPCR_FRST);
> +     spcr &= playback ? ~DAVINCI_MCBSP_SPCR_XRST :
> ~DAVINCI_MCBSP_SPCR_RRST;
> +     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
> +     toggle_clock(dev, playback);
>  }
> 
>  static int davinci_i2s_startup(struct snd_pcm_substream *substream,
> -                            struct snd_soc_dai *dai)
> +                            struct snd_soc_dai *cpu_dai)
>  {
> -     struct snd_soc_pcm_runtime *rtd = substream->private_data;
> -     struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
> -     struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
> -
> +     struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
>       cpu_dai->dma_data = dev->dma_params[substream->stream];
> -
>       return 0;
>  }
> 
> @@ -228,12 +230,11 @@ static int davinci_i2s_set_dai_fmt(struct
> snd_soc_dai *cpu_dai,
>       struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
>       unsigned int pcr;
>       unsigned int srgr;
> -     unsigned int rcr;
> -     unsigned int xcr;
>       srgr = DAVINCI_MCBSP_SRGR_FSGM |
>               DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
>               DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
> 
> +     /* set master/slave audio interface */
>       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
>       case SND_SOC_DAIFMT_CBS_CFS:
>               /* cpu is master */
> @@ -258,11 +259,8 @@ static int davinci_i2s_set_dai_fmt(struct
> snd_soc_dai *cpu_dai,
>               return -EINVAL;
>       }
> 
> -     rcr = DAVINCI_MCBSP_RCR_RFRLEN1(1);
> -     xcr = DAVINCI_MCBSP_XCR_XFIG | DAVINCI_MCBSP_XCR_XFRLEN1(1);
> +     /* interface format */
>       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> -     case SND_SOC_DAIFMT_DSP_B:
> -             break;
>       case SND_SOC_DAIFMT_I2S:
>               /* Davinci doesn't support TRUE I2S, but some codecs will
> have
>                * the left and right channels contiguous. This allows
> @@ -282,8 +280,10 @@ static int davinci_i2s_set_dai_fmt(struct
> snd_soc_dai *cpu_dai,
>                */
>               fmt ^= SND_SOC_DAIFMT_NB_IF;
>       case SND_SOC_DAIFMT_DSP_A:
> -             rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
> -             xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
> +             dev->mode = MOD_DSP_A;
> +             break;
> +     case SND_SOC_DAIFMT_DSP_B:
> +             dev->mode = MOD_DSP_B;
>               break;
>       default:
>               printk(KERN_ERR "%s:bad format\n", __func__);
> @@ -343,9 +343,8 @@ static int davinci_i2s_set_dai_fmt(struct
> snd_soc_dai *cpu_dai,
>               return -EINVAL;
>       }
>       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
> +     dev->pcr = pcr;
>       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
> -     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
> -     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
>       return 0;
>  }
> 
> @@ -356,92 +355,183 @@ static int davinci_i2s_hw_params(struct
> snd_pcm_substream *substream,
>       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>       struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai-
> >dma_data;
>       struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
> +     struct snd_soc_dai *codec_dai = dev->codec_dai;
>       struct snd_interval *i = NULL;
>       int mcbsp_word_length;
> -     u32 w;
> +     int bits_per_sample;
> +     int bits_per_frame;
> +     unsigned int rcr, xcr, srgr;
> +     int channels;
> +     int format;
> +     int element_cnt = 1;
> +     u32 spcr;
> +     int right_first = 0;
> +
> +     i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
> +     bits_per_sample = snd_interval_value(i);
> +     /* always 2 samples/frame, mono will convert to stereo */
> +     bits_per_frame = bits_per_sample << 1;
> +     srgr = DAVINCI_MCBSP_SRGR_FSGM |
> +             DAVINCI_MCBSP_SRGR_FPER(bits_per_frame - 1) |
> +             DAVINCI_MCBSP_SRGR_FWID(bits_per_sample - 1);
> 
>       /* general line settings */
> -     w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> -     if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
> -             w |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
> -             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
> +     spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
> +     spcr |= DAVINCI_MCBSP_SPCR_FREE;
> +     spcr |= (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
> +                     DAVINCI_MCBSP_SPCR_XINTM(3) :
> +                     DAVINCI_MCBSP_SPCR_RINTM(3);
> +     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
> +
> +     rcr = DAVINCI_MCBSP_RCR_RFIG;
> +     xcr = DAVINCI_MCBSP_XCR_XFIG;
> +     if (dev->mode == MOD_DSP_B) {
> +             rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0);
> +             xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0);
>       } else {
> -             w |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
> -             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
> +             rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
> +             xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
>       }
> -
> -     i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
> -     w = DAVINCI_MCBSP_SRGR_FSGM;
> -     MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1),
> 1);
> -
> -     i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
> -     MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1),
> 1);
> -     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
> -
> +     channels = params_channels(params);
> +     format = params_format(params);
>       /* Determine xfer data type */
> -     switch (params_format(params)) {
> -     case SNDRV_PCM_FORMAT_S8:
> -             dma_params->data_type = 1;
> -             mcbsp_word_length = DAVINCI_MCBSP_WORD_8;
> -             break;
> -     case SNDRV_PCM_FORMAT_S16_LE:
> -             dma_params->data_type = 2;
> -             mcbsp_word_length = DAVINCI_MCBSP_WORD_16;
> -             break;
> -     case SNDRV_PCM_FORMAT_S32_LE:
> -             dma_params->data_type = 4;
> -             mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
> -             break;
> -     default:
> -             printk(KERN_WARNING "davinci-i2s: unsupported PCM
format\n");
> -             return -EINVAL;
> -     }
> -
> -     if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
> -             w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
> -             MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length)
|
> -                            DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length),
1);
> -             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
> -
> +     if (channels == 2) {
> +             /* Combining both channels into 1 element will x10 the
> +              * amount of time between servicing the dma channel,
increase
> +              * effiency, and reduce the chance of overrun/underrun. But,
> +              * it will result in the left & right channels being
swapped.
> +              * So, let the codec know to swap them back.
> +              *
> +              * It is x10 instead of x2 because the clock from the codec
> +              * runs at mclk speed, independent of the sample rate.
> +              * So, having an entire frame at once means it has to be
> +              * serviced at the sample rate instead of the mclk speed.
> +              *
> +              * In the now very unlikely case that an underrun still
> +              * occurs, both the left and right samples will be repeated
> +              * so that no pops are heard, and the left and right
channels
> +              * won't end up being swapped because of the underrun.
> +              */
> +             right_first = 1;
> +             dma_params->convert_mono_stereo = 0;
> +             switch (format) {
> +             case SNDRV_PCM_FORMAT_S8:
> +                     dma_params->data_type = 2;      /* 2 byte frame */
> +                     mcbsp_word_length = DAVINCI_MCBSP_WORD_16;
> +                     break;
> +             case SNDRV_PCM_FORMAT_S16_LE:
> +                     dma_params->data_type = 4;      /* 4 byte frame */
> +                     mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
> +                     break;
> +             case SNDRV_PCM_FORMAT_S32_LE:
> +                     right_first = 0;
> +                     element_cnt = 2;
> +                     dma_params->data_type = 4;      /* 4 byte element */
> +                     mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
> +                     break;
> +             default:
> +                     printk(KERN_WARNING
> +                                     "davinci-i2s: unsupported PCM
format");
> +                     return -EINVAL;
> +             }
>       } else {
> -             w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
> -             MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length)
|
> -                            DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length),
1);
> -             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
> -
> +             dma_params->convert_mono_stereo = 1;
> +             /* 1 element in ram becomes 2 for stereo */
> +             element_cnt = 2;
> +             switch (format) {
> +             case SNDRV_PCM_FORMAT_S8:
> +                     /* 1 byte frame in ram */
> +                     dma_params->data_type = 1;
> +                     mcbsp_word_length = DAVINCI_MCBSP_WORD_8;
> +                     break;
> +             case SNDRV_PCM_FORMAT_S16_LE:
> +                     /* 2 byte frame in ram */
> +                     dma_params->data_type = 2;
> +                     mcbsp_word_length = DAVINCI_MCBSP_WORD_16;
> +                     break;
> +             case SNDRV_PCM_FORMAT_S32_LE:
> +                     /* 4 byte element */
> +                     dma_params->data_type = 4;
> +                     mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
> +                     break;
> +             default:
> +                     printk(KERN_WARNING
> +                                     "davinci-i2s: unsupported PCM
format");
> +                     return -EINVAL;
> +             }
>       }
> +     if (codec_dai->ops->inform_channel_order)
> +             codec_dai->ops->inform_channel_order(codec_dai,
right_first);
> +     rcr |=  DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
> +     xcr |=  DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
> +
> +     rcr |=  DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
> +             DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
> +     xcr |=  DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
> +             DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length);
> +     davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
> +     else
> +             davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
>       return 0;
>  }
> 
> +static int davinci_i2s_prepare(struct snd_pcm_substream *substream,
> +             struct snd_soc_dai *dai)
> +{
> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +     struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
> +     int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> +     davinci_mcbsp_stop(dev, playback);
> +     if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0) {
> +             /* codec is master */
> +             davinci_mcbsp_start(dev, substream);
> +     }
> +     return 0;
> +}
>  static int davinci_i2s_trigger(struct snd_pcm_substream *substream,
> int cmd,
>                              struct snd_soc_dai *dai)
>  {
> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +     struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
>       int ret = 0;
> +     int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> +     if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0)
> +             return 0;       /* return if codec is master */
> 
>       switch (cmd) {
>       case SNDRV_PCM_TRIGGER_START:
>       case SNDRV_PCM_TRIGGER_RESUME:
>       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> -             davinci_mcbsp_start(substream);
> +             davinci_mcbsp_start(dev, substream);
>               break;
>       case SNDRV_PCM_TRIGGER_STOP:
>       case SNDRV_PCM_TRIGGER_SUSPEND:
>       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> -             davinci_mcbsp_stop(substream);
> +             davinci_mcbsp_stop(dev, playback);
>               break;
>       default:
>               ret = -EINVAL;
>       }
> -
>       return ret;
>  }
> 
> +static void davinci_i2s_shutdown(struct snd_pcm_substream *substream,
> +             struct snd_soc_dai *dai)
> +{
> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +     struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
> +     davinci_mcbsp_stop(dev, 1);
> +}
> +
>  static int davinci_i2s_probe(struct platform_device *pdev,
>                            struct snd_soc_dai *dai)
>  {
>       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
>       struct snd_soc_card *card = socdev->card;
>       struct snd_soc_dai *cpu_dai = card->dai_link->cpu_dai;
> +     struct snd_soc_dai *codec_dai = card->dai_link->codec_dai;
>       struct davinci_mcbsp_dev *dev;
>       struct resource *mem, *ioarea;
>       struct evm_snd_platform_data *pdata;
> @@ -465,6 +555,7 @@ static int davinci_i2s_probe(struct platform_device
> *pdev,
>               ret = -ENOMEM;
>               goto err_release_region;
>       }
> +     dev->codec_dai = codec_dai;
> 
>       cpu_dai->private_data = dev;
> 
> @@ -517,10 +608,13 @@ static void davinci_i2s_remove(struct
> platform_device *pdev,
>       release_mem_region(mem->start, (mem->end - mem->start) + 1);
>  }
> 
> -#define DAVINCI_I2S_RATES    SNDRV_PCM_RATE_8000_96000
> +#define DAVINCI_I2S_RATES    (SNDRV_PCM_RATE_8000_96000 |\
> +     SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_KNOT |
> SNDRV_PCM_RATE_CONTINUOUS)
> 
>  static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
>       .startup        = davinci_i2s_startup,
> +     .shutdown       = davinci_i2s_shutdown,
> +     .prepare        = davinci_i2s_prepare,
>       .trigger        = davinci_i2s_trigger,
>       .hw_params      = davinci_i2s_hw_params,
>       .set_fmt        = davinci_i2s_set_dai_fmt,
> @@ -532,12 +626,14 @@ struct snd_soc_dai davinci_i2s_dai = {
>       .probe = davinci_i2s_probe,
>       .remove = davinci_i2s_remove,
>       .playback = {
> -             .channels_min = 2,
> +             /* fixme, codecs shouldn't need to lie */
> +             .channels_min = 1, /* lie, I2S dma will convert to stereo*/
>               .channels_max = 2,
>               .rates = DAVINCI_I2S_RATES,
>               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>       .capture = {
> -             .channels_min = 2,
> +             /* fixme, codecs shouldn't need to lie */
> +             .channels_min = 1, /* lie, I2S dma will convert from stereo
> */
>               .channels_max = 2,
>               .rates = DAVINCI_I2S_RATES,
>               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
> diff --git a/sound/soc/davinci/davinci-pcm.c
> b/sound/soc/davinci/davinci-pcm.c
> index 48bd9e2..4c5c06b 100644
> --- a/sound/soc/davinci/davinci-pcm.c
> +++ b/sound/soc/davinci/davinci-pcm.c
> @@ -3,6 +3,7 @@
>   *
>   * Author:      Vladimir Barinov, <[email protected]>
>   * Copyright:   (C) 2007 MontaVista Software, Inc.,
> <[email protected]>
> + * added IRAM ping/pong (C) 2008 Troy Kisky
> <[email protected]>
>   *
>   * This program is free software; you can redistribute it and/or
> modify
>   * it under the terms of the GNU General Public License version 2 as
> @@ -23,14 +24,29 @@
> 
>  #include <asm/dma.h>
>  #include <mach/edma.h>
> +#include <mach/sram.h>
> 
>  #include "davinci-pcm.h"
> 
>  #define DAVINCI_PCM_DEBUG 0
>  #if DAVINCI_PCM_DEBUG
> -#define DPRINTK(x...) printk(KERN_DEBUG x)
> +#define DPRINTK(format, arg...) printk(KERN_DEBUG format, ## arg)
> +static void print_buf_info(int lch, char *name)
> +{
> +     struct edmacc_param p;
> +     if (lch < 0)
> +             return;
> +     edma_read_slot(lch, &p);
> +     printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n",
> +                     name, lch, p.opt, p.src, p.a_b_cnt, p.dst);
> +     printk(KERN_DEBUG "    src_dst_bidx=%x link_bcntrld=%x
> src_dst_cidx=%x ccnt=%x\n",
> +                     p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx,
p.ccnt);
> +}
>  #else
> -#define DPRINTK(x...)
> +#define DPRINTK(format, arg...) do {} while (0)
> +static void print_buf_info(int lch, char *name)
> +{
> +}
>  #endif
> 
>  static struct snd_pcm_hardware davinci_pcm_hardware = {
> @@ -49,131 +65,318 @@ static struct snd_pcm_hardware
> davinci_pcm_hardware = {
>       .channels_max = 2,
>       .buffer_bytes_max = 128 * 1024,
>       .period_bytes_min = 32,
> -     .period_bytes_max = 8 * 1024,
> +     .period_bytes_max = 7 * 512,    /* This is size of ping + pong
> buffer*/
>       .periods_min = 16,
>       .periods_max = 255,
>       .fifo_size = 0,
>  };
> 
> +/*
> + * How it works....
> + *
> + * Playback:
> + * ram_params - copys 2*ping_size from start of SDRAM to iram,
> + *   links to ram_link_lch2
> + * ram_link_lch2 - copys rest of SDRAM to iram in ping_size units,
> + *   links to ram_link_lch
> + * ram_link_lch - copys entire SDRAM to iram in ping_size uints,
> + *   links to self
> + *
> + * asp_params - same as asp_link_lch[0]
> + * asp_link_lch[0] - copys from lower half of iram to asp port
> + *   links to asp_link_lch[1], triggers iram copy event on completion
> + * asp_link_lch[1] - copys from upper half of iram to asp port
> + *   links to asp_link_lch[0], triggers iram copy event on completion
> + *   triggers interrupt only needed to let upper SOC levels update
> position
> + *   in stream on completion
> + *
> + * When playback is started:
> + *   ram_params started
> + *   asp_params started
> + *
> + * Capture:
> + * ram_params - same as ram_link_lch,
> + *   links to ram_link_lch
> + * ram_link_lch - same as playback
> + *   links to self
> + *
> + * asp_params - same as playback
> + * asp_link_lch[0] - same as playback
> + * asp_link_lch[1] - same as playback
> + *
> + * When capture is started:
> + *   asp_params started
> + */
>  struct davinci_runtime_data {
>       spinlock_t lock;
> -     int period;             /* current DMA period */
> -     int master_lch;         /* Master DMA channel */
> -     int slave_lch;          /* linked parameter RAM reload slot */
> -     struct davinci_pcm_dma_params *params;  /* DMA params */
> +     int asp_master_lch;             /* Master DMA channel */
> +     int asp_link_lch[2];    /* asp parameter link channel */
> +     int ram_master_lch;
> +     int ram_link_lch;
> +     int ram_link_lch2;
> +     struct edmacc_param asp_params;
> +     struct edmacc_param ram_params;
>  };
> 
> -static void davinci_pcm_enqueue_dma(struct snd_pcm_substream
> *substream)
> -{
> -     struct davinci_runtime_data *prtd = substream->runtime-
> >private_data;
> -     struct snd_pcm_runtime *runtime = substream->runtime;
> -     int lch = prtd->slave_lch;
> -     unsigned int period_size;
> -     unsigned int dma_offset;
> -     dma_addr_t dma_pos;
> -     dma_addr_t src, dst;
> -     unsigned short src_bidx, dst_bidx;
> -     unsigned int data_type;
> -     unsigned int count;
> -
> -     period_size = snd_pcm_lib_period_bytes(substream);
> -     dma_offset = prtd->period * period_size;
> -     dma_pos = runtime->dma_addr + dma_offset;
> -
> -     pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
> -             "dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
> -
> -     data_type = prtd->params->data_type;
> -     count = period_size / data_type;
> -
> -     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> -             src = dma_pos;
> -             dst = prtd->params->dma_addr;
> -             src_bidx = data_type;
> -             dst_bidx = 0;
> -     } else {
> -             src = prtd->params->dma_addr;
> -             dst = dma_pos;
> -             src_bidx = 0;
> -             dst_bidx = data_type;
> -     }
> -
> -     edma_set_src(lch, src, INCR, W8BIT);
> -     edma_set_dest(lch, dst, INCR, W8BIT);
> -     edma_set_src_index(lch, src_bidx, 0);
> -     edma_set_dest_index(lch, dst_bidx, 0);
> -     edma_set_transfer_params(lch, data_type, count, 1, 0, ASYNC);
> -
> -     prtd->period++;
> -     if (unlikely(prtd->period >= runtime->periods))
> -             prtd->period = 0;
> -}
> 
>  static void davinci_pcm_dma_irq(unsigned lch, u16 ch_status, void
> *data)
>  {
>       struct snd_pcm_substream *substream = data;
>       struct davinci_runtime_data *prtd = substream->runtime-
> >private_data;
> -
> +     print_buf_info(prtd->ram_master_lch, "i ram_master_lch");
>       pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status);
> -
>       if (unlikely(ch_status != DMA_COMPLETE))
>               return;
> 
>       if (snd_pcm_running(substream)) {
>               snd_pcm_period_elapsed(substream);
> +     }
> +}
> +
> 
> -             spin_lock(&prtd->lock);
> -             davinci_pcm_enqueue_dma(substream);
> -             spin_unlock(&prtd->lock);
> +/*
> + * This is called after runtime->dma_addr, period_bytes and data_type
> are valid
> + */
> +static int davinci_pcm_dma_setup(struct snd_pcm_substream *substream)
> +{
> +     unsigned short ram_src_cidx, ram_dst_cidx;
> +     struct snd_pcm_runtime *runtime = substream->runtime;
> +     struct davinci_runtime_data *prtd = runtime->private_data;
> +     struct snd_dma_buffer *iram_dma =
> +             (struct snd_dma_buffer *)substream->dma_buffer.private_data;
> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +     struct davinci_pcm_dma_params *dma_data = rtd->dai->cpu_dai-
> >dma_data;
> +     unsigned int data_type = dma_data->data_type;
> +     unsigned int convert_mono_stereo = dma_data->convert_mono_stereo;
> +     /* divide by 2 for ping/pong */
> +     unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1;
> +     int lch = prtd->asp_link_lch[1];
> +     if ((data_type == 0) || (data_type > 4)) {
> +             printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type);
> +             return -EINVAL;
> +     }
> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +             dma_addr_t asp_src_pong = iram_dma->addr + ping_size;
> +             ram_src_cidx = ping_size;
> +             ram_dst_cidx = -ping_size;
> +             edma_set_src(lch, asp_src_pong, INCR, W8BIT);
> +
> +             lch = prtd->asp_link_lch[0];
> +             if (convert_mono_stereo) {
> +                     edma_set_src_index(lch, 0, data_type);
> +                     lch = prtd->asp_link_lch[1];
> +                     edma_set_src_index(lch, 0, data_type);
> +             } else {
> +                     edma_set_src_index(lch, data_type, 0);
> +                     lch = prtd->asp_link_lch[1];
> +                     edma_set_src_index(lch, data_type, 0);
> +             }
> +
> +             lch = prtd->ram_link_lch;
> +             edma_set_src(lch, runtime->dma_addr, INCR, W32BIT);
> +     } else {
> +             dma_addr_t asp_dst_pong = iram_dma->addr + ping_size;
> +             ram_src_cidx = -ping_size;
> +             ram_dst_cidx = ping_size;
> +             edma_set_dest(lch, asp_dst_pong, INCR, W8BIT);
> +
> +             lch = prtd->asp_link_lch[0];
> +             if (convert_mono_stereo) {
> +                     edma_set_dest_index(lch, 0, data_type);
> +                     lch = prtd->asp_link_lch[1];
> +                     edma_set_dest_index(lch, 0, data_type);
> +             } else {
> +                     edma_set_dest_index(lch, data_type, 0);
> +                     lch = prtd->asp_link_lch[1];
> +                     edma_set_dest_index(lch, data_type, 0);
> +             }
> +             lch = prtd->ram_link_lch;
> +             edma_set_dest(lch, runtime->dma_addr, INCR, W32BIT);
> +     }
> +
> +     lch = prtd->asp_link_lch[0];
> +     if (convert_mono_stereo) {
> +             /*
> +              * Each byte is sent twice, so
> +              * A_CNT * B_CNT * C_CNT = 2 * ping_size
> +              */
> +             edma_set_transfer_params(lch, data_type,
> +                             2, ping_size/data_type, 2, ASYNC);
> +             lch = prtd->asp_link_lch[1];
> +             edma_set_transfer_params(lch, data_type,
> +                             2, ping_size/data_type, 2, ASYNC);
> +     } else {
> +             edma_set_transfer_params(lch, data_type,
> +                             ping_size/data_type, 1, 0, ASYNC);
> +             lch = prtd->asp_link_lch[1];
> +             edma_set_transfer_params(lch, data_type,
> +                             ping_size/data_type, 1, 0, ASYNC);
> +     }
> +
> +
> +     lch = prtd->ram_link_lch;
> +     edma_set_src_index(lch, ping_size, ram_src_cidx);
> +     edma_set_dest_index(lch, ping_size, ram_dst_cidx);
> +     edma_set_transfer_params(lch, ping_size, 2,
> +                     runtime->periods, 2, ASYNC);
> +
> +     /* init master params */
> +     edma_read_slot(prtd->asp_link_lch[0], &prtd->asp_params);
> +     edma_read_slot(prtd->ram_link_lch, &prtd->ram_params);
> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +             struct edmacc_param p_ram;
> +             /* Copy entire iram buffer before playback started */
> +             prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1);
> +             /* 0 dst_bidx */
> +             prtd->ram_params.src_dst_bidx = (ping_size << 1);
> +             /* 0 dst_cidx */
> +             prtd->ram_params.src_dst_cidx = (ping_size << 1);
> +             prtd->ram_params.ccnt = 1;
> +
> +             /* Skip 1st period */
> +             edma_read_slot(prtd->ram_link_lch, &p_ram);
> +             p_ram.src += (ping_size << 1);
> +             p_ram.ccnt -= 1;
> +             edma_write_slot(prtd->ram_link_lch2, &p_ram);
> +/*
> + * when 1st started, ram -> iram dma channel will fill the entire iram
> + * Then, whenever a ping/pong asp buffer finishes, 1/2 iram will be
> filled
> + */
> +             prtd->ram_params.link_bcntrld = prtd->ram_link_lch2 << 5;
>       }
> +     return 0;
>  }
> 
> +/* 1 asp tx or rx channel using 2 parameter channels
> + * 1 ram to/from iram channel using 1 parameter channel
> + *
> + * Playback
> + * ram copy channel kicks off first,
> + * 1st ram copy of entire iram buffer completion kicks off asp channel
> + * asp tcc always kicks off ram copy of 1/2 iram buffer
> + *
> + * Record
> + * asp channel starts, tcc kicks off ram copy
> + */
>  static int davinci_pcm_dma_request(struct snd_pcm_substream
> *substream)
>  {
> -     struct davinci_runtime_data *prtd = substream->runtime-
> >private_data;
> +     struct snd_pcm_runtime *runtime = substream->runtime;
> +     struct snd_dma_buffer *iram_dma =
> +             (struct snd_dma_buffer *)substream->dma_buffer.private_data;
> +     struct davinci_runtime_data *prtd = runtime->private_data;
>       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>       struct davinci_pcm_dma_params *dma_data = rtd->dai->cpu_dai-
> >dma_data;
>       struct edmacc_param p_ram;
> +
> +     dma_addr_t asp_src_ping;
> +     dma_addr_t asp_dst_ping;
> +
>       int ret;
> +     int lch;
> 
> -     if (!dma_data)
> +     if ((!dma_data) || (!iram_dma))
>               return -ENODEV;
> 
> -     prtd->params = dma_data;
> -
> -     /* Request master DMA channel */
> -     ret = edma_alloc_channel(prtd->params->channel,
> +     /* Request ram master channel */
> +     ret = prtd->ram_master_lch = edma_alloc_channel(EDMA_CHANNEL_ANY,
>                                 davinci_pcm_dma_irq, substream,
> -                               EVENTQ_0);
> +                               EVENTQ_1);
>       if (ret < 0)
> -             return ret;
> -     prtd->master_lch = ret;
> +             goto exit1;
> 
> -     /* Request parameter RAM reload slot */
> -     ret = edma_alloc_slot(EDMA_SLOT_ANY);
> -     if (ret < 0) {
> -             edma_free_channel(prtd->master_lch);
> -             return ret;
> +     /* Request ram link channel */
> +     ret = prtd->ram_link_lch = edma_alloc_slot(EDMA_SLOT_ANY);
> +     if (ret < 0)
> +             goto exit2;
> +
> +     /* Request asp master DMA channel */
> +     ret = prtd->asp_master_lch = edma_alloc_channel(dma_data->channel,
> +                       davinci_pcm_dma_irq, substream,
> +                       EVENTQ_0);
> +     if (ret < 0)
> +             goto exit3;
> +
> +     /* Request asp link channels */
> +     ret = prtd->asp_link_lch[0] = edma_alloc_slot(EDMA_SLOT_ANY);
> +     if (ret < 0)
> +             goto exit4;
> +     ret = prtd->asp_link_lch[1] = edma_alloc_slot(EDMA_SLOT_ANY);
> +     if (ret < 0)
> +             goto exit5;
> +
> +     prtd->ram_link_lch2 = -1;
> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +             ret = prtd->ram_link_lch2 = edma_alloc_slot(EDMA_SLOT_ANY);
> +             if (ret < 0)
> +                     goto exit6;
>       }
> -     prtd->slave_lch = ret;
> -
> -     /* Issue transfer completion IRQ when the channel completes a
> -      * transfer, then always reload from the same slot (by a kind
> -      * of loopback link).  The completion IRQ handler will update
> -      * the reload slot with a new buffer.
> -      *
> -      * REVISIT save p_ram here after setting up everything except
> -      * the buffer and its length (ccnt) ... use it as a template
> -      * so davinci_pcm_enqueue_dma() takes less time in IRQ.
> -      */
> -     edma_read_slot(prtd->slave_lch, &p_ram);
> -     p_ram.opt |= TCINTEN | EDMA_TCC(prtd->master_lch);
> -     p_ram.link_bcntrld = prtd->slave_lch << 5;
> -     edma_write_slot(prtd->slave_lch, &p_ram);
> +     /* circle ping-pong buffers */
> +     edma_link(prtd->asp_link_lch[0], prtd->asp_link_lch[1]);
> +     edma_link(prtd->asp_link_lch[1], prtd->asp_link_lch[0]);
> +     /* circle ram buffers */
> +     edma_link(prtd->ram_link_lch, prtd->ram_link_lch);
> 
> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +             asp_src_ping = iram_dma->addr;
> +             asp_dst_ping = dma_data->dma_addr;      /* fifo */
> +     } else {
> +             asp_src_ping = dma_data->dma_addr;      /* fifo */
> +             asp_dst_ping = iram_dma->addr;
> +     }
> +     /* ping */
> +     lch = prtd->asp_link_lch[0];
> +     edma_set_src(lch, asp_src_ping, INCR, W16BIT);
> +     edma_set_dest(lch, asp_dst_ping, INCR, W16BIT);
> +     edma_set_src_index(lch, 0, 0);
> +     edma_set_dest_index(lch, 0, 0);
> +
> +     edma_read_slot(lch, &p_ram);
> +     p_ram.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN);
> +     p_ram.opt |= TCCHEN | EDMA_TCC(prtd->ram_master_lch & 0x3f);
> +     edma_write_slot(lch, &p_ram);
> +
> +     /* pong */
> +     lch = prtd->asp_link_lch[1];
> +     edma_set_src(lch, asp_src_ping, INCR, W16BIT);
> +     edma_set_dest(lch, asp_dst_ping, INCR, W16BIT);
> +     edma_set_src_index(lch, 0, 0);
> +     edma_set_dest_index(lch, 0, 0);
> +
> +     edma_read_slot(lch, &p_ram);
> +     p_ram.opt &= ~(TCCMODE | EDMA_TCC(0x3f));
> +     /* interrupt after every pong completion */
> +     p_ram.opt |= TCINTEN | TCCHEN | ((prtd->ram_master_lch &
> 0x3f)<<12);
> +     edma_write_slot(lch, &p_ram);
> +
> +     /* ram */
> +     lch = prtd->ram_link_lch;
> +     edma_set_src(lch, iram_dma->addr, INCR, W32BIT);
> +     edma_set_dest(lch, iram_dma->addr, INCR, W32BIT);
> +     pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u,"
> +             "for asp:%u %u %u\n", __func__,
> +             prtd->ram_master_lch, prtd->ram_link_lch, prtd-
> >ram_link_lch2,
> +             prtd->asp_master_lch, prtd->asp_link_lch[0],
> +             prtd->asp_link_lch[1]);
>       return 0;
> +exit6:
> +     edma_free_channel(prtd->ram_link_lch2);
> +exit5:
> +     edma_free_channel(prtd->asp_link_lch[0]);
> +exit4:
> +     edma_free_channel(prtd->asp_master_lch);
> +exit3:
> +     edma_free_channel(prtd->ram_link_lch);
> +exit2:
> +     edma_free_channel(prtd->ram_master_lch);
> +exit1:
> +     return ret;
>  }
> -
> +/* I2S master (codec/cpudai) should start/stop dma request,
> + *  we shouldn't ignore them
> + *  codec AIC33 needs fixed
> + */
> +#define BROKEN_MASTER 1
> +#ifdef BROKEN_MASTER
>  static int davinci_pcm_trigger(struct snd_pcm_substream *substream,
> int cmd)
>  {
>       struct davinci_runtime_data *prtd = substream->runtime-
> >private_data;
> @@ -185,12 +388,12 @@ static int davinci_pcm_trigger(struct
> snd_pcm_substream *substream, int cmd)
>       case SNDRV_PCM_TRIGGER_START:
>       case SNDRV_PCM_TRIGGER_RESUME:
>       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> -             edma_start(prtd->master_lch);
> +             edma_resume(prtd->asp_master_lch);
>               break;
>       case SNDRV_PCM_TRIGGER_STOP:
>       case SNDRV_PCM_TRIGGER_SUSPEND:
>       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> -             edma_stop(prtd->master_lch);
> +             edma_pause(prtd->asp_master_lch);
>               break;
>       default:
>               ret = -EINVAL;
> @@ -201,19 +404,32 @@ static int davinci_pcm_trigger(struct
> snd_pcm_substream *substream, int cmd)
> 
>       return ret;
>  }
> +#endif
> 
>  static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
>  {
> +     int ret;
>       struct davinci_runtime_data *prtd = substream->runtime-
> >private_data;
> -     struct edmacc_param temp;
> 
> -     prtd->period = 0;
> -     davinci_pcm_enqueue_dma(substream);
> +     ret = davinci_pcm_dma_setup(substream);
> +     if (ret < 0)
> +             return ret;
> +
> +     edma_write_slot(prtd->ram_master_lch, &prtd->ram_params);
> +     edma_write_slot(prtd->asp_master_lch, &prtd->asp_params);
> 
> -     /* Copy self-linked parameter RAM entry into master channel */
> -     edma_read_slot(prtd->slave_lch, &temp);
> -     edma_write_slot(prtd->master_lch, &temp);
> +     print_buf_info(prtd->ram_master_lch, "ram_master_lch");
> +     print_buf_info(prtd->ram_link_lch, "ram_link_lch");
> +     print_buf_info(prtd->ram_link_lch2, "ram_link_lch2");
> +     print_buf_info(prtd->asp_master_lch, "asp_master_lch");
> +     print_buf_info(prtd->asp_link_lch[0], "asp_link_lch[0]");
> +     print_buf_info(prtd->asp_link_lch[1], "asp_link_lch[1]");
> 
> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +             /* copy 1st iram buffer */
> +             edma_start(prtd->ram_master_lch);
> +     }
> +     edma_start(prtd->asp_master_lch);
>       return 0;
>  }
> 
> @@ -223,20 +439,44 @@ davinci_pcm_pointer(struct snd_pcm_substream
> *substream)
>       struct snd_pcm_runtime *runtime = substream->runtime;
>       struct davinci_runtime_data *prtd = runtime->private_data;
>       unsigned int offset;
> -     dma_addr_t count;
> -     dma_addr_t src, dst;
> +     int count_asp, count_ram;
> +     int mod_ram;
> +     dma_addr_t ram_src, ram_dst;
> +     dma_addr_t asp_src, asp_dst;
> +     unsigned int period_size = snd_pcm_lib_period_bytes(substream);
> 
>       spin_lock(&prtd->lock);
> -
> -     edma_get_position(prtd->master_lch, &src, &dst);
> -     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> -             count = src - runtime->dma_addr;
> -     else
> -             count = dst - runtime->dma_addr;
> -
> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +             /* reading ram before asp should be safe
> +              * as long as the asp transfers less than a ping size
> +              * of bytes between the 2 reads
> +              */
> +             edma_get_position(prtd->ram_master_lch,
> +                             &ram_src, &ram_dst);
> +             edma_get_position(prtd->asp_master_lch,
> +                             &asp_src, &asp_dst);
> +             count_asp = asp_src - prtd->asp_params.src;
> +             count_ram = ram_src - prtd->ram_params.src;
> +             mod_ram = count_ram % period_size;
> +             mod_ram -= count_asp;
> +             if (mod_ram < 0)
> +                     mod_ram += period_size;
> +             else if (mod_ram == 0) {
> +                     if (snd_pcm_running(substream))
> +                             mod_ram += period_size;
> +             }
> +             count_ram -= mod_ram;
> +             if (count_ram < 0)
> +                     count_ram += period_size * runtime->periods;
> +     } else {
> +             edma_get_position(prtd->ram_master_lch,
> +                             &ram_src, &ram_dst);
> +             count_ram = ram_dst - prtd->ram_params.dst;
> +     }
>       spin_unlock(&prtd->lock);
> 
> -     offset = bytes_to_frames(runtime, count);
> +     offset = bytes_to_frames(runtime, count_ram);
> +     DPRINTK("count_ram=0x%x\n", count_ram);
>       if (offset >= runtime->buffer_size)
>               offset = 0;
> 
> @@ -273,13 +513,22 @@ static int davinci_pcm_close(struct
> snd_pcm_substream *substream)
>       struct snd_pcm_runtime *runtime = substream->runtime;
>       struct davinci_runtime_data *prtd = runtime->private_data;
> 
> -     edma_unlink(prtd->slave_lch);
> -
> -     edma_free_slot(prtd->slave_lch);
> -     edma_free_channel(prtd->master_lch);
> -
> +     edma_stop(prtd->ram_master_lch);
> +     edma_stop(prtd->asp_master_lch);
> +     edma_unlink(prtd->asp_link_lch[0]);
> +     edma_unlink(prtd->asp_link_lch[1]);
> +     edma_unlink(prtd->ram_link_lch);
> +
> +     edma_free_slot(prtd->asp_link_lch[0]);
> +     edma_free_slot(prtd->asp_link_lch[1]);
> +     edma_free_channel(prtd->asp_master_lch);
> +     edma_free_slot(prtd->ram_link_lch);
> +     if (prtd->ram_link_lch2 != -1) {
> +             edma_free_slot(prtd->ram_link_lch2);
> +             prtd->ram_link_lch2 = -1;
> +     }
> +     edma_free_channel(prtd->ram_master_lch);
>       kfree(prtd);
> -
>       return 0;
>  }
> 
> @@ -313,7 +562,9 @@ static struct snd_pcm_ops davinci_pcm_ops = {
>       .hw_params =    davinci_pcm_hw_params,
>       .hw_free =      davinci_pcm_hw_free,
>       .prepare =      davinci_pcm_prepare,
> -     .trigger =      davinci_pcm_trigger,
> +#ifdef BROKEN_MASTER
> +     .trigger =      davinci_pcm_trigger,
> +#endif
>       .pointer =      davinci_pcm_pointer,
>       .mmap =         davinci_pcm_mmap,
>  };
> @@ -323,21 +574,44 @@ static int
> davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
>       struct snd_pcm_substream *substream = pcm-
> >streams[stream].substream;
>       struct snd_dma_buffer *buf = &substream->dma_buffer;
>       size_t size = davinci_pcm_hardware.buffer_bytes_max;
> +     struct snd_dma_buffer *iram_dma = NULL;
> +     dma_addr_t iram_phys = 0;
> +     void *iram_virt = NULL;
> +     unsigned int iram_size = davinci_pcm_hardware.period_bytes_max;
> 
>       buf->dev.type = SNDRV_DMA_TYPE_DEV;
>       buf->dev.dev = pcm->card->dev;
>       buf->private_data = NULL;
> +     buf->bytes = size;
>       buf->area = dma_alloc_writecombine(pcm->card->dev, size,
>                                          &buf->addr, GFP_KERNEL);
> 
>       pr_debug("davinci_pcm: preallocate_dma_buffer: area=%p, addr=%p, "
>               "size=%d\n", (void *) buf->area, (void *) buf->addr, size);
> 
> -     if (!buf->area)
> -             return -ENOMEM;
> 
> -     buf->bytes = size;
> +     if (!buf->area)
> +             goto exit1;
> +     iram_virt = sram_alloc(iram_size, &iram_phys);
> +     if (!iram_virt)
> +             goto exit2;
> +     iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL);
> +     if (!iram_dma)
> +             goto exit3;
> +     iram_dma->area = iram_virt;
> +     iram_dma->addr = iram_phys;
> +     memset(iram_dma->area, 0, iram_size);
> +     buf->private_data = iram_dma ;
>       return 0;
> +     kfree(iram_dma);
> +exit3:
> +     if (iram_virt)
> +             sram_free(iram_virt, iram_size);
> +exit2:
> +     dma_free_writecombine(pcm->card->dev, size, buf->area, buf->addr);
> +     buf->area = NULL;
> +exit1:
> +     return -ENOMEM;
>  }
> 
>  static void davinci_pcm_free(struct snd_pcm *pcm)
> @@ -347,6 +621,8 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
>       int stream;
> 
>       for (stream = 0; stream < 2; stream++) {
> +             unsigned int iram_size =
> davinci_pcm_hardware.period_bytes_max;
> +             struct snd_dma_buffer *iram_dma;
>               substream = pcm->streams[stream].substream;
>               if (!substream)
>                       continue;
> @@ -358,6 +634,11 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
>               dma_free_writecombine(pcm->card->dev, buf->bytes,
>                                     buf->area, buf->addr);
>               buf->area = NULL;
> +             iram_dma = (struct snd_dma_buffer *)buf->private_data;
> +             if (iram_dma) {
> +                     sram_free(iram_dma->area, iram_size);
> +                     kfree(iram_dma);
> +             }
>       }
>  }
> 
> @@ -375,18 +656,17 @@ static int davinci_pcm_new(struct snd_card *card,
> 
>       if (dai->playback.channels_min) {
>               ret = davinci_pcm_preallocate_dma_buffer(pcm,
> -                     SNDRV_PCM_STREAM_PLAYBACK);
> +                             SNDRV_PCM_STREAM_PLAYBACK);
>               if (ret)
>                       return ret;
>       }
> 
>       if (dai->capture.channels_min) {
>               ret = davinci_pcm_preallocate_dma_buffer(pcm,
> -                     SNDRV_PCM_STREAM_CAPTURE);
> +                             SNDRV_PCM_STREAM_CAPTURE);
>               if (ret)
>                       return ret;
>       }
> -
>       return 0;
>  }
> 
> diff --git a/sound/soc/davinci/davinci-pcm.h
> b/sound/soc/davinci/davinci-pcm.h
> index 62cb4eb..fc70161 100644
> --- a/sound/soc/davinci/davinci-pcm.h
> +++ b/sound/soc/davinci/davinci-pcm.h
> @@ -16,7 +16,8 @@ struct davinci_pcm_dma_params {
>       char *name;             /* stream identifier */
>       int channel;            /* sync dma channel ID */
>       dma_addr_t dma_addr;    /* device physical address for DMA */
> -     unsigned int data_type; /* xfer data type */
> +     unsigned char data_type;        /* xfer data type */
> +     unsigned char convert_mono_stereo;
>  };
> 
>  struct evm_snd_platform_data {
> --
> 1.5.4.3
> 
> 
> _______________________________________________
> Davinci-linux-open-source mailing list
> [email protected]
> http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source


_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to