~kris/9p

9hist

76222f6be822c006de9fa847c9b2f6c300f2b9fd — David du Colombier 24 years ago ad9af13
Plan 9 from Bell Labs 2002-06-20
4 files changed, 1352 insertions(+), 114 deletions(-)

M bitsy/etherif.h
M pc/devtv.c
M pc/etherif.h
M pc/sdata.c
M bitsy/etherif.h => bitsy/etherif.h +1 -0
@@ 22,6 22,7 @@ struct Ether {
	long	(*ifstat)(Ether*, void*, long, ulong);
	long 	(*ctl)(Ether*, void*, long); /* custom ctl messages */
	void	(*power)(Ether*, int);	/* power on/off */
	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
	void	*ctlr;

	Queue*	oq;

M pc/devtv.c => pc/devtv.c +1347 -111
@@ 9,21 9,30 @@
#include	"fns.h"
#include	"../port/error.h"
#include "io.h"
#include "hcwAMC.h"

#define max(a, b)	(((a) > (b))? (a): (b))

enum {
	Qdir = 0,
	Qdata,
	Qsubdir,
	Qsubbase,
	Qvdata = Qsubbase,
	Qadata,
	Qctl,
	Qregs,

	Brooktree_vid = 0x109e,
	Brooktree_848_did = 0x0350,
	Brooktree_878_did = 0x036E,
	Intel_vid = 0x8086,
	Intel_82437_did = 0x122d,

	K = 1024,
	M = K * K,

	Ntvs = 4,

	Numring = 16,

	ntsc_rawpixels = 910,


@@ 35,21 44,30 @@ enum {
	ntsc_vdelay = 26,					// # of scan lines.
	ntsc_vscale = 0,

	i2c_nostop = 1 << 5,
	i2c_nos1b = 1 << 4,
	i2c_timing = 7 << 4,
	i2c_bt848w3b = 1 << 2,
	i2c_bt848scl = 1 << 1,
	i2c_bt848sda = 1 << 0,
	i2c_scl = i2c_bt848scl,
	i2c_sda = i2c_bt848sda,

	i2c_miroproee = 0x80,				// MIRO PRO EEPROM
	i2c_tea6300 = i2c_miroproee,
	i2c_tea6300 = 0x80,
	i2c_tda8425 = 0x82,
	i2c_tda9840 = 0x84,
	i2c_tda9850 = 0xb6,
	i2c_haupee = 0xa0,					// Hauppage EEPROM
	i2c_stbee = 0xae,					// STB EEPROM
	i2c_msp3400 = 0x80,

	i2c_timeout = 1000,
	i2c_delay = 10,

	Bt848_miropro = 0,
	Bt848_miro,
	Bt878_hauppauge,

	// Bit fields.
	iform_muxsel1 = 3 << 5,				// 004


@@ 69,6 87,8 @@ enum {
	adc_crush = 1 << 0,					// 068

	colorfmt_rgb16 = (2 << 4) | (2 << 0),	// 0D4
	colorfmt_YCbCr422 = (8 << 4) | (8 << 0),
	colorfmt_YCbCr411 = (9 << 4) | (9 << 0),
	colorctl_gamma = 1 << 4,			// 0D8
	capctl_fullframe = 1 << 4,				// 0DC
	capctl_captureodd = 1 << 1,


@@ 79,6 99,11 @@ enum {
	intstat_i2crack = 1 << 25,
	intstat_scerr = 1 << 19,
	intstat_ocerr = 1 << 18,
	intstat_pabort = 1 << 17,
	intstat_riperr = 1 << 16,
	intstat_pperr = 1 << 15,
	intstat_fdsr = 1 << 14,
	intstat_ftrgt = 1 << 13,
	intstat_fbus = 1 << 12,
	intstat_risci = 1 << 11,
	intstat_i2cdone = 1 << 8,	


@@ 88,10 113,14 @@ enum {
	intstat_fmtchg = 1 << 0,
	intmask_etbf = 1 << 23,				// 104

	gpiodmactl_pltp23_16 = 2 << 6,		// 10C
	gpiodmactl_apwrdn = 1 << 26,			// 10C
	gpiodmactl_daes2 = 1 << 13,		
	gpiodmactl_daiomda = 1 << 6,
	gpiodmactl_pltp23_16 = 2 << 6,
	gpiodmactl_pltp23_0 = 0 << 6,	
	gpiodmactl_pltp1_16 = 2 << 4,
	gpiodmactl_pltp1_0 = 0 << 4,
	gpiodmactl_acapenable = 1 << 4,
	gpiodmactl_pktp_32 = 3 << 2,		
	gpiodmactl_pktp_0 = 0 << 2,		
	gpiodmactl_riscenable = 1 << 1,


@@ 99,11 128,14 @@ enum {

	// RISC instructions and parameters.
	fifo_vre = 0x4,
	fifo_vro = 0xC,
	fifo_vro = 0xc,
	fifo_fm1 = 0x6,
	fifo_fm3 = 0xe,

	riscirq = 1 << 24,
	riscwrite = 0x1 << 28,
	riscwrite = 1 << 28,
	riscwrite123 = 9 << 28,
	riscwrite1s23 = 11 << 28,
		riscwrite_sol = 1 << 27,
		riscwrite_eol = 1 << 26,
	riscskip = 0x2 << 28,


@@ 113,8 145,104 @@ enum {
		riscsync_vre = fifo_vre << 0,
		riscsync_vro = fifo_vro << 0,
		riscsync_fm1 = fifo_fm1 << 0,
		riscsync_fm3 = fifo_fm3 << 0,
	risclabelshift_set = 16,
	risclabelshift_reset = 20,

	AudioTuner = 0,
	AudioRadio,
	AudioExtern,
	AudioIntern,
	AudioOff,
	AudioOn,
	
	asel_tv = 0,
	asel_radio,
	asel_mic,
	asel_smxc,

	Hwbase_ad = 448000,

	msp_dem = 0x10,
	msp_bbp = 0x12,

	// Altera definitions.
	gpio_altera_data = 1 << 0,
	gpio_altera_clock = 1 << 20,
	gpio_altera_nconfig = 1 << 23,

	Ial = 0x140001,
	Idma = 0x100002,

	Adsp = 0x7fd8,
	Adsp_verifysystem = 1,
	Adsp_querysupportplay,
	Adsp_setstyle,
	Adsp_setsrate,
	Adsp_setchannels,
	Adsp_setresolution,
	Adsp_setcrcoptions,
	Adsp_bufenqfor,
	Adsp_logbuffer,
	Adsp_startplay,
	Adsp_stopplay,
	Adsp_autostop,
	Adsp_startrecord,
	Adsp_stoprecord,
	Adsp_getlastprocessed,
	Adsp_pause,
	Adsp_resume,
	Adsp_setvolume,
	Adsp_querysupportrecord,
	Adsp_generalbufenq,
	Adsp_setdownmixtype,
	Adsp_setigain,
	Adsp_setlineout,
	Adsp_setlangmixtype,

	Kfir_gc = 0,
	Kfir_dsp_riscmc,
	Kfir_dsp_risccram,
	Kfir_dsp_unitmc,
	Kfir_bsm_mc,
	Kfir_mux_mc,

	Kfir_devid_gc = 7,
	Kfir_devid_dsp = 4,
	Kfir_devid_bsm = 5,
	Kfir_devid_mux = 8,

	Kfir_200 = 200,
	Kfir_dev_inst = Kfir_200,
	Kfir_201 = 201,
	Kfir_exec = Kfir_201,
	Kfir_202 = 202,
	Kfir_adr_eready = 254,

	Kfir_d_eready_encoding = 0,
	Kfir_d_eready_ready,
	Kfir_d_eready_test,
	Kfir_d_eready_stopdetect,
	Kfir_d_eready_seqend,

	VT_KFIR_OFF = 0,
	VT_KFIR_ON,

	VT_KFIR_LAYER_II = 1,
	VT_KFIR_STEREO = 1,

	Gpioinit = 0,
	Gpiooutput,
	Gpioinput,

	Srate_5512 = 0,
	Srate_11025 = 2,
	Srate_16000 = 3,
	Srate_22050 = 4,
	Srate_32000 = 5,
	Srate_44100 = 6,
	Srate_48000 = 7,

};

typedef struct {


@@ 194,6 322,8 @@ typedef struct {
	ulong	gpiodata[64];	// 200-2FC
} Bt848;

#define packetlen	i2c

typedef struct {
	char		*name;
  	ushort	freq_vhfh;	// Start frequency


@@ 213,18 343,40 @@ typedef struct {

typedef struct {
	Lock;
	Rendez;
	Bt848	*bt848;
	Bt848	*bt878;			// Really only audio control registers
	Variant	*variant;
	Tuner	*tuner;
	Pcidev	*pci;
	uchar	i2ctuneraddr;
	uchar	i2ccmd;			// I2C command
	int		board;			// What board is this?

	ulong	cfmt;			// Current color format.
	int		channel;			// Current channel
	Ref		fref;				// Copying images?
	int		nframes;			// Number of frames to capture.
	Frame	*frames;			// DMA program
	int		lframe;			// Last frame DMAed
	int		lvframe;			// Last video frame DMAed
	uchar	*amux;			// Audio multiplexer.
	int		nablocks;			// Number of audio blocks allocated
	int		absize;			// Audio block size
	int		narblocks;		// Number of audio blocks received
	ulong	*arisc;			// Audio risc bloc
	uchar	*abuf;			// Audio data buffers
	char		ainfo[128];

	// WinTV/PVR stuff.
	int		msp;	
	Lock		kfirlock;
	ulong	i2cstate;			// Last i2c state.
	int		gpiostate;			// Current GPIO state
	ulong	alterareg;			// Last used altera register
	ulong	alteraclock;		// Used to clock the altera
	int		asrate;			// Audio sample rate
	uchar	aleft, aright;		// Left and right audio volume
	ulong	kfirclock;
	Ref		aref;				// Copying audio?
} Tv;

enum {


@@ 268,46 420,104 @@ static Tuner tuners[] = {
		0x01, 0x02, 0x08, 0x8e, 732 },
};

static int hp_tuners[] = {
	Notuner,  
        	Notuner,    
        	Notuner,  
        	Notuner,  
        	Notuner,    
        	PhilipsNTSC,  
        	Notuner,   
        	Notuner,    
        	PhilipsPAL,   
        	PhilipsSECAM, 
        	PhilipsNTSC,
        	PhilipsPALI, 
        	Notuner,        
        	Notuner,    
        	TemicPAL,     
        	TemicPALI,   
        	Notuner,        
        	PhilipsSECAM, 
        	PhilipsNTSC, 
        	PhilipsPALI, 
        	Notuner,     
        	PhilipsPAL, 
        	Notuner,       
        	PhilipsNTSC,
};

enum {
	CMvstart,
	CMastart,
	CMastop,
	CMvgastart,
	CMvstop,
	CMchannel,
	CMcolormode,
	CMvolume,
	CMmute,
};

static Cmdtab tvctlmsg[] = {
	CMvstart,			"vstart",			2,
	CMastart,			"astart",			5,
	CMastop,			"astop",			1,
	CMvgastart,		"vgastart",		3,
	CMvstop,			"vstop",			1,
	CMchannel,		"channel",			3,
};

static Dirtab tvtab[]={
	".",		{ Qdir, 0, QTDIR },	0,	DMDIR|0555,
	"tv",		{ Qdata, 0 },		0,	0600,
	"tvctl",	{ Qctl, 0 },			0,	0600,
	"tvregs",	{ Qregs, 0 },		0,	0400,
	CMcolormode,		"colormode",		2,
	CMvolume,		"volume",			3,
	CMmute,			"mute",			1,
};

static Variant variant[] = {
{	Brooktree_vid,	Brooktree_848_did,	"Brooktree 848 TV tuner",	},
{	Brooktree_vid,	Brooktree_878_did,	"Brooktree 878 TV tuner",	},
};

static char *boards[] = {
	"MIRO PRO",
	"MIRO",
	"Hauppauge Bt878",
};

static ushort Adspfsample[] = {
	0x500, 0x700, 0x400, 0x600, 0x300, 0x200, 0x000, 0x100
};
static ushort Adspstereorates[] = {
	64, 96, 112, 128, 160, 192, 224, 256, 320, 384
};

static uchar miroamux[] = { 2, 0, 0, 0, 10, 0 };
static uchar hauppaugeamux[] = { 0, 1, 2, 3, 4, 0 };
static char *nicamstate[] = {
	"analog", "???", "digital", "bad digital receiption" 
};

static Tv *tv;

static Tv tvs[Ntvs];
static int ntvs;

static int i2cread(Tv *, uchar, uchar *);
static int i2cwrite(Tv *, uchar, uchar, uchar, int);
static void tvinterrupt(Ureg *, Tv *);
static void vgastart(Tv *, ulong, int);
static void vstart(Tv *, int, int, int, int);
static void astart(Tv *, char *, uint, uint, uint);
static void vstop(Tv *);
static void astop(Tv *);
static void colormode(Tv *, char *);
static void frequency(Tv *, int, int);
static int getbpp(Tv *);
static int getbitspp(Tv *);
static char *getcolormode(ulong);
static int mspreset(Tv *);
static void i2cscan(Tv *);
static int kfirinitialize(Tv *);
static void msptune(Tv *);
static void mspvolume(Tv *, int, int, int);
static void gpioenable(Tv *, ulong, ulong);
static void gpiowrite(Tv *, ulong, ulong);

static void
tvinit(void)


@@ 323,6 533,7 @@ tvinit(void)
	pci = nil;
	while ((pci = pcimatch(pci, 0, 0)) != nil) {
		int i, t;
		Tv *tv;
		Bt848 *bt848;
		ushort hscale, hdelay;
		uchar v;


@@ 333,20 544,17 @@ tvinit(void)
		if (i == nelem(variant))
			continue;

		if (tv) {
			print("#V: Currently there is only support for one TV, ignoring %s\n",
				variant[i].name);
		if (ntvs >= Ntvs) {
			print("#V: Too many TV cards found\n");
			continue;
		}

		tv = (Tv *)malloc(sizeof(Tv));
		assert(tv);

		tv = &tvs[ntvs++];
		tv->variant = &variant[i];
		tv->pci = pci;
		tv->bt848 = (Bt848 *)upamalloc(pci->mem[0].bar & ~0x0F, 4 * K, K);
		if (tv->bt848 == nil)
			panic("#V: Cannot allocate memory for Bt848\n");
			panic("#V: Cannot allocate memory for Bt848");
		bt848 = tv->bt848;

		// i2c stuff.


@@ 355,26 563,56 @@ tvinit(void)
		else 
			tv->i2ccmd = i2c_timing | i2c_bt848scl | i2c_bt848sda;

		{
			bt848->gpioouten = 0;

			bt848->gpioouten |= 1 << 5;
			bt848->gpiodata[0] |= 1 << 5;
			microdelay(2500);
			bt848->gpiodata[0] |= 1 << 5;
			microdelay(2500);
		t = 0;
		if (i2cread(tv, i2c_haupee, &v)) {
			uchar ee[256];
			Pcidev *pci878;
			Bt848 *bt878;

			tv->board = Bt878_hauppauge;
			if (!i2cwrite(tv, i2c_haupee, 0, 0, 0))
				panic("#V: Cannot write to Hauppauge EEPROM");
			for (i = 0; i != sizeof ee; i++)
				if (!i2cread(tv, i2c_haupee + 1, &ee[i]))
					panic("#V: Cannot read from Hauppauge EEPROM");

			if (ee[9] > sizeof hp_tuners / sizeof hp_tuners[0])
				panic("#V: Tuner out of range (max %d, this %d)",
					sizeof hp_tuners / sizeof hp_tuners[0], ee[9]);
			t = hp_tuners[ee[9]];

			// Initialize the audio channel.
			if ((pci878 = pcimatch(nil, Brooktree_vid, 0x878)) == nil)
				panic("#V: Unsupported Hauppage board");

			tv->bt878 = bt878 = 
				(Bt848 *)upamalloc(pci878->mem[0].bar & ~0x0F, 4 * K, K);
			if (bt878 == nil)
				panic("#V: Cannot allocate memory for the Bt878");

			kfirinitialize(tv);
			// i2cscan(tv);
			mspreset(tv);

			bt878->gpiodmactl = 0;
			bt878->intstat = (ulong)-1;
			intrenable(pci878->intl, (void (*)(Ureg *, void *))tvinterrupt, 
					tv, pci878->tbdf, "tv");	

			tv->amux = hauppaugeamux;
		}
		
		if (i2cread(tv, i2c_haupee, &v)) 
			panic("#V: Cannot deal with hauppauge boards\n");
		if (i2cread(tv, i2c_stbee, &v))
		else if (i2cread(tv, i2c_stbee, &v)) {
			USED(t);
			panic("#V: Cannot deal with STB cards\n");
		if (i2cread(tv, i2c_miroproee, &v)) {
		}
		else if (i2cread(tv, i2c_miroproee, &v)) {
			tv->board = Bt848_miropro;
			t = ((bt848->gpiodata[0] >> 10) - 1) & 7;
			tv->amux = miroamux;
		}
		else {
			tv->board = Bt848_miro;
			tv->amux = miroamux;
			t = ((bt848->gpiodata[0] >> 10) - 1) & 7;
		}



@@ 396,7 634,7 @@ tvinit(void)

		// setpll(bt848);

		bt848->colorfmt = colorfmt_rgb16;
		tv->cfmt = bt848->colorfmt = colorfmt_rgb16;

		hscale = (ntsc_rawpixels * 4096) / ntsc_sqpixels - 4096;
		hdelay = (ntsc_clkx1delay * ntsc_hactive) / ntsc_clkx1hactive;


@@ 432,16 670,17 @@ tvinit(void)
		bt848->intstat = (ulong)-1;
		bt848->intmask = intmask | 
			intstat_vsync | intstat_scerr | intstat_risci | intstat_ocerr | 
			intstat_vpress | intstat_fmtchg | intstat_hlock;
			intstat_vpress | intstat_fmtchg;


		bt848->gpioouten &= ~0xF;
		bt848->gpioouten |= 0xF;
		bt848->gpiodata[0] &= ~0xF;
		bt848->gpiodata[0] |= 2;	// Enable audio on the MIRO card.
		if (tv->amux) {
			gpioenable(tv, ~0xfff, 0xfff);
			gpiowrite(tv, ~0xfff, tv->amux[AudioRadio]);
		}

		print("#V: %s (rev %d) (%s/%s) %.8ulX, intl %d\n", 
				tv->variant->name, pci->rid, boards[tv->board],
				tv->tuner->name, pci->mem[0].bar & ~0x0F, pci->intl);
		print("#V%ld: %s (rev %d) (%s/%s) intl %d\n", 
				tv - tvs, tv->variant->name, pci->rid, boards[tv->board],
				tv->tuner->name, pci->intl);

		intrenable(pci->intl, (void (*)(Ureg *, void *))tvinterrupt, 
				tv, pci->tbdf, "tv");	


@@ 454,29 693,113 @@ tvattach(char *spec)
	return devattach('V', spec);
}

#define TYPE(q)		((int)((q).path & 0xff))
#define DEV(q)			((int)(((q).path >> 8) & 0xff))
#define QID(d, t)		((((d) & 0xff) << 8) | (t))

static int
tv1gen(Chan *c, int i, Dir *dp)
{
	Qid qid;

	switch (i) {
	case Qvdata:
		mkqid(&qid, QID(DEV(c->qid), Qvdata), 0, QTFILE);
		devdir(c, qid, "video", 0, eve, 0444, dp);
		return 1;
	case Qadata:
		mkqid(&qid, QID(DEV(c->qid), Qadata), 0, QTFILE);
		devdir(c, qid, "audio", 0, eve, 0444, dp);
		return 1;
	case Qctl:
		mkqid(&qid, QID(DEV(c->qid), Qctl), 0, QTFILE);
		devdir(c, qid, "ctl", 0, eve, 0444, dp);
		return 1;
	case Qregs:
		mkqid(&qid, QID(DEV(c->qid), Qregs), 0, QTFILE);
		devdir(c, qid, "regs", 0, eve, 0444, dp);
		return 1;
	}
	return -1;
}

static int
tvgen(Chan *c, char *, Dirtab *, int, int i, Dir *dp)
{
	Qid qid;
	int dev;

	dev = DEV(c->qid);
	switch (TYPE(c->qid)) {
	case Qdir:
		if (i == DEVDOTDOT) {
			mkqid(&qid, Qdir, 0, QTDIR);
			devdir(c, qid, "#V", 0, eve, 0555, dp);
			return 1;
		}

		if (i >= ntvs)
			return -1;
	
		mkqid(&qid, QID(i, Qsubdir), 0, QTDIR);
		snprint(up->genbuf, sizeof(up->genbuf), "tv%d", i);
		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
		return 1;

	case Qsubdir:
		if (i == DEVDOTDOT) {
			mkqid(&qid, QID(dev, Qdir), 0, QTDIR);
			snprint(up->genbuf, sizeof(up->genbuf), "tv%d", dev);
			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
			return 1;
		}
		
		return tv1gen(c, i + Qsubbase, dp);
	
	case Qvdata:
	case Qadata:
	case Qctl:
	case Qregs:
		return tv1gen(c, TYPE(c->qid), dp);

	default:
		return -1;
	}
}

static Walkqid *
tvwalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, tvtab, nelem(tvtab), devgen);
	return devwalk(c, nc, name, nname, 0, 0, tvgen);
}

static int
tvstat(Chan *c, uchar *db, int n)
{
	return devstat(c, db, n, tvtab, nelem(tvtab), devgen);
	return devstat(c, db, n, 0, 0, tvgen);
}

static Chan*
tvopen(Chan *c, int omode)
{
	switch ((int)c->qid.path) {
	if (omode != OREAD && TYPE(c->qid) != Qctl)
		error(Eperm);

	switch (TYPE(c->qid)) {
	case Qdir:
		return devopen(c, omode, tvtab, nelem(tvtab), devgen);
		return devopen(c, omode, nil, 0, tvgen);
	case Qadata:
		if (tvs[DEV(c->qid)].bt878 == nil)
			error(Enonexist);
		break;
	}

	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;

	if (TYPE(c->qid) == Qadata)
		c->session = (Session *)0;
	return c;
}



@@ 484,24 807,33 @@ static void
tvclose(Chan *)
{}

static int
audioblock(void *)
{
	return 1;
}
	
static long
tvread(Chan *c, void *a, long n, vlong offset)
{
	static char regs[10 * K];
	static int regslen;
	Tv *tv;
	char *e, *p;
	uchar *src;

	USED(offset);

	switch((int)c->qid.path) {
	switch(TYPE(c->qid)) {
	case Qdir:
		return devdirread(c, a, n, tvtab, nelem(tvtab), devgen);
	case Qsubdir:
		return devdirread(c, a, n, 0, 0, tvgen);

	case Qdata: {
		uchar *src;
	case Qvdata: {
		int bpf, nb;

		bpf = ntsc_hactive * ntsc_vactive * getbpp(tv);
		tv = &tvs[DEV(c->qid)];
		bpf = ntsc_hactive * ntsc_vactive * getbitspp(tv) / 8;

		if (offset >= bpf) 
			return 0;


@@ 511,12 843,13 @@ tvread(Chan *c, void *a, long n, vlong offset)
			nb = bpf - offset;

		ilock(tv);
		if (tv->frames == nil || tv->lframe >= tv->nframes) {
		if (tv->frames == nil || tv->lvframe >= tv->nframes ||
			tv->frames[tv->lvframe].fbase == nil) {
			iunlock(tv);
			return 0;
		}

		src = tv->frames[tv->lframe].fbase;
		src = tv->frames[tv->lvframe].fbase;
		incref(&tv->fref);
		iunlock(tv);



@@ 525,23 858,83 @@ tvread(Chan *c, void *a, long n, vlong offset)
		return nb;
	}

	case Qadata: {
		ulong uablock = (ulong)c->session, bnum, tvablock;
		int boffs, nbytes;

		tv = &tvs[DEV(c->qid)];
		if (tv->bt878 == nil)
			error("#V: No audio device");
		if (tv->absize == 0)
			error("#V: audio not initialized");

		bnum = offset / tv->absize;
		boffs = offset % tv->absize;
		nbytes = tv->absize - boffs;

		incref(&tv->aref);
		while (1) {
			tvablock = tv->narblocks;	// Current tv block.

			if (uablock == 0)
				uablock = tvablock - 1;

			if (tvablock >= uablock + bnum + tv->narblocks)
				uablock = tvablock - 1 - bnum;

			if (uablock + bnum == tvablock) {
				sleep(tv, audioblock, nil);
				continue;
			}
			break;
		}

		print("uablock %ld, bnum %ld, boffs %d, nbytes %d, tvablock %ld\n",
				uablock, bnum, boffs, nbytes, tvablock);
		src = tv->abuf + ((uablock + bnum) % tv->nablocks) * tv->absize;
		print("copying from %.8ulX (abuf %.8ulX), nbytes %d (block %ld.%ld)\n", 
				src + boffs, tv->abuf, nbytes, uablock, bnum);

		memmove(a, src + boffs, nbytes);
		decref(&tv->aref);
		
		uablock += (boffs + nbytes) % tv->absize;
		c->session = (Session *)uablock;

		return nbytes;
	}

	case Qctl: {
		char str[128];

		snprint(str, sizeof str, "%dx%dx%d\n", 
				ntsc_hactive, ntsc_vactive, getbpp(tv));
		return readstr(offset, a, n, str);
		tv = &tvs[DEV(c->qid)];
		snprint(str, sizeof str, "%dx%dx%d %s channel %d %s\n", 
				ntsc_hactive, ntsc_vactive, getbitspp(tv),
				getcolormode(tv->cfmt), tv->channel,
				tv->ainfo);
		return readstr(offset, a, strlen(str) + 1, str);
	}
		
	case Qregs:
		if (offset == 0) {
			Bt848 *bt848 = tv->bt848;
			Bt848 *bt848;
			int i;

			tv = &tvs[DEV(c->qid)];
			bt848 = tv->bt848;

			e = regs + sizeof(regs);
			p = regs;
			for (i = 0; i < 0x300 >> 2; i++)
				p = seprint(p, e, "%.3X %.8ulX\n", i << 2, ((ulong *)bt848)[i]);
			if (tv->bt878) {
				bt848 = tv->bt878;

				for (i = 0; i < 0x300 >> 2; i++)
					p = seprint(p, e, "%.3X %.8ulX\n", 
							i << 2, ((ulong *)bt848)[i]);
			}
			
			regslen = p - regs;
		}



@@ 553,7 946,7 @@ tvread(Chan *c, void *a, long n, vlong offset)
		return readstr(offset, a, n, &regs[offset]);

	default:
		n=0;
		n = 0;
		break;
	}
	return n;


@@ 564,8 957,10 @@ tvwrite(Chan *c, void *a, long n, vlong)
{
	Cmdbuf *cb;
	Cmdtab *ct;
	Tv *tv;

	switch((int)c->qid.path) {
	tv = &tvs[DEV(c->qid)];
	switch(TYPE(c->qid)) {
	case Qctl:
		cb = parsecmd(a, n);
		if(waserror()){


@@ 579,6 974,16 @@ tvwrite(Chan *c, void *a, long n, vlong)
					ntsc_hactive, ntsc_vactive, ntsc_hactive);
			break;

		case CMastart:
			astart(tv, cb->f[1], (uint)strtol(cb->f[2], (char **)nil, 0),
					(uint)strtol(cb->f[3], (char **)nil, 0),
					(uint)strtol(cb->f[4], (char **)nil, 0));
			break;

		case CMastop:
			astop(tv);
			break;

		case CMvgastart:
			vgastart(tv, strtoul(cb->f[1], (char **)nil, 0),
					(int)strtoul(cb->f[2], (char **)nil, 0));


@@ 592,8 997,28 @@ tvwrite(Chan *c, void *a, long n, vlong)
			frequency(tv, (int)strtol(cb->f[1], (char **)nil, 0), 
				(int)strtol(cb->f[2], (char **)nil, 0));
			break;

		case CMcolormode:
			colormode(tv, cb->f[1]);
			break;

		case CMvolume:
			if (!tv->msp)
				error("#V: No volume control");

			mspvolume(tv, 0, (int)strtol(cb->f[1], (char **)nil, 0), 
						(int)strtol(cb->f[2], (char **)nil, 0));
			break;

		case CMmute:
			if (!tv->msp)
				error("#V: No volume control");

			mspvolume(tv, 1, 0, 0);
			break;
		}
		poperror();
		free(cb);
		break;

	default:


@@ 626,60 1051,91 @@ Dev tvdevtab = {
static void
tvinterrupt(Ureg *, Tv *tv)
{
	Bt848 *bt848 = tv->bt848;
	Bt848 *bt848 = tv->bt848,
		*bt878 = tv->bt878;

	while (1) {
		ulong status;
		ulong vstat, astat;
		uchar fnum;

		status = bt848->intstat;
		fnum = (status >> intstat_riscstatshift) & 0xf;
		status &= bt848->intmask;
		vstat = bt848->intstat;
		fnum = (vstat >> intstat_riscstatshift) & 0xf;
		vstat &= bt848->intmask;

		if (status == 0)
		if (bt878)
			astat = bt878->intstat & bt878->intmask;
		else
			astat = 0;

		if (vstat == 0 && astat == 0)
			break;

		bt848->intstat = status;
		if (astat)
		print("vstat %.8uX, astat %.8uX\n", vstat, astat);

		bt848->intstat = vstat;
		if (bt878)
			bt878->intstat = astat;

		if ((status & intstat_fmtchg) == intstat_fmtchg) {
		if ((vstat & intstat_fmtchg) == intstat_fmtchg) {
			iprint("int: fmtchg\n");
			status &= ~intstat_fmtchg;
			vstat &= ~intstat_fmtchg;
		}

		if ((status & intstat_vpress) == intstat_vpress) {
			iprint("int: vpress\n");
			status &= ~intstat_vpress;
		if ((vstat & intstat_vpress) == intstat_vpress) {
//			iprint("int: vpress\n");
			vstat &= ~intstat_vpress;
		}
		
		if ((status & intstat_vsync) == intstat_vsync)
			status &= ~intstat_vsync;
		if ((vstat & intstat_vsync) == intstat_vsync) {
			vstat &= ~intstat_vsync;
		}

		if ((status & intstat_scerr) == intstat_scerr) {
		if ((vstat & intstat_scerr) == intstat_scerr) {
			iprint("int: scerr\n");
			bt848->gpiodmactl &= 
				~(gpiodmactl_riscenable|gpiodmactl_fifoenable);
			bt848->gpiodmactl |= gpiodmactl_fifoenable;
			bt848->gpiodmactl |= gpiodmactl_riscenable;
			status &= ~intstat_scerr;
			vstat &= ~intstat_scerr;
		}

		if ((status & intstat_risci) == intstat_risci) {
			tv->lframe = fnum;
			status &= ~intstat_risci;
		if ((vstat & intstat_risci) == intstat_risci) {
			tv->lvframe = fnum;
			vstat &= ~intstat_risci;
		}
			
		if ((status & intstat_ocerr) == intstat_ocerr) {
		if ((vstat & intstat_ocerr) == intstat_ocerr) {
			iprint("int: ocerr\n");
			status &= ~intstat_ocerr;
			vstat &= ~intstat_ocerr;
		}

		if ((status & intstat_fbus) == intstat_fbus) {
		if ((vstat & intstat_fbus) == intstat_fbus) {
			iprint("int: fbus\n");
			status &= ~intstat_fbus;
			vstat &= ~intstat_fbus;
		}

		if (status)
			iprint("int: ignored interrupts %.8ulX\n", status);
		if (vstat)
			iprint("int: (v) ignored interrupts %.8ulX\n", vstat);

		if ((astat & intstat_risci) == intstat_risci) {
			tv->narblocks++;
			if ((tv->narblocks % 100) == 0)
				print("a");
			wakeup(tv);
			astat &= ~intstat_risci;
		}

		if ((astat & intstat_fdsr) == intstat_fdsr) {
			iprint("int: (a) fdsr\n");
			bt848->gpiodmactl &= 
				~(gpiodmactl_acapenable | 
					gpiodmactl_riscenable | gpiodmactl_fifoenable);
			astat &= ~intstat_fdsr;
		}
			
		if (astat)
			iprint("int: (a) ignored interrupts %.8ulX\n", astat);
	}
}



@@ 713,17 1169,17 @@ i2cread(Tv *tv, uchar off, uchar *v)
}

static int
i2cwrite(Tv *tv, uchar off, uchar d1, uchar d2, int both)
i2cwrite(Tv *tv, uchar addr, uchar sub, uchar data, int both)
{
	Bt848 *bt848 = tv->bt848;
	ulong intstat, data;
	ulong intstat, d;
	int i;

	bt848->intstat	= intstat_i2cdone;
	data = (off << 24) | (d1 << 16) | tv->i2ccmd;
	d = (addr << 24) | (sub << 16) | tv->i2ccmd;
	if (both)
		data |= (d2 << 8) | i2c_bt848w3b;
	bt848->i2c = data;
		d |= (data << 8) | i2c_bt848w3b;
	bt848->i2c = d;

	intstat = 0;
	for (i = 0; i != 1000; i++) {


@@ 732,8 1188,8 @@ i2cwrite(Tv *tv, uchar off, uchar d1, uchar d2, int both)
		microdelay(1000);
	}

	if (i == 1000) {
		print("i2cread: timeout\n");
	if (i == i2c_timeout) {
		print("i2cwrite: timeout\n");
		return 0;
	}



@@ 744,7 1200,7 @@ i2cwrite(Tv *tv, uchar off, uchar d1, uchar d2, int both)
}

static ulong *
riscframe(ulong paddr, int fnum, int w, int h, int stride, ulong **lastjmp)
riscpacked(ulong paddr, int fnum, int w, int h, int stride, ulong **lastjmp)
{
	ulong *p, *pbase;
	int i;


@@ 785,6 1241,144 @@ riscframe(ulong paddr, int fnum, int w, int h, int stride, ulong **lastjmp)
	return pbase;
}

static ulong *
riscplanar411(ulong paddr, int fnum, int w, int h, ulong **lastjmp)
{
	ulong *p, *pbase, Cw, Yw, Ch;
	uchar *Ybase, *Cbbase, *Crbase;
	int i, bitspp;

	bitspp = 6;
	assert(w * bitspp / 8 <= 0x7FF);
	pbase = p = (ulong *)malloc((h + 6) * 5 * sizeof(ulong));
	assert(p);

	Yw = w;
	Ybase = (uchar *)paddr;
	Cw = w >> 1;
	Ch = h >> 1;
	Cbbase = Ybase + Yw * h;
	Crbase = Cbbase + Cw * Ch;
 
	*p++ = riscsync | riscsync_resync | riscsync_vre;
	*p++ = 0;

	*p++ = riscsync | riscsync_fm3;
	*p++ = 0;

	for (i = 0; i != h / 2; i++) {
		*p++ = riscwrite123 | Yw | riscwrite_sol | riscwrite_eol;
		*p++ = (Cw << 16) | Cw;	
		*p++ = (ulong)(Ybase + i * 2 * Yw);
		*p++ = (ulong)(Cbbase + i * Cw);		// Do not interlace
		*p++ = (ulong)(Crbase + i * Cw);
	}

	*p++ = riscsync | riscsync_resync | riscsync_vro;
	*p++ = 0;

	*p++ = riscsync | riscsync_fm3;
	*p++ = 0;

	for (i = 0; i != h / 2; i++) {
		*p++ = riscwrite1s23 | Yw | riscwrite_sol | riscwrite_eol;
		*p++ = (Cw << 16) | Cw;	
		*p++ = (ulong)(Ybase + (i * 2 + 1) * Yw);
	}

	// reset status.  you really need two instructions ;-(.
	*p++ = riscjmp | (0xf << risclabelshift_reset); 	
	*p++ = PADDR(p);
	*p++ = riscjmp | riscirq | (fnum << risclabelshift_set);
	*lastjmp = p;

	return pbase;
}

static ulong *
riscplanar422(ulong paddr, int fnum, int w, int h, ulong **lastjmp)
{
	ulong *p, *pbase, Cw, Yw;
	uchar *Ybase, *Cbbase, *Crbase;
	int i, bpp;

	bpp = 2;
	assert(w * bpp <= 0x7FF);
	pbase = p = (ulong *)malloc((h + 6) * 5 * sizeof(ulong));
	assert(p);

	Yw = w;
	Ybase = (uchar *)paddr;
	Cw = w >> 1;
	Cbbase = Ybase + Yw * h;
	Crbase = Cbbase + Cw * h;

	*p++ = riscsync | riscsync_resync | riscsync_vre;
	*p++ = 0;

	*p++ = riscsync | riscsync_fm3;
	*p++ = 0;

	for (i = 0; i != h / 2; i++) {
		*p++ = riscwrite123 | Yw | riscwrite_sol | riscwrite_eol;
		*p++ = (Cw << 16) | Cw;	
		*p++ = (ulong)(Ybase + i * 2 * Yw);
		*p++ = (ulong)(Cbbase + i * 2 * Cw);
		*p++ = (ulong)(Crbase + i * 2 * Cw);
	}

	*p++ = riscsync | riscsync_resync | riscsync_vro;
	*p++ = 0;

	*p++ = riscsync | riscsync_fm3;
	*p++ = 0;

	for (i = 0; i != h / 2; i++) {
		*p++ = riscwrite123 | Yw | riscwrite_sol | riscwrite_eol;
		*p++ = (Cw << 16) | Cw;	
		*p++ = (ulong)(Ybase + (i * 2 + 1) * Yw);
		*p++ = (ulong)(Cbbase + (i * 2 + 1) * Cw);
		*p++ = (ulong)(Crbase + (i * 2 + 1) * Cw);
	}

	// reset status.  you really need two instructions ;-(.
	*p++ = riscjmp | (0xf << risclabelshift_reset); 	
	*p++ = PADDR(p);
	*p++ = riscjmp | riscirq | (fnum << risclabelshift_set);
	*lastjmp = p;

	return pbase;
}

static ulong *
riscaudio(ulong paddr, int nblocks, int bsize)
{
	ulong *p, *pbase;
	int i;

	pbase = p = (ulong *)malloc((nblocks + 3) * 2 * sizeof(ulong));
	assert(p);

	*p++ = riscsync|riscsync_fm1;
	*p++ = 0;
	
	for (i = 0; i != nblocks; i++) {
		*p++ = riscwrite | riscwrite_sol | riscwrite_eol | bsize | riscirq |
				((i & 0xf) << risclabelshift_set) | 
				((~i & 0xf) << risclabelshift_reset);
		*p++ = paddr + i * bsize;
	}

	*p++ = riscsync | riscsync_vro;
	*p++ = 0;
	*p++ = riscjmp;
	*p++ = PADDR(pbase);
	USED(p);

	return pbase;
}


static void
vactivate(Tv *tv, Frame *frames, int nframes)
{


@@ 812,13 1406,13 @@ static void
vstart(Tv *tv, int nframes, int w, int h, int stride)
{
	Frame *frames;
	int bpp, i, bpf;
	int bitspp, i, bpf;

	if (nframes >= 0x10)
		error(Ebadarg);

	bpp = getbpp(tv);
	bpf = w * h * bpp;
	bitspp = getbitspp(tv);
	bpf = w * h * bitspp / 8;

	// Add one as a spare.
	frames = (Frame *)malloc(nframes * sizeof(Frame));


@@ 836,9 1430,23 @@ vstart(Tv *tv, int nframes, int w, int h, int stride)
		if ((frames[i].fbase = (uchar *)malloc(bpf)) == nil)
			error(Enomem);
		
		frames[i].fstart = riscframe(PADDR(frames[i].fbase), i, 
								w * bpp, h, stride * bpp, 
								&frames[i].fjmp);
		switch (tv->cfmt) {
		case colorfmt_YCbCr422:
			frames[i].fstart = riscplanar422(PADDR(frames[i].fbase), i, 
									 w, h, &frames[i].fjmp);
			break;
		case colorfmt_YCbCr411:
			frames[i].fstart = riscplanar411(PADDR(frames[i].fbase), i, 
									 w, h, &frames[i].fjmp);
			break;
		case colorfmt_rgb16:
			frames[i].fstart = riscpacked(PADDR(frames[i].fbase), i, 
								     w * bitspp / 8, h, stride * bitspp / 8, 
								     &frames[i].fjmp);
			break;
		default:
			panic("vstart: Unsupport colorformat\n");
		}
	}

	for (i = 0; i != nframes; i++)


@@ 849,6 1457,100 @@ vstart(Tv *tv, int nframes, int w, int h, int stride)
}

static void
astart(Tv *tv, char *input, uint rate, uint nab, uint nasz)
{
	Bt848 *bt878 = tv->bt878;
	ulong *arisc;
	int selector;
	uchar *abuf;
	int s, d;

	if (bt878 == nil || tv->amux == nil)
		error("#V: Card does not support audio");

	selector = 0;
	if (!strcmp(input, "tv"))
		selector = asel_tv;
	else 	if (!strcmp(input, "radio"))
		selector = asel_radio;
	else 	if (!strcmp(input, "mic"))
		selector = asel_mic;
	else 	if (!strcmp(input, "smxc"))
		selector = asel_smxc;
	else
		error("#V: Invalid input");

	if (nasz > 0xfff)
		error("#V: Audio block size too big (max 0xfff)");

	abuf = (uchar *)malloc(nab * nasz * sizeof(uchar));
	assert(abuf);
	arisc = riscaudio(PADDR(abuf), nab, nasz);

	ilock(tv);
	if (tv->arisc) {
		iunlock(tv);
		free(abuf);
		free(arisc);
		error(Einuse);
	}

	tv->arisc = arisc;	
	tv->abuf = abuf;
	tv->nablocks = nab;
	tv->absize = nasz;

	bt878->riscstrtadd = PADDR(tv->arisc);
	bt878->packetlen = (nab << 16) | nasz;
	bt878->intmask = 
				intstat_scerr | intstat_ocerr | intstat_risci | 
				intstat_pabort | intstat_riperr | intstat_pperr | 
				intstat_fdsr | intstat_ftrgt | intstat_fbus;

	/* Assume analog, 16bpp */
	for (s = 0; s < 16; s++)
		if (rate << s > Hwbase_ad * 4 / 15)
			break;
	for (d = 15; d >= 4; d--)
		if (rate << s < Hwbase_ad * 4 / d)
			break;

	print("astart: sampleshift %d, decimation %d\n", s, d);
	s = d = 0;

	tv->narblocks = 0;
	bt878->gpiodmactl = gpiodmactl_fifoenable | 
			gpiodmactl_riscenable | gpiodmactl_acapenable |
			gpiodmactl_daes2 | /* gpiodmactl_apwrdn | */
			gpiodmactl_daiomda | (d << 8) | (9 << 28) | (selector << 24);
	print("dmactl %.8ulX\n", bt878->gpiodmactl);
	iunlock(tv);
}

static void
astop(Tv *tv)
{
	Bt848 *bt878 = tv->bt878;

	ilock(tv);
	if (tv->aref.ref > 0) {
		iunlock(tv);
		error(Einuse);
	}

	if (tv->abuf) {
		bt878->gpiodmactl &= ~gpiodmactl_riscenable;
		bt878->gpiodmactl &= ~gpiodmactl_fifoenable;

		free(tv->abuf);
		tv->abuf = nil;
		free(tv->arisc);
		tv->arisc = nil;
	}
	iunlock(tv);
}

static void
vgastart(Tv *tv, ulong paddr, int stride)
{
	Frame *frame;


@@ 861,10 1563,9 @@ vgastart(Tv *tv, ulong paddr, int stride)
	}

	frame->fbase = nil;
	frame->fstart = 
			riscframe(paddr, 0, ntsc_hactive * getbpp(tv), ntsc_vactive, 
					stride * getbpp(tv), 
					&frame->fjmp);
	frame->fstart = riscpacked(paddr, 0, ntsc_hactive * getbitspp(tv) / 8, 
						   ntsc_vactive, stride * getbitspp(tv) / 8, 
						   &frame->fjmp);
	*frame->fjmp = PADDR(frame->fstart);

	vactivate(tv, frame, 1);


@@ 939,23 1640,558 @@ frequency(Tv *tv, int channel, int finetune)

	div = (freq + tuner->offs + finetune) & 0x7fff;
	
	if (!i2cwrite(tv, 0xc0, (div >> 8) & 0x7f, div, 1))
	if (!i2cwrite(tv, tv->i2ctuneraddr, (div >> 8) & 0x7f, div, 1))
		error(Eio);
	
	if (!i2cwrite(tv, 0xc0, tuner->cfg, cfg, 1))
	if (!i2cwrite(tv, tv->i2ctuneraddr, tuner->cfg, cfg, 1))
		error(Eio);

	tv->channel = channel;
	if (tv->msp)
		msptune(tv);
}

static struct {
	char *cmode;
	ulong realmode;
	ulong cbits;
} colormodes[] = {
{	"RGB16",		colorfmt_rgb16,		colorfmt_rgb16,		},
{	"YCbCr422",	colorfmt_YCbCr422,	colorfmt_YCbCr422,	},
{	"YCbCr411",	colorfmt_YCbCr411,	colorfmt_YCbCr422,	},
};

static void
colormode(Tv *tv, char *colormode)
{
	Bt848 *bt848 = tv->bt848;
	int i;

	for (i = 0; i != nelem(colormodes); i++)
		if (!strcmp(colormodes[i].cmode, colormode))
			break;

	if (i == nelem(colormodes))
		error(Ebadarg);

	tv->cfmt = colormodes[i].realmode;
	bt848->colorfmt = colormodes[i].cbits;
}

static int
getbpp(Tv *tv)
getbitspp(Tv *tv)
{
	switch (tv->bt848->colorfmt) {
	switch (tv->cfmt) {
	case colorfmt_rgb16:
		return 2;
	case colorfmt_YCbCr422:
		return 16;
	case colorfmt_YCbCr411:
		return 12;
	default:
		error("getbpp: Unsupport color format\n");
		error("getbitspp: Unsupport color format\n");
	}	
	return -1;
}

static char *
getcolormode(ulong cmode)
{
	switch (cmode) {
	case colorfmt_rgb16:
		return "RGB16";
	case colorfmt_YCbCr411:
		return "YCbCr411";
	case colorfmt_YCbCr422:
		return (cmode == colorfmt_YCbCr422)? "YCbCr422": "YCbCr411";
	default:
		error("getcolormode: Unsupport color format\n");
	}	
	return nil;
}

static void
i2c_set(Tv *tv, int scl, int sda)
{
	Bt848 *bt848 = tv->bt848;
	ulong d;

	bt848->i2c = (scl << 1) | sda;
	d = bt848->i2c;
	USED(d);
	microdelay(i2c_delay);
}

static uchar
i2c_getsda(Tv *tv)
{
	Bt848 *bt848 = tv->bt848;

	return bt848->i2c & i2c_sda;
}

static void
i2c_start(Tv *tv)
{
	i2c_set(tv, 0, 1);
	i2c_set(tv, 1, 1);
	i2c_set(tv, 1, 0);
	i2c_set(tv, 0, 0);
}

static void
i2c_stop(Tv *tv)
{
	i2c_set(tv, 0, 0);
	i2c_set(tv, 1, 0);
	i2c_set(tv, 1, 1);
}

static void
i2c_bit(Tv *tv, int sda)
{
	i2c_set(tv, 0, sda);
	i2c_set(tv, 1, sda);
	i2c_set(tv, 0, sda);
}

static int
i2c_getack(Tv *tv)
{
	int ack;

	i2c_set(tv, 0, 1);
	i2c_set(tv, 1, 1);
	ack = i2c_getsda(tv);
	i2c_set(tv, 0, 1);
	return ack;
}

static int
i2c_wr8(Tv *tv, uchar d, int wait)
{
	int i, ack;

	i2c_set(tv, 0, 0);
	for (i = 0; i != 8; i++) {
		i2c_bit(tv, (d & 0x80)? 1: 0);
		d <<= 1;
	}
	if (wait)
		microdelay(wait);

	ack = i2c_getack(tv);
	return ack == 0;
}

static uchar
i2c_rd8(Tv *tv, int lastbyte)
{
	int i;
	uchar d;

	d = 0;
	i2c_set(tv, 0, 1);
	for (i = 0; i != 8; i++) {
		i2c_set(tv, 1, 1);
		d <<= 1;
		if (i2c_getsda(tv))
			d |= 1;
		i2c_set(tv, 0, 1);
	}
	
	i2c_bit(tv, lastbyte? 1: 0);
	return d;
}

static int
mspsend(Tv *tv, uchar *cmd, int ncmd)
{
	int i, j, delay;

	for (i = 0; i != 3; i++) {
		delay = 2000;

		i2c_start(tv);
		for (j = 0; j != ncmd; j++) {
			if (!i2c_wr8(tv, cmd[j], delay))
				break;
			delay = 0;
		}
		i2c_stop(tv);

		if (j == ncmd)
			return 1;

		microdelay(10000);
		print("mspsend: retrying\n");
	}

	return 0;
}

static int
mspwrite(Tv *tv, uchar sub, ushort reg, ushort v)
{
	uchar b[6];

	b[0] = i2c_msp3400;
	b[1] = sub;
	b[2] = reg >> 8;
	b[3] = reg;
	b[4] = v >> 8;
	b[5] = v;
	return mspsend(tv, b, sizeof b);
}

static int
mspread(Tv *tv, uchar sub, ushort reg, ushort *data)
{
	uchar b[4];
	int i;

	b[0] = i2c_msp3400;
	b[1] = sub;
	b[2] = reg >> 8;
	b[3] = reg;

	for (i = 0; i != 3; i++) {
		i2c_start(tv);
		if (!i2c_wr8(tv, b[0], 2000) ||
			!i2c_wr8(tv, b[1] | 1, 0) || 
			!i2c_wr8(tv, b[2], 0) || 
			!i2c_wr8(tv, b[3], 0)) {

			i2c_stop(tv);
			microdelay(10000);
			print("retrying\n");
			continue;
		}

		i2c_start(tv);

		if (!i2c_wr8(tv, b[0] | 1, 2000)) {

			i2c_stop(tv);
			continue;
		}
		
		*data = i2c_rd8(tv, 0) << 8;
		*data |= i2c_rd8(tv, 1);
		i2c_stop(tv);
		return 1;
	}
	return 0;
}

static uchar mspt_reset[] = { i2c_msp3400, 0, 0x80, 0 };
static uchar mspt_on[] = { i2c_msp3400, 0, 0, 0 };

static int
mspreset(Tv *tv)
{
	ushort v, p;
	Bt848 *bt848 = tv->bt848;
	ulong b;

	b = 1 << 5;
	gpioenable(tv, ~b, b);
	gpiowrite(tv, ~b, 0);
	microdelay(2500);
	gpiowrite(tv, ~b, b);

	bt848->i2c = 0x80;

	microdelay(2000);
	mspsend(tv, mspt_reset, sizeof mspt_reset);

	microdelay(2000);
	if (!mspsend(tv, mspt_on, sizeof mspt_on)) {
		print("#V: Cannot find MSP34x5G on the I2C bus (on)\n");
		return 0;
	}
	microdelay(2000);

	if (!mspread(tv, msp_bbp, 0x001e, &v)) {
		print("#V: Cannot read MSP34xG5 chip version\n");
		return 0;
	}

	if (!mspread(tv, msp_bbp, 0x001f, &p)) {
		print("#V: Cannot read MSP34xG5 product code\n");
		return 0;
	}

	print("#V: MSP34%dg ROM %.d, %d.%d\n", 
		(uchar)(p >> 8), (uchar)p, (uchar)(v >> 8), (uchar)v);

	tv->msp = 1;
	return 1;
}

static void
mspvolume(Tv *tv, int mute, int l, int r)
{
	short v, d;
	ushort b;

	if (mute) {
		v = 0;
		b = 0;
	}
	else {
		tv->aleft = l;
		tv->aright = r;
		d = v = max(l, r);
		if (d == 0)
			d++;
		b = ((r - l) * 0x7f) / d;
	}

	mspwrite(tv, msp_bbp, 0, v << 8);
	mspwrite(tv, msp_bbp, 7, v? 0x4000: 0);
	mspwrite(tv, msp_bbp, 1, b << 8);	
}

static char *
mspaformat(int f)
{
	switch (f) {
	case 0:
		return "unknown";
	case 2:
	case 0x20:
	case 0x30:
		return "M-BTSC";
	case 3:
		return "B/G-FM";
	case 4:
	case 9:
	case 0xB:
		return "L-AM/NICAM D/Kn";
	case 8:
		return "B/G-NICAM";
	case 0xA:
		return "I";
	case 0x40:
		return "FM-Radio";
	}
	return "unknown format";
}
	

static void
msptune(Tv *tv)
{
	ushort d, s, nicam;
	int i;

	mspvolume(tv, 1, 0, 0);
	if (!mspwrite(tv, msp_dem, 0x0030, 0x2033))
		error("#V: Cannot set MODUS register");

	if (!mspwrite(tv, msp_bbp, 0x0008, 0x0320))
		error("#V: Cannot set loadspeaker input");

	if (!mspwrite(tv, msp_dem, 0x0040, 0x0001))
		error("#V: Cannot set I2S clock freq");
	if (!mspwrite(tv, msp_bbp, 0x000d, 0x1900))
		error("#V: Cannot set SCART prescale");
	if (!mspwrite(tv, msp_bbp, 0x000e, 0x2403))
		error("#V: Cannot set FM/AM prescale");
	if (!mspwrite(tv, msp_bbp, 0x0010, 0x5a00))
		error("#V: Cannot set NICAM prescale");
	if (!mspwrite(tv, msp_dem, 0x0020, 0x0001))
		error("#V: Cannot start auto detect");

	for (d = (ushort)-1, i = 0; i != 10; i++) {
		if (!mspread(tv, msp_dem, 0x007e, &d))
			error("#V: Cannot get autodetect info MSP34xG5");

		if (d == 0 || d < 0x800)
			break;
		delay(50);
	}

	if (!mspread(tv, msp_dem, 0x0200, &s))
		error("#V: Cannot get status info MSP34xG5");

	mspvolume(tv, 0, tv->aleft, tv->aright);

	nicam = ((s >> 4) & 2) | ((s >> 9) & 1);
	snprint(tv->ainfo, sizeof tv->ainfo, "%s %s %s",
			mspaformat(d), (s & (1 << 6))? "stereo": "mono",
			nicamstate[nicam]);
}

static void
i2cscan(Tv *tv)
{
	int i, ack;

	for (i = 0; i < 0x100; i += 2) {
		i2c_start(tv);
		ack = i2c_wr8(tv, i, 0);
		i2c_stop(tv);
		if (ack) {
			print("i2c device @%.2uX\n", i);
		}
	}

	for (i = 0xf0; i != 0xff; i++) {
		i2c_start(tv);
		ack = i2c_wr8(tv, i, 0);
		i2c_stop(tv);
		if (ack)
			print("i2c device may be at @%.2uX\n", i);
	}
}

static void
gpioenable(Tv *tv, ulong mask, ulong data)
{
	Bt848 *bt848 = tv->bt848;

	bt848->gpioouten = (bt848->gpioouten & mask) | data;
}

static void
gpiowrite(Tv *tv, ulong mask, ulong data)
{
	Bt848 *bt848 = tv->bt848;

	bt848->gpiodata[0] = (bt848->gpiodata[0] & mask) | data;
}

static void
alteraoutput(Tv *tv)
{
	if (tv->gpiostate == Gpiooutput)
		return;

	gpioenable(tv, ~0xffffff, 0x56ffff);
	microdelay(10);
	tv->gpiostate = Gpiooutput;
}

static void
alterainput(Tv *tv)
{
	if (tv->gpiostate == Gpioinput)
		return;

	gpioenable(tv, ~0xffffff, 0x570000);
	microdelay(10);
	tv->gpiostate = Gpioinput;
}

static void
alterareg(Tv *tv, ulong reg)
{
	if (tv->alterareg == reg)
		return;

	gpiowrite(tv, ~0x56ffff, (reg & 0x54ffff) | tv->alteraclock);
	microdelay(10);
	tv->alterareg = reg;
}

static void
alterawrite(Tv *tv, ulong reg, ushort data)
{
	alteraoutput(tv);
	alterareg(tv, reg);

	tv->alteraclock ^= 0x20000;
	gpiowrite(tv, ~0x56ffff, (reg & 0x540000) | data | tv->alteraclock);
	microdelay(10);
}

static void
alteraread(Tv *tv, int reg, ushort *data)
{
	Bt848 *bt848 = tv->bt848;

	if (tv->alterareg != reg) {
		alteraoutput(tv);
		alterareg(tv, reg);
	}
	else {
		gpioenable(tv, ~0xffffff, 0x560000);
		microdelay(10);
	}

	alterainput(tv);
	gpiowrite(tv, ~0x570000, (reg & 0x560000) | tv->alteraclock);
	microdelay(10);
	*data = (ushort)bt848->gpiodata[0];
	microdelay(10);
}

static void
kfirloadu(Tv *tv, uchar *u, int ulen)
{
	Bt848 *bt848 = tv->bt848;
	int i, j;

	ilock(&tv->kfirlock);
	bt848->gpioouten &= 0xff000000;
	bt848->gpioouten |= gpio_altera_data |
		gpio_altera_clock | gpio_altera_nconfig;
	bt848->gpiodata[0] &= 0xff000000;
	microdelay(10);
	bt848->gpiodata[0] |= gpio_altera_nconfig;
	microdelay(10);

	// Download the microcode
	for (i = 0; i != ulen; i++)
		for (j = 0; j != 8; j++) {
			bt848->gpiodata[0] &= ~(gpio_altera_clock|gpio_altera_data);
			if (u[i] & 1)
				bt848->gpiodata[0] |= gpio_altera_data;
			bt848->gpiodata[0] |= gpio_altera_clock;
			u[i] >>= 1;
		}
	bt848->gpiodata[0] &= ~gpio_altera_clock;
	microdelay(100);

	// Initialize.
	for (i = 0; i != 30; i++) {
		bt848->gpiodata[0] &= ~gpio_altera_clock;
		bt848->gpiodata[0] |= gpio_altera_clock;
	}
	bt848->gpiodata[0] &= ~(gpio_altera_clock|gpio_altera_data);
	iunlock(&tv->kfirlock);

	tv->gpiostate = Gpioinit;
}

static void
kfirreset(Tv *tv)
{
	alterawrite(tv, 0, 0);
	microdelay(10);
	alterawrite(tv, 0x40000, 0);
	microdelay(10);
	alterawrite(tv, 0x40006, 0x80);
	microdelay(10);
	alterawrite(tv, 8, 1);
	microdelay(10);
	alterawrite(tv, 0x40004, 2);
	microdelay(10);
	alterawrite(tv, 4, 3);
	microdelay(3);
}

static int
kfirinitialize(Tv *tv)
{
	// Initialize parameters?

	tv->gpiostate = Gpioinit;
	tv->alterareg = -1;
	tv->alteraclock = 0x20000;
	kfirloadu(tv, hcwAMC, sizeof hcwAMC);
	kfirreset(tv);
	return 1;
}


M pc/etherif.h => pc/etherif.h +1 -0
@@ 20,6 20,7 @@ struct Ether {
	void	(*interrupt)(Ureg*, void*);
	long	(*ifstat)(Ether*, void*, long, ulong);
	long 	(*ctl)(Ether*, void*, long); /* custom ctl messages */
	void	(*power)(Ether*, int);	/* power on/off */
	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
	void	*ctlr;


M pc/sdata.c => pc/sdata.c +3 -3
@@ 17,7 17,7 @@ enum {
	DbgSTATE	= 0x04,		/* dump state on panic */
	DbgPROBE	= 0x08,		/* trace device probing */
	DbgDEBUG	= 0x80,		/* the current problem... */
	DbgINL		= 0x100,		/* That Inil20+ message we hate */
	DbgINL		= 0x100,	/* That Inil20+ message we hate */
};
#define DEBUG		(DbgDEBUG|DbgSTATE)



@@ 200,13 200,13 @@ typedef struct Ctlr {

	Pcidev*	pcidev;
	void	(*ienable)(Ctlr*);
	void (*idisable)(Ctlr*);
	void	(*idisable)(Ctlr*);
	SDev*	sdev;

	Drive*	drive[2];

	Prd*	prdt;			/* physical region descriptor table */
	void* prdtbase;
	void*	prdtbase;

	QLock;				/* current command */
	Drive*	curdrive;