From patchwork Mon Jun 29 19:25:03 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Mani, Arun" X-Patchwork-Id: 33026 Received: from bear.ext.ti.com (bear.ext.ti.com [192.94.94.41]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n5TJR5r4030261 for ; Mon, 29 Jun 2009 19:27:06 GMT Received: from dlep35.itg.ti.com ([157.170.170.118]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id n5TJOvf8023913; Mon, 29 Jun 2009 14:25:02 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep35.itg.ti.com (8.13.7/8.13.7) with ESMTP id n5TJOqrC008216; Mon, 29 Jun 2009 14:24:56 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id 5C43A80627; Mon, 29 Jun 2009 14:24:52 -0500 (CDT) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dlep20.itg.ti.com (dlep20.itg.ti.com [157.170.170.23]) by linux.omap.com (Postfix) with ESMTP id 2550580626 for ; Mon, 29 Jun 2009 14:24:51 -0500 (CDT) Received: from dlee75.ent.ti.com (localhost [127.0.0.1]) by dlep20.itg.ti.com (8.12.11/8.12.11) with ESMTP id n5TJOpIa012470; Mon, 29 Jun 2009 14:24:51 -0500 (CDT) Received: from dlee02.ent.ti.com ([157.170.170.17]) by dlee75.ent.ti.com ([157.170.170.72]) with mapi; Mon, 29 Jun 2009 14:24:50 -0500 From: "Mani, Arun" To: Yusuf Caglar AKYUZ Date: Mon, 29 Jun 2009 14:25:03 -0500 Thread-Topic: overrun occuring in arecord for 2.6.30 Thread-Index: Acn41HCDMysZRKr+QAGzFAh9HEGE8QAGqVGA Message-ID: <7A436F7769CA33409C6B44B358BFFF0C011DC84653@dlee02.ent.ti.com> In-Reply-To: <4A48E7D3.3070200@gmail.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: acceptlanguage: en-US MIME-Version: 1.0 Cc: "davinci-linux-open-source@linux.davincidsp.com" Subject: RE: overrun occuring in arecord for 2.6.30 X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.4 Precedence: list List-Id: davinci-linux-open-source.linux.davincidsp.com List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: davinci-linux-open-source-bounces@linux.davincidsp.com Errors-To: davinci-linux-open-source-bounces@linux.davincidsp.com Hi Caglar, I tried your patch in arago tree and still getting the overrun issues. When I replaced the pcm_lib.c file from 1.0.20 to 1.0.19 version without your patches, I was able to make the loopback work. root@arago:~# arecord -f dat -d 10 -v | aplay -f dat -d 10 -v Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo Plug PCM: Hardware PCM card 0 'DaVinci EVM' device 0 subdevice 0 Its setup is: stream : CAPTURE access : RW_INTERLEAVED format : S16_LE subformat : STD channels : 2 rate : 48000 exact rate : 48000 (48000/1) msbits : 16 buffer_size : 32768 period_size : 2048 period_time : 42666 tstamp_mode : NONE period_step : 1 avail_min : 2048 period_event : 0 start_threshold : 1 stop_threshold : 32768 silence_threshold: 0 silence_size : 0 boundary : 1073741824 Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo Plug PCM: Hardware PCM card 0 'DaVinci EVM' device 0 subdevice 0 Its setup is: stream : PLAYBACK access : RW_INTERLEAVED format : S16_LE subformat : STD channels : 2 rate : 48000 exact rate : 48000 (48000/1) msbits : 16 buffer_size : 32768 period_size : 2048 period_time : 42666 tstamp_mode : NONE period_step : 1 avail_min : 2048 period_event : 0 start_threshold : 32768 stop_threshold : 32768 silence_threshold: 0 silence_size : 0 boundary : 1073741824 overrun!!! (at least 0.508 ms long) Status: state : XRUN trigger_time: 1239740804.30893627 tstamp : 1239740804.31187085 delay : 0 avail : 32769 avail_max : 32769 overrun!!! (at least 0.286 ms long) Status: state : XRUN trigger_time: 1239740804.79549960 tstamp : 1239740804.79815502 delay : 0 avail : 32769 avail_max : 32769 overrun!!! (at least 0.247 ms long) Status: state : XRUN trigger_time: 1239740804.127971043 tstamp : 1239740804.128200168 delay : 0 avail : 32769 avail_max : 32769 overrun!!! (at least 0.241 ms long) Status: state : XRUN trigger_time: 1239740804.176707420 tstamp : 1239740804.176929545 delay : 0 avail : 32769 avail_max : 32769 underrun!!! (at least 28.353 ms long) Status: state : XRUN trigger_time: 1239740804.284247130 tstamp : 1239740804.312373672 delay : 0 avail : 32769 avail_max : 32769 underrun!!! (at least 41.886 ms long) Status: state : XRUN trigger_time: 1239740804.953256625 tstamp : 1239740804.995118874 delay : 0 avail : 32769 avail_max : 32769 underrun!!! (at least 42.059 ms long) Status: state : XRUN trigger_time: 1239740805.635967708 tstamp : 1239740805.678002958 delay : 0 avail : 32769 avail_max : 32769 underrun!!! (at least 42.261 ms long) Status: state : XRUN trigger_time: 1239740806.318778500 tstamp : 1239740806.361016292 delay : 0 avail : 32768 avail_max : 32768 underrun!!! (at least 42.051 ms long) Status: state : XRUN trigger_time: 1239740807.1613333 tstamp : 1239740807.43640333 delay : 0 avail : 32769 avail_max : 32769 underrun!!! (at least 41.951 ms long) Status: state : XRUN Thanks, Arun. -----Original Message----- From: Yusuf Caglar AKYUZ [mailto:caglarakyuz@gmail.com] Sent: Monday, June 29, 2009 12:12 PM To: Mani, Arun Cc: davinci-linux-open-source@linux.davincidsp.com Subject: Re: overrun occuring in arecord for 2.6.30 Mani, Arun wrote: > Caglar, > Thanks very much. Can I get the patch against Arago tree too. That will be of great help. > > Thanks, > Arun. > Patch is attached inline. It is against Arago next branch. Regards, Caglar diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 500d2f5..aa7753c 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,94 +125,99 @@ 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) { - 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; + 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); +} - /* 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); +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 snd_soc_device *socdev = rtd->socdev; + struct snd_soc_platform *platform = socdev->card->platform; + 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 +231,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 +260,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 +281,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 +344,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,59 +356,134 @@ 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 = rtd->socdev;//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 (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 { + 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; + } } + rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1); + xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1); - 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); + 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); - } 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); + 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; } @@ -416,18 +491,23 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, 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; @@ -436,10 +516,22 @@ static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } -#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000 +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; + int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + davinci_mcbsp_stop(dev, playback); +} + +#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, @@ -450,12 +542,14 @@ struct snd_soc_dai davinci_i2s_dai = { .name = "davinci-i2s", .id = 0, .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,}, @@ -466,6 +560,7 @@ EXPORT_SYMBOL_GPL(davinci_i2s_dai); static int davinci_i2s_probe(struct platform_device *pdev) { + struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_platform_data *pdata = pdev->dev.platform_data; struct davinci_mcbsp_dev *dev; struct resource *mem, *ioarea, *res; @@ -476,20 +571,17 @@ static int davinci_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "no mem resource?\n"); return -ENODEV; } - ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, pdev->name); if (!ioarea) { dev_err(&pdev->dev, "McBSP region already claimed\n"); return -EBUSY; } - dev = kzalloc(sizeof(struct davinci_mcbsp_dev), GFP_KERNEL); if (!dev) { ret = -ENOMEM; goto err_release_region; } - dev->clk = clk_get(&pdev->dev, pdata->clk_name); if (IS_ERR(dev->clk)) { ret = -ENODEV; diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 3ee38b6..13a8b51 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -23,6 +23,7 @@ #include #include +#include #include "davinci-pcm.h" @@ -42,70 +43,63 @@ 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, .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; pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status); @@ -114,59 +108,260 @@ static void davinci_pcm_dma_irq(unsigned lch, u16 ch_status, void *data) 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_CTLR(prtd->master_lch), 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_CTLR(prtd->ram_master_lch), 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_CTLR(prtd->asp_master_lch), EDMA_SLOT_ANY); + if (ret < 0) + goto exit4; + ret = prtd->asp_link_lch[1] + = edma_alloc_slot(EDMA_CTLR(prtd->asp_master_lch), 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_CTLR(prtd->ram_master_lch), 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(EDMA_CHAN_SLOT(prtd->master_lch)); - p_ram.link_bcntrld = EDMA_CHAN_SLOT(prtd->slave_lch) << 5; - edma_write_slot(prtd->slave_lch, &p_ram); - return 0; + /* 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; @@ -178,12 +373,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; @@ -194,19 +389,25 @@ 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); - - /* Copy self-linked parameter RAM entry into master channel */ - edma_read_slot(prtd->slave_lch, &temp); - edma_write_slot(prtd->master_lch, &temp); + 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); + 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; } @@ -216,20 +417,42 @@ 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); + 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); - 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; - - spin_unlock(&prtd->lock); - - offset = bytes_to_frames(runtime, count); + offset = bytes_to_frames(runtime, count_ram); if (offset >= runtime->buffer_size) offset = 0; @@ -243,6 +466,11 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream) int ret = 0; snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware); + /* ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL); if (prtd == NULL) @@ -266,11 +494,21 @@ 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; @@ -306,7 +544,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, +#ifdef BROKEN_MASTER .trigger = davinci_pcm_trigger, +#endif .pointer = davinci_pcm_pointer, .mmap = davinci_pcm_mmap, }; @@ -316,10 +556,15 @@ 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); @@ -327,10 +572,27 @@ static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) "size=%d\n", (void *) buf->area, (void *) buf->addr, size); if (!buf->area) - return -ENOMEM; - - buf->bytes = size; + 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) @@ -340,6 +602,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; @@ -351,6 +615,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); + } } } diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 1367647..de35441 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,7 +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. * Called by soc-core during audio PCM operations.