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/*
hw-version = <2>;
};
+ ionvideo {
+ compatible = "amlogic, ionvideo";
+ dev_name = "ionvideo";
+ status = "okay";
+ };
+
amlvideo {
compatible = "amlogic, amlvideo";
dev_name = "amlvideo";
hw-version = <2>;
};
+ ionvideo {
+ compatible = "amlogic, ionvideo";
+ dev_name = "ionvideo";
+ status = "okay";
+ };
+
amlvideo {
compatible = "amlogic, amlvideo";
dev_name = "amlvideo";
};
/* END OF AUDIO board specific */
+ ionvideo {
+ compatible = "amlogic, ionvideo";
+ dev_name = "ionvideo";
+ status = "okay";
+ };
+
amlvideo {
compatible = "amlogic, amlvideo";
dev_name = "amlvideo";
size = <0x0 0x2000000>;
};
+ ionvideo {
+ compatible = "amlogic, ionvideo";
+ dev_name = "ionvideo";
+ status = "okay";
+ };
+
amlvideo {
compatible = "amlogic, amlvideo";
dev_name = "amlvideo";
hw-version = <2>;
};
+ ionvideo {
+ compatible = "amlogic, ionvideo";
+ dev_name = "ionvideo";
+ status = "okay";
+ };
+
amlvideo {
compatible = "amlogic, amlvideo";
dev_name = "amlvideo";
status = "okay";
};
+ ionvideo {
+ compatible = "amlogic, ionvideo";
+ dev_name = "ionvideo";
+ status = "okay";
+ };
+
amlvideo {
compatible = "amlogic, amlvideo";
dev_name = "amlvideo";
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
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
obj-$(CONFIG_AMLOGIC_V4L_VIDEO2) += video_dev/
obj-$(CONFIG_AMLOGIC_POST_PROCESS_MANAGER) += ppmgr/
+obj-$(CONFIG_AMLOGIC_IONVIDEO) += ionvideo/
--- /dev/null
+#
+# 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
--- /dev/null
+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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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