root/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Rockchip ISP1 Driver - ISP Subdevice
 *
 * Copyright (C) 2019 Collabora, Ltd.
 *
 * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
 * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
 */

#include <linux/iopoll.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>

#include <media/v4l2-event.h>

#include "rkisp1-common.h"

#define RKISP1_DEF_SINK_PAD_FMT MEDIA_BUS_FMT_SRGGB10_1X10
#define RKISP1_DEF_SRC_PAD_FMT MEDIA_BUS_FMT_YUYV8_2X8

#define RKISP1_ISP_DEV_NAME     RKISP1_DRIVER_NAME "_isp"

/*
 * NOTE: MIPI controller and input MUX are also configured in this file.
 * This is because ISP Subdev describes not only ISP submodule (input size,
 * format, output size, format), but also a virtual route device.
 */

/*
 * There are many variables named with format/frame in below code,
 * please see here for their meaning.
 * Cropping in the sink pad defines the image region from the sensor.
 * Cropping in the source pad defines the region for the Image Stabilizer (IS)
 *
 * Cropping regions of ISP
 *
 * +---------------------------------------------------------+
 * | Sensor image                                            |
 * | +---------------------------------------------------+   |
 * | | CIF_ISP_ACQ (for black level)                     |   |
 * | | sink pad format                                   |   |
 * | | +--------------------------------------------+    |   |
 * | | |    CIF_ISP_OUT                             |    |   |
 * | | |    sink pad crop                           |    |   |
 * | | |    +---------------------------------+     |    |   |
 * | | |    |   CIF_ISP_IS                    |     |    |   |
 * | | |    |   source pad crop and format    |     |    |   |
 * | | |    +---------------------------------+     |    |   |
 * | | +--------------------------------------------+    |   |
 * | +---------------------------------------------------+   |
 * +---------------------------------------------------------+
 */

/* -----------------------------------------------------------------------------
 * Media block control (i.MX8MP only)
 */

#define ISP_DEWARP_CONTROL                              0x0138

#define ISP_DEWARP_CONTROL_MIPI_CSI2_HS_POLARITY        BIT(22)
#define ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_RISING      (0 << 20)
#define ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_NEGATIVE    (1 << 20)
#define ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_POSITIVE    (2 << 20)
#define ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_FALLING     (3 << 20)
#define ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_MASK        GENMASK(21, 20)
#define ISP_DEWARP_CONTROL_MIPI_ISP2_LEFT_JUST_MODE     BIT(19)
#define ISP_DEWARP_CONTROL_MIPI_ISP2_DATA_TYPE(dt)      ((dt) << 13)
#define ISP_DEWARP_CONTROL_MIPI_ISP2_DATA_TYPE_MASK     GENMASK(18, 13)

#define ISP_DEWARP_CONTROL_MIPI_CSI1_HS_POLARITY        BIT(12)
#define ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_RISING      (0 << 10)
#define ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_NEGATIVE    (1 << 10)
#define ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_POSITIVE    (2 << 10)
#define ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_FALLING     (3 << 10)
#define ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_MASK        GENMASK(11, 10)
#define ISP_DEWARP_CONTROL_MIPI_ISP1_LEFT_JUST_MODE     BIT(9)
#define ISP_DEWARP_CONTROL_MIPI_ISP1_DATA_TYPE(dt)      ((dt) << 3)
#define ISP_DEWARP_CONTROL_MIPI_ISP1_DATA_TYPE_MASK     GENMASK(8, 3)

#define ISP_DEWARP_CONTROL_GPR_ISP_1_DISABLE            BIT(1)
#define ISP_DEWARP_CONTROL_GPR_ISP_0_DISABLE            BIT(0)

static int rkisp1_gasket_enable(struct rkisp1_device *rkisp1,
                                struct media_pad *source)
{
        struct v4l2_subdev *source_sd;
        struct v4l2_mbus_frame_desc fd;
        unsigned int dt;
        u32 mask;
        u32 val;
        int ret;

        /*
         * Configure and enable the gasket with the CSI-2 data type. Set the
         * vsync polarity as active high, as that is what the ISP is configured
         * to expect in ISP_ACQ_PROP. Enable left justification, as the i.MX8MP
         * ISP has a 16-bit wide input and expects data to be left-aligned.
         */

        source_sd = media_entity_to_v4l2_subdev(source->entity);
        ret = v4l2_subdev_call(source_sd, pad, get_frame_desc,
                               source->index, &fd);
        if (ret) {
                dev_err(rkisp1->dev,
                        "failed to get frame descriptor from '%s':%u: %d\n",
                        source_sd->name, 0, ret);
                return ret;
        }

        if (fd.num_entries != 1) {
                dev_err(rkisp1->dev, "invalid frame descriptor for '%s':%u\n",
                        source_sd->name, 0);
                return -EINVAL;
        }

        dt = fd.entry[0].bus.csi2.dt;

