cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

atmel-sama7g5-isc.c (16808B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Microchip eXtended Image Sensor Controller (XISC) driver
      4 *
      5 * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries
      6 *
      7 * Author: Eugen Hristev <eugen.hristev@microchip.com>
      8 *
      9 * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS
     10 *
     11 * ISC video pipeline integrates the following submodules:
     12 * PFE: Parallel Front End to sample the camera sensor input stream
     13 * DPC: Defective Pixel Correction with black offset correction, green disparity
     14 *      correction and defective pixel correction (3 modules total)
     15 *  WB: Programmable white balance in the Bayer domain
     16 * CFA: Color filter array interpolation module
     17 *  CC: Programmable color correction
     18 * GAM: Gamma correction
     19 *VHXS: Vertical and Horizontal Scaler
     20 * CSC: Programmable color space conversion
     21 *CBHS: Contrast Brightness Hue and Saturation control
     22 * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
     23 * RLP: This module performs rounding, range limiting
     24 *      and packing of the incoming data
     25 * DMA: This module performs DMA master accesses to write frames to external RAM
     26 * HIS: Histogram module performs statistic counters on the frames
     27 */
     28
     29#include <linux/clk.h>
     30#include <linux/clkdev.h>
     31#include <linux/clk-provider.h>
     32#include <linux/delay.h>
     33#include <linux/interrupt.h>
     34#include <linux/math64.h>
     35#include <linux/module.h>
     36#include <linux/of.h>
     37#include <linux/of_graph.h>
     38#include <linux/platform_device.h>
     39#include <linux/pm_runtime.h>
     40#include <linux/regmap.h>
     41#include <linux/videodev2.h>
     42
     43#include <media/v4l2-ctrls.h>
     44#include <media/v4l2-device.h>
     45#include <media/v4l2-event.h>
     46#include <media/v4l2-image-sizes.h>
     47#include <media/v4l2-ioctl.h>
     48#include <media/v4l2-fwnode.h>
     49#include <media/v4l2-subdev.h>
     50#include <media/videobuf2-dma-contig.h>
     51
     52#include "atmel-isc-regs.h"
     53#include "atmel-isc.h"
     54
     55#define ISC_SAMA7G5_MAX_SUPPORT_WIDTH   3264
     56#define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT  2464
     57
     58#define ISC_SAMA7G5_PIPELINE \
     59	(WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
     60	CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
     61
     62/* This is a list of the formats that the ISC can *output* */
     63static const struct isc_format sama7g5_controller_formats[] = {
     64	{
     65		.fourcc		= V4L2_PIX_FMT_ARGB444,
     66	}, {
     67		.fourcc		= V4L2_PIX_FMT_ARGB555,
     68	}, {
     69		.fourcc		= V4L2_PIX_FMT_RGB565,
     70	}, {
     71		.fourcc		= V4L2_PIX_FMT_ABGR32,
     72	}, {
     73		.fourcc		= V4L2_PIX_FMT_XBGR32,
     74	}, {
     75		.fourcc		= V4L2_PIX_FMT_YUV420,
     76	}, {
     77		.fourcc		= V4L2_PIX_FMT_UYVY,
     78	}, {
     79		.fourcc		= V4L2_PIX_FMT_VYUY,
     80	}, {
     81		.fourcc		= V4L2_PIX_FMT_YUYV,
     82	}, {
     83		.fourcc		= V4L2_PIX_FMT_YUV422P,
     84	}, {
     85		.fourcc		= V4L2_PIX_FMT_GREY,
     86	}, {
     87		.fourcc		= V4L2_PIX_FMT_Y10,
     88	}, {
     89		.fourcc		= V4L2_PIX_FMT_Y16,
     90	}, {
     91		.fourcc		= V4L2_PIX_FMT_SBGGR8,
     92	}, {
     93		.fourcc		= V4L2_PIX_FMT_SGBRG8,
     94	}, {
     95		.fourcc		= V4L2_PIX_FMT_SGRBG8,
     96	}, {
     97		.fourcc		= V4L2_PIX_FMT_SRGGB8,
     98	}, {
     99		.fourcc		= V4L2_PIX_FMT_SBGGR10,
    100	}, {
    101		.fourcc		= V4L2_PIX_FMT_SGBRG10,
    102	}, {
    103		.fourcc		= V4L2_PIX_FMT_SGRBG10,
    104	}, {
    105		.fourcc		= V4L2_PIX_FMT_SRGGB10,
    106	},
    107};
    108
    109/* This is a list of formats that the ISC can receive as *input* */
    110static struct isc_format sama7g5_formats_list[] = {
    111	{
    112		.fourcc		= V4L2_PIX_FMT_SBGGR8,
    113		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,
    114		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    115		.cfa_baycfg	= ISC_BAY_CFG_BGBG,
    116	},
    117	{
    118		.fourcc		= V4L2_PIX_FMT_SGBRG8,
    119		.mbus_code	= MEDIA_BUS_FMT_SGBRG8_1X8,
    120		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    121		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
    122	},
    123	{
    124		.fourcc		= V4L2_PIX_FMT_SGRBG8,
    125		.mbus_code	= MEDIA_BUS_FMT_SGRBG8_1X8,
    126		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    127		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
    128	},
    129	{
    130		.fourcc		= V4L2_PIX_FMT_SRGGB8,
    131		.mbus_code	= MEDIA_BUS_FMT_SRGGB8_1X8,
    132		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    133		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
    134	},
    135	{
    136		.fourcc		= V4L2_PIX_FMT_SBGGR10,
    137		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,
    138		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
    139		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
    140	},
    141	{
    142		.fourcc		= V4L2_PIX_FMT_SGBRG10,
    143		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,
    144		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
    145		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
    146	},
    147	{
    148		.fourcc		= V4L2_PIX_FMT_SGRBG10,
    149		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,
    150		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
    151		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
    152	},
    153	{
    154		.fourcc		= V4L2_PIX_FMT_SRGGB10,
    155		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,
    156		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
    157		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
    158	},
    159	{
    160		.fourcc		= V4L2_PIX_FMT_SBGGR12,
    161		.mbus_code	= MEDIA_BUS_FMT_SBGGR12_1X12,
    162		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
    163		.cfa_baycfg	= ISC_BAY_CFG_BGBG,
    164	},
    165	{
    166		.fourcc		= V4L2_PIX_FMT_SGBRG12,
    167		.mbus_code	= MEDIA_BUS_FMT_SGBRG12_1X12,
    168		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
    169		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
    170	},
    171	{
    172		.fourcc		= V4L2_PIX_FMT_SGRBG12,
    173		.mbus_code	= MEDIA_BUS_FMT_SGRBG12_1X12,
    174		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
    175		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
    176	},
    177	{
    178		.fourcc		= V4L2_PIX_FMT_SRGGB12,
    179		.mbus_code	= MEDIA_BUS_FMT_SRGGB12_1X12,
    180		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
    181		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
    182	},
    183	{
    184		.fourcc		= V4L2_PIX_FMT_GREY,
    185		.mbus_code	= MEDIA_BUS_FMT_Y8_1X8,
    186		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    187	},
    188	{
    189		.fourcc		= V4L2_PIX_FMT_YUYV,
    190		.mbus_code	= MEDIA_BUS_FMT_YUYV8_2X8,
    191		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    192	},
    193	{
    194		.fourcc		= V4L2_PIX_FMT_UYVY,
    195		.mbus_code	= MEDIA_BUS_FMT_UYVY8_2X8,
    196		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    197	},
    198	{
    199		.fourcc		= V4L2_PIX_FMT_RGB565,
    200		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_LE,
    201		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    202	},
    203	{
    204		.fourcc		= V4L2_PIX_FMT_Y10,
    205		.mbus_code	= MEDIA_BUS_FMT_Y10_1X10,
    206		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
    207	},
    208};
    209
    210static void isc_sama7g5_config_csc(struct isc_device *isc)
    211{
    212	struct regmap *regmap = isc->regmap;
    213
    214	/* Convert RGB to YUV */
    215	regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
    216		     0x42 | (0x81 << 16));
    217	regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
    218		     0x19 | (0x10 << 16));
    219	regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
    220		     0xFDA | (0xFB6 << 16));
    221	regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
    222		     0x70 | (0x80 << 16));
    223	regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
    224		     0x70 | (0xFA2 << 16));
    225	regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
    226		     0xFEE | (0x80 << 16));
    227}
    228
    229static void isc_sama7g5_config_cbc(struct isc_device *isc)
    230{
    231	struct regmap *regmap = isc->regmap;
    232
    233	/* Configure what is set via v4l2 ctrls */
    234	regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness);
    235	regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast);
    236	/* Configure Hue and Saturation as neutral midpoint */
    237	regmap_write(regmap, ISC_CBCHS_HUE, 0);
    238	regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4));
    239}
    240
    241static void isc_sama7g5_config_cc(struct isc_device *isc)
    242{
    243	struct regmap *regmap = isc->regmap;
    244
    245	/* Configure each register at the neutral fixed point 1.0 or 0.0 */
    246	regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
    247	regmap_write(regmap, ISC_CC_RB_OR, 0);
    248	regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
    249	regmap_write(regmap, ISC_CC_GB_OG, 0);
    250	regmap_write(regmap, ISC_CC_BR_BG, 0);
    251	regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
    252}
    253
    254static void isc_sama7g5_config_ctrls(struct isc_device *isc,
    255				     const struct v4l2_ctrl_ops *ops)
    256{
    257	struct isc_ctrls *ctrls = &isc->ctrls;
    258	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
    259
    260	ctrls->contrast = 16;
    261
    262	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16);
    263}
    264
    265static void isc_sama7g5_config_dpc(struct isc_device *isc)
    266{
    267	u32 bay_cfg = isc->config.sd_format->cfa_baycfg;
    268	struct regmap *regmap = isc->regmap;
    269
    270	regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK,
    271			   (64 << ISC_DPC_CFG_BLOFF_SHIFT));
    272	regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK,
    273			   (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT));
    274}
    275
    276static void isc_sama7g5_config_gam(struct isc_device *isc)
    277{
    278	struct regmap *regmap = isc->regmap;
    279
    280	regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART,
    281			   ISC_GAM_CTRL_BIPART);
    282}
    283
    284static void isc_sama7g5_config_rlp(struct isc_device *isc)
    285{
    286	struct regmap *regmap = isc->regmap;
    287	u32 rlp_mode = isc->config.rlp_cfg_mode;
    288
    289	regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
    290			   ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH |
    291			   ISC_RLP_CFG_YMODE_MASK, rlp_mode);
    292}
    293
    294static void isc_sama7g5_adapt_pipeline(struct isc_device *isc)
    295{
    296	isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE;
    297}
    298
    299/* Gamma table with gamma 1/2.2 */
    300static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
    301	/* index 0 --> gamma bipartite */
    302	{
    303	      0x980,  0x4c0320,  0x650260,  0x7801e0,  0x8701a0,  0x940180,
    304	   0xa00160,  0xab0120,  0xb40120,  0xbd0120,  0xc60100,  0xce0100,
    305	   0xd600e0,  0xdd00e0,  0xe400e0,  0xeb00c0,  0xf100c0,  0xf700c0,
    306	   0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0,
    307	  0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080,
    308	  0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a,
    309	  0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030,
    310	  0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026,
    311	  0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020,
    312	  0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c,
    313	  0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a },
    314};
    315
    316static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
    317{
    318	struct device_node *np = dev->of_node;
    319	struct device_node *epn = NULL;
    320	struct isc_subdev_entity *subdev_entity;
    321	unsigned int flags;
    322	int ret;
    323	bool mipi_mode;
    324
    325	INIT_LIST_HEAD(&isc->subdev_entities);
    326
    327	mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
    328
    329	while (1) {
    330		struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
    331
    332		epn = of_graph_get_next_endpoint(np, epn);
    333		if (!epn)
    334			return 0;
    335
    336		ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
    337						 &v4l2_epn);
    338		if (ret) {
    339			ret = -EINVAL;
    340			dev_err(dev, "Could not parse the endpoint\n");
    341			break;
    342		}
    343
    344		subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
    345					     GFP_KERNEL);
    346		if (!subdev_entity) {
    347			ret = -ENOMEM;
    348			break;
    349		}
    350		subdev_entity->epn = epn;
    351
    352		flags = v4l2_epn.bus.parallel.flags;
    353
    354		if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
    355			subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
    356
    357		if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
    358			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
    359
    360		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
    361			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
    362
    363		if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
    364			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
    365					ISC_PFE_CFG0_CCIR656;
    366
    367		if (mipi_mode)
    368			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
    369
    370		list_add_tail(&subdev_entity->list, &isc->subdev_entities);
    371	}
    372	of_node_put(epn);
    373
    374	return ret;
    375}
    376
    377static int microchip_xisc_probe(struct platform_device *pdev)
    378{
    379	struct device *dev = &pdev->dev;
    380	struct isc_device *isc;
    381	struct resource *res;
    382	void __iomem *io_base;
    383	struct isc_subdev_entity *subdev_entity;
    384	int irq;
    385	int ret;
    386	u32 ver;
    387
    388	isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
    389	if (!isc)
    390		return -ENOMEM;
    391
    392	platform_set_drvdata(pdev, isc);
    393	isc->dev = dev;
    394
    395	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    396	io_base = devm_ioremap_resource(dev, res);
    397	if (IS_ERR(io_base))
    398		return PTR_ERR(io_base);
    399
    400	isc->regmap = devm_regmap_init_mmio(dev, io_base, &isc_regmap_config);
    401	if (IS_ERR(isc->regmap)) {
    402		ret = PTR_ERR(isc->regmap);
    403		dev_err(dev, "failed to init register map: %d\n", ret);
    404		return ret;
    405	}
    406
    407	irq = platform_get_irq(pdev, 0);
    408	if (irq < 0)
    409		return irq;
    410
    411	ret = devm_request_irq(dev, irq, isc_interrupt, 0,
    412			       "microchip-sama7g5-xisc", isc);
    413	if (ret < 0) {
    414		dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
    415			irq, ret);
    416		return ret;
    417	}
    418
    419	isc->gamma_table = isc_sama7g5_gamma_table;
    420	isc->gamma_max = 0;
    421
    422	isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
    423	isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
    424
    425	isc->config_dpc = isc_sama7g5_config_dpc;
    426	isc->config_csc = isc_sama7g5_config_csc;
    427	isc->config_cbc = isc_sama7g5_config_cbc;
    428	isc->config_cc = isc_sama7g5_config_cc;
    429	isc->config_gam = isc_sama7g5_config_gam;
    430	isc->config_rlp = isc_sama7g5_config_rlp;
    431	isc->config_ctrls = isc_sama7g5_config_ctrls;
    432
    433	isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
    434
    435	isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
    436	isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
    437	isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
    438	isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
    439	isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
    440	isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
    441	isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
    442	isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
    443	isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
    444
    445	isc->controller_formats = sama7g5_controller_formats;
    446	isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
    447	isc->formats_list = sama7g5_formats_list;
    448	isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
    449
    450	/* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
    451	isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
    452
    453	/* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
    454	isc->ispck_required = false;
    455
    456	ret = isc_pipeline_init(isc);
    457	if (ret)
    458		return ret;
    459
    460	isc->hclock = devm_clk_get(dev, "hclock");
    461	if (IS_ERR(isc->hclock)) {
    462		ret = PTR_ERR(isc->hclock);
    463		dev_err(dev, "failed to get hclock: %d\n", ret);
    464		return ret;
    465	}
    466
    467	ret = clk_prepare_enable(isc->hclock);
    468	if (ret) {
    469		dev_err(dev, "failed to enable hclock: %d\n", ret);
    470		return ret;
    471	}
    472
    473	ret = isc_clk_init(isc);
    474	if (ret) {
    475		dev_err(dev, "failed to init isc clock: %d\n", ret);
    476		goto unprepare_hclk;
    477	}
    478
    479	ret = v4l2_device_register(dev, &isc->v4l2_dev);
    480	if (ret) {
    481		dev_err(dev, "unable to register v4l2 device.\n");
    482		goto unprepare_hclk;
    483	}
    484
    485	ret = xisc_parse_dt(dev, isc);
    486	if (ret) {
    487		dev_err(dev, "fail to parse device tree\n");
    488		goto unregister_v4l2_device;
    489	}
    490
    491	if (list_empty(&isc->subdev_entities)) {
    492		dev_err(dev, "no subdev found\n");
    493		ret = -ENODEV;
    494		goto unregister_v4l2_device;
    495	}
    496
    497	list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
    498		struct v4l2_async_subdev *asd;
    499		struct fwnode_handle *fwnode =
    500			of_fwnode_handle(subdev_entity->epn);
    501
    502		v4l2_async_nf_init(&subdev_entity->notifier);
    503
    504		asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
    505						      fwnode,
    506						      struct v4l2_async_subdev);
    507
    508		of_node_put(subdev_entity->epn);
    509		subdev_entity->epn = NULL;
    510
    511		if (IS_ERR(asd)) {
    512			ret = PTR_ERR(asd);
    513			goto cleanup_subdev;
    514		}
    515
    516		subdev_entity->notifier.ops = &isc_async_ops;
    517
    518		ret = v4l2_async_nf_register(&isc->v4l2_dev,
    519					     &subdev_entity->notifier);
    520		if (ret) {
    521			dev_err(dev, "fail to register async notifier\n");
    522			goto cleanup_subdev;
    523		}
    524
    525		if (video_is_registered(&isc->video_dev))
    526			break;
    527	}
    528
    529	pm_runtime_set_active(dev);
    530	pm_runtime_enable(dev);
    531	pm_request_idle(dev);
    532
    533	regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
    534	dev_info(dev, "Microchip XISC version %x\n", ver);
    535
    536	return 0;
    537
    538cleanup_subdev:
    539	isc_subdev_cleanup(isc);
    540
    541unregister_v4l2_device:
    542	v4l2_device_unregister(&isc->v4l2_dev);
    543
    544unprepare_hclk:
    545	clk_disable_unprepare(isc->hclock);
    546
    547	isc_clk_cleanup(isc);
    548
    549	return ret;
    550}
    551
    552static int microchip_xisc_remove(struct platform_device *pdev)
    553{
    554	struct isc_device *isc = platform_get_drvdata(pdev);
    555
    556	pm_runtime_disable(&pdev->dev);
    557
    558	isc_subdev_cleanup(isc);
    559
    560	v4l2_device_unregister(&isc->v4l2_dev);
    561
    562	clk_disable_unprepare(isc->hclock);
    563
    564	isc_clk_cleanup(isc);
    565
    566	return 0;
    567}
    568
    569static int __maybe_unused xisc_runtime_suspend(struct device *dev)
    570{
    571	struct isc_device *isc = dev_get_drvdata(dev);
    572
    573	clk_disable_unprepare(isc->hclock);
    574
    575	return 0;
    576}
    577
    578static int __maybe_unused xisc_runtime_resume(struct device *dev)
    579{
    580	struct isc_device *isc = dev_get_drvdata(dev);
    581	int ret;
    582
    583	ret = clk_prepare_enable(isc->hclock);
    584	if (ret)
    585		return ret;
    586
    587	return ret;
    588}
    589
    590static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
    591	SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
    592};
    593
    594static const struct of_device_id microchip_xisc_of_match[] = {
    595	{ .compatible = "microchip,sama7g5-isc" },
    596	{ }
    597};
    598MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
    599
    600static struct platform_driver microchip_xisc_driver = {
    601	.probe	= microchip_xisc_probe,
    602	.remove	= microchip_xisc_remove,
    603	.driver	= {
    604		.name		= "microchip-sama7g5-xisc",
    605		.pm		= &microchip_xisc_dev_pm_ops,
    606		.of_match_table = of_match_ptr(microchip_xisc_of_match),
    607	},
    608};
    609
    610module_platform_driver(microchip_xisc_driver);
    611
    612MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
    613MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
    614MODULE_LICENSE("GPL v2");