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