        if (rkisp1->gasket_id == 0) {
                mask = ISP_DEWARP_CONTROL_MIPI_CSI1_HS_POLARITY
                     | ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_MASK
                     | ISP_DEWARP_CONTROL_MIPI_ISP1_LEFT_JUST_MODE
                     | ISP_DEWARP_CONTROL_MIPI_ISP1_DATA_TYPE_MASK
                     | ISP_DEWARP_CONTROL_GPR_ISP_0_DISABLE;
                val = ISP_DEWARP_CONTROL_MIPI_CSI1_VS_SEL_POSITIVE
                    | ISP_DEWARP_CONTROL_MIPI_ISP1_LEFT_JUST_MODE
                    | ISP_DEWARP_CONTROL_MIPI_ISP1_DATA_TYPE(dt);
        } else {
                mask = ISP_DEWARP_CONTROL_MIPI_CSI2_HS_POLARITY
                     | ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_MASK
                     | ISP_DEWARP_CONTROL_MIPI_ISP2_LEFT_JUST_MODE
                     | ISP_DEWARP_CONTROL_MIPI_ISP2_DATA_TYPE_MASK
                     | ISP_DEWARP_CONTROL_GPR_ISP_1_DISABLE;
                val = ISP_DEWARP_CONTROL_MIPI_CSI2_VS_SEL_POSITIVE
                    | ISP_DEWARP_CONTROL_MIPI_ISP2_LEFT_JUST_MODE
                    | ISP_DEWARP_CONTROL_MIPI_ISP2_DATA_TYPE(dt);
        }

        regmap_update_bits(rkisp1->gasket, ISP_DEWARP_CONTROL, mask, val);

        return 0;
}

static void rkisp1_gasket_disable(struct rkisp1_device *rkisp1)
{
        u32 mask;
        u32 val;

        if (rkisp1->gasket_id == 1) {
                mask = ISP_DEWARP_CONTROL_MIPI_ISP2_LEFT_JUST_MODE
                     | ISP_DEWARP_CONTROL_MIPI_ISP2_DATA_TYPE_MASK
                     | ISP_DEWARP_CONTROL_GPR_ISP_1_DISABLE;
                val = ISP_DEWARP_CONTROL_GPR_ISP_1_DISABLE;
        } else {
                mask = ISP_DEWARP_CONTROL_MIPI_ISP1_LEFT_JUST_MODE
                     | ISP_DEWARP_CONTROL_MIPI_ISP1_DATA_TYPE_MASK
                     | ISP_DEWARP_CONTROL_GPR_ISP_0_DISABLE;
                val = ISP_DEWARP_CONTROL_GPR_ISP_0_DISABLE;
        }

        regmap_update_bits(rkisp1->gasket, ISP_DEWARP_CONTROL, mask, val);
}

/* ----------------------------------------------------------------------------
 * Camera Interface registers configurations
 */

/*
 * Image Stabilization.
 * This should only be called when configuring CIF
 * or at the frame end interrupt
 */
static void rkisp1_config_ism(struct rkisp1_isp *isp,
                              const struct v4l2_subdev_state *sd_state)
{
        const struct v4l2_rect *src_crop =
                v4l2_subdev_state_get_crop(sd_state,
                                           RKISP1_ISP_PAD_SOURCE_VIDEO);
        struct rkisp1_device *rkisp1 = isp->rkisp1;
        u32 val;

        rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_RECENTER, 0);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_MAX_DX, 0);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_MAX_DY, 0);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_DISPLACE, 0);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_H_OFFS, src_crop->left);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_V_OFFS, src_crop->top);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_H_SIZE, src_crop->width);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_V_SIZE, src_crop->height);

        /* IS(Image Stabilization) is always on, working as output crop */
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_CTRL, 1);
        val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
        val |= RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD;
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
}

/*
 * configure ISP blocks with input format, size......
 */
static int rkisp1_config_isp(struct rkisp1_isp *isp,
                             const struct v4l2_subdev_state *sd_state,
                             enum v4l2_mbus_type mbus_type, u32 mbus_flags)
{
        struct rkisp1_device *rkisp1 = isp->rkisp1;
        u32 isp_ctrl = 0, irq_mask = 0, acq_mult = 0, acq_prop = 0;
        const struct rkisp1_mbus_info *sink_fmt;
        const struct rkisp1_mbus_info *src_fmt;
        const struct v4l2_mbus_framefmt *src_frm;
        const struct v4l2_mbus_framefmt *sink_frm;
        const struct v4l2_rect *sink_crop;

        sink_frm = v4l2_subdev_state_get_format(sd_state,
                                                RKISP1_ISP_PAD_SINK_VIDEO);
        sink_crop = v4l2_subdev_state_get_crop(sd_state,
                                               RKISP1_ISP_PAD_SINK_VIDEO);
        src_frm = v4l2_subdev_state_get_format(sd_state,
                                               RKISP1_ISP_PAD_SOURCE_VIDEO);

        sink_fmt = rkisp1_mbus_info_get_by_code(sink_frm->code);
        src_fmt = rkisp1_mbus_info_get_by_code(src_frm->code);

