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