Commit | Line | Data |
---|---|---|
3d15c764 VB |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * vivid-touch-cap.c - touch support functions. | |
4 | */ | |
5 | ||
6 | #include "vivid-core.h" | |
7 | #include "vivid-kthread-touch.h" | |
0885acd7 | 8 | #include "vivid-vid-common.h" |
3d15c764 VB |
9 | #include "vivid-touch-cap.h" |
10 | ||
11 | static int touch_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, | |
12 | unsigned int *nplanes, unsigned int sizes[], | |
13 | struct device *alloc_devs[]) | |
14 | { | |
15 | struct vivid_dev *dev = vb2_get_drv_priv(vq); | |
16 | struct v4l2_pix_format *f = &dev->tch_format; | |
17 | unsigned int size = f->sizeimage; | |
18 | ||
19 | if (*nplanes) { | |
20 | if (sizes[0] < size) | |
21 | return -EINVAL; | |
22 | } else { | |
23 | sizes[0] = size; | |
24 | } | |
25 | ||
26 | if (vq->num_buffers + *nbuffers < 2) | |
27 | *nbuffers = 2 - vq->num_buffers; | |
28 | ||
29 | *nplanes = 1; | |
30 | return 0; | |
31 | } | |
32 | ||
33 | static int touch_cap_buf_prepare(struct vb2_buffer *vb) | |
34 | { | |
35 | struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); | |
36 | struct v4l2_pix_format *f = &dev->tch_format; | |
37 | unsigned int size = f->sizeimage; | |
38 | ||
39 | if (dev->buf_prepare_error) { | |
40 | /* | |
41 | * Error injection: test what happens if buf_prepare() returns | |
42 | * an error. | |
43 | */ | |
44 | dev->buf_prepare_error = false; | |
45 | return -EINVAL; | |
46 | } | |
47 | if (vb2_plane_size(vb, 0) < size) { | |
48 | dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n", | |
49 | __func__, vb2_plane_size(vb, 0), size); | |
50 | return -EINVAL; | |
51 | } | |
52 | vb2_set_plane_payload(vb, 0, size); | |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
57 | static void touch_cap_buf_queue(struct vb2_buffer *vb) | |
58 | { | |
59 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | |
60 | struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); | |
61 | struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); | |
62 | ||
3ff4348f | 63 | vbuf->field = V4L2_FIELD_NONE; |
3d15c764 VB |
64 | spin_lock(&dev->slock); |
65 | list_add_tail(&buf->list, &dev->touch_cap_active); | |
66 | spin_unlock(&dev->slock); | |
67 | } | |
68 | ||
69 | static int touch_cap_start_streaming(struct vb2_queue *vq, unsigned int count) | |
70 | { | |
71 | struct vivid_dev *dev = vb2_get_drv_priv(vq); | |
72 | int err; | |
73 | ||
74 | dev->touch_cap_seq_count = 0; | |
75 | if (dev->start_streaming_error) { | |
76 | dev->start_streaming_error = false; | |
77 | err = -EINVAL; | |
78 | } else { | |
79 | err = vivid_start_generating_touch_cap(dev); | |
80 | } | |
81 | if (err) { | |
82 | struct vivid_buffer *buf, *tmp; | |
83 | ||
84 | list_for_each_entry_safe(buf, tmp, | |
85 | &dev->touch_cap_active, list) { | |
86 | list_del(&buf->list); | |
87 | vb2_buffer_done(&buf->vb.vb2_buf, | |
88 | VB2_BUF_STATE_QUEUED); | |
89 | } | |
90 | } | |
91 | return err; | |
92 | } | |
93 | ||
94 | /* abort streaming and wait for last buffer */ | |
95 | static void touch_cap_stop_streaming(struct vb2_queue *vq) | |
96 | { | |
97 | struct vivid_dev *dev = vb2_get_drv_priv(vq); | |
98 | ||
99 | vivid_stop_generating_touch_cap(dev); | |
100 | } | |
101 | ||
102 | static void touch_cap_buf_request_complete(struct vb2_buffer *vb) | |
103 | { | |
104 | struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); | |
105 | ||
106 | v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_touch_cap); | |
107 | } | |
108 | ||
109 | const struct vb2_ops vivid_touch_cap_qops = { | |
110 | .queue_setup = touch_cap_queue_setup, | |
111 | .buf_prepare = touch_cap_buf_prepare, | |
112 | .buf_queue = touch_cap_buf_queue, | |
113 | .start_streaming = touch_cap_start_streaming, | |
114 | .stop_streaming = touch_cap_stop_streaming, | |
115 | .buf_request_complete = touch_cap_buf_request_complete, | |
116 | .wait_prepare = vb2_ops_wait_prepare, | |
117 | .wait_finish = vb2_ops_wait_finish, | |
118 | }; | |
119 | ||
120 | int vivid_enum_fmt_tch(struct file *file, void *priv, struct v4l2_fmtdesc *f) | |
121 | { | |
122 | if (f->index) | |
123 | return -EINVAL; | |
124 | ||
125 | f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; | |
126 | return 0; | |
127 | } | |
128 | ||
129 | int vivid_g_fmt_tch(struct file *file, void *priv, struct v4l2_format *f) | |
130 | { | |
131 | struct vivid_dev *dev = video_drvdata(file); | |
132 | ||
0885acd7 HV |
133 | if (dev->multiplanar) |
134 | return -ENOTTY; | |
3d15c764 VB |
135 | f->fmt.pix = dev->tch_format; |
136 | return 0; | |
137 | } | |
138 | ||
0885acd7 HV |
139 | int vivid_g_fmt_tch_mplane(struct file *file, void *priv, struct v4l2_format *f) |
140 | { | |
141 | struct vivid_dev *dev = video_drvdata(file); | |
142 | struct v4l2_format sp_fmt; | |
143 | ||
144 | if (!dev->multiplanar) | |
145 | return -ENOTTY; | |
146 | sp_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
147 | sp_fmt.fmt.pix = dev->tch_format; | |
148 | fmt_sp2mp(&sp_fmt, f); | |
149 | return 0; | |
150 | } | |
151 | ||
3d15c764 VB |
152 | int vivid_g_parm_tch(struct file *file, void *priv, |
153 | struct v4l2_streamparm *parm) | |
154 | { | |
155 | struct vivid_dev *dev = video_drvdata(file); | |
156 | ||
0885acd7 HV |
157 | if (parm->type != (dev->multiplanar ? |
158 | V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : | |
159 | V4L2_BUF_TYPE_VIDEO_CAPTURE)) | |
3d15c764 VB |
160 | return -EINVAL; |
161 | ||
162 | parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; | |
163 | parm->parm.capture.timeperframe = dev->timeperframe_tch_cap; | |
164 | parm->parm.capture.readbuffers = 1; | |
165 | return 0; | |
166 | } | |
167 | ||
168 | int vivid_enum_input_tch(struct file *file, void *priv, struct v4l2_input *inp) | |
169 | { | |
170 | if (inp->index) | |
171 | return -EINVAL; | |
172 | ||
173 | inp->type = V4L2_INPUT_TYPE_TOUCH; | |
174 | strscpy(inp->name, "Vivid Touch", sizeof(inp->name)); | |
175 | inp->capabilities = 0; | |
176 | return 0; | |
177 | } | |
178 | ||
179 | int vivid_g_input_tch(struct file *file, void *priv, unsigned int *i) | |
180 | { | |
181 | *i = 0; | |
182 | return 0; | |
183 | } | |
184 | ||
185 | int vivid_set_touch(struct vivid_dev *dev, unsigned int i) | |
186 | { | |
187 | struct v4l2_pix_format *f = &dev->tch_format; | |
188 | ||
189 | if (i) | |
190 | return -EINVAL; | |
191 | ||
192 | f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; | |
193 | f->width = VIVID_TCH_WIDTH; | |
194 | f->height = VIVID_TCH_HEIGHT; | |
195 | f->field = V4L2_FIELD_NONE; | |
196 | f->colorspace = V4L2_COLORSPACE_RAW; | |
197 | f->bytesperline = f->width * sizeof(s16); | |
198 | f->sizeimage = f->width * f->height * sizeof(s16); | |
199 | return 0; | |
200 | } | |
201 | ||
202 | int vivid_s_input_tch(struct file *file, void *priv, unsigned int i) | |
203 | { | |
204 | return vivid_set_touch(video_drvdata(file), i); | |
205 | } | |
206 | ||
207 | static void vivid_fill_buff_noise(__s16 *tch_buf, int size) | |
208 | { | |
209 | int i; | |
210 | ||
211 | /* Fill 10% of the values within range -3 and 3, zero the others */ | |
212 | for (i = 0; i < size; i++) { | |
a251c17a | 213 | unsigned int rand = get_random_u32(); |
3d15c764 VB |
214 | |
215 | if (rand % 10) | |
216 | tch_buf[i] = 0; | |
217 | else | |
218 | tch_buf[i] = (rand / 10) % 7 - 3; | |
219 | } | |
220 | } | |
221 | ||
222 | static inline int get_random_pressure(void) | |
223 | { | |
81895a65 | 224 | return prandom_u32_max(VIVID_PRESSURE_LIMIT); |
3d15c764 VB |
225 | } |
226 | ||
227 | static void vivid_tch_buf_set(struct v4l2_pix_format *f, | |
228 | __s16 *tch_buf, | |
229 | int index) | |
230 | { | |
231 | unsigned int x = index % f->width; | |
232 | unsigned int y = index / f->width; | |
233 | unsigned int offset = VIVID_MIN_PRESSURE; | |
234 | ||
235 | tch_buf[index] = offset + get_random_pressure(); | |
236 | offset /= 2; | |
237 | if (x) | |
238 | tch_buf[index - 1] = offset + get_random_pressure(); | |
239 | if (x < f->width - 1) | |
240 | tch_buf[index + 1] = offset + get_random_pressure(); | |
241 | if (y) | |
242 | tch_buf[index - f->width] = offset + get_random_pressure(); | |
243 | if (y < f->height - 1) | |
244 | tch_buf[index + f->width] = offset + get_random_pressure(); | |
245 | offset /= 2; | |
246 | if (x && y) | |
247 | tch_buf[index - 1 - f->width] = offset + get_random_pressure(); | |
248 | if (x < f->width - 1 && y) | |
249 | tch_buf[index + 1 - f->width] = offset + get_random_pressure(); | |
250 | if (x && y < f->height - 1) | |
251 | tch_buf[index - 1 + f->width] = offset + get_random_pressure(); | |
252 | if (x < f->width - 1 && y < f->height - 1) | |
253 | tch_buf[index + 1 + f->width] = offset + get_random_pressure(); | |
254 | } | |
255 | ||
256 | void vivid_fillbuff_tch(struct vivid_dev *dev, struct vivid_buffer *buf) | |
257 | { | |
258 | struct v4l2_pix_format *f = &dev->tch_format; | |
259 | int size = f->width * f->height; | |
260 | int x, y, xstart, ystart, offset_x, offset_y; | |
261 | unsigned int test_pattern, test_pat_idx, rand; | |
262 | ||
263 | __s16 *tch_buf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); | |
264 | ||
57c1d5de | 265 | buf->vb.sequence = dev->touch_cap_with_seq_wrap_count; |
3d15c764 VB |
266 | test_pattern = (buf->vb.sequence / TCH_SEQ_COUNT) % TEST_CASE_MAX; |
267 | test_pat_idx = buf->vb.sequence % TCH_SEQ_COUNT; | |
268 | ||
269 | vivid_fill_buff_noise(tch_buf, size); | |
270 | ||
271 | if (test_pat_idx >= TCH_PATTERN_COUNT) | |
272 | return; | |
273 | ||
274 | if (test_pat_idx == 0) | |
a251c17a | 275 | dev->tch_pat_random = get_random_u32(); |
3d15c764 VB |
276 | rand = dev->tch_pat_random; |
277 | ||
278 | switch (test_pattern) { | |
279 | case SINGLE_TAP: | |
280 | if (test_pat_idx == 2) | |
281 | vivid_tch_buf_set(f, tch_buf, rand % size); | |
282 | break; | |
283 | case DOUBLE_TAP: | |
284 | if (test_pat_idx == 2 || test_pat_idx == 4) | |
285 | vivid_tch_buf_set(f, tch_buf, rand % size); | |
286 | break; | |
287 | case TRIPLE_TAP: | |
288 | if (test_pat_idx == 2 || test_pat_idx == 4 || test_pat_idx == 6) | |
289 | vivid_tch_buf_set(f, tch_buf, rand % size); | |
290 | break; | |
291 | case MOVE_LEFT_TO_RIGHT: | |
292 | vivid_tch_buf_set(f, tch_buf, | |
293 | (rand % f->height) * f->width + | |
294 | test_pat_idx * | |
295 | (f->width / TCH_PATTERN_COUNT)); | |
296 | break; | |
297 | case ZOOM_IN: | |
298 | x = f->width / 2; | |
299 | y = f->height / 2; | |
300 | offset_x = ((TCH_PATTERN_COUNT - 1 - test_pat_idx) * x) / | |
301 | TCH_PATTERN_COUNT; | |
302 | offset_y = ((TCH_PATTERN_COUNT - 1 - test_pat_idx) * y) / | |
303 | TCH_PATTERN_COUNT; | |
304 | vivid_tch_buf_set(f, tch_buf, | |
305 | (x - offset_x) + f->width * (y - offset_y)); | |
306 | vivid_tch_buf_set(f, tch_buf, | |
307 | (x + offset_x) + f->width * (y + offset_y)); | |
308 | break; | |
309 | case ZOOM_OUT: | |
310 | x = f->width / 2; | |
311 | y = f->height / 2; | |
312 | offset_x = (test_pat_idx * x) / TCH_PATTERN_COUNT; | |
313 | offset_y = (test_pat_idx * y) / TCH_PATTERN_COUNT; | |
314 | vivid_tch_buf_set(f, tch_buf, | |
315 | (x - offset_x) + f->width * (y - offset_y)); | |
316 | vivid_tch_buf_set(f, tch_buf, | |
317 | (x + offset_x) + f->width * (y + offset_y)); | |
318 | break; | |
319 | case PALM_PRESS: | |
320 | for (x = 0; x < f->width; x++) | |
321 | for (y = f->height / 2; y < f->height; y++) | |
322 | tch_buf[x + f->width * y] = VIVID_MIN_PRESSURE + | |
323 | get_random_pressure(); | |
324 | break; | |
325 | case MULTIPLE_PRESS: | |
326 | /* 16 pressure points */ | |
327 | for (y = 0; y < 4; y++) { | |
328 | for (x = 0; x < 4; x++) { | |
329 | ystart = (y * f->height) / 4 + f->height / 8; | |
330 | xstart = (x * f->width) / 4 + f->width / 8; | |
331 | vivid_tch_buf_set(f, tch_buf, | |
332 | ystart * f->width + xstart); | |
333 | } | |
334 | } | |
335 | break; | |
336 | } | |
337 | #ifdef __BIG_ENDIAN__ | |
338 | for (x = 0; x < size; x++) | |
339 | tch_buf[x] = (__force s16)__cpu_to_le16((u16)tch_buf[x]); | |
340 | #endif | |
341 | } |