~kris/9p

9hist

c188ee75798a1166272f077d3567337aa28f9fe6 — David du Colombier 25 years ago bb05708
Plan 9 from Bell Labs 2000-11-17
4 files changed, 710 insertions(+), 193 deletions(-)

M bitsy/devflash.c
M bitsy/devuda1341.c
M bitsy/main.c
M bitsy/sa1110dma.c
M bitsy/devflash.c => bitsy/devflash.c +504 -74
@@ 6,18 6,6 @@
#include	"io.h"
#include	"../port/error.h"

/* flash partitions */
typedef struct FlashPart FlashPart;
struct FlashPart
{
	QLock;
	char	name[NAMELEN];
	ulong	start;		/* byte offsets */
	ulong	end;
};

static ulong *flash = (ulong*)FLASHZERO;

/*
 *  on the bitsy, all 32 bit accesses to flash are mapped to two 16 bit
 *  accesses, one to the low half of the chip and the other to the high


@@ 27,66 15,117 @@ static ulong *flash = (ulong*)FLASHZERO;
 *  sectors for each request erase request.
 */

/*
 *  common flash memory interface
 */
struct CFIid
{
	ulong	q;
	ulong	r;
	ulong	y;
	ulong	cmd_set;
	ulong	vendor_alg;
	ulong	ext_alg_addr[2];
	ulong	alt_cmd_set;
	ulong	alt_vendor_alg;
	ulong	alt_ext_ald_addr[2];
	
#define mirror(x) (((x)<<16)|(x))

/* this defines a contiguous set of erase blocks of one size */
typedef struct FlashRegion FlashRegion;
struct FlashRegion
{
	ulong	addr;		/* start of region */
	ulong	end;		/* end of region + 1 */
	ulong	n;		/* number of blocks */
	ulong	size;		/* size of each block */
};

struct CFIsys
{
	ulong 	vcc_min;	/* 100 mv */
	ulong	vcc_max;
	ulong	vpp_min;
	ulong	vpp_max;
	ulong	word_wr_to;		/* 2**n µs */
	ulong	buf_wr_to;		/* 2**n µs */
	ulong	block_erase_to;		/* 2**n ms */
	ulong	chip_erase_to;		/* 2**n ms */
	ulong	max_word_wr_to;		/* 2**n µs */
	ulong	max_buf_wr_to;		/* 2**n µs */
	ulong	max_block_erase_to;	/* 2**n ms */
	ulong	max_chip_erase_to;	/* 2**n ms */
/* this defines a particular access algorithm */
typedef struct FlashAlg FlashAlg;
struct FlashAlg
{
	int	id;
	char	*name;
	void	(*identify)(void);	/* identify device */
	void	(*erase)(ulong);	/* erase a region */
	void	(*write)(void*, long, ulong);	/* write a region */
};

struct CFIgeom
static void	ise_id(void);
static void	ise_erase(ulong);
static void	ise_write(void*, long, ulong);

static void	afs_id(void);
static void	afs_erase(ulong);
static void	afs_write(void*, long, ulong);

FlashAlg falg[] =
{
	ulong	size;		/* 2**n bytes */
	ulong	dev_code;	/* ??? */
	ulong	max_multi;	/* max bytes in a multibyte write */
	ulong	nregion;	/* number of erase regions */
	ulong	region[1];	/* erase region info */
	{ 1,	"Intel/Sharp Extended",	ise_id, ise_erase, ise_write	},
	{ 2,	"AMD/Fujitsu Standard",	afs_id, afs_erase, afs_write	},
};

#define mirror(x) (((x)<<16)|(x))
struct
{
	RWlock;
	ulong		*p;
	ushort		algid;		/* access algorithm */
	FlashAlg	*alg;
	ushort		manid;		/* manufacturer id */
	ushort		devid;		/* device id */
	ulong		size;		/* size in bytes */
	int		wbsize;		/* size of write buffer */ 
	ulong		nr;		/* number of regions */
	uchar		bootprotect;
	FlashRegion	r[32];
	ulong		*wb;		/* staging area for write buffer */
} flash;

void
cfiquery(void)
/*
 *  common flash interface
 */
static uchar
cfigetc(int off)
{
	struct CFIid *id;
	uchar rv;

	flash[0x55] = mirror(0x98);
	id = (struct CFIid*)&flash[0x10];
	if(id.q != 'q' || id.r != 'r' || id.y != 'y')
		print("CFI not supported by flash\n");
	
	flash[0x55] = mirror(0xFF);
	flash.p[0x55] = mirror(0x98);
	rv = flash.p[off];
	flash.p[0x55] = mirror(0xFF);
	return rv;
}

void
cfigeom(void)
static ushort
cfigets(int off)
{
	return (cfigetc(off+1)<<8)|cfigetc(off);
}

static ulong
cfigetl(int off)
{
	return (cfigetc(off+3)<<24)|(cfigetc(off+2)<<16)|
		(cfigetc(off+1)<<8)|cfigetc(off);
}

static void
cfiquery(void)
{
	uchar q, r, y;
	ulong x, addr;

	q = cfigetc(0x10);
	r = cfigetc(0x11);
	y = cfigetc(0x12);
	if(q != 'Q' || r != 'R' || y != 'Y'){
		print("cfi query failed: %ux %ux %ux\n", q, r, y);
		return;
	}
	flash.algid = cfigetc(0x13);
	flash.size = 1<<(cfigetc(0x27)+1);
	flash.wbsize = 1<<(cfigetc(0x2a)+1);
	flash.nr = cfigetc(0x2c);
	if(flash.nr > nelem(flash.r)){
		print("cfi reports > %d regions\n", nelem(flash.r));
		flash.nr = nelem(flash.r);
	}
	addr = 0;
	for(q = 0; q < flash.nr; q++){
		x = cfigetl(q+0x2d);
		flash.r[q].size = 2*256*(x>>16);
		flash.r[q].n = (x&0xffff)+1;
		flash.r[q].addr = addr;
		addr += flash.r[q].size*flash.r[q].n;
		flash.r[q].end = addr;
	}
	flash.wb = malloc(flash.wbsize);
}

/*


@@ 95,30 134,35 @@ cfigeom(void)

enum
{
	Qf0=1,
	Qf1,
	Qf2,
	Qf3,
	Qfctl=1,
	Qfdata,
};

Dirtab flashdir[]={
	"f0",		{ Qf0, 0 },	0,	0664,
	"f1",		{ Qf1, 0 },	0,	0664,
	"f2",		{ Qf2, 0 },	0,	0664,
	"f3",		{ Qf3, 0 },	0,	0664,
	"flashctl",		{ Qfctl, 0 },	0,	0664,
	"flashdata",		{ Qfdata, 0 },	0,	0660,
};

void
flashinit(void)
{
	int i;

	flash.p = (ulong*)FLASHZERO;
	cfiquery();
	cfigeom();
	for(i = 0; i < nelem(falg); i++)
		if(flash.algid == falg[i].id){
			flash.alg = &falg[i];
			(*flash.alg->identify)();
			break;
		}
	flash.bootprotect = 1;
}

static Chan*
flashattach(char* spec)
{
	return devattach('r', spec);
	return devattach('F', spec);
}

static int	 


@@ 150,16 194,185 @@ flashclose(Chan*)
static long	 
flashread(Chan* c, void* a, long n, vlong off)
{
	USED(c, a, off);
	error("UUO");
	char *buf, *p, *e;
	int i;

	if(c->qid.path&CHDIR)
		return devdirread(c, a, n, flashdir, nelem(flashdir), devgen);
	switch(c->qid.path){
	default:
		error(Eperm);
	case Qfctl:
		buf = smalloc(1024);
		e = buf + 1024;
		p = seprint(buf, e, "0x%-9lux 0x%-9lux 0x%-9lux 0x%-9lux\n", flash.size,
			flash.wbsize, flash.manid, flash.devid);
		for(i = 0; i < flash.nr; i++)
			p = seprint(p, e, "0x%-9lux 0x%-9lux 0x%-9lux\n", flash.r[i].addr,
				flash.r[i].n, flash.r[i].size);
		n = readstr(off, a, n, buf);
		free(buf);
		break;
	case Qfdata:
		if(!iseve())
			error(Eperm);
		if(off >= flash.size)
			return 0;
		if(off + n > flash.size)
			n = flash.size - off;
		rlock(&flash);
		if(waserror()){
			runlock(&flash);
			nexterror();
		}
		memmove(a, ((uchar*)FLASHZERO)+off, n);
		runlock(&flash);
		poperror();
		break;
	}
	return n;
}

static void
bootprotect(ulong addr)
{
	FlashRegion *r;

	if(flash.bootprotect == 0)
		return;
	if(flash.nr == 0)
		error("writing over boot loader disallowed");
	r = flash.r;
	if(addr >= r->addr && addr < r->addr + r->size)
		error("writing over boot loader disallowed");
}

ulong
blockstart(ulong addr)
{
	FlashRegion *r, *e;
	ulong x;

	r = flash.r;
	for(e = &flash.r[flash.nr]; r < e; r++)
		if(addr >= r->addr && addr < r->end){
			x = addr - r->addr;
			x /= r->size;
			return r->addr + x*r->size;
		}
			
	return (ulong)-1;
}

ulong
blockend(ulong addr)
{
	FlashRegion *r, *e;
	ulong x;

	r = flash.r;
	for(e = &flash.r[flash.nr]; r < e; r++)
		if(addr >= r->addr && addr < r->end){
			x = addr - r->addr;
			x /= r->size;
			return r->addr + (x+1)*r->size;
		}
			
	return (ulong)-1;
}

static long
flashctlwrite(char *p, long n)
{
	Cmdbuf *cmd;
	ulong addr;

	cmd = parsecmd(p, n);
	wlock(&flash);
	if(waserror()){
		wunlock(&flash);
		nexterror();
	}
	if(strcmp(cmd->f[0], "erase") == 0){
		if(cmd->nf != 2)
			error(Ebadarg);
		addr = atoi(cmd->f[1]);
		if(addr != blockstart(addr))
			error("erase must be a block boundary");
		bootprotect(addr);
		(*flash.alg->erase)(addr);
	} else if(strcmp(cmd->f[0], "protectboot") == 0){
		if(cmd->nf == 0 || strcmp(cmd->f[1], "off") != 0)
			flash.bootprotect = 1;
		else
			flash.bootprotect = 0;
	} else
		error(Ebadarg);
	poperror();
	wunlock(&flash);
	free(cmd);

	return n;
}

static long
flashdatawrite(uchar *p, long n, long off)
{
	uchar *end;
	int m;
	long ooff = off;
	uchar *op = p;

	if((off & 0x3) || (n & 0x3))
		error("only quad writes");
	if(off >= flash.size || off+n > flash.size || n <= 0)
		error(Ebadarg);

	wlock(&flash);
	if(waserror()){
		wunlock(&flash);
		nexterror();
	}

	/* make sure we're not writing the boot sector */
	bootprotect(off);

