Fwd: [RFC] SGI O2 MACE audio ALSA module

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



(third day, third attempt to send this, apologies if the other
attempts arrive too and spam the list, but since the replies from
other lists are reaching this list I thought it better I keep trying
or the conversation would seem rather one sided)

Hi,

It's been a while since I last posted something. Here is the latest
version of my code. It is somewhat of a mess at the moment, but I plan
to tidy it all up when it works better, any // comments are not
permanent features. I have been using trial and error to find why it
play too fast.

I am glad to say that it does play sound correctly, bar one little
issue that has me stuck at the moment, it plays too fast (for any
sample frequency). Also how fast appears to depend on the player
(tried aplay and ogg123).

I have cc the ALSA dev too in the hope that between the two lists
someone may spot something. Please can anyone who replies cc me.

The patch was built against (applies to) linux-2.6.19.7 from
linux-mips.org. It'll probably work on other versions.

Notes on the module it self...

There are two code 'paths' depending on sma_indirect=1|0, this var
will be made a module load argument. When 0 ALSA will be writing
directly to the MACE ring buffer, currently the state of this code is
believed very broken I have not been working on it for a while. When 1
ALSA will be writing to a bounce buffer and the module will copy data
to the ring buffer as per the original driver.

Currently sma_indirect is hard coded 1 and it is this 'path' i'm
currently interested in fixing.

The module has some proc files for debugging, the contents of the
ad1843 regs, and for values in in mace. There is a _spy version of the
module (which wont exist in my final patch) that provides just these
proc files without making any (init) changes to any of the hardware
(ie read only). This allowed me to see what state the O2 jingle left
the various bit in.

The module has a limited mixer, which I want to do a lot more with in
future, and the front volume buttons are supported.

DAC1/ADC map to dev0 on the card, while DAC2 maps to dev1.

I don't know what else to say, please have a listen and a look. It
will play any samples 8k to 48k, and any bit format that ALSA knows
about.

Regards,

Thorben
--- linux-2.6.19.7-original/include/sound/ad1843.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.19.7/include/sound/ad1843.h	2007-04-21 06:49:27.000000000 +0100
@@ -0,0 +1,48 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright 2003 Vivien Chappelier <vivien.chappelier@xxxxxxxxxxxxxx>
+ */
+
+#ifndef __SOUND_AD1843_H
+#define __SOUND_AD1843_H
+
+typedef struct {
+  void *chip;
+  int (* read)(void *chip, int reg);
+  int (* write)(void *chip, int reg, int val);
+} ad1843_t;
+
+#define AD1843_GAIN_RECLEV 0
+#define AD1843_GAIN_LINE   1
+#define AD1843_GAIN_CD     2
+#define AD1843_GAIN_MIC    3
+#define AD1843_GAIN_PCM_0  4
+#define AD1843_GAIN_PCM_1  5
+#define AD1843_GAIN_SIZE   AD1843_GAIN_PCM_1+1
+
+int ad1843_get_gain(ad1843_t *ad1843, int id);
+int ad1843_set_gain(ad1843_t *ad1843, int id, int newval);
+int ad1843_get_recsrc(ad1843_t *ad1843);
+void ad1843_set_resample_mode(ad1843_t *ad1843, int onoff);
+int ad1843_set_recsrc(ad1843_t *ad1843, int newsrc);
+int ad1843_get_outsrc(ad1843_t *ad1843);
+int ad1843_set_outsrc(ad1843_t *ad1843, int mask);
+void ad1843_setup_dac(ad1843_t *ad1843,
+                      unsigned int id,
+                      unsigned int framerate,
+                      snd_pcm_format_t fmt,
+                      unsigned int channels);
+void ad1843_shutdown_dac(ad1843_t *ad1843,
+                         unsigned int id);
+void ad1843_setup_adc(ad1843_t *ad1843,
+                      unsigned int framerate,
+                      snd_pcm_format_t fmt,
+                      unsigned int channels);
+void ad1843_shutdown_adc(ad1843_t *ad1843);
+int ad1843_init(ad1843_t *ad1843);
+char *ad1843_dump_reg(ad1843_t *ad1843, int reg);
+
+#endif /* __SOUND_AD1843_H */

