From e8ad74c090f54cf8ce845b627e4312b6166059fd Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Thu, 11 Jun 2020 18:09:12 +0100
Subject: [PATCH] media: i2c: imx290: Add support for V4L2_CID_VBLANK

In order to calculate framerate and durations userspace needs
the vertical blanking information. This can be configurable,
and indeed the datasheet lists different values for VBLANK for
the 1080p and 720p modes.

Add the new control, and adopt the datasheet values for each mode.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 38 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 36 insertions(+), 2 deletions(-)

--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -38,6 +38,8 @@ enum imx290_clk_index {
 #define IMX290_BLKLEVEL_LOW 0x300a
 #define IMX290_BLKLEVEL_HIGH 0x300b
 #define IMX290_GAIN 0x3014
+#define IMX290_VMAX_LOW 0x3018
+#define IMX290_VMAX_MAX 0x3fff
 #define IMX290_HMAX_LOW 0x301c
 #define IMX290_HMAX_HIGH 0x301d
 #define IMX290_HMAX_MIN_2LANE 4400 /* Min of 4400 pixels = 30fps */
@@ -68,6 +70,7 @@ struct imx290_mode {
 	u32 width;
 	u32 height;
 	u32 hmax;
+	u32 vmax;
 	u8 link_freq_index;
 
 	const struct imx290_regval *data;
@@ -99,6 +102,7 @@ struct imx290 {
 	struct v4l2_ctrl *link_freq;
 	struct v4l2_ctrl *pixel_rate;
 	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *vblank;
 
 	struct mutex lock;
 };
@@ -132,8 +136,6 @@ static const char * const imx290_test_pa
 
 static const struct imx290_regval imx290_global_init_settings[] = {
 	{ 0x3007, 0x00 },
-	{ 0x3018, 0x65 },
-	{ 0x3019, 0x04 },
 	{ 0x301a, 0x00 },
 	{ 0x303a, 0x0c },
 	{ 0x3040, 0x00 },
@@ -360,6 +362,7 @@ static const struct imx290_mode imx290_m
 		.width = 1920,
 		.height = 1080,
 		.hmax = 0x1130,
+		.vmax = 0x0465,
 		.link_freq_index = FREQ_INDEX_1080P,
 		.data = imx290_1080p_settings,
 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
@@ -373,6 +376,7 @@ static const struct imx290_mode imx290_m
 		.width = 1280,
 		.height = 720,
 		.hmax = 0x19c8,
+		.vmax = 0x02ee,
 		.link_freq_index = FREQ_INDEX_720P,
 		.data = imx290_720p_settings,
 		.data_size = ARRAY_SIZE(imx290_720p_settings),
@@ -389,6 +393,7 @@ static const struct imx290_mode imx290_m
 		.width = 1920,
 		.height = 1080,
 		.hmax = 0x0898,
+		.vmax = 0x0465,
 		.link_freq_index = FREQ_INDEX_1080P,
 		.data = imx290_1080p_settings,
 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
@@ -402,6 +407,7 @@ static const struct imx290_mode imx290_m
 		.width = 1280,
 		.height = 720,
 		.hmax = 0x0ce4,
+		.vmax = 0x02ee,
 		.link_freq_index = FREQ_INDEX_720P,
 		.data = imx290_720p_settings,
 		.data_size = ARRAY_SIZE(imx290_720p_settings),
@@ -543,6 +549,19 @@ static int imx290_set_hmax(struct imx290
 	return 0;
 }
 
+static int imx290_set_vmax(struct imx290 *imx290, u32 val)
+{
+	u32 vmax = val + imx290->current_mode->height;
+	int ret;
+
+	ret = imx290_write_buffered_reg(imx290, IMX290_VMAX_LOW, 3,
+					vmax);
+	if (ret)
+		dev_err(imx290->dev, "Unable to write vmax\n");
+
+	return ret;
+}
+
 /* Stop streaming */
 static int imx290_stop_streaming(struct imx290 *imx290)
 {
@@ -574,6 +593,9 @@ static int imx290_set_ctrl(struct v4l2_c
 	case V4L2_CID_HBLANK:
 		ret = imx290_set_hmax(imx290, ctrl->val);
 		break;
+	case V4L2_CID_VBLANK:
+		ret = imx290_set_vmax(imx290, ctrl->val);
+		break;
 	case V4L2_CID_TEST_PATTERN:
 		if (ctrl->val) {
 			imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x00);
@@ -736,6 +758,12 @@ static int imx290_set_fmt(struct v4l2_su
 						 imx290->hmax_min - mode->width,
 						 IMX290_HMAX_MAX - mode->width,
 						 1, mode->hmax - mode->width);
+		if (imx290->vblank)
+			__v4l2_ctrl_modify_range(imx290->vblank,
+						 mode->vmax - mode->height,
+						 IMX290_VMAX_MAX - mode->height,
+						 1,
+						 mode->vmax - mode->height);
 	}
 
 	*format = fmt->format;
@@ -1148,6 +1176,12 @@ static int imx290_probe(struct i2c_clien
 					   IMX290_HMAX_MAX - mode->width, 1,
 					   mode->hmax - mode->width);
 
+	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+					   V4L2_CID_VBLANK,
+					   mode->vmax - mode->height,
+					   IMX290_VMAX_MAX - mode->height, 1,
+					   mode->vmax - mode->height);
+
 	imx290->link_freq =
 		v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
 				       V4L2_CID_LINK_FREQ,
