mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Jack Zhu <jack.zhu@starfivetech.com>
To: Mauro Carvalho Chehab <mchehab@kernel.org>,
	Maxime Ripard <mripard@kernel.org>,
	Rob Herring <robh+dt@kernel.org>,
	Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
	Robert Foss <rfoss@kernel.org>,
	"Todor Tomov" <todor.too@gmail.com>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	"Laurent Pinchart" <laurent.pinchart@ideasonboard.com>,
	Jernej Skrabec <jernej.skrabec@gmail.com>,
	Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>,
	Hans Verkuil <hverkuil-cisco@xs4all.nl>,
	Eugen Hristev <eugen.hristev@collabora.com>
Cc: <linux-media@vger.kernel.org>, <devicetree@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <jack.zhu@starfivetech.com>,
	<changhuang.liang@starfivetech.com>
Subject: [PATCH v2 4/6] media: cadence: Add support for external dphy and JH7110 SoC
Date: Fri, 10 Mar 2023 20:05:51 +0800	[thread overview]
Message-ID: <20230310120553.60586-5-jack.zhu@starfivetech.com> (raw)
In-Reply-To: <20230310120553.60586-1-jack.zhu@starfivetech.com>

Add support for external MIPI D-PHY and Starfive JH7110 SoC which
has the cadence csi2 receiver.

Signed-off-by: Jack Zhu <jack.zhu@starfivetech.com>
---
 drivers/media/platform/cadence/cdns-csi2rx.c | 273 ++++++++++++++++++-
 1 file changed, 263 insertions(+), 10 deletions(-)

diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index cc3ebb0d96f6..7e7b096869fc 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -10,9 +10,11 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/of_graph.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
+#include <linux/reset.h>
 #include <linux/slab.h>
 
 #include <media/v4l2-ctrls.h>
@@ -30,6 +32,12 @@
 #define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane)	((plane) << (16 + (llane) * 4))
 #define CSI2RX_STATIC_CFG_LANES_MASK			GENMASK(11, 8)
 
+#define CSI2RX_DPHY_LANE_CTRL_REG		0x40
+#define CSI2RX_DPHY_CL_RST			BIT(16)
+#define CSI2RX_DPHY_DL_RST(i)			BIT((i) + 12)
+#define CSI2RX_DPHY_CL_EN			BIT(4)
+#define CSI2RX_DPHY_DL_EN(i)			BIT(i)
+
 #define CSI2RX_STREAM_BASE(n)		(((n) + 1) * 0x100)
 
 #define CSI2RX_STREAM_CTRL_REG(n)		(CSI2RX_STREAM_BASE(n) + 0x000)
@@ -37,6 +45,7 @@
 
 #define CSI2RX_STREAM_DATA_CFG_REG(n)		(CSI2RX_STREAM_BASE(n) + 0x008)
 #define CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT		BIT(31)
+#define CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0	BIT(7)
 #define CSI2RX_STREAM_DATA_CFG_VC_SELECT(n)		BIT((n) + 16)
 
 #define CSI2RX_STREAM_CFG_REG(n)		(CSI2RX_STREAM_BASE(n) + 0x00c)
@@ -54,8 +63,19 @@ enum csi2rx_pads {
 	CSI2RX_PAD_MAX,
 };
 
+struct csi2rx_fmt {
+	u32				code;
+	u8				bpp;
+	u32				dt;
+};
+
+struct csi2rx_platform_info {
+	unsigned long sys_clk_rate;
+};
+
 struct csi2rx_priv {
 	struct device			*dev;
+	unsigned int			power_count;
 	unsigned int			count;
 
 	/*
@@ -68,6 +88,9 @@ struct csi2rx_priv {
 	struct clk			*sys_clk;
 	struct clk			*p_clk;
 	struct clk			*pixel_clk[CSI2RX_STREAMS_MAX];
+	struct reset_control		*sys_rst;
+	struct reset_control		*p_rst;
+	struct reset_control		*pixel_rst[CSI2RX_STREAMS_MAX];
 	struct phy			*dphy;
 
 	u8				lanes[CSI2RX_LANES_MAX];
@@ -83,14 +106,100 @@ struct csi2rx_priv {
 	/* Remote source */
 	struct v4l2_subdev		*source_subdev;
 	int				source_pad;
+
+	const struct csi2rx_platform_info	*platform_info;
+};
+
+static const struct csi2rx_fmt formats[] = {
+	{
+		.code	= MEDIA_BUS_FMT_SRGGB10_1X10,
+		.bpp	= 10,
+		.dt	= 0x2b,
+	},
+	{
+		.code	= MEDIA_BUS_FMT_SGRBG10_1X10,
+		.bpp	= 10,
+		.dt	= 0x2b,
+	},
+	{
+		.code	= MEDIA_BUS_FMT_SGBRG10_1X10,
+		.bpp	= 10,
+		.dt	= 0x2b,
+	},
+	{
+		.code	= MEDIA_BUS_FMT_SBGGR10_1X10,
+		.bpp	= 10,
+		.dt	= 0x2b,
+	},
 };
 
