[PATCH?] MACE Audio ALSA driver

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

 



Hi,

I have been working on an audio driver for the SGI O2.
It is more complete then the sgio2audio driver that is about, but PCM still
doesn't work for other reasons.

Namely the DMA feed doesn't seem to run correctly, see output below.

"
#comments start with # and generally come after the line they refer to

allocated ring base. CPU addr=0x9000000004620000, Bus addr=0x44620000
#Note the bus/phys address that dma_alloc_coherent gave us.

sizeof void*=8, unsigned long=8, int=4, u32=4, u32*=8, u64=8, u64*=8
in playback open
In hw param for channel 1
In pcm prepare for channel 1
in playback for channel 1, rate 44100, sub-channels 2
In pcm trigger
In DMA start for channel 1
#about to start DMA, but before we do start, lets do a push first

In DMA push for 1
ring base 0x9000000004620000, dst_base 0x9000000004621000, dst_pos 0x0, dst
0x9000000004621000

4096 remain, 0 used
reduced remain to 3968
starting push loop. ringbase=0x44620010,
       0[ writep=0x0 (0), readp=0x0 (0), depth=0x0 (0) ]
       1[ writep=0x0 (0), readp=0x0 (0), depth=0x0 (0) ]
       2[ writep=0x0 (0), readp=0x0 (0), depth=0x0 (0) ]
#this is a printout that I have dotted around to the code to watch the
# pointers in mace, these numbers are freshly read in.
#Note the change in the ringbase address! It always seems to get incremented
# by 0x10, why?

#looping here

after push loop, 0 remain, 3968 used, dst pos 3968
Finished push loop. ringbase=0x44620010,
       0[ writep=0x0 (0), readp=0x0 (0), depth=0x0 (0) ]
       1[ writep=0xF80 (3968), readp=0x0 (0), depth=0xF80 (3968) ]
       2[ writep=0x0 (0), readp=0x0 (0), depth=0x0 (0) ]
#bar ringbase, this is what we would expect

DMA started. ringbase=0x44620010,
       0[ writep=0x0 (0), readp=0x0 (0), depth=0x0 (0) ]
       1[ writep=0xF80 (3968), readp=0x40 (64), depth=0xF40 (3904) ]
       2[ writep=0x0 (0), readp=0x0 (0), depth=0x0 (0) ]
#DMA enabled, and the read pointer has moved on

In pcm pointer. ringbase=0x44620010,
       0[ writep=0x0 (0), readp=0x0 (0), depth=0x0 (0) ]
       1[ writep=0xF80 (3968), readp=0x40 (64), depth=0xF40 (3904) ]
       2[ writep=0x0 (0), readp=0x0 (0), depth=0x0 (0) ]

#This is inside one of the ALSA callbacks some time later.
#Note the read pointer has not moved any further, its always sticks at
#0x40?! Even if push code didn't put the correct data in the correct place
#as far as mace is concerned there is still a depth of 0xF40, so why has it
#stopped?
"

Currently I think it has something to do with the ad1843. I have tried to
butter interpret the specs file to improve ad1843_init() and
ad1843_setup_dac(). (Note sgio2audio will need a few changes to work 'as
was', with my ad1843.c, main change is I made channel/dac/adc index/id
consistent, ie 0=adc,1=dac1,2=dac2 see ad1843_setup_dac())

I do most of this from a remote location to the O2 at the moment (the driver
has been stable enough to allow be to remotely reboot after a test) so I
have not worked on the kinks in the mixer code (and vol buttons) other then
they run ok with out errors. (and of course if I ever get dma going I would
not be able to hear it, although my girlfriend my get a nasty/noisy shock)

It is my plan to get ALSA to write/dma directly into the 4k channel buffer
(I know what needs to be done to get alsa to do this, assuming that the way
alsa stores s24_be in 32bits is agreeable to mace) thus removing the dma
push func. But for now (while testing) I'll keep the psudo dma setup.

Any thoughts, help, major coding mistakes by my self?

Regards

Thorben

