From: Louis Kuo <louis.kuo@xxxxxxxxxxxx>
This will add the mediatek ISP3.0 seninf (sensor interface) driver found
on several Mediatek SoCs such as the mt8365.
Then seninf module has 4 physical CSI-2 inputs. Depending on the soc they
may not be all connected.
Signed-off-by: Louis Kuo <louis.kuo@xxxxxxxxxxxx>
Signed-off-by: Phi-bang Nguyen <pnguyen@xxxxxxxxxxxx>
Signed-off-by: Florian Sylvestre <fsylvestre@xxxxxxxxxxxx>
Co-developed-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>
Signed-off-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>
Co-developed-by: Julien Stephan <jstephan@xxxxxxxxxxxx>
Signed-off-by: Julien Stephan <jstephan@xxxxxxxxxxxx>
---
MAINTAINERS | 1 +
drivers/media/platform/mediatek/Kconfig | 1 +
drivers/media/platform/mediatek/Makefile | 1 +
drivers/media/platform/mediatek/isp/Kconfig | 2 +
drivers/media/platform/mediatek/isp/Makefile | 3 +
.../platform/mediatek/isp/isp_30/Kconfig | 16 +
.../platform/mediatek/isp/isp_30/Makefile | 3 +
.../mediatek/isp/isp_30/seninf/Makefile | 5 +
.../mediatek/isp/isp_30/seninf/mtk_seninf.c | 1488 +++++++++++++++++
.../isp/isp_30/seninf/mtk_seninf_reg.h | 112 ++
10 files changed, 1632 insertions(+)
create mode 100644 drivers/media/platform/mediatek/isp/Kconfig
create mode 100644 drivers/media/platform/mediatek/isp/Makefile
create mode 100644 drivers/media/platform/mediatek/isp/isp_30/Kconfig
create mode 100644 drivers/media/platform/mediatek/isp/isp_30/Makefile
create mode 100644 drivers/media/platform/mediatek/isp/isp_30/seninf/Makefile
create mode 100644 drivers/media/platform/mediatek/isp/isp_30/seninf/mtk_seninf.c
create mode 100644 drivers/media/platform/mediatek/isp/isp_30/seninf/mtk_seninf_reg.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 3ea2158864e1..52d200d5e36c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13618,6 +13618,7 @@ M: Andy Hsieh <andy.hsieh@xxxxxxxxxxxx>
S: Supported
F: Documentation/devicetree/bindings/media/mediatek,mt8365-camsv.yaml
F: Documentation/devicetree/bindings/media/mediatek,mt8365-seninf.yaml
+F: drivers/media/platform/mediatek/isp/isp_30/seninf/*
MEDIATEK SMI DRIVER
M: Yong Wu <yong.wu@xxxxxxxxxxxx>
diff --git a/drivers/media/platform/mediatek/Kconfig b/drivers/media/platform/mediatek/Kconfig
index 84104e2cd024..4e0a5a43f35e 100644
--- a/drivers/media/platform/mediatek/Kconfig
+++ b/drivers/media/platform/mediatek/Kconfig
@@ -7,3 +7,4 @@ source "drivers/media/platform/mediatek/mdp/Kconfig"
source "drivers/media/platform/mediatek/vcodec/Kconfig"
source "drivers/media/platform/mediatek/vpu/Kconfig"
source "drivers/media/platform/mediatek/mdp3/Kconfig"
+source "drivers/media/platform/mediatek/isp/Kconfig"
diff --git a/drivers/media/platform/mediatek/Makefile b/drivers/media/platform/mediatek/Makefile
index 38e6ba917fe5..695f05f525a6 100644
--- a/drivers/media/platform/mediatek/Makefile
+++ b/drivers/media/platform/mediatek/Makefile
@@ -4,3 +4,4 @@ obj-y += mdp/
obj-y += vcodec/
obj-y += vpu/
obj-y += mdp3/
+obj-y += isp/
diff --git a/drivers/media/platform/mediatek/isp/Kconfig b/drivers/media/platform/mediatek/isp/Kconfig
new file mode 100644
index 000000000000..708b9a6660d2
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/Kconfig
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+source "drivers/media/platform/mediatek/isp/isp_30/Kconfig"
diff --git a/drivers/media/platform/mediatek/isp/Makefile b/drivers/media/platform/mediatek/isp/Makefile
new file mode 100644
index 000000000000..a81ab33d0dd3
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-y += isp_30/
diff --git a/drivers/media/platform/mediatek/isp/isp_30/Kconfig b/drivers/media/platform/mediatek/isp/isp_30/Kconfig
new file mode 100644
index 000000000000..9791312589fb
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_30/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config MTK_SENINF30
+ tristate "MediaTek ISP3.0 SENINF driver"
+ depends on VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on OF
+ select V4L2_FWNODE
+ default n
+ help
+ This driver provides a MIPI CSI-2 receiver interface to connect
+ an external camera module with MediaTek ISP3.0. It is able to handle
+ multiple cameras at the same time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mtk-seninf.
diff --git a/drivers/media/platform/mediatek/isp/isp_30/Makefile b/drivers/media/platform/mediatek/isp/isp_30/Makefile
new file mode 100644
index 000000000000..ac3142de4739
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_30/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_MTK_SENINF30) += seninf/
diff --git a/drivers/media/platform/mediatek/isp/isp_30/seninf/Makefile b/drivers/media/platform/mediatek/isp/isp_30/seninf/Makefile
new file mode 100644
index 000000000000..f28480d6d6c3
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_30/seninf/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+mtk-seninf-objs += mtk_seninf.o
+
+obj-$(CONFIG_MTK_SENINF30) += mtk-seninf.o
diff --git a/drivers/media/platform/mediatek/isp/isp_30/seninf/mtk_seninf.c b/drivers/media/platform/mediatek/isp/isp_30/seninf/mtk_seninf.c
new file mode 100644
index 000000000000..67b2c697d9ca
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_30/seninf/mtk_seninf.c
@@ -0,0 +1,1488 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+#include "mtk_seninf_reg.h"
+
+#define SENINF_TIMESTAMP_STEP 0x67
+#define SENINF_SETTLE_DELAY 0x15
+#define SENINF_HS_TRAIL_PARAMETER 0x8
+
+#define SENINF_MAX_NUM_INPUTS 4
+#define SENINF_MAX_NUM_OUTPUTS 6
+#define SENINF_MAX_NUM_MUXES 6
+#define SENINF_MAX_NUM_PADS (SENINF_MAX_NUM_INPUTS + \
+ SENINF_MAX_NUM_OUTPUTS)
+
+#define SENINF_DEFAULT_BUS_FMT MEDIA_BUS_FMT_SGRBG10_1X10
+#define SENINF_DEFAULT_WIDTH 1920
+#define SENINF_DEFAULT_HEIGHT 1080
+
+#define SENINF_PAD_10BIT 0
+
+#define SENINF_TEST_MODEL 0
+#define SENINF_NORMAL_MODEL 1
+#define SENINF_ALL_ERR_IRQ_EN 0x7f
+#define SENINF_IRQ_CLR_SEL 0x80000000
+
+#define SENINF_MIPI_SENSOR 0x8
+
+#define MTK_CSI_MAX_LANES 4
+
+/* Port number in the device tree. */
+enum mtk_seninf_port {
+ CSI_PORT_0 = 0, /* 4D1C or 2D1C */
+ CSI_PORT_1, /* 4D1C */
+ CSI_PORT_2, /* 4D1C */
+ CSI_PORT_0B, /* 2D1C */
+};
+
+enum mtk_seninf_id {
+ SENINF_1 = 0,
+ SENINF_2 = 1,
+ SENINF_3 = 2,
+ SENINF_5 = 4,
+};
+
+static const u32 port_to_seninf_id[] = {
+ [CSI_PORT_0] = SENINF_1,
+ [CSI_PORT_1] = SENINF_3,
+ [CSI_PORT_2] = SENINF_5,
+ [CSI_PORT_0B] = SENINF_2,
+};
+
+enum mtk_seninf_phy_mode {
+ SENINF_PHY_MODE_NONE,
+ SENINF_PHY_MODE_4D1C,
+ SENINF_PHY_MODE_2D1C,
+};
+
+enum mtk_seninf_format_flag {
+ MTK_SENINF_FORMAT_BAYER = BIT(0),
+ MTK_SENINF_FORMAT_DPCM = BIT(1),
+ MTK_SENINF_FORMAT_JPEG = BIT(2),
+ MTK_SENINF_FORMAT_INPUT_ONLY = BIT(3),
+};
+
+/**
+ * struct mtk_seninf_conf - Model-specific SENINF parameters
+ * @model: Model description
+ * @nb_inputs: Number of SENINF inputs
+ * @nb_muxes: Number of SENINF MUX (FIFO) instances
+ * @nb_outputs: Number of outputs (to CAM and CAMSV instances)
+ */
+struct mtk_seninf_conf {
+ const char *model;
+ u8 nb_inputs;
+ u8 nb_muxes;
+ u8 nb_outputs;
+};
+
+/**
+ * struct mtk_seninf_format_info - Information about media bus formats
+ * @code: V4L2 media bus code
+ * @flags: Flags describing the format, as a combination of MTK_SENINF_FORMAT_*
+ */
+struct mtk_seninf_format_info {
+ u32 code;
+ u32 flags;
+};
+
+/**
+ * struct mtk_seninf_input - SENINF input block
+ * @pad: DT port and media entity pad number
+ * @seninf_id: SENINF hardware instance ID
+ * @base: Memory mapped I/O based address
+ * @seninf: Back pointer to the mtk_seninf
+ * @phy: PHY connected to the input
+ * @phy_mode: PHY operation mode (NONE when the input is not connected)
+ * @bus: CSI-2 bus configuration from DT
+ * @source_sd: Source subdev connected to the input
+ */
+struct mtk_seninf_input {
+ enum mtk_seninf_port pad;
+ enum mtk_seninf_id seninf_id;
+ void __iomem *base;
+ struct mtk_seninf *seninf;
+
+ struct phy *phy;
+ enum mtk_seninf_phy_mode phy_mode;
+
+ struct v4l2_mbus_config_mipi_csi2 bus;
+
+ struct v4l2_subdev *source_sd;
+};
+
+/**
+ * struct mtk_seninf_mux - SENINF MUX channel
+ * @pad: DT port and media entity pad number
+ * @mux_id: MUX hardware instance ID
+ * @base: Memory mapped I/O based address
+ * @seninf: Back pointer to the mtk_seninf
+ */
+struct mtk_seninf_mux {
+ unsigned int pad;
+ unsigned int mux_id;
+ void __iomem *base;
+ struct mtk_seninf *seninf;
+};
+
+/**
+ * struct mtk_seninf - Top-level SENINF device
+ * @dev: The (platform) device
+ * @phy: PHYs at the SENINF inputs
+ * @num_clks: Number of clocks in the clks array
+ * @clks: Clocks
+ * @base: Memory mapped I/O base address
+ * @media_dev: Media controller device
+ * @v4l2_dev: V4L2 device
+ * @subdev: V4L2 subdevice
+ * @pads: Media entity pads
+ * @notifier: V4L2 async notifier for source subdevs
+ * @ctrl_handler: V4L2 controls handler
+ * @source_format: Active format on the source pad
+ * @inputs: Array of SENINF inputs
+ * @muxes: Array of MUXes
+ * @conf: Model-specific SENINF parameters
+ * @is_testmode: Whether or not the test pattern generator is enabled
+ */
+struct mtk_seninf {
+ struct device *dev;
+ struct phy *phy[5];
+ unsigned int num_clks;
+ struct clk_bulk_data *clks;
+ void __iomem *base;
+
+ struct media_device media_dev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_subdev subdev;
+ struct media_pad pads[SENINF_MAX_NUM_PADS];
+ struct v4l2_async_notifier notifier;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ struct mtk_seninf_input inputs[SENINF_MAX_NUM_INPUTS];
+ struct mtk_seninf_mux muxes[SENINF_MAX_NUM_MUXES];
+
+ const struct mtk_seninf_conf *conf;
+
+ bool is_testmode;
+};
+
+inline struct mtk_seninf *sd_to_mtk_seninf(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mtk_seninf, subdev);
+}
+
+static inline bool mtk_seninf_pad_is_sink(struct mtk_seninf *priv,
+ unsigned int pad)
+{
+ return pad < priv->conf->nb_inputs;
+}
+
+static inline bool mtk_seninf_pad_is_source(struct mtk_seninf *priv,
+ unsigned int pad)
+{
+ return !mtk_seninf_pad_is_sink(priv, pad);
+}
+
+/* -----------------------------------------------------------------------------
+ * Formats
+ */
+
+static const struct mtk_seninf_format_info mtk_seninf_formats[] = {
+ {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR14_1X14,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG14_1X14,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG14_1X14,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB14_1X14,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .flags = MTK_SENINF_FORMAT_BAYER,
+ }, {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ }, {
+ .code = MEDIA_BUS_FMT_VYUY8_1X16,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ }, {
+ .code = MEDIA_BUS_FMT_YVYU8_1X16,
+ }, {
+ .code = MEDIA_BUS_FMT_JPEG_1X8,
+ .flags = MTK_SENINF_FORMAT_JPEG,
+ }, {
+ .code = MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8,
+ .flags = MTK_SENINF_FORMAT_JPEG,
+ },
+ /* Keep the input-only formats last. */
+ {
+ .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+ .flags = MTK_SENINF_FORMAT_DPCM | MTK_SENINF_FORMAT_INPUT_ONLY,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
+ .flags = MTK_SENINF_FORMAT_DPCM | MTK_SENINF_FORMAT_INPUT_ONLY,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
+ .flags = MTK_SENINF_FORMAT_DPCM | MTK_SENINF_FORMAT_INPUT_ONLY,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
+ .flags = MTK_SENINF_FORMAT_DPCM | MTK_SENINF_FORMAT_INPUT_ONLY,
+ }
+};
+
+static const struct mtk_seninf_format_info *mtk_seninf_format_info(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mtk_seninf_formats); ++i) {
+ if (mtk_seninf_formats[i].code == code)
+ return &mtk_seninf_formats[i];
+ }
+
+ return NULL;
+}
+
+
+static void mtk_seninf_input_setup_csi2(struct mtk_seninf_input *input,
+ struct v4l2_subdev_state *state)
+{
+ const struct mtk_seninf_format_info *fmtinfo;
+ const struct v4l2_mbus_framefmt *format;
+ unsigned int num_data_lanes = input->bus.num_data_lanes;
+ unsigned int val = 0;
+
+ format = v4l2_subdev_state_get_stream_format(state, input->pad, 0);
+ fmtinfo = mtk_seninf_format_info(format->code);
+
+ /* Configure timestamp */
+ writel(SENINF_TIMESTAMP_STEP, input->base + SENINF_TG1_TM_STP);
+
+ /* HQ */
+ writel(0x0, input->base + SENINF_TG1_PH_CNT);
+ writel(0x10001, input->base + SENINF_TG1_SEN_CK);
+
+ /* First Enable Sensor interface and select pad (0x1a04_0200) */
+ mtk_seninf_input_update(input, SENINF_CTRL, SENINF_EN, 1);
+ mtk_seninf_input_update(input, SENINF_CTRL, PAD2CAM_DATA_SEL, SENINF_PAD_10BIT);
+ mtk_seninf_input_update(input, SENINF_CTRL, SENINF_SRC_SEL, 0);
+ mtk_seninf_input_update(input, SENINF_CTRL_EXT, SENINF_CSI2_IP_EN, 1);
+ mtk_seninf_input_update(input, SENINF_CTRL_EXT, SENINF_NCSI2_IP_EN, 0);
+
+ /* DPCM Enable */
+ if (fmtinfo->flags & MTK_SENINF_FORMAT_DPCM)
+ val = SENINF_CSI2_DPCM_DI_2A_DPCM_EN;
+ else
+ val = SENINF_CSI2_DPCM_DI_30_DPCM_EN;
+ writel(val, input->base + SENINF_CSI2_DPCM);
+
+ /* Settle delay */
+ mtk_seninf_input_update(input, SENINF_CSI2_LNRD_TIMING,
+ DATA_SETTLE_PARAMETER, SENINF_SETTLE_DELAY);
+
+ /* HQ */
+ writel(0x10, input->base + SENINF_CSI2_LNRC_FSM);
+
+ /* CSI2 control */
+ val = readl(input->base + SENINF_CSI2_CTL)
+ | (FIELD_PREP(SENINF_CSI2_CTL_ED_SEL, DATA_HEADER_ORDER_DI_WCL_WCH)
+ | SENINF_CSI2_CTL_CLOCK_LANE_EN | (BIT(num_data_lanes) - 1));
+ writel(val, input->base + SENINF_CSI2_CTL);
+
+ mtk_seninf_input_update(input, SENINF_CSI2_RESYNC_MERGE_CTL,
+ BYPASS_LANE_RESYNC, 0);
+ mtk_seninf_input_update(input, SENINF_CSI2_RESYNC_MERGE_CTL, CDPHY_SEL, 0);
+ mtk_seninf_input_update(input, SENINF_CSI2_RESYNC_MERGE_CTL,
+ CPHY_LANE_RESYNC_CNT, 3);
+ mtk_seninf_input_update(input, SENINF_CSI2_MODE, CSR_CSI2_MODE, 0);
+ mtk_seninf_input_update(input, SENINF_CSI2_MODE, CSR_CSI2_HEADER_LEN, 0);
+ mtk_seninf_input_update(input, SENINF_CSI2_DPHY_SYNC, SYNC_SEQ_MASK_0, 0xff00);
+ mtk_seninf_input_update(input, SENINF_CSI2_DPHY_SYNC, SYNC_SEQ_PAT_0, 0x001d);
+
+ mtk_seninf_input_update(input, SENINF_CSI2_CTL, CLOCK_HS_OPTION, 0);
+ mtk_seninf_input_update(input, SENINF_CSI2_CTL, HSRX_DET_EN, 0);
+ mtk_seninf_input_update(input, SENINF_CSI2_CTL, HS_TRAIL_EN, 1);
+ mtk_seninf_input_update(input, SENINF_CSI2_HS_TRAIL, HS_TRAIL_PARAMETER,
+ SENINF_HS_TRAIL_PARAMETER);
+
+ /* Set debug port to output packet number */
+ mtk_seninf_input_update(input, SENINF_CSI2_DGB_SEL, DEBUG_EN, 1);
+ mtk_seninf_input_update(input, SENINF_CSI2_DGB_SEL, DEBUG_SEL, 0x1a);
+
+ /* HQ */
+ writel(0xfffffffe, input->base + SENINF_CSI2_SPARE0);
+
+ /* Enable CSI2 IRQ mask */
+ /* Turn on all interrupt */
+ writel(0xffffffff, input->base + SENINF_CSI2_INT_EN);
+ /* Write clear CSI2 IRQ */
+ writel(0xffffffff, input->base + SENINF_CSI2_INT_STATUS);
+ /* Enable CSI2 Extend IRQ mask */
+ /* Turn on all interrupt */
+ mtk_seninf_input_update(input, SENINF_CTRL, CSI2_SW_RST, 1);
+ udelay(1);
+ mtk_seninf_input_update(input, SENINF_CTRL, CSI2_SW_RST, 0);
+}
+
+static void mtk_seninf_mux_setup(struct mtk_seninf_mux *mux,
+ struct mtk_seninf_input *input,
+ struct v4l2_subdev_state *state)
+{
+ const struct mtk_seninf_format_info *fmtinfo;
+ const struct v4l2_mbus_framefmt *format;
+ unsigned int pix_sel_ext;
+ unsigned int pix_sel;
+ unsigned int hs_pol = 0;
+ unsigned int vs_pol = 0;
+ unsigned int val;
+ u32 rst_mask;
+
+ format = v4l2_subdev_state_get_stream_format(state, input->pad, 0);
+ fmtinfo = mtk_seninf_format_info(format->code);
+
+ /* Enable mux */
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_MUX_EN, 1);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_SRC_SEL, SENINF_MIPI_SENSOR);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL_EXT, SENINF_SRC_SEL_EXT, SENINF_NORMAL_MODEL);
+
+ pix_sel_ext = 0;
+ pix_sel = 1;
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL_EXT, SENINF_PIX_SEL_EXT, pix_sel_ext);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_PIX_SEL, pix_sel);
+
+ if (fmtinfo->flags & MTK_SENINF_FORMAT_JPEG) {
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FULL_WR_EN, 0);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FLUSH_EN,
+ FIFO_FLUSH_EN_JPEG_2_PIXEL_MODE);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_PUSH_EN,
+ FIFO_PUSH_EN_JPEG_2_PIXEL_MODE);
+ } else {
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FULL_WR_EN, 2);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FLUSH_EN,
+ FIFO_FLUSH_EN_NORMAL_MODE);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_PUSH_EN,
+ FIFO_PUSH_EN_NORMAL_MODE);
+ }
+
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_HSYNC_POL, hs_pol);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_VSYNC_POL, vs_pol);
+
+ val = mtk_seninf_mux_read(mux, SENINF_MUX_CTRL);
+ rst_mask = SENINF_MUX_CTRL_SENINF_IRQ_SW_RST | SENINF_MUX_CTRL_SENINF_MUX_SW_RST;
+
+ mtk_seninf_mux_write(mux, SENINF_MUX_CTRL, val | rst_mask);
+ mtk_seninf_mux_write(mux, SENINF_MUX_CTRL, val & ~rst_mask);
+
+ /* HQ */
+ mtk_seninf_mux_write(mux, SENINF_MUX_SPARE, 0xc2000);
+}
+
+static void mtk_seninf_top_mux_setup(struct mtk_seninf *priv,
+ enum mtk_seninf_id seninf_id,
+ struct mtk_seninf_mux *mux)
+{
+ unsigned int val;
+
+ /*
+ * Use the top mux (from SENINF input to MUX) to configure routing, and
+ * hardcode a 1:1 mapping from the MUX instances to the SENINF outputs.
+ */
+ val = readl(priv->base + SENINF_TOP_MUX_CTRL)
+ & ~(0xf << (mux->mux_id * 4));
+ val |= (seninf_id & 0xf) << (mux->mux_id * 4);
+ writel(val, priv->base + SENINF_TOP_MUX_CTRL);
+
+ writel(0x76541010, priv->base + SENINF_TOP_CAM_MUX_CTRL);
+}
+
+static void seninf_enable_test_pattern(struct mtk_seninf *priv,
+ struct v4l2_subdev_state *state)
+{
+ struct mtk_seninf_input *input = &priv->inputs[CSI_PORT_0];
+ struct mtk_seninf_mux *mux = &priv->muxes[0];
+ const struct mtk_seninf_format_info *fmtinfo;
+ const struct v4l2_mbus_framefmt *format;
+ unsigned int val;
+ unsigned int pix_sel_ext;
+ unsigned int pix_sel;
+ unsigned int hs_pol = 0;
+ unsigned int vs_pol = 0;
+ unsigned int seninf = 0;
+ unsigned int tm_size = 0;
+ unsigned int mux_id = mux->mux_id;
+
+ format = v4l2_subdev_state_get_stream_format(state, priv->conf->nb_inputs, 0);
+ fmtinfo = mtk_seninf_format_info(format->code);
+
+ mtk_seninf_update(priv, SENINF_TOP_CTRL, MUX_LP_MODE, 0);
+
+ mtk_seninf_update(priv, SENINF_TOP_CTRL, SENINF_PCLK_EN, 1);
+ mtk_seninf_update(priv, SENINF_TOP_CTRL, SENINF2_PCLK_EN, 1);
+
+ mtk_seninf_input_update(input, SENINF_CTRL, SENINF_EN, 1);
+ mtk_seninf_input_update(input, SENINF_CTRL, SENINF_SRC_SEL, 1);
+ mtk_seninf_input_update(input, SENINF_CTRL_EXT,
+ SENINF_TESTMDL_IP_EN, 1);
+
+ mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_EN, 1);
+ mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_PAT, 0xc);
+ mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_VSYNC, 4);
+ mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_DUMMYPXL, 0x28);
+
+ if (fmtinfo->flags & MTK_SENINF_FORMAT_BAYER)
+ mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_FMT, 0x0);
+ else
+ mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_FMT, 0x1);
+
+ tm_size = FIELD_PREP(SENINF_TG1_TM_SIZE_TM_LINE, format->height + 8);
+ switch (format->code) {
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_VYUY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YVYU8_1X16:
+ tm_size |= FIELD_PREP(SENINF_TG1_TM_SIZE_TM_PXL, format->width * 2);
+ break;
+ default:
+ tm_size |= FIELD_PREP(SENINF_TG1_TM_SIZE_TM_PXL, format->width);
+ break;
+ }
+ writel(tm_size, input->base + SENINF_TG1_TM_SIZE);
+
+ writel(TEST_MODEL_CLK_DIVIDED_CNT, input->base + SENINF_TG1_TM_CLK);
+ writel(TIME_STAMP_DIVIDER, input->base + SENINF_TG1_TM_STP);
+
+ /* Set top mux */
+ val = (readl(priv->base + SENINF_TOP_MUX_CTRL) & (~(0xf << (mux_id * 4)))) |
+ ((seninf & 0xf) << (mux_id * 4));
+ writel(val, priv->base + SENINF_TOP_MUX_CTRL);
+
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_MUX_EN, 1);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL_EXT,
+ SENINF_SRC_SEL_EXT, SENINF_TEST_MODEL);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_SRC_SEL, 1);
+
+ pix_sel_ext = 0;
+ pix_sel = 1;
+
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL_EXT,
+ SENINF_PIX_SEL_EXT, pix_sel_ext);
+
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_PIX_SEL, pix_sel);
+
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_PUSH_EN, 0x1f);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FLUSH_EN, 0x1b);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FULL_WR_EN, 2);
+
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_HSYNC_POL, hs_pol);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_VSYNC_POL, vs_pol);
+ mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_HSYNC_MASK, 1);
+
+ mtk_seninf_mux_write(mux, SENINF_MUX_INTEN,
+ SENINF_IRQ_CLR_SEL | SENINF_ALL_ERR_IRQ_EN);
+
+ mtk_seninf_mux_write(mux, SENINF_MUX_CTRL,
+ mtk_seninf_mux_read(mux, SENINF_MUX_CTRL) |
+ SENINF_MUX_CTRL_SENINF_IRQ_SW_RST |
+ SENINF_MUX_CTRL_SENINF_MUX_SW_RST);
+ udelay(1);
+ mtk_seninf_mux_write(mux, SENINF_MUX_CTRL,
+ mtk_seninf_mux_read(mux, SENINF_MUX_CTRL) &
+ ~(SENINF_MUX_CTRL_SENINF_IRQ_SW_RST |
+ SENINF_MUX_CTRL_SENINF_MUX_SW_RST));
+
+ //check this
+ writel(0x76540010, priv->base + SENINF_TOP_CAM_MUX_CTRL);
+
+ dev_dbg(priv->dev, "%s: OK\n", __func__);
+}
+