diff options
Diffstat (limited to 'sound/arm/aaci.c')
| -rw-r--r-- | sound/arm/aaci.c | 842 |
1 files changed, 491 insertions, 351 deletions
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index 559ead6367d..0e83a73efb1 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -17,13 +17,9 @@ #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/err.h> +#include <linux/amba/bus.h> +#include <linux/io.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/sizes.h> -#include <asm/hardware/amba.h> - -#include <sound/driver.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/ac97_codec.h> @@ -31,16 +27,17 @@ #include <sound/pcm_params.h> #include "aaci.h" -#include "devdma.h" #define DRIVER_NAME "aaci-pl041" +#define FRAME_PERIOD_US 21 + /* * PM support is not complete. Turn it off. */ #undef CONFIG_PM -static void aaci_ac97_select_codec(struct aaci *aaci, ac97_t *ac97) +static void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *ac97) { u32 v, maincr = aaci->maincr | MAINCR_SCRA(ac97->num); @@ -53,7 +50,11 @@ static void aaci_ac97_select_codec(struct aaci *aaci, ac97_t *ac97) if (v & SLFR_1RXV) readl(aaci->base + AACI_SL1RX); - writel(maincr, aaci->base + AACI_MAINCR); + if (maincr != readl(aaci->base + AACI_MAINCR)) { + writel(maincr, aaci->base + AACI_MAINCR); + readl(aaci->base + AACI_MAINCR); + udelay(1); + } } /* @@ -65,15 +66,17 @@ static void aaci_ac97_select_codec(struct aaci *aaci, ac97_t *ac97) * SI1TxEn, SI2TxEn and SI12TxEn bits are set in the AACI_MAINCR * register. */ -static void aaci_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) +static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) { struct aaci *aaci = ac97->private_data; + int timeout; u32 v; if (ac97->num >= 4) return; - down(&aaci->ac97_sem); + mutex_lock(&aaci->ac97_sem); aaci_ac97_select_codec(aaci, ac97); @@ -84,28 +87,36 @@ static void aaci_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val writel(val << 4, aaci->base + AACI_SL2TX); writel(reg << 12, aaci->base + AACI_SL1TX); - /* - * Wait for the transmission of both slots to complete. - */ + /* Initially, wait one frame period */ + udelay(FRAME_PERIOD_US); + + /* And then wait an additional eight frame periods for it to be sent */ + timeout = FRAME_PERIOD_US * 8; do { + udelay(1); v = readl(aaci->base + AACI_SLFR); - } while (v & (SLFR_1TXB|SLFR_2TXB)); + } while ((v & (SLFR_1TXB|SLFR_2TXB)) && --timeout); + + if (v & (SLFR_1TXB|SLFR_2TXB)) + dev_err(&aaci->dev->dev, + "timeout waiting for write to complete\n"); - up(&aaci->ac97_sem); + mutex_unlock(&aaci->ac97_sem); } /* * Read an AC'97 register. */ -static unsigned short aaci_ac97_read(ac97_t *ac97, unsigned short reg) +static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { struct aaci *aaci = ac97->private_data; + int timeout, retries = 10; u32 v; if (ac97->num >= 4) return ~0; - down(&aaci->ac97_sem); + mutex_lock(&aaci->ac97_sem); aaci_ac97_select_codec(aaci, ac97); @@ -114,49 +125,70 @@ static unsigned short aaci_ac97_read(ac97_t *ac97, unsigned short reg) */ writel((reg << 12) | (1 << 19), aaci->base + AACI_SL1TX); - /* - * Wait for the transmission to complete. - */ + /* Initially, wait one frame period */ + udelay(FRAME_PERIOD_US); + + /* And then wait an additional eight frame periods for it to be sent */ + timeout = FRAME_PERIOD_US * 8; do { + udelay(1); v = readl(aaci->base + AACI_SLFR); - } while (v & SLFR_1TXB); + } while ((v & SLFR_1TXB) && --timeout); - /* - * Give the AC'97 codec more than enough time - * to respond. (42us = ~2 frames at 48kHz.) - */ - udelay(42); + if (v & SLFR_1TXB) { + dev_err(&aaci->dev->dev, "timeout on slot 1 TX busy\n"); + v = ~0; + goto out; + } - /* - * Wait for slot 2 to indicate data. - */ + /* Now wait for the response frame */ + udelay(FRAME_PERIOD_US); + + /* And then wait an additional eight frame periods for data */ + timeout = FRAME_PERIOD_US * 8; do { + udelay(1); cond_resched(); v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV); - } while (v != (SLFR_1RXV|SLFR_2RXV)); + } while ((v != (SLFR_1RXV|SLFR_2RXV)) && --timeout); - v = readl(aaci->base + AACI_SL1RX) >> 12; - if (v == reg) { - v = readl(aaci->base + AACI_SL2RX) >> 4; - } else { - dev_err(&aaci->dev->dev, - "wrong ac97 register read back (%x != %x)\n", - v, reg); + if (v != (SLFR_1RXV|SLFR_2RXV)) { + dev_err(&aaci->dev->dev, "timeout on RX valid\n"); v = ~0; + goto out; } - up(&aaci->ac97_sem); + do { + v = readl(aaci->base + AACI_SL1RX) >> 12; + if (v == reg) { + v = readl(aaci->base + AACI_SL2RX) >> 4; + break; + } else if (--retries) { + dev_warn(&aaci->dev->dev, + "ac97 read back fail. retry\n"); + continue; + } else { + dev_warn(&aaci->dev->dev, + "wrong ac97 register read back (%x != %x)\n", + v, reg); + v = ~0; + } + } while (retries); + out: + mutex_unlock(&aaci->ac97_sem); return v; } -static inline void aaci_chan_wait_ready(struct aaci_runtime *aacirun) +static inline void +aaci_chan_wait_ready(struct aaci_runtime *aacirun, unsigned long mask) { u32 val; int timeout = 5000; do { + udelay(1); val = readl(aacirun->base + AACI_SR); - } while (val & (SR_TXB|SR_RXB) && timeout--); + } while (val & mask && timeout--); } @@ -164,35 +196,101 @@ static inline void aaci_chan_wait_ready(struct aaci_runtime *aacirun) /* * Interrupt support. */ -static void aaci_fifo_irq(struct aaci *aaci, u32 mask) +static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask) { + if (mask & ISR_ORINTR) { + dev_warn(&aaci->dev->dev, "RX overrun on chan %d\n", channel); + writel(ICLR_RXOEC1 << channel, aaci->base + AACI_INTCLR); + } + + if (mask & ISR_RXTOINTR) { + dev_warn(&aaci->dev->dev, "RX timeout on chan %d\n", channel); + writel(ICLR_RXTOFEC1 << channel, aaci->base + AACI_INTCLR); + } + + if (mask & ISR_RXINTR) { + struct aaci_runtime *aacirun = &aaci->capture; + bool period_elapsed = false; + void *ptr; + + if (!aacirun->substream || !aacirun->start) { + dev_warn(&aaci->dev->dev, "RX interrupt???\n"); + writel(0, aacirun->base + AACI_IE); + return; + } + + spin_lock(&aacirun->lock); + + ptr = aacirun->ptr; + do { + unsigned int len = aacirun->fifo_bytes; + u32 val; + + if (aacirun->bytes <= 0) { + aacirun->bytes += aacirun->period; + period_elapsed = true; + } + if (!(aacirun->cr & CR_EN)) + break; + + val = readl(aacirun->base + AACI_SR); + if (!(val & SR_RXHF)) + break; + if (!(val & SR_RXFF)) + len >>= 1; + + aacirun->bytes -= len; + + /* reading 16 bytes at a time */ + for( ; len > 0; len -= 16) { + asm( + "ldmia %1, {r0, r1, r2, r3}\n\t" + "stmia %0!, {r0, r1, r2, r3}" + : "+r" (ptr) + : "r" (aacirun->fifo) + : "r0", "r1", "r2", "r3", "cc"); + + if (ptr >= aacirun->end) + ptr = aacirun->start; + } + } while(1); + + aacirun->ptr = ptr; + + spin_unlock(&aacirun->lock); + + if (period_elapsed) + snd_pcm_period_elapsed(aacirun->substream); + } + if (mask & ISR_URINTR) { - writel(ICLR_TXUEC1, aaci->base + AACI_INTCLR); + dev_dbg(&aaci->dev->dev, "TX underrun on chan %d\n", channel); + writel(ICLR_TXUEC1 << channel, aaci->base + AACI_INTCLR); } if (mask & ISR_TXINTR) { struct aaci_runtime *aacirun = &aaci->playback; + bool period_elapsed = false; void *ptr; if (!aacirun->substream || !aacirun->start) { - dev_warn(&aaci->dev->dev, "TX interrupt???"); + dev_warn(&aaci->dev->dev, "TX interrupt???\n"); writel(0, aacirun->base + AACI_IE); return; } + spin_lock(&aacirun->lock); + ptr = aacirun->ptr; do { - unsigned int len = aacirun->fifosz; + unsigned int len = aacirun->fifo_bytes; u32 val; if (aacirun->bytes <= 0) { aacirun->bytes += aacirun->period; - aacirun->ptr = ptr; - spin_unlock(&aaci->lock); - snd_pcm_period_elapsed(aacirun->substream); - spin_lock(&aaci->lock); + period_elapsed = true; } - if (!(aacirun->cr & TXCR_TXEN)) + if (!(aacirun->cr & CR_EN)) break; val = readl(aacirun->base + AACI_SR); @@ -218,26 +316,29 @@ static void aaci_fifo_irq(struct aaci *aaci, u32 mask) } while (1); aacirun->ptr = ptr; + + spin_unlock(&aacirun->lock); + + if (period_elapsed) + snd_pcm_period_elapsed(aacirun->substream); } } -static irqreturn_t aaci_irq(int irq, void *devid, struct pt_regs *regs) +static irqreturn_t aaci_irq(int irq, void *devid) { struct aaci *aaci = devid; u32 mask; int i; - spin_lock(&aaci->lock); mask = readl(aaci->base + AACI_ALLINTS); if (mask) { u32 m = mask; for (i = 0; i < 4; i++, m >>= 7) { if (m & 0x7f) { - aaci_fifo_irq(aaci, m); + aaci_fifo_irq(aaci, i, m); } } } - spin_unlock(&aaci->lock); return mask ? IRQ_HANDLED : IRQ_NONE; } @@ -247,64 +348,7 @@ static irqreturn_t aaci_irq(int irq, void *devid, struct pt_regs *regs) /* * ALSA support. */ - -struct aaci_stream { - unsigned char codec_idx; - unsigned char rate_idx; -}; - -static struct aaci_stream aaci_streams[] = { - [ACSTREAM_FRONT] = { - .codec_idx = 0, - .rate_idx = AC97_RATES_FRONT_DAC, - }, - [ACSTREAM_SURROUND] = { - .codec_idx = 0, - .rate_idx = AC97_RATES_SURR_DAC, - }, - [ACSTREAM_LFE] = { - .codec_idx = 0, - .rate_idx = AC97_RATES_LFE_DAC, - }, -}; - -static inline unsigned int aaci_rate_mask(struct aaci *aaci, int streamid) -{ - struct aaci_stream *s = aaci_streams + streamid; - return aaci->ac97_bus->codec[s->codec_idx]->rates[s->rate_idx]; -} - -static unsigned int rate_list[] = { - 5512, 8000, 11025, 16000, 22050, 32000, 44100, - 48000, 64000, 88200, 96000, 176400, 192000 -}; - -/* - * Double-rate rule: we can support double rate iff channels == 2 - * (unimplemented) - */ -static int -aaci_rule_rate_by_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule) -{ - struct aaci *aaci = rule->private; - unsigned int rate_mask = SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_5512; - snd_interval_t *c = hw_param_interval(p, SNDRV_PCM_HW_PARAM_CHANNELS); - - switch (c->max) { - case 6: - rate_mask &= aaci_rate_mask(aaci, ACSTREAM_LFE); - case 4: - rate_mask &= aaci_rate_mask(aaci, ACSTREAM_SURROUND); - case 2: - rate_mask &= aaci_rate_mask(aaci, ACSTREAM_FRONT); - } - - return snd_interval_list(hw_param_interval(p, rule->var), - ARRAY_SIZE(rate_list), rate_list, - rate_mask); -} - -static snd_pcm_hardware_t aaci_hw_info = { +static struct snd_pcm_hardware aaci_hw_info = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | @@ -317,12 +361,9 @@ static snd_pcm_hardware_t aaci_hw_info = { */ .formats = SNDRV_PCM_FMTBIT_S16_LE, - /* should this be continuous or knot? */ - .rates = SNDRV_PCM_RATE_CONTINUOUS, - .rate_max = 48000, - .rate_min = 4000, + /* rates are setup from the AC'97 codec */ .channels_min = 2, - .channels_max = 6, + .channels_max = 2, .buffer_bytes_max = 64 * 1024, .period_bytes_min = 256, .period_bytes_max = PAGE_SIZE, @@ -330,44 +371,84 @@ static snd_pcm_hardware_t aaci_hw_info = { .periods_max = PAGE_SIZE / 16, }; -static int aaci_pcm_open(struct aaci *aaci, snd_pcm_substream_t *substream, - struct aaci_runtime *aacirun) +/* + * We can support two and four channel audio. Unfortunately + * six channel audio requires a non-standard channel ordering: + * 2 -> FL(3), FR(4) + * 4 -> FL(3), FR(4), SL(7), SR(8) + * 6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required) + * FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual) + * This requires an ALSA configuration file to correct. + */ +static int aaci_rule_channels(struct snd_pcm_hw_params *p, + struct snd_pcm_hw_rule *rule) { - snd_pcm_runtime_t *runtime = substream->runtime; - int ret; + static unsigned int channel_list[] = { 2, 4, 6 }; + struct aaci *aaci = rule->private; + unsigned int mask = 1 << 0, slots; + + /* pcms[0] is the our 5.1 PCM instance. */ + slots = aaci->ac97_bus->pcms[0].r[0].slots; + if (slots & (1 << AC97_SLOT_PCM_SLEFT)) { + mask |= 1 << 1; + if (slots & (1 << AC97_SLOT_LFE)) + mask |= 1 << 2; + } + + return snd_interval_list(hw_param_interval(p, rule->var), + ARRAY_SIZE(channel_list), channel_list, mask); +} + +static int aaci_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aaci *aaci = substream->private_data; + struct aaci_runtime *aacirun; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aacirun = &aaci->playback; + } else { + aacirun = &aaci->capture; + } aacirun->substream = substream; runtime->private_data = aacirun; runtime->hw = aaci_hw_info; + runtime->hw.rates = aacirun->pcm->rates; + snd_pcm_limit_hw_rates(runtime); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + runtime->hw.channels_max = 6; + + /* Add rule describing channel dependency. */ + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + aaci_rule_channels, aaci, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret) + return ret; + + if (aacirun->pcm->r[1].slots) + snd_ac97_pcm_double_rate_rules(runtime); + } /* - * FIXME: ALSA specifies fifo_size in bytes. If we're in normal - * mode, each 32-bit word contains one sample. If we're in - * compact mode, each 32-bit word contains two samples, effectively - * halving the FIFO size. However, we don't know for sure which - * we'll be using at this point. We set this to the lower limit. - */ - runtime->hw.fifo_size = aaci->fifosize * 2; - - /* - * Add rule describing hardware rate dependency - * on the number of channels. + * ALSA wants the byte-size of the FIFOs. As we only support + * 16-bit samples, this is twice the FIFO depth irrespective + * of whether it's in compact mode or not. */ - ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - aaci_rule_rate_by_channels, aaci, - SNDRV_PCM_HW_PARAM_CHANNELS, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (ret) - goto out; - - ret = request_irq(aaci->dev->irq[0], aaci_irq, SA_SHIRQ|SA_INTERRUPT, - DRIVER_NAME, aaci); - if (ret) - goto out; - - return 0; + runtime->hw.fifo_size = aaci->fifo_depth * 2; + + mutex_lock(&aaci->irq_lock); + if (!aaci->users++) { + ret = request_irq(aaci->dev->irq[0], aaci_irq, + IRQF_SHARED, DRIVER_NAME, aaci); + if (ret != 0) + aaci->users--; + } + mutex_unlock(&aaci->irq_lock); - out: return ret; } @@ -375,27 +456,31 @@ static int aaci_pcm_open(struct aaci *aaci, snd_pcm_substream_t *substream, /* * Common ALSA stuff */ -static int aaci_pcm_close(snd_pcm_substream_t *substream) +static int aaci_pcm_close(struct snd_pcm_substream *substream) { struct aaci *aaci = substream->private_data; struct aaci_runtime *aacirun = substream->runtime->private_data; - WARN_ON(aacirun->cr & TXCR_TXEN); + WARN_ON(aacirun->cr & CR_EN); aacirun->substream = NULL; - free_irq(aaci->dev->irq[0], aaci); + + mutex_lock(&aaci->irq_lock); + if (!--aaci->users) + free_irq(aaci->dev->irq[0], aaci); + mutex_unlock(&aaci->irq_lock); return 0; } -static int aaci_pcm_hw_free(snd_pcm_substream_t *substream) +static int aaci_pcm_hw_free(struct snd_pcm_substream *substream) { struct aaci_runtime *aacirun = substream->runtime->private_data; /* * This must not be called with the device enabled. */ - WARN_ON(aacirun->cr & TXCR_TXEN); + WARN_ON(aacirun->cr & CR_EN); if (aacirun->pcm_open) snd_ac97_pcm_close(aacirun->pcm); @@ -404,200 +489,220 @@ static int aaci_pcm_hw_free(snd_pcm_substream_t *substream) /* * Clear out the DMA and any allocated buffers. */ - devdma_hw_free(NULL, substream); + snd_pcm_lib_free_pages(substream); return 0; } -static int aaci_pcm_hw_params(snd_pcm_substream_t *substream, - struct aaci_runtime *aacirun, - snd_pcm_hw_params_t *params) +/* Channel to slot mask */ +static const u32 channels_to_slotmask[] = { + [2] = CR_SL3 | CR_SL4, + [4] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8, + [6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9, +}; + +static int aaci_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { + struct aaci_runtime *aacirun = substream->runtime->private_data; + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + int dbl = rate > 48000; int err; aaci_pcm_hw_free(substream); + if (aacirun->pcm_open) { + snd_ac97_pcm_close(aacirun->pcm); + aacirun->pcm_open = 0; + } - err = devdma_hw_alloc(NULL, substream, - params_buffer_bytes(params)); - if (err < 0) - goto out; + /* channels is already limited to 2, 4, or 6 by aaci_rule_channels */ + if (dbl && channels != 2) + return -EINVAL; - err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params), - params_channels(params), - aacirun->pcm->r[0].slots); - if (err) - goto out; + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(params)); + if (err >= 0) { + struct aaci *aaci = substream->private_data; - aacirun->pcm_open = 1; + err = snd_ac97_pcm_open(aacirun->pcm, rate, channels, + aacirun->pcm->r[dbl].slots); + + aacirun->pcm_open = err == 0; + aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16; + aacirun->cr |= channels_to_slotmask[channels + dbl * 2]; + + /* + * fifo_bytes is the number of bytes we transfer to/from + * the FIFO, including padding. So that's x4. As we're + * in compact mode, the FIFO is half the size. + */ + aacirun->fifo_bytes = aaci->fifo_depth * 4 / 2; + } - out: return err; } -static int aaci_pcm_prepare(snd_pcm_substream_t *substream) +static int aaci_pcm_prepare(struct snd_pcm_substream *substream) { - snd_pcm_runtime_t *runtime = substream->runtime; + struct snd_pcm_runtime *runtime = substream->runtime; struct aaci_runtime *aacirun = runtime->private_data; - aacirun->start = (void *)runtime->dma_area; - aacirun->end = aacirun->start + runtime->dma_bytes; + aacirun->period = snd_pcm_lib_period_bytes(substream); + aacirun->start = runtime->dma_area; + aacirun->end = aacirun->start + snd_pcm_lib_buffer_bytes(substream); aacirun->ptr = aacirun->start; - aacirun->period = - aacirun->bytes = frames_to_bytes(runtime, runtime->period_size); + aacirun->bytes = aacirun->period; return 0; } -static snd_pcm_uframes_t aaci_pcm_pointer(snd_pcm_substream_t *substream) +static snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream) { - snd_pcm_runtime_t *runtime = substream->runtime; + struct snd_pcm_runtime *runtime = substream->runtime; struct aaci_runtime *aacirun = runtime->private_data; ssize_t bytes = aacirun->ptr - aacirun->start; return bytes_to_frames(runtime, bytes); } -static int aaci_pcm_mmap(snd_pcm_substream_t *substream, struct vm_area_struct *vma) -{ - return devdma_mmap(NULL, substream, vma); -} - /* * Playback specific ALSA stuff */ -static const u32 channels_to_txmask[] = { - [2] = TXCR_TX3 | TXCR_TX4, - [4] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8, - [6] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8 | TXCR_TX6 | TXCR_TX9, -}; - -/* - * We can support two and four channel audio. Unfortunately - * six channel audio requires a non-standard channel ordering: - * 2 -> FL(3), FR(4) - * 4 -> FL(3), FR(4), SL(7), SR(8) - * 6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required) - * FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual) - * This requires an ALSA configuration file to correct. - */ -static unsigned int channel_list[] = { 2, 4, 6 }; - -static int -aaci_rule_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule) +static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun) { - struct aaci *aaci = rule->private; - unsigned int chan_mask = 1 << 0, slots; - - /* - * pcms[0] is the our 5.1 PCM instance. - */ - slots = aaci->ac97_bus->pcms[0].r[0].slots; - if (slots & (1 << AC97_SLOT_PCM_SLEFT)) { - chan_mask |= 1 << 1; - if (slots & (1 << AC97_SLOT_LFE)) - chan_mask |= 1 << 2; - } + u32 ie; - return snd_interval_list(hw_param_interval(p, rule->var), - ARRAY_SIZE(channel_list), channel_list, - chan_mask); + ie = readl(aacirun->base + AACI_IE); + ie &= ~(IE_URIE|IE_TXIE); + writel(ie, aacirun->base + AACI_IE); + aacirun->cr &= ~CR_EN; + aaci_chan_wait_ready(aacirun, SR_TXB); + writel(aacirun->cr, aacirun->base + AACI_TXCR); } -static int aaci_pcm_playback_open(snd_pcm_substream_t *substream) +static void aaci_pcm_playback_start(struct aaci_runtime *aacirun) { - struct aaci *aaci = substream->private_data; - int ret; + u32 ie; - /* - * Add rule describing channel dependency. - */ - ret = snd_pcm_hw_rule_add(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - aaci_rule_channels, aaci, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (ret) - return ret; + aaci_chan_wait_ready(aacirun, SR_TXB); + aacirun->cr |= CR_EN; - return aaci_pcm_open(aaci, substream, &aaci->playback); + ie = readl(aacirun->base + AACI_IE); + ie |= IE_URIE | IE_TXIE; + writel(ie, aacirun->base + AACI_IE); + writel(aacirun->cr, aacirun->base + AACI_TXCR); } -static int aaci_pcm_playback_hw_params(snd_pcm_substream_t *substream, - snd_pcm_hw_params_t *params) +static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { - struct aaci *aaci = substream->private_data; struct aaci_runtime *aacirun = substream->runtime->private_data; - unsigned int channels = params_channels(params); - int ret; + unsigned long flags; + int ret = 0; - WARN_ON(channels >= ARRAY_SIZE(channels_to_txmask) || - !channels_to_txmask[channels]); + spin_lock_irqsave(&aacirun->lock, flags); - ret = aaci_pcm_hw_params(substream, aacirun, params); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + aaci_pcm_playback_start(aacirun); + break; - /* - * Enable FIFO, compact mode, 16 bits per sample. - * FIXME: double rate slots? - */ - if (ret >= 0) { - aacirun->cr = TXCR_FEN | TXCR_COMPACT | TXCR_TSZ16; - aacirun->cr |= channels_to_txmask[channels]; + case SNDRV_PCM_TRIGGER_RESUME: + aaci_pcm_playback_start(aacirun); + break; - aacirun->fifosz = aaci->fifosize * 4; - if (aacirun->cr & TXCR_COMPACT) - aacirun->fifosz >>= 1; + case SNDRV_PCM_TRIGGER_STOP: + aaci_pcm_playback_stop(aacirun); + break; + + case SNDRV_PCM_TRIGGER_SUSPEND: + aaci_pcm_playback_stop(aacirun); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + break; + + default: + ret = -EINVAL; } + + spin_unlock_irqrestore(&aacirun->lock, flags); + return ret; } -static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun) +static struct snd_pcm_ops aaci_playback_ops = { + .open = aaci_pcm_open, + .close = aaci_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = aaci_pcm_hw_params, + .hw_free = aaci_pcm_hw_free, + .prepare = aaci_pcm_prepare, + .trigger = aaci_pcm_playback_trigger, + .pointer = aaci_pcm_pointer, +}; + +static void aaci_pcm_capture_stop(struct aaci_runtime *aacirun) { u32 ie; + aaci_chan_wait_ready(aacirun, SR_RXB); + ie = readl(aacirun->base + AACI_IE); - ie &= ~(IE_URIE|IE_TXIE); - writel(ie, aacirun->base + AACI_IE); - aacirun->cr &= ~TXCR_TXEN; - aaci_chan_wait_ready(aacirun); - writel(aacirun->cr, aacirun->base + AACI_TXCR); + ie &= ~(IE_ORIE | IE_RXIE); + writel(ie, aacirun->base+AACI_IE); + + aacirun->cr &= ~CR_EN; + + writel(aacirun->cr, aacirun->base + AACI_RXCR); } -static void aaci_pcm_playback_start(struct aaci_runtime *aacirun) +static void aaci_pcm_capture_start(struct aaci_runtime *aacirun) { u32 ie; - aaci_chan_wait_ready(aacirun); - aacirun->cr |= TXCR_TXEN; + aaci_chan_wait_ready(aacirun, SR_RXB); + +#ifdef DEBUG + /* RX Timeout value: bits 28:17 in RXCR */ + aacirun->cr |= 0xf << 17; +#endif + + aacirun->cr |= CR_EN; + writel(aacirun->cr, aacirun->base + AACI_RXCR); ie = readl(aacirun->base + AACI_IE); - ie |= IE_URIE | IE_TXIE; + ie |= IE_ORIE |IE_RXIE; // overrun and rx interrupt -- half full writel(ie, aacirun->base + AACI_IE); - writel(aacirun->cr, aacirun->base + AACI_TXCR); } -static int aaci_pcm_playback_trigger(snd_pcm_substream_t *substream, int cmd) +static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { - struct aaci *aaci = substream->private_data; struct aaci_runtime *aacirun = substream->runtime->private_data; unsigned long flags; int ret = 0; - spin_lock_irqsave(&aaci->lock, flags); + spin_lock_irqsave(&aacirun->lock, flags); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: - aaci_pcm_playback_start(aacirun); + aaci_pcm_capture_start(aacirun); break; case SNDRV_PCM_TRIGGER_RESUME: - aaci_pcm_playback_start(aacirun); + aaci_pcm_capture_start(aacirun); break; case SNDRV_PCM_TRIGGER_STOP: - aaci_pcm_playback_stop(aacirun); + aaci_pcm_capture_stop(aacirun); break; case SNDRV_PCM_TRIGGER_SUSPEND: - aaci_pcm_playback_stop(aacirun); + aaci_pcm_capture_stop(aacirun); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -609,69 +714,80 @@ static int aaci_pcm_playback_trigger(snd_pcm_substream_t *substream, int cmd) default: ret = -EINVAL; } - spin_unlock_irqrestore(&aaci->lock, flags); + + spin_unlock_irqrestore(&aacirun->lock, flags); return ret; } -static snd_pcm_ops_t aaci_playback_ops = { - .open = aaci_pcm_playback_open, +static int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct aaci *aaci = substream->private_data; + + aaci_pcm_prepare(substream); + + /* allow changing of sample rate */ + aaci_ac97_write(aaci->ac97, AC97_EXTENDED_STATUS, 0x0001); /* VRA */ + aaci_ac97_write(aaci->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + aaci_ac97_write(aaci->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate); + + /* Record select: Mic: 0, Aux: 3, Line: 4 */ + aaci_ac97_write(aaci->ac97, AC97_REC_SEL, 0x0404); + + return 0; +} + +static struct snd_pcm_ops aaci_capture_ops = { + .open = aaci_pcm_open, .close = aaci_pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = aaci_pcm_playback_hw_params, + .hw_params = aaci_pcm_hw_params, .hw_free = aaci_pcm_hw_free, - .prepare = aaci_pcm_prepare, - .trigger = aaci_pcm_playback_trigger, + .prepare = aaci_pcm_capture_prepare, + .trigger = aaci_pcm_capture_trigger, .pointer = aaci_pcm_pointer, - .mmap = aaci_pcm_mmap, }; - - /* * Power Management. */ #ifdef CONFIG_PM -static int aaci_do_suspend(snd_card_t *card, unsigned int state) +static int aaci_do_suspend(struct snd_card *card) { struct aaci *aaci = card->private_data; - if (aaci->card->power_state != SNDRV_CTL_POWER_D3cold) { - snd_pcm_suspend_all(aaci->pcm); - snd_power_change_state(aaci->card, SNDRV_CTL_POWER_D3cold); - } + snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); + snd_pcm_suspend_all(aaci->pcm); return 0; } -static int aaci_do_resume(snd_card_t *card, unsigned int state) +static int aaci_do_resume(struct snd_card *card) { - struct aaci *aaci = card->private_data; - if (aaci->card->power_state != SNDRV_CTL_POWER_D0) { - snd_power_change_state(aaci->card, SNDRV_CTL_POWER_D0); - } + snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -static int aaci_suspend(struct amba_device *dev, pm_message_t state) +static int aaci_suspend(struct device *dev) { - snd_card_t *card = amba_get_drvdata(dev); + struct snd_card *card = dev_get_drvdata(dev); return card ? aaci_do_suspend(card) : 0; } -static int aaci_resume(struct amba_device *dev) +static int aaci_resume(struct device *dev) { - snd_card_t *card = amba_get_drvdata(dev); + struct snd_card *card = dev_get_drvdata(dev); return card ? aaci_do_resume(card) : 0; } + +static SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume); +#define AACI_DEV_PM_OPS (&aaci_dev_pm_ops) #else -#define aaci_do_suspend NULL -#define aaci_do_resume NULL -#define aaci_suspend NULL -#define aaci_resume NULL +#define AACI_DEV_PM_OPS NULL #endif -static struct ac97_pcm ac97_defs[] __devinitdata = { - [0] = { /* Front PCM */ +static struct ac97_pcm ac97_defs[] = { + [0] = { /* Front PCM */ .exclusive = 1, .r = { [0] = { @@ -682,6 +798,12 @@ static struct ac97_pcm ac97_defs[] __devinitdata = { (1 << AC97_SLOT_PCM_SRIGHT) | (1 << AC97_SLOT_LFE), }, + [1] = { + .slots = (1 << AC97_SLOT_PCM_LEFT) | + (1 << AC97_SLOT_PCM_RIGHT) | + (1 << AC97_SLOT_PCM_LEFT_0) | + (1 << AC97_SLOT_PCM_RIGHT_0), + }, }, }, [1] = { /* PCM in */ @@ -705,16 +827,16 @@ static struct ac97_pcm ac97_defs[] __devinitdata = { } }; -static ac97_bus_ops_t aaci_bus_ops = { +static struct snd_ac97_bus_ops aaci_bus_ops = { .write = aaci_ac97_write, .read = aaci_ac97_read, }; -static int __devinit aaci_probe_ac97(struct aaci *aaci) +static int aaci_probe_ac97(struct aaci *aaci) { - ac97_template_t ac97_template; - ac97_bus_t *ac97_bus; - ac97_t *ac97; + struct snd_ac97_template ac97_template; + struct snd_ac97_bus *ac97_bus; + struct snd_ac97 *ac97; int ret; /* @@ -728,7 +850,7 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci) * Give the AC'97 codec more than enough time * to wake up. (42us = ~2 frames at 48kHz.) */ - udelay(42); + udelay(FRAME_PERIOD_US * 2); ret = snd_ac97_bus(aaci->card, 0, &aaci_bus_ops, aaci, &ac97_bus); if (ret) @@ -737,7 +859,7 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci) ac97_bus->clock = 48000; aaci->ac97_bus = ac97_bus; - memset(&ac97_template, 0, sizeof(ac97_template_t)); + memset(&ac97_template, 0, sizeof(struct snd_ac97_template)); ac97_template.private_data = aaci; ac97_template.num = 0; ac97_template.scaps = AC97_SCAP_SKIP_MODEM; @@ -745,6 +867,7 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci) ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97); if (ret) goto out; + aaci->ac97 = ac97; /* * Disable AC97 PC Beep input on audio codecs. @@ -757,40 +880,42 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci) goto out; aaci->playback.pcm = &ac97_bus->pcms[0]; + aaci->capture.pcm = &ac97_bus->pcms[1]; out: return ret; } -static void aaci_free_card(snd_card_t *card) +static void aaci_free_card(struct snd_card *card) { struct aaci *aaci = card->private_data; if (aaci->base) iounmap(aaci->base); } -static struct aaci * __devinit aaci_init_card(struct amba_device *dev) +static struct aaci *aaci_init_card(struct amba_device *dev) { struct aaci *aaci; - snd_card_t *card; + struct snd_card *card; + int err; - card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, sizeof(struct aaci)); - if (card == NULL) - return ERR_PTR(-ENOMEM); + err = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, sizeof(struct aaci), &card); + if (err < 0) + return NULL; card->private_free = aaci_free_card; - snd_card_set_pm_callback(card, aaci_do_suspend, aaci_do_resume, NULL); strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver)); strlcpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), - "%s at 0x%08lx, irq %d", - card->shortname, dev->res.start, dev->irq[0]); + "%s PL%03x rev%u at 0x%08llx, irq %d", + card->shortname, amba_part(dev), amba_rev(dev), + (unsigned long long)dev->res.start, dev->irq[0]); aaci = card->private_data; - init_MUTEX(&aaci->ac97_sem); - spin_lock_init(&aaci->lock); + mutex_init(&aaci->ac97_sem); + mutex_init(&aaci->irq_lock); aaci->card = card; aaci->dev = dev; @@ -801,12 +926,12 @@ static struct aaci * __devinit aaci_init_card(struct amba_device *dev) return aaci; } -static int __devinit aaci_init_pcm(struct aaci *aaci) +static int aaci_init_pcm(struct aaci *aaci) { - snd_pcm_t *pcm; + struct snd_pcm *pcm; int ret; - ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 0, &pcm); + ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 1, &pcm); if (ret == 0) { aaci->pcm = pcm; pcm->private_data = aaci; @@ -815,22 +940,29 @@ static int __devinit aaci_init_pcm(struct aaci *aaci) strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name)); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + NULL, 0, 64 * 1024); } return ret; } -static unsigned int __devinit aaci_size_fifo(struct aaci *aaci) +static unsigned int aaci_size_fifo(struct aaci *aaci) { - void __iomem *base = aaci->base + AACI_CSCH1; + struct aaci_runtime *aacirun = &aaci->playback; int i; - writel(TXCR_FEN | TXCR_TSZ16 | TXCR_TXEN, base + AACI_TXCR); + /* + * Enable the channel, but don't assign it to any slots, so + * it won't empty onto the AC'97 link. + */ + writel(CR_FEN | CR_SZ16 | CR_EN, aacirun->base + AACI_TXCR); - for (i = 0; !(readl(base + AACI_SR) & SR_TXFF) && i < 4096; i++) - writel(0, aaci->base + AACI_DR1); + for (i = 0; !(readl(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++) + writel(0, aacirun->fifo); - writel(0, base + AACI_TXCR); + writel(0, aacirun->base + AACI_TXCR); /* * Re-initialise the AACI after the FIFO depth test, to @@ -838,10 +970,12 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci) * disabling the channel doesn't clear the FIFO. */ writel(aaci->maincr & ~MAINCR_IE, aaci->base + AACI_MAINCR); + readl(aaci->base + AACI_MAINCR); + udelay(1); writel(aaci->maincr, aaci->base + AACI_MAINCR); /* - * If we hit 4096, we failed. Go back to the specified + * If we hit 4096 entries, we failed. Go back to the specified * fifo depth. */ if (i == 4096) @@ -850,7 +984,8 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci) return i; } -static int __devinit aaci_probe(struct amba_device *dev, void *id) +static int aaci_probe(struct amba_device *dev, + const struct amba_id *id) { struct aaci *aaci; int ret, i; @@ -860,12 +995,12 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id) return ret; aaci = aaci_init_card(dev); - if (IS_ERR(aaci)) { - ret = PTR_ERR(aaci); + if (!aaci) { + ret = -ENOMEM; goto out; } - aaci->base = ioremap(dev->res.start, SZ_4K); + aaci->base = ioremap(dev->res.start, resource_size(&dev->res)); if (!aaci->base) { ret = -ENOMEM; goto out; @@ -874,9 +1009,17 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id) /* * Playback uses AACI channel 0 */ + spin_lock_init(&aaci->playback.lock); aaci->playback.base = aaci->base + AACI_CSCH1; aaci->playback.fifo = aaci->base + AACI_DR1; + /* + * Capture uses AACI channel 0 + */ + spin_lock_init(&aaci->capture.lock); + aaci->capture.base = aaci->base + AACI_CSCH1; + aaci->capture.fifo = aaci->base + AACI_DR1; + for (i = 0; i < 4; i++) { void __iomem *base = aaci->base + i * 0x14; @@ -887,26 +1030,35 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id) writel(0x1fff, aaci->base + AACI_INTCLR); writel(aaci->maincr, aaci->base + AACI_MAINCR); - /* - * Size the FIFOs. + * Fix: ac97 read back fail errors by reading + * from any arbitrary aaci register. */ - aaci->fifosize = aaci_size_fifo(aaci); - + readl(aaci->base + AACI_CSCH1); ret = aaci_probe_ac97(aaci); if (ret) goto out; + /* + * Size the FIFOs (must be multiple of 16). + * This is the number of entries in the FIFO. + */ + aaci->fifo_depth = aaci_size_fifo(aaci); + if (aaci->fifo_depth & 15) { + printk(KERN_WARNING "AACI: FIFO depth %d not supported\n", + aaci->fifo_depth); + ret = -ENODEV; + goto out; + } + ret = aaci_init_pcm(aaci); if (ret) goto out; - snd_card_set_dev(aaci->card, &dev->dev); - ret = snd_card_register(aaci->card); if (ret == 0) { - dev_info(&dev->dev, "%s, fifo %d\n", aaci->card->longname, - aaci->fifosize); + dev_info(&dev->dev, "%s\n", aaci->card->longname); + dev_info(&dev->dev, "FIFO %u entries\n", aaci->fifo_depth); amba_set_drvdata(dev, aaci->card); return ret; } @@ -918,11 +1070,9 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id) return ret; } -static int __devexit aaci_remove(struct amba_device *dev) +static int aaci_remove(struct amba_device *dev) { - snd_card_t *card = amba_get_drvdata(dev); - - amba_set_drvdata(dev, NULL); + struct snd_card *card = amba_get_drvdata(dev); if (card) { struct aaci *aaci = card->private_data; @@ -943,29 +1093,19 @@ static struct amba_id aaci_ids[] = { { 0, 0 }, }; +MODULE_DEVICE_TABLE(amba, aaci_ids); + static struct amba_driver aaci_driver = { .drv = { .name = DRIVER_NAME, + .pm = AACI_DEV_PM_OPS, }, .probe = aaci_probe, - .remove = __devexit_p(aaci_remove), - .suspend = aaci_suspend, - .resume = aaci_resume, + .remove = aaci_remove, .id_table = aaci_ids, }; -static int __init aaci_init(void) -{ - return amba_driver_register(&aaci_driver); -} - -static void __exit aaci_exit(void) -{ - amba_driver_unregister(&aaci_driver); -} - -module_init(aaci_init); -module_exit(aaci_exit); +module_amba_driver(aaci_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ARM PrimeCell PL041 Advanced Audio CODEC Interface driver"); |
