~kris/9p

9hist

2e31dce2831e9088118a1e4363cc100bbe93d816 — David du Colombier 24 years ago e53c69c
Plan 9 from Bell Labs 2002-01-30
1 files changed, 197 insertions(+), 90 deletions(-)

M pc/devtv.c
M pc/devtv.c => pc/devtv.c +197 -90
@@ 1,6 1,5 @@
/*
  * Driver for Bt848 TV tuner.  WARNING, I used the Linux driver as
  * documentation.
  * Driver for Bt848 TV tuner.  
  *
  */
#include	"u.h"


@@ 16,7 15,6 @@ enum {
	Qdata,
	Qctl,
	Qregs,
	Qdbuf,

	Brooktree_vid = 0x109e,
	Brooktree_848_did = 0x0350,


@@ 26,6 24,8 @@ enum {
	K = 1024,
	M = K * K,

	Numring = 16,

	ntsc_rawpixels = 910,
	ntsc_sqpixels = 780,					// Including blanking & inactive
	ntsc_hactive = 640,


@@ 75,7 75,8 @@ enum {
	capctl_captureeven = 1 << 0,
	vbipacksize = 0x190,				// 0E0

	intstat_i2crack = 1 << 25,				// 100
	intstat_riscstatshift = 28,				// 100
	intstat_i2crack = 1 << 25,
	intstat_scerr = 1 << 19,
	intstat_ocerr = 1 << 18,
	intstat_fbus = 1 << 12,


@@ 112,6 113,8 @@ enum {
		riscsync_vre = fifo_vre << 0,
		riscsync_vro = fifo_vro << 0,
		riscsync_fm1 = fifo_fm1 << 0,
	risclabelshift_set = 16,
	risclabelshift_reset = 20,
};

typedef struct {


@@ 203,20 206,27 @@ typedef struct {
} Tuner;

typedef struct {
	ulong	*fstart;
	ulong	*fjmp;
	uchar	*fbase;
} Frame;

typedef struct {
	Lock;
	Bt848	*bt848;
	Variant	*variant;
	Tuner	*tuner;
	Pcidev	*pci;
	ulong	*program;	// Current DMA program
	uchar	i2ctuneraddr;
	uchar	i2ccmd;		// I2C command
	int		board;		// What board is this?
	uchar	i2ccmd;			// I2C command
	int		board;			// What board is this?

	uchar	*dbuf;
	Ref		fref;				// Copying images?
	int		nframes;			// Number of frames to capture.
	Frame	*frames;			// DMA program
	int		lframe;			// Last frame DMAed
} Tv;

// Tuner related.
enum {
	TemicPAL = 0,
	PhilipsPAL,


@@ 260,12 270,14 @@ static Tuner tuners[] = {

enum {
	CMvstart,
	CMvgastart,
	CMvstop,
	CMchannel,
};

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


@@ 275,7 287,6 @@ static Dirtab tvtab[]={
	"tv",		{ Qdata, 0 },		0,	0600,
	"tvctl",	{ Qctl, 0 },			0,	0600,
	"tvregs",	{ Qregs, 0 },		0,	0400,
	"tvdbuf",	{ Qdbuf, 0 },		0,	0400,
};

static Variant variant[] = {


@@ 292,9 303,11 @@ static Tv *tv;
static int i2cread(Tv *, uchar, uchar *);
static int i2cwrite(Tv *, uchar, uchar, uchar, int);
static void tvinterrupt(Ureg *, Tv *);
static void vstart(Tv *, ulong, int);
static void vgastart(Tv *, ulong, int);
static void vstart(Tv *, int, int, int, int);
static void vstop(Tv *);
static void frequency(Tv *, int, int);
static int getbpp(Tv *);

static void
tvinit(void)


@@ 302,9 315,6 @@ tvinit(void)
	Pcidev *pci;
	ulong intmask;

	if (!getconf("tv0"))
		return;

	// Test for a triton memory controller.
	intmask = 0;
	if (pcimatch(nil, Intel_vid, Intel_82437_did))


@@ 435,9 445,6 @@ tvinit(void)

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

		tv->dbuf = (uchar *)malloc(ntsc_hactive * ntsc_vactive * sizeof(ushort));
		memset(tv->dbuf, 0, ntsc_hactive * ntsc_vactive * sizeof(ushort));
	}
}



@@ 462,7 469,15 @@ tvstat(Chan *c, uchar *db, int n)
static Chan*
tvopen(Chan *c, int omode)
{
	return devopen(c, omode, tvtab, nelem(tvtab), devgen);
	switch ((int)c->qid.path) {
	case Qdir:
		return devopen(c, omode, tvtab, nelem(tvtab), devgen);
	}

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

static void


@@ 475,15 490,49 @@ tvread(Chan *c, void *a, long n, vlong offset)
	static char regs[10 * K];
	static int regslen;
	char *e, *p;
	int nbytes;

	USED(offset);

	switch((int)c->qid.path) {
	case Qdir:
		return devdirread(c, a, n, tvtab, nelem(tvtab), devgen);
	case Qdata:
		error(Eio);

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

		bpf = ntsc_hactive * ntsc_vactive * getbpp(tv);

		if (offset >= bpf) 
			return 0;

		nb = n;
		if (offset + nb > bpf)
			nb = bpf - offset;

		ilock(tv);
		if (tv->frames == nil || tv->lframe >= tv->nframes) {
			iunlock(tv);
			return 0;
		}

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

		memmove(a, src + offset, nb);
		decref(&tv->fref);
		return nb;
	}

	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);
	}
		
	case Qregs:
		if (offset == 0) {
			Bt848 *bt848 = tv->bt848;


@@ 503,20 552,6 @@ tvread(Chan *c, void *a, long n, vlong offset)

		return readstr(offset, a, n, &regs[offset]);

	case Qdbuf:
		if (offset > ntsc_hactive * ntsc_vactive * sizeof(ushort)) {
			memset(tv->dbuf, 0xaa, 
					ntsc_hactive * ntsc_vactive * sizeof(ushort));
			return 0;
		}

		if (offset + n > ntsc_hactive * ntsc_vactive * sizeof(ushort))
			nbytes = ntsc_hactive * ntsc_vactive * sizeof(ushort) - offset;
		else
			nbytes = n;
		memmove(a, tv->dbuf + offset, nbytes);
		return nbytes;

	default:
		n=0;
		break;


@@ 540,12 575,19 @@ tvwrite(Chan *c, void *a, long n, vlong)
		ct = lookupcmd(cb, tvctlmsg, nelem(tvctlmsg));
		switch (ct->index) {
		case CMvstart:
			vstart(tv, strtoul(cb->f[1], (char **)nil, 0),
			vstart(tv, (int)strtol(cb->f[1], (char **)nil, 0), 
					ntsc_hactive, ntsc_vactive, ntsc_hactive);
			break;

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

		case CMvstop:
			vstop(tv);
			break;

		case CMchannel:
			frequency(tv, (int)strtol(cb->f[1], (char **)nil, 0), 
				(int)strtol(cb->f[2], (char **)nil, 0));


@@ 585,9 627,18 @@ static void
tvinterrupt(Ureg *, Tv *tv)
{
	Bt848 *bt848 = tv->bt848;
	ulong status;

	while ((status = bt848->intstat & bt848->intmask) != 0) {
	while (1) {
		ulong status;
		uchar fnum;

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

		if (status == 0)
			break;

		bt848->intstat = status;

		if ((status & intstat_fmtchg) == intstat_fmtchg) {


@@ 613,7 664,7 @@ tvinterrupt(Ureg *, Tv *tv)
		}

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


@@ 692,17 743,16 @@ i2cwrite(Tv *tv, uchar off, uchar d1, uchar d2, int both)
	return 1;
}


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

	pbase = p = (ulong *)malloc((ntsc_vactive + 5) * 2 * sizeof(ulong));
	pbase = p = (ulong *)malloc((h + 6) * 2 * sizeof(ulong));
	assert(p);

	assert(ntsc_hactive * bpp <= 0x7FF);
	assert(w <= 0x7FF);

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


@@ 710,9 760,9 @@ riscprogram(ulong paddr, int stride, int bpp)
	*p++ = riscsync | riscsync_fm1;
	*p++ = 0;

	for (i = 0; i != ntsc_vactive / 2; i++) {
		*p++ = riscwrite | ntsc_hactive * bpp | riscwrite_sol | riscwrite_eol;
		*p++ = paddr + i * 2 * stride * bpp;
	for (i = 0; i != h / 2; i++) {
		*p++ = riscwrite | w | riscwrite_sol | riscwrite_eol;
		*p++ = paddr + i * 2 * stride;
	}

	*p++ = riscsync | riscsync_resync | riscsync_vro;


@@ 721,85 771,129 @@ riscprogram(ulong paddr, int stride, int bpp)
	*p++ = riscsync | riscsync_fm1;
	*p++ = 0;

	for (i = 0; i != ntsc_vactive / 2; i++) {
		*p++ = riscwrite | ntsc_hactive * bpp | riscwrite_sol | riscwrite_eol;
		*p++ = paddr + (i * 2 + 1) * stride * bpp;
	for (i = 0; i != h / 2; i++) {
		*p++ = riscwrite | w | riscwrite_sol | riscwrite_eol;
		*p++ = paddr + (i * 2 + 1) * stride;
	}

	*p++ = riscjmp;
	*p++ = PADDR(pbase);
	USED(p);
	// 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 void
vstart(Tv *tv, ulong paddr, int stride)
vactivate(Tv *tv, Frame *frames, int nframes)
{
	Bt848 *bt848 = tv->bt848;
	ulong *program, cfmt;
	int bpp;

	cfmt = bt848->colorfmt;
	bpp = -1;
	switch (cfmt) {
	case colorfmt_rgb16:
		bpp = 2;
		break;
	default:
		panic("render: Unsupport color format\n");
	}

	program = riscprogram(paddr, stride, bpp);
//	program = riscprogram(PADDR(tv->dbuf), ntsc_hactive * bpp, bpp);

	ilock(tv);
	if (waserror()) {
	if (tv->frames) {
		iunlock(tv);
		free(program);
		nexterror();
	}

	if (tv->program)
		error(Einuse);
	}
	poperror();

	if (cfmt != bt848->colorfmt)
		error(Eio);
	tv->frames = frames;
	tv->nframes = nframes;

	tv->program = program;
	bt848->riscstrtadd = PADDR(tv->program);
	bt848->riscstrtadd = PADDR(tv->frames[0].fstart);
	bt848->capctl |= capctl_captureodd|capctl_captureeven;
	bt848->gpiodmactl |= gpiodmactl_fifoenable;
	bt848->gpiodmactl |= gpiodmactl_riscenable;
	
	poperror();
	iunlock(tv);
}

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

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

	bpp = getbpp(tv);
	bpf = w * h * bpp;

	// Add one as a spare.
	frames = (Frame *)malloc(nframes * sizeof(Frame));
	assert(frames);
	if (waserror()) {
		for (i = 0; i != nframes; i++)
			if (frames[i].fbase)
				free(frames[i].fbase);
		free(frames);
		nexterror();
	}
	memset(frames, 0, nframes * sizeof(Frame));

	for (i = 0; i != nframes; i++) {
		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);
	}

	for (i = 0; i != nframes; i++)
		*frames[i].fjmp = 
			PADDR((i == nframes - 1)? frames[0].fstart: frames[i + 1].fstart);

	vactivate(tv, frames, nframes);
}

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

	frame = (Frame *)malloc(sizeof(Frame));
	assert(frame);
	if (waserror()) {
		free(frame);
		nexterror();
	}

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

	vactivate(tv, frame, 1);
}

static void
vstop(Tv *tv)
{
	Bt848 *bt848 = tv->bt848;

	ilock(tv);
	if (waserror()) {

		if (tv->program)
			free(tv->program);
	if (tv->fref.ref > 0) {
		iunlock(tv);
		nexterror();
		error(Einuse);
	}

	if (tv->program) {
	if (tv->frames) {
		int i;

		bt848->gpiodmactl &= ~gpiodmactl_riscenable;
		bt848->gpiodmactl &= ~gpiodmactl_fifoenable;
		bt848->capctl &= ~(capctl_captureodd|capctl_captureeven);

		free(tv->program);
		tv->program = nil;
		for (i = 0; i != tv->nframes; i++)
			if (tv->frames[i].fbase)
				free(tv->frames[i].fbase);
		free(tv->frames);
		tv->frames = nil;
	}
	
	poperror();
	iunlock(tv);
}



@@ 852,3 946,16 @@ frequency(Tv *tv, int channel, int finetune)
		error(Eio);
}

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