[resend,3/5] media: ov2680: Add vblank control
Commit Message
Add vblank control to allow changing the framerate /
higher exposure values.
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/media/i2c/ov2680.c | 47 ++++++++++++++++++++++++++++++++------
1 file changed, 40 insertions(+), 7 deletions(-)
Comments
Hi Hans,
Thanks for re-posting this.
On Mon, Apr 15, 2024 at 11:37:02AM +0200, Hans de Goede wrote:
> Add vblank control to allow changing the framerate /
> higher exposure values.
>
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
> drivers/media/i2c/ov2680.c | 47 ++++++++++++++++++++++++++++++++------
> 1 file changed, 40 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c
> index 6c3d7862b2aa..d44e1934b25a 100644
> --- a/drivers/media/i2c/ov2680.c
> +++ b/drivers/media/i2c/ov2680.c
> @@ -75,6 +75,8 @@
> #define OV2680_ACTIVE_START_TOP 8
> #define OV2680_MIN_CROP_WIDTH 2
> #define OV2680_MIN_CROP_HEIGHT 2
> +#define OV2680_MIN_VBLANK 4
> +#define OV2680_MAX_VBLANK 0xffff
>
> /* Fixed pre-div of 1/2 */
> #define OV2680_PLL_PREDIV0 2
> @@ -84,7 +86,7 @@
>
> /* 66MHz pixel clock: 66MHz / 1704 * 1294 = 30fps */
> #define OV2680_PIXELS_PER_LINE 1704
> -#define OV2680_LINES_PER_FRAME 1294
> +#define OV2680_LINES_PER_FRAME_30FPS 1294
>
> /* Max exposure time is VTS - 8 */
> #define OV2680_INTEGRATION_TIME_MARGIN 8
> @@ -127,6 +129,7 @@ struct ov2680_ctrls {
> struct v4l2_ctrl *test_pattern;
> struct v4l2_ctrl *link_freq;
> struct v4l2_ctrl *pixel_rate;
> + struct v4l2_ctrl *vblank;
> };
>
> struct ov2680_mode {
> @@ -394,8 +397,7 @@ static int ov2680_set_mode(struct ov2680_dev *sensor)
> sensor->mode.v_output_size, &ret);
> cci_write(sensor->regmap, OV2680_REG_TIMING_HTS,
> OV2680_PIXELS_PER_LINE, &ret);
> - cci_write(sensor->regmap, OV2680_REG_TIMING_VTS,
> - OV2680_LINES_PER_FRAME, &ret);
> + /* VTS gets set by the vblank ctrl */
> cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
> cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
> cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
> @@ -469,6 +471,15 @@ static int ov2680_exposure_set(struct ov2680_dev *sensor, u32 exp)
> NULL);
> }
>
> +static void ov2680_exposure_update_range(struct ov2680_dev *sensor)
> +{
> + int exp_max = sensor->mode.fmt.height + sensor->ctrls.vblank->val -
> + OV2680_INTEGRATION_TIME_MARGIN;
> +
> + __v4l2_ctrl_modify_range(sensor->ctrls.exposure, 0, exp_max,
> + 1, exp_max);
__v4l2_ctrl_modify_range() may fail.
> +}
> +
> static int ov2680_stream_enable(struct ov2680_dev *sensor)
> {
> int ret;
> @@ -635,7 +646,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
> struct v4l2_mbus_framefmt *try_fmt;
> const struct v4l2_rect *crop;
> unsigned int width, height;
> - int ret = 0;
> + int def, max, ret = 0;
>
> crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad,
> format->which);
> @@ -664,6 +675,15 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
> sensor->mode.fmt = format->format;
> ov2680_calc_mode(sensor);
>
> + /* vblank range is height dependent adjust and reset to default */
> + max = OV2680_MAX_VBLANK - height;
> + def = OV2680_LINES_PER_FRAME_30FPS - height;
> + __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV2680_MIN_VBLANK, max,
> + 1, def);
> + __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, def);
And so may __v4l2_ctrl_s_ctrl().
> + /* exposure range depends on vts which may have changed */
> + ov2680_exposure_update_range(sensor);
> +
> unlock:
> mutex_unlock(&sensor->lock);
>
> @@ -833,6 +853,10 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
> struct ov2680_dev *sensor = to_ov2680_dev(sd);
> int ret;
>
> + /* Update exposure range on vblank changes */
> + if (ctrl->id == V4L2_CID_VBLANK)
> + ov2680_exposure_update_range(sensor);
> +
> /* Only apply changes to the controls if the device is powered up */
> if (!pm_runtime_get_if_in_use(sensor->sd.dev)) {
> ov2680_set_bayer_order(sensor, &sensor->mode.fmt);
> @@ -855,6 +879,10 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
> case V4L2_CID_TEST_PATTERN:
> ret = ov2680_test_pattern_set(sensor, ctrl->val);
> break;
> + case V4L2_CID_VBLANK:
> + ret = cci_write(sensor->regmap, OV2680_REG_TIMING_VTS,
> + sensor->mode.fmt.height + ctrl->val, NULL);
> + break;
> default:
> ret = -EINVAL;
> break;
> @@ -913,8 +941,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
> const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops;
> struct ov2680_ctrls *ctrls = &sensor->ctrls;
> struct v4l2_ctrl_handler *hdl = &ctrls->handler;
> - int exp_max = OV2680_LINES_PER_FRAME - OV2680_INTEGRATION_TIME_MARGIN;
> - int ret = 0;
> + int def, max, ret = 0;
>
> v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops);
> sensor->sd.internal_ops = &ov2680_internal_ops;
> @@ -939,8 +966,9 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
> ARRAY_SIZE(test_pattern_menu) - 1,
> 0, 0, test_pattern_menu);
>
> + max = OV2680_LINES_PER_FRAME_30FPS - OV2680_INTEGRATION_TIME_MARGIN;
> ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
> - 0, exp_max, 1, exp_max);
> + 0, max, 1, max);
>
> ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
> 0, 1023, 1, 250);
> @@ -951,6 +979,11 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
> 0, sensor->pixel_rate,
> 1, sensor->pixel_rate);
>
> + max = OV2680_MAX_VBLANK - OV2680_DEFAULT_HEIGHT;
> + def = OV2680_LINES_PER_FRAME_30FPS - OV2680_DEFAULT_HEIGHT;
> + ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
> + OV2680_MIN_VBLANK, max, 1, def);
> +
> if (hdl->error) {
> ret = hdl->error;
> goto cleanup_entity;
@@ -75,6 +75,8 @@
#define OV2680_ACTIVE_START_TOP 8
#define OV2680_MIN_CROP_WIDTH 2
#define OV2680_MIN_CROP_HEIGHT 2
+#define OV2680_MIN_VBLANK 4
+#define OV2680_MAX_VBLANK 0xffff
/* Fixed pre-div of 1/2 */
#define OV2680_PLL_PREDIV0 2
@@ -84,7 +86,7 @@
/* 66MHz pixel clock: 66MHz / 1704 * 1294 = 30fps */
#define OV2680_PIXELS_PER_LINE 1704
-#define OV2680_LINES_PER_FRAME 1294
+#define OV2680_LINES_PER_FRAME_30FPS 1294
/* Max exposure time is VTS - 8 */
#define OV2680_INTEGRATION_TIME_MARGIN 8
@@ -127,6 +129,7 @@ struct ov2680_ctrls {
struct v4l2_ctrl *test_pattern;
struct v4l2_ctrl *link_freq;
struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *vblank;
};
struct ov2680_mode {
@@ -394,8 +397,7 @@ static int ov2680_set_mode(struct ov2680_dev *sensor)
sensor->mode.v_output_size, &ret);
cci_write(sensor->regmap, OV2680_REG_TIMING_HTS,
OV2680_PIXELS_PER_LINE, &ret);
- cci_write(sensor->regmap, OV2680_REG_TIMING_VTS,
- OV2680_LINES_PER_FRAME, &ret);
+ /* VTS gets set by the vblank ctrl */
cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
@@ -469,6 +471,15 @@ static int ov2680_exposure_set(struct ov2680_dev *sensor, u32 exp)
NULL);
}
+static void ov2680_exposure_update_range(struct ov2680_dev *sensor)
+{
+ int exp_max = sensor->mode.fmt.height + sensor->ctrls.vblank->val -
+ OV2680_INTEGRATION_TIME_MARGIN;
+
+ __v4l2_ctrl_modify_range(sensor->ctrls.exposure, 0, exp_max,
+ 1, exp_max);
+}
+
static int ov2680_stream_enable(struct ov2680_dev *sensor)
{
int ret;
@@ -635,7 +646,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *try_fmt;
const struct v4l2_rect *crop;
unsigned int width, height;
- int ret = 0;
+ int def, max, ret = 0;
crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad,
format->which);
@@ -664,6 +675,15 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
sensor->mode.fmt = format->format;
ov2680_calc_mode(sensor);
+ /* vblank range is height dependent adjust and reset to default */
+ max = OV2680_MAX_VBLANK - height;
+ def = OV2680_LINES_PER_FRAME_30FPS - height;
+ __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV2680_MIN_VBLANK, max,
+ 1, def);
+ __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, def);
+ /* exposure range depends on vts which may have changed */
+ ov2680_exposure_update_range(sensor);
+
unlock:
mutex_unlock(&sensor->lock);
@@ -833,6 +853,10 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
struct ov2680_dev *sensor = to_ov2680_dev(sd);
int ret;
+ /* Update exposure range on vblank changes */
+ if (ctrl->id == V4L2_CID_VBLANK)
+ ov2680_exposure_update_range(sensor);
+
/* Only apply changes to the controls if the device is powered up */
if (!pm_runtime_get_if_in_use(sensor->sd.dev)) {
ov2680_set_bayer_order(sensor, &sensor->mode.fmt);
@@ -855,6 +879,10 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TEST_PATTERN:
ret = ov2680_test_pattern_set(sensor, ctrl->val);
break;
+ case V4L2_CID_VBLANK:
+ ret = cci_write(sensor->regmap, OV2680_REG_TIMING_VTS,
+ sensor->mode.fmt.height + ctrl->val, NULL);
+ break;
default:
ret = -EINVAL;
break;
@@ -913,8 +941,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops;
struct ov2680_ctrls *ctrls = &sensor->ctrls;
struct v4l2_ctrl_handler *hdl = &ctrls->handler;
- int exp_max = OV2680_LINES_PER_FRAME - OV2680_INTEGRATION_TIME_MARGIN;
- int ret = 0;
+ int def, max, ret = 0;
v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops);
sensor->sd.internal_ops = &ov2680_internal_ops;
@@ -939,8 +966,9 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
ARRAY_SIZE(test_pattern_menu) - 1,
0, 0, test_pattern_menu);
+ max = OV2680_LINES_PER_FRAME_30FPS - OV2680_INTEGRATION_TIME_MARGIN;
ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
- 0, exp_max, 1, exp_max);
+ 0, max, 1, max);
ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
0, 1023, 1, 250);
@@ -951,6 +979,11 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor)
0, sensor->pixel_rate,
1, sensor->pixel_rate);
+ max = OV2680_MAX_VBLANK - OV2680_DEFAULT_HEIGHT;
+ def = OV2680_LINES_PER_FRAME_30FPS - OV2680_DEFAULT_HEIGHT;
+ ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
+ OV2680_MIN_VBLANK, max, 1, def);
+
if (hdl->error) {
ret = hdl->error;
goto cleanup_entity;