// SPDX-License-Identifier: GPL-2.0+
/*
* Video Capture Subdev for Freescale i.MX5/6 SOC
*
* Copyright (c) 2012-2016 Mentor Graphics Inc.
*/
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mc.h>
#include <media/v4l2-subdev.h>
#include <media/videobuf2-dma-contig.h>
#include <video/imx-ipu-v3.h>
#include <media/imx.h>
#include "imx-media.h"
#define IMX_CAPTURE_NAME "imx-capture"
struct capture_priv {
struct imx_media_video_dev vdev;
struct v4l2_subdev *src_sd;
int src_sd_pad;
struct device *dev;
struct imx_media_dev *md;
struct media_pad vdev_pad;
struct mutex mutex; /* capture device mutex */
/* the videobuf2 queue */
struct vb2_queue q;
/* list of ready imx_media_buffer's from q */
struct list_head ready_q;
/* protect ready_q */
spinlock_t q_lock;
/* controls inherited from subdevs */
struct v4l2_ctrl_handler ctrl_hdlr;
/* misc status */
bool stop; /* streaming is stopping */
};
#define to_capture_priv(v) container_of(v, struct capture_priv, vdev)
/* In bytes, per queue */
#define VID_MEM_LIMIT SZ_64M
static const struct vb2_ops capture_qops;
/*
* Video ioctls follow
*/
static int vidioc_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
struct capture_priv *priv = video_drvdata(file);
strscpy(cap->driver, IMX_CAPTURE_NAME, sizeof(cap->driver));
strscpy(cap->card, IMX_CAPTURE_NAME, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info),
"platform:%s", priv->src_sd->name);
return 0;
}
static int capture_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct capture_priv *priv = video_drvdata(file);
const struct imx_media_pixfmt *cc;
struct v4l2_subdev_frame_size_enum fse = {
.index = fsize->index,
.pad = priv->src_sd_pad,
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
int ret;
cc = imx_media_find_format(fsize->pixel_format, CS_SEL_ANY, true);
if (!cc)
return -EINVAL;
fse.code = cc->codes[0];
ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_size, NULL, &fse);
if (ret)
return ret;
if (fse.min_width == fse.max_width &&
fse.min_height == fse.max_height) {
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
fsize->discrete.width = fse.min_width;
fsize->discrete.height = fse.min_height;
} else {
fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
fsize->stepwise.min_width = fse.min_width;
fsize->stepwise.max_width = fse.max_width;
fsize->stepwise.min_height = fse.min_height;
fsize->stepwise.max_height = fse.max_height;
fsize->stepwise.step_width = 1;
fsize->stepwise.step_height = 1;
}
return 0;
}
static int capture_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *fival)
{
struct capture_priv *priv = video_drvdata(file);
const struct imx_media_pixfmt *cc;
struct v4l2_subdev_frame_interval_enum fie = {
.index = fival->index,
.pad = priv->src_sd_pad,
.width = fival->width,
.height = fival->height,
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
int ret;
cc = imx_media_find_format(fival->pixel_format, CS_SEL_ANY, true);
if (!cc)
return -EINVAL;
fie.code = cc->codes[0];
ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_interval,
NULL, &fie);
if (ret)
return ret;
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
fival->discrete = fie.interval;
return 0;
}
static int capture_enum_fmt_vid_cap(struct file *file, void *fh,
struct v4l2_fmtdesc *f)
{
struct capture_priv *priv = video_drvdata(file);
const struct imx_media_pixfmt *cc_src;
struct v4l2_subdev_format fmt_src;
u32 fourcc;
int ret;
fmt_src.pad = priv->src_sd_pad;
fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt