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