Re: [PATCH 1/2] media: i2c: Add driver for Sony IMX728

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

 



Hi Spencer,

thanks for the patch.

Just having a glimpse and giving some feedback.

Am Mittwoch, 26. Juni 2024, 23:15:28 CEST schrieb Spencer Hill:
> Add a driver for the Sony IMX728 image sensor.
> 
> Signed-off-by: Spencer Hill <shill@xxxxxxxxxxxxxxxxx>
> ---
>  drivers/media/i2c/Kconfig  |   11 +
>  drivers/media/i2c/Makefile |    1 +
>  drivers/media/i2c/imx728.c | 1167 ++++++++++++
>  drivers/media/i2c/imx728.h | 3458 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 4637 insertions(+)
>  create mode 100644 drivers/media/i2c/imx728.c
>  create mode 100644 drivers/media/i2c/imx728.h
> 
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index c6d3ee472d81..46b6463c558a 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -233,6 +233,17 @@ config VIDEO_IMX415
>           To compile this driver as a module, choose M here: the
>           module will be called imx415.
> 
> +config VIDEO_IMX728
> +       tristate "Sony IMX728 sensor support"
> +       depends on OF_GPIO
> +       select V4L2_CCI_I2C
> +       help
> +         This is a Video4Linux2 sensor driver for the Sony
> +         IMX728 camera.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called imx728.
> +
>  config VIDEO_MAX9271_LIB
>         tristate
> 
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index dfbe6448b549..1188420ee1b4 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o
>  obj-$(CONFIG_VIDEO_IMX355) += imx355.o
>  obj-$(CONFIG_VIDEO_IMX412) += imx412.o
>  obj-$(CONFIG_VIDEO_IMX415) += imx415.o
> +obj-$(CONFIG_VIDEO_IMX728) += imx728.o
>  obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
>  obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o
>  obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
> diff --git a/drivers/media/i2c/imx728.c b/drivers/media/i2c/imx728.c
> new file mode 100644
> index 000000000000..b23359133a22
> --- /dev/null
> +++ b/drivers/media/i2c/imx728.c
> @@ -0,0 +1,1167 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Sony IMX728 CMOS Image Sensor Driver
> + *
> + * Copyright (c) 2024 Define Design Deploy Corp
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/clk.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/v4l2-mediabus.h>
> +#include <linux/videodev2.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +
> +#include "imx728.h"
> +
> +static inline struct imx728 *to_imx728(struct v4l2_subdev *sd)
> +{
> +       return container_of(sd, struct imx728, subdev);
> +}
> +
> +static int imx728_read(struct imx728 *imx728, u16 addr, u32 *val, size_t nbytes)

Use cci_read instead and remove this function.

> +{
> +       int ret;
> +       __le32 val_le = 0;
> +
> +       ret = regmap_bulk_read(imx728->regmap, addr, &val_le, nbytes);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "%s: failed to read reg 0x%04x: %d\n",
> +                       __func__, addr, ret);
> +               return ret;
> +       }
> +
> +       *val = le32_to_cpu(val_le);
> +
> +       return 0;
> +}
> +
> +static int imx728_write(struct imx728 *imx728, u16 addr, u32 val, size_t nbytes)

Use cci_write instead and remove this function.

> +{
> +       int ret;
> +       __le32 val_le = cpu_to_le32(val);
> +
> +       ret = regmap_bulk_write(imx728->regmap, addr, &val_le, nbytes);
> +       if (ret < 0)
> +               dev_err(imx728->dev, "%s: failed to write reg 0x%04x: %d\n",
> +                       __func__, addr, ret);
> +
> +       return ret;
> +}
> +
> +static int imx728_update_bits(struct imx728 *imx728, u16 addr, u32 val, u32 mask, size_t nbytes)

Use cci_update_bits instead and remove this function.

> +{
> +       int ret;
> +       u32 cfg;
> +
> +       ret = imx728_read(imx728, addr, &cfg, nbytes);
> +       if (ret < 0)
> +               return ret;
> +
> +       cfg = (val) ? (cfg | mask) : (cfg & (~mask));
> +
> +       return imx728_write(imx728, addr, cfg, nbytes);
> +}
> +
> +static int imx728_write_table(struct imx728 *imx728,
> +                             const struct reg_sequence *regs,
> +                             unsigned int nr_regs)

Use cci_multi_reg_write instead and remove this function.

> +{
> +       int ret;
> +
> +       ret = regmap_multi_reg_write(imx728->regmap, regs, nr_regs);
> +       if (ret < 0)
> +               dev_err(imx728->dev,
> +                       "%s: failed to write reg table (%d)!\n", __func__, ret);
> +       return ret;
> +}
> +
> +static int imx728_wait_for_state(struct imx728 *imx728, enum imx728_sensor_state state)
> +{
> +       int ret, i;
> +       u32 val;
> +
> +       for (i = 0; i < 50; i++) {
> +               ret = imx728_read(imx728, 0x2CAC, &val, 1);

Please add proper register defines using CCI_REG* macros.

> +               if (ret == 0 && val == state) {
> +                       dev_info(imx728->dev, "%s: Enter state %u\n", __func__, val);
> +                       return 0;
> +               }
> +               usleep_range(1000, 10000);
> +       }
> +
> +       return -EBUSY;
> +}
> +
> +static void imx728_init_formats(struct v4l2_subdev_state *state)
> +{
> +       struct v4l2_mbus_framefmt *format;
> +
> +       format = v4l2_subdev_state_get_format(state, 0, 0);
> +       format->code = imx728_mbus_formats[0];
> +       format->width = imx728_framesizes[0].width;
> +       format->height = imx728_framesizes[0].height;
> +       format->field = V4L2_FIELD_NONE;
> +       format->colorspace = V4L2_COLORSPACE_SMPTE170M;

Are you sure about this colorspace? I would have expected
V4L2_COLORSPACE_RAW, but I don't know any details on this hardware.

Also set ycbcr_enc, quantization and xfer_func.

> +}
> +
> +static int _imx728_set_routing(struct v4l2_subdev *sd,
> +                              struct v4l2_subdev_state *state)

Why this special variant with a underscore? Just move the code
into imx728_set_routing.

