191b4e487SEugen Hristev // SPDX-License-Identifier: GPL-2.0
291b4e487SEugen Hristev /*
391b4e487SEugen Hristev * Microchip eXtended Image Sensor Controller (XISC) driver
491b4e487SEugen Hristev *
591b4e487SEugen Hristev * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries
691b4e487SEugen Hristev *
791b4e487SEugen Hristev * Author: Eugen Hristev <eugen.hristev@microchip.com>
891b4e487SEugen Hristev *
991b4e487SEugen Hristev * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS
1091b4e487SEugen Hristev *
1191b4e487SEugen Hristev * ISC video pipeline integrates the following submodules:
1291b4e487SEugen Hristev * PFE: Parallel Front End to sample the camera sensor input stream
1391b4e487SEugen Hristev * DPC: Defective Pixel Correction with black offset correction, green disparity
1491b4e487SEugen Hristev * correction and defective pixel correction (3 modules total)
1591b4e487SEugen Hristev * WB: Programmable white balance in the Bayer domain
1691b4e487SEugen Hristev * CFA: Color filter array interpolation module
1791b4e487SEugen Hristev * CC: Programmable color correction
1891b4e487SEugen Hristev * GAM: Gamma correction
1991b4e487SEugen Hristev *VHXS: Vertical and Horizontal Scaler
2091b4e487SEugen Hristev * CSC: Programmable color space conversion
2191b4e487SEugen Hristev *CBHS: Contrast Brightness Hue and Saturation control
2291b4e487SEugen Hristev * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
2391b4e487SEugen Hristev * RLP: This module performs rounding, range limiting
2491b4e487SEugen Hristev * and packing of the incoming data
2591b4e487SEugen Hristev * DMA: This module performs DMA master accesses to write frames to external RAM
2691b4e487SEugen Hristev * HIS: Histogram module performs statistic counters on the frames
2791b4e487SEugen Hristev */
2891b4e487SEugen Hristev
2991b4e487SEugen Hristev #include <linux/clk.h>
3091b4e487SEugen Hristev #include <linux/clkdev.h>
3191b4e487SEugen Hristev #include <linux/clk-provider.h>
3291b4e487SEugen Hristev #include <linux/delay.h>
3391b4e487SEugen Hristev #include <linux/interrupt.h>
3491b4e487SEugen Hristev #include <linux/math64.h>
3591b4e487SEugen Hristev #include <linux/module.h>
3691b4e487SEugen Hristev #include <linux/of.h>
3791b4e487SEugen Hristev #include <linux/of_graph.h>
3891b4e487SEugen Hristev #include <linux/platform_device.h>
3991b4e487SEugen Hristev #include <linux/pm_runtime.h>
4091b4e487SEugen Hristev #include <linux/regmap.h>
4191b4e487SEugen Hristev #include <linux/videodev2.h>
4291b4e487SEugen Hristev
4391b4e487SEugen Hristev #include <media/v4l2-ctrls.h>
4491b4e487SEugen Hristev #include <media/v4l2-device.h>
4591b4e487SEugen Hristev #include <media/v4l2-event.h>
4691b4e487SEugen Hristev #include <media/v4l2-image-sizes.h>
4791b4e487SEugen Hristev #include <media/v4l2-ioctl.h>
4891b4e487SEugen Hristev #include <media/v4l2-fwnode.h>
4991b4e487SEugen Hristev #include <media/v4l2-subdev.h>
5091b4e487SEugen Hristev #include <media/videobuf2-dma-contig.h>
5191b4e487SEugen Hristev
5291b4e487SEugen Hristev #include "microchip-isc-regs.h"
5391b4e487SEugen Hristev #include "microchip-isc.h"
5491b4e487SEugen Hristev
5591b4e487SEugen Hristev #define ISC_SAMA7G5_MAX_SUPPORT_WIDTH 3264
5691b4e487SEugen Hristev #define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT 2464
5791b4e487SEugen Hristev
5891b4e487SEugen Hristev #define ISC_SAMA7G5_PIPELINE \
5991b4e487SEugen Hristev (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
6091b4e487SEugen Hristev CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
6191b4e487SEugen Hristev
6291b4e487SEugen Hristev /* This is a list of the formats that the ISC can *output* */
6391b4e487SEugen Hristev static const struct isc_format sama7g5_controller_formats[] = {
6491b4e487SEugen Hristev {
6591b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_ARGB444,
6691b4e487SEugen Hristev }, {
6791b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_ARGB555,
6891b4e487SEugen Hristev }, {
6991b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_RGB565,
7091b4e487SEugen Hristev }, {
7191b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_ABGR32,
7291b4e487SEugen Hristev }, {
7391b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_XBGR32,
7491b4e487SEugen Hristev }, {
7591b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_YUV420,
7691b4e487SEugen Hristev }, {
7791b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_UYVY,
7891b4e487SEugen Hristev }, {
7991b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_VYUY,
8091b4e487SEugen Hristev }, {
8191b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_YUYV,
8291b4e487SEugen Hristev }, {
8391b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_YUV422P,
8491b4e487SEugen Hristev }, {
8591b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_GREY,
8691b4e487SEugen Hristev }, {
8791b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_Y10,
8891b4e487SEugen Hristev }, {
8991b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_Y16,
9091b4e487SEugen Hristev }, {
9191b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SBGGR8,
9278ba0d79SEugen Hristev .raw = true,
9391b4e487SEugen Hristev }, {
9491b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SGBRG8,
9578ba0d79SEugen Hristev .raw = true,
9691b4e487SEugen Hristev }, {
9791b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SGRBG8,
9878ba0d79SEugen Hristev .raw = true,
9991b4e487SEugen Hristev }, {
10091b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SRGGB8,
10178ba0d79SEugen Hristev .raw = true,
10291b4e487SEugen Hristev }, {
10391b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SBGGR10,
10478ba0d79SEugen Hristev .raw = true,
10591b4e487SEugen Hristev }, {
10691b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SGBRG10,
10778ba0d79SEugen Hristev .raw = true,
10891b4e487SEugen Hristev }, {
10991b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SGRBG10,
11078ba0d79SEugen Hristev .raw = true,
11191b4e487SEugen Hristev }, {
11291b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SRGGB10,
11378ba0d79SEugen Hristev .raw = true,
11478ba0d79SEugen Hristev }, {
11578ba0d79SEugen Hristev .fourcc = V4L2_PIX_FMT_SBGGR12,
11678ba0d79SEugen Hristev .raw = true,
11778ba0d79SEugen Hristev }, {
11878ba0d79SEugen Hristev .fourcc = V4L2_PIX_FMT_SGBRG12,
11978ba0d79SEugen Hristev .raw = true,
12078ba0d79SEugen Hristev }, {
12178ba0d79SEugen Hristev .fourcc = V4L2_PIX_FMT_SGRBG12,
12278ba0d79SEugen Hristev .raw = true,
12378ba0d79SEugen Hristev }, {
12478ba0d79SEugen Hristev .fourcc = V4L2_PIX_FMT_SRGGB12,
12578ba0d79SEugen Hristev .raw = true,
12691b4e487SEugen Hristev },
12791b4e487SEugen Hristev };
12891b4e487SEugen Hristev
12991b4e487SEugen Hristev /* This is a list of formats that the ISC can receive as *input* */
13091b4e487SEugen Hristev static struct isc_format sama7g5_formats_list[] = {
13191b4e487SEugen Hristev {
13291b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SBGGR8,
13391b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
13491b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
13591b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_BGBG,
13691b4e487SEugen Hristev },
13791b4e487SEugen Hristev {
13891b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SGBRG8,
13991b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
14091b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
14191b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_GBGB,
14291b4e487SEugen Hristev },
14391b4e487SEugen Hristev {
14491b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SGRBG8,
14591b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
14691b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
14791b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_GRGR,
14891b4e487SEugen Hristev },
14991b4e487SEugen Hristev {
15091b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SRGGB8,
15191b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
15291b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
15391b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_RGRG,
15491b4e487SEugen Hristev },
15591b4e487SEugen Hristev {
15691b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SBGGR10,
15791b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
15891b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
15991b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_RGRG,
16091b4e487SEugen Hristev },
16191b4e487SEugen Hristev {
16291b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SGBRG10,
16391b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
16491b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
16591b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_GBGB,
16691b4e487SEugen Hristev },
16791b4e487SEugen Hristev {
16891b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SGRBG10,
16991b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
17091b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
17191b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_GRGR,
17291b4e487SEugen Hristev },
17391b4e487SEugen Hristev {
17491b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SRGGB10,
17591b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
17691b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
17791b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_RGRG,
17891b4e487SEugen Hristev },
17991b4e487SEugen Hristev {
18091b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SBGGR12,
18191b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
18291b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
18391b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_BGBG,
18491b4e487SEugen Hristev },
18591b4e487SEugen Hristev {
18691b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SGBRG12,
18791b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
18891b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
18991b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_GBGB,
19091b4e487SEugen Hristev },
19191b4e487SEugen Hristev {
19291b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SGRBG12,
19391b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
19491b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
19591b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_GRGR,
19691b4e487SEugen Hristev },
19791b4e487SEugen Hristev {
19891b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_SRGGB12,
19991b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
20091b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
20191b4e487SEugen Hristev .cfa_baycfg = ISC_BAY_CFG_RGRG,
20291b4e487SEugen Hristev },
20391b4e487SEugen Hristev {
20491b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_GREY,
20591b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
20691b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
20791b4e487SEugen Hristev },
20891b4e487SEugen Hristev {
20991b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_YUYV,
21091b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
21191b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
21291b4e487SEugen Hristev },
21391b4e487SEugen Hristev {
21491b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_UYVY,
21591b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
21691b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
21791b4e487SEugen Hristev },
21891b4e487SEugen Hristev {
21991b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_RGB565,
22091b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
22191b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
22291b4e487SEugen Hristev },
22391b4e487SEugen Hristev {
22491b4e487SEugen Hristev .fourcc = V4L2_PIX_FMT_Y10,
22591b4e487SEugen Hristev .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
22691b4e487SEugen Hristev .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
22791b4e487SEugen Hristev },
22891b4e487SEugen Hristev };
22991b4e487SEugen Hristev
isc_sama7g5_config_csc(struct isc_device * isc)23091b4e487SEugen Hristev static void isc_sama7g5_config_csc(struct isc_device *isc)
23191b4e487SEugen Hristev {
23291b4e487SEugen Hristev struct regmap *regmap = isc->regmap;
23391b4e487SEugen Hristev
23491b4e487SEugen Hristev /* Convert RGB to YUV */
23591b4e487SEugen Hristev regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
23691b4e487SEugen Hristev 0x42 | (0x81 << 16));
23791b4e487SEugen Hristev regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
23891b4e487SEugen Hristev 0x19 | (0x10 << 16));
23991b4e487SEugen Hristev regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
24091b4e487SEugen Hristev 0xFDA | (0xFB6 << 16));
24191b4e487SEugen Hristev regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
24291b4e487SEugen Hristev 0x70 | (0x80 << 16));
24391b4e487SEugen Hristev regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
24491b4e487SEugen Hristev 0x70 | (0xFA2 << 16));
24591b4e487SEugen Hristev regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
24691b4e487SEugen Hristev 0xFEE | (0x80 << 16));
24791b4e487SEugen Hristev }
24891b4e487SEugen Hristev
isc_sama7g5_config_cbc(struct isc_device * isc)24991b4e487SEugen Hristev static void isc_sama7g5_config_cbc(struct isc_device *isc)
25091b4e487SEugen Hristev {
25191b4e487SEugen Hristev struct regmap *regmap = isc->regmap;
25291b4e487SEugen Hristev
25391b4e487SEugen Hristev /* Configure what is set via v4l2 ctrls */
25491b4e487SEugen Hristev regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness);
25591b4e487SEugen Hristev regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast);
25691b4e487SEugen Hristev /* Configure Hue and Saturation as neutral midpoint */
25791b4e487SEugen Hristev regmap_write(regmap, ISC_CBCHS_HUE, 0);
25891b4e487SEugen Hristev regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4));
25991b4e487SEugen Hristev }
26091b4e487SEugen Hristev
isc_sama7g5_config_cc(struct isc_device * isc)26191b4e487SEugen Hristev static void isc_sama7g5_config_cc(struct isc_device *isc)
26291b4e487SEugen Hristev {
26391b4e487SEugen Hristev struct regmap *regmap = isc->regmap;
26491b4e487SEugen Hristev
26591b4e487SEugen Hristev /* Configure each register at the neutral fixed point 1.0 or 0.0 */
26691b4e487SEugen Hristev regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
26791b4e487SEugen Hristev regmap_write(regmap, ISC_CC_RB_OR, 0);
26891b4e487SEugen Hristev regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
26991b4e487SEugen Hristev regmap_write(regmap, ISC_CC_GB_OG, 0);
27091b4e487SEugen Hristev regmap_write(regmap, ISC_CC_BR_BG, 0);
27191b4e487SEugen Hristev regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
27291b4e487SEugen Hristev }
27391b4e487SEugen Hristev
isc_sama7g5_config_ctrls(struct isc_device * isc,const struct v4l2_ctrl_ops * ops)27491b4e487SEugen Hristev static void isc_sama7g5_config_ctrls(struct isc_device *isc,
27591b4e487SEugen Hristev const struct v4l2_ctrl_ops *ops)
27691b4e487SEugen Hristev {
27791b4e487SEugen Hristev struct isc_ctrls *ctrls = &isc->ctrls;
27891b4e487SEugen Hristev struct v4l2_ctrl_handler *hdl = &ctrls->handler;
27991b4e487SEugen Hristev
28091b4e487SEugen Hristev ctrls->contrast = 16;
28191b4e487SEugen Hristev
28291b4e487SEugen Hristev v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16);
28391b4e487SEugen Hristev }
28491b4e487SEugen Hristev
isc_sama7g5_config_dpc(struct isc_device * isc)28591b4e487SEugen Hristev static void isc_sama7g5_config_dpc(struct isc_device *isc)
28691b4e487SEugen Hristev {
28791b4e487SEugen Hristev u32 bay_cfg = isc->config.sd_format->cfa_baycfg;
28891b4e487SEugen Hristev struct regmap *regmap = isc->regmap;
28991b4e487SEugen Hristev
29091b4e487SEugen Hristev regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK,
29191b4e487SEugen Hristev (64 << ISC_DPC_CFG_BLOFF_SHIFT));
29291b4e487SEugen Hristev regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK,
29391b4e487SEugen Hristev (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT));
29491b4e487SEugen Hristev }
29591b4e487SEugen Hristev
isc_sama7g5_config_gam(struct isc_device * isc)29691b4e487SEugen Hristev static void isc_sama7g5_config_gam(struct isc_device *isc)
29791b4e487SEugen Hristev {
29891b4e487SEugen Hristev struct regmap *regmap = isc->regmap;
29991b4e487SEugen Hristev
30091b4e487SEugen Hristev regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART,
30191b4e487SEugen Hristev ISC_GAM_CTRL_BIPART);
30291b4e487SEugen Hristev }
30391b4e487SEugen Hristev
isc_sama7g5_config_rlp(struct isc_device * isc)30491b4e487SEugen Hristev static void isc_sama7g5_config_rlp(struct isc_device *isc)
30591b4e487SEugen Hristev {
30691b4e487SEugen Hristev struct regmap *regmap = isc->regmap;
30791b4e487SEugen Hristev u32 rlp_mode = isc->config.rlp_cfg_mode;
30891b4e487SEugen Hristev
30991b4e487SEugen Hristev regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
31091b4e487SEugen Hristev ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH |
31191b4e487SEugen Hristev ISC_RLP_CFG_YMODE_MASK, rlp_mode);
31291b4e487SEugen Hristev }
31391b4e487SEugen Hristev
isc_sama7g5_adapt_pipeline(struct isc_device * isc)31491b4e487SEugen Hristev static void isc_sama7g5_adapt_pipeline(struct isc_device *isc)
31591b4e487SEugen Hristev {
31691b4e487SEugen Hristev isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE;
31791b4e487SEugen Hristev }
31891b4e487SEugen Hristev
31991b4e487SEugen Hristev /* Gamma table with gamma 1/2.2 */
32091b4e487SEugen Hristev static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
32191b4e487SEugen Hristev /* index 0 --> gamma bipartite */
32291b4e487SEugen Hristev {
32391b4e487SEugen Hristev 0x980, 0x4c0320, 0x650260, 0x7801e0, 0x8701a0, 0x940180,
32491b4e487SEugen Hristev 0xa00160, 0xab0120, 0xb40120, 0xbd0120, 0xc60100, 0xce0100,
32591b4e487SEugen Hristev 0xd600e0, 0xdd00e0, 0xe400e0, 0xeb00c0, 0xf100c0, 0xf700c0,
32691b4e487SEugen Hristev 0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0,
32791b4e487SEugen Hristev 0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080,
32891b4e487SEugen Hristev 0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a,
32991b4e487SEugen Hristev 0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030,
33091b4e487SEugen Hristev 0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026,
33191b4e487SEugen Hristev 0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020,
33291b4e487SEugen Hristev 0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c,
33391b4e487SEugen Hristev 0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a },
33491b4e487SEugen Hristev };
33591b4e487SEugen Hristev
xisc_parse_dt(struct device * dev,struct isc_device * isc)33691b4e487SEugen Hristev static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
33791b4e487SEugen Hristev {
33891b4e487SEugen Hristev struct device_node *np = dev->of_node;
33991b4e487SEugen Hristev struct device_node *epn = NULL;
34091b4e487SEugen Hristev struct isc_subdev_entity *subdev_entity;
34191b4e487SEugen Hristev unsigned int flags;
34291b4e487SEugen Hristev int ret;
34391b4e487SEugen Hristev bool mipi_mode;
34491b4e487SEugen Hristev
34591b4e487SEugen Hristev INIT_LIST_HEAD(&isc->subdev_entities);
34691b4e487SEugen Hristev
34791b4e487SEugen Hristev mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
34891b4e487SEugen Hristev
34991b4e487SEugen Hristev while (1) {
35091b4e487SEugen Hristev struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
35191b4e487SEugen Hristev
35291b4e487SEugen Hristev epn = of_graph_get_next_endpoint(np, epn);
35391b4e487SEugen Hristev if (!epn)
35491b4e487SEugen Hristev return 0;
35591b4e487SEugen Hristev
35691b4e487SEugen Hristev ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
35791b4e487SEugen Hristev &v4l2_epn);
35891b4e487SEugen Hristev if (ret) {
35991b4e487SEugen Hristev ret = -EINVAL;
36091b4e487SEugen Hristev dev_err(dev, "Could not parse the endpoint\n");
36191b4e487SEugen Hristev break;
36291b4e487SEugen Hristev }
36391b4e487SEugen Hristev
36491b4e487SEugen Hristev subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
36591b4e487SEugen Hristev GFP_KERNEL);
36691b4e487SEugen Hristev if (!subdev_entity) {
36791b4e487SEugen Hristev ret = -ENOMEM;
36891b4e487SEugen Hristev break;
36991b4e487SEugen Hristev }
37091b4e487SEugen Hristev subdev_entity->epn = epn;
37191b4e487SEugen Hristev
37291b4e487SEugen Hristev flags = v4l2_epn.bus.parallel.flags;
37391b4e487SEugen Hristev
37491b4e487SEugen Hristev if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
37591b4e487SEugen Hristev subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
37691b4e487SEugen Hristev
37791b4e487SEugen Hristev if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
37891b4e487SEugen Hristev subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
37991b4e487SEugen Hristev
38091b4e487SEugen Hristev if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
38191b4e487SEugen Hristev subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
38291b4e487SEugen Hristev
38391b4e487SEugen Hristev if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
38491b4e487SEugen Hristev subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
38591b4e487SEugen Hristev ISC_PFE_CFG0_CCIR656;
38691b4e487SEugen Hristev
38791b4e487SEugen Hristev if (mipi_mode)
38891b4e487SEugen Hristev subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
38991b4e487SEugen Hristev
39091b4e487SEugen Hristev list_add_tail(&subdev_entity->list, &isc->subdev_entities);
39191b4e487SEugen Hristev }
39291b4e487SEugen Hristev of_node_put(epn);
39391b4e487SEugen Hristev
39491b4e487SEugen Hristev return ret;
39591b4e487SEugen Hristev }
39691b4e487SEugen Hristev
microchip_xisc_probe(struct platform_device * pdev)39791b4e487SEugen Hristev static int microchip_xisc_probe(struct platform_device *pdev)
39891b4e487SEugen Hristev {
39991b4e487SEugen Hristev struct device *dev = &pdev->dev;
40091b4e487SEugen Hristev struct isc_device *isc;
40191b4e487SEugen Hristev void __iomem *io_base;
40291b4e487SEugen Hristev struct isc_subdev_entity *subdev_entity;
40391b4e487SEugen Hristev int irq;
40491b4e487SEugen Hristev int ret;
40591b4e487SEugen Hristev u32 ver;
40691b4e487SEugen Hristev
40791b4e487SEugen Hristev isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
40891b4e487SEugen Hristev if (!isc)
40991b4e487SEugen Hristev return -ENOMEM;
41091b4e487SEugen Hristev
41191b4e487SEugen Hristev platform_set_drvdata(pdev, isc);
41291b4e487SEugen Hristev isc->dev = dev;
41391b4e487SEugen Hristev
4147fa586aeSYangtao Li io_base = devm_platform_ioremap_resource(pdev, 0);
41591b4e487SEugen Hristev if (IS_ERR(io_base))
41691b4e487SEugen Hristev return PTR_ERR(io_base);
41791b4e487SEugen Hristev
41891b4e487SEugen Hristev isc->regmap = devm_regmap_init_mmio(dev, io_base, µchip_isc_regmap_config);
41991b4e487SEugen Hristev if (IS_ERR(isc->regmap)) {
42091b4e487SEugen Hristev ret = PTR_ERR(isc->regmap);
42191b4e487SEugen Hristev dev_err(dev, "failed to init register map: %d\n", ret);
42291b4e487SEugen Hristev return ret;
42391b4e487SEugen Hristev }
42491b4e487SEugen Hristev
42591b4e487SEugen Hristev irq = platform_get_irq(pdev, 0);
42691b4e487SEugen Hristev if (irq < 0)
42791b4e487SEugen Hristev return irq;
42891b4e487SEugen Hristev
42991b4e487SEugen Hristev ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,
43091b4e487SEugen Hristev "microchip-sama7g5-xisc", isc);
43191b4e487SEugen Hristev if (ret < 0) {
43291b4e487SEugen Hristev dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
43391b4e487SEugen Hristev irq, ret);
43491b4e487SEugen Hristev return ret;
43591b4e487SEugen Hristev }
43691b4e487SEugen Hristev
43791b4e487SEugen Hristev isc->gamma_table = isc_sama7g5_gamma_table;
43891b4e487SEugen Hristev isc->gamma_max = 0;
43991b4e487SEugen Hristev
44091b4e487SEugen Hristev isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
44191b4e487SEugen Hristev isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
44291b4e487SEugen Hristev
44391b4e487SEugen Hristev isc->config_dpc = isc_sama7g5_config_dpc;
44491b4e487SEugen Hristev isc->config_csc = isc_sama7g5_config_csc;
44591b4e487SEugen Hristev isc->config_cbc = isc_sama7g5_config_cbc;
44691b4e487SEugen Hristev isc->config_cc = isc_sama7g5_config_cc;
44791b4e487SEugen Hristev isc->config_gam = isc_sama7g5_config_gam;
44891b4e487SEugen Hristev isc->config_rlp = isc_sama7g5_config_rlp;
44991b4e487SEugen Hristev isc->config_ctrls = isc_sama7g5_config_ctrls;
45091b4e487SEugen Hristev
45191b4e487SEugen Hristev isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
45291b4e487SEugen Hristev
45391b4e487SEugen Hristev isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
45491b4e487SEugen Hristev isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
45591b4e487SEugen Hristev isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
45691b4e487SEugen Hristev isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
45791b4e487SEugen Hristev isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
45891b4e487SEugen Hristev isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
45991b4e487SEugen Hristev isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
46091b4e487SEugen Hristev isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
46191b4e487SEugen Hristev isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
46291b4e487SEugen Hristev
46391b4e487SEugen Hristev isc->controller_formats = sama7g5_controller_formats;
46491b4e487SEugen Hristev isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
46591b4e487SEugen Hristev isc->formats_list = sama7g5_formats_list;
46691b4e487SEugen Hristev isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
46791b4e487SEugen Hristev
46891b4e487SEugen Hristev /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
46991b4e487SEugen Hristev isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
47091b4e487SEugen Hristev
47191b4e487SEugen Hristev /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
47291b4e487SEugen Hristev isc->ispck_required = false;
47391b4e487SEugen Hristev
47491b4e487SEugen Hristev ret = microchip_isc_pipeline_init(isc);
47591b4e487SEugen Hristev if (ret)
47691b4e487SEugen Hristev return ret;
47791b4e487SEugen Hristev
47891b4e487SEugen Hristev isc->hclock = devm_clk_get(dev, "hclock");
47991b4e487SEugen Hristev if (IS_ERR(isc->hclock)) {
48091b4e487SEugen Hristev ret = PTR_ERR(isc->hclock);
48191b4e487SEugen Hristev dev_err(dev, "failed to get hclock: %d\n", ret);
48291b4e487SEugen Hristev return ret;
48391b4e487SEugen Hristev }
48491b4e487SEugen Hristev
48591b4e487SEugen Hristev ret = clk_prepare_enable(isc->hclock);
48691b4e487SEugen Hristev if (ret) {
48791b4e487SEugen Hristev dev_err(dev, "failed to enable hclock: %d\n", ret);
48891b4e487SEugen Hristev return ret;
48991b4e487SEugen Hristev }
49091b4e487SEugen Hristev
49191b4e487SEugen Hristev ret = microchip_isc_clk_init(isc);
49291b4e487SEugen Hristev if (ret) {
49391b4e487SEugen Hristev dev_err(dev, "failed to init isc clock: %d\n", ret);
49491b4e487SEugen Hristev goto unprepare_hclk;
49591b4e487SEugen Hristev }
49691b4e487SEugen Hristev
49791b4e487SEugen Hristev ret = v4l2_device_register(dev, &isc->v4l2_dev);
49891b4e487SEugen Hristev if (ret) {
49991b4e487SEugen Hristev dev_err(dev, "unable to register v4l2 device.\n");
50091b4e487SEugen Hristev goto unprepare_hclk;
50191b4e487SEugen Hristev }
50291b4e487SEugen Hristev
50391b4e487SEugen Hristev ret = xisc_parse_dt(dev, isc);
50491b4e487SEugen Hristev if (ret) {
50591b4e487SEugen Hristev dev_err(dev, "fail to parse device tree\n");
50691b4e487SEugen Hristev goto unregister_v4l2_device;
50791b4e487SEugen Hristev }
50891b4e487SEugen Hristev
50991b4e487SEugen Hristev if (list_empty(&isc->subdev_entities)) {
51091b4e487SEugen Hristev dev_err(dev, "no subdev found\n");
51191b4e487SEugen Hristev ret = -ENODEV;
51291b4e487SEugen Hristev goto unregister_v4l2_device;
51391b4e487SEugen Hristev }
51491b4e487SEugen Hristev
51591b4e487SEugen Hristev list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
516adb2dcd5SSakari Ailus struct v4l2_async_connection *asd;
51791b4e487SEugen Hristev struct fwnode_handle *fwnode =
51891b4e487SEugen Hristev of_fwnode_handle(subdev_entity->epn);
51991b4e487SEugen Hristev
520*b8ec754aSSakari Ailus v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev);
52191b4e487SEugen Hristev
52291b4e487SEugen Hristev asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
52391b4e487SEugen Hristev fwnode,
524adb2dcd5SSakari Ailus struct v4l2_async_connection);
52591b4e487SEugen Hristev
52691b4e487SEugen Hristev of_node_put(subdev_entity->epn);
52791b4e487SEugen Hristev subdev_entity->epn = NULL;
52891b4e487SEugen Hristev
52991b4e487SEugen Hristev if (IS_ERR(asd)) {
53091b4e487SEugen Hristev ret = PTR_ERR(asd);
53191b4e487SEugen Hristev goto cleanup_subdev;
53291b4e487SEugen Hristev }
53391b4e487SEugen Hristev
53491b4e487SEugen Hristev subdev_entity->notifier.ops = µchip_isc_async_ops;
53591b4e487SEugen Hristev
536*b8ec754aSSakari Ailus ret = v4l2_async_nf_register(&subdev_entity->notifier);
53791b4e487SEugen Hristev if (ret) {
53891b4e487SEugen Hristev dev_err(dev, "fail to register async notifier\n");
53991b4e487SEugen Hristev goto cleanup_subdev;
54091b4e487SEugen Hristev }
54191b4e487SEugen Hristev
54291b4e487SEugen Hristev if (video_is_registered(&isc->video_dev))
54391b4e487SEugen Hristev break;
54491b4e487SEugen Hristev }
54591b4e487SEugen Hristev
546920b2665SEugen Hristev regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
547920b2665SEugen Hristev
548920b2665SEugen Hristev ret = isc_mc_init(isc, ver);
549920b2665SEugen Hristev if (ret < 0)
550920b2665SEugen Hristev goto isc_probe_mc_init_err;
551920b2665SEugen Hristev
55291b4e487SEugen Hristev pm_runtime_set_active(dev);
55391b4e487SEugen Hristev pm_runtime_enable(dev);
55491b4e487SEugen Hristev pm_request_idle(dev);
55591b4e487SEugen Hristev
55691b4e487SEugen Hristev dev_info(dev, "Microchip XISC version %x\n", ver);
55791b4e487SEugen Hristev
55891b4e487SEugen Hristev return 0;
55991b4e487SEugen Hristev
560920b2665SEugen Hristev isc_probe_mc_init_err:
561920b2665SEugen Hristev isc_mc_cleanup(isc);
562920b2665SEugen Hristev
56391b4e487SEugen Hristev cleanup_subdev:
56491b4e487SEugen Hristev microchip_isc_subdev_cleanup(isc);
56591b4e487SEugen Hristev
56691b4e487SEugen Hristev unregister_v4l2_device:
56791b4e487SEugen Hristev v4l2_device_unregister(&isc->v4l2_dev);
56891b4e487SEugen Hristev
56991b4e487SEugen Hristev unprepare_hclk:
57091b4e487SEugen Hristev clk_disable_unprepare(isc->hclock);
57191b4e487SEugen Hristev
57291b4e487SEugen Hristev microchip_isc_clk_cleanup(isc);
57391b4e487SEugen Hristev
57491b4e487SEugen Hristev return ret;
57591b4e487SEugen Hristev }
57691b4e487SEugen Hristev
microchip_xisc_remove(struct platform_device * pdev)577447728bdSUwe Kleine-König static void microchip_xisc_remove(struct platform_device *pdev)
57891b4e487SEugen Hristev {
57991b4e487SEugen Hristev struct isc_device *isc = platform_get_drvdata(pdev);
58091b4e487SEugen Hristev
58191b4e487SEugen Hristev pm_runtime_disable(&pdev->dev);
58291b4e487SEugen Hristev
583920b2665SEugen Hristev isc_mc_cleanup(isc);
584920b2665SEugen Hristev
58591b4e487SEugen Hristev microchip_isc_subdev_cleanup(isc);
58691b4e487SEugen Hristev
58791b4e487SEugen Hristev v4l2_device_unregister(&isc->v4l2_dev);
58891b4e487SEugen Hristev
58991b4e487SEugen Hristev clk_disable_unprepare(isc->hclock);
59091b4e487SEugen Hristev
59191b4e487SEugen Hristev microchip_isc_clk_cleanup(isc);
59291b4e487SEugen Hristev }
59391b4e487SEugen Hristev
xisc_runtime_suspend(struct device * dev)59491b4e487SEugen Hristev static int __maybe_unused xisc_runtime_suspend(struct device *dev)
59591b4e487SEugen Hristev {
59691b4e487SEugen Hristev struct isc_device *isc = dev_get_drvdata(dev);
59791b4e487SEugen Hristev
59891b4e487SEugen Hristev clk_disable_unprepare(isc->hclock);
59991b4e487SEugen Hristev
60091b4e487SEugen Hristev return 0;
60191b4e487SEugen Hristev }
60291b4e487SEugen Hristev
xisc_runtime_resume(struct device * dev)60391b4e487SEugen Hristev static int __maybe_unused xisc_runtime_resume(struct device *dev)
60491b4e487SEugen Hristev {
60591b4e487SEugen Hristev struct isc_device *isc = dev_get_drvdata(dev);
60691b4e487SEugen Hristev int ret;
60791b4e487SEugen Hristev
60891b4e487SEugen Hristev ret = clk_prepare_enable(isc->hclock);
60991b4e487SEugen Hristev if (ret)
61091b4e487SEugen Hristev return ret;
61191b4e487SEugen Hristev
61291b4e487SEugen Hristev return ret;
61391b4e487SEugen Hristev }
61491b4e487SEugen Hristev
61591b4e487SEugen Hristev static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
61691b4e487SEugen Hristev SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
61791b4e487SEugen Hristev };
61891b4e487SEugen Hristev
61991b4e487SEugen Hristev #if IS_ENABLED(CONFIG_OF)
62091b4e487SEugen Hristev static const struct of_device_id microchip_xisc_of_match[] = {
62191b4e487SEugen Hristev { .compatible = "microchip,sama7g5-isc" },
62291b4e487SEugen Hristev { }
62391b4e487SEugen Hristev };
62491b4e487SEugen Hristev MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
62591b4e487SEugen Hristev #endif
62691b4e487SEugen Hristev
62791b4e487SEugen Hristev static struct platform_driver microchip_xisc_driver = {
62891b4e487SEugen Hristev .probe = microchip_xisc_probe,
629447728bdSUwe Kleine-König .remove_new = microchip_xisc_remove,
63091b4e487SEugen Hristev .driver = {
63191b4e487SEugen Hristev .name = "microchip-sama7g5-xisc",
63291b4e487SEugen Hristev .pm = µchip_xisc_dev_pm_ops,
63391b4e487SEugen Hristev .of_match_table = of_match_ptr(microchip_xisc_of_match),
63491b4e487SEugen Hristev },
63591b4e487SEugen Hristev };
63691b4e487SEugen Hristev
63791b4e487SEugen Hristev module_platform_driver(microchip_xisc_driver);
63891b4e487SEugen Hristev
63991b4e487SEugen Hristev MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
64091b4e487SEugen Hristev MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
64191b4e487SEugen Hristev MODULE_LICENSE("GPL v2");
642