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, ©, 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