        if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
                acq_mult = 1;
                if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
                        if (mbus_type == V4L2_MBUS_BT656)
                                isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT_ITU656;
                        else
                                isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT;
                } else {
                        rkisp1_write(rkisp1, RKISP1_CIF_ISP_DEMOSAIC,
                                     RKISP1_CIF_ISP_DEMOSAIC_TH(0xc));

                        if (mbus_type == V4L2_MBUS_BT656)
                                isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU656;
                        else
                                isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601;
                }
        } else if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_YUV) {
                acq_mult = 2;
                if (mbus_type == V4L2_MBUS_CSI2_DPHY) {
                        isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601;
                } else {
                        if (mbus_type == V4L2_MBUS_BT656)
                                isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU656;
                        else
                                isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601;
                }

                irq_mask |= RKISP1_CIF_ISP_DATA_LOSS;
        }

        /* Set up input acquisition properties */
        if (mbus_type == V4L2_MBUS_BT656 || mbus_type == V4L2_MBUS_PARALLEL) {
                if (mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
                        acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_POS_EDGE;

                switch (sink_fmt->bus_width) {
                case 8:
                        acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO;
                        break;
                case 10:
                        acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO;
                        break;
                case 12:
                        acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_12B;
                        break;
                default:
                        dev_err(rkisp1->dev, "Invalid bus width %u\n",
                                sink_fmt->bus_width);
                        return -EINVAL;
                }
        }

        if (mbus_type == V4L2_MBUS_PARALLEL) {
                if (mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
                        acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_VSYNC_LOW;

                if (mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
                        acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_HSYNC_LOW;
        }

        rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, isp_ctrl);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_PROP,
                     acq_prop | sink_fmt->yuv_seq |
                     RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT(sink_fmt->bayer_pat) |
                     RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_ALL);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_NR_FRAMES, 0);

        /* Acquisition Size */
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_H_OFFS, 0);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_V_OFFS, 0);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_H_SIZE,
                     acq_mult * sink_frm->width);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_V_SIZE, sink_frm->height);

        /* ISP Out Area */
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_H_OFFS, sink_crop->left);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_V_OFFS, sink_crop->top);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_H_SIZE, sink_crop->width);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_V_SIZE, sink_crop->height);

        irq_mask |= RKISP1_CIF_ISP_FRAME | RKISP1_CIF_ISP_V_START |
                    RKISP1_CIF_ISP_PIC_SIZE_ERROR;
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, irq_mask);

        if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
                rkisp1_params_disable(&rkisp1->params);
        } else {
                const struct v4l2_mbus_framefmt *src_frm;

                src_frm = v4l2_subdev_state_get_format(sd_state,
                                                       RKISP1_ISP_PAD_SOURCE_VIDEO);
                rkisp1_params_pre_configure(&rkisp1->params, sink_fmt->bayer_pat,
                                            src_frm->quantization,
                                            src_frm->ycbcr_enc);
        }

        isp->sink_fmt = sink_fmt;

        return 0;
}

/* Configure MUX */
static void rkisp1_config_path(struct rkisp1_isp *isp,
                               enum v4l2_mbus_type mbus_type)
{
        struct rkisp1_device *rkisp1 = isp->rkisp1;
        u32 dpcl = rkisp1_read(rkisp1, RKISP1_CIF_VI_DPCL);

        if (mbus_type == V4L2_MBUS_BT656 || mbus_type == V4L2_MBUS_PARALLEL)
                dpcl |= RKISP1_CIF_VI_DPCL_IF_SEL_PARALLEL;
        else if (mbus_type == V4L2_MBUS_CSI2_DPHY)
                dpcl |= RKISP1_CIF_VI_DPCL_IF_SEL_MIPI;

        rkisp1_write(rkisp1, RKISP1_CIF_VI_DPCL, dpcl);
}

/* Hardware configure Entry */
static int rkisp1_config_cif(struct rkisp1_isp *isp,
                             struct v4l2_subdev_state *sd_state,
                             enum v4l2_mbus_type mbus_type, u32 mbus_flags)
{
        int ret;

        ret = rkisp1_config_isp(isp, sd_state, mbus_type, mbus_flags);
        if (ret)
                return ret;

        rkisp1_config_path(isp, mbus_type);
        rkisp1_config_ism(isp, sd_state);

        return 0;
}

static void rkisp1_isp_stop(struct rkisp1_isp *isp)
{
        struct rkisp1_device *rkisp1 = isp->rkisp1;
        u32 val;

        /*
         * ISP(mi) stop in mi frame end -> Stop ISP(mipi) ->
         * Stop ISP(isp) ->wait for ISP isp off
         */

        /* Mask MI and ISP interrupts */
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, 0);
        rkisp1_write(rkisp1, RKISP1_CIF_MI_IMSC, 0);

        /* Flush posted writes */
        rkisp1_read(rkisp1, RKISP1_CIF_MI_IMSC);

        /*
         * Wait until the IRQ handler has ended. The IRQ handler may get called
         * even after this, but it will return immediately as the MI and ISP
         * interrupts have been masked.
         */
        synchronize_irq(rkisp1->irqs[RKISP1_IRQ_ISP]);
        if (rkisp1->irqs[RKISP1_IRQ_ISP] != rkisp1->irqs[RKISP1_IRQ_MI])
                synchronize_irq(rkisp1->irqs[RKISP1_IRQ_MI]);

        /* Clear MI and ISP interrupt status */
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, ~0);
        rkisp1_write(rkisp1, RKISP1_CIF_MI_ICR, ~0);

        /* stop ISP */
        val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
        val &= ~(RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE |
                 RKISP1_CIF_ISP_CTRL_ISP_ENABLE);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);

        val = rkisp1_read(rkisp1,       RKISP1_CIF_ISP_CTRL);
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL,
                     val | RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD);

        readx_poll_timeout(readl, rkisp1->base_addr + RKISP1_CIF_ISP_RIS,
                           val, val & RKISP1_CIF_ISP_OFF, 20, 100);
        rkisp1_write(rkisp1, RKISP1_CIF_VI_IRCL,
                     RKISP1_CIF_VI_IRCL_MIPI_SW_RST |
                     RKISP1_CIF_VI_IRCL_ISP_SW_RST);
        rkisp1_write(rkisp1, RKISP1_CIF_VI_IRCL, 0x0);

        if (rkisp1->info->isp_ver == RKISP1_V_IMX8MP)
                rkisp1_gasket_disable(rkisp1);
}

