diff options
Diffstat (limited to 'sound/arm')
| -rw-r--r-- | sound/arm/Makefile | 2 | ||||
| -rw-r--r-- | sound/arm/aaci.c | 580 | ||||
| -rw-r--r-- | sound/arm/aaci.h | 11 | ||||
| -rw-r--r-- | sound/arm/devdma.c | 80 | ||||
| -rw-r--r-- | sound/arm/devdma.h | 3 | ||||
| -rw-r--r-- | sound/arm/pxa2xx-ac97-lib.c | 123 | ||||
| -rw-r--r-- | sound/arm/pxa2xx-ac97.c | 88 | ||||
| -rw-r--r-- | sound/arm/pxa2xx-pcm-lib.c | 62 | ||||
| -rw-r--r-- | sound/arm/pxa2xx-pcm.c | 18 | ||||
| -rw-r--r-- | sound/arm/pxa2xx-pcm.h | 9 |
10 files changed, 402 insertions, 574 deletions
diff --git a/sound/arm/Makefile b/sound/arm/Makefile index 5a549ed6c8a..8c0c851d464 100644 --- a/sound/arm/Makefile +++ b/sound/arm/Makefile @@ -3,7 +3,7 @@ # obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o -snd-aaci-objs := aaci.o devdma.o +snd-aaci-objs := aaci.o obj-$(CONFIG_SND_PXA2XX_PCM) += snd-pxa2xx-pcm.o snd-pxa2xx-pcm-objs := pxa2xx-pcm.o diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index 5c48e36038f..0e83a73efb1 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -18,10 +18,7 @@ #include <linux/interrupt.h> #include <linux/err.h> #include <linux/amba/bus.h> - -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/sizes.h> +#include <linux/io.h> #include <sound/core.h> #include <sound/initval.h> @@ -30,10 +27,11 @@ #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. */ @@ -52,7 +50,11 @@ static void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *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); + } } /* @@ -68,8 +70,8 @@ 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; - int timeout = 5000; if (ac97->num >= 4) return; @@ -85,14 +87,17 @@ static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg, 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)) && --timeout); - if (!timeout) + if (v & (SLFR_1TXB|SLFR_2TXB)) dev_err(&aaci->dev->dev, "timeout waiting for write to complete\n"); @@ -105,9 +110,8 @@ static void aaci_ac97_write(struct snd_ac97 *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; - int timeout = 5000; - int retries = 10; if (ac97->num >= 4) return ~0; @@ -121,35 +125,34 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *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) && --timeout); - if (!timeout) { + if (v & SLFR_1TXB) { dev_err(&aaci->dev->dev, "timeout on slot 1 TX busy\n"); v = ~0; goto out; } - /* - * Give the AC'97 codec more than enough time - * to respond. (42us = ~2 frames at 48kHz.) - */ - udelay(42); + /* Now wait for the response frame */ + udelay(FRAME_PERIOD_US); - /* - * Wait for slot 2 to indicate data. - */ - timeout = 5000; + /* 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)) && --timeout); - if (!timeout) { + if (v != (SLFR_1RXV|SLFR_2RXV)) { dev_err(&aaci->dev->dev, "timeout on RX valid\n"); v = ~0; goto out; @@ -176,14 +179,16 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg) 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--); } @@ -205,6 +210,7 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask) if (mask & ISR_RXINTR) { struct aaci_runtime *aacirun = &aaci->capture; + bool period_elapsed = false; void *ptr; if (!aacirun->substream || !aacirun->start) { @@ -212,18 +218,17 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask) writel(0, aacirun->base + AACI_IE); return; } - ptr = aacirun->ptr; + 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 & CR_EN)) break; @@ -249,7 +254,13 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask) 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) { @@ -259,6 +270,7 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask) if (mask & ISR_TXINTR) { struct aaci_runtime *aacirun = &aaci->playback; + bool period_elapsed = false; void *ptr; if (!aacirun->substream || !aacirun->start) { @@ -267,17 +279,16 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask) 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 & CR_EN)) break; @@ -305,6 +316,11 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask) } while (1); aacirun->ptr = ptr; + + spin_unlock(&aacirun->lock); + + if (period_elapsed) + snd_pcm_period_elapsed(aacirun->substream); } } @@ -314,7 +330,6 @@ static irqreturn_t aaci_irq(int irq, void *devid) u32 mask; int i; - spin_lock(&aaci->lock); mask = readl(aaci->base + AACI_ALLINTS); if (mask) { u32 m = mask; @@ -324,7 +339,6 @@ static irqreturn_t aaci_irq(int irq, void *devid) } } } - spin_unlock(&aaci->lock); return mask ? IRQ_HANDLED : IRQ_NONE; } @@ -334,63 +348,6 @@ static irqreturn_t aaci_irq(int irq, void *devid) /* * 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(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *rule) -{ - struct aaci *aaci = rule->private; - unsigned int rate_mask = SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_5512; - struct snd_interval *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 struct snd_pcm_hardware aaci_hw_info = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | @@ -404,12 +361,9 @@ static struct snd_pcm_hardware 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, @@ -417,45 +371,84 @@ static struct snd_pcm_hardware aaci_hw_info = { .periods_max = PAGE_SIZE / 16, }; -static int __aaci_pcm_open(struct aaci *aaci, - struct snd_pcm_substream *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) +{ + 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; - int ret; + 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); - /* - * 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; + 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); + } /* - * 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, IRQF_SHARED|IRQF_DISABLED, - 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; } @@ -471,7 +464,11 @@ static int aaci_pcm_close(struct snd_pcm_substream *substream) 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; } @@ -492,39 +489,57 @@ static int aaci_pcm_hw_free(struct snd_pcm_substream *substream) /* * Clear out the DMA and any allocated buffers. */ - devdma_hw_free(NULL, substream); + snd_pcm_lib_free_pages(substream); return 0; } +/* 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 aaci_runtime *aacirun, 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; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params), - params_channels(params), - aacirun->pcm->r[0].slots); - else - err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params), - params_channels(params), - aacirun->pcm->r[1].slots); + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(params)); + if (err >= 0) { + struct aaci *aaci = substream->private_data; - if (err) - goto out; + err = snd_ac97_pcm_open(aacirun->pcm, rate, channels, + aacirun->pcm->r[dbl].slots); - aacirun->pcm_open = 1; + 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; } @@ -533,11 +548,11 @@ static int aaci_pcm_prepare(struct snd_pcm_substream *substream) 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; } @@ -551,104 +566,10 @@ static snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(runtime, bytes); } -static int aaci_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) -{ - return devdma_mmap(NULL, substream, vma); -} - /* * Playback specific ALSA stuff */ -static const u32 channels_to_txmask[] = { - [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, -}; - -/* - * 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(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *rule) -{ - 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; - } - - return snd_interval_list(hw_param_interval(p, rule->var), - ARRAY_SIZE(channel_list), channel_list, - chan_mask); -} - -static int aaci_pcm_open(struct snd_pcm_substream *substream) -{ - struct aaci *aaci = substream->private_data; - int ret; - - /* - * 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 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ret = __aaci_pcm_open(aaci, substream, &aaci->playback); - } else { - ret = __aaci_pcm_open(aaci, substream, &aaci->capture); - } - return ret; -} - -static int aaci_pcm_playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct aaci *aaci = substream->private_data; - struct aaci_runtime *aacirun = substream->runtime->private_data; - unsigned int channels = params_channels(params); - int ret; - - WARN_ON(channels >= ARRAY_SIZE(channels_to_txmask) || - !channels_to_txmask[channels]); - - ret = aaci_pcm_hw_params(substream, aacirun, params); - - /* - * Enable FIFO, compact mode, 16 bits per sample. - * FIXME: double rate slots? - */ - if (ret >= 0) { - aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16; - aacirun->cr |= channels_to_txmask[channels]; - - aacirun->fifosz = aaci->fifosize * 4; - if (aacirun->cr & CR_COMPACT) - aacirun->fifosz >>= 1; - } - return ret; -} - static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun) { u32 ie; @@ -657,7 +578,7 @@ static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun) ie &= ~(IE_URIE|IE_TXIE); writel(ie, aacirun->base + AACI_IE); aacirun->cr &= ~CR_EN; - aaci_chan_wait_ready(aacirun); + aaci_chan_wait_ready(aacirun, SR_TXB); writel(aacirun->cr, aacirun->base + AACI_TXCR); } @@ -665,7 +586,7 @@ static void aaci_pcm_playback_start(struct aaci_runtime *aacirun) { u32 ie; - aaci_chan_wait_ready(aacirun); + aaci_chan_wait_ready(aacirun, SR_TXB); aacirun->cr |= CR_EN; ie = readl(aacirun->base + AACI_IE); @@ -676,12 +597,12 @@ static void aaci_pcm_playback_start(struct aaci_runtime *aacirun) 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 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); @@ -708,7 +629,8 @@ static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cm default: ret = -EINVAL; } - spin_unlock_irqrestore(&aaci->lock, flags); + + spin_unlock_irqrestore(&aacirun->lock, flags); return ret; } @@ -717,42 +639,18 @@ 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_playback_hw_params, + .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, - .mmap = aaci_pcm_mmap, }; -static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct aaci *aaci = substream->private_data; - struct aaci_runtime *aacirun = substream->runtime->private_data; - int ret; - - ret = aaci_pcm_hw_params(substream, aacirun, params); - - if (ret >= 0) { - aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16; - - /* Line in record: slot 3 and 4 */ - aacirun->cr |= CR_SL3 | CR_SL4; - - aacirun->fifosz = aaci->fifosize * 4; - - if (aacirun->cr & CR_COMPACT) - aacirun->fifosz >>= 1; - } - return ret; -} - static void aaci_pcm_capture_stop(struct aaci_runtime *aacirun) { u32 ie; - aaci_chan_wait_ready(aacirun); + aaci_chan_wait_ready(aacirun, SR_RXB); ie = readl(aacirun->base + AACI_IE); ie &= ~(IE_ORIE | IE_RXIE); @@ -767,7 +665,7 @@ static void aaci_pcm_capture_start(struct aaci_runtime *aacirun) { u32 ie; - aaci_chan_wait_ready(aacirun); + aaci_chan_wait_ready(aacirun, SR_RXB); #ifdef DEBUG /* RX Timeout value: bits 28:17 in RXCR */ @@ -784,12 +682,11 @@ static void aaci_pcm_capture_start(struct aaci_runtime *aacirun) 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: @@ -818,7 +715,7 @@ static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd ret = -EINVAL; } - spin_unlock_irqrestore(&aaci->lock, flags); + spin_unlock_irqrestore(&aacirun->lock, flags); return ret; } @@ -845,19 +742,18 @@ 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_capture_hw_params, + .hw_params = aaci_pcm_hw_params, .hw_free = aaci_pcm_hw_free, .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(struct snd_card *card, unsigned int state) +static int aaci_do_suspend(struct snd_card *card) { struct aaci *aaci = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); @@ -865,32 +761,32 @@ static int aaci_do_suspend(struct snd_card *card, unsigned int state) return 0; } -static int aaci_do_resume(struct snd_card *card, unsigned int state) +static int aaci_do_resume(struct snd_card *card) { 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) { - struct snd_card *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) { - struct snd_card *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 = { +static struct ac97_pcm ac97_defs[] = { [0] = { /* Front PCM */ .exclusive = 1, .r = { @@ -902,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 */ @@ -930,7 +832,7 @@ static struct snd_ac97_bus_ops aaci_bus_ops = { .read = aaci_ac97_read, }; -static int __devinit aaci_probe_ac97(struct aaci *aaci) +static int aaci_probe_ac97(struct aaci *aaci) { struct snd_ac97_template ac97_template; struct snd_ac97_bus *ac97_bus; @@ -948,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) @@ -991,14 +893,14 @@ static void aaci_free_card(struct snd_card *card) 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; struct snd_card *card; int err; - err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, sizeof(struct aaci), &card); + err = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, sizeof(struct aaci), &card); if (err < 0) return NULL; @@ -1007,13 +909,13 @@ static struct aaci * __devinit aaci_init_card(struct amba_device *dev) 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%016llx, irq %d", - card->shortname, (unsigned long long)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; mutex_init(&aaci->ac97_sem); - spin_lock_init(&aaci->lock); + mutex_init(&aaci->irq_lock); aaci->card = card; aaci->dev = dev; @@ -1024,7 +926,7 @@ 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) { struct snd_pcm *pcm; int ret; @@ -1039,16 +941,22 @@ static int __devinit aaci_init_pcm(struct aaci *aaci) 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) { struct aaci_runtime *aacirun = &aaci->playback; int i; + /* + * 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(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++) @@ -1062,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) @@ -1074,7 +984,8 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci) return i; } -static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id) +static int aaci_probe(struct amba_device *dev, + const struct amba_id *id) { struct aaci *aaci; int ret, i; @@ -1089,7 +1000,7 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id) 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; @@ -1098,12 +1009,14 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *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; @@ -1117,18 +1030,23 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id) writel(0x1fff, aaci->base + AACI_INTCLR); writel(aaci->maincr, aaci->base + AACI_MAINCR); - + /* + * Fix: ac97 read back fail errors by reading + * from any arbitrary aaci register. + */ + 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->fifosize = aaci_size_fifo(aaci); - if (aaci->fifosize & 15) { - printk(KERN_WARNING "AACI: fifosize = %d not supported\n", - aaci->fifosize); + 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; } @@ -1137,12 +1055,10 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id) 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; } @@ -1154,12 +1070,10 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id) return ret; } -static int __devexit aaci_remove(struct amba_device *dev) +static int aaci_remove(struct amba_device *dev) { struct snd_card *card = amba_get_drvdata(dev); - amba_set_drvdata(dev, NULL); - if (card) { struct aaci *aaci = card->private_data; writel(0, aaci->base + AACI_MAINCR); @@ -1179,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"); diff --git a/sound/arm/aaci.h b/sound/arm/aaci.h index 924f69c1c44..5791bd5bd2a 100644 --- a/sound/arm/aaci.h +++ b/sound/arm/aaci.h @@ -202,6 +202,7 @@ struct aaci_runtime { void __iomem *base; void __iomem *fifo; + spinlock_t lock; struct ac97_pcm *pcm; int pcm_open; @@ -209,6 +210,8 @@ struct aaci_runtime { u32 cr; struct snd_pcm_substream *substream; + unsigned int period; /* byte size of a "period" */ + /* * PIO support */ @@ -216,15 +219,16 @@ struct aaci_runtime { void *end; void *ptr; int bytes; - unsigned int period; - unsigned int fifosz; + unsigned int fifo_bytes; }; struct aaci { struct amba_device *dev; struct snd_card *card; void __iomem *base; - unsigned int fifosize; + unsigned int fifo_depth; + unsigned int users; + struct mutex irq_lock; /* AC'97 */ struct mutex ac97_sem; @@ -232,7 +236,6 @@ struct aaci { struct snd_ac97 *ac97; u32 maincr; - spinlock_t lock; struct aaci_runtime playback; struct aaci_runtime capture; diff --git a/sound/arm/devdma.c b/sound/arm/devdma.c deleted file mode 100644 index 9d1e6665b54..00000000000 --- a/sound/arm/devdma.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * linux/sound/arm/devdma.c - * - * Copyright (C) 2003-2004 Russell King, All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ARM DMA shim for ALSA. - */ -#include <linux/device.h> -#include <linux/dma-mapping.h> - -#include <sound/core.h> -#include <sound/pcm.h> - -#include "devdma.h" - -void devdma_hw_free(struct device *dev, struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_dma_buffer *buf = runtime->dma_buffer_p; - - if (runtime->dma_area == NULL) - return; - - if (buf != &substream->dma_buffer) { - dma_free_coherent(buf->dev.dev, buf->bytes, buf->area, buf->addr); - kfree(runtime->dma_buffer_p); - } - - snd_pcm_set_runtime_buffer(substream, NULL); -} - -int devdma_hw_alloc(struct device *dev, struct snd_pcm_substream *substream, size_t size) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_dma_buffer *buf = runtime->dma_buffer_p; - int ret = 0; - - if (buf) { - if (buf->bytes >= size) - goto out; - devdma_hw_free(dev, substream); - } - - if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) { - buf = &substream->dma_buffer; - } else { - buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL); - if (!buf) - goto nomem; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = dev; - buf->area = dma_alloc_coherent(dev, size, &buf->addr, GFP_KERNEL); - buf->bytes = size; - buf->private_data = NULL; - - if (!buf->area) - goto free; - } - snd_pcm_set_runtime_buffer(substream, buf); - ret = 1; - out: - runtime->dma_bytes = size; - return ret; - - free: - kfree(buf); - nomem: - return -ENOMEM; -} - -int devdma_mmap(struct device *dev, struct snd_pcm_substream *substream, struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - return dma_mmap_coherent(dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); -} diff --git a/sound/arm/devdma.h b/sound/arm/devdma.h deleted file mode 100644 index d025329c8a0..00000000000 --- a/sound/arm/devdma.h +++ /dev/null @@ -1,3 +0,0 @@ -void devdma_hw_free(struct device *dev, struct snd_pcm_substream *substream); -int devdma_hw_alloc(struct device *dev, struct snd_pcm_substream *substream, size_t size); -int devdma_mmap(struct device *dev, struct snd_pcm_substream *substream, struct vm_area_struct *vma); diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index 6fdca97186e..66de90ed30c 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -16,13 +16,15 @@ #include <linux/interrupt.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/gpio.h> #include <sound/ac97_codec.h> #include <sound/pxa2xx-lib.h> -#include <asm/irq.h> +#include <mach/irqs.h> #include <mach/regs-ac97.h> -#include <mach/pxa2xx-gpio.h> #include <mach/audio.h> static DEFINE_MUTEX(car_mutex); @@ -32,6 +34,8 @@ static struct clk *ac97_clk; static struct clk *ac97conf_clk; static int reset_gpio; +extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio); + /* * Beware PXA27x bugs: * @@ -42,45 +46,6 @@ static int reset_gpio; * 1 jiffy timeout if interrupt never comes). */ -enum { - RESETGPIO_FORCE_HIGH, - RESETGPIO_FORCE_LOW, - RESETGPIO_NORMAL_ALTFUNC -}; - -/** - * set_resetgpio_mode - computes and sets the AC97_RESET gpio mode on PXA - * @mode: chosen action - * - * As the PXA27x CPUs suffer from a AC97 bug, a manual control of the reset line - * must be done to insure proper work of AC97 reset line. This function - * computes the correct gpio_mode for further use by reset functions, and - * applied the change through pxa_gpio_mode. - */ -static void set_resetgpio_mode(int resetgpio_action) -{ - int mode = 0; - - if (reset_gpio) - switch (resetgpio_action) { - case RESETGPIO_NORMAL_ALTFUNC: - if (reset_gpio == 113) - mode = 113 | GPIO_ALT_FN_2_OUT; - if (reset_gpio == 95) - mode = 95 | GPIO_ALT_FN_1_OUT; - break; - case RESETGPIO_FORCE_LOW: - mode = reset_gpio | GPIO_OUT | GPIO_DFLT_LOW; - break; - case RESETGPIO_FORCE_HIGH: - mode = reset_gpio | GPIO_OUT | GPIO_DFLT_HIGH; - break; - }; - - if (mode) - pxa_gpio_mode(mode); -} - unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { unsigned short val = -1; @@ -152,8 +117,7 @@ static inline void pxa_ac97_warm_pxa25x(void) { gsr_bits = 0; - GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN; - wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); + GCR |= GCR_WARM_RST; } static inline void pxa_ac97_cold_pxa25x(void) @@ -164,8 +128,6 @@ static inline void pxa_ac97_cold_pxa25x(void) gsr_bits = 0; GCR = GCR_COLD_RST; - GCR |= GCR_CDONE_IE|GCR_SDONE_IE; - wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); } #endif @@ -174,12 +136,11 @@ static inline void pxa_ac97_warm_pxa27x(void) { gsr_bits = 0; - /* warm reset broken on Bulverde, - so manually keep AC97 reset high */ - set_resetgpio_mode(RESETGPIO_FORCE_HIGH); + /* warm reset broken on Bulverde, so manually keep AC97 reset high */ + pxa27x_configure_ac97reset(reset_gpio, true); udelay(10); GCR |= GCR_WARM_RST; - set_resetgpio_mode(RESETGPIO_NORMAL_ALTFUNC); + pxa27x_configure_ac97reset(reset_gpio, false); udelay(500); } @@ -194,28 +155,21 @@ static inline void pxa_ac97_cold_pxa27x(void) clk_enable(ac97conf_clk); udelay(5); clk_disable(ac97conf_clk); - GCR = GCR_COLD_RST; - udelay(50); + GCR = GCR_COLD_RST | GCR_WARM_RST; } #endif #ifdef CONFIG_PXA3xx static inline void pxa_ac97_warm_pxa3xx(void) { - int timeout = 100; - gsr_bits = 0; /* Can't use interrupts */ GCR |= GCR_WARM_RST; - while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) - mdelay(1); } static inline void pxa_ac97_cold_pxa3xx(void) { - int timeout = 1000; - /* Hold CLKBPB for 100us */ GCR = 0; GCR = GCR_CLKBPB; @@ -231,14 +185,13 @@ static inline void pxa_ac97_cold_pxa3xx(void) GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); GCR = GCR_WARM_RST | GCR_COLD_RST; - while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--) - mdelay(10); } #endif bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) { unsigned long gsr; + unsigned int timeout = 100; #ifdef CONFIG_PXA25x if (cpu_is_pxa25x()) @@ -255,7 +208,11 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) pxa_ac97_warm_pxa3xx(); else #endif - BUG(); + snd_BUG(); + + while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) + mdelay(1); + gsr = GSR | gsr_bits; if (!(gsr & (GSR_PCR | GSR_SCR))) { printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", @@ -271,6 +228,7 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset); bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) { unsigned long gsr; + unsigned int timeout = 1000; #ifdef CONFIG_PXA25x if (cpu_is_pxa25x()) @@ -287,7 +245,10 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) pxa_ac97_cold_pxa3xx(); else #endif - BUG(); + snd_BUG(); + + while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) + mdelay(1); gsr = GSR | gsr_bits; if (!(gsr & (GSR_PCR | GSR_SCR))) { @@ -345,23 +306,13 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_suspend); int pxa2xx_ac97_hw_resume(void) { - if (cpu_is_pxa25x() || cpu_is_pxa27x()) { - pxa_gpio_mode(GPIO31_SYNC_AC97_MD); - pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); - pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); - pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); - } - if (cpu_is_pxa27x()) { - /* Use GPIO 113 or 95 as AC97 Reset on Bulverde */ - set_resetgpio_mode(RESETGPIO_NORMAL_ALTFUNC); - } clk_enable(ac97_clk); return 0; } EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_resume); #endif -int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev) +int pxa2xx_ac97_hw_probe(struct platform_device *dev) { int ret; pxa2xx_audio_ops_t *pdata = dev->dev.platform_data; @@ -386,16 +337,22 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev) reset_gpio = 113; } - if (cpu_is_pxa25x() || cpu_is_pxa27x()) { - pxa_gpio_mode(GPIO31_SYNC_AC97_MD); - pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); - pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); - pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); - } - if (cpu_is_pxa27x()) { - /* Use GPIO 113 as AC97 Reset on Bulverde */ - set_resetgpio_mode(RESETGPIO_NORMAL_ALTFUNC); + /* + * This gpio is needed for a work-around to a bug in the ac97 + * controller during warm reset. The direction and level is set + * here so that it is an output driven high when switching from + * AC97_nRESET alt function to generic gpio. + */ + ret = gpio_request_one(reset_gpio, GPIOF_OUT_INIT_HIGH, + "pxa27x ac97 reset"); + if (ret < 0) { + pr_err("%s: gpio_request_one() failed: %d\n", + __func__, ret); + goto err_conf; + } + pxa27x_configure_ac97reset(reset_gpio, false); + ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK"); if (IS_ERR(ac97conf_clk)) { ret = PTR_ERR(ac97conf_clk); @@ -415,7 +372,7 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev) if (ret) goto err_clk2; - ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL); + ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL); if (ret < 0) goto err_irq; @@ -438,6 +395,8 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe); void pxa2xx_ac97_hw_remove(struct platform_device *dev) { + if (cpu_is_pxa27x()) + gpio_free(reset_gpio); GCR |= GCR_ACLINK_OFF; free_irq(IRQ_AC97, NULL); if (ac97conf_clk) { diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index c570ebd9d17..3a10df6688e 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -11,14 +11,17 @@ */ #include <linux/init.h> +#include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/dmaengine.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> #include <sound/initval.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> #include <mach/regs-ac97.h> #include <mach/audio.h> @@ -40,20 +43,20 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_reset, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = { - .name = "AC97 PCM out", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(12), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_out_req = 12; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_in = { - .name = "AC97 PCM in", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(11), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_in_req = 11; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_in_req, }; static struct snd_pcm *pxa2xx_ac97_pcm; @@ -105,9 +108,9 @@ static struct pxa2xx_pcm_client pxa2xx_ac97_pcm_client = { .prepare = pxa2xx_ac97_pcm_prepare, }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP -static int pxa2xx_ac97_do_suspend(struct snd_card *card, pm_message_t state) +static int pxa2xx_ac97_do_suspend(struct snd_card *card) { pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; @@ -137,20 +140,20 @@ static int pxa2xx_ac97_do_resume(struct snd_card *card) return 0; } -static int pxa2xx_ac97_suspend(struct platform_device *dev, pm_message_t state) +static int pxa2xx_ac97_suspend(struct device *dev) { - struct snd_card *card = platform_get_drvdata(dev); + struct snd_card *card = dev_get_drvdata(dev); int ret = 0; if (card) - ret = pxa2xx_ac97_do_suspend(card, PMSG_SUSPEND); + ret = pxa2xx_ac97_do_suspend(card); return ret; } -static int pxa2xx_ac97_resume(struct platform_device *dev) +static int pxa2xx_ac97_resume(struct device *dev) { - struct snd_card *card = platform_get_drvdata(dev); + struct snd_card *card = dev_get_drvdata(dev); int ret = 0; if (card) @@ -159,25 +162,29 @@ static int pxa2xx_ac97_resume(struct platform_device *dev) return ret; } -#else -#define pxa2xx_ac97_suspend NULL -#define pxa2xx_ac97_resume NULL +static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume); #endif -static int __devinit pxa2xx_ac97_probe(struct platform_device *dev) +static int pxa2xx_ac97_probe(struct platform_device *dev) { struct snd_card *card; struct snd_ac97_bus *ac97_bus; struct snd_ac97_template ac97_template; int ret; + pxa2xx_audio_ops_t *pdata = dev->dev.platform_data; + + if (dev->id >= 0) { + dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n"); + ret = -ENXIO; + goto err_dev; + } - ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &card); + ret = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); if (ret < 0) goto err; - card->dev = &dev->dev; - strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver)); + strlcpy(card->driver, dev->dev.driver->name, sizeof(card->driver)); ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm); if (ret) @@ -200,7 +207,8 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev) snprintf(card->longname, sizeof(card->longname), "%s (%s)", dev->dev.driver->name, card->mixername); - snd_card_set_dev(card, &dev->dev); + if (pdata && pdata->codec_pdata[0]) + snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]); ret = snd_card_register(card); if (ret == 0) { platform_set_drvdata(dev, card); @@ -212,16 +220,16 @@ err_remove: err: if (card) snd_card_free(card); +err_dev: return ret; } -static int __devexit pxa2xx_ac97_remove(struct platform_device *dev) +static int pxa2xx_ac97_remove(struct platform_device *dev) { struct snd_card *card = platform_get_drvdata(dev); if (card) { snd_card_free(card); - platform_set_drvdata(dev, NULL); pxa2xx_ac97_hw_remove(dev); } @@ -230,27 +238,17 @@ static int __devexit pxa2xx_ac97_remove(struct platform_device *dev) static struct platform_driver pxa2xx_ac97_driver = { .probe = pxa2xx_ac97_probe, - .remove = __devexit_p(pxa2xx_ac97_remove), - .suspend = pxa2xx_ac97_suspend, - .resume = pxa2xx_ac97_resume, + .remove = pxa2xx_ac97_remove, .driver = { .name = "pxa2xx-ac97", .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP + .pm = &pxa2xx_ac97_pm_ops, +#endif }, }; -static int __init pxa2xx_ac97_init(void) -{ - return platform_driver_register(&pxa2xx_ac97_driver); -} - -static void __exit pxa2xx_ac97_exit(void) -{ - platform_driver_unregister(&pxa2xx_ac97_driver); -} - -module_init(pxa2xx_ac97_init); -module_exit(pxa2xx_ac97_exit); +module_platform_driver(pxa2xx_ac97_driver); MODULE_AUTHOR("Nicolas Pitre"); MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index 108b643229b..a61d7a9a995 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -4,13 +4,16 @@ * published by the Free Software Foundation. */ +#include <linux/slab.h> #include <linux/module.h> #include <linux/dma-mapping.h> +#include <linux/dmaengine.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> #include <mach/dma.h> @@ -42,6 +45,35 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, size_t period = params_period_bytes(params); pxa_dma_desc *dma_desc; dma_addr_t dma_buff_phys, next_desc_phys; + u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; + + /* temporary transition hack */ + switch (rtd->params->addr_width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + dcmd |= DCMD_WIDTH1; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + dcmd |= DCMD_WIDTH2; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + dcmd |= DCMD_WIDTH4; + break; + default: + /* can't happen */ + break; + } + + switch (rtd->params->maxburst) { + case 8: + dcmd |= DCMD_BURST8; + break; + case 16: + dcmd |= DCMD_BURST16; + break; + case 32: + dcmd |= DCMD_BURST32; + break; + } snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = totsize; @@ -54,14 +86,14 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, dma_desc->ddadr = next_desc_phys; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dma_desc->dsadr = dma_buff_phys; - dma_desc->dtadr = rtd->params->dev_addr; + dma_desc->dtadr = rtd->params->addr; } else { - dma_desc->dsadr = rtd->params->dev_addr; + dma_desc->dsadr = rtd->params->addr; dma_desc->dtadr = dma_buff_phys; } if (period > totsize) period = totsize; - dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN; + dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN; dma_desc++; dma_buff_phys += period; } while (totsize -= period); @@ -75,8 +107,10 @@ int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - if (rtd && rtd->params) - *rtd->params->drcmr = 0; + if (rtd && rtd->params && rtd->params->filter_data) { + unsigned long req = *(unsigned long *) rtd->params->filter_data; + DRCMR(req) = 0; + } snd_pcm_set_runtime_buffer(substream, NULL); return 0; @@ -135,11 +169,19 @@ EXPORT_SYMBOL(pxa2xx_pcm_pointer); int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) { struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; + unsigned long req; + + if (!prtd || !prtd->params) + return 0; + + if (prtd->dma_ch == -1) + return -EINVAL; DCSR(prtd->dma_ch) &= ~DCSR_RUN; DCSR(prtd->dma_ch) = 0; DCMD(prtd->dma_ch) = 0; - *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD; + req = *(unsigned long *) prtd->params->filter_data; + DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD; return 0; } @@ -148,7 +190,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_prepare); void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) { struct snd_pcm_substream *substream = dev_id; - struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; int dcsr; dcsr = DCSR(dma_ch); @@ -157,9 +198,11 @@ void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) if (dcsr & DCSR_ENDINTR) { snd_pcm_period_elapsed(substream); } else { - printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", - rtd->params->name, dma_ch, dcsr); + printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n", + dma_ch, dcsr); + snd_pcm_stream_lock(substream); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock(substream); } } EXPORT_SYMBOL(pxa2xx_pcm_dma_irq); @@ -202,6 +245,7 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) if (!rtd->dma_desc_array) goto err1; + rtd->dma_ch = -1; runtime->private_data = rtd; return 0; diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c index 535704f7749..83be8e3f095 100644 --- a/sound/arm/pxa2xx-pcm.c +++ b/sound/arm/pxa2xx-pcm.c @@ -10,8 +10,15 @@ * published by the Free Software Foundation. */ +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> + +#include <mach/dma.h> + #include <sound/core.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> #include "pxa2xx-pcm.h" @@ -39,7 +46,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? client->playback_params : client->capture_params; - ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("dma", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) goto err2; @@ -79,8 +86,6 @@ static struct snd_pcm_ops pxa2xx_pcm_ops = { .mmap = pxa2xx_pcm_mmap, }; -static u64 pxa2xx_pcm_dmamask = 0xffffffff; - int pxa2xx_pcm_new(struct snd_card *card, struct pxa2xx_pcm_client *client, struct snd_pcm **rpcm) { @@ -96,10 +101,9 @@ int pxa2xx_pcm_new(struct snd_card *card, struct pxa2xx_pcm_client *client, pcm->private_data = client; pcm->private_free = pxa2xx_pcm_free_dma_buffers; - if (!card->dev->dma_mask) - card->dev->dma_mask = &pxa2xx_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = 0xffffffff; + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + goto out; if (play) { int stream = SNDRV_PCM_STREAM_PLAYBACK; diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h index 65f86b56ba4..00330985bee 100644 --- a/sound/arm/pxa2xx-pcm.h +++ b/sound/arm/pxa2xx-pcm.h @@ -9,18 +9,17 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include <mach/dma.h> struct pxa2xx_runtime_data { int dma_ch; - struct pxa2xx_pcm_dma_params *params; - pxa_dma_desc *dma_desc_array; + struct snd_dmaengine_dai_dma_data *params; + struct pxa_dma_desc *dma_desc_array; dma_addr_t dma_desc_array_phys; }; struct pxa2xx_pcm_client { - struct pxa2xx_pcm_dma_params *playback_params; - struct pxa2xx_pcm_dma_params *capture_params; + struct snd_dmaengine_dai_dma_data *playback_params; + struct snd_dmaengine_dai_dma_data *capture_params; int (*startup)(struct snd_pcm_substream *); void (*shutdown)(struct snd_pcm_substream *); int (*prepare)(struct snd_pcm_substream *); |
