aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.15/950-0737-media-i2c-ov7251-Add-V4L2_CID_VBLANK.patch
blob: c4c3c221d7afb35715162280281eec3f4c0a0fad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
From 9e6a5b925f3b401c9a047237c0014367655b291a Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Thu, 17 Feb 2022 18:08:39 +0000
Subject: [PATCH] media: i2c: ov7251: Add V4L2_CID_VBLANK

This is a raw sensor so should be implementing V4L2_CID_VBLANK
instead of the frame_interval ops, as per docs at
https://www.kernel.org/doc/html/latest/driver-api/media/camera-sensor.html#frame-interval-configuration

This driver already implemented the frame_interval ops, so
removing them would be a regression.
Implement V4L2_CID_VBLANK, with the frame_interval ops setting
that control.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/ov7251.c | 56 +++++++++++++++++++++++++++++---------
 1 file changed, 43 insertions(+), 13 deletions(-)

--- a/drivers/media/i2c/ov7251.c
+++ b/drivers/media/i2c/ov7251.c
@@ -43,6 +43,8 @@
 #define OV7251_HTS			0x3a0
 #define OV7251_VTS_HIGH			0x380e
 #define OV7251_VTS_LOW			0x380f
+#define OV7251_VTS_MIN_OFFSET		92
+#define OV7251_VTS_MAX			0x7fff
 #define OV7251_TIMING_FORMAT1		0x3820
 #define OV7251_TIMING_FORMAT1_VFLIP	BIT(2)
 #define OV7251_TIMING_FORMAT2		0x3821
@@ -136,6 +138,7 @@ struct ov7251 {
 	struct v4l2_ctrl_handler ctrls;
 	struct v4l2_ctrl *exposure;
 	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *vblank;
 
 	/* Cached register values */
 	u8 aec_pk_manual;
@@ -688,6 +691,19 @@ static int ov7251_set_vflip(struct ov725
 	return ret;
 }
 
+static int ov7251_set_vblank(struct ov7251 *ov7251, s32 value)
+{
+	u16 reg;
+	u8 val[2];
+
+	reg = OV7251_VTS_HIGH;
+	value += ov7251->current_mode->height;
+	val[0] = (value & 0xff00) >> 8; /* goes to OV7251_VTS_HIGH */
+	val[1] = value & 0xff;          /* goes to OV7251_VTS_LOW */
+
+	return ov7251_write_seq_regs(ov7251, reg, val, 2);
+}
+
 static int ov7251_set_test_pattern(struct ov7251 *ov7251, s32 value)
 {
 	u8 val = ov7251->pre_isp_00;
@@ -714,9 +730,20 @@ static int ov7251_s_ctrl(struct v4l2_ctr
 {
 	struct ov7251 *ov7251 = container_of(ctrl->handler,
 					     struct ov7251, ctrls);
+	s64 max;
 	int ret;
 
 	/* v4l2_ctrl_lock() locks our mutex */
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+		/* Update max exposure while meeting expected vblanking */
+		max = ov7251->current_mode->height + ctrl->val - OV7251_EXPOSURE_OFFSET;
+		__v4l2_ctrl_modify_range(ov7251->exposure,
+					 ov7251->exposure->minimum, max,
+					 ov7251->exposure->step,
+					 ov7251->exposure->default_value);
+		break;
+	}
 
 	if (!pm_runtime_get_if_in_use(ov7251->dev))
 		return 0;
@@ -737,6 +764,9 @@ static int ov7251_s_ctrl(struct v4l2_ctr
 	case V4L2_CID_VFLIP:
 		ret = ov7251_set_vflip(ov7251, ctrl->val);
 		break;
+	case V4L2_CID_VBLANK:
+		ret = ov7251_set_vblank(ov7251, ctrl->val);
+		break;
 	default:
 		ret = -EINVAL;
 		break;
@@ -1030,14 +1060,6 @@ static int ov7251_s_stream(struct v4l2_s
 				ov7251->current_mode->height);
 			goto err_power_down;
 		}
-		ret = ov7251_write_reg(ov7251, OV7251_VTS_HIGH,
-				       ov7251->current_ival->vts >> 8);
-		if (ret)
-			goto err_power_down;
-		ret = ov7251_write_reg(ov7251, OV7251_VTS_LOW,
-				       ov7251->current_ival->vts & 0xff);
-		if (ret)
-			goto err_power_down;
 		ret = __v4l2_ctrl_handler_setup(&ov7251->ctrls);
 		if (ret < 0) {
 			dev_err(ov7251->dev, "could not sync v4l2 controls\n");
@@ -1088,12 +1110,13 @@ static int ov7251_set_frame_interval(str
 		ret = __v4l2_ctrl_modify_range(ov7251->exposure, 1,
 					       new_ival->vts -
 							OV7251_EXPOSURE_OFFSET,
-					       1, ov7251->current_mode->exposure_def);
+					       1, ov7251->exposure->val);
 		if (ret < 0)
 			goto exit;
 
-		ret = __v4l2_ctrl_s_ctrl(ov7251->exposure,
-					 ov7251->current_mode->exposure_def);
+		ret = __v4l2_ctrl_s_ctrl(ov7251->vblank,
+					 new_ival->vts -
+						ov7251->current_mode->height);
 		if (ret < 0)
 			goto exit;
 
@@ -1233,7 +1256,7 @@ static int ov7251_probe(struct i2c_clien
 	struct v4l2_ctrl *ctrl;
 	struct ov7251 *ov7251;
 	unsigned int rate = 0;
-	u32 h_blank;
+	u32 h_blank, v_blank, v_blank_max;
 	int ret;
 	int i;
 
@@ -1314,7 +1337,7 @@ static int ov7251_probe(struct i2c_clien
 	ov7251->current_mode = &ov7251_mode_info_data[0];
 	ov7251->current_ival = &ov7251_frame_ival_info_data[0];
 
-	v4l2_ctrl_handler_init(&ov7251->ctrls, 10);
+	v4l2_ctrl_handler_init(&ov7251->ctrls, 11);
 	ov7251->ctrls.lock = &ov7251->lock;
 
 	v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
@@ -1345,6 +1368,13 @@ static int ov7251_probe(struct i2c_clien
 	if (ov7251->hblank)
 		ov7251->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
+	v_blank = ov7251->current_ival->vts - ov7251->current_mode->height;
+	v_blank_max = OV7251_VTS_MAX - ov7251->current_mode->width;
+	ov7251->vblank = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+					   V4L2_CID_VBLANK,
+					   OV7251_VTS_MIN_OFFSET,
+					   v_blank_max, 1, v_blank);
+
 	ov7251->sd.ctrl_handler = &ov7251->ctrls;
 
 	if (ov7251->ctrls.error) {