--- linux-2.6.19.7-original/sound/mips/ad1843.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.19.7/sound/mips/ad1843.c	2007-07-02 11:36:16.000000000 +0100
@@ -0,0 +1,909 @@
+/*
+ *   AD1843 low level driver
+ *
+ *   Copyright 2003 Vivien Chappelier <vivien.chappelier@xxxxxxxxxxxxxx>
+ *
+ *   inspired from vwsnd.c (SGI VW audio driver)
+ *     Copyright 1999 Silicon Graphics, Inc.  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 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+
+#include <linux/slab.h>
+
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/vmalloc.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/ad1843.h>
+
+// find and remove any OSS stuff
+#include <linux/soundcard.h>
+
+/*
+ * AD1843 bitfield definitions.  All are named as in the AD1843 data
+ * sheet, with ad1843_ prepended and individual bit numbers removed.
+ *
+ * E.g., bits LSS0 through LSS2 become ad1843_LSS.
+ *
+ * Only the bitfields we need are defined.
+ */
+
+typedef struct ad1843_bitfield {
+       char reg;
+       char lo_bit;
+       char nbits;
+} ad1843_bitfield_t;
+
+static const ad1843_bitfield_t
+	ad1843_PDNO   = {  0, 14,  1 }, /* Converter Power-Down Flag */
+	ad1843_INIT   = {  0, 15,  1 }, /* Clock Initialization Flag */
+	ad1843_RIG    = {  2,  0,  4 }, /* Right ADC Input Gain */
+	ad1843_RMGE   = {  2,  4,  1 }, /* Right ADC Mic Gain Enable */
+	ad1843_RSS    = {  2,  5,  3 }, /* Right ADC Source Select */
+	ad1843_LIG    = {  2,  8,  4 }, /* Left ADC Input Gain */
+	ad1843_LMGE   = {  2, 12,  1 }, /* Left ADC Mic Gain Enable */
+	ad1843_LSS    = {  2, 13,  3 }, /* Left ADC Source Select */
+	ad1843_RX1M   = {  4,  0,  5 }, /* Right Aux 1 Mix Gain/Atten */
+	ad1843_RX1MM  = {  4,  7,  1 }, /* Right Aux 1 Mix Mute */
+	ad1843_LX1M   = {  4,  8,  5 }, /* Left Aux 1 Mix Gain/Atten */
+	ad1843_LX1MM  = {  4, 15,  1 }, /* Left Aux 1 Mix Mute */
+	ad1843_RX2M   = {  5,  0,  5 }, /* Right Aux 2 Mix Gain/Atten */
+	ad1843_RX2MM  = {  5,  7,  1 }, /* Right Aux 2 Mix Mute */
+	ad1843_LX2M   = {  5,  8,  5 }, /* Left Aux 2 Mix Gain/Atten */
+	ad1843_LX2MM  = {  5, 15,  1 }, /* Left Aux 2 Mix Mute */
+	ad1843_RMCM   = {  7,  0,  5 }, /* Right Mic Mix Gain/Atten */
+	ad1843_RMCMM  = {  7,  7,  1 }, /* Right Mic Mix Mute */
+	ad1843_LMCM   = {  7,  8,  5 }, /* Left Mic Mix Gain/Atten */
+	ad1843_LMCMM  = {  7, 15,  1 }, /* Left Mic Mix Mute */
+	ad1843_HPOS   = {  8,  4,  1 }, /* Headphone Output Voltage Swing */
+	ad1843_HPOM   = {  8,  5,  1 }, /* Headphone Output Mute */
+	ad1843_MPOM   = {  8,  6,  1 }, /* Mono Output Mute */
+	ad1843_RDA1G  = {  9,  0,  6 }, /* Right DAC1 Analog/Digital Gain */
+	ad1843_RDA1GM = {  9,  7,  1 }, /* Right DAC1 Analog Mute */
+	ad1843_LDA1G  = {  9,  8,  6 }, /* Left DAC1 Analog/Digital Gain */
+	ad1843_LDA1GM = {  9, 15,  1 }, /* Left DAC1 Analog Mute */
+	ad1843_RDA2G  = { 10,  0,  6 }, /* Right DAC2 Analog/Digital Gain */
+	ad1843_RDA2GM = { 10,  7,  1 }, /* Right DAC2 Analog Mute */
+	ad1843_LDA2G  = { 10,  8,  6 }, /* Left DAC2 Analog/Digital Gain */
+	ad1843_LDA2GM = { 10, 15,  1 }, /* Left DAC2 Analog Mute */
+	ad1843_RDA1AM = { 11,  7,  1 }, /* Right DAC1 Digital Mute */
+	ad1843_LDA1AM = { 11, 15,  1 }, /* Left DAC1 Digital Mute */
+	ad1843_RDA2AM = { 12,  7,  1 }, /* Right DAC1 Digital Mute */
+	ad1843_LDA2AM = { 12, 15,  1 }, /* Left DAC1 Digital Mute */
+	ad1843_ADLC   = { 15,  0,  2 }, /* ADC Left Sample Rate Source */
+	ad1843_ADRC   = { 15,  2,  2 }, /* ADC Right Sample Rate Source */
+	ad1843_DA1C   = { 15,  8,  2 }, /* DAC1 Sample Rate Source */
+	ad1843_DA2C   = { 15, 10,  2 }, /* DAC2 Sample Rate Source */
+	ad1843_C1M    = { 16,  0,  8 }, /* Clock 1 Rate Modifier */
+	ad1843_C1C128 = { 16, 10,  1 }, /* Clock 1 CONV1 Freq select */
+	ad1843_C1C    = { 17,  0, 16 }, /* Clock 1 Sample Rate Select */
+	ad1843_C2M    = { 19,  0,  8 }, /* Clock 2 Rate Modifier */
+	ad1843_C2C    = { 20,  0, 16 }, /* Clock 2 Sample Rate Select */
+	ad1843_C3M    = { 22,  0,  8 }, /* Clock 3 Rate Modifier */
+	ad1843_C3C    = { 23,  0, 16 }, /* Clock 3 Sample Rate Select */
+	ad1843_DAADL  = { 25,  4,  2 }, /* Digital ADC Left Source Select */
+	ad1843_DAADR  = { 25,  6,  2 }, /* Digital ADC Right Source Select */
+	ad1843_DRSFLT = { 25, 15,  1 }, /* Digital Reampler Filter Mode */
+	ad1843_ADLF   = { 26,  0,  2 }, /* ADC Left Channel Data Format */
+	ad1843_ADRF   = { 26,  2,  2 }, /* ADC Right Channel Data Format */
+	ad1843_ADTLK  = { 26,  4,  1 }, /* ADC Transmit Lock Mode Select */
+	ad1843_FRS    = { 26,  6,  1 }, /* Frame size select */
+	ad1843_SCF    = { 26,  7,  1 }, /* SCLK Frequency Select */
+	ad1843_DA1F   = { 26,  8,  2 }, /* DAC1 Data Format Select */
+	ad1843_DA2F   = { 26, 10,  2 }, /* DAC2 Data Format Select */
+	ad1843_DA1SM  = { 26, 14,  1 }, /* DAC1 Stereo/Mono Mode Select */
+	ad1843_DA2SM  = { 26, 15,  1 }, /* DAC2 Stereo/Mono Mode Select */
+	ad1843_ADLEN  = { 27,  0,  1 }, /* ADC Left Channel Enable */
+	ad1843_ADREN  = { 27,  1,  1 }, /* ADC Right Channel Enable */
+	ad1843_AAMEN  = { 27,  4,  1 }, /* Analog to Analog Mix Enable */
+	ad1843_ANAEN  = { 27,  7,  1 }, /* Analog Channel Enable */
+	ad1843_DA1EN  = { 27,  8,  1 }, /* DAC1 Enable */
+	ad1843_DA2EN  = { 27,  9,  1 }, /* DAC2 Enable */
+	ad1843_ENBT1  = { 28,  2,  1 }, /* BIT1 Pin Enable */
+	ad1843_ENCV1  = { 28,  3,  1 }, /* CONV1 Pin Enable */
+	ad1843_ENBT2  = { 28,  4,  1 }, /* BIT2 Pin Enable */
+	ad1843_ENCV2  = { 28,  5,  1 }, /* CONV2 Pin Enable */
+	ad1843_ENBT3  = { 28,  6,  1 }, /* BIT3 Pin Enable */
+	ad1843_ENCV3  = { 28,  7,  1 }, /* CONV3 Pin Enable */
+	ad1843_ENCLKO = { 28, 10,  1 }, /* ClockOut Pin Enable */
+	ad1843_C1EN   = { 28, 11,  1 }, /* Clock Generator 1 Enable */
+	ad1843_C2EN   = { 28, 12,  1 }, /* Clock Generator 2 Enable */
+	ad1843_C3EN   = { 28, 13,  1 }, /* Clock Generator 3 Enable */
+	ad1843_ACEN   = { 28, 14,  1 }, /* Autocalibration enable */
+	ad1843_PDNI   = { 28, 15,  1 }; /* Converter Power Down */
+
+
+/*
+ * The various registers of the AD1843 use three different formats for
+ * specifying gain.  The ad1843_gain structure parameterizes the
+ * formats.
+ */
+
+typedef struct ad1843_gain {
+       int     negative;               /* nonzero if gain is negative. */
+       const ad1843_bitfield_t *lfield;
+       const ad1843_bitfield_t *rfield;
+} ad1843_gain_t;
+
+const ad1843_gain_t ad1843_gain_RECLEV
+                               = { 0, &ad1843_LIG,   &ad1843_RIG };
+const ad1843_gain_t ad1843_gain_LINE
+                               = { 1, &ad1843_LX1M,  &ad1843_RX1M };
+const ad1843_gain_t ad1843_gain_CD
+                               = { 1, &ad1843_LX2M,  &ad1843_RX2M };
+const ad1843_gain_t ad1843_gain_MIC
+                               = { 1, &ad1843_LMCM,  &ad1843_RMCM };
+const ad1843_gain_t ad1843_gain_PCM_0
+                               = { 1, &ad1843_LDA1G, &ad1843_RDA1G };
+const ad1843_gain_t ad1843_gain_PCM_1
+                               = { 1, &ad1843_LDA2G, &ad1843_RDA2G };
+
+const ad1843_gain_t *ad1843_gain[AD1843_GAIN_SIZE] = {
+  &ad1843_gain_RECLEV,
+  &ad1843_gain_LINE,
+  &ad1843_gain_CD,
+  &ad1843_gain_MIC,
+  &ad1843_gain_PCM_0,
+  &ad1843_gain_PCM_1,
+};
+
+/* read the current value of an AD1843 bitfield. */
+
+static int ad1843_read_bits(ad1843_t *ad1843, const ad1843_bitfield_t *field)
+{
+       int w = ad1843->read(ad1843->chip, field->reg);
+       int val = w >> field->lo_bit & ((1 << field->nbits) - 1);
+
+       return val;
+}
+
+/*
+ * write a new value to an AD1843 bitfield and return the old value.
+ */
+
+static int ad1843_write_bits(ad1843_t *ad1843,
+                            const ad1843_bitfield_t *field,
+                            int newval)
+{
+       int w = ad1843->read(ad1843->chip, field->reg);
+       int mask = ((1 << field->nbits) - 1) << field->lo_bit;
+       int oldval = (w & mask) >> field->lo_bit;
+       int newbits = (newval << field->lo_bit) & mask;
+       w = (w & ~mask) | newbits;
+       (void) ad1843->write(ad1843->chip, field->reg, w);
+
+       return oldval;
+}
+
+/*
+ * ad1843_read_multi reads multiple bitfields from the same AD1843
+ * register.  It uses a single read cycle to do it.  (Reading the
+ * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20
+ * microseconds.)
+ *
+ * Called like this.
+ *
+ *  ad1843_read_multi(ad1843, nfields,
+ *                   &ad1843_FIELD1, &val1,
+ *                   &ad1843_FIELD2, &val2, ...);
+ */
+
+static void ad1843_read_multi(ad1843_t *ad1843, int argcount, ...)
+{
+       va_list ap;
+       const ad1843_bitfield_t *fp;
+       int w = 0, mask, *value, reg = -1;
+
+       va_start(ap, argcount);
+       while (--argcount >= 0) {
+               fp = va_arg(ap, const ad1843_bitfield_t *);
+               value = va_arg(ap, int *);
+
+               if (reg == -1) {
+                       reg = fp->reg;
+                       w = ad1843->read(ad1843->chip, reg);
+               }
+
+               mask = (1 << fp->nbits) - 1;
+               *value = w >> fp->lo_bit & mask;
+       }
+       va_end(ap);
+
+}
+
+/*
+ * ad1843_write_multi stores multiple bitfields into the same AD1843
+ * register.  It uses one read and one write cycle to do it.
+ *
+ * Called like this.
+ *
+ *  ad1843_write_multi(ad1843, nfields,
+ *                    &ad1843_FIELD1, val1,
+ *                    &ad1843_FIELF2, val2, ...);
+ */
+
+static void ad1843_write_multi(ad1843_t *ad1843, int argcount, ...)
+{
+       va_list ap;
+       int reg;
+       const ad1843_bitfield_t *fp;
+       int value;
+       int w, m, mask, bits;
+
+       mask = 0;
+       bits = 0;
+       reg = -1;
+
+       va_start(ap, argcount);
+       while (--argcount >= 0) {
+               fp = va_arg(ap, const ad1843_bitfield_t *);
+               value = va_arg(ap, int);
+               if (reg == -1)
+                       reg = fp->reg;
+
+               m = ((1 << fp->nbits) - 1) << fp->lo_bit;
+               mask |= m;
+               bits |= (value << fp->lo_bit) & m;
+       }
+       va_end(ap);
+
+       if (~mask & 0xFFFF)
+               w = ad1843->read(ad1843->chip, reg);
+       else
+               w = 0;
+       w = (w & ~mask) | bits;
+       (void) ad1843->write(ad1843->chip, reg, w);
+}
+
+/*
+ * ad1843_get_gain reads the specified register and extracts the gain value
+ * using the supplied gain type.  It returns the gain in OSS format.
+ */
+
+int ad1843_get_gain(ad1843_t *ad1843, int id)
+{
+       int lg, rg;
+        const ad1843_gain_t *gp = ad1843_gain[id];
+       unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+       ad1843_read_multi(ad1843, 2, gp->lfield, &lg, gp->rfield, &rg);
+       if (gp->negative) {
+               lg = mask - lg;
+               rg = mask - rg;
+       }
+       lg = (lg * 100 + (mask >> 1)) / mask;
+       rg = (rg * 100 + (mask >> 1)) / mask;
+       return lg << 0 | rg << 8;
+}
+
+/*
+ * Set an audio channel's gain. Converts from OSS format to AD1843's
+ * format.
+ *
+ * Returns the new gain, which may be lower than the old gain.
+ */
+
+int ad1843_set_gain(ad1843_t *ad1843,
+                    int id,
+                    int newval)
+{
+        const ad1843_gain_t *gp = ad1843_gain[id];
+       unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+       int lg = newval >> 0 & 0xFF;
+       int rg = newval >> 8;
+       if (lg < 0 || lg > 100 || rg < 0 || rg > 100)
+               return -EINVAL;
+       lg = (lg * mask + (mask >> 1)) / 100;
+       rg = (rg * mask + (mask >> 1)) / 100;
+       if (gp->negative) {
+               lg = mask - lg;
+               rg = mask - rg;
+       }
+       ad1843_write_multi(ad1843, 2, gp->lfield, lg, gp->rfield, rg);
+       return ad1843_get_gain(ad1843, id);
+}
+
+/* Returns the current recording source, in OSS format. */
+
+int ad1843_get_recsrc(ad1843_t *ad1843)
+{
+       int ls = ad1843_read_bits(ad1843, &ad1843_LSS);
+
+       switch (ls) {
+       case 1:
+               return SOUND_MASK_MIC;
+       case 2:
+               return SOUND_MASK_LINE;
+       case 3:
+               return SOUND_MASK_CD;
+       case 6:
+               return SOUND_MASK_PCM;
+       default:
+               return -1;
+       }
+}
+
+/*
+ * Enable/disable digital resample mode in the AD1843.
+ *
+ * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down
+ * while switching modes.  So we save DA's state, power them down,
+ * switch into/out of resample mode, power them up, and restore state.
+ *
+ * This will cause audible glitches if D/A or A/D is going on, so the
+ * driver disallows that (in mixer_write_ioctl()).
+ *
+ * The open question is, is this worth doing?  I'm leaving it in,
+ * because it's written, but...
+ */
+
+void ad1843_set_resample_mode(ad1843_t *ad1843, int onoff)
+{
+       /* Save DA's mute and gain (addr 9/10 is analog gain/attenuation) */
+       int save_da1 = ad1843->read(ad1843->chip, 9);
+       int save_da2 = ad1843->read(ad1843->chip, 10);
+
+       /* Power down A/D and D/A. */
+       ad1843_write_multi(ad1843, 4,
+                          &ad1843_DA1EN, 0,
+                          &ad1843_DA2EN, 0,
+                          &ad1843_ADLEN, 0,
+                          &ad1843_ADREN, 0);
+
+       /* Switch mode */
+       ad1843_write_bits(ad1843, &ad1843_DRSFLT, onoff);
+
+       /* Power up A/D and D/A. */
+       ad1843_write_multi(ad1843, 3,
+                          &ad1843_DA1EN, 1,
+                          &ad1843_DA2EN, 1,
+                          &ad1843_ADLEN, 1,
+                          &ad1843_ADREN, 1);
+
+       /* Restore DA's mute and gain. */
+       ad1843->write(ad1843->chip, 9, save_da1);
+       ad1843->write(ad1843->chip, 10, save_da2);
+}
+
+/*
+ * Set recording source.  Arg newsrc specifies an OSS channel mask.
+ *
+ * The complication is that when we switch into/out of loopback mode
+ * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of
+ * digital resampling mode.
+ *
+ * Returns newsrc on success, -errno on failure.
+ */
+
+int ad1843_set_recsrc(ad1843_t *ad1843, int newsrc)
+{
+       int bits;
+       int oldbits;
+
+       switch (newsrc) {
+       case SOUND_MASK_PCM:
+               bits = 6;
+               break;
+
+       case SOUND_MASK_MIC:
+               bits = 1;
+               break;
+
+       case SOUND_MASK_LINE:
+               bits = 2;
+               break;
+
+       case SOUND_MASK_CD:
+               bits = 3;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       oldbits = ad1843_read_bits(ad1843, &ad1843_LSS);
+       if (newsrc == SOUND_MASK_PCM && oldbits != 6) {
+
+               ad1843_set_resample_mode(ad1843, 1);
+               ad1843_write_multi(ad1843, 2,
+                                  &ad1843_DAADL, 2,
+                                  &ad1843_DAADR, 2);
+       } else if (newsrc != SOUND_MASK_PCM && oldbits == 6) {
+
+               ad1843_set_resample_mode(ad1843, 0);
+               ad1843_write_multi(ad1843, 2,
+                                  &ad1843_DAADL, 0,
+                                  &ad1843_DAADR, 0);
+       }
+       ad1843_write_multi(ad1843, 2, &ad1843_LSS, bits, &ad1843_RSS, bits);
+       return newsrc;
+}
+
+/*
+ * Return current output sources, in OSS format.
+ */
+
+int ad1843_get_outsrc(ad1843_t *ad1843)
+{
+       int pcm, line, mic, cd;
+
+       pcm  = ad1843_read_bits(ad1843, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM;
+       line = ad1843_read_bits(ad1843, &ad1843_LX1MM)  ? 0 : SOUND_MASK_LINE;
+       cd   = ad1843_read_bits(ad1843, &ad1843_LX2MM)  ? 0 : SOUND_MASK_CD;
+       mic  = ad1843_read_bits(ad1843, &ad1843_LMCMM)  ? 0 : SOUND_MASK_MIC;
+
+       return pcm | line | cd | mic;
+}
+
+/*
+ * Set output sources.  Arg is a mask of active sources in OSS format.
+ *
+ * Returns source mask on success, -errno on failure.
+ */
+
+int ad1843_set_outsrc(ad1843_t *ad1843, int mask)
+{
+       int pcm, line, mic, cd;
+
+       if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE |
+                    SOUND_MASK_CD | SOUND_MASK_MIC))
+               return -EINVAL;
+       pcm  = (mask & SOUND_MASK_PCM)  ? 0 : 1;
+       line = (mask & SOUND_MASK_LINE) ? 0 : 1;
+       mic  = (mask & SOUND_MASK_MIC)  ? 0 : 1;
+       cd   = (mask & SOUND_MASK_CD)   ? 0 : 1;
+
+       ad1843_write_multi(ad1843, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm);
+       ad1843_write_multi(ad1843, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line);
+       ad1843_write_multi(ad1843, 2, &ad1843_LX2MM, cd,   &ad1843_RX2MM, cd);
+       ad1843_write_multi(ad1843, 2, &ad1843_LMCMM, mic,  &ad1843_RMCMM, mic);
+
+       return mask;
+}
+
+/* Setup ad1843 for D/A conversion. */
+
+void ad1843_setup_dac(ad1843_t *ad1843,
+                      unsigned int id,
+                      unsigned int framerate,
+                      snd_pcm_format_t fmt,
+                      unsigned int channels)
+{
+       int ad_fmt = 1, ad_mode = 0;
+
+       switch (fmt) {
+       case SNDRV_PCM_FORMAT_S8:       ad_fmt = 0; break;
+       case SNDRV_PCM_FORMAT_U8:       ad_fmt = 0; break;
+       case SNDRV_PCM_FORMAT_S16_BE:   ad_fmt = 1; break;
+       case SNDRV_PCM_FORMAT_S24_BE:   ad_fmt = 1; break;
+       case SNDRV_PCM_FORMAT_MU_LAW:   ad_fmt = 2; break;
+       case SNDRV_PCM_FORMAT_A_LAW:    ad_fmt = 3; break;
+       default: printk("errk!\n"); break;
+       }
+
+       switch (channels) {
+       case 2:                 ad_mode = 0; break;
+       case 1:                 ad_mode = 1; break;
+       default: printk("errk!\n"); break;
+       }
+
+        if(1 == id) {
+		ad1843_write_bits(ad1843, &ad1843_C1C, framerate);
+		//ad1843_write_bits(ad1843, &ad1843_C1M, 0x00);
+		//ad1843_write_bits(ad1843, &ad1843_C1C128, 1);
+		//ad1843_write_bits(ad1843, &ad1843_FRS, 0);
+		ad1843_write_multi(ad1843, 2,
+			&ad1843_DA1SM, ad_mode,
+			&ad1843_DA1F, ad_fmt);
+		//ad1843_write_bits(ad1843, &ad1843_DA1EN, 1);
+
+        } else {
+		ad1843_write_bits(ad1843, &ad1843_C3C, framerate);
+		ad1843_write_multi(ad1843, 2,
+			&ad1843_DA2SM, ad_mode,
+			&ad1843_DA2F, ad_fmt);
+		//ad1843_write_bits(ad1843, &ad1843_DA2EN, 1);
+       }
+
+/*FIXME Debugging, will remove */
+ad1843_write_bits(ad1843, &ad1843_C1C, framerate);
+ad1843_write_bits(ad1843, &ad1843_C2C, framerate);
+ad1843_write_bits(ad1843, &ad1843_C3C, framerate);
+//ad1843_write_bits(ad1843, &ad1843_C1M, 0x00);
+//ad1843_write_bits(ad1843, &ad1843_C2M, 0x00);
+//ad1843_write_bits(ad1843, &ad1843_C3M, 0x00);
+
+}
+
+void ad1843_shutdown_dac(ad1843_t *ad1843, unsigned int id)
+{
+       if(id)
+               ad1843_write_bits(ad1843, &ad1843_DA2F, 1);
+        else
+               ad1843_write_bits(ad1843, &ad1843_DA1F, 1);
+}
+
+void ad1843_setup_adc(ad1843_t *ad1843,
+                      unsigned int framerate,
+                      snd_pcm_format_t fmt,
+                      unsigned int channels)
+{
+       int da_fmt = 0;
+
+       switch (fmt) {
+       case SNDRV_PCM_FORMAT_S8:       da_fmt = 0; break;
+       case SNDRV_PCM_FORMAT_U8:       da_fmt = 0; break;
+       case SNDRV_PCM_FORMAT_S16_BE:   da_fmt = 1; break;
+       case SNDRV_PCM_FORMAT_MU_LAW:   da_fmt = 2; break;
+       case SNDRV_PCM_FORMAT_A_LAW:    da_fmt = 3; break;
+       default: printk("errk!\n"); break;
+       }
+
+       ad1843_write_bits(ad1843, &ad1843_C3C, framerate);
+       ad1843_write_multi(ad1843, 2,
+                          &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt);
+}
+
+void ad1843_shutdown_adc(ad1843_t *ad1843)
+{
+       /* nothing to do */
+}
+
+/*
+ * Fully initialize the ad1843.  As described in the AD1843 data
+ * sheet, section "START-UP SEQUENCE".  The numbered comments are
+ * subsection headings from the data sheet.  See the data sheet, pages
+ * 52-54, for more info.
+ *
+ * return 0 on success, -errno on failure.  */
+
+int ad1843_init(ad1843_t *ad1843)
+{
+       unsigned long later;
+
+	/* 1. Power up in hardware */
+
+	/* 2. Assert ^RESET^ to 0, wait 100ns
+	 * 3. Deassert ^RESET^ _400 to 800us, poll INIT to see when ready
+	 * Done in calling driver */
+	udelay(800);
+
+	/* 3. Check that the clocks are stable */
+	if (ad1843_read_bits(ad1843, &ad1843_INIT) != 0) {
+		printk(KERN_ERR "ad1843: AD1843 Clocks not yet stable\n");
+		return -EIO;
+	}
+
+	/*set serial bus speed */
+	ad1843_write_bits(ad1843, &ad1843_SCF, 0);
+
+	/* Power down conv resources */
+	/*ad1843_write_bits(ad1843, &ad1843_PDNI, 1);
+	udelay(800);*/
+
+	/* 4. Put the conversion resources into standby PDNI to 0
+	 * Force Enable auto calibrate ACEN to 1 */
+	ad1843_write_multi(ad1843, 2,
+		&ad1843_PDNI, 0, &ad1843_ACEN, 1);
+	later = jiffies + HZ / 2;  /* roughly half a second */
+
+	/* 5. While in standby
+	 * Power up the clock generators and enable clock output pins. */
+	ad1843_write_multi(ad1843, 10, &ad1843_ENCLKO, 1,
+		&ad1843_C1EN, 1, &ad1843_ENCV1, 1, &ad1843_ENBT1, 1,
+		&ad1843_C2EN, 1, &ad1843_ENCV2, 1, &ad1843_ENBT2, 1,
+		&ad1843_C3EN, 1, &ad1843_ENCV3, 1, &ad1843_ENBT3, 1);
+
+	/* 6. Configure conversion resources while they are in standby. */
+
+
+
+	/* Did we come out of standby ? */
+	while (ad1843_read_bits(ad1843, &ad1843_PDNO)) {
+		if (time_after(jiffies, later)) {
+			printk(KERN_ERR "ad1843: AD1843 won't power up\n");
+                       return -EIO;
+		}
+		schedule();
+	}
+
+
+	/* DAC1 = Clock1, ADC = Clock2, DAC2 = Clock3  Always.
+	 * Based an ad1843 state from startup jingle */
+	ad1843_write_multi(ad1843, 4,
+		&ad1843_DA1C, 1, &ad1843_DA2C, 3,
+		&ad1843_ADLC, 2, &ad1843_ADRC, 2);
+
+	/* 7. Enable conversion resources. */
+	ad1843_write_bits(ad1843, &ad1843_ADTLK, 1);
+	ad1843_write_multi(ad1843, 6,
+		&ad1843_ANAEN, 1, &ad1843_AAMEN, 1,
+		&ad1843_DA1EN, 1, &ad1843_DA2EN, 1,
+		&ad1843_ADLEN, 1, &ad1843_ADREN, 1);
+
+       /* 8. Configure conversion resources while they are enabled. */
+
+
+       /* Unmute all channels. */
+
+       ad1843_set_outsrc(ad1843,
+                         (SOUND_MASK_PCM | SOUND_MASK_LINE |
+                          SOUND_MASK_MIC | SOUND_MASK_CD));
+       ad1843_write_multi(ad1843, 2,
+		&ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0);
+
+	ad1843_write_multi(ad1843, 2,
+		&ad1843_LDA2AM, 0, &ad1843_RDA2AM, 0);
+
+	ad1843_write_multi(ad1843, 2,
+		&ad1843_LDA1GM, 0, &ad1843_RDA1GM, 0);
+
+       /* Set default recording source to Line In and set
+        * mic gain to +20 dB.
+        */
+       ad1843_set_recsrc(ad1843, SOUND_MASK_LINE);
+       ad1843_write_multi(ad1843, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1);
+
+       /* Set Speaker Out level to +/- 4V and unmute it. */
+       ad1843_write_multi(ad1843, 3,
+                           &ad1843_HPOS, 1,
+                           &ad1843_HPOM, 0,
+                           &ad1843_MPOM, 0);
+
+       return 0;
+}
+
+char *ad1843_dump_reg(ad1843_t *ad1843, int reg)
+{
+	static const char *reg_fmt[32] = {
+		"Reg  0: Codec Status and Revision Identification\n"
+		"|INIT   %u |PDNO   %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |RES    %u |RES    %u | (%u)\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|ID3    %u ·ID2    %u ·ID1    %u ·ID0    %u | (%u)\n",
+		/*reg 1*/
+		"Reg  1: Channel Status Flags\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |SU2    %u |SU1    %u | (%u)\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|OVR1   %u ·OVR0   %u |OVL1   %u ·OVL0   %u | (%u)\n",
+		/*reg 2*/
+		"Reg  2: Input Control - ADC Source and Gain/Attenuation\n"
+		"|LSS2   %u ·LSS1   %u ·LSS0   %u |LMGE   %u "
+		"|LIG3   %u ·LIG2   %u ·LIG1   %u ·LIG0   %u | (%u)\n"
+		"|RSS2   %u ·RSS1   %u ·RSS0   %u |RMGE   %u "
+		"|RIG3   %u ·RIG2   %u ·RIG1   %u ·RIG0   %u | (%u)\n",
+		/*reg 3*/
+		"Reg  3: Mix Control - DAC2 to Mixer\n"
+		"|LD2MM  %u |RES    %u |RES    %u |LD2M4  %u "
+		"·LD2M3  %u ·LD2M2  %u ·LD2M1  %u ·LD2M0  %u | (%u)\n"
+		"|RD2MM  %u |RES    %u |RES    %u |RD2M4  %u "
+		"·RD2M3  %u ·RD2M2  %u ·RD2M1  %u ·RD2M0  %u | (%u)\n",
+		/*reg 4*/
+		"Reg  4: Auxiliary 1 to DAC1\n"
+		"|LX1MM  %u |RES    %u |RES    %u |LX1M4  %u "
+		"·LX1M3  %u ·LX1M2  %u ·LX1M1  %u ·LX1M0  %u | (%u)\n"
+		"|RX1MM  %u |RES    %u |RES    %u |RX1M4  %u "
+		"·RX1M3  %u ·RX1M2  %u ·RX1M1  %u ·RX1M0  %u | (%u)\n",
+		/*reg 5 */
+		"Reg  5: Mix Control - Auxiliary 2 to Mixer\n"
+		"|LX2MM  %u |RES    %u |RES    %u |LX2M4  %u "
+		"·LX2M3  %u ·LX2M2  %u ·LX2M1  %u ·LX2M0  %u | (%u)\n"
+		"|RX2MM  %u |RES    %u |RES    %u |RX2M4  %u "
+		"·RX2M3  %u ·RX2M2  %u ·RX2M1  %u ·RX2M0  %u | (%u)\n",
+		/*reg 6*/
+		"Reg  6: Mix Control - Auxiliary 3 to Mixer\n"
+		"|LX3MM  %u |RES    %u |RES    %u |LX3M4  %u "
+		"·LX3M3  %u ·LX3M2  %u ·LX3M1  %u ·LX3M0  %u | (%u)\n"
+		"|RX3MM  %u |RES    %u |RES    %u |RX3M4  %u "
+		"·RX3M3  %u ·RX3M2  %u ·RX3M1  %u ·RX3M0  %u | (%u)\n",
+		/*reg 7*/
+		"Reg  7: Mix Control - Mic to Mixer\n"
+		"|LMCMM  %u |RES    %u |RES    %u |LMCM4  %u "
+		"·LMCM3  %u ·LMCM2  %u ·LMCM1  %u ·LMCM0  %u | (%u)\n"
+		"|RMCMM  %u |RES    %u |RES    %u |RMCM4  %u "
+		"·RMCM3  %u ·RMCM2  %u ·RMCM1  %u ·RMCM0  %u | (%u)\n",
+		/*reg 8*/
+		"Reg  8: Mix/Misc Control - Mono In to Mixer and Misc Settings\n"
+		"|MNMM   %u |RES    %u |RES    %u |MNM4   %u "
+		"·MNM3   %u ·MNM2   %u ·MNM1   %u ·MNM0   %u | (%u)\n"
+		"|ALLMM  %u |MNOM   %u |HPOM   %u |HPOS   %u "
+		"|SUMM   %u |RES    %u |DAC2T  %u |DAC1T  %u | (%u)\n",
+		/*reg 9*/
+		"Reg  9: Output Control - DAC1 Analog Gain/Attenuation\n"
+		"|LDA1GM %u |RES    %u |LDA1G5 %u ·LDA1G4 %u "
+		"·LDA1G3 %u ·LDA1G2 %u ·LDA1G1 %u ·LDA1G0 %u | (%u)\n"
+		"|RDA1GM %u |RES    %u |RDA1G5 %u ·RDA1G4 %u "
+		"·RDA1G3 %u ·RDA1G2 %u ·RDA1G1 %u ·RDA1G0 %u | (%u)\n",
+		/*reg 10*/
+		"Reg 10: Output Control - DAC2 Analog Gain/Attenuation\n"
+		"|LDA2GM %u |RES    %u |LDA2G5 %u ·LDA2G4 %u "
+		"·LDA2G3 %u ·LDA2G2 %u ·LDA2G1 %u ·LDA2G0 %u | (%u)\n"
+		"|RDA2GM %u |RES    %u |RDA2G5 %u ·RDA2G4 %u "
+		"·RDA2G3 %u ·RDA2G2 %u ·RDA2G1 %u ·RDA2G0 %u | (%u)\n",
+		/*reg 11*/
+		"Reg 11: Output Control - DAC1 Digital Attenuation\n"
+		"|LDA1AM %u |RES    %u |LDA1A5 %u ·LDA1A4 %u "
+		"·LDA1A3 %u ·LDA1A2 %u ·LDA1A1 %u ·LDA1A0 %u | (%u)\n"
+		"|RDA1AM %u |RES    %u |RDA1A5 %u ·RDA1A4 %u "
+		"·RDA1A3 %u ·RDA1A2 %u ·RDA1A1 %u ·RDA1A0 %u | (%u)\n",
+		/*reg 12*/
+		"Reg 12: Output Control - DAC2 Digital Attenuation\n"
+		"|LDA2AM %u |RES    %u |LDA2A5 %u ·LDA2A4 %u "
+		"·LDA2A3 %u ·LDA2A2 %u ·LDA2A1 %u ·LDA2A0 %u | (%u)\n"
+		"|RDA2AM %u |RES    %u |RDA2A5 %u ·RDA2A4 %u "
+		"·RDA2A3 %u ·RDA2A2 %u ·RDA2A1 %u ·RDA2A0 %u | (%u)\n",
+		/*reg 13*/
+		"Reg 13: Digital Mix Control - ADC to DAC1\n"
+		"|LAD1MM %u |RES    %u |LAD1M5 %u ·LAD1M4 %u "
+		"·LAD1M3 %u ·LAD1M2 %u ·LAD1M1 %u ·LAD1M0 %u | (%u)\n"
+		"|RAD1MM %u |RES    %u |RAD1M5 %u ·RAD1M4 %u "
+		"·RAD1M3 %u ·RAD1M2 %u ·RAD1M1 %u ·RAD1M0 %u | (%u)\n",
+		/*reg 14*/
+		"Reg 14: Digital Mix Control - ADC to DAC2\n"
+		"|LAD2MM %u |RES    %u |LAD2M5 %u ·LAD2M4 %u "
+		"·LAD2M3 %u ·LAD2M2 %u ·LAD2M1 %u ·LAD2M0 %u | (%u)\n"
+		"|RAD2MM %u |RES    %u |RAD2M5 %u ·RAD2M4 %u "
+		"·RAD2M3 %u ·RAD2M2 %u ·RAD2M1 %u ·RAD2M0 %u | (%u)\n",
+		/*reg 15*/
+		"Reg 15: Codec Configuration - Channel Sample Rate Source Select\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|DA2C1  %u ·DA2C0  %u |DA1C1  %u ·DA1C0  %u | (%u)\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|ADRC1  %u ·ADRC0  %u |ADLC1  %u ·ADLC0  %u | (%u)\n",
+		/*reg 16*/
+		"Reg 16: Clock Generator 1 Control - Mode\n"
+		"|C1REF  %u |C1VID  %u |C1PLLG %u |C1P200 %u "
+		"|C1X8/7 %u |C1C128 %u |RES    %u |RES    %u | (%u)\n"
+		"|C1M7   %u ·C1M6   %u ·C1M5   %u ·C1M4   %u "
+		"·C1M3   %u ·C1M2   %u ·C1M1   %u ·C1M0   %u | (%u)\n",
+		/*reg 17*/
+		"Reg 17: Clock Generator 1 Control - Sample Rate\n"
+		"|C1C15  %u ·C1C14  %u ·C1C13  %u ·C2C12  %u "
+		"·C1C11  %u ·C1C10  %u ·C1C9   %u ·C1C8   %u · (%u)\n"
+		"·C1C7   %u ·C1C6   %u ·C1C5   %u ·C1C4   %u "
+		"·C1C3   %u ·C1C2   %u ·C1C1   %u ·C1C0   %u | (%u)\n",
+		/*reg 18*/
+		"Reg 18: Clock Generator 1 Control - Sample Phase Shift\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |RES    %u |C1PD   %u | (%u)\n"
+		"|C1P7   %u ·C1P6   %u ·C1P5   %u ·C1P4   %u "
+		"·C1P3   %u ·C1P2   %u ·C1P1   %u ·C1P0   %u | (%u)\n",
+		/*reg 19*/
+		"Reg 19: Clock Generator 2 Control - Mode\n"
+		"|C2REF  %u |C2VID  %u |C2PLLG %u |C2P200 %u "
+		"|C2X8/7 %u |C2C128 %u |RES    %u |RES    %u | (%u)\n"
+		"|C2M7   %u ·C2M6   %u ·C2M5   %u ·C2M4   %u "
+		"·C2M3   %u ·C2M2   %u ·C2M1   %u ·C2M0   %u | (%u)\n",
+		/*reg 20*/
+		"Reg 20: Clock Generator 2 Control - Sample Rate\n"
+		"|C2C15  %u ·C2C14  %u ·C2C13  %u ·C2C12  %u "
+		"·C2C11  %u ·C2C10  %u ·C2C9   %u ·C2C8   %u · (%u)\n"
+		"·C2C7   %u ·C2C6   %u ·C2C5   %u ·C2C4   %u "
+		"·C2C3   %u ·C2C2   %u ·C2C1   %u ·C2C0   %u | (%u)\n",
+		/*reg 21*/
+		"Reg 21: Clock Generator 2 Control - Sample Phase Shift\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |RES    %u |C2PD   %u | (%u)\n"
+		"|C2P7   %u ·C2P6   %u ·C2P5   %u ·C2P4   %u "
+		"·C2P3   %u ·C2P2   %u ·C2P1   %u ·C2P0   %u | (%u)\n",
+		/*reg 22*/
+		"Reg 22: Clock Generator 3 Control - Mode\n"
+		"|C3REF  %u |C3VID  %u |C3PLLG %u |C3P200 %u "
+		"|C3X8/7 %u |C3C128 %u |RES    %u |RES    %u | (%u)\n"
+		"|C3M7   %u ·C3M6   %u ·C3M5   %u ·C3M4   %u "
+		"·C3M3   %u ·C3M2   %u ·C3M1   %u ·C3M0   %u | (%u)\n",
+		/*reg 23*/
+		"Reg 23: Clock Generator 3 Control - Sample Rate\n"
+		"|C3C15  %u ·C3C14  %u ·C3C13  %u ·C3C12  %u "
+		"·C3C11  %u ·C3C10  %u ·C3C9   %u ·C3C8   %u · (%u)\n"
+		"·C3C7   %u ·C3C6   %u ·C3C5   %u ·C3C4   %u "
+		"·C3C3   %u ·C3C2   %u ·C3C1   %u ·C3C0   %u | (%u)\n",
+		/*reg 24*/
+		"Reg 24: Clock Generator 3 Control - Sample Phase Shift\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |RES    %u |C3PD   %u | (%u)\n"
+		"|C3P7   %u ·C3P6   %u ·C3P5   %u ·C3P4   %u "
+		"·C3P3   %u ·C3P2   %u ·C3P1   %u ·C3P0   %u | (%u)\n",
+		/*reg 25*/
+		"Reg 25: Codec Configuration - Digital Filter and Mode Select\n"
+		"|DRSFLT %u |DAMIX  %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |DA2FLT %u |DA1FLT %u | (%u)\n"
+		"|DAADR1 %u ·DAADR0 %u |DAADL1 %u ·DAADL0 %u "
+		"|RES    %u |RES    %u |ADRFLT %u |ADLFLT %u | (%u)\n",
+		/*reg 26*/
+		"Reg 26: Codec Configuration - Serial Interface\n"
+		"|DA2SM  %u |DA1SM  %u |RES    %u |RES    %u "
+		"|DA2F1  %u ·DA2F0  %u |DA1F1  %u ·DA1F0  %u | (%u)\n"
+		"|SCF    %u |FRS    %u |FRST   %u |ADTLK  %u "
+		"|ADRF1  %u ·ADRF0  %u |ADLF1  %u ·ADLF0  %u | (%u)\n",
+		/*reg 27*/
+		"Reg 27: Codec Configuration - Channel Power Down\n"
+		"|DFREE  %u |RES    %u |RES    %u |DDMEM  %u "
+		"|RES    %u |RES    %u |DA2EN  %u |DA1EN  %u | (%u)\n"
+		"|ANAEN  %u |HPEN   %u |RES    %u |AAMEN  %u "
+		"|RES    %u |RES    %u |ADREN  %u |ADLEN  %u | (%u)\n",
+		/*reg 28*/
+		"Reg 28: Codec Configuration - Fundemental Settings\n"
+		"|PDNI   %u |ACEN   %u |C3EN   %u |C2EN   %u "
+		"|C1EN   %u |ENCLKO %u |XCTL1  %u |XCTL0  %u | (%u)\n"
+		"|ENCV3  %u |ENBT3  %u |ENCV2  %u |ENBT2  %u "
+		"|ENCV1  %u |ENBT1  %u |LINRSD %u |LINLSD %u | (%u)\n",
+		/*reg 29*/
+		"Reg 29: Reserved for Future Expansion\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |RES    %u |RES    %u | (%u)\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |RES    %u |RES    %u | (%u)\n",
+		/*reg 30*/
+		"Reg 30: Reserved for Future Expansion\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |RES    %u |RES    %u | (%u)\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |RES    %u |RES    %u | (%u)\n",
+		/*reg 31*/
+		"Reg 31: Reserved for Future Expansion\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |RES    %u |RES    %u | (%u)\n"
+		"|RES    %u |RES    %u |RES    %u |RES    %u "
+		"|RES    %u |RES    %u |RES    %u |RES    %u | (%u)\n",
+		};
+	unsigned int v1 = 0, v2 = 0;
+	int w = ad1843->read(ad1843->chip, reg);
+	char *str = vmalloc_user(255);
+
+	if (NULL==str) return NULL;
+
+	switch (reg) {
+	case 0:
+		v2 = w&0xF;
+		break;
+	case 2:
+		v1 = (w>>8)&0xF; v2 = w&0xF;
+		break;
+	case 3: case 4: case 5: case 6: case 7:
+		v1 = (w>>8)&0x1F; v2 = w&0x1F;
+		break;
+	case 8:
+		v1 = (w>>8)&0x1F;
+		break;
+	case 9: case 10: case 11: case 12: case 13: case 14:
+		v1 = (w>>8)&0x3F; v2 = w&0x3F;
+		break;
+	case 16: case 18: case 19: case 21: case 22: case 24:
+		v2 = w&0xFF;
+		break;
+	case 17: case 20: case 23:
+		v2 = w&0xFFFF;
+		break;
+
+	}
+
+	snprintf(str, 255, reg_fmt[reg], (w>>15)&1,
+		(w>>14)&1, (w>>13)&1, (w>>12)&1, (w>>11)&1, (w>>10)&1,
+		(w>>9)&1,  (w>>8)&1, v1, (w>>7)&1,  (w>>6)&1,  (w>>5)&1,
+		(w>>4)&1,  (w>>3)&1,  (w>>2)&1,  (w>>1)&1,  (w>>0)&1, v2);
+
+	return str;
+}

