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(map: regmap, ISC_CSC_YR_YG + isc->offsets.csc,
203 val: 0x42 | (0x81 << 16));
204 regmap_write(map: regmap, ISC_CSC_YB_OY + isc->offsets.csc,
205 val: 0x19 | (0x10 << 16));
206 regmap_write(map: regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
207 val: 0xFDA | (0xFB6 << 16));
208 regmap_write(map: regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
209 val: 0x70 | (0x80 << 16));
210 regmap_write(map: regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
211 val: 0x70 | (0xFA2 << 16));
212 regmap_write(map: regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
213 val: 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(map: regmap, ISC_CBC_BRIGHT + isc->offsets.cbc,
221 val: isc->ctrls.brightness);
222 regmap_write(map: regmap, ISC_CBC_CONTRAST + isc->offsets.cbc,
223 val: 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(map: regmap, ISC_CC_RR_RG, val: (1 << 8));
232 regmap_write(map: regmap, ISC_CC_RB_OR, val: 0);
233 regmap_write(map: regmap, ISC_CC_GR_GG, val: (1 << 8) << 16);
234 regmap_write(map: regmap, ISC_CC_GB_OG, val: 0);
235 regmap_write(map: regmap, ISC_CC_BR_BG, val: 0);
236 regmap_write(map: regmap, ISC_CC_BB_OB, val: (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, min: -2048, max: 2047, step: 1, def: 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(map: regmap, ISC_RLP_CFG + isc->offsets.rlp,
283 ISC_RLP_CFG_MODE_MASK, val: 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 = NULL;
337 struct isc_subdev_entity *subdev_entity;
338 unsigned int flags;
339 int ret;
340
341 INIT_LIST_HEAD(list: &isc->subdev_entities);
342
343 while (1) {
344 struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
345
346 epn = of_graph_get_next_endpoint(parent: np, previous: epn);
347 if (!epn)
348 return 0;
349
350 ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
351 vep: &v4l2_epn);
352 if (ret) {
353 ret = -EINVAL;
354 dev_err(dev, "Could not parse the endpoint\n");
355 break;
356 }
357
358 subdev_entity = devm_kzalloc(dev, size: sizeof(*subdev_entity),
359 GFP_KERNEL);
360 if (!subdev_entity) {
361 ret = -ENOMEM;
362 break;
363 }
364 subdev_entity->epn = epn;
365
366 flags = v4l2_epn.bus.parallel.flags;
367
368 if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
369 subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
370
371 if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
372 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
373
374 if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
375 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
376
377 if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
378 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
379 ISC_PFE_CFG0_CCIR656;
380
381 list_add_tail(new: &subdev_entity->list, head: &isc->subdev_entities);
382 }
383 of_node_put(node: epn);
384
385 return ret;
386}
387
388static int atmel_isc_probe(struct platform_device *pdev)
389{
390 struct device *dev = &pdev->dev;
391 struct isc_device *isc;
392 void __iomem *io_base;
393 struct isc_subdev_entity *subdev_entity;
394 int irq;
395 int ret;
396 u32 ver;
397
398 isc = devm_kzalloc(dev, size: sizeof(*isc), GFP_KERNEL);
399 if (!isc)
400 return -ENOMEM;
401
402 platform_set_drvdata(pdev, data: isc);
403 isc->dev = dev;
404
405 io_base = devm_platform_ioremap_resource(pdev, index: 0);
406 if (IS_ERR(ptr: io_base))
407 return PTR_ERR(ptr: io_base);
408
409 isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config);
410 if (IS_ERR(ptr: isc->regmap)) {
411 ret = PTR_ERR(ptr: isc->regmap);
412 dev_err(dev, "failed to init register map: %d\n", ret);
413 return ret;
414 }
415
416 irq = platform_get_irq(pdev, 0);
417 if (irq < 0)
418 return irq;
419
420 ret = devm_request_irq(dev, irq, handler: atmel_isc_interrupt, irqflags: 0,
421 devname: "atmel-sama5d2-isc", dev_id: isc);
422 if (ret < 0) {
423 dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
424 irq, ret);
425 return ret;
426 }
427
428 isc->gamma_table = isc_sama5d2_gamma_table;
429 isc->gamma_max = 2;
430
431 isc->max_width = ISC_SAMA5D2_MAX_SUPPORT_WIDTH;
432 isc->max_height = ISC_SAMA5D2_MAX_SUPPORT_HEIGHT;
433
434 isc->config_dpc = isc_sama5d2_config_dpc;
435 isc->config_csc = isc_sama5d2_config_csc;
436 isc->config_cbc = isc_sama5d2_config_cbc;
437 isc->config_cc = isc_sama5d2_config_cc;
438 isc->config_gam = isc_sama5d2_config_gam;
439 isc->config_rlp = isc_sama5d2_config_rlp;
440 isc->config_ctrls = isc_sama5d2_config_ctrls;
441
442 isc->adapt_pipeline = isc_sama5d2_adapt_pipeline;
443
444 isc->offsets.csc = ISC_SAMA5D2_CSC_OFFSET;
445 isc->offsets.cbc = ISC_SAMA5D2_CBC_OFFSET;
446 isc->offsets.sub422 = ISC_SAMA5D2_SUB422_OFFSET;
447 isc->offsets.sub420 = ISC_SAMA5D2_SUB420_OFFSET;
448 isc->offsets.rlp = ISC_SAMA5D2_RLP_OFFSET;
449 isc->offsets.his = ISC_SAMA5D2_HIS_OFFSET;
450 isc->offsets.dma = ISC_SAMA5D2_DMA_OFFSET;
451 isc->offsets.version = ISC_SAMA5D2_VERSION_OFFSET;
452 isc->offsets.his_entry = ISC_SAMA5D2_HIS_ENTRY_OFFSET;
453
454 isc->controller_formats = sama5d2_controller_formats;
455 isc->controller_formats_size = ARRAY_SIZE(sama5d2_controller_formats);
456 isc->formats_list = sama5d2_formats_list;
457 isc->formats_list_size = ARRAY_SIZE(sama5d2_formats_list);
458
459 /* sama5d2-isc - 8 bits per beat */
460 isc->dcfg = ISC_DCFG_YMBSIZE_BEATS8 | ISC_DCFG_CMBSIZE_BEATS8;
461
462 /* sama5d2-isc : ISPCK is required and mandatory */
463 isc->ispck_required = true;
464
465 ret = atmel_isc_pipeline_init(isc);
466 if (ret)
467 return ret;
468
469 isc->hclock = devm_clk_get(dev, id: "hclock");
470 if (IS_ERR(ptr: isc->hclock)) {
471 ret = PTR_ERR(ptr: isc->hclock);
472 dev_err(dev, "failed to get hclock: %d\n", ret);
473 return ret;
474 }
475
476 ret = clk_prepare_enable(clk: isc->hclock);
477 if (ret) {
478 dev_err(dev, "failed to enable hclock: %d\n", ret);
479 return ret;
480 }
481
482 ret = atmel_isc_clk_init(isc);
483 if (ret) {
484 dev_err(dev, "failed to init isc clock: %d\n", ret);
485 goto unprepare_hclk;
486 }
487 ret = v4l2_device_register(dev, v4l2_dev: &isc->v4l2_dev);
488 if (ret) {
489 dev_err(dev, "unable to register v4l2 device.\n");
490 goto unprepare_clk;
491 }
492
493 ret = isc_parse_dt(dev, isc);
494 if (ret) {
495 dev_err(dev, "fail to parse device tree\n");
496 goto unregister_v4l2_device;
497 }
498
499 if (list_empty(head: &isc->subdev_entities)) {
500 dev_err(dev, "no subdev found\n");
501 ret = -ENODEV;
502 goto unregister_v4l2_device;
503 }
504
505 list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
506 struct v4l2_async_connection *asd;
507 struct fwnode_handle *fwnode =
508 of_fwnode_handle(subdev_entity->epn);
509
510 v4l2_async_nf_init(notifier: &subdev_entity->notifier, v4l2_dev: &isc->v4l2_dev);
511
512 asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
513 fwnode,
514 struct v4l2_async_connection);
515
516 of_node_put(node: subdev_entity->epn);
517 subdev_entity->epn = NULL;
518
519 if (IS_ERR(ptr: asd)) {
520 ret = PTR_ERR(ptr: asd);
521 goto cleanup_subdev;
522 }
523
524 subdev_entity->notifier.ops = &atmel_isc_async_ops;
525
526 ret = v4l2_async_nf_register(notifier: &subdev_entity->notifier);
527 if (ret) {
528 dev_err(dev, "fail to register async notifier\n");
529 goto cleanup_subdev;
530 }
531
532 if (video_is_registered(vdev: &isc->video_dev))
533 break;
534 }
535
536 pm_runtime_set_active(dev);
537 pm_runtime_enable(dev);
538 pm_request_idle(dev);
539
540 isc->ispck = isc->isc_clks[ISC_ISPCK].clk;
541
542 ret = clk_prepare_enable(clk: isc->ispck);
543 if (ret) {
544 dev_err(dev, "failed to enable ispck: %d\n", ret);
545 goto disable_pm;
546 }
547
548 /* ispck should be greater or equal to hclock */
549 ret = clk_set_rate(clk: isc->ispck, rate: clk_get_rate(clk: isc->hclock));
550 if (ret) {
551 dev_err(dev, "failed to set ispck rate: %d\n", ret);
552 goto unprepare_clk;
553 }
554
555 regmap_read(map: isc->regmap, ISC_VERSION + isc->offsets.version, val: &ver);
556 dev_info(dev, "Microchip ISC version %x\n", ver);
557
558 return 0;
559
560unprepare_clk:
561 clk_disable_unprepare(clk: isc->ispck);
562
563disable_pm:
564 pm_runtime_disable(dev);
565
566cleanup_subdev:
567 atmel_isc_subdev_cleanup(isc);
568
569unregister_v4l2_device:
570 v4l2_device_unregister(v4l2_dev: &isc->v4l2_dev);
571
572unprepare_hclk:
573 clk_disable_unprepare(clk: isc->hclock);
574
575 atmel_isc_clk_cleanup(isc);
576
577 return ret;
578}
579
580static void atmel_isc_remove(struct platform_device *pdev)
581{
582 struct isc_device *isc = platform_get_drvdata(pdev);
583
584 pm_runtime_disable(dev: &pdev->dev);
585
586 atmel_isc_subdev_cleanup(isc);
587
588 v4l2_device_unregister(v4l2_dev: &isc->v4l2_dev);
589
590 clk_disable_unprepare(clk: isc->ispck);
591 clk_disable_unprepare(clk: isc->hclock);
592
593 atmel_isc_clk_cleanup(isc);
594}
595
596static int __maybe_unused isc_runtime_suspend(struct device *dev)
597{
598 struct isc_device *isc = dev_get_drvdata(dev);
599
600 clk_disable_unprepare(clk: isc->ispck);
601 clk_disable_unprepare(clk: isc->hclock);
602
603 return 0;
604}
605
606static int __maybe_unused isc_runtime_resume(struct device *dev)
607{
608 struct isc_device *isc = dev_get_drvdata(dev);
609 int ret;
610
611 ret = clk_prepare_enable(clk: isc->hclock);
612 if (ret)
613 return ret;
614
615 ret = clk_prepare_enable(clk: isc->ispck);
616 if (ret)
617 clk_disable_unprepare(clk: isc->hclock);
618
619 return ret;
620}
621
622static const struct dev_pm_ops atmel_isc_dev_pm_ops = {
623 SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL)
624};
625
626#if IS_ENABLED(CONFIG_OF)
627static const struct of_device_id atmel_isc_of_match[] = {
628 { .compatible = "atmel,sama5d2-isc" },
629 { }
630};
631MODULE_DEVICE_TABLE(of, atmel_isc_of_match);
632#endif
633
634static struct platform_driver atmel_isc_driver = {
635 .probe = atmel_isc_probe,
636 .remove_new = atmel_isc_remove,
637 .driver = {
638 .name = "atmel-sama5d2-isc",
639 .pm = &atmel_isc_dev_pm_ops,
640 .of_match_table = of_match_ptr(atmel_isc_of_match),
641 },
642};
643
644module_platform_driver(atmel_isc_driver);
645
646MODULE_AUTHOR("Songjun Wu");
647MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC");
648MODULE_LICENSE("GPL v2");
649

source code of linux/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c