155927c98SEugen Hristev // SPDX-License-Identifier: GPL-2.0
255927c98SEugen Hristev /*
355927c98SEugen Hristev  * Microchip eXtended Image Sensor Controller (XISC) driver
455927c98SEugen Hristev  *
555927c98SEugen Hristev  * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries
655927c98SEugen Hristev  *
755927c98SEugen Hristev  * Author: Eugen Hristev <eugen.hristev@microchip.com>
855927c98SEugen Hristev  *
955927c98SEugen Hristev  * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS
1055927c98SEugen Hristev  *
1155927c98SEugen Hristev  * ISC video pipeline integrates the following submodules:
1255927c98SEugen Hristev  * PFE: Parallel Front End to sample the camera sensor input stream
1355927c98SEugen Hristev  * DPC: Defective Pixel Correction with black offset correction, green disparity
1455927c98SEugen Hristev  *      correction and defective pixel correction (3 modules total)
1555927c98SEugen Hristev  *  WB: Programmable white balance in the Bayer domain
1655927c98SEugen Hristev  * CFA: Color filter array interpolation module
1755927c98SEugen Hristev  *  CC: Programmable color correction
1855927c98SEugen Hristev  * GAM: Gamma correction
1955927c98SEugen Hristev  *VHXS: Vertical and Horizontal Scaler
2055927c98SEugen Hristev  * CSC: Programmable color space conversion
2155927c98SEugen Hristev  *CBHS: Contrast Brightness Hue and Saturation control
2255927c98SEugen Hristev  * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
2355927c98SEugen Hristev  * RLP: This module performs rounding, range limiting
2455927c98SEugen Hristev  *      and packing of the incoming data
2555927c98SEugen Hristev  * DMA: This module performs DMA master accesses to write frames to external RAM
2655927c98SEugen Hristev  * HIS: Histogram module performs statistic counters on the frames
2755927c98SEugen Hristev  */
2855927c98SEugen Hristev 
2955927c98SEugen Hristev #include <linux/clk.h>
3055927c98SEugen Hristev #include <linux/clkdev.h>
3155927c98SEugen Hristev #include <linux/clk-provider.h>
3255927c98SEugen Hristev #include <linux/delay.h>
3355927c98SEugen Hristev #include <linux/interrupt.h>
3455927c98SEugen Hristev #include <linux/math64.h>
3555927c98SEugen Hristev #include <linux/module.h>
3655927c98SEugen Hristev #include <linux/of.h>
3755927c98SEugen Hristev #include <linux/of_graph.h>
3855927c98SEugen Hristev #include <linux/platform_device.h>
3955927c98SEugen Hristev #include <linux/pm_runtime.h>
4055927c98SEugen Hristev #include <linux/regmap.h>
4155927c98SEugen Hristev #include <linux/videodev2.h>
4255927c98SEugen Hristev 
4355927c98SEugen Hristev #include <media/v4l2-ctrls.h>
4455927c98SEugen Hristev #include <media/v4l2-device.h>
4555927c98SEugen Hristev #include <media/v4l2-event.h>
4655927c98SEugen Hristev #include <media/v4l2-image-sizes.h>
4755927c98SEugen Hristev #include <media/v4l2-ioctl.h>
4855927c98SEugen Hristev #include <media/v4l2-fwnode.h>
4955927c98SEugen Hristev #include <media/v4l2-subdev.h>
5055927c98SEugen Hristev #include <media/videobuf2-dma-contig.h>
5155927c98SEugen Hristev 
5255927c98SEugen Hristev #include "atmel-isc-regs.h"
5355927c98SEugen Hristev #include "atmel-isc.h"
5455927c98SEugen Hristev 
5555927c98SEugen Hristev #define ISC_SAMA7G5_MAX_SUPPORT_WIDTH   3264
5655927c98SEugen Hristev #define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT  2464
5755927c98SEugen Hristev 
5855927c98SEugen Hristev #define ISC_SAMA7G5_PIPELINE \
5955927c98SEugen Hristev 	(WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
6055927c98SEugen Hristev 	CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
6155927c98SEugen Hristev 
6255927c98SEugen Hristev /* This is a list of the formats that the ISC can *output* */
6355927c98SEugen Hristev static const struct isc_format sama7g5_controller_formats[] = {
6455927c98SEugen Hristev 	{
6555927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_ARGB444,
6655927c98SEugen Hristev 	}, {
6755927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_ARGB555,
6855927c98SEugen Hristev 	}, {
6955927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_RGB565,
7055927c98SEugen Hristev 	}, {
7155927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_ABGR32,
7255927c98SEugen Hristev 	}, {
7355927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_XBGR32,
7455927c98SEugen Hristev 	}, {
7555927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_YUV420,
7655927c98SEugen Hristev 	}, {
7755927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_UYVY,
7855927c98SEugen Hristev 	}, {
7955927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_VYUY,
8055927c98SEugen Hristev 	}, {
8155927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_YUYV,
8255927c98SEugen Hristev 	}, {
8355927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_YUV422P,
8455927c98SEugen Hristev 	}, {
8555927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_GREY,
8655927c98SEugen Hristev 	}, {
8755927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_Y10,
8855927c98SEugen Hristev 	}, {
8955927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_Y16,
9055927c98SEugen Hristev 	}, {
9155927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SBGGR8,
9255927c98SEugen Hristev 	}, {
9355927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SGBRG8,
9455927c98SEugen Hristev 	}, {
9555927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SGRBG8,
9655927c98SEugen Hristev 	}, {
9755927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SRGGB8,
9855927c98SEugen Hristev 	}, {
9955927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SBGGR10,
10055927c98SEugen Hristev 	}, {
10155927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SGBRG10,
10255927c98SEugen Hristev 	}, {
10355927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SGRBG10,
10455927c98SEugen Hristev 	}, {
10555927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SRGGB10,
10655927c98SEugen Hristev 	},
10755927c98SEugen Hristev };
10855927c98SEugen Hristev 
10955927c98SEugen Hristev /* This is a list of formats that the ISC can receive as *input* */
11055927c98SEugen Hristev static struct isc_format sama7g5_formats_list[] = {
11155927c98SEugen Hristev 	{
11255927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SBGGR8,
11355927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,
11455927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
11555927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_BGBG,
11655927c98SEugen Hristev 	},
11755927c98SEugen Hristev 	{
11855927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SGBRG8,
11955927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SGBRG8_1X8,
12055927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
12155927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
12255927c98SEugen Hristev 	},
12355927c98SEugen Hristev 	{
12455927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SGRBG8,
12555927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SGRBG8_1X8,
12655927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
12755927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
12855927c98SEugen Hristev 	},
12955927c98SEugen Hristev 	{
13055927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SRGGB8,
13155927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SRGGB8_1X8,
13255927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
13355927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
13455927c98SEugen Hristev 	},
13555927c98SEugen Hristev 	{
13655927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SBGGR10,
13755927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,
13855927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
13955927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
14055927c98SEugen Hristev 	},
14155927c98SEugen Hristev 	{
14255927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SGBRG10,
14355927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,
14455927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
14555927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
14655927c98SEugen Hristev 	},
14755927c98SEugen Hristev 	{
14855927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SGRBG10,
14955927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,
15055927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
15155927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
15255927c98SEugen Hristev 	},
15355927c98SEugen Hristev 	{
15455927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SRGGB10,
15555927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,
15655927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
15755927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
15855927c98SEugen Hristev 	},
15955927c98SEugen Hristev 	{
16055927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SBGGR12,
16155927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SBGGR12_1X12,
16255927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
16355927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_BGBG,
16455927c98SEugen Hristev 	},
16555927c98SEugen Hristev 	{
16655927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SGBRG12,
16755927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SGBRG12_1X12,
16855927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
16955927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
17055927c98SEugen Hristev 	},
17155927c98SEugen Hristev 	{
17255927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SGRBG12,
17355927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SGRBG12_1X12,
17455927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
17555927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
17655927c98SEugen Hristev 	},
17755927c98SEugen Hristev 	{
17855927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_SRGGB12,
17955927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_SRGGB12_1X12,
18055927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
18155927c98SEugen Hristev 		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
18255927c98SEugen Hristev 	},
18355927c98SEugen Hristev 	{
18455927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_GREY,
18555927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_Y8_1X8,
18655927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
18755927c98SEugen Hristev 	},
18855927c98SEugen Hristev 	{
18955927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_YUYV,
19055927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_YUYV8_2X8,
19155927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
19255927c98SEugen Hristev 	},
19355927c98SEugen Hristev 	{
19455927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_UYVY,
19555927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_UYVY8_2X8,
19655927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
19755927c98SEugen Hristev 	},
19855927c98SEugen Hristev 	{
19955927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_RGB565,
20055927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_LE,
20155927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
20255927c98SEugen Hristev 	},
20355927c98SEugen Hristev 	{
20455927c98SEugen Hristev 		.fourcc		= V4L2_PIX_FMT_Y10,
20555927c98SEugen Hristev 		.mbus_code	= MEDIA_BUS_FMT_Y10_1X10,
20655927c98SEugen Hristev 		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
20755927c98SEugen Hristev 	},
20855927c98SEugen Hristev };
20955927c98SEugen Hristev 
isc_sama7g5_config_csc(struct isc_device * isc)21055927c98SEugen Hristev static void isc_sama7g5_config_csc(struct isc_device *isc)
21155927c98SEugen Hristev {
21255927c98SEugen Hristev 	struct regmap *regmap = isc->regmap;
21355927c98SEugen Hristev 
21455927c98SEugen Hristev 	/* Convert RGB to YUV */
21555927c98SEugen Hristev 	regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
21655927c98SEugen Hristev 		     0x42 | (0x81 << 16));
21755927c98SEugen Hristev 	regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
21855927c98SEugen Hristev 		     0x19 | (0x10 << 16));
21955927c98SEugen Hristev 	regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
22055927c98SEugen Hristev 		     0xFDA | (0xFB6 << 16));
22155927c98SEugen Hristev 	regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
22255927c98SEugen Hristev 		     0x70 | (0x80 << 16));
22355927c98SEugen Hristev 	regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
22455927c98SEugen Hristev 		     0x70 | (0xFA2 << 16));
22555927c98SEugen Hristev 	regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
22655927c98SEugen Hristev 		     0xFEE | (0x80 << 16));
22755927c98SEugen Hristev }
22855927c98SEugen Hristev 
isc_sama7g5_config_cbc(struct isc_device * isc)22955927c98SEugen Hristev static void isc_sama7g5_config_cbc(struct isc_device *isc)
23055927c98SEugen Hristev {
23155927c98SEugen Hristev 	struct regmap *regmap = isc->regmap;
23255927c98SEugen Hristev 
23355927c98SEugen Hristev 	/* Configure what is set via v4l2 ctrls */
23455927c98SEugen Hristev 	regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness);
23555927c98SEugen Hristev 	regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast);
23655927c98SEugen Hristev 	/* Configure Hue and Saturation as neutral midpoint */
23755927c98SEugen Hristev 	regmap_write(regmap, ISC_CBCHS_HUE, 0);
23855927c98SEugen Hristev 	regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4));
23955927c98SEugen Hristev }
24055927c98SEugen Hristev 
isc_sama7g5_config_cc(struct isc_device * isc)24155927c98SEugen Hristev static void isc_sama7g5_config_cc(struct isc_device *isc)
24255927c98SEugen Hristev {
24355927c98SEugen Hristev 	struct regmap *regmap = isc->regmap;
24455927c98SEugen Hristev 
24555927c98SEugen Hristev 	/* Configure each register at the neutral fixed point 1.0 or 0.0 */
24655927c98SEugen Hristev 	regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
24755927c98SEugen Hristev 	regmap_write(regmap, ISC_CC_RB_OR, 0);
24855927c98SEugen Hristev 	regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
24955927c98SEugen Hristev 	regmap_write(regmap, ISC_CC_GB_OG, 0);
25055927c98SEugen Hristev 	regmap_write(regmap, ISC_CC_BR_BG, 0);
25155927c98SEugen Hristev 	regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
25255927c98SEugen Hristev }
25355927c98SEugen Hristev 
isc_sama7g5_config_ctrls(struct isc_device * isc,const struct v4l2_ctrl_ops * ops)25455927c98SEugen Hristev static void isc_sama7g5_config_ctrls(struct isc_device *isc,
25555927c98SEugen Hristev 				     const struct v4l2_ctrl_ops *ops)
25655927c98SEugen Hristev {
25755927c98SEugen Hristev 	struct isc_ctrls *ctrls = &isc->ctrls;
25855927c98SEugen Hristev 	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
25955927c98SEugen Hristev 
26055927c98SEugen Hristev 	ctrls->contrast = 16;
26155927c98SEugen Hristev 
26255927c98SEugen Hristev 	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16);
26355927c98SEugen Hristev }
26455927c98SEugen Hristev 
isc_sama7g5_config_dpc(struct isc_device * isc)26555927c98SEugen Hristev static void isc_sama7g5_config_dpc(struct isc_device *isc)
26655927c98SEugen Hristev {
26755927c98SEugen Hristev 	u32 bay_cfg = isc->config.sd_format->cfa_baycfg;
26855927c98SEugen Hristev 	struct regmap *regmap = isc->regmap;
26955927c98SEugen Hristev 
27055927c98SEugen Hristev 	regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK,
27155927c98SEugen Hristev 			   (64 << ISC_DPC_CFG_BLOFF_SHIFT));
27255927c98SEugen Hristev 	regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK,
27355927c98SEugen Hristev 			   (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT));
27455927c98SEugen Hristev }
27555927c98SEugen Hristev 
isc_sama7g5_config_gam(struct isc_device * isc)27655927c98SEugen Hristev static void isc_sama7g5_config_gam(struct isc_device *isc)
27755927c98SEugen Hristev {
27855927c98SEugen Hristev 	struct regmap *regmap = isc->regmap;
27955927c98SEugen Hristev 
28055927c98SEugen Hristev 	regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART,
28155927c98SEugen Hristev 			   ISC_GAM_CTRL_BIPART);
28255927c98SEugen Hristev }
28355927c98SEugen Hristev 
isc_sama7g5_config_rlp(struct isc_device * isc)28455927c98SEugen Hristev static void isc_sama7g5_config_rlp(struct isc_device *isc)
28555927c98SEugen Hristev {
28655927c98SEugen Hristev 	struct regmap *regmap = isc->regmap;
28755927c98SEugen Hristev 	u32 rlp_mode = isc->config.rlp_cfg_mode;
28855927c98SEugen Hristev 
28955927c98SEugen Hristev 	regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
29055927c98SEugen Hristev 			   ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH |
29155927c98SEugen Hristev 			   ISC_RLP_CFG_YMODE_MASK, rlp_mode);
29255927c98SEugen Hristev }
29355927c98SEugen Hristev 
isc_sama7g5_adapt_pipeline(struct isc_device * isc)29455927c98SEugen Hristev static void isc_sama7g5_adapt_pipeline(struct isc_device *isc)
29555927c98SEugen Hristev {
29655927c98SEugen Hristev 	isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE;
29755927c98SEugen Hristev }
29855927c98SEugen Hristev 
29955927c98SEugen Hristev /* Gamma table with gamma 1/2.2 */
30055927c98SEugen Hristev static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
30155927c98SEugen Hristev 	/* index 0 --> gamma bipartite */
30255927c98SEugen Hristev 	{
30355927c98SEugen Hristev 	      0x980,  0x4c0320,  0x650260,  0x7801e0,  0x8701a0,  0x940180,
30455927c98SEugen Hristev 	   0xa00160,  0xab0120,  0xb40120,  0xbd0120,  0xc60100,  0xce0100,
30555927c98SEugen Hristev 	   0xd600e0,  0xdd00e0,  0xe400e0,  0xeb00c0,  0xf100c0,  0xf700c0,
30655927c98SEugen Hristev 	   0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0,
30755927c98SEugen Hristev 	  0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080,
30855927c98SEugen Hristev 	  0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a,
30955927c98SEugen Hristev 	  0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030,
31055927c98SEugen Hristev 	  0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026,
31155927c98SEugen Hristev 	  0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020,
31255927c98SEugen Hristev 	  0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c,
31355927c98SEugen Hristev 	  0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a },
31455927c98SEugen Hristev };
31555927c98SEugen Hristev 
xisc_parse_dt(struct device * dev,struct isc_device * isc)31655927c98SEugen Hristev static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
31755927c98SEugen Hristev {
31855927c98SEugen Hristev 	struct device_node *np = dev->of_node;
31955927c98SEugen Hristev 	struct device_node *epn = NULL;
32055927c98SEugen Hristev 	struct isc_subdev_entity *subdev_entity;
32155927c98SEugen Hristev 	unsigned int flags;
32255927c98SEugen Hristev 	int ret;
32355927c98SEugen Hristev 	bool mipi_mode;
32455927c98SEugen Hristev 
32555927c98SEugen Hristev 	INIT_LIST_HEAD(&isc->subdev_entities);
32655927c98SEugen Hristev 
32755927c98SEugen Hristev 	mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
32855927c98SEugen Hristev 
32955927c98SEugen Hristev 	while (1) {
33055927c98SEugen Hristev 		struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
33155927c98SEugen Hristev 
33255927c98SEugen Hristev 		epn = of_graph_get_next_endpoint(np, epn);
33355927c98SEugen Hristev 		if (!epn)
33455927c98SEugen Hristev 			return 0;
33555927c98SEugen Hristev 
33655927c98SEugen Hristev 		ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
33755927c98SEugen Hristev 						 &v4l2_epn);
33855927c98SEugen Hristev 		if (ret) {
33955927c98SEugen Hristev 			ret = -EINVAL;
34055927c98SEugen Hristev 			dev_err(dev, "Could not parse the endpoint\n");
34155927c98SEugen Hristev 			break;
34255927c98SEugen Hristev 		}
34355927c98SEugen Hristev 
34455927c98SEugen Hristev 		subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
34555927c98SEugen Hristev 					     GFP_KERNEL);
34655927c98SEugen Hristev 		if (!subdev_entity) {
34755927c98SEugen Hristev 			ret = -ENOMEM;
34855927c98SEugen Hristev 			break;
34955927c98SEugen Hristev 		}
35055927c98SEugen Hristev 		subdev_entity->epn = epn;
35155927c98SEugen Hristev 
35255927c98SEugen Hristev 		flags = v4l2_epn.bus.parallel.flags;
35355927c98SEugen Hristev 
35455927c98SEugen Hristev 		if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
35555927c98SEugen Hristev 			subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
35655927c98SEugen Hristev 
35755927c98SEugen Hristev 		if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
35855927c98SEugen Hristev 			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
35955927c98SEugen Hristev 
36055927c98SEugen Hristev 		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
36155927c98SEugen Hristev 			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
36255927c98SEugen Hristev 
36355927c98SEugen Hristev 		if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
36455927c98SEugen Hristev 			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
36555927c98SEugen Hristev 					ISC_PFE_CFG0_CCIR656;
36655927c98SEugen Hristev 
36755927c98SEugen Hristev 		if (mipi_mode)
36855927c98SEugen Hristev 			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
36955927c98SEugen Hristev 
37055927c98SEugen Hristev 		list_add_tail(&subdev_entity->list, &isc->subdev_entities);
37155927c98SEugen Hristev 	}
37255927c98SEugen Hristev 	of_node_put(epn);
37355927c98SEugen Hristev 
37455927c98SEugen Hristev 	return ret;
37555927c98SEugen Hristev }
37655927c98SEugen Hristev 
microchip_xisc_probe(struct platform_device * pdev)37755927c98SEugen Hristev static int microchip_xisc_probe(struct platform_device *pdev)
37855927c98SEugen Hristev {
37955927c98SEugen Hristev 	struct device *dev = &pdev->dev;
38055927c98SEugen Hristev 	struct isc_device *isc;
38155927c98SEugen Hristev 	void __iomem *io_base;
38255927c98SEugen Hristev 	struct isc_subdev_entity *subdev_entity;
38355927c98SEugen Hristev 	int irq;
38455927c98SEugen Hristev 	int ret;
38555927c98SEugen Hristev 	u32 ver;
38655927c98SEugen Hristev 
38755927c98SEugen Hristev 	isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
38855927c98SEugen Hristev 	if (!isc)
38955927c98SEugen Hristev 		return -ENOMEM;
39055927c98SEugen Hristev 
39155927c98SEugen Hristev 	platform_set_drvdata(pdev, isc);
39255927c98SEugen Hristev 	isc->dev = dev;
39355927c98SEugen Hristev 
394b6f790a5SYang Li 	io_base = devm_platform_ioremap_resource(pdev, 0);
39555927c98SEugen Hristev 	if (IS_ERR(io_base))
39655927c98SEugen Hristev 		return PTR_ERR(io_base);
39755927c98SEugen Hristev 
39855927c98SEugen Hristev 	isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config);
39955927c98SEugen Hristev 	if (IS_ERR(isc->regmap)) {
40055927c98SEugen Hristev 		ret = PTR_ERR(isc->regmap);
40155927c98SEugen Hristev 		dev_err(dev, "failed to init register map: %d\n", ret);
40255927c98SEugen Hristev 		return ret;
40355927c98SEugen Hristev 	}
40455927c98SEugen Hristev 
40555927c98SEugen Hristev 	irq = platform_get_irq(pdev, 0);
40655927c98SEugen Hristev 	if (irq < 0)
40755927c98SEugen Hristev 		return irq;
40855927c98SEugen Hristev 
40955927c98SEugen Hristev 	ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0,
41055927c98SEugen Hristev 			       "microchip-sama7g5-xisc", isc);
41155927c98SEugen Hristev 	if (ret < 0) {
41255927c98SEugen Hristev 		dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
41355927c98SEugen Hristev 			irq, ret);
41455927c98SEugen Hristev 		return ret;
41555927c98SEugen Hristev 	}
41655927c98SEugen Hristev 
41755927c98SEugen Hristev 	isc->gamma_table = isc_sama7g5_gamma_table;
41855927c98SEugen Hristev 	isc->gamma_max = 0;
41955927c98SEugen Hristev 
42055927c98SEugen Hristev 	isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
42155927c98SEugen Hristev 	isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
42255927c98SEugen Hristev 
42355927c98SEugen Hristev 	isc->config_dpc = isc_sama7g5_config_dpc;
42455927c98SEugen Hristev 	isc->config_csc = isc_sama7g5_config_csc;
42555927c98SEugen Hristev 	isc->config_cbc = isc_sama7g5_config_cbc;
42655927c98SEugen Hristev 	isc->config_cc = isc_sama7g5_config_cc;
42755927c98SEugen Hristev 	isc->config_gam = isc_sama7g5_config_gam;
42855927c98SEugen Hristev 	isc->config_rlp = isc_sama7g5_config_rlp;
42955927c98SEugen Hristev 	isc->config_ctrls = isc_sama7g5_config_ctrls;
43055927c98SEugen Hristev 
43155927c98SEugen Hristev 	isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
43255927c98SEugen Hristev 
43355927c98SEugen Hristev 	isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
43455927c98SEugen Hristev 	isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
43555927c98SEugen Hristev 	isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
43655927c98SEugen Hristev 	isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
43755927c98SEugen Hristev 	isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
43855927c98SEugen Hristev 	isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
43955927c98SEugen Hristev 	isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
44055927c98SEugen Hristev 	isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
44155927c98SEugen Hristev 	isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
44255927c98SEugen Hristev 
44355927c98SEugen Hristev 	isc->controller_formats = sama7g5_controller_formats;
44455927c98SEugen Hristev 	isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
44555927c98SEugen Hristev 	isc->formats_list = sama7g5_formats_list;
44655927c98SEugen Hristev 	isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
44755927c98SEugen Hristev 
44855927c98SEugen Hristev 	/* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
44955927c98SEugen Hristev 	isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
45055927c98SEugen Hristev 
45155927c98SEugen Hristev 	/* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
45255927c98SEugen Hristev 	isc->ispck_required = false;
45355927c98SEugen Hristev 
45455927c98SEugen Hristev 	ret = atmel_isc_pipeline_init(isc);
45555927c98SEugen Hristev 	if (ret)
45655927c98SEugen Hristev 		return ret;
45755927c98SEugen Hristev 
45855927c98SEugen Hristev 	isc->hclock = devm_clk_get(dev, "hclock");
45955927c98SEugen Hristev 	if (IS_ERR(isc->hclock)) {
46055927c98SEugen Hristev 		ret = PTR_ERR(isc->hclock);
46155927c98SEugen Hristev 		dev_err(dev, "failed to get hclock: %d\n", ret);
46255927c98SEugen Hristev 		return ret;
46355927c98SEugen Hristev 	}
46455927c98SEugen Hristev 
46555927c98SEugen Hristev 	ret = clk_prepare_enable(isc->hclock);
46655927c98SEugen Hristev 	if (ret) {
46755927c98SEugen Hristev 		dev_err(dev, "failed to enable hclock: %d\n", ret);
46855927c98SEugen Hristev 		return ret;
46955927c98SEugen Hristev 	}
47055927c98SEugen Hristev 
47155927c98SEugen Hristev 	ret = atmel_isc_clk_init(isc);
47255927c98SEugen Hristev 	if (ret) {
47355927c98SEugen Hristev 		dev_err(dev, "failed to init isc clock: %d\n", ret);
47455927c98SEugen Hristev 		goto unprepare_hclk;
47555927c98SEugen Hristev 	}
47655927c98SEugen Hristev 
47755927c98SEugen Hristev 	ret = v4l2_device_register(dev, &isc->v4l2_dev);
47855927c98SEugen Hristev 	if (ret) {
47955927c98SEugen Hristev 		dev_err(dev, "unable to register v4l2 device.\n");
48055927c98SEugen Hristev 		goto unprepare_hclk;
48155927c98SEugen Hristev 	}
48255927c98SEugen Hristev 
48355927c98SEugen Hristev 	ret = xisc_parse_dt(dev, isc);
48455927c98SEugen Hristev 	if (ret) {
48555927c98SEugen Hristev 		dev_err(dev, "fail to parse device tree\n");
48655927c98SEugen Hristev 		goto unregister_v4l2_device;
48755927c98SEugen Hristev 	}
48855927c98SEugen Hristev 
48955927c98SEugen Hristev 	if (list_empty(&isc->subdev_entities)) {
49055927c98SEugen Hristev 		dev_err(dev, "no subdev found\n");
49155927c98SEugen Hristev 		ret = -ENODEV;
49255927c98SEugen Hristev 		goto unregister_v4l2_device;
49355927c98SEugen Hristev 	}
49455927c98SEugen Hristev 
49555927c98SEugen Hristev 	list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
496adb2dcd5SSakari Ailus 		struct v4l2_async_connection *asd;
49755927c98SEugen Hristev 		struct fwnode_handle *fwnode =
49855927c98SEugen Hristev 			of_fwnode_handle(subdev_entity->epn);
49955927c98SEugen Hristev 
500*b8ec754aSSakari Ailus 		v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev);
50155927c98SEugen Hristev 
50255927c98SEugen Hristev 		asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
50355927c98SEugen Hristev 						      fwnode,
504adb2dcd5SSakari Ailus 						      struct v4l2_async_connection);
50555927c98SEugen Hristev 
50655927c98SEugen Hristev 		of_node_put(subdev_entity->epn);
50755927c98SEugen Hristev 		subdev_entity->epn = NULL;
50855927c98SEugen Hristev 
50955927c98SEugen Hristev 		if (IS_ERR(asd)) {
51055927c98SEugen Hristev 			ret = PTR_ERR(asd);
51155927c98SEugen Hristev 			goto cleanup_subdev;
51255927c98SEugen Hristev 		}
51355927c98SEugen Hristev 
51455927c98SEugen Hristev 		subdev_entity->notifier.ops = &atmel_isc_async_ops;
51555927c98SEugen Hristev 
516*b8ec754aSSakari Ailus 		ret = v4l2_async_nf_register(&subdev_entity->notifier);
51755927c98SEugen Hristev 		if (ret) {
51855927c98SEugen Hristev 			dev_err(dev, "fail to register async notifier\n");
51955927c98SEugen Hristev 			goto cleanup_subdev;
52055927c98SEugen Hristev 		}
52155927c98SEugen Hristev 
52255927c98SEugen Hristev 		if (video_is_registered(&isc->video_dev))
52355927c98SEugen Hristev 			break;
52455927c98SEugen Hristev 	}
52555927c98SEugen Hristev 
52655927c98SEugen Hristev 	pm_runtime_set_active(dev);
52755927c98SEugen Hristev 	pm_runtime_enable(dev);
52855927c98SEugen Hristev 	pm_request_idle(dev);
52955927c98SEugen Hristev 
53055927c98SEugen Hristev 	regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
53155927c98SEugen Hristev 	dev_info(dev, "Microchip XISC version %x\n", ver);
53255927c98SEugen Hristev 
53355927c98SEugen Hristev 	return 0;
53455927c98SEugen Hristev 
53555927c98SEugen Hristev cleanup_subdev:
53655927c98SEugen Hristev 	atmel_isc_subdev_cleanup(isc);
53755927c98SEugen Hristev 
53855927c98SEugen Hristev unregister_v4l2_device:
53955927c98SEugen Hristev 	v4l2_device_unregister(&isc->v4l2_dev);
54055927c98SEugen Hristev 
54155927c98SEugen Hristev unprepare_hclk:
54255927c98SEugen Hristev 	clk_disable_unprepare(isc->hclock);
54355927c98SEugen Hristev 
54455927c98SEugen Hristev 	atmel_isc_clk_cleanup(isc);
54555927c98SEugen Hristev 
54655927c98SEugen Hristev 	return ret;
54755927c98SEugen Hristev }
54855927c98SEugen Hristev 
microchip_xisc_remove(struct platform_device * pdev)5495b5365c7SUwe Kleine-König static void microchip_xisc_remove(struct platform_device *pdev)
55055927c98SEugen Hristev {
55155927c98SEugen Hristev 	struct isc_device *isc = platform_get_drvdata(pdev);
55255927c98SEugen Hristev 
55355927c98SEugen Hristev 	pm_runtime_disable(&pdev->dev);
55455927c98SEugen Hristev 
55555927c98SEugen Hristev 	atmel_isc_subdev_cleanup(isc);
55655927c98SEugen Hristev 
55755927c98SEugen Hristev 	v4l2_device_unregister(&isc->v4l2_dev);
55855927c98SEugen Hristev 
55955927c98SEugen Hristev 	clk_disable_unprepare(isc->hclock);
56055927c98SEugen Hristev 
56155927c98SEugen Hristev 	atmel_isc_clk_cleanup(isc);
56255927c98SEugen Hristev }
56355927c98SEugen Hristev 
xisc_runtime_suspend(struct device * dev)56455927c98SEugen Hristev static int __maybe_unused xisc_runtime_suspend(struct device *dev)
56555927c98SEugen Hristev {
56655927c98SEugen Hristev 	struct isc_device *isc = dev_get_drvdata(dev);
56755927c98SEugen Hristev 
56855927c98SEugen Hristev 	clk_disable_unprepare(isc->hclock);
56955927c98SEugen Hristev 
57055927c98SEugen Hristev 	return 0;
57155927c98SEugen Hristev }
57255927c98SEugen Hristev 
xisc_runtime_resume(struct device * dev)57355927c98SEugen Hristev static int __maybe_unused xisc_runtime_resume(struct device *dev)
57455927c98SEugen Hristev {
57555927c98SEugen Hristev 	struct isc_device *isc = dev_get_drvdata(dev);
57655927c98SEugen Hristev 	int ret;
57755927c98SEugen Hristev 
57855927c98SEugen Hristev 	ret = clk_prepare_enable(isc->hclock);
57955927c98SEugen Hristev 	if (ret)
58055927c98SEugen Hristev 		return ret;
58155927c98SEugen Hristev 
58255927c98SEugen Hristev 	return ret;
58355927c98SEugen Hristev }
58455927c98SEugen Hristev 
58555927c98SEugen Hristev static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
58655927c98SEugen Hristev 	SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
58755927c98SEugen Hristev };
58855927c98SEugen Hristev 
58955927c98SEugen Hristev #if IS_ENABLED(CONFIG_OF)
59055927c98SEugen Hristev static const struct of_device_id microchip_xisc_of_match[] = {
59155927c98SEugen Hristev 	{ .compatible = "microchip,sama7g5-isc" },
59255927c98SEugen Hristev 	{ }
59355927c98SEugen Hristev };
59455927c98SEugen Hristev MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
59555927c98SEugen Hristev #endif
59655927c98SEugen Hristev 
59755927c98SEugen Hristev static struct platform_driver microchip_xisc_driver = {
59855927c98SEugen Hristev 	.probe	= microchip_xisc_probe,
5995b5365c7SUwe Kleine-König 	.remove_new = microchip_xisc_remove,
60055927c98SEugen Hristev 	.driver	= {
60155927c98SEugen Hristev 		.name		= "microchip-sama7g5-xisc",
60255927c98SEugen Hristev 		.pm		= &microchip_xisc_dev_pm_ops,
60355927c98SEugen Hristev 		.of_match_table = of_match_ptr(microchip_xisc_of_match),
60455927c98SEugen Hristev 	},
60555927c98SEugen Hristev };
60655927c98SEugen Hristev 
60755927c98SEugen Hristev module_platform_driver(microchip_xisc_driver);
60855927c98SEugen Hristev 
60955927c98SEugen Hristev MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
61055927c98SEugen Hristev MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
61155927c98SEugen Hristev MODULE_LICENSE("GPL v2");
612