From patchwork Thu May 27 07:54:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Kepplinger X-Patchwork-Id: 74633 Received: from vger.kernel.org ([23.128.96.18]) by www.linuxtv.org with esmtp (Exim 4.92) (envelope-from ) id 1lmArE-006Gwb-Tt; Thu, 27 May 2021 07:55:09 +0000 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235103AbhE0H4j (ORCPT + 1 other); Thu, 27 May 2021 03:56:39 -0400 Received: from comms.puri.sm ([159.203.221.185]:53450 "EHLO comms.puri.sm" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235111AbhE0H4S (ORCPT ); Thu, 27 May 2021 03:56:18 -0400 Received: from localhost (localhost [127.0.0.1]) by comms.puri.sm (Postfix) with ESMTP id 56AE0E204E; Thu, 27 May 2021 00:54:46 -0700 (PDT) Received: from comms.puri.sm ([127.0.0.1]) by localhost (comms.puri.sm [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id AvVzDm61ahfv; Thu, 27 May 2021 00:54:45 -0700 (PDT) From: Martin Kepplinger To: laurent.pinchart@ideasonboard.com, mchehab@kernel.org, robh@kernel.org Cc: shawnguo@kernel.org, kernel@pengutronix.de, festevam@gmail.com, linux-imx@nxp.com, slongerbeam@gmail.com, kernel@puri.sm, krzk@kernel.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-staging@lists.linux.dev, Martin Kepplinger Subject: [PATCH v1 1/3] dt-bindings: media: document the nxp,imx8mq-mipi-csi2 receiver phy and controller Date: Thu, 27 May 2021 09:54:05 +0200 Message-Id: <20210527075407.3180744-2-martin.kepplinger@puri.sm> In-Reply-To: <20210527075407.3180744-1-martin.kepplinger@puri.sm> References: <20210527075407.3180744-1-martin.kepplinger@puri.sm> Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-LSpam-Score: -3.1 (---) X-LSpam-Report: No, score=-3.1 required=5.0 tests=BAYES_00=-1.9,HEADER_FROM_DIFFERENT_DOMAINS=0.5,MAILING_LIST_MULTI=-1,RCVD_IN_DNSWL_LOW=-0.7 autolearn=ham autolearn_force=no The i.MX8MQ SoC integrates a different MIPI CSI receiver as the i.MX8MM so describe the DT bindings for it. Signed-off-by: Martin Kepplinger --- .../bindings/media/nxp,imx8mq-mipi-csi2.yaml | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml diff --git a/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml new file mode 100644 index 000000000000..823609ac4cf5 --- /dev/null +++ b/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml @@ -0,0 +1,163 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/nxp,imx8mq-mipi-csi2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP and i.MX8MQ MIPI CSI-2 receiver + +maintainers: + - Martin Kepplinger + +description: |- + This binding covers the CSI-2 RX PHY and host controller included in the + NXP i.MX8MQ SoC. It handles the sensor/image input and process for all the + input imaging devices. + +properties: + compatible: + enum: + - fsl,imx8mq-mipi-csi2 + + reg: + maxItems: 1 + + clocks: + minItems: 4 + + clock-names: + minItems: 4 + items: + - const: core + - const: esc + - const: pxl + - const: clko2 + + assigned-clocks: + maxItems: 3 + + assigned-clock-rates: + maxItems: 3 + + assigned-clock-parents: + maxItems: 3 + + power-domains: + maxItems: 1 + + phy-reset: + description: + The phandle to the imx8mq reset-controller. + maxItems: 1 + + phy-gpr: + description: + The phandle to the imx8mq syscon iomux-gpr. + maxItems: 1 + + interconnects: + maxItems: 1 + + interconnect-names: + const: dram + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Input port node, single endpoint describing the CSI-2 transmitter. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + items: + minItems: 1 + maxItems: 4 + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + required: + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + Output port node + +required: + - compatible + - reg + - clocks + - clock-names + - assigned-clocks + - assigned-clock-rates + - assigned-clock-parents + - power-domains + - phy-reset + - phy-gpr + - ports + +additionalProperties: false + +examples: + - | + #include + #include + + mipi_csi1@30a70000 { + compatible = "fsl,imx8mq-mipi-csi2"; + reg = <0x30a70000 0x1000>; /* MIPI CSI1 Controller base addr */ + clocks = <&clk IMX8MQ_CLK_CSI1_CORE>, + <&clk IMX8MQ_CLK_CSI1_ESC>, + <&clk IMX8MQ_CLK_CSI1_PHY_REF>, + <&clk IMX8MQ_CLK_CLKO2>; + clock-names = "core", "esc", "pxl", "clko2"; + assigned-clocks = <&clk IMX8MQ_CLK_CSI1_CORE>, + <&clk IMX8MQ_CLK_CSI1_PHY_REF>, + <&clk IMX8MQ_CLK_CSI1_ESC>; + assigned-clock-rates = <266000000>, <200000000>, <66000000>; + assigned-clock-parents = <&clk IMX8MQ_SYS1_PLL_266M>, + <&clk IMX8MQ_SYS2_PLL_1000M>, + <&clk IMX8MQ_SYS1_PLL_800M>; + power-domains = <&pgc_mipi_csi1>; + phy-reset = <&src>; + phy-gpr = <&iomuxc_gpr>; + interconnects = <&noc IMX8MQ_ICM_CSI1 &noc IMX8MQ_ICS_DRAM>; + interconnect-names = "dram"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + imx8mm_mipi_csi_in: endpoint { + remote-endpoint = <&imx477_out>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + + imx8mm_mipi_csi_out: endpoint { + remote-endpoint = <&csi_in>; + }; + }; + }; + }; + +... From patchwork Thu May 27 07:54:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Kepplinger X-Patchwork-Id: 74635 Received: from vger.kernel.org ([23.128.96.18]) by www.linuxtv.org with esmtp (Exim 4.92) (envelope-from ) id 1lmArt-006Gya-3J; Thu, 27 May 2021 07:55:50 +0000 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235205AbhE0H5T (ORCPT + 1 other); Thu, 27 May 2021 03:57:19 -0400 Received: from comms.puri.sm ([159.203.221.185]:53632 "EHLO comms.puri.sm" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235229AbhE0H4y (ORCPT ); Thu, 27 May 2021 03:56:54 -0400 Received: from localhost (localhost [127.0.0.1]) by comms.puri.sm (Postfix) with ESMTP id CCF0DE2060; Thu, 27 May 2021 00:54:51 -0700 (PDT) Received: from comms.puri.sm ([127.0.0.1]) by localhost (comms.puri.sm [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 74fKleT2MbEx; Thu, 27 May 2021 00:54:49 -0700 (PDT) From: Martin Kepplinger To: laurent.pinchart@ideasonboard.com, mchehab@kernel.org, robh@kernel.org Cc: shawnguo@kernel.org, kernel@pengutronix.de, festevam@gmail.com, linux-imx@nxp.com, slongerbeam@gmail.com, kernel@puri.sm, krzk@kernel.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-staging@lists.linux.dev, Martin Kepplinger Subject: [PATCH v1 2/3] media: imx: add a driver for i.MX8MQ mipi csi rx phy and controller Date: Thu, 27 May 2021 09:54:06 +0200 Message-Id: <20210527075407.3180744-3-martin.kepplinger@puri.sm> In-Reply-To: <20210527075407.3180744-1-martin.kepplinger@puri.sm> References: <20210527075407.3180744-1-martin.kepplinger@puri.sm> Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-LSpam-Score: -3.1 (---) X-LSpam-Report: No, score=-3.1 required=5.0 tests=BAYES_00=-1.9,HEADER_FROM_DIFFERENT_DOMAINS=0.5,MAILING_LIST_MULTI=-1,RCVD_IN_DNSWL_LOW=-0.7 autolearn=ham autolearn_force=no The i.MX8MQ SoC includes a different MIPI CSI receiver than i.MX8MM so add a driver to support it. Signed-off-by: Martin Kepplinger --- drivers/staging/media/imx/Makefile | 1 + drivers/staging/media/imx/imx8mq-mipi-csi2.c | 1023 ++++++++++++++++++ 2 files changed, 1024 insertions(+) create mode 100644 drivers/staging/media/imx/imx8mq-mipi-csi2.c diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile index 6ac33275cc97..19c2fc54d424 100644 --- a/drivers/staging/media/imx/Makefile +++ b/drivers/staging/media/imx/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-mipi-csis.o +obj-$(CONFIG_VIDEO_IMX7_CSI) += imx8mq-mipi-csi2.o diff --git a/drivers/staging/media/imx/imx8mq-mipi-csi2.c b/drivers/staging/media/imx/imx8mq-mipi-csi2.c new file mode 100644 index 000000000000..67b99be3e804 --- /dev/null +++ b/drivers/staging/media/imx/imx8mq-mipi-csi2.c @@ -0,0 +1,1023 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Freescale i.MX8MQ SoC series MIPI-CSI receiver driver + * + * Copyright (C) 2021 Purism SPC + * Copyright (C) 2019 Linaro Ltd + * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define MIPI_CSI2_DRIVER_NAME "imx8mq-mipi-csi2" +#define MIPI_CSI2_SUBDEV_NAME MIPI_CSI2_DRIVER_NAME + +#define MIPI_CSI2_PAD_SINK 0 +#define MIPI_CSI2_PAD_SOURCE 1 +#define MIPI_CSI2_PADS_NUM 2 + +#define MIPI_CSI2_DEF_PIX_WIDTH 640 +#define MIPI_CSI2_DEF_PIX_HEIGHT 480 + +#define MIPI_CSI2_RX_CSR_1_BASE_ADDR 0x30A70000 +#define MIPI_CSI2_RX_CSR_1_GPR_REG 0x88 +#define MIPI_CSI2_RX_CSR_1_HW_RESET_REG 0x4C +#define MIPI_CSI2_RX_CSR_1_HW_RESET_VAL 7 +#define MIPI_CSI2_RX_CSR_2_BASE_ADDR 0x30B60000 +#define MIPI_CSI2_RX_CSR_2_GPR_REG 0xA4 +#define MIPI_CSI2_RX_CSR_2_HW_RESET_REG 0x50 +#define MIPI_CSI2_RX_CSR_2_HW_RESET_VAL 7 + +/* Register map definition */ + +/* i.MX8MQ CSI-2 controller CSR */ +#define CSI2RX_CFG_NUM_LANES 0x100 +#define CSI2RX_CFG_DISABLE_DATA_LANES 0x104 +#define CSI2RX_BIT_ERR 0x108 +#define CSI2RX_IRQ_STATUS 0x10C +#define CSI2RX_IRQ_MASK 0x110 +#define CSI2RX_ULPS_STATUS 0x114 +#define CSI2RX_PPI_ERRSOT_HS 0x118 +#define CSI2RX_PPI_ERRSOTSYNC_HS 0x11C +#define CSI2RX_PPI_ERRESC 0x120 +#define CSI2RX_PPI_ERRSYNCESC 0x124 +#define CSI2RX_PPI_ERRCONTROL 0x128 +#define CSI2RX_CFG_DISABLE_PAYLOAD_0 0x12C +#define CSI2RX_CFG_DISABLE_PAYLOAD_1 0x130 + +enum { + ST_POWERED = 1, + ST_STREAMING = 2, + ST_SUSPENDED = 4, +}; + +static const char * const mipi_csi2_clk_id[] = { + "core", + "esc", + "pxl", + "clko2", +}; + +#define GPR_CSI2_1_RX_ENABLE BIT(13) +#define GPR_CSI2_1_VID_INTFC_ENB BIT(12) +#define GPR_CSI2_1_HSEL BIT(10) +#define GPR_CSI2_1_CONT_CLK_MODE BIT(8) +#define GPR_CSI2_1_S_PRG_RXHS_SETTLE(x) (((x) & 0x3F) << 2) + +struct csi_state { + struct device *dev; + void __iomem *regs; + struct clk_bulk_data *clks; + struct reset_control *mrst; + struct regulator *mipi_phy_regulator; + u8 index; + + struct v4l2_subdev sd; + struct media_pad pads[MIPI_CSI2_PADS_NUM]; + struct v4l2_async_notifier notifier; + struct v4l2_subdev *src_sd; + + struct v4l2_fwnode_bus_mipi_csi2 bus; + u32 hs_settle; + u32 clk_settle; + + struct mutex lock; /* Protect csi2_fmt, format_mbus and state */ + const struct csi2_pix_format *csi2_fmt; + struct v4l2_mbus_framefmt format_mbus; + u32 state; + + struct regmap *hw_reset; + u8 hw_reset_reg; + + struct regmap *phy_gpr; + u8 phy_gpr_reg; + + u32 send_level; + + struct icc_path *icc_path; + s32 icc_path_bw; +}; + +/* ----------------------------------------------------------------------------- + * Format helpers + */ + +struct csi2_pix_format { + u32 code; + u8 width; +}; + +static const struct csi2_pix_format mipi_csi2_formats[] = { + /* RAW (Bayer and greyscale) formats. */ + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .width = 8, + }, { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .width = 8, + }, { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .width = 8, + }, { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .width = 8, + }, { + .code = MEDIA_BUS_FMT_Y8_1X8, + .width = 8, + }, { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .width = 10, + }, { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 10, + }, { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .width = 10, + }, { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .width = 10, + }, { + .code = MEDIA_BUS_FMT_Y10_1X10, + .width = 10, + }, { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .width = 12, + }, { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .width = 12, + }, { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .width = 12, + }, { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .width = 12, + }, { + .code = MEDIA_BUS_FMT_Y12_1X12, + .width = 12, + }, { + .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .width = 14, + }, { + .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .width = 14, + }, { + .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .width = 14, + }, { + .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .width = 14, + } +}; + +static const struct csi2_pix_format *find_csi2_format(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mipi_csi2_formats); i++) + if (code == mipi_csi2_formats[i].code) + return &mipi_csi2_formats[i]; + return NULL; +} + +/* ----------------------------------------------------------------------------- + * Hardware configuration + */ + +static inline u32 mipi_csi2_read(struct csi_state *state, u32 reg) +{ + return readl(state->regs + reg); +} + +static inline void mipi_csi2_write(struct csi_state *state, u32 reg, u32 val) +{ + writel(val, state->regs + reg); +} + +static void mipi_csi2_sw_reset(struct csi_state *state) +{ + struct device *dev = state->dev; + struct device_node *np = dev->of_node; + struct device_node *node; + phandle phandle; + int ret; + + dev_dbg(dev, "%s: starting\n", __func__); + + ret = of_property_read_u32(np, "phy-reset", &phandle); + if (ret) { + dev_info(dev, "no csis-hw-reset property found: %d\n", ret); + return; + } + + node = of_find_node_by_phandle(phandle); + if (!node) { + ret = PTR_ERR(node); + dev_dbg(dev, "not find src node by phandle: %d\n", ret); + } + state->hw_reset = syscon_node_to_regmap(node); + if (IS_ERR(state->hw_reset)) { + ret = PTR_ERR(state->hw_reset); + dev_err(dev, "failed to get src regmap: %d\n", ret); + } + of_node_put(node); + if (ret < 0) + return; + + /* reset imx8mq mipi phy */ + regmap_update_bits(state->hw_reset, state->hw_reset_reg, 7, 7); + msleep(20); +} + +static void mipi_csi2_system_enable(struct csi_state *state, int on) +{ + struct device *dev = state->dev; + struct device_node *np = dev->of_node; + struct device_node *node; + phandle phandle; + int ret; + + if (!on) { + /* Disable Data lanes */ + mipi_csi2_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, 0xf); + return; + } + + ret = of_property_read_u32(np, "phy-gpr", &phandle); + if (ret) { + dev_info(dev, "no phy-gpr property found\n"); + return; + } + + node = of_find_node_by_phandle(phandle); + if (!node) { + dev_dbg(dev, "not find gpr node by phandle\n"); + ret = PTR_ERR(node); + } + state->phy_gpr = syscon_node_to_regmap(node); + if (IS_ERR(state->phy_gpr)) { + dev_err(dev, "failed to get gpr regmap\n"); + ret = PTR_ERR(state->phy_gpr); + } + of_node_put(node); + if (ret < 0) + return; + + regmap_update_bits(state->phy_gpr, + state->phy_gpr_reg, + 0x3FFF, + GPR_CSI2_1_RX_ENABLE | + GPR_CSI2_1_VID_INTFC_ENB | + GPR_CSI2_1_HSEL | + GPR_CSI2_1_CONT_CLK_MODE | + GPR_CSI2_1_S_PRG_RXHS_SETTLE(state->hs_settle)); + + dev_dbg(dev, "%s: hs_settle: 0x%X\n", __func__, state->hs_settle); +} + +static void mipi_csi2_set_params(struct csi_state *state) +{ + int lanes = state->bus.num_data_lanes; + u32 val = 0; + int i; + + /* Lanes */ + mipi_csi2_write(state, CSI2RX_CFG_NUM_LANES, lanes - 1); + + for (i = 0; i < lanes; i++) + val |= (1 << i); + + val = 0xF & ~val; + mipi_csi2_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, val); + + dev_dbg(state->dev, "imx8mq: CSI2RX_CFG_DISABLE_DATA_LANES: 0x%X\n", val); + + /* Mask interrupt */ + // Don't let ULPS (ultra-low power status) interrupts flood + mipi_csi2_write(state, CSI2RX_IRQ_MASK, 0x1ff); + + mipi_csi2_write(state, 0x180, 1); + /* vid_vc */ + mipi_csi2_write(state, 0x184, 1); + mipi_csi2_write(state, 0x188, state->send_level); +} + +static int mipi_csi2_clk_enable(struct csi_state *state) +{ + return clk_bulk_prepare_enable(ARRAY_SIZE(mipi_csi2_clk_id), state->clks); +} + +static void mipi_csi2_clk_disable(struct csi_state *state) +{ + clk_bulk_disable_unprepare(ARRAY_SIZE(mipi_csi2_clk_id), state->clks); +} + +static int mipi_csi2_clk_get(struct csi_state *state) +{ + unsigned int i; + int ret; + + state->clks = devm_kcalloc(state->dev, ARRAY_SIZE(mipi_csi2_clk_id), + sizeof(*state->clks), GFP_KERNEL); + + if (!state->clks) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(mipi_csi2_clk_id); i++) + state->clks[i].id = mipi_csi2_clk_id[i]; + + ret = devm_clk_bulk_get(state->dev, ARRAY_SIZE(mipi_csi2_clk_id), + state->clks); + return ret; +} + +static void mipi_csi2_start_stream(struct csi_state *state) +{ + mipi_csi2_sw_reset(state); + mipi_csi2_set_params(state); + mipi_csi2_system_enable(state, true); +} + +static void mipi_csi2_stop_stream(struct csi_state *state) +{ + mipi_csi2_system_enable(state, false); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +static struct csi_state *mipi_sd_to_csi2_state(struct v4l2_subdev *sdev) +{ + return container_of(sdev, struct csi_state, sd); +} + +static int mipi_csi2_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct csi_state *state = mipi_sd_to_csi2_state(sd); + int ret; + + mipi_csi2_write(state, CSI2RX_IRQ_MASK, 0x008); + + dev_dbg(state->dev, "%s: enable: %d\n", __func__, enable); + + if (enable) { + ret = pm_runtime_get_sync(state->dev); + if (ret < 0) { + pm_runtime_put_noidle(state->dev); + return ret; + } + ret = v4l2_subdev_call(state->src_sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto done; + } + + mutex_lock(&state->lock); + + if (enable) { + if (state->state & ST_SUSPENDED) { + ret = -EBUSY; + goto unlock; + } + + mipi_csi2_start_stream(state); + ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1); + if (ret < 0) + goto unlock; + + state->state |= ST_STREAMING; + } else { + v4l2_subdev_call(state->src_sd, video, s_stream, 0); + ret = v4l2_subdev_call(state->src_sd, core, s_power, 0); + if (ret == -ENOIOCTLCMD) + ret = 0; + mipi_csi2_stop_stream(state); + state->state &= ~ST_STREAMING; + } + +unlock: + mutex_unlock(&state->lock); + +done: + if (!enable || ret < 0) + pm_runtime_put(state->dev); + + return ret; +} + +static struct v4l2_mbus_framefmt * +mipi_csi2_get_format(struct csi_state *state, + struct v4l2_subdev_pad_config *cfg, + enum v4l2_subdev_format_whence which, + unsigned int pad) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&state->sd, cfg, pad); + + return &state->format_mbus; +} + +static int mipi_csi2_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct csi_state *state = mipi_sd_to_csi2_state(sd); + struct v4l2_mbus_framefmt *fmt_sink; + struct v4l2_mbus_framefmt *fmt_source; + enum v4l2_subdev_format_whence which; + + which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_sink = mipi_csi2_get_format(state, cfg, which, MIPI_CSI2_PAD_SINK); + + fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10; + fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH; + fmt_sink->height = MIPI_CSI2_DEF_PIX_HEIGHT; + fmt_sink->field = V4L2_FIELD_NONE; + + fmt_sink->colorspace = V4L2_COLORSPACE_RAW; + fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace); + fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace); + fmt_sink->quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace, + fmt_sink->ycbcr_enc); + + /* + * When called from mipi_csi2_subdev_init() to initialize the active + * configuration, cfg is NULL, which indicates there's no source pad + * configuration to set. + */ + if (!cfg) + return 0; + + fmt_source = mipi_csi2_get_format(state, cfg, which, MIPI_CSI2_PAD_SOURCE); + *fmt_source = *fmt_sink; + + return 0; +} + +static int mipi_csi2_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct csi_state *state = mipi_sd_to_csi2_state(sd); + struct v4l2_mbus_framefmt *fmt; + + fmt = mipi_csi2_get_format(state, cfg, sdformat->which, sdformat->pad); + + mutex_lock(&state->lock); + sdformat->format = *fmt; + mutex_unlock(&state->lock); + + return 0; +} + +static int mipi_csi2_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct csi_state *state = mipi_sd_to_csi2_state(sd); + + /* + * We can't transcode in any way, the source format is identical + * to the sink format. + */ + if (code->pad == MIPI_CSI2_PAD_SOURCE) { + struct v4l2_mbus_framefmt *fmt; + + if (code->index > 0) + return -EINVAL; + + fmt = mipi_csi2_get_format(state, cfg, code->which, code->pad); + code->code = fmt->code; + return 0; + } + + if (code->pad != MIPI_CSI2_PAD_SINK) + return -EINVAL; + + if (code->index >= ARRAY_SIZE(mipi_csi2_formats)) + return -EINVAL; + + code->code = mipi_csi2_formats[code->index].code; + + return 0; +} + +static int mipi_csi2_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct csi_state *state = mipi_sd_to_csi2_state(sd); + struct csi2_pix_format const *csi2_fmt; + struct v4l2_mbus_framefmt *fmt; + + /* + * The device can't transcode in any way, the source format can't be + * modified. + */ + if (sdformat->pad == MIPI_CSI2_PAD_SOURCE) + return mipi_csi2_get_fmt(sd, cfg, sdformat); + + if (sdformat->pad != MIPI_CSI2_PAD_SINK) + return -EINVAL; + + csi2_fmt = find_csi2_format(sdformat->format.code); + if (!csi2_fmt) { + dev_err(state->dev, "no format found based on code %d\n", + sdformat->format.code); + csi2_fmt = &mipi_csi2_formats[0]; + } + + fmt = mipi_csi2_get_format(state, cfg, sdformat->which, sdformat->pad); + + mutex_lock(&state->lock); + + fmt->code = csi2_fmt->code; + fmt->width = sdformat->format.width; + fmt->height = sdformat->format.height; + + sdformat->format = *fmt; + + /* Propagate the format from sink to source. */ + fmt = mipi_csi2_get_format(state, cfg, sdformat->which, + MIPI_CSI2_PAD_SOURCE); + *fmt = sdformat->format; + + /* Store the CSI2 format descriptor for active formats. */ + if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) + state->csi2_fmt = csi2_fmt; + + mutex_unlock(&state->lock); + + /* https://community.nxp.com/t5/i-MX-Processors/Explenation-for-HS-SETTLE-parameter-in-MIPI-CSI-D-PHY-registers/m-p/764275/highlight/true#M118744 */ + if (sdformat->format.width * sdformat->format.height > 720 * 480) + state->hs_settle = 0x9; + else + state->hs_settle = 0x14; + + state->send_level = 64; + + dev_dbg(state->dev, + "%s: format %dx%d send_level %d hs_settle 0x%X\n", __func__, + sdformat->format.width, sdformat->format.height, + state->send_level, state->hs_settle); + + return 0; +} + +static int mipi_csi2_log_status(struct v4l2_subdev *sd) +{ + struct csi_state *state = mipi_sd_to_csi2_state(sd); + + mutex_lock(&state->lock); + mutex_unlock(&state->lock); + + return 0; +} + +static const struct v4l2_subdev_core_ops mipi_csi2_core_ops = { + .log_status = mipi_csi2_log_status, +}; + +static const struct v4l2_subdev_video_ops mipi_csi2_video_ops = { + .s_stream = mipi_csi2_s_stream, +}; + +static const struct v4l2_subdev_pad_ops mipi_csi2_pad_ops = { + .init_cfg = mipi_csi2_init_cfg, + .enum_mbus_code = mipi_csi2_enum_mbus_code, + .get_fmt = mipi_csi2_get_fmt, + .set_fmt = mipi_csi2_set_fmt, +}; + +static const struct v4l2_subdev_ops mipi_csi2_subdev_ops = { + .core = &mipi_csi2_core_ops, + .video = &mipi_csi2_video_ops, + .pad = &mipi_csi2_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +static int mipi_csi2_link_setup(struct media_entity *entity, + const struct media_pad *local_pad, + const struct media_pad *remote_pad, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct csi_state *state = mipi_sd_to_csi2_state(sd); + struct v4l2_subdev *remote_sd; + + dev_dbg(state->dev, "link setup %s -> %s", remote_pad->entity->name, + local_pad->entity->name); + + /* We only care about the link to the source. */ + if (!(local_pad->flags & MEDIA_PAD_FL_SINK)) + return 0; + + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + if (flags & MEDIA_LNK_FL_ENABLED) { + if (state->src_sd) + return -EBUSY; + + state->src_sd = remote_sd; + } else { + state->src_sd = NULL; + } + + return 0; +} + +static const struct media_entity_operations mipi_csi2_entity_ops = { + .link_setup = mipi_csi2_link_setup, + .link_validate = v4l2_subdev_link_validate, + .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, +}; + +/* ----------------------------------------------------------------------------- + * Async subdev notifier + */ + +static struct csi_state * +mipi_notifier_to_csi2_state(struct v4l2_async_notifier *n) +{ + return container_of(n, struct csi_state, notifier); +} + +static int mipi_csi2_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct csi_state *state = mipi_notifier_to_csi2_state(notifier); + struct media_pad *sink = &state->sd.entity.pads[MIPI_CSI2_PAD_SINK]; + + return v4l2_create_fwnode_links_to_pad(sd, sink, 0); +} + +static const struct v4l2_async_notifier_operations mipi_csi2_notify_ops = { + .bound = mipi_csi2_notify_bound, +}; + +static int mipi_csi2_async_register(struct csi_state *state) +{ + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct v4l2_async_subdev *asd; + struct fwnode_handle *ep; + unsigned int i; + int ret; + + v4l2_async_notifier_init(&state->notifier); + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) + return -ENOTCONN; + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) + goto err_parse; + + for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) { + if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) { + dev_err(state->dev, + "data lanes reordering is not supported"); + goto err_parse; + } + } + + state->bus = vep.bus.mipi_csi2; + + dev_dbg(state->dev, "data lanes: %d\n", state->bus.num_data_lanes); + dev_dbg(state->dev, "flags: 0x%08x\n", state->bus.flags); + + asd = v4l2_async_notifier_add_fwnode_remote_subdev(&state->notifier, + ep, struct v4l2_async_subdev); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + goto err_parse; + } + + fwnode_handle_put(ep); + + state->notifier.ops = &mipi_csi2_notify_ops; + + ret = v4l2_async_subdev_notifier_register(&state->sd, &state->notifier); + if (ret) + return ret; + + return v4l2_async_register_subdev(&state->sd); + +err_parse: + fwnode_handle_put(ep); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Suspend/resume + */ + +static int mipi_csi2_pm_suspend(struct device *dev, bool runtime) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct csi_state *state = mipi_sd_to_csi2_state(sd); + int ret = 0; + + mutex_lock(&state->lock); + if (state->state & ST_POWERED) { + mipi_csi2_stop_stream(state); + mipi_csi2_clk_disable(state); + state->state &= ~ST_POWERED; + if (!runtime) + state->state |= ST_SUSPENDED; + } + + mutex_unlock(&state->lock); + + ret = icc_set_bw(state->icc_path, 0, 0); + if (ret) + dev_err(dev, "icc_set_bw failed with %d\n", ret); + + return ret ? -EAGAIN : 0; +} + +static int mipi_csi2_pm_resume(struct device *dev, bool runtime) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct csi_state *state = mipi_sd_to_csi2_state(sd); + int ret = 0; + + ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw); + if (ret) { + dev_err(dev, "icc_set_bw failed with %d\n", ret); + return ret; + } + + mutex_lock(&state->lock); + if (!runtime && !(state->state & ST_SUSPENDED)) + goto unlock; + + if (!(state->state & ST_POWERED)) { + state->state |= ST_POWERED; + ret = mipi_csi2_clk_enable(state); + } + if (state->state & ST_STREAMING) + mipi_csi2_start_stream(state); + + state->state &= ~ST_SUSPENDED; + +unlock: + mutex_unlock(&state->lock); + + return ret ? -EAGAIN : 0; +} + +static int __maybe_unused mipi_csi2_suspend(struct device *dev) +{ + return mipi_csi2_pm_suspend(dev, false); +} + +static int __maybe_unused mipi_csi2_resume(struct device *dev) +{ + return mipi_csi2_pm_resume(dev, false); +} + +static int __maybe_unused mipi_csi2_runtime_suspend(struct device *dev) +{ + return mipi_csi2_pm_suspend(dev, true); +} + +static int __maybe_unused mipi_csi2_runtime_resume(struct device *dev) +{ + return mipi_csi2_pm_resume(dev, true); +} + +static const struct dev_pm_ops mipi_csi2_pm_ops = { + SET_RUNTIME_PM_OPS(mipi_csi2_runtime_suspend, mipi_csi2_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(mipi_csi2_suspend, mipi_csi2_resume) +}; + +/* ----------------------------------------------------------------------------- + * Probe/remove & platform driver + */ + +static int mipi_csi2_subdev_init(struct csi_state *state) +{ + struct v4l2_subdev *sd = &state->sd; + + v4l2_subdev_init(sd, &mipi_csi2_subdev_ops); + sd->owner = THIS_MODULE; + snprintf(sd->name, sizeof(sd->name), "%s.%d", + MIPI_CSI2_SUBDEV_NAME, state->index); + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->ctrl_handler = NULL; + + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->entity.ops = &mipi_csi2_entity_ops; + + sd->dev = state->dev; + + state->csi2_fmt = &mipi_csi2_formats[0]; + mipi_csi2_init_cfg(sd, NULL); + + state->pads[MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; + state->pads[MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE + | MEDIA_PAD_FL_MUST_CONNECT; + return media_entity_pads_init(&sd->entity, MIPI_CSI2_PADS_NUM, + state->pads); +} + +static int init_icc(struct platform_device *pdev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev); + struct csi_state *state = mipi_sd_to_csi2_state(sd); + int ret; + + /* Optional interconnect request */ + state->icc_path = of_icc_get(&pdev->dev, "dram"); + if (IS_ERR(state->icc_path)) { + ret = PTR_ERR(state->icc_path); + if (ret == -EPROBE_DEFER) + return ret; + + state->icc_path = NULL; + return 0; + } + + state->icc_path_bw = MBps_to_icc(700); + + ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw); + if (ret) { + dev_err(&pdev->dev, "icc_set_bw failed with %d\n", ret); + return ret; + } + + return 0; +} + +static int mipi_csi2_parse_dt(struct csi_state *state) +{ + struct device_node *node = state->dev->of_node; + const u32 *addr; + int len; + int reg; + + addr = of_get_property(node, "reg", &len); + if (!addr) { + dev_err(state->dev, "no reg property found\n"); + return -EINVAL; + } + + reg = be32_to_cpup(addr); + + if ((reg >> 16) == MIPI_CSI2_RX_CSR_1_BASE_ADDR >> 16) { + state->phy_gpr_reg = MIPI_CSI2_RX_CSR_1_GPR_REG; + state->hw_reset_reg = MIPI_CSI2_RX_CSR_1_HW_RESET_REG; + } else if ((reg >> 16) == MIPI_CSI2_RX_CSR_2_BASE_ADDR >> 16) { + state->phy_gpr_reg = MIPI_CSI2_RX_CSR_2_GPR_REG; + state->hw_reset_reg = MIPI_CSI2_RX_CSR_2_HW_RESET_REG; + } + + return 0; +} + +static int mipi_csi2_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct csi_state *state; + int ret; + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + mutex_init(&state->lock); + + state->dev = dev; + + ret = mipi_csi2_parse_dt(state); + if (ret < 0) { + dev_err(dev, "Failed to parse device tree: %d\n", ret); + return ret; + } + + /* Acquire resources. */ + state->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(state->regs)) + return PTR_ERR(state->regs); + + ret = mipi_csi2_clk_get(state); + if (ret < 0) + return ret; + + /* Initialize and register the subdev. */ + ret = mipi_csi2_subdev_init(state); + if (ret < 0) + goto disable_clock; + + platform_set_drvdata(pdev, &state->sd); + + ret = mipi_csi2_async_register(state); + if (ret < 0) { + dev_err(dev, "async register failed: %d\n", ret); + goto cleanup; + } + + ret = init_icc(pdev); + if (ret) + goto cleanup; + + /* Enable runtime PM. */ + pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = mipi_csi2_pm_resume(dev, true); + if (ret < 0) + goto cleanup; + } + + dev_info(dev, "lanes: %d\n", state->bus.num_data_lanes); + + return 0; + +cleanup: + media_entity_cleanup(&state->sd.entity); + v4l2_async_notifier_unregister(&state->notifier); + v4l2_async_notifier_cleanup(&state->notifier); + v4l2_async_unregister_subdev(&state->sd); +disable_clock: + mipi_csi2_clk_disable(state); + mutex_destroy(&state->lock); + + return ret; +} + +static int mipi_csi2_remove(struct platform_device *pdev) +{ + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct csi_state *state = mipi_sd_to_csi2_state(sd); + + v4l2_async_notifier_unregister(&state->notifier); + v4l2_async_notifier_cleanup(&state->notifier); + v4l2_async_unregister_subdev(&state->sd); + + pm_runtime_disable(&pdev->dev); + mipi_csi2_pm_suspend(&pdev->dev, true); + mipi_csi2_clk_disable(state); + media_entity_cleanup(&state->sd.entity); + mutex_destroy(&state->lock); + pm_runtime_set_suspended(&pdev->dev); + icc_put(state->icc_path); + + return 0; +} + +static const struct of_device_id mipi_csi2_of_match[] = { + { .compatible = "fsl,imx8mq-mipi-csi2",}, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mipi_csi2_of_match); + +static struct platform_driver mipi_csi2_driver = { + .probe = mipi_csi2_probe, + .remove = mipi_csi2_remove, + .driver = { + .of_match_table = mipi_csi2_of_match, + .name = MIPI_CSI2_DRIVER_NAME, + .pm = &mipi_csi2_pm_ops, + }, +}; + +module_platform_driver(mipi_csi2_driver); + +MODULE_DESCRIPTION("i.MX8MQ MIPI CSI-2 receiver driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx8mq-mipi-csi2"); From patchwork Thu May 27 07:54:07 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Kepplinger X-Patchwork-Id: 74634 Received: from vger.kernel.org ([23.128.96.18]) by www.linuxtv.org with esmtp (Exim 4.92) (envelope-from ) id 1lmArP-006Gwb-2j; Thu, 27 May 2021 07:55:19 +0000 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235215AbhE0H4u (ORCPT + 1 other); Thu, 27 May 2021 03:56:50 -0400 Received: from comms.puri.sm ([159.203.221.185]:53506 "EHLO comms.puri.sm" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235184AbhE0H41 (ORCPT ); Thu, 27 May 2021 03:56:27 -0400 Received: from localhost (localhost [127.0.0.1]) by comms.puri.sm (Postfix) with ESMTP id 6417BE2054; Thu, 27 May 2021 00:54:55 -0700 (PDT) Received: from comms.puri.sm ([127.0.0.1]) by localhost (comms.puri.sm [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 4lQP8-udZodX; Thu, 27 May 2021 00:54:54 -0700 (PDT) From: Martin Kepplinger To: laurent.pinchart@ideasonboard.com, mchehab@kernel.org, robh@kernel.org Cc: shawnguo@kernel.org, kernel@pengutronix.de, festevam@gmail.com, linux-imx@nxp.com, slongerbeam@gmail.com, kernel@puri.sm, krzk@kernel.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-staging@lists.linux.dev, Martin Kepplinger Subject: [PATCH v1 3/3] arm64: dts: imx8mq: add mipi csi phy and csi bridge descriptions Date: Thu, 27 May 2021 09:54:07 +0200 Message-Id: <20210527075407.3180744-4-martin.kepplinger@puri.sm> In-Reply-To: <20210527075407.3180744-1-martin.kepplinger@puri.sm> References: <20210527075407.3180744-1-martin.kepplinger@puri.sm> Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-LSpam-Score: -3.1 (---) X-LSpam-Report: No, score=-3.1 required=5.0 tests=BAYES_00=-1.9,HEADER_FROM_DIFFERENT_DOMAINS=0.5,MAILING_LIST_MULTI=-1,RCVD_IN_DNSWL_LOW=-0.7 autolearn=ham autolearn_force=no Describe the 2 available CSI interfaces on the i.MX8MQ with the MIPI-CSI2 receiver and the CSI Bridge that provides the user buffers. An image sensor is to be connected to the MIPIs' second port, described in board files. Signed-off-by: Martin Kepplinger --- arch/arm64/boot/dts/freescale/imx8mq.dtsi | 102 ++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi index 91df9c5350ae..5bf02f4f904d 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi @@ -1099,6 +1099,108 @@ uart4: serial@30a60000 { status = "disabled"; }; + mipi_csi1: mipi_csi1@30a70000 { + compatible = "fsl,imx8mq-mipi-csi2"; + reg = <0x30a70000 0x1000>; + clocks = <&clk IMX8MQ_CLK_CSI1_CORE>, + <&clk IMX8MQ_CLK_CSI1_ESC>, + <&clk IMX8MQ_CLK_CSI1_PHY_REF>, + <&clk IMX8MQ_CLK_CLKO2>; + clock-names = "core", "esc", "pxl", "clko2"; + assigned-clocks = <&clk IMX8MQ_CLK_CSI1_CORE>, + <&clk IMX8MQ_CLK_CSI1_PHY_REF>, + <&clk IMX8MQ_CLK_CSI1_ESC>; + assigned-clock-rates = <266000000>, <200000000>, <66000000>; + assigned-clock-parents = <&clk IMX8MQ_SYS1_PLL_266M>, + <&clk IMX8MQ_SYS2_PLL_1000M>, + <&clk IMX8MQ_SYS1_PLL_800M>; + power-domains = <&pgc_mipi_csi1>; + phy-reset = <&src>; + phy-gpr = <&iomuxc_gpr>; + interconnects = <&noc IMX8MQ_ICM_CSI1 &noc IMX8MQ_ICS_DRAM>; + interconnect-names = "dram"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi1_mipi_ep: endpoint { + remote-endpoint = <&csi1_ep>; + }; + }; + }; + }; + + csi1_bridge: csi1_bridge@30a90000 { + compatible = "fsl,imx7-csi"; + reg = <0x30a90000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MQ_CLK_CSI1_ROOT>; + clock-names = "mclk"; + status = "disabled"; + + port { + csi1_ep: endpoint { + remote-endpoint = <&csi1_mipi_ep>; + }; + }; + }; + + mipi_csi2: mipi_csi2@30b60000 { + compatible = "fsl,imx8mq-mipi-csi2"; + reg = <0x30b60000 0x1000>; + clocks = <&clk IMX8MQ_CLK_CSI2_CORE>, + <&clk IMX8MQ_CLK_CSI2_ESC>, + <&clk IMX8MQ_CLK_CSI2_PHY_REF>, + <&clk IMX8MQ_CLK_CLKO2>; + clock-names = "core", "esc", "pxl", "clko2"; + assigned-clocks = <&clk IMX8MQ_CLK_CSI2_CORE>, + <&clk IMX8MQ_CLK_CSI2_PHY_REF>, + <&clk IMX8MQ_CLK_CSI2_ESC>; + assigned-clock-rates = <266000000>, <333000000>, <66000000>; + assigned-clock-parents = <&clk IMX8MQ_SYS1_PLL_266M>, + <&clk IMX8MQ_SYS2_PLL_1000M>, + <&clk IMX8MQ_SYS1_PLL_800M>; + power-domains = <&pgc_mipi_csi2>; + phy-reset = <&src>; + phy-gpr = <&iomuxc_gpr>; + interconnects = <&noc IMX8MQ_ICM_CSI2 &noc IMX8MQ_ICS_DRAM>; + interconnect-names = "dram"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi2_mipi_ep: endpoint { + remote-endpoint = <&csi2_ep>; + }; + }; + }; + }; + + csi2_bridge: csi2_bridge@30b80000 { + compatible = "fsl,imx7-csi"; + reg = <0x30b80000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MQ_CLK_CSI2_ROOT>; + clock-names = "mclk"; + status = "disabled"; + + port { + csi2_ep: endpoint { + remote-endpoint = <&csi2_mipi_ep>; + }; + }; + }; + mu: mailbox@30aa0000 { compatible = "fsl,imx8mq-mu", "fsl,imx6sx-mu"; reg = <0x30aa0000 0x10000>;