]> git.baikalelectronics.ru Git - kernel.git/commitdiff
media: i2c: Add vblank control to ov7251 driver
authorDaniel Scally <djrscally@gmail.com>
Thu, 5 May 2022 23:04:02 +0000 (01:04 +0200)
committerMauro Carvalho Chehab <mchehab@kernel.org>
Tue, 17 May 2022 07:54:06 +0000 (09:54 +0200)
Add a vblank control to the ov7251 driver.

Signed-off-by: Daniel Scally <djrscally@gmail.com>
Acked-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
drivers/media/i2c/ov7251.c

index 20591d8227c9b6a4e254111200692ae04d4ab9e1..4867dc86cd2e4eb4a99c811d0b69c90d700a7bb7 100644 (file)
 #define OV7251_ACTIVE_HEIGHT           488
 
 #define OV7251_FIXED_PPL               928
+#define OV7251_TIMING_VTS_REG          0x380e
+#define OV7251_TIMING_MIN_VTS          1
+#define OV7251_TIMING_MAX_VTS          0xffff
+#define OV7251_INTEGRATION_MARGIN      20
 
 struct reg_value {
        u16 reg;
@@ -71,6 +75,7 @@ struct reg_value {
 struct ov7251_mode_info {
        u32 width;
        u32 height;
+       u32 vts;
        const struct reg_value *data;
        u32 data_size;
        u32 pixel_clock;
@@ -142,6 +147,7 @@ struct ov7251 {
        struct v4l2_ctrl *exposure;
        struct v4l2_ctrl *gain;
        struct v4l2_ctrl *hblank;
+       struct v4l2_ctrl *vblank;
 
        /* Cached register values */
        u8 aec_pk_manual;
@@ -637,6 +643,7 @@ static const struct ov7251_mode_info ov7251_mode_info_data[] = {
        {
                .width = 640,
                .height = 480,
+               .vts = 1724,
                .data = ov7251_setting_vga_30fps,
                .data_size = ARRAY_SIZE(ov7251_setting_vga_30fps),
                .exposure_max = 1704,
@@ -649,6 +656,7 @@ static const struct ov7251_mode_info ov7251_mode_info_data[] = {
        {
                .width = 640,
                .height = 480,
+               .vts = 860,
                .data = ov7251_setting_vga_60fps,
                .data_size = ARRAY_SIZE(ov7251_setting_vga_60fps),
                .exposure_max = 840,
@@ -661,6 +669,7 @@ static const struct ov7251_mode_info ov7251_mode_info_data[] = {
        {
                .width = 640,
                .height = 480,
+               .vts = 572,
                .data = ov7251_setting_vga_90fps,
                .data_size = ARRAY_SIZE(ov7251_setting_vga_90fps),
                .exposure_max = 552,
@@ -1001,12 +1010,36 @@ static const char * const ov7251_test_pattern_menu[] = {
        "Vertical Pattern Bars",
 };
 
+static int ov7251_vts_configure(struct ov7251 *ov7251, s32 vblank)
+{
+       u8 vts[2];
+
+       vts[0] = ((ov7251->current_mode->height + vblank) & 0xff00) >> 8;
+       vts[1] = ((ov7251->current_mode->height + vblank) & 0x00ff);
+
+       return ov7251_write_seq_regs(ov7251, OV7251_TIMING_VTS_REG, vts, 2);
+}
+
 static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl)
 {
        struct ov7251 *ov7251 = container_of(ctrl->handler,
                                             struct ov7251, ctrls);
        int ret;
 
+       /* If VBLANK is altered we need to update exposure to compensate */
+       if (ctrl->id == V4L2_CID_VBLANK) {
+               int exposure_max;
+
+               exposure_max = ov7251->current_mode->height + ctrl->val -
+                              OV7251_INTEGRATION_MARGIN;
+               __v4l2_ctrl_modify_range(ov7251->exposure,
+                                        ov7251->exposure->minimum,
+                                        exposure_max,
+                                        ov7251->exposure->step,
+                                        min(ov7251->exposure->val,
+                                            exposure_max));
+       }
+
        /* v4l2_ctrl_lock() locks our mutex */
 
        if (!pm_runtime_get_if_in_use(ov7251->dev))
@@ -1028,6 +1061,9 @@ static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl)
        case V4L2_CID_VFLIP:
                ret = ov7251_set_vflip(ov7251, ctrl->val);
                break;
+       case V4L2_CID_VBLANK:
+               ret = ov7251_vts_configure(ov7251, ctrl->val);
+               break;
        default:
                ret = -EINVAL;
                break;
@@ -1179,6 +1215,7 @@ static int ov7251_set_format(struct v4l2_subdev *sd,
 {
        struct ov7251 *ov7251 = to_ov7251(sd);
        struct v4l2_mbus_framefmt *__format;
+       int vblank_max, vblank_def;
        struct v4l2_rect *__crop;
        const struct ov7251_mode_info *new_mode;
        int ret = 0;
@@ -1212,6 +1249,14 @@ static int ov7251_set_format(struct v4l2_subdev *sd,
                if (ret < 0)
                        goto exit;
 
+               vblank_max = OV7251_TIMING_MAX_VTS - new_mode->height;
+               vblank_def = new_mode->vts - new_mode->height;
+               ret = __v4l2_ctrl_modify_range(ov7251->vblank,
+                                              OV7251_TIMING_MIN_VTS,
+                                              vblank_max, 1, vblank_def);
+               if (ret < 0)
+                       goto exit;
+
                ov7251->current_mode = new_mode;
        }
 
@@ -1490,6 +1535,7 @@ static int ov7251_detect_chip(struct ov7251 *ov7251)
 
 static int ov7251_init_ctrls(struct ov7251 *ov7251)
 {
+       int vblank_max, vblank_def;
        s64 pixel_rate;
        int hblank;
 
@@ -1533,6 +1579,13 @@ static int ov7251_init_ctrls(struct ov7251 *ov7251)
        if (ov7251->hblank)
                ov7251->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
+       vblank_max = OV7251_TIMING_MAX_VTS - ov7251->current_mode->height;
+       vblank_def = ov7251->current_mode->vts - ov7251->current_mode->height;
+       ov7251->vblank = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+                                          V4L2_CID_VBLANK,
+                                          OV7251_TIMING_MIN_VTS, vblank_max, 1,
+                                          vblank_def);
+
        ov7251->sd.ctrl_handler = &ov7251->ctrls;
 
        if (ov7251->ctrls.error) {