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 | 86 | unsigned int sizeimage; |
8307f0ab | 87 | unsigned int vb2_sizeimage; |
256bf813 | 88 | unsigned int sequence; |
cd12b401 | 89 | const struct v4l2_fwht_pixfmt_info *info; |
256bf813 HV |
90 | }; |
91 | ||
92 | enum { | |
93 | V4L2_M2M_SRC = 0, | |
94 | V4L2_M2M_DST = 1, | |
95 | }; | |
96 | ||
c022a4a9 DH |
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 | ||
256bf813 HV |
104 | struct vicodec_dev { |
105 | struct v4l2_device v4l2_dev; | |
c022a4a9 DH |
106 | struct vicodec_dev_instance stateful_enc; |
107 | struct vicodec_dev_instance stateful_dec; | |
fde649b4 | 108 | struct vicodec_dev_instance stateless_dec; |
256bf813 HV |
109 | #ifdef CONFIG_MEDIA_CONTROLLER |
110 | struct media_device mdev; | |
111 | #endif | |
112 | ||
256bf813 HV |
113 | }; |
114 | ||
115 | struct vicodec_ctx { | |
116 | struct v4l2_fh fh; | |
117 | struct vicodec_dev *dev; | |
118 | bool is_enc; | |
fde649b4 | 119 | bool is_stateless; |
256bf813 HV |
120 | spinlock_t *lock; |
121 | ||
122 | struct v4l2_ctrl_handler hdl; | |
256bf813 | 123 | |
256bf813 HV |
124 | /* Source and destination queue data */ |
125 | struct vicodec_q_data q_data[2]; | |
cd12b401 HV |
126 | struct v4l2_fwht_state state; |
127 | ||
256bf813 HV |
128 | u32 cur_buf_offset; |
129 | u32 comp_max_size; | |
130 | u32 comp_size; | |
ddc1b085 | 131 | u32 header_size; |
256bf813 | 132 | u32 comp_magic_cnt; |
256bf813 HV |
133 | bool comp_has_frame; |
134 | bool comp_has_next_frame; | |
3b15f68e DH |
135 | bool first_source_change_sent; |
136 | bool source_changed; | |
256bf813 HV |
137 | }; |
138 | ||
d17589af HV |
139 | static const struct v4l2_event vicodec_eos_event = { |
140 | .type = V4L2_EVENT_EOS | |
141 | }; | |
142 | ||
256bf813 HV |
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: | |
256bf813 HV |
159 | break; |
160 | } | |
161 | return NULL; | |
162 | } | |
163 | ||
86764b88 DH |
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 | ||
92dc64d4 DH |
201 | static bool validate_by_version(unsigned int flags, unsigned int version) |
202 | { | |
3abfc314 | 203 | if (!version || version > V4L2_FWHT_VERSION) |
92dc64d4 DH |
204 | return false; |
205 | ||
206 | if (version >= 2) { | |
207 | unsigned int components_num = 1 + | |
3abfc314 HV |
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; | |
92dc64d4 DH |
211 | |
212 | if (components_num == 0 || components_num > 4 || !pixenc) | |
213 | return false; | |
214 | } | |
215 | return true; | |
216 | } | |
217 | ||
997deb81 DH |
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 = | |
3abfc314 | 222 | (params->flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; |
997deb81 | 223 | unsigned int height_div = |
3abfc314 | 224 | (params->flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; |
997deb81 DH |
225 | unsigned int components_num = 3; |
226 | unsigned int pixenc = 0; | |
227 | ||
228 | if (params->version < 3) | |
229 | return false; | |
230 | ||
3abfc314 HV |
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); | |
997deb81 DH |
234 | if (v4l2_fwht_validate_fmt(cur_info, width_div, height_div, |
235 | components_num, pixenc)) | |
236 | return true; | |
237 | return false; | |
238 | } | |
239 | ||
240 | ||
241 | static void update_state_from_header(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 | ||
256bf813 | 253 | static int device_process(struct vicodec_ctx *ctx, |
b9bbbbfe DH |
254 | struct vb2_v4l2_buffer *src_vb, |
255 | struct vb2_v4l2_buffer *dst_vb) | |
256bf813 HV |
256 | { |
257 | struct vicodec_dev *dev = ctx->dev; | |
cd12b401 | 258 | struct v4l2_fwht_state *state = &ctx->state; |
b9bbbbfe | 259 | u8 *p_src, *p_dst; |
997deb81 | 260 | int ret = 0; |
256bf813 | 261 | |
997deb81 | 262 | if (ctx->is_enc || ctx->is_stateless) |
b9bbbbfe | 263 | p_src = vb2_plane_vaddr(&src_vb->vb2_buf, 0); |
256bf813 | 264 | else |
b9bbbbfe | 265 | p_src = state->compressed_frame; |
997deb81 DH |
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(src_req, &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 | */ | |
3abfc314 | 281 | if (!(ntohl(ctx->state.header.flags) & V4L2_FWHT_FL_I_FRAME)) { |
997deb81 | 282 | struct vb2_buffer *ref_vb2_buf; |
997deb81 DH |
283 | struct vb2_queue *vq_cap = |
284 | v4l2_m2m_get_vq(ctx->fh.m2m_ctx, | |
285 | V4L2_BUF_TYPE_VIDEO_CAPTURE); | |
286 | ||
5b25e543 EG |
287 | ref_vb2_buf = vb2_find_buffer(vq_cap, ctx->state.ref_frame_ts); |
288 | if (!ref_vb2_buf) | |
997deb81 | 289 | return -EINVAL; |
997deb81 DH |
290 | if (ref_vb2_buf->state == VB2_BUF_STATE_ERROR) |
291 | ret = -EINVAL; | |
292 | ctx->state.ref_frame.buf = | |
293 | vb2_plane_vaddr(ref_vb2_buf, 0); | |
294 | } else { | |
295 | ctx->state.ref_frame.buf = NULL; | |
296 | } | |
297 | } | |
b9bbbbfe DH |
298 | p_dst = vb2_plane_vaddr(&dst_vb->vb2_buf, 0); |
299 | if (!p_src || !p_dst) { | |
256bf813 HV |
300 | v4l2_err(&dev->v4l2_dev, |
301 | "Acquiring kernel pointers to buffers failed\n"); | |
302 | return -EFAULT; | |
303 | } | |
304 | ||
305 | if (ctx->is_enc) { | |
b9bbbbfe | 306 | struct vicodec_q_data *q_src; |
f902796a | 307 | int comp_sz_or_errcode; |
256bf813 | 308 | |
b9bbbbfe DH |
309 | q_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); |
310 | state->info = q_src->info; | |
f902796a DH |
311 | comp_sz_or_errcode = v4l2_fwht_encode(state, p_src, p_dst); |
312 | if (comp_sz_or_errcode < 0) | |
313 | return comp_sz_or_errcode; | |
314 | vb2_set_plane_payload(&dst_vb->vb2_buf, 0, comp_sz_or_errcode); | |
256bf813 | 315 | } else { |
8eead25c | 316 | struct vicodec_q_data *q_dst; |
f863f222 DH |
317 | unsigned int comp_frame_size = ntohl(ctx->state.header.size); |
318 | ||
8eead25c | 319 | q_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); |
f863f222 DH |
320 | if (comp_frame_size > ctx->comp_max_size) |
321 | return -EINVAL; | |
b9bbbbfe DH |
322 | state->info = q_dst->info; |
323 | ret = v4l2_fwht_decode(state, p_src, p_dst); | |
b09d8b25 | 324 | if (ret < 0) |
256bf813 | 325 | return ret; |
997deb81 DH |
326 | if (!ctx->is_stateless) |
327 | copy_cap_to_ref(p_dst, ctx->state.info, &ctx->state); | |
86764b88 | 328 | |
b9bbbbfe | 329 | vb2_set_plane_payload(&dst_vb->vb2_buf, 0, q_dst->sizeimage); |
3abfc314 | 330 | if (ntohl(ctx->state.header.flags) & V4L2_FWHT_FL_I_FRAME) |
fbbbb2cd HV |
331 | dst_vb->flags |= V4L2_BUF_FLAG_KEYFRAME; |
332 | else | |
333 | dst_vb->flags |= V4L2_BUF_FLAG_PFRAME; | |
256bf813 | 334 | } |
997deb81 | 335 | return ret; |
256bf813 HV |
336 | } |
337 | ||
338 | /* | |
339 | * mem2mem callbacks | |
340 | */ | |
c9d06df6 MCC |
341 | static enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx, |
342 | u8 **pp, u32 sz) | |
ddc1b085 DH |
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 *header = (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, magic[ctx->comp_magic_cnt], | |
359 | *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(header, magic, 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 | } | |
256bf813 HV |
398 | |
399 | /* device_run() - prepares and starts the device */ | |
400 | static void device_run(void *priv) | |
401 | { | |
256bf813 HV |
402 | struct vicodec_ctx *ctx = priv; |
403 | struct vicodec_dev *dev = ctx->dev; | |
404 | struct vb2_v4l2_buffer *src_buf, *dst_buf; | |
8eead25c | 405 | struct vicodec_q_data *q_src, *q_dst; |
256bf813 | 406 | u32 state; |
997deb81 DH |
407 | struct media_request *src_req; |
408 | ||
256bf813 HV |
409 | src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); |
410 | dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); | |
997deb81 DH |
411 | src_req = src_buf->vb2_buf.req_obj.req; |
412 | ||
b9bbbbfe | 413 | q_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); |
8eead25c | 414 | q_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); |
256bf813 HV |
415 | |
416 | state = VB2_BUF_STATE_DONE; | |
417 | if (device_process(ctx, src_buf, dst_buf)) | |
418 | state = VB2_BUF_STATE_ERROR; | |
8eead25c DH |
419 | else |
420 | dst_buf->sequence = q_dst->sequence++; | |
421 | dst_buf->flags &= ~V4L2_BUF_FLAG_LAST; | |
fbbbb2cd | 422 | v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); |
8eead25c | 423 | |
256bf813 | 424 | spin_lock(ctx->lock); |
d4d137de NA |
425 | if (!ctx->comp_has_next_frame && |
426 | v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src_buf)) { | |
256bf813 | 427 | dst_buf->flags |= V4L2_BUF_FLAG_LAST; |
d17589af | 428 | v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); |
d4d137de | 429 | v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); |
256bf813 | 430 | } |
997deb81 | 431 | if (ctx->is_enc || ctx->is_stateless) { |
b9bbbbfe | 432 | src_buf->sequence = q_src->sequence++; |
256bf813 HV |
433 | src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); |
434 | v4l2_m2m_buf_done(src_buf, state); | |
435 | } else if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == ctx->cur_buf_offset) { | |
b9bbbbfe | 436 | src_buf->sequence = q_src->sequence++; |
256bf813 HV |
437 | src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); |
438 | v4l2_m2m_buf_done(src_buf, state); | |
439 | ctx->cur_buf_offset = 0; | |
440 | ctx->comp_has_next_frame = false; | |
441 | } | |
442 | v4l2_m2m_buf_done(dst_buf, state); | |
997deb81 | 443 | |
256bf813 | 444 | ctx->comp_size = 0; |
ddc1b085 | 445 | ctx->header_size = 0; |
256bf813 HV |
446 | ctx->comp_magic_cnt = 0; |
447 | ctx->comp_has_frame = false; | |
448 | spin_unlock(ctx->lock); | |
e5bc0e1d HV |
449 | if (ctx->is_stateless && src_req) |
450 | v4l2_ctrl_request_complete(src_req, &ctx->hdl); | |
256bf813 HV |
451 | |
452 | if (ctx->is_enc) | |
c022a4a9 | 453 | v4l2_m2m_job_finish(dev->stateful_enc.m2m_dev, ctx->fh.m2m_ctx); |
fde649b4 DH |
454 | else if (ctx->is_stateless) |
455 | v4l2_m2m_job_finish(dev->stateless_dec.m2m_dev, | |
456 | ctx->fh.m2m_ctx); | |
256bf813 | 457 | else |
c022a4a9 | 458 | v4l2_m2m_job_finish(dev->stateful_dec.m2m_dev, ctx->fh.m2m_ctx); |
256bf813 HV |
459 | } |
460 | ||
b9bbbbfe | 461 | static void job_remove_src_buf(struct vicodec_ctx *ctx, u32 state) |
256bf813 HV |
462 | { |
463 | struct vb2_v4l2_buffer *src_buf; | |
b9bbbbfe | 464 | struct vicodec_q_data *q_src; |
256bf813 | 465 | |
b9bbbbfe | 466 | q_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); |
256bf813 HV |
467 | spin_lock(ctx->lock); |
468 | src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); | |
b9bbbbfe | 469 | src_buf->sequence = q_src->sequence++; |
256bf813 HV |
470 | v4l2_m2m_buf_done(src_buf, state); |
471 | ctx->cur_buf_offset = 0; | |
472 | spin_unlock(ctx->lock); | |
473 | } | |
474 | ||
3b15f68e DH |
475 | static const struct v4l2_fwht_pixfmt_info * |
476 | info_from_header(const struct fwht_cframe_hdr *p_hdr) | |
477 | { | |
478 | unsigned int flags = ntohl(p_hdr->flags); | |
3abfc314 HV |
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; | |
3b15f68e DH |
481 | unsigned int components_num = 3; |
482 | unsigned int pixenc = 0; | |
483 | unsigned int version = ntohl(p_hdr->version); | |
484 | ||
75e3e5b8 | 485 | if (version >= 2) { |
3abfc314 HV |
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); | |
3b15f68e | 489 | } |
50e4c5e1 | 490 | return v4l2_fwht_find_nth_fmt(width_div, height_div, |
3b15f68e DH |
491 | components_num, pixenc, 0); |
492 | } | |
493 | ||
494 | static bool is_header_valid(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 | ||
3b15f68e DH |
502 | if (w < MIN_WIDTH || w > MAX_WIDTH || h < MIN_HEIGHT || h > MAX_HEIGHT) |
503 | return false; | |
504 | ||
92dc64d4 DH |
505 | if (!validate_by_version(flags, version)) |
506 | return false; | |
3b15f68e DH |
507 | |
508 | info = info_from_header(p_hdr); | |
509 | if (!info) | |
510 | return false; | |
511 | return true; | |
512 | } | |
513 | ||
514 | static void update_capture_data_from_header(struct vicodec_ctx *ctx) | |
515 | { | |
516 | struct vicodec_q_data *q_dst = get_q_data(ctx, | |
517 | 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); | |
3abfc314 HV |
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; | |
3b15f68e | 523 | |
997deb81 DH |
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 | ||
3b15f68e DH |
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 | V4L2_BUF_TYPE_VIDEO_CAPTURE); | |
552 | ||
553 | vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); | |
554 | dst_buf->sequence = q_dst->sequence++; | |
555 | ||
a4d3d612 | 556 | v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, !ctx->is_enc); |
3b15f68e DH |
557 | dst_buf->flags |= V4L2_BUF_FLAG_LAST; |
558 | v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); | |
559 | } | |
560 | ||
256bf813 HV |
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; | |
b9bbbbfe | 568 | u8 *p_src; |
256bf813 HV |
569 | u8 *p; |
570 | u32 sz; | |
571 | u32 state; | |
3b15f68e DH |
572 | struct vicodec_q_data *q_dst = get_q_data(ctx, |
573 | 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; | |
997deb81 | 582 | if (ctx->is_stateless || ctx->is_enc || ctx->comp_has_frame) |
256bf813 HV |
583 | return 1; |
584 | ||
585 | restart: | |
586 | ctx->comp_has_next_frame = false; | |
587 | src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); | |
588 | if (!src_buf) | |
589 | return 0; | |
b9bbbbfe | 590 | p_src = vb2_plane_vaddr(&src_buf->vb2_buf, 0); |
256bf813 | 591 | sz = vb2_get_plane_payload(&src_buf->vb2_buf, 0); |
b9bbbbfe | 592 | p = p_src + ctx->cur_buf_offset; |
256bf813 HV |
593 | |
594 | state = VB2_BUF_STATE_DONE; | |
595 | ||
ddc1b085 DH |
596 | if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { |
597 | state = get_next_header(ctx, &p, p_src + sz - p); | |
598 | if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { | |
d4d137de NA |
599 | if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, |
600 | src_buf)) | |
d17589af | 601 | return 1; |
b9bbbbfe | 602 | job_remove_src_buf(ctx, state); |
256bf813 HV |
603 | goto restart; |
604 | } | |
3b15f68e | 605 | } |
256bf813 | 606 | |
3b15f68e | 607 | comp_frame_size = ntohl(ctx->state.header.size); |
ddc1b085 | 608 | |
3b15f68e DH |
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; | |
256bf813 | 620 | |
b9bbbbfe DH |
621 | if (copy > p_src + sz - p) |
622 | copy = p_src + sz - p; | |
623 | ||
cd12b401 | 624 | memcpy(ctx->state.compressed_frame + ctx->comp_size, |
256bf813 HV |
625 | p, copy); |
626 | p += copy; | |
627 | ctx->comp_size += copy; | |
3b15f68e | 628 | if (ctx->comp_size < max_to_copy) { |
d4d137de NA |
629 | if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, |
630 | src_buf)) | |
d17589af | 631 | return 1; |
b9bbbbfe | 632 | job_remove_src_buf(ctx, state); |
256bf813 HV |
633 | goto restart; |
634 | } | |
635 | } | |
b9bbbbfe | 636 | ctx->cur_buf_offset = p - p_src; |
3b15f68e DH |
637 | if (ctx->comp_size == comp_frame_size) |
638 | ctx->comp_has_frame = true; | |
256bf813 | 639 | ctx->comp_has_next_frame = false; |
3b15f68e DH |
640 | if (ctx->comp_has_frame && sz - ctx->cur_buf_offset >= |
641 | sizeof(struct fwht_cframe_hdr)) { | |
21abebf0 | 642 | struct fwht_cframe_hdr *p_hdr = (struct fwht_cframe_hdr *)p; |
256bf813 HV |
643 | u32 frame_size = ntohl(p_hdr->size); |
644 | u32 remaining = sz - ctx->cur_buf_offset - sizeof(*p_hdr); | |
645 | ||
646 | if (!memcmp(p, magic, sizeof(magic))) | |
647 | ctx->comp_has_next_frame = remaining >= frame_size; | |
648 | } | |
3b15f68e DH |
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(&ctx->state.header) && ctx->comp_has_frame) | |
654 | return 1; | |
655 | flags = ntohl(ctx->state.header.flags); | |
3abfc314 HV |
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; | |
3b15f68e DH |
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(ctx->fh.m2m_ctx); | |
671 | ||
672 | update_capture_data_from_header(ctx); | |
3b15f68e DH |
673 | v4l2_event_queue_fh(&ctx->fh, &rs_event); |
674 | set_last_buffer(dst_buf, src_buf, ctx); | |
675 | ctx->source_changed = true; | |
676 | return 0; | |
677 | } | |
256bf813 HV |
678 | return 1; |
679 | } | |
680 | ||
256bf813 HV |
681 | /* |
682 | * video ioctls | |
683 | */ | |
684 | ||
cd12b401 | 685 | static const struct v4l2_fwht_pixfmt_info *find_fmt(u32 fmt) |
256bf813 | 686 | { |
cd12b401 HV |
687 | const struct v4l2_fwht_pixfmt_info *info = |
688 | v4l2_fwht_find_pixfmt(fmt); | |
256bf813 | 689 | |
cd12b401 HV |
690 | if (!info) |
691 | info = v4l2_fwht_get_pixfmt(0); | |
692 | return info; | |
256bf813 HV |
693 | } |
694 | ||
695 | static int vidioc_querycap(struct file *file, void *priv, | |
696 | struct v4l2_capability *cap) | |
697 | { | |
85709cbf MCC |
698 | strscpy(cap->driver, VICODEC_NAME, sizeof(cap->driver)); |
699 | strscpy(cap->card, VICODEC_NAME, sizeof(cap->card)); | |
256bf813 HV |
700 | snprintf(cap->bus_info, sizeof(cap->bus_info), |
701 | "platform:%s", VICODEC_NAME); | |
256bf813 HV |
702 | return 0; |
703 | } | |
704 | ||
5fbd0729 DH |
705 | static int enum_fmt(struct v4l2_fmtdesc *f, struct vicodec_ctx *ctx, |
706 | bool is_out) | |
256bf813 | 707 | { |
5fbd0729 | 708 | bool is_uncomp = (ctx->is_enc && is_out) || (!ctx->is_enc && !is_out); |
256bf813 HV |
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; | |
256bf813 | 714 | |
cd12b401 HV |
715 | if (is_uncomp) { |
716 | const struct v4l2_fwht_pixfmt_info *info = | |
5fbd0729 DH |
717 | get_q_data(ctx, f->type)->info; |
718 | ||
d17589af HV |
719 | if (ctx->is_enc || |
720 | !vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) | |
5fbd0729 DH |
721 | info = v4l2_fwht_get_pixfmt(f->index); |
722 | else | |
50e4c5e1 | 723 | info = v4l2_fwht_find_nth_fmt(info->width_div, |
5fbd0729 DH |
724 | info->height_div, |
725 | info->components_num, | |
726 | info->pixenc, | |
727 | f->index); | |
cd12b401 HV |
728 | if (!info) |
729 | return -EINVAL; | |
730 | f->pixelformat = info->id; | |
731 | } else { | |
732 | if (f->index) | |
733 | return -EINVAL; | |
35e2e8b5 DH |
734 | f->pixelformat = ctx->is_stateless ? |
735 | V4L2_PIX_FMT_FWHT_STATELESS : V4L2_PIX_FMT_FWHT; | |
b867d9ce MJ |
736 | if (!ctx->is_enc && !ctx->is_stateless) |
737 | f->flags = V4L2_FMT_FLAG_DYN_RESOLUTION | | |
738 | V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM; | |
cd12b401 | 739 | } |
256bf813 HV |
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 | ||
5fbd0729 | 748 | return enum_fmt(f, ctx, false); |
256bf813 HV |
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 | ||
5fbd0729 | 756 | return enum_fmt(f, ctx, true); |
256bf813 HV |
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; | |
cd12b401 | 765 | const struct v4l2_fwht_pixfmt_info *info; |
256bf813 HV |
766 | |
767 | vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); | |
768 | if (!vq) | |
769 | return -EINVAL; | |
770 | ||
771 | q_data = get_q_data(ctx, f->type); | |
29a7a5e9 | 772 | info = q_data->info; |
256bf813 HV |
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; | |
9e812549 DH |
780 | pix->width = q_data->coded_width; |
781 | pix->height = q_data->coded_height; | |
256bf813 | 782 | pix->field = V4L2_FIELD_NONE; |
29a7a5e9 | 783 | pix->pixelformat = info->id; |
9e812549 DH |
784 | pix->bytesperline = q_data->coded_width * |
785 | info->bytesperline_mult; | |
256bf813 | 786 | pix->sizeimage = q_data->sizeimage; |
cd12b401 HV |
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; | |
256bf813 HV |
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; | |
9e812549 DH |
798 | pix_mp->width = q_data->coded_width; |
799 | pix_mp->height = q_data->coded_height; | |
256bf813 | 800 | pix_mp->field = V4L2_FIELD_NONE; |
29a7a5e9 | 801 | pix_mp->pixelformat = info->id; |
256bf813 | 802 | pix_mp->num_planes = 1; |
29a7a5e9 | 803 | pix_mp->plane_fmt[0].bytesperline = |
9e812549 | 804 | q_data->coded_width * info->bytesperline_mult; |
256bf813 | 805 | pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage; |
cd12b401 HV |
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; | |
256bf813 HV |
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(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(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; | |
29a7a5e9 | 833 | struct v4l2_plane_pix_format *plane; |
35e2e8b5 DH |
834 | const struct v4l2_fwht_pixfmt_info *info = ctx->is_stateless ? |
835 | &pixfmt_stateless_fwht : &pixfmt_fwht; | |
256bf813 HV |
836 | |
837 | switch (f->type) { | |
838 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | |
839 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: | |
840 | pix = &f->fmt.pix; | |
35e2e8b5 DH |
841 | if (pix->pixelformat != V4L2_PIX_FMT_FWHT && |
842 | pix->pixelformat != V4L2_PIX_FMT_FWHT_STATELESS) | |
29a7a5e9 | 843 | info = find_fmt(pix->pixelformat); |
9e812549 DH |
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 | ||
256bf813 | 851 | pix->field = V4L2_FIELD_NONE; |
29a7a5e9 HV |
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) | |
21abebf0 | 857 | pix->sizeimage += sizeof(struct fwht_cframe_hdr); |
256bf813 HV |
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; | |
29a7a5e9 | 862 | plane = pix_mp->plane_fmt; |
35e2e8b5 DH |
863 | if (pix_mp->pixelformat != V4L2_PIX_FMT_FWHT && |
864 | pix_mp->pixelformat != V4L2_PIX_FMT_FWHT_STATELESS) | |
29a7a5e9 HV |
865 | info = find_fmt(pix_mp->pixelformat); |
866 | pix_mp->num_planes = 1; | |
9e812549 DH |
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 | ||
256bf813 | 875 | pix_mp->field = V4L2_FIELD_NONE; |
29a7a5e9 HV |
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) | |
21abebf0 | 881 | plane->sizeimage += sizeof(struct fwht_cframe_hdr); |
256bf813 HV |
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 : | |
29a7a5e9 | 903 | find_fmt(f->fmt.pix.pixelformat)->id; |
cd12b401 HV |
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; | |
256bf813 HV |
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 : | |
29a7a5e9 | 914 | find_fmt(pix_mp->pixelformat)->id; |
cd12b401 HV |
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; | |
256bf813 HV |
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; | |
35e2e8b5 DH |
939 | if (ctx->is_enc) |
940 | pix->pixelformat = find_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; | |
256bf813 HV |
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; | |
35e2e8b5 DH |
952 | if (ctx->is_enc) |
953 | pix_mp->pixelformat = find_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; | |
256bf813 HV |
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(ctx->fh.m2m_ctx, f->type); | |
977 | if (!vq) | |
978 | return -EINVAL; | |
979 | ||
980 | q_data = get_q_data(ctx, 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 = | |
3b15f68e | 990 | !q_data->info || |
29a7a5e9 | 991 | q_data->info->id != pix->pixelformat || |
9e812549 DH |
992 | q_data->coded_width != pix->width || |
993 | q_data->coded_height != pix->height; | |
256bf813 HV |
994 | |
995 | if (vb2_is_busy(vq) && fmt_changed) | |
996 | return -EBUSY; | |
997 | ||
29a7a5e9 HV |
998 | if (pix->pixelformat == V4L2_PIX_FMT_FWHT) |
999 | q_data->info = &pixfmt_fwht; | |
35e2e8b5 DH |
1000 | else if (pix->pixelformat == V4L2_PIX_FMT_FWHT_STATELESS) |
1001 | q_data->info = &pixfmt_stateless_fwht; | |
29a7a5e9 HV |
1002 | else |
1003 | q_data->info = find_fmt(pix->pixelformat); | |
9e812549 DH |
1004 | q_data->coded_width = pix->width; |
1005 | q_data->coded_height = pix->height; | |
256bf813 HV |
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 = | |
3b15f68e | 1013 | !q_data->info || |
29a7a5e9 | 1014 | q_data->info->id != pix_mp->pixelformat || |
9e812549 DH |
1015 | q_data->coded_width != pix_mp->width || |
1016 | q_data->coded_height != pix_mp->height; | |
256bf813 HV |
1017 | |
1018 | if (vb2_is_busy(vq) && fmt_changed) | |
1019 | return -EBUSY; | |
1020 | ||
29a7a5e9 HV |
1021 | if (pix_mp->pixelformat == V4L2_PIX_FMT_FWHT) |
1022 | q_data->info = &pixfmt_fwht; | |
35e2e8b5 DH |
1023 | else if (pix_mp->pixelformat == V4L2_PIX_FMT_FWHT_STATELESS) |
1024 | q_data->info = &pixfmt_stateless_fwht; | |
29a7a5e9 HV |
1025 | else |
1026 | q_data->info = find_fmt(pix_mp->pixelformat); | |
9e812549 DH |
1027 | q_data->coded_width = pix_mp->width; |
1028 | q_data->coded_height = pix_mp->height; | |
256bf813 HV |
1029 | q_data->sizeimage = pix_mp->plane_fmt[0].sizeimage; |
1030 | break; | |
1031 | default: | |
1032 | return -EINVAL; | |
1033 | } | |
1034 | ||
1035 | dprintk(ctx->dev, | |
efec9c81 | 1036 | "Setting format for type %d, coded wxh: %dx%d, fourcc: 0x%08x\n", |
9e812549 | 1037 | f->type, q_data->coded_width, q_data->coded_height, |
9e812549 | 1038 | q_data->info->id); |
256bf813 HV |
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(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); | |
efec9c81 HV |
1059 | struct vicodec_q_data *q_data; |
1060 | struct vicodec_q_data *q_data_cap; | |
256bf813 | 1061 | struct v4l2_pix_format *pix; |
efec9c81 HV |
1062 | struct v4l2_pix_format_mplane *pix_mp; |
1063 | u32 coded_w = 0, coded_h = 0; | |
1064 | unsigned int size = 0; | |
256bf813 HV |
1065 | int ret; |
1066 | ||
efec9c81 HV |
1067 | q_data = get_q_data(ctx, f->type); |
1068 | q_data_cap = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); | |
1069 | ||
256bf813 HV |
1070 | ret = vidioc_try_fmt_vid_out(file, priv, f); |
1071 | if (ret) | |
1072 | return ret; | |
1073 | ||
efec9c81 HV |
1074 | if (ctx->is_enc) { |
1075 | struct vb2_queue *vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); | |
1076 | struct vb2_queue *vq_cap = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, | |
1077 | 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(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(vq_cap) && size > q_data_cap->sizeimage) | |
1097 | return -EBUSY; | |
1098 | } | |
1099 | ||
256bf813 HV |
1100 | ret = vidioc_s_fmt(file2ctx(file), f); |
1101 | if (!ret) { | |
efec9c81 HV |
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 | ||
256bf813 | 1110 | switch (f->type) { |
256bf813 HV |
1111 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: |
1112 | pix = &f->fmt.pix; | |
cd12b401 HV |
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; | |
256bf813 | 1117 | break; |
256bf813 HV |
1118 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: |
1119 | pix_mp = &f->fmt.pix_mp; | |
cd12b401 HV |
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; | |
256bf813 HV |
1124 | break; |
1125 | default: | |
1126 | break; | |
1127 | } | |
1128 | } | |
1129 | return ret; | |
1130 | } | |
1131 | ||
9e812549 DH |
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; | |
db9a01b3 | 1137 | |
9e812549 DH |
1138 | q_data = get_q_data(ctx, 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 | */ | |
fb56f4be | 1145 | if (ctx->is_enc && s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { |
9e812549 | 1146 | switch (s->target) { |
9e812549 DH |
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; | |
9e812549 DH |
1153 | case V4L2_SEL_TGT_CROP_DEFAULT: |
1154 | case V4L2_SEL_TGT_CROP_BOUNDS: | |
1155 | s->r.left = 0; | |
fb56f4be HV |
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; | |
9e812549 DH |
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; | |
9e812549 | 1186 | |
7243e5a0 | 1187 | if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) |
db9a01b3 HV |
1188 | return -EINVAL; |
1189 | ||
9e812549 DH |
1190 | q_data = get_q_data(ctx, s->type); |
1191 | if (!q_data) | |
1192 | return -EINVAL; | |
1193 | ||
db9a01b3 | 1194 | if (!ctx->is_enc || s->target != V4L2_SEL_TGT_CROP) |
9e812549 DH |
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 | ||
256bf813 HV |
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 | ||
9b925365 | 1214 | ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); |
256bf813 HV |
1215 | if (ret < 0) |
1216 | return ret; | |
1217 | ||
d17589af HV |
1218 | if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) || |
1219 | !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) | |
1220 | return 0; | |
1221 | ||
d4d137de NA |
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(ctx->fh.m2m_ctx)) | |
1228 | v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); | |
1229 | ||
1230 | if (ec->cmd == V4L2_ENC_CMD_START && | |
1231 | v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) | |
d17589af | 1232 | vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); |
d4d137de NA |
1233 | |
1234 | return 0; | |
256bf813 HV |
1235 | } |
1236 | ||
256bf813 HV |
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 | ||
deaf6b0a PK |
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 | ||
9b925365 | 1249 | ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); |
256bf813 HV |
1250 | if (ret < 0) |
1251 | return ret; | |
1252 | ||
d17589af HV |
1253 | if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) || |
1254 | !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) | |
1255 | return 0; | |
1256 | ||
d4d137de NA |
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(ctx->fh.m2m_ctx)) | |
1263 | v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); | |
1264 | ||
1265 | if (dc->cmd == V4L2_DEC_CMD_START && | |
1266 | v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) | |
d17589af | 1267 | vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); |
d4d137de NA |
1268 | |
1269 | return 0; | |
256bf813 HV |
1270 | } |
1271 | ||
1272 | static int vicodec_enum_framesizes(struct file *file, void *fh, | |
1273 | struct v4l2_frmsizeenum *fsize) | |
1274 | { | |
1275 | switch (fsize->pixel_format) { | |
35e2e8b5 DH |
1276 | case V4L2_PIX_FMT_FWHT_STATELESS: |
1277 | break; | |
256bf813 HV |
1278 | case V4L2_PIX_FMT_FWHT: |
1279 | break; | |
1280 | default: | |
29a7a5e9 | 1281 | if (find_fmt(fsize->pixel_format)->id == fsize->pixel_format) |
256bf813 HV |
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 | { | |
69a90057 HV |
1304 | struct vicodec_ctx *ctx = container_of(fh, struct vicodec_ctx, fh); |
1305 | ||
256bf813 | 1306 | switch (sub->type) { |
3b15f68e | 1307 | case V4L2_EVENT_SOURCE_CHANGE: |
69a90057 HV |
1308 | if (ctx->is_enc) |
1309 | return -EINVAL; | |
1771e9fb | 1310 | fallthrough; |
69a90057 | 1311 | case V4L2_EVENT_EOS: |
3b6813d6 HV |
1312 | if (ctx->is_stateless) |
1313 | return -EINVAL; | |
256bf813 HV |
1314 | return v4l2_event_subscribe(fh, sub, 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 | ||
256bf813 HV |
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 | ||
256bf813 HV |
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, | |
2f241950 | 1348 | .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs, |
256bf813 HV |
1349 | |
1350 | .vidioc_streamon = v4l2_m2m_ioctl_streamon, | |
1351 | .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, | |
1352 | ||
9e812549 DH |
1353 | .vidioc_g_selection = vidioc_g_selection, |
1354 | .vidioc_s_selection = vidioc_s_selection, | |
1355 | ||
9b925365 | 1356 | .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, |
256bf813 | 1357 | .vidioc_encoder_cmd = vicodec_encoder_cmd, |
9b925365 | 1358 | .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, |
256bf813 HV |
1359 | .vidioc_decoder_cmd = vicodec_decoder_cmd, |
1360 | .vidioc_enum_framesizes = vicodec_enum_framesizes, | |
1361 | ||
1362 | .vidioc_subscribe_event = vicodec_subscribe_event, | |
1363 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | |
1364 | }; | |
1365 | ||
1366 | ||
1367 | /* | |
1368 | * Queue operations | |
1369 | */ | |
1370 | ||
1371 | static int vicodec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, | |
1372 | unsigned int *nplanes, unsigned int sizes[], | |
1373 | struct device *alloc_devs[]) | |
1374 | { | |
1375 | struct vicodec_ctx *ctx = vb2_get_drv_priv(vq); | |
1376 | struct vicodec_q_data *q_data = get_q_data(ctx, vq->type); | |
1377 | unsigned int size = q_data->sizeimage; | |
1378 | ||
1379 | if (*nplanes) | |
1380 | return sizes[0] < size ? -EINVAL : 0; | |
1381 | ||
1382 | *nplanes = 1; | |
1383 | sizes[0] = size; | |
8307f0ab | 1384 | q_data->vb2_sizeimage = size; |
256bf813 HV |
1385 | return 0; |
1386 | } | |
1387 | ||
997deb81 DH |
1388 | static int vicodec_buf_out_validate(struct vb2_buffer *vb) |
1389 | { | |
1390 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | |
1391 | ||
1392 | vbuf->field = V4L2_FIELD_NONE; | |
1393 | return 0; | |
1394 | } | |
1395 | ||
256bf813 HV |
1396 | static int vicodec_buf_prepare(struct vb2_buffer *vb) |
1397 | { | |
1398 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | |
1399 | struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | |
1400 | struct vicodec_q_data *q_data; | |
1401 | ||
1402 | dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type); | |
1403 | ||
1404 | q_data = get_q_data(ctx, vb->vb2_queue->type); | |
1405 | if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { | |
1406 | if (vbuf->field == V4L2_FIELD_ANY) | |
1407 | vbuf->field = V4L2_FIELD_NONE; | |
1408 | if (vbuf->field != V4L2_FIELD_NONE) { | |
1409 | dprintk(ctx->dev, "%s field isn't supported\n", | |
1410 | __func__); | |
1411 | return -EINVAL; | |
1412 | } | |
1413 | } | |
1414 | ||
8307f0ab | 1415 | if (vb2_plane_size(vb, 0) < q_data->vb2_sizeimage) { |
256bf813 HV |
1416 | dprintk(ctx->dev, |
1417 | "%s data will not fit into plane (%lu < %lu)\n", | |
1418 | __func__, vb2_plane_size(vb, 0), | |
8307f0ab | 1419 | (long)q_data->vb2_sizeimage); |
256bf813 HV |
1420 | return -EINVAL; |
1421 | } | |
1422 | ||
1423 | return 0; | |
1424 | } | |
1425 | ||
1426 | static void vicodec_buf_queue(struct vb2_buffer *vb) | |
1427 | { | |
1428 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | |
1429 | struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | |
3b15f68e DH |
1430 | unsigned int sz = vb2_get_plane_payload(&vbuf->vb2_buf, 0); |
1431 | u8 *p_src = vb2_plane_vaddr(&vbuf->vb2_buf, 0); | |
1432 | u8 *p = p_src; | |
1433 | struct vb2_queue *vq_out = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, | |
1434 | V4L2_BUF_TYPE_VIDEO_OUTPUT); | |
1435 | struct vb2_queue *vq_cap = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, | |
1436 | V4L2_BUF_TYPE_VIDEO_CAPTURE); | |
1437 | bool header_valid = false; | |
1438 | static const struct v4l2_event rs_event = { | |
1439 | .type = V4L2_EVENT_SOURCE_CHANGE, | |
1440 | .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, | |
1441 | }; | |
1442 | ||
b3ab1c60 | 1443 | if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && |
d4d137de NA |
1444 | vb2_is_streaming(vb->vb2_queue) && |
1445 | v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { | |
1446 | unsigned int i; | |
d17589af | 1447 | |
d4d137de | 1448 | for (i = 0; i < vb->num_planes; i++) |
05fd87b8 | 1449 | vb2_set_plane_payload(vb, i, 0); |
d4d137de NA |
1450 | |
1451 | vbuf->field = V4L2_FIELD_NONE; | |
1452 | vbuf->sequence = | |
1453 | get_q_data(ctx, vb->vb2_queue->type)->sequence++; | |
1454 | ||
1455 | v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf); | |
1456 | v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); | |
1457 | return; | |
d17589af HV |
1458 | } |
1459 | ||
3b15f68e DH |
1460 | /* buf_queue handles only the first source change event */ |
1461 | if (ctx->first_source_change_sent) { | |
1462 | v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); | |
1463 | return; | |
1464 | } | |
1465 | ||
1466 | /* | |
1467 | * if both queues are streaming, the source change event is | |
1468 | * handled in job_ready | |
1469 | */ | |
1470 | if (vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out)) { | |
1471 | v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); | |
1472 | return; | |
1473 | } | |
1474 | ||
1475 | /* | |
997deb81 | 1476 | * source change event is relevant only for the stateful decoder |
3b15f68e DH |
1477 | * in the compressed stream |
1478 | */ | |
997deb81 | 1479 | if (ctx->is_stateless || ctx->is_enc || |
b3ab1c60 | 1480 | V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { |
3b15f68e DH |
1481 | v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); |
1482 | return; | |
1483 | } | |
1484 | ||
1485 | do { | |
1486 | enum vb2_buffer_state state = | |
1487 | get_next_header(ctx, &p, p_src + sz - p); | |
1488 | ||
1489 | if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { | |
1490 | v4l2_m2m_buf_done(vbuf, state); | |
1491 | return; | |
1492 | } | |
1493 | header_valid = is_header_valid(&ctx->state.header); | |
1494 | /* | |
1495 | * p points right after the end of the header in the | |
1496 | * buffer. If the header is invalid we set p to point | |
1497 | * to the next byte after the start of the header | |
1498 | */ | |
1499 | if (!header_valid) { | |
1500 | p = p - sizeof(struct fwht_cframe_hdr) + 1; | |
1501 | if (p < p_src) | |
1502 | p = p_src; | |
1503 | ctx->header_size = 0; | |
1504 | ctx->comp_magic_cnt = 0; | |
1505 | } | |
256bf813 | 1506 | |
3b15f68e DH |
1507 | } while (!header_valid); |
1508 | ||
1509 | ctx->cur_buf_offset = p - p_src; | |
1510 | update_capture_data_from_header(ctx); | |
1511 | ctx->first_source_change_sent = true; | |
1512 | v4l2_event_queue_fh(&ctx->fh, &rs_event); | |
256bf813 HV |
1513 | v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); |
1514 | } | |
1515 | ||
1516 | static void vicodec_return_bufs(struct vb2_queue *q, u32 state) | |
1517 | { | |
1518 | struct vicodec_ctx *ctx = vb2_get_drv_priv(q); | |
1519 | struct vb2_v4l2_buffer *vbuf; | |
1520 | ||
1521 | for (;;) { | |
1522 | if (V4L2_TYPE_IS_OUTPUT(q->type)) | |
1523 | vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); | |
1524 | else | |
1525 | vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); | |
1526 | if (vbuf == NULL) | |
1527 | return; | |
997deb81 DH |
1528 | v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, |
1529 | &ctx->hdl); | |
256bf813 HV |
1530 | spin_lock(ctx->lock); |
1531 | v4l2_m2m_buf_done(vbuf, state); | |
1532 | spin_unlock(ctx->lock); | |
1533 | } | |
1534 | } | |
1535 | ||
997deb81 DH |
1536 | static unsigned int total_frame_size(struct vicodec_q_data *q_data) |
1537 | { | |
1538 | unsigned int size; | |
1539 | unsigned int chroma_div; | |
1540 | ||
1541 | if (!q_data->info) { | |
1542 | WARN_ON(1); | |
1543 | return 0; | |
1544 | } | |
1545 | size = q_data->coded_width * q_data->coded_height; | |
1546 | chroma_div = q_data->info->width_div * q_data->info->height_div; | |
1547 | ||
1548 | if (q_data->info->components_num == 4) | |
1549 | return 2 * size + 2 * (size / chroma_div); | |
1550 | else if (q_data->info->components_num == 3) | |
1551 | return size + 2 * (size / chroma_div); | |
1552 | return size; | |
1553 | } | |
1554 | ||
256bf813 HV |
1555 | static int vicodec_start_streaming(struct vb2_queue *q, |
1556 | unsigned int count) | |
1557 | { | |
1558 | struct vicodec_ctx *ctx = vb2_get_drv_priv(q); | |
1559 | struct vicodec_q_data *q_data = get_q_data(ctx, q->type); | |
cd12b401 | 1560 | struct v4l2_fwht_state *state = &ctx->state; |
cd12b401 | 1561 | const struct v4l2_fwht_pixfmt_info *info = q_data->info; |
9e812549 | 1562 | unsigned int size = q_data->coded_width * q_data->coded_height; |
3b15f68e | 1563 | unsigned int chroma_div; |
19505719 | 1564 | unsigned int total_planes_size; |
997deb81 | 1565 | u8 *new_comp_frame = NULL; |
19505719 | 1566 | |
3b15f68e DH |
1567 | chroma_div = info->width_div * info->height_div; |
1568 | q_data->sequence = 0; | |
1569 | ||
d4d137de | 1570 | v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q); |
76eb24fc | 1571 | |
3b15f68e DH |
1572 | state->gop_cnt = 0; |
1573 | ||
1574 | if ((V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || | |
b3ab1c60 | 1575 | (V4L2_TYPE_IS_CAPTURE(q->type) && ctx->is_enc)) |
3b15f68e DH |
1576 | return 0; |
1577 | ||
35e2e8b5 DH |
1578 | if (info->id == V4L2_PIX_FMT_FWHT || |
1579 | info->id == V4L2_PIX_FMT_FWHT_STATELESS) { | |
3b15f68e DH |
1580 | vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); |
1581 | return -EINVAL; | |
1582 | } | |
997deb81 DH |
1583 | total_planes_size = total_frame_size(q_data); |
1584 | ctx->comp_max_size = total_planes_size; | |
256bf813 | 1585 | |
3b15f68e DH |
1586 | state->visible_width = q_data->visible_width; |
1587 | state->visible_height = q_data->visible_height; | |
1588 | state->coded_width = q_data->coded_width; | |
1589 | state->coded_height = q_data->coded_height; | |
1590 | state->stride = q_data->coded_width * | |
1591 | info->bytesperline_mult; | |
256bf813 | 1592 | |
997deb81 DH |
1593 | if (ctx->is_stateless) { |
1594 | state->ref_stride = state->stride; | |
1595 | return 0; | |
1596 | } | |
86764b88 | 1597 | state->ref_stride = q_data->coded_width * info->luma_alpha_step; |
997deb81 | 1598 | |
bdbfd992 DH |
1599 | state->ref_frame.buf = kvmalloc(total_planes_size, GFP_KERNEL); |
1600 | state->ref_frame.luma = state->ref_frame.buf; | |
3b15f68e DH |
1601 | new_comp_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); |
1602 | ||
1603 | if (!state->ref_frame.luma || !new_comp_frame) { | |
cd12b401 | 1604 | kvfree(state->ref_frame.luma); |
3b15f68e | 1605 | kvfree(new_comp_frame); |
256bf813 HV |
1606 | vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); |
1607 | return -ENOMEM; | |
1608 | } | |
3b15f68e DH |
1609 | /* |
1610 | * if state->compressed_frame was already allocated then | |
1611 | * it contain data of the first frame of the new resolution | |
1612 | */ | |
1613 | if (state->compressed_frame) { | |
1614 | if (ctx->comp_size > ctx->comp_max_size) | |
1615 | ctx->comp_size = ctx->comp_max_size; | |
1616 | ||
1617 | memcpy(new_comp_frame, | |
1618 | state->compressed_frame, ctx->comp_size); | |
1619 | } | |
1620 | ||
1621 | kvfree(state->compressed_frame); | |
1622 | state->compressed_frame = new_comp_frame; | |
1623 | ||
cb804507 | 1624 | if (info->components_num < 3) { |
19505719 DH |
1625 | state->ref_frame.cb = NULL; |
1626 | state->ref_frame.cr = NULL; | |
cb804507 MCC |
1627 | state->ref_frame.alpha = NULL; |
1628 | return 0; | |
19505719 | 1629 | } |
16ecf6df | 1630 | |
cb804507 MCC |
1631 | state->ref_frame.cb = state->ref_frame.luma + size; |
1632 | state->ref_frame.cr = state->ref_frame.cb + size / chroma_div; | |
1633 | ||
3b15f68e | 1634 | if (info->components_num == 4) |
16ecf6df DH |
1635 | state->ref_frame.alpha = |
1636 | state->ref_frame.cr + size / chroma_div; | |
1637 | else | |
1638 | state->ref_frame.alpha = NULL; | |
cb804507 | 1639 | |
256bf813 HV |
1640 | return 0; |
1641 | } | |
1642 | ||
1643 | static void vicodec_stop_streaming(struct vb2_queue *q) | |
1644 | { | |
1645 | struct vicodec_ctx *ctx = vb2_get_drv_priv(q); | |
1646 | ||
1647 | vicodec_return_bufs(q, VB2_BUF_STATE_ERROR); | |
1648 | ||
d4d137de NA |
1649 | v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q); |
1650 | ||
1651 | if (V4L2_TYPE_IS_OUTPUT(q->type) && | |
1652 | v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) | |
1653 | v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); | |
1654 | ||
d17589af HV |
1655 | if (!ctx->is_enc && V4L2_TYPE_IS_OUTPUT(q->type)) |
1656 | ctx->first_source_change_sent = false; | |
1657 | ||
3b15f68e DH |
1658 | if ((!V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || |
1659 | (V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) { | |
997deb81 DH |
1660 | if (!ctx->is_stateless) |
1661 | kvfree(ctx->state.ref_frame.buf); | |
bdbfd992 DH |
1662 | ctx->state.ref_frame.buf = NULL; |
1663 | ctx->state.ref_frame.luma = NULL; | |
3b15f68e DH |
1664 | ctx->comp_max_size = 0; |
1665 | ctx->source_changed = false; | |
1666 | } | |
1667 | if (V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) { | |
1668 | ctx->cur_buf_offset = 0; | |
1669 | ctx->comp_size = 0; | |
1670 | ctx->header_size = 0; | |
1671 | ctx->comp_magic_cnt = 0; | |
333b90fb DA |
1672 | ctx->comp_has_frame = false; |
1673 | ctx->comp_has_next_frame = false; | |
3b15f68e | 1674 | } |
256bf813 HV |
1675 | } |
1676 | ||
997deb81 DH |
1677 | static void vicodec_buf_request_complete(struct vb2_buffer *vb) |
1678 | { | |
1679 | struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | |
1680 | ||
1681 | v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl); | |
1682 | } | |
1683 | ||
1684 | ||
256bf813 | 1685 | static const struct vb2_ops vicodec_qops = { |
997deb81 DH |
1686 | .queue_setup = vicodec_queue_setup, |
1687 | .buf_out_validate = vicodec_buf_out_validate, | |
1688 | .buf_prepare = vicodec_buf_prepare, | |
1689 | .buf_queue = vicodec_buf_queue, | |
1690 | .buf_request_complete = vicodec_buf_request_complete, | |
1691 | .start_streaming = vicodec_start_streaming, | |
1692 | .stop_streaming = vicodec_stop_streaming, | |
1693 | .wait_prepare = vb2_ops_wait_prepare, | |
1694 | .wait_finish = vb2_ops_wait_finish, | |
256bf813 HV |
1695 | }; |
1696 | ||
1697 | static int queue_init(void *priv, struct vb2_queue *src_vq, | |
1698 | struct vb2_queue *dst_vq) | |
1699 | { | |
1700 | struct vicodec_ctx *ctx = priv; | |
1701 | int ret; | |
1702 | ||
1703 | src_vq->type = (multiplanar ? | |
1704 | V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : | |
1705 | V4L2_BUF_TYPE_VIDEO_OUTPUT); | |
1706 | src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; | |
1707 | src_vq->drv_priv = ctx; | |
1708 | src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); | |
1709 | src_vq->ops = &vicodec_qops; | |
1710 | src_vq->mem_ops = &vb2_vmalloc_memops; | |
1711 | src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; | |
fde649b4 DH |
1712 | if (ctx->is_enc) |
1713 | src_vq->lock = &ctx->dev->stateful_enc.mutex; | |
1714 | else if (ctx->is_stateless) | |
1715 | src_vq->lock = &ctx->dev->stateless_dec.mutex; | |
1716 | else | |
1717 | src_vq->lock = &ctx->dev->stateful_dec.mutex; | |
1718 | src_vq->supports_requests = ctx->is_stateless; | |
1719 | src_vq->requires_requests = ctx->is_stateless; | |
256bf813 HV |
1720 | ret = vb2_queue_init(src_vq); |
1721 | if (ret) | |
1722 | return ret; | |
1723 | ||
1724 | dst_vq->type = (multiplanar ? | |
1725 | V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : | |
1726 | V4L2_BUF_TYPE_VIDEO_CAPTURE); | |
1727 | dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; | |
7e8b591d | 1728 | dst_vq->max_num_buffers = 64; |
256bf813 HV |
1729 | dst_vq->drv_priv = ctx; |
1730 | dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); | |
1731 | dst_vq->ops = &vicodec_qops; | |
1732 | dst_vq->mem_ops = &vb2_vmalloc_memops; | |
1733 | dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; | |
1734 | dst_vq->lock = src_vq->lock; | |
1735 | ||
1736 | return vb2_queue_init(dst_vq); | |
1737 | } | |
1738 | ||
997deb81 DH |
1739 | static int vicodec_try_ctrl(struct v4l2_ctrl *ctrl) |
1740 | { | |
1741 | struct vicodec_ctx *ctx = container_of(ctrl->handler, | |
1742 | struct vicodec_ctx, hdl); | |
1743 | const struct v4l2_ctrl_fwht_params *params; | |
1744 | struct vicodec_q_data *q_dst = get_q_data(ctx, | |
1745 | V4L2_BUF_TYPE_VIDEO_CAPTURE); | |
1746 | ||
1747 | switch (ctrl->id) { | |
206bc0f6 | 1748 | case V4L2_CID_STATELESS_FWHT_PARAMS: |
997deb81 DH |
1749 | if (!q_dst->info) |
1750 | return -EINVAL; | |
1751 | params = ctrl->p_new.p_fwht_params; | |
1752 | if (params->width > q_dst->coded_width || | |
1753 | params->width < MIN_WIDTH || | |
1754 | params->height > q_dst->coded_height || | |
1755 | params->height < MIN_HEIGHT) | |
1756 | return -EINVAL; | |
1757 | if (!validate_by_version(params->flags, params->version)) | |
1758 | return -EINVAL; | |
1759 | if (!validate_stateless_params_flags(params, q_dst->info)) | |
1760 | return -EINVAL; | |
1761 | return 0; | |
1762 | default: | |
1763 | return 0; | |
1764 | } | |
1765 | return 0; | |
1766 | } | |
1767 | ||
1768 | static void update_header_from_stateless_params(struct vicodec_ctx *ctx, | |
1769 | const struct v4l2_ctrl_fwht_params *params) | |
1770 | { | |
1771 | struct fwht_cframe_hdr *p_hdr = &ctx->state.header; | |
1772 | ||
1773 | p_hdr->magic1 = FWHT_MAGIC1; | |
1774 | p_hdr->magic2 = FWHT_MAGIC2; | |
1775 | p_hdr->version = htonl(params->version); | |
1776 | p_hdr->width = htonl(params->width); | |
1777 | p_hdr->height = htonl(params->height); | |
1778 | p_hdr->flags = htonl(params->flags); | |
1779 | p_hdr->colorspace = htonl(params->colorspace); | |
1780 | p_hdr->xfer_func = htonl(params->xfer_func); | |
1781 | p_hdr->ycbcr_enc = htonl(params->ycbcr_enc); | |
1782 | p_hdr->quantization = htonl(params->quantization); | |
1783 | } | |
1784 | ||
48568b0c HV |
1785 | static int vicodec_s_ctrl(struct v4l2_ctrl *ctrl) |
1786 | { | |
1787 | struct vicodec_ctx *ctx = container_of(ctrl->handler, | |
1788 | struct vicodec_ctx, hdl); | |
997deb81 | 1789 | const struct v4l2_ctrl_fwht_params *params; |
48568b0c HV |
1790 | |
1791 | switch (ctrl->id) { | |
1792 | case V4L2_CID_MPEG_VIDEO_GOP_SIZE: | |
cd12b401 | 1793 | ctx->state.gop_size = ctrl->val; |
48568b0c | 1794 | return 0; |
2495f39c | 1795 | case V4L2_CID_FWHT_I_FRAME_QP: |
cd12b401 | 1796 | ctx->state.i_frame_qp = ctrl->val; |
48568b0c | 1797 | return 0; |
2495f39c | 1798 | case V4L2_CID_FWHT_P_FRAME_QP: |
cd12b401 | 1799 | ctx->state.p_frame_qp = ctrl->val; |
48568b0c | 1800 | return 0; |
206bc0f6 | 1801 | case V4L2_CID_STATELESS_FWHT_PARAMS: |
997deb81 DH |
1802 | params = ctrl->p_new.p_fwht_params; |
1803 | update_header_from_stateless_params(ctx, params); | |
1804 | ctx->state.ref_frame_ts = params->backward_ref_ts; | |
1805 | return 0; | |
48568b0c HV |
1806 | } |
1807 | return -EINVAL; | |
1808 | } | |
1809 | ||
299553d8 | 1810 | static const struct v4l2_ctrl_ops vicodec_ctrl_ops = { |
48568b0c | 1811 | .s_ctrl = vicodec_s_ctrl, |
997deb81 | 1812 | .try_ctrl = vicodec_try_ctrl, |
48568b0c HV |
1813 | }; |
1814 | ||
2495f39c | 1815 | static const struct v4l2_ctrl_config vicodec_ctrl_stateless_state = { |
997deb81 | 1816 | .ops = &vicodec_ctrl_ops, |
206bc0f6 | 1817 | .id = V4L2_CID_STATELESS_FWHT_PARAMS, |
2495f39c | 1818 | .elem_size = sizeof(struct v4l2_ctrl_fwht_params), |
48568b0c HV |
1819 | }; |
1820 | ||
256bf813 HV |
1821 | /* |
1822 | * File operations | |
1823 | */ | |
1824 | static int vicodec_open(struct file *file) | |
1825 | { | |
d421ba0c | 1826 | const struct v4l2_fwht_pixfmt_info *info = v4l2_fwht_get_pixfmt(0); |
256bf813 HV |
1827 | struct video_device *vfd = video_devdata(file); |
1828 | struct vicodec_dev *dev = video_drvdata(file); | |
1829 | struct vicodec_ctx *ctx = NULL; | |
1830 | struct v4l2_ctrl_handler *hdl; | |
d421ba0c HV |
1831 | unsigned int raw_size; |
1832 | unsigned int comp_size; | |
256bf813 HV |
1833 | int rc = 0; |
1834 | ||
1835 | if (mutex_lock_interruptible(vfd->lock)) | |
1836 | return -ERESTARTSYS; | |
1837 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | |
1838 | if (!ctx) { | |
1839 | rc = -ENOMEM; | |
1840 | goto open_unlock; | |
1841 | } | |
1842 | ||
c022a4a9 | 1843 | if (vfd == &dev->stateful_enc.vfd) |
256bf813 | 1844 | ctx->is_enc = true; |
fde649b4 DH |
1845 | else if (vfd == &dev->stateless_dec.vfd) |
1846 | ctx->is_stateless = true; | |
256bf813 HV |
1847 | |
1848 | v4l2_fh_init(&ctx->fh, video_devdata(file)); | |
1849 | file->private_data = &ctx->fh; | |
1850 | ctx->dev = dev; | |
1851 | hdl = &ctx->hdl; | |
358387d3 | 1852 | v4l2_ctrl_handler_init(hdl, 5); |
48568b0c HV |
1853 | v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, |
1854 | 1, 16, 1, 10); | |
2495f39c DH |
1855 | v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_FWHT_I_FRAME_QP, |
1856 | 1, 31, 1, 20); | |
1857 | v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_FWHT_P_FRAME_QP, | |
1858 | 1, 31, 1, 20); | |
358387d3 HV |
1859 | if (ctx->is_enc) |
1860 | v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, | |
1861 | V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 1, 1, 1); | |
fde649b4 DH |
1862 | if (ctx->is_stateless) |
1863 | v4l2_ctrl_new_custom(hdl, &vicodec_ctrl_stateless_state, NULL); | |
256bf813 HV |
1864 | if (hdl->error) { |
1865 | rc = hdl->error; | |
1866 | v4l2_ctrl_handler_free(hdl); | |
1867 | kfree(ctx); | |
1868 | goto open_unlock; | |
1869 | } | |
1870 | ctx->fh.ctrl_handler = hdl; | |
1871 | v4l2_ctrl_handler_setup(hdl); | |
1872 | ||
35e2e8b5 | 1873 | if (ctx->is_enc) |
d421ba0c | 1874 | ctx->q_data[V4L2_M2M_SRC].info = info; |
35e2e8b5 DH |
1875 | else if (ctx->is_stateless) |
1876 | ctx->q_data[V4L2_M2M_SRC].info = &pixfmt_stateless_fwht; | |
1877 | else | |
1878 | ctx->q_data[V4L2_M2M_SRC].info = &pixfmt_fwht; | |
9e812549 DH |
1879 | ctx->q_data[V4L2_M2M_SRC].coded_width = 1280; |
1880 | ctx->q_data[V4L2_M2M_SRC].coded_height = 720; | |
1881 | ctx->q_data[V4L2_M2M_SRC].visible_width = 1280; | |
1882 | ctx->q_data[V4L2_M2M_SRC].visible_height = 720; | |
d421ba0c HV |
1883 | raw_size = 1280 * 720 * info->sizeimage_mult / info->sizeimage_div; |
1884 | comp_size = 1280 * 720 * pixfmt_fwht.sizeimage_mult / | |
1885 | pixfmt_fwht.sizeimage_div; | |
518f6b9a | 1886 | if (ctx->is_enc) |
d421ba0c | 1887 | ctx->q_data[V4L2_M2M_SRC].sizeimage = raw_size; |
518f6b9a HV |
1888 | else if (ctx->is_stateless) |
1889 | ctx->q_data[V4L2_M2M_SRC].sizeimage = comp_size; | |
55f6fe09 HV |
1890 | else |
1891 | ctx->q_data[V4L2_M2M_SRC].sizeimage = | |
d421ba0c HV |
1892 | comp_size + sizeof(struct fwht_cframe_hdr); |
1893 | ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; | |
3b15f68e | 1894 | if (ctx->is_enc) { |
3b15f68e | 1895 | ctx->q_data[V4L2_M2M_DST].info = &pixfmt_fwht; |
d421ba0c HV |
1896 | ctx->q_data[V4L2_M2M_DST].sizeimage = |
1897 | comp_size + sizeof(struct fwht_cframe_hdr); | |
3b15f68e | 1898 | } else { |
d421ba0c HV |
1899 | ctx->q_data[V4L2_M2M_DST].info = info; |
1900 | ctx->q_data[V4L2_M2M_DST].sizeimage = raw_size; | |
3b15f68e DH |
1901 | } |
1902 | ||
cd12b401 | 1903 | ctx->state.colorspace = V4L2_COLORSPACE_REC709; |
256bf813 | 1904 | |
256bf813 | 1905 | if (ctx->is_enc) { |
c022a4a9 DH |
1906 | ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->stateful_enc.m2m_dev, |
1907 | ctx, &queue_init); | |
1908 | ctx->lock = &dev->stateful_enc.lock; | |
fde649b4 DH |
1909 | } else if (ctx->is_stateless) { |
1910 | ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->stateless_dec.m2m_dev, | |
1911 | ctx, &queue_init); | |
1912 | ctx->lock = &dev->stateless_dec.lock; | |
256bf813 | 1913 | } else { |
c022a4a9 DH |
1914 | ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->stateful_dec.m2m_dev, |
1915 | ctx, &queue_init); | |
1916 | ctx->lock = &dev->stateful_dec.lock; | |
256bf813 HV |
1917 | } |
1918 | ||
1919 | if (IS_ERR(ctx->fh.m2m_ctx)) { | |
1920 | rc = PTR_ERR(ctx->fh.m2m_ctx); | |
1921 | ||
1922 | v4l2_ctrl_handler_free(hdl); | |
1923 | v4l2_fh_exit(&ctx->fh); | |
1924 | kfree(ctx); | |
1925 | goto open_unlock; | |
1926 | } | |
1927 | ||
1928 | v4l2_fh_add(&ctx->fh); | |
1929 | ||
1930 | open_unlock: | |
1931 | mutex_unlock(vfd->lock); | |
1932 | return rc; | |
1933 | } | |
1934 | ||
1935 | static int vicodec_release(struct file *file) | |
1936 | { | |
1937 | struct video_device *vfd = video_devdata(file); | |
1938 | struct vicodec_ctx *ctx = file2ctx(file); | |
1939 | ||
256bf813 HV |
1940 | mutex_lock(vfd->lock); |
1941 | v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); | |
1942 | mutex_unlock(vfd->lock); | |
4d10452c DH |
1943 | v4l2_fh_del(&ctx->fh); |
1944 | v4l2_fh_exit(&ctx->fh); | |
1945 | v4l2_ctrl_handler_free(&ctx->hdl); | |
a04a7a21 | 1946 | kvfree(ctx->state.compressed_frame); |
256bf813 HV |
1947 | kfree(ctx); |
1948 | ||
1949 | return 0; | |
1950 | } | |
1951 | ||
997deb81 DH |
1952 | static int vicodec_request_validate(struct media_request *req) |
1953 | { | |
1954 | struct media_request_object *obj; | |
1955 | struct v4l2_ctrl_handler *parent_hdl, *hdl; | |
1956 | struct vicodec_ctx *ctx = NULL; | |
1957 | struct v4l2_ctrl *ctrl; | |
1958 | unsigned int count; | |
1959 | ||
1960 | list_for_each_entry(obj, &req->objects, list) { | |
1961 | struct vb2_buffer *vb; | |
1962 | ||
1963 | if (vb2_request_object_is_buffer(obj)) { | |
1964 | vb = container_of(obj, struct vb2_buffer, req_obj); | |
1965 | ctx = vb2_get_drv_priv(vb->vb2_queue); | |
1966 | ||
1967 | break; | |
1968 | } | |
1969 | } | |
1970 | ||
1971 | if (!ctx) { | |
1972 | pr_err("No buffer was provided with the request\n"); | |
1973 | return -ENOENT; | |
1974 | } | |
1975 | ||
1976 | count = vb2_request_buffer_cnt(req); | |
1977 | if (!count) { | |
1978 | v4l2_info(&ctx->dev->v4l2_dev, | |
1979 | "No buffer was provided with the request\n"); | |
1980 | return -ENOENT; | |
1981 | } else if (count > 1) { | |
1982 | v4l2_info(&ctx->dev->v4l2_dev, | |
1983 | "More than one buffer was provided with the request\n"); | |
1984 | return -EINVAL; | |
1985 | } | |
1986 | ||
1987 | parent_hdl = &ctx->hdl; | |
1988 | ||
1989 | hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl); | |
1990 | if (!hdl) { | |
1991 | v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control\n"); | |
1992 | return -ENOENT; | |
1993 | } | |
1994 | ctrl = v4l2_ctrl_request_hdl_ctrl_find(hdl, | |
1995 | vicodec_ctrl_stateless_state.id); | |
2e7c8fb8 | 1996 | v4l2_ctrl_request_hdl_put(hdl); |
997deb81 DH |
1997 | if (!ctrl) { |
1998 | v4l2_info(&ctx->dev->v4l2_dev, | |
1999 | "Missing required codec control\n"); | |
2000 | return -ENOENT; | |
2001 | } | |
2002 | ||
2003 | return vb2_request_validate(req); | |
2004 | } | |
2005 | ||
256bf813 HV |
2006 | static const struct v4l2_file_operations vicodec_fops = { |
2007 | .owner = THIS_MODULE, | |
2008 | .open = vicodec_open, | |
2009 | .release = vicodec_release, | |
2010 | .poll = v4l2_m2m_fop_poll, | |
2011 | .unlocked_ioctl = video_ioctl2, | |
2012 | .mmap = v4l2_m2m_fop_mmap, | |
2013 | }; | |
2014 | ||
2015 | static const struct video_device vicodec_videodev = { | |
2016 | .name = VICODEC_NAME, | |
2017 | .vfl_dir = VFL_DIR_M2M, | |
2018 | .fops = &vicodec_fops, | |
2019 | .ioctl_ops = &vicodec_ioctl_ops, | |
2020 | .minor = -1, | |
2021 | .release = video_device_release_empty, | |
2022 | }; | |
2023 | ||
997deb81 DH |
2024 | static const struct media_device_ops vicodec_m2m_media_ops = { |
2025 | .req_validate = vicodec_request_validate, | |
2026 | .req_queue = v4l2_m2m_request_queue, | |
2027 | }; | |
2028 | ||
256bf813 HV |
2029 | static const struct v4l2_m2m_ops m2m_ops = { |
2030 | .device_run = device_run, | |
256bf813 HV |
2031 | .job_ready = job_ready, |
2032 | }; | |
2033 | ||
c022a4a9 DH |
2034 | static int register_instance(struct vicodec_dev *dev, |
2035 | struct vicodec_dev_instance *dev_instance, | |
deaf6b0a | 2036 | const char *name, bool is_enc, bool is_stateless) |
c022a4a9 DH |
2037 | { |
2038 | struct video_device *vfd; | |
2039 | int ret; | |
2040 | ||
2041 | spin_lock_init(&dev_instance->lock); | |
2042 | mutex_init(&dev_instance->mutex); | |
2043 | dev_instance->m2m_dev = v4l2_m2m_init(&m2m_ops); | |
2044 | if (IS_ERR(dev_instance->m2m_dev)) { | |
2045 | v4l2_err(&dev->v4l2_dev, "Failed to init vicodec enc device\n"); | |
2046 | return PTR_ERR(dev_instance->m2m_dev); | |
2047 | } | |
2048 | ||
2049 | dev_instance->vfd = vicodec_videodev; | |
2050 | vfd = &dev_instance->vfd; | |
2051 | vfd->lock = &dev_instance->mutex; | |
2052 | vfd->v4l2_dev = &dev->v4l2_dev; | |
2053 | strscpy(vfd->name, name, sizeof(vfd->name)); | |
2054 | vfd->device_caps = V4L2_CAP_STREAMING | | |
2055 | (multiplanar ? V4L2_CAP_VIDEO_M2M_MPLANE : V4L2_CAP_VIDEO_M2M); | |
deaf6b0a | 2056 | if (is_enc || is_stateless) { |
c022a4a9 DH |
2057 | v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD); |
2058 | v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD); | |
deaf6b0a PK |
2059 | } |
2060 | if (!is_enc) { | |
c022a4a9 DH |
2061 | v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD); |
2062 | v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD); | |
2063 | } | |
2064 | video_set_drvdata(vfd, dev); | |
2065 | ||
70cad449 | 2066 | ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); |
c022a4a9 DH |
2067 | if (ret) { |
2068 | v4l2_err(&dev->v4l2_dev, "Failed to register video device '%s'\n", name); | |
2069 | v4l2_m2m_release(dev_instance->m2m_dev); | |
2070 | return ret; | |
2071 | } | |
2072 | v4l2_info(&dev->v4l2_dev, "Device '%s' registered as /dev/video%d\n", | |
2073 | name, vfd->num); | |
2074 | return 0; | |
2075 | } | |
2076 | ||
0783525f HV |
2077 | static void vicodec_v4l2_dev_release(struct v4l2_device *v4l2_dev) |
2078 | { | |
2079 | struct vicodec_dev *dev = container_of(v4l2_dev, struct vicodec_dev, v4l2_dev); | |
2080 | ||
2081 | v4l2_device_unregister(&dev->v4l2_dev); | |
2082 | v4l2_m2m_release(dev->stateful_enc.m2m_dev); | |
2083 | v4l2_m2m_release(dev->stateful_dec.m2m_dev); | |
2084 | v4l2_m2m_release(dev->stateless_dec.m2m_dev); | |
693c5f14 HV |
2085 | #ifdef CONFIG_MEDIA_CONTROLLER |
2086 | media_device_cleanup(&dev->mdev); | |
2087 | #endif | |
0783525f HV |
2088 | kfree(dev); |
2089 | } | |
2090 | ||
256bf813 HV |
2091 | static int vicodec_probe(struct platform_device *pdev) |
2092 | { | |
2093 | struct vicodec_dev *dev; | |
256bf813 HV |
2094 | int ret; |
2095 | ||
0783525f | 2096 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
256bf813 HV |
2097 | if (!dev) |
2098 | return -ENOMEM; | |
2099 | ||
256bf813 HV |
2100 | ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); |
2101 | if (ret) | |
0783525f HV |
2102 | goto free_dev; |
2103 | ||
2104 | dev->v4l2_dev.release = vicodec_v4l2_dev_release; | |
256bf813 HV |
2105 | |
2106 | #ifdef CONFIG_MEDIA_CONTROLLER | |
2107 | dev->mdev.dev = &pdev->dev; | |
c0decac1 | 2108 | strscpy(dev->mdev.model, "vicodec", sizeof(dev->mdev.model)); |
0247c75b HV |
2109 | strscpy(dev->mdev.bus_info, "platform:vicodec", |
2110 | sizeof(dev->mdev.bus_info)); | |
256bf813 | 2111 | media_device_init(&dev->mdev); |
997deb81 | 2112 | dev->mdev.ops = &vicodec_m2m_media_ops; |
256bf813 HV |
2113 | dev->v4l2_dev.mdev = &dev->mdev; |
2114 | #endif | |
2115 | ||
256bf813 HV |
2116 | platform_set_drvdata(pdev, dev); |
2117 | ||
f36592e7 | 2118 | ret = register_instance(dev, &dev->stateful_enc, "stateful-encoder", |
deaf6b0a | 2119 | true, false); |
f36592e7 | 2120 | if (ret) |
256bf813 | 2121 | goto unreg_dev; |
256bf813 | 2122 | |
f36592e7 | 2123 | ret = register_instance(dev, &dev->stateful_dec, "stateful-decoder", |
deaf6b0a | 2124 | false, false); |
f36592e7 | 2125 | if (ret) |
c022a4a9 | 2126 | goto unreg_sf_enc; |
256bf813 | 2127 | |
f36592e7 | 2128 | ret = register_instance(dev, &dev->stateless_dec, "stateless-decoder", |
deaf6b0a | 2129 | false, true); |
f36592e7 | 2130 | if (ret) |
fde649b4 DH |
2131 | goto unreg_sf_dec; |
2132 | ||
256bf813 | 2133 | #ifdef CONFIG_MEDIA_CONTROLLER |
c022a4a9 DH |
2134 | ret = v4l2_m2m_register_media_controller(dev->stateful_enc.m2m_dev, |
2135 | &dev->stateful_enc.vfd, | |
2136 | MEDIA_ENT_F_PROC_VIDEO_ENCODER); | |
256bf813 | 2137 | if (ret) { |
c022a4a9 | 2138 | v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller for enc\n"); |
256bf813 HV |
2139 | goto unreg_m2m; |
2140 | } | |
2141 | ||
c022a4a9 DH |
2142 | ret = v4l2_m2m_register_media_controller(dev->stateful_dec.m2m_dev, |
2143 | &dev->stateful_dec.vfd, | |
2144 | MEDIA_ENT_F_PROC_VIDEO_DECODER); | |
256bf813 | 2145 | if (ret) { |
c022a4a9 DH |
2146 | v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller for dec\n"); |
2147 | goto unreg_m2m_sf_enc_mc; | |
256bf813 HV |
2148 | } |
2149 | ||
fde649b4 DH |
2150 | ret = v4l2_m2m_register_media_controller(dev->stateless_dec.m2m_dev, |
2151 | &dev->stateless_dec.vfd, | |
2152 | MEDIA_ENT_F_PROC_VIDEO_DECODER); | |
2153 | if (ret) { | |
2154 | v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller for stateless dec\n"); | |
2155 | goto unreg_m2m_sf_dec_mc; | |
2156 | } | |
2157 | ||
256bf813 HV |
2158 | ret = media_device_register(&dev->mdev); |
2159 | if (ret) { | |
2160 | v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n"); | |
fde649b4 | 2161 | goto unreg_m2m_sl_dec_mc; |
256bf813 HV |
2162 | } |
2163 | #endif | |
2164 | return 0; | |
2165 | ||
2166 | #ifdef CONFIG_MEDIA_CONTROLLER | |
fde649b4 DH |
2167 | unreg_m2m_sl_dec_mc: |
2168 | v4l2_m2m_unregister_media_controller(dev->stateless_dec.m2m_dev); | |
c022a4a9 DH |
2169 | unreg_m2m_sf_dec_mc: |
2170 | v4l2_m2m_unregister_media_controller(dev->stateful_dec.m2m_dev); | |
2171 | unreg_m2m_sf_enc_mc: | |
2172 | v4l2_m2m_unregister_media_controller(dev->stateful_enc.m2m_dev); | |
256bf813 | 2173 | unreg_m2m: |
fde649b4 DH |
2174 | video_unregister_device(&dev->stateless_dec.vfd); |
2175 | v4l2_m2m_release(dev->stateless_dec.m2m_dev); | |
2176 | #endif | |
2177 | unreg_sf_dec: | |
c022a4a9 DH |
2178 | video_unregister_device(&dev->stateful_dec.vfd); |
2179 | v4l2_m2m_release(dev->stateful_dec.m2m_dev); | |
c022a4a9 DH |
2180 | unreg_sf_enc: |
2181 | video_unregister_device(&dev->stateful_enc.vfd); | |
2182 | v4l2_m2m_release(dev->stateful_enc.m2m_dev); | |
256bf813 HV |
2183 | unreg_dev: |
2184 | v4l2_device_unregister(&dev->v4l2_dev); | |
0783525f HV |
2185 | free_dev: |
2186 | kfree(dev); | |
256bf813 HV |
2187 | |
2188 | return ret; | |
2189 | } | |
2190 | ||
f3675a01 | 2191 | static void vicodec_remove(struct platform_device *pdev) |
256bf813 HV |
2192 | { |
2193 | struct vicodec_dev *dev = platform_get_drvdata(pdev); | |
2194 | ||
2195 | v4l2_info(&dev->v4l2_dev, "Removing " VICODEC_NAME); | |
2196 | ||
2197 | #ifdef CONFIG_MEDIA_CONTROLLER | |
2198 | media_device_unregister(&dev->mdev); | |
c022a4a9 DH |
2199 | v4l2_m2m_unregister_media_controller(dev->stateful_enc.m2m_dev); |
2200 | v4l2_m2m_unregister_media_controller(dev->stateful_dec.m2m_dev); | |
fde649b4 | 2201 | v4l2_m2m_unregister_media_controller(dev->stateless_dec.m2m_dev); |
256bf813 HV |
2202 | #endif |
2203 | ||
c022a4a9 DH |
2204 | video_unregister_device(&dev->stateful_enc.vfd); |
2205 | video_unregister_device(&dev->stateful_dec.vfd); | |
fde649b4 | 2206 | video_unregister_device(&dev->stateless_dec.vfd); |
0783525f | 2207 | v4l2_device_put(&dev->v4l2_dev); |
256bf813 HV |
2208 | } |
2209 | ||
2210 | static struct platform_driver vicodec_pdrv = { | |
2211 | .probe = vicodec_probe, | |
f3675a01 | 2212 | .remove_new = vicodec_remove, |
256bf813 HV |
2213 | .driver = { |
2214 | .name = VICODEC_NAME, | |
2215 | }, | |
2216 | }; | |
2217 | ||
2218 | static void __exit vicodec_exit(void) | |
2219 | { | |
2220 | platform_driver_unregister(&vicodec_pdrv); | |
2221 | platform_device_unregister(&vicodec_pdev); | |
2222 | } | |
2223 | ||
2224 | static int __init vicodec_init(void) | |
2225 | { | |
2226 | int ret; | |
2227 | ||
2228 | ret = platform_device_register(&vicodec_pdev); | |
2229 | if (ret) | |
2230 | return ret; | |
2231 | ||
2232 | ret = platform_driver_register(&vicodec_pdrv); | |
2233 | if (ret) | |
2234 | platform_device_unregister(&vicodec_pdev); | |
2235 | ||
2236 | return ret; | |
2237 | } | |
2238 | ||
2239 | module_init(vicodec_init); | |
2240 | module_exit(vicodec_exit); |