[alsa-devel] [PATCH 0/3] ASoC: atmel-pcm: add dma support
This patch series is intend to add dma support for atmel pcm which is based on snd soc dmaengine pcm framework
Bo Shen (3): ASoC: snd_dmaengine_pcm: add inline empty function ASoC: atmel-pcm: prepare for adding dma support ASoC: atmel-pcm: add dma support
include/sound/dmaengine_pcm.h | 53 +++++++++ sound/soc/atmel/atmel-pcm.c | 247 ++++++++++++++++++++++++++++++++++----- sound/soc/atmel/atmel-pcm.h | 4 +- sound/soc/atmel/atmel_ssc_dai.c | 30 ++--- 4 files changed, 292 insertions(+), 42 deletions(-)
As to atmel pcm driver support pdc and dma for audio transfer, which will be used for audio transfer depends on the ssc capability.
If without these inline empty functions, when compile the driver, it will give out following information. So, add these inline empty functions. ---<8--- sound/built-in.o: In function `atmel_pcm_hw_free': last.c:(.text+0x20a84): undefined reference to `snd_dmaengine_pcm_get_data' sound/built-in.o: In function `atmel_pcm_dma_prepare': last.c:(.text+0x20adc): undefined reference to `snd_dmaengine_pcm_get_data' sound/built-in.o: In function `atmel_pcm_hw_params': last.c:(.text+0x20c1c): undefined reference to `snd_dmaengine_pcm_open' last.c:(.text+0x20c40): undefined reference to `snd_dmaengine_pcm_set_data' last.c:(.text+0x20c50): undefined reference to `snd_hwparams_to_dma_slave_config' last.c:(.text+0x20ca0): undefined reference to `snd_dmaengine_pcm_get_chan' last.c:(.text+0x20cd0): undefined reference to `snd_dmaengine_pcm_close' last.c:(.text+0x20d00): undefined reference to `snd_dmaengine_pcm_get_chan' sound/built-in.o: In function `atmel_pcm_dma_irq': last.c:(.text+0x20ec0): undefined reference to `snd_dmaengine_pcm_get_data' sound/built-in.o: In function `atmel_pcm_close': last.c:(.text+0x20f90): undefined reference to `snd_dmaengine_pcm_get_data' last.c:(.text+0x20f9c): undefined reference to `snd_dmaengine_pcm_close' sound/built-in.o: In function `atmel_pcm_platform_register': last.c:(.text+0x21078): undefined reference to `snd_dmaengine_pcm_trigger' last.c:(.text+0x2107c): undefined reference to `snd_dmaengine_pcm_pointer_no_residue' make: *** [vmlinux] Error 1 --->8---
Signed-off-by: Bo Shen voice.shen@atmel.com --- include/sound/dmaengine_pcm.h | 53 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index b877334..58241e9 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -32,6 +32,7 @@ snd_pcm_substream_to_dma_direction(const struct snd_pcm_substream *substream) return DMA_DEV_TO_MEM; }
+#ifdef CONFIG_SND_SOC_DMAENGINE_PCM void snd_dmaengine_pcm_set_data(struct snd_pcm_substream *substream, void *data); void *snd_dmaengine_pcm_get_data(struct snd_pcm_substream *substream);
@@ -46,5 +47,57 @@ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream);
struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream); +#else +static inline void +snd_dmaengine_pcm_set_data(struct snd_pcm_substream *substream, void *data) +{ +} +static inline void +*snd_dmaengine_pcm_get_data(struct snd_pcm_substream *substream) +{ + return NULL; +} + +static inline int +snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, + const struct snd_pcm_hw_params *params, + struct dma_slave_config *slave_config) +{ + return 0; +} +static inline int +snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + return 0; +} +static inline snd_pcm_uframes_t +snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) +{ + return 0; +} +static inline snd_pcm_uframes_t +snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream) +{ + return 0; +} + +static inline int +snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, + dma_filter_fn filter_fn, void *filter_data) +{ + return 0; +} +static inline int +snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static inline struct dma_chan +*snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) +{ + return NULL; +} +#endif
#endif
Change xfer_size name to apply to PDC and DMA aswell. Specify overrun bit in interrupt mask.
Signed-off-by: Nicolas Ferre nicolas.ferre@atmel.com [voice.shen@atmel.com: split as one patch] Signed-off-by: Bo Shen voice.shen@atmel.com --- sound/soc/atmel/atmel-pcm.c | 36 ++++++++++++++++++++---------------- sound/soc/atmel/atmel-pcm.h | 4 +++- sound/soc/atmel/atmel_ssc_dai.c | 30 +++++++++++++++++------------- 3 files changed, 40 insertions(+), 30 deletions(-)
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index 40e17d1..a3471e9 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c @@ -53,7 +53,7 @@ /* TODO: These values were taken from the AT91 platform driver, check * them against real values for AT32 */ -static const struct snd_pcm_hardware atmel_pcm_hardware = { +static const struct snd_pcm_hardware atmel_pcm_pdc_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | @@ -66,6 +66,8 @@ static const struct snd_pcm_hardware atmel_pcm_hardware = { .buffer_bytes_max = 32 * 1024, };
+static const struct snd_pcm_hardware *atmel_pcm_hardware; +
/*--------------------------------------------------------------------------*\ * Data types @@ -94,7 +96,7 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = atmel_pcm_hardware.buffer_bytes_max; + size_t size = atmel_pcm_hardware->buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; @@ -116,7 +118,7 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, /*--------------------------------------------------------------------------*\ * ISR *--------------------------------------------------------------------------*/ -static void atmel_pcm_dma_irq(u32 ssc_sr, +static void atmel_pcm_pdc_irq(u32 ssc_sr, struct snd_pcm_substream *substream) { struct atmel_runtime_data *prtd = substream->runtime->private_data; @@ -142,7 +144,7 @@ static void atmel_pcm_dma_irq(u32 ssc_sr, ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->period_ptr); ssc_writex(params->ssc->regs, params->pdc->xcr, - prtd->period_size / params->pdc_xfer_size); + prtd->period_size / params->data_xfer_size); ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable); } @@ -156,7 +158,7 @@ static void atmel_pcm_dma_irq(u32 ssc_sr, ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->period_ptr); ssc_writex(params->ssc->regs, params->pdc->xncr, - prtd->period_size / params->pdc_xfer_size); + prtd->period_size / params->data_xfer_size); }
snd_pcm_period_elapsed(substream); @@ -180,7 +182,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, runtime->dma_bytes = params_buffer_bytes(params);
prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - prtd->params->dma_intr_handler = atmel_pcm_dma_irq; + prtd->params->dma_intr_handler = atmel_pcm_pdc_irq;
prtd->dma_buffer = runtime->dma_addr; prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes; @@ -209,7 +211,7 @@ static int atmel_pcm_hw_free(struct snd_pcm_substream *substream) return 0; }
-static int atmel_pcm_prepare(struct snd_pcm_substream *substream) +static int atmel_pcm_pdc_prepare(struct snd_pcm_substream *substream) { struct atmel_runtime_data *prtd = substream->runtime->private_data; struct atmel_pcm_dma_params *params = prtd->params; @@ -221,7 +223,7 @@ static int atmel_pcm_prepare(struct snd_pcm_substream *substream) return 0; }
-static int atmel_pcm_trigger(struct snd_pcm_substream *substream, +static int atmel_pcm_pdc_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *rtd = substream->runtime; @@ -240,13 +242,13 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream, ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->period_ptr); ssc_writex(params->ssc->regs, params->pdc->xcr, - prtd->period_size / params->pdc_xfer_size); + prtd->period_size / params->data_xfer_size);
prtd->period_ptr += prtd->period_size; ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->period_ptr); ssc_writex(params->ssc->regs, params->pdc->xncr, - prtd->period_size / params->pdc_xfer_size); + prtd->period_size / params->data_xfer_size);
pr_debug("atmel-pcm: trigger: " "period_ptr=%lx, xpr=%u, " @@ -264,7 +266,7 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
pr_debug("sr=%u imr=%u\n", ssc_readx(params->ssc->regs, SSC_SR), - ssc_readx(params->ssc->regs, SSC_IER)); + ssc_readx(params->ssc->regs, SSC_IMR)); break; /* SNDRV_PCM_TRIGGER_START */
case SNDRV_PCM_TRIGGER_STOP: @@ -287,7 +289,7 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream, return ret; }
-static snd_pcm_uframes_t atmel_pcm_pointer( +static snd_pcm_uframes_t atmel_pcm_pdc_pointer( struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -311,7 +313,7 @@ static int atmel_pcm_open(struct snd_pcm_substream *substream) struct atmel_runtime_data *prtd; int ret = 0;
- snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware); + snd_soc_set_runtime_hwparams(substream, atmel_pcm_hardware);
/* ensure that buffer size is a multiple of period size */ ret = snd_pcm_hw_constraint_integer(runtime, @@ -352,9 +354,9 @@ static struct snd_pcm_ops atmel_pcm_ops = { .ioctl = snd_pcm_lib_ioctl, .hw_params = atmel_pcm_hw_params, .hw_free = atmel_pcm_hw_free, - .prepare = atmel_pcm_prepare, - .trigger = atmel_pcm_trigger, - .pointer = atmel_pcm_pointer, + .prepare = atmel_pcm_pdc_prepare, + .trigger = atmel_pcm_pdc_trigger, + .pointer = atmel_pcm_pdc_pointer, .mmap = atmel_pcm_mmap, };
@@ -475,6 +477,8 @@ static struct snd_soc_platform_driver atmel_soc_platform = {
int atmel_pcm_platform_register(struct device *dev) { + atmel_pcm_hardware = &atmel_pcm_pdc_hardware; + return snd_soc_register_platform(dev, &atmel_soc_platform); } EXPORT_SYMBOL(atmel_pcm_platform_register); diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h index e6d67b3..261331d 100644 --- a/sound/soc/atmel/atmel-pcm.h +++ b/sound/soc/atmel/atmel-pcm.h @@ -50,6 +50,7 @@ struct atmel_pdc_regs { struct atmel_ssc_mask { u32 ssc_enable; /* SSC recv/trans enable */ u32 ssc_disable; /* SSC recv/trans disable */ + u32 ssc_error; /* SSC error conditions */ u32 ssc_endx; /* SSC ENDTX or ENDRX */ u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */ u32 pdc_enable; /* PDC recv/trans enable */ @@ -66,7 +67,8 @@ struct atmel_ssc_mask { */ struct atmel_pcm_dma_params { char *name; /* stream identifier */ - int pdc_xfer_size; /* PDC counter increment in bytes */ + int data_xfer_size; /* PDC counter increment in bytes, + DMA data transfer size in bytes */ struct ssc_device *ssc; /* SSC device for stream */ struct atmel_pdc_regs *pdc; /* PDC receive or transmit registers */ struct atmel_ssc_mask *mask; /* SSC & PDC status bits */ diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 7932c05..a87d6d8 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -82,6 +82,7 @@ static struct atmel_ssc_mask ssc_tx_mask = { static struct atmel_ssc_mask ssc_rx_mask = { .ssc_enable = SSC_BIT(CR_RXEN), .ssc_disable = SSC_BIT(CR_RXDIS), + .ssc_error = SSC_BIT(SR_OVRUN), .ssc_endx = SSC_BIT(SR_ENDRX), .ssc_endbuf = SSC_BIT(SR_RXBUFF), .pdc_enable = ATMEL_PDC_RXTEN, @@ -175,7 +176,8 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id) if ((dma_params != NULL) && (dma_params->dma_intr_handler != NULL)) { ssc_substream_mask = (dma_params->mask->ssc_endx | - dma_params->mask->ssc_endbuf); + dma_params->mask->ssc_endbuf | + dma_params->mask->ssc_error); if (ssc_sr & ssc_substream_mask) { dma_params->dma_intr_handler(ssc_sr, dma_params-> @@ -368,19 +370,19 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: bits = 8; - dma_params->pdc_xfer_size = 1; + dma_params->data_xfer_size = 1; break; case SNDRV_PCM_FORMAT_S16_LE: bits = 16; - dma_params->pdc_xfer_size = 2; + dma_params->data_xfer_size = 2; break; case SNDRV_PCM_FORMAT_S24_LE: bits = 24; - dma_params->pdc_xfer_size = 4; + dma_params->data_xfer_size = 4; break; case SNDRV_PCM_FORMAT_S32_LE: bits = 32; - dma_params->pdc_xfer_size = 4; + dma_params->data_xfer_size = 4; break; default: printk(KERN_WARNING "atmel_ssc_dai: unsupported PCM format"); @@ -553,15 +555,17 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, /* Reset the SSC and its PDC registers */ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
- ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0); + if (!ssc_p->ssc->pdata->use_dma) { + ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
- ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0); + }
ret = request_irq(ssc_p->ssc->irq, atmel_ssc_interrupt, 0, ssc_p->name, ssc_p);
Add dmaengine specific routines and replace PDC ones in pcm_ops if appropriate.
Signed-off-by: Nicolas Ferre nicolas.ferre@atmel.com [voice.shen@atmel.com: adapt to soc dmaengine framework] Signed-off-by: Bo Shen voice.shen@atmel.com --- sound/soc/atmel/atmel-pcm.c | 215 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 201 insertions(+), 14 deletions(-)
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index a3471e9..d18a241 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c @@ -37,15 +37,19 @@ #include <linux/slab.h> #include <linux/dma-mapping.h> #include <linux/atmel_pdc.h> +#include <linux/dmaengine.h> #include <linux/atmel-ssc.h> +#include <linux/platform_data/dma-atmel.h>
#include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/dmaengine_pcm.h>
#include "atmel-pcm.h"
+static int use_dma;
/*--------------------------------------------------------------------------*\ * Hardware definition @@ -66,6 +70,20 @@ static const struct snd_pcm_hardware atmel_pcm_pdc_hardware = { .buffer_bytes_max = 32 * 1024, };
+static const struct snd_pcm_hardware atmel_pcm_dma_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_PAUSE, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .period_bytes_min = 256, /* lighting DMA overhead */ + .period_bytes_max = 2 * 0xffff, /* if 2 bytes format */ + .periods_min = 8, + .periods_max = 1024, /* no limit */ + .buffer_bytes_max = 64 * 1024, /* 64KiB */ +}; + static const struct snd_pcm_hardware *atmel_pcm_hardware;
@@ -85,6 +103,9 @@ struct atmel_runtime_data { u32 pdc_xcr_save; u32 pdc_xnpr_save; u32 pdc_xncr_save; + + /* dmaengine data */ + struct at_dma_slave atslave; };
@@ -164,6 +185,101 @@ static void atmel_pcm_pdc_irq(u32 ssc_sr, snd_pcm_period_elapsed(substream); }
+/** + * atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC + * + * We use DMAENGINE to send/receive data to/from SSC so this ISR is only to + * check if any overrun occured. + */ +static void atmel_pcm_dma_irq(u32 ssc_sr, + struct snd_pcm_substream *substream) +{ + struct atmel_runtime_data *prtd = snd_dmaengine_pcm_get_data(substream); + struct atmel_pcm_dma_params *params = prtd->params; + + if (ssc_sr & params->mask->ssc_error) { + if (snd_pcm_running(substream)) + pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x)\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK + ? "underrun" : "overrun", params->name, + ssc_sr); + + /* stop RX and capture: will be enabled again at restart */ + ssc_writex(params->ssc->regs, SSC_CR, + params->mask->ssc_disable); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + + /* now drain RHR and read status to remove xrun condition */ + ssc_readx(params->ssc->regs, SSC_RHR); + ssc_readx(params->ssc->regs, SSC_SR); + } +} + +/*--------------------------------------------------------------------------*\ + * DMAENGINE operations +*--------------------------------------------------------------------------*/ +static bool filter(struct dma_chan *chan, void *slave) +{ + struct at_dma_slave *sl = slave; + + if (sl->dma_dev == chan->device->dev) { + chan->private = sl; + return true; + } else { + return false; + } +} + +static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct atmel_runtime_data *prtd = runtime->private_data; + struct ssc_device *ssc = prtd->params->ssc; + struct dma_chan *dma_chan; + struct dma_slave_config slave_config; + struct at_dma_slave *sdata = NULL; + int ret; + + if (ssc->pdev) + sdata = ssc->pdev->dev.platform_data; + + ret = snd_dmaengine_pcm_open(substream, filter, sdata); + if (ret) { + pr_err("atmel-pcm: dmaengine pcm open failed\n"); + return -EINVAL; + } + snd_dmaengine_pcm_set_data(substream, prtd); + + ret = snd_hwparams_to_dma_slave_config(substream, params, + &slave_config); + if (ret) { + pr_err("atmel-pcm: hwparams to dma slave configure failed\n"); + goto err; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config.dst_addr = (dma_addr_t)ssc->phybase + SSC_THR; + slave_config.dst_maxburst = 1; + } else { + slave_config.src_addr = (dma_addr_t)ssc->phybase + SSC_RHR; + slave_config.src_maxburst = 1; + } + slave_config.device_fc = false; + + dma_chan = snd_dmaengine_pcm_get_chan(substream); + if (dmaengine_slave_config(dma_chan, &slave_config)) { + pr_err("atmel-pcm: failed to configure dma channel\n"); + ret = -EBUSY; + goto err; + } + + return 0; + +err: + snd_dmaengine_pcm_close(substream); + return ret; +}
/*--------------------------------------------------------------------------*\ * PCM operations @@ -174,43 +290,74 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct atmel_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct dma_chan *dma_chan = NULL; + int ret;
/* this may get called several times by oss emulation * with different params */
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - runtime->dma_bytes = params_buffer_bytes(params);
prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - prtd->params->dma_intr_handler = atmel_pcm_pdc_irq;
prtd->dma_buffer = runtime->dma_addr; prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes; prtd->period_size = params_period_bytes(params);
+ if (use_dma) { + ret = atmel_pcm_configure_dma(substream, params); + if (ret) { + pr_err("atmel-pcm: failed to configure dma"); + return ret; + } + + dma_chan = snd_dmaengine_pcm_get_chan(substream); + + prtd->params->dma_intr_handler = atmel_pcm_dma_irq; + } else { + prtd->params->dma_intr_handler = atmel_pcm_pdc_irq; + } + pr_debug("atmel-pcm: " - "hw_params: DMA for %s initialized " + "hw_params: %s%s for %s initialized " "(dma_bytes=%u, period_size=%u)\n", + dma_chan ? "DMA " : "PDC", + dma_chan ? dma_chan_name(dma_chan) : "", prtd->params->name, runtime->dma_bytes, prtd->period_size); + return 0; }
static int atmel_pcm_hw_free(struct snd_pcm_substream *substream) { - struct atmel_runtime_data *prtd = substream->runtime->private_data; - struct atmel_pcm_dma_params *params = prtd->params; + struct atmel_runtime_data *prtd; + struct atmel_pcm_dma_params *params;
- if (params != NULL) { - ssc_writex(params->ssc->regs, SSC_PDC_PTCR, - params->mask->pdc_disable); - prtd->params->dma_intr_handler = NULL; + if (use_dma) { + prtd = snd_dmaengine_pcm_get_data(substream); + params = prtd->params; + + if (params != NULL) + prtd->params->dma_intr_handler = NULL; + } else { + prtd = substream->runtime->private_data; + params = prtd->params; + + if (params != NULL) { + ssc_writex(params->ssc->regs, SSC_PDC_PTCR, + params->mask->pdc_disable); + prtd->params->dma_intr_handler = NULL; + } }
return 0; }
+/*--------------------------------------------------------------------------*\ + * PCM callbacks using PDC +*--------------------------------------------------------------------------*/ static int atmel_pcm_pdc_prepare(struct snd_pcm_substream *substream) { struct atmel_runtime_data *prtd = substream->runtime->private_data; @@ -307,6 +454,22 @@ static snd_pcm_uframes_t atmel_pcm_pdc_pointer( return x; }
+/*--------------------------------------------------------------------------*\ + * PCM callbacks using DMAENGINE +*--------------------------------------------------------------------------*/ +static int atmel_pcm_dma_prepare(struct snd_pcm_substream *substream) +{ + struct atmel_runtime_data *prtd = snd_dmaengine_pcm_get_data(substream); + struct atmel_pcm_dma_params *params = prtd->params; + + ssc_writex(params->ssc->regs, SSC_IER, params->mask->ssc_error); + ssc_writex(params->ssc->regs, SSC_CR, params->mask->ssc_enable); + return 0; +} + +/*--------------------------------------------------------------------------*\ + * PCM open/close/mmap +*--------------------------------------------------------------------------*/ static int atmel_pcm_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -334,7 +497,14 @@ static int atmel_pcm_open(struct snd_pcm_substream *substream)
static int atmel_pcm_close(struct snd_pcm_substream *substream) { - struct atmel_runtime_data *prtd = substream->runtime->private_data; + struct atmel_runtime_data *prtd; + + if (use_dma) { + prtd = snd_dmaengine_pcm_get_data(substream); + snd_dmaengine_pcm_close(substream); + } else { + prtd = substream->runtime->private_data; + }
kfree(prtd); return 0; @@ -426,12 +596,14 @@ static int atmel_pcm_suspend(struct snd_soc_dai *dai) if (!runtime) return 0;
+ if (!use_dma) + return 0; + prtd = runtime->private_data; params = prtd->params;
/* disable the PDC and save the PDC registers */ - - ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable); + ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr); prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr); @@ -450,6 +622,9 @@ static int atmel_pcm_resume(struct snd_soc_dai *dai) if (!runtime) return 0;
+ if (use_dma) + return 0; + prtd = runtime->private_data; params = prtd->params;
@@ -458,8 +633,8 @@ static int atmel_pcm_resume(struct snd_soc_dai *dai) ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save); ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save); ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save); - ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable); + return 0; } #else @@ -477,7 +652,19 @@ static struct snd_soc_platform_driver atmel_soc_platform = {
int atmel_pcm_platform_register(struct device *dev) { - atmel_pcm_hardware = &atmel_pcm_pdc_hardware; + struct platform_device *pdev = to_platform_device(dev); + struct ssc_device *ssc = platform_get_drvdata(pdev); + + if (ssc->pdata->use_dma) { + use_dma = 1; + atmel_pcm_ops.prepare = atmel_pcm_dma_prepare; + atmel_pcm_ops.trigger = snd_dmaengine_pcm_trigger; + atmel_pcm_ops.pointer = snd_dmaengine_pcm_pointer_no_residue; + + atmel_pcm_hardware = &atmel_pcm_dma_hardware; + } else { + atmel_pcm_hardware = &atmel_pcm_pdc_hardware; + }
return snd_soc_register_platform(dev, &atmel_soc_platform); }
On Fri, Nov 23, 2012 at 02:14:17PM +0800, Bo Shen wrote:
Add dmaengine specific routines and replace PDC ones in pcm_ops if appropriate.
Signed-off-by: Nicolas Ferre nicolas.ferre@atmel.com [voice.shen@atmel.com: adapt to soc dmaengine framework] Signed-off-by: Bo Shen voice.shen@atmel.com
Why is this not just two separate drivers?
Hi Mark,
On 11/23/2012 22:27, Mark Brown wrote:
On Fri, Nov 23, 2012 at 02:14:17PM +0800, Bo Shen wrote:
Add dmaengine specific routines and replace PDC ones in pcm_ops if appropriate.
Signed-off-by: Nicolas Ferre nicolas.ferre@atmel.com [voice.shen@atmel.com: adapt to soc dmaengine framework] Signed-off-by: Bo Shen voice.shen@atmel.com
Why is this not just two separate drivers?
I am not sure I got your means. As I understand, I should keep the driver using pdc and won't touch it. And create new a new driver using dma, am I right?
or else, ...?
Best Regards Bo Shen
On Mon, Nov 26, 2012 at 10:04:28AM +0800, Bo Shen wrote:
I am not sure I got your means. As I understand, I should keep the driver using pdc and won't touch it. And create new a new driver using dma, am I right?
Yes.
participants (2)
-
Bo Shen
-
Mark Brown