diff options
Diffstat (limited to 'sound/soc/atmel/atmel-pcm.c')
-rw-r--r-- | sound/soc/atmel/atmel-pcm.c | 401 |
1 files changed, 12 insertions, 389 deletions
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index 9b84f98..e99f181 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c @@ -32,80 +32,25 @@ */ #include <linux/module.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/slab.h> #include <linux/dma-mapping.h> -#include <linux/atmel_pdc.h> -#include <linux/atmel-ssc.h> - -#include <sound/core.h> #include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> - #include "atmel-pcm.h" - -/*--------------------------------------------------------------------------*\ - * Hardware definition -\*--------------------------------------------------------------------------*/ -/* 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 = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .period_bytes_min = 32, - .period_bytes_max = 8192, - .periods_min = 2, - .periods_max = 1024, - .buffer_bytes_max = 32 * 1024, -}; - - -/*--------------------------------------------------------------------------*\ - * Data types -\*--------------------------------------------------------------------------*/ -struct atmel_runtime_data { - struct atmel_pcm_dma_params *params; - dma_addr_t dma_buffer; /* physical address of dma buffer */ - dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */ - size_t period_size; - - dma_addr_t period_ptr; /* physical address of next period */ - - /* PDC register save */ - u32 pdc_xpr_save; - u32 pdc_xcr_save; - u32 pdc_xnpr_save; - u32 pdc_xncr_save; -}; - - -/*--------------------------------------------------------------------------*\ - * Helper functions -\*--------------------------------------------------------------------------*/ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = atmel_pcm_hardware.buffer_bytes_max; + size_t size = ATMEL_SSC_DMABUF_SIZE; buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; buf->private_data = NULL; buf->area = dma_alloc_coherent(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - pr_debug("atmel-pcm:" - "preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", - (void *) buf->area, - (void *) buf->addr, - size); + &buf->addr, GFP_KERNEL); + pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d\n", + (void *)buf->area, (void *)buf->addr, size); if (!buf->area) return -ENOMEM; @@ -113,258 +58,19 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, buf->bytes = size; return 0; } -/*--------------------------------------------------------------------------*\ - * ISR -\*--------------------------------------------------------------------------*/ -static void atmel_pcm_dma_irq(u32 ssc_sr, - struct snd_pcm_substream *substream) -{ - struct atmel_runtime_data *prtd = substream->runtime->private_data; - struct atmel_pcm_dma_params *params = prtd->params; - static int count; - - count++; - - if (ssc_sr & params->mask->ssc_endbuf) { - pr_warning("atmel-pcm: buffer %s on %s" - " (SSC_SR=%#x, count=%d)\n", - substream->stream == SNDRV_PCM_STREAM_PLAYBACK - ? "underrun" : "overrun", - params->name, ssc_sr, count); - - /* re-start the PDC */ - ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, - params->mask->pdc_disable); - prtd->period_ptr += prtd->period_size; - if (prtd->period_ptr >= prtd->dma_buffer_end) - prtd->period_ptr = prtd->dma_buffer; - - 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); - ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, - params->mask->pdc_enable); - } - - if (ssc_sr & params->mask->ssc_endx) { - /* Load the PDC next pointer and counter registers */ - prtd->period_ptr += prtd->period_size; - if (prtd->period_ptr >= prtd->dma_buffer_end) - prtd->period_ptr = prtd->dma_buffer; - - 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); - } - - snd_pcm_period_elapsed(substream); -} - - -/*--------------------------------------------------------------------------*\ - * PCM operations -\*--------------------------------------------------------------------------*/ -static int atmel_pcm_hw_params(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 snd_soc_pcm_runtime *rtd = substream->private_data; - - /* 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_dma_irq; - - prtd->dma_buffer = runtime->dma_addr; - prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes; - prtd->period_size = params_period_bytes(params); - - pr_debug("atmel-pcm: " - "hw_params: DMA for %s initialized " - "(dma_bytes=%u, period_size=%u)\n", - 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; - - if (params != NULL) { - ssc_writex(params->ssc->regs, SSC_PDC_PTCR, - params->mask->pdc_disable); - prtd->params->dma_intr_handler = NULL; - } - - return 0; -} - -static int atmel_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct atmel_runtime_data *prtd = substream->runtime->private_data; - struct atmel_pcm_dma_params *params = prtd->params; - - ssc_writex(params->ssc->regs, SSC_IDR, - params->mask->ssc_endx | params->mask->ssc_endbuf); - ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, - params->mask->pdc_disable); - return 0; -} - -static int atmel_pcm_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - struct snd_pcm_runtime *rtd = substream->runtime; - struct atmel_runtime_data *prtd = rtd->private_data; - struct atmel_pcm_dma_params *params = prtd->params; - int ret = 0; - - pr_debug("atmel-pcm:buffer_size = %ld," - "dma_area = %p, dma_bytes = %u\n", - rtd->buffer_size, rtd->dma_area, rtd->dma_bytes); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - prtd->period_ptr = prtd->dma_buffer; - - 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_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); - - pr_debug("atmel-pcm: trigger: " - "period_ptr=%lx, xpr=%u, " - "xcr=%u, xnpr=%u, xncr=%u\n", - (unsigned long)prtd->period_ptr, - ssc_readx(params->ssc->regs, params->pdc->xpr), - ssc_readx(params->ssc->regs, params->pdc->xcr), - ssc_readx(params->ssc->regs, params->pdc->xnpr), - ssc_readx(params->ssc->regs, params->pdc->xncr)); - - ssc_writex(params->ssc->regs, SSC_IER, - params->mask->ssc_endx | params->mask->ssc_endbuf); - ssc_writex(params->ssc->regs, SSC_PDC_PTCR, - params->mask->pdc_enable); - - pr_debug("sr=%u imr=%u\n", - ssc_readx(params->ssc->regs, SSC_SR), - ssc_readx(params->ssc->regs, SSC_IER)); - break; /* SNDRV_PCM_TRIGGER_START */ - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, - params->mask->pdc_disable); - break; - - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, - params->mask->pdc_enable); - break; - - default: - ret = -EINVAL; - } - return ret; -} - -static snd_pcm_uframes_t atmel_pcm_pointer( - struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct atmel_runtime_data *prtd = runtime->private_data; - struct atmel_pcm_dma_params *params = prtd->params; - dma_addr_t ptr; - snd_pcm_uframes_t x; - - ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr); - x = bytes_to_frames(runtime, ptr - prtd->dma_buffer); - - if (x == runtime->buffer_size) - x = 0; - - return x; -} - -static int atmel_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct atmel_runtime_data *prtd; - int ret = 0; - - 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, - SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) - goto out; - - prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL); - if (prtd == NULL) { - ret = -ENOMEM; - goto out; - } - runtime->private_data = prtd; - - out: - return ret; -} - -static int atmel_pcm_close(struct snd_pcm_substream *substream) -{ - struct atmel_runtime_data *prtd = substream->runtime->private_data; - - kfree(prtd); - return 0; -} - -static int atmel_pcm_mmap(struct snd_pcm_substream *substream, +int atmel_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { return remap_pfn_range(vma, vma->vm_start, substream->dma_buffer.addr >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot); } +EXPORT_SYMBOL_GPL(atmel_pcm_mmap); -static struct snd_pcm_ops atmel_pcm_ops = { - .open = atmel_pcm_open, - .close = atmel_pcm_close, - .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, - .mmap = atmel_pcm_mmap, -}; - - -/*--------------------------------------------------------------------------*\ - * ASoC platform driver -\*--------------------------------------------------------------------------*/ static u64 atmel_pcm_dmamask = DMA_BIT_MASK(32); -static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) +int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; @@ -376,6 +82,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) card->dev->coherent_dma_mask = DMA_BIT_MASK(32); if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n"); ret = atmel_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) @@ -383,8 +90,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) } if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - pr_debug("atmel-pcm:" - "Allocating PCM capture DMA buffer\n"); + pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n"); ret = atmel_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) @@ -393,8 +99,9 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) out: return ret; } +EXPORT_SYMBOL_GPL(atmel_pcm_new); -static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm) +void atmel_pcm_free(struct snd_pcm *pcm) { struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; @@ -413,89 +120,5 @@ static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm) buf->area = NULL; } } +EXPORT_SYMBOL_GPL(atmel_pcm_free); -#ifdef CONFIG_PM -static int atmel_pcm_suspend(struct snd_soc_dai *dai) -{ - struct snd_pcm_runtime *runtime = dai->runtime; - struct atmel_runtime_data *prtd; - struct atmel_pcm_dma_params *params; - - if (!runtime) - 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); - - prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr); - prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr); - prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr); - prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr); - - return 0; -} - -static int atmel_pcm_resume(struct snd_soc_dai *dai) -{ - struct snd_pcm_runtime *runtime = dai->runtime; - struct atmel_runtime_data *prtd; - struct atmel_pcm_dma_params *params; - - if (!runtime) - return 0; - - prtd = runtime->private_data; - params = prtd->params; - - /* restore the PDC registers and enable the PDC */ - ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save); - 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 -#define atmel_pcm_suspend NULL -#define atmel_pcm_resume NULL -#endif - -static struct snd_soc_platform_driver atmel_soc_platform = { - .ops = &atmel_pcm_ops, - .pcm_new = atmel_pcm_new, - .pcm_free = atmel_pcm_free_dma_buffers, - .suspend = atmel_pcm_suspend, - .resume = atmel_pcm_resume, -}; - -static int __devinit atmel_soc_platform_probe(struct platform_device *pdev) -{ - return snd_soc_register_platform(&pdev->dev, &atmel_soc_platform); -} - -static int __devexit atmel_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver atmel_pcm_driver = { - .driver = { - .name = "atmel-pcm-audio", - .owner = THIS_MODULE, - }, - - .probe = atmel_soc_platform_probe, - .remove = __devexit_p(atmel_soc_platform_remove), -}; - -module_platform_driver(atmel_pcm_driver); - -MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); -MODULE_DESCRIPTION("Atmel PCM module"); -MODULE_LICENSE("GPL"); |