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 "microchip-isc-regs.h"
53 #include "microchip-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,
94 .fourcc = V4L2_PIX_FMT_SGBRG8,
97 .fourcc = V4L2_PIX_FMT_SGRBG8,
100 .fourcc = V4L2_PIX_FMT_SRGGB8,
103 .fourcc = V4L2_PIX_FMT_SBGGR10,
106 .fourcc = V4L2_PIX_FMT_SGBRG10,
109 .fourcc = V4L2_PIX_FMT_SGRBG10,
112 .fourcc = V4L2_PIX_FMT_SRGGB10,
115 .fourcc = V4L2_PIX_FMT_SBGGR12,
118 .fourcc = V4L2_PIX_FMT_SGBRG12,
121 .fourcc = V4L2_PIX_FMT_SGRBG12,
124 .fourcc = V4L2_PIX_FMT_SRGGB12,
129 /* This is a list of formats that the ISC can receive as *input* */
130 static struct isc_format sama7g5_formats_list[] = {
132 .fourcc = V4L2_PIX_FMT_SBGGR8,
133 .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
134 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
135 .cfa_baycfg = ISC_BAY_CFG_BGBG,
138 .fourcc = V4L2_PIX_FMT_SGBRG8,
139 .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
140 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
141 .cfa_baycfg = ISC_BAY_CFG_GBGB,
144 .fourcc = V4L2_PIX_FMT_SGRBG8,
145 .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
146 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
147 .cfa_baycfg = ISC_BAY_CFG_GRGR,
150 .fourcc = V4L2_PIX_FMT_SRGGB8,
151 .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
152 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
153 .cfa_baycfg = ISC_BAY_CFG_RGRG,
156 .fourcc = V4L2_PIX_FMT_SBGGR10,
157 .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
158 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
159 .cfa_baycfg = ISC_BAY_CFG_RGRG,
162 .fourcc = V4L2_PIX_FMT_SGBRG10,
163 .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
164 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
165 .cfa_baycfg = ISC_BAY_CFG_GBGB,
168 .fourcc = V4L2_PIX_FMT_SGRBG10,
169 .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
170 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
171 .cfa_baycfg = ISC_BAY_CFG_GRGR,
174 .fourcc = V4L2_PIX_FMT_SRGGB10,
175 .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
176 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
177 .cfa_baycfg = ISC_BAY_CFG_RGRG,
180 .fourcc = V4L2_PIX_FMT_SBGGR12,
181 .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
182 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
183 .cfa_baycfg = ISC_BAY_CFG_BGBG,
186 .fourcc = V4L2_PIX_FMT_SGBRG12,
187 .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
188 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
189 .cfa_baycfg = ISC_BAY_CFG_GBGB,
192 .fourcc = V4L2_PIX_FMT_SGRBG12,
193 .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
194 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
195 .cfa_baycfg = ISC_BAY_CFG_GRGR,
198 .fourcc = V4L2_PIX_FMT_SRGGB12,
199 .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
200 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
201 .cfa_baycfg = ISC_BAY_CFG_RGRG,
204 .fourcc = V4L2_PIX_FMT_GREY,
205 .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
206 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
209 .fourcc = V4L2_PIX_FMT_YUYV,
210 .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
211 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
214 .fourcc = V4L2_PIX_FMT_UYVY,
215 .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
216 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
219 .fourcc = V4L2_PIX_FMT_RGB565,
220 .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
221 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
224 .fourcc = V4L2_PIX_FMT_Y10,
225 .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
226 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
230 static void isc_sama7g5_config_csc(struct isc_device *isc)
232 struct regmap *regmap = isc->regmap;
234 /* Convert RGB to YUV */
235 regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
236 0x42 | (0x81 << 16));
237 regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
238 0x19 | (0x10 << 16));
239 regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
240 0xFDA | (0xFB6 << 16));
241 regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
242 0x70 | (0x80 << 16));
243 regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
244 0x70 | (0xFA2 << 16));
245 regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
246 0xFEE | (0x80 << 16));
249 static void isc_sama7g5_config_cbc(struct isc_device *isc)
251 struct regmap *regmap = isc->regmap;
253 /* Configure what is set via v4l2 ctrls */
254 regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness);
255 regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast);
256 /* Configure Hue and Saturation as neutral midpoint */
257 regmap_write(regmap, ISC_CBCHS_HUE, 0);
258 regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4));
261 static void isc_sama7g5_config_cc(struct isc_device *isc)
263 struct regmap *regmap = isc->regmap;
265 /* Configure each register at the neutral fixed point 1.0 or 0.0 */
266 regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
267 regmap_write(regmap, ISC_CC_RB_OR, 0);
268 regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
269 regmap_write(regmap, ISC_CC_GB_OG, 0);
270 regmap_write(regmap, ISC_CC_BR_BG, 0);
271 regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
274 static void isc_sama7g5_config_ctrls(struct isc_device *isc,
275 const struct v4l2_ctrl_ops *ops)
277 struct isc_ctrls *ctrls = &isc->ctrls;
278 struct v4l2_ctrl_handler *hdl = &ctrls->handler;
280 ctrls->contrast = 16;
282 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16);
285 static void isc_sama7g5_config_dpc(struct isc_device *isc)
287 u32 bay_cfg = isc->config.sd_format->cfa_baycfg;
288 struct regmap *regmap = isc->regmap;
290 regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK,
291 (64 << ISC_DPC_CFG_BLOFF_SHIFT));
292 regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK,
293 (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT));
296 static void isc_sama7g5_config_gam(struct isc_device *isc)
298 struct regmap *regmap = isc->regmap;
300 regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART,
301 ISC_GAM_CTRL_BIPART);
304 static void isc_sama7g5_config_rlp(struct isc_device *isc)
306 struct regmap *regmap = isc->regmap;
307 u32 rlp_mode = isc->config.rlp_cfg_mode;
309 regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
310 ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH |
311 ISC_RLP_CFG_YMODE_MASK, rlp_mode);
314 static void isc_sama7g5_adapt_pipeline(struct isc_device *isc)
316 isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE;
319 /* Gamma table with gamma 1/2.2 */
320 static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
321 /* index 0 --> gamma bipartite */
323 0x980, 0x4c0320, 0x650260, 0x7801e0, 0x8701a0, 0x940180,
324 0xa00160, 0xab0120, 0xb40120, 0xbd0120, 0xc60100, 0xce0100,
325 0xd600e0, 0xdd00e0, 0xe400e0, 0xeb00c0, 0xf100c0, 0xf700c0,
326 0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0,
327 0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080,
328 0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a,
329 0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030,
330 0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026,
331 0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020,
332 0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c,
333 0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a },
336 static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
338 struct device_node *np = dev->of_node;
339 struct device_node *epn = NULL;
340 struct isc_subdev_entity *subdev_entity;
345 INIT_LIST_HEAD(&isc->subdev_entities);
347 mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
350 struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
352 epn = of_graph_get_next_endpoint(np, epn);
356 ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
360 dev_err(dev, "Could not parse the endpoint\n");
364 subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
366 if (!subdev_entity) {
370 subdev_entity->epn = epn;
372 flags = v4l2_epn.bus.parallel.flags;
374 if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
375 subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
377 if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
378 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
380 if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
381 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
383 if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
384 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
385 ISC_PFE_CFG0_CCIR656;
388 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
390 list_add_tail(&subdev_entity->list, &isc->subdev_entities);
397 static int microchip_xisc_probe(struct platform_device *pdev)
399 struct device *dev = &pdev->dev;
400 struct isc_device *isc;
401 void __iomem *io_base;
402 struct isc_subdev_entity *subdev_entity;
407 isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
411 platform_set_drvdata(pdev, isc);
414 io_base = devm_platform_ioremap_resource(pdev, 0);
416 return PTR_ERR(io_base);
418 isc->regmap = devm_regmap_init_mmio(dev, io_base, µchip_isc_regmap_config);
419 if (IS_ERR(isc->regmap)) {
420 ret = PTR_ERR(isc->regmap);
421 dev_err(dev, "failed to init register map: %d\n", ret);
425 irq = platform_get_irq(pdev, 0);
429 ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,
430 "microchip-sama7g5-xisc", isc);
432 dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
437 isc->gamma_table = isc_sama7g5_gamma_table;
440 isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
441 isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
443 isc->config_dpc = isc_sama7g5_config_dpc;
444 isc->config_csc = isc_sama7g5_config_csc;
445 isc->config_cbc = isc_sama7g5_config_cbc;
446 isc->config_cc = isc_sama7g5_config_cc;
447 isc->config_gam = isc_sama7g5_config_gam;
448 isc->config_rlp = isc_sama7g5_config_rlp;
449 isc->config_ctrls = isc_sama7g5_config_ctrls;
451 isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
453 isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
454 isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
455 isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
456 isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
457 isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
458 isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
459 isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
460 isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
461 isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
463 isc->controller_formats = sama7g5_controller_formats;
464 isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
465 isc->formats_list = sama7g5_formats_list;
466 isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
468 /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
469 isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
471 /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
472 isc->ispck_required = false;
474 ret = microchip_isc_pipeline_init(isc);
478 isc->hclock = devm_clk_get(dev, "hclock");
479 if (IS_ERR(isc->hclock)) {
480 ret = PTR_ERR(isc->hclock);
481 dev_err(dev, "failed to get hclock: %d\n", ret);
485 ret = clk_prepare_enable(isc->hclock);
487 dev_err(dev, "failed to enable hclock: %d\n", ret);
491 ret = microchip_isc_clk_init(isc);
493 dev_err(dev, "failed to init isc clock: %d\n", ret);
497 ret = v4l2_device_register(dev, &isc->v4l2_dev);
499 dev_err(dev, "unable to register v4l2 device.\n");
503 ret = xisc_parse_dt(dev, isc);
505 dev_err(dev, "fail to parse device tree\n");
506 goto unregister_v4l2_device;
509 if (list_empty(&isc->subdev_entities)) {
510 dev_err(dev, "no subdev found\n");
512 goto unregister_v4l2_device;
515 list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
516 struct v4l2_async_connection *asd;
517 struct fwnode_handle *fwnode =
518 of_fwnode_handle(subdev_entity->epn);
520 v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev);
522 asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
524 struct v4l2_async_connection);
526 of_node_put(subdev_entity->epn);
527 subdev_entity->epn = NULL;
534 subdev_entity->notifier.ops = µchip_isc_async_ops;
536 ret = v4l2_async_nf_register(&subdev_entity->notifier);
538 dev_err(dev, "fail to register async notifier\n");
542 if (video_is_registered(&isc->video_dev))
546 regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
548 ret = isc_mc_init(isc, ver);
550 goto isc_probe_mc_init_err;
552 pm_runtime_set_active(dev);
553 pm_runtime_enable(dev);
554 pm_request_idle(dev);
556 dev_info(dev, "Microchip XISC version %x\n", ver);
560 isc_probe_mc_init_err:
564 microchip_isc_subdev_cleanup(isc);
566 unregister_v4l2_device:
567 v4l2_device_unregister(&isc->v4l2_dev);
570 clk_disable_unprepare(isc->hclock);
572 microchip_isc_clk_cleanup(isc);
577 static void microchip_xisc_remove(struct platform_device *pdev)
579 struct isc_device *isc = platform_get_drvdata(pdev);
581 pm_runtime_disable(&pdev->dev);
585 microchip_isc_subdev_cleanup(isc);
587 v4l2_device_unregister(&isc->v4l2_dev);
589 clk_disable_unprepare(isc->hclock);
591 microchip_isc_clk_cleanup(isc);
594 static int __maybe_unused xisc_runtime_suspend(struct device *dev)
596 struct isc_device *isc = dev_get_drvdata(dev);
598 clk_disable_unprepare(isc->hclock);
603 static int __maybe_unused xisc_runtime_resume(struct device *dev)
605 struct isc_device *isc = dev_get_drvdata(dev);
608 ret = clk_prepare_enable(isc->hclock);
615 static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
616 SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
619 #if IS_ENABLED(CONFIG_OF)
620 static const struct of_device_id microchip_xisc_of_match[] = {
621 { .compatible = "microchip,sama7g5-isc" },
624 MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
627 static struct platform_driver microchip_xisc_driver = {
628 .probe = microchip_xisc_probe,
629 .remove_new = microchip_xisc_remove,
631 .name = "microchip-sama7g5-xisc",
632 .pm = µchip_xisc_dev_pm_ops,
633 .of_match_table = of_match_ptr(microchip_xisc_of_match),
637 module_platform_driver(microchip_xisc_driver);
639 MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
640 MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
641 MODULE_LICENSE("GPL v2");