--- /dev/null
+// 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;
+}
#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},
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;
}
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) {
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,
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 = {
#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[] = {
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(®_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(®_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 = ®_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;
.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,
};
--- /dev/null
+/* 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 */