Re: [PATCH v2 2/2] media: i2c: Add support for alvium camera

From: Tommaso Merciai
Date: Mon May 29 2023 - 06:08:53 EST


Hi Christophe,
Thanks for the review.

On Fri, May 26, 2023 at 08:39:44PM +0200, Christophe JAILLET wrote:
> Le 26/05/2023 à 19:39, Tommaso Merciai a écrit :
> > The Alvium camera is shipped with sensor + isp in the same housing.
> > The camera can be equipped with one out of various sensor and abstract
> > the user from this. Camera is connected via MIPI CSI-2.
> >
> > Most of the sensor's features are supported, with the main exception
> > being fw update.
> >
> > The driver provides all mandatory, optional and recommended V4L2 controls
> > for maximum compatibility with libcamera
> >
> > References:
> > - https://www.alliedvision.com/en/products/embedded-vision-solutions
> >
> > Signed-off-by: Tommaso Merciai <tomm.merciai@xxxxxxxxx>
> > ---
>
> Hi,
>
> a few nit below, should it help.
>
>
> > +static int alvium_setup_mipi_fmt(struct alvium_dev *alvium)
> > +{
> > + int sz = 0;
> > + int fmt = 0;
>
> No need to init.
> If the loop index was just 'i', the code below would be slighly less
> verbose.
>
> > + int avail_fmt_cnt = 0;
> > +
> > + alvium->alvium_csi2_fmt = NULL;
> > +
> > + /* calculate fmt array size */
> > + for (fmt = 0; fmt < ALVIUM_NUM_SUPP_MIPI_DATA_FMT; fmt++) {
> > + if (alvium->is_mipi_fmt_avail[alvium_csi2_fmts[fmt].fmt_av_bit]) {
> > + if (!alvium_csi2_fmts[fmt].is_raw) {
> > + sz++;
> > + } else if (alvium_csi2_fmts[fmt].is_raw &&
> > + alvium->is_bay_avail[alvium_csi2_fmts[fmt].bay_av_bit]) {
>
> It is makes sense, this if/else looks equivalent to:
>
> if (!alvium_csi2_fmts[fmt].is_raw ||
> alvium->is_bay_avail[alvium_csi2_fmts[fmt].bay_av_bit]) {
> sz++;
>
> The same simplification could also be applied in the for loop below.

I Don't agree on this.
This is not a semplification.
This change the logic of the statement.

Also initialization of sz and avail_fmt_cnt is needed.
Maybe I can include fmt variable initialization into the for loop:

for (int fmt = 0; fmt < ALVIUM_NUM_SUPP_MIPI_DATA_FMT; fmt++) {

But from my perspective this seems clear.

>
> > + sz++;
> > + }
> > + }
> > + }
> > +
> > + /* init alvium_csi2_fmt array */
> > + alvium->alvium_csi2_fmt_n = sz;
> > + alvium->alvium_csi2_fmt = kmalloc((
> > + sizeof(struct alvium_pixfmt) * sz),
> > + GFP_KERNEL);
>
> kmalloc_array()?
> Also some unneeded ( and )

Thanks for this tip.

>
> > +
> > + /* Create the alvium_csi2 fmt array from formats available */
> > + for (fmt = 0; fmt < ALVIUM_NUM_SUPP_MIPI_DATA_FMT; fmt++) {
> > + if (alvium->is_mipi_fmt_avail[alvium_csi2_fmts[fmt].fmt_av_bit]) {
> > + if (!alvium_csi2_fmts[fmt].is_raw) {
> > + alvium->alvium_csi2_fmt[avail_fmt_cnt] =
> > + alvium_csi2_fmts[fmt];
> > + avail_fmt_cnt++;
> > + } else if (alvium_csi2_fmts[fmt].is_raw &&
> > + alvium->is_bay_avail[alvium_csi2_fmts[fmt].bay_av_bit]) {
> > + alvium->alvium_csi2_fmt[avail_fmt_cnt] =
> > + alvium_csi2_fmts[fmt];
> > + avail_fmt_cnt++;
> > + }
> > + }
> > + }
> > +
> > + return 0;
> > +}
>
> [...]
>
> > +struct alvium_mode {
> > + struct v4l2_rect crop;
> > + struct v4l2_mbus_framefmt fmt;
> > + u32 width;
> > + u32 height;
> > +
>
> Useless NL.

Right, thanks.

>
> > +};
> > +
> > +struct alvium_pixfmt {
> > + u8 id;
> > + u32 code;
> > + u32 colorspace;
> > + u8 fmt_av_bit;
> > + u8 bay_av_bit;
> > + u64 mipi_fmt_regval;
> > + u64 bay_fmt_regval;
> > + u8 is_raw;
>
> If moved a few lines above, there would be less holes in the struct.

?

>
> > +};
> > +
>
> [...]
>
> > +struct alvium_dev {
> > + struct i2c_client *i2c_client;
> > + struct v4l2_subdev sd;
> > + struct v4l2_fwnode_endpoint ep;
> > + struct media_pad pad;
> > +
> > + struct mutex lock;
> > +
> > + struct gpio_desc *reset_gpio;
> > + struct gpio_desc *pwdn_gpio;
> > +
> > + u16 bcrm_addr;
> > + alvium_bcrm_vers_t bcrm_v;
> > + alvium_fw_vers_t fw_v;
> > +
> > + alvium_avail_feat_t avail_ft;
> > + u8 is_mipi_fmt_avail[ALVIUM_NUM_SUPP_MIPI_DATA_BIT];
> > + u8 is_bay_avail[ALVIUM_NUM_BAY_AV_BIT];
> > +
> > + u32 min_csi_clk;
> > + u32 max_csi_clk;
> > + u32 img_min_width;
> > + u32 img_max_width;
> > + u32 img_inc_width;
> > + u32 img_min_height;
> > + u32 img_max_height;
> > + u32 img_inc_height;
> > + u32 min_offx;
> > + u32 max_offx;
> > + u32 inc_offx;
> > + u32 min_offy;
> > + u32 max_offy;
> > + u32 inc_offy;
> > + u64 min_gain;
> > + u64 max_gain;
> > + u64 inc_gain;
> > + u64 min_exp;
> > + u64 max_exp;
> > + u64 inc_exp;
> > + u64 min_rbalance;
> > + u64 max_rbalance;
> > + u64 inc_rbalance;
> > + u64 min_bbalance;
> > + u64 max_bbalance;
> > + u64 inc_bbalance;
> > + s32 min_hue;
> > + s32 max_hue;
> > + s32 inc_hue;
> > + u32 min_contrast;
> > + u32 max_contrast;
> > + u32 inc_contrast;
> > + u32 min_sat;
> > + u32 max_sat;
> > + u32 inc_sat;
> > + s32 min_black_lvl;
> > + s32 max_black_lvl;
> > + s32 inc_black_lvl;
> > + u64 min_gamma;
> > + u64 max_gamma;
> > + u64 inc_gamma;
> > + s32 min_sharp;
> > + s32 max_sharp;
> > + s32 inc_sharp;
> > +
> > + u32 streamon_delay;
> > +
> > + struct alvium_mode mode;
> > + struct v4l2_fract frame_interval;
> > + u64 min_fr;
> > + u64 max_fr;
> > + u64 fr;
> > +
> > + u8 h_sup_csi_lanes;
> > + struct clk *xclk;
> > + u32 xclk_freq;
> > + u32 csi_clk;
> > + u64 link_freq;
> > +
> > + struct alvium_ctrls ctrls;
> > +
> > + u8 bcrm_mode;
> > + u8 hshake_bit;
>
> What is the need of keeping this value in the struct?
> Its usage seems to be only local to some function (read from HW, then used)
>
> Should it be kept, does it make sense to have it a u8:1 and maybe some !! in
> the code, to pack it with the bitfield just a few lines below.

I don't agree on this.
Those variable are not used only locally.
Also v4l2 ctrls use those variables.
We need to keep into the priv struct of the driver.

>
>
> > +
> > + struct alvium_pixfmt *alvium_csi2_fmt;
> > + u8 alvium_csi2_fmt_n;
> > + struct v4l2_mbus_framefmt fmt;
> > +
> > + u8 streaming:1;
> > + u8 apply_fiv:1;
> > +
> > + bool upside_down;
>
> This looks only written. Is it useles or here for future use?
> Can these fields be all u8:1, or bool:1 ?

Rotation is not used.
I will drop this in v3. Thanks!

Regards,
Tommaso

>
> CJ
>
> > +};
> > +
> > +static inline struct alvium_dev *sd_to_alvium(struct v4l2_subdev *sd)
> > +{
> > + return container_of(sd, struct alvium_dev, sd);
> > +}
> > +
> > +static inline struct alvium_dev *i2c_to_alvium(struct i2c_client *client)
> > +{
> > + return sd_to_alvium(i2c_get_clientdata(client));
> > +}
> > +
> > +static inline bool alvium_is_csi2(const struct alvium_dev *alvium)
> > +{
> > + return alvium->ep.bus_type == V4L2_MBUS_CSI2_DPHY;
> > +}
> > +
> > +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
> > +{
> > + return &container_of(ctrl->handler, struct alvium_dev,
> > + ctrls.handler)->sd;
> > +}
> > +#endif /* ALVIUM_H_ */
>