Re: [PATCH V2 2/2] media: i2c: imx219: Support four-lane operation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Jun 21, 2022 at 11:05 AM Dave Stevenson
<dave.stevenson@xxxxxxxxxxxxxxx> wrote:
>
> Hi Adam
>
> On Thu, 16 Jun 2022 at 13:31, Adam Ford <aford173@xxxxxxxxx> wrote:
> >
> > The imx219 camera is capable of either two-lane or four-lane
> > operation.  When operating in four-lane, both the pixel rate and
> > link frequency change. Regardless of the mode, however, both
> > frequencies remain fixed.
> >
> > Helper functions are needed to read and set pixel and link frequencies
> > which also reduces the number of fixed registers in the table of modes.
> >
> > Since the link frequency and number of lanes is extracted from the
> > endpoint, move the endpoint handling into the probe function and
> > out of the imx219_check_hwcfg.  This simplifies the imx219_check_hwcfg
> > just a bit.
> >
> > Signed-off-by: Adam Ford <aford173@xxxxxxxxx>
> > ---
> > V2:  Replace if-else statements with ternary operator
> >      Fix 4-lane Link Rate.
> >      Fix checking the link rate so only the link rate for
> >      the given number of lanes is permitted.
> >
> > diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
> > index a43eed143999..0d9004a5c4d2 100644
> > --- a/drivers/media/i2c/imx219.c
> > +++ b/drivers/media/i2c/imx219.c
> > @@ -42,10 +42,16 @@
> >  /* External clock frequency is 24.0M */
> >  #define IMX219_XCLK_FREQ               24000000
> >
> > -/* Pixel rate is fixed at 182.4M for all the modes */
> > +/* Pixel rate is fixed for all the modes */
> >  #define IMX219_PIXEL_RATE              182400000
> > +#define IMX219_PIXEL_RATE_4LANE                280800000
> >
> >  #define IMX219_DEFAULT_LINK_FREQ       456000000
> > +#define IMX219_DEFAULT_LINK_FREQ_4LANE 363000000
> > +
> > +#define IMX219_REG_CSI_LANE_MODE       0x0114
> > +#define IMX219_CSI_2_LANE_MODE         0x01
> > +#define IMX219_CSI_4_LANE_MODE         0x03
> >
> >  /* V_TIMING internal */
> >  #define IMX219_REG_VTS                 0x0160
> > @@ -306,6 +312,10 @@ static const s64 imx219_link_freq_menu[] = {
> >         IMX219_DEFAULT_LINK_FREQ,
> >  };
> >
> > +static const s64 imx219_link_freq_4lane_menu[] = {
> > +       IMX219_DEFAULT_LINK_FREQ_4LANE,
> > +};
> > +
> >  static const char * const imx219_test_pattern_menu[] = {
> >         "Disabled",
> >         "Color Bars",
> > @@ -481,6 +491,9 @@ struct imx219 {
> >
> >         /* Streaming on/off */
> >         bool streaming;
> > +
> > +       /* Two or Four lanes */
> > +       u8 lanes;
> >  };
> >
> >  static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
> > @@ -943,6 +956,13 @@ static int imx219_get_selection(struct v4l2_subdev *sd,
> >         return -EINVAL;
> >  }
> >
> > +static int imx219_configure_lanes(struct imx219 *imx219)
> > +{
> > +       return imx219_write_reg(imx219, IMX219_REG_CSI_LANE_MODE,
> > +                               IMX219_REG_VALUE_08BIT, (imx219->lanes == 2) ?
> > +                               IMX219_CSI_2_LANE_MODE : IMX219_CSI_4_LANE_MODE);
> > +};
> > +
> >  static int imx219_start_streaming(struct imx219 *imx219)
> >  {
> >         struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
> > @@ -960,6 +980,13 @@ static int imx219_start_streaming(struct imx219 *imx219)
> >                 goto err_rpm_put;
> >         }
> >
> > +       /* Configure two or four Lane mode */
> > +       ret = imx219_configure_lanes(imx219);
> > +       if (ret) {
> > +               dev_err(&client->dev, "%s failed to configure lanes\n", __func__);
> > +               goto err_rpm_put;
> > +       }
> > +
> >         /* Apply default values of current mode */
> >         reg_list = &imx219->mode->reg_list;
> >         ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
> > @@ -1191,6 +1218,11 @@ static const struct v4l2_subdev_internal_ops imx219_internal_ops = {
> >         .open = imx219_open,
> >  };
> >
> > +static unsigned long imx219_get_pixel_rate(struct imx219 *imx219)
> > +{
> > +       return (imx219->lanes == 2) ? IMX219_PIXEL_RATE : IMX219_PIXEL_RATE_4LANE;
> > +}
> > +
> >  /* Initialize control handlers */
> >  static int imx219_init_controls(struct imx219 *imx219)
> >  {
> > @@ -1212,15 +1244,16 @@ static int imx219_init_controls(struct imx219 *imx219)
> >         /* By default, PIXEL_RATE is read only */
> >         imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
> >                                                V4L2_CID_PIXEL_RATE,
> > -                                              IMX219_PIXEL_RATE,
> > -                                              IMX219_PIXEL_RATE, 1,
> > -                                              IMX219_PIXEL_RATE);
> > +                                              imx219_get_pixel_rate(imx219),
> > +                                              imx219_get_pixel_rate(imx219), 1,
> > +                                              imx219_get_pixel_rate(imx219));
> >
> >         imx219->link_freq =
> >                 v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops,
> >                                        V4L2_CID_LINK_FREQ,
> >                                        ARRAY_SIZE(imx219_link_freq_menu) - 1, 0,
> > -                                      imx219_link_freq_menu);
> > +                                      (imx219->lanes == 2) ? imx219_link_freq_menu :
> > +                                      imx219_link_freq_4lane_menu);
> >         if (imx219->link_freq)
> >                 imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> >
> > @@ -1315,67 +1348,76 @@ static void imx219_free_controls(struct imx219 *imx219)
> >         mutex_destroy(&imx219->mutex);
> >  }
> >
> > -static int imx219_check_hwcfg(struct device *dev)
> > +static int imx219_check_hwcfg(struct imx219 *imx219, u64 link_frequency)
> >  {
> > -       struct fwnode_handle *endpoint;
> > +       struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
> > +
> > +       /* Check the number of MIPI CSI2 data lanes */
> > +       if (imx219->lanes != 2 && imx219->lanes != 4) {
> > +               dev_err(&client->dev, "only 2 or 4 data lanes are currently supported\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (link_frequency != ((imx219->lanes == 2) ?
> > +                             IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE)) {
> > +               dev_err(&client->dev, "Link frequency not supported: %lld\n",
> > +                       link_frequency);
> > +               return -EINVAL;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int imx219_probe(struct i2c_client *client)
> > +{
> > +       struct device *dev = &client->dev;
> > +       struct imx219 *imx219;
> >         struct v4l2_fwnode_endpoint ep_cfg = {
> >                 .bus_type = V4L2_MBUS_CSI2_DPHY
> >         };
> > -       int ret = -EINVAL;
> > +       struct fwnode_handle *endpoint;
> > +       int ret = 0;
> > +       u64 link_frequency = 0;
> >
> > +       imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL);
> > +       if (!imx219)
> > +               return -ENOMEM;
> > +
> > +       v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
> > +
> > +       /* Fetch the endpoint to extract lanes and link-frequency */
> >         endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
> >         if (!endpoint) {
> >                 dev_err(dev, "endpoint node not found\n");
> > -               return -EINVAL;
> > +               ret = -EINVAL;
> > +               goto fwnode_cleanup;
> >         }
> >
> >         if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
> >                 dev_err(dev, "could not parse endpoint\n");
> > -               goto error_out;
> > +               ret = -EINVAL;
> > +               goto fwnode_cleanup;
> >         }
> >
> > -       /* Check the number of MIPI CSI2 data lanes */
> > -       if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
> > -               dev_err(dev, "only 2 data lanes are currently supported\n");
> > -               goto error_out;
> > -       }
> > +       imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes;
> >
> >         /* Check the link frequency set in device tree */
> > -       if (!ep_cfg.nr_of_link_frequencies) {
> > +       if (ep_cfg.nr_of_link_frequencies != 1) {
> >                 dev_err(dev, "link-frequency property not found in DT\n");
> > -               goto error_out;
> > -       }
> > -
> > -       if (ep_cfg.nr_of_link_frequencies != 1 ||
> > -           ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) {
> > -               dev_err(dev, "Link frequency not supported: %lld\n",
> > -                       ep_cfg.link_frequencies[0]);
> > -               goto error_out;
> > +               ret = -EINVAL;
> > +               goto fwnode_cleanup;
> >         }
> > +       link_frequency = ep_cfg.link_frequencies[0];
> >
> > -       ret = 0;
> > -
> > -error_out:
> > +       /* Cleanup endpoint and handle any errors from above */
> > +fwnode_cleanup:
> >         v4l2_fwnode_endpoint_free(&ep_cfg);
> >         fwnode_handle_put(endpoint);
>
> Having a "goto" to the middle of a function, and then if(ret) return
> ret; is horrid. Working with diffs has messed this up a bit which is
> why I hadn't noticed it in the last patch set, but I was about to
> comment that link_frequency doesn't need to be initialised to 0 above,
> but it does due to this (except it should take the return path).
>
> I'm not quite sure why you need to move the fwnode calls out of
> imx219_check_hwcfg. Pass in struct imx219 *imx219 and it can check the

It seemed more appropriate to me for probe to set the values instead
of passing it to a helper function to set them.  Since the Probe
function needed to extract the fwnode info to do this, I moved the
calls out of the helper.

> link_frequency and assign imx219->lanes with the values it was already
> validating. You can drop the struct device *dev as it is available via
> imx219->sd->dev, initialised by v4l2_i2c_subdev_init. The
> v4l2_fwnode_endpoint_free and fwnode_handle_put can then remain at the
> end of the function as common cleanup code.
> Rename imx219_check_hwcfg to imx219_read_hwcfg if the name offends as
> it is now doing more than just checking it.

Would be you ok if we did it all in the probe function and drop this
helper function?

>
>   Dave
>
> > -
> > -       return ret;
> > -}
> > -
> > -static int imx219_probe(struct i2c_client *client)
> > -{
> > -       struct device *dev = &client->dev;
> > -       struct imx219 *imx219;
> > -       int ret;
> > -
> > -       imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL);
> > -       if (!imx219)
> > -               return -ENOMEM;
> > -
> > -       v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
> > +       if (ret)
> > +               return ret;
> >
> >         /* Check the hardware configuration in device tree */
> > -       if (imx219_check_hwcfg(dev))
> > +       if (imx219_check_hwcfg(imx219, link_frequency))
> >                 return -EINVAL;
> >
> >         /* Get system clock (xclk) */
> > --
> > 2.34.1
> >



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux