brintos

brintos / linux-shallow public Read only

0
0
Text · 9.5 KiB · 7db8df8 Raw
332 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * atmel-pcm.c  --  ALSA PCM interface for the Atmel atmel SoC.4 *5 *  Copyright (C) 2005 SAN People6 *  Copyright (C) 2008 Atmel7 *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 Pitre17 * Created:	Nov 30, 200418 * 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 definition56\*--------------------------------------------------------------------------*/57/* TODO: These values were taken from the AT91 platform driver, check58 *	 them against real values for AT3259 */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 types75\*--------------------------------------------------------------------------*/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 * ISR87\*--------------------------------------------------------------------------*/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_PLAYBACK100				? "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 operations136\*--------------------------------------------------------------------------*/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 = snd_soc_substream_to_rtd(substream);144 145	/* this may get called several times by oss emulation146	 * with different params */147 148	prtd->params = snd_soc_dai_get_dma_data(snd_soc_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");332