162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// SH7760 ("camelot") DMABRG audio DMA unit support
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
662306a36Sopenharmony_ci//
762306a36Sopenharmony_ci// The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which
862306a36Sopenharmony_ci// trigger an interrupt when one half of the programmed transfer size
962306a36Sopenharmony_ci// has been xmitted.
1062306a36Sopenharmony_ci//
1162306a36Sopenharmony_ci// FIXME: little-endian only for now
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/gfp.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1862306a36Sopenharmony_ci#include <sound/core.h>
1962306a36Sopenharmony_ci#include <sound/pcm.h>
2062306a36Sopenharmony_ci#include <sound/pcm_params.h>
2162306a36Sopenharmony_ci#include <sound/soc.h>
2262306a36Sopenharmony_ci#include <asm/dmabrg.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* registers and bits */
2662306a36Sopenharmony_ci#define BRGATXSAR	0x00
2762306a36Sopenharmony_ci#define BRGARXDAR	0x04
2862306a36Sopenharmony_ci#define BRGATXTCR	0x08
2962306a36Sopenharmony_ci#define BRGARXTCR	0x0C
3062306a36Sopenharmony_ci#define BRGACR		0x10
3162306a36Sopenharmony_ci#define BRGATXTCNT	0x14
3262306a36Sopenharmony_ci#define BRGARXTCNT	0x18
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define ACR_RAR		(1 << 18)
3562306a36Sopenharmony_ci#define ACR_RDS		(1 << 17)
3662306a36Sopenharmony_ci#define ACR_RDE		(1 << 16)
3762306a36Sopenharmony_ci#define ACR_TAR		(1 << 2)
3862306a36Sopenharmony_ci#define ACR_TDS		(1 << 1)
3962306a36Sopenharmony_ci#define ACR_TDE		(1 << 0)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* receiver/transmitter data alignment */
4262306a36Sopenharmony_ci#define ACR_RAM_NONE	(0 << 24)
4362306a36Sopenharmony_ci#define ACR_RAM_4BYTE	(1 << 24)
4462306a36Sopenharmony_ci#define ACR_RAM_2WORD	(2 << 24)
4562306a36Sopenharmony_ci#define ACR_TAM_NONE	(0 << 8)
4662306a36Sopenharmony_ci#define ACR_TAM_4BYTE	(1 << 8)
4762306a36Sopenharmony_ci#define ACR_TAM_2WORD	(2 << 8)
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistruct camelot_pcm {
5162306a36Sopenharmony_ci	unsigned long mmio;  /* DMABRG audio channel control reg MMIO */
5262306a36Sopenharmony_ci	unsigned int txid;    /* ID of first DMABRG IRQ for this unit */
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	struct snd_pcm_substream *tx_ss;
5562306a36Sopenharmony_ci	unsigned long tx_period_size;
5662306a36Sopenharmony_ci	unsigned int  tx_period;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	struct snd_pcm_substream *rx_ss;
5962306a36Sopenharmony_ci	unsigned long rx_period_size;
6062306a36Sopenharmony_ci	unsigned int  rx_period;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci} cam_pcm_data[2] = {
6362306a36Sopenharmony_ci	{
6462306a36Sopenharmony_ci		.mmio	=	0xFE3C0040,
6562306a36Sopenharmony_ci		.txid	=	DMABRGIRQ_A0TXF,
6662306a36Sopenharmony_ci	},
6762306a36Sopenharmony_ci	{
6862306a36Sopenharmony_ci		.mmio	=	0xFE3C0060,
6962306a36Sopenharmony_ci		.txid	=	DMABRGIRQ_A1TXF,
7062306a36Sopenharmony_ci	},
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define BRGREG(x)	(*(unsigned long *)(cam->mmio + (x)))
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*
7662306a36Sopenharmony_ci * set a minimum of 16kb per period, to avoid interrupt-"storm" and
7762306a36Sopenharmony_ci * resulting skipping. In general, the bigger the minimum size, the
7862306a36Sopenharmony_ci * better for overall system performance. (The SH7760 is a puny CPU
7962306a36Sopenharmony_ci * with a slow SDRAM interface and poor internal bus bandwidth,
8062306a36Sopenharmony_ci * *especially* when the LCDC is active).  The minimum for the DMAC
8162306a36Sopenharmony_ci * is 8 bytes; 16kbytes are enough to get skip-free playback of a
8262306a36Sopenharmony_ci * 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain
8362306a36Sopenharmony_ci * reasonable responsiveness in MPlayer.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_ci#define DMABRG_PERIOD_MIN		16 * 1024
8662306a36Sopenharmony_ci#define DMABRG_PERIOD_MAX		0x03fffffc
8762306a36Sopenharmony_ci#define DMABRG_PREALLOC_BUFFER		32 * 1024
8862306a36Sopenharmony_ci#define DMABRG_PREALLOC_BUFFER_MAX	32 * 1024
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic const struct snd_pcm_hardware camelot_pcm_hardware = {
9162306a36Sopenharmony_ci	.info = (SNDRV_PCM_INFO_MMAP |
9262306a36Sopenharmony_ci		SNDRV_PCM_INFO_INTERLEAVED |
9362306a36Sopenharmony_ci		SNDRV_PCM_INFO_BLOCK_TRANSFER |
9462306a36Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID |
9562306a36Sopenharmony_ci		SNDRV_PCM_INFO_BATCH),
9662306a36Sopenharmony_ci	.buffer_bytes_max =	DMABRG_PERIOD_MAX,
9762306a36Sopenharmony_ci	.period_bytes_min =	DMABRG_PERIOD_MIN,
9862306a36Sopenharmony_ci	.period_bytes_max =	DMABRG_PERIOD_MAX / 2,
9962306a36Sopenharmony_ci	.periods_min =		2,
10062306a36Sopenharmony_ci	.periods_max =		2,
10162306a36Sopenharmony_ci	.fifo_size =		128,
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void camelot_txdma(void *data)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct camelot_pcm *cam = data;
10762306a36Sopenharmony_ci	cam->tx_period ^= 1;
10862306a36Sopenharmony_ci	snd_pcm_period_elapsed(cam->tx_ss);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void camelot_rxdma(void *data)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct camelot_pcm *cam = data;
11462306a36Sopenharmony_ci	cam->rx_period ^= 1;
11562306a36Sopenharmony_ci	snd_pcm_period_elapsed(cam->rx_ss);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int camelot_pcm_open(struct snd_soc_component *component,
11962306a36Sopenharmony_ci			    struct snd_pcm_substream *substream)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
12262306a36Sopenharmony_ci	struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id];
12362306a36Sopenharmony_ci	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
12462306a36Sopenharmony_ci	int ret, dmairq;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* DMABRG buffer half/full events */
12962306a36Sopenharmony_ci	dmairq = (recv) ? cam->txid + 2 : cam->txid;
13062306a36Sopenharmony_ci	if (recv) {
13162306a36Sopenharmony_ci		cam->rx_ss = substream;
13262306a36Sopenharmony_ci		ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam);
13362306a36Sopenharmony_ci		if (unlikely(ret)) {
13462306a36Sopenharmony_ci			pr_debug("audio unit %d irqs already taken!\n",
13562306a36Sopenharmony_ci			     asoc_rtd_to_cpu(rtd, 0)->id);
13662306a36Sopenharmony_ci			return -EBUSY;
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci		(void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam);
13962306a36Sopenharmony_ci	} else {
14062306a36Sopenharmony_ci		cam->tx_ss = substream;
14162306a36Sopenharmony_ci		ret = dmabrg_request_irq(dmairq, camelot_txdma, cam);
14262306a36Sopenharmony_ci		if (unlikely(ret)) {
14362306a36Sopenharmony_ci			pr_debug("audio unit %d irqs already taken!\n",
14462306a36Sopenharmony_ci			     asoc_rtd_to_cpu(rtd, 0)->id);
14562306a36Sopenharmony_ci			return -EBUSY;
14662306a36Sopenharmony_ci		}
14762306a36Sopenharmony_ci		(void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam);
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci	return 0;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int camelot_pcm_close(struct snd_soc_component *component,
15362306a36Sopenharmony_ci			     struct snd_pcm_substream *substream)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
15662306a36Sopenharmony_ci	struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id];
15762306a36Sopenharmony_ci	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
15862306a36Sopenharmony_ci	int dmairq;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	dmairq = (recv) ? cam->txid + 2 : cam->txid;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (recv)
16362306a36Sopenharmony_ci		cam->rx_ss = NULL;
16462306a36Sopenharmony_ci	else
16562306a36Sopenharmony_ci		cam->tx_ss = NULL;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	dmabrg_free_irq(dmairq + 1);
16862306a36Sopenharmony_ci	dmabrg_free_irq(dmairq);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic int camelot_hw_params(struct snd_soc_component *component,
17462306a36Sopenharmony_ci			     struct snd_pcm_substream *substream,
17562306a36Sopenharmony_ci			     struct snd_pcm_hw_params *hw_params)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
17862306a36Sopenharmony_ci	struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id];
17962306a36Sopenharmony_ci	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (recv) {
18262306a36Sopenharmony_ci		cam->rx_period_size = params_period_bytes(hw_params);
18362306a36Sopenharmony_ci		cam->rx_period = 0;
18462306a36Sopenharmony_ci	} else {
18562306a36Sopenharmony_ci		cam->tx_period_size = params_period_bytes(hw_params);
18662306a36Sopenharmony_ci		cam->tx_period = 0;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci	return 0;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int camelot_prepare(struct snd_soc_component *component,
19262306a36Sopenharmony_ci			   struct snd_pcm_substream *substream)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
19562306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
19662306a36Sopenharmony_ci	struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id];
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	pr_debug("PCM data: addr 0x%08lx len %d\n",
19962306a36Sopenharmony_ci		 (u32)runtime->dma_addr, runtime->dma_bytes);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
20262306a36Sopenharmony_ci		BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area;
20362306a36Sopenharmony_ci		BRGREG(BRGATXTCR) = runtime->dma_bytes;
20462306a36Sopenharmony_ci	} else {
20562306a36Sopenharmony_ci		BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area;
20662306a36Sopenharmony_ci		BRGREG(BRGARXTCR) = runtime->dma_bytes;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic inline void dmabrg_play_dma_start(struct camelot_pcm *cam)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
21562306a36Sopenharmony_ci	/* start DMABRG engine: XFER start, auto-addr-reload */
21662306a36Sopenharmony_ci	BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic inline void dmabrg_play_dma_stop(struct camelot_pcm *cam)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
22262306a36Sopenharmony_ci	/* forcibly terminate data transmission */
22362306a36Sopenharmony_ci	BRGREG(BRGACR) = acr | ACR_TDS;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic inline void dmabrg_rec_dma_start(struct camelot_pcm *cam)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
22962306a36Sopenharmony_ci	/* start DMABRG engine: recv start, auto-reload */
23062306a36Sopenharmony_ci	BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
23662306a36Sopenharmony_ci	/* forcibly terminate data receiver */
23762306a36Sopenharmony_ci	BRGREG(BRGACR) = acr | ACR_RDS;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic int camelot_trigger(struct snd_soc_component *component,
24162306a36Sopenharmony_ci			   struct snd_pcm_substream *substream, int cmd)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
24462306a36Sopenharmony_ci	struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id];
24562306a36Sopenharmony_ci	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	switch (cmd) {
24862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
24962306a36Sopenharmony_ci		if (recv)
25062306a36Sopenharmony_ci			dmabrg_rec_dma_start(cam);
25162306a36Sopenharmony_ci		else
25262306a36Sopenharmony_ci			dmabrg_play_dma_start(cam);
25362306a36Sopenharmony_ci		break;
25462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
25562306a36Sopenharmony_ci		if (recv)
25662306a36Sopenharmony_ci			dmabrg_rec_dma_stop(cam);
25762306a36Sopenharmony_ci		else
25862306a36Sopenharmony_ci			dmabrg_play_dma_stop(cam);
25962306a36Sopenharmony_ci		break;
26062306a36Sopenharmony_ci	default:
26162306a36Sopenharmony_ci		return -EINVAL;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return 0;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic snd_pcm_uframes_t camelot_pos(struct snd_soc_component *component,
26862306a36Sopenharmony_ci				     struct snd_pcm_substream *substream)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
27162306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
27262306a36Sopenharmony_ci	struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id];
27362306a36Sopenharmony_ci	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
27462306a36Sopenharmony_ci	unsigned long pos;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* cannot use the DMABRG pointer register: under load, by the
27762306a36Sopenharmony_ci	 * time ALSA comes around to read the register, it is already
27862306a36Sopenharmony_ci	 * far ahead (or worse, already done with the fragment) of the
27962306a36Sopenharmony_ci	 * position at the time the IRQ was triggered, which results in
28062306a36Sopenharmony_ci	 * fast-playback sound in my test application (ScummVM)
28162306a36Sopenharmony_ci	 */
28262306a36Sopenharmony_ci	if (recv)
28362306a36Sopenharmony_ci		pos = cam->rx_period ? cam->rx_period_size : 0;
28462306a36Sopenharmony_ci	else
28562306a36Sopenharmony_ci		pos = cam->tx_period ? cam->tx_period_size : 0;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return bytes_to_frames(runtime, pos);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int camelot_pcm_new(struct snd_soc_component *component,
29162306a36Sopenharmony_ci			   struct snd_soc_pcm_runtime *rtd)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct snd_pcm *pcm = rtd->pcm;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
29662306a36Sopenharmony_ci	 * in MMAP mode (i.e. aplay -M)
29762306a36Sopenharmony_ci	 */
29862306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm,
29962306a36Sopenharmony_ci		SNDRV_DMA_TYPE_CONTINUOUS,
30062306a36Sopenharmony_ci		NULL,
30162306a36Sopenharmony_ci		DMABRG_PREALLOC_BUFFER,	DMABRG_PREALLOC_BUFFER_MAX);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return 0;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic const struct snd_soc_component_driver sh7760_soc_component = {
30762306a36Sopenharmony_ci	.open		= camelot_pcm_open,
30862306a36Sopenharmony_ci	.close		= camelot_pcm_close,
30962306a36Sopenharmony_ci	.hw_params	= camelot_hw_params,
31062306a36Sopenharmony_ci	.prepare	= camelot_prepare,
31162306a36Sopenharmony_ci	.trigger	= camelot_trigger,
31262306a36Sopenharmony_ci	.pointer	= camelot_pos,
31362306a36Sopenharmony_ci	.pcm_construct	= camelot_pcm_new,
31462306a36Sopenharmony_ci};
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic int sh7760_soc_platform_probe(struct platform_device *pdev)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	return devm_snd_soc_register_component(&pdev->dev, &sh7760_soc_component,
31962306a36Sopenharmony_ci					       NULL, 0);
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic struct platform_driver sh7760_pcm_driver = {
32362306a36Sopenharmony_ci	.driver = {
32462306a36Sopenharmony_ci			.name = "sh7760-pcm-audio",
32562306a36Sopenharmony_ci	},
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	.probe = sh7760_soc_platform_probe,
32862306a36Sopenharmony_ci};
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cimodule_platform_driver(sh7760_pcm_driver);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
33362306a36Sopenharmony_ciMODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
33462306a36Sopenharmony_ciMODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
335