sound/soc/uniphier/aio-dma.c

Source file repositories/reference/linux-study-clean/sound/soc/uniphier/aio-dma.c

File Facts

System
Linux kernel
Corpus path
sound/soc/uniphier/aio-dma.c
Extension
.c
Size
7460 bytes
Lines
274
Domain
Driver Families
Bucket
sound/soc
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO DMA driver.
//
// Copyright (c) 2016-2018 Socionext Inc.

#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>

#include "aio.h"

static const struct snd_pcm_hardware uniphier_aiodma_hw = {
	.info = SNDRV_PCM_INFO_MMAP |
		SNDRV_PCM_INFO_MMAP_VALID |
		SNDRV_PCM_INFO_INTERLEAVED,
	.period_bytes_min = 256,
	.period_bytes_max = 4096,
	.periods_min      = 4,
	.periods_max      = 1024,
	.buffer_bytes_max = 128 * 1024,
};

static void aiodma_pcm_irq(struct uniphier_aio_sub *sub)
{
	struct snd_pcm_runtime *runtime = sub->substream->runtime;
	int bytes = runtime->period_size *
		runtime->channels * samples_to_bytes(runtime, 1);
	int ret;

	scoped_guard(spinlock, &sub->lock) {
		ret = aiodma_rb_set_threshold(sub, runtime->dma_bytes,
					      sub->threshold + bytes);
		if (!ret)
			sub->threshold += bytes;

		aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes);
		aiodma_rb_clear_irq(sub);
	}

	snd_pcm_period_elapsed(sub->substream);
}

static void aiodma_compr_irq(struct uniphier_aio_sub *sub)
{
	struct snd_compr_runtime *runtime = sub->cstream->runtime;
	int bytes = runtime->fragment_size;
	int ret;

	scoped_guard(spinlock, &sub->lock) {
		ret = aiodma_rb_set_threshold(sub, sub->compr_bytes,
					      sub->threshold + bytes);
		if (!ret)
			sub->threshold += bytes;

		aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
		aiodma_rb_clear_irq(sub);
	}

	snd_compr_fragment_elapsed(sub->cstream);
}

static irqreturn_t aiodma_irq(int irq, void *p)
{
	struct platform_device *pdev = p;
	struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
	irqreturn_t ret = IRQ_NONE;
	int i, j;

	for (i = 0; i < chip->num_aios; i++) {
		struct uniphier_aio *aio = &chip->aios[i];

		for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
			struct uniphier_aio_sub *sub = &aio->sub[j];

			/* Skip channel that does not trigger */
			if (!sub->running || !aiodma_rb_is_irq(sub))
				continue;

			if (sub->substream)
				aiodma_pcm_irq(sub);
			if (sub->cstream)
				aiodma_compr_irq(sub);

			ret = IRQ_HANDLED;
		}

Annotation

Implementation Notes