| 
									
										
										
										
											2012-11-28 11:46:12 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * atmel-pcm.c  --  ALSA PCM interface for the Atmel atmel SoC. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (C) 2005 SAN People | 
					
						
							|  |  |  |  *  Copyright (C) 2008 Atmel | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Based on at91-pcm. by: | 
					
						
							|  |  |  |  * Frank Mandarino <fmandarino@endrelia.com> | 
					
						
							|  |  |  |  * 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 "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	= ATMEL_SSC_DMABUF_SIZE, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*--------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  |  * 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; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*--------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  |  * 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_warn("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 " | 
					
						
							| 
									
										
										
										
											2012-12-08 14:23:22 +01:00
										 |  |  | 		"(dma_bytes=%zu, period_size=%zu)\n", | 
					
						
							| 
									
										
										
										
											2012-11-28 11:46:12 +08:00
										 |  |  | 		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," | 
					
						
							| 
									
										
										
										
											2012-12-08 14:23:22 +01:00
										 |  |  | 		"dma_area = %p, dma_bytes = %zu\n", | 
					
						
							| 
									
										
										
										
											2012-11-28 11:46:12 +08:00
										 |  |  | 		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 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 | 
					
						
							|  |  |  | \*--------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | #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, | 
					
						
							|  |  |  | 	.suspend	= atmel_pcm_suspend, | 
					
						
							|  |  |  | 	.resume		= atmel_pcm_resume, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int atmel_pcm_pdc_platform_register(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return snd_soc_register_platform(dev, &atmel_soc_platform); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(atmel_pcm_pdc_platform_register); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void atmel_pcm_pdc_platform_unregister(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_soc_unregister_platform(dev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(atmel_pcm_pdc_platform_unregister); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("Atmel PCM module"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); |