static void rkisp1_config_clk(struct rkisp1_isp *isp)
{
        struct rkisp1_device *rkisp1 = isp->rkisp1;

        u32 val = RKISP1_CIF_VI_ICCL_ISP_CLK | RKISP1_CIF_VI_ICCL_CP_CLK |
                  RKISP1_CIF_VI_ICCL_MRSZ_CLK | RKISP1_CIF_VI_ICCL_SRSZ_CLK |
                  RKISP1_CIF_VI_ICCL_JPEG_CLK | RKISP1_CIF_VI_ICCL_MI_CLK |
                  RKISP1_CIF_VI_ICCL_IE_CLK | RKISP1_CIF_VI_ICCL_MIPI_CLK |
                  RKISP1_CIF_VI_ICCL_DCROP_CLK;

        rkisp1_write(rkisp1, RKISP1_CIF_VI_ICCL, val);

        /* ensure sp and mp can run at the same time in V12 */
        if (rkisp1->info->isp_ver == RKISP1_V12) {
                val = RKISP1_CIF_CLK_CTRL_MI_Y12 | RKISP1_CIF_CLK_CTRL_MI_SP |
                      RKISP1_CIF_CLK_CTRL_MI_RAW0 | RKISP1_CIF_CLK_CTRL_MI_RAW1 |
                      RKISP1_CIF_CLK_CTRL_MI_READ | RKISP1_CIF_CLK_CTRL_MI_RAWRD |
                      RKISP1_CIF_CLK_CTRL_CP | RKISP1_CIF_CLK_CTRL_IE;
                rkisp1_write(rkisp1, RKISP1_CIF_VI_ISP_CLK_CTRL_V12, val);
        }
}

static int rkisp1_isp_start(struct rkisp1_isp *isp,
                            const struct v4l2_subdev_state *sd_state,
                            struct media_pad *source)
{
        struct rkisp1_device *rkisp1 = isp->rkisp1;
        const struct v4l2_mbus_framefmt *src_fmt;
        const struct rkisp1_mbus_info *src_info;
        u32 val;
        int ret;

        rkisp1_config_clk(isp);

        if (rkisp1->info->isp_ver == RKISP1_V_IMX8MP) {
                ret = rkisp1_gasket_enable(rkisp1, source);
                if (ret)
                        return ret;
        }

        /* Activate ISP */
        val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL);
        val |= RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD |
               RKISP1_CIF_ISP_CTRL_ISP_ENABLE |
               RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE;
        rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);

        src_fmt = v4l2_subdev_state_get_format(sd_state,
                                               RKISP1_ISP_PAD_SOURCE_VIDEO);
        src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);

        if (src_info->pixel_enc != V4L2_PIXEL_ENC_BAYER)
                rkisp1_params_post_configure(&rkisp1->params);

        return 0;
}

/* ----------------------------------------------------------------------------
 * Subdev pad operations
 */

static inline struct rkisp1_isp *to_rkisp1_isp(struct v4l2_subdev *sd)
{
        return container_of(sd, struct rkisp1_isp, sd);
}

static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd,
                                     struct v4l2_subdev_state *sd_state,
                                     struct v4l2_subdev_mbus_code_enum *code)
{
        unsigned int i, dir;
        int pos = 0;

        if (code->pad == RKISP1_ISP_PAD_SINK_VIDEO) {
                dir = RKISP1_ISP_SD_SINK;
        } else if (code->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) {
                dir = RKISP1_ISP_SD_SRC;
        } else {
                if (code->index > 0)
                        return -EINVAL;
                code->code = MEDIA_BUS_FMT_METADATA_FIXED;
                return 0;
        }

        for (i = 0; ; i++) {
                const struct rkisp1_mbus_info *fmt =
                        rkisp1_mbus_info_get_by_index(i);

                if (!fmt)
                        return -EINVAL;

                if (fmt->direction & dir)
                        pos++;

                if (code->index == pos - 1) {
                        code->code = fmt->mbus_code;
                        if (fmt->pixel_enc == V4L2_PIXEL_ENC_YUV &&
                            dir == RKISP1_ISP_SD_SRC)
                                code->flags =
                                        V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION;
                        return 0;
                }
        }

        return -EINVAL;
}

static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd,
                                      struct v4l2_subdev_state *sd_state,
                                      struct v4l2_subdev_frame_size_enum *fse)
{
        struct rkisp1_isp *isp = to_rkisp1_isp(sd);
        const struct rkisp1_mbus_info *mbus_info;

        if (fse->pad == RKISP1_ISP_PAD_SINK_PARAMS ||
            fse->pad == RKISP1_ISP_PAD_SOURCE_STATS)
                return -ENOTTY;

        if (fse->index > 0)
                return -EINVAL;

        mbus_info = rkisp1_mbus_info_get_by_code(fse->code);
        if (!mbus_info)
                return -EINVAL;

        if (!(mbus_info->direction & RKISP1_ISP_SD_SINK) &&
            fse->pad == RKISP1_ISP_PAD_SINK_VIDEO)
                return -EINVAL;

        if (!(mbus_info->direction & RKISP1_ISP_SD_SRC) &&
            fse->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
                return -EINVAL;

        fse->min_width = RKISP1_ISP_MIN_WIDTH;
        fse->max_width = isp->rkisp1->info->max_width;
        fse->min_height = RKISP1_ISP_MIN_HEIGHT;
        fse->max_height = isp->rkisp1->info->max_height;

        return 0;
}

static int rkisp1_isp_init_state(struct v4l2_subdev *sd,
                                 struct v4l2_subdev_state *sd_state)
{
        struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
        struct v4l2_rect *sink_crop, *src_crop;

