1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * A virtual codec example device. |
4 | * |
5 | * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
6 | * |
7 | * This is a virtual codec device driver for testing the codec framework. |
8 | * It simulates a device that uses memory buffers for both source and |
9 | * destination and encodes or decodes the data. |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/fs.h> |
15 | #include <linux/sched.h> |
16 | #include <linux/slab.h> |
17 | |
18 | #include <linux/platform_device.h> |
19 | #include <media/v4l2-mem2mem.h> |
20 | #include <media/v4l2-device.h> |
21 | #include <media/v4l2-ioctl.h> |
22 | #include <media/v4l2-ctrls.h> |
23 | #include <media/v4l2-event.h> |
24 | #include <media/videobuf2-vmalloc.h> |
25 | |
26 | #include "codec-v4l2-fwht.h" |
27 | |
28 | MODULE_DESCRIPTION("Virtual codec device" ); |
29 | MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>" ); |
30 | MODULE_LICENSE("GPL v2" ); |
31 | |
32 | static bool multiplanar; |
33 | module_param(multiplanar, bool, 0444); |
34 | MODULE_PARM_DESC(multiplanar, |
35 | " use multi-planar API instead of single-planar API" ); |
36 | |
37 | static unsigned int debug; |
38 | module_param(debug, uint, 0644); |
39 | MODULE_PARM_DESC(debug, " activates debug info" ); |
40 | |
41 | #define VICODEC_NAME "vicodec" |
42 | #define MAX_WIDTH 4096U |
43 | #define MIN_WIDTH 640U |
44 | #define MAX_HEIGHT 2160U |
45 | #define MIN_HEIGHT 360U |
46 | |
47 | #define dprintk(dev, fmt, arg...) \ |
48 | v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) |
49 | |
50 | |
51 | struct pixfmt_info { |
52 | u32 id; |
53 | unsigned int bytesperline_mult; |
54 | unsigned int sizeimage_mult; |
55 | unsigned int sizeimage_div; |
56 | unsigned int luma_step; |
57 | unsigned int chroma_step; |
58 | /* Chroma plane subsampling */ |
59 | unsigned int width_div; |
60 | unsigned int height_div; |
61 | }; |
62 | |
63 | static const struct v4l2_fwht_pixfmt_info pixfmt_fwht = { |
64 | V4L2_PIX_FMT_FWHT, 0, 3, 1, 1, 1, 1, 1, 0, 1 |
65 | }; |
66 | |
67 | static const struct v4l2_fwht_pixfmt_info pixfmt_stateless_fwht = { |
68 | V4L2_PIX_FMT_FWHT_STATELESS, 0, 3, 1, 1, 1, 1, 1, 0, 1 |
69 | }; |
70 | |
71 | static void vicodec_dev_release(struct device *dev) |
72 | { |
73 | } |
74 | |
75 | static struct platform_device vicodec_pdev = { |
76 | .name = VICODEC_NAME, |
77 | .dev.release = vicodec_dev_release, |
78 | }; |
79 | |
80 | /* Per-queue, driver-specific private data */ |
81 | struct vicodec_q_data { |
82 | unsigned int coded_width; |
83 | unsigned int coded_height; |
84 | unsigned int visible_width; |
85 | unsigned int visible_height; |
86 | unsigned int sizeimage; |
87 | unsigned int vb2_sizeimage; |
88 | unsigned int sequence; |
89 | const struct v4l2_fwht_pixfmt_info *info; |
90 | }; |
91 | |
92 | enum { |
93 | V4L2_M2M_SRC = 0, |
94 | V4L2_M2M_DST = 1, |
95 | }; |
96 | |
97 | struct vicodec_dev_instance { |
98 | struct video_device vfd; |
99 | struct mutex mutex; |
100 | spinlock_t lock; |
101 | struct v4l2_m2m_dev *m2m_dev; |
102 | }; |
103 | |
104 | struct vicodec_dev { |
105 | struct v4l2_device v4l2_dev; |
106 | struct vicodec_dev_instance stateful_enc; |
107 | struct vicodec_dev_instance stateful_dec; |
108 | struct vicodec_dev_instance stateless_dec; |
109 | #ifdef CONFIG_MEDIA_CONTROLLER |
110 | struct media_device mdev; |
111 | #endif |
112 | |
113 | }; |
114 | |
115 | struct vicodec_ctx { |
116 | struct v4l2_fh fh; |
117 | struct vicodec_dev *dev; |
118 | bool is_enc; |
119 | bool is_stateless; |
120 | spinlock_t *lock; |
121 | |
122 | struct v4l2_ctrl_handler hdl; |
123 | |
124 | /* Source and destination queue data */ |
125 | struct vicodec_q_data q_data[2]; |
126 | struct v4l2_fwht_state state; |
127 | |
128 | u32 cur_buf_offset; |
129 | u32 comp_max_size; |
130 | u32 comp_size; |
131 | u32 ; |
132 | u32 comp_magic_cnt; |
133 | bool comp_has_frame; |
134 | bool comp_has_next_frame; |
135 | bool first_source_change_sent; |
136 | bool source_changed; |
137 | }; |
138 | |
139 | static const struct v4l2_event vicodec_eos_event = { |
140 | .type = V4L2_EVENT_EOS |
141 | }; |
142 | |
143 | static inline struct vicodec_ctx *file2ctx(struct file *file) |
144 | { |
145 | return container_of(file->private_data, struct vicodec_ctx, fh); |
146 | } |
147 | |
148 | static struct vicodec_q_data *get_q_data(struct vicodec_ctx *ctx, |
149 | enum v4l2_buf_type type) |
150 | { |
151 | switch (type) { |
152 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: |
153 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: |
154 | return &ctx->q_data[V4L2_M2M_SRC]; |
155 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
156 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: |
157 | return &ctx->q_data[V4L2_M2M_DST]; |
158 | default: |
159 | break; |
160 | } |
161 | return NULL; |
162 | } |
163 | |
164 | static void copy_cap_to_ref(const u8 *cap, const struct v4l2_fwht_pixfmt_info *info, |
165 | struct v4l2_fwht_state *state) |
166 | { |
167 | int plane_idx; |
168 | u8 *p_ref = state->ref_frame.buf; |
169 | unsigned int cap_stride = state->stride; |
170 | unsigned int ref_stride = state->ref_stride; |
171 | |
172 | for (plane_idx = 0; plane_idx < info->planes_num; plane_idx++) { |
173 | int i; |
174 | unsigned int h_div = (plane_idx == 1 || plane_idx == 2) ? |
175 | info->height_div : 1; |
176 | const u8 *row_cap = cap; |
177 | u8 *row_ref = p_ref; |
178 | |
179 | if (info->planes_num == 3 && plane_idx == 1) { |
180 | cap_stride /= 2; |
181 | ref_stride /= 2; |
182 | } |
183 | |
184 | if (plane_idx == 1 && |
185 | (info->id == V4L2_PIX_FMT_NV24 || |
186 | info->id == V4L2_PIX_FMT_NV42)) { |
187 | cap_stride *= 2; |
188 | ref_stride *= 2; |
189 | } |
190 | |
191 | for (i = 0; i < state->visible_height / h_div; i++) { |
192 | memcpy(row_ref, row_cap, ref_stride); |
193 | row_ref += ref_stride; |
194 | row_cap += cap_stride; |
195 | } |
196 | cap += cap_stride * (state->coded_height / h_div); |
197 | p_ref += ref_stride * (state->coded_height / h_div); |
198 | } |
199 | } |
200 | |
201 | static bool validate_by_version(unsigned int flags, unsigned int version) |
202 | { |
203 | if (!version || version > V4L2_FWHT_VERSION) |
204 | return false; |
205 | |
206 | if (version >= 2) { |
207 | unsigned int components_num = 1 + |
208 | ((flags & V4L2_FWHT_FL_COMPONENTS_NUM_MSK) >> |
209 | V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET); |
210 | unsigned int pixenc = flags & V4L2_FWHT_FL_PIXENC_MSK; |
211 | |
212 | if (components_num == 0 || components_num > 4 || !pixenc) |
213 | return false; |
214 | } |
215 | return true; |
216 | } |
217 | |
218 | static bool validate_stateless_params_flags(const struct v4l2_ctrl_fwht_params *params, |
219 | const struct v4l2_fwht_pixfmt_info *cur_info) |
220 | { |
221 | unsigned int width_div = |
222 | (params->flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; |
223 | unsigned int height_div = |
224 | (params->flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; |
225 | unsigned int components_num = 3; |
226 | unsigned int pixenc = 0; |
227 | |
228 | if (params->version < 3) |
229 | return false; |
230 | |
231 | components_num = 1 + ((params->flags & V4L2_FWHT_FL_COMPONENTS_NUM_MSK) >> |
232 | V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET); |
233 | pixenc = (params->flags & V4L2_FWHT_FL_PIXENC_MSK); |
234 | if (v4l2_fwht_validate_fmt(info: cur_info, width_div, height_div, |
235 | components_num, pixenc)) |
236 | return true; |
237 | return false; |
238 | } |
239 | |
240 | |
241 | static void (struct vicodec_ctx *ctx) |
242 | { |
243 | const struct fwht_cframe_hdr *p_hdr = &ctx->state.header; |
244 | |
245 | ctx->state.visible_width = ntohl(p_hdr->width); |
246 | ctx->state.visible_height = ntohl(p_hdr->height); |
247 | ctx->state.colorspace = ntohl(p_hdr->colorspace); |
248 | ctx->state.xfer_func = ntohl(p_hdr->xfer_func); |
249 | ctx->state.ycbcr_enc = ntohl(p_hdr->ycbcr_enc); |
250 | ctx->state.quantization = ntohl(p_hdr->quantization); |
251 | } |
252 | |
253 | static int device_process(struct vicodec_ctx *ctx, |
254 | struct vb2_v4l2_buffer *src_vb, |
255 | struct vb2_v4l2_buffer *dst_vb) |
256 | { |
257 | struct vicodec_dev *dev = ctx->dev; |
258 | struct v4l2_fwht_state *state = &ctx->state; |
259 | u8 *p_src, *p_dst; |
260 | int ret = 0; |
261 | |
262 | if (ctx->is_enc || ctx->is_stateless) |
263 | p_src = vb2_plane_vaddr(vb: &src_vb->vb2_buf, plane_no: 0); |
264 | else |
265 | p_src = state->compressed_frame; |
266 | |
267 | if (ctx->is_stateless) { |
268 | struct media_request *src_req = src_vb->vb2_buf.req_obj.req; |
269 | |
270 | ret = v4l2_ctrl_request_setup(req: src_req, parent: &ctx->hdl); |
271 | if (ret) |
272 | return ret; |
273 | update_state_from_header(ctx); |
274 | |
275 | ctx->state.header.size = |
276 | htonl(vb2_get_plane_payload(&src_vb->vb2_buf, 0)); |
277 | /* |
278 | * set the reference buffer from the reference timestamp |
279 | * only if this is a P-frame |
280 | */ |
281 | if (!(ntohl(ctx->state.header.flags) & V4L2_FWHT_FL_I_FRAME)) { |
282 | struct vb2_buffer *ref_vb2_buf; |
283 | struct vb2_queue *vq_cap = |
284 | v4l2_m2m_get_vq(m2m_ctx: ctx->fh.m2m_ctx, |
285 | type: V4L2_BUF_TYPE_VIDEO_CAPTURE); |
286 | |
287 | ref_vb2_buf = vb2_find_buffer(q: vq_cap, timestamp: ctx->state.ref_frame_ts); |
288 | if (!ref_vb2_buf) |
289 | return -EINVAL; |
290 | if (ref_vb2_buf->state == VB2_BUF_STATE_ERROR) |
291 | ret = -EINVAL; |
292 | ctx->state.ref_frame.buf = |
293 | vb2_plane_vaddr(vb: ref_vb2_buf, plane_no: 0); |
294 | } else { |
295 | ctx->state.ref_frame.buf = NULL; |
296 | } |
297 | } |
298 | p_dst = vb2_plane_vaddr(vb: &dst_vb->vb2_buf, plane_no: 0); |
299 | if (!p_src || !p_dst) { |
300 | v4l2_err(&dev->v4l2_dev, |
301 | "Acquiring kernel pointers to buffers failed\n" ); |
302 | return -EFAULT; |
303 | } |
304 | |
305 | if (ctx->is_enc) { |
306 | struct vicodec_q_data *q_src; |
307 | int comp_sz_or_errcode; |
308 | |
309 | q_src = get_q_data(ctx, type: V4L2_BUF_TYPE_VIDEO_OUTPUT); |
310 | state->info = q_src->info; |
311 | comp_sz_or_errcode = v4l2_fwht_encode(state, p_in: p_src, p_out: p_dst); |
312 | if (comp_sz_or_errcode < 0) |
313 | return comp_sz_or_errcode; |
314 | vb2_set_plane_payload(vb: &dst_vb->vb2_buf, plane_no: 0, size: comp_sz_or_errcode); |
315 | } else { |
316 | struct vicodec_q_data *q_dst; |
317 | unsigned int comp_frame_size = ntohl(ctx->state.header.size); |
318 | |
319 | q_dst = get_q_data(ctx, type: V4L2_BUF_TYPE_VIDEO_CAPTURE); |
320 | if (comp_frame_size > ctx->comp_max_size) |
321 | return -EINVAL; |
322 | state->info = q_dst->info; |
323 | ret = v4l2_fwht_decode(state, p_in: p_src, p_out: p_dst); |
324 | if (ret < 0) |
325 | return ret; |
326 | if (!ctx->is_stateless) |
327 | copy_cap_to_ref(cap: p_dst, info: ctx->state.info, state: &ctx->state); |
328 | |
329 | vb2_set_plane_payload(vb: &dst_vb->vb2_buf, plane_no: 0, size: q_dst->sizeimage); |
330 | if (ntohl(ctx->state.header.flags) & V4L2_FWHT_FL_I_FRAME) |
331 | dst_vb->flags |= V4L2_BUF_FLAG_KEYFRAME; |
332 | else |
333 | dst_vb->flags |= V4L2_BUF_FLAG_PFRAME; |
334 | } |
335 | return ret; |
336 | } |
337 | |
338 | /* |
339 | * mem2mem callbacks |
340 | */ |
341 | static enum vb2_buffer_state (struct vicodec_ctx *ctx, |
342 | u8 **pp, u32 sz) |
343 | { |
344 | static const u8 magic[] = { |
345 | 0x4f, 0x4f, 0x4f, 0x4f, 0xff, 0xff, 0xff, 0xff |
346 | }; |
347 | u8 *p = *pp; |
348 | u32 state; |
349 | u8 * = (u8 *)&ctx->state.header; |
350 | |
351 | state = VB2_BUF_STATE_DONE; |
352 | |
353 | if (!ctx->header_size) { |
354 | state = VB2_BUF_STATE_ERROR; |
355 | for (; p < *pp + sz; p++) { |
356 | u32 copy; |
357 | |
358 | p = memchr(p, c: magic[ctx->comp_magic_cnt], |
359 | size: *pp + sz - p); |
360 | if (!p) { |
361 | ctx->comp_magic_cnt = 0; |
362 | p = *pp + sz; |
363 | break; |
364 | } |
365 | copy = sizeof(magic) - ctx->comp_magic_cnt; |
366 | if (*pp + sz - p < copy) |
367 | copy = *pp + sz - p; |
368 | |
369 | memcpy(header + ctx->comp_magic_cnt, p, copy); |
370 | ctx->comp_magic_cnt += copy; |
371 | if (!memcmp(p: header, q: magic, size: ctx->comp_magic_cnt)) { |
372 | p += copy; |
373 | state = VB2_BUF_STATE_DONE; |
374 | break; |
375 | } |
376 | ctx->comp_magic_cnt = 0; |
377 | } |
378 | if (ctx->comp_magic_cnt < sizeof(magic)) { |
379 | *pp = p; |
380 | return state; |
381 | } |
382 | ctx->header_size = sizeof(magic); |
383 | } |
384 | |
385 | if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { |
386 | u32 copy = sizeof(struct fwht_cframe_hdr) - ctx->header_size; |
387 | |
388 | if (*pp + sz - p < copy) |
389 | copy = *pp + sz - p; |
390 | |
391 | memcpy(header + ctx->header_size, p, copy); |
392 | p += copy; |
393 | ctx->header_size += copy; |
394 | } |
395 | *pp = p; |
396 | return state; |
397 | } |
398 | |
399 | /* device_run() - prepares and starts the device */ |
400 | static void device_run(void *priv) |
401 | { |
402 | struct vicodec_ctx *ctx = priv; |
403 | struct vicodec_dev *dev = ctx->dev; |
404 | struct vb2_v4l2_buffer *src_buf, *dst_buf; |
405 | struct vicodec_q_data *q_src, *q_dst; |
406 | u32 state; |
407 | struct media_request *src_req; |
408 | |
409 | src_buf = v4l2_m2m_next_src_buf(m2m_ctx: ctx->fh.m2m_ctx); |
410 | dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx: ctx->fh.m2m_ctx); |
411 | src_req = src_buf->vb2_buf.req_obj.req; |
412 | |
413 | q_src = get_q_data(ctx, type: V4L2_BUF_TYPE_VIDEO_OUTPUT); |
414 | q_dst = get_q_data(ctx, type: V4L2_BUF_TYPE_VIDEO_CAPTURE); |
415 | |
416 | state = VB2_BUF_STATE_DONE; |
417 | if (device_process(ctx, src_vb: src_buf, dst_vb: dst_buf)) |
418 | state = VB2_BUF_STATE_ERROR; |
419 | else |
420 | dst_buf->sequence = q_dst->sequence++; |
421 | dst_buf->flags &= ~V4L2_BUF_FLAG_LAST; |
422 | v4l2_m2m_buf_copy_metadata(out_vb: src_buf, cap_vb: dst_buf, copy_frame_flags: false); |
423 | |
424 | spin_lock(lock: ctx->lock); |
425 | if (!ctx->comp_has_next_frame && |
426 | v4l2_m2m_is_last_draining_src_buf(m2m_ctx: ctx->fh.m2m_ctx, vbuf: src_buf)) { |
427 | dst_buf->flags |= V4L2_BUF_FLAG_LAST; |
428 | v4l2_event_queue_fh(fh: &ctx->fh, ev: &vicodec_eos_event); |
429 | v4l2_m2m_mark_stopped(m2m_ctx: ctx->fh.m2m_ctx); |
430 | } |
431 | if (ctx->is_enc || ctx->is_stateless) { |
432 | src_buf->sequence = q_src->sequence++; |
433 | src_buf = v4l2_m2m_src_buf_remove(m2m_ctx: ctx->fh.m2m_ctx); |
434 | v4l2_m2m_buf_done(buf: src_buf, state); |
435 | } else if (vb2_get_plane_payload(vb: &src_buf->vb2_buf, plane_no: 0) == ctx->cur_buf_offset) { |
436 | src_buf->sequence = q_src->sequence++; |
437 | src_buf = v4l2_m2m_src_buf_remove(m2m_ctx: ctx->fh.m2m_ctx); |
438 | v4l2_m2m_buf_done(buf: src_buf, state); |
439 | ctx->cur_buf_offset = 0; |
440 | ctx->comp_has_next_frame = false; |
441 | } |
442 | v4l2_m2m_buf_done(buf: dst_buf, state); |
443 | |
444 | ctx->comp_size = 0; |
445 | ctx->header_size = 0; |
446 | ctx->comp_magic_cnt = 0; |
447 | ctx->comp_has_frame = false; |
448 | spin_unlock(lock: ctx->lock); |
449 | if (ctx->is_stateless && src_req) |
450 | v4l2_ctrl_request_complete(req: src_req, parent: &ctx->hdl); |
451 | |
452 | if (ctx->is_enc) |
453 | v4l2_m2m_job_finish(m2m_dev: dev->stateful_enc.m2m_dev, m2m_ctx: ctx->fh.m2m_ctx); |
454 | else if (ctx->is_stateless) |
455 | v4l2_m2m_job_finish(m2m_dev: dev->stateless_dec.m2m_dev, |
456 | m2m_ctx: ctx->fh.m2m_ctx); |
457 | else |
458 | v4l2_m2m_job_finish(m2m_dev: dev->stateful_dec.m2m_dev, m2m_ctx: ctx->fh.m2m_ctx); |
459 | } |
460 | |
461 | static void job_remove_src_buf(struct vicodec_ctx *ctx, u32 state) |
462 | { |
463 | struct vb2_v4l2_buffer *src_buf; |
464 | struct vicodec_q_data *q_src; |
465 | |
466 | q_src = get_q_data(ctx, type: V4L2_BUF_TYPE_VIDEO_OUTPUT); |
467 | spin_lock(lock: ctx->lock); |
468 | src_buf = v4l2_m2m_src_buf_remove(m2m_ctx: ctx->fh.m2m_ctx); |
469 | src_buf->sequence = q_src->sequence++; |
470 | v4l2_m2m_buf_done(buf: src_buf, state); |
471 | ctx->cur_buf_offset = 0; |
472 | spin_unlock(lock: ctx->lock); |
473 | } |
474 | |
475 | static const struct v4l2_fwht_pixfmt_info * |
476 | (const struct fwht_cframe_hdr *p_hdr) |
477 | { |
478 | unsigned int flags = ntohl(p_hdr->flags); |
479 | unsigned int width_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; |
480 | unsigned int height_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; |
481 | unsigned int components_num = 3; |
482 | unsigned int pixenc = 0; |
483 | unsigned int version = ntohl(p_hdr->version); |
484 | |
485 | if (version >= 2) { |
486 | components_num = 1 + ((flags & V4L2_FWHT_FL_COMPONENTS_NUM_MSK) >> |
487 | V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET); |
488 | pixenc = (flags & V4L2_FWHT_FL_PIXENC_MSK); |
489 | } |
490 | return v4l2_fwht_find_nth_fmt(width_div, height_div, |
491 | components_num, pixenc, start_idx: 0); |
492 | } |
493 | |
494 | static bool (const struct fwht_cframe_hdr *p_hdr) |
495 | { |
496 | const struct v4l2_fwht_pixfmt_info *info; |
497 | unsigned int w = ntohl(p_hdr->width); |
498 | unsigned int h = ntohl(p_hdr->height); |
499 | unsigned int version = ntohl(p_hdr->version); |
500 | unsigned int flags = ntohl(p_hdr->flags); |
501 | |
502 | if (w < MIN_WIDTH || w > MAX_WIDTH || h < MIN_HEIGHT || h > MAX_HEIGHT) |
503 | return false; |
504 | |
505 | if (!validate_by_version(flags, version)) |
506 | return false; |
507 | |
508 | info = info_from_header(p_hdr); |
509 | if (!info) |
510 | return false; |
511 | return true; |
512 | } |
513 | |
514 | static void (struct vicodec_ctx *ctx) |
515 | { |
516 | struct vicodec_q_data *q_dst = get_q_data(ctx, |
517 | type: V4L2_BUF_TYPE_VIDEO_CAPTURE); |
518 | const struct fwht_cframe_hdr *p_hdr = &ctx->state.header; |
519 | const struct v4l2_fwht_pixfmt_info *info = info_from_header(p_hdr); |
520 | unsigned int flags = ntohl(p_hdr->flags); |
521 | unsigned int hdr_width_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; |
522 | unsigned int hdr_height_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; |
523 | |
524 | /* |
525 | * This function should not be used by a stateless codec since |
526 | * it changes values in q_data that are not request specific |
527 | */ |
528 | WARN_ON(ctx->is_stateless); |
529 | |
530 | q_dst->info = info; |
531 | q_dst->visible_width = ntohl(p_hdr->width); |
532 | q_dst->visible_height = ntohl(p_hdr->height); |
533 | q_dst->coded_width = vic_round_dim(q_dst->visible_width, hdr_width_div); |
534 | q_dst->coded_height = vic_round_dim(q_dst->visible_height, |
535 | hdr_height_div); |
536 | |
537 | q_dst->sizeimage = q_dst->coded_width * q_dst->coded_height * |
538 | q_dst->info->sizeimage_mult / q_dst->info->sizeimage_div; |
539 | ctx->state.colorspace = ntohl(p_hdr->colorspace); |
540 | |
541 | ctx->state.xfer_func = ntohl(p_hdr->xfer_func); |
542 | ctx->state.ycbcr_enc = ntohl(p_hdr->ycbcr_enc); |
543 | ctx->state.quantization = ntohl(p_hdr->quantization); |
544 | } |
545 | |
546 | static void set_last_buffer(struct vb2_v4l2_buffer *dst_buf, |
547 | const struct vb2_v4l2_buffer *src_buf, |
548 | struct vicodec_ctx *ctx) |
549 | { |
550 | struct vicodec_q_data *q_dst = get_q_data(ctx, |
551 | type: V4L2_BUF_TYPE_VIDEO_CAPTURE); |
552 | |
553 | vb2_set_plane_payload(vb: &dst_buf->vb2_buf, plane_no: 0, size: 0); |
554 | dst_buf->sequence = q_dst->sequence++; |
555 | |
556 | v4l2_m2m_buf_copy_metadata(out_vb: src_buf, cap_vb: dst_buf, copy_frame_flags: !ctx->is_enc); |
557 | dst_buf->flags |= V4L2_BUF_FLAG_LAST; |
558 | v4l2_m2m_buf_done(buf: dst_buf, state: VB2_BUF_STATE_DONE); |
559 | } |
560 | |
561 | static int job_ready(void *priv) |
562 | { |
563 | static const u8 magic[] = { |
564 | 0x4f, 0x4f, 0x4f, 0x4f, 0xff, 0xff, 0xff, 0xff |
565 | }; |
566 | struct vicodec_ctx *ctx = priv; |
567 | struct vb2_v4l2_buffer *src_buf; |
568 | u8 *p_src; |
569 | u8 *p; |
570 | u32 sz; |
571 | u32 state; |
572 | struct vicodec_q_data *q_dst = get_q_data(ctx, |
573 | type: V4L2_BUF_TYPE_VIDEO_CAPTURE); |
574 | unsigned int flags; |
575 | unsigned int hdr_width_div; |
576 | unsigned int hdr_height_div; |
577 | unsigned int max_to_copy; |
578 | unsigned int comp_frame_size; |
579 | |
580 | if (ctx->source_changed) |
581 | return 0; |
582 | if (ctx->is_stateless || ctx->is_enc || ctx->comp_has_frame) |
583 | return 1; |
584 | |
585 | restart: |
586 | ctx->comp_has_next_frame = false; |
587 | src_buf = v4l2_m2m_next_src_buf(m2m_ctx: ctx->fh.m2m_ctx); |
588 | if (!src_buf) |
589 | return 0; |
590 | p_src = vb2_plane_vaddr(vb: &src_buf->vb2_buf, plane_no: 0); |
591 | sz = vb2_get_plane_payload(vb: &src_buf->vb2_buf, plane_no: 0); |
592 | p = p_src + ctx->cur_buf_offset; |
593 | |
594 | state = VB2_BUF_STATE_DONE; |
595 | |
596 | if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { |
597 | state = get_next_header(ctx, pp: &p, sz: p_src + sz - p); |
598 | if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { |
599 | if (v4l2_m2m_is_last_draining_src_buf(m2m_ctx: ctx->fh.m2m_ctx, |
600 | vbuf: src_buf)) |
601 | return 1; |
602 | job_remove_src_buf(ctx, state); |
603 | goto restart; |
604 | } |
605 | } |
606 | |
607 | comp_frame_size = ntohl(ctx->state.header.size); |
608 | |
609 | /* |
610 | * The current scanned frame might be the first frame of a new |
611 | * resolution so its size might be larger than ctx->comp_max_size. |
612 | * In that case it is copied up to the current buffer capacity and |
613 | * the copy will continue after allocating new large enough buffer |
614 | * when restreaming |
615 | */ |
616 | max_to_copy = min(comp_frame_size, ctx->comp_max_size); |
617 | |
618 | if (ctx->comp_size < max_to_copy) { |
619 | u32 copy = max_to_copy - ctx->comp_size; |
620 | |
621 | if (copy > p_src + sz - p) |
622 | copy = p_src + sz - p; |
623 | |
624 | memcpy(ctx->state.compressed_frame + ctx->comp_size, |
625 | p, copy); |
626 | p += copy; |
627 | ctx->comp_size += copy; |
628 | if (ctx->comp_size < max_to_copy) { |
629 | if (v4l2_m2m_is_last_draining_src_buf(m2m_ctx: ctx->fh.m2m_ctx, |
630 | vbuf: src_buf)) |
631 | return 1; |
632 | job_remove_src_buf(ctx, state); |
633 | goto restart; |
634 | } |
635 | } |
636 | ctx->cur_buf_offset = p - p_src; |
637 | if (ctx->comp_size == comp_frame_size) |
638 | ctx->comp_has_frame = true; |
639 | ctx->comp_has_next_frame = false; |
640 | if (ctx->comp_has_frame && sz - ctx->cur_buf_offset >= |
641 | sizeof(struct fwht_cframe_hdr)) { |
642 | struct fwht_cframe_hdr *p_hdr = (struct fwht_cframe_hdr *)p; |
643 | u32 frame_size = ntohl(p_hdr->size); |
644 | u32 remaining = sz - ctx->cur_buf_offset - sizeof(*p_hdr); |
645 | |
646 | if (!memcmp(p, q: magic, size: sizeof(magic))) |
647 | ctx->comp_has_next_frame = remaining >= frame_size; |
648 | } |
649 | /* |
650 | * if the header is invalid the device_run will just drop the frame |
651 | * with an error |
652 | */ |
653 | if (!is_header_valid(p_hdr: &ctx->state.header) && ctx->comp_has_frame) |
654 | return 1; |
655 | flags = ntohl(ctx->state.header.flags); |
656 | hdr_width_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; |
657 | hdr_height_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; |
658 | |
659 | if (ntohl(ctx->state.header.width) != q_dst->visible_width || |
660 | ntohl(ctx->state.header.height) != q_dst->visible_height || |
661 | !q_dst->info || |
662 | hdr_width_div != q_dst->info->width_div || |
663 | hdr_height_div != q_dst->info->height_div) { |
664 | static const struct v4l2_event rs_event = { |
665 | .type = V4L2_EVENT_SOURCE_CHANGE, |
666 | .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, |
667 | }; |
668 | |
669 | struct vb2_v4l2_buffer *dst_buf = |
670 | v4l2_m2m_dst_buf_remove(m2m_ctx: ctx->fh.m2m_ctx); |
671 | |
672 | update_capture_data_from_header(ctx); |
673 | v4l2_event_queue_fh(fh: &ctx->fh, ev: &rs_event); |
674 | set_last_buffer(dst_buf, src_buf, ctx); |
675 | ctx->source_changed = true; |
676 | return 0; |
677 | } |
678 | return 1; |
679 | } |
680 | |
681 | /* |
682 | * video ioctls |
683 | */ |
684 | |
685 | static const struct v4l2_fwht_pixfmt_info *find_fmt(u32 fmt) |
686 | { |
687 | const struct v4l2_fwht_pixfmt_info *info = |
688 | v4l2_fwht_find_pixfmt(pixelformat: fmt); |
689 | |
690 | if (!info) |
691 | info = v4l2_fwht_get_pixfmt(idx: 0); |
692 | return info; |
693 | } |
694 | |
695 | static int vidioc_querycap(struct file *file, void *priv, |
696 | struct v4l2_capability *cap) |
697 | { |
698 | strscpy(cap->driver, VICODEC_NAME, sizeof(cap->driver)); |
699 | strscpy(cap->card, VICODEC_NAME, sizeof(cap->card)); |
700 | snprintf(buf: cap->bus_info, size: sizeof(cap->bus_info), |
701 | fmt: "platform:%s" , VICODEC_NAME); |
702 | return 0; |
703 | } |
704 | |
705 | static int enum_fmt(struct v4l2_fmtdesc *f, struct vicodec_ctx *ctx, |
706 | bool is_out) |
707 | { |
708 | bool is_uncomp = (ctx->is_enc && is_out) || (!ctx->is_enc && !is_out); |
709 | |
710 | if (V4L2_TYPE_IS_MULTIPLANAR(f->type) && !multiplanar) |
711 | return -EINVAL; |
712 | if (!V4L2_TYPE_IS_MULTIPLANAR(f->type) && multiplanar) |
713 | return -EINVAL; |
714 | |
715 | if (is_uncomp) { |
716 | const struct v4l2_fwht_pixfmt_info *info = |
717 | get_q_data(ctx, type: f->type)->info; |
718 | |
719 | if (ctx->is_enc || |
720 | !vb2_is_streaming(q: &ctx->fh.m2m_ctx->cap_q_ctx.q)) |
721 | info = v4l2_fwht_get_pixfmt(idx: f->index); |
722 | else |
723 | info = v4l2_fwht_find_nth_fmt(width_div: info->width_div, |
724 | height_div: info->height_div, |
725 | components_num: info->components_num, |
726 | pixenc: info->pixenc, |
727 | start_idx: f->index); |
728 | if (!info) |
729 | return -EINVAL; |
730 | f->pixelformat = info->id; |
731 | } else { |
732 | if (f->index) |
733 | return -EINVAL; |
734 | f->pixelformat = ctx->is_stateless ? |
735 | V4L2_PIX_FMT_FWHT_STATELESS : V4L2_PIX_FMT_FWHT; |
736 | if (!ctx->is_enc && !ctx->is_stateless) |
737 | f->flags = V4L2_FMT_FLAG_DYN_RESOLUTION | |
738 | V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM; |
739 | } |
740 | return 0; |
741 | } |
742 | |
743 | static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, |
744 | struct v4l2_fmtdesc *f) |
745 | { |
746 | struct vicodec_ctx *ctx = file2ctx(file); |
747 | |
748 | return enum_fmt(f, ctx, is_out: false); |
749 | } |
750 | |
751 | static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, |
752 | struct v4l2_fmtdesc *f) |
753 | { |
754 | struct vicodec_ctx *ctx = file2ctx(file); |
755 | |
756 | return enum_fmt(f, ctx, is_out: true); |
757 | } |
758 | |
759 | static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) |
760 | { |
761 | struct vb2_queue *vq; |
762 | struct vicodec_q_data *q_data; |
763 | struct v4l2_pix_format_mplane *pix_mp; |
764 | struct v4l2_pix_format *pix; |
765 | const struct v4l2_fwht_pixfmt_info *info; |
766 | |
767 | vq = v4l2_m2m_get_vq(m2m_ctx: ctx->fh.m2m_ctx, type: f->type); |
768 | if (!vq) |
769 | return -EINVAL; |
770 | |
771 | q_data = get_q_data(ctx, type: f->type); |
772 | info = q_data->info; |
773 | |
774 | switch (f->type) { |
775 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
776 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: |
777 | if (multiplanar) |
778 | return -EINVAL; |
779 | pix = &f->fmt.pix; |
780 | pix->width = q_data->coded_width; |
781 | pix->height = q_data->coded_height; |
782 | pix->field = V4L2_FIELD_NONE; |
783 | pix->pixelformat = info->id; |
784 | pix->bytesperline = q_data->coded_width * |
785 | info->bytesperline_mult; |
786 | pix->sizeimage = q_data->sizeimage; |
787 | pix->colorspace = ctx->state.colorspace; |
788 | pix->xfer_func = ctx->state.xfer_func; |
789 | pix->ycbcr_enc = ctx->state.ycbcr_enc; |
790 | pix->quantization = ctx->state.quantization; |
791 | break; |
792 | |
793 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: |
794 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: |
795 | if (!multiplanar) |
796 | return -EINVAL; |
797 | pix_mp = &f->fmt.pix_mp; |
798 | pix_mp->width = q_data->coded_width; |
799 | pix_mp->height = q_data->coded_height; |
800 | pix_mp->field = V4L2_FIELD_NONE; |
801 | pix_mp->pixelformat = info->id; |
802 | pix_mp->num_planes = 1; |
803 | pix_mp->plane_fmt[0].bytesperline = |
804 | q_data->coded_width * info->bytesperline_mult; |
805 | pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage; |
806 | pix_mp->colorspace = ctx->state.colorspace; |
807 | pix_mp->xfer_func = ctx->state.xfer_func; |
808 | pix_mp->ycbcr_enc = ctx->state.ycbcr_enc; |
809 | pix_mp->quantization = ctx->state.quantization; |
810 | break; |
811 | default: |
812 | return -EINVAL; |
813 | } |
814 | return 0; |
815 | } |
816 | |
817 | static int vidioc_g_fmt_vid_out(struct file *file, void *priv, |
818 | struct v4l2_format *f) |
819 | { |
820 | return vidioc_g_fmt(ctx: file2ctx(file), f); |
821 | } |
822 | |
823 | static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, |
824 | struct v4l2_format *f) |
825 | { |
826 | return vidioc_g_fmt(ctx: file2ctx(file), f); |
827 | } |
828 | |
829 | static int vidioc_try_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) |
830 | { |
831 | struct v4l2_pix_format_mplane *pix_mp; |
832 | struct v4l2_pix_format *pix; |
833 | struct v4l2_plane_pix_format *plane; |
834 | const struct v4l2_fwht_pixfmt_info *info = ctx->is_stateless ? |
835 | &pixfmt_stateless_fwht : &pixfmt_fwht; |
836 | |
837 | switch (f->type) { |
838 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
839 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: |
840 | pix = &f->fmt.pix; |
841 | if (pix->pixelformat != V4L2_PIX_FMT_FWHT && |
842 | pix->pixelformat != V4L2_PIX_FMT_FWHT_STATELESS) |
843 | info = find_fmt(fmt: pix->pixelformat); |
844 | |
845 | pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); |
846 | pix->width = vic_round_dim(pix->width, info->width_div); |
847 | |
848 | pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); |
849 | pix->height = vic_round_dim(pix->height, info->height_div); |
850 | |
851 | pix->field = V4L2_FIELD_NONE; |
852 | pix->bytesperline = |
853 | pix->width * info->bytesperline_mult; |
854 | pix->sizeimage = pix->width * pix->height * |
855 | info->sizeimage_mult / info->sizeimage_div; |
856 | if (pix->pixelformat == V4L2_PIX_FMT_FWHT) |
857 | pix->sizeimage += sizeof(struct fwht_cframe_hdr); |
858 | break; |
859 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: |
860 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: |
861 | pix_mp = &f->fmt.pix_mp; |
862 | plane = pix_mp->plane_fmt; |
863 | if (pix_mp->pixelformat != V4L2_PIX_FMT_FWHT && |
864 | pix_mp->pixelformat != V4L2_PIX_FMT_FWHT_STATELESS) |
865 | info = find_fmt(fmt: pix_mp->pixelformat); |
866 | pix_mp->num_planes = 1; |
867 | |
868 | pix_mp->width = clamp(pix_mp->width, MIN_WIDTH, MAX_WIDTH); |
869 | pix_mp->width = vic_round_dim(pix_mp->width, info->width_div); |
870 | |
871 | pix_mp->height = clamp(pix_mp->height, MIN_HEIGHT, MAX_HEIGHT); |
872 | pix_mp->height = vic_round_dim(pix_mp->height, |
873 | info->height_div); |
874 | |
875 | pix_mp->field = V4L2_FIELD_NONE; |
876 | plane->bytesperline = |
877 | pix_mp->width * info->bytesperline_mult; |
878 | plane->sizeimage = pix_mp->width * pix_mp->height * |
879 | info->sizeimage_mult / info->sizeimage_div; |
880 | if (pix_mp->pixelformat == V4L2_PIX_FMT_FWHT) |
881 | plane->sizeimage += sizeof(struct fwht_cframe_hdr); |
882 | break; |
883 | default: |
884 | return -EINVAL; |
885 | } |
886 | |
887 | return 0; |
888 | } |
889 | |
890 | static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, |
891 | struct v4l2_format *f) |
892 | { |
893 | struct vicodec_ctx *ctx = file2ctx(file); |
894 | struct v4l2_pix_format_mplane *pix_mp; |
895 | struct v4l2_pix_format *pix; |
896 | |
897 | switch (f->type) { |
898 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
899 | if (multiplanar) |
900 | return -EINVAL; |
901 | pix = &f->fmt.pix; |
902 | pix->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT : |
903 | find_fmt(fmt: f->fmt.pix.pixelformat)->id; |
904 | pix->colorspace = ctx->state.colorspace; |
905 | pix->xfer_func = ctx->state.xfer_func; |
906 | pix->ycbcr_enc = ctx->state.ycbcr_enc; |
907 | pix->quantization = ctx->state.quantization; |
908 | break; |
909 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: |
910 | if (!multiplanar) |
911 | return -EINVAL; |
912 | pix_mp = &f->fmt.pix_mp; |
913 | pix_mp->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT : |
914 | find_fmt(fmt: pix_mp->pixelformat)->id; |
915 | pix_mp->colorspace = ctx->state.colorspace; |
916 | pix_mp->xfer_func = ctx->state.xfer_func; |
917 | pix_mp->ycbcr_enc = ctx->state.ycbcr_enc; |
918 | pix_mp->quantization = ctx->state.quantization; |
919 | break; |
920 | default: |
921 | return -EINVAL; |
922 | } |
923 | |
924 | return vidioc_try_fmt(ctx, f); |
925 | } |
926 | |
927 | static int vidioc_try_fmt_vid_out(struct file *file, void *priv, |
928 | struct v4l2_format *f) |
929 | { |
930 | struct vicodec_ctx *ctx = file2ctx(file); |
931 | struct v4l2_pix_format_mplane *pix_mp; |
932 | struct v4l2_pix_format *pix; |
933 | |
934 | switch (f->type) { |
935 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: |
936 | if (multiplanar) |
937 | return -EINVAL; |
938 | pix = &f->fmt.pix; |
939 | if (ctx->is_enc) |
940 | pix->pixelformat = find_fmt(fmt: pix->pixelformat)->id; |
941 | else if (ctx->is_stateless) |
942 | pix->pixelformat = V4L2_PIX_FMT_FWHT_STATELESS; |
943 | else |
944 | pix->pixelformat = V4L2_PIX_FMT_FWHT; |
945 | if (!pix->colorspace) |
946 | pix->colorspace = V4L2_COLORSPACE_REC709; |
947 | break; |
948 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: |
949 | if (!multiplanar) |
950 | return -EINVAL; |
951 | pix_mp = &f->fmt.pix_mp; |
952 | if (ctx->is_enc) |
953 | pix_mp->pixelformat = find_fmt(fmt: pix_mp->pixelformat)->id; |
954 | else if (ctx->is_stateless) |
955 | pix_mp->pixelformat = V4L2_PIX_FMT_FWHT_STATELESS; |
956 | else |
957 | pix_mp->pixelformat = V4L2_PIX_FMT_FWHT; |
958 | if (!pix_mp->colorspace) |
959 | pix_mp->colorspace = V4L2_COLORSPACE_REC709; |
960 | break; |
961 | default: |
962 | return -EINVAL; |
963 | } |
964 | |
965 | return vidioc_try_fmt(ctx, f); |
966 | } |
967 | |
968 | static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) |
969 | { |
970 | struct vicodec_q_data *q_data; |
971 | struct vb2_queue *vq; |
972 | bool fmt_changed = true; |
973 | struct v4l2_pix_format_mplane *pix_mp; |
974 | struct v4l2_pix_format *pix; |
975 | |
976 | vq = v4l2_m2m_get_vq(m2m_ctx: ctx->fh.m2m_ctx, type: f->type); |
977 | if (!vq) |
978 | return -EINVAL; |
979 | |
980 | q_data = get_q_data(ctx, type: f->type); |
981 | if (!q_data) |
982 | return -EINVAL; |
983 | |
984 | switch (f->type) { |
985 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
986 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: |
987 | pix = &f->fmt.pix; |
988 | if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) |
989 | fmt_changed = |
990 | !q_data->info || |
991 | q_data->info->id != pix->pixelformat || |
992 | q_data->coded_width != pix->width || |
993 | q_data->coded_height != pix->height; |
994 | |
995 | if (vb2_is_busy(q: vq) && fmt_changed) |
996 | return -EBUSY; |
997 | |
998 | if (pix->pixelformat == V4L2_PIX_FMT_FWHT) |
999 | q_data->info = &pixfmt_fwht; |
1000 | else if (pix->pixelformat == V4L2_PIX_FMT_FWHT_STATELESS) |
1001 | q_data->info = &pixfmt_stateless_fwht; |
1002 | else |
1003 | q_data->info = find_fmt(fmt: pix->pixelformat); |
1004 | q_data->coded_width = pix->width; |
1005 | q_data->coded_height = pix->height; |
1006 | q_data->sizeimage = pix->sizeimage; |
1007 | break; |
1008 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: |
1009 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: |
1010 | pix_mp = &f->fmt.pix_mp; |
1011 | if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) |
1012 | fmt_changed = |
1013 | !q_data->info || |
1014 | q_data->info->id != pix_mp->pixelformat || |
1015 | q_data->coded_width != pix_mp->width || |
1016 | q_data->coded_height != pix_mp->height; |
1017 | |
1018 | if (vb2_is_busy(q: vq) && fmt_changed) |
1019 | return -EBUSY; |
1020 | |
1021 | if (pix_mp->pixelformat == V4L2_PIX_FMT_FWHT) |
1022 | q_data->info = &pixfmt_fwht; |
1023 | else if (pix_mp->pixelformat == V4L2_PIX_FMT_FWHT_STATELESS) |
1024 | q_data->info = &pixfmt_stateless_fwht; |
1025 | else |
1026 | q_data->info = find_fmt(fmt: pix_mp->pixelformat); |
1027 | q_data->coded_width = pix_mp->width; |
1028 | q_data->coded_height = pix_mp->height; |
1029 | q_data->sizeimage = pix_mp->plane_fmt[0].sizeimage; |
1030 | break; |
1031 | default: |
1032 | return -EINVAL; |
1033 | } |
1034 | |
1035 | dprintk(ctx->dev, |
1036 | "Setting format for type %d, coded wxh: %dx%d, fourcc: 0x%08x\n" , |
1037 | f->type, q_data->coded_width, q_data->coded_height, |
1038 | q_data->info->id); |
1039 | |
1040 | return 0; |
1041 | } |
1042 | |
1043 | static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, |
1044 | struct v4l2_format *f) |
1045 | { |
1046 | int ret; |
1047 | |
1048 | ret = vidioc_try_fmt_vid_cap(file, priv, f); |
1049 | if (ret) |
1050 | return ret; |
1051 | |
1052 | return vidioc_s_fmt(ctx: file2ctx(file), f); |
1053 | } |
1054 | |
1055 | static int vidioc_s_fmt_vid_out(struct file *file, void *priv, |
1056 | struct v4l2_format *f) |
1057 | { |
1058 | struct vicodec_ctx *ctx = file2ctx(file); |
1059 | struct vicodec_q_data *q_data; |
1060 | struct vicodec_q_data *q_data_cap; |
1061 | struct v4l2_pix_format *pix; |
1062 | struct v4l2_pix_format_mplane *pix_mp; |
1063 | u32 coded_w = 0, coded_h = 0; |
1064 | unsigned int size = 0; |
1065 | int ret; |
1066 | |
1067 | q_data = get_q_data(ctx, type: f->type); |
1068 | q_data_cap = get_q_data(ctx, type: V4L2_BUF_TYPE_VIDEO_CAPTURE); |
1069 | |
1070 | ret = vidioc_try_fmt_vid_out(file, priv, f); |
1071 | if (ret) |
1072 | return ret; |
1073 | |
1074 | if (ctx->is_enc) { |
1075 | struct vb2_queue *vq = v4l2_m2m_get_vq(m2m_ctx: ctx->fh.m2m_ctx, type: f->type); |
1076 | struct vb2_queue *vq_cap = v4l2_m2m_get_vq(m2m_ctx: ctx->fh.m2m_ctx, |
1077 | type: V4L2_BUF_TYPE_VIDEO_CAPTURE); |
1078 | const struct v4l2_fwht_pixfmt_info *info = ctx->is_stateless ? |
1079 | &pixfmt_stateless_fwht : &pixfmt_fwht; |
1080 | |
1081 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { |
1082 | coded_w = f->fmt.pix.width; |
1083 | coded_h = f->fmt.pix.height; |
1084 | } else { |
1085 | coded_w = f->fmt.pix_mp.width; |
1086 | coded_h = f->fmt.pix_mp.height; |
1087 | } |
1088 | if (vb2_is_busy(q: vq) && (coded_w != q_data->coded_width || |
1089 | coded_h != q_data->coded_height)) |
1090 | return -EBUSY; |
1091 | size = coded_w * coded_h * |
1092 | info->sizeimage_mult / info->sizeimage_div; |
1093 | if (!ctx->is_stateless) |
1094 | size += sizeof(struct fwht_cframe_hdr); |
1095 | |
1096 | if (vb2_is_busy(q: vq_cap) && size > q_data_cap->sizeimage) |
1097 | return -EBUSY; |
1098 | } |
1099 | |
1100 | ret = vidioc_s_fmt(ctx: file2ctx(file), f); |
1101 | if (!ret) { |
1102 | if (ctx->is_enc) { |
1103 | q_data->visible_width = coded_w; |
1104 | q_data->visible_height = coded_h; |
1105 | q_data_cap->coded_width = coded_w; |
1106 | q_data_cap->coded_height = coded_h; |
1107 | q_data_cap->sizeimage = size; |
1108 | } |
1109 | |
1110 | switch (f->type) { |
1111 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: |
1112 | pix = &f->fmt.pix; |
1113 | ctx->state.colorspace = pix->colorspace; |
1114 | ctx->state.xfer_func = pix->xfer_func; |
1115 | ctx->state.ycbcr_enc = pix->ycbcr_enc; |
1116 | ctx->state.quantization = pix->quantization; |
1117 | break; |
1118 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: |
1119 | pix_mp = &f->fmt.pix_mp; |
1120 | ctx->state.colorspace = pix_mp->colorspace; |
1121 | ctx->state.xfer_func = pix_mp->xfer_func; |
1122 | ctx->state.ycbcr_enc = pix_mp->ycbcr_enc; |
1123 | ctx->state.quantization = pix_mp->quantization; |
1124 | break; |
1125 | default: |
1126 | break; |
1127 | } |
1128 | } |
1129 | return ret; |
1130 | } |
1131 | |
1132 | static int vidioc_g_selection(struct file *file, void *priv, |
1133 | struct v4l2_selection *s) |
1134 | { |
1135 | struct vicodec_ctx *ctx = file2ctx(file); |
1136 | struct vicodec_q_data *q_data; |
1137 | |
1138 | q_data = get_q_data(ctx, type: s->type); |
1139 | if (!q_data) |
1140 | return -EINVAL; |
1141 | /* |
1142 | * encoder supports only cropping on the OUTPUT buffer |
1143 | * decoder supports only composing on the CAPTURE buffer |
1144 | */ |
1145 | if (ctx->is_enc && s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { |
1146 | switch (s->target) { |
1147 | case V4L2_SEL_TGT_CROP: |
1148 | s->r.left = 0; |
1149 | s->r.top = 0; |
1150 | s->r.width = q_data->visible_width; |
1151 | s->r.height = q_data->visible_height; |
1152 | return 0; |
1153 | case V4L2_SEL_TGT_CROP_DEFAULT: |
1154 | case V4L2_SEL_TGT_CROP_BOUNDS: |
1155 | s->r.left = 0; |
1156 | s->r.top = 0; |
1157 | s->r.width = q_data->coded_width; |
1158 | s->r.height = q_data->coded_height; |
1159 | return 0; |
1160 | } |
1161 | } else if (!ctx->is_enc && s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { |
1162 | switch (s->target) { |
1163 | case V4L2_SEL_TGT_COMPOSE: |
1164 | s->r.left = 0; |
1165 | s->r.top = 0; |
1166 | s->r.width = q_data->visible_width; |
1167 | s->r.height = q_data->visible_height; |
1168 | return 0; |
1169 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: |
1170 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: |
1171 | s->r.left = 0; |
1172 | s->r.top = 0; |
1173 | s->r.width = q_data->coded_width; |
1174 | s->r.height = q_data->coded_height; |
1175 | return 0; |
1176 | } |
1177 | } |
1178 | return -EINVAL; |
1179 | } |
1180 | |
1181 | static int vidioc_s_selection(struct file *file, void *priv, |
1182 | struct v4l2_selection *s) |
1183 | { |
1184 | struct vicodec_ctx *ctx = file2ctx(file); |
1185 | struct vicodec_q_data *q_data; |
1186 | |
1187 | if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) |
1188 | return -EINVAL; |
1189 | |
1190 | q_data = get_q_data(ctx, type: s->type); |
1191 | if (!q_data) |
1192 | return -EINVAL; |
1193 | |
1194 | if (!ctx->is_enc || s->target != V4L2_SEL_TGT_CROP) |
1195 | return -EINVAL; |
1196 | |
1197 | s->r.left = 0; |
1198 | s->r.top = 0; |
1199 | q_data->visible_width = clamp(s->r.width, MIN_WIDTH, |
1200 | q_data->coded_width); |
1201 | s->r.width = q_data->visible_width; |
1202 | q_data->visible_height = clamp(s->r.height, MIN_HEIGHT, |
1203 | q_data->coded_height); |
1204 | s->r.height = q_data->visible_height; |
1205 | return 0; |
1206 | } |
1207 | |
1208 | static int vicodec_encoder_cmd(struct file *file, void *fh, |
1209 | struct v4l2_encoder_cmd *ec) |
1210 | { |
1211 | struct vicodec_ctx *ctx = file2ctx(file); |
1212 | int ret; |
1213 | |
1214 | ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); |
1215 | if (ret < 0) |
1216 | return ret; |
1217 | |
1218 | if (!vb2_is_streaming(q: &ctx->fh.m2m_ctx->cap_q_ctx.q) || |
1219 | !vb2_is_streaming(q: &ctx->fh.m2m_ctx->out_q_ctx.q)) |
1220 | return 0; |
1221 | |
1222 | ret = v4l2_m2m_ioctl_encoder_cmd(file, fh, ec); |
1223 | if (ret < 0) |
1224 | return ret; |
1225 | |
1226 | if (ec->cmd == V4L2_ENC_CMD_STOP && |
1227 | v4l2_m2m_has_stopped(m2m_ctx: ctx->fh.m2m_ctx)) |
1228 | v4l2_event_queue_fh(fh: &ctx->fh, ev: &vicodec_eos_event); |
1229 | |
1230 | if (ec->cmd == V4L2_ENC_CMD_START && |
1231 | v4l2_m2m_has_stopped(m2m_ctx: ctx->fh.m2m_ctx)) |
1232 | vb2_clear_last_buffer_dequeued(q: &ctx->fh.m2m_ctx->cap_q_ctx.q); |
1233 | |
1234 | return 0; |
1235 | } |
1236 | |
1237 | static int vicodec_decoder_cmd(struct file *file, void *fh, |
1238 | struct v4l2_decoder_cmd *dc) |
1239 | { |
1240 | struct vicodec_ctx *ctx = file2ctx(file); |
1241 | int ret; |
1242 | |
1243 | /* |
1244 | * This ioctl should not be used with a stateless codec that doesn't |
1245 | * support holding buffers and the associated flush command. |
1246 | */ |
1247 | WARN_ON(ctx->is_stateless); |
1248 | |
1249 | ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); |
1250 | if (ret < 0) |
1251 | return ret; |
1252 | |
1253 | if (!vb2_is_streaming(q: &ctx->fh.m2m_ctx->cap_q_ctx.q) || |
1254 | !vb2_is_streaming(q: &ctx->fh.m2m_ctx->out_q_ctx.q)) |
1255 | return 0; |
1256 | |
1257 | ret = v4l2_m2m_ioctl_decoder_cmd(file, fh, dc); |
1258 | if (ret < 0) |
1259 | return ret; |
1260 | |
1261 | if (dc->cmd == V4L2_DEC_CMD_STOP && |
1262 | v4l2_m2m_has_stopped(m2m_ctx: ctx->fh.m2m_ctx)) |
1263 | v4l2_event_queue_fh(fh: &ctx->fh, ev: &vicodec_eos_event); |
1264 | |
1265 | if (dc->cmd == V4L2_DEC_CMD_START && |
1266 | v4l2_m2m_has_stopped(m2m_ctx: ctx->fh.m2m_ctx)) |
1267 | vb2_clear_last_buffer_dequeued(q: &ctx->fh.m2m_ctx->cap_q_ctx.q); |
1268 | |
1269 | return 0; |
1270 | } |
1271 | |
1272 | static int vicodec_enum_framesizes(struct file *file, void *fh, |
1273 | struct v4l2_frmsizeenum *fsize) |
1274 | { |
1275 | switch (fsize->pixel_format) { |
1276 | case V4L2_PIX_FMT_FWHT_STATELESS: |
1277 | break; |
1278 | case V4L2_PIX_FMT_FWHT: |
1279 | break; |
1280 | default: |
1281 | if (find_fmt(fmt: fsize->pixel_format)->id == fsize->pixel_format) |
1282 | break; |
1283 | return -EINVAL; |
1284 | } |
1285 | |
1286 | if (fsize->index) |
1287 | return -EINVAL; |
1288 | |
1289 | fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; |
1290 | |
1291 | fsize->stepwise.min_width = MIN_WIDTH; |
1292 | fsize->stepwise.max_width = MAX_WIDTH; |
1293 | fsize->stepwise.step_width = 8; |
1294 | fsize->stepwise.min_height = MIN_HEIGHT; |
1295 | fsize->stepwise.max_height = MAX_HEIGHT; |
1296 | fsize->stepwise.step_height = 8; |
1297 | |
1298 | return 0; |
1299 | } |
1300 | |
1301 | static int vicodec_subscribe_event(struct v4l2_fh *fh, |
1302 | const struct v4l2_event_subscription *sub) |
1303 | { |
1304 | struct vicodec_ctx *ctx = container_of(fh, struct vicodec_ctx, fh); |
1305 | |
1306 | switch (sub->type) { |
1307 | case V4L2_EVENT_SOURCE_CHANGE: |
1308 | if (ctx->is_enc) |
1309 | return -EINVAL; |
1310 | fallthrough; |
1311 | case V4L2_EVENT_EOS: |
1312 | if (ctx->is_stateless) |
1313 | return -EINVAL; |
1314 | return v4l2_event_subscribe(fh, sub, elems: 0, NULL); |
1315 | default: |
1316 | return v4l2_ctrl_subscribe_event(fh, sub); |
1317 | } |
1318 | } |
1319 | |
1320 | static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { |
1321 | .vidioc_querycap = vidioc_querycap, |
1322 | |
1323 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, |
1324 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, |
1325 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, |
1326 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, |
1327 | |
1328 | .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap, |
1329 | .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap, |
1330 | .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap, |
1331 | |
1332 | .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, |
1333 | .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, |
1334 | .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, |
1335 | .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, |
1336 | |
1337 | .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out, |
1338 | .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out, |
1339 | .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out, |
1340 | |
1341 | .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, |
1342 | .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, |
1343 | .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, |
1344 | .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, |
1345 | .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, |
1346 | .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, |
1347 | .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, |
1348 | |
1349 | .vidioc_streamon = v4l2_m2m_ioctl_streamon, |
1350 | .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, |
1351 | |
1352 | .vidioc_g_selection = vidioc_g_selection, |
1353 | .vidioc_s_selection = vidioc_s_selection, |
1354 | |
1355 | .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, |
1356 | .vidioc_encoder_cmd = vicodec_encoder_cmd, |
1357 | .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, |
1358 | .vidioc_decoder_cmd = vicodec_decoder_cmd, |
1359 | .vidioc_enum_framesizes = vicodec_enum_framesizes, |
1360 | |
1361 | .vidioc_subscribe_event = vicodec_subscribe_event, |
1362 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, |
1363 | }; |
1364 | |
1365 | |
1366 | /* |
1367 | * Queue operations |
1368 | */ |
1369 | |
1370 | static int vicodec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, |
1371 | unsigned int *nplanes, unsigned int sizes[], |
1372 | struct device *alloc_devs[]) |
1373 | { |
1374 | struct vicodec_ctx *ctx = vb2_get_drv_priv(q: vq); |
1375 | struct vicodec_q_data *q_data = get_q_data(ctx, type: vq->type); |
1376 | unsigned int size = q_data->sizeimage; |
1377 | |
1378 | if (*nplanes) |
1379 | return sizes[0] < size ? -EINVAL : 0; |
1380 | |
1381 | *nplanes = 1; |
1382 | sizes[0] = size; |
1383 | q_data->vb2_sizeimage = size; |
1384 | return 0; |
1385 | } |
1386 | |
1387 | static int vicodec_buf_out_validate(struct vb2_buffer *vb) |
1388 | { |
1389 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
1390 | |
1391 | vbuf->field = V4L2_FIELD_NONE; |
1392 | return 0; |
1393 | } |
1394 | |
1395 | static int vicodec_buf_prepare(struct vb2_buffer *vb) |
1396 | { |
1397 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
1398 | struct vicodec_ctx *ctx = vb2_get_drv_priv(q: vb->vb2_queue); |
1399 | struct vicodec_q_data *q_data; |
1400 | |
1401 | dprintk(ctx->dev, "type: %d\n" , vb->vb2_queue->type); |
1402 | |
1403 | q_data = get_q_data(ctx, type: vb->vb2_queue->type); |
1404 | if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { |
1405 | if (vbuf->field == V4L2_FIELD_ANY) |
1406 | vbuf->field = V4L2_FIELD_NONE; |
1407 | if (vbuf->field != V4L2_FIELD_NONE) { |
1408 | dprintk(ctx->dev, "%s field isn't supported\n" , |
1409 | __func__); |
1410 | return -EINVAL; |
1411 | } |
1412 | } |
1413 | |
1414 | if (vb2_plane_size(vb, plane_no: 0) < q_data->vb2_sizeimage) { |
1415 | dprintk(ctx->dev, |
1416 | "%s data will not fit into plane (%lu < %lu)\n" , |
1417 | __func__, vb2_plane_size(vb, 0), |
1418 | (long)q_data->vb2_sizeimage); |
1419 | return -EINVAL; |
1420 | } |
1421 | |
1422 | return 0; |
1423 | } |
1424 | |
1425 | static void vicodec_buf_queue(struct vb2_buffer *vb) |
1426 | { |
1427 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
1428 | struct vicodec_ctx *ctx = vb2_get_drv_priv(q: vb->vb2_queue); |
1429 | unsigned int sz = vb2_get_plane_payload(vb: &vbuf->vb2_buf, plane_no: 0); |
1430 | u8 *p_src = vb2_plane_vaddr(vb: &vbuf->vb2_buf, plane_no: 0); |
1431 | u8 *p = p_src; |
1432 | struct vb2_queue *vq_out = v4l2_m2m_get_vq(m2m_ctx: ctx->fh.m2m_ctx, |
1433 | type: V4L2_BUF_TYPE_VIDEO_OUTPUT); |
1434 | struct vb2_queue *vq_cap = v4l2_m2m_get_vq(m2m_ctx: ctx->fh.m2m_ctx, |
1435 | type: V4L2_BUF_TYPE_VIDEO_CAPTURE); |
1436 | bool = false; |
1437 | static const struct v4l2_event rs_event = { |
1438 | .type = V4L2_EVENT_SOURCE_CHANGE, |
1439 | .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, |
1440 | }; |
1441 | |
1442 | if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && |
1443 | vb2_is_streaming(q: vb->vb2_queue) && |
1444 | v4l2_m2m_dst_buf_is_last(m2m_ctx: ctx->fh.m2m_ctx)) { |
1445 | unsigned int i; |
1446 | |
1447 | for (i = 0; i < vb->num_planes; i++) |
1448 | vb2_set_plane_payload(vb, plane_no: i, size: 0); |
1449 | |
1450 | vbuf->field = V4L2_FIELD_NONE; |
1451 | vbuf->sequence = |
1452 | get_q_data(ctx, type: vb->vb2_queue->type)->sequence++; |
1453 | |
1454 | v4l2_m2m_last_buffer_done(m2m_ctx: ctx->fh.m2m_ctx, vbuf); |
1455 | v4l2_event_queue_fh(fh: &ctx->fh, ev: &vicodec_eos_event); |
1456 | return; |
1457 | } |
1458 | |
1459 | /* buf_queue handles only the first source change event */ |
1460 | if (ctx->first_source_change_sent) { |
1461 | v4l2_m2m_buf_queue(m2m_ctx: ctx->fh.m2m_ctx, vbuf); |
1462 | return; |
1463 | } |
1464 | |
1465 | /* |
1466 | * if both queues are streaming, the source change event is |
1467 | * handled in job_ready |
1468 | */ |
1469 | if (vb2_is_streaming(q: vq_cap) && vb2_is_streaming(q: vq_out)) { |
1470 | v4l2_m2m_buf_queue(m2m_ctx: ctx->fh.m2m_ctx, vbuf); |
1471 | return; |
1472 | } |
1473 | |
1474 | /* |
1475 | * source change event is relevant only for the stateful decoder |
1476 | * in the compressed stream |
1477 | */ |
1478 | if (ctx->is_stateless || ctx->is_enc || |
1479 | V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { |
1480 | v4l2_m2m_buf_queue(m2m_ctx: ctx->fh.m2m_ctx, vbuf); |
1481 | return; |
1482 | } |
1483 | |
1484 | do { |
1485 | enum vb2_buffer_state state = |
1486 | get_next_header(ctx, pp: &p, sz: p_src + sz - p); |
1487 | |
1488 | if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { |
1489 | v4l2_m2m_buf_done(buf: vbuf, state); |
1490 | return; |
1491 | } |
1492 | header_valid = is_header_valid(p_hdr: &ctx->state.header); |
1493 | /* |
1494 | * p points right after the end of the header in the |
1495 | * buffer. If the header is invalid we set p to point |
1496 | * to the next byte after the start of the header |
1497 | */ |
1498 | if (!header_valid) { |
1499 | p = p - sizeof(struct fwht_cframe_hdr) + 1; |
1500 | if (p < p_src) |
1501 | p = p_src; |
1502 | ctx->header_size = 0; |
1503 | ctx->comp_magic_cnt = 0; |
1504 | } |
1505 | |
1506 | } while (!header_valid); |
1507 | |
1508 | ctx->cur_buf_offset = p - p_src; |
1509 | update_capture_data_from_header(ctx); |
1510 | ctx->first_source_change_sent = true; |
1511 | v4l2_event_queue_fh(fh: &ctx->fh, ev: &rs_event); |
1512 | v4l2_m2m_buf_queue(m2m_ctx: ctx->fh.m2m_ctx, vbuf); |
1513 | } |
1514 | |
1515 | static void vicodec_return_bufs(struct vb2_queue *q, u32 state) |
1516 | { |
1517 | struct vicodec_ctx *ctx = vb2_get_drv_priv(q); |
1518 | struct vb2_v4l2_buffer *vbuf; |
1519 | |
1520 | for (;;) { |
1521 | if (V4L2_TYPE_IS_OUTPUT(q->type)) |
1522 | vbuf = v4l2_m2m_src_buf_remove(m2m_ctx: ctx->fh.m2m_ctx); |
1523 | else |
1524 | vbuf = v4l2_m2m_dst_buf_remove(m2m_ctx: ctx->fh.m2m_ctx); |
1525 | if (vbuf == NULL) |
1526 | return; |
1527 | v4l2_ctrl_request_complete(req: vbuf->vb2_buf.req_obj.req, |
1528 | parent: &ctx->hdl); |
1529 | spin_lock(lock: ctx->lock); |
1530 | v4l2_m2m_buf_done(buf: vbuf, state); |
1531 | spin_unlock(lock: ctx->lock); |
1532 | } |
1533 | } |
1534 | |
1535 | static unsigned int total_frame_size(struct vicodec_q_data *q_data) |
1536 | { |
1537 | unsigned int size; |
1538 | unsigned int chroma_div; |
1539 | |
1540 | if (!q_data->info) { |
1541 | WARN_ON(1); |
1542 | return 0; |
1543 | } |
1544 | size = q_data->coded_width * q_data->coded_height; |
1545 | chroma_div = q_data->info->width_div * q_data->info->height_div; |
1546 | |
1547 | if (q_data->info->components_num == 4) |
1548 | return 2 * size + 2 * (size / chroma_div); |
1549 | else if (q_data->info->components_num == 3) |
1550 | return size + 2 * (size / chroma_div); |
1551 | return size; |
1552 | } |
1553 | |
1554 | static int vicodec_start_streaming(struct vb2_queue *q, |
1555 | unsigned int count) |
1556 | { |
1557 | struct vicodec_ctx *ctx = vb2_get_drv_priv(q); |
1558 | struct vicodec_q_data *q_data = get_q_data(ctx, type: q->type); |
1559 | struct v4l2_fwht_state *state = &ctx->state; |
1560 | const struct v4l2_fwht_pixfmt_info *info = q_data->info; |
1561 | unsigned int size = q_data->coded_width * q_data->coded_height; |
1562 | unsigned int chroma_div; |
1563 | unsigned int total_planes_size; |
1564 | u8 *new_comp_frame = NULL; |
1565 | |
1566 | chroma_div = info->width_div * info->height_div; |
1567 | q_data->sequence = 0; |
1568 | |
1569 | v4l2_m2m_update_start_streaming_state(m2m_ctx: ctx->fh.m2m_ctx, q); |
1570 | |
1571 | state->gop_cnt = 0; |
1572 | |
1573 | if ((V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || |
1574 | (V4L2_TYPE_IS_CAPTURE(q->type) && ctx->is_enc)) |
1575 | return 0; |
1576 | |
1577 | if (info->id == V4L2_PIX_FMT_FWHT || |
1578 | info->id == V4L2_PIX_FMT_FWHT_STATELESS) { |
1579 | vicodec_return_bufs(q, state: VB2_BUF_STATE_QUEUED); |
1580 | return -EINVAL; |
1581 | } |
1582 | total_planes_size = total_frame_size(q_data); |
1583 | ctx->comp_max_size = total_planes_size; |
1584 | |
1585 | state->visible_width = q_data->visible_width; |
1586 | state->visible_height = q_data->visible_height; |
1587 | state->coded_width = q_data->coded_width; |
1588 | state->coded_height = q_data->coded_height; |
1589 | state->stride = q_data->coded_width * |
1590 | info->bytesperline_mult; |
1591 | |
1592 | if (ctx->is_stateless) { |
1593 | state->ref_stride = state->stride; |
1594 | return 0; |
1595 | } |
1596 | state->ref_stride = q_data->coded_width * info->luma_alpha_step; |
1597 | |
1598 | state->ref_frame.buf = kvmalloc(size: total_planes_size, GFP_KERNEL); |
1599 | state->ref_frame.luma = state->ref_frame.buf; |
1600 | new_comp_frame = kvmalloc(size: ctx->comp_max_size, GFP_KERNEL); |
1601 | |
1602 | if (!state->ref_frame.luma || !new_comp_frame) { |
1603 | kvfree(addr: state->ref_frame.luma); |
1604 | kvfree(addr: new_comp_frame); |
1605 | vicodec_return_bufs(q, state: VB2_BUF_STATE_QUEUED); |
1606 | return -ENOMEM; |
1607 | } |
1608 | /* |
1609 | * if state->compressed_frame was already allocated then |
1610 | * it contain data of the first frame of the new resolution |
1611 | */ |
1612 | if (state->compressed_frame) { |
1613 | if (ctx->comp_size > ctx->comp_max_size) |
1614 | ctx->comp_size = ctx->comp_max_size; |
1615 | |
1616 | memcpy(new_comp_frame, |
1617 | state->compressed_frame, ctx->comp_size); |
1618 | } |
1619 | |
1620 | kvfree(addr: state->compressed_frame); |
1621 | state->compressed_frame = new_comp_frame; |
1622 | |
1623 | if (info->components_num < 3) { |
1624 | state->ref_frame.cb = NULL; |
1625 | state->ref_frame.cr = NULL; |
1626 | state->ref_frame.alpha = NULL; |
1627 | return 0; |
1628 | } |
1629 | |
1630 | state->ref_frame.cb = state->ref_frame.luma + size; |
1631 | state->ref_frame.cr = state->ref_frame.cb + size / chroma_div; |
1632 | |
1633 | if (info->components_num == 4) |
1634 | state->ref_frame.alpha = |
1635 | state->ref_frame.cr + size / chroma_div; |
1636 | else |
1637 | state->ref_frame.alpha = NULL; |
1638 | |
1639 | return 0; |
1640 | } |
1641 | |
1642 | static void vicodec_stop_streaming(struct vb2_queue *q) |
1643 | { |
1644 | struct vicodec_ctx *ctx = vb2_get_drv_priv(q); |
1645 | |
1646 | vicodec_return_bufs(q, state: VB2_BUF_STATE_ERROR); |
1647 | |
1648 | v4l2_m2m_update_stop_streaming_state(m2m_ctx: ctx->fh.m2m_ctx, q); |
1649 | |
1650 | if (V4L2_TYPE_IS_OUTPUT(q->type) && |
1651 | v4l2_m2m_has_stopped(m2m_ctx: ctx->fh.m2m_ctx)) |
1652 | v4l2_event_queue_fh(fh: &ctx->fh, ev: &vicodec_eos_event); |
1653 | |
1654 | if (!ctx->is_enc && V4L2_TYPE_IS_OUTPUT(q->type)) |
1655 | ctx->first_source_change_sent = false; |
1656 | |
1657 | if ((!V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || |
1658 | (V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) { |
1659 | if (!ctx->is_stateless) |
1660 | kvfree(addr: ctx->state.ref_frame.buf); |
1661 | ctx->state.ref_frame.buf = NULL; |
1662 | ctx->state.ref_frame.luma = NULL; |
1663 | ctx->comp_max_size = 0; |
1664 | ctx->source_changed = false; |
1665 | } |
1666 | if (V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) { |
1667 | ctx->cur_buf_offset = 0; |
1668 | ctx->comp_size = 0; |
1669 | ctx->header_size = 0; |
1670 | ctx->comp_magic_cnt = 0; |
1671 | ctx->comp_has_frame = false; |
1672 | ctx->comp_has_next_frame = false; |
1673 | } |
1674 | } |
1675 | |
1676 | static void vicodec_buf_request_complete(struct vb2_buffer *vb) |
1677 | { |
1678 | struct vicodec_ctx *ctx = vb2_get_drv_priv(q: vb->vb2_queue); |
1679 | |
1680 | v4l2_ctrl_request_complete(req: vb->req_obj.req, parent: &ctx->hdl); |
1681 | } |
1682 | |
1683 | |
1684 | static const struct vb2_ops vicodec_qops = { |
1685 | .queue_setup = vicodec_queue_setup, |
1686 | .buf_out_validate = vicodec_buf_out_validate, |
1687 | .buf_prepare = vicodec_buf_prepare, |
1688 | .buf_queue = vicodec_buf_queue, |
1689 | .buf_request_complete = vicodec_buf_request_complete, |
1690 | .start_streaming = vicodec_start_streaming, |
1691 | .stop_streaming = vicodec_stop_streaming, |
1692 | .wait_prepare = vb2_ops_wait_prepare, |
1693 | .wait_finish = vb2_ops_wait_finish, |
1694 | }; |
1695 | |
1696 | static int queue_init(void *priv, struct vb2_queue *src_vq, |
1697 | struct vb2_queue *dst_vq) |
1698 | { |
1699 | struct vicodec_ctx *ctx = priv; |
1700 | int ret; |
1701 | |
1702 | src_vq->type = (multiplanar ? |
1703 | V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : |
1704 | V4L2_BUF_TYPE_VIDEO_OUTPUT); |
1705 | src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; |
1706 | src_vq->drv_priv = ctx; |
1707 | src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
1708 | src_vq->ops = &vicodec_qops; |
1709 | src_vq->mem_ops = &vb2_vmalloc_memops; |
1710 | src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; |
1711 | if (ctx->is_enc) |
1712 | src_vq->lock = &ctx->dev->stateful_enc.mutex; |
1713 | else if (ctx->is_stateless) |
1714 | src_vq->lock = &ctx->dev->stateless_dec.mutex; |
1715 | else |
1716 | src_vq->lock = &ctx->dev->stateful_dec.mutex; |
1717 | src_vq->supports_requests = ctx->is_stateless; |
1718 | src_vq->requires_requests = ctx->is_stateless; |
1719 | ret = vb2_queue_init(q: src_vq); |
1720 | if (ret) |
1721 | return ret; |
1722 | |
1723 | dst_vq->type = (multiplanar ? |
1724 | V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : |
1725 | V4L2_BUF_TYPE_VIDEO_CAPTURE); |
1726 | dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; |
1727 | dst_vq->max_num_buffers = 64; |
1728 | dst_vq->drv_priv = ctx; |
1729 | dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
1730 | dst_vq->ops = &vicodec_qops; |
1731 | dst_vq->mem_ops = &vb2_vmalloc_memops; |
1732 | dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; |
1733 | dst_vq->lock = src_vq->lock; |
1734 | |
1735 | return vb2_queue_init(q: dst_vq); |
1736 | } |
1737 | |
1738 | static int vicodec_try_ctrl(struct v4l2_ctrl *ctrl) |
1739 | { |
1740 | struct vicodec_ctx *ctx = container_of(ctrl->handler, |
1741 | struct vicodec_ctx, hdl); |
1742 | const struct v4l2_ctrl_fwht_params *params; |
1743 | struct vicodec_q_data *q_dst = get_q_data(ctx, |
1744 | type: V4L2_BUF_TYPE_VIDEO_CAPTURE); |
1745 | |
1746 | switch (ctrl->id) { |
1747 | case V4L2_CID_STATELESS_FWHT_PARAMS: |
1748 | if (!q_dst->info) |
1749 | return -EINVAL; |
1750 | params = ctrl->p_new.p_fwht_params; |
1751 | if (params->width > q_dst->coded_width || |
1752 | params->width < MIN_WIDTH || |
1753 | params->height > q_dst->coded_height || |
1754 | params->height < MIN_HEIGHT) |
1755 | return -EINVAL; |
1756 | if (!validate_by_version(flags: params->flags, version: params->version)) |
1757 | return -EINVAL; |
1758 | if (!validate_stateless_params_flags(params, cur_info: q_dst->info)) |
1759 | return -EINVAL; |
1760 | return 0; |
1761 | default: |
1762 | return 0; |
1763 | } |
1764 | return 0; |
1765 | } |
1766 | |
1767 | static void (struct vicodec_ctx *ctx, |
1768 | const struct v4l2_ctrl_fwht_params *params) |
1769 | { |
1770 | struct fwht_cframe_hdr *p_hdr = &ctx->state.header; |
1771 | |
1772 | p_hdr->magic1 = FWHT_MAGIC1; |
1773 | p_hdr->magic2 = FWHT_MAGIC2; |
1774 | p_hdr->version = htonl(params->version); |
1775 | p_hdr->width = htonl(params->width); |
1776 | p_hdr->height = htonl(params->height); |
1777 | p_hdr->flags = htonl(params->flags); |
1778 | p_hdr->colorspace = htonl(params->colorspace); |
1779 | p_hdr->xfer_func = htonl(params->xfer_func); |
1780 | p_hdr->ycbcr_enc = htonl(params->ycbcr_enc); |
1781 | p_hdr->quantization = htonl(params->quantization); |
1782 | } |
1783 | |
1784 | static int vicodec_s_ctrl(struct v4l2_ctrl *ctrl) |
1785 | { |
1786 | struct vicodec_ctx *ctx = container_of(ctrl->handler, |
1787 | struct vicodec_ctx, hdl); |
1788 | const struct v4l2_ctrl_fwht_params *params; |
1789 | |
1790 | switch (ctrl->id) { |
1791 | case V4L2_CID_MPEG_VIDEO_GOP_SIZE: |
1792 | ctx->state.gop_size = ctrl->val; |
1793 | return 0; |
1794 | case V4L2_CID_FWHT_I_FRAME_QP: |
1795 | ctx->state.i_frame_qp = ctrl->val; |
1796 | return 0; |
1797 | case V4L2_CID_FWHT_P_FRAME_QP: |
1798 | ctx->state.p_frame_qp = ctrl->val; |
1799 | return 0; |
1800 | case V4L2_CID_STATELESS_FWHT_PARAMS: |
1801 | params = ctrl->p_new.p_fwht_params; |
1802 | update_header_from_stateless_params(ctx, params); |
1803 | ctx->state.ref_frame_ts = params->backward_ref_ts; |
1804 | return 0; |
1805 | } |
1806 | return -EINVAL; |
1807 | } |
1808 | |
1809 | static const struct v4l2_ctrl_ops vicodec_ctrl_ops = { |
1810 | .s_ctrl = vicodec_s_ctrl, |
1811 | .try_ctrl = vicodec_try_ctrl, |
1812 | }; |
1813 | |
1814 | static const struct v4l2_ctrl_config vicodec_ctrl_stateless_state = { |
1815 | .ops = &vicodec_ctrl_ops, |
1816 | .id = V4L2_CID_STATELESS_FWHT_PARAMS, |
1817 | .elem_size = sizeof(struct v4l2_ctrl_fwht_params), |
1818 | }; |
1819 | |
1820 | /* |
1821 | * File operations |
1822 | */ |
1823 | static int vicodec_open(struct file *file) |
1824 | { |
1825 | const struct v4l2_fwht_pixfmt_info *info = v4l2_fwht_get_pixfmt(idx: 0); |
1826 | struct video_device *vfd = video_devdata(file); |
1827 | struct vicodec_dev *dev = video_drvdata(file); |
1828 | struct vicodec_ctx *ctx = NULL; |
1829 | struct v4l2_ctrl_handler *hdl; |
1830 | unsigned int raw_size; |
1831 | unsigned int comp_size; |
1832 | int rc = 0; |
1833 | |
1834 | if (mutex_lock_interruptible(vfd->lock)) |
1835 | return -ERESTARTSYS; |
1836 | ctx = kzalloc(size: sizeof(*ctx), GFP_KERNEL); |
1837 | if (!ctx) { |
1838 | rc = -ENOMEM; |
1839 | goto open_unlock; |
1840 | } |
1841 | |
1842 | if (vfd == &dev->stateful_enc.vfd) |
1843 | ctx->is_enc = true; |
1844 | else if (vfd == &dev->stateless_dec.vfd) |
1845 | ctx->is_stateless = true; |
1846 | |
1847 | v4l2_fh_init(fh: &ctx->fh, vdev: video_devdata(file)); |
1848 | file->private_data = &ctx->fh; |
1849 | ctx->dev = dev; |
1850 | hdl = &ctx->hdl; |
1851 | v4l2_ctrl_handler_init(hdl, 5); |
1852 | v4l2_ctrl_new_std(hdl, ops: &vicodec_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, |
1853 | min: 1, max: 16, step: 1, def: 10); |
1854 | v4l2_ctrl_new_std(hdl, ops: &vicodec_ctrl_ops, V4L2_CID_FWHT_I_FRAME_QP, |
1855 | min: 1, max: 31, step: 1, def: 20); |
1856 | v4l2_ctrl_new_std(hdl, ops: &vicodec_ctrl_ops, V4L2_CID_FWHT_P_FRAME_QP, |
1857 | min: 1, max: 31, step: 1, def: 20); |
1858 | if (ctx->is_enc) |
1859 | v4l2_ctrl_new_std(hdl, ops: &vicodec_ctrl_ops, |
1860 | V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, min: 1, max: 1, step: 1, def: 1); |
1861 | if (ctx->is_stateless) |
1862 | v4l2_ctrl_new_custom(hdl, cfg: &vicodec_ctrl_stateless_state, NULL); |
1863 | if (hdl->error) { |
1864 | rc = hdl->error; |
1865 | v4l2_ctrl_handler_free(hdl); |
1866 | kfree(objp: ctx); |
1867 | goto open_unlock; |
1868 | } |
1869 | ctx->fh.ctrl_handler = hdl; |
1870 | v4l2_ctrl_handler_setup(hdl); |
1871 | |
1872 | if (ctx->is_enc) |
1873 | ctx->q_data[V4L2_M2M_SRC].info = info; |
1874 | else if (ctx->is_stateless) |
1875 | ctx->q_data[V4L2_M2M_SRC].info = &pixfmt_stateless_fwht; |
1876 | else |
1877 | ctx->q_data[V4L2_M2M_SRC].info = &pixfmt_fwht; |
1878 | ctx->q_data[V4L2_M2M_SRC].coded_width = 1280; |
1879 | ctx->q_data[V4L2_M2M_SRC].coded_height = 720; |
1880 | ctx->q_data[V4L2_M2M_SRC].visible_width = 1280; |
1881 | ctx->q_data[V4L2_M2M_SRC].visible_height = 720; |
1882 | raw_size = 1280 * 720 * info->sizeimage_mult / info->sizeimage_div; |
1883 | comp_size = 1280 * 720 * pixfmt_fwht.sizeimage_mult / |
1884 | pixfmt_fwht.sizeimage_div; |
1885 | if (ctx->is_enc) |
1886 | ctx->q_data[V4L2_M2M_SRC].sizeimage = raw_size; |
1887 | else if (ctx->is_stateless) |
1888 | ctx->q_data[V4L2_M2M_SRC].sizeimage = comp_size; |
1889 | else |
1890 | ctx->q_data[V4L2_M2M_SRC].sizeimage = |
1891 | comp_size + sizeof(struct fwht_cframe_hdr); |
1892 | ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; |
1893 | if (ctx->is_enc) { |
1894 | ctx->q_data[V4L2_M2M_DST].info = &pixfmt_fwht; |
1895 | ctx->q_data[V4L2_M2M_DST].sizeimage = |
1896 | comp_size + sizeof(struct fwht_cframe_hdr); |
1897 | } else { |
1898 | ctx->q_data[V4L2_M2M_DST].info = info; |
1899 | ctx->q_data[V4L2_M2M_DST].sizeimage = raw_size; |
1900 | } |
1901 | |
1902 | ctx->state.colorspace = V4L2_COLORSPACE_REC709; |
1903 | |
1904 | if (ctx->is_enc) { |
1905 | ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(m2m_dev: dev->stateful_enc.m2m_dev, |
1906 | drv_priv: ctx, queue_init: &queue_init); |
1907 | ctx->lock = &dev->stateful_enc.lock; |
1908 | } else if (ctx->is_stateless) { |
1909 | ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(m2m_dev: dev->stateless_dec.m2m_dev, |
1910 | drv_priv: ctx, queue_init: &queue_init); |
1911 | ctx->lock = &dev->stateless_dec.lock; |
1912 | } else { |
1913 | ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(m2m_dev: dev->stateful_dec.m2m_dev, |
1914 | drv_priv: ctx, queue_init: &queue_init); |
1915 | ctx->lock = &dev->stateful_dec.lock; |
1916 | } |
1917 | |
1918 | if (IS_ERR(ptr: ctx->fh.m2m_ctx)) { |
1919 | rc = PTR_ERR(ptr: ctx->fh.m2m_ctx); |
1920 | |
1921 | v4l2_ctrl_handler_free(hdl); |
1922 | v4l2_fh_exit(fh: &ctx->fh); |
1923 | kfree(objp: ctx); |
1924 | goto open_unlock; |
1925 | } |
1926 | |
1927 | v4l2_fh_add(fh: &ctx->fh); |
1928 | |
1929 | open_unlock: |
1930 | mutex_unlock(lock: vfd->lock); |
1931 | return rc; |
1932 | } |
1933 | |
1934 | static int vicodec_release(struct file *file) |
1935 | { |
1936 | struct video_device *vfd = video_devdata(file); |
1937 | struct vicodec_ctx *ctx = file2ctx(file); |
1938 | |
1939 | mutex_lock(vfd->lock); |
1940 | v4l2_m2m_ctx_release(m2m_ctx: ctx->fh.m2m_ctx); |
1941 | mutex_unlock(lock: vfd->lock); |
1942 | v4l2_fh_del(fh: &ctx->fh); |
1943 | v4l2_fh_exit(fh: &ctx->fh); |
1944 | v4l2_ctrl_handler_free(hdl: &ctx->hdl); |
1945 | kvfree(addr: ctx->state.compressed_frame); |
1946 | kfree(objp: ctx); |
1947 | |
1948 | return 0; |
1949 | } |
1950 | |
1951 | static int vicodec_request_validate(struct media_request *req) |
1952 | { |
1953 | struct media_request_object *obj; |
1954 | struct v4l2_ctrl_handler *parent_hdl, *hdl; |
1955 | struct vicodec_ctx *ctx = NULL; |
1956 | struct v4l2_ctrl *ctrl; |
1957 | unsigned int count; |
1958 | |
1959 | list_for_each_entry(obj, &req->objects, list) { |
1960 | struct vb2_buffer *vb; |
1961 | |
1962 | if (vb2_request_object_is_buffer(obj)) { |
1963 | vb = container_of(obj, struct vb2_buffer, req_obj); |
1964 | ctx = vb2_get_drv_priv(q: vb->vb2_queue); |
1965 | |
1966 | break; |
1967 | } |
1968 | } |
1969 | |
1970 | if (!ctx) { |
1971 | pr_err("No buffer was provided with the request\n" ); |
1972 | return -ENOENT; |
1973 | } |
1974 | |
1975 | count = vb2_request_buffer_cnt(req); |
1976 | if (!count) { |
1977 | v4l2_info(&ctx->dev->v4l2_dev, |
1978 | "No buffer was provided with the request\n" ); |
1979 | return -ENOENT; |
1980 | } else if (count > 1) { |
1981 | v4l2_info(&ctx->dev->v4l2_dev, |
1982 | "More than one buffer was provided with the request\n" ); |
1983 | return -EINVAL; |
1984 | } |
1985 | |
1986 | parent_hdl = &ctx->hdl; |
1987 | |
1988 | hdl = v4l2_ctrl_request_hdl_find(req, parent: parent_hdl); |
1989 | if (!hdl) { |
1990 | v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control\n" ); |
1991 | return -ENOENT; |
1992 | } |
1993 | ctrl = v4l2_ctrl_request_hdl_ctrl_find(hdl, |
1994 | id: vicodec_ctrl_stateless_state.id); |
1995 | v4l2_ctrl_request_hdl_put(hdl); |
1996 | if (!ctrl) { |
1997 | v4l2_info(&ctx->dev->v4l2_dev, |
1998 | "Missing required codec control\n" ); |
1999 | return -ENOENT; |
2000 | } |
2001 | |
2002 | return vb2_request_validate(req); |
2003 | } |
2004 | |
2005 | static const struct v4l2_file_operations vicodec_fops = { |
2006 | .owner = THIS_MODULE, |
2007 | .open = vicodec_open, |
2008 | .release = vicodec_release, |
2009 | .poll = v4l2_m2m_fop_poll, |
2010 | .unlocked_ioctl = video_ioctl2, |
2011 | .mmap = v4l2_m2m_fop_mmap, |
2012 | }; |
2013 | |
2014 | static const struct video_device vicodec_videodev = { |
2015 | .name = VICODEC_NAME, |
2016 | .vfl_dir = VFL_DIR_M2M, |
2017 | .fops = &vicodec_fops, |
2018 | .ioctl_ops = &vicodec_ioctl_ops, |
2019 | .minor = -1, |
2020 | .release = video_device_release_empty, |
2021 | }; |
2022 | |
2023 | static const struct media_device_ops vicodec_m2m_media_ops = { |
2024 | .req_validate = vicodec_request_validate, |
2025 | .req_queue = v4l2_m2m_request_queue, |
2026 | }; |
2027 | |
2028 | static const struct v4l2_m2m_ops m2m_ops = { |
2029 | .device_run = device_run, |
2030 | .job_ready = job_ready, |
2031 | }; |
2032 | |
2033 | static int register_instance(struct vicodec_dev *dev, |
2034 | struct vicodec_dev_instance *dev_instance, |
2035 | const char *name, bool is_enc, bool is_stateless) |
2036 | { |
2037 | struct video_device *vfd; |
2038 | int ret; |
2039 | |
2040 | spin_lock_init(&dev_instance->lock); |
2041 | mutex_init(&dev_instance->mutex); |
2042 | dev_instance->m2m_dev = v4l2_m2m_init(m2m_ops: &m2m_ops); |
2043 | if (IS_ERR(ptr: dev_instance->m2m_dev)) { |
2044 | v4l2_err(&dev->v4l2_dev, "Failed to init vicodec enc device\n" ); |
2045 | return PTR_ERR(ptr: dev_instance->m2m_dev); |
2046 | } |
2047 | |
2048 | dev_instance->vfd = vicodec_videodev; |
2049 | vfd = &dev_instance->vfd; |
2050 | vfd->lock = &dev_instance->mutex; |
2051 | vfd->v4l2_dev = &dev->v4l2_dev; |
2052 | strscpy(vfd->name, name, sizeof(vfd->name)); |
2053 | vfd->device_caps = V4L2_CAP_STREAMING | |
2054 | (multiplanar ? V4L2_CAP_VIDEO_M2M_MPLANE : V4L2_CAP_VIDEO_M2M); |
2055 | if (is_enc || is_stateless) { |
2056 | v4l2_disable_ioctl(vdev: vfd, VIDIOC_DECODER_CMD); |
2057 | v4l2_disable_ioctl(vdev: vfd, VIDIOC_TRY_DECODER_CMD); |
2058 | } |
2059 | if (!is_enc) { |
2060 | v4l2_disable_ioctl(vdev: vfd, VIDIOC_ENCODER_CMD); |
2061 | v4l2_disable_ioctl(vdev: vfd, VIDIOC_TRY_ENCODER_CMD); |
2062 | } |
2063 | video_set_drvdata(vdev: vfd, data: dev); |
2064 | |
2065 | ret = video_register_device(vdev: vfd, type: VFL_TYPE_VIDEO, nr: 0); |
2066 | if (ret) { |
2067 | v4l2_err(&dev->v4l2_dev, "Failed to register video device '%s'\n" , name); |
2068 | v4l2_m2m_release(m2m_dev: dev_instance->m2m_dev); |
2069 | return ret; |
2070 | } |
2071 | v4l2_info(&dev->v4l2_dev, "Device '%s' registered as /dev/video%d\n" , |
2072 | name, vfd->num); |
2073 | return 0; |
2074 | } |
2075 | |
2076 | static void vicodec_v4l2_dev_release(struct v4l2_device *v4l2_dev) |
2077 | { |
2078 | struct vicodec_dev *dev = container_of(v4l2_dev, struct vicodec_dev, v4l2_dev); |
2079 | |
2080 | v4l2_device_unregister(v4l2_dev: &dev->v4l2_dev); |
2081 | v4l2_m2m_release(m2m_dev: dev->stateful_enc.m2m_dev); |
2082 | v4l2_m2m_release(m2m_dev: dev->stateful_dec.m2m_dev); |
2083 | v4l2_m2m_release(m2m_dev: dev->stateless_dec.m2m_dev); |
2084 | #ifdef CONFIG_MEDIA_CONTROLLER |
2085 | media_device_cleanup(mdev: &dev->mdev); |
2086 | #endif |
2087 | kfree(objp: dev); |
2088 | } |
2089 | |
2090 | static int vicodec_probe(struct platform_device *pdev) |
2091 | { |
2092 | struct vicodec_dev *dev; |
2093 | int ret; |
2094 | |
2095 | dev = kzalloc(size: sizeof(*dev), GFP_KERNEL); |
2096 | if (!dev) |
2097 | return -ENOMEM; |
2098 | |
2099 | ret = v4l2_device_register(dev: &pdev->dev, v4l2_dev: &dev->v4l2_dev); |
2100 | if (ret) |
2101 | goto free_dev; |
2102 | |
2103 | dev->v4l2_dev.release = vicodec_v4l2_dev_release; |
2104 | |
2105 | #ifdef CONFIG_MEDIA_CONTROLLER |
2106 | dev->mdev.dev = &pdev->dev; |
2107 | strscpy(dev->mdev.model, "vicodec" , sizeof(dev->mdev.model)); |
2108 | strscpy(dev->mdev.bus_info, "platform:vicodec" , |
2109 | sizeof(dev->mdev.bus_info)); |
2110 | media_device_init(mdev: &dev->mdev); |
2111 | dev->mdev.ops = &vicodec_m2m_media_ops; |
2112 | dev->v4l2_dev.mdev = &dev->mdev; |
2113 | #endif |
2114 | |
2115 | platform_set_drvdata(pdev, data: dev); |
2116 | |
2117 | ret = register_instance(dev, dev_instance: &dev->stateful_enc, name: "stateful-encoder" , |
2118 | is_enc: true, is_stateless: false); |
2119 | if (ret) |
2120 | goto unreg_dev; |
2121 | |
2122 | ret = register_instance(dev, dev_instance: &dev->stateful_dec, name: "stateful-decoder" , |
2123 | is_enc: false, is_stateless: false); |
2124 | if (ret) |
2125 | goto unreg_sf_enc; |
2126 | |
2127 | ret = register_instance(dev, dev_instance: &dev->stateless_dec, name: "stateless-decoder" , |
2128 | is_enc: false, is_stateless: true); |
2129 | if (ret) |
2130 | goto unreg_sf_dec; |
2131 | |
2132 | #ifdef CONFIG_MEDIA_CONTROLLER |
2133 | ret = v4l2_m2m_register_media_controller(m2m_dev: dev->stateful_enc.m2m_dev, |
2134 | vdev: &dev->stateful_enc.vfd, |
2135 | MEDIA_ENT_F_PROC_VIDEO_ENCODER); |
2136 | if (ret) { |
2137 | v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller for enc\n" ); |
2138 | goto unreg_m2m; |
2139 | } |
2140 | |
2141 | ret = v4l2_m2m_register_media_controller(m2m_dev: dev->stateful_dec.m2m_dev, |
2142 | vdev: &dev->stateful_dec.vfd, |
2143 | MEDIA_ENT_F_PROC_VIDEO_DECODER); |
2144 | if (ret) { |
2145 | v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller for dec\n" ); |
2146 | goto unreg_m2m_sf_enc_mc; |
2147 | } |
2148 | |
2149 | ret = v4l2_m2m_register_media_controller(m2m_dev: dev->stateless_dec.m2m_dev, |
2150 | vdev: &dev->stateless_dec.vfd, |
2151 | MEDIA_ENT_F_PROC_VIDEO_DECODER); |
2152 | if (ret) { |
2153 | v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller for stateless dec\n" ); |
2154 | goto unreg_m2m_sf_dec_mc; |
2155 | } |
2156 | |
2157 | ret = media_device_register(&dev->mdev); |
2158 | if (ret) { |
2159 | v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n" ); |
2160 | goto unreg_m2m_sl_dec_mc; |
2161 | } |
2162 | #endif |
2163 | return 0; |
2164 | |
2165 | #ifdef CONFIG_MEDIA_CONTROLLER |
2166 | unreg_m2m_sl_dec_mc: |
2167 | v4l2_m2m_unregister_media_controller(m2m_dev: dev->stateless_dec.m2m_dev); |
2168 | unreg_m2m_sf_dec_mc: |
2169 | v4l2_m2m_unregister_media_controller(m2m_dev: dev->stateful_dec.m2m_dev); |
2170 | unreg_m2m_sf_enc_mc: |
2171 | v4l2_m2m_unregister_media_controller(m2m_dev: dev->stateful_enc.m2m_dev); |
2172 | unreg_m2m: |
2173 | video_unregister_device(vdev: &dev->stateless_dec.vfd); |
2174 | v4l2_m2m_release(m2m_dev: dev->stateless_dec.m2m_dev); |
2175 | #endif |
2176 | unreg_sf_dec: |
2177 | video_unregister_device(vdev: &dev->stateful_dec.vfd); |
2178 | v4l2_m2m_release(m2m_dev: dev->stateful_dec.m2m_dev); |
2179 | unreg_sf_enc: |
2180 | video_unregister_device(vdev: &dev->stateful_enc.vfd); |
2181 | v4l2_m2m_release(m2m_dev: dev->stateful_enc.m2m_dev); |
2182 | unreg_dev: |
2183 | v4l2_device_unregister(v4l2_dev: &dev->v4l2_dev); |
2184 | free_dev: |
2185 | kfree(objp: dev); |
2186 | |
2187 | return ret; |
2188 | } |
2189 | |
2190 | static void vicodec_remove(struct platform_device *pdev) |
2191 | { |
2192 | struct vicodec_dev *dev = platform_get_drvdata(pdev); |
2193 | |
2194 | v4l2_info(&dev->v4l2_dev, "Removing " VICODEC_NAME); |
2195 | |
2196 | #ifdef CONFIG_MEDIA_CONTROLLER |
2197 | media_device_unregister(mdev: &dev->mdev); |
2198 | v4l2_m2m_unregister_media_controller(m2m_dev: dev->stateful_enc.m2m_dev); |
2199 | v4l2_m2m_unregister_media_controller(m2m_dev: dev->stateful_dec.m2m_dev); |
2200 | v4l2_m2m_unregister_media_controller(m2m_dev: dev->stateless_dec.m2m_dev); |
2201 | #endif |
2202 | |
2203 | video_unregister_device(vdev: &dev->stateful_enc.vfd); |
2204 | video_unregister_device(vdev: &dev->stateful_dec.vfd); |
2205 | video_unregister_device(vdev: &dev->stateless_dec.vfd); |
2206 | v4l2_device_put(v4l2_dev: &dev->v4l2_dev); |
2207 | } |
2208 | |
2209 | static struct platform_driver vicodec_pdrv = { |
2210 | .probe = vicodec_probe, |
2211 | .remove_new = vicodec_remove, |
2212 | .driver = { |
2213 | .name = VICODEC_NAME, |
2214 | }, |
2215 | }; |
2216 | |
2217 | static void __exit vicodec_exit(void) |
2218 | { |
2219 | platform_driver_unregister(&vicodec_pdrv); |
2220 | platform_device_unregister(&vicodec_pdev); |
2221 | } |
2222 | |
2223 | static int __init vicodec_init(void) |
2224 | { |
2225 | int ret; |
2226 | |
2227 | ret = platform_device_register(&vicodec_pdev); |
2228 | if (ret) |
2229 | return ret; |
2230 | |
2231 | ret = platform_driver_register(&vicodec_pdrv); |
2232 | if (ret) |
2233 | platform_device_unregister(&vicodec_pdev); |
2234 | |
2235 | return ret; |
2236 | } |
2237 | |
2238 | module_init(vicodec_init); |
2239 | module_exit(vicodec_exit); |
2240 | |