[alsa-devel] [PATCH 0/4] Adding idma driver for samsung SoCs
I2S in samsung audio subsytem has a small internal dma(idma). IDMA can be used in low power audio mode and I2S0 secondary fifo data tranfer. In this patchsets, idma driver is added. This patch is tested on SMDKV310, Nexus-s(S5PC110) with lakkyung jung (lakkyung.jung@samsung.com)
This patchset contains followings
o To Jassi Brar, Mark Brown and Liam Girdwood, [PATCH 1/4] ASoC: SAMSUNG: Modify I2S driver to support idma [PATCH 2/4] ASoC: SAMSUNG: Add I2S0 internal dma driver [PATCH 3/4] ASoC: SAMSUNG: Change platform driver for SMDKs
o To Kukjin Kim and Ben Dooks, [PATCH 4/4] ARM: SAMSUNG: Add platform device for idma
Best Regards, SB Kim
Previously, I2S driver only can support system dma. In this patch, i2s driver can support internal dma too. To share register definition, register definition is moved to i2s.h and added some register to support idma.
Signed-off-by: Sangbeom Kim sbkim73@samsung.com --- sound/soc/samsung/i2s.c | 126 ++++++++-------------------------------------- sound/soc/samsung/i2s.h | 130 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 150 insertions(+), 106 deletions(-)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index ffa09b3..404f4c2 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -21,113 +21,9 @@ #include <plat/audio.h>
#include "dma.h" +#include "idma.h" #include "i2s.h"
-#define I2SCON 0x0 -#define I2SMOD 0x4 -#define I2SFIC 0x8 -#define I2SPSR 0xc -#define I2STXD 0x10 -#define I2SRXD 0x14 -#define I2SFICS 0x18 -#define I2STXDS 0x1c - -#define CON_RSTCLR (1 << 31) -#define CON_FRXOFSTATUS (1 << 26) -#define CON_FRXORINTEN (1 << 25) -#define CON_FTXSURSTAT (1 << 24) -#define CON_FTXSURINTEN (1 << 23) -#define CON_TXSDMA_PAUSE (1 << 20) -#define CON_TXSDMA_ACTIVE (1 << 18) - -#define CON_FTXURSTATUS (1 << 17) -#define CON_FTXURINTEN (1 << 16) -#define CON_TXFIFO2_EMPTY (1 << 15) -#define CON_TXFIFO1_EMPTY (1 << 14) -#define CON_TXFIFO2_FULL (1 << 13) -#define CON_TXFIFO1_FULL (1 << 12) - -#define CON_LRINDEX (1 << 11) -#define CON_TXFIFO_EMPTY (1 << 10) -#define CON_RXFIFO_EMPTY (1 << 9) -#define CON_TXFIFO_FULL (1 << 8) -#define CON_RXFIFO_FULL (1 << 7) -#define CON_TXDMA_PAUSE (1 << 6) -#define CON_RXDMA_PAUSE (1 << 5) -#define CON_TXCH_PAUSE (1 << 4) -#define CON_RXCH_PAUSE (1 << 3) -#define CON_TXDMA_ACTIVE (1 << 2) -#define CON_RXDMA_ACTIVE (1 << 1) -#define CON_ACTIVE (1 << 0) - -#define MOD_OPCLK_CDCLK_OUT (0 << 30) -#define MOD_OPCLK_CDCLK_IN (1 << 30) -#define MOD_OPCLK_BCLK_OUT (2 << 30) -#define MOD_OPCLK_PCLK (3 << 30) -#define MOD_OPCLK_MASK (3 << 30) -#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */ - -#define MOD_BLCS_SHIFT 26 -#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT) -#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT) -#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT) -#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT) -#define MOD_BLCP_SHIFT 24 -#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT) -#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT) -#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT) -#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT) - -#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */ -#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */ -#define MOD_C1DD_HHALF (1 << 19) -#define MOD_C1DD_LHALF (1 << 18) -#define MOD_DC2_EN (1 << 17) -#define MOD_DC1_EN (1 << 16) -#define MOD_BLC_16BIT (0 << 13) -#define MOD_BLC_8BIT (1 << 13) -#define MOD_BLC_24BIT (2 << 13) -#define MOD_BLC_MASK (3 << 13) - -#define MOD_IMS_SYSMUX (1 << 10) -#define MOD_SLAVE (1 << 11) -#define MOD_TXONLY (0 << 8) -#define MOD_RXONLY (1 << 8) -#define MOD_TXRX (2 << 8) -#define MOD_MASK (3 << 8) -#define MOD_LR_LLOW (0 << 7) -#define MOD_LR_RLOW (1 << 7) -#define MOD_SDF_IIS (0 << 5) -#define MOD_SDF_MSB (1 << 5) -#define MOD_SDF_LSB (2 << 5) -#define MOD_SDF_MASK (3 << 5) -#define MOD_RCLK_256FS (0 << 3) -#define MOD_RCLK_512FS (1 << 3) -#define MOD_RCLK_384FS (2 << 3) -#define MOD_RCLK_768FS (3 << 3) -#define MOD_RCLK_MASK (3 << 3) -#define MOD_BCLK_32FS (0 << 1) -#define MOD_BCLK_48FS (1 << 1) -#define MOD_BCLK_16FS (2 << 1) -#define MOD_BCLK_24FS (3 << 1) -#define MOD_BCLK_MASK (3 << 1) -#define MOD_8BIT (1 << 0) - -#define MOD_CDCLKCON (1 << 12) - -#define PSR_PSREN (1 << 15) - -#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) -#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) - -#define FIC_TXFLUSH (1 << 15) -#define FIC_RXFLUSH (1 << 7) -#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf) -#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf) -#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f) - -#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) - struct i2s_dai { /* Platform device for this DAI */ struct platform_device *pdev; @@ -646,6 +542,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, { struct i2s_dai *i2s = to_info(dai); u32 mod = readl(i2s->addr + I2SMOD); + u32 ahb = readl(i2s->addr + I2SAHB);
if (!is_secondary(i2s)) mod &= ~(MOD_DC2_EN | MOD_DC1_EN); @@ -657,6 +554,16 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, mod |= MOD_DC1_EN; break; case 2: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s->dma_playback.dma_size = 4; + else + i2s->dma_capture.dma_size = 4; + break; + case 1: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s->dma_playback.dma_size = 2; + else + i2s->dma_capture.dma_size = 2; break; default: dev_err(&i2s->pdev->dev, "%d channels not supported\n", @@ -702,6 +609,13 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, params_format(params)); return -EINVAL; } + + if (is_secondary(i2s)) { + ahb |= (AHB_DMARLD | AHB_INTMASK); + mod |= MOD_TXS_IDMA; + } + + writel(ahb, i2s->addr + I2SAHB); writel(mod, i2s->addr + I2SMOD);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -979,6 +893,8 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) if (i2s->quirks & QUIRK_NEED_RSTCLR) writel(CON_RSTCLR, i2s->addr + I2SCON);
+ idma_init((void *)i2s->addr); + probe_exit: /* Reset any constraint on RFS and BFS */ i2s->rfs = 0; diff --git a/sound/soc/samsung/i2s.h b/sound/soc/samsung/i2s.h index 8e15f6a..7f8aa61 100644 --- a/sound/soc/samsung/i2s.h +++ b/sound/soc/samsung/i2s.h @@ -24,6 +24,134 @@
#define SAMSUNG_I2S_RCLKSRC_0 0 #define SAMSUNG_I2S_RCLKSRC_1 1 -#define SAMSUNG_I2S_CDCLK 2 +#define SAMSUNG_I2S_CDCLK 2 + +#define I2SCON 0x0 +#define I2SMOD 0x4 +#define I2SFIC 0x8 +#define I2SPSR 0xc +#define I2STXD 0x10 +#define I2SRXD 0x14 +#define I2SFICS 0x18 +#define I2STXDS 0x1c +#define I2SAHB 0x20 +#define I2SSTR0 0x24 +#define I2SSIZE 0x28 +#define I2STRNCNT 0x2c +#define I2SLVL0ADDR 0x30 +#define I2SLVL1ADDR 0x34 +#define I2SLVL2ADDR 0x38 +#define I2SLVL3ADDR 0x3c + +#define CON_RSTCLR (1 << 31) +#define CON_FRXOFSTATUS (1 << 26) +#define CON_FRXORINTEN (1 << 25) +#define CON_FTXSURSTAT (1 << 24) +#define CON_FTXSURINTEN (1 << 23) +#define CON_TXSDMA_PAUSE (1 << 20) +#define CON_TXSDMA_ACTIVE (1 << 18) + +#define CON_FTXURSTATUS (1 << 17) +#define CON_FTXURINTEN (1 << 16) +#define CON_TXFIFO2_EMPTY (1 << 15) +#define CON_TXFIFO1_EMPTY (1 << 14) +#define CON_TXFIFO2_FULL (1 << 13) +#define CON_TXFIFO1_FULL (1 << 12) + +#define CON_LRINDEX (1 << 11) +#define CON_TXFIFO_EMPTY (1 << 10) +#define CON_RXFIFO_EMPTY (1 << 9) +#define CON_TXFIFO_FULL (1 << 8) +#define CON_RXFIFO_FULL (1 << 7) +#define CON_TXDMA_PAUSE (1 << 6) +#define CON_RXDMA_PAUSE (1 << 5) +#define CON_TXCH_PAUSE (1 << 4) +#define CON_RXCH_PAUSE (1 << 3) +#define CON_TXDMA_ACTIVE (1 << 2) +#define CON_RXDMA_ACTIVE (1 << 1) +#define CON_ACTIVE (1 << 0) + +#define MOD_OPCLK_CDCLK_OUT (0 << 30) +#define MOD_OPCLK_CDCLK_IN (1 << 30) +#define MOD_OPCLK_BCLK_OUT (2 << 30) +#define MOD_OPCLK_PCLK (3 << 30) +#define MOD_OPCLK_MASK (3 << 30) +#define MOD_TXS_IDMA (1 << 28) + +#define MOD_BLCS_SHIFT 26 +#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT) +#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT) +#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT) +#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT) +#define MOD_BLCP_SHIFT 24 +#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT) +#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT) +#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT) +#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT) + +#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */ +#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */ +#define MOD_C1DD_HHALF (1 << 19) +#define MOD_C1DD_LHALF (1 << 18) +#define MOD_DC2_EN (1 << 17) +#define MOD_DC1_EN (1 << 16) +#define MOD_BLC_16BIT (0 << 13) +#define MOD_BLC_8BIT (1 << 13) +#define MOD_BLC_24BIT (2 << 13) +#define MOD_BLC_MASK (3 << 13) + +#define MOD_IMS_SYSMUX (1 << 10) +#define MOD_SLAVE (1 << 11) +#define MOD_TXONLY (0 << 8) +#define MOD_RXONLY (1 << 8) +#define MOD_TXRX (2 << 8) +#define MOD_MASK (3 << 8) +#define MOD_LR_LLOW (0 << 7) +#define MOD_LR_RLOW (1 << 7) +#define MOD_SDF_IIS (0 << 5) +#define MOD_SDF_MSB (1 << 5) +#define MOD_SDF_LSB (2 << 5) +#define MOD_SDF_MASK (3 << 5) +#define MOD_RCLK_256FS (0 << 3) +#define MOD_RCLK_512FS (1 << 3) +#define MOD_RCLK_384FS (2 << 3) +#define MOD_RCLK_768FS (3 << 3) +#define MOD_RCLK_MASK (3 << 3) +#define MOD_BCLK_32FS (0 << 1) +#define MOD_BCLK_48FS (1 << 1) +#define MOD_BCLK_16FS (2 << 1) +#define MOD_BCLK_24FS (3 << 1) +#define MOD_BCLK_MASK (3 << 1) +#define MOD_8BIT (1 << 0) + +#define MOD_CDCLKCON (1 << 12) + +#define PSR_PSREN (1 << 15) + +#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) +#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) + +#define FIC_TXFLUSH (1 << 15) +#define FIC_RXFLUSH (1 << 7) + +#define FIC_TXCOUNT(x) (((x) >> 8) & 0x7f) +#define FIC_RXCOUNT(x) (((x) >> 0) & 0x7f) +#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f) + +#define AHB_INTENLVL0 (1 << 24) +#define AHB_LVL0INT (1 << 20) +#define AHB_CLRLVL0INT (1 << 16) +#define AHB_DMARLD (1 << 5) +#define AHB_INTMASK (1 << 3) +#define AHB_DMAEN (1 << 0) +#define AHB_LVLINTMASK (0xf << 20) + +#define I2SSIZE_TRNMSK (0xffff) +#define I2SSIZE_SHIFT (16) + +#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) + +#define ST_RUNNING (1<<0) +#define ST_OPENED (1<<1)
#endif /* __SND_SOC_SAMSUNG_I2S_H */
On Thu, Jun 9, 2011 at 1:39 PM, Sangbeom Kim sbkim73@samsung.com wrote:
case 2:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- i2s->dma_playback.dma_size = 4;
- else
- i2s->dma_capture.dma_size = 4;
- break;
Why do we need this ?
- case 1:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- i2s->dma_playback.dma_size = 2;
- else
- i2s->dma_capture.dma_size = 2;
I2S doesn't support Mono.
#define SAMSUNG_I2S_RCLKSRC_0 0 #define SAMSUNG_I2S_RCLKSRC_1 1 -#define SAMSUNG_I2S_CDCLK 2 +#define SAMSUNG_I2S_CDCLK 2
Please avoid inconsequential changes.
+#define I2SCON 0x0 +#define I2SMOD 0x4 +#define I2SFIC 0x8 +#define I2SPSR 0xc +#define I2STXD 0x10 +#define I2SRXD 0x14 +#define I2SFICS 0x18 +#define I2STXDS 0x1c +#define I2SAHB 0x20 +#define I2SSTR0 0x24 +#define I2SSIZE 0x28 +#define I2STRNCNT 0x2c +#define I2SLVL0ADDR 0x30 +#define I2SLVL1ADDR 0x34 +#define I2SLVL2ADDR 0x38 +#define I2SLVL3ADDR 0x3c
+#define CON_RSTCLR (1 << 31) +#define CON_FRXOFSTATUS (1 << 26) +#define CON_FRXORINTEN (1 << 25) +#define CON_FTXSURSTAT (1 << 24) +#define CON_FTXSURINTEN (1 << 23) +#define CON_TXSDMA_PAUSE (1 << 20) +#define CON_TXSDMA_ACTIVE (1 << 18)
+#define CON_FTXURSTATUS (1 << 17) +#define CON_FTXURINTEN (1 << 16) +#define CON_TXFIFO2_EMPTY (1 << 15) +#define CON_TXFIFO1_EMPTY (1 << 14) +#define CON_TXFIFO2_FULL (1 << 13) +#define CON_TXFIFO1_FULL (1 << 12)
+#define CON_LRINDEX (1 << 11) +#define CON_TXFIFO_EMPTY (1 << 10) +#define CON_RXFIFO_EMPTY (1 << 9) +#define CON_TXFIFO_FULL (1 << 8) +#define CON_RXFIFO_FULL (1 << 7) +#define CON_TXDMA_PAUSE (1 << 6) +#define CON_RXDMA_PAUSE (1 << 5) +#define CON_TXCH_PAUSE (1 << 4) +#define CON_RXCH_PAUSE (1 << 3) +#define CON_TXDMA_ACTIVE (1 << 2) +#define CON_RXDMA_ACTIVE (1 << 1) +#define CON_ACTIVE (1 << 0)
+#define MOD_OPCLK_CDCLK_OUT (0 << 30) +#define MOD_OPCLK_CDCLK_IN (1 << 30) +#define MOD_OPCLK_BCLK_OUT (2 << 30) +#define MOD_OPCLK_PCLK (3 << 30) +#define MOD_OPCLK_MASK (3 << 30) +#define MOD_TXS_IDMA (1 << 28)
+#define MOD_BLCS_SHIFT 26 +#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT) +#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT) +#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT) +#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT) +#define MOD_BLCP_SHIFT 24 +#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT) +#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT) +#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT) +#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT)
+#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */ +#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */ +#define MOD_C1DD_HHALF (1 << 19) +#define MOD_C1DD_LHALF (1 << 18) +#define MOD_DC2_EN (1 << 17) +#define MOD_DC1_EN (1 << 16) +#define MOD_BLC_16BIT (0 << 13) +#define MOD_BLC_8BIT (1 << 13) +#define MOD_BLC_24BIT (2 << 13) +#define MOD_BLC_MASK (3 << 13)
+#define MOD_IMS_SYSMUX (1 << 10) +#define MOD_SLAVE (1 << 11) +#define MOD_TXONLY (0 << 8) +#define MOD_RXONLY (1 << 8) +#define MOD_TXRX (2 << 8) +#define MOD_MASK (3 << 8) +#define MOD_LR_LLOW (0 << 7) +#define MOD_LR_RLOW (1 << 7) +#define MOD_SDF_IIS (0 << 5) +#define MOD_SDF_MSB (1 << 5) +#define MOD_SDF_LSB (2 << 5) +#define MOD_SDF_MASK (3 << 5) +#define MOD_RCLK_256FS (0 << 3) +#define MOD_RCLK_512FS (1 << 3) +#define MOD_RCLK_384FS (2 << 3) +#define MOD_RCLK_768FS (3 << 3) +#define MOD_RCLK_MASK (3 << 3) +#define MOD_BCLK_32FS (0 << 1) +#define MOD_BCLK_48FS (1 << 1) +#define MOD_BCLK_16FS (2 << 1) +#define MOD_BCLK_24FS (3 << 1) +#define MOD_BCLK_MASK (3 << 1) +#define MOD_8BIT (1 << 0)
+#define MOD_CDCLKCON (1 << 12)
+#define PSR_PSREN (1 << 15)
+#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) +#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf)
+#define FIC_TXFLUSH (1 << 15) +#define FIC_RXFLUSH (1 << 7)
+#define FIC_TXCOUNT(x) (((x) >> 8) & 0x7f) +#define FIC_RXCOUNT(x) (((x) >> 0) & 0x7f) +#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
+#define AHB_INTENLVL0 (1 << 24) +#define AHB_LVL0INT (1 << 20) +#define AHB_CLRLVL0INT (1 << 16) +#define AHB_DMARLD (1 << 5) +#define AHB_INTMASK (1 << 3) +#define AHB_DMAEN (1 << 0) +#define AHB_LVLINTMASK (0xf << 20)
+#define I2SSIZE_TRNMSK (0xffff) +#define I2SSIZE_SHIFT (16)
The need of i2s.h is for machine drivers. Please move these register/bit definitions to a new header(say i2s-regs.h) if we have to share them with idma.c
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+#define ST_RUNNING (1<<0) +#define ST_OPENED (1<<1)
These should be moved to the c file that uses them.
On Thu, Jun 9, 2011 at 8:31 PM, Jassi Brar wrote:
case 2:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
i2s->dma_playback.dma_size = 4;
else
i2s->dma_capture.dma_size = 4;
break;
Why do we need this ?
This code don't need here, I will delete it.
case 1:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
i2s->dma_playback.dma_size = 2;
else
i2s->dma_capture.dma_size = 2;
I2S doesn't support Mono.
Yes, But some application require mono recording. It is needed mono recording support.
#define SAMSUNG_I2S_RCLKSRC_0 0 #define SAMSUNG_I2S_RCLKSRC_1 1 -#define SAMSUNG_I2S_CDCLK 2 +#define SAMSUNG_I2S_CDCLK 2
Please avoid inconsequential changes.
OK,
The need of i2s.h is for machine drivers. Please move these register/bit definitions to a new header(say i2s-regs.h) if we have to share them with idma.c
OK, I2S register have to share with idma, I will create new header file.
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+#define ST_RUNNING (1<<0) +#define ST_OPENED (1<<1)
These should be moved to the c file that uses them.
I will move it.
Thanks,
On Mon, Jun 13, 2011 at 1:28 PM, Sangbeom Kim sbkim73@samsung.com wrote:
On Thu, Jun 9, 2011 at 8:31 PM, Jassi Brar wrote:
- case 1:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- i2s->dma_playback.dma_size = 2;
- else
- i2s->dma_capture.dma_size = 2;
I2S doesn't support Mono.
Yes, But some application require mono recording. It is needed mono recording support.
Sorry I don't understand. Alsa plugins can record stereo from device and provide mono to applications.
I2S in Exynos4 and S5PC110(S5PV210) has a internal dma. It can be used low power audio mode and 2nd channel transfer.
Signed-off-by: Sangbeom Kim sbkim73@samsung.com --- sound/soc/samsung/Makefile | 2 + sound/soc/samsung/idma.c | 492 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/samsung/idma.h | 28 +++ 3 files changed, 522 insertions(+), 0 deletions(-) create mode 100644 sound/soc/samsung/idma.c create mode 100644 sound/soc/samsung/idma.h
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 683843a..2b9e251 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -1,5 +1,6 @@ # S3c24XX Platform Support snd-soc-s3c24xx-objs := dma.o +snd-soc-idma-objs := idma.o snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o snd-soc-ac97-objs := ac97.o @@ -16,6 +17,7 @@ obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o +obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o
# S3C24XX Machine Support snd-soc-jive-wm8750-objs := jive_wm8750.o diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c new file mode 100644 index 0000000..ae19546 --- /dev/null +++ b/sound/soc/samsung/idma.c @@ -0,0 +1,492 @@ +/* + * linux/sound/soc/samsung/idma.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * I2S0's Internal DMA driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "i2s.h" +#include "idma.h" +#include "dma.h" + +static const struct snd_pcm_hardware idma_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_U24_LE | + SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S8, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 128, + .period_bytes_max = PAGE_SIZE*2, + .periods_min = 1, + .periods_max = 4, +}; + +struct idma_ctrl { + spinlock_t lock; + int state; + dma_addr_t start; + dma_addr_t pos; + dma_addr_t end; + dma_addr_t period; + dma_addr_t periodsz; + void *token; + void (*cb)(void *dt, int bytes_xfer); +}; + +static struct idma_info { + spinlock_t lock; + void __iomem *regs; + int trigger_stat; +} idma; + +static void idma_getpos(dma_addr_t *src) +{ + *src = LP_TXBUFF_ADDR + + (readl(idma.regs + I2STRNCNT) & 0xffffff) * 4; +} + +void i2sdma_getpos(dma_addr_t *src) +{ + if (idma.trigger_stat == LPAM_DMA_START) + *src = LP_TXBUFF_ADDR + + (readl(idma.regs + I2STRNCNT) & 0xffffff) * 4; + else + *src = LP_TXBUFF_ADDR; +} + +static int idma_enqueue(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct idma_ctrl *prtd = substream->runtime->private_data; + u32 val; + + spin_lock(&prtd->lock); + prtd->token = (void *) substream; + spin_unlock(&prtd->lock); + + /* Internal DMA Level0 Interrupt Address */ + val = LP_TXBUFF_ADDR + prtd->periodsz; + writel(val, idma.regs + I2SLVL0ADDR); + + /* Start address0 of I2S internal DMA operation. */ + val = readl(idma.regs + I2SSTR0); + val = LP_TXBUFF_ADDR; + writel(val, idma.regs + I2SSTR0); + + /* + * Transfer block size for I2S internal DMA. + * Should decide transfer size before start dma operation + */ + val = readl(idma.regs + I2SSIZE); + val &= ~(I2SSIZE_TRNMSK << I2SSIZE_SHIFT); + + val |= (((runtime->dma_bytes >> 2) & + I2SSIZE_TRNMSK) << I2SSIZE_SHIFT); + writel(val, idma.regs + I2SSIZE); + + return 0; +} + +static void idma_setcallbk(struct snd_pcm_substream *substream, + void (*cb)(void *, int)) +{ + struct idma_ctrl *prtd = substream->runtime->private_data; + + spin_lock(&prtd->lock); + prtd->cb = cb; + spin_unlock(&prtd->lock); + + pr_debug("%s:%d dma_period=%x\n", __func__, __LINE__, prtd->periodsz); +} + +static void idma_ctrl(int op) +{ + u32 val = readl(idma.regs + I2SAHB); + + spin_lock(&idma.lock); + + switch (op) { + case LPAM_DMA_START: + val |= (AHB_INTENLVL0 | AHB_DMAEN); + break; + case LPAM_DMA_STOP: + val &= ~(AHB_INTENLVL0 | AHB_DMAEN); + break; + default: + return; + } + + writel(val, idma.regs + I2SAHB); + spin_unlock(&idma.lock); +} + +static void idma_done(void *id, int bytes_xfer) +{ + struct snd_pcm_substream *substream = id; + struct idma_ctrl *prtd = substream->runtime->private_data; + + if (prtd && (prtd->state & ST_RUNNING)) + snd_pcm_period_elapsed(substream); +} + +static int idma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct idma_ctrl *prtd = substream->runtime->private_data; + + pr_debug("Entered %s\n", __func__); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = params_buffer_bytes(params); + memset(runtime->dma_area, 0, runtime->dma_bytes); + + prtd->start = prtd->pos = runtime->dma_addr; + prtd->period = params_periods(params); + prtd->periodsz = params_period_bytes(params); + prtd->end = LP_TXBUFF_ADDR + runtime->dma_bytes; + + idma_setcallbk(substream, idma_done); + + pr_debug("DmaAddr=@%x Total=%dbytes PrdSz=%d #Prds=%d dma_area=0x%x\n", + prtd->start, runtime->dma_bytes, prtd->periodsz, + prtd->period, (unsigned int)runtime->dma_area); + + return 0; +} + +static int idma_hw_free(struct snd_pcm_substream *substream) +{ + pr_debug("Entered %s\n", __func__); + + snd_pcm_set_runtime_buffer(substream, NULL); + + return 0; +} + +static int idma_prepare(struct snd_pcm_substream *substream) +{ + struct idma_ctrl *prtd = substream->runtime->private_data; + + pr_debug("Entered %s\n", __func__); + + prtd->pos = prtd->start; + + /* flush the DMA channel */ + idma_ctrl(LPAM_DMA_STOP); + idma_enqueue(substream); + + return 0; +} + +static int idma_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct idma_ctrl *prtd = substream->runtime->private_data; + int ret = 0; + + pr_debug("Entered %s\n", __func__); + + spin_lock(&prtd->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + prtd->state |= ST_RUNNING; + idma.trigger_stat = LPAM_DMA_START; + idma_ctrl(LPAM_DMA_START); + break; + + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + prtd->state &= ~ST_RUNNING; + idma.trigger_stat = LPAM_DMA_STOP; + idma_ctrl(LPAM_DMA_STOP); + break; + + default: + ret = -EINVAL; + break; + } + + spin_unlock(&prtd->lock); + + return ret; +} + +static snd_pcm_uframes_t + idma_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct idma_ctrl *prtd = runtime->private_data; + dma_addr_t src; + unsigned long res; + + pr_debug("Entered %s\n", __func__); + + spin_lock(&prtd->lock); + + idma_getpos(&src); + res = src - prtd->start; + + spin_unlock(&prtd->lock); + + pr_debug("Pointer %x \n", src); + + if (res >= snd_pcm_lib_buffer_bytes(substream)) { + if (res == snd_pcm_lib_buffer_bytes(substream)) + res = 0; + } + + return bytes_to_frames(substream->runtime, res); +} + +static int idma_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long size, offset; + int ret; + + pr_debug("Entered %s\n", __func__); + + /* From snd_pcm_lib_mmap_iomem */ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_flags |= VM_IO; + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + ret = io_remap_pfn_range(vma, vma->vm_start, + (runtime->dma_addr + offset) >> PAGE_SHIFT, + size, vma->vm_page_prot); + + return ret; +} + +static irqreturn_t iis_irq(int irqno, void *dev_id) +{ + struct idma_ctrl *prtd = (struct idma_ctrl *)dev_id; + u32 iiscon, iisahb, val, addr; + + iisahb = readl(idma.regs + I2SAHB); + iiscon = readl(idma.regs + I2SCON); + + if (iisahb & AHB_LVL0INT) + val = AHB_CLRLVL0INT; + else + val = 0; + + if (val) { + iisahb |= val; + writel(iisahb, idma.regs + I2SAHB); + + addr = readl(idma.regs + I2SLVL0ADDR); + addr += prtd->periodsz; + + if (addr >= prtd->end) + addr = LP_TXBUFF_ADDR; + + writel(addr, idma.regs + I2SLVL0ADDR); + + /* Finished dma transfer ? */ + if (iisahb & AHB_LVLINTMASK) { + if (prtd->cb) + prtd->cb(prtd->token, prtd->periodsz); + } + } + + return IRQ_HANDLED; +} + +static int idma_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct idma_ctrl *prtd; + int ret; + + pr_debug("Entered %s\n", __func__); + + snd_soc_set_runtime_hwparams(substream, &idma_hardware); + + prtd = kzalloc(sizeof(struct idma_ctrl), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + ret = request_irq(IRQ_I2S0, iis_irq, 0, "i2s", prtd); + if (ret < 0) { + pr_err("fail to claim i2s irq , ret = %d\n", ret); + kfree(prtd); + return ret; + } + + spin_lock_init(&prtd->lock); + + runtime->private_data = prtd; + + return 0; +} + +static int idma_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct idma_ctrl *prtd = runtime->private_data; + + pr_debug("Entered %s, prtd = %p\n", __func__, prtd); + + free_irq(IRQ_I2S0, prtd); + + if (!prtd) + pr_err("idma_close called with prtd == NULL\n"); + + kfree(prtd); + + return 0; +} + +static struct snd_pcm_ops idma_ops = { + .open = idma_open, + .close = idma_close, + .ioctl = snd_pcm_lib_ioctl, + .trigger = idma_trigger, + .pointer = idma_pointer, + .mmap = idma_mmap, + .hw_params = idma_hw_params, + .hw_free = idma_hw_free, + .prepare = idma_prepare, +}; + +static void idma_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + + pr_debug("Entered %s\n", __func__); + + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) + return; + + buf = &substream->dma_buffer; + if (!buf->area) + return; + + iounmap(buf->area); + + buf->area = NULL; + buf->addr = 0; +} + +static int preallocate_idma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + + pr_debug("Entered %s\n", __func__); + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + + /* Assign PCM buffer pointers */ + buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS; + buf->addr = LP_TXBUFF_ADDR; + buf->bytes = idma_hardware.buffer_bytes_max; + buf->area = (unsigned char *)ioremap(buf->addr, buf->bytes); + pr_debug("%s: VA-%p PA-%X %ubytes\n", + __func__, buf->area, buf->addr, buf->bytes); + + return 0; +} + +static u64 idma_mask = DMA_BIT_MASK(32); + +static int idma_new(struct snd_card *card, + struct snd_soc_dai *dai, struct snd_pcm *pcm) +{ + int ret = 0; + + pr_debug("Entered %s\n", __func__); + if (!card->dev->dma_mask) + card->dev->dma_mask = &idma_mask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + if (dai->driver->playback.channels_min) + ret = preallocate_idma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + + return ret; +} + +void idma_init(void *regs) +{ + spin_lock_init(&idma.lock); + idma.regs = regs; +} + +struct snd_soc_platform_driver asoc_idma_platform = { + .ops = &idma_ops, + .pcm_new = idma_new, + .pcm_free = idma_free, +}; + +static int __devinit asoc_idma_platform_probe(struct platform_device *pdev) +{ + return snd_soc_register_platform(&pdev->dev, &asoc_idma_platform); +} + +static int __devexit asoc_idma_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_driver asoc_idma_driver = { + .driver = { + .name = "samsung-idma", + .owner = THIS_MODULE, + }, + + .probe = asoc_idma_platform_probe, + .remove = __devexit_p(asoc_idma_platform_remove), +}; + +static int __init asoc_idma_init(void) +{ + return platform_driver_register(&asoc_idma_driver); +} +module_init(asoc_idma_init); + +static void __exit asoc_idma_exit(void) +{ + platform_driver_unregister(&asoc_idma_driver); +} +module_exit(asoc_idma_exit); + +MODULE_AUTHOR("Sangbeom Kim, sbkim73@samsung.com"); +MODULE_DESCRIPTION("Samsung ASoC IDMA Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-idma"); diff --git a/sound/soc/samsung/idma.h b/sound/soc/samsung/idma.h new file mode 100644 index 0000000..2b0ac37 --- /dev/null +++ b/sound/soc/samsung/idma.h @@ -0,0 +1,28 @@ +/* + * idma.h -- I2S0's Internal Dma driver + * + * Copyright (c) 2010 Samsung Electronics Co. Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __S3C_IDMA_H_ +#define __S3C_IDMA_H_ + +#ifdef CONFIG_ARCH_EXYNOS4 +#define LP_TXBUFF_ADDR (0x02020000) +#else +#define LP_TXBUFF_ADDR (0xC0000000) +#endif + +/* idma_state */ +#define LPAM_DMA_STOP 0 +#define LPAM_DMA_START 1 + +extern void idma_init(void *regs); + +#endif /* __S3C_IDMA_H_ */
On 09/06/11 09:09, Sangbeom Kim wrote:
I2S in Exynos4 and S5PC110(S5PV210) has a internal dma. It can be used low power audio mode and 2nd channel transfer.
Signed-off-by: Sangbeom Kim sbkim73@samsung.com
Looks fine to me, but needs one fix before it can be applied.
Acked-by: Liam Girdwood lrg@ti.com
sound/soc/samsung/Makefile | 2 + sound/soc/samsung/idma.c | 492 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/samsung/idma.h | 28 +++ 3 files changed, 522 insertions(+), 0 deletions(-) create mode 100644 sound/soc/samsung/idma.c create mode 100644 sound/soc/samsung/idma.h
snip
+static int idma_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
This call API has changed in -next.
Thanks
Liam
On 09/06/11 18:51, Liam Girdwood wrote:
Looks fine to me, but needs one fix before it can be applied.
Acked-by: Liam Girdwood lrg@ti.com
sound/soc/samsung/Makefile | 2 + sound/soc/samsung/idma.c | 492
++++++++++++++++++++++++++++++++++++++++++++
sound/soc/samsung/idma.h | 28 +++ 3 files changed, 522 insertions(+), 0 deletions(-) create mode 100644 sound/soc/samsung/idma.c create mode 100644 sound/soc/samsung/idma.h
snip
+static int idma_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
This call API has changed in -next.
OK, I will check -next and will apply it.
Thanks and regards, SB Kim
On Thu, Jun 9, 2011 at 5:09 PM, Sangbeom Kim sbkim73@samsung.com wrote:
I2S in Exynos4 and S5PC110(S5PV210) has a internal dma. It can be used low power audio mode and 2nd channel transfer.
Signed-off-by: Sangbeom Kim sbkim73@samsung.com
sound/soc/samsung/Makefile | 2 + sound/soc/samsung/idma.c | 492 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/samsung/idma.h | 28 +++ 3 files changed, 522 insertions(+), 0 deletions(-) create mode 100644 sound/soc/samsung/idma.c create mode 100644 sound/soc/samsung/idma.h
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 683843a..2b9e251 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -1,5 +1,6 @@ # S3c24XX Platform Support snd-soc-s3c24xx-objs := dma.o +snd-soc-idma-objs := idma.o snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o snd-soc-ac97-objs := ac97.o @@ -16,6 +17,7 @@ obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o +obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o
# S3C24XX Machine Support snd-soc-jive-wm8750-objs := jive_wm8750.o diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c new file mode 100644 index 0000000..ae19546 --- /dev/null +++ b/sound/soc/samsung/idma.c @@ -0,0 +1,492 @@ +/*
- linux/sound/soc/samsung/idma.c
- Copyright (c) 2011 Samsung Electronics Co., Ltd.
- I2S0's Internal DMA driver
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2 of the License, or (at your
- option) any later version.
- */
+#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h>
+#include "i2s.h" +#include "idma.h" +#include "dma.h"
+static const struct snd_pcm_hardware idma_hardware = {
- .info = SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_U16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_U24_LE |
- SNDRV_PCM_FMTBIT_U8 |
- SNDRV_PCM_FMTBIT_S8,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = 128 * 1024,
- .period_bytes_min = 128,
- .period_bytes_max = PAGE_SIZE*2,
- .periods_min = 1,
- .periods_max = 4,
+};
+struct idma_ctrl {
- spinlock_t lock;
- int state;
- dma_addr_t start;
- dma_addr_t pos;
- dma_addr_t end;
- dma_addr_t period;
- dma_addr_t periodsz;
- void *token;
- void (*cb)(void *dt, int bytes_xfer);
+};
+static struct idma_info {
- spinlock_t lock;
- void __iomem *regs;
- int trigger_stat;
+} idma;
+static void idma_getpos(dma_addr_t *src) +{
- *src = LP_TXBUFF_ADDR +
- (readl(idma.regs + I2STRNCNT) & 0xffffff) * 4;
+}
+void i2sdma_getpos(dma_addr_t *src) +{
- if (idma.trigger_stat == LPAM_DMA_START)
- *src = LP_TXBUFF_ADDR +
- (readl(idma.regs + I2STRNCNT) & 0xffffff) * 4;
- else
- *src = LP_TXBUFF_ADDR;
+}
+static int idma_enqueue(struct snd_pcm_substream *substream) +{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct idma_ctrl *prtd = substream->runtime->private_data;
- u32 val;
- spin_lock(&prtd->lock);
- prtd->token = (void *) substream;
- spin_unlock(&prtd->lock);
- /* Internal DMA Level0 Interrupt Address */
- val = LP_TXBUFF_ADDR + prtd->periodsz;
- writel(val, idma.regs + I2SLVL0ADDR);
- /* Start address0 of I2S internal DMA operation. */
- val = readl(idma.regs + I2SSTR0);
- val = LP_TXBUFF_ADDR;
- writel(val, idma.regs + I2SSTR0);
- /*
- * Transfer block size for I2S internal DMA.
- * Should decide transfer size before start dma operation
- */
- val = readl(idma.regs + I2SSIZE);
- val &= ~(I2SSIZE_TRNMSK << I2SSIZE_SHIFT);
- val |= (((runtime->dma_bytes >> 2) &
- I2SSIZE_TRNMSK) << I2SSIZE_SHIFT);
- writel(val, idma.regs + I2SSIZE);
- return 0;
+}
+static void idma_setcallbk(struct snd_pcm_substream *substream,
- void (*cb)(void *, int))
+{
- struct idma_ctrl *prtd = substream->runtime->private_data;
- spin_lock(&prtd->lock);
- prtd->cb = cb;
- spin_unlock(&prtd->lock);
- pr_debug("%s:%d dma_period=%x\n", __func__, __LINE__, prtd->periodsz);
+}
+static void idma_ctrl(int op) +{
- u32 val = readl(idma.regs + I2SAHB);
- spin_lock(&idma.lock);
- switch (op) {
- case LPAM_DMA_START:
- val |= (AHB_INTENLVL0 | AHB_DMAEN);
- break;
- case LPAM_DMA_STOP:
- val &= ~(AHB_INTENLVL0 | AHB_DMAEN);
- break;
- default:
- return;
- }
- writel(val, idma.regs + I2SAHB);
- spin_unlock(&idma.lock);
+}
+static void idma_done(void *id, int bytes_xfer) +{
- struct snd_pcm_substream *substream = id;
- struct idma_ctrl *prtd = substream->runtime->private_data;
- if (prtd && (prtd->state & ST_RUNNING))
- snd_pcm_period_elapsed(substream);
+}
+static int idma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct idma_ctrl *prtd = substream->runtime->private_data;
- pr_debug("Entered %s\n", __func__);
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
- memset(runtime->dma_area, 0, runtime->dma_bytes);
- prtd->start = prtd->pos = runtime->dma_addr;
- prtd->period = params_periods(params);
- prtd->periodsz = params_period_bytes(params);
- prtd->end = LP_TXBUFF_ADDR + runtime->dma_bytes;
- idma_setcallbk(substream, idma_done);
- pr_debug("DmaAddr=@%x Total=%dbytes PrdSz=%d #Prds=%d dma_area=0x%x\n",
- prtd->start, runtime->dma_bytes, prtd->periodsz,
- prtd->period, (unsigned int)runtime->dma_area);
- return 0;
+}
+static int idma_hw_free(struct snd_pcm_substream *substream) +{
- pr_debug("Entered %s\n", __func__);
- snd_pcm_set_runtime_buffer(substream, NULL);
- return 0;
+}
+static int idma_prepare(struct snd_pcm_substream *substream) +{
- struct idma_ctrl *prtd = substream->runtime->private_data;
- pr_debug("Entered %s\n", __func__);
- prtd->pos = prtd->start;
- /* flush the DMA channel */
- idma_ctrl(LPAM_DMA_STOP);
- idma_enqueue(substream);
- return 0;
+}
+static int idma_trigger(struct snd_pcm_substream *substream, int cmd) +{
- struct idma_ctrl *prtd = substream->runtime->private_data;
- int ret = 0;
- pr_debug("Entered %s\n", __func__);
- spin_lock(&prtd->lock);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- prtd->state |= ST_RUNNING;
- idma.trigger_stat = LPAM_DMA_START;
- idma_ctrl(LPAM_DMA_START);
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- prtd->state &= ~ST_RUNNING;
- idma.trigger_stat = LPAM_DMA_STOP;
- idma_ctrl(LPAM_DMA_STOP);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- spin_unlock(&prtd->lock);
- return ret;
+}
+static snd_pcm_uframes_t
- idma_pointer(struct snd_pcm_substream *substream)
+{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct idma_ctrl *prtd = runtime->private_data;
- dma_addr_t src;
- unsigned long res;
- pr_debug("Entered %s\n", __func__);
- spin_lock(&prtd->lock);
- idma_getpos(&src);
- res = src - prtd->start;
- spin_unlock(&prtd->lock);
- pr_debug("Pointer %x \n", src);
- if (res >= snd_pcm_lib_buffer_bytes(substream)) {
- if (res == snd_pcm_lib_buffer_bytes(substream))
- res = 0;
- }
- return bytes_to_frames(substream->runtime, res);
+}
+static int idma_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+{
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long size, offset;
- int ret;
- pr_debug("Entered %s\n", __func__);
- /* From snd_pcm_lib_mmap_iomem */
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- vma->vm_flags |= VM_IO;
- size = vma->vm_end - vma->vm_start;
- offset = vma->vm_pgoff << PAGE_SHIFT;
- ret = io_remap_pfn_range(vma, vma->vm_start,
- (runtime->dma_addr + offset) >> PAGE_SHIFT,
- size, vma->vm_page_prot);
- return ret;
+}
+static irqreturn_t iis_irq(int irqno, void *dev_id) +{
- struct idma_ctrl *prtd = (struct idma_ctrl *)dev_id;
- u32 iiscon, iisahb, val, addr;
- iisahb = readl(idma.regs + I2SAHB);
- iiscon = readl(idma.regs + I2SCON);
- if (iisahb & AHB_LVL0INT)
- val = AHB_CLRLVL0INT;
- else
- val = 0;
- if (val) {
- iisahb |= val;
- writel(iisahb, idma.regs + I2SAHB);
- addr = readl(idma.regs + I2SLVL0ADDR);
- addr += prtd->periodsz;
- if (addr >= prtd->end)
- addr = LP_TXBUFF_ADDR;
- writel(addr, idma.regs + I2SLVL0ADDR);
- /* Finished dma transfer ? */
- if (iisahb & AHB_LVLINTMASK) {
- if (prtd->cb)
- prtd->cb(prtd->token, prtd->periodsz);
- }
- }
- return IRQ_HANDLED;
+}
+static int idma_open(struct snd_pcm_substream *substream) +{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct idma_ctrl *prtd;
- int ret;
- pr_debug("Entered %s\n", __func__);
- snd_soc_set_runtime_hwparams(substream, &idma_hardware);
- prtd = kzalloc(sizeof(struct idma_ctrl), GFP_KERNEL);
- if (prtd == NULL)
- return -ENOMEM;
- ret = request_irq(IRQ_I2S0, iis_irq, 0, "i2s", prtd);
- if (ret < 0) {
- pr_err("fail to claim i2s irq , ret = %d\n", ret);
- kfree(prtd);
- return ret;
- }
- spin_lock_init(&prtd->lock);
- runtime->private_data = prtd;
- return 0;
+}
+static int idma_close(struct snd_pcm_substream *substream) +{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct idma_ctrl *prtd = runtime->private_data;
- pr_debug("Entered %s, prtd = %p\n", __func__, prtd);
- free_irq(IRQ_I2S0, prtd);
- if (!prtd)
- pr_err("idma_close called with prtd == NULL\n");
- kfree(prtd);
- return 0;
+}
+static struct snd_pcm_ops idma_ops = {
- .open = idma_open,
- .close = idma_close,
- .ioctl = snd_pcm_lib_ioctl,
- .trigger = idma_trigger,
- .pointer = idma_pointer,
- .mmap = idma_mmap,
- .hw_params = idma_hw_params,
- .hw_free = idma_hw_free,
- .prepare = idma_prepare,
+};
+static void idma_free(struct snd_pcm *pcm) +{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- pr_debug("Entered %s\n", __func__);
- substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
- if (!substream)
- return;
- buf = &substream->dma_buffer;
- if (!buf->area)
- return;
- iounmap(buf->area);
- buf->area = NULL;
- buf->addr = 0;
+}
+static int preallocate_idma_buffer(struct snd_pcm *pcm, int stream) +{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- pr_debug("Entered %s\n", __func__);
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- /* Assign PCM buffer pointers */
- buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS;
- buf->addr = LP_TXBUFF_ADDR;
- buf->bytes = idma_hardware.buffer_bytes_max;
- buf->area = (unsigned char *)ioremap(buf->addr, buf->bytes);
- pr_debug("%s: VA-%p PA-%X %ubytes\n",
- __func__, buf->area, buf->addr, buf->bytes);
- return 0;
+}
+static u64 idma_mask = DMA_BIT_MASK(32);
+static int idma_new(struct snd_card *card,
- struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
- int ret = 0;
- pr_debug("Entered %s\n", __func__);
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &idma_mask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
- if (dai->driver->playback.channels_min)
- ret = preallocate_idma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- return ret;
+}
+void idma_init(void *regs) +{
- spin_lock_init(&idma.lock);
- idma.regs = regs;
+}
+struct snd_soc_platform_driver asoc_idma_platform = {
- .ops = &idma_ops,
- .pcm_new = idma_new,
- .pcm_free = idma_free,
+};
+static int __devinit asoc_idma_platform_probe(struct platform_device *pdev) +{
- return snd_soc_register_platform(&pdev->dev, &asoc_idma_platform);
+}
+static int __devexit asoc_idma_platform_remove(struct platform_device *pdev) +{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
+}
+static struct platform_driver asoc_idma_driver = {
- .driver = {
- .name = "samsung-idma",
- .owner = THIS_MODULE,
- },
- .probe = asoc_idma_platform_probe,
- .remove = __devexit_p(asoc_idma_platform_remove),
+};
+static int __init asoc_idma_init(void) +{
- return platform_driver_register(&asoc_idma_driver);
+} +module_init(asoc_idma_init);
+static void __exit asoc_idma_exit(void) +{
- platform_driver_unregister(&asoc_idma_driver);
+} +module_exit(asoc_idma_exit);
+MODULE_AUTHOR("Sangbeom Kim, sbkim73@samsung.com"); +MODULE_DESCRIPTION("Samsung ASoC IDMA Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-idma"); diff --git a/sound/soc/samsung/idma.h b/sound/soc/samsung/idma.h new file mode 100644 index 0000000..2b0ac37 --- /dev/null +++ b/sound/soc/samsung/idma.h @@ -0,0 +1,28 @@ +/*
- idma.h -- I2S0's Internal Dma driver
- Copyright (c) 2010 Samsung Electronics Co. Ltd
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2 of the License, or (at your
- option) any later version.
- */
+#ifndef __S3C_IDMA_H_ +#define __S3C_IDMA_H_
+#ifdef CONFIG_ARCH_EXYNOS4 +#define LP_TXBUFF_ADDR (0x02020000) +#else +#define LP_TXBUFF_ADDR (0xC0000000) +#endif
Are there more fancy way to define the internal DMA address? Are you sure new EXYNOS4 series will use the same address?
Thank you, Kyungmin Park
+/* idma_state */ +#define LPAM_DMA_STOP 0 +#define LPAM_DMA_START 1
+extern void idma_init(void *regs);
+#endif /* __S3C_IDMA_H_ */
1.7.1
-- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Jun 9, 2011 at 9:33 PM, Kyungmin Park wrote:
+#ifndef __S3C_IDMA_H_ +#define __S3C_IDMA_H_
+#ifdef CONFIG_ARCH_EXYNOS4 +#define LP_TXBUFF_ADDR (0x02020000) +#else +#define LP_TXBUFF_ADDR (0xC0000000) +#endif
Are there more fancy way to define the internal DMA address? Are you sure new EXYNOS4 series will use the same address?
I'm not sure that the new Exynos4 series will have a same address. I will try to find more fancy way to define idma address in the next version.
Thanks SB Kim.
On Thu, Jun 9, 2011 at 1:39 PM, Sangbeom Kim sbkim73@samsung.com wrote:
I2S in Exynos4 and S5PC110(S5PV210) has a internal dma. It can be used low power audio mode and 2nd channel transfer.
For my convenience, could you please tell how does it differ from my original implementation? Most things look same, except for a few variables.
@@ -16,6 +17,7 @@ obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o +obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o
Please check that building only for s3c64xx doesn't break by this.
+static struct idma_info {
- spinlock_t lock;
- void __iomem *regs;
- int trigger_stat;
The role of trigger_stat is not necessary.
- /* Start address0 of I2S internal DMA operation. */
- val = readl(idma.regs + I2SSTR0);
Why read when you immediately overwrite it ?
- val = LP_TXBUFF_ADDR;
- writel(val, idma.regs + I2SSTR0);
+static int idma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct idma_ctrl *prtd = substream->runtime->private_data;
- pr_debug("Entered %s\n", __func__);
can we have all the pr_debug's converted to dev_debug ?
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
- memset(runtime->dma_area, 0, runtime->dma_bytes);
Is it really needed ?
- if (iisahb & AHB_LVL0INT)
- val = AHB_CLRLVL0INT;
- else
- val = 0;
val = (iisahb & AHB_LVL0INT) ? AHB_CLRLVL0INT : 0; looks better
- if (val) {
- iisahb |= val;
- writel(iisahb, idma.regs + I2SAHB);
- addr = readl(idma.regs + I2SLVL0ADDR);
- addr += prtd->periodsz;
- if (addr >= prtd->end)
- addr = LP_TXBUFF_ADDR;
This will break if ring buffer is not a multiple of period size. Either set the constraint in open or do modulo operation here.
+static int idma_close(struct snd_pcm_substream *substream) +{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct idma_ctrl *prtd = runtime->private_data;
- pr_debug("Entered %s, prtd = %p\n", __func__, prtd);
- free_irq(IRQ_I2S0, prtd);
- if (!prtd)
- pr_err("idma_close called with prtd == NULL\n");
- kfree(prtd);
Also runtime->private_data = NULL;
diff --git a/sound/soc/samsung/idma.h b/sound/soc/samsung/idma.h new file mode 100644 index 0000000..2b0ac37 --- /dev/null +++ b/sound/soc/samsung/idma.h @@ -0,0 +1,28 @@ +/*
- idma.h -- I2S0's Internal Dma driver
- Copyright (c) 2010 Samsung Electronics Co. Ltd
Copyright 2011 ?
+/* idma_state */ +#define LPAM_DMA_STOP 0 +#define LPAM_DMA_START 1
These are internal to idma.c, please move them there.
On Thu, Jun 10, 2011 at 7:08 PM, Jassi Brar wrote:
For my convenience, could you please tell how does it differ from my original implementation? Most things look same, except for a few variables.
Original code only can support specific buffer size and period count. New idma driver can work with various buffer size and multiple period. And Original code is implemented it based on wrapper arch. But This patch can support driver arch.
@@ -16,6 +17,7 @@ obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-
v2.o
obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o +obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o
Please check that building only for s3c64xx doesn't break by this.
If It have building problem, I will modify it in the next version
+static struct idma_info {
spinlock_t lock;
void __iomem *regs;
int trigger_stat;
The role of trigger_stat is not necessary.
trigger_stat can be used LP audio mode. It can be used flag for checking idma operation.
/* Start address0 of I2S internal DMA operation. */
val = readl(idma.regs + I2SSTR0);
Why read when you immediately overwrite it ?
This is useless code, It will be deleted.
+static int idma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
+{
struct snd_pcm_runtime *runtime = substream->runtime;
struct idma_ctrl *prtd = substream->runtime->private_data;
pr_debug("Entered %s\n", __func__);
can we have all the pr_debug's converted to dev_debug ?
OK, I will change it.
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = params_buffer_bytes(params);
memset(runtime->dma_area, 0, runtime->dma_bytes);
Is it really needed ?
It can delete useless data in dma buffer. But It looks not need absolutely.
if (iisahb & AHB_LVL0INT)
val = AHB_CLRLVL0INT;
else
val = 0;
val = (iisahb & AHB_LVL0INT) ? AHB_CLRLVL0INT : 0; looks better
OK, I will change
if (val) {
iisahb |= val;
writel(iisahb, idma.regs + I2SAHB);
addr = readl(idma.regs + I2SLVL0ADDR);
addr += prtd->periodsz;
if (addr >= prtd->end)
addr = LP_TXBUFF_ADDR;
This will break if ring buffer is not a multiple of period size. Either set the constraint in open or do modulo operation here.
OK,
+static int idma_close(struct snd_pcm_substream *substream) +{
struct snd_pcm_runtime *runtime = substream->runtime;
struct idma_ctrl *prtd = runtime->private_data;
pr_debug("Entered %s, prtd = %p\n", __func__, prtd);
free_irq(IRQ_I2S0, prtd);
if (!prtd)
pr_err("idma_close called with prtd == NULL\n");
kfree(prtd);
Also runtime->private_data = NULL;
OK,
diff --git a/sound/soc/samsung/idma.h b/sound/soc/samsung/idma.h new file mode 100644 index 0000000..2b0ac37 --- /dev/null +++ b/sound/soc/samsung/idma.h @@ -0,0 +1,28 @@ +/*
- idma.h -- I2S0's Internal Dma driver
- Copyright (c) 2010 Samsung Electronics Co. Ltd
Copyright 2011 ?
OK, I will modify it.
+/* idma_state */ +#define LPAM_DMA_STOP 0 +#define LPAM_DMA_START 1
These are internal to idma.c, please move them there.
OK, I will move it.
Thanks,
On Mon, Jun 13, 2011 at 1:25 PM, Sangbeom Kim sbkim73@samsung.com wrote:
On Thu, Jun 10, 2011 at 7:08 PM, Jassi Brar wrote:
For my convenience, could you please tell how does it differ from my original implementation? Most things look same, except for a few variables.
Original code only can support specific buffer size and period count. New idma driver can work with various buffer size and multiple period. And Original code is implemented it based on wrapper arch. But This patch can support driver arch.
@@ -16,6 +17,7 @@ obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-
v2.o
obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o +obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o
Please check that building only for s3c64xx doesn't break by this.
If It have building problem, I will modify it in the next version
+static struct idma_info {
- spinlock_t lock;
- void __iomem *regs;
- int trigger_stat;
The role of trigger_stat is not necessary.
trigger_stat can be used LP audio mode. It can be used flag for checking idma operation.
ST_RUNNING bit of 'state' member of idma_ctrl structure can be used to do the same.
Btw, please rename either the idma_ctrl structure or the idma_ctrl function, the names clash.
Thnx -j
On Mon, Jun 13, 2011 at 1:25 PM, Sangbeom Kim sbkim73@samsung.com wrote:
On Thu, Jun 10, 2011 at 7:08 PM, Jassi Brar wrote:
For my convenience, could you please tell how does it differ from my original implementation? Most things look same, except for a few variables.
Original code only can support specific buffer size and period count. New idma driver can work with various buffer size and multiple period.
I believe using flexible buffer and period size with secondary i/f make sense only when _system_ dma and memory is used. iDMA provides no benefit over system DMA. Whereas for LPAM mode, you can _never_ get more power saving than using full internal buffer with slightly smaller period size. So IMHO iDMA driver should use the original approach, while system dma still allows the user to have flexible buffer and period size for low latency with sec i/f.
And Original code is implemented it based on wrapper arch. But This patch can support driver arch.
Well, there was extra wrapper driver. This driver is mostly unchanged.
Frankly, I don't see enough changes to justify overriding the MODULE_AUTHOR. Of course, the copyright belongs to Samsung still.
Thnx, -j
Change platform driver to support internal dma for smdk_wm8994 and smdk_wm8580
Signed-off-by: Sangbeom Kim sbkim73@samsung.com --- sound/soc/samsung/smdk_wm8580.c | 2 +- sound/soc/samsung/smdk_wm8994.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c index 3d26f66..fa415a1 100644 --- a/sound/soc/samsung/smdk_wm8580.c +++ b/sound/soc/samsung/smdk_wm8580.c @@ -229,7 +229,7 @@ static struct snd_soc_dai_link smdk_dai[] = { .stream_name = "Playback", .cpu_dai_name = "samsung-i2s.x", .codec_dai_name = "wm8580-hifi-playback", - .platform_name = "samsung-audio", + .platform_name = "samsung-idma", .codec_name = "wm8580-codec.0-001b", .init = smdk_wm8580_init_paifrx, .ops = &smdk_ops, diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index e7c1009..d36c575 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -134,7 +134,7 @@ static struct snd_soc_dai_link smdk_dai[] = { .stream_name = "Sec_Dai", .cpu_dai_name = "samsung-i2s.4", .codec_dai_name = "wm8994-aif1", - .platform_name = "samsung-audio", + .platform_name = "samsung-idma", .codec_name = "wm8994-codec", .ops = &smdk_ops, },
On Thu, Jun 09, 2011 at 05:09:09PM +0900, Sangbeom Kim wrote:
Change platform driver to support internal dma for smdk_wm8994 and smdk_wm8580
When you resubmit for the API change update it'd be helpful to update the changelog to explain why the platforms are being changed to use only internal DMA - what is the advantage this offers over regular DMA? This will help users understand how they should configure their own systems.
On Thu, Jun 09, 2011 at 07:47PM +0900, Mark Brown wrote:
When you resubmit for the API change update it'd be helpful to update the changelog to explain why the platforms are being changed to use only internal DMA - what is the advantage this offers over regular DMA? This will help users understand how they should configure their own systems.
OK, In the next vesion, I will add detail changelog for other users.
Thanks SB Kim
On Thu, Jun 9, 2011 at 1:39 PM, Sangbeom Kim sbkim73@samsung.com wrote:
Change platform driver to support internal dma for smdk_wm8994 and smdk_wm8580
Signed-off-by: Sangbeom Kim sbkim73@samsung.com
NAK. It will break the most common user - SMDK6410, as s3c6410 doesn't have any I2S internal DMA. Please try to test the changes before submitting. I know it's boring, but it's even more important :)
Thanks -j
On Thu, Jun 9, 2011 at 4:04 PM, Jassi Brar wrote:
NAK. It will break the most common user - SMDK6410, as s3c6410 doesn't have any I2S internal DMA. Please try to test the changes before submitting. I know it's boring, but it's even more important :)
Thanks
I only focused on S5PV210 and S5PV310. I didn't consider s3c and s5p series. In the next version, I will delete it. But I had found some problem and want to share. Like this patch, If different platform driver is used for each channel, 1st channel is overwritten by platform driver of 2nd channel. Have you ever experienced before like this problem?
Thanks, SB Kim
On Fri, Jun 10, 2011 at 12:46 PM, Sangbeom Kim sbkim73@samsung.com wrote:
On Thu, Jun 9, 2011 at 4:04 PM, Jassi Brar wrote:
NAK. It will break the most common user - SMDK6410, as s3c6410 doesn't have any I2S internal DMA. Please try to test the changes before submitting. I know it's boring, but it's even more important :)
Thanks
I only focused on S5PV210 and S5PV310. I didn't consider s3c and s5p series. In the next version, I will delete it.
Though if then everything works fine with system and internal DMA, maybe it is better to assign platform_name string at runtime from some module parameter, and leaving the default to normal system DMA ?
But I had found some problem and want to share. Like this patch, If different platform driver is used for each channel, 1st channel is overwritten by platform driver of 2nd channel. Have you ever experienced before like this problem?
IIRC, I successfully tested using system DMA with primary and iDMA with secondary channel. Only the sec chan would keep playing when the system went to suspend. Though tt was long time ago and I don't remember which Samsung kernel version.
On Fri, Jun 10, 2011 at 12:56:45PM +0530, Jassi Brar wrote:
On Fri, Jun 10, 2011 at 12:46 PM, Sangbeom Kim sbkim73@samsung.com wrote:
I only focused on S5PV210 and S5PV310. I didn't consider s3c and s5p series. In the next version, I will delete it.
Though if then everything works fine with system and internal DMA, maybe it is better to assign platform_name string at runtime from some module parameter, and leaving the default to normal system DMA ?
If iDMA is always a better choice on CPUs that support it then the cpu_is_() macros might be a good way to make the selection?
On Fri, Jun 10, 2011 at 3:19 PM, Mark Brown broonie@opensource.wolfsonmicro.com wrote:
On Fri, Jun 10, 2011 at 12:56:45PM +0530, Jassi Brar wrote:
On Fri, Jun 10, 2011 at 12:46 PM, Sangbeom Kim sbkim73@samsung.com wrote:
I only focused on S5PV210 and S5PV310. I didn't consider s3c and s5p series. In the next version, I will delete it.
Though if then everything works fine with system and internal DMA, maybe it is better to assign platform_name string at runtime from some module parameter, and leaving the default to normal system DMA ?
If iDMA is always a better choice on CPUs that support it then the cpu_is_() macros might be a good way to make the selection?
Actually I remembered just after I posted - iDMA wouldn't support capture. So iDMA is a bad choice after all.
On Fri, Jun 10, 2011 at 03:42:27PM +0530, Jassi Brar wrote:
On Fri, Jun 10, 2011 at 3:19 PM, Mark Brown
If iDMA is always a better choice on CPUs that support it then the cpu_is_() macros might be a good way to make the selection?
Actually I remembered just after I posted - iDMA wouldn't support capture. So iDMA is a bad choice after all.
So I guess the relevant boards would be better off adding a separate DAI to allow applications to select the DAI they want at runtime?
On Fri, Jun 10, 2011 at 3:46 PM, Mark Brown broonie@opensource.wolfsonmicro.com wrote:
On Fri, Jun 10, 2011 at 03:42:27PM +0530, Jassi Brar wrote:
On Fri, Jun 10, 2011 at 3:19 PM, Mark Brown
If iDMA is always a better choice on CPUs that support it then the cpu_is_() macros might be a good way to make the selection?
Actually I remembered just after I posted - iDMA wouldn't support capture. So iDMA is a bad choice after all.
So I guess the relevant boards would be better off adding a separate DAI to allow applications to select the DAI they want at runtime?
oops! he is changing platform driver for the secondary i/f which doesn't support capture in the first place. So I think it can be done, though we need to see if it would be better to provide a way(control ?) to userspace to select between sys/internal dma.
On Thu, Jun 10, 2011 at 4:27 PM, Jassi Brar wrote:
Though if then everything works fine with system and internal DMA, maybe it is better to assign platform_name string at runtime from some module parameter, and leaving the default to normal system DMA ?
OK, I will leaving the default to system DMA,
But I had found some problem and want to share. Like this patch, If different platform driver is used for each channel, 1st channel is overwritten by platform driver of 2nd channel. Have you ever experienced before like this problem?
IIRC, I successfully tested using system DMA with primary and iDMA with secondary channel. Only the sec chan would keep playing when the system went to suspend. Though tt was long time ago and I don't remember which Samsung kernel version.
Your test condition is little different with my test condition. You are using wrapper architecture. But I used each driver architecture. Like in this patch, I assigned like below
{ /* Primary DAI i/f */ .name = "WM8994 AIF1", .stream_name = "Pri_Dai", .cpu_dai_name = "samsung-i2s.0", .codec_dai_name = "wm8994-aif1", .platform_name = "samsung-audio", .codec_name = "wm8994-codec", .init = smdk_wm8994_init_paiftx, .ops = &smdk_ops, }, { /* Sec_Fifo Playback i/f */ .name = "Sec_FIFO TX", .stream_name = "Sec_Dai", .cpu_dai_name = "samsung-i2s.4", .codec_dai_name = "wm8994-aif1", .platform_name = "samsung-idma", .codec_name = "wm8994-codec", .ops = &smdk_ops, },
Primary is used by system dma, Secondary is used by idma,
I try to use secondary by aplay, It can work fine. ============================================================================ ============== [root@samsung ~]# /mars/bin/aplay -Dplughw:0,1,0 /mars/share/sounds/alsa/Front_Center.wav Entered idma_open Playing WAVE '/mars/share/sounds/alsa/Front_Center.wav' [I2S] Initialize sec_fifo idma [I2S] Done hw params Entered idma_hw_params idma_setcallbk:125 dma_period=2000 DmaAddr=@2020000 Total=96000bytes PrdSz=8192 #Prds=11 dma_area=0xd0840000 Entered idma_mmap Entered idma_prepare : Signed 16 bit Little Endian, Rate 48000 Hz, Mono Entered idma_trigger Entered idma_pointer, regs=d0822000 Pointer 2020004 Snip................. Entered idma_pointer, regs=d0822000 Pointer 202e00c Entered idma_pointer, regs=d0822000 Pointer 203000c Entered idma_pointer, regs=d0822000 Pointer 203200c Entered idma_pointer, regs=d0822000 Pointer 203400c Entered idma_pointer, regs=d0822000 Pointer 203600c Entered idma_trigger Entered idma_hw_free Entered idma_hw_free Entered idma_close, prtd = cedb8bc0 [root@Samsung ~]# ============================================================================ ============== There is no problem and sound is good,
But If I try to use primary i/f, It can't work properly. ============================================================================ ============== [root@samsung ~]# /mars/bin/aplay -Dplughw:0,0,0 /mars/share/sounds/alsa/Front_Center.wav Entered dma_open Playing WAVE '/mars/share/sounds/alsa/Front_Center.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Mono [I2S] Done hw params Entered dma_hw_params params cf82e6b0, client cf82e6b0, channel 16 Entered idma_mmap Entered dma_prepare Entered dma_enqueue dma_enqueue: loaded 0, limit 12 dma_loaded: 0 dma_loaded: 1 dma_loaded: 2 dma_loaded: 3 dma_loaded: 4 dma_loaded: 5 dma_loaded: 6 dma_loaded: 7 dma_loaded: 8 dma_loaded: 9 dma_loaded: 10 dma_loaded: 11 Entered dma_trigger Entered idma_pointer, regs=d0822000 Pointer 2020000 Entered audio_buffdone
Snip....................
Pointer 2020000 Entered audio_buffdone Entered idma_pointer, regs=d0822000 Pointer 2020000 Entered dma_trigger Entered audio_buffdone Entered dma_prepare Entered audio_buffdone Entered audio_buffdone Entered dma_enqueue dma_enqueue: loaded 0, limit 12 dma_loaded: 0 dma_loaded: 1 dma_loaded: 2 dma_loaded: 3 dma_loaded: 4 dma_loaded: 5 dma_loaded: 6 dma_loaded: 7 dma_loaded: 8 dma_loaded: 9 dma_loaded: 10 dma_loaded: 11 underrun!!! (at least 0.072 ms long) Entered dma_trigger Entered idma_pointer, regs=d0822000 Pointer 2020000 Entered audio_buffdone Entered idma_pointer, regs=d0822000 Pointer 2020000 Snip................ Entered audio_buffdone Entered idma_pointer, regs=d0822000 Pointer 2020000 Entered dma_trigger Entered audio_buffdone Entered dma_prepare Entered audio_buffdone Entered audio_buffdone Entered audio_buffdone Entered audio_buffdone Entered dma_enqueue dma_enqueue: loaded 0, limit 12 dma_loaded: 0 dma_loaded: 1 dma_loaded: 2 dma_loaded: 3 dma_loaded: 4 dma_loaded: 5 dma_loaded: 6 dma_loaded: 7 dma_loaded: 8 dma_loaded: 9 dma_loaded: 10 dma_loaded: 11 Entered dma_trigger underrun!!! (at least 0.030 ms long) Entered audio_buffdone Entered idma_pointer, regs=d0822000 Pointer 2020000 Entered audio_buffdone Entered idma_pointer, regs=d0822000 Pointer 2020000 Entered audio_buffdone Snip ............ Entered audio_buffdone Entered idma_pointer, regs=d0822000 Pointer 2020000 Entered dma_trigger Entered audio_buffdone Entered dma_hw_free Entered audio_buffdone Entered audio_buffdone Entered audio_buffdone Entered audio_buffdone Entered audio_buffdone Entered dma_hw_free Entered dma_close ============================================================================ ==== As you see above debugging log, Although system dma is assigned, instead using dma api, idma api is used. This is not a dma & idma driver's bug. This is platform driver handling bugs. There is no machine driver using different platform driver in the ASoC. So I guess that It is never confirmed before. I want to share this problem. and I want to fix this problem. So I want to get your opinion. Please check the above debug log and give your advice.
Thanks, SB Kim
Exynos4 and S5PC110(S5PV210) has Internal dma(idma) in AUDSS. To support idma, register idma platform device.
Signed-off-by: Sangbeom Kim sbkim73@samsung.com --- arch/arm/mach-exynos4/include/mach/irqs.h | 2 ++ arch/arm/mach-exynos4/mach-smdkv310.c | 1 + arch/arm/mach-s5pv210/mach-smdkv210.c | 1 + arch/arm/plat-samsung/dev-asocdma.c | 10 ++++++++++ arch/arm/plat-samsung/include/plat/devs.h | 1 + 5 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-exynos4/include/mach/irqs.h b/arch/arm/mach-exynos4/include/mach/irqs.h index 5d03730..4c78dc0 100644 --- a/arch/arm/mach-exynos4/include/mach/irqs.h +++ b/arch/arm/mach-exynos4/include/mach/irqs.h @@ -73,6 +73,8 @@ #define IRQ_SYSMMU_MFC_M1_0 COMBINER_IRQ(5, 6) #define IRQ_SYSMMU_PCIE_0 COMBINER_IRQ(5, 7)
+#define IRQ_I2S0 COMBINER_IRQ(17, 0) + #define IRQ_PDMA0 COMBINER_IRQ(21, 0) #define IRQ_PDMA1 COMBINER_IRQ(21, 1)
diff --git a/arch/arm/mach-exynos4/mach-smdkv310.c b/arch/arm/mach-exynos4/mach-smdkv310.c index 1526764..7c538c0 100644 --- a/arch/arm/mach-exynos4/mach-smdkv310.c +++ b/arch/arm/mach-exynos4/mach-smdkv310.c @@ -187,6 +187,7 @@ static struct platform_device *smdkv310_devices[] __initdata = { &exynos4_device_pd[PD_GPS], &exynos4_device_sysmmu, &samsung_asoc_dma, + &samsung_asoc_idma, &smdkv310_smsc911x, };
diff --git a/arch/arm/mach-s5pv210/mach-smdkv210.c b/arch/arm/mach-s5pv210/mach-smdkv210.c index c6a9e86..c6900f8 100644 --- a/arch/arm/mach-s5pv210/mach-smdkv210.c +++ b/arch/arm/mach-s5pv210/mach-smdkv210.c @@ -267,6 +267,7 @@ static struct platform_device *smdkv210_devices[] __initdata = { &s5pv210_device_iis0, &s5pv210_device_spdif, &samsung_asoc_dma, + &samsung_asoc_idma, &samsung_device_keypad, &smdkv210_dm9000, &smdkv210_lcd_lte480wv, diff --git a/arch/arm/plat-samsung/dev-asocdma.c b/arch/arm/plat-samsung/dev-asocdma.c index a068c4f..832fa83 100644 --- a/arch/arm/plat-samsung/dev-asocdma.c +++ b/arch/arm/plat-samsung/dev-asocdma.c @@ -23,3 +23,13 @@ struct platform_device samsung_asoc_dma = { } }; EXPORT_SYMBOL(samsung_asoc_dma); + +struct platform_device samsung_asoc_idma = { + .name = "samsung-idma", + .id = -1, + .dev = { + .dma_mask = &audio_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + } +}; +EXPORT_SYMBOL(samsung_asoc_idma); diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h index b61b8ee..54d4cd6 100644 --- a/arch/arm/plat-samsung/include/plat/devs.h +++ b/arch/arm/plat-samsung/include/plat/devs.h @@ -36,6 +36,7 @@ extern struct platform_device s3c64xx_device_spi0; extern struct platform_device s3c64xx_device_spi1;
extern struct platform_device samsung_asoc_dma; +extern struct platform_device samsung_asoc_idma;
extern struct platform_device s3c64xx_device_pcm0; extern struct platform_device s3c64xx_device_pcm1;
participants (5)
-
Jassi Brar
-
Kyungmin Park
-
Liam Girdwood
-
Mark Brown
-
Sangbeom Kim