	/* (*flash.alg->write) can't cross blocks */
	for(end = p + n; p < end; p += m){
		m = blockend(off) - off;
		if(m > end - p)
			m = end - p;
		(*flash.alg->write)(p, m, off);
		off += m;
	}

	/* make sure write succeeded */
	if(memcmp(op, &flash.p[ooff>>2], n) != 0)
		error("written bytes don't match");

	wunlock(&flash);
	poperror();

	return n;
}

static long	 
flashwrite(Chan* c, void* a, long n, vlong)
flashwrite(Chan* c, void* a, long n, vlong off)
{
	USED(c, a, off);
	error("UUO");
	if(c->qid.path & CHDIR)
		error(Eperm);

	if(!iseve())
		error(Eperm);

	switch(c->qid.path){
	default:
		panic("flashwrite");
	case Qfctl:
		return flashctlwrite(a, n);
	case Qfdata:
		return flashdatawrite(a, n, off);
	}
	return n;
}



@@ 183,3 396,220 @@ Dev flashdevtab = {
	devremove,
	devwstat,
};


/* intel/sharp extended command set */
static void
ise_reset(void)
{
	flash.p[0x55] = mirror(0xff);	/* reset */
}
static void
ise_id(void)
{
	ise_reset();
	flash.p[0x555] = mirror(0x90);	/* uncover vendor info */
	flash.manid = flash.p[00];
	flash.devid = flash.p[01];
	ise_reset();
}
static void
ise_clearerror(void)
{
	flash.p[0x100] = mirror(0x50);

}
static void
ise_error(int bank, ulong status)
{
	char err[ERRLEN];

	if(status & (1<<3)){
		sprint(err, "flash%d: low prog voltage", bank);
		error(err);
	}
	if(status & (1<<1)){
		sprint(err, "flash%d: block locked", bank);
		error(err);
	}
	if(status & (1<<5)){
		sprint(err, "flash%d: i/o error", bank);
		error(err);
	}
}
static void
ise_erase(ulong addr)
{
	ulong start;
	ulong x;

	addr >>= 2;	/* convert to ulong offset */

	flashprogpower(1);
	flash.p[addr] = mirror(0x20);
	flash.p[addr] = mirror(0xd0);
	start = m->ticks;
	do {
		x = flash.p[addr];
		if((x & mirror(1<<7)) == mirror(1<<7))
			break;
	} while(TK2MS(m->ticks-start) < 1500);
	flashprogpower(0);

	ise_clearerror();
	ise_error(0, x);
	ise_error(1, x>>16);

	ise_reset();
}
/*
 *  flash writing goes about 16 times faster if we use
 *  the write buffer.  We fill the write buffer and then
 *  issue the write request.  After the write request,
 *  subsequent reads will yield the status register or,
 *  since error bits are sticky, another write buffer can
 *  be filled and written.
 *
 *  On timeout, we issue a read status register request so
 *  that the status register can be read no matter how we
 *  exit.
 */
static int
ise_wbwrite(ulong *p, int n, ulong off)
{
	ulong start;
	int i;

	/* copy out of user space to avoid faults later */
	memmove(flash.wb, p, n*4);
	p = flash.wb;

	/* put flash into write buffer mode */
	start = m->ticks;
	for(;;) {
		/* request write buffer mode */
		flash.p[off] = mirror(0xe8);

		/* look at extended status reg for status */
		if((flash.p[off] & mirror(1<<7)) == mirror(1<<7))
			break;

		/* didn't work, keep trying for 2 secs */
		if(TK2MS(m->ticks-start) > 2000){
			/* set up to read status */
			flash.p[off] = mirror(0x70);
			return -1;
		}
	}

	/* fill write buffer */
	flash.p[off] = mirror(n-1);
	for(i = 0; i < n; i++)
		flash.p[off+i] = *p++;

	/* program from buffer */
	flash.p[off] = mirror(0xd0);

	/* subsequent reads will return status about the write */

	return n;
}
static void
ise_write(void *a, long n, ulong off)
{
	ulong *p, *end;
	int i, wbsize;
	ulong x, start, ooff;

	/* everything in terms of ulongs */
	wbsize = flash.wbsize>>2;
	off >>= 2;
	n >>= 2;
	p = a;
	ooff = off;

	/* first see if write will succeed */
	for(i = 0; i < n; i++)
		if((p[i] & flash.p[off+i]) != p[i])
			error("flash needs erase");

	if(waserror()){
		ise_reset();
		flashprogpower(0);
		nexterror();
	}
	flashprogpower(1);

	/*
	 *  use the first write to reach
 	 *  a write buffer boundary.  the intel maunal
	 *  says writes startng at wb boundaries
	 *  maximize speed.
	 */
	i = wbsize - (off & (wbsize-1));
	for(end = p + n; p < end;){
		if(i > end - p)
			i = end - p;

		if(ise_wbwrite(p, i, off) != i)
			break;

		off += i;
		p += i;
		i = wbsize;
	}

	/* wait till the programming is done */
	start = m->ticks;
	do {
		x = flash.p[ooff];
		if((x & mirror(1<<7)) == mirror(1<<7))
			break;
	} while(TK2MS(m->ticks-start) < 1000);

	ise_clearerror();
	ise_error(0, x);
	ise_error(1, x>>16);

	ise_reset();
	flashprogpower(0);
	poperror();
}

/* amd/fujitsu standard command set
 *	I don't have an amd chipset to work with
 *	so I'm loathe to write this yet.  If someone
 *	else does, please send it to me and I'll
 *	incorporate it -- presotto@bell-labs.com
 */
static void
afs_reset(void)
{
	flash.p[0x55] = mirror(0xf0);	/* reset */
}
static void
afs_id(void)
{
	afs_reset();
	flash.p[0x55] = mirror(0xf0);	/* reset */
	flash.p[0x555] = mirror(0xaa);	/* query vendor block */
	flash.p[0x2aa] = mirror(0x55);
	flash.p[0x555] = mirror(0x90);
	flash.manid = flash.p[00];
	afs_reset();
	flash.p[0x555] = mirror(0xaa);	/* query vendor block */
	flash.p[0x2aa] = mirror(0x55);
	flash.p[0x555] = mirror(0x90);
	flash.devid = flash.p[01];
	afs_reset();
}
static void
afs_erase(ulong)
{
	error("amd/fujistsu erase not implemented");
}
static void
afs_write(void*, long, ulong)
{
	error("amd/fujistsu write not implemented");
}

M bitsy/devuda1341.c => bitsy/devuda1341.c +185 -95
@@ 41,14 41,14 @@ static int debug = 1;
 */

/* 
 * L3 setup and hold times (expressed in us)
 * L3 setup and hold times (expressed in µs)
 */
#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_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) */
#define L3_HaltTime			1		/* 190 ns */

/* UDA 1341 Registers */


@@ 76,7 76,7 @@ enum {
#define UDA1341_DATA0	0
#define UDA1341_DATA1	1
#define UDA1341_STATUS	2
#define UDA1341_L3Addr	5
#define UDA1341_L3Addr	0x14

typedef struct	AQueue	AQueue;
typedef struct	Buf	Buf;


@@ 90,7 90,7 @@ enum
	Qstatus,

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

	Aclosed		= 0,


@@ 98,11 98,7 @@ enum
	Awrite,

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


@@ 164,6 160,7 @@ static struct
	ulong	totaldma;
	ulong	idledma;
	ulong	faildma;
	ulong	samedma;
} iostats;

static	struct


@@ 174,15 171,11 @@ static	struct
	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,
[Vaudio]	"audio",	Fout|Fmono,		50,	50,
[Vmic]		"mic",		Fin,			 0,	 0,

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

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


@@ 420,19 413,24 @@ audioinit(void)
	/* do nothing */
}

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;

