File Index
Files and directories
Sign in
FILE

microchip-sama7g5-isc.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/microchip/microchip-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 "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;
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
392static 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
555isc_probe_mc_init_err:
556	isc_mc_cleanup(isc);
557
558cleanup_subdev:
559	microchip_isc_subdev_cleanup(isc);
560
561unregister_v4l2_device:
562	v4l2_device_unregister(&isc->v4l2_dev);
563
564unprepare_hclk:
565	clk_disable_unprepare(isc->hclock);
566
567	microchip_isc_clk_cleanup(isc);
568
569	return ret;
570}
571
572static 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
589static 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
598static 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
610static 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)
615static const struct of_device_id microchip_xisc_of_match[] = {
616	{ .compatible = "microchip,sama7g5-isc" },
617	{ }
618};
619MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
620#endif
621
622static 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
632module_platform_driver(microchip_xisc_driver);
633
634MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
635MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
636MODULE_LICENSE("GPL v2");
~/ Read-only file preview.