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

internal speakers on Pismo



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:Matthias.Pfisterer@gmx.de>

        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 to: