summaryrefslogtreecommitdiffstats
path: root/sound/oss/maestro3.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/oss/maestro3.c')
-rw-r--r--sound/oss/maestro3.c2973
1 files changed, 2973 insertions, 0 deletions
diff --git a/sound/oss/maestro3.c b/sound/oss/maestro3.c
new file mode 100644
index 000000000000..f3dec70fcb9b
--- /dev/null
+++ b/sound/oss/maestro3.c
@@ -0,0 +1,2973 @@
+/*****************************************************************************
+ *
+ * ESS Maestro3/Allegro driver for Linux 2.4.x
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * (c) Copyright 2000 Zach Brown <zab@zabbo.net>
+ *
+ * I need to thank many people for helping make this driver happen.
+ * As always, Eric Brombaugh was a hacking machine and killed many bugs
+ * that I was too dumb to notice. Howard Kim at ESS provided reference boards
+ * and as much docs as he could. Todd and Mick at Dell tested snapshots on
+ * an army of laptops. msw and deviant at Red Hat also humoured me by hanging
+ * their laptops every few hours in the name of science.
+ *
+ * Shouts go out to Mike "DJ XPCom" Ang.
+ *
+ * History
+ * v1.23 - Jun 5 2002 - Michael Olson <olson@cs.odu.edu>
+ * added a module option to allow selection of GPIO pin number
+ * for external amp
+ * v1.22 - Feb 28 2001 - Zach Brown <zab@zabbo.net>
+ * allocate mem at insmod/setup, rather than open
+ * limit pci dma addresses to 28bit, thanks guys.
+ * v1.21 - Feb 04 2001 - Zach Brown <zab@zabbo.net>
+ * fix up really dumb notifier -> suspend oops
+ * v1.20 - Jan 30 2001 - Zach Brown <zab@zabbo.net>
+ * get rid of pm callback and use pci_dev suspend/resume instead
+ * m3_probe cleanups, including pm oops think-o
+ * v1.10 - Jan 6 2001 - Zach Brown <zab@zabbo.net>
+ * revert to lame remap_page_range mmap() just to make it work
+ * record mmap fixed.
+ * fix up incredibly broken open/release resource management
+ * duh. fix record format setting.
+ * add SMP locking and cleanup formatting here and there
+ * v1.00 - Dec 16 2000 - Zach Brown <zab@zabbo.net>
+ * port to sexy 2.4 interfaces
+ * properly align instance allocations so recording works
+ * clean up function namespace a little :/
+ * update PCI IDs based on mail from ESS
+ * arbitrarily bump version number to show its 2.4 now,
+ * 2.2 will stay 0., oss_audio port gets 2.
+ * v0.03 - Nov 05 2000 - Zach Brown <zab@zabbo.net>
+ * disable recording but allow dsp to be opened read
+ * pull out most silly compat defines
+ * v0.02 - Nov 04 2000 - Zach Brown <zab@zabbo.net>
+ * changed clocking setup for m3, slowdown fixed.
+ * codec reset is hopefully reliable now
+ * rudimentary apm/power management makes suspend/resume work
+ * v0.01 - Oct 31 2000 - Zach Brown <zab@zabbo.net>
+ * first release
+ * v0.00 - Sep 09 2000 - Zach Brown <zab@zabbo.net>
+ * first pass derivation from maestro.c
+ *
+ * TODO
+ * in/out allocated contiguously so fullduplex mmap will work?
+ * no beep on init (mute)
+ * resetup msrc data memory if freq changes?
+ *
+ * --
+ *
+ * Allow me to ramble a bit about the m3 architecture. The core of the
+ * chip is the 'assp', the custom ESS dsp that runs the show. It has
+ * a small amount of code and data ram. ESS drops binary dsp code images
+ * on our heads, but we don't get to see specs on the dsp.
+ *
+ * The constant piece of code on the dsp is the 'kernel'. It also has a
+ * chunk of the dsp memory that is statically set aside for its control
+ * info. This is the KDATA defines in maestro3.h. Part of its core
+ * data is a list of code addresses that point to the pieces of DSP code
+ * that it should walk through in its loop. These other pieces of code
+ * do the real work. The kernel presumably jumps into each of them in turn.
+ * These code images tend to have their own data area, and one can have
+ * multiple data areas representing different states for each of the 'client
+ * instance' code portions. There is generally a list in the kernel data
+ * that points to the data instances for a given piece of code.
+ *
+ * We've only been given the binary image for the 'minisrc', mini sample
+ * rate converter. This is rather annoying because it limits the work
+ * we can do on the dsp, but it also greatly simplifies the job of managing
+ * dsp data memory for the code and data for our playing streams :). We
+ * statically allocate the minisrc code into a region we 'know' to be free
+ * based on the map of the binary kernel image we're loading. We also
+ * statically allocate the data areas for the maximum number of pcm streams
+ * we can be dealing with. This max is set by the length of the static list
+ * in the kernel data that records the number of minisrc data regions we
+ * can have. Thats right, all software dsp mixing with static code list
+ * limits. Rock.
+ *
+ * How sound goes in and out is still a relative mystery. It appears
+ * that the dsp has the ability to get input and output through various
+ * 'connections'. To do IO from or to a connection, you put the address
+ * of the minisrc client area in the static kernel data lists for that
+ * input or output. so for pcm -> dsp -> mixer, we put the minisrc data
+ * instance in the DMA list and also in the list for the mixer. I guess
+ * it Just Knows which is in/out, and we give some dma control info that
+ * helps. There are all sorts of cool inputs/outputs that it seems we can't
+ * use without dsp code images that know how to use them.
+ *
+ * So at init time we preload all the memory allocation stuff and set some
+ * system wide parameters. When we really get a sound to play we build
+ * up its minisrc header (stream parameters, buffer addresses, input/output
+ * settings). Then we throw its header on the various lists. We also
+ * tickle some KDATA settings that ask the assp to raise clock interrupts
+ * and do some amount of software mixing before handing data to the ac97.
+ *
+ * Sorry for the vague details. Feel free to ask Eric or myself if you
+ * happen to be trying to use this driver elsewhere. Please accept my
+ * apologies for the quality of the OSS support code, its passed through
+ * too many hands now and desperately wants to be rethought.
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/ac97_codec.h>
+#include <linux/wait.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+
+#include "maestro3.h"
+
+#define M_DEBUG 1
+
+#define DRIVER_VERSION "1.23"
+#define M3_MODULE_NAME "maestro3"
+#define PFX M3_MODULE_NAME ": "
+
+#define M3_STATE_MAGIC 0x734d724d
+#define M3_CARD_MAGIC 0x646e6f50
+
+#define ESS_FMT_STEREO 0x01
+#define ESS_FMT_16BIT 0x02
+#define ESS_FMT_MASK 0x03
+#define ESS_DAC_SHIFT 0
+#define ESS_ADC_SHIFT 4
+
+#define DAC_RUNNING 1
+#define ADC_RUNNING 2
+
+#define SND_DEV_DSP16 5
+
+#ifdef M_DEBUG
+static int debug;
+#define DPMOD 1 /* per module load */
+#define DPSTR 2 /* per 'stream' */
+#define DPSYS 3 /* per syscall */
+#define DPCRAP 4 /* stuff the user shouldn't see unless they're really debuggin */
+#define DPINT 5 /* per interrupt, LOTS */
+#define DPRINTK(DP, args...) {if (debug >= (DP)) printk(KERN_DEBUG PFX args);}
+#else
+#define DPRINTK(x)
+#endif
+
+struct m3_list {
+ int curlen;
+ u16 mem_addr;
+ int max;
+};
+
+static int external_amp = 1;
+static int gpio_pin = -1;
+
+struct m3_state {
+ unsigned int magic;
+ struct m3_card *card;
+ unsigned char fmt, enable;
+
+ int index;
+
+ /* this locks around the oss state in the driver */
+ /* no, this lock is removed - only use card->lock */
+ /* otherwise: against what are you protecting on SMP
+ when irqhandler uses s->lock
+ and m3_assp_read uses card->lock ?
+ */
+ struct semaphore open_sem;
+ wait_queue_head_t open_wait;
+ mode_t open_mode;
+
+ int dev_audio;
+
+ struct assp_instance {
+ u16 code, data;
+ } dac_inst, adc_inst;
+
+ /* should be in dmabuf */
+ unsigned int rateadc, ratedac;
+
+ struct dmabuf {
+ void *rawbuf;
+ unsigned buforder;
+ unsigned numfrag;
+ unsigned fragshift;
+ unsigned hwptr, swptr;
+ unsigned total_bytes;
+ int count;
+ unsigned error; /* over/underrun */
+ wait_queue_head_t wait;
+ /* redundant, but makes calculations easier */
+ unsigned fragsize;
+ unsigned dmasize;
+ unsigned fragsamples;
+ /* OSS stuff */
+ unsigned mapped:1;
+ unsigned ready:1;
+ unsigned endcleared:1;
+ unsigned ossfragshift;
+ int ossmaxfrags;
+ unsigned subdivision;
+ /* new in m3 */
+ int mixer_index, dma_index, msrc_index, adc1_index;
+ int in_lists;
+ /* 2.4.. */
+ dma_addr_t handle;
+
+ } dma_dac, dma_adc;
+};
+
+struct m3_card {
+ unsigned int magic;
+
+ struct m3_card *next;
+
+ struct ac97_codec *ac97;
+ spinlock_t ac97_lock;
+
+ int card_type;
+
+#define NR_DSPS 1
+#define MAX_DSPS NR_DSPS
+ struct m3_state channels[MAX_DSPS];
+
+ /* this locks around the physical registers on the card */
+ spinlock_t lock;
+
+ /* hardware resources */
+ struct pci_dev *pcidev;
+ u32 iobase;
+ u32 irq;
+
+ int dacs_active;
+
+ int timer_users;
+
+ struct m3_list msrc_list,
+ mixer_list,
+ adc1_list,
+ dma_list;
+
+ /* for storing reset state..*/
+ u8 reset_state;
+
+ u16 *suspend_mem;
+ int in_suspend;
+ wait_queue_head_t suspend_queue;
+};
+
+/*
+ * an arbitrary volume we set the internal
+ * volume settings to so that the ac97 volume
+ * range is a little less insane. 0x7fff is
+ * max.
+ */
+#define ARB_VOLUME ( 0x6800 )
+
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+enum {
+ ESS_ALLEGRO,
+ ESS_MAESTRO3,
+ /*
+ * a maestro3 with 'hardware strapping', only
+ * found inside ESS?
+ */
+ ESS_MAESTRO3HW,
+};
+
+static char *card_names[] = {
+ [ESS_ALLEGRO] = "Allegro",
+ [ESS_MAESTRO3] = "Maestro3(i)",
+ [ESS_MAESTRO3HW] = "Maestro3(i)hw"
+};
+
+#ifndef PCI_VENDOR_ESS
+#define PCI_VENDOR_ESS 0x125D
+#endif
+
+#define M3_DEVICE(DEV, TYPE) \
+{ \
+.vendor = PCI_VENDOR_ESS, \
+.device = DEV, \
+.subvendor = PCI_ANY_ID, \
+.subdevice = PCI_ANY_ID, \
+.class = PCI_CLASS_MULTIMEDIA_AUDIO << 8, \
+.class_mask = 0xffff << 8, \
+.driver_data = TYPE, \
+}
+
+static struct pci_device_id m3_id_table[] = {
+ M3_DEVICE(0x1988, ESS_ALLEGRO),
+ M3_DEVICE(0x1998, ESS_MAESTRO3),
+ M3_DEVICE(0x199a, ESS_MAESTRO3HW),
+ {0,}
+};
+
+MODULE_DEVICE_TABLE (pci, m3_id_table);
+
+/*
+ * reports seem to indicate that the m3 is limited
+ * to 28bit bus addresses. aaaargggh...
+ */
+#define M3_PCI_DMA_MASK 0x0fffffff
+
+static unsigned
+ld2(unsigned int x)
+{
+ unsigned r = 0;
+
+ if (x >= 0x10000) {
+ x >>= 16;
+ r += 16;
+ }
+ if (x >= 0x100) {
+ x >>= 8;
+ r += 8;
+ }
+ if (x >= 0x10) {
+ x >>= 4;
+ r += 4;
+ }
+ if (x >= 4) {
+ x >>= 2;
+ r += 2;
+ }
+ if (x >= 2)
+ r++;
+ return r;
+}
+
+static struct m3_card *devs;
+
+/*
+ * I'm not very good at laying out functions in a file :)
+ */
+static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf);
+static int m3_suspend(struct pci_dev *pci_dev, pm_message_t state);
+static void check_suspend(struct m3_card *card);
+
+static struct notifier_block m3_reboot_nb = {
+ .notifier_call = m3_notifier,
+};
+
+static void m3_outw(struct m3_card *card,
+ u16 value, unsigned long reg)
+{
+ check_suspend(card);
+ outw(value, card->iobase + reg);
+}
+
+static u16 m3_inw(struct m3_card *card, unsigned long reg)
+{
+ check_suspend(card);
+ return inw(card->iobase + reg);
+}
+static void m3_outb(struct m3_card *card,
+ u8 value, unsigned long reg)
+{
+ check_suspend(card);
+ outb(value, card->iobase + reg);
+}
+static u8 m3_inb(struct m3_card *card, unsigned long reg)
+{
+ check_suspend(card);
+ return inb(card->iobase + reg);
+}
+
+/*
+ * access 16bit words to the code or data regions of the dsp's memory.
+ * index addresses 16bit words.
+ */
+static u16 __m3_assp_read(struct m3_card *card, u16 region, u16 index)
+{
+ m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+ m3_outw(card, index, DSP_PORT_MEMORY_INDEX);
+ return m3_inw(card, DSP_PORT_MEMORY_DATA);
+}
+static u16 m3_assp_read(struct m3_card *card, u16 region, u16 index)
+{
+ unsigned long flags;
+ u16 ret;
+
+ spin_lock_irqsave(&(card->lock), flags);
+ ret = __m3_assp_read(card, region, index);
+ spin_unlock_irqrestore(&(card->lock), flags);
+
+ return ret;
+}
+
+static void __m3_assp_write(struct m3_card *card,
+ u16 region, u16 index, u16 data)
+{
+ m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+ m3_outw(card, index, DSP_PORT_MEMORY_INDEX);
+ m3_outw(card, data, DSP_PORT_MEMORY_DATA);
+}
+static void m3_assp_write(struct m3_card *card,
+ u16 region, u16 index, u16 data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&(card->lock), flags);
+ __m3_assp_write(card, region, index, data);
+ spin_unlock_irqrestore(&(card->lock), flags);
+}
+
+static void m3_assp_halt(struct m3_card *card)
+{
+ card->reset_state = m3_inb(card, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK;
+ mdelay(10);
+ m3_outb(card, card->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+static void m3_assp_continue(struct m3_card *card)
+{
+ m3_outb(card, card->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+/*
+ * This makes me sad. the maestro3 has lists
+ * internally that must be packed.. 0 terminates,
+ * apparently, or maybe all unused entries have
+ * to be 0, the lists have static lengths set
+ * by the binary code images.
+ */
+
+static int m3_add_list(struct m3_card *card,
+ struct m3_list *list, u16 val)
+{
+ DPRINTK(DPSTR, "adding val 0x%x to list 0x%p at pos %d\n",
+ val, list, list->curlen);
+
+ m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+ list->mem_addr + list->curlen,
+ val);
+
+ return list->curlen++;
+
+}
+
+static void m3_remove_list(struct m3_card *card,
+ struct m3_list *list, int index)
+{
+ u16 val;
+ int lastindex = list->curlen - 1;
+
+ DPRINTK(DPSTR, "removing ind %d from list 0x%p\n",
+ index, list);
+
+ if(index != lastindex) {
+ val = m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+ list->mem_addr + lastindex);
+ m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+ list->mem_addr + index,
+ val);
+ }
+
+ m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+ list->mem_addr + lastindex,
+ 0);
+
+ list->curlen--;
+}
+
+static void set_fmt(struct m3_state *s, unsigned char mask, unsigned char data)
+{
+ int tmp;
+
+ s->fmt = (s->fmt & mask) | data;
+
+ tmp = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK;
+
+ /* write to 'mono' word */
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + SRC3_DIRECTION_OFFSET + 1,
+ (tmp & ESS_FMT_STEREO) ? 0 : 1);
+ /* write to '8bit' word */
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + SRC3_DIRECTION_OFFSET + 2,
+ (tmp & ESS_FMT_16BIT) ? 0 : 1);
+
+ tmp = (s->fmt >> ESS_ADC_SHIFT) & ESS_FMT_MASK;
+
+ /* write to 'mono' word */
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + SRC3_DIRECTION_OFFSET + 1,
+ (tmp & ESS_FMT_STEREO) ? 0 : 1);
+ /* write to '8bit' word */
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + SRC3_DIRECTION_OFFSET + 2,
+ (tmp & ESS_FMT_16BIT) ? 0 : 1);
+}
+
+static void set_dac_rate(struct m3_state *s, unsigned int rate)
+{
+ u32 freq;
+
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 8000)
+ rate = 8000;
+
+ s->ratedac = rate;
+
+ freq = ((rate << 15) + 24000 ) / 48000;
+ if(freq)
+ freq--;
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_FREQUENCY,
+ freq);
+}
+
+static void set_adc_rate(struct m3_state *s, unsigned int rate)
+{
+ u32 freq;
+
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 8000)
+ rate = 8000;
+
+ s->rateadc = rate;
+
+ freq = ((rate << 15) + 24000 ) / 48000;
+ if(freq)
+ freq--;
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_FREQUENCY,
+ freq);
+}
+
+static void inc_timer_users(struct m3_card *card)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ card->timer_users++;
+ DPRINTK(DPSYS, "inc timer users now %d\n",
+ card->timer_users);
+ if(card->timer_users != 1)
+ goto out;
+
+ __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+ KDATA_TIMER_COUNT_RELOAD,
+ 240 ) ;
+
+ __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+ KDATA_TIMER_COUNT_CURRENT,
+ 240 ) ;
+
+ m3_outw(card,
+ m3_inw(card, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE,
+ HOST_INT_CTRL);
+out:
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static void dec_timer_users(struct m3_card *card)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ card->timer_users--;
+ DPRINTK(DPSYS, "dec timer users now %d\n",
+ card->timer_users);
+ if(card->timer_users > 0 )
+ goto out;
+
+ __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+ KDATA_TIMER_COUNT_RELOAD,
+ 0 ) ;
+
+ __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+ KDATA_TIMER_COUNT_CURRENT,
+ 0 ) ;
+
+ m3_outw(card, m3_inw(card, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE,
+ HOST_INT_CTRL);
+out:
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/*
+ * {start,stop}_{adc,dac} should be called
+ * while holding the 'state' lock and they
+ * will try to grab the 'card' lock..
+ */
+static void stop_adc(struct m3_state *s)
+{
+ if (! (s->enable & ADC_RUNNING))
+ return;
+
+ s->enable &= ~ADC_RUNNING;
+ dec_timer_users(s->card);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_INSTANCE_READY, 0);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ KDATA_ADC1_REQUEST, 0);
+}
+
+static void stop_dac(struct m3_state *s)
+{
+ if (! (s->enable & DAC_RUNNING))
+ return;
+
+ DPRINTK(DPSYS, "stop_dac()\n");
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_INSTANCE_READY, 0);
+
+ s->enable &= ~DAC_RUNNING;
+ s->card->dacs_active--;
+ dec_timer_users(s->card);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ KDATA_MIXER_TASK_NUMBER,
+ s->card->dacs_active ) ;
+}
+
+static void start_dac(struct m3_state *s)
+{
+ if( (!s->dma_dac.mapped && s->dma_dac.count < 1) ||
+ !s->dma_dac.ready ||
+ (s->enable & DAC_RUNNING))
+ return;
+
+ DPRINTK(DPSYS, "start_dac()\n");
+
+ s->enable |= DAC_RUNNING;
+ s->card->dacs_active++;
+ inc_timer_users(s->card);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_INSTANCE_READY, 1);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ KDATA_MIXER_TASK_NUMBER,
+ s->card->dacs_active ) ;
+}
+
+static void start_adc(struct m3_state *s)
+{
+ if ((! s->dma_adc.mapped &&
+ s->dma_adc.count >= (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
+ || !s->dma_adc.ready
+ || (s->enable & ADC_RUNNING) )
+ return;
+
+ DPRINTK(DPSYS, "start_adc()\n");
+
+ s->enable |= ADC_RUNNING;
+ inc_timer_users(s->card);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ KDATA_ADC1_REQUEST, 1);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_INSTANCE_READY, 1);
+}
+
+static struct play_vals {
+ u16 addr, val;
+} pv[] = {
+ {CDATA_LEFT_VOLUME, ARB_VOLUME},
+ {CDATA_RIGHT_VOLUME, ARB_VOLUME},
+ {SRC3_DIRECTION_OFFSET, 0} ,
+ /* +1, +2 are stereo/16 bit */
+ {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */
+ {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+ {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+ {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+ {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+ {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+ {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+ {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */
+ {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */
+ {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+ {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+ {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+ {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */
+ {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */
+ {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */
+ {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+ {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */
+};
+
+
+/* the mode passed should be already shifted and masked */
+static void m3_play_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size)
+{
+ int dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2);
+ int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2);
+ int dsp_in_buffer = s->dac_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2);
+ int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1;
+ struct dmabuf *db = &s->dma_dac;
+ int i;
+
+ DPRINTK(DPSTR, "mode=%d rate=%d buf=%p len=%d.\n",
+ mode, rate, buffer, size);
+
+#define LO(x) ((x) & 0xffff)
+#define HI(x) LO((x) >> 16)
+
+ /* host dma buffer pointers */
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_HOST_SRC_ADDRL,
+ LO(virt_to_bus(buffer)));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_HOST_SRC_ADDRH,
+ HI(virt_to_bus(buffer)));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1L,
+ LO(virt_to_bus(buffer) + size));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1H,
+ HI(virt_to_bus(buffer) + size));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_HOST_SRC_CURRENTL,
+ LO(virt_to_bus(buffer)));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_HOST_SRC_CURRENTH,
+ HI(virt_to_bus(buffer)));
+#undef LO
+#undef HI
+
+ /* dsp buffers */
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_IN_BUF_BEGIN,
+ dsp_in_buffer);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_IN_BUF_END_PLUS_1,
+ dsp_in_buffer + (dsp_in_size / 2));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_IN_BUF_HEAD,
+ dsp_in_buffer);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_IN_BUF_TAIL,
+ dsp_in_buffer);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_OUT_BUF_BEGIN,
+ dsp_out_buffer);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_OUT_BUF_END_PLUS_1,
+ dsp_out_buffer + (dsp_out_size / 2));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_OUT_BUF_HEAD,
+ dsp_out_buffer);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_OUT_BUF_TAIL,
+ dsp_out_buffer);
+
+ /*
+ * some per client initializers
+ */
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + SRC3_DIRECTION_OFFSET + 12,
+ s->dac_inst.data + 40 + 8);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + SRC3_DIRECTION_OFFSET + 19,
+ s->dac_inst.code + MINISRC_COEF_LOC);
+
+ /* enable or disable low pass filter? */
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + SRC3_DIRECTION_OFFSET + 22,
+ s->ratedac > 45000 ? 0xff : 0 );
+
+ /* tell it which way dma is going? */
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + CDATA_DMA_CONTROL,
+ DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+ /*
+ * set an armload of static initializers
+ */
+ for(i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++)
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->dac_inst.data + pv[i].addr, pv[i].val);
+
+ /*
+ * put us in the lists if we're not already there
+ */
+
+ if(db->in_lists == 0) {
+
+ db->msrc_index = m3_add_list(s->card, &s->card->msrc_list,
+ s->dac_inst.data >> DP_SHIFT_COUNT);
+
+ db->dma_index = m3_add_list(s->card, &s->card->dma_list,
+ s->dac_inst.data >> DP_SHIFT_COUNT);
+
+ db->mixer_index = m3_add_list(s->card, &s->card->mixer_list,
+ s->dac_inst.data >> DP_SHIFT_COUNT);
+
+ db->in_lists = 1;
+ }
+
+ set_dac_rate(s,rate);
+ start_dac(s);
+}
+
+/*
+ * Native record driver
+ */
+static struct rec_vals {
+ u16 addr, val;
+} rv[] = {
+ {CDATA_LEFT_VOLUME, ARB_VOLUME},
+ {CDATA_RIGHT_VOLUME, ARB_VOLUME},
+ {SRC3_DIRECTION_OFFSET, 1} ,
+ /* +1, +2 are stereo/16 bit */
+ {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */
+ {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+ {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+ {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+ {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+ {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+ {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+ {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */
+ {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */
+ {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+ {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+ {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+ {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */
+ {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */
+ {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */
+ {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */
+ {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+ {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */
+ {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */
+};
+
+/* again, passed mode is alrady shifted/masked */
+static void m3_rec_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size)
+{
+ int dsp_in_size = MINISRC_IN_BUFFER_SIZE + (0x10 * 2);
+ int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2);
+ int dsp_in_buffer = s->adc_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2);
+ int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1;
+ struct dmabuf *db = &s->dma_adc;
+ int i;
+
+ DPRINTK(DPSTR, "rec_setup mode=%d rate=%d buf=%p len=%d.\n",
+ mode, rate, buffer, size);
+
+#define LO(x) ((x) & 0xffff)
+#define HI(x) LO((x) >> 16)
+
+ /* host dma buffer pointers */
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_HOST_SRC_ADDRL,
+ LO(virt_to_bus(buffer)));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_HOST_SRC_ADDRH,
+ HI(virt_to_bus(buffer)));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1L,
+ LO(virt_to_bus(buffer) + size));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1H,
+ HI(virt_to_bus(buffer) + size));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_HOST_SRC_CURRENTL,
+ LO(virt_to_bus(buffer)));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_HOST_SRC_CURRENTH,
+ HI(virt_to_bus(buffer)));
+#undef LO
+#undef HI
+
+ /* dsp buffers */
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_IN_BUF_BEGIN,
+ dsp_in_buffer);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_IN_BUF_END_PLUS_1,
+ dsp_in_buffer + (dsp_in_size / 2));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_IN_BUF_HEAD,
+ dsp_in_buffer);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_IN_BUF_TAIL,
+ dsp_in_buffer);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_OUT_BUF_BEGIN,
+ dsp_out_buffer);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_OUT_BUF_END_PLUS_1,
+ dsp_out_buffer + (dsp_out_size / 2));
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_OUT_BUF_HEAD,
+ dsp_out_buffer);
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_OUT_BUF_TAIL,
+ dsp_out_buffer);
+
+ /*
+ * some per client initializers
+ */
+
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + SRC3_DIRECTION_OFFSET + 12,
+ s->adc_inst.data + 40 + 8);
+
+ /* tell it which way dma is going? */
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + CDATA_DMA_CONTROL,
+ DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT +
+ DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+ /*
+ * set an armload of static initializers
+ */
+ for(i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++)
+ m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+ s->adc_inst.data + rv[i].addr, rv[i].val);
+
+ /*
+ * put us in the lists if we're not already there
+ */
+
+ if(db->in_lists == 0) {
+
+ db->adc1_index = m3_add_list(s->card, &s->card->adc1_list,
+ s->adc_inst.data >> DP_SHIFT_COUNT);
+
+ db->dma_index = m3_add_list(s->card, &s->card->dma_list,
+ s->adc_inst.data >> DP_SHIFT_COUNT);
+
+ db->msrc_index = m3_add_list(s->card, &s->card->msrc_list,
+ s->adc_inst.data >> DP_SHIFT_COUNT);
+
+ db->in_lists = 1;
+ }
+
+ set_adc_rate(s,rate);
+ start_adc(s);
+}
+/* --------------------------------------------------------------------- */
+
+static void set_dmaa(struct m3_state *s, unsigned int addr, unsigned int count)
+{
+ DPRINTK(DPINT,"set_dmaa??\n");
+}
+
+static void set_dmac(struct m3_state *s, unsigned int addr, unsigned int count)
+{
+ DPRINTK(DPINT,"set_dmac??\n");
+}
+
+static u32 get_dma_pos(struct m3_card *card,
+ int instance_addr)
+{
+ u16 hi = 0, lo = 0;
+ int retry = 10;
+
+ /*
+ * try and get a valid answer
+ */
+ while(retry--) {
+ hi = m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+ instance_addr + CDATA_HOST_SRC_CURRENTH);
+
+ lo = m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+ instance_addr + CDATA_HOST_SRC_CURRENTL);
+
+ if(hi == m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+ instance_addr + CDATA_HOST_SRC_CURRENTH))
+ break;
+ }
+ return lo | (hi<<16);
+}
+
+static u32 get_dmaa(struct m3_state *s)
+{
+ u32 offset;
+
+ offset = get_dma_pos(s->card, s->dac_inst.data) -
+ virt_to_bus(s->dma_dac.rawbuf);
+
+ DPRINTK(DPINT,"get_dmaa: 0x%08x\n",offset);
+
+ return offset;
+}
+
+static u32 get_dmac(struct m3_state *s)
+{
+ u32 offset;
+
+ offset = get_dma_pos(s->card, s->adc_inst.data) -
+ virt_to_bus(s->dma_adc.rawbuf);
+
+ DPRINTK(DPINT,"get_dmac: 0x%08x\n",offset);
+
+ return offset;
+
+}
+
+static int
+prog_dmabuf(struct m3_state *s, unsigned rec)
+{
+ struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;
+ unsigned rate = rec ? s->rateadc : s->ratedac;
+ unsigned bytepersec;
+ unsigned bufs;
+ unsigned char fmt;
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->card->lock, flags);
+
+ fmt = s->fmt;
+ if (rec) {
+ stop_adc(s);
+ fmt >>= ESS_ADC_SHIFT;
+ } else {
+ stop_dac(s);
+ fmt >>= ESS_DAC_SHIFT;
+ }
+ fmt &= ESS_FMT_MASK;
+
+ db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+
+ bytepersec = rate << sample_shift[fmt];
+ bufs = PAGE_SIZE << db->buforder;
+ if (db->ossfragshift) {
+ if ((1000 << db->ossfragshift) < bytepersec)
+ db->fragshift = ld2(bytepersec/1000);
+ else
+ db->fragshift = db->ossfragshift;
+ } else {
+ db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+ if (db->fragshift < 3)
+ db->fragshift = 3;
+ }
+ db->numfrag = bufs >