// SPDX-License-Identifier: GPL-2.0-or-later
/*
* cx18 init/start/stop/exit stream functions
*
* Derived from ivtv-streams.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
* Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
*/
#include "cx18-driver.h"
#include "cx18-io.h"
#include "cx18-fileops.h"
#include "cx18-mailbox.h"
#include "cx18-i2c.h"
#include "cx18-queue.h"
#include "cx18-ioctl.h"
#include "cx18-streams.h"
#include "cx18-cards.h"
#include "cx18-scb.h"
#include "cx18-dvb.h"
#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
static const struct v4l2_file_operations cx18_v4l2_enc_fops = {
.owner = THIS_MODULE,
.read = cx18_v4l2_read,
.open = cx18_v4l2_open,
.unlocked_ioctl = video_ioctl2,
.release = cx18_v4l2_close,
.poll = cx18_v4l2_enc_poll,
};
static const struct v4l2_file_operations cx18_v4l2_enc_yuv_fops = {
.owner = THIS_MODULE,
.open = cx18_v4l2_open,
.unlocked_ioctl = video_ioctl2,
.release = cx18_v4l2_close,
.poll = vb2_fop_poll,
.read = vb2_fop_read,
.mmap = vb2_fop_mmap,
};
/* offset from 0 to register ts v4l2 minors on */
#define CX18_V4L2_ENC_TS_OFFSET 16
/* offset from 0 to register pcm v4l2 minors on */
#define CX18_V4L2_ENC_PCM_OFFSET 24
/* offset from 0 to register yuv v4l2 minors on */
#define CX18_V4L2_ENC_YUV_OFFSET 32
static struct {
const char *name;
int vfl_type;
int num_offset;
int dma;
u32 caps;
} cx18_stream_info[] = {
{ /* CX18_ENC_STREAM_TYPE_MPG */
"encoder MPEG",
VFL_TYPE_VIDEO, 0,
DMA_FROM_DEVICE,
V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
V4L2_CAP_AUDIO | V4L2_CAP_TUNER
},
{ /* CX18_ENC_STREAM_TYPE_TS */
"TS",
VFL_TYPE_VIDEO, -1,
DMA_FROM_DEVICE,
},
{ /* CX18_ENC_STREAM_TYPE_YUV */
"encoder YUV",
VFL_TYPE_VIDEO, CX18_V4L2_ENC_YUV_OFFSET,
DMA_FROM_DEVICE,
V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | V4L2_CAP_TUNER
},
{ /* CX18_ENC_STREAM_TYPE_VBI */
"encoder VBI",
VFL_TYPE_VBI, 0,
DMA_FROM_DEVICE,
V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE |
V4L2_CAP_READWRITE | V4L2_CAP_AUDIO | V4L2_CAP_TUNER
},
{ /* CX18_ENC_STREAM_TYPE_PCM */
"encoder PCM audio",
VFL_TYPE_VIDEO, CX18_V4L2_ENC_PCM_OFFSET,
DMA_FROM_DEVICE,
V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
},
{ /* CX18_ENC_STREAM_TYPE_IDX */
"encoder IDX",
VFL_TYPE_VIDEO, -1,
DMA_FROM_DEVICE,
},
{ /* CX18_ENC_STREAM_TYPE_RAD */
"encoder radio",
VFL_TYPE_RADIO, 0,
DMA_NONE,
V4L2_CAP_RADIO | V4L2_CAP_TUNER
},
};
static int cx18_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], struct device *alloc_devs[])
{
unsigned int q_num_bufs = vb2_get_num_buffers(vq);
struct cx18_stream *s = vb2_get_drv_priv(vq);
struct cx18 *cx = s->cx;
unsigned int szimage;
/*
* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
* UYUV YUV size is (Y=(h*720) + UV=(h*(720)))
*/
if (s->pixelformat == V4L2_PIX_FMT_NV12_16L16)
szimage = cx->cxhdl.height * 720 * 3 / 2;
else
szimage = cx->cxhdl.height * 720 * 2;
/*
* Let's request at least three buffers: two for the
* DMA engine and one for userspace.
*/
if (q_num_bufs + *nbuffers < 3)
*nbuffers = 3 - q_num_bufs;
if (*nplanes) {
if (*nplanes != 1 || sizes[0] < szimage)
return -EINVAL;
return 0;
}
sizes[0] = szimage;
*nplanes = 1;
return 0;
}
static void cx18_buf_queue(struct vb2_buffer *vb)
{
struct cx18_stream *s = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct cx18_vb2_buffer *buf =
container_of(vbuf, struct cx18_vb2_buffer, vb);
unsigned long flags;
spin_lock_irqsave(&s->vb_lock, flags);
list_add_tail(&buf->list, &s->vb_capture);
spin_unlock_irqrestore(&s->vb_lock, flags);
}
static int cx18_buf_prepare(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct cx18_stream *s = vb2_get_drv_priv(vb->vb2_queue);
struct cx18 *cx = s->cx;
unsigned int size;
/*
* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
* UYUV YUV size is (Y=(h*720) + UV=(h*(720)))
*/
if (s->pixelformat == V4L2_PIX_FMT_NV12_16L16)
size = cx->cxhdl.height * 720 * 3 / 2;
else
size = cx->cxhdl.height * 720 * 2;
if (vb2_plane_size(vb, 0) < size)
return -EINVAL;
vb2_set_plane_payload(vb, 0, size);
vbuf->field = V4L2_FIELD_INTERLACED;
return 0;
}
void cx18_clear_queue(struct cx18_stream *s, enum vb2_buffer_state state)
{
while (!list_empty(&s->vb_capture)) {
struct cx18_vb2_buffer *buf;
buf = list_first_entry(&s->vb_capture,
struct cx18_vb2_buffer, list);
list_del(&buf->list);
vb2_buffer_done(&buf->vb.vb2_buf, state);
}
}
static