        /* Video. */
        sink_fmt = v4l2_subdev_state_get_format(sd_state,
                                                RKISP1_ISP_PAD_SINK_VIDEO);
        sink_fmt->width = RKISP1_DEFAULT_WIDTH;
        sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
        sink_fmt->field = V4L2_FIELD_NONE;
        sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT;
        sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
        sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
        sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
        sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;

        sink_crop = v4l2_subdev_state_get_crop(sd_state,
                                               RKISP1_ISP_PAD_SINK_VIDEO);
        sink_crop->width = RKISP1_DEFAULT_WIDTH;
        sink_crop->height = RKISP1_DEFAULT_HEIGHT;
        sink_crop->left = 0;
        sink_crop->top = 0;

        src_fmt = v4l2_subdev_state_get_format(sd_state,
                                               RKISP1_ISP_PAD_SOURCE_VIDEO);
        *src_fmt = *sink_fmt;
        src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
        src_fmt->colorspace = V4L2_COLORSPACE_SRGB;
        src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
        src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
        src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;

        src_crop = v4l2_subdev_state_get_crop(sd_state,
                                              RKISP1_ISP_PAD_SOURCE_VIDEO);
        *src_crop = *sink_crop;

        /* Parameters and statistics. */
        sink_fmt = v4l2_subdev_state_get_format(sd_state,
                                                RKISP1_ISP_PAD_SINK_PARAMS);
        src_fmt = v4l2_subdev_state_get_format(sd_state,
                                               RKISP1_ISP_PAD_SOURCE_STATS);
        sink_fmt->width = 0;
        sink_fmt->height = 0;
        sink_fmt->field = V4L2_FIELD_NONE;
        sink_fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
        *src_fmt = *sink_fmt;

        return 0;
}

static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp,
                                   struct v4l2_subdev_state *sd_state,
                                   struct v4l2_mbus_framefmt *format)
{
        const struct rkisp1_mbus_info *sink_info;
        const struct rkisp1_mbus_info *src_info;
        struct v4l2_mbus_framefmt *sink_fmt;
        struct v4l2_mbus_framefmt *src_fmt;
        const struct v4l2_rect *src_crop;
        bool set_csc;

        sink_fmt = v4l2_subdev_state_get_format(sd_state,
                                                RKISP1_ISP_PAD_SINK_VIDEO);
        src_fmt = v4l2_subdev_state_get_format(sd_state,
                                               RKISP1_ISP_PAD_SOURCE_VIDEO);
        src_crop = v4l2_subdev_state_get_crop(sd_state,
                                              RKISP1_ISP_PAD_SOURCE_VIDEO);

        /*
         * Media bus code. The ISP can operate in pass-through mode (Bayer in,
         * Bayer out or YUV in, YUV out) or process Bayer data to YUV, but
         * can't convert from YUV to Bayer.
         */
        sink_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);

        src_fmt->code = format->code;
        src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
        if (!src_info || !(src_info->direction & RKISP1_ISP_SD_SRC)) {
                src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
                src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
        }

        if (sink_info->pixel_enc == V4L2_PIXEL_ENC_YUV &&
            src_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
                src_fmt->code = sink_fmt->code;
                src_info = sink_info;
        }

        /*
         * The source width and height must be identical to the source crop
         * size.
         */
        src_fmt->width  = src_crop->width;
        src_fmt->height = src_crop->height;

        /*
         * Copy the color space for the sink pad. When converting from Bayer to
         * YUV, default to a limited quantization range.
         */
        src_fmt->colorspace = sink_fmt->colorspace;
        src_fmt->xfer_func = sink_fmt->xfer_func;
        src_fmt->ycbcr_enc = sink_fmt->ycbcr_enc;

        if (sink_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&
            src_info->pixel_enc == V4L2_PIXEL_ENC_YUV)
                src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
        else
                src_fmt->quantization = sink_fmt->quantization;

        /*
         * Allow setting the source color space fields when the SET_CSC flag is
         * set and the source format is YUV. If the sink format is YUV, don't
         * set the color primaries, transfer function or YCbCr encoding as the
         * ISP is bypassed in that case and passes YUV data through without
         * modifications.
         *
         * The color primaries and transfer function are configured through the
         * cross-talk matrix and tone curve respectively. Settings for those
         * hardware blocks are conveyed through the ISP parameters buffer, as
         * they need to combine color space information with other image tuning
         * characteristics and can't thus be computed by the kernel based on the
         * color space. The source pad colorspace and xfer_func fields are thus
         * ignored by the driver, but can be set by userspace to propagate
         * accurate color space information down the pipeline.
         */
        set_csc = format->flags & V4L2_MBUS_FRAMEFMT_SET_CSC;

        if (set_csc && src_info->pixel_enc == V4L2_PIXEL_ENC_YUV) {
                if (sink_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
                        if (format->colorspace != V4L2_COLORSPACE_DEFAULT)
                                src_fmt->colorspace = format->colorspace;
                        if (format->xfer_func != V4L2_XFER_FUNC_DEFAULT)
                                src_fmt->xfer_func = format->xfer_func;
                        if (format->ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT)
                                src_fmt->ycbcr_enc = format->ycbcr_enc;
                }

                if (format->quantization != V4L2_QUANTIZATION_DEFAULT)
                        src_fmt->quantization = format->quantization;
        }

        *format = *src_fmt;

        /*
         * Restore the SET_CSC flag if it was set to indicate support for the
         * CSC setting API.
         */
        if (set_csc)
                format->flags |= V4L2_MBUS_FRAMEFMT_SET_CSC;
}

