@@ 0,0 1,421 @@
+/*
+ * M48T59/559 Timekeeper
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "io.h"
+
+enum{
+ STB0 = 0x74,
+ STB1 = 0x75,
+ Data = 0x77,
+
+ NVOFF= 0,
+ NVLEN= 0x1ff0, /* length in bytes of NV RAM */
+
+ /*
+ * register offsets into time of day clock
+ */
+ NVflags= 0x1ff0,
+ NVctl= 0x1ff8,
+ NVsec,
+ NVmin,
+ NVhour,
+ NVday, /* (1 = Sun) */
+ NVmday, /* (1-31) */
+ NVmon, /* (1-12) */
+ NVyear, /* (0-99) */
+
+ /* NVctl */
+ RTwrite = (1<<7),
+ RTread = (1<<6),
+ RTsign = (1<<5),
+ RTcal = 0x1f,
+
+ Qdir = 0,
+ Qrtc,
+ Qnvram,
+};
+
+/*
+ * broken down time
+ */
+typedef struct
+{
+ int sec;
+ int min;
+ int hour;
+ int mday;
+ int mon;
+ int year;
+} Rtc;
+
+QLock rtclock; /* mutex on nvram operations */
+
+static Dirtab rtcdir[]={
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "rtc", {Qrtc, 0}, 0, 0644,
+ "nvram", {Qnvram, 0}, 0, 0600,
+};
+
+static ulong rtc2sec(Rtc*);
+static void sec2rtc(ulong, Rtc*);
+static void setrtc(Rtc*);
+static void nvcksum(void);
+static void nvput(int, uchar);
+static uchar nvget(int);
+
+static Chan*
+rtcattach(char *spec)
+{
+ return devattach('r', spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
+}
+
+static int
+rtcstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+ omode = openmode(omode);
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ if(strcmp(up->user, eve)!=0 && omode!=OREAD)
+ error(Eperm);
+ break;
+ case Qnvram:
+ if(strcmp(up->user, eve)!=0 || !cpuserver)
+ error(Eperm);
+ }
+ return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
+}
+
+static void
+rtcclose(Chan*)
+{
+}
+
+static long
+rtcread(Chan *c, void *buf, long n, vlong off)
+{
+ char *p;
+ ulong t;
+ int i;
+ ulong offset = off;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
+
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ qlock(&rtclock);
+ t = rtctime();
+ qunlock(&rtclock);
+ n = readnum(offset, buf, n, t, 12);
+ return n;
+ case Qnvram:
+ offset += NVOFF;
+ if(offset > NVLEN)
+ return 0;
+ if(n > NVLEN - offset)
+ n = NVLEN - offset;
+ p = buf;
+ qlock(&rtclock);
+ for(i = 0; i < n; i++)
+ p[i] = nvget(i+offset);
+ qunlock(&rtclock);
+ return n;
+ }
+ error(Egreg);
+ return -1; /* never reached */
+}
+
+static long
+rtcwrite(Chan *c, void *buf, long n, vlong off)
+{
+ Rtc rtc;
+ ulong secs;
+ char *cp, *ep;
+ int i;
+ ulong offset = off;
+
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ if(offset!=0)
+ error(Ebadarg);
+ /*
+ * read the time
+ */
+ cp = ep = buf;
+ ep += n;
+ while(cp < ep){
+ if(*cp>='0' && *cp<='9')
+ break;
+ cp++;
+ }
+ secs = strtoul(cp, 0, 0);
+ /*
+ * convert to bcd
+ */
+ sec2rtc(secs, &rtc);
+ /*
+ * write it
+ */
+ qlock(&rtclock);
+ setrtc(&rtc);
+ qunlock(&rtclock);
+ return n;
+ case Qnvram:
+ offset += NVOFF;
+ if(offset > NVLEN)
+ return 0;
+ if(n > NVLEN - offset)
+ n = NVLEN - offset;
+ qlock(&rtclock);
+ for(i = 0; i < n; i++)
+ nvput(i+offset, ((uchar*)buf)[i]);
+ nvcksum();
+ qunlock(&rtclock);
+ return n;
+ }
+ error(Egreg);
+ return -1; /* never reached */
+}
+
+long
+rtcbwrite(Chan *c, Block *bp, ulong offset)
+{
+ return devbwrite(c, bp, offset);
+}
+
+Dev rtcdevtab = {
+ 'r',
+ "rtc",
+
+ devreset,
+ devinit,
+ devshutdown,
+ rtcattach,
+ rtcwalk,
+ rtcstat,
+ rtcopen,
+ devcreate,
+ rtcclose,
+ rtcread,
+ devbread,
+ rtcwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+static void
+nvput(int offset, uchar val)
+{
+ outb(STB0, offset);
+ outb(STB1, offset>>8);
+ outb(Data, val);
+}
+
+static uchar
+nvget(int offset)
+{
+ outb(STB0, offset);
+ outb(STB1, offset>>8);
+ return inb(Data);
+}
+
+static void
+nvcksum(void)
+{
+}
+
+static int
+getbcd(int bcd)
+{
+ return (bcd&0x0f) + 10 * (bcd>>4);
+}
+
+static int
+putbcd(int val)
+{
+ return (val % 10) | (((val/10) % 10) << 4);
+}
+
+long
+rtctime(void)
+{
+ int ctl;
+ Rtc rtc;
+
+ /*
+ * convert from BCD
+ */
+ ctl = nvget(NVctl);
+ ctl &= RTsign|RTcal;
+ nvput(NVctl, ctl|RTread);
+
+ rtc.sec = getbcd(nvget(NVsec) & 0x7f);
+ rtc.min = getbcd(nvget(NVmin));
+ rtc.hour = getbcd(nvget(NVhour));
+ rtc.mday = getbcd(nvget(NVmday));
+ rtc.mon = getbcd(nvget(NVmon));
+ rtc.year = getbcd(nvget(NVyear));
+ if(rtc.year < 70)
+ rtc.year += 2000;
+ else
+ rtc.year += 1900;
+
+ nvput(NVctl, ctl);
+
+ return rtc2sec(&rtc);
+}
+
+static void
+setrtc(Rtc *rtc)
+{
+ int ctl;
+
+ ctl = nvget(NVctl);
+ ctl &= RTsign|RTcal;
+ nvput(NVctl, ctl|RTwrite);
+
+ nvput(NVsec, putbcd(rtc->sec));
+ nvput(NVmin, putbcd(rtc->min));
+ nvput(NVhour, putbcd(rtc->hour));
+ nvput(NVmday, putbcd(rtc->mday));
+ nvput(NVmon, putbcd(rtc->mon));
+ nvput(NVyear, putbcd(rtc->year % 100));
+
+ nvput(NVctl, ctl);
+}
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ * days per month plus days/year
+ */
+static int dmsize[] =
+{
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static int ldmsize[] =
+{
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * return the days/month for the given year
+ */
+static int *
+yrsize(int y)
+{
+
+ if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+/*
+ * compute seconds since Jan 1 1970
+ */
+static ulong
+rtc2sec(Rtc *rtc)
+{
+ ulong secs;
+ int i;
+ int *d2m;
+
+ secs = 0;
+
+ /*
+ * seconds per year
+ */
+ for(i = 1970; i < rtc->year; i++){
+ d2m = yrsize(i);
+ secs += d2m[0] * SEC2DAY;
+ }
+
+ /*
+ * seconds per month
+ */
+ d2m = yrsize(rtc->year);
+ for(i = 1; i < rtc->mon; i++)
+ secs += d2m[i] * SEC2DAY;
+
+ secs += (rtc->mday-1) * SEC2DAY;
+ secs += rtc->hour * SEC2HOUR;
+ secs += rtc->min * SEC2MIN;
+ secs += rtc->sec;
+
+ return secs;
+}
+
+/*
+ * compute rtc from seconds since Jan 1 1970
+ */
+static void
+sec2rtc(ulong secs, Rtc *rtc)
+{
+ int d;
+ long hms, day;
+ int *d2m;
+
+ /*
+ * break initial number into days
+ */
+ hms = secs % SEC2DAY;
+ day = secs / SEC2DAY;
+ if(hms < 0) {
+ hms += SEC2DAY;
+ day -= 1;
+ }
+
+ /*
+ * generate hours:minutes:seconds
+ */
+ rtc->sec = hms % 60;
+ d = hms / 60;
+ rtc->min = d % 60;
+ d /= 60;
+ rtc->hour = d;
+
+ /*
+ * year number
+ */
+ if(day >= 0)
+ for(d = 1970; day >= *yrsize(d); d++)
+ day -= *yrsize(d);
+ else
+ for (d = 1970; day < 0; d--)
+ day += *yrsize(d-1);
+ rtc->year = d;
+
+ /*
+ * generate month
+ */
+ d2m = yrsize(rtc->year);
+ for(d = 1; day >= d2m[d]; d++)
+ day -= d2m[d];
+ rtc->mday = day + 1;
+ rtc->mon = d;
+
+ return;
+}
@@ 0,0 1,854 @@
+/*
+ * 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);
+}
+
@@ 0,0 1,559 @@
+
+/*
+ * Matrox G200, G400 and G450.
+ * see /sys/src/cmd/aux/vga/mga4xx.c
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define Image IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+enum {
+ MATROX = 0x102B,
+ MGA4xx = 0x0525,
+ MGA200 = 0x0521,
+
+ Kilo = 1024,
+ Meg = 1024*1024,
+
+ FCOL = 0x1c24,
+ FXRIGHT = 0x1cac,
+ FXLEFT = 0x1ca8,
+ YDST = 0x1c90,
+ YLEN = 0x1c5c,
+ DWGCTL = 0x1c00,
+ DWG_TRAP = 0x04,
+ DWG_BITBLT = 0x08,
+ DWG_ILOAD = 0x09,
+ DWG_LINEAR = 0x0080,
+ DWG_SOLID = 0x0800,
+ DWG_ARZERO = 0x1000,
+ DWG_SGNZERO = 0x2000,
+ DWG_SHIFTZERO = 0x4000,
+ DWG_REPLACE = 0x000C0000,
+ DWG_REPLACE2 = (DWG_REPLACE | 0x40),
+ DWG_XOR = 0x00060010,
+ DWG_BFCOL = 0x04000000,
+ DWG_BMONOWF = 0x08000000,
+ DWG_TRANSC = 0x40000000,
+ SRCORG = 0x2cb4,
+ PITCH = 0x1c8c,
+ DSTORG = 0x2cb8,
+ PLNWRT = 0x1c1c,
+ ZORG = 0x1c0c,
+ MACCESS = 0x1c04,
+ STATUS = 0x1e14,
+ FXBNDRY = 0x1C84,
+ CXBNDRY = 0x1C80,
+ YTOP = 0x1C98,
+ YBOT = 0x1C9C,
+ YDSTLEN = 0x1C88,
+ AR0 = 0x1C60,
+ AR1 = 0x1C64,
+ AR2 = 0x1C68,
+ AR3 = 0x1C6C,
+ AR4 = 0x1C70,
+ AR5 = 0x1C74,
+ SGN = 0x1C58,
+ SGN_SCANLEFT = 1,
+ SGN_SCANRIGHT = 0,
+ SGN_SDY_POSITIVE = 0,
+ SGN_SDY_NEGATIVE = 4,
+
+ GO = 0x0100,
+ FIFOSTATUS = 0x1E10,
+ CACHEFLUSH = 0x1FFF,
+
+ CRTCEXTIDX = 0x1FDE, /* CRTC Extension Index */
+ CRTCEXTDATA = 0x1FDF, /* CRTC Extension Data */
+
+ FILL_OPERAND = 0x800c7804,
+};
+
+static Pcidev*
+mgapcimatch(void)
+{
+ Pcidev* p;
+
+ p = pcimatch(nil, MATROX, MGA4xx);
+ if (p == nil)
+ p = pcimatch(nil, MATROX, MGA200);
+ return p;
+}
+
+static ulong
+mga4xxlinear(VGAscr* scr, int* size, int* align)
+{
+ ulong aperture, oaperture;
+ int oapsize, wasupamem;
+ Pcidev * p;
+
+ oaperture = scr->aperture;
+ oapsize = scr->apsize;
+ wasupamem = scr->isupamem;
+
+ if(p = mgapcimatch()){
+ aperture = p->mem[0].bar & ~0x0F;
+ if(p->did == MGA4xx)
+ *size = 32*Meg;
+ else
+ *size = 8*Meg;
+ }
+ else
+ aperture = 0;
+
+ if(wasupamem) {
+ if(oaperture == aperture)
+ return oaperture;
+ upafree(oaperture, oapsize);
+ }
+ scr->isupamem = 0;
+
+ aperture = upamalloc(aperture, *size, *align);
+ if(aperture == 0){
+ if(wasupamem && upamalloc(oaperture, oapsize, 0)) {
+ aperture = oaperture;
+ scr->isupamem = 1;
+ }
+ else
+ scr->isupamem = 0;
+ }
+ else
+ scr->isupamem = 1;
+
+ return aperture;
+}
+
+static void
+mgawrite8(VGAscr* scr, int index, uchar val)
+{
+ ((uchar*)scr->io)[index] = val;
+}
+
+static uchar
+mgaread8(VGAscr* scr, int index)
+{
+ return ((uchar*)scr->io)[index];
+}
+
+static uchar
+crtcextset(VGAscr* scr, int index, uchar set, uchar clr)
+{
+ uchar tmp;
+
+ mgawrite8(scr, CRTCEXTIDX, index);
+ tmp = mgaread8(scr, CRTCEXTDATA);
+ mgawrite8(scr, CRTCEXTIDX, index);
+ mgawrite8(scr, CRTCEXTDATA, (tmp & ~clr) | set);
+
+ return tmp;
+}
+
+static void
+mga4xxenable(VGAscr* scr)
+{
+ Pcidev * pci;
+ int size, align;
+ ulong aperture;
+ int i, n, k;
+ uchar * p;
+ uchar x[16];
+ uchar crtcext3;
+
+ /*
+ * Only once, can't be disabled for now.
+ * scr->io holds the virtual address of
+ * the MMIO registers.
+ */
+ if(scr->io)
+ return;
+
+ pci = mgapcimatch();
+ if(pci == nil)
+ return;
+
+ scr->io = upamalloc(pci->mem[1].bar & ~0x0F, 16*1024, 0);
+ if(scr->io == 0)
+ return;
+
+ addvgaseg("mga4xxmmio", scr->io, pci->mem[1].size);
+
+ scr->io = (ulong)KADDR(scr->io);
+
+ /* need to map frame buffer here too, so vga can find memory size */
+ size = 8*Meg;
+ align = 0;
+ aperture = mga4xxlinear(scr, &size, &align);
+ if(aperture) {
+ scr->aperture = aperture;
+ addvgaseg("mga4xxscreen", aperture, size);
+
+ /* Find out how much memory is here, some multiple of 2 Meg */
+
+ /* First Set MGA Mode ... */
+ crtcext3 = crtcextset(scr, 3, 0x80, 0x00);
+
+ p = (uchar*)aperture;
+ n = (size / Meg) / 2;
+ for (i = 0; i < n; i++) {
+ k = (2*i+1)*Meg;
+ p[k] = 0;
+ p[k] = i+1;
+ *((uchar*)(scr->io + CACHEFLUSH)) = 0;
+ x[i] = p[k];
+ }
+ for(i = 1; i < n; i++)
+ if(x[i] != i+1)
+ break;
+ scr->apsize = 2*i*Meg;
+
+ crtcextset(scr, 3, crtcext3, 0xff);
+ }
+}
+
+enum {
+ Index = 0x00, /* Index */
+ Data = 0x0A, /* Data */
+
+ Cxlsb = 0x0C, /* Cursor X LSB */
+ Cxmsb = 0x0D, /* Cursor X MSB */
+ Cylsb = 0x0E, /* Cursor Y LSB */
+ Cymsb = 0x0F, /* Cursor Y MSB */
+
+ Icuradrl = 0x04, /* Cursor Base Address Low */
+ Icuradrh = 0x05, /* Cursor Base Address High */
+ Icctl = 0x06, /* Indirect Cursor Control */
+};
+
+static void
+dac4xxdisable(VGAscr* scr)
+{
+ uchar * dac4xx;
+
+ if(scr->io == 0)
+ return;
+
+ dac4xx = KADDR(scr->io+0x3C00);
+
+ *(dac4xx+Index) = Icctl;
+ *(dac4xx+Data) = 0x00;
+}
+
+static void
+dac4xxload(VGAscr* scr, Cursor* curs)
+{
+ int y;
+ uchar * p;
+ uchar * dac4xx;
+
+ if(scr->io == 0)
+ return;
+
+ dac4xx = KADDR(scr->io+0x3C00);
+
+ dac4xxdisable(scr);
+
+ p = KADDR(scr->storage);
+ for(y = 0; y < 64; y++){
+ *p++ = 0; *p++ = 0; *p++ = 0;
+ *p++ = 0; *p++ = 0; *p++ = 0;
+ if(y <16){
+ *p++ = curs->set[1+y*2]|curs->clr[1+2*y];
+ *p++ = curs->set[y*2]|curs->clr[2*y];
+ } else{
+ *p++ = 0; *p++ = 0;
+ }
+
+ *p++ = 0; *p++ = 0; *p++ = 0;
+ *p++ = 0; *p++ = 0; *p++ = 0;
+ if(y <16){
+ *p++ = curs->set[1+y*2];
+ *p++ = curs->set[y*2];
+ } else{
+ *p++ = 0; *p++ = 0;
+ }
+ }
+ scr->offset.x = 64 + curs->offset.x;
+ scr->offset.y = 64 + curs->offset.y;
+
+ *(dac4xx+Index) = Icctl;
+ *(dac4xx+Data) = 0x03;
+}
+
+static int
+dac4xxmove(VGAscr* scr, Point p)
+{
+ int x, y;
+ uchar * dac4xx;
+
+ if(scr->io == 0)
+ return 1;
+
+ dac4xx = KADDR(scr->io + 0x3C00);
+
+ x = p.x + scr->offset.x;
+ y = p.y + scr->offset.y;
+
+ *(dac4xx+Cxlsb) = x & 0xFF;
+ *(dac4xx+Cxmsb) = (x>>8) & 0x0F;
+
+ *(dac4xx+Cylsb) = y & 0xFF;
+ *(dac4xx+Cymsb) = (y>>8) & 0x0F;
+
+ return 0;
+}
+
+static void
+dac4xxenable(VGAscr* scr)
+{
+ uchar * dac4xx;
+ ulong storage;
+
+ if(scr->io == 0)
+ return;
+ dac4xx = KADDR(scr->io+0x3C00);
+
+ dac4xxdisable(scr);
+
+ storage = (scr->apsize - 4096) & ~0x3ff;
+
+ *(dac4xx+Index) = Icuradrl;
+ *(dac4xx+Data) = 0xff & (storage >> 10);
+ *(dac4xx+Index) = Icuradrh;
+ *(dac4xx+Data) = 0xff & (storage >> 18);
+
+ scr->storage = (ulong) KADDR((ulong)scr->aperture + (ulong)storage);
+
+ /* Show X11-Like Cursor */
+ *(dac4xx+Index) = Icctl;
+ *(dac4xx+Data) = 0x03;
+
+ /* Cursor Color 0 : White */
+ *(dac4xx+Index) = 0x08;
+ *(dac4xx+Data) = 0xff;
+ *(dac4xx+Index) = 0x09;
+ *(dac4xx+Data) = 0xff;
+ *(dac4xx+Index) = 0x0a;
+ *(dac4xx+Data) = 0xff;
+
+ /* Cursor Color 1 : Black */
+ *(dac4xx+Index) = 0x0c;
+ *(dac4xx+Data) = 0x00;
+ *(dac4xx+Index) = 0x0d;
+ *(dac4xx+Data) = 0x00;
+ *(dac4xx+Index) = 0x0e;
+ *(dac4xx+Data) = 0x00;
+
+ /* Cursor Color 2 : Red */
+ *(dac4xx+Index) = 0x10;
+ *(dac4xx+Data) = 0xff;
+ *(dac4xx+Index) = 0x11;
+ *(dac4xx+Data) = 0x00;
+ *(dac4xx+Index) = 0x12;
+ *(dac4xx+Data) = 0x00;
+
+ /*
+ * Load, locate and enable the
+ * 64x64 cursor in X11 mode.
+ */
+ dac4xxload(scr, &arrow);
+ dac4xxmove(scr, ZP);
+}
+
+static void
+mga4xxblank(VGAscr* scr, int blank)
+{
+ char * cp;
+ uchar * mga;
+ uchar seq1, crtcext1;
+
+ /* blank = 0 -> turn screen on */
+ /* blank = 1 -> turn screen off */
+
+ if(scr->io == 0)
+ return;
+ mga = KADDR(scr->io);
+
+ if (blank == 0) {
+ seq1 = 0x00;
+ crtcext1 = 0x00;
+ } else {
+ seq1 = 0x20;
+ crtcext1 = 0x10; /* Default value ... : standby */
+ cp = getconf("*dpms");
+ if (cp) {
+ if (cistrcmp(cp, "standby") == 0) {
+ crtcext1 = 0x10;
+ } else if (cistrcmp(cp, "suspend") == 0) {
+ crtcext1 = 0x20;
+ } else if (cistrcmp(cp, "off") == 0) {
+ crtcext1 = 0x30;
+ }
+ }
+ }
+
+ *(mga + 0x1fc4) = 1;
+ seq1 |= *(mga + 0x1fc5) & ~0x20;
+ *(mga + 0x1fc5) = seq1;
+
+ *(mga + 0x1fde) = 1;
+ crtcext1 |= *(mga + 0x1fdf) & ~0x30;
+ *(mga + 0x1fdf) = crtcext1;
+}
+
+static void
+mgawrite32(uchar * mga, ulong reg, ulong val)
+{
+ ulong * l;
+
+ l = (ulong *)(&mga[reg]);
+ l[0] = val;
+}
+
+static ulong
+mgaread32(uchar * mga, ulong reg)
+{
+ return *((ulong *)(&mga[reg]));
+}
+
+static int
+mga4xxfill(VGAscr* scr, Rectangle r, ulong color)
+{
+ uchar * mga;
+
+ /* Constant Shaded Trapezoids / Rectangle Fills */
+ if(scr->io == 0)
+ return 0;
+ mga = KADDR(scr->io);
+
+ mgawrite32(mga, DWGCTL, 0);
+ mgawrite32(mga, FCOL, color);
+ mgawrite32(mga, FXRIGHT, r.max.x);
+ mgawrite32(mga, FXLEFT, r.min.x);
+ mgawrite32(mga, YDST, r.min.y);
+ mgawrite32(mga, YLEN, Dy(r));
+ mgawrite32(mga, DWGCTL + GO, FILL_OPERAND);
+
+ while (mgaread32(mga, STATUS) & 0x00010000)
+ ;
+ return 1;
+}
+
+#define mga_fifo(n) do {} while ((mgaread32(mga, FIFOSTATUS) & 0xFF) < (n))
+
+static int
+mga4xxscroll(VGAscr* scr, Rectangle r_dst, Rectangle r_src)
+{
+ uchar * mga;
+ ulong pitch;
+ ulong w, h, start, end, y;
+
+ /* Two-operand Bitblts */
+ if(scr->io == 0)
+ return 0;
+ mga = KADDR(scr->io);
+
+ pitch = Dx(scr->gscreen->r);
+
+ mgawrite32(mga, DWGCTL, 0);
+ h = Dy(r_src);
+ w = Dx(r_src);
+ y = r_dst.min.y;
+
+ if ((r_dst.min.y < r_src.min.y) ||
+ ((r_dst.min.y == r_src.min.y) && (r_dst.min.x <= r_src.min.y))) {
+ mga_fifo(2);
+ mgawrite32(mga, DWGCTL, DWG_BITBLT | DWG_SHIFTZERO |
+ DWG_SGNZERO | DWG_BFCOL | DWG_REPLACE);
+ mgawrite32(mga, AR5, pitch);
+ w--;
+ start = r_src.min.y*pitch+r_src.min.x;
+ end = start+w;
+ } else {
+ mga_fifo(3);
+ mgawrite32(mga, DWGCTL, DWG_BITBLT | DWG_SHIFTZERO |
+ DWG_BFCOL | DWG_REPLACE);
+ mgawrite32(mga, SGN, 5);
+ mgawrite32(mga, AR5, (-pitch) & 0x3ffff);
+ w--;
+ end = (r_src.min.y+h-1)*pitch+r_src.min.x;
+ start = end+w;
+ y += h-1;
+ }
+ mga_fifo(4);
+ mgawrite32(mga, AR0, end);
+ mgawrite32(mga, AR3, start);
+ mgawrite32(mga, FXBNDRY, ((r_dst.min.x+w)<<16) | r_dst.min.x);
+ mgawrite32(mga, YDST, y);
+ mgawrite32(mga, YLEN + GO, h);
+
+ while (mgaread32(mga, STATUS) & 0x00010000)
+ ;
+ return 1;
+}
+
+static void
+mga4xxdrawinit(VGAscr* scr)
+{
+ uchar * mga;
+ Pcidev* p;
+
+ p = pcimatch(nil, MATROX, MGA4xx);
+ if (p == nil)
+ return ;
+
+ if(scr->io == 0)
+ return;
+ mga = KADDR(scr->io);
+
+ mgawrite32(mga, SRCORG, 0);
+ mgawrite32(mga, DSTORG, 0);
+ mgawrite32(mga, ZORG, 0);
+ mgawrite32(mga, PLNWRT, ~0);
+ mgawrite32(mga, FCOL, 0xffff0000);
+ mgawrite32(mga, CXBNDRY, 0xFFFF0000);
+ mgawrite32(mga, YTOP, 0);
+ mgawrite32(mga, YBOT, 0x01FFFFFF);
+ mgawrite32(mga, PITCH, Dx(scr->gscreen->r) & ((1 << 13) - 1));
+ switch(scr->gscreen->depth) {
+ case 8:
+ mgawrite32(mga, MACCESS, 0);
+ break;
+ case 32:
+ mgawrite32(mga, MACCESS, 2);
+ break;
+ default:
+ return; /* depth not supported ! */
+ }
+ scr->fill = mga4xxfill;
+ scr->scroll = mga4xxscroll;
+ scr->blank = mga4xxblank;
+}
+
+VGAdev vgamga4xxdev = {
+ "mga4xx",
+
+ mga4xxenable, /* enable */
+ 0, /* disable */
+ 0, /* page */
+ mga4xxlinear, /* linear */
+ mga4xxdrawinit,
+};
+
+VGAcur vgamga4xxcur = {
+ "mga4xxhwgc",
+ dac4xxenable,
+ dac4xxdisable,
+ dac4xxload,
+ dac4xxmove,
+};