--- linux-2.6.19.7-original/sound/mips/mace_audio.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.19.7/sound/mips/mace_audio.c	2007-07-02 11:40:32.000000000 +0100
@@ -0,0 +1,1341 @@
+/*
+ *   Sound driver for Silicon Graphics O2 Workstations MACE audio board.
+ *
+ *   Copyright 2007 Thorben Jändling <tj.trevelyan@xxxxxxxxx>
+ *   Based/Copied heavily on/from sgio2audio.c:
+ *   Copyright 2003 Vivien Chappelier <vivien.chappelier@xxxxxxxxxxxxxx>
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/*
+ * Notes
+ *
+ * Mace take 24bit audio stored/transfered in 32 bits and converts
+ * it in hardware back to 16/8bit
+ *
+ * AD1843 spec says:
+ * "Data in all four formats is always transfered MSB first"
+ * To me that says big-endian
+ * So tell ALSA we only do SNDRV_PCM_FMTBIT_S24_BE
+ *
+ * Apparently RAD1 similar see IP30 patch set
+ *
+ * Why rewrite? Mostly so that I can understand the code, writing is
+ * better then reading for that purpose I find.
+ */
+/* channel 0/ADC, 1/DAC1 -> pcm0
+ * channel 2/DAC2 -> pcm1
+ */
+
+/*************************** include ******************************/
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/gfp.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <asm/io.h>
+
+#include <asm/ip32/ip32_ints.h>
+#include <asm/ip32/mace.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+
+#include <sound/ad1843.h>
+
+/**************************** defines *******************************/
+
+/* 1: reset audio interface */
+#define SMA_CTRL_RESET          0x1UL
+/* 1: codec detected */
+#define SMA_CTRL_CODEC_PRESENT  0x2UL
+/*  2-8  : channel 1 write ptr alias */
+/*  9-15 : channel 2 read ptr alias */
+/* 16-22 : channel 3 read ptr alias */
+/* latched volume button */
+#define SMA_CTRL_VOL_BUTTON_UP   BIT(24)
+/* latched volume button */
+#define SMA_CTRL_VOL_BUTTON_DOWN BIT(23)
+
+#define SMA_CODEC_READ(reg)  ((reg) <<17) | 0x00010000UL
+#define SMA_CODEC_WRITE(reg, val) (((reg) <<17)|(val)) & 0x00FEFFFFUL
+
+#define SMA_RING_OFFSET(chi) ((chi) << 12)
+#define SMA_RING_SIZE 0x1000
+#define SMA_RING_MASK 0x0FFFUL
+
+/* int on buffer >50% full */
+#define SMA_INT_THRESHOLD_50 (2 << 5)
+/* 1: enable DMA transfer */
+#define SMA_DMA_ENABLE   BIT(9)
+
+/*old, define still inherted from sgio2audio*/
+
+#define SMA_CTRL_CHAN_RESET BIT(10) /* 1: reset channel */
+
+#define SMA_INT_THRESHOLD_DISABLED  (0 << 5) /* interrupt disabled */
+#define SMA_INT_THRESHOLD_25 (1 << 5) /* int on buffer >25% full */
+#define SMA_INT_THRESHOLD_75 (3 << 5) /* int on buffer >75% full */
+#define SMA_INT_THRESHOLD_EMPTY     (4 << 5) /* int on buffer empty */
+#define SMA_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */
+#define SMA_INT_THRESHOLD_FULL      (6 << 5) /* int on buffer empty */
+#define SMA_INT_THRESHOLD_NOT_FULL  (7 << 5) /* int on buffer !empty */
+
+/****************************** structs/types ***********************/
+
+/* chip specific record */
+typedef struct snd_mace_audio {
+	snd_card_t *card;
+	snd_pcm_t  *pcm[2];
+
+	struct {
+		snd_pcm_substream_t *substream;
+		snd_pcm_uframes_t        frames;
+
+		int                  ack;
+		void                *buffer;
+		u64                  pos;
+		spinlock_t           lock;
+	} channel[3];
+
+	/* which gain should the O2 vol butons control?*/
+	unsigned int ext_vol_for;
+
+	snd_info_entry_t  *proc_debug,
+	                  *proc_ad1843,
+	                  *proc_mace;
+
+	ad1843_t   *ad1843;
+	spinlock_t ad1843_lock;
+
+	void        *ring_base;
+	dma_addr_t   ring_base_handle;
+	unsigned long maceisa_base;
+	unsigned int mace_offset;
+
+} snd_mace_audio_t;
+
+typedef struct sma_proc_debug {
+	struct list_head link;
+
+/* name to display for the entry */
+	char  *name;
+
+/* a string value */
+	char  *val_str;
+
+/* an int val used if val_str is null */
+	long val_int;
+} sma_proc_debug_t;
+
+/************************** func declaration ****************************/
+/* mace access */
+static int mace_audio_reg_read(snd_mace_audio_t *chip, int reg);
+static int mace_audio_reg_write(snd_mace_audio_t *chip, int reg, int word);
+/* pcm */
+static int sma_pcm_open(snd_pcm_substream_t *substream);
+static int sma_pcm_close(snd_pcm_substream_t *substream);
+static int sma_pcm_hwparam
+	(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params);
+static int sma_pcm_hwfree(snd_pcm_substream_t *substream);
+static int sma_pcm_prepare(snd_pcm_substream_t *substream);
+static int sma_pcm_trigger(snd_pcm_substream_t *substream, int cmd);
+static snd_pcm_uframes_t sma_pcm_pointer
+	(snd_pcm_substream_t *substream);
+static struct page *sma_pcm_page
+	(snd_pcm_substream_t *substream, unsigned long offset);
+static int sma_pcm_ack(snd_pcm_substream_t *substream);
+static int __devinit sma_pcm_new(snd_mace_audio_t *chip);
+/* interrupt and DMA engine */
+static int sma_dma_start(snd_pcm_substream_t *substream);
+static int sma_dma_stop(snd_pcm_substream_t *substream);
+static void sma_dma_ping(snd_mace_audio_t *chip, int chi);
+static void sma_dma_refill(snd_mace_audio_t *chip, int chi);
+static irqreturn_t sma_interrupt
+	(int irq, snd_mace_audio_t *chip, struct pt_regs *regs);
+static void sma_adjust_vol(snd_mace_audio_t *chip);
+/* mixer and controls */
+static int sma_gain_info
+	(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo);
+static int sma_gain_get
+	(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+static int sma_gain_put
+	(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+static int __devinit sma_control_new(snd_mace_audio_t *chip);
+/* proc */
+static void sma_proc_ad1843_read
+	(snd_info_entry_t *proc_entry, snd_info_buffer_t *buffer);
+static void sma_proc_mace_read
+	(snd_info_entry_t *proc_entry, snd_info_buffer_t *buffer);
+static void sma_proc_debug_read
+	(snd_info_entry_t *, snd_info_buffer_t *);
+static sma_proc_debug_t* sma_proc_debug_append(char *name);
+static int sma_proc_create(snd_mace_audio_t *chip);
+/* ALSA Setup */
+static int sma_free(snd_mace_audio_t *chip);
+static int __devinit sma_create
+	(snd_card_t *card, snd_mace_audio_t **rchip);
+static int __devinit sma_probe(void);
+/* module setup */
+static int __init snd_card_mace_audio_init(void);
+static void __exit snd_card_mace_audio_exit(void);
+
+
+/************************* define psudo-funcs ***************************/
+#define sma_proc_entry_assert(entry,name) \
+	if (NULL == entry) entry = sma_proc_entry_append(name);
+
+/* pcm0 substream0 = channel 1
+ * pcm0 substream1 = channel 0
+ * pcm1 substream0 = channel 2
+ */
+#define substream_to_channel_index(substream) \
+ ((((snd_mace_audio_t*)(substream->private_data))->pcm[0]==substream->pcm)\
+ ?!(substream->number):2)
+
+/******************************** globals *******************************/
+
+/* using same for all */
+static struct snd_pcm_hardware sma_pcm_hw[2] = {
+	{ /* Direct */
+	.info = (SNDRV_PCM_INFO_MMAP |
+	         SNDRV_PCM_INFO_INTERLEAVED |
+	         SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	         SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =           SNDRV_PCM_FMTBIT_S24_BE,
+	.rates =             SNDRV_PCM_RATE_8000_48000,
+	.rate_min =          8000,
+	.rate_max =          48000,
+	.channels_min =      2,
+	.channels_max =      2,
+        .buffer_bytes_max =  4096,
+	.period_bytes_min =  32,
+	.period_bytes_max =  1024,
+	.periods_min =       4,
+	.periods_max =       128,
+
+	},{ /* inderct bounce buffer */
+
+	.info = (SNDRV_PCM_INFO_MMAP |
+	         SNDRV_PCM_INFO_INTERLEAVED |
+	         SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	         SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =           SNDRV_PCM_FMTBIT_S24_BE,
+	.rates =             SNDRV_PCM_RATE_8000_48000,
+	.rate_min =          8000,
+	.rate_max =          48000,
+	.channels_min =      2,
+	.channels_max =      2,
+        .buffer_bytes_max =  32768,
+	.period_bytes_min =  32,
+	.period_bytes_max =  32768,
+	.periods_min =       1,
+	.periods_max =       1024,
+	}
+};
+
+/* pcm operators */
+static struct snd_pcm_ops sma_pcm_ops[2] = {
+	{ /* Direct */
+	.open =       sma_pcm_open,
+	.close =      sma_pcm_close,
+	.ioctl =      snd_pcm_lib_ioctl,
+	.hw_params =  sma_pcm_hwparam,
+	.hw_free =    sma_pcm_hwfree,
+	.prepare =    sma_pcm_prepare,
+	.trigger =    sma_pcm_trigger,
+	.pointer =    sma_pcm_pointer,
+	.ack     =    sma_pcm_ack,
+	.page    =    sma_pcm_page
+
+	},{ /* inderct bounce buffer */
+	.open =       sma_pcm_open,
+	.close =      sma_pcm_close,
+	.ioctl =      snd_pcm_lib_ioctl,
+	.hw_params =  sma_pcm_hwparam,
+	.hw_free =    sma_pcm_hwfree,
+	.prepare =    sma_pcm_prepare,
+	.trigger =    sma_pcm_trigger,
+	.pointer =    sma_pcm_pointer,
+	.page    =    sma_pcm_page
+
+	}
+};
+
+/* record level mixer control */
+static snd_kcontrol_new_t sma_ctrl_reclevel __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Capture Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_RECLEV,
+	.info           = sma_gain_info,
+	.get            = sma_gain_get,
+	.put            = sma_gain_put,
+};
+
+/* line mixer control */
+static snd_kcontrol_new_t sma_ctrl_line __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Line Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_LINE,
+	.info           = sma_gain_info,
+	.get            = sma_gain_get,
+	.put            = sma_gain_put,
+};
+
+/* cd mixer control */
+static snd_kcontrol_new_t sma_ctrl_cd __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "CD Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_CD,
+	.info           = sma_gain_info,
+	.get            = sma_gain_get,
+	.put            = sma_gain_put,
+};
+
+/* mic mixer control */
+static snd_kcontrol_new_t sma_ctrl_mic __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Mic Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_MIC,
+	.info           = sma_gain_info,
+	.get            = sma_gain_get,
+	.put            = sma_gain_put,
+};
+
+/* dac1/pcm0 mixer control */
+static snd_kcontrol_new_t sma_ctrl_pcm0 __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Playback Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_PCM_0,
+	.info           = sma_gain_info,
+	.get            = sma_gain_get,
+	.put            = sma_gain_put,
+};
+
+/* dac2/pcm1 mixer control */
+static snd_kcontrol_new_t sma_ctrl_pcm1 __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Playback Aux-out Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_PCM_1,
+	.info           = sma_gain_info,
+	.get            = sma_gain_get,
+	.put            = sma_gain_put,
+};
+
+
+LIST_HEAD(sma_proc_debug_list);
+
+static int   alsa_index = -1;
+static char *alsa_id    = NULL;
+static int   alsa_enable= 1;
+static int   sma_indirect = 1;
+
+/* Global needed for module exit */
+snd_mace_audio_t *snd_mace_audio_chip = NULL;
+
+/******************************* PCM ****************************************/
+
+/* temp debug func */
+
+static void print_pointers(const char *str)
+{
+	unsigned long rb, wp, rp, depth;
+	int chi;
+
+	rb = readq(&mace->perif.ctrl.ringbase);
+	printk ("%s. ringbase=0x%lX,\n", str, rb);
+
+	for (chi=0; chi<3; chi++) {
+
+		wp = readq(&mace->perif.audio.chan[chi].write_ptr);
+		rp = readq(&mace->perif.audio.chan[chi].read_ptr);
+		depth = readq(&mace->perif.audio.chan[chi].depth);
+
+		printk("\t%d[ writep=0x%lX (%lu), readp=0x%lX (%lu), "
+			"depth=0x%lX (%lu) ]\n",
+			chi, wp, wp, rp, rp, depth, depth);
+	}
+
+}
+
+
+/* play open callback */
+static int sma_pcm_open(snd_pcm_substream_t *substream)
+{
+	//snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int chi = substream_to_channel_index(substream);
+printk("in pcm open for channel %d\n", chi);
+
+	runtime->hw = sma_pcm_hw[sma_indirect];
+
+	return 0;
+}
+
+/* play close callback */
+static int sma_pcm_close(snd_pcm_substream_t *substream)
+{
+	//snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	//FIXME code
+printk("in pcm close\n");
+
+	return 0;
+}
+
+/* setup hw for pcm */
+/* maybe called more then once */
+static int sma_pcm_hwparam
+	(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params)
+{
+	snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	int chi = substream_to_channel_index(substream);
+printk("In hw param for channel %d\n", chi);
+
+	if (sma_indirect) {
+
+		if (NULL != substream->runtime->dma_area)
+			vfree(substream->runtime->dma_area);
+
+		substream->runtime->dma_bytes =
+			params_buffer_bytes(hw_params);
+
+		substream->runtime->dma_addr = 0;
+
+		substream->runtime->dma_area =
+			vmalloc_user(substream->runtime->dma_bytes);
+		
+	} else {
+		substream->runtime->dma_bytes = SMA_RING_SIZE;
+
+		substream->runtime->dma_addr =
+			(chip->ring_base_handle + SMA_RING_OFFSET(chi)
+			/*+ chip->mace_offset*/);
+			/*chip->maceisa_base + SMA_RING_OFFSET(chi);*/
+		substream->runtime->dma_area =
+			(chip->ring_base + SMA_RING_OFFSET(chi)
+			/*+ chip->mace_offset~*/);
+	}
+
+printk("set alsa dma area=0x%lX (%lu) size=0x%lX (%lu)\n",
+	(unsigned long) substream->runtime->dma_area,
+	(unsigned long) substream->runtime->dma_area,
+	(unsigned long) substream->runtime->dma_bytes,
+	(unsigned long) substream->runtime->dma_bytes);
+
+	if (NULL == substream->runtime->dma_area) {
+		snd_printk(KERN_ERR "DMA area not allocated!\n");
+		return -ENOMEM;
+	}
+
+	return 1;
+}
+
+/* free hw for pcm */
+/* maybe called more then once */
+static int sma_pcm_hwfree(snd_pcm_substream_t *substream)
+{
+	snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	int chi = substream_to_channel_index(substream);
+printk("In hw free for channel %d\n", chi);
+
+	if (sma_indirect && NULL != substream->runtime->dma_area) {
+		vfree(substream->runtime->dma_area);
+	}
+
+	substream->runtime->dma_bytes = 0;
+	substream->runtime->dma_addr = 0;
+	substream->runtime->dma_area = NULL;
+	chip->channel[chi].buffer = NULL;
+	chip->channel[chi].pos = 0;
+	chip->channel[chi].ack = 0;
+	chip->channel[chi].frames = 0;
+
+	return 1;
+}
+
+/* prepare callback */
+static int sma_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int chi = substream_to_channel_index(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->channel[chi].lock, flags);
+
+	chip->channel[chi].pos = 0;
+	chip->channel[chi].frames = 0;
+	chip->channel[chi].ack = 0;
+	chip->channel[chi].substream = substream;
+
+	if (0 == chi) { /* capture*/
+printk("in prepare capture for channel %d\n", chi);
+		
+		ad1843_setup_adc(chip->ad1843,
+			runtime->rate,
+			SNDRV_PCM_FORMAT_S24_BE,
+			runtime->channels);
+	} else { /* playback */
+printk("in prepare playback for channel %d, rate %d, sub-channels %d\n",
+	chi, runtime->rate, runtime->channels);
+
+		ad1843_setup_dac(chip->ad1843,
+			chi,
+			runtime->rate,
+			SNDRV_PCM_FORMAT_S24_BE,
+			runtime->channels);
+	}
+
+	spin_unlock_irqrestore(&chip->channel[chi].lock, flags);
+
+	return 0;
+}
+
+/* trigger callback */
+static int sma_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+printk("In pcm trigger\n");
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START: /* start PCM engine */
+		sma_dma_start(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP: /* stop PCM engine */
+		sma_dma_stop(substream);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t sma_pcm_pointer
+	(snd_pcm_substream_t *substream)
+{
+	snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	int chi = substream_to_channel_index(substream);
+	unsigned long p;
+
+	if (sma_indirect) {
+
+		p = chip->channel[chi].pos;
+
+	} else {
+
+		p = readq(&mace->perif.audio.chan[chi].read_ptr);
+
+	}
+
+//printk("returning pointer 0x%lX (%lu) for channel %d\n", p, p, chi);
+
+
+	return  bytes_to_frames(substream->runtime, p);
+}
+
+/* get page callback */
+static struct page *sma_pcm_page
+	(snd_pcm_substream_t *substream, unsigned long offset)
+{
+	struct page *p;
+printk("in page\n");
+
+	if (sma_indirect) {
+		p = vmalloc_to_page((substream->runtime->dma_area + offset));
+	} else {
+		/*FIXME using CAC_AADR until a proper alsa dma on mips fix */
+		p = virt_to_page(
+			CAC_ADDR((substream->runtime->dma_area + offset)));
+	}
+
+	return p;
+}
+
+/* ack callback, when some more data is written */
+static int sma_pcm_ack(snd_pcm_substream_t *substream)
+{
+	//snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	int chi = substream_to_channel_index(substream);
+	unsigned long ap;
+
+	ap = frames_to_bytes(substream->runtime,
+		substream->runtime->control->appl_ptr);
+
+	//ap =- SMA_RING_SIZE;
+	if (32 > ap) ap += SMA_RING_SIZE;
+	ap -= 32;
+	ap &= SMA_RING_MASK;
+
+printk("in ack, ap=0x%lX (%lu), for channel %d\n", ap, ap, chi);
+	writeq(ap, &mace->perif.audio.chan[chi].write_ptr);
+
+	/* set DMA to wake on 50% empty and enable interrupt */
+/*	writeq(SMA_DMA_ENABLE | SMA_INT_THRESHOLD_50,
+		&mace->perif.audio.chan[chi].control);*/
+	mb();
+
+	return 0;
+}
+
+
+/* create pcm device */
+static int __devinit sma_pcm_new(snd_mace_audio_t *chip)
+{
+	snd_pcm_t *pcm;
+	int err, chi;
+
+	/* reset channels and leave them off */
+	for (chi=0; chi<3; chi++) {
+		writeq(0,
+			&mace->perif.audio.chan[chi].control);
+
+		writeq(0, &mace->perif.audio.chan[chi].write_ptr);
+		writeq(0, &mace->perif.audio.chan[chi].read_ptr);
+
+		writeq(SMA_CTRL_CHAN_RESET,
+			&mace->perif.audio.chan[chi].control);
+			
+		spin_lock_init(&chip->channel[chi].lock);
+	}
+
+	pcm = NULL;
+	err = snd_pcm_new(chip->card, "Mace Audio 1", 0, 1, 1, &pcm);
+	if (0 > err) return err;
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "Mace Audio Primary");
+
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+		&sma_pcm_ops[sma_indirect]);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+		&sma_pcm_ops[sma_indirect]);
+
+	chip->pcm[0] = pcm;
+
+
+	pcm = NULL;
+	err = snd_pcm_new(chip->card, "Mace Audio 1", 1, 1, 0, &pcm);
+	if (0 > err) return err; /*free pcm0 ? */
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "Mace Audio Secondary");
+
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+		&sma_pcm_ops[sma_indirect]);
+
+	chip->pcm[1] = pcm;
+
+	return 0;
+}
+
+/*************************** Interrupts and DMA engine ****************/
+static int sma_dma_start(snd_pcm_substream_t *substream)
+{
+	snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	int chi = substream_to_channel_index(substream);
+
+printk("In DMA enable for channel %d\n", chi);
+
+	if (sma_indirect) {
+		sma_dma_refill(chip, chi);
+	}	
+
+	/* set DMA to wake on 50% empty and enable interrupt */
+	writeq(SMA_DMA_ENABLE | SMA_INT_THRESHOLD_50,
+		&mace->perif.audio.chan[chi].control);
+	mb();
+
+	return 0;
+}
+
+static int sma_dma_stop(snd_pcm_substream_t *substream)
+{
+	int chi = substream_to_channel_index(substream);
+printk("In DMA stop for channel %d\n", chi);
+
+	writeq(SMA_CTRL_CHAN_RESET,
+		&mace->perif.audio.chan[chi].control);
+	mb();
+
+	return 0;
+}
+
+static void sma_dma_ping(snd_mace_audio_t *chip, int chi)
+{
+
+	if (sma_indirect){
+		sma_dma_refill(chip, chi);
+		snd_pcm_period_elapsed(chip->channel[chi].substream);
+	} else {
+		snd_pcm_period_elapsed(chip->channel[chi].substream);
+		/* Stop DMA if running low */
+		/*depth = readq(&mace->perif.audio.chan[chi].depth);
+		if (1024 > depth) sma_dma_stop(chip->channel[chi].substream);*/
+	}
+
+}
+
+static void sma_dma_refill(snd_mace_audio_t *chip, int chi)
+{
+	u64 *dst, dst_base, dst_pos, *src, src_base, src_pos, src_mask;
+	unsigned long flags, filled, available;
+	int i;
+	struct snd_pcm_runtime *runtime =
+		chip->channel[chi].substream->runtime;
+
+	spin_lock_irqsave(&chip->channel[chi].lock, flags);
+
+	dst_base = (u64)chip->ring_base + SMA_RING_OFFSET(chi) + chip->mace_offset;
+	src_base = (u64)runtime->dma_area;
+
+	dst_pos = readq(&mace->perif.audio.chan[chi].write_ptr);
+	src_pos = chip->channel[chi].pos;
+
+	//src_mask = runtime->dma_bytes -1;
+	src_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1;
+
+	dst = (u64*)(dst_base + dst_pos);
+	src = (u64*)(src_base + src_pos);
+;
+	filled = readq(&mace->perif.audio.chan[chi].depth);
+	available = SMA_RING_SIZE - filled;
+
+	/* don't catch up with the end, note 32 byte chunks*/
+	if (SMA_RING_SIZE <= available) available -= 32;
+
+	/* copy in 32 byte blocks */
+	while (31 < available) for (i=0; i<32; i+=sizeof(u64)) {
+
+		*dst = *src;
+
+		dst_pos += sizeof(u64);
+		dst_pos &= SMA_RING_MASK;
+		dst = (u64*)(dst_base + dst_pos);
+
+		src_pos += sizeof(u64);
+		src_pos &= src_mask;
+		src = (u64*)(src_base + src_pos);
+
+		available -= sizeof(u64);
+	}
+
+	writeq(dst_pos, &mace->perif.audio.chan[chi].write_ptr);
+	chip->channel[chi].pos = src_pos;
+	mb();
+
+	src_pos = readq(&mace->perif.audio.chan[chi].write_ptr);
+	if (src_pos != dst_pos)
+		printk("ohoh ring boundary not matched! 0x%lX != 0x%lX\n",
+			src_pos, dst_pos);
+
+	spin_unlock_irqrestore(&chip->channel[chi].lock, flags);
+}
+
+static irqreturn_t sma_interrupt
+	(int irq, snd_mace_audio_t *chip, struct pt_regs *regs)
+{
+	switch (irq) {
+	case MACEISA_AUDIO_SW_IRQ:
+		printk("Got MACEISA AUDIO SW IRQ\n");
+		return IRQ_NONE; /*TODO*/
+
+	case MACEISA_AUDIO_SC_IRQ:
+		sma_adjust_vol(chip);
+		break;
+
+	case MACEISA_AUDIO1_DMAT_IRQ:
+		sma_dma_ping(chip, 0);
+		break;
+
+	case MACEISA_AUDIO1_OF_IRQ:
+		printk("Got MACEISA AUDIO1 OF IRQ\n");
+		return IRQ_NONE; /*TODO*/
+
+	case MACEISA_AUDIO2_DMAT_IRQ:
+		sma_dma_ping(chip, 1);
+		break;
+
+	case MACEISA_AUDIO2_MERR_IRQ:
+		printk("Got MACEISA AUDIO2 MERR IRQ\n");
+		return IRQ_NONE; /*TODO*/
+
+	case MACEISA_AUDIO3_DMAT_IRQ:
+		sma_dma_ping(chip, 2);
+		break;
+
+	case MACEISA_AUDIO3_MERR_IRQ:
+		printk("Got MACEISA AUDIO3 MERR IRQ\n");
+		return IRQ_NONE; /*TODO*/
+
+	default:
+		snd_printk(KERN_ERR "Unknown IRQ received!\n");
+		return IRQ_NONE;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void sma_adjust_vol(snd_mace_audio_t *chip)
+{
+	unsigned long status, flags;
+	unsigned int rvol,lvol;
+
+	spin_lock_irqsave(&chip->ad1843_lock, flags);
+
+	status = readq(&mace->perif.audio.control);
+	rvol = ad1843_get_gain(chip->ad1843, chip->ext_vol_for);
+	lvol = (rvol>>8)&0xFF;
+	rvol &= 0xFF;
+
+printk("in volume adjust for %d, current vol %d|%d, status %ld\n",
+	chip->ext_vol_for, lvol, rvol, status);
+
+	if ((status & SMA_CTRL_VOL_BUTTON_UP) >0) {
+		status &= ~SMA_CTRL_VOL_BUTTON_UP;
+		writeq(status, &mace->perif.audio.control);
+		mb();
+
+		if (100 > lvol) lvol++;
+		if (100 > rvol) rvol++;
+	}
+
+	if ((status & SMA_CTRL_VOL_BUTTON_DOWN) >0) {
+		status &= ~SMA_CTRL_VOL_BUTTON_DOWN;
+		writeq(status, &mace->perif.audio.control);
+		mb();
+
+		if (0 < lvol) lvol--;
+		if (0 < rvol) rvol--;
+	}
+
+	ad1843_set_gain(chip->ad1843, chip->ext_vol_for, ((lvol<<8)|rvol));
+
+	mb();
+
+	status = readq(&mace->perif.audio.control);
+printk("leaving volume adjust, vol now %d|%d, status %ld\n",
+	lvol, rvol, status);
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+
+}
+
+/******************** Mixer and controls ********************/
+
+static int sma_gain_info
+	(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 100;
+
+	return 0;
+}
+
+static int sma_gain_get
+	(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	snd_mace_audio_t *chip = snd_kcontrol_chip(kcontrol);
+	int vol;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->ad1843_lock, flags);
+
+	vol = ad1843_get_gain(chip->ad1843, (int)kcontrol->private_value);
+
+	ucontrol->value.integer.value[0] = vol & 0xFF;
+	ucontrol->value.integer.value[1] = (vol >> 8) & 0xFF;
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+
+	return 0;
+}
+
+static int sma_gain_put
+	(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	snd_mace_audio_t *chip = snd_kcontrol_chip(kcontrol);
+	int newvol, oldvol;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->ad1843_lock, flags);
+
+	oldvol = ad1843_get_gain(chip->ad1843, kcontrol->private_value);
+	newvol = (ucontrol->value.integer.value[1] << 8) |
+		ucontrol->value.integer.value[0];
+
+	newvol = ad1843_set_gain(chip->ad1843, kcontrol->private_value,
+		newvol);
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+
+	return (newvol != oldvol);
+}
+
+static int __devinit sma_control_new(snd_mace_audio_t *chip)
+{
+	int err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&sma_ctrl_reclevel, chip));
+	if (0 > err) return err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&sma_ctrl_line, chip));
+	if (0 > err) return err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&sma_ctrl_cd, chip));
+	if (0 > err) return err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&sma_ctrl_mic, chip));
+	if (0 > err) return err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&sma_ctrl_pcm0, chip));
+	if (0 > err) return err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&sma_ctrl_pcm1, chip));
+	if (0 > err) return err;
+
+	/*plan a mixer control that can change this*/
+	chip->ext_vol_for = AD1843_GAIN_PCM_0;
+
+	return 0;
+}
+
+/****************************** Proc files **********************************/
+static void sma_proc_ad1843_read
+	(snd_info_entry_t *proc_entry, snd_info_buffer_t *buffer)
+{
+	int i;
+	char *str;
+	snd_mace_audio_t *chip = proc_entry->private_data;
+
+	snd_iprintf(buffer,
+		"|  15/7   |  14/6   |  13/5   |  12/4   "
+		"|  11/3   |  10/2   |   9/1   |   8/0   |\n");
+
+	for (i=0; i<32; i++){
+
+		str = ad1843_dump_reg(chip->ad1843, i);
+		if (NULL == str) continue;
+
+		snd_iprintf(buffer, str);
+
+		vfree(str);
+	}
+
+}
+
+static void sma_proc_mace_read
+	(snd_info_entry_t *proc_entry, snd_info_buffer_t *buffer)
+{
+	snd_mace_audio_t *chip = proc_entry->private_data;
+	unsigned long r,w,c,d;
+	int chi;
+
+	mb();
+	r = readq(&mace->perif.ctrl.ringbase);
+	snd_iprintf(buffer, "MACE Periferals Ringbase: 0x%lX (%lu)\n"
+		"\t(CPU Address: 0x%lX (%lu), Set Bus Address: 0x%lX (%lu))\n",
+		(unsigned long)r, (unsigned long)r,
+		(unsigned long)chip->ring_base, (unsigned long)chip->ring_base,
+		chip->ring_base_handle, chip->ring_base_handle);
+
+	c = readq(&mace->perif.audio.control);
+	snd_iprintf(buffer, "MACE Audio Control: 0x%lX (%lu)\n", c, c);
+
+	c = readq(&mace->perif.audio.codec_control);
+	snd_iprintf(buffer, "MACE Audio Codec Status Control: 0x%lX (%lu)\n", c, c);
+
+	d = readq(&mace->perif.audio.codec_mask);
+	snd_iprintf(buffer, "MACE Audio Codec Status IRQ Mask: 0x%lX (%lu)\n", d, d);
+
+	r = readq(&mace->perif.audio.codec_read);
+	snd_iprintf(buffer, "MACE Audio Codec Status Value: 0x%lX (%lu)\n", r, r);
+
+	for (chi=0; chi<3; chi++) {
+
+		mb();
+		c = readq(&mace->perif.audio.chan[chi].control);
+		r = readq(&mace->perif.audio.chan[chi].read_ptr);
+		w = readq(&mace->perif.audio.chan[chi].write_ptr);
+		d = readq(&mace->perif.audio.chan[chi].depth);
+
+		snd_iprintf(buffer, "MACE Audio Channel %u:-\n"
+			"\tControl: 0x%lX (%lu)\n"
+			"\tRead Pointer: 0x%lX (%lu)\n"
+			"\tWrite Pointer: 0x%lX (%lu)\n"
+			"\tDepth: 0x%lX (%lu)\n",
+			chi, c, c, r, r, w, w, d, d);
+
+		if (chip->channel[chi].substream &&
+		    chip->channel[chi].substream->runtime ) {
+			unsigned long ap = frames_to_bytes(
+				chip->channel[chi].substream->runtime,
+				chip->channel[chi].substream->runtime->control
+					->appl_ptr);
+
+
+			snd_iprintf(buffer, "\tALSA Runtime DMA area: 0x%lX (%lu)\n",
+				chip->channel[chi].substream->runtime->dma_area,
+				chip->channel[chi].substream->runtime->dma_area);
+
+			snd_iprintf(buffer, "\tALSA App Pointer: 0x%lX (%lu) [0x%lX (%lu)]\n",
+				ap,ap,
+				chip->channel[chi].substream->runtime->control
+					->appl_ptr,
+				chip->channel[chi].substream->runtime->control
+					->appl_ptr);
+
+		}
+	}
+}
+
+static void sma_proc_debug_read
+	(snd_info_entry_t *proc_entry, snd_info_buffer_t *buffer)
+{
+	sma_proc_debug_t *entry;
+
+	snd_iprintf(buffer, "MACE Audio debug info.\n");
+
+	list_for_each_entry(entry, &sma_proc_debug_list, link) {
+
+		if (NULL == entry->val_str)
+			snd_iprintf(buffer, "%s: \t%ld\n",
+				entry->name, entry->val_int);
+		else
+			snd_iprintf(buffer, "%s: \t%s\n",
+				entry->name, entry->val_str);
+	}
+
+}
+
+/* this is called inside the volume interrupt, but is it appropriate
+ * to be allocating memory during an interrupt? */
+static sma_proc_debug_t* sma_proc_debug_append
+	(char *name)
+{
+	sma_proc_debug_t *entry =
+		kzalloc(sizeof(sma_proc_debug_t), GFP_ATOMIC);
+
+	entry->name = name;
+
+	list_add_tail(&entry->link, &sma_proc_debug_list);
+
+	return entry;
+
+}
+
+static int sma_proc_create(snd_mace_audio_t *chip)
+{
+	int err;
+
+	err = snd_card_proc_new(chip->card, "debug", &chip->proc_debug);
+	if (0 > err) return err;
+
+	snd_info_set_text_ops(chip->proc_debug, chip,
+		sma_proc_debug_read);
+
+	err = snd_card_proc_new(chip->card, "ad1843", &chip->proc_ad1843);
+	if (0 > err) return err;
+
+	snd_info_set_text_ops(chip->proc_ad1843, chip,
+		sma_proc_ad1843_read);
+
+	err = snd_card_proc_new(chip->card, "mace", &chip->proc_mace);
+	if (0 > err) return err;
+
+	snd_info_set_text_ops(chip->proc_mace, chip,
+		sma_proc_mace_read);
+
+	return 0;
+}
+
+/******************************* ALSA setup **************************/
+
+/* chip specific destructor */
+static int sma_free(snd_mace_audio_t *chip)
+{
+	int irq;
+
+printk("sma_free called\n");
+
+	/* reset the interface */
+	writeq(SMA_CTRL_RESET, &mace->perif.audio.control);
+	udelay(1);
+	writeq(0L, &mace->perif.audio.control);
+
+	/* undo DMA ? */
+	dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE + 62,
+		chip->ring_base, chip->ring_base_handle);
+			   
+	/* release IRQ's */
+	for (irq=MACEISA_AUDIO_SW_IRQ; irq<=MACEISA_AUDIO3_MERR_IRQ; irq++){
+		free_irq(irq, chip);
+	}
+
+	/* shutdown the ad1843? */
+
+
+	/* free codec struct*/
+	kfree(chip->ad1843);
+	snd_mace_audio_chip = NULL;
+
+	return 0;
+}
+
+/* chip specific constructor */
+static int __devinit sma_create
+	(snd_card_t *card, snd_mace_audio_t **rchip)
+{
+	snd_mace_audio_t *chip;
+	int err, irq;
+	unsigned long mace_reg, mringbase;
+
+	*rchip = NULL;
+	chip = card->private_data;
+	chip->card = card;
+
+	/* DMA ring buffer */
+	chip->ring_base = dma_alloc_coherent (NULL,
+		MACEISA_RINGBUFFERS_SIZE + 62, &chip->ring_base_handle, GFP_USER);
+
+	err = (NULL == chip->ring_base)?-ENOMEM:0; /* ugly */
+	if (0> err) goto ERROR_EXIT;
+
+	/* set ring base */
+	writeq(chip->ring_base_handle, &mace->perif.ctrl.ringbase);
+	mb();
+
+	chip->maceisa_base = readq(&mace->perif.ctrl.ringbase);
+	chip->mace_offset = chip->maceisa_base - chip->ring_base_handle;
+	/*chip->ring_base += chip->mace_offset;*/
+
+printk("allocated ring base. CPU addr=0x%lX, Bus addr=0x%lX\n"
+	"\tmacebase=0x%lX, offset=0x%lX\n",
+	chip->ring_base, chip->ring_base_handle,
+	chip->maceisa_base, chip->mace_offset);
+
+	/* Allocate IRQs */
+	for (irq=MACEISA_AUDIO_SW_IRQ; irq<=MACEISA_AUDIO3_MERR_IRQ; irq++){
+		/*NOTE What does reqeust_irq return? */
+		err = request_irq(irq,
+			(irqreturn_t(*)(int, void*, struct pt_regs*))
+				&sma_interrupt,
+			SA_INTERRUPT|SA_SHIRQ, "Mace Audio", chip);
+		if (0 > err) goto ERROR_EXIT;
+	}
+
+	/* check if the codec is present */
+	mace_reg = readq(&mace->perif.audio.control);
+	err = (mace_reg & SMA_CTRL_CODEC_PRESENT)>0? 0 : -ENOENT;
+	if (0 > err) goto ERROR_EXIT;
+
+	/* reset the interface and ad1843 */
+	writeq(SMA_CTRL_RESET, &mace->perif.audio.control);
+	udelay(100);
+	writeq(0L, &mace->perif.audio.control);
+
+	/* spin locks */
+	spin_lock_init(&chip->ad1843_lock);
+
+	/* ad1843 setup */
+	chip->ad1843 = kmalloc(sizeof(ad1843_t), GFP_KERNEL);
+	err = (NULL == chip->ad1843)?-ENOMEM:0;
+	if (0 > err) goto ERROR_EXIT;
+
+	chip->ad1843->chip =  chip;
+	chip->ad1843->read =
+		(int(*)(void*, int))&mace_audio_reg_read;
+	chip->ad1843->write =
+		(int(*)(void*, int, int))&mace_audio_reg_write;
+
+	/* at mo we dont want any codec update irqs (SW) */
+	writeq(0L, &mace->perif.audio.codec_mask);
+
+	err = ad1843_init(chip->ad1843);
+	if (0 > err) goto ERROR_EXIT;
+
+	*rchip = chip;
+	snd_mace_audio_chip = chip;
+
+	return 0;
+
+ERROR_EXIT:
+	snd_printk(KERN_ERR "Attemp to initialise MACE Audio failed\n");
+	sma_free(chip);
+	return err;
+}
+
+/* constructer */
+static int __devinit sma_probe(void)
+{
+	snd_card_t      *card;
+	snd_mace_audio_t *chip;
+	int err;
+
+	/* create alsa stuff*/
+	card = snd_card_new(alsa_index, alsa_id,
+		THIS_MODULE, sizeof(snd_mace_audio_t));
+	if (NULL == card) return -ENOMEM;
+
+	/* sutup our stuff*/
+	err = sma_create(card, &chip);
+	if (0 > err) goto ERROR_EXIT;
+
+	/* tell the world who we are */
+	strcpy(card->driver, "SGI O2 MACE Audio Dirver");
+	strcpy(card->shortname, "MACE Audio");
+	sprintf(card->longname, "%s",
+		card->shortname );
+
+	/* attach our bits'n'bobs */
+	err = sma_pcm_new(chip);
+	if (0 > err) goto ERROR_EXIT;
+
+	err = sma_control_new(chip);
+	if (0 > err) goto ERROR_EXIT;
+
+	err = sma_proc_create(chip);
+	if (0 > err) goto ERROR_EXIT;
+
+	/* and we're done */
+	err = snd_card_register(card);
+	if (0 > err) goto ERROR_EXIT;
+
+	return 0;
+ERROR_EXIT:
+	snd_printk(KERN_ERR "Probe for MACE Audio failed\n");
+	snd_card_free(card);
+
+	return err;
+}
+
+/***************************** MACE access ***************************/
+static int mace_audio_reg_read(snd_mace_audio_t *chip, int reg)
+{
+	int val;
+	unsigned long flags;
+	unsigned int check;
+
+	spin_lock_irqsave(&chip->ad1843_lock, flags);
+	writeq(SMA_CODEC_READ(reg),
+		&mace->perif.audio.codec_control);
+	mb();
+	udelay(200);
+
+	check = readq(&mace->perif.audio.codec_control); /* flush bus */
+	if (unlikely((SMA_CODEC_READ(reg)) != check))
+		printk("MACE Audio reg read hic-up sent %lX, got %X\n",
+			(SMA_CODEC_READ(reg)), check);
+
+	val = (int)readq(&mace->perif.audio.codec_read);
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+
+	return val;
+}
+
+static int mace_audio_reg_write(snd_mace_audio_t *chip, int reg, int word)
+{
+	unsigned long flags;
+	unsigned int check;
+
+	spin_lock_irqsave(&chip->ad1843_lock, flags);
+
+	writeq(SMA_CODEC_WRITE(reg, word),
+		&mace->perif.audio.codec_control);
+	mb();
+
+	udelay(200);
+
+	check = readq(&mace->perif.audio.codec_control); /* flush bus */
+	if (unlikely((SMA_CODEC_WRITE(reg, word)) != check))
+		printk("MACE Audio reg write hic-up sent %lX, got %X\n",
+			(SMA_CODEC_WRITE(reg, word)), check);
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+
+	return 0;
+}
+
+/*********************** module setup ****************************/
+
+
+static int __init snd_card_mace_audio_init(void)
+{
+	int err;
+
+	err = sma_probe();
+	if (0 > err) goto ERROR_EXIT;
+
+	return 0;
+ERROR_EXIT:
+#ifdef MODULE
+	printk(KERN_ERR "Mace Audio not found or device busy\n");
+#endif
+	return err;
+}
+
+static void __exit snd_card_mace_audio_exit(void)
+{
+	if (NULL != snd_mace_audio_chip) {
+		snd_card_t *card = snd_mace_audio_chip->card;
+		sma_free(snd_mace_audio_chip);
+		snd_card_free(card);
+	}
+}
+
+MODULE_AUTHOR("Thorben Jändling <tj.trevelyan@xxxxxxxxx>");
+MODULE_DESCRIPTION("SGI O2 MACE Audio");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 MACE Audio}}");
+
+module_param(alsa_index, int, 0444);
+MODULE_PARM_DESC(alsa_index, "Index value for MACE Audio");
+module_param(alsa_id, charp, 0444);
+MODULE_PARM_DESC(alsa_id, "ID string for MACE Audio");
+module_param(alsa_enable, bool, 0444);
+MODULE_PARM_DESC(alsa_enable, "Enable MACE Audio");
+
+module_init(snd_card_mace_audio_init)
+module_exit(snd_card_mace_audio_exit)
+

