162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * atmel-pcm-dma.c -- ALSA PCM DMA support for the Atmel SoC. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Atmel 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Bo Shen <voice.shen@atmel.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Based on atmel-pcm by: 1062306a36Sopenharmony_ci * Sedji Gaouaou <sedji.gaouaou@atmel.com> 1162306a36Sopenharmony_ci * Copyright 2008 Atmel 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1962306a36Sopenharmony_ci#include <linux/dmaengine.h> 2062306a36Sopenharmony_ci#include <linux/atmel-ssc.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <sound/core.h> 2362306a36Sopenharmony_ci#include <sound/pcm.h> 2462306a36Sopenharmony_ci#include <sound/pcm_params.h> 2562306a36Sopenharmony_ci#include <sound/soc.h> 2662306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "atmel-pcm.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/*--------------------------------------------------------------------------*\ 3162306a36Sopenharmony_ci * Hardware definition 3262306a36Sopenharmony_ci\*--------------------------------------------------------------------------*/ 3362306a36Sopenharmony_cistatic const struct snd_pcm_hardware atmel_pcm_dma_hardware = { 3462306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | 3562306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 3662306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 3762306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 3862306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE, 3962306a36Sopenharmony_ci .period_bytes_min = 256, /* lighting DMA overhead */ 4062306a36Sopenharmony_ci .period_bytes_max = 2 * 0xffff, /* if 2 bytes format */ 4162306a36Sopenharmony_ci .periods_min = 8, 4262306a36Sopenharmony_ci .periods_max = 1024, /* no limit */ 4362306a36Sopenharmony_ci .buffer_bytes_max = 512 * 1024, 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * We use DMAENGINE to send/receive data to/from SSC so this ISR is only to 5062306a36Sopenharmony_ci * check if any overrun occured. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistatic void atmel_pcm_dma_irq(u32 ssc_sr, 5362306a36Sopenharmony_ci struct snd_pcm_substream *substream) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 5662306a36Sopenharmony_ci struct atmel_pcm_dma_params *prtd; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci prtd = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (ssc_sr & prtd->mask->ssc_error) { 6162306a36Sopenharmony_ci if (snd_pcm_running(substream)) 6262306a36Sopenharmony_ci pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x)\n", 6362306a36Sopenharmony_ci substream->stream == SNDRV_PCM_STREAM_PLAYBACK 6462306a36Sopenharmony_ci ? "underrun" : "overrun", prtd->name, 6562306a36Sopenharmony_ci ssc_sr); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* stop RX and capture: will be enabled again at restart */ 6862306a36Sopenharmony_ci ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_disable); 6962306a36Sopenharmony_ci snd_pcm_stop_xrun(substream); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* now drain RHR and read status to remove xrun condition */ 7262306a36Sopenharmony_ci ssc_readx(prtd->ssc->regs, SSC_RHR); 7362306a36Sopenharmony_ci ssc_readx(prtd->ssc->regs, SSC_SR); 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int atmel_pcm_configure_dma(struct snd_pcm_substream *substream, 7862306a36Sopenharmony_ci struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 8162306a36Sopenharmony_ci struct atmel_pcm_dma_params *prtd; 8262306a36Sopenharmony_ci struct ssc_device *ssc; 8362306a36Sopenharmony_ci int ret; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci prtd = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 8662306a36Sopenharmony_ci ssc = prtd->ssc; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); 8962306a36Sopenharmony_ci if (ret) { 9062306a36Sopenharmony_ci pr_err("atmel-pcm: hwparams to dma slave configure failed\n"); 9162306a36Sopenharmony_ci return ret; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci slave_config->dst_addr = ssc->phybase + SSC_THR; 9562306a36Sopenharmony_ci slave_config->dst_maxburst = 1; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci slave_config->src_addr = ssc->phybase + SSC_RHR; 9862306a36Sopenharmony_ci slave_config->src_maxburst = 1; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci prtd->dma_intr_handler = atmel_pcm_dma_irq; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = { 10662306a36Sopenharmony_ci .prepare_slave_config = atmel_pcm_configure_dma, 10762306a36Sopenharmony_ci .pcm_hardware = &atmel_pcm_dma_hardware, 10862306a36Sopenharmony_ci .prealloc_buffer_size = 64 * 1024, 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciint atmel_pcm_dma_platform_register(struct device *dev) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci return devm_snd_dmaengine_pcm_register(dev, 11462306a36Sopenharmony_ci &atmel_dmaengine_pcm_config, 0); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ciEXPORT_SYMBOL(atmel_pcm_dma_platform_register); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciMODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>"); 11962306a36Sopenharmony_ciMODULE_DESCRIPTION("Atmel DMA based PCM module"); 12062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 121