File Index
Files and directories
Sign in
FILE

atmel-sama7g5-isc.c

File preview with syntax highlighting.

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