Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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 "microchip-isc-regs.h"
 53#include "microchip-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		.raw		= true,
 93	}, {
 94		.fourcc		= V4L2_PIX_FMT_SGBRG8,
 95		.raw		= true,
 96	}, {
 97		.fourcc		= V4L2_PIX_FMT_SGRBG8,
 98		.raw		= true,
 99	}, {
100		.fourcc		= V4L2_PIX_FMT_SRGGB8,
101		.raw		= true,
102	}, {
103		.fourcc		= V4L2_PIX_FMT_SBGGR10,
104		.raw		= true,
105	}, {
106		.fourcc		= V4L2_PIX_FMT_SGBRG10,
107		.raw		= true,
108	}, {
109		.fourcc		= V4L2_PIX_FMT_SGRBG10,
110		.raw		= true,
111	}, {
112		.fourcc		= V4L2_PIX_FMT_SRGGB10,
113		.raw		= true,
114	}, {
115		.fourcc		= V4L2_PIX_FMT_SBGGR12,
116		.raw		= true,
117	}, {
118		.fourcc		= V4L2_PIX_FMT_SGBRG12,
119		.raw		= true,
120	}, {
121		.fourcc		= V4L2_PIX_FMT_SGRBG12,
122		.raw		= true,
123	}, {
124		.fourcc		= V4L2_PIX_FMT_SRGGB12,
125		.raw		= true,
126	},
127};
128
129/* This is a list of formats that the ISC can receive as *input* */
130static struct isc_format sama7g5_formats_list[] = {
131	{
132		.fourcc		= V4L2_PIX_FMT_SBGGR8,
133		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,
134		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
135		.cfa_baycfg	= ISC_BAY_CFG_BGBG,
136	},
137	{
138		.fourcc		= V4L2_PIX_FMT_SGBRG8,
139		.mbus_code	= MEDIA_BUS_FMT_SGBRG8_1X8,
140		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
141		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
142	},
143	{
144		.fourcc		= V4L2_PIX_FMT_SGRBG8,
145		.mbus_code	= MEDIA_BUS_FMT_SGRBG8_1X8,
146		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
147		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
148	},
149	{
150		.fourcc		= V4L2_PIX_FMT_SRGGB8,
151		.mbus_code	= MEDIA_BUS_FMT_SRGGB8_1X8,
152		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
153		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
154	},
155	{
156		.fourcc		= V4L2_PIX_FMT_SBGGR10,
157		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,
158		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
159		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
160	},
161	{
162		.fourcc		= V4L2_PIX_FMT_SGBRG10,
163		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,
164		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
165		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
166	},
167	{
168		.fourcc		= V4L2_PIX_FMT_SGRBG10,
169		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,
170		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
171		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
172	},
173	{
174		.fourcc		= V4L2_PIX_FMT_SRGGB10,
175		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,
176		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
177		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
178	},
179	{
180		.fourcc		= V4L2_PIX_FMT_SBGGR12,
181		.mbus_code	= MEDIA_BUS_FMT_SBGGR12_1X12,
182		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
183		.cfa_baycfg	= ISC_BAY_CFG_BGBG,
184	},
185	{
186		.fourcc		= V4L2_PIX_FMT_SGBRG12,
187		.mbus_code	= MEDIA_BUS_FMT_SGBRG12_1X12,
188		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
189		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
190	},
191	{
192		.fourcc		= V4L2_PIX_FMT_SGRBG12,
193		.mbus_code	= MEDIA_BUS_FMT_SGRBG12_1X12,
194		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
195		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
196	},
197	{
198		.fourcc		= V4L2_PIX_FMT_SRGGB12,
199		.mbus_code	= MEDIA_BUS_FMT_SRGGB12_1X12,
200		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
201		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
202	},
203	{
204		.fourcc		= V4L2_PIX_FMT_GREY,
205		.mbus_code	= MEDIA_BUS_FMT_Y8_1X8,
206		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
207	},
208	{
209		.fourcc		= V4L2_PIX_FMT_YUYV,
210		.mbus_code	= MEDIA_BUS_FMT_YUYV8_2X8,
211		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
212	},
213	{
214		.fourcc		= V4L2_PIX_FMT_UYVY,
215		.mbus_code	= MEDIA_BUS_FMT_UYVY8_2X8,
216		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
217	},
218	{
219		.fourcc		= V4L2_PIX_FMT_RGB565,
220		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_LE,
221		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
222	},
223	{
224		.fourcc		= V4L2_PIX_FMT_Y10,
225		.mbus_code	= MEDIA_BUS_FMT_Y10_1X10,
226		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
227	},
228};
229
230static void isc_sama7g5_config_csc(struct isc_device *isc)
231{
232	struct regmap *regmap = isc->regmap;
233
234	/* Convert RGB to YUV */
235	regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
236		     0x42 | (0x81 << 16));
237	regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
238		     0x19 | (0x10 << 16));
239	regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
240		     0xFDA | (0xFB6 << 16));
241	regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
242		     0x70 | (0x80 << 16));
243	regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
244		     0x70 | (0xFA2 << 16));
245	regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
246		     0xFEE | (0x80 << 16));
247}
248
249static void isc_sama7g5_config_cbc(struct isc_device *isc)
250{
251	struct regmap *regmap = isc->regmap;
252
253	/* Configure what is set via v4l2 ctrls */
254	regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness);
255	regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast);
256	/* Configure Hue and Saturation as neutral midpoint */
257	regmap_write(regmap, ISC_CBCHS_HUE, 0);
258	regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4));
259}
260
261static void isc_sama7g5_config_cc(struct isc_device *isc)
262{
263	struct regmap *regmap = isc->regmap;
264
265	/* Configure each register at the neutral fixed point 1.0 or 0.0 */
266	regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
267	regmap_write(regmap, ISC_CC_RB_OR, 0);
268	regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
269	regmap_write(regmap, ISC_CC_GB_OG, 0);
270	regmap_write(regmap, ISC_CC_BR_BG, 0);
271	regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
272}
273
274static void isc_sama7g5_config_ctrls(struct isc_device *isc,
275				     const struct v4l2_ctrl_ops *ops)
276{
277	struct isc_ctrls *ctrls = &isc->ctrls;
278	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
279
280	ctrls->contrast = 16;
281
282	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16);
283}
284
285static void isc_sama7g5_config_dpc(struct isc_device *isc)
286{
287	u32 bay_cfg = isc->config.sd_format->cfa_baycfg;
288	struct regmap *regmap = isc->regmap;
289
290	regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK,
291			   (64 << ISC_DPC_CFG_BLOFF_SHIFT));
292	regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK,
293			   (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT));
294}
295
296static void isc_sama7g5_config_gam(struct isc_device *isc)
297{
298	struct regmap *regmap = isc->regmap;
299
300	regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART,
301			   ISC_GAM_CTRL_BIPART);
302}
303
304static void isc_sama7g5_config_rlp(struct isc_device *isc)
305{
306	struct regmap *regmap = isc->regmap;
307	u32 rlp_mode = isc->config.rlp_cfg_mode;
308
309	regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
310			   ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH |
311			   ISC_RLP_CFG_YMODE_MASK, rlp_mode);
312}
313
314static void isc_sama7g5_adapt_pipeline(struct isc_device *isc)
315{
316	isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE;
317}
318
319/* Gamma table with gamma 1/2.2 */
320static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
321	/* index 0 --> gamma bipartite */
322	{
323	      0x980,  0x4c0320,  0x650260,  0x7801e0,  0x8701a0,  0x940180,
324	   0xa00160,  0xab0120,  0xb40120,  0xbd0120,  0xc60100,  0xce0100,
325	   0xd600e0,  0xdd00e0,  0xe400e0,  0xeb00c0,  0xf100c0,  0xf700c0,
326	   0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0,
327	  0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080,
328	  0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a,
329	  0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030,
330	  0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026,
331	  0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020,
332	  0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c,
333	  0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a },
334};
335
336static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
337{
338	struct device_node *np = dev->of_node;
339	struct device_node *epn = NULL;
340	struct isc_subdev_entity *subdev_entity;
341	unsigned int flags;
342	int ret;
343	bool mipi_mode;
344
345	INIT_LIST_HEAD(&isc->subdev_entities);
346
347	mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
348
349	while (1) {
350		struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
351
352		epn = of_graph_get_next_endpoint(np, epn);
353		if (!epn)
354			return 0;
355
356		ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
357						 &v4l2_epn);
358		if (ret) {
359			ret = -EINVAL;
360			dev_err(dev, "Could not parse the endpoint\n");
361			break;
362		}
363
364		subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
365					     GFP_KERNEL);
366		if (!subdev_entity) {
367			ret = -ENOMEM;
368			break;
369		}
370		subdev_entity->epn = epn;
371
372		flags = v4l2_epn.bus.parallel.flags;
373
374		if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
375			subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
376
377		if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
378			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
379
380		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
381			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
382
383		if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
384			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
385					ISC_PFE_CFG0_CCIR656;
386
387		if (mipi_mode)
388			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
389
390		list_add_tail(&subdev_entity->list, &isc->subdev_entities);
391	}
392	of_node_put(epn);
393
394	return ret;
395}
396
397static int microchip_xisc_probe(struct platform_device *pdev)
398{
399	struct device *dev = &pdev->dev;
400	struct isc_device *isc;
401	struct resource *res;
402	void __iomem *io_base;
403	struct isc_subdev_entity *subdev_entity;
404	int irq;
405	int ret;
406	u32 ver;
407
408	isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
409	if (!isc)
410		return -ENOMEM;
411
412	platform_set_drvdata(pdev, isc);
413	isc->dev = dev;
414
415	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
416	io_base = devm_ioremap_resource(dev, res);
417	if (IS_ERR(io_base))
418		return PTR_ERR(io_base);
419
420	isc->regmap = devm_regmap_init_mmio(dev, io_base, &microchip_isc_regmap_config);
421	if (IS_ERR(isc->regmap)) {
422		ret = PTR_ERR(isc->regmap);
423		dev_err(dev, "failed to init register map: %d\n", ret);
424		return ret;
425	}
426
427	irq = platform_get_irq(pdev, 0);
428	if (irq < 0)
429		return irq;
430
431	ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,
432			       "microchip-sama7g5-xisc", isc);
433	if (ret < 0) {
434		dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
435			irq, ret);
436		return ret;
437	}
438
439	isc->gamma_table = isc_sama7g5_gamma_table;
440	isc->gamma_max = 0;
441
442	isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
443	isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
444
445	isc->config_dpc = isc_sama7g5_config_dpc;
446	isc->config_csc = isc_sama7g5_config_csc;
447	isc->config_cbc = isc_sama7g5_config_cbc;
448	isc->config_cc = isc_sama7g5_config_cc;
449	isc->config_gam = isc_sama7g5_config_gam;
450	isc->config_rlp = isc_sama7g5_config_rlp;
451	isc->config_ctrls = isc_sama7g5_config_ctrls;
452
453	isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
454
455	isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
456	isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
457	isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
458	isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
459	isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
460	isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
461	isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
462	isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
463	isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
464
465	isc->controller_formats = sama7g5_controller_formats;
466	isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
467	isc->formats_list = sama7g5_formats_list;
468	isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
469
470	/* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
471	isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
472
473	/* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
474	isc->ispck_required = false;
475
476	ret = microchip_isc_pipeline_init(isc);
477	if (ret)
478		return ret;
479
480	isc->hclock = devm_clk_get(dev, "hclock");
481	if (IS_ERR(isc->hclock)) {
482		ret = PTR_ERR(isc->hclock);
483		dev_err(dev, "failed to get hclock: %d\n", ret);
484		return ret;
485	}
486
487	ret = clk_prepare_enable(isc->hclock);
488	if (ret) {
489		dev_err(dev, "failed to enable hclock: %d\n", ret);
490		return ret;
491	}
492
493	ret = microchip_isc_clk_init(isc);
494	if (ret) {
495		dev_err(dev, "failed to init isc clock: %d\n", ret);
496		goto unprepare_hclk;
497	}
498
499	ret = v4l2_device_register(dev, &isc->v4l2_dev);
500	if (ret) {
501		dev_err(dev, "unable to register v4l2 device.\n");
502		goto unprepare_hclk;
503	}
504
505	ret = xisc_parse_dt(dev, isc);
506	if (ret) {
507		dev_err(dev, "fail to parse device tree\n");
508		goto unregister_v4l2_device;
509	}
510
511	if (list_empty(&isc->subdev_entities)) {
512		dev_err(dev, "no subdev found\n");
513		ret = -ENODEV;
514		goto unregister_v4l2_device;
515	}
516
517	list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
518		struct v4l2_async_subdev *asd;
519		struct fwnode_handle *fwnode =
520			of_fwnode_handle(subdev_entity->epn);
521
522		v4l2_async_nf_init(&subdev_entity->notifier);
523
524		asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
525						      fwnode,
526						      struct v4l2_async_subdev);
527
528		of_node_put(subdev_entity->epn);
529		subdev_entity->epn = NULL;
530
531		if (IS_ERR(asd)) {
532			ret = PTR_ERR(asd);
533			goto cleanup_subdev;
534		}
535
536		subdev_entity->notifier.ops = &microchip_isc_async_ops;
537
538		ret = v4l2_async_nf_register(&isc->v4l2_dev,
539					     &subdev_entity->notifier);
540		if (ret) {
541			dev_err(dev, "fail to register async notifier\n");
542			goto cleanup_subdev;
543		}
544
545		if (video_is_registered(&isc->video_dev))
546			break;
547	}
548
549	regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
550
551	ret = isc_mc_init(isc, ver);
552	if (ret < 0)
553		goto isc_probe_mc_init_err;
554
555	pm_runtime_set_active(dev);
556	pm_runtime_enable(dev);
557	pm_request_idle(dev);
558
559	dev_info(dev, "Microchip XISC version %x\n", ver);
560
561	return 0;
562
563isc_probe_mc_init_err:
564	isc_mc_cleanup(isc);
565
566cleanup_subdev:
567	microchip_isc_subdev_cleanup(isc);
568
569unregister_v4l2_device:
570	v4l2_device_unregister(&isc->v4l2_dev);
571
572unprepare_hclk:
573	clk_disable_unprepare(isc->hclock);
574
575	microchip_isc_clk_cleanup(isc);
576
577	return ret;
578}
579
580static int microchip_xisc_remove(struct platform_device *pdev)
581{
582	struct isc_device *isc = platform_get_drvdata(pdev);
583
584	pm_runtime_disable(&pdev->dev);
585
586	isc_mc_cleanup(isc);
587
588	microchip_isc_subdev_cleanup(isc);
589
590	v4l2_device_unregister(&isc->v4l2_dev);
591
592	clk_disable_unprepare(isc->hclock);
593
594	microchip_isc_clk_cleanup(isc);
595
596	return 0;
597}
598
599static int __maybe_unused xisc_runtime_suspend(struct device *dev)
600{
601	struct isc_device *isc = dev_get_drvdata(dev);
602
603	clk_disable_unprepare(isc->hclock);
604
605	return 0;
606}
607
608static int __maybe_unused xisc_runtime_resume(struct device *dev)
609{
610	struct isc_device *isc = dev_get_drvdata(dev);
611	int ret;
612
613	ret = clk_prepare_enable(isc->hclock);
614	if (ret)
615		return ret;
616
617	return ret;
618}
619
620static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
621	SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
622};
623
624#if IS_ENABLED(CONFIG_OF)
625static const struct of_device_id microchip_xisc_of_match[] = {
626	{ .compatible = "microchip,sama7g5-isc" },
627	{ }
628};
629MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
630#endif
631
632static struct platform_driver microchip_xisc_driver = {
633	.probe	= microchip_xisc_probe,
634	.remove	= microchip_xisc_remove,
635	.driver	= {
636		.name		= "microchip-sama7g5-xisc",
637		.pm		= &microchip_xisc_dev_pm_ops,
638		.of_match_table = of_match_ptr(microchip_xisc_of_match),
639	},
640};
641
642module_platform_driver(microchip_xisc_driver);
643
644MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
645MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
646MODULE_LICENSE("GPL v2");