brintos

brintos / linux-shallow public Read only

0
0
Text · 7.7 KiB · 3391430 Raw
324 lines · c
1// SPDX-License-Identifier: GPL-2.0+2// imx-pcm-fiq.c  --  ALSA Soc Audio Layer3//4// Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>5//6// This code is based on code copyrighted by Freescale,7// Liam Girdwood, Javier Martin and probably others.8 9#include <linux/clk.h>10#include <linux/delay.h>11#include <linux/device.h>12#include <linux/dma-mapping.h>13#include <linux/init.h>14#include <linux/interrupt.h>15#include <linux/module.h>16#include <linux/platform_device.h>17#include <linux/slab.h>18 19#include <sound/core.h>20#include <sound/dmaengine_pcm.h>21#include <sound/initval.h>22#include <sound/pcm.h>23#include <sound/pcm_params.h>24#include <sound/soc.h>25 26#include <asm/fiq.h>27 28#include <linux/platform_data/asoc-imx-ssi.h>29 30#include "imx-ssi.h"31#include "imx-pcm.h"32 33struct imx_pcm_runtime_data {34	unsigned int period;35	int periods;36	unsigned long offset;37	struct hrtimer hrt;38	int poll_time_ns;39	struct snd_pcm_substream *substream;40	atomic_t playing;41	atomic_t capturing;42};43 44static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)45{46	struct imx_pcm_runtime_data *iprtd =47		container_of(hrt, struct imx_pcm_runtime_data, hrt);48	struct snd_pcm_substream *substream = iprtd->substream;49	struct pt_regs regs;50 51	if (!atomic_read(&iprtd->playing) && !atomic_read(&iprtd->capturing))52		return HRTIMER_NORESTART;53 54	get_fiq_regs(&regs);55 56	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)57		iprtd->offset = regs.ARM_r8 & 0xffff;58	else59		iprtd->offset = regs.ARM_r9 & 0xffff;60 61	snd_pcm_period_elapsed(substream);62 63	hrtimer_forward_now(hrt, ns_to_ktime(iprtd->poll_time_ns));64 65	return HRTIMER_RESTART;66}67 68static struct fiq_handler fh = {69	.name		= DRV_NAME,70};71 72static int snd_imx_pcm_hw_params(struct snd_soc_component *component,73				 struct snd_pcm_substream *substream,74				 struct snd_pcm_hw_params *params)75{76	struct snd_pcm_runtime *runtime = substream->runtime;77	struct imx_pcm_runtime_data *iprtd = runtime->private_data;78 79	iprtd->periods = params_periods(params);80	iprtd->period = params_period_bytes(params);81	iprtd->offset = 0;82	iprtd->poll_time_ns = 1000000000 / params_rate(params) *83				params_period_size(params);84 85	return 0;86}87 88static int snd_imx_pcm_prepare(struct snd_soc_component *component,89			       struct snd_pcm_substream *substream)90{91	struct snd_pcm_runtime *runtime = substream->runtime;92	struct imx_pcm_runtime_data *iprtd = runtime->private_data;93	struct pt_regs regs;94 95	get_fiq_regs(&regs);96	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)97		regs.ARM_r8 = (iprtd->period * iprtd->periods - 1) << 16;98	else99		regs.ARM_r9 = (iprtd->period * iprtd->periods - 1) << 16;100 101	set_fiq_regs(&regs);102 103	return 0;104}105 106static int imx_pcm_fiq;107 108static int snd_imx_pcm_trigger(struct snd_soc_component *component,109			       struct snd_pcm_substream *substream, int cmd)110{111	struct snd_pcm_runtime *runtime = substream->runtime;112	struct imx_pcm_runtime_data *iprtd = runtime->private_data;113 114	switch (cmd) {115	case SNDRV_PCM_TRIGGER_START:116	case SNDRV_PCM_TRIGGER_RESUME:117	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:118		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)119			atomic_set(&iprtd->playing, 1);120		else121			atomic_set(&iprtd->capturing, 1);122		hrtimer_start(&iprtd->hrt, ns_to_ktime(iprtd->poll_time_ns),123		      HRTIMER_MODE_REL);124		enable_fiq(imx_pcm_fiq);125		break;126 127	case SNDRV_PCM_TRIGGER_STOP:128	case SNDRV_PCM_TRIGGER_SUSPEND:129	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:130		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)131			atomic_set(&iprtd->playing, 0);132		else133			atomic_set(&iprtd->capturing, 0);134		if (!atomic_read(&iprtd->playing) &&135				!atomic_read(&iprtd->capturing))136			disable_fiq(imx_pcm_fiq);137		break;138 139	default:140		return -EINVAL;141	}142 143	return 0;144}145 146static snd_pcm_uframes_t147snd_imx_pcm_pointer(struct snd_soc_component *component,148		    struct snd_pcm_substream *substream)149{150	struct snd_pcm_runtime *runtime = substream->runtime;151	struct imx_pcm_runtime_data *iprtd = runtime->private_data;152 153	return bytes_to_frames(substream->runtime, iprtd->offset);154}155 156static const struct snd_pcm_hardware snd_imx_hardware = {157	.info = SNDRV_PCM_INFO_INTERLEAVED |158		SNDRV_PCM_INFO_BLOCK_TRANSFER |159		SNDRV_PCM_INFO_MMAP |160		SNDRV_PCM_INFO_MMAP_VALID |161		SNDRV_PCM_INFO_PAUSE |162		SNDRV_PCM_INFO_RESUME,163	.formats = SNDRV_PCM_FMTBIT_S16_LE,164	.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,165	.period_bytes_min = 128,166	.period_bytes_max = 16 * 1024,167	.periods_min = 4,168	.periods_max = 255,169	.fifo_size = 0,170};171 172static int snd_imx_open(struct snd_soc_component *component,173			struct snd_pcm_substream *substream)174{175	struct snd_pcm_runtime *runtime = substream->runtime;176	struct imx_pcm_runtime_data *iprtd;177	int ret;178 179	iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);180	if (iprtd == NULL)181		return -ENOMEM;182	runtime->private_data = iprtd;183 184	iprtd->substream = substream;185 186	atomic_set(&iprtd->playing, 0);187	atomic_set(&iprtd->capturing, 0);188	hrtimer_init(&iprtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);189	iprtd->hrt.function = snd_hrtimer_callback;190 191	ret = snd_pcm_hw_constraint_integer(substream->runtime,192			SNDRV_PCM_HW_PARAM_PERIODS);193	if (ret < 0) {194		kfree(iprtd);195		return ret;196	}197 198	snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);199	return 0;200}201 202static int snd_imx_close(struct snd_soc_component *component,203			 struct snd_pcm_substream *substream)204{205	struct snd_pcm_runtime *runtime = substream->runtime;206	struct imx_pcm_runtime_data *iprtd = runtime->private_data;207 208	hrtimer_cancel(&iprtd->hrt);209 210	kfree(iprtd);211 212	return 0;213}214 215static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)216{217	struct snd_card *card = rtd->card->snd_card;218	struct snd_pcm *pcm = rtd->pcm;219	int ret;220 221	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));222	if (ret)223		return ret;224 225	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,226					    pcm->card->dev,227					    IMX_SSI_DMABUF_SIZE);228}229 230static int ssi_irq;231 232static int snd_imx_pcm_new(struct snd_soc_component *component,233			   struct snd_soc_pcm_runtime *rtd)234{235	struct snd_pcm *pcm = rtd->pcm;236	struct snd_pcm_substream *substream;237	int ret;238 239	ret = imx_pcm_new(rtd);240	if (ret)241		return ret;242 243	substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;244	if (substream) {245		struct snd_dma_buffer *buf = &substream->dma_buffer;246 247		imx_ssi_fiq_tx_buffer = (unsigned long)buf->area;248	}249 250	substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;251	if (substream) {252		struct snd_dma_buffer *buf = &substream->dma_buffer;253 254		imx_ssi_fiq_rx_buffer = (unsigned long)buf->area;255	}256 257	set_fiq_handler(&imx_ssi_fiq_start,258		&imx_ssi_fiq_end - &imx_ssi_fiq_start);259 260	return 0;261}262 263static void snd_imx_pcm_free(struct snd_soc_component *component,264			     struct snd_pcm *pcm)265{266	mxc_set_irq_fiq(ssi_irq, 0);267	release_fiq(&fh);268}269 270static const struct snd_soc_component_driver imx_soc_component_fiq = {271	.open		= snd_imx_open,272	.close		= snd_imx_close,273	.hw_params	= snd_imx_pcm_hw_params,274	.prepare	= snd_imx_pcm_prepare,275	.trigger	= snd_imx_pcm_trigger,276	.pointer	= snd_imx_pcm_pointer,277	.pcm_construct	= snd_imx_pcm_new,278	.pcm_destruct	= snd_imx_pcm_free,279};280 281int imx_pcm_fiq_init(struct platform_device *pdev,282		struct imx_pcm_fiq_params *params)283{284	int ret;285 286	ret = claim_fiq(&fh);287	if (ret) {288		dev_err(&pdev->dev, "failed to claim fiq: %d", ret);289		return ret;290	}291 292	mxc_set_irq_fiq(params->irq, 1);293	ssi_irq = params->irq;294 295	imx_pcm_fiq = params->irq;296 297	imx_ssi_fiq_base = (unsigned long)params->base;298 299	params->dma_params_tx->maxburst = 4;300	params->dma_params_rx->maxburst = 6;301 302	ret = devm_snd_soc_register_component(&pdev->dev, &imx_soc_component_fiq,303					      NULL, 0);304	if (ret)305		goto failed_register;306 307	return 0;308 309failed_register:310	mxc_set_irq_fiq(ssi_irq, 0);311	release_fiq(&fh);312 313	return ret;314}315EXPORT_SYMBOL_GPL(imx_pcm_fiq_init);316 317void imx_pcm_fiq_exit(struct platform_device *pdev)318{319}320EXPORT_SYMBOL_GPL(imx_pcm_fiq_exit);321 322MODULE_DESCRIPTION("Freescale i.MX PCM FIQ handler");323MODULE_LICENSE("GPL");324