From: Tae-Sik Lee Date: Wed, 25 Jul 2018 08:34:59 +0000 (+0900) Subject: [9610] vipx : adds driver files X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=a9687ded4651f0432d9f94965c59e53f883a9cf3;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git [9610] vipx : adds driver files Change-Id: Iafe587895d40ac68d131abce07fd5d056212e0bd Signed-off-by: Tae-Sik Lee --- diff --git a/arch/arm64/boot/dts/exynos/exynos9610-rmem.dtsi b/arch/arm64/boot/dts/exynos/exynos9610-rmem.dtsi index 68d0e3e7add1..46a4e2f01b4c 100644 --- a/arch/arm64/boot/dts/exynos/exynos9610-rmem.dtsi +++ b/arch/arm64/boot/dts/exynos/exynos9610-rmem.dtsi @@ -147,7 +147,7 @@ vipx_fw_code_rmem: vipx_fw_rmem@0xB8000000 { compatible = "exynos,vipx_fw_code_rmem"; - reg = <0x0 0xb8000000 0x300000>; + reg = <0x0 0xb8000000 0x2000000>; }; }; }; diff --git a/arch/arm64/boot/dts/exynos/exynos9610.dts b/arch/arm64/boot/dts/exynos/exynos9610.dts index ca8b1ac6ec77..03033a4a0569 100644 --- a/arch/arm64/boot/dts/exynos/exynos9610.dts +++ b/arch/arm64/boot/dts/exynos/exynos9610.dts @@ -2976,7 +2976,7 @@ #size-cells = <1>; ranges; - domain-clients = <>; + domain-clients = <&vipx>; }; iommu-domain_abox { @@ -3448,4 +3448,33 @@ available_stop_owner = ; buff_size = <0x100000>; }; + + vipx: vipx@10D60000 { + compatible = "samsung,exynos-vipx"; + id = <0>; + reg = <0x0 0x10D60000 0x10000>, /* VIPX_CPU_SS1 */ + <0x0 0x10F60000 0x10000>, /* VIPX_CPU_SS2 */ + <0x0 0x10D90000 0x2000>, /* ITCM(8K) */ + <0x0 0x10DA0000 0x4000>; /* DTCM(16K) */ + pinctrl-names = "default","release"; + pinctrl-0 = <>; + pinctrl-1 = <>; + clocks = <&clock UMUX_CLKCMU_VIPX1_BUS>, + <&clock GATE_VIPX1_QCH>, + <&clock UMUX_CLKCMU_VIPX2_BUS>, + <&clock GATE_VIPX2_QCH>, + <&clock GATE_VIPX2_QCH_LOCAL>; + clock-names = "UMUX_CLKCMU_VIPX1_BUS", + "GATE_VIPX1_QCH", + "UMUX_CLKCMU_VIPX2_BUS", + "GATE_VIPX2_QCH", + "GATE_VIPX2_QCH_LOCAL"; + + /*samsung,power-domain = <&pd_vipx2>;*/ + interrupts = <0 129 0>, + <0 130 0>; + iommus = <&sysmmu_vipx1>, <&sysmmu_vipx2>; + status = "ok"; + }; + }; diff --git a/arch/arm64/configs/erd9610_defconfig b/arch/arm64/configs/erd9610_defconfig index a759db57c5f9..d6c92b9acd91 100644 --- a/arch/arm64/configs/erd9610_defconfig +++ b/arch/arm64/configs/erd9610_defconfig @@ -535,3 +535,11 @@ CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_CRYPTO_DISKCIPHER=y CONFIG_EXYNOS_FMP=y +CONFIG_VISION_SUPPORT=y +CONFIG_VISION_CORE=y +CONFIG_EXYNOS_VIPX=y +CONFIG_EXYNOS_VIPX_PLATFORM=y +CONFIG_EXYNOS_VIPX_EXYNOS9610=y +CONFIG_EXYNOS_VIPX_INTERFACE=y +CONFIG_EXYNOS_VIPX_HARDWARE=y +CONFIG_EXYNOS_VIPX_MBOX=n diff --git a/drivers/Kconfig b/drivers/Kconfig index bb6470c107db..a56a1405a62e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -217,4 +217,7 @@ source "drivers/bts/Kconfig" source "drivers/gud/Kconfig" source "drivers/ccic/Kconfig" + +source "drivers/vision/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 27e2ecc02732..71affb129b79 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -191,3 +191,6 @@ obj-$(CONFIG_EXYNOS_BTS) += bts/ obj-$(CONFIG_TRUSTONIC_TEE) += gud/ obj-$(CONFIG_USBPD_CORE) += ccic/ + +obj-$(CONFIG_VISION_SUPPORT) += vision/ + diff --git a/drivers/vision/Kconfig b/drivers/vision/Kconfig new file mode 100644 index 000000000000..3652238e6827 --- /dev/null +++ b/drivers/vision/Kconfig @@ -0,0 +1,11 @@ +menuconfig VISION_SUPPORT + bool "Vision Support" + select VISION_CORE + help + If you want to use hardware acceleration for vision + enable this option and other options below. + +if VISION_SUPPORT +source "drivers/vision/vision-core/Kconfig" +source "drivers/vision/vipx/Kconfig" +endif # VISION_SUPPORT diff --git a/drivers/vision/Makefile b/drivers/vision/Makefile new file mode 100644 index 000000000000..ee83a72906c9 --- /dev/null +++ b/drivers/vision/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_VISION_CORE) += vision-core/ +obj-$(CONFIG_EXYNOS_VIPX) += vipx/ diff --git a/drivers/vision/include/videobuf2-ion.h b/drivers/vision/include/videobuf2-ion.h new file mode 100755 index 000000000000..341d684895d4 --- /dev/null +++ b/drivers/vision/include/videobuf2-ion.h @@ -0,0 +1,168 @@ +/* include/media/videobuf2-ion.h + * + * Copyright 2011-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Definition of Android ION memory allocator for videobuf2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MEDIA_VIDEOBUF2_ION_H +#define _MEDIA_VIDEOBUF2_ION_H + +#include +#include +//#include +//#include +#include +#include + +/* flags to vb2_ion_create_context + * These flags are dependet upon heap flags in ION. + * + * bit 0 ~ ION_NUM_HEAPS: ion heap flags + * bit ION_NUM_HEAPS+1 ~ 20: non-ion flags (cached, iommu) + * bit 21 ~ BITS_PER_INT - 1: ion specific flags + */ + +/* DMA of the client device is coherent with CPU */ +#define VB2ION_CTX_COHERENT_DMA (1 << (ION_NUM_HEAPS + 3)) +/* DMA should read from memory instead of CPU cache even if DMA is coherent */ +#define VB2ION_CTX_UNCACHED_READ_DMA (1 << (ION_NUM_HEAPS + 4)) + +#define VB2ION_CONTIG_ID_NUM 16 +#define VB2ION_NUM_HEAPS 8 +/* below 6 is the above vb2-ion flags (ION_NUM_HEAPS + 1 ~ 6) */ + + +struct device; +struct vb2_buffer; + +/* - vb2_ion_set_noncoherent_dma_read(): Forces the DMA to read from system + * memory even though it is capable of snooping CPU caches. + */ +void vb2_ion_set_noncoherent_dma_read(struct device *dev, bool noncoherent); + +/* Data type of the cookie returned by vb2_plane_cookie() function call. + * The drivers do not need the definition of this structure. The only reason + * why it is defined outside of videobuf2-ion.c is to make some functions + * inline. + */ +struct vb2_ion_cookie { + dma_addr_t ioaddr; + dma_addr_t paddr; + struct sg_table *sgt; + off_t offset; +}; + +/* vb2_ion_buffer_offset - return the mapped offset of the buffer + * - cookie: pointer returned by vb2_plane_cookie() + * + * Returns offset value that the mapping starts from. + */ + +static inline off_t vb2_ion_buffer_offset(void *cookie) +{ + return IS_ERR_OR_NULL(cookie) ? + -EINVAL : ((struct vb2_ion_cookie *)cookie)->offset; +} + +/* vb2_ion_phys_address - returns the physical address of the given buffer + * - cookie: pointer returned by vb2_plane_cookie() + * - phys_addr: pointer to the store of the physical address of the buffer + * specified by cookie. + * + * Returns -EINVAL if the buffer does not have nor physically contiguous memory. + */ +static inline int vb2_ion_phys_address(void *cookie, phys_addr_t *phys_addr) +{ + struct vb2_ion_cookie *vb2cookie = cookie; + + if (WARN_ON(!phys_addr || IS_ERR_OR_NULL(cookie))) + return -EINVAL; + + if (vb2cookie->paddr) { + *phys_addr = vb2cookie->paddr; + } else { + if (vb2cookie->sgt && vb2cookie->sgt->nents == 1) { + *phys_addr = sg_phys(vb2cookie->sgt->sgl) + + vb2cookie->offset; + } else { + *phys_addr = 0; + return -EINVAL; + } + } + + return 0; +} + +/* vb2_ion_dma_address - returns the DMA address that device can see + * - cookie: pointer returned by vb2_plane_cookie() + * - dma_addr: pointer to the store of the address of the buffer specified + * by cookie. It can be either IO virtual address or physical address + * depending on the specification of allocation context which allocated + * the buffer. + * + * Returns -EINVAL if the buffer has neither IO virtual address nor physically + * contiguous memory + */ +static inline int vb2_ion_dma_address(void *cookie, dma_addr_t *dma_addr) +{ + struct vb2_ion_cookie *vb2cookie = cookie; + + if (WARN_ON(!dma_addr || IS_ERR_OR_NULL(cookie))) + return -EINVAL; + + if (vb2cookie->ioaddr == 0) + return vb2_ion_phys_address(cookie, (phys_addr_t *)dma_addr); + + *dma_addr = vb2cookie->ioaddr; + + return 0; +} + +/* vb2_ion_get_sg - returns scatterlist of the given cookie. + * - cookie: pointer returned by vb2_plane_cookie() + * - nents: pointer to the store of number of elements in the returned + * scatterlist + * + * Returns the scatterlist of the buffer specified by cookie. + * If the arguments are not correct, returns NULL. + */ +static inline struct scatterlist *vb2_ion_get_sg(void *cookie, int *nents) +{ + struct vb2_ion_cookie *vb2cookie = cookie; + + if (WARN_ON(!nents || IS_ERR_OR_NULL(cookie))) + return NULL; + + *nents = vb2cookie->sgt->nents; + return vb2cookie->sgt->sgl; +} + +/* vb2_ion_get_dmabuf - returns dmabuf of the given cookie + * - cookie: pointer returned by vb2_plane_cookie() + * + * Returns dma-buf descriptor with a reference held. + * NULL if the cookie is invalid or the buffer is not dmabuf. + * The caller must put the dmabuf when it is no longer need the dmabuf + */ +struct dma_buf *vb2_ion_get_dmabuf(void *cookie); + +/***** Cache mainatenance operations *****/ +void vb2_ion_sync_for_device(void *cookie, off_t offset, size_t size, + enum dma_data_direction dir); +void vb2_ion_sync_for_cpu(void *cookie, off_t offset, size_t size, + enum dma_data_direction dir); +int vb2_ion_buf_prepare(struct vb2_buffer *vb); +void vb2_ion_buf_finish(struct vb2_buffer *vb); +int vb2_ion_buf_prepare_exact(struct vb2_buffer *vb); +int vb2_ion_buf_finish_exact(struct vb2_buffer *vb); + +extern const struct vb2_mem_ops vb2_ion_memops; +extern struct ion_device *ion_exynos; /* drivers/gpu/ion/exynos/exynos-ion.c */ + +#endif /* _MEDIA_VIDEOBUF2_ION_H */ diff --git a/drivers/vision/include/vision-buffer.h b/drivers/vision/include/vision-buffer.h new file mode 100755 index 000000000000..3449d1125083 --- /dev/null +++ b/drivers/vision/include/vision-buffer.h @@ -0,0 +1,167 @@ +/* + * Samsung Exynos SoC series VPU driver + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef VISION_BUFFER_H_ +#define VISION_BUFFER_H_ + +#include +#include +#include +#include +#include + +#include +#if defined(CONFIG_VIDEOBUF2_CMA_PHYS) +#include +#elif defined(CONFIG_VIDEOBUF2_DMA_SG) +//#include +#endif + +#include "vision-config.h" +#include "vs4l.h" + +#define VB_MAX_BUFFER VISION_MAX_BUFFER +#define VB_MAX_PLANES VISION_MAX_PLANE + +struct vb_queue; + +struct vb_fmt { + char *name; + u32 colorspace; + u32 planes; + u32 bitsperpixel[VB_MAX_PLANES]; +}; + +struct vb_format { + u32 target; + struct vb_fmt *fmt; + u32 colorspace; + u32 plane; + u32 width; + u32 height; + u32 size[VB_MAX_PLANES]; +}; + +struct vb_format_list { + u32 count; + struct vb_format *formats; +}; + +struct vb_buffer { + struct vs4l_roi roi; + union { + unsigned long userptr; + __s32 fd; + } m; + struct dma_buf *dbuf; + void *mem_priv; + void *cookie; + void *kvaddr; + dma_addr_t dvaddr; + ulong reserved; +#if 1 // MODERN_ION + struct dma_buf *dma_buf; + struct dma_buf_attachment *attachment; + struct sg_table *sgt; + dma_addr_t daddr; + void *vaddr; + size_t size; +#endif +}; + +struct vb_container { + u32 type; + u32 target; + u32 memory; + u32 reserved[4]; + u32 count; + struct vb_buffer *buffers; + struct vb_format *format; +}; + +struct vb_container_list { + u32 direction; + u32 id; + u32 index; + unsigned long flags; + struct timeval timestamp[6]; + u32 count; + u32 user_params[_MAX_NUM_OF_USER_PARAMS]; + struct vb_container *containers; +}; + +enum vb_bundle_state { + VB_BUF_STATE_DEQUEUED, + VB_BUF_STATE_QUEUED, + VB_BUF_STATE_PROCESS, + VB_BUF_STATE_DONE, +}; + +struct vb_bundle { + /* this flag is for internal state */ + unsigned long flags; + enum vb_bundle_state state; + struct list_head queued_entry; + struct list_head process_entry; + struct list_head done_entry; + + struct vb_container_list clist; +}; + +struct vb_ops { + int (*buf_prepare)(struct vb_queue *q, struct vb_container_list *clist); + int (*buf_unprepare)(struct vb_queue *q, struct vb_container_list *clist); +}; + +enum vb_queue_state { + VB_QUEUE_STATE_FORMAT, + VB_QUEUE_STATE_START +}; + +struct vb_queue { + u32 direction; + const char *name; + unsigned long state; + unsigned int streaming:1; + struct mutex *lock; + + struct list_head queued_list; + atomic_t queued_count; + struct list_head process_list; + atomic_t process_count; + struct list_head done_list; + atomic_t done_count; + + spinlock_t done_lock; + wait_queue_head_t done_wq; + + struct vb_format_list format; + struct vb_bundle *bufs[VB_MAX_BUFFER]; + unsigned int num_buffers; + + void *alloc_dev; + const struct vb2_mem_ops *mem_ops; + const struct vb_ops *ops; + void *private_data; +}; + +int vb_queue_init(struct vb_queue *q, void *alloc_ctx, const struct vb2_mem_ops *mem_ops, const struct vb_ops *ops, struct mutex *lock, u32 direction); +int vb_queue_s_format(struct vb_queue *q, struct vs4l_format_list *f); +int vb_queue_start(struct vb_queue *q); +int vb_queue_stop(struct vb_queue *q); +int vb_queue_qbuf(struct vb_queue *q, struct vs4l_container_list *c); +int vb_queue_dqbuf(struct vb_queue *q, struct vs4l_container_list *c, bool nonblocking); +void vb_queue_process(struct vb_queue *q, struct vb_bundle *vb); +void vb_queue_done(struct vb_queue *q, struct vb_bundle *vb); + +#define call_memop(q, op, args...) (((q)->mem_ops->op) ? ((q)->mem_ops->op(args)) : 0) +#define call_op(q, op, args...) (((q)->ops->op) ? ((q)->ops->op(args)) : 0) + +#endif diff --git a/drivers/vision/include/vision-config.h b/drivers/vision/include/vision-config.h new file mode 100755 index 000000000000..ee047a04a11b --- /dev/null +++ b/drivers/vision/include/vision-config.h @@ -0,0 +1,53 @@ +/* + * Samsung Exynos SoC series VPU driver + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef VISION_CONFIG_H_ +#define VISION_CONFIG_H_ + +#define VISION_MAX_BUFFER 16 +#define VISION_MAX_PLANE 3 +#define VISION_MAP_KVADDR + +/* +#define probe_info(fmt, ...) pr_info("[V]" fmt, ##__VA_ARGS__) +#define probe_warn(fmt, args...) pr_warning("[V][WRN]" fmt, ##args) +#define probe_err(fmt, args...) pr_err("[V][ERR]%s:%d:" fmt, __func__, __LINE__, ##args) +*/ +#define DISABLE_VISION_LOG 1 + +#if DISABLE_VISION_LOG +#define vision_err_target(fmt, ...) +#define vision_warn_target(fmt, ...) +#define vision_info_target(fmt, ...) +#define vision_dbg_target(fmt, ...) + +#else + +#ifdef DEBUG_LOG_MEMORY +#define vision_err_target(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) +#define vision_warn_target(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) +#define vision_info_target(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) +#define vision_dbg_target(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) +#else +#define vision_err_target(fmt, ...) pr_err(fmt, ##__VA_ARGS__) +#define vision_warn_target(fmt, ...) pr_warning(fmt, ##__VA_ARGS__) +#define vision_info_target(fmt, ...) pr_info(fmt, ##__VA_ARGS__) +#define vision_dbg_target(fmt, ...) pr_info(fmt, ##__VA_ARGS__) +#endif + +#endif +#define vision_err(fmt, args...) \ + vision_err_target("[V][ERR]%s:%d:" fmt, __func__, __LINE__, ##args) + +#define vision_info(fmt, args...) \ + vision_info_target("[V]" fmt, ##args) + +#endif diff --git a/drivers/vision/include/vision-dev.h b/drivers/vision/include/vision-dev.h new file mode 100755 index 000000000000..53fa7f515dc7 --- /dev/null +++ b/drivers/vision/include/vision-dev.h @@ -0,0 +1,74 @@ +/* + * Samsung Exynos SoC series VPU driver + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef VISION_DEV_H_ +#define VISION_DEV_H_ + +#include +#include + +#define VISION_NUM_DEVICES 256 +#define VISION_MAJOR 82 +#define VISION_NAME "vision4linux" + +enum VISION_DEVICE_TYPE { + VISION_DEVICE_TYPE_VERTEX +}; + +struct vision_file_ops { + struct module *owner; + int (*open) (struct file *); + int (*release) (struct file *); + unsigned int (*poll) (struct file *, struct poll_table_struct *); + long (*ioctl) (struct file *, unsigned int, unsigned long); + long (*compat_ioctl)(struct file *file, unsigned int, unsigned long); +}; + +enum VISION_FLAGS { + VISION_FL_REGISTERED +}; + +struct vision_device { + int minor; + char name[32]; + unsigned long flags; + int index; + u32 type; + + /* device ops */ + const struct vision_file_ops *fops; + + /* sysfs */ + struct device dev; + struct cdev *cdev; + struct device *parent; + + /* vb2_queue associated with this device node. May be NULL. */ + //struct vb2_queue *queue; + + + int debug; /* Activates debug level*/ + + /* callbacks */ + void (*release)(struct vision_device *vdev); + + /* ioctl callbacks */ + const struct vertex_ioctl_ops *ioctl_ops; + //DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); + + /* serialization lock */ + //DECLARE_BITMAP(disable_locking, BASE_VIDIOC_PRIVATE); + struct mutex *lock; +}; + +struct vision_device *vision_devdata(struct file *file); +int vision_register_device(struct vision_device *vdev, int minor, struct module *owner); + +#endif \ No newline at end of file diff --git a/drivers/vision/include/vision-ioctl.h b/drivers/vision/include/vision-ioctl.h new file mode 100644 index 000000000000..aaeee41b05dc --- /dev/null +++ b/drivers/vision/include/vision-ioctl.h @@ -0,0 +1,31 @@ +/* + * Samsung Exynos SoC series VPU driver + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "vs4l.h" + +#ifndef VISION_IOCTL_H_ +#define VISION_IOCTL_H_ + +struct vertex_ioctl_ops { + int (*vertexioc_s_graph)(struct file *file, struct vs4l_graph *graph); + int (*vertexioc_s_format)(struct file *file, struct vs4l_format_list *flist); + int (*vertexioc_s_param)(struct file *file, struct vs4l_param_list *plist); + int (*vertexioc_s_ctrl)(struct file *file, struct vs4l_ctrl *ctrl); + int (*vertexioc_qbuf)(struct file *file, struct vs4l_container_list *clist); + int (*vertexioc_dqbuf)(struct file *file, struct vs4l_container_list *clist); + int (*vertexioc_streamon)(struct file *file); + int (*vertexioc_streamoff)(struct file *file); +}; + +long vertex_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +long vertex_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg); + +#endif + diff --git a/drivers/vision/include/vs4l.h b/drivers/vision/include/vs4l.h new file mode 100755 index 000000000000..d8cdcf7bf87a --- /dev/null +++ b/drivers/vision/include/vs4l.h @@ -0,0 +1,175 @@ +/* + * Samsung Exynos SoC series VPU driver + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef VISION_FOR_LINUX_H_ +#define VISION_FOR_LINUX_H_ + +#define VS4L_VERSION 5 +#define VS4L_TARGET_SC 0xFFFF +#define VS4L_TARGET_SC_SHIFT 16 +#define VS4L_TARGET_PU 0xFFFF +#define VS4L_TARGET_PU_SHIFT 0 +#define _MAX_NUM_OF_USER_PARAMS 80 + + +enum vs4l_graph_flag { + VS4L_GRAPH_FLAG_FIXED, + VS4L_GRAPH_FLAG_SHARED_AMONG_SUBCHAINS, + VS4L_GRAPH_FLAG_SHARING_AMONG_SUBCHAINS_PREFERRED, + VS4L_GRAPH_FLAG_SHARED_AMONG_TASKS, + VS4L_GRAPH_FLAG_SHARING_AMONG_TASKS_PREFERRED, + VS4L_GRAPH_FLAG_DSBL_LATENCY_BALANCING, + VS4L_STATIC_ALLOC_LARGE_MPRB_INSTEAD_SMALL_FLAG, + VS4L_STATIC_ALLOC_PU_INSTANCE_LSB, + VS4L_GRAPH_FLAG_PRIMITIVE, + VS4L_GRAPH_FLAG_EXCLUSIVE, + VS4L_GRAPH_FLAG_PERIODIC, + VS4L_GRAPH_FLAG_ROUTING, + VS4L_GRAPH_FLAG_BYPASS, + VS4L_GRAPH_FLAG_END +}; + +struct vs4l_graph { + __u32 id; + __u32 priority; + __u32 time; /* in millisecond */ + __u32 flags; + __u32 size; + unsigned long addr; +}; + +struct vs4l_format { + __u32 target; + __u32 format; + __u32 plane; + __u32 width; + __u32 height; +}; + +struct vs4l_format_list { + __u32 direction; + __u32 count; + struct vs4l_format *formats; +}; + +struct vs4l_param { + __u32 target; + unsigned long addr; + __u32 offset; + __u32 size; +}; + +struct vs4l_param_list { + __u32 count; + struct vs4l_param *params; +}; + +struct vs4l_ctrl { + __u32 ctrl; + __u32 value; +}; + +struct vs4l_roi { + __u32 x; + __u32 y; + __u32 w; + __u32 h; +}; + +struct vs4l_buffer { + struct vs4l_roi roi; + union { + unsigned long userptr; + __s32 fd; + } m; + unsigned long reserved; +}; + +enum vs4l_buffer_type { + VS4L_BUFFER_LIST, + VS4L_BUFFER_ROI, + VS4L_BUFFER_PYRAMID +}; + +enum vs4l_memory { + VS4L_MEMORY_USERPTR = 1, + VS4L_MEMORY_VIRTPTR, + VS4L_MEMORY_DMABUF +}; + +struct vs4l_container { + __u32 type; + __u32 target; + __u32 memory; + __u32 reserved[4]; + __u32 count; + struct vs4l_buffer *buffers; +}; + +enum vs4l_direction { + VS4L_DIRECTION_IN = 1, + VS4L_DIRECTION_OT +}; + +enum vs4l_cl_flag { + VS4L_CL_FLAG_TIMESTAMP, + VS4L_CL_FLAG_PREPARE = 8, + VS4L_CL_FLAG_INVALID, + VS4L_CL_FLAG_DONE +}; + +struct vs4l_container_list { + __u32 direction; + __u32 id; + __u32 index; + __u32 flags; + struct timeval timestamp[6]; + __u32 count; + __u32 user_params[_MAX_NUM_OF_USER_PARAMS]; + struct vs4l_container *containers; +}; + +/* + * F O U R C C C O D E S F O R V I S I O N + * + */ + +#define VS4L_DF_IMAGE(a, b, c, d) ((a) | (b << 8) | (c << 16) | (d << 24)) +#define VS4L_DF_IMAGE_RGB VS4L_DF_IMAGE('R', 'G', 'B', '2') +#define VS4L_DF_IMAGE_RGBX VS4L_DF_IMAGE('R', 'G', 'B', 'A') +#define VS4L_DF_IMAGE_NV12 VS4L_DF_IMAGE('N', 'V', '1', '2') +#define VS4L_DF_IMAGE_NV21 VS4L_DF_IMAGE('N', 'V', '2', '1') +#define VS4L_DF_IMAGE_YV12 VS4L_DF_IMAGE('Y', 'V', '1', '2') +#define VS4L_DF_IMAGE_I420 VS4L_DF_IMAGE('I', '4', '2', '0') +#define VS4L_DF_IMAGE_I422 VS4L_DF_IMAGE('I', '4', '2', '2') +#define VS4L_DF_IMAGE_YUYV VS4L_DF_IMAGE('Y', 'U', 'Y', 'V') +#define VS4L_DF_IMAGE_YUV4 VS4L_DF_IMAGE('Y', 'U', 'V', '4') +#define VS4L_DF_IMAGE_U8 VS4L_DF_IMAGE('U', '0', '0', '8') +#define VS4L_DF_IMAGE_U16 VS4L_DF_IMAGE('U', '0', '1', '6') +#define VS4L_DF_IMAGE_U32 VS4L_DF_IMAGE('U', '0', '3', '2') +#define VS4L_DF_IMAGE_S16 VS4L_DF_IMAGE('S', '0', '1', '6') +#define VS4L_DF_IMAGE_S32 VS4L_DF_IMAGE('S', '0', '3', '2') + +/* + * I O C T L C O D E S F O R V E R T E X D E V I C E + * + */ + +#define VS4L_VERTEXIOC_S_GRAPH _IOW('V', 0, struct vs4l_graph) +#define VS4L_VERTEXIOC_S_FORMAT _IOW('V', 1, struct vs4l_format_list) +#define VS4L_VERTEXIOC_S_PARAM _IOW('V', 2, struct vs4l_param_list) +#define VS4L_VERTEXIOC_S_CTRL _IOW('V', 3, struct vs4l_ctrl) +#define VS4L_VERTEXIOC_STREAM_ON _IO('V', 4) +#define VS4L_VERTEXIOC_STREAM_OFF _IO('V', 5) +#define VS4L_VERTEXIOC_QBUF _IOW('V', 6, struct vs4l_container_list) +#define VS4L_VERTEXIOC_DQBUF _IOW('V', 7, struct vs4l_container_list) + +#endif diff --git a/drivers/vision/vipx/Kconfig b/drivers/vision/vipx/Kconfig new file mode 100644 index 000000000000..669622e4c232 --- /dev/null +++ b/drivers/vision/vipx/Kconfig @@ -0,0 +1,13 @@ +menuconfig EXYNOS_VIPX + bool "Exynos VIPx driver" + select EXYNOS_VIPX_PLATFORM + select EXYNOS_VIPX_INTERFACE + help + This is a vision image processing unit + +if EXYNOS_VIPX + +source "drivers/vision/vipx/platform/Kconfig" +source "drivers/vision/vipx/interface/Kconfig" + +endif diff --git a/drivers/vision/vipx/Makefile b/drivers/vision/vipx/Makefile new file mode 100644 index 000000000000..754c3db2d9da --- /dev/null +++ b/drivers/vision/vipx/Makefile @@ -0,0 +1,16 @@ +obj-y += vipx-debug.o +obj-y += vipx-binary.o +obj-y += vipx-time.o +obj-y += vipx-taskmgr.o +obj-y += vipx-graph.o +obj-y += vipx-graphmgr.o +obj-y += vipx-queue.o +obj-y += vipx-vertex.o +obj-y += vipx-device.o +obj-y += vipx-memory.o +obj-y += vipx-system.o +obj-y += vipx-io.o +obj-$(CONFIG_EXYNOS_VIPX_INTERFACE) += interface/ +obj-$(CONFIG_EXYNOS_VIPX_PLATFORM) += platform/ + +EXTRA_CFLAGS += -Idrivers/vision/include -Idrivers/vision/vipx/include diff --git a/drivers/vision/vipx/include/interface/ap_vip_if.h b/drivers/vision/vipx/include/interface/ap_vip_if.h new file mode 100755 index 000000000000..1abe9f026ada --- /dev/null +++ b/drivers/vision/vipx/include/interface/ap_vip_if.h @@ -0,0 +1,275 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Version history + * 2017.06.27 : Initial draft + * 2017.08.25 : Released + * 2017.10.12 : Add BOOTUP_RSP + */ + +#ifndef AP_VIP_IF_H_ +#define AP_VIP_IF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// temp for previous host ver +#if 0 +#define AP_FOCAL_ZONE_SETTING 0 +#else +#define AP_FOCAL_ZONE_SETTING 1 +#endif +/** + Typedefs +*/ +typedef u32 dram_addr_t; +typedef u32 graph_id_t; +typedef u32 job_id_t; + +/** + Message type +*/ +enum +{ + BOOTUP_RSP = 1, + INIT_REQ, + INIT_RSP, + CREATE_GRAPH_REQ, + CREATE_GRAPH_RSP, + SET_GRAPH_REQ, + SET_GRAPH_RSP, + INVOKE_GRAPH_REQ, + INVOKE_GRAPH_ACK, + INVOKE_GRAPH_RSP, + DESTROY_GRAPH_REQ, + DESTROY_GRAPH_RSP, + ABORT_GRAPH_REQ, + ABORT_GRAPH_RSP, + POWER_DOWN_REQ, + POWER_DOWN_RSP, + MAX_MSG_TYPE, +}; + +typedef u32 vipx_msgid_e; + +/** + Constants +*/ +enum +{ +// temp for previous host ver +#if 0 +MAX_NUM_OF_INPUTS = 16, +#else + MAX_NUM_OF_INPUTS = 24, +#endif +#if 0 + MAX_NUM_OF_OUTPUTS = 12, +#else + MAX_NUM_OF_OUTPUTS = 8, +#endif +// temp for previous host ver +#if 0 + MAX_NUM_OF_USER_PARAMS = 8, +#else + MAX_NUM_OF_USER_PARAMS = 80, +#endif +}; + +/** + Error indications +*/ +enum +{ + SUCCESS = 0, + ERROR_INVALID_GRAPH_ID = -1, + ERROR_EXEC_PARAM_CORRUPTION = -2, + ERROR_GRAPH_DATA_CORRUPTION = -3, + ERROR_COMPILED_GRAPH_ENABLED = -4, + ERROR_INVALID_MSG_TYPE = -5, + ERROR_UNKNOWN = -6, + ERROR_INVALID = -7, + ERROR_MBX_BUSY = -8, +}; + + +typedef s32 vipx_error_e; + +/** + Predefined builtin algorithms. +*/ + +enum +{ + SCENARIO_DE = 1, + SCENARIO_SDOF_FULL, + SCENARIO_DE_SDOF, +// temp for previous host ver +// SCENARIO_ENF, + SCENARIO_DE_CAPTURE, + SCENARIO_SDOF_CAPTURE, + SCENARIO_DE_SDOF_CAPTURE, + SCENARIO_GDC, + SCENARIO_ENF, + SCENARIO_ENF_UV, + SCENARIO_BLEND, + SCENARIO_AP_MAX, +}; + +#define SCENARIO_MAX SCENARIO_AP_MAX + +typedef u32 vipx_builtin_graph_id_e; + +typedef struct +{ + vipx_error_e error; +} __attribute__((__packed__)) vipx_bootup_rsp_t; + +typedef struct +{ + dram_addr_t p_cc_heap; //< pointer to CC heap region + u32 sz_cc_heap; //< size of CC heap region +} __attribute__((__packed__)) vipx_init_req_t; + +typedef struct +{ + vipx_error_e error; +} __attribute__((__packed__)) vipx_init_rsp_t; + +typedef struct +{ + dram_addr_t p_graph; //< pointer to compiled graph + u32 sz_graph; //< size of compiled graph +} __attribute__((__packed__)) vipx_create_graph_req_t; + +typedef struct +{ + vipx_error_e error; + graph_id_t graph_id; //< graph id that should be used for invocation +} __attribute__((__packed__)) vipx_create_graph_rsp_t; + +typedef struct +{ + graph_id_t graph_id; + + dram_addr_t p_lll_bin; //< pointer to VIP binary in DRAM + u32 sz_lll_bin; //< size of VIP binary in DRAM + + int num_inputs; //< number of inputs for the graph + int num_outputs; //< number of outputs for the graph + + u32 input_width[MAX_NUM_OF_INPUTS]; //< array of input width sizes + u32 input_height[MAX_NUM_OF_INPUTS]; //< array of input height sizes + u32 input_depth[MAX_NUM_OF_INPUTS]; //< array of input depth sizes + + u32 output_width[MAX_NUM_OF_OUTPUTS]; //< array of output width sizes + u32 output_height[MAX_NUM_OF_OUTPUTS]; //< array of output height sizes + u32 output_depth[MAX_NUM_OF_OUTPUTS]; //< array of output depth sizes + + dram_addr_t p_temp; //< pointer to DRAM area for temporary buffers + u32 sz_temp; //< size of temporary buffer area +} __attribute__((__packed__)) vipx_set_graph_req_t; + +typedef struct +{ + vipx_error_e error; +} __attribute__((__packed__)) vipx_set_graph_rsp_t; + +typedef struct +{ + graph_id_t graph_id; //< graph id that should be used for invocation + int num_inputs; //< number of inputs for the graph + int num_outputs; //< number of outputs for the graph + dram_addr_t p_input[MAX_NUM_OF_INPUTS]; //< array of input buffers + dram_addr_t p_output[MAX_NUM_OF_OUTPUTS]; //< array of output buffers +#if AP_FOCAL_ZONE_SETTING + u32 user_params[MAX_NUM_OF_USER_PARAMS]; //< array of user parameters +#endif +} __attribute__((__packed__)) vipx_invoke_graph_req_t; + +typedef struct +{ + vipx_error_e error; + job_id_t job_id; +} __attribute__((__packed__)) vipx_invoke_graph_ack_t; + +typedef struct +{ + vipx_error_e error; + job_id_t job_id; +} __attribute__((__packed__)) vipx_invoke_graph_rsp_t; + +typedef struct +{ + job_id_t job_id; +} __attribute__((__packed__)) vipx_abort_graph_req_t; + +typedef struct +{ + vipx_error_e error; +} __attribute__((__packed__)) vipx_abort_graph_rsp_t; + +typedef struct +{ + graph_id_t graph_id; +} __attribute__((__packed__)) vipx_destroy_graph_req_t; + +typedef struct +{ + vipx_error_e error; +} __attribute__((__packed__)) vipx_destroy_graph_rsp_t; + +typedef struct +{ + uint32_t valid; +} __attribute__((__packed__)) vipx_powerdown_req_t; + +typedef struct +{ + vipx_error_e error; +} __attribute__((__packed__)) vipx_powerdown_rsp_t; + + +union __attribute__((__packed__)) vipx_messages_u +{ + vipx_bootup_rsp_t bootup_rsp; + vipx_init_req_t init_req; + vipx_init_rsp_t init_rsp; + vipx_create_graph_req_t create_graph_req; + vipx_create_graph_rsp_t create_graph_rsp; + vipx_set_graph_req_t set_graph_req; + vipx_set_graph_rsp_t set_graph_rsp; + vipx_invoke_graph_req_t invoke_graph_req; + vipx_invoke_graph_ack_t invoke_graph_ack; + vipx_invoke_graph_rsp_t invoke_graph_rsp; + vipx_destroy_graph_req_t destroy_graph_req; + vipx_destroy_graph_rsp_t destroy_graph_rsp; + vipx_abort_graph_req_t abort_graph_req; + vipx_abort_graph_rsp_t abort_graph_rsp; + vipx_powerdown_req_t powerdown_req; + vipx_powerdown_rsp_t powerdown_rsp; +}; + +typedef struct +{ + // TODO: valid flag to be removed. + uint32_t valid; + uint32_t transId; + vipx_msgid_e type; + union vipx_messages_u msg_u; +} __attribute__((__packed__)) vipx_msg_t; + +#ifdef __cplusplus +} +#endif + +#endif // AP_VIP_IF_H_ diff --git a/drivers/vision/vipx/include/vipx-binary.h b/drivers/vision/vipx/include/vipx-binary.h new file mode 100755 index 000000000000..fedabaa4519f --- /dev/null +++ b/drivers/vision/vipx/include/vipx-binary.h @@ -0,0 +1,44 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef VIPX_BINARY_H_ +#define VIPX_BINARY_H_ + +#include +#include + +#define VIPX_FW_PATH1 "/data/" +#define VIPX_FW_PATH2 "/vendor/firmware/" + +#define VIPX_FW_DRAM_NAME "CC_DRAM_CODE_FLASH.bin" +#define VIPX_FW_ITCM_NAME "CC_ITCM_CODE_FLASH.bin" +#define VIPX_FW_DTCM_NAME "CC_DTCM_CODE_FLASH.bin" + +#define VIPX_FW_NAME_LEN 100 +#define VIPX_VERSION_SIZE 42 + +struct vipx_binary { + struct device *dev; +}; + +int vipx_binary_init(struct vipx_binary *binary, struct device *dev); + +int vipx_binary_read(struct vipx_binary *binary, + char *path, + char *name, + void *target, + size_t target_size); +int vipx_binary_write(struct vipx_binary *binary, + char *path, + char *name, + void *target, + size_t target_size); + +#endif diff --git a/drivers/vision/vipx/include/vipx-config.h b/drivers/vision/vipx/include/vipx-config.h new file mode 100755 index 000000000000..21184fe46bd4 --- /dev/null +++ b/drivers/vision/vipx/include/vipx-config.h @@ -0,0 +1,139 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef VIPX_CONFIG_H_ +#define VIPX_CONFIG_H_ + +/* + * ================================================================================================= + * CONFIG - GLOBAL OPTIONS + * ================================================================================================= + */ + +#define VIPX_MAX_BUFFER 16 +#define VIPX_MAX_PLANE 3 + +#define VIPX_MAX_GRAPH 32 +#define VIPX_MAX_TASK VIPX_MAX_BUFFER + +/* this macro determines schedule period, the unit is mile second */ +#define VIPX_TIME_TICK 5 +/* + * ================================================================================================= + * CONFIG -PLATFORM CONFIG + * ================================================================================================= + */ +#define VIPX_AHB_BASE_ADDR 0x20200000 +#define VIPX_STOP_WAIT_COUNT 200 + + +/* + * ================================================================================================= + * CONFIG - FEATURE ENABLE + * ================================================================================================= + */ + +/* #define VIPX_DYNAMIC_RESOURCE */ + +/* + * ================================================================================================= + * CONFIG - DEBUG OPTIONS + * ================================================================================================= + */ + +/* #define DBG_STREAMING */ +#define DBG_HISTORY +/* #define DBG_INTERFACE_ISR */ +/* #define DBG_TIMEMEASURE */ +#define DBG_MAP_KVADDR +/* #define DBG_RESOURCE */ +#define DBG_MARKING +/* #define DBG_HW_SFR */ +/* #define DBG_PRINT_TASK */ +/* #define DBG_VERBOSE_IO */ + +//#define DUMP_DEBUG_LOG_REGION +#define DEBUG_LOG_MEMORY +//#define PRINT_DBG + +#define DISABLE_VIPX_LOG 0 + +#if DISABLE_VIPX_LOG +#define probe_info(fmt, ...) +#define probe_warn(fmt, args...) +#define probe_err(fmt, args...) +#define vipx_err_target(fmt, ...) +#define vipx_warn_target(fmt, ...) +#define vipx_info_target(fmt, ...) +#define vipx_dbg_target(fmt, ...) + +#else + +#define probe_info(fmt, ...) pr_info("[V]" fmt, ##__VA_ARGS__) +#define probe_warn(fmt, args...) pr_warning("[V][WRN]" fmt, ##args) +#define probe_err(fmt, args...) pr_err("[V][ERR]%s:%d:" fmt, __func__, __LINE__, ##args) + +#ifdef DEBUG_LOG_MEMORY +#define vipx_err_target(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) +#define vipx_warn_target(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) +#define vipx_info_target(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) +#ifdef PRINT_DBG +#define vipx_dbg_target(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) +#else +#define vipx_dbg_target(fmt, ...) +#endif +#else +#define vipx_err_target(fmt, ...) pr_err(fmt, ##__VA_ARGS__) +#define vipx_warn_target(fmt, ...) pr_warning(fmt, ##__VA_ARGS__) +#define vipx_info_target(fmt, ...) pr_info(fmt, ##__VA_ARGS__) +#define vipx_dbg_target(fmt, ...) pr_info(fmt, ##__VA_ARGS__) +#endif + +#endif // DISABLE_ALL_LOG + + +#define vipx_err(fmt, args...) \ + vipx_err_target("[V][ERR]%s:%d:" fmt, __func__, __LINE__, ##args) + +#define vipx_ierr(fmt, vctx, args...) \ + vipx_err_target("[V][I%d][ERR]%s:%d:" fmt, vctx->idx, __func__, __LINE__, ##args) + +#define vipx_irerr(fmt, vctx, task, args...) \ + vipx_err_target("[V][I%d][F%d][ERR]%s:%d:" fmt, vctx->idx, task->id, __func__, __LINE__, ##args) + +#define vipx_warn(fmt, args...) \ + vipx_warn_target("[V][WRN]%s:%d:" fmt, __func__, __LINE__, ##args) + +#define vipx_iwarn(fmt, vctx, args...) \ + vipx_warn_target("[V][I%d][WRN]%s:%d:" fmt, vctx->idx, __func__, __LINE__, ##args) + +#define vipx_irwarn(fmt, vctx, task, args...) \ + vipx_warn_target("[V][I%d][F%d][WRN]%s:%d:" fmt, vctx->idx, task->id, __func__, __LINE__, ##args) + +#define vipx_info(fmt, args...) \ + vipx_info_target("[V]%s:%d:" fmt, __func__, __LINE__, ##args) + +#define vipx_iinfo(fmt, vctx, args...) \ + vipx_info_target("[V][I%d]" fmt, vctx->idx, ##args) + +#define vipx_irinfo(fmt, vctx, task, args...) \ + vipx_info_target("[V][I%d][F%d]" fmt, vctx->idx, task->id, ##args) + +#define vipx_dbg(fmt, args...) \ + vipx_dbg_target("[V]" fmt, ##args) + +#define vipx_idbg(fmt, vctx, args...) \ + vipx_dbg_target("[V][I%d]" fmt, vctx->idx, ##args) + +#define vipx_irdbg(fmt, vctx, task, args...) \ + vipx_dbg_target("[V][I%d][F%d]" fmt, vctx->idx, task->id, ##args) + +#endif diff --git a/drivers/vision/vipx/include/vipx-control.h b/drivers/vision/vipx/include/vipx-control.h new file mode 100644 index 000000000000..096611e9213c --- /dev/null +++ b/drivers/vision/vipx/include/vipx-control.h @@ -0,0 +1,20 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef VIPX_CONTROL_H_ +#define VIPX_CONTROL_H_ + +#define VISION_CTRL_VIPX_BASE 0x00010000 + +#define VIPX_CTRL_DUMP (VISION_CTRL_VIPX_BASE + 1) +#define VIPX_CTRL_MODE (VISION_CTRL_VIPX_BASE + 2) +#define VIPX_CTRL_TEST (VISION_CTRL_VIPX_BASE + 3) + +#endif diff --git a/drivers/vision/vipx/include/vipx-debug.h b/drivers/vision/vipx/include/vipx-debug.h new file mode 100644 index 000000000000..a9400f49b77d --- /dev/null +++ b/drivers/vision/vipx/include/vipx-debug.h @@ -0,0 +1,93 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef VIPX_DEBUG_H_ +#define VIPX_DEBUG_H_ + +#include +#include + +#include "vipx-config.h" + +#define DEBUG_SENTENCE_MAX 300 +#define DEBUG_MONITORING_PERIOD (HZ * 5) + +struct vipx_debug_imgdump { + struct dentry *file; + u32 target_graph; + u32 target_chain; + u32 target_pu; + u32 target_index; + void *kvaddr; + void *cookie; + size_t length; + size_t offset; +}; + +struct vipx_debug_monitor { + struct timer_list timer; + u32 time_cnt; + u32 tick_cnt; + u32 sched_cnt; + u32 done_cnt; +}; + +enum vipx_debug_state { + VIPX_DEBUG_STATE_START +}; + +struct vipx_debug { + unsigned long state; + + struct dentry *root; + struct dentry *logfile; + struct dentry *grpfile; + struct dentry *buffile; + + /* graph */ + void *graphmgr_data; + void *system_data; + + struct vipx_debug_imgdump imgdump; + struct vipx_debug_monitor monitor; +}; + +struct vipx_debug_log { + size_t dsentence_pos; + char dsentence[DEBUG_SENTENCE_MAX]; +}; + +s32 atoi(const char *psz_buf); +int bitmap_scnprintf(char *buf, unsigned int buflen, + const unsigned long *maskp, int nmaskbits); + +int vipx_debug_probe(struct vipx_debug *debug, void *graphmgr_data, void *interface_data); +int vipx_debug_open(struct vipx_debug *debug); +int vipx_debug_close(struct vipx_debug *debug); +int vipx_debug_start(struct vipx_debug *debug); +int vipx_debug_stop(struct vipx_debug *debug); + +void vipx_dmsg_concate(struct vipx_debug_log *log, const char *fmt, ...); +char * vipx_dmsg_print(struct vipx_debug_log *log); +int vipx_debug_memdump8(u8 *start, u8 *end); +int vipx_debug_memdump16(u16 *start, u16 *end); +int vipx_debug_memdump32(u32 *start, u32 *end); + +#ifdef DBG_HISTORY +#define DLOG_INIT() struct vipx_debug_log vipx_debug_log = { .dsentence_pos = 0 } +#define DLOG(fmt, ...) vipx_dmsg_concate(&vipx_debug_log, fmt, ##__VA_ARGS__) +#define DLOG_OUT() vipx_dmsg_print(&vipx_debug_log) +#else +#define DLOG_INIT() +#define DLOG(fmt, ...) +#define DLOG_OUT() "FORBIDDEN HISTORY" +#endif + +#endif diff --git a/drivers/vision/vipx/include/vipx-exynos.h b/drivers/vision/vipx/include/vipx-exynos.h new file mode 100644 index 000000000000..35a00d351949 --- /dev/null +++ b/drivers/vision/vipx/include/vipx-exynos.h @@ -0,0 +1,108 @@ +/* + * Samsung Exynos SoC series VIPX driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef VIPX_EXYNOS_H_ +#define VIPX_EXYNOS_H_ + +#include +#include + +struct vipx_exynos; + +enum { + VIPX_REG_CPU_SS1 = 0, + VIPX_REG_CPU_SS2 = 1, + VIPX_REG_MAX_CNT, +}; + +struct vipx_regblock { + unsigned int offset; + unsigned int blocks; + char *name; +}; + +enum regdata_type { + /* read write */ + RW = 0, + /* read only */ + RO = 1, + /* write only */ + WO = 2, + /* write input */ + WI = 2, + /* clear after read */ + RAC = 3, + /* write 1 -> clear */ + W1C = 4, + /* write read input */ + WRI = 5, + /* write input */ + RWI = 5, + /* only scaler */ + R_W = 6, + /* read & write for clear */ + RWC = 7, + /* read & write as dual setting */ + RWS +}; + +struct vipx_reg { + unsigned int offset; + char *name; +}; + +struct vipx_field { + char *name; + unsigned int bit_start; + unsigned int bit_width; + enum regdata_type type; +}; + +struct vipx_clk { + const char *name; + struct clk *clk; +}; + +struct vipx_clk_ops { + int (*clk_cfg)(struct vipx_exynos *exynos); + int (*clk_on)(struct vipx_exynos *exynos); + int (*clk_off)(struct vipx_exynos *exynos); + int (*clk_dump)(struct vipx_exynos *exynos); +}; + +struct vipx_ctl_ops { + int (*ctl_reset)(struct vipx_exynos *exynos, bool hold); + int (*ctl_dump)(struct vipx_exynos *exynos, u32 instance); + void * (*ctl_remap)(struct vipx_exynos *exynos, u32 instance); + int (*ctl_unmap)(struct vipx_exynos *exynos, u32 instance, void *base); + int (*ctl_trigger)(struct vipx_exynos *exynos, u32 chain_id); + int (*ctl_start)(struct vipx_exynos *exynos, u32 chain_id); +}; + +struct vipx_exynos { + struct device *dev; + void **regbase; + void *ram0base; + void *ram1base; + struct pinctrl *pinctrl; + const struct vipx_clk_ops *clk_ops; + const struct vipx_ctl_ops *ctl_ops; +}; + +int vipx_exynos_probe(struct vipx_exynos *exynos, struct device *dev, + void **regs, void *ram0, void *ram1); + +void vipx_readl(void __iomem *base_addr, struct vipx_reg *reg, u32 *val); +void vipx_writel(void __iomem *base_addr, struct vipx_reg *reg, u32 val); + +#define CLK_OP(exynos, op) (exynos->clk_ops ? exynos->clk_ops->op(exynos) : 0) +#define CTL_OP(exynos, op, ...) (exynos->ctl_ops ? exynos->ctl_ops->op(exynos, ##__VA_ARGS__) : 0) + +#endif diff --git a/drivers/vision/vipx/include/vipx-interface.h b/drivers/vision/vipx/include/vipx-interface.h new file mode 100644 index 000000000000..be0c6747bd26 --- /dev/null +++ b/drivers/vision/vipx/include/vipx-interface.h @@ -0,0 +1,110 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef VIPX_INTERFACE_H_ +#define VIPX_INTERFACE_H_ + +#include +#include +#include + +#include "vipx-taskmgr.h" +#include "vipx-slab.h" + +#define VIPX_WORK_MAX_COUNT 20 +#define VIPX_WORK_MAX_DATA 24 +#define VIPX_COMMAND_TIMEOUT (3 * HZ) + +typedef enum { + VIPX_WORK_MTYPE_BLK, + VIPX_WORK_MTYPE_NBLK, +} vipx_work_msg_t; + +struct vipx_work { + struct list_head list; + u32 valid:1; + u32 id; + vipx_work_msg_t message; + u32 work_param0; + u32 work_param1; + u32 work_param2; + u32 work_param3; + u8 data[VIPX_WORK_MAX_DATA]; +}; + +struct vipx_work_list { + struct vipx_work work[VIPX_WORK_MAX_COUNT]; + spinlock_t slock; + struct list_head free_head; + u32 free_cnt; + struct list_head reply_head; + u32 reply_cnt; + wait_queue_head_t wait_queue; +}; + +enum vipx_interface_state { + VIPX_ITF_STATE_OPEN, + VIPX_ITF_STATE_BOOTUP, + VIPX_ITF_STATE_ENUM, + VIPX_ITF_STATE_START +}; + +struct vipx_interface { + void *mbox; + size_t mbox_size; + void __iomem *regs; + resource_size_t regs_size; + unsigned long state; + + struct vipx_slab_allocator slab; + struct vipx_taskmgr taskmgr; + void *cookie; + u32 done_cnt; + + struct vipx_task *request[VIPX_MAX_GRAPH]; + struct mutex request_barrier; + struct vipx_work reply[VIPX_MAX_GRAPH]; + wait_queue_head_t reply_queue; + struct vipx_task *process; + spinlock_t process_barrier; + + struct vipx_work_list work_list; + struct work_struct work_queue; + +#if defined(CONFIG_EXYNOS_EMUL_MBOX) + struct timer_list timer; +#endif + void *private_data; +}; + +int vipx_interface_probe(struct vipx_interface *interface, + struct device *dev, + void __iomem *regs, + resource_size_t regs_size, + u32 irq0, u32 irq1); +int vipx_interface_open(struct vipx_interface *interface, + void *mbox, size_t mbox_size); +int vipx_interface_close(struct vipx_interface *interface); +int vipx_interface_start(struct vipx_interface *interface); +int vipx_interface_stop(struct vipx_interface *interface); +void vipx_interface_print(struct vipx_interface *interface); + +int vipx_hw_wait_bootup(struct vipx_interface *interface); + +int vipx_hw_enum(struct vipx_interface *interface); +int vipx_hw_init(struct vipx_interface *interface, struct vipx_task *itask); +int vipx_hw_deinit(struct vipx_interface *interface, struct vipx_task *itask); +int vipx_hw_create(struct vipx_interface *interface, struct vipx_task *itask); +int vipx_hw_destroy(struct vipx_interface *interface, struct vipx_task *itask); +int vipx_hw_config(struct vipx_interface *interface, struct vipx_task *itask); +int vipx_hw_process(struct vipx_interface *interface, struct vipx_task *itask); + +#endif diff --git a/drivers/vision/vipx/include/vipx-io.h b/drivers/vision/vipx/include/vipx-io.h new file mode 100644 index 000000000000..b631efe5e2c2 --- /dev/null +++ b/drivers/vision/vipx/include/vipx-io.h @@ -0,0 +1,50 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __VIPX_IO_H__ +#define __VIPX_IO_H__ + +#include +#include "vipx-config.h" + +#define IOR8(port) readb((const volatile void *)&port) +#define IOR16(port) readw((const volatile void *)&port) +#define IOR32(port) readl((const volatile void *)&port) +#define IOR64(port) readq((const volatile void *)&port) + +#ifdef DBG_VERBOSE_IO +#define IOW8(port, val) \ + do { pr_info("ADDR: %p, VAL: 0x%02x\r\n", \ + &port, val); writeb(val, &port);\ + } while (0) +#define IOW16(port, val) \ + do { pr_info("ADDR: %p, VAL: 0x%04x\r\n", \ + &port, val); writew(val, &port);\ + } while (0) +#define IOW32(port, val) \ + do { pr_info("ADDR: %p, VAL: 0x%08x\r\n", \ + &port, val); writel(val, &port);\ + } while (0) +#define IOW64(port, val) \ + do { pr_info("ADDR: %p, VAL: 0x%016llx\r\n", \ + &port, val); writeq(val, &port);\ + } while (0) + +#else /* not VERBOSE_WRITE */ +#define IOW8(port, val) writeb(val, &port) +#define IOW16(port, val) writew(val, &port) +#define IOW32(port, val) writel(val, &port) +#define IOW64(port, val) writeq(val, &port) +#endif /* not VERBOSE_WRITE */ + +void *mem2iocpy(void *dst,void *src, u32 size); +void *io2memcpy(void *dst,void *src, u32 size); + +#endif /* __VIPXL_IO_H__ */ diff --git a/drivers/vision/vipx/include/vipx-mailbox.h b/drivers/vision/vipx/include/vipx-mailbox.h new file mode 100644 index 000000000000..ce5b2d4efc60 --- /dev/null +++ b/drivers/vision/vipx/include/vipx-mailbox.h @@ -0,0 +1,69 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef VIPX_MAILBOX_H_ +#define VIPX_MAILBOX_H_ + +#include + +#define VIPX_MAILBOX_SIGNATURE1 0xCAFE +#define VIPX_MAILBOX_SIGNATURE2 0xEFAC +#define VIPX_MAILBOX_BASEOFFSET 16 + +#define VIPX_CMD_SIGNATURE 0x1234 +#define VIPX_DUM_SIGNATURE 0x5678 + +#define MAX_MESSAGE_CNT 8 + +#define DMESG() printk("[%s:%d]\n", __func__, __LINE__) + +enum { + VIPX_MTYPE_H2F_NORMAL = 0, + VIPX_MTYPE_H2F_URGENT = 1, + VIPX_MTYPE_F2H_NORMAL = 0, + VIPX_MTYPE_F2H_URGENT = 1, + VIPX_MTYPE_MAX, +}; + +struct vipx_mailbox_h2f { + volatile u32 wmsg_idx; /* Host permission is RW */ + volatile u32 rmsg_idx; /* Host permission is R_ONLY */ + vipx_msg_t msg[MAX_MESSAGE_CNT]; +}; + +struct vipx_mailbox_f2h { + volatile u32 wmsg_idx; /* Host permission is R_ONLY */ + volatile u32 rmsg_idx; /* Host permission is RW */ + vipx_msg_t msg[MAX_MESSAGE_CNT]; +}; + +struct vipx_mailbox_stack { + struct vipx_mailbox_h2f h2f; + struct vipx_mailbox_f2h f2h; +}; + +struct vipx_mailbox_ctrl { + struct vipx_mailbox_stack *stack; + struct vipx_mailbox_stack *urgent_stack; +}; + +struct vipx_mailbox_stack * vipx_mbox_g_stack(void *mbox, u32 mbox_size); +struct vipx_mailbox_stack * vipx_mbox_g_urgent_stack(void *mbox, u32 mbox_size); +int vipx_mbox_ready(struct vipx_mailbox_ctrl *mctrl, size_t size, u32 type); +int vipx_mbox_wait_reply(struct vipx_mailbox_ctrl *mctrl, u32 cmd, u32 type); +int vipx_mbox_write(struct vipx_mailbox_ctrl *mctrl, + void *payload, size_t size, u32 type, u32 gid, u32 cmd, u32 cid); +int vipx_mbox_read(struct vipx_mailbox_ctrl *mctrl, + void *payload, u32 type, void *debug_data); + +int emul_mbox_handler(struct vipx_mailbox_ctrl *mctrl); + +#endif diff --git a/drivers/vision/vipx/include/vipx-memory.h b/drivers/vision/vipx/include/vipx-memory.h new file mode 100755 index 000000000000..1ac32d5bf2f0 --- /dev/null +++ b/drivers/vision/vipx/include/vipx-memory.h @@ -0,0 +1,191 @@ +/* + * Samsung Exynos5 SoC series VIPx driver + * + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef VIPX_MEM_H +#define VIPX_MEM_H + +#include +#include +#include +#include +#include + + +#include "vipx-binary.h" + +/* + * TEMP for test + */ +#define VIPX_CM7_DRAM_BIN_SIZE (1024 * 1024 * 3) +#define VIPX_MBOX_SIZE (1024 * 24) +#define VIPX_DEBUG_SIZE (1024 * 1024 * 16) +#define VIPX_CM7_HEAP_SIZE_PREVIEW (1024 * 1024 * 16) +#define VIPX_CM7_HEAP_SIZE_ENF (1024 * 1024 * 64) +#define VIPX_CM7_HEAP_SIZE_CAPTURE (1024 * 1024 * 128) + +#define VIPX_IOVA_DRAM_FIRMWARE 0xB8000000 +#define VIPX_IOVA_DRAM_MBOX 0xA9000000 + +struct vipx_vb2_buf; +struct vipx_vb2_buf_ops { + ulong (*plane_kvaddr)(struct vipx_vb2_buf *vbuf, u32 plane); + ulong (*plane_cookie)(struct vipx_vb2_buf *vbuf, u32 plane); + dma_addr_t (*plane_dvaddr)(struct vipx_vb2_buf *vbuf, u32 plane); + void (*plane_prepare)(struct vipx_vb2_buf *vbuf, u32 plane, + bool exact); + void (*plane_finish)(struct vipx_vb2_buf *vbuf, u32 plane, + bool exact); + void (*buf_prepare)(struct vipx_vb2_buf *vbuf, bool exact); + void (*buf_finish)(struct vipx_vb2_buf *vbuf, bool exact); +}; + +enum vipx_vbuf_cache_state { + VIPX_VBUF_CACHE_INVALIDATE, +}; + +struct vipx_vb2_buf { + struct vb2_v4l2_buffer vb; + ulong kva[VIDEO_MAX_PLANES]; + dma_addr_t dva[VIDEO_MAX_PLANES]; + + /* for cache operation */ + unsigned long cache_state; + struct list_head cache_flush_list; + + const struct vipx_vb2_buf_ops *ops; +}; + +struct vipx_priv_buf; +struct vipx_priv_buf_ops { + void (*free)(struct vipx_priv_buf *pbuf); + void *(*kvaddr)(struct vipx_priv_buf *pbuf); + dma_addr_t (*dvaddr)(struct vipx_priv_buf *pbuf); + phys_addr_t (*phaddr)(struct vipx_priv_buf *pbuf); + void (*sync_for_device)(struct vipx_priv_buf *pbuf, + off_t offset, size_t size, + enum dma_data_direction dir); + void (*sync_for_cpu)(struct vipx_priv_buf *pbuf, + off_t offset, size_t size, + enum dma_data_direction dir); +}; + +struct vipx_priv_buf { + size_t size; + size_t align; + void *ctx; + void *kvaddr; + + const struct vipx_priv_buf_ops *ops; + void *priv; + struct dma_buf *dma_buf; + struct dma_buf_attachment *attachment; + enum dma_data_direction direction; + void *kva; + dma_addr_t iova; + struct sg_table *sgt; +}; + +#define vb_to_vipx_vb2_buf(x) \ + container_of(x, struct vipx_vb2_buf, vb) + +#define CALL_BUFOP(buf, op, args...) \ + ((buf)->ops->op ? (buf)->ops->op(args) : 0) + +#define CALL_PTR_BUFOP(buf, op, args...) \ + ((buf)->ops->op ? (buf)->ops->op(args) : NULL) + +#define CALL_VOID_BUFOP(buf, op, args...) \ + do { \ + if ((buf)->ops->op) \ + (buf)->ops->op(args); \ + } while (0) + +#define call_buf_op(buf, op, args...) \ + ((buf)->ops->op ? (buf)->ops->op((buf), args) : 0) + +struct vipx_mem_ops { + void *(*init)(struct device *dev); + void (*cleanup)(void *ctx); + int (*resume)(void *ctx); + void (*suspend)(void *ctx); + void (*set_cached)(void *ctx, bool cacheable); + int (*set_alignment)(void *ctx, size_t alignment); + struct vipx_priv_buf *(*alloc)(void *ctx, size_t size, size_t align); +}; + +struct vipx_ion_ctx { + struct device *dev; + unsigned long alignment; + long flags; + + /* protects iommu_active_cnt and protected */ + struct mutex lock; + int iommu_active_cnt; +}; + +struct vipx_minfo { + struct vipx_priv_buf *pb_debug; + struct vipx_priv_buf *pb_heap; + + ulong kvaddr_debug_cnt; + + dma_addr_t paddr_fw; + dma_addr_t dvaddr_fw; + void *kvaddr_fw; + + dma_addr_t paddr_mbox; + dma_addr_t dvaddr_mbox; + void *kvaddr_mbox; + + dma_addr_t paddr_debug; + dma_addr_t dvaddr_debug; + void *kvaddr_debug; + + dma_addr_t paddr_heap; + dma_addr_t dvaddr_heap; + void *kvaddr_heap; +}; + +struct vipx_memory { + struct device *dev; + struct vipx_ion_ctx *default_ctx; + struct vipx_ion_ctx *phcontig_ctx; + const struct vipx_mem_ops *vipx_mem_ops; + const struct vb2_mem_ops *vb2_mem_ops; + const struct vipx_vb2_buf_ops *vipx_vb2_buf_ops; + struct iommu_domain *iommu_domain; + + struct vipx_minfo info; + void *priv; + struct vipx_priv_buf *(*kmalloc)(size_t size, size_t align); +}; + +#define CALL_MEMOP(mem, op, args...) \ + ((mem)->vipx_mem_ops->op ? \ + (mem)->vipx_mem_ops->op(args) : 0) + +#define CALL_PTR_MEMOP(mem, op, args...) \ + ((mem)->vipx_mem_ops->op ? \ + (mem)->vipx_mem_ops->op(args) : NULL) + +#define CALL_VOID_MEMOP(mem, op, args...) \ + do { \ + if ((mem)->vipx_mem_ops->op) \ + (mem)->vipx_mem_ops->op(args); \ + } while (0) + +int vipx_memory_probe(struct vipx_memory *mem, struct device *dev); +int vipx_memory_open(struct vipx_memory *mem); +int vipx_memory_close(struct vipx_memory *mem); +dma_addr_t vipx_allocate_heap(struct vipx_memory *mem, u32 size); +void vipx_free_heap(struct vipx_memory *mem, struct vipx_binary *binary, u32 heap_size); + +#endif diff --git a/drivers/vision/vipx/include/vipx-slab.h b/drivers/vision/vipx/include/vipx-slab.h new file mode 100644 index 000000000000..372cfc69fd67 --- /dev/null +++ b/drivers/vision/vipx/include/vipx-slab.h @@ -0,0 +1,28 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef VIPX_SLAB_H_ +#define VIPX_SLAB_H_ + +#include + +#define VIPX_SLAB_MAX_LEVEL 1 + +struct vipx_slab_allocator { + u32 cache_size[VIPX_SLAB_MAX_LEVEL]; + struct kmem_cache *cache[VIPX_SLAB_MAX_LEVEL]; +}; + +int vipx_slab_init(struct vipx_slab_allocator *allocator); +int vipx_slab_alloc(struct vipx_slab_allocator *allocator, void **target, size_t size); +int vipx_slab_free(struct vipx_slab_allocator *allocator, void *target, size_t size); + +#endif diff --git a/drivers/vision/vipx/include/vipx-taskmgr.h b/drivers/vision/vipx/include/vipx-taskmgr.h new file mode 100644 index 000000000000..cf13c3261a1e --- /dev/null +++ b/drivers/vision/vipx/include/vipx-taskmgr.h @@ -0,0 +1,194 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef VIPX_TASKMGR_H_ +#define VIPX_TASKMGR_H_ + +#include +#include + +#include "vipx-config.h" +#include "vipx-time.h" +#include "vs4l.h" + +#define TASKMGR_IDX_0 (1 << 0 ) /* graphmgr thread */ +#define TASKMGR_IDX_1 (1 << 1 ) +#define TASKMGR_IDX_2 (1 << 2 ) +#define TASKMGR_IDX_3 (1 << 3 ) +#define TASKMGR_IDX_4 (1 << 4 ) +#define TASKMGR_IDX_5 (1 << 5 ) +#define TASKMGR_IDX_6 (1 << 6 ) +#define TASKMGR_IDX_7 (1 << 7 ) +#define TASKMGR_IDX_8 (1 << 8 ) +#define TASKMGR_IDX_9 (1 << 9 ) +#define TASKMGR_IDX_10 (1 << 10) +#define TASKMGR_IDX_11 (1 << 11) +#define TASKMGR_IDX_12 (1 << 12) +#define TASKMGR_IDX_13 (1 << 13) +#define TASKMGR_IDX_14 (1 << 14) +#define TASKMGR_IDX_15 (1 << 15) +#define TASKMGR_IDX_16 (1 << 16) +#define TASKMGR_IDX_17 (1 << 17) +#define TASKMGR_IDX_18 (1 << 18) +#define TASKMGR_IDX_19 (1 << 19) +#define TASKMGR_IDX_20 (1 << 20) +#define TASKMGR_IDX_21 (1 << 21) +#define TASKMGR_IDX_22 (1 << 22) +#define TASKMGR_IDX_23 (1 << 23) +#define TASKMGR_IDX_24 (1 << 24) +#define TASKMGR_IDX_25 (1 << 25) +#define TASKMGR_IDX_26 (1 << 26) +#define TASKMGR_IDX_27 (1 << 27) +#define TASKMGR_IDX_28 (1 << 28) +#define TASKMGR_IDX_29 (1 << 29) +#define TASKMGR_IDX_30 (1 << 30) +#define TASKMGR_IDX_31 (1 << 31) + +#define taskmgr_e_barrier_irqs(taskmgr, index, flag) \ + taskmgr->sindex |= index; spin_lock_irqsave(&taskmgr->slock, flag) +#define taskmgr_x_barrier_irqr(taskmgr, index, flag) \ + spin_unlock_irqrestore(&taskmgr->slock, flag); taskmgr->sindex &= ~index +#define taskmgr_e_barrier_irq(taskmgr, index) \ + taskmgr->sindex |= index; spin_lock_irq(&taskmgr->slock) +#define taskmgr_x_barrier_irq(taskmgr, index) \ + spin_unlock_irq(&taskmgr->slock); taskmgr->sindex &= ~index +#define taskmgr_e_barrier(taskmgr, index) \ + taskmgr->sindex |= index; spin_lock(&taskmgr->slock) +#define taskmgr_x_barrier(taskmgr, index) \ + spin_unlock(&taskmgr->slock); taskmgr->sindex &= ~index + +enum vipx_task_state { + VIPX_TASK_STATE_FREE = 1, + VIPX_TASK_STATE_REQUEST, + VIPX_TASK_STATE_PREPARE, + VIPX_TASK_STATE_PROCESS, + VIPX_TASK_STATE_COMPLETE, + VIPX_TASK_STATE_INVALID +}; + +enum vipx_task_flag { + VIPX_TASK_FLAG_IOCPY = 16 +}; + +enum vipx_task_message { + VIPX_TASK_INIT = 1, + VIPX_TASK_DEINIT, + VIPX_TASK_CREATE, + VIPX_TASK_DESTROY, + VIPX_TASK_ALLOCATE, + VIPX_TASK_PROCESS, + VIPX_TASK_REQUEST = 10, + VIPX_TASK_DONE, + VIPX_TASK_NDONE +}; + +enum vipx_control_message { + VIPX_CTRL_NONE = 100, + VIPX_CTRL_STOP, + VIPX_CTRL_STOP_DONE +}; + +struct vipx_task { + struct list_head list; + struct kthread_work work; + u32 state; + + u32 message; + ulong param0; + ulong param1; + ulong param2; + ulong param3; + + struct vb_container_list *incl; + struct vb_container_list *otcl; + ulong flags; + + u32 id; + struct mutex *lock; + u32 index; + u32 findex; + u32 tdindex; + void *owner; + + struct vipx_time time[VIPX_TMP_COUNT]; +}; + +struct vipx_taskmgr { + u32 id; + u32 sindex; + spinlock_t slock; + struct vipx_task task[VIPX_MAX_TASK]; + + struct list_head fre_list; + struct list_head req_list; + struct list_head pre_list; + struct list_head pro_list; + struct list_head com_list; + + u32 tot_cnt; + u32 fre_cnt; + u32 req_cnt; + u32 pre_cnt; + u32 pro_cnt; + u32 com_cnt; +}; + +void vipx_task_s_free(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_g_free(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_free_head(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_free_tail(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_print_free_list(struct vipx_taskmgr *taskmgr); + +void vipx_task_s_request(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_g_request(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_request_head(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_request_tail(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_print_request_list(struct vipx_taskmgr *taskmgr); + +void vipx_task_s_prepare(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_g_prepare(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_prepare_head(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_prepare_tail(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_print_prepare_list(struct vipx_taskmgr *taskmgr); + +void vipx_task_s_process(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_g_process(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_process_head(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_process_tail(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_print_process_list(struct vipx_taskmgr *taskmgr); + +void vipx_task_s_complete(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_g_complete(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_complete_head(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_complete_tail(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_print_complete_list(struct vipx_taskmgr *taskmgr); + +void vipx_task_trans_fre_to_req(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_trans_req_to_pre(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_trans_req_to_pro(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_trans_req_to_com(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_trans_req_to_fre(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_trans_pre_to_pro(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_trans_pre_to_com(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_trans_pre_to_fre(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_trans_pro_to_com(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_trans_pro_to_fre(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_trans_com_to_fre(struct vipx_taskmgr *taskmgr, struct vipx_task *task); +void vipx_task_trans_any_to_fre(struct vipx_taskmgr *taskmgr, struct vipx_task *task); + +void vipx_task_pick_fre_to_req(struct vipx_taskmgr *taskmgr, struct vipx_task **task); +void vipx_task_print_all(struct vipx_taskmgr *taskmgr); + +int vipx_task_init(struct vipx_taskmgr *taskmgr, void *owner); +int vipx_task_deinit(struct vipx_taskmgr *taskmgr); +void vipx_task_flush(struct vipx_taskmgr *taskmgr); + +#endif diff --git a/drivers/vision/vipx/include/vipx-time.h b/drivers/vision/vipx/include/vipx-time.h new file mode 100644 index 000000000000..4dbb5070e32f --- /dev/null +++ b/drivers/vision/vipx/include/vipx-time.h @@ -0,0 +1,31 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +enum vipx_time_measure_point { + VIPX_TMP_QUEUE, + VIPX_TMP_REQUEST, + VIPX_TMP_RESOURCE, + VIPX_TMP_PROCESS, + VIPX_TMP_DONE, + VIPX_TMP_COUNT +}; + +struct vipx_time { + struct timeval time; +}; + +void vipx_get_timestamp(struct vipx_time *time); + +#define VIPX_TIME_IN_US(v) ((v).time.tv_sec * 1000000 + (v).time.tv_usec) diff --git a/drivers/vision/vipx/interface/Kconfig b/drivers/vision/vipx/interface/Kconfig new file mode 100644 index 000000000000..7eb9fd562bfb --- /dev/null +++ b/drivers/vision/vipx/interface/Kconfig @@ -0,0 +1,29 @@ +config EXYNOS_VIPX_INTERFACE + bool "Select Interface" + help + This is VIPx interface + +config EXYNOS_VIPX_HARDWARE + bool "Use Hardware" + depends on EXYNOS_VIPX_INTERFACE + help + This is hardware device + +choice + prompt "Select Message box type" + depends on EXYNOS_VIPX_INTERFACE + help + Select VIP(x) message box type. + +config EXYNOS_VIPX_MBOX + bool "Use VIPX MBOX" + depends on EXYNOS_VIPX_INTERFACE + help + This is message box device + +config EXYNOS_EMUL_MBOX + bool "Use EMUL MBOX" + depends on EXYNOS_VIPX_INTERFACE + help + This is message box emulator +endchoice diff --git a/drivers/vision/vipx/interface/Makefile b/drivers/vision/vipx/interface/Makefile new file mode 100644 index 000000000000..4f3b9953b4f7 --- /dev/null +++ b/drivers/vision/vipx/interface/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_EXYNOS_VIPX_HARDWARE) += hardware/ +obj-$(CONFIG_EXYNOS_VIPX_HARDWARE) += vipx-slab.o + +EXTRA_CFLAGS += -Idrivers/vision/include -Idrivers/vision/vipx/include diff --git a/drivers/vision/vipx/interface/hardware/Makefile b/drivers/vision/vipx/interface/hardware/Makefile new file mode 100644 index 000000000000..3f2917b84d3a --- /dev/null +++ b/drivers/vision/vipx/interface/hardware/Makefile @@ -0,0 +1,4 @@ +obj-y += vipx-interface.o +obj-$(CONFIG_EXYNOS_VIPX_MBOX) += vipx-mailbox.o +obj-$(CONFIG_EXYNOS_EMUL_MBOX) += emul-mailbox.o +EXTRA_CFLAGS += -Idrivers/vision/include -Idrivers/vision/vipx/include diff --git a/drivers/vision/vipx/interface/hardware/emul-mailbox.c b/drivers/vision/vipx/interface/hardware/emul-mailbox.c new file mode 100644 index 000000000000..d51e3184faa4 --- /dev/null +++ b/drivers/vision/vipx/interface/hardware/emul-mailbox.c @@ -0,0 +1,402 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "vipx-config.h" +#include "vipx-io.h" +#include "vipx-mailbox.h" +#include "vipx-interface.h" + +#include + +#define INDEX(x) (x % MAX_MESSAGE_CNT) + +struct vipx_mailbox_stack * vipx_mbox_g_stack(void *mbox, u32 mbox_size) +{ + vipx_info("sizeof mbox stack (%lx) %p\n", sizeof(struct vipx_mailbox_stack), mbox); + return (struct vipx_mailbox_stack *)mbox; +} + +struct vipx_mailbox_stack * vipx_mbox_g_urgent_stack(void *mbox, u32 mbox_size) +{ + vipx_info("%p\n", ((char *)mbox + sizeof(struct vipx_mailbox_stack))); + return (struct vipx_mailbox_stack *)((char *)mbox + sizeof(struct vipx_mailbox_stack)); +} + +static u32 __vipx_mbox_g_freesize(struct vipx_mailbox_h2f *mbox) +{ + u32 wmsg_idx = 0; + u32 rmsg_idx = 0; + u32 free_size = 0; + + BUG_ON(!mbox); + + wmsg_idx = mbox->wmsg_idx; + rmsg_idx = mbox->rmsg_idx; + + free_size = MAX_MESSAGE_CNT - (wmsg_idx - rmsg_idx); + + BUG_ON(free_size < 0); + + return free_size; +} + +int vipx_mbox_ready(struct vipx_mailbox_ctrl *mctrl, + size_t size, u32 type) +{ + u32 try_count; + struct vipx_mailbox_h2f *mbox; + u16 free_size16; + + BUG_ON(!mctrl); + BUG_ON(!IS_ALIGNED(size, 2)); + + if (type == VIPX_MTYPE_H2F_NORMAL) { + mbox = &mctrl->stack->h2f; + } else if (type == VIPX_MTYPE_H2F_URGENT) { + mbox = &mctrl->urgent_stack->h2f; + } else { + vipx_err("invalid type(%d)\n", type); + return -EINVAL; + } + try_count = 1000; + + free_size16 = __vipx_mbox_g_freesize(mbox); + while (--try_count && (free_size16 == 0)) { + vipx_warn("mbox is not ready(freesize %d)...(%d)\n", + free_size16, try_count); + udelay(10); + free_size16 = __vipx_mbox_g_freesize(mbox); + } + + if (try_count) + return 0; + else + return -EBUSY; +} + +int vipx_mbox_wait_reply(struct vipx_mailbox_ctrl *mctrl, + u32 cmd, u32 type) +{ + int ret = 0; + struct vipx_mailbox_f2h *mbox; + int try_count; + + if (type == VIPX_MTYPE_F2H_NORMAL) { + mbox = &mctrl->stack->f2h; + } else if (type == VIPX_MTYPE_F2H_URGENT) { + mbox = &mctrl->urgent_stack->f2h; + } else { + vipx_err("cmd(%d) has invalid type(%d)\n", cmd, type); + ret = -EINVAL; + goto p_err; + } + + try_count = 1000; + while (--try_count && (mbox->wmsg_idx == mbox->rmsg_idx)) { + vipx_info("waiting vipx reply(%d, %d)...(%d)\n", mbox->wmsg_idx, mbox->rmsg_idx, try_count); + msleep(1); + } + + if (try_count <= 0) { + vipx_err("waiting vipx reply is timeout\n"); + ret = -EINVAL; + goto p_err; + } + +p_err: + return ret; +} + +int vipx_mbox_write(struct vipx_mailbox_ctrl *mctrl, + void *payload, size_t size, u32 type, u32 gid, u32 cmd, u32 cid) +{ + int ret = 0; + struct vipx_mailbox_h2f *mbox; + u32 wmsg_idx; + u32 *wptr; + + BUG_ON(!mctrl); + BUG_ON(!payload); + BUG_ON(!IS_ALIGNED(size, 2)); + + if (type == VIPX_MTYPE_H2F_NORMAL) { + mbox = &mctrl->stack->h2f; + } else if (type == VIPX_MTYPE_H2F_URGENT) { + mbox = &mctrl->urgent_stack->h2f; + } else { + vipx_err("invalid type(%d)\n", type); + ret = -EINVAL; + goto p_err; + } + wmsg_idx = mbox->wmsg_idx; + wptr = (void *)&mbox->msg[wmsg_idx % MAX_MESSAGE_CNT]; + + mem2iocpy(wptr, payload, size); + +#ifdef DBG_MAILBOX_CORE + vipx_info("[I%d][MBOX][H2F] %d command(%d, %d, %d)\n", gid, + cmd, cid, wmsg_idx, mbox->rmsg_idx); +#endif + + /* increment actual write pointer */ + mbox->wmsg_idx++; + +p_err: + return ret; +} + +int vipx_mbox_read(struct vipx_mailbox_ctrl *mctrl, + void *payload, u32 type, void *debug_data) +{ + int ret = 0; + struct vipx_mailbox_f2h *mbox; + u32 rmsg_idx; + u32 *rptr; + + BUG_ON(!mctrl); + BUG_ON(!payload); + + if (type == VIPX_MTYPE_H2F_NORMAL) { + mbox = &mctrl->stack->f2h; + } else if (type == VIPX_MTYPE_H2F_URGENT) { + mbox = &mctrl->urgent_stack->f2h; + } else { + vipx_err("invalid type(%d)\n", type); + ret = -EINVAL; + goto p_err; + } + rmsg_idx = mbox->rmsg_idx; + rptr = (void *)&mbox->msg[rmsg_idx % MAX_MESSAGE_CNT]; + + io2memcpy(payload, rptr, sizeof(vipx_msg_t)); + + mbox->rmsg_idx++; + +p_err: + return ret; +} + +/* emul mbox */ + +static u32 __emul_mbox_g_freesize(struct vipx_mailbox_f2h *mbox) +{ + u32 wmsg_idx = 0; + u32 rmsg_idx = 0; + u32 free_size = 0; + + BUG_ON(!mbox); + + wmsg_idx = mbox->wmsg_idx; + rmsg_idx = mbox->rmsg_idx; + + free_size = MAX_MESSAGE_CNT - (wmsg_idx - rmsg_idx); + + BUG_ON(free_size < 0); + + return free_size; +} + +int emul_mbox_ready(struct vipx_mailbox_ctrl *mctrl, u32 type) +{ + u32 try_count; + struct vipx_mailbox_f2h *mbox; + u32 free_size; + + BUG_ON(!mctrl); + + vipx_info("________ %d\n", type); + + if (type == VIPX_MTYPE_F2H_NORMAL) { + mbox = &mctrl->stack->f2h; + } else if (type == VIPX_MTYPE_F2H_URGENT) { + mbox = &mctrl->urgent_stack->f2h; + } else { + vipx_err("invalid type(%d)\n", type); + return -EINVAL; + } + try_count = 1000; + + free_size = __emul_mbox_g_freesize(mbox); + while (--try_count && (free_size == 0)) { + vipx_warn("mbox is not ready(freesize %d)...(%d)\n", + free_size, try_count); + udelay(10); + free_size = __emul_mbox_g_freesize(mbox); + } + + if (try_count) + return 0; + else + return -EBUSY; +} + +int emul_mbox_check_message_from_host(struct vipx_mailbox_ctrl *mctrl, u32 type) +{ + int ret = 0; + u32 wmsg_idx, rmsg_idx; + + if (type == VIPX_MTYPE_H2F_NORMAL) { + wmsg_idx = mctrl->stack->h2f.wmsg_idx; + rmsg_idx = mctrl->stack->h2f.rmsg_idx; + } else if (type == VIPX_MTYPE_H2F_URGENT) { + wmsg_idx = mctrl->urgent_stack->h2f.wmsg_idx; + rmsg_idx = mctrl->urgent_stack->h2f.rmsg_idx; + } else { + vipx_err("invalid type(%d)\n", type); + ret = -EINVAL; + goto p_err; + } + + if ((wmsg_idx == rmsg_idx)) { + ret = -EINVAL; + goto p_err; + } + +p_err: + return ret; +} + +int emul_mbox_write(struct vipx_mailbox_ctrl *mctrl, + void *payload, size_t size, u32 type) +{ + struct vipx_mailbox_f2h *mbox; + u32 wmsg_idx; + u32 *wptr; + + BUG_ON(!mctrl); + BUG_ON(!payload); + BUG_ON(!IS_ALIGNED(size, 2)); + + if (type == VIPX_MTYPE_F2H_NORMAL) { + mbox = &mctrl->stack->f2h; + } else if (type == VIPX_MTYPE_F2H_URGENT) { + mbox = &mctrl->urgent_stack->f2h; + } else { + vipx_err("invalid type(%d)\n", type); + return -EINVAL; + } + wmsg_idx = mbox->wmsg_idx; + wptr = (void *)&mbox->msg[wmsg_idx % MAX_MESSAGE_CNT]; + + mem2iocpy(wptr, payload, size); + + /* increment actual write pointer */ + mbox->wmsg_idx++; + + return 0; +} + +int emul_mbox_read(struct vipx_mailbox_ctrl *mctrl, void *payload, u32 type) +{ + int ret = 0; + struct vipx_mailbox_h2f *mbox; + u32 rmsg_idx; + u32 *rptr; + + BUG_ON(!mctrl); + BUG_ON(!payload); + + if (type == VIPX_MTYPE_H2F_NORMAL) { + mbox = &mctrl->stack->h2f; + } else if (type == VIPX_MTYPE_H2F_URGENT) { + mbox = &mctrl->urgent_stack->h2f; + } else { + vipx_err("invalid type(%d)\n", type); + ret = -EINVAL; + goto p_err; + } + rmsg_idx = mbox->rmsg_idx; + rptr = (void *)&mbox->msg[rmsg_idx % MAX_MESSAGE_CNT]; + + io2memcpy(payload, rptr, sizeof(vipx_msg_t)); + + mbox->rmsg_idx++; + +p_err: + return ret; +} + +int emul_mbox_handler(struct vipx_mailbox_ctrl *mctrl) +{ + int ret = 0; + int has_urgent = 0; + int has_normal = 0; + + vipx_msg_t payload; + + /* + * Handle urgent + */ + + /* read h2f mbox */ + ret = emul_mbox_check_message_from_host(mctrl, VIPX_MTYPE_H2F_URGENT); + if (ret) + goto p_normal; + + ret = emul_mbox_read(mctrl, &payload, VIPX_MTYPE_H2F_URGENT); + + /* handle message */ + + /* write f2h mbox */ + ret = emul_mbox_ready(mctrl, VIPX_MTYPE_F2H_URGENT); + if (ret) { + vipx_err("emul_mbox_ready is failed(%d)\n", ret); + goto p_normal; + } + + ret = emul_mbox_write(mctrl, &payload, sizeof(vipx_msg_t), VIPX_MTYPE_F2H_URGENT); + if (ret) { + vipx_err("emul_mbox_write is failed(%d)\n", ret); + goto p_normal; + } + + has_urgent = 1; + +p_normal: + /* + * Handle normal + */ + + /* read h2f mbox */ + ret = emul_mbox_check_message_from_host(mctrl, VIPX_MTYPE_H2F_NORMAL); + if (ret) + goto p_err; + + ret = emul_mbox_read(mctrl, &payload, VIPX_MTYPE_H2F_NORMAL); + + /* handle message */ + + /* write f2h mbox */ + ret = emul_mbox_ready(mctrl, VIPX_MTYPE_F2H_NORMAL); + if (ret) { + vipx_err("emul_mbox_ready is failed(%d)\n", ret); + goto p_err; + } + + ret = emul_mbox_write(mctrl, &payload, sizeof(vipx_msg_t), VIPX_MTYPE_F2H_NORMAL); + if (ret) { + vipx_err("emul_mbox_write is failed(%d)\n", ret); + goto p_err; + } + + has_normal = 1; + +p_err: + if (has_urgent || has_normal) { + vipx_info("________ %d %d exit\n", has_urgent, has_normal); + } + + return (has_urgent || has_normal) ? 0 : -1; +} diff --git a/drivers/vision/vipx/interface/hardware/vipx-interface.c b/drivers/vision/vipx/interface/hardware/vipx-interface.c new file mode 100755 index 000000000000..201dec78cc8c --- /dev/null +++ b/drivers/vision/vipx/interface/hardware/vipx-interface.c @@ -0,0 +1,1369 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "vipx-config.h" +#include "vipx-interface.h" +#include "vipx-memory.h" +#include "vipx-mailbox.h" +#include "vipx-io.h" +#include "../vipx-graphmgr.h" +#include "../vipx-device.h" + +#include + +#define TURN_AROUND_TIME (HZ/20) + +#define enter_request_barrier(task) mutex_lock(task->lock); +#define exit_request_barrier(task) mutex_unlock(task->lock); +#define init_process_barrier(itf) spin_lock_init(&itf->process_barrier); +#define enter_process_barrier(itf) spin_lock_irq(&itf->process_barrier); +#define exit_process_barrier(itf) spin_unlock_irq(&itf->process_barrier); + +#ifdef DBG_INTERFACE_ISR +#define DEBUG_ISR vipx_info +#else +#define DEBUG_ISR vipx_dbg +#endif + +/* #define NON_BLOCK_INVOKE */ + +atomic_t checker; + +int __get_size_from(struct vipx_format *format, unsigned int plane, int *width, int *height) +{ + int ret = 0; + + switch (format->format) { + case VS4L_DF_IMAGE_U8: + case VS4L_DF_IMAGE_U16: + case VS4L_DF_IMAGE_RGB: + case VS4L_DF_IMAGE_U32: + *width = format->width; + *height = format->height; + break; + case VS4L_DF_IMAGE_NV21: + if (plane == 0) { + *width = format->width; + *height = format->height; + } else if (plane == 1) { + *width = format->width; + *height = format->height / 2; + } else { + vipx_err("Invalid plane(%d) for NV21 format\n", plane); + ret = -1; + } + break; + case VS4L_DF_IMAGE_YV12: + case VS4L_DF_IMAGE_I420: + if (plane == 0) { + *width = format->width; + *height = format->height; + } else if (plane == 1) { + *width = format->width / 2; + *height = format->height / 2; + } else if (plane == 2) { + *width = format->width / 2; + *height = format->height / 2; + } else { + vipx_err("Invalid plane(%d) for YV12/I420 format\n", plane); + ret = -1; + } + break; + case VS4L_DF_IMAGE_I422: + if (plane == 0) { + *width = format->width; + *height = format->height; + } else if (plane == 1) { + *width = format->width / 2; + *height = format->height; + } else if (plane == 2) { + *width = format->width / 2; + *height = format->height; + } else { + vipx_err("Invalid plane(%d) for YV12/I422 format\n", plane); + ret = -1; + } + break; + default: + vipx_err("invalid format(%d)\n", format->format); + ret = -1; + break; + } + + return ret; +} + +static void WORK_TO_FREE(struct vipx_work_list *work_list, struct vipx_work *work) +{ + unsigned long flags; + + BUG_ON(!work_list); + BUG_ON(!work); + + spin_lock_irqsave(&work_list->slock, flags); + list_add_tail(&work->list, &work_list->free_head); + work_list->free_cnt++; + spin_unlock_irqrestore(&work_list->slock, flags); + + vipx_dbg("%s:\n", __func__); +} + +static void WORK_FR_FREE(struct vipx_work_list *work_list, struct vipx_work **work) +{ + unsigned long flags; + + BUG_ON(!work_list); + BUG_ON(!work); + + spin_lock_irqsave(&work_list->slock, flags); + if (work_list->free_cnt) { + *work = container_of(work_list->free_head.next, struct vipx_work, list); + list_del(&(*work)->list); + work_list->free_cnt--; + } else { + *work = NULL; + } + spin_unlock_irqrestore(&work_list->slock, flags); + + vipx_dbg("%s:\n", __func__); +} + +static void WORK_TO_REPLY(struct vipx_work_list *work_list, struct vipx_work *work) +{ + unsigned long flags; + + BUG_ON(!work_list); + BUG_ON(!work); + + spin_lock_irqsave(&work_list->slock, flags); + list_add_tail(&work->list, &work_list->reply_head); + work_list->reply_cnt++; + spin_unlock_irqrestore(&work_list->slock, flags); + + vipx_dbg("%s:\n", __func__); +} + +static void WORK_FR_REPLY(struct vipx_work_list *work_list, struct vipx_work **work) +{ + unsigned long flags; + + BUG_ON(!work_list); + BUG_ON(!work); + + spin_lock_irqsave(&work_list->slock, flags); + if (work_list->reply_cnt) { + *work = container_of(work_list->reply_head.next, struct vipx_work, list); + list_del(&(*work)->list); + work_list->reply_cnt--; + } else { + *work = NULL; + } + spin_unlock_irqrestore(&work_list->slock, flags); + + vipx_dbg("%s:\n", __func__); +} + +static void INIT_WORK_LIST(struct vipx_work_list *work_list, u32 count) +{ + u32 i; + + work_list->free_cnt = 0; + work_list->reply_cnt = 0; + INIT_LIST_HEAD(&work_list->free_head); + INIT_LIST_HEAD(&work_list->reply_head); + spin_lock_init(&work_list->slock); + init_waitqueue_head(&work_list->wait_queue); + for (i = 0; i < count; ++i) + WORK_TO_FREE(work_list, &work_list->work[i]); + + vipx_dbg("%s:\n", __func__); +} + +static inline void __set_reply(struct vipx_interface *interface, u32 graph_idx) +{ + BUG_ON(graph_idx >= VIPX_MAX_GRAPH); + interface->reply[graph_idx].valid = 1; + wake_up(&interface->reply_queue); + + vipx_dbg("%s:\n", __func__); +} + +static inline void __clr_reply(struct vipx_interface *interface, u32 graph_idx) +{ + BUG_ON(graph_idx >= VIPX_MAX_GRAPH); + interface->reply[graph_idx].valid = 0; + + vipx_dbg("%s:\n", __func__); +} + +static int __wait_reply(struct vipx_interface *interface, u32 graph_idx) +{ + int ret = 0; + int remain_time; + u32 cmd, type; + + BUG_ON(!interface); + BUG_ON(graph_idx >= VIPX_MAX_GRAPH); + BUG_ON(!interface->request[graph_idx]); + + cmd = interface->request[graph_idx]->message; + type = interface->request[graph_idx]->param3; + +#ifdef NON_BLOCK_INVOKE + remain_time = wait_event_timeout(interface->reply_queue, + interface->reply[graph_idx].valid, VIPX_COMMAND_TIMEOUT); +#else + remain_time = wait_event_timeout(interface->reply_queue, + interface->reply[graph_idx].valid, VIPX_COMMAND_TIMEOUT * 2); +#endif + if (!remain_time) { + vipx_err("[GID:%d] %d command[type: %d] : reply is timeout\n", graph_idx, cmd, type); + ret = -ETIME; + goto p_err; + } + +p_err: + + vipx_dbg("%s:%d\n", __func__, ret); + return ret; +} + +static void __send_interrupt(struct vipx_interface *interface) +{ +#ifndef CONFIG_EXYNOS_EMUL_MBOX + u32 try_count; + u32 type, offset, val; + + BUG_ON(!interface); + BUG_ON(!interface->process); + + type = interface->process->param3; + offset = (type == VIPX_MTYPE_H2F_NORMAL) ? 0x10 : 0x0C; + + + /* Check interrupt clear */ + try_count = 100; + val = readl(interface->regs + offset); + while (--try_count && val) { + vipx_warn("waiting interrupt clear(%d)...(%d)\n", val, try_count); + val = readl(interface->regs + offset); + } + + DEBUG_ISR("[DEBUG ISR] interrupt generate (%x)\n", offset); + + /* Raise interrupt */ + writel(0x100, interface->regs + offset); +#endif + + vipx_dbg("%s:\n", __func__); +} + +static int __vipx_set_cmd(struct vipx_interface *interface, + struct vipx_task *itask) +{ + int ret = 0; + struct vipx_taskmgr *itaskmgr; + struct vipx_mailbox_ctrl *mctrl; + void *payload; + u32 cmd, gidx, cid, size, type; + ulong flag; + + BUG_ON(!interface); + BUG_ON(!itask); + BUG_ON(!itask->lock); + BUG_ON(itask->param1 >= VIPX_MAX_GRAPH); + + itaskmgr = &interface->taskmgr; + mctrl = interface->private_data; + cmd = itask->message; + payload = (void *)itask->param0; + gidx = itask->param1; + size = itask->param2; + type = itask->param3; + cid = itask->index; + + enter_request_barrier(itask); + interface->request[gidx] = itask; + + enter_process_barrier(interface); + interface->process = itask; + + ret = vipx_mbox_ready(mctrl, size, type); + if (ret) { + interface->process = NULL; + exit_process_barrier(interface); + interface->request[gidx] = NULL; + exit_request_barrier(itask); + vipx_err("vipx_mbox_ready is fail(%d)", ret); + goto p_err; + } + + taskmgr_e_barrier_irqs(itaskmgr, 0, flag); + vipx_task_trans_req_to_pro(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flag); + + ret = vipx_mbox_write(mctrl, payload, size, type, gidx, cmd, cid); + if (ret) { + interface->process = NULL; + exit_process_barrier(interface); + interface->request[gidx] = NULL; + exit_request_barrier(itask); + pr_err("vipx_mbox_write_request fail (%d)\n", ret); + goto p_err; + } + + atomic_inc(&checker); + + __send_interrupt(interface); + + DEBUG_ISR("[I%ld][MBOX] CMD : %d, ID : %d\n", itask->param1, cmd, cid); + + interface->process = NULL; + exit_process_barrier(interface); + + ret = __wait_reply(interface, gidx); + if (ret) { + interface->request[gidx] = NULL; + exit_request_barrier(itask); + vipx_err("%d command is timeout", cmd); + ret = -ETIME; + goto p_err; + } + + if (interface->reply[gidx].work_param1) { + interface->request[gidx] = NULL; + exit_request_barrier(itask); + vipx_err("%d command is error(%d)\n", cmd, interface->reply[gidx].work_param1); + ret = interface->reply[gidx].work_param1; + goto p_err; + } + + __clr_reply(interface, gidx); + + interface->request[gidx] = NULL; + exit_request_barrier(itask); + +p_err: + + vipx_dbg("%s:%d\n", __func__, ret); + return ret; +} + +#ifdef NON_BLOCK_INVOKE +static int __vipx_set_cmd_nblk(struct vipx_interface *interface, + struct vipx_task *itask) +{ + int ret = 0; + struct vipx_taskmgr *itaskmgr; + struct vipx_mailbox_ctrl *mctrl; + void *payload; + u32 cmd, gidx, cid, size, type; + ulong flag; + + BUG_ON(!interface); + BUG_ON(!itask); + + itaskmgr = &interface->taskmgr; + mctrl = interface->private_data; + cmd = itask->message; + payload = (void *)itask->param0; + gidx = itask->param1; + size = itask->param2; + type = itask->param3; + cid = itask->index; + + enter_process_barrier(interface); + interface->process = itask; + + ret = vipx_mbox_ready(mctrl, size, type); + if (ret) { + interface->process = NULL; + exit_process_barrier(interface); + vipx_err("vipx_mbox_ready is fail(%d)", ret); + goto p_err; + } + + taskmgr_e_barrier_irqs(itaskmgr, 0, flag); + vipx_task_trans_req_to_pro(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flag); + + ret = vipx_mbox_write(mctrl, payload, size, type, gidx, cmd, cid); + if (ret) { + interface->process = NULL; + exit_process_barrier(interface); + pr_err("vipx_mbox_write_request fail (%d)\n", ret); + goto p_err; + } + + atomic_inc(&checker); + + __send_interrupt(interface); + + DEBUG_ISR("[I%ld][MBOX] CMD : %d, ID : %d\n", itask->param1, cmd, cid); + + interface->process = NULL; + exit_process_barrier(interface); + +p_err: + + vipx_dbg("%s:%d\n", __func__, ret); + return ret; +} +#endif + +static int __vipx_interface_cleanup(struct vipx_interface *interface) +{ + int ret = 0; + struct vipx_taskmgr *taskmgr; + struct vipx_task *task; + ulong flags; + u32 i; + + taskmgr = &interface->taskmgr; + + if (taskmgr->req_cnt) { + vipx_err("[ITF] request count is NOT zero(%d)\n", taskmgr->req_cnt); + ret += taskmgr->req_cnt; + } + + if (taskmgr->pro_cnt) { + vipx_err("[ITF] process count is NOT zero(%d)\n", taskmgr->pro_cnt); + ret += taskmgr->pro_cnt; + } + + if (taskmgr->com_cnt) { + vipx_err("[ITF] complete count is NOT zero(%d)\n", taskmgr->com_cnt); + ret += taskmgr->com_cnt; + } + + if (ret) { + taskmgr_e_barrier_irqs(taskmgr, 0, flags); + + for (i = 1; i < taskmgr->tot_cnt; ++i) { + task = &taskmgr->task[i]; + if (task->state == VIPX_TASK_STATE_FREE) + continue; + + vipx_task_trans_any_to_fre(taskmgr, task); + ret--; + } + + taskmgr_x_barrier_irqr(taskmgr, 0, flags); + } + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +void vipx_interface_print(struct vipx_interface *interface) +{ + DLOG_INIT(); + struct vipx_taskmgr *itaskmgr; + struct vipx_task *itask, *itemp; + + BUG_ON(!interface); + + itaskmgr = &interface->taskmgr; + + DLOG("REQUEST LIST(%d) :", itaskmgr->req_cnt); + list_for_each_entry_safe(itask, itemp, &itaskmgr->req_list, list) { + DLOG(" %d(%d, %d)", itask->index, itask->param1, itask->message); + } + vipx_info("%s\n", DLOG_OUT()); + + DLOG("PROCESS LIST(%d) :", itaskmgr->pro_cnt); + list_for_each_entry_safe(itask, itemp, &itaskmgr->pro_list, list) { + DLOG(" %d(%d, %d)", itask->index, itask->param1, itask->message); + } + vipx_info("%s\n", DLOG_OUT()); + + DLOG("COMPLETE LIST(%d) :", itaskmgr->com_cnt); + list_for_each_entry_safe(itask, itemp, &itaskmgr->com_list, list) { + DLOG(" %d(%d, %d)", itask->index, itask->param1, itask->message); + } + vipx_info("%s\n", DLOG_OUT()); +} + +static irqreturn_t interface_isr(int irq, void *data) +{ + int ret = 0; + struct vipx_interface *interface; + struct vipx_mailbox_ctrl *mctrl; + struct vipx_mailbox_f2h *mbox; + struct work_struct *work_queue; + struct vipx_work_list *work_list; + struct vipx_work *work; + vipx_msg_t msg_rsp; + + interface = (struct vipx_interface *)data; + mctrl = interface->private_data; + work_queue = &interface->work_queue; + work_list = &interface->work_list; + + mbox = &mctrl->urgent_stack->f2h; + DEBUG_ISR("[DEBUG_ISR] status of urgent mbox w(%d) r(%d)\n", mbox->wmsg_idx, mbox->rmsg_idx); + while (mbox->wmsg_idx != mbox->rmsg_idx) { + ret = vipx_mbox_read(mctrl, &msg_rsp, VIPX_MTYPE_H2F_URGENT, data); + if (ret) { + vipx_err("vipx_mbox_read is fail(%d)\n", ret); + break; + } + DEBUG_ISR("[DEBUG_ISR] read urgent mbox transid %d\n", msg_rsp.transId); + + if (msg_rsp.type == BOOTUP_RSP) { + if (msg_rsp.msg_u.bootup_rsp.error == 0) { + vipx_info("CM7 bootup complete %d\n", msg_rsp.transId); + set_bit(VIPX_ITF_STATE_BOOTUP, &interface->state); + } + break; + } + + atomic_dec(&checker); + + WORK_FR_FREE(work_list, &work); + if (work) { + /* TODO */ + work->id = msg_rsp.transId; + work->message = 0; + work->work_param0 = 0; + work->work_param1 = 0; + work->work_param2 = 0; + work->work_param3 = 0; + + WORK_TO_REPLY(work_list, work); + if (!work_pending(work_queue)) + schedule_work(work_queue); + } else { + vipx_err("free work is empty\n"); + break; + } + break; + } + + mbox = &mctrl->stack->f2h; + DEBUG_ISR("[DEBUG_ISR] status of normal mbox w(%d) r(%d)\n", mbox->wmsg_idx, mbox->rmsg_idx); + while (mbox->wmsg_idx != mbox->rmsg_idx) { + ret = vipx_mbox_read(mctrl, &msg_rsp, VIPX_MTYPE_H2F_NORMAL, data); + if (ret) { + vipx_err("vipx_mbox_read is fail(%d)\n", ret); + break; + } + DEBUG_ISR("[DEBUG_ISR] read normal mbox transid %d\n", msg_rsp.transId); + + atomic_dec(&checker); + + WORK_FR_FREE(work_list, &work); + if (work) { + /* TODO */ + work->id = msg_rsp.transId; + work->message = 0; + work->work_param0 = 0; + work->work_param1 = 0; + work->work_param2 = 0; + work->work_param3 = 0; + + WORK_TO_REPLY(work_list, work); + if (!work_pending(work_queue)) + schedule_work(work_queue); + } else { + vipx_err("free work is empty\n"); + break; + } + break; + } + + return IRQ_HANDLED; +} + +static irqreturn_t interface_isr0(int irq, void *data) +{ + struct vipx_interface *interface = (struct vipx_interface *)data; + u32 val; + + val = readl(interface->regs + 0x8); + if (val & 0x1) { + val &= ~(0x1); + writel(val, interface->regs + 0x8); + interface_isr(irq, data); + } + + return IRQ_HANDLED; +} + +static irqreturn_t interface_isr1(int irq, void *data) +{ + struct vipx_interface *interface = (struct vipx_interface *)data; + u32 val; + + val = readl(interface->regs + 0x8); + if (val & (0x1 << 0x1)) { + val &= ~(0x1 << 0x1); + writel(val, interface->regs + 0x8); + interface_isr(irq, data); + } + + return IRQ_HANDLED; +} + +#ifdef CONFIG_EXYNOS_EMUL_MBOX +static void interface_timer(unsigned long data) +{ + struct vipx_interface *interface = (struct vipx_interface *)data; + struct vipx_mailbox_ctrl *mctrl; + int ret = 0; + u32 random; + + mctrl = interface->private_data; + + if (!test_bit(VIPX_ITF_STATE_START, &interface->state)) + goto exit; + + ret = emul_mbox_handler(mctrl); + if (ret) + { + if (atomic_read(&checker) != 0) { + vipx_info("nothing to handle, checker %d\n", atomic_read(&checker)); + } + goto exit; + } + + interface_isr(0, (void *)data); + +exit: + get_random_bytes(&random, sizeof(random)); + random = random % TURN_AROUND_TIME; + mod_timer(&interface->timer, jiffies + random); +} +#endif + +static void vipx_wq_func(struct work_struct *data) +{ + struct vipx_interface *interface; + struct vipx_taskmgr *itaskmgr; + struct vipx_task *itask; + struct vipx_work_list *work_list; + struct vipx_work *work; + u32 graph_idx; + ulong flags; + + interface = container_of(data, struct vipx_interface, work_queue); + itaskmgr = &interface->taskmgr; + work_list = &interface->work_list; + + WORK_FR_REPLY(work_list, &work); + while (work) { + if (work->id >= VIPX_MAX_TASK) { + vipx_err("work id is invalid(%d)\n", work->id); + break; + } + itask = &itaskmgr->task[work->id]; + + if (itask->state != VIPX_TASK_STATE_PROCESS) { + vipx_err("task(%d, %d, %ld) state is invalid(%d), work(%d, %d, %d)\n", + itask->message, itask->index, itask->param1, itask->state, + work->id, work->work_param0, work->work_param1); + vipx_interface_print(interface); + BUG(); + } + + switch (itask->message) { + case VIPX_TASK_INIT: + case VIPX_TASK_DEINIT: + case VIPX_TASK_CREATE: + case VIPX_TASK_DESTROY: + case VIPX_TASK_ALLOCATE: + case VIPX_TASK_REQUEST: + graph_idx = itask->param1; + interface->reply[graph_idx] = *work; + DEBUG_ISR("[DEBUG_ISR] set reply graph idx(%d)\n", graph_idx); + __set_reply(interface, graph_idx); + break; + case VIPX_TASK_PROCESS: + DEBUG_ISR("[DEBUG_ISR] TASK PROCESS\n"); + +#ifdef NON_BLOCK_INVOKE +#else + graph_idx = itask->param1; + interface->reply[graph_idx] = *work; + __set_reply(interface, graph_idx); +#endif + + taskmgr_e_barrier_irqs(itaskmgr, 0, flags); + vipx_task_trans_pro_to_com(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flags); + + itask->param2 = 0; + itask->param3 = 0; + vipx_graphmgr_queue(interface->cookie, itask); + interface->done_cnt++; + break; + default: + vipx_err("unresolved message(%d) have arrived\n", work->message); + break; + } + + WORK_TO_FREE(work_list, work); + WORK_FR_REPLY(work_list, &work); + } +} + +int vipx_interface_probe(struct vipx_interface *interface, + struct device *dev, + void __iomem *regs, + resource_size_t regs_size, + u32 irq0, u32 irq1) +{ + int ret = 0; + struct vipx_device *device; + struct vipx_system *system; + struct vipx_taskmgr *taskmgr; + + BUG_ON(!interface); + BUG_ON(!dev); + BUG_ON(!regs); + + system = container_of(interface, struct vipx_system, interface); + device = container_of(system, struct vipx_device, system); + + init_process_barrier(interface); + init_waitqueue_head(&interface->reply_queue); + + interface->regs = regs; + interface->regs_size = regs_size; + interface->cookie = (void *)&device->graphmgr; + clear_bit(VIPX_ITF_STATE_OPEN, &interface->state); + clear_bit(VIPX_ITF_STATE_BOOTUP, &interface->state); + clear_bit(VIPX_ITF_STATE_ENUM, &interface->state); + clear_bit(VIPX_ITF_STATE_START, &interface->state); + interface->private_data = kmalloc(sizeof(struct vipx_mailbox_ctrl), GFP_KERNEL); + if (!interface->private_data) { + probe_err("kmalloc is fail\n"); + ret = -ENOMEM; + goto p_err; + } + + ret = devm_request_irq(dev, irq0, interface_isr0, 0, dev_name(dev), interface); + if (ret) { + probe_err("devm_request_irq(0) is fail(%d)\n", ret); + goto p_err; + } + + ret = devm_request_irq(dev, irq1, interface_isr1, 0, dev_name(dev), interface); + if (ret) { + probe_err("devm_request_irq(1) is fail(%d)\n", ret); + goto p_err; + } + + taskmgr = &interface->taskmgr; + taskmgr->id = VIPX_MAX_GRAPH; + taskmgr->sindex = 0; + spin_lock_init(&taskmgr->slock); + + ret = vipx_task_init(taskmgr, interface); + if (ret) { + probe_err("vipx_task_init is fail(%d)\n", ret); + goto p_err; + } + + ret = vipx_slab_init(&interface->slab); + if (ret) { + probe_err("vipx_slab_init is fail(%d)\n", ret); + goto p_err; + } + + INIT_WORK(&interface->work_queue, vipx_wq_func); + INIT_WORK_LIST(&interface->work_list, VIPX_WORK_MAX_COUNT); + +p_err: + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +int vipx_interface_open(struct vipx_interface *interface, + void *mbox, size_t mbox_size) +{ + struct vipx_mailbox_ctrl *mctrl; + int ret = 0; + u32 i; + + BUG_ON(!interface); + BUG_ON(!mbox); + + interface->mbox = mbox; + interface->mbox_size = mbox_size; + memset(interface->mbox, 0x0, interface->mbox_size); + + mctrl = interface->private_data; + mctrl->stack = vipx_mbox_g_stack(mbox, mbox_size); + mctrl->urgent_stack = vipx_mbox_g_urgent_stack(mbox, mbox_size); + + ret = vipx_graphmgr_itf_register(interface->cookie, interface); + if (ret) { + vipx_err("vipx_graphmgr_itf_register is fail(%d)\n", ret); + goto p_err; + } + + atomic_set(&checker, 0); + + interface->process = NULL; + for (i = 0; i < VIPX_MAX_GRAPH; ++i) { + interface->request[i] = NULL; + interface->reply[i].valid = 0; + } + + interface->done_cnt = 0; + set_bit(VIPX_ITF_STATE_OPEN, &interface->state); + clear_bit(VIPX_ITF_STATE_BOOTUP, &interface->state); + clear_bit(VIPX_ITF_STATE_ENUM, &interface->state); + clear_bit(VIPX_ITF_STATE_START, &interface->state); + +p_err: + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +int vipx_interface_close(struct vipx_interface *interface) +{ + int ret = 0; + + BUG_ON(!interface); + + ret = __vipx_interface_cleanup(interface); + if (ret) + vipx_err("__vipx_interface_cleanup is fail(%d)\n", ret); + + ret = vipx_graphmgr_itf_unregister(interface->cookie, interface); + if (ret) + vipx_err("vipx_graphmgr_itf_unregister is fail(%d)\n", ret); + + interface->mbox = 0; + interface->mbox_size = 0; + + clear_bit(VIPX_ITF_STATE_OPEN, &interface->state); + clear_bit(VIPX_ITF_STATE_BOOTUP, &interface->state); + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +int vipx_interface_start(struct vipx_interface *interface) +{ + int ret = 0; + + set_bit(VIPX_ITF_STATE_START, &interface->state); + +#ifdef CONFIG_EXYNOS_EMUL_MBOX + init_timer(&interface->timer); + interface->timer.expires = jiffies + TURN_AROUND_TIME; + interface->timer.data = (unsigned long)interface; + interface->timer.function = interface_timer; + add_timer(&interface->timer); +#endif + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +int vipx_interface_stop(struct vipx_interface *interface) +{ + int errcnt = 0; + struct vipx_taskmgr *itaskmgr; + u32 retry; + + itaskmgr = &interface->taskmgr; + + retry = VIPX_STOP_WAIT_COUNT; + while (--retry && itaskmgr->req_cnt) { + vipx_warn("waiting %d request completion...(%d)\n", itaskmgr->req_cnt, retry); + msleep(10); + } + + if (!retry) { + vipx_err("request completion is fail\n"); + vipx_interface_print(interface); + errcnt++; + } + + retry = VIPX_STOP_WAIT_COUNT; + while (--retry && itaskmgr->pro_cnt) { + vipx_warn("waiting %d process completion...(%d)\n", itaskmgr->pro_cnt, retry); + msleep(10); + } + + if (!retry) { + vipx_err("process completion is fail\n"); + vipx_interface_print(interface); + errcnt++; + } + + retry = VIPX_STOP_WAIT_COUNT; + while (--retry && itaskmgr->com_cnt) { + vipx_warn("waiting %d complete completion...(%d)\n", itaskmgr->com_cnt, retry); + msleep(10); + } + + if (!retry) { + vipx_err("complete completion is fail\n"); + vipx_interface_print(interface); + errcnt++; + } + +#ifdef CONFIG_EXYNOS_EMUL_MBOX + del_timer(&interface->timer); +#endif + clear_bit(VIPX_ITF_STATE_START, &interface->state); + + vipx_info("%s:\n", __func__); + return 0; +} + +int vipx_hw_wait_bootup(struct vipx_interface *interface) +{ + int ret = 0; + int try_cnt = 10; + struct vipx_system *system = container_of(interface, struct vipx_system, interface); + struct vipx_binary *binary = &system->binary; + + BUG_ON(!interface); + + while (try_cnt && !test_bit(VIPX_ITF_STATE_BOOTUP, &interface->state)) { + msleep(5); + try_cnt--; + vipx_info("%s(): wait CM7 bootup\n",__func__); + } + + vipx_info("debug kva(0x%p), dva(0x%x), size(%ld)\n", system->memory.info.kvaddr_debug, (u32)system->memory.info.dvaddr_debug, system->memory.info.pb_debug->size); + + if (try_cnt == 0) + { + if (!IS_ERR_OR_NULL(system->memory.info.kvaddr_debug)) { + ret = vipx_binary_write(binary, VIPX_FW_PATH1, "vipx_log.bin", + system->memory.info.kvaddr_debug, VIPX_DEBUG_SIZE); + if (ret) + vipx_err("vipx_binary_write is fail(%d)\n", ret); + } + ret = -EINVAL; + } + + vipx_info("%s():%d\n", __func__, ret); + return ret; +} + +int vipx_hw_enum(struct vipx_interface *interface) +{ + int ret = 0; + + BUG_ON(!interface); + + set_bit(VIPX_ITF_STATE_ENUM, &interface->state); + + vipx_info("%s():%d\n", __func__, ret); + return ret; +} + +/* Init Req */ +int vipx_hw_init(struct vipx_interface *interface, struct vipx_task *itask) +{ + int ret = 0; + struct vipx_taskmgr *itaskmgr; + ulong flag; + vipx_msg_t payload; + + BUG_ON(!interface); + BUG_ON(!itask); + + itaskmgr = &interface->taskmgr; + + payload.transId = itask->index; + payload.type = INIT_REQ; + + /* TODO */ + /* fill init_req */ +#if 0 + payload.msg_u.init_req.p_vip_core_bin = 0; + payload.msg_u.init_req.sz_vip_core_bin = 0; + payload.msg_u.init_req.p_cc_log = 0; + payload.msg_u.init_req.sz_cc_log = 0; +#endif + + payload.msg_u.init_req.p_cc_heap = 0; + payload.msg_u.init_req.sz_cc_heap = 0; + + itask->param0 = (ulong)&payload; + /* Do not update param1. param1 is graph_idx */ + /* itask->param1 = 0; */ + itask->param2 = sizeof(vipx_msg_t); + itask->param3 = VIPX_MTYPE_H2F_URGENT; + + ret = __vipx_set_cmd(interface, itask); + if (ret) { + vipx_err("__vipx_set_cmd is fail(%d)\n", ret); + goto p_err; + } + +p_err: + taskmgr_e_barrier_irqs(itaskmgr, 0, flag); + vipx_task_trans_any_to_fre(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flag); + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +/* Power doen Req */ +int vipx_hw_deinit(struct vipx_interface *interface, struct vipx_task *itask) +{ + int ret = 0; + struct vipx_taskmgr *itaskmgr; + ulong flag; + vipx_msg_t payload; + + BUG_ON(!interface); + BUG_ON(!itask); + + itaskmgr = &interface->taskmgr; + + /* TODO */ + payload.transId = itask->index; + payload.type = POWER_DOWN_REQ; + + /* fill powerdown_graph_req */ + payload.msg_u.powerdown_req.valid = 1; + + itask->param0 = (ulong)&payload; + /* Do not update param1. param1 is graph_idx */ + /* itask->param1 = 0; */ + itask->param2 = sizeof(vipx_msg_t); + itask->param3 = VIPX_MTYPE_H2F_URGENT; + + ret = __vipx_set_cmd(interface, itask); + if (ret) { + vipx_err("__vipx_set_cmd is fail(%d)\n", ret); + goto p_err; + } + +p_err: + taskmgr_e_barrier_irqs(itaskmgr, 0, flag); + vipx_task_trans_any_to_fre(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flag); + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +/* Create graph */ +int vipx_hw_create(struct vipx_interface *interface, struct vipx_task *itask) +{ + int ret = 0; + + struct vipx_taskmgr *itaskmgr; + ulong flag; + vipx_msg_t payload; + + BUG_ON(!interface); + BUG_ON(!itask); + + itaskmgr = &interface->taskmgr; + + /* TODO */ + payload.transId = itask->index; + payload.type = CREATE_GRAPH_REQ; + + /* fill create_graph_req */ + payload.msg_u.create_graph_req.p_graph = 0; + payload.msg_u.create_graph_req.sz_graph = 0; + + itask->param0 = (ulong)&payload; + /* Do not update param1. param1 is graph_idx */ + /* itask->param1 = 0; */ + itask->param2 = sizeof(vipx_msg_t); + itask->param3 = VIPX_MTYPE_H2F_NORMAL; + + ret = __vipx_set_cmd(interface, itask); + if (ret) { + vipx_err("__vipx_set_cmd is fail(%d)\n", ret); + goto p_err; + } + +p_err: + taskmgr_e_barrier_irqs(itaskmgr, 0, flag); + vipx_task_trans_any_to_fre(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flag); + + vipx_info("%s:%d\n", __func__, ret); + + return ret; +} + +/* Destroy graph */ +int vipx_hw_destroy(struct vipx_interface *interface, struct vipx_task *itask) +{ + int ret = 0; + struct vipx_taskmgr *itaskmgr; + struct vipx_system *system = container_of(interface, struct vipx_system, interface); + ulong flag; + vipx_msg_t payload; + u32 heap_size; + + BUG_ON(!interface); + BUG_ON(!itask); + + itaskmgr = &interface->taskmgr; + + /* TODO */ + payload.transId = itask->index; + payload.type = DESTROY_GRAPH_REQ; + + /* fill destroy_graph_req */ + payload.msg_u.destroy_graph_req.graph_id = itask->param0; + vipx_dbg("[%s] graph_id(%d)\n", __func__, payload.msg_u.destroy_graph_req.graph_id); + + itask->param0 = (ulong)&payload; + /* Do not update param1. param1 is graph_idx */ + /* itask->param1 = 0; */ + itask->param2 = sizeof(vipx_msg_t); + itask->param3 = VIPX_MTYPE_H2F_NORMAL; + + ret = __vipx_set_cmd(interface, itask); + if (ret) { + vipx_err("__vipx_set_cmd is fail(%d)\n", ret); + goto p_err; + } + + if (payload.msg_u.destroy_graph_req.graph_id < SCENARIO_DE_CAPTURE) + heap_size = VIPX_CM7_HEAP_SIZE_PREVIEW; + else if (payload.msg_u.destroy_graph_req.graph_id == SCENARIO_ENF) + heap_size = VIPX_CM7_HEAP_SIZE_ENF; + else + heap_size = VIPX_CM7_HEAP_SIZE_CAPTURE; + + vipx_free_heap(&system->memory, &system->binary, heap_size); + + vipx_info("checker %d\n", atomic_read(&checker)); + +p_err: + taskmgr_e_barrier_irqs(itaskmgr, 0, flag); + vipx_task_trans_any_to_fre(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flag); + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +int vipx_hw_config(struct vipx_interface *interface, struct vipx_task *itask) +{ + int ret = 0; + struct vipx_taskmgr *itaskmgr; + struct vipx_system *system = container_of(interface, struct vipx_system, interface); + ulong flag; + vipx_msg_t payload; + int i = 0, j = 0; + int num_inputs = 0; + int num_outputs = 0; + int cur_width = 0; + int cur_height = 0; + dma_addr_t dvaddr_heap = 0; + u32 heap_size; + + struct vipx_format_list *in_list; + struct vipx_format_list *out_list; + + BUG_ON(!interface); + BUG_ON(!itask); + + itaskmgr = &interface->taskmgr; + in_list = (void *)itask->param2; + out_list = (void *)itask->param3; + + payload.transId = itask->index; + payload.type = SET_GRAPH_REQ; + + /* fill set_graph_req */ + payload.msg_u.set_graph_req.graph_id = itask->param0; + vipx_dbg("[%s] graph_id(%d)\n", __func__, payload.msg_u.set_graph_req.graph_id); + + /* TODO : How to update depth field */ + for (i = 0; i < in_list->count; i++) { + vipx_info("in-buf[%d], fmt %d, plane %d, size: %dx%d\n", + i, in_list->formats[i].format, + in_list->formats[i].plane, + in_list->formats[i].width, + in_list->formats[i].height); + for (j = 0; j < in_list->formats[i].plane; j++) { + ret = __get_size_from(&in_list->formats[i], j, &cur_width, &cur_height); + if (ret) { + vipx_err("__get_size_from fail (%d)\n", ret); + goto p_err; + } + payload.msg_u.set_graph_req.input_width[num_inputs] = cur_width; + payload.msg_u.set_graph_req.input_height[num_inputs] = cur_height; + payload.msg_u.set_graph_req.input_depth[num_inputs] = 0; + vipx_info("input[%d] WxH(%dx%d), depth(%d)\n", num_inputs, + payload.msg_u.set_graph_req.input_width[num_inputs], + payload.msg_u.set_graph_req.input_height[num_inputs], + payload.msg_u.set_graph_req.input_depth[num_inputs] + ); + num_inputs++; + } + } + + for (i = 0; i < out_list->count; i++) { + vipx_info("out-buf[%d], fmt %d, plane %d, size: %dx%d\n", + i, out_list->formats[i].format, + out_list->formats[i].plane, + out_list->formats[i].width, + out_list->formats[i].height); + for (j = 0; j < out_list->formats[i].plane; j++) { + ret = __get_size_from(&out_list->formats[i], j, &cur_width, &cur_height); + if (ret) { + vipx_err("__get_size_from fail (%d)\n", ret); + goto p_err; + } + payload.msg_u.set_graph_req.output_width[num_outputs] = cur_width; + payload.msg_u.set_graph_req.output_height[num_outputs] = cur_height; + payload.msg_u.set_graph_req.output_depth[num_outputs] = 0; + vipx_info("output[%d] WxH(%dx%d), depth(%d)\n", num_outputs, + payload.msg_u.set_graph_req.output_width[num_outputs], + payload.msg_u.set_graph_req.output_height[num_outputs], + payload.msg_u.set_graph_req.output_depth[num_outputs] + ); + num_outputs++; + } + } + // Allocate and assign heap + if (payload.msg_u.set_graph_req.graph_id < SCENARIO_DE_CAPTURE) + heap_size = VIPX_CM7_HEAP_SIZE_PREVIEW; + else if (payload.msg_u.destroy_graph_req.graph_id == SCENARIO_ENF_UV || + payload.msg_u.destroy_graph_req.graph_id == SCENARIO_ENF) + heap_size = VIPX_CM7_HEAP_SIZE_ENF; + else + heap_size = VIPX_CM7_HEAP_SIZE_CAPTURE; + dvaddr_heap = vipx_allocate_heap(&system->memory, heap_size); + + payload.msg_u.set_graph_req.p_temp = dvaddr_heap; + payload.msg_u.set_graph_req.sz_temp = heap_size; + vipx_info("CC heap dva(0x%x), size(%d)\n", (u32)dvaddr_heap, heap_size); + + payload.msg_u.set_graph_req.num_inputs = num_inputs; + payload.msg_u.set_graph_req.num_outputs = num_outputs; + vipx_info("input cnt(%d), output cnt(%d)\n", num_inputs, num_outputs); + + itask->param0 = (ulong)&payload; + /* Do not update param1. param1 is graph_idx */ + /* itask->param1 = 0; */ + itask->param2 = sizeof(vipx_msg_t); + itask->param3 = VIPX_MTYPE_H2F_NORMAL; + + ret = __vipx_set_cmd(interface, itask); + if (ret) { + vipx_err("__vipx_set_cmd is fail(%d)\n", ret); + goto p_err; + } + +p_err: + taskmgr_e_barrier_irqs(itaskmgr, 0, flag); + vipx_task_trans_any_to_fre(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flag); + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +int vipx_hw_process(struct vipx_interface *interface, struct vipx_task *itask) +{ + int ret = 0; + struct vipx_taskmgr *itaskmgr; + ulong flag; + vipx_msg_t payload; + int i = 0, j = 0; + int num_inputs = 0; + int num_outputs = 0; + int num_user_params = 0; + + struct vb_container_list *incl; + struct vb_container_list *otcl; + + BUG_ON(!interface); + BUG_ON(!itask); + + itaskmgr = &interface->taskmgr; + incl = itask->incl; + otcl = itask->otcl; + + payload.transId = itask->index; + payload.type = INVOKE_GRAPH_REQ; + + /* use itask->incl and itask->otcl */ + payload.msg_u.invoke_graph_req.graph_id = itask->param0; + vipx_dbg("[%s] graph_id(%d)\n", __func__, payload.msg_u.invoke_graph_req.graph_id); + + /* TODO : We need to consider to support multi plain buffer */ + for (i = 0; i < incl->count; i++) { + for (j = 0; j < incl->containers[i].count; j++) { + payload.msg_u.invoke_graph_req.p_input[num_inputs] = incl->containers[i].buffers[j].dvaddr; + vipx_dbg("input[%d] buffer dvaddr(%x)\n", num_inputs, + payload.msg_u.invoke_graph_req.p_input[num_inputs]); + num_inputs++; + } + } + for (i = 0; i < otcl->count; i++) { + for (j = 0; j < otcl->containers[i].count; j++) { + payload.msg_u.invoke_graph_req.p_output[num_outputs] = otcl->containers[i].buffers[j].dvaddr; + vipx_dbg("output[%d] buffer dvaddr(%x)\n", num_outputs, + payload.msg_u.invoke_graph_req.p_output[num_outputs]); + num_outputs++; + } + } + for (num_user_params = 0; num_user_params < MAX_NUM_OF_USER_PARAMS; num_user_params++) { + payload.msg_u.invoke_graph_req.user_params[num_user_params] = incl->user_params[num_user_params]; + vipx_dbg("user_params[%d] = (%d)\n", num_user_params, + payload.msg_u.invoke_graph_req.user_params[num_user_params]); + } + + payload.msg_u.invoke_graph_req.num_inputs = num_inputs; + payload.msg_u.invoke_graph_req.num_outputs = num_outputs; + vipx_dbg("input cnt(%d), output cnt(%d)\n", num_inputs, num_outputs); + + itask->param0 = (ulong)&payload; + /* Do not update param1. param1 is graph_idx */ + /* itask->param1 = 0; */ + itask->param2 = sizeof(vipx_msg_t); + itask->param3 = VIPX_MTYPE_H2F_NORMAL; + +#ifdef NON_BLOCK_INVOKE + ret = __vipx_set_cmd_nblk(interface, itask); + if (ret) { + vipx_err("__vipx_set_cmd_nblk is fail(%d)\n", ret); + goto p_err; + } +#else + ret = __vipx_set_cmd(interface, itask); + if (ret) { + vipx_err("__vipx_set_cmd is fail(%d)\n", ret); + goto p_err; + } +#endif + + vipx_dbg("%s:%d\n", __func__, ret); + return 0; + +p_err: + taskmgr_e_barrier_irqs(itaskmgr, 0, flag); + vipx_task_trans_any_to_fre(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flag); + + return ret; +} diff --git a/drivers/vision/vipx/interface/hardware/vipx-mailbox.c b/drivers/vision/vipx/interface/hardware/vipx-mailbox.c new file mode 100644 index 000000000000..a7f7566a89e2 --- /dev/null +++ b/drivers/vision/vipx/interface/hardware/vipx-mailbox.c @@ -0,0 +1,187 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "vipx-config.h" +#include "vipx-io.h" +#include "vipx-mailbox.h" +#include "vipx-interface.h" + +#include + +#define INDEX(x) (x % MAX_MESSAGE_CNT) + +struct vipx_mailbox_stack * vipx_mbox_g_stack(void *mbox, u32 mbox_size) +{ + vipx_info("sizeof mbox stack (%lx) address of normal mbox (%p)\n", + sizeof(struct vipx_mailbox_stack), mbox); + return (struct vipx_mailbox_stack *)mbox; +} + +struct vipx_mailbox_stack * vipx_mbox_g_urgent_stack(void *mbox, u32 mbox_size) +{ + vipx_info("address of urgent mbox (%p)\n", ((char *)mbox + sizeof(struct vipx_mailbox_stack))); + return (struct vipx_mailbox_stack *)((char *)mbox + sizeof(struct vipx_mailbox_stack)); +} + +static u32 __vipx_mbox_g_freesize(struct vipx_mailbox_h2f *mbox) +{ + u32 wmsg_idx = 0; + u32 rmsg_idx = 0; + u32 free_size = 0; + + BUG_ON(!mbox); + + wmsg_idx = mbox->wmsg_idx; + rmsg_idx = mbox->rmsg_idx; + + free_size = MAX_MESSAGE_CNT - (wmsg_idx - rmsg_idx); + + BUG_ON(free_size < 0); + + return free_size; +} + +int vipx_mbox_ready(struct vipx_mailbox_ctrl *mctrl, + size_t size, u32 type) +{ + u32 try_count; + struct vipx_mailbox_h2f *mbox; + u16 free_size16; + + BUG_ON(!mctrl); + BUG_ON(!IS_ALIGNED(size, 2)); + + if (type == VIPX_MTYPE_H2F_NORMAL) { + mbox = &mctrl->stack->h2f; + } else if (type == VIPX_MTYPE_H2F_URGENT) { + mbox = &mctrl->urgent_stack->h2f; + } else { + vipx_err("invalid type(%d)\n", type); + return -EINVAL; + } + try_count = 1000; + + free_size16 = __vipx_mbox_g_freesize(mbox); + while (--try_count && (free_size16 == 0)) { + vipx_warn("mbox is not ready(freesize %d)...(%d)\n", + free_size16, try_count); + udelay(10); + free_size16 = __vipx_mbox_g_freesize(mbox); + } + + if (try_count) + return 0; + else + return -EBUSY; +} + +int vipx_mbox_wait_reply(struct vipx_mailbox_ctrl *mctrl, + u32 cmd, u32 type) +{ + int ret = 0; + struct vipx_mailbox_f2h *mbox; + int try_count; + + if (type == VIPX_MTYPE_F2H_NORMAL) { + mbox = &mctrl->stack->f2h; + } else if (type == VIPX_MTYPE_F2H_URGENT) { + mbox = &mctrl->urgent_stack->f2h; + } else { + vipx_err("cmd(%d) has invalid type(%d)\n", cmd, type); + ret = -EINVAL; + goto p_err; + } + + try_count = 1000; + while (--try_count && (mbox->wmsg_idx == mbox->rmsg_idx)) { + vipx_warn("waiting vipx reply(%d, %d)...(%d)\n", mbox->wmsg_idx, mbox->rmsg_idx, try_count); + msleep(10); + } + + if (try_count <= 0) { + vipx_err("waiting vipx reply is timeout\n"); + ret = -EINVAL; + goto p_err; + } + +p_err: + return ret; +} + +int vipx_mbox_write(struct vipx_mailbox_ctrl *mctrl, + void *payload, size_t size, u32 type, u32 gid, u32 cmd, u32 cid) +{ + int ret = 0; + struct vipx_mailbox_h2f *mbox; + u32 *wptr; + + BUG_ON(!mctrl); + BUG_ON(!payload); + BUG_ON(!IS_ALIGNED(size, 2)); + + if (type == VIPX_MTYPE_H2F_NORMAL) { + mbox = &mctrl->stack->h2f; + } else if (type == VIPX_MTYPE_H2F_URGENT) { + mbox = &mctrl->urgent_stack->h2f; + } else { + vipx_err("invalid type(%d)\n", type); + ret = -EINVAL; + goto p_err; + } + wptr = (void *)&mbox->msg[mbox->wmsg_idx % MAX_MESSAGE_CNT]; + + mem2iocpy(wptr, payload, size); + + /* increment actual write pointer */ + mbox->wmsg_idx++; + +#ifdef DBG_INTERFACE_ISR + vipx_info("[I%d][MBOX][H2F] %d command(type: %d, cid: %d, wmsg_idx: %d, rmsg_idx: %d)\n", gid, + cmd, type, cid, mbox->wmsg_idx, mbox->rmsg_idx); +#endif + +p_err: + return ret; +} + +int vipx_mbox_read(struct vipx_mailbox_ctrl *mctrl, + void *payload, u32 type, void *debug_data) +{ + int ret = 0; + struct vipx_mailbox_f2h *mbox; + u32 *rptr; + + BUG_ON(!mctrl); + BUG_ON(!payload); + + if (type == VIPX_MTYPE_H2F_NORMAL) { + mbox = &mctrl->stack->f2h; + } else if (type == VIPX_MTYPE_H2F_URGENT) { + mbox = &mctrl->urgent_stack->f2h; + } else { + vipx_err("invalid type(%d)\n", type); + ret = -EINVAL; + goto p_err; + } + rptr = (void *)&mbox->msg[mbox->rmsg_idx % MAX_MESSAGE_CNT]; + + io2memcpy(payload, rptr, sizeof(vipx_msg_t)); + + /* increment actual read pointer */ + mbox->rmsg_idx++; + +p_err: + return ret; +} diff --git a/drivers/vision/vipx/interface/vipx-slab.c b/drivers/vision/vipx/interface/vipx-slab.c new file mode 100644 index 000000000000..708127b7224e --- /dev/null +++ b/drivers/vision/vipx/interface/vipx-slab.c @@ -0,0 +1,86 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "vipx-config.h" +#include "vipx-slab.h" + +int vipx_slab_init(struct vipx_slab_allocator *allocator) +{ + int ret = 0, i; + char name[100]; + size_t size; + + allocator->cache_size[0] = sizeof(vipx_msg_t); + + for (i = 0; i < VIPX_SLAB_MAX_LEVEL; ++i) { + size = allocator->cache_size[i]; + snprintf(name, sizeof(name), "vipx=size-%zd", size); + + allocator->cache[i] = kmem_cache_create(name, size, + ARCH_KMALLOC_MINALIGN, SLAB_POISON | SLAB_PANIC, NULL); + if (!allocator->cache[i]) { + probe_err("kmem_cache_create(%zd) is fail\n", size); + ret = -ENOMEM; + goto p_err; + } + } + +p_err: + return ret; +} + +int vipx_slab_alloc(struct vipx_slab_allocator *allocator, void **target, size_t size) +{ + int ret = 0, i; + + for (i = 0; i < VIPX_SLAB_MAX_LEVEL; ++i) { + if (size <= allocator->cache_size[i]) + break; + } + + if (i >= VIPX_SLAB_MAX_LEVEL) { + vipx_err("alloc size is invalid(%zd)\n", size); + ret= -EINVAL; + goto p_err; + } + + *target = kmem_cache_alloc(allocator->cache[i], GFP_KERNEL); + if (!(*target)) { + vipx_err("kmem_cache_alloc is fail\n"); + ret = -ENOMEM; + goto p_err; + } + +p_err: + return ret; +} + +int vipx_slab_free(struct vipx_slab_allocator *allocator, void *target, size_t size) +{ + int ret = 0, i; + + for (i = 0; i < VIPX_SLAB_MAX_LEVEL; ++i) { + if (size <= allocator->cache_size[i]) + break; + } + + if (i >= VIPX_SLAB_MAX_LEVEL) { + vipx_err("alloc size is invalid(%zd)\n", size); + BUG(); + } + + kmem_cache_free(allocator->cache[i], target); + + return ret; +} diff --git a/drivers/vision/vipx/platform/Kconfig b/drivers/vision/vipx/platform/Kconfig new file mode 100644 index 000000000000..741a41a1c3e4 --- /dev/null +++ b/drivers/vision/vipx/platform/Kconfig @@ -0,0 +1,10 @@ +config EXYNOS_VIPX_PLATFORM + bool "Select Platform" + help + This is a selection of platform + +config EXYNOS_VIPX_EXYNOS9610 + bool "Use Exynos9610 platform" + depends on EXYNOS_VIPX_PLATFORM + help + This is a exynos9610 platform diff --git a/drivers/vision/vipx/platform/Makefile b/drivers/vision/vipx/platform/Makefile new file mode 100644 index 000000000000..8a4e067425b8 --- /dev/null +++ b/drivers/vision/vipx/platform/Makefile @@ -0,0 +1,4 @@ +obj-y += vipx-exynos.o +obj-$(CONFIG_EXYNOS_VIPX_EXYNOS9610) += exynos9610/ + +EXTRA_CFLAGS += -Idrivers/vision/vipx/include diff --git a/drivers/vision/vipx/platform/exynos9610/Makefile b/drivers/vision/vipx/platform/exynos9610/Makefile new file mode 100644 index 000000000000..d51691a9e4c9 --- /dev/null +++ b/drivers/vision/vipx/platform/exynos9610/Makefile @@ -0,0 +1,3 @@ +obj-y += vipx-exynos9610.o + +EXTRA_CFLAGS += -Idrivers/vision/vipx/include diff --git a/drivers/vision/vipx/platform/exynos9610/vipx-exynos9610.c b/drivers/vision/vipx/platform/exynos9610/vipx-exynos9610.c new file mode 100755 index 000000000000..2024f45c51bd --- /dev/null +++ b/drivers/vision/vipx/platform/exynos9610/vipx-exynos9610.c @@ -0,0 +1,215 @@ +/* + * Samsung Exynos SoC series VIPX driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "vipx-config.h" +#include "vipx-exynos.h" +#include "vipx-exynos9610.h" +#include "vipx-debug.h" + +#define CLK_INDEX(name) VIPX_##name +#define REGISTER_CLK(name) [CLK_INDEX(name)] = {#name, NULL} + +extern int vipx_clk_set_rate(struct device *dev, u32 index, ulong frequency); +extern ulong vipx_clk_get_rate(struct device *dev, u32 index); +extern int vipx_clk_enable(struct device *dev, u32 index); +extern int vipx_clk_disable(struct device *dev, u32 index); + +enum vipx_clk_index { + CLK_INDEX(UMUX_CLKCMU_VIPX1_BUS), + CLK_INDEX(GATE_VIPX1_QCH), + CLK_INDEX(UMUX_CLKCMU_VIPX2_BUS), + CLK_INDEX(GATE_VIPX2_QCH), + CLK_INDEX(GATE_VIPX2_QCH_LOCAL) +}; + +struct vipx_clk vipx_clk_array[] = { + REGISTER_CLK(UMUX_CLKCMU_VIPX1_BUS), + REGISTER_CLK(GATE_VIPX1_QCH), + REGISTER_CLK(UMUX_CLKCMU_VIPX2_BUS), + REGISTER_CLK(GATE_VIPX2_QCH), + REGISTER_CLK(GATE_VIPX2_QCH_LOCAL) +}; + +const u32 vipx_clk_array_size = ARRAY_SIZE(vipx_clk_array); + +int vipx_exynos_clk_cfg(struct vipx_exynos *exynos) +{ + return 0; +} + +int vipx_exynos_clk_on(struct vipx_exynos *exynos) +{ + vipx_clk_enable(exynos->dev, CLK_INDEX(UMUX_CLKCMU_VIPX1_BUS)); + vipx_clk_get_rate(exynos->dev, CLK_INDEX(UMUX_CLKCMU_VIPX1_BUS)); + + vipx_clk_enable(exynos->dev, CLK_INDEX(GATE_VIPX1_QCH)); + vipx_clk_get_rate(exynos->dev, CLK_INDEX(GATE_VIPX1_QCH)); + + vipx_clk_enable(exynos->dev, CLK_INDEX(UMUX_CLKCMU_VIPX2_BUS)); + vipx_clk_get_rate(exynos->dev, CLK_INDEX(UMUX_CLKCMU_VIPX2_BUS)); + + vipx_clk_enable(exynos->dev, CLK_INDEX(GATE_VIPX2_QCH)); + vipx_clk_get_rate(exynos->dev, CLK_INDEX(GATE_VIPX2_QCH)); + + vipx_clk_enable(exynos->dev, CLK_INDEX(GATE_VIPX2_QCH_LOCAL)); + vipx_clk_get_rate(exynos->dev, CLK_INDEX(GATE_VIPX2_QCH_LOCAL)); + + vipx_info("[%s]\n", __func__); + return 0; +} + +int vipx_exynos_clk_off(struct vipx_exynos *exynos) +{ + vipx_clk_disable(exynos->dev, CLK_INDEX(UMUX_CLKCMU_VIPX1_BUS)); + vipx_clk_disable(exynos->dev, CLK_INDEX(GATE_VIPX1_QCH)); + vipx_clk_disable(exynos->dev, CLK_INDEX(UMUX_CLKCMU_VIPX2_BUS)); + vipx_clk_disable(exynos->dev, CLK_INDEX(GATE_VIPX2_QCH)); + vipx_clk_disable(exynos->dev, CLK_INDEX(GATE_VIPX2_QCH_LOCAL)); + + vipx_info("[%s]\n", __func__); + return 0; +} + +int vipx_exynos_clk_dump(struct vipx_exynos *exynos) +{ + int ret = 0; + + return ret; +} + +int dummy_vipx_exynos_clk_cfg(struct vipx_exynos *exynos) { return 0; } +int dummy_vipx_exynos_clk_on(struct vipx_exynos *exynos) { return 0; } +int dummy_vipx_exynos_clk_off(struct vipx_exynos *exynos) { return 0; } +int dummy_vipx_exynos_clk_dump(struct vipx_exynos *exynos) { return 0; } + +const struct vipx_clk_ops vipx_clk_ops = { +#ifdef CONFIG_EXYNOS_VIPX_HARDWARE + .clk_cfg = vipx_exynos_clk_cfg, + .clk_on = vipx_exynos_clk_on, + .clk_off = vipx_exynos_clk_off, + .clk_dump = vipx_exynos_clk_dump +#else + .clk_cfg = dummy_vipx_exynos_clk_cfg, + .clk_on = dummy_vipx_exynos_clk_on, + .clk_off = dummy_vipx_exynos_clk_off, + .clk_dump = dummy_vipx_exynos_clk_dump +#endif +}; + +int vipx_exynos_ctl_reset(struct vipx_exynos *exynos, bool hold) +{ + u32 val; + + vipx_readl(exynos->regbase[VIPX_REG_CPU_SS1], &vipx_regs_cpu_ss1[VIPX_CPU_SS1_GLOBAL_CTRL], &val); + val = val | 0xF1; + vipx_writel(exynos->regbase[VIPX_REG_CPU_SS1], &vipx_regs_cpu_ss1[VIPX_CPU_SS1_GLOBAL_CTRL], val); + mdelay(1); + + vipx_readl(exynos->regbase[VIPX_REG_CPU_SS2], &vipx_regs_cpu_ss2[VIPX_CPU_SS2_GLOBAL_CTRL], &val); + val = val | 0xF1; + vipx_writel(exynos->regbase[VIPX_REG_CPU_SS2], &vipx_regs_cpu_ss2[VIPX_CPU_SS2_GLOBAL_CTRL], val); + mdelay(1); + + vipx_readl(exynos->regbase[VIPX_REG_CPU_SS1], &vipx_regs_cpu_ss1[VIPX_CPU_SS1_CPU_CTRL], &val); + val = val | 0x1; + vipx_writel(exynos->regbase[VIPX_REG_CPU_SS1], &vipx_regs_cpu_ss1[VIPX_CPU_SS1_CPU_CTRL], val); + mdelay(1); + + vipx_readl(exynos->regbase[VIPX_REG_CPU_SS1], &vipx_regs_cpu_ss1[VIPX_CPU_SS1_CORTEX_CONTROL], &val); + val = val | 0x1; + vipx_writel(exynos->regbase[VIPX_REG_CPU_SS1], &vipx_regs_cpu_ss1[VIPX_CPU_SS1_CORTEX_CONTROL], val); + mdelay(1); + + vipx_writel(exynos->regbase[VIPX_REG_CPU_SS1], &vipx_regs_cpu_ss1[VIPX_CPU_SS1_CPU_CTRL], 0x0); + mdelay(1); + + /* QACTIVE */ + vipx_readl(exynos->regbase[VIPX_REG_CPU_SS1], &vipx_regs_cpu_ss1[VIPX_CPU_SS1_QCHANNEL], &val); + val = val | 0x1; + vipx_writel(exynos->regbase[VIPX_REG_CPU_SS1], &vipx_regs_cpu_ss1[VIPX_CPU_SS1_QCHANNEL], val); + mdelay(1); + + vipx_readl(exynos->regbase[VIPX_REG_CPU_SS2], &vipx_regs_cpu_ss2[VIPX_CPU_SS2_QCHANNEL], &val); + val = val | 0x1; + vipx_writel(exynos->regbase[VIPX_REG_CPU_SS2], &vipx_regs_cpu_ss2[VIPX_CPU_SS2_QCHANNEL], val); + mdelay(1); + + vipx_info("hold(%d)\n", hold); + return 0; +} + +int vipx_exynos_ctl_dump(struct vipx_exynos *exynos, u32 instance) +{ + + vipx_info("instance(%d)\n", instance); + return 0; +} + +void *vipx_exynos_ctl_remap(struct vipx_exynos *exynos, u32 instance) +{ + BUG_ON(!exynos); + BUG_ON(instance >= VIPX_REG_MAX_CNT); + + vipx_info("instance(%d)\n", instance); + return exynos->regbase[instance]; +} + +int vipx_exynos_ctl_unmap(struct vipx_exynos *exynos, u32 instance, void *base) +{ + BUG_ON(!exynos); + BUG_ON(!base); + BUG_ON(instance >= VIPX_REG_MAX_CNT); + + vipx_info("instance(%d)\n", instance); + return 0; +} + +int vipx_exynos_ctl_trigger(struct vipx_exynos *exynos, u32 chain_id) +{ + + vipx_info("chain_id(%d)\n", chain_id); + return 0; +} + +int vipx_exynos_ctl_start(struct vipx_exynos *exynos, u32 chain_id) +{ + vipx_writel(exynos->regbase[VIPX_REG_CPU_SS1], &vipx_regs_cpu_ss1[VIPX_CPU_SS1_CORTEX_CONTROL], 0x0); + + vipx_info("chain_id(%d) %x %x\n", chain_id,exynos->regbase[VIPX_REG_CPU_SS1],exynos->regbase[VIPX_REG_CPU_SS2]); + return 0; +} + +int dummy_vipx_exynos_ctl_reset(struct vipx_exynos *exynos, bool hold) { return 0; } +int dummy_vipx_exynos_ctl_dump(struct vipx_exynos *exynos, u32 instance) { return 0; } +void *dummy_vipx_exynos_ctl_remap(struct vipx_exynos *exynos, u32 instance) { return NULL; } +int dummy_vipx_exynos_ctl_unmap(struct vipx_exynos *exynos, u32 instance, void *base) { return 0; } +int dummy_vipx_exynos_ctl_trigger(struct vipx_exynos *exynos, u32 chain_id) { return 0; } +int dummy_vipx_exynos_ctl_start(struct vipx_exynos *exynos, u32 chain_id) { return 0; } + +const struct vipx_ctl_ops vipx_ctl_ops = { +#ifdef CONFIG_EXYNOS_VIPX_HARDWARE + .ctl_reset = vipx_exynos_ctl_reset, + .ctl_dump = vipx_exynos_ctl_dump, + .ctl_remap = vipx_exynos_ctl_remap, + .ctl_unmap = vipx_exynos_ctl_unmap, + .ctl_trigger = vipx_exynos_ctl_trigger, + .ctl_start = vipx_exynos_ctl_start, +#else + .ctl_reset = dummy_vipx_exynos_ctl_reset, + .ctl_dump = dummy_vipx_exynos_ctl_dump, + .ctl_remap = dummy_vipx_exynos_ctl_remap, + .ctl_unmap = dummy_vipx_exynos_ctl_unmap, + .ctl_trigger = dummy_vipx_exynos_ctl_trigger, + .ctl_start = dummy_vipx_exynos_ctl_start, +#endif +}; diff --git a/drivers/vision/vipx/platform/exynos9610/vipx-exynos9610.h b/drivers/vision/vipx/platform/exynos9610/vipx-exynos9610.h new file mode 100644 index 000000000000..466e44d7479c --- /dev/null +++ b/drivers/vision/vipx/platform/exynos9610/vipx-exynos9610.h @@ -0,0 +1,46 @@ +/* + * Samsung Exynos SoC series VIPX driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef VIPX_EXYNOS9610_H_ +#define VIPX_EXYNOS9610_H_ + +enum vipx_reg_cpu_ss1_name { + VIPX_CPU_SS1_VERSION_ID, + VIPX_CPU_SS1_QCHANNEL, + VIPX_CPU_SS1_IRQ_VIP_TO_HOST, + VIPX_CPU_SS1_IRQ0_HOST_TO_VIP, + VIPX_CPU_SS1_IRQ1_HOST_TO_VIP, + VIPX_CPU_SS1_GLOBAL_CTRL, + VIPX_CPU_SS1_CORTEX_CONTROL, + VIPX_CPU_SS1_CPU_CTRL, +}; + +enum vipx_reg_cpu_ss2_name { + VIPX_CPU_SS2_QCHANNEL, + VIPX_CPU_SS2_GLOBAL_CTRL, +}; + +struct vipx_reg vipx_regs_cpu_ss1[] = { + {0x00000, "VERSION_ID"}, + {0x00004, "SS1_QCHANNEL"}, + {0x00008, "SS1_IRQ_VIP_TO_HOST"}, + {0x0000C, "SS1_IRQ0_HOST_TO_VIP"}, + {0x00010, "SS1_IRQ1_HOST_TO_VIP"}, + {0x00014, "SS1_GLOBAL_CTRL"}, + {0x00018, "SS1_CORTEX_CONTROL"}, + {0x0001C, "SS1_CPU_CTRL"}, +}; + +struct vipx_reg vipx_regs_cpu_ss2[] = { + {0x00000, "SS2_QCHANNEL"}, + {0x00004, "SS2_GLOBAL_CTRL"}, +}; + +#endif diff --git a/drivers/vision/vipx/platform/vipx-exynos.c b/drivers/vision/vipx/platform/vipx-exynos.c new file mode 100644 index 000000000000..a4d183b8d986 --- /dev/null +++ b/drivers/vision/vipx/platform/vipx-exynos.c @@ -0,0 +1,216 @@ +#include +#include + +#include "vipx-config.h" +#include "vipx-exynos.h" + +extern struct vipx_clk vipx_clk_array[]; +extern const u32 vipx_clk_array_size; +extern const struct vipx_clk_ops vipx_clk_ops; +extern const struct vipx_ctl_ops vipx_ctl_ops; + +int vipx_clk_set_rate(struct device *dev, u32 index, ulong frequency) +{ + int ret = 0; + struct clk *clk; + + if (index >= vipx_clk_array_size) { + vipx_err("index is invalid(%d >= %d)\n", index, vipx_clk_array_size); + ret = -EINVAL; + goto p_err; + } + + clk= vipx_clk_array[index].clk; + if (IS_ERR_OR_NULL(clk)) { + vipx_err("clk is NULL(%d)\n", index); + ret = -EINVAL; + goto p_err; + } + + ret = clk_set_rate(clk, frequency); + if (ret) { + vipx_err("clk_set_rate is fail(%d)\n", ret); + goto p_err; + } + +p_err: + return ret; +} + +ulong vipx_clk_get_rate(struct device *dev, u32 index) +{ + ulong frequency; + struct clk *clk; + + if (index >= vipx_clk_array_size) { + vipx_err("index is invalid(%d >= %d)\n", index, vipx_clk_array_size); + frequency = -EINVAL; + goto p_err; + } + + clk = vipx_clk_array[index].clk; + if (IS_ERR_OR_NULL(clk)) { + vipx_err("clk is NULL(%d)\n", index); + frequency = -EINVAL; + goto p_err; + } + + frequency = clk_get_rate(clk); + +p_err: + vipx_info("%s : %ldMhz\n", vipx_clk_array[index].name, frequency/1000000); + return frequency; +} + +int vipx_clk_enable(struct device *dev, u32 index) +{ + int ret = 0; + struct clk *clk; + + if (index >= vipx_clk_array_size) { + vipx_err("index is invalid(%d >= %d)\n", index, vipx_clk_array_size); + ret = -EINVAL; + goto p_err; + } + + clk = vipx_clk_array[index].clk; + if (IS_ERR_OR_NULL(clk)) { + vipx_err("clk is NULL(%d)\n", index); + ret = -EINVAL; + goto p_err; + } + + ret = clk_prepare_enable(clk); + if (ret) { + vipx_err("clk_prepare_enable is fail(%s)\n", vipx_clk_array[index].name); + goto p_err; + } + +p_err: + return ret; +} + +int vipx_clk_disable(struct device *dev, u32 index) +{ + int ret = 0; + struct clk *clk; + + if (index >= vipx_clk_array_size) { + vipx_err("index is invalid(%d >= %d)\n", index, vipx_clk_array_size); + ret = -EINVAL; + goto p_err; + } + + clk = vipx_clk_array[index].clk; + if (IS_ERR_OR_NULL(clk)) { + vipx_err("clk is NULL(%d)\n", index); + ret = -EINVAL; + goto p_err; + } + + clk_disable_unprepare(clk); + +p_err: + return ret; +} + +static int vipx_exynos_clk_init(struct device *dev) +{ + int ret = 0; + const char *name; + struct clk *clk; + u32 index; + + for (index = 0; index < vipx_clk_array_size; ++index) { + name = vipx_clk_array[index].name; + if (!name) { + probe_err("name is NULL\n"); + ret = -EINVAL; + break; + } + + clk = clk_get(dev, name); + if (IS_ERR_OR_NULL(clk)) { + probe_err("%s clk is not found\n", name); + ret = -EINVAL; + break; + } + + vipx_clk_array[index].clk = clk; + } + + return ret; +} + +int vipx_exynos_probe(struct vipx_exynos *exynos, struct device *dev, + void **regs, void *ram0, void *ram1) +{ + int ret = 0; + + BUG_ON(!exynos); + BUG_ON(!dev); + + exynos->regbase = regs; + exynos->ram0base = ram0; + exynos->ram1base = ram1; + exynos->clk_ops = &vipx_clk_ops; + exynos->ctl_ops = &vipx_ctl_ops; + exynos->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(exynos->pinctrl)) { + probe_err("devm_pinctrl_get is fail"); + ret = PTR_ERR(exynos->pinctrl); + goto p_err; + } + + ret = vipx_exynos_clk_init(dev); + if (ret) { + probe_err("vipx_exynos_clk_init is fail(%d)", ret); + goto p_err; + } + +p_err: + probe_info("%s():%d\n", __func__, ret); + return ret; +} + +void vipx_readl(void __iomem *base_addr, struct vipx_reg *reg, u32 *val) +{ + *val = readl(base_addr + reg->offset); + +#ifdef DBG_HW_SFR + vipx_info("[REG][%s][0x%04X], val(R):[0x%08X]\n", reg->name, reg->offset, *val); +#endif +} + +void vipx_writel(void __iomem *base_addr, struct vipx_reg *reg, u32 val) +{ +#ifdef DBG_HW_SFR + vipx_info("[REG][%s][0x%04X], val(W):[0x%08X]\n", reg->name, reg->offset, val); +#endif + + writel(val, base_addr + reg->offset); +} + +void vipx_readf(void __iomem *base_addr, struct vipx_reg *reg, struct vipx_field *field, u32 *val) +{ + *val = (readl(base_addr + reg->offset) >> (field->bit_start)) & ((1 << (field->bit_width)) - 1); + +#ifdef DBG_HW_SFR + vipx_info("[REG][%s][%s][0x%04X], val(R):[0x%08X]\n", reg->name, field->name, reg->offset, *val); +#endif +} + +void vipx_writef(void __iomem *base_addr, struct vipx_reg *reg, struct vipx_field *field, u32 val) +{ + u32 mask, temp; + + mask = ((1 << field->bit_width) - 1); + temp = readl(base_addr + reg->offset) & ~(mask << field->bit_start); + temp |= (val & mask) << (field->bit_start); + +#ifdef DBG_HW_SFR + vipx_info("[REG][%s][%s][0x%04X], val(W):[0x%08X]\n", reg->name, field->name, reg->offset, val); +#endif + + writel(temp, base_addr + reg->offset); +} diff --git a/drivers/vision/vipx/vipx-binary.c b/drivers/vision/vipx/vipx-binary.c new file mode 100755 index 000000000000..51f1faca42ca --- /dev/null +++ b/drivers/vision/vipx/vipx-binary.c @@ -0,0 +1,196 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "vipx-config.h" +#include "vipx-binary.h" +#include "vipx-memory.h" + +static noinline_for_stack long __get_file_size(struct file *file) +{ + struct kstat st; + u32 request_mask = (STATX_MODE | STATX_SIZE); + + if (vfs_getattr(&file->f_path, &st, request_mask, KSTAT_QUERY_FLAGS)) + return -1; + if (!S_ISREG(st.mode)) + return -1; + if (st.size != (long)st.size) + return -1; + + return st.size; +} + +int vipx_binary_init(struct vipx_binary *binary, struct device *dev) +{ + int ret = 0; + + BUG_ON(!binary); + BUG_ON(!dev); + + binary->dev = dev; + + return ret; +} + +int vipx_binary_read(struct vipx_binary *binary, + char *path, + char *name, + void *target, + size_t target_size) +{ + int ret = 0; + loff_t pos = 0; + const struct firmware *fw_blob; + u8 *buf = NULL; + struct file *fp; + mm_segment_t old_fs; + long fsize, nread; + char fname[VIPX_FW_NAME_LEN]; + + BUG_ON(!binary); + + snprintf(fname, sizeof(fname), "%s%s", path, name); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + fp = filp_open(fname, O_RDONLY, 0); + if (IS_ERR_OR_NULL(fp)) { + set_fs(old_fs); + goto request_fw; + } + + fsize = __get_file_size(fp); + if (fsize <= 0) { + vipx_err("__get_file_size is fail(%ld)\n", fsize); + ret = -EBADF; + goto p_err; + } + + buf = vmalloc(fsize); + if (!buf) { + vipx_err("vmalloc is fail\n"); + ret = -ENOMEM; + goto p_err; + } + + nread = kernel_read(fp, buf, fsize, &pos); + if (nread != fsize) { + vipx_err("kernel_read is fail(%ld != %ld)\n", nread, fsize); + ret = -EIO; + goto p_err; + } + + if (fsize > target_size) { + vipx_err("image size is over(%ld > %ld)\n", fsize, target_size); + ret = -EIO; + goto p_err; + } + + /* no cache operation, because target is sram of vipx */ +#ifdef CONFIG_EXYNOS_VIPX_HARDWARE + memcpy(target, (void *)buf, fsize); +#endif + vipx_info("FW(%s, %ld) were applied successfully.\n", fname, fsize); + +p_err: + if (buf) + vfree(buf); + + filp_close(fp, current->files); + set_fs(old_fs); + + return ret; + +request_fw: + ret = request_firmware(&fw_blob, name, binary->dev); + if (ret) { + vipx_err("request_firmware(%s) is fail(%d)", name, ret); + ret = -EINVAL; + goto request_err; + } + + if (!fw_blob) { + vipx_err("fw_blob is NULL\n"); + ret = -EINVAL; + goto request_err; + } + + if (!fw_blob->data) { + vipx_err("fw_blob->data is NULL\n"); + ret = -EINVAL; + goto request_err; + } + + if (fw_blob->size > target_size) { + vipx_err("image size is over(%ld > %ld)\n", fw_blob->size, target_size); + ret = -EIO; + goto request_err; + } + +#ifdef CONFIG_EXYNOS_VIPX_HARDWARE + memcpy(target, fw_blob->data, fw_blob->size); +#endif + vipx_info("Binay(%s, %ld) were applied successfully.\n", name, fw_blob->size); + +request_err: + release_firmware(fw_blob); + return ret; +} + +int vipx_binary_write(struct vipx_binary *binary, + char *path, + char *name, + void *target, + size_t target_size) +{ + int ret = 0; + struct file *fp; + mm_segment_t old_fs; + long nwrite; + loff_t pos = 0; + char fname[VIPX_FW_NAME_LEN]; + + BUG_ON(!binary); + + snprintf(fname, sizeof(fname), "%s%s", path, name); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + fp = filp_open(fname, O_RDWR | O_CREAT, 0); + if (IS_ERR_OR_NULL(fp)) { + set_fs(old_fs); + vipx_err("filp_open is fail(%p)\n", fp); + ret = -EBADF; + goto p_err; + } + + vipx_info("debug kva(0x%p), size(%ld)\n", target, target_size); + + + nwrite = kernel_write(fp, target, target_size, &pos); + if (nwrite != target_size) { + filp_close(fp, current->files); + set_fs(old_fs); + vipx_err("kernel_write is fail(%ld != %ld)\n", nwrite, target_size); + ret = -EIO; + goto p_err; + } + + vipx_info("Binay(%s, %ld) were applied successfully.\n", fname, target_size); + + filp_close(fp, current->files); + set_fs(old_fs); + +p_err: + return ret; +} diff --git a/drivers/vision/vipx/vipx-debug.c b/drivers/vision/vipx/vipx-debug.c new file mode 100755 index 000000000000..958b60f97199 --- /dev/null +++ b/drivers/vision/vipx/vipx-debug.c @@ -0,0 +1,590 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "vipx-config.h" +#include "vipx-debug.h" +#include "vipx-graphmgr.h" +#include "vipx-graph.h" +#include "vipx-system.h" + +#define DEBUG_FS_ROOT_NAME "vipx" +#define DEBUG_FS_LOGFILE_NAME "fw-msg" +#define DEBUG_FS_IMGFILE_NAME "dump-img" +#define DEBUG_FS_GRPFILE_NAME "graph" +#define DEBUG_FS_BUFFILE_NAME "buffer" + +#define CHUNKSZ 32 + +s32 atoi(const char *psz_buf) +{ + const char *pch = psz_buf; + s32 base = 0; + + while (isspace(*pch)) + pch++; + + if (*pch == '-' || *pch == '+') { + base = 10; + pch++; + } else if (*pch && tolower(pch[strlen(pch) - 1]) == 'h') { + base = 16; + } + + return simple_strtoul(pch, NULL, base); +} + +int bitmap_scnprintf(char *buf, unsigned int buflen, + const unsigned long *maskp, int nmaskbits) +{ + int i, word, bit, len = 0; + unsigned long val; + const char *sep = ""; + int chunksz; + u32 chunkmask; + + chunksz = nmaskbits & (CHUNKSZ - 1); + if (chunksz == 0) + chunksz = CHUNKSZ; + + i = ALIGN(nmaskbits, CHUNKSZ) - CHUNKSZ; + for (; i >= 0; i -= CHUNKSZ) { + chunkmask = ((1ULL << chunksz) - 1); + word = i / BITS_PER_LONG; + bit = i % BITS_PER_LONG; + val = (maskp[word] >> bit) & chunkmask; + len += scnprintf(buf+len, buflen-len, "%s%0*lx", sep, + (chunksz+3)/4, val); + chunksz = CHUNKSZ; + sep = ","; + } + return len; +} + +void vipx_dmsg_concate(struct vipx_debug_log *log, const char *fmt, ...) +{ + va_list ap; + char term[50]; + u32 copy_len; + + va_start(ap, fmt); + vsnprintf(term, sizeof(term), fmt, ap); + va_end(ap); + + if (log->dsentence_pos >= DEBUG_SENTENCE_MAX) { + vipx_err("debug message(%zd) over max\n", log->dsentence_pos); + return; + } + + copy_len = min((DEBUG_SENTENCE_MAX - log->dsentence_pos - 1), strlen(term)); + strncpy(log->dsentence + log->dsentence_pos, term, copy_len); + log->dsentence_pos += copy_len; + log->dsentence[log->dsentence_pos] = 0; +} + +char * vipx_dmsg_print(struct vipx_debug_log *log) +{ + log->dsentence_pos = 0; + return log->dsentence; +} + +int vipx_debug_memdump8(u8 *start, u8 *end) +{ + int ret = 0; + u8 *cur; + u32 items, offset; + char term[50], sentence[250]; + + cur = start; + items = 0; + offset = 0; + + memset(sentence, 0, sizeof(sentence)); + snprintf(sentence, sizeof(sentence), "[V] Memory Dump8(%p ~ %p)", start, end); + + while (cur < end) { + if ((items % 16) == 0) { +#ifdef DEBUG_LOG_MEMORY + printk(KERN_DEBUG "%s\n", sentence); +#else + printk(KERN_INFO "%s\n", sentence); +#endif + offset = 0; + snprintf(term, sizeof(term), "[V] %p: ", cur); + snprintf(&sentence[offset], sizeof(sentence) - offset, "%s", term); + offset += strlen(term); + items = 0; + } + + snprintf(term, sizeof(term), "%02X ", *cur); + snprintf(&sentence[offset], sizeof(sentence) - offset, "%s", term); + offset += strlen(term); + cur++; + items++; + } + + if (items) { +#ifdef DEBUG_LOG_MEMORY + printk(KERN_DEBUG "%s\n", sentence); +#else + printk(KERN_INFO "%s\n", sentence); +#endif + } + + ret = cur - end; + + return ret; +} + + +int vipx_debug_memdump16(u16 *start, u16 *end) +{ + int ret = 0; + u16 *cur; + u32 items, offset; + char term[50], sentence[250]; + + cur = start; + items = 0; + offset = 0; + + memset(sentence, 0, sizeof(sentence)); + snprintf(sentence, sizeof(sentence), "[V] Memory Dump16(%p ~ %p)", start, end); + + while (cur < end) { + if ((items % 16) == 0) { +#ifdef DEBUG_LOG_MEMORY + printk(KERN_DEBUG "%s\n", sentence); +#else + printk(KERN_INFO "%s\n", sentence); +#endif + offset = 0; + snprintf(term, sizeof(term), "[V] %p: ", cur); + snprintf(&sentence[offset], sizeof(sentence) - offset, "%s", term); + offset += strlen(term); + items = 0; + } + + snprintf(term, sizeof(term), "0x%04X ", *cur); + snprintf(&sentence[offset], sizeof(sentence) - offset, "%s", term); + offset += strlen(term); + cur++; + items++; + } + + if (items) { +#ifdef DEBUG_LOG_MEMORY + printk(KERN_DEBUG "%s\n", sentence); +#else + printk(KERN_INFO "%s\n", sentence); +#endif + } + + ret = cur - end; + + return ret; +} + +int vipx_debug_memdump32(u32 *start, u32 *end) +{ + int ret = 0; + u32 *cur; + u32 items, offset; + char term[50], sentence[250]; + + cur = start; + items = 0; + offset = 0; + + memset(sentence, 0, sizeof(sentence)); + snprintf(sentence, sizeof(sentence), "[V] Memory Dump32(%p ~ %p)", start, end); + + while (cur < end) { + if ((items % 8) == 0) { +#ifdef DEBUG_LOG_MEMORY + printk(KERN_DEBUG "%s\n", sentence); +#else + printk(KERN_INFO "%s\n", sentence); +#endif + offset = 0; + snprintf(term, sizeof(term), "[V] %p: ", cur); + snprintf(&sentence[offset], sizeof(sentence) - offset, "%s", term); + offset += strlen(term); + items = 0; + } + + snprintf(term, sizeof(term), "0x%08X ", *cur); + snprintf(&sentence[offset], sizeof(sentence) - offset, "%s", term); + offset += strlen(term); + cur++; + items++; + } + + if (items) { +#ifdef DEBUG_LOG_MEMORY + printk(KERN_DEBUG "%s\n", sentence); +#else + printk(KERN_INFO "%s\n", sentence); +#endif + } + + ret = cur - end; + + return ret; +} + +static int vipx_debug_log_open(struct inode *inode, struct file *file) +{ + if (inode->i_private) + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t vipx_debug_log_read(struct file *file, char __user *user_buf, + size_t buf_len, loff_t *ppos) +{ + int ret = 0; + int size = 0; + + struct vipx_debug *debug; + struct vipx_system *system; + + debug = file->private_data; + if (debug == NULL) { + vipx_err("Cannot find private data\n"); + return 0; + } + + system = debug->system_data; + if (system == NULL) { + vipx_err("Cannot find system data\n"); + return 0; + } + + if (buf_len > VIPX_DEBUG_SIZE) + size = VIPX_DEBUG_SIZE; + else + size = buf_len; + + if (system->memory.info.kvaddr_debug == 0) { + vipx_err("Cannot find debug region\n"); + return 0; + } + + ret = copy_to_user(user_buf, system->memory.info.kvaddr_debug, size); + if (ret) { + vipx_err("copy_from_user is fail(%d)\n", ret); + memcpy(user_buf, system->memory.info.kvaddr_debug, size); + ret = 0; + // return 0; + } + + return size; +} + +static int vipx_debug_img_open(struct inode *inode, struct file *file) +{ + if (inode->i_private) + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t vipx_debug_img_read(struct file *file, char __user *user_buf, + size_t len, loff_t *ppos) +{ + size_t size = 0; + struct vipx_debug *debug; + struct vipx_debug_imgdump *imgdump; + int ret = 0; + + debug = file->private_data; + imgdump = &debug->imgdump; + + if (!imgdump->kvaddr) { + vipx_err("kvaddr is NULL\n"); + return 0; + } + + if (!imgdump->cookie) { + vipx_err("cookie is NULL\n"); + return 0; + } + + if (len <= imgdump->length) + size = len; + else + size = imgdump->length; + + if (!size) { + imgdump->cookie = NULL; + imgdump->kvaddr = NULL; + imgdump->length = 0; + imgdump->offset = 0; + goto p_err; + } + + /* HACK for test */ + memset(imgdump->kvaddr, 0x88, size / 2); + //vb2_ion_sync_for_device(imgdump->cookie, imgdump->offset, size, DMA_FROM_DEVICE); + ret = copy_to_user(user_buf, imgdump->kvaddr, size); + if (ret) { + vipx_err("copy_from_user is fail(%d)\n", ret); + memcpy(user_buf, imgdump->kvaddr, size); + ret = 0; + // return 0; + + } + + vipx_info("DUMP : %p, SIZE : %zd\n", imgdump->kvaddr, size); + + imgdump->offset += size; + imgdump->length -= size; + imgdump->kvaddr = (char *)imgdump->kvaddr + size; + +p_err: + return size; +} + +static ssize_t vipx_debug_img_write(struct file *file, const char __user *user_buf, + size_t len, loff_t *ppos) +{ + return len; +} + +static int vipx_debug_grp_show(struct seq_file *s, void *unused) +{ + u32 i; + struct vipx_debug *debug = s->private; + struct vipx_graphmgr *graphmgr = debug->graphmgr_data; + struct vipx_graph *graph; + + seq_printf(s, "------------------------------------------" + "----------------------------------------" + "--------------------------------------\n"); + seq_printf(s, "%7.s %7.s %7.s %7.s %7.s %7.s %7.s\n", + "graph", "prio", "period", "input", "done", "cancel", "recent"); + seq_printf(s, "------------------------------------------" + "----------------------------------------" + "--------------------------------------\n"); + + mutex_lock(&graphmgr->mlock); + for (i = 0; i < VIPX_MAX_GRAPH; ++i) { + graph = graphmgr->graph[i]; + + if (!graph) + continue; + + seq_printf(s, "%2d(%3d) %7d %7d %7d %7d %7d\n", + graph->idx, graph->uid, graph->priority, + graph->input_cnt, graph->done_cnt, graph->cancel_cnt, graph->recent); + } + mutex_unlock(&graphmgr->mlock); + + seq_printf(s, "------------------------------------------" + "----------------------------------------" + "--------------------------------------\n"); + return 0; +} + +static int vipx_debug_grp_open(struct inode *inode, struct file *file) +{ + return single_open(file, vipx_debug_grp_show, inode->i_private); +} + +static int vipx_debug_buf_show(struct seq_file *s, void *unused) +{ + return 0; +} + +static int vipx_debug_buf_open(struct inode *inode, struct file *file) +{ + return single_open(file, vipx_debug_buf_show, inode->i_private); +} + +static const struct file_operations vipx_debug_log_fops = { + .open = vipx_debug_log_open, + .read = vipx_debug_log_read, + .llseek = default_llseek +}; + +static const struct file_operations vipx_debug_img_fops = { + .open = vipx_debug_img_open, + .read = vipx_debug_img_read, + .write = vipx_debug_img_write, + .llseek = default_llseek +}; + +static const struct file_operations vipx_debug_grp_fops = { + .open = vipx_debug_grp_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +static const struct file_operations vipx_debug_buf_fops = { + .open = vipx_debug_buf_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +static void vipx_debug_monitor_fn(unsigned long data) +{ + struct vipx_debug_monitor *monitor = (struct vipx_debug_monitor *)data; + struct vipx_debug *debug = container_of(monitor, struct vipx_debug, monitor); + struct vipx_graphmgr *graphmgr = debug->graphmgr_data; + struct vipx_system *system = debug->system_data; + struct vipx_interface *interface = &system->interface; + + if (!test_bit(VIPX_DEBUG_STATE_START, &debug->state)) + return; + + if (monitor->tick_cnt == graphmgr->tick_cnt) + vipx_err("timer thread is stuck(%d, %d)\n", monitor->tick_cnt, graphmgr->tick_pos); + + if (monitor->sched_cnt == graphmgr->sched_cnt) { + struct vipx_graph *graph; + u32 i; + + vipx_info("GRAPH--------------------------------------------------------------\n"); + for (i = 0; i < VIPX_MAX_GRAPH; i++) { + graph = graphmgr->graph[i]; + if (!graph) + continue; + + vipx_graph_print(graph); + } + + vipx_info("GRAPH-MGR----------------------------------------------------------\n"); + vipx_taskdesc_print(graphmgr); + + vipx_info("INTERFACE-MGR------------------------------------------------------\n"); + vipx_interface_print(interface); + + vipx_info("-------------------------------------------------------------------\n"); + vipx_err("graph thread is stuck(%d, %d)\n", monitor->sched_cnt, graphmgr->sched_pos); + } + + monitor->tick_cnt = graphmgr->tick_cnt; + monitor->sched_cnt = graphmgr->sched_cnt; + monitor->done_cnt = interface->done_cnt; + vipx_info("TIME %d(%d, %d, %d)\n", monitor->time_cnt, monitor->sched_cnt, monitor->tick_cnt, monitor->done_cnt); + + monitor->time_cnt++; + mod_timer(&monitor->timer, jiffies + DEBUG_MONITORING_PERIOD); +} + +int __vipx_debug_stop(struct vipx_debug *debug) +{ + int ret = 0; + struct vipx_debug_monitor *monitor = &debug->monitor; + + if (!test_bit(VIPX_DEBUG_STATE_START, &debug->state)) + goto p_err; + + del_timer(&monitor->timer); + clear_bit(VIPX_DEBUG_STATE_START, &debug->state); + +p_err: + return ret; +} + +int vipx_debug_probe(struct vipx_debug *debug, void *graphmgr_data, void *system_data) +{ + debug->graphmgr_data = graphmgr_data; + debug->system_data = system_data; + + debug->root = debugfs_create_dir(DEBUG_FS_ROOT_NAME, NULL); + if (debug->root) + probe_info("%s is created\n", DEBUG_FS_ROOT_NAME); + + debug->logfile = debugfs_create_file(DEBUG_FS_LOGFILE_NAME, S_IRUSR, + debug->root, debug, &vipx_debug_log_fops); + if (debug->logfile) + probe_info("%s is created\n", DEBUG_FS_LOGFILE_NAME); + + debug->imgdump.file = debugfs_create_file(DEBUG_FS_IMGFILE_NAME, S_IRUSR, + debug->root, debug, &vipx_debug_img_fops); + if (debug->imgdump.file) + probe_info("%s is created\n", DEBUG_FS_IMGFILE_NAME); + + debug->grpfile = debugfs_create_file(DEBUG_FS_GRPFILE_NAME, S_IRUSR, + debug->root, debug, &vipx_debug_grp_fops); + if (debug->grpfile) + probe_info("%s is created\n", DEBUG_FS_GRPFILE_NAME); + + debug->grpfile = debugfs_create_file(DEBUG_FS_BUFFILE_NAME, S_IRUSR, + debug->root, debug, &vipx_debug_buf_fops); + if (debug->buffile) + probe_info("%s is created\n", DEBUG_FS_BUFFILE_NAME); + + clear_bit(VIPX_DEBUG_STATE_START, &debug->state); + + return 0; +} + +int vipx_debug_open(struct vipx_debug *debug) +{ + return 0; +} + +int vipx_debug_close(struct vipx_debug *debug) +{ + int ret = 0; + + ret = __vipx_debug_stop(debug); + if (ret) + vipx_err("__vipx_debug_stop is fail(%d)\n", ret); + + return ret; +} + +int vipx_debug_start(struct vipx_debug *debug) +{ + int ret = 0; + struct vipx_debug_monitor *monitor = &debug->monitor; + struct vipx_graphmgr *graphmgr = debug->graphmgr_data; + struct vipx_system *system = debug->system_data; + struct vipx_interface *interface = &system->interface; + + monitor->tick_cnt = graphmgr->tick_cnt; + monitor->sched_cnt = graphmgr->sched_cnt; + monitor->done_cnt = interface->done_cnt; + monitor->time_cnt = 0; + + set_bit(VIPX_DEBUG_STATE_START, &debug->state); + + init_timer(&monitor->timer); + monitor->timer.expires = jiffies + DEBUG_MONITORING_PERIOD; + monitor->timer.data = (unsigned long)monitor; + monitor->timer.function = vipx_debug_monitor_fn; + add_timer(&monitor->timer); + + return ret; +} + +int vipx_debug_stop(struct vipx_debug *debug) +{ + int ret = 0; + + ret = __vipx_debug_stop(debug); + if (ret) + vipx_err("__vipx_debug_stop is fail(%d)\n", ret); + + return ret; +} diff --git a/drivers/vision/vipx/vipx-device.c b/drivers/vision/vipx/vipx-device.c new file mode 100755 index 000000000000..75fdb9db70a7 --- /dev/null +++ b/drivers/vision/vipx/vipx-device.c @@ -0,0 +1,486 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vipx-config.h" +#include "vipx-device.h" +#include "vipx-graph.h" +#include "vipx-vertex.h" +#include "vipx-graphmgr.h" + +static struct reserved_mem *vipx_fw_code_rmem; +void *vipx_fw_code_rmem_base; + +static int vipx_device_runtime_suspend(struct device *dev); +static int vipx_device_runtime_resume(struct device *dev); + +static int __init vipx_fw_code_rmem_setup(struct reserved_mem *rmem) +{ + pr_info("%s: base=%pa, size=%pa\n", __func__, &rmem->base, &rmem->size); + + vipx_fw_code_rmem = rmem; + + return 0; +} + +RESERVEDMEM_OF_DECLARE(vipx_fw_code_rmem, "exynos,vipx_fw_code_rmem", vipx_fw_code_rmem_setup); + +void __vipx_fault_handler(struct vipx_device *device) { +#if 0 + struct vipx_system *system; + struct vipx_memory *memory; + struct vipx_graphmgr *graphmgr; + struct vipx_graph *graph; + struct vipxo_pu *pu, *temp; + struct vb_container *container; + struct vb_buffer *buffer; + u32 i, j, k; + + system = &device->system; + memory = &system->memory; + graphmgr = &device->graphmgr; + + /* 1. Internal Memory Infomation */ + vipx_info("internal memory : 0x%llX\n", memory->info.dvaddr); + + /* 2. Buffer Memroy Information */ + for (i = 0; i < VIPX_MAX_GRAPH; ++i) { + graph = graphmgr->graph[i]; + if (!graph) + continue; + + vipx_info("=================================================\n"); + vipx_info("GRAPH : %d(%d)\n", graph->id, graph->uid); + vipx_info("INPUT--------------------------------------------\n"); + list_for_each_entry_safe(pu, temp, &graph->inleaf_list, gleaf_entry) { + vipx_info("PU : %d\n", pu->id); + vipx_info("-------------------------------------------------\n"); + for (j = 0; j < VIPX_MAX_BUFFER; ++j) { + container = pu->container[j]; + if (!container) + continue; + + if (j == 0) { + vipx_info("TYPE : %d\n", container->type); + vipx_info("RESOLUTION : %d x %d\n", container->format->width, container->format->height); + vipx_info("SIZE : %d\n", container->format->size[container->format->plane]); + vipx_info("-------------------------------------------------\n"); + } + + for (k = 0; k < container->count; ++k) { + buffer = &container->buffers[k]; + vipx_info("[%d][%d]DVADDR : %llx\n", j, k, buffer->dvaddr); + vipx_info("[%d][%d]KVADDR : %p\n", j, k, buffer->kvaddr); + } + + vipx_info("-------------------------------------------------\n"); + } + } + + vipx_info("OUTPUT-------------------------------------------\n"); + list_for_each_entry_safe(pu, temp, &graph->otleaf_list, gleaf_entry) { + vipx_info("PU : %d\n", pu->id); + vipx_info("-------------------------------------------------\n"); + for (j = 0; j < VIPX_MAX_BUFFER; ++j) { + container = pu->container[j]; + if (!container) + continue; + + if (j == 0) { + vipx_info("TYPE : %d\n", container->type); + vipx_info("RESOLUTION : %d x %d\n", container->format->width, container->format->height); + vipx_info("SIZE : %d\n", container->format->size[container->format->plane]); + vipx_info("-------------------------------------------------\n"); + } + + for (k = 0; k < container->count; ++k) { + buffer = &container->buffers[k]; + vipx_info("[%d][%d]DVADDR : %llx\n", j, k, buffer->dvaddr); + vipx_info("[%d][%d]KVADDR : %p\n", j, k, buffer->kvaddr); + } + + vipx_info("-------------------------------------------------\n"); + } + } + + vipx_graph_print(graph); + } +#endif +} + +static int __attribute__((unused)) vipx_fault_handler(struct iommu_domain *domain, + struct device *dev, + unsigned long fault_addr, + int fault_flag, + void *token) +{ + struct vipx_device *device; + + pr_err("\n"); + pr_err("Device virtual(0x%X) is invalid access\n", (u32)fault_addr); + + device = dev_get_drvdata(dev); + + __vipx_fault_handler(device); + + return -EINVAL; +} + +static int __vipx_device_start(struct vipx_device *device) +{ + int ret = 0; + + if (test_bit(VIPX_DEVICE_STATE_START, &device->state)) { + vipx_err("already started\n"); + ret = -EINVAL; + goto p_err; + } + + ret = vipx_system_start(&device->system); + if (ret) { + vipx_err("vipx_system_start is fail(%d)\n", ret); + goto p_err; + } + + ret = vipx_debug_start(&device->debug); + if (ret) { + vipx_err("vipx_debug_start is fail(%d)\n", ret); + goto p_err; + } + + set_bit(VIPX_DEVICE_STATE_START, &device->state); + +p_err: + return ret; +} + +static int __vipx_device_stop(struct vipx_device *device) +{ + int ret = 0; + + if (!test_bit(VIPX_DEVICE_STATE_START, &device->state)) + goto p_err; + + ret = vipx_debug_stop(&device->debug); + if (ret) + vipx_err("vipx_debug_stop is fail(%d)\n", ret); + + ret = vipx_system_stop(&device->system); + if (ret) + vipx_err("vipx_system_stop is fail(%d)\n", ret); + + clear_bit(VIPX_DEVICE_STATE_START, &device->state); + +p_err: + return ret; +} + +static int __vipx_device_power_on(struct vipx_device *device) +{ + int ret = 0; + + ret = pm_runtime_get_sync(device->dev); + if (ret) + vipx_err("runtime resume is fail(%d)", ret); + + return ret; +} + +static int __vipx_device_power_off(struct vipx_device *device) +{ + int ret = 0; + + ret = pm_runtime_put_sync(device->dev); + if (ret) + vipx_err("runtime resume is fail(%d)", ret); + + return ret; +} + +static int vipx_device_probe(struct platform_device *pdev) +{ + int ret = 0; + struct device *dev; + struct vipx_device *device; + + BUG_ON(!pdev); + + dma_set_mask(&pdev->dev, DMA_BIT_MASK(36)); + + dev = &pdev->dev; + + device = devm_kzalloc(dev, sizeof(struct vipx_device), GFP_KERNEL); + if (!device) { + probe_err("device is NULL"); + ret = -ENOMEM; + goto p_err; + } + + ret = vipx_system_probe(&device->system, pdev); + if (ret) { + probe_err("vipx_system_probe is fail(%d)\n", ret); + ret = -EINVAL; + goto p_err; + } + + ret = vipx_vertex_probe(&device->vertex, dev); + if (ret) { + probe_err("vipx_vertex_probe is fail(%d)\n", ret); + ret = -EINVAL; + goto p_err; + } + + ret = vipx_graphmgr_probe(&device->graphmgr); + if (ret) { + probe_err("vipx_graphmgr_probe is fail(%d)\n", ret); + ret = -EINVAL; + goto p_err; + } + + ret = vipx_debug_probe(&device->debug, &device->graphmgr, &device->system); + if (ret) { + probe_err("vipx_debug_probe is fail(%d)\n", ret); + ret = -EINVAL; + goto p_err; + } + + iovmm_set_fault_handler(dev, vipx_fault_handler, NULL); + pm_runtime_enable(dev); + + device->dev = dev; + device->mode = VIPX_DEVICE_MODE_NORMAL; + clear_bit(VIPX_DEVICE_STATE_OPEN, &device->state); + clear_bit(VIPX_DEVICE_STATE_START, &device->state); + dev_set_drvdata(dev, device); + + vipx_fw_code_rmem_base = phys_to_virt(vipx_fw_code_rmem->base); + +p_err: + + probe_info("%s():%d\n", __func__, ret); + return ret; +} + +int vipx_device_open(struct vipx_device *device) +{ + int ret = 0; + + BUG_ON(!device); + + if (test_bit(VIPX_DEVICE_STATE_OPEN, &device->state)) { + vipx_err("device is already opened\n"); + ret = -EINVAL; + goto p_err; + } + + ret = vipx_system_open(&device->system); + if (ret) { + vipx_err("vipx_system_open is fail(%d)\n", ret); + goto p_err; + } + + ret = vipx_debug_open(&device->debug); + if (ret) { + vipx_err("vipx_debug_open is fail(%d)\n", ret); + goto p_err; + } + + ret = vipx_graphmgr_open(&device->graphmgr); + if (ret) { + vipx_err("vipx_graphmgr_open is fail(%d)\n", ret); + goto p_err; + } + + ret = __vipx_device_power_on(device); + if (ret) { + vipx_err("__vipx_device_power_on is fail(%d)\n", ret); + goto p_err; + } + + set_bit(VIPX_DEVICE_STATE_OPEN, &device->state); + +p_err: + vipx_info("%s():%d\n", __func__, ret); + return ret; +} + +int vipx_device_close(struct vipx_device *device) +{ + int ret = 0; + + BUG_ON(!device); + + if (!test_bit(VIPX_DEVICE_STATE_OPEN, &device->state)) { + vipx_err("device is already closed\n"); + ret = -EINVAL; + goto p_err; + } + + ret = __vipx_device_stop(device); + if (ret) + vipx_err("__vipx_device_stop is fail(%d)\n", ret); + + ret = vipx_graphmgr_close(&device->graphmgr); + if (ret) + vipx_err("vipx_graphmgr_close is fail(%d)\n", ret); + + ret = vipx_system_close(&device->system); + if (ret) + vipx_err("vipx_system_close is fail(%d)\n", ret); + + ret = vipx_debug_close(&device->debug); + if (ret) + vipx_err("vipx_debug_close is fail(%d)\n", ret); + + ret = __vipx_device_power_off(device); + if (ret) + vipx_err("__vipx_device_power_off is fail(%d)\n", ret); + + clear_bit(VIPX_DEVICE_STATE_OPEN, &device->state); + +p_err: + vipx_info("%s():%d\n", __func__, ret); + return ret; +} + +int vipx_device_start(struct vipx_device *device) +{ + int ret = 0; + + BUG_ON(!device); + + ret = __vipx_device_start(device); + if (ret) + vipx_err("__vipx_device_start is fail(%d)\n", ret); + + vipx_info("%s():%d\n", __func__, ret); + + return ret; +} + +int vipx_device_stop(struct vipx_device *device) +{ + int ret = 0; + + BUG_ON(!device); + + ret = __vipx_device_stop(device); + if (ret) + vipx_err("__vipx_device_stop is fail(%d)\n", ret); + + vipx_info("%s():%d\n", __func__, ret); + + return ret; +} + +static int vipx_device_remove(struct platform_device *pdev) +{ + return 0; +} + +static int vipx_device_suspend(struct device *dev) +{ + return 0; +} + +static int vipx_device_resume(struct device *dev) +{ + return 0; +} + +static int vipx_device_runtime_suspend(struct device *dev) +{ + int ret = 0; + struct vipx_device *device; + + device = dev_get_drvdata(dev); + + ret = vipx_system_suspend(&device->system); + if (ret) + vipx_err("vipx_system_suspend is fail(%d)\n", ret); + + vipx_info("%s():%d\n", __func__, ret); + return ret; +} + +static int vipx_device_runtime_resume(struct device *dev) +{ + int ret = 0; + struct vipx_device *device; + + device = dev_get_drvdata(dev); + + ret = vipx_system_resume(&device->system, device->mode); + if (ret) { + vipx_err("vipx_system_resume is fail(%d)\n", ret); + goto p_err; + } + +p_err: + vipx_info("%s():%d\n", __func__, ret); + return ret; +} + +static const struct dev_pm_ops vipx_pm_ops = { + .suspend = vipx_device_suspend, + .resume = vipx_device_resume, + .runtime_suspend = vipx_device_runtime_suspend, + .runtime_resume = vipx_device_runtime_resume, +}; + +static const struct of_device_id exynos_vipx_match[] = { + { + .compatible = "samsung,exynos-vipx", + }, + {} +}; +MODULE_DEVICE_TABLE(of, exynos_vipx_match); + +static struct platform_driver vipx_driver = { + .probe = vipx_device_probe, + .remove = vipx_device_remove, + .driver = { + .name = "exynos-vipx", + .owner = THIS_MODULE, + .pm = &vipx_pm_ops, + .of_match_table = of_match_ptr(exynos_vipx_match) + } +}; + +static int __init vipx_device_init(void) +{ + int ret = platform_driver_register(&vipx_driver); + if (ret) + probe_err("platform_driver_register is fail():%d\n", ret); + + probe_info("vipx device init is loaded"); + + return ret; +} +late_initcall(vipx_device_init); + +static void __exit vipx_device_exit(void) +{ + platform_driver_unregister(&vipx_driver); +} +module_exit(vipx_device_exit); + +MODULE_AUTHOR("Scott Choi"); +MODULE_DESCRIPTION("Exynos VIPx driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/vision/vipx/vipx-device.h b/drivers/vision/vipx/vipx-device.h new file mode 100644 index 000000000000..bef7594fd0d2 --- /dev/null +++ b/drivers/vision/vipx/vipx-device.h @@ -0,0 +1,45 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef VIPX_DEVICE_H_ +#define VIPX_DEVICE_H_ + +#include "vipx-graphmgr.h" +#include "vipx-system.h" +#include "vipx-vertex.h" +#include "vipx-debug.h" + +enum vipx_device_state { + VIPX_DEVICE_STATE_OPEN, + VIPX_DEVICE_STATE_START +}; + +enum vipx_device_mode { + VIPX_DEVICE_MODE_NORMAL, + VIPX_DEVICE_MODE_TEST +}; + +struct vipx_device { + struct device *dev; + unsigned long state; + u32 mode; + + struct vipx_graphmgr graphmgr; + struct vipx_system system; + struct vipx_vertex vertex; + struct vipx_debug debug; +}; + +int vipx_device_open(struct vipx_device *device); +int vipx_device_close(struct vipx_device *device); +int vipx_device_start(struct vipx_device *device); +int vipx_device_stop(struct vipx_device *device); + +#endif diff --git a/drivers/vision/vipx/vipx-graph.c b/drivers/vision/vipx/vipx-graph.c new file mode 100755 index 000000000000..51f7b9b787dc --- /dev/null +++ b/drivers/vision/vipx/vipx-graph.c @@ -0,0 +1,902 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "vipx-graphmgr.h" +#include "vipx-graph.h" +#include "vipx-debug.h" +#include "vs4l.h" + +const struct vipx_graph_ops vipx_graph_ops; + +static int __vipx_graph_start(struct vipx_graph *graph) +{ + int ret = 0; + BUG_ON(!graph); + + if (test_bit(VIPX_GRAPH_STATE_START, &graph->state)) + return 0; + + ret = vipx_graphmgr_grp_start(graph->cookie, graph); + if (ret) { + vipx_ierr("vipx_graphmgr_grp_start is fail(%d)\n", graph, ret); + goto p_err; + } + + set_bit(VIPX_GRAPH_STATE_START, &graph->state); + +p_err: + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +static int __vipx_graph_stop(struct vipx_graph *graph) +{ + int ret = 0, errcnt = 0; + u32 retry, timeout; + struct vipx_taskmgr *taskmgr; + struct vipx_task *control; + + if (!test_bit(VIPX_GRAPH_STATE_START, &graph->state)) + return 0; + + taskmgr = &graph->taskmgr; + if (taskmgr->req_cnt + taskmgr->pre_cnt) { + control = &graph->control; + control->message = VIPX_CTRL_STOP; + vipx_graphmgr_queue(graph->cookie, control); + timeout = wait_event_timeout(graph->control_wq, + control->message == VIPX_CTRL_STOP_DONE, VIPX_GRAPH_STOP_TIMEOUT); + if (!timeout) { + vipx_ierr("wait_event_timeout is expired\n", graph); + errcnt++; + } + } + + retry = VIPX_STOP_WAIT_COUNT; + while (--retry && taskmgr->req_cnt) { + vipx_iwarn("waiting %d request cancel...(%d)\n", graph, taskmgr->req_cnt, retry); + msleep(10); + } + + if (!retry) { + vipx_ierr("request cancel is fail\n", graph); + errcnt++; + } + + retry = VIPX_STOP_WAIT_COUNT; + while (--retry && taskmgr->pre_cnt) { + vipx_iwarn("waiting %d prepare cancel...(%d)\n", graph, taskmgr->pre_cnt, retry); + msleep(10); + } + + if (!retry) { + vipx_ierr("prepare cancel is fail\n", graph); + errcnt++; + } + + retry = VIPX_STOP_WAIT_COUNT; + while (--retry && taskmgr->pro_cnt) { + vipx_iwarn("waiting %d process done...(%d)\n", graph, taskmgr->pro_cnt, retry); + msleep(10); + } + + if (!retry) { + vipx_ierr("process done is fail\n", graph); + errcnt++; + } + + ret = vipx_graphmgr_grp_stop(graph->cookie, graph); + if (ret) { + vipx_ierr("vipx_graphmgr_grp_stop is fail(%d)\n", graph, ret); + errcnt++; + } + + vipx_task_flush(taskmgr); + clear_bit(VIPX_GRAPH_STATE_START, &graph->state); + + vipx_info("%s:%d\n", __func__, ret); + return errcnt; +} + +static int __vipx_graph_unmap(struct vipx_graph *graph) +{ + int ret = 0; + + clear_bit(VIPX_GRAPH_STATE_MMAPPED, &graph->state); + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +static int __vipx_graph_map(struct vipx_graph *graph) +{ + int ret = 0; + + set_bit(VIPX_GRAPH_STATE_MMAPPED, &graph->state); + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +static int __vipx_graph_alloc(struct vipx_graph *graph) +{ + int ret = 0; + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +static int __vipx_graph_free(struct vipx_graph *graph) +{ + int ret = 0; + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +static int __vipx_graph_parse(struct vipx_graph *graph) +{ + int ret = 0; + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +void vipx_graph_task_print(struct vipx_graph *graph) +{ + vipx_info("%s:\n", __func__); +} + +void vipx_graph_print(struct vipx_graph *graph) +{ + + vipx_info("%s:\n", __func__); +} + +int vipx_graph_create(struct vipx_graph **graph, void *cookie, void *memory) +{ + int ret = 0; + u32 i; + struct vipx_taskmgr *taskmgr; + + BUG_ON(!cookie); + + *graph = kzalloc(sizeof(struct vipx_graph), GFP_KERNEL); + if (*graph == NULL) { + vipx_err("kzalloc is fail"); + ret = -ENOMEM; + goto p_err; + } + + ret = vipx_graphmgr_grp_register(cookie, *graph); + if (ret) { + vipx_err("vipx_graphmgr_grp_register is fail(%d)\n", ret); + kfree(*graph); + goto p_err; + } + + (*graph)->control.message = VIPX_CTRL_NONE; + (*graph)->cookie = cookie; + (*graph)->memory = memory; + (*graph)->gops = &vipx_graph_ops; + mutex_init(&(*graph)->local_lock); + + /* task manager init */ + taskmgr = &(*graph)->taskmgr; + taskmgr->id = (*graph)->idx; + taskmgr->sindex = 0; + spin_lock_init(&taskmgr->slock); + + for (i = 0; i < VIPX_MAX_TASK; ++i) { + (*graph)->inhash[i] = VIPX_MAX_TASK; + (*graph)->othash[i] = VIPX_MAX_TASK; + } + + (*graph)->control.owner = *graph; + init_waitqueue_head(&(*graph)->control_wq); + ret = vipx_task_init(taskmgr, *graph); + if (ret) { + vipx_err("vipx_task_init is fail(%d)\n", ret); + kfree(*graph); + goto p_err; + } + + (*graph)->informat_list.count = 0; + (*graph)->informat_list.formats = 0; + (*graph)->otformat_list.count = 0; + (*graph)->otformat_list.formats = 0; + +p_err: + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +int vipx_graph_destroy(struct vipx_graph *graph) +{ + int ret = 0; + + BUG_ON(!graph); + + ret = __vipx_graph_stop(graph); + if (ret) + vipx_ierr("__vipx_graph_stop is fail(%d)\n", graph, ret); + + ret = vipx_graphmgr_grp_unregister(graph->cookie, graph); + if (ret) + vipx_ierr("vipx_graphmgr_grp_unregister is fail(%d)\n", graph, ret); + + ret = __vipx_graph_unmap(graph); + if (ret) + vipx_ierr("__vipx_graph_unmap is fail(%d)\n", graph, ret); + + ret = __vipx_graph_free(graph); + if (ret) + vipx_ierr("__vipx_graph_free is fail(%d)\n", graph, ret); + + if (graph->informat_list.formats) + kfree(graph->informat_list.formats); + graph->informat_list.formats = 0; + graph->informat_list.count = 0; + + if (graph->otformat_list.formats) + kfree(graph->otformat_list.formats); + graph->otformat_list.count = 0; + + kfree(graph); + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +int vipx_graph_config(struct vipx_graph *graph, struct vs4l_graph *info) +{ + int ret = 0; + + BUG_ON(!graph); + BUG_ON(!graph->cookie); + BUG_ON(!info); + + if (test_bit(VIPX_GRAPH_STATE_CONFIG, &graph->state)) { + vipx_ierr("graph is already configured\n", graph); + ret = -EINVAL; + goto p_err; + } + + if (info->priority > VIPX_GRAPH_MAX_PRIORITY) { + vipx_iwarn("graph priority is over(%d)\n", graph, info->priority); + info->priority = VIPX_GRAPH_MAX_PRIORITY; + } + + graph->uid = info->id; + graph->flags = info->flags; + graph->priority = info->priority; + + /* 2. graph allocation */ + ret = __vipx_graph_alloc(graph); + if (ret) { + vipx_ierr("__vipx_graph_alloc is fail(%d)\n", graph, ret); + goto p_err; + } + + /* 3. parsing */ + ret = __vipx_graph_parse(graph); + if (ret) { + vipx_ierr("__vipx_graph_parse is fail(%d)\n", graph, ret); + goto p_err; + } + + /* 4. Buffer Mapping */ + ret = __vipx_graph_map(graph); + if (ret) { + vipx_ierr("__vipx_graph_map is fail(%d)\n", graph, ret); + goto p_err; + } + + set_bit(VIPX_GRAPH_STATE_CONFIG, &graph->state); + +p_err: + vipx_iinfo("%s(%d, %d, %X):%d\n", graph, __func__, info->id, info->priority, info->flags, ret); + return ret; +} + +int vipx_graph_param(struct vipx_graph *graph, struct vs4l_param_list *plist) +{ + int ret = 0; + + set_bit(VIPX_GRAPH_FLAG_UPDATE_PARAM, &graph->flags); + + vipx_iinfo("%s:%d\n", graph, __func__, ret); + return ret; +} + +static int vipx_graph_prepare(struct vb_queue *q, struct vb_container_list *clist) +{ + int ret = 0; + + vipx_dbg("%s:%d\n", __func__, ret); + return ret; +} + +static int vipx_graph_unprepare(struct vb_queue *q, struct vb_container_list *clist) +{ + int ret = 0; + + vipx_dbg("%s:%d\n", __func__, ret); + return ret; +} + +const struct vb_ops vb_ops = { + .buf_prepare = vipx_graph_prepare, + .buf_unprepare = vipx_graph_unprepare +}; + +int vipx_graph_start(struct vipx_queue *queue) +{ + int ret = 0; + struct vipx_vertex_ctx *vctx; + struct vipx_graph *graph; + + BUG_ON(!queue); + + vctx = container_of(queue, struct vipx_vertex_ctx, queue); + graph = container_of(vctx, struct vipx_graph, vctx); + + ret = __vipx_graph_start(graph); + if (ret) + vipx_ierr("__vipx_graph_start is fail(%d)\n", graph, ret); + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +int vipx_graph_stop(struct vipx_queue *queue) +{ + int ret = 0; + struct vipx_graph *graph; + struct vipx_vertex_ctx *vctx; + + BUG_ON(!queue); + + vctx = container_of(queue, struct vipx_vertex_ctx, queue); + graph = container_of(vctx, struct vipx_graph, vctx); + + ret = __vipx_graph_stop(graph); + if (ret) + vipx_ierr("__vipx_graph_stop is fail(%d)\n", graph, ret); + + vipx_info("%s:%d\n", __func__, ret); + return ret; +} + +/* Description of flist elements + * tartget : We done need this field. Ignore. + * format : Fourcc name. Converting to char pointer. + * plane : Num of planes. + * width, height + */ +int vipx_graph_format(struct vipx_queue *queue, struct vs4l_format_list *flist) +{ + int ret = 0; + struct vipx_graph *graph; + struct vipx_vertex_ctx *vctx; + struct vipx_format_list *in_list; + struct vipx_format_list *ot_list; + int cnt = 0; + + BUG_ON(!queue); + BUG_ON(!flist); + + vctx = container_of(queue, struct vipx_vertex_ctx, queue); + graph = container_of(vctx, struct vipx_graph, vctx); + in_list = &graph->informat_list; + ot_list = &graph->otformat_list; + + if (flist->direction == VS4L_DIRECTION_IN) { + if (in_list->count != flist->count) { + if (in_list->formats) + kfree(in_list->formats); + + in_list->count = flist->count; + in_list->formats = kzalloc(in_list->count * sizeof(struct vipx_format), GFP_KERNEL); + if (!in_list->formats) { + ret = -ENOMEM; + goto p_err; + } + + for (cnt = 0; cnt < in_list->count; cnt++) { + vipx_info("[%d] size (%dx%d), format(%d), plane(%d)\n", cnt, + flist->formats[cnt].width, + flist->formats[cnt].height, + flist->formats[cnt].format, + flist->formats[cnt].plane); + + in_list->formats[cnt].width = flist->formats[cnt].width; + in_list->formats[cnt].height = flist->formats[cnt].height; + in_list->formats[cnt].format = flist->formats[cnt].format; + in_list->formats[cnt].plane = flist->formats[cnt].plane; + } + } + } else if (flist->direction == VS4L_DIRECTION_OT) { + if (ot_list->count != flist->count) { + if (ot_list->formats) + kfree(ot_list->formats); + + ot_list->count = flist->count; + ot_list->formats = kzalloc(ot_list->count * sizeof(struct vipx_format), GFP_KERNEL); + if (!ot_list->formats) { + ret = -ENOMEM; + goto p_err; + } + + for (cnt = 0; cnt < ot_list->count; cnt++) { + vipx_info("[%d] size (%dx%d), format(%d), plane(%d)\n", cnt, + flist->formats[cnt].width, + flist->formats[cnt].height, + flist->formats[cnt].format, + flist->formats[cnt].plane); + + ot_list->formats[cnt].width = flist->formats[cnt].width; + ot_list->formats[cnt].height = flist->formats[cnt].height; + ot_list->formats[cnt].format = flist->formats[cnt].format; + ot_list->formats[cnt].plane = flist->formats[cnt].plane; + } + } + } else { + vipx_err("invalid direction(%d)\n", flist->direction); + ret = -EINVAL; + goto p_err; + } + + vipx_info("%s:%d, count in/out (%d/%d)\n", __func__, ret, + graph->informat_list.count, + graph->otformat_list.count); + return ret; + +p_err: + if (in_list->formats) + kfree(in_list->formats); + in_list->formats = 0; + + if (ot_list->formats) + kfree(ot_list->formats); + ot_list->formats = 0; + + return ret; +} + +static int vipx_graph_queue(struct vipx_queue *queue, struct vb_container_list *incl, struct vb_container_list *otcl) +{ + int ret = 0; + unsigned long flag; + struct vipx_graph *graph; + struct vipx_vertex_ctx *vctx; + struct vipx_taskmgr *taskmgr; + struct vipx_task *task; + int i = 0, j = 0; + + BUG_ON(!queue); + BUG_ON(!incl); + vipx_warn("ASDFASDFASDF : %d, %d\n",incl->index, otcl->index); + //BUG_ON(incl->index < VIPX_MAX_TASK); + BUG_ON(!otcl); + //BUG_ON(otcl->index < VIPX_MAX_TASK); + + vctx = container_of(queue, struct vipx_vertex_ctx, queue); + graph = container_of(vctx, struct vipx_graph, vctx); + taskmgr = &graph->taskmgr; + + if (!test_bit(VIPX_GRAPH_STATE_START, &graph->state)) { + vipx_ierr("graph is NOT start\n", graph); + ret = -EINVAL; + goto p_err; + } + + if (incl->id != otcl->id) { + vipx_warn("buffer id is incoincidence(%d, %d)\n", incl->id, otcl->id); + otcl->id = incl->id; + } + + taskmgr_e_barrier_irqs(taskmgr, 0, flag); + vipx_task_pick_fre_to_req(taskmgr, &task); + taskmgr_x_barrier_irqr(taskmgr, 0, flag); + + if (!task) { + vipx_ierr("task is lack\n", graph); + vipx_task_print_all(taskmgr); + ret = -ENOMEM; + goto p_err; + } + + graph->inhash[incl->index] = task->index; + graph->othash[otcl->index] = task->index; + graph->input_cnt++; + + vipx_dbg("in-container list: dir(%d), id(%d), index(%d), flags(%lx), count(%d)\n", + incl->direction, incl->id, incl->index, incl->flags, incl->count); + for (i = 0; i < incl->count; i++) { + vipx_dbg("in-containers[%d] type(%d), target(%d), memory(%d), count(%d)\n", i, + incl->containers[i].type, incl->containers[i].target, + incl->containers[i].memory, incl->containers[i].count); + + for (j = 0; j < incl->containers[i].count; j++) { + vipx_dbg("in-buffer[%d] fd(%d), kvaddr(%p), dvaddr(%p)\n", j, + incl->containers[i].buffers[j].m.fd, + incl->containers[i].buffers[j].kvaddr, + (void *)incl->containers[i].buffers[j].dvaddr); + } + } + + vipx_dbg("out-container list: dir(%d), id(%d), index(%d), flags(%lx), count(%d)\n", + otcl->direction, otcl->id, otcl->index, otcl->flags, otcl->count); + for (i = 0; i < otcl->count; i++) { + vipx_dbg("out-containers[%d] type(%d), target(%d), memory(%d), count(%d)\n", i, + otcl->containers[i].type, otcl->containers[i].target, + otcl->containers[i].memory, otcl->containers[i].count); + + for (j = 0; j < otcl->containers[i].count; j++) { + vipx_dbg("out-buffer[%d] fd(%d), kvaddr(%p), dvaddr(%p)\n", j, + otcl->containers[i].buffers[j].m.fd, + otcl->containers[i].buffers[j].kvaddr, + (void *)otcl->containers[i].buffers[j].dvaddr); + } + } + + task->id = incl->id; + task->incl = incl; + task->otcl = otcl; + task->message = VIPX_TASK_REQUEST; + task->param0 = 0; + task->param1 = 0; + task->param2 = 0; + task->param3 = 0; + clear_bit(VS4L_CL_FLAG_TIMESTAMP, &task->flags); + + if ((incl->flags & (1 << VS4L_CL_FLAG_TIMESTAMP)) || + (otcl->flags & (1 << VS4L_CL_FLAG_TIMESTAMP))) { + set_bit(VS4L_CL_FLAG_TIMESTAMP, &task->flags); + vipx_get_timestamp(&task->time[VIPX_TMP_QUEUE]); + } + + vipx_graphmgr_queue(graph->cookie, task); + +p_err: + vipx_dbg("%s:%d\n", __func__, ret); + return ret; +} + +static int vipx_graph_deque(struct vipx_queue *queue, struct vb_container_list *clist) +{ + int ret = 0; + u32 findex; + unsigned long flags; + struct vipx_graph *graph; + struct vipx_vertex_ctx *vctx; + struct vipx_taskmgr *taskmgr; + struct vipx_task *task; + + BUG_ON(!queue); + BUG_ON(!clist); + //BUG_ON(clist->index < VIPX_MAX_TASK); + + vctx = container_of(queue, struct vipx_vertex_ctx, queue); + graph = container_of(vctx, struct vipx_graph, vctx); + taskmgr = &graph->taskmgr; + + if (!test_bit(VIPX_GRAPH_STATE_START, &graph->state)) { + vipx_ierr("graph is NOT start\n", graph); + ret = -EINVAL; + goto p_err; + } + + if (clist->direction == VS4L_DIRECTION_IN) + findex = graph->inhash[clist->index]; + else + findex = graph->othash[clist->index]; + + if (findex >= VIPX_MAX_TASK) { + vipx_ierr("task index(%d) invalid\n", graph, findex); + BUG(); + } + + task = &taskmgr->task[findex]; + if (task->state != VIPX_TASK_STATE_COMPLETE) { + vipx_ierr("task state(%d) is invalid\n", graph, task->state); + BUG(); + } + + if (clist->direction == VS4L_DIRECTION_IN) { + if (task->incl != clist) { + vipx_ierr("incl ptr is invalid(%p != %p)\n", graph, task->incl, clist); + BUG(); + } + + graph->inhash[clist->index] = VIPX_MAX_TASK; + task->incl = NULL; + } else { + if (task->otcl != clist) { + vipx_ierr("otcl ptr is invalid(%p != %p)\n", graph, task->otcl, clist); + BUG(); + } + + graph->othash[clist->index] = VIPX_MAX_TASK; + task->otcl = NULL; + } + + if (task->incl || task->otcl) + goto p_err; + + taskmgr_e_barrier_irqs(taskmgr, 0, flags); + vipx_task_trans_com_to_fre(taskmgr, task); + taskmgr_x_barrier_irqr(taskmgr, 0, flags); + +p_err: + vipx_dbg("%s:%d\n", __func__, ret); + return ret; +} + +const struct vipx_queue_ops vipx_queue_ops = { + .start = vipx_graph_start, + .stop = vipx_graph_stop, + .format = vipx_graph_format, + .queue = vipx_graph_queue, + .deque = vipx_graph_deque +}; + +static int vipx_graph_control(struct vipx_graph *graph, struct vipx_task *task) +{ + int ret = 0; + struct vipx_taskmgr *taskmgr; + + BUG_ON(!graph); + BUG_ON(!task); + + taskmgr = &graph->taskmgr; + + if (&graph->control != task) { + vipx_ierr("control task is invalid(%p == %p)\n", graph, &graph->control, task); + BUG(); + } + + switch (task->message) { + case VIPX_CTRL_STOP: + graph->control.message = VIPX_CTRL_STOP_DONE; + wake_up(&graph->control_wq); + break; + default: + vipx_ierr("unresolved message(%d)\n", graph, task->message); + vipx_task_print_all(taskmgr); + BUG(); + break; + } + + vipx_iinfo("%s:%d\n", graph, __func__, ret); + return ret; +} + +static int vipx_graph_request(struct vipx_graph *graph, struct vipx_task *task) +{ + int ret = 0; + unsigned long flags; + struct vipx_taskmgr *taskmgr; + + BUG_ON(!graph); + BUG_ON(!task); + + taskmgr = &graph->taskmgr; + + if (task->state != VIPX_TASK_STATE_REQUEST) { + vipx_ierr("task state(%d) is invalid\n", graph, task->state); + BUG(); + } + + taskmgr_e_barrier_irqs(taskmgr, 0, flags); + vipx_task_trans_req_to_pre(taskmgr, task); + taskmgr_x_barrier_irqr(taskmgr, 0, flags); + + if (test_bit(VS4L_CL_FLAG_TIMESTAMP, &task->flags)) + vipx_get_timestamp(&task->time[VIPX_TMP_REQUEST]); + + vipx_idbg("%s:%d\n", graph, __func__, ret); + return ret; +} + +static int vipx_graph_process(struct vipx_graph *graph, struct vipx_task *task) +{ + int ret = 0; + unsigned long flags; + struct vipx_taskmgr *taskmgr; + + BUG_ON(!graph); + BUG_ON(!task); + + taskmgr = &graph->taskmgr; + + if (task->state != VIPX_TASK_STATE_PREPARE) { + vipx_ierr("task state(%d) is invalid\n", graph, task->state); + BUG(); + } + + taskmgr_e_barrier_irqs(taskmgr, TASKMGR_IDX_0, flags); + vipx_task_trans_pre_to_pro(taskmgr, task); + taskmgr_x_barrier_irqr(taskmgr, TASKMGR_IDX_0, flags); + +#ifdef DBG_STREAMING + vipx_iinfo("PROCESS(%d, %d)\n", graph, task->index, task->id); +#endif + + if (test_bit(VS4L_CL_FLAG_TIMESTAMP, &task->flags)) + vipx_get_timestamp(&task->time[VIPX_TMP_PROCESS]); + + vipx_idbg("%s:%d\n", graph, __func__, ret); + return ret; +} + +static int vipx_graph_cancel(struct vipx_graph *graph, struct vipx_task *task) +{ + int ret = 0; + unsigned long flags; + unsigned long result; + struct vipx_taskmgr *taskmgr; + struct vipx_queue *queue; + struct vb_container_list *incl, *otcl; + + BUG_ON(!graph); + BUG_ON(!task); + + taskmgr = &graph->taskmgr; + queue = &graph->vctx.queue; + incl = task->incl; + otcl = task->otcl; + result = 0; + + if (!test_bit(VIPX_GRAPH_STATE_START, &graph->state)) { + vipx_ierr("graph is NOT start\n", graph); + BUG(); + } + + if (task->state != VIPX_TASK_STATE_PROCESS) { + vipx_ierr("task state(%d) is invalid\n", graph, task->state); + BUG(); + } + + if (test_bit(VS4L_CL_FLAG_TIMESTAMP, &task->flags)) { + vipx_get_timestamp(&task->time[VIPX_TMP_DONE]); + + if (incl->flags & (1 << VS4L_CL_FLAG_TIMESTAMP)) + memcpy(incl->timestamp, task->time, sizeof(task->time)); + + if (otcl->flags & (1 << VS4L_CL_FLAG_TIMESTAMP)) + memcpy(otcl->timestamp, task->time, sizeof(task->time)); + +#ifdef DBG_TIMEMEASURE + vipx_irinfo("[TM] G%d : QR(%ld), RR(%ld), RP(%ld), PD(%ld)\n", graph, task, graph->uid, + VIPX_TIME_IN_US(task->time[VIPX_TMP_REQUEST]) - + VIPX_TIME_IN_US(task->time[VIPX_TMP_QUEUE]), + VIPX_TIME_IN_US(task->time[VIPX_TMP_RESOURCE]) - + VIPX_TIME_IN_US(task->time[VIPX_TMP_REQUEST]), + VIPX_TIME_IN_US(task->time[VIPX_TMP_PROCESS]) - + VIPX_TIME_IN_US(task->time[VIPX_TMP_RESOURCE]), + VIPX_TIME_IN_US(task->time[VIPX_TMP_DONE]) - + VIPX_TIME_IN_US(task->time[VIPX_TMP_PROCESS])); +#endif + } + +#ifdef DBG_STREAMING + vipx_iinfo("NDONE(%d, %d)\n", graph, task->index, task->id); +#endif + set_bit(VS4L_CL_FLAG_DONE, &result); + set_bit(VS4L_CL_FLAG_INVALID, &result); + + taskmgr_e_barrier_irqs(taskmgr, TASKMGR_IDX_0, flags); + vipx_task_trans_pro_to_com(taskmgr, task); + taskmgr_x_barrier_irqr(taskmgr, TASKMGR_IDX_0, flags); + + graph->recent = task->id; + graph->done_cnt++; + vipx_queue_done(queue, incl, otcl, result); + + vipx_iinfo("%s:%d\n", graph, __func__, ret); + return ret; +} + +static int vipx_graph_done(struct vipx_graph *graph, struct vipx_task *task) +{ + int ret = 0; + unsigned long flags; + unsigned long result; + struct vipx_taskmgr *taskmgr; + struct vipx_queue *queue; + struct vb_container_list *incl, *otcl; + + BUG_ON(!graph); + BUG_ON(!task); + + taskmgr = &graph->taskmgr; + queue = &graph->vctx.queue; + incl = task->incl; + otcl = task->otcl; + result = 0; + + if (!test_bit(VIPX_GRAPH_STATE_START, &graph->state)) { + vipx_ierr("graph is NOT start\n", graph); + BUG(); + } + + if (task->state != VIPX_TASK_STATE_PROCESS) { + vipx_ierr("task state(%d) is invalid\n", graph, task->state); + BUG(); + } + + if (test_bit(VS4L_CL_FLAG_TIMESTAMP, &task->flags)) { + vipx_get_timestamp(&task->time[VIPX_TMP_DONE]); + + if (incl->flags & (1 << VS4L_CL_FLAG_TIMESTAMP)) + memcpy(incl->timestamp, task->time, sizeof(task->time)); + + if (otcl->flags & (1 << VS4L_CL_FLAG_TIMESTAMP)) + memcpy(otcl->timestamp, task->time, sizeof(task->time)); + +#ifdef DBG_TIMEMEASURE + vipx_irinfo("[TM] G%d : QR(%ld), RR(%ld), RP(%ld), PD(%ld)\n", graph, task, graph->uid, + VIPX_TIME_IN_US(task->time[VIPX_TMP_REQUEST]) - + VIPX_TIME_IN_US(task->time[VIPX_TMP_QUEUE]), + VIPX_TIME_IN_US(task->time[VIPX_TMP_RESOURCE]) - + VIPX_TIME_IN_US(task->time[VIPX_TMP_REQUEST]), + VIPX_TIME_IN_US(task->time[VIPX_TMP_PROCESS]) - + VIPX_TIME_IN_US(task->time[VIPX_TMP_RESOURCE]), + VIPX_TIME_IN_US(task->time[VIPX_TMP_DONE]) - + VIPX_TIME_IN_US(task->time[VIPX_TMP_PROCESS])); +#endif + } + + if (task->param0) { +#ifdef DBG_STREAMING + vipx_iinfo("NDONE(%d, %d)\n", graph, task->index, task->id); +#endif + set_bit(VS4L_CL_FLAG_DONE, &result); + set_bit(VS4L_CL_FLAG_INVALID, &result); + } else { +#ifdef DBG_STREAMING + vipx_iinfo("DONE(%d, %d)\n", graph, task->index, task->id); +#endif + set_bit(VS4L_CL_FLAG_DONE, &result); + } + + taskmgr_e_barrier_irqs(taskmgr, TASKMGR_IDX_0, flags); + vipx_task_trans_pro_to_com(taskmgr, task); + taskmgr_x_barrier_irqr(taskmgr, TASKMGR_IDX_0, flags); + + graph->recent = task->id; + graph->done_cnt++; + vipx_queue_done(queue, incl, otcl, result); + + vipx_idbg("%s:%d\n", graph, __func__, ret); + return ret; +} + +static int vipx_graph_update_param(struct vipx_graph *graph, struct vipx_task *task) +{ + int ret = 0; + + vipx_iinfo("%s:%d\n", graph, __func__, ret); + return ret; +} + +const struct vipx_graph_ops vipx_graph_ops = { + .control = vipx_graph_control, + .request = vipx_graph_request, + .process = vipx_graph_process, + .cancel = vipx_graph_cancel, + .done = vipx_graph_done, + .update_param = vipx_graph_update_param +}; diff --git a/drivers/vision/vipx/vipx-graph.h b/drivers/vision/vipx/vipx-graph.h new file mode 100644 index 000000000000..c9616261998d --- /dev/null +++ b/drivers/vision/vipx/vipx-graph.h @@ -0,0 +1,110 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef VIPX_GRAPH_H_ +#define VIPX_GRAPH_H_ + +#include + +#include "vs4l.h" +#include "vipx-taskmgr.h" +#include "vipx-vertex.h" +#include "vipx-interface.h" + +#define VIPX_GRAPH_MAX_VERTEX 20 +#define VIPX_GRAPH_MAX_PRIORITY 20 +#define VIPX_GRAPH_MAX_INTERMEDIATE 64 +#define VIPX_GRAPH_MAX_LEVEL 20 +#define VIPX_GRAPH_STOP_TIMEOUT (3 * HZ) + +struct vipx_graph; + +enum vipx_graph_state { + VIPX_GRAPH_STATE_CONFIG, + VIPX_GRAPH_STATE_HENROLL, + VIPX_GRAPH_STATE_HMAPPED, + VIPX_GRAPH_STATE_MMAPPED, + VIPX_GRAPH_STATE_START, +}; + +enum vipx_graph_flag { + VIPX_GRAPH_FLAG_UPDATE_PARAM = VS4L_GRAPH_FLAG_END +}; + +struct vipx_format { + u32 format; + u32 plane; + u32 width; + u32 height; +}; + +struct vipx_format_list { + u32 count; + struct vipx_format *formats; +}; + +struct vipx_graph_ops { + int (*control)(struct vipx_graph *graph, struct vipx_task *task); + int (*request)(struct vipx_graph *graph, struct vipx_task *task); + int (*process)(struct vipx_graph *graph, struct vipx_task *task); + int (*cancel)(struct vipx_graph *graph, struct vipx_task *task); + int (*done)(struct vipx_graph *graph, struct vipx_task *task); + int (*update_param)(struct vipx_graph *graph, struct vipx_task *task); +}; + +struct vipx_graph_intermediate { + u32 buffer_index; + u32 *buffer; + void *handle; + int fd; +}; + +struct vipx_graph { + u32 idx; + u32 uid; + unsigned long state; + unsigned long flags; + u32 priority; + struct mutex local_lock; + struct mutex *global_lock; + + /* for debugging */ + u32 input_cnt; + u32 cancel_cnt; + u32 done_cnt; + u32 recent; + + const struct vipx_graph_ops *gops; + + void *cookie; + void *memory; + struct vipx_pipe *pipe; + struct vipx_vertex_ctx vctx; + + struct vipx_format_list informat_list; + struct vipx_format_list otformat_list; + + u32 inhash[VIPX_MAX_TASK]; + u32 othash[VIPX_MAX_TASK]; + struct vipx_taskmgr taskmgr; + struct vipx_task control; + wait_queue_head_t control_wq; +}; + +void vipx_graph_print(struct vipx_graph *graph); +int vipx_graph_create(struct vipx_graph **graph, void *cookie, void *memory); +int vipx_graph_destroy(struct vipx_graph *graph); +int vipx_graph_config(struct vipx_graph *graph, struct vs4l_graph *info); +int vipx_graph_param(struct vipx_graph *graph, struct vs4l_param_list *plist); + +#define CALL_GOPS(g, op, ...) (((g)->gops->op) ? ((g)->gops->op(g, ##__VA_ARGS__)) : 0) + +#endif diff --git a/drivers/vision/vipx/vipx-graphmgr.c b/drivers/vision/vipx/vipx-graphmgr.c new file mode 100755 index 000000000000..39555128daba --- /dev/null +++ b/drivers/vision/vipx/vipx-graphmgr.c @@ -0,0 +1,1171 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + + +#include "vipx-taskmgr.h" +#include "vipx-device.h" +#include "vipx-graphmgr.h" + +/* + * task trans + */ + +static void __vipx_taskdesc_s_free(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + + taskdesc->state = VIPX_TASKDESC_STATE_FREE; + + list_add_tail(&taskdesc->list, &graphmgr->tdfre_list); + graphmgr->tdfre_cnt++; +} + +static void __vipx_taskdesc_free_head(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc **taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + + if (graphmgr->tdfre_cnt) + *taskdesc = container_of(graphmgr->tdfre_list.next, struct vipx_taskdesc, list); + else + *taskdesc = NULL; +} + +static void __vipx_taskdesc_s_ready(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + + taskdesc->state = VIPX_TASKDESC_STATE_FREE; + + list_add_tail(&taskdesc->list, &graphmgr->tdrdy_list); + graphmgr->tdrdy_cnt++; +} + +static void __vipx_taskdesc_g_ready(struct vipx_graphmgr *graphmgr, + struct vipx_taskdesc **taskdesc) +{ + if (graphmgr->tdrdy_cnt && + (*taskdesc = container_of(graphmgr->tdrdy_list.next, struct vipx_taskdesc, list))) { + list_del(&(*taskdesc)->list); + graphmgr->tdrdy_cnt--; + (*taskdesc)->state = VIPX_TASK_STATE_INVALID; + } else { + *taskdesc = NULL; + } +} + +static void __vipx_taskdesc_s_request(struct vipx_graphmgr *graphmgr, + struct vipx_taskdesc *prev, + struct vipx_taskdesc *taskdesc, + struct vipx_taskdesc *next) +{ + taskdesc->state = VIPX_TASKDESC_STATE_REQUEST; + + if (prev && next) { + next->list.prev = &taskdesc->list; + taskdesc->list.next = &next->list; + taskdesc->list.prev = &prev->list; + prev->list.next = &taskdesc->list; + } else if (prev) { + list_add_tail(&taskdesc->list, &graphmgr->tdreq_list); + } else { + list_add(&taskdesc->list, &graphmgr->tdreq_list); + } + + graphmgr->tdreq_cnt++; +} + +static void __vipx_taskdesc_s_alloc(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + taskdesc->state = VIPX_TASKDESC_STATE_ALLOC; + list_add_tail(&taskdesc->list, &graphmgr->tdalc_list); + graphmgr->tdalc_cnt++; +} + +void __vipx_taskdesc_s_process(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + + taskdesc->state = VIPX_TASKDESC_STATE_PROCESS; + list_add_tail(&taskdesc->list, &graphmgr->tdpro_list); + graphmgr->tdpro_cnt++; +} + +static void __vipx_taskdesc_s_complete(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + + taskdesc->state = VIPX_TASKDESC_STATE_COMPLETE; + list_add_tail(&taskdesc->list, &graphmgr->tdcom_list); + graphmgr->tdcom_cnt++; +} + +void __vipx_taskdesc_trans_fre_to_rdy(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + BUG_ON(!graphmgr->tdfre_cnt); + BUG_ON(taskdesc->state != VIPX_TASKDESC_STATE_FREE); + + list_del(&taskdesc->list); + graphmgr->tdfre_cnt--; + __vipx_taskdesc_s_ready(graphmgr, taskdesc); +} + +void __vipx_taskdesc_trans_rdy_to_fre(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + BUG_ON(!graphmgr->tdrdy_cnt); + BUG_ON(taskdesc->state != VIPX_TASKDESC_STATE_READY); + + list_del(&taskdesc->list); + graphmgr->tdrdy_cnt--; + __vipx_taskdesc_s_free(graphmgr, taskdesc); +} + +void __vipx_taskdesc_trans_req_to_alc(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + BUG_ON(!graphmgr->tdreq_cnt); + BUG_ON(taskdesc->state != VIPX_TASKDESC_STATE_REQUEST); + + list_del(&taskdesc->list); + graphmgr->tdreq_cnt--; + __vipx_taskdesc_s_alloc(graphmgr, taskdesc); +} + +void __vipx_taskdesc_trans_req_to_pro(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + BUG_ON(!graphmgr->tdreq_cnt); + BUG_ON(taskdesc->state != VIPX_TASKDESC_STATE_REQUEST); + + list_del(&taskdesc->list); + graphmgr->tdreq_cnt--; + __vipx_taskdesc_s_process(graphmgr, taskdesc); +} + +void __vipx_taskdesc_trans_req_to_fre(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + BUG_ON(!graphmgr->tdreq_cnt); + BUG_ON(taskdesc->state != VIPX_TASKDESC_STATE_REQUEST); + + list_del(&taskdesc->list); + graphmgr->tdreq_cnt--; + __vipx_taskdesc_s_free(graphmgr, taskdesc); +} + +void __vipx_taskdesc_trans_alc_to_pro(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + BUG_ON(!graphmgr->tdalc_cnt); + BUG_ON(taskdesc->state != VIPX_TASKDESC_STATE_ALLOC); + + list_del(&taskdesc->list); + graphmgr->tdalc_cnt--; + __vipx_taskdesc_s_process(graphmgr, taskdesc); +} + +void __vipx_taskdesc_trans_alc_to_fre(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + BUG_ON(!graphmgr->tdalc_cnt); + BUG_ON(taskdesc->state != VIPX_TASKDESC_STATE_ALLOC); + + list_del(&taskdesc->list); + graphmgr->tdalc_cnt--; + __vipx_taskdesc_s_free(graphmgr, taskdesc); +} + +static void __vipx_taskdesc_trans_pro_to_com(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + BUG_ON(!graphmgr->tdpro_cnt); + BUG_ON(taskdesc->state != VIPX_TASKDESC_STATE_PROCESS); + + list_del(&taskdesc->list); + graphmgr->tdpro_cnt--; + __vipx_taskdesc_s_complete(graphmgr, taskdesc); +} + +void __vipx_taskdesc_trans_pro_to_fre(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + BUG_ON(!graphmgr->tdpro_cnt); + BUG_ON(taskdesc->state != VIPX_TASKDESC_STATE_PROCESS); + + list_del(&taskdesc->list); + graphmgr->tdpro_cnt--; + __vipx_taskdesc_s_free(graphmgr, taskdesc); +} + +static void __vipx_taskdesc_trans_com_to_fre(struct vipx_graphmgr *graphmgr, struct vipx_taskdesc *taskdesc) +{ + BUG_ON(!graphmgr); + BUG_ON(!taskdesc); + BUG_ON(!graphmgr->tdcom_cnt); + BUG_ON(taskdesc->state != VIPX_TASKDESC_STATE_COMPLETE); + + list_del(&taskdesc->list); + graphmgr->tdcom_cnt--; + __vipx_taskdesc_s_free(graphmgr, taskdesc); +} + +/* + * task trans END + */ + +void vipx_taskdesc_print(struct vipx_graphmgr *graphmgr) +{ +} + +#ifdef USE_TASK_TIMER +static int vipx_time_thread(void *data) +{ + int ret = 0; + + return ret; +} +#endif + +static int __vipx_itf_init(struct vipx_interface *interface, + struct vipx_graph *graph) +{ + int ret = 0; + unsigned long flags; + struct vipx_taskmgr *itaskmgr; + struct vipx_task *itask; + + BUG_ON(!interface); + BUG_ON(!graph); + + itaskmgr = &interface->taskmgr; + + ret = vipx_hw_enum(interface); + if (ret) { + vipx_err("vipx_hw_enum is fail(%d)\n", ret); + goto p_err; + } + + taskmgr_e_barrier_irqs(itaskmgr, 0, flags); + vipx_task_pick_fre_to_req(itaskmgr, &itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flags); + + if (!itask) { + vipx_err("itask is NULL\n"); + ret = -ENOMEM; + goto p_err; + } + + itask->id = 0; + itask->lock = &graph->local_lock; + itask->findex = VIPX_MAX_TASK; + itask->tdindex = VIPX_MAX_TASKDESC; + itask->message = VIPX_TASK_INIT; + itask->param0 = graph->uid; + itask->param1 = graph->idx; + itask->param2 = 0; + itask->param3 = 0; + + ret = vipx_hw_init(interface, itask); + if (ret) { + vipx_err("vipx_hw_init is fail(%d)\n", ret); + goto p_err; + } + +p_err: + vipx_info("%s:\n", __func__); + return ret; +} + +static int __vipx_itf_deinit(struct vipx_interface *interface, + struct vipx_graph *graph) +{ + int ret = 0; + unsigned long flags; + struct vipx_taskmgr *itaskmgr; + struct vipx_task *itask; + + BUG_ON(!interface); + BUG_ON(!graph); + + itaskmgr = &interface->taskmgr; + + taskmgr_e_barrier_irqs(itaskmgr, 0, flags); + vipx_task_pick_fre_to_req(itaskmgr, &itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flags); + + if (!itask) { + vipx_err("itask is NULL\n"); + ret = -ENOMEM; + goto p_err; + } + + itask->id = 0; + itask->lock = &graph->local_lock; + itask->findex = VIPX_MAX_TASK; + itask->tdindex = VIPX_MAX_TASKDESC; + itask->message = VIPX_TASK_DEINIT; + itask->param0 = graph->uid; + itask->param1 = graph->idx; + itask->param2 = 0; + itask->param3 = 0; + + ret = vipx_hw_deinit(interface, itask); + if (ret) { + vipx_err("vipx_hw_deinit is fail(%d)\n", ret); + goto p_err; + } + +p_err: + vipx_info("%s:\n", __func__); + return ret; +} + +int __vipx_itf_create(struct vipx_interface *interface, + struct vipx_graph *graph) +{ + int ret = 0; + unsigned long flags; + struct vipx_taskmgr *itaskmgr; + struct vipx_task *itask; + + BUG_ON(!interface); + BUG_ON(!graph); + + itaskmgr = &interface->taskmgr; + + taskmgr_e_barrier_irqs(itaskmgr, 0, flags); + vipx_task_pick_fre_to_req(itaskmgr, &itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flags); + + if (!itask) { + vipx_err("itask is NULL\n"); + ret = -ENOMEM; + goto p_err; + } + +#ifdef DBG_TIMEMEASURE + vipx_get_timestamp(&itask->time[VIPX_TMP_REQUEST]); +#endif + + itask->id = 0; + itask->lock = &graph->local_lock; + itask->findex = VIPX_MAX_TASK; + itask->tdindex = VIPX_MAX_TASKDESC; + itask->message = VIPX_TASK_CREATE; + itask->param0 = graph->uid; + itask->param1 = graph->idx; + itask->param2 = (ulong)0; + itask->param3 = (ulong)0; + + ret = vipx_hw_create(interface, itask); + if (ret) { + vipx_err("vipx_hw_create is fail(%d)\n", ret); + goto p_err; + } + +#ifdef DBG_TIMEMEASURE + vipx_iinfo("[TM] C : %ldus, %ldus\n", graph, + VIPX_TIME_IN_US(itask->time[VIPX_TMP_PROCESS]) - + VIPX_TIME_IN_US(itask->time[VIPX_TMP_REQUEST]), + VIPX_TIME_IN_US(itask->time[VIPX_TMP_DONE]) - + VIPX_TIME_IN_US(itask->time[VIPX_TMP_PROCESS])); +#endif + +p_err: + vipx_info("%s:\n", __func__); + return ret; +} + +static int __vipx_itf_destroy(struct vipx_interface *interface, + struct vipx_graph *graph) +{ + int ret = 0; + unsigned long flags; + struct vipx_taskmgr *itaskmgr; + struct vipx_task *itask; + + BUG_ON(!interface); + BUG_ON(!graph); + + itaskmgr = &interface->taskmgr; + + taskmgr_e_barrier_irqs(itaskmgr, 0, flags); + vipx_task_pick_fre_to_req(itaskmgr, &itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flags); + + if (!itask) { + vipx_err("itask is NULL\n"); + ret = -ENOMEM; + goto p_err; + } + +#ifdef DBG_TIMEMEASURE + vipx_get_timestamp(&itask->time[VIPX_TMP_REQUEST]); +#endif + + itask->id = 0; + itask->lock = &graph->local_lock; + itask->findex = VIPX_MAX_TASK; + itask->tdindex = VIPX_MAX_TASKDESC; + itask->message = VIPX_TASK_DESTROY; + itask->param0 = graph->uid; + itask->param1 = graph->idx; + itask->param2 = 0; + itask->param3 = 0; + + ret = vipx_hw_destroy(interface, itask); + if (ret) { + vipx_err("vipx_hw_destory is fail(%d)\n", ret); + goto p_err; + } + +#ifdef DBG_TIMEMEASURE + vipx_iinfo("[TM] D : %ldus, %ldus\n", graph, + VIPX_TIME_IN_US(itask->time[VIPX_TMP_PROCESS]) - + VIPX_TIME_IN_US(itask->time[VIPX_TMP_REQUEST]), + VIPX_TIME_IN_US(itask->time[VIPX_TMP_DONE]) - + VIPX_TIME_IN_US(itask->time[VIPX_TMP_PROCESS])); +#endif + +p_err: + vipx_info("%s:\n", __func__); + return ret; +} + +static int __vipx_itf_config(struct vipx_interface *interface, + struct vipx_graph *graph) +{ + int ret = 0; + unsigned long flags; + struct vipx_taskmgr *itaskmgr; + struct vipx_task *itask; + + BUG_ON(!interface); + BUG_ON(!graph); + + itaskmgr = &interface->taskmgr; + + taskmgr_e_barrier_irqs(itaskmgr, 0, flags); + vipx_task_pick_fre_to_req(itaskmgr, &itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flags); + + if (!itask) { + vipx_err("itask is NULL\n"); + ret = -ENOMEM; + goto p_err; + } + +#ifdef DBG_TIMEMEASURE + vipx_get_timestamp(&itask->time[VIPX_TMP_REQUEST]); +#endif + + itask->id = 0; + itask->lock = &graph->local_lock; + itask->findex = VIPX_MAX_TASK; + itask->tdindex = VIPX_MAX_TASKDESC; + itask->message = VIPX_TASK_ALLOCATE; + itask->param0 = graph->uid; + itask->param1 = graph->idx; + itask->param2 = (ulong)&graph->informat_list; + itask->param3 = (ulong)&graph->otformat_list; + + ret = vipx_hw_config(interface, itask); + if (ret) { + vipx_err("vipx_hw_config is fail(%d)\n", ret); + goto p_err; + } + +#ifdef DBG_TIMEMEASURE + vipx_iinfo("[TM] A : %ldus, %ldus\n", graph, + VIPX_TIME_IN_US(itask->time[VIPX_TMP_PROCESS]) - + VIPX_TIME_IN_US(itask->time[VIPX_TMP_REQUEST]), + VIPX_TIME_IN_US(itask->time[VIPX_TMP_DONE]) - + VIPX_TIME_IN_US(itask->time[VIPX_TMP_PROCESS])); +#endif + +p_err: + vipx_info("%s:\n", __func__); + return ret; +} + +static int __vipx_itf_process(struct vipx_interface *interface, + struct vipx_graph *graph, + struct vipx_task *task) +{ + int ret = 0; + unsigned long flags; + struct vipx_taskmgr *itaskmgr; + struct vipx_task *itask; + + BUG_ON(!interface); + BUG_ON(!graph); + BUG_ON(!task); + + itaskmgr = &interface->taskmgr; + + taskmgr_e_barrier_irqs(itaskmgr, 0, flags); + vipx_task_pick_fre_to_req(itaskmgr, &itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flags); + + if (!itask) { + vipx_err("itask is NULL\n"); + ret = -ENOMEM; + goto p_err; + } + + if (test_bit(VIPX_GRAPH_FLAG_UPDATE_PARAM, &graph->flags)) { + ret = CALL_GOPS(graph, update_param, task); + if (ret) { + vipx_err("GOPS(update_param) is fail(%d)\n", ret); + goto p_err; + } + + clear_bit(VIPX_GRAPH_FLAG_UPDATE_PARAM, &graph->flags); + } + + itask->id = task->id; + itask->lock = &graph->local_lock; + itask->findex = task->index; + itask->tdindex = task->tdindex; + itask->message = VIPX_TASK_PROCESS; + itask->param0 = graph->uid; + itask->param1 = graph->idx; + itask->param2 = (ulong)0; /* return : DONE or NDONE */ + itask->param3 = 0; /* return : error code if param2 is NDONE */ + itask->flags = task->flags; + itask->incl = task->incl; + itask->otcl = task->otcl; + + ret = CALL_GOPS(graph, process, task); + if (ret) { + vipx_err("GOPS(process) is fail(%d)\n", ret); + goto p_err; + } + + ret = vipx_hw_process(interface, itask); + if (ret) { + vipx_err("vipx_hw_process is fail(%d)\n", ret); + goto p_err; + } + +p_err: + vipx_dbg("%s:\n", __func__); + return ret; +} + +static void __vipx_graphmgr_sched(struct vipx_graphmgr *graphmgr) +{ + int ret = 0; + struct vipx_graph *graph; + struct vipx_task *task; + struct vipx_taskdesc *ready, *request, *process, *prev, *next, *temp; + struct vipx_interface *interface; + + interface = graphmgr->interface; + ready = NULL; + request = NULL; + prev = NULL; + next = NULL; + + mutex_lock(&graphmgr->tdlock); + + /* 1. priority order */ + while (1) { + __vipx_taskdesc_g_ready(graphmgr, &ready); + if (!ready) + break; + + list_for_each_entry_safe(next, temp, &graphmgr->tdreq_list, list) { + if (ready->priority > next->priority) + break; + + prev = next; + next = NULL; + } + + __vipx_taskdesc_s_request(graphmgr, prev, ready, next); + } + + /* 2. */ + list_for_each_entry_safe(request, temp, &graphmgr->tdreq_list, list) { + graph = request->graph; + task = request->task; + + __vipx_taskdesc_trans_req_to_pro(graphmgr, request); + } + + mutex_unlock(&graphmgr->tdlock); + + /* 3. process graph */ + list_for_each_entry_safe(process, temp, &graphmgr->tdpro_list, list) { + graph = process->graph; + task = process->task; + + ret = __vipx_itf_process(interface, graph, task); + if (ret) { + vipx_err("__vipx_itf_process is fail(%d)\n", ret); + + ret = CALL_GOPS(graph, cancel, task); + if (ret) { + vipx_err("CALL_GOPS(cancel) is fail(%d)\n", ret); + BUG(); + } + + process->graph = NULL; + process->task = NULL; + __vipx_taskdesc_trans_pro_to_fre(graphmgr, process); + continue; + } + + __vipx_taskdesc_trans_pro_to_com(graphmgr, process); + } + + graphmgr->sched_cnt++; + vipx_dbg("[%s:%d]\n", __func__, __LINE__); +} + +static void vipx_graph_thread(struct kthread_work *work) +{ + int ret = 0; + struct vipx_graphmgr *graphmgr; + struct vipx_graph *graph; + struct vipx_task *task; + struct vipx_taskdesc *taskdesc, * temp; + + BUG_ON(!work); + + task = container_of(work, struct vipx_task, work); + graph = task->owner; + graphmgr = graph->cookie; + + switch (task->message) { + case VIPX_TASK_REQUEST: + ret = CALL_GOPS(graph, request, task); + if (ret) { + vipx_err("CALL_GOPS(request) is fail(%d)\n", ret); + BUG(); + } + + __vipx_taskdesc_free_head(graphmgr, &taskdesc); + if (!taskdesc) { + vipx_err("taskdesc is NULL\n"); + BUG(); + } + + task->tdindex = taskdesc->index; + taskdesc->graph = graph; + taskdesc->task = task; + taskdesc->priority = graph->priority; + __vipx_taskdesc_trans_fre_to_rdy(graphmgr, taskdesc); + break; + case VIPX_CTRL_STOP: + list_for_each_entry_safe(taskdesc, temp, &graphmgr->tdrdy_list, list) { + if (taskdesc->graph->idx != graph->idx) + continue; + + ret = CALL_GOPS(graph, cancel, taskdesc->task); + if (ret) { + vipx_err("CALL_GOPS(cancel) is fail(%d)\n", ret); + BUG(); + } + + taskdesc->graph = NULL; + taskdesc->task = NULL; + __vipx_taskdesc_trans_rdy_to_fre(graphmgr, taskdesc); + } + + mutex_lock(&graphmgr->tdlock); + + list_for_each_entry_safe(taskdesc, temp, &graphmgr->tdreq_list, list) { + if (taskdesc->graph->idx != graph->idx) + continue; + + ret = CALL_GOPS(graph, cancel, taskdesc->task); + if (ret) { + vipx_err("CALL_GOPS(cancel) is fail(%d)\n", ret); + BUG(); + } + + taskdesc->graph = NULL; + taskdesc->task = NULL; + __vipx_taskdesc_trans_req_to_fre(graphmgr, taskdesc); + } + + mutex_unlock(&graphmgr->tdlock); + + ret = CALL_GOPS(graph, control, task); + if (ret) { + vipx_err("CALL_GOPS(control) is fail(%d)\n", ret); + BUG(); + } + return; + default: + BUG(); + break; + } + + __vipx_graphmgr_sched(graphmgr); + vipx_dbg("[%s:%d]\n", __func__, __LINE__); +} + +static void vipx_interface_thread(struct kthread_work *work) +{ + int ret = 0; + u32 task_index; + u32 taskdesc_index; + unsigned long flag; + struct vipx_graphmgr *graphmgr; + struct vipx_graph *graph; + struct vipx_taskmgr *itaskmgr; + struct vipx_task *task, *itask; + struct vipx_taskdesc *taskdesc; + struct vipx_interface *interface; + + BUG_ON(!work); + + itask = container_of(work, struct vipx_task, work); + interface = itask->owner; + itaskmgr = &interface->taskmgr; + graphmgr = interface->cookie; + + switch (itask->message) { + case VIPX_TASK_ALLOCATE: + task_index = itask->findex; + taskdesc_index = itask->tdindex; + + if (taskdesc_index >= VIPX_MAX_TASKDESC) { + vipx_err("taskdesc index(%d) is invalid\n", taskdesc_index); + BUG(); + } + + if (task_index >= VIPX_MAX_TASK) { + vipx_err("task index(%d) is invalid\n", task_index); + BUG(); + } + + taskdesc = &graphmgr->taskdesc[taskdesc_index]; + if (taskdesc->state != VIPX_TASKDESC_STATE_ALLOC) { + vipx_err("taskdesc state is invalid(%d)\n", taskdesc->state); + vipx_taskdesc_print(graphmgr); + BUG(); + } + + graph = taskdesc->graph; + if (!graph) { + vipx_err("graph is NULL(%d)\n", taskdesc_index); + BUG(); + } + + task = taskdesc->task; + if (!task) { + vipx_err("task is NULL(%d)\n", taskdesc_index); + BUG(); + } + + task->message = itask->message; + task->param0 = itask->param2; + task->param1 = itask->param3; + + /* return status check */ + if (task->param0) { + vipx_err("allocation is fail(%ld, %ld)\n", task->param0, task->param1); + + ret = CALL_GOPS(graph, cancel, task); + if (ret) { + vipx_err("CALL_GOPS(cancel) is fail(%d)\n", ret); + BUG(); + } + + /* taskdesc cleanup */ + mutex_lock(&graphmgr->tdlock); + taskdesc->graph = NULL; + taskdesc->task = NULL; + __vipx_taskdesc_trans_alc_to_fre(graphmgr, taskdesc); + mutex_unlock(&graphmgr->tdlock); + } else { + /* taskdesc transition */ + mutex_lock(&graphmgr->tdlock); + __vipx_taskdesc_trans_alc_to_pro(graphmgr, taskdesc); + mutex_unlock(&graphmgr->tdlock); + } + + /* itask cleanup */ + taskmgr_e_barrier_irqs(itaskmgr, 0, flag); + vipx_task_trans_com_to_fre(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flag); + break; + case VIPX_TASK_PROCESS: + task_index = itask->findex; + taskdesc_index = itask->tdindex; + + if (taskdesc_index >= VIPX_MAX_TASKDESC) { + vipx_err("taskdesc index(%d) is invalid\n", taskdesc_index); + BUG(); + } + + if (task_index >= VIPX_MAX_TASK) { + vipx_err("task index(%d) is invalid\n", task_index); + BUG(); + } + + taskdesc = &graphmgr->taskdesc[taskdesc_index]; + if (taskdesc->state == VIPX_TASKDESC_STATE_FREE) { + vipx_err("taskdesc state is FREE(%d)\n", taskdesc->state); + vipx_taskdesc_print(graphmgr); + + /* itask cleanup */ + taskmgr_e_barrier_irqs(itaskmgr, 0, flag); + vipx_task_trans_com_to_fre(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flag); + break; + } + + if (taskdesc->state != VIPX_TASKDESC_STATE_COMPLETE) { + vipx_err("taskdesc state is invalid(%d)\n", taskdesc->state); + vipx_taskdesc_print(graphmgr); + BUG(); + } + + graph = taskdesc->graph; + if (!graph) { + vipx_err("graph is NULL(%d)\n", taskdesc_index); + BUG(); + } + + task = taskdesc->task; + if (!task) { + vipx_err("task is NULL(%d)\n", taskdesc_index); + BUG(); + } + + task->message = itask->message; + task->tdindex = VIPX_MAX_TASKDESC; + task->param0 = itask->param2; + task->param1 = itask->param3; + + ret = CALL_GOPS(graph, done, task); + if (ret) { + vipx_err("CALL_GOPS(done) is fail(%d)\n", ret); + BUG(); + } + + /* taskdesc cleanup */ + taskdesc->graph = NULL; + taskdesc->task = NULL; + __vipx_taskdesc_trans_com_to_fre(graphmgr, taskdesc); + + /* itask cleanup */ + taskmgr_e_barrier_irqs(itaskmgr, 0, flag); + vipx_task_trans_com_to_fre(itaskmgr, itask); + taskmgr_x_barrier_irqr(itaskmgr, 0, flag); + break; + default: + BUG(); + break; + } + + __vipx_graphmgr_sched(graphmgr); + vipx_dbg("[%s:%d]\n", __func__, __LINE__); +} + +int vipx_graphmgr_grp_register(struct vipx_graphmgr *graphmgr, struct vipx_graph *graph) +{ + int ret = 0; + u32 index; + + BUG_ON(!graphmgr); + BUG_ON(!graph); + + mutex_lock(&graphmgr->mlock); + for (index = 0; index < VIPX_MAX_GRAPH; index++) { + if (!graphmgr->graph[index]) { + graphmgr->graph[index] = graph; + graph->idx = index; + break; + } + } + mutex_unlock(&graphmgr->mlock); + + if (index >= VIPX_MAX_GRAPH) { + vipx_err("graph slot is lack\n"); + ret = -EINVAL; + goto p_err; + } + + kthread_init_work(&graph->control.work, vipx_graph_thread); + for (index = 0; index < VIPX_MAX_TASK; ++index) + kthread_init_work(&graph->taskmgr.task[index].work, vipx_graph_thread); + + graph->global_lock = &graphmgr->mlock; + atomic_inc(&graphmgr->active_cnt); + +p_err: + vipx_info("[%s:%d]\n", __func__, __LINE__); + return ret; +} + +int vipx_graphmgr_grp_unregister(struct vipx_graphmgr *graphmgr, struct vipx_graph *graph) +{ + int ret = 0; + + BUG_ON(!graphmgr); + BUG_ON(!graph); + + mutex_lock(&graphmgr->mlock); + graphmgr->graph[graph->idx] = NULL; + mutex_unlock(&graphmgr->mlock); + + atomic_dec(&graphmgr->active_cnt); + + vipx_info("[%s:%d]\n", __func__, __LINE__); + return ret; +} + +int vipx_graphmgr_grp_start(struct vipx_graphmgr *graphmgr, struct vipx_graph *graph) +{ + int ret = 0; + + mutex_lock(&graphmgr->mlock); + if (test_bit(VIPX_GRAPHMGR_ENUM, &graphmgr->state)) { + mutex_unlock(&graphmgr->mlock); + goto p_skip_hw_init; + } + + ret = __vipx_itf_init(graphmgr->interface, graph); + if (ret) { + mutex_unlock(&graphmgr->mlock); + vipx_err("__vipx_itf_init is fail(%d)\n", ret); + goto p_err; + } + + set_bit(VIPX_GRAPHMGR_ENUM, &graphmgr->state); + mutex_unlock(&graphmgr->mlock); + +p_skip_hw_init: +#if 0 + ret = __vipx_itf_create(graphmgr->interface, graph); + if (ret) { + vipx_err("__vipx_itf_create is fail(%d)\n", ret); + goto p_err; + } +#endif + ret = __vipx_itf_config(graphmgr->interface, graph); + if (ret) { + vipx_err("__vipx_itf_config is fail(%d)\n", ret); + goto p_err; + } + +p_err: + vipx_info("[%s:%d]\n", __func__, __LINE__); + return ret; +} + +int vipx_graphmgr_grp_stop(struct vipx_graphmgr *graphmgr, struct vipx_graph *graph) +{ + int ret = 0; + + ret = __vipx_itf_destroy(graphmgr->interface, graph); + if (ret) { + vipx_err("__vipx_itf_destroy is fail(%d)\n", ret); + goto p_err; + } + + mutex_lock(&graphmgr->mlock); + if (test_bit(VIPX_GRAPHMGR_ENUM, &graphmgr->state) && + atomic_read(&graphmgr->active_cnt) == 1) { + ret = __vipx_itf_deinit(graphmgr->interface, graph); + if (ret) { + mutex_unlock(&graphmgr->mlock); + vipx_err("__vipx_itf_deinit is fail(%d)\n", ret); + goto p_err; + } + + clear_bit(VIPX_GRAPHMGR_ENUM, &graphmgr->state); + } + mutex_unlock(&graphmgr->mlock); + +p_err: + vipx_info("[%s:%d]\n", __func__, __LINE__); + return ret; +} + +int vipx_graphmgr_itf_register(struct vipx_graphmgr *graphmgr, struct vipx_interface *interface) +{ + int ret = 0; + u32 index; + + BUG_ON(!graphmgr); + BUG_ON(!interface); + + graphmgr->interface = interface; + for (index = 0; index < VIPX_MAX_TASK; ++index) + kthread_init_work(&interface->taskmgr.task[index].work, vipx_interface_thread); + + vipx_info("[%s:%d]\n", __func__, __LINE__); + return ret; +} + +int vipx_graphmgr_itf_unregister(struct vipx_graphmgr *graphmgr, struct vipx_interface *interface) +{ + int ret = 0; + BUG_ON(!graphmgr); + BUG_ON(!interface); + + graphmgr->interface = NULL; + + vipx_info("[%s:%d]\n", __func__, __LINE__); + return ret; +} + +void vipx_graphmgr_queue(struct vipx_graphmgr *graphmgr, struct vipx_task *task) +{ + BUG_ON(!graphmgr); + BUG_ON(!task); + + kthread_queue_work(&graphmgr->worker, &task->work); + vipx_dbg("[%s:%d]\n", __func__, __LINE__); +} + +int vipx_graphmgr_probe(struct vipx_graphmgr *graphmgr) +{ + int ret = 0; + u32 index; + struct vipx_device *device = container_of(graphmgr, struct vipx_device, graphmgr); + + BUG_ON(!graphmgr); + BUG_ON(!device); + + graphmgr->tick_cnt = 0; + graphmgr->tick_pos = 0; + graphmgr->sched_cnt = 0; + graphmgr->sched_pos = 0; + graphmgr->task_graph = NULL; +#ifdef USE_TASK_TIMER + graphmgr->task_timer = NULL; +#endif + atomic_set(&graphmgr->active_cnt, 0); + mutex_init(&graphmgr->mlock); + mutex_init(&graphmgr->tdlock); + clear_bit(VIPX_GRAPHMGR_OPEN, &graphmgr->state); + clear_bit(VIPX_GRAPHMGR_ENUM, &graphmgr->state); + + for (index = 0; index < VIPX_MAX_GRAPH; ++index) + graphmgr->graph[index] = NULL; + + INIT_LIST_HEAD(&graphmgr->tdfre_list); + INIT_LIST_HEAD(&graphmgr->tdrdy_list); + INIT_LIST_HEAD(&graphmgr->tdreq_list); + INIT_LIST_HEAD(&graphmgr->tdalc_list); + INIT_LIST_HEAD(&graphmgr->tdpro_list); + INIT_LIST_HEAD(&graphmgr->tdcom_list); + + graphmgr->tdfre_cnt = 0; + graphmgr->tdrdy_cnt = 0; + graphmgr->tdreq_cnt = 0; + graphmgr->tdalc_cnt = 0; + graphmgr->tdpro_cnt = 0; + graphmgr->tdcom_cnt = 0; + + for (index = 0; index < VIPX_MAX_TASKDESC; ++index) { + graphmgr->taskdesc[index].index = index; + graphmgr->taskdesc[index].graph = NULL; + graphmgr->taskdesc[index].task = NULL; + graphmgr->taskdesc[index].state = VIPX_TASKDESC_STATE_INVALID; + __vipx_taskdesc_s_free(graphmgr, &graphmgr->taskdesc[index]); + } + + vipx_info("[%s:%d]\n", __func__, __LINE__); + return ret; +} + +int vipx_graphmgr_open(struct vipx_graphmgr *graphmgr) +{ + int ret = 0; + char name[30]; + struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; + + BUG_ON(!graphmgr); + + kthread_init_worker(&graphmgr->worker); + snprintf(name, sizeof(name), "vipx_graph"); + graphmgr->task_graph = kthread_run(kthread_worker_fn, &graphmgr->worker, name); + if (IS_ERR_OR_NULL(graphmgr->task_graph)) { + vipx_err("kthread_run is fail\n"); + ret = -EINVAL; + goto p_err; + } + + ret = sched_setscheduler_nocheck(graphmgr->task_graph, SCHED_FIFO, ¶m); + if (ret) { + vipx_err("sched_setscheduler_nocheck is fail(%d)\n", ret); + goto p_err; + } + +#ifdef USE_TASK_TIMER + snprintf(name, sizeof(name), "vipx_timer"); + graphmgr->task_timer = kthread_run(vipx_time_thread, graphmgr, name); + if (IS_ERR_OR_NULL(graphmgr->task_timer)) { + vipx_err("kthread_run is fail\n"); + ret = -EINVAL; + goto p_err; + } + + ret = sched_setscheduler_nocheck(graphmgr->task_timer, SCHED_FIFO, ¶m); + if (ret) { + vipx_err("sched_setscheduler_nocheck is fail(%d)\n", ret); + goto p_err; + } +#endif + + set_bit(VIPX_GRAPHMGR_OPEN, &graphmgr->state); + +p_err: + vipx_info("[%s:%d]\n", __func__, __LINE__); + return ret; +} + +int vipx_graphmgr_close(struct vipx_graphmgr *graphmgr) +{ + int ret = 0; + +#ifdef USE_TASK_TIMER + kthread_stop(graphmgr->task_timer); +#endif + kthread_stop(graphmgr->task_graph); + + clear_bit(VIPX_GRAPHMGR_OPEN, &graphmgr->state); + clear_bit(VIPX_GRAPHMGR_ENUM, &graphmgr->state); + + vipx_info("[%s:%d]\n", __func__, __LINE__); + return ret; +} diff --git a/drivers/vision/vipx/vipx-graphmgr.h b/drivers/vision/vipx/vipx-graphmgr.h new file mode 100644 index 000000000000..4b78a81806c3 --- /dev/null +++ b/drivers/vision/vipx/vipx-graphmgr.h @@ -0,0 +1,96 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef VIPX_GRAPHMGR_H_ +#define VIPX_GRAPHMGR_H_ + +#include +#include + +#include "vipx-taskmgr.h" +#include "vipx-graph.h" +#include "vipx-interface.h" + +#define VIPX_MAX_TASKDESC (VIPX_MAX_GRAPH * 2) + +enum vipx_taskdesc_state { + VIPX_TASKDESC_STATE_INVALID, + VIPX_TASKDESC_STATE_FREE, + VIPX_TASKDESC_STATE_READY, + VIPX_TASKDESC_STATE_REQUEST, + VIPX_TASKDESC_STATE_ALLOC, + VIPX_TASKDESC_STATE_PROCESS, + VIPX_TASKDESC_STATE_COMPLETE +}; + +struct vipx_taskdesc { + struct list_head list; + u32 index; + u32 priority; + struct vipx_graph *graph; + struct vipx_task *task; + u32 state; +}; + +enum vipx_graphmgr_state { + VIPX_GRAPHMGR_OPEN, + VIPX_GRAPHMGR_ENUM +}; + +enum vipx_graphmgr_client { + VIPX_GRAPHMGR_CLIENT_GRAPH = 1, + VIPX_GRAPHMGR_CLIENT_INTERFACE +}; + +struct vipx_graphmgr { + struct vipx_graph *graph[VIPX_MAX_GRAPH]; + atomic_t active_cnt; + unsigned long state; + struct mutex mlock; + + u32 tick_cnt; + u32 tick_pos; + u32 sched_cnt; + u32 sched_pos; + struct kthread_worker worker; + struct task_struct *task_graph; + struct task_struct *task_timer; + + struct vipx_interface *interface; + + struct mutex tdlock; + struct vipx_taskdesc taskdesc[VIPX_MAX_TASKDESC]; + struct list_head tdfre_list; + struct list_head tdrdy_list; + struct list_head tdreq_list; + struct list_head tdalc_list; + struct list_head tdpro_list; + struct list_head tdcom_list; + u32 tdfre_cnt; + u32 tdrdy_cnt; + u32 tdreq_cnt; + u32 tdalc_cnt; + u32 tdpro_cnt; + u32 tdcom_cnt; +}; + +void vipx_taskdesc_print(struct vipx_graphmgr *graphmgr); +int vipx_graphmgr_probe(struct vipx_graphmgr *graphmgr); +int vipx_graphmgr_open(struct vipx_graphmgr *graphmgr); +int vipx_graphmgr_close(struct vipx_graphmgr *graphmgr); +int vipx_graphmgr_grp_register(struct vipx_graphmgr *graphmgr, struct vipx_graph *graph); +int vipx_graphmgr_grp_unregister(struct vipx_graphmgr *graphmgr, struct vipx_graph *graph); +int vipx_graphmgr_grp_start(struct vipx_graphmgr *graphmgr, struct vipx_graph *graph); +int vipx_graphmgr_grp_stop(struct vipx_graphmgr *graphmgr, struct vipx_graph *graph); +int vipx_graphmgr_itf_register(struct vipx_graphmgr *graphmgr, struct vipx_interface *interface); +int vipx_graphmgr_itf_unregister(struct vipx_graphmgr *graphmgr, struct vipx_interface *interface); +void vipx_graphmgr_queue(struct vipx_graphmgr *graphmgr, struct vipx_task *task); +#endif diff --git a/drivers/vision/vipx/vipx-io.c b/drivers/vision/vipx/vipx-io.c new file mode 100644 index 000000000000..ecb31d970a2e --- /dev/null +++ b/drivers/vision/vipx/vipx-io.c @@ -0,0 +1,52 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "vipx-io.h" +void *mem2iocpy(void *dst,void *src, u32 size) +{ + u8 *src8; + u8 *dst8; + + src8 = (u8 *)(src); + dst8 = (u8 *)(dst); + /* first copy byte by byte till the source first alignment + * this step is necessary to ensure we do not even try to access + * data which is before the source buffer, hence it is not ours. + */ + /* complete the left overs */ + while (size--) + { + IOW8(*dst8, *src8); + dst8++; + src8++; + } + return dst; +} + +void *io2memcpy(void *dst,void *src, u32 size) +{ + u8 *src8; + u8 *dst8; + + src8 = (u8 *)(src); + dst8 = (u8 *)(dst); + + while (size--) + { + *dst8 = IOR8(*src8); + dst8++; + src8++; + } + + return dst; +} diff --git a/drivers/vision/vipx/vipx-memory.c b/drivers/vision/vipx/vipx-memory.c new file mode 100755 index 000000000000..f803887faa4f --- /dev/null +++ b/drivers/vision/vipx/vipx-memory.c @@ -0,0 +1,654 @@ +/* + * Samsung Exynos SoC series VIPx driver + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include