+static u8 csi2rx_get_bpp(u32 code)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++) {
+		if (formats[i].code == code)
+			return formats[i].bpp;
+	}
+
+	return 0;
+}
+
+static u32 csi2rx_get_dt(u32 code)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++) {
+		if (formats[i].code == code)
+			return formats[i].dt;
+	}
+
+	return 0;
+}
+
+static s64 csi2rx_get_pixel_rate(struct csi2rx_priv *csi2rx)
+{
+	struct v4l2_ctrl *ctrl;
+
+	ctrl = v4l2_ctrl_find(csi2rx->source_subdev->ctrl_handler,
+			      V4L2_CID_PIXEL_RATE);
+	if (!ctrl) {
+		dev_err(csi2rx->dev, "no pixel rate control in subdev: %s\n",
+			csi2rx->source_subdev->name);
+		return -EINVAL;
+	}
+
+	return v4l2_ctrl_g_ctrl_int64(ctrl);
+}
+
 static inline
 struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev)
 {
 	return container_of(subdev, struct csi2rx_priv, subdev);
 }
 
+static int csi2rx_s_power(struct v4l2_subdev *subdev, int on)
+{
+	struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+
+	mutex_lock(&csi2rx->lock);
+
+	if (on) {
+		if (!csi2rx->power_count)
+			phy_init(csi2rx->dphy);
+
+		csi2rx->power_count++;
+	} else {
+		csi2rx->power_count--;
+
+		if (!csi2rx->power_count)
+			phy_exit(csi2rx->dphy);
+	}
+
+	mutex_unlock(&csi2rx->lock);
+	return 0;
+}
+
 static void csi2rx_reset(struct csi2rx_priv *csi2rx)
 {
 	writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT,
@@ -101,17 +210,70 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx)
 	writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG);
 }
 
