162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Driver for the Conexant CX23885 PCIe bridge
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "cx23885.h"
962306a36Sopenharmony_ci#include "cx23885-video.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/list.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/moduleparam.h>
1562306a36Sopenharmony_ci#include <linux/kmod.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/interrupt.h>
1962306a36Sopenharmony_ci#include <linux/delay.h>
2062306a36Sopenharmony_ci#include <linux/kthread.h>
2162306a36Sopenharmony_ci#include <asm/div64.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <media/v4l2-common.h>
2462306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2562306a36Sopenharmony_ci#include <media/v4l2-event.h>
2662306a36Sopenharmony_ci#include "cx23885-ioctl.h"
2762306a36Sopenharmony_ci#include "xc2028.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <media/drv-intf/cx25840.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciMODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
3262306a36Sopenharmony_ciMODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
3362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic unsigned int video_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
3862306a36Sopenharmony_cistatic unsigned int vbi_nr[]   = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cimodule_param_array(video_nr, int, NULL, 0444);
4162306a36Sopenharmony_cimodule_param_array(vbi_nr,   int, NULL, 0444);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ciMODULE_PARM_DESC(video_nr, "video device numbers");
4462306a36Sopenharmony_ciMODULE_PARM_DESC(vbi_nr, "vbi device numbers");
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic unsigned int video_debug;
4762306a36Sopenharmony_cimodule_param(video_debug, int, 0644);
4862306a36Sopenharmony_ciMODULE_PARM_DESC(video_debug, "enable debug messages [video]");
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic unsigned int irq_debug;
5162306a36Sopenharmony_cimodule_param(irq_debug, int, 0644);
5262306a36Sopenharmony_ciMODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic unsigned int vid_limit = 16;
5562306a36Sopenharmony_cimodule_param(vid_limit, int, 0644);
5662306a36Sopenharmony_ciMODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define dprintk(level, fmt, arg...)\
5962306a36Sopenharmony_ci	do { if (video_debug >= level)\
6062306a36Sopenharmony_ci		printk(KERN_DEBUG pr_fmt("%s: video:" fmt), \
6162306a36Sopenharmony_ci			__func__, ##arg); \
6262306a36Sopenharmony_ci	} while (0)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* ------------------------------------------------------------------- */
6562306a36Sopenharmony_ci/* static data                                                         */
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define FORMAT_FLAGS_PACKED       0x01
6862306a36Sopenharmony_cistatic struct cx23885_fmt formats[] = {
6962306a36Sopenharmony_ci	{
7062306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YUYV,
7162306a36Sopenharmony_ci		.depth    = 16,
7262306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	unsigned int i;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(formats); i++)
8162306a36Sopenharmony_ci		if (formats[i].fourcc == fourcc)
8262306a36Sopenharmony_ci			return formats+i;
8362306a36Sopenharmony_ci	return NULL;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/* ------------------------------------------------------------------- */
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_civoid cx23885_video_wakeup(struct cx23885_dev *dev,
8962306a36Sopenharmony_ci	struct cx23885_dmaqueue *q, u32 count)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct cx23885_buffer *buf;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (list_empty(&q->active))
9462306a36Sopenharmony_ci		return;
9562306a36Sopenharmony_ci	buf = list_entry(q->active.next,
9662306a36Sopenharmony_ci			struct cx23885_buffer, queue);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	buf->vb.sequence = q->count++;
9962306a36Sopenharmony_ci	buf->vb.vb2_buf.timestamp = ktime_get_ns();
10062306a36Sopenharmony_ci	dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf,
10162306a36Sopenharmony_ci			buf->vb.vb2_buf.index, count, q->count);
10262306a36Sopenharmony_ci	list_del(&buf->queue);
10362306a36Sopenharmony_ci	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciint cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct v4l2_subdev_format format = {
10962306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
11062306a36Sopenharmony_ci		.format.code = MEDIA_BUS_FMT_FIXED,
11162306a36Sopenharmony_ci	};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
11462306a36Sopenharmony_ci		__func__,
11562306a36Sopenharmony_ci		(unsigned int)norm,
11662306a36Sopenharmony_ci		v4l2_norm_to_name(norm));
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (dev->tvnorm == norm)
11962306a36Sopenharmony_ci		return 0;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (dev->tvnorm != norm) {
12262306a36Sopenharmony_ci		if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq) ||
12362306a36Sopenharmony_ci		    vb2_is_busy(&dev->vb2_mpegq))
12462306a36Sopenharmony_ci			return -EBUSY;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	dev->tvnorm = norm;
12862306a36Sopenharmony_ci	dev->width = 720;
12962306a36Sopenharmony_ci	dev->height = norm_maxh(norm);
13062306a36Sopenharmony_ci	dev->field = V4L2_FIELD_INTERLACED;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	call_all(dev, video, s_std, norm);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	format.format.width = dev->width;
13562306a36Sopenharmony_ci	format.format.height = dev->height;
13662306a36Sopenharmony_ci	format.format.field = dev->field;
13762306a36Sopenharmony_ci	call_all(dev, pad, set_fmt, NULL, &format);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return 0;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
14362306a36Sopenharmony_ci				    struct pci_dev *pci,
14462306a36Sopenharmony_ci				    struct video_device *template,
14562306a36Sopenharmony_ci				    char *type)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct video_device *vfd;
14862306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	vfd = video_device_alloc();
15162306a36Sopenharmony_ci	if (NULL == vfd)
15262306a36Sopenharmony_ci		return NULL;
15362306a36Sopenharmony_ci	*vfd = *template;
15462306a36Sopenharmony_ci	vfd->v4l2_dev = &dev->v4l2_dev;
15562306a36Sopenharmony_ci	vfd->release = video_device_release;
15662306a36Sopenharmony_ci	vfd->lock = &dev->lock;
15762306a36Sopenharmony_ci	snprintf(vfd->name, sizeof(vfd->name), "%s (%s)",
15862306a36Sopenharmony_ci		 cx23885_boards[dev->board].name, type);
15962306a36Sopenharmony_ci	video_set_drvdata(vfd, dev);
16062306a36Sopenharmony_ci	return vfd;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ciint cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	/* 8 bit registers, 8 bit values */
16662306a36Sopenharmony_ci	u8 buf[] = { reg, data };
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	struct i2c_msg msg = { .addr = 0x98 >> 1,
16962306a36Sopenharmony_ci		.flags = 0, .buf = buf, .len = 2 };
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg, 1);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciu8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	/* 8 bit registers, 8 bit values */
17762306a36Sopenharmony_ci	int ret;
17862306a36Sopenharmony_ci	u8 b0[] = { reg };
17962306a36Sopenharmony_ci	u8 b1[] = { 0 };
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	struct i2c_msg msg[] = {
18262306a36Sopenharmony_ci		{ .addr = 0x98 >> 1, .flags = 0, .buf = b0, .len = 1 },
18362306a36Sopenharmony_ci		{ .addr = 0x98 >> 1, .flags = I2C_M_RD, .buf = b1, .len = 1 }
18462306a36Sopenharmony_ci	};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	ret = i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg[0], 2);
18762306a36Sopenharmony_ci	if (ret != 2)
18862306a36Sopenharmony_ci		pr_err("%s() error\n", __func__);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	return b1[0];
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void cx23885_flatiron_dump(struct cx23885_dev *dev)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	int i;
19662306a36Sopenharmony_ci	dprintk(1, "Flatiron dump\n");
19762306a36Sopenharmony_ci	for (i = 0; i < 0x24; i++) {
19862306a36Sopenharmony_ci		dprintk(1, "FI[%02x] = %02x\n", i,
19962306a36Sopenharmony_ci			cx23885_flatiron_read(dev, i));
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int cx23885_flatiron_mux(struct cx23885_dev *dev, int input)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	u8 val;
20662306a36Sopenharmony_ci	dprintk(1, "%s(input = %d)\n", __func__, input);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (input == 1)
20962306a36Sopenharmony_ci		val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) & ~FLD_CH_SEL;
21062306a36Sopenharmony_ci	else if (input == 2)
21162306a36Sopenharmony_ci		val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) | FLD_CH_SEL;
21262306a36Sopenharmony_ci	else
21362306a36Sopenharmony_ci		return -EINVAL;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	val |= 0x20; /* Enable clock to delta-sigma and dec filter */
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	cx23885_flatiron_write(dev, CH_PWR_CTRL1, val);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* Wake up */
22062306a36Sopenharmony_ci	cx23885_flatiron_write(dev, CH_PWR_CTRL2, 0);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (video_debug)
22362306a36Sopenharmony_ci		cx23885_flatiron_dump(dev);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
23162306a36Sopenharmony_ci		__func__,
23262306a36Sopenharmony_ci		input, INPUT(input)->vmux,
23362306a36Sopenharmony_ci		INPUT(input)->gpio0, INPUT(input)->gpio1,
23462306a36Sopenharmony_ci		INPUT(input)->gpio2, INPUT(input)->gpio3);
23562306a36Sopenharmony_ci	dev->input = input;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (dev->board == CX23885_BOARD_MYGICA_X8506 ||
23862306a36Sopenharmony_ci		dev->board == CX23885_BOARD_MAGICPRO_PROHDTVE2 ||
23962306a36Sopenharmony_ci		dev->board == CX23885_BOARD_MYGICA_X8507) {
24062306a36Sopenharmony_ci		/* Select Analog TV */
24162306a36Sopenharmony_ci		if (INPUT(input)->type == CX23885_VMUX_TELEVISION)
24262306a36Sopenharmony_ci			cx23885_gpio_clear(dev, GPIO_0);
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Tell the internal A/V decoder */
24662306a36Sopenharmony_ci	v4l2_subdev_call(dev->sd_cx25840, video, s_routing,
24762306a36Sopenharmony_ci			INPUT(input)->vmux, 0, 0);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) ||
25062306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_MPX885) ||
25162306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1250) ||
25262306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_HAUPPAUGE_IMPACTVCBE) ||
25362306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
25462306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) ||
25562306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1265_K4) ||
25662306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC) ||
25762306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) ||
25862306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) ||
25962306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_HAUPPAUGE_HVR5525) ||
26062306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_MYGICA_X8507) ||
26162306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_AVERMEDIA_HC81R) ||
26262306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_VIEWCAST_260E) ||
26362306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_VIEWCAST_460E) ||
26462306a36Sopenharmony_ci		(dev->board == CX23885_BOARD_AVERMEDIA_CE310B)) {
26562306a36Sopenharmony_ci		/* Configure audio routing */
26662306a36Sopenharmony_ci		v4l2_subdev_call(dev->sd_cx25840, audio, s_routing,
26762306a36Sopenharmony_ci			INPUT(input)->amux, 0, 0);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		if (INPUT(input)->amux == CX25840_AUDIO7)
27062306a36Sopenharmony_ci			cx23885_flatiron_mux(dev, 1);
27162306a36Sopenharmony_ci		else if (INPUT(input)->amux == CX25840_AUDIO6)
27262306a36Sopenharmony_ci			cx23885_flatiron_mux(dev, 2);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic int cx23885_audio_mux(struct cx23885_dev *dev, unsigned int input)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	dprintk(1, "%s(input=%d)\n", __func__, input);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* The baseband video core of the cx23885 has two audio inputs.
28362306a36Sopenharmony_ci	 * LR1 and LR2. In almost every single case so far only HVR1xxx
28462306a36Sopenharmony_ci	 * cards we've only ever supported LR1. Time to support LR2,
28562306a36Sopenharmony_ci	 * which is available via the optional white breakout header on
28662306a36Sopenharmony_ci	 * the board.
28762306a36Sopenharmony_ci	 * We'll use a could of existing enums in the card struct to allow
28862306a36Sopenharmony_ci	 * devs to specify which baseband input they need, or just default
28962306a36Sopenharmony_ci	 * to what we've always used.
29062306a36Sopenharmony_ci	 */
29162306a36Sopenharmony_ci	if (INPUT(input)->amux == CX25840_AUDIO7)
29262306a36Sopenharmony_ci		cx23885_flatiron_mux(dev, 1);
29362306a36Sopenharmony_ci	else if (INPUT(input)->amux == CX25840_AUDIO6)
29462306a36Sopenharmony_ci		cx23885_flatiron_mux(dev, 2);
29562306a36Sopenharmony_ci	else {
29662306a36Sopenharmony_ci		/* Not specifically defined, assume the default. */
29762306a36Sopenharmony_ci		cx23885_flatiron_mux(dev, 1);
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
30462306a36Sopenharmony_cistatic int cx23885_start_video_dma(struct cx23885_dev *dev,
30562306a36Sopenharmony_ci			   struct cx23885_dmaqueue *q,
30662306a36Sopenharmony_ci			   struct cx23885_buffer *buf)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* Stop the dma/fifo before we tamper with it's risc programs */
31162306a36Sopenharmony_ci	cx_clear(VID_A_DMA_CTL, 0x11);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* setup fifo + format */
31462306a36Sopenharmony_ci	cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
31562306a36Sopenharmony_ci				buf->bpl, buf->risc.dma);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* reset counter */
31862306a36Sopenharmony_ci	cx_write(VID_A_GPCNT_CTL, 3);
31962306a36Sopenharmony_ci	q->count = 0;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* enable irq */
32262306a36Sopenharmony_ci	cx23885_irq_add_enable(dev, 0x01);
32362306a36Sopenharmony_ci	cx_set(VID_A_INT_MSK, 0x000011);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* start dma */
32662306a36Sopenharmony_ci	cx_set(DEV_CNTRL2, (1<<5));
32762306a36Sopenharmony_ci	cx_set(VID_A_DMA_CTL, 0x11); /* FIFO and RISC enable */
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return 0;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *q,
33362306a36Sopenharmony_ci			   unsigned int *num_buffers, unsigned int *num_planes,
33462306a36Sopenharmony_ci			   unsigned int sizes[], struct device *alloc_devs[])
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct cx23885_dev *dev = q->drv_priv;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	*num_planes = 1;
33962306a36Sopenharmony_ci	sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3;
34062306a36Sopenharmony_ci	return 0;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	int ret;
34662306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
34762306a36Sopenharmony_ci	struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
34862306a36Sopenharmony_ci	struct cx23885_buffer *buf =
34962306a36Sopenharmony_ci		container_of(vbuf, struct cx23885_buffer, vb);
35062306a36Sopenharmony_ci	u32 line0_offset, line1_offset;
35162306a36Sopenharmony_ci	struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
35262306a36Sopenharmony_ci	int field_tff;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	buf->bpl = (dev->width * dev->fmt->depth) >> 3;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < dev->height * buf->bpl)
35762306a36Sopenharmony_ci		return -EINVAL;
35862306a36Sopenharmony_ci	vb2_set_plane_payload(vb, 0, dev->height * buf->bpl);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	switch (dev->field) {
36162306a36Sopenharmony_ci	case V4L2_FIELD_TOP:
36262306a36Sopenharmony_ci		ret = cx23885_risc_buffer(dev->pci, &buf->risc,
36362306a36Sopenharmony_ci				sgt->sgl, 0, UNSET,
36462306a36Sopenharmony_ci				buf->bpl, 0, dev->height);
36562306a36Sopenharmony_ci		break;
36662306a36Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
36762306a36Sopenharmony_ci		ret = cx23885_risc_buffer(dev->pci, &buf->risc,
36862306a36Sopenharmony_ci				sgt->sgl, UNSET, 0,
36962306a36Sopenharmony_ci				buf->bpl, 0, dev->height);
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
37262306a36Sopenharmony_ci		if (dev->tvnorm & V4L2_STD_525_60)
37362306a36Sopenharmony_ci			/* NTSC or  */
37462306a36Sopenharmony_ci			field_tff = 1;
37562306a36Sopenharmony_ci		else
37662306a36Sopenharmony_ci			field_tff = 0;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		if (cx23885_boards[dev->board].force_bff)
37962306a36Sopenharmony_ci			/* PAL / SECAM OR 888 in NTSC MODE */
38062306a36Sopenharmony_ci			field_tff = 0;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		if (field_tff) {
38362306a36Sopenharmony_ci			/* cx25840 transmits NTSC bottom field first */
38462306a36Sopenharmony_ci			dprintk(1, "%s() Creating TFF/NTSC risc\n",
38562306a36Sopenharmony_ci					__func__);
38662306a36Sopenharmony_ci			line0_offset = buf->bpl;
38762306a36Sopenharmony_ci			line1_offset = 0;
38862306a36Sopenharmony_ci		} else {
38962306a36Sopenharmony_ci			/* All other formats are top field first */
39062306a36Sopenharmony_ci			dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n",
39162306a36Sopenharmony_ci					__func__);
39262306a36Sopenharmony_ci			line0_offset = 0;
39362306a36Sopenharmony_ci			line1_offset = buf->bpl;
39462306a36Sopenharmony_ci		}
39562306a36Sopenharmony_ci		ret = cx23885_risc_buffer(dev->pci, &buf->risc,
39662306a36Sopenharmony_ci				sgt->sgl, line0_offset,
39762306a36Sopenharmony_ci				line1_offset,
39862306a36Sopenharmony_ci				buf->bpl, buf->bpl,
39962306a36Sopenharmony_ci				dev->height >> 1);
40062306a36Sopenharmony_ci		break;
40162306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_TB:
40262306a36Sopenharmony_ci		ret = cx23885_risc_buffer(dev->pci, &buf->risc,
40362306a36Sopenharmony_ci				sgt->sgl,
40462306a36Sopenharmony_ci				0, buf->bpl * (dev->height >> 1),
40562306a36Sopenharmony_ci				buf->bpl, 0,
40662306a36Sopenharmony_ci				dev->height >> 1);
40762306a36Sopenharmony_ci		break;
40862306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_BT:
40962306a36Sopenharmony_ci		ret = cx23885_risc_buffer(dev->pci, &buf->risc,
41062306a36Sopenharmony_ci				sgt->sgl,
41162306a36Sopenharmony_ci				buf->bpl * (dev->height >> 1), 0,
41262306a36Sopenharmony_ci				buf->bpl, 0,
41362306a36Sopenharmony_ci				dev->height >> 1);
41462306a36Sopenharmony_ci		break;
41562306a36Sopenharmony_ci	default:
41662306a36Sopenharmony_ci		return -EINVAL; /* should not happen */
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci	dprintk(2, "[%p/%d] buffer_init - %dx%d %dbpp 0x%08x - dma=0x%08lx\n",
41962306a36Sopenharmony_ci		buf, buf->vb.vb2_buf.index,
42062306a36Sopenharmony_ci		dev->width, dev->height, dev->fmt->depth, dev->fmt->fourcc,
42162306a36Sopenharmony_ci		(unsigned long)buf->risc.dma);
42262306a36Sopenharmony_ci	return ret;
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic void buffer_finish(struct vb2_buffer *vb)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
42862306a36Sopenharmony_ci	struct cx23885_buffer *buf = container_of(vbuf,
42962306a36Sopenharmony_ci		struct cx23885_buffer, vb);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	cx23885_free_buffer(vb->vb2_queue->drv_priv, buf);
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/*
43562306a36Sopenharmony_ci * The risc program for each buffer works as follows: it starts with a simple
43662306a36Sopenharmony_ci * 'JUMP to addr + 12', which is effectively a NOP. Then the code to DMA the
43762306a36Sopenharmony_ci * buffer follows and at the end we have a JUMP back to the start + 12 (skipping
43862306a36Sopenharmony_ci * the initial JUMP).
43962306a36Sopenharmony_ci *
44062306a36Sopenharmony_ci * This is the risc program of the first buffer to be queued if the active list
44162306a36Sopenharmony_ci * is empty and it just keeps DMAing this buffer without generating any
44262306a36Sopenharmony_ci * interrupts.
44362306a36Sopenharmony_ci *
44462306a36Sopenharmony_ci * If a new buffer is added then the initial JUMP in the code for that buffer
44562306a36Sopenharmony_ci * will generate an interrupt which signals that the previous buffer has been
44662306a36Sopenharmony_ci * DMAed successfully and that it can be returned to userspace.
44762306a36Sopenharmony_ci *
44862306a36Sopenharmony_ci * It also sets the final jump of the previous buffer to the start of the new
44962306a36Sopenharmony_ci * buffer, thus chaining the new buffer into the DMA chain. This is a single
45062306a36Sopenharmony_ci * atomic u32 write, so there is no race condition.
45162306a36Sopenharmony_ci *
45262306a36Sopenharmony_ci * The end-result of all this that you only get an interrupt when a buffer
45362306a36Sopenharmony_ci * is ready, so the control flow is very easy.
45462306a36Sopenharmony_ci */
45562306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
45862306a36Sopenharmony_ci	struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
45962306a36Sopenharmony_ci	struct cx23885_buffer   *buf = container_of(vbuf,
46062306a36Sopenharmony_ci		struct cx23885_buffer, vb);
46162306a36Sopenharmony_ci	struct cx23885_buffer   *prev;
46262306a36Sopenharmony_ci	struct cx23885_dmaqueue *q    = &dev->vidq;
46362306a36Sopenharmony_ci	unsigned long flags;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/* add jump to start */
46662306a36Sopenharmony_ci	buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12);
46762306a36Sopenharmony_ci	buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
46862306a36Sopenharmony_ci	buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12);
46962306a36Sopenharmony_ci	buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
47262306a36Sopenharmony_ci	if (list_empty(&q->active)) {
47362306a36Sopenharmony_ci		list_add_tail(&buf->queue, &q->active);
47462306a36Sopenharmony_ci		dprintk(2, "[%p/%d] buffer_queue - first active\n",
47562306a36Sopenharmony_ci			buf, buf->vb.vb2_buf.index);
47662306a36Sopenharmony_ci	} else {
47762306a36Sopenharmony_ci		buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
47862306a36Sopenharmony_ci		prev = list_entry(q->active.prev, struct cx23885_buffer,
47962306a36Sopenharmony_ci			queue);
48062306a36Sopenharmony_ci		list_add_tail(&buf->queue, &q->active);
48162306a36Sopenharmony_ci		prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
48262306a36Sopenharmony_ci		dprintk(2, "[%p/%d] buffer_queue - append to active\n",
48362306a36Sopenharmony_ci				buf, buf->vb.vb2_buf.index);
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic int cx23885_start_streaming(struct vb2_queue *q, unsigned int count)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct cx23885_dev *dev = q->drv_priv;
49162306a36Sopenharmony_ci	struct cx23885_dmaqueue *dmaq = &dev->vidq;
49262306a36Sopenharmony_ci	struct cx23885_buffer *buf = list_entry(dmaq->active.next,
49362306a36Sopenharmony_ci			struct cx23885_buffer, queue);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	cx23885_start_video_dma(dev, dmaq, buf);
49662306a36Sopenharmony_ci	return 0;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic void cx23885_stop_streaming(struct vb2_queue *q)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct cx23885_dev *dev = q->drv_priv;
50262306a36Sopenharmony_ci	struct cx23885_dmaqueue *dmaq = &dev->vidq;
50362306a36Sopenharmony_ci	unsigned long flags;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	cx_clear(VID_A_DMA_CTL, 0x11);
50662306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
50762306a36Sopenharmony_ci	while (!list_empty(&dmaq->active)) {
50862306a36Sopenharmony_ci		struct cx23885_buffer *buf = list_entry(dmaq->active.next,
50962306a36Sopenharmony_ci			struct cx23885_buffer, queue);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		list_del(&buf->queue);
51262306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic const struct vb2_ops cx23885_video_qops = {
51862306a36Sopenharmony_ci	.queue_setup    = queue_setup,
51962306a36Sopenharmony_ci	.buf_prepare  = buffer_prepare,
52062306a36Sopenharmony_ci	.buf_finish = buffer_finish,
52162306a36Sopenharmony_ci	.buf_queue    = buffer_queue,
52262306a36Sopenharmony_ci	.wait_prepare = vb2_ops_wait_prepare,
52362306a36Sopenharmony_ci	.wait_finish = vb2_ops_wait_finish,
52462306a36Sopenharmony_ci	.start_streaming = cx23885_start_streaming,
52562306a36Sopenharmony_ci	.stop_streaming = cx23885_stop_streaming,
52662306a36Sopenharmony_ci};
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
52962306a36Sopenharmony_ci/* VIDEO IOCTLS                                                       */
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
53262306a36Sopenharmony_ci	struct v4l2_format *f)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	f->fmt.pix.width        = dev->width;
53762306a36Sopenharmony_ci	f->fmt.pix.height       = dev->height;
53862306a36Sopenharmony_ci	f->fmt.pix.field        = dev->field;
53962306a36Sopenharmony_ci	f->fmt.pix.pixelformat  = dev->fmt->fourcc;
54062306a36Sopenharmony_ci	f->fmt.pix.bytesperline =
54162306a36Sopenharmony_ci		(f->fmt.pix.width * dev->fmt->depth) >> 3;
54262306a36Sopenharmony_ci	f->fmt.pix.sizeimage =
54362306a36Sopenharmony_ci		f->fmt.pix.height * f->fmt.pix.bytesperline;
54462306a36Sopenharmony_ci	f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	return 0;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
55062306a36Sopenharmony_ci	struct v4l2_format *f)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
55362306a36Sopenharmony_ci	struct cx23885_fmt *fmt;
55462306a36Sopenharmony_ci	enum v4l2_field   field;
55562306a36Sopenharmony_ci	unsigned int      maxw, maxh;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
55862306a36Sopenharmony_ci	if (NULL == fmt)
55962306a36Sopenharmony_ci		return -EINVAL;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	field = f->fmt.pix.field;
56262306a36Sopenharmony_ci	maxw  = 720;
56362306a36Sopenharmony_ci	maxh  = norm_maxh(dev->tvnorm);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (V4L2_FIELD_ANY == field) {
56662306a36Sopenharmony_ci		field = (f->fmt.pix.height > maxh/2)
56762306a36Sopenharmony_ci			? V4L2_FIELD_INTERLACED
56862306a36Sopenharmony_ci			: V4L2_FIELD_BOTTOM;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	switch (field) {
57262306a36Sopenharmony_ci	case V4L2_FIELD_TOP:
57362306a36Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
57462306a36Sopenharmony_ci		maxh = maxh / 2;
57562306a36Sopenharmony_ci		break;
57662306a36Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
57762306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_TB:
57862306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_BT:
57962306a36Sopenharmony_ci		break;
58062306a36Sopenharmony_ci	default:
58162306a36Sopenharmony_ci		field = V4L2_FIELD_INTERLACED;
58262306a36Sopenharmony_ci		break;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	f->fmt.pix.field = field;
58662306a36Sopenharmony_ci	v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
58762306a36Sopenharmony_ci			      &f->fmt.pix.height, 32, maxh, 0, 0);
58862306a36Sopenharmony_ci	f->fmt.pix.bytesperline =
58962306a36Sopenharmony_ci		(f->fmt.pix.width * fmt->depth) >> 3;
59062306a36Sopenharmony_ci	f->fmt.pix.sizeimage =
59162306a36Sopenharmony_ci		f->fmt.pix.height * f->fmt.pix.bytesperline;
59262306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return 0;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
59862306a36Sopenharmony_ci	struct v4l2_format *f)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
60162306a36Sopenharmony_ci	struct v4l2_subdev_format format = {
60262306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
60362306a36Sopenharmony_ci	};
60462306a36Sopenharmony_ci	int err;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	dprintk(2, "%s()\n", __func__);
60762306a36Sopenharmony_ci	err = vidioc_try_fmt_vid_cap(file, priv, f);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (0 != err)
61062306a36Sopenharmony_ci		return err;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq) ||
61362306a36Sopenharmony_ci	    vb2_is_busy(&dev->vb2_mpegq))
61462306a36Sopenharmony_ci		return -EBUSY;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	dev->fmt        = format_by_fourcc(f->fmt.pix.pixelformat);
61762306a36Sopenharmony_ci	dev->width      = f->fmt.pix.width;
61862306a36Sopenharmony_ci	dev->height     = f->fmt.pix.height;
61962306a36Sopenharmony_ci	dev->field	= f->fmt.pix.field;
62062306a36Sopenharmony_ci	dprintk(2, "%s() width=%d height=%d field=%d\n", __func__,
62162306a36Sopenharmony_ci		dev->width, dev->height, dev->field);
62262306a36Sopenharmony_ci	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
62362306a36Sopenharmony_ci	call_all(dev, pad, set_fmt, NULL, &format);
62462306a36Sopenharmony_ci	v4l2_fill_pix_format(&f->fmt.pix, &format.format);
62562306a36Sopenharmony_ci	/* set_fmt overwrites f->fmt.pix.field, restore it */
62662306a36Sopenharmony_ci	f->fmt.pix.field = dev->field;
62762306a36Sopenharmony_ci	return 0;
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, void  *priv,
63162306a36Sopenharmony_ci	struct v4l2_capability *cap)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	strscpy(cap->driver, "cx23885", sizeof(cap->driver));
63662306a36Sopenharmony_ci	strscpy(cap->card, cx23885_boards[dev->board].name,
63762306a36Sopenharmony_ci		sizeof(cap->card));
63862306a36Sopenharmony_ci	sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
63962306a36Sopenharmony_ci	cap->capabilities = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
64062306a36Sopenharmony_ci			    V4L2_CAP_AUDIO | V4L2_CAP_VBI_CAPTURE |
64162306a36Sopenharmony_ci			    V4L2_CAP_VIDEO_CAPTURE |
64262306a36Sopenharmony_ci			    V4L2_CAP_DEVICE_CAPS;
64362306a36Sopenharmony_ci	switch (dev->board) { /* i2c device tuners */
64462306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
64562306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR5525:
64662306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
64762306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
64862306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_TUNER;
64962306a36Sopenharmony_ci		break;
65062306a36Sopenharmony_ci	default:
65162306a36Sopenharmony_ci		if (dev->tuner_type != TUNER_ABSENT)
65262306a36Sopenharmony_ci			cap->capabilities |= V4L2_CAP_TUNER;
65362306a36Sopenharmony_ci		break;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci	return 0;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
65962306a36Sopenharmony_ci	struct v4l2_fmtdesc *f)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	if (unlikely(f->index >= ARRAY_SIZE(formats)))
66262306a36Sopenharmony_ci		return -EINVAL;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	f->pixelformat = formats[f->index].fourcc;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	return 0;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic int vidioc_g_pixelaspect(struct file *file, void *priv,
67062306a36Sopenharmony_ci				int type, struct v4l2_fract *f)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
67362306a36Sopenharmony_ci	bool is_50hz = dev->tvnorm & V4L2_STD_625_50;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
67662306a36Sopenharmony_ci		return -EINVAL;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	f->numerator = is_50hz ? 54 : 11;
67962306a36Sopenharmony_ci	f->denominator = is_50hz ? 59 : 10;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	return 0;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic int vidioc_g_selection(struct file *file, void *fh,
68562306a36Sopenharmony_ci			      struct v4l2_selection *sel)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
69062306a36Sopenharmony_ci		return -EINVAL;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	switch (sel->target) {
69362306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
69462306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
69562306a36Sopenharmony_ci		sel->r.top = 0;
69662306a36Sopenharmony_ci		sel->r.left = 0;
69762306a36Sopenharmony_ci		sel->r.width = 720;
69862306a36Sopenharmony_ci		sel->r.height = norm_maxh(dev->tvnorm);
69962306a36Sopenharmony_ci		break;
70062306a36Sopenharmony_ci	default:
70162306a36Sopenharmony_ci		return -EINVAL;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci	return 0;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
70962306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	*id = dev->tvnorm;
71262306a36Sopenharmony_ci	return 0;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
71862306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	return cx23885_set_tvnorm(dev, tvnorms);
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ciint cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	static const char *iname[] = {
72662306a36Sopenharmony_ci		[CX23885_VMUX_COMPOSITE1] = "Composite1",
72762306a36Sopenharmony_ci		[CX23885_VMUX_COMPOSITE2] = "Composite2",
72862306a36Sopenharmony_ci		[CX23885_VMUX_COMPOSITE3] = "Composite3",
72962306a36Sopenharmony_ci		[CX23885_VMUX_COMPOSITE4] = "Composite4",
73062306a36Sopenharmony_ci		[CX23885_VMUX_SVIDEO]     = "S-Video",
73162306a36Sopenharmony_ci		[CX23885_VMUX_COMPONENT]  = "Component",
73262306a36Sopenharmony_ci		[CX23885_VMUX_TELEVISION] = "Television",
73362306a36Sopenharmony_ci		[CX23885_VMUX_CABLE]      = "Cable TV",
73462306a36Sopenharmony_ci		[CX23885_VMUX_DVB]        = "DVB",
73562306a36Sopenharmony_ci		[CX23885_VMUX_DEBUG]      = "for debug only",
73662306a36Sopenharmony_ci	};
73762306a36Sopenharmony_ci	unsigned int n;
73862306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	n = i->index;
74162306a36Sopenharmony_ci	if (n >= MAX_CX23885_INPUT)
74262306a36Sopenharmony_ci		return -EINVAL;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (0 == INPUT(n)->type)
74562306a36Sopenharmony_ci		return -EINVAL;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	i->index = n;
74862306a36Sopenharmony_ci	i->type  = V4L2_INPUT_TYPE_CAMERA;
74962306a36Sopenharmony_ci	strscpy(i->name, iname[INPUT(n)->type], sizeof(i->name));
75062306a36Sopenharmony_ci	i->std = CX23885_NORMS;
75162306a36Sopenharmony_ci	if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) ||
75262306a36Sopenharmony_ci		(CX23885_VMUX_CABLE == INPUT(n)->type)) {
75362306a36Sopenharmony_ci		i->type = V4L2_INPUT_TYPE_TUNER;
75462306a36Sopenharmony_ci		i->audioset = 4;
75562306a36Sopenharmony_ci	} else {
75662306a36Sopenharmony_ci		/* Two selectable audio inputs for non-tv inputs */
75762306a36Sopenharmony_ci		i->audioset = 3;
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	if (dev->input == n) {
76162306a36Sopenharmony_ci		/* enum'd input matches our configured input.
76262306a36Sopenharmony_ci		 * Ask the video decoder to process the call
76362306a36Sopenharmony_ci		 * and give it an oppertunity to update the
76462306a36Sopenharmony_ci		 * status field.
76562306a36Sopenharmony_ci		 */
76662306a36Sopenharmony_ci		call_all(dev, video, g_input_status, &i->status);
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	return 0;
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv,
77362306a36Sopenharmony_ci				struct v4l2_input *i)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
77662306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
77762306a36Sopenharmony_ci	return cx23885_enum_input(dev, i);
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ciint cx23885_get_input(struct file *file, void *priv, unsigned int *i)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	*i = dev->input;
78562306a36Sopenharmony_ci	dprintk(1, "%s() returns %d\n", __func__, *i);
78662306a36Sopenharmony_ci	return 0;
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	return cx23885_get_input(file, priv, i);
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ciint cx23885_set_input(struct file *file, void *priv, unsigned int i)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	dprintk(1, "%s(%d)\n", __func__, i);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	if (i >= MAX_CX23885_INPUT) {
80162306a36Sopenharmony_ci		dprintk(1, "%s() -EINVAL\n", __func__);
80262306a36Sopenharmony_ci		return -EINVAL;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	if (INPUT(i)->type == 0)
80662306a36Sopenharmony_ci		return -EINVAL;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	cx23885_video_mux(dev, i);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	/* By default establish the default audio input for the card also */
81162306a36Sopenharmony_ci	/* Caller is free to use VIDIOC_S_AUDIO to override afterwards */
81262306a36Sopenharmony_ci	cx23885_audio_mux(dev, i);
81362306a36Sopenharmony_ci	return 0;
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	return cx23885_set_input(file, priv, i);
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic int vidioc_log_status(struct file *file, void *priv)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	call_all(dev, core, log_status);
82662306a36Sopenharmony_ci	return 0;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic int cx23885_query_audinput(struct file *file, void *priv,
83062306a36Sopenharmony_ci	struct v4l2_audio *i)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	static const char *iname[] = {
83362306a36Sopenharmony_ci		[0] = "Baseband L/R 1",
83462306a36Sopenharmony_ci		[1] = "Baseband L/R 2",
83562306a36Sopenharmony_ci		[2] = "TV",
83662306a36Sopenharmony_ci	};
83762306a36Sopenharmony_ci	unsigned int n;
83862306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	n = i->index;
84162306a36Sopenharmony_ci	if (n >= 3)
84262306a36Sopenharmony_ci		return -EINVAL;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	memset(i, 0, sizeof(*i));
84562306a36Sopenharmony_ci	i->index = n;
84662306a36Sopenharmony_ci	strscpy(i->name, iname[n], sizeof(i->name));
84762306a36Sopenharmony_ci	i->capability = V4L2_AUDCAP_STEREO;
84862306a36Sopenharmony_ci	return 0;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_cistatic int vidioc_enum_audinput(struct file *file, void *priv,
85362306a36Sopenharmony_ci				struct v4l2_audio *i)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	return cx23885_query_audinput(file, priv, i);
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic int vidioc_g_audinput(struct file *file, void *priv,
85962306a36Sopenharmony_ci	struct v4l2_audio *i)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if ((CX23885_VMUX_TELEVISION == INPUT(dev->input)->type) ||
86462306a36Sopenharmony_ci		(CX23885_VMUX_CABLE == INPUT(dev->input)->type))
86562306a36Sopenharmony_ci		i->index = 2;
86662306a36Sopenharmony_ci	else
86762306a36Sopenharmony_ci		i->index = dev->audinput;
86862306a36Sopenharmony_ci	dprintk(1, "%s(input=%d)\n", __func__, i->index);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	return cx23885_query_audinput(file, priv, i);
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic int vidioc_s_audinput(struct file *file, void *priv,
87462306a36Sopenharmony_ci	const struct v4l2_audio *i)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if ((CX23885_VMUX_TELEVISION == INPUT(dev->input)->type) ||
87962306a36Sopenharmony_ci		(CX23885_VMUX_CABLE == INPUT(dev->input)->type)) {
88062306a36Sopenharmony_ci		return i->index != 2 ? -EINVAL : 0;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci	if (i->index > 1)
88362306a36Sopenharmony_ci		return -EINVAL;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	dprintk(1, "%s(%d)\n", __func__, i->index);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	dev->audinput = i->index;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	/* Skip the audio defaults from the cards struct, caller wants
89062306a36Sopenharmony_ci	 * directly touch the audio mux hardware. */
89162306a36Sopenharmony_ci	cx23885_flatiron_mux(dev, dev->audinput + 1);
89262306a36Sopenharmony_ci	return 0;
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv,
89662306a36Sopenharmony_ci				struct v4l2_tuner *t)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	switch (dev->board) { /* i2c device tuners */
90162306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
90262306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR5525:
90362306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
90462306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
90562306a36Sopenharmony_ci		break;
90662306a36Sopenharmony_ci	default:
90762306a36Sopenharmony_ci		if (dev->tuner_type == TUNER_ABSENT)
90862306a36Sopenharmony_ci			return -EINVAL;
90962306a36Sopenharmony_ci		break;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci	if (0 != t->index)
91262306a36Sopenharmony_ci		return -EINVAL;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	strscpy(t->name, "Television", sizeof(t->name));
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	call_all(dev, tuner, g_tuner, t);
91762306a36Sopenharmony_ci	return 0;
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv,
92162306a36Sopenharmony_ci				const struct v4l2_tuner *t)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	switch (dev->board) { /* i2c device tuners */
92662306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
92762306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR5525:
92862306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
92962306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
93062306a36Sopenharmony_ci		break;
93162306a36Sopenharmony_ci	default:
93262306a36Sopenharmony_ci		if (dev->tuner_type == TUNER_ABSENT)
93362306a36Sopenharmony_ci			return -EINVAL;
93462306a36Sopenharmony_ci		break;
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci	if (0 != t->index)
93762306a36Sopenharmony_ci		return -EINVAL;
93862306a36Sopenharmony_ci	/* Update the A/V core */
93962306a36Sopenharmony_ci	call_all(dev, tuner, s_tuner, t);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	return 0;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv,
94562306a36Sopenharmony_ci				struct v4l2_frequency *f)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	switch (dev->board) { /* i2c device tuners */
95062306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
95162306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR5525:
95262306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
95362306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
95462306a36Sopenharmony_ci		break;
95562306a36Sopenharmony_ci	default:
95662306a36Sopenharmony_ci		if (dev->tuner_type == TUNER_ABSENT)
95762306a36Sopenharmony_ci			return -EINVAL;
95862306a36Sopenharmony_ci		break;
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci	f->type = V4L2_TUNER_ANALOG_TV;
96162306a36Sopenharmony_ci	f->frequency = dev->freq;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	call_all(dev, tuner, g_frequency, f);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	return 0;
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency *f)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	struct v4l2_ctrl *mute;
97162306a36Sopenharmony_ci	int old_mute_val = 1;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	switch (dev->board) { /* i2c device tuners */
97462306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
97562306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR5525:
97662306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
97762306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
97862306a36Sopenharmony_ci		break;
97962306a36Sopenharmony_ci	default:
98062306a36Sopenharmony_ci		if (dev->tuner_type == TUNER_ABSENT)
98162306a36Sopenharmony_ci			return -EINVAL;
98262306a36Sopenharmony_ci		break;
98362306a36Sopenharmony_ci	}
98462306a36Sopenharmony_ci	if (unlikely(f->tuner != 0))
98562306a36Sopenharmony_ci		return -EINVAL;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	dev->freq = f->frequency;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	/* I need to mute audio here */
99062306a36Sopenharmony_ci	mute = v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_AUDIO_MUTE);
99162306a36Sopenharmony_ci	if (mute) {
99262306a36Sopenharmony_ci		old_mute_val = v4l2_ctrl_g_ctrl(mute);
99362306a36Sopenharmony_ci		if (!old_mute_val)
99462306a36Sopenharmony_ci			v4l2_ctrl_s_ctrl(mute, 1);
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	call_all(dev, tuner, s_frequency, f);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	/* When changing channels it is required to reset TVAUDIO */
100062306a36Sopenharmony_ci	msleep(100);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* I need to unmute audio here */
100362306a36Sopenharmony_ci	if (old_mute_val == 0)
100462306a36Sopenharmony_ci		v4l2_ctrl_s_ctrl(mute, old_mute_val);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	return 0;
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cistatic int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
101062306a36Sopenharmony_ci	const struct v4l2_frequency *f)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	struct v4l2_ctrl *mute;
101362306a36Sopenharmony_ci	int old_mute_val = 1;
101462306a36Sopenharmony_ci	struct vb2_dvb_frontend *vfe;
101562306a36Sopenharmony_ci	struct dvb_frontend *fe;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	struct analog_parameters params = {
101862306a36Sopenharmony_ci		.mode      = V4L2_TUNER_ANALOG_TV,
101962306a36Sopenharmony_ci		.audmode   = V4L2_TUNER_MODE_STEREO,
102062306a36Sopenharmony_ci		.std       = dev->tvnorm,
102162306a36Sopenharmony_ci		.frequency = f->frequency
102262306a36Sopenharmony_ci	};
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	dev->freq = f->frequency;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	/* I need to mute audio here */
102762306a36Sopenharmony_ci	mute = v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_AUDIO_MUTE);
102862306a36Sopenharmony_ci	if (mute) {
102962306a36Sopenharmony_ci		old_mute_val = v4l2_ctrl_g_ctrl(mute);
103062306a36Sopenharmony_ci		if (!old_mute_val)
103162306a36Sopenharmony_ci			v4l2_ctrl_s_ctrl(mute, 1);
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	/* If HVR1850 */
103562306a36Sopenharmony_ci	dprintk(1, "%s() frequency=%d tuner=%d std=0x%llx\n", __func__,
103662306a36Sopenharmony_ci		params.frequency, f->tuner, params.std);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	vfe = vb2_dvb_get_frontend(&dev->ts2.frontends, 1);
103962306a36Sopenharmony_ci	if (!vfe) {
104062306a36Sopenharmony_ci		return -EINVAL;
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	fe = vfe->dvb.frontend;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) ||
104662306a36Sopenharmony_ci	    (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
104762306a36Sopenharmony_ci	    (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) ||
104862306a36Sopenharmony_ci	    (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1265_K4) ||
104962306a36Sopenharmony_ci	    (dev->board == CX23885_BOARD_HAUPPAUGE_HVR5525) ||
105062306a36Sopenharmony_ci	    (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) ||
105162306a36Sopenharmony_ci	    (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC))
105262306a36Sopenharmony_ci		fe = &dev->ts1.analog_fe;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if (fe && fe->ops.tuner_ops.set_analog_params) {
105562306a36Sopenharmony_ci		call_all(dev, video, s_std, dev->tvnorm);
105662306a36Sopenharmony_ci		fe->ops.tuner_ops.set_analog_params(fe, &params);
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci	else
105962306a36Sopenharmony_ci		pr_err("%s() No analog tuner, aborting\n", __func__);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	/* When changing channels it is required to reset TVAUDIO */
106262306a36Sopenharmony_ci	msleep(100);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	/* I need to unmute audio here */
106562306a36Sopenharmony_ci	if (old_mute_val == 0)
106662306a36Sopenharmony_ci		v4l2_ctrl_s_ctrl(mute, old_mute_val);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	return 0;
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ciint cx23885_set_frequency(struct file *file, void *priv,
107262306a36Sopenharmony_ci	const struct v4l2_frequency *f)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	struct cx23885_dev *dev = video_drvdata(file);
107562306a36Sopenharmony_ci	int ret;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	switch (dev->board) {
107862306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR1255:
107962306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
108062306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
108162306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR1850:
108262306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR5525:
108362306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
108462306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
108562306a36Sopenharmony_ci		ret = cx23885_set_freq_via_ops(dev, f);
108662306a36Sopenharmony_ci		break;
108762306a36Sopenharmony_ci	default:
108862306a36Sopenharmony_ci		ret = cx23885_set_freq(dev, f);
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	return ret;
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv,
109562306a36Sopenharmony_ci	const struct v4l2_frequency *f)
109662306a36Sopenharmony_ci{
109762306a36Sopenharmony_ci	return cx23885_set_frequency(file, priv, f);
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci/* ----------------------------------------------------------- */
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ciint cx23885_video_irq(struct cx23885_dev *dev, u32 status)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	u32 mask, count;
110562306a36Sopenharmony_ci	int handled = 0;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	mask   = cx_read(VID_A_INT_MSK);
110862306a36Sopenharmony_ci	if (0 == (status & mask))
110962306a36Sopenharmony_ci		return handled;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	cx_write(VID_A_INT_STAT, status);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	/* risc op code error, fifo overflow or line sync detection error */
111462306a36Sopenharmony_ci	if ((status & VID_BC_MSK_OPC_ERR) ||
111562306a36Sopenharmony_ci		(status & VID_BC_MSK_SYNC) ||
111662306a36Sopenharmony_ci		(status & VID_BC_MSK_OF)) {
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci		if (status & VID_BC_MSK_OPC_ERR) {
111962306a36Sopenharmony_ci			dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n",
112062306a36Sopenharmony_ci				VID_BC_MSK_OPC_ERR);
112162306a36Sopenharmony_ci			pr_warn("%s: video risc op code error\n",
112262306a36Sopenharmony_ci				dev->name);
112362306a36Sopenharmony_ci			cx23885_sram_channel_dump(dev,
112462306a36Sopenharmony_ci				&dev->sram_channels[SRAM_CH01]);
112562306a36Sopenharmony_ci		}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci		if (status & VID_BC_MSK_SYNC)
112862306a36Sopenharmony_ci			dprintk(7, " (VID_BC_MSK_SYNC 0x%08x) video lines miss-match\n",
112962306a36Sopenharmony_ci				VID_BC_MSK_SYNC);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci		if (status & VID_BC_MSK_OF)
113262306a36Sopenharmony_ci			dprintk(7, " (VID_BC_MSK_OF 0x%08x) fifo overflow\n",
113362306a36Sopenharmony_ci				VID_BC_MSK_OF);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	/* Video */
113862306a36Sopenharmony_ci	if (status & VID_BC_MSK_RISCI1) {
113962306a36Sopenharmony_ci		spin_lock(&dev->slock);
114062306a36Sopenharmony_ci		count = cx_read(VID_A_GPCNT);
114162306a36Sopenharmony_ci		cx23885_video_wakeup(dev, &dev->vidq, count);
114262306a36Sopenharmony_ci		spin_unlock(&dev->slock);
114362306a36Sopenharmony_ci		handled++;
114462306a36Sopenharmony_ci	}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	/* Allow the VBI framework to process it's payload */
114762306a36Sopenharmony_ci	handled += cx23885_vbi_irq(dev, status);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	return handled;
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci/* ----------------------------------------------------------- */
115362306a36Sopenharmony_ci/* exported stuff                                              */
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_cistatic const struct v4l2_file_operations video_fops = {
115662306a36Sopenharmony_ci	.owner	       = THIS_MODULE,
115762306a36Sopenharmony_ci	.open           = v4l2_fh_open,
115862306a36Sopenharmony_ci	.release        = vb2_fop_release,
115962306a36Sopenharmony_ci	.read           = vb2_fop_read,
116062306a36Sopenharmony_ci	.poll		= vb2_fop_poll,
116162306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
116262306a36Sopenharmony_ci	.mmap           = vb2_fop_mmap,
116362306a36Sopenharmony_ci};
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops video_ioctl_ops = {
116662306a36Sopenharmony_ci	.vidioc_querycap      = vidioc_querycap,
116762306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
116862306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
116962306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
117062306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
117162306a36Sopenharmony_ci	.vidioc_g_fmt_vbi_cap     = cx23885_vbi_fmt,
117262306a36Sopenharmony_ci	.vidioc_try_fmt_vbi_cap   = cx23885_vbi_fmt,
117362306a36Sopenharmony_ci	.vidioc_s_fmt_vbi_cap     = cx23885_vbi_fmt,
117462306a36Sopenharmony_ci	.vidioc_reqbufs       = vb2_ioctl_reqbufs,
117562306a36Sopenharmony_ci	.vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
117662306a36Sopenharmony_ci	.vidioc_querybuf      = vb2_ioctl_querybuf,
117762306a36Sopenharmony_ci	.vidioc_qbuf          = vb2_ioctl_qbuf,
117862306a36Sopenharmony_ci	.vidioc_dqbuf         = vb2_ioctl_dqbuf,
117962306a36Sopenharmony_ci	.vidioc_streamon      = vb2_ioctl_streamon,
118062306a36Sopenharmony_ci	.vidioc_streamoff     = vb2_ioctl_streamoff,
118162306a36Sopenharmony_ci	.vidioc_g_pixelaspect = vidioc_g_pixelaspect,
118262306a36Sopenharmony_ci	.vidioc_g_selection   = vidioc_g_selection,
118362306a36Sopenharmony_ci	.vidioc_s_std         = vidioc_s_std,
118462306a36Sopenharmony_ci	.vidioc_g_std         = vidioc_g_std,
118562306a36Sopenharmony_ci	.vidioc_enum_input    = vidioc_enum_input,
118662306a36Sopenharmony_ci	.vidioc_g_input       = vidioc_g_input,
118762306a36Sopenharmony_ci	.vidioc_s_input       = vidioc_s_input,
118862306a36Sopenharmony_ci	.vidioc_log_status    = vidioc_log_status,
118962306a36Sopenharmony_ci	.vidioc_g_tuner       = vidioc_g_tuner,
119062306a36Sopenharmony_ci	.vidioc_s_tuner       = vidioc_s_tuner,
119162306a36Sopenharmony_ci	.vidioc_g_frequency   = vidioc_g_frequency,
119262306a36Sopenharmony_ci	.vidioc_s_frequency   = vidioc_s_frequency,
119362306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
119462306a36Sopenharmony_ci	.vidioc_g_chip_info   = cx23885_g_chip_info,
119562306a36Sopenharmony_ci	.vidioc_g_register    = cx23885_g_register,
119662306a36Sopenharmony_ci	.vidioc_s_register    = cx23885_s_register,
119762306a36Sopenharmony_ci#endif
119862306a36Sopenharmony_ci	.vidioc_enumaudio     = vidioc_enum_audinput,
119962306a36Sopenharmony_ci	.vidioc_g_audio       = vidioc_g_audinput,
120062306a36Sopenharmony_ci	.vidioc_s_audio       = vidioc_s_audinput,
120162306a36Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
120262306a36Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
120362306a36Sopenharmony_ci};
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cistatic struct video_device cx23885_vbi_template;
120662306a36Sopenharmony_cistatic struct video_device cx23885_video_template = {
120762306a36Sopenharmony_ci	.name                 = "cx23885-video",
120862306a36Sopenharmony_ci	.fops                 = &video_fops,
120962306a36Sopenharmony_ci	.ioctl_ops	      = &video_ioctl_ops,
121062306a36Sopenharmony_ci	.tvnorms              = CX23885_NORMS,
121162306a36Sopenharmony_ci};
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_civoid cx23885_video_unregister(struct cx23885_dev *dev)
121462306a36Sopenharmony_ci{
121562306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
121662306a36Sopenharmony_ci	cx23885_irq_remove(dev, 0x01);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	if (dev->vbi_dev) {
121962306a36Sopenharmony_ci		if (video_is_registered(dev->vbi_dev))
122062306a36Sopenharmony_ci			video_unregister_device(dev->vbi_dev);
122162306a36Sopenharmony_ci		else
122262306a36Sopenharmony_ci			video_device_release(dev->vbi_dev);
122362306a36Sopenharmony_ci		dev->vbi_dev = NULL;
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci	if (dev->video_dev) {
122662306a36Sopenharmony_ci		if (video_is_registered(dev->video_dev))
122762306a36Sopenharmony_ci			video_unregister_device(dev->video_dev);
122862306a36Sopenharmony_ci		else
122962306a36Sopenharmony_ci			video_device_release(dev->video_dev);
123062306a36Sopenharmony_ci		dev->video_dev = NULL;
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	if (dev->audio_dev)
123462306a36Sopenharmony_ci		cx23885_audio_unregister(dev);
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ciint cx23885_video_register(struct cx23885_dev *dev)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	struct vb2_queue *q;
124062306a36Sopenharmony_ci	int err;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	/* Initialize VBI template */
124562306a36Sopenharmony_ci	cx23885_vbi_template = cx23885_video_template;
124662306a36Sopenharmony_ci	strscpy(cx23885_vbi_template.name, "cx23885-vbi",
124762306a36Sopenharmony_ci		sizeof(cx23885_vbi_template.name));
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	dev->tvnorm = V4L2_STD_NTSC_M;
125062306a36Sopenharmony_ci	dev->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV);
125162306a36Sopenharmony_ci	dev->field = V4L2_FIELD_INTERLACED;
125262306a36Sopenharmony_ci	dev->width = 720;
125362306a36Sopenharmony_ci	dev->height = norm_maxh(dev->tvnorm);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	/* init video dma queues */
125662306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->vidq.active);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	/* init vbi dma queues */
125962306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->vbiq.active);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	cx23885_irq_add_enable(dev, 0x01);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	if ((TUNER_ABSENT != dev->tuner_type) &&
126462306a36Sopenharmony_ci			((dev->tuner_bus == 0) || (dev->tuner_bus == 1))) {
126562306a36Sopenharmony_ci		struct v4l2_subdev *sd = NULL;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci		if (dev->tuner_addr)
126862306a36Sopenharmony_ci			sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
126962306a36Sopenharmony_ci				&dev->i2c_bus[dev->tuner_bus].i2c_adap,
127062306a36Sopenharmony_ci				"tuner", dev->tuner_addr, NULL);
127162306a36Sopenharmony_ci		else
127262306a36Sopenharmony_ci			sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
127362306a36Sopenharmony_ci				&dev->i2c_bus[dev->tuner_bus].i2c_adap,
127462306a36Sopenharmony_ci				"tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV));
127562306a36Sopenharmony_ci		if (sd) {
127662306a36Sopenharmony_ci			struct tuner_setup tun_setup;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci			memset(&tun_setup, 0, sizeof(tun_setup));
127962306a36Sopenharmony_ci			tun_setup.mode_mask = T_ANALOG_TV;
128062306a36Sopenharmony_ci			tun_setup.type = dev->tuner_type;
128162306a36Sopenharmony_ci			tun_setup.addr = v4l2_i2c_subdev_addr(sd);
128262306a36Sopenharmony_ci			tun_setup.tuner_callback = cx23885_tuner_callback;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci			v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci			if ((dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXTV1200) ||
128762306a36Sopenharmony_ci			    (dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200)) {
128862306a36Sopenharmony_ci				struct xc2028_ctrl ctrl = {
128962306a36Sopenharmony_ci					.fname = XC2028_DEFAULT_FIRMWARE,
129062306a36Sopenharmony_ci					.max_len = 64
129162306a36Sopenharmony_ci				};
129262306a36Sopenharmony_ci				struct v4l2_priv_tun_config cfg = {
129362306a36Sopenharmony_ci					.tuner = dev->tuner_type,
129462306a36Sopenharmony_ci					.priv = &ctrl
129562306a36Sopenharmony_ci				};
129662306a36Sopenharmony_ci				v4l2_subdev_call(sd, tuner, s_config, &cfg);
129762306a36Sopenharmony_ci			}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci			if (dev->board == CX23885_BOARD_AVERMEDIA_HC81R) {
130062306a36Sopenharmony_ci				struct xc2028_ctrl ctrl = {
130162306a36Sopenharmony_ci					.fname = "xc3028L-v36.fw",
130262306a36Sopenharmony_ci					.max_len = 64
130362306a36Sopenharmony_ci				};
130462306a36Sopenharmony_ci				struct v4l2_priv_tun_config cfg = {
130562306a36Sopenharmony_ci					.tuner = dev->tuner_type,
130662306a36Sopenharmony_ci					.priv = &ctrl
130762306a36Sopenharmony_ci				};
130862306a36Sopenharmony_ci				v4l2_subdev_call(sd, tuner, s_config, &cfg);
130962306a36Sopenharmony_ci			}
131062306a36Sopenharmony_ci		}
131162306a36Sopenharmony_ci	}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	/* initial device configuration */
131462306a36Sopenharmony_ci	mutex_lock(&dev->lock);
131562306a36Sopenharmony_ci	cx23885_set_tvnorm(dev, dev->tvnorm);
131662306a36Sopenharmony_ci	cx23885_video_mux(dev, 0);
131762306a36Sopenharmony_ci	cx23885_audio_mux(dev, 0);
131862306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	q = &dev->vb2_vidq;
132162306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
132262306a36Sopenharmony_ci	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
132362306a36Sopenharmony_ci	q->gfp_flags = GFP_DMA32;
132462306a36Sopenharmony_ci	q->min_buffers_needed = 2;
132562306a36Sopenharmony_ci	q->drv_priv = dev;
132662306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct cx23885_buffer);
132762306a36Sopenharmony_ci	q->ops = &cx23885_video_qops;
132862306a36Sopenharmony_ci	q->mem_ops = &vb2_dma_sg_memops;
132962306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
133062306a36Sopenharmony_ci	q->lock = &dev->lock;
133162306a36Sopenharmony_ci	q->dev = &dev->pci->dev;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	err = vb2_queue_init(q);
133462306a36Sopenharmony_ci	if (err < 0)
133562306a36Sopenharmony_ci		goto fail_unreg;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	q = &dev->vb2_vbiq;
133862306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
133962306a36Sopenharmony_ci	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
134062306a36Sopenharmony_ci	q->gfp_flags = GFP_DMA32;
134162306a36Sopenharmony_ci	q->min_buffers_needed = 2;
134262306a36Sopenharmony_ci	q->drv_priv = dev;
134362306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct cx23885_buffer);
134462306a36Sopenharmony_ci	q->ops = &cx23885_vbi_qops;
134562306a36Sopenharmony_ci	q->mem_ops = &vb2_dma_sg_memops;
134662306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
134762306a36Sopenharmony_ci	q->lock = &dev->lock;
134862306a36Sopenharmony_ci	q->dev = &dev->pci->dev;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	err = vb2_queue_init(q);
135162306a36Sopenharmony_ci	if (err < 0)
135262306a36Sopenharmony_ci		goto fail_unreg;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	/* register Video device */
135562306a36Sopenharmony_ci	dev->video_dev = cx23885_vdev_init(dev, dev->pci,
135662306a36Sopenharmony_ci		&cx23885_video_template, "video");
135762306a36Sopenharmony_ci	dev->video_dev->queue = &dev->vb2_vidq;
135862306a36Sopenharmony_ci	dev->video_dev->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
135962306a36Sopenharmony_ci				      V4L2_CAP_AUDIO | V4L2_CAP_VIDEO_CAPTURE;
136062306a36Sopenharmony_ci	switch (dev->board) { /* i2c device tuners */
136162306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
136262306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR5525:
136362306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
136462306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
136562306a36Sopenharmony_ci		dev->video_dev->device_caps |= V4L2_CAP_TUNER;
136662306a36Sopenharmony_ci		break;
136762306a36Sopenharmony_ci	default:
136862306a36Sopenharmony_ci		if (dev->tuner_type != TUNER_ABSENT)
136962306a36Sopenharmony_ci			dev->video_dev->device_caps |= V4L2_CAP_TUNER;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	err = video_register_device(dev->video_dev, VFL_TYPE_VIDEO,
137362306a36Sopenharmony_ci				    video_nr[dev->nr]);
137462306a36Sopenharmony_ci	if (err < 0) {
137562306a36Sopenharmony_ci		pr_info("%s: can't register video device\n",
137662306a36Sopenharmony_ci			dev->name);
137762306a36Sopenharmony_ci		goto fail_unreg;
137862306a36Sopenharmony_ci	}
137962306a36Sopenharmony_ci	pr_info("%s: registered device %s [v4l2]\n",
138062306a36Sopenharmony_ci	       dev->name, video_device_node_name(dev->video_dev));
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	/* register VBI device */
138362306a36Sopenharmony_ci	dev->vbi_dev = cx23885_vdev_init(dev, dev->pci,
138462306a36Sopenharmony_ci		&cx23885_vbi_template, "vbi");
138562306a36Sopenharmony_ci	dev->vbi_dev->queue = &dev->vb2_vbiq;
138662306a36Sopenharmony_ci	dev->vbi_dev->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
138762306a36Sopenharmony_ci				    V4L2_CAP_AUDIO | V4L2_CAP_VBI_CAPTURE;
138862306a36Sopenharmony_ci	switch (dev->board) { /* i2c device tuners */
138962306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
139062306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_HVR5525:
139162306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
139262306a36Sopenharmony_ci	case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
139362306a36Sopenharmony_ci		dev->vbi_dev->device_caps |= V4L2_CAP_TUNER;
139462306a36Sopenharmony_ci		break;
139562306a36Sopenharmony_ci	default:
139662306a36Sopenharmony_ci		if (dev->tuner_type != TUNER_ABSENT)
139762306a36Sopenharmony_ci			dev->vbi_dev->device_caps |= V4L2_CAP_TUNER;
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci	err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
140062306a36Sopenharmony_ci				    vbi_nr[dev->nr]);
140162306a36Sopenharmony_ci	if (err < 0) {
140262306a36Sopenharmony_ci		pr_info("%s: can't register vbi device\n",
140362306a36Sopenharmony_ci			dev->name);
140462306a36Sopenharmony_ci		goto fail_unreg;
140562306a36Sopenharmony_ci	}
140662306a36Sopenharmony_ci	pr_info("%s: registered device %s\n",
140762306a36Sopenharmony_ci	       dev->name, video_device_node_name(dev->vbi_dev));
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	/* Register ALSA audio device */
141062306a36Sopenharmony_ci	dev->audio_dev = cx23885_audio_register(dev);
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	return 0;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_cifail_unreg:
141562306a36Sopenharmony_ci	cx23885_video_unregister(dev);
141662306a36Sopenharmony_ci	return err;
141762306a36Sopenharmony_ci}
1418