Commit 642b70d5 authored by Frank Li's avatar Frank Li Committed by Hans Verkuil
Browse files

media: imx8mq-mipi-csi2: Add support for i.MX8QXP



Add support for i.MX8QXP, which has a dedicated control and status register
(CSR) space. Enable obtaining the second register space and initializing
PHY and link settings accordingly.

Reviewed-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarFrank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20250522-8qxp_camera-v5-10-d4be869fdb7e@nxp.com


Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarHans Verkuil <hverkuil@xs4all.nl>
parent 382d53e9
Loading
Loading
Loading
Loading
+111 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
 * Copyright (C) 2021 Purism SPC
 */

#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
@@ -88,6 +89,7 @@ static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = {
struct imx8mq_plat_data {
	int (*enable)(struct csi_state *state, u32 hs_settle);
	void (*disable)(struct csi_state *state);
	bool use_reg_csr;
};

/*
@@ -167,6 +169,95 @@ static const struct imx8mq_plat_data imx8mq_data = {
	.enable = imx8mq_gpr_enable,
};

/* -----------------------------------------------------------------------------
 * i.MX8QXP
 */

#define CSI2SS_PL_CLK_INTERVAL_US		100
#define CSI2SS_PL_CLK_TIMEOUT_US		100000

#define CSI2SS_PLM_CTRL				0x0
#define CSI2SS_PLM_CTRL_ENABLE_PL		BIT(0)
#define CSI2SS_PLM_CTRL_VSYNC_OVERRIDE		BIT(9)
#define CSI2SS_PLM_CTRL_HSYNC_OVERRIDE		BIT(10)
#define CSI2SS_PLM_CTRL_VALID_OVERRIDE		BIT(11)
#define CSI2SS_PLM_CTRL_POLARITY_HIGH		BIT(12)
#define CSI2SS_PLM_CTRL_PL_CLK_RUN		BIT(31)

#define CSI2SS_PHY_CTRL				0x4
#define CSI2SS_PHY_CTRL_RX_ENABLE		BIT(0)
#define CSI2SS_PHY_CTRL_AUTO_PD_EN		BIT(1)
#define CSI2SS_PHY_CTRL_DDRCLK_EN		BIT(2)
#define CSI2SS_PHY_CTRL_CONT_CLK_MODE		BIT(3)
#define CSI2SS_PHY_CTRL_RX_HS_SETTLE_MASK	GENMASK(9, 4)
#define CSI2SS_PHY_CTRL_RTERM_SEL		BIT(21)
#define CSI2SS_PHY_CTRL_PD			BIT(22)

#define CSI2SS_DATA_TYPE_DISABLE_BF		0x38
#define CSI2SS_DATA_TYPE_DISABLE_BF_MASK	GENMASK(23, 0)

#define CSI2SS_CTRL_CLK_RESET			0x44
#define CSI2SS_CTRL_CLK_RESET_EN		BIT(0)

static int imx8qxp_gpr_enable(struct csi_state *state, u32 hs_settle)
{
	int ret;
	u32 val;

	/* Clear format */
	regmap_clear_bits(state->phy_gpr, CSI2SS_DATA_TYPE_DISABLE_BF,
			  CSI2SS_DATA_TYPE_DISABLE_BF_MASK);

	regmap_write(state->phy_gpr, CSI2SS_PLM_CTRL, 0x0);

	regmap_write(state->phy_gpr, CSI2SS_PHY_CTRL,
		     FIELD_PREP(CSI2SS_PHY_CTRL_RX_HS_SETTLE_MASK, hs_settle) |
		     CSI2SS_PHY_CTRL_RX_ENABLE | CSI2SS_PHY_CTRL_DDRCLK_EN |
		     CSI2SS_PHY_CTRL_CONT_CLK_MODE | CSI2SS_PHY_CTRL_PD |
		     CSI2SS_PHY_CTRL_RTERM_SEL | CSI2SS_PHY_CTRL_AUTO_PD_EN);

	ret = regmap_read_poll_timeout(state->phy_gpr, CSI2SS_PLM_CTRL,
				       val, !(val & CSI2SS_PLM_CTRL_PL_CLK_RUN),
				       CSI2SS_PL_CLK_INTERVAL_US,
				       CSI2SS_PL_CLK_TIMEOUT_US);

	if (ret) {
		dev_err(state->dev, "Timeout waiting for Pixel-Link clock\n");
		return ret;
	}

	/* Enable Pixel link Master */
	regmap_set_bits(state->phy_gpr, CSI2SS_PLM_CTRL,
			CSI2SS_PLM_CTRL_ENABLE_PL | CSI2SS_PLM_CTRL_VALID_OVERRIDE);

	/* PHY Enable */
	regmap_clear_bits(state->phy_gpr, CSI2SS_PHY_CTRL,
			  CSI2SS_PHY_CTRL_PD | CSI2SS_PLM_CTRL_POLARITY_HIGH);

	/* Release Reset */
	regmap_set_bits(state->phy_gpr, CSI2SS_CTRL_CLK_RESET, CSI2SS_CTRL_CLK_RESET_EN);

	return ret;
}

static void imx8qxp_gpr_disable(struct csi_state *state)
{
	/* Disable Pixel Link */
	regmap_write(state->phy_gpr, CSI2SS_PLM_CTRL, 0x0);

	/* Disable PHY */
	regmap_write(state->phy_gpr, CSI2SS_PHY_CTRL, 0x0);

	regmap_clear_bits(state->phy_gpr, CSI2SS_CTRL_CLK_RESET,
			  CSI2SS_CTRL_CLK_RESET_EN);
};

static const struct imx8mq_plat_data imx8qxp_data = {
	.enable = imx8qxp_gpr_enable,
	.disable = imx8qxp_gpr_disable,
	.use_reg_csr = true,
};

static const struct csi2_pix_format imx8mq_mipi_csi_formats[] = {
	/* RAW (Bayer and greyscale) formats. */
	{
@@ -865,6 +956,25 @@ static int imx8mq_mipi_csi_parse_dt(struct csi_state *state)
		return PTR_ERR(state->rst);
	}

	if (state->pdata->use_reg_csr) {
		const struct regmap_config regmap_config = {
			.reg_bits = 32,
			.val_bits = 32,
			.reg_stride = 4,
		};
		void __iomem *base;

		base = devm_platform_ioremap_resource(to_platform_device(dev), 1);
		if (IS_ERR(base))
			return dev_err_probe(dev, IS_ERR(base), "Missing CSR register\n");

		state->phy_gpr = devm_regmap_init_mmio(dev, base, &regmap_config);
		if (IS_ERR(state->phy_gpr))
			return dev_err_probe(dev, PTR_ERR(state->phy_gpr),
					     "Failed to init CSI MMIO regmap\n");
		return 0;
	}

	ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val,
					 ARRAY_SIZE(out_val));
	if (ret) {
@@ -984,6 +1094,7 @@ static void imx8mq_mipi_csi_remove(struct platform_device *pdev)

static const struct of_device_id imx8mq_mipi_csi_of_match[] = {
	{ .compatible = "fsl,imx8mq-mipi-csi2", .data = &imx8mq_data },
	{ .compatible = "fsl,imx8qxp-mipi-csi2", .data = &imx8qxp_data },
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match);