1 // SPDX-License-Identifier: GPL-2.0
3 * Microchip eXtended Image Sensor Controller (XISC) driver
5 * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries
7 * Author: Eugen Hristev <eugen.hristev@microchip.com>
9 * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS
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
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>
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>
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>
52 #include "atmel-isc-regs.h"
53 #include "atmel-isc.h"
55 #define ISC_SAMA7G5_MAX_SUPPORT_WIDTH 3264
56 #define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT 2464
58 #define ISC_SAMA7G5_PIPELINE \
59 (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
60 CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
62 /* This is a list of the formats that the ISC can *output* */
63 static const struct isc_format sama7g5_controller_formats[] = {
65 .fourcc = V4L2_PIX_FMT_ARGB444,
67 .fourcc = V4L2_PIX_FMT_ARGB555,
69 .fourcc = V4L2_PIX_FMT_RGB565,
71 .fourcc = V4L2_PIX_FMT_ABGR32,
73 .fourcc = V4L2_PIX_FMT_XBGR32,
75 .fourcc = V4L2_PIX_FMT_YUV420,
77 .fourcc = V4L2_PIX_FMT_UYVY,
79 .fourcc = V4L2_PIX_FMT_VYUY,
81 .fourcc = V4L2_PIX_FMT_YUYV,
83 .fourcc = V4L2_PIX_FMT_YUV422P,
85 .fourcc = V4L2_PIX_FMT_GREY,
87 .fourcc = V4L2_PIX_FMT_Y10,
89 .fourcc = V4L2_PIX_FMT_Y16,
91 .fourcc = V4L2_PIX_FMT_SBGGR8,
93 .fourcc = V4L2_PIX_FMT_SGBRG8,
95 .fourcc = V4L2_PIX_FMT_SGRBG8,
97 .fourcc = V4L2_PIX_FMT_SRGGB8,
99 .fourcc = V4L2_PIX_FMT_SBGGR10,
101 .fourcc = V4L2_PIX_FMT_SGBRG10,
103 .fourcc = V4L2_PIX_FMT_SGRBG10,
105 .fourcc = V4L2_PIX_FMT_SRGGB10,
109 /* This is a list of formats that the ISC can receive as *input* */
110 static struct isc_format sama7g5_formats_list[] = {
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
184 .fourcc = V4L2_PIX_FMT_GREY,
185 .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
186 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
189 .fourcc = V4L2_PIX_FMT_YUYV,
190 .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
191 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
194 .fourcc = V4L2_PIX_FMT_UYVY,
195 .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
196 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
199 .fourcc = V4L2_PIX_FMT_RGB565,
200 .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
201 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
204 .fourcc = V4L2_PIX_FMT_Y10,
205 .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
206 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
210 static void isc_sama7g5_config_csc(struct isc_device *isc)
212 struct regmap *regmap = isc->regmap;
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));
229 static void isc_sama7g5_config_cbc(struct isc_device *isc)
231 struct regmap *regmap = isc->regmap;
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));
241 static void isc_sama7g5_config_cc(struct isc_device *isc)
243 struct regmap *regmap = isc->regmap;
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));
254 static void isc_sama7g5_config_ctrls(struct isc_device *isc,
255 const struct v4l2_ctrl_ops *ops)
257 struct isc_ctrls *ctrls = &isc->ctrls;
258 struct v4l2_ctrl_handler *hdl = &ctrls->handler;
260 ctrls->contrast = 16;
262 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16);
265 static void isc_sama7g5_config_dpc(struct isc_device *isc)
267 u32 bay_cfg = isc->config.sd_format->cfa_baycfg;
268 struct regmap *regmap = isc->regmap;
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));
276 static void isc_sama7g5_config_gam(struct isc_device *isc)
278 struct regmap *regmap = isc->regmap;
280 regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART,
281 ISC_GAM_CTRL_BIPART);
284 static void isc_sama7g5_config_rlp(struct isc_device *isc)
286 struct regmap *regmap = isc->regmap;
287 u32 rlp_mode = isc->config.rlp_cfg_mode;
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);
294 static void isc_sama7g5_adapt_pipeline(struct isc_device *isc)
296 isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE;
299 /* Gamma table with gamma 1/2.2 */
300 static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
301 /* index 0 --> gamma bipartite */
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 },
316 static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
318 struct device_node *np = dev->of_node;
319 struct device_node *epn = NULL;
320 struct isc_subdev_entity *subdev_entity;
325 INIT_LIST_HEAD(&isc->subdev_entities);
327 mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
330 struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
332 epn = of_graph_get_next_endpoint(np, epn);
336 ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
340 dev_err(dev, "Could not parse the endpoint\n");
344 subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
346 if (!subdev_entity) {
350 subdev_entity->epn = epn;
352 flags = v4l2_epn.bus.parallel.flags;
354 if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
355 subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
357 if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
358 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
360 if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
361 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
363 if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
364 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
365 ISC_PFE_CFG0_CCIR656;
368 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
370 list_add_tail(&subdev_entity->list, &isc->subdev_entities);
377 static int microchip_xisc_probe(struct platform_device *pdev)
379 struct device *dev = &pdev->dev;
380 struct isc_device *isc;
381 struct resource *res;
382 void __iomem *io_base;
383 struct isc_subdev_entity *subdev_entity;
388 isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
392 platform_set_drvdata(pdev, isc);
395 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
396 io_base = devm_ioremap_resource(dev, res);
398 return PTR_ERR(io_base);
400 isc->regmap = devm_regmap_init_mmio(dev, io_base, &isc_regmap_config);
401 if (IS_ERR(isc->regmap)) {
402 ret = PTR_ERR(isc->regmap);
403 dev_err(dev, "failed to init register map: %d\n", ret);
407 irq = platform_get_irq(pdev, 0);
411 ret = devm_request_irq(dev, irq, isc_interrupt, 0,
412 "microchip-sama7g5-xisc", isc);
414 dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
419 isc->gamma_table = isc_sama7g5_gamma_table;
422 isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
423 isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
425 isc->config_dpc = isc_sama7g5_config_dpc;
426 isc->config_csc = isc_sama7g5_config_csc;
427 isc->config_cbc = isc_sama7g5_config_cbc;
428 isc->config_cc = isc_sama7g5_config_cc;
429 isc->config_gam = isc_sama7g5_config_gam;
430 isc->config_rlp = isc_sama7g5_config_rlp;
431 isc->config_ctrls = isc_sama7g5_config_ctrls;
433 isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
435 isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
436 isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
437 isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
438 isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
439 isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
440 isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
441 isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
442 isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
443 isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
445 isc->controller_formats = sama7g5_controller_formats;
446 isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
447 isc->formats_list = sama7g5_formats_list;
448 isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
450 /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
451 isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
453 /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
454 isc->ispck_required = false;
456 ret = isc_pipeline_init(isc);
460 isc->hclock = devm_clk_get(dev, "hclock");
461 if (IS_ERR(isc->hclock)) {
462 ret = PTR_ERR(isc->hclock);
463 dev_err(dev, "failed to get hclock: %d\n", ret);
467 ret = clk_prepare_enable(isc->hclock);
469 dev_err(dev, "failed to enable hclock: %d\n", ret);
473 ret = isc_clk_init(isc);
475 dev_err(dev, "failed to init isc clock: %d\n", ret);
479 ret = v4l2_device_register(dev, &isc->v4l2_dev);
481 dev_err(dev, "unable to register v4l2 device.\n");
485 ret = xisc_parse_dt(dev, isc);
487 dev_err(dev, "fail to parse device tree\n");
488 goto unregister_v4l2_device;
491 if (list_empty(&isc->subdev_entities)) {
492 dev_err(dev, "no subdev found\n");
494 goto unregister_v4l2_device;
497 list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
498 struct v4l2_async_subdev *asd;
499 struct fwnode_handle *fwnode =
500 of_fwnode_handle(subdev_entity->epn);
502 v4l2_async_nf_init(&subdev_entity->notifier);
504 asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
506 struct v4l2_async_subdev);
508 of_node_put(subdev_entity->epn);
509 subdev_entity->epn = NULL;
516 subdev_entity->notifier.ops = &isc_async_ops;
518 ret = v4l2_async_nf_register(&isc->v4l2_dev,
519 &subdev_entity->notifier);
521 dev_err(dev, "fail to register async notifier\n");
525 if (video_is_registered(&isc->video_dev))
529 pm_runtime_set_active(dev);
530 pm_runtime_enable(dev);
531 pm_request_idle(dev);
533 regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
534 dev_info(dev, "Microchip XISC version %x\n", ver);
539 isc_subdev_cleanup(isc);
541 unregister_v4l2_device:
542 v4l2_device_unregister(&isc->v4l2_dev);
545 clk_disable_unprepare(isc->hclock);
547 isc_clk_cleanup(isc);
552 static int microchip_xisc_remove(struct platform_device *pdev)
554 struct isc_device *isc = platform_get_drvdata(pdev);
556 pm_runtime_disable(&pdev->dev);
558 isc_subdev_cleanup(isc);
560 v4l2_device_unregister(&isc->v4l2_dev);
562 clk_disable_unprepare(isc->hclock);
564 isc_clk_cleanup(isc);
569 static int __maybe_unused xisc_runtime_suspend(struct device *dev)
571 struct isc_device *isc = dev_get_drvdata(dev);
573 clk_disable_unprepare(isc->hclock);
578 static int __maybe_unused xisc_runtime_resume(struct device *dev)
580 struct isc_device *isc = dev_get_drvdata(dev);
583 ret = clk_prepare_enable(isc->hclock);
590 static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
591 SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
594 static const struct of_device_id microchip_xisc_of_match[] = {
595 { .compatible = "microchip,sama7g5-isc" },
598 MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
600 static struct platform_driver microchip_xisc_driver = {
601 .probe = microchip_xisc_probe,
602 .remove = microchip_xisc_remove,
604 .name = "microchip-sama7g5-xisc",
605 .pm = µchip_xisc_dev_pm_ops,
606 .of_match_table = of_match_ptr(microchip_xisc_of_match),
610 module_platform_driver(microchip_xisc_driver);
612 MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
613 MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
614 MODULE_LICENSE("GPL v2");