[an error occurred while processing this directive]
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID:
 <PN3P287MB182950EC8691183FBFC4EC098BFBA@PN3P287MB1829.INDP287.PROD.OUTLOOK.COM>
Date: Thu, 30 Oct 2025 14:55:00 +0000
From: Tarang Raval <tarang.raval@...iconsignals.io>
To: Svyatoslav Ryhel <clamor95@...il.com>, Mauro Carvalho Chehab
	<mchehab@...nel.org>, Rob Herring <robh@...nel.org>, Krzysztof Kozlowski
	<krzk+dt@...nel.org>, Conor Dooley <conor+dt@...nel.org>, Sakari Ailus
	<sakari.ailus@...ux.intel.com>, Hans Verkuil <hverkuil@...all.nl>, Hans de
 Goede <hansg@...nel.org>, André Apitzsch <git@...tzsch.eu>,
	Sylvain Petinot <sylvain.petinot@...s.st.com>, Benjamin Mugnier
	<benjamin.mugnier@...s.st.com>, Dongcheng Yan <dongcheng.yan@...el.com>,
	Heimir Thor Sverrisson <heimir.sverrisson@...il.com>
CC: "linux-media@...r.kernel.org" <linux-media@...r.kernel.org>,
	"devicetree@...r.kernel.org" <devicetree@...r.kernel.org>,
	"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v3 2/2] media: i2c: add Sony IMX111 CMOS camera sensor
 driver
Hi Svyatoslav,
> Add a v4l2 sub-device driver for the Sony IMX111 image sensor. This is a
> camera sensor using the i2c bus for control and the csi-2 bus for data.
> 
> The following features are supported:
> - manual exposure, digital and analog gain control support
> - pixel rate/link freq control support
> - supported resolution up to 3280x2464 for single shot capture
> - supported resolution up to 1920x1080 @ 30fps for video
> - supported bayer order output SGBRG10 and SGBRG8
> 
> Camera module seems to be partially compatible with Nokia SMIA but it
> lacks a few registers required for clock calculations and has different
> vendor-specific per-mode configurations which makes it incompatible with
> existing CCS driver.
> 
> Signed-off-by: Svyatoslav Ryhel <clamor95@...il.com>
---
> +static int imx111_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +   struct imx111 *sensor = ctrl_to_imx111(ctrl);
> +   struct device *dev = regmap_get_device(sensor->regmap);
> +   s64 max;
> +   int ret = 0;
> +
> +   /* Propagate change of current control to all related controls */
> +   switch (ctrl->id) {
Do we need the switch statement, since only one case is present? 
You can use an 'if' instead. 
> +   case V4L2_CID_VBLANK:
> +         /* Update max exposure while meeting expected vblanking */
> +         max = sensor->cur_mode->height + ctrl->val - 5;
You can define a macro for the value 5 to improve readability.
Also, make this change in the init_control function. 
> +         __v4l2_ctrl_modify_range(sensor->exposure,
> +                            sensor->exposure->minimum,
> +                            max, sensor->exposure->step, max);
This may fail; consider adding an error check.
> +         break;
> +   }
> +
> +   /*
> +    * Applying V4L2 control value only happens
> +    * when power is up for streaming
> +    */
> +   if (!pm_runtime_get_if_in_use(dev))
> +         return 0;
> +
> +   switch (ctrl->id) {
> +   case V4L2_CID_ANALOGUE_GAIN:
> +         cci_write(sensor->regmap, IMX111_REG_ANALOG_GAIN, ctrl->val, &ret);
> +         break;
> +   case V4L2_CID_DIGITAL_GAIN:
> +         ret = imx111_update_digital_gain(sensor, ctrl->val);
> +         break;
> +   case V4L2_CID_EXPOSURE:
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     IMX111_GROUP_WRITE_ON, &ret);
> +         cci_write(sensor->regmap, IMX111_INTEGRATION_TIME, ctrl->val, &ret);
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     0, &ret);
> +         break;
> +   case V4L2_CID_HBLANK:
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     IMX111_GROUP_WRITE_ON, &ret);
> +         dev_err(dev, "writing 0x%x to HTL\n", sensor->cur_mode->width + ctrl->val);
> +         cci_write(sensor->regmap, IMX111_HORIZONTAL_TOTAL_LENGTH,
> +                 sensor->cur_mode->width + ctrl->val, &ret);
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     0, &ret);
> +         break;
> +   case V4L2_CID_VBLANK:
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     IMX111_GROUP_WRITE_ON, &ret);
> +         dev_err(dev, "writing 0x%x to VTL\n", sensor->cur_mode->height + ctrl->val);
> +         cci_write(sensor->regmap, IMX111_VERTICAL_TOTAL_LENGTH,
> +                 sensor->cur_mode->height + ctrl->val, &ret);
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     0, &ret);
> +         break;
> +   case V4L2_CID_HFLIP:
> +   case V4L2_CID_VFLIP:
> +         cci_write(sensor->regmap, IMX111_IMAGE_ORIENTATION,
> +                 sensor->hflip->val | sensor->vflip->val << 1, &ret);
> +         break;
> +   case V4L2_CID_TEST_PATTERN:
> +         cci_write(sensor->regmap, IMX111_TEST_PATTERN, ctrl->val, &ret);
> +         break;
> +   case V4L2_CID_TEST_PATTERN_RED:
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     IMX111_GROUP_WRITE_ON, &ret);
> +         cci_write(sensor->regmap, IMX111_SOLID_COLOR_RED, ctrl->val, &ret);
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     0, &ret);
> +         break;
> +   case V4L2_CID_TEST_PATTERN_GREENR:
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     IMX111_GROUP_WRITE_ON, &ret);
> +         cci_write(sensor->regmap, IMX111_SOLID_COLOR_GR, ctrl->val, &ret);
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     0, &ret);
> +         break;
> +   case V4L2_CID_TEST_PATTERN_BLUE:
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     IMX111_GROUP_WRITE_ON, &ret);
> +         cci_write(sensor->regmap, IMX111_SOLID_COLOR_BLUE, ctrl->val, &ret);
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     0, &ret);
> +         break;
> +   case V4L2_CID_TEST_PATTERN_GREENB:
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     IMX111_GROUP_WRITE_ON, &ret);
> +         cci_write(sensor->regmap, IMX111_SOLID_COLOR_GB, ctrl->val, &ret);
> +         cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +                     0, &ret);
> +         break;
> +   default:
> +         ret = -EINVAL;
> +   }
> +
> +   pm_runtime_put(dev);
> +
> +   return ret;
> +}
---
> +static int imx111_init_controls(struct imx111 *sensor)
> +{
> +   const struct v4l2_ctrl_ops *ops = &imx111_ctrl_ops;
> +   struct device *dev = regmap_get_device(sensor->regmap);
> +   const struct imx111_mode *mode = sensor->cur_mode;
> +   struct v4l2_fwnode_device_properties props;
> +   struct v4l2_subdev *sd = &sensor->sd;
No need for a new variable; there is only one user in the function.
> +   struct v4l2_ctrl_handler *hdl = &sensor->hdl;
> +   s64 pixel_rate_min, pixel_rate_max;
> +   int i, ret;
> +
> +   ret = v4l2_fwnode_device_parse(dev, &props);
> +   if (ret < 0)
> +         return ret;
> +
> +   ret = v4l2_ctrl_handler_init(hdl, 13);
Now there are 15 controls. 
No need for explicit error checking; you can omit the error check if you'd like.
> +   if (ret)
> +         return ret;
> +
> +   pixel_rate_min = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW10);
> +   pixel_rate_max = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW8);
> +   sensor->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE,
> +                                  pixel_rate_min, pixel_rate_max,
> +                                  1, div_u64(sensor->pixel_clk_raw,
> +                                  2 * sensor->data_depth));
> +
> +   sensor->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ,
> +                                    0, 0, &sensor->default_link_freq);
> +   if (sensor->link_freq)
> +         sensor->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +   v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
> +                 IMX111_ANA_GAIN_MIN, IMX111_ANA_GAIN_MAX,
> +                 IMX111_ANA_GAIN_STEP, IMX111_ANA_GAIN_DEFAULT);
> +
> +   v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
> +                 IMX111_DGTL_GAIN_MIN, IMX111_DGTL_GAIN_MAX,
> +                 IMX111_DGTL_GAIN_STEP, IMX111_DGTL_GAIN_DEFAULT);
> +
> +   sensor->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
> +   if (sensor->hflip)
> +         sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> +
> +   sensor->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
> +   if (sensor->vflip)
> +         sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> +
> +   sensor->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, IMX111_VBLANK_MIN,
> +                              IMX111_VTL_MAX - mode->height, 1,
> +                              mode->vtl_def - mode->height);
> +   sensor->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, IMX111_HBLANK_MIN,
> +                              IMX111_HTL_MAX - mode->width, 1,
> +                              mode->htl_def - mode->width);
> +
> +   /*
> +    * The maximum coarse integration time is the frame length in lines
> +    * minus five.
> +    */
> +   sensor->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
> +                                IMX111_INTEGRATION_TIME_MIN,
> +                                IMX111_PIXEL_ARRAY_HEIGHT - 5,
> +                                IMX111_INTEGRATION_TIME_STEP,
> +                                IMX111_PIXEL_ARRAY_HEIGHT - 5);
> +
> +   v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
> +
> +   v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
> +                          ARRAY_SIZE(test_pattern_menu) - 1, 0, 0,
> +                          test_pattern_menu);
> +   for (i = 0; i < 4; i++) {
> +         /*
> +          * The assumption is that
> +          * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
> +          * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
> +          * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
> +          */
> +         v4l2_ctrl_new_std(hdl, ops, V4L2_CID_TEST_PATTERN_RED + i,
> +                       IMX111_TESTP_COLOUR_MIN, IMX111_TESTP_COLOUR_MAX,
> +                       IMX111_TESTP_COLOUR_STEP, IMX111_TESTP_COLOUR_MAX);
> +         /* The "Solid color" pattern is white by default */
> +   }
> +
> +   if (hdl->error)
> +         return hdl->error;
> +
> +   sd->ctrl_handler = hdl;
> +
> +   return 0;
> +};
---
> +static int imx111_initialize(struct imx111 *sensor)
> +{
> +   struct device *dev = regmap_get_device(sensor->regmap);
> +   int ret;
ret = 0;
> +
> +   /* Configure the PLL. */
> +   cci_write(sensor->regmap, IMX111_PRE_PLL_CLK_DIVIDER_PLL1,
> +           sensor->pll->pre_div, &ret);
> +   cci_write(sensor->regmap, IMX111_PLL_MULTIPLIER_PLL1, sensor->pll->mult, &ret);
> +   cci_write(sensor->regmap, IMX111_POST_DIVIDER, IMX111_POST_DIVIDER_DIV1, &ret);
> +   cci_write(sensor->regmap, IMX111_PLL_SETTLING_TIME,
> +           to_settle_delay(sensor->pll->extclk_rate), &ret);
> +
> +   ret = cci_multi_reg_write(sensor->regmap, imx111_global_init,
> +                       ARRAY_SIZE(imx111_global_init), NULL);
You are overwriting the previous errors.
please use ret |= 
> +   if (ret < 0) {
> +         dev_err(dev, "Failed to initialize the sensor\n");
> +         return ret;
> +   }
> +
> +   return 0;
> +}
---
> +static int imx111_set_format(struct v4l2_subdev *sd,
> +                    struct v4l2_subdev_state *state,
> +                    struct v4l2_subdev_format *format)
> +{
> +   struct imx111 *sensor = sd_to_imx111(sd);
> +   struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
> +   struct v4l2_mbus_framefmt *fmt;
> +   const struct imx111_mode *mode;
> +
> +   mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes),
> +                           width, height,
> +                           mbus_fmt->width, mbus_fmt->height);
> +
> +   fmt = v4l2_subdev_state_get_format(state, format->pad);
> +
> +   fmt->code = imx111_get_format_code(sensor, mbus_fmt->code, false);
> +   fmt->width = mode->width;
> +   fmt->height = mode->height;
> +   fmt->colorspace = V4L2_COLORSPACE_RAW;
> +
> +   *mbus_fmt = *fmt;
> +
> +   if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> +         sensor->cur_mode = mode;
> +         sensor->data_depth = imx111_get_format_bpp(fmt);
> +         __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate,
> +                            div_u64(sensor->pixel_clk_raw, 2 * sensor->data_depth));
> +
> +         __v4l2_ctrl_modify_range(sensor->vblank, IMX111_VBLANK_MIN,
> +                            IMX111_VTL_MAX - mode->height, 1,
> +                            mode->vtl_def - mode->height);
> +         __v4l2_ctrl_s_ctrl(sensor->vblank, mode->vtl_def - mode->height);
> +
> +         __v4l2_ctrl_modify_range(sensor->hblank, IMX111_HBLANK_MIN,
> +                            IMX111_HTL_MAX - mode->width, 1,
> +                            mode->htl_def - mode->width);
> +         __v4l2_ctrl_s_ctrl(sensor->hblank, mode->htl_def - mode->width);
All the above V4L2 operations need to check for errors.
> +   }
> +
> +   return 0;
> +}
---
> +static int imx111_identify_module(struct imx111 *sensor)
> +{
> +   struct device *dev = regmap_get_device(sensor->regmap);
> +   u64 value, revision, manufacturer;
> +   int ret;
> +
> +   ret = cci_read(sensor->regmap, IMX111_PRODUCT_ID, &value, NULL);
> +   if (ret)
> +         return ret;
> +
> +   if (value != IMX111_CHIP_ID) {
> +         dev_err(dev, "chip id mismatch: %x!=%04llx", IMX111_CHIP_ID, value);
> +         return -ENXIO;
> +   }
> +
> +   cci_read(sensor->regmap, IMX111_REVISION, &revision, NULL);
> +   cci_read(sensor->regmap, IMX111_MANUFACTURER_ID, &manufacturer, NULL);
Instead of NULL, pass ret for the error code, and return ret at the end.
> +
> +   dev_dbg(dev, "module IMX%03llx rev. %llu manufacturer %llu\n",
> +         value, revision, manufacturer);
> +
> +   return 0;
> +}
> +
> +static int imx111_clk_init(struct imx111 *sensor)
> +{
> +   struct device *dev = regmap_get_device(sensor->regmap);
> +   u32 ndata_lanes = sensor->bus_cfg.bus.mipi_csi2.num_data_lanes;
> +   u64 extclk_rate, system_clk;
> +   unsigned int i;
> +
> +   extclk_rate = clk_get_rate(sensor->extclk);
> +   if (!extclk_rate)
> +         return dev_err_probe(dev, -EINVAL, "EXTCLK rate unknown\n");
> +
> +   for (i = 0; i < ARRAY_SIZE(imx111_pll); i++) {
> +         if (clk_get_rate(sensor->extclk) == imx111_pll[i].extclk_rate) {
> +               sensor->pll = &imx111_pll[i];
> +               break;
> +         }
> +   }
> +   if (!sensor->pll)
> +         return dev_err_probe(dev, -EINVAL, "Unsupported EXTCLK rate %llu\n", extclk_rate);
Max line length should be 80 columns. This applies everywhere the line 
length exceeds 80 characters.
> +
> +   system_clk = div_u64(extclk_rate, sensor->pll->pre_div) * sensor->pll->mult;
> +
> +   /*
> +    * Pixel clock or Logic clock is used for internal image processing is
> +    * generated by dividing into 1/10 or 1/8 frequency according to the
> +    * word length of the CSI2 interface. This clock is designating the pixel
> +    * rate and used as the base of integration time, frame rate etc.
> +    */
> +   sensor->pixel_clk_raw = system_clk * ndata_lanes;
> +
> +   /*
> +    * The CSI-2 bus is clocked for 16-bit per pixel, transmitted in DDR over n lanes
> +    * for RAW10 default format.
> +    */
> +   sensor->default_link_freq = div_u64(sensor->pixel_clk_raw * 8,
> +                               2 * IMX111_DATA_DEPTH_RAW10);
> +
> +   if (sensor->bus_cfg.nr_of_link_frequencies != 1 ||
> +       sensor->bus_cfg.link_frequencies[0] != sensor->default_link_freq)
> +         return dev_err_probe(dev, -EINVAL,
> +                          "Unsupported DT link-frequencies, expected %llu\n",
> +                          sensor->default_link_freq);
> +
> +   return 0;
> +}
---
> +static const struct of_device_id imx111_of_match[] = {
> +   { .compatible = "sony,imx111" },
> +   { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, imx111_of_match);
> +
> +static struct i2c_driver imx111_i2c_driver = {
> +   .driver = {
> +         .name = "imx111",
> +         .of_match_table = imx111_of_match,
> +         .pm = &imx111_pm_ops,
> +   },
> +   .probe = imx111_probe,
> +   .remove = imx111_remove,
> +};
> +module_i2c_driver(imx111_i2c_driver);
> +
> +MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@...il.com>");
> +MODULE_DESCRIPTION("Sony IMX111 CMOS Image Sensor driver");
> +MODULE_LICENSE("GPL");
Best Regards,
Tarang
Powered by blists - more mailing lists
 
