Hi,

after three days of fiddling with hardware registers, I managed to make
it sound!
I'll work with Iain Sandoe to clean it up and merge it into the
mainstream kernel.

For the really impatient:
- use Ben Herrenschidt's 2.2.17pre10-ben2 kernel source
- apply Iain's dmasound patch
- replace drivers/sound/dmasound/dmasound_awacs.c and .../awacs_defs.h
with the attached versions

Note that this contains ugly hacks, lots of debugging code, may crash on
any machine other than Pismo, ... you got the picture.

Matthias

-- 
Matthias Pfisterer      <mailto:[EMAIL PROTECTED]>

        Share your knowledge.
        It's a way to achieve immortality.

                 (from a nepalese mantra)

Java Sound Examples:
http://rupert.informatik.uni-stuttgart.de/~pfistere/jsexamples/
Tritonus, the open source implementation of the Java Sound API:
http://tritonus.sourceforge.net/
--------------------------------------------------------------
/*
 *  linux/drivers/sound/dmasound/dmasound_awacs.c
 *
 *  PowerMac `AWACS' and `Burgundy' DMA Sound Driver
 *
 *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
 */
#include <linux/module.h>
#include <linux/config.h>
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/soundcard.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
#define COMPILING_2_4_X 1
#endif

#include <linux/nvram.h>
#include <linux/vt_kern.h>

#ifdef COMPILING_2_4_X
#include <linux/adb.h>
#include <linux/cuda.h>
#include <linux/pmu.h>
#else
#include <asm/adb.h>
#include <asm/cuda.h>
#include <asm/pmu.h>
#endif

#include <asm/uaccess.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/dbdma.h>
#include <asm/feature.h>
#include <asm/irq.h>

#include "awacs_defs.h"
#include "dmasound.h"

#ifdef COMPILING_2_4_X
/* 
 * reference to the CUDA controller type 
*/
extern sys_ctrler_t sys_ctrler ;
#endif

/*
 * Interrupt numbers and addresses, obtained from the device tree.
 */
static int awacs_irq, awacs_tx_irq, awacs_rx_irq;
static volatile struct awacs_regs *awacs;
static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma;
static int awacs_rate_index;
static int awacs_subframe;
static int awacs_spkr_vol;
static struct device_node* awacs_node;

static char awacs_name[64];
static int awacs_revision;
#define AWACS_BURGUNDY  100             /* fake revision # for burgundy */

/*
 * Space for the DBDMA command blocks.
 */
static void *awacs_tx_cmd_space;
static volatile struct dbdma_cmd *awacs_tx_cmds;

static void *awacs_rx_cmd_space;
static volatile struct dbdma_cmd *awacs_rx_cmds;

/*
 * Cached values of AWACS registers (we can't read them).
 * Except on the burgundy. XXX
 */
// screamer has additional registers 5, 6 and 7.
int awacs_reg[8];
int     is_screamer = 1;

#define HAS_16BIT_TABLES
#undef HAS_8BIT_TABLES

/*
 * Stuff for outputting a beep.  The values range from -327 to +327
 * so we can multiply by an amplitude in the range 0..100 to get a
 * signed short value to put in the output buffer.
 */
static short beep_wform[256] = {
        0,      40,     79,     117,    153,    187,    218,    245,
        269,    288,    304,    316,    323,    327,    327,    324,
        318,    310,    299,    288,    275,    262,    249,    236,
        224,    213,    204,    196,    190,    186,    183,    182,
        182,    183,    186,    189,    192,    196,    200,    203,
        206,    208,    209,    209,    209,    207,    204,    201,
        197,    193,    188,    183,    179,    174,    170,    166,
        163,    161,    160,    159,    159,    160,    161,    162,
        164,    166,    168,    169,    171,    171,    171,    170,
        169,    167,    163,    159,    155,    150,    144,    139,
        133,    128,    122,    117,    113,    110,    107,    105,
        103,    103,    103,    103,    104,    104,    105,    105,
        105,    103,    101,    97,     92,     86,     78,     68,
        58,     45,     32,     18,     3,      -11,    -26,    -41,
        -55,    -68,    -79,    -88,    -95,    -100,   -102,   -102,
        -99,    -93,    -85,    -75,    -62,    -48,    -33,    -16,
        0,      16,     33,     48,     62,     75,     85,     93,
        99,     102,    102,    100,    95,     88,     79,     68,
        55,     41,     26,     11,     -3,     -18,    -32,    -45,
        -58,    -68,    -78,    -86,    -92,    -97,    -101,   -103,
        -105,   -105,   -105,   -104,   -104,   -103,   -103,   -103,
        -103,   -105,   -107,   -110,   -113,   -117,   -122,   -128,
        -133,   -139,   -144,   -150,   -155,   -159,   -163,   -167,
        -169,   -170,   -171,   -171,   -171,   -169,   -168,   -166,
        -164,   -162,   -161,   -160,   -159,   -159,   -160,   -161,
        -163,   -166,   -170,   -174,   -179,   -183,   -188,   -193,
        -197,   -201,   -204,   -207,   -209,   -209,   -209,   -208,
        -206,   -203,   -200,   -196,   -192,   -189,   -186,   -183,
        -182,   -182,   -183,   -186,   -190,   -196,   -204,   -213,
        -224,   -236,   -249,   -262,   -275,   -288,   -299,   -310,
        -318,   -324,   -327,   -327,   -323,   -316,   -304,   -288,
        -269,   -245,   -218,   -187,   -153,   -117,   -79,    -40,
};

#define BEEP_SRATE      22050   /* 22050 Hz sample rate */
#define BEEP_BUFLEN     512
#define BEEP_VOLUME     15      /* 0 - 100 */

static int beep_volume = BEEP_VOLUME;
static int beep_playing = 0;
static int awacs_beep_state = 0;
static short *beep_buf;
static volatile struct dbdma_cmd *beep_dbdma_cmd;
static void (*orig_mksound)(unsigned int, unsigned int);
static int is_pbook_3400;
static unsigned char *latch_base;
static int is_pbook_G3;
static unsigned char *macio_base;

/* Burgundy functions */
static void awacs_burgundy_wcw(unsigned addr,unsigned newval);
static unsigned awacs_burgundy_rcw(unsigned addr);
static void awacs_burgundy_write_volume(unsigned address, int volume);
static int awacs_burgundy_read_volume(unsigned address);
static void awacs_burgundy_write_mvolume(unsigned address, int volume);
static int awacs_burgundy_read_mvolume(unsigned address);

#ifdef CONFIG_PMAC_PBOOK
/*
 * Stuff for restoring after a sleep.
 */
static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when);
struct pmu_sleep_notifier awacs_sleep_notifier = {
        awacs_sleep_notify, SLEEP_LEVEL_SOUND,
};
#endif /* CONFIG_PMAC_PBOOK */

static int expand_bal;  /* Balance factor for expanding (not volume!) */
static int expand_data; /* Data for expanding */


/*** Translations ************************************************************/


/* ++TeSche: radically changed for new expanding purposes...
 *
 * These two routines now deal with copying/expanding/translating the samples
 * from user space into our buffer at the right frequency. They take care about
 * how much data there's actually to read, how much buffer space there is and
 * to convert samples into the right frequency/encoding. They will only work on
 * complete samples so it may happen they leave some bytes in the input stream
 * if the user didn't write a multiple of the current sample size. They both
 * return the number of bytes they've used from both streams so you may detect
 * such a situation. Luckily all programs should be able to cope with that.
 *
 * I think I've optimized anything as far as one can do in plain C, all
 * variables should fit in registers and the loops are really short. There's
 * one loop for every possible situation. Writing a more generalized and thus
 * parameterized loop would only produce slower code. Feel free to optimize
 * this in assembler if you like. :)
 *
 * I think these routines belong here because they're not yet really hardware
 * independent, especially the fact that the Falcon can play 16bit samples
 * only in stereo is hardcoded in both of them!
 *
 * ++geert: split in even more functions (one per format)
 */

static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft);
static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
                          u_char frame[], ssize_t *frameUsed,
                          ssize_t frameLeft);
static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
                          u_char frame[], ssize_t *frameUsed,
                          ssize_t frameLeft);
static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft);
static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft);
static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
                            u_char frame[], ssize_t *frameUsed,
                            ssize_t frameLeft);
static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft);
static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft);
static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
                            u_char frame[], ssize_t *frameUsed,
                            ssize_t frameLeft);
static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
                            u_char frame[], ssize_t *frameUsed,
                            ssize_t frameLeft);
static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft);
static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft);


/*** Low level stuff *********************************************************/


static void PMacOpen(void);
static void PMacRelease(void);
static void *PMacAlloc(unsigned int size, int flags);
static void PMacFree(void *ptr, unsigned int size);
static int PMacIrqInit(void);
#ifdef MODULE
static void PMacIrqCleanup(void);
#endif
static void PMacSilence(void);
static void PMacInit(void);
static int PMacSetFormat(int format);
static int PMacSetVolume(int volume);
static void PMacPlay(void);
static void PMacRecord(void);
static void pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs);
static void pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs);
static void pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs);
static void awacs_write(int val);
static int awacs_get_volume(int reg, int lshift);
static int awacs_volume_setter(int volume, int n, int mute, int lshift);
static void awacs_mksound(unsigned int hz, unsigned int ticks);
static void awacs_nosound(unsigned long xx);


/*** Mid level stuff **********************************************************/


static int PMacMixerIoctl(u_int cmd, u_long arg);
static void PMacWriteSqSetup(void);
static void PMacReadSqSetup(void);
static void PMacAbortRead(void);


/*** Translations ************************************************************/


