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