## diffname pc/devtv.c 2002/0125
## diff -e /dev/null /n/emeliedump/2002/0125/sys/src/9/pc/devtv.c
0a
/*
* Driver for Bt848 TV tuner. WARNING, I used the Linux driver as
* documentation.
*
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
enum {
Qdir = 0,
Qdata,
Qctl,
Qregs,
Qdbuf,
Brooktree_vid = 0x109e,
Brooktree_848_did = 0x0350,
Intel_vid = 0x8086,
Intel_82437_did = 0x122d,
K = 1024,
M = K * K,
ntsc_rawpixels = 910,
ntsc_sqpixels = 780, // Including blanking & inactive
ntsc_hactive = 640,
ntsc_vactive = 480,
ntsc_clkx1delay = 135, // Clock ticks.
ntsc_clkx1hactive = 754,
ntsc_vdelay = 26, // # of scan lines.
ntsc_vscale = 0,
i2c_timing = 7 << 4,
i2c_bt848w3b = 1 << 2,
i2c_bt848scl = 1 << 1,
i2c_bt848sda = 1 << 0,
i2c_miroproee = 0x80, // MIRO PRO EEPROM
i2c_tea6300 = i2c_miroproee,
i2c_tda8425 = 0x82,
i2c_tda9840 = 0x84,
i2c_tda9850 = 0xb6,
i2c_haupee = 0xa0, // Hauppage EEPROM
i2c_stbee = 0xae, // STB EEPROM
Bt848_miropro = 0,
Bt848_miro,
// Bit fields.
iform_muxsel1 = 3 << 5, // 004
iform_muxsel0 = 2 << 5,
iform_xtselmask = 3 << 3,
iform_xtauto = 3 << 3,
iform_formatmask = 7 << 0,
iform_ntsc = 1 << 0,
control_ldec = 1 << 5, // 02C
contrast_100percent = 0xd8, // 030
vscale_interlaced = 1 << 5, // 04C
adelay_ntsc = 104, // 060
bdelay_ntsc = 93, // 064
adc_crush = 1 << 0, // 068
colorfmt_rgb16 = (2 << 4) | (2 << 0), // 0D4
colorctl_gamma = 1 << 4, // 0D8
capctl_fullframe = 1 << 4, // 0DC
capctl_captureodd = 1 << 1,
capctl_captureeven = 1 << 0,
vbipacksize = 0x190, // 0E0
intstat_i2crack = 1 << 25, // 100
intstat_scerr = 1 << 19,
intstat_ocerr = 1 << 18,
intstat_fbus = 1 << 12,
intstat_risci = 1 << 11,
intstat_i2cdone = 1 << 8,
intstat_vpress = 1 << 5,
intstat_hlock = 1 << 4,
intstat_vsync = 1 << 1,
intstat_fmtchg = 1 << 0,
intmask_etbf = 1 << 23, // 104
gpiodmactl_pltp23_16 = 2 << 6, // 10C
gpiodmactl_pltp23_0 = 0 << 6,
gpiodmactl_pltp1_16 = 2 << 4,
gpiodmactl_pltp1_0 = 0 << 4,
gpiodmactl_pktp_32 = 3 << 2,
gpiodmactl_pktp_0 = 0 << 2,
gpiodmactl_riscenable = 1 << 1,
gpiodmactl_fifoenable = 1 << 0,
// RISC instructions and parameters.
fifo_vre = 0x4,
fifo_vro = 0xC,
fifo_fm1 = 0x6,
riscirq = 1 << 24,
riscwrite = 0x1 << 28,
riscwrite_sol = 1 << 27,
riscwrite_eol = 1 << 26,
riscskip = 0x2 << 28,
riscjmp = 0x7 << 28,
riscsync = 0x8 << 28,
riscsync_resync = 1 << 15,
riscsync_vre = fifo_vre << 0,
riscsync_vro = fifo_vro << 0,
riscsync_fm1 = fifo_fm1 << 0,
};
typedef struct {
ushort vid;
ushort did;
char *name;
} Variant;
typedef struct {
ulong devstat; // 000
ulong iform; // 004
ulong tdec; // 008
ulong ecrop; // 00C
ulong evdelaylo; // 010
ulong evactivelo; // 014
ulong ehdelaylo; // 018
ulong ehactivelo; // 01C
ulong ehscalehi; // 020
ulong ehscalelo; // 024
ulong bright; // 028
ulong econtrol; // 02C
ulong contrastlo; // 030
ulong satulo; // 034
ulong satvlo; // 038
ulong hue; // 03C
ulong escloop; // 040
ulong pad0; // 044
ulong oform; // 048
ulong evscalehi; // 04C
ulong evscalelo; // 050
ulong test; // 054
ulong pad1[2]; // 058-05C
ulong adelay; // 060
ulong bdelay; // 064
ulong adc; // 068
ulong evtc; // 06C
ulong pad2[3]; // 070-078
ulong sreset; // 07C
ulong tglb; // 080
ulong tgctrl; // 084
ulong pad3; // 088
ulong ocrop; // 08C
ulong ovdelaylo; // 090
ulong ovactivelo; // 094
ulong ohdelaylo; // 098
ulong ohactivelo; // 09C
ulong ohscalehi; // 0A0
ulong ohscalelo; // 0A4
ulong pad4; // 0A8
ulong ocontrol; // 0AC
ulong pad5[4]; // 0B0-0BC
ulong oscloop; // 0C0
ulong pad6[2]; // 0C4-0C8
ulong ovscalehi; // 0CC
ulong ovscalelo; // 0D0
ulong colorfmt; // 0D4
ulong colorctl; // 0D8
ulong capctl; // 0DC
ulong vbipacksize; // 0E0
ulong vbipackdel; // 0E4
ulong fcap; // 0E8
ulong ovtc; // 0EC
ulong pllflo; // 0F0
ulong pllfhi; // 0F4
ulong pllxci; // 0F8
ulong dvsif; // 0FC
ulong intstat; // 100
ulong intmask; // 104
ulong pad7; // 108
ulong gpiodmactl; // 10C
ulong i2c; // 110
ulong riscstrtadd; // 114
ulong gpioouten; // 118
ulong gpioreginp; // 11C
ulong risccount; // 120
ulong pad8[55]; // 124-1FC
ulong gpiodata[64]; // 200-2FC
} Bt848;
typedef struct {
char *name;
ushort freq_vhfh; // Start frequency
ushort freq_uhf;
uchar VHF_L;
uchar VHF_H;
uchar UHF;
uchar cfg;
ushort offs;
} Tuner;
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 *dbuf;
} Tv;
// Tuner related.
enum {
TemicPAL = 0,
PhilipsPAL,
PhilipsNTSC,
PhilipsSECAM,
Notuner,
PhilipsPALI,
TemicNTSC,
TemicPALI,
Temic4036,
AlpsTSBH1,
AlpsTSBE1,
Freqmultiplier = 16,
};
static Tuner tuners[] = {
{"Temic PAL", Freqmultiplier * 140.25, Freqmultiplier * 463.25,
0x02, 0x04, 0x01, 0x8e, 623 },
{"Philips PAL_I", Freqmultiplier * 140.25, Freqmultiplier * 463.25,
0xa0, 0x90, 0x30, 0x8e, 623 },
{"Philips NTSC", Freqmultiplier * 157.25, Freqmultiplier * 451.25,
0xA0, 0x90, 0x30, 0x8e, 732 },
{"Philips SECAM", Freqmultiplier * 168.25, Freqmultiplier * 447.25,
0xA7, 0x97, 0x37, 0x8e, 623 },
{"NoTuner", 0, 0,
0x00, 0x00, 0x00, 0x00, 0 },
{"Philips PAL", Freqmultiplier * 168.25, Freqmultiplier * 447.25,
0xA0, 0x90, 0x30, 0x8e, 623 },
{"Temic NTSC", Freqmultiplier * 157.25, Freqmultiplier * 463.25,
0x02, 0x04, 0x01, 0x8e, 732 },
{"TEMIC PAL_I", Freqmultiplier * 170.00, Freqmultiplier * 450.00,
0x02, 0x04, 0x01, 0x8e, 623 },
{"Temic 4036 FY5 NTSC", Freqmultiplier * 157.25, Freqmultiplier * 463.25,
0xa0, 0x90, 0x30, 0x8e, 732 },
{"Alps TSBH1", Freqmultiplier * 137.25, Freqmultiplier * 385.25,
0x01, 0x02, 0x08, 0x8e, 732 },
{"Alps TSBE1", Freqmultiplier * 137.25, Freqmultiplier * 385.25,
0x01, 0x02, 0x08, 0x8e, 732 },
};
enum {
CMvstart,
CMvstop,
CMchannel,
};
static Cmdtab tvctlmsg[] = {
CMvstart, "vstart", 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,
"tvdbuf", { Qdbuf, 0 }, 0, 0400,
};
static Variant variant[] = {
{ Brooktree_vid, Brooktree_848_did, "Brooktree 848 TV tuner", },
};
static char *boards[] = {
"MIRO PRO",
"MIRO",
};
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 vstop(Tv *);
static void frequency(Tv *, int, int);
static void
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))
intmask = intmask_etbf;
pci = nil;
while ((pci = pcimatch(pci, 0, 0)) != nil) {
int i, t;
Bt848 *bt848;
ushort hscale, hdelay;
uchar v;
for (i = 0; i != nelem(variant); i++)
if (pci->vid == variant[i].vid && pci->did == variant[i].did)
break;
if (i == nelem(variant))
continue;
if (tv) {
print("#V: Currently there is only support for one TV, ignoring %s\n",
variant[i].name);
continue;
}
tv = (Tv *)malloc(sizeof(Tv));
assert(tv);
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");
bt848 = tv->bt848;
// i2c stuff.
if (pci->did >= 878)
tv->i2ccmd = 0x83;
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);
}
if (i2cread(tv, i2c_haupee, &v))
panic("#V: Cannot deal with hauppauge boards\n");
if (i2cread(tv, i2c_stbee, &v))
panic("#V: Cannot deal with STB cards\n");
if (i2cread(tv, i2c_miroproee, &v)) {
tv->board = Bt848_miropro;
t = ((bt848->gpiodata[0] >> 10) - 1) & 7;
}
else {
tv->board = Bt848_miro;
t = ((bt848->gpiodata[0] >> 10) - 1) & 7;
}
if (t >= nelem(tuners))
t = 4;
tv->tuner = &tuners[t];
tv->i2ctuneraddr =
i2cread(tv, 0xc1, &v)? 0xc0:
i2cread(tv, 0xc3, &v)? 0xc2:
i2cread(tv, 0xc5, &v)? 0xc4:
i2cread(tv, 0xc7, &v)? 0xc6: -1;
bt848->capctl = capctl_fullframe;
bt848->adelay = adelay_ntsc;
bt848->bdelay = bdelay_ntsc;
bt848->iform = iform_muxsel0|iform_xtauto|iform_ntsc;
bt848->vbipacksize = vbipacksize & 0xff;
bt848->vbipackdel = (vbipacksize >> 8) & 1;
// setpll(bt848);
bt848->colorfmt = colorfmt_rgb16;
hscale = (ntsc_rawpixels * 4096) / ntsc_sqpixels - 4096;
hdelay = (ntsc_clkx1delay * ntsc_hactive) / ntsc_clkx1hactive;
bt848->ovtc = bt848->evtc = 0;
bt848->ehscalehi = bt848->ohscalehi = (hscale >> 8) & 0xff;
bt848->ehscalelo = bt848->ohscalelo = hscale & 0xff;
bt848->evscalehi &= ~0x1f;
bt848->ovscalehi &= ~0x1f;
bt848->evscalehi |= vscale_interlaced | ((ntsc_vscale >> 8) & 0x1f);
bt848->ovscalehi |= vscale_interlaced | (ntsc_vscale >> 8) & 0x1f;
bt848->evscalelo = bt848->ovscalelo = ntsc_vscale & 0xff;
bt848->ehactivelo = bt848->ohactivelo = ntsc_hactive & 0xff;
bt848->ehdelaylo = bt848->ohdelaylo = hdelay & 0xff;
bt848->evactivelo = bt848->ovactivelo = ntsc_vactive & 0xff;
bt848->evdelaylo = bt848->ovdelaylo = ntsc_vdelay & 0xff;
bt848->ecrop = bt848->ocrop =
((ntsc_hactive >> 8) & 0x03) |
((hdelay >> 6) & 0x0C) |
((ntsc_vactive >> 4) & 0x30) |
((ntsc_vdelay >> 2) & 0xC0);
bt848->colorctl = colorctl_gamma;
bt848->capctl = 0;
bt848->gpiodmactl = gpiodmactl_pltp23_16 |
gpiodmactl_pltp1_16 | gpiodmactl_pktp_32;
bt848->gpioreginp = 0;
bt848->contrastlo = contrast_100percent;
bt848->bright = 16;
bt848->adc = (2 << 6) | adc_crush;
bt848->econtrol = bt848->ocontrol = control_ldec;
bt848->escloop = bt848->oscloop = 0;
bt848->intstat = (ulong)-1;
bt848->intmask = intmask |
intstat_vsync | intstat_scerr | intstat_risci | intstat_ocerr |
intstat_vpress | intstat_fmtchg | intstat_hlock;
bt848->gpioouten &= ~0xF;
bt848->gpioouten |= 0xF;
bt848->gpiodata[0] &= ~0xF;
bt848->gpiodata[0] |= 2; // Enable audio on the MIRO card.
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);
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));
}
}
static Chan*
tvattach(char *spec)
{
return devattach('V', spec);
}
static Walkqid *
tvwalk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, tvtab, nelem(tvtab), devgen);
}
static int
tvstat(Chan *c, uchar *db, int n)
{
return devstat(c, db, n, tvtab, nelem(tvtab), devgen);
}
static Chan*
tvopen(Chan *c, int omode)
{
return devopen(c, omode, tvtab, nelem(tvtab), devgen);
}
static void
tvclose(Chan *)
{}
static long
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 Qregs:
if (offset == 0) {
Bt848 *bt848 = tv->bt848;
int i;
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]);
regslen = p - regs;
}
if (offset >= regslen)
return 0;
if (offset + n > regslen)
n = regslen - offset;
return readstr(offset, a, n, ®s[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;
}
return n;
}
static long
tvwrite(Chan *c, void *a, long n, vlong)
{
Cmdbuf *cb;
Cmdtab *ct;
switch((int)c->qid.path) {
case Qctl:
cb = parsecmd(a, n);
if(waserror()){
free(cb);
nexterror();
}
ct = lookupcmd(cb, tvctlmsg, nelem(tvctlmsg));
switch (ct->index) {
case CMvstart:
vstart(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));
break;
}
poperror();
break;
default:
error(Eio);
}
return n;
}
Dev tvdevtab = {
'V',
"tv",
devreset,
tvinit,
devshutdown,
tvattach,
tvwalk,
tvstat,
tvopen,
devcreate,
tvclose,
tvread,
devbread,
tvwrite,
devbwrite,
devremove,
devwstat,
};
static void
tvinterrupt(Ureg *, Tv *tv)
{
Bt848 *bt848 = tv->bt848;
ulong status;
while ((status = bt848->intstat & bt848->intmask) != 0) {
bt848->intstat = status;
if ((status & intstat_fmtchg) == intstat_fmtchg) {
iprint("int: fmtchg\n");
status &= ~intstat_fmtchg;
}
if ((status & intstat_vpress) == intstat_vpress) {
iprint("int: vpress\n");
status &= ~intstat_vpress;
}
if ((status & intstat_vsync) == intstat_vsync)
status &= ~intstat_vsync;
if ((status & 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;
}
if ((status & intstat_risci) == intstat_risci) {
iprint("int: risci\n");
status &= ~intstat_risci;
}
if ((status & intstat_ocerr) == intstat_ocerr) {
iprint("int: ocerr\n");
status &= ~intstat_ocerr;
}
if ((status & intstat_fbus) == intstat_fbus) {
iprint("int: fbus\n");
status &= ~intstat_fbus;
}
if (status)
iprint("int: ignored interrupts %.8ulX\n", status);
}
}
static int
i2cread(Tv *tv, uchar off, uchar *v)
{
Bt848 *bt848 = tv->bt848;
ulong intstat;
int i;
bt848->intstat = intstat_i2cdone;
bt848->i2c = (off << 24) | tv->i2ccmd;
intstat = -1;
for (i = 0; i != 1000; i++) {
if ((intstat = bt848->intstat) & intstat_i2cdone)
break;
microdelay(1000);
}
if (i == 1000) {
print("i2cread: timeout\n");
return 0;
}
if ((intstat & intstat_i2crack) == 0)
return 0;
*v = bt848->i2c >> 8;
return 1;
}
static int
i2cwrite(Tv *tv, uchar off, uchar d1, uchar d2, int both)
{
Bt848 *bt848 = tv->bt848;
ulong intstat, data;
int i;
bt848->intstat = intstat_i2cdone;
data = (off << 24) | (d1 << 16) | tv->i2ccmd;
if (both)
data |= (d2 << 8) | i2c_bt848w3b;
bt848->i2c = data;
intstat = 0;
for (i = 0; i != 1000; i++) {
if ((intstat = bt848->intstat) & intstat_i2cdone)
break;
microdelay(1000);
}
if (i == 1000) {
print("i2cread: timeout\n");
return 0;
}
if ((intstat & intstat_i2crack) == 0)
return 0;
return 1;
}
static ulong *
riscprogram(ulong paddr, int stride, int bpp)
{
ulong *p, *pbase;
int i;
pbase = p = (ulong *)malloc((ntsc_vactive + 5) * 2 * sizeof(ulong));
assert(p);
assert(ntsc_hactive * bpp <= 0x7FF);
*p++ = riscsync | riscsync_resync | riscsync_vre;
*p++ = 0;
*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;
}
*p++ = riscsync | riscsync_resync | riscsync_vro;
*p++ = 0;
*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;
}
*p++ = riscjmp;
*p++ = PADDR(pbase);
USED(p);
return pbase;
}
static void
vstart(Tv *tv, ulong paddr, int stride)
{
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()) {
iunlock(tv);
free(program);
nexterror();
}
if (tv->program)
error(Einuse);
if (cfmt != bt848->colorfmt)
error(Eio);
tv->program = program;
bt848->riscstrtadd = PADDR(tv->program);
bt848->capctl |= capctl_captureodd|capctl_captureeven;
bt848->gpiodmactl |= gpiodmactl_fifoenable;
bt848->gpiodmactl |= gpiodmactl_riscenable;
poperror();
iunlock(tv);
}
static void
vstop(Tv *tv)
{
Bt848 *bt848 = tv->bt848;
ilock(tv);
if (waserror()) {
if (tv->program)
free(tv->program);
iunlock(tv);
nexterror();
}
if (tv->program) {
bt848->gpiodmactl &= ~gpiodmactl_riscenable;
bt848->gpiodmactl &= ~gpiodmactl_fifoenable;
bt848->capctl &= ~(capctl_captureodd|capctl_captureeven);
free(tv->program);
tv->program = nil;
}
poperror();
iunlock(tv);
}
static long
hrcfreq[] = { /* HRC CATV frequencies */
0, 7200, 5400, 6000, 6600, 7800, 8400, 17400,
18000, 18600, 19200, 19800, 20400, 21000, 12000, 12600,
13200, 13800, 14400, 15000, 15600, 16200, 16800, 21600,
22200, 22800, 23400, 24000, 24600, 25200, 25800, 26400,
27000, 27600, 28200, 28800, 29400, 30000, 30600, 31200,
31800, 32400, 33000, 33600, 34200, 34800, 35400, 36000,
36600, 37200, 37800, 38400, 39000, 39600, 40200, 40800,
41400, 42000, 42600, 43200, 43800, 44400, 45000, 45600,
46200, 46800, 47400, 48000, 48600, 49200, 49800, 50400,
51000, 51600, 52200, 52800, 53400, 54000, 54600, 55200,
55800, 56400, 57000, 57600, 58200, 58800, 59400, 60000,
60600, 61200, 61800, 62400, 63000, 63600, 64200, 9000,
9600, 10200, 10800, 11400, 64800, 65400, 66000, 66600,
67200, 67800, 68400, 69000, 69600, 70200, 70800, 71400,
72000, 72600, 73200, 73800, 74400, 75000, 75600, 76200,
76800, 77400, 78000, 78600, 79200, 79800,
};
static void
frequency(Tv *tv, int channel, int finetune)
{
Tuner *tuner = tv->tuner;
long freq;
ushort div;
uchar cfg;
if (channel < 0 || channel > nelem(hrcfreq))
error(Ebadarg);
freq = (hrcfreq[channel] * Freqmultiplier) / 100;
if (freq < tuner->freq_vhfh)
cfg = tuner->VHF_L;
else if (freq < tuner->freq_uhf)
cfg = tuner->VHF_H;
else
cfg = tuner->UHF;
div = (freq + tuner->offs + finetune) & 0x7fff;
if (!i2cwrite(tv, 0xc0, (div >> 8) & 0x7f, div, 1))
error(Eio);
if (!i2cwrite(tv, 0xc0, tuner->cfg, cfg, 1))
error(Eio);
}
.
## diffname pc/devtv.c 2002/0130
## diff -e /n/emeliedump/2002/0125/sys/src/9/pc/devtv.c /n/emeliedump/2002/0130/sys/src/9/pc/devtv.c
854a
static int
getbpp(Tv *tv)
{
switch (tv->bt848->colorfmt) {
case colorfmt_rgb16:
return 2;
default:
error("getbpp: Unsupport color format\n");
}
return -1;
}
.
801,802d
798,799c
for (i = 0; i != tv->nframes; i++)
if (tv->frames[i].fbase)
free(tv->frames[i].fbase);
free(tv->frames);
tv->frames = nil;
.
793c
if (tv->frames) {
int i;
.
790c
error(Einuse);
.
785,788c
if (tv->fref.ref > 0) {
.
779a
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
.
775d
766,770c
bt848->riscstrtadd = PADDR(tv->frames[0].fstart);
.
763,764c
tv->frames = frames;
tv->nframes = nframes;
.
761a
poperror();
.
759,760c
error(Einuse);
.
757c
if (tv->frames) {
.
743,755d
740,741d
737c
vactivate(Tv *tv, Frame *frames, int nframes)
.
729,731c
// reset status. you really need two instructions ;-(.
*p++ = riscjmp | (0xf << risclabelshift_reset);
*p++ = PADDR(p);
*p++ = riscjmp | riscirq | (fnum << risclabelshift_set);
*lastjmp = p;
.
724,726c
for (i = 0; i != h / 2; i++) {
*p++ = riscwrite | w | riscwrite_sol | riscwrite_eol;
*p++ = paddr + (i * 2 + 1) * stride;
.
713,715c
for (i = 0; i != h / 2; i++) {
*p++ = riscwrite | w | riscwrite_sol | riscwrite_eol;
*p++ = paddr + i * 2 * stride;
.
705c
assert(w <= 0x7FF);
.
702c
pbase = p = (ulong *)malloc((h + 6) * 2 * sizeof(ulong));
.
697c
riscframe(ulong paddr, int fnum, int w, int h, int stride, ulong **lastjmp)
.
695d
616c
tv->lframe = fnum;
.
590c
while (1) {
ulong status;
uchar fnum;
status = bt848->intstat;
fnum = (status >> intstat_riscstatshift) & 0xf;
status &= bt848->intmask;
if (status == 0)
break;
.
588d
548a
.
545a
.
543c
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),
.
506,519d
485,486c
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);
}
.
478d
465c
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;
.
438,440d
305,307d
297a
static int getbpp(Tv *);
.
295c
static void vgastart(Tv *, ulong, int);
static void vstart(Tv *, int, int, int, int);
.
278d
268c
CMvstart, "vstart", 2,
CMvgastart, "vgastart", 3,
.
262a
CMvgastart,
.
219d
216c
Ref fref; // Copying images?
int nframes; // Number of frames to capture.
Frame *frames; // DMA program
int lframe; // Last frame DMAed
.
213,214c
uchar i2ccmd; // I2C command
int board; // What board is this?
.
211d
205a
ulong *fstart;
ulong *fjmp;
uchar *fbase;
} Frame;
typedef struct {
.
114a
risclabelshift_set = 16,
risclabelshift_reset = 20,
.
78c
intstat_riscstatshift = 28, // 100
intstat_i2crack = 1 << 25,
.
28a
Numring = 16,
.
19d
2,3c
* Driver for Bt848 TV tuner.
.
## diffname pc/devtv.c 2002/0620
## diff -e /n/emeliedump/2002/0130/sys/src/9/pc/devtv.c /n/emeliedump/2002/0620/sys/src/9/pc/devtv.c
961a
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;
}
.
960a
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);
}
.
956c
error("getbitspp: Unsupport color format\n");
.
954c
case colorfmt_YCbCr422:
return 16;
case colorfmt_YCbCr411:
return 12;
.
952c
switch (tv->cfmt) {
.
950c
getbitspp(Tv *tv)
.
948a
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;
}
.
946a
tv->channel = channel;
if (tv->msp)
msptune(tv);
.
945c
if (!i2cwrite(tv, tv->i2ctuneraddr, tuner->cfg, cfg, 1))
.
942c
if (!i2cwrite(tv, tv->i2ctuneraddr, (div >> 8) & 0x7f, div, 1))
.
864,867c
frame->fstart = riscpacked(paddr, 0, ntsc_hactive * getbitspp(tv) / 8,
ntsc_vactive, stride * getbitspp(tv) / 8,
&frame->fjmp);
.
851a
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
.
839,841c
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");
}
.
820,821c
bitspp = getbitspp(tv);
bpf = w * h * bitspp / 8;
.
815c
int bitspp, i, bpf;
.
787a
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;
}
.
747c
riscpacked(ulong paddr, int fnum, int w, int h, int stride, ulong **lastjmp)
.
735,736c
if (i == i2c_timeout) {
print("i2cwrite: timeout\n");
.
725,726c
d |= (data << 8) | i2c_bt848w3b;
bt848->i2c = d;
.
723c
d = (addr << 24) | (sub << 16) | tv->i2ccmd;
.
719c
ulong intstat, d;
.
716c
i2cwrite(Tv *tv, uchar addr, uchar sub, uchar data, int both)
.
681,682c
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);
.
678c
vstat &= ~intstat_fbus;
.
676c
if ((vstat & intstat_fbus) == intstat_fbus) {
.
673c
vstat &= ~intstat_ocerr;
.
671c
if ((vstat & intstat_ocerr) == intstat_ocerr) {
.
666,668c
if ((vstat & intstat_risci) == intstat_risci) {
tv->lvframe = fnum;
vstat &= ~intstat_risci;
.
663c
vstat &= ~intstat_scerr;
.
657c
if ((vstat & intstat_scerr) == intstat_scerr) {
.
654,655c
if ((vstat & intstat_vsync) == intstat_vsync) {
vstat &= ~intstat_vsync;
}
.
649,651c
if ((vstat & intstat_vpress) == intstat_vpress) {
// iprint("int: vpress\n");
vstat &= ~intstat_vpress;
.
646c
vstat &= ~intstat_fmtchg;
.
644c
bt848->intstat = vstat;
if (bt878)
bt878->intstat = astat;
if ((vstat & intstat_fmtchg) == intstat_fmtchg) {
.
642c
if (astat)
print("vstat %.8uX, astat %.8uX\n", vstat, astat);
.
639c
if (bt878)
astat = bt878->intstat & bt878->intmask;
else
astat = 0;
if (vstat == 0 && astat == 0)
.
635,637c
vstat = bt848->intstat;
fnum = (vstat >> intstat_riscstatshift) & 0xf;
vstat &= bt848->intmask;
.
632c
ulong vstat, astat;
.
629c
Bt848 *bt848 = tv->bt848,
*bt878 = tv->bt878;
.
596a
free(cb);
.
594a
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;
.
581a
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;
.
568c
tv = &tvs[DEV(c->qid)];
switch(TYPE(c->qid)) {
.
566a
Tv *tv;
.
556c
n = 0;
.
544a
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]);
}
.
540a
tv = &tvs[DEV(c->qid)];
bt848 = tv->bt848;
.
538c
Bt848 *bt848;
.
531,533c
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);
.
527a
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;
}
.
519c
src = tv->frames[tv->lvframe].fbase;
.
514c
if (tv->frames == nil || tv->lvframe >= tv->nframes ||
tv->frames[tv->lvframe].fbase == nil) {
.
504c
tv = &tvs[DEV(c->qid)];
bpf = ntsc_hactive * ntsc_vactive * getbitspp(tv) / 8;
.
500,501c
case Qvdata: {
.
498c
case Qsubdir:
return devdirread(c, a, n, 0, 0, tvgen);
.
496c
switch(TYPE(c->qid)) {
.
492a
uchar *src;
.
491a
Tv *tv;
.
486a
static int
audioblock(void *)
{
return 1;
}
.
479a
if (TYPE(c->qid) == Qadata)
c->session = (Session *)0;
.
474c
return devopen(c, omode, nil, 0, tvgen);
case Qadata:
if (tvs[DEV(c->qid)].bt878 == nil)
error(Enonexist);
break;
.
472c
if (omode != OREAD && TYPE(c->qid) != Qctl)
error(Eperm);
switch (TYPE(c->qid)) {
.
466c
return devstat(c, db, n, 0, 0, tvgen);
.
460c
return devwalk(c, nc, name, nname, 0, 0, tvgen);
.
456a
#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;
}
}
.
445a
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);
.
442,444c
if (tv->amux) {
gpioenable(tv, ~0xfff, 0xfff);
gpiowrite(tv, ~0xfff, tv->amux[AudioRadio]);
}
.
437,440d
435c
intstat_vpress | intstat_fmtchg;
.
399c
tv->cfmt = bt848->colorfmt = colorfmt_rgb16;
.
377a
tv->amux = miroamux;
.
374a
tv->amux = miroamux;
.
372c
}
else if (i2cread(tv, i2c_miroproee, &v)) {
.
367,370c
else if (i2cread(tv, i2c_stbee, &v)) {
USED(t);
.
361,365c
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;
.
358,359c
t = 0;
if (i2cread(tv, i2c_haupee, &v)) {
uchar ee[256];
Pcidev *pci878;
Bt848 *bt878;
.
349c
panic("#V: Cannot allocate memory for Bt848");
.
342,344c
tv = &tvs[ntvs++];
.
336,338c
if (ntvs >= Ntvs) {
print("#V: Too many TV cards found\n");
.
325a
Tv *tv;
.
310c
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);
.
308a
static void astop(Tv *);
static void colormode(Tv *, char *);
.
307a
static void astart(Tv *, char *, uint, uint, uint);
.
302a
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 tvs[Ntvs];
static int ntvs;
.
301c
static ushort Adspfsample[] = {
0x500, 0x700, 0x400, 0x600, 0x300, 0x200, 0x000, 0x100
};
static ushort Adspstereorates[] = {
64, 96, 112, 128, 160, 192, 224, 256, 320, 384
};
.
298a
"Hauppauge Bt878",
.
293a
{ Brooktree_vid, Brooktree_878_did, "Brooktree 878 TV tuner", },
.
285,291d
282a
CMcolormode, "colormode", 2,
CMvolume, "volume", 3,
CMmute, "mute", 1,
.
279a
CMastart, "astart", 5,
CMastop, "astop", 1,
.
275a
CMcolormode,
CMvolume,
CMmute,
.
272a
CMastart,
CMastop,
.
270a
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,
};
.
227c
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?
.
223c
ulong cfmt; // Current color format.
int channel; // Current channel
.
216a
Bt848 *bt878; // Really only audio control registers
.
215a
Rendez;
.
196a
#define packetlen i2c
.
117a
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,
.
115a
riscsync_fm3 = fifo_fm3 << 0,
.
106c
riscwrite = 1 << 28,
riscwrite123 = 9 << 28,
riscwrite1s23 = 11 << 28,
.
103a
fifo_fm3 = 0xe,
.
102c
fifo_vro = 0xc,
.
94a
gpiodmactl_acapenable = 1 << 4,
.
91c
gpiodmactl_apwrdn = 1 << 26, // 10C
gpiodmactl_daes2 = 1 << 13,
gpiodmactl_daiomda = 1 << 6,
gpiodmactl_pltp23_16 = 2 << 6,
.
81a
intstat_pabort = 1 << 17,
intstat_riperr = 1 << 16,
intstat_pperr = 1 << 15,
intstat_fdsr = 1 << 14,
intstat_ftrgt = 1 << 13,
.
71a
colorfmt_YCbCr422 = (8 << 4) | (8 << 0),
colorfmt_YCbCr411 = (9 << 4) | (9 << 0),
.
52a
Bt878_hauppauge,
.
50a
i2c_timeout = 1000,
i2c_delay = 10,
.
49a
i2c_msp3400 = 0x80,
.
44c
i2c_tea6300 = 0x80,
.
41a
i2c_scl = i2c_bt848scl,
i2c_sda = i2c_bt848sda,
.
37a
i2c_nostop = 1 << 5,
i2c_nos1b = 1 << 4,
.
26a
Ntvs = 4,
.
20a
Brooktree_878_did = 0x036E,
.
15c
Qsubdir,
Qsubbase,
Qvdata = Qsubbase,
Qadata,
.
12a
#define max(a, b) (((a) > (b))? (a): (b))
.
11a
#include "hcwAMC.h"
.
## diffname pc/devtv.c 2002/0622
## diff -e /n/emeliedump/2002/0620/sys/src/9/pc/devtv.c /n/emeliedump/2002/0622/sys/src/9/pc/devtv.c
785c
if (omode != OREAD &&
TYPE(c->qid) != Qctl && TYPE(c->qid) != Qvdata)
.
## diffname pc/devtv.c 2002/0712
## diff -e /n/emeliedump/2002/0622/sys/src/9/pc/devtv.c /n/emeliedump/2002/0712/sys/src/9/pc/devtv.c
1075c
print("vstat %.8luX, astat %.8luX\n", vstat, astat);
.
## diffname pc/devtv.c 2002/0821
## diff -e /n/emeliedump/2002/0712/sys/src/9/pc/devtv.c /n/emeliedump/2002/0821/sys/src/9/pc/devtv.c
1520d
|