static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft)
{
        short *table = dmasound.soft.format == AFMT_MU_LAW
                ? dmasound_ulaw2dma16 : dmasound_alaw2dma16;
        ssize_t count, used;
        short *p = (short *) &frame[*frameUsed];
        int val, stereo = dmasound.soft.stereo;

        frameLeft >>= 2;
        if (stereo)
                userCount >>= 1;
        used = count = min(userCount, frameLeft);
        while (count > 0) {
                u_char data;
                if (get_user(data, userPtr++))
                        return -EFAULT;
                val = table[data];
                *p++ = val;
                if (stereo) {
                        if (get_user(data, userPtr++))
                                return -EFAULT;
                        val = table[data];
                }
                *p++ = val;
                count--;
        }
        *frameUsed += used * 4;
        return stereo? used * 2: used;
}


static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
                          u_char frame[], ssize_t *frameUsed,
                          ssize_t frameLeft)
{
        ssize_t count, used;
        short *p = (short *) &frame[*frameUsed];
        int val, stereo = dmasound.soft.stereo;

        frameLeft >>= 2;
        if (stereo)
                userCount >>= 1;
        used = count = min(userCount, frameLeft);
        while (count > 0) {
                u_char data;
                if (get_user(data, userPtr++))
                        return -EFAULT;
                val = data << 8;
                *p++ = val;
                if (stereo) {
                        if (get_user(data, userPtr++))
                                return -EFAULT;
                        val = data << 8;
                }
                *p++ = val;
                count--;
        }
        *frameUsed += used * 4;
        return stereo? used * 2: used;
}


static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
                          u_char frame[], ssize_t *frameUsed,
                          ssize_t frameLeft)
{
        ssize_t count, used;
        short *p = (short *) &frame[*frameUsed];
        int val, stereo = dmasound.soft.stereo;

        frameLeft >>= 2;
        if (stereo)
                userCount >>= 1;
        used = count = min(userCount, frameLeft);
        while (count > 0) {
                u_char data;
                if (get_user(data, userPtr++))
                        return -EFAULT;
                val = (data ^ 0x80) << 8;
                *p++ = val;
                if (stereo) {
                        if (get_user(data, userPtr++))
                                return -EFAULT;
                        val = (data ^ 0x80) << 8;
                }
                *p++ = val;
                count--;
        }
        *frameUsed += used * 4;
        return stereo? used * 2: used;
}


static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft)
{
        ssize_t count, used;
        int stereo = dmasound.soft.stereo;
        short *fp = (short *) &frame[*frameUsed];

        frameLeft >>= 2;
        userCount >>= (stereo? 2: 1);
        used = count = min(userCount, frameLeft);
        if (!stereo) {
                short *up = (short *) userPtr;
                while (count > 0) {
                        short data;
                        if (get_user(data, up++))
                                return -EFAULT;
                        *fp++ = data;
                        *fp++ = data;
                        count--;
                }
        } else {
                if (copy_from_user(fp, userPtr, count * 4))
                        return -EFAULT;
        }
        *frameUsed += used * 4;
        return stereo? used * 4: used * 2;
}

static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft)
{
        ssize_t count, used;
        int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
        int stereo = dmasound.soft.stereo;
        short *fp = (short *) &frame[*frameUsed];
        short *up = (short *) userPtr;

        frameLeft >>= 2;
        userCount >>= (stereo? 2: 1);
        used = count = min(userCount, frameLeft);
        while (count > 0) {
                int data;
                if (get_user(data, up++))
                        return -EFAULT;
                data ^= mask;
                *fp++ = data;
                if (stereo) {
                        if (get_user(data, up++))
                                return -EFAULT;
                        data ^= mask;
                }
                *fp++ = data;
                count--;
        }
        *frameUsed += used * 4;
        return stereo? used * 4: used * 2;
}


static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
                            u_char frame[], ssize_t *frameUsed,
                            ssize_t frameLeft)
{
        unsigned short *table = (unsigned short *)
                (dmasound.soft.format == AFMT_MU_LAW
                 ? dmasound_ulaw2dma16 : dmasound_alaw2dma16);
        unsigned int data = expand_data;
        unsigned int *p = (unsigned int *) &frame[*frameUsed];
        int bal = expand_bal;
        int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
        int utotal, ftotal;
        int stereo = dmasound.soft.stereo;

        frameLeft >>= 2;
        if (stereo)
                userCount >>= 1;
        ftotal = frameLeft;
        utotal = userCount;
        while (frameLeft) {
                u_char c;
                if (bal < 0) {
                        if (userCount == 0)
                                break;
                        if (get_user(c, userPtr++))
                                return -EFAULT;
                        data = table[c];
                        if (stereo) {
                                if (get_user(c, userPtr++))
                                        return -EFAULT;
                                data = (data << 16) + table[c];
                        } else
                                data = (data << 16) + data;
                        userCount--;
                        bal += hSpeed;
                }
                *p++ = data;
                frameLeft--;
                bal -= sSpeed;
        }
        expand_bal = bal;
        expand_data = data;
        *frameUsed += (ftotal - frameLeft) * 4;
        utotal -= userCount;
        return stereo? utotal * 2: utotal;
}


static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft)
{
        unsigned int *p = (unsigned int *) &frame[*frameUsed];
        unsigned int data = expand_data;
        int bal = expand_bal;
        int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
        int stereo = dmasound.soft.stereo;
        int utotal, ftotal;

        frameLeft >>= 2;
        if (stereo)
                userCount >>= 1;
        ftotal = frameLeft;
        utotal = userCount;
        while (frameLeft) {
                u_char c;
                if (bal < 0) {
                        if (userCount == 0)
                                break;
                        if (get_user(c, userPtr++))
                                return -EFAULT;
                        data = c << 8;
                        if (stereo) {
                                if (get_user(c, userPtr++))
                                        return -EFAULT;
                                data = (data << 16) + (c << 8);
                        } else
                                data = (data << 16) + data;
                        userCount--;
                        bal += hSpeed;
                }
                *p++ = data;
                frameLeft--;
                bal -= sSpeed;
        }
        expand_bal = bal;
        expand_data = data;
        *frameUsed += (ftotal - frameLeft) * 4;
        utotal -= userCount;
        return stereo? utotal * 2: utotal;
}


static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft)
{
        unsigned int *p = (unsigned int *) &frame[*frameUsed];
        unsigned int data = expand_data;
        int bal = expand_bal;
        int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
        int stereo = dmasound.soft.stereo;
        int utotal, ftotal;

        frameLeft >>= 2;
        if (stereo)
                userCount >>= 1;
        ftotal = frameLeft;
        utotal = userCount;
        while (frameLeft) {
                u_char c;
                if (bal < 0) {
                        if (userCount == 0)
                                break;
                        if (get_user(c, userPtr++))
                                return -EFAULT;
                        data = (c ^ 0x80) << 8;
                        if (stereo) {
                                if (get_user(c, userPtr++))
                                        return -EFAULT;
                                data = (data << 16) + ((c ^ 0x80) << 8);
                        } else
                                data = (data << 16) + data;
                        userCount--;
                        bal += hSpeed;
                }
                *p++ = data;
                frameLeft--;
                bal -= sSpeed;
        }
        expand_bal = bal;
        expand_data = data;
        *frameUsed += (ftotal - frameLeft) * 4;
        utotal -= userCount;
        return stereo? utotal * 2: utotal;
}


static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
                            u_char frame[], ssize_t *frameUsed,
                            ssize_t frameLeft)
{
        unsigned int *p = (unsigned int *) &frame[*frameUsed];
        unsigned int data = expand_data;
        unsigned short *up = (unsigned short *) userPtr;
        int bal = expand_bal;
        int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
        int stereo = dmasound.soft.stereo;
        int utotal, ftotal;

        frameLeft >>= 2;
        userCount >>= (stereo? 2: 1);
        ftotal = frameLeft;
        utotal = userCount;
        while (frameLeft) {
                unsigned short c;
                if (bal < 0) {
                        if (userCount == 0)
                                break;
                        if (get_user(data, up++))
                                return -EFAULT;
                        if (stereo) {
                                if (get_user(c, up++))
                                        return -EFAULT;
                                data = (data << 16) + c;
                        } else
                                data = (data << 16) + data;
                        userCount--;
                        bal += hSpeed;
                }
                *p++ = data;
                frameLeft--;
                bal -= sSpeed;
        }
        expand_bal = bal;
        expand_data = data;
        *frameUsed += (ftotal - frameLeft) * 4;
        utotal -= userCount;
        return stereo? utotal * 4: utotal * 2;
}


static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
                            u_char frame[], ssize_t *frameUsed,
                            ssize_t frameLeft)
{
        int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
        unsigned int *p = (unsigned int *) &frame[*frameUsed];
        unsigned int data = expand_data;
        unsigned short *up = (unsigned short *) userPtr;
        int bal = expand_bal;
        int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
        int stereo = dmasound.soft.stereo;
        int utotal, ftotal;

        frameLeft >>= 2;
        userCount >>= (stereo? 2: 1);
        ftotal = frameLeft;
        utotal = userCount;
        while (frameLeft) {
                unsigned short c;
                if (bal < 0) {
                        if (userCount == 0)
                                break;
                        if (get_user(data, up++))
                                return -EFAULT;
                        data ^= mask;
                        if (stereo) {
                                if (get_user(c, up++))
                                        return -EFAULT;
                                data = (data << 16) + (c ^ mask);
                        } else
                                data = (data << 16) + data;
                        userCount--;
                        bal += hSpeed;
                }
                *p++ = data;
                frameLeft--;
                bal -= sSpeed;
        }
        expand_bal = bal;
        expand_data = data;
        *frameUsed += (ftotal - frameLeft) * 4;
        utotal -= userCount;
        return stereo? utotal * 4: utotal * 2;
}

static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount,
                          u_char frame[], ssize_t *frameUsed,
                          ssize_t frameLeft)
{
        ssize_t count, used;
        short *p = (short *) &frame[*frameUsed];
        int val, stereo = dmasound.soft.stereo;

        frameLeft >>= 2;
        if (stereo)
                userCount >>= 1;
        used = count = min(userCount, frameLeft);
        while (count > 0) {
                u_char data;

                val = *p++;
                data = val >> 8;
                if (put_user(data, (u_char *)userPtr++))
                        return -EFAULT;
                if (stereo) {
                        val = *p;
                        data = val >> 8;
                        if (put_user(data, (u_char *)userPtr++))
                                return -EFAULT;
                }
                p++;
                count--;
        }
        *frameUsed += used * 4;
        return stereo? used * 2: used;
}


static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount,
                          u_char frame[], ssize_t *frameUsed,
                          ssize_t frameLeft)
{
        ssize_t count, used;
        short *p = (short *) &frame[*frameUsed];
        int val, stereo = dmasound.soft.stereo;

        frameLeft >>= 2;
        if (stereo)
                userCount >>= 1;
        used = count = min(userCount, frameLeft);
        while (count > 0) {
                u_char data;

                val = *p++;
                data = (val >> 8) ^ 0x80;
                if (put_user(data, (u_char *)userPtr++))
                        return -EFAULT;
                if (stereo) {
                        val = *p;
                        data = (val >> 8) ^ 0x80;
                        if (put_user(data, (u_char *)userPtr++))
                                return -EFAULT;
                }
                p++;
                count--;
        }
        *frameUsed += used * 4;
        return stereo? used * 2: used;
}


static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft)
{
        ssize_t count, used;
        int stereo = dmasound.soft.stereo;
        short *fp = (short *) &frame[*frameUsed];

        frameLeft >>= 2;
        userCount >>= (stereo? 2: 1);
        used = count = min(userCount, frameLeft);
        if (!stereo) {
                short *up = (short *) userPtr;
                while (count > 0) {
                        short data;
                        data = *fp;
                        if (put_user(data, up++))
                                return -EFAULT;
                        fp+=2;
                        count--;
                }
        } else {
                if (copy_to_user((u_char *)userPtr, fp, count * 4))
                        return -EFAULT;
        }
        *frameUsed += used * 4;
        return stereo? used * 4: used * 2;
}

static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
                           u_char frame[], ssize_t *frameUsed,
                           ssize_t frameLeft)
{
        ssize_t count, used;
        int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
        int stereo = dmasound.soft.stereo;
        short *fp = (short *) &frame[*frameUsed];
        short *up = (short *) userPtr;

        frameLeft >>= 2;
        userCount >>= (stereo? 2: 1);
        used = count = min(userCount, frameLeft);
        while (count > 0) {
                int data;

                data = *fp++;
                data ^= mask;
                if (put_user(data, up++))
                        return -EFAULT;
                if (stereo) {
                        data = *fp;
                        data ^= mask;
                        if (put_user(data, up++))
                                return -EFAULT;
                }
                fp++;
                count--;
        }
        *frameUsed += used * 4;
        return stereo? used * 4: used * 2;
}


static TRANS transAwacsNormal = {
        ct_ulaw:        pmac_ct_law,
        ct_alaw:        pmac_ct_law,
        ct_s8:          pmac_ct_s8,
        ct_u8:          pmac_ct_u8,
        ct_s16be:       pmac_ct_s16,
        ct_u16be:       pmac_ct_u16,
        ct_s16le:       pmac_ct_s16,
        ct_u16le:       pmac_ct_u16,
};

static TRANS transAwacsExpand = {
        ct_ulaw:        pmac_ctx_law,
        ct_alaw:        pmac_ctx_law,
        ct_s8:          pmac_ctx_s8,
        ct_u8:          pmac_ctx_u8,
        ct_s16be:       pmac_ctx_s16,
        ct_u16be:       pmac_ctx_u16,
        ct_s16le:       pmac_ctx_s16,
        ct_u16le:       pmac_ctx_u16,
};

static TRANS transAwacsNormalRead = {
        ct_s8:          pmac_ct_s8_read,
        ct_u8:          pmac_ct_u8_read,
        ct_s16be:       pmac_ct_s16_read,
        ct_u16be:       pmac_ct_u16_read,
        ct_s16le:       pmac_ct_s16_read,
        ct_u16le:       pmac_ct_u16_read,
};

/*** Low level stuff *********************************************************/



/*
 * PCI PowerMac, with AWACS and DBDMA.
 */

static void PMacOpen(void)
{
        MOD_INC_USE_COUNT;
}

static void PMacRelease(void)
{
        MOD_DEC_USE_COUNT;
}

static void *PMacAlloc(unsigned int size, int flags)
{
        return kmalloc(size, flags);
}

static void PMacFree(void *ptr, unsigned int size)
{
        kfree(ptr);
}

static int __init PMacIrqInit(void)
{
        if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0)
            || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0)
            || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "AWACS in", 0))
                return 0;
        return 1;
}

#ifdef MODULE
static void PMacIrqCleanup(void)
{
        /* turn off output dma */
        out_le32(&awacs_txdma->control, RUN<<16);
        /* disable interrupts from awacs interface */
        out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff);
#ifdef CONFIG_PMAC_PBOOK
        if (is_pbook_G3) {
                feature_clear(awacs_node, FEATURE_Sound_power);
                feature_clear(awacs_node, FEATURE_Sound_CLK_enable);
        }
#endif
        free_irq(awacs_irq, 0);
        free_irq(awacs_tx_irq, 0);
        free_irq(awacs_rx_irq, 0);
        kfree(awacs_tx_cmd_space);
        if (awacs_rx_cmd_space)
                kfree(awacs_rx_cmd_space);
        if (beep_buf)
                kfree(beep_buf);
        kd_mksound = orig_mksound;
#ifdef CONFIG_PMAC_PBOOK
        pmu_unregister_sleep_notifier(&awacs_sleep_notifier);
#endif
}
#endif /* MODULE */

static void PMacSilence(void)
{
        /* turn off output dma */
        out_le32(&awacs_txdma->control, RUN<<16);
}

static int awacs_freqs[8] = {
        44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350
};
static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };

static void PMacInit(void)
{
        int i, tolerance;

        switch (dmasound.soft.format) {
        case AFMT_S16_LE:
        case AFMT_U16_LE:
                dmasound.hard.format = AFMT_S16_LE;
                break;
        default:
                dmasound.hard.format = AFMT_S16_BE;
                break;
        }
        dmasound.hard.stereo = 1;
        dmasound.hard.size = 16;

        /*
         * If we have a sample rate which is within catchRadius percent
         * of the requested value, we don't have to expand the samples.
         * Otherwise choose the next higher rate.
         * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz.
         */
        i = 8;
        do {
                tolerance = catchRadius * awacs_freqs[--i] / 100;
                if (awacs_freqs_ok[i]
                    && dmasound.soft.speed <= awacs_freqs[i] + tolerance)
                        break;
        } while (i > 0);
        if (dmasound.soft.speed >= awacs_freqs[i] - tolerance)
                dmasound.trans_write = &transAwacsNormal;
        else
                dmasound.trans_write = &transAwacsExpand;
        dmasound.trans_read = &transAwacsNormalRead;
        dmasound.hard.speed = awacs_freqs[i];
        awacs_rate_index = i;

        /* XXX disable error interrupt on burgundy for now */
        out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11
                 | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
        awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3);
        awacs_write(awacs_reg[1] | MASK_ADDR1);
        out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);

        /* We really want to execute a DMA stop command, after the AWACS
         * is initialized.
         * For reasons I don't understand, it stops the hissing noise
         * common to many PowerBook G3 systems (like mine :-).
         */
        out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
        st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
        out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
        out_le32(&awacs_txdma->control, RUN | (RUN << 16));

        expand_bal = -dmasound.soft.speed;
}

static int PMacSetFormat(int format)
{
        int size;

        switch (format) {
        case AFMT_QUERY:
                return dmasound.soft.format;
        case AFMT_MU_LAW:
        case AFMT_A_LAW:
        case AFMT_U8:
        case AFMT_S8:
                size = 8;
                break;
        case AFMT_S16_BE:
        case AFMT_U16_BE:
        case AFMT_S16_LE:
        case AFMT_U16_LE:
                size = 16;
                break;
        default: /* :-) */
                printk(KERN_ERR "dmasound: unknown format 0x%x, using 
AFMT_U8\n",
                       format);
                size = 8;
                format = AFMT_U8;
        }

        dmasound.soft.format = format;
        dmasound.soft.size = size;
        if (dmasound.minDev == SND_DEV_DSP) {
                dmasound.dsp.format = format;
                dmasound.dsp.size = size;
        }

        PMacInit();

        return format;
}

#define AWACS_VOLUME_TO_MASK(x) (15 - ((((x) - 1) * 15) / 99))
#define AWACS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 15))

static int awacs_get_volume(int reg, int lshift)
{
        int volume;

        volume = AWACS_MASK_TO_VOLUME((reg >> lshift) & 0xf);
        volume |= AWACS_MASK_TO_VOLUME(reg & 0xf) << 8;
        return volume;
}

static int awacs_volume_setter(int volume, int n, int mute, int lshift)
{
        int r1, rn;

        if (mute && volume == 0) {
                r1 = awacs_reg[1] | mute;
        } else {
                r1 = awacs_reg[1] & ~mute;
                rn = awacs_reg[n] & ~(0xf | (0xf << lshift));
                rn |= ((AWACS_VOLUME_TO_MASK(volume & 0xff) & 0xf) << lshift);
                rn |= AWACS_VOLUME_TO_MASK((volume >> 8) & 0xff) & 0xf;
                awacs_reg[n] = rn;
                awacs_write((n << 12) | rn);
                volume = awacs_get_volume(rn, lshift);
        }
        if (r1 != awacs_reg[1]) {
                awacs_reg[1] = r1;
                awacs_write(r1 | MASK_ADDR1);
        }
        return volume;
}

static int PMacSetVolume(int volume)
{
        return awacs_volume_setter(volume, 2, MASK_AMUTE, 6);
}

static void PMacPlay(void)
{
        volatile struct dbdma_cmd *cp;
        int i, count;
        unsigned long flags;

        save_flags(flags); cli();
        if (awacs_beep_state) {
                /* sound takes precedence over beeps */
                out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
                out_le32(&awacs->control,
                         (in_le32(&awacs->control) & ~0x1f00)
                         | (awacs_rate_index << 8));
                out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
                out_le32(&awacs_txdma->cmdptr, 
virt_to_bus(&(awacs_tx_cmds[(write_sq.front+write_sq.active) % 
write_sq.max_count])));

                beep_playing = 0;
                awacs_beep_state = 0;
        }
        i = write_sq.front + write_sq.active;
        if (i >= write_sq.max_count)
                i -= write_sq.max_count;
        while (write_sq.active < 2 && write_sq.active < write_sq.count) {
                count = (write_sq.count == write_sq.active + 
1)?write_sq.rear_size:write_sq.block_size;
                if (count < write_sq.block_size && !write_sq.syncing)
                        /* last block not yet filled, and we're not syncing. */
                        break;
                cp = &awacs_tx_cmds[i];
                st_le16(&cp->req_count, count);
                st_le16(&cp->xfer_status, 0);
                if (++i >= write_sq.max_count)
                        i = 0;
                out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP);
                out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
                if (write_sq.active == 0)
                        out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
                out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + 
(RUN|WAKE));
                ++write_sq.active;
        }
        restore_flags(flags);
}


static void PMacRecord(void)
{
        unsigned long flags;

        if (read_sq.active)
                return;

        save_flags(flags); cli();

        /* This is all we have to do......Just start it up.
        */
        out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
        read_sq.active = 1;

        restore_flags(flags);
}


static void
pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs)
{
        int i = write_sq.front;
        int stat;
        volatile struct dbdma_cmd *cp;

        while (write_sq.active > 0) {
                cp = &awacs_tx_cmds[i];
                stat = ld_le16(&cp->xfer_status);
                if ((stat & ACTIVE) == 0)
                        break;  /* this frame is still going */
                --write_sq.count;
                --write_sq.active;
                if (++i >= write_sq.max_count)
                        i = 0;
        }
        if (i != write_sq.front)
                WAKE_UP(write_sq.action_queue);
        write_sq.front = i;

        PMacPlay();

        if (!write_sq.active)
                WAKE_UP(write_sq.sync_queue);
}


static void
pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs)
{

        /* For some reason on my PowerBook G3, I get one interrupt
         * when the interrupt vector is installed (like something is
         * pending).  This happens before the dbdma is initialize by
         * us, so I just check the command pointer and if it is zero,
         * just blow it off.
         */
        if (in_le32(&awacs_rxdma->cmdptr) == 0)
                return;

        /* We also want to blow 'em off when shutting down.
        */
        if (read_sq.active == 0)
                return;

        /* Check multiple buffers in case we were held off from
         * interrupt processing for a long time.  Geeze, I really hope
         * this doesn't happen.
         */
        while (awacs_rx_cmds[read_sq.rear].xfer_status) {

                /* Clear status and move on to next buffer.
                */
                awacs_rx_cmds[read_sq.rear].xfer_status = 0;
                read_sq.rear++;

                /* Wrap the buffer ring.
                */
                if (read_sq.rear >= read_sq.max_active)
                        read_sq.rear = 0;

                /* If we have caught up to the front buffer, bump it.
                 * This will cause weird (but not fatal) results if the
                 * read loop is currently using this buffer.  The user is
                 * behind in this case anyway, so weird things are going
                 * to happen.
                 */
                if (read_sq.rear == read_sq.front) {
                        read_sq.front++;
                        if (read_sq.front >= read_sq.max_active)
                                read_sq.front = 0;
                }
        }

        WAKE_UP(read_sq.action_queue);
}


static void
pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs)
{
        int ctrl = in_le32(&awacs->control);

        if (ctrl & MASK_PORTCHG) {
                /* do something when headphone is plugged/unplugged? */
        }
        if (ctrl & MASK_CNTLERR) {
                int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16;
                if (err != 0 && awacs_revision < AWACS_BURGUNDY)
                        printk(KERN_ERR "AWACS: error %x\n", err);
        }
        /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */
        out_le32(&awacs->control, ctrl);
}

static void
awacs_write(int val)
{
        if (awacs_revision >= AWACS_BURGUNDY)
                return;
        printk(__FILE__ ": writing to codec control register: %X\n", val);
        while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
                ;       /* XXX should have timeout */
        out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22));
}

static void awacs_nosound(unsigned long xx)
{
        unsigned long flags;

        save_flags(flags); cli();
        if (beep_playing) {
                st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
                out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
                out_le32(&awacs->control,
                         (in_le32(&awacs->control) & ~0x1f00)
                         | (awacs_rate_index << 8));
                out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
                beep_playing = 0;
        }
        restore_flags(flags);
}

static struct timer_list beep_timer = {
        function: awacs_nosound
};

static void awacs_mksound(unsigned int hz, unsigned int ticks)
{
        unsigned long flags;
        int beep_speed = 0;
        int srate;
        int period, ncycles, nsamples;
        int i, j, f;
        short *p;
        static int beep_hz_cache;
        static int beep_nsamples_cache;
        static int beep_volume_cache;

        for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i)
                if (awacs_freqs_ok[i])
                        beep_speed = i;
        srate = awacs_freqs[beep_speed];

        if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) {
#if 1
                /* this is a hack for broken X server code */
                hz = 750;
                ticks = 12;
#else
                /* cancel beep currently playing */
                awacs_nosound(0);
                return;
#endif
        }
        save_flags(flags); cli();
        del_timer(&beep_timer);
        if (ticks) {
                beep_timer.expires = jiffies + ticks;
                add_timer(&beep_timer);
        }
        if (beep_playing || write_sq.active || beep_buf == NULL) {
                restore_flags(flags);
                return;         /* too hard, sorry :-( */
        }
        beep_playing = 1;
        st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS);
        restore_flags(flags);

        if (hz == beep_hz_cache && beep_volume == beep_volume_cache) {
                nsamples = beep_nsamples_cache;
        } else {
                period = srate * 256 / hz;      /* fixed point */
                ncycles = BEEP_BUFLEN * 256 / period;
                nsamples = (period * ncycles) >> 8;
                f = ncycles * 65536 / nsamples;
                j = 0;
                p = beep_buf;
                for (i = 0; i < nsamples; ++i, p += 2) {
                        p[0] = p[1] = beep_wform[j >> 8] * beep_volume;
                        j = (j + f) & 0xffff;
                }
                beep_hz_cache = hz;
                beep_volume_cache = beep_volume;
                beep_nsamples_cache = nsamples;
        }

        st_le16(&beep_dbdma_cmd->req_count, nsamples*4);
        st_le16(&beep_dbdma_cmd->xfer_status, 0);
        st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd));
        st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf));
        awacs_beep_state = 1;

        save_flags(flags); cli();
        if (beep_playing) {     /* i.e. haven't been terminated already */
                out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
                out_le32(&awacs->control,
                         (in_le32(&awacs->control) & ~0x1f00)
                         | (beep_speed << 8));
                out_le32(&awacs->byteswap, 0);
                out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
                out_le32(&awacs_txdma->control, RUN | (RUN << 16));
        }
        restore_flags(flags);
}

#ifdef CONFIG_PMAC_PBOOK
/*
 * Save state when going to sleep, restore it afterwards.
 */
static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
{
        switch (when) {
        case PBOOK_SLEEP_NOW:
                /* XXX we should stop any dma in progress when going to sleep
                   and restart it when we wake. */
                PMacSilence();
                disable_irq(awacs_irq);
                disable_irq(awacs_tx_irq);
                if (is_pbook_G3) {
                        feature_clear(awacs_node, FEATURE_Sound_CLK_enable);
                        feature_clear(awacs_node, FEATURE_Sound_power);
                }
                break;
        case PBOOK_WAKE:
                /* There is still a problem on wake. Sound seems to work fine
                   if I launch mpg123 and resumes fine if mpg123 was playing,
                   but the console beep is dead until I do something with the
                   mixer. Probably yet another timing issue */
                if (!feature_test(awacs_node, FEATURE_Sound_CLK_enable)
                    || !feature_test(awacs_node, FEATURE_Sound_power)) {
                        /* these aren't present on the 3400 AFAIK -- paulus */
                        feature_set(awacs_node, FEATURE_Sound_CLK_enable);
                        feature_set(awacs_node, FEATURE_Sound_power);
                        mdelay(1000);
                }
                out_le32(&awacs->control, MASK_IEPC
                         | (awacs_rate_index << 8) | 0x11
                         | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
                awacs_write(awacs_reg[0] | MASK_ADDR0);
                awacs_write(awacs_reg[1] | MASK_ADDR1);
                awacs_write(awacs_reg[2] | MASK_ADDR2);
                awacs_write(awacs_reg[4] | MASK_ADDR4);
                // TODO: additional screamer registers
                out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
                enable_irq(awacs_irq);
                enable_irq(awacs_tx_irq);
                if (awacs_revision == 3) {
                        mdelay(100);
                        awacs_write(0x6000);
                        mdelay(2);
                        awacs_write(awacs_reg[1] | MASK_ADDR1);
                }
                /* enable CD sound input */
                if (macio_base && is_pbook_G3) {
                        out_8(macio_base + 0x37, 3);
                } else if (is_pbook_3400) {
                        feature_set(awacs_node, FEATURE_IOBUS_enable);
                        udelay(10);
                        in_8(latch_base + 0x190);
                }
                /* Resume pending sounds. */
                PMacPlay();
        }
        return PBOOK_SLEEP_OK;
}
#endif /* CONFIG_PMAC_PBOOK */


/* All the burgundy functions: */

/* Waits for busy flag to clear */
inline static void
awacs_burgundy_busy_wait(void)
{
        while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
                ;
}

inline static void
awacs_burgundy_extend_wait(void)
{
        while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND))
                ;
        while (in_le32(&awacs->codec_stat) & MASK_EXTEND)
                ;
}

static void
awacs_burgundy_wcw(unsigned addr, unsigned val)
{
        out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff));
        awacs_burgundy_busy_wait();
        out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff));
        awacs_burgundy_busy_wait();
        out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff));
        awacs_burgundy_busy_wait();
        out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff));
        awacs_burgundy_busy_wait();
}