PS
I would like to thanks all those that have already helped me with this in
the IRC channel.
diff -Nu linux-2.6.19.7.b/sound/mips/ad1843.c linux-2.6.19.7/sound/mips/ad1843.c
--- linux-2.6.19.7.b/sound/mips/ad1843.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.19.7/sound/mips/ad1843.c	2007-03-12 13:34:16.000000000 +0000
@@ -0,0 +1,649 @@
+/*
+ *   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 <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 = { 11,  7,  1 }, /* Right DAC1 Digital Mute */
+       ad1843_LDA2AM = { 11, 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_C1C    = { 17,  0, 16 }, /* Clock 1 Sample Rate Select */
+       ad1843_C2C    = { 20,  0, 16 }, /* Clock 2 Sample Rate Select */
+       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_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 = 0, 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_multi(ad1843, 2,
+			&ad1843_DA1SM, ad_mode,
+			&ad1843_DA1F, ad_fmt);
+		ad1843_write_bits(ad1843, &ad1843_DA1EN, 1);
+
+        } else {
+		ad1843_write_bits(ad1843, &ad1843_C2C, framerate);
+		ad1843_write_multi(ad1843, 2,
+			&ad1843_DA2SM, ad_mode,
+			&ad1843_DA2F, ad_fmt);
+		ad1843_write_bits(ad1843, &ad1843_DA2EN, 1);
+       }
+}
+
+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 */
+
+	/*set serial bus speed */
+	ad1843_write_bits(ad1843, &ad1843_SCF, 1);
+
+	/* Power down conv resources */
+	ad1843_write_bits(ad1843, &ad1843_PDNI, 1);
+	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;
+	}
+
+	/* 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. */
+
+	/* DAC1/2 use clock 1/2 as source, ADC uses clock 3.  Always. */
+	ad1843_write_multi(ad1843, 4,
+		&ad1843_DA1C, 1, &ad1843_DA2C, 2,
+		&ad1843_ADLC, 3, &ad1843_ADRC, 3);
+
+	/* 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();
+	}
+
+	/* 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, 4,
+                           &ad1843_LDA1AM, 0,
+                           &ad1843_RDA1AM, 0,
+                           &ad1843_LDA2AM, 0,
+                           &ad1843_RDA2AM, 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;
+}

diff -Nu linux-2.6.19.7.b/sound/mips/Kconfig linux-2.6.19.7/sound/mips/Kconfig
--- linux-2.6.19.7.b/sound/mips/Kconfig	2007-03-10 12:11:23.000000000 +0000
+++ linux-2.6.19.7/sound/mips/Kconfig	2007-03-12 13:32:23.000000000 +0000
@@ -11,5 +11,11 @@
 	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.
+
 endmenu
 
diff -Nu linux-2.6.19.7.b/sound/mips/mace_audio.c linux-2.6.19.7/sound/mips/mace_audio.c
--- linux-2.6.19.7.b/sound/mips/mace_audio.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.19.7/sound/mips/mace_audio.c	2007-03-12 13:34:26.000000000 +0000
@@ -0,0 +1,1175 @@
+/*
+ *   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/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 MACE_AUDIO_CTRL_RESET          0x00000001
+/* 1: codec detected */
+#define MACE_AUDIO_CTRL_CODEC_PRESENT  0x00000002
+/*  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 MACE_AUDIO_CTRL_VOL_BUTTON_UP   BIT(24)
+/* latched volume button */
+#define MACE_AUDIO_CTRL_VOL_BUTTON_DOWN BIT(23)
+
+#define MACE_AUDIO_CODEC_READ(reg)  (reg <<16) | 0x00010000
+#define MACE_AUDIO_CODEC_WRITE(reg, val) ((reg <<16)|val) & 0x00FEFFFF
+
+#define MACE_AUDIO_RING_SHIFT 12
+#define MACE_AUDIO_RING_SIZE (1 << MACE_AUDIO_RING_SHIFT)
+#define MACE_AUDIO_RING_MASK (MACE_AUDIO_RING_SIZE - 1)
+
+/* int on buffer >50% full */
+#define MACE_AUDIO_INT_THRESHOLD_50 (2 << 5)
+/* 1: enable DMA transfer */
+#define MACE_AUDIO_DMA_ENABLE   BIT(9)
+
+/*old, define still inherted from sgio2audio*/
+
+#define MACE_AUDIO_PTR_SHIFT     5
+#define MACE_AUDIO_PTR_MASK      ((1 << 6) - 1)
+#define MACE_AUDIO_CTRL_CHAN_RESET BIT(10) /* 1: reset channel */
+
+#define MACE_AUDIO_INT_THRESHOLD_DISABLED  (0 << 5) /* interrupt disabled */
+#define MACE_AUDIO_INT_THRESHOLD_25 (1 << 5) /* int on buffer >25% full */
+#define MACE_AUDIO_INT_THRESHOLD_75 (3 << 5) /* int on buffer >75% full */
+#define MACE_AUDIO_INT_THRESHOLD_EMPTY     (4 << 5) /* int on buffer empty */
+#define MACE_AUDIO_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */
+#define MACE_AUDIO_INT_THRESHOLD_FULL      (6 << 5) /* int on buffer empty */
+#define MACE_AUDIO_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;
+
+		void                *buffer;
+		unsigned int         pos;
+		spinlock_t           lock;
+	} channel[3];
+
+	/* which gain should the O2 vol butons control?*/
+	unsigned int ext_vol_for;
+
+	snd_info_entry_t  *proc;
+
+	ad1843_t   *ad1843;
+	spinlock_t ad1843_lock;
+
+	void        *ring_base;
+	dma_addr_t   ring_base_handle;
+
+	void       *alsa_dma_base;
+
+} snd_mace_audio_t;
+
+typedef struct snd_mace_audio_proc_entry {
+	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;
+} snd_mace_audio_proc_entry_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 snd_mace_audio_playback_open(snd_pcm_substream_t *substream);
+static int snd_mace_audio_playback_close(snd_pcm_substream_t *substream);
+static int snd_mace_audio_capture_open(snd_pcm_substream_t *substream);
+static int snd_mace_audio_capture_close(snd_pcm_substream_t *substream);
+static int snd_mace_audio_hw_param
+	(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params);
+static int snd_mace_audio_hw_free(snd_pcm_substream_t *substream);
+static int snd_mace_audio_pcm_prepare(snd_pcm_substream_t *substream);
+static int snd_mace_audio_pcm_trigger(snd_pcm_substream_t *substream, int cmd);
+static snd_pcm_uframes_t snd_mace_audio_pcm_pointer
+	(snd_pcm_substream_t *substream);
+static struct page *snd_mace_audio_page
+	(snd_pcm_substream_t *substream, unsigned long offset);
+static int __devinit snd_mace_audio_pcm_new(snd_mace_audio_t *chip);
+/* interrupt and DMA engine */
+static int snd_mace_audio_dma_start(snd_pcm_substream_t *substream);
+static int snd_mace_audio_dma_stop(snd_pcm_substream_t *substream);
+static int snd_mace_audio_dma_push(snd_mace_audio_t *chip, int chi);
+static irqreturn_t snd_mace_audio_interrupt
+	(int irq, snd_mace_audio_t *chip, struct pt_regs *regs);
+static void snd_mace_audio_adjust_vol(snd_mace_audio_t *chip);
+/* proc, mixer and controls */
+static int snd_mace_audio_gain_info
+	(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo);
+static int snd_mace_audio_gain_get
+	(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+static int snd_mace_audio_gain_put
+	(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+static int __devinit snd_mace_audio_control_new(snd_mace_audio_t *chip);
+static void snd_mace_audio_proc_read
+	(snd_info_entry_t *, snd_info_buffer_t *);
+static snd_mace_audio_proc_entry_t* snd_mace_audio_proc_entry_append(char *name);
+/* ALSA Setup */
+static int snd_mace_audio_free(snd_mace_audio_t *chip);
+static int __devinit snd_mace_audio_create
+	(snd_card_t *card, snd_mace_audio_t **rchip);
+static int __devinit snd_mace_audio_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 snd_mace_audio_proc_entry_assert(entry,name) \
+	if (NULL == entry) entry = snd_mace_audio_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 snd_mace_audio_hw = {
+	.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 =  4096,
+	.period_bytes_max =  32768,
+	.periods_min =       1,
+	.periods_max =       1024,
+};
+
+/* playback operators */
+static snd_pcm_ops_t snd_mace_audio_playback_ops = {
+	.open =       snd_mace_audio_playback_open,
+	.close =      snd_mace_audio_playback_close,
+	.ioctl =      snd_pcm_lib_ioctl,
+	.hw_params =  snd_mace_audio_hw_param,
+	.hw_free =    snd_mace_audio_hw_free,
+	.prepare =    snd_mace_audio_pcm_prepare,
+	.trigger =    snd_mace_audio_pcm_trigger,
+	.pointer =    snd_mace_audio_pcm_pointer,
+	.page    =    snd_mace_audio_page,
+};
+
+/* capture operators */
+static snd_pcm_ops_t snd_mace_audio_capture_ops = {
+	.open =       snd_mace_audio_capture_open,
+	.close =      snd_mace_audio_capture_close,
+	.ioctl =      snd_pcm_lib_ioctl,
+	.hw_params =  snd_mace_audio_hw_param,
+	.hw_free =    snd_mace_audio_hw_free,
+	.prepare =    snd_mace_audio_pcm_prepare,
+	.trigger =    snd_mace_audio_pcm_trigger,
+	.pointer =    snd_mace_audio_pcm_pointer,
+	.page    =    snd_mace_audio_page,
+};
+
+/* record level mixer control */
+static snd_kcontrol_new_t snd_mace_audio_ctrl_reclevel __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Capture Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_RECLEV,
+	.info           = snd_mace_audio_gain_info,
+	.get            = snd_mace_audio_gain_get,
+	.put            = snd_mace_audio_gain_put,
+};
+
+/* line mixer control */
+static snd_kcontrol_new_t snd_mace_audio_ctrl_line __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Line Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_LINE,
+	.info           = snd_mace_audio_gain_info,
+	.get            = snd_mace_audio_gain_get,
+	.put            = snd_mace_audio_gain_put,
+};
+
+/* cd mixer control */
+static snd_kcontrol_new_t snd_mace_audio_ctrl_cd __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "CD Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_CD,
+	.info           = snd_mace_audio_gain_info,
+	.get            = snd_mace_audio_gain_get,
+	.put            = snd_mace_audio_gain_put,
+};
+
+/* mic mixer control */
+static snd_kcontrol_new_t snd_mace_audio_ctrl_mic __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Mic Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_MIC,
+	.info           = snd_mace_audio_gain_info,
+	.get            = snd_mace_audio_gain_get,
+	.put            = snd_mace_audio_gain_put,
+};
+
+/* dac1/pcm0 mixer control */
+static snd_kcontrol_new_t snd_mace_audio_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           = snd_mace_audio_gain_info,
+	.get            = snd_mace_audio_gain_get,
+	.put            = snd_mace_audio_gain_put,
+};
+
+/* dac2/pcm1 mixer control */
+static snd_kcontrol_new_t snd_mace_audio_ctrl_pcm1 __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Playback Out-2 Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_PCM_1,
+	.info           = snd_mace_audio_gain_info,
+	.get            = snd_mace_audio_gain_get,
+	.put            = snd_mace_audio_gain_put,
+};
+
+
+LIST_HEAD(snd_mace_audio_proc_list);
+
+static int   alsa_index = -1;
+static char *alsa_id    = NULL;
+static int   alsa_enable= 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 (%ld), readp=0x%lX (%ld), "
+			"depth=0x%lX (%ld) ]\n",
+			chi, wp, wp, rp, rp, depth, depth);
+	}
+
+}
+
+/* play open callback */
+static int snd_mace_audio_playback_open(snd_pcm_substream_t *substream)
+{
+	//snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+printk("in playback open\n");
+
+	runtime->hw = snd_mace_audio_hw;
+
+	return 0;
+}
+
+/* play close callback */
+static int snd_mace_audio_playback_close(snd_pcm_substream_t *substream)
+{
+	//snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	//FIXME code
+printk("in playback close\n");
+
+	return 0;
+}
+
+/* capture open callback */
+static int snd_mace_audio_capture_open(snd_pcm_substream_t *substream)
+{
+	//snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+printk("in capture open\n");
+
+	runtime->hw = snd_mace_audio_hw;
+
+	return 0;
+}
+
+/* close callback */
+static int snd_mace_audio_capture_close(snd_pcm_substream_t *substream)
+{
+	//snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	//FIXME code
+printk("in capture close\n");
+
+	return 0;
+}
+
+/* setup hw for pcm */
+/* maybe called more then once */
+static int snd_mace_audio_hw_param
+	(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 (NULL != substream->runtime->dma_area)
+		vfree(substream->runtime->dma_area);
+
+	substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
+	substream->runtime->dma_area =
+		vmalloc_user(substream->runtime->dma_bytes);
+
+/*
+	substream->runtime->dma_bytes = MACE_AUDIO_RING_SIZE;
+	substream->runtime->dma_area =
+		(chip->alsa_dma_base + (chi << MACE_AUDIO_RING_SHIFT));
+*/
+	
+	if (NULL == substream->runtime->dma_area) return -ENOMEM;
+
+	return 0;
+}
+
+/* free hw for pcm */
+/* maybe called more then once */
+static int snd_mace_audio_hw_free(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 (NULL != substream->runtime->dma_area)
+		vfree(substream->runtime->dma_area);
+
+	substream->runtime->dma_bytes = 0;
+	substream->runtime->dma_area = NULL;
+	chip->channel[chi].buffer = NULL;
+	chip->channel[chi].pos = 0;
+	chip->channel[chi].frames = 0;
+
+	return 0;
+}
+
+/* prepare callback */
+static int snd_mace_audio_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;
+printk("In pcm prepare for channel %d\n", chi);
+
+	spin_lock_irqsave(&chip->channel[chi].lock, flags);
+
+	chip->channel[chi].pos = 0;
+	chip->channel[chi].frames = 0;
+	chip->channel[chi].substream = substream;
+
+	if (0 == chi) { /* capture*/
+printk("in capture\n");
+		ad1843_setup_adc(chip->ad1843,
+			runtime->rate,
+			SNDRV_PCM_FORMAT_S24_BE, /*24bit?*/
+			runtime->channels);
+	} else { /* playback */
+printk("in 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, /*24bit?*/
+			runtime->channels);
+	}
+
+	spin_unlock_irqrestore(&chip->channel[chi].lock, flags);
+
+	return 0;
+}
+
+/* trigger callback */
+static int snd_mace_audio_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+printk("In pcm trigger\n");
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START: /* start PCM engine */
+		snd_mace_audio_dma_start(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP: /* stop PCM engine */
+		snd_mace_audio_dma_stop(substream);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t snd_mace_audio_pcm_pointer
+	(snd_pcm_substream_t *substream)
+{
+	snd_mace_audio_t *chip = snd_pcm_substream_chip(substream);
+	int chi = substream_to_channel_index(substream);
+	snd_pcm_uframes_t current_ptr;
+
+print_pointers("In pcm pointer");
+
+	current_ptr = bytes_to_frames(substream->runtime,
+		chip->channel[chi].pos);
+
+	return current_ptr;
+}
+
+/* get page callback */
+static struct page *snd_mace_audio_page
+	(snd_pcm_substream_t *substream, unsigned long offset)
+{
+	return vmalloc_to_page(substream->runtime->dma_area + offset);
+}
+
+/* create pcm device */
+static int __devinit snd_mace_audio_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(MACE_AUDIO_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 1");
+
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+		&snd_mace_audio_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+		&snd_mace_audio_capture_ops);
+
+	chip->pcm[0] = pcm;
+
+
+	pcm = NULL;
+	err = snd_pcm_new(chip->card, "Mace Audio 2", 1, 1, 0, &pcm);
+	if (0 > err) return err; /*free pcm0 ? */
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "Mace Audio Out-2");
+
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+		&snd_mace_audio_playback_ops);
+
+	chip->pcm[1] = pcm;
+
+	return 0;
+}
+
+/*************************** Interrupts and DMA engine ****************/
+static int snd_mace_audio_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 start for channel %d\n", chi);
+
+	writeq(MACE_AUDIO_CTRL_CHAN_RESET,
+		&mace->perif.audio.chan[chi].control);
+	udelay(10);
+	writeq(0L, &mace->perif.audio.chan[chi].control);
+	mb();
+
+	/* push a full buffer */
+	snd_mace_audio_dma_push(chip, chi);
+
+	/* set DMA to wake on 50% empty and enable interrupt */
+	writeq(MACE_AUDIO_DMA_ENABLE |
+		MACE_AUDIO_INT_THRESHOLD_50,
+		&mace->perif.audio.chan[chi].control);
+	mb();
+print_pointers("DMA started");
+
+	return 0;
+}
+
+static int snd_mace_audio_dma_stop(snd_pcm_substream_t *substream)
+{
+	int chi = substream_to_channel_index(substream);
+printk("In DMA stop for channel %d\n", chi);
+
+	writeq(MACE_AUDIO_CTRL_CHAN_RESET,
+		&mace->perif.audio.chan[chi].control);
+	udelay(20);
+	writeq(0L, &mace->perif.audio.chan[chi].control);
+
+print_pointers("DMA stopped");
+
+	return 0;
+}
+
+static int snd_mace_audio_dma_push(snd_mace_audio_t *chip, int chi)
+{
+	void *dst_base, *src_base;
+	unsigned long dst_pos, src_pos;
+	u64 *dst;
+	s32 *src;
+	unsigned long flags, remain, used, tmp;
+	unsigned int periods;
+	snd_pcm_runtime_t *runtime = chip->channel[chi].substream->runtime;
+
+printk("In DMA push for %d\n", chi);
+
+	spin_lock_irqsave(&chip->channel[chi].lock, flags);
+
+	/* we want to move around inside our 4k, so make dst_pos=0
+	 * the bottom of it */
+	dst_base = (unsigned long)chip->ring_base + (chi << MACE_AUDIO_RING_SHIFT);
+	dst_pos  = readq(&mace->perif.audio.chan[chi].write_ptr);
+
+	src_base = runtime->dma_area;
+	src_pos  = chip->channel[chi].pos;
+	/* pos relative to base? */
+
+	dst = (dst_base + dst_pos);
+	src = (src_base + src_pos);
+
+printk("ring base 0x%lX, dst_base 0x%lX, dst_pos 0x%lX, dst 0x%lX\n",
+	(unsigned long)chip->ring_base, dst_base, dst_pos, dst);
+
+	used = readq(&mace->perif.audio.chan[chi].depth);
+	remain = MACE_AUDIO_RING_SIZE - used;
+
+printk("%ld remain, %ld used\n", remain, used);
+
+	/* try not to catch up with read pointer*/
+	while (MACE_AUDIO_RING_SIZE <= remain) remain -= (4 << 5);
+printk("reduced remain to %ld\n", remain);
+
+print_pointers("starting push loop");
+
+	while (0 < remain) {
+/* ignore any sign extend at mo
+ * I am hoping to skip this intermediate step and have alsa write
+ * direct to the ring buffer, I'm leaving this here now to be able
+ * to watch/listen what happens first
+ * but first I need to get DMA feed working.
+ */
+		tmp = src[0];
+		*dst = ((tmp << 32)|src[1]) & 0x007FFFFF007FFFFF;
+
+		dst_pos += sizeof(u64);
+		dst_pos &= MACE_AUDIO_RING_MASK;
+		dst = (dst_base + dst_pos);
+
+		src_pos += 2* sizeof(s32);
+		/*src_pos &= MACE_AUDIO_RING_MASK;*/
+		src_pos &= (runtime->dma_bytes -1);
+		src = (src_base + src_pos);
+
+		remain -= sizeof(u64);
+	}
+
+	mb();
+
+	/* put dst_pos back for write back */
+	writeq(dst_pos, &mace->perif.audio.chan[chi].write_ptr);
+	chip->channel[chi].pos = src_pos;
+	mb();
+
+used = readq(&mace->perif.audio.chan[chi].depth);
+printk("after push loop, %ld remain, %ld used, dst pos %ld\n", remain, used, dst_pos);
+
+
+print_pointers("Finished push loop");
+
+	/* check if a period has elapsed ?FIXME? */
+	chip->channel[chi].frames += bytes_to_frames(runtime, used);
+	periods = chip->channel[chi].frames >= runtime->period_size;
+	chip->channel[chi].frames %= runtime->period_size;
+
+	spin_unlock_irqrestore(&chip->channel[chi].lock, flags);
+
+	return (0 < periods);
+}
+
+static irqreturn_t snd_mace_audio_interrupt
+	(int irq, snd_mace_audio_t *chip, struct pt_regs *regs)
+{
+printk("In interrupt %d\n", irq);
+
+	switch (irq) {
+	case MACEISA_AUDIO_SW_IRQ:
+		printk("Got MACEISA AUDIO SW IRQ\n");
+		return IRQ_NONE; /*TODO*/
+
+	case MACEISA_AUDIO_SC_IRQ:
+		snd_mace_audio_adjust_vol(chip);
+		break;
+
+	case MACEISA_AUDIO1_DMAT_IRQ:
+		if(snd_mace_audio_dma_push(chip, 0))
+			snd_pcm_period_elapsed(chip->channel[0].substream);
+		break;
+
+	case MACEISA_AUDIO1_OF_IRQ:
+		printk("Got MACEISA AUDIO1 OF IRQ\n");
+		return IRQ_NONE; /*TODO*/
+
+	case MACEISA_AUDIO2_DMAT_IRQ:
+		if (snd_mace_audio_dma_push(chip, 1))
+			snd_pcm_period_elapsed(chip->channel[1].substream);
+		break;
+
+	case MACEISA_AUDIO2_MERR_IRQ:
+		printk("Got MACEISA AUDIO2 MERR IRQ\n");
+		return IRQ_NONE; /*TODO*/
+
+	case MACEISA_AUDIO3_DMAT_IRQ:
+		if (snd_mace_audio_dma_push(chip, 2))
+			snd_pcm_period_elapsed(chip->channel[2].substream);
+		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 snd_mace_audio_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 & MACE_AUDIO_CTRL_VOL_BUTTON_UP) >0) {
+		status &= ~MACE_AUDIO_CTRL_VOL_BUTTON_UP;
+		writeq(status, &mace->perif.audio.control);
+
+		if (100 > lvol) lvol++;
+		if (100 > rvol) rvol++;
+	}
+
+	if ((status & MACE_AUDIO_CTRL_VOL_BUTTON_DOWN) >0) {
+		status &= ~MACE_AUDIO_CTRL_VOL_BUTTON_DOWN;
+		writeq(status, &mace->perif.audio.control);
+
+		if (0 < lvol) lvol--;
+		if (0 < rvol) rvol--;
+	}
+
+	ad1843_set_gain(chip->ad1843, AD1843_GAIN_PCM_0,
+		((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);
+
+}
+
+/******************** Proc file, Mixer and controls ********************/
+
+static int snd_mace_audio_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 snd_mace_audio_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 snd_mace_audio_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 snd_mace_audio_control_new(snd_mace_audio_t *chip)
+{
+	int err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&snd_mace_audio_ctrl_reclevel, chip));
+	if (0 > err) return err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&snd_mace_audio_ctrl_line, chip));
+	if (0 > err) return err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&snd_mace_audio_ctrl_cd, chip));
+	if (0 > err) return err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&snd_mace_audio_ctrl_mic, chip));
+	if (0 > err) return err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&snd_mace_audio_ctrl_pcm0, chip));
+	if (0 > err) return err;
+
+	err = snd_ctl_add(chip->card,
+		snd_ctl_new1(&snd_mace_audio_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;
+}
+
+static void snd_mace_audio_proc_read
+	(snd_info_entry_t *proc_entry, snd_info_buffer_t *buffer)
+{
+	snd_mace_audio_proc_entry_t *entry;
+
+	snd_iprintf(buffer, "MACE Audio debug info.\n");
+
+	list_for_each_entry(entry, &snd_mace_audio_proc_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 snd_mace_audio_proc_entry_t* snd_mace_audio_proc_entry_append
+	(char *name)
+{
+	snd_mace_audio_proc_entry_t *entry =
+		kzalloc(sizeof(snd_mace_audio_proc_entry_t), GFP_ATOMIC);
+
+	entry->name = name;
+
+	list_add_tail(&entry->link, &snd_mace_audio_proc_list);
+
+	return entry;
+
+}
+
+/******************************* ALSA setup **************************/
+
+/* chip specific destructor */
+static int snd_mace_audio_free(snd_mace_audio_t *chip)
+{
+	int irq;
+
+printk("snd_mace_audio_free called\n");
+
+	/* reset the interface */
+	writeq(MACE_AUDIO_CTRL_RESET, &mace->perif.audio.control);
+	udelay(1);
+	writeq(0L, &mace->perif.audio.control);
+
+	/* undo DMA ? */
+
+	/* 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_card_free(chip->card);
+	snd_mace_audio_chip = NULL;
+
+	return 0;
+}
+
+/* chip specific constructor */
+static int __devinit snd_mace_audio_create
+	(snd_card_t *card, snd_mace_audio_t **rchip)
+{
+	snd_mace_audio_t *chip;
+	int err, irq;
+	unsigned long mace_reg;
+
+	*rchip = NULL;
+	chip = card->private_data;
+	chip->card = card;
+
+	/* DMA ring buffer */
+	chip->ring_base = dma_alloc_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
+		&chip->ring_base_handle, 0);
+	err = (NULL == chip->ring_base)?-ENOMEM:0; /* ugly */
+	if (0> err) goto ERROR_EXIT;
+
+printk("allocated ring base. CPU addr=0x%lX, Bus addr=0x%lX\n",
+	chip->ring_base, chip->ring_base_handle);
+
+	/* Mirror ring buffer arrangement for alsa in the hope to one day
+	 * remove the intermediate step (dma push) and have alsa write to
+	 * the ring buffer
+	 * junk: no real hw at this point */
+/*	chip->alsa_dma_base = kzalloc(3*(MACE_AUDIO_RING_SIZE), 0);
+	err = (NULL == chip->alsa_dma_base)?-ENOMEM:0; *//* ugly */
+/*	if (0> err) goto ERROR_EXIT;*/
+
+	/* See if this will be DMA-able for Mace */
+	/*junk = dma_map_single(NULL, chip->alsa_dma_base,
+		3*MACE_AUDIO_RING_SIZE, PCI_DMA_BIDIRECTIONAL);*/
+/*
+	chip->alsa_dma_base = dma_alloc_coherent(NULL, 3*MACE_AUDIO_RING_SIZE,
+		&junk, GFP_USER|__GFP_DMA);*/
+
+printk("sizeof void*=%ld, unsigned long=%ld, int=%ld, "
+	"u32=%ld, u32*=%ld, u64=%ld, u64*=%ld\n",
+	sizeof(void*), sizeof(unsigned long), sizeof(int),
+	sizeof(u32), sizeof(u32*), sizeof(u64), sizeof(u64*));
+
+	/* 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*))
+				&snd_mace_audio_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 & MACE_AUDIO_CTRL_CODEC_PRESENT)>0? 0 : -ENOENT;
+	if (0 > err) goto ERROR_EXIT;
+
+	/* reset the interface and ad1843 */
+	writeq(MACE_AUDIO_CTRL_RESET, &mace->perif.audio.control);
+	udelay(10);
+	writeq(0L, &mace->perif.audio.control);
+
+	/* set ring base */
+	writeq(chip->ring_base_handle, &mace->perif.ctrl.ringbase);
+	mb();
+
+	/* 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");
+	snd_mace_audio_free(chip);
+	return err;
+}
+
+/* constructer */
+static int __devinit snd_mace_audio_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 = snd_mace_audio_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 = snd_mace_audio_pcm_new(chip);
+	if (0 > err) goto ERROR_EXIT;
+
+	err = snd_mace_audio_control_new(chip);
+	if (0 > err) goto ERROR_EXIT;
+
+	err = snd_card_proc_new(card, "debug", &chip->proc);
+	if (0 > err) goto ERROR_EXIT;
+
+	snd_info_set_text_ops(chip->proc, chip, snd_mace_audio_proc_read);
+
+	/* 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(MACE_AUDIO_CODEC_READ(reg),
+		&mace->perif.audio.codec_control);
+	mb();
+
+	check = readq(&mace->perif.audio.codec_control); /* flush bus */
+	if ((MACE_AUDIO_CODEC_READ(reg)) != check)
+		printk("MACE Audio reg read hic-up sent %X, got %X\n",
+			(MACE_AUDIO_CODEC_READ(reg)), check);
+
+	udelay(200);
+	val = readw(&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(MACE_AUDIO_CODEC_WRITE(reg, word),
+		&mace->perif.audio.codec_control);
+	mb();
+
+	check = readq(&mace->perif.audio.codec_control); /* flush bus */
+	if ((MACE_AUDIO_CODEC_WRITE(reg, word)) != check)
+		printk("MACE Audio reg write hic-up sent %X, got %X\n",
+			(MACE_AUDIO_CODEC_WRITE(reg, word)), check);
+
+	udelay(200);
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+
+	return 0;
+}
+
+/*********************** module setup ****************************/
+
+
+static int __init snd_card_mace_audio_init(void)
+{
+	int err;
+
+	err = snd_mace_audio_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_mace_audio_free(snd_mace_audio_chip);
+}
+
+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)
+

diff -Nu linux-2.6.19.7.b/sound/mips/Makefile linux-2.6.19.7/sound/mips/Makefile
--- linux-2.6.19.7.b/sound/mips/Makefile	2007-03-10 12:11:23.000000000 +0000
+++ linux-2.6.19.7/sound/mips/Makefile	2007-03-12 13:33:09.000000000 +0000
@@ -3,6 +3,9 @@
 #
 
 snd-au1x00-objs := au1x00.o
+snd-mace-audio-objs := mace_audio.o ad1843.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
+obj-$(CONFIG_SND_MACE_AUDIO) += snd-mace-audio.o
+

diff -Nu linux-2.6.19.7.b/include/sound/ad1843.h linux-2.6.19.7/include/sound/ad1843.h
--- linux-2.6.19.7.b/include/sound/ad1843.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.19.7/include/sound/ad1843.h	2007-03-12 13:35:13.000000000 +0000
@@ -0,0 +1,47 @@
+/*
+ * 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);
+
+#endif /* __SOUND_AD1843_H */

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

  Powered by Linux