162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci    bttv - Bt848 frame grabber driver
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci    Copyright (C) 1996,97,98 Ralph  Metzler <rjkm@thp.uni-koeln.de>
762306a36Sopenharmony_ci			   & Marcus Metzler <mocm@thp.uni-koeln.de>
862306a36Sopenharmony_ci    (c) 1999-2002 Gerd Knorr <kraxel@bytesex.org>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci    some v4l2 code lines are taken from Justin's bttv2 driver which is
1162306a36Sopenharmony_ci    (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci    V4L1 removal from:
1462306a36Sopenharmony_ci    (c) 2005-2006 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci    Fixes to be fully V4L2 compliant by
1762306a36Sopenharmony_ci    (c) 2006 Mauro Carvalho Chehab <mchehab@kernel.org>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci    Cropping and overscan support
2062306a36Sopenharmony_ci    Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
2162306a36Sopenharmony_ci    Sponsored by OPQ Systems AB
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci*/
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <linux/init.h>
2862306a36Sopenharmony_ci#include <linux/module.h>
2962306a36Sopenharmony_ci#include <linux/delay.h>
3062306a36Sopenharmony_ci#include <linux/slab.h>
3162306a36Sopenharmony_ci#include <linux/errno.h>
3262306a36Sopenharmony_ci#include <linux/fs.h>
3362306a36Sopenharmony_ci#include <linux/kernel.h>
3462306a36Sopenharmony_ci#include <linux/sched.h>
3562306a36Sopenharmony_ci#include <linux/interrupt.h>
3662306a36Sopenharmony_ci#include <linux/kdev_t.h>
3762306a36Sopenharmony_ci#include "bttvp.h"
3862306a36Sopenharmony_ci#include <media/v4l2-common.h>
3962306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
4062306a36Sopenharmony_ci#include <media/v4l2-event.h>
4162306a36Sopenharmony_ci#include <media/i2c/tvaudio.h>
4262306a36Sopenharmony_ci#include <media/drv-intf/msp3400.h>
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include <asm/io.h>
4762306a36Sopenharmony_ci#include <asm/byteorder.h>
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include <media/i2c/saa6588.h>
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define BTTV_VERSION "0.9.19"
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciunsigned int bttv_num;			/* number of Bt848s in use */
5462306a36Sopenharmony_cistruct bttv *bttvs[BTTV_MAX];
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ciunsigned int bttv_debug;
5762306a36Sopenharmony_ciunsigned int bttv_verbose = 1;
5862306a36Sopenharmony_ciunsigned int bttv_gpio;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* config variables */
6162306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
6262306a36Sopenharmony_cistatic unsigned int bigendian=1;
6362306a36Sopenharmony_ci#else
6462306a36Sopenharmony_cistatic unsigned int bigendian;
6562306a36Sopenharmony_ci#endif
6662306a36Sopenharmony_cistatic unsigned int radio[BTTV_MAX];
6762306a36Sopenharmony_cistatic unsigned int irq_debug;
6862306a36Sopenharmony_cistatic unsigned int gbuffers = 8;
6962306a36Sopenharmony_cistatic unsigned int gbufsize = 0x208000;
7062306a36Sopenharmony_cistatic unsigned int reset_crop = 1;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
7362306a36Sopenharmony_cistatic int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
7462306a36Sopenharmony_cistatic int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
7562306a36Sopenharmony_cistatic int debug_latency;
7662306a36Sopenharmony_cistatic int disable_ir;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic unsigned int fdsr;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* options */
8162306a36Sopenharmony_cistatic unsigned int combfilter;
8262306a36Sopenharmony_cistatic unsigned int lumafilter;
8362306a36Sopenharmony_cistatic unsigned int automute    = 1;
8462306a36Sopenharmony_cistatic unsigned int chroma_agc;
8562306a36Sopenharmony_cistatic unsigned int agc_crush   = 1;
8662306a36Sopenharmony_cistatic unsigned int whitecrush_upper = 0xCF;
8762306a36Sopenharmony_cistatic unsigned int whitecrush_lower = 0x7F;
8862306a36Sopenharmony_cistatic unsigned int vcr_hack;
8962306a36Sopenharmony_cistatic unsigned int irq_iswitch;
9062306a36Sopenharmony_cistatic unsigned int uv_ratio    = 50;
9162306a36Sopenharmony_cistatic unsigned int full_luma_range;
9262306a36Sopenharmony_cistatic unsigned int coring;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* API features (turn on/off stuff for testing) */
9562306a36Sopenharmony_cistatic unsigned int v4l2        = 1;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* insmod args */
9862306a36Sopenharmony_cimodule_param(bttv_verbose,      int, 0644);
9962306a36Sopenharmony_cimodule_param(bttv_gpio,         int, 0644);
10062306a36Sopenharmony_cimodule_param(bttv_debug,        int, 0644);
10162306a36Sopenharmony_cimodule_param(irq_debug,         int, 0644);
10262306a36Sopenharmony_cimodule_param(debug_latency,     int, 0644);
10362306a36Sopenharmony_cimodule_param(disable_ir,        int, 0444);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cimodule_param(fdsr,              int, 0444);
10662306a36Sopenharmony_cimodule_param(gbuffers,          int, 0444);
10762306a36Sopenharmony_cimodule_param(gbufsize,          int, 0444);
10862306a36Sopenharmony_cimodule_param(reset_crop,        int, 0444);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cimodule_param(v4l2,              int, 0644);
11162306a36Sopenharmony_cimodule_param(bigendian,         int, 0644);
11262306a36Sopenharmony_cimodule_param(irq_iswitch,       int, 0644);
11362306a36Sopenharmony_cimodule_param(combfilter,        int, 0444);
11462306a36Sopenharmony_cimodule_param(lumafilter,        int, 0444);
11562306a36Sopenharmony_cimodule_param(automute,          int, 0444);
11662306a36Sopenharmony_cimodule_param(chroma_agc,        int, 0444);
11762306a36Sopenharmony_cimodule_param(agc_crush,         int, 0444);
11862306a36Sopenharmony_cimodule_param(whitecrush_upper,  int, 0444);
11962306a36Sopenharmony_cimodule_param(whitecrush_lower,  int, 0444);
12062306a36Sopenharmony_cimodule_param(vcr_hack,          int, 0444);
12162306a36Sopenharmony_cimodule_param(uv_ratio,          int, 0444);
12262306a36Sopenharmony_cimodule_param(full_luma_range,   int, 0444);
12362306a36Sopenharmony_cimodule_param(coring,            int, 0444);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cimodule_param_array(radio,       int, NULL, 0444);
12662306a36Sopenharmony_cimodule_param_array(video_nr,    int, NULL, 0444);
12762306a36Sopenharmony_cimodule_param_array(radio_nr,    int, NULL, 0444);
12862306a36Sopenharmony_cimodule_param_array(vbi_nr,      int, NULL, 0444);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ciMODULE_PARM_DESC(radio, "The TV card supports radio, default is 0 (no)");
13162306a36Sopenharmony_ciMODULE_PARM_DESC(bigendian, "byte order of the framebuffer, default is native endian");
13262306a36Sopenharmony_ciMODULE_PARM_DESC(bttv_verbose, "verbose startup messages, default is 1 (yes)");
13362306a36Sopenharmony_ciMODULE_PARM_DESC(bttv_gpio, "log gpio changes, default is 0 (no)");
13462306a36Sopenharmony_ciMODULE_PARM_DESC(bttv_debug, "debug messages, default is 0 (no)");
13562306a36Sopenharmony_ciMODULE_PARM_DESC(irq_debug, "irq handler debug messages, default is 0 (no)");
13662306a36Sopenharmony_ciMODULE_PARM_DESC(disable_ir, "disable infrared remote support");
13762306a36Sopenharmony_ciMODULE_PARM_DESC(gbuffers, "number of capture buffers. range 2-32, default 8");
13862306a36Sopenharmony_ciMODULE_PARM_DESC(gbufsize, "size of the capture buffers, default is 0x208000");
13962306a36Sopenharmony_ciMODULE_PARM_DESC(reset_crop, "reset cropping parameters at open(), default is 1 (yes) for compatibility with older applications");
14062306a36Sopenharmony_ciMODULE_PARM_DESC(automute, "mute audio on bad/missing video signal, default is 1 (yes)");
14162306a36Sopenharmony_ciMODULE_PARM_DESC(chroma_agc, "enables the AGC of chroma signal, default is 0 (no)");
14262306a36Sopenharmony_ciMODULE_PARM_DESC(agc_crush, "enables the luminance AGC crush, default is 1 (yes)");
14362306a36Sopenharmony_ciMODULE_PARM_DESC(whitecrush_upper, "sets the white crush upper value, default is 207");
14462306a36Sopenharmony_ciMODULE_PARM_DESC(whitecrush_lower, "sets the white crush lower value, default is 127");
14562306a36Sopenharmony_ciMODULE_PARM_DESC(vcr_hack, "enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)");
14662306a36Sopenharmony_ciMODULE_PARM_DESC(irq_iswitch, "switch inputs in irq handler");
14762306a36Sopenharmony_ciMODULE_PARM_DESC(uv_ratio, "ratio between u and v gains, default is 50");
14862306a36Sopenharmony_ciMODULE_PARM_DESC(full_luma_range, "use the full luma range, default is 0 (no)");
14962306a36Sopenharmony_ciMODULE_PARM_DESC(coring, "set the luma coring level, default is 0 (no)");
15062306a36Sopenharmony_ciMODULE_PARM_DESC(video_nr, "video device numbers");
15162306a36Sopenharmony_ciMODULE_PARM_DESC(vbi_nr, "vbi device numbers");
15262306a36Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "radio device numbers");
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ciMODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
15562306a36Sopenharmony_ciMODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
15662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
15762306a36Sopenharmony_ciMODULE_VERSION(BTTV_VERSION);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#define V4L2_CID_PRIVATE_COMBFILTER		(V4L2_CID_USER_BTTV_BASE + 0)
16062306a36Sopenharmony_ci#define V4L2_CID_PRIVATE_AUTOMUTE		(V4L2_CID_USER_BTTV_BASE + 1)
16162306a36Sopenharmony_ci#define V4L2_CID_PRIVATE_LUMAFILTER		(V4L2_CID_USER_BTTV_BASE + 2)
16262306a36Sopenharmony_ci#define V4L2_CID_PRIVATE_AGC_CRUSH		(V4L2_CID_USER_BTTV_BASE + 3)
16362306a36Sopenharmony_ci#define V4L2_CID_PRIVATE_VCR_HACK		(V4L2_CID_USER_BTTV_BASE + 4)
16462306a36Sopenharmony_ci#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER	(V4L2_CID_USER_BTTV_BASE + 5)
16562306a36Sopenharmony_ci#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER	(V4L2_CID_USER_BTTV_BASE + 6)
16662306a36Sopenharmony_ci#define V4L2_CID_PRIVATE_UV_RATIO		(V4L2_CID_USER_BTTV_BASE + 7)
16762306a36Sopenharmony_ci#define V4L2_CID_PRIVATE_FULL_LUMA_RANGE	(V4L2_CID_USER_BTTV_BASE + 8)
16862306a36Sopenharmony_ci#define V4L2_CID_PRIVATE_CORING			(V4L2_CID_USER_BTTV_BASE + 9)
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
17162306a36Sopenharmony_ci/* sysfs                                                                   */
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic ssize_t card_show(struct device *cd,
17462306a36Sopenharmony_ci			 struct device_attribute *attr, char *buf)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct video_device *vfd = to_video_device(cd);
17762306a36Sopenharmony_ci	struct bttv *btv = video_get_drvdata(vfd);
17862306a36Sopenharmony_ci	return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(card);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
18362306a36Sopenharmony_ci/* dvb auto-load setup                                                     */
18462306a36Sopenharmony_ci#if defined(CONFIG_MODULES) && defined(MODULE)
18562306a36Sopenharmony_cistatic void request_module_async(struct work_struct *work)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	request_module("dvb-bt8xx");
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic void request_modules(struct bttv *dev)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	INIT_WORK(&dev->request_module_wk, request_module_async);
19362306a36Sopenharmony_ci	schedule_work(&dev->request_module_wk);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void flush_request_modules(struct bttv *dev)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	flush_work(&dev->request_module_wk);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci#else
20162306a36Sopenharmony_ci#define request_modules(dev)
20262306a36Sopenharmony_ci#define flush_request_modules(dev) do {} while(0)
20362306a36Sopenharmony_ci#endif /* CONFIG_MODULES */
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
20762306a36Sopenharmony_ci/* static data                                                             */
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/* special timing tables from conexant... */
21062306a36Sopenharmony_cistatic u8 SRAM_Table[][60] =
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	/* PAL digital input over GPIO[7:0] */
21362306a36Sopenharmony_ci	{
21462306a36Sopenharmony_ci		45, // 45 bytes following
21562306a36Sopenharmony_ci		0x36,0x11,0x01,0x00,0x90,0x02,0x05,0x10,0x04,0x16,
21662306a36Sopenharmony_ci		0x12,0x05,0x11,0x00,0x04,0x12,0xC0,0x00,0x31,0x00,
21762306a36Sopenharmony_ci		0x06,0x51,0x08,0x03,0x89,0x08,0x07,0xC0,0x44,0x00,
21862306a36Sopenharmony_ci		0x81,0x01,0x01,0xA9,0x0D,0x02,0x02,0x50,0x03,0x37,
21962306a36Sopenharmony_ci		0x37,0x00,0xAF,0x21,0x00
22062306a36Sopenharmony_ci	},
22162306a36Sopenharmony_ci	/* NTSC digital input over GPIO[7:0] */
22262306a36Sopenharmony_ci	{
22362306a36Sopenharmony_ci		51, // 51 bytes following
22462306a36Sopenharmony_ci		0x0C,0xC0,0x00,0x00,0x90,0x02,0x03,0x10,0x03,0x06,
22562306a36Sopenharmony_ci		0x10,0x04,0x12,0x12,0x05,0x02,0x13,0x04,0x19,0x00,
22662306a36Sopenharmony_ci		0x04,0x39,0x00,0x06,0x59,0x08,0x03,0x83,0x08,0x07,
22762306a36Sopenharmony_ci		0x03,0x50,0x00,0xC0,0x40,0x00,0x86,0x01,0x01,0xA6,
22862306a36Sopenharmony_ci		0x0D,0x02,0x03,0x11,0x01,0x05,0x37,0x00,0xAC,0x21,
22962306a36Sopenharmony_ci		0x00,
23062306a36Sopenharmony_ci	},
23162306a36Sopenharmony_ci	// TGB_NTSC392 // quartzsight
23262306a36Sopenharmony_ci	// This table has been modified to be used for Fusion Rev D
23362306a36Sopenharmony_ci	{
23462306a36Sopenharmony_ci		0x2A, // size of table = 42
23562306a36Sopenharmony_ci		0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24,
23662306a36Sopenharmony_ci		0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10,
23762306a36Sopenharmony_ci		0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00,
23862306a36Sopenharmony_ci		0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3,
23962306a36Sopenharmony_ci		0x20, 0x00
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci};
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci/* minhdelayx1	first video pixel we can capture on a line and
24462306a36Sopenharmony_ci   hdelayx1	start of active video, both relative to rising edge of
24562306a36Sopenharmony_ci		/HRESET pulse (0H) in 1 / fCLKx1.
24662306a36Sopenharmony_ci   swidth	width of active video and
24762306a36Sopenharmony_ci   totalwidth	total line width, both in 1 / fCLKx1.
24862306a36Sopenharmony_ci   sqwidth	total line width in square pixels.
24962306a36Sopenharmony_ci   vdelay	start of active video in 2 * field lines relative to
25062306a36Sopenharmony_ci		trailing edge of /VRESET pulse (VDELAY register).
25162306a36Sopenharmony_ci   sheight	height of active video in 2 * field lines.
25262306a36Sopenharmony_ci   extraheight	Added to sheight for cropcap.bounds.height only
25362306a36Sopenharmony_ci   videostart0	ITU-R frame line number of the line corresponding
25462306a36Sopenharmony_ci		to vdelay in the first field. */
25562306a36Sopenharmony_ci#define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth,	 \
25662306a36Sopenharmony_ci		vdelay, sheight, extraheight, videostart0)		 \
25762306a36Sopenharmony_ci	.cropcap.bounds.left = minhdelayx1,				 \
25862306a36Sopenharmony_ci	/* * 2 because vertically we count field lines times two, */	 \
25962306a36Sopenharmony_ci	/* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */		 \
26062306a36Sopenharmony_ci	.cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \
26162306a36Sopenharmony_ci	/* 4 is a safety margin at the end of the line. */		 \
26262306a36Sopenharmony_ci	.cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4,	 \
26362306a36Sopenharmony_ci	.cropcap.bounds.height = (sheight) + (extraheight) + (vdelay) -	 \
26462306a36Sopenharmony_ci				 MIN_VDELAY,				 \
26562306a36Sopenharmony_ci	.cropcap.defrect.left = hdelayx1,				 \
26662306a36Sopenharmony_ci	.cropcap.defrect.top = (videostart0) * 2,			 \
26762306a36Sopenharmony_ci	.cropcap.defrect.width = swidth,				 \
26862306a36Sopenharmony_ci	.cropcap.defrect.height = sheight,				 \
26962306a36Sopenharmony_ci	.cropcap.pixelaspect.numerator = totalwidth,			 \
27062306a36Sopenharmony_ci	.cropcap.pixelaspect.denominator = sqwidth,
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ciconst struct bttv_tvnorm bttv_tvnorms[] = {
27362306a36Sopenharmony_ci	/* PAL-BDGHI */
27462306a36Sopenharmony_ci	/* max. active video is actually 922, but 924 is divisible by 4 and 3! */
27562306a36Sopenharmony_ci	/* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
27662306a36Sopenharmony_ci	{
27762306a36Sopenharmony_ci		.v4l2_id        = V4L2_STD_PAL,
27862306a36Sopenharmony_ci		.name           = "PAL",
27962306a36Sopenharmony_ci		.Fsc            = 35468950,
28062306a36Sopenharmony_ci		.swidth         = 924,
28162306a36Sopenharmony_ci		.sheight        = 576,
28262306a36Sopenharmony_ci		.totalwidth     = 1135,
28362306a36Sopenharmony_ci		.adelay         = 0x7f,
28462306a36Sopenharmony_ci		.bdelay         = 0x72,
28562306a36Sopenharmony_ci		.iform          = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
28662306a36Sopenharmony_ci		.scaledtwidth   = 1135,
28762306a36Sopenharmony_ci		.hdelayx1       = 186,
28862306a36Sopenharmony_ci		.hactivex1      = 924,
28962306a36Sopenharmony_ci		.vdelay         = 0x20,
29062306a36Sopenharmony_ci		.vbipack        = 255, /* min (2048 / 4, 0x1ff) & 0xff */
29162306a36Sopenharmony_ci		.sram           = 0,
29262306a36Sopenharmony_ci		/* ITU-R frame line number of the first VBI line
29362306a36Sopenharmony_ci		   we can capture, of the first and second field.
29462306a36Sopenharmony_ci		   The last line is determined by cropcap.bounds. */
29562306a36Sopenharmony_ci		.vbistart       = { 7, 320 },
29662306a36Sopenharmony_ci		CROPCAP(/* minhdelayx1 */ 68,
29762306a36Sopenharmony_ci			/* hdelayx1 */ 186,
29862306a36Sopenharmony_ci			/* Should be (768 * 1135 + 944 / 2) / 944.
29962306a36Sopenharmony_ci			   cropcap.defrect is used for image width
30062306a36Sopenharmony_ci			   checks, so we keep the old value 924. */
30162306a36Sopenharmony_ci			/* swidth */ 924,
30262306a36Sopenharmony_ci			/* totalwidth */ 1135,
30362306a36Sopenharmony_ci			/* sqwidth */ 944,
30462306a36Sopenharmony_ci			/* vdelay */ 0x20,
30562306a36Sopenharmony_ci			/* sheight */ 576,
30662306a36Sopenharmony_ci			/* bt878 (and bt848?) can capture another
30762306a36Sopenharmony_ci			   line below active video. */
30862306a36Sopenharmony_ci			/* extraheight */ 2,
30962306a36Sopenharmony_ci			/* videostart0 */ 23)
31062306a36Sopenharmony_ci	},{
31162306a36Sopenharmony_ci		.v4l2_id        = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
31262306a36Sopenharmony_ci		.name           = "NTSC",
31362306a36Sopenharmony_ci		.Fsc            = 28636363,
31462306a36Sopenharmony_ci		.swidth         = 768,
31562306a36Sopenharmony_ci		.sheight        = 480,
31662306a36Sopenharmony_ci		.totalwidth     = 910,
31762306a36Sopenharmony_ci		.adelay         = 0x68,
31862306a36Sopenharmony_ci		.bdelay         = 0x5d,
31962306a36Sopenharmony_ci		.iform          = (BT848_IFORM_NTSC|BT848_IFORM_XT0),
32062306a36Sopenharmony_ci		.scaledtwidth   = 910,
32162306a36Sopenharmony_ci		.hdelayx1       = 128,
32262306a36Sopenharmony_ci		.hactivex1      = 910,
32362306a36Sopenharmony_ci		.vdelay         = 0x1a,
32462306a36Sopenharmony_ci		.vbipack        = 144, /* min (1600 / 4, 0x1ff) & 0xff */
32562306a36Sopenharmony_ci		.sram           = 1,
32662306a36Sopenharmony_ci		.vbistart	= { 10, 273 },
32762306a36Sopenharmony_ci		CROPCAP(/* minhdelayx1 */ 68,
32862306a36Sopenharmony_ci			/* hdelayx1 */ 128,
32962306a36Sopenharmony_ci			/* Should be (640 * 910 + 780 / 2) / 780? */
33062306a36Sopenharmony_ci			/* swidth */ 768,
33162306a36Sopenharmony_ci			/* totalwidth */ 910,
33262306a36Sopenharmony_ci			/* sqwidth */ 780,
33362306a36Sopenharmony_ci			/* vdelay */ 0x1a,
33462306a36Sopenharmony_ci			/* sheight */ 480,
33562306a36Sopenharmony_ci			/* extraheight */ 0,
33662306a36Sopenharmony_ci			/* videostart0 */ 23)
33762306a36Sopenharmony_ci	},{
33862306a36Sopenharmony_ci		.v4l2_id        = V4L2_STD_SECAM,
33962306a36Sopenharmony_ci		.name           = "SECAM",
34062306a36Sopenharmony_ci		.Fsc            = 35468950,
34162306a36Sopenharmony_ci		.swidth         = 924,
34262306a36Sopenharmony_ci		.sheight        = 576,
34362306a36Sopenharmony_ci		.totalwidth     = 1135,
34462306a36Sopenharmony_ci		.adelay         = 0x7f,
34562306a36Sopenharmony_ci		.bdelay         = 0xb0,
34662306a36Sopenharmony_ci		.iform          = (BT848_IFORM_SECAM|BT848_IFORM_XT1),
34762306a36Sopenharmony_ci		.scaledtwidth   = 1135,
34862306a36Sopenharmony_ci		.hdelayx1       = 186,
34962306a36Sopenharmony_ci		.hactivex1      = 922,
35062306a36Sopenharmony_ci		.vdelay         = 0x20,
35162306a36Sopenharmony_ci		.vbipack        = 255,
35262306a36Sopenharmony_ci		.sram           = 0, /* like PAL, correct? */
35362306a36Sopenharmony_ci		.vbistart	= { 7, 320 },
35462306a36Sopenharmony_ci		CROPCAP(/* minhdelayx1 */ 68,
35562306a36Sopenharmony_ci			/* hdelayx1 */ 186,
35662306a36Sopenharmony_ci			/* swidth */ 924,
35762306a36Sopenharmony_ci			/* totalwidth */ 1135,
35862306a36Sopenharmony_ci			/* sqwidth */ 944,
35962306a36Sopenharmony_ci			/* vdelay */ 0x20,
36062306a36Sopenharmony_ci			/* sheight */ 576,
36162306a36Sopenharmony_ci			/* extraheight */ 0,
36262306a36Sopenharmony_ci			/* videostart0 */ 23)
36362306a36Sopenharmony_ci	},{
36462306a36Sopenharmony_ci		.v4l2_id        = V4L2_STD_PAL_Nc,
36562306a36Sopenharmony_ci		.name           = "PAL-Nc",
36662306a36Sopenharmony_ci		.Fsc            = 28636363,
36762306a36Sopenharmony_ci		.swidth         = 640,
36862306a36Sopenharmony_ci		.sheight        = 576,
36962306a36Sopenharmony_ci		.totalwidth     = 910,
37062306a36Sopenharmony_ci		.adelay         = 0x68,
37162306a36Sopenharmony_ci		.bdelay         = 0x5d,
37262306a36Sopenharmony_ci		.iform          = (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
37362306a36Sopenharmony_ci		.scaledtwidth   = 780,
37462306a36Sopenharmony_ci		.hdelayx1       = 130,
37562306a36Sopenharmony_ci		.hactivex1      = 734,
37662306a36Sopenharmony_ci		.vdelay         = 0x1a,
37762306a36Sopenharmony_ci		.vbipack        = 144,
37862306a36Sopenharmony_ci		.sram           = -1,
37962306a36Sopenharmony_ci		.vbistart	= { 7, 320 },
38062306a36Sopenharmony_ci		CROPCAP(/* minhdelayx1 */ 68,
38162306a36Sopenharmony_ci			/* hdelayx1 */ 130,
38262306a36Sopenharmony_ci			/* swidth */ (640 * 910 + 780 / 2) / 780,
38362306a36Sopenharmony_ci			/* totalwidth */ 910,
38462306a36Sopenharmony_ci			/* sqwidth */ 780,
38562306a36Sopenharmony_ci			/* vdelay */ 0x1a,
38662306a36Sopenharmony_ci			/* sheight */ 576,
38762306a36Sopenharmony_ci			/* extraheight */ 0,
38862306a36Sopenharmony_ci			/* videostart0 */ 23)
38962306a36Sopenharmony_ci	},{
39062306a36Sopenharmony_ci		.v4l2_id        = V4L2_STD_PAL_M,
39162306a36Sopenharmony_ci		.name           = "PAL-M",
39262306a36Sopenharmony_ci		.Fsc            = 28636363,
39362306a36Sopenharmony_ci		.swidth         = 640,
39462306a36Sopenharmony_ci		.sheight        = 480,
39562306a36Sopenharmony_ci		.totalwidth     = 910,
39662306a36Sopenharmony_ci		.adelay         = 0x68,
39762306a36Sopenharmony_ci		.bdelay         = 0x5d,
39862306a36Sopenharmony_ci		.iform          = (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
39962306a36Sopenharmony_ci		.scaledtwidth   = 780,
40062306a36Sopenharmony_ci		.hdelayx1       = 135,
40162306a36Sopenharmony_ci		.hactivex1      = 754,
40262306a36Sopenharmony_ci		.vdelay         = 0x1a,
40362306a36Sopenharmony_ci		.vbipack        = 144,
40462306a36Sopenharmony_ci		.sram           = -1,
40562306a36Sopenharmony_ci		.vbistart	= { 10, 273 },
40662306a36Sopenharmony_ci		CROPCAP(/* minhdelayx1 */ 68,
40762306a36Sopenharmony_ci			/* hdelayx1 */ 135,
40862306a36Sopenharmony_ci			/* swidth */ (640 * 910 + 780 / 2) / 780,
40962306a36Sopenharmony_ci			/* totalwidth */ 910,
41062306a36Sopenharmony_ci			/* sqwidth */ 780,
41162306a36Sopenharmony_ci			/* vdelay */ 0x1a,
41262306a36Sopenharmony_ci			/* sheight */ 480,
41362306a36Sopenharmony_ci			/* extraheight */ 0,
41462306a36Sopenharmony_ci			/* videostart0 */ 23)
41562306a36Sopenharmony_ci	},{
41662306a36Sopenharmony_ci		.v4l2_id        = V4L2_STD_PAL_N,
41762306a36Sopenharmony_ci		.name           = "PAL-N",
41862306a36Sopenharmony_ci		.Fsc            = 35468950,
41962306a36Sopenharmony_ci		.swidth         = 768,
42062306a36Sopenharmony_ci		.sheight        = 576,
42162306a36Sopenharmony_ci		.totalwidth     = 1135,
42262306a36Sopenharmony_ci		.adelay         = 0x7f,
42362306a36Sopenharmony_ci		.bdelay         = 0x72,
42462306a36Sopenharmony_ci		.iform          = (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
42562306a36Sopenharmony_ci		.scaledtwidth   = 944,
42662306a36Sopenharmony_ci		.hdelayx1       = 186,
42762306a36Sopenharmony_ci		.hactivex1      = 922,
42862306a36Sopenharmony_ci		.vdelay         = 0x20,
42962306a36Sopenharmony_ci		.vbipack        = 144,
43062306a36Sopenharmony_ci		.sram           = -1,
43162306a36Sopenharmony_ci		.vbistart       = { 7, 320 },
43262306a36Sopenharmony_ci		CROPCAP(/* minhdelayx1 */ 68,
43362306a36Sopenharmony_ci			/* hdelayx1 */ 186,
43462306a36Sopenharmony_ci			/* swidth */ (768 * 1135 + 944 / 2) / 944,
43562306a36Sopenharmony_ci			/* totalwidth */ 1135,
43662306a36Sopenharmony_ci			/* sqwidth */ 944,
43762306a36Sopenharmony_ci			/* vdelay */ 0x20,
43862306a36Sopenharmony_ci			/* sheight */ 576,
43962306a36Sopenharmony_ci			/* extraheight */ 0,
44062306a36Sopenharmony_ci			/* videostart0 */ 23)
44162306a36Sopenharmony_ci	},{
44262306a36Sopenharmony_ci		.v4l2_id        = V4L2_STD_NTSC_M_JP,
44362306a36Sopenharmony_ci		.name           = "NTSC-JP",
44462306a36Sopenharmony_ci		.Fsc            = 28636363,
44562306a36Sopenharmony_ci		.swidth         = 640,
44662306a36Sopenharmony_ci		.sheight        = 480,
44762306a36Sopenharmony_ci		.totalwidth     = 910,
44862306a36Sopenharmony_ci		.adelay         = 0x68,
44962306a36Sopenharmony_ci		.bdelay         = 0x5d,
45062306a36Sopenharmony_ci		.iform          = (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
45162306a36Sopenharmony_ci		.scaledtwidth   = 780,
45262306a36Sopenharmony_ci		.hdelayx1       = 135,
45362306a36Sopenharmony_ci		.hactivex1      = 754,
45462306a36Sopenharmony_ci		.vdelay         = 0x16,
45562306a36Sopenharmony_ci		.vbipack        = 144,
45662306a36Sopenharmony_ci		.sram           = -1,
45762306a36Sopenharmony_ci		.vbistart       = { 10, 273 },
45862306a36Sopenharmony_ci		CROPCAP(/* minhdelayx1 */ 68,
45962306a36Sopenharmony_ci			/* hdelayx1 */ 135,
46062306a36Sopenharmony_ci			/* swidth */ (640 * 910 + 780 / 2) / 780,
46162306a36Sopenharmony_ci			/* totalwidth */ 910,
46262306a36Sopenharmony_ci			/* sqwidth */ 780,
46362306a36Sopenharmony_ci			/* vdelay */ 0x16,
46462306a36Sopenharmony_ci			/* sheight */ 480,
46562306a36Sopenharmony_ci			/* extraheight */ 0,
46662306a36Sopenharmony_ci			/* videostart0 */ 23)
46762306a36Sopenharmony_ci	},{
46862306a36Sopenharmony_ci		/* that one hopefully works with the strange timing
46962306a36Sopenharmony_ci		 * which video recorders produce when playing a NTSC
47062306a36Sopenharmony_ci		 * tape on a PAL TV ... */
47162306a36Sopenharmony_ci		.v4l2_id        = V4L2_STD_PAL_60,
47262306a36Sopenharmony_ci		.name           = "PAL-60",
47362306a36Sopenharmony_ci		.Fsc            = 35468950,
47462306a36Sopenharmony_ci		.swidth         = 924,
47562306a36Sopenharmony_ci		.sheight        = 480,
47662306a36Sopenharmony_ci		.totalwidth     = 1135,
47762306a36Sopenharmony_ci		.adelay         = 0x7f,
47862306a36Sopenharmony_ci		.bdelay         = 0x72,
47962306a36Sopenharmony_ci		.iform          = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
48062306a36Sopenharmony_ci		.scaledtwidth   = 1135,
48162306a36Sopenharmony_ci		.hdelayx1       = 186,
48262306a36Sopenharmony_ci		.hactivex1      = 924,
48362306a36Sopenharmony_ci		.vdelay         = 0x1a,
48462306a36Sopenharmony_ci		.vbipack        = 255,
48562306a36Sopenharmony_ci		.vtotal         = 524,
48662306a36Sopenharmony_ci		.sram           = -1,
48762306a36Sopenharmony_ci		.vbistart	= { 10, 273 },
48862306a36Sopenharmony_ci		CROPCAP(/* minhdelayx1 */ 68,
48962306a36Sopenharmony_ci			/* hdelayx1 */ 186,
49062306a36Sopenharmony_ci			/* swidth */ 924,
49162306a36Sopenharmony_ci			/* totalwidth */ 1135,
49262306a36Sopenharmony_ci			/* sqwidth */ 944,
49362306a36Sopenharmony_ci			/* vdelay */ 0x1a,
49462306a36Sopenharmony_ci			/* sheight */ 480,
49562306a36Sopenharmony_ci			/* extraheight */ 0,
49662306a36Sopenharmony_ci			/* videostart0 */ 23)
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci};
49962306a36Sopenharmony_cistatic const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
50262306a36Sopenharmony_ci/* bttv format list
50362306a36Sopenharmony_ci   packed pixel formats must come first */
50462306a36Sopenharmony_cistatic const struct bttv_format formats[] = {
50562306a36Sopenharmony_ci	{
50662306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_GREY,
50762306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_Y8,
50862306a36Sopenharmony_ci		.depth    = 8,
50962306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
51062306a36Sopenharmony_ci	},{
51162306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_HI240,
51262306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_RGB8,
51362306a36Sopenharmony_ci		.depth    = 8,
51462306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
51562306a36Sopenharmony_ci	},{
51662306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_RGB555,
51762306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_RGB15,
51862306a36Sopenharmony_ci		.depth    = 16,
51962306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
52062306a36Sopenharmony_ci	},{
52162306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_RGB555X,
52262306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_RGB15,
52362306a36Sopenharmony_ci		.btswap   = 0x03, /* byteswap */
52462306a36Sopenharmony_ci		.depth    = 16,
52562306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
52662306a36Sopenharmony_ci	},{
52762306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_RGB565,
52862306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_RGB16,
52962306a36Sopenharmony_ci		.depth    = 16,
53062306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
53162306a36Sopenharmony_ci	},{
53262306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_RGB565X,
53362306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_RGB16,
53462306a36Sopenharmony_ci		.btswap   = 0x03, /* byteswap */
53562306a36Sopenharmony_ci		.depth    = 16,
53662306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
53762306a36Sopenharmony_ci	},{
53862306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_BGR24,
53962306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_RGB24,
54062306a36Sopenharmony_ci		.depth    = 24,
54162306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
54262306a36Sopenharmony_ci	},{
54362306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_BGR32,
54462306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_RGB32,
54562306a36Sopenharmony_ci		.depth    = 32,
54662306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
54762306a36Sopenharmony_ci	},{
54862306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_RGB32,
54962306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_RGB32,
55062306a36Sopenharmony_ci		.btswap   = 0x0f, /* byte+word swap */
55162306a36Sopenharmony_ci		.depth    = 32,
55262306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
55362306a36Sopenharmony_ci	},{
55462306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YUYV,
55562306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_YUY2,
55662306a36Sopenharmony_ci		.depth    = 16,
55762306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
55862306a36Sopenharmony_ci	},{
55962306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_UYVY,
56062306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_YUY2,
56162306a36Sopenharmony_ci		.btswap   = 0x03, /* byteswap */
56262306a36Sopenharmony_ci		.depth    = 16,
56362306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
56462306a36Sopenharmony_ci	},{
56562306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YUV422P,
56662306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_YCrCb422,
56762306a36Sopenharmony_ci		.depth    = 16,
56862306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PLANAR,
56962306a36Sopenharmony_ci		.hshift   = 1,
57062306a36Sopenharmony_ci		.vshift   = 0,
57162306a36Sopenharmony_ci	},{
57262306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YUV420,
57362306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_YCrCb422,
57462306a36Sopenharmony_ci		.depth    = 12,
57562306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PLANAR,
57662306a36Sopenharmony_ci		.hshift   = 1,
57762306a36Sopenharmony_ci		.vshift   = 1,
57862306a36Sopenharmony_ci	},{
57962306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YVU420,
58062306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_YCrCb422,
58162306a36Sopenharmony_ci		.depth    = 12,
58262306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
58362306a36Sopenharmony_ci		.hshift   = 1,
58462306a36Sopenharmony_ci		.vshift   = 1,
58562306a36Sopenharmony_ci	},{
58662306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YUV411P,
58762306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_YCrCb411,
58862306a36Sopenharmony_ci		.depth    = 12,
58962306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PLANAR,
59062306a36Sopenharmony_ci		.hshift   = 2,
59162306a36Sopenharmony_ci		.vshift   = 0,
59262306a36Sopenharmony_ci	},{
59362306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YUV410,
59462306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_YCrCb411,
59562306a36Sopenharmony_ci		.depth    = 9,
59662306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PLANAR,
59762306a36Sopenharmony_ci		.hshift   = 2,
59862306a36Sopenharmony_ci		.vshift   = 2,
59962306a36Sopenharmony_ci	},{
60062306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YVU410,
60162306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_YCrCb411,
60262306a36Sopenharmony_ci		.depth    = 9,
60362306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
60462306a36Sopenharmony_ci		.hshift   = 2,
60562306a36Sopenharmony_ci		.vshift   = 2,
60662306a36Sopenharmony_ci	},{
60762306a36Sopenharmony_ci		.fourcc   = -1,
60862306a36Sopenharmony_ci		.btformat = BT848_COLOR_FMT_RAW,
60962306a36Sopenharmony_ci		.depth    = 8,
61062306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_RAW,
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci};
61362306a36Sopenharmony_cistatic const unsigned int FORMATS = ARRAY_SIZE(formats);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
61662306a36Sopenharmony_ci/* resource management                                                     */
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci/*
61962306a36Sopenharmony_ci   RESOURCE_    allocated by                freed by
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci   VIDEO_READ   bttv_read 1)                bttv_read 2)
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci   VIDEO_STREAM VIDIOC_STREAMON             VIDIOC_STREAMOFF
62462306a36Sopenharmony_ci		 VIDIOC_QBUF 1)              bttv_release
62562306a36Sopenharmony_ci		 VIDIOCMCAPTURE 1)
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci   VBI		 VIDIOC_STREAMON             VIDIOC_STREAMOFF
62862306a36Sopenharmony_ci		 VIDIOC_QBUF 1)              bttv_release
62962306a36Sopenharmony_ci		 bttv_read, bttv_poll 1) 3)
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci   1) The resource must be allocated when we enter buffer prepare functions
63262306a36Sopenharmony_ci      and remain allocated while buffers are in the DMA queue.
63362306a36Sopenharmony_ci   2) This is a single frame read.
63462306a36Sopenharmony_ci   3) This is a continuous read, implies VIDIOC_STREAMON.
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci   Note this driver permits video input and standard changes regardless if
63762306a36Sopenharmony_ci   resources are allocated.
63862306a36Sopenharmony_ci*/
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci#define VBI_RESOURCES (RESOURCE_VBI)
64162306a36Sopenharmony_ci#define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \
64262306a36Sopenharmony_ci			 RESOURCE_VIDEO_STREAM)
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ciint check_alloc_btres_lock(struct bttv *btv, int bit)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	int xbits; /* mutual exclusive resources */
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	xbits = bit;
64962306a36Sopenharmony_ci	if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM))
65062306a36Sopenharmony_ci		xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	/* is it free? */
65362306a36Sopenharmony_ci	if (btv->resources & xbits) {
65462306a36Sopenharmony_ci		/* no, someone else uses it */
65562306a36Sopenharmony_ci		goto fail;
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if ((bit & VIDEO_RESOURCES)
65962306a36Sopenharmony_ci	    && 0 == (btv->resources & VIDEO_RESOURCES)) {
66062306a36Sopenharmony_ci		/* Do crop - use current, don't - use default parameters. */
66162306a36Sopenharmony_ci		__s32 top = btv->crop[!!btv->do_crop].rect.top;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		if (btv->vbi_end > top)
66462306a36Sopenharmony_ci			goto fail;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		/* We cannot capture the same line as video and VBI data.
66762306a36Sopenharmony_ci		   Claim scan lines crop[].rect.top to bottom. */
66862306a36Sopenharmony_ci		btv->crop_start = top;
66962306a36Sopenharmony_ci	} else if (bit & VBI_RESOURCES) {
67062306a36Sopenharmony_ci		__s32 end = btv->vbi_fmt.end;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		if (end > btv->crop_start)
67362306a36Sopenharmony_ci			goto fail;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		/* Claim scan lines above btv->vbi_fmt.end. */
67662306a36Sopenharmony_ci		btv->vbi_end = end;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* it's free, grab it */
68062306a36Sopenharmony_ci	btv->resources |= bit;
68162306a36Sopenharmony_ci	return 1;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci fail:
68462306a36Sopenharmony_ci	return 0;
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic
68862306a36Sopenharmony_ciint check_btres(struct bttv *btv, int bit)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	return (btv->resources & bit);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic
69462306a36Sopenharmony_ciint locked_btres(struct bttv *btv, int bit)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	return (btv->resources & bit);
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci/* Call with btv->lock down. */
70062306a36Sopenharmony_cistatic void
70162306a36Sopenharmony_cidisclaim_vbi_lines(struct bttv *btv)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	btv->vbi_end = 0;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci/* Call with btv->lock down. */
70762306a36Sopenharmony_cistatic void
70862306a36Sopenharmony_cidisclaim_video_lines(struct bttv *btv)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	const struct bttv_tvnorm *tvnorm;
71162306a36Sopenharmony_ci	u8 crop;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	tvnorm = &bttv_tvnorms[btv->tvnorm];
71462306a36Sopenharmony_ci	btv->crop_start = tvnorm->cropcap.bounds.top
71562306a36Sopenharmony_ci		+ tvnorm->cropcap.bounds.height;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	/* VBI capturing ends at VDELAY, start of video capturing, no
71862306a36Sopenharmony_ci	   matter how many lines the VBI RISC program expects. When video
71962306a36Sopenharmony_ci	   capturing is off, it shall no longer "preempt" VBI capturing,
72062306a36Sopenharmony_ci	   so we set VDELAY to maximum. */
72162306a36Sopenharmony_ci	crop = btread(BT848_E_CROP) | 0xc0;
72262306a36Sopenharmony_ci	btwrite(crop, BT848_E_CROP);
72362306a36Sopenharmony_ci	btwrite(0xfe, BT848_E_VDELAY_LO);
72462306a36Sopenharmony_ci	btwrite(crop, BT848_O_CROP);
72562306a36Sopenharmony_ci	btwrite(0xfe, BT848_O_VDELAY_LO);
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_civoid free_btres_lock(struct bttv *btv, int bits)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	if ((btv->resources & bits) != bits) {
73162306a36Sopenharmony_ci		/* trying to free resources not allocated by us ... */
73262306a36Sopenharmony_ci		pr_err("BUG! (btres)\n");
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci	btv->resources &= ~bits;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	bits = btv->resources;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (0 == (bits & VIDEO_RESOURCES))
73962306a36Sopenharmony_ci		disclaim_video_lines(btv);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (0 == (bits & VBI_RESOURCES))
74262306a36Sopenharmony_ci		disclaim_vbi_lines(btv);
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
74662306a36Sopenharmony_ci/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC          */
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C
74962306a36Sopenharmony_ci   PLL_X = Reference pre-divider (0=1, 1=2)
75062306a36Sopenharmony_ci   PLL_C = Post divider (0=6, 1=4)
75162306a36Sopenharmony_ci   PLL_I = Integer input
75262306a36Sopenharmony_ci   PLL_F = Fractional input
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci   F_input = 28.636363 MHz:
75562306a36Sopenharmony_ci   PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
75662306a36Sopenharmony_ci*/
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cistatic void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	unsigned char fl, fh, fi;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	/* prevent overflows */
76362306a36Sopenharmony_ci	fin/=4;
76462306a36Sopenharmony_ci	fout/=4;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	fout*=12;
76762306a36Sopenharmony_ci	fi=fout/fin;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	fout=(fout%fin)*256;
77062306a36Sopenharmony_ci	fh=fout/fin;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	fout=(fout%fin)*256;
77362306a36Sopenharmony_ci	fl=fout/fin;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	btwrite(fl, BT848_PLL_F_LO);
77662306a36Sopenharmony_ci	btwrite(fh, BT848_PLL_F_HI);
77762306a36Sopenharmony_ci	btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cistatic void set_pll(struct bttv *btv)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	int i;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (!btv->pll.pll_crystal)
78562306a36Sopenharmony_ci		return;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (btv->pll.pll_ofreq == btv->pll.pll_current) {
78862306a36Sopenharmony_ci		dprintk("%d: PLL: no change required\n", btv->c.nr);
78962306a36Sopenharmony_ci		return;
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
79362306a36Sopenharmony_ci		/* no PLL needed */
79462306a36Sopenharmony_ci		if (btv->pll.pll_current == 0)
79562306a36Sopenharmony_ci			return;
79662306a36Sopenharmony_ci		if (bttv_verbose)
79762306a36Sopenharmony_ci			pr_info("%d: PLL can sleep, using XTAL (%d)\n",
79862306a36Sopenharmony_ci				btv->c.nr, btv->pll.pll_ifreq);
79962306a36Sopenharmony_ci		btwrite(0x00,BT848_TGCTRL);
80062306a36Sopenharmony_ci		btwrite(0x00,BT848_PLL_XCI);
80162306a36Sopenharmony_ci		btv->pll.pll_current = 0;
80262306a36Sopenharmony_ci		return;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	if (bttv_verbose)
80662306a36Sopenharmony_ci		pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n",
80762306a36Sopenharmony_ci			btv->c.nr,
80862306a36Sopenharmony_ci			btv->pll.pll_ifreq, btv->pll.pll_ofreq);
80962306a36Sopenharmony_ci	set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	for (i=0; i<10; i++) {
81262306a36Sopenharmony_ci		/*  Let other people run while the PLL stabilizes */
81362306a36Sopenharmony_ci		msleep(10);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci		if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
81662306a36Sopenharmony_ci			btwrite(0,BT848_DSTATUS);
81762306a36Sopenharmony_ci		} else {
81862306a36Sopenharmony_ci			btwrite(0x08,BT848_TGCTRL);
81962306a36Sopenharmony_ci			btv->pll.pll_current = btv->pll.pll_ofreq;
82062306a36Sopenharmony_ci			if (bttv_verbose)
82162306a36Sopenharmony_ci				pr_info("PLL set ok\n");
82262306a36Sopenharmony_ci			return;
82362306a36Sopenharmony_ci		}
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci	btv->pll.pll_current = -1;
82662306a36Sopenharmony_ci	if (bttv_verbose)
82762306a36Sopenharmony_ci		pr_info("Setting PLL failed\n");
82862306a36Sopenharmony_ci	return;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci/* used to switch between the bt848's analog/digital video capture modes */
83262306a36Sopenharmony_cistatic void bt848A_set_timing(struct bttv *btv)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	int i, len;
83562306a36Sopenharmony_ci	int table_idx = bttv_tvnorms[btv->tvnorm].sram;
83662306a36Sopenharmony_ci	int fsc       = bttv_tvnorms[btv->tvnorm].Fsc;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (btv->input == btv->dig) {
83962306a36Sopenharmony_ci		dprintk("%d: load digital timing table (table_idx=%d)\n",
84062306a36Sopenharmony_ci			btv->c.nr,table_idx);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		/* timing change...reset timing generator address */
84362306a36Sopenharmony_ci		btwrite(0x00, BT848_TGCTRL);
84462306a36Sopenharmony_ci		btwrite(0x02, BT848_TGCTRL);
84562306a36Sopenharmony_ci		btwrite(0x00, BT848_TGCTRL);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci		len=SRAM_Table[table_idx][0];
84862306a36Sopenharmony_ci		for(i = 1; i <= len; i++)
84962306a36Sopenharmony_ci			btwrite(SRAM_Table[table_idx][i],BT848_TGLB);
85062306a36Sopenharmony_ci		btv->pll.pll_ofreq = 27000000;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		set_pll(btv);
85362306a36Sopenharmony_ci		btwrite(0x11, BT848_TGCTRL);
85462306a36Sopenharmony_ci		btwrite(0x41, BT848_DVSIF);
85562306a36Sopenharmony_ci	} else {
85662306a36Sopenharmony_ci		btv->pll.pll_ofreq = fsc;
85762306a36Sopenharmony_ci		set_pll(btv);
85862306a36Sopenharmony_ci		btwrite(0x0, BT848_DVSIF);
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic void bt848_bright(struct bttv *btv, int bright)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	int value;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	// printk("set bright: %d\n", bright); // DEBUG
86962306a36Sopenharmony_ci	btv->bright = bright;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	/* We want -128 to 127 we get 0-65535 */
87262306a36Sopenharmony_ci	value = (bright >> 8) - 128;
87362306a36Sopenharmony_ci	btwrite(value & 0xff, BT848_BRIGHT);
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_cistatic void bt848_hue(struct bttv *btv, int hue)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	int value;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	btv->hue = hue;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* -128 to 127 */
88362306a36Sopenharmony_ci	value = (hue >> 8) - 128;
88462306a36Sopenharmony_ci	btwrite(value & 0xff, BT848_HUE);
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic void bt848_contrast(struct bttv *btv, int cont)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	int value,hibit;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	btv->contrast = cont;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	/* 0-511 */
89462306a36Sopenharmony_ci	value = (cont  >> 7);
89562306a36Sopenharmony_ci	hibit = (value >> 6) & 4;
89662306a36Sopenharmony_ci	btwrite(value & 0xff, BT848_CONTRAST_LO);
89762306a36Sopenharmony_ci	btaor(hibit, ~4, BT848_E_CONTROL);
89862306a36Sopenharmony_ci	btaor(hibit, ~4, BT848_O_CONTROL);
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cistatic void bt848_sat(struct bttv *btv, int color)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	int val_u,val_v,hibits;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	btv->saturation = color;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* 0-511 for the color */
90862306a36Sopenharmony_ci	val_u   = ((color * btv->opt_uv_ratio) / 50) >> 7;
90962306a36Sopenharmony_ci	val_v   = (((color * (100 - btv->opt_uv_ratio) / 50) >>7)*180L)/254;
91062306a36Sopenharmony_ci	hibits  = (val_u >> 7) & 2;
91162306a36Sopenharmony_ci	hibits |= (val_v >> 8) & 1;
91262306a36Sopenharmony_ci	btwrite(val_u & 0xff, BT848_SAT_U_LO);
91362306a36Sopenharmony_ci	btwrite(val_v & 0xff, BT848_SAT_V_LO);
91462306a36Sopenharmony_ci	btaor(hibits, ~3, BT848_E_CONTROL);
91562306a36Sopenharmony_ci	btaor(hibits, ~3, BT848_O_CONTROL);
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic int
92162306a36Sopenharmony_civideo_mux(struct bttv *btv, unsigned int input)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	int mux,mask2;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (input >= bttv_tvcards[btv->c.type].video_inputs)
92662306a36Sopenharmony_ci		return -EINVAL;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	/* needed by RemoteVideo MX */
92962306a36Sopenharmony_ci	mask2 = bttv_tvcards[btv->c.type].gpiomask2;
93062306a36Sopenharmony_ci	if (mask2)
93162306a36Sopenharmony_ci		gpio_inout(mask2,mask2);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (input == btv->svhs)  {
93462306a36Sopenharmony_ci		btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
93562306a36Sopenharmony_ci		btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
93662306a36Sopenharmony_ci	} else {
93762306a36Sopenharmony_ci		btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
93862306a36Sopenharmony_ci		btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci	mux = bttv_muxsel(btv, input);
94162306a36Sopenharmony_ci	btaor(mux<<5, ~(3<<5), BT848_IFORM);
94262306a36Sopenharmony_ci	dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	/* card specific hook */
94562306a36Sopenharmony_ci	if(bttv_tvcards[btv->c.type].muxsel_hook)
94662306a36Sopenharmony_ci		bttv_tvcards[btv->c.type].muxsel_hook (btv, input);
94762306a36Sopenharmony_ci	return 0;
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistatic char *audio_modes[] = {
95162306a36Sopenharmony_ci	"audio: tuner", "audio: radio", "audio: extern",
95262306a36Sopenharmony_ci	"audio: intern", "audio: mute"
95362306a36Sopenharmony_ci};
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_cistatic void
95662306a36Sopenharmony_ciaudio_mux_gpio(struct bttv *btv, int input, int mute)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	int gpio_val, signal, mute_gpio;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
96162306a36Sopenharmony_ci		   bttv_tvcards[btv->c.type].gpiomask);
96262306a36Sopenharmony_ci	signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	/* automute */
96562306a36Sopenharmony_ci	mute_gpio = mute || (btv->opt_automute && (!signal || !btv->users)
96662306a36Sopenharmony_ci				&& !btv->has_radio_tuner);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (mute_gpio)
96962306a36Sopenharmony_ci		gpio_val = bttv_tvcards[btv->c.type].gpiomute;
97062306a36Sopenharmony_ci	else
97162306a36Sopenharmony_ci		gpio_val = bttv_tvcards[btv->c.type].gpiomux[input];
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	switch (btv->c.type) {
97462306a36Sopenharmony_ci	case BTTV_BOARD_VOODOOTV_FM:
97562306a36Sopenharmony_ci	case BTTV_BOARD_VOODOOTV_200:
97662306a36Sopenharmony_ci		gpio_val = bttv_tda9880_setnorm(btv, gpio_val);
97762306a36Sopenharmony_ci		break;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	default:
98062306a36Sopenharmony_ci		gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	if (bttv_gpio)
98462306a36Sopenharmony_ci		bttv_gpio_tracking(btv, audio_modes[mute_gpio ? 4 : input]);
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistatic int
98862306a36Sopenharmony_ciaudio_mute(struct bttv *btv, int mute)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	struct v4l2_ctrl *ctrl;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	audio_mux_gpio(btv, btv->audio_input, mute);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (btv->sd_msp34xx) {
99562306a36Sopenharmony_ci		ctrl = v4l2_ctrl_find(btv->sd_msp34xx->ctrl_handler, V4L2_CID_AUDIO_MUTE);
99662306a36Sopenharmony_ci		if (ctrl)
99762306a36Sopenharmony_ci			v4l2_ctrl_s_ctrl(ctrl, mute);
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci	if (btv->sd_tvaudio) {
100062306a36Sopenharmony_ci		ctrl = v4l2_ctrl_find(btv->sd_tvaudio->ctrl_handler, V4L2_CID_AUDIO_MUTE);
100162306a36Sopenharmony_ci		if (ctrl)
100262306a36Sopenharmony_ci			v4l2_ctrl_s_ctrl(ctrl, mute);
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci	if (btv->sd_tda7432) {
100562306a36Sopenharmony_ci		ctrl = v4l2_ctrl_find(btv->sd_tda7432->ctrl_handler, V4L2_CID_AUDIO_MUTE);
100662306a36Sopenharmony_ci		if (ctrl)
100762306a36Sopenharmony_ci			v4l2_ctrl_s_ctrl(ctrl, mute);
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci	return 0;
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cistatic int
101362306a36Sopenharmony_ciaudio_input(struct bttv *btv, int input)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci	audio_mux_gpio(btv, input, btv->mute);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	if (btv->sd_msp34xx) {
101862306a36Sopenharmony_ci		u32 in;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci		/* Note: the inputs tuner/radio/extern/intern are translated
102162306a36Sopenharmony_ci		   to msp routings. This assumes common behavior for all msp3400
102262306a36Sopenharmony_ci		   based TV cards. When this assumption fails, then the
102362306a36Sopenharmony_ci		   specific MSP routing must be added to the card table.
102462306a36Sopenharmony_ci		   For now this is sufficient. */
102562306a36Sopenharmony_ci		switch (input) {
102662306a36Sopenharmony_ci		case TVAUDIO_INPUT_RADIO:
102762306a36Sopenharmony_ci			/* Some boards need the msp do to the radio demod */
102862306a36Sopenharmony_ci			if (btv->radio_uses_msp_demodulator) {
102962306a36Sopenharmony_ci				in = MSP_INPUT_DEFAULT;
103062306a36Sopenharmony_ci				break;
103162306a36Sopenharmony_ci			}
103262306a36Sopenharmony_ci			in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
103362306a36Sopenharmony_ci				    MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
103462306a36Sopenharmony_ci			break;
103562306a36Sopenharmony_ci		case TVAUDIO_INPUT_EXTERN:
103662306a36Sopenharmony_ci			in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
103762306a36Sopenharmony_ci				    MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
103862306a36Sopenharmony_ci			break;
103962306a36Sopenharmony_ci		case TVAUDIO_INPUT_INTERN:
104062306a36Sopenharmony_ci			/* Yes, this is the same input as for RADIO. I doubt
104162306a36Sopenharmony_ci			   if this is ever used. The only board with an INTERN
104262306a36Sopenharmony_ci			   input is the BTTV_BOARD_AVERMEDIA98. I wonder how
104362306a36Sopenharmony_ci			   that was tested. My guess is that the whole INTERN
104462306a36Sopenharmony_ci			   input does not work. */
104562306a36Sopenharmony_ci			in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
104662306a36Sopenharmony_ci				    MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
104762306a36Sopenharmony_ci			break;
104862306a36Sopenharmony_ci		case TVAUDIO_INPUT_TUNER:
104962306a36Sopenharmony_ci		default:
105062306a36Sopenharmony_ci			/* This is the only card that uses TUNER2, and afaik,
105162306a36Sopenharmony_ci			   is the only difference between the VOODOOTV_FM
105262306a36Sopenharmony_ci			   and VOODOOTV_200 */
105362306a36Sopenharmony_ci			if (btv->c.type == BTTV_BOARD_VOODOOTV_200)
105462306a36Sopenharmony_ci				in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \
105562306a36Sopenharmony_ci					MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER);
105662306a36Sopenharmony_ci			else
105762306a36Sopenharmony_ci				in = MSP_INPUT_DEFAULT;
105862306a36Sopenharmony_ci			break;
105962306a36Sopenharmony_ci		}
106062306a36Sopenharmony_ci		v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing,
106162306a36Sopenharmony_ci			       in, MSP_OUTPUT_DEFAULT, 0);
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci	if (btv->sd_tvaudio) {
106462306a36Sopenharmony_ci		v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing,
106562306a36Sopenharmony_ci				 input, 0, 0);
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci	return 0;
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_cistatic void
107162306a36Sopenharmony_cibttv_crop_calc_limits(struct bttv_crop *c)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	/* Scale factor min. 1:1, max. 16:1. Min. image size
107462306a36Sopenharmony_ci	   48 x 32. Scaled width must be a multiple of 4. */
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (1) {
107762306a36Sopenharmony_ci		/* For bug compatibility with VIDIOCGCAP and image
107862306a36Sopenharmony_ci		   size checks in earlier driver versions. */
107962306a36Sopenharmony_ci		c->min_scaled_width = 48;
108062306a36Sopenharmony_ci		c->min_scaled_height = 32;
108162306a36Sopenharmony_ci	} else {
108262306a36Sopenharmony_ci		c->min_scaled_width =
108362306a36Sopenharmony_ci			(max_t(unsigned int, 48, c->rect.width >> 4) + 3) & ~3;
108462306a36Sopenharmony_ci		c->min_scaled_height =
108562306a36Sopenharmony_ci			max_t(unsigned int, 32, c->rect.height >> 4);
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	c->max_scaled_width  = c->rect.width & ~3;
108962306a36Sopenharmony_ci	c->max_scaled_height = c->rect.height;
109062306a36Sopenharmony_ci}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_cistatic void
109362306a36Sopenharmony_cibttv_crop_reset(struct bttv_crop *c, unsigned int norm)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	c->rect = bttv_tvnorms[norm].cropcap.defrect;
109662306a36Sopenharmony_ci	bttv_crop_calc_limits(c);
109762306a36Sopenharmony_ci}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci/* Call with btv->lock down. */
110062306a36Sopenharmony_cistatic int
110162306a36Sopenharmony_ciset_tvnorm(struct bttv *btv, unsigned int norm)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	const struct bttv_tvnorm *tvnorm;
110462306a36Sopenharmony_ci	v4l2_std_id id;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	WARN_ON(norm >= BTTV_TVNORMS);
110762306a36Sopenharmony_ci	WARN_ON(btv->tvnorm >= BTTV_TVNORMS);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	tvnorm = &bttv_tvnorms[norm];
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	if (memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap,
111262306a36Sopenharmony_ci		    sizeof (tvnorm->cropcap))) {
111362306a36Sopenharmony_ci		bttv_crop_reset(&btv->crop[0], norm);
111462306a36Sopenharmony_ci		btv->crop[1] = btv->crop[0]; /* current = default */
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		if (0 == (btv->resources & VIDEO_RESOURCES)) {
111762306a36Sopenharmony_ci			btv->crop_start = tvnorm->cropcap.bounds.top
111862306a36Sopenharmony_ci				+ tvnorm->cropcap.bounds.height;
111962306a36Sopenharmony_ci		}
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	btv->tvnorm = norm;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	btwrite(tvnorm->adelay, BT848_ADELAY);
112562306a36Sopenharmony_ci	btwrite(tvnorm->bdelay, BT848_BDELAY);
112662306a36Sopenharmony_ci	btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
112762306a36Sopenharmony_ci	      BT848_IFORM);
112862306a36Sopenharmony_ci	btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE);
112962306a36Sopenharmony_ci	btwrite(1, BT848_VBI_PACK_DEL);
113062306a36Sopenharmony_ci	bt848A_set_timing(btv);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	switch (btv->c.type) {
113362306a36Sopenharmony_ci	case BTTV_BOARD_VOODOOTV_FM:
113462306a36Sopenharmony_ci	case BTTV_BOARD_VOODOOTV_200:
113562306a36Sopenharmony_ci		bttv_tda9880_setnorm(btv, gpio_read());
113662306a36Sopenharmony_ci		break;
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci	id = tvnorm->v4l2_id;
113962306a36Sopenharmony_ci	bttv_call_all(btv, video, s_std, id);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	return 0;
114262306a36Sopenharmony_ci}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci/* Call with btv->lock down. */
114562306a36Sopenharmony_cistatic void
114662306a36Sopenharmony_ciset_input(struct bttv *btv, unsigned int input, unsigned int norm)
114762306a36Sopenharmony_ci{
114862306a36Sopenharmony_ci	unsigned long flags;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	btv->input = input;
115162306a36Sopenharmony_ci	if (irq_iswitch) {
115262306a36Sopenharmony_ci		spin_lock_irqsave(&btv->s_lock,flags);
115362306a36Sopenharmony_ci		if (btv->curr.frame_irq) {
115462306a36Sopenharmony_ci			/* active capture -> delayed input switch */
115562306a36Sopenharmony_ci			btv->new_input = input;
115662306a36Sopenharmony_ci		} else {
115762306a36Sopenharmony_ci			video_mux(btv,input);
115862306a36Sopenharmony_ci		}
115962306a36Sopenharmony_ci		spin_unlock_irqrestore(&btv->s_lock,flags);
116062306a36Sopenharmony_ci	} else {
116162306a36Sopenharmony_ci		video_mux(btv,input);
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci	btv->audio_input = (btv->tuner_type != TUNER_ABSENT && input == 0) ?
116462306a36Sopenharmony_ci				TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN;
116562306a36Sopenharmony_ci	audio_input(btv, btv->audio_input);
116662306a36Sopenharmony_ci	set_tvnorm(btv, norm);
116762306a36Sopenharmony_ci}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_civoid init_irqreg(struct bttv *btv)
117062306a36Sopenharmony_ci{
117162306a36Sopenharmony_ci	/* clear status */
117262306a36Sopenharmony_ci	btwrite(0xfffffUL, BT848_INT_STAT);
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (bttv_tvcards[btv->c.type].no_video) {
117562306a36Sopenharmony_ci		/* i2c only */
117662306a36Sopenharmony_ci		btwrite(BT848_INT_I2CDONE,
117762306a36Sopenharmony_ci			BT848_INT_MASK);
117862306a36Sopenharmony_ci	} else {
117962306a36Sopenharmony_ci		/* full video */
118062306a36Sopenharmony_ci		btwrite((btv->triton1)  |
118162306a36Sopenharmony_ci			(btv->gpioirq ? BT848_INT_GPINT : 0) |
118262306a36Sopenharmony_ci			BT848_INT_SCERR |
118362306a36Sopenharmony_ci			(fdsr ? BT848_INT_FDSR : 0) |
118462306a36Sopenharmony_ci			BT848_INT_RISCI | BT848_INT_OCERR |
118562306a36Sopenharmony_ci			BT848_INT_FMTCHG|BT848_INT_HLOCK|
118662306a36Sopenharmony_ci			BT848_INT_I2CDONE,
118762306a36Sopenharmony_ci			BT848_INT_MASK);
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cistatic void init_bt848(struct bttv *btv)
119262306a36Sopenharmony_ci{
119362306a36Sopenharmony_ci	if (bttv_tvcards[btv->c.type].no_video) {
119462306a36Sopenharmony_ci		/* very basic init only */
119562306a36Sopenharmony_ci		init_irqreg(btv);
119662306a36Sopenharmony_ci		return;
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	btwrite(0x00, BT848_CAP_CTL);
120062306a36Sopenharmony_ci	btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
120162306a36Sopenharmony_ci	btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	/* set planar and packed mode trigger points and         */
120462306a36Sopenharmony_ci	/* set rising edge of inverted GPINTR pin as irq trigger */
120562306a36Sopenharmony_ci	btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
120662306a36Sopenharmony_ci		BT848_GPIO_DMA_CTL_PLTP1_16|
120762306a36Sopenharmony_ci		BT848_GPIO_DMA_CTL_PLTP23_16|
120862306a36Sopenharmony_ci		BT848_GPIO_DMA_CTL_GPINTC|
120962306a36Sopenharmony_ci		BT848_GPIO_DMA_CTL_GPINTI,
121062306a36Sopenharmony_ci		BT848_GPIO_DMA_CTL);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	btwrite(0x20, BT848_E_VSCALE_HI);
121362306a36Sopenharmony_ci	btwrite(0x20, BT848_O_VSCALE_HI);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	v4l2_ctrl_handler_setup(&btv->ctrl_handler);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	/* interrupt */
121862306a36Sopenharmony_ci	init_irqreg(btv);
121962306a36Sopenharmony_ci}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_cistatic void bttv_reinit_bt848(struct bttv *btv)
122262306a36Sopenharmony_ci{
122362306a36Sopenharmony_ci	unsigned long flags;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	if (bttv_verbose)
122662306a36Sopenharmony_ci		pr_info("%d: reset, reinitialize\n", btv->c.nr);
122762306a36Sopenharmony_ci	spin_lock_irqsave(&btv->s_lock,flags);
122862306a36Sopenharmony_ci	btv->errors=0;
122962306a36Sopenharmony_ci	bttv_set_dma(btv,0);
123062306a36Sopenharmony_ci	spin_unlock_irqrestore(&btv->s_lock,flags);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	init_bt848(btv);
123362306a36Sopenharmony_ci	btv->pll.pll_current = -1;
123462306a36Sopenharmony_ci	set_input(btv, btv->input, btv->tvnorm);
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic int bttv_s_ctrl(struct v4l2_ctrl *c)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	struct bttv *btv = container_of(c->handler, struct bttv, ctrl_handler);
124062306a36Sopenharmony_ci	int val;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	switch (c->id) {
124362306a36Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
124462306a36Sopenharmony_ci		bt848_bright(btv, c->val);
124562306a36Sopenharmony_ci		break;
124662306a36Sopenharmony_ci	case V4L2_CID_HUE:
124762306a36Sopenharmony_ci		bt848_hue(btv, c->val);
124862306a36Sopenharmony_ci		break;
124962306a36Sopenharmony_ci	case V4L2_CID_CONTRAST:
125062306a36Sopenharmony_ci		bt848_contrast(btv, c->val);
125162306a36Sopenharmony_ci		break;
125262306a36Sopenharmony_ci	case V4L2_CID_SATURATION:
125362306a36Sopenharmony_ci		bt848_sat(btv, c->val);
125462306a36Sopenharmony_ci		break;
125562306a36Sopenharmony_ci	case V4L2_CID_COLOR_KILLER:
125662306a36Sopenharmony_ci		if (c->val) {
125762306a36Sopenharmony_ci			btor(BT848_SCLOOP_CKILL, BT848_E_SCLOOP);
125862306a36Sopenharmony_ci			btor(BT848_SCLOOP_CKILL, BT848_O_SCLOOP);
125962306a36Sopenharmony_ci		} else {
126062306a36Sopenharmony_ci			btand(~BT848_SCLOOP_CKILL, BT848_E_SCLOOP);
126162306a36Sopenharmony_ci			btand(~BT848_SCLOOP_CKILL, BT848_O_SCLOOP);
126262306a36Sopenharmony_ci		}
126362306a36Sopenharmony_ci		break;
126462306a36Sopenharmony_ci	case V4L2_CID_AUDIO_MUTE:
126562306a36Sopenharmony_ci		audio_mute(btv, c->val);
126662306a36Sopenharmony_ci		btv->mute = c->val;
126762306a36Sopenharmony_ci		break;
126862306a36Sopenharmony_ci	case V4L2_CID_AUDIO_VOLUME:
126962306a36Sopenharmony_ci		btv->volume_gpio(btv, c->val);
127062306a36Sopenharmony_ci		break;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	case V4L2_CID_CHROMA_AGC:
127362306a36Sopenharmony_ci		val = c->val ? BT848_SCLOOP_CAGC : 0;
127462306a36Sopenharmony_ci		btwrite(val, BT848_E_SCLOOP);
127562306a36Sopenharmony_ci		btwrite(val, BT848_O_SCLOOP);
127662306a36Sopenharmony_ci		break;
127762306a36Sopenharmony_ci	case V4L2_CID_PRIVATE_COMBFILTER:
127862306a36Sopenharmony_ci		btv->opt_combfilter = c->val;
127962306a36Sopenharmony_ci		break;
128062306a36Sopenharmony_ci	case V4L2_CID_PRIVATE_LUMAFILTER:
128162306a36Sopenharmony_ci		if (c->val) {
128262306a36Sopenharmony_ci			btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL);
128362306a36Sopenharmony_ci			btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL);
128462306a36Sopenharmony_ci		} else {
128562306a36Sopenharmony_ci			btor(BT848_CONTROL_LDEC, BT848_E_CONTROL);
128662306a36Sopenharmony_ci			btor(BT848_CONTROL_LDEC, BT848_O_CONTROL);
128762306a36Sopenharmony_ci		}
128862306a36Sopenharmony_ci		break;
128962306a36Sopenharmony_ci	case V4L2_CID_PRIVATE_AUTOMUTE:
129062306a36Sopenharmony_ci		btv->opt_automute = c->val;
129162306a36Sopenharmony_ci		break;
129262306a36Sopenharmony_ci	case V4L2_CID_PRIVATE_AGC_CRUSH:
129362306a36Sopenharmony_ci		btwrite(BT848_ADC_RESERVED |
129462306a36Sopenharmony_ci				(c->val ? BT848_ADC_CRUSH : 0),
129562306a36Sopenharmony_ci				BT848_ADC);
129662306a36Sopenharmony_ci		break;
129762306a36Sopenharmony_ci	case V4L2_CID_PRIVATE_VCR_HACK:
129862306a36Sopenharmony_ci		btv->opt_vcr_hack = c->val;
129962306a36Sopenharmony_ci		break;
130062306a36Sopenharmony_ci	case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
130162306a36Sopenharmony_ci		btwrite(c->val, BT848_WC_UP);
130262306a36Sopenharmony_ci		break;
130362306a36Sopenharmony_ci	case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
130462306a36Sopenharmony_ci		btwrite(c->val, BT848_WC_DOWN);
130562306a36Sopenharmony_ci		break;
130662306a36Sopenharmony_ci	case V4L2_CID_PRIVATE_UV_RATIO:
130762306a36Sopenharmony_ci		btv->opt_uv_ratio = c->val;
130862306a36Sopenharmony_ci		bt848_sat(btv, btv->saturation);
130962306a36Sopenharmony_ci		break;
131062306a36Sopenharmony_ci	case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
131162306a36Sopenharmony_ci		btaor((c->val << 7), ~BT848_OFORM_RANGE, BT848_OFORM);
131262306a36Sopenharmony_ci		break;
131362306a36Sopenharmony_ci	case V4L2_CID_PRIVATE_CORING:
131462306a36Sopenharmony_ci		btaor((c->val << 5), ~BT848_OFORM_CORE32, BT848_OFORM);
131562306a36Sopenharmony_ci		break;
131662306a36Sopenharmony_ci	default:
131762306a36Sopenharmony_ci		return -EINVAL;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci	return 0;
132062306a36Sopenharmony_ci}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops bttv_ctrl_ops = {
132562306a36Sopenharmony_ci	.s_ctrl = bttv_s_ctrl,
132662306a36Sopenharmony_ci};
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_cistatic struct v4l2_ctrl_config bttv_ctrl_combfilter = {
132962306a36Sopenharmony_ci	.ops = &bttv_ctrl_ops,
133062306a36Sopenharmony_ci	.id = V4L2_CID_PRIVATE_COMBFILTER,
133162306a36Sopenharmony_ci	.name = "Comb Filter",
133262306a36Sopenharmony_ci	.type = V4L2_CTRL_TYPE_BOOLEAN,
133362306a36Sopenharmony_ci	.min = 0,
133462306a36Sopenharmony_ci	.max = 1,
133562306a36Sopenharmony_ci	.step = 1,
133662306a36Sopenharmony_ci	.def = 1,
133762306a36Sopenharmony_ci};
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_cistatic struct v4l2_ctrl_config bttv_ctrl_automute = {
134062306a36Sopenharmony_ci	.ops = &bttv_ctrl_ops,
134162306a36Sopenharmony_ci	.id = V4L2_CID_PRIVATE_AUTOMUTE,
134262306a36Sopenharmony_ci	.name = "Auto Mute",
134362306a36Sopenharmony_ci	.type = V4L2_CTRL_TYPE_BOOLEAN,
134462306a36Sopenharmony_ci	.min = 0,
134562306a36Sopenharmony_ci	.max = 1,
134662306a36Sopenharmony_ci	.step = 1,
134762306a36Sopenharmony_ci	.def = 1,
134862306a36Sopenharmony_ci};
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_cistatic struct v4l2_ctrl_config bttv_ctrl_lumafilter = {
135162306a36Sopenharmony_ci	.ops = &bttv_ctrl_ops,
135262306a36Sopenharmony_ci	.id = V4L2_CID_PRIVATE_LUMAFILTER,
135362306a36Sopenharmony_ci	.name = "Luma Decimation Filter",
135462306a36Sopenharmony_ci	.type = V4L2_CTRL_TYPE_BOOLEAN,
135562306a36Sopenharmony_ci	.min = 0,
135662306a36Sopenharmony_ci	.max = 1,
135762306a36Sopenharmony_ci	.step = 1,
135862306a36Sopenharmony_ci	.def = 1,
135962306a36Sopenharmony_ci};
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_cistatic struct v4l2_ctrl_config bttv_ctrl_agc_crush = {
136262306a36Sopenharmony_ci	.ops = &bttv_ctrl_ops,
136362306a36Sopenharmony_ci	.id = V4L2_CID_PRIVATE_AGC_CRUSH,
136462306a36Sopenharmony_ci	.name = "AGC Crush",
136562306a36Sopenharmony_ci	.type = V4L2_CTRL_TYPE_BOOLEAN,
136662306a36Sopenharmony_ci	.min = 0,
136762306a36Sopenharmony_ci	.max = 1,
136862306a36Sopenharmony_ci	.step = 1,
136962306a36Sopenharmony_ci	.def = 1,
137062306a36Sopenharmony_ci};
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_cistatic struct v4l2_ctrl_config bttv_ctrl_vcr_hack = {
137362306a36Sopenharmony_ci	.ops = &bttv_ctrl_ops,
137462306a36Sopenharmony_ci	.id = V4L2_CID_PRIVATE_VCR_HACK,
137562306a36Sopenharmony_ci	.name = "VCR Hack",
137662306a36Sopenharmony_ci	.type = V4L2_CTRL_TYPE_BOOLEAN,
137762306a36Sopenharmony_ci	.min = 0,
137862306a36Sopenharmony_ci	.max = 1,
137962306a36Sopenharmony_ci	.step = 1,
138062306a36Sopenharmony_ci	.def = 1,
138162306a36Sopenharmony_ci};
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_cistatic struct v4l2_ctrl_config bttv_ctrl_whitecrush_lower = {
138462306a36Sopenharmony_ci	.ops = &bttv_ctrl_ops,
138562306a36Sopenharmony_ci	.id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER,
138662306a36Sopenharmony_ci	.name = "Whitecrush Lower",
138762306a36Sopenharmony_ci	.type = V4L2_CTRL_TYPE_INTEGER,
138862306a36Sopenharmony_ci	.min = 0,
138962306a36Sopenharmony_ci	.max = 255,
139062306a36Sopenharmony_ci	.step = 1,
139162306a36Sopenharmony_ci	.def = 0x7f,
139262306a36Sopenharmony_ci};
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_cistatic struct v4l2_ctrl_config bttv_ctrl_whitecrush_upper = {
139562306a36Sopenharmony_ci	.ops = &bttv_ctrl_ops,
139662306a36Sopenharmony_ci	.id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER,
139762306a36Sopenharmony_ci	.name = "Whitecrush Upper",
139862306a36Sopenharmony_ci	.type = V4L2_CTRL_TYPE_INTEGER,
139962306a36Sopenharmony_ci	.min = 0,
140062306a36Sopenharmony_ci	.max = 255,
140162306a36Sopenharmony_ci	.step = 1,
140262306a36Sopenharmony_ci	.def = 0xcf,
140362306a36Sopenharmony_ci};
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_cistatic struct v4l2_ctrl_config bttv_ctrl_uv_ratio = {
140662306a36Sopenharmony_ci	.ops = &bttv_ctrl_ops,
140762306a36Sopenharmony_ci	.id = V4L2_CID_PRIVATE_UV_RATIO,
140862306a36Sopenharmony_ci	.name = "UV Ratio",
140962306a36Sopenharmony_ci	.type = V4L2_CTRL_TYPE_INTEGER,
141062306a36Sopenharmony_ci	.min = 0,
141162306a36Sopenharmony_ci	.max = 100,
141262306a36Sopenharmony_ci	.step = 1,
141362306a36Sopenharmony_ci	.def = 50,
141462306a36Sopenharmony_ci};
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cistatic struct v4l2_ctrl_config bttv_ctrl_full_luma = {
141762306a36Sopenharmony_ci	.ops = &bttv_ctrl_ops,
141862306a36Sopenharmony_ci	.id = V4L2_CID_PRIVATE_FULL_LUMA_RANGE,
141962306a36Sopenharmony_ci	.name = "Full Luma Range",
142062306a36Sopenharmony_ci	.type = V4L2_CTRL_TYPE_BOOLEAN,
142162306a36Sopenharmony_ci	.min = 0,
142262306a36Sopenharmony_ci	.max = 1,
142362306a36Sopenharmony_ci	.step = 1,
142462306a36Sopenharmony_ci};
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_cistatic struct v4l2_ctrl_config bttv_ctrl_coring = {
142762306a36Sopenharmony_ci	.ops = &bttv_ctrl_ops,
142862306a36Sopenharmony_ci	.id = V4L2_CID_PRIVATE_CORING,
142962306a36Sopenharmony_ci	.name = "Coring",
143062306a36Sopenharmony_ci	.type = V4L2_CTRL_TYPE_INTEGER,
143162306a36Sopenharmony_ci	.min = 0,
143262306a36Sopenharmony_ci	.max = 3,
143362306a36Sopenharmony_ci	.step = 1,
143462306a36Sopenharmony_ci};
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_civoid bttv_gpio_tracking(struct bttv *btv, char *comment)
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	unsigned int outbits, data;
144262306a36Sopenharmony_ci	outbits = btread(BT848_GPIO_OUT_EN);
144362306a36Sopenharmony_ci	data    = btread(BT848_GPIO_DATA);
144462306a36Sopenharmony_ci	pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
144562306a36Sopenharmony_ci		 btv->c.nr, outbits, data & outbits, data & ~outbits, comment);
144662306a36Sopenharmony_ci}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_cistatic const struct bttv_format*
144962306a36Sopenharmony_ciformat_by_fourcc(int fourcc)
145062306a36Sopenharmony_ci{
145162306a36Sopenharmony_ci	unsigned int i;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	for (i = 0; i < FORMATS; i++) {
145462306a36Sopenharmony_ci		if (-1 == formats[i].fourcc)
145562306a36Sopenharmony_ci			continue;
145662306a36Sopenharmony_ci		if (formats[i].fourcc == fourcc)
145762306a36Sopenharmony_ci			return formats+i;
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci	return NULL;
146062306a36Sopenharmony_ci}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
146362306a36Sopenharmony_ci/* video4linux (1) interface                                               */
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
146662306a36Sopenharmony_ci		       unsigned int *num_planes, unsigned int sizes[],
146762306a36Sopenharmony_ci		       struct device *alloc_devs[])
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	struct bttv *btv = vb2_get_drv_priv(q);
147062306a36Sopenharmony_ci	unsigned int size = btv->fmt->depth * btv->width * btv->height >> 3;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	if (*num_planes)
147362306a36Sopenharmony_ci		return sizes[0] < size ? -EINVAL : 0;
147462306a36Sopenharmony_ci	*num_planes = 1;
147562306a36Sopenharmony_ci	sizes[0] = size;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	return 0;
147862306a36Sopenharmony_ci}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_cistatic void buf_queue(struct vb2_buffer *vb)
148162306a36Sopenharmony_ci{
148262306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
148362306a36Sopenharmony_ci	struct vb2_queue *vq = vb->vb2_queue;
148462306a36Sopenharmony_ci	struct bttv *btv = vb2_get_drv_priv(vq);
148562306a36Sopenharmony_ci	struct bttv_buffer *buf = container_of(vbuf, struct bttv_buffer, vbuf);
148662306a36Sopenharmony_ci	unsigned long flags;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	spin_lock_irqsave(&btv->s_lock, flags);
148962306a36Sopenharmony_ci	if (list_empty(&btv->capture)) {
149062306a36Sopenharmony_ci		btv->loop_irq = BT848_RISC_VIDEO;
149162306a36Sopenharmony_ci		if (vb2_is_streaming(&btv->vbiq))
149262306a36Sopenharmony_ci			btv->loop_irq |= BT848_RISC_VBI;
149362306a36Sopenharmony_ci		bttv_set_dma(btv, BT848_CAP_CTL_CAPTURE_ODD |
149462306a36Sopenharmony_ci			     BT848_CAP_CTL_CAPTURE_EVEN);
149562306a36Sopenharmony_ci	}
149662306a36Sopenharmony_ci	list_add_tail(&buf->list, &btv->capture);
149762306a36Sopenharmony_ci	spin_unlock_irqrestore(&btv->s_lock, flags);
149862306a36Sopenharmony_ci}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_cistatic int buf_prepare(struct vb2_buffer *vb)
150162306a36Sopenharmony_ci{
150262306a36Sopenharmony_ci	struct vb2_queue *vq = vb->vb2_queue;
150362306a36Sopenharmony_ci	struct bttv *btv = vb2_get_drv_priv(vq);
150462306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
150562306a36Sopenharmony_ci	struct bttv_buffer *buf = container_of(vbuf, struct bttv_buffer, vbuf);
150662306a36Sopenharmony_ci	unsigned int size = (btv->fmt->depth * btv->width * btv->height) >> 3;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size)
150962306a36Sopenharmony_ci		return -EINVAL;
151062306a36Sopenharmony_ci	vb2_set_plane_payload(vb, 0, size);
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	if (btv->field != V4L2_FIELD_ALTERNATE) {
151362306a36Sopenharmony_ci		buf->vbuf.field = btv->field;
151462306a36Sopenharmony_ci	} else if (btv->field_last == V4L2_FIELD_TOP) {
151562306a36Sopenharmony_ci		buf->vbuf.field = V4L2_FIELD_BOTTOM;
151662306a36Sopenharmony_ci		btv->field_last = V4L2_FIELD_BOTTOM;
151762306a36Sopenharmony_ci	} else {
151862306a36Sopenharmony_ci		buf->vbuf.field = V4L2_FIELD_TOP;
151962306a36Sopenharmony_ci		btv->field_last = V4L2_FIELD_TOP;
152062306a36Sopenharmony_ci	}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	/* Allocate memory for risc struct and create the risc program. */
152362306a36Sopenharmony_ci	return bttv_buffer_risc(btv, buf);
152462306a36Sopenharmony_ci}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_cistatic void buf_cleanup(struct vb2_buffer *vb)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	struct vb2_queue *vq = vb->vb2_queue;
152962306a36Sopenharmony_ci	struct bttv *btv = vb2_get_drv_priv(vq);
153062306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
153162306a36Sopenharmony_ci	struct bttv_buffer *buf = container_of(vbuf, struct bttv_buffer, vbuf);
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	btcx_riscmem_free(btv->c.pci, &buf->top);
153462306a36Sopenharmony_ci	btcx_riscmem_free(btv->c.pci, &buf->bottom);
153562306a36Sopenharmony_ci}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *q, unsigned int count)
153862306a36Sopenharmony_ci{
153962306a36Sopenharmony_ci	int seqnr = 0;
154062306a36Sopenharmony_ci	struct bttv_buffer *buf;
154162306a36Sopenharmony_ci	struct bttv *btv = vb2_get_drv_priv(q);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	if (!check_alloc_btres_lock(btv, RESOURCE_VIDEO_STREAM)) {
154462306a36Sopenharmony_ci		if (btv->field_count)
154562306a36Sopenharmony_ci			seqnr++;
154662306a36Sopenharmony_ci		while (!list_empty(&btv->capture)) {
154762306a36Sopenharmony_ci			buf = list_entry(btv->capture.next,
154862306a36Sopenharmony_ci					 struct bttv_buffer, list);
154962306a36Sopenharmony_ci			list_del(&buf->list);
155062306a36Sopenharmony_ci			buf->vbuf.sequence = (btv->field_count >> 1) + seqnr++;
155162306a36Sopenharmony_ci			vb2_buffer_done(&buf->vbuf.vb2_buf,
155262306a36Sopenharmony_ci					VB2_BUF_STATE_QUEUED);
155362306a36Sopenharmony_ci		}
155462306a36Sopenharmony_ci		return -EBUSY;
155562306a36Sopenharmony_ci	}
155662306a36Sopenharmony_ci	if (!vb2_is_streaming(&btv->vbiq)) {
155762306a36Sopenharmony_ci		init_irqreg(btv);
155862306a36Sopenharmony_ci		btv->field_count = 0;
155962306a36Sopenharmony_ci	}
156062306a36Sopenharmony_ci	btv->framedrop = 0;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	return 0;
156362306a36Sopenharmony_ci}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *q)
156662306a36Sopenharmony_ci{
156762306a36Sopenharmony_ci	unsigned long flags;
156862306a36Sopenharmony_ci	struct bttv *btv = vb2_get_drv_priv(q);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	vb2_wait_for_all_buffers(q);
157162306a36Sopenharmony_ci	spin_lock_irqsave(&btv->s_lock, flags);
157262306a36Sopenharmony_ci	free_btres_lock(btv, RESOURCE_VIDEO_STREAM);
157362306a36Sopenharmony_ci	if (!vb2_is_streaming(&btv->vbiq)) {
157462306a36Sopenharmony_ci		/* stop field counter */
157562306a36Sopenharmony_ci		btand(~BT848_INT_VSYNC, BT848_INT_MASK);
157662306a36Sopenharmony_ci	}
157762306a36Sopenharmony_ci	spin_unlock_irqrestore(&btv->s_lock, flags);
157862306a36Sopenharmony_ci}
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_cistatic const struct vb2_ops bttv_video_qops = {
158162306a36Sopenharmony_ci	.queue_setup    = queue_setup,
158262306a36Sopenharmony_ci	.buf_queue      = buf_queue,
158362306a36Sopenharmony_ci	.buf_prepare    = buf_prepare,
158462306a36Sopenharmony_ci	.buf_cleanup    = buf_cleanup,
158562306a36Sopenharmony_ci	.start_streaming = start_streaming,
158662306a36Sopenharmony_ci	.stop_streaming = stop_streaming,
158762306a36Sopenharmony_ci	.wait_prepare   = vb2_ops_wait_prepare,
158862306a36Sopenharmony_ci	.wait_finish    = vb2_ops_wait_finish,
158962306a36Sopenharmony_ci};
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_cistatic void radio_enable(struct bttv *btv)
159262306a36Sopenharmony_ci{
159362306a36Sopenharmony_ci	/* Switch to the radio tuner */
159462306a36Sopenharmony_ci	if (!btv->has_radio_tuner) {
159562306a36Sopenharmony_ci		btv->has_radio_tuner = 1;
159662306a36Sopenharmony_ci		bttv_call_all(btv, tuner, s_radio);
159762306a36Sopenharmony_ci		btv->audio_input = TVAUDIO_INPUT_RADIO;
159862306a36Sopenharmony_ci		audio_input(btv, btv->audio_input);
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_cistatic int bttv_s_std(struct file *file, void *priv, v4l2_std_id id)
160362306a36Sopenharmony_ci{
160462306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
160562306a36Sopenharmony_ci	unsigned int i;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	for (i = 0; i < BTTV_TVNORMS; i++)
160862306a36Sopenharmony_ci		if (id & bttv_tvnorms[i].v4l2_id)
160962306a36Sopenharmony_ci			break;
161062306a36Sopenharmony_ci	if (i == BTTV_TVNORMS)
161162306a36Sopenharmony_ci		return -EINVAL;
161262306a36Sopenharmony_ci	btv->std = id;
161362306a36Sopenharmony_ci	set_tvnorm(btv, i);
161462306a36Sopenharmony_ci	return 0;
161562306a36Sopenharmony_ci}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_cistatic int bttv_g_std(struct file *file, void *priv, v4l2_std_id *id)
161862306a36Sopenharmony_ci{
161962306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	*id = btv->std;
162262306a36Sopenharmony_ci	return 0;
162362306a36Sopenharmony_ci}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_cistatic int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
162662306a36Sopenharmony_ci{
162762306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
163062306a36Sopenharmony_ci		*id &= V4L2_STD_625_50;
163162306a36Sopenharmony_ci	else
163262306a36Sopenharmony_ci		*id &= V4L2_STD_525_60;
163362306a36Sopenharmony_ci	return 0;
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_cistatic int bttv_enum_input(struct file *file, void *priv,
163762306a36Sopenharmony_ci					struct v4l2_input *i)
163862306a36Sopenharmony_ci{
163962306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	if (i->index >= bttv_tvcards[btv->c.type].video_inputs)
164262306a36Sopenharmony_ci		return -EINVAL;
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	i->type     = V4L2_INPUT_TYPE_CAMERA;
164562306a36Sopenharmony_ci	i->audioset = 0;
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	if (btv->tuner_type != TUNER_ABSENT && i->index == 0) {
164862306a36Sopenharmony_ci		sprintf(i->name, "Television");
164962306a36Sopenharmony_ci		i->type  = V4L2_INPUT_TYPE_TUNER;
165062306a36Sopenharmony_ci		i->tuner = 0;
165162306a36Sopenharmony_ci	} else if (i->index == btv->svhs) {
165262306a36Sopenharmony_ci		sprintf(i->name, "S-Video");
165362306a36Sopenharmony_ci	} else {
165462306a36Sopenharmony_ci		sprintf(i->name, "Composite%d", i->index);
165562306a36Sopenharmony_ci	}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	if (i->index == btv->input) {
165862306a36Sopenharmony_ci		__u32 dstatus = btread(BT848_DSTATUS);
165962306a36Sopenharmony_ci		if (0 == (dstatus & BT848_DSTATUS_PRES))
166062306a36Sopenharmony_ci			i->status |= V4L2_IN_ST_NO_SIGNAL;
166162306a36Sopenharmony_ci		if (0 == (dstatus & BT848_DSTATUS_HLOC))
166262306a36Sopenharmony_ci			i->status |= V4L2_IN_ST_NO_H_LOCK;
166362306a36Sopenharmony_ci	}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	i->std = BTTV_NORMS;
166662306a36Sopenharmony_ci	return 0;
166762306a36Sopenharmony_ci}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_cistatic int bttv_g_input(struct file *file, void *priv, unsigned int *i)
167062306a36Sopenharmony_ci{
167162306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	*i = btv->input;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	return 0;
167662306a36Sopenharmony_ci}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_cistatic int bttv_s_input(struct file *file, void *priv, unsigned int i)
167962306a36Sopenharmony_ci{
168062306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	if (i >= bttv_tvcards[btv->c.type].video_inputs)
168362306a36Sopenharmony_ci		return -EINVAL;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	set_input(btv, i, btv->tvnorm);
168662306a36Sopenharmony_ci	return 0;
168762306a36Sopenharmony_ci}
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_cistatic int bttv_s_tuner(struct file *file, void *priv,
169062306a36Sopenharmony_ci					const struct v4l2_tuner *t)
169162306a36Sopenharmony_ci{
169262306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	if (t->index)
169562306a36Sopenharmony_ci		return -EINVAL;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	bttv_call_all(btv, tuner, s_tuner, t);
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	if (btv->audio_mode_gpio) {
170062306a36Sopenharmony_ci		struct v4l2_tuner copy = *t;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci		btv->audio_mode_gpio(btv, &copy, 1);
170362306a36Sopenharmony_ci	}
170462306a36Sopenharmony_ci	return 0;
170562306a36Sopenharmony_ci}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_cistatic int bttv_g_frequency(struct file *file, void *priv,
170862306a36Sopenharmony_ci					struct v4l2_frequency *f)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	if (f->tuner)
171362306a36Sopenharmony_ci		return -EINVAL;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	if (f->type == V4L2_TUNER_RADIO)
171662306a36Sopenharmony_ci		radio_enable(btv);
171762306a36Sopenharmony_ci	f->frequency = f->type == V4L2_TUNER_RADIO ?
171862306a36Sopenharmony_ci				btv->radio_freq : btv->tv_freq;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	return 0;
172162306a36Sopenharmony_ci}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_cistatic void bttv_set_frequency(struct bttv *btv, const struct v4l2_frequency *f)
172462306a36Sopenharmony_ci{
172562306a36Sopenharmony_ci	struct v4l2_frequency new_freq = *f;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	bttv_call_all(btv, tuner, s_frequency, f);
172862306a36Sopenharmony_ci	/* s_frequency may clamp the frequency, so get the actual
172962306a36Sopenharmony_ci	   frequency before assigning radio/tv_freq. */
173062306a36Sopenharmony_ci	bttv_call_all(btv, tuner, g_frequency, &new_freq);
173162306a36Sopenharmony_ci	if (new_freq.type == V4L2_TUNER_RADIO) {
173262306a36Sopenharmony_ci		radio_enable(btv);
173362306a36Sopenharmony_ci		btv->radio_freq = new_freq.frequency;
173462306a36Sopenharmony_ci		if (btv->has_tea575x) {
173562306a36Sopenharmony_ci			btv->tea.freq = btv->radio_freq;
173662306a36Sopenharmony_ci			snd_tea575x_set_freq(&btv->tea);
173762306a36Sopenharmony_ci		}
173862306a36Sopenharmony_ci	} else {
173962306a36Sopenharmony_ci		btv->tv_freq = new_freq.frequency;
174062306a36Sopenharmony_ci	}
174162306a36Sopenharmony_ci}
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_cistatic int bttv_s_frequency(struct file *file, void *priv,
174462306a36Sopenharmony_ci					const struct v4l2_frequency *f)
174562306a36Sopenharmony_ci{
174662306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	if (f->tuner)
174962306a36Sopenharmony_ci		return -EINVAL;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	bttv_set_frequency(btv, f);
175262306a36Sopenharmony_ci	return 0;
175362306a36Sopenharmony_ci}
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_cistatic int bttv_log_status(struct file *file, void *f)
175662306a36Sopenharmony_ci{
175762306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
175862306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	v4l2_ctrl_handler_log_status(vdev->ctrl_handler, btv->c.v4l2_dev.name);
176162306a36Sopenharmony_ci	bttv_call_all(btv, core, log_status);
176262306a36Sopenharmony_ci	return 0;
176362306a36Sopenharmony_ci}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
176662306a36Sopenharmony_cistatic int bttv_g_register(struct file *file, void *f,
176762306a36Sopenharmony_ci					struct v4l2_dbg_register *reg)
176862306a36Sopenharmony_ci{
176962306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	/* bt848 has a 12-bit register space */
177262306a36Sopenharmony_ci	reg->reg &= 0xfff;
177362306a36Sopenharmony_ci	reg->val = btread(reg->reg);
177462306a36Sopenharmony_ci	reg->size = 1;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	return 0;
177762306a36Sopenharmony_ci}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_cistatic int bttv_s_register(struct file *file, void *f,
178062306a36Sopenharmony_ci					const struct v4l2_dbg_register *reg)
178162306a36Sopenharmony_ci{
178262306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	/* bt848 has a 12-bit register space */
178562306a36Sopenharmony_ci	btwrite(reg->val, reg->reg & 0xfff);
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	return 0;
178862306a36Sopenharmony_ci}
178962306a36Sopenharmony_ci#endif
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci/* Given cropping boundaries b and the scaled width and height of a
179262306a36Sopenharmony_ci   single field or frame, which must not exceed hardware limits, this
179362306a36Sopenharmony_ci   function adjusts the cropping parameters c. */
179462306a36Sopenharmony_cistatic void
179562306a36Sopenharmony_cibttv_crop_adjust	(struct bttv_crop *             c,
179662306a36Sopenharmony_ci			 const struct v4l2_rect *	b,
179762306a36Sopenharmony_ci			 __s32                          width,
179862306a36Sopenharmony_ci			 __s32                          height,
179962306a36Sopenharmony_ci			 enum v4l2_field                field)
180062306a36Sopenharmony_ci{
180162306a36Sopenharmony_ci	__s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field);
180262306a36Sopenharmony_ci	__s32 max_left;
180362306a36Sopenharmony_ci	__s32 max_top;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	if (width < c->min_scaled_width) {
180662306a36Sopenharmony_ci		/* Max. hor. scale factor 16:1. */
180762306a36Sopenharmony_ci		c->rect.width = width * 16;
180862306a36Sopenharmony_ci	} else if (width > c->max_scaled_width) {
180962306a36Sopenharmony_ci		/* Min. hor. scale factor 1:1. */
181062306a36Sopenharmony_ci		c->rect.width = width;
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci		max_left = b->left + b->width - width;
181362306a36Sopenharmony_ci		max_left = min(max_left, (__s32) MAX_HDELAY);
181462306a36Sopenharmony_ci		if (c->rect.left > max_left)
181562306a36Sopenharmony_ci			c->rect.left = max_left;
181662306a36Sopenharmony_ci	}
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	if (height < c->min_scaled_height) {
181962306a36Sopenharmony_ci		/* Max. vert. scale factor 16:1, single fields 8:1. */
182062306a36Sopenharmony_ci		c->rect.height = height * 16;
182162306a36Sopenharmony_ci	} else if (frame_height > c->max_scaled_height) {
182262306a36Sopenharmony_ci		/* Min. vert. scale factor 1:1.
182362306a36Sopenharmony_ci		   Top and height count field lines times two. */
182462306a36Sopenharmony_ci		c->rect.height = (frame_height + 1) & ~1;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci		max_top = b->top + b->height - c->rect.height;
182762306a36Sopenharmony_ci		if (c->rect.top > max_top)
182862306a36Sopenharmony_ci			c->rect.top = max_top;
182962306a36Sopenharmony_ci	}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	bttv_crop_calc_limits(c);
183262306a36Sopenharmony_ci}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci/* Returns an error if scaling to a frame or single field with the given
183562306a36Sopenharmony_ci   width and height is not possible with the current cropping parameters
183662306a36Sopenharmony_ci   and width aligned according to width_mask. If adjust_size is TRUE the
183762306a36Sopenharmony_ci   function may adjust the width and/or height instead, rounding width
183862306a36Sopenharmony_ci   to (width + width_bias) & width_mask. If adjust_crop is TRUE it may
183962306a36Sopenharmony_ci   also adjust the current cropping parameters to get closer to the
184062306a36Sopenharmony_ci   desired image size. */
184162306a36Sopenharmony_cistatic int
184262306a36Sopenharmony_cilimit_scaled_size_lock(struct bttv *btv, __s32 *width, __s32 *height,
184362306a36Sopenharmony_ci		       enum v4l2_field field, unsigned int width_mask,
184462306a36Sopenharmony_ci		       unsigned int width_bias, int adjust_size,
184562306a36Sopenharmony_ci		       int adjust_crop)
184662306a36Sopenharmony_ci{
184762306a36Sopenharmony_ci	const struct v4l2_rect *b;
184862306a36Sopenharmony_ci	struct bttv_crop *c;
184962306a36Sopenharmony_ci	__s32 min_width;
185062306a36Sopenharmony_ci	__s32 min_height;
185162306a36Sopenharmony_ci	__s32 max_width;
185262306a36Sopenharmony_ci	__s32 max_height;
185362306a36Sopenharmony_ci	int rc;
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	WARN_ON((int)width_mask >= 0 ||
185662306a36Sopenharmony_ci		width_bias >= (unsigned int)(-width_mask));
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	/* Make sure tvnorm, vbi_end and the current cropping parameters
185962306a36Sopenharmony_ci	   remain consistent until we're done. */
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	/* Do crop - use current, don't - use default parameters. */
186462306a36Sopenharmony_ci	c = &btv->crop[!!btv->do_crop];
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	if (btv->do_crop
186762306a36Sopenharmony_ci	    && adjust_size
186862306a36Sopenharmony_ci	    && adjust_crop
186962306a36Sopenharmony_ci	    && !locked_btres(btv, VIDEO_RESOURCES)) {
187062306a36Sopenharmony_ci		min_width = 48;
187162306a36Sopenharmony_ci		min_height = 32;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci		/* We cannot scale up. When the scaled image is larger
187462306a36Sopenharmony_ci		   than crop.rect we adjust the crop.rect as required
187562306a36Sopenharmony_ci		   by the V4L2 spec, hence cropcap.bounds are our limit. */
187662306a36Sopenharmony_ci		max_width = min_t(unsigned int, b->width, MAX_HACTIVE);
187762306a36Sopenharmony_ci		max_height = b->height;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci		/* We cannot capture the same line as video and VBI data.
188062306a36Sopenharmony_ci		   Note btv->vbi_end is really a minimum, see
188162306a36Sopenharmony_ci		   bttv_vbi_try_fmt(). */
188262306a36Sopenharmony_ci		if (btv->vbi_end > b->top) {
188362306a36Sopenharmony_ci			max_height -= btv->vbi_end - b->top;
188462306a36Sopenharmony_ci			rc = -EBUSY;
188562306a36Sopenharmony_ci			if (min_height > max_height)
188662306a36Sopenharmony_ci				goto fail;
188762306a36Sopenharmony_ci		}
188862306a36Sopenharmony_ci	} else {
188962306a36Sopenharmony_ci		rc = -EBUSY;
189062306a36Sopenharmony_ci		if (btv->vbi_end > c->rect.top)
189162306a36Sopenharmony_ci			goto fail;
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci		min_width  = c->min_scaled_width;
189462306a36Sopenharmony_ci		min_height = c->min_scaled_height;
189562306a36Sopenharmony_ci		max_width  = c->max_scaled_width;
189662306a36Sopenharmony_ci		max_height = c->max_scaled_height;
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci		adjust_crop = 0;
189962306a36Sopenharmony_ci	}
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	min_width = (min_width - width_mask - 1) & width_mask;
190262306a36Sopenharmony_ci	max_width = max_width & width_mask;
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	/* Max. scale factor is 16:1 for frames, 8:1 for fields. */
190562306a36Sopenharmony_ci	/* Min. scale factor is 1:1. */
190662306a36Sopenharmony_ci	max_height >>= !V4L2_FIELD_HAS_BOTH(field);
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	if (adjust_size) {
190962306a36Sopenharmony_ci		*width = clamp(*width, min_width, max_width);
191062306a36Sopenharmony_ci		*height = clamp(*height, min_height, max_height);
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci		/* Round after clamping to avoid overflow. */
191362306a36Sopenharmony_ci		*width = (*width + width_bias) & width_mask;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci		if (adjust_crop) {
191662306a36Sopenharmony_ci			bttv_crop_adjust(c, b, *width, *height, field);
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci			if (btv->vbi_end > c->rect.top) {
191962306a36Sopenharmony_ci				/* Move the crop window out of the way. */
192062306a36Sopenharmony_ci				c->rect.top = btv->vbi_end;
192162306a36Sopenharmony_ci			}
192262306a36Sopenharmony_ci		}
192362306a36Sopenharmony_ci	} else {
192462306a36Sopenharmony_ci		rc = -EINVAL;
192562306a36Sopenharmony_ci		if (*width  < min_width ||
192662306a36Sopenharmony_ci		    *height < min_height ||
192762306a36Sopenharmony_ci		    *width  > max_width ||
192862306a36Sopenharmony_ci		    *height > max_height ||
192962306a36Sopenharmony_ci		    0 != (*width & ~width_mask))
193062306a36Sopenharmony_ci			goto fail;
193162306a36Sopenharmony_ci	}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	rc = 0; /* success */
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci fail:
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	return rc;
193862306a36Sopenharmony_ci}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_cistatic int bttv_switch_type(struct bttv *btv, enum v4l2_buf_type type)
194162306a36Sopenharmony_ci{
194262306a36Sopenharmony_ci	int res;
194362306a36Sopenharmony_ci	struct vb2_queue *q;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	switch (type) {
194662306a36Sopenharmony_ci	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
194762306a36Sopenharmony_ci		q = &btv->capq;
194862306a36Sopenharmony_ci		res = RESOURCE_VIDEO_STREAM;
194962306a36Sopenharmony_ci		break;
195062306a36Sopenharmony_ci	case V4L2_BUF_TYPE_VBI_CAPTURE:
195162306a36Sopenharmony_ci		q = &btv->vbiq;
195262306a36Sopenharmony_ci		res = RESOURCE_VBI;
195362306a36Sopenharmony_ci		break;
195462306a36Sopenharmony_ci	default:
195562306a36Sopenharmony_ci		WARN_ON(1);
195662306a36Sopenharmony_ci		return -EINVAL;
195762306a36Sopenharmony_ci	}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	if (check_btres(btv, res))
196062306a36Sopenharmony_ci		return -EBUSY;
196162306a36Sopenharmony_ci	if (vb2_is_busy(q))
196262306a36Sopenharmony_ci		return -EBUSY;
196362306a36Sopenharmony_ci	btv->type = type;
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	return 0;
196662306a36Sopenharmony_ci}
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_cistatic void
196962306a36Sopenharmony_cipix_format_set_size     (struct v4l2_pix_format *       f,
197062306a36Sopenharmony_ci			 const struct bttv_format *     fmt,
197162306a36Sopenharmony_ci			 unsigned int                   width,
197262306a36Sopenharmony_ci			 unsigned int                   height)
197362306a36Sopenharmony_ci{
197462306a36Sopenharmony_ci	f->width = width;
197562306a36Sopenharmony_ci	f->height = height;
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	if (fmt->flags & FORMAT_FLAGS_PLANAR) {
197862306a36Sopenharmony_ci		f->bytesperline = width; /* Y plane */
197962306a36Sopenharmony_ci		f->sizeimage = (width * height * fmt->depth) >> 3;
198062306a36Sopenharmony_ci	} else {
198162306a36Sopenharmony_ci		f->bytesperline = (width * fmt->depth) >> 3;
198262306a36Sopenharmony_ci		f->sizeimage = height * f->bytesperline;
198362306a36Sopenharmony_ci	}
198462306a36Sopenharmony_ci}
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_cistatic int bttv_g_fmt_vid_cap(struct file *file, void *priv,
198762306a36Sopenharmony_ci					struct v4l2_format *f)
198862306a36Sopenharmony_ci{
198962306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	pix_format_set_size(&f->fmt.pix, btv->fmt, btv->width, btv->height);
199262306a36Sopenharmony_ci	f->fmt.pix.field = btv->field;
199362306a36Sopenharmony_ci	f->fmt.pix.pixelformat  = btv->fmt->fourcc;
199462306a36Sopenharmony_ci	f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	return 0;
199762306a36Sopenharmony_ci}
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_cistatic void bttv_get_width_mask_vid_cap(const struct bttv_format *fmt,
200062306a36Sopenharmony_ci					unsigned int *width_mask,
200162306a36Sopenharmony_ci					unsigned int *width_bias)
200262306a36Sopenharmony_ci{
200362306a36Sopenharmony_ci	if (fmt->flags & FORMAT_FLAGS_PLANAR) {
200462306a36Sopenharmony_ci		*width_mask = ~15; /* width must be a multiple of 16 pixels */
200562306a36Sopenharmony_ci		*width_bias = 8;   /* nearest */
200662306a36Sopenharmony_ci	} else {
200762306a36Sopenharmony_ci		*width_mask = ~3; /* width must be a multiple of 4 pixels */
200862306a36Sopenharmony_ci		*width_bias = 2;  /* nearest */
200962306a36Sopenharmony_ci	}
201062306a36Sopenharmony_ci}
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_cistatic int bttv_try_fmt_vid_cap(struct file *file, void *priv,
201362306a36Sopenharmony_ci						struct v4l2_format *f)
201462306a36Sopenharmony_ci{
201562306a36Sopenharmony_ci	const struct bttv_format *fmt;
201662306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
201762306a36Sopenharmony_ci	enum v4l2_field field;
201862306a36Sopenharmony_ci	__s32 width, height;
201962306a36Sopenharmony_ci	__s32 height2;
202062306a36Sopenharmony_ci	unsigned int width_mask, width_bias;
202162306a36Sopenharmony_ci	int rc;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
202462306a36Sopenharmony_ci	if (NULL == fmt)
202562306a36Sopenharmony_ci		return -EINVAL;
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	field = f->fmt.pix.field;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	switch (field) {
203062306a36Sopenharmony_ci	case V4L2_FIELD_TOP:
203162306a36Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
203262306a36Sopenharmony_ci	case V4L2_FIELD_ALTERNATE:
203362306a36Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
203462306a36Sopenharmony_ci		break;
203562306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_BT:
203662306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_TB:
203762306a36Sopenharmony_ci		if (!(fmt->flags & FORMAT_FLAGS_PLANAR)) {
203862306a36Sopenharmony_ci			field = V4L2_FIELD_SEQ_TB;
203962306a36Sopenharmony_ci			break;
204062306a36Sopenharmony_ci		}
204162306a36Sopenharmony_ci		fallthrough;
204262306a36Sopenharmony_ci	default: /* FIELD_ANY case */
204362306a36Sopenharmony_ci		height2 = btv->crop[!!btv->do_crop].rect.height >> 1;
204462306a36Sopenharmony_ci		field = (f->fmt.pix.height > height2)
204562306a36Sopenharmony_ci			? V4L2_FIELD_INTERLACED
204662306a36Sopenharmony_ci			: V4L2_FIELD_BOTTOM;
204762306a36Sopenharmony_ci		break;
204862306a36Sopenharmony_ci	}
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	width = f->fmt.pix.width;
205162306a36Sopenharmony_ci	height = f->fmt.pix.height;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias);
205462306a36Sopenharmony_ci	rc = limit_scaled_size_lock(btv, &width, &height, field, width_mask,
205562306a36Sopenharmony_ci				    width_bias, 1, 0);
205662306a36Sopenharmony_ci	if (0 != rc)
205762306a36Sopenharmony_ci		return rc;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	/* update data for the application */
206062306a36Sopenharmony_ci	f->fmt.pix.field = field;
206162306a36Sopenharmony_ci	pix_format_set_size(&f->fmt.pix, fmt, width, height);
206262306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	return 0;
206562306a36Sopenharmony_ci}
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_cistatic int bttv_s_fmt_vid_cap(struct file *file, void *priv,
206862306a36Sopenharmony_ci			      struct v4l2_format *f)
206962306a36Sopenharmony_ci{
207062306a36Sopenharmony_ci	int retval;
207162306a36Sopenharmony_ci	const struct bttv_format *fmt;
207262306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
207362306a36Sopenharmony_ci	__s32 width, height;
207462306a36Sopenharmony_ci	unsigned int width_mask, width_bias;
207562306a36Sopenharmony_ci	enum v4l2_field field;
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	retval = bttv_switch_type(btv, f->type);
207862306a36Sopenharmony_ci	if (0 != retval)
207962306a36Sopenharmony_ci		return retval;
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	retval = bttv_try_fmt_vid_cap(file, priv, f);
208262306a36Sopenharmony_ci	if (0 != retval)
208362306a36Sopenharmony_ci		return retval;
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	width = f->fmt.pix.width;
208662306a36Sopenharmony_ci	height = f->fmt.pix.height;
208762306a36Sopenharmony_ci	field = f->fmt.pix.field;
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
209062306a36Sopenharmony_ci	bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias);
209162306a36Sopenharmony_ci	retval = limit_scaled_size_lock(btv, &width, &height, f->fmt.pix.field,
209262306a36Sopenharmony_ci					width_mask, width_bias, 1, 1);
209362306a36Sopenharmony_ci	if (0 != retval)
209462306a36Sopenharmony_ci		return retval;
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	f->fmt.pix.field = field;
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	/* update our state information */
209962306a36Sopenharmony_ci	btv->fmt = fmt;
210062306a36Sopenharmony_ci	btv->width = f->fmt.pix.width;
210162306a36Sopenharmony_ci	btv->height = f->fmt.pix.height;
210262306a36Sopenharmony_ci	btv->field = f->fmt.pix.field;
210362306a36Sopenharmony_ci	/*
210462306a36Sopenharmony_ci	 * When field is V4L2_FIELD_ALTERNATE, buffers will be either
210562306a36Sopenharmony_ci	 * V4L2_FIELD_TOP or V4L2_FIELD_BOTTOM depending on the value of
210662306a36Sopenharmony_ci	 * field_last. Initialize field_last to V4L2_FIELD_BOTTOM so that
210762306a36Sopenharmony_ci	 * streaming starts with a V4L2_FIELD_TOP buffer.
210862306a36Sopenharmony_ci	 */
210962306a36Sopenharmony_ci	btv->field_last = V4L2_FIELD_BOTTOM;
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	return 0;
211262306a36Sopenharmony_ci}
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_cistatic int bttv_querycap(struct file *file, void  *priv,
211562306a36Sopenharmony_ci				struct v4l2_capability *cap)
211662306a36Sopenharmony_ci{
211762306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	if (0 == v4l2)
212062306a36Sopenharmony_ci		return -EINVAL;
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	strscpy(cap->driver, "bttv", sizeof(cap->driver));
212362306a36Sopenharmony_ci	strscpy(cap->card, btv->video_dev.name, sizeof(cap->card));
212462306a36Sopenharmony_ci	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
212562306a36Sopenharmony_ci			    V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS;
212662306a36Sopenharmony_ci	if (video_is_registered(&btv->vbi_dev))
212762306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
212862306a36Sopenharmony_ci	if (video_is_registered(&btv->radio_dev)) {
212962306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_RADIO;
213062306a36Sopenharmony_ci		if (btv->has_tea575x)
213162306a36Sopenharmony_ci			cap->capabilities |= V4L2_CAP_HW_FREQ_SEEK;
213262306a36Sopenharmony_ci	}
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	/*
213562306a36Sopenharmony_ci	 * No need to lock here: those vars are initialized during board
213662306a36Sopenharmony_ci	 * probe and remains untouched during the rest of the driver lifecycle
213762306a36Sopenharmony_ci	 */
213862306a36Sopenharmony_ci	if (btv->has_saa6588)
213962306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
214062306a36Sopenharmony_ci	if (btv->tuner_type != TUNER_ABSENT)
214162306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_TUNER;
214262306a36Sopenharmony_ci	return 0;
214362306a36Sopenharmony_ci}
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_cistatic int bttv_enum_fmt_vid_cap(struct file *file, void  *priv,
214662306a36Sopenharmony_ci				 struct v4l2_fmtdesc *f)
214762306a36Sopenharmony_ci{
214862306a36Sopenharmony_ci	int index = -1, i;
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	for (i = 0; i < FORMATS; i++) {
215162306a36Sopenharmony_ci		if (formats[i].fourcc != -1)
215262306a36Sopenharmony_ci			index++;
215362306a36Sopenharmony_ci		if ((unsigned int)index == f->index)
215462306a36Sopenharmony_ci			break;
215562306a36Sopenharmony_ci	}
215662306a36Sopenharmony_ci	if (FORMATS == i)
215762306a36Sopenharmony_ci		return -EINVAL;
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci	f->pixelformat = formats[i].fourcc;
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	return 0;
216262306a36Sopenharmony_ci}
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_cistatic int bttv_g_parm(struct file *file, void *f,
216562306a36Sopenharmony_ci				struct v4l2_streamparm *parm)
216662306a36Sopenharmony_ci{
216762306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
217062306a36Sopenharmony_ci		return -EINVAL;
217162306a36Sopenharmony_ci	parm->parm.capture.readbuffers = gbuffers;
217262306a36Sopenharmony_ci	v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id,
217362306a36Sopenharmony_ci				    &parm->parm.capture.timeperframe);
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	return 0;
217662306a36Sopenharmony_ci}
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_cistatic int bttv_g_tuner(struct file *file, void *priv,
217962306a36Sopenharmony_ci				struct v4l2_tuner *t)
218062306a36Sopenharmony_ci{
218162306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	if (0 != t->index)
218462306a36Sopenharmony_ci		return -EINVAL;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	t->rxsubchans = V4L2_TUNER_SUB_MONO;
218762306a36Sopenharmony_ci	t->capability = V4L2_TUNER_CAP_NORM;
218862306a36Sopenharmony_ci	bttv_call_all(btv, tuner, g_tuner, t);
218962306a36Sopenharmony_ci	strscpy(t->name, "Television", sizeof(t->name));
219062306a36Sopenharmony_ci	t->type       = V4L2_TUNER_ANALOG_TV;
219162306a36Sopenharmony_ci	if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
219262306a36Sopenharmony_ci		t->signal = 0xffff;
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	if (btv->audio_mode_gpio)
219562306a36Sopenharmony_ci		btv->audio_mode_gpio(btv, t, 0);
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	return 0;
219862306a36Sopenharmony_ci}
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_cistatic int bttv_g_pixelaspect(struct file *file, void *priv,
220162306a36Sopenharmony_ci			      int type, struct v4l2_fract *f)
220262306a36Sopenharmony_ci{
220362306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
220662306a36Sopenharmony_ci		return -EINVAL;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	/* defrect and bounds are set via g_selection */
220962306a36Sopenharmony_ci	*f = bttv_tvnorms[btv->tvnorm].cropcap.pixelaspect;
221062306a36Sopenharmony_ci	return 0;
221162306a36Sopenharmony_ci}
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_cistatic int bttv_g_selection(struct file *file, void *f, struct v4l2_selection *sel)
221462306a36Sopenharmony_ci{
221562306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
221862306a36Sopenharmony_ci		return -EINVAL;
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	switch (sel->target) {
222162306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
222262306a36Sopenharmony_ci		sel->r = btv->crop[!!btv->do_crop].rect;
222362306a36Sopenharmony_ci		break;
222462306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
222562306a36Sopenharmony_ci		sel->r = bttv_tvnorms[btv->tvnorm].cropcap.defrect;
222662306a36Sopenharmony_ci		break;
222762306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
222862306a36Sopenharmony_ci		sel->r = bttv_tvnorms[btv->tvnorm].cropcap.bounds;
222962306a36Sopenharmony_ci		break;
223062306a36Sopenharmony_ci	default:
223162306a36Sopenharmony_ci		return -EINVAL;
223262306a36Sopenharmony_ci	}
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci	return 0;
223562306a36Sopenharmony_ci}
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_cistatic int bttv_s_selection(struct file *file, void *f, struct v4l2_selection *sel)
223862306a36Sopenharmony_ci{
223962306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
224062306a36Sopenharmony_ci	const struct v4l2_rect *b;
224162306a36Sopenharmony_ci	int retval;
224262306a36Sopenharmony_ci	struct bttv_crop c;
224362306a36Sopenharmony_ci	__s32 b_left;
224462306a36Sopenharmony_ci	__s32 b_top;
224562306a36Sopenharmony_ci	__s32 b_right;
224662306a36Sopenharmony_ci	__s32 b_bottom;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
224962306a36Sopenharmony_ci		return -EINVAL;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	if (sel->target != V4L2_SEL_TGT_CROP)
225262306a36Sopenharmony_ci		return -EINVAL;
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	/* Make sure tvnorm, vbi_end and the current cropping
225562306a36Sopenharmony_ci	   parameters remain consistent until we're done. Note
225662306a36Sopenharmony_ci	   read() may change vbi_end in check_alloc_btres_lock(). */
225762306a36Sopenharmony_ci	retval = -EBUSY;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	if (locked_btres(btv, VIDEO_RESOURCES))
226062306a36Sopenharmony_ci		return retval;
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci	b_left = b->left;
226562306a36Sopenharmony_ci	b_right = b_left + b->width;
226662306a36Sopenharmony_ci	b_bottom = b->top + b->height;
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	b_top = max(b->top, btv->vbi_end);
226962306a36Sopenharmony_ci	if (b_top + 32 >= b_bottom) {
227062306a36Sopenharmony_ci		return retval;
227162306a36Sopenharmony_ci	}
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	/* Min. scaled size 48 x 32. */
227462306a36Sopenharmony_ci	c.rect.left = clamp_t(s32, sel->r.left, b_left, b_right - 48);
227562306a36Sopenharmony_ci	c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	c.rect.width = clamp_t(s32, sel->r.width,
227862306a36Sopenharmony_ci			     48, b_right - c.rect.left);
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	c.rect.top = clamp_t(s32, sel->r.top, b_top, b_bottom - 32);
228162306a36Sopenharmony_ci	/* Top and height must be a multiple of two. */
228262306a36Sopenharmony_ci	c.rect.top = (c.rect.top + 1) & ~1;
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci	c.rect.height = clamp_t(s32, sel->r.height,
228562306a36Sopenharmony_ci			      32, b_bottom - c.rect.top);
228662306a36Sopenharmony_ci	c.rect.height = (c.rect.height + 1) & ~1;
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	bttv_crop_calc_limits(&c);
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci	sel->r = c.rect;
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	btv->crop[1] = c;
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	btv->do_crop = 1;
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	if (btv->width < c.min_scaled_width)
229762306a36Sopenharmony_ci		btv->width = c.min_scaled_width;
229862306a36Sopenharmony_ci	else if (btv->width > c.max_scaled_width)
229962306a36Sopenharmony_ci		btv->width = c.max_scaled_width;
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	if (btv->height < c.min_scaled_height)
230262306a36Sopenharmony_ci		btv->height = c.min_scaled_height;
230362306a36Sopenharmony_ci	else if (btv->height > c.max_scaled_height)
230462306a36Sopenharmony_ci		btv->height = c.max_scaled_height;
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_ci	return 0;
230762306a36Sopenharmony_ci}
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_cistatic const struct v4l2_file_operations bttv_fops =
231062306a36Sopenharmony_ci{
231162306a36Sopenharmony_ci	.owner		  = THIS_MODULE,
231262306a36Sopenharmony_ci	.open		  = v4l2_fh_open,
231362306a36Sopenharmony_ci	.release	  = vb2_fop_release,
231462306a36Sopenharmony_ci	.unlocked_ioctl	  = video_ioctl2,
231562306a36Sopenharmony_ci	.read		  = vb2_fop_read,
231662306a36Sopenharmony_ci	.mmap		  = vb2_fop_mmap,
231762306a36Sopenharmony_ci	.poll		  = vb2_fop_poll,
231862306a36Sopenharmony_ci};
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops bttv_ioctl_ops = {
232162306a36Sopenharmony_ci	.vidioc_querycap                = bttv_querycap,
232262306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap        = bttv_enum_fmt_vid_cap,
232362306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap           = bttv_g_fmt_vid_cap,
232462306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap         = bttv_try_fmt_vid_cap,
232562306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap           = bttv_s_fmt_vid_cap,
232662306a36Sopenharmony_ci	.vidioc_g_fmt_vbi_cap           = bttv_g_fmt_vbi_cap,
232762306a36Sopenharmony_ci	.vidioc_try_fmt_vbi_cap         = bttv_try_fmt_vbi_cap,
232862306a36Sopenharmony_ci	.vidioc_s_fmt_vbi_cap           = bttv_s_fmt_vbi_cap,
232962306a36Sopenharmony_ci	.vidioc_g_pixelaspect           = bttv_g_pixelaspect,
233062306a36Sopenharmony_ci	.vidioc_reqbufs                 = vb2_ioctl_reqbufs,
233162306a36Sopenharmony_ci	.vidioc_create_bufs             = vb2_ioctl_create_bufs,
233262306a36Sopenharmony_ci	.vidioc_querybuf                = vb2_ioctl_querybuf,
233362306a36Sopenharmony_ci	.vidioc_qbuf                    = vb2_ioctl_qbuf,
233462306a36Sopenharmony_ci	.vidioc_dqbuf                   = vb2_ioctl_dqbuf,
233562306a36Sopenharmony_ci	.vidioc_streamon                = vb2_ioctl_streamon,
233662306a36Sopenharmony_ci	.vidioc_streamoff               = vb2_ioctl_streamoff,
233762306a36Sopenharmony_ci	.vidioc_s_std                   = bttv_s_std,
233862306a36Sopenharmony_ci	.vidioc_g_std                   = bttv_g_std,
233962306a36Sopenharmony_ci	.vidioc_enum_input              = bttv_enum_input,
234062306a36Sopenharmony_ci	.vidioc_g_input                 = bttv_g_input,
234162306a36Sopenharmony_ci	.vidioc_s_input                 = bttv_s_input,
234262306a36Sopenharmony_ci	.vidioc_g_tuner                 = bttv_g_tuner,
234362306a36Sopenharmony_ci	.vidioc_s_tuner                 = bttv_s_tuner,
234462306a36Sopenharmony_ci	.vidioc_g_selection             = bttv_g_selection,
234562306a36Sopenharmony_ci	.vidioc_s_selection             = bttv_s_selection,
234662306a36Sopenharmony_ci	.vidioc_g_parm                  = bttv_g_parm,
234762306a36Sopenharmony_ci	.vidioc_g_frequency             = bttv_g_frequency,
234862306a36Sopenharmony_ci	.vidioc_s_frequency             = bttv_s_frequency,
234962306a36Sopenharmony_ci	.vidioc_log_status		= bttv_log_status,
235062306a36Sopenharmony_ci	.vidioc_querystd		= bttv_querystd,
235162306a36Sopenharmony_ci	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
235262306a36Sopenharmony_ci	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
235362306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
235462306a36Sopenharmony_ci	.vidioc_g_register		= bttv_g_register,
235562306a36Sopenharmony_ci	.vidioc_s_register		= bttv_s_register,
235662306a36Sopenharmony_ci#endif
235762306a36Sopenharmony_ci};
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_cistatic struct video_device bttv_video_template = {
236062306a36Sopenharmony_ci	.fops         = &bttv_fops,
236162306a36Sopenharmony_ci	.ioctl_ops    = &bttv_ioctl_ops,
236262306a36Sopenharmony_ci	.tvnorms      = BTTV_NORMS,
236362306a36Sopenharmony_ci};
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
236662306a36Sopenharmony_ci/* radio interface                                                         */
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_cistatic int radio_open(struct file *file)
236962306a36Sopenharmony_ci{
237062306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
237162306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
237262306a36Sopenharmony_ci	int ret = v4l2_fh_open(file);
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	if (ret)
237562306a36Sopenharmony_ci		return ret;
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	dprintk("open dev=%s\n", video_device_node_name(vdev));
237862306a36Sopenharmony_ci	dprintk("%d: open called (radio)\n", btv->c.nr);
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	btv->radio_user++;
238162306a36Sopenharmony_ci	audio_mute(btv, btv->mute);
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	return 0;
238462306a36Sopenharmony_ci}
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_cistatic int radio_release(struct file *file)
238762306a36Sopenharmony_ci{
238862306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
238962306a36Sopenharmony_ci	struct saa6588_command cmd;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	btv->radio_user--;
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci	bttv_call_all(btv, core, command, SAA6588_CMD_CLOSE, &cmd);
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	if (btv->radio_user == 0)
239662306a36Sopenharmony_ci		btv->has_radio_tuner = 0;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	v4l2_fh_release(file);
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	return 0;
240162306a36Sopenharmony_ci}
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_cistatic int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
240462306a36Sopenharmony_ci{
240562306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	if (0 != t->index)
240862306a36Sopenharmony_ci		return -EINVAL;
240962306a36Sopenharmony_ci	strscpy(t->name, "Radio", sizeof(t->name));
241062306a36Sopenharmony_ci	t->type = V4L2_TUNER_RADIO;
241162306a36Sopenharmony_ci	radio_enable(btv);
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	bttv_call_all(btv, tuner, g_tuner, t);
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	if (btv->audio_mode_gpio)
241662306a36Sopenharmony_ci		btv->audio_mode_gpio(btv, t, 0);
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	if (btv->has_tea575x)
241962306a36Sopenharmony_ci		return snd_tea575x_g_tuner(&btv->tea, t);
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	return 0;
242262306a36Sopenharmony_ci}
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_cistatic int radio_s_tuner(struct file *file, void *priv,
242562306a36Sopenharmony_ci					const struct v4l2_tuner *t)
242662306a36Sopenharmony_ci{
242762306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_ci	if (0 != t->index)
243062306a36Sopenharmony_ci		return -EINVAL;
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	radio_enable(btv);
243362306a36Sopenharmony_ci	bttv_call_all(btv, tuner, s_tuner, t);
243462306a36Sopenharmony_ci	return 0;
243562306a36Sopenharmony_ci}
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_cistatic int radio_s_hw_freq_seek(struct file *file, void *priv,
243862306a36Sopenharmony_ci					const struct v4l2_hw_freq_seek *a)
243962306a36Sopenharmony_ci{
244062306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
244162306a36Sopenharmony_ci
244262306a36Sopenharmony_ci	if (btv->has_tea575x)
244362306a36Sopenharmony_ci		return snd_tea575x_s_hw_freq_seek(file, &btv->tea, a);
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	return -ENOTTY;
244662306a36Sopenharmony_ci}
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_cistatic int radio_enum_freq_bands(struct file *file, void *priv,
244962306a36Sopenharmony_ci					 struct v4l2_frequency_band *band)
245062306a36Sopenharmony_ci{
245162306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	if (btv->has_tea575x)
245462306a36Sopenharmony_ci		return snd_tea575x_enum_freq_bands(&btv->tea, band);
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	return -ENOTTY;
245762306a36Sopenharmony_ci}
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_cistatic ssize_t radio_read(struct file *file, char __user *data,
246062306a36Sopenharmony_ci			 size_t count, loff_t *ppos)
246162306a36Sopenharmony_ci{
246262306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
246362306a36Sopenharmony_ci	struct saa6588_command cmd;
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	cmd.block_count = count / 3;
246662306a36Sopenharmony_ci	cmd.nonblocking = file->f_flags & O_NONBLOCK;
246762306a36Sopenharmony_ci	cmd.buffer = data;
246862306a36Sopenharmony_ci	cmd.instance = file;
246962306a36Sopenharmony_ci	cmd.result = -ENODEV;
247062306a36Sopenharmony_ci	radio_enable(btv);
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci	bttv_call_all(btv, core, command, SAA6588_CMD_READ, &cmd);
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	return cmd.result;
247562306a36Sopenharmony_ci}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_cistatic __poll_t radio_poll(struct file *file, poll_table *wait)
247862306a36Sopenharmony_ci{
247962306a36Sopenharmony_ci	struct bttv *btv = video_drvdata(file);
248062306a36Sopenharmony_ci	struct saa6588_command cmd;
248162306a36Sopenharmony_ci	__poll_t rc = v4l2_ctrl_poll(file, wait);
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	radio_enable(btv);
248462306a36Sopenharmony_ci	cmd.instance = file;
248562306a36Sopenharmony_ci	cmd.event_list = wait;
248662306a36Sopenharmony_ci	cmd.poll_mask = 0;
248762306a36Sopenharmony_ci	bttv_call_all(btv, core, command, SAA6588_CMD_POLL, &cmd);
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	return rc | cmd.poll_mask;
249062306a36Sopenharmony_ci}
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_cistatic const struct v4l2_file_operations radio_fops =
249362306a36Sopenharmony_ci{
249462306a36Sopenharmony_ci	.owner	  = THIS_MODULE,
249562306a36Sopenharmony_ci	.open	  = radio_open,
249662306a36Sopenharmony_ci	.read     = radio_read,
249762306a36Sopenharmony_ci	.release  = radio_release,
249862306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
249962306a36Sopenharmony_ci	.poll     = radio_poll,
250062306a36Sopenharmony_ci};
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops radio_ioctl_ops = {
250362306a36Sopenharmony_ci	.vidioc_querycap        = bttv_querycap,
250462306a36Sopenharmony_ci	.vidioc_log_status	= bttv_log_status,
250562306a36Sopenharmony_ci	.vidioc_g_tuner         = radio_g_tuner,
250662306a36Sopenharmony_ci	.vidioc_s_tuner         = radio_s_tuner,
250762306a36Sopenharmony_ci	.vidioc_g_frequency     = bttv_g_frequency,
250862306a36Sopenharmony_ci	.vidioc_s_frequency     = bttv_s_frequency,
250962306a36Sopenharmony_ci	.vidioc_s_hw_freq_seek	= radio_s_hw_freq_seek,
251062306a36Sopenharmony_ci	.vidioc_enum_freq_bands	= radio_enum_freq_bands,
251162306a36Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
251262306a36Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
251362306a36Sopenharmony_ci};
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_cistatic struct video_device radio_template = {
251662306a36Sopenharmony_ci	.fops      = &radio_fops,
251762306a36Sopenharmony_ci	.ioctl_ops = &radio_ioctl_ops,
251862306a36Sopenharmony_ci};
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
252162306a36Sopenharmony_ci/* some debug code                                                         */
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_cistatic int bttv_risc_decode(u32 risc)
252462306a36Sopenharmony_ci{
252562306a36Sopenharmony_ci	static char *instr[16] = {
252662306a36Sopenharmony_ci		[ BT848_RISC_WRITE     >> 28 ] = "write",
252762306a36Sopenharmony_ci		[ BT848_RISC_SKIP      >> 28 ] = "skip",
252862306a36Sopenharmony_ci		[ BT848_RISC_WRITEC    >> 28 ] = "writec",
252962306a36Sopenharmony_ci		[ BT848_RISC_JUMP      >> 28 ] = "jump",
253062306a36Sopenharmony_ci		[ BT848_RISC_SYNC      >> 28 ] = "sync",
253162306a36Sopenharmony_ci		[ BT848_RISC_WRITE123  >> 28 ] = "write123",
253262306a36Sopenharmony_ci		[ BT848_RISC_SKIP123   >> 28 ] = "skip123",
253362306a36Sopenharmony_ci		[ BT848_RISC_WRITE1S23 >> 28 ] = "write1s23",
253462306a36Sopenharmony_ci	};
253562306a36Sopenharmony_ci	static int incr[16] = {
253662306a36Sopenharmony_ci		[ BT848_RISC_WRITE     >> 28 ] = 2,
253762306a36Sopenharmony_ci		[ BT848_RISC_JUMP      >> 28 ] = 2,
253862306a36Sopenharmony_ci		[ BT848_RISC_SYNC      >> 28 ] = 2,
253962306a36Sopenharmony_ci		[ BT848_RISC_WRITE123  >> 28 ] = 5,
254062306a36Sopenharmony_ci		[ BT848_RISC_SKIP123   >> 28 ] = 2,
254162306a36Sopenharmony_ci		[ BT848_RISC_WRITE1S23 >> 28 ] = 3,
254262306a36Sopenharmony_ci	};
254362306a36Sopenharmony_ci	static char *bits[] = {
254462306a36Sopenharmony_ci		"be0",  "be1",  "be2",  "be3/resync",
254562306a36Sopenharmony_ci		"set0", "set1", "set2", "set3",
254662306a36Sopenharmony_ci		"clr0", "clr1", "clr2", "clr3",
254762306a36Sopenharmony_ci		"irq",  "res",  "eol",  "sol",
254862306a36Sopenharmony_ci	};
254962306a36Sopenharmony_ci	int i;
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	pr_cont("0x%08x [ %s", risc,
255262306a36Sopenharmony_ci	       instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
255362306a36Sopenharmony_ci	for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
255462306a36Sopenharmony_ci		if (risc & (1 << (i + 12)))
255562306a36Sopenharmony_ci			pr_cont(" %s", bits[i]);
255662306a36Sopenharmony_ci	pr_cont(" count=%d ]\n", risc & 0xfff);
255762306a36Sopenharmony_ci	return incr[risc >> 28] ? incr[risc >> 28] : 1;
255862306a36Sopenharmony_ci}
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_cistatic void bttv_risc_disasm(struct bttv *btv,
256162306a36Sopenharmony_ci			     struct btcx_riscmem *risc)
256262306a36Sopenharmony_ci{
256362306a36Sopenharmony_ci	unsigned int i,j,n;
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	pr_info("%s: risc disasm: %p [dma=0x%08lx]\n",
256662306a36Sopenharmony_ci		btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
256762306a36Sopenharmony_ci	for (i = 0; i < (risc->size >> 2); i += n) {
256862306a36Sopenharmony_ci		pr_info("%s:   0x%lx: ",
256962306a36Sopenharmony_ci			btv->c.v4l2_dev.name,
257062306a36Sopenharmony_ci			(unsigned long)(risc->dma + (i<<2)));
257162306a36Sopenharmony_ci		n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
257262306a36Sopenharmony_ci		for (j = 1; j < n; j++)
257362306a36Sopenharmony_ci			pr_info("%s:   0x%lx: 0x%08x [ arg #%d ]\n",
257462306a36Sopenharmony_ci				btv->c.v4l2_dev.name,
257562306a36Sopenharmony_ci				(unsigned long)(risc->dma + ((i+j)<<2)),
257662306a36Sopenharmony_ci				risc->cpu[i+j], j);
257762306a36Sopenharmony_ci		if (0 == risc->cpu[i])
257862306a36Sopenharmony_ci			break;
257962306a36Sopenharmony_ci	}
258062306a36Sopenharmony_ci}
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_cistatic void bttv_print_riscaddr(struct bttv *btv)
258362306a36Sopenharmony_ci{
258462306a36Sopenharmony_ci	pr_info("  main: %08llx\n", (unsigned long long)btv->main.dma);
258562306a36Sopenharmony_ci	pr_info("  vbi : o=%08llx e=%08llx\n",
258662306a36Sopenharmony_ci		btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
258762306a36Sopenharmony_ci		btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
258862306a36Sopenharmony_ci	pr_info("  cap : o=%08llx e=%08llx\n",
258962306a36Sopenharmony_ci		btv->curr.top
259062306a36Sopenharmony_ci		? (unsigned long long)btv->curr.top->top.dma : 0,
259162306a36Sopenharmony_ci		btv->curr.bottom
259262306a36Sopenharmony_ci		? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
259362306a36Sopenharmony_ci	bttv_risc_disasm(btv, &btv->main);
259462306a36Sopenharmony_ci}
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
259762306a36Sopenharmony_ci/* irq handler                                                             */
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_cistatic char *irq_name[] = {
260062306a36Sopenharmony_ci	"FMTCHG",  // format change detected (525 vs. 625)
260162306a36Sopenharmony_ci	"VSYNC",   // vertical sync (new field)
260262306a36Sopenharmony_ci	"HSYNC",   // horizontal sync
260362306a36Sopenharmony_ci	"OFLOW",   // chroma/luma AGC overflow
260462306a36Sopenharmony_ci	"HLOCK",   // horizontal lock changed
260562306a36Sopenharmony_ci	"VPRES",   // video presence changed
260662306a36Sopenharmony_ci	"6", "7",
260762306a36Sopenharmony_ci	"I2CDONE", // hw irc operation finished
260862306a36Sopenharmony_ci	"GPINT",   // gpio port triggered irq
260962306a36Sopenharmony_ci	"10",
261062306a36Sopenharmony_ci	"RISCI",   // risc instruction triggered irq
261162306a36Sopenharmony_ci	"FBUS",    // pixel data fifo dropped data (high pci bus latencies)
261262306a36Sopenharmony_ci	"FTRGT",   // pixel data fifo overrun
261362306a36Sopenharmony_ci	"FDSR",    // fifo data stream resyncronisation
261462306a36Sopenharmony_ci	"PPERR",   // parity error (data transfer)
261562306a36Sopenharmony_ci	"RIPERR",  // parity error (read risc instructions)
261662306a36Sopenharmony_ci	"PABORT",  // pci abort
261762306a36Sopenharmony_ci	"OCERR",   // risc instruction error
261862306a36Sopenharmony_ci	"SCERR",   // syncronisation error
261962306a36Sopenharmony_ci};
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_cistatic void bttv_print_irqbits(u32 print, u32 mark)
262262306a36Sopenharmony_ci{
262362306a36Sopenharmony_ci	unsigned int i;
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	pr_cont("bits:");
262662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(irq_name); i++) {
262762306a36Sopenharmony_ci		if (print & (1 << i))
262862306a36Sopenharmony_ci			pr_cont(" %s", irq_name[i]);
262962306a36Sopenharmony_ci		if (mark & (1 << i))
263062306a36Sopenharmony_ci			pr_cont("*");
263162306a36Sopenharmony_ci	}
263262306a36Sopenharmony_ci}
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_cistatic void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
263562306a36Sopenharmony_ci{
263662306a36Sopenharmony_ci	pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
263762306a36Sopenharmony_ci		btv->c.nr,
263862306a36Sopenharmony_ci		(unsigned long)btv->main.dma,
263962306a36Sopenharmony_ci		(unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
264062306a36Sopenharmony_ci		(unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
264162306a36Sopenharmony_ci		(unsigned long)rc);
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci	if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
264462306a36Sopenharmony_ci		pr_notice("%d: Oh, there (temporarily?) is no input signal. Ok, then this is harmless, don't worry ;)\n",
264562306a36Sopenharmony_ci			  btv->c.nr);
264662306a36Sopenharmony_ci		return;
264762306a36Sopenharmony_ci	}
264862306a36Sopenharmony_ci	pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n",
264962306a36Sopenharmony_ci		  btv->c.nr);
265062306a36Sopenharmony_ci	pr_notice("%d: Lets try to catch the culprit red-handed ...\n",
265162306a36Sopenharmony_ci		  btv->c.nr);
265262306a36Sopenharmony_ci	dump_stack();
265362306a36Sopenharmony_ci}
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_cistatic int
265662306a36Sopenharmony_cibttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
265762306a36Sopenharmony_ci{
265862306a36Sopenharmony_ci	struct bttv_buffer *item;
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_ci	memset(set,0,sizeof(*set));
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci	/* capture request ? */
266362306a36Sopenharmony_ci	if (!list_empty(&btv->capture)) {
266462306a36Sopenharmony_ci		set->frame_irq = BT848_RISC_VIDEO;
266562306a36Sopenharmony_ci		item = list_entry(btv->capture.next, struct bttv_buffer, list);
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci		if (V4L2_FIELD_HAS_TOP(item->vbuf.field))
266862306a36Sopenharmony_ci			set->top    = item;
266962306a36Sopenharmony_ci		if (V4L2_FIELD_HAS_BOTTOM(item->vbuf.field))
267062306a36Sopenharmony_ci			set->bottom = item;
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci		/* capture request for other field ? */
267362306a36Sopenharmony_ci		if (!V4L2_FIELD_HAS_BOTH(item->vbuf.field) &&
267462306a36Sopenharmony_ci		    item->list.next != &btv->capture) {
267562306a36Sopenharmony_ci			item = list_entry(item->list.next,
267662306a36Sopenharmony_ci					  struct bttv_buffer, list);
267762306a36Sopenharmony_ci			/* Mike Isely <isely@pobox.com> - Only check
267862306a36Sopenharmony_ci			 * and set up the bottom field in the logic
267962306a36Sopenharmony_ci			 * below.  Don't ever do the top field.  This
268062306a36Sopenharmony_ci			 * of course means that if we set up the
268162306a36Sopenharmony_ci			 * bottom field in the above code that we'll
268262306a36Sopenharmony_ci			 * actually skip a field.  But that's OK.
268362306a36Sopenharmony_ci			 * Having processed only a single buffer this
268462306a36Sopenharmony_ci			 * time, then the next time around the first
268562306a36Sopenharmony_ci			 * available buffer should be for a top field.
268662306a36Sopenharmony_ci			 * That will then cause us here to set up a
268762306a36Sopenharmony_ci			 * top then a bottom field in the normal way.
268862306a36Sopenharmony_ci			 * The alternative to this understanding is
268962306a36Sopenharmony_ci			 * that we set up the second available buffer
269062306a36Sopenharmony_ci			 * as a top field, but that's out of order
269162306a36Sopenharmony_ci			 * since this driver always processes the top
269262306a36Sopenharmony_ci			 * field first - the effect will be the two
269362306a36Sopenharmony_ci			 * buffers being returned in the wrong order,
269462306a36Sopenharmony_ci			 * with the second buffer also being delayed
269562306a36Sopenharmony_ci			 * by one field time (owing to the fifo nature
269662306a36Sopenharmony_ci			 * of videobuf).  Worse still, we'll be stuck
269762306a36Sopenharmony_ci			 * doing fields out of order now every time
269862306a36Sopenharmony_ci			 * until something else causes a field to be
269962306a36Sopenharmony_ci			 * dropped.  By effectively forcing a field to
270062306a36Sopenharmony_ci			 * drop this way then we always get back into
270162306a36Sopenharmony_ci			 * sync within a single frame time.  (Out of
270262306a36Sopenharmony_ci			 * order fields can screw up deinterlacing
270362306a36Sopenharmony_ci			 * algorithms.) */
270462306a36Sopenharmony_ci			if (!V4L2_FIELD_HAS_BOTH(item->vbuf.field)) {
270562306a36Sopenharmony_ci				if (!set->bottom &&
270662306a36Sopenharmony_ci				    item->vbuf.field == V4L2_FIELD_BOTTOM)
270762306a36Sopenharmony_ci					set->bottom = item;
270862306a36Sopenharmony_ci				if (set->top && set->bottom) {
270962306a36Sopenharmony_ci					/*
271062306a36Sopenharmony_ci					 * The buffer set has a top buffer and
271162306a36Sopenharmony_ci					 * a bottom buffer and they are not
271262306a36Sopenharmony_ci					 * copies of each other.
271362306a36Sopenharmony_ci					 */
271462306a36Sopenharmony_ci					set->top_irq = BT848_RISC_TOP;
271562306a36Sopenharmony_ci				}
271662306a36Sopenharmony_ci			}
271762306a36Sopenharmony_ci		}
271862306a36Sopenharmony_ci	}
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci	dprintk("%d: next set: top=%p bottom=%p [irq=%d,%d]\n",
272162306a36Sopenharmony_ci		btv->c.nr, set->top, set->bottom,
272262306a36Sopenharmony_ci		set->frame_irq, set->top_irq);
272362306a36Sopenharmony_ci	return 0;
272462306a36Sopenharmony_ci}
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_cistatic void
272762306a36Sopenharmony_cibttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
272862306a36Sopenharmony_ci		      struct bttv_buffer_set *curr, unsigned int state)
272962306a36Sopenharmony_ci{
273062306a36Sopenharmony_ci	u64 ts = ktime_get_ns();
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci	if (wakeup->top == wakeup->bottom) {
273362306a36Sopenharmony_ci		if (NULL != wakeup->top && curr->top != wakeup->top) {
273462306a36Sopenharmony_ci			if (irq_debug > 1)
273562306a36Sopenharmony_ci				pr_debug("%d: wakeup: both=%p\n",
273662306a36Sopenharmony_ci					 btv->c.nr, wakeup->top);
273762306a36Sopenharmony_ci			wakeup->top->vbuf.vb2_buf.timestamp = ts;
273862306a36Sopenharmony_ci			wakeup->top->vbuf.sequence = btv->field_count >> 1;
273962306a36Sopenharmony_ci			vb2_buffer_done(&wakeup->top->vbuf.vb2_buf, state);
274062306a36Sopenharmony_ci			if (btv->field_count == 0)
274162306a36Sopenharmony_ci				btor(BT848_INT_VSYNC, BT848_INT_MASK);
274262306a36Sopenharmony_ci		}
274362306a36Sopenharmony_ci	} else {
274462306a36Sopenharmony_ci		if (NULL != wakeup->top && curr->top != wakeup->top) {
274562306a36Sopenharmony_ci			if (irq_debug > 1)
274662306a36Sopenharmony_ci				pr_debug("%d: wakeup: top=%p\n",
274762306a36Sopenharmony_ci					 btv->c.nr, wakeup->top);
274862306a36Sopenharmony_ci			wakeup->top->vbuf.vb2_buf.timestamp = ts;
274962306a36Sopenharmony_ci			wakeup->top->vbuf.sequence = btv->field_count >> 1;
275062306a36Sopenharmony_ci			vb2_buffer_done(&wakeup->top->vbuf.vb2_buf, state);
275162306a36Sopenharmony_ci			if (btv->field_count == 0)
275262306a36Sopenharmony_ci				btor(BT848_INT_VSYNC, BT848_INT_MASK);
275362306a36Sopenharmony_ci		}
275462306a36Sopenharmony_ci		if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
275562306a36Sopenharmony_ci			if (irq_debug > 1)
275662306a36Sopenharmony_ci				pr_debug("%d: wakeup: bottom=%p\n",
275762306a36Sopenharmony_ci					 btv->c.nr, wakeup->bottom);
275862306a36Sopenharmony_ci			wakeup->bottom->vbuf.vb2_buf.timestamp = ts;
275962306a36Sopenharmony_ci			wakeup->bottom->vbuf.sequence = btv->field_count >> 1;
276062306a36Sopenharmony_ci			vb2_buffer_done(&wakeup->bottom->vbuf.vb2_buf, state);
276162306a36Sopenharmony_ci			if (btv->field_count == 0)
276262306a36Sopenharmony_ci				btor(BT848_INT_VSYNC, BT848_INT_MASK);
276362306a36Sopenharmony_ci		}
276462306a36Sopenharmony_ci	}
276562306a36Sopenharmony_ci}
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_cistatic void
276862306a36Sopenharmony_cibttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup,
276962306a36Sopenharmony_ci				unsigned int state)
277062306a36Sopenharmony_ci{
277162306a36Sopenharmony_ci	if (NULL == wakeup)
277262306a36Sopenharmony_ci		return;
277362306a36Sopenharmony_ci	wakeup->vbuf.vb2_buf.timestamp = ktime_get_ns();
277462306a36Sopenharmony_ci	wakeup->vbuf.sequence = btv->field_count >> 1;
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci	/*
277762306a36Sopenharmony_ci	 * Ugly hack for backwards compatibility.
277862306a36Sopenharmony_ci	 * Some applications expect that the last 4 bytes of
277962306a36Sopenharmony_ci	 * the VBI data contains the sequence number.
278062306a36Sopenharmony_ci	 *
278162306a36Sopenharmony_ci	 * This makes it possible to associate the VBI data
278262306a36Sopenharmony_ci	 * with the video frame if you use read() to get the
278362306a36Sopenharmony_ci	 * VBI data.
278462306a36Sopenharmony_ci	 */
278562306a36Sopenharmony_ci	if (vb2_fileio_is_active(wakeup->vbuf.vb2_buf.vb2_queue)) {
278662306a36Sopenharmony_ci		u32 *vaddr = vb2_plane_vaddr(&wakeup->vbuf.vb2_buf, 0);
278762306a36Sopenharmony_ci		unsigned long size =
278862306a36Sopenharmony_ci			vb2_get_plane_payload(&wakeup->vbuf.vb2_buf, 0) / 4;
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci		if (vaddr && size) {
279162306a36Sopenharmony_ci			vaddr += size - 1;
279262306a36Sopenharmony_ci			*vaddr = wakeup->vbuf.sequence;
279362306a36Sopenharmony_ci		}
279462306a36Sopenharmony_ci	}
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci	vb2_buffer_done(&wakeup->vbuf.vb2_buf, state);
279762306a36Sopenharmony_ci	if (btv->field_count == 0)
279862306a36Sopenharmony_ci		btor(BT848_INT_VSYNC, BT848_INT_MASK);
279962306a36Sopenharmony_ci}
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_cistatic void bttv_irq_timeout(struct timer_list *t)
280262306a36Sopenharmony_ci{
280362306a36Sopenharmony_ci	struct bttv *btv = from_timer(btv, t, timeout);
280462306a36Sopenharmony_ci	struct bttv_buffer_set old,new;
280562306a36Sopenharmony_ci	struct bttv_buffer *ovbi;
280662306a36Sopenharmony_ci	struct bttv_buffer *item;
280762306a36Sopenharmony_ci	unsigned long flags;
280862306a36Sopenharmony_ci	int seqnr = 0;
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	if (bttv_verbose) {
281162306a36Sopenharmony_ci		pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
281262306a36Sopenharmony_ci			btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
281362306a36Sopenharmony_ci			btread(BT848_RISC_COUNT));
281462306a36Sopenharmony_ci		bttv_print_irqbits(btread(BT848_INT_STAT),0);
281562306a36Sopenharmony_ci		pr_cont("\n");
281662306a36Sopenharmony_ci	}
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci	spin_lock_irqsave(&btv->s_lock,flags);
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	/* deactivate stuff */
282162306a36Sopenharmony_ci	memset(&new,0,sizeof(new));
282262306a36Sopenharmony_ci	old  = btv->curr;
282362306a36Sopenharmony_ci	ovbi = btv->cvbi;
282462306a36Sopenharmony_ci	btv->curr = new;
282562306a36Sopenharmony_ci	btv->cvbi = NULL;
282662306a36Sopenharmony_ci	btv->loop_irq = 0;
282762306a36Sopenharmony_ci	bttv_buffer_activate_video(btv, &new);
282862306a36Sopenharmony_ci	bttv_buffer_activate_vbi(btv,   NULL);
282962306a36Sopenharmony_ci	bttv_set_dma(btv, 0);
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_ci	/* wake up */
283262306a36Sopenharmony_ci	bttv_irq_wakeup_video(btv, &old, &new, VB2_BUF_STATE_DONE);
283362306a36Sopenharmony_ci	bttv_irq_wakeup_vbi(btv, ovbi, VB2_BUF_STATE_DONE);
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci	/* cancel all outstanding capture / vbi requests */
283662306a36Sopenharmony_ci	if (btv->field_count)
283762306a36Sopenharmony_ci		seqnr++;
283862306a36Sopenharmony_ci	while (!list_empty(&btv->capture)) {
283962306a36Sopenharmony_ci		item = list_entry(btv->capture.next, struct bttv_buffer, list);
284062306a36Sopenharmony_ci		list_del(&item->list);
284162306a36Sopenharmony_ci		item->vbuf.vb2_buf.timestamp = ktime_get_ns();
284262306a36Sopenharmony_ci		item->vbuf.sequence = (btv->field_count >> 1) + seqnr++;
284362306a36Sopenharmony_ci		vb2_buffer_done(&item->vbuf.vb2_buf, VB2_BUF_STATE_ERROR);
284462306a36Sopenharmony_ci	}
284562306a36Sopenharmony_ci	while (!list_empty(&btv->vcapture)) {
284662306a36Sopenharmony_ci		item = list_entry(btv->vcapture.next, struct bttv_buffer, list);
284762306a36Sopenharmony_ci		list_del(&item->list);
284862306a36Sopenharmony_ci		item->vbuf.vb2_buf.timestamp = ktime_get_ns();
284962306a36Sopenharmony_ci		item->vbuf.sequence = (btv->field_count >> 1) + seqnr++;
285062306a36Sopenharmony_ci		vb2_buffer_done(&item->vbuf.vb2_buf, VB2_BUF_STATE_ERROR);
285162306a36Sopenharmony_ci	}
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci	btv->errors++;
285462306a36Sopenharmony_ci	spin_unlock_irqrestore(&btv->s_lock,flags);
285562306a36Sopenharmony_ci}
285662306a36Sopenharmony_ci
285762306a36Sopenharmony_cistatic void
285862306a36Sopenharmony_cibttv_irq_wakeup_top(struct bttv *btv)
285962306a36Sopenharmony_ci{
286062306a36Sopenharmony_ci	struct bttv_buffer *wakeup = btv->curr.top;
286162306a36Sopenharmony_ci
286262306a36Sopenharmony_ci	if (NULL == wakeup)
286362306a36Sopenharmony_ci		return;
286462306a36Sopenharmony_ci
286562306a36Sopenharmony_ci	spin_lock(&btv->s_lock);
286662306a36Sopenharmony_ci	btv->curr.top_irq = 0;
286762306a36Sopenharmony_ci	btv->curr.top = NULL;
286862306a36Sopenharmony_ci	bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
286962306a36Sopenharmony_ci	wakeup->vbuf.vb2_buf.timestamp = ktime_get_ns();
287062306a36Sopenharmony_ci	wakeup->vbuf.sequence = btv->field_count >> 1;
287162306a36Sopenharmony_ci	vb2_buffer_done(&wakeup->vbuf.vb2_buf, VB2_BUF_STATE_DONE);
287262306a36Sopenharmony_ci	if (btv->field_count == 0)
287362306a36Sopenharmony_ci		btor(BT848_INT_VSYNC, BT848_INT_MASK);
287462306a36Sopenharmony_ci	spin_unlock(&btv->s_lock);
287562306a36Sopenharmony_ci}
287662306a36Sopenharmony_ci
287762306a36Sopenharmony_cistatic inline int is_active(struct btcx_riscmem *risc, u32 rc)
287862306a36Sopenharmony_ci{
287962306a36Sopenharmony_ci	if (rc < risc->dma)
288062306a36Sopenharmony_ci		return 0;
288162306a36Sopenharmony_ci	if (rc > risc->dma + risc->size)
288262306a36Sopenharmony_ci		return 0;
288362306a36Sopenharmony_ci	return 1;
288462306a36Sopenharmony_ci}
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_cistatic void
288762306a36Sopenharmony_cibttv_irq_switch_video(struct bttv *btv)
288862306a36Sopenharmony_ci{
288962306a36Sopenharmony_ci	struct bttv_buffer_set new;
289062306a36Sopenharmony_ci	struct bttv_buffer_set old;
289162306a36Sopenharmony_ci	dma_addr_t rc;
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	spin_lock(&btv->s_lock);
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_ci	/* new buffer set */
289662306a36Sopenharmony_ci	bttv_irq_next_video(btv, &new);
289762306a36Sopenharmony_ci	rc = btread(BT848_RISC_COUNT);
289862306a36Sopenharmony_ci	if ((btv->curr.top    && is_active(&btv->curr.top->top,       rc)) ||
289962306a36Sopenharmony_ci	    (btv->curr.bottom && is_active(&btv->curr.bottom->bottom, rc))) {
290062306a36Sopenharmony_ci		btv->framedrop++;
290162306a36Sopenharmony_ci		if (debug_latency)
290262306a36Sopenharmony_ci			bttv_irq_debug_low_latency(btv, rc);
290362306a36Sopenharmony_ci		spin_unlock(&btv->s_lock);
290462306a36Sopenharmony_ci		return;
290562306a36Sopenharmony_ci	}
290662306a36Sopenharmony_ci
290762306a36Sopenharmony_ci	/* switch over */
290862306a36Sopenharmony_ci	old = btv->curr;
290962306a36Sopenharmony_ci	btv->curr = new;
291062306a36Sopenharmony_ci	btv->loop_irq &= ~BT848_RISC_VIDEO;
291162306a36Sopenharmony_ci	bttv_buffer_activate_video(btv, &new);
291262306a36Sopenharmony_ci	bttv_set_dma(btv, 0);
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci	/* switch input */
291562306a36Sopenharmony_ci	if (UNSET != btv->new_input) {
291662306a36Sopenharmony_ci		video_mux(btv,btv->new_input);
291762306a36Sopenharmony_ci		btv->new_input = UNSET;
291862306a36Sopenharmony_ci	}
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	/* wake up finished buffers */
292162306a36Sopenharmony_ci	bttv_irq_wakeup_video(btv, &old, &new, VB2_BUF_STATE_DONE);
292262306a36Sopenharmony_ci	spin_unlock(&btv->s_lock);
292362306a36Sopenharmony_ci}
292462306a36Sopenharmony_ci
292562306a36Sopenharmony_cistatic void
292662306a36Sopenharmony_cibttv_irq_switch_vbi(struct bttv *btv)
292762306a36Sopenharmony_ci{
292862306a36Sopenharmony_ci	struct bttv_buffer *new = NULL;
292962306a36Sopenharmony_ci	struct bttv_buffer *old;
293062306a36Sopenharmony_ci	u32 rc;
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_ci	spin_lock(&btv->s_lock);
293362306a36Sopenharmony_ci
293462306a36Sopenharmony_ci	if (!list_empty(&btv->vcapture))
293562306a36Sopenharmony_ci		new = list_entry(btv->vcapture.next, struct bttv_buffer, list);
293662306a36Sopenharmony_ci	old = btv->cvbi;
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci	rc = btread(BT848_RISC_COUNT);
293962306a36Sopenharmony_ci	if (NULL != old && (is_active(&old->top,    rc) ||
294062306a36Sopenharmony_ci			    is_active(&old->bottom, rc))) {
294162306a36Sopenharmony_ci		btv->framedrop++;
294262306a36Sopenharmony_ci		if (debug_latency)
294362306a36Sopenharmony_ci			bttv_irq_debug_low_latency(btv, rc);
294462306a36Sopenharmony_ci		spin_unlock(&btv->s_lock);
294562306a36Sopenharmony_ci		return;
294662306a36Sopenharmony_ci	}
294762306a36Sopenharmony_ci
294862306a36Sopenharmony_ci	/* switch */
294962306a36Sopenharmony_ci	btv->cvbi = new;
295062306a36Sopenharmony_ci	btv->loop_irq &= ~BT848_RISC_VBI;
295162306a36Sopenharmony_ci	bttv_buffer_activate_vbi(btv, new);
295262306a36Sopenharmony_ci	bttv_set_dma(btv, 0);
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	bttv_irq_wakeup_vbi(btv, old, VB2_BUF_STATE_DONE);
295562306a36Sopenharmony_ci	spin_unlock(&btv->s_lock);
295662306a36Sopenharmony_ci}
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_cistatic irqreturn_t bttv_irq(int irq, void *dev_id)
295962306a36Sopenharmony_ci{
296062306a36Sopenharmony_ci	u32 stat,astat;
296162306a36Sopenharmony_ci	u32 dstat;
296262306a36Sopenharmony_ci	int count;
296362306a36Sopenharmony_ci	struct bttv *btv;
296462306a36Sopenharmony_ci	int handled = 0;
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_ci	btv=(struct bttv *)dev_id;
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	count=0;
296962306a36Sopenharmony_ci	while (1) {
297062306a36Sopenharmony_ci		/* get/clear interrupt status bits */
297162306a36Sopenharmony_ci		stat=btread(BT848_INT_STAT);
297262306a36Sopenharmony_ci		astat=stat&btread(BT848_INT_MASK);
297362306a36Sopenharmony_ci		if (!astat)
297462306a36Sopenharmony_ci			break;
297562306a36Sopenharmony_ci		handled = 1;
297662306a36Sopenharmony_ci		btwrite(stat,BT848_INT_STAT);
297762306a36Sopenharmony_ci
297862306a36Sopenharmony_ci		/* get device status bits */
297962306a36Sopenharmony_ci		dstat=btread(BT848_DSTATUS);
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci		if (irq_debug) {
298262306a36Sopenharmony_ci			pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ",
298362306a36Sopenharmony_ci				 btv->c.nr, count, btv->field_count,
298462306a36Sopenharmony_ci				 stat>>28, btread(BT848_RISC_COUNT));
298562306a36Sopenharmony_ci			bttv_print_irqbits(stat,astat);
298662306a36Sopenharmony_ci			if (stat & BT848_INT_HLOCK)
298762306a36Sopenharmony_ci				pr_cont("   HLOC => %s",
298862306a36Sopenharmony_ci					dstat & BT848_DSTATUS_HLOC
298962306a36Sopenharmony_ci					? "yes" : "no");
299062306a36Sopenharmony_ci			if (stat & BT848_INT_VPRES)
299162306a36Sopenharmony_ci				pr_cont("   PRES => %s",
299262306a36Sopenharmony_ci					dstat & BT848_DSTATUS_PRES
299362306a36Sopenharmony_ci					? "yes" : "no");
299462306a36Sopenharmony_ci			if (stat & BT848_INT_FMTCHG)
299562306a36Sopenharmony_ci				pr_cont("   NUML => %s",
299662306a36Sopenharmony_ci					dstat & BT848_DSTATUS_NUML
299762306a36Sopenharmony_ci					? "625" : "525");
299862306a36Sopenharmony_ci			pr_cont("\n");
299962306a36Sopenharmony_ci		}
300062306a36Sopenharmony_ci
300162306a36Sopenharmony_ci		if (astat&BT848_INT_VSYNC)
300262306a36Sopenharmony_ci			btv->field_count++;
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci		if ((astat & BT848_INT_GPINT) && btv->remote) {
300562306a36Sopenharmony_ci			bttv_input_irq(btv);
300662306a36Sopenharmony_ci		}
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci		if (astat & BT848_INT_I2CDONE) {
300962306a36Sopenharmony_ci			btv->i2c_done = stat;
301062306a36Sopenharmony_ci			wake_up(&btv->i2c_queue);
301162306a36Sopenharmony_ci		}
301262306a36Sopenharmony_ci
301362306a36Sopenharmony_ci		if ((astat & BT848_INT_RISCI) && (stat & BT848_INT_RISCS_VBI))
301462306a36Sopenharmony_ci			bttv_irq_switch_vbi(btv);
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_ci		if ((astat & BT848_INT_RISCI) && (stat & BT848_INT_RISCS_TOP))
301762306a36Sopenharmony_ci			bttv_irq_wakeup_top(btv);
301862306a36Sopenharmony_ci
301962306a36Sopenharmony_ci		if ((astat & BT848_INT_RISCI) && (stat & BT848_INT_RISCS_VIDEO))
302062306a36Sopenharmony_ci			bttv_irq_switch_video(btv);
302162306a36Sopenharmony_ci
302262306a36Sopenharmony_ci		if ((astat & BT848_INT_HLOCK)  &&  btv->opt_automute)
302362306a36Sopenharmony_ci			/* trigger automute */
302462306a36Sopenharmony_ci			audio_mux_gpio(btv, btv->audio_input, btv->mute);
302562306a36Sopenharmony_ci
302662306a36Sopenharmony_ci		if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
302762306a36Sopenharmony_ci			pr_info("%d: %s%s @ %08x,",
302862306a36Sopenharmony_ci				btv->c.nr,
302962306a36Sopenharmony_ci				(astat & BT848_INT_SCERR) ? "SCERR" : "",
303062306a36Sopenharmony_ci				(astat & BT848_INT_OCERR) ? "OCERR" : "",
303162306a36Sopenharmony_ci				btread(BT848_RISC_COUNT));
303262306a36Sopenharmony_ci			bttv_print_irqbits(stat,astat);
303362306a36Sopenharmony_ci			pr_cont("\n");
303462306a36Sopenharmony_ci			if (bttv_debug)
303562306a36Sopenharmony_ci				bttv_print_riscaddr(btv);
303662306a36Sopenharmony_ci		}
303762306a36Sopenharmony_ci		if (fdsr && astat & BT848_INT_FDSR) {
303862306a36Sopenharmony_ci			pr_info("%d: FDSR @ %08x\n",
303962306a36Sopenharmony_ci				btv->c.nr, btread(BT848_RISC_COUNT));
304062306a36Sopenharmony_ci			if (bttv_debug)
304162306a36Sopenharmony_ci				bttv_print_riscaddr(btv);
304262306a36Sopenharmony_ci		}
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ci		count++;
304562306a36Sopenharmony_ci		if (count > 4) {
304662306a36Sopenharmony_ci
304762306a36Sopenharmony_ci			if (count > 8 || !(astat & BT848_INT_GPINT)) {
304862306a36Sopenharmony_ci				btwrite(0, BT848_INT_MASK);
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci				pr_err("%d: IRQ lockup, cleared int mask [",
305162306a36Sopenharmony_ci				       btv->c.nr);
305262306a36Sopenharmony_ci			} else {
305362306a36Sopenharmony_ci				pr_err("%d: IRQ lockup, clearing GPINT from int mask [",
305462306a36Sopenharmony_ci				       btv->c.nr);
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_ci				btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT),
305762306a36Sopenharmony_ci						BT848_INT_MASK);
305862306a36Sopenharmony_ci			}
305962306a36Sopenharmony_ci
306062306a36Sopenharmony_ci			bttv_print_irqbits(stat,astat);
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci			pr_cont("]\n");
306362306a36Sopenharmony_ci		}
306462306a36Sopenharmony_ci	}
306562306a36Sopenharmony_ci	btv->irq_total++;
306662306a36Sopenharmony_ci	if (handled)
306762306a36Sopenharmony_ci		btv->irq_me++;
306862306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
306962306a36Sopenharmony_ci}
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci
307262306a36Sopenharmony_ci/* ----------------------------------------------------------------------- */
307362306a36Sopenharmony_ci/* initialization                                                          */
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_cistatic int vdev_init(struct bttv *btv, struct video_device *vfd,
307662306a36Sopenharmony_ci		     const struct video_device *template,
307762306a36Sopenharmony_ci		     const char *type_name)
307862306a36Sopenharmony_ci{
307962306a36Sopenharmony_ci	int err;
308062306a36Sopenharmony_ci	struct vb2_queue *q;
308162306a36Sopenharmony_ci	*vfd = *template;
308262306a36Sopenharmony_ci	vfd->v4l2_dev = &btv->c.v4l2_dev;
308362306a36Sopenharmony_ci	vfd->release = video_device_release_empty;
308462306a36Sopenharmony_ci	video_set_drvdata(vfd, btv);
308562306a36Sopenharmony_ci	snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
308662306a36Sopenharmony_ci		 btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
308762306a36Sopenharmony_ci		 type_name, bttv_tvcards[btv->c.type].name);
308862306a36Sopenharmony_ci	if (btv->tuner_type == TUNER_ABSENT) {
308962306a36Sopenharmony_ci		v4l2_disable_ioctl(vfd, VIDIOC_G_FREQUENCY);
309062306a36Sopenharmony_ci		v4l2_disable_ioctl(vfd, VIDIOC_S_FREQUENCY);
309162306a36Sopenharmony_ci		v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER);
309262306a36Sopenharmony_ci		v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER);
309362306a36Sopenharmony_ci	}
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	if (strcmp(type_name, "radio") == 0)
309662306a36Sopenharmony_ci		return 0;
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci	if (strcmp(type_name, "video") == 0) {
309962306a36Sopenharmony_ci		q = &btv->capq;
310062306a36Sopenharmony_ci		q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
310162306a36Sopenharmony_ci		q->ops = &bttv_video_qops;
310262306a36Sopenharmony_ci	} else if (strcmp(type_name, "vbi") == 0) {
310362306a36Sopenharmony_ci		q = &btv->vbiq;
310462306a36Sopenharmony_ci		q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
310562306a36Sopenharmony_ci		q->ops = &bttv_vbi_qops;
310662306a36Sopenharmony_ci	} else {
310762306a36Sopenharmony_ci		return -EINVAL;
310862306a36Sopenharmony_ci	}
310962306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
311062306a36Sopenharmony_ci	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | VB2_DMABUF;
311162306a36Sopenharmony_ci	q->mem_ops = &vb2_dma_sg_memops;
311262306a36Sopenharmony_ci	q->drv_priv = btv;
311362306a36Sopenharmony_ci	q->gfp_flags = __GFP_DMA32;
311462306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct bttv_buffer);
311562306a36Sopenharmony_ci	q->lock = &btv->lock;
311662306a36Sopenharmony_ci	q->min_buffers_needed = 2;
311762306a36Sopenharmony_ci	q->dev = &btv->c.pci->dev;
311862306a36Sopenharmony_ci	err = vb2_queue_init(q);
311962306a36Sopenharmony_ci	if (err)
312062306a36Sopenharmony_ci		return err;
312162306a36Sopenharmony_ci	vfd->queue = q;
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_ci	return 0;
312462306a36Sopenharmony_ci}
312562306a36Sopenharmony_ci
312662306a36Sopenharmony_cistatic void bttv_unregister_video(struct bttv *btv)
312762306a36Sopenharmony_ci{
312862306a36Sopenharmony_ci	video_unregister_device(&btv->video_dev);
312962306a36Sopenharmony_ci	video_unregister_device(&btv->vbi_dev);
313062306a36Sopenharmony_ci	video_unregister_device(&btv->radio_dev);
313162306a36Sopenharmony_ci}
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci/* register video4linux devices */
313462306a36Sopenharmony_cistatic int bttv_register_video(struct bttv *btv)
313562306a36Sopenharmony_ci{
313662306a36Sopenharmony_ci	/* video */
313762306a36Sopenharmony_ci	vdev_init(btv, &btv->video_dev, &bttv_video_template, "video");
313862306a36Sopenharmony_ci	btv->video_dev.device_caps = V4L2_CAP_VIDEO_CAPTURE |
313962306a36Sopenharmony_ci				     V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
314062306a36Sopenharmony_ci	if (btv->tuner_type != TUNER_ABSENT)
314162306a36Sopenharmony_ci		btv->video_dev.device_caps |= V4L2_CAP_TUNER;
314262306a36Sopenharmony_ci
314362306a36Sopenharmony_ci	if (video_register_device(&btv->video_dev, VFL_TYPE_VIDEO,
314462306a36Sopenharmony_ci				  video_nr[btv->c.nr]) < 0)
314562306a36Sopenharmony_ci		goto err;
314662306a36Sopenharmony_ci	pr_info("%d: registered device %s\n",
314762306a36Sopenharmony_ci		btv->c.nr, video_device_node_name(&btv->video_dev));
314862306a36Sopenharmony_ci	if (device_create_file(&btv->video_dev.dev,
314962306a36Sopenharmony_ci				     &dev_attr_card)<0) {
315062306a36Sopenharmony_ci		pr_err("%d: device_create_file 'card' failed\n", btv->c.nr);
315162306a36Sopenharmony_ci		goto err;
315262306a36Sopenharmony_ci	}
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci	/* vbi */
315562306a36Sopenharmony_ci	vdev_init(btv, &btv->vbi_dev, &bttv_video_template, "vbi");
315662306a36Sopenharmony_ci	btv->vbi_dev.device_caps = V4L2_CAP_VBI_CAPTURE | V4L2_CAP_READWRITE |
315762306a36Sopenharmony_ci				   V4L2_CAP_STREAMING;
315862306a36Sopenharmony_ci	if (btv->tuner_type != TUNER_ABSENT)
315962306a36Sopenharmony_ci		btv->vbi_dev.device_caps |= V4L2_CAP_TUNER;
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci	if (video_register_device(&btv->vbi_dev, VFL_TYPE_VBI,
316262306a36Sopenharmony_ci				  vbi_nr[btv->c.nr]) < 0)
316362306a36Sopenharmony_ci		goto err;
316462306a36Sopenharmony_ci	pr_info("%d: registered device %s\n",
316562306a36Sopenharmony_ci		btv->c.nr, video_device_node_name(&btv->vbi_dev));
316662306a36Sopenharmony_ci
316762306a36Sopenharmony_ci	if (!btv->has_radio)
316862306a36Sopenharmony_ci		return 0;
316962306a36Sopenharmony_ci	/* radio */
317062306a36Sopenharmony_ci	vdev_init(btv, &btv->radio_dev, &radio_template, "radio");
317162306a36Sopenharmony_ci	btv->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
317262306a36Sopenharmony_ci	if (btv->has_saa6588)
317362306a36Sopenharmony_ci		btv->radio_dev.device_caps |= V4L2_CAP_READWRITE |
317462306a36Sopenharmony_ci					      V4L2_CAP_RDS_CAPTURE;
317562306a36Sopenharmony_ci	if (btv->has_tea575x)
317662306a36Sopenharmony_ci		btv->radio_dev.device_caps |= V4L2_CAP_HW_FREQ_SEEK;
317762306a36Sopenharmony_ci	btv->radio_dev.ctrl_handler = &btv->radio_ctrl_handler;
317862306a36Sopenharmony_ci	if (video_register_device(&btv->radio_dev, VFL_TYPE_RADIO,
317962306a36Sopenharmony_ci				  radio_nr[btv->c.nr]) < 0)
318062306a36Sopenharmony_ci		goto err;
318162306a36Sopenharmony_ci	pr_info("%d: registered device %s\n",
318262306a36Sopenharmony_ci		btv->c.nr, video_device_node_name(&btv->radio_dev));
318362306a36Sopenharmony_ci
318462306a36Sopenharmony_ci	/* all done */
318562306a36Sopenharmony_ci	return 0;
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci err:
318862306a36Sopenharmony_ci	bttv_unregister_video(btv);
318962306a36Sopenharmony_ci	return -1;
319062306a36Sopenharmony_ci}
319162306a36Sopenharmony_ci
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
319462306a36Sopenharmony_ci/* response on cards with no firmware is not enabled by OF */
319562306a36Sopenharmony_cistatic void pci_set_command(struct pci_dev *dev)
319662306a36Sopenharmony_ci{
319762306a36Sopenharmony_ci#if defined(__powerpc__)
319862306a36Sopenharmony_ci	unsigned int cmd;
319962306a36Sopenharmony_ci
320062306a36Sopenharmony_ci	pci_read_config_dword(dev, PCI_COMMAND, &cmd);
320162306a36Sopenharmony_ci	cmd = (cmd | PCI_COMMAND_MEMORY );
320262306a36Sopenharmony_ci	pci_write_config_dword(dev, PCI_COMMAND, cmd);
320362306a36Sopenharmony_ci#endif
320462306a36Sopenharmony_ci}
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_cistatic int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id)
320762306a36Sopenharmony_ci{
320862306a36Sopenharmony_ci	struct v4l2_frequency init_freq = {
320962306a36Sopenharmony_ci		.tuner = 0,
321062306a36Sopenharmony_ci		.type = V4L2_TUNER_ANALOG_TV,
321162306a36Sopenharmony_ci		.frequency = 980,
321262306a36Sopenharmony_ci	};
321362306a36Sopenharmony_ci	int result;
321462306a36Sopenharmony_ci	unsigned char lat;
321562306a36Sopenharmony_ci	struct bttv *btv;
321662306a36Sopenharmony_ci	struct v4l2_ctrl_handler *hdl;
321762306a36Sopenharmony_ci
321862306a36Sopenharmony_ci	if (bttv_num == BTTV_MAX)
321962306a36Sopenharmony_ci		return -ENOMEM;
322062306a36Sopenharmony_ci	pr_info("Bt8xx card found (%d)\n", bttv_num);
322162306a36Sopenharmony_ci	bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
322262306a36Sopenharmony_ci	if (btv == NULL) {
322362306a36Sopenharmony_ci		pr_err("out of memory\n");
322462306a36Sopenharmony_ci		return -ENOMEM;
322562306a36Sopenharmony_ci	}
322662306a36Sopenharmony_ci	btv->c.nr  = bttv_num;
322762306a36Sopenharmony_ci	snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name),
322862306a36Sopenharmony_ci			"bttv%d", btv->c.nr);
322962306a36Sopenharmony_ci
323062306a36Sopenharmony_ci	/* initialize structs / fill in defaults */
323162306a36Sopenharmony_ci	mutex_init(&btv->lock);
323262306a36Sopenharmony_ci	spin_lock_init(&btv->s_lock);
323362306a36Sopenharmony_ci	spin_lock_init(&btv->gpio_lock);
323462306a36Sopenharmony_ci	init_waitqueue_head(&btv->i2c_queue);
323562306a36Sopenharmony_ci	INIT_LIST_HEAD(&btv->c.subs);
323662306a36Sopenharmony_ci	INIT_LIST_HEAD(&btv->capture);
323762306a36Sopenharmony_ci	INIT_LIST_HEAD(&btv->vcapture);
323862306a36Sopenharmony_ci
323962306a36Sopenharmony_ci	timer_setup(&btv->timeout, bttv_irq_timeout, 0);
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_ci	btv->i2c_rc = -1;
324262306a36Sopenharmony_ci	btv->tuner_type  = UNSET;
324362306a36Sopenharmony_ci	btv->new_input   = UNSET;
324462306a36Sopenharmony_ci	btv->has_radio=radio[btv->c.nr];
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci	/* pci stuff (init, get irq/mmio, ... */
324762306a36Sopenharmony_ci	btv->c.pci = dev;
324862306a36Sopenharmony_ci	btv->id  = dev->device;
324962306a36Sopenharmony_ci	if (pci_enable_device(dev)) {
325062306a36Sopenharmony_ci		pr_warn("%d: Can't enable device\n", btv->c.nr);
325162306a36Sopenharmony_ci		result = -EIO;
325262306a36Sopenharmony_ci		goto free_mem;
325362306a36Sopenharmony_ci	}
325462306a36Sopenharmony_ci	if (dma_set_mask(&dev->dev, DMA_BIT_MASK(32))) {
325562306a36Sopenharmony_ci		pr_warn("%d: No suitable DMA available\n", btv->c.nr);
325662306a36Sopenharmony_ci		result = -EIO;
325762306a36Sopenharmony_ci		goto free_mem;
325862306a36Sopenharmony_ci	}
325962306a36Sopenharmony_ci	if (!request_mem_region(pci_resource_start(dev,0),
326062306a36Sopenharmony_ci				pci_resource_len(dev,0),
326162306a36Sopenharmony_ci				btv->c.v4l2_dev.name)) {
326262306a36Sopenharmony_ci		pr_warn("%d: can't request iomem (0x%llx)\n",
326362306a36Sopenharmony_ci			btv->c.nr,
326462306a36Sopenharmony_ci			(unsigned long long)pci_resource_start(dev, 0));
326562306a36Sopenharmony_ci		result = -EBUSY;
326662306a36Sopenharmony_ci		goto free_mem;
326762306a36Sopenharmony_ci	}
326862306a36Sopenharmony_ci	pci_set_master(dev);
326962306a36Sopenharmony_ci	pci_set_command(dev);
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci	result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
327262306a36Sopenharmony_ci	if (result < 0) {
327362306a36Sopenharmony_ci		pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr);
327462306a36Sopenharmony_ci		goto fail0;
327562306a36Sopenharmony_ci	}
327662306a36Sopenharmony_ci	hdl = &btv->ctrl_handler;
327762306a36Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 20);
327862306a36Sopenharmony_ci	btv->c.v4l2_dev.ctrl_handler = hdl;
327962306a36Sopenharmony_ci	v4l2_ctrl_handler_init(&btv->radio_ctrl_handler, 6);
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ci	btv->revision = dev->revision;
328262306a36Sopenharmony_ci	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
328362306a36Sopenharmony_ci	pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n",
328462306a36Sopenharmony_ci		bttv_num, btv->id, btv->revision, pci_name(dev),
328562306a36Sopenharmony_ci		btv->c.pci->irq, lat,
328662306a36Sopenharmony_ci		(unsigned long long)pci_resource_start(dev, 0));
328762306a36Sopenharmony_ci	schedule();
328862306a36Sopenharmony_ci
328962306a36Sopenharmony_ci	btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
329062306a36Sopenharmony_ci	if (NULL == btv->bt848_mmio) {
329162306a36Sopenharmony_ci		pr_err("%d: ioremap() failed\n", btv->c.nr);
329262306a36Sopenharmony_ci		result = -EIO;
329362306a36Sopenharmony_ci		goto fail1;
329462306a36Sopenharmony_ci	}
329562306a36Sopenharmony_ci
329662306a36Sopenharmony_ci	/* identify card */
329762306a36Sopenharmony_ci	bttv_idcard(btv);
329862306a36Sopenharmony_ci
329962306a36Sopenharmony_ci	/* disable irqs, register irq handler */
330062306a36Sopenharmony_ci	btwrite(0, BT848_INT_MASK);
330162306a36Sopenharmony_ci	result = request_irq(btv->c.pci->irq, bttv_irq,
330262306a36Sopenharmony_ci	    IRQF_SHARED, btv->c.v4l2_dev.name, (void *)btv);
330362306a36Sopenharmony_ci	if (result < 0) {
330462306a36Sopenharmony_ci		pr_err("%d: can't get IRQ %d\n",
330562306a36Sopenharmony_ci		       bttv_num, btv->c.pci->irq);
330662306a36Sopenharmony_ci		goto fail1;
330762306a36Sopenharmony_ci	}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci	if (0 != bttv_handle_chipset(btv)) {
331062306a36Sopenharmony_ci		result = -EIO;
331162306a36Sopenharmony_ci		goto fail2;
331262306a36Sopenharmony_ci	}
331362306a36Sopenharmony_ci
331462306a36Sopenharmony_ci	/* init options from insmod args */
331562306a36Sopenharmony_ci	btv->opt_combfilter = combfilter;
331662306a36Sopenharmony_ci	bttv_ctrl_combfilter.def = combfilter;
331762306a36Sopenharmony_ci	bttv_ctrl_lumafilter.def = lumafilter;
331862306a36Sopenharmony_ci	btv->opt_automute   = automute;
331962306a36Sopenharmony_ci	bttv_ctrl_automute.def = automute;
332062306a36Sopenharmony_ci	bttv_ctrl_agc_crush.def = agc_crush;
332162306a36Sopenharmony_ci	btv->opt_vcr_hack   = vcr_hack;
332262306a36Sopenharmony_ci	bttv_ctrl_vcr_hack.def = vcr_hack;
332362306a36Sopenharmony_ci	bttv_ctrl_whitecrush_upper.def = whitecrush_upper;
332462306a36Sopenharmony_ci	bttv_ctrl_whitecrush_lower.def = whitecrush_lower;
332562306a36Sopenharmony_ci	btv->opt_uv_ratio   = uv_ratio;
332662306a36Sopenharmony_ci	bttv_ctrl_uv_ratio.def = uv_ratio;
332762306a36Sopenharmony_ci	bttv_ctrl_full_luma.def = full_luma_range;
332862306a36Sopenharmony_ci	bttv_ctrl_coring.def = coring;
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci	/* fill struct bttv with some useful defaults */
333162306a36Sopenharmony_ci	btv->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
333262306a36Sopenharmony_ci	btv->width = 320;
333362306a36Sopenharmony_ci	btv->height = 240;
333462306a36Sopenharmony_ci	btv->field = V4L2_FIELD_INTERLACED;
333562306a36Sopenharmony_ci	btv->input = 0;
333662306a36Sopenharmony_ci	btv->tvnorm = 0; /* Index into bttv_tvnorms[] i.e. PAL. */
333762306a36Sopenharmony_ci	bttv_vbi_fmt_reset(&btv->vbi_fmt, btv->tvnorm);
333862306a36Sopenharmony_ci	btv->vbi_count[0] = VBI_DEFLINES;
333962306a36Sopenharmony_ci	btv->vbi_count[1] = VBI_DEFLINES;
334062306a36Sopenharmony_ci	btv->do_crop = 0;
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops,
334362306a36Sopenharmony_ci			V4L2_CID_BRIGHTNESS, 0, 0xff00, 0x100, 32768);
334462306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops,
334562306a36Sopenharmony_ci			V4L2_CID_CONTRAST, 0, 0xff80, 0x80, 0x6c00);
334662306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops,
334762306a36Sopenharmony_ci			V4L2_CID_SATURATION, 0, 0xff80, 0x80, 32768);
334862306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops,
334962306a36Sopenharmony_ci			V4L2_CID_COLOR_KILLER, 0, 1, 1, 0);
335062306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops,
335162306a36Sopenharmony_ci			V4L2_CID_HUE, 0, 0xff00, 0x100, 32768);
335262306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops,
335362306a36Sopenharmony_ci			V4L2_CID_CHROMA_AGC, 0, 1, 1, !!chroma_agc);
335462306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops,
335562306a36Sopenharmony_ci		V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
335662306a36Sopenharmony_ci	if (btv->volume_gpio)
335762306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops,
335862306a36Sopenharmony_ci			V4L2_CID_AUDIO_VOLUME, 0, 0xff00, 0x100, 0xff00);
335962306a36Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &bttv_ctrl_combfilter, NULL);
336062306a36Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &bttv_ctrl_automute, NULL);
336162306a36Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &bttv_ctrl_lumafilter, NULL);
336262306a36Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &bttv_ctrl_agc_crush, NULL);
336362306a36Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &bttv_ctrl_vcr_hack, NULL);
336462306a36Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &bttv_ctrl_whitecrush_lower, NULL);
336562306a36Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &bttv_ctrl_whitecrush_upper, NULL);
336662306a36Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &bttv_ctrl_uv_ratio, NULL);
336762306a36Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &bttv_ctrl_full_luma, NULL);
336862306a36Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &bttv_ctrl_coring, NULL);
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci	/* initialize hardware */
337162306a36Sopenharmony_ci	if (bttv_gpio)
337262306a36Sopenharmony_ci		bttv_gpio_tracking(btv,"pre-init");
337362306a36Sopenharmony_ci
337462306a36Sopenharmony_ci	bttv_risc_init_main(btv);
337562306a36Sopenharmony_ci	init_bt848(btv);
337662306a36Sopenharmony_ci
337762306a36Sopenharmony_ci	/* gpio */
337862306a36Sopenharmony_ci	btwrite(0x00, BT848_GPIO_REG_INP);
337962306a36Sopenharmony_ci	btwrite(0x00, BT848_GPIO_OUT_EN);
338062306a36Sopenharmony_ci	if (bttv_verbose)
338162306a36Sopenharmony_ci		bttv_gpio_tracking(btv,"init");
338262306a36Sopenharmony_ci
338362306a36Sopenharmony_ci	/* needs to be done before i2c is registered */
338462306a36Sopenharmony_ci	bttv_init_card1(btv);
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_ci	/* register i2c + gpio */
338762306a36Sopenharmony_ci	init_bttv_i2c(btv);
338862306a36Sopenharmony_ci
338962306a36Sopenharmony_ci	/* some card-specific stuff (needs working i2c) */
339062306a36Sopenharmony_ci	bttv_init_card2(btv);
339162306a36Sopenharmony_ci	bttv_init_tuner(btv);
339262306a36Sopenharmony_ci	if (btv->tuner_type != TUNER_ABSENT) {
339362306a36Sopenharmony_ci		bttv_set_frequency(btv, &init_freq);
339462306a36Sopenharmony_ci		btv->radio_freq = 90500 * 16; /* 90.5Mhz default */
339562306a36Sopenharmony_ci	}
339662306a36Sopenharmony_ci	btv->std = V4L2_STD_PAL;
339762306a36Sopenharmony_ci	init_irqreg(btv);
339862306a36Sopenharmony_ci	if (!bttv_tvcards[btv->c.type].no_video)
339962306a36Sopenharmony_ci		v4l2_ctrl_handler_setup(hdl);
340062306a36Sopenharmony_ci	if (hdl->error) {
340162306a36Sopenharmony_ci		result = hdl->error;
340262306a36Sopenharmony_ci		goto fail2;
340362306a36Sopenharmony_ci	}
340462306a36Sopenharmony_ci	/* mute device */
340562306a36Sopenharmony_ci	audio_mute(btv, 1);
340662306a36Sopenharmony_ci
340762306a36Sopenharmony_ci	/* register video4linux + input */
340862306a36Sopenharmony_ci	if (!bttv_tvcards[btv->c.type].no_video) {
340962306a36Sopenharmony_ci		v4l2_ctrl_add_handler(&btv->radio_ctrl_handler, hdl,
341062306a36Sopenharmony_ci				v4l2_ctrl_radio_filter, false);
341162306a36Sopenharmony_ci		if (btv->radio_ctrl_handler.error) {
341262306a36Sopenharmony_ci			result = btv->radio_ctrl_handler.error;
341362306a36Sopenharmony_ci			goto fail2;
341462306a36Sopenharmony_ci		}
341562306a36Sopenharmony_ci		set_input(btv, btv->input, btv->tvnorm);
341662306a36Sopenharmony_ci		bttv_crop_reset(&btv->crop[0], btv->tvnorm);
341762306a36Sopenharmony_ci		btv->crop[1] = btv->crop[0]; /* current = default */
341862306a36Sopenharmony_ci		disclaim_vbi_lines(btv);
341962306a36Sopenharmony_ci		disclaim_video_lines(btv);
342062306a36Sopenharmony_ci		bttv_register_video(btv);
342162306a36Sopenharmony_ci	}
342262306a36Sopenharmony_ci
342362306a36Sopenharmony_ci	/* add subdevices and autoload dvb-bt8xx if needed */
342462306a36Sopenharmony_ci	if (bttv_tvcards[btv->c.type].has_dvb) {
342562306a36Sopenharmony_ci		bttv_sub_add_device(&btv->c, "dvb");
342662306a36Sopenharmony_ci		request_modules(btv);
342762306a36Sopenharmony_ci	}
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ci	if (!disable_ir) {
343062306a36Sopenharmony_ci		init_bttv_i2c_ir(btv);
343162306a36Sopenharmony_ci		bttv_input_init(btv);
343262306a36Sopenharmony_ci	}
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_ci	/* everything is fine */
343562306a36Sopenharmony_ci	bttv_num++;
343662306a36Sopenharmony_ci	return 0;
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_cifail2:
343962306a36Sopenharmony_ci	free_irq(btv->c.pci->irq,btv);
344062306a36Sopenharmony_ci
344162306a36Sopenharmony_cifail1:
344262306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&btv->ctrl_handler);
344362306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&btv->radio_ctrl_handler);
344462306a36Sopenharmony_ci	v4l2_device_unregister(&btv->c.v4l2_dev);
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_cifail0:
344762306a36Sopenharmony_ci	if (btv->bt848_mmio)
344862306a36Sopenharmony_ci		iounmap(btv->bt848_mmio);
344962306a36Sopenharmony_ci	release_mem_region(pci_resource_start(btv->c.pci,0),
345062306a36Sopenharmony_ci			   pci_resource_len(btv->c.pci,0));
345162306a36Sopenharmony_ci	pci_disable_device(btv->c.pci);
345262306a36Sopenharmony_ci
345362306a36Sopenharmony_cifree_mem:
345462306a36Sopenharmony_ci	bttvs[btv->c.nr] = NULL;
345562306a36Sopenharmony_ci	kfree(btv);
345662306a36Sopenharmony_ci	return result;
345762306a36Sopenharmony_ci}
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_cistatic void bttv_remove(struct pci_dev *pci_dev)
346062306a36Sopenharmony_ci{
346162306a36Sopenharmony_ci	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
346262306a36Sopenharmony_ci	struct bttv *btv = to_bttv(v4l2_dev);
346362306a36Sopenharmony_ci
346462306a36Sopenharmony_ci	if (bttv_verbose)
346562306a36Sopenharmony_ci		pr_info("%d: unloading\n", btv->c.nr);
346662306a36Sopenharmony_ci
346762306a36Sopenharmony_ci	if (bttv_tvcards[btv->c.type].has_dvb)
346862306a36Sopenharmony_ci		flush_request_modules(btv);
346962306a36Sopenharmony_ci
347062306a36Sopenharmony_ci	/* shutdown everything (DMA+IRQs) */
347162306a36Sopenharmony_ci	btand(~15, BT848_GPIO_DMA_CTL);
347262306a36Sopenharmony_ci	btwrite(0, BT848_INT_MASK);
347362306a36Sopenharmony_ci	btwrite(~0x0, BT848_INT_STAT);
347462306a36Sopenharmony_ci	btwrite(0x0, BT848_GPIO_OUT_EN);
347562306a36Sopenharmony_ci	if (bttv_gpio)
347662306a36Sopenharmony_ci		bttv_gpio_tracking(btv,"cleanup");
347762306a36Sopenharmony_ci
347862306a36Sopenharmony_ci	/* tell gpio modules we are leaving ... */
347962306a36Sopenharmony_ci	btv->shutdown=1;
348062306a36Sopenharmony_ci	bttv_input_fini(btv);
348162306a36Sopenharmony_ci	bttv_sub_del_devices(&btv->c);
348262306a36Sopenharmony_ci
348362306a36Sopenharmony_ci	/* unregister i2c_bus + input */
348462306a36Sopenharmony_ci	fini_bttv_i2c(btv);
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_ci	/* unregister video4linux */
348762306a36Sopenharmony_ci	bttv_unregister_video(btv);
348862306a36Sopenharmony_ci
348962306a36Sopenharmony_ci	/* free allocated memory */
349062306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&btv->ctrl_handler);
349162306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&btv->radio_ctrl_handler);
349262306a36Sopenharmony_ci	btcx_riscmem_free(btv->c.pci,&btv->main);
349362306a36Sopenharmony_ci
349462306a36Sopenharmony_ci	/* free resources */
349562306a36Sopenharmony_ci	free_irq(btv->c.pci->irq,btv);
349662306a36Sopenharmony_ci	del_timer_sync(&btv->timeout);
349762306a36Sopenharmony_ci	iounmap(btv->bt848_mmio);
349862306a36Sopenharmony_ci	release_mem_region(pci_resource_start(btv->c.pci,0),
349962306a36Sopenharmony_ci			   pci_resource_len(btv->c.pci,0));
350062306a36Sopenharmony_ci	pci_disable_device(btv->c.pci);
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_ci	v4l2_device_unregister(&btv->c.v4l2_dev);
350362306a36Sopenharmony_ci	bttvs[btv->c.nr] = NULL;
350462306a36Sopenharmony_ci	kfree(btv);
350562306a36Sopenharmony_ci
350662306a36Sopenharmony_ci	return;
350762306a36Sopenharmony_ci}
350862306a36Sopenharmony_ci
350962306a36Sopenharmony_cistatic int __maybe_unused bttv_suspend(struct device *dev)
351062306a36Sopenharmony_ci{
351162306a36Sopenharmony_ci	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
351262306a36Sopenharmony_ci	struct bttv *btv = to_bttv(v4l2_dev);
351362306a36Sopenharmony_ci	struct bttv_buffer_set idle;
351462306a36Sopenharmony_ci	unsigned long flags;
351562306a36Sopenharmony_ci
351662306a36Sopenharmony_ci	dprintk("%d: suspend\n", btv->c.nr);
351762306a36Sopenharmony_ci
351862306a36Sopenharmony_ci	/* stop dma + irqs */
351962306a36Sopenharmony_ci	spin_lock_irqsave(&btv->s_lock,flags);
352062306a36Sopenharmony_ci	memset(&idle, 0, sizeof(idle));
352162306a36Sopenharmony_ci	btv->state.video = btv->curr;
352262306a36Sopenharmony_ci	btv->state.vbi   = btv->cvbi;
352362306a36Sopenharmony_ci	btv->state.loop_irq = btv->loop_irq;
352462306a36Sopenharmony_ci	btv->curr = idle;
352562306a36Sopenharmony_ci	btv->loop_irq = 0;
352662306a36Sopenharmony_ci	bttv_buffer_activate_video(btv, &idle);
352762306a36Sopenharmony_ci	bttv_buffer_activate_vbi(btv, NULL);
352862306a36Sopenharmony_ci	bttv_set_dma(btv, 0);
352962306a36Sopenharmony_ci	btwrite(0, BT848_INT_MASK);
353062306a36Sopenharmony_ci	spin_unlock_irqrestore(&btv->s_lock,flags);
353162306a36Sopenharmony_ci
353262306a36Sopenharmony_ci	/* save bt878 state */
353362306a36Sopenharmony_ci	btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN);
353462306a36Sopenharmony_ci	btv->state.gpio_data   = gpio_read();
353562306a36Sopenharmony_ci
353662306a36Sopenharmony_ci	btv->state.disabled = 1;
353762306a36Sopenharmony_ci	return 0;
353862306a36Sopenharmony_ci}
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_cistatic int __maybe_unused bttv_resume(struct device *dev)
354162306a36Sopenharmony_ci{
354262306a36Sopenharmony_ci	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
354362306a36Sopenharmony_ci	struct bttv *btv = to_bttv(v4l2_dev);
354462306a36Sopenharmony_ci	unsigned long flags;
354562306a36Sopenharmony_ci
354662306a36Sopenharmony_ci	dprintk("%d: resume\n", btv->c.nr);
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci	btv->state.disabled = 0;
354962306a36Sopenharmony_ci
355062306a36Sopenharmony_ci	/* restore bt878 state */
355162306a36Sopenharmony_ci	bttv_reinit_bt848(btv);
355262306a36Sopenharmony_ci	gpio_inout(0xffffff, btv->state.gpio_enable);
355362306a36Sopenharmony_ci	gpio_write(btv->state.gpio_data);
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ci	/* restart dma */
355662306a36Sopenharmony_ci	spin_lock_irqsave(&btv->s_lock,flags);
355762306a36Sopenharmony_ci	btv->curr = btv->state.video;
355862306a36Sopenharmony_ci	btv->cvbi = btv->state.vbi;
355962306a36Sopenharmony_ci	btv->loop_irq = btv->state.loop_irq;
356062306a36Sopenharmony_ci	bttv_buffer_activate_video(btv, &btv->curr);
356162306a36Sopenharmony_ci	bttv_buffer_activate_vbi(btv, btv->cvbi);
356262306a36Sopenharmony_ci	bttv_set_dma(btv, 0);
356362306a36Sopenharmony_ci	spin_unlock_irqrestore(&btv->s_lock,flags);
356462306a36Sopenharmony_ci	return 0;
356562306a36Sopenharmony_ci}
356662306a36Sopenharmony_ci
356762306a36Sopenharmony_cistatic const struct pci_device_id bttv_pci_tbl[] = {
356862306a36Sopenharmony_ci	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0},
356962306a36Sopenharmony_ci	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0},
357062306a36Sopenharmony_ci	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0},
357162306a36Sopenharmony_ci	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0},
357262306a36Sopenharmony_ci	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879), 0},
357362306a36Sopenharmony_ci	{0,}
357462306a36Sopenharmony_ci};
357562306a36Sopenharmony_ci
357662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, bttv_pci_tbl);
357762306a36Sopenharmony_ci
357862306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(bttv_pm_ops,
357962306a36Sopenharmony_ci			 bttv_suspend,
358062306a36Sopenharmony_ci			 bttv_resume);
358162306a36Sopenharmony_ci
358262306a36Sopenharmony_cistatic struct pci_driver bttv_pci_driver = {
358362306a36Sopenharmony_ci	.name      = "bttv",
358462306a36Sopenharmony_ci	.id_table  = bttv_pci_tbl,
358562306a36Sopenharmony_ci	.probe     = bttv_probe,
358662306a36Sopenharmony_ci	.remove    = bttv_remove,
358762306a36Sopenharmony_ci	.driver.pm = &bttv_pm_ops,
358862306a36Sopenharmony_ci};
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_cistatic int __init bttv_init_module(void)
359162306a36Sopenharmony_ci{
359262306a36Sopenharmony_ci	int ret;
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_ci	bttv_num = 0;
359562306a36Sopenharmony_ci
359662306a36Sopenharmony_ci	pr_info("driver version %s loaded\n", BTTV_VERSION);
359762306a36Sopenharmony_ci	if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
359862306a36Sopenharmony_ci		gbuffers = 2;
359962306a36Sopenharmony_ci	if (gbufsize > BTTV_MAX_FBUF)
360062306a36Sopenharmony_ci		gbufsize = BTTV_MAX_FBUF;
360162306a36Sopenharmony_ci	gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
360262306a36Sopenharmony_ci	if (bttv_verbose)
360362306a36Sopenharmony_ci		pr_info("using %d buffers with %dk (%d pages) each for capture\n",
360462306a36Sopenharmony_ci			gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
360562306a36Sopenharmony_ci
360662306a36Sopenharmony_ci	bttv_check_chipset();
360762306a36Sopenharmony_ci
360862306a36Sopenharmony_ci	ret = bus_register(&bttv_sub_bus_type);
360962306a36Sopenharmony_ci	if (ret < 0) {
361062306a36Sopenharmony_ci		pr_warn("bus_register error: %d\n", ret);
361162306a36Sopenharmony_ci		return ret;
361262306a36Sopenharmony_ci	}
361362306a36Sopenharmony_ci	ret = pci_register_driver(&bttv_pci_driver);
361462306a36Sopenharmony_ci	if (ret < 0)
361562306a36Sopenharmony_ci		bus_unregister(&bttv_sub_bus_type);
361662306a36Sopenharmony_ci
361762306a36Sopenharmony_ci	return ret;
361862306a36Sopenharmony_ci}
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_cistatic void __exit bttv_cleanup_module(void)
362162306a36Sopenharmony_ci{
362262306a36Sopenharmony_ci	pci_unregister_driver(&bttv_pci_driver);
362362306a36Sopenharmony_ci	bus_unregister(&bttv_sub_bus_type);
362462306a36Sopenharmony_ci}
362562306a36Sopenharmony_ci
362662306a36Sopenharmony_cimodule_init(bttv_init_module);
362762306a36Sopenharmony_cimodule_exit(bttv_cleanup_module);
3628