Troy, I tried this patch on DM355 EVM. It works fine.
Thanks, Chaithrika > -----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
