ionvideo: initial add the driver
authorGuosong Zhou <guosong.zhou@amlogic.com>
Thu, 6 Apr 2017 10:25:33 +0000 (18:25 +0800)
committerVictor Wan <victor.wan@amlogic.com>
Mon, 10 Apr 2017 09:15:15 +0000 (02:15 -0700)
PD#138714: initial add the driver

1.Add amlogic ionvideo driver;
2.device tree support of ionvideo for p212/q200/skt/p400/p401;
3.related Makefiles/Kconfig/Headfiles update;

Change-Id: I2c0013a8ab256f73618b7f583c3b275fa3aaeebb
Signed-off-by: Guosong Zhou <guosong.zhou@amlogic.com>
19 files changed:
MAINTAINERS
arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts
arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts
arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts
arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts
arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts
arch/arm64/boot/dts/amlogic/gxm_skt.dts
arch/arm64/configs/meson64_defconfig
drivers/amlogic/media/video_processor/Kconfig
drivers/amlogic/media/video_processor/Makefile
drivers/amlogic/media/video_processor/ionvideo/Kconfig [new file with mode: 0644]
drivers/amlogic/media/video_processor/ionvideo/Makefile [new file with mode: 0644]
drivers/amlogic/media/video_processor/ionvideo/ion_priv.h [new file with mode: 0644]
drivers/amlogic/media/video_processor/ionvideo/ionvideo.c [new file with mode: 0644]
drivers/amlogic/media/video_processor/ionvideo/ionvideo.h [new file with mode: 0644]
drivers/amlogic/media/video_processor/ionvideo/map.h [new file with mode: 0644]
drivers/amlogic/media/video_processor/ionvideo/ppmgr2.c [new file with mode: 0644]
drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.c [new file with mode: 0644]
drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.h [new file with mode: 0644]

index a85256a..c58a089 100644 (file)
@@ -13768,4 +13768,16 @@ M: Rongjun Chen <rongjun.chen@amlogic.com>
 F: drivers/amlogic/bluetooth/
 F: drivers/amlogic/wifi/
 
+AMLOGIC IONVIDEO DRIVER
+M: Guosong Zhou <guosong.zhou@amlogic.com>
+F: arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts
+F: arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts
+F: arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts
+F: arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts
+F: arch/arm64/boot/dts/amlogic/gxm_p401_2g.dts
+F: arch/arm64/boot/dts/amlogic/gxm_skt.dts
+F: arch/arm64/configs/meson64_defconfig
+F: drivers/amlogic/media/video_processor/Kconfig
+F: drivers/amlogic/media/video_processor/Makefile
+F: drivers/amlogic/media/video_processor/ionvideo/*
 
index 634c101..27251b8 100644 (file)
                hw-version = <2>;
        };
 
+       ionvideo {
+               compatible = "amlogic, ionvideo";
+               dev_name = "ionvideo";
+               status = "okay";
+       };
+
        amlvideo {
                compatible = "amlogic, amlvideo";
                dev_name = "amlvideo";
index f44f195..47d42b3 100644 (file)
                hw-version = <2>;
        };
 
+       ionvideo {
+               compatible = "amlogic, ionvideo";
+               dev_name = "ionvideo";
+               status = "okay";
+       };
+
        amlvideo {
                compatible = "amlogic, amlvideo";
                dev_name = "amlvideo";
index a0cc6f1..02cd114 100644 (file)
        };
        /* END OF AUDIO board specific */
 
+       ionvideo {
+               compatible = "amlogic, ionvideo";
+               dev_name = "ionvideo";
+               status = "okay";
+       };
+
        amlvideo {
                compatible = "amlogic, amlvideo";
                dev_name = "amlvideo";
index 20f08e8..927c5ac 100644 (file)
                        size = <0x0 0x2000000>;
                };
 
+               ionvideo {
+                       compatible = "amlogic, ionvideo";
+                       dev_name = "ionvideo";
+                       status = "okay";
+               };
+
                amlvideo {
                        compatible = "amlogic, amlvideo";
                        dev_name = "amlvideo";
index 03af58e..9cba955 100644 (file)
                hw-version = <2>;
        };
 
+       ionvideo {
+               compatible = "amlogic, ionvideo";
+               dev_name = "ionvideo";
+               status = "okay";
+       };
+
        amlvideo {
                compatible = "amlogic, amlvideo";
                dev_name = "amlvideo";
index 937da66..9a8625f 100644 (file)
                status = "okay";
        };
 