--- linux-2.6.19.7-original/sound/mips/mace_audio_spy.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.19.7/sound/mips/mace_audio_spy.c	2007-06-28 10:46:24.000000000 +0100
@@ -0,0 +1,524 @@
+/*
+ *   Sound driver for Silicon Graphics O2 Workstations MACE audio board.
+ *
+ *   Copyright 2007 Thorben Jändling <tj.trevelyan@xxxxxxxxx>
+ *   Based/Copied heavily on/from sgio2audio.c:
+ *   Copyright 2003 Vivien Chappelier <vivien.chappelier@xxxxxxxxxxxxxx>
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/*
+ * To snoop at the state mace audio is in after he ARCS jingle
+ *
+ */
+
+
+/*************************** include ******************************/
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/gfp.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <asm/io.h>
+
+#include <asm/ip32/ip32_ints.h>
+#include <asm/ip32/mace.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+
+#include <sound/ad1843.h>
+
+/**************************** defines *******************************/
+
+/* 1: reset audio interface */
+#define SMA_CTRL_RESET          0x1UL
+/* 1: codec detected */
+#define SMA_CTRL_CODEC_PRESENT  0x2UL
+/*  2-8  : channel 1 write ptr alias */
+/*  9-15 : channel 2 read ptr alias */
+/* 16-22 : channel 3 read ptr alias */
+/* latched volume button */
+#define SMA_CTRL_VOL_BUTTON_UP   BIT(24)
+/* latched volume button */
+#define SMA_CTRL_VOL_BUTTON_DOWN BIT(23)
+
+#define SMA_CODEC_READ(reg)  ((reg) <<17) | 0x00010000UL
+#define SMA_CODEC_WRITE(reg, val) (((reg) <<17)|(val)) & 0x00FEFFFFUL
+
+#define SMA_RING_OFFSET(chi) ((chi) << 12)
+#define SMA_RING_SIZE 0x1000
+#define SMA_RING_MASK 0x0FFFUL
+
+/* int on buffer >50% full */
+#define SMA_INT_THRESHOLD_50 (2 << 5)
+/* 1: enable DMA transfer */
+#define SMA_DMA_ENABLE   BIT(9)
+
+/*old, define still inherted from sgio2audio*/
+
+#define SMA_CTRL_CHAN_RESET BIT(10) /* 1: reset channel */
+
+#define SMA_INT_THRESHOLD_DISABLED  (0 << 5) /* interrupt disabled */
+#define SMA_INT_THRESHOLD_25 (1 << 5) /* int on buffer >25% full */
+#define SMA_INT_THRESHOLD_75 (3 << 5) /* int on buffer >75% full */
+#define SMA_INT_THRESHOLD_EMPTY     (4 << 5) /* int on buffer empty */
+#define SMA_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */
+#define SMA_INT_THRESHOLD_FULL      (6 << 5) /* int on buffer empty */
+#define SMA_INT_THRESHOLD_NOT_FULL  (7 << 5) /* int on buffer !empty */
+
+/****************************** structs/types ***********************/
+
+/* chip specific record */
+typedef struct snd_mace_audio {
+	snd_card_t *card;
+	snd_pcm_t  *pcm[2];
+
+	struct {
+		snd_pcm_substream_t *substream;
+		snd_pcm_uframes_t        frames;
+
+		int                  ack;
+		void                *buffer;
+		u64                  pos;
+		spinlock_t           lock;
+	} channel[3];
+
+	/* which gain should the O2 vol butons control?*/
+	unsigned int ext_vol_for;
+
+	snd_info_entry_t  *proc_debug,
+	                  *proc_ad1843,
+	                  *proc_mace;
+
+	ad1843_t   *ad1843;
+	spinlock_t ad1843_lock;
+
+	void        *ring_base;
+	dma_addr_t   ring_base_handle;
+	unsigned long maceisa_base;
+	unsigned int mace_offset;
+
+} snd_mace_audio_t;
+
+typedef struct sma_proc_debug {
+	struct list_head link;
+
+/* name to display for the entry */
+	char  *name;
+
+/* a string value */
+	char  *val_str;
+
+/* an int val used if val_str is null */
+	long val_int;
+} sma_proc_debug_t;
+
+/************************** func declaration ****************************/
+/* mace access */
+static int mace_audio_reg_read(snd_mace_audio_t *chip, int reg);
+static int mace_audio_reg_write(snd_mace_audio_t *chip, int reg, int word);
+/* proc */
+static void sma_proc_ad1843_read
+	(snd_info_entry_t *proc_entry, snd_info_buffer_t *buffer);
+static void sma_proc_mace_read
+	(snd_info_entry_t *proc_entry, snd_info_buffer_t *buffer);
+static void sma_proc_debug_read
+	(snd_info_entry_t *, snd_info_buffer_t *);
+static sma_proc_debug_t* sma_proc_debug_append(char *name);
+static int sma_proc_create(snd_mace_audio_t *chip);
+/* ALSA Setup */
+static int sma_free(snd_mace_audio_t *chip);
+static int __devinit sma_create
+	(snd_card_t *card, snd_mace_audio_t **rchip);
+static int __devinit sma_probe(void);
+/* module setup */
+static int __init snd_card_mace_audio_init(void);
+static void __exit snd_card_mace_audio_exit(void);
+
+
+/************************* define psudo-funcs ***************************/
+#define sma_proc_entry_assert(entry,name) \
+	if (NULL == entry) entry = sma_proc_entry_append(name);
+
+/* pcm0 substream0 = channel 1
+ * pcm0 substream1 = channel 0
+ * pcm1 substream0 = channel 2
+ */
+#define substream_to_channel_index(substream) \
+ ((((snd_mace_audio_t*)(substream->private_data))->pcm[0]==substream->pcm)\
+ ?!(substream->number):2)
+
+/******************************** globals *******************************/
+
+
+LIST_HEAD(sma_proc_debug_list);
+
+static int   alsa_index = -1;
+static char *alsa_id    = NULL;
+static int   alsa_enable= 1;
+static int   sma_indirect = 1;
+
+/* Global needed for module exit */
+snd_mace_audio_t *snd_mace_audio_chip = NULL;
+
+
+/****************************** Proc files **********************************/
+static void sma_proc_ad1843_read
+	(snd_info_entry_t *proc_entry, snd_info_buffer_t *buffer)
+{
+	int i;
+	char *str;
+	snd_mace_audio_t *chip = proc_entry->private_data;
+
+	snd_iprintf(buffer,
+		"|  15/7   |  14/6   |  13/5   |  12/4   "
+		"|  11/3   |  10/2   |   9/1   |   8/0   |\n");
+
+	for (i=0; i<32; i++){
+
+		str = ad1843_dump_reg(chip->ad1843, i);
+		if (NULL == str) continue;
+
+		snd_iprintf(buffer, str);
+
+		vfree(str);
+	}
+
+}
+
+static void sma_proc_mace_read
+	(snd_info_entry_t *proc_entry, snd_info_buffer_t *buffer)
+{
+	snd_mace_audio_t *chip = proc_entry->private_data;
+	unsigned long r,w,c,d;
+	int chi;
+
+	mb();
+	r = readq(&mace->perif.ctrl.ringbase);
+	snd_iprintf(buffer, "MACE Periferals Ringbase: 0x%lX (%lu)\n"
+		"\t(CPU Address: 0x%lX (%lu), Set Bus Address: 0x%lX (%lu))\n",
+		(unsigned long)r, (unsigned long)r,
+		(unsigned long)chip->ring_base, (unsigned long)chip->ring_base,
+		chip->ring_base_handle, chip->ring_base_handle);
+
+	c = readq(&mace->perif.audio.control);
+	snd_iprintf(buffer, "MACE Audio Control: 0x%lX (%lu)\n", c, c);
+
+	c = readq(&mace->perif.audio.codec_control);
+	snd_iprintf(buffer, "MACE Audio Codec Status Control: 0x%lX (%lu)\n", c, c);
+
+	d = readq(&mace->perif.audio.codec_mask);
+	snd_iprintf(buffer, "MACE Audio Codec Status IRQ Mask: 0x%lX (%lu)\n", d, d);
+
+	r = readq(&mace->perif.audio.codec_read);
+	snd_iprintf(buffer, "MACE Audio Codec Status Value: 0x%lX (%lu)\n", r, r);
+
+	for (chi=0; chi<3; chi++) {
+
+		mb();
+		c = readq(&mace->perif.audio.chan[chi].control);
+		r = readq(&mace->perif.audio.chan[chi].read_ptr);
+		w = readq(&mace->perif.audio.chan[chi].write_ptr);
+		d = readq(&mace->perif.audio.chan[chi].depth);
+
+		snd_iprintf(buffer, "MACE Audio Channel %u:-\n"
+			"\tControl: 0x%lX (%lu)\n"
+			"\tRead Pointer: 0x%lX (%lu)\n"
+			"\tWrite Pointer: 0x%lX (%lu)\n"
+			"\tDepth: 0x%lX (%lu)\n",
+			chi, c, c, r, r, w, w, d, d);
+
+		if (chip->channel[chi].substream &&
+		    chip->channel[chi].substream->runtime ) {
+			unsigned long ap = frames_to_bytes(
+				chip->channel[chi].substream->runtime,
+				chip->channel[chi].substream->runtime->control
+					->appl_ptr);
+
+
+			snd_iprintf(buffer, "\tALSA Runtime DMA area: 0x%lX (%lu)\n",
+				chip->channel[chi].substream->runtime->dma_area,
+				chip->channel[chi].substream->runtime->dma_area);
+
+			snd_iprintf(buffer, "\tALSA App Pointer: 0x%lX (%lu) [0x%lX (%lu)]\n",
+				ap,ap,
+				chip->channel[chi].substream->runtime->control
+					->appl_ptr,
+				chip->channel[chi].substream->runtime->control
+					->appl_ptr);
+
+		}
+	}
+}
+
+static void sma_proc_debug_read
+	(snd_info_entry_t *proc_entry, snd_info_buffer_t *buffer)
+{
+	sma_proc_debug_t *entry;
+
+	snd_iprintf(buffer, "MACE Audio debug info.\n");
+
+	list_for_each_entry(entry, &sma_proc_debug_list, link) {
+
+		if (NULL == entry->val_str)
+			snd_iprintf(buffer, "%s: \t%ld\n",
+				entry->name, entry->val_int);
+		else
+			snd_iprintf(buffer, "%s: \t%s\n",
+				entry->name, entry->val_str);
+	}
+
+}
+
+/* this is called inside the volume interrupt, but is it appropriate
+ * to be allocating memory during an interrupt? */
+static sma_proc_debug_t* sma_proc_debug_append
+	(char *name)
+{
+	sma_proc_debug_t *entry =
+		kzalloc(sizeof(sma_proc_debug_t), GFP_ATOMIC);
+
+	entry->name = name;
+
+	list_add_tail(&entry->link, &sma_proc_debug_list);
+
+	return entry;
+
+}
+
+static int sma_proc_create(snd_mace_audio_t *chip)
+{
+	int err;
+
+	err = snd_card_proc_new(chip->card, "debug", &chip->proc_debug);
+	if (0 > err) return err;
+
+	snd_info_set_text_ops(chip->proc_debug, chip,
+		sma_proc_debug_read);
+
+	err = snd_card_proc_new(chip->card, "ad1843", &chip->proc_ad1843);
+	if (0 > err) return err;
+
+	snd_info_set_text_ops(chip->proc_ad1843, chip,
+		sma_proc_ad1843_read);
+
+	err = snd_card_proc_new(chip->card, "mace", &chip->proc_mace);
+	if (0 > err) return err;
+
+	snd_info_set_text_ops(chip->proc_mace, chip,
+		sma_proc_mace_read);
+
+	return 0;
+}
+
+/******************************* ALSA setup **************************/
+
+/* chip specific destructor */
+static int sma_free(snd_mace_audio_t *chip)
+{
+	int irq;
+
+printk("sma_free called\n");
+
+	/* free codec struct*/
+	kfree(chip->ad1843);
+	snd_mace_audio_chip = NULL;
+
+	return 0;
+}
+
+/* chip specific constructor, but we don't init anything */
+static int __devinit sma_create
+	(snd_card_t *card, snd_mace_audio_t **rchip)
+{
+	snd_mace_audio_t *chip;
+	int err, irq;
+	unsigned long mace_reg, mringbase;
+
+	*rchip = NULL;
+	chip = card->private_data;
+	chip->card = card;
+
+	/* check if the codec is present */
+	mace_reg = readq(&mace->perif.audio.control);
+	err = (mace_reg & SMA_CTRL_CODEC_PRESENT)>0? 0 : -ENOENT;
+	if (0 > err) goto ERROR_EXIT;
+
+	/* spin locks */
+	spin_lock_init(&chip->ad1843_lock);
+
+	/* ad1843 setup */
+	chip->ad1843 = kmalloc(sizeof(ad1843_t), GFP_KERNEL);
+	err = (NULL == chip->ad1843)?-ENOMEM:0;
+	if (0 > err) goto ERROR_EXIT;
+
+	chip->ad1843->chip =  chip;
+	chip->ad1843->read =
+		(int(*)(void*, int))&mace_audio_reg_read;
+	chip->ad1843->write =
+		(int(*)(void*, int, int))&mace_audio_reg_write;
+
+	*rchip = chip;
+	snd_mace_audio_chip = chip;
+
+	return 0;
+
+ERROR_EXIT:
+	snd_printk(KERN_ERR "Attemp to initialise MACE Audio failed\n");
+	sma_free(chip);
+	return err;
+}
+
+/* constructer */
+static int __devinit sma_probe(void)
+{
+	snd_card_t      *card;
+	snd_mace_audio_t *chip;
+	int err;
+
+	/* create alsa stuff*/
+	card = snd_card_new(alsa_index, alsa_id,
+		THIS_MODULE, sizeof(snd_mace_audio_t));
+	if (NULL == card) return -ENOMEM;
+
+	/* sutup our stuff*/
+	err = sma_create(card, &chip);
+	if (0 > err) goto ERROR_EXIT;
+
+	/* tell the world who we are */
+	strcpy(card->driver, "SGI O2 MACE Audio Spy");
+	strcpy(card->shortname, "MACE Audio Spy");
+	sprintf(card->longname, "%s",
+		card->shortname );
+
+	err = sma_proc_create(chip);
+	if (0 > err) goto ERROR_EXIT;
+
+	/* and we're done */
+	err = snd_card_register(card);
+	if (0 > err) goto ERROR_EXIT;
+
+	return 0;
+ERROR_EXIT:
+	snd_printk(KERN_ERR "Probe for MACE Audio failed\n");
+	snd_card_free(card);
+
+	return err;
+}
+
+/***************************** MACE access ***************************/
+static int mace_audio_reg_read(snd_mace_audio_t *chip, int reg)
+{
+	int val;
+	unsigned long flags;
+	unsigned int check;
+
+	spin_lock_irqsave(&chip->ad1843_lock, flags);
+	writeq(SMA_CODEC_READ(reg),
+		&mace->perif.audio.codec_control);
+	mb();
+	udelay(200);
+
+	check = readq(&mace->perif.audio.codec_control); /* flush bus */
+	if (unlikely((SMA_CODEC_READ(reg)) != check))
+		printk("MACE Audio reg read hic-up sent %lX, got %X\n",
+			(SMA_CODEC_READ(reg)), check);
+
+	val = (int)readq(&mace->perif.audio.codec_read);
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+
+	return val;
+}
+
+static int mace_audio_reg_write(snd_mace_audio_t *chip, int reg, int word)
+{
+	unsigned long flags;
+	unsigned int check;
+
+	spin_lock_irqsave(&chip->ad1843_lock, flags);
+
+	writeq(SMA_CODEC_WRITE(reg, word),
+		&mace->perif.audio.codec_control);
+	mb();
+
+	udelay(200);
+
+	check = readq(&mace->perif.audio.codec_control); /* flush bus */
+	if (unlikely((SMA_CODEC_WRITE(reg, word)) != check))
+		printk("MACE Audio reg write hic-up sent %lX, got %X\n",
+			(SMA_CODEC_WRITE(reg, word)), check);
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+
+	return 0;
+}
+
+/*********************** module setup ****************************/
+
+
+static int __init snd_card_mace_audio_init(void)
+{
+	int err;
+
+	err = sma_probe();
+	if (0 > err) goto ERROR_EXIT;
+
+	return 0;
+ERROR_EXIT:
+#ifdef MODULE
+	printk(KERN_ERR "Mace Audio not found or device busy\n");
+#endif
+	return err;
+}
+
+static void __exit snd_card_mace_audio_exit(void)
+{
+	if (NULL != snd_mace_audio_chip) {
+		snd_card_t *card = snd_mace_audio_chip->card;
+		sma_free(snd_mace_audio_chip);
+		snd_card_free(card);
+	}
+}
+
+MODULE_AUTHOR("Thorben Jändling <tj.trevelyan@xxxxxxxxx>");
+MODULE_DESCRIPTION("SGI O2 MACE Audio Spy");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 MACE Audio}}");
+
+module_param(alsa_index, int, 0444);
+MODULE_PARM_DESC(alsa_index, "Index value for MACE Audio");
+module_param(alsa_id, charp, 0444);
+MODULE_PARM_DESC(alsa_id, "ID string for MACE Audio");
+module_param(alsa_enable, bool, 0444);
+MODULE_PARM_DESC(alsa_enable, "Enable MACE Audio");
+
+module_init(snd_card_mace_audio_init)
+module_exit(snd_card_mace_audio_exit)
+