static void
audioenable(void)
enable(void)
{
	uchar	data[2];
	ushort	data;

	L3_init();

	/* Setup the uarts */
    ppcregs->assignment &= ~(1<<18);

    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);
	ppcregs->assignment &= ~(1<<18);

	sspregs->control0 = 0;
	sspregs->control0 = 0x031f; /* 16 bits, TI frames, serial clock rate 3 */


@@ 445,79 443,159 @@ audioenable(void)
	audiomute(0);

	/* external clock configured for 44100 samples/sec */
	gpioregs->direction |=  (GPIO_CLK_SET0_o|GPIO_CLK_SET1_o);
/* 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;
	gpioregs->set	= GPIO_CLK_SET0_o;
	gpioregs->clear	= GPIO_CLK_SET1_o;

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

	/* Reset the chip */
	data[0] = 2<<UdaStatusSC | 1<<UdaStatusIF | 1<<UdaStatusRST;
	L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1 );
	gpioregs->set = EGPIO_codec_reset;
	data = status0 | 1<<UdaStatusRST;
	L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&data, 1 );
	gpioregs->clear = EGPIO_codec_reset;
	gpioregs->set = EGPIO_codec_reset;
	/* write uda 1341 status[0] */
	data[0] &= ~(1<<UdaStatusRST); /* clear reset */
	L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1 );
	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 );

	/* write uda 1341 status[1] */
	data[0] = 0x80 | 0x3<<UdaStatusPC | 1<<UdaStatusIGS | 1<<UdaStatusOGS;
	L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1);
	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);
	}
}

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

	/* write uda 1341 data0[2] (mode switch) */
	data[0] = 0x80 | 3;	/* mode = 3 */
	L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 1);
	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;
	}
}

	/* 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);
static void
mxvolume(void) {
	int *left, *right;

	/* 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 );
	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);
		}
	}

	/* 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 );
}

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);
	if (debug) {
		print("#A: audio enabled\n");
		print("\tsspregs->control0 = 0x%lux\n", sspregs->control0);
		print("\tsspregs->control1 = 0x%lux\n", sspregs->control1);
		print("uchar	status1	= 0x%2.2ux\n", status1);
		print("uchar	data00	= 0x%2.2ux\n", data00);
	}
}

static void
sendaudio(IOstate *b) {
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) {
	/* interrupt routine calls this too */
	int n;

	if (debug > 1) print("#A: sendaudio\n");
	ilock(&b->ilock);
	while (b->next != b->filling) {
		assert(b->next->nbytes);
		if ((n = dmastart(b->dma, b->next->virt, b->next->nbytes)) == 0) {
	ilock(&s->ilock);
	while (s->next != s->filling) {
		assert(s->next->nbytes);
		if ((n = dmastart(s->dma, s->next->virt, s->next->nbytes)) == 0) {
			iostats.faildma++;
			break;
		}
		iostats.totaldma++;
		if (n == 1) iostats.idledma++;
		if (debug > 1) print("#A: dmastart @%p\n", b->next);
		b->next->nbytes = 0;
		b->next++;
		if (b->next == &b->buf[Nbuf])
			b->next = &b->buf[0];
		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];
	}
	iunlock(&b->ilock);
	iunlock(&s->ilock);
}

static void


@@ 557,6 635,7 @@ audiostat(Chan *c, char *db)
static Chan*
audioopen(Chan *c, int omode)
{
	IOstate *s;

	switch(c->qid.path & ~CHDIR) {
	default:


@@ 580,29 659,33 @@ audioopen(Chan *c, int omode)
			qunlock(&audio);
			error(Einuse);
		}
		audioenable();
		enable();
		memset(&iostats, 0, sizeof(iostats));
		if (omode & Aread) {
			inenable();
			s = &audio.i;
			/* read */
			audio.amode |= Aread;
			if(audio.i.bufinit == 0)
				bufinit(&audio.i);
			setempty(&audio.i);
			audio.i.chan = c;
			audio.i.dma = dmaalloc(0, 0, 8, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)&audio.o);
			s->chan = c;
			s->dma = dmaalloc(0, 0, 4, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)&audio.o);
		}
		if (omode & 0x2) {
			outenable();
			s = &audio.o;
			/* write */
//			amplifierpower(1);
//			audiomute(0);
			audio.amode |= Awrite;
			if(audio.o.bufinit == 0)
				bufinit(&audio.o);
			setempty(&audio.o);
			audio.o.chan = c;
			audio.o.dma = dmaalloc(0, 0, 8, 2, SSPXmitDMA, Port4SSP, audiointr, (void*)&audio.o);
			if(s->bufinit == 0)
				bufinit(s);
			setempty(s);
			s->chan = c;
			s->dma = dmaalloc(0, 0, 4, 2, SSPXmitDMA, Port4SSP, audiointr, (void*)s);
		}
