162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  cx18 init/start/stop/exit stream functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Derived from ivtv-streams.c
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
862306a36Sopenharmony_ci *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "cx18-driver.h"
1262306a36Sopenharmony_ci#include "cx18-io.h"
1362306a36Sopenharmony_ci#include "cx18-fileops.h"
1462306a36Sopenharmony_ci#include "cx18-mailbox.h"
1562306a36Sopenharmony_ci#include "cx18-i2c.h"
1662306a36Sopenharmony_ci#include "cx18-queue.h"
1762306a36Sopenharmony_ci#include "cx18-ioctl.h"
1862306a36Sopenharmony_ci#include "cx18-streams.h"
1962306a36Sopenharmony_ci#include "cx18-cards.h"
2062306a36Sopenharmony_ci#include "cx18-scb.h"
2162306a36Sopenharmony_ci#include "cx18-dvb.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define CX18_DSP0_INTERRUPT_MASK	0xd0004C
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic const struct v4l2_file_operations cx18_v4l2_enc_fops = {
2662306a36Sopenharmony_ci	.owner = THIS_MODULE,
2762306a36Sopenharmony_ci	.read = cx18_v4l2_read,
2862306a36Sopenharmony_ci	.open = cx18_v4l2_open,
2962306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
3062306a36Sopenharmony_ci	.release = cx18_v4l2_close,
3162306a36Sopenharmony_ci	.poll = cx18_v4l2_enc_poll,
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic const struct v4l2_file_operations cx18_v4l2_enc_yuv_fops = {
3562306a36Sopenharmony_ci	.owner = THIS_MODULE,
3662306a36Sopenharmony_ci	.open = cx18_v4l2_open,
3762306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
3862306a36Sopenharmony_ci	.release = cx18_v4l2_close,
3962306a36Sopenharmony_ci	.poll = vb2_fop_poll,
4062306a36Sopenharmony_ci	.read = vb2_fop_read,
4162306a36Sopenharmony_ci	.mmap = vb2_fop_mmap,
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* offset from 0 to register ts v4l2 minors on */
4562306a36Sopenharmony_ci#define CX18_V4L2_ENC_TS_OFFSET   16
4662306a36Sopenharmony_ci/* offset from 0 to register pcm v4l2 minors on */
4762306a36Sopenharmony_ci#define CX18_V4L2_ENC_PCM_OFFSET  24
4862306a36Sopenharmony_ci/* offset from 0 to register yuv v4l2 minors on */
4962306a36Sopenharmony_ci#define CX18_V4L2_ENC_YUV_OFFSET  32
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic struct {
5262306a36Sopenharmony_ci	const char *name;
5362306a36Sopenharmony_ci	int vfl_type;
5462306a36Sopenharmony_ci	int num_offset;
5562306a36Sopenharmony_ci	int dma;
5662306a36Sopenharmony_ci	u32 caps;
5762306a36Sopenharmony_ci} cx18_stream_info[] = {
5862306a36Sopenharmony_ci	{	/* CX18_ENC_STREAM_TYPE_MPG */
5962306a36Sopenharmony_ci		"encoder MPEG",
6062306a36Sopenharmony_ci		VFL_TYPE_VIDEO, 0,
6162306a36Sopenharmony_ci		DMA_FROM_DEVICE,
6262306a36Sopenharmony_ci		V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
6362306a36Sopenharmony_ci		V4L2_CAP_AUDIO | V4L2_CAP_TUNER
6462306a36Sopenharmony_ci	},
6562306a36Sopenharmony_ci	{	/* CX18_ENC_STREAM_TYPE_TS */
6662306a36Sopenharmony_ci		"TS",
6762306a36Sopenharmony_ci		VFL_TYPE_VIDEO, -1,
6862306a36Sopenharmony_ci		DMA_FROM_DEVICE,
6962306a36Sopenharmony_ci	},
7062306a36Sopenharmony_ci	{	/* CX18_ENC_STREAM_TYPE_YUV */
7162306a36Sopenharmony_ci		"encoder YUV",
7262306a36Sopenharmony_ci		VFL_TYPE_VIDEO, CX18_V4L2_ENC_YUV_OFFSET,
7362306a36Sopenharmony_ci		DMA_FROM_DEVICE,
7462306a36Sopenharmony_ci		V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
7562306a36Sopenharmony_ci		V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | V4L2_CAP_TUNER
7662306a36Sopenharmony_ci	},
7762306a36Sopenharmony_ci	{	/* CX18_ENC_STREAM_TYPE_VBI */
7862306a36Sopenharmony_ci		"encoder VBI",
7962306a36Sopenharmony_ci		VFL_TYPE_VBI, 0,
8062306a36Sopenharmony_ci		DMA_FROM_DEVICE,
8162306a36Sopenharmony_ci		V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE |
8262306a36Sopenharmony_ci		V4L2_CAP_READWRITE | V4L2_CAP_AUDIO | V4L2_CAP_TUNER
8362306a36Sopenharmony_ci	},
8462306a36Sopenharmony_ci	{	/* CX18_ENC_STREAM_TYPE_PCM */
8562306a36Sopenharmony_ci		"encoder PCM audio",
8662306a36Sopenharmony_ci		VFL_TYPE_VIDEO, CX18_V4L2_ENC_PCM_OFFSET,
8762306a36Sopenharmony_ci		DMA_FROM_DEVICE,
8862306a36Sopenharmony_ci		V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
8962306a36Sopenharmony_ci	},
9062306a36Sopenharmony_ci	{	/* CX18_ENC_STREAM_TYPE_IDX */
9162306a36Sopenharmony_ci		"encoder IDX",
9262306a36Sopenharmony_ci		VFL_TYPE_VIDEO, -1,
9362306a36Sopenharmony_ci		DMA_FROM_DEVICE,
9462306a36Sopenharmony_ci	},
9562306a36Sopenharmony_ci	{	/* CX18_ENC_STREAM_TYPE_RAD */
9662306a36Sopenharmony_ci		"encoder radio",
9762306a36Sopenharmony_ci		VFL_TYPE_RADIO, 0,
9862306a36Sopenharmony_ci		DMA_NONE,
9962306a36Sopenharmony_ci		V4L2_CAP_RADIO | V4L2_CAP_TUNER
10062306a36Sopenharmony_ci	},
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int cx18_queue_setup(struct vb2_queue *vq,
10462306a36Sopenharmony_ci			    unsigned int *nbuffers, unsigned int *nplanes,
10562306a36Sopenharmony_ci			    unsigned int sizes[], struct device *alloc_devs[])
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct cx18_stream *s = vb2_get_drv_priv(vq);
10862306a36Sopenharmony_ci	struct cx18 *cx = s->cx;
10962306a36Sopenharmony_ci	unsigned int szimage;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/*
11262306a36Sopenharmony_ci	 * HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
11362306a36Sopenharmony_ci	 * UYUV YUV size is (Y=(h*720) + UV=(h*(720)))
11462306a36Sopenharmony_ci	 */
11562306a36Sopenharmony_ci	if (s->pixelformat == V4L2_PIX_FMT_NV12_16L16)
11662306a36Sopenharmony_ci		szimage = cx->cxhdl.height * 720 * 3 / 2;
11762306a36Sopenharmony_ci	else
11862306a36Sopenharmony_ci		szimage = cx->cxhdl.height * 720 * 2;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/*
12162306a36Sopenharmony_ci	 * Let's request at least three buffers: two for the
12262306a36Sopenharmony_ci	 * DMA engine and one for userspace.
12362306a36Sopenharmony_ci	 */
12462306a36Sopenharmony_ci	if (vq->num_buffers + *nbuffers < 3)
12562306a36Sopenharmony_ci		*nbuffers = 3 - vq->num_buffers;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (*nplanes) {
12862306a36Sopenharmony_ci		if (*nplanes != 1 || sizes[0] < szimage)
12962306a36Sopenharmony_ci			return -EINVAL;
13062306a36Sopenharmony_ci		return 0;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	sizes[0] = szimage;
13462306a36Sopenharmony_ci	*nplanes = 1;
13562306a36Sopenharmony_ci	return 0;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void cx18_buf_queue(struct vb2_buffer *vb)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct cx18_stream *s = vb2_get_drv_priv(vb->vb2_queue);
14162306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
14262306a36Sopenharmony_ci	struct cx18_vb2_buffer *buf =
14362306a36Sopenharmony_ci		container_of(vbuf, struct cx18_vb2_buffer, vb);
14462306a36Sopenharmony_ci	unsigned long flags;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	spin_lock_irqsave(&s->vb_lock, flags);
14762306a36Sopenharmony_ci	list_add_tail(&buf->list, &s->vb_capture);
14862306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->vb_lock, flags);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int cx18_buf_prepare(struct vb2_buffer *vb)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
15462306a36Sopenharmony_ci	struct cx18_stream *s = vb2_get_drv_priv(vb->vb2_queue);
15562306a36Sopenharmony_ci	struct cx18 *cx = s->cx;
15662306a36Sopenharmony_ci	unsigned int size;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/*
15962306a36Sopenharmony_ci	 * HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
16062306a36Sopenharmony_ci	 * UYUV YUV size is (Y=(h*720) + UV=(h*(720)))
16162306a36Sopenharmony_ci	 */
16262306a36Sopenharmony_ci	if (s->pixelformat == V4L2_PIX_FMT_NV12_16L16)
16362306a36Sopenharmony_ci		size = cx->cxhdl.height * 720 * 3 / 2;
16462306a36Sopenharmony_ci	else
16562306a36Sopenharmony_ci		size = cx->cxhdl.height * 720 * 2;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size)
16862306a36Sopenharmony_ci		return -EINVAL;
16962306a36Sopenharmony_ci	vb2_set_plane_payload(vb, 0, size);
17062306a36Sopenharmony_ci	vbuf->field = V4L2_FIELD_INTERLACED;
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_civoid cx18_clear_queue(struct cx18_stream *s, enum vb2_buffer_state state)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	while (!list_empty(&s->vb_capture)) {
17762306a36Sopenharmony_ci		struct cx18_vb2_buffer *buf;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		buf = list_first_entry(&s->vb_capture,
18062306a36Sopenharmony_ci				       struct cx18_vb2_buffer, list);
18162306a36Sopenharmony_ci		list_del(&buf->list);
18262306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, state);
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic int cx18_start_streaming(struct vb2_queue *vq, unsigned int count)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct v4l2_fh *owner = vq->owner;
18962306a36Sopenharmony_ci	struct cx18_stream *s = vb2_get_drv_priv(vq);
19062306a36Sopenharmony_ci	unsigned long flags;
19162306a36Sopenharmony_ci	int rc;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (WARN_ON(!owner)) {
19462306a36Sopenharmony_ci		rc = -EIO;
19562306a36Sopenharmony_ci		goto clear_queue;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	s->sequence = 0;
19962306a36Sopenharmony_ci	rc = cx18_start_capture(fh2id(owner));
20062306a36Sopenharmony_ci	if (!rc) {
20162306a36Sopenharmony_ci		/* Establish a buffer timeout */
20262306a36Sopenharmony_ci		mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies);
20362306a36Sopenharmony_ci		return 0;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ciclear_queue:
20762306a36Sopenharmony_ci	spin_lock_irqsave(&s->vb_lock, flags);
20862306a36Sopenharmony_ci	cx18_clear_queue(s, VB2_BUF_STATE_QUEUED);
20962306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->vb_lock, flags);
21062306a36Sopenharmony_ci	return rc;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void cx18_stop_streaming(struct vb2_queue *vq)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct cx18_stream *s = vb2_get_drv_priv(vq);
21662306a36Sopenharmony_ci	unsigned long flags;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	cx18_stop_capture(s, 0);
21962306a36Sopenharmony_ci	timer_delete_sync(&s->vb_timeout);
22062306a36Sopenharmony_ci	spin_lock_irqsave(&s->vb_lock, flags);
22162306a36Sopenharmony_ci	cx18_clear_queue(s, VB2_BUF_STATE_ERROR);
22262306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->vb_lock, flags);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic const struct vb2_ops cx18_vb2_qops = {
22662306a36Sopenharmony_ci	.queue_setup		= cx18_queue_setup,
22762306a36Sopenharmony_ci	.buf_queue		= cx18_buf_queue,
22862306a36Sopenharmony_ci	.buf_prepare		= cx18_buf_prepare,
22962306a36Sopenharmony_ci	.start_streaming	= cx18_start_streaming,
23062306a36Sopenharmony_ci	.stop_streaming		= cx18_stop_streaming,
23162306a36Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
23262306a36Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
23362306a36Sopenharmony_ci};
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int cx18_stream_init(struct cx18 *cx, int type)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct cx18_stream *s = &cx->streams[type];
23862306a36Sopenharmony_ci	int err = 0;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	memset(s, 0, sizeof(*s));
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* initialize cx18_stream fields */
24362306a36Sopenharmony_ci	s->dvb = NULL;
24462306a36Sopenharmony_ci	s->cx = cx;
24562306a36Sopenharmony_ci	s->type = type;
24662306a36Sopenharmony_ci	s->name = cx18_stream_info[type].name;
24762306a36Sopenharmony_ci	s->handle = CX18_INVALID_TASK_HANDLE;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	s->dma = cx18_stream_info[type].dma;
25062306a36Sopenharmony_ci	s->v4l2_dev_caps = cx18_stream_info[type].caps;
25162306a36Sopenharmony_ci	s->buffers = cx->stream_buffers[type];
25262306a36Sopenharmony_ci	s->buf_size = cx->stream_buf_size[type];
25362306a36Sopenharmony_ci	INIT_LIST_HEAD(&s->buf_pool);
25462306a36Sopenharmony_ci	s->bufs_per_mdl = 1;
25562306a36Sopenharmony_ci	s->mdl_size = s->buf_size * s->bufs_per_mdl;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	init_waitqueue_head(&s->waitq);
25862306a36Sopenharmony_ci	s->id = -1;
25962306a36Sopenharmony_ci	spin_lock_init(&s->q_free.lock);
26062306a36Sopenharmony_ci	cx18_queue_init(&s->q_free);
26162306a36Sopenharmony_ci	spin_lock_init(&s->q_busy.lock);
26262306a36Sopenharmony_ci	cx18_queue_init(&s->q_busy);
26362306a36Sopenharmony_ci	spin_lock_init(&s->q_full.lock);
26462306a36Sopenharmony_ci	cx18_queue_init(&s->q_full);
26562306a36Sopenharmony_ci	spin_lock_init(&s->q_idle.lock);
26662306a36Sopenharmony_ci	cx18_queue_init(&s->q_idle);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	INIT_WORK(&s->out_work_order, cx18_out_work_handler);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	INIT_LIST_HEAD(&s->vb_capture);
27162306a36Sopenharmony_ci	timer_setup(&s->vb_timeout, cx18_vb_timeout, 0);
27262306a36Sopenharmony_ci	spin_lock_init(&s->vb_lock);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (type == CX18_ENC_STREAM_TYPE_YUV) {
27562306a36Sopenharmony_ci		s->vb_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		/* Assume the previous pixel default */
27862306a36Sopenharmony_ci		s->pixelformat = V4L2_PIX_FMT_NV12_16L16;
27962306a36Sopenharmony_ci		s->vb_bytes_per_frame = cx->cxhdl.height * 720 * 3 / 2;
28062306a36Sopenharmony_ci		s->vb_bytes_per_line = 720;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		s->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
28362306a36Sopenharmony_ci		s->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
28462306a36Sopenharmony_ci		s->vidq.drv_priv = s;
28562306a36Sopenharmony_ci		s->vidq.buf_struct_size = sizeof(struct cx18_vb2_buffer);
28662306a36Sopenharmony_ci		s->vidq.ops = &cx18_vb2_qops;
28762306a36Sopenharmony_ci		s->vidq.mem_ops = &vb2_vmalloc_memops;
28862306a36Sopenharmony_ci		s->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
28962306a36Sopenharmony_ci		s->vidq.min_buffers_needed = 2;
29062306a36Sopenharmony_ci		s->vidq.gfp_flags = GFP_DMA32;
29162306a36Sopenharmony_ci		s->vidq.dev = &cx->pci_dev->dev;
29262306a36Sopenharmony_ci		s->vidq.lock = &cx->serialize_lock;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		err = vb2_queue_init(&s->vidq);
29562306a36Sopenharmony_ci		if (err)
29662306a36Sopenharmony_ci			v4l2_err(&cx->v4l2_dev, "cannot init vb2 queue\n");
29762306a36Sopenharmony_ci		s->video_dev.queue = &s->vidq;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci	return err;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int cx18_prep_dev(struct cx18 *cx, int type)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct cx18_stream *s = &cx->streams[type];
30562306a36Sopenharmony_ci	u32 cap = cx->v4l2_cap;
30662306a36Sopenharmony_ci	int num_offset = cx18_stream_info[type].num_offset;
30762306a36Sopenharmony_ci	int num = cx->instance + cx18_first_minor + num_offset;
30862306a36Sopenharmony_ci	int err;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/*
31162306a36Sopenharmony_ci	 * These five fields are always initialized.
31262306a36Sopenharmony_ci	 * For analog capture related streams, if video_dev.v4l2_dev == NULL then the
31362306a36Sopenharmony_ci	 * stream is not in use.
31462306a36Sopenharmony_ci	 * For the TS stream, if dvb == NULL then the stream is not in use.
31562306a36Sopenharmony_ci	 * In those cases no other fields but these four can be used.
31662306a36Sopenharmony_ci	 */
31762306a36Sopenharmony_ci	s->video_dev.v4l2_dev = NULL;
31862306a36Sopenharmony_ci	s->dvb = NULL;
31962306a36Sopenharmony_ci	s->cx = cx;
32062306a36Sopenharmony_ci	s->type = type;
32162306a36Sopenharmony_ci	s->name = cx18_stream_info[type].name;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* Check whether the radio is supported */
32462306a36Sopenharmony_ci	if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO))
32562306a36Sopenharmony_ci		return 0;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* Check whether VBI is supported */
32862306a36Sopenharmony_ci	if (type == CX18_ENC_STREAM_TYPE_VBI &&
32962306a36Sopenharmony_ci	    !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
33062306a36Sopenharmony_ci		return 0;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* User explicitly selected 0 buffers for these streams, so don't
33362306a36Sopenharmony_ci	   create them. */
33462306a36Sopenharmony_ci	if (cx18_stream_info[type].dma != DMA_NONE &&
33562306a36Sopenharmony_ci	    cx->stream_buffers[type] == 0) {
33662306a36Sopenharmony_ci		CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
33762306a36Sopenharmony_ci		return 0;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	err = cx18_stream_init(cx, type);
34162306a36Sopenharmony_ci	if (err)
34262306a36Sopenharmony_ci		return err;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* Allocate the cx18_dvb struct only for the TS on cards with DTV */
34562306a36Sopenharmony_ci	if (type == CX18_ENC_STREAM_TYPE_TS) {
34662306a36Sopenharmony_ci		if (cx->card->hw_all & CX18_HW_DVB) {
34762306a36Sopenharmony_ci			s->dvb = kzalloc(sizeof(struct cx18_dvb), GFP_KERNEL);
34862306a36Sopenharmony_ci			if (s->dvb == NULL) {
34962306a36Sopenharmony_ci				CX18_ERR("Couldn't allocate cx18_dvb structure for %s\n",
35062306a36Sopenharmony_ci					 s->name);
35162306a36Sopenharmony_ci				return -ENOMEM;
35262306a36Sopenharmony_ci			}
35362306a36Sopenharmony_ci		} else {
35462306a36Sopenharmony_ci			/* Don't need buffers for the TS, if there is no DVB */
35562306a36Sopenharmony_ci			s->buffers = 0;
35662306a36Sopenharmony_ci		}
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (num_offset == -1)
36062306a36Sopenharmony_ci		return 0;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* initialize the v4l2 video device structure */
36362306a36Sopenharmony_ci	snprintf(s->video_dev.name, sizeof(s->video_dev.name), "%s %s",
36462306a36Sopenharmony_ci		 cx->v4l2_dev.name, s->name);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	s->video_dev.num = num;
36762306a36Sopenharmony_ci	s->video_dev.v4l2_dev = &cx->v4l2_dev;
36862306a36Sopenharmony_ci	if (type == CX18_ENC_STREAM_TYPE_YUV)
36962306a36Sopenharmony_ci		s->video_dev.fops = &cx18_v4l2_enc_yuv_fops;
37062306a36Sopenharmony_ci	else
37162306a36Sopenharmony_ci		s->video_dev.fops = &cx18_v4l2_enc_fops;
37262306a36Sopenharmony_ci	s->video_dev.release = video_device_release_empty;
37362306a36Sopenharmony_ci	if (cx->card->video_inputs->video_type == CX18_CARD_INPUT_VID_TUNER)
37462306a36Sopenharmony_ci		s->video_dev.tvnorms = cx->tuner_std;
37562306a36Sopenharmony_ci	else
37662306a36Sopenharmony_ci		s->video_dev.tvnorms = V4L2_STD_ALL;
37762306a36Sopenharmony_ci	s->video_dev.lock = &cx->serialize_lock;
37862306a36Sopenharmony_ci	cx18_set_funcs(&s->video_dev);
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/* Initialize v4l2 variables and register v4l2 devices */
38362306a36Sopenharmony_ciint cx18_streams_setup(struct cx18 *cx)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	int type, ret;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* Setup V4L2 Devices */
38862306a36Sopenharmony_ci	for (type = 0; type < CX18_MAX_STREAMS; type++) {
38962306a36Sopenharmony_ci		/* Prepare device */
39062306a36Sopenharmony_ci		ret = cx18_prep_dev(cx, type);
39162306a36Sopenharmony_ci		if (ret < 0)
39262306a36Sopenharmony_ci			break;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		/* Allocate Stream */
39562306a36Sopenharmony_ci		ret = cx18_stream_alloc(&cx->streams[type]);
39662306a36Sopenharmony_ci		if (ret < 0)
39762306a36Sopenharmony_ci			break;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci	if (type == CX18_MAX_STREAMS)
40062306a36Sopenharmony_ci		return 0;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* One or more streams could not be initialized. Clean 'em all up. */
40362306a36Sopenharmony_ci	cx18_streams_cleanup(cx, 0);
40462306a36Sopenharmony_ci	return ret;
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic int cx18_reg_dev(struct cx18 *cx, int type)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct cx18_stream *s = &cx->streams[type];
41062306a36Sopenharmony_ci	int vfl_type = cx18_stream_info[type].vfl_type;
41162306a36Sopenharmony_ci	const char *name;
41262306a36Sopenharmony_ci	int num, ret;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (type == CX18_ENC_STREAM_TYPE_TS && s->dvb != NULL) {
41562306a36Sopenharmony_ci		ret = cx18_dvb_register(s);
41662306a36Sopenharmony_ci		if (ret < 0) {
41762306a36Sopenharmony_ci			CX18_ERR("DVB failed to register\n");
41862306a36Sopenharmony_ci			return ret;
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (s->video_dev.v4l2_dev == NULL)
42362306a36Sopenharmony_ci		return 0;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	num = s->video_dev.num;
42662306a36Sopenharmony_ci	s->video_dev.device_caps = s->v4l2_dev_caps;	/* device capabilities */
42762306a36Sopenharmony_ci	/* card number + user defined offset + device offset */
42862306a36Sopenharmony_ci	if (type != CX18_ENC_STREAM_TYPE_MPG) {
42962306a36Sopenharmony_ci		struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		if (s_mpg->video_dev.v4l2_dev)
43262306a36Sopenharmony_ci			num = s_mpg->video_dev.num
43362306a36Sopenharmony_ci			    + cx18_stream_info[type].num_offset;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci	video_set_drvdata(&s->video_dev, s);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/* Register device. First try the desired minor, then any free one. */
43862306a36Sopenharmony_ci	ret = video_register_device_no_warn(&s->video_dev, vfl_type, num);
43962306a36Sopenharmony_ci	if (ret < 0) {
44062306a36Sopenharmony_ci		CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
44162306a36Sopenharmony_ci			s->name, num);
44262306a36Sopenharmony_ci		s->video_dev.v4l2_dev = NULL;
44362306a36Sopenharmony_ci		return ret;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	name = video_device_node_name(&s->video_dev);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	switch (vfl_type) {
44962306a36Sopenharmony_ci	case VFL_TYPE_VIDEO:
45062306a36Sopenharmony_ci		CX18_INFO("Registered device %s for %s (%d x %d.%02d kB)\n",
45162306a36Sopenharmony_ci			  name, s->name, cx->stream_buffers[type],
45262306a36Sopenharmony_ci			  cx->stream_buf_size[type] / 1024,
45362306a36Sopenharmony_ci			  (cx->stream_buf_size[type] * 100 / 1024) % 100);
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	case VFL_TYPE_RADIO:
45762306a36Sopenharmony_ci		CX18_INFO("Registered device %s for %s\n", name, s->name);
45862306a36Sopenharmony_ci		break;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	case VFL_TYPE_VBI:
46162306a36Sopenharmony_ci		if (cx->stream_buffers[type])
46262306a36Sopenharmony_ci			CX18_INFO("Registered device %s for %s (%d x %d bytes)\n",
46362306a36Sopenharmony_ci				  name, s->name, cx->stream_buffers[type],
46462306a36Sopenharmony_ci				  cx->stream_buf_size[type]);
46562306a36Sopenharmony_ci		else
46662306a36Sopenharmony_ci			CX18_INFO("Registered device %s for %s\n",
46762306a36Sopenharmony_ci				name, s->name);
46862306a36Sopenharmony_ci		break;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	return 0;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci/* Register v4l2 devices */
47562306a36Sopenharmony_ciint cx18_streams_register(struct cx18 *cx)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	int type;
47862306a36Sopenharmony_ci	int err;
47962306a36Sopenharmony_ci	int ret = 0;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* Register V4L2 devices */
48262306a36Sopenharmony_ci	for (type = 0; type < CX18_MAX_STREAMS; type++) {
48362306a36Sopenharmony_ci		err = cx18_reg_dev(cx, type);
48462306a36Sopenharmony_ci		if (err && ret == 0)
48562306a36Sopenharmony_ci			ret = err;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if (ret == 0)
48962306a36Sopenharmony_ci		return 0;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	/* One or more streams could not be initialized. Clean 'em all up. */
49262306a36Sopenharmony_ci	cx18_streams_cleanup(cx, 1);
49362306a36Sopenharmony_ci	return ret;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci/* Unregister v4l2 devices */
49762306a36Sopenharmony_civoid cx18_streams_cleanup(struct cx18 *cx, int unregister)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct video_device *vdev;
50062306a36Sopenharmony_ci	int type;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* Teardown all streams */
50362306a36Sopenharmony_ci	for (type = 0; type < CX18_MAX_STREAMS; type++) {
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci		/* The TS has a cx18_dvb structure, not a video_device */
50662306a36Sopenharmony_ci		if (type == CX18_ENC_STREAM_TYPE_TS) {
50762306a36Sopenharmony_ci			if (cx->streams[type].dvb != NULL) {
50862306a36Sopenharmony_ci				if (unregister)
50962306a36Sopenharmony_ci					cx18_dvb_unregister(&cx->streams[type]);
51062306a36Sopenharmony_ci				kfree(cx->streams[type].dvb);
51162306a36Sopenharmony_ci				cx->streams[type].dvb = NULL;
51262306a36Sopenharmony_ci				cx18_stream_free(&cx->streams[type]);
51362306a36Sopenharmony_ci			}
51462306a36Sopenharmony_ci			continue;
51562306a36Sopenharmony_ci		}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		/* No struct video_device, but can have buffers allocated */
51862306a36Sopenharmony_ci		if (type == CX18_ENC_STREAM_TYPE_IDX) {
51962306a36Sopenharmony_ci			/* If the module params didn't inhibit IDX ... */
52062306a36Sopenharmony_ci			if (cx->stream_buffers[type] != 0) {
52162306a36Sopenharmony_ci				cx->stream_buffers[type] = 0;
52262306a36Sopenharmony_ci				/*
52362306a36Sopenharmony_ci				 * Before calling cx18_stream_free(),
52462306a36Sopenharmony_ci				 * check if the IDX stream was actually set up.
52562306a36Sopenharmony_ci				 * Needed, since the cx18_probe() error path
52662306a36Sopenharmony_ci				 * exits through here as well as normal clean up
52762306a36Sopenharmony_ci				 */
52862306a36Sopenharmony_ci				if (cx->streams[type].buffers != 0)
52962306a36Sopenharmony_ci					cx18_stream_free(&cx->streams[type]);
53062306a36Sopenharmony_ci			}
53162306a36Sopenharmony_ci			continue;
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		/* If struct video_device exists, can have buffers allocated */
53562306a36Sopenharmony_ci		vdev = &cx->streams[type].video_dev;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci		if (vdev->v4l2_dev == NULL)
53862306a36Sopenharmony_ci			continue;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		cx18_stream_free(&cx->streams[type]);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		if (type == CX18_ENC_STREAM_TYPE_YUV)
54362306a36Sopenharmony_ci			vb2_video_unregister_device(vdev);
54462306a36Sopenharmony_ci		else
54562306a36Sopenharmony_ci			video_unregister_device(vdev);
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void cx18_vbi_setup(struct cx18_stream *s)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct cx18 *cx = s->cx;
55262306a36Sopenharmony_ci	int raw = cx18_raw_vbi(cx);
55362306a36Sopenharmony_ci	u32 data[CX2341X_MBOX_MAX_DATA];
55462306a36Sopenharmony_ci	int lines;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (cx->is_60hz) {
55762306a36Sopenharmony_ci		cx->vbi.count = 12;
55862306a36Sopenharmony_ci		cx->vbi.start[0] = 10;
55962306a36Sopenharmony_ci		cx->vbi.start[1] = 273;
56062306a36Sopenharmony_ci	} else {        /* PAL/SECAM */
56162306a36Sopenharmony_ci		cx->vbi.count = 18;
56262306a36Sopenharmony_ci		cx->vbi.start[0] = 6;
56362306a36Sopenharmony_ci		cx->vbi.start[1] = 318;
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	/* setup VBI registers */
56762306a36Sopenharmony_ci	if (raw)
56862306a36Sopenharmony_ci		v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi);
56962306a36Sopenharmony_ci	else
57062306a36Sopenharmony_ci		v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/*
57362306a36Sopenharmony_ci	 * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw
57462306a36Sopenharmony_ci	 * VBI when the first analog capture channel starts, as once it starts
57562306a36Sopenharmony_ci	 * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup
57662306a36Sopenharmony_ci	 * (i.e. for the VBI capture channels).  We also send it for each
57762306a36Sopenharmony_ci	 * analog capture channel anyway just to make sure we get the proper
57862306a36Sopenharmony_ci	 * behavior
57962306a36Sopenharmony_ci	 */
58062306a36Sopenharmony_ci	if (raw) {
58162306a36Sopenharmony_ci		lines = cx->vbi.count * 2;
58262306a36Sopenharmony_ci	} else {
58362306a36Sopenharmony_ci		/*
58462306a36Sopenharmony_ci		 * For 525/60 systems, according to the VIP 2 & BT.656 std:
58562306a36Sopenharmony_ci		 * The EAV RP code's Field bit toggles on line 4, a few lines
58662306a36Sopenharmony_ci		 * after the Vertcal Blank bit has already toggled.
58762306a36Sopenharmony_ci		 * Tell the encoder to capture 21-4+1=18 lines per field,
58862306a36Sopenharmony_ci		 * since we want lines 10 through 21.
58962306a36Sopenharmony_ci		 *
59062306a36Sopenharmony_ci		 * For 625/50 systems, according to the VIP 2 & BT.656 std:
59162306a36Sopenharmony_ci		 * The EAV RP code's Field bit toggles on line 1, a few lines
59262306a36Sopenharmony_ci		 * after the Vertcal Blank bit has already toggled.
59362306a36Sopenharmony_ci		 * (We've actually set the digitizer so that the Field bit
59462306a36Sopenharmony_ci		 * toggles on line 2.) Tell the encoder to capture 23-2+1=22
59562306a36Sopenharmony_ci		 * lines per field, since we want lines 6 through 23.
59662306a36Sopenharmony_ci		 */
59762306a36Sopenharmony_ci		lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	data[0] = s->handle;
60162306a36Sopenharmony_ci	/* Lines per field */
60262306a36Sopenharmony_ci	data[1] = (lines / 2) | ((lines / 2) << 16);
60362306a36Sopenharmony_ci	/* bytes per line */
60462306a36Sopenharmony_ci	data[2] = (raw ? VBI_ACTIVE_SAMPLES
60562306a36Sopenharmony_ci		       : (cx->is_60hz ? VBI_HBLANK_SAMPLES_60HZ
60662306a36Sopenharmony_ci				      : VBI_HBLANK_SAMPLES_50HZ));
60762306a36Sopenharmony_ci	/* Every X number of frames a VBI interrupt arrives
60862306a36Sopenharmony_ci	   (frames as in 25 or 30 fps) */
60962306a36Sopenharmony_ci	data[3] = 1;
61062306a36Sopenharmony_ci	/*
61162306a36Sopenharmony_ci	 * Set the SAV/EAV RP codes to look for as start/stop points
61262306a36Sopenharmony_ci	 * when in VIP-1.1 mode
61362306a36Sopenharmony_ci	 */
61462306a36Sopenharmony_ci	if (raw) {
61562306a36Sopenharmony_ci		/*
61662306a36Sopenharmony_ci		 * Start codes for beginning of "active" line in vertical blank
61762306a36Sopenharmony_ci		 * 0x20 (               VerticalBlank                )
61862306a36Sopenharmony_ci		 * 0x60 (     EvenField VerticalBlank                )
61962306a36Sopenharmony_ci		 */
62062306a36Sopenharmony_ci		data[4] = 0x20602060;
62162306a36Sopenharmony_ci		/*
62262306a36Sopenharmony_ci		 * End codes for end of "active" raw lines and regular lines
62362306a36Sopenharmony_ci		 * 0x30 (               VerticalBlank HorizontalBlank)
62462306a36Sopenharmony_ci		 * 0x70 (     EvenField VerticalBlank HorizontalBlank)
62562306a36Sopenharmony_ci		 * 0x90 (Task                         HorizontalBlank)
62662306a36Sopenharmony_ci		 * 0xd0 (Task EvenField               HorizontalBlank)
62762306a36Sopenharmony_ci		 */
62862306a36Sopenharmony_ci		data[5] = 0x307090d0;
62962306a36Sopenharmony_ci	} else {
63062306a36Sopenharmony_ci		/*
63162306a36Sopenharmony_ci		 * End codes for active video, we want data in the hblank region
63262306a36Sopenharmony_ci		 * 0xb0 (Task         0 VerticalBlank HorizontalBlank)
63362306a36Sopenharmony_ci		 * 0xf0 (Task EvenField VerticalBlank HorizontalBlank)
63462306a36Sopenharmony_ci		 *
63562306a36Sopenharmony_ci		 * Since the V bit is only allowed to toggle in the EAV RP code,
63662306a36Sopenharmony_ci		 * just before the first active region line, these two
63762306a36Sopenharmony_ci		 * are problematic:
63862306a36Sopenharmony_ci		 * 0x90 (Task                         HorizontalBlank)
63962306a36Sopenharmony_ci		 * 0xd0 (Task EvenField               HorizontalBlank)
64062306a36Sopenharmony_ci		 *
64162306a36Sopenharmony_ci		 * We have set the digitzer such that we don't have to worry
64262306a36Sopenharmony_ci		 * about these problem codes.
64362306a36Sopenharmony_ci		 */
64462306a36Sopenharmony_ci		data[4] = 0xB0F0B0F0;
64562306a36Sopenharmony_ci		/*
64662306a36Sopenharmony_ci		 * Start codes for beginning of active line in vertical blank
64762306a36Sopenharmony_ci		 * 0xa0 (Task           VerticalBlank                )
64862306a36Sopenharmony_ci		 * 0xe0 (Task EvenField VerticalBlank                )
64962306a36Sopenharmony_ci		 */
65062306a36Sopenharmony_ci		data[5] = 0xA0E0A0E0;
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n",
65462306a36Sopenharmony_ci			data[0], data[1], data[2], data[3], data[4], data[5]);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_civoid cx18_stream_rotate_idx_mdls(struct cx18 *cx)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
66262306a36Sopenharmony_ci	struct cx18_mdl *mdl;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (!cx18_stream_enabled(s))
66562306a36Sopenharmony_ci		return;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	/* Return if the firmware is not running low on MDLs */
66862306a36Sopenharmony_ci	if ((atomic_read(&s->q_free.depth) + atomic_read(&s->q_busy.depth)) >=
66962306a36Sopenharmony_ci					    CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN)
67062306a36Sopenharmony_ci		return;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	/* Return if there are no MDLs to rotate back to the firmware */
67362306a36Sopenharmony_ci	if (atomic_read(&s->q_full.depth) < 2)
67462306a36Sopenharmony_ci		return;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/*
67762306a36Sopenharmony_ci	 * Take the oldest IDX MDL still holding data, and discard its index
67862306a36Sopenharmony_ci	 * entries by scheduling the MDL to go back to the firmware
67962306a36Sopenharmony_ci	 */
68062306a36Sopenharmony_ci	mdl = cx18_dequeue(s, &s->q_full);
68162306a36Sopenharmony_ci	if (mdl != NULL)
68262306a36Sopenharmony_ci		cx18_enqueue(s, mdl, &s->q_free);
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cistatic
68662306a36Sopenharmony_cistruct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s,
68762306a36Sopenharmony_ci					   struct cx18_mdl *mdl)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	struct cx18 *cx = s->cx;
69062306a36Sopenharmony_ci	struct cx18_queue *q;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* Don't give it to the firmware, if we're not running a capture */
69362306a36Sopenharmony_ci	if (s->handle == CX18_INVALID_TASK_HANDLE ||
69462306a36Sopenharmony_ci	    test_bit(CX18_F_S_STOPPING, &s->s_flags) ||
69562306a36Sopenharmony_ci	    !test_bit(CX18_F_S_STREAMING, &s->s_flags))
69662306a36Sopenharmony_ci		return cx18_enqueue(s, mdl, &s->q_free);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	q = cx18_enqueue(s, mdl, &s->q_busy);
69962306a36Sopenharmony_ci	if (q != &s->q_busy)
70062306a36Sopenharmony_ci		return q; /* The firmware has the max MDLs it can handle */
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	cx18_mdl_sync_for_device(s, mdl);
70362306a36Sopenharmony_ci	cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
70462306a36Sopenharmony_ci		  (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem,
70562306a36Sopenharmony_ci		  s->bufs_per_mdl, mdl->id, s->mdl_size);
70662306a36Sopenharmony_ci	return q;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_cistatic
71062306a36Sopenharmony_civoid _cx18_stream_load_fw_queue(struct cx18_stream *s)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	struct cx18_queue *q;
71362306a36Sopenharmony_ci	struct cx18_mdl *mdl;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	if (atomic_read(&s->q_free.depth) == 0 ||
71662306a36Sopenharmony_ci	    atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
71762306a36Sopenharmony_ci		return;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* Move from q_free to q_busy notifying the firmware, until the limit */
72062306a36Sopenharmony_ci	do {
72162306a36Sopenharmony_ci		mdl = cx18_dequeue(s, &s->q_free);
72262306a36Sopenharmony_ci		if (mdl == NULL)
72362306a36Sopenharmony_ci			break;
72462306a36Sopenharmony_ci		q = _cx18_stream_put_mdl_fw(s, mdl);
72562306a36Sopenharmony_ci	} while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM
72662306a36Sopenharmony_ci		 && q == &s->q_busy);
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_civoid cx18_out_work_handler(struct work_struct *work)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	struct cx18_stream *s =
73262306a36Sopenharmony_ci			 container_of(work, struct cx18_stream, out_work_order);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	_cx18_stream_load_fw_queue(s);
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic void cx18_stream_configure_mdls(struct cx18_stream *s)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	cx18_unload_queues(s);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	switch (s->type) {
74262306a36Sopenharmony_ci	case CX18_ENC_STREAM_TYPE_YUV:
74362306a36Sopenharmony_ci		/*
74462306a36Sopenharmony_ci		 * Height should be a multiple of 32 lines.
74562306a36Sopenharmony_ci		 * Set the MDL size to the exact size needed for one frame.
74662306a36Sopenharmony_ci		 * Use enough buffers per MDL to cover the MDL size
74762306a36Sopenharmony_ci		 */
74862306a36Sopenharmony_ci		if (s->pixelformat == V4L2_PIX_FMT_NV12_16L16)
74962306a36Sopenharmony_ci			s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2;
75062306a36Sopenharmony_ci		else
75162306a36Sopenharmony_ci			s->mdl_size = 720 * s->cx->cxhdl.height * 2;
75262306a36Sopenharmony_ci		s->bufs_per_mdl = s->mdl_size / s->buf_size;
75362306a36Sopenharmony_ci		if (s->mdl_size % s->buf_size)
75462306a36Sopenharmony_ci			s->bufs_per_mdl++;
75562306a36Sopenharmony_ci		break;
75662306a36Sopenharmony_ci	case CX18_ENC_STREAM_TYPE_VBI:
75762306a36Sopenharmony_ci		s->bufs_per_mdl = 1;
75862306a36Sopenharmony_ci		if  (cx18_raw_vbi(s->cx)) {
75962306a36Sopenharmony_ci			s->mdl_size = (s->cx->is_60hz ? 12 : 18)
76062306a36Sopenharmony_ci						       * 2 * VBI_ACTIVE_SAMPLES;
76162306a36Sopenharmony_ci		} else {
76262306a36Sopenharmony_ci			/*
76362306a36Sopenharmony_ci			 * See comment in cx18_vbi_setup() below about the
76462306a36Sopenharmony_ci			 * extra lines we capture in sliced VBI mode due to
76562306a36Sopenharmony_ci			 * the lines on which EAV RP codes toggle.
76662306a36Sopenharmony_ci			*/
76762306a36Sopenharmony_ci			s->mdl_size = s->cx->is_60hz
76862306a36Sopenharmony_ci				   ? (21 - 4 + 1) * 2 * VBI_HBLANK_SAMPLES_60HZ
76962306a36Sopenharmony_ci				   : (23 - 2 + 1) * 2 * VBI_HBLANK_SAMPLES_50HZ;
77062306a36Sopenharmony_ci		}
77162306a36Sopenharmony_ci		break;
77262306a36Sopenharmony_ci	default:
77362306a36Sopenharmony_ci		s->bufs_per_mdl = 1;
77462306a36Sopenharmony_ci		s->mdl_size = s->buf_size * s->bufs_per_mdl;
77562306a36Sopenharmony_ci		break;
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	cx18_load_queues(s);
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ciint cx18_start_v4l2_encode_stream(struct cx18_stream *s)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	u32 data[MAX_MB_ARGUMENTS];
78462306a36Sopenharmony_ci	struct cx18 *cx = s->cx;
78562306a36Sopenharmony_ci	int captype = 0;
78662306a36Sopenharmony_ci	struct cx18_stream *s_idx;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (!cx18_stream_enabled(s))
78962306a36Sopenharmony_ci		return -EINVAL;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	switch (s->type) {
79462306a36Sopenharmony_ci	case CX18_ENC_STREAM_TYPE_MPG:
79562306a36Sopenharmony_ci		captype = CAPTURE_CHANNEL_TYPE_MPEG;
79662306a36Sopenharmony_ci		cx->mpg_data_received = cx->vbi_data_inserted = 0;
79762306a36Sopenharmony_ci		cx->dualwatch_jiffies = jiffies;
79862306a36Sopenharmony_ci		cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode);
79962306a36Sopenharmony_ci		cx->search_pack_header = 0;
80062306a36Sopenharmony_ci		break;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	case CX18_ENC_STREAM_TYPE_IDX:
80362306a36Sopenharmony_ci		captype = CAPTURE_CHANNEL_TYPE_INDEX;
80462306a36Sopenharmony_ci		break;
80562306a36Sopenharmony_ci	case CX18_ENC_STREAM_TYPE_TS:
80662306a36Sopenharmony_ci		captype = CAPTURE_CHANNEL_TYPE_TS;
80762306a36Sopenharmony_ci		break;
80862306a36Sopenharmony_ci	case CX18_ENC_STREAM_TYPE_YUV:
80962306a36Sopenharmony_ci		captype = CAPTURE_CHANNEL_TYPE_YUV;
81062306a36Sopenharmony_ci		break;
81162306a36Sopenharmony_ci	case CX18_ENC_STREAM_TYPE_PCM:
81262306a36Sopenharmony_ci		captype = CAPTURE_CHANNEL_TYPE_PCM;
81362306a36Sopenharmony_ci		break;
81462306a36Sopenharmony_ci	case CX18_ENC_STREAM_TYPE_VBI:
81562306a36Sopenharmony_ci#ifdef CX18_ENCODER_PARSES_SLICED
81662306a36Sopenharmony_ci		captype = cx18_raw_vbi(cx) ?
81762306a36Sopenharmony_ci		     CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
81862306a36Sopenharmony_ci#else
81962306a36Sopenharmony_ci		/*
82062306a36Sopenharmony_ci		 * Currently we set things up so that Sliced VBI from the
82162306a36Sopenharmony_ci		 * digitizer is handled as Raw VBI by the encoder
82262306a36Sopenharmony_ci		 */
82362306a36Sopenharmony_ci		captype = CAPTURE_CHANNEL_TYPE_VBI;
82462306a36Sopenharmony_ci#endif
82562306a36Sopenharmony_ci		cx->vbi.frame = 0;
82662306a36Sopenharmony_ci		cx->vbi.inserted_frame = 0;
82762306a36Sopenharmony_ci		memset(cx->vbi.sliced_mpeg_size,
82862306a36Sopenharmony_ci			0, sizeof(cx->vbi.sliced_mpeg_size));
82962306a36Sopenharmony_ci		break;
83062306a36Sopenharmony_ci	default:
83162306a36Sopenharmony_ci		return -EINVAL;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	/* Clear Streamoff flags in case left from last capture */
83562306a36Sopenharmony_ci	clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE);
83862306a36Sopenharmony_ci	s->handle = data[0];
83962306a36Sopenharmony_ci	cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	/*
84262306a36Sopenharmony_ci	 * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and
84362306a36Sopenharmony_ci	 * set up all the parameters, as it is not obvious which parameters the
84462306a36Sopenharmony_ci	 * firmware shares across capture channel types and which it does not.
84562306a36Sopenharmony_ci	 *
84662306a36Sopenharmony_ci	 * Some of the cx18_vapi() calls below apply to only certain capture
84762306a36Sopenharmony_ci	 * channel types.  We're hoping there's no harm in calling most of them
84862306a36Sopenharmony_ci	 * anyway, as long as the values are all consistent.  Setting some
84962306a36Sopenharmony_ci	 * shared parameters will have no effect once an analog capture channel
85062306a36Sopenharmony_ci	 * has started streaming.
85162306a36Sopenharmony_ci	 */
85262306a36Sopenharmony_ci	if (captype != CAPTURE_CHANNEL_TYPE_TS) {
85362306a36Sopenharmony_ci		cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
85462306a36Sopenharmony_ci		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
85562306a36Sopenharmony_ci		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0);
85662306a36Sopenharmony_ci		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		/*
85962306a36Sopenharmony_ci		 * Audio related reset according to
86062306a36Sopenharmony_ci		 * Documentation/driver-api/media/drivers/cx2341x-devel.rst
86162306a36Sopenharmony_ci		 */
86262306a36Sopenharmony_ci		if (atomic_read(&cx->ana_capturing) == 0)
86362306a36Sopenharmony_ci			cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
86462306a36Sopenharmony_ci				  s->handle, 12);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci		/*
86762306a36Sopenharmony_ci		 * Number of lines for Field 1 & Field 2 according to
86862306a36Sopenharmony_ci		 * Documentation/driver-api/media/drivers/cx2341x-devel.rst
86962306a36Sopenharmony_ci		 * Field 1 is 312 for 625 line systems in BT.656
87062306a36Sopenharmony_ci		 * Field 2 is 313 for 625 line systems in BT.656
87162306a36Sopenharmony_ci		 */
87262306a36Sopenharmony_ci		cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3,
87362306a36Sopenharmony_ci			  s->handle, 312, 313);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE)
87662306a36Sopenharmony_ci			cx18_vbi_setup(s);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci		/*
87962306a36Sopenharmony_ci		 * Select to receive I, P, and B frame index entries, if the
88062306a36Sopenharmony_ci		 * index stream is enabled.  Otherwise disable index entry
88162306a36Sopenharmony_ci		 * generation.
88262306a36Sopenharmony_ci		 */
88362306a36Sopenharmony_ci		s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
88462306a36Sopenharmony_ci		cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2,
88562306a36Sopenharmony_ci				 s->handle, cx18_stream_enabled(s_idx) ? 7 : 0);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci		/* Call out to the common CX2341x API setup for user controls */
88862306a36Sopenharmony_ci		cx->cxhdl.priv = s;
88962306a36Sopenharmony_ci		cx2341x_handler_setup(&cx->cxhdl);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci		/*
89262306a36Sopenharmony_ci		 * When starting a capture and we're set for radio,
89362306a36Sopenharmony_ci		 * ensure the video is muted, despite the user control.
89462306a36Sopenharmony_ci		 */
89562306a36Sopenharmony_ci		if (!cx->cxhdl.video_mute &&
89662306a36Sopenharmony_ci		    test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
89762306a36Sopenharmony_ci			cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
89862306a36Sopenharmony_ci			  (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci		/* Enable the Video Format Converter for UYVY 4:2:2 support,
90162306a36Sopenharmony_ci		 * rather than the default HM12 Macroblovk 4:2:0 support.
90262306a36Sopenharmony_ci		 */
90362306a36Sopenharmony_ci		if (captype == CAPTURE_CHANNEL_TYPE_YUV) {
90462306a36Sopenharmony_ci			if (s->pixelformat == V4L2_PIX_FMT_UYVY)
90562306a36Sopenharmony_ci				cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
90662306a36Sopenharmony_ci					s->handle, 1);
90762306a36Sopenharmony_ci			else
90862306a36Sopenharmony_ci				/* If in doubt, default to HM12 */
90962306a36Sopenharmony_ci				cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
91062306a36Sopenharmony_ci					s->handle, 0);
91162306a36Sopenharmony_ci		}
91262306a36Sopenharmony_ci	}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	if (atomic_read(&cx->tot_capturing) == 0) {
91562306a36Sopenharmony_ci		cx2341x_handler_set_busy(&cx->cxhdl, 1);
91662306a36Sopenharmony_ci		clear_bit(CX18_F_I_EOS, &cx->i_flags);
91762306a36Sopenharmony_ci		cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK);
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle,
92162306a36Sopenharmony_ci		(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
92262306a36Sopenharmony_ci		(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	/* Init all the cpu_mdls for this stream */
92562306a36Sopenharmony_ci	cx18_stream_configure_mdls(s);
92662306a36Sopenharmony_ci	_cx18_stream_load_fw_queue(s);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	/* begin_capture */
92962306a36Sopenharmony_ci	if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
93062306a36Sopenharmony_ci		CX18_DEBUG_WARN("Error starting capture!\n");
93162306a36Sopenharmony_ci		/* Ensure we're really not capturing before releasing MDLs */
93262306a36Sopenharmony_ci		set_bit(CX18_F_S_STOPPING, &s->s_flags);
93362306a36Sopenharmony_ci		if (s->type == CX18_ENC_STREAM_TYPE_MPG)
93462306a36Sopenharmony_ci			cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
93562306a36Sopenharmony_ci		else
93662306a36Sopenharmony_ci			cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
93762306a36Sopenharmony_ci		clear_bit(CX18_F_S_STREAMING, &s->s_flags);
93862306a36Sopenharmony_ci		/* FIXME - CX18_F_S_STREAMOFF as well? */
93962306a36Sopenharmony_ci		cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
94062306a36Sopenharmony_ci		cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
94162306a36Sopenharmony_ci		s->handle = CX18_INVALID_TASK_HANDLE;
94262306a36Sopenharmony_ci		clear_bit(CX18_F_S_STOPPING, &s->s_flags);
94362306a36Sopenharmony_ci		if (atomic_read(&cx->tot_capturing) == 0) {
94462306a36Sopenharmony_ci			set_bit(CX18_F_I_EOS, &cx->i_flags);
94562306a36Sopenharmony_ci			cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
94662306a36Sopenharmony_ci		}
94762306a36Sopenharmony_ci		return -EINVAL;
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/* you're live! sit back and await interrupts :) */
95162306a36Sopenharmony_ci	if (captype != CAPTURE_CHANNEL_TYPE_TS)
95262306a36Sopenharmony_ci		atomic_inc(&cx->ana_capturing);
95362306a36Sopenharmony_ci	atomic_inc(&cx->tot_capturing);
95462306a36Sopenharmony_ci	return 0;
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ciEXPORT_SYMBOL(cx18_start_v4l2_encode_stream);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_civoid cx18_stop_all_captures(struct cx18 *cx)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	int i;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
96362306a36Sopenharmony_ci		struct cx18_stream *s = &cx->streams[i];
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci		if (!cx18_stream_enabled(s))
96662306a36Sopenharmony_ci			continue;
96762306a36Sopenharmony_ci		if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
96862306a36Sopenharmony_ci			cx18_stop_v4l2_encode_stream(s, 0);
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ciint cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
97362306a36Sopenharmony_ci{
97462306a36Sopenharmony_ci	struct cx18 *cx = s->cx;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	if (!cx18_stream_enabled(s))
97762306a36Sopenharmony_ci		return -EINVAL;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/* This function assumes that you are allowed to stop the capture
98062306a36Sopenharmony_ci	   and that we are actually capturing */
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	CX18_DEBUG_INFO("Stop Capture\n");
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (atomic_read(&cx->tot_capturing) == 0)
98562306a36Sopenharmony_ci		return 0;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	set_bit(CX18_F_S_STOPPING, &s->s_flags);
98862306a36Sopenharmony_ci	if (s->type == CX18_ENC_STREAM_TYPE_MPG)
98962306a36Sopenharmony_ci		cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end);
99062306a36Sopenharmony_ci	else
99162306a36Sopenharmony_ci		cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) {
99462306a36Sopenharmony_ci		CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	if (s->type != CX18_ENC_STREAM_TYPE_TS)
99862306a36Sopenharmony_ci		atomic_dec(&cx->ana_capturing);
99962306a36Sopenharmony_ci	atomic_dec(&cx->tot_capturing);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	/* Clear capture and no-read bits */
100262306a36Sopenharmony_ci	clear_bit(CX18_F_S_STREAMING, &s->s_flags);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/* Tell the CX23418 it can't use our buffers anymore */
100562306a36Sopenharmony_ci	cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
100862306a36Sopenharmony_ci	s->handle = CX18_INVALID_TASK_HANDLE;
100962306a36Sopenharmony_ci	clear_bit(CX18_F_S_STOPPING, &s->s_flags);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	if (atomic_read(&cx->tot_capturing) > 0)
101262306a36Sopenharmony_ci		return 0;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	cx2341x_handler_set_busy(&cx->cxhdl, 0);
101562306a36Sopenharmony_ci	cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
101662306a36Sopenharmony_ci	wake_up(&s->waitq);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	return 0;
101962306a36Sopenharmony_ci}
102062306a36Sopenharmony_ciEXPORT_SYMBOL(cx18_stop_v4l2_encode_stream);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ciu32 cx18_find_handle(struct cx18 *cx)
102362306a36Sopenharmony_ci{
102462306a36Sopenharmony_ci	int i;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	/* find first available handle to be used for global settings */
102762306a36Sopenharmony_ci	for (i = 0; i < CX18_MAX_STREAMS; i++) {
102862306a36Sopenharmony_ci		struct cx18_stream *s = &cx->streams[i];
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci		if (s->video_dev.v4l2_dev && (s->handle != CX18_INVALID_TASK_HANDLE))
103162306a36Sopenharmony_ci			return s->handle;
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci	return CX18_INVALID_TASK_HANDLE;
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_cistruct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	int i;
103962306a36Sopenharmony_ci	struct cx18_stream *s;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	if (handle == CX18_INVALID_TASK_HANDLE)
104262306a36Sopenharmony_ci		return NULL;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	for (i = 0; i < CX18_MAX_STREAMS; i++) {
104562306a36Sopenharmony_ci		s = &cx->streams[i];
104662306a36Sopenharmony_ci		if (s->handle != handle)
104762306a36Sopenharmony_ci			continue;
104862306a36Sopenharmony_ci		if (cx18_stream_enabled(s))
104962306a36Sopenharmony_ci			return s;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci	return NULL;
105262306a36Sopenharmony_ci}
1053