162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Microchip Image Sensor Controller (ISC) common driver base
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016-2019 Microchip Technology, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Songjun Wu
862306a36Sopenharmony_ci * Author: Eugen Hristev <eugen.hristev@microchip.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/math64.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/of_graph.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1962306a36Sopenharmony_ci#include <linux/regmap.h>
2062306a36Sopenharmony_ci#include <linux/videodev2.h>
2162306a36Sopenharmony_ci#include <linux/atmel-isc-media.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <media/v4l2-ctrls.h>
2462306a36Sopenharmony_ci#include <media/v4l2-device.h>
2562306a36Sopenharmony_ci#include <media/v4l2-event.h>
2662306a36Sopenharmony_ci#include <media/v4l2-image-sizes.h>
2762306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2862306a36Sopenharmony_ci#include <media/v4l2-fwnode.h>
2962306a36Sopenharmony_ci#include <media/v4l2-subdev.h>
3062306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include "microchip-isc-regs.h"
3362306a36Sopenharmony_ci#include "microchip-isc.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define ISC_IS_FORMAT_RAW(mbus_code) \
3662306a36Sopenharmony_ci	(((mbus_code) & 0xf000) == 0x3000)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define ISC_IS_FORMAT_GREY(mbus_code) \
3962306a36Sopenharmony_ci	(((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \
4062306a36Sopenharmony_ci	(((mbus_code) == MEDIA_BUS_FMT_Y8_1X8)))
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic inline void isc_update_v4l2_ctrls(struct isc_device *isc)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct isc_ctrls *ctrls = &isc->ctrls;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* In here we set the v4l2 controls w.r.t. our pipeline config */
4762306a36Sopenharmony_ci	v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]);
4862306a36Sopenharmony_ci	v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]);
4962306a36Sopenharmony_ci	v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]);
5062306a36Sopenharmony_ci	v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]);
5362306a36Sopenharmony_ci	v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]);
5462306a36Sopenharmony_ci	v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]);
5562306a36Sopenharmony_ci	v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic inline void isc_update_awb_ctrls(struct isc_device *isc)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct isc_ctrls *ctrls = &isc->ctrls;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* In here we set our actual hw pipeline config */
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	regmap_write(isc->regmap, ISC_WB_O_RGR,
6562306a36Sopenharmony_ci		     ((ctrls->offset[ISC_HIS_CFG_MODE_R])) |
6662306a36Sopenharmony_ci		     ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16));
6762306a36Sopenharmony_ci	regmap_write(isc->regmap, ISC_WB_O_BGB,
6862306a36Sopenharmony_ci		     ((ctrls->offset[ISC_HIS_CFG_MODE_B])) |
6962306a36Sopenharmony_ci		     ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16));
7062306a36Sopenharmony_ci	regmap_write(isc->regmap, ISC_WB_G_RGR,
7162306a36Sopenharmony_ci		     ctrls->gain[ISC_HIS_CFG_MODE_R] |
7262306a36Sopenharmony_ci		     (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16));
7362306a36Sopenharmony_ci	regmap_write(isc->regmap, ISC_WB_G_BGB,
7462306a36Sopenharmony_ci		     ctrls->gain[ISC_HIS_CFG_MODE_B] |
7562306a36Sopenharmony_ci		     (ctrls->gain[ISC_HIS_CFG_MODE_GB] << 16));
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic inline void isc_reset_awb_ctrls(struct isc_device *isc)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	unsigned int c;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) {
8362306a36Sopenharmony_ci		/* gains have a fixed point at 9 decimals */
8462306a36Sopenharmony_ci		isc->ctrls.gain[c] = 1 << 9;
8562306a36Sopenharmony_ci		/* offsets are in 2's complements */
8662306a36Sopenharmony_ci		isc->ctrls.offset[c] = 0;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int isc_queue_setup(struct vb2_queue *vq,
9162306a36Sopenharmony_ci			   unsigned int *nbuffers, unsigned int *nplanes,
9262306a36Sopenharmony_ci			   unsigned int sizes[], struct device *alloc_devs[])
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct isc_device *isc = vb2_get_drv_priv(vq);
9562306a36Sopenharmony_ci	unsigned int size = isc->fmt.fmt.pix.sizeimage;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (*nplanes)
9862306a36Sopenharmony_ci		return sizes[0] < size ? -EINVAL : 0;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	*nplanes = 1;
10162306a36Sopenharmony_ci	sizes[0] = size;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return 0;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int isc_buffer_prepare(struct vb2_buffer *vb)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
10962306a36Sopenharmony_ci	struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue);
11062306a36Sopenharmony_ci	unsigned long size = isc->fmt.fmt.pix.sizeimage;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size) {
11362306a36Sopenharmony_ci		dev_err(isc->dev, "buffer too small (%lu < %lu)\n",
11462306a36Sopenharmony_ci			vb2_plane_size(vb, 0), size);
11562306a36Sopenharmony_ci		return -EINVAL;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	vb2_set_plane_payload(vb, 0, size);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	vbuf->field = isc->fmt.fmt.pix.field;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic void isc_crop_pfe(struct isc_device *isc)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
12862306a36Sopenharmony_ci	u32 h, w;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	h = isc->fmt.fmt.pix.height;
13162306a36Sopenharmony_ci	w = isc->fmt.fmt.pix.width;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/*
13462306a36Sopenharmony_ci	 * In case the sensor is not RAW, it will output a pixel (12-16 bits)
13562306a36Sopenharmony_ci	 * with two samples on the ISC Data bus (which is 8-12)
13662306a36Sopenharmony_ci	 * ISC will count each sample, so, we need to multiply these values
13762306a36Sopenharmony_ci	 * by two, to get the real number of samples for the required pixels.
13862306a36Sopenharmony_ci	 */
13962306a36Sopenharmony_ci	if (!ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) {
14062306a36Sopenharmony_ci		h <<= 1;
14162306a36Sopenharmony_ci		w <<= 1;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/*
14562306a36Sopenharmony_ci	 * We limit the column/row count that the ISC will output according
14662306a36Sopenharmony_ci	 * to the configured resolution that we want.
14762306a36Sopenharmony_ci	 * This will avoid the situation where the sensor is misconfigured,
14862306a36Sopenharmony_ci	 * sending more data, and the ISC will just take it and DMA to memory,
14962306a36Sopenharmony_ci	 * causing corruption.
15062306a36Sopenharmony_ci	 */
15162306a36Sopenharmony_ci	regmap_write(regmap, ISC_PFE_CFG1,
15262306a36Sopenharmony_ci		     (ISC_PFE_CFG1_COLMIN(0) & ISC_PFE_CFG1_COLMIN_MASK) |
15362306a36Sopenharmony_ci		     (ISC_PFE_CFG1_COLMAX(w - 1) & ISC_PFE_CFG1_COLMAX_MASK));
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	regmap_write(regmap, ISC_PFE_CFG2,
15662306a36Sopenharmony_ci		     (ISC_PFE_CFG2_ROWMIN(0) & ISC_PFE_CFG2_ROWMIN_MASK) |
15762306a36Sopenharmony_ci		     (ISC_PFE_CFG2_ROWMAX(h - 1) & ISC_PFE_CFG2_ROWMAX_MASK));
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	regmap_update_bits(regmap, ISC_PFE_CFG0,
16062306a36Sopenharmony_ci			   ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN,
16162306a36Sopenharmony_ci			   ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void isc_start_dma(struct isc_device *isc)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
16762306a36Sopenharmony_ci	u32 sizeimage = isc->fmt.fmt.pix.sizeimage;
16862306a36Sopenharmony_ci	u32 dctrl_dview;
16962306a36Sopenharmony_ci	dma_addr_t addr0;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0);
17262306a36Sopenharmony_ci	regmap_write(regmap, ISC_DAD0 + isc->offsets.dma, addr0);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	switch (isc->config.fourcc) {
17562306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV420:
17662306a36Sopenharmony_ci		regmap_write(regmap, ISC_DAD1 + isc->offsets.dma,
17762306a36Sopenharmony_ci			     addr0 + (sizeimage * 2) / 3);
17862306a36Sopenharmony_ci		regmap_write(regmap, ISC_DAD2 + isc->offsets.dma,
17962306a36Sopenharmony_ci			     addr0 + (sizeimage * 5) / 6);
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV422P:
18262306a36Sopenharmony_ci		regmap_write(regmap, ISC_DAD1 + isc->offsets.dma,
18362306a36Sopenharmony_ci			     addr0 + sizeimage / 2);
18462306a36Sopenharmony_ci		regmap_write(regmap, ISC_DAD2 + isc->offsets.dma,
18562306a36Sopenharmony_ci			     addr0 + (sizeimage * 3) / 4);
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	default:
18862306a36Sopenharmony_ci		break;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	dctrl_dview = isc->config.dctrl_dview;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	regmap_write(regmap, ISC_DCTRL + isc->offsets.dma,
19462306a36Sopenharmony_ci		     dctrl_dview | ISC_DCTRL_IE_IS);
19562306a36Sopenharmony_ci	spin_lock(&isc->awb_lock);
19662306a36Sopenharmony_ci	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE);
19762306a36Sopenharmony_ci	spin_unlock(&isc->awb_lock);
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic void isc_set_pipeline(struct isc_device *isc, u32 pipeline)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
20362306a36Sopenharmony_ci	struct isc_ctrls *ctrls = &isc->ctrls;
20462306a36Sopenharmony_ci	u32 val, bay_cfg;
20562306a36Sopenharmony_ci	const u32 *gamma;
20662306a36Sopenharmony_ci	unsigned int i;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */
20962306a36Sopenharmony_ci	for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
21062306a36Sopenharmony_ci		val = pipeline & BIT(i) ? 1 : 0;
21162306a36Sopenharmony_ci		regmap_field_write(isc->pipeline[i], val);
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (!pipeline)
21562306a36Sopenharmony_ci		return;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	bay_cfg = isc->config.sd_format->cfa_baycfg;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	regmap_write(regmap, ISC_WB_CFG, bay_cfg);
22062306a36Sopenharmony_ci	isc_update_awb_ctrls(isc);
22162306a36Sopenharmony_ci	isc_update_v4l2_ctrls(isc);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	gamma = &isc->gamma_table[ctrls->gamma_index][0];
22662306a36Sopenharmony_ci	regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES);
22762306a36Sopenharmony_ci	regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES);
22862306a36Sopenharmony_ci	regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	isc->config_dpc(isc);
23162306a36Sopenharmony_ci	isc->config_csc(isc);
23262306a36Sopenharmony_ci	isc->config_cbc(isc);
23362306a36Sopenharmony_ci	isc->config_cc(isc);
23462306a36Sopenharmony_ci	isc->config_gam(isc);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic int isc_update_profile(struct isc_device *isc)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
24062306a36Sopenharmony_ci	u32 sr;
24162306a36Sopenharmony_ci	int counter = 100;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	regmap_read(regmap, ISC_CTRLSR, &sr);
24662306a36Sopenharmony_ci	while ((sr & ISC_CTRL_UPPRO) && counter--) {
24762306a36Sopenharmony_ci		usleep_range(1000, 2000);
24862306a36Sopenharmony_ci		regmap_read(regmap, ISC_CTRLSR, &sr);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (counter < 0) {
25262306a36Sopenharmony_ci		v4l2_warn(&isc->v4l2_dev, "Time out to update profile\n");
25362306a36Sopenharmony_ci		return -ETIMEDOUT;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return 0;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void isc_set_histogram(struct isc_device *isc, bool enable)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
26262306a36Sopenharmony_ci	struct isc_ctrls *ctrls = &isc->ctrls;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (enable) {
26562306a36Sopenharmony_ci		regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his,
26662306a36Sopenharmony_ci			     ISC_HIS_CFG_MODE_GR |
26762306a36Sopenharmony_ci			     (isc->config.sd_format->cfa_baycfg
26862306a36Sopenharmony_ci					<< ISC_HIS_CFG_BAYSEL_SHIFT) |
26962306a36Sopenharmony_ci					ISC_HIS_CFG_RAR);
27062306a36Sopenharmony_ci		regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his,
27162306a36Sopenharmony_ci			     ISC_HIS_CTRL_EN);
27262306a36Sopenharmony_ci		regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE);
27362306a36Sopenharmony_ci		ctrls->hist_id = ISC_HIS_CFG_MODE_GR;
27462306a36Sopenharmony_ci		isc_update_profile(isc);
27562306a36Sopenharmony_ci		regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		ctrls->hist_stat = HIST_ENABLED;
27862306a36Sopenharmony_ci	} else {
27962306a36Sopenharmony_ci		regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE);
28062306a36Sopenharmony_ci		regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his,
28162306a36Sopenharmony_ci			     ISC_HIS_CTRL_DIS);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		ctrls->hist_stat = HIST_DISABLED;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int isc_configure(struct isc_device *isc)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
29062306a36Sopenharmony_ci	u32 pfe_cfg0, dcfg, mask, pipeline;
29162306a36Sopenharmony_ci	struct isc_subdev_entity *subdev = isc->current_subdev;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	pfe_cfg0 = isc->config.sd_format->pfe_cfg0_bps;
29462306a36Sopenharmony_ci	pipeline = isc->config.bits_pipeline;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	dcfg = isc->config.dcfg_imode | isc->dcfg;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	pfe_cfg0  |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE;
29962306a36Sopenharmony_ci	mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW |
30062306a36Sopenharmony_ci	       ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW |
30162306a36Sopenharmony_ci	       ISC_PFE_CFG0_MODE_MASK | ISC_PFE_CFG0_CCIR_CRC |
30262306a36Sopenharmony_ci	       ISC_PFE_CFG0_CCIR656 | ISC_PFE_CFG0_MIPI;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	isc->config_rlp(isc);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	regmap_write(regmap, ISC_DCFG + isc->offsets.dma, dcfg);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* Set the pipeline */
31162306a36Sopenharmony_ci	isc_set_pipeline(isc, pipeline);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/*
31462306a36Sopenharmony_ci	 * The current implemented histogram is available for RAW R, B, GB, GR
31562306a36Sopenharmony_ci	 * channels. We need to check if sensor is outputting RAW BAYER
31662306a36Sopenharmony_ci	 */
31762306a36Sopenharmony_ci	if (isc->ctrls.awb &&
31862306a36Sopenharmony_ci	    ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
31962306a36Sopenharmony_ci		isc_set_histogram(isc, true);
32062306a36Sopenharmony_ci	else
32162306a36Sopenharmony_ci		isc_set_histogram(isc, false);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* Update profile */
32462306a36Sopenharmony_ci	return isc_update_profile(isc);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic int isc_prepare_streaming(struct vb2_queue *vq)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct isc_device *isc = vb2_get_drv_priv(vq);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return media_pipeline_start(isc->video_dev.entity.pads, &isc->mpipe);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct isc_device *isc = vb2_get_drv_priv(vq);
33762306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
33862306a36Sopenharmony_ci	struct isc_buffer *buf;
33962306a36Sopenharmony_ci	unsigned long flags;
34062306a36Sopenharmony_ci	int ret;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Enable stream on the sub device */
34362306a36Sopenharmony_ci	ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1);
34462306a36Sopenharmony_ci	if (ret && ret != -ENOIOCTLCMD) {
34562306a36Sopenharmony_ci		dev_err(isc->dev, "stream on failed in subdev %d\n", ret);
34662306a36Sopenharmony_ci		goto err_start_stream;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(isc->dev);
35062306a36Sopenharmony_ci	if (ret < 0) {
35162306a36Sopenharmony_ci		dev_err(isc->dev, "RPM resume failed in subdev %d\n",
35262306a36Sopenharmony_ci			ret);
35362306a36Sopenharmony_ci		goto err_pm_get;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	ret = isc_configure(isc);
35762306a36Sopenharmony_ci	if (unlikely(ret))
35862306a36Sopenharmony_ci		goto err_configure;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* Enable DMA interrupt */
36162306a36Sopenharmony_ci	regmap_write(regmap, ISC_INTEN, ISC_INT_DDONE);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	spin_lock_irqsave(&isc->dma_queue_lock, flags);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	isc->sequence = 0;
36662306a36Sopenharmony_ci	isc->stop = false;
36762306a36Sopenharmony_ci	reinit_completion(&isc->comp);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	isc->cur_frm = list_first_entry(&isc->dma_queue,
37062306a36Sopenharmony_ci					struct isc_buffer, list);
37162306a36Sopenharmony_ci	list_del(&isc->cur_frm->list);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	isc_crop_pfe(isc);
37462306a36Sopenharmony_ci	isc_start_dma(isc);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* if we streaming from RAW, we can do one-shot white balance adj */
37962306a36Sopenharmony_ci	if (ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
38062306a36Sopenharmony_ci		v4l2_ctrl_activate(isc->do_wb_ctrl, true);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cierr_configure:
38562306a36Sopenharmony_ci	pm_runtime_put_sync(isc->dev);
38662306a36Sopenharmony_cierr_pm_get:
38762306a36Sopenharmony_ci	v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cierr_start_stream:
39062306a36Sopenharmony_ci	spin_lock_irqsave(&isc->dma_queue_lock, flags);
39162306a36Sopenharmony_ci	list_for_each_entry(buf, &isc->dma_queue, list)
39262306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
39362306a36Sopenharmony_ci	INIT_LIST_HEAD(&isc->dma_queue);
39462306a36Sopenharmony_ci	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return ret;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void isc_unprepare_streaming(struct vb2_queue *vq)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct isc_device *isc = vb2_get_drv_priv(vq);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* Stop media pipeline */
40462306a36Sopenharmony_ci	media_pipeline_stop(isc->video_dev.entity.pads);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic void isc_stop_streaming(struct vb2_queue *vq)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct isc_device *isc = vb2_get_drv_priv(vq);
41062306a36Sopenharmony_ci	unsigned long flags;
41162306a36Sopenharmony_ci	struct isc_buffer *buf;
41262306a36Sopenharmony_ci	int ret;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	mutex_lock(&isc->awb_mutex);
41562306a36Sopenharmony_ci	v4l2_ctrl_activate(isc->do_wb_ctrl, false);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	isc->stop = true;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* Wait until the end of the current frame */
42062306a36Sopenharmony_ci	if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ))
42162306a36Sopenharmony_ci		dev_err(isc->dev, "Timeout waiting for end of the capture\n");
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	mutex_unlock(&isc->awb_mutex);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/* Disable DMA interrupt */
42662306a36Sopenharmony_ci	regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	pm_runtime_put_sync(isc->dev);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/* Disable stream on the sub device */
43162306a36Sopenharmony_ci	ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0);
43262306a36Sopenharmony_ci	if (ret && ret != -ENOIOCTLCMD)
43362306a36Sopenharmony_ci		dev_err(isc->dev, "stream off failed in subdev\n");
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/* Release all active buffers */
43662306a36Sopenharmony_ci	spin_lock_irqsave(&isc->dma_queue_lock, flags);
43762306a36Sopenharmony_ci	if (unlikely(isc->cur_frm)) {
43862306a36Sopenharmony_ci		vb2_buffer_done(&isc->cur_frm->vb.vb2_buf,
43962306a36Sopenharmony_ci				VB2_BUF_STATE_ERROR);
44062306a36Sopenharmony_ci		isc->cur_frm = NULL;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci	list_for_each_entry(buf, &isc->dma_queue, list)
44362306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
44462306a36Sopenharmony_ci	INIT_LIST_HEAD(&isc->dma_queue);
44562306a36Sopenharmony_ci	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void isc_buffer_queue(struct vb2_buffer *vb)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
45162306a36Sopenharmony_ci	struct isc_buffer *buf = container_of(vbuf, struct isc_buffer, vb);
45262306a36Sopenharmony_ci	struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue);
45362306a36Sopenharmony_ci	unsigned long flags;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	spin_lock_irqsave(&isc->dma_queue_lock, flags);
45662306a36Sopenharmony_ci	if (!isc->cur_frm && list_empty(&isc->dma_queue) &&
45762306a36Sopenharmony_ci	    vb2_start_streaming_called(vb->vb2_queue)) {
45862306a36Sopenharmony_ci		isc->cur_frm = buf;
45962306a36Sopenharmony_ci		isc_start_dma(isc);
46062306a36Sopenharmony_ci	} else {
46162306a36Sopenharmony_ci		list_add_tail(&buf->list, &isc->dma_queue);
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic const struct vb2_ops isc_vb2_ops = {
46762306a36Sopenharmony_ci	.queue_setup		= isc_queue_setup,
46862306a36Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
46962306a36Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
47062306a36Sopenharmony_ci	.buf_prepare		= isc_buffer_prepare,
47162306a36Sopenharmony_ci	.start_streaming	= isc_start_streaming,
47262306a36Sopenharmony_ci	.stop_streaming		= isc_stop_streaming,
47362306a36Sopenharmony_ci	.buf_queue		= isc_buffer_queue,
47462306a36Sopenharmony_ci	.prepare_streaming	= isc_prepare_streaming,
47562306a36Sopenharmony_ci	.unprepare_streaming	= isc_unprepare_streaming,
47662306a36Sopenharmony_ci};
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic int isc_querycap(struct file *file, void *priv,
47962306a36Sopenharmony_ci			struct v4l2_capability *cap)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	struct isc_device *isc = video_drvdata(file);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	strscpy(cap->driver, "microchip-isc", sizeof(cap->driver));
48462306a36Sopenharmony_ci	strscpy(cap->card, "Microchip Image Sensor Controller", sizeof(cap->card));
48562306a36Sopenharmony_ci	snprintf(cap->bus_info, sizeof(cap->bus_info),
48662306a36Sopenharmony_ci		 "platform:%s", isc->v4l2_dev.name);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return 0;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic int isc_enum_fmt_vid_cap(struct file *file, void *priv,
49262306a36Sopenharmony_ci				struct v4l2_fmtdesc *f)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct isc_device *isc = video_drvdata(file);
49562306a36Sopenharmony_ci	u32 index = f->index;
49662306a36Sopenharmony_ci	u32 i, supported_index = 0;
49762306a36Sopenharmony_ci	struct isc_format *fmt;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/*
50062306a36Sopenharmony_ci	 * If we are not asked a specific mbus_code, we have to report all
50162306a36Sopenharmony_ci	 * the formats that we can output.
50262306a36Sopenharmony_ci	 */
50362306a36Sopenharmony_ci	if (!f->mbus_code) {
50462306a36Sopenharmony_ci		if (index >= isc->controller_formats_size)
50562306a36Sopenharmony_ci			return -EINVAL;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci		f->pixelformat = isc->controller_formats[index].fourcc;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		return 0;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/*
51362306a36Sopenharmony_ci	 * If a specific mbus_code is requested, check if we support
51462306a36Sopenharmony_ci	 * this mbus_code as input for the ISC.
51562306a36Sopenharmony_ci	 * If it's supported, then we report the corresponding pixelformat
51662306a36Sopenharmony_ci	 * as first possible option for the ISC.
51762306a36Sopenharmony_ci	 * E.g. mbus MEDIA_BUS_FMT_YUYV8_2X8 and report
51862306a36Sopenharmony_ci	 * 'YUYV' (YUYV 4:2:2)
51962306a36Sopenharmony_ci	 */
52062306a36Sopenharmony_ci	fmt = isc_find_format_by_code(isc, f->mbus_code, &i);
52162306a36Sopenharmony_ci	if (!fmt)
52262306a36Sopenharmony_ci		return -EINVAL;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (!index) {
52562306a36Sopenharmony_ci		f->pixelformat = fmt->fourcc;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		return 0;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	supported_index++;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* If the index is not raw, we don't have anymore formats to report */
53362306a36Sopenharmony_ci	if (!ISC_IS_FORMAT_RAW(f->mbus_code))
53462306a36Sopenharmony_ci		return -EINVAL;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/*
53762306a36Sopenharmony_ci	 * We are asked for a specific mbus code, which is raw.
53862306a36Sopenharmony_ci	 * We have to search through the formats we can convert to.
53962306a36Sopenharmony_ci	 * We have to skip the raw formats, we cannot convert to raw.
54062306a36Sopenharmony_ci	 * E.g. 'AR12' (16-bit ARGB 4-4-4-4), 'AR15' (16-bit ARGB 1-5-5-5), etc.
54162306a36Sopenharmony_ci	 */
54262306a36Sopenharmony_ci	for (i = 0; i < isc->controller_formats_size; i++) {
54362306a36Sopenharmony_ci		if (isc->controller_formats[i].raw)
54462306a36Sopenharmony_ci			continue;
54562306a36Sopenharmony_ci		if (index == supported_index) {
54662306a36Sopenharmony_ci			f->pixelformat = isc->controller_formats[i].fourcc;
54762306a36Sopenharmony_ci			return 0;
54862306a36Sopenharmony_ci		}
54962306a36Sopenharmony_ci		supported_index++;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return -EINVAL;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic int isc_g_fmt_vid_cap(struct file *file, void *priv,
55662306a36Sopenharmony_ci			     struct v4l2_format *fmt)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct isc_device *isc = video_drvdata(file);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	*fmt = isc->fmt;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	return 0;
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci/*
56662306a36Sopenharmony_ci * Checks the current configured format, if ISC can output it,
56762306a36Sopenharmony_ci * considering which type of format the ISC receives from the sensor
56862306a36Sopenharmony_ci */
56962306a36Sopenharmony_cistatic int isc_try_validate_formats(struct isc_device *isc)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	int ret;
57262306a36Sopenharmony_ci	bool bayer = false, yuv = false, rgb = false, grey = false;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	/* all formats supported by the RLP module are OK */
57562306a36Sopenharmony_ci	switch (isc->try_config.fourcc) {
57662306a36Sopenharmony_ci	case V4L2_PIX_FMT_SBGGR8:
57762306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGBRG8:
57862306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGRBG8:
57962306a36Sopenharmony_ci	case V4L2_PIX_FMT_SRGGB8:
58062306a36Sopenharmony_ci	case V4L2_PIX_FMT_SBGGR10:
58162306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGBRG10:
58262306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGRBG10:
58362306a36Sopenharmony_ci	case V4L2_PIX_FMT_SRGGB10:
58462306a36Sopenharmony_ci	case V4L2_PIX_FMT_SBGGR12:
58562306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGBRG12:
58662306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGRBG12:
58762306a36Sopenharmony_ci	case V4L2_PIX_FMT_SRGGB12:
58862306a36Sopenharmony_ci		ret = 0;
58962306a36Sopenharmony_ci		bayer = true;
59062306a36Sopenharmony_ci		break;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV420:
59362306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV422P:
59462306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUYV:
59562306a36Sopenharmony_ci	case V4L2_PIX_FMT_UYVY:
59662306a36Sopenharmony_ci	case V4L2_PIX_FMT_VYUY:
59762306a36Sopenharmony_ci		ret = 0;
59862306a36Sopenharmony_ci		yuv = true;
59962306a36Sopenharmony_ci		break;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGB565:
60262306a36Sopenharmony_ci	case V4L2_PIX_FMT_ABGR32:
60362306a36Sopenharmony_ci	case V4L2_PIX_FMT_XBGR32:
60462306a36Sopenharmony_ci	case V4L2_PIX_FMT_ARGB444:
60562306a36Sopenharmony_ci	case V4L2_PIX_FMT_ARGB555:
60662306a36Sopenharmony_ci		ret = 0;
60762306a36Sopenharmony_ci		rgb = true;
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci	case V4L2_PIX_FMT_GREY:
61062306a36Sopenharmony_ci	case V4L2_PIX_FMT_Y10:
61162306a36Sopenharmony_ci	case V4L2_PIX_FMT_Y16:
61262306a36Sopenharmony_ci		ret = 0;
61362306a36Sopenharmony_ci		grey = true;
61462306a36Sopenharmony_ci		break;
61562306a36Sopenharmony_ci	default:
61662306a36Sopenharmony_ci	/* any other different formats are not supported */
61762306a36Sopenharmony_ci		dev_err(isc->dev, "Requested unsupported format.\n");
61862306a36Sopenharmony_ci		ret = -EINVAL;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci	dev_dbg(isc->dev,
62162306a36Sopenharmony_ci		"Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n",
62262306a36Sopenharmony_ci		rgb, yuv, grey, bayer);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (bayer &&
62562306a36Sopenharmony_ci	    !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
62662306a36Sopenharmony_ci		dev_err(isc->dev, "Cannot output RAW if we do not receive RAW.\n");
62762306a36Sopenharmony_ci		return -EINVAL;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) &&
63162306a36Sopenharmony_ci	    !ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) {
63262306a36Sopenharmony_ci		dev_err(isc->dev, "Cannot output GREY if we do not receive RAW/GREY.\n");
63362306a36Sopenharmony_ci		return -EINVAL;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if ((rgb || bayer || yuv) &&
63762306a36Sopenharmony_ci	    ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) {
63862306a36Sopenharmony_ci		dev_err(isc->dev, "Cannot convert GREY to another format.\n");
63962306a36Sopenharmony_ci		return -EINVAL;
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	return ret;
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci/*
64662306a36Sopenharmony_ci * Configures the RLP and DMA modules, depending on the output format
64762306a36Sopenharmony_ci * configured for the ISC.
64862306a36Sopenharmony_ci * If direct_dump == true, just dump raw data 8/16 bits depending on format.
64962306a36Sopenharmony_ci */
65062306a36Sopenharmony_cistatic int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	isc->try_config.rlp_cfg_mode = 0;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	switch (isc->try_config.fourcc) {
65562306a36Sopenharmony_ci	case V4L2_PIX_FMT_SBGGR8:
65662306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGBRG8:
65762306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGRBG8:
65862306a36Sopenharmony_ci	case V4L2_PIX_FMT_SRGGB8:
65962306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8;
66062306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
66162306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
66262306a36Sopenharmony_ci		isc->try_config.bpp = 8;
66362306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 8;
66462306a36Sopenharmony_ci		break;
66562306a36Sopenharmony_ci	case V4L2_PIX_FMT_SBGGR10:
66662306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGBRG10:
66762306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGRBG10:
66862306a36Sopenharmony_ci	case V4L2_PIX_FMT_SRGGB10:
66962306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT10;
67062306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
67162306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
67262306a36Sopenharmony_ci		isc->try_config.bpp = 16;
67362306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 16;
67462306a36Sopenharmony_ci		break;
67562306a36Sopenharmony_ci	case V4L2_PIX_FMT_SBGGR12:
67662306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGBRG12:
67762306a36Sopenharmony_ci	case V4L2_PIX_FMT_SGRBG12:
67862306a36Sopenharmony_ci	case V4L2_PIX_FMT_SRGGB12:
67962306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT12;
68062306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
68162306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
68262306a36Sopenharmony_ci		isc->try_config.bpp = 16;
68362306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 16;
68462306a36Sopenharmony_ci		break;
68562306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGB565:
68662306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_RGB565;
68762306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
68862306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
68962306a36Sopenharmony_ci		isc->try_config.bpp = 16;
69062306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 16;
69162306a36Sopenharmony_ci		break;
69262306a36Sopenharmony_ci	case V4L2_PIX_FMT_ARGB444:
69362306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB444;
69462306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
69562306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
69662306a36Sopenharmony_ci		isc->try_config.bpp = 16;
69762306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 16;
69862306a36Sopenharmony_ci		break;
69962306a36Sopenharmony_ci	case V4L2_PIX_FMT_ARGB555:
70062306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB555;
70162306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
70262306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
70362306a36Sopenharmony_ci		isc->try_config.bpp = 16;
70462306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 16;
70562306a36Sopenharmony_ci		break;
70662306a36Sopenharmony_ci	case V4L2_PIX_FMT_ABGR32:
70762306a36Sopenharmony_ci	case V4L2_PIX_FMT_XBGR32:
70862306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB32;
70962306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
71062306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
71162306a36Sopenharmony_ci		isc->try_config.bpp = 32;
71262306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 32;
71362306a36Sopenharmony_ci		break;
71462306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV420:
71562306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC;
71662306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC420P;
71762306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR;
71862306a36Sopenharmony_ci		isc->try_config.bpp = 12;
71962306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 8; /* only first plane */
72062306a36Sopenharmony_ci		break;
72162306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV422P:
72262306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC;
72362306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC422P;
72462306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR;
72562306a36Sopenharmony_ci		isc->try_config.bpp = 16;
72662306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 8; /* only first plane */
72762306a36Sopenharmony_ci		break;
72862306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUYV:
72962306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_YUYV;
73062306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
73162306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
73262306a36Sopenharmony_ci		isc->try_config.bpp = 16;
73362306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 16;
73462306a36Sopenharmony_ci		break;
73562306a36Sopenharmony_ci	case V4L2_PIX_FMT_UYVY:
73662306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_UYVY;
73762306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
73862306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
73962306a36Sopenharmony_ci		isc->try_config.bpp = 16;
74062306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 16;
74162306a36Sopenharmony_ci		break;
74262306a36Sopenharmony_ci	case V4L2_PIX_FMT_VYUY:
74362306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_VYUY;
74462306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
74562306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
74662306a36Sopenharmony_ci		isc->try_config.bpp = 16;
74762306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 16;
74862306a36Sopenharmony_ci		break;
74962306a36Sopenharmony_ci	case V4L2_PIX_FMT_GREY:
75062306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY8;
75162306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
75262306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
75362306a36Sopenharmony_ci		isc->try_config.bpp = 8;
75462306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 8;
75562306a36Sopenharmony_ci		break;
75662306a36Sopenharmony_ci	case V4L2_PIX_FMT_Y16:
75762306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10 | ISC_RLP_CFG_LSH;
75862306a36Sopenharmony_ci		fallthrough;
75962306a36Sopenharmony_ci	case V4L2_PIX_FMT_Y10:
76062306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode |= ISC_RLP_CFG_MODE_DATY10;
76162306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
76262306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
76362306a36Sopenharmony_ci		isc->try_config.bpp = 16;
76462306a36Sopenharmony_ci		isc->try_config.bpp_v4l2 = 16;
76562306a36Sopenharmony_ci		break;
76662306a36Sopenharmony_ci	default:
76762306a36Sopenharmony_ci		return -EINVAL;
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (direct_dump) {
77162306a36Sopenharmony_ci		isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8;
77262306a36Sopenharmony_ci		isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
77362306a36Sopenharmony_ci		isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
77462306a36Sopenharmony_ci		return 0;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	return 0;
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci/*
78162306a36Sopenharmony_ci * Configuring pipeline modules, depending on which format the ISC outputs
78262306a36Sopenharmony_ci * and considering which format it has as input from the sensor.
78362306a36Sopenharmony_ci */
78462306a36Sopenharmony_cistatic int isc_try_configure_pipeline(struct isc_device *isc)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	switch (isc->try_config.fourcc) {
78762306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGB565:
78862306a36Sopenharmony_ci	case V4L2_PIX_FMT_ARGB555:
78962306a36Sopenharmony_ci	case V4L2_PIX_FMT_ARGB444:
79062306a36Sopenharmony_ci	case V4L2_PIX_FMT_ABGR32:
79162306a36Sopenharmony_ci	case V4L2_PIX_FMT_XBGR32:
79262306a36Sopenharmony_ci		/* if sensor format is RAW, we convert inside ISC */
79362306a36Sopenharmony_ci		if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
79462306a36Sopenharmony_ci			isc->try_config.bits_pipeline = CFA_ENABLE |
79562306a36Sopenharmony_ci				WB_ENABLE | GAM_ENABLES | DPC_BLCENABLE |
79662306a36Sopenharmony_ci				CC_ENABLE;
79762306a36Sopenharmony_ci		} else {
79862306a36Sopenharmony_ci			isc->try_config.bits_pipeline = 0x0;
79962306a36Sopenharmony_ci		}
80062306a36Sopenharmony_ci		break;
80162306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV420:
80262306a36Sopenharmony_ci		/* if sensor format is RAW, we convert inside ISC */
80362306a36Sopenharmony_ci		if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
80462306a36Sopenharmony_ci			isc->try_config.bits_pipeline = CFA_ENABLE |
80562306a36Sopenharmony_ci				CSC_ENABLE | GAM_ENABLES | WB_ENABLE |
80662306a36Sopenharmony_ci				SUB420_ENABLE | SUB422_ENABLE | CBC_ENABLE |
80762306a36Sopenharmony_ci				DPC_BLCENABLE;
80862306a36Sopenharmony_ci		} else {
80962306a36Sopenharmony_ci			isc->try_config.bits_pipeline = 0x0;
81062306a36Sopenharmony_ci		}
81162306a36Sopenharmony_ci		break;
81262306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV422P:
81362306a36Sopenharmony_ci		/* if sensor format is RAW, we convert inside ISC */
81462306a36Sopenharmony_ci		if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
81562306a36Sopenharmony_ci			isc->try_config.bits_pipeline = CFA_ENABLE |
81662306a36Sopenharmony_ci				CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
81762306a36Sopenharmony_ci				SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE;
81862306a36Sopenharmony_ci		} else {
81962306a36Sopenharmony_ci			isc->try_config.bits_pipeline = 0x0;
82062306a36Sopenharmony_ci		}
82162306a36Sopenharmony_ci		break;
82262306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUYV:
82362306a36Sopenharmony_ci	case V4L2_PIX_FMT_UYVY:
82462306a36Sopenharmony_ci	case V4L2_PIX_FMT_VYUY:
82562306a36Sopenharmony_ci		/* if sensor format is RAW, we convert inside ISC */
82662306a36Sopenharmony_ci		if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
82762306a36Sopenharmony_ci			isc->try_config.bits_pipeline = CFA_ENABLE |
82862306a36Sopenharmony_ci				CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
82962306a36Sopenharmony_ci				SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE;
83062306a36Sopenharmony_ci		} else {
83162306a36Sopenharmony_ci			isc->try_config.bits_pipeline = 0x0;
83262306a36Sopenharmony_ci		}
83362306a36Sopenharmony_ci		break;
83462306a36Sopenharmony_ci	case V4L2_PIX_FMT_GREY:
83562306a36Sopenharmony_ci	case V4L2_PIX_FMT_Y16:
83662306a36Sopenharmony_ci		/* if sensor format is RAW, we convert inside ISC */
83762306a36Sopenharmony_ci		if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
83862306a36Sopenharmony_ci			isc->try_config.bits_pipeline = CFA_ENABLE |
83962306a36Sopenharmony_ci				CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
84062306a36Sopenharmony_ci				CBC_ENABLE | DPC_BLCENABLE;
84162306a36Sopenharmony_ci		} else {
84262306a36Sopenharmony_ci			isc->try_config.bits_pipeline = 0x0;
84362306a36Sopenharmony_ci		}
84462306a36Sopenharmony_ci		break;
84562306a36Sopenharmony_ci	default:
84662306a36Sopenharmony_ci		if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code))
84762306a36Sopenharmony_ci			isc->try_config.bits_pipeline = WB_ENABLE | DPC_BLCENABLE;
84862306a36Sopenharmony_ci		else
84962306a36Sopenharmony_ci			isc->try_config.bits_pipeline = 0x0;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* Tune the pipeline to product specific */
85362306a36Sopenharmony_ci	isc->adapt_pipeline(isc);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	return 0;
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic void isc_try_fse(struct isc_device *isc,
85962306a36Sopenharmony_ci			struct v4l2_subdev_state *sd_state)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct v4l2_subdev_frame_size_enum fse = {
86262306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_TRY,
86362306a36Sopenharmony_ci	};
86462306a36Sopenharmony_ci	int ret;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	/*
86762306a36Sopenharmony_ci	 * If we do not know yet which format the subdev is using, we cannot
86862306a36Sopenharmony_ci	 * do anything.
86962306a36Sopenharmony_ci	 */
87062306a36Sopenharmony_ci	if (!isc->config.sd_format)
87162306a36Sopenharmony_ci		return;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	fse.code = isc->try_config.sd_format->mbus_code;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
87662306a36Sopenharmony_ci			       sd_state, &fse);
87762306a36Sopenharmony_ci	/*
87862306a36Sopenharmony_ci	 * Attempt to obtain format size from subdev. If not available,
87962306a36Sopenharmony_ci	 * just use the maximum ISC can receive.
88062306a36Sopenharmony_ci	 */
88162306a36Sopenharmony_ci	if (ret) {
88262306a36Sopenharmony_ci		sd_state->pads->try_crop.width = isc->max_width;
88362306a36Sopenharmony_ci		sd_state->pads->try_crop.height = isc->max_height;
88462306a36Sopenharmony_ci	} else {
88562306a36Sopenharmony_ci		sd_state->pads->try_crop.width = fse.max_width;
88662306a36Sopenharmony_ci		sd_state->pads->try_crop.height = fse.max_height;
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct v4l2_pix_format *pixfmt = &f->fmt.pix;
89362306a36Sopenharmony_ci	unsigned int i;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
89662306a36Sopenharmony_ci		return -EINVAL;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	isc->try_config.fourcc = isc->controller_formats[0].fourcc;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	/* find if the format requested is supported */
90162306a36Sopenharmony_ci	for (i = 0; i < isc->controller_formats_size; i++)
90262306a36Sopenharmony_ci		if (isc->controller_formats[i].fourcc == pixfmt->pixelformat) {
90362306a36Sopenharmony_ci			isc->try_config.fourcc = pixfmt->pixelformat;
90462306a36Sopenharmony_ci			break;
90562306a36Sopenharmony_ci		}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	isc_try_configure_rlp_dma(isc, false);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* Limit to Microchip ISC hardware capabilities */
91062306a36Sopenharmony_ci	v4l_bound_align_image(&pixfmt->width, 16, isc->max_width, 0,
91162306a36Sopenharmony_ci			      &pixfmt->height, 16, isc->max_height, 0, 0);
91262306a36Sopenharmony_ci	/* If we did not find the requested format, we will fallback here */
91362306a36Sopenharmony_ci	pixfmt->pixelformat = isc->try_config.fourcc;
91462306a36Sopenharmony_ci	pixfmt->colorspace = V4L2_COLORSPACE_SRGB;
91562306a36Sopenharmony_ci	pixfmt->field = V4L2_FIELD_NONE;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp_v4l2) >> 3;
91862306a36Sopenharmony_ci	pixfmt->sizeimage = ((pixfmt->width * isc->try_config.bpp) >> 3) *
91962306a36Sopenharmony_ci			     pixfmt->height;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	isc->try_fmt = *f;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	return 0;
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_cistatic int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	isc_try_fmt(isc, f);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	/* make the try configuration active */
93162306a36Sopenharmony_ci	isc->config = isc->try_config;
93262306a36Sopenharmony_ci	isc->fmt = isc->try_fmt;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	dev_dbg(isc->dev, "ISC set_fmt to %.4s @%dx%d\n",
93562306a36Sopenharmony_ci		(char *)&f->fmt.pix.pixelformat,
93662306a36Sopenharmony_ci		f->fmt.pix.width, f->fmt.pix.height);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	return 0;
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic int isc_validate(struct isc_device *isc)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	int ret;
94462306a36Sopenharmony_ci	int i;
94562306a36Sopenharmony_ci	struct isc_format *sd_fmt = NULL;
94662306a36Sopenharmony_ci	struct v4l2_pix_format *pixfmt = &isc->fmt.fmt.pix;
94762306a36Sopenharmony_ci	struct v4l2_subdev_format format = {
94862306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
94962306a36Sopenharmony_ci		.pad = isc->remote_pad,
95062306a36Sopenharmony_ci	};
95162306a36Sopenharmony_ci	struct v4l2_subdev_pad_config pad_cfg = {};
95262306a36Sopenharmony_ci	struct v4l2_subdev_state pad_state = {
95362306a36Sopenharmony_ci		.pads = &pad_cfg,
95462306a36Sopenharmony_ci	};
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	/* Get current format from subdev */
95762306a36Sopenharmony_ci	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, get_fmt, NULL,
95862306a36Sopenharmony_ci			       &format);
95962306a36Sopenharmony_ci	if (ret)
96062306a36Sopenharmony_ci		return ret;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	/* Identify the subdev's format configuration */
96362306a36Sopenharmony_ci	for (i = 0; i < isc->formats_list_size; i++)
96462306a36Sopenharmony_ci		if (isc->formats_list[i].mbus_code == format.format.code) {
96562306a36Sopenharmony_ci			sd_fmt = &isc->formats_list[i];
96662306a36Sopenharmony_ci			break;
96762306a36Sopenharmony_ci		}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	/* Check if the format is not supported */
97062306a36Sopenharmony_ci	if (!sd_fmt) {
97162306a36Sopenharmony_ci		dev_err(isc->dev,
97262306a36Sopenharmony_ci			"Current subdevice is streaming a media bus code that is not supported 0x%x\n",
97362306a36Sopenharmony_ci			format.format.code);
97462306a36Sopenharmony_ci		return -EPIPE;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	/* At this moment we know which format the subdev will use */
97862306a36Sopenharmony_ci	isc->try_config.sd_format = sd_fmt;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	/* If the sensor is not RAW, we can only do a direct dump */
98162306a36Sopenharmony_ci	if (!ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code))
98262306a36Sopenharmony_ci		isc_try_configure_rlp_dma(isc, true);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	/* Limit to Microchip ISC hardware capabilities */
98562306a36Sopenharmony_ci	v4l_bound_align_image(&format.format.width, 16, isc->max_width, 0,
98662306a36Sopenharmony_ci			      &format.format.height, 16, isc->max_height, 0, 0);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	/* Check if the frame size is the same. Otherwise we may overflow */
98962306a36Sopenharmony_ci	if (pixfmt->height != format.format.height ||
99062306a36Sopenharmony_ci	    pixfmt->width != format.format.width) {
99162306a36Sopenharmony_ci		dev_err(isc->dev,
99262306a36Sopenharmony_ci			"ISC not configured with the proper frame size: %dx%d\n",
99362306a36Sopenharmony_ci			format.format.width, format.format.height);
99462306a36Sopenharmony_ci		return -EPIPE;
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	dev_dbg(isc->dev,
99862306a36Sopenharmony_ci		"Identified subdev using format %.4s with %dx%d %d bpp\n",
99962306a36Sopenharmony_ci		(char *)&sd_fmt->fourcc, pixfmt->width, pixfmt->height,
100062306a36Sopenharmony_ci		isc->try_config.bpp);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* Reset and restart AWB if the subdevice changed the format */
100362306a36Sopenharmony_ci	if (isc->try_config.sd_format && isc->config.sd_format &&
100462306a36Sopenharmony_ci	    isc->try_config.sd_format != isc->config.sd_format) {
100562306a36Sopenharmony_ci		isc->ctrls.hist_stat = HIST_INIT;
100662306a36Sopenharmony_ci		isc_reset_awb_ctrls(isc);
100762306a36Sopenharmony_ci		isc_update_v4l2_ctrls(isc);
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	/* Validate formats */
101162306a36Sopenharmony_ci	ret = isc_try_validate_formats(isc);
101262306a36Sopenharmony_ci	if (ret)
101362306a36Sopenharmony_ci		return ret;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	/* Obtain frame sizes if possible to have crop requirements ready */
101662306a36Sopenharmony_ci	isc_try_fse(isc, &pad_state);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	/* Configure ISC pipeline for the config */
101962306a36Sopenharmony_ci	ret = isc_try_configure_pipeline(isc);
102062306a36Sopenharmony_ci	if (ret)
102162306a36Sopenharmony_ci		return ret;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	isc->config = isc->try_config;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	dev_dbg(isc->dev, "New ISC configuration in place\n");
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	return 0;
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic int isc_s_fmt_vid_cap(struct file *file, void *priv,
103162306a36Sopenharmony_ci			     struct v4l2_format *f)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct isc_device *isc = video_drvdata(file);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if (vb2_is_busy(&isc->vb2_vidq))
103662306a36Sopenharmony_ci		return -EBUSY;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	return isc_set_fmt(isc, f);
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistatic int isc_try_fmt_vid_cap(struct file *file, void *priv,
104262306a36Sopenharmony_ci			       struct v4l2_format *f)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci	struct isc_device *isc = video_drvdata(file);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	return isc_try_fmt(isc, f);
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_cistatic int isc_enum_input(struct file *file, void *priv,
105062306a36Sopenharmony_ci			  struct v4l2_input *inp)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci	if (inp->index != 0)
105362306a36Sopenharmony_ci		return -EINVAL;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	inp->type = V4L2_INPUT_TYPE_CAMERA;
105662306a36Sopenharmony_ci	inp->std = 0;
105762306a36Sopenharmony_ci	strscpy(inp->name, "Camera", sizeof(inp->name));
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	return 0;
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_cistatic int isc_g_input(struct file *file, void *priv, unsigned int *i)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	*i = 0;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	return 0;
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cistatic int isc_s_input(struct file *file, void *priv, unsigned int i)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	if (i > 0)
107262306a36Sopenharmony_ci		return -EINVAL;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	return 0;
107562306a36Sopenharmony_ci}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_cistatic int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	struct isc_device *isc = video_drvdata(file);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	return v4l2_g_parm_cap(video_devdata(file), isc->current_subdev->sd, a);
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_cistatic int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	struct isc_device *isc = video_drvdata(file);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	return v4l2_s_parm_cap(video_devdata(file), isc->current_subdev->sd, a);
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic int isc_enum_framesizes(struct file *file, void *fh,
109262306a36Sopenharmony_ci			       struct v4l2_frmsizeenum *fsize)
109362306a36Sopenharmony_ci{
109462306a36Sopenharmony_ci	struct isc_device *isc = video_drvdata(file);
109562306a36Sopenharmony_ci	int ret = -EINVAL;
109662306a36Sopenharmony_ci	int i;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (fsize->index)
109962306a36Sopenharmony_ci		return -EINVAL;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	for (i = 0; i < isc->controller_formats_size; i++)
110262306a36Sopenharmony_ci		if (isc->controller_formats[i].fourcc == fsize->pixel_format)
110362306a36Sopenharmony_ci			ret = 0;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	if (ret)
110662306a36Sopenharmony_ci		return ret;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	fsize->stepwise.min_width = 16;
111162306a36Sopenharmony_ci	fsize->stepwise.max_width = isc->max_width;
111262306a36Sopenharmony_ci	fsize->stepwise.min_height = 16;
111362306a36Sopenharmony_ci	fsize->stepwise.max_height = isc->max_height;
111462306a36Sopenharmony_ci	fsize->stepwise.step_width = 1;
111562306a36Sopenharmony_ci	fsize->stepwise.step_height = 1;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	return 0;
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops isc_ioctl_ops = {
112162306a36Sopenharmony_ci	.vidioc_querycap		= isc_querycap,
112262306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= isc_enum_fmt_vid_cap,
112362306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap		= isc_g_fmt_vid_cap,
112462306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap		= isc_s_fmt_vid_cap,
112562306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap		= isc_try_fmt_vid_cap,
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	.vidioc_enum_input		= isc_enum_input,
112862306a36Sopenharmony_ci	.vidioc_g_input			= isc_g_input,
112962306a36Sopenharmony_ci	.vidioc_s_input			= isc_s_input,
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
113262306a36Sopenharmony_ci	.vidioc_querybuf		= vb2_ioctl_querybuf,
113362306a36Sopenharmony_ci	.vidioc_qbuf			= vb2_ioctl_qbuf,
113462306a36Sopenharmony_ci	.vidioc_expbuf			= vb2_ioctl_expbuf,
113562306a36Sopenharmony_ci	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
113662306a36Sopenharmony_ci	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
113762306a36Sopenharmony_ci	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
113862306a36Sopenharmony_ci	.vidioc_streamon		= vb2_ioctl_streamon,
113962306a36Sopenharmony_ci	.vidioc_streamoff		= vb2_ioctl_streamoff,
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	.vidioc_g_parm			= isc_g_parm,
114262306a36Sopenharmony_ci	.vidioc_s_parm			= isc_s_parm,
114362306a36Sopenharmony_ci	.vidioc_enum_framesizes		= isc_enum_framesizes,
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	.vidioc_log_status		= v4l2_ctrl_log_status,
114662306a36Sopenharmony_ci	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
114762306a36Sopenharmony_ci	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
114862306a36Sopenharmony_ci};
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_cistatic int isc_open(struct file *file)
115162306a36Sopenharmony_ci{
115262306a36Sopenharmony_ci	struct isc_device *isc = video_drvdata(file);
115362306a36Sopenharmony_ci	struct v4l2_subdev *sd = isc->current_subdev->sd;
115462306a36Sopenharmony_ci	int ret;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	if (mutex_lock_interruptible(&isc->lock))
115762306a36Sopenharmony_ci		return -ERESTARTSYS;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	ret = v4l2_fh_open(file);
116062306a36Sopenharmony_ci	if (ret < 0)
116162306a36Sopenharmony_ci		goto unlock;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (!v4l2_fh_is_singular_file(file))
116462306a36Sopenharmony_ci		goto unlock;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	ret = v4l2_subdev_call(sd, core, s_power, 1);
116762306a36Sopenharmony_ci	if (ret < 0 && ret != -ENOIOCTLCMD) {
116862306a36Sopenharmony_ci		v4l2_fh_release(file);
116962306a36Sopenharmony_ci		goto unlock;
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	ret = isc_set_fmt(isc, &isc->fmt);
117362306a36Sopenharmony_ci	if (ret) {
117462306a36Sopenharmony_ci		v4l2_subdev_call(sd, core, s_power, 0);
117562306a36Sopenharmony_ci		v4l2_fh_release(file);
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ciunlock:
117962306a36Sopenharmony_ci	mutex_unlock(&isc->lock);
118062306a36Sopenharmony_ci	return ret;
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic int isc_release(struct file *file)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	struct isc_device *isc = video_drvdata(file);
118662306a36Sopenharmony_ci	struct v4l2_subdev *sd = isc->current_subdev->sd;
118762306a36Sopenharmony_ci	bool fh_singular;
118862306a36Sopenharmony_ci	int ret;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	mutex_lock(&isc->lock);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	fh_singular = v4l2_fh_is_singular_file(file);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	ret = _vb2_fop_release(file, NULL);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	if (fh_singular)
119762306a36Sopenharmony_ci		v4l2_subdev_call(sd, core, s_power, 0);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	mutex_unlock(&isc->lock);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	return ret;
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_cistatic const struct v4l2_file_operations isc_fops = {
120562306a36Sopenharmony_ci	.owner		= THIS_MODULE,
120662306a36Sopenharmony_ci	.open		= isc_open,
120762306a36Sopenharmony_ci	.release	= isc_release,
120862306a36Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
120962306a36Sopenharmony_ci	.read		= vb2_fop_read,
121062306a36Sopenharmony_ci	.mmap		= vb2_fop_mmap,
121162306a36Sopenharmony_ci	.poll		= vb2_fop_poll,
121262306a36Sopenharmony_ci};
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ciirqreturn_t microchip_isc_interrupt(int irq, void *dev_id)
121562306a36Sopenharmony_ci{
121662306a36Sopenharmony_ci	struct isc_device *isc = (struct isc_device *)dev_id;
121762306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
121862306a36Sopenharmony_ci	u32 isc_intsr, isc_intmask, pending;
121962306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	regmap_read(regmap, ISC_INTSR, &isc_intsr);
122262306a36Sopenharmony_ci	regmap_read(regmap, ISC_INTMASK, &isc_intmask);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	pending = isc_intsr & isc_intmask;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	if (likely(pending & ISC_INT_DDONE)) {
122762306a36Sopenharmony_ci		spin_lock(&isc->dma_queue_lock);
122862306a36Sopenharmony_ci		if (isc->cur_frm) {
122962306a36Sopenharmony_ci			struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb;
123062306a36Sopenharmony_ci			struct vb2_buffer *vb = &vbuf->vb2_buf;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci			vb->timestamp = ktime_get_ns();
123362306a36Sopenharmony_ci			vbuf->sequence = isc->sequence++;
123462306a36Sopenharmony_ci			vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
123562306a36Sopenharmony_ci			isc->cur_frm = NULL;
123662306a36Sopenharmony_ci		}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci		if (!list_empty(&isc->dma_queue) && !isc->stop) {
123962306a36Sopenharmony_ci			isc->cur_frm = list_first_entry(&isc->dma_queue,
124062306a36Sopenharmony_ci							struct isc_buffer, list);
124162306a36Sopenharmony_ci			list_del(&isc->cur_frm->list);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci			isc_start_dma(isc);
124462306a36Sopenharmony_ci		}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci		if (isc->stop)
124762306a36Sopenharmony_ci			complete(&isc->comp);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci		ret = IRQ_HANDLED;
125062306a36Sopenharmony_ci		spin_unlock(&isc->dma_queue_lock);
125162306a36Sopenharmony_ci	}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	if (pending & ISC_INT_HISDONE) {
125462306a36Sopenharmony_ci		schedule_work(&isc->awb_work);
125562306a36Sopenharmony_ci		ret = IRQ_HANDLED;
125662306a36Sopenharmony_ci	}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	return ret;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_interrupt);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cistatic void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
126562306a36Sopenharmony_ci	struct isc_ctrls *ctrls = &isc->ctrls;
126662306a36Sopenharmony_ci	u32 *hist_count = &ctrls->hist_count[ctrls->hist_id];
126762306a36Sopenharmony_ci	u32 *hist_entry = &ctrls->hist_entry[0];
126862306a36Sopenharmony_ci	u32 i;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	*min = 0;
127162306a36Sopenharmony_ci	*max = HIST_ENTRIES;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	regmap_bulk_read(regmap, ISC_HIS_ENTRY + isc->offsets.his_entry,
127462306a36Sopenharmony_ci			 hist_entry, HIST_ENTRIES);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	*hist_count = 0;
127762306a36Sopenharmony_ci	/*
127862306a36Sopenharmony_ci	 * we deliberately ignore the end of the histogram,
127962306a36Sopenharmony_ci	 * the most white pixels
128062306a36Sopenharmony_ci	 */
128162306a36Sopenharmony_ci	for (i = 1; i < HIST_ENTRIES; i++) {
128262306a36Sopenharmony_ci		if (*hist_entry && !*min)
128362306a36Sopenharmony_ci			*min = i;
128462306a36Sopenharmony_ci		if (*hist_entry)
128562306a36Sopenharmony_ci			*max = i;
128662306a36Sopenharmony_ci		*hist_count += i * (*hist_entry++);
128762306a36Sopenharmony_ci	}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	if (!*min)
129062306a36Sopenharmony_ci		*min = 1;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	dev_dbg(isc->dev, "isc wb: hist_id %u, hist_count %u",
129362306a36Sopenharmony_ci		ctrls->hist_id, *hist_count);
129462306a36Sopenharmony_ci}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_cistatic void isc_wb_update(struct isc_ctrls *ctrls)
129762306a36Sopenharmony_ci{
129862306a36Sopenharmony_ci	struct isc_device *isc = container_of(ctrls, struct isc_device, ctrls);
129962306a36Sopenharmony_ci	u32 *hist_count = &ctrls->hist_count[0];
130062306a36Sopenharmony_ci	u32 c, offset[4];
130162306a36Sopenharmony_ci	u64 avg = 0;
130262306a36Sopenharmony_ci	/* We compute two gains, stretch gain and grey world gain */
130362306a36Sopenharmony_ci	u32 s_gain[4], gw_gain[4];
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	/*
130662306a36Sopenharmony_ci	 * According to Grey World, we need to set gains for R/B to normalize
130762306a36Sopenharmony_ci	 * them towards the green channel.
130862306a36Sopenharmony_ci	 * Thus we want to keep Green as fixed and adjust only Red/Blue
130962306a36Sopenharmony_ci	 * Compute the average of the both green channels first
131062306a36Sopenharmony_ci	 */
131162306a36Sopenharmony_ci	avg = (u64)hist_count[ISC_HIS_CFG_MODE_GR] +
131262306a36Sopenharmony_ci		(u64)hist_count[ISC_HIS_CFG_MODE_GB];
131362306a36Sopenharmony_ci	avg >>= 1;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	dev_dbg(isc->dev, "isc wb: green components average %llu\n", avg);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	/* Green histogram is null, nothing to do */
131862306a36Sopenharmony_ci	if (!avg)
131962306a36Sopenharmony_ci		return;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) {
132262306a36Sopenharmony_ci		/*
132362306a36Sopenharmony_ci		 * the color offset is the minimum value of the histogram.
132462306a36Sopenharmony_ci		 * we stretch this color to the full range by substracting
132562306a36Sopenharmony_ci		 * this value from the color component.
132662306a36Sopenharmony_ci		 */
132762306a36Sopenharmony_ci		offset[c] = ctrls->hist_minmax[c][HIST_MIN_INDEX];
132862306a36Sopenharmony_ci		/*
132962306a36Sopenharmony_ci		 * The offset is always at least 1. If the offset is 1, we do
133062306a36Sopenharmony_ci		 * not need to adjust it, so our result must be zero.
133162306a36Sopenharmony_ci		 * the offset is computed in a histogram on 9 bits (0..512)
133262306a36Sopenharmony_ci		 * but the offset in register is based on
133362306a36Sopenharmony_ci		 * 12 bits pipeline (0..4096).
133462306a36Sopenharmony_ci		 * we need to shift with the 3 bits that the histogram is
133562306a36Sopenharmony_ci		 * ignoring
133662306a36Sopenharmony_ci		 */
133762306a36Sopenharmony_ci		ctrls->offset[c] = (offset[c] - 1) << 3;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci		/*
134062306a36Sopenharmony_ci		 * the offset is then taken and converted to 2's complements,
134162306a36Sopenharmony_ci		 * and must be negative, as we subtract this value from the
134262306a36Sopenharmony_ci		 * color components
134362306a36Sopenharmony_ci		 */
134462306a36Sopenharmony_ci		ctrls->offset[c] = -ctrls->offset[c];
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci		/*
134762306a36Sopenharmony_ci		 * the stretch gain is the total number of histogram bins
134862306a36Sopenharmony_ci		 * divided by the actual range of color component (Max - Min)
134962306a36Sopenharmony_ci		 * If we compute gain like this, the actual color component
135062306a36Sopenharmony_ci		 * will be stretched to the full histogram.
135162306a36Sopenharmony_ci		 * We need to shift 9 bits for precision, we have 9 bits for
135262306a36Sopenharmony_ci		 * decimals
135362306a36Sopenharmony_ci		 */
135462306a36Sopenharmony_ci		s_gain[c] = (HIST_ENTRIES << 9) /
135562306a36Sopenharmony_ci			(ctrls->hist_minmax[c][HIST_MAX_INDEX] -
135662306a36Sopenharmony_ci			ctrls->hist_minmax[c][HIST_MIN_INDEX] + 1);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci		/*
135962306a36Sopenharmony_ci		 * Now we have to compute the gain w.r.t. the average.
136062306a36Sopenharmony_ci		 * Add/lose gain to the component towards the average.
136162306a36Sopenharmony_ci		 * If it happens that the component is zero, use the
136262306a36Sopenharmony_ci		 * fixed point value : 1.0 gain.
136362306a36Sopenharmony_ci		 */
136462306a36Sopenharmony_ci		if (hist_count[c])
136562306a36Sopenharmony_ci			gw_gain[c] = div_u64(avg << 9, hist_count[c]);
136662306a36Sopenharmony_ci		else
136762306a36Sopenharmony_ci			gw_gain[c] = 1 << 9;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci		dev_dbg(isc->dev,
137062306a36Sopenharmony_ci			"isc wb: component %d, s_gain %u, gw_gain %u\n",
137162306a36Sopenharmony_ci			c, s_gain[c], gw_gain[c]);
137262306a36Sopenharmony_ci		/* multiply both gains and adjust for decimals */
137362306a36Sopenharmony_ci		ctrls->gain[c] = s_gain[c] * gw_gain[c];
137462306a36Sopenharmony_ci		ctrls->gain[c] >>= 9;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci		/* make sure we are not out of range */
137762306a36Sopenharmony_ci		ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0));
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci		dev_dbg(isc->dev, "isc wb: component %d, final gain %u\n",
138062306a36Sopenharmony_ci			c, ctrls->gain[c]);
138162306a36Sopenharmony_ci	}
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cistatic void isc_awb_work(struct work_struct *w)
138562306a36Sopenharmony_ci{
138662306a36Sopenharmony_ci	struct isc_device *isc =
138762306a36Sopenharmony_ci		container_of(w, struct isc_device, awb_work);
138862306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
138962306a36Sopenharmony_ci	struct isc_ctrls *ctrls = &isc->ctrls;
139062306a36Sopenharmony_ci	u32 hist_id = ctrls->hist_id;
139162306a36Sopenharmony_ci	u32 baysel;
139262306a36Sopenharmony_ci	unsigned long flags;
139362306a36Sopenharmony_ci	u32 min, max;
139462306a36Sopenharmony_ci	int ret;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	if (ctrls->hist_stat != HIST_ENABLED)
139762306a36Sopenharmony_ci		return;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	isc_hist_count(isc, &min, &max);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	dev_dbg(isc->dev,
140262306a36Sopenharmony_ci		"isc wb mode %d: hist min %u , max %u\n", hist_id, min, max);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min;
140562306a36Sopenharmony_ci	ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	if (hist_id != ISC_HIS_CFG_MODE_B) {
140862306a36Sopenharmony_ci		hist_id++;
140962306a36Sopenharmony_ci	} else {
141062306a36Sopenharmony_ci		isc_wb_update(ctrls);
141162306a36Sopenharmony_ci		hist_id = ISC_HIS_CFG_MODE_GR;
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	ctrls->hist_id = hist_id;
141562306a36Sopenharmony_ci	baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(isc->dev);
141862306a36Sopenharmony_ci	if (ret < 0)
141962306a36Sopenharmony_ci		return;
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	/*
142262306a36Sopenharmony_ci	 * only update if we have all the required histograms and controls
142362306a36Sopenharmony_ci	 * if awb has been disabled, we need to reset registers as well.
142462306a36Sopenharmony_ci	 */
142562306a36Sopenharmony_ci	if (hist_id == ISC_HIS_CFG_MODE_GR || ctrls->awb == ISC_WB_NONE) {
142662306a36Sopenharmony_ci		/*
142762306a36Sopenharmony_ci		 * It may happen that DMA Done IRQ will trigger while we are
142862306a36Sopenharmony_ci		 * updating white balance registers here.
142962306a36Sopenharmony_ci		 * In that case, only parts of the controls have been updated.
143062306a36Sopenharmony_ci		 * We can avoid that by locking the section.
143162306a36Sopenharmony_ci		 */
143262306a36Sopenharmony_ci		spin_lock_irqsave(&isc->awb_lock, flags);
143362306a36Sopenharmony_ci		isc_update_awb_ctrls(isc);
143462306a36Sopenharmony_ci		spin_unlock_irqrestore(&isc->awb_lock, flags);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci		/*
143762306a36Sopenharmony_ci		 * if we are doing just the one time white balance adjustment,
143862306a36Sopenharmony_ci		 * we are basically done.
143962306a36Sopenharmony_ci		 */
144062306a36Sopenharmony_ci		if (ctrls->awb == ISC_WB_ONETIME) {
144162306a36Sopenharmony_ci			dev_info(isc->dev,
144262306a36Sopenharmony_ci				 "Completed one time white-balance adjustment.\n");
144362306a36Sopenharmony_ci			/* update the v4l2 controls values */
144462306a36Sopenharmony_ci			isc_update_v4l2_ctrls(isc);
144562306a36Sopenharmony_ci			ctrls->awb = ISC_WB_NONE;
144662306a36Sopenharmony_ci		}
144762306a36Sopenharmony_ci	}
144862306a36Sopenharmony_ci	regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his,
144962306a36Sopenharmony_ci		     hist_id | baysel | ISC_HIS_CFG_RAR);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	/*
145262306a36Sopenharmony_ci	 * We have to make sure the streaming has not stopped meanwhile.
145362306a36Sopenharmony_ci	 * ISC requires a frame to clock the internal profile update.
145462306a36Sopenharmony_ci	 * To avoid issues, lock the sequence with a mutex
145562306a36Sopenharmony_ci	 */
145662306a36Sopenharmony_ci	mutex_lock(&isc->awb_mutex);
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	/* streaming is not active anymore */
145962306a36Sopenharmony_ci	if (isc->stop) {
146062306a36Sopenharmony_ci		mutex_unlock(&isc->awb_mutex);
146162306a36Sopenharmony_ci		return;
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	isc_update_profile(isc);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	mutex_unlock(&isc->awb_mutex);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	/* if awb has been disabled, we don't need to start another histogram */
146962306a36Sopenharmony_ci	if (ctrls->awb)
147062306a36Sopenharmony_ci		regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	pm_runtime_put_sync(isc->dev);
147362306a36Sopenharmony_ci}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_cistatic int isc_s_ctrl(struct v4l2_ctrl *ctrl)
147662306a36Sopenharmony_ci{
147762306a36Sopenharmony_ci	struct isc_device *isc = container_of(ctrl->handler,
147862306a36Sopenharmony_ci					     struct isc_device, ctrls.handler);
147962306a36Sopenharmony_ci	struct isc_ctrls *ctrls = &isc->ctrls;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
148262306a36Sopenharmony_ci		return 0;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	switch (ctrl->id) {
148562306a36Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
148662306a36Sopenharmony_ci		ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
148762306a36Sopenharmony_ci		break;
148862306a36Sopenharmony_ci	case V4L2_CID_CONTRAST:
148962306a36Sopenharmony_ci		ctrls->contrast = ctrl->val & ISC_CBC_CONTRAST_MASK;
149062306a36Sopenharmony_ci		break;
149162306a36Sopenharmony_ci	case V4L2_CID_GAMMA:
149262306a36Sopenharmony_ci		ctrls->gamma_index = ctrl->val;
149362306a36Sopenharmony_ci		break;
149462306a36Sopenharmony_ci	default:
149562306a36Sopenharmony_ci		return -EINVAL;
149662306a36Sopenharmony_ci	}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	return 0;
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops isc_ctrl_ops = {
150262306a36Sopenharmony_ci	.s_ctrl	= isc_s_ctrl,
150362306a36Sopenharmony_ci};
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_cistatic int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl)
150662306a36Sopenharmony_ci{
150762306a36Sopenharmony_ci	struct isc_device *isc = container_of(ctrl->handler,
150862306a36Sopenharmony_ci					     struct isc_device, ctrls.handler);
150962306a36Sopenharmony_ci	struct isc_ctrls *ctrls = &isc->ctrls;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
151262306a36Sopenharmony_ci		return 0;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	switch (ctrl->id) {
151562306a36Sopenharmony_ci	case V4L2_CID_AUTO_WHITE_BALANCE:
151662306a36Sopenharmony_ci		if (ctrl->val == 1)
151762306a36Sopenharmony_ci			ctrls->awb = ISC_WB_AUTO;
151862306a36Sopenharmony_ci		else
151962306a36Sopenharmony_ci			ctrls->awb = ISC_WB_NONE;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci		/* configure the controls with new values from v4l2 */
152262306a36Sopenharmony_ci		if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new)
152362306a36Sopenharmony_ci			ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val;
152462306a36Sopenharmony_ci		if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new)
152562306a36Sopenharmony_ci			ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val;
152662306a36Sopenharmony_ci		if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new)
152762306a36Sopenharmony_ci			ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val;
152862306a36Sopenharmony_ci		if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new)
152962306a36Sopenharmony_ci			ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci		if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new)
153262306a36Sopenharmony_ci			ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val;
153362306a36Sopenharmony_ci		if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new)
153462306a36Sopenharmony_ci			ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val;
153562306a36Sopenharmony_ci		if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new)
153662306a36Sopenharmony_ci			ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val;
153762306a36Sopenharmony_ci		if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new)
153862306a36Sopenharmony_ci			ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci		isc_update_awb_ctrls(isc);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci		mutex_lock(&isc->awb_mutex);
154362306a36Sopenharmony_ci		if (vb2_is_streaming(&isc->vb2_vidq)) {
154462306a36Sopenharmony_ci			/*
154562306a36Sopenharmony_ci			 * If we are streaming, we can update profile to
154662306a36Sopenharmony_ci			 * have the new settings in place.
154762306a36Sopenharmony_ci			 */
154862306a36Sopenharmony_ci			isc_update_profile(isc);
154962306a36Sopenharmony_ci		} else {
155062306a36Sopenharmony_ci			/*
155162306a36Sopenharmony_ci			 * The auto cluster will activate automatically this
155262306a36Sopenharmony_ci			 * control. This has to be deactivated when not
155362306a36Sopenharmony_ci			 * streaming.
155462306a36Sopenharmony_ci			 */
155562306a36Sopenharmony_ci			v4l2_ctrl_activate(isc->do_wb_ctrl, false);
155662306a36Sopenharmony_ci		}
155762306a36Sopenharmony_ci		mutex_unlock(&isc->awb_mutex);
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci		/* if we have autowhitebalance on, start histogram procedure */
156062306a36Sopenharmony_ci		if (ctrls->awb == ISC_WB_AUTO &&
156162306a36Sopenharmony_ci		    vb2_is_streaming(&isc->vb2_vidq) &&
156262306a36Sopenharmony_ci		    ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
156362306a36Sopenharmony_ci			isc_set_histogram(isc, true);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci		/*
156662306a36Sopenharmony_ci		 * for one time whitebalance adjustment, check the button,
156762306a36Sopenharmony_ci		 * if it's pressed, perform the one time operation.
156862306a36Sopenharmony_ci		 */
156962306a36Sopenharmony_ci		if (ctrls->awb == ISC_WB_NONE &&
157062306a36Sopenharmony_ci		    ctrl->cluster[ISC_CTRL_DO_WB]->is_new &&
157162306a36Sopenharmony_ci		    !(ctrl->cluster[ISC_CTRL_DO_WB]->flags &
157262306a36Sopenharmony_ci		    V4L2_CTRL_FLAG_INACTIVE)) {
157362306a36Sopenharmony_ci			ctrls->awb = ISC_WB_ONETIME;
157462306a36Sopenharmony_ci			isc_set_histogram(isc, true);
157562306a36Sopenharmony_ci			dev_dbg(isc->dev, "One time white-balance started.\n");
157662306a36Sopenharmony_ci		}
157762306a36Sopenharmony_ci		return 0;
157862306a36Sopenharmony_ci	}
157962306a36Sopenharmony_ci	return 0;
158062306a36Sopenharmony_ci}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_cistatic int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	struct isc_device *isc = container_of(ctrl->handler,
158562306a36Sopenharmony_ci					     struct isc_device, ctrls.handler);
158662306a36Sopenharmony_ci	struct isc_ctrls *ctrls = &isc->ctrls;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	switch (ctrl->id) {
158962306a36Sopenharmony_ci	/* being a cluster, this id will be called for every control */
159062306a36Sopenharmony_ci	case V4L2_CID_AUTO_WHITE_BALANCE:
159162306a36Sopenharmony_ci		ctrl->cluster[ISC_CTRL_R_GAIN]->val =
159262306a36Sopenharmony_ci					ctrls->gain[ISC_HIS_CFG_MODE_R];
159362306a36Sopenharmony_ci		ctrl->cluster[ISC_CTRL_B_GAIN]->val =
159462306a36Sopenharmony_ci					ctrls->gain[ISC_HIS_CFG_MODE_B];
159562306a36Sopenharmony_ci		ctrl->cluster[ISC_CTRL_GR_GAIN]->val =
159662306a36Sopenharmony_ci					ctrls->gain[ISC_HIS_CFG_MODE_GR];
159762306a36Sopenharmony_ci		ctrl->cluster[ISC_CTRL_GB_GAIN]->val =
159862306a36Sopenharmony_ci					ctrls->gain[ISC_HIS_CFG_MODE_GB];
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci		ctrl->cluster[ISC_CTRL_R_OFF]->val =
160162306a36Sopenharmony_ci			ctrls->offset[ISC_HIS_CFG_MODE_R];
160262306a36Sopenharmony_ci		ctrl->cluster[ISC_CTRL_B_OFF]->val =
160362306a36Sopenharmony_ci			ctrls->offset[ISC_HIS_CFG_MODE_B];
160462306a36Sopenharmony_ci		ctrl->cluster[ISC_CTRL_GR_OFF]->val =
160562306a36Sopenharmony_ci			ctrls->offset[ISC_HIS_CFG_MODE_GR];
160662306a36Sopenharmony_ci		ctrl->cluster[ISC_CTRL_GB_OFF]->val =
160762306a36Sopenharmony_ci			ctrls->offset[ISC_HIS_CFG_MODE_GB];
160862306a36Sopenharmony_ci		break;
160962306a36Sopenharmony_ci	}
161062306a36Sopenharmony_ci	return 0;
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops isc_awb_ops = {
161462306a36Sopenharmony_ci	.s_ctrl = isc_s_awb_ctrl,
161562306a36Sopenharmony_ci	.g_volatile_ctrl = isc_g_volatile_awb_ctrl,
161662306a36Sopenharmony_ci};
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci#define ISC_CTRL_OFF(_name, _id, _name_str) \
161962306a36Sopenharmony_ci	static const struct v4l2_ctrl_config _name = { \
162062306a36Sopenharmony_ci		.ops = &isc_awb_ops, \
162162306a36Sopenharmony_ci		.id = _id, \
162262306a36Sopenharmony_ci		.name = _name_str, \
162362306a36Sopenharmony_ci		.type = V4L2_CTRL_TYPE_INTEGER, \
162462306a36Sopenharmony_ci		.flags = V4L2_CTRL_FLAG_SLIDER, \
162562306a36Sopenharmony_ci		.min = -4095, \
162662306a36Sopenharmony_ci		.max = 4095, \
162762306a36Sopenharmony_ci		.step = 1, \
162862306a36Sopenharmony_ci		.def = 0, \
162962306a36Sopenharmony_ci	}
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ciISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset");
163262306a36Sopenharmony_ciISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset");
163362306a36Sopenharmony_ciISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset");
163462306a36Sopenharmony_ciISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset");
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci#define ISC_CTRL_GAIN(_name, _id, _name_str) \
163762306a36Sopenharmony_ci	static const struct v4l2_ctrl_config _name = { \
163862306a36Sopenharmony_ci		.ops = &isc_awb_ops, \
163962306a36Sopenharmony_ci		.id = _id, \
164062306a36Sopenharmony_ci		.name = _name_str, \
164162306a36Sopenharmony_ci		.type = V4L2_CTRL_TYPE_INTEGER, \
164262306a36Sopenharmony_ci		.flags = V4L2_CTRL_FLAG_SLIDER, \
164362306a36Sopenharmony_ci		.min = 0, \
164462306a36Sopenharmony_ci		.max = 8191, \
164562306a36Sopenharmony_ci		.step = 1, \
164662306a36Sopenharmony_ci		.def = 512, \
164762306a36Sopenharmony_ci	}
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ciISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain");
165062306a36Sopenharmony_ciISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain");
165162306a36Sopenharmony_ciISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain");
165262306a36Sopenharmony_ciISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain");
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_cistatic int isc_ctrl_init(struct isc_device *isc)
165562306a36Sopenharmony_ci{
165662306a36Sopenharmony_ci	const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
165762306a36Sopenharmony_ci	struct isc_ctrls *ctrls = &isc->ctrls;
165862306a36Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
165962306a36Sopenharmony_ci	int ret;
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	ctrls->hist_stat = HIST_INIT;
166262306a36Sopenharmony_ci	isc_reset_awb_ctrls(isc);
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	ret = v4l2_ctrl_handler_init(hdl, 13);
166562306a36Sopenharmony_ci	if (ret < 0)
166662306a36Sopenharmony_ci		return ret;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	/* Initialize product specific controls. For example, contrast */
166962306a36Sopenharmony_ci	isc->config_ctrls(isc, ops);
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	ctrls->brightness = 0;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
167462306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, isc->gamma_max, 1,
167562306a36Sopenharmony_ci			  isc->gamma_max);
167662306a36Sopenharmony_ci	isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
167762306a36Sopenharmony_ci					  V4L2_CID_AUTO_WHITE_BALANCE,
167862306a36Sopenharmony_ci					  0, 1, 1, 1);
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	/* do_white_balance is a button, so min,max,step,default are ignored */
168162306a36Sopenharmony_ci	isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
168262306a36Sopenharmony_ci					    V4L2_CID_DO_WHITE_BALANCE,
168362306a36Sopenharmony_ci					    0, 0, 0, 0);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	if (!isc->do_wb_ctrl) {
168662306a36Sopenharmony_ci		ret = hdl->error;
168762306a36Sopenharmony_ci		v4l2_ctrl_handler_free(hdl);
168862306a36Sopenharmony_ci		return ret;
168962306a36Sopenharmony_ci	}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	v4l2_ctrl_activate(isc->do_wb_ctrl, false);
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL);
169462306a36Sopenharmony_ci	isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL);
169562306a36Sopenharmony_ci	isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL);
169662306a36Sopenharmony_ci	isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL);
169762306a36Sopenharmony_ci	isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL);
169862306a36Sopenharmony_ci	isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL);
169962306a36Sopenharmony_ci	isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL);
170062306a36Sopenharmony_ci	isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL);
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	/*
170362306a36Sopenharmony_ci	 * The cluster is in auto mode with autowhitebalance enabled
170462306a36Sopenharmony_ci	 * and manual mode otherwise.
170562306a36Sopenharmony_ci	 */
170662306a36Sopenharmony_ci	v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true);
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	v4l2_ctrl_handler_setup(hdl);
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	return 0;
171162306a36Sopenharmony_ci}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_cistatic int isc_async_bound(struct v4l2_async_notifier *notifier,
171462306a36Sopenharmony_ci			   struct v4l2_subdev *subdev,
171562306a36Sopenharmony_ci			   struct v4l2_async_connection *asd)
171662306a36Sopenharmony_ci{
171762306a36Sopenharmony_ci	struct isc_device *isc = container_of(notifier->v4l2_dev,
171862306a36Sopenharmony_ci					      struct isc_device, v4l2_dev);
171962306a36Sopenharmony_ci	struct isc_subdev_entity *subdev_entity =
172062306a36Sopenharmony_ci		container_of(notifier, struct isc_subdev_entity, notifier);
172162306a36Sopenharmony_ci	int pad;
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	if (video_is_registered(&isc->video_dev)) {
172462306a36Sopenharmony_ci		dev_err(isc->dev, "only supports one sub-device.\n");
172562306a36Sopenharmony_ci		return -EBUSY;
172662306a36Sopenharmony_ci	}
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	subdev_entity->sd = subdev;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
173162306a36Sopenharmony_ci					  MEDIA_PAD_FL_SOURCE);
173262306a36Sopenharmony_ci	if (pad < 0) {
173362306a36Sopenharmony_ci		dev_err(isc->dev, "failed to find pad for %s\n", subdev->name);
173462306a36Sopenharmony_ci		return pad;
173562306a36Sopenharmony_ci	}
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	isc->remote_pad = pad;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	return 0;
174062306a36Sopenharmony_ci}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_cistatic void isc_async_unbind(struct v4l2_async_notifier *notifier,
174362306a36Sopenharmony_ci			     struct v4l2_subdev *subdev,
174462306a36Sopenharmony_ci			     struct v4l2_async_connection *asd)
174562306a36Sopenharmony_ci{
174662306a36Sopenharmony_ci	struct isc_device *isc = container_of(notifier->v4l2_dev,
174762306a36Sopenharmony_ci					      struct isc_device, v4l2_dev);
174862306a36Sopenharmony_ci	mutex_destroy(&isc->awb_mutex);
174962306a36Sopenharmony_ci	cancel_work_sync(&isc->awb_work);
175062306a36Sopenharmony_ci	video_unregister_device(&isc->video_dev);
175162306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&isc->ctrls.handler);
175262306a36Sopenharmony_ci}
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_cistruct isc_format *isc_find_format_by_code(struct isc_device *isc,
175562306a36Sopenharmony_ci					   unsigned int code, int *index)
175662306a36Sopenharmony_ci{
175762306a36Sopenharmony_ci	struct isc_format *fmt = &isc->formats_list[0];
175862306a36Sopenharmony_ci	unsigned int i;
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	for (i = 0; i < isc->formats_list_size; i++) {
176162306a36Sopenharmony_ci		if (fmt->mbus_code == code) {
176262306a36Sopenharmony_ci			*index = i;
176362306a36Sopenharmony_ci			return fmt;
176462306a36Sopenharmony_ci		}
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci		fmt++;
176762306a36Sopenharmony_ci	}
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	return NULL;
177062306a36Sopenharmony_ci}
177162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(isc_find_format_by_code);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_cistatic int isc_set_default_fmt(struct isc_device *isc)
177462306a36Sopenharmony_ci{
177562306a36Sopenharmony_ci	struct v4l2_format f = {
177662306a36Sopenharmony_ci		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
177762306a36Sopenharmony_ci		.fmt.pix = {
177862306a36Sopenharmony_ci			.width		= VGA_WIDTH,
177962306a36Sopenharmony_ci			.height		= VGA_HEIGHT,
178062306a36Sopenharmony_ci			.field		= V4L2_FIELD_NONE,
178162306a36Sopenharmony_ci			.pixelformat	= isc->controller_formats[0].fourcc,
178262306a36Sopenharmony_ci		},
178362306a36Sopenharmony_ci	};
178462306a36Sopenharmony_ci	int ret;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	ret = isc_try_fmt(isc, &f);
178762306a36Sopenharmony_ci	if (ret)
178862306a36Sopenharmony_ci		return ret;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	isc->fmt = f;
179162306a36Sopenharmony_ci	return 0;
179262306a36Sopenharmony_ci}
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_cistatic int isc_async_complete(struct v4l2_async_notifier *notifier)
179562306a36Sopenharmony_ci{
179662306a36Sopenharmony_ci	struct isc_device *isc = container_of(notifier->v4l2_dev,
179762306a36Sopenharmony_ci					      struct isc_device, v4l2_dev);
179862306a36Sopenharmony_ci	struct video_device *vdev = &isc->video_dev;
179962306a36Sopenharmony_ci	struct vb2_queue *q = &isc->vb2_vidq;
180062306a36Sopenharmony_ci	int ret = 0;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	INIT_WORK(&isc->awb_work, isc_awb_work);
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev);
180562306a36Sopenharmony_ci	if (ret < 0) {
180662306a36Sopenharmony_ci		dev_err(isc->dev, "Failed to register subdev nodes\n");
180762306a36Sopenharmony_ci		return ret;
180862306a36Sopenharmony_ci	}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	isc->current_subdev = container_of(notifier,
181162306a36Sopenharmony_ci					   struct isc_subdev_entity, notifier);
181262306a36Sopenharmony_ci	mutex_init(&isc->lock);
181362306a36Sopenharmony_ci	mutex_init(&isc->awb_mutex);
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	init_completion(&isc->comp);
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	/* Initialize videobuf2 queue */
181862306a36Sopenharmony_ci	q->type			= V4L2_BUF_TYPE_VIDEO_CAPTURE;
181962306a36Sopenharmony_ci	q->io_modes		= VB2_MMAP | VB2_DMABUF | VB2_READ;
182062306a36Sopenharmony_ci	q->drv_priv		= isc;
182162306a36Sopenharmony_ci	q->buf_struct_size	= sizeof(struct isc_buffer);
182262306a36Sopenharmony_ci	q->ops			= &isc_vb2_ops;
182362306a36Sopenharmony_ci	q->mem_ops		= &vb2_dma_contig_memops;
182462306a36Sopenharmony_ci	q->timestamp_flags	= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
182562306a36Sopenharmony_ci	q->lock			= &isc->lock;
182662306a36Sopenharmony_ci	q->min_buffers_needed	= 1;
182762306a36Sopenharmony_ci	q->dev			= isc->dev;
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	ret = vb2_queue_init(q);
183062306a36Sopenharmony_ci	if (ret < 0) {
183162306a36Sopenharmony_ci		dev_err(isc->dev, "vb2_queue_init() failed: %d\n", ret);
183262306a36Sopenharmony_ci		goto isc_async_complete_err;
183362306a36Sopenharmony_ci	}
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	/* Init video dma queues */
183662306a36Sopenharmony_ci	INIT_LIST_HEAD(&isc->dma_queue);
183762306a36Sopenharmony_ci	spin_lock_init(&isc->dma_queue_lock);
183862306a36Sopenharmony_ci	spin_lock_init(&isc->awb_lock);
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	ret = isc_set_default_fmt(isc);
184162306a36Sopenharmony_ci	if (ret) {
184262306a36Sopenharmony_ci		dev_err(isc->dev, "Could not set default format\n");
184362306a36Sopenharmony_ci		goto isc_async_complete_err;
184462306a36Sopenharmony_ci	}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	ret = isc_ctrl_init(isc);
184762306a36Sopenharmony_ci	if (ret) {
184862306a36Sopenharmony_ci		dev_err(isc->dev, "Init isc ctrols failed: %d\n", ret);
184962306a36Sopenharmony_ci		goto isc_async_complete_err;
185062306a36Sopenharmony_ci	}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	/* Register video device */
185362306a36Sopenharmony_ci	strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
185462306a36Sopenharmony_ci	vdev->release		= video_device_release_empty;
185562306a36Sopenharmony_ci	vdev->fops		= &isc_fops;
185662306a36Sopenharmony_ci	vdev->ioctl_ops		= &isc_ioctl_ops;
185762306a36Sopenharmony_ci	vdev->v4l2_dev		= &isc->v4l2_dev;
185862306a36Sopenharmony_ci	vdev->vfl_dir		= VFL_DIR_RX;
185962306a36Sopenharmony_ci	vdev->queue		= q;
186062306a36Sopenharmony_ci	vdev->lock		= &isc->lock;
186162306a36Sopenharmony_ci	vdev->ctrl_handler	= &isc->ctrls.handler;
186262306a36Sopenharmony_ci	vdev->device_caps	= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
186362306a36Sopenharmony_ci				  V4L2_CAP_IO_MC;
186462306a36Sopenharmony_ci	video_set_drvdata(vdev, isc);
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
186762306a36Sopenharmony_ci	if (ret < 0) {
186862306a36Sopenharmony_ci		dev_err(isc->dev, "video_register_device failed: %d\n", ret);
186962306a36Sopenharmony_ci		goto isc_async_complete_err;
187062306a36Sopenharmony_ci	}
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	ret = isc_scaler_link(isc);
187362306a36Sopenharmony_ci	if (ret < 0)
187462306a36Sopenharmony_ci		goto isc_async_complete_unregister_device;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	ret = media_device_register(&isc->mdev);
187762306a36Sopenharmony_ci	if (ret < 0)
187862306a36Sopenharmony_ci		goto isc_async_complete_unregister_device;
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	return 0;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ciisc_async_complete_unregister_device:
188362306a36Sopenharmony_ci	video_unregister_device(vdev);
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ciisc_async_complete_err:
188662306a36Sopenharmony_ci	mutex_destroy(&isc->awb_mutex);
188762306a36Sopenharmony_ci	mutex_destroy(&isc->lock);
188862306a36Sopenharmony_ci	return ret;
188962306a36Sopenharmony_ci}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ciconst struct v4l2_async_notifier_operations microchip_isc_async_ops = {
189262306a36Sopenharmony_ci	.bound = isc_async_bound,
189362306a36Sopenharmony_ci	.unbind = isc_async_unbind,
189462306a36Sopenharmony_ci	.complete = isc_async_complete,
189562306a36Sopenharmony_ci};
189662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_async_ops);
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_civoid microchip_isc_subdev_cleanup(struct isc_device *isc)
189962306a36Sopenharmony_ci{
190062306a36Sopenharmony_ci	struct isc_subdev_entity *subdev_entity;
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
190362306a36Sopenharmony_ci		v4l2_async_nf_unregister(&subdev_entity->notifier);
190462306a36Sopenharmony_ci		v4l2_async_nf_cleanup(&subdev_entity->notifier);
190562306a36Sopenharmony_ci	}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	INIT_LIST_HEAD(&isc->subdev_entities);
190862306a36Sopenharmony_ci}
190962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_subdev_cleanup);
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ciint microchip_isc_pipeline_init(struct isc_device *isc)
191262306a36Sopenharmony_ci{
191362306a36Sopenharmony_ci	struct device *dev = isc->dev;
191462306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
191562306a36Sopenharmony_ci	struct regmap_field *regs;
191662306a36Sopenharmony_ci	unsigned int i;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	/*
191962306a36Sopenharmony_ci	 * DPCEN-->GDCEN-->BLCEN-->WB-->CFA-->CC-->
192062306a36Sopenharmony_ci	 * GAM-->VHXS-->CSC-->CBC-->SUB422-->SUB420
192162306a36Sopenharmony_ci	 */
192262306a36Sopenharmony_ci	const struct reg_field regfields[ISC_PIPE_LINE_NODE_NUM] = {
192362306a36Sopenharmony_ci		REG_FIELD(ISC_DPC_CTRL, 0, 0),
192462306a36Sopenharmony_ci		REG_FIELD(ISC_DPC_CTRL, 1, 1),
192562306a36Sopenharmony_ci		REG_FIELD(ISC_DPC_CTRL, 2, 2),
192662306a36Sopenharmony_ci		REG_FIELD(ISC_WB_CTRL, 0, 0),
192762306a36Sopenharmony_ci		REG_FIELD(ISC_CFA_CTRL, 0, 0),
192862306a36Sopenharmony_ci		REG_FIELD(ISC_CC_CTRL, 0, 0),
192962306a36Sopenharmony_ci		REG_FIELD(ISC_GAM_CTRL, 0, 0),
193062306a36Sopenharmony_ci		REG_FIELD(ISC_GAM_CTRL, 1, 1),
193162306a36Sopenharmony_ci		REG_FIELD(ISC_GAM_CTRL, 2, 2),
193262306a36Sopenharmony_ci		REG_FIELD(ISC_GAM_CTRL, 3, 3),
193362306a36Sopenharmony_ci		REG_FIELD(ISC_VHXS_CTRL, 0, 0),
193462306a36Sopenharmony_ci		REG_FIELD(ISC_CSC_CTRL + isc->offsets.csc, 0, 0),
193562306a36Sopenharmony_ci		REG_FIELD(ISC_CBC_CTRL + isc->offsets.cbc, 0, 0),
193662306a36Sopenharmony_ci		REG_FIELD(ISC_SUB422_CTRL + isc->offsets.sub422, 0, 0),
193762306a36Sopenharmony_ci		REG_FIELD(ISC_SUB420_CTRL + isc->offsets.sub420, 0, 0),
193862306a36Sopenharmony_ci	};
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
194162306a36Sopenharmony_ci		regs = devm_regmap_field_alloc(dev, regmap, regfields[i]);
194262306a36Sopenharmony_ci		if (IS_ERR(regs))
194362306a36Sopenharmony_ci			return PTR_ERR(regs);
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci		isc->pipeline[i] =  regs;
194662306a36Sopenharmony_ci	}
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci	return 0;
194962306a36Sopenharmony_ci}
195062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_pipeline_init);
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_cistatic int isc_link_validate(struct media_link *link)
195362306a36Sopenharmony_ci{
195462306a36Sopenharmony_ci	struct video_device *vdev =
195562306a36Sopenharmony_ci		media_entity_to_video_device(link->sink->entity);
195662306a36Sopenharmony_ci	struct isc_device *isc = video_get_drvdata(vdev);
195762306a36Sopenharmony_ci	int ret;
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	ret = v4l2_subdev_link_validate(link);
196062306a36Sopenharmony_ci	if (ret)
196162306a36Sopenharmony_ci		return ret;
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	return isc_validate(isc);
196462306a36Sopenharmony_ci}
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_cistatic const struct media_entity_operations isc_entity_operations = {
196762306a36Sopenharmony_ci	.link_validate = isc_link_validate,
196862306a36Sopenharmony_ci};
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ciint isc_mc_init(struct isc_device *isc, u32 ver)
197162306a36Sopenharmony_ci{
197262306a36Sopenharmony_ci	const struct of_device_id *match;
197362306a36Sopenharmony_ci	int ret;
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	isc->video_dev.entity.function = MEDIA_ENT_F_IO_V4L;
197662306a36Sopenharmony_ci	isc->video_dev.entity.flags = MEDIA_ENT_FL_DEFAULT;
197762306a36Sopenharmony_ci	isc->video_dev.entity.ops = &isc_entity_operations;
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	isc->pads[ISC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	ret = media_entity_pads_init(&isc->video_dev.entity, ISC_PADS_NUM,
198262306a36Sopenharmony_ci				     isc->pads);
198362306a36Sopenharmony_ci	if (ret < 0) {
198462306a36Sopenharmony_ci		dev_err(isc->dev, "media entity init failed\n");
198562306a36Sopenharmony_ci		return ret;
198662306a36Sopenharmony_ci	}
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	isc->mdev.dev = isc->dev;
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	match = of_match_node(isc->dev->driver->of_match_table,
199162306a36Sopenharmony_ci			      isc->dev->of_node);
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	strscpy(isc->mdev.driver_name, KBUILD_MODNAME,
199462306a36Sopenharmony_ci		sizeof(isc->mdev.driver_name));
199562306a36Sopenharmony_ci	strscpy(isc->mdev.model, match->compatible, sizeof(isc->mdev.model));
199662306a36Sopenharmony_ci	snprintf(isc->mdev.bus_info, sizeof(isc->mdev.bus_info), "platform:%s",
199762306a36Sopenharmony_ci		 isc->v4l2_dev.name);
199862306a36Sopenharmony_ci	isc->mdev.hw_revision = ver;
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	media_device_init(&isc->mdev);
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	isc->v4l2_dev.mdev = &isc->mdev;
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	return isc_scaler_init(isc);
200562306a36Sopenharmony_ci}
200662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(isc_mc_init);
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_civoid isc_mc_cleanup(struct isc_device *isc)
200962306a36Sopenharmony_ci{
201062306a36Sopenharmony_ci	media_entity_cleanup(&isc->video_dev.entity);
201162306a36Sopenharmony_ci	media_device_cleanup(&isc->mdev);
201262306a36Sopenharmony_ci}
201362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(isc_mc_cleanup);
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci/* regmap configuration */
201662306a36Sopenharmony_ci#define MICROCHIP_ISC_REG_MAX    0xd5c
201762306a36Sopenharmony_ciconst struct regmap_config microchip_isc_regmap_config = {
201862306a36Sopenharmony_ci	.reg_bits       = 32,
201962306a36Sopenharmony_ci	.reg_stride     = 4,
202062306a36Sopenharmony_ci	.val_bits       = 32,
202162306a36Sopenharmony_ci	.max_register	= MICROCHIP_ISC_REG_MAX,
202262306a36Sopenharmony_ci};
202362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_regmap_config);
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ciMODULE_AUTHOR("Songjun Wu");
202662306a36Sopenharmony_ciMODULE_AUTHOR("Eugen Hristev");
202762306a36Sopenharmony_ciMODULE_DESCRIPTION("Microchip ISC common code base");
202862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
2029