brintos

brintos / linux-shallow public Read only

0
0
Text · 12.9 KiB · 345f338 Raw
458 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// Freescale MPC5200 PSC DMA4// ALSA SoC Platform driver5//6// Copyright (C) 2008 Secret Lab Technologies Ltd.7// Copyright (C) 2009 Jon Smirl, Digispeaker8 9#include <linux/module.h>10#include <linux/dma-mapping.h>11#include <linux/slab.h>12#include <linux/of.h>13#include <linux/of_address.h>14#include <linux/of_irq.h>15#include <linux/platform_device.h>16 17#include <sound/soc.h>18 19#include <linux/fsl/bestcomm/bestcomm.h>20#include <linux/fsl/bestcomm/gen_bd.h>21#include <asm/mpc52xx_psc.h>22 23#include "mpc5200_dma.h"24 25#define DRV_NAME "mpc5200_dma"26 27/*28 * Interrupt handlers29 */30static irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma)31{32	struct psc_dma *psc_dma = _psc_dma;33	struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;34	u16 isr;35 36	isr = in_be16(&regs->mpc52xx_psc_isr);37 38	/* Playback underrun error */39	if (psc_dma->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP))40		psc_dma->stats.underrun_count++;41 42	/* Capture overrun error */43	if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR))44		psc_dma->stats.overrun_count++;45 46	out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);47 48	return IRQ_HANDLED;49}50 51/**52 * psc_dma_bcom_enqueue_next_buffer - Enqueue another audio buffer53 * @s: pointer to stream private data structure54 *55 * Enqueues another audio period buffer into the bestcomm queue.56 *57 * Note: The routine must only be called when there is space available in58 * the queue.  Otherwise the enqueue will fail and the audio ring buffer59 * will get out of sync60 */61static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s)62{63	struct bcom_bd *bd;64 65	/* Prepare and enqueue the next buffer descriptor */66	bd = bcom_prepare_next_buffer(s->bcom_task);67	bd->status = s->period_bytes;68	bd->data[0] = s->runtime->dma_addr + (s->period_next * s->period_bytes);69	bcom_submit_next_buffer(s->bcom_task, NULL);70 71	/* Update for next period */72	s->period_next = (s->period_next + 1) % s->runtime->periods;73}74 75/* Bestcomm DMA irq handler */76static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream)77{78	struct psc_dma_stream *s = _psc_dma_stream;79 80	spin_lock(&s->psc_dma->lock);81	/* For each finished period, dequeue the completed period buffer82	 * and enqueue a new one in it's place. */83	while (bcom_buffer_done(s->bcom_task)) {84		bcom_retrieve_buffer(s->bcom_task, NULL, NULL);85 86		s->period_current = (s->period_current+1) % s->runtime->periods;87		s->period_count++;88 89		psc_dma_bcom_enqueue_next_buffer(s);90	}91	spin_unlock(&s->psc_dma->lock);92 93	/* If the stream is active, then also inform the PCM middle layer94	 * of the period finished event. */95	if (s->active)96		snd_pcm_period_elapsed(s->stream);97 98	return IRQ_HANDLED;99}100 101/**102 * psc_dma_trigger: start and stop the DMA transfer.103 * @component: triggered component104 * @substream: triggered substream105 * @cmd: triggered command106 *107 * This function is called by ALSA to start, stop, pause, and resume the DMA108 * transfer of data.109 */110static int psc_dma_trigger(struct snd_soc_component *component,111			   struct snd_pcm_substream *substream, int cmd)112{113	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);114	struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));115	struct snd_pcm_runtime *runtime = substream->runtime;116	struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma);117	struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;118	u16 imr;119	unsigned long flags;120	int i;121 122	switch (cmd) {123	case SNDRV_PCM_TRIGGER_START:124		dev_dbg(psc_dma->dev, "START: stream=%i fbits=%u ps=%u #p=%u\n",125			substream->pstr->stream, runtime->frame_bits,126			(int)runtime->period_size, runtime->periods);127		s->period_bytes = frames_to_bytes(runtime,128						  runtime->period_size);129		s->period_next = 0;130		s->period_current = 0;131		s->active = 1;132		s->period_count = 0;133		s->runtime = runtime;134 135		/* Fill up the bestcomm bd queue and enable DMA.136		 * This will begin filling the PSC's fifo.137		 */138		spin_lock_irqsave(&psc_dma->lock, flags);139 140		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)141			bcom_gen_bd_rx_reset(s->bcom_task);142		else143			bcom_gen_bd_tx_reset(s->bcom_task);144 145		for (i = 0; i < runtime->periods; i++)146			if (!bcom_queue_full(s->bcom_task))147				psc_dma_bcom_enqueue_next_buffer(s);148 149		bcom_enable(s->bcom_task);150		spin_unlock_irqrestore(&psc_dma->lock, flags);151 152		out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);153 154		break;155 156	case SNDRV_PCM_TRIGGER_STOP:157		dev_dbg(psc_dma->dev, "STOP: stream=%i periods_count=%i\n",158			substream->pstr->stream, s->period_count);159		s->active = 0;160 161		spin_lock_irqsave(&psc_dma->lock, flags);162		bcom_disable(s->bcom_task);163		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)164			bcom_gen_bd_rx_reset(s->bcom_task);165		else166			bcom_gen_bd_tx_reset(s->bcom_task);167		spin_unlock_irqrestore(&psc_dma->lock, flags);168 169		break;170 171	default:172		dev_dbg(psc_dma->dev, "unhandled trigger: stream=%i cmd=%i\n",173			substream->pstr->stream, cmd);174		return -EINVAL;175	}176 177	/* Update interrupt enable settings */178	imr = 0;179	if (psc_dma->playback.active)180		imr |= MPC52xx_PSC_IMR_TXEMP;181	if (psc_dma->capture.active)182		imr |= MPC52xx_PSC_IMR_ORERR;183	out_be16(&regs->isr_imr.imr, psc_dma->imr | imr);184 185	return 0;186}187 188 189/* ---------------------------------------------------------------------190 * The PSC DMA 'ASoC platform' driver191 *192 * Can be referenced by an 'ASoC machine' driver193 * This driver only deals with the audio bus; it doesn't have any194 * interaction with the attached codec195 */196 197static const struct snd_pcm_hardware psc_dma_hardware = {198	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |199		SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |200		SNDRV_PCM_INFO_BATCH,201	.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |202		SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,203	.period_bytes_max	= 1024 * 1024,204	.period_bytes_min	= 32,205	.periods_min		= 2,206	.periods_max		= 256,207	.buffer_bytes_max	= 2 * 1024 * 1024,208	.fifo_size		= 512,209};210 211static int psc_dma_open(struct snd_soc_component *component,212			struct snd_pcm_substream *substream)213{214	struct snd_pcm_runtime *runtime = substream->runtime;215	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);216	struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));217	struct psc_dma_stream *s;218	int rc;219 220	dev_dbg(psc_dma->dev, "psc_dma_open(substream=%p)\n", substream);221 222	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)223		s = &psc_dma->capture;224	else225		s = &psc_dma->playback;226 227	snd_soc_set_runtime_hwparams(substream, &psc_dma_hardware);228 229	rc = snd_pcm_hw_constraint_integer(runtime,230		SNDRV_PCM_HW_PARAM_PERIODS);231	if (rc < 0) {232		dev_err(substream->pcm->card->dev, "invalid buffer size\n");233		return rc;234	}235 236	s->stream = substream;237	return 0;238}239 240static int psc_dma_close(struct snd_soc_component *component,241			 struct snd_pcm_substream *substream)242{243	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);244	struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));245	struct psc_dma_stream *s;246 247	dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream);248 249	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)250		s = &psc_dma->capture;251	else252		s = &psc_dma->playback;253 254	if (!psc_dma->playback.active &&255	    !psc_dma->capture.active) {256 257		/* Disable all interrupts and reset the PSC */258		out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);259		out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */260	}261	s->stream = NULL;262	return 0;263}264 265static snd_pcm_uframes_t266psc_dma_pointer(struct snd_soc_component *component,267		struct snd_pcm_substream *substream)268{269	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);270	struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));271	struct psc_dma_stream *s;272	dma_addr_t count;273 274	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)275		s = &psc_dma->capture;276	else277		s = &psc_dma->playback;278 279	count = s->period_current * s->period_bytes;280 281	return bytes_to_frames(substream->runtime, count);282}283 284static int psc_dma_new(struct snd_soc_component *component,285		       struct snd_soc_pcm_runtime *rtd)286{287	struct snd_card *card = rtd->card->snd_card;288	struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);289	struct snd_pcm *pcm = rtd->pcm;290	size_t size = psc_dma_hardware.buffer_bytes_max;291	int rc;292 293	dev_dbg(component->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n",294		card, dai, pcm);295 296	rc = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));297	if (rc)298		return rc;299 300	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev,301					    size);302}303 304static const struct snd_soc_component_driver mpc5200_audio_dma_component = {305	.name		= DRV_NAME,306	.open		= psc_dma_open,307	.close		= psc_dma_close,308	.pointer	= psc_dma_pointer,309	.trigger	= psc_dma_trigger,310	.pcm_construct	= psc_dma_new,311};312 313int mpc5200_audio_dma_create(struct platform_device *op)314{315	phys_addr_t fifo;316	struct psc_dma *psc_dma;317	struct resource res;318	int size, irq, rc;319	const __be32 *prop;320	void __iomem *regs;321	int ret;322 323	/* Fetch the registers and IRQ of the PSC */324	irq = irq_of_parse_and_map(op->dev.of_node, 0);325	if (of_address_to_resource(op->dev.of_node, 0, &res)) {326		dev_err(&op->dev, "Missing reg property\n");327		return -ENODEV;328	}329	regs = ioremap(res.start, resource_size(&res));330	if (!regs) {331		dev_err(&op->dev, "Could not map registers\n");332		return -ENODEV;333	}334 335	/* Allocate and initialize the driver private data */336	psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL);337	if (!psc_dma) {338		ret = -ENOMEM;339		goto out_unmap;340	}341 342	/* Get the PSC ID */343	prop = of_get_property(op->dev.of_node, "cell-index", &size);344	if (!prop || size < sizeof *prop) {345		ret = -ENODEV;346		goto out_free;347	}348 349	spin_lock_init(&psc_dma->lock);350	mutex_init(&psc_dma->mutex);351	psc_dma->id = be32_to_cpu(*prop);352	psc_dma->irq = irq;353	psc_dma->psc_regs = regs;354	psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs;355	psc_dma->dev = &op->dev;356	psc_dma->playback.psc_dma = psc_dma;357	psc_dma->capture.psc_dma = psc_dma;358	snprintf(psc_dma->name, sizeof(psc_dma->name), "PSC%d", psc_dma->id);359 360	/* Find the address of the fifo data registers and setup the361	 * DMA tasks */362	fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32);363	psc_dma->capture.bcom_task =364		bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512);365	psc_dma->playback.bcom_task =366		bcom_psc_gen_bd_tx_init(psc_dma->id, 10, fifo);367	if (!psc_dma->capture.bcom_task ||368	    !psc_dma->playback.bcom_task) {369		dev_err(&op->dev, "Could not allocate bestcomm tasks\n");370		ret = -ENODEV;371		goto out_free;372	}373 374	/* Disable all interrupts and reset the PSC */375	out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);376	 /* reset receiver */377	out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_RX);378	 /* reset transmitter */379	out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_TX);380	 /* reset error */381	out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_ERR_STAT);382	 /* reset mode */383	out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_SEL_MODE_REG_1);384 385	/* Set up mode register;386	 * First write: RxRdy (FIFO Alarm) generates rx FIFO irq387	 * Second write: register Normal mode for non loopback388	 */389	out_8(&psc_dma->psc_regs->mode, 0);390	out_8(&psc_dma->psc_regs->mode, 0);391 392	/* Set the TX and RX fifo alarm thresholds */393	out_be16(&psc_dma->fifo_regs->rfalarm, 0x100);394	out_8(&psc_dma->fifo_regs->rfcntl, 0x4);395	out_be16(&psc_dma->fifo_regs->tfalarm, 0x100);396	out_8(&psc_dma->fifo_regs->tfcntl, 0x7);397 398	/* Lookup the IRQ numbers */399	psc_dma->playback.irq =400		bcom_get_task_irq(psc_dma->playback.bcom_task);401	psc_dma->capture.irq =402		bcom_get_task_irq(psc_dma->capture.bcom_task);403 404	rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED,405			 "psc-dma-status", psc_dma);406	rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED,407			  "psc-dma-capture", &psc_dma->capture);408	rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED,409			  "psc-dma-playback", &psc_dma->playback);410	if (rc) {411		ret = -ENODEV;412		goto out_irq;413	}414 415	/* Save what we've done so it can be found again later */416	dev_set_drvdata(&op->dev, psc_dma);417 418	/* Tell the ASoC OF helpers about it */419	return devm_snd_soc_register_component(&op->dev,420					&mpc5200_audio_dma_component, NULL, 0);421out_irq:422	free_irq(psc_dma->irq, psc_dma);423	free_irq(psc_dma->capture.irq, &psc_dma->capture);424	free_irq(psc_dma->playback.irq, &psc_dma->playback);425out_free:426	kfree(psc_dma);427out_unmap:428	iounmap(regs);429	return ret;430}431EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create);432 433int mpc5200_audio_dma_destroy(struct platform_device *op)434{435	struct psc_dma *psc_dma = dev_get_drvdata(&op->dev);436 437	dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n");438 439	bcom_gen_bd_rx_release(psc_dma->capture.bcom_task);440	bcom_gen_bd_tx_release(psc_dma->playback.bcom_task);441 442	/* Release irqs */443	free_irq(psc_dma->irq, psc_dma);444	free_irq(psc_dma->capture.irq, &psc_dma->capture);445	free_irq(psc_dma->playback.irq, &psc_dma->playback);446 447	iounmap(psc_dma->psc_regs);448	kfree(psc_dma);449	dev_set_drvdata(&op->dev, NULL);450 451	return 0;452}453EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy);454 455MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");456MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver");457MODULE_LICENSE("GPL");458