1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * cx18 init/start/stop/exit stream functions
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Derived from ivtv-streams.c
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
8*4882a593Smuzhiyun * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include "cx18-driver.h"
12*4882a593Smuzhiyun #include "cx18-io.h"
13*4882a593Smuzhiyun #include "cx18-fileops.h"
14*4882a593Smuzhiyun #include "cx18-mailbox.h"
15*4882a593Smuzhiyun #include "cx18-i2c.h"
16*4882a593Smuzhiyun #include "cx18-queue.h"
17*4882a593Smuzhiyun #include "cx18-ioctl.h"
18*4882a593Smuzhiyun #include "cx18-streams.h"
19*4882a593Smuzhiyun #include "cx18-cards.h"
20*4882a593Smuzhiyun #include "cx18-scb.h"
21*4882a593Smuzhiyun #include "cx18-dvb.h"
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define CX18_DSP0_INTERRUPT_MASK 0xd0004C
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun static const struct v4l2_file_operations cx18_v4l2_enc_fops = {
26*4882a593Smuzhiyun .owner = THIS_MODULE,
27*4882a593Smuzhiyun .read = cx18_v4l2_read,
28*4882a593Smuzhiyun .open = cx18_v4l2_open,
29*4882a593Smuzhiyun .unlocked_ioctl = video_ioctl2,
30*4882a593Smuzhiyun .release = cx18_v4l2_close,
31*4882a593Smuzhiyun .poll = cx18_v4l2_enc_poll,
32*4882a593Smuzhiyun .mmap = cx18_v4l2_mmap,
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun /* offset from 0 to register ts v4l2 minors on */
36*4882a593Smuzhiyun #define CX18_V4L2_ENC_TS_OFFSET 16
37*4882a593Smuzhiyun /* offset from 0 to register pcm v4l2 minors on */
38*4882a593Smuzhiyun #define CX18_V4L2_ENC_PCM_OFFSET 24
39*4882a593Smuzhiyun /* offset from 0 to register yuv v4l2 minors on */
40*4882a593Smuzhiyun #define CX18_V4L2_ENC_YUV_OFFSET 32
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun static struct {
43*4882a593Smuzhiyun const char *name;
44*4882a593Smuzhiyun int vfl_type;
45*4882a593Smuzhiyun int num_offset;
46*4882a593Smuzhiyun int dma;
47*4882a593Smuzhiyun u32 caps;
48*4882a593Smuzhiyun } cx18_stream_info[] = {
49*4882a593Smuzhiyun { /* CX18_ENC_STREAM_TYPE_MPG */
50*4882a593Smuzhiyun "encoder MPEG",
51*4882a593Smuzhiyun VFL_TYPE_VIDEO, 0,
52*4882a593Smuzhiyun PCI_DMA_FROMDEVICE,
53*4882a593Smuzhiyun V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
54*4882a593Smuzhiyun V4L2_CAP_AUDIO | V4L2_CAP_TUNER
55*4882a593Smuzhiyun },
56*4882a593Smuzhiyun { /* CX18_ENC_STREAM_TYPE_TS */
57*4882a593Smuzhiyun "TS",
58*4882a593Smuzhiyun VFL_TYPE_VIDEO, -1,
59*4882a593Smuzhiyun PCI_DMA_FROMDEVICE,
60*4882a593Smuzhiyun },
61*4882a593Smuzhiyun { /* CX18_ENC_STREAM_TYPE_YUV */
62*4882a593Smuzhiyun "encoder YUV",
63*4882a593Smuzhiyun VFL_TYPE_VIDEO, CX18_V4L2_ENC_YUV_OFFSET,
64*4882a593Smuzhiyun PCI_DMA_FROMDEVICE,
65*4882a593Smuzhiyun V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
66*4882a593Smuzhiyun V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | V4L2_CAP_TUNER
67*4882a593Smuzhiyun },
68*4882a593Smuzhiyun { /* CX18_ENC_STREAM_TYPE_VBI */
69*4882a593Smuzhiyun "encoder VBI",
70*4882a593Smuzhiyun VFL_TYPE_VBI, 0,
71*4882a593Smuzhiyun PCI_DMA_FROMDEVICE,
72*4882a593Smuzhiyun V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE |
73*4882a593Smuzhiyun V4L2_CAP_READWRITE | V4L2_CAP_TUNER
74*4882a593Smuzhiyun },
75*4882a593Smuzhiyun { /* CX18_ENC_STREAM_TYPE_PCM */
76*4882a593Smuzhiyun "encoder PCM audio",
77*4882a593Smuzhiyun VFL_TYPE_VIDEO, CX18_V4L2_ENC_PCM_OFFSET,
78*4882a593Smuzhiyun PCI_DMA_FROMDEVICE,
79*4882a593Smuzhiyun V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
80*4882a593Smuzhiyun },
81*4882a593Smuzhiyun { /* CX18_ENC_STREAM_TYPE_IDX */
82*4882a593Smuzhiyun "encoder IDX",
83*4882a593Smuzhiyun VFL_TYPE_VIDEO, -1,
84*4882a593Smuzhiyun PCI_DMA_FROMDEVICE,
85*4882a593Smuzhiyun },
86*4882a593Smuzhiyun { /* CX18_ENC_STREAM_TYPE_RAD */
87*4882a593Smuzhiyun "encoder radio",
88*4882a593Smuzhiyun VFL_TYPE_RADIO, 0,
89*4882a593Smuzhiyun PCI_DMA_NONE,
90*4882a593Smuzhiyun V4L2_CAP_RADIO | V4L2_CAP_TUNER
91*4882a593Smuzhiyun },
92*4882a593Smuzhiyun };
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun
cx18_dma_free(struct videobuf_queue * q,struct cx18_stream * s,struct cx18_videobuf_buffer * buf)95*4882a593Smuzhiyun static void cx18_dma_free(struct videobuf_queue *q,
96*4882a593Smuzhiyun struct cx18_stream *s, struct cx18_videobuf_buffer *buf)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun videobuf_waiton(q, &buf->vb, 0, 0);
99*4882a593Smuzhiyun videobuf_vmalloc_free(&buf->vb);
100*4882a593Smuzhiyun buf->vb.state = VIDEOBUF_NEEDS_INIT;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
cx18_prepare_buffer(struct videobuf_queue * q,struct cx18_stream * s,struct cx18_videobuf_buffer * buf,u32 pixelformat,unsigned int width,unsigned int height,enum v4l2_field field)103*4882a593Smuzhiyun static int cx18_prepare_buffer(struct videobuf_queue *q,
104*4882a593Smuzhiyun struct cx18_stream *s,
105*4882a593Smuzhiyun struct cx18_videobuf_buffer *buf,
106*4882a593Smuzhiyun u32 pixelformat,
107*4882a593Smuzhiyun unsigned int width, unsigned int height,
108*4882a593Smuzhiyun enum v4l2_field field)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun struct cx18 *cx = s->cx;
111*4882a593Smuzhiyun int rc = 0;
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun /* check settings */
114*4882a593Smuzhiyun buf->bytes_used = 0;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun if ((width < 48) || (height < 32))
117*4882a593Smuzhiyun return -EINVAL;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun buf->vb.size = (width * height * 2);
120*4882a593Smuzhiyun if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
121*4882a593Smuzhiyun return -EINVAL;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun /* alloc + fill struct (if changed) */
124*4882a593Smuzhiyun if (buf->vb.width != width || buf->vb.height != height ||
125*4882a593Smuzhiyun buf->vb.field != field || s->pixelformat != pixelformat ||
126*4882a593Smuzhiyun buf->tvnorm != cx->std) {
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun buf->vb.width = width;
129*4882a593Smuzhiyun buf->vb.height = height;
130*4882a593Smuzhiyun buf->vb.field = field;
131*4882a593Smuzhiyun buf->tvnorm = cx->std;
132*4882a593Smuzhiyun s->pixelformat = pixelformat;
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
135*4882a593Smuzhiyun UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
136*4882a593Smuzhiyun if (s->pixelformat == V4L2_PIX_FMT_HM12)
137*4882a593Smuzhiyun s->vb_bytes_per_frame = height * 720 * 3 / 2;
138*4882a593Smuzhiyun else
139*4882a593Smuzhiyun s->vb_bytes_per_frame = height * 720 * 2;
140*4882a593Smuzhiyun cx18_dma_free(q, s, buf);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
144*4882a593Smuzhiyun return -EINVAL;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun if (buf->vb.field == 0)
147*4882a593Smuzhiyun buf->vb.field = V4L2_FIELD_INTERLACED;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
150*4882a593Smuzhiyun buf->vb.width = width;
151*4882a593Smuzhiyun buf->vb.height = height;
152*4882a593Smuzhiyun buf->vb.field = field;
153*4882a593Smuzhiyun buf->tvnorm = cx->std;
154*4882a593Smuzhiyun s->pixelformat = pixelformat;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
157*4882a593Smuzhiyun UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
158*4882a593Smuzhiyun if (s->pixelformat == V4L2_PIX_FMT_HM12)
159*4882a593Smuzhiyun s->vb_bytes_per_frame = height * 720 * 3 / 2;
160*4882a593Smuzhiyun else
161*4882a593Smuzhiyun s->vb_bytes_per_frame = height * 720 * 2;
162*4882a593Smuzhiyun rc = videobuf_iolock(q, &buf->vb, NULL);
163*4882a593Smuzhiyun if (rc != 0)
164*4882a593Smuzhiyun goto fail;
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun buf->vb.state = VIDEOBUF_PREPARED;
167*4882a593Smuzhiyun return 0;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun fail:
170*4882a593Smuzhiyun cx18_dma_free(q, s, buf);
171*4882a593Smuzhiyun return rc;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun /* VB_MIN_BUFSIZE is lcm(1440 * 480, 1440 * 576)
176*4882a593Smuzhiyun 1440 is a single line of 4:2:2 YUV at 720 luma samples wide
177*4882a593Smuzhiyun */
178*4882a593Smuzhiyun #define VB_MIN_BUFFERS 32
179*4882a593Smuzhiyun #define VB_MIN_BUFSIZE 4147200
180*4882a593Smuzhiyun
buffer_setup(struct videobuf_queue * q,unsigned int * count,unsigned int * size)181*4882a593Smuzhiyun static int buffer_setup(struct videobuf_queue *q,
182*4882a593Smuzhiyun unsigned int *count, unsigned int *size)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun struct cx18_stream *s = q->priv_data;
185*4882a593Smuzhiyun struct cx18 *cx = s->cx;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun *size = 2 * cx->cxhdl.width * cx->cxhdl.height;
188*4882a593Smuzhiyun if (*count == 0)
189*4882a593Smuzhiyun *count = VB_MIN_BUFFERS;
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun while (*size * *count > VB_MIN_BUFFERS * VB_MIN_BUFSIZE)
192*4882a593Smuzhiyun (*count)--;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun q->field = V4L2_FIELD_INTERLACED;
195*4882a593Smuzhiyun q->last = V4L2_FIELD_INTERLACED;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun return 0;
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun
buffer_prepare(struct videobuf_queue * q,struct videobuf_buffer * vb,enum v4l2_field field)200*4882a593Smuzhiyun static int buffer_prepare(struct videobuf_queue *q,
201*4882a593Smuzhiyun struct videobuf_buffer *vb,
202*4882a593Smuzhiyun enum v4l2_field field)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun struct cx18_videobuf_buffer *buf =
205*4882a593Smuzhiyun container_of(vb, struct cx18_videobuf_buffer, vb);
206*4882a593Smuzhiyun struct cx18_stream *s = q->priv_data;
207*4882a593Smuzhiyun struct cx18 *cx = s->cx;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun return cx18_prepare_buffer(q, s, buf, s->pixelformat,
210*4882a593Smuzhiyun cx->cxhdl.width, cx->cxhdl.height, field);
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun
buffer_release(struct videobuf_queue * q,struct videobuf_buffer * vb)213*4882a593Smuzhiyun static void buffer_release(struct videobuf_queue *q,
214*4882a593Smuzhiyun struct videobuf_buffer *vb)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun struct cx18_videobuf_buffer *buf =
217*4882a593Smuzhiyun container_of(vb, struct cx18_videobuf_buffer, vb);
218*4882a593Smuzhiyun struct cx18_stream *s = q->priv_data;
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun cx18_dma_free(q, s, buf);
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun
buffer_queue(struct videobuf_queue * q,struct videobuf_buffer * vb)223*4882a593Smuzhiyun static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun struct cx18_videobuf_buffer *buf =
226*4882a593Smuzhiyun container_of(vb, struct cx18_videobuf_buffer, vb);
227*4882a593Smuzhiyun struct cx18_stream *s = q->priv_data;
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun buf->vb.state = VIDEOBUF_QUEUED;
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun list_add_tail(&buf->vb.queue, &s->vb_capture);
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun static const struct videobuf_queue_ops cx18_videobuf_qops = {
235*4882a593Smuzhiyun .buf_setup = buffer_setup,
236*4882a593Smuzhiyun .buf_prepare = buffer_prepare,
237*4882a593Smuzhiyun .buf_queue = buffer_queue,
238*4882a593Smuzhiyun .buf_release = buffer_release,
239*4882a593Smuzhiyun };
240*4882a593Smuzhiyun
cx18_stream_init(struct cx18 * cx,int type)241*4882a593Smuzhiyun static void cx18_stream_init(struct cx18 *cx, int type)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun struct cx18_stream *s = &cx->streams[type];
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun memset(s, 0, sizeof(*s));
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun /* initialize cx18_stream fields */
248*4882a593Smuzhiyun s->dvb = NULL;
249*4882a593Smuzhiyun s->cx = cx;
250*4882a593Smuzhiyun s->type = type;
251*4882a593Smuzhiyun s->name = cx18_stream_info[type].name;
252*4882a593Smuzhiyun s->handle = CX18_INVALID_TASK_HANDLE;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun s->dma = cx18_stream_info[type].dma;
255*4882a593Smuzhiyun s->v4l2_dev_caps = cx18_stream_info[type].caps;
256*4882a593Smuzhiyun s->buffers = cx->stream_buffers[type];
257*4882a593Smuzhiyun s->buf_size = cx->stream_buf_size[type];
258*4882a593Smuzhiyun INIT_LIST_HEAD(&s->buf_pool);
259*4882a593Smuzhiyun s->bufs_per_mdl = 1;
260*4882a593Smuzhiyun s->mdl_size = s->buf_size * s->bufs_per_mdl;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun init_waitqueue_head(&s->waitq);
263*4882a593Smuzhiyun s->id = -1;
264*4882a593Smuzhiyun spin_lock_init(&s->q_free.lock);
265*4882a593Smuzhiyun cx18_queue_init(&s->q_free);
266*4882a593Smuzhiyun spin_lock_init(&s->q_busy.lock);
267*4882a593Smuzhiyun cx18_queue_init(&s->q_busy);
268*4882a593Smuzhiyun spin_lock_init(&s->q_full.lock);
269*4882a593Smuzhiyun cx18_queue_init(&s->q_full);
270*4882a593Smuzhiyun spin_lock_init(&s->q_idle.lock);
271*4882a593Smuzhiyun cx18_queue_init(&s->q_idle);
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun INIT_WORK(&s->out_work_order, cx18_out_work_handler);
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun INIT_LIST_HEAD(&s->vb_capture);
276*4882a593Smuzhiyun timer_setup(&s->vb_timeout, cx18_vb_timeout, 0);
277*4882a593Smuzhiyun spin_lock_init(&s->vb_lock);
278*4882a593Smuzhiyun if (type == CX18_ENC_STREAM_TYPE_YUV) {
279*4882a593Smuzhiyun spin_lock_init(&s->vbuf_q_lock);
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun s->vb_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
282*4882a593Smuzhiyun videobuf_queue_vmalloc_init(&s->vbuf_q, &cx18_videobuf_qops,
283*4882a593Smuzhiyun &cx->pci_dev->dev, &s->vbuf_q_lock,
284*4882a593Smuzhiyun V4L2_BUF_TYPE_VIDEO_CAPTURE,
285*4882a593Smuzhiyun V4L2_FIELD_INTERLACED,
286*4882a593Smuzhiyun sizeof(struct cx18_videobuf_buffer),
287*4882a593Smuzhiyun s, &cx->serialize_lock);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun /* Assume the previous pixel default */
290*4882a593Smuzhiyun s->pixelformat = V4L2_PIX_FMT_HM12;
291*4882a593Smuzhiyun s->vb_bytes_per_frame = cx->cxhdl.height * 720 * 3 / 2;
292*4882a593Smuzhiyun s->vb_bytes_per_line = 720;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun
cx18_prep_dev(struct cx18 * cx,int type)296*4882a593Smuzhiyun static int cx18_prep_dev(struct cx18 *cx, int type)
297*4882a593Smuzhiyun {
298*4882a593Smuzhiyun struct cx18_stream *s = &cx->streams[type];
299*4882a593Smuzhiyun u32 cap = cx->v4l2_cap;
300*4882a593Smuzhiyun int num_offset = cx18_stream_info[type].num_offset;
301*4882a593Smuzhiyun int num = cx->instance + cx18_first_minor + num_offset;
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun /*
304*4882a593Smuzhiyun * These five fields are always initialized.
305*4882a593Smuzhiyun * For analog capture related streams, if video_dev.v4l2_dev == NULL then the
306*4882a593Smuzhiyun * stream is not in use.
307*4882a593Smuzhiyun * For the TS stream, if dvb == NULL then the stream is not in use.
308*4882a593Smuzhiyun * In those cases no other fields but these four can be used.
309*4882a593Smuzhiyun */
310*4882a593Smuzhiyun s->video_dev.v4l2_dev = NULL;
311*4882a593Smuzhiyun s->dvb = NULL;
312*4882a593Smuzhiyun s->cx = cx;
313*4882a593Smuzhiyun s->type = type;
314*4882a593Smuzhiyun s->name = cx18_stream_info[type].name;
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun /* Check whether the radio is supported */
317*4882a593Smuzhiyun if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO))
318*4882a593Smuzhiyun return 0;
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun /* Check whether VBI is supported */
321*4882a593Smuzhiyun if (type == CX18_ENC_STREAM_TYPE_VBI &&
322*4882a593Smuzhiyun !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
323*4882a593Smuzhiyun return 0;
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun /* User explicitly selected 0 buffers for these streams, so don't
326*4882a593Smuzhiyun create them. */
327*4882a593Smuzhiyun if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
328*4882a593Smuzhiyun cx->stream_buffers[type] == 0) {
329*4882a593Smuzhiyun CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
330*4882a593Smuzhiyun return 0;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun cx18_stream_init(cx, type);
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun /* Allocate the cx18_dvb struct only for the TS on cards with DTV */
336*4882a593Smuzhiyun if (type == CX18_ENC_STREAM_TYPE_TS) {
337*4882a593Smuzhiyun if (cx->card->hw_all & CX18_HW_DVB) {
338*4882a593Smuzhiyun s->dvb = kzalloc(sizeof(struct cx18_dvb), GFP_KERNEL);
339*4882a593Smuzhiyun if (s->dvb == NULL) {
340*4882a593Smuzhiyun CX18_ERR("Couldn't allocate cx18_dvb structure for %s\n",
341*4882a593Smuzhiyun s->name);
342*4882a593Smuzhiyun return -ENOMEM;
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun } else {
345*4882a593Smuzhiyun /* Don't need buffers for the TS, if there is no DVB */
346*4882a593Smuzhiyun s->buffers = 0;
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun if (num_offset == -1)
351*4882a593Smuzhiyun return 0;
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun /* initialize the v4l2 video device structure */
354*4882a593Smuzhiyun snprintf(s->video_dev.name, sizeof(s->video_dev.name), "%s %s",
355*4882a593Smuzhiyun cx->v4l2_dev.name, s->name);
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun s->video_dev.num = num;
358*4882a593Smuzhiyun s->video_dev.v4l2_dev = &cx->v4l2_dev;
359*4882a593Smuzhiyun s->video_dev.fops = &cx18_v4l2_enc_fops;
360*4882a593Smuzhiyun s->video_dev.release = video_device_release_empty;
361*4882a593Smuzhiyun if (cx->card->video_inputs->video_type == CX18_CARD_INPUT_VID_TUNER)
362*4882a593Smuzhiyun s->video_dev.tvnorms = cx->tuner_std;
363*4882a593Smuzhiyun else
364*4882a593Smuzhiyun s->video_dev.tvnorms = V4L2_STD_ALL;
365*4882a593Smuzhiyun s->video_dev.lock = &cx->serialize_lock;
366*4882a593Smuzhiyun cx18_set_funcs(&s->video_dev);
367*4882a593Smuzhiyun return 0;
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun /* Initialize v4l2 variables and register v4l2 devices */
cx18_streams_setup(struct cx18 * cx)371*4882a593Smuzhiyun int cx18_streams_setup(struct cx18 *cx)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun int type, ret;
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun /* Setup V4L2 Devices */
376*4882a593Smuzhiyun for (type = 0; type < CX18_MAX_STREAMS; type++) {
377*4882a593Smuzhiyun /* Prepare device */
378*4882a593Smuzhiyun ret = cx18_prep_dev(cx, type);
379*4882a593Smuzhiyun if (ret < 0)
380*4882a593Smuzhiyun break;
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun /* Allocate Stream */
383*4882a593Smuzhiyun ret = cx18_stream_alloc(&cx->streams[type]);
384*4882a593Smuzhiyun if (ret < 0)
385*4882a593Smuzhiyun break;
386*4882a593Smuzhiyun }
387*4882a593Smuzhiyun if (type == CX18_MAX_STREAMS)
388*4882a593Smuzhiyun return 0;
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun /* One or more streams could not be initialized. Clean 'em all up. */
391*4882a593Smuzhiyun cx18_streams_cleanup(cx, 0);
392*4882a593Smuzhiyun return ret;
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun
cx18_reg_dev(struct cx18 * cx,int type)395*4882a593Smuzhiyun static int cx18_reg_dev(struct cx18 *cx, int type)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun struct cx18_stream *s = &cx->streams[type];
398*4882a593Smuzhiyun int vfl_type = cx18_stream_info[type].vfl_type;
399*4882a593Smuzhiyun const char *name;
400*4882a593Smuzhiyun int num, ret;
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun if (type == CX18_ENC_STREAM_TYPE_TS && s->dvb != NULL) {
403*4882a593Smuzhiyun ret = cx18_dvb_register(s);
404*4882a593Smuzhiyun if (ret < 0) {
405*4882a593Smuzhiyun CX18_ERR("DVB failed to register\n");
406*4882a593Smuzhiyun return ret;
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun }
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun if (s->video_dev.v4l2_dev == NULL)
411*4882a593Smuzhiyun return 0;
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun num = s->video_dev.num;
414*4882a593Smuzhiyun s->video_dev.device_caps = s->v4l2_dev_caps; /* device capabilities */
415*4882a593Smuzhiyun /* card number + user defined offset + device offset */
416*4882a593Smuzhiyun if (type != CX18_ENC_STREAM_TYPE_MPG) {
417*4882a593Smuzhiyun struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun if (s_mpg->video_dev.v4l2_dev)
420*4882a593Smuzhiyun num = s_mpg->video_dev.num
421*4882a593Smuzhiyun + cx18_stream_info[type].num_offset;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun video_set_drvdata(&s->video_dev, s);
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun /* Register device. First try the desired minor, then any free one. */
426*4882a593Smuzhiyun ret = video_register_device_no_warn(&s->video_dev, vfl_type, num);
427*4882a593Smuzhiyun if (ret < 0) {
428*4882a593Smuzhiyun CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
429*4882a593Smuzhiyun s->name, num);
430*4882a593Smuzhiyun s->video_dev.v4l2_dev = NULL;
431*4882a593Smuzhiyun return ret;
432*4882a593Smuzhiyun }
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun name = video_device_node_name(&s->video_dev);
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun switch (vfl_type) {
437*4882a593Smuzhiyun case VFL_TYPE_VIDEO:
438*4882a593Smuzhiyun CX18_INFO("Registered device %s for %s (%d x %d.%02d kB)\n",
439*4882a593Smuzhiyun name, s->name, cx->stream_buffers[type],
440*4882a593Smuzhiyun cx->stream_buf_size[type] / 1024,
441*4882a593Smuzhiyun (cx->stream_buf_size[type] * 100 / 1024) % 100);
442*4882a593Smuzhiyun break;
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun case VFL_TYPE_RADIO:
445*4882a593Smuzhiyun CX18_INFO("Registered device %s for %s\n", name, s->name);
446*4882a593Smuzhiyun break;
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun case VFL_TYPE_VBI:
449*4882a593Smuzhiyun if (cx->stream_buffers[type])
450*4882a593Smuzhiyun CX18_INFO("Registered device %s for %s (%d x %d bytes)\n",
451*4882a593Smuzhiyun name, s->name, cx->stream_buffers[type],
452*4882a593Smuzhiyun cx->stream_buf_size[type]);
453*4882a593Smuzhiyun else
454*4882a593Smuzhiyun CX18_INFO("Registered device %s for %s\n",
455*4882a593Smuzhiyun name, s->name);
456*4882a593Smuzhiyun break;
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun return 0;
460*4882a593Smuzhiyun }
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun /* Register v4l2 devices */
cx18_streams_register(struct cx18 * cx)463*4882a593Smuzhiyun int cx18_streams_register(struct cx18 *cx)
464*4882a593Smuzhiyun {
465*4882a593Smuzhiyun int type;
466*4882a593Smuzhiyun int err;
467*4882a593Smuzhiyun int ret = 0;
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun /* Register V4L2 devices */
470*4882a593Smuzhiyun for (type = 0; type < CX18_MAX_STREAMS; type++) {
471*4882a593Smuzhiyun err = cx18_reg_dev(cx, type);
472*4882a593Smuzhiyun if (err && ret == 0)
473*4882a593Smuzhiyun ret = err;
474*4882a593Smuzhiyun }
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun if (ret == 0)
477*4882a593Smuzhiyun return 0;
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun /* One or more streams could not be initialized. Clean 'em all up. */
480*4882a593Smuzhiyun cx18_streams_cleanup(cx, 1);
481*4882a593Smuzhiyun return ret;
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun /* Unregister v4l2 devices */
cx18_streams_cleanup(struct cx18 * cx,int unregister)485*4882a593Smuzhiyun void cx18_streams_cleanup(struct cx18 *cx, int unregister)
486*4882a593Smuzhiyun {
487*4882a593Smuzhiyun struct video_device *vdev;
488*4882a593Smuzhiyun int type;
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun /* Teardown all streams */
491*4882a593Smuzhiyun for (type = 0; type < CX18_MAX_STREAMS; type++) {
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun /* The TS has a cx18_dvb structure, not a video_device */
494*4882a593Smuzhiyun if (type == CX18_ENC_STREAM_TYPE_TS) {
495*4882a593Smuzhiyun if (cx->streams[type].dvb != NULL) {
496*4882a593Smuzhiyun if (unregister)
497*4882a593Smuzhiyun cx18_dvb_unregister(&cx->streams[type]);
498*4882a593Smuzhiyun kfree(cx->streams[type].dvb);
499*4882a593Smuzhiyun cx->streams[type].dvb = NULL;
500*4882a593Smuzhiyun cx18_stream_free(&cx->streams[type]);
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun continue;
503*4882a593Smuzhiyun }
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun /* No struct video_device, but can have buffers allocated */
506*4882a593Smuzhiyun if (type == CX18_ENC_STREAM_TYPE_IDX) {
507*4882a593Smuzhiyun /* If the module params didn't inhibit IDX ... */
508*4882a593Smuzhiyun if (cx->stream_buffers[type] != 0) {
509*4882a593Smuzhiyun cx->stream_buffers[type] = 0;
510*4882a593Smuzhiyun /*
511*4882a593Smuzhiyun * Before calling cx18_stream_free(),
512*4882a593Smuzhiyun * check if the IDX stream was actually set up.
513*4882a593Smuzhiyun * Needed, since the cx18_probe() error path
514*4882a593Smuzhiyun * exits through here as well as normal clean up
515*4882a593Smuzhiyun */
516*4882a593Smuzhiyun if (cx->streams[type].buffers != 0)
517*4882a593Smuzhiyun cx18_stream_free(&cx->streams[type]);
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun continue;
520*4882a593Smuzhiyun }
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun /* If struct video_device exists, can have buffers allocated */
523*4882a593Smuzhiyun vdev = &cx->streams[type].video_dev;
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun if (vdev->v4l2_dev == NULL)
526*4882a593Smuzhiyun continue;
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun if (type == CX18_ENC_STREAM_TYPE_YUV)
529*4882a593Smuzhiyun videobuf_mmap_free(&cx->streams[type].vbuf_q);
530*4882a593Smuzhiyun
531*4882a593Smuzhiyun cx18_stream_free(&cx->streams[type]);
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun video_unregister_device(vdev);
534*4882a593Smuzhiyun }
535*4882a593Smuzhiyun }
536*4882a593Smuzhiyun
cx18_vbi_setup(struct cx18_stream * s)537*4882a593Smuzhiyun static void cx18_vbi_setup(struct cx18_stream *s)
538*4882a593Smuzhiyun {
539*4882a593Smuzhiyun struct cx18 *cx = s->cx;
540*4882a593Smuzhiyun int raw = cx18_raw_vbi(cx);
541*4882a593Smuzhiyun u32 data[CX2341X_MBOX_MAX_DATA];
542*4882a593Smuzhiyun int lines;
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun if (cx->is_60hz) {
545*4882a593Smuzhiyun cx->vbi.count = 12;
546*4882a593Smuzhiyun cx->vbi.start[0] = 10;
547*4882a593Smuzhiyun cx->vbi.start[1] = 273;
548*4882a593Smuzhiyun } else { /* PAL/SECAM */
549*4882a593Smuzhiyun cx->vbi.count = 18;
550*4882a593Smuzhiyun cx->vbi.start[0] = 6;
551*4882a593Smuzhiyun cx->vbi.start[1] = 318;
552*4882a593Smuzhiyun }
553*4882a593Smuzhiyun
554*4882a593Smuzhiyun /* setup VBI registers */
555*4882a593Smuzhiyun if (raw)
556*4882a593Smuzhiyun v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi);
557*4882a593Smuzhiyun else
558*4882a593Smuzhiyun v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced);
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun /*
561*4882a593Smuzhiyun * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw
562*4882a593Smuzhiyun * VBI when the first analog capture channel starts, as once it starts
563*4882a593Smuzhiyun * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup
564*4882a593Smuzhiyun * (i.e. for the VBI capture channels). We also send it for each
565*4882a593Smuzhiyun * analog capture channel anyway just to make sure we get the proper
566*4882a593Smuzhiyun * behavior
567*4882a593Smuzhiyun */
568*4882a593Smuzhiyun if (raw) {
569*4882a593Smuzhiyun lines = cx->vbi.count * 2;
570*4882a593Smuzhiyun } else {
571*4882a593Smuzhiyun /*
572*4882a593Smuzhiyun * For 525/60 systems, according to the VIP 2 & BT.656 std:
573*4882a593Smuzhiyun * The EAV RP code's Field bit toggles on line 4, a few lines
574*4882a593Smuzhiyun * after the Vertcal Blank bit has already toggled.
575*4882a593Smuzhiyun * Tell the encoder to capture 21-4+1=18 lines per field,
576*4882a593Smuzhiyun * since we want lines 10 through 21.
577*4882a593Smuzhiyun *
578*4882a593Smuzhiyun * For 625/50 systems, according to the VIP 2 & BT.656 std:
579*4882a593Smuzhiyun * The EAV RP code's Field bit toggles on line 1, a few lines
580*4882a593Smuzhiyun * after the Vertcal Blank bit has already toggled.
581*4882a593Smuzhiyun * (We've actually set the digitizer so that the Field bit
582*4882a593Smuzhiyun * toggles on line 2.) Tell the encoder to capture 23-2+1=22
583*4882a593Smuzhiyun * lines per field, since we want lines 6 through 23.
584*4882a593Smuzhiyun */
585*4882a593Smuzhiyun lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2;
586*4882a593Smuzhiyun }
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun data[0] = s->handle;
589*4882a593Smuzhiyun /* Lines per field */
590*4882a593Smuzhiyun data[1] = (lines / 2) | ((lines / 2) << 16);
591*4882a593Smuzhiyun /* bytes per line */
592*4882a593Smuzhiyun data[2] = (raw ? VBI_ACTIVE_SAMPLES
593*4882a593Smuzhiyun : (cx->is_60hz ? VBI_HBLANK_SAMPLES_60HZ
594*4882a593Smuzhiyun : VBI_HBLANK_SAMPLES_50HZ));
595*4882a593Smuzhiyun /* Every X number of frames a VBI interrupt arrives
596*4882a593Smuzhiyun (frames as in 25 or 30 fps) */
597*4882a593Smuzhiyun data[3] = 1;
598*4882a593Smuzhiyun /*
599*4882a593Smuzhiyun * Set the SAV/EAV RP codes to look for as start/stop points
600*4882a593Smuzhiyun * when in VIP-1.1 mode
601*4882a593Smuzhiyun */
602*4882a593Smuzhiyun if (raw) {
603*4882a593Smuzhiyun /*
604*4882a593Smuzhiyun * Start codes for beginning of "active" line in vertical blank
605*4882a593Smuzhiyun * 0x20 ( VerticalBlank )
606*4882a593Smuzhiyun * 0x60 ( EvenField VerticalBlank )
607*4882a593Smuzhiyun */
608*4882a593Smuzhiyun data[4] = 0x20602060;
609*4882a593Smuzhiyun /*
610*4882a593Smuzhiyun * End codes for end of "active" raw lines and regular lines
611*4882a593Smuzhiyun * 0x30 ( VerticalBlank HorizontalBlank)
612*4882a593Smuzhiyun * 0x70 ( EvenField VerticalBlank HorizontalBlank)
613*4882a593Smuzhiyun * 0x90 (Task HorizontalBlank)
614*4882a593Smuzhiyun * 0xd0 (Task EvenField HorizontalBlank)
615*4882a593Smuzhiyun */
616*4882a593Smuzhiyun data[5] = 0x307090d0;
617*4882a593Smuzhiyun } else {
618*4882a593Smuzhiyun /*
619*4882a593Smuzhiyun * End codes for active video, we want data in the hblank region
620*4882a593Smuzhiyun * 0xb0 (Task 0 VerticalBlank HorizontalBlank)
621*4882a593Smuzhiyun * 0xf0 (Task EvenField VerticalBlank HorizontalBlank)
622*4882a593Smuzhiyun *
623*4882a593Smuzhiyun * Since the V bit is only allowed to toggle in the EAV RP code,
624*4882a593Smuzhiyun * just before the first active region line, these two
625*4882a593Smuzhiyun * are problematic:
626*4882a593Smuzhiyun * 0x90 (Task HorizontalBlank)
627*4882a593Smuzhiyun * 0xd0 (Task EvenField HorizontalBlank)
628*4882a593Smuzhiyun *
629*4882a593Smuzhiyun * We have set the digitzer such that we don't have to worry
630*4882a593Smuzhiyun * about these problem codes.
631*4882a593Smuzhiyun */
632*4882a593Smuzhiyun data[4] = 0xB0F0B0F0;
633*4882a593Smuzhiyun /*
634*4882a593Smuzhiyun * Start codes for beginning of active line in vertical blank
635*4882a593Smuzhiyun * 0xa0 (Task VerticalBlank )
636*4882a593Smuzhiyun * 0xe0 (Task EvenField VerticalBlank )
637*4882a593Smuzhiyun */
638*4882a593Smuzhiyun data[5] = 0xA0E0A0E0;
639*4882a593Smuzhiyun }
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n",
642*4882a593Smuzhiyun data[0], data[1], data[2], data[3], data[4], data[5]);
643*4882a593Smuzhiyun
644*4882a593Smuzhiyun cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
645*4882a593Smuzhiyun }
646*4882a593Smuzhiyun
cx18_stream_rotate_idx_mdls(struct cx18 * cx)647*4882a593Smuzhiyun void cx18_stream_rotate_idx_mdls(struct cx18 *cx)
648*4882a593Smuzhiyun {
649*4882a593Smuzhiyun struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
650*4882a593Smuzhiyun struct cx18_mdl *mdl;
651*4882a593Smuzhiyun
652*4882a593Smuzhiyun if (!cx18_stream_enabled(s))
653*4882a593Smuzhiyun return;
654*4882a593Smuzhiyun
655*4882a593Smuzhiyun /* Return if the firmware is not running low on MDLs */
656*4882a593Smuzhiyun if ((atomic_read(&s->q_free.depth) + atomic_read(&s->q_busy.depth)) >=
657*4882a593Smuzhiyun CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN)
658*4882a593Smuzhiyun return;
659*4882a593Smuzhiyun
660*4882a593Smuzhiyun /* Return if there are no MDLs to rotate back to the firmware */
661*4882a593Smuzhiyun if (atomic_read(&s->q_full.depth) < 2)
662*4882a593Smuzhiyun return;
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun /*
665*4882a593Smuzhiyun * Take the oldest IDX MDL still holding data, and discard its index
666*4882a593Smuzhiyun * entries by scheduling the MDL to go back to the firmware
667*4882a593Smuzhiyun */
668*4882a593Smuzhiyun mdl = cx18_dequeue(s, &s->q_full);
669*4882a593Smuzhiyun if (mdl != NULL)
670*4882a593Smuzhiyun cx18_enqueue(s, mdl, &s->q_free);
671*4882a593Smuzhiyun }
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun static
_cx18_stream_put_mdl_fw(struct cx18_stream * s,struct cx18_mdl * mdl)674*4882a593Smuzhiyun struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s,
675*4882a593Smuzhiyun struct cx18_mdl *mdl)
676*4882a593Smuzhiyun {
677*4882a593Smuzhiyun struct cx18 *cx = s->cx;
678*4882a593Smuzhiyun struct cx18_queue *q;
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun /* Don't give it to the firmware, if we're not running a capture */
681*4882a593Smuzhiyun if (s->handle == CX18_INVALID_TASK_HANDLE ||
682*4882a593Smuzhiyun test_bit(CX18_F_S_STOPPING, &s->s_flags) ||
683*4882a593Smuzhiyun !test_bit(CX18_F_S_STREAMING, &s->s_flags))
684*4882a593Smuzhiyun return cx18_enqueue(s, mdl, &s->q_free);
685*4882a593Smuzhiyun
686*4882a593Smuzhiyun q = cx18_enqueue(s, mdl, &s->q_busy);
687*4882a593Smuzhiyun if (q != &s->q_busy)
688*4882a593Smuzhiyun return q; /* The firmware has the max MDLs it can handle */
689*4882a593Smuzhiyun
690*4882a593Smuzhiyun cx18_mdl_sync_for_device(s, mdl);
691*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
692*4882a593Smuzhiyun (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem,
693*4882a593Smuzhiyun s->bufs_per_mdl, mdl->id, s->mdl_size);
694*4882a593Smuzhiyun return q;
695*4882a593Smuzhiyun }
696*4882a593Smuzhiyun
697*4882a593Smuzhiyun static
_cx18_stream_load_fw_queue(struct cx18_stream * s)698*4882a593Smuzhiyun void _cx18_stream_load_fw_queue(struct cx18_stream *s)
699*4882a593Smuzhiyun {
700*4882a593Smuzhiyun struct cx18_queue *q;
701*4882a593Smuzhiyun struct cx18_mdl *mdl;
702*4882a593Smuzhiyun
703*4882a593Smuzhiyun if (atomic_read(&s->q_free.depth) == 0 ||
704*4882a593Smuzhiyun atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
705*4882a593Smuzhiyun return;
706*4882a593Smuzhiyun
707*4882a593Smuzhiyun /* Move from q_free to q_busy notifying the firmware, until the limit */
708*4882a593Smuzhiyun do {
709*4882a593Smuzhiyun mdl = cx18_dequeue(s, &s->q_free);
710*4882a593Smuzhiyun if (mdl == NULL)
711*4882a593Smuzhiyun break;
712*4882a593Smuzhiyun q = _cx18_stream_put_mdl_fw(s, mdl);
713*4882a593Smuzhiyun } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM
714*4882a593Smuzhiyun && q == &s->q_busy);
715*4882a593Smuzhiyun }
716*4882a593Smuzhiyun
cx18_out_work_handler(struct work_struct * work)717*4882a593Smuzhiyun void cx18_out_work_handler(struct work_struct *work)
718*4882a593Smuzhiyun {
719*4882a593Smuzhiyun struct cx18_stream *s =
720*4882a593Smuzhiyun container_of(work, struct cx18_stream, out_work_order);
721*4882a593Smuzhiyun
722*4882a593Smuzhiyun _cx18_stream_load_fw_queue(s);
723*4882a593Smuzhiyun }
724*4882a593Smuzhiyun
cx18_stream_configure_mdls(struct cx18_stream * s)725*4882a593Smuzhiyun static void cx18_stream_configure_mdls(struct cx18_stream *s)
726*4882a593Smuzhiyun {
727*4882a593Smuzhiyun cx18_unload_queues(s);
728*4882a593Smuzhiyun
729*4882a593Smuzhiyun switch (s->type) {
730*4882a593Smuzhiyun case CX18_ENC_STREAM_TYPE_YUV:
731*4882a593Smuzhiyun /*
732*4882a593Smuzhiyun * Height should be a multiple of 32 lines.
733*4882a593Smuzhiyun * Set the MDL size to the exact size needed for one frame.
734*4882a593Smuzhiyun * Use enough buffers per MDL to cover the MDL size
735*4882a593Smuzhiyun */
736*4882a593Smuzhiyun if (s->pixelformat == V4L2_PIX_FMT_HM12)
737*4882a593Smuzhiyun s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2;
738*4882a593Smuzhiyun else
739*4882a593Smuzhiyun s->mdl_size = 720 * s->cx->cxhdl.height * 2;
740*4882a593Smuzhiyun s->bufs_per_mdl = s->mdl_size / s->buf_size;
741*4882a593Smuzhiyun if (s->mdl_size % s->buf_size)
742*4882a593Smuzhiyun s->bufs_per_mdl++;
743*4882a593Smuzhiyun break;
744*4882a593Smuzhiyun case CX18_ENC_STREAM_TYPE_VBI:
745*4882a593Smuzhiyun s->bufs_per_mdl = 1;
746*4882a593Smuzhiyun if (cx18_raw_vbi(s->cx)) {
747*4882a593Smuzhiyun s->mdl_size = (s->cx->is_60hz ? 12 : 18)
748*4882a593Smuzhiyun * 2 * VBI_ACTIVE_SAMPLES;
749*4882a593Smuzhiyun } else {
750*4882a593Smuzhiyun /*
751*4882a593Smuzhiyun * See comment in cx18_vbi_setup() below about the
752*4882a593Smuzhiyun * extra lines we capture in sliced VBI mode due to
753*4882a593Smuzhiyun * the lines on which EAV RP codes toggle.
754*4882a593Smuzhiyun */
755*4882a593Smuzhiyun s->mdl_size = s->cx->is_60hz
756*4882a593Smuzhiyun ? (21 - 4 + 1) * 2 * VBI_HBLANK_SAMPLES_60HZ
757*4882a593Smuzhiyun : (23 - 2 + 1) * 2 * VBI_HBLANK_SAMPLES_50HZ;
758*4882a593Smuzhiyun }
759*4882a593Smuzhiyun break;
760*4882a593Smuzhiyun default:
761*4882a593Smuzhiyun s->bufs_per_mdl = 1;
762*4882a593Smuzhiyun s->mdl_size = s->buf_size * s->bufs_per_mdl;
763*4882a593Smuzhiyun break;
764*4882a593Smuzhiyun }
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun cx18_load_queues(s);
767*4882a593Smuzhiyun }
768*4882a593Smuzhiyun
cx18_start_v4l2_encode_stream(struct cx18_stream * s)769*4882a593Smuzhiyun int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
770*4882a593Smuzhiyun {
771*4882a593Smuzhiyun u32 data[MAX_MB_ARGUMENTS];
772*4882a593Smuzhiyun struct cx18 *cx = s->cx;
773*4882a593Smuzhiyun int captype = 0;
774*4882a593Smuzhiyun struct cx18_stream *s_idx;
775*4882a593Smuzhiyun
776*4882a593Smuzhiyun if (!cx18_stream_enabled(s))
777*4882a593Smuzhiyun return -EINVAL;
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun switch (s->type) {
782*4882a593Smuzhiyun case CX18_ENC_STREAM_TYPE_MPG:
783*4882a593Smuzhiyun captype = CAPTURE_CHANNEL_TYPE_MPEG;
784*4882a593Smuzhiyun cx->mpg_data_received = cx->vbi_data_inserted = 0;
785*4882a593Smuzhiyun cx->dualwatch_jiffies = jiffies;
786*4882a593Smuzhiyun cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode);
787*4882a593Smuzhiyun cx->search_pack_header = 0;
788*4882a593Smuzhiyun break;
789*4882a593Smuzhiyun
790*4882a593Smuzhiyun case CX18_ENC_STREAM_TYPE_IDX:
791*4882a593Smuzhiyun captype = CAPTURE_CHANNEL_TYPE_INDEX;
792*4882a593Smuzhiyun break;
793*4882a593Smuzhiyun case CX18_ENC_STREAM_TYPE_TS:
794*4882a593Smuzhiyun captype = CAPTURE_CHANNEL_TYPE_TS;
795*4882a593Smuzhiyun break;
796*4882a593Smuzhiyun case CX18_ENC_STREAM_TYPE_YUV:
797*4882a593Smuzhiyun captype = CAPTURE_CHANNEL_TYPE_YUV;
798*4882a593Smuzhiyun break;
799*4882a593Smuzhiyun case CX18_ENC_STREAM_TYPE_PCM:
800*4882a593Smuzhiyun captype = CAPTURE_CHANNEL_TYPE_PCM;
801*4882a593Smuzhiyun break;
802*4882a593Smuzhiyun case CX18_ENC_STREAM_TYPE_VBI:
803*4882a593Smuzhiyun #ifdef CX18_ENCODER_PARSES_SLICED
804*4882a593Smuzhiyun captype = cx18_raw_vbi(cx) ?
805*4882a593Smuzhiyun CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
806*4882a593Smuzhiyun #else
807*4882a593Smuzhiyun /*
808*4882a593Smuzhiyun * Currently we set things up so that Sliced VBI from the
809*4882a593Smuzhiyun * digitizer is handled as Raw VBI by the encoder
810*4882a593Smuzhiyun */
811*4882a593Smuzhiyun captype = CAPTURE_CHANNEL_TYPE_VBI;
812*4882a593Smuzhiyun #endif
813*4882a593Smuzhiyun cx->vbi.frame = 0;
814*4882a593Smuzhiyun cx->vbi.inserted_frame = 0;
815*4882a593Smuzhiyun memset(cx->vbi.sliced_mpeg_size,
816*4882a593Smuzhiyun 0, sizeof(cx->vbi.sliced_mpeg_size));
817*4882a593Smuzhiyun break;
818*4882a593Smuzhiyun default:
819*4882a593Smuzhiyun return -EINVAL;
820*4882a593Smuzhiyun }
821*4882a593Smuzhiyun
822*4882a593Smuzhiyun /* Clear Streamoff flags in case left from last capture */
823*4882a593Smuzhiyun clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
824*4882a593Smuzhiyun
825*4882a593Smuzhiyun cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE);
826*4882a593Smuzhiyun s->handle = data[0];
827*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
828*4882a593Smuzhiyun
829*4882a593Smuzhiyun /*
830*4882a593Smuzhiyun * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and
831*4882a593Smuzhiyun * set up all the parameters, as it is not obvious which parameters the
832*4882a593Smuzhiyun * firmware shares across capture channel types and which it does not.
833*4882a593Smuzhiyun *
834*4882a593Smuzhiyun * Some of the cx18_vapi() calls below apply to only certain capture
835*4882a593Smuzhiyun * channel types. We're hoping there's no harm in calling most of them
836*4882a593Smuzhiyun * anyway, as long as the values are all consistent. Setting some
837*4882a593Smuzhiyun * shared parameters will have no effect once an analog capture channel
838*4882a593Smuzhiyun * has started streaming.
839*4882a593Smuzhiyun */
840*4882a593Smuzhiyun if (captype != CAPTURE_CHANNEL_TYPE_TS) {
841*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
842*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
843*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0);
844*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1);
845*4882a593Smuzhiyun
846*4882a593Smuzhiyun /*
847*4882a593Smuzhiyun * Audio related reset according to
848*4882a593Smuzhiyun * Documentation/driver-api/media/drivers/cx2341x-devel.rst
849*4882a593Smuzhiyun */
850*4882a593Smuzhiyun if (atomic_read(&cx->ana_capturing) == 0)
851*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
852*4882a593Smuzhiyun s->handle, 12);
853*4882a593Smuzhiyun
854*4882a593Smuzhiyun /*
855*4882a593Smuzhiyun * Number of lines for Field 1 & Field 2 according to
856*4882a593Smuzhiyun * Documentation/driver-api/media/drivers/cx2341x-devel.rst
857*4882a593Smuzhiyun * Field 1 is 312 for 625 line systems in BT.656
858*4882a593Smuzhiyun * Field 2 is 313 for 625 line systems in BT.656
859*4882a593Smuzhiyun */
860*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3,
861*4882a593Smuzhiyun s->handle, 312, 313);
862*4882a593Smuzhiyun
863*4882a593Smuzhiyun if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE)
864*4882a593Smuzhiyun cx18_vbi_setup(s);
865*4882a593Smuzhiyun
866*4882a593Smuzhiyun /*
867*4882a593Smuzhiyun * Select to receive I, P, and B frame index entries, if the
868*4882a593Smuzhiyun * index stream is enabled. Otherwise disable index entry
869*4882a593Smuzhiyun * generation.
870*4882a593Smuzhiyun */
871*4882a593Smuzhiyun s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
872*4882a593Smuzhiyun cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2,
873*4882a593Smuzhiyun s->handle, cx18_stream_enabled(s_idx) ? 7 : 0);
874*4882a593Smuzhiyun
875*4882a593Smuzhiyun /* Call out to the common CX2341x API setup for user controls */
876*4882a593Smuzhiyun cx->cxhdl.priv = s;
877*4882a593Smuzhiyun cx2341x_handler_setup(&cx->cxhdl);
878*4882a593Smuzhiyun
879*4882a593Smuzhiyun /*
880*4882a593Smuzhiyun * When starting a capture and we're set for radio,
881*4882a593Smuzhiyun * ensure the video is muted, despite the user control.
882*4882a593Smuzhiyun */
883*4882a593Smuzhiyun if (!cx->cxhdl.video_mute &&
884*4882a593Smuzhiyun test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
885*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
886*4882a593Smuzhiyun (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1);
887*4882a593Smuzhiyun
888*4882a593Smuzhiyun /* Enable the Video Format Converter for UYVY 4:2:2 support,
889*4882a593Smuzhiyun * rather than the default HM12 Macroblovk 4:2:0 support.
890*4882a593Smuzhiyun */
891*4882a593Smuzhiyun if (captype == CAPTURE_CHANNEL_TYPE_YUV) {
892*4882a593Smuzhiyun if (s->pixelformat == V4L2_PIX_FMT_UYVY)
893*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
894*4882a593Smuzhiyun s->handle, 1);
895*4882a593Smuzhiyun else
896*4882a593Smuzhiyun /* If in doubt, default to HM12 */
897*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
898*4882a593Smuzhiyun s->handle, 0);
899*4882a593Smuzhiyun }
900*4882a593Smuzhiyun }
901*4882a593Smuzhiyun
902*4882a593Smuzhiyun if (atomic_read(&cx->tot_capturing) == 0) {
903*4882a593Smuzhiyun cx2341x_handler_set_busy(&cx->cxhdl, 1);
904*4882a593Smuzhiyun clear_bit(CX18_F_I_EOS, &cx->i_flags);
905*4882a593Smuzhiyun cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK);
906*4882a593Smuzhiyun }
907*4882a593Smuzhiyun
908*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle,
909*4882a593Smuzhiyun (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
910*4882a593Smuzhiyun (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
911*4882a593Smuzhiyun
912*4882a593Smuzhiyun /* Init all the cpu_mdls for this stream */
913*4882a593Smuzhiyun cx18_stream_configure_mdls(s);
914*4882a593Smuzhiyun _cx18_stream_load_fw_queue(s);
915*4882a593Smuzhiyun
916*4882a593Smuzhiyun /* begin_capture */
917*4882a593Smuzhiyun if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
918*4882a593Smuzhiyun CX18_DEBUG_WARN("Error starting capture!\n");
919*4882a593Smuzhiyun /* Ensure we're really not capturing before releasing MDLs */
920*4882a593Smuzhiyun set_bit(CX18_F_S_STOPPING, &s->s_flags);
921*4882a593Smuzhiyun if (s->type == CX18_ENC_STREAM_TYPE_MPG)
922*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
923*4882a593Smuzhiyun else
924*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
925*4882a593Smuzhiyun clear_bit(CX18_F_S_STREAMING, &s->s_flags);
926*4882a593Smuzhiyun /* FIXME - CX18_F_S_STREAMOFF as well? */
927*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
928*4882a593Smuzhiyun cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
929*4882a593Smuzhiyun s->handle = CX18_INVALID_TASK_HANDLE;
930*4882a593Smuzhiyun clear_bit(CX18_F_S_STOPPING, &s->s_flags);
931*4882a593Smuzhiyun if (atomic_read(&cx->tot_capturing) == 0) {
932*4882a593Smuzhiyun set_bit(CX18_F_I_EOS, &cx->i_flags);
933*4882a593Smuzhiyun cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
934*4882a593Smuzhiyun }
935*4882a593Smuzhiyun return -EINVAL;
936*4882a593Smuzhiyun }
937*4882a593Smuzhiyun
938*4882a593Smuzhiyun /* you're live! sit back and await interrupts :) */
939*4882a593Smuzhiyun if (captype != CAPTURE_CHANNEL_TYPE_TS)
940*4882a593Smuzhiyun atomic_inc(&cx->ana_capturing);
941*4882a593Smuzhiyun atomic_inc(&cx->tot_capturing);
942*4882a593Smuzhiyun return 0;
943*4882a593Smuzhiyun }
944*4882a593Smuzhiyun EXPORT_SYMBOL(cx18_start_v4l2_encode_stream);
945*4882a593Smuzhiyun
cx18_stop_all_captures(struct cx18 * cx)946*4882a593Smuzhiyun void cx18_stop_all_captures(struct cx18 *cx)
947*4882a593Smuzhiyun {
948*4882a593Smuzhiyun int i;
949*4882a593Smuzhiyun
950*4882a593Smuzhiyun for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
951*4882a593Smuzhiyun struct cx18_stream *s = &cx->streams[i];
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun if (!cx18_stream_enabled(s))
954*4882a593Smuzhiyun continue;
955*4882a593Smuzhiyun if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
956*4882a593Smuzhiyun cx18_stop_v4l2_encode_stream(s, 0);
957*4882a593Smuzhiyun }
958*4882a593Smuzhiyun }
959*4882a593Smuzhiyun
cx18_stop_v4l2_encode_stream(struct cx18_stream * s,int gop_end)960*4882a593Smuzhiyun int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
961*4882a593Smuzhiyun {
962*4882a593Smuzhiyun struct cx18 *cx = s->cx;
963*4882a593Smuzhiyun
964*4882a593Smuzhiyun if (!cx18_stream_enabled(s))
965*4882a593Smuzhiyun return -EINVAL;
966*4882a593Smuzhiyun
967*4882a593Smuzhiyun /* This function assumes that you are allowed to stop the capture
968*4882a593Smuzhiyun and that we are actually capturing */
969*4882a593Smuzhiyun
970*4882a593Smuzhiyun CX18_DEBUG_INFO("Stop Capture\n");
971*4882a593Smuzhiyun
972*4882a593Smuzhiyun if (atomic_read(&cx->tot_capturing) == 0)
973*4882a593Smuzhiyun return 0;
974*4882a593Smuzhiyun
975*4882a593Smuzhiyun set_bit(CX18_F_S_STOPPING, &s->s_flags);
976*4882a593Smuzhiyun if (s->type == CX18_ENC_STREAM_TYPE_MPG)
977*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end);
978*4882a593Smuzhiyun else
979*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
980*4882a593Smuzhiyun
981*4882a593Smuzhiyun if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) {
982*4882a593Smuzhiyun CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
983*4882a593Smuzhiyun }
984*4882a593Smuzhiyun
985*4882a593Smuzhiyun if (s->type != CX18_ENC_STREAM_TYPE_TS)
986*4882a593Smuzhiyun atomic_dec(&cx->ana_capturing);
987*4882a593Smuzhiyun atomic_dec(&cx->tot_capturing);
988*4882a593Smuzhiyun
989*4882a593Smuzhiyun /* Clear capture and no-read bits */
990*4882a593Smuzhiyun clear_bit(CX18_F_S_STREAMING, &s->s_flags);
991*4882a593Smuzhiyun
992*4882a593Smuzhiyun /* Tell the CX23418 it can't use our buffers anymore */
993*4882a593Smuzhiyun cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
994*4882a593Smuzhiyun
995*4882a593Smuzhiyun cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
996*4882a593Smuzhiyun s->handle = CX18_INVALID_TASK_HANDLE;
997*4882a593Smuzhiyun clear_bit(CX18_F_S_STOPPING, &s->s_flags);
998*4882a593Smuzhiyun
999*4882a593Smuzhiyun if (atomic_read(&cx->tot_capturing) > 0)
1000*4882a593Smuzhiyun return 0;
1001*4882a593Smuzhiyun
1002*4882a593Smuzhiyun cx2341x_handler_set_busy(&cx->cxhdl, 0);
1003*4882a593Smuzhiyun cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
1004*4882a593Smuzhiyun wake_up(&s->waitq);
1005*4882a593Smuzhiyun
1006*4882a593Smuzhiyun return 0;
1007*4882a593Smuzhiyun }
1008*4882a593Smuzhiyun EXPORT_SYMBOL(cx18_stop_v4l2_encode_stream);
1009*4882a593Smuzhiyun
cx18_find_handle(struct cx18 * cx)1010*4882a593Smuzhiyun u32 cx18_find_handle(struct cx18 *cx)
1011*4882a593Smuzhiyun {
1012*4882a593Smuzhiyun int i;
1013*4882a593Smuzhiyun
1014*4882a593Smuzhiyun /* find first available handle to be used for global settings */
1015*4882a593Smuzhiyun for (i = 0; i < CX18_MAX_STREAMS; i++) {
1016*4882a593Smuzhiyun struct cx18_stream *s = &cx->streams[i];
1017*4882a593Smuzhiyun
1018*4882a593Smuzhiyun if (s->video_dev.v4l2_dev && (s->handle != CX18_INVALID_TASK_HANDLE))
1019*4882a593Smuzhiyun return s->handle;
1020*4882a593Smuzhiyun }
1021*4882a593Smuzhiyun return CX18_INVALID_TASK_HANDLE;
1022*4882a593Smuzhiyun }
1023*4882a593Smuzhiyun
cx18_handle_to_stream(struct cx18 * cx,u32 handle)1024*4882a593Smuzhiyun struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
1025*4882a593Smuzhiyun {
1026*4882a593Smuzhiyun int i;
1027*4882a593Smuzhiyun struct cx18_stream *s;
1028*4882a593Smuzhiyun
1029*4882a593Smuzhiyun if (handle == CX18_INVALID_TASK_HANDLE)
1030*4882a593Smuzhiyun return NULL;
1031*4882a593Smuzhiyun
1032*4882a593Smuzhiyun for (i = 0; i < CX18_MAX_STREAMS; i++) {
1033*4882a593Smuzhiyun s = &cx->streams[i];
1034*4882a593Smuzhiyun if (s->handle != handle)
1035*4882a593Smuzhiyun continue;
1036*4882a593Smuzhiyun if (cx18_stream_enabled(s))
1037*4882a593Smuzhiyun return s;
1038*4882a593Smuzhiyun }
1039*4882a593Smuzhiyun return NULL;
1040*4882a593Smuzhiyun }
1041