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