+       ionvideo {
+               compatible = "amlogic, ionvideo";
+               dev_name = "ionvideo";
+               status = "okay";
+       };
+
        amlvideo {
                compatible = "amlogic, amlvideo";
                dev_name = "amlvideo";
index ea57275..d0d7c8a 100644 (file)
@@ -226,6 +226,8 @@ CONFIG_AMLOGIC_V4L_VIDEO=y
 CONFIG_AMLOGIC_V4L_VIDEO2=y
 CONFIG_AMLOGIC_POST_PROCESS_MANAGER=y
 CONFIG_AMLOGIC_POST_PROCESS_MANAGER_PPSCALER=y
+CONFIG_AMLOGIC_VIDEOBUF2_ION=y
+CONFIG_AMLOGIC_IONVIDEO=y
 CONFIG_AMLOGIC_MMC=y
 CONFIG_AMLOGIC_NAND=y
 CONFIG_AMLOGIC_VRTC=y
index b7e9d6c..d91cdc1 100644 (file)
@@ -15,6 +15,7 @@ if AMLOGIC_VIDEO_PROCESSOR
 
 source "drivers/amlogic/media/video_processor/video_dev/Kconfig"
 source "drivers/amlogic/media/video_processor/ppmgr/Kconfig"
+source "drivers/amlogic/media/video_processor/ionvideo/Kconfig"
 
 endif
 
index 0e21e94..230f8ab 100644 (file)
@@ -1,2 +1,3 @@
 obj-$(CONFIG_AMLOGIC_V4L_VIDEO2) += video_dev/
 obj-$(CONFIG_AMLOGIC_POST_PROCESS_MANAGER) += ppmgr/
+obj-$(CONFIG_AMLOGIC_IONVIDEO) += ionvideo/
diff --git a/drivers/amlogic/media/video_processor/ionvideo/Kconfig b/drivers/amlogic/media/video_processor/ionvideo/Kconfig
new file mode 100644 (file)
index 0000000..e9898c5
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# Amlogic ionvideo device configuation
+#
+
+menu "Amlogic ion video support"
+
+config AMLOGIC_VIDEOBUF2_ION
+       tristate "videobuf2-ion video device support"
+       depends on VIDEO_DEV
+       depends on VIDEO_V4L2
+       depends on VIDEOBUF2_CORE
+       depends on VIDEOBUF2_MEMOPS
+       depends on DMA_SHARED_BUFFER
+       default n
+
+       ---help---
+               capture ion video to user
+               select to enable capture ion video to user
+               enabled by default
+               good luck
+
+
+config AMLOGIC_IONVIDEO
+       tristate "Amlogic ion video device support"
+       depends on AMLOGIC_VIDEOBUF2_ION
+       default n
+
+       ---help---
+       capture ion video to user
+
+endmenu
diff --git a/drivers/amlogic/media/video_processor/ionvideo/Makefile b/drivers/amlogic/media/video_processor/ionvideo/Makefile
new file mode 100644 (file)
index 0000000..53f9cf7
--- /dev/null
@@ -0,0 +1,5 @@
+asflags-y=-mfloat-abi=softfp -mfpu=neon
+ccflags-y += -Idrivers/staging/android/ion
+
+obj-$(CONFIG_AMLOGIC_VIDEOBUF2_ION) += videobuf2-ion.o
+obj-$(CONFIG_AMLOGIC_IONVIDEO) += ionvideo.o ppmgr2.o
diff --git a/drivers/amlogic/media/video_processor/ionvideo/ion_priv.h b/drivers/amlogic/media/video_processor/ionvideo/ion_priv.h
new file mode 100644 (file)
index 0000000..c301dbb
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * drivers/amlogic/media/video_processor/ionvideo/ion_priv.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _ION_PRIV_H
+#define _ION_PRIV_H
+
+#include <ion.h>
+#include <linux/kref.h>
+#include <linux/mm_types.h>
+#include <linux/mutex.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/shrinker.h>
+#include <linux/types.h>
+
+struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
+
+/**
+ * struct ion_buffer - metadata for a particular buffer
+ * @ref:               refernce count
+ * @node:              node in the ion_device buffers tree
+ * @dev:               back pointer to the ion_device
+ * @heap:              back pointer to the heap the buffer came from
+ * @flags:             buffer specific flags
+ * @size:              size of the buffer
+ * @priv_virt:         private data to the buffer representable as
+ *                     a void *
+ * @priv_phys:         private data to the buffer representable as
+ *                     an ion_phys_addr_t (and someday a phys_addr_t)
+ * @lock:              protects the buffers cnt fields
+ * @kmap_cnt:          number of times the buffer is mapped to the kernel
+ * @vaddr:             the kenrel mapping if kmap_cnt is not zero
+ * @dmap_cnt:          number of times the buffer is mapped for dma
+ * @sg_table:          the sg table for the buffer if dmap_cnt is not zero
+ * @dirty:             bitmask representing which pages of this buffer have
+ *                     been dirtied by the cpu and need cache maintenance
+ *                     before dma
+ * @vmas:              list of vma's mapping this buffer
+ * @handle_count:      count of handles referencing this buffer
+ * @task_comm:         taskcomm of last client to reference this buffer in a
+ *                     handle, used for debugging
+ * @pid:               pid of last client to reference this buffer in a
+ *                     handle, used for debugging
+ */
+struct ion_buffer {
+       struct kref ref;
+       struct rb_node node;
+       struct ion_device *dev;
+       struct ion_heap *heap;
+       unsigned long flags;
+       size_t size;
+       union {
+               void *priv_virt;
+               ion_phys_addr_t priv_phys;
+       };
+       struct mutex lock;
+       int kmap_cnt;
+       void *vaddr;
+       int dmap_cnt;
+       struct sg_table *sg_table;
+       unsigned long *dirty;
+       struct list_head vmas;
+       /* used to track orphaned buffers */
+       int handle_count;
+       char task_comm[TASK_COMM_LEN];
+       pid_t pid;
+};
+
+/**
+ * struct ion_heap_ops - ops to operate on a given heap
+ * @allocate:          allocate memory
+ * @free:              free memory
+ * @phys               get physical address of a buffer (only define on
+ *                     physically contiguous heaps)
+ * @map_dma            map the memory for dma to a scatterlist
+ * @unmap_dma          unmap the memory for dma
+ * @map_kernel         map memory to the kernel
+ * @unmap_kernel       unmap memory to the kernel
+ * @map_user           map memory to userspace
+ */
+struct ion_heap_ops {
+       int (*allocate)(struct ion_heap *heap,
+                        struct ion_buffer *buffer, unsigned long len,
+                        unsigned long align, unsigned long flags);
+       void (*free)(struct ion_buffer *buffer);
+       int (*phys)(struct ion_heap *heap, struct ion_buffer *buffer,
+                    ion_phys_addr_t *addr, size_t *len);
+       struct sg_table * (*map_dma)(struct ion_heap *heap,
+                                       struct ion_buffer *buffer);
+       void (*unmap_dma)(struct ion_heap *heap, struct ion_buffer *buffer);
+       void * (*map_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
+       void (*unmap_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
+       int (*map_user)(struct ion_heap *mapper, struct ion_buffer *buffer,
+                        struct vm_area_struct *vma);
+};
+
+/**
+ * struct ion_heap - represents a heap in the system
+ * @node:              rb node to put the heap on the device's tree of heaps
+ * @dev:               back pointer to the ion_device
+ * @type:              type of heap
+ * @ops:               ops struct as above
+ * @id:                        id of heap, also indicates priority of this heap when
+ *                     allocating.  These are specified by platform data and
+ *                     MUST be unique
+ * @name:              used for debugging
+ * @debug_show:                called when heap debug file is read to add any
+ *                     heap specific debug info to output
+ *
+ * Represents a pool of memory from which buffers can be made.  In some
+ * systems the only heap is regular system memory allocated via vmalloc.
+ * On others, some blocks might require large physically contiguous buffers
+ * that are allocated from a specially reserved heap.
+ */
+struct ion_heap {
+       struct plist_node node;
+       struct ion_device *dev;
+       enum ion_heap_type type;
+       struct ion_heap_ops *ops;
+       unsigned int id;
+       const char *name;
+       int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *);
+};
+
+/**
+ * ion_buffer_cached - this ion buffer is cached
+ * @buffer:            buffer
+ *
+ * indicates whether this ion buffer is cached
+ */
+bool ion_buffer_cached(struct ion_buffer *buffer);
+
+/**
+ * ion_buffer_fault_user_mappings - fault in user mappings of this buffer
+ * @buffer:            buffer
+ *
+ * indicates whether userspace mappings of this buffer will be faulted
+ * in, this can affect how buffers are allocated from the heap.
+ */
+bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer);
+
+/**
+ * ion_device_create - allocates and returns an ion device
+ * @custom_ioctl:      arch specific ioctl function if applicable
+ *
+ * returns a valid device or -PTR_ERR
+ */
+struct ion_device *ion_device_create(long (*custom_ioctl)
+                                    (struct ion_client *client,
+                                     unsigned int cmd,
+                                     unsigned long arg));
+
+/**
+ * ion_device_destroy - free and device and it's resource
+ * @dev:               the device
+ */
+void ion_device_destroy(struct ion_device *dev);
+
+/**
+ * ion_device_add_heap - adds a heap to the ion device
+ * @dev:               the device
+ * @heap:              the heap to add
+ */
+void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap);
+
+/**
+ * some helpers for common operations on buffers using the sg_table
+ * and vaddr fields
+ */
+void *ion_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer);
+void ion_heap_unmap_kernel(struct ion_heap *heap, struct ion_buffer *buffer);
+int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
+                       struct vm_area_struct *area);
+int ion_heap_buffer_zero(struct ion_buffer *buffer);
+
+
+/**
+ * functions for creating and destroying the built in ion heaps.
+ * architectures can add their own custom architecture specific
+ * heaps as appropriate.
+ */
+
+struct ion_heap *ion_heap_create(struct ion_platform_heap *platform_heap);
+void ion_heap_destroy(struct ion_heap *heap);
+
+struct ion_heap *
+       ion_system_heap_create(struct ion_platform_heap *platform_heap);
+
+void ion_system_heap_destroy(struct ion_heap *heap);
+
+struct ion_heap *
+       ion_system_contig_heap_create(struct ion_platform_heap *platform_heap);
+
+void ion_system_contig_heap_destroy(struct ion_heap *heap);
+
+struct ion_heap *
+       ion_carveout_heap_create(struct ion_platform_heap *platform_heap);
+
+void ion_carveout_heap_destroy(struct ion_heap *heap);
+
+struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *platform_heap);
+void ion_chunk_heap_destroy(struct ion_heap *heap);
+/**
+ * kernel api to allocate/free from carveout -- used when carveout is
+ * used to back an architecture specific custom heap
+ */
+ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, unsigned long size,
+                                     unsigned long align);
+void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
+                      unsigned long size);
+/**
+ * The carveout heap returns physical addresses, since 0 may be a valid
+ * physical address, this is used to indicate allocation failed
+ */
+#define ION_CARVEOUT_ALLOCATE_FAIL -1
+
+/**
+ * functions for creating and destroying a heap pool -- allows you
+ * to keep a pool of pre allocated memory to use from your heap.  Keeping
+ * a pool of memory that is ready for dma, ie any cached mapping have been
+ * invalidated from the cache, provides a significant performance benefit on
+ * many systems
+ */
+
+/**
+ * struct ion_page_pool - pagepool struct
+ * @high_count:                number of highmem items in the pool
+ * @low_count:         number of lowmem items in the pool
+ * @high_items:                list of highmem items
+ * @low_items:         list of lowmem items
+ * @shrinker:          a shrinker for the items
+ * @mutex:             lock protecting this struct and especially the count
+ *                     item list
+ * @alloc:             function to be used to allocate pageory when the pool
+ *                     is empty
+ * @free:              function to be used to free pageory back to the system
+ *                     when the shrinker fires
+ * @gfp_mask:          gfp_mask to use from alloc
+ * @order:             order of pages in the pool
+ * @list:              plist node for list of pools
+ *
+ * Allows you to keep a pool of pre allocated pages to use from your heap.
+ * Keeping a pool of pages that is ready for dma, ie any cached mapping have
+ * been invalidated from the cache, provides a significant performance benefit
+ * on many systems
+ */
+struct ion_page_pool {
+       int high_count;
+       int low_count;
+       struct list_head high_items;
+       struct list_head low_items;
+       struct mutex mutex;
+       void * (*alloc)(struct ion_page_pool *pool);
+       void (*free)(struct ion_page_pool *pool, struct page *page);
+       gfp_t gfp_mask;
+       unsigned int order;
+       struct plist_node list;
+};
+
+struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order);
+void ion_page_pool_destroy(struct ion_page_pool *pool);
+void *ion_page_pool_alloc(struct ion_page_pool *pool);
+void ion_page_pool_free(struct ion_page_pool *pool, struct page *page);
+
+#endif /* _ION_PRIV_H */
diff --git a/drivers/amlogic/media/video_processor/ionvideo/ionvideo.c b/drivers/amlogic/media/video_processor/ionvideo/ionvideo.c
new file mode 100644 (file)
index 0000000..cca5600
--- /dev/null
@@ -0,0 +1,1514 @@
+/*
+ * drivers/amlogic/media/video_processor/ionvideo/ionvideo.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/amlogic/media/vout/vout_notify.h>
+#include <linux/amlogic/media/vfm/vframe.h>
+#include <linux/amlogic/media/vfm/vframe_provider.h>
+#include <linux/amlogic/media/vfm/vframe_receiver.h>
+#include "ionvideo.h"
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+#include <linux/platform_device.h>
+
+#define IONVIDEO_MODULE_NAME "ionvideo"
+
+#define IONVIDEO_VERSION "1.0"
+#define RECEIVER_NAME "ionvideo"
+
+#define V4L2_CID_USER_AMLOGIC_IONVIDEO_BASE  (V4L2_CID_USER_BASE + 0x1100)
+
+static struct mutex ppmgr2_ge2d_canvas_mutex;
+
+static unsigned int video_nr_base = 13;
+module_param(video_nr_base, uint, 0644);
+MODULE_PARM_DESC(video_nr_base, "videoX start number, 13 is the base nr");
+
+static int scaling_rate = 100;
+static int ionvideo_seek_flag;
+
+#ifdef CONFIG_MULTI_DEC
+static unsigned int n_devs = 9;
+#else
+static unsigned int n_devs = 1;
+#endif
+
+module_param(n_devs, uint, 0644);
+MODULE_PARM_DESC(n_devs, "number of video devices to create");
+
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+static unsigned int vid_limit = 16;
+module_param(vid_limit, uint, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
+static unsigned int freerun_mode = 1;
+module_param(freerun_mode, uint, 0664);
+MODULE_PARM_DESC(freerun_mode, "av synchronization");
+
+static const struct ionvideo_fmt formats[] = {
+       {.name = "RGB32 (LE)",
+       .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
+       .depth = 32, },
+
+       {.name = "RGB565 (LE)",
+       .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+       .depth = 16, },
+
+       {.name = "RGB24 (LE)",
+       .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+       .depth = 24, },
+
+       {.name = "RGB24 (BE)",
+       .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+       .depth = 24, },
+
+       {.name = "12  Y/CbCr 4:2:0",
+       .fourcc = V4L2_PIX_FMT_NV12,
+       .depth = 12, },
+
+       {.name = "12  Y/CrCb 4:2:0",
+       .fourcc = V4L2_PIX_FMT_NV21,
+       .depth = 12, },
+
+       {.name = "YUV420P",
+       .fourcc = V4L2_PIX_FMT_YUV420,
+       .depth = 12, },
+
+       {.name = "YVU420P",
+       .fourcc = V4L2_PIX_FMT_YVU420,
+       .depth = 12, }
+};
+
+/* supported controls
+ * static struct v4l2_queryctrl ionvideo_node_qctrl[] =
+ * {
+ *  {
+ *             .id = V4L2_CID_USER_AMLOGIC_IONVIDEO_BASE,
+ *             .type = V4L2_CTRL_TYPE_INTEGER,
+ *             .name = "freerun_mode",
+ *             .minimum = 0,
+ *             .maximum = 1,
+ *             .step = 1,
+ *             .default_value = 1,
+ *             .flags = V4L2_CTRL_FLAG_SLIDER,
+ *     }
+ *     };
+ */
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+                               struct v4l2_control *ctrl)
+{
+       struct ionvideo_dev *dev = video_drvdata(file);
+
+       if (ctrl->id == V4L2_CID_USER_AMLOGIC_IONVIDEO_BASE) {
+               if (ctrl->value) {
+                       dev->freerun_mode = 1;
+                       IONVID_INFO("ionvideo: set freerun mode\n");
+               }
+       }
+       return 0;
+}
+
+static const struct ionvideo_fmt *__get_format(u32 pixelformat)
+{
+       const struct ionvideo_fmt *fmt;
+       unsigned int k;
+
+       for (k = 0; k < ARRAY_SIZE(formats); k++) {
+               fmt = &formats[k];
+               if (fmt->fourcc == pixelformat)
+                       break;
+       }
+
+       if (k == ARRAY_SIZE(formats))
+               return NULL;
+
+       return &formats[k];
+}
+
+static const struct ionvideo_fmt *get_format(struct v4l2_format *f)
+{
+       return __get_format(f->fmt.pix.pixelformat);
+}
+
+static DEFINE_SPINLOCK(devlist_lock);
+static unsigned long ionvideo_devlist_lock(void)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&devlist_lock, flags);
+       return flags;
+}
+
+static void ionvideo_devlist_unlock(unsigned long flags)
+{
+       spin_unlock_irqrestore(&devlist_lock, flags);
+}
+
+static LIST_HEAD(ionvideo_devlist);
+
+static DEFINE_SPINLOCK(ion_states_lock);
+static int ionvideo_vf_get_states(struct vframe_states *states)
+{
+       int ret = -1;
+       unsigned long flags;
+       struct vframe_provider_s *vfp;
+
+       vfp = vf_get_provider(RECEIVER_NAME);
+       spin_lock_irqsave(&ion_states_lock, flags);
+       if (vfp && vfp->ops && vfp->ops->vf_states)
+               ret = vfp->ops->vf_states(states, vfp->op_arg);
+
+       spin_unlock_irqrestore(&ion_states_lock, flags);
+       return ret;
+}
+
+/* ------------------------------------------------------------------
+ * DMA and thread functions
+ * ------------------------------------------------------------------
+ */
+unsigned int get_ionvideo_debug(void)
+{
+       return debug;
+}
+EXPORT_SYMBOL(get_ionvideo_debug);
+
+static void videoc_omx_compute_pts(struct ionvideo_dev *dev,
+                                       struct vframe_s *vf)
+{
+       if (dev->pts == 0) {
+               if (dev->is_omx_video_started == 0) {
+                       dev->pts = dev->last_pts_us64
+                               + (DUR2PTS(vf->duration) * 100 / 9);
+                       if ((vf->type & 0x1) == VIDTYPE_INTERLACE)
+                               dev->pts += (DUR2PTS(vf->duration) * 100 / 9);
+               }
+       }
+       if (dev->is_omx_video_started)
+               dev->is_omx_video_started = 0;
+
+       dev->last_pts_us64 = dev->pts;
+}
+
+#if 0
+static int canvas_is_valid(struct ionvideo_dev *dev, int index)
+{
+       struct ppmgr2_device *ppd;
+       int ret = 0;
+
+       if (!dev)
+               return 0;
+       ppd = &dev->ppmgr2_dev;
+       if (!ppd)
+               return 0;
+       if (ppd->canvas_id[index] >= 0)
+               ret = 1;
+       if (ppd->canvas_id[index] < 0)
+               IONVID_ERR("cancas %d is invalid\n", ppd->canvas_id[index]);
+       return ret;
+}
+#endif
+
+static int ionvideo_fillbuff(struct ionvideo_dev *dev,
+                               struct ionvideo_buffer *buf)
+{
+
+       struct vframe_s *vf;
+#ifdef IONVIDEO_DEBUG
+       struct vb2_buffer *vb = &(buf->vb);
+#endif
+       int ret = 0;
+       /* ------------------------------------------------------- */
+#if 0
+       if (!canvas_is_valid(dev, vb->v4l2_buf.index))
+               return -1;
+#endif
+       vf = vf_get(dev->vf_receiver_name);
+       if (!vf)
+               return -EAGAIN;
+       if (vf->flag & VFRAME_FLAG_SWITCHING_FENSE) {
+               vf_put(vf, dev->vf_receiver_name);
+               return -EAGAIN;
+       }
+
+       if (vf && dev->once_record == 1) {
+               dev->once_record = 0;
+               if ((vf->type & VIDTYPE_INTERLACE_BOTTOM) == 0x3)
+                       dev->ppmgr2_dev.bottom_first = 1;
+               else
+                       dev->ppmgr2_dev.bottom_first = 0;
+
+       }
+       if (dev->freerun_mode == 0) {
+               if ((vf->type & 0x1) == VIDTYPE_INTERLACE) {
+                       if ((dev->ppmgr2_dev.bottom_first
+                               && (vf->type & 0x2)) || (dev
+                               ->ppmgr2_dev
+                               .bottom_first
+                               == 0
+                               && ((vf
+                               ->type
+                               & 0x2)
+                               == 0))) {
+                               buf->pts = vf->pts;
+                               buf->duration = vf->duration;
+                       }
+               } else {
+                       buf->pts = vf->pts;
+                       buf->duration = vf->duration;
+               }
+#ifdef IONVIDEO_DEBUG
+               ret = ppmgr2_process(vf, &dev->ppmgr2_dev, vb->v4l2_buf.index);
+#endif
+               if (ret) {
+                       vf_put(vf, dev->vf_receiver_name);
+                       return ret;
+               }
+               vf_put(vf, dev->vf_receiver_name);
+       } else {
+               if ((vf->type & 0x1) == VIDTYPE_INTERLACE) {
+                       if ((dev->ppmgr2_dev.bottom_first
+                               && (vf->type & 0x2)) || (dev
+                               ->ppmgr2_dev
+                               .bottom_first
+                               == 0
+                               && ((vf
+                               ->type
+                               & 0x2)
+                               == 0)))
+                               dev->pts = vf->pts_us64;
+               } else
+                       dev->pts = vf->pts_us64;
+               /* for omx AdaptivePlayback */
+               if (vf->width <= ((dev->width + 31) & (~31)))
+                       dev->ppmgr2_dev.dst_width = vf->width;
+               if (vf->height <= dev->height)
+                       dev->ppmgr2_dev.dst_height = vf->height;
+
+               if ((dev->ppmgr2_dev.dst_width >= 1920) && (dev->ppmgr2_dev
+                       .dst_height
+                       >= 1080)
+                       && (vf->type & VIDTYPE_INTERLACE)) {
+                       dev->ppmgr2_dev.dst_width = dev->ppmgr2_dev.dst_width
+                               * scaling_rate
+                                                       / 100;
+                       dev->ppmgr2_dev.dst_height = dev->ppmgr2_dev.dst_height
+                               * scaling_rate
+                                                       / 100;
+               }
+#ifdef IONVIDEO_DEBUG
+               ret = ppmgr2_process(vf, &dev->ppmgr2_dev, vb->v4l2_buf.index);
+#endif
+               if (ret) {
+                       vf_put(vf, dev->vf_receiver_name);
+                       return ret;
+               }
+               videoc_omx_compute_pts(dev, vf);
+               vf_put(vf, dev->vf_receiver_name);
+#ifdef IONVIDEO_DEBUG
+               buf->vb.v4l2_buf.timestamp.tv_sec = dev->pts >> 32;
+               buf->vb.v4l2_buf.timestamp.tv_usec = dev->pts & 0xFFFFFFFF;
+               buf->vb.v4l2_buf.timecode.type = dev->ppmgr2_dev.dst_width;
+               buf->vb.v4l2_buf.timecode.flags = dev->ppmgr2_dev.dst_height;
+#endif
+       }
+       /* ------------------------------------------------------- */
+       return 0;
+}
+
+static int ionvideo_size_changed(struct ionvideo_dev *dev, int aw, int ah)
+{
+
+       v4l_bound_align_image(&aw, 48, MAX_WIDTH, 5, &ah, 32, MAX_HEIGHT, 0, 0);
+       dev->c_width = aw;
+       dev->c_height = ah;
+       if (aw != dev->width || ah != dev->height) {
+               dprintk(dev, 2, "Video frame size changed w:%d h:%d\n", aw, ah);
+               return -EAGAIN;
+       }
+       return 0;
+}
+
+static void ionvideo_thread_tick(struct ionvideo_dev *dev)
+{
+       struct ionvideo_dmaqueue *dma_q = &dev->vidq;
+       struct ionvideo_buffer *buf;
+       unsigned long flags = 0;
+       struct vframe_s *vf;
+
+       int w, h;
+
+       dprintk(dev, 4, "Thread tick\n");
+       /* video seekTo clear list */
+
+       if (!dev)
+               return;
+
+       vf = vf_peek(dev->vf_receiver_name);
+       if (!vf) {
+               dev->vf_wait_cnt++;
+               /* msleep(5); */
+               usleep_range(1000, 2000);
+               return;
+       }
+       dev->ppmgr2_dev.dst_width = dev->width;
+       dev->ppmgr2_dev.dst_height = dev->height;
+       if ((vf->width >= 1920) && (vf->height >= 1080)
+               && (vf->type & VIDTYPE_INTERLACE)) {
+               dev->ppmgr2_dev.dst_width = vf->width * scaling_rate / 100;
+               dev->ppmgr2_dev.dst_height = vf->height * scaling_rate / 100;
+               w = dev->ppmgr2_dev.dst_width;
+               h = dev->ppmgr2_dev.dst_height;
+       } else {
+               w = vf->width;
+               h = vf->height;
+       }
+       if (dev->freerun_mode == 0 && ionvideo_size_changed(dev, w, h)) {
+               /* msleep(10); */
+               usleep_range(4000, 5000);
+               return;
+       }
+       spin_lock_irqsave(&dev->slock, flags);
+       if (list_empty(&dma_q->active)) {
+               dprintk(dev, 3, "No active queue to serve\n");
+               spin_unlock_irqrestore(&dev->slock, flags);
+               schedule_timeout_interruptible(msecs_to_jiffies(20));
+               return;
+       }
+       buf = list_entry(dma_q->active.next, struct ionvideo_buffer, list);
+       spin_unlock_irqrestore(&dev->slock, flags);
+       /* Fill buffer */
+       if (ionvideo_fillbuff(dev, buf))
+               return;
+       dev->vf_wait_cnt = 0;
+
+       spin_lock_irqsave(&dev->slock, flags);
+       list_del(&buf->list);
+       spin_unlock_irqrestore(&dev->slock, flags);
+       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+       dma_q->vb_ready++;
+#ifdef IONVIDEO_DEBUG
+       dprintk(dev, 4, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
+#endif
+}
+
+#define frames_to_ms(frames)                                   \
+((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR)
+
+static void ionvideo_sleep(struct ionvideo_dev *dev)
+{
+       struct ionvideo_dmaqueue *dma_q = &dev->vidq;
+       /* int timeout; */
+       DECLARE_WAITQUEUE(wait, current);
+
+       dprintk(dev, 4, "%s dma_q=0x%08lx\n", __func__, (unsigned long)dma_q);
+
+       add_wait_queue(&dma_q->wq, &wait);
+       if (kthread_should_stop())
+               goto stop_task;
+
+       /* Calculate time to wake up */
+       /* timeout = msecs_to_jiffies(frames_to_ms(1)); */
+
+       ionvideo_thread_tick(dev);
+
+       /* schedule_timeout_interruptible(timeout); */
+
+stop_task: remove_wait_queue(&dma_q->wq, &wait);
+       try_to_freeze();
+}
+
+static int ionvideo_thread(void *data)
+{
+       struct ionvideo_dev *dev = data;
+
+       dprintk(dev, 2, "thread started\n");
+
+       set_freezable();
+
+       dev->thread_stopped = 0;
+       for (;;) {
+               ionvideo_sleep(dev);
+
+               if (kthread_should_stop())
+                       break;
+       }
+       dev->thread_stopped = 1;
+       wake_up_interruptible(&dev->wq);
+       dprintk(dev, 2, "thread: exit\n");
+       return 0;
+}
+
+static int ionvideo_start_generating(struct ionvideo_dev *dev)
+{
+       struct ionvideo_dmaqueue *dma_q = &dev->vidq;
+
+       dev->is_omx_video_started = 1;
+
+       dprintk(dev, 2, "%s\n", __func__);
+
+       /* Resets frame counters */
+       dev->ms = 0;
+       /* dev->jiffies = jiffies; */
+
+       init_waitqueue_head(&dev->wq);
+
+       /* dma_q->ini_jiffies = jiffies; */
+       dma_q->kthread = kthread_run(ionvideo_thread, dev, dev->v4l2_dev.name);
+
+       if (IS_ERR(dma_q->kthread)) {
+               v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
+               return PTR_ERR(dma_q->kthread);
+       }
+       /* Wakes thread */
+       wake_up_interruptible(&dma_q->wq);
+
+       dprintk(dev, 2, "returning from %s\n", __func__);
+       return 0;
+}
+
+static void ionvideo_stop_generating(struct ionvideo_dev *dev)
+{
+       struct ionvideo_dmaqueue *dma_q = &dev->vidq;
+
+       dprintk(dev, 2, "%s\n", __func__);
+
+       /* shutdown control thread */
+       if (dma_q->kthread) {
+               kthread_stop(dma_q->kthread);
+               dma_q->kthread = NULL;
+       }
+       wait_event_interruptible_timeout(dev->wq,
+                       dev->thread_stopped != 0, HZ/5);
+       /*
+        * Typical driver might need to wait here until dma engine stops.
+        * In this case we can abort imiedetly, so it's just a noop.
+        */
+
+       /* Release all active buffers */
+       while (!list_empty(&dma_q->active)) {
+               struct ionvideo_buffer *buf;
+
+               buf = list_entry(dma_q->active.next, struct ionvideo_buffer,
+                                       list);
+               list_del(&buf->list);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+#ifdef IONVIDEO_DEBUG
+               dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
+#endif
+       }
+}
+/* ------------------------------------------------------------------
+ * Videobuf operations
+ * ------------------------------------------------------------------
+ */
+#ifdef IONVIDEO_DEBUG
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+                       unsigned int *nbuffers, unsigned int *nplanes,
+                       unsigned int sizes[], void *alloc_ctxs[])
+{
+       struct ionvideo_dev *dev = vb2_get_drv_priv(vq);
+       unsigned long size;
+
+       if (fmt)
+               size = fmt->fmt.pix.sizeimage;
+       else
+               size = (dev->width * dev->height * dev->pixelsize) >> 3;
+
+       if (size == 0)
+               return -EINVAL;
+
+       if (*nbuffers == 0)
+               *nbuffers = 32;
+
+       while (size * *nbuffers > vid_limit * MAX_WIDTH * MAX_HEIGHT)
+               (*nbuffers)--;
+
+       *nplanes = 1;
+
+       sizes[0] = size;
+
+       /*
+        * videobuf2-vmalloc allocator is context-less so no need to set
+        * alloc_ctxs array.
+        */
+
+       dprintk(dev, 2, "%s, count=%d, size=%ld\n", __func__, *nbuffers, size);
+
+       return 0;
+}
+#endif
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+       struct ionvideo_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       struct ionvideo_buffer *buf = container_of(vb, struct ionvideo_buffer,
+                                                       vb);
+       unsigned long size;
+#ifdef IONVIDEO_DEBUG
+       dprintk(dev, 2, "%s, field=%d\n", __func__, vb->v4l2_buf.field);
+#endif
+       WARN_ON(dev->fmt == NULL);
+
+       /*
+        * Theses properties only change when queue is idle, see s_fmt.
+        * The below checks should not be performed here, on each
+        * buffer_prepare (i.e. on each qbuf). Most of the code in this function
+        * should thus be moved to buffer_init and s_fmt.
+        */
+       if (dev->width < 48 || dev->width > MAX_WIDTH
+               || dev->height < 32 || dev->height > MAX_HEIGHT)
+               return -EINVAL;
+
+       size = (dev->width * dev->height * dev->pixelsize) >> 3;
+       if (vb2_plane_size(vb, 0) < size) {
+               dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n",
+                       __func__, vb2_plane_size(vb, 0), size);
+               return -EINVAL;
+       }
+
+       vb2_set_plane_payload(&buf->vb, 0, size);
+
+       buf->fmt = dev->fmt;
+
+       return 0;
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+       struct ionvideo_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       struct ionvideo_buffer *buf = container_of(vb, struct ionvideo_buffer,
+                                                       vb);
+       struct ionvideo_dmaqueue *vidq = &dev->vidq;
+       unsigned long flags = 0;
+
+       dprintk(dev, 2, "%s\n", __func__);
+
+       spin_lock_irqsave(&dev->slock, flags);
+       list_add_tail(&buf->list, &vidq->active);
+       spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+       struct ionvideo_dev *dev = vb2_get_drv_priv(vq);
+       struct ionvideo_dmaqueue *dma_q = &dev->vidq;
+
+       dev->is_actived = 1;
+       dma_q->vb_ready = 0;
+       dprintk(dev, 2, "%s\n", __func__);
+       return ionvideo_start_generating(dev);
+}
+
+/* abort streaming and wait for last buffer */
+static void stop_streaming(struct vb2_queue *vq)
+{
+       struct ionvideo_dev *dev = vb2_get_drv_priv(vq);
+
+       dev->is_actived = 0;
+       dprintk(dev, 2, "%s\n", __func__);
+       ionvideo_stop_generating(dev);
+}
+
+static void ionvideo_lock(struct vb2_queue *vq)
+{
+       struct ionvideo_dev *dev = vb2_get_drv_priv(vq);
+
+       mutex_lock(&dev->mutex);
+}
+
+static void ionvideo_unlock(struct vb2_queue *vq)
+{
+       struct ionvideo_dev *dev = vb2_get_drv_priv(vq);
+
+       mutex_unlock(&dev->mutex);
+}
+
+static const struct vb2_ops ionvideo_video_qops = {
+#ifdef IONVIDEO_DEBUG
+       .queue_setup = queue_setup,
+#endif
+       .buf_prepare = buffer_prepare,
+       .buf_queue = buffer_queue,
+       .start_streaming = start_streaming,
+       .stop_streaming = stop_streaming,
+       .wait_prepare = ionvideo_unlock,
+       .wait_finish = ionvideo_lock,
+};
+
+/* ------------------------------------------------------------------
+ * IOCTL vidioc handling
+ * ------------------------------------------------------------------
+ */
+static int vidioc_open(struct file *file)
+{
+       struct ionvideo_dev *dev = video_drvdata(file);
+
+       if (dev->fd_num > 0 || ppmgr2_init(&(dev->ppmgr2_dev)) < 0) {
+               pr_err("vidioc_open error\n");
+               return -EBUSY;
+       }
+
+       dev->fd_num++;
+       dev->pts = 0;
+       dev->c_width = 0;
+       dev->c_height = 0;
+       dev->once_record = 1;
+       dev->ppmgr2_dev.bottom_first = 0;
+       dev->skip_frames = 0;
+       dev->vf_wait_cnt = 0;
+       /*for libplayer osd*/
+       dev->freerun_mode = freerun_mode;
+       dprintk(dev, 2, "vidioc_open\n");
+       IONVID_INFO("ionvideo open\n");
+       init_waitqueue_head(&dev->wq);
+       return v4l2_fh_open(file);
+}
+
+static int vidioc_release(struct file *file)
+{
+       struct ionvideo_dev *dev = video_drvdata(file);
+
+       ionvideo_stop_generating(dev);
+       IONVID_INFO("ionvideo_stop_generating!!!!\n");
+       ppmgr2_release(&(dev->ppmgr2_dev));
+       dprintk(dev, 2, "vidioc_release\n");
+       IONVID_INFO("ionvideo release\n");
+       if (dev->fd_num > 0)
+               dev->fd_num--;
+
+       dev->once_record = 0;
+       return vb2_fop_release(file);
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+                               struct v4l2_capability *cap)
+{
+       struct ionvideo_dev *dev = video_drvdata(file);
+
+       strcpy(cap->driver, "ionvideo");
+       strcpy(cap->card, "ionvideo");
+       snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+                       dev->v4l2_dev.name);
+       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
+                               | V4L2_CAP_READWRITE;
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+       return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+                                       struct v4l2_fmtdesc *f)
+{
+       const struct ionvideo_fmt *fmt;
+
+       if (f->index >= ARRAY_SIZE(formats))
+               return -EINVAL;
+
+       fmt = &formats[f->index];
+
+       strlcpy(f->description, fmt->name, sizeof(f->description));
+       f->pixelformat = fmt->fourcc;
+       return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct ionvideo_dev *dev = video_drvdata(file);
+       struct vb2_queue *q = &dev->vb_vidq;
+       int ret = 0;
+       unsigned long flags;
+
+       if (dev->freerun_mode == 0) {
+               if (dev->c_width == 0 || dev->c_height == 0)
+                       return -EINVAL;
+
+               f->fmt.pix.width = dev->c_width;
+               f->fmt.pix.height = dev->c_height;
+               spin_lock_irqsave(&q->done_lock, flags);
+               ret = list_empty(&q->done_list);
+               spin_unlock_irqrestore(&q->done_lock, flags);
+               if (!ret)
+                       return -EAGAIN;
+
+       } else {
+               f->fmt.pix.width = dev->width;
+               f->fmt.pix.height = dev->height;
+       }
+       f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+       f->fmt.pix.pixelformat = dev->fmt->fourcc;
+       f->fmt.pix.bytesperline = (f->fmt.pix.width * dev->fmt->depth) >> 3;
+       f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+       if (dev->fmt->is_yuv)
+               f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+       else
+               f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+
+       return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct ionvideo_dev *dev = video_drvdata(file);
+       const struct ionvideo_fmt *fmt;
+
+       fmt = get_format(f);
+       if (!fmt) {
+               dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
+                       f->fmt.pix.pixelformat);
+               return -EINVAL;
+       }
+
+       f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+       v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 4,
+                               &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
+       f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+       f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+       if (fmt->is_yuv)
+               f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+       else
+               f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+       f->fmt.pix.priv = 0;
+       return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct ionvideo_dev *dev = video_drvdata(file);
+       struct vb2_queue *q = &dev->vb_vidq;
+
+       int ret = vidioc_try_fmt_vid_cap(file, priv, f);
+
+       if (ret < 0)
+               return ret;
+
+       if (vb2_is_busy(q)) {
+               dprintk(dev, 1, "%s device busy\n", __func__);
+               return -EBUSY;
+       }
+       dev->fmt = get_format(f);
+       dev->pixelsize = dev->fmt->depth;
+       dev->width = f->fmt.pix.width;
+       dev->height = f->fmt.pix.height;
+       if ((dev->width == 0) || (dev->height == 0))
+               IONVID_ERR("ion buffer w/h info is invalid!!!!!!!!!!!\n");
+
+       return 0;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *fh,
+                                       struct v4l2_frmsizeenum *fsize)
+{
+       static const struct v4l2_frmsize_stepwise sizes = {
+               48, MAX_WIDTH, 4, 32, MAX_HEIGHT, 1};
+       int i;
+
+       if (fsize->index)
+               return -EINVAL;
+       for (i = 0; i < ARRAY_SIZE(formats); i++)
+               if (formats[i].fourcc == fsize->pixel_format)
+                       break;
+       if (i == ARRAY_SIZE(formats))
+               return -EINVAL;
+       fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+       fsize->stepwise = sizes;
+       return 0;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+       struct ionvideo_dev *dev = video_drvdata(file);
+       struct ionvideo_dmaqueue *dma_q = &dev->vidq;
+       struct ppmgr2_device *ppmgr2_dev = &(dev->ppmgr2_dev);
+       int ret = 0;
+
+       p->length = 0;
+       ret = vb2_ioctl_qbuf(file, priv, p);
+       if (ret != 0)
+               return ret;
+
+
+       if (!ppmgr2_dev->phy_addr[p->index]) {
+               struct vb2_buffer *vb;
+               struct vb2_queue *q;
+               void *phy_addr = NULL;
+
+               q = dev->vdev.queue;
+               vb = q->bufs[p->index];
+               phy_addr = vb2_plane_cookie(vb, 0);
+               ppmgr2_dev->phy_addr[p->index] = phy_addr;
+               ppmgr2_dev->dst_buffer_width = ALIGN(dev->width, 32);
+               ppmgr2_dev->dst_buffer_height = dev->height;
+               ppmgr2_dev->ge2d_fmt =  v4l_to_ge2d_format(dev->fmt->fourcc);
+               if (phy_addr) {
+#if 0
+                       ret = ppmgr2_canvas_config(ppmgr2_dev, dev->width,
+                                                       dev->height,
+                                                       dev->fmt->fourcc,
+                                                       phy_addr, p->index);
+#endif
+               } else {
+                       return -ENOMEM;
+               }
+       }
+       wake_up_interruptible(&dma_q->wq);
+       return ret;
+}
+
+static int vidioc_synchronization_dqbuf(struct file *file, void *priv,
+                                       struct v4l2_buffer *p)
+{
+       struct vb2_buffer *vb = NULL;
+       struct vb2_queue *q;
+       struct ionvideo_dev *dev = video_drvdata(file);
+       struct ionvideo_buffer *buf;
+       int ret = 0;
+       int d = 0;
+       unsigned long flags;
+
+       q = dev->vdev.queue;
+       if (dev->receiver_register) {
+               /* clear the frame buffer queue */
+               while (!list_empty(&q->done_list)) {
+                       ret = vb2_ioctl_dqbuf(file, priv, p);
+                       if (ret)
+                               return ret;
+
+                       ret = vb2_ioctl_qbuf(file, priv, p);
+                       if (ret)
+                               return ret;
+
+               }
+               IONVID_INFO("init to clear the done list buffer.done\n");
+               dev->receiver_register = 0;
+               dev->is_video_started = 0;
+               return -EAGAIN;
+       }
+       spin_lock_irqsave(&q->done_lock, flags);
+       if (list_empty(&q->done_list)) {
+               spin_unlock_irqrestore(&q->done_lock, flags);
+               return -EAGAIN;
+       }
+       vb = list_first_entry(&q->done_list, struct vb2_buffer,
+                               done_entry);
+       spin_unlock_irqrestore(&q->done_lock, flags);
+
+       buf = container_of(vb, struct ionvideo_buffer, vb);
+       if (dev->is_video_started == 0) {
+               IONVID_INFO("Execute the VIDEO_START cmd. pts=%llx\n",
+                       buf->pts);
+               tsync_avevent_locked(
+                       VIDEO_START,
+                       buf->pts ? buf->pts : timestamp_vpts_get());
+               d = 0;
+               dev->is_video_started = 1;
+       } else {
+               if (buf->pts == 0) {
+                       buf->pts =
+                               timestamp_vpts_get() + DUR2PTS(
+                                       buf->duration);
+               }
+
+               if (abs(timestamp_pcrscr_get() - buf->pts) >
+                       tsync_vpts_discontinuity_margin()) {
+                       tsync_avevent_locked(
+                               VIDEO_TSTAMP_DISCONTINUITY,
+                               buf->pts);
+               } else {
+                       timestamp_vpts_set(buf->pts);
+               }
+               d = (buf->pts - timestamp_pcrscr_get());
+       }
+
+       if (d > 450) {
+               return -EAGAIN;
+       } else if (d < -11520) {
+               int s = 3;
+
+               while (s--) {
+                       ret = vb2_ioctl_dqbuf(file, priv, p);
+                       if (ret)
+                               return ret;
+
+
+                       if (buf->pts == 0) {
+                               buf->pts =
+                                       timestamp_vpts_get() + DUR2PTS(
+                                               buf->duration);
+                       }
+
+                       if (abs(timestamp_pcrscr_get() - buf->pts) >
+                               tsync_vpts_discontinuity_margin()) {
+                               tsync_avevent_locked(
+                                       VIDEO_TSTAMP_DISCONTINUITY,
+                                       buf->pts);
+                       } else {
+                               timestamp_vpts_set(buf->pts);
+                       }
+
+                       if (list_empty(&q->done_list))
+                               break;
+                       ret = vb2_ioctl_qbuf(file, priv, p);
+                       if (ret)
+                               return ret;
+                       dev->skip_frames++;
+               }
+               dprintk(dev, 1, "s:%u\n", dev->skip_frames);
+       } else {
+               ret = vb2_ioctl_dqbuf(file, priv, p);
+               if (ret)
+                       return ret;
+
+       }
+       p->timestamp.tv_sec = 0;
+       p->timestamp.tv_usec = timestamp_vpts_get();
+
+       return 0;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+       struct ionvideo_dev *dev = video_drvdata(file);
+       struct ionvideo_dmaqueue *dma_q = &dev->vidq;
+       int ret = 0;
+
+       if (dev->freerun_mode == 0)
+               return vidioc_synchronization_dqbuf(file, priv, p);
+
+       ret = vb2_ioctl_dqbuf(file, priv, p);
+       if (ret == 0)
+               dma_q->vb_ready--;
+       return ret;
+}
+
+#define NUM_INPUTS 10
+/* only one input in this sample driver */
+static int vidioc_enum_input(struct file *file, void *priv,
+                               struct v4l2_input *inp)
+{
+       if (inp->index >= NUM_INPUTS)
+               return -EINVAL;
+
+       inp->type = V4L2_INPUT_TYPE_CAMERA;
+       sprintf(inp->name, "Camera %u", inp->index);
+       return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+       struct ionvideo_dev *dev = video_drvdata(file);
+
+       *i = dev->input;
+       return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+       struct ionvideo_dev *dev = video_drvdata(file);
+
+       if (i >= NUM_INPUTS)
+               return -EINVAL;
+
+       if (i == dev->input)
+               return 0;
+
+       dev->input = i;
+       return 0;
+}
+
+/* ------------------------------------------------------------------
+ * File operations for the device
+ * ------------------------------------------------------------------
+ */
+static const struct v4l2_file_operations ionvideo_fops = {
+       .owner = THIS_MODULE,
+       .open = vidioc_open,
+       .release = vidioc_release,
+       .read = vb2_fop_read,
+       .poll = vb2_fop_poll,
+       .unlocked_ioctl = video_ioctl2,/* V4L2 ioctl handler */
+       .mmap = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops ionvideo_ioctl_ops = {
+       .vidioc_querycap = vidioc_querycap,
+       .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+       .vidioc_enum_framesizes = vidioc_enum_framesizes,
+       .vidioc_reqbufs = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs = vb2_ioctl_create_bufs,
+       .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf = vb2_ioctl_querybuf,
+       .vidioc_qbuf = vidioc_qbuf,
+       .vidioc_dqbuf = vidioc_dqbuf,
+       .vidioc_enum_input = vidioc_enum_input,
+       .vidioc_g_input = vidioc_g_input,
+       .vidioc_s_input = vidioc_s_input,
+       .vidioc_streamon = vb2_ioctl_streamon,
+       .vidioc_streamoff = vb2_ioctl_streamoff,
+       .vidioc_log_status = v4l2_ctrl_log_status,
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+       .vidioc_s_ctrl = vidioc_s_ctrl,
+};
+
+static const struct video_device ionvideo_template = {
+       .name = "ionvideo",
+       .fops = &ionvideo_fops,
+       .ioctl_ops = &ionvideo_ioctl_ops,
+       .release = video_device_release_empty,
+};
+
+/* -----------------------------------------------------------------
+ * Initialization and module stuff
+ * -----------------------------------------------------------------
+ */
+/* struct vb2_dc_conf * ionvideo_dma_ctx = NULL; */
+static int ionvideo_release(void)
+{
+       struct ionvideo_dev *dev;
+       struct list_head *list;
+       unsigned long flags;
+
+       flags = ionvideo_devlist_lock();
+
+       while (!list_empty(&ionvideo_devlist)) {
+               list = ionvideo_devlist.next;
+               list_del(list);
+               ionvideo_devlist_unlock(flags);
+
+               dev = list_entry(list, struct ionvideo_dev, ionvideo_devlist);
+
+               v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+                               video_device_node_name(&dev->vdev));
+               video_unregister_device(&dev->vdev);
+               v4l2_device_unregister(&dev->v4l2_dev);
+               kfree(dev);
+
+               flags = ionvideo_devlist_lock();
+       }
+       /* vb2_dma_contig_cleanup_ctx(ionvideo_dma_ctx); */
+
+       ionvideo_devlist_unlock(flags);
+
+       return 0;
+}
+
+static int video_receiver_event_fun(int type, void *data, void *private_data)
+{
+#ifdef CONFIG_AM_VOUT
+       char *configured[2];
+       char framerate[20] = {0};
+#endif
+       struct ionvideo_dev *dev = (struct ionvideo_dev *)private_data;
+
+       if (type == VFRAME_EVENT_PROVIDER_UNREG) {
+               dev->receiver_register = 0;
+               dev->is_omx_video_started = 0;
+               /*tsync_avevent(VIDEO_STOP, 0);*/
+               IONVID_INFO("unreg:ionvideo\n");
+       } else if (type == VFRAME_EVENT_PROVIDER_REG) {
+               dev->receiver_register = 1;
+               dev->is_omx_video_started = 1;
+               dev->ppmgr2_dev.interlaced_num = 0;
+               IONVID_INFO("reg:ionvideo\n");
+       } else if (type == VFRAME_EVENT_PROVIDER_QUREY_STATE) {
+               if (dev->vf_wait_cnt > 1)
+                       return RECEIVER_INACTIVE;
+               return RECEIVER_ACTIVE;
+       } else if (type == VFRAME_EVENT_PROVIDER_FR_HINT) {
+               if ((data != NULL) && (ionvideo_seek_flag == 0)) {
+#ifdef CONFIG_AM_VOUT
+                       /*set_vframe_rate_hint((unsigned long)(data));*/
+                       sprintf(framerate, "FRAME_RATE_HINT=%lu",
+                       (unsigned long)data);
+                       configured[0] = framerate;
+                       configured[1] = NULL;
+                       kobject_uevent_env(&(dev->vdev.dev.kobj),
+                               KOBJ_CHANGE, configured);
+                       pr_info("%s: sent uevent %s\n",
+                                       __func__, configured[0]);
+#endif
+               }
+       } else if (type == VFRAME_EVENT_PROVIDER_FR_END_HINT) {
+#ifdef CONFIG_AM_VOUT
+               if (ionvideo_seek_flag == 0) {
+                       configured[0] = "FRAME_RATE_END_HINT";
+                       configured[1] = NULL;
+                       kobject_uevent_env(&(dev->vdev.dev.kobj),
+                                       KOBJ_CHANGE, configured);
+                       pr_info("%s: sent uevent %s\n",
+                               __func__, configured[0]);
+               }
+#endif
+       }
+
+       return 0;
+}
+
+static const struct vframe_receiver_op_s video_vf_receiver = {
+       .event_cb = video_receiver_event_fun
+};
+
+static int __init ionvideo_create_instance(int inst)
+{
+       struct ionvideo_dev *dev;
+       struct video_device *vfd;
+       struct vb2_queue *q;
+       int ret;
+       unsigned long flags;
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
+           "%s-%03d", IONVIDEO_MODULE_NAME, inst);
+       ret = v4l2_device_register(NULL, &dev->v4l2_dev);
+       if (ret)
+               goto free_dev;
+
+       dev->fmt = &formats[0];
+       dev->width = 640;
+       dev->height = 480;
+       dev->pixelsize = dev->fmt->depth;
+       dev->fd_num = 0;
+       dev->ionvideo_v4l_num = inst + video_nr_base;
+       dev->ppmgr2_dev.ge2d_canvas_mutex = &ppmgr2_ge2d_canvas_mutex;
+
+       /* initialize locks */
+       spin_lock_init(&dev->slock);
+
+       /* initialize queue */
+       q = &dev->vb_vidq;
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+       q->drv_priv = dev;
+       q->buf_struct_size = sizeof(struct ionvideo_buffer);
+       q->ops = &ionvideo_video_qops;
+       q->mem_ops = &vb2_ion_memops;
+#ifdef IONVIDEO_DEBUG
+       q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+#else
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+#endif
+
+#ifdef IONVIDEO_DEBUG
+       ret = vb2_queue_init(q);
+       if (ret)
+               goto unreg_dev;
+#endif
+
+       mutex_init(&dev->mutex);
+
+       /* init video dma queues */
+       INIT_LIST_HEAD(&dev->vidq.active);
+       init_waitqueue_head(&dev->vidq.wq);
+       dev->vidq.pdev = dev;
+
+       vfd = &dev->vdev;
+       *vfd = ionvideo_template;
+       vfd->dev_debug = debug;
+       vfd->v4l2_dev = &dev->v4l2_dev;
+       vfd->queue = q;
+#ifdef IONVIDEO_DEBUG
+       set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+#endif
+
+       /*
+        * Provide a mutex to v4l2 core. It will be used to protect
+        * all fops and v4l2 ioctls.
+        */
+       vfd->lock = &dev->mutex;
+       video_set_drvdata(vfd, dev);
+
+       ret = video_register_device(vfd, VFL_TYPE_GRABBER,
+                               inst + video_nr_base);
+       if (ret < 0)
+               goto unreg_dev;
+
+       dev->inst = inst;
+       snprintf(dev->vf_receiver_name, ION_VF_RECEIVER_NAME_SIZE,
+               (inst == 0) ? RECEIVER_NAME : RECEIVER_NAME ".%x",
+               inst & 0xff);
+
+       vf_receiver_init(&dev->video_vf_receiver,
+                       dev->vf_receiver_name,
+                       &video_vf_receiver, dev);
+       vf_reg_receiver(&dev->video_vf_receiver);
+       v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
+               video_device_node_name(vfd));
+
+       /* add to device list */
+       flags = ionvideo_devlist_lock();
+       list_add_tail(&dev->ionvideo_devlist, &ionvideo_devlist);
+       ionvideo_devlist_unlock(flags);
+
+       return 0;
+
+unreg_dev:
+       v4l2_device_unregister(&dev->v4l2_dev);
+free_dev:
+       kfree(dev);
+       return ret;
+}
+
+int ionvideo_assign_map(char **receiver_name, int *inst)
+{
+       unsigned long flags;
+       struct ionvideo_dev *dev = NULL;
+       struct list_head *p;
+
+       flags = ionvideo_devlist_lock();
+
+       list_for_each(p, &ionvideo_devlist) {
+               dev = list_entry(p, struct ionvideo_dev, ionvideo_devlist);
+
+               if ((dev->inst == *inst) && (!dev->mapped)) {
+                       dev->mapped = true;
+                       *inst = dev->inst;
+                       *receiver_name = dev->vf_receiver_name;
+                       ionvideo_devlist_unlock(flags);
+                       return 0;
+               }
+       }
+
+       ionvideo_devlist_unlock(flags);
+
+       return -ENODEV;
+}
+
+int ionvideo_alloc_map(char **receiver_name, int *inst)
+{
+       unsigned long flags;
+       struct ionvideo_dev *dev = NULL;
+       struct list_head *p;
+
+       flags = ionvideo_devlist_lock();
+
+       list_for_each(p, &ionvideo_devlist) {
+               dev = list_entry(p, struct ionvideo_dev, ionvideo_devlist);
+
+               if ((dev->inst >= 0) && (!dev->mapped)) {
+                       dev->mapped = true;
+                       *inst = dev->inst;
+                       *receiver_name = dev->vf_receiver_name;
+                       ionvideo_devlist_unlock(flags);
+                       return 0;
+               }
+       }
+
+       ionvideo_devlist_unlock(flags);
+       return -ENODEV;
+}
+
+void ionvideo_release_map(int inst)
+{
+       unsigned long flags;
+       struct ionvideo_dev *dev = NULL;
+       struct list_head *p;
+
+       flags = ionvideo_devlist_lock();
+
+       list_for_each(p, &ionvideo_devlist) {
+               dev = list_entry(p, struct ionvideo_dev, ionvideo_devlist);
+               if ((dev->inst == inst) && (dev->mapped)) {
+                       dev->mapped = false;
+                       pr_info("ionvideo_release_map %d OK\n", dev->inst);
+                       break;
+               }
+       }
+
+       ionvideo_devlist_unlock(flags);
+}
+
+static ssize_t vframe_states_show(struct class *class,
+                                       struct class_attribute *attr, char *buf)
+{
+       int ret = 0;
+       struct vframe_states states;
+       /* unsigned long flags; */
+
+       if (ionvideo_vf_get_states(&states) == 0) {
+               ret += sprintf(buf + ret,
+                              "vframe_pool_size=%d\n", states.vf_pool_size);
+
+               ret += sprintf(buf + ret, "vframe buf_free_num=%d\n",
+                               states.buf_free_num);
+               ret += sprintf(buf + ret, "vframe buf_recycle_num=%d\n",
+                               states.buf_recycle_num);
+               ret += sprintf(buf + ret, "vframe buf_avail_num=%d\n",
+                               states.buf_avail_num);
+       } else {
+               ret += sprintf(buf + ret, "vframe no states\n");
+       }
+
+       return ret;
+}
+
+static ssize_t scaling_rate_show(struct class *cla,
+                                       struct class_attribute *attr, char *buf)
+{
+       return snprintf(buf, 80, "current scaling rate is %d\n", scaling_rate);
+}
+
+static ssize_t scaling_rate_write(struct class *cla,
+                                       struct class_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       ssize_t size;
+       char *endp = NULL;
+       unsigned long tmp;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &tmp);
+       if (ret != 0) {
+               IONVID_ERR("ERROR parsing string:%s to unsigned long\n", buf);
+               return ret;
+       }
+       /* scaling_rate = simple_strtoul(buf, &endp, 0); */
+       scaling_rate = tmp;
+       size = endp - buf;
+       return count;
+}
+
+static ssize_t scaling_ionvideo_seek_flag_show(struct class *cla,
+                               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", ionvideo_seek_flag);
+}
+
+static ssize_t scaling_ionvideo_seek_flag_write(struct class *cla,
+                                       struct class_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       size_t r;
+
+       r = kstrtoint(buf, 0, &ionvideo_seek_flag);
+       if (r != 0)
+               return -EINVAL;
+       return count;
+}
+
+static struct class_attribute ion_video_class_attrs[] = {
+__ATTR_RO(vframe_states),
+__ATTR(scaling_rate,
+0644,
+scaling_rate_show,
+scaling_rate_write),
+__ATTR(ionvideo_seek_flag,
+0644,
+scaling_ionvideo_seek_flag_show,
+scaling_ionvideo_seek_flag_write),
+__ATTR_NULL };
+static struct class ionvideo_class = {.name = "ionvideo", .class_attrs =
+ion_video_class_attrs, };
+
+/* This routine allocates from 1 to n_devs virtual drivers.
+ * The real maximum number of virtual drivers will depend on how many drivers
+ * will succeed. This is limited to the maximum number of devices that
+ * videodev supports, which is equal to VIDEO_NUM_DEVICES.
+ */
+static int ionvideo_driver_probe(struct platform_device *pdev)
+{
+       int ret = 0, i;
+
+       ret = class_register(&ionvideo_class);
+       if (ret < 0)
+               return ret;
+       if (n_devs <= 0)
+               n_devs = 1;
+
+       mutex_init(&ppmgr2_ge2d_canvas_mutex);
+
+       for (i = 0; i < n_devs; i++) {
+               ret = ionvideo_create_instance(i);
+               if (ret) {
+                       /* If some instantiations succeeded, keep driver */
+                       if (i)
+                               ret = 0;
+                       break;
+               }
+       }
+
+       if (ret < 0) {
+               IONVID_ERR("ionvideo: error %d while loading driver\n", ret);
+               return ret;
+       }
+
+       IONVID_INFO("Video Technology Magazine Ion Video\n");
+       IONVID_INFO("Capture Board ver %s successfully loaded\n",
+       IONVIDEO_VERSION);
+
+       /* n_devs will reflect the actual number of allocated devices */
+       n_devs = i;
+
+       return ret;
+}
+
+static int ionvideo_drv_remove(struct platform_device *pdev)
+{
+       ionvideo_release();
+       class_unregister(&ionvideo_class);
+       return 0;
+}
+
+static const struct of_device_id ionvideo_dt_match[] = {
+       {
+               .compatible = "amlogic, ionvideo",
+       },
+};
+
+/* general interface for a linux driver .*/
+static struct platform_driver ionvideo_drv = {
+.probe = ionvideo_driver_probe,
+.remove = ionvideo_drv_remove,
+.driver = {
+               .name = "ionvideo",
+               .owner = THIS_MODULE,
+               .of_match_table = ionvideo_dt_match,
+       }
+};
+
+static int __init ionvideo_init(void)
+{
+       if (platform_driver_register(&ionvideo_drv)) {
+               pr_err("Failed to register ionvideo driver\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __exit ionvideo_exit(void)
+{
+       platform_driver_unregister(&ionvideo_drv);
+}
+
+MODULE_DESCRIPTION("Video Technology Magazine Ion Video Capture Board");
+MODULE_AUTHOR("Amlogic, Shuai Cao<shuai.cao@amlogic.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(IONVIDEO_VERSION);
+
+module_init(ionvideo_init);
+module_exit(ionvideo_exit);
diff --git a/drivers/amlogic/media/video_processor/ionvideo/ionvideo.h b/drivers/amlogic/media/video_processor/ionvideo/ionvideo.h
new file mode 100644 (file)
index 0000000..a5a4a6a
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * drivers/amlogic/media/video_processor/ionvideo/ionvideo.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _IONVIDEO_H
+#define _IONVIDEO_H
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/delay.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-common.h>
+#include <media/videobuf2-core.h>
+
+#include <linux/mm.h>
+/* #include <mach/mod_gate.h> */
+
+#include <linux/amlogic/media/vfm/vframe.h>
+#include <linux/amlogic/media/vfm/vframe_provider.h>
+#include <linux/amlogic/media/vfm/vframe_receiver.h>
+#include <linux/amlogic/media/ge2d/ge2d.h>
+#include <linux/amlogic/media/canvas/canvas.h>
+
+#include <linux/amlogic/media/frame_sync/timestamp.h>
+#include <linux/amlogic/media/frame_sync/tsync.h>
+#include "videobuf2-ion.h"
+
+/* Wake up at about 30 fps */
+#define WAKE_NUMERATOR 30
+#define WAKE_DENOMINATOR 1001
+
+#define MAX_WIDTH 4096
+#define MAX_HEIGHT 4096
+
+#define IONVID_INFO(fmt, args...) pr_info("ionvid: info: "fmt"", ## args)
+#define IONVID_DBG(fmt, args...) pr_debug("ionvid: dbg: "fmt"", ## args)
+#define IONVID_ERR(fmt, args...) pr_err("ionvid: err: "fmt"", ## args)
+
+#define DUR2PTS(x) ((x) - ((x) >> 4))
+
+#define dprintk(dev, level, fmt, arg...)                    \
+v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
+
+#define ppmgr2_printk(level, fmt, arg...)                   \
+do {                                                    \
+       if (get_ionvideo_debug() >= level)                  \
+               pr_debug("ppmgr2-dev: " fmt, ## arg);  \
+} while (0)
+
+#define PPMGR2_CANVAS_INDEX_SRC (PPMGR2_CANVAS_INDEX + 3)
+
+/* ------------------------------------------------------------------
+ * Basic structures
+ * ------------------------------------------------------------------
+ */
+
+struct ionvideo_fmt {
+       char *name;
+       u32 fourcc; /* v4l2 format id */
+       u8 depth;
+       bool is_yuv;
+};
+
+/* buffer for one video frame */
+struct ionvideo_buffer {
+       /* common v4l buffer stuff -- must be first */
+       struct vb2_buffer vb;
+       struct list_head list;
+       const struct ionvideo_fmt *fmt;
+       u64 pts;
+       u32 duration;
+};
+
+struct ionvideo_dmaqueue {
+       struct list_head active;
+
+       /* thread for generating video stream*/
+       struct task_struct *kthread;
+       wait_queue_head_t wq;
+       /* Counters to control fps rate */
+       int vb_ready;
+       struct ionvideo_dev *pdev;
+};
+
+struct ppmgr2_device {
+       int dst_width;
+       int dst_height;
+       int dst_buffer_width;
+       int dst_buffer_height;
+       int ge2d_fmt;
+       int canvas_id[PPMGR2_MAX_CANVAS];
+       void *phy_addr[PPMGR2_MAX_CANVAS];
+       int phy_size;
+
+       struct ge2d_context_s *context;
+       struct config_para_ex_s ge2d_config;
+
+       int angle;
+       int mirror;
+       int paint_mode;
+       int interlaced_num;
+       int bottom_first;
+
+       struct mutex *ge2d_canvas_mutex;
+};
+
+#define ION_VF_RECEIVER_NAME_SIZE 32
+
+struct ionvideo_dev {
+       struct list_head ionvideo_devlist;
+       struct v4l2_device v4l2_dev;
+       struct video_device vdev;
+       int fd_num;
+       int ionvideo_v4l_num;
+
+       spinlock_t slock;
+       struct mutex mutex;
+
+       struct ionvideo_dmaqueue vidq;
+
+       /* Several counters */
+       unsigned int ms;
+       unsigned long jiffies;
+
+       /* Input Number */
+       int input;
+
+       /* video capture */
+       const struct ionvideo_fmt *fmt;
+       unsigned int width, height;
+       unsigned int c_width, c_height;
+       struct vb2_queue vb_vidq;
+       unsigned int field_count;
+
+       unsigned int pixelsize;
+
+       struct ppmgr2_device ppmgr2_dev;
+       struct vframe_receiver_s video_vf_receiver;
+       u64 pts;
+       u8 receiver_register;
+       u8 is_video_started;
+       u32 skip;
+       int once_record;
+       u8 is_omx_video_started;
+       int is_actived;
+       u64 last_pts_us64;
+       unsigned int freerun_mode;
+       unsigned int skip_frames;
+
+       wait_queue_head_t wq;
+
+       char vf_receiver_name[ION_VF_RECEIVER_NAME_SIZE];
+       int inst;
+       bool mapped;
+       bool thread_stopped;
+       int vf_wait_cnt;
+};
+
+unsigned int get_ionvideo_debug(void);
+
+int ppmgr2_init(struct ppmgr2_device *ppd);
+int ppmgr2_canvas_config(struct ppmgr2_device *ppd, int index);
+int ppmgr2_process(struct vframe_s *vf, struct ppmgr2_device *ppd, int index);
+int ppmgr2_top_process(struct vframe_s *vf, struct ppmgr2_device *ppd,
+                       int index);
+int ppmgr2_bottom_process(struct vframe_s *vf, struct ppmgr2_device *ppd,
+                               int index);
+void ppmgr2_release(struct ppmgr2_device *ppd);
+void ppmgr2_set_angle(struct ppmgr2_device *ppd, int angle);
+void ppmgr2_set_mirror(struct ppmgr2_device *ppd, int mirror);
+void ppmgr2_set_paint_mode(struct ppmgr2_device *ppd, int paint_mode);
+int v4l_to_ge2d_format(int v4l2_format);
+
+#endif
diff --git a/drivers/amlogic/media/video_processor/ionvideo/map.h b/drivers/amlogic/media/video_processor/ionvideo/map.h
new file mode 100644 (file)
index 0000000..7c6778e
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * drivers/amlogic/media/video_processor/ionvideo/map.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __ASM_MACH_MAP_H
+#define __ASM_MACH_MAP_H
+
+#include <linux/io.h>
+
+struct map_desc {
+       unsigned long virtual;
+       unsigned long pfn;
+       unsigned long length;
+       unsigned int type;
+};
+
+/* types 0-3 are defined in asm/io.h */
+#define MT_UNCACHED            4
+#define MT_CACHECLEAN          5
+#define MT_MINICLEAN           6
+#define MT_LOW_VECTORS         7
+#define MT_HIGH_VECTORS                8
+#define MT_MEMORY              9
+#define MT_ROM                 10
+#define MT_MEMORY_NONCACHED    11
+#define MT_MEMORY_DTCM         12
+#define MT_MEMORY_ITCM         13
+#define MT_MEMORY_SO           14
+#define MT_MEMORY_DMA_READY    15
+
+#ifdef CONFIG_MMU
+extern void iotable_init(struct map_desc *io_desc, int nr);
+extern void vm_reserve_area_early(unsigned long addr, unsigned long size,
+                                 void *caller);
+
+#ifdef CONFIG_DEBUG_LL
+extern void debug_ll_addr(unsigned long *paddr, unsigned long *vaddr);
+extern void debug_ll_io_init(void);
+#else
+static inline void debug_ll_io_init(void) {}
+#endif
+
+struct mem_type;
+extern const struct mem_type *get_mem_type(unsigned int type);
+/*
+ * external interface to remap single page with appropriate type
+ */
+extern int ioremap_page(unsigned long virt, unsigned long phys,
+                       const struct mem_type *mtype);
+#else
+#define iotable_init(map, num) do { } while (0)
+#define vm_reserve_area_early(a, s, c) do { } while (0)
+#endif
+
+#endif
diff --git a/drivers/amlogic/media/video_processor/ionvideo/ppmgr2.c b/drivers/amlogic/media/video_processor/ionvideo/ppmgr2.c
new file mode 100644 (file)
index 0000000..e74d486
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+ * drivers/amlogic/media/video_processor/ionvideo/ppmgr2.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include "ionvideo.h"
+
+static inline void paint_mode_convert(int paint_mode, int *src_position,
+                                       int *dst_paint_position,
+                                       int *dst_plane_position)
+{
+
+       if (paint_mode == 0) { /* stretch full */
+               dst_paint_position[0] = dst_plane_position[0];
+               dst_paint_position[1] = dst_plane_position[1];
+               dst_paint_position[2] = dst_plane_position[2];
+               dst_paint_position[3] = dst_plane_position[3];
+       } else if (paint_mode == 1) { /* keep size */
+               dst_paint_position[0] =
+                       (dst_plane_position[2] - src_position[2]) >> 1;
+               dst_paint_position[1] =
+                       (dst_plane_position[3] - src_position[3]) >> 1;
+               dst_paint_position[2] = src_position[2];
+               dst_paint_position[3] = src_position[3];
+       } else if (paint_mode == 2) {
+               int dw = 0, dh = 0;
+
+               if (src_position[2] * dst_plane_position[3] >=
+                       dst_plane_position[2]
+                       * src_position[3]) { /* crop full */
+                       dh = dst_plane_position[3];
+                       dw = dh * src_position[2] / src_position[3];
+               } else {
+                       dw = dst_plane_position[2];
+                       dh = dw * src_position[3] / src_position[2];
+               }
+               dst_paint_position[0] = (dst_plane_position[2] - dw) >> 1;
+               dst_paint_position[1] = (dst_plane_position[3] - dh) >> 1;
+               dst_paint_position[2] = dw;
+               dst_paint_position[3] = dh;
+       } else if (paint_mode == 3) { /* keep ration black */
+               int dw = 0, dh = 0;
+
+               if (src_position[2] * dst_plane_position[3] >=
+                       dst_plane_position[2] * src_position[3]) {
+                       dw = dst_plane_position[2];
+                       dh = dw * src_position[3] / src_position[2];
+               } else {
+                       dh = dst_plane_position[3];
+                       dw = dh * src_position[2] / src_position[3];
+               }
+               dst_paint_position[0] = (dst_plane_position[2] - dw) >> 1;
+               dst_paint_position[1] = (dst_plane_position[3] - dh) >> 1;
+               dst_paint_position[2] = dw;
+               dst_paint_position[3] = dh;
+       } else if (paint_mode == 4) {
+               ;
+       }
+}
+
+static int get_input_format(struct vframe_s *vf)
+{
+       int format = GE2D_FORMAT_M24_NV21;
+
+       if (vf->type & VIDTYPE_VIU_422) {
+               if ((vf->type & 3) == VIDTYPE_INTERLACE_BOTTOM) {
+                       format = GE2D_FORMAT_S16_YUV422
+                               | (GE2D_FORMAT_S16_YUV422B & (3 << 3));
+               } else if ((vf->type & 3) == VIDTYPE_INTERLACE_TOP) {
+                       format = GE2D_FORMAT_S16_YUV422
+                               | (GE2D_FORMAT_S16_YUV422T & (3 << 3));
+               } else {
+                       format = GE2D_FORMAT_S16_YUV422;
+               }
+       } else if (vf->type & VIDTYPE_VIU_NV21) {
+               if ((vf->type & 3) == VIDTYPE_INTERLACE_BOTTOM) {
+                       format = GE2D_FORMAT_M24_NV21
+                               | (GE2D_FORMAT_M24_NV21B & (3 << 3));
+               } else if ((vf->type & 3) == VIDTYPE_INTERLACE_TOP) {
+                       format = GE2D_FORMAT_M24_NV21
+                               | (GE2D_FORMAT_M24_NV21T & (3 << 3));
+               } else {
+                       format = GE2D_FORMAT_M24_NV21;
+               }
+       } else {
+               if ((vf->type & 3) == VIDTYPE_INTERLACE_BOTTOM) {
+                       format = GE2D_FORMAT_M24_YUV420
+                               | (GE2D_FMT_M24_YUV420B & (3 << 3));
+               } else if ((vf->type & 3) == VIDTYPE_INTERLACE_TOP) {
+                       format = GE2D_FORMAT_M24_YUV420
+                               | (GE2D_FORMAT_M24_YUV420T & (3 << 3));
+               } else {
+                       format = GE2D_FORMAT_M24_YUV420;
+               }
+       }
+       return format;
+}
+
+static inline void ge2d_src_config(struct vframe_s *vf,
+                                       struct config_para_ex_s *ge2d_config)
+{
+       struct vframe_s src_vf = *vf;
+       struct canvas_s src_cs0, src_cs1, src_cs2;
+
+       if (vf->canvas0Addr == (u32)-1) {
+               canvas_config_config(PPMGR2_CANVAS_INDEX_SRC,
+                                       &src_vf.canvas0_config[0]);
+               canvas_config_config(PPMGR2_CANVAS_INDEX_SRC + 1,
+                                       &src_vf.canvas0_config[1]);
+               if (src_vf.plane_num == 2) {
+                       src_vf.canvas0Addr =
+                               (PPMGR2_CANVAS_INDEX_SRC)
+                               | ((PPMGR2_CANVAS_INDEX_SRC + 1) << 8)
+                               | ((PPMGR2_CANVAS_INDEX_SRC + 1) << 16);
+               } else if (src_vf.plane_num == 3) {
+                       canvas_config_config(PPMGR2_CANVAS_INDEX_SRC + 2,
+                                       &src_vf.canvas0_config[2]);
+
+                       src_vf.canvas0Addr =
+                               (PPMGR2_CANVAS_INDEX_SRC)
+                               | ((PPMGR2_CANVAS_INDEX_SRC + 1) << 8)
+                               | ((PPMGR2_CANVAS_INDEX_SRC + 2) << 16);
+               }
+
+               ge2d_config->src_planes[0].addr =
+                               src_vf.canvas0_config[0].phy_addr;
+               ge2d_config->src_planes[0].w =
+                               src_vf.canvas0_config[0].width;
+               ge2d_config->src_planes[0].h =
+                               src_vf.canvas0_config[0].height;
+               ge2d_config->src_planes[1].addr =
+                               src_vf.canvas0_config[1].phy_addr;
+               ge2d_config->src_planes[1].w =
+                               src_vf.canvas0_config[1].width;
+               ge2d_config->src_planes[1].h =
+                               src_vf.canvas0_config[1].height << 1;
+
+               if (src_vf.plane_num == 3) {
+                       ge2d_config->src_planes[2].addr =
+                                       src_vf.canvas0_config[2].phy_addr;
+                       ge2d_config->src_planes[2].w =
+                                       src_vf.canvas0_config[2].width;
+                       ge2d_config->src_planes[2].h =
+                                       src_vf.canvas0_config[2].height << 1;
+               }
+       } else {
+               canvas_read(src_vf.canvas0Addr & 0xff, &src_cs0);
+               canvas_read(src_vf.canvas0Addr >> 8 & 0xff, &src_cs1);
+               canvas_read(src_vf.canvas0Addr >> 16 & 0xff, &src_cs2);
+               ge2d_config->src_planes[0].addr = src_cs0.addr;
+               ge2d_config->src_planes[0].w = src_cs0.width;
+               ge2d_config->src_planes[0].h = src_cs0.height;
+               ge2d_config->src_planes[1].addr = src_cs1.addr;
+               ge2d_config->src_planes[1].w = src_cs1.width;
+               ge2d_config->src_planes[1].h = src_cs1.height;
+               ge2d_config->src_planes[2].addr = src_cs2.addr;
+               ge2d_config->src_planes[2].w = src_cs2.width;
+               ge2d_config->src_planes[2].h = src_cs2.height;
+       }
+       /* data operating. */
+       ge2d_config->alu_const_color = 0; /* 0x000000ff; */
+       ge2d_config->bitmask_en = 0;
+       ge2d_config->src1_gb_alpha = 0; /* 0xff; */
+
+       ge2d_config->src_key.key_enable = 0;
+       ge2d_config->src_key.key_mask = 0;
+       ge2d_config->src_key.key_mode = 0;
+       ge2d_config->src_para.canvas_index = src_vf.canvas0Addr;
+       ge2d_config->src_para.mem_type = CANVAS_TYPE_INVALID;
+       ge2d_config->src_para.format = get_input_format(&src_vf);
+       ge2d_config->src_para.fill_color_en = 0;
+       ge2d_config->src_para.fill_mode = 0;
+       ge2d_config->src_para.x_rev = 0;
+       ge2d_config->src_para.y_rev = 0;
+       ge2d_config->src_para.color = 0xffffffff;
+       ge2d_config->src_para.top = 0;
+       ge2d_config->src_para.left = 0;
+       ge2d_config->src_para.width = src_vf.width;
+       if (vf->type & VIDTYPE_INTERLACE)
+               ge2d_config->src_para.height = src_vf.height >> 1;
+       else
+               ge2d_config->src_para.height = src_vf.height;
+
+       ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID;
+/* ppmgr2_printk(2, "vf_width is %d , vf_height is %d type:%p\n",
+ * vf->width, vf->height, (void *)vf->type);
+ */
+}
+
+static int ge2d_paint_dst(struct ge2d_context_s *context,
+                               struct config_para_ex_s *ge2d_config,
+                               int dst_canvas_id, int dst_pixel_format,
+                               int *src_position, int *dst_paint_position,
+                               int *dst_plane_position)
+{
+       struct canvas_s dst_cd;
+
+       ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID;
+       ge2d_config->dst_para.fill_color_en = 0;
+       ge2d_config->dst_para.fill_mode = 0;
+       ge2d_config->dst_para.color = 0;
+       ge2d_config->dst_para.top = dst_plane_position[0];
+       ge2d_config->dst_para.left = dst_plane_position[1];
+       ge2d_config->dst_para.width = dst_plane_position[2];
+       ge2d_config->dst_para.height = dst_plane_position[3];
+
+       if (dst_pixel_format == GE2D_FORMAT_S8_Y) {
+               canvas_read(dst_canvas_id & 0xff, &dst_cd);
+               ge2d_config->dst_planes[0].addr = dst_cd.addr;
+               ge2d_config->dst_planes[0].w = dst_cd.width;
+               ge2d_config->dst_planes[0].h = dst_cd.height;
+               ge2d_config->dst_para.canvas_index = dst_canvas_id & 0xff;
+               ge2d_config->dst_para.format = dst_pixel_format
+                       | GE2D_LITTLE_ENDIAN;
+
+               if (ge2d_context_config_ex(context, ge2d_config) < 0) {
+                       ppmgr2_printk(1, "Ge2d configing error.\n");
+                       return -1;
+               }
+               stretchblt_noalpha(context, src_position[0], src_position[1],
+                                       src_position[2], src_position[3],
+                                       dst_paint_position[0],
+                                       dst_paint_position[1],
+                                       dst_paint_position[2],
+                                       dst_paint_position[3]);
+               canvas_read(dst_canvas_id >> 8 & 0xff, &dst_cd);
+               ge2d_config->dst_planes[0].addr = dst_cd.addr;
+               ge2d_config->dst_planes[0].w = dst_cd.width;
+               ge2d_config->dst_planes[0].h = dst_cd.height;
+               ge2d_config->dst_para.canvas_index = dst_canvas_id >> 8 & 0xff;
+               ge2d_config->dst_para.format = GE2D_FORMAT_S8_CB
+                       | GE2D_LITTLE_ENDIAN;
+               ge2d_config->dst_para.width = dst_paint_position[2] >> 1;
+               ge2d_config->dst_para.height = dst_paint_position[3] >> 1;
+
+               if (ge2d_context_config_ex(context, ge2d_config) < 0) {
+                       ppmgr2_printk(1, "Ge2d configing error.\n");
+                       return -1;
+               }
+               stretchblt_noalpha(context, src_position[0], src_position[1],
+                                       src_position[2], src_position[3],
+                                       dst_paint_position[0],
+                                       dst_paint_position[1],
+                                       dst_paint_position[2],
+                                       dst_paint_position[3]);
+
+               canvas_read(dst_canvas_id >> 16 & 0xff, &dst_cd);
+               ge2d_config->dst_planes[0].addr = dst_cd.addr;
+               ge2d_config->dst_planes[0].w = dst_cd.width;
+               ge2d_config->dst_planes[0].h = dst_cd.height;
+               ge2d_config->dst_para.canvas_index = dst_canvas_id >> 16 & 0xff;
+               ge2d_config->dst_para.format = GE2D_FORMAT_S8_CR
+                       | GE2D_LITTLE_ENDIAN;
+
+               if (ge2d_context_config_ex(context, ge2d_config) < 0) {
+                       ppmgr2_printk(1, "Ge2d configing error.\n");
+                       return -1;
+               }
+               stretchblt_noalpha(context, src_position[0], src_position[1],
+                                       src_position[2], src_position[3],
+                                       dst_paint_position[0],
+                                       dst_paint_position[1],
+                                       dst_paint_position[2],
+                                       dst_paint_position[3]);
+       } else {
+               canvas_read(dst_canvas_id & 0xff, &dst_cd);
+               ge2d_config->dst_planes[0].addr = dst_cd.addr;
+               ge2d_config->dst_planes[0].w = dst_cd.width;
+               ge2d_config->dst_planes[0].h = dst_cd.height;
+               ge2d_config->dst_para.format = dst_pixel_format
+                       | GE2D_LITTLE_ENDIAN;
+               ge2d_config->dst_para.canvas_index = dst_canvas_id;
+
+               if ((dst_paint_position[2] > dst_cd.width)
+                               || (dst_paint_position[3] > dst_cd.height)) {
+                       ppmgr2_printk(0, "error: id %d,width %d,height %d, ",
+                                       dst_canvas_id,
+                                       dst_cd.width,
+                                       dst_cd.height);
+                       ppmgr2_printk(0, "dst_width %d,dst_height %d\n",
+                                       dst_paint_position[2],
+                                       dst_paint_position[3]);
+                       ppmgr2_printk(1, "error case : dst addr:%p\n",
+                                       (void *)dst_cd.addr);
+                       return -1;
+               }
+               if (ge2d_context_config_ex(context, ge2d_config) < 0) {
+                       ppmgr2_printk(1, "Ge2d configing error.\n");
+                       return -1;
+               }
+               stretchblt_noalpha(context, src_position[0], src_position[1],
+                                       src_position[2], src_position[3],
+                                       dst_paint_position[0],
+                                       dst_paint_position[1],
+                                       dst_paint_position[2],
+                                       dst_paint_position[3]);
+       }
+/* ppmgr2_printk(2, "dst addr:%p w:%d h:%d canvas_id:%p format:%p\n",
+ * (void *)dst_cd.addr, dst_cd.width, dst_cd.height,
+ * (void *)dst_canvas_id,
+ * (void *)ge2d_config->dst_para.format);
+ */
+       ppmgr2_printk(2, "dst plane w:%d h:%d paint w:%d h:%d\n",
+                       dst_plane_position[2], dst_plane_position[3],
+                       dst_paint_position[2], dst_paint_position[3]);
+
+       return 0;
+}
+
+static inline void ge2d_mirror_config(int dst_mirror,
+                                       struct config_para_ex_s *ge2d_config)
+{
+       if (dst_mirror == 1) {
+               ge2d_config->dst_para.x_rev = 1;
+               ge2d_config->dst_para.y_rev = 0;
+       } else if (dst_mirror == 2) {
+               ge2d_config->dst_para.x_rev = 0;
+               ge2d_config->dst_para.y_rev = 1;
+       } else {
+               ge2d_config->dst_para.x_rev = 0;
+               ge2d_config->dst_para.y_rev = 0;
+       }
+}
+
+static inline void ge2d_angle_config(int dst_angle,
+                                       struct config_para_ex_s *ge2d_config)
+{
+       if (dst_angle == 1) {
+               ge2d_config->dst_xy_swap = 1;
+               ge2d_config->dst_para.x_rev ^= 1;
+       } else if (dst_angle == 2) {
+               ge2d_config->dst_para.x_rev ^= 1;
+               ge2d_config->dst_para.y_rev ^= 1;
+       } else if (dst_angle == 3) {
+               ge2d_config->dst_xy_swap = 1;
+               ge2d_config->dst_para.y_rev ^= 1;
+       } else {
+               ge2d_config->dst_xy_swap = 0;
+       }
+}
+
+/*
+ * use ppmgr2 need to init struct ge2d_context_s,
+ * pixel_format, canvas_width, canvas_height,
+ * phy_addr, buffer_size, canvas_number.
+ */
+int ppmgr2_init(struct ppmgr2_device *ppd)
+{
+       int i = 0;
+       /* switch_mod_gate_by_name("ge2d", 1); */
+       ppd->context = create_ge2d_work_queue();
+       if (!ppd->context) {
+               ppmgr2_printk(1, "create ge2d work queue error!\n");
+               return -1;
+       }
+       ppmgr2_printk(2, "ppmgr2_init!\n");
+       ppd->paint_mode = 0;
+       ppd->angle = 0;
+       ppd->mirror = 0;
+       ppd->ge2d_fmt = 0;
+       ppd->dst_width = 0;
+       ppd->dst_height = 0;
+       for (i = 0; i < PPMGR2_MAX_CANVAS; i++) {
+               ppd->phy_addr[i] = NULL;
+               ppd->canvas_id[i] = -1;
+       }
+       return 0;
+}
+
+int ppmgr2_canvas_config(struct ppmgr2_device *ppd, int index)
+{
+       int canvas_width = ppd->dst_buffer_width;
+       int canvas_height = ppd->dst_buffer_height;
+       void *phy_addr;
+
+       if (!ppd->phy_addr) {
+               ppmgr2_printk(1, "NULL physical address!\n");
+               return -1;
+       }
+
+       phy_addr = ppd->phy_addr[index];
+
+       if (index >= PPMGR2_MAX_CANVAS) {
+               ppmgr2_printk(0, "canvas index too large! %d>=%d\n", index,
+                               PPMGR2_MAX_CANVAS);
+               return -1;
+       }
+
+       if (ppd->ge2d_fmt == GE2D_FORMAT_M24_NV21 || ppd->ge2d_fmt
+               == GE2D_FORMAT_M24_NV12) {
+               canvas_config(PPMGR2_CANVAS_INDEX, (ulong)phy_addr,
+                               canvas_width, canvas_height, CANVAS_ADDR_NOWRAP,
+                               CANVAS_BLKMODE_LINEAR);
+               canvas_config(
+                       PPMGR2_CANVAS_INDEX + 1,
+                       (ulong)(phy_addr + (canvas_width * canvas_height)),
+                       canvas_width, canvas_height >> 1, CANVAS_ADDR_NOWRAP,
+                       CANVAS_BLKMODE_LINEAR);
+               ppd->canvas_id[index] = (PPMGR2_CANVAS_INDEX)
+                       | ((PPMGR2_CANVAS_INDEX + 1) << 8);
+       } else if (ppd->ge2d_fmt == GE2D_FORMAT_S8_Y) {
+               canvas_config(PPMGR2_CANVAS_INDEX, (ulong)phy_addr,
+                               canvas_width, canvas_height, CANVAS_ADDR_NOWRAP,
+                               CANVAS_BLKMODE_LINEAR);
+               canvas_config(
+                       PPMGR2_CANVAS_INDEX + 1,
+                       (ulong)(phy_addr + canvas_width * canvas_height),
+                       canvas_width >> 1, canvas_height >> 1,
+                       CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
+               canvas_config(
+                       PPMGR2_CANVAS_INDEX + 2,
+                       (ulong)(phy_addr + (canvas_width * canvas_height * 5
+                               >> 2)),
+                       canvas_width >> 1, canvas_height >> 1,
+                       CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
+               ppd->canvas_id[index] = (PPMGR2_CANVAS_INDEX)
+                       | ((PPMGR2_CANVAS_INDEX + 1) << 8)
+                       | ((PPMGR2_CANVAS_INDEX + 2) << 16);
+       } else {
+               int bpp = 0;
+
+               if (ppd->ge2d_fmt == GE2D_FORMAT_S32_ABGR) {
+                       bpp = 4;
+               } else if (ppd->ge2d_fmt == GE2D_FORMAT_S24_BGR
+                       || ppd->ge2d_fmt == GE2D_FORMAT_S24_RGB) {
+                       bpp = 3;
+               } else if (ppd->ge2d_fmt == GE2D_FORMAT_S16_RGB_565) {
+                       bpp = 2;
+               } else {
+                       ppmgr2_printk(1, "Not support format!\n");
+                       return -1;
+               }
+               canvas_config(PPMGR2_CANVAS_INDEX, (ulong)phy_addr,
+                               canvas_width * bpp, canvas_height,
+                               CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
+               ppd->canvas_id[index] = PPMGR2_CANVAS_INDEX;
+
+       }
+       ppmgr2_printk(2, "canvas[%d] phy_addr:%p width:%d height:%d\n", index,
+                       phy_addr, canvas_width, canvas_height);
+
+       return 0;
+}
+
+void ppmgr2_set_angle(struct ppmgr2_device *ppd, int angle)
+{
+       ppd->angle = angle;
+}
+
+void ppmgr2_set_mirror(struct ppmgr2_device *ppd, int mirror)
+{
+       ppd->mirror = mirror;
+}
+
+void ppmgr2_set_paint_mode(struct ppmgr2_device *ppd, int paint_mode)
+{
+       ppd->paint_mode = paint_mode;
+}
+
+int ppmgr2_process(struct vframe_s *vf, struct ppmgr2_device *ppd, int index)
+{
+       int ret = 0;
+       struct vframe_s *src_vf = vf;
+       int src_position[4] = {0};
+       int dst_paint_position[4] = {0}, dst_plane_position[4] = {0};
+       int dst_pixel_format = ppd->ge2d_fmt;
+       struct ge2d_context_s *context = ppd->context;
+       struct config_para_ex_s *ge2d_config = &(ppd->ge2d_config);
+       int angle = (ppd->angle + src_vf->orientation) % 4;
+       int dst_canvas_id = 0;
+
+       if (src_vf->type & VIDTYPE_INTERLACE) {
+               if ((ppd->bottom_first && src_vf->type & 0x2)
+                       || (ppd->bottom_first == 0
+                       && (src_vf->type & 0x2) == 0)) {
+                       return -EAGAIN;
+               }
+       }
+
+       mutex_lock(ppd->ge2d_canvas_mutex);
+       ppmgr2_canvas_config(ppd, index);
+       dst_canvas_id = ppd->canvas_id[index];
+       src_position[0] = 0;
+       src_position[1] = 0;
+       src_position[2] = src_vf->width;
+       src_position[3] = src_vf->height;
+       if (src_position[2] == 0 || src_position[3] == 0) {
+               ppmgr2_printk(1, "Source frame error!\n");
+               mutex_unlock(ppd->ge2d_canvas_mutex);
+               return -1;
+       }
+       dst_plane_position[0] = 0;
+       dst_plane_position[1] = 0;
+       dst_plane_position[2] = ppd->dst_width;
+       dst_plane_position[3] = ppd->dst_height;
+
+       ge2d_src_config(src_vf, ge2d_config);
+
+       ge2d_mirror_config(ppd->mirror, ge2d_config);
+       ge2d_angle_config(angle, ge2d_config);
+       paint_mode_convert(ppd->paint_mode, src_position, dst_paint_position,
+                               dst_plane_position);
+
+       if (src_vf->type & VIDTYPE_INTERLACE)
+               src_position[3] = src_vf->height >> 1;
+
+       ret = ge2d_paint_dst(context, ge2d_config, dst_canvas_id,
+                               dst_pixel_format, src_position,
+                               dst_paint_position, dst_plane_position);
+
+       mutex_unlock(ppd->ge2d_canvas_mutex);
+
+       return ret;
+}
+
+void ppmgr2_release(struct ppmgr2_device *ppd)
+{
+       if (ppd->context)
+               destroy_ge2d_work_queue(ppd->context);
+
+       /* switch_mod_gate_by_name("ge2d", 0); */
+       ppmgr2_printk(2, "ppmgr2_release!\n");
+}
+
+int v4l_to_ge2d_format(int v4l2_format)
+{
+       int format = GE2D_FORMAT_M24_NV21;
+
+       switch (v4l2_format) {
+       case V4L2_PIX_FMT_RGB32:
+               format = GE2D_FORMAT_S32_ABGR;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               format = GE2D_FORMAT_S16_RGB_565;
+               break;
+       case V4L2_PIX_FMT_BGR24:
+               format = GE2D_FORMAT_S24_RGB;
+               break;
+       case V4L2_PIX_FMT_RGB24:
+               format = GE2D_FORMAT_S24_BGR;
+               break;
+       case V4L2_PIX_FMT_NV12:
+               format = GE2D_FORMAT_M24_NV12;
+               break;
+       case V4L2_PIX_FMT_NV21:
+               format = GE2D_FORMAT_M24_NV21;
+               break;
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_YVU420:
+               format = GE2D_FORMAT_S8_Y;
+               break;
+       default:
+               break;
+       }
+       return format;
+}
diff --git a/drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.c b/drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.c
new file mode 100644 (file)
index 0000000..2039af4
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+
+/*#include <asm/mach/map.h>*/
+#include <ion.h>
+#include "ion_priv.h"
+#include "videobuf2-ion.h"
+
+struct vb2_ion_buf {
+       void *vaddr;
+       struct page **pages;
+       struct vm_area_struct *vma;
+       int write;
+       unsigned long size;
+       unsigned int n_pages;
+       atomic_t refcount;
+       struct vb2_vmarea_handler handler;
+       struct dma_buf *dbuf;
+};
+
+static void vb2_ion_put(void *buf_priv);
+
+#ifdef IONVIDEO_DEBUG
+static void *vb2_ion_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_flags)
+{
+       struct vb2_ion_buf *buf;
+
+       buf = kzalloc(sizeof(*buf), GFP_KERNEL | gfp_flags);
+       if (!buf)
+               return NULL;
+
+       buf->size = size;
+       buf->vaddr = vmalloc_user(buf->size);
+       buf->handler.refcount = &buf->refcount;
+       buf->handler.put = vb2_ion_put;
+       buf->handler.arg = buf;
+
+       if (!buf->vaddr) {
+               pr_debug("ion of size %ld failed\n", buf->size);
+               kfree(buf);
+               return NULL;
+       }
+
+       atomic_inc(&buf->refcount);
+       return buf;
+}
+#endif
+
+static void vb2_ion_put(void *buf_priv)
+{
+       struct vb2_ion_buf *buf = buf_priv;
+
+       if (atomic_dec_and_test(&buf->refcount)) {
+               vfree(buf->vaddr);
+               kfree(buf);
+       }
+}
+
+#ifdef IONVIDEO_DEBUG
+static void *vb2_ion_get_userptr(void *alloc_ctx, unsigned long vaddr,
+                                       unsigned long size, int write)
+{
+       struct vb2_ion_buf *buf;
+       unsigned long first, last;
+       int n_pages, offset;
+       struct vm_area_struct *vma;
+       dma_addr_t physp;
+
+       buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+       if (!buf)
+               return NULL;
+
+       buf->write = write;
+       offset = vaddr & ~PAGE_MASK;
+       buf->size = size;
+
+       vma = find_vma(current->mm, vaddr);
+       if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
+#ifdef IONVIDEO_DEBUG
+               if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
+                       goto fail_pages_array_alloc;
+#endif
+               buf->vma = vma;
+               buf->vaddr = ioremap_nocache(physp, size);
+               if (!buf->vaddr)
+                       goto fail_pages_array_alloc;
+       } else {
+               first = vaddr >> PAGE_SHIFT;
+               last = (vaddr + size - 1) >> PAGE_SHIFT;
+               buf->n_pages = last - first + 1;
+               buf->pages = kcalloc(buf->n_pages, sizeof(struct page *),
+                                       GFP_KERNEL);
+               if (!buf->pages)
+                       goto fail_pages_array_alloc;
+
+               /* current->mm->mmap_sem is taken by videobuf2 core */
+#ifdef IONVIDEO_DEBUG
+               n_pages = get_user_pages(current, current->mm,
+                                               vaddr & PAGE_MASK, buf->n_pages,
+                                               write, 1, /* force */
+                                               buf->pages, NULL);
+#endif
+               if (n_pages != buf->n_pages)
+                       goto fail_get_user_pages;
+
+               buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
+                                       PAGE_KERNEL);
+               if (!buf->vaddr)
+                       goto fail_get_user_pages;
+       }
+
+       buf->vaddr += offset;
+       return buf;
+
+fail_get_user_pages:
+       pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
+                       buf->n_pages);
+       while (--n_pages >= 0)
+               put_page(buf->pages[n_pages]);
+       kfree(buf->pages);
+
+fail_pages_array_alloc: kfree(buf);
+
+       return NULL;
+}
+#endif
+
+static void vb2_ion_put_userptr(void *buf_priv)
+{
+       struct vb2_ion_buf *buf = buf_priv;
+       unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
+       unsigned int i;
+
+       if (buf->pages) {
+               if (vaddr)
+                       vm_unmap_ram((void *)vaddr, buf->n_pages);
+               for (i = 0; i < buf->n_pages; ++i) {
+                       if (buf->write)
+                               set_page_dirty_lock(buf->pages[i]);
+                       put_page(buf->pages[i]);
+               }
+               kfree(buf->pages);
+       } else {
+#ifdef IONVIDEO_DEBUG
+               if (buf->vma)
+                       vb2_put_vma(buf->vma);
+#endif
+               iounmap(buf->vaddr);
+       }
+       kfree(buf);
+}
+
+static void *vb2_ion_vaddr(void *buf_priv)
+{
+       struct vb2_ion_buf *buf = buf_priv;
+
+       if (!buf->vaddr) {
+               pr_err("Address of an unallocated plane requested");
+               pr_err("or cannot map user pointer\n");
+               return NULL;
+       }
+
+       return buf->vaddr;
+}
+
+static unsigned int vb2_ion_num_users(void *buf_priv)
+{
+       struct vb2_ion_buf *buf = buf_priv;
+
+       return atomic_read(&buf->refcount);
+}
+
+static int vb2_ion_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+       struct vb2_ion_buf *buf = buf_priv;
+       int ret;
+
+       pr_info("11vb2_ion_mmap\n");
+       if (!buf) {
+               pr_err("No memory to map\n");
+               return -EINVAL;
+       }
+
+       ret = remap_vmalloc_range(vma, buf->vaddr, 0);
+       if (ret) {
+               pr_err("Remapping ion memory, error: %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * Make sure that vm_areas for 2 buffers won't be merged together
+        */
+       vma->vm_flags |= VM_DONTEXPAND;
+
+       /*
+        * Use common vm_area operations to track buffer refcount.
+        */
+       vma->vm_private_data = &buf->handler;
+       vma->vm_ops = &vb2_common_vm_ops;
+
+       vma->vm_ops->open(vma);
+       pr_info("22vb2_ion_mmap\n");
+       return 0;
+}
+
+/*********************************************/
+/*       callbacks for DMABUF buffers        */
+/*********************************************/
+
+static int vb2_ion_map_dmabuf(void *mem_priv)
+{
+       /* struct vb2_ion_buf *buf = mem_priv; */
+       /*  */
+       /* struct ion_buffer *buffer = buf->dbuf->priv; */
+       /* int mtype = MT_MEMORY_NONCACHED; */
+       /*  */
+       /* if (buffer->flags & ION_FLAG_CACHED) */
+       /* mtype = MT_MEMORY; */
+#if 0
+       buf->vaddr = __arm_ioremap(buffer->priv_phys, buffer->size, mtype);
+
+       return buf->vaddr ? 0 : -EFAULT;
+#else
+       return 0;
+#endif
+}
+
+static void vb2_ion_unmap_dmabuf(void *mem_priv)
+{
+       /* struct vb2_ion_buf *buf = mem_priv; */
+
+#if 0
+       __arm_iounmap(buf->vaddr);
+
+       buf->vaddr = NULL;
+#endif
+}
+
+static void vb2_ion_detach_dmabuf(void *mem_priv)
+{
+       struct vb2_ion_buf *buf = mem_priv;
+
+       if (buf->vaddr)
+               dma_buf_vunmap(buf->dbuf, buf->vaddr);
+
+       kfree(buf);
+}
+
+#ifdef IONVIDEO_DEBUG
+static void *vb2_ion_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
+                                       unsigned long size, int write)
+{
+       struct vb2_ion_buf *buf;
+
+       if (dbuf->size < size)
+               return ERR_PTR(-EFAULT);
+
+       buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+       if (!buf)
+               return ERR_PTR(-ENOMEM);
+
+       buf->dbuf = dbuf;
+       buf->write = write;
+       buf->size = size;
+
+       return buf;
+}
+#endif
+
+static void *vb2_ion_cookie(void *buf_priv)
+{
+       struct vb2_ion_buf *buf = buf_priv;
+
+       struct ion_buffer *buffer = buf->dbuf->priv;
+#if 0
+       return (void *)buffer->priv_phys;
+#endif
+       struct sg_table *table = buffer->priv_virt;
+       struct page *page = sg_page(table->sgl);
+
+       ion_phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
+
+       return (void *)paddr;
+}
+
+const struct vb2_mem_ops vb2_ion_memops = {
+#ifdef IONVIDEO_DEBUG
+       .alloc = vb2_ion_alloc,
+#endif
+       .put = vb2_ion_put,
+#ifdef IONVIDEO_DEBUG
+       .get_userptr = vb2_ion_get_userptr,
+#endif
+       .put_userptr = vb2_ion_put_userptr,
+       .map_dmabuf = vb2_ion_map_dmabuf,
+       .unmap_dmabuf = vb2_ion_unmap_dmabuf,
+#ifdef IONVIDEO_DEBUG
+       .attach_dmabuf = vb2_ion_attach_dmabuf,
+#endif
+       .detach_dmabuf = vb2_ion_detach_dmabuf,
+       .vaddr = vb2_ion_vaddr,
+       .mmap = vb2_ion_mmap,
+       .num_users = vb2_ion_num_users,
+       .cookie = vb2_ion_cookie,
+};
+EXPORT_SYMBOL_GPL(vb2_ion_memops);
+
+MODULE_DESCRIPTION("ion memory handling routines for videobuf2");
+MODULE_AUTHOR("Shuai Cao <shuai.cao@amlogic.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.h b/drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.h
new file mode 100644 (file)
index 0000000..b152557
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _MEDIA_VIDEOBUF2_ION_H
+#define _MEDIA_VIDEOBUF2_ION_H
+
+#include <media/videobuf2-core.h>
+
+extern const struct vb2_mem_ops vb2_ion_memops;
+
+#endif