cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

atmel-pcm-pdc.c (9723B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * atmel-pcm.c  --  ALSA PCM interface for the Atmel atmel SoC.
      4 *
      5 *  Copyright (C) 2005 SAN People
      6 *  Copyright (C) 2008 Atmel
      7 *
      8 * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
      9 *
     10 * Based on at91-pcm. by:
     11 * Frank Mandarino <fmandarino@endrelia.com>
     12 * Copyright 2006 Endrelia Technologies Inc.
     13 *
     14 * Based on pxa2xx-pcm.c by:
     15 *
     16 * Author:	Nicolas Pitre
     17 * Created:	Nov 30, 2004
     18 * Copyright:	(C) 2004 MontaVista Software, Inc.
     19 */
     20
     21#include <linux/module.h>
     22#include <linux/init.h>
     23#include <linux/platform_device.h>
     24#include <linux/slab.h>
     25#include <linux/dma-mapping.h>
     26#include <linux/atmel_pdc.h>
     27#include <linux/atmel-ssc.h>
     28
     29#include <sound/core.h>
     30#include <sound/pcm.h>
     31#include <sound/pcm_params.h>
     32#include <sound/soc.h>
     33
     34#include "atmel-pcm.h"
     35
     36
     37static int atmel_pcm_new(struct snd_soc_component *component,
     38			 struct snd_soc_pcm_runtime *rtd)
     39{
     40	struct snd_card *card = rtd->card->snd_card;
     41	int ret;
     42
     43	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
     44	if (ret)
     45		return ret;
     46
     47	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
     48				       card->dev, ATMEL_SSC_DMABUF_SIZE,
     49				       ATMEL_SSC_DMABUF_SIZE);
     50
     51	return 0;
     52}
     53
     54/*--------------------------------------------------------------------------*\
     55 * Hardware definition
     56\*--------------------------------------------------------------------------*/
     57/* TODO: These values were taken from the AT91 platform driver, check
     58 *	 them against real values for AT32
     59 */
     60static const struct snd_pcm_hardware atmel_pcm_hardware = {
     61	.info			= SNDRV_PCM_INFO_MMAP |
     62				  SNDRV_PCM_INFO_MMAP_VALID |
     63				  SNDRV_PCM_INFO_INTERLEAVED |
     64				  SNDRV_PCM_INFO_PAUSE,
     65	.period_bytes_min	= 32,
     66	.period_bytes_max	= 8192,
     67	.periods_min		= 2,
     68	.periods_max		= 1024,
     69	.buffer_bytes_max	= ATMEL_SSC_DMABUF_SIZE,
     70};
     71
     72
     73/*--------------------------------------------------------------------------*\
     74 * Data types
     75\*--------------------------------------------------------------------------*/
     76struct atmel_runtime_data {
     77	struct atmel_pcm_dma_params *params;
     78	dma_addr_t dma_buffer;		/* physical address of dma buffer */
     79	dma_addr_t dma_buffer_end;	/* first address beyond DMA buffer */
     80	size_t period_size;
     81
     82	dma_addr_t period_ptr;		/* physical address of next period */
     83};
     84
     85/*--------------------------------------------------------------------------*\
     86 * ISR
     87\*--------------------------------------------------------------------------*/
     88static void atmel_pcm_dma_irq(u32 ssc_sr,
     89	struct snd_pcm_substream *substream)
     90{
     91	struct atmel_runtime_data *prtd = substream->runtime->private_data;
     92	struct atmel_pcm_dma_params *params = prtd->params;
     93	static int count;
     94
     95	count++;
     96
     97	if (ssc_sr & params->mask->ssc_endbuf) {
     98		pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
     99				substream->stream == SNDRV_PCM_STREAM_PLAYBACK
    100				? "underrun" : "overrun",
    101				params->name, ssc_sr, count);
    102
    103		/* re-start the PDC */
    104		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
    105			   params->mask->pdc_disable);
    106		prtd->period_ptr += prtd->period_size;
    107		if (prtd->period_ptr >= prtd->dma_buffer_end)
    108			prtd->period_ptr = prtd->dma_buffer;
    109
    110		ssc_writex(params->ssc->regs, params->pdc->xpr,
    111			   prtd->period_ptr);
    112		ssc_writex(params->ssc->regs, params->pdc->xcr,
    113			   prtd->period_size / params->pdc_xfer_size);
    114		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
    115			   params->mask->pdc_enable);
    116	}
    117
    118	if (ssc_sr & params->mask->ssc_endx) {
    119		/* Load the PDC next pointer and counter registers */
    120		prtd->period_ptr += prtd->period_size;
    121		if (prtd->period_ptr >= prtd->dma_buffer_end)
    122			prtd->period_ptr = prtd->dma_buffer;
    123
    124		ssc_writex(params->ssc->regs, params->pdc->xnpr,
    125			   prtd->period_ptr);
    126		ssc_writex(params->ssc->regs, params->pdc->xncr,
    127			   prtd->period_size / params->pdc_xfer_size);
    128	}
    129
    130	snd_pcm_period_elapsed(substream);
    131}
    132
    133
    134/*--------------------------------------------------------------------------*\
    135 * PCM operations
    136\*--------------------------------------------------------------------------*/
    137static int atmel_pcm_hw_params(struct snd_soc_component *component,
    138			       struct snd_pcm_substream *substream,
    139			       struct snd_pcm_hw_params *params)
    140{
    141	struct snd_pcm_runtime *runtime = substream->runtime;
    142	struct atmel_runtime_data *prtd = runtime->private_data;
    143	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    144
    145	/* this may get called several times by oss emulation
    146	 * with different params */
    147
    148	prtd->params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
    149	prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
    150
    151	prtd->dma_buffer = runtime->dma_addr;
    152	prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
    153	prtd->period_size = params_period_bytes(params);
    154
    155	pr_debug("atmel-pcm: "
    156		"hw_params: DMA for %s initialized "
    157		"(dma_bytes=%zu, period_size=%zu)\n",
    158		prtd->params->name,
    159		runtime->dma_bytes,
    160		prtd->period_size);
    161	return 0;
    162}
    163
    164static int atmel_pcm_hw_free(struct snd_soc_component *component,
    165			     struct snd_pcm_substream *substream)
    166{
    167	struct atmel_runtime_data *prtd = substream->runtime->private_data;
    168	struct atmel_pcm_dma_params *params = prtd->params;
    169
    170	if (params != NULL) {
    171		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
    172			   params->mask->pdc_disable);
    173		prtd->params->dma_intr_handler = NULL;
    174	}
    175
    176	return 0;
    177}
    178
    179static int atmel_pcm_prepare(struct snd_soc_component *component,
    180			     struct snd_pcm_substream *substream)
    181{
    182	struct atmel_runtime_data *prtd = substream->runtime->private_data;
    183	struct atmel_pcm_dma_params *params = prtd->params;
    184
    185	ssc_writex(params->ssc->regs, SSC_IDR,
    186		   params->mask->ssc_endx | params->mask->ssc_endbuf);
    187	ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
    188		   params->mask->pdc_disable);
    189	return 0;
    190}
    191
    192static int atmel_pcm_trigger(struct snd_soc_component *component,
    193			     struct snd_pcm_substream *substream, int cmd)
    194{
    195	struct snd_pcm_runtime *rtd = substream->runtime;
    196	struct atmel_runtime_data *prtd = rtd->private_data;
    197	struct atmel_pcm_dma_params *params = prtd->params;
    198	int ret = 0;
    199
    200	pr_debug("atmel-pcm:buffer_size = %ld,"
    201		"dma_area = %p, dma_bytes = %zu\n",
    202		rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
    203
    204	switch (cmd) {
    205	case SNDRV_PCM_TRIGGER_START:
    206		prtd->period_ptr = prtd->dma_buffer;
    207
    208		ssc_writex(params->ssc->regs, params->pdc->xpr,
    209			   prtd->period_ptr);
    210		ssc_writex(params->ssc->regs, params->pdc->xcr,
    211			   prtd->period_size / params->pdc_xfer_size);
    212
    213		prtd->period_ptr += prtd->period_size;
    214		ssc_writex(params->ssc->regs, params->pdc->xnpr,
    215			   prtd->period_ptr);
    216		ssc_writex(params->ssc->regs, params->pdc->xncr,
    217			   prtd->period_size / params->pdc_xfer_size);
    218
    219		pr_debug("atmel-pcm: trigger: "
    220			"period_ptr=%lx, xpr=%u, "
    221			"xcr=%u, xnpr=%u, xncr=%u\n",
    222			(unsigned long)prtd->period_ptr,
    223			ssc_readx(params->ssc->regs, params->pdc->xpr),
    224			ssc_readx(params->ssc->regs, params->pdc->xcr),
    225			ssc_readx(params->ssc->regs, params->pdc->xnpr),
    226			ssc_readx(params->ssc->regs, params->pdc->xncr));
    227
    228		ssc_writex(params->ssc->regs, SSC_IER,
    229			   params->mask->ssc_endx | params->mask->ssc_endbuf);
    230		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
    231			   params->mask->pdc_enable);
    232
    233		pr_debug("sr=%u imr=%u\n",
    234			ssc_readx(params->ssc->regs, SSC_SR),
    235			ssc_readx(params->ssc->regs, SSC_IER));
    236		break;		/* SNDRV_PCM_TRIGGER_START */
    237
    238	case SNDRV_PCM_TRIGGER_STOP:
    239	case SNDRV_PCM_TRIGGER_SUSPEND:
    240	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    241		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
    242			   params->mask->pdc_disable);
    243		break;
    244
    245	case SNDRV_PCM_TRIGGER_RESUME:
    246	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    247		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
    248			   params->mask->pdc_enable);
    249		break;
    250
    251	default:
    252		ret = -EINVAL;
    253	}
    254
    255	return ret;
    256}
    257
    258static snd_pcm_uframes_t atmel_pcm_pointer(struct snd_soc_component *component,
    259					   struct snd_pcm_substream *substream)
    260{
    261	struct snd_pcm_runtime *runtime = substream->runtime;
    262	struct atmel_runtime_data *prtd = runtime->private_data;
    263	struct atmel_pcm_dma_params *params = prtd->params;
    264	dma_addr_t ptr;
    265	snd_pcm_uframes_t x;
    266
    267	ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
    268	x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
    269
    270	if (x == runtime->buffer_size)
    271		x = 0;
    272
    273	return x;
    274}
    275
    276static int atmel_pcm_open(struct snd_soc_component *component,
    277			  struct snd_pcm_substream *substream)
    278{
    279	struct snd_pcm_runtime *runtime = substream->runtime;
    280	struct atmel_runtime_data *prtd;
    281	int ret = 0;
    282
    283	snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
    284
    285	/* ensure that buffer size is a multiple of period size */
    286	ret = snd_pcm_hw_constraint_integer(runtime,
    287						SNDRV_PCM_HW_PARAM_PERIODS);
    288	if (ret < 0)
    289		goto out;
    290
    291	prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
    292	if (prtd == NULL) {
    293		ret = -ENOMEM;
    294		goto out;
    295	}
    296	runtime->private_data = prtd;
    297
    298 out:
    299	return ret;
    300}
    301
    302static int atmel_pcm_close(struct snd_soc_component *component,
    303			   struct snd_pcm_substream *substream)
    304{
    305	struct atmel_runtime_data *prtd = substream->runtime->private_data;
    306
    307	kfree(prtd);
    308	return 0;
    309}
    310
    311static const struct snd_soc_component_driver atmel_soc_platform = {
    312	.open		= atmel_pcm_open,
    313	.close		= atmel_pcm_close,
    314	.hw_params	= atmel_pcm_hw_params,
    315	.hw_free	= atmel_pcm_hw_free,
    316	.prepare	= atmel_pcm_prepare,
    317	.trigger	= atmel_pcm_trigger,
    318	.pointer	= atmel_pcm_pointer,
    319	.pcm_construct	= atmel_pcm_new,
    320};
    321
    322int atmel_pcm_pdc_platform_register(struct device *dev)
    323{
    324	return devm_snd_soc_register_component(dev, &atmel_soc_platform,
    325					       NULL, 0);
    326}
    327EXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
    328
    329MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
    330MODULE_DESCRIPTION("Atmel PCM module");
    331MODULE_LICENSE("GPL");