blob: 1c7230b925390a3f31dbd77c8b99978691b5433d [file] [log] [blame]
Googler9398cc32022-12-02 17:21:52 +08001// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2/*
3 * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
4 */
5
6/* Linux Headers */
7#include <linux/module.h>
8#include <linux/printk.h>
9#include <linux/amlogic/major.h>
10#include <linux/platform_device.h>
11#include <linux/time.h>
12#include <linux/uaccess.h>
13#include <linux/file.h>
14#include <linux/dma-direction.h>
15#include <linux/dma-buf.h>
16#include <linux/ion.h>
17
18/* Amlogic Headers */
19#include <linux/meson_ion.h>
20#include <linux/amlogic/media/vfm/vframe.h>
21#include <linux/amlogic/media/vfm/vframe_provider.h>
22#include <linux/amlogic/media/vfm/vframe_receiver.h>
23#include <linux/amlogic/media/vfm/vfm_ext.h>
24#include <linux/amlogic/media/canvas/canvas.h>
25#include <linux/amlogic/media/canvas/canvas_mgr.h>
26#include <linux/amlogic/media/vout/vinfo.h>
27#include <linux/amlogic/media/vout/vout_notify.h>
28#include <linux/amlogic/media/amvecm/amvecm.h>
29
30/* Local Headers */
31#include "video_priv.h"
32#include "dummy_provider.h"
33
34static struct video_provider_device_s video_provider_device;
35static struct vframe_provider_s vp_vfm_prov;
36static struct vp_pool_s vp_pool[VP_VFM_POOL_SIZE];
37static s32 fill_ptr, get_ptr, put_ptr;
38static int vp_canvas_table[VP_VFM_POOL_SIZE];
39static DEFINE_SPINLOCK(lock);
40unsigned int dummy_video_log_level;
41
42#define INCPTR(p) ptr_wrap_inc(&(p))
43
44static inline void ptr_wrap_inc(u32 *ptr)
45{
46 u32 i = *ptr;
47
48 i++;
49 if (i >= VP_VFM_POOL_SIZE)
50 i = 0;
51 *ptr = i;
52}
53
54static inline u32 index2canvas(u32 index)
55{
56 return vp_canvas_table[index];
57}
58
59static int has_unused_pool(void)
60{
61 int i;
62
63 for (i = 0; i < VP_VFM_POOL_SIZE; i++) {
64 if (vp_pool[i].used == 0)
65 return i;
66 }
67 return -1;
68}
69
70static ssize_t log_level_show(struct class *cla,
71 struct class_attribute *attr,
72 char *buf)
73{
74 return snprintf(buf, 40, "%d\n", dummy_video_log_level);
75}
76
77static ssize_t log_level_store(struct class *cla,
78 struct class_attribute *attr,
79 const char *buf, size_t count)
80{
81 int res = 0;
82 int ret = 0;
83
84 ret = kstrtoint(buf, 0, &res);
85 if (ret) {
86 vp_err("kstrtoint err\n");
87 return -EINVAL;
88 }
89
90 vp_info("log_level: %d->%d\n", dummy_video_log_level, res);
91 dummy_video_log_level = res;
92
93 return count;
94}
95
96static CLASS_ATTR_RW(log_level);
97
98static struct attribute *video_provider_class_attrs[] = {
99 &class_attr_log_level.attr,
100 NULL
101};
102ATTRIBUTE_GROUPS(video_provider_class);
103
104static struct class video_provider_class = {
105 .name = VIDEO_PROVIDER_NAME,
106 .class_groups = video_provider_class_groups,
107};
108
109static struct vframe_s *vp_vfm_peek(void *op_arg)
110{
111 vp_dbg2("%s (%d).\n", __func__, get_ptr);
112 if (get_ptr == fill_ptr)
113 return NULL;
114 return &vp_pool[get_ptr].vfm;
115}
116
117static struct vframe_s *vp_vfm_get(void *op_arg)
118{
119 struct vframe_s *vf;
120
121 vp_dbg2("%s (%d).\n", __func__, get_ptr);
122
123 if (get_ptr == fill_ptr)
124 return NULL;
125 vf = &vp_pool[get_ptr].vfm;
126 INCPTR(get_ptr);
127
128 return vf;
129}
130
131static void vp_vfm_put(struct vframe_s *vf, void *op_arg)
132{
133 int i;
134 int canvas_addr;
135
136 vp_dbg2("%s.\n", __func__);
137
138 if (!vf)
139 return;
140 INCPTR(put_ptr);
141
142 if (put_ptr == fill_ptr) {
143 vp_info("buffer%d is being in use, skip\n", fill_ptr);
144 return;
145 }
146
147 for (i = 0; i < VP_VFM_POOL_SIZE; i++) {
148 canvas_addr = index2canvas(i);
149 if (vf->canvas0Addr == (canvas_addr & 0xff)) {
150 vp_pool[i].used = 0;
151 vp_dbg("******recycle buffer index : %d ******\n", i);
152 }
153 }
154}
155
156static int vp_event_cb(int type, void *data, void *private_data)
157{
158 vp_dbg2("%s type(0x%x).\n", __func__, type);
159
160 return 0;
161}
162
163static int vp_vfm_states(struct vframe_states *states, void *op_arg)
164{
165 int i;
166 unsigned long flags;
167
168 vp_dbg2("%s.\n", __func__);
169
170 if (!states) {
171 vp_err("vframe_states is NULL");
172 return -EINVAL;
173 }
174
175 spin_lock_irqsave(&lock, flags);
176 states->vf_pool_size = VP_VFM_POOL_SIZE;
177 i = fill_ptr - get_ptr;
178 if (i < 0)
179 i += VP_VFM_POOL_SIZE;
180 states->buf_avail_num = i;
181 spin_unlock_irqrestore(&lock, flags);
182 return 0;
183}
184
185static const struct vframe_operations_s vp_vfm_ops = {
186 .peek = vp_vfm_peek,
187 .get = vp_vfm_get,
188 .put = vp_vfm_put,
189 .event_cb = vp_event_cb,
190 .vf_states = vp_vfm_states,
191};
192
193static void video_provider_release_path(void)
194{
195 vf_unreg_provider(&vp_vfm_prov);
196 vfm_map_remove(VP_VFPATH_ID);
197}
198
199static int video_provider_creat_path(void)
200{
201 int ret = -1;
202 char path_id[] = VP_VFPATH_ID;
203 char path_chain[] = VP_VFPATH_CHAIN;
204
205 if (vfm_map_add(path_id, path_chain) < 0) {
206 vp_err("video_provider map creation failed\n");
207 return -ENOMEM;
208 }
209
210 vf_provider_init(&vp_vfm_prov, VIDEO_PROVIDER_NAME,
211 &vp_vfm_ops, NULL);
212 ret = vf_reg_provider(&vp_vfm_prov);
213 if (ret < 0)
214 vp_info("vfm path is aleady created\n");
215 ret = vf_notify_receiver(VIDEO_PROVIDER_NAME,
216 VFRAME_EVENT_PROVIDER_START, NULL);
217 if (ret < 0) {
218 vp_err("notify receiver error\n");
219 video_provider_release_path();
220 }
221
222 return ret;
223}
224
225static int canvas_table_alloc(void)
226{
227 int i;
228
229 for (i = 0; i < VP_VFM_POOL_SIZE; i++) {
230 if (vp_canvas_table[i])
231 break;
232 }
233
234 /* alloc 2 * VP_VFM_POOL_SIZE for multi planes */
235 if (i == VP_VFM_POOL_SIZE) {
236 u32 canvas_table[VP_VFM_POOL_SIZE * 2];
237
238 if (canvas_pool_alloc_canvas_table("video_provider",
239 canvas_table,
240 VP_VFM_POOL_SIZE * 2,
241 CANVAS_MAP_TYPE_1)) {
242 pr_err("%s allocate canvas error.\n", __func__);
243 return -ENOMEM;
244 }
245 for (i = 0; i < VP_VFM_POOL_SIZE; i++)
246 vp_canvas_table[i] = (canvas_table[2 * i] |
247 (canvas_table[2 * i + 1] << 8));
248 } else {
249 vp_info("canvas_table is already alloced");
250 }
251
252 return 0;
253}
254
255static void canvas_table_release(void)
256{
257 int i;
258
259 for (i = 0; i < ARRAY_SIZE(vp_canvas_table); i++) {
260 if (vp_canvas_table[i]) {
261 if (vp_canvas_table[i] & 0xff)
262 canvas_pool_map_free_canvas
263 (vp_canvas_table[i] & 0xff);
264 if ((vp_canvas_table[i] >> 8) & 0xff)
265 canvas_pool_map_free_canvas
266 ((vp_canvas_table[i] >> 8) & 0xff);
267 if ((vp_canvas_table[i] >> 16) & 0xff)
268 canvas_pool_map_free_canvas
269 ((vp_canvas_table[i] >> 16) & 0xff);
270 }
271 vp_canvas_table[i] = 0;
272 }
273}
274
275static int video_provider_open(struct inode *inode, struct file *file)
276{
277 int ret = -1;
278
279 _video_set_disable(VIDEO_DISABLE_FORNEXT);
280
281 ret = video_provider_creat_path();
282 if (ret < 0) {
283 vp_err("video_provider_creat_path failed\n");
284 return -ENOMEM;
285 }
286
287 ret = canvas_table_alloc();
288 if (ret < 0)
289 return ret;
290
291 fill_ptr = 0;
292 get_ptr = 0;
293 put_ptr = 0;
294
295 return 0;
296}
297
298static int vp_dma_buf_get_phys(int fd, unsigned long *addr)
299{
300 long ret = -1;
301 struct dma_buf *dbuf = NULL;
302 struct dma_buf_attachment *d_att = NULL;
303 struct sg_table *sg = NULL;
304 struct device *dev = video_provider_device.dev;
305 enum dma_data_direction dir = DMA_TO_DEVICE;
306 struct page *page;
307
308 if (fd < 0 || !dev) {
309 vp_err("error input param or dev is null\n");
310 return -EINVAL;
311 }
312
313 dbuf = dma_buf_get(fd);
314 if (IS_ERR(dbuf)) {
315 vp_err("failed to get dma buffer\n");
316 return -EINVAL;
317 }
318
319 d_att = dma_buf_attach(dbuf, dev);
320 if (IS_ERR(d_att)) {
321 vp_err("failed to set dma attach\n");
322 ret = -EINVAL;
323 goto attach_err;
324 }
325
326 sg = dma_buf_map_attachment(d_att, dir);
327 if (IS_ERR(sg)) {
328 vp_err("failed to get dma sg\n");
329 ret = -EINVAL;
330 goto map_attach_err;
331 } else {
332 page = sg_page(sg->sgl);
333 *addr = PFN_PHYS(page_to_pfn(page));
334 ret = 0;
335 }
336
337 dma_buf_unmap_attachment(d_att, sg, dir);
338
339map_attach_err:
340 dma_buf_detach(dbuf, d_att);
341
342attach_err:
343 dma_buf_put(dbuf);
344
345 return ret;
346}
347
348static int get_fram_phyaddr(struct vp_frame_s *frame_info, unsigned long *addr)
349{
350 int ret = -1;
351 size_t len = 0;
352
353 if (!frame_info || !addr) {
354 vp_err("%s-%d frame_info is NULL\n", __func__, __LINE__);
355 return -EINVAL;
356 }
357
358 switch (frame_info->mem_type) {
359 case VP_MEM_ION:
360 ret = meson_ion_share_fd_to_phys(frame_info->shared_fd,
361 (phys_addr_t *)addr, &len);
362 if (ret != 0)
363 return ret;
364 vp_dbg("ion frame addr 0x%lx, len %zu\n", *addr, len);
365 break;
366 case VP_MEM_DMABUF:
367 ret = vp_dma_buf_get_phys(frame_info->shared_fd, addr);
368 if (ret != 0)
369 return ret;
370 vp_dbg("dma frame addr 0x%lx, len %zu\n", *addr, len);
371 break;
372 default:
373 vp_info("%s-%d mem type error\n", __func__, __LINE__);
374 return -EINVAL;
375 }
376
377 return 0;
378}
379
380static int set_vfm_type(struct vp_frame_s *frame_info,
381 struct vframe_s *vf, int *bpp)
382{
383 if (!frame_info || !vf || !bpp) {
384 vp_info("%s-%d vf error\n", __func__, __LINE__);
385 return -EINVAL;
386 }
387 switch (frame_info->format) {
388 case VP_FMT_NV21:
389 vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD |
390 VIDTYPE_VIU_NV21;
391 *bpp = 8;
392 break;
393 case VP_FMT_NV12:
394 vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD |
395 VIDTYPE_VIU_NV12;
396 *bpp = 8;
397 break;
398 case VP_FMT_RGB888:
399 *bpp = 24;
400 vf->type = VIDTYPE_VIU_444 | VIDTYPE_VIU_SINGLE_PLANE |
401 VIDTYPE_VIU_FIELD;
402 break;
403 case VP_FMT_YUV444_PACKED:
404 vf->type = VIDTYPE_VIU_444 | VIDTYPE_VIU_SINGLE_PLANE |
405 VIDTYPE_VIU_FIELD;
406 *bpp = 24;
407 break;
408 default:
409 vp_info("%s-%d vf error\n", __func__, __LINE__);
410 return -EINVAL;
411 }
412 switch (frame_info->endian) {
413 case VP_BIG_ENDIAN:
414 vf->flag &= ~VFRAME_FLAG_VIDEO_LINEAR;
415 break;
416 case VP_LITTLE_ENDIAN:
417 vf->flag |= VFRAME_FLAG_VIDEO_LINEAR;
418 break;
419 default:
420 vp_info("%s-%d vf error\n", __func__, __LINE__);
421 return -EINVAL;
422 }
423
424 video_provider_device.vinfo = get_current_vinfo();
425
426 /* indicate the vframe is a limited range frame */
427 vf->signal_type =
428 (1 << 29) /* video available */
429 | (5 << 26) /* unspecified */
430 | (0 << 25) /* limited */
431 | (1 << 24); /* color available */
432 if (video_provider_device.vinfo->width >= 1280 &&
433 video_provider_device.vinfo->height >= 720) {
434 /* >= 720p, use 709 */
435 vf->signal_type |=
436 (1 << 16) /* bt709 */
437 | (1 << 8) /* bt709 */
438 | (1 << 0); /* bt709 */
439 } else {
440 /* < 720p, use 709 */
441 vf->signal_type |=
442 (3 << 16) /* bt601 */
443 | (3 << 8) /* bt601 */
444 | (3 << 0); /* bt601 */
445 }
446
447 return 0;
448}
449
450static int set_vfm_info_from_frame(struct vp_frame_s *frame_info)
451{
452 int ret = -1;
453 int index;
454 unsigned long addr = 0;
455 struct vframe_s *new_vf;
456 int bpp;
457 unsigned int canvas_width;
458
459 index = has_unused_pool();
460 if (index < 0) {
461 vp_info("no buffer available, need post ASAP\n");
462 return -ENOMEM;
463 }
464 memset(&vp_pool[fill_ptr], 0, sizeof(struct vp_pool_s));
465
466 ret = get_fram_phyaddr(frame_info, &addr);
467 if (ret < 0)
468 return ret;
469
470 new_vf = &vp_pool[fill_ptr].vfm;
471 ret = set_vfm_type(frame_info, new_vf, &bpp);
472 if (ret < 0)
473 return ret;
474 vp_dbg("vf type val=0x%x\n", new_vf->type);
475
476 /* 256bit align, then calc bytes*/
477 canvas_width = ((frame_info->width * bpp + 0xff) & ~0xff) / 8;
478 vp_dbg("canvas_width val=%u\n", canvas_width);
479
480 switch (frame_info->format) {
481 case VP_FMT_NV21:
482 case VP_FMT_NV12:
483 canvas_config(vp_canvas_table[fill_ptr] & 0xff,
484 addr,
485 canvas_width, frame_info->height,
486 CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
487 addr += canvas_width * frame_info->height;
488 canvas_config((vp_canvas_table[fill_ptr] >> 8) & 0xff,
489 addr,
490 canvas_width, frame_info->height / 2,
491 CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
492 new_vf->canvas0Addr = vp_canvas_table[fill_ptr];
493 break;
494 case VP_FMT_RGB888:
495 case VP_FMT_YUV444_PACKED:
496 canvas_config(vp_canvas_table[fill_ptr] & 0xff,
497 addr,
498 canvas_width, frame_info->height,
499 CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
500 new_vf->canvas0Addr = vp_canvas_table[fill_ptr] & 0xff;
501 break;
502 default:
503 vp_err("unsupported format to canvas_config\n");
504 return -EINVAL;
505 }
506 new_vf->width = frame_info->width;
507 new_vf->height = frame_info->height;
508 new_vf->index = fill_ptr;
509 new_vf->duration_pulldown = 0;
510 new_vf->pts = 0;
511 new_vf->pts_us64 = 0;
512 new_vf->ratio_control = 0;
513
514 INCPTR(fill_ptr);
515
516 return 0;
517}
518
519static void post_frame(void)
520{
521 vf_notify_receiver(VIDEO_PROVIDER_NAME,
522 VFRAME_EVENT_PROVIDER_VFRAME_READY,
523 NULL);
524}
525
526static long video_provider_ioctl(struct file *filp, unsigned int cmd,
527 unsigned long args)
528{
529 long ret = 0;
530 void __user *argp = (void __user *)args;
531 struct vp_frame_s frame_info;
532
533 switch (cmd) {
534 case VIDEO_PROVIDER_IOCTL_RENDER:
535 if (!copy_from_user(&frame_info, argp, sizeof(frame_info))) {
536 vp_dbg("render: canvas index 0x%x\n",
537 vp_canvas_table[fill_ptr]);
538 vp_dbg(" frame format: %d\n", frame_info.format);
539 vp_dbg(" frame width: %d\n", frame_info.width);
540 vp_dbg(" frame height: %d\n", frame_info.height);
541 vp_dbg(" frame mem_type: %d\n", frame_info.mem_type);
542 vp_dbg("frame shared_fd: %d\n", frame_info.shared_fd);
543 vp_dbg(" frame endian: %d\n", frame_info.endian);
544 ret = set_vfm_info_from_frame(&frame_info);
545 } else {
546 ret = -EINVAL;
547 }
548 break;
549 case VIDEO_PROVIDER_IOCTL_POST:
550 vp_dbg("post: canvas index 0x%x\n", vp_canvas_table[get_ptr]);
551 post_frame();
552 break;
553 default:
554 vp_err("%s-%d, para err\n", __func__, __LINE__);
555 ret = -EINVAL;
556 }
557 return ret;
558}
559
560static int video_provider_release(struct inode *inode, struct file *file)
561{
562 video_provider_release_path();
563 canvas_table_release();
564
565 return 0;
566}
567
568#ifdef CONFIG_COMPAT
569static long video_provider_compat_ioctl(struct file *filp, unsigned int cmd,
570 unsigned long args)
571{
572 long ret = 0;
573
574 ret = video_provider_ioctl(filp, cmd, (ulong)compat_ptr(args));
575 return ret;
576}
577#endif
578
579static const struct file_operations video_provider_fops = {
580 .owner = THIS_MODULE,
581 .open = video_provider_open,
582 .unlocked_ioctl = video_provider_ioctl,
583#ifdef CONFIG_COMPAT
584 .compat_ioctl = video_provider_compat_ioctl,
585#endif
586 .release = video_provider_release,
587};
588
589static int video_provider_probe(struct platform_device *pdev)
590{
591 int ret = 0;
592
593 strcpy(video_provider_device.name, VIDEO_PROVIDER_NAME);
594 ret = register_chrdev(0, video_provider_device.name,
595 &video_provider_fops);
596 if (ret <= 0) {
597 vp_err("register video provider device error\n");
598 return ret;
599 }
600 video_provider_device.major = ret;
601 vp_info("video provider major:%d\n", ret);
602 ret = class_register(&video_provider_class);
603 if (ret < 0) {
604 vp_err("error create video provider class\n");
605 return ret;
606 }
607 video_provider_device.cla = &video_provider_class;
608 video_provider_device.dev = device_create(video_provider_device.cla,
609 NULL, MKDEV(video_provider_device.major,
610 0), NULL, video_provider_device.name);
611 if (IS_ERR(video_provider_device.dev)) {
612 vp_err("create video provider device error\n");
613 class_unregister(video_provider_device.cla);
614 return -1;
615 }
616
617 return ret;
618}
619
620static int video_provider_remove(struct platform_device *pdev)
621{
622 vp_info("%s\n", __func__);
623 if (!video_provider_device.cla)
624 return 0;
625
626 if (video_provider_device.dev)
627 device_destroy(video_provider_device.cla,
628 MKDEV(video_provider_device.major, 0));
629 class_unregister(video_provider_device.cla);
630 unregister_chrdev(video_provider_device.major,
631 video_provider_device.name);
632
633 return 0;
634}
635
636static const struct of_device_id amlogic_video_provider_dt_match[] = {
637 {
638 .compatible = "amlogic, dummy_video_provider",
639 },
640 {},
641};
642
643static struct platform_driver video_provider_drv = {
644 .probe = video_provider_probe,
645 .remove = video_provider_remove,
646 .driver = {
647 .name = "video_provider",
648 .owner = THIS_MODULE,
649 .of_match_table = amlogic_video_provider_dt_match,
650 }
651};
652
653static int __init video_provider_init_module(void)
654{
655 vp_info("%s\n", __func__);
656
657 if (platform_driver_register(&video_provider_drv)) {
658 pr_err("Failed to register video provider driver error\n");
659 return -ENODEV;
660 }
661
662 return 0;
663}
664
665static void __exit video_provider_remove_module(void)
666{
667 platform_driver_unregister(&video_provider_drv);
668 vp_info("video provider module removed.\n");
669}
670
671module_init(video_provider_init_module);
672module_exit(video_provider_remove_module);
673
674MODULE_DESCRIPTION("Amlogic dummy video provider driver");
675MODULE_LICENSE("GPL");