static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp,
                                    struct v4l2_subdev_state *sd_state,
                                    struct v4l2_rect *r)
{
        struct v4l2_mbus_framefmt *src_fmt;
        const struct v4l2_rect *sink_crop;
        struct v4l2_rect *src_crop;

        src_crop = v4l2_subdev_state_get_crop(sd_state,
                                              RKISP1_ISP_PAD_SOURCE_VIDEO);
        sink_crop = v4l2_subdev_state_get_crop(sd_state,
                                               RKISP1_ISP_PAD_SINK_VIDEO);

        src_crop->left = ALIGN(r->left, 2);
        src_crop->width = ALIGN(r->width, 2);
        src_crop->top = r->top;
        src_crop->height = r->height;
        rkisp1_sd_adjust_crop_rect(src_crop, sink_crop);

        *r = *src_crop;

        /* Propagate to out format */
        src_fmt = v4l2_subdev_state_get_format(sd_state,
                                               RKISP1_ISP_PAD_SOURCE_VIDEO);
        rkisp1_isp_set_src_fmt(isp, sd_state, src_fmt);
}

static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp,
                                     struct v4l2_subdev_state *sd_state,
                                     struct v4l2_rect *r)
{
        struct v4l2_rect *sink_crop, *src_crop;
        const struct v4l2_mbus_framefmt *sink_fmt;

        sink_crop = v4l2_subdev_state_get_crop(sd_state,
                                               RKISP1_ISP_PAD_SINK_VIDEO);
        sink_fmt = v4l2_subdev_state_get_format(sd_state,
                                                RKISP1_ISP_PAD_SINK_VIDEO);

        sink_crop->left = ALIGN(r->left, 2);
        sink_crop->width = ALIGN(r->width, 2);
        sink_crop->top = r->top;
        sink_crop->height = r->height;
        rkisp1_sd_adjust_crop(sink_crop, sink_fmt);

        *r = *sink_crop;

        /* Propagate to out crop */
        src_crop = v4l2_subdev_state_get_crop(sd_state,
                                              RKISP1_ISP_PAD_SOURCE_VIDEO);
        rkisp1_isp_set_src_crop(isp, sd_state, src_crop);
}

static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp,
                                    struct v4l2_subdev_state *sd_state,
                                    struct v4l2_mbus_framefmt *format)
{
        const struct rkisp1_mbus_info *mbus_info;
        struct v4l2_mbus_framefmt *sink_fmt;
        struct v4l2_rect *sink_crop;
        bool is_yuv;

        sink_fmt = v4l2_subdev_state_get_format(sd_state,
                                                RKISP1_ISP_PAD_SINK_VIDEO);
        sink_fmt->code = format->code;
        mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
        if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) {
                sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT;
                mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
        }

        sink_fmt->width = clamp_t(u32, format->width,
                                  RKISP1_ISP_MIN_WIDTH,
                                  isp->rkisp1->info->max_width);
        sink_fmt->height = clamp_t(u32, format->height,
                                   RKISP1_ISP_MIN_HEIGHT,
                                   isp->rkisp1->info->max_height);

        /*
         * Adjust the color space fields. Accept any color primaries and
         * transfer function for both YUV and Bayer. For YUV any YCbCr encoding
         * and quantization range is also accepted. For Bayer formats, the YCbCr
         * encoding isn't applicable, and the quantization range can only be
         * full.
         */
        is_yuv = mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV;

        sink_fmt->colorspace = format->colorspace ? :
                               (is_yuv ? V4L2_COLORSPACE_SRGB :
                                V4L2_COLORSPACE_RAW);
        sink_fmt->xfer_func = format->xfer_func ? :
                              V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace);
        if (is_yuv) {
                sink_fmt->ycbcr_enc = format->ycbcr_enc ? :
                        V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace);
                sink_fmt->quantization = format->quantization ? :
                        V4L2_MAP_QUANTIZATION_DEFAULT(false, sink_fmt->colorspace,
                                                      sink_fmt->ycbcr_enc);
        } else {
                /*
                 * The YCbCr encoding isn't applicable for non-YUV formats, but
                 * V4L2 has no "no encoding" value. Hardcode it to Rec. 601, it
                 * should be ignored by userspace.
                 */
                sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
                sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
        }

        *format = *sink_fmt;

        /* Propagate to in crop */
        sink_crop = v4l2_subdev_state_get_crop(sd_state,
                                               RKISP1_ISP_PAD_SINK_VIDEO);
        rkisp1_isp_set_sink_crop(isp, sd_state, sink_crop);
}

static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd,
                              struct v4l2_subdev_state *sd_state,
                              struct v4l2_subdev_format *fmt)
{
        struct rkisp1_isp *isp = to_rkisp1_isp(sd);

        if (fmt->pad == RKISP1_ISP_PAD_SINK_VIDEO)
                rkisp1_isp_set_sink_fmt(isp, sd_state, &fmt->format);
        else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
                rkisp1_isp_set_src_fmt(isp, sd_state, &fmt->format);
        else
                fmt->format = *v4l2_subdev_state_get_format(sd_state,
                                                            fmt->pad);

        return 0;
}

static int rkisp1_isp_get_selection(struct v4l2_subdev *sd,
                                    struct v4l2_subdev_state *sd_state,
                                    struct v4l2_subdev_selection *sel)
{
        int ret = 0;

        if (sel->pad != RKISP1_ISP_PAD_SOURCE_VIDEO &&
            sel->pad != RKISP1_ISP_PAD_SINK_VIDEO)
                return -EINVAL;