> +{
> +       struct v4l2_subdev_route routes[] = {
> +               {
> +                       .source_pad = 0,
> +                       .source_stream = 0,
> +                       .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
> +               },
> +               {
> +                       .source_pad = 0,
> +                       .source_stream = 1,
> +               }
> +       };
> +
> +       struct v4l2_subdev_krouting routing = {
> +               .num_routes = ARRAY_SIZE(routes),
> +               .routes = routes,
> +       };
> +
> +       int ret;
> +
> +       ret = v4l2_subdev_set_routing(sd, state, &routing);
> +       if (ret < 0)
> +               return ret;
> +
> +       imx728_init_formats(state);
> +
> +       return 0;
> +}
> +
> +static int imx728_enum_mbus_code(struct v4l2_subdev *sd,
> +                                struct v4l2_subdev_state *state,
> +                                struct v4l2_subdev_mbus_code_enum *code)
> +{
> +
> +       if (code->index >= ARRAY_SIZE(imx728_mbus_formats))
> +               return -EINVAL;
> +
> +       code->code = imx728_mbus_formats[code->index];
> +
> +       return 0;
> +}
> +
> +static int imx728_enum_frame_sizes(struct v4l2_subdev *sd,
> +                                  struct v4l2_subdev_state *state,
> +                                  struct v4l2_subdev_frame_size_enum *fse)
> +{
> +       unsigned int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(imx728_mbus_formats); ++i) {
> +               if (imx728_mbus_formats[i] == fse->code)
> +                       break;
> +       }
> +
> +       if (i == ARRAY_SIZE(imx728_mbus_formats))
> +               return -EINVAL;
> +
> +       if (fse->index >= ARRAY_SIZE(imx728_framesizes))
> +               return -EINVAL;
> +
> +       fse->min_width = imx728_framesizes[fse->index].width;
> +       fse->max_width = fse->min_width;
> +       fse->min_height = imx728_framesizes[fse->index].height;
> +       fse->max_height = fse->min_height;
> +
> +       return 0;
> +}
> +
> +static int imx728_set_fmt(struct v4l2_subdev *sd,
> +                         struct v4l2_subdev_state *state,
> +                         struct v4l2_subdev_format *fmt)
> +{
> +       struct imx728 *imx728 = to_imx728(sd);
> +       struct v4l2_mbus_framefmt *format;
> +       const struct v4l2_area *fsize;
> +       unsigned int i;
> +       u32 code;
> +       int ret = 0;
> +
> +       if (fmt->pad != 0)
> +               return -EINVAL;
> +
> +       if (fmt->stream != 0)
> +               return -EINVAL;
> +
> +       for (i = 0; i < ARRAY_SIZE(imx728_mbus_formats); ++i) {
> +               if (imx728_mbus_formats[i] == fmt->format.code) {
> +                       code = fmt->format.code;
> +                       break;
> +               }
> +       }
> +
> +       if (i == ARRAY_SIZE(imx728_mbus_formats))
> +               code = imx728_mbus_formats[0];
> +
> +       fsize = v4l2_find_nearest_size(imx728_framesizes,
> +                                      ARRAY_SIZE(imx728_framesizes), width,
> +                                      height, fmt->format.width,
> +                                      fmt->format.height);
> +
> +       mutex_lock(&imx728->lock);
> +
> +       format = v4l2_subdev_state_get_format(state, fmt->pad, fmt->stream);
> +
> +       if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && imx728->streaming) {
> +               ret = -EBUSY;
> +               goto done;
> +       }
> +
> +       format->code = code;
> +       format->width = fsize->width;
> +       format->height = fsize->height;

This should be done before calling v4l2_subdev_state_get_format, no?
Also ycbcr_enc, quantization and xfer_func are missing.

