Re: [PATCH v2 5/8] media: platform: amd: isp4 subdev and firmware loading handling added

Mario Limonciello
2025-07-06T20:56:02Z

On 6/20/2025 5:31 AM, Du, Bin wrote:
> Thanks Mario, add some comments
>
> On 6/19/2025 12:35 AM, Mario Limonciello wrote:
>> On 6/18/2025 4:19 AM, Bin Du wrote:
>>> Isp4 sub-device is implementing v4l2 sub-device interface. It has one
>>> capture video node, and supports only preview stream. It manages
>>> firmware states, stream configuration and mipi phy configuration.
>>> This change also adds interrupt handling and notification for isp
>>> firmware to isp-subdevice.
>>>
>>> Signed-off-by: Bin Du <Bin.Du@amd.com>
>>> Signed-off-by: Svetoslav Stoilov <Svetoslav.Stoilov@amd.com>
>>> ---
>>>   drivers/media/platform/amd/isp4/Makefile      |    3 +-
>>>   drivers/media/platform/amd/isp4/isp4.c        |  267 +++-
>>>   drivers/media/platform/amd/isp4/isp4.h        |    4 +-
>>>   drivers/media/platform/amd/isp4/isp4_subdev.c | 1192 +++++++++++++++++
>>>   drivers/media/platform/amd/isp4/isp4_subdev.h |  145 ++
>>>   5 files changed, 1601 insertions(+), 10 deletions(-)
>>>   create mode 100644 drivers/media/platform/amd/isp4/isp4_subdev.c
>>>   create mode 100644 drivers/media/platform/amd/isp4/isp4_subdev.h
>>>
>>> diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/
>>> media/ platform/amd/isp4/Makefile
>>> index c0166f954516..52defc06189e 100644
>>> --- a/drivers/media/platform/amd/isp4/Makefile
>>> +++ b/drivers/media/platform/amd/isp4/Makefile
>>> @@ -3,9 +3,10 @@
>>>   # Copyright (C) 2025 Advanced Micro Devices, Inc.
>>>   obj-$(CONFIG_AMD_ISP4) += amd_capture.o
>>> -amd_capture-objs := isp4.o    \
>>> +amd_capture-objs := isp4_subdev.o \
>>>               isp4_phy.o \
>>>               isp4_interface.o \
>>> +            isp4.o    \
>>>               isp4_hw.o    \
>>>   ccflags-y += -I$(srctree)/drivers/media/platform/amd/isp4
>>> diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/
>>> platform/amd/isp4/isp4.c
>>> index d0be90c5ec3b..c1aca2bd35e3 100644
>>> --- a/drivers/media/platform/amd/isp4/isp4.c
>>> +++ b/drivers/media/platform/amd/isp4/isp4.c
>>> @@ -5,13 +5,20 @@
>>>   #include <linux/pm_runtime.h>
>>>   #include <linux/vmalloc.h>
>>> +#include <media/v4l2-fwnode.h>
>>>   #include <media/v4l2-ioctl.h>
>>> -#include "isp4.h"
>>> +#include "amdgpu_object.h"
>>> -#define VIDEO_BUF_NUM 5
>>> +#include "isp4.h"
>>> +#include "isp4_hw.h"
>>>   #define ISP4_DRV_NAME "amd_isp_capture"
>>> +#define ISP4_FW_RESP_RB_IRQ_STATUS_MASK \
>>> +    (ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK  | \
>>> +     ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT10_INT_MASK | \
>>> +     ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT11_INT_MASK | \
>>> +     ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK)
>>>   /* interrupt num */
>>>   static const u32 isp4_ringbuf_interrupt_num[] = {
>>> @@ -24,23 +31,233 @@ static const u32 isp4_ringbuf_interrupt_num[] = {
>>>   #define to_isp4_device(dev) \
>>>       ((struct isp4_device *)container_of(dev, struct isp4_device,
>>> v4l2_dev))
>>> +static int isp4_create_links(struct isp4_device *isp4_dev,
>>> +                 struct v4l2_subdev *sensor_sdev)
>>> +{
>>> +    struct v4l2_subdev *isp4_sdev = &isp4_dev->isp_sdev.sdev;
>>> +    struct device *dev = &isp4_dev->pdev->dev;
>>> +    int ret;
>>> +
>>> +    ret = media_create_pad_link(&sensor_sdev->entity,
>>> +                    0, &isp4_sdev->entity, 0,
>>> +                    MEDIA_LNK_FL_ENABLED |
>>> +                    MEDIA_LNK_FL_IMMUTABLE);
>>> +    if (ret)
>>> +        dev_err(dev, "create sensor to isp link fail:%d\n", ret);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int isp4_register_subdev_and_create_links(struct isp4_device
>>> *isp_dev,
>>> +                         struct v4l2_subdev *sdev)
>>> +{
>>> +    struct device *dev = &isp_dev->pdev->dev;
>>> +    int ret;
>>> +
>>> +    ret = isp4_create_links(isp_dev, sdev);
>>> +    if (ret)
>>> +        dev_err(dev, "fail create isp link:%d\n", ret);
>>
>> You're discarding ret here.  Shouldn't you return ret in the failure
>> path?
> Even if it fails, some Apps which doesn't depend on the media link can
> still work, so just print error message and ignore it so at least these
> Apps can still work, do you think it acceptible?>

I think it should be a warn or an info if it's an acceptable situation
along with a comment to help us years later understand why the error
handling is that way.

>>> +
>>> +    ret = v4l2_device_register_subdev_nodes(&isp_dev->v4l2_dev);
>>> +    if (ret != 0) {
>>> +        dev_warn(dev, "register subdev as nodes fail:%d\n", ret);
>>> +        ret = 0;
>>
>> This isn't fatal?
>>
> This call is just expose subdev node to user space, even if it fails,
> some Apps can still work, so just print error message and ignore it so
> at least these Apps can still work, do you think it acceptible?>> +    }

I guess same comment as above.

>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int isp4_camera_sensor_bound(struct v4l2_async_notifier
>>> *notifier,
>>> +                    struct v4l2_subdev *sensor_sdev,
>>> +                    struct v4l2_async_connection *asd)
>>> +{
>>> +    struct isp4_device *isp_dev = to_isp4_device(notifier->v4l2_dev);
>>> +    struct device *dev = &isp_dev->pdev->dev;
>>> +    int ret;
>>> +
>>> +    ret = isp4_register_subdev_and_create_links(isp_dev, sensor_sdev);
>>> +    if (ret)
>>> +        dev_err(dev, "register sensor subdev fail:%d\n",
>>> +            ret);
>>> +    else
>>> +        dev_dbg(dev, "register sensor subdev suc\n");
>>
>> Probably can drop this now
>>
> I'd prefer to keeping this for quickly identifying possible camera open
> issue, do you think it acceptible?

Sure.

>
>>> +    return ret;
>>> +}
>>> +
>>> +static void isp4_camera_sensor_unbind(struct v4l2_async_notifier
>>> *notifier,
>>> +                      struct v4l2_subdev *sensor_sdev,
>>> +                      struct v4l2_async_connection *asd)
>>> +{
>>> +}
>>> +
>>> +static const struct v4l2_async_notifier_operations
>>> isp4_camera_sensor_ops = {
>>> +    .bound = isp4_camera_sensor_bound,
>>> +    .unbind = isp4_camera_sensor_unbind,
>>> +};
>>> +
>>> +static void isp4_wake_up_resp_thread(struct isp4_subdev *isp, u32
>>> index)
>>> +{
>>> +    if (isp && index < ISP4SD_MAX_FW_RESP_STREAM_NUM) {
>>> +        struct isp4sd_thread_handler *thread_ctx =
>>> +                &isp->fw_resp_thread[index];
>>> +
>>> +        thread_ctx->wq_cond = 1;
>>> +        wake_up_interruptible(&thread_ctx->waitq);
>>> +    }
>>> +}
>>> +
>>> +static void isp4_resp_interrupt_notify(struct isp4_subdev *isp, u32
>>> intr_status)
>>> +{
>>> +    bool wake = (isp->ispif.status == ISP4IF_STATUS_FW_RUNNING);
>>> +
>>> +    u32 intr_ack = 0;
>>> +
>>> +    /* global response */
>>> +    if (intr_status &
>>> +        ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK) {
>>> +        if (wake)
>>> +            isp4_wake_up_resp_thread(isp, 0);
>>> +
>>> +        intr_ack |=
>>> ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK;
>>> +    }
>>> +
>>> +    /* stream 1 response */
>>> +    if (intr_status &
>>> +        ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK) {
>>> +        if (wake)
>>> +            isp4_wake_up_resp_thread(isp, 1);
>>> +
>>> +        intr_ack |= ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK;
>>> +    }
>>> +
>>> +    /* stream 2 response */
>>> +    if (intr_status &
>>> +        ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT10_INT_MASK) {
>>> +        if (wake)
>>> +            isp4_wake_up_resp_thread(isp, 2);
>>> +
>>> +        intr_ack |=
>>> ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT10_ACK_MASK;
>>> +    }
>>> +
>>> +    /* stream 3 response */
>>> +    if (intr_status &
>>> +        ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT11_INT_MASK) {
>>> +        if (wake)
>>> +            isp4_wake_up_resp_thread(isp, 3);
>>> +
>>> +        intr_ack |=
>>> ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT11_ACK_MASK;
>>> +    }
>>> +
>>> +    /* clear ISP_SYS interrupts */
>>> +    isp4hw_wreg(ISP4_GET_ISP_REG_BASE(isp), ISP_SYS_INT0_ACK,
>>> intr_ack);
>>> +}
>>> +
>>>   static irqreturn_t isp4_irq_handler(int irq, void *arg)
>>>   {
>>> -    struct isp4_device *isp_dev = dev_get_drvdata((struct device
>>> *)arg);
>>> +    struct isp4_device *isp_dev = dev_get_drvdata(arg);
>>> +    struct isp4_subdev *isp = NULL;
>>> +    u32 isp_sys_irq_status = 0x0;
>>> +    u32 r1;
>>>       if (!isp_dev)
>>> -        goto error_drv_data;
>>> +        return IRQ_HANDLED;
>>
>> As mentioned in the first patch, avoid ping ponging the code.
>>
> Thanks, will remove the code ping ponging in next patch>> +
>>> +    isp = &isp_dev->isp_sdev;
>>> +    /* check ISP_SYS interrupts status */
>>> +    r1 = isp4hw_rreg(ISP4_GET_ISP_REG_BASE(isp), ISP_SYS_INT0_STATUS);
>>> +
>>> +    isp_sys_irq_status = r1 & ISP4_FW_RESP_RB_IRQ_STATUS_MASK;
>>> +
>>> +    isp4_resp_interrupt_notify(isp, isp_sys_irq_status);
>>> -error_drv_data:
>> As mentioned in the first patch, avoid ping ponging the code.
> Thanks, will remove the code ping ponging in next patch>>       return
> IRQ_HANDLED;
>>>   }
>>> +static int isp4_parse_fwnode_init_async_nf(struct isp4_device *isp_dev)
>>> +{
>>> +    struct isp4_subdev *isp_sdev = &isp_dev->isp_sdev;
>>> +    struct device *dev = &isp_dev->pdev->dev;
>>> +    struct v4l2_fwnode_endpoint bus_cfg = {
>>> +        .bus_type = V4L2_MBUS_CSI2_DPHY
>>> +    };
>>> +    struct fwnode_handle *remote_ep = NULL;
>>> +    struct fwnode_handle *local_ep = NULL;
>>> +    struct v4l2_async_connection *asd;
>>> +    struct fwnode_handle *fwnode;
>>> +    struct fwnode_endpoint fwep;
>>> +    int ret;
>>> +
>>> +    fwnode = dev_fwnode(dev);
>>> +
>>> +    local_ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
>>> +    if (!local_ep) {
>>> +        ret = -ENXIO;
>>> +        goto err_fwnode;
>>> +    }
>>> +
>>> +    remote_ep = fwnode_graph_get_remote_endpoint(local_ep);
>>> +    if (!remote_ep) {
>>> +        ret = -ENXIO;
>>> +        goto err_fwnode;
>>> +    }
>>> +
>>> +    ret = fwnode_graph_parse_endpoint(remote_ep, &fwep);
>>> +    if (ret)
>>> +        goto err_fwnode;
>>> +    isp_sdev->phy_id = fwep.port;
>>> +
>>> +    ret = v4l2_fwnode_endpoint_alloc_parse(remote_ep, &bus_cfg);
>>> +    if (ret)
>>> +        goto err_fwnode;
>>> +
>>> +    if (!bus_cfg.nr_of_link_frequencies) {
>>> +        ret = -EINVAL;
>>> +        dev_err(dev, "fail invalid link freq number %u\n",
>>> bus_cfg.nr_of_link_frequencies);
>>> +        v4l2_fwnode_endpoint_free(&bus_cfg);
>>> +        goto err_fwnode;
>>> +    }
>>> +    isp_sdev->phy_link_freq = *bus_cfg.link_frequencies;
>>> +    v4l2_fwnode_endpoint_free(&bus_cfg);
>>> +
>>> +    isp_sdev->phy_num_data_lanes =
>>> +        fwnode_property_count_u32(remote_ep, "data-lanes");
>>> +
>>> +    v4l2_async_nf_init(&isp_dev->notifier, &isp_dev->v4l2_dev);
>>> +
>>> +    asd = v4l2_async_nf_add_fwnode(&isp_dev->notifier, remote_ep,
>>> +                       struct v4l2_async_connection);
>>> +    if (IS_ERR(asd)) {
>>> +        ret = PTR_ERR(asd);
>>> +        goto err_async_nf_cleanup;
>>> +    }
>>> +
>>> +    isp_dev->notifier.ops = &isp4_camera_sensor_ops;
>>> +    ret = v4l2_async_nf_register(&isp_dev->notifier);
>>> +    if (ret) {
>>> +        dev_err(dev, "v4l2_async_nf_register fail:%d", ret);
>>> +        goto err_async_nf_cleanup;
>>> +    }
>>> +
>>> +    return 0;
>>> +
>>> +err_async_nf_cleanup:
>>> +    v4l2_async_nf_cleanup(&isp_dev->notifier);
>>> +err_fwnode:
>>> +    if (remote_ep)
>>> +        fwnode_handle_put(remote_ep);
>>> +    if (local_ep)
>>> +        fwnode_handle_put(remote_ep);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>>   /*
>>>    * amd capture module
>>>    */
>>>   static int isp4_capture_probe(struct platform_device *pdev)
>>>   {
>>>       struct device *dev = &pdev->dev;
>>> +    struct isp4_subdev *isp_sdev;
>>>       struct isp4_device *isp_dev;
>>>       int i, irq, ret;
>>> @@ -52,6 +269,17 @@ static int isp4_capture_probe(struct
>>> platform_device *pdev)
>>>       isp_dev->pdev = pdev;
>>>       dev->init_name = ISP4_DRV_NAME;
>>> +    isp_sdev = &isp_dev->isp_sdev;
>>> +    isp_sdev->mmio = devm_platform_ioremap_resource(pdev, 0);
>>> +    if (IS_ERR(isp_sdev->mmio))
>>> +        return dev_err_probe(dev, PTR_ERR(isp_sdev->mmio),
>>> +                     "isp ioremap fail\n");
>>> +
>>> +    isp_sdev->isp_phy_mmio = devm_platform_ioremap_resource(pdev, 1);
>>> +    if (IS_ERR(isp_sdev->isp_phy_mmio))
>>> +        return dev_err_probe(dev, PTR_ERR(isp_sdev->isp_phy_mmio),
>>> +                     "isp phy mmio ioremap fail\n");
>>> +
>>>       for (i = 0; i < ARRAY_SIZE(isp4_ringbuf_interrupt_num); i++) {
>>>           irq = platform_get_irq(pdev, isp4_ringbuf_interrupt_num[i]);
>>>           if (irq < 0)
>>> @@ -90,10 +318,23 @@ static int isp4_capture_probe(struct
>>> platform_device *pdev)
>>>       dev_dbg(dev, "AMD ISP v4l2 device registered\n");
>>> +    ret = isp4sd_init(&isp_dev->isp_sdev, &isp_dev->v4l2_dev,
>>> +              isp_dev->pltf_data->adev);
>>> +    if (ret) {
>>> +        dev_err(dev, "fail init isp4 sub dev %d\n", ret);
>>> +        goto err_unreg_v4l2;
>>> +    }
>>> +
>>> +    ret = isp4_parse_fwnode_init_async_nf(isp_dev);
>>> +    if (ret) {
>>> +        dev_err(dev, "fail to parse fwnode %d\n", ret);
>>> +        goto err_unreg_v4l2;
>>> +    }
>>> +
>>>       ret = media_device_register(&isp_dev->mdev);
>>>       if (ret) {
>>>           dev_err(dev, "fail to register media device %d\n", ret);
>>> -        goto err_unreg_v4l2;
>>> +        goto err_isp4_deinit;
>>>       }
>>>       platform_set_drvdata(pdev, isp_dev);
>>> @@ -103,6 +344,10 @@ static int isp4_capture_probe(struct
>>> platform_device *pdev)
>>>       return 0;
>>> +err_isp4_deinit:
>>> +    v4l2_async_nf_unregister(&isp_dev->notifier);
>>> +    v4l2_async_nf_cleanup(&isp_dev->notifier);
>>> +    isp4sd_deinit(&isp_dev->isp_sdev);
>>>   err_unreg_v4l2:
>>>       v4l2_device_unregister(&isp_dev->v4l2_dev);
>>> @@ -112,11 +357,17 @@ static int isp4_capture_probe(struct
>>> platform_device *pdev)
>>>   static void isp4_capture_remove(struct platform_device *pdev)
>>>   {
>>>       struct isp4_device *isp_dev = platform_get_drvdata(pdev);
>>> -    struct device *dev = &pdev->dev;
>>> +
>>> +    v4l2_async_nf_unregister(&isp_dev->notifier);
>>> +    v4l2_async_nf_cleanup(&isp_dev->notifier);
>>> +    v4l2_device_unregister_subdev(&isp_dev->isp_sdev.sdev);
>>>       media_device_unregister(&isp_dev->mdev);
>>> +    media_entity_cleanup(&isp_dev->isp_sdev.sdev.entity);
>>>       v4l2_device_unregister(&isp_dev->v4l2_dev);
>>> -    dev_dbg(dev, "AMD ISP v4l2 device unregistered\n");
>>> +    dev_dbg(&pdev->dev, "AMD ISP v4l2 device unregistered\n");
>>
>> Don't ping pong this
>>
> This log will be removed in next patch as you suggested in other comment
>>> +
>>> +    isp4sd_deinit(&isp_dev->isp_sdev);
>>>   }
>>>   static struct platform_driver isp4_capture_drv = {
>>> diff --git a/drivers/media/platform/amd/isp4/isp4.h b/drivers/media/
>>> platform/amd/isp4/isp4.h
>>> index 27a7362ce6f9..596431b4a5c2 100644
>>> --- a/drivers/media/platform/amd/isp4/isp4.h
>>> +++ b/drivers/media/platform/amd/isp4/isp4.h
>>> @@ -7,9 +7,9 @@
>>>   #define _ISP4_H_
>>>   #include <linux/mutex.h>
>>> -#include <media/v4l2-device.h>
>>>   #include <media/videobuf2-memops.h>
>>>   #include <media/videobuf2-vmalloc.h>
>>> +#include "isp4_subdev.h"
>>>   #define ISP4_GET_ISP_REG_BASE(isp4sd) (((isp4sd))->mmio)
>>> @@ -25,11 +25,13 @@ struct isp4_platform_data {
>>>   struct isp4_device {
>>>       struct v4l2_device v4l2_dev;
>>> +    struct isp4_subdev isp_sdev;
>>>       struct media_device mdev;
>>>       struct isp4_platform_data *pltf_data;
>>>       struct platform_device *pdev;
>>>       struct notifier_block i2c_nb;
>>> +    struct v4l2_async_notifier notifier;
>>>   };
>>>   #endif /* isp4.h */
>>> diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/
>>> media/platform/amd/isp4/isp4_subdev.c
>>> new file mode 100644
>>> index 000000000000..978164031067
>>> --- /dev/null
>>> +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c
>>> @@ -0,0 +1,1192 @@
>>> +// SPDX-License-Identifier: GPL-2.0+
>>> +/*
>>> + * Copyright (C) 2025 Advanced Micro Devices, Inc.
>>> + */
>>> +
>>> +#include <linux/mutex.h>
>>> +#include <linux/pm_domain.h>
>>> +#include <linux/pm_runtime.h>
>>> +
>>> +#include "isp4_fw_cmd_resp.h"
>>> +#include "isp4_hw.h"
>>> +#include "isp4_interface.h"
>>> +#include "isp4_phy.h"
>>> +#include "isp4_subdev.h"
>>> +#include <linux/units.h>
>>> +
>>> +#define ISP4SD_MAX_CMD_RESP_BUF_SIZE (4 * 1024)
>>> +#define ISP4SD_MIN_BUF_CNT_BEF_START_STREAM 4
>>> +
>>> +#define ISP4SD_PERFORMANCE_STATE_LOW 0
>>> +#define ISP4SD_PERFORMANCE_STATE_HIGH 1
>>> +
>>> +#define ISP4SD_FW_CMD_TIMEOUT_IN_MS  500
>>> +#define ISP4SD_WAIT_RESP_IRQ_TIMEOUT  5 /* ms */
>>> +/* align 32KB */
>>> +#define ISP4SD_META_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info),
>>> 0x8000)
>>> +
>>> +#define to_isp4_subdev(v4l2_sdev)  \
>>> +    container_of(v4l2_sdev, struct isp4_subdev, sdev)
>>> +
>>> +#define to_isp4_vdev(isp4_vid)  \
>>> +    container_of(isp4_vid, struct isp4_subdev, isp_vdev)
>>> +
>>> +static const char *isp4sd_entity_name = "amd isp4";
>>> +
>>> +struct isp4sd_mbus_image_format_remap {
>>> +    u32                mbus_code;
>>> +    enum isp4fw_image_format    image_format;
>>> +};
>>> +
>>> +static const struct isp4sd_mbus_image_format_remap
>>> +    isp4sd_image_formats[] = {
>>> +    {
>>> +        .mbus_code    = MEDIA_BUS_FMT_YUYV8_1_5X8,
>>> +        .image_format    = IMAGE_FORMAT_NV12,
>>> +    },
>>> +    {
>>> +        .mbus_code    = MEDIA_BUS_FMT_YUYV8_1X16,
>>> +        .image_format    = IMAGE_FORMAT_YUV422INTERLEAVED,
>>> +    },
>>> +};
>>> +
>>> +static void isp4sd_module_enable(struct isp4_subdev *isp_subdev,
>>> bool enable)
>>> +{
>>> +    if (isp_subdev->enable_gpio) {
>>> +        gpiod_set_value(isp_subdev->enable_gpio, enable ? 1 : 0);
>>> +        dev_dbg(isp_subdev->dev, "%s isp_subdev module\n",
>>> +            enable ? "enable" : "disable");
>>> +    }
>>> +}
>>> +
>>> +static int isp4sd_setup_fw_mem_pool(struct isp4_subdev *isp_subdev)
>>> +{
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct isp4fw_cmd_send_buffer buf_type;
>>> +    struct device *dev = isp_subdev->dev;
>>> +    int ret;
>>> +
>>> +    if (!ispif->fw_mem_pool) {
>>> +        dev_err(dev, "fail to alloc mem pool\n");
>>> +        return -ENOMEM;
>>> +    }
>>> +
>>> +    memset(&buf_type, 0, sizeof(buf_type));
>>> +    buf_type.buffer_type = BUFFER_TYPE_MEM_POOL;
>>> +    buf_type.buffer.buf_tags = 0;
>>> +    buf_type.buffer.vmid_space.bit.vmid = 0;
>>> +    buf_type.buffer.vmid_space.bit.space = ADDR_SPACE_TYPE_GPU_VA;
>>> +    isp4if_split_addr64(ispif->fw_mem_pool->gpu_mc_addr,
>>> +                &buf_type.buffer.buf_base_a_lo,
>>> +                &buf_type.buffer.buf_base_a_hi);
>>> +    buf_type.buffer.buf_size_a = (u32)ispif->fw_mem_pool->mem_size;
>>> +
>>> +    ret = isp4if_send_command(ispif, CMD_ID_SEND_BUFFER,
>>> +                  &buf_type, sizeof(buf_type));
>>> +    if (ret) {
>>> +        dev_err(dev, "send fw mem pool 0x%llx(%u) fail %d\n",
>>> +            ispif->fw_mem_pool->gpu_mc_addr,
>>> +            buf_type.buffer.buf_size_a,
>>> +            ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    dev_dbg(dev, "send fw mem pool 0x%llx(%u) suc\n",
>>> +        ispif->fw_mem_pool->gpu_mc_addr,
>>> +        buf_type.buffer.buf_size_a);
>>> +
>>> +    return 0;
>>> +};
>>> +
>>> +static int isp4sd_set_stream_path(struct isp4_subdev *isp_subdev)
>>> +{
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct isp4fw_cmd_set_stream_cfg cmd = {0};
>>> +    struct device *dev = isp_subdev->dev;
>>> +
>>> +    cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id =
>>> SENSOR_ID_ON_MIPI0;
>>> +    cmd.stream_cfg.mipi_pipe_path_cfg.b_enable = true;
>>> +    cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id =
>>> MIPI0_ISP_PIPELINE_ID;
>>> +
>>> +    cmd.stream_cfg.b_enable_tnr = true;
>>> +    dev_dbg(dev, "isp4fw_sensor_id %d, pipeId 0x%x EnableTnr %u\n",
>>> +        cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id,
>>> +        cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id,
>>> +        cmd.stream_cfg.b_enable_tnr);
>>> +
>>> +    return isp4if_send_command(ispif, CMD_ID_SET_STREAM_CONFIG,
>>> +                   &cmd, sizeof(cmd));
>>> +}
>>> +
>>> +static int isp4sd_send_meta_buf(struct isp4_subdev *isp_subdev)
>>> +{
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct isp4fw_cmd_send_buffer buf_type = { 0 };
>>> +    struct isp4sd_sensor_info *sensor_info;
>>> +    struct device *dev = isp_subdev->dev;
>>> +    u32 i;
>>> +
>>> +    sensor_info = &isp_subdev->sensor_info;
>>> +    for (i = 0; i < ISP4IF_MAX_STREAM_META_BUF_COUNT; i++) {
>>> +        int ret;
>>> +
>>> +        if (!sensor_info->meta_info_buf[i]) {
>>> +            dev_err(dev, "fail for no meta info buf(%u)\n", i);
>>> +            return -ENOMEM;
>>> +        }
>>> +        buf_type.buffer_type = BUFFER_TYPE_META_INFO;
>>> +        buf_type.buffer.buf_tags = 0;
>>> +        buf_type.buffer.vmid_space.bit.vmid = 0;
>>> +        buf_type.buffer.vmid_space.bit.space = ADDR_SPACE_TYPE_GPU_VA;
>>> +        isp4if_split_addr64(sensor_info->meta_info_buf[i]->gpu_mc_addr,
>>> +                    &buf_type.buffer.buf_base_a_lo,
>>> +                    &buf_type.buffer.buf_base_a_hi);
>>> +        buf_type.buffer.buf_size_a =
>>> +            (u32)sensor_info->meta_info_buf[i]->mem_size;
>>> +        ret = isp4if_send_command(ispif, CMD_ID_SEND_BUFFER,
>>> +                      &buf_type,
>>> +                      sizeof(buf_type));
>>> +        if (ret) {
>>> +            dev_err(dev, "send meta info(%u) fail\n", i);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>> +    dev_dbg(dev, "send meta info suc\n");
>>> +    return 0;
>>> +}
>>> +
>>> +static int isp4sd_enum_mbus_code(struct v4l2_subdev *isp_subdev,
>>> +                 struct v4l2_subdev_state *state,
>>> +                 struct v4l2_subdev_mbus_code_enum *code_enum)
>>> +{
>>> +    if (code_enum->index >= ARRAY_SIZE(isp4sd_image_formats))
>>> +        return -EINVAL;
>>> +
>>> +    code_enum->code = isp4sd_image_formats[code_enum->index].mbus_code;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static bool isp4sd_get_str_out_prop(struct isp4_subdev *isp_subdev,
>>> +                    struct isp4fw_image_prop *out_prop,
>>> +                    struct v4l2_subdev_state *state, u32 pad)
>>> +{
>>> +    struct v4l2_mbus_framefmt *format = NULL;
>>> +    struct device *dev = isp_subdev->dev;
>>> +    bool ret;
>>> +
>>> +    format = v4l2_subdev_state_get_format(state, pad, 0);
>>> +    if (!format) {
>>> +        dev_err(dev, "fail get subdev state format\n");
>>> +        return false;
>>> +    }
>>> +
>>> +    switch (format->code) {
>>> +    case MEDIA_BUS_FMT_YUYV8_1_5X8:
>>> +        out_prop->image_format = IMAGE_FORMAT_NV12;
>>> +        out_prop->width = format->width;
>>> +        out_prop->height = format->height;
>>> +        out_prop->luma_pitch = format->width;
>>> +        out_prop->chroma_pitch = out_prop->width;
>>> +        ret = true;
>>> +        break;
>>> +    case MEDIA_BUS_FMT_YUYV8_1X16:
>>> +        out_prop->image_format = IMAGE_FORMAT_YUV422INTERLEAVED;
>>> +        out_prop->width = format->width;
>>> +        out_prop->height = format->height;
>>> +        out_prop->luma_pitch = format->width * 2;
>>> +        out_prop->chroma_pitch = 0;
>>> +        ret = true;
>>> +        break;
>>> +    default:
>>> +        dev_err(dev, "fail for bad image format:0x%x\n",
>>> +            format->code);
>>> +        ret = false;
>>> +        break;
>>> +    }
>>> +
>>> +    if (!out_prop->width || !out_prop->height)
>>> +        ret = false;
>>> +    return ret;
>>> +}
>>> +
>>> +static int isp4sd_kickoff_stream(struct isp4_subdev *isp_subdev, u32
>>> w, u32 h)
>>> +{
>>> +    struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct device *dev = isp_subdev->dev;
>>> +
>>> +    if (sensor_info->status == ISP4SD_START_STATUS_STARTED) {
>>> +        return 0;
>>> +    } else if (sensor_info->status == ISP4SD_START_STATUS_START_FAIL) {
>>> +        dev_err(dev, "fail for previous start fail\n");
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    dev_dbg(dev, "w:%u,h:%u\n", w, h);
>>> +
>>> +    sensor_info->status = ISP4SD_START_STATUS_START_FAIL;
>>> +
>>> +    if (isp4sd_send_meta_buf(isp_subdev)) {
>>> +        dev_err(dev, "fail to send meta buf\n");
>>> +        return -EINVAL;
>>> +    };
>>> +
>>> +    sensor_info->status = ISP4SD_START_STATUS_NOT_START;
>>> +
>>> +    if (!sensor_info->start_stream_cmd_sent &&
>>> +        sensor_info->buf_sent_cnt >=
>>> +        ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) {
>>> +        int ret = isp4if_send_command(ispif, CMD_ID_START_STREAM,
>>> +                          NULL, 0);
>>> +        if (ret) {
>>> +            dev_err(dev, "fail to start stream\n");
>>> +            return ret;
>>> +        }
>>> +
>>> +        sensor_info->start_stream_cmd_sent = true;
>>> +    } else {
>>> +        dev_dbg(dev,
>>> +            "no send START_STREAM, start_sent %u, buf_sent %u\n",
>>> +            sensor_info->start_stream_cmd_sent,
>>> +            sensor_info->buf_sent_cnt);
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int isp4sd_setup_output(struct isp4_subdev *isp_subdev,
>>> +                   struct v4l2_subdev_state *state, u32 pad)
>>> +{
>>> +    struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
>>> +    struct isp4sd_output_info *output_info =
>>> +            &isp_subdev->sensor_info.output_info;
>>> +    struct isp4fw_cmd_set_out_ch_prop cmd_ch_prop = {0};
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct isp4fw_cmd_enable_out_ch cmd_ch_en = {0};
>>> +    struct device *dev = isp_subdev->dev;
>>> +    struct isp4fw_image_prop *out_prop;
>>> +    int ret;
>>> +
>>> +    if (output_info->start_status == ISP4SD_START_STATUS_STARTED)
>>> +        return 0;
>>> +
>>> +    if (output_info->start_status == ISP4SD_START_STATUS_START_FAIL) {
>>> +        dev_err(dev, "fail for previous start fail\n");
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    out_prop = &cmd_ch_prop.image_prop;
>>> +    cmd_ch_prop.ch = ISP_PIPE_OUT_CH_PREVIEW;
>>> +    cmd_ch_en.ch = ISP_PIPE_OUT_CH_PREVIEW;
>>> +    cmd_ch_en.is_enable = true;
>>> +
>>> +    if (!isp4sd_get_str_out_prop(isp_subdev, out_prop, state, pad)) {
>>> +        dev_err(dev, "fail to get out prop\n");
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    dev_dbg(dev, "channel: w:h=%u:%u,lp:%u,cp%u\n",
>>> +        cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height,
>>> +        cmd_ch_prop.image_prop.luma_pitch,
>>> +        cmd_ch_prop.image_prop.chroma_pitch);
>>> +
>>> +    ret = isp4if_send_command(ispif, CMD_ID_SET_OUT_CHAN_PROP,
>>> +                  &cmd_ch_prop,
>>> +                  sizeof(cmd_ch_prop));
>>> +    if (ret) {
>>> +        output_info->start_status = ISP4SD_START_STATUS_START_FAIL;
>>> +        dev_err(dev, "fail to set out prop\n");
>>> +        return ret;
>>> +    };
>>> +
>>> +    ret = isp4if_send_command(ispif, CMD_ID_ENABLE_OUT_CHAN,
>>> +                  &cmd_ch_en, sizeof(cmd_ch_en));
>>> +
>>> +    if (ret) {
>>> +        output_info->start_status = ISP4SD_START_STATUS_START_FAIL;
>>> +        dev_err(dev, "fail to enable channel\n");
>>> +        return ret;
>>> +    }
>>> +
>>> +    if (!sensor_info->start_stream_cmd_sent) {
>>> +        ret = isp4sd_kickoff_stream(isp_subdev, out_prop->width,
>>> +                        out_prop->height);
>>> +        if (ret) {
>>> +            dev_err(dev, "kickoff stream fail %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +        /* sensor_info->start_stream_cmd_sent will be set to true
>>> +         * 1. in isp4sd_kickoff_stream, if app first send buffer then
>>> +         * start stream
>>> +         * 2. in isp_set_stream_buf, if app first start stream, then
>>> +         * send buffer
>>> +         * because ISP FW has the requirement, host needs to send
>>> buffer
>>> +         * before send start stream cmd
>>> +         */
>>> +        if (sensor_info->start_stream_cmd_sent) {
>>> +            sensor_info->status = ISP4SD_START_STATUS_STARTED;
>>> +            output_info->start_status = ISP4SD_START_STATUS_STARTED;
>>> +            dev_dbg(dev, "kickoff stream suc,start cmd sent\n");
>>> +        } else {
>>> +            dev_dbg(dev, "kickoff stream suc,start cmd not sent\n");
>>> +        }
>>> +    } else {
>>> +        dev_dbg(dev, "stream running, no need kickoff\n");
>>> +        output_info->start_status = ISP4SD_START_STATUS_STARTED;
>>> +    }
>>> +
>>> +    dev_dbg(dev, "setup output suc\n");
>>> +    return 0;
>>> +}
>>> +
>>> +static int isp4sd_alloc_meta_buf(struct isp4_subdev *isp_subdev)
>>> +{
>>> +    struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct device *dev = isp_subdev->dev;
>>> +    u32 i;
>>> +
>>> +    /* TODO: check alloc method */
>>> +    for (i = 0; i < ISP4IF_MAX_STREAM_META_BUF_COUNT; i++) {
>>> +        if (!sensor_info->meta_info_buf[i]) {
>>> +            sensor_info->meta_info_buf[i] =
>>> +                ispif->metainfo_buf_pool[i];
>>> +            if (sensor_info->meta_info_buf[i]) {
>>> +                dev_dbg(dev, "valid %u meta_info_buf ok\n", i);
>>> +            } else {
>>> +                dev_err(dev,
>>> +                    "invalid %u meta_info_buf fail\n", i);
>>> +                return -ENOMEM;
>>> +            }
>>> +        }
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int isp4sd_init_stream(struct isp4_subdev *isp_subdev)
>>> +{
>>> +    struct device *dev = isp_subdev->dev;
>>> +    int ret;
>>> +
>>> +    ret  = isp4sd_setup_fw_mem_pool(isp_subdev);
>>> +    if (ret) {
>>> +        dev_err(dev, "fail to  setup fw mem pool\n");
>>> +        return ret;
>>> +    }
>>> +
>>> +    ret  = isp4sd_alloc_meta_buf(isp_subdev);
>>> +    if (ret) {
>>> +        dev_err(dev, "fail to alloc fw driver shared buf\n");
>>> +        return ret;
>>> +    }
>>> +
>>> +    ret = isp4sd_set_stream_path(isp_subdev);
>>> +    if (ret) {
>>> +        dev_err(dev, "fail to setup stream path\n");
>>> +        return ret;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static void isp4sd_reset_stream_info(struct isp4_subdev *isp_subdev,
>>> +                     struct v4l2_subdev_state *state, u32 pad)
>>> +{
>>> +    struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
>>> +    struct v4l2_mbus_framefmt *format = NULL;
>>> +    struct isp4sd_output_info *str_info;
>>> +    int i;
>>> +
>>> +    format = v4l2_subdev_state_get_format(state,
>>> +                          pad,
>>> +                          0);
>>> +
>>> +    if (!format) {
>>> +        pr_err("fail reset stream info for not get format\n");
>>> +
>>> +    } else {
>>> +        memset(format, 0, sizeof(*format));
>>> +        format->code = MEDIA_BUS_FMT_YUYV8_1_5X8;
>>> +    }
>>> +
>>> +    for (i = 0; i < ISP4IF_MAX_STREAM_META_BUF_COUNT; i++)
>>> +        sensor_info->meta_info_buf[i] = NULL;
>>> +
>>> +    str_info = &sensor_info->output_info;
>>> +    str_info->start_status = ISP4SD_START_STATUS_NOT_START;
>>> +}
>>> +
>>> +static bool isp4sd_is_stream_running(struct isp4_subdev *isp_subdev)
>>> +{
>>> +    struct isp4sd_sensor_info *sif;
>>> +    enum isp4sd_start_status stat;
>>> +
>>> +    sif = &isp_subdev->sensor_info;
>>> +    stat = sif->output_info.start_status;
>>> +    if (stat == ISP4SD_START_STATUS_STARTED)
>>> +        return true;
>>> +
>>> +    return false;
>>> +}
>>> +
>>> +static void isp4sd_reset_camera_info(struct isp4_subdev *isp_subdev,
>>> +                     struct v4l2_subdev_state *state, u32 pad)
>>> +{
>>> +    struct isp4sd_sensor_info *info  = &isp_subdev->sensor_info;
>>> +
>>> +    info->status = ISP4SD_START_STATUS_NOT_START;
>>> +    isp4sd_reset_stream_info(isp_subdev, state, pad);
>>> +
>>> +    info->start_stream_cmd_sent = false;
>>> +}
>>> +
>>> +static int isp4sd_uninit_stream(struct isp4_subdev *isp_subdev,
>>> +                struct v4l2_subdev_state *state, u32 pad)
>>> +{
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct device *dev = isp_subdev->dev;
>>> +    bool running;
>>> +
>>> +    running = isp4sd_is_stream_running(isp_subdev);
>>> +
>>> +    if (running) {
>>> +        dev_dbg(dev, "fail for stream is still running\n");
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    isp4sd_reset_camera_info(isp_subdev, state, pad);
>>> +
>>> +    isp4if_clear_cmdq(ispif);
>>> +    return 0;
>>> +}
>>> +
>>> +static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev,
>>> +                    enum isp4if_stream_id stream_id,
>>> +                    struct isp4fw_resp_cmd_done *para)
>>> +{
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct isp4if_cmd_element *ele =
>>> +        isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para-
>>> >cmd_id);
>>> +    struct device *dev = isp_subdev->dev;
>>> +
>>> +    dev_dbg(dev, "stream %d,cmd (0x%08x)(%d),seq %u, ele %p\n",
>>> +        stream_id,
>>> +        para->cmd_id, para->cmd_status, para->cmd_seq_num,
>>> +        ele);
>>> +
>>> +    if (!ele)
>>> +        return;
>>> +
>>> +    if (ele->wq) {
>>> +        dev_dbg(dev, "signal event %p\n", ele->wq);
>>> +        if (ele->wq_cond)
>>> +            *ele->wq_cond = 1;
>>> +        wake_up(ele->wq);
>>> +    }
>>> +
>>> +    kfree(ele);
>>> +}
>>> +
>>> +static struct isp4fw_meta_info *
>>> +isp4sd_get_meta_by_mc(struct isp4_subdev *isp_subdev,
>>> +              u64 mc)
>>> +{
>>> +    u32 i;
>>> +
>>> +    for (i = 0; i < ISP4IF_MAX_STREAM_META_BUF_COUNT; i++) {
>>> +        struct isp4if_gpu_mem_info *meta_info_buf =
>>> +                isp_subdev->sensor_info.meta_info_buf[i];
>>> +
>>> +        if (meta_info_buf) {
>>> +            if (mc == meta_info_buf->gpu_mc_addr)
>>> +                return meta_info_buf->sys_addr;
>>> +        }
>>> +    }
>>> +    return NULL;
>>> +};
>>> +
>>> +static struct isp4if_img_buf_node *
>>> +isp4sd_preview_done(struct isp4_subdev *isp_subdev,
>>> +            struct isp4fw_meta_info *meta)
>>> +{
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct isp4if_img_buf_node *prev = NULL;
>>> +    struct device *dev = isp_subdev->dev;
>>> +
>>> +    if (!meta) {
>>> +        dev_err(dev, "fail bad param for preview done\n");
>>> +        return prev;
>>> +    }
>>> +
>>> +    if (meta->preview.enabled &&
>>> +        (meta->preview.status == BUFFER_STATUS_SKIPPED ||
>>> +         meta->preview.status == BUFFER_STATUS_DONE ||
>>> +         meta->preview.status == BUFFER_STATUS_DIRTY)) {
>>> +        prev = isp4if_dequeue_buffer(ispif);
>>> +        if (!prev)
>>> +            dev_err(dev, "fail null prev buf\n");
>>> +
>>> +    } else if (meta->preview.enabled) {
>>> +        dev_err(dev, "fail bad preview status %u\n",
>>> +            meta->preview.status);
>>> +    }
>>> +
>>> +    return prev;
>>> +}
>>> +
>>> +static void isp4sd_send_meta_info(struct isp4_subdev *isp_subdev,
>>> +                  u64 meta_info_mc)
>>> +{
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct isp4fw_cmd_send_buffer buf_type;
>>> +    struct device *dev = isp_subdev->dev;
>>> +
>>> +    if (isp_subdev->sensor_info.status !=
>>> ISP4SD_START_STATUS_STARTED) {
>>> +        dev_warn(dev, "not working status %i, meta_info 0x%llx\n",
>>> +             isp_subdev->sensor_info.status, meta_info_mc);
>>> +        return;
>>> +    }
>>> +
>>> +    if (meta_info_mc) {
>>> +        memset(&buf_type, 0, sizeof(buf_type));
>>> +        buf_type.buffer_type = BUFFER_TYPE_META_INFO;
>>> +        buf_type.buffer.buf_tags = 0;
>>> +        buf_type.buffer.vmid_space.bit.vmid = 0;
>>> +        buf_type.buffer.vmid_space.bit.space = ADDR_SPACE_TYPE_GPU_VA;
>>> +        isp4if_split_addr64(meta_info_mc,
>>> +                    &buf_type.buffer.buf_base_a_lo,
>>> +                    &buf_type.buffer.buf_base_a_hi);
>>> +
>>> +        buf_type.buffer.buf_size_a = ISP4SD_META_BUF_SIZE;
>>> +        if (isp4if_send_command(ispif, CMD_ID_SEND_BUFFER,
>>> +                    &buf_type, sizeof(buf_type))) {
>>> +            dev_err(dev, "fail send meta_info 0x%llx\n",
>>> +                meta_info_mc);
>>> +        } else {
>>> +            dev_dbg(dev, "resend meta_info 0x%llx\n", meta_info_mc);
>>> +        }
>>> +    }
>>> +}
>>> +
>>> +static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev,
>>> +                      enum isp4if_stream_id stream_id,
>>> +                      struct isp4fw_resp_param_package *para)
>>> +{
>>> +    struct isp4if_img_buf_node *prev = NULL;
>>> +    struct device *dev = isp_subdev->dev;
>>> +    struct isp4fw_meta_info *meta;
>>> +    u64 mc = 0;
>>> +
>>> +    mc = isp4if_join_addr64(para->package_addr_lo, para-
>>> >package_addr_hi);
>>> +    meta = isp4sd_get_meta_by_mc(isp_subdev, mc);
>>> +    if (mc == 0 || !meta) {
>>> +        dev_err(dev, "fail to get meta from mc %llx\n", mc);
>>> +        return;
>>> +    }
>>> +
>>> +    dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,(%i)\n",
>>> +        ktime_get_ns(), stream_id, meta->poc,
>>> +        meta->preview.enabled,
>>> +        meta->preview.status);
>>> +
>>> +    prev = isp4sd_preview_done(isp_subdev, meta);
>>> +
>>> +    isp4if_dealloc_buffer_node(prev);
>>> +
>>> +    if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED)
>>> +        isp4sd_send_meta_info(isp_subdev, mc);
>>> +
>>> +    dev_dbg(dev, "stream_id:%d, status:%d\n", stream_id,
>>> +        isp_subdev->sensor_info.status);
>>> +}
>>> +
>>> +static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev,
>>> +                enum isp4if_stream_id stream_id)
>>> +{
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct device *dev = isp_subdev->dev;
>>> +    struct isp4fw_resp resp;
>>> +
>>> +    if (ispif->status < ISP4IF_STATUS_FW_RUNNING)
>>> +        return;
>>> +
>>> +    while (true) {
>>> +        s32 ret;
>>> +
>>> +        ret = isp4if_f2h_resp(ispif, stream_id, &resp);
>>> +        if (ret)
>>> +            break;
>>> +
>>> +        switch (resp.resp_id) {
>>> +        case RESP_ID_CMD_DONE:
>>> +            isp4sd_fw_resp_cmd_done(isp_subdev, stream_id,
>>> +                        &resp.param.cmd_done);
>>> +            break;
>>> +        case RESP_ID_NOTI_FRAME_DONE:
>>> +            isp4sd_fw_resp_frame_done(isp_subdev, stream_id,
>>> +                          &resp.param.frame_done);
>>> +            break;
>>> +        default:
>>> +            dev_err(dev, "-><- fail respid (0x%x)\n",
>>> +                resp.resp_id);
>>> +            break;
>>> +        }
>>> +    }
>>> +}
>>> +
>>> +static s32 isp4sd_fw_resp_thread_wrapper(void *context)
>>> +{
>>> +    struct isp4_subdev_thread_param *para = context;
>>> +    struct isp4sd_thread_handler *thread_ctx;
>>> +    enum isp4if_stream_id stream_id;
>>> +
>>> +    struct isp4_subdev *isp_subdev;
>>> +    struct device *dev;
>>> +    u64 timeout;
>>> +
>>> +    if (!para)
>>> +        return -EINVAL;
>>> +
>>> +    isp_subdev = para->isp_subdev;
>>> +    dev = isp_subdev->dev;
>>> +
>>> +    switch (para->idx) {
>>> +    case 0:
>>> +        stream_id = ISP4IF_STREAM_ID_GLOBAL;
>>> +        break;
>>> +    case 1:
>>> +        stream_id = ISP4IF_STREAM_ID_1;
>>> +        break;
>>> +    default:
>>> +        dev_err(dev, "fail invalid %d\n", para->idx);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    thread_ctx = &isp_subdev->fw_resp_thread[para->idx];
>>> +
>>> +    thread_ctx->wq_cond = 0;
>>> +    mutex_init(&thread_ctx->mutex);
>>> +    init_waitqueue_head(&thread_ctx->waitq);
>>> +    timeout = msecs_to_jiffies(ISP4SD_WAIT_RESP_IRQ_TIMEOUT);
>>> +
>>> +    dev_dbg(dev, "[%u] started\n", para->idx);
>>> +
>>> +    while (true) {
>>> +        wait_event_interruptible_timeout(thread_ctx->waitq,
>>> +                         thread_ctx->wq_cond != 0,
>>> +                         timeout);
>>> +        thread_ctx->wq_cond = 0;
>>> +
>>> +        if (kthread_should_stop()) {
>>> +            dev_dbg(dev, "[%u] quit\n", para->idx);
>>> +            break;
>>> +        }
>>> +
>>> +        mutex_lock(&thread_ctx->mutex);
>>> +        isp4sd_fw_resp_func(isp_subdev, stream_id);
>>> +        mutex_unlock(&thread_ctx->mutex);
>>> +    }
>>> +
>>> +    mutex_destroy(&thread_ctx->mutex);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int isp4sd_start_resp_proc_threads(struct isp4_subdev
>>> *isp_subdev)
>>> +{
>>> +    struct device *dev = isp_subdev->dev;
>>> +    int i;
>>> +
>>> +    for (i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) {
>>> +        struct isp4sd_thread_handler *thread_ctx =
>>> +                &isp_subdev->fw_resp_thread[i];
>>> +
>>> +        isp_subdev->isp_resp_para[i].idx = i;
>>> +        isp_subdev->isp_resp_para[i].isp_subdev = isp_subdev;
>>> +
>>> +        thread_ctx->thread = kthread_run(isp4sd_fw_resp_thread_wrapper,
>>> +                         &isp_subdev->isp_resp_para[i],
>>> +                         "amd_isp4_thread");
>>> +        if (IS_ERR(thread_ctx->thread)) {
>>> +            dev_err(dev, "create thread [%d] fail\n", i);
>>> +            return -EINVAL;
>>> +        }
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int isp4sd_stop_resp_proc_threads(struct isp4_subdev
>>> *isp_subdev)
>>> +{
>>> +    int i;
>>> +
>>> +    for (i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) {
>>> +        struct isp4sd_thread_handler *thread_ctx =
>>> +                &isp_subdev->fw_resp_thread[i];
>>> +
>>> +        if (thread_ctx->thread) {
>>> +            kthread_stop(thread_ctx->thread);
>>> +            thread_ctx->thread = NULL;
>>> +        }
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static u32 isp4sd_get_started_stream_count(struct isp4_subdev
>>> *isp_subdev)
>>> +{
>>> +    u32 cnt = 0;
>>> +
>>> +    if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED)
>>> +        cnt++;
>>> +    return cnt;
>>> +}
>>> +
>>> +static int isp4sd_pwroff_and_deinit(struct isp4_subdev *isp_subdev)
>>> +{
>>> +    struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
>>> +    unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_LOW;
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +
>>> +    struct device *dev = isp_subdev->dev;
>>> +    u32 cnt;
>>> +    int ret;
>>> +
>>> +    mutex_lock(&isp_subdev->ops_mutex);
>>> +
>>> +    if (sensor_info->status == ISP4SD_START_STATUS_STARTED) {
>>> +        dev_err(dev, "fail for stream still running\n");
>>> +        mutex_unlock(&isp_subdev->ops_mutex);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    sensor_info->status = ISP4SD_START_STATUS_NOT_START;
>>> +    cnt = isp4sd_get_started_stream_count(isp_subdev);
>>> +    if (cnt > 0) {
>>> +        dev_dbg(dev, "no need power off isp_subdev\n");
>>> +        mutex_unlock(&isp_subdev->ops_mutex);
>>> +        return 0;
>>> +    }
>>> +
>>> +    isp4if_stop(ispif);
>>> +
>>> +    ret = dev_pm_genpd_set_performance_state(dev, perf_state);
>>> +    if (ret)
>>> +        dev_err(dev,
>>> +            "fail to set isp_subdev performance state %u,ret %d\n",
>>> +            perf_state, ret);
>>> +    isp4sd_stop_resp_proc_threads(isp_subdev);
>>> +    dev_dbg(dev, "isp_subdev stop resp proc streads suc");
>>> +    /* hold ccpu reset */
>>> +    isp4hw_wreg(isp_subdev->mmio, ISP_SOFT_RESET, 0x0);
>>> +    isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0);
>>> +    ret = pm_runtime_put_sync(dev);
>>> +    if (ret)
>>> +        dev_err(dev, "power off isp_subdev fail %d\n", ret);
>>> +    else
>>> +        dev_dbg(dev, "power off isp_subdev suc\n");
>>> +
>>> +    ispif->status = ISP4IF_STATUS_PWR_OFF;
>>> +    isp4if_clear_cmdq(ispif);
>>> +    isp4sd_module_enable(isp_subdev, false);
>>> +
>>> +    msleep(20);
>>> +
>>> +    mutex_unlock(&isp_subdev->ops_mutex);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int isp4sd_pwron_and_init(struct isp4_subdev *isp_subdev)
>>> +{
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct device *dev = isp_subdev->dev;
>>> +    int ret;
>>> +
>>> +    if (ispif->status == ISP4IF_STATUS_FW_RUNNING) {
>>> +        dev_dbg(dev, "camera already opened, do nothing\n");
>>> +        return 0;
>>> +    }
>>> +
>>> +    mutex_lock(&isp_subdev->ops_mutex);
>>> +
>>> +    isp4sd_module_enable(isp_subdev, true);
>>> +
>>> +    isp_subdev->sensor_info.start_stream_cmd_sent = false;
>>> +    isp_subdev->sensor_info.buf_sent_cnt = 0;
>>> +
>>> +    if (ispif->status < ISP4IF_STATUS_PWR_ON) {
>>> +        unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_HIGH;
>>> +
>>> +        ret = pm_runtime_resume_and_get(dev);
>>> +        if (ret) {
>>> +            dev_err(dev, "fail to power on isp_subdev ret %d\n",
>>> +                ret);
>>> +            goto err_unlock_and_close;
>>> +        }
>>> +
>>> +        /* ISPPG ISP Power Status */
>>> +        isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0x7FF);
>>> +        ret = dev_pm_genpd_set_performance_state(dev, perf_state);
>>> +        if (ret) {
>>> +            dev_err(dev,
>>> +                "fail to set performance state %u, ret %d\n",
>>> +                perf_state, ret);
>>> +            goto err_unlock_and_close;
>>> +        }
>>> +
>>> +        ispif->status = ISP4IF_STATUS_PWR_ON;
>>> +
>>> +        if (isp4sd_start_resp_proc_threads(isp_subdev)) {
>>> +            dev_err(dev, "isp_start_resp_proc_threads fail");
>>> +            goto err_unlock_and_close;
>>> +        } else {
>>> +            dev_dbg(dev, "create resp threads ok");
>>> +        }
>>> +    }
>>> +
>>> +    isp_subdev->sensor_info.start_stream_cmd_sent = false;
>>> +    isp_subdev->sensor_info.buf_sent_cnt = 0;
>>> +
>>> +    ret = isp4if_start(ispif);
>>> +    if (ret) {
>>> +        dev_err(dev, "fail to start isp_subdev interface\n");
>>> +        goto err_unlock_and_close;
>>> +    }
>>> +
>>> +    mutex_unlock(&isp_subdev->ops_mutex);
>>> +    return 0;
>>> +err_unlock_and_close:
>>> +    mutex_unlock(&isp_subdev->ops_mutex);
>>> +    isp4sd_pwroff_and_deinit(isp_subdev);
>>> +    return -EINVAL;
>>> +}
>>> +
>>> +static int isp4sd_stop_stream(struct isp4_subdev *isp_subdev,
>>> +                  struct v4l2_subdev_state *state, u32 pad)
>>> +{
>>> +    struct isp4sd_output_info *output_info =
>>> +            &isp_subdev->sensor_info.output_info;
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct device *dev = isp_subdev->dev;
>>> +    int ret = 0;
>>> +
>>> +    dev_dbg(dev, "status %i\n", output_info->start_status);
>>> +    mutex_lock(&isp_subdev->ops_mutex);
>>> +
>>> +    if (output_info->start_status == ISP4SD_START_STATUS_STARTED) {
>>> +        struct isp4fw_cmd_enable_out_ch cmd_ch_disable;
>>> +
>>> +        cmd_ch_disable.ch = ISP_PIPE_OUT_CH_PREVIEW;
>>> +        cmd_ch_disable.is_enable = false;
>>> +        ret = isp4if_send_command_sync(ispif,
>>> +                           CMD_ID_ENABLE_OUT_CHAN,
>>> +                           &cmd_ch_disable,
>>> +                           sizeof(cmd_ch_disable),
>>> +                           ISP4SD_FW_CMD_TIMEOUT_IN_MS);
>>> +        if (ret)
>>> +            dev_err(dev, "fail to disable stream\n");
>>> +        else
>>> +            dev_dbg(dev, "wait disable stream suc\n");
>>> +
>>> +        ret = isp4if_send_command_sync(ispif, CMD_ID_STOP_STREAM,
>>> +                           NULL,
>>> +                           0,
>>> +                           ISP4SD_FW_CMD_TIMEOUT_IN_MS);
>>> +        if (ret)
>>> +            dev_err(dev, "fail to stop steam\n");
>>> +        else
>>> +            dev_dbg(dev, "wait stop stream suc\n");
>>> +    }
>>> +
>>> +    isp4if_clear_bufq(ispif);
>>> +
>>> +    output_info->start_status = ISP4SD_START_STATUS_NOT_START;
>>> +    isp4sd_reset_stream_info(isp_subdev, state, pad);
>>> +
>>> +    mutex_unlock(&isp_subdev->ops_mutex);
>>> +
>>> +    isp4sd_uninit_stream(isp_subdev, state, pad);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int isp4sd_start_stream(struct isp4_subdev *isp_subdev,
>>> +                   struct v4l2_subdev_state *state, u32 pad)
>>> +{
>>> +    struct isp4sd_output_info *output_info =
>>> +            &isp_subdev->sensor_info.output_info;
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct device *dev = isp_subdev->dev;
>>> +    int ret;
>>> +
>>> +    mutex_lock(&isp_subdev->ops_mutex);
>>> +
>>> +    if (ispif->status != ISP4IF_STATUS_FW_RUNNING) {
>>> +        mutex_unlock(&isp_subdev->ops_mutex);
>>> +        dev_err(dev, "fail, bad fsm %d", ispif->status);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    ret = isp4sd_init_stream(isp_subdev);
>>> +
>>> +    if (ret) {
>>> +        dev_err(dev, "fail to init isp_subdev stream\n");
>>> +        ret = -EINVAL;
>>> +        goto unlock_and_check_ret;
>>> +    }
>>> +
>>> +    if (output_info->start_status == ISP4SD_START_STATUS_STARTED) {
>>> +        ret = 0;
>>> +        dev_dbg(dev, "stream started, do nothing\n");
>>> +        goto unlock_and_check_ret;
>>> +    } else if (output_info->start_status ==
>>> +           ISP4SD_START_STATUS_START_FAIL) {
>>> +        ret = -EINVAL;
>>> +        dev_err(dev, "stream  fail to start before\n");
>>> +        goto unlock_and_check_ret;
>>> +    }
>>> +
>>> +    if (isp4sd_setup_output(isp_subdev, state, pad)) {
>>> +        dev_err(dev, "fail to setup output\n");
>>> +        ret = -EINVAL;
>>> +    } else {
>>> +        ret = 0;
>>> +        dev_dbg(dev, "suc to setup out\n");
>>> +    }
>>> +unlock_and_check_ret:
>>> +    mutex_unlock(&isp_subdev->ops_mutex);
>>> +    if (ret) {
>>> +        isp4sd_stop_stream(isp_subdev, state, pad);
>>> +        dev_err(dev, "start stream fail\n");
>>> +    } else {
>>> +        dev_dbg(dev, "start stream suc\n");
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int isp4sd_subdev_pre_streamon(struct v4l2_subdev *sd, u32
>>> flags)
>>> +{
>>> +    struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
>>> +    u64 phy_bit_rate = isp_subdev->phy_link_freq * isp_subdev-
>>> >phy_num_data_lanes / HZ_PER_MHZ;
>>> +    u32 num_data_lanes = isp_subdev->phy_num_data_lanes;
>>> +    u32 phy_id = isp_subdev->phy_id;
>>> +    int ret;
>>> +
>>> +    ret = isp4phy_start(isp_subdev->dev,
>>> +                isp_subdev->isp_phy_mmio, phy_id,
>>> +                phy_bit_rate, num_data_lanes);
>>> +    if (ret) {
>>> +        dev_err(isp_subdev->dev,
>>> +            "fail start phy,lane %d id %u bitrate %llu, %d\n",
>>> +            num_data_lanes, phy_id, phy_bit_rate, ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    dev_dbg(isp_subdev->dev, "start phy suc,lane %d id %u bit_rate
>>> %llu\n",
>>> +        num_data_lanes, phy_id, phy_bit_rate);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int isp4sd_subdev_post_streamoff(struct v4l2_subdev *sd)
>>> +{
>>> +    struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
>>> +    int ret;
>>> +
>>> +    dev_dbg(isp_subdev->dev, "stopping phy %u\n", isp_subdev->phy_id);
>>> +    ret = isp4phy_stop(isp_subdev->isp_phy_mmio,
>>> +               isp_subdev->phy_id);
>>> +    if (ret)
>>> +        dev_err(isp_subdev->dev, "fail to stop the Phy:%d", ret);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int isp4sd_set_power(struct v4l2_subdev *sd, int on)
>>> +{
>>> +    struct isp4_subdev *ispsd = to_isp4_subdev(sd);
>>> +
>>> +    if (on)
>>> +        return isp4sd_pwron_and_init(ispsd);
>>> +    else
>>> +        return isp4sd_pwroff_and_deinit(ispsd);
>>> +};
>>> +
>>> +static const struct v4l2_subdev_core_ops isp4sd_core_ops = {
>>> +    .s_power = isp4sd_set_power,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_video_ops isp4sd_video_ops = {
>>> +    .s_stream = v4l2_subdev_s_stream_helper,
>>> +    .pre_streamon = isp4sd_subdev_pre_streamon,
>>> +    .post_streamoff = isp4sd_subdev_post_streamoff,
>>> +};
>>> +
>>> +static int isp4sd_set_pad_format(struct v4l2_subdev *sd,
>>> +                 struct v4l2_subdev_state *sd_state,
>>> +                 struct v4l2_subdev_format *fmt)
>>> +{
>>> +    struct isp4sd_output_info *steam_info =
>>> +        &(to_isp4_subdev(sd)->sensor_info.output_info);
>>> +    struct v4l2_mbus_framefmt *format;
>>> +
>>> +    format = v4l2_subdev_state_get_format(sd_state, fmt->pad);
>>> +
>>> +    if (!format) {
>>> +        dev_err(sd->dev, "fail to get state format\n");
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    *format = fmt->format;
>>> +    switch (format->code) {
>>> +    case MEDIA_BUS_FMT_YUYV8_1_5X8:
>>> +        steam_info->image_size = format->width * format->height *
>>> 3 / 2;
>>> +        break;
>>> +    case MEDIA_BUS_FMT_YUYV8_1X16:
>>> +        steam_info->image_size = format->width * format->height * 2;
>>> +        break;
>>> +    default:
>>> +        steam_info->image_size = 0;
>>> +        break;
>>> +    }
>>> +    if (!steam_info->image_size) {
>>> +        dev_err(sd->dev,
>>> +            "fail set pad format,code 0x%x,width %u, height %u\n",
>>> +            format->code, format->width, format->height);
>>> +        return -EINVAL;
>>> +    }
>>> +    dev_dbg(sd->dev,
>>> +        "set pad format suc, code:%x w:%u h:%u size:%u\n", format-
>>> >code,
>>> +        format->width, format->height, steam_info->image_size);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int isp4sd_enable_streams(struct v4l2_subdev *sd,
>>> +                 struct v4l2_subdev_state *state, u32 pad,
>>> +                 u64 streams_mask)
>>> +{
>>> +    struct isp4_subdev *ispsd = to_isp4_subdev(sd);
>>> +
>>> +    return isp4sd_start_stream(ispsd, state, pad);
>>> +}
>>> +
>>> +static int isp4sd_disable_streams(struct v4l2_subdev *sd,
>>> +                  struct v4l2_subdev_state *state, u32 pad,
>>> +                  u64 streams_mask)
>>> +{
>>> +    struct isp4_subdev *ispsd = to_isp4_subdev(sd);
>>> +
>>> +    return isp4sd_stop_stream(ispsd, state, pad);
>>> +}
>>> +
>>> +static const struct v4l2_subdev_pad_ops isp4sd_pad_ops = {
>>> +    .enum_mbus_code    = isp4sd_enum_mbus_code,
>>> +    .get_fmt = v4l2_subdev_get_fmt,
>>> +    .set_fmt = isp4sd_set_pad_format,
>>> +    .enable_streams = isp4sd_enable_streams,
>>> +    .disable_streams = isp4sd_disable_streams,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_ops isp4sd_subdev_ops = {
>>> +    .core = &isp4sd_core_ops,
>>> +    .video = &isp4sd_video_ops,
>>> +    .pad = &isp4sd_pad_ops,
>>> +};
>>> +
>>> +static int isp4sd_sdev_link_validate(struct media_link *link)
>>> +{
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct media_entity_operations isp4sd_sdev_ent_ops = {
>>> +    .link_validate = isp4sd_sdev_link_validate,
>>> +};
>>> +
>>> +int isp4sd_init(struct isp4_subdev *isp_subdev,
>>> +        struct v4l2_device *v4l2_dev,
>>> +        void *amdgpu_dev)
>>> +{
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +    struct isp4sd_sensor_info *sensor_info;
>>> +    struct device *dev = v4l2_dev->dev;
>>> +    int ret;
>>> +
>>> +    isp_subdev->dev = dev;
>>> +    isp_subdev->amdgpu_dev = amdgpu_dev;
>>> +    v4l2_subdev_init(&isp_subdev->sdev, &isp4sd_subdev_ops);
>>> +    isp_subdev->sdev.owner = THIS_MODULE;
>>> +    isp_subdev->sdev.dev = dev;
>>> +    snprintf(isp_subdev->sdev.name, sizeof(isp_subdev->sdev.name),
>>> "%s",
>>> +         dev_name(dev));
>>> +
>>> +    isp_subdev->sdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
>>> +    isp_subdev->sdev.entity.name = isp4sd_entity_name;
>>> +    isp_subdev->sdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
>>> +    isp_subdev->sdev.entity.ops = &isp4sd_sdev_ent_ops;
>>> +    isp_subdev->sdev_pad[0].flags = MEDIA_PAD_FL_SINK;
>>> +    isp_subdev->sdev_pad[1].flags = MEDIA_PAD_FL_SOURCE;
>>> +    ret = media_entity_pads_init(&isp_subdev->sdev.entity, 2,
>>> +                     isp_subdev->sdev_pad);
>>> +    if (ret) {
>>> +        dev_err(dev, "fail to init isp4 subdev entity pad %d\n", ret);
>>> +        return ret;
>>> +    }
>>> +    ret = v4l2_subdev_init_finalize(&isp_subdev->sdev);
>>> +    if (ret < 0) {
>>> +        dev_err(dev, "fail to init finalize isp4 subdev %d\n",
>>> +            ret);
>>> +        return ret;
>>> +    }
>>> +    ret = v4l2_device_register_subdev(v4l2_dev, &isp_subdev->sdev);
>>> +    if (ret) {
>>> +        dev_err(dev, "fail to register isp4 subdev to V4L2 device
>>> %d\n",
>>> +            ret);
>>> +        goto err_media_clean_up;
>>> +    }
>>> +
>>> +    sensor_info = &isp_subdev->sensor_info;
>>> +
>>> +    isp4if_init(ispif, dev, amdgpu_dev, isp_subdev->mmio);
>>> +
>>> +    mutex_init(&isp_subdev->ops_mutex);
>>> +    sensor_info->start_stream_cmd_sent = false;
>>> +    sensor_info->status = ISP4SD_START_STATUS_NOT_START;
>>> +
>>> +    /* create ISP enable gpio control */
>>> +    isp_subdev->enable_gpio = devm_gpiod_get(isp_subdev->dev,
>>> +                         "enable_isp",
>>> +                         GPIOD_OUT_LOW);
>>> +    if (IS_ERR(isp_subdev->enable_gpio)) {
>>> +        dev_err(dev, "fail to get gpiod %d\n", ret);
>>> +        media_entity_cleanup(&isp_subdev->sdev.entity);
>>> +        return PTR_ERR(isp_subdev->enable_gpio);
>>> +    }
>>> +
>>> +    isp_subdev->host2fw_seq_num = 1;
>>> +    ispif->status = ISP4IF_STATUS_PWR_OFF;
>>> +
>>> +    if (ret)
>>> +        goto err_media_clean_up;
>>> +    return ret;
>>> +
>>> +err_media_clean_up:
>>> +    media_entity_cleanup(&isp_subdev->sdev.entity);
>>> +    return ret;
>>> +}
>>> +
>>> +void isp4sd_deinit(struct isp4_subdev *isp_subdev)
>>> +{
>>> +    struct isp4_interface *ispif = &isp_subdev->ispif;
>>> +
>>> +    media_entity_cleanup(&isp_subdev->sdev.entity);
>>> +    isp4if_deinit(ispif);
>>> +    isp4sd_module_enable(isp_subdev, false);
>>> +
>>> +    ispif->status = ISP4IF_STATUS_PWR_OFF;
>>> +}
>>> diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/
>>> media/platform/amd/isp4/isp4_subdev.h
>>> new file mode 100644
>>> index 000000000000..bcbb93dce18f
>>> --- /dev/null
>>> +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h
>>> @@ -0,0 +1,145 @@
>>> +/* SPDX-License-Identifier: GPL-2.0+ */
>>> +/*
>>> + * Copyright (C) 2025 Advanced Micro Devices, Inc.
>>> + */
>>> +
>>> +#ifndef _ISP4_CONTEXT_H_
>>> +#define _ISP4_CONTEXT_H_
>>> +
>>> +#include <linux/delay.h>
>>> +#include <linux/firmware.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/uaccess.h>
>>> +#include <linux/types.h>
>>> +#include <linux/debugfs.h>
>>> +#include <media/v4l2-device.h>
>>> +#include <media/videobuf2-vmalloc.h>
>>> +
>>> +#include "isp4_fw_cmd_resp.h"
>>> +#include "isp4_hw_reg.h"
>>> +#include "isp4_interface.h"
>>> +
>>> +/*
>>> + * one is for none sesnor specefic response which is not used now
>>> + * another is for sensor specific response
>>> + */
>>> +#define ISP4SD_MAX_FW_RESP_STREAM_NUM 2
>>> +
>>> +/*
>>> + * cmd used to register frame done callback, parameter is
>>> + * struct isp4sd_register_framedone_cb_param *
>>> + * when a image buffer is filled by ISP, ISP will call the
>>> registered callback.
>>> + * callback func prototype is isp4sd_framedone_cb, cb_ctx can be
>>> anything
>>> + * provided by caller which will be provided back as the first
>>> parameter of the
>>> + * callback function.
>>> + * both cb_func and cb_ctx are provide by caller, set cb_func to
>>> NULL to
>>> + * unregister the callback
>>> + */
>>> +
>>> +/* used to indicate the ISP status*/
>>> +enum isp4sd_status {
>>> +    ISP4SD_STATUS_PWR_OFF,
>>> +    ISP4SD_STATUS_PWR_ON,
>>> +    ISP4SD_STATUS_FW_RUNNING,
>>> +    ISP4SD_STATUS_MAX
>>> +};
>>> +
>>> +/*used to indicate the status of sensor, output stream */
>>> +enum isp4sd_start_status {
>>> +    ISP4SD_START_STATUS_NOT_START,
>>> +    ISP4SD_START_STATUS_STARTED,
>>> +    ISP4SD_START_STATUS_START_FAIL,
>>> +};
>>> +
>>> +struct isp4sd_img_buf_node {
>>> +    struct list_head node;
>>> +    struct isp4if_img_buf_info buf_info;
>>> +};
>>> +
>>> +/* this is isp output after processing bayer raw input from sensor */
>>> +struct isp4sd_output_info {
>>> +    enum isp4sd_start_status start_status;
>>> +    u32 image_size;
>>> +};
>>> +
>>> +/* This struct represents the sensor info which is input or source
>>> of ISP,
>>> + * meta_info_buf is the buffer store the fw to driver metainfo response
>>> + * status is the sensor status
>>> + * output_info is the isp output info after ISP processing the
>>> sensor input,
>>> + * start_stream_cmd_sent mean if CMD_ID_START_STREAM has sent to fw.
>>> + * buf_sent_cnt is buffer count app has sent to receive the images
>>> + */
>>> +struct isp4sd_sensor_info {
>>> +    struct isp4if_gpu_mem_info *
>>> +        meta_info_buf[ISP4IF_MAX_STREAM_META_BUF_COUNT];
>>> +    struct isp4sd_output_info output_info;
>>> +    enum isp4sd_start_status status;
>>> +    bool start_stream_cmd_sent;
>>> +    u32 buf_sent_cnt;
>>> +};
>>> +
>>> +/*
>>> + * Thread created by driver to receive fw response
>>> + * thread will be wakeup by fw to driver response interrupt
>>> + */
>>> +struct isp4sd_thread_handler {
>>> +    struct task_struct *thread;
>>> +    struct mutex mutex; /* mutex */
>>> +    wait_queue_head_t waitq;
>>> +    int wq_cond;
>>> +};
>>> +
>>> +struct isp4_subdev_thread_param {
>>> +    u32 idx;
>>> +    struct isp4_subdev *isp_subdev;
>>> +};
>>> +
>>> +struct isp4_subdev {
>>> +    struct v4l2_subdev sdev;
>>> +    struct isp4_interface ispif;
>>> +
>>> +    /*
>>> +     * sdev_pad[0] is sink pad connected to sensor
>>> +     * sdev_pad[0] is sink pad connected to sensor
>>> +     * sdev_pad[1] is sourc pad connected v4l2 video device
>>> +     */
>>> +    struct media_pad sdev_pad[2];
>>> +
>>> +    enum isp4sd_status isp_status;
>>> +    struct mutex ops_mutex; /* ops_mutex */
>>> +
>>> +    /* Used to store fw cmds sent to FW whose response driver needs
>>> +     * to wait for
>>> +     */
>>> +    struct isp4sd_thread_handler
>>> +        fw_resp_thread[ISP4SD_MAX_FW_RESP_STREAM_NUM];
>>> +
>>> +    u32 host2fw_seq_num;
>>> +
>>> +    struct isp4sd_sensor_info sensor_info;
>>> +
>>> +    /* gpio descriptor */
>>> +    struct gpio_desc *enable_gpio;
>>> +    struct device *dev;
>>> +    void *amdgpu_dev;
>>> +    void __iomem *mmio;
>>> +    struct isp4_subdev_thread_param
>>> +        isp_resp_para[ISP4SD_MAX_FW_RESP_STREAM_NUM];
>>> +#ifdef CONFIG_DEBUG_FS
>>> +    struct dentry *debugfs_dir;
>>> +    bool enable_fw_log;
>>> +    char *fw_log_output;
>>> +#endif
>>> +    u32 phy_num_data_lanes;
>>> +    u32 phy_id;
>>> +    u64 phy_link_freq;
>>> +
>>> +    void __iomem *isp_phy_mmio;
>>> +};
>>> +
>>> +int isp4sd_init(struct isp4_subdev *isp_subdev,
>>> +        struct v4l2_device *v4l2_dev,
>>> +        void *amdgpu_dev);
>>> +void isp4sd_deinit(struct isp4_subdev *isp_subdev);
>>> +
>>> +#endif
>>
>