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 {
68 .fourcc = V4L2_PIX_FMT_ARGB555,
69 },
70 {
71 .fourcc = V4L2_PIX_FMT_RGB565,
72 },
73 {
74 .fourcc = V4L2_PIX_FMT_ABGR32,
75 },
76 {
77 .fourcc = V4L2_PIX_FMT_XBGR32,
78 },
79 {
80 .fourcc = V4L2_PIX_FMT_YUV420,
81 },
82 {
83 .fourcc = V4L2_PIX_FMT_UYVY,
84 },
85 {
86 .fourcc = V4L2_PIX_FMT_VYUY,
87 },
88 {
89 .fourcc = V4L2_PIX_FMT_YUYV,
90 },
91 {
92 .fourcc = V4L2_PIX_FMT_YUV422P,
93 },
94 {
95 .fourcc = V4L2_PIX_FMT_GREY,
96 },
97 {
98 .fourcc = V4L2_PIX_FMT_Y10,
99 },
100 {
101 .fourcc = V4L2_PIX_FMT_Y16,
102 },
103 };
104
105 /* This is a list of formats that the ISC can receive as *input* */
106 static struct isc_format sama7g5_formats_list[] = {
107 {
108 .fourcc = V4L2_PIX_FMT_SBGGR8,
109 .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
110 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
111 .cfa_baycfg = ISC_BAY_CFG_BGBG,
112 },
113 {
114 .fourcc = V4L2_PIX_FMT_SGBRG8,
115 .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
116 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
117 .cfa_baycfg = ISC_BAY_CFG_GBGB,
118 },
119 {
120 .fourcc = V4L2_PIX_FMT_SGRBG8,
121 .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
122 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
123 .cfa_baycfg = ISC_BAY_CFG_GRGR,
124 },
125 {
126 .fourcc = V4L2_PIX_FMT_SRGGB8,
127 .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
128 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
129 .cfa_baycfg = ISC_BAY_CFG_RGRG,
130 },
131 {
132 .fourcc = V4L2_PIX_FMT_SBGGR10,
133 .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
134 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
135 .cfa_baycfg = ISC_BAY_CFG_RGRG,
136 },
137 {
138 .fourcc = V4L2_PIX_FMT_SGBRG10,
139 .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
140 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
141 .cfa_baycfg = ISC_BAY_CFG_GBGB,
142 },
143 {
144 .fourcc = V4L2_PIX_FMT_SGRBG10,
145 .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
146 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
147 .cfa_baycfg = ISC_BAY_CFG_GRGR,
148 },
149 {
150 .fourcc = V4L2_PIX_FMT_SRGGB10,
151 .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
152 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
153 .cfa_baycfg = ISC_BAY_CFG_RGRG,
154 },
155 {
156 .fourcc = V4L2_PIX_FMT_SBGGR12,
157 .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
158 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
159 .cfa_baycfg = ISC_BAY_CFG_BGBG,
160 },
161 {
162 .fourcc = V4L2_PIX_FMT_SGBRG12,
163 .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
164 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
165 .cfa_baycfg = ISC_BAY_CFG_GBGB,
166 },
167 {
168 .fourcc = V4L2_PIX_FMT_SGRBG12,
169 .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
170 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
171 .cfa_baycfg = ISC_BAY_CFG_GRGR,
172 },
173 {
174 .fourcc = V4L2_PIX_FMT_SRGGB12,
175 .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
176 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
177 .cfa_baycfg = ISC_BAY_CFG_RGRG,
178 },
179 {
180 .fourcc = V4L2_PIX_FMT_GREY,
181 .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
182 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
183 },
184 {
185 .fourcc = V4L2_PIX_FMT_YUYV,
186 .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
187 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
188 },
189 {
190 .fourcc = V4L2_PIX_FMT_UYVY,
191 .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
192 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
193 },
194 {
195 .fourcc = V4L2_PIX_FMT_RGB565,
196 .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
197 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
198 },
199 {
200 .fourcc = V4L2_PIX_FMT_Y10,
201 .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
202 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
203 },
204
205 };
206
isc_sama7g5_config_csc(struct isc_device * isc)207 static void isc_sama7g5_config_csc(struct isc_device *isc)
208 {
209 struct regmap *regmap = isc->regmap;
210
211 /* Convert RGB to YUV */
212 regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
213 0x42 | (0x81 << 16));
214 regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
215 0x19 | (0x10 << 16));
216 regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
217 0xFDA | (0xFB6 << 16));
218 regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
219 0x70 | (0x80 << 16));
220 regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
221 0x70 | (0xFA2 << 16));
222 regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
223 0xFEE | (0x80 << 16));
224 }
225
isc_sama7g5_config_cbc(struct isc_device * isc)226 static void isc_sama7g5_config_cbc(struct isc_device *isc)
227 {
228 struct regmap *regmap = isc->regmap;
229
230 /* Configure what is set via v4l2 ctrls */
231 regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness);
232 regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast);
233 /* Configure Hue and Saturation as neutral midpoint */
234 regmap_write(regmap, ISC_CBCHS_HUE, 0);
235 regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4));
236 }
237
isc_sama7g5_config_cc(struct isc_device * isc)238 static void isc_sama7g5_config_cc(struct isc_device *isc)
239 {
240 struct regmap *regmap = isc->regmap;
241
242 /* Configure each register at the neutral fixed point 1.0 or 0.0 */
243 regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
244 regmap_write(regmap, ISC_CC_RB_OR, 0);
245 regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
246 regmap_write(regmap, ISC_CC_GB_OG, 0);
247 regmap_write(regmap, ISC_CC_BR_BG, 0);
248 regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
249 }
250
isc_sama7g5_config_ctrls(struct isc_device * isc,const struct v4l2_ctrl_ops * ops)251 static void isc_sama7g5_config_ctrls(struct isc_device *isc,
252 const struct v4l2_ctrl_ops *ops)
253 {
254 struct isc_ctrls *ctrls = &isc->ctrls;
255 struct v4l2_ctrl_handler *hdl = &ctrls->handler;
256
257 ctrls->contrast = 16;
258
259 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16);
260 }
261
isc_sama7g5_config_dpc(struct isc_device * isc)262 static void isc_sama7g5_config_dpc(struct isc_device *isc)
263 {
264 u32 bay_cfg = isc->config.sd_format->cfa_baycfg;
265 struct regmap *regmap = isc->regmap;
266
267 regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK,
268 (64 << ISC_DPC_CFG_BLOFF_SHIFT));
269 regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK,
270 (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT));
271 }
272
isc_sama7g5_config_gam(struct isc_device * isc)273 static void isc_sama7g5_config_gam(struct isc_device *isc)
274 {
275 struct regmap *regmap = isc->regmap;
276
277 regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART,
278 ISC_GAM_CTRL_BIPART);
279 }
280
isc_sama7g5_config_rlp(struct isc_device * isc)281 static void isc_sama7g5_config_rlp(struct isc_device *isc)
282 {
283 struct regmap *regmap = isc->regmap;
284 u32 rlp_mode = isc->config.rlp_cfg_mode;
285
286 regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
287 ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH |
288 ISC_RLP_CFG_YMODE_MASK, rlp_mode);
289 }
290
isc_sama7g5_adapt_pipeline(struct isc_device * isc)291 static void isc_sama7g5_adapt_pipeline(struct isc_device *isc)
292 {
293 isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE;
294 }
295
296 /* Gamma table with gamma 1/2.2 */
297 static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
298 /* index 0 --> gamma bipartite */
299 {
300 0x980, 0x4c0320, 0x650260, 0x7801e0, 0x8701a0, 0x940180,
301 0xa00160, 0xab0120, 0xb40120, 0xbd0120, 0xc60100, 0xce0100,
302 0xd600e0, 0xdd00e0, 0xe400e0, 0xeb00c0, 0xf100c0, 0xf700c0,
303 0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0,
304 0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080,
305 0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a,
306 0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030,
307 0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026,
308 0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020,
309 0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c,
310 0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a },
311 };
312
xisc_parse_dt(struct device * dev,struct isc_device * isc)313 static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
314 {
315 struct device_node *np = dev->of_node;
316 struct device_node *epn = NULL;
317 struct isc_subdev_entity *subdev_entity;
318 unsigned int flags;
319 int ret;
320 bool mipi_mode;
321
322 INIT_LIST_HEAD(&isc->subdev_entities);
323
324 mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
325
326 while (1) {
327 struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
328
329 epn = of_graph_get_next_endpoint(np, epn);
330 if (!epn)
331 return 0;
332
333 ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
334 &v4l2_epn);
335 if (ret) {
336 ret = -EINVAL;
337 dev_err(dev, "Could not parse the endpoint\n");
338 break;
339 }
340
341 subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
342 GFP_KERNEL);
343 if (!subdev_entity) {
344 ret = -ENOMEM;
345 break;
346 }
347 subdev_entity->epn = epn;
348
349 flags = v4l2_epn.bus.parallel.flags;
350
351 if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
352 subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
353
354 if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
355 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
356
357 if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
358 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
359
360 if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
361 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
362 ISC_PFE_CFG0_CCIR656;
363
364 if (mipi_mode)
365 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
366
367 list_add_tail(&subdev_entity->list, &isc->subdev_entities);
368 }
369 of_node_put(epn);
370
371 return ret;
372 }
373
microchip_xisc_probe(struct platform_device * pdev)374 static int microchip_xisc_probe(struct platform_device *pdev)
375 {
376 struct device *dev = &pdev->dev;
377 struct isc_device *isc;
378 struct resource *res;
379 void __iomem *io_base;
380 struct isc_subdev_entity *subdev_entity;
381 int irq;
382 int ret;
383 u32 ver;
384
385 isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
386 if (!isc)
387 return -ENOMEM;
388
389 platform_set_drvdata(pdev, isc);
390 isc->dev = dev;
391
392 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
393 io_base = devm_ioremap_resource(dev, res);
394 if (IS_ERR(io_base))
395 return PTR_ERR(io_base);
396
397 isc->regmap = devm_regmap_init_mmio(dev, io_base, &isc_regmap_config);
398 if (IS_ERR(isc->regmap)) {
399 ret = PTR_ERR(isc->regmap);
400 dev_err(dev, "failed to init register map: %d\n", ret);
401 return ret;
402 }
403
404 irq = platform_get_irq(pdev, 0);
405 if (irq < 0)
406 return irq;
407
408 ret = devm_request_irq(dev, irq, isc_interrupt, 0,
409 "microchip-sama7g5-xisc", isc);
410 if (ret < 0) {
411 dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
412 irq, ret);
413 return ret;
414 }
415
416 isc->gamma_table = isc_sama7g5_gamma_table;
417 isc->gamma_max = 0;
418
419 isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
420 isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
421
422 isc->config_dpc = isc_sama7g5_config_dpc;
423 isc->config_csc = isc_sama7g5_config_csc;
424 isc->config_cbc = isc_sama7g5_config_cbc;
425 isc->config_cc = isc_sama7g5_config_cc;
426 isc->config_gam = isc_sama7g5_config_gam;
427 isc->config_rlp = isc_sama7g5_config_rlp;
428 isc->config_ctrls = isc_sama7g5_config_ctrls;
429
430 isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
431
432 isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
433 isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
434 isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
435 isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
436 isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
437 isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
438 isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
439 isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
440 isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
441
442 isc->controller_formats = sama7g5_controller_formats;
443 isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
444 isc->formats_list = sama7g5_formats_list;
445 isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
446
447 /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
448 isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
449
450 /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
451 isc->ispck_required = false;
452
453 ret = isc_pipeline_init(isc);
454 if (ret)
455 return ret;
456
457 isc->hclock = devm_clk_get(dev, "hclock");
458 if (IS_ERR(isc->hclock)) {
459 ret = PTR_ERR(isc->hclock);
460 dev_err(dev, "failed to get hclock: %d\n", ret);
461 return ret;
462 }
463
464 ret = clk_prepare_enable(isc->hclock);
465 if (ret) {
466 dev_err(dev, "failed to enable hclock: %d\n", ret);
467 return ret;
468 }
469
470 ret = isc_clk_init(isc);
471 if (ret) {
472 dev_err(dev, "failed to init isc clock: %d\n", ret);
473 goto unprepare_hclk;
474 }
475
476 ret = v4l2_device_register(dev, &isc->v4l2_dev);
477 if (ret) {
478 dev_err(dev, "unable to register v4l2 device.\n");
479 goto unprepare_hclk;
480 }
481
482 ret = xisc_parse_dt(dev, isc);
483 if (ret) {
484 dev_err(dev, "fail to parse device tree\n");
485 goto unregister_v4l2_device;
486 }
487
488 if (list_empty(&isc->subdev_entities)) {
489 dev_err(dev, "no subdev found\n");
490 ret = -ENODEV;
491 goto unregister_v4l2_device;
492 }
493
494 list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
495 struct v4l2_async_subdev *asd;
496
497 v4l2_async_notifier_init(&subdev_entity->notifier);
498
499 asd = v4l2_async_notifier_add_fwnode_remote_subdev(
500 &subdev_entity->notifier,
501 of_fwnode_handle(subdev_entity->epn),
502 struct v4l2_async_subdev);
503
504 of_node_put(subdev_entity->epn);
505 subdev_entity->epn = NULL;
506
507 if (IS_ERR(asd)) {
508 ret = PTR_ERR(asd);
509 goto cleanup_subdev;
510 }
511
512 subdev_entity->notifier.ops = &isc_async_ops;
513
514 ret = v4l2_async_notifier_register(&isc->v4l2_dev,
515 &subdev_entity->notifier);
516 if (ret) {
517 dev_err(dev, "fail to register async notifier\n");
518 goto cleanup_subdev;
519 }
520
521 if (video_is_registered(&isc->video_dev))
522 break;
523 }
524
525 pm_runtime_set_active(dev);
526 pm_runtime_enable(dev);
527 pm_request_idle(dev);
528
529 regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
530 dev_info(dev, "Microchip XISC version %x\n", ver);
531
532 return 0;
533
534 cleanup_subdev:
535 isc_subdev_cleanup(isc);
536
537 unregister_v4l2_device:
538 v4l2_device_unregister(&isc->v4l2_dev);
539
540 unprepare_hclk:
541 clk_disable_unprepare(isc->hclock);
542
543 isc_clk_cleanup(isc);
544
545 return ret;
546 }
547
microchip_xisc_remove(struct platform_device * pdev)548 static int microchip_xisc_remove(struct platform_device *pdev)
549 {
550 struct isc_device *isc = platform_get_drvdata(pdev);
551
552 pm_runtime_disable(&pdev->dev);
553
554 isc_subdev_cleanup(isc);
555
556 v4l2_device_unregister(&isc->v4l2_dev);
557
558 clk_disable_unprepare(isc->hclock);
559
560 isc_clk_cleanup(isc);
561
562 return 0;
563 }
564
xisc_runtime_suspend(struct device * dev)565 static int __maybe_unused xisc_runtime_suspend(struct device *dev)
566 {
567 struct isc_device *isc = dev_get_drvdata(dev);
568
569 clk_disable_unprepare(isc->hclock);
570
571 return 0;
572 }
573
xisc_runtime_resume(struct device * dev)574 static int __maybe_unused xisc_runtime_resume(struct device *dev)
575 {
576 struct isc_device *isc = dev_get_drvdata(dev);
577 int ret;
578
579 ret = clk_prepare_enable(isc->hclock);
580 if (ret)
581 return ret;
582
583 return ret;
584 }
585
586 static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
587 SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
588 };
589
590 #if IS_ENABLED(CONFIG_OF)
591 static const struct of_device_id microchip_xisc_of_match[] = {
592 { .compatible = "microchip,sama7g5-isc" },
593 { }
594 };
595 MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
596 #endif
597
598 static struct platform_driver microchip_xisc_driver = {
599 .probe = microchip_xisc_probe,
600 .remove = microchip_xisc_remove,
601 .driver = {
602 .name = "microchip-sama7g5-xisc",
603 .pm = µchip_xisc_dev_pm_ops,
604 .of_match_table = of_match_ptr(microchip_xisc_of_match),
605 },
606 };
607
608 module_platform_driver(microchip_xisc_driver);
609
610 MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
611 MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
612 MODULE_LICENSE("GPL v2");
613