aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-gumstix-2.6.15/bkpxa-pxa-ac97.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/linux/linux-gumstix-2.6.15/bkpxa-pxa-ac97.patch')
-rw-r--r--recipes/linux/linux-gumstix-2.6.15/bkpxa-pxa-ac97.patch1415
1 files changed, 0 insertions, 1415 deletions
diff --git a/recipes/linux/linux-gumstix-2.6.15/bkpxa-pxa-ac97.patch b/recipes/linux/linux-gumstix-2.6.15/bkpxa-pxa-ac97.patch
deleted file mode 100644
index efb7e73e15..0000000000
--- a/recipes/linux/linux-gumstix-2.6.15/bkpxa-pxa-ac97.patch
+++ /dev/null
@@ -1,1415 +0,0 @@
-Index: linux-2.6.15gum/include/linux/ac97_codec.h
-===================================================================
---- linux-2.6.15gum.orig/include/linux/ac97_codec.h
-+++ linux-2.6.15gum/include/linux/ac97_codec.h
-@@ -259,7 +259,8 @@ struct ac97_codec {
- int type;
- u32 model;
-
-- int modem:1;
-+ unsigned modem:1;
-+ unsigned power:1;
-
- struct ac97_ops *codec_ops;
-
-Index: linux-2.6.15gum/sound/oss/Kconfig
-===================================================================
---- linux-2.6.15gum.orig/sound/oss/Kconfig
-+++ linux-2.6.15gum/sound/oss/Kconfig
-@@ -178,6 +178,14 @@ config SOUND_MAESTRO3
- Say Y or M if you have a sound system driven by ESS's Maestro 3
- PCI sound chip.
-
-+config SOUND_PXA_AC97
-+ tristate "PXA AC97 support"
-+ depends on SOUND_PRIME!=n && ARCH_PXA && SOUND
-+
-+config SOUND_PXA_AUDIO
-+ tristate "PXA audio support"
-+ depends on SOUND_PXA_AC97
-+
- config SOUND_ICH
- tristate "Intel ICH (i8xx) audio support"
- depends on SOUND_PRIME && PCI
-@@ -1125,6 +1133,9 @@ config SOUND_AD1980
- tristate "AD1980 front/back switch plugin"
- depends on SOUND_PRIME && OBSOLETE_OSS_DRIVER
-
-+config SOUND_WM97XX
-+ tristate "WM97XX sound/touchscreen codec"
-+
- config SOUND_SH_DAC_AUDIO
- tristate "SuperH DAC audio support"
- depends on SOUND_PRIME && CPU_SH3
-Index: linux-2.6.15gum/sound/oss/Makefile
-===================================================================
---- linux-2.6.15gum.orig/sound/oss/Makefile
-+++ linux-2.6.15gum/sound/oss/Makefile
-@@ -44,6 +44,8 @@ obj-$(CONFIG_SOUND_VIA82CXXX) += via82cx
- ifeq ($(CONFIG_MIDI_VIA82CXXX),y)
- obj-$(CONFIG_SOUND_VIA82CXXX) += sound.o uart401.o
- endif
-+obj-$(CONFIG_SOUND_PXA_AC97) += pxa-ac97.o ac97_codec.o
-+obj-$(CONFIG_SOUND_PXA_AUDIO) += pxa-audio.o
- obj-$(CONFIG_SOUND_YMFPCI) += ymfpci.o ac97_codec.o
- ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y)
- obj-$(CONFIG_SOUND_YMFPCI) += opl3.o uart401.o
-Index: linux-2.6.15gum/sound/oss/ac97_codec.c
-===================================================================
---- linux-2.6.15gum.orig/sound/oss/ac97_codec.c
-+++ linux-2.6.15gum/sound/oss/ac97_codec.c
-@@ -84,6 +84,7 @@ static int crystal_digital_control(struc
- static int cmedia_init(struct ac97_codec * codec);
- static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
- static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-+static int ucb1400_init(struct ac97_codec *codec);
-
-
- /*
-@@ -119,6 +120,7 @@ static struct ac97_ops crystal_digital_o
- static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL };
- static struct ac97_ops cmedia_ops = { NULL, eapd_control, NULL};
- static struct ac97_ops cmedia_digital_ops = { cmedia_init, eapd_control, cmedia_digital_control};
-+static struct ac97_ops ucb1400_ops = { ucb1400_init, eapd_control, NULL };
-
- /* sorted by vendor/device id */
- static const struct {
-@@ -164,6 +166,7 @@ static const struct {
- {0x4e534331, "National Semiconductor LM4549", &null_ops},
- {0x53494c22, "Silicon Laboratory Si3036", &null_ops},
- {0x53494c23, "Silicon Laboratory Si3038", &null_ops},
-+ {0x50534304, "Philips UCB1400", &ucb1400_ops},
- {0x545200FF, "TriTech TR?????", &tritech_m_ops},
- {0x54524102, "TriTech TR28022", &null_ops},
- {0x54524103, "TriTech TR28023", &null_ops},
-@@ -461,6 +464,17 @@ static void ac97_write_mixer(struct ac97
- val = codec->codec_read(codec, mh->offset);
- printk(" -> 0x%04x\n", val);
- #endif
-+
-+ if (val & AC97_MUTE)
-+ val = 0;
-+ else
-+ val = 1;
-+ if ((oss_channel == SOUND_MIXER_VOLUME) &&
-+ (codec->codec_ops->amplifier) &&
-+ (codec->power != val)) {
-+ codec->power = val;
-+ codec->codec_ops->amplifier (codec, codec->power);
-+ }
- }
-
- /* a thin wrapper for write_mixer */
-@@ -1092,6 +1106,13 @@ static int wolfson_init05(struct ac97_co
- {
- /* set front mixer volume */
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
-+ /*codec->codec_write(codec, 0x78, 0xc004);
-+ while(1){
-+ codec->codec_write(codec, 0x76, 0xa020);
-+ printk("%08x ", codec->codec_read(codec, 0x76));
-+ printk("%08x ", codec->codec_read(codec, 0x78));
-+ printk("%08x\n", codec->codec_read(codec, 0x7A));
-+ }*/
- return 0;
- }
-
-@@ -1313,6 +1334,14 @@ static int pt101_init(struct ac97_codec
- }
- #endif
-
-+static int ucb1400_init(struct ac97_codec *codec)
-+{
-+ codec->codec_write(codec,AC97_EXTENDED_STATUS,1);
-+ //codec->codec_write(codec, 0x6a, 0x1ff7);
-+ codec->codec_write(codec, 0x6a, 0x0050);
-+ codec->codec_write(codec, 0x6c, 0x0030);
-+ return 0;
-+}
-
- EXPORT_SYMBOL(ac97_read_proc);
- EXPORT_SYMBOL(ac97_probe_codec);
-Index: linux-2.6.15gum/sound/oss/pxa-ac97.c
-===================================================================
---- /dev/null
-+++ linux-2.6.15gum/sound/oss/pxa-ac97.c
-@@ -0,0 +1,357 @@
-+/*
-+ * linux/drivers/sound/pxa-ac97.c -- AC97 interface for the Cotula chip
-+ *
-+ * Author: Nicolas Pitre
-+ * Created: Aug 15, 2001
-+ * Copyright: MontaVista Software Inc.
-+ *
-+ * Forward ported to 2.6 by Ian Molton 15/09/2003
-+ *
-+ * 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/init.h>
-+#include <linux/module.h>
-+#include <linux/kernel.h>
-+#include <linux/slab.h>
-+#include <linux/pci.h>
-+#include <linux/interrupt.h>
-+#include <linux/completion.h>
-+#include <linux/delay.h>
-+#include <linux/poll.h>
-+#include <linux/sound.h>
-+#include <linux/soundcard.h>
-+#include <linux/ac97_codec.h>
-+
-+#include <asm/hardware.h>
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/irq.h>
-+#include <asm/uaccess.h>
-+#include <asm/semaphore.h>
-+#include <asm/dma.h>
-+
-+#include "pxa-audio.h"
-+
-+static struct completion CAR_completion;
-+static int waitingForMask;
-+static DECLARE_MUTEX(CAR_mutex);
-+
-+static u16 pxa_ac97_read(struct ac97_codec *codec, u8 reg)
-+{
-+ u16 val = -1;
-+
-+ down(&CAR_mutex);
-+ if (!(CAR & CAR_CAIP)) {
-+ volatile u32 *reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1);
-+
-+ waitingForMask=GSR_SDONE;
-+
-+ init_completion(&CAR_completion);
-+ (void)*reg_addr; //start read access across the ac97 link
-+ wait_for_completion(&CAR_completion);
-+
-+ if (GSR & GSR_RDCS) {
-+ GSR = GSR_RDCS; //write a 1 to clear
-+ printk(KERN_CRIT "%s: read codec register timeout.\n", __FUNCTION__);
-+ }
-+
-+ init_completion(&CAR_completion);
-+ val = *reg_addr; //valid data now but we've just started another cycle...
-+ wait_for_completion(&CAR_completion);
-+
-+ } else {
-+ printk(KERN_CRIT"%s: CAR_CAIP already set\n", __FUNCTION__);
-+ }
-+ up(&CAR_mutex);
-+ //printk("%s(0x%02x) = 0x%04x\n", __FUNCTION__, reg, val);
-+ return val;
-+}
-+
-+static void pxa_ac97_write(struct ac97_codec *codec, u8 reg, u16 val)
-+{
-+ down(&CAR_mutex);
-+ if (!(CAR & CAR_CAIP)) {
-+ volatile u32 *reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1);
-+
-+ waitingForMask=GSR_CDONE;
-+ init_completion(&CAR_completion);
-+ *reg_addr = val;
-+ wait_for_completion(&CAR_completion);
-+ } else {
-+ printk(KERN_CRIT "%s: CAR_CAIP already set\n", __FUNCTION__);
-+ }
-+ up(&CAR_mutex);
-+ //printk("%s(0x%02x, 0x%04x)\n", __FUNCTION__, reg, val);
-+}
-+
-+static irqreturn_t pxa_ac97_irq(int irq, void *dev_id, struct pt_regs *regs)
-+{
-+ int gsr = GSR;
-+ GSR = gsr & (GSR_SDONE|GSR_CDONE); //write a 1 to clear
-+ if (gsr & waitingForMask)
-+ complete(&CAR_completion);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static struct ac97_codec pxa_ac97_codec = {
-+ codec_read: pxa_ac97_read,
-+ codec_write: pxa_ac97_write,
-+};
-+
-+static DECLARE_MUTEX(pxa_ac97_mutex);
-+static int pxa_ac97_refcount;
-+
-+int pxa_ac97_get(struct ac97_codec **codec)
-+{
-+ int ret;
-+
-+ *codec = NULL;
-+ down(&pxa_ac97_mutex);
-+
-+ if (!pxa_ac97_refcount) {
-+ ret = request_irq(IRQ_AC97, pxa_ac97_irq, 0, "AC97", NULL);
-+ if (ret)
-+ return ret;
-+
-+ CKEN |= CKEN2_AC97;
-+
-+ 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);
-+
-+ GCR = 0;
-+ udelay(10);
-+ GCR = GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE;
-+ while (!(GSR & GSR_PCR)) {
-+ schedule();
-+ }
-+
-+ ret = ac97_probe_codec(&pxa_ac97_codec);
-+ if (ret != 1) {
-+ free_irq(IRQ_AC97, NULL);
-+ GCR = GCR_ACLINK_OFF;
-+ CKEN &= ~CKEN2_AC97;
-+ return ret;
-+ }
-+ }
-+
-+ pxa_ac97_refcount++;
-+ up(&pxa_ac97_mutex);
-+ *codec = &pxa_ac97_codec;
-+ return 0;
-+}
-+
-+void pxa_ac97_put(void)
-+{
-+ down(&pxa_ac97_mutex);
-+ pxa_ac97_refcount--;
-+ if (!pxa_ac97_refcount) {
-+ GCR = GCR_ACLINK_OFF;
-+ CKEN &= ~CKEN2_AC97;
-+ free_irq(IRQ_AC97, NULL);
-+ }
-+ up(&pxa_ac97_mutex);
-+}
-+
-+EXPORT_SYMBOL(pxa_ac97_get);
-+EXPORT_SYMBOL(pxa_ac97_put);
-+
-+
-+/*
-+ * Audio Mixer stuff
-+ */
-+
-+static audio_state_t ac97_audio_state;
-+static audio_stream_t ac97_audio_in;
-+
-+/*
-+ * According to the PXA250 spec, mic-in should use different
-+ * DRCMR and different AC97 FIFO.
-+ * Unfortunately current UCB1400 versions (up to ver 2A) don't
-+ * produce slot 6 for the audio input frame, therefore the PXA
-+ * AC97 mic-in FIFO is always starved.
-+ * But since UCB1400 is not the only audio CODEC out there,
-+ * this is still enabled by default.
-+ */
-+static void update_audio_in (void)
-+{
-+#if 1
-+ long val;
-+
-+ /* Use the value stuffed by ac97_recmask_io()
-+ * into recording select register
-+ */
-+ val = pxa_ac97_codec.codec_read(&pxa_ac97_codec, AC97_RECORD_SELECT);
-+ pxa_audio_clear_buf(&ac97_audio_in);
-+ *ac97_audio_in.drcmr = 0;
-+ if (val == 0) {
-+ ac97_audio_in.dcmd = DCMD_RXMCDR;
-+ ac97_audio_in.drcmr = &DRCMRRXMCDR;
-+ ac97_audio_in.dev_addr = __PREG(MCDR);
-+ } else {
-+ ac97_audio_in.dcmd = DCMD_RXPCDR;
-+ ac97_audio_in.drcmr = &DRCMRRXPCDR;
-+ ac97_audio_in.dev_addr = __PREG(PCDR);
-+ }
-+ if (ac97_audio_state.rd_ref)
-+ *ac97_audio_in.drcmr =
-+ ac97_audio_in.dma_ch | DRCMR_MAPVLD;
-+#endif
-+}
-+
-+static int mixer_ioctl( struct inode *inode, struct file *file,
-+ unsigned int cmd, unsigned long arg)
-+{
-+ int ret;
-+
-+ ret = pxa_ac97_codec.mixer_ioctl(&pxa_ac97_codec, cmd, arg);
-+ if (ret)
-+ return ret;
-+
-+ /* We must snoop for some commands to provide our own extra processing */
-+ switch (cmd) {
-+ case SOUND_MIXER_WRITE_RECSRC:
-+ update_audio_in ();
-+ break;
-+ }
-+ return 0;
-+}
-+
-+static struct file_operations mixer_fops = {
-+ ioctl: mixer_ioctl,
-+ llseek: no_llseek,
-+ owner: THIS_MODULE
-+};
-+
-+/*
-+ * AC97 codec ioctls
-+ */
-+
-+static int codec_adc_rate = 48000;
-+static int codec_dac_rate = 48000;
-+
-+static int ac97_ioctl(struct inode *inode, struct file *file,
-+ unsigned int cmd, unsigned long arg)
-+{
-+ int ret;
-+ long val = 0;
-+
-+ switch(cmd) {
-+ case SNDCTL_DSP_STEREO:
-+ ret = get_user(val, (int *) arg);
-+ if (ret)
-+ return ret;
-+ /* FIXME: do we support mono? */
-+ ret = (val == 0) ? -EINVAL : 1;
-+ return put_user(ret, (int *) arg);
-+
-+ case SNDCTL_DSP_CHANNELS:
-+ case SOUND_PCM_READ_CHANNELS:
-+ /* FIXME: do we support mono? */
-+ return put_user(2, (long *) arg);
-+
-+ case SNDCTL_DSP_SPEED:
-+ ret = get_user(val, (long *) arg);
-+ if (ret)
-+ return ret;
-+ if (file->f_mode & FMODE_READ)
-+ codec_adc_rate = ac97_set_adc_rate(&pxa_ac97_codec, val);
-+ if (file->f_mode & FMODE_WRITE)
-+ codec_dac_rate = ac97_set_dac_rate(&pxa_ac97_codec, val);
-+ /* fall through */
-+ case SOUND_PCM_READ_RATE:
-+ if (file->f_mode & FMODE_READ)
-+ val = codec_adc_rate;
-+ if (file->f_mode & FMODE_WRITE)
-+ val = codec_dac_rate;
-+ return put_user(val, (long *) arg);
-+
-+ case SNDCTL_DSP_SETFMT:
-+ case SNDCTL_DSP_GETFMTS:
-+ /* FIXME: can we do other fmts? */
-+ return put_user(AFMT_S16_LE, (long *) arg);
-+
-+ default:
-+ /* Maybe this is meant for the mixer (As per OSS Docs) */
-+ return mixer_ioctl(inode, file, cmd, arg);
-+ }
-+ return 0;
-+}
-+
-+
-+/*
-+ * Audio stuff
-+ */
-+
-+static audio_stream_t ac97_audio_out = {
-+ name: "AC97 audio out",
-+ dcmd: DCMD_TXPCDR,
-+ drcmr: &DRCMRTXPCDR,
-+ dev_addr: __PREG(PCDR),
-+};
-+
-+static audio_stream_t ac97_audio_in = {
-+ name: "AC97 audio in",
-+ dcmd: DCMD_RXPCDR,
-+ drcmr: &DRCMRRXPCDR,
-+ dev_addr: __PREG(PCDR),
-+};
-+
-+static audio_state_t ac97_audio_state = {
-+ output_stream: &ac97_audio_out,
-+ input_stream: &ac97_audio_in,
-+ client_ioctl: ac97_ioctl,
-+ sem: __MUTEX_INITIALIZER(ac97_audio_state.sem),
-+};
-+
-+static int ac97_audio_open(struct inode *inode, struct file *file)
-+{
-+ return pxa_audio_attach(inode, file, &ac97_audio_state);
-+}
-+
-+/*
-+ * Missing fields of this structure will be patched with the call
-+ * to pxa_audio_attach().
-+ */
-+
-+static struct file_operations ac97_audio_fops = {
-+ open: ac97_audio_open,
-+ owner: THIS_MODULE
-+};
-+
-+
-+static int __init pxa_ac97_init(void)
-+{
-+ int ret;
-+ struct ac97_codec *dummy;
-+
-+ ret = pxa_ac97_get(&dummy);
-+ if (ret)
-+ return ret;
-+
-+ update_audio_in ();
-+
-+ ac97_audio_state.dev_dsp = register_sound_dsp(&ac97_audio_fops, -1);
-+ pxa_ac97_codec.dev_mixer = register_sound_mixer(&mixer_fops, -1);
-+
-+ return 0;
-+}
-+
-+static void __exit pxa_ac97_exit(void)
-+{
-+ unregister_sound_dsp(ac97_audio_state.dev_dsp);
-+ unregister_sound_mixer(pxa_ac97_codec.dev_mixer);
-+ pxa_ac97_put();
-+}
-+
-+
-+module_init(pxa_ac97_init);
-+module_exit(pxa_ac97_exit);
-+
-+MODULE_AUTHOR("Nicolas Pitre");
-+MODULE_DESCRIPTION("AC97 interface for the Cotula chip");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.15gum/sound/oss/pxa-audio.c
-===================================================================
---- /dev/null
-+++ linux-2.6.15gum/sound/oss/pxa-audio.c
-@@ -0,0 +1,858 @@
-+/*
-+ * linux/drivers/sound/pxa-audio.c -- audio interface for the Cotula chip
-+ *
-+ * Author: Nicolas Pitre
-+ * Created: Aug 15, 2001
-+ * 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/init.h>
-+#include <linux/module.h>
-+#include <linux/kernel.h>
-+#include <linux/slab.h>
-+#include <linux/pci.h>
-+#include <linux/poll.h>
-+#include <linux/sound.h>
-+#include <linux/soundcard.h>
-+
-+#include <asm/hardware.h>
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/irq.h>
-+#include <asm/uaccess.h>
-+#include <asm/semaphore.h>
-+#include <asm/dma.h>
-+
-+#include "pxa-audio.h"
-+
-+
-+#define AUDIO_NBFRAGS_DEFAULT 8
-+#define AUDIO_FRAGSIZE_DEFAULT 8192
-+
-+#define MAX_DMA_SIZE 4096
-+#define DMA_DESC_SIZE sizeof(pxa_dma_desc)
-+
-+
-+/*
-+ * This function frees all buffers
-+ */
-+#define audio_clear_buf pxa_audio_clear_buf
-+
-+void pxa_audio_clear_buf(audio_stream_t * s)
-+{
-+ DECLARE_WAITQUEUE(wait, current);
-+ int frag;
-+
-+ if (!s->buffers)
-+ return;
-+
-+ /* Ensure DMA isn't running */
-+ set_current_state(TASK_UNINTERRUPTIBLE);
-+ add_wait_queue(&s->stop_wq, &wait);
-+ DCSR(s->dma_ch) = DCSR_STOPIRQEN;
-+ schedule();
-+ remove_wait_queue(&s->stop_wq, &wait);
-+
-+ /* free DMA buffers */
-+ for (frag = 0; frag < s->nbfrags; frag++) {
-+ audio_buf_t *b = &s->buffers[frag];
-+ if (!b->master)
-+ continue;
-+ dma_free_writecombine(NULL, b->master, b->data, b->dma_desc->dsadr);
-+ }
-+
-+ /* free descriptor ring */
-+ if (s->buffers->dma_desc)
-+ dma_free_writecombine(NULL, s->nbfrags * s->descs_per_frag * DMA_DESC_SIZE,
-+ s->buffers->dma_desc, s->dma_desc_phys);
-+
-+ /* free buffer structure array */
-+ kfree(s->buffers);
-+ s->buffers = NULL;
-+}
-+
-+/*
-+ * This function allocates the DMA descriptor array and buffer data space
-+ * according to the current number of fragments and fragment size.
-+ */
-+static int audio_setup_buf(audio_stream_t * s)
-+{
-+ pxa_dma_desc *dma_desc;
-+ dma_addr_t dma_desc_phys;
-+ int nb_desc, frag, i, buf_size = 0;
-+ char *dma_buf = NULL;
-+ dma_addr_t dma_buf_phys = 0;
-+
-+ if (s->buffers)
-+ return -EBUSY;
-+
-+ /* Our buffer structure array */
-+ s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);
-+ if (!s->buffers)
-+ goto err;
-+ memzero(s->buffers, sizeof(audio_buf_t) * s->nbfrags);
-+
-+ /*
-+ * Our DMA descriptor array:
-+ * for Each fragment we have one checkpoint descriptor plus one
-+ * descriptor per MAX_DMA_SIZE byte data blocks.
-+ */
-+ nb_desc = (1 + (s->fragsize + MAX_DMA_SIZE - 1)/MAX_DMA_SIZE) * s->nbfrags;
-+ dma_desc = dma_alloc_writecombine(NULL, nb_desc * DMA_DESC_SIZE,
-+ &dma_desc_phys, GFP_KERNEL);
-+
-+ if (!dma_desc)
-+ goto err;
-+ s->descs_per_frag = nb_desc / s->nbfrags;
-+ s->buffers->dma_desc = dma_desc;
-+ s->dma_desc_phys = dma_desc_phys;
-+ for (i = 0; i < nb_desc - 1; i++)
-+ dma_desc[i].ddadr = dma_desc_phys + (i + 1) * DMA_DESC_SIZE;
-+ dma_desc[i].ddadr = dma_desc_phys;
-+
-+ /* Our actual DMA buffers */
-+ for (frag = 0; frag < s->nbfrags; frag++) {
-+ audio_buf_t *b = &s->buffers[frag];
-+
-+ /*
-+ * Let's allocate non-cached memory for DMA buffers.
-+ * We try to allocate all memory at once.
-+ * If this fails (a common reason is memory fragmentation),
-+ * then we'll try allocating smaller buffers.
-+ */
-+ if (!buf_size) {
-+ buf_size = (s->nbfrags - frag) * s->fragsize;
-+ do {
-+ dma_buf = dma_alloc_writecombine(NULL, buf_size,
-+ &dma_buf_phys,
-+ GFP_KERNEL);
-+ if (!dma_buf)
-+ buf_size -= s->fragsize;
-+ } while (!dma_buf && buf_size);
-+ if (!dma_buf)
-+ goto err;
-+ b->master = buf_size;
-+ memzero(dma_buf, buf_size);
-+ }
-+
-+ /*
-+ * Set up our checkpoint descriptor. Since the count
-+ * is always zero, we'll abuse the dsadr and dtadr fields
-+ * just in case this one is picked up by the hardware
-+ * while processing SOUND_DSP_GETPTR.
-+ */
-+ dma_desc->dsadr = dma_buf_phys;
-+ dma_desc->dtadr = dma_buf_phys;
-+ dma_desc->dcmd = DCMD_ENDIRQEN;
-+ if (s->output && !s->mapped)
-+ dma_desc->ddadr |= DDADR_STOP;
-+ b->dma_desc = dma_desc++;
-+
-+ /* set up the actual data descriptors */
-+ for (i = 0; (i * MAX_DMA_SIZE) < s->fragsize; i++) {
-+ dma_desc[i].dsadr = (s->output) ?
-+ (dma_buf_phys + i*MAX_DMA_SIZE) : s->dev_addr;
-+ dma_desc[i].dtadr = (s->output) ?
-+ s->dev_addr : (dma_buf_phys + i*MAX_DMA_SIZE);
-+ dma_desc[i].dcmd = s->dcmd |
-+ ((s->fragsize < MAX_DMA_SIZE) ?
-+ s->fragsize : MAX_DMA_SIZE);
-+ }
-+ dma_desc += i;
-+
-+ /* handle buffer pointers */
-+ b->data = dma_buf;
-+ dma_buf += s->fragsize;
-+ dma_buf_phys += s->fragsize;
-+ buf_size -= s->fragsize;
-+ }
-+
-+ s->usr_frag = s->dma_frag = 0;
-+ s->bytecount = 0;
-+ s->fragcount = 0;
-+ sema_init(&s->sem, (s->output) ? s->nbfrags : 0);
-+ return 0;
-+
-+err:
-+ printk("pxa-audio: unable to allocate audio memory\n ");
-+ audio_clear_buf(s);
-+ return -ENOMEM;
-+}
-+
-+/*
-+ * Our DMA interrupt handler
-+ */
-+static void audio_dma_irq(int ch, void *dev_id, struct pt_regs *regs)
-+{
-+ audio_stream_t *s = dev_id;
-+ u_int dcsr;
-+
-+ dcsr = DCSR(ch);
-+ DCSR(ch) = dcsr & ~DCSR_STOPIRQEN;
-+
-+ if (!s->buffers) {
-+ printk("AC97 DMA: wow... received IRQ for channel %d but no buffer exists\n", ch);
-+ return;
-+ }
-+
-+ if (dcsr & DCSR_BUSERR)
-+ printk("AC97 DMA: bus error interrupt on channel %d\n", ch);
-+
-+ if (dcsr & DCSR_ENDINTR) {
-+ u_long cur_dma_desc;
-+ u_int cur_dma_frag;
-+
-+ /*
-+ * Find out which DMA desc is current. Note that DDADR
-+ * points to the next desc, not the current one.
-+ */
-+ cur_dma_desc = DDADR(ch) - s->dma_desc_phys - DMA_DESC_SIZE;
-+
-+ /*
-+ * Let the compiler nicely optimize constant divisors into
-+ * multiplications for the common cases which is much faster.
-+ * Common cases: x = 1 + (1 << y) for y = [0..3]
-+ */
-+ switch (s->descs_per_frag) {
-+ case 2: cur_dma_frag = cur_dma_desc / (2*DMA_DESC_SIZE); break;
-+ case 3: cur_dma_frag = cur_dma_desc / (3*DMA_DESC_SIZE); break;
-+ case 5: cur_dma_frag = cur_dma_desc / (5*DMA_DESC_SIZE); break;
-+ case 9: cur_dma_frag = cur_dma_desc / (9*DMA_DESC_SIZE); break;
-+ default: cur_dma_frag =
-+ cur_dma_desc / (s->descs_per_frag * DMA_DESC_SIZE);
-+ }
-+
-+ /* Account for possible wrap back of cur_dma_desc above */
-+ if (cur_dma_frag >= s->nbfrags)
-+ cur_dma_frag = s->nbfrags - 1;
-+
-+ while (s->dma_frag != cur_dma_frag) {
-+ if (!s->mapped) {
-+ /*
-+ * This fragment is done - set the checkpoint
-+ * descriptor to STOP until it is gets
-+ * processed by the read or write function.
-+ */
-+ s->buffers[s->dma_frag].dma_desc->ddadr |= DDADR_STOP;
-+ up(&s->sem);
-+ }
-+ if (++s->dma_frag >= s->nbfrags)
-+ s->dma_frag = 0;
-+
-+ /* Accounting */
-+ s->bytecount += s->fragsize;
-+ s->fragcount++;
-+ }
-+
-+ /* ... and for polling processes */
-+ wake_up(&s->frag_wq);
-+ }
-+
-+ if ((dcsr & DCSR_STOPIRQEN) && (dcsr & DCSR_STOPSTATE))
-+ wake_up(&s->stop_wq);
-+}
-+
-+/*
-+ * Validate and sets up buffer fragments, etc.
-+ */
-+static int audio_set_fragments(audio_stream_t *s, int val)
-+{
-+ if (s->mapped || DCSR(s->dma_ch) & DCSR_RUN)
-+ return -EBUSY;
-+ if (s->buffers)
-+ audio_clear_buf(s);
-+ s->nbfrags = (val >> 16) & 0x7FFF;
-+ val &= 0xffff;
-+ if (val < 5)
-+ val = 5;
-+ if (val > 15)
-+ val = 15;
-+ s->fragsize = 1 << val;
-+ if (s->nbfrags < 2)
-+ s->nbfrags = 2;
-+ if (s->nbfrags * s->fragsize > 256 * 1024)
-+ s->nbfrags = 256 * 1024 / s->fragsize;
-+ if (audio_setup_buf(s))
-+ return -ENOMEM;
-+ return val|(s->nbfrags << 16);
-+}
-+
-+
-+/*
-+ * The fops functions
-+ */
-+
-+static int audio_write(struct file *file, const char *buffer,
-+ size_t count, loff_t * ppos)
-+{
-+ const char *buffer0 = buffer;
-+ audio_state_t *state = (audio_state_t *)file->private_data;
-+ audio_stream_t *s = state->output_stream;
-+ int chunksize, ret = 0;
-+
-+ if (ppos != &file->f_pos)
-+ return -ESPIPE;
-+ if (s->mapped)
-+ return -ENXIO;
-+ if (!s->buffers && audio_setup_buf(s))
-+ return -ENOMEM;
-+
-+ while (count > 0) {
-+ audio_buf_t *b = &s->buffers[s->usr_frag];
-+
-+ /* Grab a fragment */
-+ if (file->f_flags & O_NONBLOCK) {
-+ ret = -EAGAIN;
-+ if (down_trylock(&s->sem))
-+ break;
-+ } else {
-+ ret = -ERESTARTSYS;
-+ if (down_interruptible(&s->sem))
-+ break;
-+ }
-+
-+ /* Feed the current buffer */
-+ chunksize = s->fragsize - b->offset;
-+ if (chunksize > count)
-+ chunksize = count;
-+ if (copy_from_user(b->data + b->offset, buffer, chunksize)) {
-+ up(&s->sem);
-+ return -EFAULT;
-+ }
-+
-+ b->offset += chunksize;
-+ buffer += chunksize;
-+ count -= chunksize;
-+ if (b->offset < s->fragsize) {
-+ ret = 0;
-+ up(&s->sem);
-+ break;
-+ }
-+
-+ /*
-+ * Activate DMA on current buffer.
-+ * We unlock this fragment's checkpoint descriptor and
-+ * kick DMA if it is idle. Using checkpoint descriptors
-+ * allows for control operations without the need for
-+ * stopping the DMA channel if it is already running.
-+ */
-+ b->offset = 0;
-+ b->dma_desc->ddadr &= ~DDADR_STOP;
-+ if (DCSR(s->dma_ch) & DCSR_STOPSTATE) {
-+ DDADR(s->dma_ch) = b->dma_desc->ddadr;
-+ DCSR(s->dma_ch) = DCSR_RUN;
-+ }
-+
-+ /* move the index to the next fragment */
-+ if (++s->usr_frag >= s->nbfrags)
-+ s->usr_frag = 0;
-+ }
-+
-+ if ((buffer - buffer0))
-+ ret = buffer - buffer0;
-+ return ret;
-+}
-+
-+
-+static int audio_read(struct file *file, char *buffer,
-+ size_t count, loff_t * ppos)
-+{
-+ char *buffer0 = buffer;
-+ audio_state_t *state = file->private_data;
-+ audio_stream_t *s = state->input_stream;
-+ int chunksize, ret = 0;
-+
-+ if (ppos != &file->f_pos)
-+ return -ESPIPE;
-+ if (s->mapped)
-+ return -ENXIO;
-+ if (!s->buffers && audio_setup_buf(s))
-+ return -ENOMEM;
-+
-+ while (count > 0) {
-+ audio_buf_t *b = &s->buffers[s->usr_frag];
-+
-+ /* prime DMA */
-+ if (DCSR(s->dma_ch) & DCSR_STOPSTATE) {
-+ DDADR(s->dma_ch) =
-+ s->buffers[s->dma_frag].dma_desc->ddadr;
-+ DCSR(s->dma_ch) = DCSR_RUN;
-+ }
-+
-+ /* Wait for a buffer to become full */
-+ if (file->f_flags & O_NONBLOCK) {
-+ ret = -EAGAIN;
-+ if (down_trylock(&s->sem))
-+ break;
-+ } else {
-+ ret = -ERESTARTSYS;
-+ if (down_interruptible(&s->sem))
-+ break;
-+ }
-+
-+ /* Grab data from current buffer */
-+ chunksize = s->fragsize - b->offset;
-+ if (chunksize > count)
-+ chunksize = count;
-+ if (copy_to_user(buffer, b->data + b->offset, chunksize)) {
-+ up(&s->sem);
-+ return -EFAULT;
-+ }
-+ b->offset += chunksize;
-+ buffer += chunksize;
-+ count -= chunksize;
-+ if (b->offset < s->fragsize) {
-+ ret = 0;
-+ up(&s->sem);
-+ break;
-+ }
-+
-+ /*
-+ * Make this buffer available for DMA again.
-+ * We unlock this fragment's checkpoint descriptor and
-+ * kick DMA if it is idle. Using checkpoint descriptors
-+ * allows for control operations without the need for
-+ * stopping the DMA channel if it is already running.
-+ */
-+ b->offset = 0;
-+ b->dma_desc->ddadr &= ~DDADR_STOP;
-+
-+ /* move the index to the next fragment */
-+ if (++s->usr_frag >= s->nbfrags)
-+ s->usr_frag = 0;
-+ }
-+
-+ if ((buffer - buffer0))
-+ ret = buffer - buffer0;
-+ return ret;
-+}
-+
-+
-+static int audio_sync(struct file *file)
-+{
-+ audio_state_t *state = file->private_data;
-+ audio_stream_t *s = state->output_stream;
-+ audio_buf_t *b;
-+ pxa_dma_desc *final_desc;
-+ u_long dcmd_save = 0;
-+ DECLARE_WAITQUEUE(wait, current);
-+
-+ if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped)
-+ return 0;
-+
-+ /*
-+ * Send current buffer if it contains data. Be sure to send
-+ * a full sample count.
-+ */
-+ final_desc = NULL;
-+ b = &s->buffers[s->usr_frag];
-+ if (b->offset &= ~3) {
-+ final_desc = &b->dma_desc[1 + b->offset/MAX_DMA_SIZE];
-+ b->offset &= (MAX_DMA_SIZE-1);
-+ dcmd_save = final_desc->dcmd;
-+ final_desc->dcmd = b->offset | s->dcmd | DCMD_ENDIRQEN;
-+ final_desc->ddadr |= DDADR_STOP;
-+ b->offset = 0;
-+ b->dma_desc->ddadr &= ~DDADR_STOP;
-+ if (DCSR(s->dma_ch) & DCSR_STOPSTATE) {
-+ DDADR(s->dma_ch) = b->dma_desc->ddadr;
-+ DCSR(s->dma_ch) = DCSR_RUN;
-+ }
-+ }
-+
-+ /* Wait for DMA to complete. */
-+ set_current_state(TASK_INTERRUPTIBLE);
-+#if 0
-+ /*
-+ * The STOPSTATE IRQ never seem to occur if DCSR_STOPIRQEN is set
-+ * along wotj DCSR_RUN. Silicon bug?
-+ */
-+ add_wait_queue(&s->stop_wq, &wait);
-+ DCSR(s->dma_ch) |= DCSR_STOPIRQEN;
-+ schedule();
-+#else
-+ add_wait_queue(&s->frag_wq, &wait);
-+ while ((DCSR(s->dma_ch) & DCSR_RUN) && !signal_pending(current)) {
-+ schedule();
-+ set_current_state(TASK_INTERRUPTIBLE);
-+ }
-+#endif
-+ set_current_state(TASK_RUNNING);
-+ remove_wait_queue(&s->frag_wq, &wait);
-+
-+ /* Restore the descriptor chain. */
-+ if (final_desc) {
-+ final_desc->dcmd = dcmd_save;
-+ final_desc->ddadr &= ~DDADR_STOP;
-+ b->dma_desc->ddadr |= DDADR_STOP;
-+ }
-+ return 0;
-+}
-+
-+
-+static unsigned int audio_poll(struct file *file,
-+ struct poll_table_struct *wait)
-+{
-+ audio_state_t *state = file->private_data;
-+ audio_stream_t *is = state->input_stream;
-+ audio_stream_t *os = state->output_stream;
-+ unsigned int mask = 0;
-+
-+ if (file->f_mode & FMODE_READ) {
-+ /* Start audio input if not already active */
-+ if (!is->buffers && audio_setup_buf(is))
-+ return -ENOMEM;
-+ if (DCSR(is->dma_ch) & DCSR_STOPSTATE) {
-+ DDADR(is->dma_ch) =
-+ is->buffers[is->dma_frag].dma_desc->ddadr;
-+ DCSR(is->dma_ch) = DCSR_RUN;
-+ }
-+ poll_wait(file, &is->frag_wq, wait);
-+ }
-+
-+ if (file->f_mode & FMODE_WRITE) {
-+ if (!os->buffers && audio_setup_buf(os))
-+ return -ENOMEM;
-+ poll_wait(file, &os->frag_wq, wait);
-+ }
-+
-+ if (file->f_mode & FMODE_READ)
-+ if (( is->mapped && is->bytecount > 0) ||
-+ (!is->mapped && atomic_read(&is->sem.count) > 0))
-+ mask |= POLLIN | POLLRDNORM;
-+
-+ if (file->f_mode & FMODE_WRITE)
-+ if (( os->mapped && os->bytecount > 0) ||
-+ (!os->mapped && atomic_read(&os->sem.count) > 0))
-+ mask |= POLLOUT | POLLWRNORM;
-+
-+ return mask;
-+}
-+
-+
-+static int audio_ioctl( struct inode *inode, struct file *file,
-+ uint cmd, ulong arg)
-+{
-+ audio_state_t *state = file->private_data;
-+ audio_stream_t *os = state->output_stream;
-+ audio_stream_t *is = state->input_stream;
-+ long val;
-+
-+ switch (cmd) {
-+ case OSS_GETVERSION:
-+ return put_user(SOUND_VERSION, (int *)arg);
-+
-+ case SNDCTL_DSP_GETBLKSIZE:
-+ if (file->f_mode & FMODE_WRITE)
-+ return put_user(os->fragsize, (int *)arg);
-+ else
-+ return put_user(is->fragsize, (int *)arg);
-+
-+ case SNDCTL_DSP_GETCAPS:
-+ val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP;
-+ if (is && os)
-+ val |= DSP_CAP_DUPLEX;
-+ return put_user(val, (int *)arg);
-+
-+ case SNDCTL_DSP_SETFRAGMENT:
-+ if (get_user(val, (long *) arg))
-+ return -EFAULT;
-+ if (file->f_mode & FMODE_READ) {
-+ int ret = audio_set_fragments(is, val);
-+ if (ret < 0)
-+ return ret;
-+ ret = put_user(ret, (int *)arg);
-+ if (ret)
-+ return ret;
-+ }
-+ if (file->f_mode & FMODE_WRITE) {
-+ int ret = audio_set_fragments(os, val);
-+ if (ret < 0)
-+ return ret;
-+ ret = put_user(ret, (int *)arg);
-+ if (ret)
-+ return ret;
-+ }
-+ return 0;
-+
-+ case SNDCTL_DSP_SYNC:
-+ return audio_sync(file);
-+
-+ case SNDCTL_DSP_SETDUPLEX:
-+ return 0;
-+
-+ case SNDCTL_DSP_POST:
-+ return 0;
-+
-+ case SNDCTL_DSP_GETTRIGGER:
-+ val = 0;
-+ if (file->f_mode & FMODE_READ && DCSR(is->dma_ch) & DCSR_RUN)
-+ val |= PCM_ENABLE_INPUT;
-+ if (file->f_mode & FMODE_WRITE && DCSR(os->dma_ch) & DCSR_RUN)
-+ val |= PCM_ENABLE_OUTPUT;
-+ return put_user(val, (int *)arg);
-+
-+ case SNDCTL_DSP_SETTRIGGER:
-+ if (get_user(val, (int *)arg))
-+ return -EFAULT;
-+ if (file->f_mode & FMODE_READ) {
-+ if (val & PCM_ENABLE_INPUT) {
-+ if (!is->buffers && audio_setup_buf(is))
-+ return -ENOMEM;
-+ if (!(DCSR(is->dma_ch) & DCSR_RUN)) {
-+ audio_buf_t *b = &is->buffers[is->dma_frag];
-+ DDADR(is->dma_ch) = b->dma_desc->ddadr;
-+ DCSR(is->dma_ch) = DCSR_RUN;
-+ }
-+ } else {
-+ DCSR(is->dma_ch) = 0;
-+ }
-+ }
-+ if (file->f_mode & FMODE_WRITE) {
-+ if (val & PCM_ENABLE_OUTPUT) {
-+ if (!os->buffers && audio_setup_buf(os))
-+ return -ENOMEM;
-+ if (!(DCSR(os->dma_ch) & DCSR_RUN)) {
-+ audio_buf_t *b = &os->buffers[os->dma_frag];
-+ DDADR(os->dma_ch) = b->dma_desc->ddadr;
-+ DCSR(os->dma_ch) = DCSR_RUN;
-+ }
-+ } else {
-+ DCSR(os->dma_ch) = 0;
-+ }
-+ }
-+ return 0;
-+
-+ case SNDCTL_DSP_GETOSPACE:
-+ case SNDCTL_DSP_GETISPACE:
-+ {
-+ audio_buf_info inf = { 0, };
-+ audio_stream_t *s = (cmd == SNDCTL_DSP_GETOSPACE) ? os : is;
-+
-+ if ((s == is && !(file->f_mode & FMODE_READ)) ||
-+ (s == os && !(file->f_mode & FMODE_WRITE)))
-+ return -EINVAL;
-+ if (!s->buffers && audio_setup_buf(s))
-+ return -ENOMEM;
-+ inf.bytes = atomic_read(&s->sem.count) * s->fragsize;
-+ inf.bytes -= s->buffers[s->usr_frag].offset;
-+ inf.fragments = inf.bytes / s->fragsize;
-+ inf.fragsize = s->fragsize;
-+ inf.fragstotal = s->nbfrags;
-+ return copy_to_user((void *)arg, &inf, sizeof(inf));
-+ }
-+
-+ case SNDCTL_DSP_GETOPTR:
-+ case SNDCTL_DSP_GETIPTR:
-+ {
-+ count_info inf = { 0, };
-+ audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is;
-+ dma_addr_t ptr;
-+ int bytecount, offset;
-+ unsigned long flags;
-+
-+ if ((s == is && !(file->f_mode & FMODE_READ)) ||
-+ (s == os && !(file->f_mode & FMODE_WRITE)))
-+ return -EINVAL;
-+ local_irq_save(flags);
-+ if (DCSR(s->dma_ch) & DCSR_RUN) {
-+ audio_buf_t *b;
-+ ptr = (s->output) ? DSADR(s->dma_ch) : DTADR(s->dma_ch);
-+ b = &s->buffers[s->dma_frag];
-+ offset = ptr - b->dma_desc->dsadr;
-+ if (offset >= s->fragsize)
-+ offset = s->fragsize - 4;
-+ } else {
-+ offset = 0;
-+ }
-+ inf.ptr = s->dma_frag * s->fragsize + offset;
-+ bytecount = s->bytecount + offset;
-+ s->bytecount = -offset;
-+ inf.blocks = s->fragcount;
-+ s->fragcount = 0;
-+ local_irq_restore(flags);
-+ if (bytecount < 0)
-+ bytecount = 0;
-+ inf.bytes = bytecount;
-+ return copy_to_user((void *)arg, &inf, sizeof(inf));
-+ }
-+
-+ case SNDCTL_DSP_NONBLOCK:
-+ file->f_flags |= O_NONBLOCK;
-+ return 0;
-+
-+ case SNDCTL_DSP_RESET:
-+ if (file->f_mode & FMODE_WRITE)
-+ audio_clear_buf(os);
-+ if (file->f_mode & FMODE_READ)
-+ audio_clear_buf(is);
-+ return 0;
-+
-+ default:
-+ return state->client_ioctl ?
-+ state->client_ioctl(inode, file, cmd, arg) : -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+
-+static int audio_mmap(struct file *file, struct vm_area_struct *vma)
-+{
-+ audio_state_t *state = file->private_data;
-+ audio_stream_t *s;
-+ unsigned long size, vma_addr;
-+ int i, ret;
-+
-+ if (vma->vm_pgoff != 0)
-+ return -EINVAL;
-+
-+ if (vma->vm_flags & VM_WRITE) {
-+ if (!state->wr_ref)
-+ return -EINVAL;;
-+ s = state->output_stream;
-+ } else if (vma->vm_flags & VM_READ) {
-+ if (!state->rd_ref)
-+ return -EINVAL;
-+ s = state->input_stream;
-+ } else return -EINVAL;
-+
-+ if (s->mapped)
-+ return -EINVAL;
-+ size = vma->vm_end - vma->vm_start;
-+ if (size != s->fragsize * s->nbfrags)
-+ return -EINVAL;
-+ if (!s->buffers && audio_setup_buf(s))
-+ return -ENOMEM;
-+ vma_addr = vma->vm_start;
-+ for (i = 0; i < s->nbfrags; i++) {
-+ audio_buf_t *buf = &s->buffers[i];
-+ if (!buf->master)
-+ continue;
-+ ret = remap_page_range(vma, vma->vm_start, buf->dma_desc->dsadr,
-+ buf->master, vma->vm_page_prot);
-+ if (ret)
-+ return ret;
-+ vma_addr += buf->master;
-+ }
-+ for (i = 0; i < s->nbfrags; i++)
-+ s->buffers[i].dma_desc->ddadr &= ~DDADR_STOP;
-+ s->mapped = 1;
-+ return 0;
-+}
-+
-+
-+static int audio_release(struct inode *inode, struct file *file)
-+{
-+ audio_state_t *state = file->private_data;
-+
-+ down(&state->sem);
-+
-+ if (file->f_mode & FMODE_READ) {
-+ audio_clear_buf(state->input_stream);
-+ *state->input_stream->drcmr = 0;
-+ pxa_free_dma(state->input_stream->dma_ch);
-+ state->rd_ref = 0;
-+ }
-+
-+ if (file->f_mode & FMODE_WRITE) {
-+ audio_sync(file);
-+ audio_clear_buf(state->output_stream);
-+ *state->output_stream->drcmr = 0;
-+ pxa_free_dma(state->output_stream->dma_ch);
-+ state->wr_ref = 0;
-+ }
-+
-+ up(&state->sem);
-+ return 0;
-+}
-+
-+
-+int pxa_audio_attach(struct inode *inode, struct file *file,
-+ audio_state_t *state)
-+{
-+ audio_stream_t *is = state->input_stream;
-+ audio_stream_t *os = state->output_stream;
-+ int err;
-+
-+ down(&state->sem);
-+
-+ /* access control */
-+ err = -ENODEV;
-+ if ((file->f_mode & FMODE_WRITE) && !os)
-+ goto out;
-+ if ((file->f_mode & FMODE_READ) && !is)
-+ goto out;
-+ err = -EBUSY;
-+ if ((file->f_mode & FMODE_WRITE) && state->wr_ref)
-+ goto out;
-+ if ((file->f_mode & FMODE_READ) && state->rd_ref)
-+ goto out;
-+
-+ /* request DMA channels */
-+ if (file->f_mode & FMODE_WRITE) {
-+ err = pxa_request_dma(os->name, DMA_PRIO_LOW,
-+ audio_dma_irq, os);
-+ if (err < 0)
-+ goto out;
-+ os->dma_ch = err;
-+ }
-+ if (file->f_mode & FMODE_READ) {
-+ err = pxa_request_dma(is->name, DMA_PRIO_LOW,
-+ audio_dma_irq, is);
-+ if (err < 0) {
-+ if (file->f_mode & FMODE_WRITE) {
-+ *os->drcmr = 0;
-+ pxa_free_dma(os->dma_ch);
-+ }
-+ goto out;
-+ }
-+ is->dma_ch = err;
-+ }
-+
-+ file->private_data = state;
-+ file->f_op->release = audio_release;
-+ file->f_op->write = audio_write;
-+ file->f_op->read = audio_read;
-+ file->f_op->mmap = audio_mmap;
-+ file->f_op->poll = audio_poll;
-+ file->f_op->ioctl = audio_ioctl;
-+ file->f_op->llseek = no_llseek;
-+
-+ if ((file->f_mode & FMODE_WRITE)) {
-+ state->wr_ref = 1;
-+ os->fragsize = AUDIO_FRAGSIZE_DEFAULT;
-+ os->nbfrags = AUDIO_NBFRAGS_DEFAULT;
-+ os->output = 1;
-+ os->mapped = 0;
-+ init_waitqueue_head(&os->frag_wq);
-+ init_waitqueue_head(&os->stop_wq);
-+ *os->drcmr = os->dma_ch | DRCMR_MAPVLD;
-+ }
-+ if (file->f_mode & FMODE_READ) {
-+ state->rd_ref = 1;
-+ is->fragsize = AUDIO_FRAGSIZE_DEFAULT;
-+ is->nbfrags = AUDIO_NBFRAGS_DEFAULT;
-+ is->output = 0;
-+ is->mapped = 0;
-+ init_waitqueue_head(&is->frag_wq);
-+ init_waitqueue_head(&is->stop_wq);
-+ *is->drcmr = is->dma_ch | DRCMR_MAPVLD;
-+ }
-+
-+ err = 0;
-+
-+out:
-+ up(&state->sem);
-+ return err;
-+}
-+
-+EXPORT_SYMBOL(pxa_audio_attach);
-+EXPORT_SYMBOL(pxa_audio_clear_buf);
-+
-+MODULE_AUTHOR("Nicolas Pitre, MontaVista Software Inc.");
-+MODULE_DESCRIPTION("audio interface for the Cotula chip");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.15gum/sound/oss/pxa-audio.h
-===================================================================
---- /dev/null
-+++ linux-2.6.15gum/sound/oss/pxa-audio.h
-@@ -0,0 +1,54 @@
-+/*
-+ * linux/drivers/sound/pxa-audio.h -- audio interface for the Cotula chip
-+ *
-+ * Author: Nicolas Pitre
-+ * Created: Aug 15, 2001
-+ * 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.
-+ */
-+
-+typedef struct {
-+ int offset; /* current buffer position */
-+ char *data; /* actual buffer */
-+ pxa_dma_desc *dma_desc; /* pointer to the starting desc */
-+ int master; /* owner for buffer allocation, contain size whn true */
-+} audio_buf_t;
-+
-+typedef struct {
-+ char *name; /* stream identifier */
-+ audio_buf_t *buffers; /* pointer to audio buffer array */
-+ u_int usr_frag; /* user fragment index */
-+ u_int dma_frag; /* DMA fragment index */
-+ u_int fragsize; /* fragment size */
-+ u_int nbfrags; /* number of fragments */
-+ u_int dma_ch; /* DMA channel number */
-+ dma_addr_t dma_desc_phys; /* phys addr of descriptor ring */
-+ u_int descs_per_frag; /* nbr descriptors per fragment */
-+ int bytecount; /* nbr of processed bytes */
-+ int fragcount; /* nbr of fragment transitions */
-+ struct semaphore sem; /* account for fragment usage */
-+ wait_queue_head_t frag_wq; /* for poll(), etc. */
-+ wait_queue_head_t stop_wq; /* for users of DCSR_STOPIRQEN */
-+ u_long dcmd; /* DMA descriptor dcmd field */
-+ volatile u32 *drcmr; /* the DMA request channel to use */
-+ u_long dev_addr; /* device physical address for DMA */
-+ int mapped:1; /* mmap()'ed buffers */
-+ int output:1; /* 0 for input, 1 for output */
-+} audio_stream_t;
-+
-+typedef struct {
-+ audio_stream_t *output_stream;
-+ audio_stream_t *input_stream;
-+ int dev_dsp; /* audio device handle */
-+ int rd_ref:1; /* open reference for recording */
-+ int wr_ref:1; /* open reference for playback */
-+ int (*client_ioctl)(struct inode *, struct file *, uint, ulong);
-+ struct semaphore sem; /* prevent races in attach/release */
-+} audio_state_t;
-+
-+extern int pxa_audio_attach(struct inode *inode, struct file *file,
-+ audio_state_t *state);
-+extern void pxa_audio_clear_buf(audio_stream_t *s);