+static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx)
+{
+	union phy_configure_opts opts = { };
+	struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
+	struct v4l2_subdev_format sd_fmt;
+	s64 pixel_rate;
+	int ret;
+	u8 bpp;
+
+	sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	sd_fmt.pad = 0;
+
+	ret = v4l2_subdev_call(csi2rx->source_subdev, pad, get_fmt, NULL,
+			       &sd_fmt);
+	if (ret)
+		return ret;
+
+	bpp = csi2rx_get_bpp(sd_fmt.format.code);
+	if (!bpp)
+		return -EINVAL;
+
+	pixel_rate = csi2rx_get_pixel_rate(csi2rx);
+	if (pixel_rate < 0)
+		return pixel_rate;
+
+	ret = phy_mipi_dphy_get_default_config(pixel_rate, bpp,
+					       csi2rx->num_lanes, cfg);
+	if (ret)
+		return ret;
+
+	phy_pm_runtime_get_sync(csi2rx->dphy);
+
+	ret = phy_power_on(csi2rx->dphy);
+	if (ret)
+		goto out;
+
+	ret = phy_configure(csi2rx->dphy, &opts);
+	if (ret) {
+		/* Can't do anything if it fails. Ignore the return value. */
+		phy_power_off(csi2rx->dphy);
+		goto out;
+	}
+
+out:
+	phy_pm_runtime_put_sync(csi2rx->dphy);
+
+	return ret;
+}
+
 static int csi2rx_start(struct csi2rx_priv *csi2rx)
 {
+	struct v4l2_subdev_format sd_fmt;
 	unsigned int i;
 	unsigned long lanes_used = 0;
 	u32 reg;
+	u32 dt = 0;
 	int ret;
 
 	ret = clk_prepare_enable(csi2rx->p_clk);
 	if (ret)
 		return ret;
 
+	reset_control_deassert(csi2rx->p_rst);
+
 	csi2rx_reset(csi2rx);
 
 	reg = csi2rx->num_lanes << 8;
@@ -139,6 +301,29 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
 	if (ret)
 		goto err_disable_pclk;
 
+	/* Enable DPHY clk and data lanes. */
+	if (csi2rx->dphy) {
+		reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST;
+		for (i = 0; i < csi2rx->num_lanes; i++) {
+			reg |= CSI2RX_DPHY_DL_EN(csi2rx->lanes[i] - 1);
+			reg |= CSI2RX_DPHY_DL_RST(csi2rx->lanes[i] - 1);
+		}
+
+		writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
+	}
+
+	sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	sd_fmt.pad = 0;
+
+	ret = v4l2_subdev_call(csi2rx->source_subdev, pad, get_fmt, NULL,
+			       &sd_fmt);
+	if (ret)
+		dev_warn(csi2rx->dev, "Couldn't get format\n");
+
+	dt = csi2rx_get_dt(sd_fmt.format.code);
+	if (!dt)
+		dev_warn(csi2rx->dev, "Couldn't get dt\n");
+
 	/*
 	 * Create a static mapping between the CSI virtual channels
 	 * and the output stream.
@@ -154,6 +339,8 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
 		if (ret)
 			goto err_disable_pixclk;
 
+		reset_control_deassert(csi2rx->pixel_rst[i]);
+
 		writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF,
 		       csi2rx->base + CSI2RX_STREAM_CFG_REG(i));
 
@@ -161,6 +348,11 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
 		       CSI2RX_STREAM_DATA_CFG_VC_SELECT(i),
 		       csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i));
 
+		if (dt)
+			writel(readl(csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)) |
+			       CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0 | dt,
+			       csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i));
+
 		writel(CSI2RX_STREAM_CTRL_START,
 		       csi2rx->base + CSI2RX_STREAM_CTRL_REG(i));
 	}
@@ -169,10 +361,27 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
 	if (ret)
 		goto err_disable_pixclk;
 
+	if (csi2rx->platform_info && csi2rx->platform_info->sys_clk_rate > 0)
+		clk_set_rate(csi2rx->sys_clk,
+			     csi2rx->platform_info->sys_clk_rate);
+
+	reset_control_deassert(csi2rx->sys_rst);
+
+	if (csi2rx->dphy) {
+		ret = csi2rx_configure_ext_dphy(csi2rx);
+		if (ret) {
+			dev_err(csi2rx->dev,
+				"Failed to configure external DPHY: %d\n", ret);
+			goto err_disable_sysclk;
+		}
+	}
+
 	clk_disable_unprepare(csi2rx->p_clk);
 
 	return 0;
 
+err_disable_sysclk:
+	clk_disable_unprepare(csi2rx->sys_clk);
 err_disable_pixclk:
 	for (; i > 0; i--)
 		clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
@@ -188,18 +397,28 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
 	unsigned int i;
 
 	clk_prepare_enable(csi2rx->p_clk);
+	reset_control_assert(csi2rx->sys_rst);
 	clk_disable_unprepare(csi2rx->sys_clk);
 
 	for (i = 0; i < csi2rx->max_streams; i++) {
 		writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i));
 
+		reset_control_assert(csi2rx->pixel_rst[i]);
 		clk_disable_unprepare(csi2rx->pixel_clk[i]);
 	}
 
+	reset_control_assert(csi2rx->p_rst);
 	clk_disable_unprepare(csi2rx->p_clk);
 
 	if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false))
 		dev_warn(csi2rx->dev, "Couldn't disable our subdev\n");
+
+	if (csi2rx->dphy) {
+		writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
+
+		if (phy_power_off(csi2rx->dphy))
+			dev_warn(csi2rx->dev, "Couldn't power off DPHY\n");
+	}
 }
 
 static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
@@ -236,11 +455,16 @@ static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
 	return ret;
 }
 
+static const struct v4l2_subdev_core_ops csi2rx_core_ops = {
+	.s_power = csi2rx_s_power,
+};
+
 static const struct v4l2_subdev_video_ops csi2rx_video_ops = {
 	.s_stream	= csi2rx_s_stream,
 };
 
 static const struct v4l2_subdev_ops csi2rx_subdev_ops = {
+	.core		= &csi2rx_core_ops,
 	.video		= &csi2rx_video_ops,
 };
 
@@ -250,6 +474,8 @@ static int csi2rx_async_bound(struct v4l2_async_notifier *notifier,
 {
 	struct v4l2_subdev *subdev = notifier->sd;
 	struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+	struct v4l2_device *v4l2_dev;
+	int ret;
 
 	csi2rx->source_pad = media_entity_get_fwnode_pad(&s_subdev->entity,
 							 s_subdev->fwnode,
@@ -265,6 +491,15 @@ static int csi2rx_async_bound(struct v4l2_async_notifier *notifier,
 	dev_dbg(csi2rx->dev, "Bound %s pad: %d\n", s_subdev->name,
 		csi2rx->source_pad);
 
+	/* ensure source subdev register subdev node */
+	v4l2_dev = notifier->v4l2_dev ? notifier->v4l2_dev :
+		notifier->parent->v4l2_dev;
+	if (v4l2_dev) {
+		ret = v4l2_device_register_subdev_nodes(v4l2_dev);
+		if (ret < 0)
+			return ret;
+	}
+
 	return media_create_pad_link(&csi2rx->source_subdev->entity,
 				     csi2rx->source_pad,
 				     &csi2rx->subdev.entity, 0,
@@ -299,21 +534,23 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx,
 		return PTR_ERR(csi2rx->p_clk);
 	}
 
+	csi2rx->sys_rst =
+		devm_reset_control_get_optional_exclusive(&pdev->dev,
+							  "sys_rst");
+	if (IS_ERR(csi2rx->sys_rst))
+		return PTR_ERR(csi2rx->sys_rst);
+
+	csi2rx->p_rst =
+		devm_reset_control_get_optional_exclusive(&pdev->dev, "p_rst");
+	if (IS_ERR(csi2rx->p_rst))
+		return PTR_ERR(csi2rx->p_rst);
+
 	csi2rx->dphy = devm_phy_optional_get(&pdev->dev, "dphy");
 	if (IS_ERR(csi2rx->dphy)) {
 		dev_err(&pdev->dev, "Couldn't get external D-PHY\n");
 		return PTR_ERR(csi2rx->dphy);
 	}
 
-	/*
-	 * FIXME: Once we'll have external D-PHY support, the check
-	 * will need to be removed.
-	 */
-	if (csi2rx->dphy) {
-		dev_err(&pdev->dev, "External D-PHY not supported yet\n");
-		return -EINVAL;
-	}
-
 	ret = clk_prepare_enable(csi2rx->p_clk);
 	if (ret) {
 		dev_err(&pdev->dev, "Couldn't prepare and enable P clock\n");
@@ -343,13 +580,14 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx,
 	 * FIXME: Once we'll have internal D-PHY support, the check
 	 * will need to be removed.
 	 */
-	if (csi2rx->has_internal_dphy) {
+	if (!csi2rx->dphy && csi2rx->has_internal_dphy) {
 		dev_err(&pdev->dev, "Internal D-PHY not supported yet\n");
 		return -EINVAL;
 	}
 
 	for (i = 0; i < csi2rx->max_streams; i++) {
 		char clk_name[16];
+		char rst_name[16];
 
 		snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i);
 		csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name);
@@ -357,6 +595,13 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx,
 			dev_err(&pdev->dev, "Couldn't get clock %s\n", clk_name);
 			return PTR_ERR(csi2rx->pixel_clk[i]);
 		}
+
+		snprintf(rst_name, sizeof(rst_name), "pixel_if%u_rst", i);
+		csi2rx->pixel_rst[i] =
+			devm_reset_control_get_optional_exclusive(&pdev->dev,
+								  rst_name);
+		if (IS_ERR(csi2rx->pixel_rst[i]))
+			return PTR_ERR(csi2rx->pixel_rst[i]);
 	}
 
 	return 0;
