Re: [PATCH v2 2/2] media: i2c: Add imx283 camera sensor driver

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

 



On Thu, Mar 7, 2024 at 11:41 PM Umang Jain <umang.jain@xxxxxxxxxxxxxxxx> wrote:
>
> From: Kieran Bingham <kieran.bingham@xxxxxxxxxxxxxxxx>
>
> Add a v4l2 subdevice driver for the Sony IMX283 image sensor.
>
> The IMX283 is a 20MP Diagonal 15.86 mm (Type 1) CMOS Image Sensor with
> Square Pixel for Color Cameras.
>
> The following features are supported:
> - Manual exposure an gain control support
> - vblank/hblank/link freq control support
> - Test pattern support control
> - Arbitrary horizontal and vertical cropping
> - Supported resolution:
>   - 5472x3648 @ 20fps (SRGGB12)
>   - 5472x3648 @ 25fps (SRGGB10)
>   - 2736x1824 @ 50fps (SRGGB12)

...

+ array_size.h
+ bitops.h
+ container_of.h

> +#include <linux/clk.h>
> +#include <linux/delay.h>

+ errno.h

> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>

+ mutex.h

> +#include <linux/of.h>

How is this being used, please?

> +#include <linux/pm_runtime.h>

+ property.h

> +#include <linux/regulator/consumer.h>

+ types.h

> +#include <linux/units.h>

...

> +#define IMX283_HMAX_MAX                        0xffff

I would rather use the form of (BIT(16) - 1) to imply that this is
limited by bit field in the HW.

...

> +#define   IMX283_VMAX_MAX              0xfffff

Ditto.

...

> +#define IMX283_XCLR_MIN_DELAY_US       1000
> +#define IMX283_XCLR_DELAY_RANGE_US     1000

Perhaps (1 * USEC_PER_MSEC) ?

...

> +       /* Veritcal 2/9 subsampling, horizontal 3 binning cropping */

Vertical

...

> +static const s64 link_frequencies[] = {
> +       720 * MEGA, /* 1440 Mbps lane data rate */
> +       360 * MEGA, /* 720 Mbps data lane rate */

HZ_PER_MHZ

> +};

...

> +/* regulator supplies */
> +static const char *const imx283_supply_name[] = {
> +       "vadd", /* Analog (2.9V) supply */
> +       "vdd1", /* Supply Voltage 2 (1.8V) supply */
> +       "vdd2", /* Supply Voltage 3 (1.2V) supply */
> +};
> +
> +

One blank line is enough.

...

> +       default:
> +               *mode_list = NULL;
> +               *num_modes = 0;

break;

...

> +       u64 link_frequency = link_frequencies[__ffs(imx283->link_freq_bitmap)];
> +
> +       return link_frequency * ddr * lanes / bpp;

No overflow on 32-bit?

...

