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 9b84f985770..e99f1811300 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");  |