Re: [alsa-devel] [RFC] SGI O2 MACE audio ALSA module

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

 



Hi,

Here is another patch that moves towards addressing some of the issues
raised about the last patch. Also I had trouble mailing the last patch
to the linux mips list, mainly because I attached the patch as opposed
to putting inline, so this time its pasted in. (I guess we'll find out
if thats safe with Gmail).

This patch was built against Linux-mips.org 2.6.21.6

On 03/07/07, Takashi Iwai <tiwai@xxxxxxx> wrote:
At Mon, 2 Jul 2007 12:27:58 +0100,
The patch includes old typdefed structs which were already removed
from the upstream, such as, snd_card_t.  Please replace them
appropriately.  You can find the replacement in
include/sound/typedefs.h (in the old kernel tree).

Fixed, does not use typedefs for ALSA structs.

Similarly, I'd recommend to avoid typedefs in your own code, too.

I still have two typedef structs of my own. However these aren't used
outside the single C source file.


Other things I noticed through a quick glance:

- Follow the standard coding style, e.g. 80 chars in a line, don't put
  if-block in a single line, etc.

I tidily hard wrapped lines that i spotted over 80. How many people
still using 80 column terminals? Maybe 132 would be a better choice
these days?

Is something like:
if (0 > err) goto ERROR_EXIT;
really that bad? In two lines it isn't much clearer and just user more space.

- Avoid non-ASCII letters, especially outside the comments

Outside of the umlaut in my own name, I think the only place I had
UTF-8 chars was in ad1843_dump_reg strings, I have fixed this, please
let me know if there are more.

- You don't need *_irqsave() in get/put callbacks of the control API.
  It's always schedulable, so, spin_lock_irq() suffices.

Done

- ad1843_lock could be better implemented with mutex if you have long
  delays inside the spinlock (except for the calls from irq handler)

Doing this would be something new to me, so it's on my research list/todo list.

- please remove uneeded debug printks.  If they are useful, keep it in
  another macro form.

Substituted for snd_printd (which requires CONFIG_SND_DEBUG), but
still with no indentation so I can find and delete them in future.

Could you fix these and repost?
Yes =)

It still plays audio too fast.
Please let me know about your experience with it, and
tips/sugestions/bug fixes/etc you have.

Regards,

Thorben

Patch can also be found here:

http://www.linux-mips.org/pub/linux/mips/people/trevelyan/mace_audio/mace_audio-20070709.patch

-------------------------------------