> +static u64 imx283_internal_clock(unsigned int pixel_rate, unsigned int pixels)
> +{
> +       /*
> +        * Determine the following operation without overflow:
> +        *    pixels = 72 * MEGA / pixel_rate

Use proper units in the comments, I believe you want MHz here.

> +        * The internal clock at 72MHz and Pixel Rate (between 240 and 576MHz)
> +        * can easily overflow this calculation, so pre-divide to simplify.
> +        */
> +       const u32 iclk_pre = 72 * MEGA / 1000000;

HZ_PER_MHZ
MEGA

(But why? wouldn't simply 72 be enough here?)

> +       const u32 pclk_pre = pixel_rate / 1000000;

MEGA

> +       return pixels * iclk_pre / pclk_pre;
> +}

...

> +static u64 imx283_iclk_to_pix(unsigned int pixel_rate, unsigned int cycles)

Same comments as per above function.

...

> +       do_div(numerator, hmax);

> +       numerator = clamp_t(u32, numerator, 0, 0xFFFFFFFF);

Please, avoid _t variant, better to use proper types.
Also the last should be U32_MAX, no?

> +       return numerator;

return clamp(...);

...

> +       max_shr = min_t(u64, max_shr, 0xFFFF);

No min_t() please. You probably wanted clamp() here.

...

> +/*
> + * Integration Time [s] = [{VMAX x (SVR + 1) – (SHR)} x HMAX + offset]
> + *                      / (72 x 10^6)

Indent better, so we can read easily.

> + */

...

> +       shr = (u32)(vmax * (svr + 1) - temp);
> +
> +       return shr;

Unneeded casting, simply

return vmas * ...;

...

> +               current_exposure = clamp_t(u32, current_exposure, min_exposure,
> +                                          max_exposure);

Can it be clamp()?

...

> +       case V4L2_CID_VBLANK:
> +               imx283->vmax = mode->height + ctrl->val;
> +               dev_dbg(imx283->dev, "V4L2_CID_VBLANK : %d  VMAX : %d\n",
> +                       ctrl->val, imx283->vmax);

%d or %u? Check ALL specifiers to have proper signedness.

> +               ret = cci_write(imx283->cci, IMX283_REG_VMAX, imx283->vmax, NULL);
> +               break;

...

> +       usleep_range(1000, 2000); /* 1st Stabilisation period of 1 ms or more */

fsleep()

...

> +       /* Apply customized values from controls (HMAX/VMAX/SHR) */
> +       ret =  __v4l2_ctrl_handler_setup(imx283->sd.ctrl_handler);
> +
> +       return ret;

return __v4l2_ctrl_handler_setup(...);

...

> +       ret = cci_write(imx283->cci, IMX283_REG_STANDBY, IMX283_STBLOGIC, NULL);
> +       if (ret)
> +               dev_err(imx283->dev, "%s failed to set stream\n", __func__);

Why __func__?

...

> +       ret = regulator_bulk_enable(ARRAY_SIZE(imx283_supply_name),
> +                                   imx283->supplies);
> +       if (ret) {
> +               dev_err(imx283->dev, "%s: failed to enable regulators\n",
> +                       __func__);
> +               return ret;

Ditto.

> +       }
> +
> +       ret = clk_prepare_enable(imx283->xclk);
> +       if (ret) {
> +               dev_err(imx283->dev, "%s: failed to enable clock\n",
> +                       __func__);

Ditto.

> +               goto reg_off;
> +       }

This Q applies to all error messages.

...

> +static int __maybe_unused imx283_power_off(struct device *dev)

No __maybe_unused in a new code.

...

> +       switch (sel->target) {
> +       case V4L2_SEL_TGT_CROP: {
> +               sel->r = *v4l2_subdev_state_get_crop(sd_state, 0);
> +               return 0;
> +       }
> +
> +       case V4L2_SEL_TGT_NATIVE_SIZE:
> +               sel->r = imx283_native_area;
> +               return 0;
> +
> +       case V4L2_SEL_TGT_CROP_DEFAULT:
> +       case V4L2_SEL_TGT_CROP_BOUNDS:
> +               sel->r = imx283_active_area;
> +               return 0;
> +       }
> +
> +       return -EINVAL;

Instead use default: case.

...

> +       mutex_lock(imx283->ctrl_handler.lock);

> +       mutex_unlock(imx283->ctrl_handler.lock);

Can you use cleanup.h?

...

> +static int imx283_parse_endpoint(struct imx283 *imx283)
> +{
> +       struct fwnode_handle *fwnode = dev_fwnode(imx283->dev);

Split this assignment that it will be easier to understand at the
first use where it came from.

> +       struct v4l2_fwnode_endpoint bus_cfg = {
> +               .bus_type = V4L2_MBUS_CSI2_DPHY
> +       };
> +       struct fwnode_handle *ep;
> +       int ret;

fwnode = ...

> +       if (!fwnode)
> +               return -ENXIO;
> +
> +       ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
> +       if (!ep) {
> +               dev_err(imx283->dev, "Failed to get next endpoint\n");
> +               return -ENXIO;
> +       }
> +
> +       ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
> +       fwnode_handle_put(ep);
> +       if (ret)
> +               return ret;
> +
> +       if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
> +               dev_err(imx283->dev,
> +                       "number of CSI2 data lanes %d is not supported\n",
> +                       bus_cfg.bus.mipi_csi2.num_data_lanes);
> +               ret = -EINVAL;
> +               goto done_endpoint_free;
> +       }
> +
> +       ret = v4l2_link_freq_to_bitmap(imx283->dev, bus_cfg.link_frequencies,
> +                                      bus_cfg.nr_of_link_frequencies,
> +                                      link_frequencies, ARRAY_SIZE(link_frequencies),
> +                                      &imx283->link_freq_bitmap);
> +
> +done_endpoint_free:
> +       v4l2_fwnode_endpoint_free(&bus_cfg);
> +
> +       return ret;
> +};

