diff mbox series

[2/2] media: i2c: imx290: Add support for imx327 variant

Message ID 20230203102439.237527-3-alexander.stein@ew.tq-group.com (mailing list archive)
State New, archived
Headers show
Series media: i2c: imx290: imx327 support | expand

Commit Message

Alexander Stein Feb. 3, 2023, 10:24 a.m. UTC
Both sensors are quite similar. Their specs only differ regarding LVDS
and parallel output but are identical regarding MIPI-CSI-2 interface.
But they use a different init setting of hard-coded values, taken from
the datasheet.

Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
---
 drivers/media/i2c/imx290.c | 88 +++++++++++++++++++++++++++++++++-----
 1 file changed, 77 insertions(+), 11 deletions(-)

Comments

Dave Stevenson Feb. 3, 2023, 10:55 a.m. UTC | #1
Hi Alexander

On Fri, 3 Feb 2023 at 10:24, Alexander Stein
<alexander.stein@ew.tq-group.com> wrote:
>
> Both sensors are quite similar. Their specs only differ regarding LVDS
> and parallel output but are identical regarding MIPI-CSI-2 interface.
> But they use a different init setting of hard-coded values, taken from
> the datasheet.
>
> Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
> ---
>  drivers/media/i2c/imx290.c | 88 +++++++++++++++++++++++++++++++++-----
>  1 file changed, 77 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index e642e1df520d..337252b2ec15 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -164,6 +164,36 @@
>  #define CLK_74_25      1
>  #define NUM_CLK                2
>
> +enum imx290_model {
> +       IMX290,
> +       IMX290_MONO,
> +       IMX327,
> +};
> +
> +struct imx290_device_data {
> +       enum imx290_model model;
> +       const char *name;
> +       u8 mono;
> +};
> +
> +static const struct imx290_device_data imx290_models[] = {
> +       [IMX290] = {
> +               .model = IMX290,
> +               .name = "imx290",
> +               .mono = 0,
> +       },
> +       [IMX290_MONO] = {
> +               .model = IMX290_MONO,
> +               .name = "imx290-mono",
> +               .mono = 1,
> +       },
> +       [IMX327] = {
> +               .model = IMX327,
> +               .name = "imx327",
> +               .mono = 0,
> +       },
> +};
> +
>  struct imx290_regval {
>         u32 reg;
>         u32 val;
> @@ -210,9 +240,9 @@ struct imx290 {
>         struct device *dev;
>         struct clk *xclk;
>         struct regmap *regmap;
> +       const struct imx290_device_data *devdata;
>         u32 xclk_freq;
>         u8 nlanes;
> -       u8 mono;
>
>         struct v4l2_subdev sd;
>         struct media_pad pad;
> @@ -240,7 +270,7 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
>   * Modes and formats
>   */
>
> -static const struct imx290_regval imx290_global_init_settings[] = {
> +static const struct imx290_regval imx290_global_init_settings_290[] = {
>         { IMX290_WINWV_OB, 12 },
>         { IMX290_WINPH, 0 },
>         { IMX290_WINPV, 0 },
> @@ -292,6 +322,23 @@ static const struct imx290_regval imx290_global_init_settings[] = {
>         { IMX290_REG_8BIT(0x33b3), 0x04 },
>  };
>
> +static const struct imx290_regval imx290_global_init_settings_327[] = {
> +       { IMX290_WINWV_OB, 12 },
> +       { IMX290_WINPH, 0 },
> +       { IMX290_WINPV, 0 },
> +       { IMX290_WINWH, 1948 },
> +       { IMX290_WINWV, 1097 },
> +       { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC |
> +                          IMX290_XSOUTSEL_XHSOUTSEL_HSYNC },
> +       { IMX290_REG_8BIT(0x3011), 0x0A },

What datasheet are you working from? Mine (2019/03/25) has a
correction listed at v0.3 of:
Register 3011h setting 0Ah -> 02h

> +       { IMX290_REG_8BIT(0x3012), 0x64 },
> +       { IMX290_REG_8BIT(0x3013), 0x00 },
> +       { IMX290_REG_8BIT(0x309e), 0x4A },
> +       { IMX290_REG_8BIT(0x309f), 0x4A },

309e/f undocumented in my datasheet beyond "default value 5Ah, set to "4Ah"".
Not documented in imx290 or imx462 datasheets either. I'll read it
back from IMX290 and IMX462 when I get to the office and see if 0x4a
is the default anyway, in which case it can be generic.

> +       { IMX290_REG_8BIT(0x3128), 0x04 },

Correction v0.3 - register address 3128h deleted.

> +       { IMX290_REG_8BIT(0x313b), 0x41 },

Correction v0.3 - Register address 313Bh setting 41h -> 61h.


I'll check the defaults on imx290 and imx462, because there is no harm
in adding those register writes if they happen to be the defaults.
There is also a fair amount of duplication between
imx290_global_init_settings_290 and imx290_global_init_settings_327 -
it'd be nice to reduce it down to the minimum set of diffs.

> +};
> +
>  static const struct imx290_regval imx290_37_125mhz_clock[] = {
>         { IMX290_EXTCK_FREQ, 0x2520 },
>         { IMX290_INCKSEL7, 0x49 },
> @@ -558,7 +605,7 @@ imx290_format_info(const struct imx290 *imx290, u32 code)
>         for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) {
>                 const struct imx290_format_info *info = &imx290_formats[i];
>
> -               if (info->code[imx290->mono] == code)
> +               if (info->code[imx290->devdata->mono] == code)
>                         return info;
>         }
>
> @@ -957,11 +1004,27 @@ static int imx290_start_streaming(struct imx290 *imx290,
>                                   struct v4l2_subdev_state *state)
>  {
>         const struct v4l2_mbus_framefmt *format;
> +       const struct imx290_regval *regs;
> +       unsigned int reg_num;
>         int ret;
>
> +       switch (imx290->devdata->model) {
> +       case IMX290:
> +       case IMX290_MONO:
> +               regs = imx290_global_init_settings_290;
> +               reg_num = ARRAY_SIZE(imx290_global_init_settings_290);
> +               break;
> +       case IMX327:
> +               regs = imx290_global_init_settings_327;
> +               reg_num = ARRAY_SIZE(imx290_global_init_settings_327);
> +               break;
> +       default:
> +               dev_err(imx290->dev, "Invalid model: %u\n", imx290->devdata->model);
> +               return -EINVAL;
> +       }
> +
>         /* Set init register settings */
> -       ret = imx290_set_register_array(imx290, imx290_global_init_settings,
> -                                       ARRAY_SIZE(imx290_global_init_settings));
> +       ret = imx290_set_register_array(imx290, regs, reg_num);
>         if (ret < 0) {
>                 dev_err(imx290->dev, "Could not set init registers\n");
>                 return ret;
> @@ -1072,7 +1135,7 @@ static int imx290_enum_mbus_code(struct v4l2_subdev *sd,
>         if (code->index >= ARRAY_SIZE(imx290_formats))
>                 return -EINVAL;
>
> -       code->code = imx290_formats[code->index].code[imx290->mono];
> +       code->code = imx290_formats[code->index].code[imx290->devdata->mono];
>
>         return 0;
>  }
> @@ -1114,7 +1177,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
>         fmt->format.height = mode->height;
>
>         if (!imx290_format_info(imx290, fmt->format.code))
> -               fmt->format.code = imx290_formats[0].code[imx290->mono];
> +               fmt->format.code = imx290_formats[0].code[imx290->devdata->mono];
>
>         fmt->format.field = V4L2_FIELD_NONE;
>         fmt->format.colorspace = V4L2_COLORSPACE_RAW;
> @@ -1422,8 +1485,9 @@ static s64 imx290_check_link_freqs(const struct imx290 *imx290,
>  }
>
>  static const struct of_device_id imx290_of_match[] = {
> -       { .compatible = "sony,imx290" },
> -       { .compatible = "sony,imx290-mono", .data = (void *)1 },
> +       { .compatible = "sony,imx290", .data = &imx290_models[IMX290] },
> +       { .compatible = "sony,imx290-mono", .data = &imx290_models[IMX290_MONO] },
> +       { .compatible = "sony,imx327",  .data = &imx290_models[IMX327] },

Based on Laurent's requests my parent to this set will be switching to
imx290 (as legacy), imx290lqr and imx290llr as the compatible strings.
imx327 ought to follow the same pattern.

  Dave

>         { /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, imx290_of_match);
> @@ -1441,8 +1505,7 @@ static int imx290_parse_dt(struct imx290 *imx290)
>         s64 fq;
>
>         match = i2c_of_match_device(imx290_of_match, client);
> -       if (match)
> -               imx290->mono = match->data ? 1 : 0;
> +       imx290->devdata = match->data;
>
>         endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev), NULL);
>         if (!endpoint) {
> @@ -1561,6 +1624,9 @@ static int imx290_probe(struct i2c_client *client)
>         if (ret)
>                 goto err_pm;
>
> +       v4l2_i2c_subdev_set_name(&imx290->sd, client,
> +                                imx290->devdata->name, NULL);
> +
>         /*
>          * Finally, register the V4L2 subdev. This must be done after
>          * initializing everything as the subdev can be used immediately after
> --
> 2.34.1
>
Alexander Stein Feb. 3, 2023, 11:10 a.m. UTC | #2
Hi Dave,

Am Freitag, 3. Februar 2023, 11:55:55 CET schrieb Dave Stevenson:
> Hi Alexander
> 
> On Fri, 3 Feb 2023 at 10:24, Alexander Stein
> 
> <alexander.stein@ew.tq-group.com> wrote:
> > Both sensors are quite similar. Their specs only differ regarding LVDS
> > and parallel output but are identical regarding MIPI-CSI-2 interface.
> > But they use a different init setting of hard-coded values, taken from
> > the datasheet.
> > 
> > Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
> > ---
> > 
> >  drivers/media/i2c/imx290.c | 88 +++++++++++++++++++++++++++++++++-----
> >  1 file changed, 77 insertions(+), 11 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index e642e1df520d..337252b2ec15 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -164,6 +164,36 @@
> > 
> >  #define CLK_74_25      1
> >  #define NUM_CLK                2
> > 
> > +enum imx290_model {
> > +       IMX290,
> > +       IMX290_MONO,
> > +       IMX327,
> > +};
> > +
> > +struct imx290_device_data {
> > +       enum imx290_model model;
> > +       const char *name;
> > +       u8 mono;
> > +};
> > +
> > +static const struct imx290_device_data imx290_models[] = {
> > +       [IMX290] = {
> > +               .model = IMX290,
> > +               .name = "imx290",
> > +               .mono = 0,
> > +       },
> > +       [IMX290_MONO] = {
> > +               .model = IMX290_MONO,
> > +               .name = "imx290-mono",
> > +               .mono = 1,
> > +       },
> > +       [IMX327] = {
> > +               .model = IMX327,
> > +               .name = "imx327",
> > +               .mono = 0,
> > +       },
> > +};
> > +
> > 
> >  struct imx290_regval {
> >  
> >         u32 reg;
> >         u32 val;
> > 
> > @@ -210,9 +240,9 @@ struct imx290 {
> > 
> >         struct device *dev;
> >         struct clk *xclk;
> >         struct regmap *regmap;
> > 
> > +       const struct imx290_device_data *devdata;
> > 
> >         u32 xclk_freq;
> >         u8 nlanes;
> > 
> > -       u8 mono;
> > 
> >         struct v4l2_subdev sd;
> >         struct media_pad pad;
> > 
> > @@ -240,7 +270,7 @@ static inline struct imx290 *to_imx290(struct
> > v4l2_subdev *_sd)> 
> >   * Modes and formats
> >   */
> > 
> > -static const struct imx290_regval imx290_global_init_settings[] = {
> > +static const struct imx290_regval imx290_global_init_settings_290[] = {
> > 
> >         { IMX290_WINWV_OB, 12 },
> >         { IMX290_WINPH, 0 },
> >         { IMX290_WINPV, 0 },
> > 
> > @@ -292,6 +322,23 @@ static const struct imx290_regval
> > imx290_global_init_settings[] = {> 
> >         { IMX290_REG_8BIT(0x33b3), 0x04 },
> >  
> >  };
> > 
> > +static const struct imx290_regval imx290_global_init_settings_327[] = {
> > +       { IMX290_WINWV_OB, 12 },
> > +       { IMX290_WINPH, 0 },
> > +       { IMX290_WINPV, 0 },
> > +       { IMX290_WINWH, 1948 },
> > +       { IMX290_WINWV, 1097 },
> > +       { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC |
> > +                          IMX290_XSOUTSEL_XHSOUTSEL_HSYNC },
> > +       { IMX290_REG_8BIT(0x3011), 0x0A },
> 
> What datasheet are you working from? Mine (2019/03/25) has a
> correction listed at v0.3 of:
> Register 3011h setting 0Ah -> 02h

I've got a v0.2 (2017/05/25). As you have v0.3 I am not really surprised you 
have different/additional values.

> > +       { IMX290_REG_8BIT(0x3012), 0x64 },
> > +       { IMX290_REG_8BIT(0x3013), 0x00 },
> > +       { IMX290_REG_8BIT(0x309e), 0x4A },
> > +       { IMX290_REG_8BIT(0x309f), 0x4A },
> 
> 309e/f undocumented in my datasheet beyond "default value 5Ah, set to
> "4Ah"". Not documented in imx290 or imx462 datasheets either. I'll read it
> back from IMX290 and IMX462 when I get to the office and see if 0x4a is the
> default anyway, in which case it can be generic.

Exactly, they are not documented at all, just marked red indicating it's 
different from reset default.

> > +       { IMX290_REG_8BIT(0x3128), 0x04 },
> 
> Correction v0.3 - register address 3128h deleted.
> 
> > +       { IMX290_REG_8BIT(0x313b), 0x41 },
> 
> Correction v0.3 - Register address 313Bh setting 41h -> 61h.
> 
> 
> I'll check the defaults on imx290 and imx462, because there is no harm
> in adding those register writes if they happen to be the defaults.
> There is also a fair amount of duplication between
> imx290_global_init_settings_290 and imx290_global_init_settings_327 -
> it'd be nice to reduce it down to the minimum set of diffs.

Thanks, I'll update to the values you provided and split the common settings.

> > +};
> > +
> > 
> >  static const struct imx290_regval imx290_37_125mhz_clock[] = {
> >  
> >         { IMX290_EXTCK_FREQ, 0x2520 },
> >         { IMX290_INCKSEL7, 0x49 },
> > 
> > @@ -558,7 +605,7 @@ imx290_format_info(const struct imx290 *imx290, u32
> > code)> 
> >         for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) {
> >         
> >                 const struct imx290_format_info *info =
> >                 &imx290_formats[i];
> > 
> > -               if (info->code[imx290->mono] == code)
> > +               if (info->code[imx290->devdata->mono] == code)
> > 
> >                         return info;
> >         
> >         }
> > 
> > @@ -957,11 +1004,27 @@ static int imx290_start_streaming(struct imx290
> > *imx290,> 
> >                                   struct v4l2_subdev_state *state)
> >  
> >  {
> >  
> >         const struct v4l2_mbus_framefmt *format;
> > 
> > +       const struct imx290_regval *regs;
> > +       unsigned int reg_num;
> > 
> >         int ret;
> > 
> > +       switch (imx290->devdata->model) {
> > +       case IMX290:
> > +       case IMX290_MONO:
> > +               regs = imx290_global_init_settings_290;
> > +               reg_num = ARRAY_SIZE(imx290_global_init_settings_290);
> > +               break;
> > +       case IMX327:
> > +               regs = imx290_global_init_settings_327;
> > +               reg_num = ARRAY_SIZE(imx290_global_init_settings_327);
> > +               break;
> > +       default:
> > +               dev_err(imx290->dev, "Invalid model: %u\n",
> > imx290->devdata->model); +               return -EINVAL;
> > +       }
> > +
> > 
> >         /* Set init register settings */
> > 
> > -       ret = imx290_set_register_array(imx290,
> > imx290_global_init_settings, -                                      
> > ARRAY_SIZE(imx290_global_init_settings)); +       ret =
> > imx290_set_register_array(imx290, regs, reg_num);
> > 
> >         if (ret < 0) {
> >         
> >                 dev_err(imx290->dev, "Could not set init registers\n");
> >                 return ret;
> > 
> > @@ -1072,7 +1135,7 @@ static int imx290_enum_mbus_code(struct v4l2_subdev
> > *sd,> 
> >         if (code->index >= ARRAY_SIZE(imx290_formats))
> >         
> >                 return -EINVAL;
> > 
> > -       code->code = imx290_formats[code->index].code[imx290->mono];
> > +       code->code =
> > imx290_formats[code->index].code[imx290->devdata->mono];> 
> >         return 0;
> >  
> >  }
> > 
> > @@ -1114,7 +1177,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> > 
> >         fmt->format.height = mode->height;
> >         
> >         if (!imx290_format_info(imx290, fmt->format.code))
> > 
> > -               fmt->format.code = imx290_formats[0].code[imx290->mono];
> > +               fmt->format.code =
> > imx290_formats[0].code[imx290->devdata->mono];> 
> >         fmt->format.field = V4L2_FIELD_NONE;
> >         fmt->format.colorspace = V4L2_COLORSPACE_RAW;
> > 
> > @@ -1422,8 +1485,9 @@ static s64 imx290_check_link_freqs(const struct
> > imx290 *imx290,> 
> >  }
> >  
> >  static const struct of_device_id imx290_of_match[] = {
> > 
> > -       { .compatible = "sony,imx290" },
> > -       { .compatible = "sony,imx290-mono", .data = (void *)1 },
> > +       { .compatible = "sony,imx290", .data = &imx290_models[IMX290] },
> > +       { .compatible = "sony,imx290-mono", .data =
> > &imx290_models[IMX290_MONO] }, +       { .compatible = "sony,imx327", 
> > .data = &imx290_models[IMX327] },
> Based on Laurent's requests my parent to this set will be switching to
> imx290 (as legacy), imx290lqr and imx290llr as the compatible strings.
> imx327 ought to follow the same pattern.

Okay thanks. I'll update to imx327lqr as well. Put me on CC so I'll notice the 
update & conflict.

Best regards,
Alexander

>   Dave
> 
> >         { /* sentinel */ }
> >  
> >  };
> >  MODULE_DEVICE_TABLE(of, imx290_of_match);
> > 
> > @@ -1441,8 +1505,7 @@ static int imx290_parse_dt(struct imx290 *imx290)
> > 
> >         s64 fq;
> >         
> >         match = i2c_of_match_device(imx290_of_match, client);
> > 
> > -       if (match)
> > -               imx290->mono = match->data ? 1 : 0;
> > +       imx290->devdata = match->data;
> > 
> >         endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev),
> >         NULL);
> >         if (!endpoint) {
> > 
> > @@ -1561,6 +1624,9 @@ static int imx290_probe(struct i2c_client *client)
> > 
> >         if (ret)
> >         
> >                 goto err_pm;
> > 
> > +       v4l2_i2c_subdev_set_name(&imx290->sd, client,
> > +                                imx290->devdata->name, NULL);
> > +
> > 
> >         /*
> >         
> >          * Finally, register the V4L2 subdev. This must be done after
> >          * initializing everything as the subdev can be used immediately
> >          after
> > 
> > --
> > 2.34.1
Dave Stevenson Feb. 3, 2023, 11:38 a.m. UTC | #3
On Fri, 3 Feb 2023 at 11:10, Alexander Stein
<alexander.stein@ew.tq-group.com> wrote:
>
> Hi Dave,
>
> Am Freitag, 3. Februar 2023, 11:55:55 CET schrieb Dave Stevenson:
> > Hi Alexander
> >
> > On Fri, 3 Feb 2023 at 10:24, Alexander Stein
> >
> > <alexander.stein@ew.tq-group.com> wrote:
> > > Both sensors are quite similar. Their specs only differ regarding LVDS
> > > and parallel output but are identical regarding MIPI-CSI-2 interface.
> > > But they use a different init setting of hard-coded values, taken from
> > > the datasheet.
> > >
> > > Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
> > > ---
> > >
> > >  drivers/media/i2c/imx290.c | 88 +++++++++++++++++++++++++++++++++-----
> > >  1 file changed, 77 insertions(+), 11 deletions(-)
> > >
> > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > index e642e1df520d..337252b2ec15 100644
> > > --- a/drivers/media/i2c/imx290.c
> > > +++ b/drivers/media/i2c/imx290.c
> > > @@ -164,6 +164,36 @@
> > >
> > >  #define CLK_74_25      1
> > >  #define NUM_CLK                2
> > >
> > > +enum imx290_model {
> > > +       IMX290,
> > > +       IMX290_MONO,
> > > +       IMX327,
> > > +};
> > > +
> > > +struct imx290_device_data {
> > > +       enum imx290_model model;
> > > +       const char *name;
> > > +       u8 mono;
> > > +};
> > > +
> > > +static const struct imx290_device_data imx290_models[] = {
> > > +       [IMX290] = {
> > > +               .model = IMX290,
> > > +               .name = "imx290",
> > > +               .mono = 0,
> > > +       },
> > > +       [IMX290_MONO] = {
> > > +               .model = IMX290_MONO,
> > > +               .name = "imx290-mono",
> > > +               .mono = 1,
> > > +       },
> > > +       [IMX327] = {
> > > +               .model = IMX327,
> > > +               .name = "imx327",
> > > +               .mono = 0,
> > > +       },
> > > +};
> > > +
> > >
> > >  struct imx290_regval {
> > >
> > >         u32 reg;
> > >         u32 val;
> > >
> > > @@ -210,9 +240,9 @@ struct imx290 {
> > >
> > >         struct device *dev;
> > >         struct clk *xclk;
> > >         struct regmap *regmap;
> > >
> > > +       const struct imx290_device_data *devdata;
> > >
> > >         u32 xclk_freq;
> > >         u8 nlanes;
> > >
> > > -       u8 mono;
> > >
> > >         struct v4l2_subdev sd;
> > >         struct media_pad pad;
> > >
> > > @@ -240,7 +270,7 @@ static inline struct imx290 *to_imx290(struct
> > > v4l2_subdev *_sd)>
> > >   * Modes and formats
> > >   */
> > >
> > > -static const struct imx290_regval imx290_global_init_settings[] = {
> > > +static const struct imx290_regval imx290_global_init_settings_290[] = {
> > >
> > >         { IMX290_WINWV_OB, 12 },
> > >         { IMX290_WINPH, 0 },
> > >         { IMX290_WINPV, 0 },
> > >
> > > @@ -292,6 +322,23 @@ static const struct imx290_regval
> > > imx290_global_init_settings[] = {>
> > >         { IMX290_REG_8BIT(0x33b3), 0x04 },
> > >
> > >  };
> > >
> > > +static const struct imx290_regval imx290_global_init_settings_327[] = {
> > > +       { IMX290_WINWV_OB, 12 },
> > > +       { IMX290_WINPH, 0 },
> > > +       { IMX290_WINPV, 0 },
> > > +       { IMX290_WINWH, 1948 },
> > > +       { IMX290_WINWV, 1097 },
> > > +       { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC |
> > > +                          IMX290_XSOUTSEL_XHSOUTSEL_HSYNC },
> > > +       { IMX290_REG_8BIT(0x3011), 0x0A },
> >
> > What datasheet are you working from? Mine (2019/03/25) has a
> > correction listed at v0.3 of:
> > Register 3011h setting 0Ah -> 02h
>
> I've got a v0.2 (2017/05/25). As you have v0.3 I am not really surprised you
> have different/additional values.

Revision history appears to be
0.1 2017/02/23
0.2 2017/05/25
0.3 2017/09/04
E17Z06 2017/12/18
E17Z06A81 2018/02/01
E1706B93 2019/03/25
so you are a way behind.

>
> > > +       { IMX290_REG_8BIT(0x3012), 0x64 },
> > > +       { IMX290_REG_8BIT(0x3013), 0x00 },
> > > +       { IMX290_REG_8BIT(0x309e), 0x4A },
> > > +       { IMX290_REG_8BIT(0x309f), 0x4A },
> >
> > 309e/f undocumented in my datasheet beyond "default value 5Ah, set to
> > "4Ah"". Not documented in imx290 or imx462 datasheets either. I'll read it
> > back from IMX290 and IMX462 when I get to the office and see if 0x4a is the
> > default anyway, in which case it can be generic.

I can't read back from my IMX290LLR as Vision Components use a
secondary MCU to stop you reading registers.

IMX462LQR default 0x5a 0x5a :-(

> Exactly, they are not documented at all, just marked red indicating it's
> different from reset default.
>
> > > +       { IMX290_REG_8BIT(0x3128), 0x04 },
> >
> > Correction v0.3 - register address 3128h deleted.
> >
> > > +       { IMX290_REG_8BIT(0x313b), 0x41 },
> >
> > Correction v0.3 - Register address 313Bh setting 41h -> 61h.

IMX462LQR default 0x51

> >
> >
> > I'll check the defaults on imx290 and imx462, because there is no harm
> > in adding those register writes if they happen to be the defaults.
> > There is also a fair amount of duplication between
> > imx290_global_init_settings_290 and imx290_global_init_settings_327 -
> > it'd be nice to reduce it down to the minimum set of diffs.
>
> Thanks, I'll update to the values you provided and split the common settings.

So annoyingly that does mean we need a sensor specific table, but I
think it is only 3 registers (0x309e, 0x309ef, and 0x313b). None of
those need to be set for IMX290 or 462.

  Dave

> > > +};
> > > +
> > >
> > >  static const struct imx290_regval imx290_37_125mhz_clock[] = {
> > >
> > >         { IMX290_EXTCK_FREQ, 0x2520 },
> > >         { IMX290_INCKSEL7, 0x49 },
> > >
> > > @@ -558,7 +605,7 @@ imx290_format_info(const struct imx290 *imx290, u32
> > > code)>
> > >         for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) {
> > >
> > >                 const struct imx290_format_info *info =
> > >                 &imx290_formats[i];
> > >
> > > -               if (info->code[imx290->mono] == code)
> > > +               if (info->code[imx290->devdata->mono] == code)
> > >
> > >                         return info;
> > >
> > >         }
> > >
> > > @@ -957,11 +1004,27 @@ static int imx290_start_streaming(struct imx290
> > > *imx290,>
> > >                                   struct v4l2_subdev_state *state)
> > >
> > >  {
> > >
> > >         const struct v4l2_mbus_framefmt *format;
> > >
> > > +       const struct imx290_regval *regs;
> > > +       unsigned int reg_num;
> > >
> > >         int ret;
> > >
> > > +       switch (imx290->devdata->model) {
> > > +       case IMX290:
> > > +       case IMX290_MONO:
> > > +               regs = imx290_global_init_settings_290;
> > > +               reg_num = ARRAY_SIZE(imx290_global_init_settings_290);
> > > +               break;
> > > +       case IMX327:
> > > +               regs = imx290_global_init_settings_327;
> > > +               reg_num = ARRAY_SIZE(imx290_global_init_settings_327);
> > > +               break;
> > > +       default:
> > > +               dev_err(imx290->dev, "Invalid model: %u\n",
> > > imx290->devdata->model); +               return -EINVAL;
> > > +       }
> > > +
> > >
> > >         /* Set init register settings */
> > >
> > > -       ret = imx290_set_register_array(imx290,
> > > imx290_global_init_settings, -
> > > ARRAY_SIZE(imx290_global_init_settings)); +       ret =
> > > imx290_set_register_array(imx290, regs, reg_num);
> > >
> > >         if (ret < 0) {
> > >
> > >                 dev_err(imx290->dev, "Could not set init registers\n");
> > >                 return ret;
> > >
> > > @@ -1072,7 +1135,7 @@ static int imx290_enum_mbus_code(struct v4l2_subdev
> > > *sd,>
> > >         if (code->index >= ARRAY_SIZE(imx290_formats))
> > >
> > >                 return -EINVAL;
> > >
> > > -       code->code = imx290_formats[code->index].code[imx290->mono];
> > > +       code->code =
> > > imx290_formats[code->index].code[imx290->devdata->mono];>
> > >         return 0;
> > >
> > >  }
> > >
> > > @@ -1114,7 +1177,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> > >
> > >         fmt->format.height = mode->height;
> > >
> > >         if (!imx290_format_info(imx290, fmt->format.code))
> > >
> > > -               fmt->format.code = imx290_formats[0].code[imx290->mono];
> > > +               fmt->format.code =
> > > imx290_formats[0].code[imx290->devdata->mono];>
> > >         fmt->format.field = V4L2_FIELD_NONE;
> > >         fmt->format.colorspace = V4L2_COLORSPACE_RAW;
> > >
> > > @@ -1422,8 +1485,9 @@ static s64 imx290_check_link_freqs(const struct
> > > imx290 *imx290,>
> > >  }
> > >
> > >  static const struct of_device_id imx290_of_match[] = {
> > >
> > > -       { .compatible = "sony,imx290" },
> > > -       { .compatible = "sony,imx290-mono", .data = (void *)1 },
> > > +       { .compatible = "sony,imx290", .data = &imx290_models[IMX290] },
> > > +       { .compatible = "sony,imx290-mono", .data =
> > > &imx290_models[IMX290_MONO] }, +       { .compatible = "sony,imx327",
> > > .data = &imx290_models[IMX327] },
> > Based on Laurent's requests my parent to this set will be switching to
> > imx290 (as legacy), imx290lqr and imx290llr as the compatible strings.
> > imx327 ought to follow the same pattern.
>
> Okay thanks. I'll update to imx327lqr as well. Put me on CC so I'll notice the
> update & conflict.
>
> Best regards,
> Alexander
>
> >   Dave
> >
> > >         { /* sentinel */ }
> > >
> > >  };
> > >  MODULE_DEVICE_TABLE(of, imx290_of_match);
> > >
> > > @@ -1441,8 +1505,7 @@ static int imx290_parse_dt(struct imx290 *imx290)
> > >
> > >         s64 fq;
> > >
> > >         match = i2c_of_match_device(imx290_of_match, client);
> > >
> > > -       if (match)
> > > -               imx290->mono = match->data ? 1 : 0;
> > > +       imx290->devdata = match->data;
> > >
> > >         endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev),
> > >         NULL);
> > >         if (!endpoint) {
> > >
> > > @@ -1561,6 +1624,9 @@ static int imx290_probe(struct i2c_client *client)
> > >
> > >         if (ret)
> > >
> > >                 goto err_pm;
> > >
> > > +       v4l2_i2c_subdev_set_name(&imx290->sd, client,
> > > +                                imx290->devdata->name, NULL);
> > > +
> > >
> > >         /*
> > >
> > >          * Finally, register the V4L2 subdev. This must be done after
> > >          * initializing everything as the subdev can be used immediately
> > >          after
> > >
> > > --
> > > 2.34.1
>
>
>
>
diff mbox series

Patch

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index e642e1df520d..337252b2ec15 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -164,6 +164,36 @@ 
 #define CLK_74_25	1
 #define NUM_CLK		2
 
+enum imx290_model {
+	IMX290,
+	IMX290_MONO,
+	IMX327,
+};
+
+struct imx290_device_data {
+	enum imx290_model model;
+	const char *name;
+	u8 mono;
+};
+
+static const struct imx290_device_data imx290_models[] = {
+	[IMX290] = {
+		.model = IMX290,
+		.name = "imx290",
+		.mono = 0,
+	},
+	[IMX290_MONO] = {
+		.model = IMX290_MONO,
+		.name = "imx290-mono",
+		.mono = 1,
+	},
+	[IMX327] = {
+		.model = IMX327,
+		.name = "imx327",
+		.mono = 0,
+	},
+};
+
 struct imx290_regval {
 	u32 reg;
 	u32 val;
@@ -210,9 +240,9 @@  struct imx290 {
 	struct device *dev;
 	struct clk *xclk;
 	struct regmap *regmap;
+	const struct imx290_device_data *devdata;
 	u32 xclk_freq;
 	u8 nlanes;
-	u8 mono;
 
 	struct v4l2_subdev sd;
 	struct media_pad pad;
@@ -240,7 +270,7 @@  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
  * Modes and formats
  */
 
-static const struct imx290_regval imx290_global_init_settings[] = {
+static const struct imx290_regval imx290_global_init_settings_290[] = {
 	{ IMX290_WINWV_OB, 12 },
 	{ IMX290_WINPH, 0 },
 	{ IMX290_WINPV, 0 },
@@ -292,6 +322,23 @@  static const struct imx290_regval imx290_global_init_settings[] = {
 	{ IMX290_REG_8BIT(0x33b3), 0x04 },
 };
 
+static const struct imx290_regval imx290_global_init_settings_327[] = {
+	{ IMX290_WINWV_OB, 12 },
+	{ IMX290_WINPH, 0 },
+	{ IMX290_WINPV, 0 },
+	{ IMX290_WINWH, 1948 },
+	{ IMX290_WINWV, 1097 },
+	{ IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC |
+			   IMX290_XSOUTSEL_XHSOUTSEL_HSYNC },
+	{ IMX290_REG_8BIT(0x3011), 0x0A },
+	{ IMX290_REG_8BIT(0x3012), 0x64 },
+	{ IMX290_REG_8BIT(0x3013), 0x00 },
+	{ IMX290_REG_8BIT(0x309e), 0x4A },
+	{ IMX290_REG_8BIT(0x309f), 0x4A },
+	{ IMX290_REG_8BIT(0x3128), 0x04 },
+	{ IMX290_REG_8BIT(0x313b), 0x41 },
+};
+
 static const struct imx290_regval imx290_37_125mhz_clock[] = {
 	{ IMX290_EXTCK_FREQ, 0x2520 },
 	{ IMX290_INCKSEL7, 0x49 },
@@ -558,7 +605,7 @@  imx290_format_info(const struct imx290 *imx290, u32 code)
 	for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) {
 		const struct imx290_format_info *info = &imx290_formats[i];
 
-		if (info->code[imx290->mono] == code)
+		if (info->code[imx290->devdata->mono] == code)
 			return info;
 	}
 
@@ -957,11 +1004,27 @@  static int imx290_start_streaming(struct imx290 *imx290,
 				  struct v4l2_subdev_state *state)
 {
 	const struct v4l2_mbus_framefmt *format;
+	const struct imx290_regval *regs;
+	unsigned int reg_num;
 	int ret;
 
+	switch (imx290->devdata->model) {
+	case IMX290:
+	case IMX290_MONO:
+		regs = imx290_global_init_settings_290;
+		reg_num = ARRAY_SIZE(imx290_global_init_settings_290);
+		break;
+	case IMX327:
+		regs = imx290_global_init_settings_327;
+		reg_num = ARRAY_SIZE(imx290_global_init_settings_327);
+		break;
+	default:
+		dev_err(imx290->dev, "Invalid model: %u\n", imx290->devdata->model);
+		return -EINVAL;
+	}
+
 	/* Set init register settings */
-	ret = imx290_set_register_array(imx290, imx290_global_init_settings,
-					ARRAY_SIZE(imx290_global_init_settings));
+	ret = imx290_set_register_array(imx290, regs, reg_num);
 	if (ret < 0) {
 		dev_err(imx290->dev, "Could not set init registers\n");
 		return ret;
@@ -1072,7 +1135,7 @@  static int imx290_enum_mbus_code(struct v4l2_subdev *sd,
 	if (code->index >= ARRAY_SIZE(imx290_formats))
 		return -EINVAL;
 
-	code->code = imx290_formats[code->index].code[imx290->mono];
+	code->code = imx290_formats[code->index].code[imx290->devdata->mono];
 
 	return 0;
 }
@@ -1114,7 +1177,7 @@  static int imx290_set_fmt(struct v4l2_subdev *sd,
 	fmt->format.height = mode->height;
 
 	if (!imx290_format_info(imx290, fmt->format.code))
-		fmt->format.code = imx290_formats[0].code[imx290->mono];
+		fmt->format.code = imx290_formats[0].code[imx290->devdata->mono];
 
 	fmt->format.field = V4L2_FIELD_NONE;
 	fmt->format.colorspace = V4L2_COLORSPACE_RAW;
@@ -1422,8 +1485,9 @@  static s64 imx290_check_link_freqs(const struct imx290 *imx290,
 }
 
 static const struct of_device_id imx290_of_match[] = {
-	{ .compatible = "sony,imx290" },
-	{ .compatible = "sony,imx290-mono", .data = (void *)1 },
+	{ .compatible = "sony,imx290", .data = &imx290_models[IMX290] },
+	{ .compatible = "sony,imx290-mono", .data = &imx290_models[IMX290_MONO] },
+	{ .compatible = "sony,imx327",  .data = &imx290_models[IMX327] },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, imx290_of_match);
@@ -1441,8 +1505,7 @@  static int imx290_parse_dt(struct imx290 *imx290)
 	s64 fq;
 
 	match = i2c_of_match_device(imx290_of_match, client);
-	if (match)
-		imx290->mono = match->data ? 1 : 0;
+	imx290->devdata = match->data;
 
 	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev), NULL);
 	if (!endpoint) {
@@ -1561,6 +1624,9 @@  static int imx290_probe(struct i2c_client *client)
 	if (ret)
 		goto err_pm;
 
+	v4l2_i2c_subdev_set_name(&imx290->sd, client,
+				 imx290->devdata->name, NULL);
+
 	/*
 	 * Finally, register the V4L2 subdev. This must be done after
 	 * initializing everything as the subdev can be used immediately after