        switch (sel->target) {
        case V4L2_SEL_TGT_CROP_BOUNDS:
                if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) {
                        struct v4l2_mbus_framefmt *fmt;

                        fmt = v4l2_subdev_state_get_format(sd_state, sel->pad);
                        sel->r.height = fmt->height;
                        sel->r.width = fmt->width;
                        sel->r.left = 0;
                        sel->r.top = 0;
                } else {
                        sel->r = *v4l2_subdev_state_get_crop(sd_state,
                                                             RKISP1_ISP_PAD_SINK_VIDEO);
                }
                break;

        case V4L2_SEL_TGT_CROP:
                sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad);
                break;

        default:
                ret = -EINVAL;
                break;
        }

        return ret;
}

static int rkisp1_isp_set_selection(struct v4l2_subdev *sd,
                                    struct v4l2_subdev_state *sd_state,
                                    struct v4l2_subdev_selection *sel)
{
        struct rkisp1_isp *isp = to_rkisp1_isp(sd);
        int ret = 0;

        if (sel->target != V4L2_SEL_TGT_CROP)
                return -EINVAL;

        dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%ux%u\n", __func__,
                sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);

        if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO)
                rkisp1_isp_set_sink_crop(isp, sd_state, &sel->r);
        else if (sel->pad == RKISP1_ISP_PAD_SOURCE_VIDEO)
                rkisp1_isp_set_src_crop(isp, sd_state, &sel->r);
        else
                ret = -EINVAL;

        return ret;
}

static int rkisp1_subdev_link_validate(struct media_link *link)
{
        if (link->sink->index == RKISP1_ISP_PAD_SINK_PARAMS)
                return 0;

        return v4l2_subdev_link_validate(link);
}

static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = {
        .enum_mbus_code = rkisp1_isp_enum_mbus_code,
        .enum_frame_size = rkisp1_isp_enum_frame_size,
        .get_selection = rkisp1_isp_get_selection,
        .set_selection = rkisp1_isp_set_selection,
        .get_fmt = v4l2_subdev_get_fmt,
        .set_fmt = rkisp1_isp_set_fmt,
        .link_validate = v4l2_subdev_link_validate_default,
};

/* ----------------------------------------------------------------------------
 * Stream operations
 */

static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable)
{
        struct rkisp1_isp *isp = to_rkisp1_isp(sd);
        struct rkisp1_device *rkisp1 = isp->rkisp1;
        struct v4l2_subdev_state *sd_state;
        struct media_pad *source_pad;
        struct media_pad *sink_pad;
        enum v4l2_mbus_type mbus_type;
        u32 mbus_flags;
        int ret;

        if (!enable) {
                v4l2_subdev_call(rkisp1->source, video, s_stream, false);
                rkisp1_isp_stop(isp);
                return 0;
        }

        sink_pad = &isp->pads[RKISP1_ISP_PAD_SINK_VIDEO];
        source_pad = media_pad_remote_pad_unique(sink_pad);
        if (IS_ERR(source_pad)) {
                dev_dbg(rkisp1->dev, "Failed to get source for ISP: %pe\n",
                        source_pad);
                return -EPIPE;
        }

        rkisp1->source = media_entity_to_v4l2_subdev(source_pad->entity);
        if (!rkisp1->source) {
                /* This should really not happen, so is not worth a message. */
                return -EPIPE;
        }

        if (rkisp1->source == &rkisp1->csi.sd) {
                mbus_type = V4L2_MBUS_CSI2_DPHY;
                mbus_flags = 0;
        } else {
                const struct rkisp1_sensor_async *asd;
                struct v4l2_async_connection *asc;

                asc = v4l2_async_connection_unique(rkisp1->source);
                if (!asc)
                        return -EPIPE;

                asd = container_of(asc, struct rkisp1_sensor_async, asd);

                mbus_type = asd->mbus_type;
                mbus_flags = asd->mbus_flags;
        }

        isp->frame_sequence = -1;
        isp->frame_active = false;

        sd_state = v4l2_subdev_lock_and_get_active_state(sd);

        ret = rkisp1_config_cif(isp, sd_state, mbus_type, mbus_flags);
        if (ret)
                goto out_unlock;

        ret = rkisp1_isp_start(isp, sd_state, source_pad);
        if (ret)
                goto out_unlock;

        ret = v4l2_subdev_call(rkisp1->source, video, s_stream, true);
        if (ret) {
                rkisp1_isp_stop(isp);
                goto out_unlock;
        }

out_unlock:
        v4l2_subdev_unlock_state(sd_state);
        return ret;
}

static int rkisp1_isp_subs_evt(struct v4l2_subdev *sd, struct v4l2_fh *fh,
                               struct v4l2_event_subscription *sub)
{
        if (sub->type != V4L2_EVENT_FRAME_SYNC)
                return -EINVAL;

        /* V4L2_EVENT_FRAME_SYNC doesn't require an id, so zero should be set */
        if (sub->id != 0)
                return -EINVAL;

        return v4l2_event_subscribe(fh, sub, 0, NULL);
}

static const struct media_entity_operations rkisp1_isp_media_ops = {
        .link_validate = rkisp1_subdev_link_validate,
};

static const struct v4l2_subdev_video_ops rkisp1_isp_video_ops = {
        .s_stream = rkisp1_isp_s_stream,
};

static const struct v4l2_subdev_core_ops rkisp1_isp_core_ops = {
        .subscribe_event = rkisp1_isp_subs_evt,
        .unsubscribe_event = v4l2_event_subdev_unsubscribe,
};

