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 <guosong.zhou@amlogic.com>
This commit is contained in:
Guosong Zhou
2017-04-06 18:25:33 +08:00
committed by Victor Wan
parent e96f8d445b
commit 252cab3b35
19 changed files with 3075 additions and 0 deletions

View File

@@ -13768,4 +13768,16 @@ M: Rongjun Chen <rongjun.chen@amlogic.com>
F: drivers/amlogic/bluetooth/
F: drivers/amlogic/wifi/
AMLOGIC IONVIDEO DRIVER
M: Guosong Zhou <guosong.zhou@amlogic.com>
F: arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts
F: arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts
F: arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts
F: arch/arm64/boot/dts/amlogic/gxl_p400_2g.dts
F: arch/arm64/boot/dts/amlogic/gxm_p401_2g.dts
F: arch/arm64/boot/dts/amlogic/gxm_skt.dts
F: arch/arm64/configs/meson64_defconfig
F: drivers/amlogic/media/video_processor/Kconfig
F: drivers/amlogic/media/video_processor/Makefile
F: drivers/amlogic/media/video_processor/ionvideo/*

View File

@@ -1022,6 +1022,12 @@
hw-version = <2>;
};
ionvideo {
compatible = "amlogic, ionvideo";
dev_name = "ionvideo";
status = "okay";
};
amlvideo {
compatible = "amlogic, amlvideo";
dev_name = "amlvideo";

View File

@@ -1020,6 +1020,12 @@
hw-version = <2>;
};
ionvideo {
compatible = "amlogic, ionvideo";
dev_name = "ionvideo";
status = "okay";
};
amlvideo {
compatible = "amlogic, amlvideo";
dev_name = "amlvideo";

View File

@@ -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";

View File

@@ -79,6 +79,12 @@
size = <0x0 0x2000000>;
};
ionvideo {
compatible = "amlogic, ionvideo";
dev_name = "ionvideo";
status = "okay";
};
amlvideo {
compatible = "amlogic, amlvideo";
dev_name = "amlvideo";

View File

@@ -1043,6 +1043,12 @@
hw-version = <2>;
};
ionvideo {
compatible = "amlogic, ionvideo";
dev_name = "ionvideo";
status = "okay";
};
amlvideo {
compatible = "amlogic, amlvideo";
dev_name = "amlvideo";

View File

@@ -810,6 +810,12 @@
status = "okay";
};
ionvideo {
compatible = "amlogic, ionvideo";
dev_name = "ionvideo";
status = "okay";
};
amlvideo {
compatible = "amlogic, amlvideo";
dev_name = "amlvideo";

View File

@@ -226,6 +226,8 @@ CONFIG_AMLOGIC_V4L_VIDEO=y
CONFIG_AMLOGIC_V4L_VIDEO2=y
CONFIG_AMLOGIC_POST_PROCESS_MANAGER=y
CONFIG_AMLOGIC_POST_PROCESS_MANAGER_PPSCALER=y
CONFIG_AMLOGIC_VIDEOBUF2_ION=y
CONFIG_AMLOGIC_IONVIDEO=y
CONFIG_AMLOGIC_MMC=y
CONFIG_AMLOGIC_NAND=y
CONFIG_AMLOGIC_VRTC=y

View File

@@ -15,6 +15,7 @@ if AMLOGIC_VIDEO_PROCESSOR
source "drivers/amlogic/media/video_processor/video_dev/Kconfig"
source "drivers/amlogic/media/video_processor/ppmgr/Kconfig"
source "drivers/amlogic/media/video_processor/ionvideo/Kconfig"
endif

View File

@@ -1,2 +1,3 @@
obj-$(CONFIG_AMLOGIC_V4L_VIDEO2) += video_dev/
obj-$(CONFIG_AMLOGIC_POST_PROCESS_MANAGER) += ppmgr/
obj-$(CONFIG_AMLOGIC_IONVIDEO) += ionvideo/

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,279 @@
/*
* drivers/amlogic/media/video_processor/ionvideo/ion_priv.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef _ION_PRIV_H
#define _ION_PRIV_H
#include <ion.h>
#include <linux/kref.h>
#include <linux/mm_types.h>
#include <linux/mutex.h>
#include <linux/rbtree.h>
#include <linux/sched.h>
#include <linux/shrinker.h>
#include <linux/types.h>
struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
/**
* struct ion_buffer - metadata for a particular buffer
* @ref: refernce count
* @node: node in the ion_device buffers tree
* @dev: back pointer to the ion_device
* @heap: back pointer to the heap the buffer came from
* @flags: buffer specific flags
* @size: size of the buffer
* @priv_virt: private data to the buffer representable as
* a void *
* @priv_phys: private data to the buffer representable as
* an ion_phys_addr_t (and someday a phys_addr_t)
* @lock: protects the buffers cnt fields
* @kmap_cnt: number of times the buffer is mapped to the kernel
* @vaddr: the kenrel mapping if kmap_cnt is not zero
* @dmap_cnt: number of times the buffer is mapped for dma
* @sg_table: the sg table for the buffer if dmap_cnt is not zero
* @dirty: bitmask representing which pages of this buffer have
* been dirtied by the cpu and need cache maintenance
* before dma
* @vmas: list of vma's mapping this buffer
* @handle_count: count of handles referencing this buffer
* @task_comm: taskcomm of last client to reference this buffer in a
* handle, used for debugging
* @pid: pid of last client to reference this buffer in a
* handle, used for debugging
*/
struct ion_buffer {
struct kref ref;
struct rb_node node;
struct ion_device *dev;
struct ion_heap *heap;
unsigned long flags;
size_t size;
union {
void *priv_virt;
ion_phys_addr_t priv_phys;
};
struct mutex lock;
int kmap_cnt;
void *vaddr;
int dmap_cnt;
struct sg_table *sg_table;
unsigned long *dirty;
struct list_head vmas;
/* used to track orphaned buffers */
int handle_count;
char task_comm[TASK_COMM_LEN];
pid_t pid;
};
/**
* struct ion_heap_ops - ops to operate on a given heap
* @allocate: allocate memory
* @free: free memory
* @phys get physical address of a buffer (only define on
* physically contiguous heaps)
* @map_dma map the memory for dma to a scatterlist
* @unmap_dma unmap the memory for dma
* @map_kernel map memory to the kernel
* @unmap_kernel unmap memory to the kernel
* @map_user map memory to userspace
*/
struct ion_heap_ops {
int (*allocate)(struct ion_heap *heap,
struct ion_buffer *buffer, unsigned long len,
unsigned long align, unsigned long flags);
void (*free)(struct ion_buffer *buffer);
int (*phys)(struct ion_heap *heap, struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len);
struct sg_table * (*map_dma)(struct ion_heap *heap,
struct ion_buffer *buffer);
void (*unmap_dma)(struct ion_heap *heap, struct ion_buffer *buffer);
void * (*map_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
void (*unmap_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
int (*map_user)(struct ion_heap *mapper, struct ion_buffer *buffer,
struct vm_area_struct *vma);
};
/**
* struct ion_heap - represents a heap in the system
* @node: rb node to put the heap on the device's tree of heaps
* @dev: back pointer to the ion_device
* @type: type of heap
* @ops: ops struct as above
* @id: id of heap, also indicates priority of this heap when
* allocating. These are specified by platform data and
* MUST be unique
* @name: used for debugging
* @debug_show: called when heap debug file is read to add any
* heap specific debug info to output
*
* Represents a pool of memory from which buffers can be made. In some
* systems the only heap is regular system memory allocated via vmalloc.
* On others, some blocks might require large physically contiguous buffers
* that are allocated from a specially reserved heap.
*/
struct ion_heap {
struct plist_node node;
struct ion_device *dev;
enum ion_heap_type type;
struct ion_heap_ops *ops;
unsigned int id;
const char *name;
int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *);
};
/**
* ion_buffer_cached - this ion buffer is cached
* @buffer: buffer
*
* indicates whether this ion buffer is cached
*/
bool ion_buffer_cached(struct ion_buffer *buffer);
/**
* ion_buffer_fault_user_mappings - fault in user mappings of this buffer
* @buffer: buffer
*
* indicates whether userspace mappings of this buffer will be faulted
* in, this can affect how buffers are allocated from the heap.
*/
bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer);
/**
* ion_device_create - allocates and returns an ion device
* @custom_ioctl: arch specific ioctl function if applicable
*
* returns a valid device or -PTR_ERR
*/
struct ion_device *ion_device_create(long (*custom_ioctl)
(struct ion_client *client,
unsigned int cmd,
unsigned long arg));
/**
* ion_device_destroy - free and device and it's resource
* @dev: the device
*/
void ion_device_destroy(struct ion_device *dev);
/**
* ion_device_add_heap - adds a heap to the ion device
* @dev: the device
* @heap: the heap to add
*/
void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap);
/**
* some helpers for common operations on buffers using the sg_table
* and vaddr fields
*/
void *ion_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer);
void ion_heap_unmap_kernel(struct ion_heap *heap, struct ion_buffer *buffer);
int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
struct vm_area_struct *area);
int ion_heap_buffer_zero(struct ion_buffer *buffer);
/**
* functions for creating and destroying the built in ion heaps.
* architectures can add their own custom architecture specific
* heaps as appropriate.
*/
struct ion_heap *ion_heap_create(struct ion_platform_heap *platform_heap);
void ion_heap_destroy(struct ion_heap *heap);
struct ion_heap *
ion_system_heap_create(struct ion_platform_heap *platform_heap);
void ion_system_heap_destroy(struct ion_heap *heap);
struct ion_heap *
ion_system_contig_heap_create(struct ion_platform_heap *platform_heap);
void ion_system_contig_heap_destroy(struct ion_heap *heap);
struct ion_heap *
ion_carveout_heap_create(struct ion_platform_heap *platform_heap);
void ion_carveout_heap_destroy(struct ion_heap *heap);
struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *platform_heap);
void ion_chunk_heap_destroy(struct ion_heap *heap);
/**
* kernel api to allocate/free from carveout -- used when carveout is
* used to back an architecture specific custom heap
*/
ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, unsigned long size,
unsigned long align);
void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
unsigned long size);
/**
* The carveout heap returns physical addresses, since 0 may be a valid
* physical address, this is used to indicate allocation failed
*/
#define ION_CARVEOUT_ALLOCATE_FAIL -1
/**
* functions for creating and destroying a heap pool -- allows you
* to keep a pool of pre allocated memory to use from your heap. Keeping
* a pool of memory that is ready for dma, ie any cached mapping have been
* invalidated from the cache, provides a significant performance benefit on
* many systems
*/
/**
* struct ion_page_pool - pagepool struct
* @high_count: number of highmem items in the pool
* @low_count: number of lowmem items in the pool
* @high_items: list of highmem items
* @low_items: list of lowmem items
* @shrinker: a shrinker for the items
* @mutex: lock protecting this struct and especially the count
* item list
* @alloc: function to be used to allocate pageory when the pool
* is empty
* @free: function to be used to free pageory back to the system
* when the shrinker fires
* @gfp_mask: gfp_mask to use from alloc
* @order: order of pages in the pool
* @list: plist node for list of pools
*
* Allows you to keep a pool of pre allocated pages to use from your heap.
* Keeping a pool of pages that is ready for dma, ie any cached mapping have
* been invalidated from the cache, provides a significant performance benefit
* on many systems
*/
struct ion_page_pool {
int high_count;
int low_count;
struct list_head high_items;
struct list_head low_items;
struct mutex mutex;
void * (*alloc)(struct ion_page_pool *pool);
void (*free)(struct ion_page_pool *pool, struct page *page);
gfp_t gfp_mask;
unsigned int order;
struct plist_node list;
};
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order);
void ion_page_pool_destroy(struct ion_page_pool *pool);
void *ion_page_pool_alloc(struct ion_page_pool *pool);
void ion_page_pool_free(struct ion_page_pool *pool, struct page *page);
#endif /* _ION_PRIV_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,199 @@
/*
* drivers/amlogic/media/video_processor/ionvideo/ionvideo.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef _IONVIDEO_H
#define _IONVIDEO_H
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/delay.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-common.h>
#include <media/videobuf2-core.h>
#include <linux/mm.h>
/* #include <mach/mod_gate.h> */
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/amlogic/media/vfm/vframe_receiver.h>
#include <linux/amlogic/media/ge2d/ge2d.h>
#include <linux/amlogic/media/canvas/canvas.h>
#include <linux/amlogic/media/frame_sync/timestamp.h>
#include <linux/amlogic/media/frame_sync/tsync.h>
#include "videobuf2-ion.h"
/* Wake up at about 30 fps */
#define WAKE_NUMERATOR 30
#define WAKE_DENOMINATOR 1001
#define MAX_WIDTH 4096
#define MAX_HEIGHT 4096
#define IONVID_INFO(fmt, args...) pr_info("ionvid: info: "fmt"", ## args)
#define IONVID_DBG(fmt, args...) pr_debug("ionvid: dbg: "fmt"", ## args)
#define IONVID_ERR(fmt, args...) pr_err("ionvid: err: "fmt"", ## args)
#define DUR2PTS(x) ((x) - ((x) >> 4))
#define dprintk(dev, level, fmt, arg...) \
v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
#define ppmgr2_printk(level, fmt, arg...) \
do { \
if (get_ionvideo_debug() >= level) \
pr_debug("ppmgr2-dev: " fmt, ## arg); \
} while (0)
#define PPMGR2_CANVAS_INDEX_SRC (PPMGR2_CANVAS_INDEX + 3)
/* ------------------------------------------------------------------
* Basic structures
* ------------------------------------------------------------------
*/
struct ionvideo_fmt {
char *name;
u32 fourcc; /* v4l2 format id */
u8 depth;
bool is_yuv;
};
/* buffer for one video frame */
struct ionvideo_buffer {
/* common v4l buffer stuff -- must be first */
struct vb2_buffer vb;
struct list_head list;
const struct ionvideo_fmt *fmt;
u64 pts;
u32 duration;
};
struct ionvideo_dmaqueue {
struct list_head active;
/* thread for generating video stream*/
struct task_struct *kthread;
wait_queue_head_t wq;
/* Counters to control fps rate */
int vb_ready;
struct ionvideo_dev *pdev;
};
struct ppmgr2_device {
int dst_width;
int dst_height;
int dst_buffer_width;
int dst_buffer_height;
int ge2d_fmt;
int canvas_id[PPMGR2_MAX_CANVAS];
void *phy_addr[PPMGR2_MAX_CANVAS];
int phy_size;
struct ge2d_context_s *context;
struct config_para_ex_s ge2d_config;
int angle;
int mirror;
int paint_mode;
int interlaced_num;
int bottom_first;
struct mutex *ge2d_canvas_mutex;
};
#define ION_VF_RECEIVER_NAME_SIZE 32
struct ionvideo_dev {
struct list_head ionvideo_devlist;
struct v4l2_device v4l2_dev;
struct video_device vdev;
int fd_num;
int ionvideo_v4l_num;
spinlock_t slock;
struct mutex mutex;
struct ionvideo_dmaqueue vidq;
/* Several counters */
unsigned int ms;
unsigned long jiffies;
/* Input Number */
int input;
/* video capture */
const struct ionvideo_fmt *fmt;
unsigned int width, height;
unsigned int c_width, c_height;
struct vb2_queue vb_vidq;
unsigned int field_count;
unsigned int pixelsize;
struct ppmgr2_device ppmgr2_dev;
struct vframe_receiver_s video_vf_receiver;
u64 pts;
u8 receiver_register;
u8 is_video_started;
u32 skip;
int once_record;
u8 is_omx_video_started;
int is_actived;
u64 last_pts_us64;
unsigned int freerun_mode;
unsigned int skip_frames;
wait_queue_head_t wq;
char vf_receiver_name[ION_VF_RECEIVER_NAME_SIZE];
int inst;
bool mapped;
bool thread_stopped;
int vf_wait_cnt;
};
unsigned int get_ionvideo_debug(void);
int ppmgr2_init(struct ppmgr2_device *ppd);
int ppmgr2_canvas_config(struct ppmgr2_device *ppd, int index);
int ppmgr2_process(struct vframe_s *vf, struct ppmgr2_device *ppd, int index);
int ppmgr2_top_process(struct vframe_s *vf, struct ppmgr2_device *ppd,
int index);
int ppmgr2_bottom_process(struct vframe_s *vf, struct ppmgr2_device *ppd,
int index);
void ppmgr2_release(struct ppmgr2_device *ppd);
void ppmgr2_set_angle(struct ppmgr2_device *ppd, int angle);
void ppmgr2_set_mirror(struct ppmgr2_device *ppd, int mirror);
void ppmgr2_set_paint_mode(struct ppmgr2_device *ppd, int paint_mode);
int v4l_to_ge2d_format(int v4l2_format);
#endif

View File

@@ -0,0 +1,68 @@
/*
* drivers/amlogic/media/video_processor/ionvideo/map.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __ASM_MACH_MAP_H
#define __ASM_MACH_MAP_H
#include <linux/io.h>
struct map_desc {
unsigned long virtual;
unsigned long pfn;
unsigned long length;
unsigned int type;
};
/* types 0-3 are defined in asm/io.h */
#define MT_UNCACHED 4
#define MT_CACHECLEAN 5
#define MT_MINICLEAN 6
#define MT_LOW_VECTORS 7
#define MT_HIGH_VECTORS 8
#define MT_MEMORY 9
#define MT_ROM 10
#define MT_MEMORY_NONCACHED 11
#define MT_MEMORY_DTCM 12
#define MT_MEMORY_ITCM 13
#define MT_MEMORY_SO 14
#define MT_MEMORY_DMA_READY 15
#ifdef CONFIG_MMU
extern void iotable_init(struct map_desc *io_desc, int nr);
extern void vm_reserve_area_early(unsigned long addr, unsigned long size,
void *caller);
#ifdef CONFIG_DEBUG_LL
extern void debug_ll_addr(unsigned long *paddr, unsigned long *vaddr);
extern void debug_ll_io_init(void);
#else
static inline void debug_ll_io_init(void) {}
#endif
struct mem_type;
extern const struct mem_type *get_mem_type(unsigned int type);
/*
* external interface to remap single page with appropriate type
*/
extern int ioremap_page(unsigned long virt, unsigned long phys,
const struct mem_type *mtype);
#else
#define iotable_init(map, num) do { } while (0)
#define vm_reserve_area_early(a, s, c) do { } while (0)
#endif
#endif

View File

@@ -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;
}

