162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB
462306a36Sopenharmony_ci//		    video capture devices
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
762306a36Sopenharmony_ci//		      Markus Rechberger <mrechberger@gmail.com>
862306a36Sopenharmony_ci//		      Mauro Carvalho Chehab <mchehab@kernel.org>
962306a36Sopenharmony_ci//		      Sascha Sommer <saschasommer@freenet.de>
1062306a36Sopenharmony_ci// Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com>
1162306a36Sopenharmony_ci//
1262306a36Sopenharmony_ci//	Some parts based on SN9C10x PC Camera Controllers GPL driver made
1362306a36Sopenharmony_ci//		by Luca Risolia <luca.risolia@studio.unibo.it>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "em28xx.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/list.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/kernel.h>
2162306a36Sopenharmony_ci#include <linux/bitmap.h>
2262306a36Sopenharmony_ci#include <linux/usb.h>
2362306a36Sopenharmony_ci#include <linux/i2c.h>
2462306a36Sopenharmony_ci#include <linux/mm.h>
2562306a36Sopenharmony_ci#include <linux/mutex.h>
2662306a36Sopenharmony_ci#include <linux/slab.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "em28xx-v4l.h"
2962306a36Sopenharmony_ci#include <media/v4l2-common.h>
3062306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
3162306a36Sopenharmony_ci#include <media/v4l2-event.h>
3262306a36Sopenharmony_ci#include <media/drv-intf/msp3400.h>
3362306a36Sopenharmony_ci#include <media/tuner.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
3662306a36Sopenharmony_ci		      "Markus Rechberger <mrechberger@gmail.com>, " \
3762306a36Sopenharmony_ci		      "Mauro Carvalho Chehab <mchehab@kernel.org>, " \
3862306a36Sopenharmony_ci		      "Sascha Sommer <saschasommer@freenet.de>"
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic unsigned int isoc_debug;
4162306a36Sopenharmony_cimodule_param(isoc_debug, int, 0644);
4262306a36Sopenharmony_ciMODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic unsigned int disable_vbi;
4562306a36Sopenharmony_cimodule_param(disable_vbi, int, 0644);
4662306a36Sopenharmony_ciMODULE_PARM_DESC(disable_vbi, "disable vbi support");
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int alt;
4962306a36Sopenharmony_cimodule_param(alt, int, 0644);
5062306a36Sopenharmony_ciMODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define em28xx_videodbg(fmt, arg...) do {				\
5362306a36Sopenharmony_ci	if (video_debug)						\
5462306a36Sopenharmony_ci		dev_printk(KERN_DEBUG, &dev->intf->dev,			\
5562306a36Sopenharmony_ci			   "video: %s: " fmt, __func__, ## arg);	\
5662306a36Sopenharmony_ci} while (0)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define em28xx_isocdbg(fmt, arg...) do {\
5962306a36Sopenharmony_ci	if (isoc_debug) \
6062306a36Sopenharmony_ci		dev_printk(KERN_DEBUG, &dev->intf->dev,			\
6162306a36Sopenharmony_ci			   "isoc: %s: " fmt, __func__, ## arg);		\
6262306a36Sopenharmony_ci} while (0)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
6562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC " - v4l2 interface");
6662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
6762306a36Sopenharmony_ciMODULE_VERSION(EM28XX_VERSION);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define EM25XX_FRMDATAHDR_BYTE1			0x02
7062306a36Sopenharmony_ci#define EM25XX_FRMDATAHDR_BYTE2_STILL_IMAGE	0x20
7162306a36Sopenharmony_ci#define EM25XX_FRMDATAHDR_BYTE2_FRAME_END	0x02
7262306a36Sopenharmony_ci#define EM25XX_FRMDATAHDR_BYTE2_FRAME_ID	0x01
7362306a36Sopenharmony_ci#define EM25XX_FRMDATAHDR_BYTE2_MASK	(EM25XX_FRMDATAHDR_BYTE2_STILL_IMAGE | \
7462306a36Sopenharmony_ci					 EM25XX_FRMDATAHDR_BYTE2_FRAME_END |   \
7562306a36Sopenharmony_ci					 EM25XX_FRMDATAHDR_BYTE2_FRAME_ID)
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U };
7862306a36Sopenharmony_cistatic unsigned int vbi_nr[]   = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U };
7962306a36Sopenharmony_cistatic unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U };
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cimodule_param_array(video_nr, int, NULL, 0444);
8262306a36Sopenharmony_cimodule_param_array(vbi_nr, int, NULL, 0444);
8362306a36Sopenharmony_cimodule_param_array(radio_nr, int, NULL, 0444);
8462306a36Sopenharmony_ciMODULE_PARM_DESC(video_nr, "video device numbers");
8562306a36Sopenharmony_ciMODULE_PARM_DESC(vbi_nr,   "vbi device numbers");
8662306a36Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "radio device numbers");
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic unsigned int video_debug;
8962306a36Sopenharmony_cimodule_param(video_debug, int, 0644);
9062306a36Sopenharmony_ciMODULE_PARM_DESC(video_debug, "enable debug messages [video]");
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* supported video standards */
9362306a36Sopenharmony_cistatic struct em28xx_fmt format[] = {
9462306a36Sopenharmony_ci	{
9562306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YUYV,
9662306a36Sopenharmony_ci		.depth    = 16,
9762306a36Sopenharmony_ci		.reg	  = EM28XX_OUTFMT_YUV422_Y0UY1V,
9862306a36Sopenharmony_ci	}, {
9962306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_RGB565,
10062306a36Sopenharmony_ci		.depth    = 16,
10162306a36Sopenharmony_ci		.reg      = EM28XX_OUTFMT_RGB_16_656,
10262306a36Sopenharmony_ci	}, {
10362306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_SRGGB8,
10462306a36Sopenharmony_ci		.depth    = 8,
10562306a36Sopenharmony_ci		.reg      = EM28XX_OUTFMT_RGB_8_RGRG,
10662306a36Sopenharmony_ci	}, {
10762306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_SBGGR8,
10862306a36Sopenharmony_ci		.depth    = 8,
10962306a36Sopenharmony_ci		.reg      = EM28XX_OUTFMT_RGB_8_BGBG,
11062306a36Sopenharmony_ci	}, {
11162306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_SGRBG8,
11262306a36Sopenharmony_ci		.depth    = 8,
11362306a36Sopenharmony_ci		.reg      = EM28XX_OUTFMT_RGB_8_GRGR,
11462306a36Sopenharmony_ci	}, {
11562306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_SGBRG8,
11662306a36Sopenharmony_ci		.depth    = 8,
11762306a36Sopenharmony_ci		.reg      = EM28XX_OUTFMT_RGB_8_GBGB,
11862306a36Sopenharmony_ci	}, {
11962306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YUV411P,
12062306a36Sopenharmony_ci		.depth    = 12,
12162306a36Sopenharmony_ci		.reg      = EM28XX_OUTFMT_YUV411,
12262306a36Sopenharmony_ci	},
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/*FIXME: maxw should be dependent of alt mode */
12662306a36Sopenharmony_cistatic inline unsigned int norm_maxw(struct em28xx *dev)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (dev->is_webcam)
13162306a36Sopenharmony_ci		return v4l2->sensor_xres;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (dev->board.max_range_640_480)
13462306a36Sopenharmony_ci		return 640;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return 720;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic inline unsigned int norm_maxh(struct em28xx *dev)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (dev->is_webcam)
14462306a36Sopenharmony_ci		return v4l2->sensor_yres;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (dev->board.max_range_640_480)
14762306a36Sopenharmony_ci		return 480;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return (v4l2->norm & V4L2_STD_625_50) ? 576 : 480;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int em28xx_vbi_supported(struct em28xx *dev)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	/* Modprobe option to manually disable */
15562306a36Sopenharmony_ci	if (disable_vbi == 1)
15662306a36Sopenharmony_ci		return 0;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (dev->is_webcam)
15962306a36Sopenharmony_ci		return 0;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* FIXME: check subdevices for VBI support */
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (dev->chip_id == CHIP_ID_EM2860 ||
16462306a36Sopenharmony_ci	    dev->chip_id == CHIP_ID_EM2883)
16562306a36Sopenharmony_ci		return 1;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* Version of em28xx that does not support VBI */
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/*
17262306a36Sopenharmony_ci * em28xx_wake_i2c()
17362306a36Sopenharmony_ci * configure i2c attached devices
17462306a36Sopenharmony_ci */
17562306a36Sopenharmony_cistatic void em28xx_wake_i2c(struct em28xx *dev)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	v4l2_device_call_all(v4l2_dev, 0, core,  reset, 0);
18062306a36Sopenharmony_ci	v4l2_device_call_all(v4l2_dev, 0, video, s_routing,
18162306a36Sopenharmony_ci			     INPUT(dev->ctl_input)->vmux, 0, 0);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int em28xx_colorlevels_set_default(struct em28xx *dev)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R20_YGAIN, CONTRAST_DEFAULT);
18762306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R21_YOFFSET, BRIGHTNESS_DEFAULT);
18862306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R22_UVGAIN, SATURATION_DEFAULT);
18962306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R23_UOFFSET, BLUE_BALANCE_DEFAULT);
19062306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R24_VOFFSET, RED_BALANCE_DEFAULT);
19162306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, SHARPNESS_DEFAULT);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20);
19462306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20);
19562306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20);
19662306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20);
19762306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00);
19862306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00);
19962306a36Sopenharmony_ci	return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int em28xx_set_outfmt(struct em28xx *dev)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	int ret;
20562306a36Sopenharmony_ci	u8 fmt, vinctrl;
20662306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	fmt = v4l2->format->reg;
20962306a36Sopenharmony_ci	if (!dev->is_em25xx)
21062306a36Sopenharmony_ci		fmt |= 0x20;
21162306a36Sopenharmony_ci	/*
21262306a36Sopenharmony_ci	 * NOTE: it's not clear if this is really needed !
21362306a36Sopenharmony_ci	 * The datasheets say bit 5 is a reserved bit and devices seem to work
21462306a36Sopenharmony_ci	 * fine without it. But the Windows driver sets it for em2710/50+em28xx
21562306a36Sopenharmony_ci	 * devices and we've always been setting it, too.
21662306a36Sopenharmony_ci	 *
21762306a36Sopenharmony_ci	 * em2765 (em25xx, em276x/7x/8x) devices do NOT work with this bit set,
21862306a36Sopenharmony_ci	 * it's likely used for an additional (compressed ?) format there.
21962306a36Sopenharmony_ci	 */
22062306a36Sopenharmony_ci	ret = em28xx_write_reg(dev, EM28XX_R27_OUTFMT, fmt);
22162306a36Sopenharmony_ci	if (ret < 0)
22262306a36Sopenharmony_ci		return ret;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, v4l2->vinmode);
22562306a36Sopenharmony_ci	if (ret < 0)
22662306a36Sopenharmony_ci		return ret;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	vinctrl = v4l2->vinctl;
22962306a36Sopenharmony_ci	if (em28xx_vbi_supported(dev) == 1) {
23062306a36Sopenharmony_ci		vinctrl |= EM28XX_VINCTRL_VBI_RAW;
23162306a36Sopenharmony_ci		em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00);
23262306a36Sopenharmony_ci		em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH,
23362306a36Sopenharmony_ci				 v4l2->vbi_width / 4);
23462306a36Sopenharmony_ci		em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, v4l2->vbi_height);
23562306a36Sopenharmony_ci		if (v4l2->norm & V4L2_STD_525_60) {
23662306a36Sopenharmony_ci			/* NTSC */
23762306a36Sopenharmony_ci			em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09);
23862306a36Sopenharmony_ci		} else if (v4l2->norm & V4L2_STD_625_50) {
23962306a36Sopenharmony_ci			/* PAL */
24062306a36Sopenharmony_ci			em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07);
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
24862306a36Sopenharmony_ci				  u8 ymin, u8 ymax)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	em28xx_videodbg("em28xx Scale: (%d,%d)-(%d,%d)\n",
25162306a36Sopenharmony_ci			xmin, ymin, xmax, ymax);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1);
25462306a36Sopenharmony_ci	em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1);
25562306a36Sopenharmony_ci	em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1);
25662306a36Sopenharmony_ci	return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart,
26062306a36Sopenharmony_ci				    u16 width, u16 height)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	u8 cwidth = width >> 2;
26362306a36Sopenharmony_ci	u8 cheight = height >> 2;
26462306a36Sopenharmony_ci	u8 overflow = (height >> 9 & 0x02) | (width >> 10 & 0x01);
26562306a36Sopenharmony_ci	/* NOTE: size limit: 2047x1023 = 2MPix */
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	em28xx_videodbg("capture area set to (%d,%d): %dx%d\n",
26862306a36Sopenharmony_ci			hstart, vstart,
26962306a36Sopenharmony_ci		       ((overflow & 2) << 9 | cwidth << 2),
27062306a36Sopenharmony_ci		       ((overflow & 1) << 10 | cheight << 2));
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1);
27362306a36Sopenharmony_ci	em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1);
27462306a36Sopenharmony_ci	em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1);
27562306a36Sopenharmony_ci	em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1);
27662306a36Sopenharmony_ci	em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* FIXME: function/meaning of these registers ? */
27962306a36Sopenharmony_ci	/* FIXME: align width+height to multiples of 4 ?! */
28062306a36Sopenharmony_ci	if (dev->is_em25xx) {
28162306a36Sopenharmony_ci		em28xx_write_reg(dev, 0x34, width >> 4);
28262306a36Sopenharmony_ci		em28xx_write_reg(dev, 0x35, height >> 4);
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	u8 mode = 0x00;
28962306a36Sopenharmony_ci	/* the em2800 scaler only supports scaling down to 50% */
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (dev->board.is_em2800) {
29262306a36Sopenharmony_ci		mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00);
29362306a36Sopenharmony_ci	} else {
29462306a36Sopenharmony_ci		u8 buf[2];
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		buf[0] = h;
29762306a36Sopenharmony_ci		buf[1] = h >> 8;
29862306a36Sopenharmony_ci		em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		buf[0] = v;
30162306a36Sopenharmony_ci		buf[1] = v >> 8;
30262306a36Sopenharmony_ci		em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2);
30362306a36Sopenharmony_ci		/*
30462306a36Sopenharmony_ci		 * it seems that both H and V scalers must be active
30562306a36Sopenharmony_ci		 * to work correctly
30662306a36Sopenharmony_ci		 */
30762306a36Sopenharmony_ci		mode = (h || v) ? 0x30 : 0x00;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci	return em28xx_write_reg(dev, EM28XX_R26_COMPR, mode);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/* FIXME: this only function read values from dev */
31362306a36Sopenharmony_cistatic int em28xx_resolution_set(struct em28xx *dev)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
31662306a36Sopenharmony_ci	int width = norm_maxw(dev);
31762306a36Sopenharmony_ci	int height = norm_maxh(dev);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* Properly setup VBI */
32062306a36Sopenharmony_ci	v4l2->vbi_width = 720;
32162306a36Sopenharmony_ci	if (v4l2->norm & V4L2_STD_525_60)
32262306a36Sopenharmony_ci		v4l2->vbi_height = 12;
32362306a36Sopenharmony_ci	else
32462306a36Sopenharmony_ci		v4l2->vbi_height = 18;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	em28xx_set_outfmt(dev);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/*
33162306a36Sopenharmony_ci	 * If we don't set the start position to 2 in VBI mode, we end up
33262306a36Sopenharmony_ci	 * with line 20/21 being YUYV encoded instead of being in 8-bit
33362306a36Sopenharmony_ci	 * greyscale.  The core of the issue is that line 21 (and line 23 for
33462306a36Sopenharmony_ci	 * PAL WSS) are inside of active video region, and as a result they
33562306a36Sopenharmony_ci	 * get the pixelformatting associated with that area.  So by cropping
33662306a36Sopenharmony_ci	 * it out, we end up with the same format as the rest of the VBI
33762306a36Sopenharmony_ci	 * region
33862306a36Sopenharmony_ci	 */
33962306a36Sopenharmony_ci	if (em28xx_vbi_supported(dev) == 1)
34062306a36Sopenharmony_ci		em28xx_capture_area_set(dev, 0, 2, width, height);
34162306a36Sopenharmony_ci	else
34262306a36Sopenharmony_ci		em28xx_capture_area_set(dev, 0, 0, width, height);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return em28xx_scaler_set(dev, v4l2->hscale, v4l2->vscale);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci/* Set USB alternate setting for analog video */
34862306a36Sopenharmony_cistatic int em28xx_set_alternate(struct em28xx *dev)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
35162306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(dev->intf);
35262306a36Sopenharmony_ci	int err;
35362306a36Sopenharmony_ci	int i;
35462306a36Sopenharmony_ci	unsigned int min_pkt_size = v4l2->width * 2 + 4;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/*
35762306a36Sopenharmony_ci	 * NOTE: for isoc transfers, only alt settings > 0 are allowed
35862306a36Sopenharmony_ci	 * bulk transfers seem to work only with alt=0 !
35962306a36Sopenharmony_ci	 */
36062306a36Sopenharmony_ci	dev->alt = 0;
36162306a36Sopenharmony_ci	if (alt > 0 && alt < dev->num_alt) {
36262306a36Sopenharmony_ci		em28xx_videodbg("alternate forced to %d\n", dev->alt);
36362306a36Sopenharmony_ci		dev->alt = alt;
36462306a36Sopenharmony_ci		goto set_alt;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci	if (dev->analog_xfer_bulk)
36762306a36Sopenharmony_ci		goto set_alt;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/*
37062306a36Sopenharmony_ci	 * When image size is bigger than a certain value,
37162306a36Sopenharmony_ci	 * the frame size should be increased, otherwise, only
37262306a36Sopenharmony_ci	 * green screen will be received.
37362306a36Sopenharmony_ci	 */
37462306a36Sopenharmony_ci	if (v4l2->width * 2 * v4l2->height > 720 * 240 * 2)
37562306a36Sopenharmony_ci		min_pkt_size *= 2;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	for (i = 0; i < dev->num_alt; i++) {
37862306a36Sopenharmony_ci		/* stop when the selected alt setting offers enough bandwidth */
37962306a36Sopenharmony_ci		if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) {
38062306a36Sopenharmony_ci			dev->alt = i;
38162306a36Sopenharmony_ci			break;
38262306a36Sopenharmony_ci		/*
38362306a36Sopenharmony_ci		 * otherwise make sure that we end up with the maximum
38462306a36Sopenharmony_ci		 * bandwidth because the min_pkt_size equation might be wrong.
38562306a36Sopenharmony_ci		 *
38662306a36Sopenharmony_ci		 */
38762306a36Sopenharmony_ci		} else if (dev->alt_max_pkt_size_isoc[i] >
38862306a36Sopenharmony_ci			   dev->alt_max_pkt_size_isoc[dev->alt])
38962306a36Sopenharmony_ci			dev->alt = i;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ciset_alt:
39362306a36Sopenharmony_ci	/*
39462306a36Sopenharmony_ci	 * NOTE: for bulk transfers, we need to call usb_set_interface()
39562306a36Sopenharmony_ci	 * even if the previous settings were the same. Otherwise streaming
39662306a36Sopenharmony_ci	 * fails with all urbs having status = -EOVERFLOW !
39762306a36Sopenharmony_ci	 */
39862306a36Sopenharmony_ci	if (dev->analog_xfer_bulk) {
39962306a36Sopenharmony_ci		dev->max_pkt_size = 512; /* USB 2.0 spec */
40062306a36Sopenharmony_ci		dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER;
40162306a36Sopenharmony_ci	} else { /* isoc */
40262306a36Sopenharmony_ci		em28xx_videodbg("minimum isoc packet size: %u (alt=%d)\n",
40362306a36Sopenharmony_ci				min_pkt_size, dev->alt);
40462306a36Sopenharmony_ci		dev->max_pkt_size =
40562306a36Sopenharmony_ci				  dev->alt_max_pkt_size_isoc[dev->alt];
40662306a36Sopenharmony_ci		dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci	em28xx_videodbg("setting alternate %d with wMaxPacketSize=%u\n",
40962306a36Sopenharmony_ci			dev->alt, dev->max_pkt_size);
41062306a36Sopenharmony_ci	err = usb_set_interface(udev, dev->ifnum, dev->alt);
41162306a36Sopenharmony_ci	if (err < 0) {
41262306a36Sopenharmony_ci		dev_err(&dev->intf->dev,
41362306a36Sopenharmony_ci			"cannot change alternate number to %d (error=%i)\n",
41462306a36Sopenharmony_ci			dev->alt, err);
41562306a36Sopenharmony_ci		return err;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci	return 0;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci/*
42162306a36Sopenharmony_ci * DMA and thread functions
42262306a36Sopenharmony_ci */
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci/*
42562306a36Sopenharmony_ci * Finish the current buffer
42662306a36Sopenharmony_ci */
42762306a36Sopenharmony_cistatic inline void finish_buffer(struct em28xx *dev,
42862306a36Sopenharmony_ci				 struct em28xx_buffer *buf)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	buf->vb.sequence = dev->v4l2->field_count++;
43362306a36Sopenharmony_ci	if (dev->v4l2->progressive)
43462306a36Sopenharmony_ci		buf->vb.field = V4L2_FIELD_NONE;
43562306a36Sopenharmony_ci	else
43662306a36Sopenharmony_ci		buf->vb.field = V4L2_FIELD_INTERLACED;
43762306a36Sopenharmony_ci	buf->vb.vb2_buf.timestamp = ktime_get_ns();
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci/*
44362306a36Sopenharmony_ci * Copy picture data from USB buffer to video buffer
44462306a36Sopenharmony_ci */
44562306a36Sopenharmony_cistatic void em28xx_copy_video(struct em28xx *dev,
44662306a36Sopenharmony_ci			      struct em28xx_buffer *buf,
44762306a36Sopenharmony_ci			      unsigned char *usb_buf,
44862306a36Sopenharmony_ci			      unsigned long len)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
45162306a36Sopenharmony_ci	void *fieldstart, *startwrite, *startread;
45262306a36Sopenharmony_ci	int  linesdone, currlinedone, offset, lencopy, remain;
45362306a36Sopenharmony_ci	int bytesperline = v4l2->width << 1;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (buf->pos + len > buf->length)
45662306a36Sopenharmony_ci		len = buf->length - buf->pos;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	startread = usb_buf;
45962306a36Sopenharmony_ci	remain = len;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (v4l2->progressive || buf->top_field)
46262306a36Sopenharmony_ci		fieldstart = buf->vb_buf;
46362306a36Sopenharmony_ci	else /* interlaced mode, even nr. of lines */
46462306a36Sopenharmony_ci		fieldstart = buf->vb_buf + bytesperline;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	linesdone = buf->pos / bytesperline;
46762306a36Sopenharmony_ci	currlinedone = buf->pos % bytesperline;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (v4l2->progressive)
47062306a36Sopenharmony_ci		offset = linesdone * bytesperline + currlinedone;
47162306a36Sopenharmony_ci	else
47262306a36Sopenharmony_ci		offset = linesdone * bytesperline * 2 + currlinedone;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	startwrite = fieldstart + offset;
47562306a36Sopenharmony_ci	lencopy = bytesperline - currlinedone;
47662306a36Sopenharmony_ci	lencopy = lencopy > remain ? remain : lencopy;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->length) {
47962306a36Sopenharmony_ci		em28xx_isocdbg("Overflow of %zu bytes past buffer end (1)\n",
48062306a36Sopenharmony_ci			       ((char *)startwrite + lencopy) -
48162306a36Sopenharmony_ci			      ((char *)buf->vb_buf + buf->length));
48262306a36Sopenharmony_ci		remain = (char *)buf->vb_buf + buf->length -
48362306a36Sopenharmony_ci			 (char *)startwrite;
48462306a36Sopenharmony_ci		lencopy = remain;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci	if (lencopy <= 0)
48762306a36Sopenharmony_ci		return;
48862306a36Sopenharmony_ci	memcpy(startwrite, startread, lencopy);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	remain -= lencopy;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	while (remain > 0) {
49362306a36Sopenharmony_ci		if (v4l2->progressive)
49462306a36Sopenharmony_ci			startwrite += lencopy;
49562306a36Sopenharmony_ci		else
49662306a36Sopenharmony_ci			startwrite += lencopy + bytesperline;
49762306a36Sopenharmony_ci		startread += lencopy;
49862306a36Sopenharmony_ci		if (bytesperline > remain)
49962306a36Sopenharmony_ci			lencopy = remain;
50062306a36Sopenharmony_ci		else
50162306a36Sopenharmony_ci			lencopy = bytesperline;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		if ((char *)startwrite + lencopy > (char *)buf->vb_buf +
50462306a36Sopenharmony_ci		    buf->length) {
50562306a36Sopenharmony_ci			em28xx_isocdbg("Overflow of %zu bytes past buffer end(2)\n",
50662306a36Sopenharmony_ci				       ((char *)startwrite + lencopy) -
50762306a36Sopenharmony_ci				       ((char *)buf->vb_buf + buf->length));
50862306a36Sopenharmony_ci			remain = (char *)buf->vb_buf + buf->length -
50962306a36Sopenharmony_ci				 (char *)startwrite;
51062306a36Sopenharmony_ci			lencopy = remain;
51162306a36Sopenharmony_ci		}
51262306a36Sopenharmony_ci		if (lencopy <= 0)
51362306a36Sopenharmony_ci			break;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		memcpy(startwrite, startread, lencopy);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		remain -= lencopy;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	buf->pos += len;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci/*
52462306a36Sopenharmony_ci * Copy VBI data from USB buffer to video buffer
52562306a36Sopenharmony_ci */
52662306a36Sopenharmony_cistatic void em28xx_copy_vbi(struct em28xx *dev,
52762306a36Sopenharmony_ci			    struct em28xx_buffer *buf,
52862306a36Sopenharmony_ci			    unsigned char *usb_buf,
52962306a36Sopenharmony_ci			    unsigned long len)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	unsigned int offset;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (buf->pos + len > buf->length)
53462306a36Sopenharmony_ci		len = buf->length - buf->pos;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	offset = buf->pos;
53762306a36Sopenharmony_ci	/* Make sure the bottom field populates the second half of the frame */
53862306a36Sopenharmony_ci	if (buf->top_field == 0)
53962306a36Sopenharmony_ci		offset += dev->v4l2->vbi_width * dev->v4l2->vbi_height;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	memcpy(buf->vb_buf + offset, usb_buf, len);
54262306a36Sopenharmony_ci	buf->pos += len;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic inline void print_err_status(struct em28xx *dev,
54662306a36Sopenharmony_ci				    int packet, int status)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	char *errmsg = "Unknown";
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	switch (status) {
55162306a36Sopenharmony_ci	case -ENOENT:
55262306a36Sopenharmony_ci		errmsg = "unlinked synchronously";
55362306a36Sopenharmony_ci		break;
55462306a36Sopenharmony_ci	case -ECONNRESET:
55562306a36Sopenharmony_ci		errmsg = "unlinked asynchronously";
55662306a36Sopenharmony_ci		break;
55762306a36Sopenharmony_ci	case -ENOSR:
55862306a36Sopenharmony_ci		errmsg = "Buffer error (overrun)";
55962306a36Sopenharmony_ci		break;
56062306a36Sopenharmony_ci	case -EPIPE:
56162306a36Sopenharmony_ci		errmsg = "Stalled (device not responding)";
56262306a36Sopenharmony_ci		break;
56362306a36Sopenharmony_ci	case -EOVERFLOW:
56462306a36Sopenharmony_ci		errmsg = "Babble (bad cable?)";
56562306a36Sopenharmony_ci		break;
56662306a36Sopenharmony_ci	case -EPROTO:
56762306a36Sopenharmony_ci		errmsg = "Bit-stuff error (bad cable?)";
56862306a36Sopenharmony_ci		break;
56962306a36Sopenharmony_ci	case -EILSEQ:
57062306a36Sopenharmony_ci		errmsg = "CRC/Timeout (could be anything)";
57162306a36Sopenharmony_ci		break;
57262306a36Sopenharmony_ci	case -ETIME:
57362306a36Sopenharmony_ci		errmsg = "Device does not respond";
57462306a36Sopenharmony_ci		break;
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci	if (packet < 0) {
57762306a36Sopenharmony_ci		em28xx_isocdbg("URB status %d [%s].\n",	status, errmsg);
57862306a36Sopenharmony_ci	} else {
57962306a36Sopenharmony_ci		em28xx_isocdbg("URB packet %d, status %d [%s].\n",
58062306a36Sopenharmony_ci			       packet, status, errmsg);
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci/*
58562306a36Sopenharmony_ci * get the next available buffer from dma queue
58662306a36Sopenharmony_ci */
58762306a36Sopenharmony_cistatic inline struct em28xx_buffer *get_next_buf(struct em28xx *dev,
58862306a36Sopenharmony_ci						 struct em28xx_dmaqueue *dma_q)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct em28xx_buffer *buf;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (list_empty(&dma_q->active)) {
59362306a36Sopenharmony_ci		em28xx_isocdbg("No active queue to serve\n");
59462306a36Sopenharmony_ci		return NULL;
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	/* Get the next buffer */
59862306a36Sopenharmony_ci	buf = list_entry(dma_q->active.next, struct em28xx_buffer, list);
59962306a36Sopenharmony_ci	/* Cleans up buffer - Useful for testing for frame/URB loss */
60062306a36Sopenharmony_ci	list_del(&buf->list);
60162306a36Sopenharmony_ci	buf->pos = 0;
60262306a36Sopenharmony_ci	buf->vb_buf = buf->mem;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return buf;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci/*
60862306a36Sopenharmony_ci * Finish the current buffer if completed and prepare for the next field
60962306a36Sopenharmony_ci */
61062306a36Sopenharmony_cistatic struct em28xx_buffer *
61162306a36Sopenharmony_cifinish_field_prepare_next(struct em28xx *dev,
61262306a36Sopenharmony_ci			  struct em28xx_buffer *buf,
61362306a36Sopenharmony_ci			  struct em28xx_dmaqueue *dma_q)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (v4l2->progressive || v4l2->top_field) { /* Brand new frame */
61862306a36Sopenharmony_ci		if (buf)
61962306a36Sopenharmony_ci			finish_buffer(dev, buf);
62062306a36Sopenharmony_ci		buf = get_next_buf(dev, dma_q);
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci	if (buf) {
62362306a36Sopenharmony_ci		buf->top_field = v4l2->top_field;
62462306a36Sopenharmony_ci		buf->pos = 0;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	return buf;
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci/*
63162306a36Sopenharmony_ci * Process data packet according to the em2710/em2750/em28xx frame data format
63262306a36Sopenharmony_ci */
63362306a36Sopenharmony_cistatic inline void process_frame_data_em28xx(struct em28xx *dev,
63462306a36Sopenharmony_ci					     unsigned char *data_pkt,
63562306a36Sopenharmony_ci					     unsigned int  data_len)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct em28xx_v4l2      *v4l2 = dev->v4l2;
63862306a36Sopenharmony_ci	struct em28xx_buffer    *buf = dev->usb_ctl.vid_buf;
63962306a36Sopenharmony_ci	struct em28xx_buffer    *vbi_buf = dev->usb_ctl.vbi_buf;
64062306a36Sopenharmony_ci	struct em28xx_dmaqueue  *dma_q = &dev->vidq;
64162306a36Sopenharmony_ci	struct em28xx_dmaqueue  *vbi_dma_q = &dev->vbiq;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	/*
64462306a36Sopenharmony_ci	 * capture type 0 = vbi start
64562306a36Sopenharmony_ci	 * capture type 1 = vbi in progress
64662306a36Sopenharmony_ci	 * capture type 2 = video start
64762306a36Sopenharmony_ci	 * capture type 3 = video in progress
64862306a36Sopenharmony_ci	 */
64962306a36Sopenharmony_ci	if (data_len >= 4) {
65062306a36Sopenharmony_ci		/*
65162306a36Sopenharmony_ci		 * NOTE: Headers are always 4 bytes and
65262306a36Sopenharmony_ci		 * never split across packets
65362306a36Sopenharmony_ci		 */
65462306a36Sopenharmony_ci		if (data_pkt[0] == 0x88 && data_pkt[1] == 0x88 &&
65562306a36Sopenharmony_ci		    data_pkt[2] == 0x88 && data_pkt[3] == 0x88) {
65662306a36Sopenharmony_ci			/* Continuation */
65762306a36Sopenharmony_ci			data_pkt += 4;
65862306a36Sopenharmony_ci			data_len -= 4;
65962306a36Sopenharmony_ci		} else if (data_pkt[0] == 0x33 && data_pkt[1] == 0x95) {
66062306a36Sopenharmony_ci			/* Field start (VBI mode) */
66162306a36Sopenharmony_ci			v4l2->capture_type = 0;
66262306a36Sopenharmony_ci			v4l2->vbi_read = 0;
66362306a36Sopenharmony_ci			em28xx_isocdbg("VBI START HEADER !!!\n");
66462306a36Sopenharmony_ci			v4l2->top_field = !(data_pkt[2] & 1);
66562306a36Sopenharmony_ci			data_pkt += 4;
66662306a36Sopenharmony_ci			data_len -= 4;
66762306a36Sopenharmony_ci		} else if (data_pkt[0] == 0x22 && data_pkt[1] == 0x5a) {
66862306a36Sopenharmony_ci			/* Field start (VBI disabled) */
66962306a36Sopenharmony_ci			v4l2->capture_type = 2;
67062306a36Sopenharmony_ci			em28xx_isocdbg("VIDEO START HEADER !!!\n");
67162306a36Sopenharmony_ci			v4l2->top_field = !(data_pkt[2] & 1);
67262306a36Sopenharmony_ci			data_pkt += 4;
67362306a36Sopenharmony_ci			data_len -= 4;
67462306a36Sopenharmony_ci		}
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci	/*
67762306a36Sopenharmony_ci	 * NOTE: With bulk transfers, intermediate data packets
67862306a36Sopenharmony_ci	 * have no continuation header
67962306a36Sopenharmony_ci	 */
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (v4l2->capture_type == 0) {
68262306a36Sopenharmony_ci		vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q);
68362306a36Sopenharmony_ci		dev->usb_ctl.vbi_buf = vbi_buf;
68462306a36Sopenharmony_ci		v4l2->capture_type = 1;
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (v4l2->capture_type == 1) {
68862306a36Sopenharmony_ci		int vbi_size = v4l2->vbi_width * v4l2->vbi_height;
68962306a36Sopenharmony_ci		int vbi_data_len = ((v4l2->vbi_read + data_len) > vbi_size) ?
69062306a36Sopenharmony_ci				   (vbi_size - v4l2->vbi_read) : data_len;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		/* Copy VBI data */
69362306a36Sopenharmony_ci		if (vbi_buf)
69462306a36Sopenharmony_ci			em28xx_copy_vbi(dev, vbi_buf, data_pkt, vbi_data_len);
69562306a36Sopenharmony_ci		v4l2->vbi_read += vbi_data_len;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci		if (vbi_data_len < data_len) {
69862306a36Sopenharmony_ci			/* Continue with copying video data */
69962306a36Sopenharmony_ci			v4l2->capture_type = 2;
70062306a36Sopenharmony_ci			data_pkt += vbi_data_len;
70162306a36Sopenharmony_ci			data_len -= vbi_data_len;
70262306a36Sopenharmony_ci		}
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (v4l2->capture_type == 2) {
70662306a36Sopenharmony_ci		buf = finish_field_prepare_next(dev, buf, dma_q);
70762306a36Sopenharmony_ci		dev->usb_ctl.vid_buf = buf;
70862306a36Sopenharmony_ci		v4l2->capture_type = 3;
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	if (v4l2->capture_type == 3 && buf && data_len > 0)
71262306a36Sopenharmony_ci		em28xx_copy_video(dev, buf, data_pkt, data_len);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci/*
71662306a36Sopenharmony_ci * Process data packet according to the em25xx/em276x/7x/8x frame data format
71762306a36Sopenharmony_ci */
71862306a36Sopenharmony_cistatic inline void process_frame_data_em25xx(struct em28xx *dev,
71962306a36Sopenharmony_ci					     unsigned char *data_pkt,
72062306a36Sopenharmony_ci					     unsigned int  data_len)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct em28xx_buffer    *buf = dev->usb_ctl.vid_buf;
72362306a36Sopenharmony_ci	struct em28xx_dmaqueue  *dmaq = &dev->vidq;
72462306a36Sopenharmony_ci	struct em28xx_v4l2      *v4l2 = dev->v4l2;
72562306a36Sopenharmony_ci	bool frame_end = false;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	/* Check for header */
72862306a36Sopenharmony_ci	/*
72962306a36Sopenharmony_ci	 * NOTE: at least with bulk transfers, only the first packet
73062306a36Sopenharmony_ci	 * has a header and has always set the FRAME_END bit
73162306a36Sopenharmony_ci	 */
73262306a36Sopenharmony_ci	if (data_len >= 2) {	/* em25xx header is only 2 bytes long */
73362306a36Sopenharmony_ci		if ((data_pkt[0] == EM25XX_FRMDATAHDR_BYTE1) &&
73462306a36Sopenharmony_ci		    ((data_pkt[1] & ~EM25XX_FRMDATAHDR_BYTE2_MASK) == 0x00)) {
73562306a36Sopenharmony_ci			v4l2->top_field = !(data_pkt[1] &
73662306a36Sopenharmony_ci					   EM25XX_FRMDATAHDR_BYTE2_FRAME_ID);
73762306a36Sopenharmony_ci			frame_end = data_pkt[1] &
73862306a36Sopenharmony_ci				    EM25XX_FRMDATAHDR_BYTE2_FRAME_END;
73962306a36Sopenharmony_ci			data_pkt += 2;
74062306a36Sopenharmony_ci			data_len -= 2;
74162306a36Sopenharmony_ci		}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		/* Finish field and prepare next (BULK only) */
74462306a36Sopenharmony_ci		if (dev->analog_xfer_bulk && frame_end) {
74562306a36Sopenharmony_ci			buf = finish_field_prepare_next(dev, buf, dmaq);
74662306a36Sopenharmony_ci			dev->usb_ctl.vid_buf = buf;
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci		/*
74962306a36Sopenharmony_ci		 * NOTE: in ISOC mode when a new frame starts and buf==NULL,
75062306a36Sopenharmony_ci		 * we COULD already prepare a buffer here to avoid skipping the
75162306a36Sopenharmony_ci		 * first frame.
75262306a36Sopenharmony_ci		 */
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/* Copy data */
75662306a36Sopenharmony_ci	if (buf && data_len > 0)
75762306a36Sopenharmony_ci		em28xx_copy_video(dev, buf, data_pkt, data_len);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	/* Finish frame (ISOC only) => avoids lag of 1 frame */
76062306a36Sopenharmony_ci	if (!dev->analog_xfer_bulk && frame_end) {
76162306a36Sopenharmony_ci		buf = finish_field_prepare_next(dev, buf, dmaq);
76262306a36Sopenharmony_ci		dev->usb_ctl.vid_buf = buf;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	/*
76662306a36Sopenharmony_ci	 * NOTES:
76762306a36Sopenharmony_ci	 *
76862306a36Sopenharmony_ci	 * 1) Tested with USB bulk transfers only !
76962306a36Sopenharmony_ci	 * The wording in the datasheet suggests that isoc might work different.
77062306a36Sopenharmony_ci	 * The current code assumes that with isoc transfers each packet has a
77162306a36Sopenharmony_ci	 * header like with the other em28xx devices.
77262306a36Sopenharmony_ci	 *
77362306a36Sopenharmony_ci	 * 2) Support for interlaced mode is pure theory. It has not been
77462306a36Sopenharmony_ci	 * tested and it is unknown if these devices actually support it.
77562306a36Sopenharmony_ci	 */
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci/* Processes and copies the URB data content (video and VBI data) */
77962306a36Sopenharmony_cistatic inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	int xfer_bulk, num_packets, i;
78262306a36Sopenharmony_ci	unsigned char *usb_data_pkt;
78362306a36Sopenharmony_ci	unsigned int usb_data_len;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	if (!dev)
78662306a36Sopenharmony_ci		return 0;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (dev->disconnected)
78962306a36Sopenharmony_ci		return 0;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (urb->status < 0)
79262306a36Sopenharmony_ci		print_err_status(dev, -1, urb->status);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	xfer_bulk = usb_pipebulk(urb->pipe);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (xfer_bulk) /* bulk */
79762306a36Sopenharmony_ci		num_packets = 1;
79862306a36Sopenharmony_ci	else /* isoc */
79962306a36Sopenharmony_ci		num_packets = urb->number_of_packets;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	for (i = 0; i < num_packets; i++) {
80262306a36Sopenharmony_ci		if (xfer_bulk) { /* bulk */
80362306a36Sopenharmony_ci			usb_data_len = urb->actual_length;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci			usb_data_pkt = urb->transfer_buffer;
80662306a36Sopenharmony_ci		} else { /* isoc */
80762306a36Sopenharmony_ci			if (urb->iso_frame_desc[i].status < 0) {
80862306a36Sopenharmony_ci				print_err_status(dev, i,
80962306a36Sopenharmony_ci						 urb->iso_frame_desc[i].status);
81062306a36Sopenharmony_ci				if (urb->iso_frame_desc[i].status != -EPROTO)
81162306a36Sopenharmony_ci					continue;
81262306a36Sopenharmony_ci			}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci			usb_data_len = urb->iso_frame_desc[i].actual_length;
81562306a36Sopenharmony_ci			if (usb_data_len > dev->max_pkt_size) {
81662306a36Sopenharmony_ci				em28xx_isocdbg("packet bigger than packet size");
81762306a36Sopenharmony_ci				continue;
81862306a36Sopenharmony_ci			}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci			usb_data_pkt = urb->transfer_buffer +
82162306a36Sopenharmony_ci				       urb->iso_frame_desc[i].offset;
82262306a36Sopenharmony_ci		}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci		if (usb_data_len == 0) {
82562306a36Sopenharmony_ci			/* NOTE: happens very often with isoc transfers */
82662306a36Sopenharmony_ci			/* em28xx_usbdbg("packet %d is empty",i); - spammy */
82762306a36Sopenharmony_ci			continue;
82862306a36Sopenharmony_ci		}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		if (dev->is_em25xx)
83162306a36Sopenharmony_ci			process_frame_data_em25xx(dev,
83262306a36Sopenharmony_ci						  usb_data_pkt, usb_data_len);
83362306a36Sopenharmony_ci		else
83462306a36Sopenharmony_ci			process_frame_data_em28xx(dev,
83562306a36Sopenharmony_ci						  usb_data_pkt, usb_data_len);
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci	return 1;
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_cistatic int get_resource(enum v4l2_buf_type f_type)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	switch (f_type) {
84362306a36Sopenharmony_ci	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
84462306a36Sopenharmony_ci		return EM28XX_RESOURCE_VIDEO;
84562306a36Sopenharmony_ci	case V4L2_BUF_TYPE_VBI_CAPTURE:
84662306a36Sopenharmony_ci		return EM28XX_RESOURCE_VBI;
84762306a36Sopenharmony_ci	default:
84862306a36Sopenharmony_ci		WARN_ON(1);
84962306a36Sopenharmony_ci		return -1; /* Indicate that device is busy */
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci/* Usage lock check functions */
85462306a36Sopenharmony_cistatic int res_get(struct em28xx *dev, enum v4l2_buf_type f_type)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	int res_type = get_resource(f_type);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	/* is it free? */
85962306a36Sopenharmony_ci	if (dev->resources & res_type) {
86062306a36Sopenharmony_ci		/* no, someone else uses it */
86162306a36Sopenharmony_ci		return -EBUSY;
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	/* it's free, grab it */
86562306a36Sopenharmony_ci	dev->resources |= res_type;
86662306a36Sopenharmony_ci	em28xx_videodbg("res: get %d\n", res_type);
86762306a36Sopenharmony_ci	return 0;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_cistatic void res_free(struct em28xx *dev, enum v4l2_buf_type f_type)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	int res_type = get_resource(f_type);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	dev->resources &= ~res_type;
87562306a36Sopenharmony_ci	em28xx_videodbg("res: put %d\n", res_type);
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic void em28xx_v4l2_media_release(struct em28xx *dev)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
88162306a36Sopenharmony_ci	int i;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	for (i = 0; i < MAX_EM28XX_INPUT; i++) {
88462306a36Sopenharmony_ci		if (!INPUT(i)->type)
88562306a36Sopenharmony_ci			return;
88662306a36Sopenharmony_ci		media_device_unregister_entity(&dev->input_ent[i]);
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci#endif
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci/*
89262306a36Sopenharmony_ci * Media Controller helper functions
89362306a36Sopenharmony_ci */
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic int em28xx_enable_analog_tuner(struct em28xx *dev)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
89862306a36Sopenharmony_ci	struct media_device *mdev = dev->media_dev;
89962306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
90062306a36Sopenharmony_ci	struct media_entity *source;
90162306a36Sopenharmony_ci	struct media_link *link, *found_link = NULL;
90262306a36Sopenharmony_ci	int ret, active_links = 0;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	if (!mdev || !v4l2->decoder)
90562306a36Sopenharmony_ci		return 0;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/*
90862306a36Sopenharmony_ci	 * This will find the tuner that is connected into the decoder.
90962306a36Sopenharmony_ci	 * Technically, this is not 100% correct, as the device may be
91062306a36Sopenharmony_ci	 * using an analog input instead of the tuner. However, as we can't
91162306a36Sopenharmony_ci	 * do DVB streaming while the DMA engine is being used for V4L2,
91262306a36Sopenharmony_ci	 * this should be enough for the actual needs.
91362306a36Sopenharmony_ci	 */
91462306a36Sopenharmony_ci	list_for_each_entry(link, &v4l2->decoder->links, list) {
91562306a36Sopenharmony_ci		if (link->sink->entity == v4l2->decoder) {
91662306a36Sopenharmony_ci			found_link = link;
91762306a36Sopenharmony_ci			if (link->flags & MEDIA_LNK_FL_ENABLED)
91862306a36Sopenharmony_ci				active_links++;
91962306a36Sopenharmony_ci			break;
92062306a36Sopenharmony_ci		}
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (active_links == 1 || !found_link)
92462306a36Sopenharmony_ci		return 0;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	source = found_link->source->entity;
92762306a36Sopenharmony_ci	list_for_each_entry(link, &source->links, list) {
92862306a36Sopenharmony_ci		struct media_entity *sink;
92962306a36Sopenharmony_ci		int flags = 0;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		sink = link->sink->entity;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci		if (sink == v4l2->decoder)
93462306a36Sopenharmony_ci			flags = MEDIA_LNK_FL_ENABLED;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci		ret = media_entity_setup_link(link, flags);
93762306a36Sopenharmony_ci		if (ret) {
93862306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
93962306a36Sopenharmony_ci				"Couldn't change link %s->%s to %s. Error %d\n",
94062306a36Sopenharmony_ci				source->name, sink->name,
94162306a36Sopenharmony_ci				flags ? "enabled" : "disabled",
94262306a36Sopenharmony_ci				ret);
94362306a36Sopenharmony_ci			return ret;
94462306a36Sopenharmony_ci		}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci		em28xx_videodbg("link %s->%s was %s\n",
94762306a36Sopenharmony_ci				source->name, sink->name,
94862306a36Sopenharmony_ci				flags ? "ENABLED" : "disabled");
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci#endif
95162306a36Sopenharmony_ci	return 0;
95262306a36Sopenharmony_ci}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_cistatic const char * const iname[] = {
95562306a36Sopenharmony_ci	[EM28XX_VMUX_COMPOSITE]  = "Composite",
95662306a36Sopenharmony_ci	[EM28XX_VMUX_SVIDEO]     = "S-Video",
95762306a36Sopenharmony_ci	[EM28XX_VMUX_TELEVISION] = "Television",
95862306a36Sopenharmony_ci	[EM28XX_RADIO]           = "Radio",
95962306a36Sopenharmony_ci};
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic void em28xx_v4l2_create_entities(struct em28xx *dev)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER)
96462306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
96562306a36Sopenharmony_ci	int ret, i;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	/* Initialize Video, VBI and Radio pads */
96862306a36Sopenharmony_ci	v4l2->video_pad.flags = MEDIA_PAD_FL_SINK;
96962306a36Sopenharmony_ci	ret = media_entity_pads_init(&v4l2->vdev.entity, 1, &v4l2->video_pad);
97062306a36Sopenharmony_ci	if (ret < 0)
97162306a36Sopenharmony_ci		dev_err(&dev->intf->dev,
97262306a36Sopenharmony_ci			"failed to initialize video media entity!\n");
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	if (em28xx_vbi_supported(dev)) {
97562306a36Sopenharmony_ci		v4l2->vbi_pad.flags = MEDIA_PAD_FL_SINK;
97662306a36Sopenharmony_ci		ret = media_entity_pads_init(&v4l2->vbi_dev.entity, 1,
97762306a36Sopenharmony_ci					     &v4l2->vbi_pad);
97862306a36Sopenharmony_ci		if (ret < 0)
97962306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
98062306a36Sopenharmony_ci				"failed to initialize vbi media entity!\n");
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	/* Webcams don't have input connectors */
98462306a36Sopenharmony_ci	if (dev->is_webcam)
98562306a36Sopenharmony_ci		return;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	/* Create entities for each input connector */
98862306a36Sopenharmony_ci	for (i = 0; i < MAX_EM28XX_INPUT; i++) {
98962306a36Sopenharmony_ci		struct media_entity *ent = &dev->input_ent[i];
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		if (!INPUT(i)->type)
99262306a36Sopenharmony_ci			break;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci		ent->name = iname[INPUT(i)->type];
99562306a36Sopenharmony_ci		ent->flags = MEDIA_ENT_FL_CONNECTOR;
99662306a36Sopenharmony_ci		dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		switch (INPUT(i)->type) {
99962306a36Sopenharmony_ci		case EM28XX_VMUX_COMPOSITE:
100062306a36Sopenharmony_ci			ent->function = MEDIA_ENT_F_CONN_COMPOSITE;
100162306a36Sopenharmony_ci			break;
100262306a36Sopenharmony_ci		case EM28XX_VMUX_SVIDEO:
100362306a36Sopenharmony_ci			ent->function = MEDIA_ENT_F_CONN_SVIDEO;
100462306a36Sopenharmony_ci			break;
100562306a36Sopenharmony_ci		default: /* EM28XX_VMUX_TELEVISION or EM28XX_RADIO */
100662306a36Sopenharmony_ci			if (dev->tuner_type != TUNER_ABSENT)
100762306a36Sopenharmony_ci				ent->function = MEDIA_ENT_F_CONN_RF;
100862306a36Sopenharmony_ci			break;
100962306a36Sopenharmony_ci		}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]);
101262306a36Sopenharmony_ci		if (ret < 0)
101362306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
101462306a36Sopenharmony_ci				"failed to initialize input pad[%d]!\n", i);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci		ret = media_device_register_entity(dev->media_dev, ent);
101762306a36Sopenharmony_ci		if (ret < 0)
101862306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
101962306a36Sopenharmony_ci				"failed to register input entity %d!\n", i);
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci#endif
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci/*
102562306a36Sopenharmony_ci * Videobuf2 operations
102662306a36Sopenharmony_ci */
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq,
102962306a36Sopenharmony_ci		       unsigned int *nbuffers, unsigned int *nplanes,
103062306a36Sopenharmony_ci		       unsigned int sizes[], struct device *alloc_devs[])
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	struct em28xx *dev = vb2_get_drv_priv(vq);
103362306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
103462306a36Sopenharmony_ci	unsigned long size =
103562306a36Sopenharmony_ci		    (v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	if (*nplanes)
103862306a36Sopenharmony_ci		return sizes[0] < size ? -EINVAL : 0;
103962306a36Sopenharmony_ci	*nplanes = 1;
104062306a36Sopenharmony_ci	sizes[0] = size;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	em28xx_enable_analog_tuner(dev);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	return 0;
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cistatic int
104862306a36Sopenharmony_cibuffer_prepare(struct vb2_buffer *vb)
104962306a36Sopenharmony_ci{
105062306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
105162306a36Sopenharmony_ci	struct em28xx        *dev = vb2_get_drv_priv(vb->vb2_queue);
105262306a36Sopenharmony_ci	struct em28xx_v4l2   *v4l2 = dev->v4l2;
105362306a36Sopenharmony_ci	unsigned long size;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	em28xx_videodbg("%s, field=%d\n", __func__, vbuf->field);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	size = (v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size) {
106062306a36Sopenharmony_ci		em28xx_videodbg("%s data will not fit into plane (%lu < %lu)\n",
106162306a36Sopenharmony_ci				__func__, vb2_plane_size(vb, 0), size);
106262306a36Sopenharmony_ci		return -EINVAL;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci	vb2_set_plane_payload(vb, 0, size);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	return 0;
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ciint em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	struct em28xx *dev = vb2_get_drv_priv(vq);
107262306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
107362306a36Sopenharmony_ci	struct v4l2_frequency f;
107462306a36Sopenharmony_ci	struct v4l2_fh *owner;
107562306a36Sopenharmony_ci	int rc = 0;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	em28xx_videodbg("%s\n", __func__);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	dev->v4l2->field_count = 0;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	/*
108262306a36Sopenharmony_ci	 * Make sure streaming is not already in progress for this type
108362306a36Sopenharmony_ci	 * of filehandle (e.g. video, vbi)
108462306a36Sopenharmony_ci	 */
108562306a36Sopenharmony_ci	rc = res_get(dev, vq->type);
108662306a36Sopenharmony_ci	if (rc)
108762306a36Sopenharmony_ci		return rc;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	if (v4l2->streaming_users == 0) {
109062306a36Sopenharmony_ci		/* First active streaming user, so allocate all the URBs */
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci		/* Allocate the USB bandwidth */
109362306a36Sopenharmony_ci		em28xx_set_alternate(dev);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		/*
109662306a36Sopenharmony_ci		 * Needed, since GPIO might have disabled power of
109762306a36Sopenharmony_ci		 * some i2c device
109862306a36Sopenharmony_ci		 */
109962306a36Sopenharmony_ci		em28xx_wake_i2c(dev);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci		v4l2->capture_type = -1;
110262306a36Sopenharmony_ci		rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE,
110362306a36Sopenharmony_ci					  dev->analog_xfer_bulk,
110462306a36Sopenharmony_ci					  EM28XX_NUM_BUFS,
110562306a36Sopenharmony_ci					  dev->max_pkt_size,
110662306a36Sopenharmony_ci					  dev->packet_multiplier,
110762306a36Sopenharmony_ci					  em28xx_urb_data_copy);
110862306a36Sopenharmony_ci		if (rc < 0)
110962306a36Sopenharmony_ci			return rc;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci		/*
111262306a36Sopenharmony_ci		 * djh: it's not clear whether this code is still needed.  I'm
111362306a36Sopenharmony_ci		 * leaving it in here for now entirely out of concern for
111462306a36Sopenharmony_ci		 * backward compatibility (the old code did it)
111562306a36Sopenharmony_ci		 */
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci		/* Ask tuner to go to analog or radio mode */
111862306a36Sopenharmony_ci		memset(&f, 0, sizeof(f));
111962306a36Sopenharmony_ci		f.frequency = v4l2->frequency;
112062306a36Sopenharmony_ci		owner = (struct v4l2_fh *)vq->owner;
112162306a36Sopenharmony_ci		if (owner && owner->vdev->vfl_type == VFL_TYPE_RADIO)
112262306a36Sopenharmony_ci			f.type = V4L2_TUNER_RADIO;
112362306a36Sopenharmony_ci		else
112462306a36Sopenharmony_ci			f.type = V4L2_TUNER_ANALOG_TV;
112562306a36Sopenharmony_ci		v4l2_device_call_all(&v4l2->v4l2_dev,
112662306a36Sopenharmony_ci				     0, tuner, s_frequency, &f);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		/* Enable video stream at TV decoder */
112962306a36Sopenharmony_ci		v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 1);
113062306a36Sopenharmony_ci	}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	v4l2->streaming_users++;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	return rc;
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_cistatic void em28xx_stop_streaming(struct vb2_queue *vq)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	struct em28xx *dev = vb2_get_drv_priv(vq);
114062306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
114162306a36Sopenharmony_ci	struct em28xx_dmaqueue *vidq = &dev->vidq;
114262306a36Sopenharmony_ci	unsigned long flags = 0;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	em28xx_videodbg("%s\n", __func__);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	res_free(dev, vq->type);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if (v4l2->streaming_users-- == 1) {
114962306a36Sopenharmony_ci		/* Disable video stream at TV decoder */
115062306a36Sopenharmony_ci		v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 0);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci		/* Last active user, so shutdown all the URBS */
115362306a36Sopenharmony_ci		em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE);
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
115762306a36Sopenharmony_ci	if (dev->usb_ctl.vid_buf) {
115862306a36Sopenharmony_ci		vb2_buffer_done(&dev->usb_ctl.vid_buf->vb.vb2_buf,
115962306a36Sopenharmony_ci				VB2_BUF_STATE_ERROR);
116062306a36Sopenharmony_ci		dev->usb_ctl.vid_buf = NULL;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci	while (!list_empty(&vidq->active)) {
116362306a36Sopenharmony_ci		struct em28xx_buffer *buf;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci		buf = list_entry(vidq->active.next, struct em28xx_buffer, list);
116662306a36Sopenharmony_ci		list_del(&buf->list);
116762306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_civoid em28xx_stop_vbi_streaming(struct vb2_queue *vq)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	struct em28xx *dev = vb2_get_drv_priv(vq);
117562306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
117662306a36Sopenharmony_ci	struct em28xx_dmaqueue *vbiq = &dev->vbiq;
117762306a36Sopenharmony_ci	unsigned long flags = 0;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	em28xx_videodbg("%s\n", __func__);
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	res_free(dev, vq->type);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	if (v4l2->streaming_users-- == 1) {
118462306a36Sopenharmony_ci		/* Disable video stream at TV decoder */
118562306a36Sopenharmony_ci		v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 0);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		/* Last active user, so shutdown all the URBS */
118862306a36Sopenharmony_ci		em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE);
118962306a36Sopenharmony_ci	}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
119262306a36Sopenharmony_ci	if (dev->usb_ctl.vbi_buf) {
119362306a36Sopenharmony_ci		vb2_buffer_done(&dev->usb_ctl.vbi_buf->vb.vb2_buf,
119462306a36Sopenharmony_ci				VB2_BUF_STATE_ERROR);
119562306a36Sopenharmony_ci		dev->usb_ctl.vbi_buf = NULL;
119662306a36Sopenharmony_ci	}
119762306a36Sopenharmony_ci	while (!list_empty(&vbiq->active)) {
119862306a36Sopenharmony_ci		struct em28xx_buffer *buf;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci		buf = list_entry(vbiq->active.next, struct em28xx_buffer, list);
120162306a36Sopenharmony_ci		list_del(&buf->list);
120262306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
120562306a36Sopenharmony_ci}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_cistatic void
120862306a36Sopenharmony_cibuffer_queue(struct vb2_buffer *vb)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
121162306a36Sopenharmony_ci	struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue);
121262306a36Sopenharmony_ci	struct em28xx_buffer *buf =
121362306a36Sopenharmony_ci		container_of(vbuf, struct em28xx_buffer, vb);
121462306a36Sopenharmony_ci	struct em28xx_dmaqueue *vidq = &dev->vidq;
121562306a36Sopenharmony_ci	unsigned long flags = 0;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	em28xx_videodbg("%s\n", __func__);
121862306a36Sopenharmony_ci	buf->mem = vb2_plane_vaddr(vb, 0);
121962306a36Sopenharmony_ci	buf->length = vb2_plane_size(vb, 0);
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
122262306a36Sopenharmony_ci	list_add_tail(&buf->list, &vidq->active);
122362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_cistatic const struct vb2_ops em28xx_video_qops = {
122762306a36Sopenharmony_ci	.queue_setup    = queue_setup,
122862306a36Sopenharmony_ci	.buf_prepare    = buffer_prepare,
122962306a36Sopenharmony_ci	.buf_queue      = buffer_queue,
123062306a36Sopenharmony_ci	.start_streaming = em28xx_start_analog_streaming,
123162306a36Sopenharmony_ci	.stop_streaming = em28xx_stop_streaming,
123262306a36Sopenharmony_ci	.wait_prepare   = vb2_ops_wait_prepare,
123362306a36Sopenharmony_ci	.wait_finish    = vb2_ops_wait_finish,
123462306a36Sopenharmony_ci};
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_cistatic int em28xx_vb2_setup(struct em28xx *dev)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	int rc;
123962306a36Sopenharmony_ci	struct vb2_queue *q;
124062306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	/* Setup Videobuf2 for Video capture */
124362306a36Sopenharmony_ci	q = &v4l2->vb_vidq;
124462306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
124562306a36Sopenharmony_ci	q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
124662306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
124762306a36Sopenharmony_ci	q->drv_priv = dev;
124862306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct em28xx_buffer);
124962306a36Sopenharmony_ci	q->ops = &em28xx_video_qops;
125062306a36Sopenharmony_ci	q->mem_ops = &vb2_vmalloc_memops;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	rc = vb2_queue_init(q);
125362306a36Sopenharmony_ci	if (rc < 0)
125462306a36Sopenharmony_ci		return rc;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	/* Setup Videobuf2 for VBI capture */
125762306a36Sopenharmony_ci	q = &v4l2->vb_vbiq;
125862306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
125962306a36Sopenharmony_ci	q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
126062306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
126162306a36Sopenharmony_ci	q->drv_priv = dev;
126262306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct em28xx_buffer);
126362306a36Sopenharmony_ci	q->ops = &em28xx_vbi_qops;
126462306a36Sopenharmony_ci	q->mem_ops = &vb2_vmalloc_memops;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	rc = vb2_queue_init(q);
126762306a36Sopenharmony_ci	if (rc < 0)
126862306a36Sopenharmony_ci		return rc;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	return 0;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci/*
127462306a36Sopenharmony_ci * v4l2 interface
127562306a36Sopenharmony_ci */
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cistatic void video_mux(struct em28xx *dev, int index)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	dev->ctl_input = index;
128262306a36Sopenharmony_ci	dev->ctl_ainput = INPUT(index)->amux;
128362306a36Sopenharmony_ci	dev->ctl_aoutput = INPUT(index)->aout;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	if (!dev->ctl_aoutput)
128662306a36Sopenharmony_ci		dev->ctl_aoutput = EM28XX_AOUT_MASTER;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	v4l2_device_call_all(v4l2_dev, 0, video, s_routing,
128962306a36Sopenharmony_ci			     INPUT(index)->vmux, 0, 0);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	if (dev->has_msp34xx) {
129262306a36Sopenharmony_ci		if (dev->i2s_speed) {
129362306a36Sopenharmony_ci			v4l2_device_call_all(v4l2_dev, 0, audio,
129462306a36Sopenharmony_ci					     s_i2s_clock_freq, dev->i2s_speed);
129562306a36Sopenharmony_ci		}
129662306a36Sopenharmony_ci		/* Note: this is msp3400 specific */
129762306a36Sopenharmony_ci		v4l2_device_call_all(v4l2_dev, 0, audio, s_routing,
129862306a36Sopenharmony_ci				     dev->ctl_ainput,
129962306a36Sopenharmony_ci				     MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0);
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	if (dev->board.adecoder != EM28XX_NOADECODER) {
130362306a36Sopenharmony_ci		v4l2_device_call_all(v4l2_dev, 0, audio, s_routing,
130462306a36Sopenharmony_ci				     dev->ctl_ainput, dev->ctl_aoutput, 0);
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	em28xx_audio_analog_set(dev);
130862306a36Sopenharmony_ci}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_cistatic void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	struct em28xx *dev = priv;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	/*
131562306a36Sopenharmony_ci	 * In the case of non-AC97 volume controls, we still need
131662306a36Sopenharmony_ci	 * to do some setups at em28xx, in order to mute/unmute
131762306a36Sopenharmony_ci	 * and to adjust audio volume. However, the value ranges
131862306a36Sopenharmony_ci	 * should be checked by the corresponding V4L subdriver.
131962306a36Sopenharmony_ci	 */
132062306a36Sopenharmony_ci	switch (ctrl->id) {
132162306a36Sopenharmony_ci	case V4L2_CID_AUDIO_MUTE:
132262306a36Sopenharmony_ci		dev->mute = ctrl->val;
132362306a36Sopenharmony_ci		em28xx_audio_analog_set(dev);
132462306a36Sopenharmony_ci		break;
132562306a36Sopenharmony_ci	case V4L2_CID_AUDIO_VOLUME:
132662306a36Sopenharmony_ci		dev->volume = ctrl->val;
132762306a36Sopenharmony_ci		em28xx_audio_analog_set(dev);
132862306a36Sopenharmony_ci		break;
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_cistatic int em28xx_s_ctrl(struct v4l2_ctrl *ctrl)
133362306a36Sopenharmony_ci{
133462306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 =
133562306a36Sopenharmony_ci		  container_of(ctrl->handler, struct em28xx_v4l2, ctrl_handler);
133662306a36Sopenharmony_ci	struct em28xx *dev = v4l2->dev;
133762306a36Sopenharmony_ci	int ret = -EINVAL;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	switch (ctrl->id) {
134062306a36Sopenharmony_ci	case V4L2_CID_AUDIO_MUTE:
134162306a36Sopenharmony_ci		dev->mute = ctrl->val;
134262306a36Sopenharmony_ci		ret = em28xx_audio_analog_set(dev);
134362306a36Sopenharmony_ci		break;
134462306a36Sopenharmony_ci	case V4L2_CID_AUDIO_VOLUME:
134562306a36Sopenharmony_ci		dev->volume = ctrl->val;
134662306a36Sopenharmony_ci		ret = em28xx_audio_analog_set(dev);
134762306a36Sopenharmony_ci		break;
134862306a36Sopenharmony_ci	case V4L2_CID_CONTRAST:
134962306a36Sopenharmony_ci		ret = em28xx_write_reg(dev, EM28XX_R20_YGAIN, ctrl->val);
135062306a36Sopenharmony_ci		break;
135162306a36Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
135262306a36Sopenharmony_ci		ret = em28xx_write_reg(dev, EM28XX_R21_YOFFSET, ctrl->val);
135362306a36Sopenharmony_ci		break;
135462306a36Sopenharmony_ci	case V4L2_CID_SATURATION:
135562306a36Sopenharmony_ci		ret = em28xx_write_reg(dev, EM28XX_R22_UVGAIN, ctrl->val);
135662306a36Sopenharmony_ci		break;
135762306a36Sopenharmony_ci	case V4L2_CID_BLUE_BALANCE:
135862306a36Sopenharmony_ci		ret = em28xx_write_reg(dev, EM28XX_R23_UOFFSET, ctrl->val);
135962306a36Sopenharmony_ci		break;
136062306a36Sopenharmony_ci	case V4L2_CID_RED_BALANCE:
136162306a36Sopenharmony_ci		ret = em28xx_write_reg(dev, EM28XX_R24_VOFFSET, ctrl->val);
136262306a36Sopenharmony_ci		break;
136362306a36Sopenharmony_ci	case V4L2_CID_SHARPNESS:
136462306a36Sopenharmony_ci		ret = em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, ctrl->val);
136562306a36Sopenharmony_ci		break;
136662306a36Sopenharmony_ci	}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	return (ret < 0) ? ret : 0;
136962306a36Sopenharmony_ci}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops em28xx_ctrl_ops = {
137262306a36Sopenharmony_ci	.s_ctrl = em28xx_s_ctrl,
137362306a36Sopenharmony_ci};
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_cistatic void size_to_scale(struct em28xx *dev,
137662306a36Sopenharmony_ci			  unsigned int width, unsigned int height,
137762306a36Sopenharmony_ci			unsigned int *hscale, unsigned int *vscale)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	unsigned int          maxw = norm_maxw(dev);
138062306a36Sopenharmony_ci	unsigned int          maxh = norm_maxh(dev);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	*hscale = (((unsigned long)maxw) << 12) / width - 4096L;
138362306a36Sopenharmony_ci	if (*hscale > EM28XX_HVSCALE_MAX)
138462306a36Sopenharmony_ci		*hscale = EM28XX_HVSCALE_MAX;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	*vscale = (((unsigned long)maxh) << 12) / height - 4096L;
138762306a36Sopenharmony_ci	if (*vscale > EM28XX_HVSCALE_MAX)
138862306a36Sopenharmony_ci		*vscale = EM28XX_HVSCALE_MAX;
138962306a36Sopenharmony_ci}
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_cistatic void scale_to_size(struct em28xx *dev,
139262306a36Sopenharmony_ci			  unsigned int hscale, unsigned int vscale,
139362306a36Sopenharmony_ci			  unsigned int *width, unsigned int *height)
139462306a36Sopenharmony_ci{
139562306a36Sopenharmony_ci	unsigned int          maxw = norm_maxw(dev);
139662306a36Sopenharmony_ci	unsigned int          maxh = norm_maxh(dev);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	*width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
139962306a36Sopenharmony_ci	*height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	/* Don't let width or height to be zero */
140262306a36Sopenharmony_ci	if (*width < 1)
140362306a36Sopenharmony_ci		*width = 1;
140462306a36Sopenharmony_ci	if (*height < 1)
140562306a36Sopenharmony_ci		*height = 1;
140662306a36Sopenharmony_ci}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci/*
140962306a36Sopenharmony_ci * IOCTL vidioc handling
141062306a36Sopenharmony_ci */
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
141362306a36Sopenharmony_ci				struct v4l2_format *f)
141462306a36Sopenharmony_ci{
141562306a36Sopenharmony_ci	struct em28xx         *dev = video_drvdata(file);
141662306a36Sopenharmony_ci	struct em28xx_v4l2    *v4l2 = dev->v4l2;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	f->fmt.pix.width = v4l2->width;
141962306a36Sopenharmony_ci	f->fmt.pix.height = v4l2->height;
142062306a36Sopenharmony_ci	f->fmt.pix.pixelformat = v4l2->format->fourcc;
142162306a36Sopenharmony_ci	f->fmt.pix.bytesperline = (v4l2->width * v4l2->format->depth + 7) >> 3;
142262306a36Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * v4l2->height;
142362306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	/* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
142662306a36Sopenharmony_ci	if (v4l2->progressive)
142762306a36Sopenharmony_ci		f->fmt.pix.field = V4L2_FIELD_NONE;
142862306a36Sopenharmony_ci	else
142962306a36Sopenharmony_ci		f->fmt.pix.field = v4l2->interlaced_fieldmode ?
143062306a36Sopenharmony_ci			   V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
143162306a36Sopenharmony_ci	return 0;
143262306a36Sopenharmony_ci}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_cistatic struct em28xx_fmt *format_by_fourcc(unsigned int fourcc)
143562306a36Sopenharmony_ci{
143662306a36Sopenharmony_ci	unsigned int i;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(format); i++)
143962306a36Sopenharmony_ci		if (format[i].fourcc == fourcc)
144062306a36Sopenharmony_ci			return &format[i];
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	return NULL;
144362306a36Sopenharmony_ci}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
144662306a36Sopenharmony_ci				  struct v4l2_format *f)
144762306a36Sopenharmony_ci{
144862306a36Sopenharmony_ci	struct em28xx         *dev   = video_drvdata(file);
144962306a36Sopenharmony_ci	struct em28xx_v4l2    *v4l2  = dev->v4l2;
145062306a36Sopenharmony_ci	unsigned int          width  = f->fmt.pix.width;
145162306a36Sopenharmony_ci	unsigned int          height = f->fmt.pix.height;
145262306a36Sopenharmony_ci	unsigned int          maxw   = norm_maxw(dev);
145362306a36Sopenharmony_ci	unsigned int          maxh   = norm_maxh(dev);
145462306a36Sopenharmony_ci	unsigned int          hscale, vscale;
145562306a36Sopenharmony_ci	struct em28xx_fmt     *fmt;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
145862306a36Sopenharmony_ci	if (!fmt) {
145962306a36Sopenharmony_ci		fmt = &format[0];
146062306a36Sopenharmony_ci		em28xx_videodbg("Fourcc format (%08x) invalid. Using default (%08x).\n",
146162306a36Sopenharmony_ci				f->fmt.pix.pixelformat, fmt->fourcc);
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	if (dev->board.is_em2800) {
146562306a36Sopenharmony_ci		/* the em2800 can only scale down to 50% */
146662306a36Sopenharmony_ci		height = height > (3 * maxh / 4) ? maxh : maxh / 2;
146762306a36Sopenharmony_ci		width = width > (3 * maxw / 4) ? maxw : maxw / 2;
146862306a36Sopenharmony_ci		/*
146962306a36Sopenharmony_ci		 * MaxPacketSize for em2800 is too small to capture at full
147062306a36Sopenharmony_ci		 * resolution use half of maxw as the scaler can only scale
147162306a36Sopenharmony_ci		 * to 50%
147262306a36Sopenharmony_ci		 */
147362306a36Sopenharmony_ci		if (width == maxw && height == maxh)
147462306a36Sopenharmony_ci			width /= 2;
147562306a36Sopenharmony_ci	} else {
147662306a36Sopenharmony_ci		/*
147762306a36Sopenharmony_ci		 * width must even because of the YUYV format
147862306a36Sopenharmony_ci		 * height must be even because of interlacing
147962306a36Sopenharmony_ci		 */
148062306a36Sopenharmony_ci		v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh,
148162306a36Sopenharmony_ci				      1, 0);
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci	/* Avoid division by zero at size_to_scale */
148462306a36Sopenharmony_ci	if (width < 1)
148562306a36Sopenharmony_ci		width = 1;
148662306a36Sopenharmony_ci	if (height < 1)
148762306a36Sopenharmony_ci		height = 1;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	size_to_scale(dev, width, height, &hscale, &vscale);
149062306a36Sopenharmony_ci	scale_to_size(dev, hscale, vscale, &width, &height);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	f->fmt.pix.width = width;
149362306a36Sopenharmony_ci	f->fmt.pix.height = height;
149462306a36Sopenharmony_ci	f->fmt.pix.pixelformat = fmt->fourcc;
149562306a36Sopenharmony_ci	f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3;
149662306a36Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
149762306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
149862306a36Sopenharmony_ci	if (v4l2->progressive)
149962306a36Sopenharmony_ci		f->fmt.pix.field = V4L2_FIELD_NONE;
150062306a36Sopenharmony_ci	else
150162306a36Sopenharmony_ci		f->fmt.pix.field = v4l2->interlaced_fieldmode ?
150262306a36Sopenharmony_ci			   V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	return 0;
150562306a36Sopenharmony_ci}
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_cistatic int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc,
150862306a36Sopenharmony_ci				   unsigned int width, unsigned int height)
150962306a36Sopenharmony_ci{
151062306a36Sopenharmony_ci	struct em28xx_fmt     *fmt;
151162306a36Sopenharmony_ci	struct em28xx_v4l2    *v4l2 = dev->v4l2;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	fmt = format_by_fourcc(fourcc);
151462306a36Sopenharmony_ci	if (!fmt)
151562306a36Sopenharmony_ci		return -EINVAL;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	v4l2->format = fmt;
151862306a36Sopenharmony_ci	v4l2->width  = width;
151962306a36Sopenharmony_ci	v4l2->height = height;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	/* set new image size */
152262306a36Sopenharmony_ci	size_to_scale(dev, v4l2->width, v4l2->height,
152362306a36Sopenharmony_ci		      &v4l2->hscale, &v4l2->vscale);
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	em28xx_resolution_set(dev);
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	return 0;
152862306a36Sopenharmony_ci}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
153162306a36Sopenharmony_ci				struct v4l2_format *f)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
153462306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	if (vb2_is_busy(&v4l2->vb_vidq))
153762306a36Sopenharmony_ci		return -EBUSY;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	vidioc_try_fmt_vid_cap(file, priv, f);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	return em28xx_set_video_format(dev, f->fmt.pix.pixelformat,
154262306a36Sopenharmony_ci				f->fmt.pix.width, f->fmt.pix.height);
154362306a36Sopenharmony_ci}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
154662306a36Sopenharmony_ci{
154762306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	*norm = dev->v4l2->norm;
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	return 0;
155262306a36Sopenharmony_ci}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_cistatic int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, video, querystd, norm);
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	return 0;
156162306a36Sopenharmony_ci}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	struct em28xx      *dev  = video_drvdata(file);
156662306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
156762306a36Sopenharmony_ci	struct v4l2_format f;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	if (norm == v4l2->norm)
157062306a36Sopenharmony_ci		return 0;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	if (v4l2->streaming_users > 0)
157362306a36Sopenharmony_ci		return -EBUSY;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	v4l2->norm = norm;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	/* Adjusts width/height, if needed */
157862306a36Sopenharmony_ci	f.fmt.pix.width = 720;
157962306a36Sopenharmony_ci	f.fmt.pix.height = (norm & V4L2_STD_525_60) ? 480 : 576;
158062306a36Sopenharmony_ci	vidioc_try_fmt_vid_cap(file, priv, &f);
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	/* set new image size */
158362306a36Sopenharmony_ci	v4l2->width = f.fmt.pix.width;
158462306a36Sopenharmony_ci	v4l2->height = f.fmt.pix.height;
158562306a36Sopenharmony_ci	size_to_scale(dev, v4l2->width, v4l2->height,
158662306a36Sopenharmony_ci		      &v4l2->hscale, &v4l2->vscale);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	em28xx_resolution_set(dev);
158962306a36Sopenharmony_ci	v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm);
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	return 0;
159262306a36Sopenharmony_ci}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_cistatic int vidioc_g_parm(struct file *file, void *priv,
159562306a36Sopenharmony_ci			 struct v4l2_streamparm *p)
159662306a36Sopenharmony_ci{
159762306a36Sopenharmony_ci	struct v4l2_subdev_frame_interval ival = { 0 };
159862306a36Sopenharmony_ci	struct em28xx      *dev  = video_drvdata(file);
159962306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
160062306a36Sopenharmony_ci	int rc = 0;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
160362306a36Sopenharmony_ci	    p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
160462306a36Sopenharmony_ci		return -EINVAL;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	p->parm.capture.readbuffers = EM28XX_MIN_BUF;
160762306a36Sopenharmony_ci	p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
160862306a36Sopenharmony_ci	if (dev->is_webcam) {
160962306a36Sopenharmony_ci		rc = v4l2_device_call_until_err(&v4l2->v4l2_dev, 0,
161062306a36Sopenharmony_ci						video, g_frame_interval, &ival);
161162306a36Sopenharmony_ci		if (!rc)
161262306a36Sopenharmony_ci			p->parm.capture.timeperframe = ival.interval;
161362306a36Sopenharmony_ci	} else {
161462306a36Sopenharmony_ci		v4l2_video_std_frame_period(v4l2->norm,
161562306a36Sopenharmony_ci					    &p->parm.capture.timeperframe);
161662306a36Sopenharmony_ci	}
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	return rc;
161962306a36Sopenharmony_ci}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_cistatic int vidioc_s_parm(struct file *file, void *priv,
162262306a36Sopenharmony_ci			 struct v4l2_streamparm *p)
162362306a36Sopenharmony_ci{
162462306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
162562306a36Sopenharmony_ci	struct v4l2_subdev_frame_interval ival = {
162662306a36Sopenharmony_ci		0,
162762306a36Sopenharmony_ci		p->parm.capture.timeperframe
162862306a36Sopenharmony_ci	};
162962306a36Sopenharmony_ci	int rc = 0;
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	if (!dev->is_webcam)
163262306a36Sopenharmony_ci		return -ENOTTY;
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
163562306a36Sopenharmony_ci	    p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
163662306a36Sopenharmony_ci		return -EINVAL;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	memset(&p->parm, 0, sizeof(p->parm));
163962306a36Sopenharmony_ci	p->parm.capture.readbuffers = EM28XX_MIN_BUF;
164062306a36Sopenharmony_ci	p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
164162306a36Sopenharmony_ci	rc = v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, 0,
164262306a36Sopenharmony_ci					video, s_frame_interval, &ival);
164362306a36Sopenharmony_ci	if (!rc)
164462306a36Sopenharmony_ci		p->parm.capture.timeperframe = ival.interval;
164562306a36Sopenharmony_ci	return rc;
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv,
164962306a36Sopenharmony_ci			     struct v4l2_input *i)
165062306a36Sopenharmony_ci{
165162306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
165262306a36Sopenharmony_ci	unsigned int       n;
165362306a36Sopenharmony_ci	int j;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	n = i->index;
165662306a36Sopenharmony_ci	if (n >= MAX_EM28XX_INPUT)
165762306a36Sopenharmony_ci		return -EINVAL;
165862306a36Sopenharmony_ci	if (!INPUT(n)->type)
165962306a36Sopenharmony_ci		return -EINVAL;
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	i->type = V4L2_INPUT_TYPE_CAMERA;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	strscpy(i->name, iname[INPUT(n)->type], sizeof(i->name));
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	if (INPUT(n)->type == EM28XX_VMUX_TELEVISION)
166662306a36Sopenharmony_ci		i->type = V4L2_INPUT_TYPE_TUNER;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	i->std = dev->v4l2->vdev.tvnorms;
166962306a36Sopenharmony_ci	/* webcams do not have the STD API */
167062306a36Sopenharmony_ci	if (dev->is_webcam)
167162306a36Sopenharmony_ci		i->capabilities = 0;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	/* Dynamically generates an audioset bitmask */
167462306a36Sopenharmony_ci	i->audioset = 0;
167562306a36Sopenharmony_ci	for (j = 0; j < MAX_EM28XX_INPUT; j++)
167662306a36Sopenharmony_ci		if (dev->amux_map[j] != EM28XX_AMUX_UNUSED)
167762306a36Sopenharmony_ci			i->audioset |= 1 << j;
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	return 0;
168062306a36Sopenharmony_ci}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
168362306a36Sopenharmony_ci{
168462306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	*i = dev->ctl_input;
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	return 0;
168962306a36Sopenharmony_ci}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i)
169262306a36Sopenharmony_ci{
169362306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	if (i >= MAX_EM28XX_INPUT)
169662306a36Sopenharmony_ci		return -EINVAL;
169762306a36Sopenharmony_ci	if (!INPUT(i)->type)
169862306a36Sopenharmony_ci		return -EINVAL;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	video_mux(dev, i);
170162306a36Sopenharmony_ci	return 0;
170262306a36Sopenharmony_ci}
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_cistatic int em28xx_fill_audio_input(struct em28xx *dev,
170562306a36Sopenharmony_ci				   const char *s,
170662306a36Sopenharmony_ci				   struct v4l2_audio *a,
170762306a36Sopenharmony_ci				   unsigned int index)
170862306a36Sopenharmony_ci{
170962306a36Sopenharmony_ci	unsigned int idx = dev->amux_map[index];
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	/*
171262306a36Sopenharmony_ci	 * With msp3400, almost all mappings use the default (amux = 0).
171362306a36Sopenharmony_ci	 * The only one may use a different value is WinTV USB2, where it
171462306a36Sopenharmony_ci	 * can also be SCART1 input.
171562306a36Sopenharmony_ci	 * As it is very doubtful that we would see new boards with msp3400,
171662306a36Sopenharmony_ci	 * let's just reuse the existing switch.
171762306a36Sopenharmony_ci	 */
171862306a36Sopenharmony_ci	if (dev->has_msp34xx && idx != EM28XX_AMUX_UNUSED)
171962306a36Sopenharmony_ci		idx = EM28XX_AMUX_LINE_IN;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	switch (idx) {
172262306a36Sopenharmony_ci	case EM28XX_AMUX_VIDEO:
172362306a36Sopenharmony_ci		strscpy(a->name, "Television", sizeof(a->name));
172462306a36Sopenharmony_ci		break;
172562306a36Sopenharmony_ci	case EM28XX_AMUX_LINE_IN:
172662306a36Sopenharmony_ci		strscpy(a->name, "Line In", sizeof(a->name));
172762306a36Sopenharmony_ci		break;
172862306a36Sopenharmony_ci	case EM28XX_AMUX_VIDEO2:
172962306a36Sopenharmony_ci		strscpy(a->name, "Television alt", sizeof(a->name));
173062306a36Sopenharmony_ci		break;
173162306a36Sopenharmony_ci	case EM28XX_AMUX_PHONE:
173262306a36Sopenharmony_ci		strscpy(a->name, "Phone", sizeof(a->name));
173362306a36Sopenharmony_ci		break;
173462306a36Sopenharmony_ci	case EM28XX_AMUX_MIC:
173562306a36Sopenharmony_ci		strscpy(a->name, "Mic", sizeof(a->name));
173662306a36Sopenharmony_ci		break;
173762306a36Sopenharmony_ci	case EM28XX_AMUX_CD:
173862306a36Sopenharmony_ci		strscpy(a->name, "CD", sizeof(a->name));
173962306a36Sopenharmony_ci		break;
174062306a36Sopenharmony_ci	case EM28XX_AMUX_AUX:
174162306a36Sopenharmony_ci		strscpy(a->name, "Aux", sizeof(a->name));
174262306a36Sopenharmony_ci		break;
174362306a36Sopenharmony_ci	case EM28XX_AMUX_PCM_OUT:
174462306a36Sopenharmony_ci		strscpy(a->name, "PCM", sizeof(a->name));
174562306a36Sopenharmony_ci		break;
174662306a36Sopenharmony_ci	case EM28XX_AMUX_UNUSED:
174762306a36Sopenharmony_ci	default:
174862306a36Sopenharmony_ci		return -EINVAL;
174962306a36Sopenharmony_ci	}
175062306a36Sopenharmony_ci	a->index = index;
175162306a36Sopenharmony_ci	a->capability = V4L2_AUDCAP_STEREO;
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	em28xx_videodbg("%s: audio input index %d is '%s'\n",
175462306a36Sopenharmony_ci			s, a->index, a->name);
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	return 0;
175762306a36Sopenharmony_ci}
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_cistatic int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	if (a->index >= MAX_EM28XX_INPUT)
176462306a36Sopenharmony_ci		return -EINVAL;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	return em28xx_fill_audio_input(dev, __func__, a, a->index);
176762306a36Sopenharmony_ci}
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_cistatic int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
177062306a36Sopenharmony_ci{
177162306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
177262306a36Sopenharmony_ci	int i;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	for (i = 0; i < MAX_EM28XX_INPUT; i++)
177562306a36Sopenharmony_ci		if (dev->ctl_ainput == dev->amux_map[i])
177662306a36Sopenharmony_ci			return em28xx_fill_audio_input(dev, __func__, a, i);
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	/* Should never happen! */
177962306a36Sopenharmony_ci	return -EINVAL;
178062306a36Sopenharmony_ci}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_cistatic int vidioc_s_audio(struct file *file, void *priv,
178362306a36Sopenharmony_ci			  const struct v4l2_audio *a)
178462306a36Sopenharmony_ci{
178562306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
178662306a36Sopenharmony_ci	int idx, i;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	if (a->index >= MAX_EM28XX_INPUT)
178962306a36Sopenharmony_ci		return -EINVAL;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	idx = dev->amux_map[a->index];
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	if (idx == EM28XX_AMUX_UNUSED)
179462306a36Sopenharmony_ci		return -EINVAL;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	dev->ctl_ainput = idx;
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	/*
179962306a36Sopenharmony_ci	 * FIXME: This is wrong, as different inputs at em28xx_cards
180062306a36Sopenharmony_ci	 * may have different audio outputs. So, the right thing
180162306a36Sopenharmony_ci	 * to do is to implement VIDIOC_G_AUDOUT/VIDIOC_S_AUDOUT.
180262306a36Sopenharmony_ci	 * With the current board definitions, this would work fine,
180362306a36Sopenharmony_ci	 * as, currently, all boards fit.
180462306a36Sopenharmony_ci	 */
180562306a36Sopenharmony_ci	for (i = 0; i < MAX_EM28XX_INPUT; i++)
180662306a36Sopenharmony_ci		if (idx == dev->amux_map[i])
180762306a36Sopenharmony_ci			break;
180862306a36Sopenharmony_ci	if (i == MAX_EM28XX_INPUT)
180962306a36Sopenharmony_ci		return -EINVAL;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	dev->ctl_aoutput = INPUT(i)->aout;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	if (!dev->ctl_aoutput)
181462306a36Sopenharmony_ci		dev->ctl_aoutput = EM28XX_AOUT_MASTER;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	em28xx_videodbg("%s: set audio input to %d\n", __func__,
181762306a36Sopenharmony_ci			dev->ctl_ainput);
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	return 0;
182062306a36Sopenharmony_ci}
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv,
182362306a36Sopenharmony_ci			  struct v4l2_tuner *t)
182462306a36Sopenharmony_ci{
182562306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	if (t->index != 0)
182862306a36Sopenharmony_ci		return -EINVAL;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	strscpy(t->name, "Tuner", sizeof(t->name));
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t);
183362306a36Sopenharmony_ci	return 0;
183462306a36Sopenharmony_ci}
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv,
183762306a36Sopenharmony_ci			  const struct v4l2_tuner *t)
183862306a36Sopenharmony_ci{
183962306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	if (t->index != 0)
184262306a36Sopenharmony_ci		return -EINVAL;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t);
184562306a36Sopenharmony_ci	return 0;
184662306a36Sopenharmony_ci}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv,
184962306a36Sopenharmony_ci			      struct v4l2_frequency *f)
185062306a36Sopenharmony_ci{
185162306a36Sopenharmony_ci	struct em28xx         *dev = video_drvdata(file);
185262306a36Sopenharmony_ci	struct em28xx_v4l2    *v4l2 = dev->v4l2;
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	if (f->tuner != 0)
185562306a36Sopenharmony_ci		return -EINVAL;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	f->frequency = v4l2->frequency;
185862306a36Sopenharmony_ci	return 0;
185962306a36Sopenharmony_ci}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv,
186262306a36Sopenharmony_ci			      const struct v4l2_frequency *f)
186362306a36Sopenharmony_ci{
186462306a36Sopenharmony_ci	struct v4l2_frequency  new_freq = *f;
186562306a36Sopenharmony_ci	struct em28xx             *dev  = video_drvdata(file);
186662306a36Sopenharmony_ci	struct em28xx_v4l2        *v4l2 = dev->v4l2;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	if (f->tuner != 0)
186962306a36Sopenharmony_ci		return -EINVAL;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f);
187262306a36Sopenharmony_ci	v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, g_frequency, &new_freq);
187362306a36Sopenharmony_ci	v4l2->frequency = new_freq.frequency;
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	return 0;
187662306a36Sopenharmony_ci}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
187962306a36Sopenharmony_cistatic int vidioc_g_chip_info(struct file *file, void *priv,
188062306a36Sopenharmony_ci			      struct v4l2_dbg_chip_info *chip)
188162306a36Sopenharmony_ci{
188262306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	if (chip->match.addr > 1)
188562306a36Sopenharmony_ci		return -EINVAL;
188662306a36Sopenharmony_ci	if (chip->match.addr == 1)
188762306a36Sopenharmony_ci		strscpy(chip->name, "ac97", sizeof(chip->name));
188862306a36Sopenharmony_ci	else
188962306a36Sopenharmony_ci		strscpy(chip->name,
189062306a36Sopenharmony_ci			dev->v4l2->v4l2_dev.name, sizeof(chip->name));
189162306a36Sopenharmony_ci	return 0;
189262306a36Sopenharmony_ci}
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_cistatic int em28xx_reg_len(int reg)
189562306a36Sopenharmony_ci{
189662306a36Sopenharmony_ci	switch (reg) {
189762306a36Sopenharmony_ci	case EM28XX_R40_AC97LSB:
189862306a36Sopenharmony_ci	case EM28XX_R30_HSCALELOW:
189962306a36Sopenharmony_ci	case EM28XX_R32_VSCALELOW:
190062306a36Sopenharmony_ci		return 2;
190162306a36Sopenharmony_ci	default:
190262306a36Sopenharmony_ci		return 1;
190362306a36Sopenharmony_ci	}
190462306a36Sopenharmony_ci}
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_cistatic int vidioc_g_register(struct file *file, void *priv,
190762306a36Sopenharmony_ci			     struct v4l2_dbg_register *reg)
190862306a36Sopenharmony_ci{
190962306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
191062306a36Sopenharmony_ci	int ret;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	if (reg->match.addr > 1)
191362306a36Sopenharmony_ci		return -EINVAL;
191462306a36Sopenharmony_ci	if (reg->match.addr) {
191562306a36Sopenharmony_ci		ret = em28xx_read_ac97(dev, reg->reg);
191662306a36Sopenharmony_ci		if (ret < 0)
191762306a36Sopenharmony_ci			return ret;
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci		reg->val = ret;
192062306a36Sopenharmony_ci		reg->size = 1;
192162306a36Sopenharmony_ci		return 0;
192262306a36Sopenharmony_ci	}
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	/* Match host */
192562306a36Sopenharmony_ci	reg->size = em28xx_reg_len(reg->reg);
192662306a36Sopenharmony_ci	if (reg->size == 1) {
192762306a36Sopenharmony_ci		ret = em28xx_read_reg(dev, reg->reg);
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci		if (ret < 0)
193062306a36Sopenharmony_ci			return ret;
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci		reg->val = ret;
193362306a36Sopenharmony_ci	} else {
193462306a36Sopenharmony_ci		__le16 val = 0;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci		ret = dev->em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
193762306a36Sopenharmony_ci						   reg->reg, (char *)&val, 2);
193862306a36Sopenharmony_ci		if (ret < 0)
193962306a36Sopenharmony_ci			return ret;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci		reg->val = le16_to_cpu(val);
194262306a36Sopenharmony_ci	}
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	return 0;
194562306a36Sopenharmony_ci}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_cistatic int vidioc_s_register(struct file *file, void *priv,
194862306a36Sopenharmony_ci			     const struct v4l2_dbg_register *reg)
194962306a36Sopenharmony_ci{
195062306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
195162306a36Sopenharmony_ci	__le16 buf;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	if (reg->match.addr > 1)
195462306a36Sopenharmony_ci		return -EINVAL;
195562306a36Sopenharmony_ci	if (reg->match.addr)
195662306a36Sopenharmony_ci		return em28xx_write_ac97(dev, reg->reg, reg->val);
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	/* Match host */
195962306a36Sopenharmony_ci	buf = cpu_to_le16(reg->val);
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	return em28xx_write_regs(dev, reg->reg, (char *)&buf,
196262306a36Sopenharmony_ci			       em28xx_reg_len(reg->reg));
196362306a36Sopenharmony_ci}
196462306a36Sopenharmony_ci#endif
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, void  *priv,
196762306a36Sopenharmony_ci			   struct v4l2_capability *cap)
196862306a36Sopenharmony_ci{
196962306a36Sopenharmony_ci	struct em28xx         *dev  = video_drvdata(file);
197062306a36Sopenharmony_ci	struct em28xx_v4l2    *v4l2 = dev->v4l2;
197162306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(dev->intf);
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	strscpy(cap->driver, "em28xx", sizeof(cap->driver));
197462306a36Sopenharmony_ci	strscpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
197562306a36Sopenharmony_ci	usb_make_path(udev, cap->bus_info, sizeof(cap->bus_info));
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_READWRITE |
197862306a36Sopenharmony_ci			    V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
197962306a36Sopenharmony_ci	if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE)
198062306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_AUDIO;
198162306a36Sopenharmony_ci	if (dev->tuner_type != TUNER_ABSENT)
198262306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_TUNER;
198362306a36Sopenharmony_ci	if (video_is_registered(&v4l2->vbi_dev))
198462306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
198562306a36Sopenharmony_ci	if (video_is_registered(&v4l2->radio_dev))
198662306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_RADIO;
198762306a36Sopenharmony_ci	return 0;
198862306a36Sopenharmony_ci}
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
199162306a36Sopenharmony_ci				   struct v4l2_fmtdesc *f)
199262306a36Sopenharmony_ci{
199362306a36Sopenharmony_ci	if (unlikely(f->index >= ARRAY_SIZE(format)))
199462306a36Sopenharmony_ci		return -EINVAL;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	f->pixelformat = format[f->index].fourcc;
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	return 0;
199962306a36Sopenharmony_ci}
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_cistatic int vidioc_enum_framesizes(struct file *file, void *priv,
200262306a36Sopenharmony_ci				  struct v4l2_frmsizeenum *fsize)
200362306a36Sopenharmony_ci{
200462306a36Sopenharmony_ci	struct em28xx         *dev = video_drvdata(file);
200562306a36Sopenharmony_ci	struct em28xx_fmt     *fmt;
200662306a36Sopenharmony_ci	unsigned int	      maxw = norm_maxw(dev);
200762306a36Sopenharmony_ci	unsigned int	      maxh = norm_maxh(dev);
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	fmt = format_by_fourcc(fsize->pixel_format);
201062306a36Sopenharmony_ci	if (!fmt) {
201162306a36Sopenharmony_ci		em28xx_videodbg("Fourcc format (%08x) invalid.\n",
201262306a36Sopenharmony_ci				fsize->pixel_format);
201362306a36Sopenharmony_ci		return -EINVAL;
201462306a36Sopenharmony_ci	}
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	if (dev->board.is_em2800) {
201762306a36Sopenharmony_ci		if (fsize->index > 1)
201862306a36Sopenharmony_ci			return -EINVAL;
201962306a36Sopenharmony_ci		fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
202062306a36Sopenharmony_ci		fsize->discrete.width = maxw / (1 + fsize->index);
202162306a36Sopenharmony_ci		fsize->discrete.height = maxh / (1 + fsize->index);
202262306a36Sopenharmony_ci		return 0;
202362306a36Sopenharmony_ci	}
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	if (fsize->index != 0)
202662306a36Sopenharmony_ci		return -EINVAL;
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	/* Report a continuous range */
202962306a36Sopenharmony_ci	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
203062306a36Sopenharmony_ci	scale_to_size(dev, EM28XX_HVSCALE_MAX, EM28XX_HVSCALE_MAX,
203162306a36Sopenharmony_ci		      &fsize->stepwise.min_width, &fsize->stepwise.min_height);
203262306a36Sopenharmony_ci	if (fsize->stepwise.min_width < 48)
203362306a36Sopenharmony_ci		fsize->stepwise.min_width = 48;
203462306a36Sopenharmony_ci	if (fsize->stepwise.min_height < 38)
203562306a36Sopenharmony_ci		fsize->stepwise.min_height = 38;
203662306a36Sopenharmony_ci	fsize->stepwise.max_width = maxw;
203762306a36Sopenharmony_ci	fsize->stepwise.max_height = maxh;
203862306a36Sopenharmony_ci	fsize->stepwise.step_width = 1;
203962306a36Sopenharmony_ci	fsize->stepwise.step_height = 1;
204062306a36Sopenharmony_ci	return 0;
204162306a36Sopenharmony_ci}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci/* RAW VBI ioctls */
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_cistatic int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
204662306a36Sopenharmony_ci				struct v4l2_format *format)
204762306a36Sopenharmony_ci{
204862306a36Sopenharmony_ci	struct em28xx         *dev  = video_drvdata(file);
204962306a36Sopenharmony_ci	struct em28xx_v4l2    *v4l2 = dev->v4l2;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	format->fmt.vbi.samples_per_line = v4l2->vbi_width;
205262306a36Sopenharmony_ci	format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
205362306a36Sopenharmony_ci	format->fmt.vbi.offset = 0;
205462306a36Sopenharmony_ci	format->fmt.vbi.flags = 0;
205562306a36Sopenharmony_ci	format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
205662306a36Sopenharmony_ci	format->fmt.vbi.count[0] = v4l2->vbi_height;
205762306a36Sopenharmony_ci	format->fmt.vbi.count[1] = v4l2->vbi_height;
205862306a36Sopenharmony_ci	memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved));
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	/* Varies by video standard (NTSC, PAL, etc.) */
206162306a36Sopenharmony_ci	if (v4l2->norm & V4L2_STD_525_60) {
206262306a36Sopenharmony_ci		/* NTSC */
206362306a36Sopenharmony_ci		format->fmt.vbi.start[0] = 10;
206462306a36Sopenharmony_ci		format->fmt.vbi.start[1] = 273;
206562306a36Sopenharmony_ci	} else if (v4l2->norm & V4L2_STD_625_50) {
206662306a36Sopenharmony_ci		/* PAL */
206762306a36Sopenharmony_ci		format->fmt.vbi.start[0] = 6;
206862306a36Sopenharmony_ci		format->fmt.vbi.start[1] = 318;
206962306a36Sopenharmony_ci	}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	return 0;
207262306a36Sopenharmony_ci}
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci/*
207562306a36Sopenharmony_ci * RADIO ESPECIFIC IOCTLS
207662306a36Sopenharmony_ci */
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_cistatic int radio_g_tuner(struct file *file, void *priv,
207962306a36Sopenharmony_ci			 struct v4l2_tuner *t)
208062306a36Sopenharmony_ci{
208162306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci	if (unlikely(t->index > 0))
208462306a36Sopenharmony_ci		return -EINVAL;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	strscpy(t->name, "Radio", sizeof(t->name));
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t);
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	return 0;
209162306a36Sopenharmony_ci}
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_cistatic int radio_s_tuner(struct file *file, void *priv,
209462306a36Sopenharmony_ci			 const struct v4l2_tuner *t)
209562306a36Sopenharmony_ci{
209662306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(file);
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	if (t->index != 0)
209962306a36Sopenharmony_ci		return -EINVAL;
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t);
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	return 0;
210462306a36Sopenharmony_ci}
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci/*
210762306a36Sopenharmony_ci * em28xx_free_v4l2() - Free struct em28xx_v4l2
210862306a36Sopenharmony_ci *
210962306a36Sopenharmony_ci * @ref: struct kref for struct em28xx_v4l2
211062306a36Sopenharmony_ci *
211162306a36Sopenharmony_ci * Called when all users of struct em28xx_v4l2 are gone
211262306a36Sopenharmony_ci */
211362306a36Sopenharmony_cistatic void em28xx_free_v4l2(struct kref *ref)
211462306a36Sopenharmony_ci{
211562306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = container_of(ref, struct em28xx_v4l2, ref);
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	v4l2->dev->v4l2 = NULL;
211862306a36Sopenharmony_ci	kfree(v4l2);
211962306a36Sopenharmony_ci}
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci/*
212262306a36Sopenharmony_ci * em28xx_v4l2_open()
212362306a36Sopenharmony_ci * inits the device and starts isoc transfer
212462306a36Sopenharmony_ci */
212562306a36Sopenharmony_cistatic int em28xx_v4l2_open(struct file *filp)
212662306a36Sopenharmony_ci{
212762306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(filp);
212862306a36Sopenharmony_ci	struct em28xx *dev = video_drvdata(filp);
212962306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
213062306a36Sopenharmony_ci	enum v4l2_buf_type fh_type = 0;
213162306a36Sopenharmony_ci	int ret;
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	switch (vdev->vfl_type) {
213462306a36Sopenharmony_ci	case VFL_TYPE_VIDEO:
213562306a36Sopenharmony_ci		fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
213662306a36Sopenharmony_ci		break;
213762306a36Sopenharmony_ci	case VFL_TYPE_VBI:
213862306a36Sopenharmony_ci		fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
213962306a36Sopenharmony_ci		break;
214062306a36Sopenharmony_ci	case VFL_TYPE_RADIO:
214162306a36Sopenharmony_ci		break;
214262306a36Sopenharmony_ci	default:
214362306a36Sopenharmony_ci		return -EINVAL;
214462306a36Sopenharmony_ci	}
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	em28xx_videodbg("open dev=%s type=%s users=%d\n",
214762306a36Sopenharmony_ci			video_device_node_name(vdev), v4l2_type_names[fh_type],
214862306a36Sopenharmony_ci			v4l2->users);
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	if (mutex_lock_interruptible(&dev->lock))
215162306a36Sopenharmony_ci		return -ERESTARTSYS;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci	ret = v4l2_fh_open(filp);
215462306a36Sopenharmony_ci	if (ret) {
215562306a36Sopenharmony_ci		dev_err(&dev->intf->dev,
215662306a36Sopenharmony_ci			"%s: v4l2_fh_open() returned error %d\n",
215762306a36Sopenharmony_ci		       __func__, ret);
215862306a36Sopenharmony_ci		mutex_unlock(&dev->lock);
215962306a36Sopenharmony_ci		return ret;
216062306a36Sopenharmony_ci	}
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	if (v4l2->users == 0) {
216362306a36Sopenharmony_ci		em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci		if (vdev->vfl_type != VFL_TYPE_RADIO)
216662306a36Sopenharmony_ci			em28xx_resolution_set(dev);
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci		/*
216962306a36Sopenharmony_ci		 * Needed, since GPIO might have disabled power
217062306a36Sopenharmony_ci		 * of some i2c devices
217162306a36Sopenharmony_ci		 */
217262306a36Sopenharmony_ci		em28xx_wake_i2c(dev);
217362306a36Sopenharmony_ci	}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	if (vdev->vfl_type == VFL_TYPE_RADIO) {
217662306a36Sopenharmony_ci		em28xx_videodbg("video_open: setting radio device\n");
217762306a36Sopenharmony_ci		v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_radio);
217862306a36Sopenharmony_ci	}
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	kref_get(&dev->ref);
218162306a36Sopenharmony_ci	kref_get(&v4l2->ref);
218262306a36Sopenharmony_ci	v4l2->users++;
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	return 0;
218762306a36Sopenharmony_ci}
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci/*
219062306a36Sopenharmony_ci * em28xx_v4l2_fini()
219162306a36Sopenharmony_ci * unregisters the v4l2,i2c and usb devices
219262306a36Sopenharmony_ci * called when the device gets disconnected or at module unload
219362306a36Sopenharmony_ci */
219462306a36Sopenharmony_cistatic int em28xx_v4l2_fini(struct em28xx *dev)
219562306a36Sopenharmony_ci{
219662306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	if (dev->is_audio_only) {
219962306a36Sopenharmony_ci		/* Shouldn't initialize IR for this interface */
220062306a36Sopenharmony_ci		return 0;
220162306a36Sopenharmony_ci	}
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci	if (!dev->has_video) {
220462306a36Sopenharmony_ci		/* This device does not support the v4l2 extension */
220562306a36Sopenharmony_ci		return 0;
220662306a36Sopenharmony_ci	}
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	if (!v4l2)
220962306a36Sopenharmony_ci		return 0;
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	dev_info(&dev->intf->dev, "Closing video extension\n");
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	mutex_lock(&dev->lock);
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	v4l2_device_disconnect(&v4l2->v4l2_dev);
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE);
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	em28xx_v4l2_media_release(dev);
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	if (video_is_registered(&v4l2->radio_dev)) {
222262306a36Sopenharmony_ci		dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n",
222362306a36Sopenharmony_ci			 video_device_node_name(&v4l2->radio_dev));
222462306a36Sopenharmony_ci		video_unregister_device(&v4l2->radio_dev);
222562306a36Sopenharmony_ci	}
222662306a36Sopenharmony_ci	if (video_is_registered(&v4l2->vbi_dev)) {
222762306a36Sopenharmony_ci		dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n",
222862306a36Sopenharmony_ci			 video_device_node_name(&v4l2->vbi_dev));
222962306a36Sopenharmony_ci		video_unregister_device(&v4l2->vbi_dev);
223062306a36Sopenharmony_ci	}
223162306a36Sopenharmony_ci	if (video_is_registered(&v4l2->vdev)) {
223262306a36Sopenharmony_ci		dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n",
223362306a36Sopenharmony_ci			 video_device_node_name(&v4l2->vdev));
223462306a36Sopenharmony_ci		video_unregister_device(&v4l2->vdev);
223562306a36Sopenharmony_ci	}
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&v4l2->ctrl_handler);
223862306a36Sopenharmony_ci	v4l2_device_unregister(&v4l2->v4l2_dev);
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	kref_put(&v4l2->ref, em28xx_free_v4l2);
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	kref_put(&dev->ref, em28xx_free_device);
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	return 0;
224762306a36Sopenharmony_ci}
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_cistatic int em28xx_v4l2_suspend(struct em28xx *dev)
225062306a36Sopenharmony_ci{
225162306a36Sopenharmony_ci	if (dev->is_audio_only)
225262306a36Sopenharmony_ci		return 0;
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	if (!dev->has_video)
225562306a36Sopenharmony_ci		return 0;
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	dev_info(&dev->intf->dev, "Suspending video extension\n");
225862306a36Sopenharmony_ci	em28xx_stop_urbs(dev);
225962306a36Sopenharmony_ci	return 0;
226062306a36Sopenharmony_ci}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_cistatic int em28xx_v4l2_resume(struct em28xx *dev)
226362306a36Sopenharmony_ci{
226462306a36Sopenharmony_ci	if (dev->is_audio_only)
226562306a36Sopenharmony_ci		return 0;
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	if (!dev->has_video)
226862306a36Sopenharmony_ci		return 0;
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	dev_info(&dev->intf->dev, "Resuming video extension\n");
227162306a36Sopenharmony_ci	/* what do we do here */
227262306a36Sopenharmony_ci	return 0;
227362306a36Sopenharmony_ci}
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci/*
227662306a36Sopenharmony_ci * em28xx_v4l2_close()
227762306a36Sopenharmony_ci * stops streaming and deallocates all resources allocated by the v4l2
227862306a36Sopenharmony_ci * calls and ioctls
227962306a36Sopenharmony_ci */
228062306a36Sopenharmony_cistatic int em28xx_v4l2_close(struct file *filp)
228162306a36Sopenharmony_ci{
228262306a36Sopenharmony_ci	struct em28xx         *dev  = video_drvdata(filp);
228362306a36Sopenharmony_ci	struct em28xx_v4l2    *v4l2 = dev->v4l2;
228462306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(dev->intf);
228562306a36Sopenharmony_ci	int              err;
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci	em28xx_videodbg("users=%d\n", v4l2->users);
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	vb2_fop_release(filp);
229062306a36Sopenharmony_ci	mutex_lock(&dev->lock);
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	if (v4l2->users == 1) {
229362306a36Sopenharmony_ci		/* No sense to try to write to the device */
229462306a36Sopenharmony_ci		if (dev->disconnected)
229562306a36Sopenharmony_ci			goto exit;
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci		/* Save some power by putting tuner to sleep */
229862306a36Sopenharmony_ci		v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, standby);
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci		/* do this before setting alternate! */
230162306a36Sopenharmony_ci		em28xx_set_mode(dev, EM28XX_SUSPEND);
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci		/* set alternate 0 */
230462306a36Sopenharmony_ci		dev->alt = 0;
230562306a36Sopenharmony_ci		em28xx_videodbg("setting alternate 0\n");
230662306a36Sopenharmony_ci		err = usb_set_interface(udev, 0, 0);
230762306a36Sopenharmony_ci		if (err < 0) {
230862306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
230962306a36Sopenharmony_ci				"cannot change alternate number to 0 (error=%i)\n",
231062306a36Sopenharmony_ci				err);
231162306a36Sopenharmony_ci		}
231262306a36Sopenharmony_ci	}
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ciexit:
231562306a36Sopenharmony_ci	v4l2->users--;
231662306a36Sopenharmony_ci	kref_put(&v4l2->ref, em28xx_free_v4l2);
231762306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
231862306a36Sopenharmony_ci	kref_put(&dev->ref, em28xx_free_device);
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	return 0;
232162306a36Sopenharmony_ci}
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_cistatic const struct v4l2_file_operations em28xx_v4l_fops = {
232462306a36Sopenharmony_ci	.owner         = THIS_MODULE,
232562306a36Sopenharmony_ci	.open          = em28xx_v4l2_open,
232662306a36Sopenharmony_ci	.release       = em28xx_v4l2_close,
232762306a36Sopenharmony_ci	.read          = vb2_fop_read,
232862306a36Sopenharmony_ci	.poll          = vb2_fop_poll,
232962306a36Sopenharmony_ci	.mmap          = vb2_fop_mmap,
233062306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
233162306a36Sopenharmony_ci};
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops video_ioctl_ops = {
233462306a36Sopenharmony_ci	.vidioc_querycap            = vidioc_querycap,
233562306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap    = vidioc_enum_fmt_vid_cap,
233662306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap       = vidioc_g_fmt_vid_cap,
233762306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap     = vidioc_try_fmt_vid_cap,
233862306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap       = vidioc_s_fmt_vid_cap,
233962306a36Sopenharmony_ci	.vidioc_g_fmt_vbi_cap       = vidioc_g_fmt_vbi_cap,
234062306a36Sopenharmony_ci	.vidioc_try_fmt_vbi_cap     = vidioc_g_fmt_vbi_cap,
234162306a36Sopenharmony_ci	.vidioc_s_fmt_vbi_cap       = vidioc_g_fmt_vbi_cap,
234262306a36Sopenharmony_ci	.vidioc_enum_framesizes     = vidioc_enum_framesizes,
234362306a36Sopenharmony_ci	.vidioc_enumaudio           = vidioc_enumaudio,
234462306a36Sopenharmony_ci	.vidioc_g_audio             = vidioc_g_audio,
234562306a36Sopenharmony_ci	.vidioc_s_audio             = vidioc_s_audio,
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	.vidioc_reqbufs             = vb2_ioctl_reqbufs,
234862306a36Sopenharmony_ci	.vidioc_create_bufs         = vb2_ioctl_create_bufs,
234962306a36Sopenharmony_ci	.vidioc_prepare_buf         = vb2_ioctl_prepare_buf,
235062306a36Sopenharmony_ci	.vidioc_querybuf            = vb2_ioctl_querybuf,
235162306a36Sopenharmony_ci	.vidioc_qbuf                = vb2_ioctl_qbuf,
235262306a36Sopenharmony_ci	.vidioc_dqbuf               = vb2_ioctl_dqbuf,
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	.vidioc_g_std               = vidioc_g_std,
235562306a36Sopenharmony_ci	.vidioc_querystd            = vidioc_querystd,
235662306a36Sopenharmony_ci	.vidioc_s_std               = vidioc_s_std,
235762306a36Sopenharmony_ci	.vidioc_g_parm		    = vidioc_g_parm,
235862306a36Sopenharmony_ci	.vidioc_s_parm		    = vidioc_s_parm,
235962306a36Sopenharmony_ci	.vidioc_enum_input          = vidioc_enum_input,
236062306a36Sopenharmony_ci	.vidioc_g_input             = vidioc_g_input,
236162306a36Sopenharmony_ci	.vidioc_s_input             = vidioc_s_input,
236262306a36Sopenharmony_ci	.vidioc_streamon            = vb2_ioctl_streamon,
236362306a36Sopenharmony_ci	.vidioc_streamoff           = vb2_ioctl_streamoff,
236462306a36Sopenharmony_ci	.vidioc_g_tuner             = vidioc_g_tuner,
236562306a36Sopenharmony_ci	.vidioc_s_tuner             = vidioc_s_tuner,
236662306a36Sopenharmony_ci	.vidioc_g_frequency         = vidioc_g_frequency,
236762306a36Sopenharmony_ci	.vidioc_s_frequency         = vidioc_s_frequency,
236862306a36Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
236962306a36Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
237062306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
237162306a36Sopenharmony_ci	.vidioc_g_chip_info         = vidioc_g_chip_info,
237262306a36Sopenharmony_ci	.vidioc_g_register          = vidioc_g_register,
237362306a36Sopenharmony_ci	.vidioc_s_register          = vidioc_s_register,
237462306a36Sopenharmony_ci#endif
237562306a36Sopenharmony_ci};
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_cistatic const struct video_device em28xx_video_template = {
237862306a36Sopenharmony_ci	.fops		= &em28xx_v4l_fops,
237962306a36Sopenharmony_ci	.ioctl_ops	= &video_ioctl_ops,
238062306a36Sopenharmony_ci	.release	= video_device_release_empty,
238162306a36Sopenharmony_ci	.tvnorms	= V4L2_STD_ALL,
238262306a36Sopenharmony_ci};
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_cistatic const struct v4l2_file_operations radio_fops = {
238562306a36Sopenharmony_ci	.owner         = THIS_MODULE,
238662306a36Sopenharmony_ci	.open          = em28xx_v4l2_open,
238762306a36Sopenharmony_ci	.release       = em28xx_v4l2_close,
238862306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
238962306a36Sopenharmony_ci};
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops radio_ioctl_ops = {
239262306a36Sopenharmony_ci	.vidioc_querycap      = vidioc_querycap,
239362306a36Sopenharmony_ci	.vidioc_g_tuner       = radio_g_tuner,
239462306a36Sopenharmony_ci	.vidioc_s_tuner       = radio_s_tuner,
239562306a36Sopenharmony_ci	.vidioc_g_frequency   = vidioc_g_frequency,
239662306a36Sopenharmony_ci	.vidioc_s_frequency   = vidioc_s_frequency,
239762306a36Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
239862306a36Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
239962306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
240062306a36Sopenharmony_ci	.vidioc_g_chip_info   = vidioc_g_chip_info,
240162306a36Sopenharmony_ci	.vidioc_g_register    = vidioc_g_register,
240262306a36Sopenharmony_ci	.vidioc_s_register    = vidioc_s_register,
240362306a36Sopenharmony_ci#endif
240462306a36Sopenharmony_ci};
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_cistatic struct video_device em28xx_radio_template = {
240762306a36Sopenharmony_ci	.fops		= &radio_fops,
240862306a36Sopenharmony_ci	.ioctl_ops	= &radio_ioctl_ops,
240962306a36Sopenharmony_ci	.release	= video_device_release_empty,
241062306a36Sopenharmony_ci};
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
241362306a36Sopenharmony_cistatic unsigned short saa711x_addrs[] = {
241462306a36Sopenharmony_ci	0x4a >> 1, 0x48 >> 1,   /* SAA7111, SAA7111A and SAA7113 */
241562306a36Sopenharmony_ci	0x42 >> 1, 0x40 >> 1,   /* SAA7114, SAA7115 and SAA7118 */
241662306a36Sopenharmony_ci	I2C_CLIENT_END };
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_cistatic unsigned short tvp5150_addrs[] = {
241962306a36Sopenharmony_ci	0xb8 >> 1,
242062306a36Sopenharmony_ci	0xba >> 1,
242162306a36Sopenharmony_ci	I2C_CLIENT_END
242262306a36Sopenharmony_ci};
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_cistatic unsigned short msp3400_addrs[] = {
242562306a36Sopenharmony_ci	0x80 >> 1,
242662306a36Sopenharmony_ci	0x88 >> 1,
242762306a36Sopenharmony_ci	I2C_CLIENT_END
242862306a36Sopenharmony_ci};
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci/******************************** usb interface ******************************/
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_cistatic void em28xx_vdev_init(struct em28xx *dev,
243362306a36Sopenharmony_ci			     struct video_device *vfd,
243462306a36Sopenharmony_ci			     const struct video_device *template,
243562306a36Sopenharmony_ci			     const char *type_name)
243662306a36Sopenharmony_ci{
243762306a36Sopenharmony_ci	*vfd		= *template;
243862306a36Sopenharmony_ci	vfd->v4l2_dev	= &dev->v4l2->v4l2_dev;
243962306a36Sopenharmony_ci	vfd->lock	= &dev->lock;
244062306a36Sopenharmony_ci	if (dev->is_webcam)
244162306a36Sopenharmony_ci		vfd->tvnorms = 0;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	snprintf(vfd->name, sizeof(vfd->name), "%s %s",
244462306a36Sopenharmony_ci		 dev_name(&dev->intf->dev), type_name);
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	video_set_drvdata(vfd, dev);
244762306a36Sopenharmony_ci}
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_cistatic void em28xx_tuner_setup(struct em28xx *dev, unsigned short tuner_addr)
245062306a36Sopenharmony_ci{
245162306a36Sopenharmony_ci	struct em28xx_v4l2      *v4l2 = dev->v4l2;
245262306a36Sopenharmony_ci	struct v4l2_device      *v4l2_dev = &v4l2->v4l2_dev;
245362306a36Sopenharmony_ci	struct tuner_setup      tun_setup;
245462306a36Sopenharmony_ci	struct v4l2_frequency   f;
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	memset(&tun_setup, 0, sizeof(tun_setup));
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
245962306a36Sopenharmony_ci	tun_setup.tuner_callback = em28xx_tuner_callback;
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci	if (dev->board.radio.type) {
246262306a36Sopenharmony_ci		tun_setup.type = dev->board.radio.type;
246362306a36Sopenharmony_ci		tun_setup.addr = dev->board.radio_addr;
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci		v4l2_device_call_all(v4l2_dev,
246662306a36Sopenharmony_ci				     0, tuner, s_type_addr, &tun_setup);
246762306a36Sopenharmony_ci	}
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci	if (dev->tuner_type != TUNER_ABSENT && dev->tuner_type) {
247062306a36Sopenharmony_ci		tun_setup.type   = dev->tuner_type;
247162306a36Sopenharmony_ci		tun_setup.addr   = tuner_addr;
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci		v4l2_device_call_all(v4l2_dev,
247462306a36Sopenharmony_ci				     0, tuner, s_type_addr, &tun_setup);
247562306a36Sopenharmony_ci	}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	if (dev->board.tda9887_conf) {
247862306a36Sopenharmony_ci		struct v4l2_priv_tun_config tda9887_cfg;
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci		tda9887_cfg.tuner = TUNER_TDA9887;
248162306a36Sopenharmony_ci		tda9887_cfg.priv = &dev->board.tda9887_conf;
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci		v4l2_device_call_all(v4l2_dev,
248462306a36Sopenharmony_ci				     0, tuner, s_config, &tda9887_cfg);
248562306a36Sopenharmony_ci	}
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci	if (dev->tuner_type == TUNER_XC2028) {
248862306a36Sopenharmony_ci		struct v4l2_priv_tun_config  xc2028_cfg;
248962306a36Sopenharmony_ci		struct xc2028_ctrl           ctl;
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci		memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
249262306a36Sopenharmony_ci		memset(&ctl, 0, sizeof(ctl));
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci		em28xx_setup_xc3028(dev, &ctl);
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_ci		xc2028_cfg.tuner = TUNER_XC2028;
249762306a36Sopenharmony_ci		xc2028_cfg.priv  = &ctl;
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci		v4l2_device_call_all(v4l2_dev, 0, tuner, s_config, &xc2028_cfg);
250062306a36Sopenharmony_ci	}
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci	/* configure tuner */
250362306a36Sopenharmony_ci	f.tuner = 0;
250462306a36Sopenharmony_ci	f.type = V4L2_TUNER_ANALOG_TV;
250562306a36Sopenharmony_ci	f.frequency = 9076;     /* just a magic number */
250662306a36Sopenharmony_ci	v4l2->frequency = f.frequency;
250762306a36Sopenharmony_ci	v4l2_device_call_all(v4l2_dev, 0, tuner, s_frequency, &f);
250862306a36Sopenharmony_ci}
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_cistatic int em28xx_v4l2_init(struct em28xx *dev)
251162306a36Sopenharmony_ci{
251262306a36Sopenharmony_ci	u8 val;
251362306a36Sopenharmony_ci	int ret;
251462306a36Sopenharmony_ci	unsigned int maxw;
251562306a36Sopenharmony_ci	struct v4l2_ctrl_handler *hdl;
251662306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2;
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci	if (dev->is_audio_only) {
251962306a36Sopenharmony_ci		/* Shouldn't initialize IR for this interface */
252062306a36Sopenharmony_ci		return 0;
252162306a36Sopenharmony_ci	}
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci	if (!dev->has_video) {
252462306a36Sopenharmony_ci		/* This device does not support the v4l2 extension */
252562306a36Sopenharmony_ci		return 0;
252662306a36Sopenharmony_ci	}
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	dev_info(&dev->intf->dev, "Registering V4L2 extension\n");
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	mutex_lock(&dev->lock);
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	v4l2 = kzalloc(sizeof(*v4l2), GFP_KERNEL);
253362306a36Sopenharmony_ci	if (!v4l2) {
253462306a36Sopenharmony_ci		mutex_unlock(&dev->lock);
253562306a36Sopenharmony_ci		return -ENOMEM;
253662306a36Sopenharmony_ci	}
253762306a36Sopenharmony_ci	kref_init(&v4l2->ref);
253862306a36Sopenharmony_ci	v4l2->dev = dev;
253962306a36Sopenharmony_ci	dev->v4l2 = v4l2;
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
254262306a36Sopenharmony_ci	v4l2->v4l2_dev.mdev = dev->media_dev;
254362306a36Sopenharmony_ci#endif
254462306a36Sopenharmony_ci	ret = v4l2_device_register(&dev->intf->dev, &v4l2->v4l2_dev);
254562306a36Sopenharmony_ci	if (ret < 0) {
254662306a36Sopenharmony_ci		dev_err(&dev->intf->dev,
254762306a36Sopenharmony_ci			"Call to v4l2_device_register() failed!\n");
254862306a36Sopenharmony_ci		goto err;
254962306a36Sopenharmony_ci	}
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	hdl = &v4l2->ctrl_handler;
255262306a36Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 8);
255362306a36Sopenharmony_ci	v4l2->v4l2_dev.ctrl_handler = hdl;
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci	if (dev->is_webcam)
255662306a36Sopenharmony_ci		v4l2->progressive = true;
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	/*
255962306a36Sopenharmony_ci	 * Default format, used for tvp5150 or saa711x output formats
256062306a36Sopenharmony_ci	 */
256162306a36Sopenharmony_ci	v4l2->vinmode = EM28XX_VINMODE_YUV422_CbYCrY;
256262306a36Sopenharmony_ci	v4l2->vinctl  = EM28XX_VINCTRL_INTERLACED |
256362306a36Sopenharmony_ci			EM28XX_VINCTRL_CCIR656_ENABLE;
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	/* request some modules */
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci	if (dev->has_msp34xx)
256862306a36Sopenharmony_ci		v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
256962306a36Sopenharmony_ci				    &dev->i2c_adap[dev->def_i2c_bus],
257062306a36Sopenharmony_ci				    "msp3400", 0, msp3400_addrs);
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	if (dev->board.decoder == EM28XX_SAA711X)
257362306a36Sopenharmony_ci		v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
257462306a36Sopenharmony_ci				    &dev->i2c_adap[dev->def_i2c_bus],
257562306a36Sopenharmony_ci				    "saa7115_auto", 0, saa711x_addrs);
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	if (dev->board.decoder == EM28XX_TVP5150)
257862306a36Sopenharmony_ci		v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
257962306a36Sopenharmony_ci				    &dev->i2c_adap[dev->def_i2c_bus],
258062306a36Sopenharmony_ci				    "tvp5150", 0, tvp5150_addrs);
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	if (dev->board.adecoder == EM28XX_TVAUDIO)
258362306a36Sopenharmony_ci		v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
258462306a36Sopenharmony_ci				    &dev->i2c_adap[dev->def_i2c_bus],
258562306a36Sopenharmony_ci				    "tvaudio", dev->board.tvaudio_addr, NULL);
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci	/* Initialize tuner and camera */
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	if (dev->board.tuner_type != TUNER_ABSENT) {
259062306a36Sopenharmony_ci		unsigned short tuner_addr = dev->board.tuner_addr;
259162306a36Sopenharmony_ci		int has_demod = (dev->board.tda9887_conf & TDA9887_PRESENT);
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci		if (dev->board.radio.type)
259462306a36Sopenharmony_ci			v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
259562306a36Sopenharmony_ci					    &dev->i2c_adap[dev->def_i2c_bus],
259662306a36Sopenharmony_ci					    "tuner", dev->board.radio_addr,
259762306a36Sopenharmony_ci					    NULL);
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci		if (has_demod)
260062306a36Sopenharmony_ci			v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
260162306a36Sopenharmony_ci					    &dev->i2c_adap[dev->def_i2c_bus],
260262306a36Sopenharmony_ci					    "tuner", 0,
260362306a36Sopenharmony_ci					    v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
260462306a36Sopenharmony_ci		if (tuner_addr == 0) {
260562306a36Sopenharmony_ci			enum v4l2_i2c_tuner_type type =
260662306a36Sopenharmony_ci				has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
260762306a36Sopenharmony_ci			struct v4l2_subdev *sd;
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci			sd = v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
261062306a36Sopenharmony_ci						 &dev->i2c_adap[dev->def_i2c_bus],
261162306a36Sopenharmony_ci						 "tuner", 0,
261262306a36Sopenharmony_ci						 v4l2_i2c_tuner_addrs(type));
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_ci			if (sd)
261562306a36Sopenharmony_ci				tuner_addr = v4l2_i2c_subdev_addr(sd);
261662306a36Sopenharmony_ci		} else {
261762306a36Sopenharmony_ci			v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
261862306a36Sopenharmony_ci					    &dev->i2c_adap[dev->def_i2c_bus],
261962306a36Sopenharmony_ci					    "tuner", tuner_addr, NULL);
262062306a36Sopenharmony_ci		}
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci		em28xx_tuner_setup(dev, tuner_addr);
262362306a36Sopenharmony_ci	}
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	if (dev->em28xx_sensor != EM28XX_NOSENSOR)
262662306a36Sopenharmony_ci		em28xx_init_camera(dev);
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	/* Configure audio */
262962306a36Sopenharmony_ci	ret = em28xx_audio_setup(dev);
263062306a36Sopenharmony_ci	if (ret < 0) {
263162306a36Sopenharmony_ci		dev_err(&dev->intf->dev,
263262306a36Sopenharmony_ci			"%s: Error while setting audio - error [%d]!\n",
263362306a36Sopenharmony_ci			__func__, ret);
263462306a36Sopenharmony_ci		goto unregister_dev;
263562306a36Sopenharmony_ci	}
263662306a36Sopenharmony_ci	if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
263762306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
263862306a36Sopenharmony_ci				  V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
263962306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
264062306a36Sopenharmony_ci				  V4L2_CID_AUDIO_VOLUME, 0, 0x1f, 1, 0x1f);
264162306a36Sopenharmony_ci	} else {
264262306a36Sopenharmony_ci		/* install the em28xx notify callback */
264362306a36Sopenharmony_ci		v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_MUTE),
264462306a36Sopenharmony_ci				 em28xx_ctrl_notify, dev);
264562306a36Sopenharmony_ci		v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_VOLUME),
264662306a36Sopenharmony_ci				 em28xx_ctrl_notify, dev);
264762306a36Sopenharmony_ci	}
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci	/* wake i2c devices */
265062306a36Sopenharmony_ci	em28xx_wake_i2c(dev);
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci	/* init video dma queues */
265362306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->vidq.active);
265462306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->vbiq.active);
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci	if (dev->has_msp34xx) {
265762306a36Sopenharmony_ci		/* Send a reset to other chips via gpio */
265862306a36Sopenharmony_ci		ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7);
265962306a36Sopenharmony_ci		if (ret < 0) {
266062306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
266162306a36Sopenharmony_ci				"%s: em28xx_write_reg - msp34xx(1) failed! error [%d]\n",
266262306a36Sopenharmony_ci				__func__, ret);
266362306a36Sopenharmony_ci			goto unregister_dev;
266462306a36Sopenharmony_ci		}
266562306a36Sopenharmony_ci		usleep_range(10000, 11000);
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci		ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff);
266862306a36Sopenharmony_ci		if (ret < 0) {
266962306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
267062306a36Sopenharmony_ci				"%s: em28xx_write_reg - msp34xx(2) failed! error [%d]\n",
267162306a36Sopenharmony_ci				__func__, ret);
267262306a36Sopenharmony_ci			goto unregister_dev;
267362306a36Sopenharmony_ci		}
267462306a36Sopenharmony_ci		usleep_range(10000, 11000);
267562306a36Sopenharmony_ci	}
267662306a36Sopenharmony_ci
267762306a36Sopenharmony_ci	/* set default norm */
267862306a36Sopenharmony_ci	v4l2->norm = V4L2_STD_PAL;
267962306a36Sopenharmony_ci	v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm);
268062306a36Sopenharmony_ci	v4l2->interlaced_fieldmode = EM28XX_INTERLACED_DEFAULT;
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	/* Analog specific initialization */
268362306a36Sopenharmony_ci	v4l2->format = &format[0];
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	maxw = norm_maxw(dev);
268662306a36Sopenharmony_ci	/*
268762306a36Sopenharmony_ci	 * MaxPacketSize for em2800 is too small to capture at full resolution
268862306a36Sopenharmony_ci	 * use half of maxw as the scaler can only scale to 50%
268962306a36Sopenharmony_ci	 */
269062306a36Sopenharmony_ci	if (dev->board.is_em2800)
269162306a36Sopenharmony_ci		maxw /= 2;
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci	em28xx_set_video_format(dev, format[0].fourcc,
269462306a36Sopenharmony_ci				maxw, norm_maxh(dev));
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	video_mux(dev, 0);
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci	/* Audio defaults */
269962306a36Sopenharmony_ci	dev->mute = 1;
270062306a36Sopenharmony_ci	dev->volume = 0x1f;
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci/*	em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
270362306a36Sopenharmony_ci	val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK);
270462306a36Sopenharmony_ci	em28xx_write_reg(dev, EM28XX_R0F_XCLK,
270562306a36Sopenharmony_ci			 (EM28XX_XCLK_AUDIO_UNMUTE | val));
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	em28xx_set_outfmt(dev);
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci	/* Add image controls */
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci	/*
271262306a36Sopenharmony_ci	 * NOTE: at this point, the subdevices are already registered, so
271362306a36Sopenharmony_ci	 * bridge controls are only added/enabled when no subdevice provides
271462306a36Sopenharmony_ci	 * them
271562306a36Sopenharmony_ci	 */
271662306a36Sopenharmony_ci	if (!v4l2_ctrl_find(hdl, V4L2_CID_CONTRAST))
271762306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
271862306a36Sopenharmony_ci				  V4L2_CID_CONTRAST,
271962306a36Sopenharmony_ci				  0, 0x1f, 1, CONTRAST_DEFAULT);
272062306a36Sopenharmony_ci	if (!v4l2_ctrl_find(hdl, V4L2_CID_BRIGHTNESS))
272162306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
272262306a36Sopenharmony_ci				  V4L2_CID_BRIGHTNESS,
272362306a36Sopenharmony_ci				  -0x80, 0x7f, 1, BRIGHTNESS_DEFAULT);
272462306a36Sopenharmony_ci	if (!v4l2_ctrl_find(hdl, V4L2_CID_SATURATION))
272562306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
272662306a36Sopenharmony_ci				  V4L2_CID_SATURATION,
272762306a36Sopenharmony_ci				  0, 0x1f, 1, SATURATION_DEFAULT);
272862306a36Sopenharmony_ci	if (!v4l2_ctrl_find(hdl, V4L2_CID_BLUE_BALANCE))
272962306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
273062306a36Sopenharmony_ci				  V4L2_CID_BLUE_BALANCE,
273162306a36Sopenharmony_ci				  -0x30, 0x30, 1, BLUE_BALANCE_DEFAULT);
273262306a36Sopenharmony_ci	if (!v4l2_ctrl_find(hdl, V4L2_CID_RED_BALANCE))
273362306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
273462306a36Sopenharmony_ci				  V4L2_CID_RED_BALANCE,
273562306a36Sopenharmony_ci				  -0x30, 0x30, 1, RED_BALANCE_DEFAULT);
273662306a36Sopenharmony_ci	if (!v4l2_ctrl_find(hdl, V4L2_CID_SHARPNESS))
273762306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
273862306a36Sopenharmony_ci				  V4L2_CID_SHARPNESS,
273962306a36Sopenharmony_ci				  0, 0x0f, 1, SHARPNESS_DEFAULT);
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	/* Reset image controls */
274262306a36Sopenharmony_ci	em28xx_colorlevels_set_default(dev);
274362306a36Sopenharmony_ci	v4l2_ctrl_handler_setup(hdl);
274462306a36Sopenharmony_ci	ret = hdl->error;
274562306a36Sopenharmony_ci	if (ret)
274662306a36Sopenharmony_ci		goto unregister_dev;
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci	/* allocate and fill video video_device struct */
274962306a36Sopenharmony_ci	em28xx_vdev_init(dev, &v4l2->vdev, &em28xx_video_template, "video");
275062306a36Sopenharmony_ci	mutex_init(&v4l2->vb_queue_lock);
275162306a36Sopenharmony_ci	mutex_init(&v4l2->vb_vbi_queue_lock);
275262306a36Sopenharmony_ci	v4l2->vdev.queue = &v4l2->vb_vidq;
275362306a36Sopenharmony_ci	v4l2->vdev.queue->lock = &v4l2->vb_queue_lock;
275462306a36Sopenharmony_ci	v4l2->vdev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE |
275562306a36Sopenharmony_ci				 V4L2_CAP_STREAMING;
275662306a36Sopenharmony_ci	if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE)
275762306a36Sopenharmony_ci		v4l2->vdev.device_caps |= V4L2_CAP_AUDIO;
275862306a36Sopenharmony_ci	if (dev->tuner_type != TUNER_ABSENT)
275962306a36Sopenharmony_ci		v4l2->vdev.device_caps |= V4L2_CAP_TUNER;
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	/* disable inapplicable ioctls */
276362306a36Sopenharmony_ci	if (dev->is_webcam) {
276462306a36Sopenharmony_ci		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_QUERYSTD);
276562306a36Sopenharmony_ci		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_STD);
276662306a36Sopenharmony_ci		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_STD);
276762306a36Sopenharmony_ci	} else {
276862306a36Sopenharmony_ci		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_PARM);
276962306a36Sopenharmony_ci	}
277062306a36Sopenharmony_ci	if (dev->tuner_type == TUNER_ABSENT) {
277162306a36Sopenharmony_ci		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_TUNER);
277262306a36Sopenharmony_ci		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_TUNER);
277362306a36Sopenharmony_ci		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_FREQUENCY);
277462306a36Sopenharmony_ci		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_FREQUENCY);
277562306a36Sopenharmony_ci	}
277662306a36Sopenharmony_ci	if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) {
277762306a36Sopenharmony_ci		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_AUDIO);
277862306a36Sopenharmony_ci		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_AUDIO);
277962306a36Sopenharmony_ci	}
278062306a36Sopenharmony_ci
278162306a36Sopenharmony_ci	/* register v4l2 video video_device */
278262306a36Sopenharmony_ci	ret = video_register_device(&v4l2->vdev, VFL_TYPE_VIDEO,
278362306a36Sopenharmony_ci				    video_nr[dev->devno]);
278462306a36Sopenharmony_ci	if (ret) {
278562306a36Sopenharmony_ci		dev_err(&dev->intf->dev,
278662306a36Sopenharmony_ci			"unable to register video device (error=%i).\n", ret);
278762306a36Sopenharmony_ci		goto unregister_dev;
278862306a36Sopenharmony_ci	}
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	/* Allocate and fill vbi video_device struct */
279162306a36Sopenharmony_ci	if (em28xx_vbi_supported(dev) == 1) {
279262306a36Sopenharmony_ci		em28xx_vdev_init(dev, &v4l2->vbi_dev, &em28xx_video_template,
279362306a36Sopenharmony_ci				 "vbi");
279462306a36Sopenharmony_ci
279562306a36Sopenharmony_ci		v4l2->vbi_dev.queue = &v4l2->vb_vbiq;
279662306a36Sopenharmony_ci		v4l2->vbi_dev.queue->lock = &v4l2->vb_vbi_queue_lock;
279762306a36Sopenharmony_ci		v4l2->vbi_dev.device_caps = V4L2_CAP_STREAMING |
279862306a36Sopenharmony_ci			V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE;
279962306a36Sopenharmony_ci		if (dev->tuner_type != TUNER_ABSENT)
280062306a36Sopenharmony_ci			v4l2->vbi_dev.device_caps |= V4L2_CAP_TUNER;
280162306a36Sopenharmony_ci
280262306a36Sopenharmony_ci		/* disable inapplicable ioctls */
280362306a36Sopenharmony_ci		v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_PARM);
280462306a36Sopenharmony_ci		if (dev->tuner_type == TUNER_ABSENT) {
280562306a36Sopenharmony_ci			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_TUNER);
280662306a36Sopenharmony_ci			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_TUNER);
280762306a36Sopenharmony_ci			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_FREQUENCY);
280862306a36Sopenharmony_ci			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_FREQUENCY);
280962306a36Sopenharmony_ci		}
281062306a36Sopenharmony_ci		if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) {
281162306a36Sopenharmony_ci			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_AUDIO);
281262306a36Sopenharmony_ci			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_AUDIO);
281362306a36Sopenharmony_ci		}
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_ci		/* register v4l2 vbi video_device */
281662306a36Sopenharmony_ci		ret = video_register_device(&v4l2->vbi_dev, VFL_TYPE_VBI,
281762306a36Sopenharmony_ci					    vbi_nr[dev->devno]);
281862306a36Sopenharmony_ci		if (ret < 0) {
281962306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
282062306a36Sopenharmony_ci				"unable to register vbi device\n");
282162306a36Sopenharmony_ci			goto unregister_dev;
282262306a36Sopenharmony_ci		}
282362306a36Sopenharmony_ci	}
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
282662306a36Sopenharmony_ci		em28xx_vdev_init(dev, &v4l2->radio_dev, &em28xx_radio_template,
282762306a36Sopenharmony_ci				 "radio");
282862306a36Sopenharmony_ci		v4l2->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
282962306a36Sopenharmony_ci		ret = video_register_device(&v4l2->radio_dev, VFL_TYPE_RADIO,
283062306a36Sopenharmony_ci					    radio_nr[dev->devno]);
283162306a36Sopenharmony_ci		if (ret < 0) {
283262306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
283362306a36Sopenharmony_ci				"can't register radio device\n");
283462306a36Sopenharmony_ci			goto unregister_dev;
283562306a36Sopenharmony_ci		}
283662306a36Sopenharmony_ci		dev_info(&dev->intf->dev,
283762306a36Sopenharmony_ci			 "Registered radio device as %s\n",
283862306a36Sopenharmony_ci			 video_device_node_name(&v4l2->radio_dev));
283962306a36Sopenharmony_ci	}
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	/* Init entities at the Media Controller */
284262306a36Sopenharmony_ci	em28xx_v4l2_create_entities(dev);
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
284562306a36Sopenharmony_ci	ret = v4l2_mc_create_media_graph(dev->media_dev);
284662306a36Sopenharmony_ci	if (ret) {
284762306a36Sopenharmony_ci		dev_err(&dev->intf->dev,
284862306a36Sopenharmony_ci			"failed to create media graph\n");
284962306a36Sopenharmony_ci		em28xx_v4l2_media_release(dev);
285062306a36Sopenharmony_ci		goto unregister_dev;
285162306a36Sopenharmony_ci	}
285262306a36Sopenharmony_ci#endif
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci	dev_info(&dev->intf->dev,
285562306a36Sopenharmony_ci		 "V4L2 video device registered as %s\n",
285662306a36Sopenharmony_ci		 video_device_node_name(&v4l2->vdev));
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci	if (video_is_registered(&v4l2->vbi_dev))
285962306a36Sopenharmony_ci		dev_info(&dev->intf->dev,
286062306a36Sopenharmony_ci			 "V4L2 VBI device registered as %s\n",
286162306a36Sopenharmony_ci			 video_device_node_name(&v4l2->vbi_dev));
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci	/* Save some power by putting tuner to sleep */
286462306a36Sopenharmony_ci	v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, standby);
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_ci	/* initialize videobuf2 stuff */
286762306a36Sopenharmony_ci	em28xx_vb2_setup(dev);
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	dev_info(&dev->intf->dev,
287062306a36Sopenharmony_ci		 "V4L2 extension successfully initialized\n");
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci	kref_get(&dev->ref);
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
287562306a36Sopenharmony_ci	return 0;
287662306a36Sopenharmony_ci
287762306a36Sopenharmony_ciunregister_dev:
287862306a36Sopenharmony_ci	if (video_is_registered(&v4l2->radio_dev)) {
287962306a36Sopenharmony_ci		dev_info(&dev->intf->dev,
288062306a36Sopenharmony_ci			 "V4L2 device %s deregistered\n",
288162306a36Sopenharmony_ci			 video_device_node_name(&v4l2->radio_dev));
288262306a36Sopenharmony_ci		video_unregister_device(&v4l2->radio_dev);
288362306a36Sopenharmony_ci	}
288462306a36Sopenharmony_ci	if (video_is_registered(&v4l2->vbi_dev)) {
288562306a36Sopenharmony_ci		dev_info(&dev->intf->dev,
288662306a36Sopenharmony_ci			 "V4L2 device %s deregistered\n",
288762306a36Sopenharmony_ci			 video_device_node_name(&v4l2->vbi_dev));
288862306a36Sopenharmony_ci		video_unregister_device(&v4l2->vbi_dev);
288962306a36Sopenharmony_ci	}
289062306a36Sopenharmony_ci	if (video_is_registered(&v4l2->vdev)) {
289162306a36Sopenharmony_ci		dev_info(&dev->intf->dev,
289262306a36Sopenharmony_ci			 "V4L2 device %s deregistered\n",
289362306a36Sopenharmony_ci			 video_device_node_name(&v4l2->vdev));
289462306a36Sopenharmony_ci		video_unregister_device(&v4l2->vdev);
289562306a36Sopenharmony_ci	}
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&v4l2->ctrl_handler);
289862306a36Sopenharmony_ci	v4l2_device_unregister(&v4l2->v4l2_dev);
289962306a36Sopenharmony_cierr:
290062306a36Sopenharmony_ci	dev->v4l2 = NULL;
290162306a36Sopenharmony_ci	kref_put(&v4l2->ref, em28xx_free_v4l2);
290262306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
290362306a36Sopenharmony_ci	return ret;
290462306a36Sopenharmony_ci}
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_cistatic struct em28xx_ops v4l2_ops = {
290762306a36Sopenharmony_ci	.id   = EM28XX_V4L2,
290862306a36Sopenharmony_ci	.name = "Em28xx v4l2 Extension",
290962306a36Sopenharmony_ci	.init = em28xx_v4l2_init,
291062306a36Sopenharmony_ci	.fini = em28xx_v4l2_fini,
291162306a36Sopenharmony_ci	.suspend = em28xx_v4l2_suspend,
291262306a36Sopenharmony_ci	.resume = em28xx_v4l2_resume,
291362306a36Sopenharmony_ci};
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_cistatic int __init em28xx_video_register(void)
291662306a36Sopenharmony_ci{
291762306a36Sopenharmony_ci	return em28xx_register_extension(&v4l2_ops);
291862306a36Sopenharmony_ci}
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_cistatic void __exit em28xx_video_unregister(void)
292162306a36Sopenharmony_ci{
292262306a36Sopenharmony_ci	em28xx_unregister_extension(&v4l2_ops);
292362306a36Sopenharmony_ci}
292462306a36Sopenharmony_ci
292562306a36Sopenharmony_cimodule_init(em28xx_video_register);
292662306a36Sopenharmony_cimodule_exit(em28xx_video_unregister);
2927