aboutsummaryrefslogtreecommitdiff
path: root/sound/arm
diff options
context:
space:
mode:
Diffstat (limited to 'sound/arm')
-rw-r--r--sound/arm/Kconfig35
-rw-r--r--sound/arm/Makefile9
-rw-r--r--sound/arm/aaci.c595
-rw-r--r--sound/arm/aaci.h11
-rw-r--r--sound/arm/devdma.c80
-rw-r--r--sound/arm/devdma.h3
-rw-r--r--sound/arm/pxa2xx-ac97-lib.c415
-rw-r--r--sound/arm/pxa2xx-ac97.c288
-rw-r--r--sound/arm/pxa2xx-pcm-lib.c321
-rw-r--r--sound/arm/pxa2xx-pcm.c264
-rw-r--r--sound/arm/pxa2xx-pcm.h16
-rw-r--r--sound/arm/sa11xx-uda1341.c984
12 files changed, 1109 insertions, 1912 deletions
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index 2e4a5e0d16d..885683a3b0b 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -1,22 +1,19 @@
# ALSA ARM drivers
-menu "ALSA ARM devices"
- depends on SND!=n && ARM
-
-config SND_SA11XX_UDA1341
- tristate "SA11xx UDA1341TS driver (iPaq H3600)"
- depends on ARCH_SA1100 && SND && L3
- select SND_PCM
+menuconfig SND_ARM
+ bool "ARM sound devices"
+ depends on ARM
+ default y
help
- Say Y here if you have a Compaq iPaq H3x00 handheld computer
- and want to use its Philips UDA 1341 audio chip.
+ Support for sound devices specific to ARM architectures.
+ Drivers that are implemented on ASoC can be found in
+ "ALSA for SoC audio support" section.
- To compile this driver as a module, choose M here: the module
- will be called snd-sa11xx-uda1341.
+if SND_ARM
config SND_ARMAACI
tristate "ARM PrimeCell PL041 AC Link support"
- depends on SND && ARM_AMBA
+ depends on ARM_AMBA
select SND_PCM
select SND_AC97_CODEC
@@ -24,13 +21,23 @@ config SND_PXA2XX_PCM
tristate
select SND_PCM
+config SND_PXA2XX_LIB
+ tristate
+ select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97
+
+config SND_PXA2XX_LIB_AC97
+ bool
+
config SND_PXA2XX_AC97
tristate "AC97 driver for the Intel PXA2xx chip"
- depends on ARCH_PXA && SND
+ depends on ARCH_PXA
select SND_PXA2XX_PCM
select SND_AC97_CODEC
+ select SND_PXA2XX_LIB
+ select SND_PXA2XX_LIB_AC97
help
Say Y or M if you want to support any AC97 codec attached to
the PXA2xx AC97 interface.
-endmenu
+endif # SND_ARM
+
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 4ef6dd00c6e..8c0c851d464 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -2,14 +2,15 @@
# Makefile for ALSA
#
-obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o
-snd-sa11xx-uda1341-objs := sa11xx-uda1341.o
-
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
+obj-$(CONFIG_SND_PXA2XX_LIB) += snd-pxa2xx-lib.o
+snd-pxa2xx-lib-y := pxa2xx-pcm-lib.o
+snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o
+
obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
snd-pxa2xx-ac97-objs := pxa2xx-ac97.o
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index b0a47449496..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--);
+ } 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--);
+ } 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--);
+ } 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,28 +893,29 @@ 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;
- 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;
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;
@@ -1023,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;
@@ -1038,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++)
@@ -1061,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)
@@ -1073,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;
@@ -1083,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;
@@ -1097,12 +1009,14 @@ 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;
@@ -1116,18 +1030,23 @@ static int __devinit aaci_probe(struct amba_device *dev, void *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;
}
@@ -1136,12 +1055,10 @@ static int __devinit aaci_probe(struct amba_device *dev, void *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;
}
@@ -1153,12 +1070,10 @@ 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)
{
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);
@@ -1178,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
new file mode 100644
index 00000000000..66de90ed30c
--- /dev/null
+++ b/sound/arm/pxa2xx-ac97-lib.c
@@ -0,0 +1,415 @@
+/*
+ * Based on sound/arm/pxa2xx-ac97.c and sound/soc/pxa/pxa2xx-ac97.c
+ * which contain:
+ *
+ * Author: Nicolas Pitre
+ * Created: Dec 02, 2004
+ * Copyright: MontaVista Software Inc.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#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 <mach/irqs.h>
+#include <mach/regs-ac97.h>
+#include <mach/audio.h>
+
+static DEFINE_MUTEX(car_mutex);
+static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
+static volatile long gsr_bits;
+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:
+ *
+ * o Slot 12 read from modem space will hang controller.
+ * o CDONE, SDONE interrupt fails after any slot 12 IO.
+ *
+ * We therefore have an hybrid approach for waiting on SDONE (interrupt or
+ * 1 jiffy timeout if interrupt never comes).
+ */
+
+unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+ unsigned short val = -1;
+ volatile u32 *reg_addr;
+
+ mutex_lock(&car_mutex);
+
+ /* set up primary or secondary codec space */
+ if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
+ reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+ else
+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+ reg_addr += (reg >> 1);
+
+ /* start read access across the ac97 link */
+ GSR = GSR_CDONE | GSR_SDONE;
+ gsr_bits = 0;
+ val = *reg_addr;
+ if (reg == AC97_GPIO_STATUS)
+ goto out;
+ if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
+ !((GSR | gsr_bits) & GSR_SDONE)) {
+ printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
+ __func__, reg, GSR | gsr_bits);
+ val = -1;
+ goto out;
+ }
+
+ /* valid data now */
+ GSR = GSR_CDONE | GSR_SDONE;
+ gsr_bits = 0;
+ val = *reg_addr;
+ /* but we've just started another cycle... */
+ wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
+
+out: mutex_unlock(&car_mutex);
+ return val;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_read);
+
+void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ volatile u32 *reg_addr;
+
+ mutex_lock(&car_mutex);
+
+ /* set up primary or secondary codec space */
+ if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
+ reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+ else
+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+ reg_addr += (reg >> 1);
+
+ GSR = GSR_CDONE | GSR_SDONE;
+ gsr_bits = 0;
+ *reg_addr = val;
+ if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
+ !((GSR | gsr_bits) & GSR_CDONE))
+ printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
+ __func__, reg, GSR | gsr_bits);
+
+ mutex_unlock(&car_mutex);
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_write);
+
+#ifdef CONFIG_PXA25x
+static inline void pxa_ac97_warm_pxa25x(void)
+{
+ gsr_bits = 0;
+
+ GCR |= GCR_WARM_RST;
+}
+
+static inline void pxa_ac97_cold_pxa25x(void)
+{
+ GCR &= GCR_COLD_RST; /* clear everything but nCRST */
+ GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+
+ gsr_bits = 0;
+
+ GCR = GCR_COLD_RST;
+}
+#endif
+
+#ifdef CONFIG_PXA27x
+static inline void pxa_ac97_warm_pxa27x(void)
+{
+ gsr_bits = 0;
+
+ /* warm reset broken on Bulverde, so manually keep AC97 reset high */
+ pxa27x_configure_ac97reset(reset_gpio, true);
+ udelay(10);
+ GCR |= GCR_WARM_RST;
+ pxa27x_configure_ac97reset(reset_gpio, false);
+ udelay(500);
+}
+
+static inline void pxa_ac97_cold_pxa27x(void)
+{
+ GCR &= GCR_COLD_RST; /* clear everything but nCRST */
+ GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+
+ gsr_bits = 0;
+
+ /* PXA27x Developers Manual section 13.5.2.2.1 */
+ clk_enable(ac97conf_clk);
+ udelay(5);
+ clk_disable(ac97conf_clk);
+ GCR = GCR_COLD_RST | GCR_WARM_RST;
+}
+#endif
+
+#ifdef CONFIG_PXA3xx
+static inline void pxa_ac97_warm_pxa3xx(void)
+{
+ gsr_bits = 0;
+
+ /* Can't use interrupts */
+ GCR |= GCR_WARM_RST;
+}
+
+static inline void pxa_ac97_cold_pxa3xx(void)
+{
+ /* Hold CLKBPB for 100us */
+ GCR = 0;
+ GCR = GCR_CLKBPB;
+ udelay(100);
+ GCR = 0;
+
+ GCR &= GCR_COLD_RST; /* clear everything but nCRST */
+ GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+
+ gsr_bits = 0;
+
+ /* Can't use interrupts on PXA3xx */
+ GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+
+ GCR = GCR_WARM_RST | GCR_COLD_RST;
+}
+#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())
+ pxa_ac97_warm_pxa25x();
+ else
+#endif
+#ifdef CONFIG_PXA27x
+ if (cpu_is_pxa27x())
+ pxa_ac97_warm_pxa27x();
+ else
+#endif
+#ifdef CONFIG_PXA3xx
+ if (cpu_is_pxa3xx())
+ pxa_ac97_warm_pxa3xx();
+ else
+#endif
+ 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",
+ __func__, gsr);
+
+ return false;
+ }
+
+ return true;
+}
+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())
+ pxa_ac97_cold_pxa25x();
+ else
+#endif
+#ifdef CONFIG_PXA27x
+ if (cpu_is_pxa27x())
+ pxa_ac97_cold_pxa27x();
+ else
+#endif
+#ifdef CONFIG_PXA3xx
+ if (cpu_is_pxa3xx())
+ pxa_ac97_cold_pxa3xx();
+ else
+#endif
+ 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: cold reset timeout (GSR=%#lx)\n",
+ __func__, gsr);
+
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset);
+
+
+void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97)
+{
+ GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+ GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_finish_reset);
+
+static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
+{
+ long status;
+
+ status = GSR;
+ if (status) {
+ GSR = status;
+ gsr_bits |= status;
+ wake_up(&gsr_wq);
+
+ /* Although we don't use those we still need to clear them
+ since they tend to spuriously trigger when MMC is used
+ (hardware bug? go figure)... */
+ if (cpu_is_pxa27x()) {
+ MISR = MISR_EOC;
+ PISR = PISR_EOC;
+ MCSR = MCSR_EOC;
+ }
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+#ifdef CONFIG_PM
+int pxa2xx_ac97_hw_suspend(void)
+{
+ GCR |= GCR_ACLINK_OFF;
+ clk_disable(ac97_clk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_suspend);
+
+int pxa2xx_ac97_hw_resume(void)
+{
+ clk_enable(ac97_clk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_resume);
+#endif
+
+int pxa2xx_ac97_hw_probe(struct platform_device *dev)
+{
+ int ret;
+ pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
+
+ if (pdata) {
+ switch (pdata->reset_gpio) {
+ case 95:
+ case 113:
+ reset_gpio = pdata->reset_gpio;
+ break;
+ case 0:
+ reset_gpio = 113;
+ break;
+ case -1:
+ break;
+ default:
+ dev_err(&dev->dev, "Invalid reset GPIO %d\n",
+ pdata->reset_gpio);
+ }
+ } else {
+ if (cpu_is_pxa27x())
+ reset_gpio = 113;
+ }
+
+ if (cpu_is_pxa27x()) {
+ /*
+ * 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);
+ ac97conf_clk = NULL;
+ goto err_conf;
+ }
+ }
+
+ ac97_clk = clk_get(&dev->dev, "AC97CLK");
+ if (IS_ERR(ac97_clk)) {
+ ret = PTR_ERR(ac97_clk);
+ ac97_clk = NULL;
+ goto err_clk;
+ }
+
+ ret = clk_enable(ac97_clk);
+ if (ret)
+ goto err_clk2;
+
+ ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
+ if (ret < 0)
+ goto err_irq;
+
+ return 0;
+
+err_irq:
+ GCR |= GCR_ACLINK_OFF;
+err_clk2:
+ clk_put(ac97_clk);
+ ac97_clk = NULL;
+err_clk:
+ if (ac97conf_clk) {
+ clk_put(ac97conf_clk);
+ ac97conf_clk = NULL;
+ }
+err_conf:
+ return ret;
+}
+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) {
+ clk_put(ac97conf_clk);
+ ac97conf_clk = NULL;
+ }
+ clk_disable(ac97_clk);
+ clk_put(ac97_clk);
+ ac97_clk = NULL;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_remove);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("Intel/Marvell PXA sound library");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 5d86e680975..3a10df6688e 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -11,169 +11,30 @@
*/
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/module.h>
-#include <linux/kernel.h>
#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/delay.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 <asm/irq.h>
-#include <linux/mutex.h>
-#include <asm/hardware.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/audio.h>
+#include <mach/regs-ac97.h>
+#include <mach/audio.h>
#include "pxa2xx-pcm.h"
-
-static DEFINE_MUTEX(car_mutex);
-static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
-static volatile long gsr_bits;
-
-/*
- * Beware PXA27x bugs:
- *
- * o Slot 12 read from modem space will hang controller.
- * o CDONE, SDONE interrupt fails after any slot 12 IO.
- *
- * We therefore have an hybrid approach for waiting on SDONE (interrupt or
- * 1 jiffy timeout if interrupt never comes).
- */
-
-static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
-{
- unsigned short val = -1;
- volatile u32 *reg_addr;
-
- mutex_lock(&car_mutex);
-
- /* set up primary or secondary codec space */
- reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
- reg_addr += (reg >> 1);
-
- /* start read access across the ac97 link */
- GSR = GSR_CDONE | GSR_SDONE;
- gsr_bits = 0;
- val = *reg_addr;
- if (reg == AC97_GPIO_STATUS)
- goto out;
- if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
- !((GSR | gsr_bits) & GSR_SDONE)) {
- printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
- __FUNCTION__, reg, GSR | gsr_bits);
- val = -1;
- goto out;
- }
-
- /* valid data now */
- GSR = GSR_CDONE | GSR_SDONE;
- gsr_bits = 0;
- val = *reg_addr;
- /* but we've just started another cycle... */
- wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
-
-out: mutex_unlock(&car_mutex);
- return val;
-}
-
-static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
-{
- volatile u32 *reg_addr;
-
- mutex_lock(&car_mutex);
-
- /* set up primary or secondary codec space */
- reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
- reg_addr += (reg >> 1);
-
- GSR = GSR_CDONE | GSR_SDONE;
- gsr_bits = 0;
- *reg_addr = val;
- if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
- !((GSR | gsr_bits) & GSR_CDONE))
- printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
- __FUNCTION__, reg, GSR | gsr_bits);
-
- mutex_unlock(&car_mutex);
-}
-
static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
{
- /* First, try cold reset */
- GCR &= GCR_COLD_RST; /* clear everything but nCRST */
- GCR &= ~GCR_COLD_RST; /* then assert nCRST */
-
- gsr_bits = 0;
-#ifdef CONFIG_PXA27x
- /* PXA27x Developers Manual section 13.5.2.2.1 */
- pxa_set_cken(CKEN_AC97CONF, 1);
- udelay(5);
- pxa_set_cken(CKEN_AC97CONF, 0);
- GCR = GCR_COLD_RST;
- udelay(50);
-#else
- GCR = GCR_COLD_RST;
- GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
- wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
-#endif
-
- if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
- printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
- __FUNCTION__, gsr_bits);
-
- /* let's try warm reset */
- gsr_bits = 0;
-#ifdef CONFIG_PXA27x
- /* warm reset broken on Bulverde,
- so manually keep AC97 reset high */
- pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);
- udelay(10);
- GCR |= GCR_WARM_RST;
- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
- udelay(500);
-#else
- GCR |= GCR_WARM_RST|GCR_PRIRDY_IEN|GCR_SECRDY_IEN;
- wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
-#endif
-
- if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
- printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
- __FUNCTION__, gsr_bits);
+ if (!pxa2xx_ac97_try_cold_reset(ac97)) {
+ pxa2xx_ac97_try_warm_reset(ac97);
}
- GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
- GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
-}
-
-static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
-{
- long status;
-
- status = GSR;
- if (status) {
- GSR = status;
- gsr_bits |= status;
- wake_up(&gsr_wq);
-
-#ifdef CONFIG_PXA27x
- /* Although we don't use those we still need to clear them
- since they tend to spuriously trigger when MMC is used
- (hardware bug? go figure)... */
- MISR = MISR_EOC;
- PISR = PISR_EOC;
- MCSR = MCSR_EOC;
-#endif
-
- return IRQ_HANDLED;
- }
-
- return IRQ_NONE;
+ pxa2xx_ac97_finish_reset(ac97);
}
static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
@@ -182,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 = &DRCMRTXPCDR,
- .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 = &DRCMRRXPCDR,
- .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;
@@ -247,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;
@@ -258,17 +119,19 @@ static int pxa2xx_ac97_do_suspend(struct snd_card *card, pm_message_t state)
snd_ac97_suspend(pxa2xx_ac97_ac97);
if (platform_ops && platform_ops->suspend)
platform_ops->suspend(platform_ops->priv);
- GCR |= GCR_ACLINK_OFF;
- pxa_set_cken(CKEN_AC97, 0);
- return 0;
+ return pxa2xx_ac97_hw_suspend();
}
static int pxa2xx_ac97_do_resume(struct snd_card *card)
{
pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
+ int rc;
+
+ rc = pxa2xx_ac97_hw_resume();
+ if (rc)
+ return rc;
- pxa_set_cken(CKEN_AC97, 1);
if (platform_ops && platform_ops->resume)
platform_ops->resume(platform_ops->priv);
snd_ac97_resume(pxa2xx_ac97_ac97);
@@ -277,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)
@@ -299,86 +162,75 @@ 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 = -ENOMEM;
- card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, 0);
- if (!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)
goto err;
- ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
- if (ret < 0)
+ ret = pxa2xx_ac97_hw_probe(dev);
+ if (ret)
goto err;
- 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);
-#ifdef CONFIG_PXA27x
- /* Use GPIO 113 as AC97 Reset on Bulverde */
- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
-#endif
- pxa_set_cken(CKEN_AC97, 1);
-
ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);
if (ret)
- goto err;
+ goto err_remove;
memset(&ac97_template, 0, sizeof(ac97_template));
ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);
if (ret)
- goto err;
+ goto err_remove;
snprintf(card->shortname, sizeof(card->shortname),
"%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97));
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);
return 0;
}
- err:
+err_remove:
+ pxa2xx_ac97_hw_remove(dev);
+err:
if (card)
snd_card_free(card);
- if (CKEN & (1 << CKEN_AC97)) {
- GCR |= GCR_ACLINK_OFF;
- free_irq(IRQ_AC97, NULL);
- pxa_set_cken(CKEN_AC97, 0);
- }
+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);
- GCR |= GCR_ACLINK_OFF;
- free_irq(IRQ_AC97, NULL);
- pxa_set_cken(CKEN_AC97, 0);
+ pxa2xx_ac97_hw_remove(dev);
}
return 0;
@@ -386,27 +238,19 @@ 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");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-ac97");
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
new file mode 100644
index 00000000000..a61d7a9a995
--- /dev/null
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -0,0 +1,321 @@
+/*
+ * 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.
+ */
+
+#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>
+
+#include "pxa2xx-pcm.h"
+
+static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192 - 32,
+ .periods_min = 1,
+ .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
+ .buffer_bytes_max = 128 * 1024,
+ .fifo_size = 32,
+};
+
+int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *rtd = runtime->private_data;
+ size_t totsize = params_buffer_bytes(params);
+ 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;
+
+ dma_desc = rtd->dma_desc_array;
+ next_desc_phys = rtd->dma_desc_array_phys;
+ dma_buff_phys = runtime->dma_addr;
+ do {
+ next_desc_phys += sizeof(pxa_dma_desc);
+ dma_desc->ddadr = next_desc_phys;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_desc->dsadr = dma_buff_phys;
+ dma_desc->dtadr = rtd->params->addr;
+ } else {
+ dma_desc->dsadr = rtd->params->addr;
+ dma_desc->dtadr = dma_buff_phys;
+ }
+ if (period > totsize)
+ period = totsize;
+ dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN;
+ dma_desc++;
+ dma_buff_phys += period;
+ } while (totsize -= period);
+ dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
+
+ return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
+
+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->filter_data) {
+ unsigned long req = *(unsigned long *) rtd->params->filter_data;
+ DRCMR(req) = 0;
+ }
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
+
+int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
+ DCSR(prtd->dma_ch) = DCSR_RUN;
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ DCSR(prtd->dma_ch) &= ~DCSR_RUN;
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ DCSR(prtd->dma_ch) |= DCSR_RUN;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
+ DCSR(prtd->dma_ch) |= DCSR_RUN;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(pxa2xx_pcm_trigger);
+
+snd_pcm_uframes_t
+pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *prtd = runtime->private_data;
+
+ dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
+ snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+
+ if (x == runtime->buffer_size)
+ x = 0;
+ return x;
+}
+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;
+ req = *(unsigned long *) prtd->params->filter_data;
+ DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD;
+
+ return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
+
+void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
+{
+ struct snd_pcm_substream *substream = dev_id;
+ int dcsr;
+
+ dcsr = DCSR(dma_ch);
+ DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
+
+ if (dcsr & DCSR_ENDINTR) {
+ snd_pcm_period_elapsed(substream);
+ } else {
+ 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);
+
+int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *rtd;
+ int ret;
+
+ runtime->hw = pxa2xx_pcm_hardware;
+
+ /*
+ * For mysterious reasons (and despite what the manual says)
+ * playback samples are lost if the DMA count is not a multiple
+ * of the DMA burst size. Let's add a rule to enforce that.
+ */
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ if (ret)
+ goto out;
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+ if (ret)
+ goto out;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ ret = -ENOMEM;
+ rtd = kzalloc(sizeof(*rtd), GFP_KERNEL);
+ if (!rtd)
+ goto out;
+ rtd->dma_desc_array =
+ dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+ &rtd->dma_desc_array_phys, GFP_KERNEL);
+ if (!rtd->dma_desc_array)
+ goto err1;
+
+ rtd->dma_ch = -1;
+ runtime->private_data = rtd;
+ return 0;
+
+ err1:
+ kfree(rtd);
+ out:
+ return ret;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_open);
+
+int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pxa2xx_runtime_data *rtd = runtime->private_data;
+
+ dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+ rtd->dma_desc_array, rtd->dma_desc_array_phys);
+ kfree(rtd);
+ return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_close);
+
+int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+EXPORT_SYMBOL(pxa2xx_pcm_mmap);
+
+int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area)
+ return -ENOMEM;
+ buf->bytes = size;
+ return 0;
+}
+EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer);
+
+void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+ dma_free_writecombine(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("Intel PXA2xx sound library");
+MODULE_LICENSE("GPL");
diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c
index 0ede9e4656a..83be8e3f095 100644
--- a/sound/arm/pxa2xx-pcm.c
+++ b/sound/arm/pxa2xx-pcm.c
@@ -11,182 +11,26 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/slab.h>
#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
+#include <mach/dma.h>
-#include <asm/dma.h>
-#include <asm/hardware.h>
-#include <asm/arch/pxa-regs.h>
+#include <sound/core.h>
+#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
#include "pxa2xx-pcm.h"
-
-static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .period_bytes_min = 32,
- .period_bytes_max = 8192 - 32,
- .periods_min = 1,
- .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
- .buffer_bytes_max = 128 * 1024,
- .fifo_size = 32,
-};
-
-struct pxa2xx_runtime_data {
- int dma_ch;
- struct pxa2xx_pcm_dma_params *params;
- pxa_dma_desc *dma_desc_array;
- dma_addr_t dma_desc_array_phys;
-};
-
-static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
- size_t totsize = params_buffer_bytes(params);
- size_t period = params_period_bytes(params);
- pxa_dma_desc *dma_desc;
- dma_addr_t dma_buff_phys, next_desc_phys;
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = totsize;
-
- dma_desc = rtd->dma_desc_array;
- next_desc_phys = rtd->dma_desc_array_phys;
- dma_buff_phys = runtime->dma_addr;
- do {
- next_desc_phys += sizeof(pxa_dma_desc);
- 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;
- } else {
- dma_desc->dsadr = rtd->params->dev_addr;
- dma_desc->dtadr = dma_buff_phys;
- }
- if (period > totsize)
- period = totsize;
- dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN;
- dma_desc++;
- dma_buff_phys += period;
- } while (totsize -= period);
- dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
-
- return 0;
-}
-
-static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
-
- *rtd->params->drcmr = 0;
- snd_pcm_set_runtime_buffer(substream, NULL);
- return 0;
-}
-
static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
{
struct pxa2xx_pcm_client *client = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
- DCSR(rtd->dma_ch) &= ~DCSR_RUN;
- DCSR(rtd->dma_ch) = 0;
- DCMD(rtd->dma_ch) = 0;
- *rtd->params->drcmr = rtd->dma_ch | DRCMR_MAPVLD;
+ __pxa2xx_pcm_prepare(substream);
return client->prepare(substream);
}
-static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- DDADR(rtd->dma_ch) = rtd->dma_desc_array_phys;
- DCSR(rtd->dma_ch) = DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- DCSR(rtd->dma_ch) &= ~DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- DCSR(rtd->dma_ch) |= DCSR_RUN;
- break;
-
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static 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);
- DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
-
- 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 );
- snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- }
-}
-
-static snd_pcm_uframes_t pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
- dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- DSADR(rtd->dma_ch) : DTADR(rtd->dma_ch);
- snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
- if (x == runtime->buffer_size)
- x = 0;
- return x;
-}
-
-static int
-pxa2xx_pcm_hw_rule_mult32(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
-{
- struct snd_interval *i = hw_param_interval(params, rule->var);
- int changed = 0;
-
- if (i->min & 31) {
- i->min = (i->min & ~31) + 32;
- i->openmin = 0;
- changed = 1;
- }
-
- if (i->max & 31) {
- i->max &= ~31;
- i->openmax = 0;
- changed = 1;
- }
-
- return changed;
-}
-
static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
{
struct pxa2xx_pcm_client *client = substream->private_data;
@@ -194,53 +38,27 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
struct pxa2xx_runtime_data *rtd;
int ret;
- runtime->hw = pxa2xx_pcm_hardware;
-
- /*
- * For mysterious reasons (and despite what the manual says)
- * playback samples are lost if the DMA count is not a multiple
- * of the DMA burst size. Let's add a rule to enforce that.
- */
- ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- pxa2xx_pcm_hw_rule_mult32, NULL,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1);
- if (ret)
- goto out;
- ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- pxa2xx_pcm_hw_rule_mult32, NULL,
- SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
+ ret = __pxa2xx_pcm_open(substream);
if (ret)
goto out;
- ret = -ENOMEM;
- rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
- if (!rtd)
- goto out;
- rtd->dma_desc_array =
- dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- &rtd->dma_desc_array_phys, GFP_KERNEL);
- if (!rtd->dma_desc_array)
- goto err1;
+ rtd = runtime->private_data;
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;
rtd->dma_ch = ret;
- runtime->private_data = rtd;
ret = client->startup(substream);
if (!ret)
goto out;
pxa_free_dma(rtd->dma_ch);
err2:
- dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- rtd->dma_desc_array, rtd->dma_desc_array_phys);
- err1:
- kfree(rtd);
+ __pxa2xx_pcm_close(substream);
out:
return ret;
}
@@ -252,71 +70,22 @@ static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
pxa_free_dma(rtd->dma_ch);
client->shutdown(substream);
- dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- rtd->dma_desc_array, rtd->dma_desc_array_phys);
- kfree(rtd);
- return 0;
-}
-static int
-pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
+ return __pxa2xx_pcm_close(substream);
}
static struct snd_pcm_ops pxa2xx_pcm_ops = {
.open = pxa2xx_pcm_open,
.close = pxa2xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
- .hw_params = pxa2xx_pcm_hw_params,
- .hw_free = pxa2xx_pcm_hw_free,
+ .hw_params = __pxa2xx_pcm_hw_params,
+ .hw_free = __pxa2xx_pcm_hw_free,
.prepare = pxa2xx_pcm_prepare,
.trigger = pxa2xx_pcm_trigger,
.pointer = pxa2xx_pcm_pointer,
.mmap = pxa2xx_pcm_mmap,
};
-static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = size;
- return 0;
-}
-
-static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
- dma_free_writecombine(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-
-static u64 pxa2xx_pcm_dmamask = 0xffffffff;
-
int pxa2xx_pcm_new(struct snd_card *card, struct pxa2xx_pcm_client *client,
struct snd_pcm **rpcm)
{
@@ -332,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 b79f1e80378..00330985bee 100644
--- a/sound/arm/pxa2xx-pcm.h
+++ b/sound/arm/pxa2xx-pcm.h
@@ -10,16 +10,16 @@
* published by the Free Software Foundation.
*/
-struct pxa2xx_pcm_dma_params {
- char *name; /* stream identifier */
- u32 dcmd; /* DMA descriptor dcmd field */
- volatile u32 *drcmr; /* the DMA request channel to use */
- u32 dev_addr; /* device physical address for DMA */
+struct pxa2xx_runtime_data {
+ int dma_ch;
+ 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 *);
diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
deleted file mode 100644
index 0eff33ca0f7..00000000000
--- a/sound/arm/sa11xx-uda1341.c
+++ /dev/null
@@ -1,984 +0,0 @@
-/*
- * Driver for Philips UDA1341TS on Compaq iPAQ H3600 soundcard
- * Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License.
- *
- * History:
- *
- * 2002-03-13 Tomas Kasparek initial release - based on h3600-uda1341.c from OSS
- * 2002-03-20 Tomas Kasparek playback over ALSA is working
- * 2002-03-28 Tomas Kasparek playback over OSS emulation is working
- * 2002-03-29 Tomas Kasparek basic capture is working (native ALSA)
- * 2002-03-29 Tomas Kasparek capture is working (OSS emulation)
- * 2002-04-04 Tomas Kasparek better rates handling (allow non-standard rates)
- * 2003-02-14 Brian Avery fixed full duplex mode, other updates
- * 2003-02-20 Tomas Kasparek merged updates by Brian (except HAL)
- * 2003-04-19 Jaroslav Kysela recoded DMA stuff to follow 2.4.18rmk3-hh24 kernel
- * working suspend and resume
- * 2003-04-28 Tomas Kasparek updated work by Jaroslav to compile it under 2.5.x again
- * merged HAL layer (patches from Brian)
- */
-
-/* $Id: sa11xx-uda1341.c,v 1.27 2005/12/07 09:13:42 cladisch Exp $ */
-
-/***************************************************************************************************
-*
-* To understand what Alsa Drivers should be doing look at "Writing an Alsa Driver" by Takashi Iwai
-* available in the Alsa doc section on the website
-*
-* A few notes to make things clearer. The UDA1341 is hooked up to Serial port 4 on the SA1100.
-* We are using SSP mode to talk to the UDA1341. The UDA1341 bit & wordselect clocks are generated
-* by this UART. Unfortunately, the clock only runs if the transmit buffer has something in it.
-* So, if we are just recording, we feed the transmit DMA stream a bunch of 0x0000 so that the
-* transmit buffer is full and the clock keeps going. The zeroes come from FLUSH_BASE_PHYS which
-* is a mem loc that always decodes to 0's w/ no off chip access.
-*
-* Some alsa terminology:
-* frame => num_channels * sample_size e.g stereo 16 bit is 2 * 16 = 32 bytes
-* period => the least number of bytes that will generate an interrupt e.g. we have a 1024 byte
-* buffer and 4 periods in the runtime structure this means we'll get an int every 256
-* bytes or 4 times per buffer.
-* A number of the sizes are in frames rather than bytes, use frames_to_bytes and
-* bytes_to_frames to convert. The easiest way to tell the units is to look at the
-* type i.e. runtime-> buffer_size is in frames and its type is snd_pcm_uframes_t
-*
-* Notes about the pointer fxn:
-* The pointer fxn needs to return the offset into the dma buffer in frames.
-* Interrupts must be blocked before calling the dma_get_pos fxn to avoid race with interrupts.
-*
-* Notes about pause/resume
-* Implementing this would be complicated so it's skipped. The problem case is:
-* A full duplex connection is going, then play is paused. At this point you need to start xmitting
-* 0's to keep the record active which means you cant just freeze the dma and resume it later you'd
-* need to save off the dma info, and restore it properly on a resume. Yeach!
-*
-* Notes about transfer methods:
-* The async write calls fail. I probably need to implement something else to support them?
-*
-***************************************************************************************************/
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/errno.h>
-#include <linux/ioctl.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-
-#ifdef CONFIG_PM
-#include <linux/pm.h>
-#endif
-
-#include <asm/hardware.h>
-#include <asm/arch/h3600.h>
-#include <asm/mach-types.h>
-#include <asm/dma.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/initval.h>
-
-#include <linux/l3/l3.h>
-
-#undef DEBUG_MODE
-#undef DEBUG_FUNCTION_NAMES
-#include <sound/uda1341.h>
-
-/*
- * FIXME: Is this enough as autodetection of 2.4.X-rmkY-hhZ kernels?
- * We use DMA stuff from 2.4.18-rmk3-hh24 here to be able to compile this
- * module for Familiar 0.6.1
- */
-
-/* {{{ Type definitions */
-
-MODULE_AUTHOR("Tomas Kasparek <tomas.kasparek@seznam.cz>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SA1100/SA1111 + UDA1341TS driver for ALSA");
-MODULE_SUPPORTED_DEVICE("{{UDA1341,iPAQ H3600 UDA1341TS}}");
-
-static char *id; /* ID for this card */
-
-module_param(id, charp, 0444);
-MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UDA1341TS soundcard.");
-
-struct audio_stream {
- char *id; /* identification string */
- int stream_id; /* numeric identification */
- dma_device_t dma_dev; /* device identifier for DMA */
-#ifdef HH_VERSION
- dmach_t dmach; /* dma channel identification */
-#else
- dma_regs_t *dma_regs; /* points to our DMA registers */
-#endif
- unsigned int active:1; /* we are using this stream for transfer now */
- int period; /* current transfer period */
- int periods; /* current count of periods registerd in the DMA engine */
- int tx_spin; /* are we recoding - flag used to do DMA trans. for sync */
- unsigned int old_offset;
- spinlock_t dma_lock; /* for locking in DMA operations (see dma-sa1100.c in the kernel) */
- struct snd_pcm_substream *stream;
-};
-
-struct sa11xx_uda1341 {
- struct snd_card *card;
- struct l3_client *uda1341;
- struct snd_pcm *pcm;
- long samplerate;
- struct audio_stream s[2]; /* playback & capture */
-};
-
-static unsigned int rates[] = {
- 8000, 10666, 10985, 14647,
- 16000, 21970, 22050, 24000,
- 29400, 32000, 44100, 48000,
-};
-
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
-};
-
-static struct platform_device *device;
-
-/* }}} */
-
-/* {{{ Clock and sample rate stuff */
-
-/*
- * Stop-gap solution until rest of hh.org HAL stuff is merged.
- */
-#define GPIO_H3600_CLK_SET0 GPIO_GPIO (12)
-#define GPIO_H3600_CLK_SET1 GPIO_GPIO (13)
-
-#ifdef CONFIG_SA1100_H3XXX
-#define clr_sa11xx_uda1341_egpio(x) clr_h3600_egpio(x)
-#define set_sa11xx_uda1341_egpio(x) set_h3600_egpio(x)
-#else
-#error This driver could serve H3x00 handhelds only!
-#endif
-
-static void sa11xx_uda1341_set_audio_clock(long val)
-{
- switch (val) {
- case 24000: case 32000: case 48000: /* 00: 12.288 MHz */
- GPCR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
- break;
-
- case 22050: case 29400: case 44100: /* 01: 11.2896 MHz */
- GPSR = GPIO_H3600_CLK_SET0;
- GPCR = GPIO_H3600_CLK_SET1;
- break;
-
- case 8000: case 10666: case 16000: /* 10: 4.096 MHz */
- GPCR = GPIO_H3600_CLK_SET0;
- GPSR = GPIO_H3600_CLK_SET1;
- break;
-
- case 10985: case 14647: case 21970: /* 11: 5.6245 MHz */
- GPSR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
- break;
- }
-}
-
-static void sa11xx_uda1341_set_samplerate(struct sa11xx_uda1341 *sa11xx_uda1341, long rate)
-{
- int clk_div = 0;
- int clk=0;
-
- /* We don't want to mess with clocks when frames are in flight */
- Ser4SSCR0 &= ~SSCR0_SSE;
- /* wait for any frame to complete */
- udelay(125);
-
- /*
- * We have the following clock sources:
- * 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz
- * Those can be divided either by 256, 384 or 512.
- * This makes up 12 combinations for the following samplerates...
- */
- if (rate >= 48000)
- rate = 48000;
- else if (rate >= 44100)
- rate = 44100;
- else if (rate >= 32000)
- rate = 32000;
- else if (rate >= 29400)
- rate = 29400;
- else if (rate >= 24000)
- rate = 24000;
- else if (rate >= 22050)
- rate = 22050;
- else if (rate >= 21970)
- rate = 21970;
- else if (rate >= 16000)
- rate = 16000;
- else if (rate >= 14647)
- rate = 14647;
- else if (rate >= 10985)
- rate = 10985;
- else if (rate >= 10666)
- rate = 10666;
- else
- rate = 8000;
-
- /* Set the external clock generator */
-
- sa11xx_uda1341_set_audio_clock(rate);
-
- /* Select the clock divisor */
- switch (rate) {
- case 8000:
- case 10985:
- case 22050:
- case 24000:
- clk = F512;
- clk_div = SSCR0_SerClkDiv(16);
- break;
- case 16000:
- case 21970:
- case 44100:
- case 48000:
- clk = F256;
- clk_div = SSCR0_SerClkDiv(8);
- break;
- case 10666:
- case 14647:
- case 29400:
- case 32000:
- clk = F384;
- clk_div = SSCR0_SerClkDiv(12);
- break;
- }
-
- /* FMT setting should be moved away when other FMTs are added (FIXME) */
- l3_command(sa11xx_uda1341->uda1341, CMD_FORMAT, (void *)LSB16);
-
- l3_command(sa11xx_uda1341->uda1341, CMD_FS, (void *)clk);
- Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE;
- sa11xx_uda1341->samplerate = rate;
-}
-
-/* }}} */
-
-/* {{{ HW init and shutdown */
-
-static void sa11xx_uda1341_audio_init(struct sa11xx_uda1341 *sa11xx_uda1341)
-{
- unsigned long flags;
-
- /* Setup DMA stuff */
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].id = "UDA1341 out";
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = SNDRV_PCM_STREAM_PLAYBACK;
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = DMA_Ser4SSPWr;
-
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].id = "UDA1341 in";
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = SNDRV_PCM_STREAM_CAPTURE;
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = DMA_Ser4SSPRd;
-
- /* Initialize the UDA1341 internal state */
-
- /* Setup the uarts */
- local_irq_save(flags);
- GAFR |= (GPIO_SSP_CLK);
- GPDR &= ~(GPIO_SSP_CLK);
- Ser4SSCR0 = 0;
- Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8);
- Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk;
- Ser4SSCR0 |= SSCR0_SSE;
- local_irq_restore(flags);
-
- /* Enable the audio power */
-
- clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
- set_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
- set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-
- /* Wait for the UDA1341 to wake up */
- mdelay(1); //FIXME - was removed by Perex - Why?
-
- /* Initialize the UDA1341 internal state */
- l3_open(sa11xx_uda1341->uda1341);
-
- /* external clock configuration (after l3_open - regs must be initialized */
- sa11xx_uda1341_set_samplerate(sa11xx_uda1341, sa11xx_uda1341->samplerate);
-
- /* Wait for the UDA1341 to wake up */
- set_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
- mdelay(1);
-
- /* make the left and right channels unswapped (flip the WS latch) */
- Ser4SSDR = 0;
-
- clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-}
-
-static void sa11xx_uda1341_audio_shutdown(struct sa11xx_uda1341 *sa11xx_uda1341)
-{
- /* mute on */
- set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-
- /* disable the audio power and all signals leading to the audio chip */
- l3_close(sa11xx_uda1341->uda1341);
- Ser4SSCR0 = 0;
- clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
-
- /* power off and mute off */
- /* FIXME - is muting off necesary??? */
-
- clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
- clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-}
-
-/* }}} */
-
-/* {{{ DMA staff */
-
-/*
- * these are the address and sizes used to fill the xmit buffer
- * so we can get a clock in record only mode
- */
-#define FORCE_CLOCK_ADDR (dma_addr_t)FLUSH_BASE_PHYS
-#define FORCE_CLOCK_SIZE 4096 // was 2048
-
-// FIXME Why this value exactly - wrote comment
-#define DMA_BUF_SIZE 8176 /* <= MAX_DMA_SIZE from asm/arch-sa1100/dma.h */
-
-#ifdef HH_VERSION
-
-static int audio_dma_request(struct audio_stream *s, void (*callback)(void *, int))
-{
- int ret;
-
- ret = sa1100_request_dma(&s->dmach, s->id, s->dma_dev);
- if (ret < 0) {
- printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
- return ret;
- }
- sa1100_dma_set_callback(s->dmach, callback);
- return 0;
-}
-
-static inline void audio_dma_free(struct audio_stream *s)
-{
- sa1100_free_dma(s->dmach);
- s->dmach = -1;
-}
-
-#else
-
-static int audio_dma_request(struct audio_stream *s, void (*callback)(void *))
-{
- int ret;
-
- ret = sa1100_request_dma(s->dma_dev, s->id, callback, s, &s->dma_regs);
- if (ret < 0)
- printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
- return ret;
-}
-
-static void audio_dma_free(struct audio_stream *s)
-{
- sa1100_free_dma(s->dma_regs);
- s->dma_regs = 0;
-}
-
-#endif
-
-static u_int audio_get_dma_pos(struct audio_stream *s)
-{
- struct snd_pcm_substream *substream = s->stream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int offset;
- unsigned long flags;
- dma_addr_t addr;
-
- // this must be called w/ interrupts locked out see dma-sa1100.c in the kernel
- spin_lock_irqsave(&s->dma_lock, flags);
-#ifdef HH_VERSION
- sa1100_dma_get_current(s->dmach, NULL, &addr);
-#else
- addr = sa1100_get_dma_pos((s)->dma_regs);
-#endif
- offset = addr - runtime->dma_addr;
- spin_unlock_irqrestore(&s->dma_lock, flags);
-
- offset = bytes_to_frames(runtime,offset);
- if (offset >= runtime->buffer_size)
- offset = 0;
-
- return offset;
-}
-
-/*
- * this stops the dma and clears the dma ptrs
- */
-static void audio_stop_dma(struct audio_stream *s)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&s->dma_lock, flags);
- s->active = 0;
- s->period = 0;
- /* this stops the dma channel and clears the buffer ptrs */
-#ifdef HH_VERSION
- sa1100_dma_flush_all(s->dmach);
-#else
- sa1100_clear_dma(s->dma_regs);
-#endif
- spin_unlock_irqrestore(&s->dma_lock, flags);
-}
-
-static void audio_process_dma(struct audio_stream *s)
-{
- struct snd_pcm_substream *substream = s->stream;
- struct snd_pcm_runtime *runtime;
- unsigned int dma_size;
- unsigned int offset;
- int ret;
-
- /* we are requested to process synchronization DMA transfer */
- if (s->tx_spin) {
- snd_assert(s->stream_id == SNDRV_PCM_STREAM_PLAYBACK, return);
- /* fill the xmit dma buffers and return */
-#ifdef HH_VERSION
- sa1100_dma_set_spin(s->dmach, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
-#else
- while (1) {
- ret = sa1100_start_dma(s->dma_regs, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
- if (ret)
- return;
- }
-#endif
- return;
- }
-
- /* must be set here - only valid for running streams, not for forced_clock dma fills */
- runtime = substream->runtime;
- while (s->active && s->periods < runtime->periods) {
- dma_size = frames_to_bytes(runtime, runtime->period_size);
- if (s->old_offset) {
- /* a little trick, we need resume from old position */
- offset = frames_to_bytes(runtime, s->old_offset - 1);
- s->old_offset = 0;
- s->periods = 0;
- s->period = offset / dma_size;
- offset %= dma_size;
- dma_size = dma_size - offset;
- if (!dma_size)
- continue; /* special case */
- } else {
- offset = dma_size * s->period;
- snd_assert(dma_size <= DMA_BUF_SIZE, );
- }
-#ifdef HH_VERSION
- ret = sa1100_dma_queue_buffer(s->dmach, s, runtime->dma_addr + offset, dma_size);
- if (ret)
- return; //FIXME
-#else
- ret = sa1100_start_dma((s)->dma_regs, runtime->dma_addr + offset, dma_size);
- if (ret) {
- printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i)\n", ret);
- return;
- }
-#endif
-
- s->period++;
- s->period %= runtime->periods;
- s->periods++;
- }
-}
-
-#ifdef HH_VERSION
-static void audio_dma_callback(void *data, int size)
-#else
-static void audio_dma_callback(void *data)
-#endif
-{
- struct audio_stream *s = data;
-
- /*
- * If we are getting a callback for an active stream then we inform
- * the PCM middle layer we've finished a period
- */
- if (s->active)
- snd_pcm_period_elapsed(s->stream);
-
- spin_lock(&s->dma_lock);
- if (!s->tx_spin && s->periods > 0)
- s->periods--;
- audio_process_dma(s);
- spin_unlock(&s->dma_lock);
-}
-
-/* }}} */
-
-/* {{{ PCM setting */
-
-/* {{{ trigger & timer */
-
-static int snd_sa11xx_uda1341_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream);
- int stream_id = substream->pstr->stream;
- struct audio_stream *s = &chip->s[stream_id];
- struct audio_stream *s1 = &chip->s[stream_id ^ 1];
- int err = 0;
-
- /* note local interrupts are already disabled in the midlevel code */
- spin_lock(&s->dma_lock);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- /* now we need to make sure a record only stream has a clock */
- if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
- /* we need to force fill the xmit DMA with zeros */
- s1->tx_spin = 1;
- audio_process_dma(s1);
- }
- /* this case is when you were recording then you turn on a
- * playback stream so we stop (also clears it) the dma first,
- * clear the sync flag and then we let it turned on
- */
- else {
- s->tx_spin = 0;
- }
-
- /* requested stream startup */
- s->active = 1;
- audio_process_dma(s);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- /* requested stream shutdown */
- audio_stop_dma(s);
-
- /*
- * now we need to make sure a record only stream has a clock
- * so if we're stopping a playback with an active capture
- * we need to turn the 0 fill dma on for the xmit side
- */
- if (stream_id == SNDRV_PCM_STREAM_PLAYBACK && s1->active) {
- /* we need to force fill the xmit DMA with zeros */
- s->tx_spin = 1;
- audio_process_dma(s);
- }
- /*
- * we killed a capture only stream, so we should also kill
- * the zero fill transmit
- */
- else {
- if (s1->tx_spin) {
- s1->tx_spin = 0;
- audio_stop_dma(s1);
- }
- }
-
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- s->active = 0;
-#ifdef HH_VERSION
- sa1100_dma_stop(s->dmach);
-#else
- //FIXME - DMA API
-#endif
- s->old_offset = audio_get_dma_pos(s) + 1;
-#ifdef HH_VERSION
- sa1100_dma_flush_all(s->dmach);
-#else
- //FIXME - DMA API
-#endif
- s->periods = 0;
- break;
- case SNDRV_PCM_TRIGGER_RESUME:
- s->active = 1;
- s->tx_spin = 0;
- audio_process_dma(s);
- if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
- s1->tx_spin = 1;
- audio_process_dma(s1);
- }
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-#ifdef HH_VERSION
- sa1100_dma_stop(s->dmach);
-#else
- //FIXME - DMA API
-#endif
- s->active = 0;
- if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
- if (s1->active) {
- s->tx_spin = 1;
- s->old_offset = audio_get_dma_pos(s) + 1;
-#ifdef HH_VERSION
- sa1100_dma_flush_all(s->dmach);
-#else
- //FIXME - DMA API
-#endif
- audio_process_dma(s);
- }
- } else {
- if (s1->tx_spin) {
- s1->tx_spin = 0;
-#ifdef HH_VERSION
- sa1100_dma_flush_all(s1->dmach);
-#else
- //FIXME - DMA API
-#endif
- }
- }
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- s->active = 1;
- if (s->old_offset) {
- s->tx_spin = 0;
- audio_process_dma(s);
- break;
- }
- if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
- s1->tx_spin = 1;
- audio_process_dma(s1);
- }
-#ifdef HH_VERSION
- sa1100_dma_resume(s->dmach);
-#else
- //FIXME - DMA API
-#endif
- break;
- default:
- err = -EINVAL;
- break;
- }
- spin_unlock(&s->dma_lock);
- return err;
-}
-
-static int snd_sa11xx_uda1341_prepare(struct snd_pcm_substream *substream)
-{
- struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct audio_stream *s = &chip->s[substream->pstr->stream];
-
- /* set requested samplerate */
- sa11xx_uda1341_set_samplerate(chip, runtime->rate);
-
- /* set requestd format when available */
- /* set FMT here !!! FIXME */
-
- s->period = 0;
- s->periods = 0;
-
- return 0;
-}
-
-static snd_pcm_uframes_t snd_sa11xx_uda1341_pointer(struct snd_pcm_substream *substream)
-{
- struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream);
- return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
-}
-
-/* }}} */
-
-static struct snd_pcm_hardware snd_sa11xx_uda1341_capture =
-{
- .info = (SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
- SNDRV_PCM_RATE_KNOT),
- .rate_min = 8000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = 64*1024,
- .period_bytes_min = 64,
- .period_bytes_max = DMA_BUF_SIZE,
- .periods_min = 2,
- .periods_max = 255,
- .fifo_size = 0,
-};
-
-static struct snd_pcm_hardware snd_sa11xx_uda1341_playback =
-{
- .info = (SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
- SNDRV_PCM_RATE_KNOT),
- .rate_min = 8000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = 64*1024,
- .period_bytes_min = 64,
- .period_bytes_max = DMA_BUF_SIZE,
- .periods_min = 2,
- .periods_max = 255,
- .fifo_size = 0,
-};
-
-static int snd_card_sa11xx_uda1341_open(struct snd_pcm_substream *substream)
-{
- struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int stream_id = substream->pstr->stream;
- int err;
-
- chip->s[stream_id].stream = substream;
-
- if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
- runtime->hw = snd_sa11xx_uda1341_playback;
- else
- runtime->hw = snd_sa11xx_uda1341_capture;
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
- return err;
- if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0)
- return err;
-
- return 0;
-}
-
-static int snd_card_sa11xx_uda1341_close(struct snd_pcm_substream *substream)
-{
- struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream);
-
- chip->s[substream->pstr->stream].stream = NULL;
- return 0;
-}
-
-/* {{{ HW params & free */
-
-static int snd_sa11xx_uda1341_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
-
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_sa11xx_uda1341_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-/* }}} */
-
-static struct snd_pcm_ops snd_card_sa11xx_uda1341_playback_ops = {
- .open = snd_card_sa11xx_uda1341_open,
- .close = snd_card_sa11xx_uda1341_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sa11xx_uda1341_hw_params,
- .hw_free = snd_sa11xx_uda1341_hw_free,
- .prepare = snd_sa11xx_uda1341_prepare,
- .trigger = snd_sa11xx_uda1341_trigger,
- .pointer = snd_sa11xx_uda1341_pointer,
-};
-
-static struct snd_pcm_ops snd_card_sa11xx_uda1341_capture_ops = {
- .open = snd_card_sa11xx_uda1341_open,
- .close = snd_card_sa11xx_uda1341_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sa11xx_uda1341_hw_params,
- .hw_free = snd_sa11xx_uda1341_hw_free,
- .prepare = snd_sa11xx_uda1341_prepare,
- .trigger = snd_sa11xx_uda1341_trigger,
- .pointer = snd_sa11xx_uda1341_pointer,
-};
-
-static int __init snd_card_sa11xx_uda1341_pcm(struct sa11xx_uda1341 *sa11xx_uda1341, int device)
-{
- struct snd_pcm *pcm;
- int err;
-
- if ((err = snd_pcm_new(sa11xx_uda1341->card, "UDA1341 PCM", device, 1, 1, &pcm)) < 0)
- return err;
-
- /*
- * this sets up our initial buffers and sets the dma_type to isa.
- * isa works but I'm not sure why (or if) it's the right choice
- * this may be too large, trying it for now
- */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, 64*1024);
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_uda1341_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_uda1341_capture_ops);
- pcm->private_data = sa11xx_uda1341;
- pcm->info_flags = 0;
- strcpy(pcm->name, "UDA1341 PCM");
-
- sa11xx_uda1341_audio_init(sa11xx_uda1341);
-
- /* setup DMA controller */
- audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK], audio_dma_callback);
- audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE], audio_dma_callback);
-
- sa11xx_uda1341->pcm = pcm;
-
- return 0;
-}
-
-/* }}} */
-
-/* {{{ module init & exit */
-
-#ifdef CONFIG_PM
-
-static int snd_sa11xx_uda1341_suspend(struct platform_device *devptr,
- pm_message_t state)
-{
- struct snd_card *card = platform_get_drvdata(devptr);
- struct sa11xx_uda1341 *chip = card->private_data;
-
- snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
-#ifdef HH_VERSION
- sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
- sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
-#else
- //FIXME
-#endif
- l3_command(chip->uda1341, CMD_SUSPEND, NULL);
- sa11xx_uda1341_audio_shutdown(chip);
-
- return 0;
-}
-
-static int snd_sa11xx_uda1341_resume(struct platform_device *devptr)
-{
- struct snd_card *card = platform_get_drvdata(devptr);
- struct sa11xx_uda1341 *chip = card->private_data;
-
- sa11xx_uda1341_audio_init(chip);
- l3_command(chip->uda1341, CMD_RESUME, NULL);
-#ifdef HH_VERSION
- sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
- sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
-#else
- //FIXME
-#endif
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
- return 0;
-}
-#endif /* COMFIG_PM */
-
-void snd_sa11xx_uda1341_free(struct snd_card *card)
-{
- struct sa11xx_uda1341 *chip = card->private_data;
-
- audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
- audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
-}
-
-static int __init sa11xx_uda1341_probe(struct platform_device *devptr)
-{
- int err;
- struct snd_card *card;
- struct sa11xx_uda1341 *chip;
-
- /* register the soundcard */
- card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct sa11xx_uda1341));
- if (card == NULL)
- return -ENOMEM;
-
- chip = card->private_data;
- spin_lock_init(&chip->s[0].dma_lock);
- spin_lock_init(&chip->s[1].dma_lock);
-
- card->private_free = snd_sa11xx_uda1341_free;
- chip->card = card;
- chip->samplerate = AUDIO_RATE_DEFAULT;
-
- // mixer
- if ((err = snd_chip_uda1341_mixer_new(card, &chip->uda1341)))
- goto nodev;
-
- // PCM
- if ((err = snd_card_sa11xx_uda1341_pcm(chip, 0)) < 0)
- goto nodev;
-
- strcpy(card->driver, "UDA1341");
- strcpy(card->shortname, "H3600 UDA1341TS");
- sprintf(card->longname, "Compaq iPAQ H3600 with Philips UDA1341TS");
-
- snd_card_set_dev(card, &devptr->dev);
-
- if ((err = snd_card_register(card)) == 0) {
- printk( KERN_INFO "iPAQ audio support initialized\n" );
- platform_set_drvdata(devptr, card);
- return 0;
- }
-
- nodev:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit sa11xx_uda1341_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
- return 0;
-}
-
-#define SA11XX_UDA1341_DRIVER "sa11xx_uda1341"
-
-static struct platform_driver sa11xx_uda1341_driver = {
- .probe = sa11xx_uda1341_probe,
- .remove = __devexit_p(sa11xx_uda1341_remove),
-#ifdef CONFIG_PM
- .suspend = snd_sa11xx_uda1341_suspend,
- .resume = snd_sa11xx_uda1341_resume,
-#endif
- .driver = {
- .name = SA11XX_UDA1341_DRIVER,
- },
-};
-
-static int __init sa11xx_uda1341_init(void)
-{
- int err;
-
- if (!machine_is_h3xxx())
- return -ENODEV;
- if ((err = platform_driver_register(&sa11xx_uda1341_driver)) < 0)
- return err;
- device = platform_device_register_simple(SA11XX_UDA1341_DRIVER, -1, NULL, 0);
- if (!IS_ERR(device)) {
- if (platform_get_drvdata(device))
- return 0;
- platform_device_unregister(device);
- err = -ENODEV;
- } else
- err = PTR_ERR(device);
- platform_driver_unregister(&sa11xx_uda1341_driver);
- return err;
-}
-
-static void __exit sa11xx_uda1341_exit(void)
-{
- platform_device_unregister(device);
- platform_driver_unregister(&sa11xx_uda1341_driver);
-}
-
-module_init(sa11xx_uda1341_init);
-module_exit(sa11xx_uda1341_exit);
-
-/* }}} */
-
-/*
- * Local variables:
- * indent-tabs-mode: t
- * End:
- */