...

> +       imx283->cci = devm_cci_regmap_init_i2c(client, 16);
> +       if (IS_ERR(imx283->cci)) {
> +               ret = PTR_ERR(imx283->cci);
> +               dev_err(imx283->dev, "failed to initialize CCI: %d\n", ret);
> +               return ret;

What's wrong with

  return dev_err_probe(...);

?

> +       }

...

> +       if (!imx283->freq) {
> +               dev_err(imx283->dev, "xclk frequency unsupported: %d Hz\n", xclk_freq);
> +               return -EINVAL;

return dev_err_probe(...);

> +       }

...

> +       ret = imx283_parse_endpoint(imx283);
> +       if (ret) {
> +               dev_err(imx283->dev, "failed to parse endpoint configuration\n");
> +               return ret;

Ditto.

Make all messages in ->probe() stage to use the same pattern.

> +       }

...

> +       /* Request optional enable pin */
> +       imx283->reset_gpio = devm_gpiod_get_optional(imx283->dev, "reset",
> +                                                    GPIOD_OUT_LOW);

No error check?! Why?

...

> +       ret = media_entity_pads_init(&imx283->sd.entity, 1, &imx283->pad);
> +       if (ret) {
> +               dev_err(imx283->dev, "failed to init entity pads: %d\n", ret);

dev_err_probe(...);

> +               goto error_handler_free;
> +       }
> +
> +       imx283->sd.state_lock = imx283->ctrl_handler.lock;
> +       ret = v4l2_subdev_init_finalize(&imx283->sd);
> +       if (ret < 0) {
> +               dev_err(imx283->dev, "subdev init error: %d\n", ret);

Ditto.

> +               goto error_media_entity;
> +       }
> +
> +       ret = v4l2_async_register_subdev_sensor(&imx283->sd);
> +       if (ret < 0) {
> +               dev_err(imx283->dev, "failed to register sensor sub-device: %d\n", ret);

Ditto.

> +               goto error_subdev_cleanup;
> +       }

...

> +static const struct dev_pm_ops imx283_pm_ops = {
> +       SET_RUNTIME_PM_OPS(imx283_power_off, imx283_power_on, NULL)
> +};

Use the respective DEFINE_* PM macro.

...

> +static const struct of_device_id imx283_dt_ids[] = {
> +       { .compatible = "sony,imx283", },

Inner comma is not needed.

> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, imx283_dt_ids);
> +
> +static struct i2c_driver imx283_i2c_driver = {
> +       .driver = {
> +               .name = "imx283",
> +               .of_match_table = imx283_dt_ids,

> +               .pm = &imx283_pm_ops,

Missing respective PM macro.

> +       },
> +       .probe = imx283_probe,
> +       .remove = imx283_remove,
> +};

> +

Unneeded blank line.

> +module_i2c_driver(imx283_i2c_driver);

-- 
With Best Regards,
Andy Shevchenko





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

  Powered by Linux