> +
> +       fmt->format = *format;
> +
> +done:
> +       mutex_unlock(&imx728->lock);
> +
> +       return ret;
> +}
> +
> +static int imx728_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
> +                                struct v4l2_mbus_frame_desc *fd)
> +{
> +       struct v4l2_subdev_state *state;
> +       struct v4l2_mbus_framefmt *fmt;
> +       u32 bpp;
> +       int ret = 0;
> +
> +       if (pad != 0)
> +               return -EINVAL;
> +
> +       state = v4l2_subdev_lock_and_get_active_state(sd);
> +
> +       fmt = v4l2_subdev_state_get_format(state, 0, 0);
> +       if (!fmt) {
> +               ret = -EPIPE;
> +               goto out;
> +       }
> +
> +       memset(fd, 0, sizeof(*fd));
> +
> +       fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
> +
> +       bpp = 10;
> +
> +       fd->entry[fd->num_entries].stream = 0;
> +
> +       fd->entry[fd->num_entries].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
> +       fd->entry[fd->num_entries].length = fmt->width * fmt->height * bpp / 8;
> +       fd->entry[fd->num_entries].pixelcode = fmt->code;
> +       fd->entry[fd->num_entries].bus.csi2.vc = 0;
> +       fd->entry[fd->num_entries].bus.csi2.dt = 0x2b;
> +
> +       fd->num_entries++;
> +
> +out:
> +
> +       v4l2_subdev_unlock_state(state);
> +
> +       return ret;
> +}
> +
> +static int imx728_set_routing(struct v4l2_subdev *sd,
> +                             struct v4l2_subdev_state *state,
> +                             enum v4l2_subdev_format_whence which,
> +                             struct v4l2_subdev_krouting *routing)
> +{
> +       int ret;
> +
> +       if (routing->num_routes == 0 || routing->num_routes > 1)
> +               return -EINVAL;
> +
> +       ret = _imx728_set_routing(sd, state);
> +
> +       return ret;
> +}
> +
> +static int imx728_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +       struct imx728 *imx728 = container_of(ctrl->handler,
> +                                       struct imx728, ctrl.handler);
> +       int ret = 0;
> +
> +       dev_dbg(imx728->dev, "%s: %s, value: %d\n",
> +                       __func__, ctrl->name, ctrl->val);
> +
> +       if (!pm_runtime_get_if_in_use(imx728->dev))
> +               return 0;
> +
> +       switch (ctrl->id) {
> +       case V4L2_CID_EXPOSURE:
> +               ret = imx728_write(imx728, 0x98DC, ctrl->val, 4);
> +               break;
> +       case V4L2_CID_ANALOGUE_GAIN:
> +               ret = imx728_update_bits(imx728, 0x98F8,
> +                               (ctrl->val * 10),
> +                               0x1FFFF, 4);
> +               break;
> +       case V4L2_CID_HFLIP:
> +               ret = imx728_update_bits(imx728, 0x9651,
> +                                        ctrl->val, 0x1, 1);
> +               ret |= imx728_update_bits(imx728, 0xB67C,
> +                                         ctrl->val, 0x1, 1);
> +               break;
> +       case V4L2_CID_VFLIP:
> +               ret = imx728_update_bits(imx728, 0x9651,
> +                                        ctrl->val << 1, 0x2, 1);
> +               ret = imx728_update_bits(imx728, 0xB67D,
> +                                        ctrl->val, 0x1, 1);
> +               break;
> +       case V4L2_CID_WIDE_DYNAMIC_RANGE:
> +       case V4L2_CID_TEST_PATTERN:
> +               // Both of these are configured during start stream.

Are they not configurable while streaming?

> +               ret = 0;
> +               break;
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       pm_runtime_put_noidle(imx728->dev);
> +       return ret;
> +}
> +
> +static int imx728_detect(struct imx728 *imx728)
> +{
> +       int ret;
> +       u32 minor, major;
> +
> +       ret = imx728_read(imx728, 0x6002, &major, 1);
> +       if (ret != 0) {
> +               dev_err(imx728->dev, "Could not read PARAM_MAJOR_VER!");
> +               return ret;
> +       }
> +       ret = imx728_read(imx728, 0x6000, &minor, 1);
> +       if (ret != 0) {
> +               dev_err(imx728->dev, "Could not read PARAM_MINOR_VER!");
> +               return ret;
> +       }
> +       dev_dbg(imx728->dev, "Got version: %d.%d", major, minor);
> +
> +       return 0;
> +}
> +
> +static int imx728_reset(struct imx728 *imx728)
> +{
> +
> +       if (!IS_ERR_OR_NULL(imx728->xclr_gpio)) {
> +               gpiod_set_value_cansleep(imx728->xclr_gpio, 1);
> +               usleep_range(1000, 10000);
> +               gpiod_set_value_cansleep(imx728->xclr_gpio, 0);
> +               msleep(100);
> +
> +               return 0;
> +       }
> +
> +       return -1;
> +}
> +
> +static int imx728_power_on(struct imx728 *imx728)
> +{
> +       int ret;
> +
> +       ret = clk_prepare_enable(imx728->clk);
> +       if (ret < 0)
> +               return ret;
> +
> +       imx728_reset(imx728);
> +       return 0;
> +}
> +
> +static int imx728_power_off(struct imx728 *imx728)
> +{
> +
> +       if (imx728->xclr_gpio) {
> +               gpiod_set_value_cansleep(imx728->xclr_gpio, 1);
> +
> +               usleep_range(1, 10);
> +       }
> +       clk_disable_unprepare(imx728->clk);
> +       return 0;
> +}
> +
> +static int imx728_get_frame_interval(struct v4l2_subdev *sd,
> +                                    struct v4l2_subdev_state *sd_state,
> +                                    struct v4l2_subdev_frame_interval *fi)
> +{
> +       struct imx728 *imx728 = to_imx728(sd);
> +
> +       fi->interval.numerator = 1;
> +       fi->interval.denominator = imx728->fps;
> +       return 0;
> +}
> +
> +static int imx728_set_frame_interval(struct v4l2_subdev *sd,
> +                                    struct v4l2_subdev_state *sd_state,
> +                                    struct v4l2_subdev_frame_interval *fi)
> +{
> +       struct imx728 *imx728 = to_imx728(sd);
> +       u32 req_fps;
> +
> +       mutex_lock(&imx728->lock);
> +
> +       if (fi->interval.numerator == 0 || fi->interval.denominator == 0) {
> +               fi->interval.denominator = IMX728_FRAMERATE_DEFAULT;
> +               fi->interval.numerator = 1;
> +       }
> +
> +       req_fps = clamp_val(DIV_ROUND_CLOSEST(fi->interval.denominator,
> +                                             fi->interval.numerator),
> +                           IMX728_FRAMERATE_MIN, IMX728_FRAMERATE_MAX);
> +
> +       fi->interval.numerator = 1;
> +       fi->interval.denominator = req_fps;
> +
> +       imx728->fps = req_fps;
> +
> +       mutex_unlock(&imx728->lock);
> +       dev_dbg(imx728->dev, "%s frame rate = %d\n", __func__, imx728->fps);
> +
> +       return 0;
> +}
> +
> +static int imx728_powerup_to_standby(struct imx728 *imx728)
> +{
> +       int ret;
> +
> +       dev_info(imx728->dev, "powerup -> standby...");
> +
> +       ret = imx728_reset(imx728);
> +       if (ret) {
> +               dev_err(imx728->dev, "Error resetting: %i", ret);
> +               return ret;
> +       }
> +
> +       ret = imx728_wait_for_state(imx728, IMX728_SENSOR_STATE_SLEEP);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Could not transition to Sleep state!");
> +               return ret;
> +       }
> +
> +       ret = imx728_write(imx728, 0x1B20, imx728->clk_rate / 1000000, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't write INCK frequency!");
> +               return ret;
> +       }
> +
> +       ret = imx728_write(imx728, 0x1B1C, 0x1, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't write INCK frequency!");

Error message doesn't seem to match. This is a fixed write, independent from any frequency.

> +               return ret;
> +       }
> +
> +       ret = imx728_write(imx728, 0x1B05, 0xFF, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't write to CK_SLEEP!");
> +               return ret;
> +       }
> +
> +       ret = imx728_wait_for_state(imx728, IMX728_SENSOR_STATE_STANDBY);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't transition from Sleep to Standby state!");
> +               return ret;
> +       }
> +
> +       ret = imx728_write(imx728, 0xFFFF, IMX728_REMAP_MODE_STANDBY, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't write regmap mode!");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int imx728_hdr_fixed_brightness(struct imx728 *imx728)
> +{
> +       int ret;
> +
> +       // Sony recommended values
> +       unsigned int exposure_sp1_sp2_us = 10000;
> +       unsigned int exposure_sp1vs_us = 56;
> +       unsigned int sp1h_gain = 240;
> +       unsigned int sp1l_gain = 75;
> +       unsigned int sp1ec_gain = 21;
> +       unsigned int sp2_gain = 33;
> +       unsigned int sp1vs_gain = 84;
> +
> +       ret = imx728_write(imx728, 0x98DC, exposure_sp1_sp2_us, 4);
> +       ret |= imx728_write(imx728, 0x98E4, exposure_sp1_sp2_us, 4);
> +       ret |= imx728_write(imx728, 0x98EC, exposure_sp1vs_us, 4);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Failed to write fixed exposure values.");
> +               return ret;
> +       }
> +
> +       ret = imx728_update_bits(imx728, 0x98F8,
> +                         sp1h_gain,
> +                         0x1FFFF,
> +                         4);
> +       ret |= imx728_update_bits(imx728, 0x98FC,
> +                         sp1l_gain,
> +                         0x1FFFF,
> +                         4);
> +       ret |= imx728_update_bits(imx728, 0x9900,
> +                         sp1ec_gain,
> +                         0x1FFFF,
> +                         4);
> +       ret |= imx728_update_bits(imx728, 0x9904,
> +                         sp2_gain,
> +                         0x1FFFF,
> +                         4);
> +       ret |= imx728_update_bits(imx728, 0x9908,
> +                         sp1vs_gain,
> +                         0x1FFFF,
> +                         4);
> +
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Failed to write fixed gain values.");
> +               return ret;
> +       }
> +
> +       dev_info(imx728->dev, "Wrote fixed brightness for HDR");
> +
> +       return 0;
> +}
> +
> +static int imx728_hdr_configure(
> +       struct imx728 *imx728,
> +       struct imx728_ctrl_point *points,
> +       int hdr_bits)
> +{
> +       u32 hdr_norm_x0;
> +       u32 hdr_norm_x1;
> +       u32 hdr_norm_y0;
> +       u32 hdr_norm_y1;
> +
> +       int ret;
> +       int i;
> +
> +       switch (hdr_bits) {
> +       case 20:
> +               hdr_norm_x0 = 0x2000;
> +               hdr_norm_x1 = 0x5000;
> +
> +               hdr_norm_y0 = 0x0;
> +               hdr_norm_y1 = 0xd000;
> +               break;
> +       case 24:
> +               hdr_norm_x0 = 0x3000;
> +               hdr_norm_x1 = 0x5000;
> +
> +               hdr_norm_y0 = 0x0;
> +               hdr_norm_y1 = 0xe000;
> +               break;
> +       default:
> +               dev_err(imx728->dev, "%i bit HDR not supported.", hdr_bits);
> +               break;
> +       }
> +
> +       ret = imx728_write(imx728, 0x9C60, hdr_norm_x0, 4);
> +       ret |= imx728_write(imx728, 0x9C6C, hdr_norm_x0, 4);
> +       ret |= imx728_write(imx728, 0x9C64, hdr_norm_x1, 4);
> +       ret |= imx728_write(imx728, 0x9C70, hdr_norm_x1, 4);
> +       ret |= imx728_write(imx728, 0x9C68, hdr_norm_y0, 2);
> +       ret |= imx728_write(imx728, 0x9C74, hdr_norm_y0, 2);
> +       ret |= imx728_write(imx728, 0x9C6A, hdr_norm_y1, 2);
> +       ret |= imx728_write(imx728, 0x9C76, hdr_norm_y1, 2);
> +
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Failed when setting HDR Normalization gains: %i", ret);
> +               return ret;
> +       }
> +
> +       for (i = 0; i < 16; i++) {
> +               ret = imx728_write(
> +                       imx728,
> +                       IMX728_REG_CTRL_POINT_X(i),
> +                       points->x, 4);
> +
> +               ret |= imx728_write(
> +                       imx728,
> +                       IMX728_REG_CTRL_POINT_Y(i),
> +                       points->y, 4);
> +
> +               if (ret < 0) {
> +                       dev_err(imx728->dev, "Failed to write control point %i", i);
> +                       return ret;
> +               }
> +
> +               if ((points+1)->x >= 0 && (points+1)->y >= 0)
> +                       points++;
> +       }
> +
> +       return imx728_hdr_fixed_brightness(imx728);
> +}
> +
> +static int imx728_configure(struct imx728 *imx728)
> +{
> +       int ret;
> +       bool hdr = imx728->ctrl.wdr->val;
> +       enum imx728_img_raw_mode img_out_mode;
> +       enum imx728_drive_mode mode_sel;
> +       enum imx728_aemode ae_mode;
> +
> +       if (hdr) {
> +               img_out_mode = IMX728_IMG_MODE_HDR;
> +               mode_sel = IMX728_MODE_3856x2176_40_4LANE_RAW10;
> +               ae_mode = IMX728_AEMODE_FULL_ME;
> +       } else {
> +               img_out_mode = IMX728_IMG_MODE_LINEAR;
> +               mode_sel = IMX728_MODE_3856x2176_40_4LANE_RAW10;
> +               ae_mode = IMX728_AEMODE_FULL_ME;
> +       }
> +
> +       ret = imx728_write(imx728, 0x98AC, ae_mode, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't set AE mode!");
> +               return ret;
> +       }
> +
> +       ret = imx728_write(imx728, 0xA248, IMX728_AWBMODE_FULL_MWB, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't set full manual white balance mode!");
> +               return ret;
> +       }
> +
> +       ret = imx728_update_bits(imx728, 0x1808, 0x1, 0x1, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't enable full manual white balance mode!");
> +               return ret;
> +       }
> +
> +       ret = imx728_write(imx728, 0x98E0, IMX728_FME_SHTVAL_UNIT_MICROSECONDS, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't write exposure time unit to microseconds!");
> +               return ret;
> +       }
> +
> +       ret = imx728_write(imx728, 0x98E8, IMX728_FME_SHTVAL_UNIT_MICROSECONDS, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't write exposure time unit to microseconds!");
> +               return ret;
> +       }
> +
> +       ret = imx728_write(imx728, 0x98F0, IMX728_FME_SHTVAL_UNIT_MICROSECONDS, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't write exposure time unit to microseconds!");
> +               return ret;
> +       }
> +
> +       if (hdr) {
> +               ret = imx728_hdr_configure(imx728, imx728_hdr_20bit, 20);
> +               if (ret < 0) {
> +                       dev_err(imx728->dev, "Couldn't configure sensor for HDR mode: %i", ret);
> +                       return ret;
> +               }
> +       }
> +
> +       dev_info(imx728->dev, "Disabling metadata...");
> +       ret = imx728_write(imx728, 0x1708, 0x00, 1);
> +       ret |= imx728_write(imx728, 0x1709, 0x00, 1);
> +       ret |= imx728_write(imx728, 0x170A, 0x00, 1);
> +       ret |= imx728_write(imx728, 0x1B40, 0x00, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Error disabling metadata: %i", ret);
> +               return ret;
> +       }
> +
> +       ret = imx728_update_bits(imx728, 0x9728,
> +                         mode_sel,
> +                         0x7FFF,
> +                         2);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't write mode select.");
> +               return ret;
> +       }
> +
> +       ret = imx728_update_bits(imx728, 0xEC7E,
> +                         img_out_mode,
> +                         0x7,
> +                         1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't select image out mode.");
> +               return ret;
> +       }
> +
> +       ret = imx728_write(imx728, 0xEC12, 0x28, 2);
> +       ret |= imx728_write(imx728, 0xEC14, 0x0, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Error disabling OB output.");
> +               return ret;
> +       }
> +
> +       ret = imx728_write(imx728, 0x1761, 0x0, 1);
> +       if (ret < 0)
> +               dev_err(imx728->dev, "Error disabling skew calibration from sensor to SER");
> +
> +       switch (imx728->ctrl.pg_mode->val) {
> +       case 0:
> +               break;
> +       case 1:
> +               // Horizontal Color Bars
> +               ret = imx728_write(imx728, 0x1A2A, 8, 2);
> +               ret |= imx728_write(imx728, 0x1A30, 0, 3);
> +               ret |= imx728_write(imx728, 0x1A38, 1, 3);
> +               ret |= imx728_write(imx728, 0xB58F, 0x0, 1);
> +               ret |= imx728_write(imx728, 0xB6C5, 0x0, 1);
> +               break;
> +       case 2:
> +               // Vertical Color Bars
> +               ret = imx728_write(imx728, 0x1A2C, 16, 2);
> +               ret |= imx728_write(imx728, 0x1A30, 0, 3);
> +               ret |= imx728_write(imx728, 0x1A38, 1, 3);
> +               ret |= imx728_write(imx728, 0xB58F, 0x1, 1);
> +               ret |= imx728_write(imx728, 0xB6C5, 0x1, 1);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       // Assume that everything except 'disabled' requires pattern gen enable
> +       if (imx728->ctrl.pg_mode->val != 0) {
> +               ret |= imx728_write(imx728, 0xB58E, 0x1, 1);
> +               ret |= imx728_write(imx728, 0xB6C4, 0x1, 1);
> +
> +               if (ret < 0) {
> +                       dev_err(imx728->dev, "Failed to enable PG mode.");
> +                       return ret;
> +               }
> +       }
> +
> +       ret = imx728_update_bits(imx728, 0x9714,
> +                         IMX728_RAW_SEL_SP1H,
> +                         0x7,
> +                         1);
> +       ret |= imx728_update_bits(imx728, 0xB684,
> +                          IMX728_RAW_SEL_SP1H,
> +                          0x7,
> +                          1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Failed to set subpixel register");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int imx728_start_stream(struct imx728 *imx728)
> +{
> +       int ret;
> +
> +       ret = imx728_powerup_to_standby(imx728);
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = imx728_write_table(imx728, imx728_3840x2160, ARRAY_SIZE(imx728_3840x2160));
> +       if (ret < 0)
> +               return ret;
> +
> +       msleep(100);
> +
> +       ret = imx728_configure(imx728);
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = __v4l2_ctrl_handler_setup(imx728->subdev.ctrl_handler);
> +       if (ret < 0) {
> +               dev_err(imx728->dev,
> +                       "%s: failed to apply v4l2 ctrls: %d\n", __func__, ret);
> +               return ret;
> +       }
> +
> +       ret = imx728_write(imx728, 0x1B04, 0x5C, 1);
> +       if (ret < 0)
> +               return ret;
> +       ret = imx728_write(imx728, 0x1B04, 0xA3, 1);
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = imx728_wait_for_state(imx728, IMX728_SENSOR_STATE_STREAMING);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Could not transition to Streaming!");
> +               return ret;
> +       }
> +
> +       msleep(20);
> +
> +       return 0;
> +}
> +
> +static int imx728_stop_stream(struct imx728 *imx728)
> +{
> +       int ret;
> +
> +       ret = imx728_write(imx728, 0x1B04, 0xFF, 1);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Error sending stop stream command: %i", ret);
> +               return ret;
> +       }
> +
> +       imx728_wait_for_state(imx728, IMX728_SENSOR_STATE_STANDBY);
> +       if (ret < 0) {
> +               dev_err(imx728->dev, "Couldn't transition out of Streaming mode!");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int imx728_set_stream(struct v4l2_subdev *sd, int enable)
> +{
> +       struct imx728 *imx728 = to_imx728(sd);
> +       int ret;
> +
> +       mutex_lock(&imx728->lock);
> +       if (imx728->streaming == enable) {
> +               mutex_unlock(&imx728->lock);
> +               return 0;
> +       }
> +
> +       if (enable) {
> +               ret = pm_runtime_get_sync(imx728->dev);
> +               if (ret < 0) {
> +                       pm_runtime_put_noidle(imx728->dev);
> +                       goto err_unlock;
> +               }
> +
> +               ret = imx728_start_stream(imx728);
> +               if (ret < 0)
> +                       goto err_runtime_put;
> +       } else {
> +               ret = imx728_stop_stream(imx728);
> +               if (ret < 0)
> +                       goto err_runtime_put;
> +               pm_runtime_mark_last_busy(imx728->dev);
> +               pm_runtime_put_autosuspend(imx728->dev);
> +       }
> +
> +       imx728->streaming = enable;
> +
> +       __v4l2_ctrl_grab(imx728->ctrl.wdr, enable);
> +       __v4l2_ctrl_grab(imx728->ctrl.h_flip, enable);
> +       __v4l2_ctrl_grab(imx728->ctrl.v_flip, enable);
> +       __v4l2_ctrl_grab(imx728->ctrl.pg_mode, enable);
> +
> +       mutex_unlock(&imx728->lock);
> +       return 0;
> +
> +err_runtime_put:
> +       pm_runtime_put(imx728->dev);
> +
> +err_unlock:
> +       mutex_unlock(&imx728->lock);
> +       dev_err(imx728->dev,
> +               "%s: failed to setup streaming %d\n", __func__, ret);
> +       return ret;
> +}
> +
> +static const struct v4l2_subdev_core_ops imx728_core_ops = {
> +       .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
> +       .unsubscribe_event = v4l2_event_subdev_unsubscribe,
> +};
> +
> +static const struct v4l2_subdev_video_ops imx728_subdev_video_ops = {
> +       .s_stream = imx728_set_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops imx728_subdev_pad_ops = {
> +       .enum_mbus_code = imx728_enum_mbus_code,
> +       .enum_frame_size = imx728_enum_frame_sizes,
> +       .get_fmt = v4l2_subdev_get_fmt,
> +       .set_fmt = imx728_set_fmt,
> +       .get_frame_interval = imx728_get_frame_interval,
> +       .set_frame_interval = imx728_set_frame_interval,
> +       .set_routing = imx728_set_routing,
> +       .get_frame_desc = imx728_get_frame_desc,
> +};
> +
> +static const struct v4l2_subdev_ops imx728_subdev_ops = {
> +       .core  = &imx728_core_ops,
> +       .video = &imx728_subdev_video_ops,
> +       .pad   = &imx728_subdev_pad_ops,
> +};
> +
> +static const struct v4l2_ctrl_ops imx728_ctrl_ops = {
> +       .s_ctrl = imx728_set_ctrl,
> +};
> +
> +static int imx728_probe(struct i2c_client *client)
> +{
> +       struct imx728 *imx728;
> +       struct v4l2_subdev *sd;
> +       struct v4l2_ctrl_handler *ctrl_hdr;
> +       int ret;
> +
> +       imx728 = devm_kzalloc(&client->dev, sizeof(*imx728), GFP_KERNEL);
> +       if (!imx728)
> +               return -ENOMEM;
> +
> +       imx728->dev = &client->dev;
> +
> +       imx728->regmap = devm_regmap_init_i2c(client, &imx728_regmap_config);

Please use devm_cci_regmap_init_i2c().

> +       if (IS_ERR(imx728->regmap))
> +               return PTR_ERR(imx728->regmap);
> +
> +       imx728->xclr_gpio = devm_gpiod_get_optional(imx728->dev,
> +                                            "xclr", GPIOD_OUT_LOW);
> +       if (IS_ERR(imx728->xclr_gpio))
> +               return PTR_ERR(imx728->xclr_gpio);
> +
> +       imx728->clk = devm_clk_get(imx728->dev, "inck");
> +       if (IS_ERR(imx728->clk))
> +               return PTR_ERR(imx728->clk);
> +
> +       imx728->clk_rate = clk_get_rate(imx728->clk);
> +       dev_info(imx728->dev, "inck rate: %lu Hz\n", imx728->clk_rate);

No need for this. clock frequency is available in debugfs if necessary.

> +
> +       if (imx728->clk_rate < 18000000 || imx728->clk_rate > 30000000)
> +               return -EINVAL;
> +
> +       ret = imx728_power_on(imx728);
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = imx728_detect(imx728);
> +       if (ret < 0)
> +               return ret;
> +
> +       sd = &imx728->subdev;
> +       v4l2_i2c_subdev_init(sd, client, &imx728_subdev_ops);
> +
> +       sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
> +                    V4L2_SUBDEV_FL_HAS_EVENTS |
> +                    V4L2_SUBDEV_FL_STREAMS;
> +
> +       imx728->pad.flags = MEDIA_PAD_FL_SOURCE;
> +       sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +       ret = media_entity_pads_init(&sd->entity, 1, &imx728->pad);
> +       if (ret < 0) {
> +               dev_err(imx728->dev,
> +                       "%s: media entity init failed %d\n", __func__, ret);
> +               return ret;
> +       }
> +
> +       ctrl_hdr = &imx728->ctrl.handler;
> +       ret = v4l2_ctrl_handler_init(ctrl_hdr, 8);
> +       if (ret < 0) {
> +               dev_err(imx728->dev,
> +                       "%s: ctrl handler init failed: %d\n", __func__, ret);
> +               goto err_media_cleanup;
> +       }
> +
> +       mutex_init(&imx728->lock);
> +       imx728->ctrl.handler.lock = &imx728->lock;
> +       imx728->fps = IMX728_FRAMERATE_DEFAULT;
> +
> +       imx728->ctrl.exposure = v4l2_ctrl_new_std(ctrl_hdr, &imx728_ctrl_ops,
> +                                                 V4L2_CID_EXPOSURE, 0,
> +                                                 33000, 1,
> +                                                 IMX728_EXPOSURE_DEFAULT);
> +
> +       imx728->ctrl.again = v4l2_ctrl_new_std(ctrl_hdr, &imx728_ctrl_ops,
> +                                              V4L2_CID_ANALOGUE_GAIN, 0,
> +                                              102, 1,
> +                                              24);
> +
> +       imx728->ctrl.wdr = v4l2_ctrl_new_std(ctrl_hdr, &imx728_ctrl_ops,
> +                                            V4L2_CID_WIDE_DYNAMIC_RANGE,
> +                                            0, 1, 1, 1);
> +
> +       imx728->ctrl.h_flip = v4l2_ctrl_new_std(ctrl_hdr, &imx728_ctrl_ops,
> +                                               V4L2_CID_HFLIP, 0, 1, 1, 0);
> +
> +       imx728->ctrl.v_flip = v4l2_ctrl_new_std(ctrl_hdr, &imx728_ctrl_ops,
> +                                               V4L2_CID_VFLIP, 0, 1, 1, 0);
> +
> +       imx728->ctrl.pg_mode = v4l2_ctrl_new_std_menu_items(ctrl_hdr,
> +                                       &imx728_ctrl_ops, V4L2_CID_TEST_PATTERN,
> +                                       ARRAY_SIZE(imx728_ctrl_pg_qmenu) - 1,
> +                                       0, 0, imx728_ctrl_pg_qmenu);
> +
> +       imx728->ctrl.pixel_rate = v4l2_ctrl_new_std(ctrl_hdr, &imx728_ctrl_ops,
> +                                            V4L2_CID_PIXEL_RATE,
> +                                            IMX728_PIXEL_RATE,
> +                                            IMX728_PIXEL_RATE, 1,
> +                                            IMX728_PIXEL_RATE);
> +
> +       imx728->ctrl.link_freq = v4l2_ctrl_new_int_menu(ctrl_hdr, &imx728_ctrl_ops,
> +                                                V4L2_CID_LINK_FREQ,
> +                                                ARRAY_SIZE(imx728_link_freq_menu) - 1, 0,
> +                                                imx728_link_freq_menu);
> +
> +       if (imx728->ctrl.link_freq)
> +               imx728->ctrl.link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +       imx728->subdev.ctrl_handler = ctrl_hdr;
> +       if (imx728->ctrl.handler.error) {
> +               ret = imx728->ctrl.handler.error;
> +               dev_err(imx728->dev,
> +                       "%s: failed to add the ctrls: %d\n", __func__, ret);
> +               goto err_ctrl_free;
> +       }
> +
> +       pm_runtime_set_active(imx728->dev);
> +       pm_runtime_enable(imx728->dev);
> +       pm_runtime_set_autosuspend_delay(imx728->dev, IMX728_PM_IDLE_TIMEOUT);
> +       pm_runtime_use_autosuspend(imx728->dev);
> +       pm_runtime_get_noresume(imx728->dev);
> +
> +       ret = v4l2_subdev_init_finalize(sd);
> +       if (ret < 0)
> +               goto err_pm_disable;
> +
> +       ret = v4l2_async_register_subdev(sd);
> +       if (ret < 0) {
> +               dev_err(imx728->dev,
> +                       "%s: v4l2 subdev register failed %d\n", __func__, ret);
> +               goto err_subdev_cleanup;
> +       }
> +
> +       dev_info(imx728->dev, "imx728 probed\n");
> +       pm_runtime_mark_last_busy(imx728->dev);
> +       pm_runtime_put_autosuspend(imx728->dev);
> +       return 0;
> +
> +err_subdev_cleanup:
> +       v4l2_subdev_cleanup(&imx728->subdev);
> +
> +err_pm_disable:
> +       pm_runtime_dont_use_autosuspend(imx728->dev);
> +       pm_runtime_put_noidle(imx728->dev);
> +       pm_runtime_disable(imx728->dev);
> +
> +err_ctrl_free:
> +       v4l2_ctrl_handler_free(ctrl_hdr);
> +       mutex_destroy(&imx728->lock);
> +
> +err_media_cleanup:
> +       media_entity_cleanup(&imx728->subdev.entity);
> +
> +       return ret;
> +}
> +
> +static int __maybe_unused imx728_runtime_resume(struct device *dev)
> +{
> +       struct i2c_client *client = to_i2c_client(dev);
> +       struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +       struct imx728 *imx728 = to_imx728(sd);
> +       int ret;
> +
> +       ret = imx728_power_on(imx728);
> +       if (ret < 0)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +static int __maybe_unused imx728_runtime_suspend(struct device *dev)
> +{
> +       struct i2c_client *client = to_i2c_client(dev);
> +       struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +       struct imx728 *imx728 = to_imx728(sd);
> +
> +       imx728_power_off(imx728);
> +
> +       return 0;
> +}
> +
> +static int __maybe_unused imx728_suspend(struct device *dev)
> +{
> +       struct i2c_client *client = to_i2c_client(dev);
> +       struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +       struct imx728 *imx728 = to_imx728(sd);
> +       int ret;
> +
> +       if (imx728->streaming)
> +               imx728_stop_stream(imx728);
> +
> +       ret = pm_runtime_force_suspend(dev);
> +       if (ret < 0)
> +               dev_warn(dev, "%s: failed to suspend: %i\n", __func__, ret);
> +
> +       return 0;
> +}
> +
> +static int __maybe_unused imx728_resume(struct device *dev)
> +{
> +       struct i2c_client *client = to_i2c_client(dev);
> +       struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +       struct imx728 *imx728 = to_imx728(sd);
> +       int ret;
> +
> +       ret = pm_runtime_force_resume(dev);
> +       if (ret < 0)
> +               dev_warn(dev, "%s: failed to resume: %i\n", __func__, ret);
> +
> +       if (imx728->streaming)
> +               ret = imx728_start_stream(imx728);
> +
> +       if (ret < 0) {
> +               imx728_stop_stream(imx728);
> +               imx728->streaming = 0;
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static void imx728_remove(struct i2c_client *client)
> +{
> +       struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +       struct imx728 *imx728 = to_imx728(sd);
> +
> +       v4l2_async_unregister_subdev(sd);
> +       v4l2_ctrl_handler_free(&imx728->ctrl.handler);
> +       v4l2_subdev_cleanup(&imx728->subdev);
> +       media_entity_cleanup(&sd->entity);
> +       mutex_destroy(&imx728->lock);
> +
> +       pm_runtime_disable(imx728->dev);
> +       pm_runtime_dont_use_autosuspend(imx728->dev);
> +       pm_runtime_set_suspended(imx728->dev);
> +}
> +
> +static const struct dev_pm_ops imx728_pm_ops = {
> +       SET_RUNTIME_PM_OPS(imx728_runtime_suspend,
> +                          imx728_runtime_resume, NULL)
> +       SET_SYSTEM_SLEEP_PM_OPS(imx728_suspend, imx728_resume)
> +};
> +
> +static const struct of_device_id imx728_dt_id[] = {
> +       { .compatible = "sony,imx728" },
> +       { /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, imx728_dt_id);
> +
> +static struct i2c_driver imx728_i2c_driver = {
> +       .driver = {
> +               .name = "imx728",
> +               .of_match_table = of_match_ptr(imx728_dt_id),
> +               .pm = &imx728_pm_ops,
> +       },
> +       .probe = imx728_probe,
> +       .remove = imx728_remove,
> +};
> +
> +module_i2c_driver(imx728_i2c_driver);
> +
> +MODULE_DESCRIPTION("Camera Sensor Driver for Sony IMX728");
> +MODULE_AUTHOR("Spencer Hill <shill@xxxxxxxxxxxxxxxxx>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/i2c/imx728.h b/drivers/media/i2c/imx728.h
> new file mode 100644
> index 000000000000..6f320214b780
> --- /dev/null
> +++ b/drivers/media/i2c/imx728.h

There is only a single user of this header, move this into the c file.

> @@ -0,0 +1,3458 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Sony IMX728 CMOS Image Sensor Driver
> + *
> + * Copyright (c) 2024 Define Design Deploy Corp
> + */
> +
> +#include <linux/types.h>
> +
> +#define IMX728_OUT_WIDTH               3840
> +#define IMX728_OUT_HEIGHT              2160
> +
> +#define IMX728_FRAMERATE_MAX           30
> +#define IMX728_FRAMERATE_DEFAULT       30
> +#define IMX728_FRAMERATE_MIN           10
> +
> +#define IMX728_PIXEL_RATE              225504000
> +#define IMX728_LINK_FREQ               800000000
> +
> +#define IMX728_EXPOSURE_DEFAULT                10000
> +
> +#define IMX728_PM_IDLE_TIMEOUT         1000
> +
> +
> +#define IMX728_REG_CTRL_POINT_X(i) (0xA198 + (i) * 8)
> +#define IMX728_REG_CTRL_POINT_Y(i) (IMX728_REG_CTRL_POINT_X(i) + 4)
> +
> +enum imx728_sensor_state {
> +       IMX728_SENSOR_STATE_SLEEP               = 0x02,
> +       IMX728_SENSOR_STATE_STANDBY             = 0x04,
> +       IMX728_SENSOR_STATE_STREAMING           = 0x10,
> +       IMX728_SENSOR_STATE_SAFE                = 0x20,
> +};
> +
> +
> +enum imx728_remap_mode_id {
> +       IMX728_REMAP_MODE_STANDBY = 0x00,
> +       IMX728_REMAP_MODE_STANDBY_PIXEL_SHADING_COMPENSATION = 0x01,
> +       IMX728_REMAP_MODE_STANDBY_SPOT_PIXEL_COMPENSATION = 0x02,
> +       IMX728_REMAP_MODE_STREAMING = 0x04,
> +       IMX728_REMAP_MODE_STREAMING_PIXEL_SHADING_COMPENSATION = 0x05,
> +       IMX728_REMAP_MODE_STREAMING_SPOT_PIXEL_COMPENSATION = 0x06,
> +       IMX728_REMAP_MODE_SLEEP = 0x20,
> +};
> +
> +enum imx728_drive_mode {
> +       IMX728_MODE_3856x2176_45_4LANE_RAW10 = 0x01,
> +       IMX728_MODE_3856x2176_45_4LANE_RAW12 = 0x02,
> +       IMX728_MODE_3856x2176_45_4LANE_RAW16 = 0x03,
> +       IMX728_MODE_3856x2176_45_4LANE_RAW20 = 0x04,
> +       IMX728_MODE_3856x2176_45_4LANE_RAW12_HDR = 0x05,
> +       IMX728_MODE_3856x2176_40_4LANE_RAW10 = 0x11,
> +       IMX728_MODE_3856x2176_40_4LANE_RAW12 = 0x12,
> +       IMX728_MODE_3856x2176_40_4LANE_RAW16 = 0x13,
> +       IMX728_MODE_3856x2176_40_4LANE_RAW20 = 0x14,
> +       IMX728_MODE_3856x2176_40_4LANE_RAW12_HDR = 0x16,
> +};
> +
> +enum imx728_awbmode {
> +       IMX728_AWBMODE_ATW = 0,
> +       IMX728_AWBMODE_ALL_PULL_IN = 1,
> +       IMX728_AWBMODE_USER_PRESET = 2,
> +       IMX728_AWBMODE_FULL_MWB = 3,
> +       IMX728_AWBMODE_HOLD = 4,
> +};
> +
> +enum imx728_img_raw_mode {
> +       IMX728_IMG_MODE_LINEAR = 0x0,
> +       IMX728_IMG_MODE_LI = 0x1,
> +       IMX728_IMG_MODE_HDR = 0x2,
> +       IMX728_IMG_MODE_LI_HDR = 0x3,
> +};
> +
> +enum imx728_aemode {
> +       IMX728_AEMODE_AE_AUTO  = 0,
> +       IMX728_AEMODE_AE_HOLD  = 1,
> +       IMX728_AEMODE_SCALE_ME = 2,
> +       IMX728_AEMODE_FULL_ME  = 3,
> +};
> +
> +enum imx728_fme_shtval_unit {
> +       IMX728_FME_SHTVAL_UNIT_LINES            = 1,
> +       IMX728_FME_SHTVAL_UNIT_MICROSECONDS     = 3,
> +       IMX728_FME_SHTVAL_UNIT_FRAMES           = 4,
> +};
> +
> +enum imx728_linear_raw_sel {
> +       IMX728_RAW_SEL_SP1H = 0x0,
> +       IMX728_RAW_SEL_SP1L = 0x1,
> +       IMX728_RAW_SEL_SP1EC = 0x2,
> +       IMX728_RAW_SEL_SP2 = 0x3,
> +       IMX728_RAW_SEL_SP1VS = 0x4
> +};
> +
> +enum imx728_binn_avg {
> +       IMX728_BINN_SIMPLE_AVG,
> +       IMX728_BINN_WEIGHTED_AVG,
> +};
> +
> +struct imx728_ctrl {
> +       struct v4l2_ctrl_handler handler;
> +       struct v4l2_ctrl *wdr;
> +       struct v4l2_ctrl *exposure;
> +       struct v4l2_ctrl *again;
> +       struct v4l2_ctrl *h_flip;
> +       struct v4l2_ctrl *v_flip;
> +       struct v4l2_ctrl *pg_mode;
> +       struct v4l2_ctrl *pixel_rate;
> +       struct v4l2_ctrl *link_freq;
> +};
> +
> +struct imx728_ctrl_point {

What does ctrl_point mean? What is x and y?

> +       int x, y;
> +};
> +
> +/*
> + * struct imx728 - imx728 device structure
> + * @dev: Device handle
> + * @clk: Pointer to imx728 clock
> + * @client: Pointer to I2C client
> + * @regmap: Pointer to regmap structure
> + * @xclr_gpio: Pointer to XCLR gpio
> + * @subdev: V4L2 subdevice structure
> + * @format: V4L2 media bus frame format structure
> + *             (width and height are in sync with the compose rect)
> + * @pad: Media pad structure
> + * @ctrl: imx728 control structure
> + * @clk_rate: Frequency of imx728 clock
> + * @lock: Mutex structure for V4L2 ctrl handler
> + * @streaming: Flag to store streaming on/off status
> + */
> +struct imx728 {
> +       struct device *dev;
> +
> +       struct clk *clk;
> +       struct i2c_client *client;
> +       struct regmap *regmap;
> +       struct gpio_desc *xclr_gpio;
> +
> +       struct v4l2_subdev subdev;
> +       struct v4l2_mbus_framefmt format;
> +       struct media_pad pad;
> +
> +       struct imx728_ctrl ctrl;
> +
> +       unsigned long clk_rate;
> +       u32 fps;
> +
> +       struct mutex lock;
> +       bool streaming;
> +};
> +
> +static const struct v4l2_area imx728_framesizes[] = {
> +       {
> +               .width = IMX728_OUT_WIDTH,
> +               .height = IMX728_OUT_HEIGHT,
> +       },

Are you sure this is the only supported resolution? I would prefer using
actual numbers here.

> +};
> +
> +static const u32 imx728_mbus_formats[] = {
> +       MEDIA_BUS_FMT_SRGGB10_1X10,
> +};
> +
> +static const s64 imx728_link_freq_menu[] = {
> +       IMX728_LINK_FREQ,
> +};
> +
> +static const struct regmap_config imx728_regmap_config = {
> +       .reg_bits = 16,
> +       .val_bits = 8,
> +};
> +
> +static const char *const imx728_ctrl_pg_qmenu[] = {
> +       "Disabled",
> +       "Horizontal Color Bars",
> +       "Vertical Color Bars",
> +};
> +
> +static struct imx728_ctrl_point imx728_hdr_20bit[] = {
> +       {0, 0},
> +       {1566 >> 4, 938},
> +       {105740 >> 4, 1863},
> +       {387380 >> 4, 2396},
> +       {3818601 >> 4, 3251},
> +       {16777215 >> 4, 4095},
> +       {-1, -1}
> +};
> +
> +static const struct reg_sequence imx728_3840x2160[] = {

Please use struct cci_reg_sequence.

Best regards,
Alexander

> +       {0xFFFF, 0x00, 1000},
> +       {0x1749, 0x01},
> +       {0x174B, 0x01},
> [snip]
> +};
> --
> 2.40.1
> 
> Please be aware that this email includes email addresses outside of the organization.
> 
> 


-- 
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
http://www.tq-group.com/







[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