ASoC: Add files to manage the pcm interface for all atmel boards.
PATCH 4/7 ASoC: Add files to manage the pcm interface for all atmel boards.
Add files to manage the pcm interface for all atmel boards(merge between sound/soc/at91 sound/soc/at32). As well as the atmel_ssc_dai file, it will use the common atmel_ssc API.
This file is based on at32-pcm. and at91-pcm.c.
---sound/soc/atmel/atmel-pcm.c | 484 +++++++++++++++++++++++++++++++++++++++++++sound/soc/atmel/atmel-pcm.h | 86 ++++++++
2 files changed, 570 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/atmel/atmel-pcm.c
create mode 100644 sound/soc/atmel/atmel-pcm.h
sound/soc/atmel/atmel-pcm.c new file +484 -0
@@ -0,0 +1,484 @@ +/* +* atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC. +* +* Copyright (C) 2005 SAN People +* Copyright (C) 2008 Atmel +* +* Authors: Sedji Gaouaou <[email protected]> +* +* Based on at91-pcm. by: +* Frank Mandarino <[email protected]> +* Copyright 2006 Endrelia Technologies Inc. +* +* Based on pxa2xx-pcm.c by: +* +* Author: Nicolas Pitre +* Created: Nov 30, 2004 +* Copyright: (C) 2004 MontaVista Software, Inc. +* +* 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#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 <mach/hardware.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 */ +int periods;/* period index of period_ptr */ + +/* 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; + + 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); + +if (!buf->area) +return -ENOMEM ; + + 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 =0 ; + + 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 = rtd->dai->cpu_dai->dma_data; + 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, +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); +} + +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 =0xffffffff ; + +static int atmel_pcm_new (struct snd_card *card, +struct snd_soc_dai *dai,struct snd_pcm *pcm) +{ +int ret =0 ; + +if (!card->dev->dma_mask) + card->dev->dma_mask = &atmel_pcm_dmamask; +if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask =0xffffffff ; + +if (dai->playback.channels_min) { + ret =atmel_pcm_preallocate_dma_buffer (pcm, +SNDRV_PCM_STREAM_PLAYBACK ); +if (ret) + goto out; + } + +if (dai->capture.channels_min) { +pr_debug ("at32-pcm:" +"Allocating PCM capture DMA buffer\n" ); + ret =atmel_pcm_preallocate_dma_buffer (pcm, +SNDRV_PCM_STREAM_CAPTURE ); +if (ret) + goto out; + } + out: +return ret; +} + +static void atmel_pcm_free_dma_buffers (struct snd_pcm *pcm) +{ +struct snd_pcm_substream *substream; +struct snd_dma_buffer *buf; +int stream; + +for (stream =0 ; stream <2 ; stream++) { + substream = pcm->streams[stream].substream; +if (!substream) +continue ; + + buf = &substream->dma_buffer; +if (!buf->area) +continue ; +dma_free_coherent (pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area =NULL ; + } +} + +#ifdef CONFIG_PM +static int atmel_pcm_suspend (struct platform_device *pdev, +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 platform_device *pdev, +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 + +struct snd_soc_platform atmel_soc_platform = { + .name ="atmel-audio" , + .pcm_ops = &atmel_pcm_ops, + .pcm_new = atmel_pcm_new, + .pcm_free = atmel_pcm_free_dma_buffers, + .suspend = atmel_pcm_suspend, + .resume = atmel_pcm_resume, +}; +EXPORT_SYMBOL_GPL (atmel_soc_platform); + +MODULE_AUTHOR ("Sedji Gaouaou <[email protected]>" ); +MODULE_DESCRIPTION ("Atmel PCM module" ); +MODULE_LICENSE ("GPL" );
sound/soc/atmel/atmel-pcm.h new file +86 -0
86 lines not on this page · raw message
Signature · 1 line
1.5.3.7
Quoted 1 line
From
37a7a3d9f7c652f1912e448949b8173118144a20Mon Sep 17 00:00:00 2001
From: Sedji Gaouaou <[email protected]>
Date: Fri, 3 Oct 2008 11:39:03 +0200
Subject: [PATCH] Add files to manage the pcm interface for all atmel boards(merge between
sound/soc/at91 sound/soc/at32)
--- sound/soc/atmel/atmel-pcm.c | 484 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/atmel/atmel-pcm.h | 86 ++++++++ 2 files changed, 570 insertions(+), 0 deletions(-) create mode 100644 sound/soc/atmel/atmel-pcm.c create mode 100644 sound/soc/atmel/atmel-pcm.h
sound/soc/atmel/atmel-pcm.c new file +484 -0
484 lines not on this page · raw message
sound/soc/atmel/atmel-pcm.h new file +86 -0
86 lines not on this page · raw message
Signature · 1 line
1.5.3.7
_______________________________________________
Alsa-devel mailing list
[email protected]
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel