Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/bitsy/devuda1341.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname bitsy/devuda1341.c 2000/1103
## diff -e /dev/null /n/emeliedump/2000/1103/sys/src/9/bitsy/devuda1341.c
0a
/*
 *	SAC/UDA 1341 Audio driver for the Bitsy
 *
 *	The Philips UDA 1341 sound chip is accessed through the Serial Audio
 *	Controller (SAC) of the StrongARM SA-1110.  This is much more a SAC
 *	controller than a UDA controller, but we have a devsac.c already.
 *
 *	The code morphs Nicolas Pitre's <nico@cam.org> Linux controller
 *	and Ken's Soundblaster controller.
 *
 *	The interface should be identical to that of devaudio.c
 */
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"io.h"

/*
 * GPIO based L3 bus support.
 *
 * This provides control of Philips L3 type devices. 
 * GPIO lines are used for clock, data and mode pins.
 *
 * Note: The L3 pins are shared with I2C devices. This should not present
 * any problems as long as an I2C start sequence is not generated. This is
 * defined as a 1->0 transition on the data lines when the clock is high.
 * It is critical this code only allow data transitions when the clock
 * is low. This is always legal in L3.
 *
 * The IIC interface requires the clock and data pin to be LOW when idle. We
 * must make sure we leave them in this state.
 *
 * It appears the read data is generated on the falling edge of the clock
 * and should be held stable during the clock high time.
 */

/* 
 * L3 setup and hold times (expressed in us)
 */
#define L3_DataSetupTime	1		/* 190 ns */
#define L3_DataHoldTime		1		/*  30 ns */
#define L3_ModeSetupTime	1		/* 190 ns */
#define L3_ModeHoldTime		1		/* 190 ns */
#define L3_ClockHighTime	100		/* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
#define L3_ClockLowTime		100		/* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
#define L3_HaltTime			1		/* 190 ns */

/* UDA 1341 Registers */
enum {
	/* Status0 register */
	UdaStatusDC		= 0,	/* 1 bit */
	UdaStatusIF		= 1,	/* 3 bits */
	UdaStatusSC		= 4,	/* 2 bits */
	UdaStatusRST	= 6,	/* 1 bit */
};

enum {
	/* Status1 register */
	UdaStatusPC		= 0,	/* 2 bits */
	UdaStatusDS		= 2,	/* 1 bit */
	UdaStatusPDA	= 3,	/* 1 bit */
	UdaStatusPAD	= 4,	/* 1 bit */
	UdaStatusIGS	= 5,	/* 1 bit */
	UdaStatusOGS	= 6,	/* 1 bit */
};

/*
 * UDA1341 L3 address and command types
 */
#define UDA1341_L3Addr	5
#define UDA1341_DATA0	0
#define UDA1341_DATA1	1
#define UDA1341_STATUS	2

typedef struct	AQueue	AQueue;
typedef struct	Buf	Buf;

enum
{
	Qdir		= 0,
	Qaudio,
	Qvolume,
	Qstatus,

	Fmono		= 1,
	Fin		= 2,
	Fout		= 4,

	Aclosed		= 0,
	Aread,
	Awrite,

	Vaudio		= 0,
	Vsynth,
	Vcd,
	Vline,
	Vmic,
	Vspeaker,
	Vtreb,
	Vbass,
	Vspeed,
	Nvol,

	Bufsize		= 16*1024,	/* 92 ms each */
	Nbuf		= 16,		/* 1.5 seconds total */

	Speed		= 44100,
	Ncmd		= 50,		/* max volume command words */
};

Dirtab
audiodir[] =
{
	"audio",	{Qaudio},		0,	0666,
	"volume",	{Qvolume},		0,	0666,
	"audiostat",{Qstatus},		0,	0444,
};

struct	Buf
{
	uchar*	virt;
	ulong	phys;
	Buf*	next;
};
struct	AQueue
{
	Lock;
	Buf*	first;
	Buf*	last;
};
static	struct
{
	QLock;
	Rendez	vous;
	int	bufinit;	/* boolean if buffers allocated */
	int	curcount;	/* how much data in current buffer */
	int	active;		/* boolean dma running */
	int	intr;		/* boolean an interrupt has happened */
	int	amode;		/* Aclosed/Aread/Awrite for /audio */
	int	rivol[Nvol];		/* right/left input/output volumes */
	int	livol[Nvol];
	int	rovol[Nvol];
	int	lovol[Nvol];
	int	major;		/* SB16 major version number (sb 4) */
	int	minor;		/* SB16 minor version number */
	ulong	totcount;	/* how many bytes processed since open */
	vlong	tottime;	/* time at which totcount bytes were processed */

	Buf	buf[Nbuf];	/* buffers and queues */
	AQueue	empty;
	AQueue	full;
	Buf*	current;
	Buf*	filling;
} audio;

static	struct
{
	char*	name;
	int	flag;
	int	ilval;		/* initial values */
	int	irval;
} volumes[] =
{
[Vaudio]	"audio",	Fout, 		50,	50,
[Vsynth]	"synth",	Fin|Fout,	0,	0,
[Vcd]		"cd",		Fin|Fout,	0,	0,
[Vline]		"line",		Fin|Fout,	0,	0,
[Vmic]		"mic",		Fin|Fout|Fmono,	0,	0,
[Vspeaker]	"speaker",	Fout|Fmono,	0,	0,

[Vtreb]		"treb",		Fout, 		50,	50,
[Vbass]		"bass",		Fout, 		50,	50,

[Vspeed]	"speed",	Fin|Fout|Fmono,	Speed,	Speed,
		0
};

/*
 * Grab control of the IIC/L3 shared pins
 */
static inline void L3_acquirepins(void)
{
	GPSR = (GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
	GPDR |=  (GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
}

/*
 * Release control of the IIC/L3 shared pins
 */
static inline void L3_releasepins(void)
{
	GPDR &= ~(GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
	GPCR = (GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
}

/*
 * Initialize the interface
 */
static void __init L3_init(void)
{
	GAFR &= ~(GPIO_L3_SDA_io | GPIO_L3_SCLK_o | GPIO_L3_MODE_o);
	GPSR = GPIO_L3_MODE_o;
	GPDR |= GPIO_L3_MODE_o;
	L3_releasepins();
}

/*
 * Get a bit. The clock is high on entry and on exit. Data is read after
 * the clock low time has expired.
 */
static inline int L3_getbit(void)
{
	int data;

	GPCR = GPIO_L3_SCLK_o;
	udelay(L3_ClockLowTime);

	data = (GPLR & GPIO_L3_SDA_io) ? 1 : 0;

 	GPSR = GPIO_L3_SCLK_o;
	udelay(L3_ClockHighTime);

	return data;
}

/*
 * Send a bit. The clock is high on entry and on exit. Data is sent only
 * when the clock is low (I2C compatibility).
 */
static inline void L3_sendbit(int bit)
{
	GPCR = GPIO_L3_SCLK_o;

	if (bit & 1)
		GPSR = GPIO_L3_SDA_io;
	else
		GPCR = GPIO_L3_SDA_io;

	/* Assumes L3_DataSetupTime < L3_ClockLowTime */
	udelay(L3_ClockLowTime);

	GPSR = GPIO_L3_SCLK_o;
	udelay(L3_ClockHighTime);
}

/*
 * Send a byte. The mode line is set or pulsed based on the mode sequence
 * count. The mode line is high on entry and exit. The mod line is pulsed
 * before the second data byte and before ech byte thereafter.
 */
static void L3_sendbyte(char data, int mode)
{
	int i;

	L3_acquirepins();

	switch(mode) {
	    case 0: /* Address mode */
		GPCR = GPIO_L3_MODE_o;
		break;
	    case 1: /* First data byte */
		break;
	    default: /* Subsequent bytes */
		GPCR = GPIO_L3_MODE_o;
		udelay(L3_HaltTime);
		GPSR = GPIO_L3_MODE_o;
		break;
	}

	udelay(L3_ModeSetupTime);

	for (i = 0; i < 8; i++)
		L3_sendbit(data >> i);

	if (mode == 0)  /* Address mode */
		GPSR = GPIO_L3_MODE_o;

	udelay(L3_ModeHoldTime);

	L3_releasepins();
}

/*
 * Get a byte. The mode line is set or pulsed based on the mode sequence
 * count. The mode line is high on entry and exit. The mod line is pulsed
 * before the second data byte and before each byte thereafter. This
 * function is never valid with mode == 0 (address cycle) as the address
 * is always sent on the bus, not read.
 */
static char L3_getbyte(int mode)
{
	char data = 0;
	int i;

	L3_acquirepins();
	GPDR &= ~(GPIO_L3_SDA_io);

	switch(mode) {
	    case 0: /* Address mode - never valid */
		break;
	    case 1: /* First data byte */
		break;
	    default: /* Subsequent bytes */
		GPCR = GPIO_L3_MODE_o;
		udelay(L3_HaltTime);
		GPSR = GPIO_L3_MODE_o;
		break;
	}

	udelay(L3_ModeSetupTime);

	for (i = 0; i < 8; i++)
		data |= (L3_getbit() << i);

	udelay(L3_ModeHoldTime);

	L3_releasepins();

	return data;
}

/*
 * Write data to a device on the L3 bus. The address is passed as well as
 * the data and length. The length written is returned. The register space
 * is encoded in the address (low two bits are set and device address is
 * in the upper 6 bits).
 */
static int L3_write(char addr, char *data, int len)
{
	int mode = 0;
	int bytes = len;

	L3_sendbyte(addr, mode++);
	while(len--)
		L3_sendbyte(*data++, mode++);

	return bytes;
}

/*
 * Read data from a device on the L3 bus. The address is passed as well as
 * the data and length. The length read is returned. The register space
 * is encoded in the address (low two bits are set and device address is
 * in the upper 6 bits).
 */
static int L3_read(char addr, char * data, int len)
{
	int mode = 0;
	int bytes = len;

	L3_sendbyte(addr, mode++);
	while(len--)
		*data++ = L3_getbyte(mode++);

	return bytes;
}

static	void	swab(uchar*);

static	char	Emajor[]	= "soundblaster not responding/wrong version";
static	char	Emode[]		= "illegal open mode";
static	char	Evolume[]	= "illegal volume specifier";

static	int
sbcmd(int val)
{
	int i, s;

	for(i=1<<16; i!=0; i--) {
		s = inb(blaster.wstatus);
		if((s & 0x80) == 0) {
			outb(blaster.write, val);
			return 0;
		}
	}
/*	print("#A: sbcmd (0x%.2x) timeout\n", val);	/**/
	return 1;
}

static	int
sbread(void)
{
	int i, s;

	for(i=1<<16; i!=0; i--) {
		s = inb(blaster.rstatus);
		if((s & 0x80) != 0) {
			return inb(blaster.read);
		}
	}
/*	print("#A: sbread did not respond\n");	/**/
	return -1;
}

static int
ess1688w(int reg, int val)
{
	if(sbcmd(reg) || sbcmd(val))
		return 1;

	return 0;
}

static int
ess1688r(int reg)
{
	if(sbcmd(0xC0) || sbcmd(reg))
		return -1;

	return sbread();
}

static	int
mxcmd(int addr, int val)
{

	outb(blaster.mixaddr, addr);
	outb(blaster.mixdata, val);
	return 1;
}

static	int
mxread(int addr)
{
	int s;

	outb(blaster.mixaddr, addr);
	s = inb(blaster.mixdata);
	return s;
}

static	void
mxcmds(int s, int v)
{

	if(v > 100)
		v = 100;
	if(v < 0)
		v = 0;
	mxcmd(s, (v*255)/100);
}

static	void
mxcmdt(int s, int v)
{

	if(v > 100)
		v = 100;
	if(v <= 0)
		mxcmd(s, 0);
	else
		mxcmd(s, 255-100+v);
}

static	void
mxcmdu(int s, int v)
{

	if(v > 100)
		v = 100;
	if(v <= 0)
		v = 0;
	mxcmd(s, 128-50+v);
}

static	void
mxvolume(void)
{
	int *left, *right;
	int source;

	if(audio.amode == Aread){
		left = audio.livol;
		right = audio.rivol;
	}else{
		left = audio.lovol;
		right = audio.rovol;
	}

	ilock(&blaster);

	mxcmd(0x30, 255);		/* left master */
	mxcmd(0x31, 255);		/* right master */
	mxcmd(0x3f, 0);		/* left igain */
	mxcmd(0x40, 0);		/* right igain */
	mxcmd(0x41, 0);		/* left ogain */
	mxcmd(0x42, 0);		/* right ogain */

	mxcmds(0x32, left[Vaudio]);
	mxcmds(0x33, right[Vaudio]);

	mxcmds(0x34, left[Vsynth]);
	mxcmds(0x35, right[Vsynth]);

	mxcmds(0x36, left[Vcd]);
	mxcmds(0x37, right[Vcd]);

	mxcmds(0x38, left[Vline]);
	mxcmds(0x39, right[Vline]);

	mxcmds(0x3a, left[Vmic]);
	mxcmds(0x3b, left[Vspeaker]);

	mxcmdu(0x44, left[Vtreb]);
	mxcmdu(0x45, right[Vtreb]);

	mxcmdu(0x46, left[Vbass]);
	mxcmdu(0x47, right[Vbass]);

	source = 0;
	if(left[Vsynth])
		source |= 1<<6;
	if(right[Vsynth])
		source |= 1<<5;
	if(left[Vaudio])
		source |= 1<<4;
	if(right[Vaudio])
		source |= 1<<3;
	if(left[Vcd])
		source |= 1<<2;
	if(right[Vcd])
		source |= 1<<1;
	if(left[Vmic])
		source |= 1<<0;
	if(audio.amode == Aread)
		mxcmd(0x3c, 0);		/* output switch */
	else
		mxcmd(0x3c, source);
	mxcmd(0x3d, source);		/* input left switch */
	mxcmd(0x3e, source);		/* input right switch */
	iunlock(&blaster);
}

static	Buf*
getbuf(AQueue *q)
{
	Buf *b;

	ilock(q);
	b = q->first;
	if(b)
		q->first = b->next;
	iunlock(q);

	return b;
}

static	void
putbuf(AQueue *q, Buf *b)
{

	ilock(q);
	b->next = 0;
	if(q->first)
		q->last->next = b;
	else
		q->first = b;
	q->last = b;
	iunlock(q);
}

/*
 * move the dma to the next buffer
 */
static	void
contindma(void)
{
	Buf *b;

	if(!audio.active)
		goto shutdown;

	b = audio.current;
	if(audio.amode == Aread) {
		if(b)	/* shouldn't happen */
			putbuf(&audio.full, b);
		b = getbuf(&audio.empty);
	} else {
		if(b)	/* shouldn't happen */
			putbuf(&audio.empty, b);
		b = getbuf(&audio.full);
	}
	audio.current = b;
	if(b == 0)
		goto shutdown;

	if(dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread) >= 0)
		return;
	print("#A: dmasetup fail\n");
	putbuf(&audio.empty, b);

shutdown:
	dmaend(blaster.dma);
	sbcmd(0xd9);				/* exit at end of count */
	sbcmd(0xd5);				/* pause */
	audio.curcount = 0;
	audio.active = 0;
}

/*
 * cause sb to get an interrupt per buffer.
 * start first dma
 */
static	void
sb16startdma(void)
{
	ulong count;
	int speed;

	ilock(&blaster);
	dmaend(blaster.dma);
	if(audio.amode == Aread) {
		sbcmd(0x42);			/* input sampling rate */
		speed = audio.livol[Vspeed];
	} else {
		sbcmd(0x41);			/* output sampling rate */
		speed = audio.lovol[Vspeed];
	}
	sbcmd(speed>>8);
	sbcmd(speed);

	count = (Bufsize >> 1) - 1;
	if(audio.amode == Aread)
		sbcmd(0xbe);			/* A/D, autoinit */
	else
		sbcmd(0xb6);			/* D/A, autoinit */
	sbcmd(0x30);				/* stereo, 16 bit */
	sbcmd(count);
	sbcmd(count>>8);

	audio.active = 1;
	audio.tottime = todget(nil);
	contindma();
	iunlock(&blaster);
}

static int
ess1688reset(void)
{
	int i;

	outb(blaster.reset, 3);
	delay(1);			/* >3 υs */
	outb(blaster.reset, 0);
	delay(1);

	i = sbread();
	if(i != 0xAA) {
		print("#A: no response 0x%.2x\n", i);
		return 1;
	}

	if(sbcmd(0xC6)){		/* extended mode */
		print("#A: barf 3\n");
		return 1;
	}

	return 0;
}

static	void
ess1688startdma(void)
{
	ulong count;
	int speed, x;

	ilock(&blaster);
	dmaend(blaster.dma);

	if(audio.amode == Awrite)
		ess1688reset();
	if(audio.amode == Aread)
		sbcmd(0xD3);			/* speaker off */

	/*
	 * Set the speed.
	 */
	if(audio.amode == Aread)
		speed = audio.livol[Vspeed];
	else
		speed = audio.lovol[Vspeed];
	if(speed < 4000)
		speed = 4000;
	else if(speed > 48000)
		speed = 48000;

	if(speed > 22000)
		  x = 0x80|(256-(795500+speed/2)/speed);
	else
		  x = 128-(397700+speed/2)/speed;
	ess1688w(0xA1, x & 0xFF);

	speed = (speed * 9) / 20;
	x = 256 - 7160000 / (speed * 82);
	ess1688w(0xA2, x & 0xFF);

	if(audio.amode == Aread)
		ess1688w(0xB8, 0x0E);		/* A/D, autoinit */
	else
		ess1688w(0xB8, 0x04);		/* D/A, autoinit */
	x = ess1688r(0xA8) & ~0x03;
	ess1688w(0xA8, x|0x01);			/* 2 channels */
	ess1688w(0xB9, 2);			/* demand mode, 4 bytes per request */

	if(audio.amode == Awrite)
		ess1688w(0xB6, 0);
	ess1688w(0xB7, 0x71);
	ess1688w(0xB7, 0xBC);

	x = ess1688r(0xB1) & 0x0F;
	ess1688w(0xB1, x|0x50);
	x = ess1688r(0xB2) & 0x0F;
	ess1688w(0xB2, x|0x50);
	if(audio.amode == Awrite)
		sbcmd(0xD1);			/* speaker on */

	count = -Bufsize;
	ess1688w(0xA4, count & 0xFF);
	ess1688w(0xA5, (count>>8) & 0xFF);
	x = ess1688r(0xB8);
	ess1688w(0xB8, x|0x05);

	audio.active = 1;
	audio.tottime = todget(nil);
	contindma();
	iunlock(&blaster);
}

/*
 * if audio is stopped,
 * start it up again.
 */
static	void
pokeaudio(void)
{
	if(!audio.active)
		blaster.startdma();
}

static void
sb16intr(void)
{
	int stat, dummy;

	stat = mxread(0x82) & 7;		/* get irq status */
	if(stat) {
		dummy = 0;
		if(stat & 2) {
			ilock(&blaster);
			dummy = inb(blaster.clri16);
			audio.totcount += Bufsize;
			audio.tottime = todget(nil);
			contindma();
			iunlock(&blaster);
			audio.intr = 1;
			wakeup(&audio.vous);
		}
		if(stat & 1) {
			dummy = inb(blaster.clri8);
		}
		if(stat & 4) {
			dummy = inb(blaster.clri401);
		}
		USED(dummy);
	}
}

static void
ess1688intr(void)
{
	int dummy;

	if(audio.active){
		ilock(&blaster);
		audio.totcount += Bufsize;
		audio.tottime = todget(nil);
		contindma();
		dummy = inb(blaster.clri8);
		iunlock(&blaster);
		audio.intr = 1;
		wakeup(&audio.vous);
		USED(dummy);
	}
	else
		print("#A: unexpected ess1688 interrupt\n");
}

void
audiosbintr(void)
{
	/*
	 * Carrera interrupt interface.
	 */
	blaster.intr();
}

static void
pcaudiosbintr(Ureg*, void*)
{
	/*
	 * x86 interrupt interface.
	 */
	blaster.intr();
}

void
audiodmaintr(void)
{
/*	print("#A: dma interrupt\n");	/**/
}

static int
anybuf(void*)
{
	return audio.intr;
}

/*
 * wait for some output to get
 * empty buffers back.
 */
static void
waitaudio(void)
{

	audio.intr = 0;
	pokeaudio();
	tsleep(&audio.vous, anybuf, 0, 10*1000);
	if(audio.intr == 0) {
/*		print("#A: audio timeout\n");	/**/
		audio.active = 0;
		pokeaudio();
	}
}

static void
sbbufinit(void)
{
	int i;
	void *p;

	for(i=0; i<Nbuf; i++) {
		p = xspanalloc(Bufsize, CACHELINESZ, 64*1024);
		dcflush(p, Bufsize);
		audio.buf[i].virt = UNCACHED(uchar, p);
		audio.buf[i].phys = (ulong)PADDR(p);
	}
}

static	void
setempty(void)
{
	int i;

	ilock(&blaster);
	audio.empty.first = 0;
	audio.empty.last = 0;
	audio.full.first = 0;
	audio.full.last = 0;
	audio.current = 0;
	audio.filling = 0;
	for(i=0; i<Nbuf; i++)
		putbuf(&audio.empty, &audio.buf[i]);
	audio.totcount = 0;
	audio.tottime = 0LL;
	iunlock(&blaster);
}

static	void
resetlevel(void)
{
	int i;

	for(i=0; volumes[i].name; i++) {
		audio.lovol[i] = volumes[i].ilval;
		audio.rovol[i] = volumes[i].irval;
		audio.livol[i] = volumes[i].ilval;
		audio.rivol[i] = volumes[i].irval;
	}
}

static int
ess1688(ISAConf* sbconf)
{
	int i, major, minor;

	/*
	 * Try for ESS1688.
	 */
	sbcmd(0xE7);			/* get version */
	major = sbread();
	minor = sbread();
	if(major != 0x68 || minor != 0x8B){
		print("#A: model 0x%.2x 0x%.2x; not ESS1688 compatible\n", major, minor);
		return 1;
	}

	ess1688reset();

	switch(sbconf->irq){
	case 2:
	case 9:
		i = 0x50|(0<<2);
		break;
	case 5:
		i = 0x50|(1<<2);
		break;
	case 7:
		i = 0x50|(2<<2);
		break;
	case 10:
		i = 0x50|(3<<2);
		break;
	default:
		print("#A: bad ESS1688 irq %lud\n", sbconf->irq);
		return 1;
	}
	ess1688w(0xB1, i);

	switch(sbconf->dma){
	case 0:
		i = 0x50|(1<<2);
		break;
	case 1:
		i = 0xF0|(2<<2);
		break;
	case 3:
		i = 0x50|(3<<2);
		break;
	default:
		print("#A: bad ESS1688 dma %lud\n", sbconf->dma);
		return 1;
	}
	ess1688w(0xB2, i);

	ess1688reset();

	blaster.startdma = ess1688startdma;
	blaster.intr = ess1688intr;

	return 0;
}

static void
audioinit(void)
{
	int err;
   
	/* Acquire and initialize DMA */
	if( audio_init_dma( &output_stream, "UDA1341 DMA out" ) ||
	    audio_init_dma( &input_stream, "UDA1341 DMA in" ) ){
		audio_clear_dma( &output_stream );
		audio_clear_dma( &input_stream );
		return -EBUSY;
	}

	L3_init();
	audio_ssp_init();

        audio_uda1341_reset();

	init_waitqueue_head(&audio_waitq);

	/* Set some default mixer values... */
	STATUS_1.DAC_gain = 1;
	STATUS_1.ADC_gain = 1;
	L3_write( (UDA1341_L3Addr<<2)|UDA1341_STATUS, (char*)&STATUS_1, 1 );
	DATA0_0.volume = 15;
	L3_write( (UDA1341_L3Addr<<2)|UDA1341_DATA0, (char*)&DATA0_0, 1 );
	DATA0_2.mode = 3;
	L3_write( (UDA1341_L3Addr<<2)|UDA1341_DATA0, (char*)&DATA0_2, 1 );
	DATA0_ext2.mixer_mode = 2;
	DATA0_ext2.mic_level = 4;
	L3_write( (UDA1341_L3Addr<<2)|UDA1341_DATA0, (char*)&DATA0_ext2, 2 );
	DATA0_ext4.AGC_ctrl = 1;
	L3_write( (UDA1341_L3Addr<<2)|UDA1341_DATA0, (char*)&DATA0_ext4, 2 );
	DATA0_ext6.AGC_level = 3;
	L3_write( (UDA1341_L3Addr<<2)|UDA1341_DATA0, (char*)&DATA0_ext6, 2 );

	/* register devices */
	audio_dev_dsp = register_sound_dsp(&UDA1341_dsp_fops, -1);
	audio_dev_mixer = register_sound_mixer(&UDA1341_mixer_fops, -1);

	printk( AUDIO_NAME_VERBOSE " initialized\n" );

	return 0;
}
.
## diffname bitsy/devuda1341.c 2000/1104
## diff -e /n/emeliedump/2000/1103/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1104/sys/src/9/bitsy/devuda1341.c
563,944d
367,536d
361,363d
233c
static void L3_sendbit(int bit)
.
214c
static int L3_getbit(void)
.
193c
static void L3_releasepins(void)
.
184c
static void L3_acquirepins(void)
.
156a
} input, output;

static	struct
{
	QLock;
	Rendez	vous;
	int	bufinit;	/* boolean if buffers allocated */
	int	curcount;	/* how much data in current buffer */
	int	active;		/* boolean dma running */
	int	intr;		/* boolean an interrupt has happened */
	int	amode;		/* Aclosed/Aread/Awrite for /audio */
	int	rivol[Nvol];		/* right/left input/output volumes */
	int	livol[Nvol];
	int	rovol[Nvol];
	int	lovol[Nvol];
	int	major;		/* SB16 major version number (sb 4) */
	int	minor;		/* SB16 minor version number */
	ulong	totcount;	/* how many bytes processed since open */
	vlong	tottime;	/* time at which totcount bytes were processed */

	Buf	buf[Nbuf];	/* buffers and queues */
	AQueue	empty;
	AQueue	full;
	Buf*	current;
	Buf*	filling;
.
133a

.
127a

.
## diffname bitsy/devuda1341.c 2000/1107
## diff -e /n/emeliedump/2000/1104/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1107/sys/src/9/bitsy/devuda1341.c
462a

static void
audioinit(void)
{}


static Chan*
audioattach(char *param)
{
	return devattach('A', param);
}

static int
audiowalk(Chan *c, char *name)
{
	return devwalk(c, name, audiodir, nelem(audiodir), devgen);
}

static void
audiostat(Chan *c, char *db)
{
	devstat(c, db, audiodir, nelem(audiodir), devgen);
}

static Chan*
audioopen(Chan *c, int omode)
{
	int amode;

	switch(c->qid.path & ~CHDIR) {
	default:
		error(Eperm);
		break;

	case Qstatus:
		if((omode&7) != OREAD)
			error(Eperm);
	case Qvolume:
	case Qdir:
		break;

	case Qaudio:
		amode = Awrite;
		switch (omode & 0x7)
		case OREAD:
			break;
		case OWRITE
		qlock(&audio);
		if(audio.amode != Aclosed){
			qunlock(&audio);
			error(Einuse);
		}
		if(audio.bufinit == 0) {
			audio.bufinit = 1;
			sbbufinit();
		}
		audio.amode = amode;
		setempty();
		audio.curcount = 0;
		qunlock(&audio);
		mxvolume();
		break;
	}
	c = devopen(c, omode, audiodir, nelem(audiodir), devgen);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;

	return c;
}

static void
audioclose(Chan *c)
{
	Buf *b;

	switch(c->qid.path & ~CHDIR) {
	default:
		error(Eperm);
		break;

	case Qdir:
	case Qvolume:
	case Qstatus:
		break;

	case Qaudio:
		if(c->flag & COPEN) {
			qlock(&audio);
			if(audio.amode == Awrite) {
				/* flush out last partial buffer */
				b = audio.filling;
				if(b) {
					audio.filling = 0;
					memset(b->virt+audio.curcount, 0, Bufsize-audio.curcount);
					swab(b->virt);
					putbuf(&audio.full, b);
				}
				if(!audio.active && audio.full.first)
					pokeaudio();
			}
			audio.amode = Aclosed;
			if(waserror()){
				qunlock(&audio);
				nexterror();
			}
			while(audio.active)
				waitaudio();
			setempty();
			poperror();
			qunlock(&audio);
		}
		break;
	}
}

static long
audioread(Chan *c, void *v, long n, vlong off)
{
	int liv, riv, lov, rov;
	long m, n0;
	char buf[300];
	Buf *b;
	int j;
	ulong offset = off;
	char *a;

	n0 = n;
	a = v;
	switch(c->qid.path & ~CHDIR) {
	default:
		error(Eperm);
		break;

	case Qdir:
		return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);

	case Qaudio:
		if(audio.amode != Aread)
			error(Emode);
		qlock(&audio);
		if(waserror()){
			qunlock(&audio);
			nexterror();
		}
		while(n > 0) {
			b = audio.filling;
			if(b == 0) {
				b = getbuf(&audio.full);
				if(b == 0) {
					waitaudio();
					continue;
				}
				audio.filling = b;
				swab(b->virt);
				audio.curcount = 0;
			}
			m = Bufsize-audio.curcount;
			if(m > n)
				m = n;
			memmove(a, b->virt+audio.curcount, m);

			audio.curcount += m;
			n -= m;
			a += m;
			if(audio.curcount >= Bufsize) {
				audio.filling = 0;
				putbuf(&audio.empty, b);
			}
		}
		poperror();
		qunlock(&audio);
		break;

	case Qstatus:
		buf[0] = 0;
		snprint(buf, sizeof(buf), "bytes %lud\ntime %lld\n",
			audio.totcount, audio.tottime);
		return readstr(offset, a, n, buf);

	case Qvolume:
		j = 0;
		buf[0] = 0;
		for(m=0; volumes[m].name; m++){
			liv = audio.livol[m];
			riv = audio.rivol[m];
			lov = audio.lovol[m];
			rov = audio.rovol[m];
			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
				else{
					if(volumes[m].flag & Fin)
						j += snprint(buf+j, sizeof(buf)-j,
							" in %d", liv);
					if(volumes[m].flag & Fout)
						j += snprint(buf+j, sizeof(buf)-j,
							" out %d", lov);
				}
			}else{
				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
				    liv==lov && riv==rov)
					j += snprint(buf+j, sizeof(buf)-j,
						" left %d right %d",
						liv, riv);
				else{
					if(volumes[m].flag & Fin)
						j += snprint(buf+j, sizeof(buf)-j,
							" in left %d right %d",
							liv, riv);
					if(volumes[m].flag & Fout)
						j += snprint(buf+j, sizeof(buf)-j,
							" out left %d right %d",
							lov, rov);
				}
			}
			j += snprint(buf+j, sizeof(buf)-j, "\n");
		}
		return readstr(offset, a, n, buf);
	}
	return n0-n;
}

static long
audiowrite(Chan *c, void *vp, long n, vlong)
{
	long m, n0;
	int i, nf, v, left, right, in, out;
	char buf[255], *field[Ncmd];
	Buf *b;
	char *a;

	a = vp;
	n0 = n;
	switch(c->qid.path & ~CHDIR) {
	default:
		error(Eperm);
		break;

	case Qvolume:
		v = Vaudio;
		left = 1;
		right = 1;
		in = 1;
		out = 1;
		if(n > sizeof(buf)-1)
			n = sizeof(buf)-1;
		memmove(buf, a, n);
		buf[n] = '\0';

		nf = getfields(buf, field, Ncmd, 1, " \t\n");
		for(i = 0; i < nf; i++){
			/*
			 * a number is volume
			 */
			if(field[i][0] >= '0' && field[i][0] <= '9') {
				m = strtoul(field[i], 0, 10);
				if(left && out)
					audio.lovol[v] = m;
				if(left && in)
					audio.livol[v] = m;
				if(right && out)
					audio.rovol[v] = m;
				if(right && in)
					audio.rivol[v] = m;
				mxvolume();
				goto cont0;
			}

			for(m=0; volumes[m].name; m++) {
				if(strcmp(field[i], volumes[m].name) == 0) {
					v = m;
					in = 1;
					out = 1;
					left = 1;
					right = 1;
					goto cont0;
				}
			}

			if(strcmp(field[i], "reset") == 0) {
				resetlevel();
				mxvolume();
				goto cont0;
			}
			if(strcmp(field[i], "in") == 0) {
				in = 1;
				out = 0;
				goto cont0;
			}
			if(strcmp(field[i], "out") == 0) {
				in = 0;
				out = 1;
				goto cont0;
			}
			if(strcmp(field[i], "left") == 0) {
				left = 1;
				right = 0;
				goto cont0;
			}
			if(strcmp(field[i], "right") == 0) {
				left = 0;
				right = 1;
				goto cont0;
			}
			error(Evolume);
			break;
		cont0:;
		}
		break;

	case Qaudio:
		if(audio.amode != Awrite)
			error(Emode);
		qlock(&audio);
		if(waserror()){
			qunlock(&audio);
			nexterror();
		}
		while(n > 0) {
			b = audio.filling;
			if(b == 0) {
				b = getbuf(&audio.empty);
				if(b == 0) {
					waitaudio();
					continue;
				}
				audio.filling = b;
				audio.curcount = 0;
			}

			m = Bufsize-audio.curcount;
			if(m > n)
				m = n;
			memmove(b->virt+audio.curcount, a, m);

			audio.curcount += m;
			n -= m;
			a += m;
			if(audio.curcount >= Bufsize) {
				audio.filling = 0;
				swab(b->virt);
				putbuf(&audio.full, b);
			}
		}
		poperror();
		qunlock(&audio);
		break;
	}
	return n0 - n;
}

Dev audiodevtab = {
	'A',
	"audio",

	devreset,
	audioinit,
	audioattach,
	devclone,
	audiowalk,
	audiostat,
	audioopen,
	devcreate,
	audioclose,
	audioread,
	devbread,
	audiowrite,
	devbwrite,
	devremove,
	devwstat,
};
.
## diffname bitsy/devuda1341.c 2000/1108
## diff -e /n/emeliedump/2000/1107/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1108/sys/src/9/bitsy/devuda1341.c
515,517c
		if (omode & AudioIn) {
			/* read */
			audio.amode |= AudioIn;
			if((audio.bufinit & AudioIn) == 0) {
				audio.bufinit |= AudioIn;
				sbbufinit();
			}
		}
		if (omode & 0x2) {
			/* write */
			audio.amode |= 0x2;
.
511c
		omode++;
		if(audio.amode & omode){
.
505,509c
		if ((omode & 0x7) > 2)
			error(Ebadarg);
.
## diffname bitsy/devuda1341.c 2000/1109
## diff -e /n/emeliedump/2000/1108/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1109/sys/src/9/bitsy/devuda1341.c
816c
		qunlock(a);
.
808,812c
			p += m;
			if(a->filling->nbytes >= Bufsize) {
				sendaudio(a);
				a->filling++;
				if (a->filling == &a->buf[Nbuf])
					a->filling = a->buf;
.
806c
			a->filling->nbytes += m;
.
804c
			memmove(a->filling->buf + a->filling->nbytes, p, m);
.
801c
			m = Bufsize - a->filling->nbytes;
.
790,799c
			/* wait if dma in progress */
			while (a->filling->active)
				waitaudio(a);
.
786c
			qunlock(a);
.
784c
		a = &audio.o;
		qlock(a);
.
782c
		if((audio.amode & Awrite) == 0)
.
717c
		memmove(buf, p, n);
.
702c
	p = vp;
.
700c
	char *p;
	IOstate *a;
.
567,568d
560,564c
				b = audio.o.filling;
				if(audio.o.buf[b].count) {
.
558c
			if(audio.amode & Awrite) {
.
529c
//		mxvolume();
.
525,527d
523c
			audio.amode |= Awrite;
			if(audio.o.bufinit == 0)
				bufinit(&audio.o);
			setempty(&audio.o);
.
515,519c
			audio.amode |= Aread;
			if(audio.i.bufinit == 0)
				bufinit(&audio.i);
			setempty(&audio.i);
.
513c
		if (omode & Aread) {
.
508d
505c
		omode = (omode & 0x7) + 1;
		if (omode & ~(Aread | Awrite))
.
490d
468d
465,466c
sendaudio(IOstat *b) {
	if (dmastart(b->chan, b->current->virt, b->current->nbytes)) {
		b->current->active++;
		b->current++;
		if (b->current == &b->buf[Nbuf])
			b->current == &b->buf[0];
	}
}
.
409,416c
	for (i = 0; i < Nbuf; i++) {
		b->buf[i].nbytes = 0;
		b->buf[i].active = 0;
	}
	b->filling = b->buf;
	b->current = b->buf;
.
407a
	int i;
.
402,406c
static void
setempty(IOstate *b)
.
396,400c
	for (i = 0; i < Nbuf; i++)
		b->buf[i].virt = xalloc(Bufsize);
	b->bufinit = 1;
};
.
394c
	int i;
.
391,392c
static void
bufinit(IOstate *b)
.
164,183c
	int		amode;			/* Aclosed/Aread/Awrite for /audio */
	int		intr;			/* boolean an interrupt has happened */
	int		rivol[Nvol];	/* right/left input/output volumes */
	int		livol[Nvol];
	int		rovol[Nvol];
	int		lovol[Nvol];
	ulong	totcount;		/* how many bytes processed since open */
	vlong	tottime;		/* time at which totcount bytes were processed */
	IOstate	i;
	IOstate	o;
.
154,160d
140,152c
	int		bufinit;		/* boolean, if buffers allocated */
	Buf		buf[Nbuf];		/* buffers and queues */
	Buf		*current;		/* next candidate for dma */
	Buf		*filling;		/* buffer being filled */
};
.
131,137d
129c
struct	IOstate
.
125,126c
	uint	nbytes;
.
123a
	int		active;			/* dma running */
.
107,108c
	Bufsize		= 8*1024,	/* 46 ms each */
	Nbuf		= 32,		/* 1.5 seconds total */
.
79a
typedef struct	IOstate IOstate;
.
## diffname bitsy/devuda1341.c 2000/1110
## diff -e /n/emeliedump/2000/1109/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1110/sys/src/9/bitsy/devuda1341.c
770a
				sendaudio(a);
.
767d
755,756c
			while (a->active && a->filling == a->current)
				sleep(&a->vous, audioqnotfull, a);
.
569,602d
540a
			qunlock(&audio.o);
.
537,539c
			if (audio.o.chan == c) {
				/* closing the write end */
				audio.amode &= ~Awrite;

				while (audio.o.filling->nbytes) {
					audio.o.filling++;
					if (audio.o.filling == &audio.o.buf[Nbuf])
						audio.o.filling = &audio.o.buf[0];
					sendaudio(&audio.o);
				}
				while(!audioidle(&audio.o))
					sleep(&audio.o.vous, audioidle, &audio.o);
				audioamplifier(0);
				setempty(&audio.o);
			}
			if (audio.i.chan == c) {
				/* closing the read end */
				audio.amode &= ~Aread;
				setempty(&audio.i);
			}
			if (audio.amode == 0) {
				/* turn audio off */
				audiopower(0);
			}
.
533a
				qunlock(&audio.o);
.
525,532c
			qlock(&audio.o);
.
510c
	IOstate *b;
.
493a
			audio.o.chan = c;
			&audio.o.dma = dmaalloc(0, 0, 8, 2, AudioDMA, void *port, void (*f)(int, ulong))
.
489a
			amplifierpower(1);
			audiomute(0);
.
485a
			audio.i.chan = c;
.
480a
		audiopower(1);
.
474c
//		if (omode & ~(Aread | Awrite))
		if (omode & ~(Awrite))
.
435a
	wakeup(s->vous);
.
434a
		if (canlock(s)) {
			sendaudio(s);
			unlock(s);
		}
.
430,432c
	/* call this with lock held */
	while (b->next != b->filling) {
		assert(b->next->nbytes);
		if (dmastart(b->dma, b->next->virt, b->next->nbytes) == 0)
			break;
		incref(&b->active);
		b->next++;
		if (b->next == &b->buf[Nbuf])
			b->next == &b->buf[0];
	}
}

static void
audiointr(void *x, ulong state) {
	IOstate *s = x;

	if (s == &audio.o) {
		decref(&s->active);
		/* Only interrupt routine touches s->current */
		s->current->nbytes = 0;
		s->current++;
.
423,424d
419,421c
	print("audio enabled\n");
.
402a
	sspregs->control0 = 0x10<<0 | 0x1<<4 | 1<<7 | 0x8<<8;
	sspregs->control1 = 0<<3 + 0<<4 + 1<<5;

	/* Enable the audio power */
          set_bitsy_egpio(EGPIO_BITSY_CODEC_NRESET|EGPIO_BITSY_AUD_AMP_ON|EGPIO_BITSY_AUD_PWR_ON);
          clr_bitsy_egpio(EGPIO_BITSY_QMUTE);
	  gpioregs->direction |=  (GPIO_BITSY_CLK_SET0|GPIO_BITSY_CLK_SET1);
	  /* external clock configured for 44100 samples/sec */
          GPSR =   GPIO_BITSY_CLK_SET0;
          GPCR =   GPIO_BITSY_CLK_SET1;

 
        printk("gpioregs->altfunc=%08x gpioregs->direction=%08x GPLR=%08x\n", gpioregs->altfunc, gpioregs->direction, GPLR);
        printk("PPDR=%08x PPSR=%08x PPAR=%08x PPFR=%08x\n", PPDR, PPSR, PPAR, PPFR);

	/* Wait for the UDA1341 to wake up */
	mdelay(100);
        printk("Ser4SSSR=%08x\n", Ser4SSSR);

    audio_uda1341_reset();

.
401c
    gpioregs->altfunc &= ~(GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM);
	gpioregs->altfunc |= (GPIO_SSP_CLK);
	gpioregs->direction &= ~(GPIO_SSP_CLK);
.
399c
	/* Setup the uarts */
    ppcregs->assignment &= ~(1<<18);	/* Could be the other way round! */
.
397d
387,394d
385a
	/* do nothing */
}

static void
audioenable(void)
{
.
382a
static int
audioqnotfull(IOstate *s)
{
	/* called with lock */
	return s->active == 0 || s->filling != s->current;
}

static int
audioqnotempty(IOstate *s)
{
	/* called with lock */
	return s->next != s->filling;
}

static int
audioidle(IOstate *s)
{
	/* called with lock */
	return s->active == 0;
}

.
380a
	b->next = b->buf;
.
345c
static int
L3_read(char addr, char * data, int len)
.
327c
static int
L3_write(char addr, char *data, int len)
.
314c
	µdelay(L3_ModeHoldTime);
.
309c
	µdelay(L3_ModeSetupTime);
.
302,305c
	default: /* Subsequent bytes */
		gpioregs->clear = GPIO_L3_MODE_o;
		µdelay(L3_HaltTime);
		gpioregs->set = GPIO_L3_MODE_o;
.
300c
	case 1: /* First data byte */
.
298c
	case 0: /* Address mode - never valid */
.
295c
	gpioregs->direction &= ~(GPIO_L3_SDA_io);
.
289c
static char
L3_getbyte(int mode)
.
277c
	µdelay(L3_ModeHoldTime);
.
275c
		gpioregs->set = GPIO_L3_MODE_o;
.
269c
	µdelay(L3_ModeSetupTime);
.
262,265c
	default: /* Subsequent bytes */
		gpioregs->clear = GPIO_L3_MODE_o;
		µdelay(L3_HaltTime);
		gpioregs->set = GPIO_L3_MODE_o;
.
260c
	case 1: /* First data byte */
.
257,258c
	case 0: /* Address mode */
		gpioregs->clear = GPIO_L3_MODE_o;
.
250c
static void
L3_sendbyte(char data, int mode)
.
241,242c
	gpioregs->set = GPIO_L3_SCLK_o;
	µdelay(L3_ClockHighTime);
.
239c
	µdelay(L3_ClockLowTime);
.
236c
		gpioregs->clear = GPIO_L3_SDA_io;
.
234c
		gpioregs->set = GPIO_L3_SDA_io;
.
231c
	gpioregs->clear = GPIO_L3_SCLK_o;
.
229c
static void
L3_sendbit(int bit)
.
219,220c
 	gpioregs->set = GPIO_L3_SCLK_o;
	µdelay(L3_ClockHighTime);
.
217c
	data = (gpioregs->level & GPIO_L3_SDA_io) ? 1 : 0;
.
214,215c
	gpioregs->clear = GPIO_L3_SCLK_o;
	µdelay(L3_ClockLowTime);
.
210c
static int
L3_getbit(void)
.
200,202c
	gpioregs->altfunc &= ~(GPIO_L3_SDA_io | GPIO_L3_SCLK_o | GPIO_L3_MODE_o);
	gpioregs->set = GPIO_L3_MODE_o;
	gpioregs->direction |= GPIO_L3_MODE_o;
.
198c
static void 
L3_init(void)
.
191,192c
	gpioregs->direction &= ~(GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
	gpioregs->clear = (GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
.
189c
static void
L3_releasepins(void)
.
182,183c
	gpioregs->set = (GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
	gpioregs->direction |=  (GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
.
180c
static void
L3_acquirepins(void)
.
133,137c
	Rendez			vous;
	Chan			*chan;			/* chan of open */
	int				dma;			/* dma chan, alloc on open, free on close */
	int				bufinit;		/* boolean, if buffers allocated */
	Buf				buf[Nbuf];		/* buffers and queues */
	int				active;			/* # of dmas in progress */
	volatile Buf	*current;		/* next dma to finish */
	volatile Buf	*next;			/* next candidate for dma */
	volatile Buf	*filling;		/* buffer being filled */
.
125d
19a
#include	"sa1110dma.h"
.
## diffname bitsy/devuda1341.c 2000/1111
## diff -e /n/emeliedump/2000/1110/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1111/sys/src/9/bitsy/devuda1341.c
844c
Dev uda1341devtab = {
.
825c
			memmove(a->filling->virt + a->filling->nbytes, p, m);
.
819c
			while (a->active.ref && a->filling == a->current)
.
778,779c
//				resetlevel();
//				mxvolume();
.
762d
725d
651d
625c
				amplifierpower(0);
.
592d
575c
			audio.o.dma = dmaalloc(0, 0, 8, 2, AudioDMA, Port4MCP, audiointr, (void*)&audio.o);
.
568,569c
//			amplifierpower(1);
//			audiomute(0);
.
557c
		audioenable();
.
510c
	wakeup(&s->vous);
.
507c
			unlock((Lock*)s);
.
503,505c
		if (s->current == &s->buf[Nbuf])
			s->current = &s->buf[0];
		if (canlock((Lock*)s)) {
.
495c
audiointr(void *x, ulong) {
.
490c
			b->next = &b->buf[0];
.
481c
sendaudio(IOstate *b) {
.
477c
	/* write uda 1341 status[1] */
	data[0] = 0x80 | 0x3<<UdaStatusPC | 1<<UdaStatusIGS | 1<<UdaStatusOGS;
	L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1);

	/* write uda 1341 data0[0] (volume) */
	data[0] = 0x00 | /* volume */ 15 & 0x3f;	/* 6 bits, others must be 0 */
	L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 1);

	/* write uda 1341 data0[2] (mode switch) */
	data[0] = 0x80 | 3;	/* mode = 3 */
	L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 1);

	/* set mixer mode and level */
	data[0] = 0xc0 | 2 /* ext address */;
	data[1] = 0xe0 | 2 | 4 << 2;	/* mixer mode and mic level */
	L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2);

	/* set agc control and input amplifier gain */
	data[0] = 0xc0 | 4 /* ext address */;
	data[1] = 0xe0 | 1<<4 | 3;	/* AGC control and input ampl gain */
	L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2 );

	/* set agc time constant and output level */
	data[0] = 0xc0 | 6 /* ext address */;
	data[1] = 0xe0 | 0<<2 | 3;	/* agc time constant and output level */
	L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2 );

	print("audio enabled\n");
.
475c
	data[0] = 2<<4 /* system clock */ | 1<<1 /* lsb16 */;
	L3_write( (UDA1341_L3Addr<<2)|UDA1341_STATUS, data, 1 );
	/* Reset done */
.
459,473c
    gpioregs->clear = EGPIO_codec_reset;
    gpioregs->set = EGPIO_codec_reset;
.
457c
	/* Reset the chip */
	data[0] = 2<<4 /* system clock */ | 1<<1 /* lsb16 */ | 1<<6 /* reset */;
	L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1 );
.
454,455c
	delay(100);
    print("Ser4SSSR=0x%lux\n", sspregs->status);
.
450,452d
448a
	gpioregs->direction |=  (GPIO_CLK_SET0_o|GPIO_CLK_SET1_o);
	  /* external clock configured for 44100 samples/sec */
    gpioregs->set = GPIO_CLK_SET0_o;
    gpioregs->clear = GPIO_CLK_SET1_o;
.
442,447c
	audiopower(1);
	amplifierpower(1);
	audiomute(0);
.
434,436c
    gpioregs->altfunc &= ~(GPIO_SSP_TXD_o | GPIO_SSP_RXD_i | GPIO_SSP_SCLK_o | GPIO_SSP_SFRM_o);
	gpioregs->altfunc |= (GPIO_SSP_CLK_i);
	gpioregs->direction &= ~(GPIO_SSP_CLK_i);
.
427c
	uchar	data[2];
.
415c
	return s->active.ref == 0;
.
413a
	IOstate *s = x;
.
412c
audioidle(void *x)
.
401c
	return s->active.ref == 0 || s->filling != s->current;
.
399a
	IOstate *s = x;

.
398c
audioqnotfull(void *x)
.
390d
358c
L3_read(uchar addr, uchar *data, int len)
.
339c
L3_write(uchar addr, uchar *data, int len)
.
138c
	Ref				active;			/* # of dmas in progress */
.
77a
#define UDA1341_L3Addr	5
.
74d
## diffname bitsy/devuda1341.c 2000/1113
## diff -e /n/emeliedump/2000/1111/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1113/sys/src/9/bitsy/devuda1341.c
822a
		if (debug) print("#A: write %ld\n", n);
.
596a
		if (debug) print("#A: open done\n");
.
500a
	if (debug) print("#A: sendaudio\n");
.
495c
	if (debug) print("#A: audio enabled\n");
.
387a
	if (debug) print("#A: setempty\n");
.
377a
	if (debug) print("#A: bufinit\n");
.
21a
static int debug = 1;

.
## diffname bitsy/devuda1341.c 2000/1114
## diff -e /n/emeliedump/2000/1113/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1114/sys/src/9/bitsy/devuda1341.c
598c
			audio.o.dma = dmaalloc(0, 0, 8, 2, SSPXmitDMA, Port4SSP, audiointr, (void*)&audio.o);
.
587a
			audio.i.chan = c;
			audio.i.dma = dmaalloc(0, 0, 8, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)&audio.o);
.
586d
## diffname bitsy/devuda1341.c 2000/1115
## diff -e /n/emeliedump/2000/1114/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1115/sys/src/9/bitsy/devuda1341.c
852a
				if (debug > 1) print("#A: filled @%p\n", a->filling);
.
842a
			}
.
841c
			while (a->active.ref && a->filling == a->current) {
				if (debug > 1) print("#A: sleep\n");
.
830c
		if (debug > 1) print("#A: write %ld\n", n);
.
641c
				if (audio.o.filling->nbytes) {
.
628a
		if (debug > 1) print("#A: close\n");
.
602a
		qunlock(&audio);
.
601d
528,531c
		decref(&s->active);
		sendaudio(s);
.
524c
		if (debug > 1) iprint("#A: audio interrupt @%p\n", s->current);
.
522d
514a
	iunlock(&b->ilock);
.
510a
		if (dmastart(b->dma, b->next->virt, b->next->nbytes) == 0) {
			decref(&b->active);
			break;
		}
		if (debug > 1) print("#A: dmastart @%p\n", b->next);
		b->next->nbytes = 0;
.
508,509d
504,505c
	/* interrupt routine calls this too */
	if (debug > 1) print("#A: sendaudio\n");
	ilock(&b->ilock);
.
498a
	/* Reset the chip */
	data[0] = 2<<4 /* system clock */ | 1<<1 /* lsb16 */ | 1<<6 /* reset */;
	L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1 );

	delay(10);

	data[0] = 2<<4 /* system clock */ | 1<<1 /* lsb16 */;
	L3_write( (UDA1341_L3Addr<<2)|UDA1341_STATUS, data, 1 );
	/* Reset done */

.
465,466c
	delay(10);
.
460a
    gpioregs->clear = EGPIO_codec_reset;
    gpioregs->set = EGPIO_codec_reset;

.
446a
	sspregs->control0 |= 1<<7;	/* enable */

.
444c
	sspregs->control0 = 0xf<<0 | 0x1<<4 | 0x3<<8;
.
438c
    ppcregs->assignment &= (1<<18);	/* Could be the other way round! */
.
134a
	Lock			ilock;
.
## diffname bitsy/devuda1341.c 2000/1116
## diff -e /n/emeliedump/2000/1115/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1116/sys/src/9/bitsy/devuda1341.c
860c
			while (!dmaidle(a->dma) && a->filling == a->current) {
.
682a
			print("total dmas: %lud\n", iostats.totaldma);
			print("dmas while idle: %lud\n", iostats.idledma);
			print("dmas while busy: %lud\n", iostats.faildma);
.
669a
				dmafree(audio.o.dma);
.
666,667c
				delay(2000);
		//		dmawait(audio.o.dma);
				if (!dmaidle(audio.o.dma))
					print("dma still busy\n");
.
598a
		memset(&iostats, 0, sizeof(iostats));
.
548,549c
		if (ndma > 0)
			sendaudio(s);
.
539c
audiointr(void *x, ulong ndma) {
.
528a
		iostats.totaldma++;
		if (n == 1) iostats.idledma++;
.
524,526c
		if ((n = dmastart(b->dma, b->next->virt, b->next->nbytes)) == 0) {
			iostats.faildma++;
.
519a
	int n;

.
504,514c
	if (debug) {
		print("#A: audio enabled\n");
		print("\tsspregs->control0 = 0x%lux\n", sspregs->control0);
		print("\tsspregs->control1 = 0x%lux\n", sspregs->control1);
	}
.
482c
	data[0] = 15 & 0x3f;	/* 6 bits, others must be 0 */
.
471,476d
469a
	gpioregs->set = EGPIO_codec_reset;
	gpioregs->clear = EGPIO_codec_reset;
	/* write uda 1341 status[0] */
	data[0] &= ~(1<<UdaStatusRST); /* clear reset */
	L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1 );
.
468c
	data[0] = 2<<UdaStatusSC | 1<<UdaStatusIF | 1<<UdaStatusRST;
.
464,466d
462d
456,459c
/* This is purportedly the wrong way round: 0 should be set, 1 cleared */
    gpioregs->set	= GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
//    gpioregs->clear	= GPIO_CLK_SET1_o;

.
454a
	/* external clock configured for 44100 samples/sec */
.
448,449d
445,446c
	sspregs->control0 = 0;
	sspregs->control0 = 0x031f; /* 16 bits, TI frames, serial clock rate 3 */
	sspregs->control1 = 0x0020; /* ext clock */
	sspregs->control0 = 0x039f;	/* enable */
.
439c
    ppcregs->assignment &= ~(1<<18);
.
410,424d
407c
	return dmaidle(s->dma) || s->filling != s->current;
.
161a
static struct
{
	ulong	bytes;
	ulong	totaldma;
	ulong	idledma;
	ulong	faildma;
} iostats;

.
141d
## diffname bitsy/devuda1341.c 2000/1117
## diff -e /n/emeliedump/2000/1116/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1117/sys/src/9/bitsy/devuda1341.c
810,811c
				resetlevel();
				mxvolume();
.
674a
			print("out of order dma: %lud\n", iostats.samedma);
.
671a
			poperror();
.
669,670d
662a
				indisable();
.
657,658c
				setempty(s);
				dmafree(s->dma);
				qunlock(s);
				poperror();
.
652,655c
				dmawait(s->dma);
				outdisable();
.
646,650c
				if (s->filling->nbytes) {
					/* send remaining partial buffer */
					s->filling++;
					if (s->filling == &s->buf[Nbuf])
						s->filling = &s->buf[0];
					sendaudio(s);
.
642a
				s = &audio.o;
				qlock(s);
				if(waserror()){
					qunlock(s);
					nexterror();
				}
.
638d
636d
620a
	IOstate *s;
.
605c
		mxvolume();
.
599,603c
			if(s->bufinit == 0)
				bufinit(s);
			setempty(s);
			s->chan = c;
			s->dma = dmaalloc(0, 0, 4, 2, SSPXmitDMA, Port4SSP, audiointr, (void*)s);
.
594a
			outenable();
			s = &audio.o;
.
591,592c
			s->chan = c;
			s->dma = dmaalloc(0, 0, 4, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)&audio.o);
.
585a
			inenable();
			s = &audio.i;
.
583c
		enable();
.
559a
	IOstate *s;
.
520c
	iunlock(&s->ilock);
.
513,518c
		switch (n) {
		case 1:
			iostats.idledma++;
			break;
		case 3:
			iostats.faildma++;
			break;
		}
		if (debug > 1) print("#A: dmastart @%p\n", s->next);
		s->next->nbytes = 0;
		s->next++;
		if (s->next == &s->buf[Nbuf])
			s->next = &s->buf[0];
.
505,508c
	ilock(&s->ilock);
	while (s->next != s->filling) {
		assert(s->next->nbytes);
		if ((n = dmastart(s->dma, s->next->virt, s->next->nbytes)) == 0) {
.
500c
outdisable(void) {
	/* turn off DAC, clear output gain switch */
	status1 &= ~0x41;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
	if (debug) {
		print("uchar	status1	= 0x%2.2ux\n", status1);
	}
}

static void
inenable(void) {
	/* turn on ADC, set input gain switch */
	status1 |= 0x22;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
	if (debug) {
		print("uchar	status1	= 0x%2.2ux\n", status1);
	}
}

static void
indisable(void) {
	/* turn off ADC, clear input gain switch */
	status1 &= ~0x22;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
	if (debug) {
		print("uchar	status1	= 0x%2.2ux\n", status1);
	}
}

static void
sendaudio(IOstate *s) {
.
493,495c
		print("uchar	status1	= 0x%2.2ux\n", status1);
		print("uchar	data00	= 0x%2.2ux\n", data00);
.
491a
static void
outenable(void) {
	/* turn on DAC, set output gain switch */
	status1 |= 0x41;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
	/* set volume */
	data00 |= 0xf;
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data00, 1);
.
487,490c
}
.
482,485c
	if(audio.amode & Aread){
		left = audio.livol;
		right = audio.rivol;
	}
	if(audio.amode & Awrite){
		left = audio.lovol;
		right = audio.rovol;
		data00 &= ~0x3f;
		data00 |= ((200-left[Vaudio]-right[Vaudio])*0x3f/200)&0x3f;
		L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data00, 1);
		if (debug) print("uchar	data00	= 0x%2.2ux (audio out)\n", data00);
		if (left[Vtreb]+right[Vtreb] <= 100
		 && left[Vbass]+right[Vbass] <= 100) {
			/* settings neutral */
			data02 &= ~0x03;
			L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data02, 1);
			if (debug) print("uchar	data02	= 0x%2.2ux (mode flat)\n", data02);
		} else {
			data02 |= 0x03;
			L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data02, 1);
			if (debug) print("uchar	data02	= 0x%2.2ux (mode boost)\n", data02);
			data01 |= ~0x3f;
			data01 |= ((left[Vtreb]+right[Vtreb]-100)*0x3/100)&0x03;
			data01 |= (((left[Vbass]+right[Vbass]-100)*0xf/100)&0xf)<<2;
			L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data01, 1);
			if (debug) print("uchar	data01	= 0x%2.2ux (bass&treb)\n", data01);
		}
	}
.
477,480c
static void
mxvolume(void) {
	int *left, *right;
.
473,475c
	for(i=0; volumes[i].name; i++) {
		audio.lovol[i] = volumes[i].ilval;
		audio.rovol[i] = volumes[i].irval;
		audio.livol[i] = volumes[i].ilval;
		audio.rivol[i] = volumes[i].irval;
	}
}
.
469,471c
static	void
resetlevel(void)
{
	int i;
.
465,467c
	if (debug) {
		print("uchar	status0	= 0x%2.2ux\n", status0);
		print("uchar	status1	= 0x%2.2ux\n", status1);
		print("uchar	data02	= 0x%2.2ux\n", data02);
		print("ushort	data0e2	= 0x%4.4ux\n", data0e2);
		print("ushort	data0e4	= 0x%4.4ux\n", data0e4);
		print("ushort	data0e6	= 0x%4.4ux\n", data0e6);
		print("#A: audio enabled\n");
		print("\tsspregs->control0 = 0x%lux\n", sspregs->control0);
		print("\tsspregs->control1 = 0x%lux\n", sspregs->control1);
	}
}
.
462,463c
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status0, 1 );
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data02, 1);
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data0e2, 2);
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data0e4, 2 );
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data0e6, 2 );
.
460a
	gpioregs->set = EGPIO_codec_reset;
.
457,459c
	data = status0 | 1<<UdaStatusRST;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&data, 1 );
.
448,451c
	gpioregs->set	= GPIO_CLK_SET0_o;
	gpioregs->clear	= GPIO_CLK_SET1_o;
.
433,436d
431c
	ppcregs->assignment &= ~(1<<18);
.
426c
	ushort	data;
.
424c
enable(void)
.
422a
uchar	status0	= 0x22;
uchar	status1	= 0x80;
uchar	data00	= 0x00;		/* volume control, bits 0 – 5 */
uchar	data01	= 0x40;
uchar	data02	= 0x90;
ushort	data0e2	= 0xf2c2;
ushort	data0e4	= 0xf3c4;
ushort	data0e6	= 0xe3c6;

.
184,185c
[Vtreb]		"treb",		Fout|Fmono,		50,	50,
[Vbass]		"bass",		Fout|Fmono, 	50,	50,
.
177,182c
[Vaudio]	"audio",	Fout|Fmono,		50,	50,
[Vmic]		"mic",		Fin,			 0,	 0,
.
166a
	ulong	samedma;
.
105d
101,103d
93c
	Fin			= 2,
.
79c
#define UDA1341_L3Addr	0x14
.
50,51c
#define L3_ClockHighTime	10		/* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
#define L3_ClockLowTime		10		/* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
.
44c
 * L3 setup and hold times (expressed in µs)
.
## diffname bitsy/devuda1341.c 2000/1118
## diff -e /n/emeliedump/2000/1117/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1118/sys/src/9/bitsy/devuda1341.c
876a
				if (m < 0 || m > 100) 
					error(Evolume);
.
838c
		return readstr(offset, p, n, buf);
.
797c
		return readstr(offset, p, n, buf);
.
791a
		break;
.
790a
		if (debug > 1) print("#A: read %ld\n", n);
		if((audio.amode & Aread) == 0)
			error(Emode);
		s = &audio.o;
		qlock(s);
		if(waserror()){
			qunlock(s);
			nexterror();
		}
		while(n > 0) {
			/* wait if dma in progress */
			while (!dmaidle(s->dma) && s->emptying == s->next) {
				if (debug > 1) print("#A: sleep\n");
				sleep(&s->vous, audioqnotempty, s);
			}

			m = Bufsize - s->emptying->nbytes;
			if(m > n)
				m = n;
			memmove(p, s->emptying->virt + s->emptying->nbytes, m);

			s->emptying->nbytes -= m;
			n -= m;
			p += m;
			if(s->emptying->nbytes == 0) {
				if (debug > 1) print("#A: emptied @%p\n", s->emptying);
				s->emptying++;
				if (s->emptying == &s->buf[Nbuf])
					s->emptying = s->buf;
				sendaudio(s);
			}
		}
		poperror();
		qunlock(s);
.
788c
		return devdirread(c, p, n, audiodir, nelem(audiodir), devgen);
.
781c
	p = v;
.
778c
	char *p;
	IOstate *s;
.
753c
				setempty(s);
				dmafree(s->dma);
				qunlock(s);
				poperror();
.
751a
				s = &audio.i;
				qlock(s);
				if(waserror()){
					qunlock(s);
					nexterror();
				}
				dmawait(s->dma);
.
731,733d
724a
				/* closing the write end */
				audio.amode &= ~Awrite;
.
673c
			s->dma = dmaalloc(0, 0, 4, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)s);
.
670,671c
				bufinit(s);
			setempty(s);
.
612a
	} else if (s == &audio.i) {
		/* Only interrupt routine touches s->current */
		if (debug > 1) iprint("#A: audio interrupt @%p\n", s->current);
		s->current->nbytes = Bufsize;
		s->current++;
		if (s->current == &s->buf[Nbuf])
			s->current = &s->buf[0];
		if (ndma > 0)
			recvaudio(s);
.
601a
recvaudio(IOstate *s) {
	/* interrupt routine calls this too */
	int n;

	if (debug > 1) print("#A: recvaudio\n");
	ilock(&s->ilock);
	while (s->next != s->emptying) {
		assert(s->next->nbytes == 0);
		if ((n = dmastart(s->dma, s->next->phys, Bufsize)) == 0) {
			iostats.faildma++;
			break;
		}
		iostats.totaldma++;
		switch (n) {
		case 1:
			iostats.idledma++;
			break;
		case 3:
			iostats.faildma++;
			break;
		}
		if (debug > 1) print("#A: dmastart @%p\n", s->next);
		s->next++;
		if (s->next == &s->buf[Nbuf])
			s->next = &s->buf[0];
	}
	iunlock(&s->ilock);
}

static void
.
579c
		if ((n = dmastart(s->dma, s->next->phys, s->next->nbytes)) == 0) {
.
497a
		if (left[Vmic]+right[Vmic] == 0) {
			/* Turn on automatic gain control (AGC) */
			data0e4 |= 0x1000;
			L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data0e4, 2 );
		} else {
			int v;
			/* Turn on manual gain control */
			v = ((left[Vmic]+right[Vmic])*0x7f/200)&0x7f;
			data0e4 &= ~0x1300;
			data0e5 &= ~0x1f00;
			data0e4 |= (v & 0x3)<<8;
			data0e5 |= (v & 0x7c)<<6;
			L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data0e4, 2 );
			L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data0e5, 2 );
		}
		
.
462d
422c
/* there is no data0e3 */
ushort	data0e4	= 0xe0c4;
ushort	data0e5	= 0xe0c5;
.
420a
ushort	data0e0	= 0xe0c0;
ushort	data0e1	= 0xe0c1;
.
406d
401a
audioqnotempty(void *x)
{
	IOstate *s = x;

	return dmaidle(s->dma) || s->emptying != s->next;
}

static int
.
383a
		b->buf[i].phys = PADDR(b->buf[i].virt);
	}
.
382c
	for (i = 0; i < Nbuf; i++) {
.
175c
[Vmic]		"mic",		Fin|Fmono,		 0,	 0,
.
139a
/* just be be cute (and to have defines like linux, a real operating system) */
#define emptying filling
.
124a
	ulong	phys;
.
80a
enum {
	UDA1341_DATA0 =	0,
	UDA1341_DATA1,
	UDA1341_STATUS,
	UDA1341_L3Addr = 0x14,
};

.
76,79d
46,52c
enum {
	L3_DataSetupTime =	1,		/* 190 ns */
	L3_DataHoldTime =	1,		/*  30 ns */
	L3_ModeSetupTime =	1,		/* 190 ns */
	L3_ModeHoldTime =	1,		/* 190 ns */
	L3_ClockHighTime =	10,		/* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
	L3_ClockLowTime =	10,		/* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
	L3_HaltTime =		1,		/* 190 ns */
};
.
## diffname bitsy/devuda1341.c 2000/1120
## diff -e /n/emeliedump/2000/1118/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1120/sys/src/9/bitsy/devuda1341.c
1049a
		mxvolume();
.
1025a
			if(strcmp(field[i], "debug") == 0) {
				debug = debug?0:1;
				goto cont0;
			}
.
1023d
905c
				recvaudio(s);
.
762a
		resetlevel();
.
557a
		if (left[Vfilter]+right[Vfilter] == 0)
			data02 &= ~0x10;
		else
			data02 |= 0x10;
		if (left[Vinvert]+right[Vinvert] == 0)
			status1 &= ~0x14;
		else
			status1 |= 0x14;
		L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
		L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data00, 1);
		L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data01, 1);
		L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data02, 1);
		if (debug) {
			print("uchar	status1	= 0x%2.2ux;\n", status1);
			print("uchar	data00	= 0x%2.2ux; /* audio out */\n", data00);
			print("uchar	data01	= 0x%2.2ux; /* bass&treb */\n", data01);
			print("uchar	data02	= 0x%2.2ux; /* mode */\n", data02);
		}
.
555,556d
550,552c
			data01 &= ~0x3f;
.
546,548c
		else {
.
543c
		 && left[Vbass]+right[Vbass] <= 100)
.
540,541d
514a
	if (debug) print("mxvolume\n");
.
437c
uchar	data02	= 0x80;
.
185,186c
[Vtreb]		"treb",		Fout|Fmono,		 50,	 50,
[Vbass]		"bass",		Fout|Fmono, 	 50,	 50,
[Vfilter]	"filter",	Fout|Fmono,		  0,	  0,
[Vinvert]	"invert",	Fin|Fout|Fmono,	  0,	  0,
.
182,183c
[Vaudio]	"audio",	Fout|Fmono,		 80,	 80,
[Vmic]		"mic",		Fin|Fmono,		  0,	  0,
.
112c
	Bufsize		= 4*1024,	/* 46 ms each */
.
109a
	Vfilter,
	Vinvert,
.
22c
static int debug = 0;
.
## diffname bitsy/devuda1341.c 2000/1121
## diff -e /n/emeliedump/2000/1120/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1121/sys/src/9/bitsy/devuda1341.c
916,922d
903c
			while (!dmaidle(s->dma) && s->emptying == s->current) {
.
901a
			if(s->emptying->nbytes == 0) {
				if (debug > 1) print("#A: emptied @%p\n", s->emptying);
				recvaudio(s);
				s->emptying++;
				if (s->emptying == &s->buf[Nbuf])
					s->emptying = s->buf;
			}
.
895c
		s = &audio.i;
.
784,785c
	c = devopen(c, mode, audiodir, nelem(audiodir), devgen);
	c->mode = openmode(mode);
.
768,770d
765c
		if (omode & Awrite) {
.
761a
			s->emptying = &s->buf[Nbuf-1];
.
759c
			if(s->bufinit == 0)
.
757d
744,745c
		if (omode & ~(Aread | Awrite))
.
728a
	int omode = mode;
.
726c
audioopen(Chan *c, int mode)
.
694,701c
		else if (s == &audio.i)
.
686,692c
	if (debug > 1) iprint("#A: audio interrupt @%p\n", s->current);
	/* Only interrupt routine touches s->current */
	s->current++;
	if (s->current == &s->buf[Nbuf])
		s->current = &s->buf[0];
	if (ndma > 0) {
		if (s == &audio.o)
.
593a
	audiomute(1);
	amplifierpower(0);
.
579a
	amplifierpower(1);
	audiomute(0);
.
467,468d
434c
	/* Turn MCP operations off */
	mcpregs = mapspecial(MCPREGS, 0x34);
	mcpregs->status &= ~(1<<16);

	sspregs = mapspecial(SSPREGS, 32);
.
430a
SSPregs *sspregs;
MCPregs	*mcpregs;

.
420c
	return dmaidle(s->dma) || s->emptying != s->current;
.
22c
static int debug = 2;
.
## diffname bitsy/devuda1341.c 2000/1122
## diff -e /n/emeliedump/2000/1121/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1122/sys/src/9/bitsy/devuda1341.c
1129a
	audiopower,
.
856c
				egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 0);
.
847d
832d
780c
		if (debug) print("open done\n");
.
765c
			s->dma = dmaalloc(1, 0, 4, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)s);
.
695c
	if (debug) {
		if (debug > 1)
			iprint("#A: audio interrupt @%p\n", s->current);
		else
			iprint("-");
	}
.
683c
		if (debug) {
			if (debug > 1)
				print("dmastart @%p\n", s->next);
			else
				iprint("+");
		}
.
652c
		if (debug) {
			if (debug > 1)
				print("dmastart @%p\n", s->next);
			else
				iprint("+");
		}
.
630a
disable(void) {
	egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 0);
}

static void
audiopower(int flag) {
	if (flag) {
		egpiobits(EGPIO_audio_power, 1);
		egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 1);
	} else {
		/* power off */
		egpiobits(EGPIO_audio_power, 0);
		egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 0);
	}
}

static void
.
626c
		print("indisable:	status1	= 0x%2.2ux\n", status1);
.
621a
	dmastop(audio.i.dma);
.
616c
		print("inenable:	status1	= 0x%2.2ux\n", status1);
.
612a
//	egpiobits(EGPIO_audio_power, 1);
//	audiomute(0);
.
607a
	egpiobits(EGPIO_audio_power, 0);
.
606c
		print("outdisable:	status1	= 0x%2.2ux\n", status1);
.
602c
	egpiobits(EGPIO_audio_power, 0);
.
599a
	dmastop(audio.o.dma);
.
593,594c
		print("outenable:	status1	= 0x%2.2ux\n", status1);
		print("outenable:	data00	= 0x%2.2ux\n", data00);
.
585c
	egpiobits(EGPIO_audio_power, 1);
.
579d
573,576c
			print("mxvolume:	status1	= 0x%2.2ux\n", status1);
			print("mxvolume:	data00	= 0x%2.2ux\n", data00);
			print("mxvolume:	data01	= 0x%2.2ux\n", data01);
			print("mxvolume:	data02	= 0x%2.2ux\n", data02);
.
567c
			status1 |= 0x10;
.
565c
			status1 &= ~0x10;
.
543c
		if (left[Vinvert]+right[Vinvert] == 0)
			status1 &= ~0x04;
		else
			status1 |= 0x04;
		L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1);
		if (debug) {
			print("mxvolume:	status1	= 0x%2.2ux\n", status1);
			print("mxvolume:	data0e4	= 0x%4.4ux\n", data0e4);
			print("mxvolume:	data0e5	= 0x%4.4ux\n", data0e5);
		}
.
524d
495,503c
		print("enable:	status0	= 0x%2.2ux\n", status0);
		print("enable:	status1	= 0x%2.2ux\n", status1);
		print("enable:	data02	= 0x%2.2ux\n", data02);
		print("enable:	data0e2	= 0x%4.4ux\n", data0e2);
		print("enable:	data0e4	= 0x%4.4ux\n", data0e4);
		print("enable:	data0e6	= 0x%4.4ux\n", data0e6);
		print("enable:	sspregs->control0 = 0x%lux\n", sspregs->control0);
		print("enable:	sspregs->control1 = 0x%lux\n", sspregs->control1);
.
473c
	egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 1);
.
406c
	if (debug) print("setempty\n");
.
393c
	if (debug) print("bufinit\n");
.
384a
void
audiomute(int on)
{
	egpiobits(EGPIO_audio_mute, on);
}

.
22c
static int debug = 0;
.
## diffname bitsy/devuda1341.c 2000/1123
## diff -e /n/emeliedump/2000/1122/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1123/sys/src/9/bitsy/devuda1341.c
659,660c
		if (audio.omode & Aread)
			indisable();
		if (audio.omode & Awrite)
			outdisable();
		egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset | EGPIO_audio_power, 0);
.
653a
	if (debug) {
		iprint("audiopower %d\n", flag);
	}
.
647,652c
void
.
627,628d
## diffname bitsy/devuda1341.c 2000/1125
## diff -e /n/emeliedump/2000/1123/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1125/sys/src/9/bitsy/devuda1341.c
823a
			audio.amode |= Awrite;
.
813a
			audio.amode |= Aread;
.
807d
733a
void
audiopower(int flag) {
	IOstate *s;

	if (debug) {
		iprint("audiopower %d\n", flag);
	}
	if (flag) {
		/* power on only when necessary */
		if (audio.amode) {
			egpiobits(EGPIO_audio_power | EGPIO_audio_ic_power | EGPIO_codec_reset, 1);
			enable();
			if (audio.amode & Aread) {
				inenable();
				s = &audio.i;
				dmareset(s->dma, 1, 0, 4, 2, SSPRecvDMA, Port4SSP);
				recvaudio(s);
			}
			if (audio.amode & Awrite) {
				outenable();
				s = &audio.o;
				dmareset(s->dma, 0, 0, 4, 2, SSPXmitDMA, Port4SSP);
				sendaudio(s);
			}
			mxvolume();
		}
	} else {
		/* power off */
		if (audio.amode & Aread)
			indisable();
		if (audio.amode & Awrite)
			outdisable();
		egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset | EGPIO_audio_power, 0);
	}
}

.
645,662d
## diffname bitsy/devuda1341.c 2000/1130
## diff -e /n/emeliedump/2000/1125/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1130/sys/src/9/bitsy/devuda1341.c
925,928c
			if (debug) {
				print("total dmas: %lud\n", iostats.totaldma);
				print("dmas while idle: %lud\n", iostats.idledma);
				print("dmas while busy: %lud\n", iostats.faildma);
				print("out of order dma: %lud\n", iostats.samedma);
			}
.
## diffname bitsy/devuda1341.c 2000/1207
## diff -e /n/emeliedump/2000/1130/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1207/sys/src/9/bitsy/devuda1341.c
184,193c
[Vaudio]	{"audio",	Fout|Fmono,	 80,	 80},
[Vmic]		{"mic",		Fin|Fmono,	  0,	  0},
[Vtreb]		{"treb",	Fout|Fmono,	 50,	 50},
[Vbass]		{"bass",	Fout|Fmono, 	 50,	 50},
[Vspeed]	{"speed",	Fin|Fout|Fmono,	Speed,	Speed},
[Vfilter]	{"filter",	Fout|Fmono,	  0,	  0},
[Vinvert]	{"invert",	Fin|Fout|Fmono,	  0,	  0},
[Nvol]		{0}
.
## diffname bitsy/devuda1341.c 2000/1216
## diff -e /n/emeliedump/2000/1207/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2000/1216/sys/src/9/bitsy/devuda1341.c
842d
## diffname bitsy/devuda1341.c 2001/0331
## diff -e /n/emeliedump/2000/1216/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0331/sys/src/9/bitsy/devuda1341.c
942a
	uchar voldata;
.
47,53c
	L3_DataSetupTime =	10,	/* 190 ns */
	L3_DataHoldTime =		10,	/*  30 ns */
	L3_ModeSetupTime =	10,	/* 190 ns */
	L3_ModeHoldTime =		10,	/* 190 ns */
	L3_ClockHighTime =		100,	/* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
	L3_ClockLowTime =		100,	/* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
	L3_HaltTime =			10,	/* 190 ns */
.
## diffname bitsy/devuda1341.c 2001/0421
## diff -e /n/emeliedump/2001/0331/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0421/sys/src/9/bitsy/devuda1341.c
1127a
			}
			if(strcmp(field[i], "reg") == 0) {
				if(nf < 3)
					error(Evolume);
				setreg(field[1], atoi(field[2]), nf == 4 ? atoi(field[3]):1);
				return n0;
.
943d
639c
		print("indisable:	status1	= 0x%2.2ux\n", status1[0]);
.
636,637c
	status1[0] &= ~0x22;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
.
628c
		print("inenable:	status1	= 0x%2.2ux\n", status1[0]);
.
625,626c
	status1[0] |= 0x22;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
.
617c
		print("outdisable:	status1	= 0x%2.2ux\n", status1[0]);
.
614,615c
	status1[0] &= ~0x41;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
.
603,604c
		print("outenable:	status1	= 0x%2.2ux\n", status1[0]);
		print("outenable:	data00	= 0x%2.2ux\n", data00[0]);
.
600,601c
	data00[0] |= 0xf;
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
.
597,598c
	status1[0] |= 0x41;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
.
592a
setreg(char *name, int val, int n)
{
	uchar x[2];
	int i;

	x[0] = val;
	x[1] = val>>8;

	if(strcmp(name, "pause") == 0){
		for(i = 0; i < n; i++)
			µdelay(val);
		return;
	}

	switch(n){
	case 1:
	case 2:
		break;
	default:
		error("setreg");
	}

	if(strcmp(name, "status") == 0){
		L3_write(UDA1341_L3Addr | UDA1341_STATUS, x, n);
	} else if(strcmp(name, "data0") == 0){
		L3_write(UDA1341_L3Addr | UDA1341_DATA0, x, n);
	} else if(strcmp(name, "data1") == 0){
		L3_write(UDA1341_L3Addr | UDA1341_DATA1, x, n);
	} else
		error("setreg");
}

static void
.
584,587c
			print("mxvolume:	status1	= 0x%2.2ux\n", status1[0]);
			print("mxvolume:	data00	= 0x%2.2ux\n", data00[0]);
			print("mxvolume:	data01	= 0x%2.2ux\n", data01[0]);
			print("mxvolume:	data02	= 0x%2.2ux\n", data02[0]);
.
578,582c
			status1[0] |= 0x10;
		L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
		L3_write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
		L3_write(UDA1341_L3Addr | UDA1341_DATA0, data01, 1);
		L3_write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
.
576c
			status1[0] &= ~0x10;
.
574c
			data02[0]|= 0x10;
.
572c
			data02[0] &= ~0x10;
.
566,569c
			data02[0] |= 0x03;
			data01[0] &= ~0x3f;
			data01[0] |= ((left[Vtreb]+right[Vtreb]-100)*0x3/100)&0x03;
			data01[0] |= (((left[Vbass]+right[Vbass]-100)*0xf/100)&0xf)<<2;
.
564c
			data02[0] &= ~0x03;
.
559,560c
		data00[0] &= ~0x3f;
		data00[0] |= ((200-left[Vaudio]-right[Vaudio])*0x3f/200)&0x3f;
.
551,553c
			print("mxvolume:	status1	= 0x%2.2ux\n", status1[0]);
			print("mxvolume:	data0e4	= 0x%4.4ux\n", data0e4[0]|data0e4[0]<<8);
			print("mxvolume:	data0e5	= 0x%4.4ux\n", data0e5[0]|data0e5[0]<<8);
.
548,549c
			status1[0] |= 0x04;
		L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
.
546c
			status1[0] &= ~0x04;
.
538,543c
			data0e4[1] &= ~0x13;
			data0e5[1] &= ~0x1f;
			data0e4[1] |= v & 0x3;
			data0e5[0] |= (v & 0x7c)<<6;
			data0e5[1] |= (v & 0x7c)>>2;
			L3_write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
			L3_write(UDA1341_L3Addr | UDA1341_DATA0, data0e5, 2 );
.
532,533c
			data0e4[1] |= 0x10;
			L3_write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
.
499,504c
		print("enable:	status0	= 0x%2.2ux\n", status0[0]);
		print("enable:	status1	= 0x%2.2ux\n", status1[0]);
		print("enable:	data02	= 0x%2.2ux\n", data02[0]);
		print("enable:	data0e2	= 0x%4.4ux\n", data0e2[0] | data0e2[1]<<8);
		print("enable:	data0e4	= 0x%4.4ux\n", data0e4[0] | data0e4[1]<<8);
		print("enable:	data0e6	= 0x%4.4ux\n", data0e6[0] | data0e6[1]<<8);
.
492,496c
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1 );
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, data0e2, 2);
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, data0e6, 2 );
.
487,488c
	data[0] = status0[0] | 1<<UdaStatusRST;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, data, 1 );
.
464c
	uchar	data[1];
.
457,459c
uchar	data0e4[2]	= {0xc4, 0xe0};
uchar	data0e5[2]	= {0xc5, 0xe0};
uchar	data0e6[2]	= {0xc6, 0xe3};
.
448,455c
uchar	status0[1]		= {0x22};
uchar	status1[1]		= {0x80};
uchar	data00[1]		= {0x00};		/* volume control, bits 0 – 5 */
uchar	data01[1]		= {0x40};
uchar	data02[1]		= {0x80};
uchar	data0e0[2]	= {0xc0, 0xe0};
uchar	data0e1[2]	= {0xc1, 0xe0};
uchar	data0e2[2]	= {0xc2, 0xf2};
.
381a
 */
.
379c
	L3_releasepins();
.
376a
	gpioregs->direction &= ~(GPIO_L3_SDA_io);
.
375a
	L3_acquirepins();
.
369c

 * Commented out, not used
.
360c
	L3_releasepins();
.
356a
	L3_acquirepins();
.
318,319c
//suspect		L3_acquirepins();
.
302c
//suspect		L3_releasepins();
.
296a
	µdelay(L3_ModeHoldTime);

.
277c
//suspect	L3_acquirepins();
.
211d
201a
	µdelay(2);
.
193a
static void	setreg(char *name, int val, int n);

.
67,68c
	UdaStatusPC	= 0,	/* 2 bits */
	UdaStatusDS	= 2,	/* 1 bit */
.
62c
	UdaStatusRST		= 6,	/* 1 bit */
.
47,53c
	L3_DataSetupTime =	1,	/* 190 ns */
	L3_DataHoldTime =		1,	/*  30 ns */
	L3_ModeSetupTime =	1,	/* 190 ns */
	L3_ModeHoldTime =	1,	/* 190 ns */
	L3_ClockHighTime =	1,	/* 100 ns */
	L3_ClockLowTime =		1,	/* 100 ns */
	L3_HaltTime =			1,	/* 190 ns */
.
## diffname bitsy/devuda1341.c 2001/0428
## diff -e /n/emeliedump/2001/0421/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0428/sys/src/9/bitsy/devuda1341.c
342,343d
322,323d
305,306d
299,300d
279,280d
223,224d
213c
	gpioregs->direction &= ~(GPIO_L3_MODE_o | GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
	µdelay(L3_ReleaseTime);
.
202,204c
	gpioregs->set = (GPIO_L3_MODE_o | GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
	gpioregs->direction |=  (GPIO_L3_MODE_o | GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
	µdelay(L3_AcquireTime);
.
47,53c
	L3_AcquireTime =		1,
	L3_ReleaseTime =		1,
	L3_DataSetupTime =	(190+999)/1000,	/* 190 ns */
	L3_DataHoldTime =		( 30+999)/1000,
	L3_ModeSetupTime =	(190+999)/1000,
	L3_ModeHoldTime =	(190+999)/1000,
	L3_ClockHighTime =	(100+999)/1000,
	L3_ClockLowTime =		(100+999)/1000,
	L3_HaltTime =			(190+999)/1000,
.
## diffname bitsy/devuda1341.c 2001/0501
## diff -e /n/emeliedump/2001/0428/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0501/sys/src/9/bitsy/devuda1341.c
444a

.
22c
static int debug = 2;
.
## diffname bitsy/devuda1341.c 2001/0502
## diff -e /n/emeliedump/2001/0501/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0502/sys/src/9/bitsy/devuda1341.c
1012,1015c
			m = (s->emptying->nbytes > n)? n: s->emptying->nbytes;
			memmove(p, s->emptying->virt + Bufsize - 
					  s->emptying->nbytes, m);
.
955d
935,949d
933d
931c
				if ((audio.amode & Aread) == 0)
					dmafree(s->dma);
.
928c
				while(waserror()) {
					dmawait(s->dma);
					if (dmaidle(s->dma))
						break;
				}
.
917,920d
908,910c
			if (audio.i.chan == c) {
				/* closing the read end */
				audio.amode &= ~Aread;
				s = &audio.i;
				qlock(s);
				indisable();
				setempty(s);
				dmafree(s->dma);
				qunlock(s);
				if ((audio.amode & Awrite) == 0) {
					s = &audio.o;
					qlock(s);
					while(waserror()) {
						dmawait(s->dma);
						if (dmaidle(s->dma))
							break;
					}
					outdisable();
					setempty(s);
					dmafree(s->dma);
					qunlock(s);
				}
.
877a
		if (audio.amode == Aread)
			sendaudio(&audio.o);
			
.
874a
			outenable();
.
873a
		}	
		if (omode & Awrite) {
.
868d
865,866c
		if (omode & (Aread|Awrite) && (audio.amode & Awrite) == 0) {
.
801c
		} else if (s == &audio.i)
.
794,799c
	if (s == &audio.i || (ndma & ~zerodma)) {
		/* A dma, not of a zero buffer completed, update current
		 * Only interrupt routine touches s->current
		 */
		s->current->nbytes = (s == &audio.i)? Bufsize: 0;
		s->current++;
		if (s->current == &s->buf[Nbuf])
			s->current = &s->buf[0];
	}
	if (ndma) {
		if (s == &audio.o) {
			zerodma &= ~ndma;
.
727c
		switch (n >> 8) {
.
691c
		switch (n >> 8) {
.
683a
	if ((audio.amode &  Aread) && s->next == s->filling && dmaidle(s->dma)) {
		// send an empty buffer to provide an input clock
		zerodma |= dmastart(s->dma, zeroes.phys, Bufsize) & 0xff;
		if (zerodma == 0)
			if (debug) print("emptyfail\n");
		iostats.empties++;
		iunlock(&s->ilock);
		return;
	}
.
401a

	if (b == &audio.o) {
		zeroes.virt = xalloc(Bufsize);
		zeroes.phys = PADDR(b->buf[i].virt);
		memset(zeroes.virt, 0, Bufsize);
	}
.
399a
		memset(b->buf[i].virt, 0xAA, Bufsize);
.
191,193c
[Vfilter]	{"filter",	Fout|Fmono,		  0,		  0},
[Vinvert]	{"invert",	Fin|Fout|Fmono,	  0,		  0},
[Nvol]	{0}
.
186,189c
[Vaudio]	{"audio",	Fout|Fmono,	 	80,		80},
[Vmic]	{"mic",	Fin|Fmono,		  0,		  0},
[Vtreb]	{"treb",	Fout|Fmono,		50,		50},
[Vbass]	{"bass",	Fout|Fmono, 		50,		50},
.
175a
	ulong	empties;
.
168a
Buf	zeroes;
int	zerodma;	/* dma buffer used for sending zero */

.
150c
/* to have defines just like linux — there's a real operating system */
.
148c
	volatile Buf	*next;		/* next candidate for dma */
.
142,146c
	Rendez		vous;
	Chan		*chan;		/* chan of open */
	int			dma;			/* dma chan, alloc on open, free on close */
	int			bufinit;		/* boolean, if buffers allocated */
	Buf			buf[Nbuf];		/* buffers and queues */
.
22c
static int debug = 0;
.
## diffname bitsy/devuda1341.c 2001/0503
## diff -e /n/emeliedump/2001/0502/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0503/sys/src/9/bitsy/devuda1341.c
973,977c
				while(waserror())
					;
				dmawait(s->dma);
				poperror();
.
950,954c
					while(waserror())
						;
					dmawait(s->dma);
					poperror();
.
## diffname bitsy/devuda1341.c 2001/0504
## diff -e /n/emeliedump/2001/0503/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0504/sys/src/9/bitsy/devuda1341.c
905c
		if (audio.amode & Aread)
.
## diffname bitsy/devuda1341.c 2001/0508
## diff -e /n/emeliedump/2001/0504/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0508/sys/src/9/bitsy/devuda1341.c
1221d
1217c
			while (!dmaidle(a->dma) && !zerodma && a->filling == a->current) {
.
1121a
	case Qspeed:
		if(n > sizeof(buf)-1)
			n = sizeof(buf)-1;
		memmove(buf, p, n);
		buf[n] = '\0';
		nf = getfields(buf, field, Ncmd, 1, " \t\n");
		if (nf != 2 && nf != 1) error(Ebadarg);
		if (nf == 2) {
			if (strcmp(field[0], "speed")) 
				error(Ebadarg);
			i = 1;
		}
		else
			i = 0;
		mxspeed((int)strtol(field[i], (char **)nil, 0));
		break;
		
.
1061a
	case Qspeed:
		buf[0] = '\0';
		snprint(buf, sizeof(buf), "speed %d\n", rate);
		return readstr(offset, p, n, buf);

.
930a
	case Qspeed:
.
902a
		mxspeed(Speed);
.
865a
	case Qspeed:
.
697c
		zerodma |= dmastart(s->dma, Flushbuf, Bufsize) & 0xff;
.
534a
mxspeed(int _rate)
{
	uchar data;

	egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 1);
	/* set the external clock generator */
	rate = _rate;
	switch (rate) {
	case 32000:
	case 48000:
		/* 00 */
		gpioregs->clear = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
		break;
	default:
		rate = 44100;
	case 44100:
		/* 01 */
		gpioregs->set = GPIO_CLK_SET0_o;
		gpioregs->clear = GPIO_CLK_SET1_o;
		break;
	case 8000:
	case 16000:
		/* 10 */
		gpioregs->set = GPIO_CLK_SET1_o;
		gpioregs->clear = GPIO_CLK_SET0_o;
		break;
	case 11025:
	case 22050:
		/* 11 */
		gpioregs->set = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
		break;
	}
	delay(100);

	switch (rate) {
	case 8000:
	case 11025:
		data = SC512FS;
		break;
	case 16000:
	case 22050:
	case 44100:
	case 48000:
		data = SC256FS;
		break;
	case 32000:
		data = SC384FS;
		break;
	}
	data |= (LSB16 << 4)|0x2;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, &data, 1);
}
	

static void
.
472a
mxspeed(int _rate);

static void
.
407,412d
199a
static int rate = Speed;		/* Current sample rate */
.
169d
127a
	"speed",	{Qspeed},			0,	0666,
.
126c
	"audio",	{Qaudio},			0,	0666,
.
122a
enum {
	Flushbuf = 0xe0000000,
};

/* System Clock */
enum {
	SC512FS = 0,
	SC384FS = 1,
	SC256FS = 2,
};

/* Format */
enum {
	LSB16 = 1,
	LSB18 = 2,
};

.
96a
	Qspeed,
.
## diffname bitsy/devuda1341.c 2001/0509
## diff -e /n/emeliedump/2001/0508/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0509/sys/src/9/bitsy/devuda1341.c
1215c
		i = (int)strtol(field[i], (char **)nil, 0);
		switch (i) {
		case 16000:
		case 22050:
		case 44100:
		case 48000:
			volumes[Vspeed].ilval = volumes[Vspeed].irval = rate = i;
			break;
		default:
			error(Ebadarg);
		}
.
975d
604a
static	void
resetlevel(void)
{
	int i;

	for(i=0; volumes[i].name; i++) {
		audio.lovol[i] = volumes[i].ilval;
		audio.rovol[i] = volumes[i].irval;
		audio.livol[i] = volumes[i].ilval;
		audio.rivol[i] = volumes[i].irval;
	}
}

.
603d
600,601c
	data[0] = status0[0] | 1<<UdaStatusRST | clock;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, data, 1 );
	if (debug)
		print("enable:	status0	= 0x%2.2ux\n", data[0]);

	gpioregs->clear = EGPIO_codec_reset;
	gpioregs->set = EGPIO_codec_reset;
	/* write uda 1341 status[0] */
	data[0] = status0[0] | clock;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, data, 1);
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, data0e2, 2);
	L3_write(UDA1341_L3Addr | UDA1341_DATA0, data0e6, 2 );

	if (debug) {
		print("enable:	status0	= 0x%2.2ux\n", data[0]);
		print("enable:	status1	= 0x%2.2ux\n", status1[0]);
		print("enable:	data02	= 0x%2.2ux\n", data02[0]);
		print("enable:	data0e2	= 0x%4.4ux\n", data0e2[0] | data0e2[1]<<8);
		print("enable:	data0e4	= 0x%4.4ux\n", data0e4[0] | data0e4[1]<<8);
		print("enable:	data0e6	= 0x%4.4ux\n", data0e6[0] | data0e6[1]<<8);
		print("enable:	sspregs->control0 = 0x%lux\n", sspregs->control0);
		print("enable:	sspregs->control1 = 0x%lux\n", sspregs->control1);
	}
.
597c
		clock = SC384FS;	/* Only works in MSB mode! */
.
594c
		clock = SC256FS;
.
589a
	default:
.
588c
		clock = SC512FS;	/* Only works in MSB mode! */
.
584a
	/* Reset the chip */
.
582a

	/* Wait for the UDA1341 to wake up */
.
507,557d
491c
	uchar	data[1], clock;
.
486,488d
472c
uchar	status0[1]		= {0x02};
.
137,138c
	LSB16 = 1 << 1,
	LSB18 = 2 << 1,
	LSB20 = 3 << 1,
	MSB = 4 << 1,
	MSB16 = 5 << 1,
	MSB18 = 6 << 1,
	MSB20 = 7 << 1,
.
130,132c
	SC512FS = 0 << 2,
	SC384FS = 1 << 2,
	SC256FS = 2 << 2,
.
128c
/* System Clock -- according to the manual, it seems that when the UDA is
    configured in non MSB/I2S mode, it uses a divisor of 256 to the 12.288MHz
    clock.  The other rates are only supported in MSB mode, which should be 
    implemented at some point */
.
117c
	Bufsize		= /* 4* */ 1024,	/* 46 ms each */
.
## diffname bitsy/devuda1341.c 2001/0510
## diff -e /n/emeliedump/2001/0509/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0510/sys/src/9/bitsy/devuda1341.c
1327c
			if(a->filling->nbytes >= bufsize) {
.
1319c
			m = bufsize - a->filling->nbytes;
.
1207,1216c
			while (i < nf) {
				int newval;

				if (i + 1 >= nf) error(Ebadarg);
				newval = (int)strtol(field[i + 1], (char **)nil, 0);
				if (!strcmp(field[i], "speed"))
					setrate(newval);
				else if (!strcmp(field[i], "dmasize"))
					setbufsize(newval);
				else
					error(Ebadarg);
				i += 2;
			}
.
1199,1205c
		if (nf == 1)
			/* The user just supplied a number */
			setrate((int)strtol(field[0], (char **)nil, 0));
		else {
.
1176a
static void
setrate(int newrate)
{
	switch (newrate) {
	case 16000:
	case 22050:
	case 44100:
	case 48000:
		volumes[Vspeed].ilval = volumes[Vspeed].irval = rate = newrate;
		break;
	default:
		error(Ebadarg);
	}
}

static void
setbufsize(int newbufsize)
{
	if (newbufsize < 0 || newbufsize > 8 * 1024 || newbufsize % sizeof(ulong))
		error(Ebadarg);
	bufsize = newbufsize;
}

.
1130c
		snprint(buf, sizeof(buf), "speed %d\ndmasize %d\n", rate, bufsize);
.
1110c
			memmove(p, s->emptying->virt + bufsize - 
.
882c
		s->current->nbytes = (s == &audio.i)? bufsize: 0;
.
806c
		if ((n = dmastart(s->dma, s->next->phys, bufsize)) == 0) {
.
761c
		zerodma |= dmastart(s->dma, Flushbuf, bufsize) & 0xff;
.
226a
static int bufsize = Bufsize;	/* Current buffer size */
.
225d
117c
	Bufsize		= 4* 1024,	/* 46 ms each */
.
## diffname bitsy/devuda1341.c 2001/0512
## diff -e /n/emeliedump/2001/0510/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0512/sys/src/9/bitsy/devuda1341.c
118c
	Nbuf		= 10,		/* 1.5 seconds total */
.
## diffname bitsy/devuda1341.c 2001/0526
## diff -e /n/emeliedump/2001/0512/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0526/sys/src/9/bitsy/devuda1341.c
1130c
		snprint(buf, sizeof(buf), "speed %d\ndmasize %d\nsendbytes %d\n", rate, bufsize, nsendb);
.
1128a
		s = &audio.o;
		nbufs = s->filling - s->current;
		if (nbufs < 0) nbufs = Nbuf + nbufs;
		nsendb = (nbufs - 1) * bufsize + s->current->nbytes;
.
1067c
	int liv, riv, lov, rov, nsendb, nbufs;
.
140,146c
	LSB16 =	1 << 1,
	LSB18 =	2 << 1,
	LSB20 =	3 << 1,
	MSB =	4 << 1,
	MSB16 =	5 << 1,
	MSB18 =	6 << 1,
	MSB20 =	7 << 1,
.
121c
	Ncmd		= 50,			/* max volume command words */
.
118c
	Nbuf			= 10,			/* 1.5 seconds total */
.
102c
	Fout			= 4,
.
## diffname bitsy/devuda1341.c 2001/0528
## diff -e /n/emeliedump/2001/0526/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0528/sys/src/9/bitsy/devuda1341.c
1376d
1215c
	switch((ulong)c->qid.path) {
.
1077c
	switch((ulong)c->qid.path) {
.
989c
	switch((ulong)c->qid.path) {
.
921c
	switch((ulong)c->qid.path) {
.
912c
	return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
.
909,910c
static int
audiostat(Chan *c, uchar *db, int n)
.
906c
	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
.
904c
audiowalk(Chan *c, Chan *nc, char **name, int nname)
.
774,789d
769,772c
		s->next->nbytes &= ~0x3;	/* must be a multiple of 4 */
		if(s->next->nbytes) {
			if ((n = dmastart(s->dma, s->next->phys, s->next->nbytes)) == 0) {
				iostats.faildma++;
				break;
			}
			iostats.totaldma++;
			switch (n >> 8) {
			case 1:
				iostats.idledma++;
				break;
			case 3:
				iostats.faildma++;
				break;
			}
			if (debug) {
				if (debug > 1)
					print("dmastart @%p\n", s->next);
				else
					iprint("+");
			}
			s->next->nbytes = 0;
.
151a
	".",		{Qdir, 0, QTDIR},	0,	DMDIR|0555,
.
12a

.
## diffname bitsy/devuda1341.c 2001/0529
## diff -e /n/emeliedump/2001/0528/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0529/sys/src/9/bitsy/devuda1341.c
907c
static Walkqid*
.
## diffname bitsy/devuda1341.c 2001/0530
## diff -e /n/emeliedump/2001/0529/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0530/sys/src/9/bitsy/devuda1341.c
118,119c
	Bufsize		= 4* 1024,	/* 23 ms each */
	Nbuf			= 10,			/* 230 ms total */
.
## diffname bitsy/devuda1341.c 2001/0605
## diff -e /n/emeliedump/2001/0530/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0605/sys/src/9/bitsy/devuda1341.c
1358c
			if(a->filling->nbytes >= volumes[Vbufsize].ilval) {
.
1350c
			m = volumes[Vbufsize].ilval - a->filling->nbytes;
.
1269,1278c
				if (volumes[v].setval)
					volumes[v].setval(in, out, left, right, m);
.
1224,1250d
1205c
	volumes[Vbufsize].ilval = value;
.
1203c
	if ((value % 8) != 0 || value < 8 || value >= Bufsize)
.
1201c
setbufsize(int, int, int, int, int value)
.
1197a

	/* Wait for the UDA1341 to wake up */
	delay(100);

	/* Reset the chip */
	status0[0] &= ~CLOCKMASK;

	status0[0] |=clock;
	volumes[Vspeed].ilval = speed;
.
1196c
		speed = 44100;
	case 44100:
		/* 01 */
		gpioregs->set = GPIO_CLK_SET0_o;
		gpioregs->clear = GPIO_CLK_SET1_o;
		clock = SC256FS;
		break;
	case 8000:
		/* 10 */
		gpioregs->set = GPIO_CLK_SET1_o;
		gpioregs->clear = GPIO_CLK_SET0_o;
		clock = SC512FS;	/* Only works in MSB mode! */
		break;
	case 16000:
		/* 10 */
		gpioregs->set = GPIO_CLK_SET1_o;
		gpioregs->clear = GPIO_CLK_SET0_o;
		clock = SC256FS;
		break;
	case 11025:
		/* 11 */
		gpioregs->set = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
		clock = SC512FS;	/* Only works in MSB mode! */
		break;
	case 22050:
		/* 11 */
		gpioregs->set = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
		clock = SC256FS;
		break;
.
1193c
		/* 00 */
		gpioregs->clear = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
		clock = SC256FS;
.
1188,1191c
	if (value < 0 || value > 100) 
		error(Evolume);
	if(left && out)
		audio.lovol[Vaudio] = value;
	if(left && in)
		audio.livol[Vaudio] = value;
	if(right && out)
		audio.rovol[Vaudio] = value;
	if(right && in)
		audio.rivol[Vaudio] = value;
}

static void 
setspeed(int, int, int, int, int speed)
{
	uchar	clock;

	/* external clock configured for 44100 samples/sec */
	switch (speed) {
	case 32000:
		/* 00 */
		gpioregs->clear = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
		clock = SC384FS;	/* Only works in MSB mode! */
		break;
.
1186c
setaudio(int in, int out, int left, int right, int value)
.
1145,1148c
			if (m == Vaudio) {
				liv = audio.livol[m];
				riv = audio.rivol[m];
				lov = audio.lovol[m];
				rov = audio.rovol[m];
			}
			else {
				lov = liv = volumes[m].ilval;
				rov = riv = volumes[m].irval;
			}
	
.
1132,1138c
	case Qstats:
		buf[0] = 0;
		snprint(buf, sizeof(buf), 
			    "bytes %lud\nRX dmas %lud, while idle %lud, while busy %lud, "
			    "out-of-order %lud, empty dmas %lud\n"
			    "TX dmas %lud, while idle %lud, while busy %lud, "
			    "out-of-order %lud, empty dmas %lud\n",
  			    iostats.bytes, iostats.rx.totaldma, iostats.rx.idledma, 
			    iostats.rx.faildma, iostats.rx.samedma, iostats.rx.empties,
 			    iostats.tx.totaldma, iostats.tx.idledma, 
			    iostats.tx.faildma, iostats.tx.samedma, iostats.tx.empties);

.
1114c
			memmove(p, s->emptying->virt + volumes[Vbufsize].ilval - 
.
1071c
	int liv, riv, lov, rov;
.
1057,1062d
1001a
	case Qstats:
.
1000d
934d
930a
	case Qstats:
.
916c
 	return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
.
910c
 	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
.
886c
		s->current->nbytes = (s == &audio.i)? volumes[Vbufsize].ilval: 0;
.
820c
			iostats.rx.faildma++;
.
817c
			iostats.rx.idledma++;
.
814c
		iostats.rx.totaldma++;
.
810,811c
		if ((n = dmastart(s->dma, s->next->phys, volumes[Vbufsize].ilval)) == 0) {
			iostats.rx.faildma++;
.
783c
				iostats.tx.faildma++;
.
780c
				iostats.tx.idledma++;
.
777c
			iostats.tx.totaldma++;
.
774c
				iostats.tx.faildma++;
.
766c
		iostats.tx.empties++;
.
763c
		zerodma |= dmastart(s->dma, Flushbuf, volumes[Vbufsize].ilval) & 0xff;
.
603a
	setspeed(0, 0, 0, 0, volumes[Vspeed].ilval);
	if (!dmaidle(audio.i.dma) || !dmaidle(audio.o.dma))
		L3_write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1);

.
569a

	if (debug)
		print("enable:	status0	= 0x%2.2ux\n", data[0]);

.
568c
	data[0] = status0[0];
.
562,564d
540,560c
	data[0] = status0[0] | 1 << UdaStatusRST;
.
513,538c
	setspeed(0, 0, 0, 0, volumes[Vspeed].ilval);
.
498c
	uchar	data[1];
.
227,228d
218,224c
[Vaudio]	{"audio",		Fout|Fmono,	 	80,		80,	setaudio },
[Vmic]	{"mic",		Fin|Fmono,		  0,		  0,	nil },
[Vtreb]	{"treb",		Fout|Fmono,		50,		50,	nil },
[Vbass]	{"bass",		Fout|Fmono, 		50,		50,	nil },
[Vspeed]	{"speed",		Fin|Fout|Fmono,	Speed,	Speed,	setspeed },
[Vbufsize]	{"bufsize",	Fin|Fout|Fmono,	Bufsize,	Bufsize,	setbufsize },
[Vfilter]	{"filter",		Fout|Fmono,		  0,		  0,	nil },
[Vinvert]	{"invert",		Fin|Fout|Fmono,	  0,		  0,	nil },
.
213,215c
	int		flag;
	int		ilval;		/* initial  values */
	int		irval;
	void		(*setval)(int, int, int, int, int);
.
209a
static void setaudio(int in, int out, int left, int right, int value);
static void setspeed(int in, int out, int left, int right, int value);
static void setbufsize(int in, int out, int left, int right, int value);

.
207a
} iostats_t;

static struct
{
	ulong	bytes;
	iostats_t	rx, tx;
.
200,202c
typedef struct {
.
153,157c
 	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,
	"audio",		{Qaudio},			0,	0666,
	"volume",		{Qvolume},		0,	0666,
	"audiostatus",	{Qstatus},			0,	0444,
	"audiostats",	{Qstats},			0,	0444,
.
136a
	CLOCKMASK = 3 << 2,
.
118,119c
	Bufsize		= 4* 1024,	/* 46 ms each */
	Nbuf			= 10,			/* 1.5 seconds total */
.
113a
	Vbufsize,
.
99a
	Qstats,
.
98d
13d
## diffname bitsy/devuda1341.c 2001/0620
## diff -e /n/emeliedump/2001/0605/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0620/sys/src/9/bitsy/devuda1341.c
1086d
944a
		qunlock(&audio);
.
942d
## diffname bitsy/devuda1341.c 2001/0810
## diff -e /n/emeliedump/2001/0620/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/0810/sys/src/9/bitsy/devuda1341.c
1023c
				egpiobits(EGPIO_codec_reset, 0);
				audioicpower(0);
.
837c
		egpiobits(EGPIO_codec_reset, 0);
		audioamppower(0);
		audioicpower(0);
.
815c
			audioamppower(1);
			audioicpower(1);
			egpiobits(EGPIO_codec_reset, 1);
.
699c
	audioamppower(0);
.
693d
675c
	audioamppower(1);
.
520c
	audioicpower(1);
	egpiobits(EGPIO_codec_reset, 1);
.
## diffname bitsy/devuda1341.c 2001/1117
## diff -e /n/emeliedump/2001/0810/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/1117/sys/src/9/bitsy/devuda1341.c
1273c
		nf = tokenize(buf, field, Ncmd);
.
## diffname bitsy/devuda1341.c 2001/1121
## diff -e /n/emeliedump/2001/1117/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2001/1121/sys/src/9/bitsy/devuda1341.c
1334a
		poperror();
		free(cb);
.
1327c
				setreg(cb->f[1], atoi(cb->f[2]), cb->nf == 4 ? atoi(cb->f[3]):1);
.
1324,1325c
			if(strcmp(cb->f[i], "reg") == 0) {
				if(cb->nf < 3)
.
1319c
			if(strcmp(cb->f[i], "right") == 0) {
.
1314c
			if(strcmp(cb->f[i], "left") == 0) {
.
1309c
			if(strcmp(cb->f[i], "out") == 0) {
.
1304c
			if(strcmp(cb->f[i], "in") == 0) {
.
1300c
			if(strcmp(cb->f[i], "debug") == 0) {
.
1296c
			if(strcmp(cb->f[i], "reset") == 0) {
.
1286c
				if(strcmp(cb->f[i], volumes[m].name) == 0) {
.
1278,1279c
			if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') {
				m = strtoul(cb->f[i], 0, 10);
.
1273,1274c
		for(i = 0; i < cb->nf; i++){
.
1268,1271c
		cb = parsecmd(p, n);
		if(waserror()){
			free(cb);
			nexterror();
		}
.
1253a
	Cmdbuf *cb;
.
1250,1251c
	int i, v, left, right, in, out;
.
237a

.
## diffname bitsy/devuda1341.c 2002/0109
## diff -e /n/emeliedump/2001/1121/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2002/0109/sys/src/9/bitsy/devuda1341.c
1384a
	devshutdown,
.
## diffname bitsy/devuda1341.c 2002/0606
## diff -e /n/emeliedump/2002/0109/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2002/0606/sys/src/9/bitsy/devuda1341.c
652c
			microdelay(val);
.
377c
	microdelay(L3_ModeHoldTime);
.
372c
	microdelay(L3_ModeSetupTime);
.
367c
		microdelay(L3_HaltTime);
.
344c
	microdelay(L3_ModeHoldTime);
.
336c
	microdelay(L3_ModeSetupTime);
.
331c
		microdelay(L3_HaltTime);
.
310c
	microdelay(L3_ClockHighTime);
.
307c
	microdelay(L3_ClockLowTime);
.
287c
	microdelay(L3_ClockHighTime);
.
282c
	microdelay(L3_ClockLowTime);
.
259c
	microdelay(L3_ReleaseTime);
.
249c
	microdelay(L3_AcquireTime);
.
## diffname bitsy/devuda1341.c 2002/1112
## diff -e /n/emeliedump/2002/0606/sys/src/9/bitsy/devuda1341.c /n/emeliedump/2002/1112/sys/src/9/bitsy/devuda1341.c
488c
	sspregs = mapspecial(SSPREGS, sizeof(SSPregs));
.
485c
	mcpregs = mapspecial(MCPREGS, sizeof(MCPregs));
.

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.