xref: /linux/drivers/media/platform/microchip/microchip-sama7g5-isc.c (revision 3fd6c59042dbba50391e30862beac979491145fe)
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* */
63 static 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* */
130 static 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 
isc_sama7g5_config_csc(struct isc_device * isc)230 static 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 
isc_sama7g5_config_cbc(struct isc_device * isc)249 static 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 
isc_sama7g5_config_cc(struct isc_device * isc)261 static 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 
isc_sama7g5_config_ctrls(struct isc_device * isc,const struct v4l2_ctrl_ops * ops)274 static 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 
isc_sama7g5_config_dpc(struct isc_device * isc)285 static 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 
isc_sama7g5_config_gam(struct isc_device * isc)296 static 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 
isc_sama7g5_config_rlp(struct isc_device * isc)304 static 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 
isc_sama7g5_adapt_pipeline(struct isc_device * isc)314 static 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 */
320 static 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 
xisc_parse_dt(struct device * dev,struct isc_device * isc)336 static 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;
340 	struct isc_subdev_entity *subdev_entity;
341 	unsigned int flags;
342 	bool mipi_mode;
343 
344 	INIT_LIST_HEAD(&isc->subdev_entities);
345 
346 	mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
347 
348 	for_each_endpoint_of_node(np, epn) {
349 		struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
350 		int ret;
351 
352 		ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
353 						 &v4l2_epn);
354 		if (ret) {
355 			of_node_put(epn);
356 			dev_err(dev, "Could not parse the endpoint\n");
357 			return -EINVAL;
358 		}
359 
360 		subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
361 					     GFP_KERNEL);
362 		if (!subdev_entity) {
363 			of_node_put(epn);
364 			return -ENOMEM;
365 		}
366 		subdev_entity->epn = epn;
367 
368 		flags = v4l2_epn.bus.parallel.flags;
369 
370 		if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
371 			subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
372 
373 		if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
374 			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
375 
376 		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
377 			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
378 
379 		if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
380 			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
381 					ISC_PFE_CFG0_CCIR656;
382 
383 		if (mipi_mode)
384 			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
385 
386 		list_add_tail(&subdev_entity->list, &isc->subdev_entities);
387 	}
388 
389 	return 0;
390 }
391 
microchip_xisc_probe(struct platform_device * pdev)392 static int microchip_xisc_probe(struct platform_device *pdev)
393 {
394 	struct device *dev = &pdev->dev;
395 	struct isc_device *isc;
396 	void __iomem *io_base;
397 	struct isc_subdev_entity *subdev_entity;
398 	int irq;
399 	int ret;
400 	u32 ver;
401 
402 	isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
403 	if (!isc)
404 		return -ENOMEM;
405 
406 	platform_set_drvdata(pdev, isc);
407 	isc->dev = dev;
408 
409 	io_base = devm_platform_ioremap_resource(pdev, 0);
410 	if (IS_ERR(io_base))
411 		return PTR_ERR(io_base);
412 
413 	isc->regmap = devm_regmap_init_mmio(dev, io_base, &microchip_isc_regmap_config);
414 	if (IS_ERR(isc->regmap)) {
415 		ret = PTR_ERR(isc->regmap);
416 		dev_err(dev, "failed to init register map: %d\n", ret);
417 		return ret;
418 	}
419 
420 	irq = platform_get_irq(pdev, 0);
421 	if (irq < 0)
422 		return irq;
423 
424 	ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,
425 			       "microchip-sama7g5-xisc", isc);
426 	if (ret < 0) {
427 		dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
428 			irq, ret);
429 		return ret;
430 	}
431 
432 	isc->gamma_table = isc_sama7g5_gamma_table;
433 	isc->gamma_max = 0;
434 
435 	isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
436 	isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
437 
438 	isc->config_dpc = isc_sama7g5_config_dpc;
439 	isc->config_csc = isc_sama7g5_config_csc;
440 	isc->config_cbc = isc_sama7g5_config_cbc;
441 	isc->config_cc = isc_sama7g5_config_cc;
442 	isc->config_gam = isc_sama7g5_config_gam;
443 	isc->config_rlp = isc_sama7g5_config_rlp;
444 	isc->config_ctrls = isc_sama7g5_config_ctrls;
445 
446 	isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
447 
448 	isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
449 	isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
450 	isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
451 	isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
452 	isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
453 	isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
454 	isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
455 	isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
456 	isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
457 
458 	isc->controller_formats = sama7g5_controller_formats;
459 	isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
460 	isc->formats_list = sama7g5_formats_list;
461 	isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
462 
463 	/* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
464 	isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
465 
466 	/* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
467 	isc->ispck_required = false;
468 
469 	ret = microchip_isc_pipeline_init(isc);
470 	if (ret)
471 		return ret;
472 
473 	isc->hclock = devm_clk_get(dev, "hclock");
474 	if (IS_ERR(isc->hclock)) {
475 		ret = PTR_ERR(isc->hclock);
476 		dev_err(dev, "failed to get hclock: %d\n", ret);
477 		return ret;
478 	}
479 
480 	ret = clk_prepare_enable(isc->hclock);
481 	if (ret) {
482 		dev_err(dev, "failed to enable hclock: %d\n", ret);
483 		return ret;
484 	}
485 
486 	ret = microchip_isc_clk_init(isc);
487 	if (ret) {
488 		dev_err(dev, "failed to init isc clock: %d\n", ret);
489 		goto unprepare_hclk;
490 	}
491 
492 	ret = v4l2_device_register(dev, &isc->v4l2_dev);
493 	if (ret) {
494 		dev_err(dev, "unable to register v4l2 device.\n");
495 		goto unprepare_hclk;
496 	}
497 
498 	ret = xisc_parse_dt(dev, isc);
499 	if (ret) {
500 		dev_err(dev, "fail to parse device tree\n");
501 		goto unregister_v4l2_device;
502 	}
503 
504 	if (list_empty(&isc->subdev_entities)) {
505 		dev_err(dev, "no subdev found\n");
506 		ret = -ENODEV;
507 		goto unregister_v4l2_device;
508 	}
509 
510 	list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
511 		struct v4l2_async_connection *asd;
512 		struct fwnode_handle *fwnode =
513 			of_fwnode_handle(subdev_entity->epn);
514 
515 		v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev);
516 
517 		asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
518 						      fwnode,
519 						      struct v4l2_async_connection);
520 
521 		of_node_put(subdev_entity->epn);
522 		subdev_entity->epn = NULL;
523 
524 		if (IS_ERR(asd)) {
525 			ret = PTR_ERR(asd);
526 			goto cleanup_subdev;
527 		}
528 
529 		subdev_entity->notifier.ops = &microchip_isc_async_ops;
530 
531 		ret = v4l2_async_nf_register(&subdev_entity->notifier);
532 		if (ret) {
533 			dev_err(dev, "fail to register async notifier\n");
534 			goto cleanup_subdev;
535 		}
536 
537 		if (video_is_registered(&isc->video_dev))
538 			break;
539 	}
540 
541 	regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
542 
543 	ret = isc_mc_init(isc, ver);
544 	if (ret < 0)
545 		goto isc_probe_mc_init_err;
546 
547 	pm_runtime_set_active(dev);
548 	pm_runtime_enable(dev);
549 	pm_request_idle(dev);
550 
551 	dev_info(dev, "Microchip XISC version %x\n", ver);
552 
553 	return 0;
554 
555 isc_probe_mc_init_err:
556 	isc_mc_cleanup(isc);
557 
558 cleanup_subdev:
559 	microchip_isc_subdev_cleanup(isc);
560 
561 unregister_v4l2_device:
562 	v4l2_device_unregister(&isc->v4l2_dev);
563 
564 unprepare_hclk:
565 	clk_disable_unprepare(isc->hclock);
566 
567 	microchip_isc_clk_cleanup(isc);
568 
569 	return ret;
570 }
571 
microchip_xisc_remove(struct platform_device * pdev)572 static void microchip_xisc_remove(struct platform_device *pdev)
573 {
574 	struct isc_device *isc = platform_get_drvdata(pdev);
575 
576 	pm_runtime_disable(&pdev->dev);
577 
578 	isc_mc_cleanup(isc);
579 
580 	microchip_isc_subdev_cleanup(isc);
581 
582 	v4l2_device_unregister(&isc->v4l2_dev);
583 
584 	clk_disable_unprepare(isc->hclock);
585 
586 	microchip_isc_clk_cleanup(isc);
587 }
588 
xisc_runtime_suspend(struct device * dev)589 static int __maybe_unused xisc_runtime_suspend(struct device *dev)
590 {
591 	struct isc_device *isc = dev_get_drvdata(dev);
592 
593 	clk_disable_unprepare(isc->hclock);
594 
595 	return 0;
596 }
597 
xisc_runtime_resume(struct device * dev)598 static int __maybe_unused xisc_runtime_resume(struct device *dev)
599 {
600 	struct isc_device *isc = dev_get_drvdata(dev);
601 	int ret;
602 
603 	ret = clk_prepare_enable(isc->hclock);
604 	if (ret)
605 		return ret;
606 
607 	return ret;
608 }
609 
610 static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
611 	SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
612 };
613 
614 #if IS_ENABLED(CONFIG_OF)
615 static const struct of_device_id microchip_xisc_of_match[] = {
616 	{ .compatible = "microchip,sama7g5-isc" },
617 	{ }
618 };
619 MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
620 #endif
621 
622 static struct platform_driver microchip_xisc_driver = {
623 	.probe	= microchip_xisc_probe,
624 	.remove = microchip_xisc_remove,
625 	.driver	= {
626 		.name		= "microchip-sama7g5-xisc",
627 		.pm		= &microchip_xisc_dev_pm_ops,
628 		.of_match_table = of_match_ptr(microchip_xisc_of_match),
629 	},
630 };
631 
632 module_platform_driver(microchip_xisc_driver);
633 
634 MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
635 MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
636 MODULE_LICENSE("GPL v2");
637