//		mxvolume();
		mxvolume();
		qunlock(&audio);
		if (debug) print("#A: open done\n");
		break;


@@ 618,6 701,7 @@ audioopen(Chan *c, int omode)
static void
audioclose(Chan *c)
{
	IOstate *s;

	switch(c->qid.path & ~CHDIR) {
	default:


@@ 633,45 717,51 @@ audioclose(Chan *c)
		if (debug > 1) print("#A: close\n");
		if(c->flag & COPEN) {
			qlock(&audio);
			qlock(&audio.o);
			if(waserror()){
				qunlock(&audio.o);
				qunlock(&audio);
				nexterror();
			}
			if (audio.o.chan == c) {
				s = &audio.o;
				qlock(s);
				if(waserror()){
					qunlock(s);
					nexterror();
				}
				/* closing the write end */
				audio.amode &= ~Awrite;

				if (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);
				if (s->filling->nbytes) {
					/* send remaining partial buffer */
					s->filling++;
					if (s->filling == &s->buf[Nbuf])
						s->filling = &s->buf[0];
					sendaudio(s);
				}
				delay(2000);
		//		dmawait(audio.o.dma);
				if (!dmaidle(audio.o.dma))
					print("dma still busy\n");
				dmawait(s->dma);
				outdisable();
				amplifierpower(0);
				setempty(&audio.o);
				dmafree(audio.o.dma);
				setempty(s);
				dmafree(s->dma);
				qunlock(s);
				poperror();
			}
			if (audio.i.chan == c) {
				/* closing the read end */
				audio.amode &= ~Aread;
				indisable();
				setempty(&audio.i);
			}
			if (audio.amode == 0) {
				/* turn audio off */
				audiopower(0);
			}
			poperror();
			qunlock(&audio.o);
			qunlock(&audio);
			poperror();
			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);
		}
		break;
	}


