File Index
Files and directories
Sign in
FILE

atmel-sama5d2-isc.c

File preview with syntax highlighting.

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