--- linux-2.6.19.7-original/sound/mips/Kconfig	2007-03-10 12:11:23.000000000 +0000
+++ linux-2.6.19.7/sound/mips/Kconfig	2007-06-28 11:35:38.000000000 +0100
@@ -11,5 +11,18 @@
 	help
 	  ALSA Sound driver for the Au1x00's AC97 port.
 
+config SND_MACE_AUDIO
+	tristate "SGI O2 MACE Audio"
+	depends on  SND && SGI_IP32
+	help
+	Sound support for the SGI O2 Workstation.
+
+config SND_MACE_AUDIO_SPY
+	tristate "SGI O2 MACE Audio Spy"
+	depends on  SND && SGI_IP32
+	help
+	Spy on MACE Audio Hardware state. Do not use if you don't know
+	what this module is for or why you want to use it.
+
 endmenu
 

--- linux-2.6.19.7-original/sound/mips/Makefile	2007-03-10 12:11:23.000000000 +0000
+++ linux-2.6.19.7/sound/mips/Makefile	2007-06-28 11:35:36.000000000 +0100
@@ -3,6 +3,11 @@
 #
 
 snd-au1x00-objs := au1x00.o
+snd-mace-audio-objs := mace_audio.o ad1843.o
+snd-mace-audio-spy-objs := mace_audio_spy.o ad1843.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
+obj-$(CONFIG_SND_MACE_AUDIO) += snd-mace-audio.o
+obj-$(CONFIG_SND_MACE_AUDIO_SPY) += snd-mace-audio-spy.o
+

[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux