v4l2: starfive ISP add private ioctl
authorliuxl0327 <liuxl0327@starfivetech.com>
Mon, 21 Mar 2022 07:20:43 +0000 (15:20 +0800)
committermason.huo <mason.huo@starfivetech.com>
Fri, 1 Jul 2022 07:05:38 +0000 (15:05 +0800)
Signed-off-by: mason.huo <mason.huo@starfivetech.com>
drivers/media/platform/starfive/Makefile
drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.c [new file with mode: 0644]
drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.h [new file with mode: 0644]
drivers/media/platform/starfive/v4l2_driver/stf_isp.c [changed mode: 0755->0644]
drivers/media/platform/starfive/v4l2_driver/stf_isp.h [changed mode: 0755->0644]
drivers/media/platform/starfive/v4l2_driver/stf_isp_hw_ops.c [changed mode: 0755->0644]
drivers/media/platform/starfive/v4l2_driver/stf_isp_ioctl.h [new file with mode: 0644]

index d7a886d..65cd17b 100755 (executable)
@@ -25,4 +25,5 @@ obj-$(CONFIG_VIDEO_STF_VIN) +=        v4l2_driver/stfcamss.o \
                                v4l2_driver/stf_csi_hw_ops.o \
                                v4l2_driver/stf_csiphy_hw_ops.o \
                                v4l2_driver/stf_isp_hw_ops.o \
-                               v4l2_driver/stf_dvp_hw_ops.o
+                               v4l2_driver/stf_dvp_hw_ops.o \
+                               v4l2_driver/stf_dmabuf.o
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.c b/drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.c
new file mode 100644 (file)
index 0000000..f202014
--- /dev/null
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+
+#include <linux/dma-buf.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "stf_isp_ioctl.h"
+#include "stf_dmabuf.h"
+
+#define TOTAL_SIZE_LIMIT      (64 * 1024 * 1024)
+
+static size_t total_size;
+static struct vb2_queue        vb2_queue = {
+       .dma_attrs = 0,
+       .gfp_flags = 0,
+       .dma_dir = DMA_TO_DEVICE,
+};
+static struct vb2_buffer vb = {
+       .vb2_queue = &vb2_queue,
+};
+
+static int dmabuf_create(struct device *dev,
+                          struct dmabuf_create *head)
+{
+       struct dma_buf *dmabuf = NULL;
+       void *mem_priv = NULL;
+       dma_addr_t *paddr = NULL;
+       int ret = 0;
+
+       mem_priv = vb2_dma_contig_memops.alloc(dev, vb.vb2_queue->dma_attrs,
+                               head->size, vb.vb2_queue->dma_dir, vb.vb2_queue->gfp_flags);
+       if (IS_ERR_OR_NULL(mem_priv)) {
+               if (mem_priv)
+                       ret = PTR_ERR(mem_priv);
+               goto exit;
+       }
+
+       dmabuf = vb2_dma_contig_memops.get_dmabuf(mem_priv, O_RDWR);
+       if (IS_ERR(dmabuf)) {
+               ret = PTR_ERR(dmabuf);
+               goto free;
+       }
+
+       head->fd = dma_buf_fd(dmabuf, O_CLOEXEC);
+       if (head->fd < 0) {
+               dma_buf_put(dmabuf);
+               ret = head->fd;
+               goto free;
+       }
+
+       paddr = vb2_dma_contig_memops.cookie(mem_priv);
+       head->paddr = *paddr;
+       return 0;
+free:
+       vb2_dma_contig_memops.put(mem_priv);
+exit:
+       return ret;
+}
+
+int stf_dmabuf_ioctl_alloc(struct device *dev, void *arg)
+{
+       struct dmabuf_create *head = arg;
+       int ret = -EINVAL;
+
+       if (IS_ERR_OR_NULL(head))
+               return -EFAULT;
+
+       head->size = PAGE_ALIGN(head->size);
+       if (!head->size)
+               return -EINVAL;
+       if ((head->size + total_size) > TOTAL_SIZE_LIMIT)
+               return -ENOMEM;
+
+       ret = dmabuf_create(dev, head);
+       if (ret)
+               return -EFAULT;
+
+       total_size += head->size;
+       return ret;
+}
+
+int stf_dmabuf_ioctl_free(struct device *dev, void *arg)
+{
+       struct dmabuf_create *head = arg;
+       struct dma_buf *dmabuf = NULL;
+       int ret = 0;
+
+       if (IS_ERR_OR_NULL(head))
+               return -EFAULT;
+       if (head->size != PAGE_ALIGN(head->size))
+               return -EINVAL;
+       if (head->size > total_size)
+               return -EINVAL;
+
+       dmabuf = dma_buf_get(head->fd);
+       if (IS_ERR_OR_NULL(dmabuf))
+               return -EINVAL;
+
+       dma_buf_put(dmabuf);
+       vb2_dma_contig_memops.put(dmabuf->priv);
+       total_size -= head->size;
+       return ret;
+}
+
+int stf_dmabuf_ioctl(struct device *dev, unsigned int cmd, void *arg)
+{
+       int ret = -ENOIOCTLCMD;
+
+       switch (cmd) {
+       case VIDIOC_STF_DMABUF_ALLOC:
+               ret = stf_dmabuf_ioctl_alloc(dev, arg);
+               break;
+       case VIDIOC_STF_DMABUF_FREE:
+               ret = stf_dmabuf_ioctl_free(dev, arg);
+               break;
+       default:
+               break;
+       }
+       return ret;
+}
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.h b/drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.h
new file mode 100644 (file)
index 0000000..6b2f0d4
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#ifndef STF_DMABUF_H
+#define STF_DMABUF_H
+
+extern int stf_dmabuf_ioctl(struct device *dev, unsigned int cmd, void *arg);
+
+#endif /* STF_DMABUF_H */
old mode 100755 (executable)
new mode 100644 (file)
index a46a042..70a0dde
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 #include <linux/firmware.h>
+#include "stf_isp_ioctl.h"
+#include "stf_dmabuf.h"
 
