// SPDX-License-Identifier: GPL-2.0-or-later
/*
* cx18 ioctl system call
*
* Derived from ivtv-ioctl.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-version.h"
#include "cx18-mailbox.h"
#include "cx18-i2c.h"
#include "cx18-queue.h"
#include "cx18-fileops.h"
#include "cx18-vbi.h"
#include "cx18-audio.h"
#include "cx18-video.h"
#include "cx18-streams.h"
#include "cx18-ioctl.h"
#include "cx18-gpio.h"
#include "cx18-controls.h"
#include "cx18-cards.h"
#include "cx18-av-core.h"
#include <media/tveeprom.h>
#include <media/v4l2-event.h>
static const struct v4l2_fmtdesc cx18_formats_yuv[] = {
{
.index = 0,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.pixelformat = V4L2_PIX_FMT_NV12_16L16,
},
{
.index = 1,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.pixelformat = V4L2_PIX_FMT_UYVY,
},
};
static const struct v4l2_fmtdesc cx18_formats_mpeg[] = {
{
.index = 0,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.flags = V4L2_FMT_FLAG_COMPRESSED,
.pixelformat = V4L2_PIX_FMT_MPEG,
},
};
static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
struct v4l2_format *fmt)
{
struct cx18_open_id *id = fh2id(fh);
struct cx18 *cx = id->cx;
struct cx18_stream *s = &cx->streams[id->type];
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
pixfmt->width = cx->cxhdl.width;
pixfmt->height = cx->cxhdl.height;
pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
pixfmt->field = V4L2_FIELD_INTERLACED;
if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
pixfmt->pixelformat = s->pixelformat;
pixfmt->sizeimage = s->vb_bytes_per_frame;
pixfmt->bytesperline = s->vb_bytes_per_line;
} else {
pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
pixfmt->sizeimage = 128 * 1024;
pixfmt->bytesperline = 0;
}
return 0;
}
static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
struct v4l2_format *fmt)
{
struct cx18_open_id *id = fh2id(fh);
struct cx18 *cx = id->cx;
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
int w = pixfmt->width;
int h = pixfmt->height;
w = min(w, 720);
w = max(w, 720 / 16);
h = min(h, cx->is_50hz ? 576 : 480);
h = max(h, (cx->is_50hz ? 576 : 480) / 8);
if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
if (pixfmt->pixelformat != V4L2_PIX_FMT_NV12_16L16 &&
pixfmt->pixelformat != V4L2_PIX_FMT_UYVY)
pixfmt->pixelformat = V4L2_PIX_FMT_UYVY;
/* YUV height must be a multiple of 32 */
h = round_up(h, 32);
/*
* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
* UYUV YUV size is (Y=(h*720) + UV=(h*(720)))
*/
if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12_16L16) {
pixfmt->sizeimage = h * 720 * 3 / 2;
pixfmt->bytesperline = 720; /* First plane */
} else {
pixfmt->sizeimage = h * 720 * 2;
pixfmt->bytesperline = 1440; /* Packed */
}
} else {
pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
pixfmt->sizeimage = 128 * 1024;
pixfmt->bytesperline = 0;
}
pixfmt->width = w;
pixfmt->height = h;
pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
pixfmt->field = V4L2_FIELD_INTERLACED;
return 0;
}
static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
struct v4l2_format *fmt)
{
struct cx18_open_id *id = fh2id(fh);
struct cx18 *cx = id->cx;
struct v4l2_subdev_format format = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
struct cx18_stream *s = &cx->streams[id->type];
int ret;
int w, h;
ret = cx18_try_fmt_vid_cap(file, fh, fmt);
if (ret)
return ret;
w = fmt->fmt.pix.width;
h = fmt->fmt.pix.height;
if (cx->cxhdl.width == w && cx->cxhdl.height == h &&
s->pixelformat == fmt->fmt.pix.pixelformat)
return 0;
if (atomic_read(&cx->ana_capturing) > 0)
return -EBUSY;
s->pixelformat = fmt->fmt.pix.pixelformat;
s->vb_bytes_per_frame = fmt->fmt.pix.sizeimage;
s