Message ID | 20211017182449.64192-12-jacopo+renesas@jmondi.org (mailing list archive) |
---|---|
State | New |
Delegated to: | Kieran Bingham |
Headers | show |
Series | media: Add multiplexed support to R-Car CSI-2 and GMSL | expand |
Hi Jacopo, Thank you for the patch. On Sun, Oct 17, 2021 at 08:24:47PM +0200, Jacopo Mondi wrote: > Configure the CSI-2 receiver by inspecting the remote subdev frame_desc. > > Configure the link clock rate, field handling and CSI-2 Virtual Channel > and DT filtering using the frame descriptor retrieved from the > transmitter. > > This change also makes mandatory for any subdevice that operates with > the R-Car CSI-2 receiver to implement the .get_frame_desc() operation. > > [based on a patch from] > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> > [rework based on lates multistream support] > Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org> > --- > drivers/media/platform/rcar-vin/rcar-csi2.c | 145 +++++++++++++++----- > 1 file changed, 110 insertions(+), 35 deletions(-) > > diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c > index b60845b1e563..4ef7b9cb1ab7 100644 > --- a/drivers/media/platform/rcar-vin/rcar-csi2.c > +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c > @@ -69,10 +69,7 @@ struct rcar_csi2; > #define FLD_REG 0x1c > #define FLD_FLD_NUM(n) (((n) & 0xff) << 16) > #define FLD_DET_SEL(n) (((n) & 0x3) << 4) > -#define FLD_FLD_EN4 BIT(3) > -#define FLD_FLD_EN3 BIT(2) > -#define FLD_FLD_EN2 BIT(1) > -#define FLD_FLD_EN BIT(0) > +#define FLD_FLD_EN(n) BIT((n) & 0xf) > > /* Automatic Standby Control */ > #define ASTBY_REG 0x20 > @@ -339,6 +336,17 @@ static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) > return NULL; > } > > +static const struct rcar_csi2_format *rcsi2_datatype_to_fmt(unsigned int dt) > +{ > + unsigned int i; > + > + for (i = 0; i < ARRAY_SIZE(rcar_csi2_formats); i++) > + if (rcar_csi2_formats[i].datatype == dt) > + return &rcar_csi2_formats[i]; > + > + return NULL; > +} > + > enum rcar_csi2_pads { > RCAR_CSI2_SINK, > RCAR_CSI2_SOURCE_VC0, > @@ -370,8 +378,6 @@ struct rcar_csi2 { > struct v4l2_subdev *remote; > unsigned int remote_pad; > > - struct v4l2_mbus_framefmt mf; > - > struct mutex lock; > int stream_count; > > @@ -421,6 +427,32 @@ static int rcsi2_exit_standby(struct rcar_csi2 *priv) > return 0; > } > > +static int rcsi2_get_remote_frame_desc(struct rcar_csi2 *priv, > + struct v4l2_mbus_frame_desc *fd) > +{ > + struct media_pad *pad; > + int ret; > + > + if (!priv->remote) > + return -ENODEV; > + > + pad = media_entity_remote_pad(&priv->pads[RCAR_CSI2_SINK]); > + if (!pad) > + return -ENODEV; > + > + ret = v4l2_subdev_call(priv->remote, pad, get_frame_desc, > + pad->index, fd); > + if (ret) > + return ret; > + > + if (fd->type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { > + dev_err(priv->dev, "Frame desc do not describe CSI-2 link"); > + return -EINVAL; > + } > + > + return 0; > +} > + > static int rcsi2_wait_phy_start(struct rcar_csi2 *priv, > unsigned int lanes) > { > @@ -460,11 +492,14 @@ static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps) > return 0; > } > > -static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp, > - unsigned int lanes) > +static int rcsi2_calc_mbps(struct rcar_csi2 *priv, > + struct v4l2_mbus_frame_desc *fd, unsigned int lanes) > { > + const struct v4l2_mbus_frame_desc_entry_csi2 *csi2_desc; > + const struct rcar_csi2_format *format; > struct v4l2_subdev *source; > struct v4l2_ctrl *ctrl; > + unsigned int i; > u64 mbps; > > if (!priv->remote) > @@ -480,12 +515,30 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp, > return -EINVAL; > } > > + /* Verify that all remote streams send the same datatype. */ I don't think that's a good idea. Capturing image data and embedded data is a common use case, and those use different data types. > + csi2_desc = NULL; > + for (i = 0; i < fd->num_entries; i++) { > + struct v4l2_mbus_frame_desc_entry_csi2 *stream_desc; > + > + stream_desc = &fd->entry[i].bus.csi2; > + if (!csi2_desc) > + csi2_desc = stream_desc; > + > + if (csi2_desc->dt != stream_desc->dt) { > + dev_err(priv->dev, > + "Remote streams with different DT: %u - %u\n", > + csi2_desc->dt, stream_desc->dt); > + return -EINVAL; > + } > + } > + format = rcsi2_datatype_to_fmt(csi2_desc->dt); > + > /* > * Calculate the phypll in mbps. > * link_freq = (pixel_rate * bits_per_sample) / (2 * nr_of_lanes) > * bps = link_freq * 2 > */ > - mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp; > + mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * format->bpp; > do_div(mbps, lanes * 1000000); > > return mbps; > @@ -541,49 +594,64 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, > > static int rcsi2_start_receiver(struct rcar_csi2 *priv) > { > - const struct rcar_csi2_format *format; > + const struct v4l2_subdev_stream_configs *configs; > + struct v4l2_subdev_state *state; > u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0; > + struct v4l2_mbus_frame_desc fd; > unsigned int lanes; > unsigned int i; > int mbps, ret; > > - dev_dbg(priv->dev, "Input size (%ux%u%c)\n", > - priv->mf.width, priv->mf.height, > - priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i'); > - > - /* Code is validated in set_fmt. */ > - format = rcsi2_code_to_fmt(priv->mf.code); > + /* Get information about multiplexed link. */ > + ret = rcsi2_get_remote_frame_desc(priv, &fd); > + if (ret) > + return ret; > > - /* > - * Enable all supported CSI-2 channels with virtual channel and > - * data type matching. > - * > - * NOTE: It's not possible to get individual datatype for each > - * source virtual channel. Once this is possible in V4L2 > - * it should be used here. > - */ > - for (i = 0; i < priv->info->num_channels; i++) { > + for (i = 0; i < fd.num_entries; i++) { > + struct v4l2_mbus_frame_desc_entry *entry = &fd.entry[i]; > u32 vcdt_part; > > - vcdt_part = VCDT_SEL_VC(i) | VCDT_VCDTN_EN | VCDT_SEL_DTN_ON | > - VCDT_SEL_DT(format->datatype); > + vcdt_part = VCDT_SEL_VC(entry->bus.csi2.vc) | > + VCDT_VCDTN_EN | VCDT_SEL_DTN_ON | > + VCDT_SEL_DT(entry->bus.csi2.dt); > > /* Store in correct reg and offset. */ > - if (i < 2) > - vcdt |= vcdt_part << ((i % 2) * 16); > + if (entry->bus.csi2.vc < 2) > + vcdt |= vcdt_part << > + ((entry->bus.csi2.vc % 2) * 16); > else > - vcdt2 |= vcdt_part << ((i % 2) * 16); > + vcdt2 |= vcdt_part << > + ((entry->bus.csi2.vc % 2) * 16); > + > + dev_dbg(priv->dev, "VC: %d datatype: 0x%x\n", > + entry->bus.csi2.vc, entry->bus.csi2.dt); > } > > - if (priv->mf.field == V4L2_FIELD_ALTERNATE) { > - fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2 > - | FLD_FLD_EN; > + /* > + * Configure field handling inspecting the formats of the > + * sink pad streams. > + */ > + state = v4l2_subdev_lock_active_state(&priv->subdev); > + configs = &state->stream_configs; > + for (i = 0; i < configs->num_configs; ++i) { > + const struct v4l2_subdev_stream_config *config = configs->configs; > + > + if (config->pad != RCAR_CSI2_SINK) > + continue; > + > + if (config->fmt.field != V4L2_FIELD_ALTERNATE) > + continue; > > - if (priv->mf.height == 240) > + fld |= FLD_DET_SEL(1); > + fld |= FLD_FLD_EN(config->stream); > + > + /* PAL vs NTSC. */ > + if (config->fmt.height == 240) > fld |= FLD_FLD_NUM(0); > else > fld |= FLD_FLD_NUM(1); > } > + v4l2_subdev_unlock_state(state); > > /* > * Get the number of active data lanes inspecting the remote mbus > @@ -596,7 +664,7 @@ static int rcsi2_start_receiver(struct rcar_csi2 *priv) > phycnt = PHYCNT_ENABLECLK; > phycnt |= (1 << lanes) - 1; > > - mbps = rcsi2_calc_mbps(priv, format->bpp, lanes); > + mbps = rcsi2_calc_mbps(priv, &fd, lanes); > if (mbps < 0) > return mbps; > > @@ -894,6 +962,13 @@ static int rcsi2_notify_bound(struct v4l2_async_notifier *notifier, > struct rcar_csi2 *priv = notifier_to_csi2(notifier); > int pad; > > + if (!v4l2_subdev_has_op(subdev, pad, get_frame_desc)) { > + dev_err(priv->dev, > + "Subdev %s bound failed: missing get_frame_desc()\n", > + subdev->name); > + return -EINVAL; > + } > + > pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, > MEDIA_PAD_FL_SOURCE); > if (pad < 0) {
diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index b60845b1e563..4ef7b9cb1ab7 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -69,10 +69,7 @@ struct rcar_csi2; #define FLD_REG 0x1c #define FLD_FLD_NUM(n) (((n) & 0xff) << 16) #define FLD_DET_SEL(n) (((n) & 0x3) << 4) -#define FLD_FLD_EN4 BIT(3) -#define FLD_FLD_EN3 BIT(2) -#define FLD_FLD_EN2 BIT(1) -#define FLD_FLD_EN BIT(0) +#define FLD_FLD_EN(n) BIT((n) & 0xf) /* Automatic Standby Control */ #define ASTBY_REG 0x20 @@ -339,6 +336,17 @@ static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) return NULL; } +static const struct rcar_csi2_format *rcsi2_datatype_to_fmt(unsigned int dt) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rcar_csi2_formats); i++) + if (rcar_csi2_formats[i].datatype == dt) + return &rcar_csi2_formats[i]; + + return NULL; +} + enum rcar_csi2_pads { RCAR_CSI2_SINK, RCAR_CSI2_SOURCE_VC0, @@ -370,8 +378,6 @@ struct rcar_csi2 { struct v4l2_subdev *remote; unsigned int remote_pad; - struct v4l2_mbus_framefmt mf; - struct mutex lock; int stream_count; @@ -421,6 +427,32 @@ static int rcsi2_exit_standby(struct rcar_csi2 *priv) return 0; } +static int rcsi2_get_remote_frame_desc(struct rcar_csi2 *priv, + struct v4l2_mbus_frame_desc *fd) +{ + struct media_pad *pad; + int ret; + + if (!priv->remote) + return -ENODEV; + + pad = media_entity_remote_pad(&priv->pads[RCAR_CSI2_SINK]); + if (!pad) + return -ENODEV; + + ret = v4l2_subdev_call(priv->remote, pad, get_frame_desc, + pad->index, fd); + if (ret) + return ret; + + if (fd->type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_err(priv->dev, "Frame desc do not describe CSI-2 link"); + return -EINVAL; + } + + return 0; +} + static int rcsi2_wait_phy_start(struct rcar_csi2 *priv, unsigned int lanes) { @@ -460,11 +492,14 @@ static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps) return 0; } -static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp, - unsigned int lanes) +static int rcsi2_calc_mbps(struct rcar_csi2 *priv, + struct v4l2_mbus_frame_desc *fd, unsigned int lanes) { + const struct v4l2_mbus_frame_desc_entry_csi2 *csi2_desc; + const struct rcar_csi2_format *format; struct v4l2_subdev *source; struct v4l2_ctrl *ctrl; + unsigned int i; u64 mbps; if (!priv->remote) @@ -480,12 +515,30 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp, return -EINVAL; } + /* Verify that all remote streams send the same datatype. */ + csi2_desc = NULL; + for (i = 0; i < fd->num_entries; i++) { + struct v4l2_mbus_frame_desc_entry_csi2 *stream_desc; + + stream_desc = &fd->entry[i].bus.csi2; + if (!csi2_desc) + csi2_desc = stream_desc; + + if (csi2_desc->dt != stream_desc->dt) { + dev_err(priv->dev, + "Remote streams with different DT: %u - %u\n", + csi2_desc->dt, stream_desc->dt); + return -EINVAL; + } + } + format = rcsi2_datatype_to_fmt(csi2_desc->dt); + /* * Calculate the phypll in mbps. * link_freq = (pixel_rate * bits_per_sample) / (2 * nr_of_lanes) * bps = link_freq * 2 */ - mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp; + mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * format->bpp; do_div(mbps, lanes * 1000000); return mbps; @@ -541,49 +594,64 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, static int rcsi2_start_receiver(struct rcar_csi2 *priv) { - const struct rcar_csi2_format *format; + const struct v4l2_subdev_stream_configs *configs; + struct v4l2_subdev_state *state; u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0; + struct v4l2_mbus_frame_desc fd; unsigned int lanes; unsigned int i; int mbps, ret; - dev_dbg(priv->dev, "Input size (%ux%u%c)\n", - priv->mf.width, priv->mf.height, - priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i'); - - /* Code is validated in set_fmt. */ - format = rcsi2_code_to_fmt(priv->mf.code); + /* Get information about multiplexed link. */ + ret = rcsi2_get_remote_frame_desc(priv, &fd); + if (ret) + return ret; - /* - * Enable all supported CSI-2 channels with virtual channel and - * data type matching. - * - * NOTE: It's not possible to get individual datatype for each - * source virtual channel. Once this is possible in V4L2 - * it should be used here. - */ - for (i = 0; i < priv->info->num_channels; i++) { + for (i = 0; i < fd.num_entries; i++) { + struct v4l2_mbus_frame_desc_entry *entry = &fd.entry[i]; u32 vcdt_part; - vcdt_part = VCDT_SEL_VC(i) | VCDT_VCDTN_EN | VCDT_SEL_DTN_ON | - VCDT_SEL_DT(format->datatype); + vcdt_part = VCDT_SEL_VC(entry->bus.csi2.vc) | + VCDT_VCDTN_EN | VCDT_SEL_DTN_ON | + VCDT_SEL_DT(entry->bus.csi2.dt); /* Store in correct reg and offset. */ - if (i < 2) - vcdt |= vcdt_part << ((i % 2) * 16); + if (entry->bus.csi2.vc < 2) + vcdt |= vcdt_part << + ((entry->bus.csi2.vc % 2) * 16); else - vcdt2 |= vcdt_part << ((i % 2) * 16); + vcdt2 |= vcdt_part << + ((entry->bus.csi2.vc % 2) * 16); + + dev_dbg(priv->dev, "VC: %d datatype: 0x%x\n", + entry->bus.csi2.vc, entry->bus.csi2.dt); } - if (priv->mf.field == V4L2_FIELD_ALTERNATE) { - fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2 - | FLD_FLD_EN; + /* + * Configure field handling inspecting the formats of the + * sink pad streams. + */ + state = v4l2_subdev_lock_active_state(&priv->subdev); + configs = &state->stream_configs; + for (i = 0; i < configs->num_configs; ++i) { + const struct v4l2_subdev_stream_config *config = configs->configs; + + if (config->pad != RCAR_CSI2_SINK) + continue; + + if (config->fmt.field != V4L2_FIELD_ALTERNATE) + continue; - if (priv->mf.height == 240) + fld |= FLD_DET_SEL(1); + fld |= FLD_FLD_EN(config->stream); + + /* PAL vs NTSC. */ + if (config->fmt.height == 240) fld |= FLD_FLD_NUM(0); else fld |= FLD_FLD_NUM(1); } + v4l2_subdev_unlock_state(state); /* * Get the number of active data lanes inspecting the remote mbus @@ -596,7 +664,7 @@ static int rcsi2_start_receiver(struct rcar_csi2 *priv) phycnt = PHYCNT_ENABLECLK; phycnt |= (1 << lanes) - 1; - mbps = rcsi2_calc_mbps(priv, format->bpp, lanes); + mbps = rcsi2_calc_mbps(priv, &fd, lanes); if (mbps < 0) return mbps; @@ -894,6 +962,13 @@ static int rcsi2_notify_bound(struct v4l2_async_notifier *notifier, struct rcar_csi2 *priv = notifier_to_csi2(notifier); int pad; + if (!v4l2_subdev_has_op(subdev, pad, get_frame_desc)) { + dev_err(priv->dev, + "Subdev %s bound failed: missing get_frame_desc()\n", + subdev->name); + return -EINVAL; + } + pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, MEDIA_PAD_FL_SOURCE); if (pad < 0) {