[13/19] media: max9286: Implement support for LINK_FREQ
Commit Message
The max9286 now supports multiple streams. For this reason, reporting
the data bitrate through the PIXEL_RATE control is not meaningful
anymore (or better, is less meaningful that what it already was).
In order to support multiplexed receivers, which want to be informed
about the CSI-2 link frequency, implement support for the
V4L2_CID_LINK_FREQ control.
Record in the driver a set of pre-computed link frequencies in order to
support the two currently supported GMSL camera modules (rdacm20 and
rdacm21) and all the supported number of active channels and CSI-2 data
lanes.
Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
drivers/media/i2c/max9286.c | 143 ++++++++++++++++++++++++++++++++++--
1 file changed, 136 insertions(+), 7 deletions(-)
Comments
Hi Jacopo,
Thank you for the patch.
On Tue, Apr 30, 2024 at 12:39:49PM +0200, Jacopo Mondi wrote:
> The max9286 now supports multiple streams. For this reason, reporting
> the data bitrate through the PIXEL_RATE control is not meaningful
> anymore (or better, is less meaningful that what it already was).
>
> In order to support multiplexed receivers, which want to be informed
> about the CSI-2 link frequency, implement support for the
> V4L2_CID_LINK_FREQ control.
I think
https://lore.kernel.org/linux-media/20240429190852.1008003-3-sakari.ailus@linux.intel.com
may be more suitable for this use case.
>
> Record in the driver a set of pre-computed link frequencies in order to
> support the two currently supported GMSL camera modules (rdacm20 and
s/camera modules/cameras/
> rdacm21) and all the supported number of active channels and CSI-2 data
> lanes.
>
> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> ---
> drivers/media/i2c/max9286.c | 143 ++++++++++++++++++++++++++++++++++--
> 1 file changed, 136 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> index 4b4f4c03c10a..0e08a1041eed 100644
> --- a/drivers/media/i2c/max9286.c
> +++ b/drivers/media/i2c/max9286.c
> @@ -148,6 +148,12 @@ struct max9286_format_info {
> u32 code;
> /* The gmsl data format configuration. */
> u8 gmsl_dt;
> + /*
> + * gmsl_bpp is the pixel format bit-width per word as reported in
> + * table 20. and it is used to calculate the GMSL bandwidth and CSI-2
Table 20 of what ?
> + * link speed.
> + */
> + u8 gmsl_bpp;
> /* The format bpp, used for stride calculation. */
> u8 bpp;
> /* The Data Type identifier as defined by the MIPI CSI-2 standard. */
> @@ -201,6 +207,7 @@ struct max9286_priv {
>
> struct v4l2_ctrl_handler ctrls;
> struct v4l2_ctrl *pixelrate_ctrl;
> + struct v4l2_ctrl *link_freq;
> unsigned int pixelrate;
>
> unsigned int nsources;
> @@ -242,41 +249,49 @@ static const struct max9286_format_info max9286_formats[] = {
> {
> .code = MEDIA_BUS_FMT_UYVY8_1X16,
> .gmsl_dt = MAX9286_DATATYPE_YUV422_8BIT,
> + .gmsl_bpp = 8,
> .bpp = 16,
> .mipi_dt = MIPI_CSI2_DT_YUV422_8B,
> }, {
> .code = MEDIA_BUS_FMT_VYUY8_1X16,
> .gmsl_dt = MAX9286_DATATYPE_YUV422_8BIT,
> + .gmsl_bpp = 8,
> .bpp = 16,
> .mipi_dt = MIPI_CSI2_DT_YUV422_8B,
> }, {
> .code = MEDIA_BUS_FMT_YUYV8_1X16,
> .gmsl_dt = MAX9286_DATATYPE_YUV422_8BIT,
> + .gmsl_bpp = 8,
> .bpp = 16,
> .mipi_dt = MIPI_CSI2_DT_YUV422_8B,
> }, {
> .code = MEDIA_BUS_FMT_YVYU8_1X16,
> .gmsl_dt = MAX9286_DATATYPE_YUV422_8BIT,
> + .gmsl_bpp = 8,
> .bpp = 16,
> .mipi_dt = MIPI_CSI2_DT_YUV422_8B,
> }, {
> .code = MEDIA_BUS_FMT_SBGGR12_1X12,
> .gmsl_dt = MAX9286_DATATYPE_RAW12,
> + .gmsl_bpp = 12,
> .bpp = 12,
> .mipi_dt = MIPI_CSI2_DT_RAW12,
> }, {
> .code = MEDIA_BUS_FMT_SGBRG12_1X12,
> .gmsl_dt = MAX9286_DATATYPE_RAW12,
> + .gmsl_bpp = 12,
> .bpp = 12,
> .mipi_dt = MIPI_CSI2_DT_RAW12,
> }, {
> .code = MEDIA_BUS_FMT_SGRBG12_1X12,
> .gmsl_dt = MAX9286_DATATYPE_RAW12,
> + .gmsl_bpp = 12,
> .bpp = 12,
> .mipi_dt = MIPI_CSI2_DT_RAW12,
> }, {
> .code = MEDIA_BUS_FMT_SRGGB12_1X12,
> .gmsl_dt = MAX9286_DATATYPE_RAW12,
> + .gmsl_bpp = 12,
> .bpp = 12,
> .mipi_dt = MIPI_CSI2_DT_RAW12,
> },
> @@ -293,6 +308,60 @@ static const struct max9286_i2c_speed max9286_i2c_speeds[] = {
> { .rate = 837000, .mstbt = MAX9286_I2CMSTBT_837KBPS },
> };
>
> +/*
> + * According to the chip manual the CSI-2 link frequency is calculated as:
> + *
> + * f-CSI2 = pclk * ((width * channels) / lanes)
> + *
> + * Where:
> + * pclk = serializer input word rate
> + * width = pixel format bits per word (gmsl_bpp in max9286_format_info)
> + * channels = number of enabled links
> + * lanes = number of CSI-2 data lanes
> + *
> + * The following link frequencies have been computed by combining the following
> + * parameters:
> + *
> + * pixel_rates[] = { 44000000, 55000000 }; for RDACM20 and RDACM21
> + * bpps[] = { 8, 12 }; for the formats supported by the max9286 driver
> + * channels[] = { 1, 2, 3, 4 };
> + * lanes[] = { 1, 2, 3, 4 };
> + *
> + * and by filtering out all results that would lead to an invalid
> + * link_frequency, as the MAX9286 has a CSI-2 bandwidth limit of 1200Mbps per
> + * lane (600MHz clock frequency).
> + *
> + * If a new serializer with a different pixel rate, or a new image format with
> + * a different bpp is supported, the below table needs to be updated.
> + */
> +static const s64 max9286_link_freqs[] = {
> + 88000000,
> + 110000000,
> + 132000000,
> + 165000000,
> + 176000000,
> + 220000000,
> + 264000000,
> + 275000000,
> + 330000000,
> + 352000000,
> + 396000000,
> + 440000000,
> + 495000000,
> + 528000000,
> + 550000000,
> +};
> +
> +static int max9286_get_link_freq_index(s64 link_freq)
> +{
> + for (unsigned int i = 0; i < ARRAY_SIZE(max9286_link_freqs); ++i) {
> + if (max9286_link_freqs[i] == link_freq)
> + return i;
> + }
> +
> + return -EINVAL;
> +}
> +
> /* -----------------------------------------------------------------------------
> * I2C IO
> */
> @@ -636,10 +705,18 @@ static void max9286_set_fsync_period(struct max9286_priv *priv,
> * V4L2 Subdev
> */
>
> -static int max9286_set_pixelrate(struct max9286_priv *priv)
> +static int max9286_set_pixelrate(struct max9286_priv *priv,
> + struct v4l2_subdev_state *state,
> + unsigned int link_index)
> {
> + const struct max9286_format_info *info;
> struct max9286_source *source = NULL;
> + struct v4l2_mbus_framefmt *fmt;
> + unsigned int num_sources = 0;
> + int link_freq_index;
> u64 pixelrate = 0;
> + s64 link_freq;
> + int ret;
>
> for_each_source(priv, source) {
> struct v4l2_ctrl *ctrl;
> @@ -662,6 +739,8 @@ static int max9286_set_pixelrate(struct max9286_priv *priv)
> "Unable to calculate pixel rate\n");
> return -EINVAL;
> }
> +
> + num_sources++;
> }
>
> if (!pixelrate) {
> @@ -670,14 +749,33 @@ static int max9286_set_pixelrate(struct max9286_priv *priv)
> return -EINVAL;
> }
>
> - priv->pixelrate = pixelrate;
> + fmt = v4l2_subdev_state_get_format(state, link_index);
> + info = max9286_get_format_info(fmt->code);
> + if (WARN_ON(!info))
> + return -EINVAL;
> +
> + /*
> + * Compute the link frequency and find the right index to update
> + * the menu control with.
> + */
> + link_freq = pixelrate * ((info->gmsl_bpp * num_sources) /
> + priv->csi2_data_lanes);
> +
> + link_freq_index = max9286_get_link_freq_index(link_freq);
> + if (WARN_ON(link_freq_index < 0))
> + return -EINVAL;
>
> /*
> * The CSI-2 transmitter pixel rate is the single source rate multiplied
> * by the number of available sources.
> */
> - return v4l2_ctrl_s_ctrl_int64(priv->pixelrate_ctrl,
> - pixelrate * priv->nsources);
> + ret = __v4l2_ctrl_s_ctrl_int64(priv->pixelrate_ctrl,
> + pixelrate * priv->nsources);
> + if (ret)
> + return -EINVAL;
> + priv->pixelrate = pixelrate;
> +
> + return __v4l2_ctrl_s_ctrl(priv->link_freq, link_freq_index);
> }
>
> static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
> @@ -687,6 +785,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
> struct max9286_priv *priv = sd_to_max9286(notifier->sd);
> struct max9286_source *source = to_max9286_asd(asd)->source;
> unsigned int index = to_index(priv, source);
> + struct v4l2_subdev_state *state;
> unsigned int src_pad;
> int ret;
>
> @@ -739,7 +838,11 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
> max9286_check_config_link(priv, priv->source_mask);
> max9286_configure_i2c(priv, false);
>
> - return max9286_set_pixelrate(priv);
> + state = v4l2_subdev_lock_and_get_active_state(notifier->sd);
> + ret = max9286_set_pixelrate(priv, state, index);
> + v4l2_subdev_unlock_state(state);
> +
> + return ret;
> }
>
> static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
> @@ -926,7 +1029,9 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
> struct v4l2_subdev_state *state,
> struct v4l2_subdev_format *format)
> {
> + struct max9286_priv *priv = sd_to_max9286(sd);
> const struct max9286_format_info *info;
> + int ret;
>
> /*
> * Disable setting format on the source pad: format is propagated
> @@ -949,7 +1054,22 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
> *v4l2_subdev_state_get_opposite_stream_format(state, format->pad, 0) =
> format->format;
>
> - return 0;
> + /*
> + * Update the pixel rate and link_rate controls. We cannot do that in
> + * s_stream() as the receiver is likely fetching the frame_desc and
> + * reading the link_rate control before calling this driver's s_stream.
> + *
> + * Update the controls here.
> + *
> + * TODO: Once the routing table will be made writable and links
> + * can be enabled/disabled, the controls need to be updated there too.
> + */
> + ret = max9286_set_pixelrate(priv, state, format->pad);
> + if (ret)
> + dev_err(&priv->client->dev,
> + "Unsupported format/lanes/channel combination: clock rate too high");
> +
> + return ret;
> }
>
> static int max9286_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
> @@ -1068,6 +1188,7 @@ static int max9286_s_ctrl(struct v4l2_ctrl *ctrl)
> {
> switch (ctrl->id) {
> case V4L2_CID_PIXEL_RATE:
> + case V4L2_CID_LINK_FREQ:
> return 0;
> default:
> return -EINVAL;
> @@ -1097,12 +1218,20 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
> priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
> V4L2_SUBDEV_FL_STREAMS;
>
> - v4l2_ctrl_handler_init(&priv->ctrls, 1);
> + v4l2_ctrl_handler_init(&priv->ctrls, 2);
> priv->pixelrate_ctrl = v4l2_ctrl_new_std(&priv->ctrls,
> &max9286_ctrl_ops,
> V4L2_CID_PIXEL_RATE,
> 1, INT_MAX, 1, 50000000);
>
> + priv->link_freq =
> + v4l2_ctrl_new_int_menu(&priv->ctrls, &max9286_ctrl_ops,
> + V4L2_CID_LINK_FREQ,
> + ARRAY_SIZE(max9286_link_freqs) - 1, 0,
> + max9286_link_freqs);
> + if (priv->link_freq)
> + priv->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> priv->sd.ctrl_handler = &priv->ctrls;
> ret = priv->ctrls.error;
> if (ret)
@@ -148,6 +148,12 @@ struct max9286_format_info {
u32 code;
/* The gmsl data format configuration. */
u8 gmsl_dt;
+ /*
+ * gmsl_bpp is the pixel format bit-width per word as reported in
+ * table 20. and it is used to calculate the GMSL bandwidth and CSI-2
+ * link speed.
+ */
+ u8 gmsl_bpp;
/* The format bpp, used for stride calculation. */
u8 bpp;
/* The Data Type identifier as defined by the MIPI CSI-2 standard. */
@@ -201,6 +207,7 @@ struct max9286_priv {
struct v4l2_ctrl_handler ctrls;
struct v4l2_ctrl *pixelrate_ctrl;
+ struct v4l2_ctrl *link_freq;
unsigned int pixelrate;
unsigned int nsources;
@@ -242,41 +249,49 @@ static const struct max9286_format_info max9286_formats[] = {
{
.code = MEDIA_BUS_FMT_UYVY8_1X16,
.gmsl_dt = MAX9286_DATATYPE_YUV422_8BIT,
+ .gmsl_bpp = 8,
.bpp = 16,
.mipi_dt = MIPI_CSI2_DT_YUV422_8B,
}, {
.code = MEDIA_BUS_FMT_VYUY8_1X16,
.gmsl_dt = MAX9286_DATATYPE_YUV422_8BIT,
+ .gmsl_bpp = 8,
.bpp = 16,
.mipi_dt = MIPI_CSI2_DT_YUV422_8B,
}, {
.code = MEDIA_BUS_FMT_YUYV8_1X16,
.gmsl_dt = MAX9286_DATATYPE_YUV422_8BIT,
+ .gmsl_bpp = 8,
.bpp = 16,
.mipi_dt = MIPI_CSI2_DT_YUV422_8B,
}, {
.code = MEDIA_BUS_FMT_YVYU8_1X16,
.gmsl_dt = MAX9286_DATATYPE_YUV422_8BIT,
+ .gmsl_bpp = 8,
.bpp = 16,
.mipi_dt = MIPI_CSI2_DT_YUV422_8B,
}, {
.code = MEDIA_BUS_FMT_SBGGR12_1X12,
.gmsl_dt = MAX9286_DATATYPE_RAW12,
+ .gmsl_bpp = 12,
.bpp = 12,
.mipi_dt = MIPI_CSI2_DT_RAW12,
}, {
.code = MEDIA_BUS_FMT_SGBRG12_1X12,
.gmsl_dt = MAX9286_DATATYPE_RAW12,
+ .gmsl_bpp = 12,
.bpp = 12,
.mipi_dt = MIPI_CSI2_DT_RAW12,
}, {
.code = MEDIA_BUS_FMT_SGRBG12_1X12,
.gmsl_dt = MAX9286_DATATYPE_RAW12,
+ .gmsl_bpp = 12,
.bpp = 12,
.mipi_dt = MIPI_CSI2_DT_RAW12,
}, {
.code = MEDIA_BUS_FMT_SRGGB12_1X12,
.gmsl_dt = MAX9286_DATATYPE_RAW12,
+ .gmsl_bpp = 12,
.bpp = 12,
.mipi_dt = MIPI_CSI2_DT_RAW12,
},
@@ -293,6 +308,60 @@ static const struct max9286_i2c_speed max9286_i2c_speeds[] = {
{ .rate = 837000, .mstbt = MAX9286_I2CMSTBT_837KBPS },
};
+/*
+ * According to the chip manual the CSI-2 link frequency is calculated as:
+ *
+ * f-CSI2 = pclk * ((width * channels) / lanes)
+ *
+ * Where:
+ * pclk = serializer input word rate
+ * width = pixel format bits per word (gmsl_bpp in max9286_format_info)
+ * channels = number of enabled links
+ * lanes = number of CSI-2 data lanes
+ *
+ * The following link frequencies have been computed by combining the following
+ * parameters:
+ *
+ * pixel_rates[] = { 44000000, 55000000 }; for RDACM20 and RDACM21
+ * bpps[] = { 8, 12 }; for the formats supported by the max9286 driver
+ * channels[] = { 1, 2, 3, 4 };
+ * lanes[] = { 1, 2, 3, 4 };
+ *
+ * and by filtering out all results that would lead to an invalid
+ * link_frequency, as the MAX9286 has a CSI-2 bandwidth limit of 1200Mbps per
+ * lane (600MHz clock frequency).
+ *
+ * If a new serializer with a different pixel rate, or a new image format with
+ * a different bpp is supported, the below table needs to be updated.
+ */
+static const s64 max9286_link_freqs[] = {
+ 88000000,
+ 110000000,
+ 132000000,
+ 165000000,
+ 176000000,
+ 220000000,
+ 264000000,
+ 275000000,
+ 330000000,
+ 352000000,
+ 396000000,
+ 440000000,
+ 495000000,
+ 528000000,
+ 550000000,
+};
+
+static int max9286_get_link_freq_index(s64 link_freq)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(max9286_link_freqs); ++i) {
+ if (max9286_link_freqs[i] == link_freq)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
/* -----------------------------------------------------------------------------
* I2C IO
*/
@@ -636,10 +705,18 @@ static void max9286_set_fsync_period(struct max9286_priv *priv,
* V4L2 Subdev
*/
-static int max9286_set_pixelrate(struct max9286_priv *priv)
+static int max9286_set_pixelrate(struct max9286_priv *priv,
+ struct v4l2_subdev_state *state,
+ unsigned int link_index)
{
+ const struct max9286_format_info *info;
struct max9286_source *source = NULL;
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int num_sources = 0;
+ int link_freq_index;
u64 pixelrate = 0;
+ s64 link_freq;
+ int ret;
for_each_source(priv, source) {
struct v4l2_ctrl *ctrl;
@@ -662,6 +739,8 @@ static int max9286_set_pixelrate(struct max9286_priv *priv)
"Unable to calculate pixel rate\n");
return -EINVAL;
}
+
+ num_sources++;
}
if (!pixelrate) {
@@ -670,14 +749,33 @@ static int max9286_set_pixelrate(struct max9286_priv *priv)
return -EINVAL;
}
- priv->pixelrate = pixelrate;
+ fmt = v4l2_subdev_state_get_format(state, link_index);
+ info = max9286_get_format_info(fmt->code);
+ if (WARN_ON(!info))
+ return -EINVAL;
+
+ /*
+ * Compute the link frequency and find the right index to update
+ * the menu control with.
+ */
+ link_freq = pixelrate * ((info->gmsl_bpp * num_sources) /
+ priv->csi2_data_lanes);
+
+ link_freq_index = max9286_get_link_freq_index(link_freq);
+ if (WARN_ON(link_freq_index < 0))
+ return -EINVAL;
/*
* The CSI-2 transmitter pixel rate is the single source rate multiplied
* by the number of available sources.
*/
- return v4l2_ctrl_s_ctrl_int64(priv->pixelrate_ctrl,
- pixelrate * priv->nsources);
+ ret = __v4l2_ctrl_s_ctrl_int64(priv->pixelrate_ctrl,
+ pixelrate * priv->nsources);
+ if (ret)
+ return -EINVAL;
+ priv->pixelrate = pixelrate;
+
+ return __v4l2_ctrl_s_ctrl(priv->link_freq, link_freq_index);
}
static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
@@ -687,6 +785,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
struct max9286_priv *priv = sd_to_max9286(notifier->sd);
struct max9286_source *source = to_max9286_asd(asd)->source;
unsigned int index = to_index(priv, source);
+ struct v4l2_subdev_state *state;
unsigned int src_pad;
int ret;
@@ -739,7 +838,11 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
max9286_check_config_link(priv, priv->source_mask);
max9286_configure_i2c(priv, false);
- return max9286_set_pixelrate(priv);
+ state = v4l2_subdev_lock_and_get_active_state(notifier->sd);
+ ret = max9286_set_pixelrate(priv, state, index);
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
}
static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
@@ -926,7 +1029,9 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
+ struct max9286_priv *priv = sd_to_max9286(sd);
const struct max9286_format_info *info;
+ int ret;
/*
* Disable setting format on the source pad: format is propagated
@@ -949,7 +1054,22 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
*v4l2_subdev_state_get_opposite_stream_format(state, format->pad, 0) =
format->format;
- return 0;
+ /*
+ * Update the pixel rate and link_rate controls. We cannot do that in
+ * s_stream() as the receiver is likely fetching the frame_desc and
+ * reading the link_rate control before calling this driver's s_stream.
+ *
+ * Update the controls here.
+ *
+ * TODO: Once the routing table will be made writable and links
+ * can be enabled/disabled, the controls need to be updated there too.
+ */
+ ret = max9286_set_pixelrate(priv, state, format->pad);
+ if (ret)
+ dev_err(&priv->client->dev,
+ "Unsupported format/lanes/channel combination: clock rate too high");
+
+ return ret;
}
static int max9286_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
@@ -1068,6 +1188,7 @@ static int max9286_s_ctrl(struct v4l2_ctrl *ctrl)
{
switch (ctrl->id) {
case V4L2_CID_PIXEL_RATE:
+ case V4L2_CID_LINK_FREQ:
return 0;
default:
return -EINVAL;
@@ -1097,12 +1218,20 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
V4L2_SUBDEV_FL_STREAMS;
- v4l2_ctrl_handler_init(&priv->ctrls, 1);
+ v4l2_ctrl_handler_init(&priv->ctrls, 2);
priv->pixelrate_ctrl = v4l2_ctrl_new_std(&priv->ctrls,
&max9286_ctrl_ops,
V4L2_CID_PIXEL_RATE,
1, INT_MAX, 1, 50000000);
+ priv->link_freq =
+ v4l2_ctrl_new_int_menu(&priv->ctrls, &max9286_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(max9286_link_freqs) - 1, 0,
+ max9286_link_freqs);
+ if (priv->link_freq)
+ priv->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
priv->sd.ctrl_handler = &priv->ctrls;
ret = priv->ctrls.error;
if (ret)