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 <linux/platform_device.h> |
17 | #include <linux/module.h> |
18 | #include <linux/of.h> |
19 | #include <linux/pm.h> |
20 | |
21 | #include <media/v4l2-device.h> |
22 | #include <media/v4l2-ioctl.h> |
23 | #include <media/v4l2-ctrls.h> |
24 | #include <media/v4l2-mem2mem.h> |
25 | |
26 | #include "cedrus.h" |
27 | #include "cedrus_video.h" |
28 | #include "cedrus_dec.h" |
29 | #include "cedrus_hw.h" |
30 | |
31 | static int cedrus_try_ctrl(struct v4l2_ctrl *ctrl) |
32 | { |
33 | if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) { |
34 | const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; |
35 | |
36 | if (sps->chroma_format_idc != 1) |
37 | /* Only 4:2:0 is supported */ |
38 | return -EINVAL; |
39 | if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) |
40 | /* Luma and chroma bit depth mismatch */ |
41 | return -EINVAL; |
42 | if (sps->bit_depth_luma_minus8 != 0) |
43 | /* Only 8-bit is supported */ |
44 | return -EINVAL; |
45 | } else if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) { |
46 | const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps; |
47 | struct cedrus_ctx *ctx = container_of(ctrl->handler, struct cedrus_ctx, hdl); |
48 | unsigned int bit_depth, max_depth; |
49 | struct vb2_queue *vq; |
50 | |
51 | if (sps->chroma_format_idc != 1) |
52 | /* Only 4:2:0 is supported */ |
53 | return -EINVAL; |
54 | |
55 | bit_depth = max(sps->bit_depth_luma_minus8, |
56 | sps->bit_depth_chroma_minus8) + 8; |
57 | |
58 | if (cedrus_is_capable(ctx, CEDRUS_CAPABILITY_H265_10_DEC)) |
59 | max_depth = 10; |
60 | else |
61 | max_depth = 8; |
62 | |
63 | if (bit_depth > max_depth) |
64 | return -EINVAL; |
65 | |
66 | vq = v4l2_m2m_get_vq(m2m_ctx: ctx->fh.m2m_ctx, |
67 | type: V4L2_BUF_TYPE_VIDEO_CAPTURE); |
68 | |
69 | /* |
70 | * Bit depth can't be higher than currently set once |
71 | * buffers are allocated. |
72 | */ |
73 | if (vb2_is_busy(q: vq)) { |
74 | if (ctx->bit_depth < bit_depth) |
75 | return -EINVAL; |
76 | } else { |
77 | ctx->bit_depth = bit_depth; |
78 | cedrus_reset_cap_format(ctx); |
79 | } |
80 | } |
81 | |
82 | return 0; |
83 | } |
84 | |
85 | static const struct v4l2_ctrl_ops cedrus_ctrl_ops = { |
86 | .try_ctrl = cedrus_try_ctrl, |
87 | }; |
88 | |
89 | static const struct cedrus_control cedrus_controls[] = { |
90 | { |
91 | .cfg = { |
92 | .id = V4L2_CID_STATELESS_MPEG2_SEQUENCE, |
93 | }, |
94 | .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC, |
95 | }, |
96 | { |
97 | .cfg = { |
98 | .id = V4L2_CID_STATELESS_MPEG2_PICTURE, |
99 | }, |
100 | .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC, |
101 | }, |
102 | { |
103 | .cfg = { |
104 | .id = V4L2_CID_STATELESS_MPEG2_QUANTISATION, |
105 | }, |
106 | .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC, |
107 | }, |
108 | { |
109 | .cfg = { |
110 | .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, |
111 | }, |
112 | .capabilities = CEDRUS_CAPABILITY_H264_DEC, |
113 | }, |
114 | { |
115 | .cfg = { |
116 | .id = V4L2_CID_STATELESS_H264_SLICE_PARAMS, |
117 | }, |
118 | .capabilities = CEDRUS_CAPABILITY_H264_DEC, |
119 | }, |
120 | { |
121 | .cfg = { |
122 | .id = V4L2_CID_STATELESS_H264_SPS, |
123 | .ops = &cedrus_ctrl_ops, |
124 | }, |
125 | .capabilities = CEDRUS_CAPABILITY_H264_DEC, |
126 | }, |
127 | { |
128 | .cfg = { |
129 | .id = V4L2_CID_STATELESS_H264_PPS, |
130 | }, |
131 | .capabilities = CEDRUS_CAPABILITY_H264_DEC, |
132 | }, |
133 | { |
134 | .cfg = { |
135 | .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, |
136 | }, |
137 | .capabilities = CEDRUS_CAPABILITY_H264_DEC, |
138 | }, |
139 | { |
140 | .cfg = { |
141 | .id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS, |
142 | }, |
143 | .capabilities = CEDRUS_CAPABILITY_H264_DEC, |
144 | }, |
145 | { |
146 | .cfg = { |
147 | .id = V4L2_CID_STATELESS_H264_DECODE_MODE, |
148 | .max = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, |
149 | .def = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, |
150 | }, |
151 | .capabilities = CEDRUS_CAPABILITY_H264_DEC, |
152 | }, |
153 | { |
154 | .cfg = { |
155 | .id = V4L2_CID_STATELESS_H264_START_CODE, |
156 | .max = V4L2_STATELESS_H264_START_CODE_NONE, |
157 | .def = V4L2_STATELESS_H264_START_CODE_NONE, |
158 | }, |
159 | .capabilities = CEDRUS_CAPABILITY_H264_DEC, |
160 | }, |
161 | /* |
162 | * We only expose supported profiles information, |
163 | * and not levels as it's not clear what is supported |
164 | * for each hardware/core version. |
165 | * In any case, TRY/S_FMT will clamp the format resolution |
166 | * to the maximum supported. |
167 | */ |
168 | { |
169 | .cfg = { |
170 | .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, |
171 | .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, |
172 | .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, |
173 | .max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, |
174 | .menu_skip_mask = |
175 | BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), |
176 | }, |
177 | .capabilities = CEDRUS_CAPABILITY_H264_DEC, |
178 | }, |
179 | { |
180 | .cfg = { |
181 | .id = V4L2_CID_STATELESS_HEVC_SPS, |
182 | .ops = &cedrus_ctrl_ops, |
183 | }, |
184 | .capabilities = CEDRUS_CAPABILITY_H265_DEC, |
185 | }, |
186 | { |
187 | .cfg = { |
188 | .id = V4L2_CID_STATELESS_HEVC_PPS, |
189 | }, |
190 | .capabilities = CEDRUS_CAPABILITY_H265_DEC, |
191 | }, |
192 | { |
193 | .cfg = { |
194 | .id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS, |
195 | /* The driver can only handle 1 entry per slice for now */ |
196 | .dims = { 1 }, |
197 | }, |
198 | .capabilities = CEDRUS_CAPABILITY_H265_DEC, |
199 | }, |
200 | { |
201 | .cfg = { |
202 | .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, |
203 | }, |
204 | .capabilities = CEDRUS_CAPABILITY_H265_DEC, |
205 | }, |
206 | { |
207 | .cfg = { |
208 | .id = V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS, |
209 | /* maximum 256 entry point offsets per slice */ |
210 | .dims = { 256 }, |
211 | .max = 0xffffffff, |
212 | .step = 1, |
213 | }, |
214 | .capabilities = CEDRUS_CAPABILITY_H265_DEC, |
215 | }, |
216 | { |
217 | .cfg = { |
218 | .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, |
219 | .max = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED, |
220 | .def = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED, |
221 | }, |
222 | .capabilities = CEDRUS_CAPABILITY_H265_DEC, |
223 | }, |
224 | { |
225 | .cfg = { |
226 | .id = V4L2_CID_STATELESS_HEVC_START_CODE, |
227 | .max = V4L2_STATELESS_HEVC_START_CODE_NONE, |
228 | .def = V4L2_STATELESS_HEVC_START_CODE_NONE, |
229 | }, |
230 | .capabilities = CEDRUS_CAPABILITY_H265_DEC, |
231 | }, |
232 | { |
233 | .cfg = { |
234 | .id = V4L2_CID_STATELESS_VP8_FRAME, |
235 | }, |
236 | .capabilities = CEDRUS_CAPABILITY_VP8_DEC, |
237 | }, |
238 | { |
239 | .cfg = { |
240 | .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, |
241 | }, |
242 | .capabilities = CEDRUS_CAPABILITY_H265_DEC, |
243 | }, |
244 | }; |
245 | |
246 | #define CEDRUS_CONTROLS_COUNT ARRAY_SIZE(cedrus_controls) |
247 | |
248 | void *cedrus_find_control_data(struct cedrus_ctx *ctx, u32 id) |
249 | { |
250 | unsigned int i; |
251 | |
252 | for (i = 0; ctx->ctrls[i]; i++) |
253 | if (ctx->ctrls[i]->id == id) |
254 | return ctx->ctrls[i]->p_cur.p; |
255 | |
256 | return NULL; |
257 | } |
258 | |
259 | u32 cedrus_get_num_of_controls(struct cedrus_ctx *ctx, u32 id) |
260 | { |
261 | unsigned int i; |
262 | |
263 | for (i = 0; ctx->ctrls[i]; i++) |
264 | if (ctx->ctrls[i]->id == id) |
265 | return ctx->ctrls[i]->elems; |
266 | |
267 | return 0; |
268 | } |
269 | |
270 | static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx) |
271 | { |
272 | struct v4l2_ctrl_handler *hdl = &ctx->hdl; |
273 | struct v4l2_ctrl *ctrl; |
274 | unsigned int ctrl_size; |
275 | unsigned int i, j; |
276 | |
277 | v4l2_ctrl_handler_init(hdl, CEDRUS_CONTROLS_COUNT); |
278 | if (hdl->error) { |
279 | v4l2_err(&dev->v4l2_dev, |
280 | "Failed to initialize control handler: %d\n" , |
281 | hdl->error); |
282 | return hdl->error; |
283 | } |
284 | |
285 | ctrl_size = sizeof(ctrl) * CEDRUS_CONTROLS_COUNT + 1; |
286 | |
287 | ctx->ctrls = kzalloc(size: ctrl_size, GFP_KERNEL); |
288 | if (!ctx->ctrls) |
289 | return -ENOMEM; |
290 | |
291 | j = 0; |
292 | for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) { |
293 | if (!cedrus_is_capable(ctx, capabilities: cedrus_controls[i].capabilities)) |
294 | continue; |
295 | |
296 | ctrl = v4l2_ctrl_new_custom(hdl, cfg: &cedrus_controls[i].cfg, |
297 | NULL); |
298 | if (hdl->error) { |
299 | v4l2_err(&dev->v4l2_dev, |
300 | "Failed to create %s control: %d\n" , |
301 | v4l2_ctrl_get_name(cedrus_controls[i].cfg.id), |
302 | hdl->error); |
303 | |
304 | v4l2_ctrl_handler_free(hdl); |
305 | kfree(objp: ctx->ctrls); |
306 | ctx->ctrls = NULL; |
307 | return hdl->error; |
308 | } |
309 | |
310 | ctx->ctrls[j++] = ctrl; |
311 | } |
312 | |
313 | ctx->fh.ctrl_handler = hdl; |
314 | v4l2_ctrl_handler_setup(hdl); |
315 | |
316 | return 0; |
317 | } |
318 | |
319 | static int cedrus_request_validate(struct media_request *req) |
320 | { |
321 | struct media_request_object *obj; |
322 | struct cedrus_ctx *ctx = NULL; |
323 | unsigned int count; |
324 | |
325 | list_for_each_entry(obj, &req->objects, list) { |
326 | struct vb2_buffer *vb; |
327 | |
328 | if (vb2_request_object_is_buffer(obj)) { |
329 | vb = container_of(obj, struct vb2_buffer, req_obj); |
330 | ctx = vb2_get_drv_priv(q: vb->vb2_queue); |
331 | |
332 | break; |
333 | } |
334 | } |
335 | |
336 | if (!ctx) |
337 | return -ENOENT; |
338 | |
339 | count = vb2_request_buffer_cnt(req); |
340 | if (!count) { |
341 | v4l2_info(&ctx->dev->v4l2_dev, |
342 | "No buffer was provided with the request\n" ); |
343 | return -ENOENT; |
344 | } else if (count > 1) { |
345 | v4l2_info(&ctx->dev->v4l2_dev, |
346 | "More than one buffer was provided with the request\n" ); |
347 | return -EINVAL; |
348 | } |
349 | |
350 | return vb2_request_validate(req); |
351 | } |
352 | |
353 | static int cedrus_open(struct file *file) |
354 | { |
355 | struct cedrus_dev *dev = video_drvdata(file); |
356 | struct cedrus_ctx *ctx = NULL; |
357 | int ret; |
358 | |
359 | if (mutex_lock_interruptible(&dev->dev_mutex)) |
360 | return -ERESTARTSYS; |
361 | |
362 | ctx = kzalloc(size: sizeof(*ctx), GFP_KERNEL); |
363 | if (!ctx) { |
364 | mutex_unlock(lock: &dev->dev_mutex); |
365 | return -ENOMEM; |
366 | } |
367 | |
368 | v4l2_fh_init(fh: &ctx->fh, vdev: video_devdata(file)); |
369 | file->private_data = &ctx->fh; |
370 | ctx->dev = dev; |
371 | ctx->bit_depth = 8; |
372 | |
373 | ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(m2m_dev: dev->m2m_dev, drv_priv: ctx, |
374 | queue_init: &cedrus_queue_init); |
375 | if (IS_ERR(ptr: ctx->fh.m2m_ctx)) { |
376 | ret = PTR_ERR(ptr: ctx->fh.m2m_ctx); |
377 | goto err_free; |
378 | } |
379 | |
380 | cedrus_reset_out_format(ctx); |
381 | |
382 | ret = cedrus_init_ctrls(dev, ctx); |
383 | if (ret) |
384 | goto err_m2m_release; |
385 | |
386 | v4l2_fh_add(fh: &ctx->fh); |
387 | |
388 | mutex_unlock(lock: &dev->dev_mutex); |
389 | |
390 | return 0; |
391 | |
392 | err_m2m_release: |
393 | v4l2_m2m_ctx_release(m2m_ctx: ctx->fh.m2m_ctx); |
394 | err_free: |
395 | kfree(objp: ctx); |
396 | mutex_unlock(lock: &dev->dev_mutex); |
397 | |
398 | return ret; |
399 | } |
400 | |
401 | static int cedrus_release(struct file *file) |
402 | { |
403 | struct cedrus_dev *dev = video_drvdata(file); |
404 | struct cedrus_ctx *ctx = container_of(file->private_data, |
405 | struct cedrus_ctx, fh); |
406 | |
407 | mutex_lock(&dev->dev_mutex); |
408 | |
409 | v4l2_fh_del(fh: &ctx->fh); |
410 | v4l2_m2m_ctx_release(m2m_ctx: ctx->fh.m2m_ctx); |
411 | |
412 | v4l2_ctrl_handler_free(hdl: &ctx->hdl); |
413 | kfree(objp: ctx->ctrls); |
414 | |
415 | v4l2_fh_exit(fh: &ctx->fh); |
416 | |
417 | kfree(objp: ctx); |
418 | |
419 | mutex_unlock(lock: &dev->dev_mutex); |
420 | |
421 | return 0; |
422 | } |
423 | |
424 | static const struct v4l2_file_operations cedrus_fops = { |
425 | .owner = THIS_MODULE, |
426 | .open = cedrus_open, |
427 | .release = cedrus_release, |
428 | .poll = v4l2_m2m_fop_poll, |
429 | .unlocked_ioctl = video_ioctl2, |
430 | .mmap = v4l2_m2m_fop_mmap, |
431 | }; |
432 | |
433 | static const struct video_device cedrus_video_device = { |
434 | .name = CEDRUS_NAME, |
435 | .vfl_dir = VFL_DIR_M2M, |
436 | .fops = &cedrus_fops, |
437 | .ioctl_ops = &cedrus_ioctl_ops, |
438 | .minor = -1, |
439 | .release = video_device_release_empty, |
440 | .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, |
441 | }; |
442 | |
443 | static const struct v4l2_m2m_ops cedrus_m2m_ops = { |
444 | .device_run = cedrus_device_run, |
445 | }; |
446 | |
447 | static const struct media_device_ops cedrus_m2m_media_ops = { |
448 | .req_validate = cedrus_request_validate, |
449 | .req_queue = v4l2_m2m_request_queue, |
450 | }; |
451 | |
452 | static int cedrus_probe(struct platform_device *pdev) |
453 | { |
454 | struct cedrus_dev *dev; |
455 | struct video_device *vfd; |
456 | int ret; |
457 | |
458 | dev = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dev), GFP_KERNEL); |
459 | if (!dev) |
460 | return -ENOMEM; |
461 | |
462 | platform_set_drvdata(pdev, data: dev); |
463 | |
464 | dev->vfd = cedrus_video_device; |
465 | dev->dev = &pdev->dev; |
466 | dev->pdev = pdev; |
467 | |
468 | ret = cedrus_hw_probe(dev); |
469 | if (ret) { |
470 | dev_err(&pdev->dev, "Failed to probe hardware\n" ); |
471 | return ret; |
472 | } |
473 | |
474 | mutex_init(&dev->dev_mutex); |
475 | |
476 | INIT_DELAYED_WORK(&dev->watchdog_work, cedrus_watchdog); |
477 | |
478 | ret = v4l2_device_register(dev: &pdev->dev, v4l2_dev: &dev->v4l2_dev); |
479 | if (ret) { |
480 | dev_err(&pdev->dev, "Failed to register V4L2 device\n" ); |
481 | return ret; |
482 | } |
483 | |
484 | vfd = &dev->vfd; |
485 | vfd->lock = &dev->dev_mutex; |
486 | vfd->v4l2_dev = &dev->v4l2_dev; |
487 | |
488 | snprintf(buf: vfd->name, size: sizeof(vfd->name), fmt: "%s" , cedrus_video_device.name); |
489 | video_set_drvdata(vdev: vfd, data: dev); |
490 | |
491 | dev->m2m_dev = v4l2_m2m_init(m2m_ops: &cedrus_m2m_ops); |
492 | if (IS_ERR(ptr: dev->m2m_dev)) { |
493 | v4l2_err(&dev->v4l2_dev, |
494 | "Failed to initialize V4L2 M2M device\n" ); |
495 | ret = PTR_ERR(ptr: dev->m2m_dev); |
496 | |
497 | goto err_v4l2; |
498 | } |
499 | |
500 | dev->mdev.dev = &pdev->dev; |
501 | strscpy(dev->mdev.model, CEDRUS_NAME, sizeof(dev->mdev.model)); |
502 | strscpy(dev->mdev.bus_info, "platform:" CEDRUS_NAME, |
503 | sizeof(dev->mdev.bus_info)); |
504 | |
505 | media_device_init(mdev: &dev->mdev); |
506 | dev->mdev.ops = &cedrus_m2m_media_ops; |
507 | dev->v4l2_dev.mdev = &dev->mdev; |
508 | |
509 | ret = video_register_device(vdev: vfd, type: VFL_TYPE_VIDEO, nr: 0); |
510 | if (ret) { |
511 | v4l2_err(&dev->v4l2_dev, "Failed to register video device\n" ); |
512 | goto err_m2m; |
513 | } |
514 | |
515 | v4l2_info(&dev->v4l2_dev, |
516 | "Device registered as /dev/video%d\n" , vfd->num); |
517 | |
518 | ret = v4l2_m2m_register_media_controller(m2m_dev: dev->m2m_dev, vdev: vfd, |
519 | MEDIA_ENT_F_PROC_VIDEO_DECODER); |
520 | if (ret) { |
521 | v4l2_err(&dev->v4l2_dev, |
522 | "Failed to initialize V4L2 M2M media controller\n" ); |
523 | goto err_video; |
524 | } |
525 | |
526 | ret = media_device_register(&dev->mdev); |
527 | if (ret) { |
528 | v4l2_err(&dev->v4l2_dev, "Failed to register media device\n" ); |
529 | goto err_m2m_mc; |
530 | } |
531 | |
532 | return 0; |
533 | |
534 | err_m2m_mc: |
535 | v4l2_m2m_unregister_media_controller(m2m_dev: dev->m2m_dev); |
536 | err_video: |
537 | video_unregister_device(vdev: &dev->vfd); |
538 | err_m2m: |
539 | v4l2_m2m_release(m2m_dev: dev->m2m_dev); |
540 | err_v4l2: |
541 | v4l2_device_unregister(v4l2_dev: &dev->v4l2_dev); |
542 | |
543 | return ret; |
544 | } |
545 | |
546 | static void cedrus_remove(struct platform_device *pdev) |
547 | { |
548 | struct cedrus_dev *dev = platform_get_drvdata(pdev); |
549 | |
550 | cancel_delayed_work_sync(dwork: &dev->watchdog_work); |
551 | if (media_devnode_is_registered(devnode: dev->mdev.devnode)) { |
552 | media_device_unregister(mdev: &dev->mdev); |
553 | v4l2_m2m_unregister_media_controller(m2m_dev: dev->m2m_dev); |
554 | media_device_cleanup(mdev: &dev->mdev); |
555 | } |
556 | |
557 | v4l2_m2m_release(m2m_dev: dev->m2m_dev); |
558 | video_unregister_device(vdev: &dev->vfd); |
559 | v4l2_device_unregister(v4l2_dev: &dev->v4l2_dev); |
560 | |
561 | cedrus_hw_remove(dev); |
562 | } |
563 | |
564 | static const struct cedrus_variant sun4i_a10_cedrus_variant = { |
565 | .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC | |
566 | CEDRUS_CAPABILITY_H264_DEC | |
567 | CEDRUS_CAPABILITY_VP8_DEC, |
568 | .mod_rate = 320000000, |
569 | }; |
570 | |
571 | static const struct cedrus_variant sun5i_a13_cedrus_variant = { |
572 | .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC | |
573 | CEDRUS_CAPABILITY_H264_DEC | |
574 | CEDRUS_CAPABILITY_VP8_DEC, |
575 | .mod_rate = 320000000, |
576 | }; |
577 | |
578 | static const struct cedrus_variant sun7i_a20_cedrus_variant = { |
579 | .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC | |
580 | CEDRUS_CAPABILITY_H264_DEC | |
581 | CEDRUS_CAPABILITY_VP8_DEC, |
582 | .mod_rate = 320000000, |
583 | }; |
584 | |
585 | static const struct cedrus_variant sun8i_a33_cedrus_variant = { |
586 | .capabilities = CEDRUS_CAPABILITY_UNTILED | |
587 | CEDRUS_CAPABILITY_MPEG2_DEC | |
588 | CEDRUS_CAPABILITY_H264_DEC | |
589 | CEDRUS_CAPABILITY_VP8_DEC, |
590 | .mod_rate = 320000000, |
591 | }; |
592 | |
593 | static const struct cedrus_variant sun8i_h3_cedrus_variant = { |
594 | .capabilities = CEDRUS_CAPABILITY_UNTILED | |
595 | CEDRUS_CAPABILITY_MPEG2_DEC | |
596 | CEDRUS_CAPABILITY_H264_DEC | |
597 | CEDRUS_CAPABILITY_H265_DEC | |
598 | CEDRUS_CAPABILITY_VP8_DEC, |
599 | .mod_rate = 402000000, |
600 | }; |
601 | |
602 | static const struct cedrus_variant sun8i_v3s_cedrus_variant = { |
603 | .capabilities = CEDRUS_CAPABILITY_UNTILED | |
604 | CEDRUS_CAPABILITY_H264_DEC, |
605 | .mod_rate = 297000000, |
606 | }; |
607 | |
608 | static const struct cedrus_variant sun8i_r40_cedrus_variant = { |
609 | .capabilities = CEDRUS_CAPABILITY_UNTILED | |
610 | CEDRUS_CAPABILITY_MPEG2_DEC | |
611 | CEDRUS_CAPABILITY_H264_DEC | |
612 | CEDRUS_CAPABILITY_VP8_DEC, |
613 | .mod_rate = 297000000, |
614 | }; |
615 | |
616 | static const struct cedrus_variant sun20i_d1_cedrus_variant = { |
617 | .capabilities = CEDRUS_CAPABILITY_UNTILED | |
618 | CEDRUS_CAPABILITY_MPEG2_DEC | |
619 | CEDRUS_CAPABILITY_H264_DEC | |
620 | CEDRUS_CAPABILITY_H265_DEC, |
621 | .mod_rate = 432000000, |
622 | }; |
623 | |
624 | static const struct cedrus_variant sun50i_a64_cedrus_variant = { |
625 | .capabilities = CEDRUS_CAPABILITY_UNTILED | |
626 | CEDRUS_CAPABILITY_MPEG2_DEC | |
627 | CEDRUS_CAPABILITY_H264_DEC | |
628 | CEDRUS_CAPABILITY_H265_DEC | |
629 | CEDRUS_CAPABILITY_VP8_DEC, |
630 | .mod_rate = 402000000, |
631 | }; |
632 | |
633 | static const struct cedrus_variant sun50i_h5_cedrus_variant = { |
634 | .capabilities = CEDRUS_CAPABILITY_UNTILED | |
635 | CEDRUS_CAPABILITY_MPEG2_DEC | |
636 | CEDRUS_CAPABILITY_H264_DEC | |
637 | CEDRUS_CAPABILITY_H265_DEC | |
638 | CEDRUS_CAPABILITY_VP8_DEC, |
639 | .mod_rate = 402000000, |
640 | }; |
641 | |
642 | static const struct cedrus_variant sun50i_h6_cedrus_variant = { |
643 | .capabilities = CEDRUS_CAPABILITY_UNTILED | |
644 | CEDRUS_CAPABILITY_MPEG2_DEC | |
645 | CEDRUS_CAPABILITY_H264_DEC | |
646 | CEDRUS_CAPABILITY_H265_DEC | |
647 | CEDRUS_CAPABILITY_H265_10_DEC | |
648 | CEDRUS_CAPABILITY_VP8_DEC, |
649 | .mod_rate = 600000000, |
650 | }; |
651 | |
652 | static const struct of_device_id cedrus_dt_match[] = { |
653 | { |
654 | .compatible = "allwinner,sun4i-a10-video-engine" , |
655 | .data = &sun4i_a10_cedrus_variant, |
656 | }, |
657 | { |
658 | .compatible = "allwinner,sun5i-a13-video-engine" , |
659 | .data = &sun5i_a13_cedrus_variant, |
660 | }, |
661 | { |
662 | .compatible = "allwinner,sun7i-a20-video-engine" , |
663 | .data = &sun7i_a20_cedrus_variant, |
664 | }, |
665 | { |
666 | .compatible = "allwinner,sun8i-a33-video-engine" , |
667 | .data = &sun8i_a33_cedrus_variant, |
668 | }, |
669 | { |
670 | .compatible = "allwinner,sun8i-h3-video-engine" , |
671 | .data = &sun8i_h3_cedrus_variant, |
672 | }, |
673 | { |
674 | .compatible = "allwinner,sun8i-v3s-video-engine" , |
675 | .data = &sun8i_v3s_cedrus_variant, |
676 | }, |
677 | { |
678 | .compatible = "allwinner,sun8i-r40-video-engine" , |
679 | .data = &sun8i_r40_cedrus_variant, |
680 | }, |
681 | { |
682 | .compatible = "allwinner,sun20i-d1-video-engine" , |
683 | .data = &sun20i_d1_cedrus_variant, |
684 | }, |
685 | { |
686 | .compatible = "allwinner,sun50i-a64-video-engine" , |
687 | .data = &sun50i_a64_cedrus_variant, |
688 | }, |
689 | { |
690 | .compatible = "allwinner,sun50i-h5-video-engine" , |
691 | .data = &sun50i_h5_cedrus_variant, |
692 | }, |
693 | { |
694 | .compatible = "allwinner,sun50i-h6-video-engine" , |
695 | .data = &sun50i_h6_cedrus_variant, |
696 | }, |
697 | { /* sentinel */ } |
698 | }; |
699 | MODULE_DEVICE_TABLE(of, cedrus_dt_match); |
700 | |
701 | static const struct dev_pm_ops cedrus_dev_pm_ops = { |
702 | SET_RUNTIME_PM_OPS(cedrus_hw_suspend, |
703 | cedrus_hw_resume, NULL) |
704 | }; |
705 | |
706 | static struct platform_driver cedrus_driver = { |
707 | .probe = cedrus_probe, |
708 | .remove_new = cedrus_remove, |
709 | .driver = { |
710 | .name = CEDRUS_NAME, |
711 | .of_match_table = cedrus_dt_match, |
712 | .pm = &cedrus_dev_pm_ops, |
713 | }, |
714 | }; |
715 | module_platform_driver(cedrus_driver); |
716 | |
717 | MODULE_LICENSE("GPL v2" ); |
718 | MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>" ); |
719 | MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>" ); |
720 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>" ); |
721 | MODULE_DESCRIPTION("Cedrus VPU driver" ); |
722 | |