diff mbox series

[09/21] media: i2c: imx258: Add support for running on 2 CSI data lanes

Message ID 20230530173000.3060865-10-dave.stevenson@raspberrypi.com (mailing list archive)
State New, archived
Headers show
Series imx258 improvements series | expand

Commit Message

Dave Stevenson May 30, 2023, 5:29 p.m. UTC
Extends the driver to also support 2 data lanes.
Frame rates are obviously more restricted on 2 lanes, but some
hardware simply hasn't wired more up.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx258.c | 211 ++++++++++++++++++++++++++++++++-----
 1 file changed, 187 insertions(+), 24 deletions(-)

Comments

Dave Stevenson May 30, 2023, 5:37 p.m. UTC | #1
On Tue, 30 May 2023 at 18:30, Dave Stevenson
<dave.stevenson@raspberrypi.com> wrote:
>
> Extends the driver to also support 2 data lanes.
> Frame rates are obviously more restricted on 2 lanes, but some
> hardware simply hasn't wired more up.
>
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx258.c | 211 ++++++++++++++++++++++++++++++++-----
>  1 file changed, 187 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
> index d40521f9a3c6..433dff7f1fa0 100644
> --- a/drivers/media/i2c/imx258.c
> +++ b/drivers/media/i2c/imx258.c
> @@ -86,13 +86,17 @@ struct imx258_reg_list {
>         const struct imx258_reg *regs;
>  };
>
> +#define IMX258_LANE_CONFIGS    2
> +#define IMX258_2_LANE_MODE     0
> +#define IMX258_4_LANE_MODE     1
> +
>  /* Link frequency config */
>  struct imx258_link_freq_config {
>         u64 link_frequency;
>         u32 pixels_per_line;
>
>         /* PLL registers for this link frequency */
> -       struct imx258_reg_list reg_list;
> +       struct imx258_reg_list reg_list[IMX258_LANE_CONFIGS];
>  };
>
>  /* Mode : resolution and related config&values */
> @@ -112,8 +116,30 @@ struct imx258_mode {
>         struct imx258_reg_list reg_list;
>  };
>
> -/* 4208x3120 needs 1267Mbps/lane, 4 lanes */
> -static const struct imx258_reg mipi_1267mbps_19_2mhz[] = {
> +/* 4208x3120 needs 1267Mbps/lane, 4 lanes. Use that rate on 2 lanes as well */
> +static const struct imx258_reg mipi_1267mbps_19_2mhz_2l[] = {
> +       { 0x0136, 0x13 },
> +       { 0x0137, 0x33 },
> +       { 0x0301, 0x0A },
> +       { 0x0303, 0x02 },
> +       { 0x0305, 0x03 },
> +       { 0x0306, 0x00 },
> +       { 0x0307, 0xC6 },
> +       { 0x0309, 0x0A },
> +       { 0x030B, 0x01 },
> +       { 0x030D, 0x02 },
> +       { 0x030E, 0x00 },
> +       { 0x030F, 0xD8 },
> +       { 0x0310, 0x00 },
> +
> +       { 0x0114, 0x01 },
> +       { 0x0820, 0x09 },
> +       { 0x0821, 0xa6 },
> +       { 0x0822, 0x66 },
> +       { 0x0823, 0x66 },
> +};
> +
> +static const struct imx258_reg mipi_1267mbps_19_2mhz_4l[] = {
>         { 0x0136, 0x13 },
>         { 0x0137, 0x33 },
>         { 0x0301, 0x05 },
> @@ -127,16 +153,18 @@ static const struct imx258_reg mipi_1267mbps_19_2mhz[] = {
>         { 0x030E, 0x00 },
>         { 0x030F, 0xD8 },
>         { 0x0310, 0x00 },
> +
> +       { 0x0114, 0x03 },
>         { 0x0820, 0x13 },
>         { 0x0821, 0x4C },
>         { 0x0822, 0xCC },
>         { 0x0823, 0xCC },
>  };
>
> -static const struct imx258_reg mipi_1272mbps_24mhz[] = {
> +static const struct imx258_reg mipi_1272mbps_24mhz_2l[] = {
>         { 0x0136, 0x18 },
>         { 0x0137, 0x00 },
> -       { 0x0301, 0x05 },
> +       { 0x0301, 0x0a },
>         { 0x0303, 0x02 },
>         { 0x0305, 0x04 },
>         { 0x0306, 0x00 },
> @@ -147,13 +175,59 @@ static const struct imx258_reg mipi_1272mbps_24mhz[] = {
>         { 0x030E, 0x00 },
>         { 0x030F, 0xD8 },
>         { 0x0310, 0x00 },
> +
> +       { 0x0114, 0x01 },
>         { 0x0820, 0x13 },
>         { 0x0821, 0x4C },
>         { 0x0822, 0xCC },
>         { 0x0823, 0xCC },
>  };
>
> -static const struct imx258_reg mipi_640mbps_19_2mhz[] = {
> +static const struct imx258_reg mipi_1272mbps_24mhz_4l[] = {
> +       { 0x0136, 0x18 },
> +       { 0x0137, 0x00 },
> +       { 0x0301, 0x05 },
> +       { 0x0303, 0x02 },
> +       { 0x0305, 0x04 },
> +       { 0x0306, 0x00 },
> +       { 0x0307, 0xD4 },
> +       { 0x0309, 0x0A },
> +       { 0x030B, 0x01 },
> +       { 0x030D, 0x02 },
> +       { 0x030E, 0x00 },
> +       { 0x030F, 0xD8 },
> +       { 0x0310, 0x00 },
> +
> +       { 0x0114, 0x03 },
> +       { 0x0820, 0x13 },
> +       { 0x0821, 0xE0 },
> +       { 0x0822, 0x00 },
> +       { 0x0823, 0x00 },
> +};
> +
> +static const struct imx258_reg mipi_640mbps_19_2mhz_2l[] = {
> +       { 0x0136, 0x13 },
> +       { 0x0137, 0x33 },
> +       { 0x0301, 0x05 },
> +       { 0x0303, 0x02 },
> +       { 0x0305, 0x03 },
> +       { 0x0306, 0x00 },
> +       { 0x0307, 0x64 },
> +       { 0x0309, 0x0A },
> +       { 0x030B, 0x01 },
> +       { 0x030D, 0x02 },
> +       { 0x030E, 0x00 },
> +       { 0x030F, 0xD8 },
> +       { 0x0310, 0x00 },
> +
> +       { 0x0114, 0x01 },
> +       { 0x0820, 0x05 },
> +       { 0x0821, 0x00 },
> +       { 0x0822, 0x00 },
> +       { 0x0823, 0x00 },
> +};
> +
> +static const struct imx258_reg mipi_640mbps_19_2mhz_4l[] = {
>         { 0x0136, 0x13 },
>         { 0x0137, 0x33 },
>         { 0x0301, 0x05 },
> @@ -167,13 +241,37 @@ static const struct imx258_reg mipi_640mbps_19_2mhz[] = {
>         { 0x030E, 0x00 },
>         { 0x030F, 0xD8 },
>         { 0x0310, 0x00 },
> +
> +       { 0x0114, 0x03 },
>         { 0x0820, 0x0A },
>         { 0x0821, 0x00 },
>         { 0x0822, 0x00 },
>         { 0x0823, 0x00 },
>  };
>
> -static const struct imx258_reg mipi_642mbps_24mhz[] = {
> +static const struct imx258_reg mipi_642mbps_24mhz_2l[] = {
> +       { 0x0136, 0x18 },
> +       { 0x0137, 0x00 },
> +       { 0x0301, 0x0A },
> +       { 0x0303, 0x02 },
> +       { 0x0305, 0x04 },
> +       { 0x0306, 0x00 },
> +       { 0x0307, 0x6B },
> +       { 0x0309, 0x0A },
> +       { 0x030B, 0x01 },
> +       { 0x030D, 0x02 },
> +       { 0x030E, 0x00 },
> +       { 0x030F, 0xD8 },
> +       { 0x0310, 0x00 },
> +
> +       { 0x0114, 0x01 },
> +       { 0x0820, 0x0A },
> +       { 0x0821, 0x00 },
> +       { 0x0822, 0x00 },
> +       { 0x0823, 0x00 },
> +};
> +
> +static const struct imx258_reg mipi_642mbps_24mhz_4l[] = {
>         { 0x0136, 0x18 },
>         { 0x0137, 0x00 },
>         { 0x0301, 0x05 },
> @@ -187,6 +285,8 @@ static const struct imx258_reg mipi_642mbps_24mhz[] = {
>         { 0x030E, 0x00 },
>         { 0x030F, 0xD8 },
>         { 0x0310, 0x00 },
> +
> +       { 0x0114, 0x03 },
>         { 0x0820, 0x0A },
>         { 0x0821, 0x00 },
>         { 0x0822, 0x00 },
> @@ -241,7 +341,6 @@ static const struct imx258_reg mode_common_regs[] = {
>         { 0x5F05, 0xED },
>         { 0x0112, 0x0A },
>         { 0x0113, 0x0A },
> -       { 0x0114, 0x03 },
>         { 0x0342, 0x14 },
>         { 0x0343, 0xE8 },
>         { 0x0344, 0x00 },
> @@ -360,11 +459,13 @@ enum {
>
>  /*
>   * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample
> - * data rate => double data rate; number of lanes => 4; bits per pixel => 10
> + * data rate => double data rate;
> + * number of lanes => (configurable 2 or 4);
> + * bits per pixel => 10
>   */
> -static u64 link_freq_to_pixel_rate(u64 f)
> +static u64 link_freq_to_pixel_rate(u64 f, unsigned int nlanes)
>  {
> -       f *= 2 * 4;
> +       f *= 2 * nlanes;
>         do_div(f, 10);
>
>         return f;
> @@ -394,15 +495,27 @@ static const struct imx258_link_freq_config link_freq_configs_19_2[] = {
>         [IMX258_LINK_FREQ_1267MBPS] = {
>                 .pixels_per_line = IMX258_PPL_DEFAULT,
>                 .reg_list = {
> -                       .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz),
> -                       .regs = mipi_1267mbps_19_2mhz,
> +                       [IMX258_2_LANE_MODE] = {
> +                               .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_2l),
> +                               .regs = mipi_1267mbps_19_2mhz_2l,
> +                       },
> +                       [IMX258_4_LANE_MODE] = {
> +                               .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_4l),
> +                               .regs = mipi_1267mbps_19_2mhz_4l,
> +                       },
>                 }
>         },
>         [IMX258_LINK_FREQ_640MBPS] = {
>                 .pixels_per_line = IMX258_PPL_DEFAULT,
>                 .reg_list = {
> -                       .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz),
> -                       .regs = mipi_640mbps_19_2mhz,
> +                       [IMX258_2_LANE_MODE] = {
> +                               .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_2l),
> +                               .regs = mipi_640mbps_19_2mhz_2l,
> +                       },
> +                       [IMX258_4_LANE_MODE] = {
> +                               .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_4l),
> +                               .regs = mipi_640mbps_19_2mhz_4l,
> +                       },
>                 }
>         },
>  };
> @@ -411,15 +524,27 @@ static const struct imx258_link_freq_config link_freq_configs_24[] = {
>         [IMX258_LINK_FREQ_1267MBPS] = {
>                 .pixels_per_line = IMX258_PPL_DEFAULT,
>                 .reg_list = {
> -                       .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz),
> -                       .regs = mipi_1272mbps_24mhz,
> +                       [IMX258_2_LANE_MODE] = {
> +                               .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_2l),
> +                               .regs = mipi_1272mbps_24mhz_2l,
> +                       },
> +                       [IMX258_4_LANE_MODE] = {
> +                               .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_4l),
> +                               .regs = mipi_1272mbps_24mhz_4l,
> +                       },
>                 }
>         },
>         [IMX258_LINK_FREQ_640MBPS] = {
>                 .pixels_per_line = IMX258_PPL_DEFAULT,
>                 .reg_list = {
> -                       .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz),
> -                       .regs = mipi_642mbps_24mhz,
> +                       [IMX258_2_LANE_MODE] = {
> +                               .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_2l),
> +                               .regs = mipi_642mbps_24mhz_2l,
> +                       },
> +                       [IMX258_4_LANE_MODE] = {
> +                               .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_4l),
> +                               .regs = mipi_642mbps_24mhz_4l,
> +                       },
>                 }
>         },
>  };
> @@ -478,6 +603,7 @@ struct imx258 {
>
>         const struct imx258_link_freq_config *link_freq_configs;
>         const s64 *link_freq_menu_items;
> +       unsigned int nlanes;
>
>         /*
>          * Mutex for serialized access:
> @@ -787,7 +913,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd,
>                 __v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index);
>
>                 link_freq = imx258->link_freq_menu_items[mode->link_freq_index];
> -               pixel_rate = link_freq_to_pixel_rate(link_freq);
> +               pixel_rate = link_freq_to_pixel_rate(link_freq, imx258->nlanes);
>                 __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate);
>                 /* Update limits and set FPS to default */
>                 vblank_def = imx258->cur_mode->vts_def -
> @@ -816,11 +942,13 @@ static int imx258_start_streaming(struct imx258 *imx258)
>  {
>         struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
>         const struct imx258_reg_list *reg_list;
> +       const struct imx258_link_freq_config *link_freq_cfg;
>         int ret, link_freq_index;
>
>         /* Setup PLL */
>         link_freq_index = imx258->cur_mode->link_freq_index;
> -       reg_list = &imx258->link_freq_configs[link_freq_index].reg_list;
> +       link_freq_cfg = &imx258->link_freq_configs[link_freq_index];
> +       reg_list = &link_freq_cfg->reg_list[imx258->nlanes == 2 ? 0 : 1];
>         ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs);
>         if (ret) {
>                 dev_err(&client->dev, "%s failed to set plls\n", __func__);
> @@ -1074,9 +1202,11 @@ static int imx258_init_controls(struct imx258 *imx258)
>                 vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>
>         pixel_rate_max =
> -               link_freq_to_pixel_rate(imx258->link_freq_menu_items[0]);
> +               link_freq_to_pixel_rate(imx258->link_freq_menu_items[0],
> +                                       imx258->nlanes);
>         pixel_rate_min =
> -               link_freq_to_pixel_rate(imx258->link_freq_menu_items[1]);
> +               link_freq_to_pixel_rate(imx258->link_freq_menu_items[1],
> +                                       imx258->nlanes);
>         /* By default, PIXEL_RATE is read only */
>         imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
>                                 V4L2_CID_PIXEL_RATE,
> @@ -1174,6 +1304,10 @@ static int imx258_get_regulators(struct imx258 *imx258,
>  static int imx258_probe(struct i2c_client *client)
>  {
>         struct imx258 *imx258;
> +       struct fwnode_handle *endpoint;
> +       struct v4l2_fwnode_endpoint ep = {
> +               .bus_type = V4L2_MBUS_CSI2_DPHY
> +       };
>         int ret;
>         u32 val = 0;
>
> @@ -1216,13 +1350,38 @@ static int imx258_probe(struct i2c_client *client)
>                 return -EINVAL;
>         }
>
> +       endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
> +       if (!endpoint) {
> +               dev_err(&client->dev, "Endpoint node not found\n");
> +               return -EINVAL;
> +       }
> +
> +       ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
> +       fwnode_handle_put(endpoint);
> +       if (ret == -ENXIO) {
> +               dev_err(&client->dev, "Unsupported bus type, should be CSI2\n");
> +               goto error_endpoint_poweron;
> +       } else if (ret) {
> +               dev_err(&client->dev, "Parsing endpoint node failed\n");
> +               goto error_endpoint_poweron;
> +       }
> +
> +       /* Get number of data lanes */
> +       imx258->nlanes = ep.bus.mipi_csi2.num_data_lanes;
> +       if (imx258->nlanes != 2 && imx258->nlanes != 4) {
> +               dev_err(&client->dev, "Invalid data lanes: %u\n",
> +                       imx258->nlanes);
> +               ret = -EINVAL;
> +               goto error_endpoint_poweron;
> +       }
> +
>         /* Initialize subdev */
>         v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
>
>         /* Will be powered off via pm_runtime_idle */
>         ret = imx258_power_on(&client->dev);
>         if (ret)
> -               return ret;
> +               goto error_endpoint_poweron;
>
>         /* Check module identity */
>         ret = imx258_identify_module(imx258);
> @@ -1255,6 +1414,7 @@ static int imx258_probe(struct i2c_client *client)
>         pm_runtime_set_active(&client->dev);
>         pm_runtime_enable(&client->dev);
>         pm_runtime_idle(&client->dev);
> +       v4l2_fwnode_endpoint_free(&endpoint);

Apologies, I've messed up in making a last minute change to fix this
leak, and didn't hit compile again :-(

This should be
   v4l2_fwnode_endpoint_free(&ep);
as in the error path below.

I'll wait a few days to action any early review comments made, and
then send a v2 fixing that up.

  Dave

>
>         return 0;
>
> @@ -1267,6 +1427,9 @@ static int imx258_probe(struct i2c_client *client)
>  error_identify:
>         imx258_power_off(&client->dev);
>
> +error_endpoint_poweron:
> +       v4l2_fwnode_endpoint_free(&ep);
> +
>         return ret;
>  }
>
> --
> 2.25.1
>
kernel test robot May 31, 2023, 8:07 a.m. UTC | #2
Hi Dave,

kernel test robot noticed the following build errors:

[auto build test ERROR on media-tree/master]
[also build test ERROR on linus/master v6.4-rc4 next-20230531]
[cannot apply to robh/for-next sailus-media-tree/streams]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Dave-Stevenson/media-i2c-imx258-Remove-unused-defines/20230531-013342
base:   git://linuxtv.org/media_tree.git master
patch link:    https://lore.kernel.org/r/20230530173000.3060865-10-dave.stevenson%40raspberrypi.com
patch subject: [PATCH 09/21] media: i2c: imx258: Add support for running on 2 CSI data lanes
config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20230531/202305311525.0POx1Mzk-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 12.3.0
reproduce (this is a W=1 build):
        mkdir -p ~/bin
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/e3fbe7eaf1c1b9f3b031308dbd3433a982bd3fe6
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Dave-Stevenson/media-i2c-imx258-Remove-unused-defines/20230531-013342
        git checkout e3fbe7eaf1c1b9f3b031308dbd3433a982bd3fe6
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.3.0 ~/bin/make.cross W=1 O=build_dir ARCH=m68k olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.3.0 ~/bin/make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash drivers/media/i2c/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202305311525.0POx1Mzk-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/media/i2c/imx258.c: In function 'imx258_probe':
>> drivers/media/i2c/imx258.c:1417:35: error: passing argument 1 of 'v4l2_fwnode_endpoint_free' from incompatible pointer type [-Werror=incompatible-pointer-types]
    1417 |         v4l2_fwnode_endpoint_free(&endpoint);
         |                                   ^~~~~~~~~
         |                                   |
         |                                   struct fwnode_handle **
   In file included from drivers/media/i2c/imx258.c:13:
   include/media/v4l2-fwnode.h:239:61: note: expected 'struct v4l2_fwnode_endpoint *' but argument is of type 'struct fwnode_handle **'
     239 | void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep);
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
   cc1: some warnings being treated as errors


vim +/v4l2_fwnode_endpoint_free +1417 drivers/media/i2c/imx258.c

  1303	
  1304	static int imx258_probe(struct i2c_client *client)
  1305	{
  1306		struct imx258 *imx258;
  1307		struct fwnode_handle *endpoint;
  1308		struct v4l2_fwnode_endpoint ep = {
  1309			.bus_type = V4L2_MBUS_CSI2_DPHY
  1310		};
  1311		int ret;
  1312		u32 val = 0;
  1313	
  1314		imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL);
  1315		if (!imx258)
  1316			return -ENOMEM;
  1317	
  1318		ret = imx258_get_regulators(imx258, client);
  1319		if (ret)
  1320			return ret;
  1321	
  1322		imx258->clk = devm_clk_get_optional(&client->dev, NULL);
  1323		if (IS_ERR(imx258->clk))
  1324			return dev_err_probe(&client->dev, PTR_ERR(imx258->clk),
  1325					     "error getting clock\n");
  1326		if (!imx258->clk) {
  1327			dev_dbg(&client->dev,
  1328				"no clock provided, using clock-frequency property\n");
  1329	
  1330			device_property_read_u32(&client->dev, "clock-frequency", &val);
  1331		} else if (IS_ERR(imx258->clk)) {
  1332			return dev_err_probe(&client->dev, PTR_ERR(imx258->clk),
  1333					     "error getting clock\n");
  1334		} else {
  1335			val = clk_get_rate(imx258->clk);
  1336		}
  1337	
  1338		switch (val) {
  1339		case 19200000:
  1340			imx258->link_freq_configs = link_freq_configs_19_2;
  1341			imx258->link_freq_menu_items = link_freq_menu_items_19_2;
  1342			break;
  1343		case 24000000:
  1344			imx258->link_freq_configs = link_freq_configs_24;
  1345			imx258->link_freq_menu_items = link_freq_menu_items_24;
  1346			break;
  1347		default:
  1348			dev_err(&client->dev, "input clock frequency of %u not supported\n",
  1349				val);
  1350			return -EINVAL;
  1351		}
  1352	
  1353		endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
  1354		if (!endpoint) {
  1355			dev_err(&client->dev, "Endpoint node not found\n");
  1356			return -EINVAL;
  1357		}
  1358	
  1359		ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
  1360		fwnode_handle_put(endpoint);
  1361		if (ret == -ENXIO) {
  1362			dev_err(&client->dev, "Unsupported bus type, should be CSI2\n");
  1363			goto error_endpoint_poweron;
  1364		} else if (ret) {
  1365			dev_err(&client->dev, "Parsing endpoint node failed\n");
  1366			goto error_endpoint_poweron;
  1367		}
  1368	
  1369		/* Get number of data lanes */
  1370		imx258->nlanes = ep.bus.mipi_csi2.num_data_lanes;
  1371		if (imx258->nlanes != 2 && imx258->nlanes != 4) {
  1372			dev_err(&client->dev, "Invalid data lanes: %u\n",
  1373				imx258->nlanes);
  1374			ret = -EINVAL;
  1375			goto error_endpoint_poweron;
  1376		}
  1377	
  1378		/* Initialize subdev */
  1379		v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
  1380	
  1381		/* Will be powered off via pm_runtime_idle */
  1382		ret = imx258_power_on(&client->dev);
  1383		if (ret)
  1384			goto error_endpoint_poweron;
  1385	
  1386		/* Check module identity */
  1387		ret = imx258_identify_module(imx258);
  1388		if (ret)
  1389			goto error_identify;
  1390	
  1391		/* Set default mode to max resolution */
  1392		imx258->cur_mode = &supported_modes[0];
  1393	
  1394		ret = imx258_init_controls(imx258);
  1395		if (ret)
  1396			goto error_identify;
  1397	
  1398		/* Initialize subdev */
  1399		imx258->sd.internal_ops = &imx258_internal_ops;
  1400		imx258->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
  1401		imx258->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
  1402	
  1403		/* Initialize source pad */
  1404		imx258->pad.flags = MEDIA_PAD_FL_SOURCE;
  1405	
  1406		ret = media_entity_pads_init(&imx258->sd.entity, 1, &imx258->pad);
  1407		if (ret)
  1408			goto error_handler_free;
  1409	
  1410		ret = v4l2_async_register_subdev_sensor(&imx258->sd);
  1411		if (ret < 0)
  1412			goto error_media_entity;
  1413	
  1414		pm_runtime_set_active(&client->dev);
  1415		pm_runtime_enable(&client->dev);
  1416		pm_runtime_idle(&client->dev);
> 1417		v4l2_fwnode_endpoint_free(&endpoint);
  1418	
  1419		return 0;
  1420	
  1421	error_media_entity:
  1422		media_entity_cleanup(&imx258->sd.entity);
  1423	
  1424	error_handler_free:
  1425		imx258_free_controls(imx258);
  1426	
  1427	error_identify:
  1428		imx258_power_off(&client->dev);
  1429	
  1430	error_endpoint_poweron:
  1431		v4l2_fwnode_endpoint_free(&ep);
  1432	
  1433		return ret;
  1434	}
  1435
Jacopo Mondi June 2, 2023, 1:23 p.m. UTC | #3
Hi Dave

On Tue, May 30, 2023 at 06:29:48PM +0100, Dave Stevenson wrote:
> Extends the driver to also support 2 data lanes.
> Frame rates are obviously more restricted on 2 lanes, but some
> hardware simply hasn't wired more up.
>
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx258.c | 211 ++++++++++++++++++++++++++++++++-----
>  1 file changed, 187 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
> index d40521f9a3c6..433dff7f1fa0 100644
> --- a/drivers/media/i2c/imx258.c
> +++ b/drivers/media/i2c/imx258.c
> @@ -86,13 +86,17 @@ struct imx258_reg_list {
>  	const struct imx258_reg *regs;
>  };
>
> +#define IMX258_LANE_CONFIGS	2
> +#define IMX258_2_LANE_MODE	0
> +#define IMX258_4_LANE_MODE	1
> +

nit: This might be better expressed as an enum...

>  /* Link frequency config */
>  struct imx258_link_freq_config {
>  	u64 link_frequency;
>  	u32 pixels_per_line;
>
>  	/* PLL registers for this link frequency */
> -	struct imx258_reg_list reg_list;
> +	struct imx258_reg_list reg_list[IMX258_LANE_CONFIGS];
>  };
>
>  /* Mode : resolution and related config&values */
> @@ -112,8 +116,30 @@ struct imx258_mode {
>  	struct imx258_reg_list reg_list;
>  };
>
> -/* 4208x3120 needs 1267Mbps/lane, 4 lanes */
> -static const struct imx258_reg mipi_1267mbps_19_2mhz[] = {
> +/* 4208x3120 needs 1267Mbps/lane, 4 lanes. Use that rate on 2 lanes as well */

I might be missing how the same link freq (and thus lane bandwidth)
gives the same frame rate on 4 and 2 data lanes setup..

I get a total bandwidth with default VTS and PPL for 10bpp mode at 30
fps of

        vts * ppl * bpp * fps =
        3152 * 5352 * 10 * 30 = 5060851200 bit/sec

The resulting lane bandwidth for 4 lanes is
        1.265.212.800 which ~= 1267 Mbps/lane you have above
and results in a link frequency of
        632606400 which matches your 634MHz/636MHz

With 2 lanes either you double the bandwidth or you halve the frame
rate. Seeing that now link_freq_to_pixel_rate() will give you a *2
result in 4 lanes configuration, I presume full res mode runs at 15FPS
on 2 lanes ? If that's the case, should you remove '30FPS' from the
VTS default value defines ?


> +static const struct imx258_reg mipi_1267mbps_19_2mhz_2l[] = {
> +	{ 0x0136, 0x13 },
> +	{ 0x0137, 0x33 },
> +	{ 0x0301, 0x0A },
> +	{ 0x0303, 0x02 },
> +	{ 0x0305, 0x03 },
> +	{ 0x0306, 0x00 },
> +	{ 0x0307, 0xC6 },
> +	{ 0x0309, 0x0A },
> +	{ 0x030B, 0x01 },
> +	{ 0x030D, 0x02 },
> +	{ 0x030E, 0x00 },
> +	{ 0x030F, 0xD8 },
> +	{ 0x0310, 0x00 },
> +
> +	{ 0x0114, 0x01 },
> +	{ 0x0820, 0x09 },
> +	{ 0x0821, 0xa6 },
> +	{ 0x0822, 0x66 },
> +	{ 0x0823, 0x66 },
> +};
> +
> +static const struct imx258_reg mipi_1267mbps_19_2mhz_4l[] = {
>  	{ 0x0136, 0x13 },
>  	{ 0x0137, 0x33 },
>  	{ 0x0301, 0x05 },
> @@ -127,16 +153,18 @@ static const struct imx258_reg mipi_1267mbps_19_2mhz[] = {
>  	{ 0x030E, 0x00 },
>  	{ 0x030F, 0xD8 },
>  	{ 0x0310, 0x00 },
> +
> +	{ 0x0114, 0x03 },
>  	{ 0x0820, 0x13 },
>  	{ 0x0821, 0x4C },
>  	{ 0x0822, 0xCC },
>  	{ 0x0823, 0xCC },
>  };
>
> -static const struct imx258_reg mipi_1272mbps_24mhz[] = {
> +static const struct imx258_reg mipi_1272mbps_24mhz_2l[] = {
>  	{ 0x0136, 0x18 },
>  	{ 0x0137, 0x00 },
> -	{ 0x0301, 0x05 },
> +	{ 0x0301, 0x0a },
>  	{ 0x0303, 0x02 },
>  	{ 0x0305, 0x04 },
>  	{ 0x0306, 0x00 },
> @@ -147,13 +175,59 @@ static const struct imx258_reg mipi_1272mbps_24mhz[] = {
>  	{ 0x030E, 0x00 },
>  	{ 0x030F, 0xD8 },
>  	{ 0x0310, 0x00 },
> +
> +	{ 0x0114, 0x01 },
>  	{ 0x0820, 0x13 },
>  	{ 0x0821, 0x4C },
>  	{ 0x0822, 0xCC },
>  	{ 0x0823, 0xCC },
>  };
>
> -static const struct imx258_reg mipi_640mbps_19_2mhz[] = {
> +static const struct imx258_reg mipi_1272mbps_24mhz_4l[] = {
> +	{ 0x0136, 0x18 },
> +	{ 0x0137, 0x00 },
> +	{ 0x0301, 0x05 },
> +	{ 0x0303, 0x02 },
> +	{ 0x0305, 0x04 },
> +	{ 0x0306, 0x00 },
> +	{ 0x0307, 0xD4 },
> +	{ 0x0309, 0x0A },
> +	{ 0x030B, 0x01 },
> +	{ 0x030D, 0x02 },
> +	{ 0x030E, 0x00 },
> +	{ 0x030F, 0xD8 },
> +	{ 0x0310, 0x00 },
> +
> +	{ 0x0114, 0x03 },
> +	{ 0x0820, 0x13 },
> +	{ 0x0821, 0xE0 },
> +	{ 0x0822, 0x00 },
> +	{ 0x0823, 0x00 },
> +};
> +
> +static const struct imx258_reg mipi_640mbps_19_2mhz_2l[] = {
> +	{ 0x0136, 0x13 },
> +	{ 0x0137, 0x33 },
> +	{ 0x0301, 0x05 },
> +	{ 0x0303, 0x02 },
> +	{ 0x0305, 0x03 },
> +	{ 0x0306, 0x00 },
> +	{ 0x0307, 0x64 },
> +	{ 0x0309, 0x0A },
> +	{ 0x030B, 0x01 },
> +	{ 0x030D, 0x02 },
> +	{ 0x030E, 0x00 },
> +	{ 0x030F, 0xD8 },
> +	{ 0x0310, 0x00 },
> +
> +	{ 0x0114, 0x01 },
> +	{ 0x0820, 0x05 },
> +	{ 0x0821, 0x00 },
> +	{ 0x0822, 0x00 },
> +	{ 0x0823, 0x00 },
> +};
> +
> +static const struct imx258_reg mipi_640mbps_19_2mhz_4l[] = {
>  	{ 0x0136, 0x13 },
>  	{ 0x0137, 0x33 },
>  	{ 0x0301, 0x05 },
> @@ -167,13 +241,37 @@ static const struct imx258_reg mipi_640mbps_19_2mhz[] = {
>  	{ 0x030E, 0x00 },
>  	{ 0x030F, 0xD8 },
>  	{ 0x0310, 0x00 },
> +
> +	{ 0x0114, 0x03 },
>  	{ 0x0820, 0x0A },
>  	{ 0x0821, 0x00 },
>  	{ 0x0822, 0x00 },
>  	{ 0x0823, 0x00 },
>  };
>
> -static const struct imx258_reg mipi_642mbps_24mhz[] = {
> +static const struct imx258_reg mipi_642mbps_24mhz_2l[] = {
> +	{ 0x0136, 0x18 },
> +	{ 0x0137, 0x00 },
> +	{ 0x0301, 0x0A },
> +	{ 0x0303, 0x02 },
> +	{ 0x0305, 0x04 },
> +	{ 0x0306, 0x00 },
> +	{ 0x0307, 0x6B },
> +	{ 0x0309, 0x0A },
> +	{ 0x030B, 0x01 },
> +	{ 0x030D, 0x02 },
> +	{ 0x030E, 0x00 },
> +	{ 0x030F, 0xD8 },
> +	{ 0x0310, 0x00 },
> +
> +	{ 0x0114, 0x01 },
> +	{ 0x0820, 0x0A },
> +	{ 0x0821, 0x00 },
> +	{ 0x0822, 0x00 },
> +	{ 0x0823, 0x00 },
> +};
> +
> +static const struct imx258_reg mipi_642mbps_24mhz_4l[] = {
>  	{ 0x0136, 0x18 },
>  	{ 0x0137, 0x00 },
>  	{ 0x0301, 0x05 },
> @@ -187,6 +285,8 @@ static const struct imx258_reg mipi_642mbps_24mhz[] = {
>  	{ 0x030E, 0x00 },
>  	{ 0x030F, 0xD8 },
>  	{ 0x0310, 0x00 },
> +
> +	{ 0x0114, 0x03 },
>  	{ 0x0820, 0x0A },
>  	{ 0x0821, 0x00 },
>  	{ 0x0822, 0x00 },
> @@ -241,7 +341,6 @@ static const struct imx258_reg mode_common_regs[] = {
>  	{ 0x5F05, 0xED },
>  	{ 0x0112, 0x0A },
>  	{ 0x0113, 0x0A },
> -	{ 0x0114, 0x03 },
>  	{ 0x0342, 0x14 },
>  	{ 0x0343, 0xE8 },
>  	{ 0x0344, 0x00 },
> @@ -360,11 +459,13 @@ enum {
>
>  /*
>   * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample
> - * data rate => double data rate; number of lanes => 4; bits per pixel => 10
> + * data rate => double data rate;
> + * number of lanes => (configurable 2 or 4);
> + * bits per pixel => 10
>   */
> -static u64 link_freq_to_pixel_rate(u64 f)
> +static u64 link_freq_to_pixel_rate(u64 f, unsigned int nlanes)
>  {
> -	f *= 2 * 4;
> +	f *= 2 * nlanes;
>  	do_div(f, 10);
>
>  	return f;
> @@ -394,15 +495,27 @@ static const struct imx258_link_freq_config link_freq_configs_19_2[] = {
>  	[IMX258_LINK_FREQ_1267MBPS] = {
>  		.pixels_per_line = IMX258_PPL_DEFAULT,
>  		.reg_list = {
> -			.num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz),
> -			.regs = mipi_1267mbps_19_2mhz,
> +			[IMX258_2_LANE_MODE] = {
> +				.num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_2l),
> +				.regs = mipi_1267mbps_19_2mhz_2l,
> +			},
> +			[IMX258_4_LANE_MODE] = {
> +				.num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_4l),
> +				.regs = mipi_1267mbps_19_2mhz_4l,
> +			},
>  		}
>  	},
>  	[IMX258_LINK_FREQ_640MBPS] = {
>  		.pixels_per_line = IMX258_PPL_DEFAULT,
>  		.reg_list = {
> -			.num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz),
> -			.regs = mipi_640mbps_19_2mhz,
> +			[IMX258_2_LANE_MODE] = {
> +				.num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_2l),
> +				.regs = mipi_640mbps_19_2mhz_2l,
> +			},
> +			[IMX258_4_LANE_MODE] = {
> +				.num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_4l),
> +				.regs = mipi_640mbps_19_2mhz_4l,
> +			},
>  		}
>  	},
>  };
> @@ -411,15 +524,27 @@ static const struct imx258_link_freq_config link_freq_configs_24[] = {
>  	[IMX258_LINK_FREQ_1267MBPS] = {
>  		.pixels_per_line = IMX258_PPL_DEFAULT,
>  		.reg_list = {
> -			.num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz),
> -			.regs = mipi_1272mbps_24mhz,
> +			[IMX258_2_LANE_MODE] = {
> +				.num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_2l),
> +				.regs = mipi_1272mbps_24mhz_2l,
> +			},
> +			[IMX258_4_LANE_MODE] = {
> +				.num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_4l),
> +				.regs = mipi_1272mbps_24mhz_4l,
> +			},
>  		}
>  	},
>  	[IMX258_LINK_FREQ_640MBPS] = {
>  		.pixels_per_line = IMX258_PPL_DEFAULT,
>  		.reg_list = {
> -			.num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz),
> -			.regs = mipi_642mbps_24mhz,
> +			[IMX258_2_LANE_MODE] = {
> +				.num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_2l),
> +				.regs = mipi_642mbps_24mhz_2l,
> +			},
> +			[IMX258_4_LANE_MODE] = {
> +				.num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_4l),
> +				.regs = mipi_642mbps_24mhz_4l,
> +			},
>  		}
>  	},
>  };
> @@ -478,6 +603,7 @@ struct imx258 {
>
>  	const struct imx258_link_freq_config *link_freq_configs;
>  	const s64 *link_freq_menu_items;
> +	unsigned int nlanes;
>
>  	/*
>  	 * Mutex for serialized access:
> @@ -787,7 +913,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd,
>  		__v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index);
>
>  		link_freq = imx258->link_freq_menu_items[mode->link_freq_index];
> -		pixel_rate = link_freq_to_pixel_rate(link_freq);
> +		pixel_rate = link_freq_to_pixel_rate(link_freq, imx258->nlanes);
>  		__v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate);
>  		/* Update limits and set FPS to default */
>  		vblank_def = imx258->cur_mode->vts_def -
> @@ -816,11 +942,13 @@ static int imx258_start_streaming(struct imx258 *imx258)
>  {
>  	struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
>  	const struct imx258_reg_list *reg_list;
> +	const struct imx258_link_freq_config *link_freq_cfg;
>  	int ret, link_freq_index;
>
>  	/* Setup PLL */
>  	link_freq_index = imx258->cur_mode->link_freq_index;
> -	reg_list = &imx258->link_freq_configs[link_freq_index].reg_list;
> +	link_freq_cfg = &imx258->link_freq_configs[link_freq_index];
> +	reg_list = &link_freq_cfg->reg_list[imx258->nlanes == 2 ? 0 : 1];
>  	ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs);
>  	if (ret) {
>  		dev_err(&client->dev, "%s failed to set plls\n", __func__);
> @@ -1074,9 +1202,11 @@ static int imx258_init_controls(struct imx258 *imx258)
>  		vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>
>  	pixel_rate_max =
> -		link_freq_to_pixel_rate(imx258->link_freq_menu_items[0]);
> +		link_freq_to_pixel_rate(imx258->link_freq_menu_items[0],
> +					imx258->nlanes);
>  	pixel_rate_min =
> -		link_freq_to_pixel_rate(imx258->link_freq_menu_items[1]);
> +		link_freq_to_pixel_rate(imx258->link_freq_menu_items[1],
> +					imx258->nlanes);
>  	/* By default, PIXEL_RATE is read only */
>  	imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
>  				V4L2_CID_PIXEL_RATE,
> @@ -1174,6 +1304,10 @@ static int imx258_get_regulators(struct imx258 *imx258,
>  static int imx258_probe(struct i2c_client *client)
>  {
>  	struct imx258 *imx258;
> +	struct fwnode_handle *endpoint;
> +	struct v4l2_fwnode_endpoint ep = {
> +		.bus_type = V4L2_MBUS_CSI2_DPHY
> +	};
>  	int ret;
>  	u32 val = 0;
>
> @@ -1216,13 +1350,38 @@ static int imx258_probe(struct i2c_client *client)
>  		return -EINVAL;
>  	}
>
> +	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
> +	if (!endpoint) {
> +		dev_err(&client->dev, "Endpoint node not found\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
> +	fwnode_handle_put(endpoint);
> +	if (ret == -ENXIO) {

Doesn't the core warns you already ? Anywy, doesn't hurt I suppose

> +		dev_err(&client->dev, "Unsupported bus type, should be CSI2\n");
> +		goto error_endpoint_poweron;
> +	} else if (ret) {
> +		dev_err(&client->dev, "Parsing endpoint node failed\n");
> +		goto error_endpoint_poweron;

It's a pretty peculiar name :) Why not 'error_endpoint_free' ?

Anyway, if parsing fails, I don't think you need to v4l2_fwnode_endpoint_free()
as the core should take care of doing so.

> +	}
> +
> +	/* Get number of data lanes */
> +	imx258->nlanes = ep.bus.mipi_csi2.num_data_lanes;
> +	if (imx258->nlanes != 2 && imx258->nlanes != 4) {
> +		dev_err(&client->dev, "Invalid data lanes: %u\n",
> +			imx258->nlanes);
> +		ret = -EINVAL;
> +		goto error_endpoint_poweron;
> +	}
> +
>  	/* Initialize subdev */
>  	v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
>
>  	/* Will be powered off via pm_runtime_idle */
>  	ret = imx258_power_on(&client->dev);
>  	if (ret)
> -		return ret;
> +		goto error_endpoint_poweron;
>
>  	/* Check module identity */
>  	ret = imx258_identify_module(imx258);
> @@ -1255,6 +1414,7 @@ static int imx258_probe(struct i2c_client *client)
>  	pm_runtime_set_active(&client->dev);
>  	pm_runtime_enable(&client->dev);
>  	pm_runtime_idle(&client->dev);
> +	v4l2_fwnode_endpoint_free(&endpoint);

Do you need to free in _remove() too ?

>
>  	return 0;
>
> @@ -1267,6 +1427,9 @@ static int imx258_probe(struct i2c_client *client)
>  error_identify:
>  	imx258_power_off(&client->dev);
>
> +error_endpoint_poweron:
> +	v4l2_fwnode_endpoint_free(&ep);
> +
>  	return ret;
>  }
>
> --
> 2.25.1
>
Dave Stevenson June 2, 2023, 5:46 p.m. UTC | #4
Hi Jacopo

Thanks for the review

On Fri, 2 Jun 2023 at 14:23, Jacopo Mondi <jacopo.mondi@ideasonboard.com> wrote:
>
> Hi Dave
>
> On Tue, May 30, 2023 at 06:29:48PM +0100, Dave Stevenson wrote:
> > Extends the driver to also support 2 data lanes.
> > Frame rates are obviously more restricted on 2 lanes, but some
> > hardware simply hasn't wired more up.
> >
> > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > ---
> >  drivers/media/i2c/imx258.c | 211 ++++++++++++++++++++++++++++++++-----
> >  1 file changed, 187 insertions(+), 24 deletions(-)
> >
> > diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
> > index d40521f9a3c6..433dff7f1fa0 100644
> > --- a/drivers/media/i2c/imx258.c
> > +++ b/drivers/media/i2c/imx258.c
> > @@ -86,13 +86,17 @@ struct imx258_reg_list {
> >       const struct imx258_reg *regs;
> >  };
> >
> > +#define IMX258_LANE_CONFIGS  2
> > +#define IMX258_2_LANE_MODE   0
> > +#define IMX258_4_LANE_MODE   1
> > +
>
> nit: This might be better expressed as an enum...

Will do. It's always a toss up between the two.

> >  /* Link frequency config */
> >  struct imx258_link_freq_config {
> >       u64 link_frequency;
> >       u32 pixels_per_line;
> >
> >       /* PLL registers for this link frequency */
> > -     struct imx258_reg_list reg_list;
> > +     struct imx258_reg_list reg_list[IMX258_LANE_CONFIGS];
> >  };
> >
> >  /* Mode : resolution and related config&values */
> > @@ -112,8 +116,30 @@ struct imx258_mode {
> >       struct imx258_reg_list reg_list;
> >  };
> >
> > -/* 4208x3120 needs 1267Mbps/lane, 4 lanes */
> > -static const struct imx258_reg mipi_1267mbps_19_2mhz[] = {
> > +/* 4208x3120 needs 1267Mbps/lane, 4 lanes. Use that rate on 2 lanes as well */
>
> I might be missing how the same link freq (and thus lane bandwidth)
> gives the same frame rate on 4 and 2 data lanes setup..
>
> I get a total bandwidth with default VTS and PPL for 10bpp mode at 30
> fps of
>
>         vts * ppl * bpp * fps =
>         3152 * 5352 * 10 * 30 = 5060851200 bit/sec
>
> The resulting lane bandwidth for 4 lanes is
>         1.265.212.800 which ~= 1267 Mbps/lane you have above
> and results in a link frequency of
>         632606400 which matches your 634MHz/636MHz
>
> With 2 lanes either you double the bandwidth or you halve the frame
> rate. Seeing that now link_freq_to_pixel_rate() will give you a *2
> result in 4 lanes configuration, I presume full res mode runs at 15FPS
> on 2 lanes ? If that's the case, should you remove '30FPS' from the
> VTS default value defines ?

The comment here doesn't specify a frame rate, although admittedly it
is ambiguously worded.
I'll reword to:
4208x3120 @ 30fps needs 1267Mbps/lane when using 4 lanes.
To avoid further computation of clock settings, adopt the same per
lane data rate when using 2 lanes, thus allowing a maximum of 15fps.

> > +static const struct imx258_reg mipi_1267mbps_19_2mhz_2l[] = {
> > +     { 0x0136, 0x13 },
> > +     { 0x0137, 0x33 },
> > +     { 0x0301, 0x0A },
> > +     { 0x0303, 0x02 },
> > +     { 0x0305, 0x03 },
> > +     { 0x0306, 0x00 },
> > +     { 0x0307, 0xC6 },
> > +     { 0x0309, 0x0A },
> > +     { 0x030B, 0x01 },
> > +     { 0x030D, 0x02 },
> > +     { 0x030E, 0x00 },
> > +     { 0x030F, 0xD8 },
> > +     { 0x0310, 0x00 },
> > +
> > +     { 0x0114, 0x01 },
> > +     { 0x0820, 0x09 },
> > +     { 0x0821, 0xa6 },
> > +     { 0x0822, 0x66 },
> > +     { 0x0823, 0x66 },
> > +};
> > +
> > +static const struct imx258_reg mipi_1267mbps_19_2mhz_4l[] = {
> >       { 0x0136, 0x13 },
> >       { 0x0137, 0x33 },
> >       { 0x0301, 0x05 },
> > @@ -127,16 +153,18 @@ static const struct imx258_reg mipi_1267mbps_19_2mhz[] = {
> >       { 0x030E, 0x00 },
> >       { 0x030F, 0xD8 },
> >       { 0x0310, 0x00 },
> > +
> > +     { 0x0114, 0x03 },
> >       { 0x0820, 0x13 },
> >       { 0x0821, 0x4C },
> >       { 0x0822, 0xCC },
> >       { 0x0823, 0xCC },
> >  };
> >
> > -static const struct imx258_reg mipi_1272mbps_24mhz[] = {
> > +static const struct imx258_reg mipi_1272mbps_24mhz_2l[] = {
> >       { 0x0136, 0x18 },
> >       { 0x0137, 0x00 },
> > -     { 0x0301, 0x05 },
> > +     { 0x0301, 0x0a },
> >       { 0x0303, 0x02 },
> >       { 0x0305, 0x04 },
> >       { 0x0306, 0x00 },
> > @@ -147,13 +175,59 @@ static const struct imx258_reg mipi_1272mbps_24mhz[] = {
> >       { 0x030E, 0x00 },
> >       { 0x030F, 0xD8 },
> >       { 0x0310, 0x00 },
> > +
> > +     { 0x0114, 0x01 },
> >       { 0x0820, 0x13 },
> >       { 0x0821, 0x4C },
> >       { 0x0822, 0xCC },
> >       { 0x0823, 0xCC },
> >  };
> >
> > -static const struct imx258_reg mipi_640mbps_19_2mhz[] = {
> > +static const struct imx258_reg mipi_1272mbps_24mhz_4l[] = {
> > +     { 0x0136, 0x18 },
> > +     { 0x0137, 0x00 },
> > +     { 0x0301, 0x05 },
> > +     { 0x0303, 0x02 },
> > +     { 0x0305, 0x04 },
> > +     { 0x0306, 0x00 },
> > +     { 0x0307, 0xD4 },
> > +     { 0x0309, 0x0A },
> > +     { 0x030B, 0x01 },
> > +     { 0x030D, 0x02 },
> > +     { 0x030E, 0x00 },
> > +     { 0x030F, 0xD8 },
> > +     { 0x0310, 0x00 },
> > +
> > +     { 0x0114, 0x03 },
> > +     { 0x0820, 0x13 },
> > +     { 0x0821, 0xE0 },
> > +     { 0x0822, 0x00 },
> > +     { 0x0823, 0x00 },
> > +};
> > +
> > +static const struct imx258_reg mipi_640mbps_19_2mhz_2l[] = {
> > +     { 0x0136, 0x13 },
> > +     { 0x0137, 0x33 },
> > +     { 0x0301, 0x05 },
> > +     { 0x0303, 0x02 },
> > +     { 0x0305, 0x03 },
> > +     { 0x0306, 0x00 },
> > +     { 0x0307, 0x64 },
> > +     { 0x0309, 0x0A },
> > +     { 0x030B, 0x01 },
> > +     { 0x030D, 0x02 },
> > +     { 0x030E, 0x00 },
> > +     { 0x030F, 0xD8 },
> > +     { 0x0310, 0x00 },
> > +
> > +     { 0x0114, 0x01 },
> > +     { 0x0820, 0x05 },
> > +     { 0x0821, 0x00 },
> > +     { 0x0822, 0x00 },
> > +     { 0x0823, 0x00 },
> > +};
> > +
> > +static const struct imx258_reg mipi_640mbps_19_2mhz_4l[] = {
> >       { 0x0136, 0x13 },
> >       { 0x0137, 0x33 },
> >       { 0x0301, 0x05 },
> > @@ -167,13 +241,37 @@ static const struct imx258_reg mipi_640mbps_19_2mhz[] = {
> >       { 0x030E, 0x00 },
> >       { 0x030F, 0xD8 },
> >       { 0x0310, 0x00 },
> > +
> > +     { 0x0114, 0x03 },
> >       { 0x0820, 0x0A },
> >       { 0x0821, 0x00 },
> >       { 0x0822, 0x00 },
> >       { 0x0823, 0x00 },
> >  };
> >
> > -static const struct imx258_reg mipi_642mbps_24mhz[] = {
> > +static const struct imx258_reg mipi_642mbps_24mhz_2l[] = {
> > +     { 0x0136, 0x18 },
> > +     { 0x0137, 0x00 },
> > +     { 0x0301, 0x0A },
> > +     { 0x0303, 0x02 },
> > +     { 0x0305, 0x04 },
> > +     { 0x0306, 0x00 },
> > +     { 0x0307, 0x6B },
> > +     { 0x0309, 0x0A },
> > +     { 0x030B, 0x01 },
> > +     { 0x030D, 0x02 },
> > +     { 0x030E, 0x00 },
> > +     { 0x030F, 0xD8 },
> > +     { 0x0310, 0x00 },
> > +
> > +     { 0x0114, 0x01 },
> > +     { 0x0820, 0x0A },
> > +     { 0x0821, 0x00 },
> > +     { 0x0822, 0x00 },
> > +     { 0x0823, 0x00 },
> > +};
> > +
> > +static const struct imx258_reg mipi_642mbps_24mhz_4l[] = {
> >       { 0x0136, 0x18 },
> >       { 0x0137, 0x00 },
> >       { 0x0301, 0x05 },
> > @@ -187,6 +285,8 @@ static const struct imx258_reg mipi_642mbps_24mhz[] = {
> >       { 0x030E, 0x00 },
> >       { 0x030F, 0xD8 },
> >       { 0x0310, 0x00 },
> > +
> > +     { 0x0114, 0x03 },
> >       { 0x0820, 0x0A },
> >       { 0x0821, 0x00 },
> >       { 0x0822, 0x00 },
> > @@ -241,7 +341,6 @@ static const struct imx258_reg mode_common_regs[] = {
> >       { 0x5F05, 0xED },
> >       { 0x0112, 0x0A },
> >       { 0x0113, 0x0A },
> > -     { 0x0114, 0x03 },
> >       { 0x0342, 0x14 },
> >       { 0x0343, 0xE8 },
> >       { 0x0344, 0x00 },
> > @@ -360,11 +459,13 @@ enum {
> >
> >  /*
> >   * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample
> > - * data rate => double data rate; number of lanes => 4; bits per pixel => 10
> > + * data rate => double data rate;
> > + * number of lanes => (configurable 2 or 4);
> > + * bits per pixel => 10
> >   */
> > -static u64 link_freq_to_pixel_rate(u64 f)
> > +static u64 link_freq_to_pixel_rate(u64 f, unsigned int nlanes)
> >  {
> > -     f *= 2 * 4;
> > +     f *= 2 * nlanes;
> >       do_div(f, 10);
> >
> >       return f;
> > @@ -394,15 +495,27 @@ static const struct imx258_link_freq_config link_freq_configs_19_2[] = {
> >       [IMX258_LINK_FREQ_1267MBPS] = {
> >               .pixels_per_line = IMX258_PPL_DEFAULT,
> >               .reg_list = {
> > -                     .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz),
> > -                     .regs = mipi_1267mbps_19_2mhz,
> > +                     [IMX258_2_LANE_MODE] = {
> > +                             .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_2l),
> > +                             .regs = mipi_1267mbps_19_2mhz_2l,
> > +                     },
> > +                     [IMX258_4_LANE_MODE] = {
> > +                             .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_4l),
> > +                             .regs = mipi_1267mbps_19_2mhz_4l,
> > +                     },
> >               }
> >       },
> >       [IMX258_LINK_FREQ_640MBPS] = {
> >               .pixels_per_line = IMX258_PPL_DEFAULT,
> >               .reg_list = {
> > -                     .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz),
> > -                     .regs = mipi_640mbps_19_2mhz,
> > +                     [IMX258_2_LANE_MODE] = {
> > +                             .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_2l),
> > +                             .regs = mipi_640mbps_19_2mhz_2l,
> > +                     },
> > +                     [IMX258_4_LANE_MODE] = {
> > +                             .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_4l),
> > +                             .regs = mipi_640mbps_19_2mhz_4l,
> > +                     },
> >               }
> >       },
> >  };
> > @@ -411,15 +524,27 @@ static const struct imx258_link_freq_config link_freq_configs_24[] = {
> >       [IMX258_LINK_FREQ_1267MBPS] = {
> >               .pixels_per_line = IMX258_PPL_DEFAULT,
> >               .reg_list = {
> > -                     .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz),
> > -                     .regs = mipi_1272mbps_24mhz,
> > +                     [IMX258_2_LANE_MODE] = {
> > +                             .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_2l),
> > +                             .regs = mipi_1272mbps_24mhz_2l,
> > +                     },
> > +                     [IMX258_4_LANE_MODE] = {
> > +                             .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_4l),
> > +                             .regs = mipi_1272mbps_24mhz_4l,
> > +                     },
> >               }
> >       },
> >       [IMX258_LINK_FREQ_640MBPS] = {
> >               .pixels_per_line = IMX258_PPL_DEFAULT,
> >               .reg_list = {
> > -                     .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz),
> > -                     .regs = mipi_642mbps_24mhz,
> > +                     [IMX258_2_LANE_MODE] = {
> > +                             .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_2l),
> > +                             .regs = mipi_642mbps_24mhz_2l,
> > +                     },
> > +                     [IMX258_4_LANE_MODE] = {
> > +                             .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_4l),
> > +                             .regs = mipi_642mbps_24mhz_4l,
> > +                     },
> >               }
> >       },
> >  };
> > @@ -478,6 +603,7 @@ struct imx258 {
> >
> >       const struct imx258_link_freq_config *link_freq_configs;
> >       const s64 *link_freq_menu_items;
> > +     unsigned int nlanes;
> >
> >       /*
> >        * Mutex for serialized access:
> > @@ -787,7 +913,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd,
> >               __v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index);
> >
> >               link_freq = imx258->link_freq_menu_items[mode->link_freq_index];
> > -             pixel_rate = link_freq_to_pixel_rate(link_freq);
> > +             pixel_rate = link_freq_to_pixel_rate(link_freq, imx258->nlanes);
> >               __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate);
> >               /* Update limits and set FPS to default */
> >               vblank_def = imx258->cur_mode->vts_def -
> > @@ -816,11 +942,13 @@ static int imx258_start_streaming(struct imx258 *imx258)
> >  {
> >       struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
> >       const struct imx258_reg_list *reg_list;
> > +     const struct imx258_link_freq_config *link_freq_cfg;
> >       int ret, link_freq_index;
> >
> >       /* Setup PLL */
> >       link_freq_index = imx258->cur_mode->link_freq_index;
> > -     reg_list = &imx258->link_freq_configs[link_freq_index].reg_list;
> > +     link_freq_cfg = &imx258->link_freq_configs[link_freq_index];
> > +     reg_list = &link_freq_cfg->reg_list[imx258->nlanes == 2 ? 0 : 1];
> >       ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs);
> >       if (ret) {
> >               dev_err(&client->dev, "%s failed to set plls\n", __func__);
> > @@ -1074,9 +1202,11 @@ static int imx258_init_controls(struct imx258 *imx258)
> >               vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> >
> >       pixel_rate_max =
> > -             link_freq_to_pixel_rate(imx258->link_freq_menu_items[0]);
> > +             link_freq_to_pixel_rate(imx258->link_freq_menu_items[0],
> > +                                     imx258->nlanes);
> >       pixel_rate_min =
> > -             link_freq_to_pixel_rate(imx258->link_freq_menu_items[1]);
> > +             link_freq_to_pixel_rate(imx258->link_freq_menu_items[1],
> > +                                     imx258->nlanes);
> >       /* By default, PIXEL_RATE is read only */
> >       imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
> >                               V4L2_CID_PIXEL_RATE,
> > @@ -1174,6 +1304,10 @@ static int imx258_get_regulators(struct imx258 *imx258,
> >  static int imx258_probe(struct i2c_client *client)
> >  {
> >       struct imx258 *imx258;
> > +     struct fwnode_handle *endpoint;
> > +     struct v4l2_fwnode_endpoint ep = {
> > +             .bus_type = V4L2_MBUS_CSI2_DPHY
> > +     };
> >       int ret;
> >       u32 val = 0;
> >
> > @@ -1216,13 +1350,38 @@ static int imx258_probe(struct i2c_client *client)
> >               return -EINVAL;
> >       }
> >
> > +     endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
> > +     if (!endpoint) {
> > +             dev_err(&client->dev, "Endpoint node not found\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
> > +     fwnode_handle_put(endpoint);
> > +     if (ret == -ENXIO) {
>
> Doesn't the core warns you already ? Anywy, doesn't hurt I suppose

It looks to me like it's a pr_debug, so not apparently.
https://elixir.bootlin.com/linux/latest/source/drivers/media/v4l2-core/v4l2-fwnode.c#L443

There's no real need to differentiate why it failed here, so I'll
combine the two error cases and include the error code.

> > +             dev_err(&client->dev, "Unsupported bus type, should be CSI2\n");
> > +             goto error_endpoint_poweron;
> > +     } else if (ret) {
> > +             dev_err(&client->dev, "Parsing endpoint node failed\n");
> > +             goto error_endpoint_poweron;
>
> It's a pretty peculiar name :) Why not 'error_endpoint_free' ?

It's been a while since I originally wrote this. Probably because it's
called if either the endpoint parsing fails, or if power_on fails.
I'll rename it as you suggest.

> Anyway, if parsing fails, I don't think you need to v4l2_fwnode_endpoint_free()
> as the core should take care of doing so.

Yes, it looks like it does, so I can just return here.
I still need the error path should the number of lanes be invalid, or
imx258_power_on fails, just not here.

> > +     }
> > +
> > +     /* Get number of data lanes */
> > +     imx258->nlanes = ep.bus.mipi_csi2.num_data_lanes;
> > +     if (imx258->nlanes != 2 && imx258->nlanes != 4) {
> > +             dev_err(&client->dev, "Invalid data lanes: %u\n",
> > +                     imx258->nlanes);
> > +             ret = -EINVAL;
> > +             goto error_endpoint_poweron;
> > +     }
> > +
> >       /* Initialize subdev */
> >       v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
> >
> >       /* Will be powered off via pm_runtime_idle */
> >       ret = imx258_power_on(&client->dev);
> >       if (ret)
> > -             return ret;
> > +             goto error_endpoint_poweron;
> >
> >       /* Check module identity */
> >       ret = imx258_identify_module(imx258);
> > @@ -1255,6 +1414,7 @@ static int imx258_probe(struct i2c_client *client)
> >       pm_runtime_set_active(&client->dev);
> >       pm_runtime_enable(&client->dev);
> >       pm_runtime_idle(&client->dev);
> > +     v4l2_fwnode_endpoint_free(&endpoint);
>
> Do you need to free in _remove() too ?

This is the success path at the end of _probe. We don't need the
endpoint after this, and it's a local to this function, therefore
cleaning up here.
As noted earlier, this should be:
v4l2_fwnode_endpoint_free(&ep);

Thanks
  Dave

> >
> >       return 0;
> >
> > @@ -1267,6 +1427,9 @@ static int imx258_probe(struct i2c_client *client)
> >  error_identify:
> >       imx258_power_off(&client->dev);
> >
> > +error_endpoint_poweron:
> > +     v4l2_fwnode_endpoint_free(&ep);
> > +
> >       return ret;
> >  }
> >
> > --
> > 2.25.1
> >
diff mbox series

Patch

diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index d40521f9a3c6..433dff7f1fa0 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -86,13 +86,17 @@  struct imx258_reg_list {
 	const struct imx258_reg *regs;
 };
 
+#define IMX258_LANE_CONFIGS	2
+#define IMX258_2_LANE_MODE	0
+#define IMX258_4_LANE_MODE	1
+
 /* Link frequency config */
 struct imx258_link_freq_config {
 	u64 link_frequency;
 	u32 pixels_per_line;
 
 	/* PLL registers for this link frequency */
-	struct imx258_reg_list reg_list;
+	struct imx258_reg_list reg_list[IMX258_LANE_CONFIGS];
 };
 
 /* Mode : resolution and related config&values */
@@ -112,8 +116,30 @@  struct imx258_mode {
 	struct imx258_reg_list reg_list;
 };
 
-/* 4208x3120 needs 1267Mbps/lane, 4 lanes */
-static const struct imx258_reg mipi_1267mbps_19_2mhz[] = {
+/* 4208x3120 needs 1267Mbps/lane, 4 lanes. Use that rate on 2 lanes as well */
+static const struct imx258_reg mipi_1267mbps_19_2mhz_2l[] = {
+	{ 0x0136, 0x13 },
+	{ 0x0137, 0x33 },
+	{ 0x0301, 0x0A },
+	{ 0x0303, 0x02 },
+	{ 0x0305, 0x03 },
+	{ 0x0306, 0x00 },
+	{ 0x0307, 0xC6 },
+	{ 0x0309, 0x0A },
+	{ 0x030B, 0x01 },
+	{ 0x030D, 0x02 },
+	{ 0x030E, 0x00 },
+	{ 0x030F, 0xD8 },
+	{ 0x0310, 0x00 },
+
+	{ 0x0114, 0x01 },
+	{ 0x0820, 0x09 },
+	{ 0x0821, 0xa6 },
+	{ 0x0822, 0x66 },
+	{ 0x0823, 0x66 },
+};
+
+static const struct imx258_reg mipi_1267mbps_19_2mhz_4l[] = {
 	{ 0x0136, 0x13 },
 	{ 0x0137, 0x33 },
 	{ 0x0301, 0x05 },
@@ -127,16 +153,18 @@  static const struct imx258_reg mipi_1267mbps_19_2mhz[] = {
 	{ 0x030E, 0x00 },
 	{ 0x030F, 0xD8 },
 	{ 0x0310, 0x00 },
+
+	{ 0x0114, 0x03 },
 	{ 0x0820, 0x13 },
 	{ 0x0821, 0x4C },
 	{ 0x0822, 0xCC },
 	{ 0x0823, 0xCC },
 };
 
-static const struct imx258_reg mipi_1272mbps_24mhz[] = {
+static const struct imx258_reg mipi_1272mbps_24mhz_2l[] = {
 	{ 0x0136, 0x18 },
 	{ 0x0137, 0x00 },
-	{ 0x0301, 0x05 },
+	{ 0x0301, 0x0a },
 	{ 0x0303, 0x02 },
 	{ 0x0305, 0x04 },
 	{ 0x0306, 0x00 },
@@ -147,13 +175,59 @@  static const struct imx258_reg mipi_1272mbps_24mhz[] = {
 	{ 0x030E, 0x00 },
 	{ 0x030F, 0xD8 },
 	{ 0x0310, 0x00 },
+
+	{ 0x0114, 0x01 },
 	{ 0x0820, 0x13 },
 	{ 0x0821, 0x4C },
 	{ 0x0822, 0xCC },
 	{ 0x0823, 0xCC },
 };
 
-static const struct imx258_reg mipi_640mbps_19_2mhz[] = {
+static const struct imx258_reg mipi_1272mbps_24mhz_4l[] = {
+	{ 0x0136, 0x18 },
+	{ 0x0137, 0x00 },
+	{ 0x0301, 0x05 },
+	{ 0x0303, 0x02 },
+	{ 0x0305, 0x04 },
+	{ 0x0306, 0x00 },
+	{ 0x0307, 0xD4 },
+	{ 0x0309, 0x0A },
+	{ 0x030B, 0x01 },
+	{ 0x030D, 0x02 },
+	{ 0x030E, 0x00 },
+	{ 0x030F, 0xD8 },
+	{ 0x0310, 0x00 },
+
+	{ 0x0114, 0x03 },
+	{ 0x0820, 0x13 },
+	{ 0x0821, 0xE0 },
+	{ 0x0822, 0x00 },
+	{ 0x0823, 0x00 },
+};
+
+static const struct imx258_reg mipi_640mbps_19_2mhz_2l[] = {
+	{ 0x0136, 0x13 },
+	{ 0x0137, 0x33 },
+	{ 0x0301, 0x05 },
+	{ 0x0303, 0x02 },
+	{ 0x0305, 0x03 },
+	{ 0x0306, 0x00 },
+	{ 0x0307, 0x64 },
+	{ 0x0309, 0x0A },
+	{ 0x030B, 0x01 },
+	{ 0x030D, 0x02 },
+	{ 0x030E, 0x00 },
+	{ 0x030F, 0xD8 },
+	{ 0x0310, 0x00 },
+
+	{ 0x0114, 0x01 },
+	{ 0x0820, 0x05 },
+	{ 0x0821, 0x00 },
+	{ 0x0822, 0x00 },
+	{ 0x0823, 0x00 },
+};
+
+static const struct imx258_reg mipi_640mbps_19_2mhz_4l[] = {
 	{ 0x0136, 0x13 },
 	{ 0x0137, 0x33 },
 	{ 0x0301, 0x05 },
@@ -167,13 +241,37 @@  static const struct imx258_reg mipi_640mbps_19_2mhz[] = {
 	{ 0x030E, 0x00 },
 	{ 0x030F, 0xD8 },
 	{ 0x0310, 0x00 },
+
+	{ 0x0114, 0x03 },
 	{ 0x0820, 0x0A },
 	{ 0x0821, 0x00 },
 	{ 0x0822, 0x00 },
 	{ 0x0823, 0x00 },
 };
 
-static const struct imx258_reg mipi_642mbps_24mhz[] = {
+static const struct imx258_reg mipi_642mbps_24mhz_2l[] = {
+	{ 0x0136, 0x18 },
+	{ 0x0137, 0x00 },
+	{ 0x0301, 0x0A },
+	{ 0x0303, 0x02 },
+	{ 0x0305, 0x04 },
+	{ 0x0306, 0x00 },
+	{ 0x0307, 0x6B },
+	{ 0x0309, 0x0A },
+	{ 0x030B, 0x01 },
+	{ 0x030D, 0x02 },
+	{ 0x030E, 0x00 },
+	{ 0x030F, 0xD8 },
+	{ 0x0310, 0x00 },
+
+	{ 0x0114, 0x01 },
+	{ 0x0820, 0x0A },
+	{ 0x0821, 0x00 },
+	{ 0x0822, 0x00 },
+	{ 0x0823, 0x00 },
+};
+
+static const struct imx258_reg mipi_642mbps_24mhz_4l[] = {
 	{ 0x0136, 0x18 },
 	{ 0x0137, 0x00 },
 	{ 0x0301, 0x05 },
@@ -187,6 +285,8 @@  static const struct imx258_reg mipi_642mbps_24mhz[] = {
 	{ 0x030E, 0x00 },
 	{ 0x030F, 0xD8 },
 	{ 0x0310, 0x00 },
+
+	{ 0x0114, 0x03 },
 	{ 0x0820, 0x0A },
 	{ 0x0821, 0x00 },
 	{ 0x0822, 0x00 },
@@ -241,7 +341,6 @@  static const struct imx258_reg mode_common_regs[] = {
 	{ 0x5F05, 0xED },
 	{ 0x0112, 0x0A },
 	{ 0x0113, 0x0A },
-	{ 0x0114, 0x03 },
 	{ 0x0342, 0x14 },
 	{ 0x0343, 0xE8 },
 	{ 0x0344, 0x00 },
@@ -360,11 +459,13 @@  enum {
 
 /*
  * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample
- * data rate => double data rate; number of lanes => 4; bits per pixel => 10
+ * data rate => double data rate;
+ * number of lanes => (configurable 2 or 4);
+ * bits per pixel => 10
  */
-static u64 link_freq_to_pixel_rate(u64 f)
+static u64 link_freq_to_pixel_rate(u64 f, unsigned int nlanes)
 {
-	f *= 2 * 4;
+	f *= 2 * nlanes;
 	do_div(f, 10);
 
 	return f;
@@ -394,15 +495,27 @@  static const struct imx258_link_freq_config link_freq_configs_19_2[] = {
 	[IMX258_LINK_FREQ_1267MBPS] = {
 		.pixels_per_line = IMX258_PPL_DEFAULT,
 		.reg_list = {
-			.num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz),
-			.regs = mipi_1267mbps_19_2mhz,
+			[IMX258_2_LANE_MODE] = {
+				.num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_2l),
+				.regs = mipi_1267mbps_19_2mhz_2l,
+			},
+			[IMX258_4_LANE_MODE] = {
+				.num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_4l),
+				.regs = mipi_1267mbps_19_2mhz_4l,
+			},
 		}
 	},
 	[IMX258_LINK_FREQ_640MBPS] = {
 		.pixels_per_line = IMX258_PPL_DEFAULT,
 		.reg_list = {
-			.num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz),
-			.regs = mipi_640mbps_19_2mhz,
+			[IMX258_2_LANE_MODE] = {
+				.num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_2l),
+				.regs = mipi_640mbps_19_2mhz_2l,
+			},
+			[IMX258_4_LANE_MODE] = {
+				.num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_4l),
+				.regs = mipi_640mbps_19_2mhz_4l,
+			},
 		}
 	},
 };
@@ -411,15 +524,27 @@  static const struct imx258_link_freq_config link_freq_configs_24[] = {
 	[IMX258_LINK_FREQ_1267MBPS] = {
 		.pixels_per_line = IMX258_PPL_DEFAULT,
 		.reg_list = {
-			.num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz),
-			.regs = mipi_1272mbps_24mhz,
+			[IMX258_2_LANE_MODE] = {
+				.num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_2l),
+				.regs = mipi_1272mbps_24mhz_2l,
+			},
+			[IMX258_4_LANE_MODE] = {
+				.num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_4l),
+				.regs = mipi_1272mbps_24mhz_4l,
+			},
 		}
 	},
 	[IMX258_LINK_FREQ_640MBPS] = {
 		.pixels_per_line = IMX258_PPL_DEFAULT,
 		.reg_list = {
-			.num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz),
-			.regs = mipi_642mbps_24mhz,
+			[IMX258_2_LANE_MODE] = {
+				.num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_2l),
+				.regs = mipi_642mbps_24mhz_2l,
+			},
+			[IMX258_4_LANE_MODE] = {
+				.num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_4l),
+				.regs = mipi_642mbps_24mhz_4l,
+			},
 		}
 	},
 };
@@ -478,6 +603,7 @@  struct imx258 {
 
 	const struct imx258_link_freq_config *link_freq_configs;
 	const s64 *link_freq_menu_items;
+	unsigned int nlanes;
 
 	/*
 	 * Mutex for serialized access:
@@ -787,7 +913,7 @@  static int imx258_set_pad_format(struct v4l2_subdev *sd,
 		__v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index);
 
 		link_freq = imx258->link_freq_menu_items[mode->link_freq_index];
-		pixel_rate = link_freq_to_pixel_rate(link_freq);
+		pixel_rate = link_freq_to_pixel_rate(link_freq, imx258->nlanes);
 		__v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate);
 		/* Update limits and set FPS to default */
 		vblank_def = imx258->cur_mode->vts_def -
@@ -816,11 +942,13 @@  static int imx258_start_streaming(struct imx258 *imx258)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
 	const struct imx258_reg_list *reg_list;
+	const struct imx258_link_freq_config *link_freq_cfg;
 	int ret, link_freq_index;
 
 	/* Setup PLL */
 	link_freq_index = imx258->cur_mode->link_freq_index;
-	reg_list = &imx258->link_freq_configs[link_freq_index].reg_list;
+	link_freq_cfg = &imx258->link_freq_configs[link_freq_index];
+	reg_list = &link_freq_cfg->reg_list[imx258->nlanes == 2 ? 0 : 1];
 	ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs);
 	if (ret) {
 		dev_err(&client->dev, "%s failed to set plls\n", __func__);
@@ -1074,9 +1202,11 @@  static int imx258_init_controls(struct imx258 *imx258)
 		vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
 	pixel_rate_max =
-		link_freq_to_pixel_rate(imx258->link_freq_menu_items[0]);
+		link_freq_to_pixel_rate(imx258->link_freq_menu_items[0],
+					imx258->nlanes);
 	pixel_rate_min =
-		link_freq_to_pixel_rate(imx258->link_freq_menu_items[1]);
+		link_freq_to_pixel_rate(imx258->link_freq_menu_items[1],
+					imx258->nlanes);
 	/* By default, PIXEL_RATE is read only */
 	imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
 				V4L2_CID_PIXEL_RATE,
@@ -1174,6 +1304,10 @@  static int imx258_get_regulators(struct imx258 *imx258,
 static int imx258_probe(struct i2c_client *client)
 {
 	struct imx258 *imx258;
+	struct fwnode_handle *endpoint;
+	struct v4l2_fwnode_endpoint ep = {
+		.bus_type = V4L2_MBUS_CSI2_DPHY
+	};
 	int ret;
 	u32 val = 0;
 
@@ -1216,13 +1350,38 @@  static int imx258_probe(struct i2c_client *client)
 		return -EINVAL;
 	}
 
+	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
+	if (!endpoint) {
+		dev_err(&client->dev, "Endpoint node not found\n");
+		return -EINVAL;
+	}
+
+	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
+	fwnode_handle_put(endpoint);
+	if (ret == -ENXIO) {
+		dev_err(&client->dev, "Unsupported bus type, should be CSI2\n");
+		goto error_endpoint_poweron;
+	} else if (ret) {
+		dev_err(&client->dev, "Parsing endpoint node failed\n");
+		goto error_endpoint_poweron;
+	}
+
+	/* Get number of data lanes */
+	imx258->nlanes = ep.bus.mipi_csi2.num_data_lanes;
+	if (imx258->nlanes != 2 && imx258->nlanes != 4) {
+		dev_err(&client->dev, "Invalid data lanes: %u\n",
+			imx258->nlanes);
+		ret = -EINVAL;
+		goto error_endpoint_poweron;
+	}
+
 	/* Initialize subdev */
 	v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
 
 	/* Will be powered off via pm_runtime_idle */
 	ret = imx258_power_on(&client->dev);
 	if (ret)
-		return ret;
+		goto error_endpoint_poweron;
 
 	/* Check module identity */
 	ret = imx258_identify_module(imx258);
@@ -1255,6 +1414,7 @@  static int imx258_probe(struct i2c_client *client)
 	pm_runtime_set_active(&client->dev);
 	pm_runtime_enable(&client->dev);
 	pm_runtime_idle(&client->dev);
+	v4l2_fwnode_endpoint_free(&endpoint);
 
 	return 0;
 
@@ -1267,6 +1427,9 @@  static int imx258_probe(struct i2c_client *client)
 error_identify:
 	imx258_power_off(&client->dev);
 
+error_endpoint_poweron:
+	v4l2_fwnode_endpoint_free(&ep);
+
 	return ret;
 }