]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/staging/media/sunxi/cedrus/cedrus_video.c
media: cedrus: Add H264 decoding support
[linux.git] / drivers / staging / media / sunxi / cedrus / cedrus_video.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Cedrus VPU driver
4  *
5  * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
6  * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
7  * Copyright (C) 2018 Bootlin
8  *
9  * Based on the vim2m driver, that is:
10  *
11  * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
12  * Pawel Osciak, <pawel@osciak.com>
13  * Marek Szyprowski, <m.szyprowski@samsung.com>
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_RAW,
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         /* Limit to hardware min/max. */
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_RAW:
108                 /* Zero bytes per line for encoded source. */
109                 bytesperline = 0;
110
111                 break;
112
113         case V4L2_PIX_FMT_SUNXI_TILED_NV12:
114                 /* 32-aligned stride. */
115                 bytesperline = ALIGN(width, 32);
116
117                 /* 32-aligned height. */
118                 height = ALIGN(height, 32);
119
120                 /* Luma plane size. */
121                 sizeimage = bytesperline * height;
122
123                 /* Chroma plane size. */
124                 sizeimage += bytesperline * height / 2;
125
126                 break;
127
128         case V4L2_PIX_FMT_NV12:
129                 /* 16-aligned stride. */
130                 bytesperline = ALIGN(width, 16);
131
132                 /* 16-aligned height. */
133                 height = ALIGN(height, 16);
134
135                 /* Luma plane size. */
136                 sizeimage = bytesperline * height;
137
138                 /* Chroma plane size. */
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         /* Index among formats that match the requested direction. */
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         /* Matched format. */
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         /* Fall back to dummy default by lack of hardware configuration. */
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         /* Fall back to dummy default by lack of hardware configuration. */
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         /* Source image size has to be provided by userspace. */
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         /* Propagate colorspace information to capture. */
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_init(struct vb2_buffer *vb)
415 {
416         struct vb2_queue *vq = vb->vb2_queue;
417         struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
418
419         if (!V4L2_TYPE_IS_OUTPUT(vq->type))
420                 ctx->dst_bufs[vb->index] = vb;
421
422         return 0;
423 }
424
425 static void cedrus_buf_cleanup(struct vb2_buffer *vb)
426 {
427         struct vb2_queue *vq = vb->vb2_queue;
428         struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
429
430         if (!V4L2_TYPE_IS_OUTPUT(vq->type))
431                 ctx->dst_bufs[vb->index] = NULL;
432 }
433
434 static int cedrus_buf_out_validate(struct vb2_buffer *vb)
435 {
436         struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
437
438         vbuf->field = V4L2_FIELD_NONE;
439         return 0;
440 }
441
442 static int cedrus_buf_prepare(struct vb2_buffer *vb)
443 {
444         struct vb2_queue *vq = vb->vb2_queue;
445         struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
446         struct v4l2_pix_format *pix_fmt;
447
448         if (V4L2_TYPE_IS_OUTPUT(vq->type))
449                 pix_fmt = &ctx->src_fmt;
450         else
451                 pix_fmt = &ctx->dst_fmt;
452
453         if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
454                 return -EINVAL;
455
456         vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
457
458         return 0;
459 }
460
461 static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count)
462 {
463         struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
464         struct cedrus_dev *dev = ctx->dev;
465         int ret = 0;
466
467         switch (ctx->src_fmt.pixelformat) {
468         case V4L2_PIX_FMT_MPEG2_SLICE:
469                 ctx->current_codec = CEDRUS_CODEC_MPEG2;
470                 break;
471
472         case V4L2_PIX_FMT_H264_SLICE_RAW:
473                 ctx->current_codec = CEDRUS_CODEC_H264;
474                 break;
475
476         default:
477                 return -EINVAL;
478         }
479
480         if (V4L2_TYPE_IS_OUTPUT(vq->type) &&
481             dev->dec_ops[ctx->current_codec]->start)
482                 ret = dev->dec_ops[ctx->current_codec]->start(ctx);
483
484         if (ret)
485                 cedrus_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
486
487         return ret;
488 }
489
490 static void cedrus_stop_streaming(struct vb2_queue *vq)
491 {
492         struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
493         struct cedrus_dev *dev = ctx->dev;
494
495         if (V4L2_TYPE_IS_OUTPUT(vq->type) &&
496             dev->dec_ops[ctx->current_codec]->stop)
497                 dev->dec_ops[ctx->current_codec]->stop(ctx);
498
499         cedrus_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
500 }
501
502 static void cedrus_buf_queue(struct vb2_buffer *vb)
503 {
504         struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
505         struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
506
507         v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
508 }
509
510 static void cedrus_buf_request_complete(struct vb2_buffer *vb)
511 {
512         struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
513
514         v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
515 }
516
517 static struct vb2_ops cedrus_qops = {
518         .queue_setup            = cedrus_queue_setup,
519         .buf_prepare            = cedrus_buf_prepare,
520         .buf_init               = cedrus_buf_init,
521         .buf_cleanup            = cedrus_buf_cleanup,
522         .buf_queue              = cedrus_buf_queue,
523         .buf_out_validate       = cedrus_buf_out_validate,
524         .buf_request_complete   = cedrus_buf_request_complete,
525         .start_streaming        = cedrus_start_streaming,
526         .stop_streaming         = cedrus_stop_streaming,
527         .wait_prepare           = vb2_ops_wait_prepare,
528         .wait_finish            = vb2_ops_wait_finish,
529 };
530
531 int cedrus_queue_init(void *priv, struct vb2_queue *src_vq,
532                       struct vb2_queue *dst_vq)
533 {
534         struct cedrus_ctx *ctx = priv;
535         int ret;
536
537         src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
538         src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
539         src_vq->drv_priv = ctx;
540         src_vq->buf_struct_size = sizeof(struct cedrus_buffer);
541         src_vq->min_buffers_needed = 1;
542         src_vq->ops = &cedrus_qops;
543         src_vq->mem_ops = &vb2_dma_contig_memops;
544         src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
545         src_vq->lock = &ctx->dev->dev_mutex;
546         src_vq->dev = ctx->dev->dev;
547         src_vq->supports_requests = true;
548         src_vq->requires_requests = true;
549
550         ret = vb2_queue_init(src_vq);
551         if (ret)
552                 return ret;
553
554         dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
555         dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
556         dst_vq->drv_priv = ctx;
557         dst_vq->buf_struct_size = sizeof(struct cedrus_buffer);
558         dst_vq->min_buffers_needed = 1;
559         dst_vq->ops = &cedrus_qops;
560         dst_vq->mem_ops = &vb2_dma_contig_memops;
561         dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
562         dst_vq->lock = &ctx->dev->dev_mutex;
563         dst_vq->dev = ctx->dev->dev;
564
565         return vb2_queue_init(dst_vq);
566 }