1 // SPDX-License-Identifier: GPL-2.0
3 * Microchip Image Sensor Controller (ISC) driver
5 * Copyright (C) 2016-2019 Microchip Technology, Inc.
11 * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA
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
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>
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>
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>
49 #include "microchip-isc-regs.h"
50 #include "microchip-isc.h"
52 #define ISC_SAMA5D2_MAX_SUPPORT_WIDTH 2592
53 #define ISC_SAMA5D2_MAX_SUPPORT_HEIGHT 1944
55 #define ISC_SAMA5D2_PIPELINE \
56 (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
57 CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
59 /* This is a list of the formats that the ISC can *output* */
60 static const struct isc_format sama5d2_controller_formats[] = {
62 .fourcc = V4L2_PIX_FMT_ARGB444,
64 .fourcc = V4L2_PIX_FMT_ARGB555,
66 .fourcc = V4L2_PIX_FMT_RGB565,
68 .fourcc = V4L2_PIX_FMT_ABGR32,
70 .fourcc = V4L2_PIX_FMT_XBGR32,
72 .fourcc = V4L2_PIX_FMT_YUV420,
74 .fourcc = V4L2_PIX_FMT_YUYV,
76 .fourcc = V4L2_PIX_FMT_YUV422P,
78 .fourcc = V4L2_PIX_FMT_GREY,
80 .fourcc = V4L2_PIX_FMT_Y10,
82 .fourcc = V4L2_PIX_FMT_SBGGR8,
85 .fourcc = V4L2_PIX_FMT_SGBRG8,
88 .fourcc = V4L2_PIX_FMT_SGRBG8,
91 .fourcc = V4L2_PIX_FMT_SRGGB8,
94 .fourcc = V4L2_PIX_FMT_SBGGR10,
97 .fourcc = V4L2_PIX_FMT_SGBRG10,
100 .fourcc = V4L2_PIX_FMT_SGRBG10,
103 .fourcc = V4L2_PIX_FMT_SRGGB10,
106 .fourcc = V4L2_PIX_FMT_SBGGR12,
109 .fourcc = V4L2_PIX_FMT_SGBRG12,
112 .fourcc = V4L2_PIX_FMT_SGRBG12,
115 .fourcc = V4L2_PIX_FMT_SRGGB12,
120 /* This is a list of formats that the ISC can receive as *input* */
121 static struct isc_format sama5d2_formats_list[] = {
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
195 .fourcc = V4L2_PIX_FMT_GREY,
196 .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
197 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
200 .fourcc = V4L2_PIX_FMT_YUYV,
201 .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
202 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
205 .fourcc = V4L2_PIX_FMT_RGB565,
206 .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
207 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
210 .fourcc = V4L2_PIX_FMT_Y10,
211 .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
212 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
217 static void isc_sama5d2_config_csc(struct isc_device *isc)
219 struct regmap *regmap = isc->regmap;
221 /* Convert RGB to YUV */
222 regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
223 0x42 | (0x81 << 16));
224 regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
225 0x19 | (0x10 << 16));
226 regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
227 0xFDA | (0xFB6 << 16));
228 regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
229 0x70 | (0x80 << 16));
230 regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
231 0x70 | (0xFA2 << 16));
232 regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
233 0xFEE | (0x80 << 16));
236 static void isc_sama5d2_config_cbc(struct isc_device *isc)
238 struct regmap *regmap = isc->regmap;
240 regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc,
241 isc->ctrls.brightness);
242 regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc,
243 isc->ctrls.contrast);
246 static void isc_sama5d2_config_cc(struct isc_device *isc)
248 struct regmap *regmap = isc->regmap;
250 /* Configure each register at the neutral fixed point 1.0 or 0.0 */
251 regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
252 regmap_write(regmap, ISC_CC_RB_OR, 0);
253 regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
254 regmap_write(regmap, ISC_CC_GB_OG, 0);
255 regmap_write(regmap, ISC_CC_BR_BG, 0);
256 regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
259 static void isc_sama5d2_config_ctrls(struct isc_device *isc,
260 const struct v4l2_ctrl_ops *ops)
262 struct isc_ctrls *ctrls = &isc->ctrls;
263 struct v4l2_ctrl_handler *hdl = &ctrls->handler;
265 ctrls->contrast = 256;
267 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256);
270 static void isc_sama5d2_config_dpc(struct isc_device *isc)
272 /* This module is not present on sama5d2 pipeline */
275 static void isc_sama5d2_config_gam(struct isc_device *isc)
277 /* No specific gamma configuration */
280 static void isc_sama5d2_config_rlp(struct isc_device *isc)
282 struct regmap *regmap = isc->regmap;
283 u32 rlp_mode = isc->config.rlp_cfg_mode;
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.
294 * Thus, if the YCYC mode is selected, replace it with the
295 * sama5d2-compliant mode which is YYCC .
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;
302 regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
303 ISC_RLP_CFG_MODE_MASK, rlp_mode);
306 static void isc_sama5d2_adapt_pipeline(struct isc_device *isc)
308 isc->try_config.bits_pipeline &= ISC_SAMA5D2_PIPELINE;
311 /* Gamma table with gamma 1/2.2 */
312 static 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 },
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 },
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 },
353 static int isc_parse_dt(struct device *dev, struct isc_device *isc)
355 struct device_node *np = dev->of_node;
356 struct device_node *epn = NULL;
357 struct isc_subdev_entity *subdev_entity;
361 INIT_LIST_HEAD(&isc->subdev_entities);
364 struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
366 epn = of_graph_get_next_endpoint(np, epn);
370 ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
374 dev_err(dev, "Could not parse the endpoint\n");
378 subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
380 if (!subdev_entity) {
384 subdev_entity->epn = epn;
386 flags = v4l2_epn.bus.parallel.flags;
388 if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
389 subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
391 if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
392 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
394 if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
395 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
397 if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
398 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
399 ISC_PFE_CFG0_CCIR656;
401 list_add_tail(&subdev_entity->list, &isc->subdev_entities);
408 static int microchip_isc_probe(struct platform_device *pdev)
410 struct device *dev = &pdev->dev;
411 struct isc_device *isc;
412 struct resource *res;
413 void __iomem *io_base;
414 struct isc_subdev_entity *subdev_entity;
419 isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
423 platform_set_drvdata(pdev, isc);
426 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
427 io_base = devm_ioremap_resource(dev, res);
429 return PTR_ERR(io_base);
431 isc->regmap = devm_regmap_init_mmio(dev, io_base, µchip_isc_regmap_config);
432 if (IS_ERR(isc->regmap)) {
433 ret = PTR_ERR(isc->regmap);
434 dev_err(dev, "failed to init register map: %d\n", ret);
438 irq = platform_get_irq(pdev, 0);
442 ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,
443 "microchip-sama5d2-isc", isc);
445 dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
450 isc->gamma_table = isc_sama5d2_gamma_table;
453 isc->max_width = ISC_SAMA5D2_MAX_SUPPORT_WIDTH;
454 isc->max_height = ISC_SAMA5D2_MAX_SUPPORT_HEIGHT;
456 isc->config_dpc = isc_sama5d2_config_dpc;
457 isc->config_csc = isc_sama5d2_config_csc;
458 isc->config_cbc = isc_sama5d2_config_cbc;
459 isc->config_cc = isc_sama5d2_config_cc;
460 isc->config_gam = isc_sama5d2_config_gam;
461 isc->config_rlp = isc_sama5d2_config_rlp;
462 isc->config_ctrls = isc_sama5d2_config_ctrls;
464 isc->adapt_pipeline = isc_sama5d2_adapt_pipeline;
466 isc->offsets.csc = ISC_SAMA5D2_CSC_OFFSET;
467 isc->offsets.cbc = ISC_SAMA5D2_CBC_OFFSET;
468 isc->offsets.sub422 = ISC_SAMA5D2_SUB422_OFFSET;
469 isc->offsets.sub420 = ISC_SAMA5D2_SUB420_OFFSET;
470 isc->offsets.rlp = ISC_SAMA5D2_RLP_OFFSET;
471 isc->offsets.his = ISC_SAMA5D2_HIS_OFFSET;
472 isc->offsets.dma = ISC_SAMA5D2_DMA_OFFSET;
473 isc->offsets.version = ISC_SAMA5D2_VERSION_OFFSET;
474 isc->offsets.his_entry = ISC_SAMA5D2_HIS_ENTRY_OFFSET;
476 isc->controller_formats = sama5d2_controller_formats;
477 isc->controller_formats_size = ARRAY_SIZE(sama5d2_controller_formats);
478 isc->formats_list = sama5d2_formats_list;
479 isc->formats_list_size = ARRAY_SIZE(sama5d2_formats_list);
481 /* sama5d2-isc - 8 bits per beat */
482 isc->dcfg = ISC_DCFG_YMBSIZE_BEATS8 | ISC_DCFG_CMBSIZE_BEATS8;
484 /* sama5d2-isc : ISPCK is required and mandatory */
485 isc->ispck_required = true;
487 ret = microchip_isc_pipeline_init(isc);
491 isc->hclock = devm_clk_get(dev, "hclock");
492 if (IS_ERR(isc->hclock)) {
493 ret = PTR_ERR(isc->hclock);
494 dev_err(dev, "failed to get hclock: %d\n", ret);
498 ret = clk_prepare_enable(isc->hclock);
500 dev_err(dev, "failed to enable hclock: %d\n", ret);
504 ret = microchip_isc_clk_init(isc);
506 dev_err(dev, "failed to init isc clock: %d\n", ret);
509 ret = v4l2_device_register(dev, &isc->v4l2_dev);
511 dev_err(dev, "unable to register v4l2 device.\n");
515 ret = isc_parse_dt(dev, isc);
517 dev_err(dev, "fail to parse device tree\n");
518 goto unregister_v4l2_device;
521 if (list_empty(&isc->subdev_entities)) {
522 dev_err(dev, "no subdev found\n");
524 goto unregister_v4l2_device;
527 list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
528 struct v4l2_async_subdev *asd;
529 struct fwnode_handle *fwnode =
530 of_fwnode_handle(subdev_entity->epn);
532 v4l2_async_nf_init(&subdev_entity->notifier);
534 asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
536 struct v4l2_async_subdev);
538 of_node_put(subdev_entity->epn);
539 subdev_entity->epn = NULL;
546 subdev_entity->notifier.ops = µchip_isc_async_ops;
548 ret = v4l2_async_nf_register(&isc->v4l2_dev,
549 &subdev_entity->notifier);
551 dev_err(dev, "fail to register async notifier\n");
555 if (video_is_registered(&isc->video_dev))
559 regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
561 ret = isc_mc_init(isc, ver);
563 goto isc_probe_mc_init_err;
565 pm_runtime_set_active(dev);
566 pm_runtime_enable(dev);
567 pm_request_idle(dev);
569 isc->ispck = isc->isc_clks[ISC_ISPCK].clk;
571 ret = clk_prepare_enable(isc->ispck);
573 dev_err(dev, "failed to enable ispck: %d\n", ret);
577 /* ispck should be greater or equal to hclock */
578 ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock));
580 dev_err(dev, "failed to set ispck rate: %d\n", ret);
584 dev_info(dev, "Microchip ISC version %x\n", ver);
589 clk_disable_unprepare(isc->ispck);
592 pm_runtime_disable(dev);
594 isc_probe_mc_init_err:
598 microchip_isc_subdev_cleanup(isc);
600 unregister_v4l2_device:
601 v4l2_device_unregister(&isc->v4l2_dev);
604 clk_disable_unprepare(isc->hclock);
606 microchip_isc_clk_cleanup(isc);
611 static void microchip_isc_remove(struct platform_device *pdev)
613 struct isc_device *isc = platform_get_drvdata(pdev);
615 pm_runtime_disable(&pdev->dev);
619 microchip_isc_subdev_cleanup(isc);
621 v4l2_device_unregister(&isc->v4l2_dev);
623 clk_disable_unprepare(isc->ispck);
624 clk_disable_unprepare(isc->hclock);
626 microchip_isc_clk_cleanup(isc);
629 static int __maybe_unused isc_runtime_suspend(struct device *dev)
631 struct isc_device *isc = dev_get_drvdata(dev);
633 clk_disable_unprepare(isc->ispck);
634 clk_disable_unprepare(isc->hclock);
639 static int __maybe_unused isc_runtime_resume(struct device *dev)
641 struct isc_device *isc = dev_get_drvdata(dev);
644 ret = clk_prepare_enable(isc->hclock);
648 ret = clk_prepare_enable(isc->ispck);
650 clk_disable_unprepare(isc->hclock);
655 static const struct dev_pm_ops microchip_isc_dev_pm_ops = {
656 SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL)
659 #if IS_ENABLED(CONFIG_OF)
660 static const struct of_device_id microchip_isc_of_match[] = {
661 { .compatible = "atmel,sama5d2-isc" },
664 MODULE_DEVICE_TABLE(of, microchip_isc_of_match);
667 static struct platform_driver microchip_isc_driver = {
668 .probe = microchip_isc_probe,
669 .remove_new = microchip_isc_remove,
671 .name = "microchip-sama5d2-isc",
672 .pm = µchip_isc_dev_pm_ops,
673 .of_match_table = of_match_ptr(microchip_isc_of_match),
677 module_platform_driver(microchip_isc_driver);
679 MODULE_AUTHOR("Songjun Wu");
680 MODULE_DESCRIPTION("The V4L2 driver for Microchip-ISC");
681 MODULE_LICENSE("GPL v2");