1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cb5a6ffcSRussell King /*
3cb5a6ffcSRussell King * linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver
4cb5a6ffcSRussell King *
5cb5a6ffcSRussell King * Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
6cb5a6ffcSRussell King *
7cb5a6ffcSRussell King * Documentation: ARM DDI 0173B
8cb5a6ffcSRussell King */
9cb5a6ffcSRussell King #include <linux/module.h>
10cb5a6ffcSRussell King #include <linux/delay.h>
11cb5a6ffcSRussell King #include <linux/init.h>
12cb5a6ffcSRussell King #include <linux/ioport.h>
13cb5a6ffcSRussell King #include <linux/device.h>
14cb5a6ffcSRussell King #include <linux/spinlock.h>
15cb5a6ffcSRussell King #include <linux/interrupt.h>
16cb5a6ffcSRussell King #include <linux/err.h>
17a62c80e5SRussell King #include <linux/amba/bus.h>
1888cdca9cSRussell King #include <linux/io.h>
19cb5a6ffcSRussell King
20cb5a6ffcSRussell King #include <sound/core.h>
21cb5a6ffcSRussell King #include <sound/initval.h>
22cb5a6ffcSRussell King #include <sound/ac97_codec.h>
23cb5a6ffcSRussell King #include <sound/pcm.h>
24cb5a6ffcSRussell King #include <sound/pcm_params.h>
25cb5a6ffcSRussell King
26cb5a6ffcSRussell King #include "aaci.h"
27cb5a6ffcSRussell King
28cb5a6ffcSRussell King #define DRIVER_NAME "aaci-pl041"
29cb5a6ffcSRussell King
30250c7a61SRussell King #define FRAME_PERIOD_US 21
31250c7a61SRussell King
32cb5a6ffcSRussell King /*
33cb5a6ffcSRussell King * PM support is not complete. Turn it off.
34cb5a6ffcSRussell King */
35cb5a6ffcSRussell King #undef CONFIG_PM
36cb5a6ffcSRussell King
aaci_ac97_select_codec(struct aaci * aaci,struct snd_ac97 * ac97)37ceb9e476STakashi Iwai static void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *ac97)
38cb5a6ffcSRussell King {
39cb5a6ffcSRussell King u32 v, maincr = aaci->maincr | MAINCR_SCRA(ac97->num);
40cb5a6ffcSRussell King
41cb5a6ffcSRussell King /*
42cb5a6ffcSRussell King * Ensure that the slot 1/2 RX registers are empty.
43cb5a6ffcSRussell King */
44cb5a6ffcSRussell King v = readl(aaci->base + AACI_SLFR);
45cb5a6ffcSRussell King if (v & SLFR_2RXV)
46cb5a6ffcSRussell King readl(aaci->base + AACI_SL2RX);
47cb5a6ffcSRussell King if (v & SLFR_1RXV)
48cb5a6ffcSRussell King readl(aaci->base + AACI_SL1RX);
49cb5a6ffcSRussell King
507c289385SRussell King if (maincr != readl(aaci->base + AACI_MAINCR)) {
51cb5a6ffcSRussell King writel(maincr, aaci->base + AACI_MAINCR);
527c289385SRussell King readl(aaci->base + AACI_MAINCR);
537c289385SRussell King udelay(1);
547c289385SRussell King }
55cb5a6ffcSRussell King }
56cb5a6ffcSRussell King
57cb5a6ffcSRussell King /*
58cb5a6ffcSRussell King * P29:
59cb5a6ffcSRussell King * The recommended use of programming the external codec through slot 1
60cb5a6ffcSRussell King * and slot 2 data is to use the channels during setup routines and the
61cb5a6ffcSRussell King * slot register at any other time. The data written into slot 1, slot 2
62cb5a6ffcSRussell King * and slot 12 registers is transmitted only when their corresponding
63cb5a6ffcSRussell King * SI1TxEn, SI2TxEn and SI12TxEn bits are set in the AACI_MAINCR
64cb5a6ffcSRussell King * register.
65cb5a6ffcSRussell King */
aaci_ac97_write(struct snd_ac97 * ac97,unsigned short reg,unsigned short val)6614d178a1SKevin Hilman static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
6714d178a1SKevin Hilman unsigned short val)
68cb5a6ffcSRussell King {
69cb5a6ffcSRussell King struct aaci *aaci = ac97->private_data;
70250c7a61SRussell King int timeout;
71cb5a6ffcSRussell King u32 v;
72cb5a6ffcSRussell King
73cb5a6ffcSRussell King if (ac97->num >= 4)
74cb5a6ffcSRussell King return;
75cb5a6ffcSRussell King
7612aa7579SIngo Molnar mutex_lock(&aaci->ac97_sem);
77cb5a6ffcSRussell King
78cb5a6ffcSRussell King aaci_ac97_select_codec(aaci, ac97);
79cb5a6ffcSRussell King
80cb5a6ffcSRussell King /*
81cb5a6ffcSRussell King * P54: You must ensure that AACI_SL2TX is always written
82cb5a6ffcSRussell King * to, if required, before data is written to AACI_SL1TX.
83cb5a6ffcSRussell King */
84cb5a6ffcSRussell King writel(val << 4, aaci->base + AACI_SL2TX);
85cb5a6ffcSRussell King writel(reg << 12, aaci->base + AACI_SL1TX);
86cb5a6ffcSRussell King
87250c7a61SRussell King /* Initially, wait one frame period */
88250c7a61SRussell King udelay(FRAME_PERIOD_US);
89250c7a61SRussell King
90250c7a61SRussell King /* And then wait an additional eight frame periods for it to be sent */
91250c7a61SRussell King timeout = FRAME_PERIOD_US * 8;
92cb5a6ffcSRussell King do {
93250c7a61SRussell King udelay(1);
94cb5a6ffcSRussell King v = readl(aaci->base + AACI_SLFR);
95f6f35bbeSRoel Kluin } while ((v & (SLFR_1TXB|SLFR_2TXB)) && --timeout);
9614d178a1SKevin Hilman
9769058cd6SRussell King if (v & (SLFR_1TXB|SLFR_2TXB))
9814d178a1SKevin Hilman dev_err(&aaci->dev->dev,
9914d178a1SKevin Hilman "timeout waiting for write to complete\n");
100cb5a6ffcSRussell King
10112aa7579SIngo Molnar mutex_unlock(&aaci->ac97_sem);
102cb5a6ffcSRussell King }
103cb5a6ffcSRussell King
104cb5a6ffcSRussell King /*
105cb5a6ffcSRussell King * Read an AC'97 register.
106cb5a6ffcSRussell King */
aaci_ac97_read(struct snd_ac97 * ac97,unsigned short reg)107ceb9e476STakashi Iwai static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
108cb5a6ffcSRussell King {
109cb5a6ffcSRussell King struct aaci *aaci = ac97->private_data;
110250c7a61SRussell King int timeout, retries = 10;
111cb5a6ffcSRussell King u32 v;
112cb5a6ffcSRussell King
113cb5a6ffcSRussell King if (ac97->num >= 4)
114cb5a6ffcSRussell King return ~0;
115cb5a6ffcSRussell King
11612aa7579SIngo Molnar mutex_lock(&aaci->ac97_sem);
117cb5a6ffcSRussell King
118cb5a6ffcSRussell King aaci_ac97_select_codec(aaci, ac97);
119cb5a6ffcSRussell King
120cb5a6ffcSRussell King /*
121cb5a6ffcSRussell King * Write the register address to slot 1.
122cb5a6ffcSRussell King */
123cb5a6ffcSRussell King writel((reg << 12) | (1 << 19), aaci->base + AACI_SL1TX);
124cb5a6ffcSRussell King
125250c7a61SRussell King /* Initially, wait one frame period */
126250c7a61SRussell King udelay(FRAME_PERIOD_US);
127250c7a61SRussell King
128250c7a61SRussell King /* And then wait an additional eight frame periods for it to be sent */
129250c7a61SRussell King timeout = FRAME_PERIOD_US * 8;
130cb5a6ffcSRussell King do {
131250c7a61SRussell King udelay(1);
132cb5a6ffcSRussell King v = readl(aaci->base + AACI_SLFR);
133f6f35bbeSRoel Kluin } while ((v & SLFR_1TXB) && --timeout);
13414d178a1SKevin Hilman
13569058cd6SRussell King if (v & SLFR_1TXB) {
13614d178a1SKevin Hilman dev_err(&aaci->dev->dev, "timeout on slot 1 TX busy\n");
13714d178a1SKevin Hilman v = ~0;
13814d178a1SKevin Hilman goto out;
13914d178a1SKevin Hilman }
140cb5a6ffcSRussell King
141250c7a61SRussell King /* Now wait for the response frame */
142250c7a61SRussell King udelay(FRAME_PERIOD_US);
143cb5a6ffcSRussell King
144250c7a61SRussell King /* And then wait an additional eight frame periods for data */
145250c7a61SRussell King timeout = FRAME_PERIOD_US * 8;
146cb5a6ffcSRussell King do {
147250c7a61SRussell King udelay(1);
148cb5a6ffcSRussell King cond_resched();
149cb5a6ffcSRussell King v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV);
150f6f35bbeSRoel Kluin } while ((v != (SLFR_1RXV|SLFR_2RXV)) && --timeout);
151cb5a6ffcSRussell King
15269058cd6SRussell King if (v != (SLFR_1RXV|SLFR_2RXV)) {
15314d178a1SKevin Hilman dev_err(&aaci->dev->dev, "timeout on RX valid\n");
15414d178a1SKevin Hilman v = ~0;
15514d178a1SKevin Hilman goto out;
15614d178a1SKevin Hilman }
15714d178a1SKevin Hilman
15814d178a1SKevin Hilman do {
159cb5a6ffcSRussell King v = readl(aaci->base + AACI_SL1RX) >> 12;
160cb5a6ffcSRussell King if (v == reg) {
161cb5a6ffcSRussell King v = readl(aaci->base + AACI_SL2RX) >> 4;
16214d178a1SKevin Hilman break;
16314d178a1SKevin Hilman } else if (--retries) {
16414d178a1SKevin Hilman dev_warn(&aaci->dev->dev,
16514d178a1SKevin Hilman "ac97 read back fail. retry\n");
16614d178a1SKevin Hilman continue;
167cb5a6ffcSRussell King } else {
16814d178a1SKevin Hilman dev_warn(&aaci->dev->dev,
169cb5a6ffcSRussell King "wrong ac97 register read back (%x != %x)\n",
170cb5a6ffcSRussell King v, reg);
171cb5a6ffcSRussell King v = ~0;
172cb5a6ffcSRussell King }
17314d178a1SKevin Hilman } while (retries);
17414d178a1SKevin Hilman out:
17512aa7579SIngo Molnar mutex_unlock(&aaci->ac97_sem);
176cb5a6ffcSRussell King return v;
177cb5a6ffcSRussell King }
178cb5a6ffcSRussell King
179d6a89fefSRussell King static inline void
aaci_chan_wait_ready(struct aaci_runtime * aacirun,unsigned long mask)180d6a89fefSRussell King aaci_chan_wait_ready(struct aaci_runtime *aacirun, unsigned long mask)
181cb5a6ffcSRussell King {
182cb5a6ffcSRussell King u32 val;
183cb5a6ffcSRussell King int timeout = 5000;
184cb5a6ffcSRussell King
185cb5a6ffcSRussell King do {
186250c7a61SRussell King udelay(1);
187cb5a6ffcSRussell King val = readl(aacirun->base + AACI_SR);
188d6a89fefSRussell King } while (val & mask && timeout--);
189cb5a6ffcSRussell King }
190cb5a6ffcSRussell King
191cb5a6ffcSRussell King
192cb5a6ffcSRussell King
193cb5a6ffcSRussell King /*
194cb5a6ffcSRussell King * Interrupt support.
195cb5a6ffcSRussell King */
aaci_fifo_irq(struct aaci * aaci,int channel,u32 mask)19662578cbfSKevin Hilman static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
197cb5a6ffcSRussell King {
19841762b8cSKevin Hilman if (mask & ISR_ORINTR) {
19941762b8cSKevin Hilman dev_warn(&aaci->dev->dev, "RX overrun on chan %d\n", channel);
20041762b8cSKevin Hilman writel(ICLR_RXOEC1 << channel, aaci->base + AACI_INTCLR);
20141762b8cSKevin Hilman }
20241762b8cSKevin Hilman
20341762b8cSKevin Hilman if (mask & ISR_RXTOINTR) {
20441762b8cSKevin Hilman dev_warn(&aaci->dev->dev, "RX timeout on chan %d\n", channel);
20541762b8cSKevin Hilman writel(ICLR_RXTOFEC1 << channel, aaci->base + AACI_INTCLR);
20641762b8cSKevin Hilman }
20741762b8cSKevin Hilman
20841762b8cSKevin Hilman if (mask & ISR_RXINTR) {
20941762b8cSKevin Hilman struct aaci_runtime *aacirun = &aaci->capture;
210ea51d0b1SRussell King bool period_elapsed = false;
21141762b8cSKevin Hilman void *ptr;
21241762b8cSKevin Hilman
21341762b8cSKevin Hilman if (!aacirun->substream || !aacirun->start) {
214898eb71cSJoe Perches dev_warn(&aaci->dev->dev, "RX interrupt???\n");
21541762b8cSKevin Hilman writel(0, aacirun->base + AACI_IE);
21641762b8cSKevin Hilman return;
21741762b8cSKevin Hilman }
21841762b8cSKevin Hilman
219d6a89fefSRussell King spin_lock(&aacirun->lock);
220d6a89fefSRussell King
221d6a89fefSRussell King ptr = aacirun->ptr;
22241762b8cSKevin Hilman do {
2235d350cbaSRussell King unsigned int len = aacirun->fifo_bytes;
22441762b8cSKevin Hilman u32 val;
22541762b8cSKevin Hilman
22641762b8cSKevin Hilman if (aacirun->bytes <= 0) {
22741762b8cSKevin Hilman aacirun->bytes += aacirun->period;
228ea51d0b1SRussell King period_elapsed = true;
22941762b8cSKevin Hilman }
23041762b8cSKevin Hilman if (!(aacirun->cr & CR_EN))
23141762b8cSKevin Hilman break;
23241762b8cSKevin Hilman
23341762b8cSKevin Hilman val = readl(aacirun->base + AACI_SR);
23441762b8cSKevin Hilman if (!(val & SR_RXHF))
23541762b8cSKevin Hilman break;
23641762b8cSKevin Hilman if (!(val & SR_RXFF))
23741762b8cSKevin Hilman len >>= 1;
23841762b8cSKevin Hilman
23941762b8cSKevin Hilman aacirun->bytes -= len;
24041762b8cSKevin Hilman
24141762b8cSKevin Hilman /* reading 16 bytes at a time */
24241762b8cSKevin Hilman for( ; len > 0; len -= 16) {
24341762b8cSKevin Hilman asm(
24441762b8cSKevin Hilman "ldmia %1, {r0, r1, r2, r3}\n\t"
24541762b8cSKevin Hilman "stmia %0!, {r0, r1, r2, r3}"
24641762b8cSKevin Hilman : "+r" (ptr)
24741762b8cSKevin Hilman : "r" (aacirun->fifo)
24841762b8cSKevin Hilman : "r0", "r1", "r2", "r3", "cc");
24941762b8cSKevin Hilman
25041762b8cSKevin Hilman if (ptr >= aacirun->end)
25141762b8cSKevin Hilman ptr = aacirun->start;
25241762b8cSKevin Hilman }
25341762b8cSKevin Hilman } while(1);
254d6a89fefSRussell King
25541762b8cSKevin Hilman aacirun->ptr = ptr;
256d6a89fefSRussell King
257d6a89fefSRussell King spin_unlock(&aacirun->lock);
258ea51d0b1SRussell King
259ea51d0b1SRussell King if (period_elapsed)
260ea51d0b1SRussell King snd_pcm_period_elapsed(aacirun->substream);
26141762b8cSKevin Hilman }
26241762b8cSKevin Hilman
263cb5a6ffcSRussell King if (mask & ISR_URINTR) {
26462578cbfSKevin Hilman dev_dbg(&aaci->dev->dev, "TX underrun on chan %d\n", channel);
26562578cbfSKevin Hilman writel(ICLR_TXUEC1 << channel, aaci->base + AACI_INTCLR);
266cb5a6ffcSRussell King }
267cb5a6ffcSRussell King
268cb5a6ffcSRussell King if (mask & ISR_TXINTR) {
269cb5a6ffcSRussell King struct aaci_runtime *aacirun = &aaci->playback;
270ea51d0b1SRussell King bool period_elapsed = false;
271cb5a6ffcSRussell King void *ptr;
272cb5a6ffcSRussell King
273cb5a6ffcSRussell King if (!aacirun->substream || !aacirun->start) {
274898eb71cSJoe Perches dev_warn(&aaci->dev->dev, "TX interrupt???\n");
275cb5a6ffcSRussell King writel(0, aacirun->base + AACI_IE);
276cb5a6ffcSRussell King return;
277cb5a6ffcSRussell King }
278cb5a6ffcSRussell King
279d6a89fefSRussell King spin_lock(&aacirun->lock);
280d6a89fefSRussell King
281cb5a6ffcSRussell King ptr = aacirun->ptr;
282cb5a6ffcSRussell King do {
2835d350cbaSRussell King unsigned int len = aacirun->fifo_bytes;
284cb5a6ffcSRussell King u32 val;
285cb5a6ffcSRussell King
286cb5a6ffcSRussell King if (aacirun->bytes <= 0) {
287cb5a6ffcSRussell King aacirun->bytes += aacirun->period;
288ea51d0b1SRussell King period_elapsed = true;
289cb5a6ffcSRussell King }
29041762b8cSKevin Hilman if (!(aacirun->cr & CR_EN))
291cb5a6ffcSRussell King break;
292cb5a6ffcSRussell King
293cb5a6ffcSRussell King val = readl(aacirun->base + AACI_SR);
294cb5a6ffcSRussell King if (!(val & SR_TXHE))
295cb5a6ffcSRussell King break;
296cb5a6ffcSRussell King if (!(val & SR_TXFE))
297cb5a6ffcSRussell King len >>= 1;
298cb5a6ffcSRussell King
299cb5a6ffcSRussell King aacirun->bytes -= len;
300cb5a6ffcSRussell King
301cb5a6ffcSRussell King /* writing 16 bytes at a time */
302cb5a6ffcSRussell King for ( ; len > 0; len -= 16) {
303cb5a6ffcSRussell King asm(
304cb5a6ffcSRussell King "ldmia %0!, {r0, r1, r2, r3}\n\t"
305cb5a6ffcSRussell King "stmia %1, {r0, r1, r2, r3}"
306cb5a6ffcSRussell King : "+r" (ptr)
307cb5a6ffcSRussell King : "r" (aacirun->fifo)
308cb5a6ffcSRussell King : "r0", "r1", "r2", "r3", "cc");
309cb5a6ffcSRussell King
310cb5a6ffcSRussell King if (ptr >= aacirun->end)
311cb5a6ffcSRussell King ptr = aacirun->start;
312cb5a6ffcSRussell King }
313cb5a6ffcSRussell King } while (1);
314cb5a6ffcSRussell King
315cb5a6ffcSRussell King aacirun->ptr = ptr;
316d6a89fefSRussell King
317d6a89fefSRussell King spin_unlock(&aacirun->lock);
318ea51d0b1SRussell King
319ea51d0b1SRussell King if (period_elapsed)
320ea51d0b1SRussell King snd_pcm_period_elapsed(aacirun->substream);
321cb5a6ffcSRussell King }
322cb5a6ffcSRussell King }
323cb5a6ffcSRussell King
aaci_irq(int irq,void * devid)3247d12e780SDavid Howells static irqreturn_t aaci_irq(int irq, void *devid)
325cb5a6ffcSRussell King {
326cb5a6ffcSRussell King struct aaci *aaci = devid;
327cb5a6ffcSRussell King u32 mask;
328cb5a6ffcSRussell King int i;
329cb5a6ffcSRussell King
330cb5a6ffcSRussell King mask = readl(aaci->base + AACI_ALLINTS);
331cb5a6ffcSRussell King if (mask) {
332cb5a6ffcSRussell King u32 m = mask;
333cb5a6ffcSRussell King for (i = 0; i < 4; i++, m >>= 7) {
334cb5a6ffcSRussell King if (m & 0x7f) {
33562578cbfSKevin Hilman aaci_fifo_irq(aaci, i, m);
336cb5a6ffcSRussell King }
337cb5a6ffcSRussell King }
338cb5a6ffcSRussell King }
339cb5a6ffcSRussell King
340cb5a6ffcSRussell King return mask ? IRQ_HANDLED : IRQ_NONE;
341cb5a6ffcSRussell King }
342cb5a6ffcSRussell King
343cb5a6ffcSRussell King
344cb5a6ffcSRussell King
345cb5a6ffcSRussell King /*
346cb5a6ffcSRussell King * ALSA support.
347cb5a6ffcSRussell King */
348db566fb9SBhumika Goyal static const struct snd_pcm_hardware aaci_hw_info = {
349cb5a6ffcSRussell King .info = SNDRV_PCM_INFO_MMAP |
350cb5a6ffcSRussell King SNDRV_PCM_INFO_MMAP_VALID |
351cb5a6ffcSRussell King SNDRV_PCM_INFO_INTERLEAVED |
352cb5a6ffcSRussell King SNDRV_PCM_INFO_BLOCK_TRANSFER |
353cb5a6ffcSRussell King SNDRV_PCM_INFO_RESUME,
354cb5a6ffcSRussell King
355cb5a6ffcSRussell King /*
356cb5a6ffcSRussell King * ALSA doesn't support 18-bit or 20-bit packed into 32-bit
357cb5a6ffcSRussell King * words. It also doesn't support 12-bit at all.
358cb5a6ffcSRussell King */
359cb5a6ffcSRussell King .formats = SNDRV_PCM_FMTBIT_S16_LE,
360cb5a6ffcSRussell King
3616ca867c8SRussell King /* rates are setup from the AC'97 codec */
362cb5a6ffcSRussell King .channels_min = 2,
363e831d80bSRussell King .channels_max = 2,
364cb5a6ffcSRussell King .buffer_bytes_max = 64 * 1024,
365cb5a6ffcSRussell King .period_bytes_min = 256,
366cb5a6ffcSRussell King .period_bytes_max = PAGE_SIZE,
367cb5a6ffcSRussell King .periods_min = 4,
368cb5a6ffcSRussell King .periods_max = PAGE_SIZE / 16,
369cb5a6ffcSRussell King };
370cb5a6ffcSRussell King
371e831d80bSRussell King /*
372e831d80bSRussell King * We can support two and four channel audio. Unfortunately
373e831d80bSRussell King * six channel audio requires a non-standard channel ordering:
374e831d80bSRussell King * 2 -> FL(3), FR(4)
375e831d80bSRussell King * 4 -> FL(3), FR(4), SL(7), SR(8)
376e831d80bSRussell King * 6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
377e831d80bSRussell King * FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
378e831d80bSRussell King * This requires an ALSA configuration file to correct.
379e831d80bSRussell King */
aaci_rule_channels(struct snd_pcm_hw_params * p,struct snd_pcm_hw_rule * rule)380e831d80bSRussell King static int aaci_rule_channels(struct snd_pcm_hw_params *p,
381e831d80bSRussell King struct snd_pcm_hw_rule *rule)
382e831d80bSRussell King {
3837840d8a1STakashi Iwai static const unsigned int channel_list[] = { 2, 4, 6 };
384e831d80bSRussell King struct aaci *aaci = rule->private;
385e831d80bSRussell King unsigned int mask = 1 << 0, slots;
386e831d80bSRussell King
387e831d80bSRussell King /* pcms[0] is the our 5.1 PCM instance. */
388e831d80bSRussell King slots = aaci->ac97_bus->pcms[0].r[0].slots;
389e831d80bSRussell King if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
390e831d80bSRussell King mask |= 1 << 1;
391e831d80bSRussell King if (slots & (1 << AC97_SLOT_LFE))
392e831d80bSRussell King mask |= 1 << 2;
393e831d80bSRussell King }
394e831d80bSRussell King
395e831d80bSRussell King return snd_interval_list(hw_param_interval(p, rule->var),
396e831d80bSRussell King ARRAY_SIZE(channel_list), channel_list, mask);
397e831d80bSRussell King }
398e831d80bSRussell King
aaci_pcm_open(struct snd_pcm_substream * substream)399e831d80bSRussell King static int aaci_pcm_open(struct snd_pcm_substream *substream)
400cb5a6ffcSRussell King {
401ceb9e476STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
402e831d80bSRussell King struct aaci *aaci = substream->private_data;
403e831d80bSRussell King struct aaci_runtime *aacirun;
404b60fb519SRussell King int ret = 0;
405cb5a6ffcSRussell King
406e831d80bSRussell King if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
407e831d80bSRussell King aacirun = &aaci->playback;
408e831d80bSRussell King } else {
409e831d80bSRussell King aacirun = &aaci->capture;
410e831d80bSRussell King }
411e831d80bSRussell King
412cb5a6ffcSRussell King aacirun->substream = substream;
413cb5a6ffcSRussell King runtime->private_data = aacirun;
414cb5a6ffcSRussell King runtime->hw = aaci_hw_info;
4156ca867c8SRussell King runtime->hw.rates = aacirun->pcm->rates;
4166ca867c8SRussell King snd_pcm_limit_hw_rates(runtime);
417cb5a6ffcSRussell King
418e831d80bSRussell King if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
419e831d80bSRussell King runtime->hw.channels_max = 6;
420e831d80bSRussell King
421e831d80bSRussell King /* Add rule describing channel dependency. */
422e831d80bSRussell King ret = snd_pcm_hw_rule_add(substream->runtime, 0,
423e831d80bSRussell King SNDRV_PCM_HW_PARAM_CHANNELS,
424e831d80bSRussell King aaci_rule_channels, aaci,
425e831d80bSRussell King SNDRV_PCM_HW_PARAM_CHANNELS, -1);
426e831d80bSRussell King if (ret)
427e831d80bSRussell King return ret;
428e831d80bSRussell King
429e831d80bSRussell King if (aacirun->pcm->r[1].slots)
430a08d5658SRussell King snd_ac97_pcm_double_rate_rules(runtime);
431e831d80bSRussell King }
432a08d5658SRussell King
433cb5a6ffcSRussell King /*
4345d350cbaSRussell King * ALSA wants the byte-size of the FIFOs. As we only support
4355d350cbaSRussell King * 16-bit samples, this is twice the FIFO depth irrespective
4365d350cbaSRussell King * of whether it's in compact mode or not.
437cb5a6ffcSRussell King */
4385d350cbaSRussell King runtime->hw.fifo_size = aaci->fifo_depth * 2;
439cb5a6ffcSRussell King
440b60fb519SRussell King mutex_lock(&aaci->irq_lock);
441b60fb519SRussell King if (!aaci->users++) {
442b60fb519SRussell King ret = request_irq(aaci->dev->irq[0], aaci_irq,
44388e24c3aSYong Zhang IRQF_SHARED, DRIVER_NAME, aaci);
444b60fb519SRussell King if (ret != 0)
445b60fb519SRussell King aaci->users--;
446b60fb519SRussell King }
447b60fb519SRussell King mutex_unlock(&aaci->irq_lock);
448cb5a6ffcSRussell King
449cb5a6ffcSRussell King return ret;
450cb5a6ffcSRussell King }
451cb5a6ffcSRussell King
452cb5a6ffcSRussell King
453cb5a6ffcSRussell King /*
454cb5a6ffcSRussell King * Common ALSA stuff
455cb5a6ffcSRussell King */
aaci_pcm_close(struct snd_pcm_substream * substream)456ceb9e476STakashi Iwai static int aaci_pcm_close(struct snd_pcm_substream *substream)
457cb5a6ffcSRussell King {
458cb5a6ffcSRussell King struct aaci *aaci = substream->private_data;
459cb5a6ffcSRussell King struct aaci_runtime *aacirun = substream->runtime->private_data;
460cb5a6ffcSRussell King
46141762b8cSKevin Hilman WARN_ON(aacirun->cr & CR_EN);
462cb5a6ffcSRussell King
463cb5a6ffcSRussell King aacirun->substream = NULL;
464b60fb519SRussell King
465b60fb519SRussell King mutex_lock(&aaci->irq_lock);
466b60fb519SRussell King if (!--aaci->users)
467cb5a6ffcSRussell King free_irq(aaci->dev->irq[0], aaci);
468b60fb519SRussell King mutex_unlock(&aaci->irq_lock);
469cb5a6ffcSRussell King
470cb5a6ffcSRussell King return 0;
471cb5a6ffcSRussell King }
472cb5a6ffcSRussell King
aaci_pcm_hw_free(struct snd_pcm_substream * substream)473ceb9e476STakashi Iwai static int aaci_pcm_hw_free(struct snd_pcm_substream *substream)
474cb5a6ffcSRussell King {
475cb5a6ffcSRussell King struct aaci_runtime *aacirun = substream->runtime->private_data;
476cb5a6ffcSRussell King
477cb5a6ffcSRussell King /*
478cb5a6ffcSRussell King * This must not be called with the device enabled.
479cb5a6ffcSRussell King */
48041762b8cSKevin Hilman WARN_ON(aacirun->cr & CR_EN);
481cb5a6ffcSRussell King
482cb5a6ffcSRussell King if (aacirun->pcm_open)
483cb5a6ffcSRussell King snd_ac97_pcm_close(aacirun->pcm);
484cb5a6ffcSRussell King aacirun->pcm_open = 0;
485cb5a6ffcSRussell King
486cb5a6ffcSRussell King return 0;
487cb5a6ffcSRussell King }
488cb5a6ffcSRussell King
48958e8a474SRussell King /* Channel to slot mask */
49058e8a474SRussell King static const u32 channels_to_slotmask[] = {
49158e8a474SRussell King [2] = CR_SL3 | CR_SL4,
49258e8a474SRussell King [4] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8,
49358e8a474SRussell King [6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9,
49458e8a474SRussell King };
49558e8a474SRussell King
aaci_pcm_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)496ceb9e476STakashi Iwai static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
497ceb9e476STakashi Iwai struct snd_pcm_hw_params *params)
498cb5a6ffcSRussell King {
49958e8a474SRussell King struct aaci_runtime *aacirun = substream->runtime->private_data;
5008ee0c758STakashi Iwai struct aaci *aaci = substream->private_data;
50158e8a474SRussell King unsigned int channels = params_channels(params);
50258e8a474SRussell King unsigned int rate = params_rate(params);
50358e8a474SRussell King int dbl = rate > 48000;
504cb5a6ffcSRussell King int err;
505cb5a6ffcSRussell King
506cb5a6ffcSRussell King aaci_pcm_hw_free(substream);
5074acd57c3SRussell King if (aacirun->pcm_open) {
5084acd57c3SRussell King snd_ac97_pcm_close(aacirun->pcm);
5094acd57c3SRussell King aacirun->pcm_open = 0;
5104acd57c3SRussell King }
511cb5a6ffcSRussell King
51258e8a474SRussell King /* channels is already limited to 2, 4, or 6 by aaci_rule_channels */
51358e8a474SRussell King if (dbl && channels != 2)
51458e8a474SRussell King return -EINVAL;
51558e8a474SRussell King
51658e8a474SRussell King err = snd_ac97_pcm_open(aacirun->pcm, rate, channels,
517a08d5658SRussell King aacirun->pcm->r[dbl].slots);
518cb5a6ffcSRussell King
5194e30b691SRussell King aacirun->pcm_open = err == 0;
520d3aee799SRussell King aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
52158e8a474SRussell King aacirun->cr |= channels_to_slotmask[channels + dbl * 2];
522d3aee799SRussell King
5235d350cbaSRussell King /*
5245d350cbaSRussell King * fifo_bytes is the number of bytes we transfer to/from
5255d350cbaSRussell King * the FIFO, including padding. So that's x4. As we're
5265d350cbaSRussell King * in compact mode, the FIFO is half the size.
5275d350cbaSRussell King */
5285d350cbaSRussell King aacirun->fifo_bytes = aaci->fifo_depth * 4 / 2;
529cb5a6ffcSRussell King
530cb5a6ffcSRussell King return err;
531cb5a6ffcSRussell King }
532cb5a6ffcSRussell King
aaci_pcm_prepare(struct snd_pcm_substream * substream)533ceb9e476STakashi Iwai static int aaci_pcm_prepare(struct snd_pcm_substream *substream)
534cb5a6ffcSRussell King {
535ceb9e476STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
536cb5a6ffcSRussell King struct aaci_runtime *aacirun = runtime->private_data;
537cb5a6ffcSRussell King
538c0dea82cSRussell King aacirun->period = snd_pcm_lib_period_bytes(substream);
5394e30b691SRussell King aacirun->start = runtime->dma_area;
54088cdca9cSRussell King aacirun->end = aacirun->start + snd_pcm_lib_buffer_bytes(substream);
541cb5a6ffcSRussell King aacirun->ptr = aacirun->start;
542c0dea82cSRussell King aacirun->bytes = aacirun->period;
543cb5a6ffcSRussell King
544cb5a6ffcSRussell King return 0;
545cb5a6ffcSRussell King }
546cb5a6ffcSRussell King
aaci_pcm_pointer(struct snd_pcm_substream * substream)547ceb9e476STakashi Iwai static snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream)
548cb5a6ffcSRussell King {
549ceb9e476STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
550cb5a6ffcSRussell King struct aaci_runtime *aacirun = runtime->private_data;
551cb5a6ffcSRussell King ssize_t bytes = aacirun->ptr - aacirun->start;
552cb5a6ffcSRussell King
553cb5a6ffcSRussell King return bytes_to_frames(runtime, bytes);
554cb5a6ffcSRussell King }
555cb5a6ffcSRussell King
556cb5a6ffcSRussell King
557cb5a6ffcSRussell King /*
558cb5a6ffcSRussell King * Playback specific ALSA stuff
559cb5a6ffcSRussell King */
aaci_pcm_playback_stop(struct aaci_runtime * aacirun)560cb5a6ffcSRussell King static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun)
561cb5a6ffcSRussell King {
562cb5a6ffcSRussell King u32 ie;
563cb5a6ffcSRussell King
564cb5a6ffcSRussell King ie = readl(aacirun->base + AACI_IE);
565cb5a6ffcSRussell King ie &= ~(IE_URIE|IE_TXIE);
566cb5a6ffcSRussell King writel(ie, aacirun->base + AACI_IE);
56741762b8cSKevin Hilman aacirun->cr &= ~CR_EN;
568d6a89fefSRussell King aaci_chan_wait_ready(aacirun, SR_TXB);
569cb5a6ffcSRussell King writel(aacirun->cr, aacirun->base + AACI_TXCR);
570cb5a6ffcSRussell King }
571cb5a6ffcSRussell King
aaci_pcm_playback_start(struct aaci_runtime * aacirun)572cb5a6ffcSRussell King static void aaci_pcm_playback_start(struct aaci_runtime *aacirun)
573cb5a6ffcSRussell King {
574cb5a6ffcSRussell King u32 ie;
575cb5a6ffcSRussell King
576d6a89fefSRussell King aaci_chan_wait_ready(aacirun, SR_TXB);
57741762b8cSKevin Hilman aacirun->cr |= CR_EN;
578cb5a6ffcSRussell King
579cb5a6ffcSRussell King ie = readl(aacirun->base + AACI_IE);
580cb5a6ffcSRussell King ie |= IE_URIE | IE_TXIE;
581cb5a6ffcSRussell King writel(ie, aacirun->base + AACI_IE);
582cb5a6ffcSRussell King writel(aacirun->cr, aacirun->base + AACI_TXCR);
583cb5a6ffcSRussell King }
584cb5a6ffcSRussell King
aaci_pcm_playback_trigger(struct snd_pcm_substream * substream,int cmd)585ceb9e476STakashi Iwai static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
586cb5a6ffcSRussell King {
587cb5a6ffcSRussell King struct aaci_runtime *aacirun = substream->runtime->private_data;
588cb5a6ffcSRussell King unsigned long flags;
589cb5a6ffcSRussell King int ret = 0;
590cb5a6ffcSRussell King
591d6a89fefSRussell King spin_lock_irqsave(&aacirun->lock, flags);
592d6a89fefSRussell King
593cb5a6ffcSRussell King switch (cmd) {
594cb5a6ffcSRussell King case SNDRV_PCM_TRIGGER_START:
595cb5a6ffcSRussell King aaci_pcm_playback_start(aacirun);
596cb5a6ffcSRussell King break;
597cb5a6ffcSRussell King
598cb5a6ffcSRussell King case SNDRV_PCM_TRIGGER_RESUME:
599cb5a6ffcSRussell King aaci_pcm_playback_start(aacirun);
600cb5a6ffcSRussell King break;
601cb5a6ffcSRussell King
602cb5a6ffcSRussell King case SNDRV_PCM_TRIGGER_STOP:
603cb5a6ffcSRussell King aaci_pcm_playback_stop(aacirun);
604cb5a6ffcSRussell King break;
605cb5a6ffcSRussell King
606cb5a6ffcSRussell King case SNDRV_PCM_TRIGGER_SUSPEND:
607cb5a6ffcSRussell King aaci_pcm_playback_stop(aacirun);
608cb5a6ffcSRussell King break;
609cb5a6ffcSRussell King
610cb5a6ffcSRussell King case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
611cb5a6ffcSRussell King break;
612cb5a6ffcSRussell King
613cb5a6ffcSRussell King case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
614cb5a6ffcSRussell King break;
615cb5a6ffcSRussell King
616cb5a6ffcSRussell King default:
617cb5a6ffcSRussell King ret = -EINVAL;
618cb5a6ffcSRussell King }
619d6a89fefSRussell King
620d6a89fefSRussell King spin_unlock_irqrestore(&aacirun->lock, flags);
621cb5a6ffcSRussell King
622cb5a6ffcSRussell King return ret;
623cb5a6ffcSRussell King }
624cb5a6ffcSRussell King
62528f05480SArvind Yadav static const struct snd_pcm_ops aaci_playback_ops = {
62641762b8cSKevin Hilman .open = aaci_pcm_open,
627cb5a6ffcSRussell King .close = aaci_pcm_close,
62858e8a474SRussell King .hw_params = aaci_pcm_hw_params,
629cb5a6ffcSRussell King .hw_free = aaci_pcm_hw_free,
630cb5a6ffcSRussell King .prepare = aaci_pcm_prepare,
631cb5a6ffcSRussell King .trigger = aaci_pcm_playback_trigger,
632cb5a6ffcSRussell King .pointer = aaci_pcm_pointer,
633cb5a6ffcSRussell King };
634cb5a6ffcSRussell King
aaci_pcm_capture_stop(struct aaci_runtime * aacirun)63541762b8cSKevin Hilman static void aaci_pcm_capture_stop(struct aaci_runtime *aacirun)
63641762b8cSKevin Hilman {
63741762b8cSKevin Hilman u32 ie;
63841762b8cSKevin Hilman
639d6a89fefSRussell King aaci_chan_wait_ready(aacirun, SR_RXB);
64041762b8cSKevin Hilman
64141762b8cSKevin Hilman ie = readl(aacirun->base + AACI_IE);
64241762b8cSKevin Hilman ie &= ~(IE_ORIE | IE_RXIE);
64341762b8cSKevin Hilman writel(ie, aacirun->base+AACI_IE);
64441762b8cSKevin Hilman
64541762b8cSKevin Hilman aacirun->cr &= ~CR_EN;
64641762b8cSKevin Hilman
64741762b8cSKevin Hilman writel(aacirun->cr, aacirun->base + AACI_RXCR);
64841762b8cSKevin Hilman }
64941762b8cSKevin Hilman
aaci_pcm_capture_start(struct aaci_runtime * aacirun)65041762b8cSKevin Hilman static void aaci_pcm_capture_start(struct aaci_runtime *aacirun)
65141762b8cSKevin Hilman {
65241762b8cSKevin Hilman u32 ie;
65341762b8cSKevin Hilman
654d6a89fefSRussell King aaci_chan_wait_ready(aacirun, SR_RXB);
65541762b8cSKevin Hilman
65641762b8cSKevin Hilman #ifdef DEBUG
65741762b8cSKevin Hilman /* RX Timeout value: bits 28:17 in RXCR */
65841762b8cSKevin Hilman aacirun->cr |= 0xf << 17;
65941762b8cSKevin Hilman #endif
66041762b8cSKevin Hilman
66141762b8cSKevin Hilman aacirun->cr |= CR_EN;
66241762b8cSKevin Hilman writel(aacirun->cr, aacirun->base + AACI_RXCR);
66341762b8cSKevin Hilman
66441762b8cSKevin Hilman ie = readl(aacirun->base + AACI_IE);
66541762b8cSKevin Hilman ie |= IE_ORIE |IE_RXIE; // overrun and rx interrupt -- half full
66641762b8cSKevin Hilman writel(ie, aacirun->base + AACI_IE);
66741762b8cSKevin Hilman }
66841762b8cSKevin Hilman
aaci_pcm_capture_trigger(struct snd_pcm_substream * substream,int cmd)6698a371840SRussell King static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
6708a371840SRussell King {
67141762b8cSKevin Hilman struct aaci_runtime *aacirun = substream->runtime->private_data;
67241762b8cSKevin Hilman unsigned long flags;
67341762b8cSKevin Hilman int ret = 0;
67441762b8cSKevin Hilman
675d6a89fefSRussell King spin_lock_irqsave(&aacirun->lock, flags);
67641762b8cSKevin Hilman
67741762b8cSKevin Hilman switch (cmd) {
67841762b8cSKevin Hilman case SNDRV_PCM_TRIGGER_START:
67941762b8cSKevin Hilman aaci_pcm_capture_start(aacirun);
68041762b8cSKevin Hilman break;
68141762b8cSKevin Hilman
68241762b8cSKevin Hilman case SNDRV_PCM_TRIGGER_RESUME:
68341762b8cSKevin Hilman aaci_pcm_capture_start(aacirun);
68441762b8cSKevin Hilman break;
68541762b8cSKevin Hilman
68641762b8cSKevin Hilman case SNDRV_PCM_TRIGGER_STOP:
68741762b8cSKevin Hilman aaci_pcm_capture_stop(aacirun);
68841762b8cSKevin Hilman break;
68941762b8cSKevin Hilman
69041762b8cSKevin Hilman case SNDRV_PCM_TRIGGER_SUSPEND:
69141762b8cSKevin Hilman aaci_pcm_capture_stop(aacirun);
69241762b8cSKevin Hilman break;
69341762b8cSKevin Hilman
69441762b8cSKevin Hilman case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
69541762b8cSKevin Hilman break;
69641762b8cSKevin Hilman
69741762b8cSKevin Hilman case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
69841762b8cSKevin Hilman break;
69941762b8cSKevin Hilman
70041762b8cSKevin Hilman default:
70141762b8cSKevin Hilman ret = -EINVAL;
70241762b8cSKevin Hilman }
70341762b8cSKevin Hilman
704d6a89fefSRussell King spin_unlock_irqrestore(&aacirun->lock, flags);
70541762b8cSKevin Hilman
70641762b8cSKevin Hilman return ret;
70741762b8cSKevin Hilman }
70841762b8cSKevin Hilman
aaci_pcm_capture_prepare(struct snd_pcm_substream * substream)7098a371840SRussell King static int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream)
71041762b8cSKevin Hilman {
71141762b8cSKevin Hilman struct snd_pcm_runtime *runtime = substream->runtime;
71241762b8cSKevin Hilman struct aaci *aaci = substream->private_data;
71341762b8cSKevin Hilman
71441762b8cSKevin Hilman aaci_pcm_prepare(substream);
71541762b8cSKevin Hilman
71641762b8cSKevin Hilman /* allow changing of sample rate */
71741762b8cSKevin Hilman aaci_ac97_write(aaci->ac97, AC97_EXTENDED_STATUS, 0x0001); /* VRA */
71841762b8cSKevin Hilman aaci_ac97_write(aaci->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
71941762b8cSKevin Hilman aaci_ac97_write(aaci->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate);
72041762b8cSKevin Hilman
72141762b8cSKevin Hilman /* Record select: Mic: 0, Aux: 3, Line: 4 */
72241762b8cSKevin Hilman aaci_ac97_write(aaci->ac97, AC97_REC_SEL, 0x0404);
72341762b8cSKevin Hilman
72441762b8cSKevin Hilman return 0;
72541762b8cSKevin Hilman }
72641762b8cSKevin Hilman
72728f05480SArvind Yadav static const struct snd_pcm_ops aaci_capture_ops = {
72841762b8cSKevin Hilman .open = aaci_pcm_open,
72941762b8cSKevin Hilman .close = aaci_pcm_close,
73058e8a474SRussell King .hw_params = aaci_pcm_hw_params,
73141762b8cSKevin Hilman .hw_free = aaci_pcm_hw_free,
73241762b8cSKevin Hilman .prepare = aaci_pcm_capture_prepare,
73341762b8cSKevin Hilman .trigger = aaci_pcm_capture_trigger,
73441762b8cSKevin Hilman .pointer = aaci_pcm_pointer,
73541762b8cSKevin Hilman };
736cb5a6ffcSRussell King
737cb5a6ffcSRussell King /*
738cb5a6ffcSRussell King * Power Management.
739cb5a6ffcSRussell King */
740cb5a6ffcSRussell King #ifdef CONFIG_PM
aaci_do_suspend(struct snd_card * card)741b13a7149SUlf Hansson static int aaci_do_suspend(struct snd_card *card)
742cb5a6ffcSRussell King {
743cb5a6ffcSRussell King struct aaci *aaci = card->private_data;
744792a6c51STakashi Iwai snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
745cb5a6ffcSRussell King return 0;
746cb5a6ffcSRussell King }
747cb5a6ffcSRussell King
aaci_do_resume(struct snd_card * card)748b13a7149SUlf Hansson static int aaci_do_resume(struct snd_card *card)
749cb5a6ffcSRussell King {
750792a6c51STakashi Iwai snd_power_change_state(card, SNDRV_CTL_POWER_D0);
751cb5a6ffcSRussell King return 0;
752cb5a6ffcSRussell King }
753cb5a6ffcSRussell King
aaci_suspend(struct device * dev)754b13a7149SUlf Hansson static int aaci_suspend(struct device *dev)
755cb5a6ffcSRussell King {
756b13a7149SUlf Hansson struct snd_card *card = dev_get_drvdata(dev);
757cb5a6ffcSRussell King return card ? aaci_do_suspend(card) : 0;
758cb5a6ffcSRussell King }
759cb5a6ffcSRussell King
aaci_resume(struct device * dev)760b13a7149SUlf Hansson static int aaci_resume(struct device *dev)
761cb5a6ffcSRussell King {
762b13a7149SUlf Hansson struct snd_card *card = dev_get_drvdata(dev);
763cb5a6ffcSRussell King return card ? aaci_do_resume(card) : 0;
764cb5a6ffcSRussell King }
765b13a7149SUlf Hansson
766b13a7149SUlf Hansson static SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume);
767b13a7149SUlf Hansson #define AACI_DEV_PM_OPS (&aaci_dev_pm_ops)
768cb5a6ffcSRussell King #else
769b13a7149SUlf Hansson #define AACI_DEV_PM_OPS NULL
770cb5a6ffcSRussell King #endif
771cb5a6ffcSRussell King
772cb5a6ffcSRussell King
7739b419cd4SArvind Yadav static const struct ac97_pcm ac97_defs[] = {
774cb5a6ffcSRussell King [0] = { /* Front PCM */
775cb5a6ffcSRussell King .exclusive = 1,
776cb5a6ffcSRussell King .r = {
777cb5a6ffcSRussell King [0] = {
778cb5a6ffcSRussell King .slots = (1 << AC97_SLOT_PCM_LEFT) |
779cb5a6ffcSRussell King (1 << AC97_SLOT_PCM_RIGHT) |
780cb5a6ffcSRussell King (1 << AC97_SLOT_PCM_CENTER) |
781cb5a6ffcSRussell King (1 << AC97_SLOT_PCM_SLEFT) |
782cb5a6ffcSRussell King (1 << AC97_SLOT_PCM_SRIGHT) |
783cb5a6ffcSRussell King (1 << AC97_SLOT_LFE),
784cb5a6ffcSRussell King },
785a08d5658SRussell King [1] = {
786a08d5658SRussell King .slots = (1 << AC97_SLOT_PCM_LEFT) |
787a08d5658SRussell King (1 << AC97_SLOT_PCM_RIGHT) |
788a08d5658SRussell King (1 << AC97_SLOT_PCM_LEFT_0) |
789a08d5658SRussell King (1 << AC97_SLOT_PCM_RIGHT_0),
790a08d5658SRussell King },
791cb5a6ffcSRussell King },
792cb5a6ffcSRussell King },
793cb5a6ffcSRussell King [1] = { /* PCM in */
794cb5a6ffcSRussell King .stream = 1,
795cb5a6ffcSRussell King .exclusive = 1,
796cb5a6ffcSRussell King .r = {
797cb5a6ffcSRussell King [0] = {
798cb5a6ffcSRussell King .slots = (1 << AC97_SLOT_PCM_LEFT) |
799cb5a6ffcSRussell King (1 << AC97_SLOT_PCM_RIGHT),
800cb5a6ffcSRussell King },
801cb5a6ffcSRussell King },
802cb5a6ffcSRussell King },
803cb5a6ffcSRussell King [2] = { /* Mic in */
804cb5a6ffcSRussell King .stream = 1,
805cb5a6ffcSRussell King .exclusive = 1,
806cb5a6ffcSRussell King .r = {
807cb5a6ffcSRussell King [0] = {
808cb5a6ffcSRussell King .slots = (1 << AC97_SLOT_MIC),
809cb5a6ffcSRussell King },
810cb5a6ffcSRussell King },
811cb5a6ffcSRussell King }
812cb5a6ffcSRussell King };
813cb5a6ffcSRussell King
81474d2bae3STakashi Iwai static const struct snd_ac97_bus_ops aaci_bus_ops = {
815cb5a6ffcSRussell King .write = aaci_ac97_write,
816cb5a6ffcSRussell King .read = aaci_ac97_read,
817cb5a6ffcSRussell King };
818cb5a6ffcSRussell King
aaci_probe_ac97(struct aaci * aaci)8196c9dc19cSBill Pemberton static int aaci_probe_ac97(struct aaci *aaci)
820cb5a6ffcSRussell King {
821ceb9e476STakashi Iwai struct snd_ac97_template ac97_template;
822ceb9e476STakashi Iwai struct snd_ac97_bus *ac97_bus;
823ceb9e476STakashi Iwai struct snd_ac97 *ac97;
824cb5a6ffcSRussell King int ret;
825cb5a6ffcSRussell King
826cb5a6ffcSRussell King /*
827cb5a6ffcSRussell King * Assert AACIRESET for 2us
828cb5a6ffcSRussell King */
829cb5a6ffcSRussell King writel(0, aaci->base + AACI_RESET);
830cb5a6ffcSRussell King udelay(2);
831cb5a6ffcSRussell King writel(RESET_NRST, aaci->base + AACI_RESET);
832cb5a6ffcSRussell King
833cb5a6ffcSRussell King /*
834cb5a6ffcSRussell King * Give the AC'97 codec more than enough time
835cb5a6ffcSRussell King * to wake up. (42us = ~2 frames at 48kHz.)
836cb5a6ffcSRussell King */
837250c7a61SRussell King udelay(FRAME_PERIOD_US * 2);
838cb5a6ffcSRussell King
839cb5a6ffcSRussell King ret = snd_ac97_bus(aaci->card, 0, &aaci_bus_ops, aaci, &ac97_bus);
840cb5a6ffcSRussell King if (ret)
841cb5a6ffcSRussell King goto out;
842cb5a6ffcSRussell King
843cb5a6ffcSRussell King ac97_bus->clock = 48000;
844cb5a6ffcSRussell King aaci->ac97_bus = ac97_bus;
845cb5a6ffcSRussell King
846ceb9e476STakashi Iwai memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
847cb5a6ffcSRussell King ac97_template.private_data = aaci;
848cb5a6ffcSRussell King ac97_template.num = 0;
849cb5a6ffcSRussell King ac97_template.scaps = AC97_SCAP_SKIP_MODEM;
850cb5a6ffcSRussell King
851cb5a6ffcSRussell King ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
852cb5a6ffcSRussell King if (ret)
853cb5a6ffcSRussell King goto out;
85441762b8cSKevin Hilman aaci->ac97 = ac97;
855cb5a6ffcSRussell King
856cb5a6ffcSRussell King /*
857cb5a6ffcSRussell King * Disable AC97 PC Beep input on audio codecs.
858cb5a6ffcSRussell King */
859cb5a6ffcSRussell King if (ac97_is_audio(ac97))
860cb5a6ffcSRussell King snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x801e);
861cb5a6ffcSRussell King
862cb5a6ffcSRussell King ret = snd_ac97_pcm_assign(ac97_bus, ARRAY_SIZE(ac97_defs), ac97_defs);
863cb5a6ffcSRussell King if (ret)
864cb5a6ffcSRussell King goto out;
865cb5a6ffcSRussell King
866cb5a6ffcSRussell King aaci->playback.pcm = &ac97_bus->pcms[0];
86741762b8cSKevin Hilman aaci->capture.pcm = &ac97_bus->pcms[1];
868cb5a6ffcSRussell King
869cb5a6ffcSRussell King out:
870cb5a6ffcSRussell King return ret;
871cb5a6ffcSRussell King }
872cb5a6ffcSRussell King
aaci_free_card(struct snd_card * card)873ceb9e476STakashi Iwai static void aaci_free_card(struct snd_card *card)
874cb5a6ffcSRussell King {
875cb5a6ffcSRussell King struct aaci *aaci = card->private_data;
876ff6defa6SMarkus Elfring
877cb5a6ffcSRussell King iounmap(aaci->base);
878cb5a6ffcSRussell King }
879cb5a6ffcSRussell King
aaci_init_card(struct amba_device * dev)8806c9dc19cSBill Pemberton static struct aaci *aaci_init_card(struct amba_device *dev)
881cb5a6ffcSRussell King {
882cb5a6ffcSRussell King struct aaci *aaci;
883ceb9e476STakashi Iwai struct snd_card *card;
884bd7dd77cSTakashi Iwai int err;
885cb5a6ffcSRussell King
8864a875580STakashi Iwai err = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
887bd7dd77cSTakashi Iwai THIS_MODULE, sizeof(struct aaci), &card);
888bd7dd77cSTakashi Iwai if (err < 0)
889631e8ad4STakashi Iwai return NULL;
890cb5a6ffcSRussell King
891cb5a6ffcSRussell King card->private_free = aaci_free_card;
892cb5a6ffcSRussell King
89375b1a8f9SJoe Perches strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
89475b1a8f9SJoe Perches strscpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));
895cb5a6ffcSRussell King snprintf(card->longname, sizeof(card->longname),
896f006d8fcSRussell King "%s PL%03x rev%u at 0x%08llx, irq %d",
897f006d8fcSRussell King card->shortname, amba_part(dev), amba_rev(dev),
898f006d8fcSRussell King (unsigned long long)dev->res.start, dev->irq[0]);
899cb5a6ffcSRussell King
900cb5a6ffcSRussell King aaci = card->private_data;
90112aa7579SIngo Molnar mutex_init(&aaci->ac97_sem);
902b60fb519SRussell King mutex_init(&aaci->irq_lock);
903cb5a6ffcSRussell King aaci->card = card;
904cb5a6ffcSRussell King aaci->dev = dev;
905cb5a6ffcSRussell King
906cb5a6ffcSRussell King /* Set MAINCR to allow slot 1 and 2 data IO */
907cb5a6ffcSRussell King aaci->maincr = MAINCR_IE | MAINCR_SL1RXEN | MAINCR_SL1TXEN |
908cb5a6ffcSRussell King MAINCR_SL2RXEN | MAINCR_SL2TXEN;
909cb5a6ffcSRussell King
910cb5a6ffcSRussell King return aaci;
911cb5a6ffcSRussell King }
912cb5a6ffcSRussell King
aaci_init_pcm(struct aaci * aaci)9136c9dc19cSBill Pemberton static int aaci_init_pcm(struct aaci *aaci)
914cb5a6ffcSRussell King {
915ceb9e476STakashi Iwai struct snd_pcm *pcm;
916cb5a6ffcSRussell King int ret;
917cb5a6ffcSRussell King
91841762b8cSKevin Hilman ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 1, &pcm);
919cb5a6ffcSRussell King if (ret == 0) {
920cb5a6ffcSRussell King aaci->pcm = pcm;
921cb5a6ffcSRussell King pcm->private_data = aaci;
922cb5a6ffcSRussell King pcm->info_flags = 0;
923cb5a6ffcSRussell King
92475b1a8f9SJoe Perches strscpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
925cb5a6ffcSRussell King
926cb5a6ffcSRussell King snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
92741762b8cSKevin Hilman snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
9288ee0c758STakashi Iwai snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
929bc70a9d7STakashi Iwai aaci->card->dev,
930bc70a9d7STakashi Iwai 0, 64 * 1024);
931cb5a6ffcSRussell King }
932cb5a6ffcSRussell King
933cb5a6ffcSRussell King return ret;
934cb5a6ffcSRussell King }
935cb5a6ffcSRussell King
aaci_size_fifo(struct aaci * aaci)9366c9dc19cSBill Pemberton static unsigned int aaci_size_fifo(struct aaci *aaci)
937cb5a6ffcSRussell King {
93841762b8cSKevin Hilman struct aaci_runtime *aacirun = &aaci->playback;
939cb5a6ffcSRussell King int i;
940cb5a6ffcSRussell King
9415d350cbaSRussell King /*
9425d350cbaSRussell King * Enable the channel, but don't assign it to any slots, so
9435d350cbaSRussell King * it won't empty onto the AC'97 link.
9445d350cbaSRussell King */
94541762b8cSKevin Hilman writel(CR_FEN | CR_SZ16 | CR_EN, aacirun->base + AACI_TXCR);
946cb5a6ffcSRussell King
94741762b8cSKevin Hilman for (i = 0; !(readl(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++)
94841762b8cSKevin Hilman writel(0, aacirun->fifo);
949cb5a6ffcSRussell King
95041762b8cSKevin Hilman writel(0, aacirun->base + AACI_TXCR);
951cb5a6ffcSRussell King
952cb5a6ffcSRussell King /*
953cb5a6ffcSRussell King * Re-initialise the AACI after the FIFO depth test, to
954cb5a6ffcSRussell King * ensure that the FIFOs are empty. Unfortunately, merely
955cb5a6ffcSRussell King * disabling the channel doesn't clear the FIFO.
956cb5a6ffcSRussell King */
957cb5a6ffcSRussell King writel(aaci->maincr & ~MAINCR_IE, aaci->base + AACI_MAINCR);
9587c289385SRussell King readl(aaci->base + AACI_MAINCR);
9597c289385SRussell King udelay(1);
960cb5a6ffcSRussell King writel(aaci->maincr, aaci->base + AACI_MAINCR);
961cb5a6ffcSRussell King
962cb5a6ffcSRussell King /*
9635d350cbaSRussell King * If we hit 4096 entries, we failed. Go back to the specified
964cb5a6ffcSRussell King * fifo depth.
965cb5a6ffcSRussell King */
966cb5a6ffcSRussell King if (i == 4096)
967cb5a6ffcSRussell King i = 8;
968cb5a6ffcSRussell King
969cb5a6ffcSRussell King return i;
970cb5a6ffcSRussell King }
971cb5a6ffcSRussell King
aaci_probe(struct amba_device * dev,const struct amba_id * id)9726c9dc19cSBill Pemberton static int aaci_probe(struct amba_device *dev,
973aa25afadSRussell King const struct amba_id *id)
974cb5a6ffcSRussell King {
975cb5a6ffcSRussell King struct aaci *aaci;
976cb5a6ffcSRussell King int ret, i;
977cb5a6ffcSRussell King
978cb5a6ffcSRussell King ret = amba_request_regions(dev, NULL);
979cb5a6ffcSRussell King if (ret)
980cb5a6ffcSRussell King return ret;
981cb5a6ffcSRussell King
982cb5a6ffcSRussell King aaci = aaci_init_card(dev);
983631e8ad4STakashi Iwai if (!aaci) {
984631e8ad4STakashi Iwai ret = -ENOMEM;
985cb5a6ffcSRussell King goto out;
986cb5a6ffcSRussell King }
987cb5a6ffcSRussell King
988dc890c2dSLinus Walleij aaci->base = ioremap(dev->res.start, resource_size(&dev->res));
989cb5a6ffcSRussell King if (!aaci->base) {
990cb5a6ffcSRussell King ret = -ENOMEM;
991cb5a6ffcSRussell King goto out;
992cb5a6ffcSRussell King }
993cb5a6ffcSRussell King
994cb5a6ffcSRussell King /*
995cb5a6ffcSRussell King * Playback uses AACI channel 0
996cb5a6ffcSRussell King */
997d6a89fefSRussell King spin_lock_init(&aaci->playback.lock);
998cb5a6ffcSRussell King aaci->playback.base = aaci->base + AACI_CSCH1;
999cb5a6ffcSRussell King aaci->playback.fifo = aaci->base + AACI_DR1;
1000cb5a6ffcSRussell King
100141762b8cSKevin Hilman /*
100241762b8cSKevin Hilman * Capture uses AACI channel 0
100341762b8cSKevin Hilman */
1004d6a89fefSRussell King spin_lock_init(&aaci->capture.lock);
100541762b8cSKevin Hilman aaci->capture.base = aaci->base + AACI_CSCH1;
100641762b8cSKevin Hilman aaci->capture.fifo = aaci->base + AACI_DR1;
100741762b8cSKevin Hilman
1008cb5a6ffcSRussell King for (i = 0; i < 4; i++) {
1009e12ba644Sviro@ZenIV.linux.org.uk void __iomem *base = aaci->base + i * 0x14;
1010cb5a6ffcSRussell King
1011cb5a6ffcSRussell King writel(0, base + AACI_IE);
1012cb5a6ffcSRussell King writel(0, base + AACI_TXCR);
1013cb5a6ffcSRussell King writel(0, base + AACI_RXCR);
1014cb5a6ffcSRussell King }
1015cb5a6ffcSRussell King
1016cb5a6ffcSRussell King writel(0x1fff, aaci->base + AACI_INTCLR);
1017cb5a6ffcSRussell King writel(aaci->maincr, aaci->base + AACI_MAINCR);
1018b68b58fdSPhilby John /*
1019b68b58fdSPhilby John * Fix: ac97 read back fail errors by reading
1020b68b58fdSPhilby John * from any arbitrary aaci register.
1021b68b58fdSPhilby John */
1022b68b58fdSPhilby John readl(aaci->base + AACI_CSCH1);
1023cb5a6ffcSRussell King ret = aaci_probe_ac97(aaci);
1024cb5a6ffcSRussell King if (ret)
1025cb5a6ffcSRussell King goto out;
1026cb5a6ffcSRussell King
1027f27f218cSCatalin Marinas /*
1028f27f218cSCatalin Marinas * Size the FIFOs (must be multiple of 16).
10295d350cbaSRussell King * This is the number of entries in the FIFO.
1030f27f218cSCatalin Marinas */
10315d350cbaSRussell King aaci->fifo_depth = aaci_size_fifo(aaci);
10325d350cbaSRussell King if (aaci->fifo_depth & 15) {
10335d350cbaSRussell King printk(KERN_WARNING "AACI: FIFO depth %d not supported\n",
10345d350cbaSRussell King aaci->fifo_depth);
1035f27f218cSCatalin Marinas ret = -ENODEV;
1036f27f218cSCatalin Marinas goto out;
1037f27f218cSCatalin Marinas }
1038f27f218cSCatalin Marinas
1039cb5a6ffcSRussell King ret = aaci_init_pcm(aaci);
1040cb5a6ffcSRussell King if (ret)
1041cb5a6ffcSRussell King goto out;
1042cb5a6ffcSRussell King
1043cb5a6ffcSRussell King ret = snd_card_register(aaci->card);
1044cb5a6ffcSRussell King if (ret == 0) {
10455d350cbaSRussell King dev_info(&dev->dev, "%s\n", aaci->card->longname);
10465d350cbaSRussell King dev_info(&dev->dev, "FIFO %u entries\n", aaci->fifo_depth);
1047cb5a6ffcSRussell King amba_set_drvdata(dev, aaci->card);
1048cb5a6ffcSRussell King return ret;
1049cb5a6ffcSRussell King }
1050cb5a6ffcSRussell King
1051cb5a6ffcSRussell King out:
1052cb5a6ffcSRussell King if (aaci)
1053cb5a6ffcSRussell King snd_card_free(aaci->card);
1054cb5a6ffcSRussell King amba_release_regions(dev);
1055cb5a6ffcSRussell King return ret;
1056cb5a6ffcSRussell King }
1057cb5a6ffcSRussell King
aaci_remove(struct amba_device * dev)1058*3fd269e7SUwe Kleine-König static void aaci_remove(struct amba_device *dev)
1059cb5a6ffcSRussell King {
1060ceb9e476STakashi Iwai struct snd_card *card = amba_get_drvdata(dev);
1061cb5a6ffcSRussell King
1062cb5a6ffcSRussell King if (card) {
1063cb5a6ffcSRussell King struct aaci *aaci = card->private_data;
1064cb5a6ffcSRussell King writel(0, aaci->base + AACI_MAINCR);
1065cb5a6ffcSRussell King
1066cb5a6ffcSRussell King snd_card_free(card);
1067cb5a6ffcSRussell King amba_release_regions(dev);
1068cb5a6ffcSRussell King }
1069cb5a6ffcSRussell King }
1070cb5a6ffcSRussell King
1071cb5a6ffcSRussell King static struct amba_id aaci_ids[] = {
1072cb5a6ffcSRussell King {
1073cb5a6ffcSRussell King .id = 0x00041041,
1074cb5a6ffcSRussell King .mask = 0x000fffff,
1075cb5a6ffcSRussell King },
1076cb5a6ffcSRussell King { 0, 0 },
1077cb5a6ffcSRussell King };
1078cb5a6ffcSRussell King
10799d5c6273SDave Martin MODULE_DEVICE_TABLE(amba, aaci_ids);
10809d5c6273SDave Martin
1081cb5a6ffcSRussell King static struct amba_driver aaci_driver = {
1082cb5a6ffcSRussell King .drv = {
1083cb5a6ffcSRussell King .name = DRIVER_NAME,
1084b13a7149SUlf Hansson .pm = AACI_DEV_PM_OPS,
1085cb5a6ffcSRussell King },
1086cb5a6ffcSRussell King .probe = aaci_probe,
10876c9dc19cSBill Pemberton .remove = aaci_remove,
1088cb5a6ffcSRussell King .id_table = aaci_ids,
1089cb5a6ffcSRussell King };
1090cb5a6ffcSRussell King
10919e5ed094Sviresh kumar module_amba_driver(aaci_driver);
1092cb5a6ffcSRussell King
1093cb5a6ffcSRussell King MODULE_LICENSE("GPL");
1094cb5a6ffcSRussell King MODULE_DESCRIPTION("ARM PrimeCell PL041 Advanced Audio CODEC Interface driver");
1095