@@ 807,8 897,8 @@ audiowrite(Chan *c, void *vp, long n, vlong)
			}

			if(strcmp(field[i], "reset") == 0) {
//				resetlevel();
//				mxvolume();
				resetlevel();
				mxvolume();
				goto cont0;
			}
			if(strcmp(field[i], "in") == 0) {

M bitsy/main.c => bitsy/main.c +2 -1
@@ 376,7 376,8 @@ gpioinit(void)
	gpioregs->falling = 0;
	gpioregs->altfunc |= 
		GPIO_LDD8_o|GPIO_LDD9_o|GPIO_LDD10_o|GPIO_LDD11_o
		|GPIO_LDD12_o|GPIO_LDD13_o|GPIO_LDD14_o|GPIO_LDD15_o;
		|GPIO_LDD12_o|GPIO_LDD13_o|GPIO_LDD14_o|GPIO_LDD15_o
		|GPIO_SSP_CLK_i;

	egpioreg = mapspecial(EGPIOREGS, 4);
}

M bitsy/sa1110dma.c => bitsy/sa1110dma.c +19 -23
@@ 41,7 41,6 @@ enum {
};

typedef struct DMAchan {
	Lock;
	int		allocated;
	Rendez	r;
	void	(*intr)(void*, ulong);


@@ 71,7 70,7 @@ dmainit(void) {
	int i;

	/* map the lcd regs into the kernel's virtual space */
	dmaregs = (struct dmaregs*)mapspecial(DMAREGS, NDMA*sizeof(struct dmaregs));;
	dmaregs = (struct dmaregs*)mapspecial(DMAREGS, NDMA*sizeof(struct dmaregs));
	if (debug) print("dma: dmaalloc registers 0x%ux mapped at 0x%p\n",
		DMAREGS, dmaregs);
	for (i = 0; i < NDMA; i++) {


@@ 110,6 109,8 @@ dmaalloc(int rd, int bigendian, int burstsize, int datumsize, int device, ulong 

void
dmafree(int i) {
	dmaregs[i].dcsr_clr = 0xff;
	dmaregs[i].ddar = 0;
	dma.chan[i].allocated = 0;
	dma.chan[i].intr = nil;
}


@@ 117,35 118,38 @@ dmafree(int i) {
ulong
dmastart(int chan, void *addr, int count) {
	ulong status, n;
	static int last;

	ilock(&dma.chan[chan]);
	/* If this gets called from interrupt routines, make sure ilocks are used */
	status = dmaregs[chan].dcsr_rd;
	if (debug > 1)
		iprint("dma: dmastart 0x%lux\n", status);

	if ((status & (1<<STRTA|1<<STRTB|1<<RUN)) == (1<<STRTA|1<<STRTB|1<<RUN)) {
		iunlock(&dma.chan[chan]);
		return 0;
	}
	cachewbregion((ulong)addr, count);
	n = 1;
	if ((status & (1<<BIU | 1<<STRTB)) == (1<<BIU | 1<<STRTB) ||
		(status & (1<<BIU | 1<<STRTA)) == 0) {
		assert((status & 1<<STRTA) == 0);
		if (status & 1<<RUN)
			n = 2;
		if (status & 1<<STRTA)
			iprint("writing busy dma entry 0x%lux\n", status);
		if (status & 1<<STRTB)
			n = (last == 1)?2:3;
		last = 2;
		dmaregs[chan].dstrtA = addr;
		dmaregs[chan].dxcntA = count-1;
		dmaregs[chan].dcsr_set = 1<<RUN | 1<<IE | 1<<STRTA;
	} else {
		assert((status & 1<<STRTB) == 0);
		if (status & 1<<RUN)
			n = 2;
		if (status & 1<<STRTB)
			iprint("writing busy dma entry 0x%lux\n", status);
		if (status & 1<<STRTA)
			n = (last == 2)?2:3;
		last = 1;
		dmaregs[chan].dstrtB = addr;
		dmaregs[chan].dxcntB = count-1;
		dmaregs[chan].dcsr_set = 1<<RUN | 1<<IE | 1<<STRTB;
	}
	iunlock(&dma.chan[chan]);
	return n;
}



@@ 154,7 158,8 @@ dmaidle(int chan) {
	ulong status;

	status = dmaregs[chan].dcsr_rd;
	return (status & (1<<STRTA|1<<STRTB|1<<RUN)) == 0;
	if (debug > 1) print("dmaidle: 0x%lux\n", status);
	return (status & (1<<STRTA|1<<STRTB)) == 0;
}

static int


@@ 162,7 167,7 @@ _dmaidle(void* chan) {
	ulong status;

	status = dmaregs[(int)chan].dcsr_rd;
	return (status & (1<<STRTA|1<<STRTB|1<<RUN)) == 0;
	return (status & (1<<STRTA|1<<STRTB)) == 0;
}

void


@@ 182,7 187,6 @@ dmaintr(Ureg*, void *x)
	ulong dcsr, donebit;

	i = regs - dmaregs;
	ilock(&dma.chan[i]);
	dcsr = regs->dcsr_rd;
	if (debug > 1)
		iprint("dma: interrupt channel %d, status 0x%lux\n", i, dcsr);


@@ 191,19 195,11 @@ dmaintr(Ureg*, void *x)
	donebit = 1<<((dcsr&1<<BIU)?DONEA:DONEB);
	if (dcsr & donebit) {
		regs->dcsr_clr = donebit;
		/* try the other bit as well */
		donebit ^= 1<<DONEA|1<<DONEB;
		if (dcsr & donebit) {
			regs->dcsr_clr = donebit;
		}
		iunlock(&dma.chan[i]);
		if (dma.chan[i].intr) {
			dcsr &= 1<<STRTA | 1<<STRTB | 1<<RUN;
			(*dma.chan[i].intr)(dma.chan[i].param, dcsr == 0);
			(*dma.chan[i].intr)(dma.chan[i].param, dcsr & (1<<DONEA|1<<DONEB));
		}
		wakeup(&dma.chan[i].r);
		return;
	}
	iprint("spurious DMA interrupt, channel %d, status 0x%lux\n", i, dcsr);
	iunlock(&dma.chan[i]);
}