Contributors: 11
Author Tokens Token Proportion Commits Commit Proportion
Helen Mae Koike Fornazier 2695 57.86% 3 4.48%
Laurent Pinchart 1252 26.88% 29 43.28%
Dafna Hirschfeld 189 4.06% 17 25.37%
Sebastian Fricke 132 2.83% 1 1.49%
Tomi Valkeinen 123 2.64% 4 5.97%
Jacob Chen 92 1.98% 2 2.99%
Paul Elder 84 1.80% 3 4.48%
Sakari Ailus 42 0.90% 3 4.48%
Heiko Stübner 37 0.79% 2 2.99%
Steve Longerbeam 11 0.24% 2 2.99%
Hans Verkuil 1 0.02% 1 1.49%
Total 4658 67


// 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,
			      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,
			     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 {
		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,
			    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)
{
	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 = RKISP1_ISP_MAX_WIDTH;
	fse->min_height = RKISP1_ISP_MIN_HEIGHT;
	fse->max_height = RKISP1_ISP_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,
				  RKISP1_ISP_MAX_WIDTH);
	sink_fmt->height = clamp_t(u32, format->height,
				   RKISP1_ISP_MIN_HEIGHT,
				   RKISP1_ISP_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)/%dx%d\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: %ld\n",
			PTR_ERR(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;

	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_queue_event_sof(struct rkisp1_isp *isp)
{
	struct v4l2_event event = {
		.type = V4L2_EVENT_FRAME_SYNC,
	};

	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 generating new frame */
	if (status & RKISP1_CIF_ISP_V_START) {
		rkisp1->isp.frame_sequence++;
		rkisp1_isp_queue_event_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->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);
	}

	return IRQ_HANDLED;
}