--- linux-2.6.21.6-b/include/sound/ad1843.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.21.6/include/sound/ad1843.h	2007-07-09 19:14:18.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.21.6-b/sound/mips/ad1843.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.21.6/sound/mips/ad1843.c	2007-07-09 20:09:15.000000000 +0100
@@ -0,0 +1,911 @@
+/*
+ *   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.21.6-b/sound/mips/mace_audio.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.21.6/sound/mips/mace_audio.c	2007-07-09 20:29:50.000000000 +0100
@@ -0,0 +1,1347 @@
+/*
+ *   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 {
+	struct snd_card *card;
+	struct snd_pcm  *pcm[2];
+
+	struct {
+		struct snd_pcm_substream *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;
+
+	struct snd_info_entry  *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(struct snd_pcm_substream *substream);
+static int sma_pcm_close(struct snd_pcm_substream *substream);
+static int sma_pcm_hwparam
+	(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params);
+static int sma_pcm_hwfree(struct snd_pcm_substream *substream);
+static int sma_pcm_prepare(struct snd_pcm_substream *substream);
+static int sma_pcm_trigger(struct snd_pcm_substream *substream, int cmd);
+static snd_pcm_uframes_t sma_pcm_pointer
+	(struct snd_pcm_substream *substream);
+static struct page *sma_pcm_page
+	(struct snd_pcm_substream *substream, unsigned long offset);
+static int sma_pcm_ack(struct snd_pcm_substream *substream);
+static int __devinit sma_pcm_new(snd_mace_audio_t *chip);
+/* interrupt and DMA engine */
+static int sma_dma_start(struct snd_pcm_substream *substream);
+static int sma_dma_stop(struct snd_pcm_substream *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
+	(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
+static int sma_gain_get
+	(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+static int sma_gain_put
+	(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+static int __devinit sma_control_new(snd_mace_audio_t *chip);
+/* proc */
+static void sma_proc_ad1843_read
+	(struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer);
+static void sma_proc_mace_read
+	(struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer);
+static void sma_proc_debug_read
+	(struct snd_info_entry *, struct snd_info_buffer *);
+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
+	(struct snd_card *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 struct snd_kcontrol_new 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 struct snd_kcontrol_new 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 struct snd_kcontrol_new 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 struct snd_kcontrol_new 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 struct snd_kcontrol_new 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 struct snd_kcontrol_new 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);
+	snd_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);
+
+		snd_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(struct snd_pcm_substream *substream)
+{
+	//snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int chi = substream_to_channel_index(substream);
+snd_printd("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(struct snd_pcm_substream *substream)
+{
+	//snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	//FIXME code
+snd_printd("in pcm close\n");
+
+	return 0;
+}
+
+/* setup hw for pcm */
+/* maybe called more then once */
+static int sma_pcm_hwparam
+	(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
+{
+	snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	int chi = substream_to_channel_index(substream);
+snd_printd("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~*/);
+	}
+
+snd_printd("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(struct snd_pcm_substream *substream)
+{
+	snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	int chi = substream_to_channel_index(substream);
+snd_printd("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(struct snd_pcm_substream *substream)
+{
+	snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *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*/
+snd_printd("in prepare capture for channel %d\n", chi);
+		
+		ad1843_setup_adc(chip->ad1843,
+			runtime->rate,
+			SNDRV_PCM_FORMAT_S24_BE,
+			runtime->channels);
+	} else { /* playback */
+snd_printd("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(struct snd_pcm_substream *substream, int cmd)
+{
+snd_printd("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
+	(struct snd_pcm_substream *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);
+
+	}
+
+//snd_printd("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
+	(struct snd_pcm_substream *substream, unsigned long offset)
+{
+	struct page *p;
+snd_printd("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(struct snd_pcm_substream *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;
+
+snd_printd("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)
+{
+	struct snd_pcm *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(struct snd_pcm_substream *substream)
+{
+	snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	int chi = substream_to_channel_index(substream);
+
+snd_printd("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(struct snd_pcm_substream *substream)
+{
+	int chi = substream_to_channel_index(substream);
+snd_printd("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)
+		snd_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;
+
+snd_printd("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);
+snd_printd("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
+	(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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
+	(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	snd_mace_audio_t *chip = snd_kcontrol_chip(kcontrol);
+	int vol;
+
+	spin_lock_irq(&chip->ad1843_lock);
+
+	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_irq(&chip->ad1843_lock);
+
+	return 0;
+}
+
+static int sma_gain_put
+	(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	snd_mace_audio_t *chip = snd_kcontrol_chip(kcontrol);
+	int newvol, oldvol;
+	unsigned long flags;
+
+	spin_lock_irq(&chip->ad1843_lock);
+
+	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_irq(&chip->ad1843_lock);
+
+	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
+	(struct snd_info_entry *proc_entry, struct snd_info_buffer *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
+	(struct snd_info_entry *proc_entry, struct snd_info_buffer *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
+	(struct snd_info_entry *proc_entry, struct snd_info_buffer *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;
+
+snd_printd("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
+	(struct snd_card *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;*/
+
+snd_printd("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)
+{
+	struct snd_card      *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))
+		snd_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))
+		snd_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
+	snd_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) {
+		struct snd_card *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.21.6-b/sound/mips/mace_audio_spy.c	1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.21.6/sound/mips/mace_audio_spy.c	2007-07-09
19:51:22.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 {
+	struct snd_card *card;
+	struct snd_pcm  *pcm[2];
+
+	struct {
+		struct snd_pcm_substream *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;
+
+	struct snd_info_entry  *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
+	(struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer);
+static void sma_proc_mace_read
+	(struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer);
+static void sma_proc_debug_read
+	(struct snd_info_entry *, struct snd_info_buffer *);
+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
+	(struct snd_card *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
+	(struct snd_info_entry *proc_entry, struct snd_info_buffer *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
+	(struct snd_info_entry *proc_entry, struct snd_info_buffer *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
+	(struct snd_info_entry *proc_entry, struct snd_info_buffer *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
+	(struct snd_card *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)
+{
+	struct snd_card      *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) {
+		struct snd_card *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.21.6-b/sound/mips/Kconfig	2007-07-09 11:57:38.000000000 +0100
+++ linux-2.6.21.6/sound/mips/Kconfig	2007-07-09 19:14:18.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.21.6-b/sound/mips/Makefile	2007-07-09 11:57:38.000000000 +0100
+++ linux-2.6.21.6/sound/mips/Makefile	2007-07-09 19:14:18.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