@@ -425,6 +670,7 @@ static int csi2rx_probe(struct platform_device *pdev)
 	csi2rx = kzalloc(sizeof(*csi2rx), GFP_KERNEL);
 	if (!csi2rx)
 		return -ENOMEM;
+	csi2rx->platform_info = of_device_get_match_data(&pdev->dev);
 	platform_set_drvdata(pdev, csi2rx);
 	csi2rx->dev = &pdev->dev;
 	mutex_init(&csi2rx->lock);
@@ -441,6 +687,7 @@ static int csi2rx_probe(struct platform_device *pdev)
 	csi2rx->subdev.dev = &pdev->dev;
 	v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops);
 	v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev);
+	csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 	snprintf(csi2rx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s",
 		 KBUILD_MODNAME, dev_name(&pdev->dev));
 
@@ -462,6 +709,7 @@ static int csi2rx_probe(struct platform_device *pdev)
 	dev_info(&pdev->dev,
 		 "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n",
 		 csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams,
+		 csi2rx->dphy ? "external" :
 		 csi2rx->has_internal_dphy ? "internal" : "no");
 
 	return 0;
@@ -483,8 +731,13 @@ static int csi2rx_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct csi2rx_platform_info stf_jh7110_info = {
+	.sys_clk_rate = 297000000,
+};
+
 static const struct of_device_id csi2rx_of_table[] = {
 	{ .compatible = "cdns,csi2rx" },
+	{ .compatible = "starfive,jh7110-csi2rx", .data = &stf_jh7110_info },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, csi2rx_of_table);
-- 
2.34.1


  parent reply	other threads:[~2023-03-10 12:06 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-10 12:05 [PATCH v2 0/6] Add Starfive Camera Subsystem driver Jack Zhu
2023-03-10 12:05 ` [PATCH v2 1/6] media: dt-bindings: Add bindings for JH7110 Camera Subsystem Jack Zhu
2023-03-12 11:12   ` Laurent Pinchart
2023-03-20  6:24     ` Jack Zhu
2023-03-12 11:16   ` Krzysztof Kozlowski
2023-03-20  6:27     ` Jack Zhu
2023-03-10 12:05 ` [PATCH v2 2/6] media: dt-bindings: cadence-csi2rx: Convert to DT schema Jack Zhu
2023-03-10 13:55   ` Rob Herring
2023-03-20  6:30     ` Jack Zhu
2023-03-12 11:09   ` Laurent Pinchart
2023-03-20  8:08     ` Jack Zhu
2023-03-12 11:17   ` Krzysztof Kozlowski
2023-03-20  8:19     ` Jack Zhu
2023-03-10 12:05 ` [PATCH v2 3/6] media: admin-guide: Add starfive_camss.rst for Starfive Camera Subsystem Jack Zhu
2023-03-10 12:05 ` Jack Zhu [this message]
2023-03-12 11:33   ` [PATCH v2 4/6] media: cadence: Add support for external dphy and JH7110 SoC Laurent Pinchart
2023-03-20 11:54     ` Jack Zhu
2023-03-10 12:05 ` [PATCH v2 5/6] MAINTAINERS: Add Starfive Camera Subsystem driver Jack Zhu
2023-03-12 11:14   ` Laurent Pinchart
2023-03-20 12:03     ` Jack Zhu
2023-03-10 12:05 ` [PATCH v2 6/6] media: starfive: " Jack Zhu
2023-03-10 14:09   ` kernel test robot
2023-03-10 14:09   ` kernel test robot
     [not found]   ` <20230312124339.GD2545@pendragon.ideasonboard.com>
     [not found]     ` <650b6882-ea02-e4c8-1f73-9e5bdeab290d@starfivetech.com>
2023-03-21 17:56       ` Laurent Pinchart
2023-03-23 11:22         ` Jack Zhu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230310120553.60586-5-jack.zhu@starfivetech.com \
    --to=jack.zhu@starfivetech.com \
    --cc=andrzejtp2010@gmail.com \
    --cc=changhuang.liang@starfivetech.com \
    --cc=devicetree@vger.kernel.org \
    --cc=eugen.hristev@collabora.com \
    --cc=hverkuil-cisco@xs4all.nl \
    --cc=jernej.skrabec@gmail.com \
    --cc=krzysztof.kozlowski+dt@linaro.org \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=mchehab@kernel.org \
    --cc=mripard@kernel.org \
    --cc=p.zabel@pengutronix.de \
    --cc=rfoss@kernel.org \
    --cc=robh+dt@kernel.org \
    --cc=todor.too@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®