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

source code of linux/drivers/media/platform/microchip/microchip-sama5d2-isc.c