View File

@@ -0,0 +1,334 @@
/*
* drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-memops.h>
/*#include <asm/mach/map.h>*/
#include <ion.h>
#include "ion_priv.h"
#include "videobuf2-ion.h"
struct vb2_ion_buf {
void *vaddr;
struct page **pages;
struct vm_area_struct *vma;
int write;
unsigned long size;
unsigned int n_pages;
atomic_t refcount;
struct vb2_vmarea_handler handler;
struct dma_buf *dbuf;
};
static void vb2_ion_put(void *buf_priv);
#ifdef IONVIDEO_DEBUG
static void *vb2_ion_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_flags)
{
struct vb2_ion_buf *buf;
buf = kzalloc(sizeof(*buf), GFP_KERNEL | gfp_flags);
if (!buf)
return NULL;
buf->size = size;
buf->vaddr = vmalloc_user(buf->size);
buf->handler.refcount = &buf->refcount;
buf->handler.put = vb2_ion_put;
buf->handler.arg = buf;
if (!buf->vaddr) {
pr_debug("ion of size %ld failed\n", buf->size);
kfree(buf);
return NULL;
}
atomic_inc(&buf->refcount);
return buf;
}
#endif
static void vb2_ion_put(void *buf_priv)
{
struct vb2_ion_buf *buf = buf_priv;
if (atomic_dec_and_test(&buf->refcount)) {
vfree(buf->vaddr);
kfree(buf);
}
}
#ifdef IONVIDEO_DEBUG
static void *vb2_ion_get_userptr(void *alloc_ctx, unsigned long vaddr,
unsigned long size, int write)
{
struct vb2_ion_buf *buf;
unsigned long first, last;
int n_pages, offset;
struct vm_area_struct *vma;
dma_addr_t physp;
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
return NULL;
buf->write = write;
offset = vaddr & ~PAGE_MASK;
buf->size = size;
vma = find_vma(current->mm, vaddr);
if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
#ifdef IONVIDEO_DEBUG
if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
goto fail_pages_array_alloc;
#endif
buf->vma = vma;
buf->vaddr = ioremap_nocache(physp, size);
if (!buf->vaddr)
goto fail_pages_array_alloc;
} else {
first = vaddr >> PAGE_SHIFT;
last = (vaddr + size - 1) >> PAGE_SHIFT;
buf->n_pages = last - first + 1;
buf->pages = kcalloc(buf->n_pages, sizeof(struct page *),
GFP_KERNEL);
if (!buf->pages)
goto fail_pages_array_alloc;
/* current->mm->mmap_sem is taken by videobuf2 core */
#ifdef IONVIDEO_DEBUG
n_pages = get_user_pages(current, current->mm,
vaddr & PAGE_MASK, buf->n_pages,
write, 1, /* force */
buf->pages, NULL);
#endif
if (n_pages != buf->n_pages)
goto fail_get_user_pages;
buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
PAGE_KERNEL);
if (!buf->vaddr)
goto fail_get_user_pages;
}
buf->vaddr += offset;
return buf;
fail_get_user_pages:
pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
buf->n_pages);
while (--n_pages >= 0)
put_page(buf->pages[n_pages]);
kfree(buf->pages);
fail_pages_array_alloc: kfree(buf);
return NULL;
}
#endif
static void vb2_ion_put_userptr(void *buf_priv)
{
struct vb2_ion_buf *buf = buf_priv;
unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
unsigned int i;
if (buf->pages) {
if (vaddr)
vm_unmap_ram((void *)vaddr, buf->n_pages);
for (i = 0; i < buf->n_pages; ++i) {
if (buf->write)
set_page_dirty_lock(buf->pages[i]);
put_page(buf->pages[i]);
}
kfree(buf->pages);
} else {
#ifdef IONVIDEO_DEBUG
if (buf->vma)
vb2_put_vma(buf->vma);
#endif
iounmap(buf->vaddr);
}
kfree(buf);
}
static void *vb2_ion_vaddr(void *buf_priv)
{
struct vb2_ion_buf *buf = buf_priv;
if (!buf->vaddr) {
pr_err("Address of an unallocated plane requested");
pr_err("or cannot map user pointer\n");
return NULL;
}
return buf->vaddr;
}
static unsigned int vb2_ion_num_users(void *buf_priv)
{
struct vb2_ion_buf *buf = buf_priv;
return atomic_read(&buf->refcount);
}
static int vb2_ion_mmap(void *buf_priv, struct vm_area_struct *vma)
{
struct vb2_ion_buf *buf = buf_priv;
int ret;
pr_info("11vb2_ion_mmap\n");
if (!buf) {
pr_err("No memory to map\n");
return -EINVAL;
}
ret = remap_vmalloc_range(vma, buf->vaddr, 0);
if (ret) {
pr_err("Remapping ion memory, error: %d\n", ret);
return ret;
}
/*
* Make sure that vm_areas for 2 buffers won't be merged together
*/
vma->vm_flags |= VM_DONTEXPAND;
/*
* Use common vm_area operations to track buffer refcount.
*/
vma->vm_private_data = &buf->handler;
vma->vm_ops = &vb2_common_vm_ops;
vma->vm_ops->open(vma);
pr_info("22vb2_ion_mmap\n");
return 0;
}
/*********************************************/
/* callbacks for DMABUF buffers */
/*********************************************/
static int vb2_ion_map_dmabuf(void *mem_priv)
{
/* struct vb2_ion_buf *buf = mem_priv; */
/* */
/* struct ion_buffer *buffer = buf->dbuf->priv; */
/* int mtype = MT_MEMORY_NONCACHED; */
/* */
/* if (buffer->flags & ION_FLAG_CACHED) */
/* mtype = MT_MEMORY; */
#if 0
buf->vaddr = __arm_ioremap(buffer->priv_phys, buffer->size, mtype);
return buf->vaddr ? 0 : -EFAULT;
#else
return 0;
#endif
}
static void vb2_ion_unmap_dmabuf(void *mem_priv)
{
/* struct vb2_ion_buf *buf = mem_priv; */
#if 0
__arm_iounmap(buf->vaddr);
buf->vaddr = NULL;
#endif
}
static void vb2_ion_detach_dmabuf(void *mem_priv)
{
struct vb2_ion_buf *buf = mem_priv;
if (buf->vaddr)
dma_buf_vunmap(buf->dbuf, buf->vaddr);
kfree(buf);
}
#ifdef IONVIDEO_DEBUG
static void *vb2_ion_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
unsigned long size, int write)
{
struct vb2_ion_buf *buf;
if (dbuf->size < size)
return ERR_PTR(-EFAULT);
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
buf->dbuf = dbuf;
buf->write = write;
buf->size = size;
return buf;
}
#endif
static void *vb2_ion_cookie(void *buf_priv)
{
struct vb2_ion_buf *buf = buf_priv;
struct ion_buffer *buffer = buf->dbuf->priv;
#if 0
return (void *)buffer->priv_phys;
#endif
struct sg_table *table = buffer->priv_virt;
struct page *page = sg_page(table->sgl);
ion_phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
return (void *)paddr;
}
const struct vb2_mem_ops vb2_ion_memops = {
#ifdef IONVIDEO_DEBUG
.alloc = vb2_ion_alloc,
#endif
.put = vb2_ion_put,
#ifdef IONVIDEO_DEBUG
.get_userptr = vb2_ion_get_userptr,
#endif
.put_userptr = vb2_ion_put_userptr,
.map_dmabuf = vb2_ion_map_dmabuf,
.unmap_dmabuf = vb2_ion_unmap_dmabuf,
#ifdef IONVIDEO_DEBUG
.attach_dmabuf = vb2_ion_attach_dmabuf,
#endif
.detach_dmabuf = vb2_ion_detach_dmabuf,
.vaddr = vb2_ion_vaddr,
.mmap = vb2_ion_mmap,
.num_users = vb2_ion_num_users,
.cookie = vb2_ion_cookie,
};
EXPORT_SYMBOL_GPL(vb2_ion_memops);
MODULE_DESCRIPTION("ion memory handling routines for videobuf2");
MODULE_AUTHOR("Shuai Cao <shuai.cao@amlogic.com>");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,25 @@
/*
* drivers/amlogic/media/video_processor/ionvideo/videobuf2-ion.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef _MEDIA_VIDEOBUF2_ION_H
#define _MEDIA_VIDEOBUF2_ION_H
#include <media/videobuf2-core.h>
extern const struct vb2_mem_ops vb2_ion_memops;
#endif