/* ************************************************************************* * 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 #include #include #include #include #include #include "cif_isp10.h" #include "cif_isp10_regs.h" #include "cif_isp10_version.h" #include #include #include #include #include #include #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");