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