static unsigned
awacs_burgundy_rcw(unsigned addr)
{
        unsigned val = 0;
        unsigned long flags;

        /* should have timeouts here */
        save_flags(flags); cli();

        out_le32(&awacs->codec_ctrl, addr + 0x100000);
        awacs_burgundy_busy_wait();
        awacs_burgundy_extend_wait();
        val += (in_le32(&awacs->codec_stat) >> 4) & 0xff;

        out_le32(&awacs->codec_ctrl, addr + 0x100100);
        awacs_burgundy_busy_wait();
        awacs_burgundy_extend_wait();
        val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8;

        out_le32(&awacs->codec_ctrl, addr + 0x100200);
        awacs_burgundy_busy_wait();
        awacs_burgundy_extend_wait();
        val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16;

        out_le32(&awacs->codec_ctrl, addr + 0x100300);
        awacs_burgundy_busy_wait();
        awacs_burgundy_extend_wait();
        val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24;

        restore_flags(flags);

        return val;
}


static void
awacs_burgundy_wcb(unsigned addr, unsigned val)
{
        out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff));
        awacs_burgundy_busy_wait();
}

static unsigned
awacs_burgundy_rcb(unsigned addr)
{
        unsigned val = 0;
        unsigned long flags;

        /* should have timeouts here */
        save_flags(flags); cli();

        out_le32(&awacs->codec_ctrl, addr + 0x100000);
        awacs_burgundy_busy_wait();
        awacs_burgundy_extend_wait();
        val += (in_le32(&awacs->codec_stat) >> 4) & 0xff;

        restore_flags(flags);

        return val;
}

static int
awacs_burgundy_check(void)
{
        /* Checks to see the chip is alive and kicking */
        int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE;

        return error == 0xf0000;
}

static int
awacs_burgundy_init(void)
{
        if (awacs_burgundy_check()) {
                printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n");
                return 1;
        }

        awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES,
                           DEF_BURGUNDY_OUTPUTENABLES);
        awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
                           DEF_BURGUNDY_MORE_OUTPUTENABLES);
        awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS,
                           DEF_BURGUNDY_OUTPUTSELECTS);

        awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21,
                           DEF_BURGUNDY_INPSEL21);
        awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3,
                           DEF_BURGUNDY_INPSEL3);
        awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD,
                           DEF_BURGUNDY_GAINCD);
        awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE,
                           DEF_BURGUNDY_GAINLINE);
        awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC,
                           DEF_BURGUNDY_GAINMIC);
        awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM,
                           DEF_BURGUNDY_GAINMODEM);

        awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER,
                           DEF_BURGUNDY_ATTENSPEAKER);
        awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT,
                           DEF_BURGUNDY_ATTENLINEOUT);
        awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP,
                           DEF_BURGUNDY_ATTENHP);

        awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME,
                           DEF_BURGUNDY_MASTER_VOLUME);
        awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD,
                           DEF_BURGUNDY_VOLCD);
        awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE,
                           DEF_BURGUNDY_VOLLINE);
        awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC,
                           DEF_BURGUNDY_VOLMIC);
        return 0;
}

static void
awacs_burgundy_write_volume(unsigned address, int volume)
{
        int hardvolume,lvolume,rvolume;

        lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0;
        rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0;

        hardvolume = lvolume + (rvolume << 16);

        awacs_burgundy_wcw(address, hardvolume);
}

static int
awacs_burgundy_read_volume(unsigned address)
{
        int softvolume,wvolume;

        wvolume = awacs_burgundy_rcw(address);

        softvolume = (wvolume & 0xff) - 155;
        softvolume += (((wvolume >> 16) & 0xff) - 155)<<8;

        return softvolume > 0 ? softvolume : 0;
}




static int
awacs_burgundy_read_mvolume(unsigned address)
{
        int lvolume,rvolume,wvolume;

        wvolume = awacs_burgundy_rcw(address);

        wvolume &= 0xffff;

        rvolume = (wvolume & 0xff) - 155;
        lvolume = ((wvolume & 0xff00)>>8) - 155;

        return lvolume + (rvolume << 8);
}


static void
awacs_burgundy_write_mvolume(unsigned address, int volume)
{
        int lvolume,rvolume,hardvolume;

        lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0;
        rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0;

        hardvolume = lvolume + (rvolume << 8);
        hardvolume += (hardvolume << 16);

        awacs_burgundy_wcw(address, hardvolume);
}

/* End burgundy functions */





/* Turn on sound output, needed on G3 desktop powermacs */
static void
awacs_enable_amp(int spkr_vol)
{
        struct adb_request req;

        awacs_spkr_vol = spkr_vol;
#ifdef COMPILING_2_4_X
        if (sys_ctrler != SYS_CTRLER_CUDA){
                printk(KERN_INFO "Didn't find a CUDA resource to switch sound 
on\n") ;
                return; }
#else
        if( adb_hardware != ADB_VIACUDA) {
                printk(KERN_INFO "Didn't find the ADB resources to switch sound 
on\n");
                return;
        }
#endif

        /* turn on headphones */

        cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
                     0x8a, 4, 0);
        while (!req.complete) cuda_poll();
        cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
                     0x8a, 6, 0);
        while (!req.complete) cuda_poll();

        /* turn on speaker */
        cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
                     0x8a, 3, (100 - (spkr_vol & 0xff)) * 32 / 100);
        while (!req.complete) cuda_poll();
        cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
                     0x8a, 5, (100 - ((spkr_vol >> 8) & 0xff)) * 32 / 100);
        while (!req.complete) cuda_poll();

        cuda_request(&req, NULL, 5, CUDA_PACKET,
                     CUDA_GET_SET_IIC, 0x8a, 1, 0x29);
        while (!req.complete) cuda_poll();
}


/*** Mid level stuff *********************************************************/


/*
 * /dev/mixer abstraction
 */

static int awacs_mixer_ioctl(u_int cmd, u_long arg)
{
        int data;

        // printk(__FILE__ ": awacs_mixer_ioctl()\n");
        switch (cmd) {
        case SOUND_MIXER_READ_DEVMASK:
                data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
                        | SOUND_MASK_LINE | SOUND_MASK_MIC
                        | SOUND_MASK_CD | SOUND_MASK_RECLEV
                        | SOUND_MASK_ALTPCM
                        | SOUND_MASK_MONITOR;
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_READ_RECMASK:
                data = SOUND_MASK_LINE | SOUND_MASK_MIC
                        | SOUND_MASK_CD;
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_READ_RECSRC:
          printk(__FILE__ ": mixer: read recsrc\n");
                data = 0;
                if (awacs_reg[0] & MASK_MUX_AUDIN)
                        data |= SOUND_MASK_LINE;
                if (awacs_reg[0] & MASK_MUX_MIC)
                        data |= SOUND_MASK_MIC;
                if (awacs_reg[0] & MASK_MUX_CD)
                        data |= SOUND_MASK_CD;
                if (awacs_reg[1] & MASK_LOOPTHRU)
                        data |= SOUND_MASK_MONITOR;
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_WRITE_RECSRC:
                IOCTL_IN(arg, data);
          printk(__FILE__ ": mixer: write recsrc: %d\n", data);
                data &= (SOUND_MASK_LINE
                         | SOUND_MASK_MIC | SOUND_MASK_CD
                         | SOUND_MASK_MONITOR);
                awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
                                  | MASK_MUX_AUDIN);
                awacs_reg[1] &= ~MASK_LOOPTHRU;
                if (data & SOUND_MASK_LINE)
                        awacs_reg[0] |= MASK_MUX_AUDIN;
                if (data & SOUND_MASK_MIC)
                        awacs_reg[0] |= MASK_MUX_MIC;
                if (data & SOUND_MASK_CD)
                        awacs_reg[0] |= MASK_MUX_CD;
                if (data & SOUND_MASK_MONITOR)
                  awacs_reg[1] |= MASK_LOOPTHRU;
                awacs_write(awacs_reg[0] | MASK_ADDR0);
                awacs_write(awacs_reg[1] | MASK_ADDR1);
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_READ_STEREODEVS:
                data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
                        | SOUND_MASK_RECLEV;
          printk(__FILE__ ": mixer: read stereodevs: %d\n", data);
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_READ_CAPS:
          printk(__FILE__ ": mixer: read caps: 0\n");
                return IOCTL_OUT(arg, 0);
        case SOUND_MIXER_READ_VOLUME:
                data = (awacs_reg[1] & MASK_AMUTE)? 0:
                        awacs_get_volume(awacs_reg[2], 6);
          printk(__FILE__ ": mixer: read volume: %d\n", data);
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_WRITE_VOLUME:
                IOCTL_IN(arg, data);
                printk(__FILE__ ": mixer: write volume: %d\n", data);
                return IOCTL_OUT(arg, PMacSetVolume(data));
        case SOUND_MIXER_READ_SPEAKER:
                if (awacs_revision == 3
#ifdef COMPILING_2_4_X
                    && sys_ctrler == SYS_CTRLER_CUDA
#else
                    && (adb_hardware == ADB_VIACUDA)
#endif
                        )
                {
                        data = awacs_spkr_vol;
                        // printk(__FILE__ ": mixer: speaker a\n");
                }
                else
                {
                        data = (awacs_reg[1] & MASK_CMUTE)? 0:
                                awacs_get_volume(awacs_reg[4], 6);
                        // printk(__FILE__ ": mixer: speaker b\n");
                }
                printk(__FILE__ ": mixer: read speaker: %d\n", data);
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_WRITE_SPEAKER:
                IOCTL_IN(arg, data);
                if (awacs_revision == 3
#ifdef COMPILING_2_4_X
                    && sys_ctrler == SYS_CTRLER_CUDA
#else
                    && (adb_hardware == ADB_VIACUDA)
#endif
                        )
                        awacs_enable_amp(data);
                else
                {
                        printk(__FILE__ ": mixer: write speaker: %d\n", data);
                        data = awacs_volume_setter(data, 4, MASK_CMUTE, 6);
                }
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_WRITE_ALTPCM:  /* really bell volume */
                IOCTL_IN(arg, data);
                beep_volume = data & 0xff;
                                /* fall through */
        case SOUND_MIXER_READ_ALTPCM:
                return IOCTL_OUT(arg, beep_volume);
        case SOUND_MIXER_WRITE_LINE:
                printk(__FILE__ ": mixer: write line\n");
                IOCTL_IN(arg, data);
                awacs_reg[0] &= ~MASK_MUX_AUDIN;
                if ((data & 0xff) >= 50)
                        awacs_reg[0] |= MASK_MUX_AUDIN;
                awacs_write(MASK_ADDR0 | awacs_reg[0]);
                                /* fall through */
        case SOUND_MIXER_READ_LINE:
                printk(__FILE__ ": mixer: read line\n");
                data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0;
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_WRITE_MIC:
                printk(__FILE__ ": mixer: write mic\n");
                IOCTL_IN(arg, data);
                data &= 0xff;
                awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE);
                if (data >= 25) {
                        awacs_reg[0] |= MASK_MUX_MIC;
                        if (data >= 75)
                                awacs_reg[0] |= MASK_GAINLINE;
                }
                awacs_write(MASK_ADDR0 | awacs_reg[0]);
                                /* fall through */
        case SOUND_MIXER_READ_MIC:
                data = (awacs_reg[0] & MASK_MUX_MIC)?
                        (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0;
                printk(__FILE__ ": mixer: read mic\n");
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_WRITE_CD:
                IOCTL_IN(arg, data);
                awacs_reg[0] &= ~MASK_MUX_CD;
                if ((data & 0xff) >= 50)
                        awacs_reg[0] |= MASK_MUX_CD;
                awacs_write(MASK_ADDR0 | awacs_reg[0]);
                                /* fall through */
        case SOUND_MIXER_READ_CD:
                data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0;
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_WRITE_RECLEV:
                IOCTL_IN(arg, data);
                data = awacs_volume_setter(data, 0, 0, 4);
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_READ_RECLEV:
                data = awacs_get_volume(awacs_reg[0], 4);
                return IOCTL_OUT(arg, data);
        case MIXER_WRITE(SOUND_MIXER_MONITOR):
                /*
                 *      TODO: test if we have a screamer. In this case,
                 *      set real loopthrough level.
                 */
                IOCTL_IN(arg, data);
                awacs_reg[1] &= ~MASK_LOOPTHRU;
                if ((data & 0xff) >= 50)
                        awacs_reg[1] |= MASK_LOOPTHRU;
                awacs_write(MASK_ADDR1 | awacs_reg[1]);
                /* fall through */
        case MIXER_READ(SOUND_MIXER_MONITOR):
                data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0;
                return IOCTL_OUT(arg, data);
        }
        return -EINVAL;
}

static int burgundy_mixer_ioctl(u_int cmd, u_long arg)
{
        int data;

        printk(__FILE__ ": burgundy_mixer_ioctl()\n");
        /* We are, we are, we are... Burgundy or better */
        switch(cmd) {
        case SOUND_MIXER_READ_DEVMASK:
                data = SOUND_MASK_VOLUME | SOUND_MASK_CD |
                        SOUND_MASK_LINE | SOUND_MASK_MIC |
                        SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM;
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_READ_RECMASK:
                data = SOUND_MASK_LINE | SOUND_MASK_MIC
                        | SOUND_MASK_CD;
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_READ_RECSRC:
                data = 0;
                if (awacs_reg[0] & MASK_MUX_AUDIN)
                        data |= SOUND_MASK_LINE;
                if (awacs_reg[0] & MASK_MUX_MIC)
                        data |= SOUND_MASK_MIC;
                if (awacs_reg[0] & MASK_MUX_CD)
                        data |= SOUND_MASK_CD;
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_WRITE_RECSRC:
                IOCTL_IN(arg, data);
                data &= (SOUND_MASK_LINE
                         | SOUND_MASK_MIC | SOUND_MASK_CD);
                awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
                                  | MASK_MUX_AUDIN);
                if (data & SOUND_MASK_LINE)
                        awacs_reg[0] |= MASK_MUX_AUDIN;
                if (data & SOUND_MASK_MIC)
                        awacs_reg[0] |= MASK_MUX_MIC;
                if (data & SOUND_MASK_CD)
                        awacs_reg[0] |= MASK_MUX_CD;
                awacs_write(awacs_reg[0] | MASK_ADDR0);
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_READ_STEREODEVS:
                data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
                        | SOUND_MASK_RECLEV | SOUND_MASK_CD
                        | SOUND_MASK_LINE;
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_READ_CAPS:
                return IOCTL_OUT(arg, 0);
        case SOUND_MIXER_WRITE_VOLUME:
                IOCTL_IN(arg, data);
                awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, 
data);
                                /* Fall through */
        case SOUND_MIXER_READ_VOLUME:
                return IOCTL_OUT(arg, 
awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME));
        case SOUND_MIXER_WRITE_SPEAKER:
                IOCTL_IN(arg, data);

                if (!(data & 0xff)) {
                        /* Mute the left speaker */
                        
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
                                           
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2);
                } else {
                        /* Unmute the left speaker */
                        
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
                                           
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2);
                }
                if (!(data & 0xff00)) {
                        /* Mute the right speaker */
                        
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
                                           
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4);
                } else {
                        /* Unmute the right speaker */
                        
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
                                           
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4);
                }

                data = (((data&0xff)*16)/100 > 0xf ? 0xf :
                        (((data&0xff)*16)/100)) + 
                        ((((data>>8)*16)/100 > 0xf ? 0xf :
                          ((((data>>8)*16)/100)))<<4);

                awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data);
                                /* Fall through */
        case SOUND_MIXER_READ_SPEAKER:
                data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER);
                data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8);
                return IOCTL_OUT(arg, ~data);
        case SOUND_MIXER_WRITE_ALTPCM:  /* really bell volume */
                IOCTL_IN(arg, data);
                beep_volume = data & 0xff;
                                /* fall through */
        case SOUND_MIXER_READ_ALTPCM:
                return IOCTL_OUT(arg, beep_volume);
        case SOUND_MIXER_WRITE_LINE:
                IOCTL_IN(arg, data);
                awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data);

                                /* fall through */
        case SOUND_MIXER_READ_LINE:
                data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE);  
                        
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_WRITE_MIC:
                IOCTL_IN(arg, data);
                                /* Mic is mono device */
                data = (data << 8) + (data << 24);
                awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data);
                                /* fall through */
        case SOUND_MIXER_READ_MIC:
                data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC);   
                        
                data <<= 24;
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_WRITE_CD:
                IOCTL_IN(arg, data);
                awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data);
                                /* fall through */
        case SOUND_MIXER_READ_CD:
                data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD);
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_WRITE_RECLEV:
                IOCTL_IN(arg, data);
                data = awacs_volume_setter(data, 0, 0, 4);
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_READ_RECLEV:
                data = awacs_get_volume(awacs_reg[0], 4);
                return IOCTL_OUT(arg, data);
        case SOUND_MIXER_OUTMASK:
                break;
        case SOUND_MIXER_OUTSRC:
                break;
        }
        return -EINVAL;
}

static int PMacMixerIoctl(u_int cmd, u_long arg)
{
        /* Different IOCTLS for burgundy*/
  /* printk(__FILE__ ": avacs_revision = %d\n", awacs_revision); */
        if (awacs_revision >= AWACS_BURGUNDY)
                return burgundy_mixer_ioctl(cmd, arg);
        return awacs_mixer_ioctl(cmd, arg);
}


static void PMacWriteSqSetup(void)
{
        int i;
        volatile struct dbdma_cmd *cp;

        cp = awacs_tx_cmds;
        memset((void *)cp, 0, (write_sq.numBufs+1) * sizeof(struct dbdma_cmd));
        for (i = 0; i < write_sq.numBufs; ++i, ++cp) {
                st_le32(&cp->phy_addr, virt_to_bus(write_sq.buffers[i]));
        }
        st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
        st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds));
        out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
        out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds));
}

static void PMacReadSqSetup(void)
{
        int i;
        volatile struct dbdma_cmd *cp;

        cp = awacs_rx_cmds;
        memset((void *)cp, 0, (read_sq.numBufs+1) * sizeof(struct dbdma_cmd));

        /* Set dma buffers up in a loop */
        for (i = 0; i < read_sq.numBufs; i++,cp++) {
                st_le32(&cp->phy_addr, virt_to_bus(read_sq.buffers[i]));
                st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS);
                st_le16(&cp->req_count, read_sq.block_size);
                st_le16(&cp->xfer_status, 0);
        }

        /* The next two lines make the thing loop around.
        */
        st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
        st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds));

        /* Don't start until the first read is done.
         * This will also abort any operations in progress if the DMA
         * happens to be running (and it shouldn't).
         */
        out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
        out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds));

}

static void PMacAbortRead(void)
{
        int i;
        volatile struct dbdma_cmd *cp;

        cp = awacs_rx_cmds;
        for (i = 0; i < read_sq.numBufs; i++,cp++)
                st_le16(&cp->command, DBDMA_STOP);
        /*
         * We should probably wait for the thing to stop before we
         * release the memory
         */
}


/*** Machine definitions *****************************************************/


static MACHINE machPMac = {
        name:           awacs_name,
        name2:          "AWACS",
        open:           PMacOpen,
        release:        PMacRelease,
        dma_alloc:      PMacAlloc,
        dma_free:       PMacFree,
        irqinit:        PMacIrqInit,
#ifdef MODULE
        irqcleanup:     PMacIrqCleanup,
#endif /* MODULE */
        init:           PMacInit,
        silence:        PMacSilence,
        setFormat:      PMacSetFormat,
        setVolume:      PMacSetVolume,
        play:           PMacPlay,
        record:         PMacRecord,
        mixer_ioctl:    PMacMixerIoctl,
        write_sq_setup: PMacWriteSqSetup,
        read_sq_setup:  PMacReadSqSetup,
        abort_read:     PMacAbortRead,
        min_dsp_speed:  8000
};


