@@ -255,6 +255,7 @@ struct imx290 {
struct v4l2_ctrl *link_freq;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *vtotal;
struct v4l2_ctrl *exposure;
struct {
struct v4l2_ctrl *hflip;
@@ -782,8 +783,7 @@ static void imx290_exposure_update(struct imx290 *imx290,
{
unsigned int exposure_max;
- exposure_max = imx290->vblank->val + mode->height -
- IMX290_EXPOSURE_OFFSET;
+ exposure_max = imx290->vtotal->val - IMX290_EXPOSURE_OFFSET;
__v4l2_ctrl_modify_range(imx290->exposure, 1, exposure_max, 1,
exposure_max);
}
@@ -794,7 +794,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
struct imx290, ctrls);
const struct v4l2_mbus_framefmt *format;
struct v4l2_subdev_state *state;
- int ret = 0, vmax;
+ int ret = 0;
/*
* Return immediately for controls that don't need to be applied to the
@@ -803,10 +803,22 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
return 0;
- if (ctrl->id == V4L2_CID_VBLANK) {
- /* Changing vblank changes the allowed range for exposure. */
+ /* Changing vtotal changes the allowed range for exposure. */
+ if (ctrl->id == V4L2_CID_VTOTAL)
imx290_exposure_update(imx290, imx290->current_mode);
- }
+
+ /*
+ * vblank and vtotal depend on each other, therefore also update the
+ * other one.
+ */
+ if (ctrl->id == V4L2_CID_VBLANK &&
+ imx290->vtotal->val != ctrl->val + imx290->current_mode->height)
+ __v4l2_ctrl_s_ctrl(imx290->vtotal,
+ ctrl->val + imx290->current_mode->height);
+ if (ctrl->id == V4L2_CID_VTOTAL &&
+ imx290->vblank->val != ctrl->val - imx290->current_mode->height)
+ __v4l2_ctrl_s_ctrl(imx290->vblank,
+ ctrl->val - imx290->current_mode->height);
/* V4L2 controls values will be applied only when power is already up */
if (!pm_runtime_get_if_in_use(imx290->dev))
@@ -821,9 +833,14 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_VBLANK:
- ret = imx290_write(imx290, IMX290_VMAX,
- ctrl->val + imx290->current_mode->height,
- NULL);
+ /* vblank is updated by vtotal. */
+ break;
+
+ case V4L2_CID_VTOTAL:
+ ret = imx290_write(imx290, IMX290_VMAX, ctrl->val, NULL);
+ if (ret)
+ goto err;
+
/*
* Due to the way that exposure is programmed in this sensor in
* relation to VMAX, we have to reprogramme it whenever VMAX is
@@ -834,9 +851,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
ctrl = imx290->exposure;
fallthrough;
case V4L2_CID_EXPOSURE:
- vmax = imx290->vblank->val + imx290->current_mode->height;
ret = imx290_write(imx290, IMX290_SHS1,
- vmax - ctrl->val - 1, NULL);
+ imx290->vtotal->val - ctrl->val - 1, NULL);
break;
case V4L2_CID_TEST_PATTERN:
@@ -880,6 +896,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
break;
}
+err:
pm_runtime_mark_last_busy(imx290->dev);
pm_runtime_put_autosuspend(imx290->dev);
@@ -911,11 +928,14 @@ static void imx290_ctrl_update(struct imx290 *imx290,
unsigned int vblank_max = IMX290_VMAX_MAX - mode->height;
__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
+ __v4l2_ctrl_s_ctrl(imx290->vblank, imx290->vtotal->val - mode->height);
__v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
hblank_min);
__v4l2_ctrl_modify_range(imx290->vblank, vblank_min, vblank_max, 1,
vblank_min);
+ __v4l2_ctrl_modify_range(imx290->vtotal, mode->vmax_min,
+ IMX290_VMAX_MAX, 1, mode->vmax_min);
}
static int imx290_ctrl_init(struct imx290 *imx290)
@@ -947,7 +967,7 @@ static int imx290_ctrl_init(struct imx290 *imx290)
/*
* Correct range will be determined through imx290_ctrl_update setting
- * V4L2_CID_VBLANK.
+ * V4L2_CID_VTOTAL.
*/
imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
V4L2_CID_EXPOSURE, 1, 65535, 1,
@@ -983,6 +1003,9 @@ static int imx290_ctrl_init(struct imx290 *imx290)
imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
V4L2_CID_VBLANK, 1, 1, 1, 1);
+ imx290->vtotal = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+ V4L2_CID_VTOTAL, 1, IMX290_VMAX_MAX,
+ 1, 1);
imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);