162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Documentation: ARM DDI 0173B
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/ioport.h>
1362306a36Sopenharmony_ci#include <linux/device.h>
1462306a36Sopenharmony_ci#include <linux/spinlock.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/err.h>
1762306a36Sopenharmony_ci#include <linux/amba/bus.h>
1862306a36Sopenharmony_ci#include <linux/io.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <sound/core.h>
2162306a36Sopenharmony_ci#include <sound/initval.h>
2262306a36Sopenharmony_ci#include <sound/ac97_codec.h>
2362306a36Sopenharmony_ci#include <sound/pcm.h>
2462306a36Sopenharmony_ci#include <sound/pcm_params.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "aaci.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define DRIVER_NAME	"aaci-pl041"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define FRAME_PERIOD_US	21
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * PM support is not complete.  Turn it off.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_ci#undef CONFIG_PM
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *ac97)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	u32 v, maincr = aaci->maincr | MAINCR_SCRA(ac97->num);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/*
4262306a36Sopenharmony_ci	 * Ensure that the slot 1/2 RX registers are empty.
4362306a36Sopenharmony_ci	 */
4462306a36Sopenharmony_ci	v = readl(aaci->base + AACI_SLFR);
4562306a36Sopenharmony_ci	if (v & SLFR_2RXV)
4662306a36Sopenharmony_ci		readl(aaci->base + AACI_SL2RX);
4762306a36Sopenharmony_ci	if (v & SLFR_1RXV)
4862306a36Sopenharmony_ci		readl(aaci->base + AACI_SL1RX);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (maincr != readl(aaci->base + AACI_MAINCR)) {
5162306a36Sopenharmony_ci		writel(maincr, aaci->base + AACI_MAINCR);
5262306a36Sopenharmony_ci		readl(aaci->base + AACI_MAINCR);
5362306a36Sopenharmony_ci		udelay(1);
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/*
5862306a36Sopenharmony_ci * P29:
5962306a36Sopenharmony_ci *  The recommended use of programming the external codec through slot 1
6062306a36Sopenharmony_ci *  and slot 2 data is to use the channels during setup routines and the
6162306a36Sopenharmony_ci *  slot register at any other time.  The data written into slot 1, slot 2
6262306a36Sopenharmony_ci *  and slot 12 registers is transmitted only when their corresponding
6362306a36Sopenharmony_ci *  SI1TxEn, SI2TxEn and SI12TxEn bits are set in the AACI_MAINCR
6462306a36Sopenharmony_ci *  register.
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_cistatic void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
6762306a36Sopenharmony_ci			    unsigned short val)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct aaci *aaci = ac97->private_data;
7062306a36Sopenharmony_ci	int timeout;
7162306a36Sopenharmony_ci	u32 v;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (ac97->num >= 4)
7462306a36Sopenharmony_ci		return;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	mutex_lock(&aaci->ac97_sem);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	aaci_ac97_select_codec(aaci, ac97);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/*
8162306a36Sopenharmony_ci	 * P54: You must ensure that AACI_SL2TX is always written
8262306a36Sopenharmony_ci	 * to, if required, before data is written to AACI_SL1TX.
8362306a36Sopenharmony_ci	 */
8462306a36Sopenharmony_ci	writel(val << 4, aaci->base + AACI_SL2TX);
8562306a36Sopenharmony_ci	writel(reg << 12, aaci->base + AACI_SL1TX);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* Initially, wait one frame period */
8862306a36Sopenharmony_ci	udelay(FRAME_PERIOD_US);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* And then wait an additional eight frame periods for it to be sent */
9162306a36Sopenharmony_ci	timeout = FRAME_PERIOD_US * 8;
9262306a36Sopenharmony_ci	do {
9362306a36Sopenharmony_ci		udelay(1);
9462306a36Sopenharmony_ci		v = readl(aaci->base + AACI_SLFR);
9562306a36Sopenharmony_ci	} while ((v & (SLFR_1TXB|SLFR_2TXB)) && --timeout);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (v & (SLFR_1TXB|SLFR_2TXB))
9862306a36Sopenharmony_ci		dev_err(&aaci->dev->dev,
9962306a36Sopenharmony_ci			"timeout waiting for write to complete\n");
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	mutex_unlock(&aaci->ac97_sem);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*
10562306a36Sopenharmony_ci * Read an AC'97 register.
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_cistatic unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct aaci *aaci = ac97->private_data;
11062306a36Sopenharmony_ci	int timeout, retries = 10;
11162306a36Sopenharmony_ci	u32 v;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (ac97->num >= 4)
11462306a36Sopenharmony_ci		return ~0;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	mutex_lock(&aaci->ac97_sem);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	aaci_ac97_select_codec(aaci, ac97);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/*
12162306a36Sopenharmony_ci	 * Write the register address to slot 1.
12262306a36Sopenharmony_ci	 */
12362306a36Sopenharmony_ci	writel((reg << 12) | (1 << 19), aaci->base + AACI_SL1TX);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Initially, wait one frame period */
12662306a36Sopenharmony_ci	udelay(FRAME_PERIOD_US);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* And then wait an additional eight frame periods for it to be sent */
12962306a36Sopenharmony_ci	timeout = FRAME_PERIOD_US * 8;
13062306a36Sopenharmony_ci	do {
13162306a36Sopenharmony_ci		udelay(1);
13262306a36Sopenharmony_ci		v = readl(aaci->base + AACI_SLFR);
13362306a36Sopenharmony_ci	} while ((v & SLFR_1TXB) && --timeout);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (v & SLFR_1TXB) {
13662306a36Sopenharmony_ci		dev_err(&aaci->dev->dev, "timeout on slot 1 TX busy\n");
13762306a36Sopenharmony_ci		v = ~0;
13862306a36Sopenharmony_ci		goto out;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* Now wait for the response frame */
14262306a36Sopenharmony_ci	udelay(FRAME_PERIOD_US);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* And then wait an additional eight frame periods for data */
14562306a36Sopenharmony_ci	timeout = FRAME_PERIOD_US * 8;
14662306a36Sopenharmony_ci	do {
14762306a36Sopenharmony_ci		udelay(1);
14862306a36Sopenharmony_ci		cond_resched();
14962306a36Sopenharmony_ci		v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV);
15062306a36Sopenharmony_ci	} while ((v != (SLFR_1RXV|SLFR_2RXV)) && --timeout);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (v != (SLFR_1RXV|SLFR_2RXV)) {
15362306a36Sopenharmony_ci		dev_err(&aaci->dev->dev, "timeout on RX valid\n");
15462306a36Sopenharmony_ci		v = ~0;
15562306a36Sopenharmony_ci		goto out;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	do {
15962306a36Sopenharmony_ci		v = readl(aaci->base + AACI_SL1RX) >> 12;
16062306a36Sopenharmony_ci		if (v == reg) {
16162306a36Sopenharmony_ci			v = readl(aaci->base + AACI_SL2RX) >> 4;
16262306a36Sopenharmony_ci			break;
16362306a36Sopenharmony_ci		} else if (--retries) {
16462306a36Sopenharmony_ci			dev_warn(&aaci->dev->dev,
16562306a36Sopenharmony_ci				 "ac97 read back fail.  retry\n");
16662306a36Sopenharmony_ci			continue;
16762306a36Sopenharmony_ci		} else {
16862306a36Sopenharmony_ci			dev_warn(&aaci->dev->dev,
16962306a36Sopenharmony_ci				"wrong ac97 register read back (%x != %x)\n",
17062306a36Sopenharmony_ci				v, reg);
17162306a36Sopenharmony_ci			v = ~0;
17262306a36Sopenharmony_ci		}
17362306a36Sopenharmony_ci	} while (retries);
17462306a36Sopenharmony_ci out:
17562306a36Sopenharmony_ci	mutex_unlock(&aaci->ac97_sem);
17662306a36Sopenharmony_ci	return v;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic inline void
18062306a36Sopenharmony_ciaaci_chan_wait_ready(struct aaci_runtime *aacirun, unsigned long mask)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	u32 val;
18362306a36Sopenharmony_ci	int timeout = 5000;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	do {
18662306a36Sopenharmony_ci		udelay(1);
18762306a36Sopenharmony_ci		val = readl(aacirun->base + AACI_SR);
18862306a36Sopenharmony_ci	} while (val & mask && timeout--);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/*
19462306a36Sopenharmony_ci * Interrupt support.
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_cistatic void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	if (mask & ISR_ORINTR) {
19962306a36Sopenharmony_ci		dev_warn(&aaci->dev->dev, "RX overrun on chan %d\n", channel);
20062306a36Sopenharmony_ci		writel(ICLR_RXOEC1 << channel, aaci->base + AACI_INTCLR);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (mask & ISR_RXTOINTR) {
20462306a36Sopenharmony_ci		dev_warn(&aaci->dev->dev, "RX timeout on chan %d\n", channel);
20562306a36Sopenharmony_ci		writel(ICLR_RXTOFEC1 << channel, aaci->base + AACI_INTCLR);
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (mask & ISR_RXINTR) {
20962306a36Sopenharmony_ci		struct aaci_runtime *aacirun = &aaci->capture;
21062306a36Sopenharmony_ci		bool period_elapsed = false;
21162306a36Sopenharmony_ci		void *ptr;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		if (!aacirun->substream || !aacirun->start) {
21462306a36Sopenharmony_ci			dev_warn(&aaci->dev->dev, "RX interrupt???\n");
21562306a36Sopenharmony_ci			writel(0, aacirun->base + AACI_IE);
21662306a36Sopenharmony_ci			return;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		spin_lock(&aacirun->lock);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		ptr = aacirun->ptr;
22262306a36Sopenharmony_ci		do {
22362306a36Sopenharmony_ci			unsigned int len = aacirun->fifo_bytes;
22462306a36Sopenharmony_ci			u32 val;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci			if (aacirun->bytes <= 0) {
22762306a36Sopenharmony_ci				aacirun->bytes += aacirun->period;
22862306a36Sopenharmony_ci				period_elapsed = true;
22962306a36Sopenharmony_ci			}
23062306a36Sopenharmony_ci			if (!(aacirun->cr & CR_EN))
23162306a36Sopenharmony_ci				break;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci			val = readl(aacirun->base + AACI_SR);
23462306a36Sopenharmony_ci			if (!(val & SR_RXHF))
23562306a36Sopenharmony_ci				break;
23662306a36Sopenharmony_ci			if (!(val & SR_RXFF))
23762306a36Sopenharmony_ci				len >>= 1;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci			aacirun->bytes -= len;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci			/* reading 16 bytes at a time */
24262306a36Sopenharmony_ci			for( ; len > 0; len -= 16) {
24362306a36Sopenharmony_ci				asm(
24462306a36Sopenharmony_ci					"ldmia	%1, {r0, r1, r2, r3}\n\t"
24562306a36Sopenharmony_ci					"stmia	%0!, {r0, r1, r2, r3}"
24662306a36Sopenharmony_ci					: "+r" (ptr)
24762306a36Sopenharmony_ci					: "r" (aacirun->fifo)
24862306a36Sopenharmony_ci					: "r0", "r1", "r2", "r3", "cc");
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci				if (ptr >= aacirun->end)
25162306a36Sopenharmony_ci					ptr = aacirun->start;
25262306a36Sopenharmony_ci			}
25362306a36Sopenharmony_ci		} while(1);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		aacirun->ptr = ptr;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		spin_unlock(&aacirun->lock);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		if (period_elapsed)
26062306a36Sopenharmony_ci			snd_pcm_period_elapsed(aacirun->substream);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (mask & ISR_URINTR) {
26462306a36Sopenharmony_ci		dev_dbg(&aaci->dev->dev, "TX underrun on chan %d\n", channel);
26562306a36Sopenharmony_ci		writel(ICLR_TXUEC1 << channel, aaci->base + AACI_INTCLR);
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (mask & ISR_TXINTR) {
26962306a36Sopenharmony_ci		struct aaci_runtime *aacirun = &aaci->playback;
27062306a36Sopenharmony_ci		bool period_elapsed = false;
27162306a36Sopenharmony_ci		void *ptr;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		if (!aacirun->substream || !aacirun->start) {
27462306a36Sopenharmony_ci			dev_warn(&aaci->dev->dev, "TX interrupt???\n");
27562306a36Sopenharmony_ci			writel(0, aacirun->base + AACI_IE);
27662306a36Sopenharmony_ci			return;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		spin_lock(&aacirun->lock);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		ptr = aacirun->ptr;
28262306a36Sopenharmony_ci		do {
28362306a36Sopenharmony_ci			unsigned int len = aacirun->fifo_bytes;
28462306a36Sopenharmony_ci			u32 val;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci			if (aacirun->bytes <= 0) {
28762306a36Sopenharmony_ci				aacirun->bytes += aacirun->period;
28862306a36Sopenharmony_ci				period_elapsed = true;
28962306a36Sopenharmony_ci			}
29062306a36Sopenharmony_ci			if (!(aacirun->cr & CR_EN))
29162306a36Sopenharmony_ci				break;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci			val = readl(aacirun->base + AACI_SR);
29462306a36Sopenharmony_ci			if (!(val & SR_TXHE))
29562306a36Sopenharmony_ci				break;
29662306a36Sopenharmony_ci			if (!(val & SR_TXFE))
29762306a36Sopenharmony_ci				len >>= 1;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci			aacirun->bytes -= len;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci			/* writing 16 bytes at a time */
30262306a36Sopenharmony_ci			for ( ; len > 0; len -= 16) {
30362306a36Sopenharmony_ci				asm(
30462306a36Sopenharmony_ci					"ldmia	%0!, {r0, r1, r2, r3}\n\t"
30562306a36Sopenharmony_ci					"stmia	%1, {r0, r1, r2, r3}"
30662306a36Sopenharmony_ci					: "+r" (ptr)
30762306a36Sopenharmony_ci					: "r" (aacirun->fifo)
30862306a36Sopenharmony_ci					: "r0", "r1", "r2", "r3", "cc");
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci				if (ptr >= aacirun->end)
31162306a36Sopenharmony_ci					ptr = aacirun->start;
31262306a36Sopenharmony_ci			}
31362306a36Sopenharmony_ci		} while (1);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		aacirun->ptr = ptr;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		spin_unlock(&aacirun->lock);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		if (period_elapsed)
32062306a36Sopenharmony_ci			snd_pcm_period_elapsed(aacirun->substream);
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic irqreturn_t aaci_irq(int irq, void *devid)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct aaci *aaci = devid;
32762306a36Sopenharmony_ci	u32 mask;
32862306a36Sopenharmony_ci	int i;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	mask = readl(aaci->base + AACI_ALLINTS);
33162306a36Sopenharmony_ci	if (mask) {
33262306a36Sopenharmony_ci		u32 m = mask;
33362306a36Sopenharmony_ci		for (i = 0; i < 4; i++, m >>= 7) {
33462306a36Sopenharmony_ci			if (m & 0x7f) {
33562306a36Sopenharmony_ci				aaci_fifo_irq(aaci, i, m);
33662306a36Sopenharmony_ci			}
33762306a36Sopenharmony_ci		}
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return mask ? IRQ_HANDLED : IRQ_NONE;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci/*
34662306a36Sopenharmony_ci * ALSA support.
34762306a36Sopenharmony_ci */
34862306a36Sopenharmony_cistatic const struct snd_pcm_hardware aaci_hw_info = {
34962306a36Sopenharmony_ci	.info			= SNDRV_PCM_INFO_MMAP |
35062306a36Sopenharmony_ci				  SNDRV_PCM_INFO_MMAP_VALID |
35162306a36Sopenharmony_ci				  SNDRV_PCM_INFO_INTERLEAVED |
35262306a36Sopenharmony_ci				  SNDRV_PCM_INFO_BLOCK_TRANSFER |
35362306a36Sopenharmony_ci				  SNDRV_PCM_INFO_RESUME,
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/*
35662306a36Sopenharmony_ci	 * ALSA doesn't support 18-bit or 20-bit packed into 32-bit
35762306a36Sopenharmony_ci	 * words.  It also doesn't support 12-bit at all.
35862306a36Sopenharmony_ci	 */
35962306a36Sopenharmony_ci	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* rates are setup from the AC'97 codec */
36262306a36Sopenharmony_ci	.channels_min		= 2,
36362306a36Sopenharmony_ci	.channels_max		= 2,
36462306a36Sopenharmony_ci	.buffer_bytes_max	= 64 * 1024,
36562306a36Sopenharmony_ci	.period_bytes_min	= 256,
36662306a36Sopenharmony_ci	.period_bytes_max	= PAGE_SIZE,
36762306a36Sopenharmony_ci	.periods_min		= 4,
36862306a36Sopenharmony_ci	.periods_max		= PAGE_SIZE / 16,
36962306a36Sopenharmony_ci};
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci/*
37262306a36Sopenharmony_ci * We can support two and four channel audio.  Unfortunately
37362306a36Sopenharmony_ci * six channel audio requires a non-standard channel ordering:
37462306a36Sopenharmony_ci *   2 -> FL(3), FR(4)
37562306a36Sopenharmony_ci *   4 -> FL(3), FR(4), SL(7), SR(8)
37662306a36Sopenharmony_ci *   6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
37762306a36Sopenharmony_ci *        FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
37862306a36Sopenharmony_ci * This requires an ALSA configuration file to correct.
37962306a36Sopenharmony_ci */
38062306a36Sopenharmony_cistatic int aaci_rule_channels(struct snd_pcm_hw_params *p,
38162306a36Sopenharmony_ci	struct snd_pcm_hw_rule *rule)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	static const unsigned int channel_list[] = { 2, 4, 6 };
38462306a36Sopenharmony_ci	struct aaci *aaci = rule->private;
38562306a36Sopenharmony_ci	unsigned int mask = 1 << 0, slots;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* pcms[0] is the our 5.1 PCM instance. */
38862306a36Sopenharmony_ci	slots = aaci->ac97_bus->pcms[0].r[0].slots;
38962306a36Sopenharmony_ci	if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
39062306a36Sopenharmony_ci		mask |= 1 << 1;
39162306a36Sopenharmony_ci		if (slots & (1 << AC97_SLOT_LFE))
39262306a36Sopenharmony_ci			mask |= 1 << 2;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return snd_interval_list(hw_param_interval(p, rule->var),
39662306a36Sopenharmony_ci				 ARRAY_SIZE(channel_list), channel_list, mask);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int aaci_pcm_open(struct snd_pcm_substream *substream)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
40262306a36Sopenharmony_ci	struct aaci *aaci = substream->private_data;
40362306a36Sopenharmony_ci	struct aaci_runtime *aacirun;
40462306a36Sopenharmony_ci	int ret = 0;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
40762306a36Sopenharmony_ci		aacirun = &aaci->playback;
40862306a36Sopenharmony_ci	} else {
40962306a36Sopenharmony_ci		aacirun = &aaci->capture;
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	aacirun->substream = substream;
41362306a36Sopenharmony_ci	runtime->private_data = aacirun;
41462306a36Sopenharmony_ci	runtime->hw = aaci_hw_info;
41562306a36Sopenharmony_ci	runtime->hw.rates = aacirun->pcm->rates;
41662306a36Sopenharmony_ci	snd_pcm_limit_hw_rates(runtime);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
41962306a36Sopenharmony_ci		runtime->hw.channels_max = 6;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		/* Add rule describing channel dependency. */
42262306a36Sopenharmony_ci		ret = snd_pcm_hw_rule_add(substream->runtime, 0,
42362306a36Sopenharmony_ci					  SNDRV_PCM_HW_PARAM_CHANNELS,
42462306a36Sopenharmony_ci					  aaci_rule_channels, aaci,
42562306a36Sopenharmony_ci					  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
42662306a36Sopenharmony_ci		if (ret)
42762306a36Sopenharmony_ci			return ret;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		if (aacirun->pcm->r[1].slots)
43062306a36Sopenharmony_ci			snd_ac97_pcm_double_rate_rules(runtime);
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	/*
43462306a36Sopenharmony_ci	 * ALSA wants the byte-size of the FIFOs.  As we only support
43562306a36Sopenharmony_ci	 * 16-bit samples, this is twice the FIFO depth irrespective
43662306a36Sopenharmony_ci	 * of whether it's in compact mode or not.
43762306a36Sopenharmony_ci	 */
43862306a36Sopenharmony_ci	runtime->hw.fifo_size = aaci->fifo_depth * 2;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	mutex_lock(&aaci->irq_lock);
44162306a36Sopenharmony_ci	if (!aaci->users++) {
44262306a36Sopenharmony_ci		ret = request_irq(aaci->dev->irq[0], aaci_irq,
44362306a36Sopenharmony_ci			   IRQF_SHARED, DRIVER_NAME, aaci);
44462306a36Sopenharmony_ci		if (ret != 0)
44562306a36Sopenharmony_ci			aaci->users--;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci	mutex_unlock(&aaci->irq_lock);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return ret;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/*
45462306a36Sopenharmony_ci * Common ALSA stuff
45562306a36Sopenharmony_ci */
45662306a36Sopenharmony_cistatic int aaci_pcm_close(struct snd_pcm_substream *substream)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	struct aaci *aaci = substream->private_data;
45962306a36Sopenharmony_ci	struct aaci_runtime *aacirun = substream->runtime->private_data;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	WARN_ON(aacirun->cr & CR_EN);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	aacirun->substream = NULL;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	mutex_lock(&aaci->irq_lock);
46662306a36Sopenharmony_ci	if (!--aaci->users)
46762306a36Sopenharmony_ci		free_irq(aaci->dev->irq[0], aaci);
46862306a36Sopenharmony_ci	mutex_unlock(&aaci->irq_lock);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	return 0;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic int aaci_pcm_hw_free(struct snd_pcm_substream *substream)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct aaci_runtime *aacirun = substream->runtime->private_data;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/*
47862306a36Sopenharmony_ci	 * This must not be called with the device enabled.
47962306a36Sopenharmony_ci	 */
48062306a36Sopenharmony_ci	WARN_ON(aacirun->cr & CR_EN);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (aacirun->pcm_open)
48362306a36Sopenharmony_ci		snd_ac97_pcm_close(aacirun->pcm);
48462306a36Sopenharmony_ci	aacirun->pcm_open = 0;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	return 0;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci/* Channel to slot mask */
49062306a36Sopenharmony_cistatic const u32 channels_to_slotmask[] = {
49162306a36Sopenharmony_ci	[2] = CR_SL3 | CR_SL4,
49262306a36Sopenharmony_ci	[4] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8,
49362306a36Sopenharmony_ci	[6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9,
49462306a36Sopenharmony_ci};
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
49762306a36Sopenharmony_ci			      struct snd_pcm_hw_params *params)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct aaci_runtime *aacirun = substream->runtime->private_data;
50062306a36Sopenharmony_ci	struct aaci *aaci = substream->private_data;
50162306a36Sopenharmony_ci	unsigned int channels = params_channels(params);
50262306a36Sopenharmony_ci	unsigned int rate = params_rate(params);
50362306a36Sopenharmony_ci	int dbl = rate > 48000;
50462306a36Sopenharmony_ci	int err;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	aaci_pcm_hw_free(substream);
50762306a36Sopenharmony_ci	if (aacirun->pcm_open) {
50862306a36Sopenharmony_ci		snd_ac97_pcm_close(aacirun->pcm);
50962306a36Sopenharmony_ci		aacirun->pcm_open = 0;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* channels is already limited to 2, 4, or 6 by aaci_rule_channels */
51362306a36Sopenharmony_ci	if (dbl && channels != 2)
51462306a36Sopenharmony_ci		return -EINVAL;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	err = snd_ac97_pcm_open(aacirun->pcm, rate, channels,
51762306a36Sopenharmony_ci				aacirun->pcm->r[dbl].slots);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	aacirun->pcm_open = err == 0;
52062306a36Sopenharmony_ci	aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
52162306a36Sopenharmony_ci	aacirun->cr |= channels_to_slotmask[channels + dbl * 2];
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/*
52462306a36Sopenharmony_ci	 * fifo_bytes is the number of bytes we transfer to/from
52562306a36Sopenharmony_ci	 * the FIFO, including padding.  So that's x4.  As we're
52662306a36Sopenharmony_ci	 * in compact mode, the FIFO is half the size.
52762306a36Sopenharmony_ci	 */
52862306a36Sopenharmony_ci	aacirun->fifo_bytes = aaci->fifo_depth * 4 / 2;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return err;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic int aaci_pcm_prepare(struct snd_pcm_substream *substream)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
53662306a36Sopenharmony_ci	struct aaci_runtime *aacirun = runtime->private_data;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	aacirun->period	= snd_pcm_lib_period_bytes(substream);
53962306a36Sopenharmony_ci	aacirun->start	= runtime->dma_area;
54062306a36Sopenharmony_ci	aacirun->end	= aacirun->start + snd_pcm_lib_buffer_bytes(substream);
54162306a36Sopenharmony_ci	aacirun->ptr	= aacirun->start;
54262306a36Sopenharmony_ci	aacirun->bytes	= aacirun->period;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return 0;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
55062306a36Sopenharmony_ci	struct aaci_runtime *aacirun = runtime->private_data;
55162306a36Sopenharmony_ci	ssize_t bytes = aacirun->ptr - aacirun->start;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	return bytes_to_frames(runtime, bytes);
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci/*
55862306a36Sopenharmony_ci * Playback specific ALSA stuff
55962306a36Sopenharmony_ci */
56062306a36Sopenharmony_cistatic void aaci_pcm_playback_stop(struct aaci_runtime *aacirun)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	u32 ie;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	ie = readl(aacirun->base + AACI_IE);
56562306a36Sopenharmony_ci	ie &= ~(IE_URIE|IE_TXIE);
56662306a36Sopenharmony_ci	writel(ie, aacirun->base + AACI_IE);
56762306a36Sopenharmony_ci	aacirun->cr &= ~CR_EN;
56862306a36Sopenharmony_ci	aaci_chan_wait_ready(aacirun, SR_TXB);
56962306a36Sopenharmony_ci	writel(aacirun->cr, aacirun->base + AACI_TXCR);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic void aaci_pcm_playback_start(struct aaci_runtime *aacirun)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	u32 ie;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	aaci_chan_wait_ready(aacirun, SR_TXB);
57762306a36Sopenharmony_ci	aacirun->cr |= CR_EN;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	ie = readl(aacirun->base + AACI_IE);
58062306a36Sopenharmony_ci	ie |= IE_URIE | IE_TXIE;
58162306a36Sopenharmony_ci	writel(ie, aacirun->base + AACI_IE);
58262306a36Sopenharmony_ci	writel(aacirun->cr, aacirun->base + AACI_TXCR);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct aaci_runtime *aacirun = substream->runtime->private_data;
58862306a36Sopenharmony_ci	unsigned long flags;
58962306a36Sopenharmony_ci	int ret = 0;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	spin_lock_irqsave(&aacirun->lock, flags);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	switch (cmd) {
59462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
59562306a36Sopenharmony_ci		aaci_pcm_playback_start(aacirun);
59662306a36Sopenharmony_ci		break;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
59962306a36Sopenharmony_ci		aaci_pcm_playback_start(aacirun);
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
60362306a36Sopenharmony_ci		aaci_pcm_playback_stop(aacirun);
60462306a36Sopenharmony_ci		break;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
60762306a36Sopenharmony_ci		aaci_pcm_playback_stop(aacirun);
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
61162306a36Sopenharmony_ci		break;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
61462306a36Sopenharmony_ci		break;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	default:
61762306a36Sopenharmony_ci		ret = -EINVAL;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	spin_unlock_irqrestore(&aacirun->lock, flags);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	return ret;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic const struct snd_pcm_ops aaci_playback_ops = {
62662306a36Sopenharmony_ci	.open		= aaci_pcm_open,
62762306a36Sopenharmony_ci	.close		= aaci_pcm_close,
62862306a36Sopenharmony_ci	.hw_params	= aaci_pcm_hw_params,
62962306a36Sopenharmony_ci	.hw_free	= aaci_pcm_hw_free,
63062306a36Sopenharmony_ci	.prepare	= aaci_pcm_prepare,
63162306a36Sopenharmony_ci	.trigger	= aaci_pcm_playback_trigger,
63262306a36Sopenharmony_ci	.pointer	= aaci_pcm_pointer,
63362306a36Sopenharmony_ci};
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic void aaci_pcm_capture_stop(struct aaci_runtime *aacirun)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	u32 ie;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	aaci_chan_wait_ready(aacirun, SR_RXB);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	ie = readl(aacirun->base + AACI_IE);
64262306a36Sopenharmony_ci	ie &= ~(IE_ORIE | IE_RXIE);
64362306a36Sopenharmony_ci	writel(ie, aacirun->base+AACI_IE);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	aacirun->cr &= ~CR_EN;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	writel(aacirun->cr, aacirun->base + AACI_RXCR);
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_cistatic void aaci_pcm_capture_start(struct aaci_runtime *aacirun)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	u32 ie;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	aaci_chan_wait_ready(aacirun, SR_RXB);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci#ifdef DEBUG
65762306a36Sopenharmony_ci	/* RX Timeout value: bits 28:17 in RXCR */
65862306a36Sopenharmony_ci	aacirun->cr |= 0xf << 17;
65962306a36Sopenharmony_ci#endif
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	aacirun->cr |= CR_EN;
66262306a36Sopenharmony_ci	writel(aacirun->cr, aacirun->base + AACI_RXCR);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	ie = readl(aacirun->base + AACI_IE);
66562306a36Sopenharmony_ci	ie |= IE_ORIE |IE_RXIE; // overrun and rx interrupt -- half full
66662306a36Sopenharmony_ci	writel(ie, aacirun->base + AACI_IE);
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct aaci_runtime *aacirun = substream->runtime->private_data;
67262306a36Sopenharmony_ci	unsigned long flags;
67362306a36Sopenharmony_ci	int ret = 0;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	spin_lock_irqsave(&aacirun->lock, flags);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	switch (cmd) {
67862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
67962306a36Sopenharmony_ci		aaci_pcm_capture_start(aacirun);
68062306a36Sopenharmony_ci		break;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
68362306a36Sopenharmony_ci		aaci_pcm_capture_start(aacirun);
68462306a36Sopenharmony_ci		break;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
68762306a36Sopenharmony_ci		aaci_pcm_capture_stop(aacirun);
68862306a36Sopenharmony_ci		break;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
69162306a36Sopenharmony_ci		aaci_pcm_capture_stop(aacirun);
69262306a36Sopenharmony_ci		break;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
69562306a36Sopenharmony_ci		break;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
69862306a36Sopenharmony_ci		break;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	default:
70162306a36Sopenharmony_ci		ret = -EINVAL;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	spin_unlock_irqrestore(&aacirun->lock, flags);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	return ret;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_cistatic int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
71262306a36Sopenharmony_ci	struct aaci *aaci = substream->private_data;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	aaci_pcm_prepare(substream);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	/* allow changing of sample rate */
71762306a36Sopenharmony_ci	aaci_ac97_write(aaci->ac97, AC97_EXTENDED_STATUS, 0x0001); /* VRA */
71862306a36Sopenharmony_ci	aaci_ac97_write(aaci->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
71962306a36Sopenharmony_ci	aaci_ac97_write(aaci->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	/* Record select: Mic: 0, Aux: 3, Line: 4 */
72262306a36Sopenharmony_ci	aaci_ac97_write(aaci->ac97, AC97_REC_SEL, 0x0404);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	return 0;
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic const struct snd_pcm_ops aaci_capture_ops = {
72862306a36Sopenharmony_ci	.open		= aaci_pcm_open,
72962306a36Sopenharmony_ci	.close		= aaci_pcm_close,
73062306a36Sopenharmony_ci	.hw_params	= aaci_pcm_hw_params,
73162306a36Sopenharmony_ci	.hw_free	= aaci_pcm_hw_free,
73262306a36Sopenharmony_ci	.prepare	= aaci_pcm_capture_prepare,
73362306a36Sopenharmony_ci	.trigger	= aaci_pcm_capture_trigger,
73462306a36Sopenharmony_ci	.pointer	= aaci_pcm_pointer,
73562306a36Sopenharmony_ci};
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci/*
73862306a36Sopenharmony_ci * Power Management.
73962306a36Sopenharmony_ci */
74062306a36Sopenharmony_ci#ifdef CONFIG_PM
74162306a36Sopenharmony_cistatic int aaci_do_suspend(struct snd_card *card)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	struct aaci *aaci = card->private_data;
74462306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
74562306a36Sopenharmony_ci	return 0;
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic int aaci_do_resume(struct snd_card *card)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
75162306a36Sopenharmony_ci	return 0;
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic int aaci_suspend(struct device *dev)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
75762306a36Sopenharmony_ci	return card ? aaci_do_suspend(card) : 0;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic int aaci_resume(struct device *dev)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
76362306a36Sopenharmony_ci	return card ? aaci_do_resume(card) : 0;
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume);
76762306a36Sopenharmony_ci#define AACI_DEV_PM_OPS (&aaci_dev_pm_ops)
76862306a36Sopenharmony_ci#else
76962306a36Sopenharmony_ci#define AACI_DEV_PM_OPS NULL
77062306a36Sopenharmony_ci#endif
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic const struct ac97_pcm ac97_defs[] = {
77462306a36Sopenharmony_ci	[0] = {	/* Front PCM */
77562306a36Sopenharmony_ci		.exclusive = 1,
77662306a36Sopenharmony_ci		.r = {
77762306a36Sopenharmony_ci			[0] = {
77862306a36Sopenharmony_ci				.slots	= (1 << AC97_SLOT_PCM_LEFT) |
77962306a36Sopenharmony_ci					  (1 << AC97_SLOT_PCM_RIGHT) |
78062306a36Sopenharmony_ci					  (1 << AC97_SLOT_PCM_CENTER) |
78162306a36Sopenharmony_ci					  (1 << AC97_SLOT_PCM_SLEFT) |
78262306a36Sopenharmony_ci					  (1 << AC97_SLOT_PCM_SRIGHT) |
78362306a36Sopenharmony_ci					  (1 << AC97_SLOT_LFE),
78462306a36Sopenharmony_ci			},
78562306a36Sopenharmony_ci			[1] = {
78662306a36Sopenharmony_ci				.slots	= (1 << AC97_SLOT_PCM_LEFT) |
78762306a36Sopenharmony_ci					  (1 << AC97_SLOT_PCM_RIGHT) |
78862306a36Sopenharmony_ci					  (1 << AC97_SLOT_PCM_LEFT_0) |
78962306a36Sopenharmony_ci					  (1 << AC97_SLOT_PCM_RIGHT_0),
79062306a36Sopenharmony_ci			},
79162306a36Sopenharmony_ci		},
79262306a36Sopenharmony_ci	},
79362306a36Sopenharmony_ci	[1] = {	/* PCM in */
79462306a36Sopenharmony_ci		.stream = 1,
79562306a36Sopenharmony_ci		.exclusive = 1,
79662306a36Sopenharmony_ci		.r = {
79762306a36Sopenharmony_ci			[0] = {
79862306a36Sopenharmony_ci				.slots	= (1 << AC97_SLOT_PCM_LEFT) |
79962306a36Sopenharmony_ci					  (1 << AC97_SLOT_PCM_RIGHT),
80062306a36Sopenharmony_ci			},
80162306a36Sopenharmony_ci		},
80262306a36Sopenharmony_ci	},
80362306a36Sopenharmony_ci	[2] = {	/* Mic in */
80462306a36Sopenharmony_ci		.stream = 1,
80562306a36Sopenharmony_ci		.exclusive = 1,
80662306a36Sopenharmony_ci		.r = {
80762306a36Sopenharmony_ci			[0] = {
80862306a36Sopenharmony_ci				.slots	= (1 << AC97_SLOT_MIC),
80962306a36Sopenharmony_ci			},
81062306a36Sopenharmony_ci		},
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci};
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic const struct snd_ac97_bus_ops aaci_bus_ops = {
81562306a36Sopenharmony_ci	.write	= aaci_ac97_write,
81662306a36Sopenharmony_ci	.read	= aaci_ac97_read,
81762306a36Sopenharmony_ci};
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic int aaci_probe_ac97(struct aaci *aaci)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	struct snd_ac97_template ac97_template;
82262306a36Sopenharmony_ci	struct snd_ac97_bus *ac97_bus;
82362306a36Sopenharmony_ci	struct snd_ac97 *ac97;
82462306a36Sopenharmony_ci	int ret;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/*
82762306a36Sopenharmony_ci	 * Assert AACIRESET for 2us
82862306a36Sopenharmony_ci	 */
82962306a36Sopenharmony_ci	writel(0, aaci->base + AACI_RESET);
83062306a36Sopenharmony_ci	udelay(2);
83162306a36Sopenharmony_ci	writel(RESET_NRST, aaci->base + AACI_RESET);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	/*
83462306a36Sopenharmony_ci	 * Give the AC'97 codec more than enough time
83562306a36Sopenharmony_ci	 * to wake up. (42us = ~2 frames at 48kHz.)
83662306a36Sopenharmony_ci	 */
83762306a36Sopenharmony_ci	udelay(FRAME_PERIOD_US * 2);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	ret = snd_ac97_bus(aaci->card, 0, &aaci_bus_ops, aaci, &ac97_bus);
84062306a36Sopenharmony_ci	if (ret)
84162306a36Sopenharmony_ci		goto out;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	ac97_bus->clock = 48000;
84462306a36Sopenharmony_ci	aaci->ac97_bus = ac97_bus;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
84762306a36Sopenharmony_ci	ac97_template.private_data = aaci;
84862306a36Sopenharmony_ci	ac97_template.num = 0;
84962306a36Sopenharmony_ci	ac97_template.scaps = AC97_SCAP_SKIP_MODEM;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
85262306a36Sopenharmony_ci	if (ret)
85362306a36Sopenharmony_ci		goto out;
85462306a36Sopenharmony_ci	aaci->ac97 = ac97;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	/*
85762306a36Sopenharmony_ci	 * Disable AC97 PC Beep input on audio codecs.
85862306a36Sopenharmony_ci	 */
85962306a36Sopenharmony_ci	if (ac97_is_audio(ac97))
86062306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x801e);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	ret = snd_ac97_pcm_assign(ac97_bus, ARRAY_SIZE(ac97_defs), ac97_defs);
86362306a36Sopenharmony_ci	if (ret)
86462306a36Sopenharmony_ci		goto out;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	aaci->playback.pcm = &ac97_bus->pcms[0];
86762306a36Sopenharmony_ci	aaci->capture.pcm  = &ac97_bus->pcms[1];
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci out:
87062306a36Sopenharmony_ci	return ret;
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic void aaci_free_card(struct snd_card *card)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	struct aaci *aaci = card->private_data;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	iounmap(aaci->base);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic struct aaci *aaci_init_card(struct amba_device *dev)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	struct aaci *aaci;
88362306a36Sopenharmony_ci	struct snd_card *card;
88462306a36Sopenharmony_ci	int err;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	err = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
88762306a36Sopenharmony_ci			   THIS_MODULE, sizeof(struct aaci), &card);
88862306a36Sopenharmony_ci	if (err < 0)
88962306a36Sopenharmony_ci		return NULL;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	card->private_free = aaci_free_card;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
89462306a36Sopenharmony_ci	strscpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));
89562306a36Sopenharmony_ci	snprintf(card->longname, sizeof(card->longname),
89662306a36Sopenharmony_ci		 "%s PL%03x rev%u at 0x%08llx, irq %d",
89762306a36Sopenharmony_ci		 card->shortname, amba_part(dev), amba_rev(dev),
89862306a36Sopenharmony_ci		 (unsigned long long)dev->res.start, dev->irq[0]);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	aaci = card->private_data;
90162306a36Sopenharmony_ci	mutex_init(&aaci->ac97_sem);
90262306a36Sopenharmony_ci	mutex_init(&aaci->irq_lock);
90362306a36Sopenharmony_ci	aaci->card = card;
90462306a36Sopenharmony_ci	aaci->dev = dev;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/* Set MAINCR to allow slot 1 and 2 data IO */
90762306a36Sopenharmony_ci	aaci->maincr = MAINCR_IE | MAINCR_SL1RXEN | MAINCR_SL1TXEN |
90862306a36Sopenharmony_ci		       MAINCR_SL2RXEN | MAINCR_SL2TXEN;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	return aaci;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic int aaci_init_pcm(struct aaci *aaci)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct snd_pcm *pcm;
91662306a36Sopenharmony_ci	int ret;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 1, &pcm);
91962306a36Sopenharmony_ci	if (ret == 0) {
92062306a36Sopenharmony_ci		aaci->pcm = pcm;
92162306a36Sopenharmony_ci		pcm->private_data = aaci;
92262306a36Sopenharmony_ci		pcm->info_flags = 0;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci		strscpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
92762306a36Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
92862306a36Sopenharmony_ci		snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
92962306a36Sopenharmony_ci					       aaci->card->dev,
93062306a36Sopenharmony_ci					       0, 64 * 1024);
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	return ret;
93462306a36Sopenharmony_ci}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_cistatic unsigned int aaci_size_fifo(struct aaci *aaci)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	struct aaci_runtime *aacirun = &aaci->playback;
93962306a36Sopenharmony_ci	int i;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	/*
94262306a36Sopenharmony_ci	 * Enable the channel, but don't assign it to any slots, so
94362306a36Sopenharmony_ci	 * it won't empty onto the AC'97 link.
94462306a36Sopenharmony_ci	 */
94562306a36Sopenharmony_ci	writel(CR_FEN | CR_SZ16 | CR_EN, aacirun->base + AACI_TXCR);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	for (i = 0; !(readl(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++)
94862306a36Sopenharmony_ci		writel(0, aacirun->fifo);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	writel(0, aacirun->base + AACI_TXCR);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/*
95362306a36Sopenharmony_ci	 * Re-initialise the AACI after the FIFO depth test, to
95462306a36Sopenharmony_ci	 * ensure that the FIFOs are empty.  Unfortunately, merely
95562306a36Sopenharmony_ci	 * disabling the channel doesn't clear the FIFO.
95662306a36Sopenharmony_ci	 */
95762306a36Sopenharmony_ci	writel(aaci->maincr & ~MAINCR_IE, aaci->base + AACI_MAINCR);
95862306a36Sopenharmony_ci	readl(aaci->base + AACI_MAINCR);
95962306a36Sopenharmony_ci	udelay(1);
96062306a36Sopenharmony_ci	writel(aaci->maincr, aaci->base + AACI_MAINCR);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	/*
96362306a36Sopenharmony_ci	 * If we hit 4096 entries, we failed.  Go back to the specified
96462306a36Sopenharmony_ci	 * fifo depth.
96562306a36Sopenharmony_ci	 */
96662306a36Sopenharmony_ci	if (i == 4096)
96762306a36Sopenharmony_ci		i = 8;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	return i;
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic int aaci_probe(struct amba_device *dev,
97362306a36Sopenharmony_ci		      const struct amba_id *id)
97462306a36Sopenharmony_ci{
97562306a36Sopenharmony_ci	struct aaci *aaci;
97662306a36Sopenharmony_ci	int ret, i;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	ret = amba_request_regions(dev, NULL);
97962306a36Sopenharmony_ci	if (ret)
98062306a36Sopenharmony_ci		return ret;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	aaci = aaci_init_card(dev);
98362306a36Sopenharmony_ci	if (!aaci) {
98462306a36Sopenharmony_ci		ret = -ENOMEM;
98562306a36Sopenharmony_ci		goto out;
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	aaci->base = ioremap(dev->res.start, resource_size(&dev->res));
98962306a36Sopenharmony_ci	if (!aaci->base) {
99062306a36Sopenharmony_ci		ret = -ENOMEM;
99162306a36Sopenharmony_ci		goto out;
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	/*
99562306a36Sopenharmony_ci	 * Playback uses AACI channel 0
99662306a36Sopenharmony_ci	 */
99762306a36Sopenharmony_ci	spin_lock_init(&aaci->playback.lock);
99862306a36Sopenharmony_ci	aaci->playback.base = aaci->base + AACI_CSCH1;
99962306a36Sopenharmony_ci	aaci->playback.fifo = aaci->base + AACI_DR1;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	/*
100262306a36Sopenharmony_ci	 * Capture uses AACI channel 0
100362306a36Sopenharmony_ci	 */
100462306a36Sopenharmony_ci	spin_lock_init(&aaci->capture.lock);
100562306a36Sopenharmony_ci	aaci->capture.base = aaci->base + AACI_CSCH1;
100662306a36Sopenharmony_ci	aaci->capture.fifo = aaci->base + AACI_DR1;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
100962306a36Sopenharmony_ci		void __iomem *base = aaci->base + i * 0x14;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		writel(0, base + AACI_IE);
101262306a36Sopenharmony_ci		writel(0, base + AACI_TXCR);
101362306a36Sopenharmony_ci		writel(0, base + AACI_RXCR);
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	writel(0x1fff, aaci->base + AACI_INTCLR);
101762306a36Sopenharmony_ci	writel(aaci->maincr, aaci->base + AACI_MAINCR);
101862306a36Sopenharmony_ci	/*
101962306a36Sopenharmony_ci	 * Fix: ac97 read back fail errors by reading
102062306a36Sopenharmony_ci	 * from any arbitrary aaci register.
102162306a36Sopenharmony_ci	 */
102262306a36Sopenharmony_ci	readl(aaci->base + AACI_CSCH1);
102362306a36Sopenharmony_ci	ret = aaci_probe_ac97(aaci);
102462306a36Sopenharmony_ci	if (ret)
102562306a36Sopenharmony_ci		goto out;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	/*
102862306a36Sopenharmony_ci	 * Size the FIFOs (must be multiple of 16).
102962306a36Sopenharmony_ci	 * This is the number of entries in the FIFO.
103062306a36Sopenharmony_ci	 */
103162306a36Sopenharmony_ci	aaci->fifo_depth = aaci_size_fifo(aaci);
103262306a36Sopenharmony_ci	if (aaci->fifo_depth & 15) {
103362306a36Sopenharmony_ci		printk(KERN_WARNING "AACI: FIFO depth %d not supported\n",
103462306a36Sopenharmony_ci		       aaci->fifo_depth);
103562306a36Sopenharmony_ci		ret = -ENODEV;
103662306a36Sopenharmony_ci		goto out;
103762306a36Sopenharmony_ci	}
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	ret = aaci_init_pcm(aaci);
104062306a36Sopenharmony_ci	if (ret)
104162306a36Sopenharmony_ci		goto out;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	ret = snd_card_register(aaci->card);
104462306a36Sopenharmony_ci	if (ret == 0) {
104562306a36Sopenharmony_ci		dev_info(&dev->dev, "%s\n", aaci->card->longname);
104662306a36Sopenharmony_ci		dev_info(&dev->dev, "FIFO %u entries\n", aaci->fifo_depth);
104762306a36Sopenharmony_ci		amba_set_drvdata(dev, aaci->card);
104862306a36Sopenharmony_ci		return ret;
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci out:
105262306a36Sopenharmony_ci	if (aaci)
105362306a36Sopenharmony_ci		snd_card_free(aaci->card);
105462306a36Sopenharmony_ci	amba_release_regions(dev);
105562306a36Sopenharmony_ci	return ret;
105662306a36Sopenharmony_ci}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_cistatic void aaci_remove(struct amba_device *dev)
105962306a36Sopenharmony_ci{
106062306a36Sopenharmony_ci	struct snd_card *card = amba_get_drvdata(dev);
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	if (card) {
106362306a36Sopenharmony_ci		struct aaci *aaci = card->private_data;
106462306a36Sopenharmony_ci		writel(0, aaci->base + AACI_MAINCR);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci		snd_card_free(card);
106762306a36Sopenharmony_ci		amba_release_regions(dev);
106862306a36Sopenharmony_ci	}
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_cistatic struct amba_id aaci_ids[] = {
107262306a36Sopenharmony_ci	{
107362306a36Sopenharmony_ci		.id	= 0x00041041,
107462306a36Sopenharmony_ci		.mask	= 0x000fffff,
107562306a36Sopenharmony_ci	},
107662306a36Sopenharmony_ci	{ 0, 0 },
107762306a36Sopenharmony_ci};
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(amba, aaci_ids);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_cistatic struct amba_driver aaci_driver = {
108262306a36Sopenharmony_ci	.drv		= {
108362306a36Sopenharmony_ci		.name	= DRIVER_NAME,
108462306a36Sopenharmony_ci		.pm	= AACI_DEV_PM_OPS,
108562306a36Sopenharmony_ci	},
108662306a36Sopenharmony_ci	.probe		= aaci_probe,
108762306a36Sopenharmony_ci	.remove		= aaci_remove,
108862306a36Sopenharmony_ci	.id_table	= aaci_ids,
108962306a36Sopenharmony_ci};
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cimodule_amba_driver(aaci_driver);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
109462306a36Sopenharmony_ciMODULE_DESCRIPTION("ARM PrimeCell PL041 Advanced Audio CODEC Interface driver");
1095