xref: /plan9/sys/src/9/pc/devtv.c (revision 567483c891f7c54442ce1d593e764767ee5fcaf7)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier   * Driver for Bt848 TV tuner.
39a747e4fSDavid du Colombier   *
49a747e4fSDavid du Colombier   */
59a747e4fSDavid du Colombier #include	"u.h"
69a747e4fSDavid du Colombier #include	"../port/lib.h"
79a747e4fSDavid du Colombier #include	"mem.h"
89a747e4fSDavid du Colombier #include	"dat.h"
99a747e4fSDavid du Colombier #include	"fns.h"
109a747e4fSDavid du Colombier #include	"../port/error.h"
119a747e4fSDavid du Colombier #include "io.h"
123ff48bf5SDavid du Colombier #include "hcwAMC.h"
133ff48bf5SDavid du Colombier 
143ff48bf5SDavid du Colombier #define max(a, b)	(((a) > (b))? (a): (b))
159a747e4fSDavid du Colombier 
169a747e4fSDavid du Colombier enum {
179a747e4fSDavid du Colombier 	Qdir = 0,
183ff48bf5SDavid du Colombier 	Qsubdir,
193ff48bf5SDavid du Colombier 	Qsubbase,
203ff48bf5SDavid du Colombier 	Qvdata = Qsubbase,
213ff48bf5SDavid du Colombier 	Qadata,
229a747e4fSDavid du Colombier 	Qctl,
239a747e4fSDavid du Colombier 	Qregs,
249a747e4fSDavid du Colombier 
259a747e4fSDavid du Colombier 	Brooktree_vid = 0x109e,
269a747e4fSDavid du Colombier 	Brooktree_848_did = 0x0350,
273ff48bf5SDavid du Colombier 	Brooktree_878_did = 0x036E,
289a747e4fSDavid du Colombier 	Intel_vid = 0x8086,
299a747e4fSDavid du Colombier 	Intel_82437_did = 0x122d,
309a747e4fSDavid du Colombier 
319a747e4fSDavid du Colombier 	K = 1024,
329a747e4fSDavid du Colombier 	M = K * K,
339a747e4fSDavid du Colombier 
343ff48bf5SDavid du Colombier 	Ntvs = 4,
353ff48bf5SDavid du Colombier 
369a747e4fSDavid du Colombier 	Numring = 16,
379a747e4fSDavid du Colombier 
389a747e4fSDavid du Colombier 	ntsc_rawpixels = 910,
3941dd6b47SDavid du Colombier 	ntsc_sqpixels = 780,		/* Including blanking & inactive */
409a747e4fSDavid du Colombier 	ntsc_hactive = 640,
419a747e4fSDavid du Colombier 	ntsc_vactive = 480,
4241dd6b47SDavid du Colombier 	ntsc_clkx1delay = 135,		/* Clock ticks. */
439a747e4fSDavid du Colombier 	ntsc_clkx1hactive = 754,
4441dd6b47SDavid du Colombier 	ntsc_vdelay = 26,		/* # of scan lines. */
459a747e4fSDavid du Colombier 	ntsc_vscale = 0,
469a747e4fSDavid du Colombier 
473ff48bf5SDavid du Colombier 	i2c_nostop = 1 << 5,
483ff48bf5SDavid du Colombier 	i2c_nos1b  = 1 << 4,
499a747e4fSDavid du Colombier 	i2c_timing = 7 << 4,
509a747e4fSDavid du Colombier 	i2c_bt848w3b = 1 << 2,
519a747e4fSDavid du Colombier 	i2c_bt848scl = 1 << 1,
529a747e4fSDavid du Colombier 	i2c_bt848sda = 1 << 0,
533ff48bf5SDavid du Colombier 	i2c_scl = i2c_bt848scl,
543ff48bf5SDavid du Colombier 	i2c_sda = i2c_bt848sda,
559a747e4fSDavid du Colombier 
5641dd6b47SDavid du Colombier 	i2c_miroproee = 0x80,		/* MIRO PRO EEPROM */
573ff48bf5SDavid du Colombier 	i2c_tea6300 = 0x80,
589a747e4fSDavid du Colombier 	i2c_tda8425 = 0x82,
599a747e4fSDavid du Colombier 	i2c_tda9840 = 0x84,
609a747e4fSDavid du Colombier 	i2c_tda9850 = 0xb6,
6141dd6b47SDavid du Colombier 	i2c_haupee = 0xa0,		/* Hauppage EEPROM */
6241dd6b47SDavid du Colombier 	i2c_stbee = 0xae,		/* STB EEPROM */
633ff48bf5SDavid du Colombier 	i2c_msp3400 = 0x80,
643ff48bf5SDavid du Colombier 
653ff48bf5SDavid du Colombier 	i2c_timeout = 1000,
663ff48bf5SDavid du Colombier 	i2c_delay = 10,
679a747e4fSDavid du Colombier 
689a747e4fSDavid du Colombier 	Bt848_miropro = 0,
699a747e4fSDavid du Colombier 	Bt848_miro,
703ff48bf5SDavid du Colombier 	Bt878_hauppauge,
719a747e4fSDavid du Colombier 
7241dd6b47SDavid du Colombier 	/* Bit fields. */
7341dd6b47SDavid du Colombier 	iform_muxsel1 = 3 << 5,		/* 004 */
749a747e4fSDavid du Colombier 	iform_muxsel0 = 2 << 5,
759a747e4fSDavid du Colombier 	iform_xtselmask = 3 << 3,
769a747e4fSDavid du Colombier 	iform_xtauto = 3 << 3,
779a747e4fSDavid du Colombier 	iform_formatmask = 7 << 0,
789a747e4fSDavid du Colombier 	iform_ntsc = 1 << 0,
799a747e4fSDavid du Colombier 
8041dd6b47SDavid du Colombier 	control_ldec = 1 << 5,		/* 02C */
8141dd6b47SDavid du Colombier 	contrast_100percent = 0xd8,	/* 030 */
829a747e4fSDavid du Colombier 
8341dd6b47SDavid du Colombier 	vscale_interlaced = 1 << 5,	/* 04C */
849a747e4fSDavid du Colombier 
8541dd6b47SDavid du Colombier 	adelay_ntsc = 104,		/* 060 */
8641dd6b47SDavid du Colombier 	bdelay_ntsc = 93,		/* 064 */
8741dd6b47SDavid du Colombier 	adc_crush = 1 << 0,		/* 068 */
889a747e4fSDavid du Colombier 
8941dd6b47SDavid du Colombier 	colorfmt_rgb16 = 2 << 4 | 2 << 0,	/* 0D4 */
9041dd6b47SDavid du Colombier 	colorfmt_YCbCr422 = 8 << 4 | 8 << 0,
9141dd6b47SDavid du Colombier 	colorfmt_YCbCr411 = 9 << 4 | 9 << 0,
9241dd6b47SDavid du Colombier 	colorctl_gamma     = 1 << 4,	/* 0D8 */
9341dd6b47SDavid du Colombier 	capctl_fullframe   = 1 << 4,	/* 0DC */
949a747e4fSDavid du Colombier 	capctl_captureodd  = 1 << 1,
959a747e4fSDavid du Colombier 	capctl_captureeven = 1 << 0,
9641dd6b47SDavid du Colombier 	vbipacksize = 0x190,		/* 0E0 */
979a747e4fSDavid du Colombier 
9841dd6b47SDavid du Colombier 	intstat_riscstatshift = 28,	/* 100 */
999a747e4fSDavid du Colombier 	intstat_i2crack = 1 << 25,
1009a747e4fSDavid du Colombier 	intstat_scerr = 1 << 19,
1019a747e4fSDavid du Colombier 	intstat_ocerr = 1 << 18,
1023ff48bf5SDavid du Colombier 	intstat_pabort = 1 << 17,
1033ff48bf5SDavid du Colombier 	intstat_riperr = 1 << 16,
1043ff48bf5SDavid du Colombier 	intstat_pperr = 1 << 15,
1053ff48bf5SDavid du Colombier 	intstat_fdsr = 1 << 14,
1063ff48bf5SDavid du Colombier 	intstat_ftrgt = 1 << 13,
1079a747e4fSDavid du Colombier 	intstat_fbus = 1 << 12,
1089a747e4fSDavid du Colombier 	intstat_risci = 1 << 11,
1099a747e4fSDavid du Colombier 	intstat_i2cdone = 1 << 8,
1109a747e4fSDavid du Colombier 	intstat_vpress = 1 << 5,
1119a747e4fSDavid du Colombier 	intstat_hlock = 1 << 4,
1129a747e4fSDavid du Colombier 	intstat_vsync = 1 << 1,
1139a747e4fSDavid du Colombier 	intstat_fmtchg = 1 << 0,
11441dd6b47SDavid du Colombier 	intmask_etbf = 1 << 23,		/* 104 */
1159a747e4fSDavid du Colombier 
11641dd6b47SDavid du Colombier 	gpiodmactl_apwrdn = 1 << 26,	/* 10C */
1173ff48bf5SDavid du Colombier 	gpiodmactl_daes2 = 1 << 13,
1183ff48bf5SDavid du Colombier 	gpiodmactl_daiomda = 1 << 6,
1193ff48bf5SDavid du Colombier 	gpiodmactl_pltp23_16 = 2 << 6,
1209a747e4fSDavid du Colombier 	gpiodmactl_pltp23_0 = 0 << 6,
1219a747e4fSDavid du Colombier 	gpiodmactl_pltp1_16 = 2 << 4,
1229a747e4fSDavid du Colombier 	gpiodmactl_pltp1_0 = 0 << 4,
1233ff48bf5SDavid du Colombier 	gpiodmactl_acapenable = 1 << 4,
1249a747e4fSDavid du Colombier 	gpiodmactl_pktp_32 = 3 << 2,
1259a747e4fSDavid du Colombier 	gpiodmactl_pktp_0 = 0 << 2,
1269a747e4fSDavid du Colombier 	gpiodmactl_riscenable = 1 << 1,
1279a747e4fSDavid du Colombier 	gpiodmactl_fifoenable = 1 << 0,
1289a747e4fSDavid du Colombier 
12941dd6b47SDavid du Colombier 	/* RISC instructions and parameters. */
1309a747e4fSDavid du Colombier 	fifo_vre = 0x4,
1313ff48bf5SDavid du Colombier 	fifo_vro = 0xc,
1329a747e4fSDavid du Colombier 	fifo_fm1 = 0x6,
1333ff48bf5SDavid du Colombier 	fifo_fm3 = 0xe,
1349a747e4fSDavid du Colombier 
1359a747e4fSDavid du Colombier 	riscirq = 1 << 24,
1363ff48bf5SDavid du Colombier 	riscwrite = 1 << 28,
1373ff48bf5SDavid du Colombier 	riscwrite123 = 9 << 28,
1383ff48bf5SDavid du Colombier 	riscwrite1s23 = 11 << 28,
1399a747e4fSDavid du Colombier 		riscwrite_sol = 1 << 27,
1409a747e4fSDavid du Colombier 		riscwrite_eol = 1 << 26,
1419a747e4fSDavid du Colombier 	riscskip = 0x2 << 28,
1429a747e4fSDavid du Colombier 	riscjmp = 0x7 << 28,
1439a747e4fSDavid du Colombier 	riscsync = 0x8 << 28,
1449a747e4fSDavid du Colombier 		riscsync_resync = 1 << 15,
1459a747e4fSDavid du Colombier 		riscsync_vre = fifo_vre << 0,
1469a747e4fSDavid du Colombier 		riscsync_vro = fifo_vro << 0,
1479a747e4fSDavid du Colombier 		riscsync_fm1 = fifo_fm1 << 0,
1483ff48bf5SDavid du Colombier 		riscsync_fm3 = fifo_fm3 << 0,
1499a747e4fSDavid du Colombier 	risclabelshift_set = 16,
1509a747e4fSDavid du Colombier 	risclabelshift_reset = 20,
1513ff48bf5SDavid du Colombier 
1523ff48bf5SDavid du Colombier 	AudioTuner = 0,
1533ff48bf5SDavid du Colombier 	AudioRadio,
1543ff48bf5SDavid du Colombier 	AudioExtern,
1553ff48bf5SDavid du Colombier 	AudioIntern,
1563ff48bf5SDavid du Colombier 	AudioOff,
1573ff48bf5SDavid du Colombier 	AudioOn,
1583ff48bf5SDavid du Colombier 
1593ff48bf5SDavid du Colombier 	asel_tv = 0,
1603ff48bf5SDavid du Colombier 	asel_radio,
1613ff48bf5SDavid du Colombier 	asel_mic,
1623ff48bf5SDavid du Colombier 	asel_smxc,
1633ff48bf5SDavid du Colombier 
1643ff48bf5SDavid du Colombier 	Hwbase_ad = 448000,
1653ff48bf5SDavid du Colombier 
1663ff48bf5SDavid du Colombier 	msp_dem = 0x10,
1673ff48bf5SDavid du Colombier 	msp_bbp = 0x12,
1683ff48bf5SDavid du Colombier 
16941dd6b47SDavid du Colombier 	/* Altera definitions. */
1703ff48bf5SDavid du Colombier 	gpio_altera_data = 1 << 0,
1713ff48bf5SDavid du Colombier 	gpio_altera_clock = 1 << 20,
1723ff48bf5SDavid du Colombier 	gpio_altera_nconfig = 1 << 23,
1733ff48bf5SDavid du Colombier 
1743ff48bf5SDavid du Colombier 	Ial = 0x140001,
1753ff48bf5SDavid du Colombier 	Idma = 0x100002,
1763ff48bf5SDavid du Colombier 
1773ff48bf5SDavid du Colombier 	Adsp = 0x7fd8,
1783ff48bf5SDavid du Colombier 	Adsp_verifysystem = 1,
1793ff48bf5SDavid du Colombier 	Adsp_querysupportplay,
1803ff48bf5SDavid du Colombier 	Adsp_setstyle,
1813ff48bf5SDavid du Colombier 	Adsp_setsrate,
1823ff48bf5SDavid du Colombier 	Adsp_setchannels,
1833ff48bf5SDavid du Colombier 	Adsp_setresolution,
1843ff48bf5SDavid du Colombier 	Adsp_setcrcoptions,
1853ff48bf5SDavid du Colombier 	Adsp_bufenqfor,
1863ff48bf5SDavid du Colombier 	Adsp_logbuffer,
1873ff48bf5SDavid du Colombier 	Adsp_startplay,
1883ff48bf5SDavid du Colombier 	Adsp_stopplay,
1893ff48bf5SDavid du Colombier 	Adsp_autostop,
1903ff48bf5SDavid du Colombier 	Adsp_startrecord,
1913ff48bf5SDavid du Colombier 	Adsp_stoprecord,
1923ff48bf5SDavid du Colombier 	Adsp_getlastprocessed,
1933ff48bf5SDavid du Colombier 	Adsp_pause,
1943ff48bf5SDavid du Colombier 	Adsp_resume,
1953ff48bf5SDavid du Colombier 	Adsp_setvolume,
1963ff48bf5SDavid du Colombier 	Adsp_querysupportrecord,
1973ff48bf5SDavid du Colombier 	Adsp_generalbufenq,
1983ff48bf5SDavid du Colombier 	Adsp_setdownmixtype,
1993ff48bf5SDavid du Colombier 	Adsp_setigain,
2003ff48bf5SDavid du Colombier 	Adsp_setlineout,
2013ff48bf5SDavid du Colombier 	Adsp_setlangmixtype,
2023ff48bf5SDavid du Colombier 
2033ff48bf5SDavid du Colombier 	Kfir_gc = 0,
2043ff48bf5SDavid du Colombier 	Kfir_dsp_riscmc,
2053ff48bf5SDavid du Colombier 	Kfir_dsp_risccram,
2063ff48bf5SDavid du Colombier 	Kfir_dsp_unitmc,
2073ff48bf5SDavid du Colombier 	Kfir_bsm_mc,
2083ff48bf5SDavid du Colombier 	Kfir_mux_mc,
2093ff48bf5SDavid du Colombier 
2103ff48bf5SDavid du Colombier 	Kfir_devid_gc = 7,
2113ff48bf5SDavid du Colombier 	Kfir_devid_dsp = 4,
2123ff48bf5SDavid du Colombier 	Kfir_devid_bsm = 5,
2133ff48bf5SDavid du Colombier 	Kfir_devid_mux = 8,
2143ff48bf5SDavid du Colombier 
2153ff48bf5SDavid du Colombier 	Kfir_200 = 200,
2163ff48bf5SDavid du Colombier 	Kfir_dev_inst = Kfir_200,
2173ff48bf5SDavid du Colombier 	Kfir_201 = 201,
2183ff48bf5SDavid du Colombier 	Kfir_exec = Kfir_201,
2193ff48bf5SDavid du Colombier 	Kfir_202 = 202,
2203ff48bf5SDavid du Colombier 	Kfir_adr_eready = 254,
2213ff48bf5SDavid du Colombier 
2223ff48bf5SDavid du Colombier 	Kfir_d_eready_encoding = 0,
2233ff48bf5SDavid du Colombier 	Kfir_d_eready_ready,
2243ff48bf5SDavid du Colombier 	Kfir_d_eready_test,
2253ff48bf5SDavid du Colombier 	Kfir_d_eready_stopdetect,
2263ff48bf5SDavid du Colombier 	Kfir_d_eready_seqend,
2273ff48bf5SDavid du Colombier 
2283ff48bf5SDavid du Colombier 	VT_KFIR_OFF = 0,
2293ff48bf5SDavid du Colombier 	VT_KFIR_ON,
2303ff48bf5SDavid du Colombier 
2313ff48bf5SDavid du Colombier 	VT_KFIR_LAYER_II = 1,
2323ff48bf5SDavid du Colombier 	VT_KFIR_STEREO = 1,
2333ff48bf5SDavid du Colombier 
2343ff48bf5SDavid du Colombier 	Gpioinit = 0,
2353ff48bf5SDavid du Colombier 	Gpiooutput,
2363ff48bf5SDavid du Colombier 	Gpioinput,
2373ff48bf5SDavid du Colombier 
2383ff48bf5SDavid du Colombier 	Srate_5512 = 0,
2393ff48bf5SDavid du Colombier 	Srate_11025 = 2,
2403ff48bf5SDavid du Colombier 	Srate_16000 = 3,
2413ff48bf5SDavid du Colombier 	Srate_22050 = 4,
2423ff48bf5SDavid du Colombier 	Srate_32000 = 5,
2433ff48bf5SDavid du Colombier 	Srate_44100 = 6,
2443ff48bf5SDavid du Colombier 	Srate_48000 = 7,
2453ff48bf5SDavid du Colombier 
2469a747e4fSDavid du Colombier };
2479a747e4fSDavid du Colombier 
24859c21d95SDavid du Colombier typedef struct Variant Variant;
24959c21d95SDavid du Colombier struct Variant {
2509a747e4fSDavid du Colombier 	ushort	vid;
2519a747e4fSDavid du Colombier 	ushort	did;
2529a747e4fSDavid du Colombier 	char	*name;
25359c21d95SDavid du Colombier };
2549a747e4fSDavid du Colombier 
25559c21d95SDavid du Colombier typedef struct Bt848 Bt848;
25659c21d95SDavid du Colombier struct Bt848 {
25741dd6b47SDavid du Colombier 	ulong	devstat;	/* 000 */
25841dd6b47SDavid du Colombier 	ulong	iform;		/* 004 */
25941dd6b47SDavid du Colombier 	ulong	tdec;		/* 008 */
26041dd6b47SDavid du Colombier 	ulong	ecrop;		/* 00C */
26141dd6b47SDavid du Colombier 	ulong	evdelaylo;	/* 010 */
26241dd6b47SDavid du Colombier 	ulong	evactivelo;	/* 014 */
26341dd6b47SDavid du Colombier 	ulong	ehdelaylo;	/* 018 */
26441dd6b47SDavid du Colombier 	ulong	ehactivelo;	/* 01C */
26541dd6b47SDavid du Colombier 	ulong	ehscalehi;	/* 020 */
26641dd6b47SDavid du Colombier 	ulong	ehscalelo;	/* 024 */
26741dd6b47SDavid du Colombier 	ulong	bright;		/* 028 */
26841dd6b47SDavid du Colombier 	ulong	econtrol;	/* 02C */
26941dd6b47SDavid du Colombier 	ulong	contrastlo;	/* 030 */
27041dd6b47SDavid du Colombier 	ulong	satulo;		/* 034 */
27141dd6b47SDavid du Colombier 	ulong	satvlo;		/* 038 */
27241dd6b47SDavid du Colombier 	ulong	hue;		/* 03C */
27341dd6b47SDavid du Colombier 	ulong	escloop;	/* 040 */
27441dd6b47SDavid du Colombier 	ulong	pad0;		/* 044 */
27541dd6b47SDavid du Colombier 	ulong	oform;		/* 048 */
27641dd6b47SDavid du Colombier 	ulong	evscalehi;	/* 04C */
27741dd6b47SDavid du Colombier 	ulong	evscalelo;	/* 050 */
27841dd6b47SDavid du Colombier 	ulong	test;		/* 054 */
27941dd6b47SDavid du Colombier 	ulong	pad1[2];	/* 058-05C */
28041dd6b47SDavid du Colombier 	ulong	adelay;		/* 060 */
28141dd6b47SDavid du Colombier 	ulong	bdelay;		/* 064 */
28241dd6b47SDavid du Colombier 	ulong	adc;		/* 068 */
28341dd6b47SDavid du Colombier 	ulong	evtc;		/* 06C */
28441dd6b47SDavid du Colombier 	ulong	pad2[3];	/* 070-078 */
28541dd6b47SDavid du Colombier 	ulong	sreset;		/* 07C */
28641dd6b47SDavid du Colombier 	ulong	tglb;		/* 080 */
28741dd6b47SDavid du Colombier 	ulong	tgctrl;		/* 084 */
28841dd6b47SDavid du Colombier 	ulong	pad3;		/* 088 */
28941dd6b47SDavid du Colombier 	ulong	ocrop;		/* 08C */
29041dd6b47SDavid du Colombier 	ulong	ovdelaylo;	/* 090 */
29141dd6b47SDavid du Colombier 	ulong	ovactivelo;	/* 094 */
29241dd6b47SDavid du Colombier 	ulong	ohdelaylo;	/* 098 */
29341dd6b47SDavid du Colombier 	ulong	ohactivelo;	/* 09C */
29441dd6b47SDavid du Colombier 	ulong	ohscalehi;	/* 0A0 */
29541dd6b47SDavid du Colombier 	ulong	ohscalelo;	/* 0A4 */
29641dd6b47SDavid du Colombier 	ulong	pad4;		/* 0A8 */
29741dd6b47SDavid du Colombier 	ulong	ocontrol;	/* 0AC */
29841dd6b47SDavid du Colombier 	ulong	pad5[4];	/* 0B0-0BC */
29941dd6b47SDavid du Colombier 	ulong	oscloop;	/* 0C0 */
30041dd6b47SDavid du Colombier 	ulong	pad6[2];	/* 0C4-0C8 */
30141dd6b47SDavid du Colombier 	ulong	ovscalehi;	/* 0CC */
30241dd6b47SDavid du Colombier 	ulong	ovscalelo;	/* 0D0 */
30341dd6b47SDavid du Colombier 	ulong	colorfmt;	/* 0D4 */
30441dd6b47SDavid du Colombier 	ulong	colorctl;	/* 0D8 */
30541dd6b47SDavid du Colombier 	ulong	capctl;		/* 0DC */
30641dd6b47SDavid du Colombier 	ulong	vbipacksize;	/* 0E0 */
30741dd6b47SDavid du Colombier 	ulong	vbipackdel;	/* 0E4 */
30841dd6b47SDavid du Colombier 	ulong	fcap;		/* 0E8 */
30941dd6b47SDavid du Colombier 	ulong	ovtc;		/* 0EC */
31041dd6b47SDavid du Colombier 	ulong	pllflo;		/* 0F0 */
31141dd6b47SDavid du Colombier 	ulong	pllfhi;		/* 0F4 */
31241dd6b47SDavid du Colombier 	ulong	pllxci;		/* 0F8 */
31341dd6b47SDavid du Colombier 	ulong	dvsif;		/* 0FC */
31441dd6b47SDavid du Colombier 	ulong	intstat;	/* 100 */
31541dd6b47SDavid du Colombier 	ulong	intmask;	/* 104 */
31641dd6b47SDavid du Colombier 	ulong	pad7;		/* 108 */
31741dd6b47SDavid du Colombier 	ulong	gpiodmactl;	/* 10C */
31841dd6b47SDavid du Colombier 	ulong	i2c;		/* 110 */
31941dd6b47SDavid du Colombier 	ulong	riscstrtadd;	/* 114 */
32041dd6b47SDavid du Colombier 	ulong	gpioouten;	/* 118 */
32141dd6b47SDavid du Colombier 	ulong	gpioreginp;	/* 11C */
32241dd6b47SDavid du Colombier 	ulong	risccount;	/* 120 */
32341dd6b47SDavid du Colombier 	ulong	pad8[55];	/* 124-1FC */
32441dd6b47SDavid du Colombier 	ulong	gpiodata[64];	/* 200-2FC */
32559c21d95SDavid du Colombier };
3269a747e4fSDavid du Colombier 
3273ff48bf5SDavid du Colombier #define packetlen	i2c
3283ff48bf5SDavid du Colombier 
32959c21d95SDavid du Colombier typedef struct Tuner Tuner;
33059c21d95SDavid du Colombier struct Tuner {
3319a747e4fSDavid du Colombier 	char	*name;
33241dd6b47SDavid du Colombier   	ushort	freq_vhfh;	/* Start frequency */
3339a747e4fSDavid du Colombier 	ushort	freq_uhf;
3349a747e4fSDavid du Colombier 	uchar	VHF_L;
3359a747e4fSDavid du Colombier 	uchar	VHF_H;
3369a747e4fSDavid du Colombier 	uchar	UHF;
3379a747e4fSDavid du Colombier 	uchar	cfg;
3389a747e4fSDavid du Colombier 	ushort	offs;
33959c21d95SDavid du Colombier };
3409a747e4fSDavid du Colombier 
34159c21d95SDavid du Colombier typedef struct Frame Frame;
34259c21d95SDavid du Colombier struct Frame {
3439a747e4fSDavid du Colombier 	ulong	*fstart;
3449a747e4fSDavid du Colombier 	ulong	*fjmp;
3459a747e4fSDavid du Colombier 	uchar	*fbase;
34659c21d95SDavid du Colombier };
3479a747e4fSDavid du Colombier 
34859c21d95SDavid du Colombier typedef struct Tv Tv;
34959c21d95SDavid du Colombier struct Tv {
3509a747e4fSDavid du Colombier 	Lock;
3513ff48bf5SDavid du Colombier 	Rendez;
3529a747e4fSDavid du Colombier 	Bt848	*bt848;
35341dd6b47SDavid du Colombier 	Bt848	*bt878;		/* Really only audio control registers */
3549a747e4fSDavid du Colombier 	Variant	*variant;
3559a747e4fSDavid du Colombier 	Tuner	*tuner;
3569a747e4fSDavid du Colombier 	Pcidev	*pci;
3579a747e4fSDavid du Colombier 	uchar	i2ctuneraddr;
35841dd6b47SDavid du Colombier 	uchar	i2ccmd;		/* I2C command */
35941dd6b47SDavid du Colombier 	int	board;		/* What board is this? */
36041dd6b47SDavid du Colombier 	ulong	cfmt;		/* Current color format. */
36141dd6b47SDavid du Colombier 	int	channel;	/* Current channel */
36241dd6b47SDavid du Colombier 	Ref	fref;		/* Copying images? */
36341dd6b47SDavid du Colombier 	int	nframes;	/* Number of frames to capture. */
36441dd6b47SDavid du Colombier 	Frame	*frames;	/* DMA program */
36541dd6b47SDavid du Colombier 	int	lvframe;	/* Last video frame DMAed */
36641dd6b47SDavid du Colombier 	uchar	*amux;		/* Audio multiplexer. */
36741dd6b47SDavid du Colombier 	int	nablocks;	/* Number of audio blocks allocated */
36841dd6b47SDavid du Colombier 	int	absize;		/* Audio block size */
36941dd6b47SDavid du Colombier 	int	narblocks;	/* Number of audio blocks received */
37041dd6b47SDavid du Colombier 	ulong	*arisc;		/* Audio risc bloc */
37141dd6b47SDavid du Colombier 	uchar	*abuf;		/* Audio data buffers */
3723ff48bf5SDavid du Colombier 	char	ainfo[128];
3733ff48bf5SDavid du Colombier 
37441dd6b47SDavid du Colombier 	/* WinTV/PVR stuff. */
3753ff48bf5SDavid du Colombier 	int	msp;
3763ff48bf5SDavid du Colombier 	Lock	kfirlock;
37741dd6b47SDavid du Colombier 	ulong	i2cstate;	/* Last i2c state. */
37841dd6b47SDavid du Colombier 	int	gpiostate;	/* Current GPIO state */
37941dd6b47SDavid du Colombier 	ulong	alterareg;	/* Last used altera register */
38041dd6b47SDavid du Colombier 	ulong	alteraclock;	/* Used to clock the altera */
38141dd6b47SDavid du Colombier 	int	asrate;		/* Audio sample rate */
38241dd6b47SDavid du Colombier 	uchar	aleft, aright;	/* Left and right audio volume */
3833ff48bf5SDavid du Colombier 	ulong	kfirclock;
38441dd6b47SDavid du Colombier 	Ref	aref;		/* Copying audio? */
38559c21d95SDavid du Colombier };
3869a747e4fSDavid du Colombier 
3879a747e4fSDavid du Colombier enum {
3889a747e4fSDavid du Colombier 	TemicPAL = 0,
3899a747e4fSDavid du Colombier 	PhilipsPAL,
3909a747e4fSDavid du Colombier 	PhilipsNTSC,
3919a747e4fSDavid du Colombier 	PhilipsSECAM,
3929a747e4fSDavid du Colombier 	Notuner,
3939a747e4fSDavid du Colombier 	PhilipsPALI,
3949a747e4fSDavid du Colombier 	TemicNTSC,
3959a747e4fSDavid du Colombier 	TemicPALI,
3969a747e4fSDavid du Colombier 	Temic4036,
3979a747e4fSDavid du Colombier 	AlpsTSBH1,
3989a747e4fSDavid du Colombier 	AlpsTSBE1,
3999a747e4fSDavid du Colombier 
4009a747e4fSDavid du Colombier 	Freqmultiplier = 16,
4019a747e4fSDavid du Colombier };
4029a747e4fSDavid du Colombier 
4039a747e4fSDavid du Colombier static Tuner tuners[] = {
4049a747e4fSDavid du Colombier         {"Temic PAL", Freqmultiplier * 140.25, Freqmultiplier * 463.25,
4059a747e4fSDavid du Colombier 		0x02, 0x04, 0x01, 0x8e, 623 },
4069a747e4fSDavid du Colombier 	{"Philips PAL_I", Freqmultiplier * 140.25, Freqmultiplier * 463.25,
4079a747e4fSDavid du Colombier 		0xa0, 0x90, 0x30, 0x8e, 623 },
4089a747e4fSDavid du Colombier 	{"Philips NTSC",  Freqmultiplier * 157.25, Freqmultiplier * 451.25,
4099a747e4fSDavid du Colombier 		0xA0, 0x90, 0x30, 0x8e, 732 },
4109a747e4fSDavid du Colombier 	{"Philips SECAM", Freqmultiplier * 168.25, Freqmultiplier * 447.25,
4119a747e4fSDavid du Colombier 		0xA7, 0x97, 0x37, 0x8e, 623 },
4129a747e4fSDavid du Colombier 	{"NoTuner", 0, 0,
4139a747e4fSDavid du Colombier 		0x00, 0x00, 0x00, 0x00, 0 },
4149a747e4fSDavid du Colombier 	{"Philips PAL", Freqmultiplier * 168.25, Freqmultiplier * 447.25,
4159a747e4fSDavid du Colombier 		0xA0, 0x90, 0x30, 0x8e, 623 },
4169a747e4fSDavid du Colombier 	{"Temic NTSC", Freqmultiplier * 157.25, Freqmultiplier * 463.25,
4179a747e4fSDavid du Colombier 		0x02, 0x04, 0x01, 0x8e, 732 },
4189a747e4fSDavid du Colombier 	{"TEMIC PAL_I", Freqmultiplier * 170.00, Freqmultiplier * 450.00,
4199a747e4fSDavid du Colombier 		0x02, 0x04, 0x01, 0x8e, 623 },
4209a747e4fSDavid du Colombier 	{"Temic 4036 FY5 NTSC", Freqmultiplier * 157.25, Freqmultiplier * 463.25,
4219a747e4fSDavid du Colombier 		0xa0, 0x90, 0x30, 0x8e, 732 },
4229a747e4fSDavid du Colombier 	{"Alps TSBH1", Freqmultiplier * 137.25, Freqmultiplier * 385.25,
4239a747e4fSDavid du Colombier 		0x01, 0x02, 0x08, 0x8e, 732 },
4249a747e4fSDavid du Colombier 	{"Alps TSBE1", Freqmultiplier * 137.25, Freqmultiplier * 385.25,
4259a747e4fSDavid du Colombier 		0x01, 0x02, 0x08, 0x8e, 732 },
4269a747e4fSDavid du Colombier };
4279a747e4fSDavid du Colombier 
4283ff48bf5SDavid du Colombier static int hp_tuners[] = {
4293ff48bf5SDavid du Colombier 	Notuner,
4303ff48bf5SDavid du Colombier 	Notuner,
4313ff48bf5SDavid du Colombier 	Notuner,
4323ff48bf5SDavid du Colombier 	Notuner,
4333ff48bf5SDavid du Colombier 	Notuner,
4343ff48bf5SDavid du Colombier 	PhilipsNTSC,
4353ff48bf5SDavid du Colombier 	Notuner,
4363ff48bf5SDavid du Colombier 	Notuner,
4373ff48bf5SDavid du Colombier 	PhilipsPAL,
4383ff48bf5SDavid du Colombier 	PhilipsSECAM,
4393ff48bf5SDavid du Colombier 	PhilipsNTSC,
4403ff48bf5SDavid du Colombier 	PhilipsPALI,
4413ff48bf5SDavid du Colombier 	Notuner,
4423ff48bf5SDavid du Colombier 	Notuner,
4433ff48bf5SDavid du Colombier 	TemicPAL,
4443ff48bf5SDavid du Colombier 	TemicPALI,
4453ff48bf5SDavid du Colombier 	Notuner,
4463ff48bf5SDavid du Colombier 	PhilipsSECAM,
4473ff48bf5SDavid du Colombier 	PhilipsNTSC,
4483ff48bf5SDavid du Colombier 	PhilipsPALI,
4493ff48bf5SDavid du Colombier 	Notuner,
4503ff48bf5SDavid du Colombier 	PhilipsPAL,
4513ff48bf5SDavid du Colombier 	Notuner,
4523ff48bf5SDavid du Colombier 	PhilipsNTSC,
4533ff48bf5SDavid du Colombier };
4543ff48bf5SDavid du Colombier 
4559a747e4fSDavid du Colombier enum {
4569a747e4fSDavid du Colombier 	CMvstart,
4573ff48bf5SDavid du Colombier 	CMastart,
4583ff48bf5SDavid du Colombier 	CMastop,
4599a747e4fSDavid du Colombier 	CMvgastart,
4609a747e4fSDavid du Colombier 	CMvstop,
4619a747e4fSDavid du Colombier 	CMchannel,
4623ff48bf5SDavid du Colombier 	CMcolormode,
4633ff48bf5SDavid du Colombier 	CMvolume,
4643ff48bf5SDavid du Colombier 	CMmute,
4659a747e4fSDavid du Colombier };
4669a747e4fSDavid du Colombier 
4679a747e4fSDavid du Colombier static Cmdtab tvctlmsg[] = {
4689a747e4fSDavid du Colombier 	CMvstart,	"vstart",	2,
4693ff48bf5SDavid du Colombier 	CMastart,	"astart",	5,
4703ff48bf5SDavid du Colombier 	CMastop,	"astop",	1,
4719a747e4fSDavid du Colombier 	CMvgastart,	"vgastart",	3,
4729a747e4fSDavid du Colombier 	CMvstop,	"vstop",	1,
4739a747e4fSDavid du Colombier 	CMchannel,	"channel",	3,
4743ff48bf5SDavid du Colombier 	CMcolormode,	"colormode",	2,
4753ff48bf5SDavid du Colombier 	CMvolume,	"volume",	3,
4763ff48bf5SDavid du Colombier 	CMmute,		"mute",		1,
4779a747e4fSDavid du Colombier };
4789a747e4fSDavid du Colombier 
4799a747e4fSDavid du Colombier static Variant variant[] = {
4809a747e4fSDavid du Colombier 	{ Brooktree_vid, Brooktree_848_did, "Brooktree 848 TV tuner", },
4813ff48bf5SDavid du Colombier 	{ Brooktree_vid, Brooktree_878_did, "Brooktree 878 TV tuner", },
4829a747e4fSDavid du Colombier };
4839a747e4fSDavid du Colombier 
4849a747e4fSDavid du Colombier static char *boards[] = {
4859a747e4fSDavid du Colombier 	"MIRO PRO",
4869a747e4fSDavid du Colombier 	"MIRO",
4873ff48bf5SDavid du Colombier 	"Hauppauge Bt878",
4889a747e4fSDavid du Colombier };
4899a747e4fSDavid du Colombier 
4903ff48bf5SDavid du Colombier static ushort Adspfsample[] = {
4913ff48bf5SDavid du Colombier 	0x500, 0x700, 0x400, 0x600, 0x300, 0x200, 0x000, 0x100
4923ff48bf5SDavid du Colombier };
4933ff48bf5SDavid du Colombier static ushort Adspstereorates[] = {
4943ff48bf5SDavid du Colombier 	64, 96, 112, 128, 160, 192, 224, 256, 320, 384
4953ff48bf5SDavid du Colombier };
4963ff48bf5SDavid du Colombier 
4973ff48bf5SDavid du Colombier static uchar miroamux[] = { 2, 0, 0, 0, 10, 0 };
4983ff48bf5SDavid du Colombier static uchar hauppaugeamux[] = { 0, 1, 2, 3, 4, 0 };
4993ff48bf5SDavid du Colombier static char *nicamstate[] = {
5003ff48bf5SDavid du Colombier 	"analog", "???", "digital", "bad digital receiption"
5013ff48bf5SDavid du Colombier };
5023ff48bf5SDavid du Colombier 
5033ff48bf5SDavid du Colombier 
5043ff48bf5SDavid du Colombier static Tv tvs[Ntvs];
5053ff48bf5SDavid du Colombier static int ntvs;
5069a747e4fSDavid du Colombier 
5079a747e4fSDavid du Colombier static int i2cread(Tv *, uchar, uchar *);
5089a747e4fSDavid du Colombier static int i2cwrite(Tv *, uchar, uchar, uchar, int);
5099a747e4fSDavid du Colombier static void tvinterrupt(Ureg *, Tv *);
5109a747e4fSDavid du Colombier static void vgastart(Tv *, ulong, int);
5119a747e4fSDavid du Colombier static void vstart(Tv *, int, int, int, int);
5123ff48bf5SDavid du Colombier static void astart(Tv *, char *, uint, uint, uint);
5139a747e4fSDavid du Colombier static void vstop(Tv *);
5143ff48bf5SDavid du Colombier static void astop(Tv *);
5153ff48bf5SDavid du Colombier static void colormode(Tv *, char *);
5169a747e4fSDavid du Colombier static void frequency(Tv *, int, int);
5173ff48bf5SDavid du Colombier static int getbitspp(Tv *);
5183ff48bf5SDavid du Colombier static char *getcolormode(ulong);
5193ff48bf5SDavid du Colombier static int mspreset(Tv *);
5203ff48bf5SDavid du Colombier static void i2cscan(Tv *);
5213ff48bf5SDavid du Colombier static int kfirinitialize(Tv *);
5223ff48bf5SDavid du Colombier static void msptune(Tv *);
5233ff48bf5SDavid du Colombier static void mspvolume(Tv *, int, int, int);
5243ff48bf5SDavid du Colombier static void gpioenable(Tv *, ulong, ulong);
5253ff48bf5SDavid du Colombier static void gpiowrite(Tv *, ulong, ulong);
5269a747e4fSDavid du Colombier 
5279a747e4fSDavid du Colombier static void
tvinit(void)5289a747e4fSDavid du Colombier tvinit(void)
5299a747e4fSDavid du Colombier {
5309a747e4fSDavid du Colombier 	Pcidev *pci;
5319a747e4fSDavid du Colombier 	ulong intmask;
5329a747e4fSDavid du Colombier 
53341dd6b47SDavid du Colombier 	/* Test for a triton memory controller. */
5349a747e4fSDavid du Colombier 	intmask = 0;
5359a747e4fSDavid du Colombier 	if (pcimatch(nil, Intel_vid, Intel_82437_did))
5369a747e4fSDavid du Colombier 		intmask = intmask_etbf;
5379a747e4fSDavid du Colombier 
5389a747e4fSDavid du Colombier 	pci = nil;
5399a747e4fSDavid du Colombier 	while ((pci = pcimatch(pci, 0, 0)) != nil) {
5409a747e4fSDavid du Colombier 		int i, t;
5413ff48bf5SDavid du Colombier 		Tv *tv;
5429a747e4fSDavid du Colombier 		Bt848 *bt848;
5439a747e4fSDavid du Colombier 		ushort hscale, hdelay;
5449a747e4fSDavid du Colombier 		uchar v;
5459a747e4fSDavid du Colombier 
5469a747e4fSDavid du Colombier 		for (i = 0; i != nelem(variant); i++)
5479a747e4fSDavid du Colombier 			if (pci->vid == variant[i].vid && pci->did == variant[i].did)
5489a747e4fSDavid du Colombier 				break;
5499a747e4fSDavid du Colombier 		if (i == nelem(variant))
5509a747e4fSDavid du Colombier 			continue;
5519a747e4fSDavid du Colombier 
5523ff48bf5SDavid du Colombier 		if (ntvs >= Ntvs) {
5533ff48bf5SDavid du Colombier 			print("#V: Too many TV cards found\n");
5549a747e4fSDavid du Colombier 			continue;
5559a747e4fSDavid du Colombier 		}
5569a747e4fSDavid du Colombier 
5573ff48bf5SDavid du Colombier 		tv = &tvs[ntvs++];
5589a747e4fSDavid du Colombier 		tv->variant = &variant[i];
5599a747e4fSDavid du Colombier 		tv->pci = pci;
5604de34a7eSDavid du Colombier 		tv->bt848 = (Bt848 *)vmap(pci->mem[0].bar & ~0x0F, 4 * K);
5619a747e4fSDavid du Colombier 		if (tv->bt848 == nil)
5623ff48bf5SDavid du Colombier 			panic("#V: Cannot allocate memory for Bt848");
5639a747e4fSDavid du Colombier 		bt848 = tv->bt848;
5649a747e4fSDavid du Colombier 
56541dd6b47SDavid du Colombier 		/* i2c stuff. */
5669a747e4fSDavid du Colombier 		if (pci->did >= 878)
5679a747e4fSDavid du Colombier 			tv->i2ccmd = 0x83;
5689a747e4fSDavid du Colombier 		else
5699a747e4fSDavid du Colombier 			tv->i2ccmd = i2c_timing | i2c_bt848scl | i2c_bt848sda;
5709a747e4fSDavid du Colombier 
5713ff48bf5SDavid du Colombier 		t = 0;
5723ff48bf5SDavid du Colombier 		if (i2cread(tv, i2c_haupee, &v)) {
5733ff48bf5SDavid du Colombier 			uchar ee[256];
5743ff48bf5SDavid du Colombier 			Pcidev *pci878;
5753ff48bf5SDavid du Colombier 			Bt848 *bt878;
5769a747e4fSDavid du Colombier 
5773ff48bf5SDavid du Colombier 			tv->board = Bt878_hauppauge;
5783ff48bf5SDavid du Colombier 			if (!i2cwrite(tv, i2c_haupee, 0, 0, 0))
5793ff48bf5SDavid du Colombier 				panic("#V: Cannot write to Hauppauge EEPROM");
5803ff48bf5SDavid du Colombier 			for (i = 0; i != sizeof ee; i++)
5813ff48bf5SDavid du Colombier 				if (!i2cread(tv, i2c_haupee + 1, &ee[i]))
5823ff48bf5SDavid du Colombier 					panic("#V: Cannot read from Hauppauge EEPROM");
5833ff48bf5SDavid du Colombier 
5843ff48bf5SDavid du Colombier 			if (ee[9] > sizeof hp_tuners / sizeof hp_tuners[0])
5853ff48bf5SDavid du Colombier 				panic("#V: Tuner out of range (max %d, this %d)",
5863ff48bf5SDavid du Colombier 					sizeof hp_tuners / sizeof hp_tuners[0], ee[9]);
5873ff48bf5SDavid du Colombier 			t = hp_tuners[ee[9]];
5883ff48bf5SDavid du Colombier 
58941dd6b47SDavid du Colombier 			/* Initialize the audio channel. */
5903ff48bf5SDavid du Colombier 			if ((pci878 = pcimatch(nil, Brooktree_vid, 0x878)) == nil)
5913ff48bf5SDavid du Colombier 				panic("#V: Unsupported Hauppage board");
5923ff48bf5SDavid du Colombier 
5933ff48bf5SDavid du Colombier 			tv->bt878 = bt878 =
5944de34a7eSDavid du Colombier 				(Bt848 *)vmap(pci878->mem[0].bar & ~0x0F, 4 * K);
5953ff48bf5SDavid du Colombier 			if (bt878 == nil)
5963ff48bf5SDavid du Colombier 				panic("#V: Cannot allocate memory for the Bt878");
5973ff48bf5SDavid du Colombier 
5983ff48bf5SDavid du Colombier 			kfirinitialize(tv);
5993ff48bf5SDavid du Colombier 			// i2cscan(tv);
6003ff48bf5SDavid du Colombier 			mspreset(tv);
6013ff48bf5SDavid du Colombier 
6023ff48bf5SDavid du Colombier 			bt878->gpiodmactl = 0;
6033ff48bf5SDavid du Colombier 			bt878->intstat = (ulong)-1;
6043ff48bf5SDavid du Colombier 			intrenable(pci878->intl, (void (*)(Ureg *, void *))tvinterrupt,
6053ff48bf5SDavid du Colombier 					tv, pci878->tbdf, "tv");
6063ff48bf5SDavid du Colombier 
6073ff48bf5SDavid du Colombier 			tv->amux = hauppaugeamux;
6089a747e4fSDavid du Colombier 		}
6093ff48bf5SDavid du Colombier 		else if (i2cread(tv, i2c_stbee, &v)) {
6103ff48bf5SDavid du Colombier 			USED(t);
6119a747e4fSDavid du Colombier 			panic("#V: Cannot deal with STB cards\n");
6123ff48bf5SDavid du Colombier 		}
6133ff48bf5SDavid du Colombier 		else if (i2cread(tv, i2c_miroproee, &v)) {
6149a747e4fSDavid du Colombier 			tv->board = Bt848_miropro;
6159a747e4fSDavid du Colombier 			t = ((bt848->gpiodata[0] >> 10) - 1) & 7;
6163ff48bf5SDavid du Colombier 			tv->amux = miroamux;
6179a747e4fSDavid du Colombier 		}
6189a747e4fSDavid du Colombier 		else {
6199a747e4fSDavid du Colombier 			tv->board = Bt848_miro;
6203ff48bf5SDavid du Colombier 			tv->amux = miroamux;
6219a747e4fSDavid du Colombier 			t = ((bt848->gpiodata[0] >> 10) - 1) & 7;
6229a747e4fSDavid du Colombier 		}
6239a747e4fSDavid du Colombier 
6249a747e4fSDavid du Colombier 		if (t >= nelem(tuners))
6259a747e4fSDavid du Colombier 			t = 4;
6269a747e4fSDavid du Colombier 		tv->tuner = &tuners[t];
6279a747e4fSDavid du Colombier 		tv->i2ctuneraddr =
6289a747e4fSDavid du Colombier 			i2cread(tv, 0xc1, &v)? 0xc0:
6299a747e4fSDavid du Colombier 			i2cread(tv, 0xc3, &v)? 0xc2:
6309a747e4fSDavid du Colombier 			i2cread(tv, 0xc5, &v)? 0xc4:
6319a747e4fSDavid du Colombier 			i2cread(tv, 0xc7, &v)? 0xc6: -1;
6329a747e4fSDavid du Colombier 
6339a747e4fSDavid du Colombier 		bt848->capctl = capctl_fullframe;
6349a747e4fSDavid du Colombier 		bt848->adelay = adelay_ntsc;
6359a747e4fSDavid du Colombier 		bt848->bdelay = bdelay_ntsc;
6369a747e4fSDavid du Colombier 		bt848->iform = iform_muxsel0|iform_xtauto|iform_ntsc;
6379a747e4fSDavid du Colombier 		bt848->vbipacksize = vbipacksize & 0xff;
6389a747e4fSDavid du Colombier 		bt848->vbipackdel = (vbipacksize >> 8) & 1;
6399a747e4fSDavid du Colombier 
6409a747e4fSDavid du Colombier 		// setpll(bt848);
6419a747e4fSDavid du Colombier 
6423ff48bf5SDavid du Colombier 		tv->cfmt = bt848->colorfmt = colorfmt_rgb16;
6439a747e4fSDavid du Colombier 
6449a747e4fSDavid du Colombier 		hscale = (ntsc_rawpixels * 4096) / ntsc_sqpixels - 4096;
6459a747e4fSDavid du Colombier 		hdelay = (ntsc_clkx1delay * ntsc_hactive) / ntsc_clkx1hactive;
6469a747e4fSDavid du Colombier 
6479a747e4fSDavid du Colombier 		bt848->ovtc = bt848->evtc = 0;
6489a747e4fSDavid du Colombier 		bt848->ehscalehi = bt848->ohscalehi = (hscale >> 8) & 0xff;
6499a747e4fSDavid du Colombier 		bt848->ehscalelo = bt848->ohscalelo = hscale & 0xff;
6509a747e4fSDavid du Colombier 		bt848->evscalehi &= ~0x1f;
6519a747e4fSDavid du Colombier 		bt848->ovscalehi &= ~0x1f;
6529a747e4fSDavid du Colombier 		bt848->evscalehi |= vscale_interlaced | ((ntsc_vscale >> 8) & 0x1f);
6539a747e4fSDavid du Colombier 		bt848->ovscalehi |= vscale_interlaced | (ntsc_vscale >> 8) & 0x1f;
6549a747e4fSDavid du Colombier 		bt848->evscalelo = bt848->ovscalelo = ntsc_vscale & 0xff;
6559a747e4fSDavid du Colombier 		bt848->ehactivelo = bt848->ohactivelo = ntsc_hactive & 0xff;
6569a747e4fSDavid du Colombier 		bt848->ehdelaylo = bt848->ohdelaylo = hdelay & 0xff;
6579a747e4fSDavid du Colombier 		bt848->evactivelo = bt848->ovactivelo = ntsc_vactive & 0xff;
6589a747e4fSDavid du Colombier 		bt848->evdelaylo = bt848->ovdelaylo = ntsc_vdelay & 0xff;
6599a747e4fSDavid du Colombier 		bt848->ecrop = bt848->ocrop =
6609a747e4fSDavid du Colombier 			((ntsc_hactive >> 8) & 0x03) |
6619a747e4fSDavid du Colombier 			((hdelay >> 6) & 0x0C) |
6629a747e4fSDavid du Colombier 	        		((ntsc_vactive >> 4) & 0x30) |
6639a747e4fSDavid du Colombier 			((ntsc_vdelay >> 2) & 0xC0);
6649a747e4fSDavid du Colombier 
6659a747e4fSDavid du Colombier 		bt848->colorctl = colorctl_gamma;
6669a747e4fSDavid du Colombier 		bt848->capctl = 0;
6679a747e4fSDavid du Colombier 		bt848->gpiodmactl = gpiodmactl_pltp23_16 |
6689a747e4fSDavid du Colombier 			gpiodmactl_pltp1_16 | gpiodmactl_pktp_32;
6699a747e4fSDavid du Colombier 		bt848->gpioreginp = 0;
6709a747e4fSDavid du Colombier 		bt848->contrastlo = contrast_100percent;
6719a747e4fSDavid du Colombier 		bt848->bright = 16;
6729a747e4fSDavid du Colombier 		bt848->adc = (2 << 6) | adc_crush;
6739a747e4fSDavid du Colombier 		bt848->econtrol = bt848->ocontrol = control_ldec;
6749a747e4fSDavid du Colombier 		bt848->escloop = bt848->oscloop = 0;
6759a747e4fSDavid du Colombier 		bt848->intstat = (ulong)-1;
6769a747e4fSDavid du Colombier 		bt848->intmask = intmask |
6779a747e4fSDavid du Colombier 			intstat_vsync | intstat_scerr | intstat_risci | intstat_ocerr |
6783ff48bf5SDavid du Colombier 			intstat_vpress | intstat_fmtchg;
6799a747e4fSDavid du Colombier 
6809a747e4fSDavid du Colombier 
6813ff48bf5SDavid du Colombier 		if (tv->amux) {
6823ff48bf5SDavid du Colombier 			gpioenable(tv, ~0xfff, 0xfff);
6833ff48bf5SDavid du Colombier 			gpiowrite(tv, ~0xfff, tv->amux[AudioRadio]);
6843ff48bf5SDavid du Colombier 		}
6853ff48bf5SDavid du Colombier 
6863ff48bf5SDavid du Colombier 		print("#V%ld: %s (rev %d) (%s/%s) intl %d\n",
6873ff48bf5SDavid du Colombier 			tv - tvs, tv->variant->name, pci->rid, boards[tv->board],
6883ff48bf5SDavid du Colombier 			tv->tuner->name, pci->intl);
6899a747e4fSDavid du Colombier 
6909a747e4fSDavid du Colombier 		intrenable(pci->intl, (void (*)(Ureg *, void *))tvinterrupt,
6919a747e4fSDavid du Colombier 			tv, pci->tbdf, "tv");
6929a747e4fSDavid du Colombier 	}
6939a747e4fSDavid du Colombier }
6949a747e4fSDavid du Colombier 
6959a747e4fSDavid du Colombier static Chan*
tvattach(char * spec)6969a747e4fSDavid du Colombier tvattach(char *spec)
6979a747e4fSDavid du Colombier {
6989a747e4fSDavid du Colombier 	return devattach('V', spec);
6999a747e4fSDavid du Colombier }
7009a747e4fSDavid du Colombier 
7013ff48bf5SDavid du Colombier #define TYPE(q)		((int)((q).path & 0xff))
7023ff48bf5SDavid du Colombier #define DEV(q)		((int)(((q).path >> 8) & 0xff))
7033ff48bf5SDavid du Colombier #define QID(d, t)	((((d) & 0xff) << 8) | (t))
7043ff48bf5SDavid du Colombier 
7053ff48bf5SDavid du Colombier static int
tv1gen(Chan * c,int i,Dir * dp)7063ff48bf5SDavid du Colombier tv1gen(Chan *c, int i, Dir *dp)
7073ff48bf5SDavid du Colombier {
7083ff48bf5SDavid du Colombier 	Qid qid;
7093ff48bf5SDavid du Colombier 
7103ff48bf5SDavid du Colombier 	switch (i) {
7113ff48bf5SDavid du Colombier 	case Qvdata:
7123ff48bf5SDavid du Colombier 		mkqid(&qid, QID(DEV(c->qid), Qvdata), 0, QTFILE);
7133ff48bf5SDavid du Colombier 		devdir(c, qid, "video", 0, eve, 0444, dp);
7143ff48bf5SDavid du Colombier 		return 1;
7153ff48bf5SDavid du Colombier 	case Qadata:
7163ff48bf5SDavid du Colombier 		mkqid(&qid, QID(DEV(c->qid), Qadata), 0, QTFILE);
7173ff48bf5SDavid du Colombier 		devdir(c, qid, "audio", 0, eve, 0444, dp);
7183ff48bf5SDavid du Colombier 		return 1;
7193ff48bf5SDavid du Colombier 	case Qctl:
7203ff48bf5SDavid du Colombier 		mkqid(&qid, QID(DEV(c->qid), Qctl), 0, QTFILE);
7213ff48bf5SDavid du Colombier 		devdir(c, qid, "ctl", 0, eve, 0444, dp);
7223ff48bf5SDavid du Colombier 		return 1;
7233ff48bf5SDavid du Colombier 	case Qregs:
7243ff48bf5SDavid du Colombier 		mkqid(&qid, QID(DEV(c->qid), Qregs), 0, QTFILE);
7253ff48bf5SDavid du Colombier 		devdir(c, qid, "regs", 0, eve, 0444, dp);
7263ff48bf5SDavid du Colombier 		return 1;
7273ff48bf5SDavid du Colombier 	}
7283ff48bf5SDavid du Colombier 	return -1;
7293ff48bf5SDavid du Colombier }
7303ff48bf5SDavid du Colombier 
7313ff48bf5SDavid du Colombier static int
tvgen(Chan * c,char *,Dirtab *,int,int i,Dir * dp)7323ff48bf5SDavid du Colombier tvgen(Chan *c, char *, Dirtab *, int, int i, Dir *dp)
7333ff48bf5SDavid du Colombier {
7343ff48bf5SDavid du Colombier 	Qid qid;
7353ff48bf5SDavid du Colombier 	int dev;
7363ff48bf5SDavid du Colombier 
7373ff48bf5SDavid du Colombier 	dev = DEV(c->qid);
7383ff48bf5SDavid du Colombier 	switch (TYPE(c->qid)) {
7393ff48bf5SDavid du Colombier 	case Qdir:
7403ff48bf5SDavid du Colombier 		if (i == DEVDOTDOT) {
7413ff48bf5SDavid du Colombier 			mkqid(&qid, Qdir, 0, QTDIR);
7423ff48bf5SDavid du Colombier 			devdir(c, qid, "#V", 0, eve, 0555, dp);
7433ff48bf5SDavid du Colombier 			return 1;
7443ff48bf5SDavid du Colombier 		}
7453ff48bf5SDavid du Colombier 
7463ff48bf5SDavid du Colombier 		if (i >= ntvs)
7473ff48bf5SDavid du Colombier 			return -1;
7483ff48bf5SDavid du Colombier 
7493ff48bf5SDavid du Colombier 		mkqid(&qid, QID(i, Qsubdir), 0, QTDIR);
7503ff48bf5SDavid du Colombier 		snprint(up->genbuf, sizeof(up->genbuf), "tv%d", i);
7513ff48bf5SDavid du Colombier 		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
7523ff48bf5SDavid du Colombier 		return 1;
7533ff48bf5SDavid du Colombier 
7543ff48bf5SDavid du Colombier 	case Qsubdir:
7553ff48bf5SDavid du Colombier 		if (i == DEVDOTDOT) {
7563ff48bf5SDavid du Colombier 			mkqid(&qid, QID(dev, Qdir), 0, QTDIR);
7573ff48bf5SDavid du Colombier 			snprint(up->genbuf, sizeof(up->genbuf), "tv%d", dev);
7583ff48bf5SDavid du Colombier 			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
7593ff48bf5SDavid du Colombier 			return 1;
7603ff48bf5SDavid du Colombier 		}
7613ff48bf5SDavid du Colombier 
7623ff48bf5SDavid du Colombier 		return tv1gen(c, i + Qsubbase, dp);
7633ff48bf5SDavid du Colombier 
7643ff48bf5SDavid du Colombier 	case Qvdata:
7653ff48bf5SDavid du Colombier 	case Qadata:
7663ff48bf5SDavid du Colombier 	case Qctl:
7673ff48bf5SDavid du Colombier 	case Qregs:
7683ff48bf5SDavid du Colombier 		return tv1gen(c, TYPE(c->qid), dp);
7693ff48bf5SDavid du Colombier 
7703ff48bf5SDavid du Colombier 	default:
7713ff48bf5SDavid du Colombier 		return -1;
7723ff48bf5SDavid du Colombier 	}
7733ff48bf5SDavid du Colombier }
7743ff48bf5SDavid du Colombier 
7759a747e4fSDavid du Colombier static Walkqid *
tvwalk(Chan * c,Chan * nc,char ** name,int nname)7769a747e4fSDavid du Colombier tvwalk(Chan *c, Chan *nc, char **name, int nname)
7779a747e4fSDavid du Colombier {
7783ff48bf5SDavid du Colombier 	return devwalk(c, nc, name, nname, 0, 0, tvgen);
7799a747e4fSDavid du Colombier }
7809a747e4fSDavid du Colombier 
7819a747e4fSDavid du Colombier static int
tvstat(Chan * c,uchar * db,int n)7829a747e4fSDavid du Colombier tvstat(Chan *c, uchar *db, int n)
7839a747e4fSDavid du Colombier {
7843ff48bf5SDavid du Colombier 	return devstat(c, db, n, 0, 0, tvgen);
7859a747e4fSDavid du Colombier }
7869a747e4fSDavid du Colombier 
7879a747e4fSDavid du Colombier static Chan*
tvopen(Chan * c,int omode)7889a747e4fSDavid du Colombier tvopen(Chan *c, int omode)
7899a747e4fSDavid du Colombier {
7903ff48bf5SDavid du Colombier 	if (omode != OREAD &&
7913ff48bf5SDavid du Colombier 		TYPE(c->qid) != Qctl && TYPE(c->qid) != Qvdata)
7923ff48bf5SDavid du Colombier 		error(Eperm);
7933ff48bf5SDavid du Colombier 
7943ff48bf5SDavid du Colombier 	switch (TYPE(c->qid)) {
7959a747e4fSDavid du Colombier 	case Qdir:
7963ff48bf5SDavid du Colombier 		return devopen(c, omode, nil, 0, tvgen);
7973ff48bf5SDavid du Colombier 	case Qadata:
7983ff48bf5SDavid du Colombier 		if (tvs[DEV(c->qid)].bt878 == nil)
7993ff48bf5SDavid du Colombier 			error(Enonexist);
8003ff48bf5SDavid du Colombier 		break;
8019a747e4fSDavid du Colombier 	}
8029a747e4fSDavid du Colombier 
8039a747e4fSDavid du Colombier 	c->mode = openmode(omode);
8049a747e4fSDavid du Colombier 	c->flag |= COPEN;
8059a747e4fSDavid du Colombier 	c->offset = 0;
8063ff48bf5SDavid du Colombier 
8073ff48bf5SDavid du Colombier 	if (TYPE(c->qid) == Qadata)
808061a3f44SDavid du Colombier 		c->aux = nil;
8099a747e4fSDavid du Colombier 	return c;
8109a747e4fSDavid du Colombier }
8119a747e4fSDavid du Colombier 
8129a747e4fSDavid du Colombier static void
tvclose(Chan *)8139a747e4fSDavid du Colombier tvclose(Chan *)
81441dd6b47SDavid du Colombier {
81541dd6b47SDavid du Colombier }
8169a747e4fSDavid du Colombier 
8173ff48bf5SDavid du Colombier static int
audioblock(void *)8183ff48bf5SDavid du Colombier audioblock(void *)
8193ff48bf5SDavid du Colombier {
8203ff48bf5SDavid du Colombier 	return 1;
8213ff48bf5SDavid du Colombier }
8223ff48bf5SDavid du Colombier 
8239a747e4fSDavid du Colombier static long
tvread(Chan * c,void * a,long n,vlong offset)8249a747e4fSDavid du Colombier tvread(Chan *c, void *a, long n, vlong offset)
8259a747e4fSDavid du Colombier {
8269a747e4fSDavid du Colombier 	static char regs[10 * K];
8279a747e4fSDavid du Colombier 	static int regslen;
8283ff48bf5SDavid du Colombier 	Tv *tv;
8299a747e4fSDavid du Colombier 	char *e, *p;
8303ff48bf5SDavid du Colombier 	uchar *src;
8319a747e4fSDavid du Colombier 
8329a747e4fSDavid du Colombier 	USED(offset);
8339a747e4fSDavid du Colombier 
8343ff48bf5SDavid du Colombier 	switch(TYPE(c->qid)) {
8359a747e4fSDavid du Colombier 	case Qdir:
8363ff48bf5SDavid du Colombier 	case Qsubdir:
8373ff48bf5SDavid du Colombier 		return devdirread(c, a, n, 0, 0, tvgen);
8389a747e4fSDavid du Colombier 
8393ff48bf5SDavid du Colombier 	case Qvdata: {
8409a747e4fSDavid du Colombier 		int bpf, nb;
8419a747e4fSDavid du Colombier 
8423ff48bf5SDavid du Colombier 		tv = &tvs[DEV(c->qid)];
8433ff48bf5SDavid du Colombier 		bpf = ntsc_hactive * ntsc_vactive * getbitspp(tv) / 8;
8449a747e4fSDavid du Colombier 
8459a747e4fSDavid du Colombier 		if (offset >= bpf)
8469a747e4fSDavid du Colombier 			return 0;
8479a747e4fSDavid du Colombier 
8489a747e4fSDavid du Colombier 		nb = n;
8499a747e4fSDavid du Colombier 		if (offset + nb > bpf)
8509a747e4fSDavid du Colombier 			nb = bpf - offset;
8519a747e4fSDavid du Colombier 
8529a747e4fSDavid du Colombier 		ilock(tv);
8533ff48bf5SDavid du Colombier 		if (tv->frames == nil || tv->lvframe >= tv->nframes ||
8543ff48bf5SDavid du Colombier 			tv->frames[tv->lvframe].fbase == nil) {
8559a747e4fSDavid du Colombier 			iunlock(tv);
8569a747e4fSDavid du Colombier 			return 0;
8579a747e4fSDavid du Colombier 		}
8589a747e4fSDavid du Colombier 
8593ff48bf5SDavid du Colombier 		src = tv->frames[tv->lvframe].fbase;
8609a747e4fSDavid du Colombier 		incref(&tv->fref);
8619a747e4fSDavid du Colombier 		iunlock(tv);
8629a747e4fSDavid du Colombier 
8639a747e4fSDavid du Colombier 		memmove(a, src + offset, nb);
8649a747e4fSDavid du Colombier 		decref(&tv->fref);
8659a747e4fSDavid du Colombier 		return nb;
8669a747e4fSDavid du Colombier 	}
8679a747e4fSDavid du Colombier 
8683ff48bf5SDavid du Colombier 	case Qadata: {
869061a3f44SDavid du Colombier 		ulong uablock = (ulong)c->aux, bnum, tvablock;
8703ff48bf5SDavid du Colombier 		int boffs, nbytes;
8713ff48bf5SDavid du Colombier 
8723ff48bf5SDavid du Colombier 		tv = &tvs[DEV(c->qid)];
8733ff48bf5SDavid du Colombier 		if (tv->bt878 == nil)
8743ff48bf5SDavid du Colombier 			error("#V: No audio device");
8753ff48bf5SDavid du Colombier 		if (tv->absize == 0)
8763ff48bf5SDavid du Colombier 			error("#V: audio not initialized");
8773ff48bf5SDavid du Colombier 
8783ff48bf5SDavid du Colombier 		bnum = offset / tv->absize;
8793ff48bf5SDavid du Colombier 		boffs = offset % tv->absize;
8803ff48bf5SDavid du Colombier 		nbytes = tv->absize - boffs;
8813ff48bf5SDavid du Colombier 
8823ff48bf5SDavid du Colombier 		incref(&tv->aref);
88341dd6b47SDavid du Colombier 		for (;;) {
88441dd6b47SDavid du Colombier 			tvablock = tv->narblocks;	/* Current tv block. */
8853ff48bf5SDavid du Colombier 
8863ff48bf5SDavid du Colombier 			if (uablock == 0)
8873ff48bf5SDavid du Colombier 				uablock = tvablock - 1;
8883ff48bf5SDavid du Colombier 
8893ff48bf5SDavid du Colombier 			if (tvablock >= uablock + bnum + tv->narblocks)
8903ff48bf5SDavid du Colombier 				uablock = tvablock - 1 - bnum;
8913ff48bf5SDavid du Colombier 
8923ff48bf5SDavid du Colombier 			if (uablock + bnum == tvablock) {
8933ff48bf5SDavid du Colombier 				sleep(tv, audioblock, nil);
8943ff48bf5SDavid du Colombier 				continue;
8953ff48bf5SDavid du Colombier 			}
8963ff48bf5SDavid du Colombier 			break;
8973ff48bf5SDavid du Colombier 		}
8983ff48bf5SDavid du Colombier 
8993ff48bf5SDavid du Colombier 		print("uablock %ld, bnum %ld, boffs %d, nbytes %d, tvablock %ld\n",
9003ff48bf5SDavid du Colombier 			uablock, bnum, boffs, nbytes, tvablock);
9013ff48bf5SDavid du Colombier 		src = tv->abuf + ((uablock + bnum) % tv->nablocks) * tv->absize;
902*567483c8SDavid du Colombier 		print("copying from %#p (abuf %#p), nbytes %d (block %ld.%ld)\n",
9033ff48bf5SDavid du Colombier 			src + boffs, tv->abuf, nbytes, uablock, bnum);
9043ff48bf5SDavid du Colombier 
9053ff48bf5SDavid du Colombier 		memmove(a, src + boffs, nbytes);
9063ff48bf5SDavid du Colombier 		decref(&tv->aref);
9073ff48bf5SDavid du Colombier 
9083ff48bf5SDavid du Colombier 		uablock += (boffs + nbytes) % tv->absize;
909061a3f44SDavid du Colombier 		c->aux = (void*)uablock;
9103ff48bf5SDavid du Colombier 
9113ff48bf5SDavid du Colombier 		return nbytes;
9123ff48bf5SDavid du Colombier 	}
9133ff48bf5SDavid du Colombier 
9149a747e4fSDavid du Colombier 	case Qctl: {
9159a747e4fSDavid du Colombier 		char str[128];
9169a747e4fSDavid du Colombier 
9173ff48bf5SDavid du Colombier 		tv = &tvs[DEV(c->qid)];
9183ff48bf5SDavid du Colombier 		snprint(str, sizeof str, "%dx%dx%d %s channel %d %s\n",
9193ff48bf5SDavid du Colombier 			ntsc_hactive, ntsc_vactive, getbitspp(tv),
92041dd6b47SDavid du Colombier 			getcolormode(tv->cfmt), tv->channel, tv->ainfo);
9213ff48bf5SDavid du Colombier 		return readstr(offset, a, strlen(str) + 1, str);
9229a747e4fSDavid du Colombier 	}
9239a747e4fSDavid du Colombier 
9249a747e4fSDavid du Colombier 	case Qregs:
9259a747e4fSDavid du Colombier 		if (offset == 0) {
9263ff48bf5SDavid du Colombier 			Bt848 *bt848;
9279a747e4fSDavid du Colombier 			int i;
9289a747e4fSDavid du Colombier 
9293ff48bf5SDavid du Colombier 			tv = &tvs[DEV(c->qid)];
9303ff48bf5SDavid du Colombier 			bt848 = tv->bt848;
9313ff48bf5SDavid du Colombier 
9329a747e4fSDavid du Colombier 			e = regs + sizeof(regs);
9339a747e4fSDavid du Colombier 			p = regs;
9349a747e4fSDavid du Colombier 			for (i = 0; i < 0x300 >> 2; i++)
93541dd6b47SDavid du Colombier 				p = seprint(p, e, "%.3X %.8ulX\n", i << 2,
93641dd6b47SDavid du Colombier 					((ulong *)bt848)[i]);
9373ff48bf5SDavid du Colombier 			if (tv->bt878) {
9383ff48bf5SDavid du Colombier 				bt848 = tv->bt878;
9393ff48bf5SDavid du Colombier 
9403ff48bf5SDavid du Colombier 				for (i = 0; i < 0x300 >> 2; i++)
9413ff48bf5SDavid du Colombier 					p = seprint(p, e, "%.3X %.8ulX\n",
9423ff48bf5SDavid du Colombier 						i << 2, ((ulong *)bt848)[i]);
9433ff48bf5SDavid du Colombier 			}
9443ff48bf5SDavid du Colombier 
9459a747e4fSDavid du Colombier 			regslen = p - regs;
9469a747e4fSDavid du Colombier 		}
9479a747e4fSDavid du Colombier 
9489a747e4fSDavid du Colombier 		if (offset >= regslen)
9499a747e4fSDavid du Colombier 			return 0;
9509a747e4fSDavid du Colombier 		if (offset + n > regslen)
9519a747e4fSDavid du Colombier 			n = regslen - offset;
9529a747e4fSDavid du Colombier 
9539a747e4fSDavid du Colombier 		return readstr(offset, a, n, &regs[offset]);
9549a747e4fSDavid du Colombier 
9559a747e4fSDavid du Colombier 	default:
9569a747e4fSDavid du Colombier 		n = 0;
9579a747e4fSDavid du Colombier 		break;
9589a747e4fSDavid du Colombier 	}
9599a747e4fSDavid du Colombier 	return n;
9609a747e4fSDavid du Colombier }
9619a747e4fSDavid du Colombier 
9629a747e4fSDavid du Colombier static long
tvwrite(Chan * c,void * a,long n,vlong)9639a747e4fSDavid du Colombier tvwrite(Chan *c, void *a, long n, vlong)
9649a747e4fSDavid du Colombier {
9659a747e4fSDavid du Colombier 	Cmdbuf *cb;
9669a747e4fSDavid du Colombier 	Cmdtab *ct;
9673ff48bf5SDavid du Colombier 	Tv *tv;
9689a747e4fSDavid du Colombier 
9693ff48bf5SDavid du Colombier 	tv = &tvs[DEV(c->qid)];
9703ff48bf5SDavid du Colombier 	switch(TYPE(c->qid)) {
9719a747e4fSDavid du Colombier 	case Qctl:
9729a747e4fSDavid du Colombier 		cb = parsecmd(a, n);
9739a747e4fSDavid du Colombier 		if(waserror()){
9749a747e4fSDavid du Colombier 			free(cb);
9759a747e4fSDavid du Colombier 			nexterror();
9769a747e4fSDavid du Colombier 		}
9779a747e4fSDavid du Colombier 		ct = lookupcmd(cb, tvctlmsg, nelem(tvctlmsg));
9789a747e4fSDavid du Colombier 		switch (ct->index) {
9799a747e4fSDavid du Colombier 		case CMvstart:
9809a747e4fSDavid du Colombier 			vstart(tv, (int)strtol(cb->f[1], (char **)nil, 0),
9819a747e4fSDavid du Colombier 				ntsc_hactive, ntsc_vactive, ntsc_hactive);
9829a747e4fSDavid du Colombier 			break;
9839a747e4fSDavid du Colombier 
9843ff48bf5SDavid du Colombier 		case CMastart:
9853ff48bf5SDavid du Colombier 			astart(tv, cb->f[1], (uint)strtol(cb->f[2], (char **)nil, 0),
9863ff48bf5SDavid du Colombier 				(uint)strtol(cb->f[3], (char **)nil, 0),
9873ff48bf5SDavid du Colombier 				(uint)strtol(cb->f[4], (char **)nil, 0));
9883ff48bf5SDavid du Colombier 			break;
9893ff48bf5SDavid du Colombier 
9903ff48bf5SDavid du Colombier 		case CMastop:
9913ff48bf5SDavid du Colombier 			astop(tv);
9923ff48bf5SDavid du Colombier 			break;
9933ff48bf5SDavid du Colombier 
9949a747e4fSDavid du Colombier 		case CMvgastart:
9959a747e4fSDavid du Colombier 			vgastart(tv, strtoul(cb->f[1], (char **)nil, 0),
9969a747e4fSDavid du Colombier 				(int)strtoul(cb->f[2], (char **)nil, 0));
9979a747e4fSDavid du Colombier 			break;
9989a747e4fSDavid du Colombier 
9999a747e4fSDavid du Colombier 		case CMvstop:
10009a747e4fSDavid du Colombier 			vstop(tv);
10019a747e4fSDavid du Colombier 			break;
10029a747e4fSDavid du Colombier 
10039a747e4fSDavid du Colombier 		case CMchannel:
10049a747e4fSDavid du Colombier 			frequency(tv, (int)strtol(cb->f[1], (char **)nil, 0),
10059a747e4fSDavid du Colombier 				(int)strtol(cb->f[2], (char **)nil, 0));
10069a747e4fSDavid du Colombier 			break;
10073ff48bf5SDavid du Colombier 
10083ff48bf5SDavid du Colombier 		case CMcolormode:
10093ff48bf5SDavid du Colombier 			colormode(tv, cb->f[1]);
10103ff48bf5SDavid du Colombier 			break;
10113ff48bf5SDavid du Colombier 
10123ff48bf5SDavid du Colombier 		case CMvolume:
10133ff48bf5SDavid du Colombier 			if (!tv->msp)
10143ff48bf5SDavid du Colombier 				error("#V: No volume control");
10153ff48bf5SDavid du Colombier 
10163ff48bf5SDavid du Colombier 			mspvolume(tv, 0, (int)strtol(cb->f[1], (char **)nil, 0),
10173ff48bf5SDavid du Colombier 				(int)strtol(cb->f[2], (char **)nil, 0));
10183ff48bf5SDavid du Colombier 			break;
10193ff48bf5SDavid du Colombier 
10203ff48bf5SDavid du Colombier 		case CMmute:
10213ff48bf5SDavid du Colombier 			if (!tv->msp)
10223ff48bf5SDavid du Colombier 				error("#V: No volume control");
10233ff48bf5SDavid du Colombier 
10243ff48bf5SDavid du Colombier 			mspvolume(tv, 1, 0, 0);
10253ff48bf5SDavid du Colombier 			break;
10269a747e4fSDavid du Colombier 		}
10279a747e4fSDavid du Colombier 		poperror();
10283ff48bf5SDavid du Colombier 		free(cb);
10299a747e4fSDavid du Colombier 		break;
10309a747e4fSDavid du Colombier 
10319a747e4fSDavid du Colombier 	default:
10329a747e4fSDavid du Colombier 		error(Eio);
10339a747e4fSDavid du Colombier 	}
10349a747e4fSDavid du Colombier 	return n;
10359a747e4fSDavid du Colombier }
10369a747e4fSDavid du Colombier 
10379a747e4fSDavid du Colombier Dev tvdevtab = {
10389a747e4fSDavid du Colombier 	'V',
10399a747e4fSDavid du Colombier 	"tv",
10409a747e4fSDavid du Colombier 
10419a747e4fSDavid du Colombier 	devreset,
10429a747e4fSDavid du Colombier 	tvinit,
10439a747e4fSDavid du Colombier 	devshutdown,
10449a747e4fSDavid du Colombier 	tvattach,
10459a747e4fSDavid du Colombier 	tvwalk,
10469a747e4fSDavid du Colombier 	tvstat,
10479a747e4fSDavid du Colombier 	tvopen,
10489a747e4fSDavid du Colombier 	devcreate,
10499a747e4fSDavid du Colombier 	tvclose,
10509a747e4fSDavid du Colombier 	tvread,
10519a747e4fSDavid du Colombier 	devbread,
10529a747e4fSDavid du Colombier 	tvwrite,
10539a747e4fSDavid du Colombier 	devbwrite,
10549a747e4fSDavid du Colombier 	devremove,
10559a747e4fSDavid du Colombier 	devwstat,
10569a747e4fSDavid du Colombier };
10579a747e4fSDavid du Colombier 
10589a747e4fSDavid du Colombier static void
tvinterrupt(Ureg *,Tv * tv)10599a747e4fSDavid du Colombier tvinterrupt(Ureg *, Tv *tv)
10609a747e4fSDavid du Colombier {
106141dd6b47SDavid du Colombier 	Bt848 *bt848 = tv->bt848, *bt878 = tv->bt878;
10629a747e4fSDavid du Colombier 
106341dd6b47SDavid du Colombier 	for (;;) {
10643ff48bf5SDavid du Colombier 		ulong vstat, astat;
10659a747e4fSDavid du Colombier 		uchar fnum;
10669a747e4fSDavid du Colombier 
10673ff48bf5SDavid du Colombier 		vstat = bt848->intstat;
10683ff48bf5SDavid du Colombier 		fnum = (vstat >> intstat_riscstatshift) & 0xf;
10693ff48bf5SDavid du Colombier 		vstat &= bt848->intmask;
10709a747e4fSDavid du Colombier 
10713ff48bf5SDavid du Colombier 		if (bt878)
10723ff48bf5SDavid du Colombier 			astat = bt878->intstat & bt878->intmask;
10733ff48bf5SDavid du Colombier 		else
10743ff48bf5SDavid du Colombier 			astat = 0;
10753ff48bf5SDavid du Colombier 
10763ff48bf5SDavid du Colombier 		if (vstat == 0 && astat == 0)
10779a747e4fSDavid du Colombier 			break;
10789a747e4fSDavid du Colombier 
10793ff48bf5SDavid du Colombier 		if (astat)
10803ff48bf5SDavid du Colombier 			print("vstat %.8luX, astat %.8luX\n", vstat, astat);
10819a747e4fSDavid du Colombier 
10823ff48bf5SDavid du Colombier 		bt848->intstat = vstat;
10833ff48bf5SDavid du Colombier 		if (bt878)
10843ff48bf5SDavid du Colombier 			bt878->intstat = astat;
10853ff48bf5SDavid du Colombier 
10863ff48bf5SDavid du Colombier 		if ((vstat & intstat_fmtchg) == intstat_fmtchg) {
10879a747e4fSDavid du Colombier 			iprint("int: fmtchg\n");
10883ff48bf5SDavid du Colombier 			vstat &= ~intstat_fmtchg;
10899a747e4fSDavid du Colombier 		}
10909a747e4fSDavid du Colombier 
10913ff48bf5SDavid du Colombier 		if ((vstat & intstat_vpress) == intstat_vpress) {
10923ff48bf5SDavid du Colombier //			iprint("int: vpress\n");
10933ff48bf5SDavid du Colombier 			vstat &= ~intstat_vpress;
10949a747e4fSDavid du Colombier 		}
10959a747e4fSDavid du Colombier 
109641dd6b47SDavid du Colombier 		if ((vstat & intstat_vsync) == intstat_vsync)
10973ff48bf5SDavid du Colombier 			vstat &= ~intstat_vsync;
10989a747e4fSDavid du Colombier 
10993ff48bf5SDavid du Colombier 		if ((vstat & intstat_scerr) == intstat_scerr) {
11009a747e4fSDavid du Colombier 			iprint("int: scerr\n");
11019a747e4fSDavid du Colombier 			bt848->gpiodmactl &=
11029a747e4fSDavid du Colombier 				~(gpiodmactl_riscenable|gpiodmactl_fifoenable);
11039a747e4fSDavid du Colombier 			bt848->gpiodmactl |= gpiodmactl_fifoenable;
11049a747e4fSDavid du Colombier 			bt848->gpiodmactl |= gpiodmactl_riscenable;
11053ff48bf5SDavid du Colombier 			vstat &= ~intstat_scerr;
11069a747e4fSDavid du Colombier 		}
11079a747e4fSDavid du Colombier 
11083ff48bf5SDavid du Colombier 		if ((vstat & intstat_risci) == intstat_risci) {
11093ff48bf5SDavid du Colombier 			tv->lvframe = fnum;
11103ff48bf5SDavid du Colombier 			vstat &= ~intstat_risci;
11119a747e4fSDavid du Colombier 		}
11129a747e4fSDavid du Colombier 
11133ff48bf5SDavid du Colombier 		if ((vstat & intstat_ocerr) == intstat_ocerr) {
11149a747e4fSDavid du Colombier 			iprint("int: ocerr\n");
11153ff48bf5SDavid du Colombier 			vstat &= ~intstat_ocerr;
11169a747e4fSDavid du Colombier 		}
11179a747e4fSDavid du Colombier 
11183ff48bf5SDavid du Colombier 		if ((vstat & intstat_fbus) == intstat_fbus) {
11199a747e4fSDavid du Colombier 			iprint("int: fbus\n");
11203ff48bf5SDavid du Colombier 			vstat &= ~intstat_fbus;
11219a747e4fSDavid du Colombier 		}
11229a747e4fSDavid du Colombier 
11233ff48bf5SDavid du Colombier 		if (vstat)
11243ff48bf5SDavid du Colombier 			iprint("int: (v) ignored interrupts %.8ulX\n", vstat);
11253ff48bf5SDavid du Colombier 
11263ff48bf5SDavid du Colombier 		if ((astat & intstat_risci) == intstat_risci) {
11273ff48bf5SDavid du Colombier 			tv->narblocks++;
11283ff48bf5SDavid du Colombier 			if ((tv->narblocks % 100) == 0)
11293ff48bf5SDavid du Colombier 				print("a");
11303ff48bf5SDavid du Colombier 			wakeup(tv);
11313ff48bf5SDavid du Colombier 			astat &= ~intstat_risci;
11323ff48bf5SDavid du Colombier 		}
11333ff48bf5SDavid du Colombier 
11343ff48bf5SDavid du Colombier 		if ((astat & intstat_fdsr) == intstat_fdsr) {
11353ff48bf5SDavid du Colombier 			iprint("int: (a) fdsr\n");
11363ff48bf5SDavid du Colombier 			bt848->gpiodmactl &=
11373ff48bf5SDavid du Colombier 				~(gpiodmactl_acapenable |
11383ff48bf5SDavid du Colombier 					gpiodmactl_riscenable | gpiodmactl_fifoenable);
11393ff48bf5SDavid du Colombier 			astat &= ~intstat_fdsr;
11403ff48bf5SDavid du Colombier 		}
11413ff48bf5SDavid du Colombier 
11423ff48bf5SDavid du Colombier 		if (astat)
11433ff48bf5SDavid du Colombier 			iprint("int: (a) ignored interrupts %.8ulX\n", astat);
11449a747e4fSDavid du Colombier 	}
11459a747e4fSDavid du Colombier }
11469a747e4fSDavid du Colombier 
11479a747e4fSDavid du Colombier static int
i2cread(Tv * tv,uchar off,uchar * v)11489a747e4fSDavid du Colombier i2cread(Tv *tv, uchar off, uchar *v)
11499a747e4fSDavid du Colombier {
11509a747e4fSDavid du Colombier 	Bt848 *bt848 = tv->bt848;
11519a747e4fSDavid du Colombier 	ulong intstat;
11529a747e4fSDavid du Colombier 	int i;
11539a747e4fSDavid du Colombier 
11549a747e4fSDavid du Colombier 	bt848->intstat	= intstat_i2cdone;
11559a747e4fSDavid du Colombier 	bt848->i2c = (off << 24) | tv->i2ccmd;
11569a747e4fSDavid du Colombier 
11579a747e4fSDavid du Colombier 	intstat = -1;
11589a747e4fSDavid du Colombier 	for (i = 0; i != 1000; i++) {
11599a747e4fSDavid du Colombier 		if ((intstat = bt848->intstat) & intstat_i2cdone)
11609a747e4fSDavid du Colombier 			break;
11619a747e4fSDavid du Colombier 		microdelay(1000);
11629a747e4fSDavid du Colombier 	}
11639a747e4fSDavid du Colombier 
11649a747e4fSDavid du Colombier 	if (i == 1000) {
11659a747e4fSDavid du Colombier 		print("i2cread: timeout\n");
11669a747e4fSDavid du Colombier 		return 0;
11679a747e4fSDavid du Colombier 	}
11689a747e4fSDavid du Colombier 
11699a747e4fSDavid du Colombier 	if ((intstat & intstat_i2crack) == 0)
11709a747e4fSDavid du Colombier 		return 0;
11719a747e4fSDavid du Colombier 
11729a747e4fSDavid du Colombier 	*v = bt848->i2c >> 8;
11739a747e4fSDavid du Colombier 	return 1;
11749a747e4fSDavid du Colombier }
11759a747e4fSDavid du Colombier 
11769a747e4fSDavid du Colombier static int
i2cwrite(Tv * tv,uchar addr,uchar sub,uchar data,int both)11773ff48bf5SDavid du Colombier i2cwrite(Tv *tv, uchar addr, uchar sub, uchar data, int both)
11789a747e4fSDavid du Colombier {
11799a747e4fSDavid du Colombier 	Bt848 *bt848 = tv->bt848;
11803ff48bf5SDavid du Colombier 	ulong intstat, d;
11819a747e4fSDavid du Colombier 	int i;
11829a747e4fSDavid du Colombier 
11839a747e4fSDavid du Colombier 	bt848->intstat	= intstat_i2cdone;
11843ff48bf5SDavid du Colombier 	d = (addr << 24) | (sub << 16) | tv->i2ccmd;
11859a747e4fSDavid du Colombier 	if (both)
11863ff48bf5SDavid du Colombier 		d |= (data << 8) | i2c_bt848w3b;
11873ff48bf5SDavid du Colombier 	bt848->i2c = d;
11889a747e4fSDavid du Colombier 
11899a747e4fSDavid du Colombier 	intstat = 0;
11909a747e4fSDavid du Colombier 	for (i = 0; i != 1000; i++) {
11919a747e4fSDavid du Colombier 		if ((intstat = bt848->intstat) & intstat_i2cdone)
11929a747e4fSDavid du Colombier 			break;
11939a747e4fSDavid du Colombier 		microdelay(1000);
11949a747e4fSDavid du Colombier 	}
11959a747e4fSDavid du Colombier 
11963ff48bf5SDavid du Colombier 	if (i == i2c_timeout) {
11973ff48bf5SDavid du Colombier 		print("i2cwrite: timeout\n");
11989a747e4fSDavid du Colombier 		return 0;
11999a747e4fSDavid du Colombier 	}
12009a747e4fSDavid du Colombier 
12019a747e4fSDavid du Colombier 	if ((intstat & intstat_i2crack) == 0)
12029a747e4fSDavid du Colombier 		return 0;
12039a747e4fSDavid du Colombier 
12049a747e4fSDavid du Colombier 	return 1;
12059a747e4fSDavid du Colombier }
12069a747e4fSDavid du Colombier 
12079a747e4fSDavid du Colombier static ulong *
riscpacked(ulong pa,int fnum,int w,int h,int stride,ulong ** lastjmp)12084de34a7eSDavid du Colombier riscpacked(ulong pa, int fnum, int w, int h, int stride, ulong **lastjmp)
12099a747e4fSDavid du Colombier {
12109a747e4fSDavid du Colombier 	ulong *p, *pbase;
12119a747e4fSDavid du Colombier 	int i;
12129a747e4fSDavid du Colombier 
12139a747e4fSDavid du Colombier 	pbase = p = (ulong *)malloc((h + 6) * 2 * sizeof(ulong));
12149a747e4fSDavid du Colombier 	assert(p);
12159a747e4fSDavid du Colombier 
12169a747e4fSDavid du Colombier 	assert(w <= 0x7FF);
12179a747e4fSDavid du Colombier 
12189a747e4fSDavid du Colombier 	*p++ = riscsync | riscsync_resync | riscsync_vre;
12199a747e4fSDavid du Colombier 	*p++ = 0;
12209a747e4fSDavid du Colombier 
12219a747e4fSDavid du Colombier 	*p++ = riscsync | riscsync_fm1;
12229a747e4fSDavid du Colombier 	*p++ = 0;
12239a747e4fSDavid du Colombier 
12249a747e4fSDavid du Colombier 	for (i = 0; i != h / 2; i++) {
12259a747e4fSDavid du Colombier 		*p++ = riscwrite | w | riscwrite_sol | riscwrite_eol;
12264de34a7eSDavid du Colombier 		*p++ = pa + i * 2 * stride;
12279a747e4fSDavid du Colombier 	}
12289a747e4fSDavid du Colombier 
12299a747e4fSDavid du Colombier 	*p++ = riscsync | riscsync_resync | riscsync_vro;
12309a747e4fSDavid du Colombier 	*p++ = 0;
12319a747e4fSDavid du Colombier 
12329a747e4fSDavid du Colombier 	*p++ = riscsync | riscsync_fm1;
12339a747e4fSDavid du Colombier 	*p++ = 0;
12349a747e4fSDavid du Colombier 
12359a747e4fSDavid du Colombier 	for (i = 0; i != h / 2; i++) {
12369a747e4fSDavid du Colombier 		*p++ = riscwrite | w | riscwrite_sol | riscwrite_eol;
12374de34a7eSDavid du Colombier 		*p++ = pa + (i * 2 + 1) * stride;
12389a747e4fSDavid du Colombier 	}
12399a747e4fSDavid du Colombier 
124041dd6b47SDavid du Colombier 	/* reset status.  you really need two instructions ;-(. */
12419a747e4fSDavid du Colombier 	*p++ = riscjmp | (0xf << risclabelshift_reset);
12429a747e4fSDavid du Colombier 	*p++ = PADDR(p);
12439a747e4fSDavid du Colombier 	*p++ = riscjmp | riscirq | (fnum << risclabelshift_set);
12449a747e4fSDavid du Colombier 	*lastjmp = p;
12459a747e4fSDavid du Colombier 
12469a747e4fSDavid du Colombier 	return pbase;
12479a747e4fSDavid du Colombier }
12489a747e4fSDavid du Colombier 
12493ff48bf5SDavid du Colombier static ulong *
riscplanar411(ulong pa,int fnum,int w,int h,ulong ** lastjmp)12504de34a7eSDavid du Colombier riscplanar411(ulong pa, int fnum, int w, int h, ulong **lastjmp)
12513ff48bf5SDavid du Colombier {
12523ff48bf5SDavid du Colombier 	ulong *p, *pbase, Cw, Yw, Ch;
12533ff48bf5SDavid du Colombier 	uchar *Ybase, *Cbbase, *Crbase;
12543ff48bf5SDavid du Colombier 	int i, bitspp;
12553ff48bf5SDavid du Colombier 
12563ff48bf5SDavid du Colombier 	bitspp = 6;
12573ff48bf5SDavid du Colombier 	assert(w * bitspp / 8 <= 0x7FF);
12583ff48bf5SDavid du Colombier 	pbase = p = (ulong *)malloc((h + 6) * 5 * sizeof(ulong));
12593ff48bf5SDavid du Colombier 	assert(p);
12603ff48bf5SDavid du Colombier 
12613ff48bf5SDavid du Colombier 	Yw = w;
12624de34a7eSDavid du Colombier 	Ybase = (uchar *)pa;
12633ff48bf5SDavid du Colombier 	Cw = w >> 1;
12643ff48bf5SDavid du Colombier 	Ch = h >> 1;
12653ff48bf5SDavid du Colombier 	Cbbase = Ybase + Yw * h;
12663ff48bf5SDavid du Colombier 	Crbase = Cbbase + Cw * Ch;
12673ff48bf5SDavid du Colombier 
12683ff48bf5SDavid du Colombier 	*p++ = riscsync | riscsync_resync | riscsync_vre;
12693ff48bf5SDavid du Colombier 	*p++ = 0;
12703ff48bf5SDavid du Colombier 
12713ff48bf5SDavid du Colombier 	*p++ = riscsync | riscsync_fm3;
12723ff48bf5SDavid du Colombier 	*p++ = 0;
12733ff48bf5SDavid du Colombier 
12743ff48bf5SDavid du Colombier 	for (i = 0; i != h / 2; i++) {
12753ff48bf5SDavid du Colombier 		*p++ = riscwrite123 | Yw | riscwrite_sol | riscwrite_eol;
12763ff48bf5SDavid du Colombier 		*p++ = (Cw << 16) | Cw;
12773ff48bf5SDavid du Colombier 		*p++ = (ulong)(Ybase + i * 2 * Yw);
127841dd6b47SDavid du Colombier 		*p++ = (ulong)(Cbbase + i * Cw);	/* Do not interlace */
12793ff48bf5SDavid du Colombier 		*p++ = (ulong)(Crbase + i * Cw);
12803ff48bf5SDavid du Colombier 	}
12813ff48bf5SDavid du Colombier 
12823ff48bf5SDavid du Colombier 	*p++ = riscsync | riscsync_resync | riscsync_vro;
12833ff48bf5SDavid du Colombier 	*p++ = 0;
12843ff48bf5SDavid du Colombier 
12853ff48bf5SDavid du Colombier 	*p++ = riscsync | riscsync_fm3;
12863ff48bf5SDavid du Colombier 	*p++ = 0;
12873ff48bf5SDavid du Colombier 
12883ff48bf5SDavid du Colombier 	for (i = 0; i != h / 2; i++) {
12893ff48bf5SDavid du Colombier 		*p++ = riscwrite1s23 | Yw | riscwrite_sol | riscwrite_eol;
12903ff48bf5SDavid du Colombier 		*p++ = (Cw << 16) | Cw;
12913ff48bf5SDavid du Colombier 		*p++ = (ulong)(Ybase + (i * 2 + 1) * Yw);
12923ff48bf5SDavid du Colombier 	}
12933ff48bf5SDavid du Colombier 
129441dd6b47SDavid du Colombier 	/* reset status.  you really need two instructions ;-(. */
12953ff48bf5SDavid du Colombier 	*p++ = riscjmp | (0xf << risclabelshift_reset);
12963ff48bf5SDavid du Colombier 	*p++ = PADDR(p);
12973ff48bf5SDavid du Colombier 	*p++ = riscjmp | riscirq | (fnum << risclabelshift_set);
12983ff48bf5SDavid du Colombier 	*lastjmp = p;
12993ff48bf5SDavid du Colombier 
13003ff48bf5SDavid du Colombier 	return pbase;
13013ff48bf5SDavid du Colombier }
13023ff48bf5SDavid du Colombier 
13033ff48bf5SDavid du Colombier static ulong *
riscplanar422(ulong pa,int fnum,int w,int h,ulong ** lastjmp)13044de34a7eSDavid du Colombier riscplanar422(ulong pa, int fnum, int w, int h, ulong **lastjmp)
13053ff48bf5SDavid du Colombier {
13063ff48bf5SDavid du Colombier 	ulong *p, *pbase, Cw, Yw;
13073ff48bf5SDavid du Colombier 	uchar *Ybase, *Cbbase, *Crbase;
13083ff48bf5SDavid du Colombier 	int i, bpp;
13093ff48bf5SDavid du Colombier 
13103ff48bf5SDavid du Colombier 	bpp = 2;
13113ff48bf5SDavid du Colombier 	assert(w * bpp <= 0x7FF);
13123ff48bf5SDavid du Colombier 	pbase = p = (ulong *)malloc((h + 6) * 5 * sizeof(ulong));
13133ff48bf5SDavid du Colombier 	assert(p);
13143ff48bf5SDavid du Colombier 
13153ff48bf5SDavid du Colombier 	Yw = w;
13164de34a7eSDavid du Colombier 	Ybase = (uchar *)pa;
13173ff48bf5SDavid du Colombier 	Cw = w >> 1;
13183ff48bf5SDavid du Colombier 	Cbbase = Ybase + Yw * h;
13193ff48bf5SDavid du Colombier 	Crbase = Cbbase + Cw * h;
13203ff48bf5SDavid du Colombier 
13213ff48bf5SDavid du Colombier 	*p++ = riscsync | riscsync_resync | riscsync_vre;
13223ff48bf5SDavid du Colombier 	*p++ = 0;
13233ff48bf5SDavid du Colombier 
13243ff48bf5SDavid du Colombier 	*p++ = riscsync | riscsync_fm3;
13253ff48bf5SDavid du Colombier 	*p++ = 0;
13263ff48bf5SDavid du Colombier 
13273ff48bf5SDavid du Colombier 	for (i = 0; i != h / 2; i++) {
13283ff48bf5SDavid du Colombier 		*p++ = riscwrite123 | Yw | riscwrite_sol | riscwrite_eol;
13293ff48bf5SDavid du Colombier 		*p++ = (Cw << 16) | Cw;
13303ff48bf5SDavid du Colombier 		*p++ = (ulong)(Ybase + i * 2 * Yw);
13313ff48bf5SDavid du Colombier 		*p++ = (ulong)(Cbbase + i * 2 * Cw);
13323ff48bf5SDavid du Colombier 		*p++ = (ulong)(Crbase + i * 2 * Cw);
13333ff48bf5SDavid du Colombier 	}
13343ff48bf5SDavid du Colombier 
13353ff48bf5SDavid du Colombier 	*p++ = riscsync | riscsync_resync | riscsync_vro;
13363ff48bf5SDavid du Colombier 	*p++ = 0;
13373ff48bf5SDavid du Colombier 
13383ff48bf5SDavid du Colombier 	*p++ = riscsync | riscsync_fm3;
13393ff48bf5SDavid du Colombier 	*p++ = 0;
13403ff48bf5SDavid du Colombier 
13413ff48bf5SDavid du Colombier 	for (i = 0; i != h / 2; i++) {
13423ff48bf5SDavid du Colombier 		*p++ = riscwrite123 | Yw | riscwrite_sol | riscwrite_eol;
13433ff48bf5SDavid du Colombier 		*p++ = (Cw << 16) | Cw;
13443ff48bf5SDavid du Colombier 		*p++ = (ulong)(Ybase + (i * 2 + 1) * Yw);
13453ff48bf5SDavid du Colombier 		*p++ = (ulong)(Cbbase + (i * 2 + 1) * Cw);
13463ff48bf5SDavid du Colombier 		*p++ = (ulong)(Crbase + (i * 2 + 1) * Cw);
13473ff48bf5SDavid du Colombier 	}
13483ff48bf5SDavid du Colombier 
134941dd6b47SDavid du Colombier 	/* reset status.  you really need two instructions ;-(. */
13503ff48bf5SDavid du Colombier 	*p++ = riscjmp | (0xf << risclabelshift_reset);
13513ff48bf5SDavid du Colombier 	*p++ = PADDR(p);
13523ff48bf5SDavid du Colombier 	*p++ = riscjmp | riscirq | (fnum << risclabelshift_set);
13533ff48bf5SDavid du Colombier 	*lastjmp = p;
13543ff48bf5SDavid du Colombier 
13553ff48bf5SDavid du Colombier 	return pbase;
13563ff48bf5SDavid du Colombier }
13573ff48bf5SDavid du Colombier 
13583ff48bf5SDavid du Colombier static ulong *
riscaudio(ulong pa,int nblocks,int bsize)13594de34a7eSDavid du Colombier riscaudio(ulong pa, int nblocks, int bsize)
13603ff48bf5SDavid du Colombier {
13613ff48bf5SDavid du Colombier 	ulong *p, *pbase;
13623ff48bf5SDavid du Colombier 	int i;
13633ff48bf5SDavid du Colombier 
13643ff48bf5SDavid du Colombier 	pbase = p = (ulong *)malloc((nblocks + 3) * 2 * sizeof(ulong));
13653ff48bf5SDavid du Colombier 	assert(p);
13663ff48bf5SDavid du Colombier 
13673ff48bf5SDavid du Colombier 	*p++ = riscsync|riscsync_fm1;
13683ff48bf5SDavid du Colombier 	*p++ = 0;
13693ff48bf5SDavid du Colombier 
13703ff48bf5SDavid du Colombier 	for (i = 0; i != nblocks; i++) {
13713ff48bf5SDavid du Colombier 		*p++ = riscwrite | riscwrite_sol | riscwrite_eol | bsize | riscirq |
13723ff48bf5SDavid du Colombier 			((i & 0xf) << risclabelshift_set) |
13733ff48bf5SDavid du Colombier 			((~i & 0xf) << risclabelshift_reset);
13744de34a7eSDavid du Colombier 		*p++ = pa + i * bsize;
13753ff48bf5SDavid du Colombier 	}
13763ff48bf5SDavid du Colombier 
13773ff48bf5SDavid du Colombier 	*p++ = riscsync | riscsync_vro;
13783ff48bf5SDavid du Colombier 	*p++ = 0;
13793ff48bf5SDavid du Colombier 	*p++ = riscjmp;
13803ff48bf5SDavid du Colombier 	*p++ = PADDR(pbase);
13813ff48bf5SDavid du Colombier 	USED(p);
13823ff48bf5SDavid du Colombier 
13833ff48bf5SDavid du Colombier 	return pbase;
13843ff48bf5SDavid du Colombier }
13853ff48bf5SDavid du Colombier 
13863ff48bf5SDavid du Colombier 
13879a747e4fSDavid du Colombier static void
vactivate(Tv * tv,Frame * frames,int nframes)13889a747e4fSDavid du Colombier vactivate(Tv *tv, Frame *frames, int nframes)
13899a747e4fSDavid du Colombier {
13909a747e4fSDavid du Colombier 	Bt848 *bt848 = tv->bt848;
13919a747e4fSDavid du Colombier 
13929a747e4fSDavid du Colombier 	ilock(tv);
13939a747e4fSDavid du Colombier 	if (tv->frames) {
13949a747e4fSDavid du Colombier 		iunlock(tv);
13959a747e4fSDavid du Colombier 		error(Einuse);
13969a747e4fSDavid du Colombier 	}
13979a747e4fSDavid du Colombier 	poperror();
13989a747e4fSDavid du Colombier 
13999a747e4fSDavid du Colombier 	tv->frames = frames;
14009a747e4fSDavid du Colombier 	tv->nframes = nframes;
14019a747e4fSDavid du Colombier 
14029a747e4fSDavid du Colombier 	bt848->riscstrtadd = PADDR(tv->frames[0].fstart);
14039a747e4fSDavid du Colombier 	bt848->capctl |= capctl_captureodd|capctl_captureeven;
14049a747e4fSDavid du Colombier 	bt848->gpiodmactl |= gpiodmactl_fifoenable;
14059a747e4fSDavid du Colombier 	bt848->gpiodmactl |= gpiodmactl_riscenable;
14069a747e4fSDavid du Colombier 
14079a747e4fSDavid du Colombier 	iunlock(tv);
14089a747e4fSDavid du Colombier }
14099a747e4fSDavid du Colombier 
14109a747e4fSDavid du Colombier static void
vstart(Tv * tv,int nframes,int w,int h,int stride)14119a747e4fSDavid du Colombier vstart(Tv *tv, int nframes, int w, int h, int stride)
14129a747e4fSDavid du Colombier {
14139a747e4fSDavid du Colombier 	Frame *frames;
14143ff48bf5SDavid du Colombier 	int bitspp, i, bpf;
14159a747e4fSDavid du Colombier 
14169a747e4fSDavid du Colombier 	if (nframes >= 0x10)
14179a747e4fSDavid du Colombier 		error(Ebadarg);
14189a747e4fSDavid du Colombier 
14193ff48bf5SDavid du Colombier 	bitspp = getbitspp(tv);
14203ff48bf5SDavid du Colombier 	bpf = w * h * bitspp / 8;
14219a747e4fSDavid du Colombier 
142241dd6b47SDavid du Colombier 	/* Add one as a spare. */
14239a747e4fSDavid du Colombier 	frames = (Frame *)malloc(nframes * sizeof(Frame));
14249a747e4fSDavid du Colombier 	assert(frames);
14259a747e4fSDavid du Colombier 	if (waserror()) {
14269a747e4fSDavid du Colombier 		for (i = 0; i != nframes; i++)
14279a747e4fSDavid du Colombier 			if (frames[i].fbase)
14289a747e4fSDavid du Colombier 				free(frames[i].fbase);
14299a747e4fSDavid du Colombier 		free(frames);
14309a747e4fSDavid du Colombier 		nexterror();
14319a747e4fSDavid du Colombier 	}
14329a747e4fSDavid du Colombier 	memset(frames, 0, nframes * sizeof(Frame));
14339a747e4fSDavid du Colombier 
14349a747e4fSDavid du Colombier 	for (i = 0; i != nframes; i++) {
14359a747e4fSDavid du Colombier 		if ((frames[i].fbase = (uchar *)malloc(bpf)) == nil)
14369a747e4fSDavid du Colombier 			error(Enomem);
14379a747e4fSDavid du Colombier 
14383ff48bf5SDavid du Colombier 		switch (tv->cfmt) {
14393ff48bf5SDavid du Colombier 		case colorfmt_YCbCr422:
144041dd6b47SDavid du Colombier 			frames[i].fstart = riscplanar422(PADDR(frames[i].fbase),				i, w, h, &frames[i].fjmp);
14413ff48bf5SDavid du Colombier 			break;
14423ff48bf5SDavid du Colombier 		case colorfmt_YCbCr411:
144341dd6b47SDavid du Colombier 			frames[i].fstart = riscplanar411(PADDR(frames[i].fbase),
144441dd6b47SDavid du Colombier 				i, w, h, &frames[i].fjmp);
14453ff48bf5SDavid du Colombier 			break;
14463ff48bf5SDavid du Colombier 		case colorfmt_rgb16:
14473ff48bf5SDavid du Colombier 			frames[i].fstart = riscpacked(PADDR(frames[i].fbase), i,
14483ff48bf5SDavid du Colombier 				w * bitspp / 8, h, stride * bitspp / 8,
14499a747e4fSDavid du Colombier 				&frames[i].fjmp);
14503ff48bf5SDavid du Colombier 			break;
14513ff48bf5SDavid du Colombier 		default:
14523ff48bf5SDavid du Colombier 			panic("vstart: Unsupport colorformat\n");
14533ff48bf5SDavid du Colombier 		}
14549a747e4fSDavid du Colombier 	}
14559a747e4fSDavid du Colombier 
14569a747e4fSDavid du Colombier 	for (i = 0; i != nframes; i++)
145741dd6b47SDavid du Colombier 		*frames[i].fjmp = PADDR(i == nframes - 1? frames[0].fstart:
145841dd6b47SDavid du Colombier 			frames[i + 1].fstart);
14599a747e4fSDavid du Colombier 
14609a747e4fSDavid du Colombier 	vactivate(tv, frames, nframes);
14619a747e4fSDavid du Colombier }
14629a747e4fSDavid du Colombier 
14639a747e4fSDavid du Colombier static void
astart(Tv * tv,char * input,uint rate,uint nab,uint nasz)14643ff48bf5SDavid du Colombier astart(Tv *tv, char *input, uint rate, uint nab, uint nasz)
14653ff48bf5SDavid du Colombier {
14663ff48bf5SDavid du Colombier 	Bt848 *bt878 = tv->bt878;
14673ff48bf5SDavid du Colombier 	ulong *arisc;
14683ff48bf5SDavid du Colombier 	int selector;
14693ff48bf5SDavid du Colombier 	uchar *abuf;
14703ff48bf5SDavid du Colombier 	int s, d;
14713ff48bf5SDavid du Colombier 
14723ff48bf5SDavid du Colombier 	if (bt878 == nil || tv->amux == nil)
14733ff48bf5SDavid du Colombier 		error("#V: Card does not support audio");
14743ff48bf5SDavid du Colombier 
14753ff48bf5SDavid du Colombier 	selector = 0;
14763ff48bf5SDavid du Colombier 	if (!strcmp(input, "tv"))
14773ff48bf5SDavid du Colombier 		selector = asel_tv;
14783ff48bf5SDavid du Colombier 	else if (!strcmp(input, "radio"))
14793ff48bf5SDavid du Colombier 		selector = asel_radio;
14803ff48bf5SDavid du Colombier 	else if (!strcmp(input, "mic"))
14813ff48bf5SDavid du Colombier 		selector = asel_mic;
14823ff48bf5SDavid du Colombier 	else if (!strcmp(input, "smxc"))
14833ff48bf5SDavid du Colombier 		selector = asel_smxc;
14843ff48bf5SDavid du Colombier 	else
14853ff48bf5SDavid du Colombier 		error("#V: Invalid input");
14863ff48bf5SDavid du Colombier 
14873ff48bf5SDavid du Colombier 	if (nasz > 0xfff)
14883ff48bf5SDavid du Colombier 		error("#V: Audio block size too big (max 0xfff)");
14893ff48bf5SDavid du Colombier 
14903ff48bf5SDavid du Colombier 	abuf = (uchar *)malloc(nab * nasz * sizeof(uchar));
14913ff48bf5SDavid du Colombier 	assert(abuf);
14923ff48bf5SDavid du Colombier 	arisc = riscaudio(PADDR(abuf), nab, nasz);
14933ff48bf5SDavid du Colombier 
14943ff48bf5SDavid du Colombier 	ilock(tv);
14953ff48bf5SDavid du Colombier 	if (tv->arisc) {
14963ff48bf5SDavid du Colombier 		iunlock(tv);
14973ff48bf5SDavid du Colombier 		free(abuf);
14983ff48bf5SDavid du Colombier 		free(arisc);
14993ff48bf5SDavid du Colombier 		error(Einuse);
15003ff48bf5SDavid du Colombier 	}
15013ff48bf5SDavid du Colombier 
15023ff48bf5SDavid du Colombier 	tv->arisc = arisc;
15033ff48bf5SDavid du Colombier 	tv->abuf = abuf;
15043ff48bf5SDavid du Colombier 	tv->nablocks = nab;
15053ff48bf5SDavid du Colombier 	tv->absize = nasz;
15063ff48bf5SDavid du Colombier 
15073ff48bf5SDavid du Colombier 	bt878->riscstrtadd = PADDR(tv->arisc);
15083ff48bf5SDavid du Colombier 	bt878->packetlen = (nab << 16) | nasz;
150941dd6b47SDavid du Colombier 	bt878->intmask = intstat_scerr | intstat_ocerr | intstat_risci |
15103ff48bf5SDavid du Colombier 			intstat_pabort | intstat_riperr | intstat_pperr |
15113ff48bf5SDavid du Colombier 			intstat_fdsr | intstat_ftrgt | intstat_fbus;
15123ff48bf5SDavid du Colombier 
15133ff48bf5SDavid du Colombier 	/* Assume analog, 16bpp */
15143ff48bf5SDavid du Colombier 	for (s = 0; s < 16; s++)
15153ff48bf5SDavid du Colombier 		if (rate << s > Hwbase_ad * 4 / 15)
15163ff48bf5SDavid du Colombier 			break;
15173ff48bf5SDavid du Colombier 	for (d = 15; d >= 4; d--)
15183ff48bf5SDavid du Colombier 		if (rate << s < Hwbase_ad * 4 / d)
15193ff48bf5SDavid du Colombier 			break;
15203ff48bf5SDavid du Colombier 
15213ff48bf5SDavid du Colombier 	print("astart: sampleshift %d, decimation %d\n", s, d);
15223ff48bf5SDavid du Colombier 
15233ff48bf5SDavid du Colombier 	tv->narblocks = 0;
15243ff48bf5SDavid du Colombier 	bt878->gpiodmactl = gpiodmactl_fifoenable |
15253ff48bf5SDavid du Colombier 		gpiodmactl_riscenable | gpiodmactl_acapenable |
15263ff48bf5SDavid du Colombier 		gpiodmactl_daes2 |		/* gpiodmactl_apwrdn | */
152741dd6b47SDavid du Colombier 		gpiodmactl_daiomda | d << 8 | 9 << 28 | selector << 24;
15283ff48bf5SDavid du Colombier 	print("dmactl %.8ulX\n", bt878->gpiodmactl);
15293ff48bf5SDavid du Colombier 	iunlock(tv);
15303ff48bf5SDavid du Colombier }
15313ff48bf5SDavid du Colombier 
15323ff48bf5SDavid du Colombier static void
astop(Tv * tv)15333ff48bf5SDavid du Colombier astop(Tv *tv)
15343ff48bf5SDavid du Colombier {
15353ff48bf5SDavid du Colombier 	Bt848 *bt878 = tv->bt878;
15363ff48bf5SDavid du Colombier 
15373ff48bf5SDavid du Colombier 	ilock(tv);
15383ff48bf5SDavid du Colombier 	if (tv->aref.ref > 0) {
15393ff48bf5SDavid du Colombier 		iunlock(tv);
15403ff48bf5SDavid du Colombier 		error(Einuse);
15413ff48bf5SDavid du Colombier 	}
15423ff48bf5SDavid du Colombier 
15433ff48bf5SDavid du Colombier 	if (tv->abuf) {
15443ff48bf5SDavid du Colombier 		bt878->gpiodmactl &= ~gpiodmactl_riscenable;
15453ff48bf5SDavid du Colombier 		bt878->gpiodmactl &= ~gpiodmactl_fifoenable;
15463ff48bf5SDavid du Colombier 
15473ff48bf5SDavid du Colombier 		free(tv->abuf);
15483ff48bf5SDavid du Colombier 		tv->abuf = nil;
15493ff48bf5SDavid du Colombier 		free(tv->arisc);
15503ff48bf5SDavid du Colombier 		tv->arisc = nil;
15513ff48bf5SDavid du Colombier 	}
15523ff48bf5SDavid du Colombier 	iunlock(tv);
15533ff48bf5SDavid du Colombier }
15543ff48bf5SDavid du Colombier 
15553ff48bf5SDavid du Colombier static void
vgastart(Tv * tv,ulong pa,int stride)15564de34a7eSDavid du Colombier vgastart(Tv *tv, ulong pa, int stride)
15579a747e4fSDavid du Colombier {
15589a747e4fSDavid du Colombier 	Frame *frame;
15599a747e4fSDavid du Colombier 
15609a747e4fSDavid du Colombier 	frame = (Frame *)malloc(sizeof(Frame));
15619a747e4fSDavid du Colombier 	assert(frame);
15629a747e4fSDavid du Colombier 	if (waserror()) {
15639a747e4fSDavid du Colombier 		free(frame);
15649a747e4fSDavid du Colombier 		nexterror();
15659a747e4fSDavid du Colombier 	}
15669a747e4fSDavid du Colombier 
15679a747e4fSDavid du Colombier 	frame->fbase = nil;
15684de34a7eSDavid du Colombier 	frame->fstart = riscpacked(pa, 0, ntsc_hactive * getbitspp(tv) / 8,
156941dd6b47SDavid du Colombier 		ntsc_vactive, stride * getbitspp(tv) / 8, &frame->fjmp);
15709a747e4fSDavid du Colombier 	*frame->fjmp = PADDR(frame->fstart);
15719a747e4fSDavid du Colombier 
15729a747e4fSDavid du Colombier 	vactivate(tv, frame, 1);
15739a747e4fSDavid du Colombier }
15749a747e4fSDavid du Colombier 
15759a747e4fSDavid du Colombier static void
vstop(Tv * tv)15769a747e4fSDavid du Colombier vstop(Tv *tv)
15779a747e4fSDavid du Colombier {
15789a747e4fSDavid du Colombier 	Bt848 *bt848 = tv->bt848;
15799a747e4fSDavid du Colombier 
15809a747e4fSDavid du Colombier 	ilock(tv);
15819a747e4fSDavid du Colombier 	if (tv->fref.ref > 0) {
15829a747e4fSDavid du Colombier 		iunlock(tv);
15839a747e4fSDavid du Colombier 		error(Einuse);
15849a747e4fSDavid du Colombier 	}
15859a747e4fSDavid du Colombier 
15869a747e4fSDavid du Colombier 	if (tv->frames) {
15879a747e4fSDavid du Colombier 		int i;
15889a747e4fSDavid du Colombier 
15899a747e4fSDavid du Colombier 		bt848->gpiodmactl &= ~gpiodmactl_riscenable;
15909a747e4fSDavid du Colombier 		bt848->gpiodmactl &= ~gpiodmactl_fifoenable;
15919a747e4fSDavid du Colombier 		bt848->capctl &= ~(capctl_captureodd|capctl_captureeven);
15929a747e4fSDavid du Colombier 
15939a747e4fSDavid du Colombier 		for (i = 0; i != tv->nframes; i++)
15949a747e4fSDavid du Colombier 			if (tv->frames[i].fbase)
15959a747e4fSDavid du Colombier 				free(tv->frames[i].fbase);
15969a747e4fSDavid du Colombier 		free(tv->frames);
15979a747e4fSDavid du Colombier 		tv->frames = nil;
15989a747e4fSDavid du Colombier 	}
15999a747e4fSDavid du Colombier 	iunlock(tv);
16009a747e4fSDavid du Colombier }
16019a747e4fSDavid du Colombier 
160241dd6b47SDavid du Colombier static long hrcfreq[] = {		/* HRC CATV frequencies */
16039a747e4fSDavid du Colombier 	    0,  7200,  5400,  6000,  6600,  7800,  8400, 17400,
16049a747e4fSDavid du Colombier 	18000, 18600, 19200, 19800, 20400, 21000, 12000, 12600,
16059a747e4fSDavid du Colombier 	13200, 13800, 14400, 15000, 15600, 16200, 16800, 21600,
16069a747e4fSDavid du Colombier 	22200, 22800, 23400, 24000, 24600, 25200, 25800, 26400,
16079a747e4fSDavid du Colombier 	27000, 27600, 28200, 28800, 29400, 30000, 30600, 31200,
16089a747e4fSDavid du Colombier 	31800, 32400, 33000, 33600, 34200, 34800, 35400, 36000,
16099a747e4fSDavid du Colombier 	36600, 37200, 37800, 38400, 39000, 39600, 40200, 40800,
16109a747e4fSDavid du Colombier 	41400, 42000, 42600, 43200, 43800, 44400, 45000, 45600,
16119a747e4fSDavid du Colombier 	46200, 46800, 47400, 48000, 48600, 49200, 49800, 50400,
16129a747e4fSDavid du Colombier 	51000, 51600, 52200, 52800, 53400, 54000, 54600, 55200,
16139a747e4fSDavid du Colombier 	55800, 56400, 57000, 57600, 58200, 58800, 59400, 60000,
16149a747e4fSDavid du Colombier 	60600, 61200, 61800, 62400, 63000, 63600, 64200,  9000,
16159a747e4fSDavid du Colombier 	 9600, 10200, 10800, 11400, 64800, 65400, 66000, 66600,
16169a747e4fSDavid du Colombier 	67200, 67800, 68400, 69000, 69600, 70200, 70800, 71400,
16179a747e4fSDavid du Colombier 	72000, 72600, 73200, 73800, 74400, 75000, 75600, 76200,
16189a747e4fSDavid du Colombier 	76800, 77400, 78000, 78600, 79200, 79800,
16199a747e4fSDavid du Colombier };
16209a747e4fSDavid du Colombier 
16219a747e4fSDavid du Colombier static void
frequency(Tv * tv,int channel,int finetune)16229a747e4fSDavid du Colombier frequency(Tv *tv, int channel, int finetune)
16239a747e4fSDavid du Colombier {
16249a747e4fSDavid du Colombier 	Tuner *tuner = tv->tuner;
16259a747e4fSDavid du Colombier 	long freq;
16269a747e4fSDavid du Colombier 	ushort div;
16279a747e4fSDavid du Colombier 	uchar cfg;
16289a747e4fSDavid du Colombier 
16299a747e4fSDavid du Colombier 	if (channel < 0 || channel > nelem(hrcfreq))
16309a747e4fSDavid du Colombier 		error(Ebadarg);
16319a747e4fSDavid du Colombier 
16329a747e4fSDavid du Colombier 	freq = (hrcfreq[channel] * Freqmultiplier) / 100;
16339a747e4fSDavid du Colombier 
16349a747e4fSDavid du Colombier 	if (freq < tuner->freq_vhfh)
16359a747e4fSDavid du Colombier 		cfg = tuner->VHF_L;
16369a747e4fSDavid du Colombier 	else if (freq < tuner->freq_uhf)
16379a747e4fSDavid du Colombier 		cfg = tuner->VHF_H;
16389a747e4fSDavid du Colombier 	else
16399a747e4fSDavid du Colombier 		cfg = tuner->UHF;
16409a747e4fSDavid du Colombier 
16419a747e4fSDavid du Colombier 	div = (freq + tuner->offs + finetune) & 0x7fff;
16429a747e4fSDavid du Colombier 
16433ff48bf5SDavid du Colombier 	if (!i2cwrite(tv, tv->i2ctuneraddr, (div >> 8) & 0x7f, div, 1))
16449a747e4fSDavid du Colombier 		error(Eio);
16459a747e4fSDavid du Colombier 
16463ff48bf5SDavid du Colombier 	if (!i2cwrite(tv, tv->i2ctuneraddr, tuner->cfg, cfg, 1))
16479a747e4fSDavid du Colombier 		error(Eio);
16483ff48bf5SDavid du Colombier 
16493ff48bf5SDavid du Colombier 	tv->channel = channel;
16503ff48bf5SDavid du Colombier 	if (tv->msp)
16513ff48bf5SDavid du Colombier 		msptune(tv);
16523ff48bf5SDavid du Colombier }
16533ff48bf5SDavid du Colombier 
16543ff48bf5SDavid du Colombier static struct {
16553ff48bf5SDavid du Colombier 	char	*cmode;
16563ff48bf5SDavid du Colombier 	ulong	realmode;
16573ff48bf5SDavid du Colombier 	ulong	cbits;
16583ff48bf5SDavid du Colombier } colormodes[] = {
16593ff48bf5SDavid du Colombier 	{ "RGB16",	colorfmt_rgb16,		colorfmt_rgb16, },
16603ff48bf5SDavid du Colombier 	{ "YCbCr422",	colorfmt_YCbCr422,	colorfmt_YCbCr422, },
16613ff48bf5SDavid du Colombier 	{ "YCbCr411",	colorfmt_YCbCr411,	colorfmt_YCbCr422, },
16623ff48bf5SDavid du Colombier };
16633ff48bf5SDavid du Colombier 
16643ff48bf5SDavid du Colombier static void
colormode(Tv * tv,char * colormode)16653ff48bf5SDavid du Colombier colormode(Tv *tv, char *colormode)
16663ff48bf5SDavid du Colombier {
16673ff48bf5SDavid du Colombier 	Bt848 *bt848 = tv->bt848;
16683ff48bf5SDavid du Colombier 	int i;
16693ff48bf5SDavid du Colombier 
16703ff48bf5SDavid du Colombier 	for (i = 0; i != nelem(colormodes); i++)
16713ff48bf5SDavid du Colombier 		if (!strcmp(colormodes[i].cmode, colormode))
16723ff48bf5SDavid du Colombier 			break;
16733ff48bf5SDavid du Colombier 
16743ff48bf5SDavid du Colombier 	if (i == nelem(colormodes))
16753ff48bf5SDavid du Colombier 		error(Ebadarg);
16763ff48bf5SDavid du Colombier 
16773ff48bf5SDavid du Colombier 	tv->cfmt = colormodes[i].realmode;
16783ff48bf5SDavid du Colombier 	bt848->colorfmt = colormodes[i].cbits;
16799a747e4fSDavid du Colombier }
16809a747e4fSDavid du Colombier 
16819a747e4fSDavid du Colombier static int
getbitspp(Tv * tv)16823ff48bf5SDavid du Colombier getbitspp(Tv *tv)
16839a747e4fSDavid du Colombier {
16843ff48bf5SDavid du Colombier 	switch (tv->cfmt) {
16859a747e4fSDavid du Colombier 	case colorfmt_rgb16:
16863ff48bf5SDavid du Colombier 	case colorfmt_YCbCr422:
16873ff48bf5SDavid du Colombier 		return 16;
16883ff48bf5SDavid du Colombier 	case colorfmt_YCbCr411:
16893ff48bf5SDavid du Colombier 		return 12;
16909a747e4fSDavid du Colombier 	default:
16913ff48bf5SDavid du Colombier 		error("getbitspp: Unsupport color format\n");
16929a747e4fSDavid du Colombier 	}
16939a747e4fSDavid du Colombier 	return -1;
16949a747e4fSDavid du Colombier }
16959a747e4fSDavid du Colombier 
16963ff48bf5SDavid du Colombier static char *
getcolormode(ulong cmode)16973ff48bf5SDavid du Colombier getcolormode(ulong cmode)
16983ff48bf5SDavid du Colombier {
16993ff48bf5SDavid du Colombier 	switch (cmode) {
17003ff48bf5SDavid du Colombier 	case colorfmt_rgb16:
17013ff48bf5SDavid du Colombier 		return "RGB16";
17023ff48bf5SDavid du Colombier 	case colorfmt_YCbCr411:
17033ff48bf5SDavid du Colombier 		return "YCbCr411";
17043ff48bf5SDavid du Colombier 	case colorfmt_YCbCr422:
17053ff48bf5SDavid du Colombier 		return (cmode == colorfmt_YCbCr422)? "YCbCr422": "YCbCr411";
17063ff48bf5SDavid du Colombier 	default:
17073ff48bf5SDavid du Colombier 		error("getcolormode: Unsupport color format\n");
17083ff48bf5SDavid du Colombier 	}
17093ff48bf5SDavid du Colombier 	return nil;
17103ff48bf5SDavid du Colombier }
17113ff48bf5SDavid du Colombier 
17123ff48bf5SDavid du Colombier static void
i2c_set(Tv * tv,int scl,int sda)17133ff48bf5SDavid du Colombier i2c_set(Tv *tv, int scl, int sda)
17143ff48bf5SDavid du Colombier {
17153ff48bf5SDavid du Colombier 	Bt848 *bt848 = tv->bt848;
17163ff48bf5SDavid du Colombier 	ulong d;
17173ff48bf5SDavid du Colombier 
17183ff48bf5SDavid du Colombier 	bt848->i2c = (scl << 1) | sda;
17193ff48bf5SDavid du Colombier 	d = bt848->i2c;
17203ff48bf5SDavid du Colombier 	USED(d);
17213ff48bf5SDavid du Colombier 	microdelay(i2c_delay);
17223ff48bf5SDavid du Colombier }
17233ff48bf5SDavid du Colombier 
17243ff48bf5SDavid du Colombier static uchar
i2c_getsda(Tv * tv)17253ff48bf5SDavid du Colombier i2c_getsda(Tv *tv)
17263ff48bf5SDavid du Colombier {
17273ff48bf5SDavid du Colombier 	Bt848 *bt848 = tv->bt848;
17283ff48bf5SDavid du Colombier 
17293ff48bf5SDavid du Colombier 	return bt848->i2c & i2c_sda;
17303ff48bf5SDavid du Colombier }
17313ff48bf5SDavid du Colombier 
17323ff48bf5SDavid du Colombier static void
i2c_start(Tv * tv)17333ff48bf5SDavid du Colombier i2c_start(Tv *tv)
17343ff48bf5SDavid du Colombier {
17353ff48bf5SDavid du Colombier 	i2c_set(tv, 0, 1);
17363ff48bf5SDavid du Colombier 	i2c_set(tv, 1, 1);
17373ff48bf5SDavid du Colombier 	i2c_set(tv, 1, 0);
17383ff48bf5SDavid du Colombier 	i2c_set(tv, 0, 0);
17393ff48bf5SDavid du Colombier }
17403ff48bf5SDavid du Colombier 
17413ff48bf5SDavid du Colombier static void
i2c_stop(Tv * tv)17423ff48bf5SDavid du Colombier i2c_stop(Tv *tv)
17433ff48bf5SDavid du Colombier {
17443ff48bf5SDavid du Colombier 	i2c_set(tv, 0, 0);
17453ff48bf5SDavid du Colombier 	i2c_set(tv, 1, 0);
17463ff48bf5SDavid du Colombier 	i2c_set(tv, 1, 1);
17473ff48bf5SDavid du Colombier }
17483ff48bf5SDavid du Colombier 
17493ff48bf5SDavid du Colombier static void
i2c_bit(Tv * tv,int sda)17503ff48bf5SDavid du Colombier i2c_bit(Tv *tv, int sda)
17513ff48bf5SDavid du Colombier {
17523ff48bf5SDavid du Colombier 	i2c_set(tv, 0, sda);
17533ff48bf5SDavid du Colombier 	i2c_set(tv, 1, sda);
17543ff48bf5SDavid du Colombier 	i2c_set(tv, 0, sda);
17553ff48bf5SDavid du Colombier }
17563ff48bf5SDavid du Colombier 
17573ff48bf5SDavid du Colombier static int
i2c_getack(Tv * tv)17583ff48bf5SDavid du Colombier i2c_getack(Tv *tv)
17593ff48bf5SDavid du Colombier {
17603ff48bf5SDavid du Colombier 	int ack;
17613ff48bf5SDavid du Colombier 
17623ff48bf5SDavid du Colombier 	i2c_set(tv, 0, 1);
17633ff48bf5SDavid du Colombier 	i2c_set(tv, 1, 1);
17643ff48bf5SDavid du Colombier 	ack = i2c_getsda(tv);
17653ff48bf5SDavid du Colombier 	i2c_set(tv, 0, 1);
17663ff48bf5SDavid du Colombier 	return ack;
17673ff48bf5SDavid du Colombier }
17683ff48bf5SDavid du Colombier 
17693ff48bf5SDavid du Colombier static int
i2c_wr8(Tv * tv,uchar d,int wait)17703ff48bf5SDavid du Colombier i2c_wr8(Tv *tv, uchar d, int wait)
17713ff48bf5SDavid du Colombier {
17723ff48bf5SDavid du Colombier 	int i, ack;
17733ff48bf5SDavid du Colombier 
17743ff48bf5SDavid du Colombier 	i2c_set(tv, 0, 0);
17753ff48bf5SDavid du Colombier 	for (i = 0; i != 8; i++) {
17763ff48bf5SDavid du Colombier 		i2c_bit(tv, (d & 0x80)? 1: 0);
17773ff48bf5SDavid du Colombier 		d <<= 1;
17783ff48bf5SDavid du Colombier 	}
17793ff48bf5SDavid du Colombier 	if (wait)
17803ff48bf5SDavid du Colombier 		microdelay(wait);
17813ff48bf5SDavid du Colombier 
17823ff48bf5SDavid du Colombier 	ack = i2c_getack(tv);
17833ff48bf5SDavid du Colombier 	return ack == 0;
17843ff48bf5SDavid du Colombier }
17853ff48bf5SDavid du Colombier 
17863ff48bf5SDavid du Colombier static uchar
i2c_rd8(Tv * tv,int lastbyte)17873ff48bf5SDavid du Colombier i2c_rd8(Tv *tv, int lastbyte)
17883ff48bf5SDavid du Colombier {
17893ff48bf5SDavid du Colombier 	int i;
17903ff48bf5SDavid du Colombier 	uchar d;
17913ff48bf5SDavid du Colombier 
17923ff48bf5SDavid du Colombier 	d = 0;
17933ff48bf5SDavid du Colombier 	i2c_set(tv, 0, 1);
17943ff48bf5SDavid du Colombier 	for (i = 0; i != 8; i++) {
17953ff48bf5SDavid du Colombier 		i2c_set(tv, 1, 1);
17963ff48bf5SDavid du Colombier 		d <<= 1;
17973ff48bf5SDavid du Colombier 		if (i2c_getsda(tv))
17983ff48bf5SDavid du Colombier 			d |= 1;
17993ff48bf5SDavid du Colombier 		i2c_set(tv, 0, 1);
18003ff48bf5SDavid du Colombier 	}
18013ff48bf5SDavid du Colombier 
18023ff48bf5SDavid du Colombier 	i2c_bit(tv, lastbyte? 1: 0);
18033ff48bf5SDavid du Colombier 	return d;
18043ff48bf5SDavid du Colombier }
18053ff48bf5SDavid du Colombier 
18063ff48bf5SDavid du Colombier static int
mspsend(Tv * tv,uchar * cmd,int ncmd)18073ff48bf5SDavid du Colombier mspsend(Tv *tv, uchar *cmd, int ncmd)
18083ff48bf5SDavid du Colombier {
18093ff48bf5SDavid du Colombier 	int i, j, delay;
18103ff48bf5SDavid du Colombier 
18113ff48bf5SDavid du Colombier 	for (i = 0; i != 3; i++) {
18123ff48bf5SDavid du Colombier 		delay = 2000;
18133ff48bf5SDavid du Colombier 
18143ff48bf5SDavid du Colombier 		i2c_start(tv);
18153ff48bf5SDavid du Colombier 		for (j = 0; j != ncmd; j++) {
18163ff48bf5SDavid du Colombier 			if (!i2c_wr8(tv, cmd[j], delay))
18173ff48bf5SDavid du Colombier 				break;
18183ff48bf5SDavid du Colombier 			delay = 0;
18193ff48bf5SDavid du Colombier 		}
18203ff48bf5SDavid du Colombier 		i2c_stop(tv);
18213ff48bf5SDavid du Colombier 
18223ff48bf5SDavid du Colombier 		if (j == ncmd)
18233ff48bf5SDavid du Colombier 			return 1;
18243ff48bf5SDavid du Colombier 
18253ff48bf5SDavid du Colombier 		microdelay(10000);
18263ff48bf5SDavid du Colombier 		print("mspsend: retrying\n");
18273ff48bf5SDavid du Colombier 	}
18283ff48bf5SDavid du Colombier 
18293ff48bf5SDavid du Colombier 	return 0;
18303ff48bf5SDavid du Colombier }
18313ff48bf5SDavid du Colombier 
18323ff48bf5SDavid du Colombier static int
mspwrite(Tv * tv,uchar sub,ushort reg,ushort v)18333ff48bf5SDavid du Colombier mspwrite(Tv *tv, uchar sub, ushort reg, ushort v)
18343ff48bf5SDavid du Colombier {
18353ff48bf5SDavid du Colombier 	uchar b[6];
18363ff48bf5SDavid du Colombier 
18373ff48bf5SDavid du Colombier 	b[0] = i2c_msp3400;
18383ff48bf5SDavid du Colombier 	b[1] = sub;
18393ff48bf5SDavid du Colombier 	b[2] = reg >> 8;
18403ff48bf5SDavid du Colombier 	b[3] = reg;
18413ff48bf5SDavid du Colombier 	b[4] = v >> 8;
18423ff48bf5SDavid du Colombier 	b[5] = v;
18433ff48bf5SDavid du Colombier 	return mspsend(tv, b, sizeof b);
18443ff48bf5SDavid du Colombier }
18453ff48bf5SDavid du Colombier 
18463ff48bf5SDavid du Colombier static int
mspread(Tv * tv,uchar sub,ushort reg,ushort * data)18473ff48bf5SDavid du Colombier mspread(Tv *tv, uchar sub, ushort reg, ushort *data)
18483ff48bf5SDavid du Colombier {
18493ff48bf5SDavid du Colombier 	uchar b[4];
18503ff48bf5SDavid du Colombier 	int i;
18513ff48bf5SDavid du Colombier 
18523ff48bf5SDavid du Colombier 	b[0] = i2c_msp3400;
18533ff48bf5SDavid du Colombier 	b[1] = sub;
18543ff48bf5SDavid du Colombier 	b[2] = reg >> 8;
18553ff48bf5SDavid du Colombier 	b[3] = reg;
18563ff48bf5SDavid du Colombier 
18573ff48bf5SDavid du Colombier 	for (i = 0; i != 3; i++) {
18583ff48bf5SDavid du Colombier 		i2c_start(tv);
18593ff48bf5SDavid du Colombier 		if (!i2c_wr8(tv, b[0], 2000) ||
18603ff48bf5SDavid du Colombier 			!i2c_wr8(tv, b[1] | 1, 0) ||
18613ff48bf5SDavid du Colombier 			!i2c_wr8(tv, b[2], 0) ||
18623ff48bf5SDavid du Colombier 			!i2c_wr8(tv, b[3], 0)) {
18633ff48bf5SDavid du Colombier 
18643ff48bf5SDavid du Colombier 			i2c_stop(tv);
18653ff48bf5SDavid du Colombier 			microdelay(10000);
18663ff48bf5SDavid du Colombier 			print("retrying\n");
18673ff48bf5SDavid du Colombier 			continue;
18683ff48bf5SDavid du Colombier 		}
18693ff48bf5SDavid du Colombier 
18703ff48bf5SDavid du Colombier 		i2c_start(tv);
18713ff48bf5SDavid du Colombier 
18723ff48bf5SDavid du Colombier 		if (!i2c_wr8(tv, b[0] | 1, 2000)) {
18733ff48bf5SDavid du Colombier 			i2c_stop(tv);
18743ff48bf5SDavid du Colombier 			continue;
18753ff48bf5SDavid du Colombier 		}
18763ff48bf5SDavid du Colombier 
18773ff48bf5SDavid du Colombier 		*data  = i2c_rd8(tv, 0) << 8;
18783ff48bf5SDavid du Colombier 		*data |= i2c_rd8(tv, 1);
18793ff48bf5SDavid du Colombier 		i2c_stop(tv);
18803ff48bf5SDavid du Colombier 		return 1;
18813ff48bf5SDavid du Colombier 	}
18823ff48bf5SDavid du Colombier 	return 0;
18833ff48bf5SDavid du Colombier }
18843ff48bf5SDavid du Colombier 
18853ff48bf5SDavid du Colombier static uchar mspt_reset[] = { i2c_msp3400, 0, 0x80, 0 };
18863ff48bf5SDavid du Colombier static uchar mspt_on[] = { i2c_msp3400, 0, 0, 0 };
18873ff48bf5SDavid du Colombier 
18883ff48bf5SDavid du Colombier static int
mspreset(Tv * tv)18893ff48bf5SDavid du Colombier mspreset(Tv *tv)
18903ff48bf5SDavid du Colombier {
18913ff48bf5SDavid du Colombier 	ushort v, p;
18923ff48bf5SDavid du Colombier 	Bt848 *bt848 = tv->bt848;
18933ff48bf5SDavid du Colombier 	ulong b;
18943ff48bf5SDavid du Colombier 
18953ff48bf5SDavid du Colombier 	b = 1 << 5;
18963ff48bf5SDavid du Colombier 	gpioenable(tv, ~b, b);
18973ff48bf5SDavid du Colombier 	gpiowrite(tv, ~b, 0);
18983ff48bf5SDavid du Colombier 	microdelay(2500);
18993ff48bf5SDavid du Colombier 	gpiowrite(tv, ~b, b);
19003ff48bf5SDavid du Colombier 
19013ff48bf5SDavid du Colombier 	bt848->i2c = 0x80;
19023ff48bf5SDavid du Colombier 
19033ff48bf5SDavid du Colombier 	microdelay(2000);
19043ff48bf5SDavid du Colombier 	mspsend(tv, mspt_reset, sizeof mspt_reset);
19053ff48bf5SDavid du Colombier 
19063ff48bf5SDavid du Colombier 	microdelay(2000);
19073ff48bf5SDavid du Colombier 	if (!mspsend(tv, mspt_on, sizeof mspt_on)) {
19083ff48bf5SDavid du Colombier 		print("#V: Cannot find MSP34x5G on the I2C bus (on)\n");
19093ff48bf5SDavid du Colombier 		return 0;
19103ff48bf5SDavid du Colombier 	}
19113ff48bf5SDavid du Colombier 	microdelay(2000);
19123ff48bf5SDavid du Colombier 
19133ff48bf5SDavid du Colombier 	if (!mspread(tv, msp_bbp, 0x001e, &v)) {
19143ff48bf5SDavid du Colombier 		print("#V: Cannot read MSP34xG5 chip version\n");
19153ff48bf5SDavid du Colombier 		return 0;
19163ff48bf5SDavid du Colombier 	}
19173ff48bf5SDavid du Colombier 
19183ff48bf5SDavid du Colombier 	if (!mspread(tv, msp_bbp, 0x001f, &p)) {
19193ff48bf5SDavid du Colombier 		print("#V: Cannot read MSP34xG5 product code\n");
19203ff48bf5SDavid du Colombier 		return 0;
19213ff48bf5SDavid du Colombier 	}
19223ff48bf5SDavid du Colombier 
19233ff48bf5SDavid du Colombier 	print("#V: MSP34%dg ROM %.d, %d.%d\n",
19243ff48bf5SDavid du Colombier 		(uchar)(p >> 8), (uchar)p, (uchar)(v >> 8), (uchar)v);
19253ff48bf5SDavid du Colombier 
19263ff48bf5SDavid du Colombier 	tv->msp = 1;
19273ff48bf5SDavid du Colombier 	return 1;
19283ff48bf5SDavid du Colombier }
19293ff48bf5SDavid du Colombier 
19303ff48bf5SDavid du Colombier static void
mspvolume(Tv * tv,int mute,int l,int r)19313ff48bf5SDavid du Colombier mspvolume(Tv *tv, int mute, int l, int r)
19323ff48bf5SDavid du Colombier {
19333ff48bf5SDavid du Colombier 	short v, d;
19343ff48bf5SDavid du Colombier 	ushort b;
19353ff48bf5SDavid du Colombier 
19363ff48bf5SDavid du Colombier 	if (mute) {
19373ff48bf5SDavid du Colombier 		v = 0;
19383ff48bf5SDavid du Colombier 		b = 0;
19393ff48bf5SDavid du Colombier 	}
19403ff48bf5SDavid du Colombier 	else {
19413ff48bf5SDavid du Colombier 		tv->aleft = l;
19423ff48bf5SDavid du Colombier 		tv->aright = r;
19433ff48bf5SDavid du Colombier 		d = v = max(l, r);
19443ff48bf5SDavid du Colombier 		if (d == 0)
19453ff48bf5SDavid du Colombier 			d++;
19463ff48bf5SDavid du Colombier 		b = ((r - l) * 0x7f) / d;
19473ff48bf5SDavid du Colombier 	}
19483ff48bf5SDavid du Colombier 
19493ff48bf5SDavid du Colombier 	mspwrite(tv, msp_bbp, 0, v << 8);
19503ff48bf5SDavid du Colombier 	mspwrite(tv, msp_bbp, 7, v? 0x4000: 0);
19513ff48bf5SDavid du Colombier 	mspwrite(tv, msp_bbp, 1, b << 8);
19523ff48bf5SDavid du Colombier }
19533ff48bf5SDavid du Colombier 
19543ff48bf5SDavid du Colombier static char *
mspaformat(int f)19553ff48bf5SDavid du Colombier mspaformat(int f)
19563ff48bf5SDavid du Colombier {
19573ff48bf5SDavid du Colombier 	switch (f) {
19583ff48bf5SDavid du Colombier 	case 0:
19593ff48bf5SDavid du Colombier 		return "unknown";
19603ff48bf5SDavid du Colombier 	case 2:
19613ff48bf5SDavid du Colombier 	case 0x20:
19623ff48bf5SDavid du Colombier 	case 0x30:
19633ff48bf5SDavid du Colombier 		return "M-BTSC";
19643ff48bf5SDavid du Colombier 	case 3:
19653ff48bf5SDavid du Colombier 		return "B/G-FM";
19663ff48bf5SDavid du Colombier 	case 4:
19673ff48bf5SDavid du Colombier 	case 9:
19683ff48bf5SDavid du Colombier 	case 0xB:
19693ff48bf5SDavid du Colombier 		return "L-AM/NICAM D/Kn";
19703ff48bf5SDavid du Colombier 	case 8:
19713ff48bf5SDavid du Colombier 		return "B/G-NICAM";
19723ff48bf5SDavid du Colombier 	case 0xA:
19733ff48bf5SDavid du Colombier 		return "I";
19743ff48bf5SDavid du Colombier 	case 0x40:
19753ff48bf5SDavid du Colombier 		return "FM-Radio";
19763ff48bf5SDavid du Colombier 	}
19773ff48bf5SDavid du Colombier 	return "unknown format";
19783ff48bf5SDavid du Colombier }
19793ff48bf5SDavid du Colombier 
19803ff48bf5SDavid du Colombier 
19813ff48bf5SDavid du Colombier static void
msptune(Tv * tv)19823ff48bf5SDavid du Colombier msptune(Tv *tv)
19833ff48bf5SDavid du Colombier {
19843ff48bf5SDavid du Colombier 	ushort d, s, nicam;
19853ff48bf5SDavid du Colombier 	int i;
19863ff48bf5SDavid du Colombier 
19873ff48bf5SDavid du Colombier 	mspvolume(tv, 1, 0, 0);
19883ff48bf5SDavid du Colombier 	if (!mspwrite(tv, msp_dem, 0x0030, 0x2033))
19893ff48bf5SDavid du Colombier 		error("#V: Cannot set MODUS register");
19903ff48bf5SDavid du Colombier 
19913ff48bf5SDavid du Colombier 	if (!mspwrite(tv, msp_bbp, 0x0008, 0x0320))
19923ff48bf5SDavid du Colombier 		error("#V: Cannot set loadspeaker input");
19933ff48bf5SDavid du Colombier 
19943ff48bf5SDavid du Colombier 	if (!mspwrite(tv, msp_dem, 0x0040, 0x0001))
19953ff48bf5SDavid du Colombier 		error("#V: Cannot set I2S clock freq");
19963ff48bf5SDavid du Colombier 	if (!mspwrite(tv, msp_bbp, 0x000d, 0x1900))
19973ff48bf5SDavid du Colombier 		error("#V: Cannot set SCART prescale");
19983ff48bf5SDavid du Colombier 	if (!mspwrite(tv, msp_bbp, 0x000e, 0x2403))
19993ff48bf5SDavid du Colombier 		error("#V: Cannot set FM/AM prescale");
20003ff48bf5SDavid du Colombier 	if (!mspwrite(tv, msp_bbp, 0x0010, 0x5a00))
20013ff48bf5SDavid du Colombier 		error("#V: Cannot set NICAM prescale");
20023ff48bf5SDavid du Colombier 	if (!mspwrite(tv, msp_dem, 0x0020, 0x0001))
20033ff48bf5SDavid du Colombier 		error("#V: Cannot start auto detect");
20043ff48bf5SDavid du Colombier 
20053ff48bf5SDavid du Colombier 	for (d = (ushort)-1, i = 0; i != 10; i++) {
20063ff48bf5SDavid du Colombier 		if (!mspread(tv, msp_dem, 0x007e, &d))
20073ff48bf5SDavid du Colombier 			error("#V: Cannot get autodetect info MSP34xG5");
20083ff48bf5SDavid du Colombier 
20093ff48bf5SDavid du Colombier 		if (d == 0 || d < 0x800)
20103ff48bf5SDavid du Colombier 			break;
20113ff48bf5SDavid du Colombier 		delay(50);
20123ff48bf5SDavid du Colombier 	}
20133ff48bf5SDavid du Colombier 
20143ff48bf5SDavid du Colombier 	if (!mspread(tv, msp_dem, 0x0200, &s))
20153ff48bf5SDavid du Colombier 		error("#V: Cannot get status info MSP34xG5");
20163ff48bf5SDavid du Colombier 
20173ff48bf5SDavid du Colombier 	mspvolume(tv, 0, tv->aleft, tv->aright);
20183ff48bf5SDavid du Colombier 
20193ff48bf5SDavid du Colombier 	nicam = ((s >> 4) & 2) | ((s >> 9) & 1);
20203ff48bf5SDavid du Colombier 	snprint(tv->ainfo, sizeof tv->ainfo, "%s %s %s",
20213ff48bf5SDavid du Colombier 		mspaformat(d), (s & (1 << 6))? "stereo": "mono",
20223ff48bf5SDavid du Colombier 		nicamstate[nicam]);
20233ff48bf5SDavid du Colombier }
20243ff48bf5SDavid du Colombier 
20253ff48bf5SDavid du Colombier static void
i2cscan(Tv * tv)20263ff48bf5SDavid du Colombier i2cscan(Tv *tv)
20273ff48bf5SDavid du Colombier {
20283ff48bf5SDavid du Colombier 	int i, ack;
20293ff48bf5SDavid du Colombier 
20303ff48bf5SDavid du Colombier 	for (i = 0; i < 0x100; i += 2) {
20313ff48bf5SDavid du Colombier 		i2c_start(tv);
20323ff48bf5SDavid du Colombier 		ack = i2c_wr8(tv, i, 0);
20333ff48bf5SDavid du Colombier 		i2c_stop(tv);
203441dd6b47SDavid du Colombier 		if (ack)
20353ff48bf5SDavid du Colombier 			print("i2c device @%.2uX\n", i);
20363ff48bf5SDavid du Colombier 	}
20373ff48bf5SDavid du Colombier 
20383ff48bf5SDavid du Colombier 	for (i = 0xf0; i != 0xff; i++) {
20393ff48bf5SDavid du Colombier 		i2c_start(tv);
20403ff48bf5SDavid du Colombier 		ack = i2c_wr8(tv, i, 0);
20413ff48bf5SDavid du Colombier 		i2c_stop(tv);
20423ff48bf5SDavid du Colombier 		if (ack)
20433ff48bf5SDavid du Colombier 			print("i2c device may be at @%.2uX\n", i);
20443ff48bf5SDavid du Colombier 	}
20453ff48bf5SDavid du Colombier }
20463ff48bf5SDavid du Colombier 
20473ff48bf5SDavid du Colombier static void
gpioenable(Tv * tv,ulong mask,ulong data)20483ff48bf5SDavid du Colombier gpioenable(Tv *tv, ulong mask, ulong data)
20493ff48bf5SDavid du Colombier {
20503ff48bf5SDavid du Colombier 	Bt848 *bt848 = tv->bt848;
20513ff48bf5SDavid du Colombier 
20523ff48bf5SDavid du Colombier 	bt848->gpioouten = (bt848->gpioouten & mask) | data;
20533ff48bf5SDavid du Colombier }
20543ff48bf5SDavid du Colombier 
20553ff48bf5SDavid du Colombier static void
gpiowrite(Tv * tv,ulong mask,ulong data)20563ff48bf5SDavid du Colombier gpiowrite(Tv *tv, ulong mask, ulong data)
20573ff48bf5SDavid du Colombier {
20583ff48bf5SDavid du Colombier 	Bt848 *bt848 = tv->bt848;
20593ff48bf5SDavid du Colombier 
20603ff48bf5SDavid du Colombier 	bt848->gpiodata[0] = (bt848->gpiodata[0] & mask) | data;
20613ff48bf5SDavid du Colombier }
20623ff48bf5SDavid du Colombier 
20633ff48bf5SDavid du Colombier static void
alteraoutput(Tv * tv)20643ff48bf5SDavid du Colombier alteraoutput(Tv *tv)
20653ff48bf5SDavid du Colombier {
20663ff48bf5SDavid du Colombier 	if (tv->gpiostate == Gpiooutput)
20673ff48bf5SDavid du Colombier 		return;
20683ff48bf5SDavid du Colombier 
20693ff48bf5SDavid du Colombier 	gpioenable(tv, ~0xffffff, 0x56ffff);
20703ff48bf5SDavid du Colombier 	microdelay(10);
20713ff48bf5SDavid du Colombier 	tv->gpiostate = Gpiooutput;
20723ff48bf5SDavid du Colombier }
20733ff48bf5SDavid du Colombier 
20743ff48bf5SDavid du Colombier static void
alterainput(Tv * tv)20753ff48bf5SDavid du Colombier alterainput(Tv *tv)
20763ff48bf5SDavid du Colombier {
20773ff48bf5SDavid du Colombier 	if (tv->gpiostate == Gpioinput)
20783ff48bf5SDavid du Colombier 		return;
20793ff48bf5SDavid du Colombier 
20803ff48bf5SDavid du Colombier 	gpioenable(tv, ~0xffffff, 0x570000);
20813ff48bf5SDavid du Colombier 	microdelay(10);
20823ff48bf5SDavid du Colombier 	tv->gpiostate = Gpioinput;
20833ff48bf5SDavid du Colombier }
20843ff48bf5SDavid du Colombier 
20853ff48bf5SDavid du Colombier static void
alterareg(Tv * tv,ulong reg)20863ff48bf5SDavid du Colombier alterareg(Tv *tv, ulong reg)
20873ff48bf5SDavid du Colombier {
20883ff48bf5SDavid du Colombier 	if (tv->alterareg == reg)
20893ff48bf5SDavid du Colombier 		return;
20903ff48bf5SDavid du Colombier 
20913ff48bf5SDavid du Colombier 	gpiowrite(tv, ~0x56ffff, (reg & 0x54ffff) | tv->alteraclock);
20923ff48bf5SDavid du Colombier 	microdelay(10);
20933ff48bf5SDavid du Colombier 	tv->alterareg = reg;
20943ff48bf5SDavid du Colombier }
20953ff48bf5SDavid du Colombier 
20963ff48bf5SDavid du Colombier static void
alterawrite(Tv * tv,ulong reg,ushort data)20973ff48bf5SDavid du Colombier alterawrite(Tv *tv, ulong reg, ushort data)
20983ff48bf5SDavid du Colombier {
20993ff48bf5SDavid du Colombier 	alteraoutput(tv);
21003ff48bf5SDavid du Colombier 	alterareg(tv, reg);
21013ff48bf5SDavid du Colombier 
21023ff48bf5SDavid du Colombier 	tv->alteraclock ^= 0x20000;
21033ff48bf5SDavid du Colombier 	gpiowrite(tv, ~0x56ffff, (reg & 0x540000) | data | tv->alteraclock);
21043ff48bf5SDavid du Colombier 	microdelay(10);
21053ff48bf5SDavid du Colombier }
21063ff48bf5SDavid du Colombier 
21073ff48bf5SDavid du Colombier static void
alteraread(Tv * tv,int reg,ushort * data)21083ff48bf5SDavid du Colombier alteraread(Tv *tv, int reg, ushort *data)
21093ff48bf5SDavid du Colombier {
21103ff48bf5SDavid du Colombier 	Bt848 *bt848 = tv->bt848;
21113ff48bf5SDavid du Colombier 
21123ff48bf5SDavid du Colombier 	if (tv->alterareg != reg) {
21133ff48bf5SDavid du Colombier 		alteraoutput(tv);
21143ff48bf5SDavid du Colombier 		alterareg(tv, reg);
21153ff48bf5SDavid du Colombier 	}
21163ff48bf5SDavid du Colombier 	else {
21173ff48bf5SDavid du Colombier 		gpioenable(tv, ~0xffffff, 0x560000);
21183ff48bf5SDavid du Colombier 		microdelay(10);
21193ff48bf5SDavid du Colombier 	}
21203ff48bf5SDavid du Colombier 
21213ff48bf5SDavid du Colombier 	alterainput(tv);
21223ff48bf5SDavid du Colombier 	gpiowrite(tv, ~0x570000, (reg & 0x560000) | tv->alteraclock);
21233ff48bf5SDavid du Colombier 	microdelay(10);
21243ff48bf5SDavid du Colombier 	*data = (ushort)bt848->gpiodata[0];
21253ff48bf5SDavid du Colombier 	microdelay(10);
21263ff48bf5SDavid du Colombier }
21273ff48bf5SDavid du Colombier 
21283ff48bf5SDavid du Colombier static void
kfirloadu(Tv * tv,uchar * u,int ulen)21293ff48bf5SDavid du Colombier kfirloadu(Tv *tv, uchar *u, int ulen)
21303ff48bf5SDavid du Colombier {
21313ff48bf5SDavid du Colombier 	Bt848 *bt848 = tv->bt848;
21323ff48bf5SDavid du Colombier 	int i, j;
21333ff48bf5SDavid du Colombier 
21343ff48bf5SDavid du Colombier 	ilock(&tv->kfirlock);
21353ff48bf5SDavid du Colombier 	bt848->gpioouten &= 0xff000000;
21363ff48bf5SDavid du Colombier 	bt848->gpioouten |= gpio_altera_data |
21373ff48bf5SDavid du Colombier 		gpio_altera_clock | gpio_altera_nconfig;
21383ff48bf5SDavid du Colombier 	bt848->gpiodata[0] &= 0xff000000;
21393ff48bf5SDavid du Colombier 	microdelay(10);
21403ff48bf5SDavid du Colombier 	bt848->gpiodata[0] |= gpio_altera_nconfig;
21413ff48bf5SDavid du Colombier 	microdelay(10);
21423ff48bf5SDavid du Colombier 
214341dd6b47SDavid du Colombier 	/* Download the microcode */
21443ff48bf5SDavid du Colombier 	for (i = 0; i != ulen; i++)
21453ff48bf5SDavid du Colombier 		for (j = 0; j != 8; j++) {
21463ff48bf5SDavid du Colombier 			bt848->gpiodata[0] &= ~(gpio_altera_clock|gpio_altera_data);
21473ff48bf5SDavid du Colombier 			if (u[i] & 1)
21483ff48bf5SDavid du Colombier 				bt848->gpiodata[0] |= gpio_altera_data;
21493ff48bf5SDavid du Colombier 			bt848->gpiodata[0] |= gpio_altera_clock;
21503ff48bf5SDavid du Colombier 			u[i] >>= 1;
21513ff48bf5SDavid du Colombier 		}
21523ff48bf5SDavid du Colombier 	bt848->gpiodata[0] &= ~gpio_altera_clock;
21533ff48bf5SDavid du Colombier 	microdelay(100);
21543ff48bf5SDavid du Colombier 
215541dd6b47SDavid du Colombier 	/* Initialize. */
21563ff48bf5SDavid du Colombier 	for (i = 0; i != 30; i++) {
21573ff48bf5SDavid du Colombier 		bt848->gpiodata[0] &= ~gpio_altera_clock;
21583ff48bf5SDavid du Colombier 		bt848->gpiodata[0] |= gpio_altera_clock;
21593ff48bf5SDavid du Colombier 	}
21603ff48bf5SDavid du Colombier 	bt848->gpiodata[0] &= ~(gpio_altera_clock|gpio_altera_data);
21613ff48bf5SDavid du Colombier 	iunlock(&tv->kfirlock);
21623ff48bf5SDavid du Colombier 
21633ff48bf5SDavid du Colombier 	tv->gpiostate = Gpioinit;
21643ff48bf5SDavid du Colombier }
21653ff48bf5SDavid du Colombier 
21663ff48bf5SDavid du Colombier static void
kfirreset(Tv * tv)21673ff48bf5SDavid du Colombier kfirreset(Tv *tv)
21683ff48bf5SDavid du Colombier {
21693ff48bf5SDavid du Colombier 	alterawrite(tv, 0, 0);
21703ff48bf5SDavid du Colombier 	microdelay(10);
21713ff48bf5SDavid du Colombier 	alterawrite(tv, 0x40000, 0);
21723ff48bf5SDavid du Colombier 	microdelay(10);
21733ff48bf5SDavid du Colombier 	alterawrite(tv, 0x40006, 0x80);
21743ff48bf5SDavid du Colombier 	microdelay(10);
21753ff48bf5SDavid du Colombier 	alterawrite(tv, 8, 1);
21763ff48bf5SDavid du Colombier 	microdelay(10);
21773ff48bf5SDavid du Colombier 	alterawrite(tv, 0x40004, 2);
21783ff48bf5SDavid du Colombier 	microdelay(10);
21793ff48bf5SDavid du Colombier 	alterawrite(tv, 4, 3);
21803ff48bf5SDavid du Colombier 	microdelay(3);
21813ff48bf5SDavid du Colombier }
21823ff48bf5SDavid du Colombier 
21833ff48bf5SDavid du Colombier static int
kfirinitialize(Tv * tv)21843ff48bf5SDavid du Colombier kfirinitialize(Tv *tv)
21853ff48bf5SDavid du Colombier {
218641dd6b47SDavid du Colombier 	/* Initialize parameters? */
21873ff48bf5SDavid du Colombier 
21883ff48bf5SDavid du Colombier 	tv->gpiostate = Gpioinit;
21893ff48bf5SDavid du Colombier 	tv->alterareg = -1;
21903ff48bf5SDavid du Colombier 	tv->alteraclock = 0x20000;
21913ff48bf5SDavid du Colombier 	kfirloadu(tv, hcwAMC, sizeof hcwAMC);
21923ff48bf5SDavid du Colombier 	kfirreset(tv);
21933ff48bf5SDavid du Colombier 	return 1;
21943ff48bf5SDavid du Colombier }
2195