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