-#define STF_ISP_NAME "stf_isp"
+static int user_config_isp;
 
 static const struct isp_format isp_formats_st7110[] = {
        { MEDIA_BUS_FMT_YUYV8_2X8, 16},
@@ -35,6 +37,7 @@ int stf_isp_subdev_init(struct stfcamss *stfcamss, int id)
        isp_dev->nformats = ARRAY_SIZE(isp_formats_st7110);
        mutex_init(&isp_dev->stream_lock);
        mutex_init(&isp_dev->setfile_lock);
+       atomic_set(&isp_dev->shadow_count, 0);
        return 0;
 }
 
@@ -811,6 +814,7 @@ static int stf_isp_load_setfile(struct stf_isp_dev *isp_dev, char *file_name)
 static long stf_isp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
 {
        struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
+       struct device *dev = isp_dev->stfcamss->dev;
        int ret = -ENOIOCTLCMD;
 
        switch (cmd) {
@@ -825,12 +829,61 @@ static long stf_isp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
                ret = stf_isp_load_setfile(isp_dev, fw_info->filename);
                break;
        }
+       case VIDIOC_STF_DMABUF_ALLOC:
+       case VIDIOC_STF_DMABUF_FREE:
+               ret = stf_dmabuf_ioctl(dev, cmd, arg);
+               break;
+       case VIDIOC_STFISP_GET_REG:
+               ret = isp_dev->hw_ops->isp_reg_read(isp_dev, arg);
+               break;
+       case VIDIOC_STFISP_SET_REG:
+               ret = isp_dev->hw_ops->isp_reg_write(isp_dev, arg);
+               break;
+       case VIDIOC_STFISP_SHADOW_LOCK:
+               if (atomic_add_unless(&isp_dev->shadow_count, 1, 1))
+                       ret = 0;
+               else
+                       ret = -EBUSY;
+               st_debug(ST_ISP, "%s, %d, ret = %d\n", __func__, __LINE__, ret);
+               break;
+       case VIDIOC_STFISP_SHADOW_UNLOCK:
+               if (atomic_dec_if_positive(&isp_dev->shadow_count) < 0)
+                       ret = -EINVAL;
+               else
+                       ret = 0;
+               st_debug(ST_ISP, "%s, %d, ret = %d\n", __func__, __LINE__, ret);
+               break;
+       case VIDIOC_STFISP_SHADOW_UNLOCK_N_TRIGGER:
+               {
+                       isp_dev->hw_ops->isp_shadow_trigger(isp_dev);
+                       if (atomic_dec_if_positive(&isp_dev->shadow_count) < 0)
+                               ret = -EINVAL;
+                       else
+                               ret = 0;
+                       st_debug(ST_ISP, "%s, %d, ret = %d\n", __func__, __LINE__, ret);
+               }
+               break;
+       case VIDIOC_STFISP_SET_USER_CONFIG_ISP:
+               st_debug(ST_ISP, "%s, %d set user_config_isp\n", __func__, __LINE__);
+               user_config_isp = 1;
+               break;
        default:
                break;
        }
        return ret;
 }
 
