[09/21] media: i2c: imx258: Add support for running on 2 CSI data lanes
Commit Message
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
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
>
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
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
>
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
> >
@@ -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;
}