18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * atmel-pcm.c  --  ALSA PCM interface for the Atmel atmel SoC.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2005 SAN People
68c2ecf20Sopenharmony_ci *  Copyright (C) 2008 Atmel
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Based on at91-pcm. by:
118c2ecf20Sopenharmony_ci * Frank Mandarino <fmandarino@endrelia.com>
128c2ecf20Sopenharmony_ci * Copyright 2006 Endrelia Technologies Inc.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Based on pxa2xx-pcm.c by:
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Author:	Nicolas Pitre
178c2ecf20Sopenharmony_ci * Created:	Nov 30, 2004
188c2ecf20Sopenharmony_ci * Copyright:	(C) 2004 MontaVista Software, Inc.
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/module.h>
228c2ecf20Sopenharmony_ci#include <linux/init.h>
238c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
268c2ecf20Sopenharmony_ci#include <linux/atmel_pdc.h>
278c2ecf20Sopenharmony_ci#include <linux/atmel-ssc.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <sound/core.h>
308c2ecf20Sopenharmony_ci#include <sound/pcm.h>
318c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
328c2ecf20Sopenharmony_ci#include <sound/soc.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "atmel-pcm.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
388c2ecf20Sopenharmony_ci	int stream)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
418c2ecf20Sopenharmony_ci	struct snd_dma_buffer *buf = &substream->dma_buffer;
428c2ecf20Sopenharmony_ci	size_t size = ATMEL_SSC_DMABUF_SIZE;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	buf->dev.type = SNDRV_DMA_TYPE_DEV;
458c2ecf20Sopenharmony_ci	buf->dev.dev = pcm->card->dev;
468c2ecf20Sopenharmony_ci	buf->private_data = NULL;
478c2ecf20Sopenharmony_ci	buf->area = dma_alloc_coherent(pcm->card->dev, size,
488c2ecf20Sopenharmony_ci			&buf->addr, GFP_KERNEL);
498c2ecf20Sopenharmony_ci	pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n",
508c2ecf20Sopenharmony_ci			(void *)buf->area, (void *)(long)buf->addr, size);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (!buf->area)
538c2ecf20Sopenharmony_ci		return -ENOMEM;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	buf->bytes = size;
568c2ecf20Sopenharmony_ci	return 0;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int atmel_pcm_mmap(struct snd_soc_component *component,
608c2ecf20Sopenharmony_ci			  struct snd_pcm_substream *substream,
618c2ecf20Sopenharmony_ci			  struct vm_area_struct *vma)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	return remap_pfn_range(vma, vma->vm_start,
648c2ecf20Sopenharmony_ci		       substream->dma_buffer.addr >> PAGE_SHIFT,
658c2ecf20Sopenharmony_ci		       vma->vm_end - vma->vm_start, vma->vm_page_prot);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int atmel_pcm_new(struct snd_soc_component *component,
698c2ecf20Sopenharmony_ci			 struct snd_soc_pcm_runtime *rtd)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct snd_card *card = rtd->card->snd_card;
728c2ecf20Sopenharmony_ci	struct snd_pcm *pcm = rtd->pcm;
738c2ecf20Sopenharmony_ci	int ret;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
768c2ecf20Sopenharmony_ci	if (ret)
778c2ecf20Sopenharmony_ci		return ret;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
808c2ecf20Sopenharmony_ci		pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n");
818c2ecf20Sopenharmony_ci		ret = atmel_pcm_preallocate_dma_buffer(pcm,
828c2ecf20Sopenharmony_ci			SNDRV_PCM_STREAM_PLAYBACK);
838c2ecf20Sopenharmony_ci		if (ret)
848c2ecf20Sopenharmony_ci			goto out;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
888c2ecf20Sopenharmony_ci		pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n");
898c2ecf20Sopenharmony_ci		ret = atmel_pcm_preallocate_dma_buffer(pcm,
908c2ecf20Sopenharmony_ci			SNDRV_PCM_STREAM_CAPTURE);
918c2ecf20Sopenharmony_ci		if (ret)
928c2ecf20Sopenharmony_ci			goto out;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci out:
958c2ecf20Sopenharmony_ci	return ret;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void atmel_pcm_free(struct snd_soc_component *component,
998c2ecf20Sopenharmony_ci			   struct snd_pcm *pcm)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
1028c2ecf20Sopenharmony_ci	struct snd_dma_buffer *buf;
1038c2ecf20Sopenharmony_ci	int stream;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	for (stream = 0; stream < 2; stream++) {
1068c2ecf20Sopenharmony_ci		substream = pcm->streams[stream].substream;
1078c2ecf20Sopenharmony_ci		if (!substream)
1088c2ecf20Sopenharmony_ci			continue;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		buf = &substream->dma_buffer;
1118c2ecf20Sopenharmony_ci		if (!buf->area)
1128c2ecf20Sopenharmony_ci			continue;
1138c2ecf20Sopenharmony_ci		dma_free_coherent(pcm->card->dev, buf->bytes,
1148c2ecf20Sopenharmony_ci				  buf->area, buf->addr);
1158c2ecf20Sopenharmony_ci		buf->area = NULL;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/*--------------------------------------------------------------------------*\
1208c2ecf20Sopenharmony_ci * Hardware definition
1218c2ecf20Sopenharmony_ci\*--------------------------------------------------------------------------*/
1228c2ecf20Sopenharmony_ci/* TODO: These values were taken from the AT91 platform driver, check
1238c2ecf20Sopenharmony_ci *	 them against real values for AT32
1248c2ecf20Sopenharmony_ci */
1258c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware atmel_pcm_hardware = {
1268c2ecf20Sopenharmony_ci	.info			= SNDRV_PCM_INFO_MMAP |
1278c2ecf20Sopenharmony_ci				  SNDRV_PCM_INFO_MMAP_VALID |
1288c2ecf20Sopenharmony_ci				  SNDRV_PCM_INFO_INTERLEAVED |
1298c2ecf20Sopenharmony_ci				  SNDRV_PCM_INFO_PAUSE,
1308c2ecf20Sopenharmony_ci	.period_bytes_min	= 32,
1318c2ecf20Sopenharmony_ci	.period_bytes_max	= 8192,
1328c2ecf20Sopenharmony_ci	.periods_min		= 2,
1338c2ecf20Sopenharmony_ci	.periods_max		= 1024,
1348c2ecf20Sopenharmony_ci	.buffer_bytes_max	= ATMEL_SSC_DMABUF_SIZE,
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/*--------------------------------------------------------------------------*\
1398c2ecf20Sopenharmony_ci * Data types
1408c2ecf20Sopenharmony_ci\*--------------------------------------------------------------------------*/
1418c2ecf20Sopenharmony_cistruct atmel_runtime_data {
1428c2ecf20Sopenharmony_ci	struct atmel_pcm_dma_params *params;
1438c2ecf20Sopenharmony_ci	dma_addr_t dma_buffer;		/* physical address of dma buffer */
1448c2ecf20Sopenharmony_ci	dma_addr_t dma_buffer_end;	/* first address beyond DMA buffer */
1458c2ecf20Sopenharmony_ci	size_t period_size;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	dma_addr_t period_ptr;		/* physical address of next period */
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/*--------------------------------------------------------------------------*\
1518c2ecf20Sopenharmony_ci * ISR
1528c2ecf20Sopenharmony_ci\*--------------------------------------------------------------------------*/
1538c2ecf20Sopenharmony_cistatic void atmel_pcm_dma_irq(u32 ssc_sr,
1548c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct atmel_runtime_data *prtd = substream->runtime->private_data;
1578c2ecf20Sopenharmony_ci	struct atmel_pcm_dma_params *params = prtd->params;
1588c2ecf20Sopenharmony_ci	static int count;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	count++;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (ssc_sr & params->mask->ssc_endbuf) {
1638c2ecf20Sopenharmony_ci		pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
1648c2ecf20Sopenharmony_ci				substream->stream == SNDRV_PCM_STREAM_PLAYBACK
1658c2ecf20Sopenharmony_ci				? "underrun" : "overrun",
1668c2ecf20Sopenharmony_ci				params->name, ssc_sr, count);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		/* re-start the PDC */
1698c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
1708c2ecf20Sopenharmony_ci			   params->mask->pdc_disable);
1718c2ecf20Sopenharmony_ci		prtd->period_ptr += prtd->period_size;
1728c2ecf20Sopenharmony_ci		if (prtd->period_ptr >= prtd->dma_buffer_end)
1738c2ecf20Sopenharmony_ci			prtd->period_ptr = prtd->dma_buffer;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, params->pdc->xpr,
1768c2ecf20Sopenharmony_ci			   prtd->period_ptr);
1778c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, params->pdc->xcr,
1788c2ecf20Sopenharmony_ci			   prtd->period_size / params->pdc_xfer_size);
1798c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
1808c2ecf20Sopenharmony_ci			   params->mask->pdc_enable);
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (ssc_sr & params->mask->ssc_endx) {
1848c2ecf20Sopenharmony_ci		/* Load the PDC next pointer and counter registers */
1858c2ecf20Sopenharmony_ci		prtd->period_ptr += prtd->period_size;
1868c2ecf20Sopenharmony_ci		if (prtd->period_ptr >= prtd->dma_buffer_end)
1878c2ecf20Sopenharmony_ci			prtd->period_ptr = prtd->dma_buffer;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, params->pdc->xnpr,
1908c2ecf20Sopenharmony_ci			   prtd->period_ptr);
1918c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, params->pdc->xncr,
1928c2ecf20Sopenharmony_ci			   prtd->period_size / params->pdc_xfer_size);
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	snd_pcm_period_elapsed(substream);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/*--------------------------------------------------------------------------*\
2008c2ecf20Sopenharmony_ci * PCM operations
2018c2ecf20Sopenharmony_ci\*--------------------------------------------------------------------------*/
2028c2ecf20Sopenharmony_cistatic int atmel_pcm_hw_params(struct snd_soc_component *component,
2038c2ecf20Sopenharmony_ci			       struct snd_pcm_substream *substream,
2048c2ecf20Sopenharmony_ci			       struct snd_pcm_hw_params *params)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2078c2ecf20Sopenharmony_ci	struct atmel_runtime_data *prtd = runtime->private_data;
2088c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* this may get called several times by oss emulation
2118c2ecf20Sopenharmony_ci	 * with different params */
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
2148c2ecf20Sopenharmony_ci	runtime->dma_bytes = params_buffer_bytes(params);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	prtd->params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
2178c2ecf20Sopenharmony_ci	prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	prtd->dma_buffer = runtime->dma_addr;
2208c2ecf20Sopenharmony_ci	prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
2218c2ecf20Sopenharmony_ci	prtd->period_size = params_period_bytes(params);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	pr_debug("atmel-pcm: "
2248c2ecf20Sopenharmony_ci		"hw_params: DMA for %s initialized "
2258c2ecf20Sopenharmony_ci		"(dma_bytes=%zu, period_size=%zu)\n",
2268c2ecf20Sopenharmony_ci		prtd->params->name,
2278c2ecf20Sopenharmony_ci		runtime->dma_bytes,
2288c2ecf20Sopenharmony_ci		prtd->period_size);
2298c2ecf20Sopenharmony_ci	return 0;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic int atmel_pcm_hw_free(struct snd_soc_component *component,
2338c2ecf20Sopenharmony_ci			     struct snd_pcm_substream *substream)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct atmel_runtime_data *prtd = substream->runtime->private_data;
2368c2ecf20Sopenharmony_ci	struct atmel_pcm_dma_params *params = prtd->params;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (params != NULL) {
2398c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
2408c2ecf20Sopenharmony_ci			   params->mask->pdc_disable);
2418c2ecf20Sopenharmony_ci		prtd->params->dma_intr_handler = NULL;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	return 0;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int atmel_pcm_prepare(struct snd_soc_component *component,
2488c2ecf20Sopenharmony_ci			     struct snd_pcm_substream *substream)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct atmel_runtime_data *prtd = substream->runtime->private_data;
2518c2ecf20Sopenharmony_ci	struct atmel_pcm_dma_params *params = prtd->params;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	ssc_writex(params->ssc->regs, SSC_IDR,
2548c2ecf20Sopenharmony_ci		   params->mask->ssc_endx | params->mask->ssc_endbuf);
2558c2ecf20Sopenharmony_ci	ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
2568c2ecf20Sopenharmony_ci		   params->mask->pdc_disable);
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int atmel_pcm_trigger(struct snd_soc_component *component,
2618c2ecf20Sopenharmony_ci			     struct snd_pcm_substream *substream, int cmd)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *rtd = substream->runtime;
2648c2ecf20Sopenharmony_ci	struct atmel_runtime_data *prtd = rtd->private_data;
2658c2ecf20Sopenharmony_ci	struct atmel_pcm_dma_params *params = prtd->params;
2668c2ecf20Sopenharmony_ci	int ret = 0;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	pr_debug("atmel-pcm:buffer_size = %ld,"
2698c2ecf20Sopenharmony_ci		"dma_area = %p, dma_bytes = %zu\n",
2708c2ecf20Sopenharmony_ci		rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	switch (cmd) {
2738c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2748c2ecf20Sopenharmony_ci		prtd->period_ptr = prtd->dma_buffer;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, params->pdc->xpr,
2778c2ecf20Sopenharmony_ci			   prtd->period_ptr);
2788c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, params->pdc->xcr,
2798c2ecf20Sopenharmony_ci			   prtd->period_size / params->pdc_xfer_size);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		prtd->period_ptr += prtd->period_size;
2828c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, params->pdc->xnpr,
2838c2ecf20Sopenharmony_ci			   prtd->period_ptr);
2848c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, params->pdc->xncr,
2858c2ecf20Sopenharmony_ci			   prtd->period_size / params->pdc_xfer_size);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		pr_debug("atmel-pcm: trigger: "
2888c2ecf20Sopenharmony_ci			"period_ptr=%lx, xpr=%u, "
2898c2ecf20Sopenharmony_ci			"xcr=%u, xnpr=%u, xncr=%u\n",
2908c2ecf20Sopenharmony_ci			(unsigned long)prtd->period_ptr,
2918c2ecf20Sopenharmony_ci			ssc_readx(params->ssc->regs, params->pdc->xpr),
2928c2ecf20Sopenharmony_ci			ssc_readx(params->ssc->regs, params->pdc->xcr),
2938c2ecf20Sopenharmony_ci			ssc_readx(params->ssc->regs, params->pdc->xnpr),
2948c2ecf20Sopenharmony_ci			ssc_readx(params->ssc->regs, params->pdc->xncr));
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, SSC_IER,
2978c2ecf20Sopenharmony_ci			   params->mask->ssc_endx | params->mask->ssc_endbuf);
2988c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
2998c2ecf20Sopenharmony_ci			   params->mask->pdc_enable);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		pr_debug("sr=%u imr=%u\n",
3028c2ecf20Sopenharmony_ci			ssc_readx(params->ssc->regs, SSC_SR),
3038c2ecf20Sopenharmony_ci			ssc_readx(params->ssc->regs, SSC_IER));
3048c2ecf20Sopenharmony_ci		break;		/* SNDRV_PCM_TRIGGER_START */
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
3078c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
3088c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3098c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
3108c2ecf20Sopenharmony_ci			   params->mask->pdc_disable);
3118c2ecf20Sopenharmony_ci		break;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
3148c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3158c2ecf20Sopenharmony_ci		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
3168c2ecf20Sopenharmony_ci			   params->mask->pdc_enable);
3178c2ecf20Sopenharmony_ci		break;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	default:
3208c2ecf20Sopenharmony_ci		ret = -EINVAL;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	return ret;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t atmel_pcm_pointer(struct snd_soc_component *component,
3278c2ecf20Sopenharmony_ci					   struct snd_pcm_substream *substream)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
3308c2ecf20Sopenharmony_ci	struct atmel_runtime_data *prtd = runtime->private_data;
3318c2ecf20Sopenharmony_ci	struct atmel_pcm_dma_params *params = prtd->params;
3328c2ecf20Sopenharmony_ci	dma_addr_t ptr;
3338c2ecf20Sopenharmony_ci	snd_pcm_uframes_t x;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
3368c2ecf20Sopenharmony_ci	x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (x == runtime->buffer_size)
3398c2ecf20Sopenharmony_ci		x = 0;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return x;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int atmel_pcm_open(struct snd_soc_component *component,
3458c2ecf20Sopenharmony_ci			  struct snd_pcm_substream *substream)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
3488c2ecf20Sopenharmony_ci	struct atmel_runtime_data *prtd;
3498c2ecf20Sopenharmony_ci	int ret = 0;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* ensure that buffer size is a multiple of period size */
3548c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_constraint_integer(runtime,
3558c2ecf20Sopenharmony_ci						SNDRV_PCM_HW_PARAM_PERIODS);
3568c2ecf20Sopenharmony_ci	if (ret < 0)
3578c2ecf20Sopenharmony_ci		goto out;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
3608c2ecf20Sopenharmony_ci	if (prtd == NULL) {
3618c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3628c2ecf20Sopenharmony_ci		goto out;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci	runtime->private_data = prtd;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci out:
3678c2ecf20Sopenharmony_ci	return ret;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic int atmel_pcm_close(struct snd_soc_component *component,
3718c2ecf20Sopenharmony_ci			   struct snd_pcm_substream *substream)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct atmel_runtime_data *prtd = substream->runtime->private_data;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	kfree(prtd);
3768c2ecf20Sopenharmony_ci	return 0;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver atmel_soc_platform = {
3808c2ecf20Sopenharmony_ci	.open		= atmel_pcm_open,
3818c2ecf20Sopenharmony_ci	.close		= atmel_pcm_close,
3828c2ecf20Sopenharmony_ci	.hw_params	= atmel_pcm_hw_params,
3838c2ecf20Sopenharmony_ci	.hw_free	= atmel_pcm_hw_free,
3848c2ecf20Sopenharmony_ci	.prepare	= atmel_pcm_prepare,
3858c2ecf20Sopenharmony_ci	.trigger	= atmel_pcm_trigger,
3868c2ecf20Sopenharmony_ci	.pointer	= atmel_pcm_pointer,
3878c2ecf20Sopenharmony_ci	.mmap		= atmel_pcm_mmap,
3888c2ecf20Sopenharmony_ci	.pcm_construct	= atmel_pcm_new,
3898c2ecf20Sopenharmony_ci	.pcm_destruct	= atmel_pcm_free,
3908c2ecf20Sopenharmony_ci};
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ciint atmel_pcm_pdc_platform_register(struct device *dev)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	return devm_snd_soc_register_component(dev, &atmel_soc_platform,
3958c2ecf20Sopenharmony_ci					       NULL, 0);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
4008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Atmel PCM module");
4018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
402