static const struct v4l2_subdev_ops rkisp1_isp_ops = {
        .core = &rkisp1_isp_core_ops,
        .video = &rkisp1_isp_video_ops,
        .pad = &rkisp1_isp_pad_ops,
};

static const struct v4l2_subdev_internal_ops rkisp1_isp_internal_ops = {
        .init_state = rkisp1_isp_init_state,
};

int rkisp1_isp_register(struct rkisp1_device *rkisp1)
{
        struct rkisp1_isp *isp = &rkisp1->isp;
        struct media_pad *pads = isp->pads;
        struct v4l2_subdev *sd = &isp->sd;
        int ret;

        isp->rkisp1 = rkisp1;

        v4l2_subdev_init(sd, &rkisp1_isp_ops);
        sd->internal_ops = &rkisp1_isp_internal_ops;
        sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
        sd->entity.ops = &rkisp1_isp_media_ops;
        sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
        sd->owner = THIS_MODULE;
        strscpy(sd->name, RKISP1_ISP_DEV_NAME, sizeof(sd->name));

        pads[RKISP1_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK |
                                                MEDIA_PAD_FL_MUST_CONNECT;
        pads[RKISP1_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
        pads[RKISP1_ISP_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE;
        pads[RKISP1_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;

        ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX, pads);
        if (ret)
                goto err_entity_cleanup;

        ret = v4l2_subdev_init_finalize(sd);
        if (ret)
                goto err_subdev_cleanup;

        ret = v4l2_device_register_subdev(&rkisp1->v4l2_dev, sd);
        if (ret) {
                dev_err(rkisp1->dev, "Failed to register isp subdev\n");
                goto err_subdev_cleanup;
        }

        return 0;

err_subdev_cleanup:
        v4l2_subdev_cleanup(sd);
err_entity_cleanup:
        media_entity_cleanup(&sd->entity);
        isp->sd.v4l2_dev = NULL;
        return ret;
}

void rkisp1_isp_unregister(struct rkisp1_device *rkisp1)
{
        struct rkisp1_isp *isp = &rkisp1->isp;

        if (!isp->sd.v4l2_dev)
                return;

        v4l2_device_unregister_subdev(&isp->sd);
        v4l2_subdev_cleanup(&isp->sd);
        media_entity_cleanup(&isp->sd.entity);
}

/* ----------------------------------------------------------------------------
 * Interrupt handlers
 */

static void rkisp1_isp_sof(struct rkisp1_isp *isp)
{
        struct v4l2_event event = {
                .type = V4L2_EVENT_FRAME_SYNC,
        };

        isp->frame_sequence++;
        isp->frame_active = true;

        event.u.frame_sync.frame_sequence = isp->frame_sequence;
        v4l2_event_queue(isp->sd.devnode, &event);
}

irqreturn_t rkisp1_isp_isr(int irq, void *ctx)
{
        struct device *dev = ctx;
        struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
        u32 status, isp_err;

        if (!rkisp1->irqs_enabled)
                return IRQ_NONE;

        status = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS);
        if (!status)
                return IRQ_NONE;

        rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, status);

        /*
         * Vertical sync signal, starting new frame. Defer handling of vsync
         * after RKISP1_CIF_ISP_FRAME if the previous frame was not completed
         * yet.
         */
        if (status & RKISP1_CIF_ISP_V_START && !rkisp1->isp.frame_active) {
                status &= ~RKISP1_CIF_ISP_V_START;
                rkisp1_isp_sof(&rkisp1->isp);
                if (status & RKISP1_CIF_ISP_FRAME) {
                        WARN_ONCE(1, "irq delay is too long, buffers might not be in sync\n");
                        rkisp1->debug.irq_delay++;
                }
        }

        if (status & RKISP1_CIF_ISP_PIC_SIZE_ERROR) {
                /* Clear pic_size_error */
                isp_err = rkisp1_read(rkisp1, RKISP1_CIF_ISP_ERR);
                if (isp_err & RKISP1_CIF_ISP_ERR_INFORM_SIZE)
                        rkisp1->debug.inform_size_error++;
                if (isp_err & RKISP1_CIF_ISP_ERR_IS_SIZE)
                        rkisp1->debug.img_stabilization_size_error++;
                if (isp_err & RKISP1_CIF_ISP_ERR_OUTFORM_SIZE)
                        rkisp1->debug.outform_size_error++;
                rkisp1_write(rkisp1, RKISP1_CIF_ISP_ERR_CLR, isp_err);
        } else if (status & RKISP1_CIF_ISP_DATA_LOSS) {
                /* keep track of data_loss in debugfs */
                rkisp1->debug.data_loss++;
        }

        if (status & RKISP1_CIF_ISP_FRAME) {
                u32 isp_ris;

                rkisp1->isp.frame_active = false;
                rkisp1->debug.complete_frames++;

                /* New frame from the sensor received */
                isp_ris = rkisp1_read(rkisp1, RKISP1_CIF_ISP_RIS);
                if (isp_ris & RKISP1_STATS_MEAS_MASK)
                        rkisp1_stats_isr(&rkisp1->stats, isp_ris);
                /*
                 * Then update changed configs. Some of them involve
                 * lot of register writes. Do those only one per frame.
                 * Do the updates in the order of the processing flow.
                 */
                rkisp1_params_isr(rkisp1);
        }

        /*
         * Deferred handling of vsync if RKISP1_CIF_ISP_V_START and
         * RKISP1_CIF_ISP_FRAME occurred in the same irq.
         */
        if (status & RKISP1_CIF_ISP_V_START)
                rkisp1_isp_sof(&rkisp1->isp);

        return IRQ_HANDLED;
}