From 252cab3b35cc8e265e3d893c8475928cb42828f7 Mon Sep 17 00:00:00 2001 From: Guosong Zhou Date: Thu, 6 Apr 2017 18:25:33 +0800 Subject: [PATCH] ionvideo: initial add the driver 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 --- MAINTAINERS | 12 + arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts | 6 + arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts | 6 + arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts | 6 + arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts | 6 + arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts | 6 + arch/arm64/boot/dts/amlogic/gxm_skt.dts | 6 + arch/arm64/configs/meson64_defconfig | 2 + drivers/amlogic/media/video_processor/Kconfig | 1 + .../amlogic/media/video_processor/Makefile | 1 + .../media/video_processor/ionvideo/Kconfig | 31 + .../media/video_processor/ionvideo/Makefile | 5 + .../media/video_processor/ionvideo/ion_priv.h | 279 +++ .../media/video_processor/ionvideo/ionvideo.c | 1514 +++++++++++++++++ .../media/video_processor/ionvideo/ionvideo.h | 199 +++ .../media/video_processor/ionvideo/map.h | 68 + .../media/video_processor/ionvideo/ppmgr2.c | 568 +++++++ .../video_processor/ionvideo/videobuf2-ion.c | 334 ++++ .../video_processor/ionvideo/videobuf2-ion.h | 25 + 19 files changed, 3075 insertions(+) create mode 100644 drivers/amlogic/media/video_processor/ionvideo/Kconfig create mode 100644 drivers/amlogic/media/video_processor/ionvideo/Makefile create mode 100644 drivers/amlogic/media/video_processor/ionvideo/ion_priv.h create mode 100644 drivers/amlogic/media/video_processor/ionvideo/ionvideo.c create mode 100644 drivers/amlogic/media/video_processor/ionvideo/ionvideo.h create mode 100644 drivers/amlogic/media/video_processor/ionvideo/map.h create mode 100644 drivers/amlogic/media/video_processor/ionvideo/ppmgr2.c create mode 100644 drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.c create mode 100644 drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.h diff --git a/MAINTAINERS b/MAINTAINERS index a85256ae216b..c58a0890ecf1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13768,4 +13768,16 @@ M: Rongjun Chen F: drivers/amlogic/bluetooth/ F: drivers/amlogic/wifi/ +AMLOGIC IONVIDEO DRIVER +M: Guosong Zhou +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/* diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts index 634c1016be0f..27251b82bc69 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts @@ -1022,6 +1022,12 @@ hw-version = <2>; }; + ionvideo { + compatible = "amlogic, ionvideo"; + dev_name = "ionvideo"; + status = "okay"; + }; + amlvideo { compatible = "amlogic, amlvideo"; dev_name = "amlvideo"; diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts index f44f1951b680..47d42b3e78fa 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts @@ -1020,6 +1020,12 @@ hw-version = <2>; }; + ionvideo { + compatible = "amlogic, ionvideo"; + dev_name = "ionvideo"; + status = "okay"; + }; + amlvideo { compatible = "amlogic, amlvideo"; dev_name = "amlvideo"; diff --git a/arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts b/arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts index a0cc6f1e4bb1..02cd114c1e18 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts @@ -677,6 +677,12 @@ }; /* END OF AUDIO board specific */ + ionvideo { + compatible = "amlogic, ionvideo"; + dev_name = "ionvideo"; + status = "okay"; + }; + amlvideo { compatible = "amlogic, amlvideo"; dev_name = "amlvideo"; diff --git a/arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts b/arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts index 20f08e8a7f6f..927c5ac6096a 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p401_2g.dts @@ -79,6 +79,12 @@ size = <0x0 0x2000000>; }; + ionvideo { + compatible = "amlogic, ionvideo"; + dev_name = "ionvideo"; + status = "okay"; + }; + amlvideo { compatible = "amlogic, amlvideo"; dev_name = "amlvideo"; diff --git a/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts b/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts index 03af58e0809c..9cba95572d31 100644 --- a/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts @@ -1043,6 +1043,12 @@ hw-version = <2>; }; + ionvideo { + compatible = "amlogic, ionvideo"; + dev_name = "ionvideo"; + status = "okay"; + }; + amlvideo { compatible = "amlogic, amlvideo"; dev_name = "amlvideo"; diff --git a/arch/arm64/boot/dts/amlogic/gxm_skt.dts b/arch/arm64/boot/dts/amlogic/gxm_skt.dts index 937da66b0af7..9a8625f2cb61 100644 --- a/arch/arm64/boot/dts/amlogic/gxm_skt.dts +++ b/arch/arm64/boot/dts/amlogic/gxm_skt.dts @@ -810,6 +810,12 @@ status = "okay"; }; + ionvideo { + compatible = "amlogic, ionvideo"; + dev_name = "ionvideo"; + status = "okay"; + }; + amlvideo { compatible = "amlogic, amlvideo"; dev_name = "amlvideo"; diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index ea5727501b2d..d0d7c8a454a2 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -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 diff --git a/drivers/amlogic/media/video_processor/Kconfig b/drivers/amlogic/media/video_processor/Kconfig index b7e9d6cd3598..d91cdc163b37 100644 --- a/drivers/amlogic/media/video_processor/Kconfig +++ b/drivers/amlogic/media/video_processor/Kconfig @@ -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 diff --git a/drivers/amlogic/media/video_processor/Makefile b/drivers/amlogic/media/video_processor/Makefile index 0e21e9403f30..230f8abbbaaf 100644 --- a/drivers/amlogic/media/video_processor/Makefile +++ b/drivers/amlogic/media/video_processor/Makefile @@ -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 index 000000000000..e9898c5eb13c --- /dev/null +++ b/drivers/amlogic/media/video_processor/ionvideo/Kconfig @@ -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 index 000000000000..53f9cf7f7dd9 --- /dev/null +++ b/drivers/amlogic/media/video_processor/ionvideo/Makefile @@ -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 index 000000000000..c301dbb54f03 --- /dev/null +++ b/drivers/amlogic/media/video_processor/ionvideo/ion_priv.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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 index 000000000000..cca560026391 --- /dev/null +++ b/drivers/amlogic/media/video_processor/ionvideo/ionvideo.c @@ -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 +#include +#include +#include +#include "ionvideo.h" +#include +#include +#include + +#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"); +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 index 000000000000..a5a4a6a9ed09 --- /dev/null +++ b/drivers/amlogic/media/video_processor/ionvideo/ionvideo.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +/* #include */ + +#include +#include +#include +#include +#include + +#include +#include +#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 index 000000000000..7c6778ec67d9 --- /dev/null +++ b/drivers/amlogic/media/video_processor/ionvideo/map.h @@ -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 + +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 index 000000000000..e74d4861de05 --- /dev/null +++ b/drivers/amlogic/media/video_processor/ionvideo/ppmgr2.c @@ -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 index 000000000000..2039af48cfc4 --- /dev/null +++ b/drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.c @@ -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 +#include +#include +#include +#include +#include + +#include +#include + +/*#include */ +#include +#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 "); +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 index 000000000000..b1525573c4ec --- /dev/null +++ b/drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.h @@ -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 + +extern const struct vb2_mem_ops vb2_ion_memops; + +#endif