diff mbox series

[5/6] media: cadence: cdns-csi2rx: Support multiple pixels per clock cycle

Message ID 20250324-probe_fixes-v1-5-5cd5b9e1cfac@ideasonboard.com (mailing list archive)
State New
Headers show
Series media: ti, cdns: Multiple pixel support and misc fixes | expand

Commit Message

Jai Luthra March 24, 2025, 12:01 p.m. UTC
The output pixel interface is a parallel bus (32 bits), which
supports sending multiple pixels (1, 2 or 4) per clock cycle for
smaller pixel widths like RAW8-RAW16.

Dual-pixel and Quad-pixel modes can be a requirement if the export rate
of the Cadence IP in Single-pixel mode maxes out before the maximum
supported DPHY-RX frequency, which is the case with TI's integration of
this IP [1].

So, we export a function that lets the downstream hardware block request
a higher pixel-per-clock on a particular output pad.

We check if we can support the requested pixels per clock given the
known maximum for the currently configured format. If not, we set it
to the highest feasible value and return this value to the caller.

[1] Section 12.6.1.4.8.14 CSI_RX_IF Programming Restrictions of AM62 TRM

Link: https://www.ti.com/lit/pdf/spruj16
Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
---
 drivers/media/platform/cadence/cdns-csi2rx.c | 74 +++++++++++++++++++++-------
 drivers/media/platform/cadence/cdns-csi2rx.h | 19 +++++++
 2 files changed, 75 insertions(+), 18 deletions(-)

Comments

kernel test robot March 24, 2025, 2:28 p.m. UTC | #1
Hi Jai,

kernel test robot noticed the following build errors:

[auto build test ERROR on 586de92313fcab8ed84ac5f78f4d2aae2db92c59]

url:    https://github.com/intel-lab-lkp/linux/commits/Jai-Luthra/media-ti-j721e-csi2rx-Use-devm_of_platform_populate/20250324-200457
base:   586de92313fcab8ed84ac5f78f4d2aae2db92c59
patch link:    https://lore.kernel.org/r/20250324-probe_fixes-v1-5-5cd5b9e1cfac%40ideasonboard.com
patch subject: [PATCH 5/6] media: cadence: cdns-csi2rx: Support multiple pixels per clock cycle
config: hexagon-randconfig-001-20250324 (https://download.01.org/0day-ci/archive/20250324/202503242153.Yn1DfnS5-lkp@intel.com/config)
compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project c2692afc0a92cd5da140dfcdfff7818a5b8ce997)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250324/202503242153.Yn1DfnS5-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503242153.Yn1DfnS5-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/media/platform/cadence/cdns-csi2rx.c:286:10: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     286 |                 reg |= FIELD_PREP(CSI2RX_STREAM_CFG_NUM_PIXELS_MASK,
         |                        ^
   1 error generated.


vim +/FIELD_PREP +286 drivers/media/platform/cadence/cdns-csi2rx.c

   214	
   215	static int csi2rx_start(struct csi2rx_priv *csi2rx)
   216	{
   217		unsigned int i;
   218		unsigned long lanes_used = 0;
   219		u32 reg;
   220		int ret;
   221	
   222		ret = clk_prepare_enable(csi2rx->p_clk);
   223		if (ret)
   224			return ret;
   225	
   226		reset_control_deassert(csi2rx->p_rst);
   227		csi2rx_reset(csi2rx);
   228	
   229		reg = csi2rx->num_lanes << 8;
   230		for (i = 0; i < csi2rx->num_lanes; i++) {
   231			reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csi2rx->lanes[i]);
   232			set_bit(csi2rx->lanes[i], &lanes_used);
   233		}
   234	
   235		/*
   236		 * Even the unused lanes need to be mapped. In order to avoid
   237		 * to map twice to the same physical lane, keep the lanes used
   238		 * in the previous loop, and only map unused physical lanes to
   239		 * the rest of our logical lanes.
   240		 */
   241		for (i = csi2rx->num_lanes; i < csi2rx->max_lanes; i++) {
   242			unsigned int idx = find_first_zero_bit(&lanes_used,
   243							       csi2rx->max_lanes);
   244			set_bit(idx, &lanes_used);
   245			reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, i + 1);
   246		}
   247	
   248		writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG);
   249	
   250		/* Enable DPHY clk and data lanes. */
   251		if (csi2rx->dphy) {
   252			reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST;
   253			for (i = 0; i < csi2rx->num_lanes; i++) {
   254				reg |= CSI2RX_DPHY_DL_EN(csi2rx->lanes[i] - 1);
   255				reg |= CSI2RX_DPHY_DL_RST(csi2rx->lanes[i] - 1);
   256			}
   257	
   258			writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
   259	
   260			ret = csi2rx_configure_ext_dphy(csi2rx);
   261			if (ret) {
   262				dev_err(csi2rx->dev,
   263					"Failed to configure external DPHY: %d\n", ret);
   264				goto err_disable_pclk;
   265			}
   266		}
   267	
   268		/*
   269		 * Create a static mapping between the CSI virtual channels
   270		 * and the output stream.
   271		 *
   272		 * This should be enhanced, but v4l2 lacks the support for
   273		 * changing that mapping dynamically.
   274		 *
   275		 * We also cannot enable and disable independent streams here,
   276		 * hence the reference counting.
   277		 */
   278		for (i = 0; i < csi2rx->max_streams; i++) {
   279			ret = clk_prepare_enable(csi2rx->pixel_clk[i]);
   280			if (ret)
   281				goto err_disable_pixclk;
   282	
   283			reset_control_deassert(csi2rx->pixel_rst[i]);
   284	
   285			reg = CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF;
 > 286			reg |= FIELD_PREP(CSI2RX_STREAM_CFG_NUM_PIXELS_MASK,
   287					  csi2rx->num_pixels[i]);
   288			writel(reg, csi2rx->base + CSI2RX_STREAM_CFG_REG(i));
   289	
   290			/*
   291			 * Enable one virtual channel. When multiple virtual channels
   292			 * are supported this will have to be changed.
   293			 */
   294			writel(CSI2RX_STREAM_DATA_CFG_VC_SELECT(0),
   295			       csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i));
   296	
   297			writel(CSI2RX_STREAM_CTRL_START,
   298			       csi2rx->base + CSI2RX_STREAM_CTRL_REG(i));
   299		}
   300	
   301		ret = clk_prepare_enable(csi2rx->sys_clk);
   302		if (ret)
   303			goto err_disable_pixclk;
   304	
   305		reset_control_deassert(csi2rx->sys_rst);
   306	
   307		ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
   308		if (ret)
   309			goto err_disable_sysclk;
   310	
   311		clk_disable_unprepare(csi2rx->p_clk);
   312	
   313		return 0;
   314	
   315	err_disable_sysclk:
   316		clk_disable_unprepare(csi2rx->sys_clk);
   317	err_disable_pixclk:
   318		for (; i > 0; i--) {
   319			reset_control_assert(csi2rx->pixel_rst[i - 1]);
   320			clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
   321		}
   322	
   323		if (csi2rx->dphy) {
   324			writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
   325			phy_power_off(csi2rx->dphy);
   326		}
   327	err_disable_pclk:
   328		clk_disable_unprepare(csi2rx->p_clk);
   329	
   330		return ret;
   331	}
   332
kernel test robot March 24, 2025, 6:21 p.m. UTC | #2
Hi Jai,

kernel test robot noticed the following build errors:

[auto build test ERROR on 586de92313fcab8ed84ac5f78f4d2aae2db92c59]

url:    https://github.com/intel-lab-lkp/linux/commits/Jai-Luthra/media-ti-j721e-csi2rx-Use-devm_of_platform_populate/20250324-200457
base:   586de92313fcab8ed84ac5f78f4d2aae2db92c59
patch link:    https://lore.kernel.org/r/20250324-probe_fixes-v1-5-5cd5b9e1cfac%40ideasonboard.com
patch subject: [PATCH 5/6] media: cadence: cdns-csi2rx: Support multiple pixels per clock cycle
config: sparc-randconfig-002-20250324 (https://download.01.org/0day-ci/archive/20250325/202503250056.MfBrAipQ-lkp@intel.com/config)
compiler: sparc64-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250325/202503250056.MfBrAipQ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503250056.MfBrAipQ-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/media/platform/cadence/cdns-csi2rx.c: In function 'csi2rx_start':
>> drivers/media/platform/cadence/cdns-csi2rx.c:286:24: error: implicit declaration of function 'FIELD_PREP' [-Werror=implicit-function-declaration]
     286 |                 reg |= FIELD_PREP(CSI2RX_STREAM_CFG_NUM_PIXELS_MASK,
         |                        ^~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/FIELD_PREP +286 drivers/media/platform/cadence/cdns-csi2rx.c

   214	
   215	static int csi2rx_start(struct csi2rx_priv *csi2rx)
   216	{
   217		unsigned int i;
   218		unsigned long lanes_used = 0;
   219		u32 reg;
   220		int ret;
   221	
   222		ret = clk_prepare_enable(csi2rx->p_clk);
   223		if (ret)
   224			return ret;
   225	
   226		reset_control_deassert(csi2rx->p_rst);
   227		csi2rx_reset(csi2rx);
   228	
   229		reg = csi2rx->num_lanes << 8;
   230		for (i = 0; i < csi2rx->num_lanes; i++) {
   231			reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csi2rx->lanes[i]);
   232			set_bit(csi2rx->lanes[i], &lanes_used);
   233		}
   234	
   235		/*
   236		 * Even the unused lanes need to be mapped. In order to avoid
   237		 * to map twice to the same physical lane, keep the lanes used
   238		 * in the previous loop, and only map unused physical lanes to
   239		 * the rest of our logical lanes.
   240		 */
   241		for (i = csi2rx->num_lanes; i < csi2rx->max_lanes; i++) {
   242			unsigned int idx = find_first_zero_bit(&lanes_used,
   243							       csi2rx->max_lanes);
   244			set_bit(idx, &lanes_used);
   245			reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, i + 1);
   246		}
   247	
   248		writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG);
   249	
   250		/* Enable DPHY clk and data lanes. */
   251		if (csi2rx->dphy) {
   252			reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST;
   253			for (i = 0; i < csi2rx->num_lanes; i++) {
   254				reg |= CSI2RX_DPHY_DL_EN(csi2rx->lanes[i] - 1);
   255				reg |= CSI2RX_DPHY_DL_RST(csi2rx->lanes[i] - 1);
   256			}
   257	
   258			writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
   259	
   260			ret = csi2rx_configure_ext_dphy(csi2rx);
   261			if (ret) {
   262				dev_err(csi2rx->dev,
   263					"Failed to configure external DPHY: %d\n", ret);
   264				goto err_disable_pclk;
   265			}
   266		}
   267	
   268		/*
   269		 * Create a static mapping between the CSI virtual channels
   270		 * and the output stream.
   271		 *
   272		 * This should be enhanced, but v4l2 lacks the support for
   273		 * changing that mapping dynamically.
   274		 *
   275		 * We also cannot enable and disable independent streams here,
   276		 * hence the reference counting.
   277		 */
   278		for (i = 0; i < csi2rx->max_streams; i++) {
   279			ret = clk_prepare_enable(csi2rx->pixel_clk[i]);
   280			if (ret)
   281				goto err_disable_pixclk;
   282	
   283			reset_control_deassert(csi2rx->pixel_rst[i]);
   284	
   285			reg = CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF;
 > 286			reg |= FIELD_PREP(CSI2RX_STREAM_CFG_NUM_PIXELS_MASK,
   287					  csi2rx->num_pixels[i]);
   288			writel(reg, csi2rx->base + CSI2RX_STREAM_CFG_REG(i));
   289	
   290			/*
   291			 * Enable one virtual channel. When multiple virtual channels
   292			 * are supported this will have to be changed.
   293			 */
   294			writel(CSI2RX_STREAM_DATA_CFG_VC_SELECT(0),
   295			       csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i));
   296	
   297			writel(CSI2RX_STREAM_CTRL_START,
   298			       csi2rx->base + CSI2RX_STREAM_CTRL_REG(i));
   299		}
   300	
   301		ret = clk_prepare_enable(csi2rx->sys_clk);
   302		if (ret)
   303			goto err_disable_pixclk;
   304	
   305		reset_control_deassert(csi2rx->sys_rst);
   306	
   307		ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
   308		if (ret)
   309			goto err_disable_sysclk;
   310	
   311		clk_disable_unprepare(csi2rx->p_clk);
   312	
   313		return 0;
   314	
   315	err_disable_sysclk:
   316		clk_disable_unprepare(csi2rx->sys_clk);
   317	err_disable_pixclk:
   318		for (; i > 0; i--) {
   319			reset_control_assert(csi2rx->pixel_rst[i - 1]);
   320			clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
   321		}
   322	
   323		if (csi2rx->dphy) {
   324			writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
   325			phy_power_off(csi2rx->dphy);
   326		}
   327	err_disable_pclk:
   328		clk_disable_unprepare(csi2rx->p_clk);
   329	
   330		return ret;
   331	}
   332
diff mbox series

Patch

diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index b489967563cde96ac109c44a665b30e573125721..e394afe954fcdb7219dfd14df7a82e8e19cbd572 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -22,6 +22,8 @@ 
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 
+#include "cdns-csi2rx.h"
+
 #define CSI2RX_DEVICE_CFG_REG			0x000
 
 #define CSI2RX_SOFT_RESET_REG			0x004
@@ -53,6 +55,8 @@ 
 
 #define CSI2RX_STREAM_CFG_REG(n)		(CSI2RX_STREAM_BASE(n) + 0x00c)
 #define CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF		(1 << 8)
+#define CSI2RX_STREAM_CFG_NUM_PIXELS_MASK		GENMASK(5, 4)
+#define CSI2RX_STREAM_CFG_NUM_PIXELS(n)			((n) >> 1)
 
 #define CSI2RX_LANES_MAX	4
 #define CSI2RX_STREAMS_MAX	4
@@ -68,7 +72,10 @@  enum csi2rx_pads {
 
 struct csi2rx_fmt {
 	u32				code;
+	/* width of a single pixel on CSI-2 bus */
 	u8				bpp;
+	/* max pixels per clock supported on output bus */
+	u8				max_pixels;
 };
 
 struct csi2rx_priv {
@@ -90,6 +97,7 @@  struct csi2rx_priv {
 	struct reset_control		*pixel_rst[CSI2RX_STREAMS_MAX];
 	struct phy			*dphy;
 
+	u8				num_pixels[CSI2RX_STREAMS_MAX];
 	u8				lanes[CSI2RX_LANES_MAX];
 	u8				num_lanes;
 	u8				max_lanes;
@@ -106,22 +114,22 @@  struct csi2rx_priv {
 };
 
 static const struct csi2rx_fmt formats[] = {
-	{ .code	= MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, },
-	{ .code	= MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, },
-	{ .code	= MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, },
-	{ .code	= MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, },
-	{ .code	= MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, },
-	{ .code	= MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, },
-	{ .code	= MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, },
-	{ .code	= MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, },
-	{ .code	= MEDIA_BUS_FMT_Y8_1X8,     .bpp = 8, },
-	{ .code	= MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, },
-	{ .code	= MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, },
-	{ .code	= MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, },
-	{ .code	= MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, },
-	{ .code	= MEDIA_BUS_FMT_RGB565_1X16,  .bpp = 16, },
-	{ .code	= MEDIA_BUS_FMT_RGB888_1X24,  .bpp = 24, },
-	{ .code	= MEDIA_BUS_FMT_BGR888_1X24,  .bpp = 24, },
+	{ .code	= MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, .max_pixels = 2, },
+	{ .code	= MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, .max_pixels = 2, },
+	{ .code	= MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, .max_pixels = 2, },
+	{ .code	= MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, .max_pixels = 2, },
+	{ .code	= MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, .max_pixels = 4, },
+	{ .code	= MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, .max_pixels = 4, },
+	{ .code	= MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, .max_pixels = 4, },
+	{ .code	= MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, .max_pixels = 4, },
+	{ .code	= MEDIA_BUS_FMT_Y8_1X8,     .bpp = 8, .max_pixels = 4, },
+	{ .code	= MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, .max_pixels = 2, },
+	{ .code	= MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, .max_pixels = 2, },
+	{ .code	= MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, .max_pixels = 2, },
+	{ .code	= MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, .max_pixels = 2, },
+	{ .code	= MEDIA_BUS_FMT_RGB565_1X16,  .bpp = 16, .max_pixels = 1, },
+	{ .code	= MEDIA_BUS_FMT_RGB888_1X24,  .bpp = 24, .max_pixels = 1, },
+	{ .code	= MEDIA_BUS_FMT_BGR888_1X24,  .bpp = 24, .max_pixels = 1, },
 };
 
 static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code)
@@ -274,8 +282,10 @@  static int csi2rx_start(struct csi2rx_priv *csi2rx)
 
 		reset_control_deassert(csi2rx->pixel_rst[i]);
 
-		writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF,
-		       csi2rx->base + CSI2RX_STREAM_CFG_REG(i));
+		reg = CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF;
+		reg |= FIELD_PREP(CSI2RX_STREAM_CFG_NUM_PIXELS_MASK,
+				  csi2rx->num_pixels[i]);
+		writel(reg, csi2rx->base + CSI2RX_STREAM_CFG_REG(i));
 
 		/*
 		 * Enable one virtual channel. When multiple virtual channels
@@ -456,6 +466,34 @@  static int csi2rx_init_state(struct v4l2_subdev *subdev,
 	return csi2rx_set_fmt(subdev, state, &format);
 }
 
+int cdns_csi2rx_negotiate_ppc(struct v4l2_subdev *subdev, unsigned int pad,
+			      u8 *ppc)
+{
+	struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+	const struct csi2rx_fmt *csi_fmt;
+	struct v4l2_subdev_state *state;
+	struct v4l2_mbus_framefmt *fmt;
+	int ret = 0;
+
+	if (!ppc || pad < CSI2RX_PAD_SOURCE_STREAM0 || pad >= CSI2RX_PAD_MAX)
+		return -EINVAL;
+
+	state = v4l2_subdev_lock_and_get_active_state(subdev);
+	fmt = v4l2_subdev_state_get_format(state, pad);
+	csi_fmt = csi2rx_get_fmt_by_code(fmt->code);
+
+	/* Reduce requested PPC if it is too high */
+	*ppc = min(*ppc, csi_fmt->max_pixels);
+
+	v4l2_subdev_unlock_state(state);
+
+	csi2rx->num_pixels[pad - CSI2RX_PAD_SOURCE_STREAM0] =
+		CSI2RX_STREAM_CFG_NUM_PIXELS(*ppc);
+
+	return ret;
+}
+EXPORT_SYMBOL(cdns_csi2rx_negotiate_ppc);
+
 static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = {
 	.enum_mbus_code	= csi2rx_enum_mbus_code,
 	.get_fmt	= v4l2_subdev_get_fmt,
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.h b/drivers/media/platform/cadence/cdns-csi2rx.h
new file mode 100644
index 0000000000000000000000000000000000000000..128d47e8513c99c083f49e249e876be6d19389f6
--- /dev/null
+++ b/drivers/media/platform/cadence/cdns-csi2rx.h
@@ -0,0 +1,19 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef CDNS_CSI2RX_H
+#define CDNS_CSI2RX_H
+
+#include <media/v4l2-subdev.h>
+
+/**
+ * cdns_csi2rx_negotiate_ppc - Negotiate pixel-per-clock on output interface
+ *
+ * @subdev: point to &struct v4l2_subdev
+ * @pad: pad number of the source pad
+ * @ppc: pointer to requested pixel-per-clock value
+ *
+ * Returns 0 on success, negative error code otherwise.
+ */
+int cdns_csi2rx_negotiate_ppc(struct v4l2_subdev *subdev, unsigned int pad,
+			      u8 *ppc);
+
+#endif