2025-05-10 21:58:58 +08:00

2256 lines
58 KiB
C

/*
*************************************************************************
* Rockchip driver for CIF ISP 1.0
* (Based on Intel driver for sofiaxxx)
*
* Copyright (C) 2015 Intel Mobile Communications GmbH
* Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*************************************************************************
*/
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-dma-sg.h>
#include "cif_isp10.h"
#include "cif_isp10_regs.h"
#include "cif_isp10_version.h"
#include <linux/module.h>
#include <linux/of.h>
#include <media/v4l2-controls_rockchip.h>
#include <linux/pm_runtime.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#define CIF_ISP10_V4L2_SP_DEV_MAJOR -1
#define CIF_ISP10_V4L2_ISP_DEV_MAJOR -1
#define CIF_ISP10_V4L2_MP_DEV_MAJOR -1
#define CIF_ISP10_V4L2_DMA_DEV_MAJOR -1
#define SP_DEV 0
#define MP_DEV 1
#define DMA_DEV 2
#define ISP_DEV 3
/* One structure per open file handle */
struct cif_isp10_v4l2_fh {
enum cif_isp10_stream_id stream_id;
struct v4l2_fh fh;
};
/* One structure per video node */
struct cif_isp10_v4l2_node {
struct vb2_queue buf_queue;
/* queue lock */
struct mutex qlock;
struct video_device vdev;
struct media_pad pad;
struct cif_isp10_pipeline *pipe;
int users;
struct cif_isp10_v4l2_fh *owner;
};
/* One structure per device */
struct cif_isp10_v4l2_device {
struct cif_isp10_v4l2_node node[4];
};
static struct cif_isp10_v4l2_fh *to_fh(struct file *file)
{
if (!file || !file->private_data)
return NULL;
return container_of(file->private_data, struct cif_isp10_v4l2_fh, fh);
}
static struct cif_isp10_v4l2_node *to_node(struct cif_isp10_v4l2_fh *fh)
{
struct video_device *vdev = fh ? fh->fh.vdev : NULL;
if (!fh || !vdev)
return NULL;
return container_of(vdev, struct cif_isp10_v4l2_node, vdev);
}
static inline struct cif_isp10_v4l2_node *queue_to_node(struct vb2_queue *q)
{
return container_of(q, struct cif_isp10_v4l2_node, buf_queue);
}
static inline struct cif_isp10_buffer *to_cif_isp10_vb(
struct vb2_v4l2_buffer *vb)
{
return container_of(vb, struct cif_isp10_buffer, vb);
}
static struct vb2_queue *to_vb2_queue(
struct file *file)
{
struct cif_isp10_v4l2_fh *fh = to_fh(file);
struct video_device *vdev = fh ? fh->fh.vdev : NULL;
struct cif_isp10_v4l2_node *node = to_node(fh);
struct vb2_queue *q;
if (unlikely(!vdev)) {
cif_isp10_pltfrm_pr_err(NULL,
"vdev is NULL\n");
WARN_ON(1);
}
q = &node->buf_queue;
if (unlikely(!q)) {
cif_isp10_pltfrm_pr_err(NULL,
"buffer queue is NULL\n");
WARN_ON(1);
}
return q;
}
static enum cif_isp10_stream_id to_stream_id(
struct file *file)
{
struct cif_isp10_v4l2_fh *fh;
if (unlikely(!file)) {
cif_isp10_pltfrm_pr_err(NULL,
"NULL file handle\n");
WARN_ON(1);
}
fh = to_fh(file);
if (unlikely(!fh)) {
cif_isp10_pltfrm_pr_err(NULL,
"fh is NULL\n");
WARN_ON(1);
}
return fh->stream_id;
}
static struct cif_isp10_device *to_cif_isp10_device(
struct vb2_queue *queue)
{
return queue->drv_priv;
}
static enum cif_isp10_stream_id to_cif_isp10_stream_id(
struct vb2_queue *queue)
{
struct cif_isp10_v4l2_node *node =
container_of(queue, struct cif_isp10_v4l2_node, buf_queue);
struct video_device *vdev =
&node->vdev;
if (!strcmp(vdev->name, SP_VDEV_NAME))
return CIF_ISP10_STREAM_SP;
if (!strcmp(vdev->name, MP_VDEV_NAME))
return CIF_ISP10_STREAM_MP;
if (!strcmp(vdev->name, DMA_VDEV_NAME))
return CIF_ISP10_STREAM_DMA;
cif_isp10_pltfrm_pr_err(NULL,
"unsupported/unknown device name %s\n", vdev->name);
return -EINVAL;
}
static const char *cif_isp10_v4l2_buf_type_string(
enum v4l2_buf_type buf_type)
{
switch (buf_type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
return "VIDEO_CAPTURE";
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
return "VIDEO_OVERLAY";
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return "VIDEO_OUTPUT";
default:
break;
}
return "UNKNOWN/UNSUPPORTED";
}
const char *cif_isp10_v4l2_pix_fmt_string(
int pix_fmt)
{
switch (pix_fmt) {
case V4L2_PIX_FMT_RGB332:
return "V4L2-RGB332";
case V4L2_PIX_FMT_RGB555:
return "V4L2-RGB555";
case V4L2_PIX_FMT_RGB565:
return "V4L2-RGB565";
case V4L2_PIX_FMT_RGB555X:
return "V4L2-RGB555X";
case V4L2_PIX_FMT_RGB565X:
return "V4L2-RGB565X";
case V4L2_PIX_FMT_BGR24:
return "V4L2-BGR24";
case V4L2_PIX_FMT_RGB24:
return "V4L2-RGB24";
case V4L2_PIX_FMT_BGR32:
return "V4L2-BGR32";
case V4L2_PIX_FMT_RGB32:
return "V4L2-RGB32";
case V4L2_PIX_FMT_GREY:
return "V4L2-GREY";
case V4L2_PIX_FMT_YVU410:
return "V4L2-YVU410";
case V4L2_PIX_FMT_YVU420:
return "V4L2-YVU420";
case V4L2_PIX_FMT_YUYV:
return "V4L2-YUYV";
case V4L2_PIX_FMT_UYVY:
return "V4L2-UYVY";
case V4L2_PIX_FMT_YUV422P:
return "V4L2-YUV422P";
case V4L2_PIX_FMT_YUV411P:
return "V4L2-YUV411P";
case V4L2_PIX_FMT_Y41P:
return "V4L2-Y41P";
case V4L2_PIX_FMT_NV12:
return "V4L2-NV12";
case V4L2_PIX_FMT_NV21:
return "V4L2-NV21";
case V4L2_PIX_FMT_YUV410:
return "V4L2-YUV410";
case V4L2_PIX_FMT_YUV420:
return "V4L2--YUV420";
case V4L2_PIX_FMT_YYUV:
return "V4L2-YYUV";
case V4L2_PIX_FMT_HI240:
return "V4L2-HI240";
case V4L2_PIX_FMT_WNVA:
return "V4L2-WNVA";
case V4L2_PIX_FMT_NV16:
return "V4L2-NV16";
case V4L2_PIX_FMT_YUV444:
return "V4L2-YUV444P";
case V4L2_PIX_FMT_NV24:
return "M5-YUV444SP";
case V4L2_PIX_FMT_JPEG:
return "V4L2-JPEG";
case V4L2_PIX_FMT_SGRBG10:
return "RAW-BAYER-10Bits";
case V4L2_PIX_FMT_SGRBG8:
return "RAW-BAYER-8Bits";
}
return "UNKNOWN/UNSUPPORTED";
}
static int cif_isp10_v4l2_cid2cif_isp10_cid(u32 v4l2_cid)
{
switch (v4l2_cid) {
case V4L2_CID_FLASH_LED_MODE:
return CIF_ISP10_CID_FLASH_MODE;
case V4L2_CID_AUTOGAIN:
return CIF_ISP10_CID_AUTO_GAIN;
case V4L2_EXPOSURE_AUTO:
return CIF_ISP10_CID_AUTO_EXPOSURE;
case V4L2_CID_AUTO_WHITE_BALANCE:
return CIF_ISP10_CID_AUTO_WHITE_BALANCE;
case V4L2_CID_BLACK_LEVEL:
return CIF_ISP10_CID_BLACK_LEVEL;
case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
return CIF_ISP10_CID_WB_TEMPERATURE;
case V4L2_CID_EXPOSURE:
return CIF_ISP10_CID_EXPOSURE_TIME;
case V4L2_CID_GAIN:
return CIF_ISP10_CID_ANALOG_GAIN;
case V4L2_CID_FOCUS_ABSOLUTE:
return CIF_ISP10_CID_FOCUS_ABSOLUTE;
case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
return CIF_ISP10_CID_AUTO_N_PRESET_WHITE_BALANCE;
case V4L2_CID_SCENE_MODE:
return CIF_ISP10_CID_SCENE_MODE;
case V4L2_CID_COLORFX:
return CIF_ISP10_CID_IMAGE_EFFECT;
case V4L2_CID_JPEG_COMPRESSION_QUALITY:
return CIF_ISP10_CID_JPEG_QUALITY;
case V4L2_CID_HFLIP:
return CIF_ISP10_CID_HFLIP;
case V4L2_CID_VFLIP:
return CIF_ISP10_CID_VFLIP;
case V4L2_CID_ISO_SENSITIVITY:
return CIF_ISP10_CID_ISO_SENSITIVITY;
case RK_V4L2_CID_AUTO_FPS:
return CIF_ISP10_CID_AUTO_FPS;
case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
return CIF_ISP10_CID_MIN_BUFFER_FOR_CAPTURE;
case V4L2_CID_TEST_PATTERN:
return CIF_ISP10_CID_TEST_PATTERN;
default:
cif_isp10_pltfrm_pr_err(NULL,
"unknown/unsupported V4L2 CID 0x%x\n",
v4l2_cid);
break;
}
return -EINVAL;
}
static enum cif_isp10_image_effect cif_isp10_v4l2_colorfx2cif_isp10_ie(
u32 v4l2_colorfx)
{
switch (v4l2_colorfx) {
case V4L2_COLORFX_SEPIA:
return CIF_ISP10_IE_SEPIA;
case V4L2_COLORFX_BW:
return CIF_ISP10_IE_BW;
case V4L2_COLORFX_NEGATIVE:
return CIF_ISP10_IE_NEGATIVE;
case V4L2_COLORFX_EMBOSS:
return CIF_ISP10_IE_EMBOSS;
case V4L2_COLORFX_SKETCH:
return CIF_ISP10_IE_SKETCH;
case V4L2_COLORFX_NONE:
return CIF_ISP10_IE_NONE;
default:
cif_isp10_pltfrm_pr_err(NULL,
"unknown/unsupported V4L2 COLORFX %d\n",
v4l2_colorfx);
break;
}
return -EINVAL;
}
static enum cif_isp10_pix_fmt cif_isp10_v4l2_pix_fmt2cif_isp10_pix_fmt(
u32 v4l2_pix_fmt, struct vb2_queue *queue)
{
/*struct cif_isp10_v4l2_node *node =
* container_of(queue, struct cif_isp10_v4l2_node, buf_queue);
* struct video_device *vdev =
* &node->vdev;
*/
switch (v4l2_pix_fmt) {
case V4L2_PIX_FMT_GREY:
return CIF_YUV400;
case V4L2_PIX_FMT_Y10:
return CIF_Y10;
case V4L2_PIX_FMT_Y12:
return CIF_Y12;
case V4L2_PIX_FMT_YUV420:
return CIF_YUV420P;
case V4L2_PIX_FMT_YVU420:
return CIF_YVU420P;
case V4L2_PIX_FMT_NV12:
return CIF_YUV420SP;
case V4L2_PIX_FMT_NV21:
return CIF_YVU420SP;
case V4L2_PIX_FMT_YUYV:
return CIF_YUV422I;
case V4L2_PIX_FMT_YVYU:
return CIF_YVU422I;
case V4L2_PIX_FMT_UYVY:
return CIF_UYV422I;
case V4L2_PIX_FMT_YUV422P:
return CIF_YUV422P;
case V4L2_PIX_FMT_NV16:
return CIF_YUV422SP;
case V4L2_PIX_FMT_YUV444:
return CIF_YUV444P;
case V4L2_PIX_FMT_NV24:
return CIF_YUV444SP;
case V4L2_PIX_FMT_RGB565:
return CIF_RGB565;
case V4L2_PIX_FMT_RGB24:
return CIF_RGB888;
case V4L2_PIX_FMT_SBGGR8:
return CIF_BAYER_SBGGR8;
case V4L2_PIX_FMT_SGBRG8:
return CIF_BAYER_SGBRG8;
case V4L2_PIX_FMT_SGRBG8:
return CIF_BAYER_SGRBG8;
case V4L2_PIX_FMT_SRGGB8:
return CIF_BAYER_SRGGB8;
case V4L2_PIX_FMT_SBGGR10:
return CIF_BAYER_SBGGR10;
case V4L2_PIX_FMT_SGBRG10:
return CIF_BAYER_SGBRG10;
case V4L2_PIX_FMT_SGRBG10:
return CIF_BAYER_SGRBG10;
case V4L2_PIX_FMT_SRGGB10:
return CIF_BAYER_SRGGB10;
case V4L2_PIX_FMT_SBGGR12:
return CIF_BAYER_SBGGR12;
case V4L2_PIX_FMT_SGBRG12:
return CIF_BAYER_SGBRG12;
case V4L2_PIX_FMT_SGRBG12:
return CIF_BAYER_SGRBG12;
case V4L2_PIX_FMT_SRGGB12:
return CIF_BAYER_SRGGB12;
case V4L2_PIX_FMT_JPEG:
return CIF_JPEG;
default:
cif_isp10_pltfrm_pr_err(NULL,
"unknown or unsupported V4L2 pixel format %c%c%c%c\n",
(u8)(v4l2_pix_fmt & 0xff),
(u8)((v4l2_pix_fmt >> 8) & 0xff),
(u8)((v4l2_pix_fmt >> 16) & 0xff),
(u8)((v4l2_pix_fmt >> 24) & 0xff));
return CIF_UNKNOWN_FORMAT;
}
}
static u32 cif_isp10_pix_fmt2v4l2_pix_fmt(
enum cif_isp10_pix_fmt pix_fmt, struct vb2_queue *queue)
{
/*struct cif_isp10_v4l2_node *node =
* container_of(queue, struct cif_isp10_v4l2_node, buf_queue);
* struct video_device *vdev =
* &node->vdev;
*/
switch (pix_fmt) {
case CIF_YUV400:
return V4L2_PIX_FMT_GREY;
case CIF_YUV420P:
return V4L2_PIX_FMT_YUV420;
case CIF_YVU420P:
return V4L2_PIX_FMT_YVU420;
case CIF_YUV420SP:
return V4L2_PIX_FMT_NV12;
case CIF_YVU420SP:
return V4L2_PIX_FMT_NV21;
case CIF_YUV422I:
return V4L2_PIX_FMT_YUYV;
case CIF_UYV422I:
return V4L2_PIX_FMT_UYVY;
case CIF_YUV422P:
return V4L2_PIX_FMT_YUV422P;
case CIF_YUV422SP:
return V4L2_PIX_FMT_NV16;
case CIF_YUV444P:
return V4L2_PIX_FMT_YUV444;
case CIF_YUV444SP:
return V4L2_PIX_FMT_NV24;
case CIF_RGB565:
return V4L2_PIX_FMT_RGB565;
case CIF_RGB888:
return V4L2_PIX_FMT_RGB24;
case CIF_BAYER_SBGGR8:
return V4L2_PIX_FMT_SBGGR8;
case CIF_BAYER_SGBRG8:
return V4L2_PIX_FMT_SGBRG8;
case CIF_BAYER_SGRBG8:
return V4L2_PIX_FMT_SGRBG8;
case CIF_BAYER_SRGGB8:
return V4L2_PIX_FMT_SRGGB8;
case CIF_BAYER_SBGGR10:
return V4L2_PIX_FMT_SBGGR10;
case CIF_BAYER_SGBRG10:
return V4L2_PIX_FMT_SGBRG10;
case CIF_BAYER_SGRBG10:
return V4L2_PIX_FMT_SGRBG10;
case CIF_BAYER_SRGGB10:
return V4L2_PIX_FMT_SRGGB10;
case CIF_BAYER_SBGGR12:
return V4L2_PIX_FMT_SBGGR12;
case CIF_BAYER_SGBRG12:
return V4L2_PIX_FMT_SGBRG12;
case CIF_BAYER_SGRBG12:
return V4L2_PIX_FMT_SGRBG12;
case CIF_BAYER_SRGGB12:
return V4L2_PIX_FMT_SRGGB12;
case CIF_JPEG:
return V4L2_PIX_FMT_JPEG;
default:
cif_isp10_pltfrm_pr_err(NULL,
"unknown or unsupported V4L2 pixel format %x\n",
pix_fmt);
return 0;
}
}
static int cif_isp10_v4l2_register_video_device(
struct cif_isp10_device *dev,
struct video_device *vdev,
const char *name,
int qtype,
int major,
const struct v4l2_file_operations *fops,
const struct v4l2_ioctl_ops *ioctl_ops)
{
int ret;
vdev->release = video_device_release;
strlcpy(vdev->name, name, sizeof(vdev->name));
vdev->vfl_type = qtype;
vdev->fops = fops;
video_set_drvdata(vdev, dev);
vdev->minor = -1;
vdev->ioctl_ops = ioctl_ops;
vdev->v4l2_dev = &dev->v4l2_dev;
if (qtype == V4L2_BUF_TYPE_VIDEO_OUTPUT)
vdev->vfl_dir = VFL_DIR_TX;
else
vdev->vfl_dir = VFL_DIR_RX;
ret = video_register_device(vdev, VFL_TYPE_GRABBER, major);
if (IS_ERR_VALUE(ret)) {
cif_isp10_pltfrm_pr_err(NULL,
"video_register_device failed with error %d\n", ret);
goto err;
}
cif_isp10_pltfrm_pr_info(NULL,
"video device video%d.%d (%s) successfully registered\n",
major, vdev->minor, name);
return 0;
err:
video_device_release(vdev);
cif_isp10_pltfrm_pr_err(NULL,
"failed with err %d\n", ret);
return ret;
}
static int cif_isp10_v4l2_register_imgsrc_subdev(
struct cif_isp10_device *dev)
{
unsigned int i;
struct v4l2_subdev *sd;
for (i = 0; i < dev->img_src_cnt; i++) {
if (dev->img_src_array[i] != NULL) {
sd = (struct v4l2_subdev *)
cif_isp10_img_src_g_img_src(
dev->img_src_array[i]);
if (sd) {
if (v4l2_device_register_subdev(
&dev->v4l2_dev,
sd) < 0)
cif_isp10_pltfrm_pr_err(dev->dev,
"register subdev(%s) failed!",
cif_isp10_img_src_g_name(dev->img_src_array[i]));
}
}
}
return v4l2_device_register_subdev_nodes(&dev->v4l2_dev);
}
static int cif_isp10_v4l2_streamon(
struct file *file,
void *priv,
enum v4l2_buf_type buf_type)
{
int ret;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
static u32 streamon_cnt_sp;
static u32 streamon_cnt_mp;
static u32 streamon_cnt_dma;
u32 stream_ids = to_stream_id(file);
mutex_lock(&dev->api_mutex);
cif_isp10_pltfrm_pr_dbg(dev->dev, "%s(%d)\n",
cif_isp10_v4l2_buf_type_string(queue->type),
(stream_ids & CIF_ISP10_STREAM_MP) ? ++streamon_cnt_mp :
((stream_ids & CIF_ISP10_STREAM_SP) ? ++streamon_cnt_sp :
++streamon_cnt_dma));
ret = vb2_streamon(queue, buf_type);
if (IS_ERR_VALUE(ret)) {
cif_isp10_pltfrm_pr_err(dev->dev,
"videobuf_streamon failed\n");
goto err;
}
ret = cif_isp10_streamon(dev, stream_ids);
if (IS_ERR_VALUE(ret)) {
goto err;
}
mutex_unlock(&dev->api_mutex);
return 0;
err:
(void)vb2_queue_release(queue);
cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret);
mutex_unlock(&dev->api_mutex);
return ret;
}
static int cif_isp10_v4l2_do_streamoff(
struct file *file)
{
int ret = 0;
int err;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
u32 stream_ids = to_stream_id(file);
cif_isp10_pltfrm_pr_dbg(dev->dev, "%s\n",
cif_isp10_v4l2_buf_type_string(queue->type));
err = cif_isp10_streamoff(dev, stream_ids);
if (IS_ERR_VALUE(err))
ret = -EFAULT;
err = vb2_streamoff(queue, queue->type);
if (IS_ERR_VALUE(err)) {
cif_isp10_pltfrm_pr_err(dev->dev,
"videobuf_streamoff failed with error %d\n", err);
ret = -EFAULT;
}
if (IS_ERR_VALUE(ret))
cif_isp10_pltfrm_pr_err(dev->dev,
"failed with error %d\n", ret);
return ret;
}
static int cif_isp10_v4l2_streamoff(
struct file *file,
void *priv,
enum v4l2_buf_type buf_type)
{
int ret;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
mutex_lock(&dev->api_mutex);
ret = cif_isp10_v4l2_do_streamoff(file);
if (IS_ERR_VALUE(ret))
cif_isp10_pltfrm_pr_err(NULL,
"failed with error %d\n", ret);
mutex_unlock(&dev->api_mutex);
return ret;
}
static int cif_isp10_v4l2_qbuf(
struct file *file,
void *priv,
struct v4l2_buffer *buf)
{
int ret;
ret = vb2_ioctl_qbuf(file, priv, buf);
if (IS_ERR_VALUE(ret))
cif_isp10_pltfrm_pr_err(NULL,
"videobuf_qbuf failed with error %d\n", ret);
return ret;
}
static int cif_isp10_v4l2_dqbuf(
struct file *file,
void *priv,
struct v4l2_buffer *buf)
{
int ret;
ret = vb2_ioctl_dqbuf(file, priv, buf);
if (IS_ERR_VALUE(ret) && (ret != -EAGAIN))
cif_isp10_pltfrm_pr_err(NULL,
"videobuf_dqbuf failed with error %d\n", ret);
else
cif_isp10_pltfrm_pr_dbg(NULL,
"dequeued buffer %d, size %d\n",
buf->index, buf->length);
return ret;
}
static int cif_isp10_v4l2_reqbufs(
struct file *file,
void *priv,
struct v4l2_requestbuffers *req)
{
int ret;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
enum cif_isp10_stream_id strm = to_cif_isp10_stream_id(queue);
mutex_lock(&dev->api_mutex);
ret = vb2_ioctl_reqbufs(file, priv, req);
if (IS_ERR_VALUE(ret)) {
cif_isp10_pltfrm_pr_err(NULL,
"videobuf_reqbufs failed with error %d\n", ret);
}
cif_isp10_reqbufs(dev, strm, req);
mutex_unlock(&dev->api_mutex);
return ret;
}
static int cif_isp10_v4l2_querybuf(
struct file *file,
void *priv,
struct v4l2_buffer *buf)
{
int ret;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
mutex_lock(&dev->api_mutex);
ret = vb2_ioctl_querybuf(file, priv, buf);
if (IS_ERR_VALUE(ret))
cif_isp10_pltfrm_pr_err(NULL,
"videobuf_querybuf failed with error %d\n", ret);
mutex_unlock(&dev->api_mutex);
return ret;
}
static int cif_isp10_v4l2_s_ctrl(
struct file *file,
void *priv,
struct v4l2_control *vc)
{
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
enum cif_isp10_cid id =
cif_isp10_v4l2_cid2cif_isp10_cid(vc->id);
int val = vc->value;
int ret;
if (IS_ERR_VALUE(id))
return id;
mutex_lock(&dev->api_mutex);
switch (vc->id) {
case V4L2_CID_COLORFX:
val = cif_isp10_v4l2_colorfx2cif_isp10_ie(val);
break;
case V4L2_CID_FLASH_LED_MODE:
if (vc->value == V4L2_FLASH_LED_MODE_NONE)
val = CIF_ISP10_FLASH_MODE_OFF;
else if (vc->value == V4L2_FLASH_LED_MODE_FLASH)
val = CIF_ISP10_FLASH_MODE_FLASH;
else if (vc->value == V4L2_FLASH_LED_MODE_TORCH)
val = CIF_ISP10_FLASH_MODE_TORCH;
else
val = -EINVAL;
break;
default:
break;
}
ret = cif_isp10_s_ctrl(dev, id, val);
mutex_unlock(&dev->api_mutex);
return ret;
}
static int cif_isp10_v4l2_s_fmt(
struct file *file,
void *priv,
struct v4l2_format *f)
{
int ret;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
struct cif_isp10_v4l2_fh *fh = to_fh(file);
struct cif_isp10_v4l2_node *node = to_node(fh);
struct cif_isp10_strm_fmt strm_fmt;
cif_isp10_pltfrm_pr_dbg(NULL,
"%s\n",
cif_isp10_v4l2_buf_type_string(queue->type));
mutex_lock(&dev->api_mutex);
if (node->owner && node->owner != fh) {
mutex_unlock(&dev->api_mutex);
return -EBUSY;
}
strm_fmt.frm_fmt.pix_fmt =
cif_isp10_v4l2_pix_fmt2cif_isp10_pix_fmt(
f->fmt.pix.pixelformat, queue);
strm_fmt.frm_fmt.width = f->fmt.pix.width;
strm_fmt.frm_fmt.height = f->fmt.pix.height;
/* strm_fmt.frm_fmt.quantization = f->fmt.pix.quantization; */
strm_fmt.frm_fmt.quantization = 0;
ret = cif_isp10_s_fmt(dev,
to_stream_id(file),
&strm_fmt,
f->fmt.pix.bytesperline);
//TODO:: check s_fmt format field and size;
f->fmt.pix.field = V4L2_FIELD_NONE;
cif_isp10_calc_min_out_buff_size(dev,
to_stream_id(file), &f->fmt.pix.sizeimage, false);
//f->fmt.pix.sizeimage = PAGE_ALIGN(f->fmt.pix.sizeimage);
if (IS_ERR_VALUE(ret))
goto err;
mutex_unlock(&dev->api_mutex);
return 0;
err:
cif_isp10_pltfrm_pr_err(NULL,
"failed with error %d\n", ret);
mutex_unlock(&dev->api_mutex);
return ret;
}
/* existence of this function is checked by V4L2 */
static int cif_isp10_v4l2_g_fmt(
struct file *file,
void *priv,
struct v4l2_format *f)
{
enum cif_isp10_pix_fmt pix_fmt;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
enum cif_isp10_stream_id stream_id = to_cif_isp10_stream_id(queue);
mutex_lock(&dev->api_mutex);
switch (stream_id) {
case CIF_ISP10_STREAM_SP:
pix_fmt = dev->config.mi_config.sp.output.pix_fmt;
f->fmt.pix.width =
dev->config.mi_config.sp.output.width;
f->fmt.pix.height =
dev->config.mi_config.sp.output.height;
f->fmt.pix.pixelformat = cif_isp10_pix_fmt2v4l2_pix_fmt(pix_fmt, queue);
break;
case CIF_ISP10_STREAM_MP:
pix_fmt = dev->config.mi_config.mp.output.pix_fmt;
f->fmt.pix.width =
dev->config.mi_config.mp.output.width;
f->fmt.pix.height =
dev->config.mi_config.mp.output.height;
f->fmt.pix.pixelformat = cif_isp10_pix_fmt2v4l2_pix_fmt(pix_fmt, queue);
break;
default:
mutex_unlock(&dev->api_mutex);
return -EINVAL;
}
mutex_unlock(&dev->api_mutex);
return 0;
}
static int cif_isp10_v4l2_g_input(
struct file *file,
void *priv,
unsigned int *i)
{
int ret;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
mutex_lock(&dev->api_mutex);
ret = cif_isp10_g_input(dev, i);
if (IS_ERR_VALUE(ret))
goto err;
mutex_unlock(&dev->api_mutex);
return 0;
err:
cif_isp10_pltfrm_pr_err(NULL,
"failed with error %d\n", ret);
mutex_unlock(&dev->api_mutex);
return ret;
}
static int cif_isp10_v4l2_s_input(
struct file *file,
void *priv,
unsigned int i)
{
int ret;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
cif_isp10_pltfrm_pr_dbg(dev->dev, "setting input to %d\n", i);
mutex_lock(&dev->api_mutex);
ret = cif_isp10_s_input(dev, i);
if (IS_ERR_VALUE(ret))
goto err;
mutex_unlock(&dev->api_mutex);
return 0;
err:
cif_isp10_pltfrm_pr_err(NULL,
"failed with error %d\n", ret);
mutex_unlock(&dev->api_mutex);
return ret;
}
static int cif_isp10_v4l2_enum_framesizes(
struct file *file,
void *priv,
struct v4l2_frmsizeenum *fsize)
{
/* THIS FUNCTION IS UNDER CONSTRUCTION */
int ret;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
struct v4l2_subdev_frame_size_enum fse;
mutex_lock(&dev->api_mutex);
if (IS_ERR_OR_NULL(dev->img_src)) {
cif_isp10_pltfrm_pr_err(NULL,
"input has not yet been selected, cannot enumerate formats\n");
ret = -ENODEV;
goto err;
}
memset(&fse, 0x00, sizeof(fse));
fse.index = fsize->index;
ret = cif_isp10_img_src_enum_frame_size(dev->img_src, &fse);
if (IS_ERR_VALUE(ret))
goto err;
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
fsize->discrete.width = fse.max_width;
fsize->discrete.height = fse.max_height;
mutex_unlock(&dev->api_mutex);
return 0;
err:
mutex_unlock(&dev->api_mutex);
return ret;
}
static int cif_isp10_v4l2_vb2_queue_setup(struct vb2_queue *queue,
const void *parg,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
int ret;
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
enum cif_isp10_stream_id strm = to_cif_isp10_stream_id(queue);
cif_isp10_pltfrm_pr_dbg(NULL,
"%s request planes %d count %d, size %d\n",
cif_isp10_v4l2_buf_type_string(queue->type),
*num_planes,
*num_buffers, sizes[0]);
if (*num_planes == 0)
*num_planes = 1;
if (*num_buffers == 0)
*num_buffers = 4;
alloc_ctxs[0] = dev->alloc_ctx;
ret = cif_isp10_calc_min_out_buff_size(dev, strm, &sizes[0], false);
//sizes[0] = PAGE_ALIGN(sizes[0] );
if (ret)
return -EINVAL;
cif_isp10_pltfrm_pr_dbg(NULL, "%s count %d, size %d\n",
cif_isp10_v4l2_buf_type_string(queue->type),
*num_buffers, sizes[0]);
return 0;
}
static void cif_isp10_v4l2_vb2_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct cif_isp10_buffer *ispbuf = to_cif_isp10_vb(vbuf);
struct vb2_queue *queue = vb->vb2_queue;
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
enum cif_isp10_stream_id strm = to_cif_isp10_stream_id(queue);
struct cif_isp10_stream *stream = to_stream_by_id(dev, strm);
u32 size;
unsigned long lock_flags = 0;
cif_isp10_pltfrm_pr_dbg(NULL,
"buffer type %s\n",
cif_isp10_v4l2_buf_type_string(queue->type));
spin_lock_irqsave(&dev->vbq_lock, lock_flags);
list_add_tail(&ispbuf->queue, &stream->buf_queue);
cif_isp10_calc_min_out_buff_size(dev, strm, &size, false);
//size = PAGE_ALIGN(size);
vb2_set_plane_payload(vb, 0, size);
spin_unlock_irqrestore(&dev->vbq_lock, lock_flags);
}
static void cif_isp10_v4l2_vb2_stop_streaming(struct vb2_queue *queue)
{
struct cif_isp10_v4l2_node *node;
enum cif_isp10_stream_id strm = to_cif_isp10_stream_id(queue);
struct cif_isp10_stream *stream = NULL;
struct cif_isp10_device *dev;
struct cif_isp10_buffer *buf, *buf_tmp;
unsigned long lock_flags = 0;
node = queue_to_node(queue);
dev = video_get_drvdata(&node->vdev);
stream = to_stream_by_id(dev, strm);
spin_lock_irqsave(&dev->vbq_lock, lock_flags);
if (stream->curr_buf) {
vb2_buffer_done(&stream->curr_buf->vb.vb2_buf,
VB2_BUF_STATE_ERROR);
stream->curr_buf = NULL;
}
if (stream->next_buf) {
vb2_buffer_done(&stream->next_buf->vb.vb2_buf,
VB2_BUF_STATE_ERROR);
stream->next_buf = NULL;
}
list_for_each_entry_safe(buf, buf_tmp, &stream->buf_queue, queue) {
list_del(&buf->queue);
if (buf->vb.vb2_buf.state == VB2_BUF_STATE_ACTIVE)
vb2_buffer_done(&buf->vb.vb2_buf,
VB2_BUF_STATE_ERROR);
}
spin_unlock_irqrestore(&dev->vbq_lock, lock_flags);
}
static struct vb2_ops cif_isp10_v4l2_vb2_ops = {
.queue_setup = cif_isp10_v4l2_vb2_queue_setup,
.buf_queue = cif_isp10_v4l2_vb2_queue,
//.buf_cleanup = cif_isp10_v4l2_vb2_release,
//.buf_init = cif_isp10_v4l2_vb2_init,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
.stop_streaming = cif_isp10_v4l2_vb2_stop_streaming,
};
static int cif_isp10_init_vb2_queue(struct vb2_queue *q,
struct cif_isp10_device *dev,
enum v4l2_buf_type buf_type)
{
struct cif_isp10_v4l2_node *node;
memset(q, 0, sizeof(*q));
node = queue_to_node(q);
mutex_init(&node->qlock);
q->type = buf_type;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
q->drv_priv = dev;
q->ops = &cif_isp10_v4l2_vb2_ops;
q->buf_struct_size = sizeof(struct cif_isp10_buffer);
q->min_buffers_needed = 4;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &node->qlock;
#ifdef CIF_ISP10_MODE_DMA_CONTIG
q->mem_ops = &vb2_dma_contig_memops;
dev->alloc_ctx = vb2_dma_contig_init_ctx(dev->dev);
#endif
#ifdef CIF_ISP10_MODE_DMA_SG
q->mem_ops = &vb2_dma_sg_memops;
dev->alloc_ctx = vb2_dma_sg_init_ctx(dev->dev);
#endif
return vb2_queue_init(q);
}
static int cif_isp10_v4l2_open(
struct file *file)
{
int ret;
struct video_device *vdev = video_devdata(file);
struct cif_isp10_device *dev = video_get_drvdata(vdev);
struct cif_isp10_v4l2_fh *fh;
struct cif_isp10_v4l2_node *node;
enum v4l2_buf_type buf_type;
enum cif_isp10_stream_id stream_id;
struct cif_isp10_v4l2_device *cif_isp10_v4l2_dev =
(struct cif_isp10_v4l2_device *)dev->nodes;
cif_isp10_pltfrm_pr_dbg(NULL,
"video device video%d.%d (%s)\n",
vdev->num, vdev->minor, vdev->name);
mutex_lock(&dev->api_mutex);
if (vdev->minor == cif_isp10_v4l2_dev->node[SP_DEV].vdev.minor) {
buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
stream_id = CIF_ISP10_STREAM_SP;
} else if (vdev->minor == cif_isp10_v4l2_dev->node[MP_DEV].vdev.minor) {
buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
stream_id = CIF_ISP10_STREAM_MP;
} else if (vdev->minor ==
cif_isp10_v4l2_dev->node[DMA_DEV].vdev.minor) {
buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
stream_id = CIF_ISP10_STREAM_DMA;
} else {
cif_isp10_pltfrm_pr_err(NULL,
"invalid video device video%d.%d (%s)\n",
vdev->num, vdev->minor, vdev->name);
ret = -EINVAL;
goto err;
}
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (!fh) {
cif_isp10_pltfrm_pr_err(NULL,
"memory allocation failed\n");
ret = -ENOMEM;
goto err;
}
fh->stream_id = stream_id;
file->private_data = &fh->fh;
v4l2_fh_init(&fh->fh, vdev);
v4l2_fh_add(&fh->fh);
node = to_node(fh);
if (++node->users > 1) {
mutex_unlock(&dev->api_mutex);
return 0;
}
/* First open of the device, so initialize everything */
node->owner = NULL;
dev->img_src_exps.inited = false;
cif_isp10_init_vb2_queue(to_vb2_queue(file), dev, buf_type);
vdev->queue = to_vb2_queue(file);
ret = cif_isp10_init(dev, to_stream_id(file));
if (IS_ERR_VALUE(ret)) {
v4l2_fh_del(&fh->fh);
v4l2_fh_exit(&fh->fh);
kfree(fh);
node->users--;
goto err;
}
mutex_unlock(&dev->api_mutex);
return 0;
err:
cif_isp10_pltfrm_pr_err(NULL,
"failed with error %d\n", ret);
mutex_unlock(&dev->api_mutex);
return ret;
}
static int cif_isp10_v4l2_release(struct file *file)
{
int ret = 0;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
struct cif_isp10_v4l2_fh *fh = to_fh(file);
struct cif_isp10_v4l2_node *node = to_node(fh);
enum cif_isp10_stream_id stream_id = to_stream_id(file);
cif_isp10_pltfrm_pr_dbg(dev->dev, "%s\n",
cif_isp10_v4l2_buf_type_string(queue->type));
mutex_lock(&dev->api_mutex);
if (node->users) {
--node->users;
} else {
cif_isp10_pltfrm_pr_warn(dev->dev,
"number of users for this device is already 0\n");
mutex_unlock(&dev->api_mutex);
return 0;
}
if (!node->users) {
if (queue->streaming)
if (IS_ERR_VALUE(cif_isp10_v4l2_do_streamoff(file)))
cif_isp10_pltfrm_pr_warn(dev->dev,
"streamoff failed\n");
/* Last close, so uninitialize hardware */
ret = cif_isp10_release(dev, stream_id);
vb2_queue_release(queue);
}
if (node->owner == fh)
node->owner = NULL;
if (file->private_data == queue->owner)
queue->owner = NULL;
v4l2_fh_del(&fh->fh);
v4l2_fh_exit(&fh->fh);
kfree(fh);
if (IS_ERR_VALUE(ret))
cif_isp10_pltfrm_pr_err(dev->dev,
"failed with error %d\n", ret);
mutex_unlock(&dev->api_mutex);
return ret;
}
static bool cifisp10_meta_mmap;
module_param_named(meta_mmap, cifisp10_meta_mmap, bool, 0644);
MODULE_PARM_DESC(meta_mmap, "Meta mmap onoff (N-Y)");
static unsigned int cif_isp10_v4l2_poll(
struct file *file,
struct poll_table_struct *wait)
{
struct cif_isp10_v4l2_fh *fh;
int ret;
struct vb2_queue *queue;
unsigned long req_events;
if (!cifisp10_meta_mmap)
return vb2_fop_poll(file, wait);
ret = 0;
fh = to_fh(file);
queue = to_vb2_queue(file);
req_events = poll_requested_events(wait);
cif_isp10_pltfrm_pr_dbg(NULL, "%s\n",
cif_isp10_v4l2_buf_type_string(queue->type));
if (v4l2_event_pending(&fh->fh))
ret = POLLPRI;
else if (req_events & POLLPRI)
poll_wait(file, &fh->fh.wait, wait);
if (!(req_events & (POLLIN | POLLOUT | POLLRDNORM)))
return ret;
ret |= vb2_fop_poll(file, wait);
if (ret & POLLERR) {
cif_isp10_pltfrm_pr_err(NULL,
"videobuf_poll_stream failed with error 0x%x\n", ret);
}
return ret;
}
/*
* VMA operations.
*/
static void cif_isp10_v4l2_vm_open(struct vm_area_struct *vma)
{
unsigned long flags = 0;
struct cif_isp10_metadata_s *metadata =
(struct cif_isp10_metadata_s *)vma->vm_private_data;
spin_lock_irqsave(&metadata->spinlock, flags);
metadata->vmas++;
spin_unlock_irqrestore(&metadata->spinlock, flags);
}
static void cif_isp10_v4l2_vm_close(struct vm_area_struct *vma)
{
unsigned long flags = 0;
struct cif_isp10_metadata_s *metadata =
(struct cif_isp10_metadata_s *)vma->vm_private_data;
spin_lock_irqsave(&metadata->spinlock, flags);
metadata->vmas--;
spin_unlock_irqrestore(&metadata->spinlock, flags);
}
static const struct vm_operations_struct cif_isp10_vm_ops = {
.open = cif_isp10_v4l2_vm_open,
.close = cif_isp10_v4l2_vm_close,
};
int cif_isp10_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
{
struct vb2_queue *queue;
struct cif_isp10_device *dev;
enum cif_isp10_stream_id strm;
int retval;
if (!cifisp10_meta_mmap)
return vb2_fop_mmap(file, vma);
queue = to_vb2_queue(file);
dev = to_cif_isp10_device(queue);
strm = to_stream_id(file);
retval = cif_isp10_mmap(dev, strm, vma);
if (retval < 0)
goto done;
vma->vm_ops = &cif_isp10_vm_ops;
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
cif_isp10_v4l2_vm_open(vma);
done:
return retval;
}
const struct v4l2_file_operations cif_isp10_v4l2_fops = {
.open = cif_isp10_v4l2_open,
.unlocked_ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = video_ioctl2,
#endif
.release = cif_isp10_v4l2_release,
.poll = cif_isp10_v4l2_poll,
.mmap = cif_isp10_v4l2_mmap,
};
/*TBD: clean up code below this line******************************************/
static int v4l2_querycap(struct file *file,
void *priv, struct v4l2_capability *cap)
{
struct vb2_queue *queue = to_vb2_queue(file);
struct video_device *vdev = video_devdata(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
u32 stream_ids = to_stream_id(file);
strcpy(cap->driver, DRIVER_NAME);
strlcpy(cap->card, vdev->name, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info),
"platform:" DRIVER_NAME "-%03i",
dev->dev_id);
if (stream_ids == CIF_ISP10_STREAM_SP) {
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING;
cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING;
} else if (stream_ids == CIF_ISP10_STREAM_MP) {
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING;
cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING;
}
else if (stream_ids == CIF_ISP10_STREAM_DMA)
cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE |
V4L2_CAP_VIDEO_M2M;
cap->capabilities |= V4L2_CAP_DEVICE_CAPS;
cap->device_caps |= V4L2_CAP_DEVICE_CAPS;
return 0;
}
static int cif_isp10_v4l2_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
if (sub->type != V4L2_EVENT_FRAME_SYNC)
return -EINVAL;
return v4l2_event_subscribe(fh, sub, 16, NULL);
}
static int cif_isp10_v4l2_unsubscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
return v4l2_event_unsubscribe(fh, sub);
}
static void cif_isp10_v4l2_event(
struct cif_isp10_device *dev,
__u32 frame_sequence)
{
struct cif_isp10_v4l2_device *cif_isp10_v4l2_dev =
(struct cif_isp10_v4l2_device *)dev->nodes;
struct v4l2_event ev;
memset(&ev, 0, sizeof(ev));
ev.type = V4L2_EVENT_FRAME_SYNC;
ev.u.frame_sync.frame_sequence = frame_sequence;
v4l2_event_queue(&cif_isp10_v4l2_dev->node[SP_DEV].vdev, &ev);
}
static void cif_isp10_v4l2_requeue_bufs(
struct cif_isp10_device *dev,
enum cif_isp10_stream_id stream_id)
{
struct cif_isp10_buffer *ispbuf;
struct vb2_buffer *buf;
struct vb2_queue *q = NULL;
struct cif_isp10_v4l2_device *cif_isp10_v4l2_dev =
(struct cif_isp10_v4l2_device *)dev->nodes;
if (stream_id == CIF_ISP10_STREAM_SP)
q = &cif_isp10_v4l2_dev->node[SP_DEV].buf_queue;
else if (stream_id == CIF_ISP10_STREAM_MP)
q = &cif_isp10_v4l2_dev->node[MP_DEV].buf_queue;
else if (stream_id == CIF_ISP10_STREAM_DMA)
q = &cif_isp10_v4l2_dev->node[DMA_DEV].buf_queue;
else
WARN_ON(1);
dev = to_cif_isp10_device(q);
list_for_each_entry(buf, &q->queued_list, queued_entry) {
if (buf->state == VB2_BUF_STATE_DONE)
continue;
ispbuf = to_cif_isp10_vb(to_vb2_v4l2_buffer(buf));
if (!IS_ERR_VALUE(cif_isp10_qbuf(
to_cif_isp10_device(q), stream_id, ispbuf))) {
spin_lock(&dev->vbreq_lock);
if (buf->state == VB2_BUF_STATE_QUEUED) {
buf->state = VB2_BUF_STATE_ACTIVE;
atomic_inc(&q->owned_by_drv_count);
} else if (buf->state == VB2_BUF_STATE_ACTIVE) {
/* nothing */
} else {
cif_isp10_pltfrm_pr_err(NULL,
"skip state change for buf: %d, state: %d\n",
buf->index, buf->state);
}
spin_unlock(&dev->vbreq_lock);
} else {
cif_isp10_pltfrm_pr_err(NULL,
"failed for buffer %d\n", buf->index);
}
}
}
static long v4l2_default_ioctl(struct file *file, void *fh,
bool valid_prio, unsigned int cmd, void *arg)
{
int ret = -EINVAL;
u32 h_offs;
u32 v_offs;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
mutex_lock(&dev->api_mutex);
if (!arg) {
cif_isp10_pltfrm_pr_err(dev->dev,
"NULL Pointer Violation from IOCTL arg:0x%lx\n",
(unsigned long)arg);
mutex_unlock(&dev->api_mutex);
return ret;
}
if (cmd == RK_VIDIOC_SENSOR_MODE_DATA) {
struct isp_supplemental_sensor_mode_data *p_mode_data =
(struct isp_supplemental_sensor_mode_data *)arg;
ret = (int)cif_isp10_img_src_ioctl(dev->img_src,
RK_VIDIOC_SENSOR_MODE_DATA, p_mode_data);
if (ret < 0) {
cif_isp10_pltfrm_pr_err(dev->dev,
"failed to get sensor mode data\n");
mutex_unlock(&dev->api_mutex);
return ret;
}
p_mode_data->isp_input_width =
dev->config.isp_config.input->defrect.width;
p_mode_data->isp_input_height =
dev->config.isp_config.input->defrect.height;
p_mode_data->isp_input_horizontal_start =
dev->config.isp_config.input->defrect.left;
p_mode_data->isp_input_vertical_start =
dev->config.isp_config.input->defrect.top;
p_mode_data->isp_output_width =
dev->config.isp_config.output.width;
p_mode_data->isp_output_height =
dev->config.isp_config.output.height;
if (p_mode_data->isp_output_width == 0 ||
p_mode_data->isp_output_height == 0) {
ret = cif_isp10_calc_isp_cropping(dev,
&p_mode_data->isp_output_width,
&p_mode_data->isp_output_height,
&h_offs,
&v_offs);
if (IS_ERR_VALUE(ret)) {
cif_isp10_pltfrm_pr_err(dev->dev,
"failed to get isp_output data\n");
mutex_unlock(&dev->api_mutex);
return ret;
}
}
} else if (cmd == RK_VIDIOC_CAMERA_MODULEINFO) {
struct camera_module_info_s *p_camera_module =
(struct camera_module_info_s *)arg;
ret = (int)cif_isp10_img_src_ioctl(dev->img_src,
RK_VIDIOC_CAMERA_MODULEINFO, p_camera_module);
if (ret < 0) {
cif_isp10_pltfrm_pr_err(dev->dev,
"failed to get camera module information\n");
mutex_unlock(&dev->api_mutex);
return ret;
}
} else if (cmd == RK_VIDIOC_SENSOR_CONFIGINFO) {
struct sensor_config_info_s *p_sensor_config =
(struct sensor_config_info_s *)arg;
ret = (int)cif_isp10_img_src_ioctl(dev->img_src,
RK_VIDIOC_SENSOR_CONFIGINFO, p_sensor_config);
if (ret < 0) {
cif_isp10_pltfrm_pr_err(dev->dev,
"failed to get camera module information\n");
mutex_unlock(&dev->api_mutex);
return ret;
}
} else if (cmd == RK_VIDIOC_SENSOR_REG_ACCESS) {
struct sensor_reg_rw_s *p_sensor_rw =
(struct sensor_reg_rw_s *)arg;
ret = (int)cif_isp10_img_src_ioctl(dev->img_src,
RK_VIDIOC_SENSOR_REG_ACCESS, p_sensor_rw);
if (ret < 0) {
cif_isp10_pltfrm_pr_err(dev->dev,
"failed to get camera module information\n");
mutex_unlock(&dev->api_mutex);
return ret;
}
}
mutex_unlock(&dev->api_mutex);
return ret;
}
static int v4l2_g_parm(
struct file *file,
void *priv,
struct v4l2_streamparm *a)
{
return 0;
}
static int v4l2_s_parm(
struct file *file,
void *priv,
struct v4l2_streamparm *a)
{
return 0;
}
static int v4l2_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
const char *inp_name;
mutex_lock(&dev->api_mutex);
if ((queue->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
(queue->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)) {
cif_isp10_pltfrm_pr_err(NULL,
"wrong buffer queue %d\n", queue->type);
mutex_unlock(&dev->api_mutex);
return -EINVAL;
}
inp_name = cif_isp10_g_input_name(dev, input->index);
if (IS_ERR_OR_NULL(inp_name)) {
mutex_unlock(&dev->api_mutex);
return -EINVAL;
}
input->type = V4L2_INPUT_TYPE_CAMERA;
input->std = V4L2_STD_UNKNOWN;
strncpy(input->name, inp_name, sizeof(input->name)-1);
mutex_unlock(&dev->api_mutex);
return 0;
}
/* ================================================================= */
static int mainpath_g_ctrl(
struct file *file,
void *priv,
struct v4l2_control *vc)
{
int ret = -EINVAL;
switch (vc->id) {
default:
return -EINVAL;
}
return ret;
}
#ifdef NOT_YET
static int mainpath_try_fmt_cap(struct v4l2_format *f)
{
int ifmt = 0;
struct v4l2_pix_format *pix = &f->fmt.pix;
cif_isp10_pltfrm_pr_dbg(NULL, "\n");
for (ifmt = 0; ifmt < get_cif_isp10_output_format_size(); ifmt++) {
if (pix->pixelformat ==
get_cif_isp10_output_format(ifmt)->fourcc)
break;
}
if (ifmt == get_cif_isp10_output_format_size())
ifmt = 0;
pix->bytesperline = pix->width *
get_cif_isp10_output_format(ifmt)->depth / 8;
switch (pix->pixelformat) {
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_YUV422P:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_GREY:
case V4L2_PIX_FMT_YUV444:
case V4L2_PIX_FMT_NV24:
case V4L2_PIX_FMT_JPEG:
pix->colorspace = V4L2_COLORSPACE_JPEG;
break;
case V4L2_PIX_FMT_RGB32:
case V4L2_PIX_FMT_BGR32:
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_RGB565X:
case V4L2_PIX_FMT_SGRBG10:
pix->colorspace = V4L2_COLORSPACE_SRGB;
break;
default:
WARN_ON(1);
break;
}
return 0;
}
#endif
static int v4l2_enum_fmt_cap(struct file *file, void *fh,
struct v4l2_fmtdesc *f)
{
int ret = 0;
int xgold_num_format = 0;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
mutex_lock(&dev->api_mutex);
xgold_num_format = get_cif_isp10_output_format_desc_size();
if ((f->index >= xgold_num_format) ||
(get_cif_isp10_output_format_desc(f->index)->pixelformat == 0)) {
cif_isp10_pltfrm_pr_err(NULL, "index %d\n", f->index);
mutex_unlock(&dev->api_mutex);
return -EINVAL;
}
strlcpy(f->description,
get_cif_isp10_output_format_desc(f->index)->description,
sizeof(f->description));
f->pixelformat =
get_cif_isp10_output_format_desc(f->index)->pixelformat;
f->flags = get_cif_isp10_output_format_desc(f->index)->flags;
mutex_unlock(&dev->api_mutex);
return ret;
}
static int v4l2_g_ctrl(struct file *file, void *priv,
struct v4l2_control *vc)
{
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
enum cif_isp10_cid id =
cif_isp10_v4l2_cid2cif_isp10_cid(vc->id);
int ret;
mutex_lock(&dev->api_mutex);
if (id == CIF_ISP10_CID_MIN_BUFFER_FOR_CAPTURE) {
/* Three buffers needed at least.
* one for MI_MP_Y_BASE_AD_INIT, one for MI_MP_Y_BASE_AD_SHD
* the other one stay in the waiting queue.
*/
vc->value = 3;
cif_isp10_pltfrm_pr_dbg(dev->dev,
"V4L2_CID_MIN_BUFFERS_FOR_CAPTURE %d\n",
vc->value);
mutex_unlock(&dev->api_mutex);
return 0;
}
ret = cif_isp10_img_src_g_ctrl(dev->img_src, id, &vc->value);
mutex_unlock(&dev->api_mutex);
return ret;
}
static int v4l2_s_ext_ctrls(struct file *file, void *priv,
struct v4l2_ext_controls *vc_ext)
{
struct cif_isp10_img_src_ctrl *ctrls;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
struct cif_isp10_img_src_ext_ctrl ctrl;
int ret = -EINVAL;
unsigned int i, j;
bool cls_exp = false;
/* The only use-case is gain and exposure to sensor. Thus no check if
* this shall go to img_src or not as of now.
*/
cif_isp10_pltfrm_pr_dbg(dev->dev, "count %d\n",
vc_ext->count);
if (vc_ext->count == 0)
return ret;
ctrls = kmalloc(vc_ext->count *
sizeof(struct cif_isp10_img_src_ctrl), GFP_KERNEL);
if (!ctrls)
return -ENOMEM;
if (vc_ext->controls[0].id == RK_V4L2_CID_CLS_EXP) {
j = 1;
cls_exp = true;
ctrl.cnt = vc_ext->count - 1;
} else {
j = 0;
cls_exp = false;
ctrl.cnt = vc_ext->count;
}
/*current kernel version don't define
*this member for struct v4l2_ext_control.
*/
/*ctrl.class = vc_ext->ctrl_class;*/
ctrl.ctrls = ctrls;
for (i = 0; i < ctrl.cnt; i++, j++) {
ctrls[i].id = vc_ext->controls[j].id;
ctrls[i].val = vc_ext->controls[j].value;
}
ret = cif_isp10_s_exp(dev, &ctrl, cls_exp);
return ret;
}
int cif_isp10_v4l2_cropcap(
struct file *file,
void *fh,
struct v4l2_cropcap *a)
{
int ret = 0;
struct vb2_queue *queue = to_vb2_queue(file);
struct cif_isp10_device *dev = to_cif_isp10_device(queue);
u32 target_width, target_height;
u32 h_offs, v_offs;
mutex_lock(&dev->api_mutex);
if ((dev->config.input_sel == CIF_ISP10_INP_DMA) ||
(dev->config.input_sel == CIF_ISP10_INP_DMA_IE)) {
/* calculate cropping for aspect ratio */
ret = cif_isp10_calc_isp_cropping(dev,
&dev->isp_dev.input_width, &dev->isp_dev.input_height,
&h_offs, &v_offs);
/* Get output size */
ret = cif_isp10_get_target_frm_size(dev,
&target_width, &target_height);
if (ret < 0) {
cif_isp10_pltfrm_pr_err(dev->dev,
"failed to get target frame size\n");
mutex_unlock(&dev->api_mutex);
return ret;
}
cif_isp10_pltfrm_pr_dbg(dev->dev,
"CIF_IN_W=%d, CIF_IN_H=%d, ISP_IN_W=%d, ISP_IN_H=%d, target_width=%d, target_height=%d\n",
dev->config.isp_config.input->width,
dev->config.isp_config.input->height,
dev->isp_dev.input_width,
dev->isp_dev.input_height,
target_width,
target_height);
/* This is the input to Bayer after input formatter cropping */
a->defrect.top = 0;
a->defrect.left = 0;
a->defrect.width = dev->isp_dev.input_width;
a->defrect.height = dev->isp_dev.input_height;
/* This is the minimum cropping window for the IS module */
a->bounds.width = 2;
a->bounds.height = 2;
a->bounds.top = (a->defrect.height - a->bounds.height) / 2;
a->bounds.left = (a->defrect.width - a->bounds.width) / 2;
a->type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
} else if (!CIF_ISP10_INP_IS_DMA(dev->config.input_sel)) {
/* calculate cropping for aspect ratio */
ret = cif_isp10_calc_isp_cropping(dev,
&dev->isp_dev.input_width, &dev->isp_dev.input_height,
&h_offs, &v_offs);
/* Get output size */
ret = cif_isp10_get_target_frm_size(dev,
&target_width, &target_height);
if (ret < 0) {
cif_isp10_pltfrm_pr_err(dev->dev,
"failed to get target frame size\n");
mutex_unlock(&dev->api_mutex);
return ret;
}
/* This is the input to Bayer after input formatter cropping */
a->defrect.top =
v_offs + dev->config.isp_config.input->defrect.top;
a->defrect.left =
h_offs + dev->config.isp_config.input->defrect.left;
a->defrect.width = dev->isp_dev.input_width;
a->defrect.height = dev->isp_dev.input_height;
a->bounds.top = 0;
a->bounds.left = 0;
a->bounds.width = dev->config.isp_config.input->width;
a->bounds.height = dev->config.isp_config.input->height;
a->type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
} else {
cif_isp10_pltfrm_pr_err(dev->dev,
"invalid input\n");
}
cif_isp10_pltfrm_pr_dbg(dev->dev,
"v4l2_cropcap: defrect(%d,%d,%d,%d) bounds(%d,%d,%d,%d)\n",
a->defrect.width,
a->defrect.height,
a->defrect.left,
a->defrect.top,
a->bounds.width,
a->bounds.height,
a->bounds.left,
a->bounds.top);
mutex_unlock(&dev->api_mutex);
return ret;
}
int cif_isp10_v4l2_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
{
return 0;
}
/*
* This is a write only function, so the upper layer
* will ignore the changes to 'a'. So don't use 'a' to pass
* the actual cropping parameters, the upper layer
* should call g_crop to get the actual window.
*/
int cif_isp10_v4l2_s_crop(
struct file *file,
void *fh,
const struct v4l2_crop *a)
{
return 0;
}
int cif_isp10_v4l2_try_fmt(struct file *file, void *fh,
struct v4l2_format *f)
{
return 0;
}
const struct v4l2_ioctl_ops cif_isp10_v4l2_sp_ioctlops = {
.vidioc_reqbufs = cif_isp10_v4l2_reqbufs,
.vidioc_querybuf = cif_isp10_v4l2_querybuf,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_qbuf = cif_isp10_v4l2_qbuf,
.vidioc_dqbuf = cif_isp10_v4l2_dqbuf,
.vidioc_streamon = cif_isp10_v4l2_streamon,
.vidioc_streamoff = cif_isp10_v4l2_streamoff,
.vidioc_g_input = cif_isp10_v4l2_g_input,
.vidioc_s_input = cif_isp10_v4l2_s_input,
.vidioc_enum_input = v4l2_enum_input,
.vidioc_g_ctrl = v4l2_g_ctrl,
.vidioc_s_ctrl = cif_isp10_v4l2_s_ctrl,
.vidioc_s_fmt_vid_cap = cif_isp10_v4l2_s_fmt,
.vidioc_g_fmt_vid_cap = cif_isp10_v4l2_g_fmt,
.vidioc_s_fmt_vid_cap_mplane = cif_isp10_v4l2_s_fmt,
.vidioc_g_fmt_vid_cap_mplane = cif_isp10_v4l2_g_fmt,
.vidioc_s_fmt_vid_overlay = cif_isp10_v4l2_s_fmt,
.vidioc_g_fmt_vid_overlay = cif_isp10_v4l2_g_fmt,
.vidioc_s_ext_ctrls = v4l2_s_ext_ctrls,
.vidioc_enum_fmt_vid_cap = v4l2_enum_fmt_cap,
.vidioc_enum_framesizes = cif_isp10_v4l2_enum_framesizes,
.vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_querycap = v4l2_querycap,
.vidioc_cropcap = cif_isp10_v4l2_cropcap,
.vidioc_s_crop = cif_isp10_v4l2_s_crop,
.vidioc_g_crop = cif_isp10_v4l2_g_crop,
.vidioc_subscribe_event = cif_isp10_v4l2_subscribe_event,
.vidioc_unsubscribe_event = cif_isp10_v4l2_unsubscribe_event,
.vidioc_default = v4l2_default_ioctl,
.vidioc_try_fmt_vid_cap = cif_isp10_v4l2_try_fmt,
.vidioc_s_parm = v4l2_s_parm,
.vidioc_g_parm = v4l2_g_parm,
};
const struct v4l2_ioctl_ops cif_isp10_v4l2_mp_ioctlops = {
.vidioc_reqbufs = cif_isp10_v4l2_reqbufs,
.vidioc_querybuf = cif_isp10_v4l2_querybuf,
.vidioc_qbuf = cif_isp10_v4l2_qbuf,
.vidioc_dqbuf = cif_isp10_v4l2_dqbuf,
.vidioc_streamon = cif_isp10_v4l2_streamon,
.vidioc_streamoff = cif_isp10_v4l2_streamoff,
.vidioc_g_input = cif_isp10_v4l2_g_input,
.vidioc_s_input = cif_isp10_v4l2_s_input,
.vidioc_enum_input = v4l2_enum_input,
.vidioc_g_ctrl = mainpath_g_ctrl,
.vidioc_s_ctrl = cif_isp10_v4l2_s_ctrl,
.vidioc_s_fmt_vid_cap = cif_isp10_v4l2_s_fmt,
.vidioc_g_fmt_vid_cap = cif_isp10_v4l2_g_fmt,
.vidioc_s_ext_ctrls = v4l2_s_ext_ctrls,
.vidioc_enum_fmt_vid_cap = v4l2_enum_fmt_cap,
.vidioc_enum_framesizes = cif_isp10_v4l2_enum_framesizes,
.vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_s_parm = v4l2_s_parm,
.vidioc_querycap = v4l2_querycap,
.vidioc_cropcap = cif_isp10_v4l2_cropcap,
.vidioc_s_crop = cif_isp10_v4l2_s_crop,
.vidioc_g_crop = cif_isp10_v4l2_g_crop,
.vidioc_default = v4l2_default_ioctl,
.vidioc_try_fmt_vid_cap = cif_isp10_v4l2_try_fmt,
.vidioc_g_parm = v4l2_g_parm,
};
const struct v4l2_ioctl_ops cif_isp10_v4l2_dma_ioctlops = {
.vidioc_reqbufs = cif_isp10_v4l2_reqbufs,
.vidioc_querybuf = cif_isp10_v4l2_querybuf,
.vidioc_qbuf = cif_isp10_v4l2_qbuf,
.vidioc_dqbuf = cif_isp10_v4l2_dqbuf,
.vidioc_streamon = cif_isp10_v4l2_streamon,
.vidioc_streamoff = cif_isp10_v4l2_streamoff,
.vidioc_s_fmt_vid_out = cif_isp10_v4l2_s_fmt,
.vidioc_g_fmt_vid_out = cif_isp10_v4l2_g_fmt,
.vidioc_cropcap = cif_isp10_v4l2_cropcap,
.vidioc_s_crop = cif_isp10_v4l2_s_crop,
.vidioc_g_crop = cif_isp10_v4l2_g_crop,
.vidioc_querycap = v4l2_querycap,
};
static struct pltfrm_soc_cfg rk3288_cfg = {
.name = CIF_ISP10_SOC_RK3288,
.soc_cfg = pltfrm_rk3288_cfg,
};
static struct pltfrm_soc_cfg rk3399_cfg = {
.name = CIF_ISP10_SOC_RK3399,
.soc_cfg = pltfrm_rk3399_cfg,
};
static const struct of_device_id cif_isp10_v4l2_of_match[] = {
{.compatible = "rockchip,rk3288-cif-isp",
.data = (void *)&rk3288_cfg},
{.compatible = "rockchip,rk3399-cif-isp",
.data = (void *)&rk3399_cfg},
{},
};
static unsigned int g_cif_isp10_v4l2_dev_cnt;
static struct cif_isp10_v4l2_device *g_cif_isp10_v4l2_dev[4];
static int cif_isp10_v4l2_drv_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device_node *node = pdev->dev.of_node;
struct cif_isp10_device *dev = NULL;
struct cif_isp10_v4l2_device *cif_isp10_v4l2_dev;
int ret;
cif_isp10_pltfrm_pr_info(NULL, "CIF ISP10 driver version: v%x.%x.%x\n",
CONFIG_CIFISP10_DRIVER_VERSION >> 16,
(CONFIG_CIFISP10_DRIVER_VERSION & 0xff00) >> 8,
CONFIG_CIFISP10_DRIVER_VERSION & 0x00ff);
cif_isp10_v4l2_dev = devm_kzalloc(
&pdev->dev,
sizeof(struct cif_isp10_v4l2_device),
GFP_KERNEL);
if (IS_ERR_OR_NULL(cif_isp10_v4l2_dev)) {
ret = -ENOMEM;
goto err;
}
match = of_match_node(cif_isp10_v4l2_of_match, node);
dev = cif_isp10_create(&pdev->dev,
cif_isp10_v4l2_event,
cif_isp10_v4l2_requeue_bufs,
(struct pltfrm_soc_cfg *)match->data);
if (IS_ERR_OR_NULL(dev)) {
ret = -ENODEV;
goto err;
}
dev->dev_id = g_cif_isp10_v4l2_dev_cnt;
dev->isp_dev.dev_id = &dev->dev_id;
dev->nodes = (void *)cif_isp10_v4l2_dev;
dev->isp_state = CIF_ISP10_STATE_IDLE;
spin_lock_init(&dev->vbq_lock);
spin_lock_init(&dev->vbreq_lock);
spin_lock_init(&dev->iowrite32_verify_lock);
spin_lock_init(&dev->isp_state_lock);
init_waitqueue_head(&dev->isp_stop_wait);
mutex_init(&dev->api_mutex);
ret = v4l2_device_register(dev->dev, &dev->v4l2_dev);
if (IS_ERR_VALUE(ret)) {
cif_isp10_pltfrm_pr_err(NULL,
"V4L2 device registration failed\n");
goto err;
}
ret = cif_isp10_v4l2_register_video_device(
dev,
&cif_isp10_v4l2_dev->node[SP_DEV].vdev,
SP_VDEV_NAME,
V4L2_CAP_VIDEO_OVERLAY,
CIF_ISP10_V4L2_SP_DEV_MAJOR,
&cif_isp10_v4l2_fops,
&cif_isp10_v4l2_sp_ioctlops);
if (ret)
goto err;
ret = register_cifisp_device(&dev->isp_dev,
&cif_isp10_v4l2_dev->node[ISP_DEV].vdev,
&dev->v4l2_dev,
dev->config.base_addr);
if (ret)
goto err;
ret = cif_isp10_v4l2_register_video_device(
dev,
&cif_isp10_v4l2_dev->node[MP_DEV].vdev,
MP_VDEV_NAME,
V4L2_CAP_VIDEO_CAPTURE,
CIF_ISP10_V4L2_MP_DEV_MAJOR,
&cif_isp10_v4l2_fops,
&cif_isp10_v4l2_mp_ioctlops);
if (ret)
goto err;
ret = cif_isp10_v4l2_register_video_device(
dev,
&cif_isp10_v4l2_dev->node[DMA_DEV].vdev,
DMA_VDEV_NAME,
V4L2_CAP_VIDEO_OUTPUT,
CIF_ISP10_V4L2_DMA_DEV_MAJOR,
&cif_isp10_v4l2_fops,
&cif_isp10_v4l2_dma_ioctlops);
if (ret)
goto err;
cif_isp10_v4l2_register_imgsrc_subdev(
dev);
pm_runtime_enable(&pdev->dev);
g_cif_isp10_v4l2_dev[g_cif_isp10_v4l2_dev_cnt] =
cif_isp10_v4l2_dev;
g_cif_isp10_v4l2_dev_cnt++;
return 0;
err:
cif_isp10_destroy(dev);
return ret;
}
/* ======================================================================== */
static int cif_isp10_v4l2_drv_remove(struct platform_device *pdev)
{
struct cif_isp10_device *cif_isp10_dev =
(struct cif_isp10_device *)platform_get_drvdata(pdev);
struct cif_isp10_v4l2_device *cif_isp10_v4l2_dev =
(struct cif_isp10_v4l2_device *)cif_isp10_dev->nodes;
if (IS_ERR_VALUE(cif_isp10_release(cif_isp10_dev,
CIF_ISP10_ALL_STREAMS)))
cif_isp10_pltfrm_pr_warn(cif_isp10_dev->dev,
"CIF power off failed\n");
video_unregister_device(&cif_isp10_v4l2_dev->node[SP_DEV].vdev);
video_unregister_device(&cif_isp10_v4l2_dev->node[MP_DEV].vdev);
video_unregister_device(&cif_isp10_v4l2_dev->node[DMA_DEV].vdev);
unregister_cifisp_device(&cif_isp10_v4l2_dev->node[ISP_DEV].vdev);
v4l2_device_unregister(&cif_isp10_dev->v4l2_dev);
cif_isp10_pltfrm_dev_release(&pdev->dev, cif_isp10_dev);
cif_isp10_destroy(cif_isp10_dev);
g_cif_isp10_v4l2_dev_cnt--;
g_cif_isp10_v4l2_dev[g_cif_isp10_v4l2_dev_cnt] = NULL;
return 0;
}
static int cif_isp10_v4l2_drv_suspend(struct platform_device *pdev,
pm_message_t state)
{
int ret = 0;
struct cif_isp10_device *cif_isp10_dev =
(struct cif_isp10_device *)platform_get_drvdata(pdev);
cif_isp10_pltfrm_pr_dbg(cif_isp10_dev->dev, "\n");
ret = cif_isp10_suspend(cif_isp10_dev);
if (IS_ERR_VALUE(ret))
goto err;
cif_isp10_pltfrm_pinctrl_set_state(&pdev->dev,
CIF_ISP10_PINCTRL_STATE_SLEEP);
return 0;
err:
cif_isp10_pltfrm_pr_err(cif_isp10_dev->dev,
"failed with error %d\n", ret);
return ret;
}
static int cif_isp10_v4l2_drv_resume(struct platform_device *pdev)
{
int ret = 0;
struct cif_isp10_device *cif_isp10_dev =
(struct cif_isp10_device *)platform_get_drvdata(pdev);
cif_isp10_pltfrm_pr_dbg(cif_isp10_dev->dev, "\n");
if (!cif_isp10_dev->img_src) {
cif_isp10_pltfrm_pr_err(
cif_isp10_dev->dev,
"cif_isp10_dev img_src is null!\n");
goto err;
}
ret = cif_isp10_resume(cif_isp10_dev);
if (IS_ERR_VALUE(ret))
goto err;
cif_isp10_pltfrm_pinctrl_set_state(&pdev->dev,
CIF_ISP10_PINCTRL_STATE_DEFAULT);
return 0;
err:
cif_isp10_pltfrm_pr_err(cif_isp10_dev->dev,
"failed with error %d\n", ret);
return ret;
}
static int cif_isp10_runtime_suspend(struct device *dev)
{
cif_isp10_pltfrm_pr_dbg(dev, "\n");
return cif_isp10_pltfrm_pm_set_state(dev, CIF_ISP10_PM_STATE_SUSPENDED);
}
static int cif_isp10_runtime_resume(struct device *dev)
{
cif_isp10_pltfrm_pr_dbg(dev, "\n");
return cif_isp10_pltfrm_pm_set_state(dev, CIF_ISP10_PM_STATE_SW_STNDBY);
}
static const struct dev_pm_ops cif_isp10_dev_pm_ops = {
SET_RUNTIME_PM_OPS(cif_isp10_runtime_suspend,
cif_isp10_runtime_resume, NULL)
};
static struct platform_driver cif_isp10_v4l2_plat_drv = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(cif_isp10_v4l2_of_match),
.pm = &cif_isp10_dev_pm_ops,
},
.probe = cif_isp10_v4l2_drv_probe,
.remove = cif_isp10_v4l2_drv_remove,
.suspend = cif_isp10_v4l2_drv_suspend,
.resume = cif_isp10_v4l2_drv_resume,
};
/* ======================================================================== */
static int cif_isp10_v4l2_init(void)
{
int ret;
g_cif_isp10_v4l2_dev_cnt = 0;
ret = platform_driver_register(&cif_isp10_v4l2_plat_drv);
if (ret) {
cif_isp10_pltfrm_pr_err(NULL,
"cannot register platform driver, failed with %d\n",
ret);
return -ENODEV;
}
return ret;
}
/* ======================================================================== */
static void __exit cif_isp10_v4l2_exit(void)
{
platform_driver_unregister(&cif_isp10_v4l2_plat_drv);
}
/* ======================================================================== */
void cif_isp10_v4l2_s_frame_interval(
unsigned int numerator,
unsigned int denominator)
{
struct cif_isp10_v4l2_device *cif_isp10_v4l2_dev;
struct cif_isp10_device *cif_isp10_dev;
struct vb2_queue *queue;
struct cif_isp10_frm_intrvl frm_intrvl;
unsigned int i;
for (i = 0; i < g_cif_isp10_v4l2_dev_cnt; i++) {
if (g_cif_isp10_v4l2_dev[i] == NULL)
continue;
cif_isp10_v4l2_dev =
g_cif_isp10_v4l2_dev[i];
queue = (struct vb2_queue *)
&cif_isp10_v4l2_dev->node[SP_DEV].buf_queue;
cif_isp10_dev = to_cif_isp10_device(queue);
if (cif_isp10_dev->img_src == NULL)
continue;
frm_intrvl.numerator = numerator;
frm_intrvl.denominator = denominator;
cif_isp10_img_src_s_frame_interval(
cif_isp10_dev->img_src,
&frm_intrvl);
}
}
int cif_isp10_v4l2_g_frame_interval(
unsigned int *numerator,
unsigned int *denominator)
{
struct cif_isp10_v4l2_device *cif_isp10_v4l2_dev;
struct cif_isp10_device *cif_isp10_dev;
struct vb2_queue *queue;
struct cif_isp10_frm_intrvl frm_intrvl;
unsigned int i;
int ret = -EFAULT;
for (i = 0; i < g_cif_isp10_v4l2_dev_cnt; i++) {
if (g_cif_isp10_v4l2_dev[i] == NULL)
continue;
cif_isp10_v4l2_dev =
g_cif_isp10_v4l2_dev[i];
queue = (struct vb2_queue *)
&cif_isp10_v4l2_dev->node[SP_DEV].buf_queue;
cif_isp10_dev = to_cif_isp10_device(queue);
if (cif_isp10_dev->img_src == NULL)
continue;
ret = cif_isp10_img_src_g_frame_interval(
cif_isp10_dev->img_src,
&frm_intrvl);
if (ret == 0) {
*numerator = frm_intrvl.numerator;
*denominator = frm_intrvl.denominator;
}
}
return ret;
}
device_initcall_sync(cif_isp10_v4l2_init);
module_exit(cif_isp10_v4l2_exit);
MODULE_DESCRIPTION("V4L2 interface for CIF ISP10 driver");
MODULE_AUTHOR("George");
MODULE_LICENSE("GPL");