brintos

brintos / linux-shallow public Read only

0
0
Text · 18.3 KiB · 60b6d92 Raw
674 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Microchip Image Sensor Controller (ISC) driver4 *5 * Copyright (C) 2016-2019 Microchip Technology, Inc.6 *7 * Author: Songjun Wu8 * Author: Eugen Hristev <eugen.hristev@microchip.com>9 *10 *11 * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA12 *13 * ISC video pipeline integrates the following submodules:14 * PFE: Parallel Front End to sample the camera sensor input stream15 *  WB: Programmable white balance in the Bayer domain16 * CFA: Color filter array interpolation module17 *  CC: Programmable color correction18 * GAM: Gamma correction19 * CSC: Programmable color space conversion20 * CBC: Contrast and Brightness control21 * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling22 * RLP: This module performs rounding, range limiting23 *      and packing of the incoming data24 */25 26#include <linux/clk.h>27#include <linux/clkdev.h>28#include <linux/clk-provider.h>29#include <linux/delay.h>30#include <linux/interrupt.h>31#include <linux/math64.h>32#include <linux/module.h>33#include <linux/of.h>34#include <linux/of_graph.h>35#include <linux/platform_device.h>36#include <linux/pm_runtime.h>37#include <linux/regmap.h>38#include <linux/videodev2.h>39 40#include <media/v4l2-ctrls.h>41#include <media/v4l2-device.h>42#include <media/v4l2-event.h>43#include <media/v4l2-image-sizes.h>44#include <media/v4l2-ioctl.h>45#include <media/v4l2-fwnode.h>46#include <media/v4l2-subdev.h>47#include <media/videobuf2-dma-contig.h>48 49#include "microchip-isc-regs.h"50#include "microchip-isc.h"51 52#define ISC_SAMA5D2_MAX_SUPPORT_WIDTH   259253#define ISC_SAMA5D2_MAX_SUPPORT_HEIGHT  194454 55#define ISC_SAMA5D2_PIPELINE \56	(WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \57	CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)58 59/* This is a list of the formats that the ISC can *output* */60static const struct isc_format sama5d2_controller_formats[] = {61	{62		.fourcc		= V4L2_PIX_FMT_ARGB444,63	}, {64		.fourcc		= V4L2_PIX_FMT_ARGB555,65	}, {66		.fourcc		= V4L2_PIX_FMT_RGB565,67	}, {68		.fourcc		= V4L2_PIX_FMT_ABGR32,69	}, {70		.fourcc		= V4L2_PIX_FMT_XBGR32,71	}, {72		.fourcc		= V4L2_PIX_FMT_YUV420,73	}, {74		.fourcc		= V4L2_PIX_FMT_YUYV,75	}, {76		.fourcc		= V4L2_PIX_FMT_YUV422P,77	}, {78		.fourcc		= V4L2_PIX_FMT_GREY,79	}, {80		.fourcc		= V4L2_PIX_FMT_Y10,81	}, {82		.fourcc		= V4L2_PIX_FMT_SBGGR8,83		.raw		= true,84	}, {85		.fourcc		= V4L2_PIX_FMT_SGBRG8,86		.raw		= true,87	}, {88		.fourcc		= V4L2_PIX_FMT_SGRBG8,89		.raw		= true,90	}, {91		.fourcc		= V4L2_PIX_FMT_SRGGB8,92		.raw		= true,93	}, {94		.fourcc		= V4L2_PIX_FMT_SBGGR10,95		.raw		= true,96	}, {97		.fourcc		= V4L2_PIX_FMT_SGBRG10,98		.raw		= true,99	}, {100		.fourcc		= V4L2_PIX_FMT_SGRBG10,101		.raw		= true,102	}, {103		.fourcc		= V4L2_PIX_FMT_SRGGB10,104		.raw		= true,105	}, {106		.fourcc		= V4L2_PIX_FMT_SBGGR12,107		.raw		= true,108	}, {109		.fourcc		= V4L2_PIX_FMT_SGBRG12,110		.raw		= true,111	}, {112		.fourcc		= V4L2_PIX_FMT_SGRBG12,113		.raw		= true,114	}, {115		.fourcc		= V4L2_PIX_FMT_SRGGB12,116		.raw		= true,117	},118};119 120/* This is a list of formats that the ISC can receive as *input* */121static struct isc_format sama5d2_formats_list[] = {122	{123		.fourcc		= V4L2_PIX_FMT_SBGGR8,124		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,125		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,126		.cfa_baycfg	= ISC_BAY_CFG_BGBG,127	},128	{129		.fourcc		= V4L2_PIX_FMT_SGBRG8,130		.mbus_code	= MEDIA_BUS_FMT_SGBRG8_1X8,131		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,132		.cfa_baycfg	= ISC_BAY_CFG_GBGB,133	},134	{135		.fourcc		= V4L2_PIX_FMT_SGRBG8,136		.mbus_code	= MEDIA_BUS_FMT_SGRBG8_1X8,137		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,138		.cfa_baycfg	= ISC_BAY_CFG_GRGR,139	},140	{141		.fourcc		= V4L2_PIX_FMT_SRGGB8,142		.mbus_code	= MEDIA_BUS_FMT_SRGGB8_1X8,143		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,144		.cfa_baycfg	= ISC_BAY_CFG_RGRG,145	},146	{147		.fourcc		= V4L2_PIX_FMT_SBGGR10,148		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,149		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,150		.cfa_baycfg	= ISC_BAY_CFG_RGRG,151	},152	{153		.fourcc		= V4L2_PIX_FMT_SGBRG10,154		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,155		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,156		.cfa_baycfg	= ISC_BAY_CFG_GBGB,157	},158	{159		.fourcc		= V4L2_PIX_FMT_SGRBG10,160		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,161		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,162		.cfa_baycfg	= ISC_BAY_CFG_GRGR,163	},164	{165		.fourcc		= V4L2_PIX_FMT_SRGGB10,166		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,167		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,168		.cfa_baycfg	= ISC_BAY_CFG_RGRG,169	},170	{171		.fourcc		= V4L2_PIX_FMT_SBGGR12,172		.mbus_code	= MEDIA_BUS_FMT_SBGGR12_1X12,173		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,174		.cfa_baycfg	= ISC_BAY_CFG_BGBG,175	},176	{177		.fourcc		= V4L2_PIX_FMT_SGBRG12,178		.mbus_code	= MEDIA_BUS_FMT_SGBRG12_1X12,179		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,180		.cfa_baycfg	= ISC_BAY_CFG_GBGB,181	},182	{183		.fourcc		= V4L2_PIX_FMT_SGRBG12,184		.mbus_code	= MEDIA_BUS_FMT_SGRBG12_1X12,185		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,186		.cfa_baycfg	= ISC_BAY_CFG_GRGR,187	},188	{189		.fourcc		= V4L2_PIX_FMT_SRGGB12,190		.mbus_code	= MEDIA_BUS_FMT_SRGGB12_1X12,191		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,192		.cfa_baycfg	= ISC_BAY_CFG_RGRG,193	},194	{195		.fourcc		= V4L2_PIX_FMT_GREY,196		.mbus_code	= MEDIA_BUS_FMT_Y8_1X8,197		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,198	},199	{200		.fourcc		= V4L2_PIX_FMT_YUYV,201		.mbus_code	= MEDIA_BUS_FMT_YUYV8_2X8,202		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,203	},204	{205		.fourcc		= V4L2_PIX_FMT_RGB565,206		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_LE,207		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,208	},209	{210		.fourcc		= V4L2_PIX_FMT_Y10,211		.mbus_code	= MEDIA_BUS_FMT_Y10_1X10,212		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,213	},214 215};216 217static void isc_sama5d2_config_csc(struct isc_device *isc)218{219	struct regmap *regmap = isc->regmap;220 221	/* Convert RGB to YUV */222	regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,223		     0x42 | (0x81 << 16));224	regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,225		     0x19 | (0x10 << 16));226	regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,227		     0xFDA | (0xFB6 << 16));228	regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,229		     0x70 | (0x80 << 16));230	regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,231		     0x70 | (0xFA2 << 16));232	regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,233		     0xFEE | (0x80 << 16));234}235 236static void isc_sama5d2_config_cbc(struct isc_device *isc)237{238	struct regmap *regmap = isc->regmap;239 240	regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc,241		     isc->ctrls.brightness);242	regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc,243		     isc->ctrls.contrast);244}245 246static void isc_sama5d2_config_cc(struct isc_device *isc)247{248	struct regmap *regmap = isc->regmap;249 250	/* Configure each register at the neutral fixed point 1.0 or 0.0 */251	regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));252	regmap_write(regmap, ISC_CC_RB_OR, 0);253	regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);254	regmap_write(regmap, ISC_CC_GB_OG, 0);255	regmap_write(regmap, ISC_CC_BR_BG, 0);256	regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));257}258 259static void isc_sama5d2_config_ctrls(struct isc_device *isc,260				     const struct v4l2_ctrl_ops *ops)261{262	struct isc_ctrls *ctrls = &isc->ctrls;263	struct v4l2_ctrl_handler *hdl = &ctrls->handler;264 265	ctrls->contrast = 256;266 267	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256);268}269 270static void isc_sama5d2_config_dpc(struct isc_device *isc)271{272	/* This module is not present on sama5d2 pipeline */273}274 275static void isc_sama5d2_config_gam(struct isc_device *isc)276{277	/* No specific gamma configuration */278}279 280static void isc_sama5d2_config_rlp(struct isc_device *isc)281{282	struct regmap *regmap = isc->regmap;283	u32 rlp_mode = isc->config.rlp_cfg_mode;284 285	/*286	 * In sama5d2, the YUV planar modes and the YUYV modes are treated287	 * in the same way in RLP register.288	 * Normally, YYCC mode should be Luma(n) - Color B(n) - Color R (n)289	 * and YCYC should be Luma(n + 1) - Color B (n) - Luma (n) - Color R (n)290	 * but in sama5d2, the YCYC mode does not exist, and YYCC must be291	 * selected for both planar and interleaved modes, as in fact292	 * both modes are supported.293	 *294	 * Thus, if the YCYC mode is selected, replace it with the295	 * sama5d2-compliant mode which is YYCC .296	 */297	if ((rlp_mode & ISC_RLP_CFG_MODE_MASK) == ISC_RLP_CFG_MODE_YCYC) {298		rlp_mode &= ~ISC_RLP_CFG_MODE_MASK;299		rlp_mode |= ISC_RLP_CFG_MODE_YYCC;300	}301 302	regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,303			   ISC_RLP_CFG_MODE_MASK, rlp_mode);304}305 306static void isc_sama5d2_adapt_pipeline(struct isc_device *isc)307{308	isc->try_config.bits_pipeline &= ISC_SAMA5D2_PIPELINE;309}310 311/* Gamma table with gamma 1/2.2 */312static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = {313	/* 0 --> gamma 1/1.8 */314	{      0x65,  0x66002F,  0x950025,  0xBB0020,  0xDB001D,  0xF8001A,315	  0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012,316	  0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F,317	  0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E,318	  0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C,319	  0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B,320	  0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A,321	  0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A,322	  0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A,323	  0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009,324	  0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 },325 326	/* 1 --> gamma 1/2 */327	{      0x7F,  0x800034,  0xB50028,  0xDE0021, 0x100001E, 0x11E001B,328	  0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013,329	  0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F,330	  0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D,331	  0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B,332	  0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A,333	  0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A,334	  0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009,335	  0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009,336	  0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009,337	  0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 },338 339	/* 2 --> gamma 1/2.2 */340	{      0x99,  0x9B0038,  0xD4002A,  0xFF0023, 0x122001F, 0x141001B,341	  0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012,342	  0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F,343	  0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C,344	  0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B,345	  0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A,346	  0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009,347	  0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009,348	  0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008,349	  0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007,350	  0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 },351};352 353static int isc_parse_dt(struct device *dev, struct isc_device *isc)354{355	struct device_node *np = dev->of_node;356	struct device_node *epn;357	struct isc_subdev_entity *subdev_entity;358	unsigned int flags;359 360	INIT_LIST_HEAD(&isc->subdev_entities);361 362	for_each_endpoint_of_node(np, epn) {363		struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };364		int ret;365 366		ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),367						 &v4l2_epn);368		if (ret) {369			of_node_put(epn);370			dev_err(dev, "Could not parse the endpoint\n");371			return -EINVAL;372		}373 374		subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),375					     GFP_KERNEL);376		if (!subdev_entity) {377			of_node_put(epn);378			return -ENOMEM;379		}380		subdev_entity->epn = epn;381 382		flags = v4l2_epn.bus.parallel.flags;383 384		if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)385			subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;386 387		if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)388			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;389 390		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)391			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;392 393		if (v4l2_epn.bus_type == V4L2_MBUS_BT656)394			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |395					ISC_PFE_CFG0_CCIR656;396 397		list_add_tail(&subdev_entity->list, &isc->subdev_entities);398	}399 400	return 0;401}402 403static int microchip_isc_probe(struct platform_device *pdev)404{405	struct device *dev = &pdev->dev;406	struct isc_device *isc;407	void __iomem *io_base;408	struct isc_subdev_entity *subdev_entity;409	int irq;410	int ret;411	u32 ver;412 413	isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);414	if (!isc)415		return -ENOMEM;416 417	platform_set_drvdata(pdev, isc);418	isc->dev = dev;419 420	io_base = devm_platform_ioremap_resource(pdev, 0);421	if (IS_ERR(io_base))422		return PTR_ERR(io_base);423 424	isc->regmap = devm_regmap_init_mmio(dev, io_base, &microchip_isc_regmap_config);425	if (IS_ERR(isc->regmap)) {426		ret = PTR_ERR(isc->regmap);427		dev_err(dev, "failed to init register map: %d\n", ret);428		return ret;429	}430 431	irq = platform_get_irq(pdev, 0);432	if (irq < 0)433		return irq;434 435	ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,436			       "microchip-sama5d2-isc", isc);437	if (ret < 0) {438		dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",439			irq, ret);440		return ret;441	}442 443	isc->gamma_table = isc_sama5d2_gamma_table;444	isc->gamma_max = 2;445 446	isc->max_width = ISC_SAMA5D2_MAX_SUPPORT_WIDTH;447	isc->max_height = ISC_SAMA5D2_MAX_SUPPORT_HEIGHT;448 449	isc->config_dpc = isc_sama5d2_config_dpc;450	isc->config_csc = isc_sama5d2_config_csc;451	isc->config_cbc = isc_sama5d2_config_cbc;452	isc->config_cc = isc_sama5d2_config_cc;453	isc->config_gam = isc_sama5d2_config_gam;454	isc->config_rlp = isc_sama5d2_config_rlp;455	isc->config_ctrls = isc_sama5d2_config_ctrls;456 457	isc->adapt_pipeline = isc_sama5d2_adapt_pipeline;458 459	isc->offsets.csc = ISC_SAMA5D2_CSC_OFFSET;460	isc->offsets.cbc = ISC_SAMA5D2_CBC_OFFSET;461	isc->offsets.sub422 = ISC_SAMA5D2_SUB422_OFFSET;462	isc->offsets.sub420 = ISC_SAMA5D2_SUB420_OFFSET;463	isc->offsets.rlp = ISC_SAMA5D2_RLP_OFFSET;464	isc->offsets.his = ISC_SAMA5D2_HIS_OFFSET;465	isc->offsets.dma = ISC_SAMA5D2_DMA_OFFSET;466	isc->offsets.version = ISC_SAMA5D2_VERSION_OFFSET;467	isc->offsets.his_entry = ISC_SAMA5D2_HIS_ENTRY_OFFSET;468 469	isc->controller_formats = sama5d2_controller_formats;470	isc->controller_formats_size = ARRAY_SIZE(sama5d2_controller_formats);471	isc->formats_list = sama5d2_formats_list;472	isc->formats_list_size = ARRAY_SIZE(sama5d2_formats_list);473 474	/* sama5d2-isc - 8 bits per beat */475	isc->dcfg = ISC_DCFG_YMBSIZE_BEATS8 | ISC_DCFG_CMBSIZE_BEATS8;476 477	/* sama5d2-isc : ISPCK is required and mandatory */478	isc->ispck_required = true;479 480	ret = microchip_isc_pipeline_init(isc);481	if (ret)482		return ret;483 484	isc->hclock = devm_clk_get(dev, "hclock");485	if (IS_ERR(isc->hclock)) {486		ret = PTR_ERR(isc->hclock);487		dev_err(dev, "failed to get hclock: %d\n", ret);488		return ret;489	}490 491	ret = clk_prepare_enable(isc->hclock);492	if (ret) {493		dev_err(dev, "failed to enable hclock: %d\n", ret);494		return ret;495	}496 497	ret = microchip_isc_clk_init(isc);498	if (ret) {499		dev_err(dev, "failed to init isc clock: %d\n", ret);500		goto unprepare_hclk;501	}502	ret = v4l2_device_register(dev, &isc->v4l2_dev);503	if (ret) {504		dev_err(dev, "unable to register v4l2 device.\n");505		goto unprepare_clk;506	}507 508	ret = isc_parse_dt(dev, isc);509	if (ret) {510		dev_err(dev, "fail to parse device tree\n");511		goto unregister_v4l2_device;512	}513 514	if (list_empty(&isc->subdev_entities)) {515		dev_err(dev, "no subdev found\n");516		ret = -ENODEV;517		goto unregister_v4l2_device;518	}519 520	list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {521		struct v4l2_async_connection *asd;522		struct fwnode_handle *fwnode =523			of_fwnode_handle(subdev_entity->epn);524 525		v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev);526 527		asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,528						      fwnode,529						      struct v4l2_async_connection);530 531		of_node_put(subdev_entity->epn);532		subdev_entity->epn = NULL;533 534		if (IS_ERR(asd)) {535			ret = PTR_ERR(asd);536			goto cleanup_subdev;537		}538 539		subdev_entity->notifier.ops = &microchip_isc_async_ops;540 541		ret = v4l2_async_nf_register(&subdev_entity->notifier);542		if (ret) {543			dev_err(dev, "fail to register async notifier\n");544			goto cleanup_subdev;545		}546 547		if (video_is_registered(&isc->video_dev))548			break;549	}550 551	regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);552 553	ret = isc_mc_init(isc, ver);554	if (ret < 0)555		goto isc_probe_mc_init_err;556 557	pm_runtime_set_active(dev);558	pm_runtime_enable(dev);559	pm_request_idle(dev);560 561	isc->ispck = isc->isc_clks[ISC_ISPCK].clk;562 563	ret = clk_prepare_enable(isc->ispck);564	if (ret) {565		dev_err(dev, "failed to enable ispck: %d\n", ret);566		goto disable_pm;567	}568 569	/* ispck should be greater or equal to hclock */570	ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock));571	if (ret) {572		dev_err(dev, "failed to set ispck rate: %d\n", ret);573		goto unprepare_clk;574	}575 576	dev_info(dev, "Microchip ISC version %x\n", ver);577 578	return 0;579 580unprepare_clk:581	clk_disable_unprepare(isc->ispck);582 583disable_pm:584	pm_runtime_disable(dev);585 586isc_probe_mc_init_err:587	isc_mc_cleanup(isc);588 589cleanup_subdev:590	microchip_isc_subdev_cleanup(isc);591 592unregister_v4l2_device:593	v4l2_device_unregister(&isc->v4l2_dev);594 595unprepare_hclk:596	clk_disable_unprepare(isc->hclock);597 598	microchip_isc_clk_cleanup(isc);599 600	return ret;601}602 603static void microchip_isc_remove(struct platform_device *pdev)604{605	struct isc_device *isc = platform_get_drvdata(pdev);606 607	pm_runtime_disable(&pdev->dev);608 609	isc_mc_cleanup(isc);610 611	microchip_isc_subdev_cleanup(isc);612 613	v4l2_device_unregister(&isc->v4l2_dev);614 615	clk_disable_unprepare(isc->ispck);616	clk_disable_unprepare(isc->hclock);617 618	microchip_isc_clk_cleanup(isc);619}620 621static int __maybe_unused isc_runtime_suspend(struct device *dev)622{623	struct isc_device *isc = dev_get_drvdata(dev);624 625	clk_disable_unprepare(isc->ispck);626	clk_disable_unprepare(isc->hclock);627 628	return 0;629}630 631static int __maybe_unused isc_runtime_resume(struct device *dev)632{633	struct isc_device *isc = dev_get_drvdata(dev);634	int ret;635 636	ret = clk_prepare_enable(isc->hclock);637	if (ret)638		return ret;639 640	ret = clk_prepare_enable(isc->ispck);641	if (ret)642		clk_disable_unprepare(isc->hclock);643 644	return ret;645}646 647static const struct dev_pm_ops microchip_isc_dev_pm_ops = {648	SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL)649};650 651#if IS_ENABLED(CONFIG_OF)652static const struct of_device_id microchip_isc_of_match[] = {653	{ .compatible = "atmel,sama5d2-isc" },654	{ }655};656MODULE_DEVICE_TABLE(of, microchip_isc_of_match);657#endif658 659static struct platform_driver microchip_isc_driver = {660	.probe	= microchip_isc_probe,661	.remove_new = microchip_isc_remove,662	.driver	= {663		.name		= "microchip-sama5d2-isc",664		.pm		= &microchip_isc_dev_pm_ops,665		.of_match_table = of_match_ptr(microchip_isc_of_match),666	},667};668 669module_platform_driver(microchip_isc_driver);670 671MODULE_AUTHOR("Songjun Wu");672MODULE_DESCRIPTION("The V4L2 driver for Microchip-ISC");673MODULE_LICENSE("GPL v2");674