This source file includes following definitions.
- cedrus_file2ctx
- cedrus_find_format
- cedrus_check_format
- cedrus_prepare_format
- cedrus_querycap
- cedrus_enum_fmt
- cedrus_enum_fmt_vid_cap
- cedrus_enum_fmt_vid_out
- cedrus_g_fmt_vid_cap
- cedrus_g_fmt_vid_out
- cedrus_try_fmt_vid_cap
- cedrus_try_fmt_vid_out
- cedrus_s_fmt_vid_cap
- cedrus_s_fmt_vid_out
- cedrus_queue_setup
- cedrus_queue_cleanup
- cedrus_buf_out_validate
- cedrus_buf_prepare
- cedrus_start_streaming
- cedrus_stop_streaming
- cedrus_buf_queue
- cedrus_buf_request_complete
- cedrus_queue_init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 #include <media/videobuf2-dma-contig.h>
17 #include <media/v4l2-device.h>
18 #include <media/v4l2-ioctl.h>
19 #include <media/v4l2-event.h>
20 #include <media/v4l2-mem2mem.h>
21
22 #include "cedrus.h"
23 #include "cedrus_video.h"
24 #include "cedrus_dec.h"
25 #include "cedrus_hw.h"
26
27 #define CEDRUS_DECODE_SRC BIT(0)
28 #define CEDRUS_DECODE_DST BIT(1)
29
30 #define CEDRUS_MIN_WIDTH 16U
31 #define CEDRUS_MIN_HEIGHT 16U
32 #define CEDRUS_MAX_WIDTH 3840U
33 #define CEDRUS_MAX_HEIGHT 2160U
34
35 static struct cedrus_format cedrus_formats[] = {
36 {
37 .pixelformat = V4L2_PIX_FMT_MPEG2_SLICE,
38 .directions = CEDRUS_DECODE_SRC,
39 },
40 {
41 .pixelformat = V4L2_PIX_FMT_H264_SLICE,
42 .directions = CEDRUS_DECODE_SRC,
43 },
44 {
45 .pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12,
46 .directions = CEDRUS_DECODE_DST,
47 },
48 {
49 .pixelformat = V4L2_PIX_FMT_NV12,
50 .directions = CEDRUS_DECODE_DST,
51 .capabilities = CEDRUS_CAPABILITY_UNTILED,
52 },
53 };
54
55 #define CEDRUS_FORMATS_COUNT ARRAY_SIZE(cedrus_formats)
56
57 static inline struct cedrus_ctx *cedrus_file2ctx(struct file *file)
58 {
59 return container_of(file->private_data, struct cedrus_ctx, fh);
60 }
61
62 static struct cedrus_format *cedrus_find_format(u32 pixelformat, u32 directions,
63 unsigned int capabilities)
64 {
65 struct cedrus_format *fmt;
66 unsigned int i;
67
68 for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) {
69 fmt = &cedrus_formats[i];
70
71 if (fmt->capabilities && (fmt->capabilities & capabilities) !=
72 fmt->capabilities)
73 continue;
74
75 if (fmt->pixelformat == pixelformat &&
76 (fmt->directions & directions) != 0)
77 break;
78 }
79
80 if (i == CEDRUS_FORMATS_COUNT)
81 return NULL;
82
83 return &cedrus_formats[i];
84 }
85
86 static bool cedrus_check_format(u32 pixelformat, u32 directions,
87 unsigned int capabilities)
88 {
89 return cedrus_find_format(pixelformat, directions, capabilities);
90 }
91
92 static void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt)
93 {
94 unsigned int width = pix_fmt->width;
95 unsigned int height = pix_fmt->height;
96 unsigned int sizeimage = pix_fmt->sizeimage;
97 unsigned int bytesperline = pix_fmt->bytesperline;
98
99 pix_fmt->field = V4L2_FIELD_NONE;
100
101
102 width = clamp(width, CEDRUS_MIN_WIDTH, CEDRUS_MAX_WIDTH);
103 height = clamp(height, CEDRUS_MIN_HEIGHT, CEDRUS_MAX_HEIGHT);
104
105 switch (pix_fmt->pixelformat) {
106 case V4L2_PIX_FMT_MPEG2_SLICE:
107 case V4L2_PIX_FMT_H264_SLICE:
108
109 bytesperline = 0;
110
111 break;
112
113 case V4L2_PIX_FMT_SUNXI_TILED_NV12:
114
115 bytesperline = ALIGN(width, 32);
116
117
118 height = ALIGN(height, 32);
119
120
121 sizeimage = bytesperline * height;
122
123
124 sizeimage += bytesperline * height / 2;
125
126 break;
127
128 case V4L2_PIX_FMT_NV12:
129
130 bytesperline = ALIGN(width, 16);
131
132
133 height = ALIGN(height, 16);
134
135
136 sizeimage = bytesperline * height;
137
138
139 sizeimage += bytesperline * height / 2;
140
141 break;
142 }
143
144 pix_fmt->width = width;
145 pix_fmt->height = height;
146
147 pix_fmt->bytesperline = bytesperline;
148 pix_fmt->sizeimage = sizeimage;
149 }
150
151 static int cedrus_querycap(struct file *file, void *priv,
152 struct v4l2_capability *cap)
153 {
154 strscpy(cap->driver, CEDRUS_NAME, sizeof(cap->driver));
155 strscpy(cap->card, CEDRUS_NAME, sizeof(cap->card));
156 snprintf(cap->bus_info, sizeof(cap->bus_info),
157 "platform:%s", CEDRUS_NAME);
158
159 return 0;
160 }
161
162 static int cedrus_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
163 u32 direction)
164 {
165 struct cedrus_ctx *ctx = cedrus_file2ctx(file);
166 struct cedrus_dev *dev = ctx->dev;
167 unsigned int capabilities = dev->capabilities;
168 struct cedrus_format *fmt;
169 unsigned int i, index;
170
171
172 index = 0;
173
174 for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) {
175 fmt = &cedrus_formats[i];
176
177 if (fmt->capabilities && (fmt->capabilities & capabilities) !=
178 fmt->capabilities)
179 continue;
180
181 if (!(cedrus_formats[i].directions & direction))
182 continue;
183
184 if (index == f->index)
185 break;
186
187 index++;
188 }
189
190
191 if (i < CEDRUS_FORMATS_COUNT) {
192 f->pixelformat = cedrus_formats[i].pixelformat;
193
194 return 0;
195 }
196
197 return -EINVAL;
198 }
199
200 static int cedrus_enum_fmt_vid_cap(struct file *file, void *priv,
201 struct v4l2_fmtdesc *f)
202 {
203 return cedrus_enum_fmt(file, f, CEDRUS_DECODE_DST);
204 }
205
206 static int cedrus_enum_fmt_vid_out(struct file *file, void *priv,
207 struct v4l2_fmtdesc *f)
208 {
209 return cedrus_enum_fmt(file, f, CEDRUS_DECODE_SRC);
210 }
211
212 static int cedrus_g_fmt_vid_cap(struct file *file, void *priv,
213 struct v4l2_format *f)
214 {
215 struct cedrus_ctx *ctx = cedrus_file2ctx(file);
216
217
218 if (!ctx->dst_fmt.width || !ctx->dst_fmt.height) {
219 f->fmt.pix.pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12;
220 cedrus_prepare_format(&f->fmt.pix);
221
222 return 0;
223 }
224
225 f->fmt.pix = ctx->dst_fmt;
226
227 return 0;
228 }
229
230 static int cedrus_g_fmt_vid_out(struct file *file, void *priv,
231 struct v4l2_format *f)
232 {
233 struct cedrus_ctx *ctx = cedrus_file2ctx(file);
234
235
236 if (!ctx->dst_fmt.width || !ctx->dst_fmt.height) {
237 f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG2_SLICE;
238 f->fmt.pix.sizeimage = SZ_1K;
239 cedrus_prepare_format(&f->fmt.pix);
240
241 return 0;
242 }
243
244 f->fmt.pix = ctx->src_fmt;
245
246 return 0;
247 }
248
249 static int cedrus_try_fmt_vid_cap(struct file *file, void *priv,
250 struct v4l2_format *f)
251 {
252 struct cedrus_ctx *ctx = cedrus_file2ctx(file);
253 struct cedrus_dev *dev = ctx->dev;
254 struct v4l2_pix_format *pix_fmt = &f->fmt.pix;
255
256 if (!cedrus_check_format(pix_fmt->pixelformat, CEDRUS_DECODE_DST,
257 dev->capabilities))
258 return -EINVAL;
259
260 cedrus_prepare_format(pix_fmt);
261
262 return 0;
263 }
264
265 static int cedrus_try_fmt_vid_out(struct file *file, void *priv,
266 struct v4l2_format *f)
267 {
268 struct cedrus_ctx *ctx = cedrus_file2ctx(file);
269 struct cedrus_dev *dev = ctx->dev;
270 struct v4l2_pix_format *pix_fmt = &f->fmt.pix;
271
272 if (!cedrus_check_format(pix_fmt->pixelformat, CEDRUS_DECODE_SRC,
273 dev->capabilities))
274 return -EINVAL;
275
276
277 if (pix_fmt->sizeimage == 0)
278 return -EINVAL;
279
280 cedrus_prepare_format(pix_fmt);
281
282 return 0;
283 }
284
285 static int cedrus_s_fmt_vid_cap(struct file *file, void *priv,
286 struct v4l2_format *f)
287 {
288 struct cedrus_ctx *ctx = cedrus_file2ctx(file);
289 struct cedrus_dev *dev = ctx->dev;
290 struct vb2_queue *vq;
291 int ret;
292
293 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
294 if (vb2_is_busy(vq))
295 return -EBUSY;
296
297 ret = cedrus_try_fmt_vid_cap(file, priv, f);
298 if (ret)
299 return ret;
300
301 ctx->dst_fmt = f->fmt.pix;
302
303 cedrus_dst_format_set(dev, &ctx->dst_fmt);
304
305 return 0;
306 }
307
308 static int cedrus_s_fmt_vid_out(struct file *file, void *priv,
309 struct v4l2_format *f)
310 {
311 struct cedrus_ctx *ctx = cedrus_file2ctx(file);
312 struct vb2_queue *vq;
313 int ret;
314
315 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
316 if (vb2_is_busy(vq))
317 return -EBUSY;
318
319 ret = cedrus_try_fmt_vid_out(file, priv, f);
320 if (ret)
321 return ret;
322
323 ctx->src_fmt = f->fmt.pix;
324
325
326 ctx->dst_fmt.colorspace = f->fmt.pix.colorspace;
327 ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func;
328 ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
329 ctx->dst_fmt.quantization = f->fmt.pix.quantization;
330
331 return 0;
332 }
333
334 const struct v4l2_ioctl_ops cedrus_ioctl_ops = {
335 .vidioc_querycap = cedrus_querycap,
336
337 .vidioc_enum_fmt_vid_cap = cedrus_enum_fmt_vid_cap,
338 .vidioc_g_fmt_vid_cap = cedrus_g_fmt_vid_cap,
339 .vidioc_try_fmt_vid_cap = cedrus_try_fmt_vid_cap,
340 .vidioc_s_fmt_vid_cap = cedrus_s_fmt_vid_cap,
341
342 .vidioc_enum_fmt_vid_out = cedrus_enum_fmt_vid_out,
343 .vidioc_g_fmt_vid_out = cedrus_g_fmt_vid_out,
344 .vidioc_try_fmt_vid_out = cedrus_try_fmt_vid_out,
345 .vidioc_s_fmt_vid_out = cedrus_s_fmt_vid_out,
346
347 .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
348 .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
349 .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
350 .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
351 .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
352 .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
353 .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
354
355 .vidioc_streamon = v4l2_m2m_ioctl_streamon,
356 .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
357
358 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
359 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
360 };
361
362 static int cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
363 unsigned int *nplanes, unsigned int sizes[],
364 struct device *alloc_devs[])
365 {
366 struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
367 struct cedrus_dev *dev = ctx->dev;
368 struct v4l2_pix_format *pix_fmt;
369 u32 directions;
370
371 if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
372 directions = CEDRUS_DECODE_SRC;
373 pix_fmt = &ctx->src_fmt;
374 } else {
375 directions = CEDRUS_DECODE_DST;
376 pix_fmt = &ctx->dst_fmt;
377 }
378
379 if (!cedrus_check_format(pix_fmt->pixelformat, directions,
380 dev->capabilities))
381 return -EINVAL;
382
383 if (*nplanes) {
384 if (sizes[0] < pix_fmt->sizeimage)
385 return -EINVAL;
386 } else {
387 sizes[0] = pix_fmt->sizeimage;
388 *nplanes = 1;
389 }
390
391 return 0;
392 }
393
394 static void cedrus_queue_cleanup(struct vb2_queue *vq, u32 state)
395 {
396 struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
397 struct vb2_v4l2_buffer *vbuf;
398
399 for (;;) {
400 if (V4L2_TYPE_IS_OUTPUT(vq->type))
401 vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
402 else
403 vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
404
405 if (!vbuf)
406 return;
407
408 v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
409 &ctx->hdl);
410 v4l2_m2m_buf_done(vbuf, state);
411 }
412 }
413
414 static int cedrus_buf_out_validate(struct vb2_buffer *vb)
415 {
416 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
417
418 vbuf->field = V4L2_FIELD_NONE;
419 return 0;
420 }
421
422 static int cedrus_buf_prepare(struct vb2_buffer *vb)
423 {
424 struct vb2_queue *vq = vb->vb2_queue;
425 struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
426 struct v4l2_pix_format *pix_fmt;
427
428 if (V4L2_TYPE_IS_OUTPUT(vq->type))
429 pix_fmt = &ctx->src_fmt;
430 else
431 pix_fmt = &ctx->dst_fmt;
432
433 if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
434 return -EINVAL;
435
436 vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
437
438 return 0;
439 }
440
441 static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count)
442 {
443 struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
444 struct cedrus_dev *dev = ctx->dev;
445 int ret = 0;
446
447 switch (ctx->src_fmt.pixelformat) {
448 case V4L2_PIX_FMT_MPEG2_SLICE:
449 ctx->current_codec = CEDRUS_CODEC_MPEG2;
450 break;
451
452 case V4L2_PIX_FMT_H264_SLICE:
453 ctx->current_codec = CEDRUS_CODEC_H264;
454 break;
455
456 default:
457 return -EINVAL;
458 }
459
460 if (V4L2_TYPE_IS_OUTPUT(vq->type) &&
461 dev->dec_ops[ctx->current_codec]->start)
462 ret = dev->dec_ops[ctx->current_codec]->start(ctx);
463
464 if (ret)
465 cedrus_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
466
467 return ret;
468 }
469
470 static void cedrus_stop_streaming(struct vb2_queue *vq)
471 {
472 struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
473 struct cedrus_dev *dev = ctx->dev;
474
475 if (V4L2_TYPE_IS_OUTPUT(vq->type) &&
476 dev->dec_ops[ctx->current_codec]->stop)
477 dev->dec_ops[ctx->current_codec]->stop(ctx);
478
479 cedrus_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
480 }
481
482 static void cedrus_buf_queue(struct vb2_buffer *vb)
483 {
484 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
485 struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
486
487 v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
488 }
489
490 static void cedrus_buf_request_complete(struct vb2_buffer *vb)
491 {
492 struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
493
494 v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
495 }
496
497 static struct vb2_ops cedrus_qops = {
498 .queue_setup = cedrus_queue_setup,
499 .buf_prepare = cedrus_buf_prepare,
500 .buf_queue = cedrus_buf_queue,
501 .buf_out_validate = cedrus_buf_out_validate,
502 .buf_request_complete = cedrus_buf_request_complete,
503 .start_streaming = cedrus_start_streaming,
504 .stop_streaming = cedrus_stop_streaming,
505 .wait_prepare = vb2_ops_wait_prepare,
506 .wait_finish = vb2_ops_wait_finish,
507 };
508
509 int cedrus_queue_init(void *priv, struct vb2_queue *src_vq,
510 struct vb2_queue *dst_vq)
511 {
512 struct cedrus_ctx *ctx = priv;
513 int ret;
514
515 src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
516 src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
517 src_vq->drv_priv = ctx;
518 src_vq->buf_struct_size = sizeof(struct cedrus_buffer);
519 src_vq->min_buffers_needed = 1;
520 src_vq->ops = &cedrus_qops;
521 src_vq->mem_ops = &vb2_dma_contig_memops;
522 src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
523 src_vq->lock = &ctx->dev->dev_mutex;
524 src_vq->dev = ctx->dev->dev;
525 src_vq->supports_requests = true;
526 src_vq->requires_requests = true;
527
528 ret = vb2_queue_init(src_vq);
529 if (ret)
530 return ret;
531
532 dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
533 dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
534 dst_vq->drv_priv = ctx;
535 dst_vq->buf_struct_size = sizeof(struct cedrus_buffer);
536 dst_vq->min_buffers_needed = 1;
537 dst_vq->ops = &cedrus_qops;
538 dst_vq->mem_ops = &vb2_dma_contig_memops;
539 dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
540 dst_vq->lock = &ctx->dev->dev_mutex;
541 dst_vq->dev = ctx->dev->dev;
542
543 return vb2_queue_init(dst_vq);
544 }