+int isp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+       struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
+
+       st_debug(ST_ISP, "%s, %d\n", __func__, __LINE__);
+       while (atomic_dec_if_positive(&isp_dev->shadow_count) > 0)
+               st_warn(ST_ISP, "user not unlocked the shadow lock, driver unlock it!\n");
+
+       return 0;
+}
+
 static const struct v4l2_subdev_core_ops isp_core_ops = {
        .s_power = isp_set_power,
        .ioctl = stf_isp_ioctl,
@@ -860,6 +913,7 @@ static const struct v4l2_subdev_ops isp_v4l2_ops = {
 
 static const struct v4l2_subdev_internal_ops isp_v4l2_internal_ops = {
        .open = isp_init_formats,
+       .close = isp_close,
 };
 
 static const struct media_entity_operations isp_media_ops = {
old mode 100755 (executable)
new mode 100644 (file)
index e4938f8..65b467d
 #include <media/media-entity.h>
 #include <video/stf-vin.h>
 
+#define STF_ISP_NAME "stf_isp"
 #define STF_ISP_PAD_SINK     0
 #define STF_ISP_PAD_SRC      1
 #define STF_ISP_PADS_NUM     2
 
 #define STF_ISP0_SETFILE     "stf_isp0_fw.bin"
 #define STF_ISP1_SETFILE     "stf_isp1_fw.bin"
-#define FILENAME_MAX_LEN     30
 
 #define SCALER_RATIO_MAX     1  // no compose function
 #define STF_ISP_REG_OFFSET_MAX  0x0FFF
 #define ISP_REG_DUMP_CFG_1      0x00000028
 #define ISP_REG_IESHD_ADDR      0x00000A50
 
-struct stfisp_fw_info {
-       char __user filename[FILENAME_MAX_LEN];
-};
-
-#define VIDIOC_STFISP_LOAD_FW \
-               _IOW('V', BASE_VIDIOC_PRIVATE + 1, struct stfisp_fw_info)
-
 struct isp_format {
        u32 code;
        u8 bpp;
@@ -64,6 +57,9 @@ struct isp_hw_ops {
                        struct v4l2_rect *crop, u32 mcode);
                        // u32 width, u32 height);
        int (*isp_stream_set)(struct stf_isp_dev *isp_dev, int on);
+       int (*isp_reg_read)(struct stf_isp_dev *isp_dev, void *arg);
+       int (*isp_reg_write)(struct stf_isp_dev *isp_dev, void *arg);
+       int (*isp_shadow_trigger)(struct stf_isp_dev *isp_dev);
 };
 
 struct isp_ctrls {
@@ -114,6 +110,7 @@ struct stf_isp_dev {
        struct isp_hw_ops *hw_ops;
        struct mutex stream_lock;
        int stream_count;
+       atomic_t shadow_count;
 
        struct isp_ctrls ctrls;
        struct mutex setfile_lock;
old mode 100755 (executable)
new mode 100644 (file)
index 5ff06be..33c2696
@@ -11,6 +11,7 @@
 #include <linux/fb.h>
 #include <linux/module.h>
 #include <video/stf-vin.h>
+#include "stf_isp_ioctl.h"
 #include <linux/delay.h>
 
 static const struct regval_t isp_sc2235_reg_config_list[] = {
@@ -384,6 +385,452 @@ static int stf_isp_stream_set(struct stf_isp_dev *isp_dev, int on)
        return 0;
 }
 
+static union reg_buf reg_buf;
+static int stf_isp_reg_read(struct stf_isp_dev *isp_dev, void *arg)
+{
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase;
+       struct isp_reg_param *reg_param = arg;
+       u32 size;
+       unsigned long r;
+
+       if (reg_param->reg_buf == NULL) {
+               st_err(ST_ISP, "Failed to access register. The pointer is NULL!!!\n");
+               return -EINVAL;
+       }
+
+       if (isp_dev->id == 0)
+               ispbase = vin->isp_isp0_base;
+       else
+               ispbase = vin->isp_isp1_base;
+
+       size = 0;
+       switch (reg_param->reg_info.method) {
+       case STF_ISP_REG_METHOD_ONE_REG:
+               break;
+
+       case STF_ISP_REG_METHOD_SERIES:
+               if (reg_param->reg_info.length > STF_ISP_REG_BUF_SIZE) {
+                       st_err(ST_ISP, "Failed to access register. \
+                               The (length=0x%08X > 0x%08X) is out of size!!!\n",
+                               reg_param->reg_info.length, STF_ISP_REG_BUF_SIZE);
+                       return -EINVAL;
+               }
+               break;
+
+       case STF_ISP_REG_METHOD_MODULE:
+               /* This mode is not supported in the V4L2 version. */
+               st_err(ST_ISP, "Reg Read - Failed to access register. The method = \
+                       STF_ISP_REG_METHOD_MODULE is not supported!!!\n");
+               return -ENOTTY;
+
+       case STF_ISP_REG_METHOD_TABLE:
+               if (reg_param->reg_info.length > STF_ISP_REG_TBL_BUF_SIZE) {
+                       st_err(ST_ISP, "Failed to access register. \
+                               The (length=0x%08X > 0x%08X) is out of size!!!\n",
+                               reg_param->reg_info.length, STF_ISP_REG_TBL_BUF_SIZE);
+                       return -EINVAL;
+               }
+               size = sizeof(u32) * reg_param->reg_info.length * 2;
+               break;
+
+       case STF_ISP_REG_METHOD_TABLE_2:
+               if (reg_param->reg_info.length > STF_ISP_REG_TBL_2_BUF_SIZE) {
+                       st_err(ST_ISP, "Failed to access register. \
+                               The (length=0x%08X > 0x%08X) is out of size!!!\n",
+                               reg_param->reg_info.length, STF_ISP_REG_TBL_2_BUF_SIZE);
+                       return -EINVAL;
+               }
+               size = sizeof(u32) * reg_param->reg_info.length * 3;
+               break;
+
+       case STF_ISP_REG_METHOD_TABLE_3:
+               if (reg_param->reg_info.length > STF_ISP_REG_TBL_3_BUF_SIZE) {
+                       st_err(ST_ISP, "Failed to access register. \
+                               The (length=0x%08X > 0x%08X) is out of size!!!\n",
+                               reg_param->reg_info.length, STF_ISP_REG_TBL_3_BUF_SIZE);
+                       return -EINVAL;
+               }
+               size = sizeof(u32) * reg_param->reg_info.length * 4;
+               break;
+
+       case STF_ISP_REG_METHOD_SMPL_PACK:
+               st_err(ST_ISP, "Reg Read - Failed to access register. The method = \
+                       STF_ISP_REG_METHOD_SMPL_PACK is not supported!!!\n");
+               return -ENOTTY;
+
+       case STF_ISP_REG_METHOD_SOFT_RDMA:
+               // This mode is not supported in the V4L2 version.
+               st_err(ST_ISP, "Reg Read - Failed to access register. The method = \
+                       STF_ISP_REG_METHOD_SOFT_RDMA is not supported!!!\n");
+               return -ENOTTY;
+
+       default:
+               st_err(ST_ISP, "Failed to access register. The method=%d \
+                       is not supported!!!\n", reg_param->reg_info.method);
+               return -ENOTTY;
+       }
+
+       memset(&reg_buf, 0, sizeof(union reg_buf));
+       if (size) {
+               r = copy_from_user((u8 *)reg_buf.buffer,
+                       (u8 *)reg_param->reg_buf->buffer, size);
+               if (r) {
+                       st_err(ST_ISP, "Failed to call copy_from_user for the \
+                               reg_param->reg_buf value\n");
+                       return -EIO;
+               }
+       }
+
+       size = 0;
+       switch (reg_param->reg_info.method) {
+       case STF_ISP_REG_METHOD_ONE_REG:
+               reg_buf.buffer[0] = reg_read(ispbase, reg_param->reg_info.offset);
+               size = sizeof(u32);
+               break;
+
+       case STF_ISP_REG_METHOD_SERIES:
+               for (r = 0; r < reg_param->reg_info.length; r++) {
+                       reg_buf.buffer[r] = reg_read(ispbase,
+                               reg_param->reg_info.offset + (r * 4));
+               }
+               size = sizeof(u32) * reg_param->reg_info.length;
+               break;
+
+       case STF_ISP_REG_METHOD_MODULE:
+               break;
+
+       case STF_ISP_REG_METHOD_TABLE:
+               for (r = 0; r < reg_param->reg_info.length; r++) {
+                       reg_buf.reg_tbl[r].value = reg_read(ispbase,
+                               reg_buf.reg_tbl[r].offset);
+               }
+               size = sizeof(u32) * reg_param->reg_info.length * 2;
+               break;
+
+       case STF_ISP_REG_METHOD_TABLE_2:
+               for (r = 0; r < reg_param->reg_info.length; r++) {
+                       if (reg_buf.reg_tbl2[r].mask) {
+                               reg_buf.reg_tbl2[r].value = (reg_read(ispbase,
+                                       reg_buf.reg_tbl2[r].offset)
+                                               & reg_buf.reg_tbl2[r].mask);
+                       } else {
+                               reg_buf.reg_tbl2[r].value = reg_read(ispbase,
+                                       reg_buf.reg_tbl2[r].offset);
+                       }
+               }
+               size = sizeof(u32) * reg_param->reg_info.length * 3;
+               break;
+
+       case STF_ISP_REG_METHOD_TABLE_3:
+               for (r = 0; r < reg_param->reg_info.length; r++) {
+                       if (reg_buf.reg_tbl3[r].mask) {
+                               reg_buf.reg_tbl3[r].value = (reg_read(ispbase,
+                                       reg_buf.reg_tbl3[r].offset)
+                                               & reg_buf.reg_tbl3[r].mask);
+                       } else {
+                               reg_buf.reg_tbl3[r].value = reg_read(ispbase,
+                                       reg_buf.reg_tbl3[r].offset);
+                       }
+                       if (reg_buf.reg_tbl3[r].delay_ms) {
+                               usleep_range(1000 * reg_buf.reg_tbl3[r].delay_ms,
+                                       1000 * reg_buf.reg_tbl3[r].delay_ms + 100);
+                       }
+               }
+               size = sizeof(u32) * reg_param->reg_info.length * 4;
+               break;
+
+       case STF_ISP_REG_METHOD_SMPL_PACK:
+               break;
+
+       case STF_ISP_REG_METHOD_SOFT_RDMA:
+               break;
+
+       default:
+               break;
+       }
+
+       r = copy_to_user((u8 *)reg_param->reg_buf->buffer, (u8 *)reg_buf.buffer,
+               size);
+       if (r) {
+               st_err(ST_ISP, "Failed to call copy_to_user for the \
+                       reg_param->buffer value\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int stf_isp_soft_rdma(struct stf_isp_dev *isp_dev, u32 rdma_addr)
+{
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase;
+       struct isp_rdma_info *rdma_info = NULL;
+       s32 len;
+       u32 offset;
+       int ret = 0;
+
+       if (isp_dev->id == 0)
+               ispbase = vin->isp_isp0_base;
+       else
+               ispbase = vin->isp_isp1_base;
+
+       rdma_info = phys_to_virt(rdma_addr);
+       while (1) {
+               if (rdma_info->tag == RDMA_WR_ONE) {
+                       reg_write(ispbase, rdma_info->offset, rdma_info->param);
+                       rdma_info++;
+               } else if (rdma_info->tag == RDMA_WR_SRL) {
+                       offset = rdma_info->offset;
+                       len = rdma_info->param;
+                       rdma_info++;
+                       while (len > 0) {
+                               reg_write(ispbase, offset, rdma_info->param);
+                               offset += 4;
+                               len--;
+                               if (len > 0) {
+                                       reg_write(ispbase, offset, rdma_info->value);
+                                       len--;
+                               }
+                               offset += 4;
+                               rdma_info++;
+                       }
+               } else if (rdma_info->tag == RDMA_LINK) {
+                       rdma_info = phys_to_virt(rdma_info->param);
+               } else if (rdma_info->tag == RDMA_SINT) {
+                       /* Software not support this command. */
+                       rdma_info++;
+               } else if (rdma_info->tag == RDMA_END) {
+                       break;
+               } else
+                       rdma_info++;
+       }
+
+       return ret;
+}
+
+static int stf_isp_reg_write(struct stf_isp_dev *isp_dev, void *arg)
+{
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase;
+       struct isp_reg_param *reg_param = arg;
+       struct isp_rdma_info *rdma_info = NULL;
+       s32 len;
+       u32 offset;
+       u32 size;
+       unsigned long r;
+       int ret = 0;
+
+       if ((reg_param->reg_buf == NULL)
+               && (reg_param->reg_info.method != STF_ISP_REG_METHOD_SOFT_RDMA)) {
+               st_err(ST_ISP, "Failed to access register. \
+                       The register buffer pointer is NULL!!!\n");
+               return -EINVAL;
+       }
+
+       if (isp_dev->id == 0)
+               ispbase = vin->isp_isp0_base;
+       else
+               ispbase = vin->isp_isp1_base;
+
+       size = 0;
+       switch (reg_param->reg_info.method) {
+       case STF_ISP_REG_METHOD_ONE_REG:
+               size = sizeof(u32);
+               break;
+
+       case STF_ISP_REG_METHOD_SERIES:
+               if (reg_param->reg_info.length > STF_ISP_REG_BUF_SIZE) {
+                       st_err(ST_ISP, "Failed to access register. \
+                               The (length=0x%08X > 0x%08X) is out of size!!!\n",
+                               reg_param->reg_info.length, STF_ISP_REG_BUF_SIZE);
+                       return -EINVAL;
+               }
+               size = sizeof(u32) * reg_param->reg_info.length;
+               break;
+
+       case STF_ISP_REG_METHOD_MODULE:
+               // This mode is not supported in the V4L2 version.
+               st_err(ST_ISP, "Reg Write - Failed to access register. \
+                       The method = STF_ISP_REG_METHOD_MODULE is not supported!!!\n");
+               return -ENOTTY;
+
+       case STF_ISP_REG_METHOD_TABLE:
+               if (reg_param->reg_info.length > STF_ISP_REG_TBL_BUF_SIZE) {
+                       st_err(ST_ISP, "Failed to access register. \
+                               The (length=0x%08X > 0x%08X) is out of size!!!\n",
+                               reg_param->reg_info.length, STF_ISP_REG_TBL_BUF_SIZE);
+                       return -EINVAL;
+               }
+               size = sizeof(u32) * reg_param->reg_info.length * 2;
+               break;
+
+       case STF_ISP_REG_METHOD_TABLE_2:
+               if (reg_param->reg_info.length > STF_ISP_REG_TBL_2_BUF_SIZE) {
+                       st_err(ST_ISP, "Failed to access register. \
+                               The (length=0x%08X > 0x%08X) is out of size!!!\n",
+                               reg_param->reg_info.length, STF_ISP_REG_TBL_2_BUF_SIZE);
+                       return -EINVAL;
+               }
+               size = sizeof(u32) * reg_param->reg_info.length * 3;
+               break;
+
+       case STF_ISP_REG_METHOD_TABLE_3:
+               if (reg_param->reg_info.length > STF_ISP_REG_TBL_3_BUF_SIZE) {
+                       st_err(ST_ISP, "Failed to access register. \
+                               The (length=0x%08X > 0x%08X) is out of size!!!\n",
+                               reg_param->reg_info.length, STF_ISP_REG_TBL_3_BUF_SIZE);
+                       return -EINVAL;
+               }
+               size = sizeof(u32) * reg_param->reg_info.length * 4;
+               break;
+
+       case STF_ISP_REG_METHOD_SMPL_PACK:
+               if (reg_param->reg_info.length > STF_ISP_REG_SMPL_PACK_BUF_SIZE) {
+                       st_err(ST_ISP, "Failed to access register. \
+                               The (length=0x%08X > 0x%08X) is out of size!!!\n",
+                               reg_param->reg_info.length, STF_ISP_REG_SMPL_PACK_BUF_SIZE);
+                       return -EINVAL;
+               }
+               size = sizeof(u32) * reg_param->reg_info.length * 2;
+               break;
+
+       case STF_ISP_REG_METHOD_SOFT_RDMA:
+               break;
+
+       default:
+               st_err(ST_ISP, "Failed to access register. The method=%d \
+                       is not supported!!!\n", reg_param->reg_info.method);
+               return -ENOTTY;
+       }
+
+       memset(&reg_buf, 0, sizeof(union reg_buf));
+       if (size) {
+               r = copy_from_user((u8 *)reg_buf.buffer,
+                       (u8 *)reg_param->reg_buf->buffer, size);
+               if (r) {
+                       st_err(ST_ISP, "Failed to call copy_from_user for the \
+                               reg_param->reg_buf value\n");
+                       return -EIO;
+               }
+       }
+
+       switch (reg_param->reg_info.method) {
+       case STF_ISP_REG_METHOD_ONE_REG:
+               reg_write(ispbase, reg_param->reg_info.offset, reg_buf.buffer[0]);
+               break;
+
+       case STF_ISP_REG_METHOD_SERIES:
+               for (r = 0; r < reg_param->reg_info.length; r++) {
+                       reg_write(ispbase, reg_param->reg_info.offset + (r * 4),
+                               reg_buf.buffer[r]);
+               }
+               break;
+
+       case STF_ISP_REG_METHOD_MODULE:
+               /* This mode is not supported in the V4L2 version. */
+               break;
+
+       case STF_ISP_REG_METHOD_TABLE:
+               for (r = 0; r < reg_param->reg_info.length; r++) {
+                       reg_write(ispbase, reg_buf.reg_tbl[r].offset,
+                               reg_buf.reg_tbl[r].value);
+               }
+               break;
+
+       case STF_ISP_REG_METHOD_TABLE_2:
+               for (r = 0; r < reg_param->reg_info.length; r++) {
+                       if (reg_buf.reg_tbl2[r].mask) {
+                               reg_set_bit(ispbase, reg_buf.reg_tbl2[r].offset,
+                                       reg_buf.reg_tbl2[r].mask, reg_buf.reg_tbl2[r].value);
+                       } else {
+                               reg_write(ispbase, reg_buf.reg_tbl2[r].offset,
+                                       reg_buf.reg_tbl2[r].value);
+                       }
+               }
+               break;
+
+       case STF_ISP_REG_METHOD_TABLE_3:
+               for (r = 0; r < reg_param->reg_info.length; r++) {
+                       if (reg_buf.reg_tbl3[r].mask) {
+                               reg_set_bit(ispbase, reg_buf.reg_tbl3[r].offset,
+                                       reg_buf.reg_tbl3[r].mask, reg_buf.reg_tbl3[r].value);
+                       } else {
+                               reg_write(ispbase, reg_buf.reg_tbl3[r].offset,
+                                       reg_buf.reg_tbl3[r].value);
+                       }
+                       if (reg_buf.reg_tbl3[r].delay_ms) {
+                               usleep_range(1000 * reg_buf.reg_tbl3[r].delay_ms,
+                                       1000 * reg_buf.reg_tbl3[r].delay_ms + 100);
+                       }
+               }
+               break;
+
+       case STF_ISP_REG_METHOD_SMPL_PACK:
+               size = reg_param->reg_info.length;
+               rdma_info = &reg_buf.rdma_cmd[0];
+               while (size) {
+                       if (rdma_info->tag == RDMA_WR_ONE) {
+                               reg_write(ispbase, rdma_info->offset, rdma_info->param);
+                               rdma_info++;
+                               size--;
+                       } else if (rdma_info->tag == RDMA_WR_SRL) {
+                               offset = rdma_info->offset;
+                               len = rdma_info->param;
+                               rdma_info++;
+                               size--;
+                               while (size && (len > 0)) {
+                                       reg_write(ispbase, offset, rdma_info->param);
+                                       offset += 4;
+                                       len--;
+                                       if (len > 0) {
+                                               reg_write(ispbase, offset, rdma_info->value);
+                                               len--;
+                                       }
+                                       offset += 4;
+                                       rdma_info++;
+                                       size--;
+                               }
+                       } else if (rdma_info->tag == RDMA_END) {
+                               break;
+                       } else {
+                               rdma_info++;
+                               size--;
+                       }
+               }
+               break;
+
+       case STF_ISP_REG_METHOD_SOFT_RDMA:
+               /*
+                * Simulation the hardware RDMA behavior to debug and verify
+                * the RDMA chain.
+                */
+               ret = stf_isp_soft_rdma(isp_dev, reg_param->reg_info.offset);
+               break;
+
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static int stf_isp_shadow_trigger(struct stf_isp_dev *isp_dev)
+{
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase;
+
+       if (isp_dev->id == 0)
+               ispbase = vin->isp_isp0_base;
+       else
+               ispbase = vin->isp_isp1_base;
+
+       // shadow update
+       reg_set_bit(ispbase, ISP_REG_CSIINTS_ADDR, (BIT(17) | BIT(16)), 0x30000);
+       reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, (BIT(1) | BIT(0)), 0x3);
+       return 0;
+}
+
 void dump_isp_reg(void *__iomem ispbase, int id)
 {
        int j;
@@ -419,4 +866,7 @@ struct isp_hw_ops isp_ops = {
        .isp_config_set        = stf_isp_config_set,
        .isp_set_format        = stf_isp_set_format,
        .isp_stream_set        = stf_isp_stream_set,
+       .isp_reg_read          = stf_isp_reg_read,
+       .isp_reg_write         = stf_isp_reg_write,
+       .isp_shadow_trigger    = stf_isp_shadow_trigger,
 };
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_isp_ioctl.h b/drivers/media/platform/starfive/v4l2_driver/stf_isp_ioctl.h
new file mode 100644 (file)
index 0000000..2b07d6f
--- /dev/null
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#ifndef STF_ISP_IOCTL_H
+#define STF_ISP_IOCTL_H
+
+
+#include <media/v4l2-ctrls.h>
+
+
+#define FILENAME_MAX_LEN     30
+
+#define ISP_IOC                         ('V')
+#define STF_ISP_REG_BUF_SIZE            (768)
+#define STF_ISP_REG_TBL_BUF_SIZE        (STF_ISP_REG_BUF_SIZE / 2)
+#define STF_ISP_REG_TBL_2_BUF_SIZE      (STF_ISP_REG_BUF_SIZE / 3)
+#define STF_ISP_REG_TBL_3_BUF_SIZE      (STF_ISP_REG_BUF_SIZE / 4)
+#define STF_ISP_REG_SMPL_PACK_BUF_SIZE  (STF_ISP_REG_BUF_SIZE / 2)
+#define RDMA_WR_ONE                     (0xA0)
+#define RDMA_WR_SRL                     (0xA1)
+#define RDMA_LINK                       (0xA2)
+#define RDMA_SINT                       (0xA3)
+#define RDMA_END                        (0xAF)
+
+
+enum _STF_ISP_IOCTL {
+       STF_ISP_IOCTL_LOAD_FW = BASE_VIDIOC_PRIVATE + 1,
+       STF_ISP_IOCTL_DMABUF_ALLOC,
+       STF_ISP_IOCTL_DMABUF_FREE,
+       STF_ISP_IOCTL_GET_HW_VER,
+       STF_ISP_IOCTL_REG,
+       STF_ISP_IOCTL_SHADOW_LOCK,
+       STF_ISP_IOCTL_SHADOW_UNLOCK,
+       STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER,
+       STF_ISP_IOCTL_SET_USER_CONFIG_ISP,
+       STF_ISP_IOCTL_MAX
+};
+
+enum _STF_ISP_REG_METHOD {
+       STF_ISP_REG_METHOD_ONE_REG = 0,
+       STF_ISP_REG_METHOD_SERIES,
+       STF_ISP_REG_METHOD_MODULE,
+       STF_ISP_REG_METHOD_TABLE,
+       STF_ISP_REG_METHOD_TABLE_2,
+       STF_ISP_REG_METHOD_TABLE_3,
+       STF_ISP_REG_METHOD_SMPL_PACK,
+       STF_ISP_REG_METHOD_SOFT_RDMA,
+       STF_ISP_REG_METHOD_MAX
+};
+
+
+struct stfisp_fw_info {
+       char __user filename[FILENAME_MAX_LEN];
+};
+
+struct dmabuf_create {
+       __u32 fd;
+       __u32 size;
+       __u32 paddr;
+};
+
+struct isp_rdma_info {
+       u32 param;
+       union {
+               u32 value;
+               struct {
+                       u32 offset  : 24;
+                       u32 tag     : 8;
+               };
+       };
+};
+
+struct isp_reg_info {
+       /** @brief [in] access method of register */
+       u8 method;
+       /** @brief [in] offset indicated which register will be read/write */
+       u32 offset;
+       /** @brief [in] length for indicated how much register will be read/write */
+       u32 length;
+};
+
+union reg_buf {
+       u32 buffer[STF_ISP_REG_BUF_SIZE];
+       struct {
+               u32 offset;
+               u32 value;
+       } reg_tbl[STF_ISP_REG_TBL_BUF_SIZE];
+       struct {
+               u32 offset;
+               u32 value;
+               u32 mask;
+       } reg_tbl2[STF_ISP_REG_TBL_2_BUF_SIZE];
+       struct {
+               u32 offset;
+               u32 value;
+               u32 mask;
+               u32 delay_ms;
+       } reg_tbl3[STF_ISP_REG_TBL_3_BUF_SIZE];
+       struct isp_rdma_info rdma_cmd[STF_ISP_REG_SMPL_PACK_BUF_SIZE];
+};
+
+struct isp_reg_param {
+       /** @brief [in, out] register read/write information */
+       struct isp_reg_info reg_info;
+       /** @brief [in, out] buffer */
+       union reg_buf *reg_buf;
+};
+
+
+#define VIDIOC_STFISP_LOAD_FW \
+       _IOW(ISP_IOC, STF_ISP_IOCTL_LOAD_FW, struct stfisp_fw_info)
+#define VIDIOC_STF_DMABUF_ALLOC \
+       _IOWR(ISP_IOC, STF_ISP_IOCTL_DMABUF_ALLOC, struct dmabuf_create)
+#define VIDIOC_STF_DMABUF_FREE \
+       _IOWR(ISP_IOC, STF_ISP_IOCTL_DMABUF_FREE, struct dmabuf_create)
+#define VIDIOC_STFISP_GET_REG \
+       _IOWR(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param)
+#define VIDIOC_STFISP_SET_REG \
+       _IOW(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param)
+#define VIDIOC_STFISP_SHADOW_LOCK \
+       _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_LOCK)
+#define VIDIOC_STFISP_SHADOW_UNLOCK \
+       _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK)
+#define VIDIOC_STFISP_SHADOW_UNLOCK_N_TRIGGER \
+       _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER)
+#define VIDIOC_STFISP_SET_USER_CONFIG_ISP \
+       _IO(ISP_IOC, STF_ISP_IOCTL_SET_USER_CONFIG_ISP)
+
+
+#endif /* STF_ISP_IOCTL_H */