On Wed May 6 17:32:45 2026 +0800, Bin Du wrote:
> ISP firmware controls ISP HW pipeline using dedicated embedded processor
> called ccpu. The communication between ISP FW and driver is using commands
> and response messages sent through the ring buffer. Command buffers support
> either global setting that is not specific to the stream and support stream
> specific parameters. Response buffers contain ISP FW notification
> information such as frame buffer done and command done. IRQ is used for
> receiving response buffer from ISP firmware, which is handled in the main
> isp4 media device. ISP ccpu is booted up through the firmware loading
> helper function prior to stream start. Memory used for command buffer and
> response buffer needs to be allocated from amdgpu buffer manager because
> isp4 is a child device of amdgpu.
> 
> Co-developed-by: Sultan Alsawaf <[email protected]>
> Signed-off-by: Sultan Alsawaf <[email protected]>
> Co-developed-by: Svetoslav Stoilov <[email protected]>
> Signed-off-by: Svetoslav Stoilov <[email protected]>
> Signed-off-by: Bin Du <[email protected]>
> Reviewed-by: Sultan Alsawaf <[email protected]>
> Tested-by: Alexey Zagorodnikov <[email protected]>
> Tested-by: Kate Hsuan <[email protected]>
> Signed-off-by: Sakari Ailus <[email protected]>

Patch committed.

Thanks,
Sakari Ailus

 MAINTAINERS                                        |   3 +
 drivers/media/platform/amd/isp4/Makefile           |   3 +-
 drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h | 318 ++++++++
 drivers/media/platform/amd/isp4/isp4_interface.c   | 833 +++++++++++++++++++++
 drivers/media/platform/amd/isp4/isp4_interface.h   | 144 ++++
 5 files changed, 1300 insertions(+), 1 deletion(-)

---

diff --git a/MAINTAINERS b/MAINTAINERS
index e2561d95d251..1d19168cecaa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1164,7 +1164,10 @@ F:       drivers/media/platform/amd/isp4/Kconfig
 F:     drivers/media/platform/amd/isp4/Makefile
 F:     drivers/media/platform/amd/isp4/isp4.c
 F:     drivers/media/platform/amd/isp4/isp4.h
+F:     drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h
 F:     drivers/media/platform/amd/isp4/isp4_hw_reg.h
+F:     drivers/media/platform/amd/isp4/isp4_interface.c
+F:     drivers/media/platform/amd/isp4/isp4_interface.h
 
 AMD KFD
 M:     Felix Kuehling <[email protected]>
diff --git a/drivers/media/platform/amd/isp4/Makefile 
b/drivers/media/platform/amd/isp4/Makefile
index 500b81ce5d14..c7eadd33fc97 100644
--- a/drivers/media/platform/amd/isp4/Makefile
+++ b/drivers/media/platform/amd/isp4/Makefile
@@ -3,4 +3,5 @@
 # Copyright (C) 2025 Advanced Micro Devices, Inc.
 
 obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o
-amd_isp4_capture-objs := isp4.o
+amd_isp4_capture-objs := isp4.o \
+                         isp4_interface.o
diff --git a/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h 
b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h
new file mode 100644
index 000000000000..88bacb00355c
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h
@@ -0,0 +1,318 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _ISP4_FW_CMD_RESP_H_
+#define _ISP4_FW_CMD_RESP_H_
+
+/*
+ *        Two types of command/response channel.
+ *          Type Global Command has one command/response channel.
+ *          Type Stream Command has one command/response channel.
+ *-----------                                        ------------
+ *|         |       ---------------------------      |          |
+ *|         |  ---->|  Global Command         |----> |          |
+ *|         |       ---------------------------      |          |
+ *|         |                                        |          |
+ *|         |                                        |          |
+ *|         |       ---------------------------      |          |
+ *|         |  ---->|   Stream Command        |----> |          |
+ *|         |       ---------------------------      |          |
+ *|         |                                        |          |
+ *|         |                                        |          |
+ *|         |                                        |          |
+ *|  HOST   |                                        | Firmware |
+ *|         |                                        |          |
+ *|         |                                        |          |
+ *|         |       --------------------------       |          |
+ *|         |  <----|  Global Response       |<----  |          |
+ *|         |       --------------------------       |          |
+ *|         |                                        |          |
+ *|         |                                        |          |
+ *|         |       --------------------------       |          |
+ *|         |  <----|  Stream Response       |<----  |          |
+ *|         |       --------------------------       |          |
+ *|         |                                        |          |
+ *|         |                                        |          |
+ *-----------                                        ------------
+ */
+
+/*
+ *        cmd_id is in the format of following type:
+ *        type: indicate command type, global/stream commands.
+ *        group: indicate the command group.
+ *        id: A unique command identification in one type and group.
+ *        |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->|
+ *        |      type       |      group      |       id       |
+ */
+
+#define ISP4FW_CMD_TYPE_SHIFT            24
+#define ISP4FW_CMD_GROUP_SHIFT           16
+#define ISP4FW_CMD_TYPE_STREAM_CTRL      (0x2U << ISP4FW_CMD_TYPE_SHIFT)
+
+#define ISP4FW_CMD_GROUP_STREAM_CTRL     (0x1U << ISP4FW_CMD_GROUP_SHIFT)
+#define ISP4FW_CMD_GROUP_STREAM_BUFFER   (0x4U << ISP4FW_CMD_GROUP_SHIFT)
+
+/* Stream  Command */
+#define ISP4FW_CMD_ID_SET_STREAM_CONFIG  (ISP4FW_CMD_TYPE_STREAM_CTRL\
+                                        | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x1)
+#define ISP4FW_CMD_ID_SET_OUT_CHAN_PROP  (ISP4FW_CMD_TYPE_STREAM_CTRL\
+                                        | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x3)
+#define ISP4FW_CMD_ID_ENABLE_OUT_CHAN    (ISP4FW_CMD_TYPE_STREAM_CTRL\
+                                        | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x5)
+#define ISP4FW_CMD_ID_START_STREAM       (ISP4FW_CMD_TYPE_STREAM_CTRL\
+                                        | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x7)
+#define ISP4FW_CMD_ID_STOP_STREAM        (ISP4FW_CMD_TYPE_STREAM_CTRL\
+                                        | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x8)
+
+/* Stream Buffer Command */
+#define ISP4FW_CMD_ID_SEND_BUFFER        (ISP4FW_CMD_TYPE_STREAM_CTRL\
+                                        | ISP4FW_CMD_GROUP_STREAM_BUFFER | 0x1)
+
+/*
+ *        resp_id is in the format of following type:
+ *        type: indicate command type, global/stream commands.
+ *        group: indicate the command group.
+ *        id: A unique command identification in one type and group.
+ *        |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->|
+ *        |      type       |      group      |       id       |
+ */
+
+#define ISP4FW_RESP_GROUP_SHIFT          16
+
+#define ISP4FW_RESP_GROUP_GENERAL        (0x1 << ISP4FW_RESP_GROUP_SHIFT)
+#define ISP4FW_RESP_GROUP_NOTIFICATION   (0x3 << ISP4FW_RESP_GROUP_SHIFT)
+
+/* General Response */
+#define ISP4FW_RESP_ID_CMD_DONE          (ISP4FW_RESP_GROUP_GENERAL | 0x1)
+
+/* Notification */
+#define ISP4FW_RESP_ID_NOTI_FRAME_DONE   (ISP4FW_RESP_GROUP_NOTIFICATION | 0x1)
+
+#define ISP4FW_CMD_STATUS_SUCCESS        0
+#define ISP4FW_CMD_STATUS_FAIL           1
+#define ISP4FW_CMD_STATUS_SKIPPED        2
+
+#define ISP4FW_ADDR_SPACE_TYPE_GPU_VA    4
+
+#define ISP4FW_MEMORY_POOL_SIZE          (100 * 1024 * 1024)
+
+/*
+ * standard ISP pipeline: mipicsi=>isp
+ */
+#define ISP4FW_MIPI0_ISP_PIPELINE_ID     0x5f91
+
+enum isp4fw_sensor_id {
+       /* Sensor id for ISP input from MIPI port 0 */
+       ISP4FW_SENSOR_ID_ON_MIPI0  = 0,
+};
+
+enum isp4fw_stream_id {
+       ISP4FW_STREAM_ID_INVALID = -1,
+       ISP4FW_STREAM_ID_1 = 0,
+       ISP4FW_STREAM_ID_2 = 1,
+       ISP4FW_STREAM_ID_3 = 2,
+       ISP4FW_STREAM_ID_MAXIMUM
+};
+
+enum isp4fw_image_format {
+       /* 4:2:0,semi-planar, 8-bit */
+       ISP4FW_IMAGE_FORMAT_NV12 = 1,
+       /* interleave, 4:2:2, 8-bit */
+       ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED = 7,
+};
+
+enum isp4fw_pipe_out_ch {
+       ISP4FW_ISP_PIPE_OUT_CH_PREVIEW = 0,
+};
+
+enum isp4fw_yuv_range {
+       ISP4FW_ISP_YUV_RANGE_FULL = 0,     /* YUV value range in 0~255 */
+       ISP4FW_ISP_YUV_RANGE_NARROW = 1,   /* YUV value range in 16~235 */
+       ISP4FW_ISP_YUV_RANGE_MAX
+};
+
+enum isp4fw_buffer_type {
+       ISP4FW_BUFFER_TYPE_PREVIEW = 8,
+       ISP4FW_BUFFER_TYPE_META_INFO = 10,
+       ISP4FW_BUFFER_TYPE_MEM_POOL = 15,
+};
+
+enum isp4fw_buffer_status {
+       /* The buffer is INVALID */
+       ISP4FW_BUFFER_STATUS_INVALID,
+       /* The buffer is not filled with image data */
+       ISP4FW_BUFFER_STATUS_SKIPPED,
+       /* The buffer is available and awaiting to be filled */
+       ISP4FW_BUFFER_STATUS_EXIST,
+       /* The buffer is filled with image data */
+       ISP4FW_BUFFER_STATUS_DONE,
+       /* The buffer is unavailable */
+       ISP4FW_BUFFER_STATUS_LACK,
+       /* The buffer is dirty, probably caused by LMI leakage */
+       ISP4FW_BUFFER_STATUS_DIRTY,
+       ISP4FW_BUFFER_STATUS_MAX
+};
+
+enum isp4fw_buffer_source {
+       /* The buffer is from the stream buffer queue */
+       ISP4FW_BUFFER_SOURCE_STREAM,
+};
+
+struct isp4fw_error_code {
+       u32 code1;
+       u32 code2;
+       u32 code3;
+       u32 code4;
+       u32 code5;
+};
+
+/* Command Structure for FW */
+
+struct isp4fw_cmd {
+       u32 cmd_seq_num;
+       u32 cmd_id;
+       u32 cmd_param[12];
+       u16 cmd_stream_id;
+       u8 cmd_silent_resp;
+       u8 reserved;
+       u32 cmd_check_sum;
+};
+
+struct isp4fw_resp_cmd_done {
+       /*
+        * The host2fw command seqNum.
+        * To indicate which command this response refers to.
+        */
+       u32 cmd_seq_num;
+       /* The host2fw command id for host double check. */
+       u32 cmd_id;
+       /*
+        * Indicate the command process status.
+        * 0 means success. 1 means fail. 2 means skipped
+        */
+       u16 cmd_status;
+       /*
+        * If cmd_status is 1, the command failed. The host can check
+        * isp4fw_error_code for details.
+        */
+       u16 isp4fw_error_code;
+       /* The response payload type varies by cmd. */
+       u8 payload[36];
+};
+
+struct isp4fw_resp_param_package {
+       u32 package_addr_lo;    /* The low 32 bit of the pkg address. */
+       u32 package_addr_hi;    /* The high 32 bit of the pkg address. */
+       u32 package_size;       /* The total pkg size in bytes. */
+       u32 package_check_sum;  /* The byte sum of the pkg. */
+};
+
+struct isp4fw_resp {
+       u32 resp_seq_num;
+       u32 resp_id;
+       union {
+               struct isp4fw_resp_cmd_done cmd_done;
+               struct isp4fw_resp_param_package frame_done;
+               u32 resp_param[12];
+       } param;
+       u8  reserved[4];
+       u32 resp_check_sum;
+};
+
+struct isp4fw_mipi_pipe_path_cfg {
+       u32 b_enable;
+       enum isp4fw_sensor_id isp4fw_sensor_id;
+};
+
+struct isp4fw_isp_pipe_path_cfg {
+       u32  isp_pipe_id;       /* pipe ids for pipeline construction */
+};
+
+struct isp4fw_isp_stream_cfg {
+       /* Isp mipi path */
+       struct isp4fw_mipi_pipe_path_cfg mipi_pipe_path_cfg;
+       /* Isp pipe path */
+       struct isp4fw_isp_pipe_path_cfg  isp_pipe_path_cfg;
+       /* enable TNR */
+       u32 b_enable_tnr;
+       /*
+        * Number of frames for RTA processing.
+        * Set to 0 to use the firmware's default value.
+        */
+       u32 rta_frames_per_proc;
+};
+
+struct isp4fw_image_prop {
+       enum isp4fw_image_format image_format;
+       u32 width;
+       u32 height;
+       u32 luma_pitch;
+       u32 chroma_pitch;
+       enum isp4fw_yuv_range yuv_range;
+};
+
+struct isp4fw_buffer {
+       /*
+        * A check num for debug usage, host can set the buf_tags
+        * to different numbers
+        */
+       u32 buf_tags;
+       union {
+               u32 value;
+               struct {
+                       u32 space : 16;
+                       u32 vmid  : 16;
+               } bit;
+       } vmid_space;
+       u32 buf_base_a_lo;              /* Low address of buffer A */
+       u32 buf_base_a_hi;              /* High address of buffer A */
+       u32 buf_size_a;                 /* Buffer size of buffer A */
+
+       u32 buf_base_b_lo;              /* Low address of buffer B */
+       u32 buf_base_b_hi;              /* High address of buffer B */
+       u32 buf_size_b;                 /* Buffer size of buffer B */
+
+       u32 buf_base_c_lo;              /* Low address of buffer C */
+       u32 buf_base_c_hi;              /* High address of buffer C */
+       u32 buf_size_c;                 /* Buffer size of buffer C */
+};
+
+struct isp4fw_buffer_meta_info {
+       u32 enabled;                                    /* enabled flag */
+       enum isp4fw_buffer_status status;               /* BufferStatus */
+       struct isp4fw_error_code err;                   /* err code */
+       enum isp4fw_buffer_source source;               /* BufferSource */
+       struct isp4fw_image_prop image_prop;            /* image_prop */
+       struct isp4fw_buffer buffer;                    /* buffer info */
+};
+
+struct isp4fw_meta_info {
+       u32 poc;                                /* frame id */
+       u32 fc_id;                              /* frame ctl id */
+       u32 time_stamp_lo;                      /* timestamp low 32 bits */
+       u32 time_stamp_hi;                      /* timestamp_high 32 bits */
+       struct isp4fw_buffer_meta_info preview; /* preview BufferMetaInfo */
+};
+
+struct isp4fw_cmd_send_buffer {
+       enum isp4fw_buffer_type buffer_type;
+       struct isp4fw_buffer buffer;            /* buffer info */
+};
+
+struct isp4fw_cmd_set_out_ch_prop {
+       enum isp4fw_pipe_out_ch ch;     /* ISP output channel */
+       struct isp4fw_image_prop image_prop;    /* image property */
+};
+
+struct isp4fw_cmd_enable_out_ch {
+       enum isp4fw_pipe_out_ch ch;     /* ISP output channel */
+       u32 is_enable;                  /* If channel is enabled or not */
+};
+
+struct isp4fw_cmd_set_stream_cfg {
+       struct isp4fw_isp_stream_cfg stream_cfg; /* stream path config */
+};
+
+#endif /* _ISP4_FW_CMD_RESP_H_ */
diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c 
b/drivers/media/platform/amd/isp4/isp4_interface.c
new file mode 100644
index 000000000000..fc9ad7d91a7e
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_interface.c
@@ -0,0 +1,833 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/iopoll.h>
+
+#include "isp4_fw_cmd_resp.h"
+#include "isp4_hw_reg.h"
+#include "isp4_interface.h"
+
+#define ISP4IF_FW_RESP_RB_IRQ_EN_MASK \
+       (ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK\
+        | ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK)
+
+#define ISP4IF_FW_CMD_TIMEOUT (HZ / 2)
+
+struct isp4if_rb_config {
+       const char *name;
+       u32 index;
+       u32 reg_rptr;
+       u32 reg_wptr;
+       u32 reg_base_lo;
+       u32 reg_base_hi;
+       u32 reg_size;
+       u32 val_size;
+       u64 base_mc_addr;
+       void *base_sys_addr;
+};
+
+/* FW cmd ring buffer configuration */
+static struct isp4if_rb_config isp4if_cmd_rb_config[ISP4IF_STREAM_ID_MAX] = {
+       {
+               .name = "CMD_RB_GBL0",
+               .index = 3,
+               .reg_rptr = ISP_RB_RPTR4,
+               .reg_wptr = ISP_RB_WPTR4,
+               .reg_base_lo = ISP_RB_BASE_LO4,
+               .reg_base_hi = ISP_RB_BASE_HI4,
+               .reg_size = ISP_RB_SIZE4,
+       },
+       {
+               .name = "CMD_RB_STR1",
+               .index = 0,
+               .reg_rptr = ISP_RB_RPTR1,
+               .reg_wptr = ISP_RB_WPTR1,
+               .reg_base_lo = ISP_RB_BASE_LO1,
+               .reg_base_hi = ISP_RB_BASE_HI1,
+               .reg_size = ISP_RB_SIZE1,
+       },
+       {
+               .name = "CMD_RB_STR2",
+               .index = 1,
+               .reg_rptr = ISP_RB_RPTR2,
+               .reg_wptr = ISP_RB_WPTR2,
+               .reg_base_lo = ISP_RB_BASE_LO2,
+               .reg_base_hi = ISP_RB_BASE_HI2,
+               .reg_size = ISP_RB_SIZE2,
+       },
+       {
+               .name = "CMD_RB_STR3",
+               .index = 2,
+               .reg_rptr = ISP_RB_RPTR3,
+               .reg_wptr = ISP_RB_WPTR3,
+               .reg_base_lo = ISP_RB_BASE_LO3,
+               .reg_base_hi = ISP_RB_BASE_HI3,
+               .reg_size = ISP_RB_SIZE3,
+       },
+};
+
+/* FW resp ring buffer configuration */
+static struct isp4if_rb_config isp4if_resp_rb_config[ISP4IF_STREAM_ID_MAX] = {
+       {
+               .name = "RES_RB_GBL0",
+               .index = 3,
+               .reg_rptr = ISP_RB_RPTR12,
+               .reg_wptr = ISP_RB_WPTR12,
+               .reg_base_lo = ISP_RB_BASE_LO12,
+               .reg_base_hi = ISP_RB_BASE_HI12,
+               .reg_size = ISP_RB_SIZE12,
+       },
+       {
+               .name = "RES_RB_STR1",
+               .index = 0,
+               .reg_rptr = ISP_RB_RPTR9,
+               .reg_wptr = ISP_RB_WPTR9,
+               .reg_base_lo = ISP_RB_BASE_LO9,
+               .reg_base_hi = ISP_RB_BASE_HI9,
+               .reg_size = ISP_RB_SIZE9,
+       },
+       {
+               .name = "RES_RB_STR2",
+               .index = 1,
+               .reg_rptr = ISP_RB_RPTR10,
+               .reg_wptr = ISP_RB_WPTR10,
+               .reg_base_lo = ISP_RB_BASE_LO10,
+               .reg_base_hi = ISP_RB_BASE_HI10,
+               .reg_size = ISP_RB_SIZE10,
+       },
+       {
+               .name = "RES_RB_STR3",
+               .index = 2,
+               .reg_rptr = ISP_RB_RPTR11,
+               .reg_wptr = ISP_RB_WPTR11,
+               .reg_base_lo = ISP_RB_BASE_LO11,
+               .reg_base_hi = ISP_RB_BASE_HI11,
+               .reg_size = ISP_RB_SIZE11,
+       },
+};
+
+/* FW log ring buffer configuration */
+static struct isp4if_rb_config isp4if_log_rb_config = {
+       .name = "LOG_RB",
+       .index = 0,
+       .reg_rptr = ISP_LOG_RB_RPTR0,
+       .reg_wptr = ISP_LOG_RB_WPTR0,
+       .reg_base_lo = ISP_LOG_RB_BASE_LO0,
+       .reg_base_hi = ISP_LOG_RB_BASE_HI0,
+       .reg_size = ISP_LOG_RB_SIZE0,
+};
+
+static struct isp4if_gpu_mem_info *
+isp4if_gpu_mem_alloc(struct isp4_interface *ispif, u32 mem_size)
+{
+       struct isp4if_gpu_mem_info *mem_info;
+       struct device *dev = ispif->dev;
+       int ret;
+
+       mem_info = kmalloc_obj(*mem_info, GFP_KERNEL);
+       if (!mem_info)
+               return NULL;
+
+       mem_info->mem_size = mem_size;
+       ret = isp_kernel_buffer_alloc(dev, mem_info->mem_size,
+                                     &mem_info->mem_handle,
+                                     &mem_info->gpu_mc_addr,
+                                     &mem_info->sys_addr);
+       if (ret) {
+               kfree(mem_info);
+               return NULL;
+       }
+
+       return mem_info;
+}
+
+static void isp4if_gpu_mem_free(struct isp4_interface *ispif,
+                               struct isp4if_gpu_mem_info **mem_info_ptr)
+{
+       struct isp4if_gpu_mem_info *mem_info = *mem_info_ptr;
+       struct device *dev = ispif->dev;
+
+       if (!mem_info) {
+               dev_err(dev, "invalid mem_info\n");
+               return;
+       }
+
+       *mem_info_ptr = NULL;
+       isp_kernel_buffer_free(&mem_info->mem_handle, &mem_info->gpu_mc_addr,
+                              &mem_info->sys_addr);
+       kfree(mem_info);
+}
+
+static void isp4if_dealloc_fw_gpumem(struct isp4_interface *ispif)
+{
+       isp4if_gpu_mem_free(ispif, &ispif->fw_mem_pool);
+       isp4if_gpu_mem_free(ispif, &ispif->fw_cmd_resp_buf);
+       isp4if_gpu_mem_free(ispif, &ispif->fw_log_buf);
+
+       for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++)
+               isp4if_gpu_mem_free(ispif, &ispif->meta_info_buf[i]);
+}
+
+static int isp4if_alloc_fw_gpumem(struct isp4_interface *ispif)
+{
+       struct device *dev = ispif->dev;
+
+       ispif->fw_mem_pool = isp4if_gpu_mem_alloc(ispif,
+                                                 ISP4FW_MEMORY_POOL_SIZE);
+       if (!ispif->fw_mem_pool)
+               goto error_no_memory;
+
+       ispif->fw_cmd_resp_buf =
+               isp4if_gpu_mem_alloc(ispif, ISP4IF_RB_PMBMAP_MEM_SIZE);
+       if (!ispif->fw_cmd_resp_buf)
+               goto error_no_memory;
+
+       ispif->fw_log_buf =
+               isp4if_gpu_mem_alloc(ispif, ISP4IF_FW_LOG_RINGBUF_SIZE);
+       if (!ispif->fw_log_buf)
+               goto error_no_memory;
+
+       for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) {
+               ispif->meta_info_buf[i] =
+                       isp4if_gpu_mem_alloc(ispif, ISP4IF_META_INFO_BUF_SIZE);
+               if (!ispif->meta_info_buf[i])
+                       goto error_no_memory;
+       }
+
+       return 0;
+
+error_no_memory:
+       dev_err(dev, "failed to allocate gpu memory\n");
+       return -ENOMEM;
+}
+
+static u32 isp4if_compute_check_sum(const void *buf, size_t buf_size)
+{
+       const u8 *surplus_ptr;
+       const u32 *buffer;
+       u32 checksum = 0;
+       size_t i;
+
+       buffer = (const u32 *)buf;
+       for (i = 0; i < buf_size / sizeof(u32); i++)
+               checksum += buffer[i];
+
+       surplus_ptr = (const u8 *)&buffer[i];
+       /* add surplus data crc checksum */
+       for (i = 0; i < buf_size % sizeof(u32); i++)
+               checksum += surplus_ptr[i];
+
+       return checksum;
+}
+
+void isp4if_clear_cmdq(struct isp4_interface *ispif)
+{
+       struct isp4if_cmd_element *buf_node, *tmp_node;
+       LIST_HEAD(free_list);
+
+       scoped_guard(spinlock, &ispif->cmdq_lock)
+               list_splice_init(&ispif->cmdq, &free_list);
+
+       list_for_each_entry_safe(buf_node, tmp_node, &free_list, list)
+               kfree(buf_node);
+}
+
+static bool isp4if_is_cmdq_rb_full(struct isp4_interface *ispif,
+                                  enum isp4if_stream_id stream)
+{
+       struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream];
+       u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr;
+       u32 len = rb_config->val_size;
+       u32 rd_ptr, wr_ptr;
+       u32 bytes_free;
+
+       rd_ptr = isp4hw_rreg(ispif->mmio, rreg);
+       wr_ptr = isp4hw_rreg(ispif->mmio, wreg);
+
+       /*
+        * Read and write pointers are equal, indicating the ring buffer
+        * is empty
+        */
+       if (wr_ptr == rd_ptr)
+               return false;
+
+       if (wr_ptr > rd_ptr)
+               bytes_free = len - (wr_ptr - rd_ptr);
+       else
+               bytes_free = rd_ptr - wr_ptr;
+
+       /*
+        * Ignore one byte from the bytes free to prevent rd_ptr from equaling
+        * wr_ptr when the ring buffer is full, because rd_ptr == wr_ptr is
+        * supposed to indicate that the ring buffer is empty.
+        */
+       return bytes_free <= sizeof(struct isp4fw_cmd);
+}
+
+struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface 
*ispif,
+                                                  u32 seq_num, u32 cmd_id)
+{
+       struct isp4if_cmd_element *ele;
+
+       guard(spinlock)(&ispif->cmdq_lock);
+
+       list_for_each_entry(ele, &ispif->cmdq, list) {
+               if (ele->seq_num == seq_num && ele->cmd_id == cmd_id) {
+                       list_del(&ele->list);
+                       return ele;
+               }
+       }
+
+       return NULL;
+}
+
+/* Must check that isp4if_is_cmdq_rb_full() == false before calling */
+static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif,
+                                   enum isp4if_stream_id stream,
+                                   const struct isp4fw_cmd *cmd)
+{
+       struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream];
+       u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr;
+       void *mem_sys = rb_config->base_sys_addr;
+       const u32 cmd_sz = sizeof(*cmd);
+       struct device *dev = ispif->dev;
+       u32 len = rb_config->val_size;
+       const void *src = cmd;
+       u32 rd_ptr, wr_ptr;
+       u32 bytes_to_end;
+
+       rd_ptr = isp4hw_rreg(ispif->mmio, rreg);
+       wr_ptr = isp4hw_rreg(ispif->mmio, wreg);
+       if (rd_ptr >= len || wr_ptr >= len) {
+               dev_err(dev,
+                       "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, 
cmd_sz=%u\n",
+                       stream, rd_ptr, wr_ptr, len, cmd_sz);
+               return -EINVAL;
+       }
+
+       bytes_to_end = len - wr_ptr;
+       if (bytes_to_end >= cmd_sz) {
+               /* FW cmd is just a straight copy to the write pointer */
+               memcpy(mem_sys + wr_ptr, src, cmd_sz);
+               isp4hw_wreg(ispif->mmio, wreg, (wr_ptr + cmd_sz) % len);
+       } else {
+               /*
+                * FW cmd is split because the ring buffer needs to wrap
+                * around
+                */
+               memcpy(mem_sys + wr_ptr, src, bytes_to_end);
+               memcpy(mem_sys, src + bytes_to_end, cmd_sz - bytes_to_end);
+               isp4hw_wreg(ispif->mmio, wreg, cmd_sz - bytes_to_end);
+       }
+
+       return 0;
+}
+
+static inline enum isp4if_stream_id isp4if_get_fw_stream(u32 cmd_id)
+{
+       return ISP4IF_STREAM_ID_1;
+}
+
+static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id,
+                             const void *package,
+                             u32 package_size, bool sync)
+{
+       enum isp4if_stream_id stream = isp4if_get_fw_stream(cmd_id);
+       struct isp4if_cmd_element *ele = NULL;
+       struct device *dev = ispif->dev;
+       struct isp4fw_cmd cmd;
+       u32 seq_num;
+       int ret;
+
+       if (package_size > sizeof(cmd.cmd_param)) {
+               dev_err(dev, "fail pkgsize(%u) > %zu cmd:0x%x, stream %d\n",
+                       package_size, sizeof(cmd.cmd_param), cmd_id, stream);
+               return -EINVAL;
+       }
+
+       /*
+        * The struct will be shared with ISP FW, use memset() to guarantee
+        * padding bits are zeroed, since this is not guaranteed on all
+        * compilers.
+        */
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = cmd_id;
+       switch (stream) {
+       case ISP4IF_STREAM_ID_GLOBAL:
+               cmd.cmd_stream_id = ISP4FW_STREAM_ID_INVALID;
+               break;
+       case ISP4IF_STREAM_ID_1:
+               cmd.cmd_stream_id = ISP4FW_STREAM_ID_1;
+               break;
+       default:
+               dev_err(dev, "fail bad stream id %d\n", stream);
+               return -EINVAL;
+       }
+
+       /* Allocate the sync command object early and outside of the lock */
+       if (sync) {
+               ele = kmalloc_obj(*ele, GFP_KERNEL);
+               if (!ele)
+                       return -ENOMEM;
+
+               /* Get two references: one for the resp thread, one for us */
+               atomic_set(&ele->refcnt, 2);
+               init_completion(&ele->cmd_done);
+       }
+
+       if (package && package_size)
+               memcpy(cmd.cmd_param, package, package_size);
+
+       scoped_guard(mutex, &ispif->isp4if_mutex) {
+               ret = read_poll_timeout(isp4if_is_cmdq_rb_full, ret, !ret,
+                                       ISP4IF_RB_FULL_SLEEP_US,
+                                       ISP4IF_RB_FULL_TIMEOUT_US, false, ispif,
+                                       stream);
+               if (ret) {
+                       struct isp4if_rb_config *rb_config =
+                                       &isp4if_resp_rb_config[stream];
+                       u32 rd_ptr = isp4hw_rreg(ispif->mmio,
+                                                rb_config->reg_rptr);
+                       u32 wr_ptr = isp4hw_rreg(ispif->mmio,
+                                                rb_config->reg_wptr);
+
+                       dev_err(dev,
+                               "fail to get free cmdq slot, stream (%d),rd %u, 
wr %u\n",
+                               stream, rd_ptr, wr_ptr);
+                       ret = -ETIMEDOUT;
+                       goto free_ele;
+               }
+
+               seq_num = ispif->host2fw_seq_num++;
+               cmd.cmd_seq_num = seq_num;
+               cmd.cmd_check_sum = isp4if_compute_check_sum(&cmd, sizeof(cmd)
+                                                            - sizeof(u32));
+
+               /*
+                * only append the fw cmd to queue when its response needs to
+                * be waited for, currently there are only two such commands,
+                * disable channel and stop stream which are only sent after
+                * close camera
+                */
+               if (ele) {
+                       ele->seq_num = seq_num;
+                       ele->cmd_id = cmd_id;
+                       scoped_guard(spinlock, &ispif->cmdq_lock)
+                               list_add_tail(&ele->list, &ispif->cmdq);
+               }
+
+               ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd);
+               if (ret) {
+                       dev_err(dev,
+                               "fail for insert_isp_fw_cmd cmd_id (0x%08x)\n",
+                               cmd_id);
+                       goto err_dequeue_ele;
+               }
+       }
+
+       if (ele) {
+               ret = wait_for_completion_timeout(&ele->cmd_done,
+                                                 ISP4IF_FW_CMD_TIMEOUT);
+               if (!ret) {
+                       ret = -ETIMEDOUT;
+                       goto err_dequeue_ele;
+               }
+
+               ret = 0;
+               goto put_ele_ref;
+       }
+
+       return 0;
+
+err_dequeue_ele:
+       /*
+        * Try to remove the command from the queue. If that fails, then it
+        * means the response thread is currently using the object, and we need
+        * to use the refcount to avoid a use-after-free by either side.
+        */
+       if (ele && isp4if_rm_cmd_from_cmdq(ispif, seq_num, cmd_id))
+               goto free_ele;
+
+put_ele_ref:
+       /* Don't free the command if we didn't put the last reference */
+       if (ele && atomic_dec_return(&ele->refcnt))
+               ele = NULL;
+
+free_ele:
+       /*
+        * The response handler or the timeout/error path must dequeue the
+        * command element before we own the final reference.
+        */
+       if (ele)
+               INIT_LIST_HEAD(&ele->list);
+       kfree(ele);
+       return ret;
+}
+
+static int isp4if_send_buffer(struct isp4_interface *ispif,
+                             struct isp4if_img_buf_info *buf_info)
+{
+       struct isp4fw_cmd_send_buffer cmd;
+
+       /*
+        * The struct will be shared with ISP FW, use memset() to guarantee
+        * padding bits are zeroed, since this is not guaranteed on all
+        * compilers.
+        */
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.buffer_type = ISP4FW_BUFFER_TYPE_PREVIEW;
+       cmd.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
+       isp4if_split_addr64(buf_info->planes[0].mc_addr,
+                           &cmd.buffer.buf_base_a_lo,
+                           &cmd.buffer.buf_base_a_hi);
+       cmd.buffer.buf_size_a = buf_info->planes[0].len;
+
+       isp4if_split_addr64(buf_info->planes[1].mc_addr,
+                           &cmd.buffer.buf_base_b_lo,
+                           &cmd.buffer.buf_base_b_hi);
+       cmd.buffer.buf_size_b = buf_info->planes[1].len;
+
+       isp4if_split_addr64(buf_info->planes[2].mc_addr,
+                           &cmd.buffer.buf_base_c_lo,
+                           &cmd.buffer.buf_base_c_hi);
+       cmd.buffer.buf_size_c = buf_info->planes[2].len;
+
+       return isp4if_send_fw_cmd(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &cmd,
+                                 sizeof(cmd), false);
+}
+
+static void isp4if_init_rb_config(struct isp4_interface *ispif,
+                                 struct isp4if_rb_config *rb_config)
+{
+       isp4hw_wreg(ispif->mmio, rb_config->reg_rptr, 0x0);
+       isp4hw_wreg(ispif->mmio, rb_config->reg_wptr, 0x0);
+       isp4hw_wreg(ispif->mmio, rb_config->reg_base_lo,
+                   rb_config->base_mc_addr);
+       isp4hw_wreg(ispif->mmio, rb_config->reg_base_hi,
+                   rb_config->base_mc_addr >> 32);
+       isp4hw_wreg(ispif->mmio, rb_config->reg_size, rb_config->val_size);
+}
+
+static int isp4if_fw_init(struct isp4_interface *ispif)
+{
+       u32 aligned_rb_chunk_size = ISP4IF_RB_PMBMAP_MEM_CHUNK & 0xffffffc0;
+       struct isp4if_rb_config *rb_config;
+       u32 offset;
+       unsigned int i;
+
+       /* initialize CMD_RB streams */
+       for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) {
+               rb_config = (isp4if_cmd_rb_config + i);
+               offset = aligned_rb_chunk_size * rb_config->index;
+
+               rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE;
+               rb_config->base_sys_addr =
+                       ispif->fw_cmd_resp_buf->sys_addr + offset;
+               rb_config->base_mc_addr =
+                       ispif->fw_cmd_resp_buf->gpu_mc_addr + offset;
+
+               isp4if_init_rb_config(ispif, rb_config);
+       }
+
+       /* initialize RESP_RB streams */
+       for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) {
+               rb_config = (isp4if_resp_rb_config + i);
+               offset = aligned_rb_chunk_size *
+                        (rb_config->index + ISP4IF_RESP_CHAN_TO_RB_OFFSET - 1);
+
+               rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE;
+               rb_config->base_sys_addr =
+                       ispif->fw_cmd_resp_buf->sys_addr + offset;
+               rb_config->base_mc_addr =
+                       ispif->fw_cmd_resp_buf->gpu_mc_addr + offset;
+
+               isp4if_init_rb_config(ispif, rb_config);
+       }
+
+       /* initialize LOG_RB stream */
+       rb_config = &isp4if_log_rb_config;
+       rb_config->val_size = ISP4IF_FW_LOG_RINGBUF_SIZE;
+       rb_config->base_mc_addr = ispif->fw_log_buf->gpu_mc_addr;
+       rb_config->base_sys_addr = ispif->fw_log_buf->sys_addr;
+
+       isp4if_init_rb_config(ispif, rb_config);
+
+       return 0;
+}
+
+static int isp4if_wait_fw_ready(struct isp4_interface *ispif,
+                               u32 isp_status_addr)
+{
+       struct device *dev = ispif->dev;
+       u32 timeout_ms = 100;
+       u32 interval_ms = 1;
+       u32 reg_val;
+
+       /* wait for FW initialize done! */
+       if (!read_poll_timeout(isp4hw_rreg, reg_val, reg_val
+                              & ISP_STATUS__CCPU_REPORT_MASK,
+                              interval_ms * 1000, timeout_ms * 1000, false,
+                              ispif->mmio, isp_status_addr))
+               return 0;
+
+       dev_err(dev, "ISP CCPU FW boot failed\n");
+
+       return -ETIME;
+}
+
+static void isp4if_enable_ccpu(struct isp4_interface *ispif)
+{
+       u32 reg_val;
+
+       reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET);
+       reg_val &= (~ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK);
+       isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val);
+
+       usleep_range(100, 150);
+
+       reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL);
+       reg_val &= (~ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK);
+       isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val);
+}
+
+static void isp4if_disable_ccpu(struct isp4_interface *ispif)
+{
+       u32 reg_val;
+
+       reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL);
+       reg_val |= ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK;
+       isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val);
+
+       usleep_range(100, 150);
+
+       reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET);
+       reg_val |= ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK;
+       isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val);
+}
+
+static int isp4if_fw_boot(struct isp4_interface *ispif)
+{
+       struct device *dev = ispif->dev;
+
+       if (ispif->status != ISP4IF_STATUS_PWR_ON) {
+               dev_err(dev, "invalid isp power status %d\n", ispif->status);
+               return -EINVAL;
+       }
+
+       isp4if_disable_ccpu(ispif);
+
+       isp4if_fw_init(ispif);
+
+       /* clear ccpu status */
+       isp4hw_wreg(ispif->mmio, ISP_STATUS, 0x0);
+
+       isp4if_enable_ccpu(ispif);
+
+       if (isp4if_wait_fw_ready(ispif, ISP_STATUS)) {
+               isp4if_disable_ccpu(ispif);
+               return -EINVAL;
+       }
+
+       /* enable interrupts */
+       isp4hw_wreg(ispif->mmio, ISP_SYS_INT0_EN,
+                   ISP4IF_FW_RESP_RB_IRQ_EN_MASK);
+
+       ispif->status = ISP4IF_STATUS_FW_RUNNING;
+
+       dev_dbg(dev, "ISP CCPU FW boot success\n");
+
+       return 0;
+}
+
+int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream,
+                   struct isp4fw_resp *resp)
+{
+       struct isp4if_rb_config *rb_config = &isp4if_resp_rb_config[stream];
+       u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr;
+       void *mem_sys = rb_config->base_sys_addr;
+       const u32 resp_sz = sizeof(*resp);
+       struct device *dev = ispif->dev;
+       u32 len = rb_config->val_size;
+       u32 rd_ptr, wr_ptr;
+       u32 bytes_to_end;
+       void *dst = resp;
+       u32 checksum;
+
+       rd_ptr = isp4hw_rreg(ispif->mmio, rreg);
+       wr_ptr = isp4hw_rreg(ispif->mmio, wreg);
+       if (rd_ptr >= len || wr_ptr >= len)
+               goto err_rb_invalid;
+
+       /*
+        * Read and write pointers are equal, indicating the ring buffer is
+        * empty
+        */
+       if (rd_ptr == wr_ptr)
+               return -ENODATA;
+
+       bytes_to_end = len - rd_ptr;
+       if (bytes_to_end >= resp_sz) {
+               /* FW response is just a straight copy from the read pointer */
+               if (wr_ptr > rd_ptr && wr_ptr - rd_ptr < resp_sz)
+                       goto err_rb_invalid;
+
+               memcpy(dst, mem_sys + rd_ptr, resp_sz);
+               isp4hw_wreg(ispif->mmio, rreg, (rd_ptr + resp_sz) % len);
+       } else {
+               /*
+                * FW response is split because the ring buffer wrapped
+                * around
+                */
+               if (wr_ptr > rd_ptr || wr_ptr < resp_sz - bytes_to_end)
+                       goto err_rb_invalid;
+
+               memcpy(dst, mem_sys + rd_ptr, bytes_to_end);
+               memcpy(dst + bytes_to_end, mem_sys, resp_sz - bytes_to_end);
+               isp4hw_wreg(ispif->mmio, rreg, resp_sz - bytes_to_end);
+       }
+
+       checksum = isp4if_compute_check_sum(resp, resp_sz - sizeof(u32));
+       if (checksum != resp->resp_check_sum) {
+               dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n",
+                       checksum, resp->resp_check_sum, rd_ptr, wr_ptr);
+               dev_err(dev, "(%u), seqNo %u, resp_id (0x%x)\n",
+                       stream, resp->resp_seq_num,
+                       resp->resp_id);
+               return -EINVAL;
+       }
+
+       return 0;
+
+err_rb_invalid:
+       dev_err(dev,
+               "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, resp_sz=%u\n",
+               stream, rd_ptr, wr_ptr, len, resp_sz);
+       return -EINVAL;
+}
+
+int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id,
+                       const void *package, u32 package_size)
+{
+       return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, false);
+}
+
+int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id,
+                            const void *package, u32 package_size)
+{
+       return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, true);
+}
+
+void isp4if_clear_bufq(struct isp4_interface *ispif)
+{
+       struct isp4if_img_buf_node *buf_node, *tmp_node;
+       LIST_HEAD(free_list);
+
+       scoped_guard(spinlock, &ispif->bufq_lock)
+               list_splice_init(&ispif->bufq, &free_list);
+
+       list_for_each_entry_safe(buf_node, tmp_node, &free_list, node)
+               kfree(buf_node);
+}
+
+void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node)
+{
+       kfree(buf_node);
+}
+
+struct isp4if_img_buf_node *
+isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info)
+{
+       struct isp4if_img_buf_node *node;
+
+       node = kmalloc_obj(*node, GFP_KERNEL);
+       if (node)
+               node->buf_info = *buf_info;
+
+       return node;
+}
+
+struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif)
+{
+       struct isp4if_img_buf_node *buf_node;
+
+       guard(spinlock)(&ispif->bufq_lock);
+
+       buf_node = list_first_entry_or_null(&ispif->bufq, typeof(*buf_node),
+                                           node);
+       if (buf_node)
+               list_del(&buf_node->node);
+
+       return buf_node;
+}
+
+int isp4if_queue_buffer(struct isp4_interface *ispif,
+                       struct isp4if_img_buf_node *buf_node)
+{
+       int ret;
+
+       ret = isp4if_send_buffer(ispif, &buf_node->buf_info);
+       if (ret)
+               return ret;
+
+       scoped_guard(spinlock, &ispif->bufq_lock)
+               list_add_tail(&buf_node->node, &ispif->bufq);
+
+       return 0;
+}
+
+int isp4if_stop(struct isp4_interface *ispif)
+{
+       isp4if_disable_ccpu(ispif);
+
+       isp4if_dealloc_fw_gpumem(ispif);
+
+       return 0;
+}
+
+int isp4if_start(struct isp4_interface *ispif)
+{
+       int ret;
+
+       ret = isp4if_alloc_fw_gpumem(ispif);
+       if (ret)
+               return ret;
+
+       ret = isp4if_fw_boot(ispif);
+       if (ret)
+               goto failed_fw_boot;
+
+       return 0;
+
+failed_fw_boot:
+       isp4if_dealloc_fw_gpumem(ispif);
+       return ret;
+}
+
+int isp4if_deinit(struct isp4_interface *ispif)
+{
+       isp4if_clear_cmdq(ispif);
+
+       isp4if_clear_bufq(ispif);
+
+       mutex_destroy(&ispif->isp4if_mutex);
+
+       return 0;
+}
+
+int isp4if_init(struct isp4_interface *ispif, struct device *dev,
+               void __iomem *isp_mmio)
+{
+       ispif->dev = dev;
+       ispif->mmio = isp_mmio;
+
+       spin_lock_init(&ispif->cmdq_lock); /* used for cmdq access */
+       spin_lock_init(&ispif->bufq_lock); /* used for bufq access */
+       mutex_init(&ispif->isp4if_mutex); /* used for commands sent to ispfw */
+
+       INIT_LIST_HEAD(&ispif->cmdq);
+       INIT_LIST_HEAD(&ispif->bufq);
+
+       return 0;
+}
diff --git a/drivers/media/platform/amd/isp4/isp4_interface.h 
b/drivers/media/platform/amd/isp4/isp4_interface.h
new file mode 100644
index 000000000000..ce3ac9b9e5cd
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_interface.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _ISP4_INTERFACE_H_
+#define _ISP4_INTERFACE_H_
+
+#include <drm/amd/isp.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+struct isp4fw_resp;
+
+#define ISP4IF_RB_MAX 25
+#define ISP4IF_RESP_CHAN_TO_RB_OFFSET 9
+#define ISP4IF_RB_PMBMAP_MEM_SIZE (SZ_16M - 1)
+#define ISP4IF_RB_PMBMAP_MEM_CHUNK \
+       (ISP4IF_RB_PMBMAP_MEM_SIZE / (ISP4IF_RB_MAX - 1))
+#define ISP4IF_HOST2FW_COMMAND_SIZE sizeof(struct isp4fw_cmd)
+#define ISP4IF_MAX_NUM_HOST2FW_COMMAND 40
+#define ISP4IF_FW_CMD_BUF_SIZE \
+       (ISP4IF_MAX_NUM_HOST2FW_COMMAND * ISP4IF_HOST2FW_COMMAND_SIZE)
+#define ISP4IF_RB_FULL_SLEEP_US (33 * USEC_PER_MSEC)
+#define ISP4IF_RB_FULL_TIMEOUT_US (10 * ISP4IF_RB_FULL_SLEEP_US)
+
+#define ISP4IF_META_INFO_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 
0x8000)
+#define ISP4IF_MAX_STREAM_BUF_COUNT 8
+
+#define ISP4IF_FW_LOG_RINGBUF_SIZE SZ_2M
+
+enum isp4if_stream_id {
+       ISP4IF_STREAM_ID_GLOBAL = 0,
+       ISP4IF_STREAM_ID_1 = 1,
+       ISP4IF_STREAM_ID_MAX = 4
+};
+
+enum isp4if_status {
+       ISP4IF_STATUS_PWR_OFF,
+       ISP4IF_STATUS_PWR_ON,
+       ISP4IF_STATUS_FW_RUNNING,
+       ISP4IF_FSM_STATUS_MAX
+};
+
+struct isp4if_gpu_mem_info {
+       u64 mem_size;
+       u64 gpu_mc_addr;
+       void *sys_addr;
+       void *mem_handle;
+};
+
+struct isp4if_img_buf_info {
+       struct {
+               void *sys_addr;
+               u64 mc_addr;
+               u32 len;
+       } planes[3];
+};
+
+struct isp4if_img_buf_node {
+       struct list_head node;
+       struct isp4if_img_buf_info buf_info;
+};
+
+struct isp4if_cmd_element {
+       struct list_head list;
+       u32 seq_num;
+       u32 cmd_id;
+       struct completion cmd_done;
+       atomic_t refcnt;
+};
+
+struct isp4_interface {
+       struct device *dev;
+       void __iomem *mmio;
+
+       spinlock_t cmdq_lock; /* used for cmdq access */
+       spinlock_t bufq_lock; /* used for bufq access */
+       struct mutex isp4if_mutex; /* used to send fw cmd and read fw log */
+
+       struct list_head cmdq; /* commands sent to fw */
+       struct list_head bufq; /* buffers sent to fw */
+
+       enum isp4if_status status;
+       u32 host2fw_seq_num;
+
+       /* ISP fw buffers */
+       struct isp4if_gpu_mem_info *fw_log_buf;
+       struct isp4if_gpu_mem_info *fw_cmd_resp_buf;
+       struct isp4if_gpu_mem_info *fw_mem_pool;
+       struct isp4if_gpu_mem_info *meta_info_buf[ISP4IF_MAX_STREAM_BUF_COUNT];
+};
+
+static inline void isp4if_split_addr64(u64 addr, u32 *lo, u32 *hi)
+{
+       if (lo)
+               *lo = addr & 0xffffffff;
+
+       if (hi)
+               *hi = addr >> 32;
+}
+
+static inline u64 isp4if_join_addr64(u32 lo, u32 hi)
+{
+       return (((u64)hi) << 32) | (u64)lo;
+}
+
+int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream,
+                   struct isp4fw_resp *resp);
+
+int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id,
+                       const void *package, u32 package_size);
+
+int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id,
+                            const void *package, u32 package_size);
+
+struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface 
*ispif,
+                                                  u32 seq_num, u32 cmd_id);
+
+void isp4if_clear_cmdq(struct isp4_interface *ispif);
+
+void isp4if_clear_bufq(struct isp4_interface *ispif);
+
+void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node);
+
+struct isp4if_img_buf_node *
+isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info);
+
+struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface 
*ispif);
+
+int isp4if_queue_buffer(struct isp4_interface *ispif,
+                       struct isp4if_img_buf_node *buf_node);
+
+int isp4if_stop(struct isp4_interface *ispif);
+
+int isp4if_start(struct isp4_interface *ispif);
+
+int isp4if_deinit(struct isp4_interface *ispif);
+
+int isp4if_init(struct isp4_interface *ispif, struct device *dev,
+               void __iomem *isp_mmio);
+
+#endif /* _ISP4_INTERFACE_H_ */
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to