/*** Config & Setup **********************************************************/


int __init dmasound_awacs_init(void)
{
        struct device_node *np;

        printk(__FILE__ ": dmasound_awacs_init()\n");
        if (_machine != _MACH_Pmac)
                return -ENODEV;

        awacs_subframe = 0;
        awacs_revision = 0;
        np = find_devices("awacs");
        if (np == 0) {
                /*
                 * powermac G3 models have a node called "davbus"
                 * with a child called "sound".
                 */
                struct device_node *sound;
                printk(__FILE__ ": device node awacs not found!\n");
                np = find_devices("davbus");
                sound = find_devices("sound");
                if (sound != 0 && sound->parent == np) {
                        unsigned int *prop, l, i;
                        printk(__FILE__ ": device nodes davbus and sound 
found!\n");
                        prop = (unsigned int *)
                                get_property(sound, "sub-frame", 0);
                        if (prop != 0 && *prop >= 0 && *prop < 16)
                          {
                            printk(__FILE__ ": subframe: %d\n", *prop);
                                awacs_subframe = *prop;
                          }
                        if (device_is_compatible(sound, "burgundy"))
                          {
                            printk(__FILE__ ": burgundy recognized!\n");
                                awacs_revision = AWACS_BURGUNDY;
                          }

                        /* look for a property saying what sample rates
                           are available */
                        for (i = 0; i < 8; ++i)
                                awacs_freqs_ok[i] = 0;
                        prop = (unsigned int *) get_property
                                (sound, "sample-rates", &l);
                        if (prop == 0)
                                prop = (unsigned int *) get_property
                                        (sound, "output-frame-rates", &l);
                        if (prop != 0) {
                                for (l /= sizeof(int); l > 0; --l) {
                                        /* sometimes the rate is in the
                                           high-order 16 bits (?) */
                                        unsigned int r = *prop++;
                                        if (r >= 0x10000)
                                                r >>= 16;
                                        for (i = 0; i < 8; ++i) {
                                                if (r == awacs_freqs[i]) {
                                                        awacs_freqs_ok[i] = 1;
                                                        break;
                                                }
                                        }
                                }
                        } else {
                                /* assume just 44.1k is OK */
                                awacs_freqs_ok[0] = 1;
                                machPMac.min_dsp_speed = 44100 ;
                        }
                }
        }
        if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) {
                int vol;
                dmasound.mach = machPMac;
printk("doing IO remap of awacs regs.\n");
                awacs = (volatile struct awacs_regs *)
                        ioremap(np->addrs[0].address, 0x80);
                awacs_txdma = (volatile struct dbdma_regs *)
                        ioremap(np->addrs[1].address, 0x100);
                awacs_rxdma = (volatile struct dbdma_regs *)
                        ioremap(np->addrs[2].address, 0x100);

                awacs_irq = np->intrs[0].line;
                awacs_tx_irq = np->intrs[1].line;
                awacs_rx_irq = np->intrs[2].line;

                awacs_tx_cmd_space = kmalloc((write_sq.numBufs + 4) * 
sizeof(struct dbdma_cmd),
                                             GFP_KERNEL);
                if (awacs_tx_cmd_space == NULL) {
                        printk(KERN_ERR "DMA sound driver: Not enough buffer 
memory, driver disabled!\n");
                        return -ENOMEM;
                }
                awacs_node = np;
#ifdef CONFIG_PMAC_PBOOK
                if (machine_is_compatible("PowerBook1,1")
                    || machine_is_compatible("AAPL,PowerBook1998")) {
                        feature_set(np, FEATURE_Sound_CLK_enable);
                        feature_set(np, FEATURE_Sound_power);
                        /* Shorter delay will not work */
                        mdelay(1000);
                }
#endif
                awacs_tx_cmds = (volatile struct dbdma_cmd *)
                        DBDMA_ALIGN(awacs_tx_cmd_space);


                awacs_rx_cmd_space = kmalloc((read_sq.numBufs + 4) * 
sizeof(struct dbdma_cmd),
                                             GFP_KERNEL);
                if (awacs_rx_cmd_space == NULL) {
                  printk(KERN_ERR "DMA sound driver: No memory for input, 
driver disabled\n");
                        return -ENOMEM; 
                }
                awacs_rx_cmds = (volatile struct dbdma_cmd *)
                  DBDMA_ALIGN(awacs_rx_cmd_space);

                awacs_reg[0] = MASK_MUX_CD;
                awacs_reg[1] = MASK_LOOPTHRU; /* | MASK_PAROUT;*/
                /* get default volume from nvram */
                vol = (~nvram_read_byte(0x1308) & 7) << 1;
                awacs_reg[2] = vol + (vol << 6);
                awacs_reg[4] = vol + (vol << 6);
                awacs_reg[5] = 0;
                awacs_reg[6] = 0;
                awacs_reg[7] = 0;
                out_le32(&awacs->control, 0x11);
                awacs_write(awacs_reg[0] + MASK_ADDR0);
                awacs_write(awacs_reg[1] + MASK_ADDR1);
                awacs_write(awacs_reg[2] + MASK_ADDR2);
                awacs_write(awacs_reg[4] + MASK_ADDR4);
                if (is_screamer)
                {
                        awacs_write(awacs_reg[5] + MASK_ADDR5);
                        awacs_write(awacs_reg[6] + MASK_ADDR6);
                        awacs_write(awacs_reg[7] + MASK_ADDR7);
                }

                /* Initialize recent versions of the awacs */
                if (awacs_revision == 0) {
                        awacs_revision =
                                (in_le32(&awacs->codec_stat) >> 12) & 0xf;
                        if (awacs_revision == 3) {
                                mdelay(100);
                                awacs_write(0x6000);    /* !!!! */
                                mdelay(2);
                                awacs_write(awacs_reg[1] + MASK_ADDR1);
                                awacs_enable_amp(100 * 0x101);
                        }
                }
                if (awacs_revision >= AWACS_BURGUNDY)
                        awacs_burgundy_init();

                /* Initialize beep stuff */
                beep_dbdma_cmd = awacs_tx_cmds + (write_sq.numBufs + 1);
                orig_mksound = kd_mksound;
                kd_mksound = awacs_mksound;
                beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
                if (beep_buf == NULL)
                        printk(KERN_WARNING "dmasound: no memory for "
                               "beep buffer\n");
#ifdef CONFIG_PMAC_PBOOK
                pmu_register_sleep_notifier(&awacs_sleep_notifier);
#endif /* CONFIG_PMAC_PBOOK */

                /* Powerbooks have odd ways of enabling inputs such as
                   an expansion-bay CD or sound from an internal modem
                   or a PC-card modem. */
                printk("trying to detect PB...");
                if (machine_is_compatible("AAPL,3400/2400")
                        || machine_is_compatible("AAPL,3500")) {
                        printk("3400\n");
                        is_pbook_3400 = 1;
                        /*
                         * Enable CD and PC-card sound inputs.
                         * This is done by reading from address
                         * f301a000, + 0x10 to enable the expansion-bay
                         * CD sound input, + 0x80 to enable the PC-card
                         * sound input.  The 0x100 enables the SCSI bus
                         * terminator power.
                         */
                        latch_base = (unsigned char *) ioremap
                                (0xf301a000, 0x1000);
                        in_8(latch_base + 0x190);
                } else if (machine_is_compatible("PowerBook1,1")
                           || machine_is_compatible("PowerBook3,1")
                           || machine_is_compatible("AAPL,PowerBook1998")) {
                        struct device_node* mio;
                        printk("G3\n");
                        macio_base = 0;
                        is_pbook_G3 = 1;
                        for (mio = np->parent; mio; mio = mio->parent) {
                                if (strcmp(mio->name, "mac-io") == 0
                                    && mio->n_addrs > 0) {
                                        macio_base = (unsigned char *) ioremap
                                                (mio->addrs[0].address, 0x40);
                                        break;
                                }
                        }
                        /* enable CD sound input */
                        if (macio_base)
                        {
                                out_8(macio_base + 0x37, 3);
                                printk("CD sound enabled\n");
                        }
                }
                else
                {
                        printk("none\n");
                }
                sprintf(awacs_name, "PowerMac (AWACS rev %d) ",
                        awacs_revision);
                return dmasound_init();
        }
        return -ENODEV;
}

#ifdef COMPILING_2_4_X
static void __exit dmasound_awacs_cleanup(void)
{
        dmasound_deinit();
}

module_init(dmasound_awacs_init);
module_exit(dmasound_awacs_cleanup);
#else

#ifdef MODULE
int init_module(void)
{
        return dmasound_awacs_init() ;
}

int cleanup_module(void)
{
        dmasound_deinit() ;
        return 0 ;
}
#endif
#endif
/*********************************************************/
/* This file was written by someone, somewhere, sometime */
/* And is released into the Public Domain                */
/*********************************************************/

#ifndef _AWACS_DEFS_H_
#define _AWACS_DEFS_H_

/*******************************/
/* AWACs Audio Register Layout */
/*******************************/

struct awacs_regs {
    unsigned    control;        /* Audio control register */
    unsigned    pad0[3];
    unsigned    codec_ctrl;     /* Codec control register */
    unsigned    pad1[3];
    unsigned    codec_stat;     /* Codec status register */
    unsigned    pad2[3];
    unsigned    clip_count;     /* Clipping count register */
    unsigned    pad3[3];
    unsigned    byteswap;       /* Data is little-endian if 1 */
};

/*******************/
/* Audio Bit Masks */
/*******************/

/* Audio Control Reg Bit Masks */
/* ----- ------- --- --- ----- */
#define MASK_ISFSEL     (0xf)           /* Input SubFrame Select */
#define MASK_OSFSEL     (0xf << 4)      /* Output SubFrame Select */
#define MASK_RATE       (0x7 << 8)      /* Sound Rate */
#define MASK_CNTLERR    (0x1 << 11)     /* Error */
#define MASK_PORTCHG    (0x1 << 12)     /* Port Change */
#define MASK_IEE        (0x1 << 13)     /* Enable Interrupt on Error */
#define MASK_IEPC       (0x1 << 14)     /* Enable Interrupt on Port Change */
#define MASK_SSFSEL     (0x3 << 15)     /* Status SubFrame Select */

/* Audio Codec Control Reg Bit Masks */
/* ----- ----- ------- --- --- ----- */
#define MASK_NEWECMD    (0x1 << 24)     /* Lock: don't write to reg when 1 */
#define MASK_EMODESEL   (0x3 << 22)     /* Send info out on which frame? */
#define MASK_EXMODEADDR (0x3ff << 12)   /* Extended Mode Address -- 10 bits */
#define MASK_EXMODEDATA (0xfff)         /* Extended Mode Data -- 12 bits */

/* Audio Codec Control Address Values / Masks */
/* ----- ----- ------- ------- ------ - ----- */
#define MASK_ADDR0      (0x0 << 12)     /* Expanded Data Mode Address 0 */
#define MASK_ADDR_MUX   MASK_ADDR0      /* Mux Control */
#define MASK_ADDR_GAIN  MASK_ADDR0

#define MASK_ADDR1      (0x1 << 12)     /* Expanded Data Mode Address 1 */
#define MASK_ADDR_MUTE  MASK_ADDR1
#define MASK_ADDR_RATE  MASK_ADDR1

#define MASK_ADDR2      (0x2 << 12)     /* Expanded Data Mode Address 2 */
#define MASK_ADDR_VOLA  MASK_ADDR2      /* Volume Control A -- Headphones */
#define MASK_ADDR_VOLHD MASK_ADDR2

#define MASK_ADDR4      (0x4 << 12)     /* Expanded Data Mode Address 4 */
#define MASK_ADDR_VOLC  MASK_ADDR4      /* Volume Control C -- Speaker */
#define MASK_ADDR_VOLSPK MASK_ADDR4

/* additional registers of screamer */
#define MASK_ADDR5      (0x5 << 12)     /* Expanded Data Mode Address 5 */
#define MASK_ADDR6      (0x6 << 12)     /* Expanded Data Mode Address 6 */
#define MASK_ADDR7      (0x7 << 12)     /* Expanded Data Mode Address 7 */

/* Address 0 Bit Masks & Macros */
/* ------- - --- ----- - ------ */
#define MASK_GAINRIGHT  (0xf)           /* Gain Right Mask */
#define MASK_GAINLEFT   (0xf << 4)      /* Gain Left Mask */
#define MASK_GAINLINE   (0x1 << 8)      /* Change Gain for Line??? */
#define MASK_GAINMIC    (0x0 << 8)      /* Change Gain for Mic??? */

#define MASK_MUX_CD     (0x1 << 9)      /* Select CD in MUX */
#define MASK_MUX_AUDIN  (0x1 << 10)     /* Select Audio In in MUX */
#define MASK_MUX_MIC    (0x1 << 11)     /* Select Mic in MUX */
#define MASK_MUX_LINE   MASK_MUX_AUDIN

#define GAINRIGHT(x)    ((x) & MASK_GAINRIGHT)
#define GAINLEFT(x)     (((x) << 4) & MASK_GAINLEFT)

/* Address 1 Bit Masks */
/* ------- - --- ----- */
#define MASK_ADDR1RES1  (0x3)           /* Reserved */
#define MASK_RECALIBRATE (0x1 << 2)     /* Recalibrate */
#define MASK_SAMPLERATE (0x7 << 3)      /* Sample Rate: */
#define MASK_LOOPTHRU   (0x1 << 6)      /* Loopthrough Enable */
#define MASK_CMUTE      (0x1 << 7)      /* Output C (Speaker) Mute when 1 */
#define MASK_SPKMUTE    MASK_CMUTE
#define MASK_ADDR1RES2  (0x1 << 8)      /* Reserved */
#define MASK_AMUTE      (0x1 << 9)      /* Output A (Headphone) Mute when 1 */
#define MASK_HDMUTE     MASK_AMUTE
#define MASK_PAROUT     (0x3 << 10)     /* Parallel Out (???) */

#define SAMPLERATE_48000        (0x0 << 3)      /* 48 or 44.1 kHz */
#define SAMPLERATE_32000        (0x1 << 3)      /* 32 or 29.4 kHz */
#define SAMPLERATE_24000        (0x2 << 3)      /* 24 or 22.05 kHz */
#define SAMPLERATE_19200        (0x3 << 3)      /* 19.2 or 17.64 kHz */
#define SAMPLERATE_16000        (0x4 << 3)      /* 16 or 14.7 kHz */
#define SAMPLERATE_12000        (0x5 << 3)      /* 12 or 11.025 kHz */
#define SAMPLERATE_9600         (0x6 << 3)      /* 9.6 or 8.82 kHz */
#define SAMPLERATE_8000         (0x7 << 3)      /* 8 or 7.35 kHz */

/* Address 2 & 4 Bit Masks & Macros */
/* ------- - - - --- ----- - ------ */
#define MASK_OUTVOLRIGHT (0xf)          /* Output Right Volume */
#define MASK_ADDR2RES1  (0x2 << 4)      /* Reserved */
#define MASK_ADDR4RES1  MASK_ADDR2RES1
#define MASK_OUTVOLLEFT (0xf << 6)      /* Output Left Volume */
#define MASK_ADDR2RES2  (0x2 << 10)     /* Reserved */
#define MASK_ADDR4RES2  MASK_ADDR2RES2

#define VOLRIGHT(x)     (((~(x)) & MASK_OUTVOLRIGHT))
#define VOLLEFT(x)      (((~(x)) << 6) & MASK_OUTVOLLEFT)

/* Audio Codec Status Reg Bit Masks */
/* ----- ----- ------ --- --- ----- */
#define MASK_EXTEND     (0x1 << 23)     /* Extend */
#define MASK_VALID      (0x1 << 22)     /* Valid Data? */
#define MASK_OFLEFT     (0x1 << 21)     /* Overflow Left */
#define MASK_OFRIGHT    (0x1 << 20)     /* Overflow Right */
#define MASK_ERRCODE    (0xf << 16)     /* Error Code */
#define MASK_REVISION   (0xf << 12)     /* Revision Number */
#define MASK_MFGID      (0xf << 8)      /* Mfg. ID */
#define MASK_CODSTATRES (0xf << 4)      /* bits 4 - 7 reserved */
#define MASK_INPPORT    (0xf)           /* Input Port */
#define MASK_HDPCONN    8               /* headphone plugged in */

/* Clipping Count Reg Bit Masks */
/* -------- ----- --- --- ----- */
#define MASK_CLIPLEFT   (0xff << 7)     /* Clipping Count, Left Channel */
#define MASK_CLIPRIGHT  (0xff)          /* Clipping Count, Right Channel */

/* DBDMA ChannelStatus Bit Masks */
/* ----- ------------- --- ----- */
#define MASK_CSERR      (0x1 << 7)      /* Error */
#define MASK_EOI        (0x1 << 6)      /* End of Input -- only for Input 
Channel */
#define MASK_CSUNUSED   (0x1f << 1)     /* bits 1-5 not used */
#define MASK_WAIT       (0x1)           /* Wait */

/* Various Rates */
/* ------- ----- */
#define RATE_48000      (0x0 << 8)      /* 48 kHz */
#define RATE_44100      (0x0 << 8)      /* 44.1 kHz */
#define RATE_32000      (0x1 << 8)      /* 32 kHz */
#define RATE_29400      (0x1 << 8)      /* 29.4 kHz */
#define RATE_24000      (0x2 << 8)      /* 24 kHz */
#define RATE_22050      (0x2 << 8)      /* 22.05 kHz */
#define RATE_19200      (0x3 << 8)      /* 19.2 kHz */
#define RATE_17640      (0x3 << 8)      /* 17.64 kHz */
#define RATE_16000      (0x4 << 8)      /* 16 kHz */
#define RATE_14700      (0x4 << 8)      /* 14.7 kHz */
#define RATE_12000      (0x5 << 8)      /* 12 kHz */
#define RATE_11025      (0x5 << 8)      /* 11.025 kHz */
#define RATE_9600       (0x6 << 8)      /* 9.6 kHz */
#define RATE_8820       (0x6 << 8)      /* 8.82 kHz */
#define RATE_8000       (0x7 << 8)      /* 8 kHz */
#define RATE_7350       (0x7 << 8)      /* 7.35 kHz */

#define RATE_LOW        1       /* HIGH = 48kHz, etc;  LOW = 44.1kHz, etc. */


/* Burgundy values */

#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12)
#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12)

#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12)

#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12)

#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12)
#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12)

#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12)

#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12)

#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12)
#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12)
#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12)

#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1)
#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2)
#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3)
#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4)

#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1)
#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2)
#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3)
#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4)


/* These are all default values for the burgundy */
#define DEF_BURGUNDY_INPSEL21 (0xAA)
#define DEF_BURGUNDY_INPSEL3 (0x0A)

#define DEF_BURGUNDY_GAINCD (0x33)
#define DEF_BURGUNDY_GAINLINE (0x44)
#define DEF_BURGUNDY_GAINMIC (0x44)
#define DEF_BURGUNDY_GAINMODEM (0x06)

/* Remember: lowest volume here is 0x9b */
#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC)
#define DEF_BURGUNDY_VOLLINE (0x00000000)
#define DEF_BURGUNDY_VOLMIC (0x00000000)
#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC)

#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f)
#define DEF_BURGUNDY_OUTPUTENABLES (0x0A)

#define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF)

#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E)

#define DEF_BURGUNDY_ATTENSPEAKER (0x44)
#define DEF_BURGUNDY_ATTENLINEOUT (0xCC)
#define DEF_BURGUNDY_ATTENHP (0xCC)

#endif /* _AWACS_DEFS_H_ */

Reply via email to