diff options
author | Dorian Snyder <dastin1015@gmail.com> | 2014-06-15 01:24:33 -0700 |
---|---|---|
committer | Dorian Snyder <dastin1015@gmail.com> | 2014-08-04 18:46:06 +0000 |
commit | d90b43b963027b4d1559cec14e97117827a6458d (patch) | |
tree | 00d1334c383628e7d6f38a4f7ca0c575a4916d91 /drivers/media/video/samsung/ump | |
parent | 89e6992285b4274996b39cd953c0adb5e95a3236 (diff) | |
download | kernel_samsung_smdk4412-d90b43b963027b4d1559cec14e97117827a6458d.zip kernel_samsung_smdk4412-d90b43b963027b4d1559cec14e97117827a6458d.tar.gz kernel_samsung_smdk4412-d90b43b963027b4d1559cec14e97117827a6458d.tar.bz2 |
mali: add r3p1 for devices that need it
d710 needs to use old mali drivers due to new ones causing signal to be completely killed
Change-Id: I450c356b50e3f3521a63717a1c241e3b818b936f
Diffstat (limited to 'drivers/media/video/samsung/ump')
42 files changed, 5453 insertions, 0 deletions
diff --git a/drivers/media/video/samsung/ump/Kconfig b/drivers/media/video/samsung/ump/Kconfig new file mode 100644 index 0000000..09e8daf --- /dev/null +++ b/drivers/media/video/samsung/ump/Kconfig @@ -0,0 +1,58 @@ + +# +## S3C Multimedia Mali configuration +## +# +# For UMP +config VIDEO_UMP + bool "Enable UMP(Unified Memory Provider)" + default n + ---help--- + This enables UMP memory provider + +config UMP_VCM_ALLOC + depends on VIDEO_UMP && VCM + default y + bool "Enable ump-vcm(virtual contiguous memory) memory" + help + Use VCM(virtual-contiguous-memory) to allocate physical memory. + + +config UMP_R3P1_LSI + bool "Uses the R3P1 as a ump module" + depends on VIDEO_UMP + default n + ---help--- + This uses the r3p1 as a UMP kernel module + +choice +depends on VIDEO_UMP +prompt "UMP MEMEMORY OPTION" +default UMP_OSMEM_ONLY +config UMP_DED_ONLY + bool "ump dedicated memory only" + ---help--- + This enables UMP dedicated memory only option +config UMP_OSMEM_ONLY + bool "ump OS memory only" + ---help--- + This enables UMP OS memory only option +config UMP_VCM_ONLY + bool "ump VCM memory" + ---help--- + This enables UMP VCM memory only option + +endchoice +config UMP_MEM_SIZE +int "UMP Memory Size" + depends on VIDEO_UMP + default "512" + ---help--- + This value is dedicated memory size of UMP (unit is MByte). +# For UMP_DEBUG +config VIDEO_UMP_DEBUG + bool "Enables debug messages" + depends on VIDEO_UMP + default n + help + This enables UMP driver debug messages. diff --git a/drivers/media/video/samsung/ump/Kconfig_module b/drivers/media/video/samsung/ump/Kconfig_module new file mode 100644 index 0000000..3ae316c --- /dev/null +++ b/drivers/media/video/samsung/ump/Kconfig_module @@ -0,0 +1,16 @@ +config UMP + tristate "UMP support" + depends on ARM + ---help--- + This enables support for the UMP memory allocation and sharing API. + + To compile this driver as a module, choose M here: the module will be + called ump. + +config UMP_DEBUG + bool "Enable extra debug in UMP" + depends on UMP + default y + ---help--- + This enabled extra debug checks and messages in UMP. + diff --git a/drivers/media/video/samsung/ump/Makefile b/drivers/media/video/samsung/ump/Makefile new file mode 100644 index 0000000..3a1aac0 --- /dev/null +++ b/drivers/media/video/samsung/ump/Makefile @@ -0,0 +1,93 @@ +# +# Copyright (C) 2010-2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +ifeq ($(CONFIG_UMP_DED_ONLY),y) +UMP_MEM_SIZE= $(CONFIG_UMP_MEM_SIZE) +USING_MEMORY=0 +endif + +ifeq ($(CONFIG_UMP_OSMEM_ONLY),y) +UMP_MEM_SIZE= $(CONFIG_UMP_MEM_SIZE) +USING_MEMORY=1 +endif + +ifeq ($(CONFIG_UMP_VCM_ONLY),y) +UMP_MEM_SIZE= $(CONFIG_UMP_MEM_SIZE) +USING_MEMORY=2 +endif + + +# For UMP Debug +ifeq ($(CONFIG_VIDEO_UMP_DEBUG),y) +DEFINES += -DDEBUG +endif + +# Set up our defines, which will be passed to gcc +DEFINES += -DMALI_USE_UNIFIED_MEMORY_PROVIDER +DEFINES += -DUSING_MEMORY=$(USING_MEMORY) +DEFINES += -DUMP_MEM_SIZE=$(UMP_MEM_SIZE) +DEFINES += -DMALI_STATE_TRACKING=1 + +UDD_FILE_PREFIX := drivers/media/video/samsung/ump/ +KBUILDROOT = + +# linux build system integration + +obj-$(CONFIG_VIDEO_UMP) += ump.o + +# For customer releases the Linux Device Drivers will be provided as ARM proprietary and GPL releases: +# The ARM proprietary product will only include the license/proprietary directory +# The GPL product will only include the license/gpl directory + +INCLUDES += \ + -I$(UDD_FILE_PREFIX)\ + -I$(UDD_FILE_PREFIX)common\ + -I$(UDD_FILE_PREFIX)linux\ + -I$(UDD_FILE_PREFIX)include\ + -I$(UDD_FILE_PREFIX)linux/license/gpl/\ + -I$(UDD_FILE_PREFIX)../mali/common\ + -I$(UDD_FILE_PREFIX)../mali/linux + +OSKFILES+=\ + $(KBUILDROOT)../mali/linux/mali_osk_atomics.o \ + $(KBUILDROOT)../mali/linux/mali_osk_locks.o \ + $(KBUILDROOT)../mali/linux/mali_osk_math.o \ + $(KBUILDROOT)../mali/linux/mali_osk_memory.o \ + $(KBUILDROOT)../mali/linux/mali_osk_misc.o + +ump-y := \ + $(KBUILDROOT)linux/ump_kernel_linux.o \ + $(KBUILDROOT)linux/ump_kernel_memory_backend_os.o \ + $(KBUILDROOT)linux/ump_kernel_memory_backend_dedicated.o \ + $(KBUILDROOT)linux/ump_memory_backend.o \ + $(KBUILDROOT)linux/ump_ukk_wrappers.o \ + $(KBUILDROOT)linux/ump_ukk_ref_wrappers.o \ + $(KBUILDROOT)linux/ump_osk_atomics.o \ + $(KBUILDROOT)linux/ump_osk_low_level_mem.o \ + $(KBUILDROOT)linux/ump_osk_misc.o \ + $(KBUILDROOT)common/ump_kernel_common.o \ + $(KBUILDROOT)common/ump_kernel_descriptor_mapping.o \ + $(KBUILDROOT)common/ump_kernel_api.o \ + $(KBUILDROOT)common/ump_kernel_ref_drv.o\ + $(OSKFILES) + +ump-$(CONFIG_UMP_VCM_ALLOC) += \ + $(KBUILDROOT)linux/ump_kernel_memory_backend_vcm.o \ + +EXTRA_CFLAGS += $(INCLUDES) \ + $(DEFINES) + + +# Get subversion revision number, fall back to 0000 if no svn info is available +SVN_REV:=$(shell ((svnversion | grep -E "^[0-9]+" && echo -n 'Revision: ' && svnversion) || git svn info | sed -e 's/$$$$/M/' | grep '^Revision: ' || echo ${MALI_RELEASE_NAME}) 2>/dev/null | sed -e 's/^Revision: //') + +EXTRA_CFLAGS += -DSVN_REV=$(SVN_REV) +EXTRA_CFLAGS += -DSVN_REV_STRING=\"$(SVN_REV)\" + diff --git a/drivers/media/video/samsung/ump/Makefile.common b/drivers/media/video/samsung/ump/Makefile.common new file mode 100644 index 0000000..26a3d6c --- /dev/null +++ b/drivers/media/video/samsung/ump/Makefile.common @@ -0,0 +1,20 @@ +# +# Copyright (C) 2010-2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +SRC = $(UMP_FILE_PREFIX)common/ump_kernel_common.c \ + $(UMP_FILE_PREFIX)common/ump_kernel_descriptor_mapping.c \ + $(UMP_FILE_PREFIX)common/ump_kernel_api.c \ + $(UMP_FILE_PREFIX)common/ump_kernel_ref_drv.c + +# Get subversion revision number, fall back to 0000 if no svn info is available +SVN_REV:=$(shell ((svnversion | grep -E "^[0-9]+" && echo -n 'Revision: ' && svnversion) || git svn info | sed -e 's/$$$$/M/' | grep '^Revision: ' || echo ${MALI_RELEASE_NAME}) 2>/dev/null | sed -e 's/^Revision: //') + +EXTRA_CFLAGS += -DSVN_REV=$(SVN_REV) +EXTRA_CFLAGS += -DSVN_REV_STRING=\"$(SVN_REV)\" diff --git a/drivers/media/video/samsung/ump/arch b/drivers/media/video/samsung/ump/arch new file mode 120000 index 0000000..58ffbe7 --- /dev/null +++ b/drivers/media/video/samsung/ump/arch @@ -0,0 +1 @@ +arch-release
\ No newline at end of file diff --git a/drivers/media/video/samsung/ump/arch-debug b/drivers/media/video/samsung/ump/arch-debug new file mode 120000 index 0000000..0ed0909 --- /dev/null +++ b/drivers/media/video/samsung/ump/arch-debug @@ -0,0 +1 @@ +arch-pegasus-m400/
\ No newline at end of file diff --git a/drivers/media/video/samsung/ump/arch-orion-m400/config.h b/drivers/media/video/samsung/ump/arch-orion-m400/config.h new file mode 100644 index 0000000..7afbca6 --- /dev/null +++ b/drivers/media/video/samsung/ump/arch-orion-m400/config.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_UMP_H__ +#define __ARCH_CONFIG_UMP_H__ + +#define ARCH_UMP_BACKEND_DEFAULT USING_MEMORY +#if (USING_MEMORY == 0) /* Dedicated Memory */ +#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0x2C000000 +#else +#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0 +#endif + +#define ARCH_UMP_MEMORY_SIZE_DEFAULT UMP_MEM_SIZE*1024*1024 +#endif /* __ARCH_CONFIG_UMP_H__ */ diff --git a/drivers/media/video/samsung/ump/arch-pb-virtex5/config.h b/drivers/media/video/samsung/ump/arch-pb-virtex5/config.h new file mode 100644 index 0000000..532fc94 --- /dev/null +++ b/drivers/media/video/samsung/ump/arch-pb-virtex5/config.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +#define ARCH_UMP_BACKEND_DEFAULT 0 +#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0xCE000000 +#define ARCH_UMP_MEMORY_SIZE_DEFAULT 32UL * 1024UL * 1024UL + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/media/video/samsung/ump/arch-pegasus-m400/config.h b/drivers/media/video/samsung/ump/arch-pegasus-m400/config.h new file mode 100644 index 0000000..7afbca6 --- /dev/null +++ b/drivers/media/video/samsung/ump/arch-pegasus-m400/config.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_UMP_H__ +#define __ARCH_CONFIG_UMP_H__ + +#define ARCH_UMP_BACKEND_DEFAULT USING_MEMORY +#if (USING_MEMORY == 0) /* Dedicated Memory */ +#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0x2C000000 +#else +#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0 +#endif + +#define ARCH_UMP_MEMORY_SIZE_DEFAULT UMP_MEM_SIZE*1024*1024 +#endif /* __ARCH_CONFIG_UMP_H__ */ diff --git a/drivers/media/video/samsung/ump/arch-release b/drivers/media/video/samsung/ump/arch-release new file mode 120000 index 0000000..0ed0909 --- /dev/null +++ b/drivers/media/video/samsung/ump/arch-release @@ -0,0 +1 @@ +arch-pegasus-m400/
\ No newline at end of file diff --git a/drivers/media/video/samsung/ump/common/ump_kernel_api.c b/drivers/media/video/samsung/ump/common/ump_kernel_api.c new file mode 100644 index 0000000..83f0d30 --- /dev/null +++ b/drivers/media/video/samsung/ump/common/ump_kernel_api.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_common.h" + + + +/* ---------------- UMP kernel space API functions follows ---------------- */ + + + +UMP_KERNEL_API_EXPORT ump_secure_id ump_dd_secure_id_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + DBG_MSG(5, ("Returning secure ID. ID: %u\n", mem->secure_id)); + + return mem->secure_id; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id) +{ + ump_dd_mem * mem; + + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + DBG_MSG(5, ("Getting handle from secure ID. ID: %u\n", secure_id)); + if (0 != ump_descriptor_mapping_get(device.secure_id_map, (int)secure_id, (void**)&mem)) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(1, ("Secure ID not found. ID: %u\n", secure_id)); + return UMP_DD_HANDLE_INVALID; + } + + ump_dd_reference_add(mem); + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + return (ump_dd_handle)mem; +} + +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_get(ump_secure_id secure_id) +{ + ump_dd_mem * mem; + + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + DBG_MSG(5, ("Getting handle from secure ID. ID: %u\n", secure_id)); + if (0 != ump_descriptor_mapping_get(device.secure_id_map, (int)secure_id, (void**)&mem)) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(1, ("Secure ID not found. ID: %u\n", secure_id)); + return UMP_DD_HANDLE_INVALID; + } + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + return (ump_dd_handle)mem; +} + +UMP_KERNEL_API_EXPORT unsigned long ump_dd_phys_block_count_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*) memh; + + DEBUG_ASSERT_POINTER(mem); + + return mem->nr_blocks; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle memh, ump_dd_physical_block * blocks, unsigned long num_blocks) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + if (blocks == NULL) + { + DBG_MSG(1, ("NULL parameter in ump_dd_phys_blocks_get()\n")); + return UMP_DD_INVALID; + } + + if (mem->nr_blocks != num_blocks) + { + DBG_MSG(1, ("Specified number of blocks do not match actual number of blocks\n")); + return UMP_DD_INVALID; + } + + DBG_MSG(5, ("Returning physical block information. ID: %u\n", mem->secure_id)); + + _mali_osk_memcpy(blocks, mem->block_array, sizeof(ump_dd_physical_block) * mem->nr_blocks); + + return UMP_DD_SUCCESS; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle memh, unsigned long index, ump_dd_physical_block * block) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + if (block == NULL) + { + DBG_MSG(1, ("NULL parameter in ump_dd_phys_block_get()\n")); + return UMP_DD_INVALID; + } + + if (index >= mem->nr_blocks) + { + DBG_MSG(5, ("Invalid index specified in ump_dd_phys_block_get()\n")); + return UMP_DD_INVALID; + } + + DBG_MSG(5, ("Returning physical block information. ID: %u, index: %lu\n", mem->secure_id, index)); + + *block = mem->block_array[index]; + + return UMP_DD_SUCCESS; +} + + + +UMP_KERNEL_API_EXPORT unsigned long ump_dd_size_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*)memh; + + DEBUG_ASSERT_POINTER(mem); + + DBG_MSG(5, ("Returning size. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes)); + + return mem->size_bytes; +} + + + +UMP_KERNEL_API_EXPORT void ump_dd_reference_add(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*)memh; + int new_ref; + + DEBUG_ASSERT_POINTER(mem); + + new_ref = _ump_osk_atomic_inc_and_read(&mem->ref_count); + + DBG_MSG(5, ("Memory reference incremented. ID: %u, new value: %d\n", mem->secure_id, new_ref)); +} + + + +UMP_KERNEL_API_EXPORT void ump_dd_reference_release(ump_dd_handle memh) +{ + int new_ref; + ump_dd_mem * mem = (ump_dd_mem*)memh; + + DEBUG_ASSERT_POINTER(mem); + + /* We must hold this mutex while doing the atomic_dec_and_read, to protect + that elements in the ump_descriptor_mapping table is always valid. If they + are not, userspace may accidently map in this secure_ids right before its freed + giving a mapped backdoor into unallocated memory.*/ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + new_ref = _ump_osk_atomic_dec_and_read(&mem->ref_count); + + DBG_MSG(5, ("Memory reference decremented. ID: %u, new value: %d\n", mem->secure_id, new_ref)); + + if (0 == new_ref) + { + DBG_MSG(3, ("Final release of memory. ID: %u\n", mem->secure_id)); + + ump_descriptor_mapping_free(device.secure_id_map, (int)mem->secure_id); + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + mem->release_func(mem->ctx, mem); + _mali_osk_free(mem); + } + else + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + } +} + + + +/* --------------- Handling of user space requests follows --------------- */ + + +_mali_osk_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args ) +{ + ump_session_data * session_data; + + DEBUG_ASSERT_POINTER( args ); + DEBUG_ASSERT_POINTER( args->ctx ); + + session_data = (ump_session_data *)args->ctx; + + /* check compatability */ + if (args->version == UMP_IOCTL_API_VERSION) + { + DBG_MSG(3, ("API version set to newest %d (compatible)\n", GET_VERSION(args->version))); + args->compatible = 1; + session_data->api_version = args->version; + } + else if (args->version == MAKE_VERSION_ID(1)) + { + DBG_MSG(2, ("API version set to depricated: %d (compatible)\n", GET_VERSION(args->version))); + args->compatible = 1; + session_data->api_version = args->version; + } + else + { + DBG_MSG(2, ("API version set to %d (incompatible with client version %d)\n", GET_VERSION(UMP_IOCTL_API_VERSION), GET_VERSION(args->version))); + args->compatible = 0; + args->version = UMP_IOCTL_API_VERSION; /* report our version */ + } + + return _MALI_OSK_ERR_OK; +} + + +_mali_osk_errcode_t _ump_ukk_release( _ump_uk_release_s *release_info ) +{ + ump_session_memory_list_element * session_memory_element; + ump_session_memory_list_element * tmp; + ump_session_data * session_data; + _mali_osk_errcode_t ret = _MALI_OSK_ERR_INVALID_FUNC; + int secure_id; + + DEBUG_ASSERT_POINTER( release_info ); + DEBUG_ASSERT_POINTER( release_info->ctx ); + + /* Retreive the session data */ + session_data = (ump_session_data*)release_info->ctx; + + /* If there are many items in the memory session list we + * could be de-referencing this pointer a lot so keep a local copy + */ + secure_id = release_info->secure_id; + + DBG_MSG(4, ("Releasing memory with IOCTL, ID: %u\n", secure_id)); + + /* Iterate through the memory list looking for the requested secure ID */ + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _MALI_OSK_LIST_FOREACHENTRY(session_memory_element, tmp, &session_data->list_head_session_memory_list, ump_session_memory_list_element, list) + { + if ( session_memory_element->mem->secure_id == secure_id) + { + ump_dd_mem *release_mem; + + release_mem = session_memory_element->mem; + _mali_osk_list_del(&session_memory_element->list); + ump_dd_reference_release(release_mem); + _mali_osk_free(session_memory_element); + + ret = _MALI_OSK_ERR_OK; + break; + } + } + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG_IF(1, _MALI_OSK_ERR_OK != ret, ("UMP memory with ID %u does not belong to this session.\n", secure_id)); + + DBG_MSG(4, ("_ump_ukk_release() returning 0x%x\n", ret)); + return ret; +} + +_mali_osk_errcode_t _ump_ukk_size_get( _ump_uk_size_get_s *user_interaction ) +{ + ump_dd_mem * mem; + _mali_osk_errcode_t ret = _MALI_OSK_ERR_FAULT; + + DEBUG_ASSERT_POINTER( user_interaction ); + + /* We lock the mappings so things don't get removed while we are looking for the memory */ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + if (0 == ump_descriptor_mapping_get(device.secure_id_map, (int)user_interaction->secure_id, (void**)&mem)) + { + user_interaction->size = mem->size_bytes; + DBG_MSG(4, ("Returning size. ID: %u, size: %lu ", (ump_secure_id)user_interaction->secure_id, (unsigned long)user_interaction->size)); + ret = _MALI_OSK_ERR_OK; + } + else + { + user_interaction->size = 0; + DBG_MSG(1, ("Failed to look up mapping in ump_ioctl_size_get(). ID: %u\n", (ump_secure_id)user_interaction->secure_id)); + } + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + return ret; +} + + + +void _ump_ukk_msync( _ump_uk_msync_s *args ) +{ + ump_dd_mem * mem = NULL; + void *virtual = NULL; + u32 size = 0; + u32 offset = 0; + + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); + + if (NULL == mem) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_msync(). ID: %u\n", (ump_secure_id)args->secure_id)); + return; + } + /* Ensure the memory doesn't dissapear when we are flushing it. */ + ump_dd_reference_add(mem); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + /* Returns the cache settings back to Userspace */ + args->is_cached=mem->is_cached; + + /* If this flag is the only one set, we should not do the actual flush, only the readout */ + if ( _UMP_UK_MSYNC_READOUT_CACHE_ENABLED==args->op ) + { + DBG_MSG(3, ("_ump_ukk_msync READOUT ID: %u Enabled: %d\n", (ump_secure_id)args->secure_id, mem->is_cached)); + goto msync_release_and_return; + } + + /* Nothing to do if the memory is not caches */ + if ( 0==mem->is_cached ) + { + DBG_MSG(3, ("_ump_ukk_msync IGNORING ID: %u Enabled: %d OP: %d\n", (ump_secure_id)args->secure_id, mem->is_cached, args->op)); + goto msync_release_and_return; + } + DBG_MSG(3, ("UMP[%02u] _ump_ukk_msync Flush OP: %d Address: 0x%08x Mapping: 0x%08x\n", + (ump_secure_id)args->secure_id, args->op, args->address, args->mapping)); + + if ( args->address ) + { + virtual = (void *)((u32)args->address); + offset = (u32)((args->address) - (args->mapping)); + } else { + /* Flush entire mapping when no address is specified. */ + virtual = args->mapping; + } + if ( args->size ) + { + size = args->size; + } else { + /* Flush entire mapping when no size is specified. */ + size = mem->size_bytes - offset; + } + + if ( (offset + size) > mem->size_bytes ) + { + DBG_MSG(1, ("Trying to flush more than the entire UMP allocation: offset: %u + size: %u > %u\n", offset, size, mem->size_bytes)); + goto msync_release_and_return; + } + + /* The actual cache flush - Implemented for each OS*/ + _ump_osk_msync( mem, virtual, offset, size, args->op, NULL); + +msync_release_and_return: + ump_dd_reference_release(mem); + return; +} + +void _ump_ukk_cache_operations_control(_ump_uk_cache_operations_control_s* args) +{ + ump_session_data * session_data; + ump_uk_cache_op_control op; + + DEBUG_ASSERT_POINTER( args ); + DEBUG_ASSERT_POINTER( args->ctx ); + + op = args->op; + session_data = (ump_session_data *)args->ctx; + + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + if ( op== _UMP_UK_CACHE_OP_START ) + { + session_data->cache_operations_ongoing++; + DBG_MSG(4, ("Cache ops start\n" )); + if ( session_data->cache_operations_ongoing != 1 ) + { + DBG_MSG(2, ("UMP: Number of simultanious cache control ops: %d\n", session_data->cache_operations_ongoing) ); + } + } + else if ( op== _UMP_UK_CACHE_OP_FINISH ) + { + DBG_MSG(4, ("Cache ops finish\n")); + session_data->cache_operations_ongoing--; + #if 0 + if ( session_data->has_pending_level1_cache_flush) + { + /* This function will set has_pending_level1_cache_flush=0 */ + _ump_osk_msync( NULL, NULL, 0, 0, _UMP_UK_MSYNC_FLUSH_L1, session_data); + } + #endif + + /* to be on the safe side: always flush l1 cache when cache operations are done */ + _ump_osk_msync( NULL, NULL, 0, 0, _UMP_UK_MSYNC_FLUSH_L1, session_data); + DBG_MSG(4, ("Cache ops finish end\n" )); + } + else + { + DBG_MSG(1, ("Illegal call to %s at line %d\n", __FUNCTION__, __LINE__)); + } + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + +} + +void _ump_ukk_switch_hw_usage(_ump_uk_switch_hw_usage_s *args ) +{ + ump_dd_mem * mem = NULL; + ump_uk_user old_user; + ump_uk_msync_op cache_op = _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE; + ump_session_data *session_data; + + DEBUG_ASSERT_POINTER( args ); + DEBUG_ASSERT_POINTER( args->ctx ); + + session_data = (ump_session_data *)args->ctx; + + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); + + if (NULL == mem) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_switch_hw_usage(). ID: %u\n", (ump_secure_id)args->secure_id)); + return; + } + + old_user = mem->hw_device; + mem->hw_device = args->new_user; + + DBG_MSG(3, ("UMP[%02u] Switch usage Start New: %s Prev: %s.\n", (ump_secure_id)args->secure_id, args->new_user?"MALI":"CPU",old_user?"MALI":"CPU")); + + if ( ! mem->is_cached ) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(3, ("UMP[%02u] Changing owner of uncached memory. Cache flushing not needed.\n", (ump_secure_id)args->secure_id)); + return; + } + + if ( old_user == args->new_user) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(4, ("UMP[%02u] Setting the new_user equal to previous for. Cache flushing not needed.\n", (ump_secure_id)args->secure_id)); + return; + } + if ( + /* Previous AND new is both different from CPU */ + (old_user != _UMP_UK_USED_BY_CPU) && (args->new_user != _UMP_UK_USED_BY_CPU ) + ) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(4, ("UMP[%02u] Previous and new user is not CPU. Cache flushing not needed.\n", (ump_secure_id)args->secure_id)); + return; + } + + if ( (old_user != _UMP_UK_USED_BY_CPU ) && (args->new_user==_UMP_UK_USED_BY_CPU) ) + { + cache_op =_UMP_UK_MSYNC_INVALIDATE; + DBG_MSG(4, ("UMP[%02u] Cache invalidation needed\n", (ump_secure_id)args->secure_id)); +#ifdef UMP_SKIP_INVALIDATION +#error + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(4, ("UMP[%02u] Performing Cache invalidation SKIPPED\n", (ump_secure_id)args->secure_id)); + return; +#endif + } + /* Ensure the memory doesn't dissapear when we are flushing it. */ + ump_dd_reference_add(mem); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + /* Take lock to protect: session->cache_operations_ongoing and session->has_pending_level1_cache_flush */ + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + /* Actual cache flush */ + _ump_osk_msync( mem, NULL, 0, mem->size_bytes, cache_op, session_data); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + ump_dd_reference_release(mem); + DBG_MSG(4, ("UMP[%02u] Switch usage Finish\n", (ump_secure_id)args->secure_id)); + return; +} + +void _ump_ukk_lock(_ump_uk_lock_s *args ) +{ + ump_dd_mem * mem = NULL; + + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); + + if (NULL == mem) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(1, ("UMP[%02u] Failed to look up mapping in _ump_ukk_lock(). ID: %u\n", (ump_secure_id)args->secure_id)); + return; + } + ump_dd_reference_add(mem); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + DBG_MSG(1, ("UMP[%02u] Lock. New lock flag: %d. Old Lock flag:\n", (u32)args->secure_id, (u32)args->lock_usage, (u32) mem->lock_usage )); + + mem->lock_usage = (ump_lock_usage) args->lock_usage; + + /** TODO: TAKE LOCK HERE */ + + ump_dd_reference_release(mem); +} + +void _ump_ukk_unlock(_ump_uk_unlock_s *args ) +{ + ump_dd_mem * mem = NULL; + + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); + + if (NULL == mem) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_unlock(). ID: %u\n", (ump_secure_id)args->secure_id)); + return; + } + ump_dd_reference_add(mem); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + DBG_MSG(1, ("UMP[%02u] Unlocking. Old Lock flag:\n", (u32)args->secure_id, (u32) mem->lock_usage )); + + mem->lock_usage = (ump_lock_usage) UMP_NOT_LOCKED; + + /** TODO: RELEASE LOCK HERE */ + + ump_dd_reference_release(mem); +} diff --git a/drivers/media/video/samsung/ump/common/ump_kernel_common.c b/drivers/media/video/samsung/ump/common/ump_kernel_common.c new file mode 100644 index 0000000..a27bc77 --- /dev/null +++ b/drivers/media/video/samsung/ump/common/ump_kernel_common.c @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_bitops.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" + + + +/** + * Define the initial and maximum size of number of secure_ids on the system + */ +#define UMP_SECURE_ID_TABLE_ENTRIES_INITIAL (128 ) +#define UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM (4096 ) + + +/** + * Define the initial and maximum size of the ump_session_data::cookies_map, + * which is a \ref ump_descriptor_mapping. This limits how many secure_ids + * may be mapped into a particular process using _ump_ukk_map_mem(). + */ + +#define UMP_COOKIES_PER_SESSION_INITIAL (UMP_SECURE_ID_TABLE_ENTRIES_INITIAL ) +#define UMP_COOKIES_PER_SESSION_MAXIMUM (UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM) + +struct ump_dev device; + +_mali_osk_errcode_t ump_kernel_constructor(void) +{ + _mali_osk_errcode_t err; + + /* Perform OS Specific initialization */ + err = _ump_osk_init(); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("Failed to initiaze the UMP Device Driver")); + return err; + } + + /* Init the global device */ + _mali_osk_memset(&device, 0, sizeof(device) ); + + /* Create the descriptor map, which will be used for mapping secure ID to ump_dd_mem structs */ + device.secure_id_map_lock = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0 , 0); + if (NULL == device.secure_id_map_lock) + { + MSG_ERR(("Failed to create OSK lock for secure id lookup table\n")); + return _MALI_OSK_ERR_NOMEM; + } + + device.secure_id_map = ump_descriptor_mapping_create(UMP_SECURE_ID_TABLE_ENTRIES_INITIAL, UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM); + if (NULL == device.secure_id_map) + { + _mali_osk_lock_term(device.secure_id_map_lock); + MSG_ERR(("Failed to create secure id lookup table\n")); + return _MALI_OSK_ERR_NOMEM; + } + + /* Init memory backend */ + device.backend = ump_memory_backend_create(); + if (NULL == device.backend) + { + MSG_ERR(("Failed to create memory backend\n")); + _mali_osk_lock_term(device.secure_id_map_lock); + ump_descriptor_mapping_destroy(device.secure_id_map); + return _MALI_OSK_ERR_NOMEM; + } + + return _MALI_OSK_ERR_OK; +} + +void ump_kernel_destructor(void) +{ + DEBUG_ASSERT_POINTER(device.secure_id_map); + DEBUG_ASSERT_POINTER(device.secure_id_map_lock); + + _mali_osk_lock_term(device.secure_id_map_lock); + device.secure_id_map_lock = NULL; + + ump_descriptor_mapping_destroy(device.secure_id_map); + device.secure_id_map = NULL; + + device.backend->shutdown(device.backend); + device.backend = NULL; + + ump_memory_backend_destroy(); + + _ump_osk_term(); +} + +/** Creates a new UMP session + */ +_mali_osk_errcode_t _ump_ukk_open( void** context ) +{ + struct ump_session_data * session_data; + + /* allocated struct to track this session */ + session_data = (struct ump_session_data *)_mali_osk_malloc(sizeof(struct ump_session_data)); + if (NULL == session_data) + { + MSG_ERR(("Failed to allocate ump_session_data in ump_file_open()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + session_data->lock = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 0); + if( NULL == session_data->lock ) + { + MSG_ERR(("Failed to initialize lock for ump_session_data in ump_file_open()\n")); + _mali_osk_free(session_data); + return _MALI_OSK_ERR_NOMEM; + } + + session_data->cookies_map = ump_descriptor_mapping_create( UMP_COOKIES_PER_SESSION_INITIAL, UMP_COOKIES_PER_SESSION_MAXIMUM ); + + if ( NULL == session_data->cookies_map ) + { + MSG_ERR(("Failed to create descriptor mapping for _ump_ukk_map_mem cookies\n")); + + _mali_osk_lock_term( session_data->lock ); + _mali_osk_free( session_data ); + return _MALI_OSK_ERR_NOMEM; + } + + _MALI_OSK_INIT_LIST_HEAD(&session_data->list_head_session_memory_list); + + _MALI_OSK_INIT_LIST_HEAD(&session_data->list_head_session_memory_mappings_list); + + /* Since initial version of the UMP interface did not use the API_VERSION ioctl we have to assume + that it is this version, and not the "latest" one: UMP_IOCTL_API_VERSION + Current and later API versions would do an additional call to this IOCTL and update this variable + to the correct one.*/ + session_data->api_version = MAKE_VERSION_ID(1); + + *context = (void*)session_data; + + session_data->cache_operations_ongoing = 0 ; + session_data->has_pending_level1_cache_flush = 0; + + DBG_MSG(2, ("New session opened\n")); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_ukk_close( void** context ) +{ + struct ump_session_data * session_data; + ump_session_memory_list_element * item; + ump_session_memory_list_element * tmp; + + session_data = (struct ump_session_data *)*context; + if (NULL == session_data) + { + MSG_ERR(("Session data is NULL in _ump_ukk_close()\n")); + return _MALI_OSK_ERR_INVALID_ARGS; + } + + /* Unmap any descriptors mapped in. */ + if (0 == _mali_osk_list_empty(&session_data->list_head_session_memory_mappings_list)) + { + ump_memory_allocation *descriptor; + ump_memory_allocation *temp; + + DBG_MSG(1, ("Memory mappings found on session usage list during session termination\n")); + + /* use the 'safe' list iterator, since freeing removes the active block from the list we're iterating */ + _MALI_OSK_LIST_FOREACHENTRY(descriptor, temp, &session_data->list_head_session_memory_mappings_list, ump_memory_allocation, list) + { + _ump_uk_unmap_mem_s unmap_args; + DBG_MSG(4, ("Freeing block with phys address 0x%x size 0x%x mapped in user space at 0x%x\n", + descriptor->phys_addr, descriptor->size, descriptor->mapping)); + unmap_args.ctx = (void*)session_data; + unmap_args.mapping = descriptor->mapping; + unmap_args.size = descriptor->size; + unmap_args._ukk_private = NULL; /* NOTE: unused */ + unmap_args.cookie = descriptor->cookie; + + /* NOTE: This modifies the list_head_session_memory_mappings_list */ + _ump_ukk_unmap_mem( &unmap_args ); + } + } + + /* ASSERT that we really did free everything, because _ump_ukk_unmap_mem() + * can fail silently. */ + DEBUG_ASSERT( _mali_osk_list_empty(&session_data->list_head_session_memory_mappings_list) ); + + _MALI_OSK_LIST_FOREACHENTRY(item, tmp, &session_data->list_head_session_memory_list, ump_session_memory_list_element, list) + { + _mali_osk_list_del(&item->list); + DBG_MSG(2, ("Releasing UMP memory %u as part of file close\n", item->mem->secure_id)); + ump_dd_reference_release(item->mem); + _mali_osk_free(item); + } + + ump_descriptor_mapping_destroy( session_data->cookies_map ); + + _mali_osk_lock_term(session_data->lock); + _mali_osk_free(session_data); + + DBG_MSG(2, ("Session closed\n")); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_ukk_map_mem( _ump_uk_map_mem_s *args ) +{ + struct ump_session_data * session_data; + ump_memory_allocation * descriptor; /* Describes current mapping of memory */ + _mali_osk_errcode_t err; + unsigned long offset = 0; + unsigned long left; + ump_dd_handle handle; /* The real UMP handle for this memory. Its real datatype is ump_dd_mem* */ + ump_dd_mem * mem; /* The real UMP memory. It is equal to the handle, but with exposed struct */ + u32 block; + int map_id; + + session_data = (ump_session_data *)args->ctx; + if( NULL == session_data ) + { + MSG_ERR(("Session data is NULL in _ump_ukk_map_mem()\n")); + return _MALI_OSK_ERR_INVALID_ARGS; + } + /* SEC kernel stability 2012-02-17 */ + if (NULL == session_data->cookies_map) + { + MSG_ERR(("session_data->cookies_map is NULL in _ump_ukk_map_mem()\n")); + return _MALI_OSK_ERR_INVALID_ARGS; + } + descriptor = (ump_memory_allocation*) _mali_osk_calloc( 1, sizeof(ump_memory_allocation)); + if (NULL == descriptor) + { + MSG_ERR(("ump_ukk_map_mem: descriptor allocation failed\n")); + return _MALI_OSK_ERR_NOMEM; + } + + handle = ump_dd_handle_create_from_secure_id(args->secure_id); + if ( UMP_DD_HANDLE_INVALID == handle) + { + _mali_osk_free(descriptor); + DBG_MSG(1, ("Trying to map unknown secure ID %u\n", args->secure_id)); + return _MALI_OSK_ERR_FAULT; + } + + mem = (ump_dd_mem*)handle; + DEBUG_ASSERT(mem); + if (mem->size_bytes != args->size) + { + _mali_osk_free(descriptor); + ump_dd_reference_release(handle); + DBG_MSG(1, ("Trying to map too much or little. ID: %u, virtual size=%lu, UMP size: %lu\n", args->secure_id, args->size, mem->size_bytes)); + return _MALI_OSK_ERR_FAULT; + } + + map_id = ump_descriptor_mapping_allocate_mapping( session_data->cookies_map, (void*) descriptor ); + + if (map_id < 0) + { + _mali_osk_free(descriptor); + ump_dd_reference_release(handle); + DBG_MSG(1, ("ump_ukk_map_mem: unable to allocate a descriptor_mapping for return cookie\n")); + + return _MALI_OSK_ERR_NOMEM; + } + + descriptor->size = args->size; + descriptor->handle = handle; + descriptor->phys_addr = args->phys_addr; + descriptor->process_mapping_info = args->_ukk_private; + descriptor->ump_session = session_data; + descriptor->cookie = (u32)map_id; + + if ( mem->is_cached ) + { + descriptor->is_cached = 1; + args->is_cached = 1; + DBG_MSG(3, ("Mapping UMP secure_id: %d as cached.\n", args->secure_id)); + } + else if ( args->is_cached) + { + mem->is_cached = 1; + descriptor->is_cached = 1; + DBG_MSG(3, ("Warning mapping UMP secure_id: %d. As cached, while it was allocated uncached.\n", args->secure_id)); + } + else + { + descriptor->is_cached = 0; + args->is_cached = 0; + DBG_MSG(3, ("Mapping UMP secure_id: %d as Uncached.\n", args->secure_id)); + } + + _mali_osk_list_init( &descriptor->list ); + + err = _ump_osk_mem_mapregion_init( descriptor ); + if( _MALI_OSK_ERR_OK != err ) + { + DBG_MSG(1, ("Failed to initialize memory mapping in _ump_ukk_map_mem(). ID: %u\n", args->secure_id)); + ump_descriptor_mapping_free( session_data->cookies_map, map_id ); + _mali_osk_free(descriptor); + ump_dd_reference_release(mem); + return err; + } + + DBG_MSG(4, ("Mapping virtual to physical memory: ID: %u, size:%lu, first physical addr: 0x%08lx, number of regions: %lu\n", + mem->secure_id, + mem->size_bytes, + ((NULL != mem->block_array) ? mem->block_array->addr : 0), + mem->nr_blocks)); + + left = descriptor->size; + /* loop over all blocks and map them in */ + for (block = 0; block < mem->nr_blocks; block++) + { + unsigned long size_to_map; + + if (left > mem->block_array[block].size) + { + size_to_map = mem->block_array[block].size; + } + else + { + size_to_map = left; + } + + if (_MALI_OSK_ERR_OK != _ump_osk_mem_mapregion_map(descriptor, offset, (u32 *)&(mem->block_array[block].addr), size_to_map ) ) + { + DBG_MSG(1, ("WARNING: _ump_ukk_map_mem failed to map memory into userspace\n")); + ump_descriptor_mapping_free( session_data->cookies_map, map_id ); + ump_dd_reference_release(mem); + _ump_osk_mem_mapregion_term( descriptor ); + _mali_osk_free(descriptor); + return _MALI_OSK_ERR_FAULT; + } + left -= size_to_map; + offset += size_to_map; + } + + /* Add to the ump_memory_allocation tracking list */ + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_list_add( &descriptor->list, &session_data->list_head_session_memory_mappings_list ); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + args->mapping = descriptor->mapping; + args->cookie = descriptor->cookie; + + return _MALI_OSK_ERR_OK; +} + +void _ump_ukk_unmap_mem( _ump_uk_unmap_mem_s *args ) +{ + struct ump_session_data * session_data; + ump_memory_allocation * descriptor; + ump_dd_handle handle; + + session_data = (ump_session_data *)args->ctx; + + if( NULL == session_data ) + { + MSG_ERR(("Session data is NULL in _ump_ukk_map_mem()\n")); + return; + } + /* SEC kernel stability 2012-02-17 */ + if (NULL == session_data->cookies_map) + { + MSG_ERR(("session_data->cookies_map is NULL in _ump_ukk_map_mem()\n")); + return; + } + if (0 != ump_descriptor_mapping_get( session_data->cookies_map, (int)args->cookie, (void**)&descriptor) ) + { + MSG_ERR(("_ump_ukk_map_mem: cookie 0x%X not found for this session\n", args->cookie )); + return; + } + + DEBUG_ASSERT_POINTER(descriptor); + + handle = descriptor->handle; + if ( UMP_DD_HANDLE_INVALID == handle) + { + DBG_MSG(1, ("WARNING: Trying to unmap unknown handle: UNKNOWN\n")); + return; + } + + /* Remove the ump_memory_allocation from the list of tracked mappings */ + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_list_del( &descriptor->list ); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + ump_descriptor_mapping_free( session_data->cookies_map, (int)args->cookie ); + + ump_dd_reference_release(handle); + + _ump_osk_mem_mapregion_term( descriptor ); + _mali_osk_free(descriptor); +} + +u32 _ump_ukk_report_memory_usage( void ) +{ + if(device.backend->stat) + return device.backend->stat(device.backend); + else + return 0; +} diff --git a/drivers/media/video/samsung/ump/common/ump_kernel_common.h b/drivers/media/video/samsung/ump/common/ump_kernel_common.h new file mode 100644 index 0000000..6e3a2e9 --- /dev/null +++ b/drivers/media/video/samsung/ump/common/ump_kernel_common.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_COMMON_H__ +#define __UMP_KERNEL_COMMON_H__ + +#include "ump_kernel_types.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" + + +#ifdef DEBUG + extern int ump_debug_level; + #define UMP_DEBUG_PRINT(args) _mali_osk_dbgmsg args + #define UMP_DEBUG_CODE(args) args + #define DBG_MSG(level,args) do { /* args should be in brackets */ \ + ((level) <= ump_debug_level)?\ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")), \ + UMP_DEBUG_PRINT(args):0; \ + } while (0) + + #define DBG_MSG_IF(level,condition,args) /* args should be in brackets */ \ + if((condition)&&((level) <= ump_debug_level)) {\ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")); \ + UMP_DEBUG_PRINT(args); \ + } + + #define DBG_MSG_ELSE(level,args) /* args should be in brackets */ \ + else if((level) <= ump_debug_level) { \ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")); \ + UMP_DEBUG_PRINT(args); \ + } + + #define DEBUG_ASSERT_POINTER(pointer) do {if( (pointer)== NULL) MSG_ERR(("NULL pointer " #pointer)); } while(0) + #define DEBUG_ASSERT(condition) do {if(!(condition)) MSG_ERR(("ASSERT failed: " #condition)); } while(0) +#else /* DEBUG */ + #define UMP_DEBUG_PRINT(args) do {} while(0) + #define UMP_DEBUG_CODE(args) + #define DBG_MSG(level,args) do {} while(0) + #define DBG_MSG_IF(level,condition,args) do {} while(0) + #define DBG_MSG_ELSE(level,args) do {} while(0) + #define DEBUG_ASSERT(condition) do {} while(0) + #define DEBUG_ASSERT_POINTER(pointer) do {} while(0) +#endif /* DEBUG */ + +#define MSG_ERR(args) do{ /* args should be in brackets */ \ + _mali_osk_dbgmsg("UMP: ERR: %s\n" ,__FILE__); \ + _mali_osk_dbgmsg( " %s()%4d\n", __FUNCTION__, __LINE__) ; \ + _mali_osk_dbgmsg args ; \ + _mali_osk_dbgmsg("\n"); \ + } while(0) + +#define MSG(args) do{ /* args should be in brackets */ \ + _mali_osk_dbgmsg("UMP: "); \ + _mali_osk_dbgmsg args; \ + } while (0) + + + +/* + * This struct is used to store per session data. + * A session is created when someone open() the device, and + * closed when someone close() it or the user space application terminates. + */ +typedef struct ump_session_data +{ + _mali_osk_list_t list_head_session_memory_list; /**< List of ump allocations made by the process (elements are ump_session_memory_list_element) */ + _mali_osk_list_t list_head_session_memory_mappings_list; /**< List of ump_memory_allocations mapped in */ + int api_version; + _mali_osk_lock_t * lock; + ump_descriptor_mapping * cookies_map; /**< Secure mapping of cookies from _ump_ukk_map_mem() */ + int cache_operations_ongoing; + int has_pending_level1_cache_flush; +} ump_session_data; + + + +/* + * This struct is used to track the UMP memory references a session has. + * We need to track this in order to be able to clean up after user space processes + * which don't do it themself (e.g. due to a crash or premature termination). + */ +typedef struct ump_session_memory_list_element +{ + struct ump_dd_mem * mem; + _mali_osk_list_t list; +} ump_session_memory_list_element; + + + +/* + * Device specific data, created when device driver is loaded, and then kept as the global variable device. + */ +typedef struct ump_dev +{ + _mali_osk_lock_t * secure_id_map_lock; + ump_descriptor_mapping * secure_id_map; + ump_memory_backend * backend; +} ump_dev; + + + +extern int ump_debug_level; +extern struct ump_dev device; + +_mali_osk_errcode_t ump_kernel_constructor(void); +void ump_kernel_destructor(void); +int map_errcode( _mali_osk_errcode_t err ); + +/** + * variables from user space cannot be dereferenced from kernel space; tagging them + * with __user allows the GCC compiler to generate a warning. Other compilers may + * not support this so we define it here as an empty macro if the compiler doesn't + * define it. + */ +#ifndef __user +#define __user +#endif + +#endif /* __UMP_KERNEL_COMMON_H__ */ diff --git a/drivers/media/video/samsung/ump/common/ump_kernel_descriptor_mapping.c b/drivers/media/video/samsung/ump/common/ump_kernel_descriptor_mapping.c new file mode 100644 index 0000000..cc7b8be --- /dev/null +++ b/drivers/media/video/samsung/ump/common/ump_kernel_descriptor_mapping.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_bitops.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" + +#define MALI_PAD_INT(x) (((x) + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1)) + +/** + * Allocate a descriptor table capable of holding 'count' mappings + * @param count Number of mappings in the table + * @return Pointer to a new table, NULL on error + */ +static ump_descriptor_table * descriptor_table_alloc(int count); + +/** + * Free a descriptor table + * @param table The table to free + */ +static void descriptor_table_free(ump_descriptor_table * table); + +ump_descriptor_mapping * ump_descriptor_mapping_create(int init_entries, int max_entries) +{ + ump_descriptor_mapping * map = _mali_osk_calloc(1, sizeof(ump_descriptor_mapping) ); + + init_entries = MALI_PAD_INT(init_entries); + max_entries = MALI_PAD_INT(max_entries); + + if (NULL != map) + { + map->table = descriptor_table_alloc(init_entries); + if (NULL != map->table) + { + map->lock = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE | _MALI_OSK_LOCKFLAG_READERWRITER, 0 , 0); + if ( NULL != map->lock ) + { + _mali_osk_set_nonatomic_bit(0, map->table->usage); /* reserve bit 0 to prevent NULL/zero logic to kick in */ + map->max_nr_mappings_allowed = max_entries; + map->current_nr_mappings = init_entries; + return map; + } + descriptor_table_free(map->table); + } + _mali_osk_free(map); + } + return NULL; +} + +void ump_descriptor_mapping_destroy(ump_descriptor_mapping * map) +{ + descriptor_table_free(map->table); + _mali_osk_lock_term( map->lock ); + _mali_osk_free(map); +} + +int ump_descriptor_mapping_allocate_mapping(ump_descriptor_mapping * map, void * target) +{ + int descriptor = -1;/*-EFAULT;*/ + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + descriptor = _mali_osk_find_first_zero_bit(map->table->usage, map->current_nr_mappings); + if (descriptor == map->current_nr_mappings) + { + int nr_mappings_new; + /* no free descriptor, try to expand the table */ + ump_descriptor_table * new_table; + ump_descriptor_table * old_table = map->table; + nr_mappings_new= map->current_nr_mappings *2; + + if (map->current_nr_mappings >= map->max_nr_mappings_allowed) + { + descriptor = -1; + goto unlock_and_exit; + } + + new_table = descriptor_table_alloc(nr_mappings_new); + if (NULL == new_table) + { + descriptor = -1; + goto unlock_and_exit; + } + + _mali_osk_memcpy(new_table->usage, old_table->usage, (sizeof(unsigned long)*map->current_nr_mappings) / BITS_PER_LONG); + _mali_osk_memcpy(new_table->mappings, old_table->mappings, map->current_nr_mappings * sizeof(void*)); + map->table = new_table; + map->current_nr_mappings = nr_mappings_new; + descriptor_table_free(old_table); + } + + /* we have found a valid descriptor, set the value and usage bit */ + _mali_osk_set_nonatomic_bit(descriptor, map->table->usage); + map->table->mappings[descriptor] = target; + +unlock_and_exit: + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RW); + return descriptor; +} + +int ump_descriptor_mapping_get(ump_descriptor_mapping * map, int descriptor, void** target) +{ + int result = -1;/*-EFAULT;*/ + DEBUG_ASSERT(map); + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + *target = map->table->mappings[descriptor]; + result = 0; + } + else *target = NULL; + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + return result; +} + +int ump_descriptor_mapping_set(ump_descriptor_mapping * map, int descriptor, void * target) +{ + int result = -1;/*-EFAULT;*/ + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + map->table->mappings[descriptor] = target; + result = 0; + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + return result; +} + +void ump_descriptor_mapping_free(ump_descriptor_mapping * map, int descriptor) +{ + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + map->table->mappings[descriptor] = NULL; + _mali_osk_clear_nonatomic_bit(descriptor, map->table->usage); + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RW); +} + +static ump_descriptor_table * descriptor_table_alloc(int count) +{ + ump_descriptor_table * table; + + table = _mali_osk_calloc(1, sizeof(ump_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG) + (sizeof(void*) * count) ); + + if (NULL != table) + { + table->usage = (u32*)((u8*)table + sizeof(ump_descriptor_table)); + table->mappings = (void**)((u8*)table + sizeof(ump_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG)); + } + + return table; +} + +static void descriptor_table_free(ump_descriptor_table * table) +{ + _mali_osk_free(table); +} diff --git a/drivers/media/video/samsung/ump/common/ump_kernel_descriptor_mapping.h b/drivers/media/video/samsung/ump/common/ump_kernel_descriptor_mapping.h new file mode 100644 index 0000000..05b3982 --- /dev/null +++ b/drivers/media/video/samsung/ump/common/ump_kernel_descriptor_mapping.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_descriptor_mapping.h + */ + +#ifndef __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ +#define __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ + +#include "mali_osk.h" + +/** + * The actual descriptor mapping table, never directly accessed by clients + */ +typedef struct ump_descriptor_table +{ + u32 * usage; /**< Pointer to bitpattern indicating if a descriptor is valid/used or not */ + void** mappings; /**< Array of the pointers the descriptors map to */ +} ump_descriptor_table; + +/** + * The descriptor mapping object + * Provides a separate namespace where we can map an integer to a pointer + */ +typedef struct ump_descriptor_mapping +{ + _mali_osk_lock_t *lock; /**< Lock protecting access to the mapping object */ + int max_nr_mappings_allowed; /**< Max number of mappings to support in this namespace */ + int current_nr_mappings; /**< Current number of possible mappings */ + ump_descriptor_table * table; /**< Pointer to the current mapping table */ +} ump_descriptor_mapping; + +/** + * Create a descriptor mapping object + * Create a descriptor mapping capable of holding init_entries growable to max_entries + * @param init_entries Number of entries to preallocate memory for + * @param max_entries Number of entries to max support + * @return Pointer to a descriptor mapping object, NULL on failure + */ +ump_descriptor_mapping * ump_descriptor_mapping_create(int init_entries, int max_entries); + +/** + * Destroy a descriptor mapping object + * @param map The map to free + */ +void ump_descriptor_mapping_destroy(ump_descriptor_mapping * map); + +/** + * Allocate a new mapping entry (descriptor ID) + * Allocates a new entry in the map. + * @param map The map to allocate a new entry in + * @param target The value to map to + * @return The descriptor allocated, a negative value on error + */ +int ump_descriptor_mapping_allocate_mapping(ump_descriptor_mapping * map, void * target); + +/** + * Get the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to a pointer which will receive the stored value + * @return 0 on successful lookup, negative on error + */ +int ump_descriptor_mapping_get(ump_descriptor_mapping * map, int descriptor, void** target); + +/** + * Set the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to replace the current value with + * @return 0 on successful lookup, negative on error + */ +int ump_descriptor_mapping_set(ump_descriptor_mapping * map, int descriptor, void * target); + +/** + * Free the descriptor ID + * For the descriptor to be reused it has to be freed + * @param map The map to free the descriptor from + * @param descriptor The descriptor ID to free + */ +void ump_descriptor_mapping_free(ump_descriptor_mapping * map, int descriptor); + +#endif /* __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ */ diff --git a/drivers/media/video/samsung/ump/common/ump_kernel_memory_backend.h b/drivers/media/video/samsung/ump/common/ump_kernel_memory_backend.h new file mode 100644 index 0000000..73915ee --- /dev/null +++ b/drivers/media/video/samsung/ump/common/ump_kernel_memory_backend.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_mapping.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_H__ + +#include "ump_kernel_interface.h" +#include "ump_kernel_types.h" + + +typedef struct ump_memory_allocation +{ + void * phys_addr; + void * mapping; + unsigned long size; + ump_dd_handle handle; + void * process_mapping_info; + u32 cookie; /**< necessary on some U/K interface implementations */ + struct ump_session_data * ump_session; /**< Session that this allocation belongs to */ + _mali_osk_list_t list; /**< List for linking together memory allocations into the session's memory head */ + u32 is_cached; +} ump_memory_allocation; + +typedef struct ump_memory_backend +{ + int (*allocate)(void* ctx, ump_dd_mem * descriptor); + void (*release)(void* ctx, ump_dd_mem * descriptor); + void (*shutdown)(struct ump_memory_backend * backend); + u32 (*stat)(struct ump_memory_backend *backend); + int (*pre_allocate_physical_check)(void *ctx, u32 size); + u32 (*adjust_to_mali_phys)(void *ctx, u32 cpu_phys); + void *(*get)(ump_dd_mem *mem, void *args); + void (*set)(ump_dd_mem *mem, void *args); + void * ctx; +} ump_memory_backend; + +ump_memory_backend * ump_memory_backend_create ( void ); +void ump_memory_backend_destroy( void ); + +#endif /*__UMP_KERNEL_MEMORY_BACKEND_H__ */ diff --git a/drivers/media/video/samsung/ump/common/ump_kernel_ref_drv.c b/drivers/media/video/samsung/ump/common/ump_kernel_ref_drv.c new file mode 100644 index 0000000..cb13232 --- /dev/null +++ b/drivers/media/video/samsung/ump/common/ump_kernel_ref_drv.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" + +#include "ump_kernel_interface_ref_drv.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" + +#define UMP_MINIMUM_SIZE 4096 +#define UMP_MINIMUM_SIZE_MASK (~(UMP_MINIMUM_SIZE-1)) +#define UMP_SIZE_ALIGN(x) (((x)+UMP_MINIMUM_SIZE-1)&UMP_MINIMUM_SIZE_MASK) +#define UMP_ADDR_ALIGN_OFFSET(x) ((x)&(UMP_MINIMUM_SIZE-1)) +static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor); + +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block * blocks, unsigned long num_blocks) +{ + ump_dd_mem * mem; + unsigned long size_total = 0; + int map_id; + u32 i; + + /* Go through the input blocks and verify that they are sane */ + for (i=0; i < num_blocks; i++) + { + unsigned long addr = blocks[i].addr; + unsigned long size = blocks[i].size; + + DBG_MSG(5, ("Adding physical memory to new handle. Address: 0x%08lx, size: %lu\n", addr, size)); + size_total += blocks[i].size; + + if (0 != UMP_ADDR_ALIGN_OFFSET(addr)) + { + MSG_ERR(("Trying to create UMP memory from unaligned physical address. Address: 0x%08lx\n", addr)); + return UMP_DD_HANDLE_INVALID; + } + + if (0 != UMP_ADDR_ALIGN_OFFSET(size)) + { + MSG_ERR(("Trying to create UMP memory with unaligned size. Size: %lu\n", size)); + return UMP_DD_HANDLE_INVALID; + } + } + + /* Allocate the ump_dd_mem struct for this allocation */ + mem = _mali_osk_malloc(sizeof(*mem)); + if (NULL == mem) + { + DBG_MSG(1, ("Could not allocate ump_dd_mem in ump_dd_handle_create_from_phys_blocks()\n")); + return UMP_DD_HANDLE_INVALID; + } + + /* Find a secure ID for this allocation */ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*) mem); + + if (map_id < 0) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(mem); + DBG_MSG(1, ("Failed to allocate secure ID in ump_dd_handle_create_from_phys_blocks()\n")); + return UMP_DD_HANDLE_INVALID; + } + + /* Now, make a copy of the block information supplied by the user */ + mem->block_array = _mali_osk_malloc(sizeof(ump_dd_physical_block)* num_blocks); + if (NULL == mem->block_array) + { + ump_descriptor_mapping_free(device.secure_id_map, map_id); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(mem); + DBG_MSG(1, ("Could not allocate a mem handle for function ump_dd_handle_create_from_phys_blocks().\n")); + return UMP_DD_HANDLE_INVALID; + } + + _mali_osk_memcpy(mem->block_array, blocks, sizeof(ump_dd_physical_block) * num_blocks); + + /* And setup the rest of the ump_dd_mem struct */ + _mali_osk_atomic_init(&mem->ref_count, 1); + mem->secure_id = (ump_secure_id)map_id; + mem->size_bytes = size_total; + mem->nr_blocks = num_blocks; + mem->backend_info = NULL; + mem->ctx = NULL; + mem->release_func = phys_blocks_release; + /* For now UMP handles created by ump_dd_handle_create_from_phys_blocks() is forced to be Uncached */ + mem->is_cached = 0; + mem->hw_device = _UMP_UK_USED_BY_CPU; + mem->lock_usage = UMP_NOT_LOCKED; + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(3, ("UMP memory created. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes)); + + return (ump_dd_handle)mem; +} + +static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor) +{ + _mali_osk_free(descriptor->block_array); + descriptor->block_array = NULL; +} + +_mali_osk_errcode_t _ump_ukk_allocate( _ump_uk_allocate_s *user_interaction ) +{ + ump_session_data * session_data = NULL; + ump_dd_mem *new_allocation = NULL; + ump_session_memory_list_element * session_memory_element = NULL; + int map_id; + + DEBUG_ASSERT_POINTER( user_interaction ); + DEBUG_ASSERT_POINTER( user_interaction->ctx ); + + session_data = (ump_session_data *) user_interaction->ctx; + + session_memory_element = _mali_osk_calloc( 1, sizeof(ump_session_memory_list_element)); + if (NULL == session_memory_element) + { + DBG_MSG(1, ("Failed to allocate ump_session_memory_list_element in ump_ioctl_allocate()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + + new_allocation = _mali_osk_calloc( 1, sizeof(ump_dd_mem)); + if (NULL==new_allocation) + { + _mali_osk_free(session_memory_element); + DBG_MSG(1, ("Failed to allocate ump_dd_mem in _ump_ukk_allocate()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + /* Create a secure ID for this allocation */ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*)new_allocation); + + if (map_id < 0) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(session_memory_element); + _mali_osk_free(new_allocation); + DBG_MSG(1, ("Failed to allocate secure ID in ump_ioctl_allocate()\n")); + return - _MALI_OSK_ERR_INVALID_FUNC; + } + + /* Initialize the part of the new_allocation that we know so for */ + new_allocation->secure_id = (ump_secure_id)map_id; + _mali_osk_atomic_init(&new_allocation->ref_count,1); + if ( 0==(UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE & user_interaction->constraints) ) + new_allocation->is_cached = 0; + else new_allocation->is_cached = 1; + + /* special case a size of 0, we should try to emulate what malloc does in this case, which is to return a valid pointer that must be freed, but can't be dereferences */ + if (0 == user_interaction->size) + { + user_interaction->size = 1; /* emulate by actually allocating the minimum block size */ + } + + new_allocation->size_bytes = UMP_SIZE_ALIGN(user_interaction->size); /* Page align the size */ + new_allocation->lock_usage = UMP_NOT_LOCKED; + + /* Now, ask the active memory backend to do the actual memory allocation */ + if (!device.backend->allocate( device.backend->ctx, new_allocation ) ) + { + DBG_MSG(3, ("OOM: No more UMP memory left. Failed to allocate memory in ump_ioctl_allocate(). Size: %lu, requested size: %lu\n", new_allocation->size_bytes, (unsigned long)user_interaction->size)); + ump_descriptor_mapping_free(device.secure_id_map, map_id); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(new_allocation); + _mali_osk_free(session_memory_element); + return _MALI_OSK_ERR_INVALID_FUNC; + } + new_allocation->hw_device = _UMP_UK_USED_BY_CPU; + new_allocation->ctx = device.backend->ctx; + new_allocation->release_func = device.backend->release; + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + /* Initialize the session_memory_element, and add it to the session object */ + session_memory_element->mem = new_allocation; + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_list_add(&(session_memory_element->list), &(session_data->list_head_session_memory_list)); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + user_interaction->secure_id = new_allocation->secure_id; + user_interaction->size = new_allocation->size_bytes; + DBG_MSG(3, ("UMP memory allocated. ID: %u, size: %lu\n", new_allocation->secure_id, new_allocation->size_bytes)); + + return _MALI_OSK_ERR_OK; +} + + +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_meminfo_set(ump_dd_handle memh, void* args) +{ + ump_dd_mem * mem; + ump_secure_id secure_id; + + DEBUG_ASSERT_POINTER(memh); + + secure_id = ump_dd_secure_id_get(memh); + + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + if (0 == ump_descriptor_mapping_get(device.secure_id_map, (int)secure_id, (void**)&mem)) + { + device.backend->set(mem, args); + } + else + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(1, ("Failed to look up mapping in ump_meminfo_set(). ID: %u\n", (ump_secure_id)secure_id)); + return UMP_DD_INVALID; + } + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + return UMP_DD_SUCCESS; +} + +UMP_KERNEL_API_EXPORT void *ump_dd_meminfo_get(ump_secure_id secure_id, void* args) +{ + ump_dd_mem * mem; + void *result; + + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + if (0 == ump_descriptor_mapping_get(device.secure_id_map, (int)secure_id, (void**)&mem)) + { + result = device.backend->get(mem, args); + } + else + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(1, ("Failed to look up mapping in ump_meminfo_get(). ID: %u\n", (ump_secure_id)secure_id)); + return UMP_DD_HANDLE_INVALID; + } + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + return result; +} + +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_get_from_vaddr(unsigned long vaddr) +{ + ump_dd_mem * mem; + + DBG_MSG(5, ("Getting handle from Virtual address. vaddr: %u\n", vaddr)); + + _ump_osk_mem_mapregion_get(&mem, vaddr); + + DBG_MSG(1, ("Getting handle's Handle : 0x%8lx\n", mem)); + + return (ump_dd_handle)mem; +} + diff --git a/drivers/media/video/samsung/ump/common/ump_kernel_types.h b/drivers/media/video/samsung/ump/common/ump_kernel_types.h new file mode 100644 index 0000000..19a9755 --- /dev/null +++ b/drivers/media/video/samsung/ump/common/ump_kernel_types.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_TYPES_H__ +#define __UMP_KERNEL_TYPES_H__ + +#include "ump_kernel_interface.h" +#include "mali_osk.h" + + +typedef enum +{ + UMP_USED_BY_CPU = 0, + UMP_USED_BY_MALI = 1, + UMP_USED_BY_UNKNOWN_DEVICE= 100, +} ump_hw_usage; + +typedef enum +{ + UMP_NOT_LOCKED = 0, + UMP_READ = 1, + UMP_READ_WRITE = 3, +} ump_lock_usage; + + +/* + * This struct is what is "behind" a ump_dd_handle + */ +typedef struct ump_dd_mem +{ + ump_secure_id secure_id; + _mali_osk_atomic_t ref_count; + unsigned long size_bytes; + unsigned long nr_blocks; + ump_dd_physical_block * block_array; + void (*release_func)(void * ctx, struct ump_dd_mem * descriptor); + void * ctx; + void * backend_info; + int is_cached; + ump_hw_usage hw_device; + ump_lock_usage lock_usage; +} ump_dd_mem; + + + +#endif /* __UMP_KERNEL_TYPES_H__ */ diff --git a/drivers/media/video/samsung/ump/common/ump_osk.h b/drivers/media/video/samsung/ump/common/ump_osk.h new file mode 100644 index 0000000..dabdc7f --- /dev/null +++ b/drivers/media/video/samsung/ump/common/ump_osk.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk.h + * Defines the OS abstraction layer for the UMP kernel device driver (OSK) + */ + +#ifndef __UMP_OSK_H__ +#define __UMP_OSK_H__ + +#include <mali_osk.h> +#include <ump_kernel_memory_backend.h> +#include "ump_uk_types.h" +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +_mali_osk_errcode_t _ump_osk_init( void ); + +_mali_osk_errcode_t _ump_osk_term( void ); + +int _ump_osk_atomic_inc_and_read( _mali_osk_atomic_t *atom ); + +int _ump_osk_atomic_dec_and_read( _mali_osk_atomic_t *atom ); + +_mali_osk_errcode_t _ump_osk_mem_mapregion_init( ump_memory_allocation *descriptor ); + +_mali_osk_errcode_t _ump_osk_mem_mapregion_map( ump_memory_allocation * descriptor, u32 offset, u32 * phys_addr, unsigned long size ); + +void _ump_osk_mem_mapregion_term( ump_memory_allocation * descriptor ); + +void _ump_osk_msync( ump_dd_mem * mem, void * virt, u32 offset, u32 size, ump_uk_msync_op op, ump_session_data * session_data ); + +void _ump_osk_mem_mapregion_get( ump_dd_mem ** mem, unsigned long vaddr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/media/video/samsung/ump/common/ump_uk_types.h b/drivers/media/video/samsung/ump/common/ump_uk_types.h new file mode 100644 index 0000000..143588d --- /dev/null +++ b/drivers/media/video/samsung/ump/common/ump_uk_types.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_uk_types.h + * Defines the types and constants used in the user-kernel interface + */ + +#ifndef __UMP_UK_TYPES_H__ +#define __UMP_UK_TYPES_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Helpers for API version handling */ +#define MAKE_VERSION_ID(x) (((x) << 16UL) | (x)) +#define IS_VERSION_ID(x) (((x) & 0xFFFF) == (((x) >> 16UL) & 0xFFFF)) +#define GET_VERSION(x) (((x) >> 16UL) & 0xFFFF) +#define IS_API_MATCH(x, y) (IS_VERSION_ID((x)) && IS_VERSION_ID((y)) && (GET_VERSION((x)) == GET_VERSION((y)))) + +/** + * API version define. + * Indicates the version of the kernel API + * The version is a 16bit integer incremented on each API change. + * The 16bit integer is stored twice in a 32bit integer + * So for version 1 the value would be 0x00010001 + */ +#define UMP_IOCTL_API_VERSION MAKE_VERSION_ID(2) + +typedef enum +{ + _UMP_IOC_QUERY_API_VERSION = 1, + _UMP_IOC_ALLOCATE, + _UMP_IOC_RELEASE, + _UMP_IOC_SIZE_GET, + _UMP_IOC_MAP_MEM, /* not used in Linux */ + _UMP_IOC_UNMAP_MEM, /* not used in Linux */ + _UMP_IOC_MSYNC, + _UMP_IOC_CACHE_OPERATIONS_CONTROL, + _UMP_IOC_SWITCH_HW_USAGE, + _UMP_IOC_LOCK, + _UMP_IOC_UNLOCK, +#ifdef CONFIG_ION_EXYNOS + _UMP_IOC_ION_IMPORT, +#endif +}_ump_uk_functions; + +typedef enum +{ + UMP_REF_DRV_UK_CONSTRAINT_NONE = 0, + UMP_REF_DRV_UK_CONSTRAINT_PHYSICALLY_LINEAR = 1, + UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE = 128, +} ump_uk_alloc_constraints; + +typedef enum +{ + _UMP_UK_MSYNC_CLEAN = 0, + _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE = 1, + _UMP_UK_MSYNC_INVALIDATE = 2, + _UMP_UK_MSYNC_FLUSH_L1 = 3, + _UMP_UK_MSYNC_READOUT_CACHE_ENABLED = 128, +} ump_uk_msync_op; + +typedef enum +{ + _UMP_UK_CACHE_OP_START = 0, + _UMP_UK_CACHE_OP_FINISH = 1, +} ump_uk_cache_op_control; + +typedef enum +{ + _UMP_UK_READ = 1, + _UMP_UK_READ_WRITE = 3, +} ump_uk_lock_usage; + +typedef enum +{ + _UMP_UK_USED_BY_CPU = 0, + _UMP_UK_USED_BY_MALI = 1, + _UMP_UK_USED_BY_UNKNOWN_DEVICE= 100, +} ump_uk_user; + +/** + * Get API version ([in,out] u32 api_version, [out] u32 compatible) + */ +typedef struct _ump_uk_api_version_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 version; /**< Set to the user space version on entry, stores the device driver version on exit */ + u32 compatible; /**< Non-null if the device is compatible with the client */ +} _ump_uk_api_version_s; + +/** + * ALLOCATE ([out] u32 secure_id, [in,out] u32 size, [in] contraints) + */ +typedef struct _ump_uk_allocate_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Return value from DD to Userdriver */ + u32 size; /**< Input and output. Requested size; input. Returned size; output */ + ump_uk_alloc_constraints constraints; /**< Only input to Devicedriver */ +} _ump_uk_allocate_s; + +#ifdef CONFIG_ION_EXYNOS +typedef struct _ump_uk_ion_import_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + int ion_fd; /**< ion_fd */ + u32 secure_id; /**< Return value from DD to Userdriver */ + u32 size; /**< Input and output. Requested size; input. Returned size; output */ + ump_uk_alloc_constraints constraints; /**< Only input to Devicedriver */ +} _ump_uk_ion_import_s; +#endif + +/** + * SIZE_GET ([in] u32 secure_id, [out]size ) + */ +typedef struct _ump_uk_size_get_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Input to DD */ + u32 size; /**< Returned size; output */ +} _ump_uk_size_get_s; + +/** + * Release ([in] u32 secure_id) + */ +typedef struct _ump_uk_release_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Input to DD */ +} _ump_uk_release_s; + +typedef struct _ump_uk_map_mem_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [out] Returns user-space virtual address for the mapping */ + void *phys_addr; /**< [in] physical address */ + unsigned long size; /**< [in] size */ + u32 secure_id; /**< [in] secure_id to assign to mapping */ + void * _ukk_private; /**< Only used inside linux port between kernel frontend and common part to store vma */ + u32 cookie; + u32 is_cached; /**< [in,out] caching of CPU mappings */ +} _ump_uk_map_mem_s; + +typedef struct _ump_uk_unmap_mem_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; + u32 size; + void * _ukk_private; + u32 cookie; +} _ump_uk_unmap_mem_s; + +typedef struct _ump_uk_msync_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [in] mapping addr */ + void *address; /**< [in] flush start addr */ + u32 size; /**< [in] size to flush */ + ump_uk_msync_op op; /**< [in] flush operation */ + u32 cookie; /**< [in] cookie stored with reference to the kernel mapping internals */ + u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ + u32 is_cached; /**< [out] caching of CPU mappings */ +} _ump_uk_msync_s; + +typedef struct _ump_uk_cache_operations_control_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + ump_uk_cache_op_control op; /**< [in] cache operations start/stop */ +} _ump_uk_cache_operations_control_s; + + +typedef struct _ump_uk_switch_hw_usage_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ + ump_uk_user new_user; /**< [in] cookie stored with reference to the kernel mapping internals */ + +} _ump_uk_switch_hw_usage_s; + +typedef struct _ump_uk_lock_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ + ump_uk_lock_usage lock_usage; +} _ump_uk_lock_s; + +typedef struct _ump_uk_unlock_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ +} _ump_uk_unlock_s; + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UK_TYPES_H__ */ diff --git a/drivers/media/video/samsung/ump/common/ump_ukk.h b/drivers/media/video/samsung/ump/common/ump_ukk.h new file mode 100644 index 0000000..56e4be3 --- /dev/null +++ b/drivers/media/video/samsung/ump/common/ump_ukk.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk.h + * Defines the kernel-side interface of the user-kernel interface + */ + +#ifndef __UMP_UKK_H__ +#define __UMP_UKK_H__ + +#include "mali_osk.h" +#include "ump_uk_types.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +_mali_osk_errcode_t _ump_ukk_open( void** context ); + +_mali_osk_errcode_t _ump_ukk_close( void** context ); + +_mali_osk_errcode_t _ump_ukk_allocate( _ump_uk_allocate_s *user_interaction ); + +_mali_osk_errcode_t _ump_ukk_release( _ump_uk_release_s *release_info ); + +_mali_osk_errcode_t _ump_ukk_size_get( _ump_uk_size_get_s *user_interaction ); + +_mali_osk_errcode_t _ump_ukk_map_mem( _ump_uk_map_mem_s *args ); + +_mali_osk_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args ); + +void _ump_ukk_unmap_mem( _ump_uk_unmap_mem_s *args ); + +void _ump_ukk_msync( _ump_uk_msync_s *args ); + +void _ump_ukk_cache_operations_control(_ump_uk_cache_operations_control_s* args); + +void _ump_ukk_switch_hw_usage(_ump_uk_switch_hw_usage_s *args ); + +void _ump_ukk_lock(_ump_uk_lock_s *args ); + +void _ump_ukk_unlock(_ump_uk_unlock_s *args ); + +u32 _ump_ukk_report_memory_usage( void ); + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UKK_H__ */ diff --git a/drivers/media/video/samsung/ump/include/ump_kernel_interface.h b/drivers/media/video/samsung/ump/include/ump_kernel_interface.h new file mode 100644 index 0000000..042c8b1 --- /dev/null +++ b/drivers/media/video/samsung/ump/include/ump_kernel_interface.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_interface.h + * + * This file contains the kernel space part of the UMP API. + */ + +#ifndef __UMP_KERNEL_INTERFACE_H__ +#define __UMP_KERNEL_INTERFACE_H__ + + +/** @defgroup ump_kernel_space_api UMP Kernel Space API + * @{ */ + + +#include "ump_kernel_platform.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/** + * External representation of a UMP handle in kernel space. + */ +typedef void * ump_dd_handle; + +/** + * Typedef for a secure ID, a system wide identificator for UMP memory buffers. + */ +typedef unsigned int ump_secure_id; + + +/** + * Value to indicate an invalid UMP memory handle. + */ +#define UMP_DD_HANDLE_INVALID ((ump_dd_handle)0) + + +/** + * Value to indicate an invalid secure Id. + */ +#define UMP_INVALID_SECURE_ID ((ump_secure_id)-1) + + +/** + * UMP error codes for kernel space. + */ +typedef enum +{ + UMP_DD_SUCCESS, /**< indicates success */ + UMP_DD_INVALID, /**< indicates failure */ +} ump_dd_status_code; + + +/** + * Struct used to describe a physical block used by UMP memory + */ +typedef struct ump_dd_physical_block +{ + unsigned long addr; /**< The physical address of the block */ + unsigned long size; /**< The length of the block, typically page aligned */ +} ump_dd_physical_block; + + +/** + * Retrieves the secure ID for the specified UMP memory. + * + * This identificator is unique across the entire system, and uniquely identifies + * the specified UMP memory. This identificator can later be used through the + * @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id" or + * @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" + * functions in order to access this UMP memory, for instance from another process. + * + * @note There is a user space equivalent function called @ref ump_secure_id_get "ump_secure_id_get" + * + * @see ump_dd_handle_create_from_secure_id + * @see ump_handle_create_from_secure_id + * @see ump_secure_id_get + * + * @param mem Handle to UMP memory. + * + * @return Returns the secure ID for the specified UMP memory. + */ +UMP_KERNEL_API_EXPORT ump_secure_id ump_dd_secure_id_get(ump_dd_handle mem); + + +/** + * Retrieves a handle to allocated UMP memory. + * + * The usage of UMP memory is reference counted, so this will increment the reference + * count by one for the specified UMP memory. + * Use @ref ump_dd_reference_release "ump_dd_reference_release" when there is no longer any + * use for the retrieved handle. + * + * @note There is a user space equivalent function called @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" + * + * @see ump_dd_reference_release + * @see ump_handle_create_from_secure_id + * + * @param secure_id The secure ID of the UMP memory to open, that can be retrieved using the @ref ump_secure_id_get "ump_secure_id_get " function. + * + * @return UMP_INVALID_MEMORY_HANDLE indicates failure, otherwise a valid handle is returned. + */ +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id); + + +/** + * Retrieves the number of physical blocks used by the specified UMP memory. + * + * This function retrieves the number of @ref ump_dd_physical_block "ump_dd_physical_block" structs needed + * to describe the physical memory layout of the given UMP memory. This can later be used when calling + * the functions @ref ump_dd_phys_blocks_get "ump_dd_phys_blocks_get" and + * @ref ump_dd_phys_block_get "ump_dd_phys_block_get". + * + * @see ump_dd_phys_blocks_get + * @see ump_dd_phys_block_get + * + * @param mem Handle to UMP memory. + * + * @return The number of ump_dd_physical_block structs required to describe the physical memory layout of the specified UMP memory. + */ +UMP_KERNEL_API_EXPORT unsigned long ump_dd_phys_block_count_get(ump_dd_handle mem); + + +/** + * Retrieves all physical memory block information for specified UMP memory. + * + * This function can be used by other device drivers in order to create MMU tables. + * + * @note This function will fail if the num_blocks parameter is either to large or to small. + * + * @see ump_dd_phys_block_get + * + * @param mem Handle to UMP memory. + * @param blocks An array of @ref ump_dd_physical_block "ump_dd_physical_block" structs that will receive the physical description. + * @param num_blocks The number of blocks to return in the blocks array. Use the function + * @ref ump_dd_phys_block_count_get "ump_dd_phys_block_count_get" first to determine the number of blocks required. + * + * @return UMP_DD_SUCCESS indicates success, UMP_DD_INVALID indicates failure. + */ +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle mem, ump_dd_physical_block * blocks, unsigned long num_blocks); + + +/** + * Retrieves the physical memory block information for specified block for the specified UMP memory. + * + * This function can be used by other device drivers in order to create MMU tables. + * + * @note This function will return UMP_DD_INVALID if the specified index is out of range. + * + * @see ump_dd_phys_blocks_get + * + * @param mem Handle to UMP memory. + * @param index Which physical info block to retrieve. + * @param block Pointer to a @ref ump_dd_physical_block "ump_dd_physical_block" struct which will receive the requested information. + * + * @return UMP_DD_SUCCESS indicates success, UMP_DD_INVALID indicates failure. + */ +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle mem, unsigned long index, ump_dd_physical_block * block); + + +/** + * Retrieves the actual size of the specified UMP memory. + * + * The size is reported in bytes, and is typically page aligned. + * + * @note There is a user space equivalent function called @ref ump_size_get "ump_size_get" + * + * @see ump_size_get + * + * @param mem Handle to UMP memory. + * + * @return Returns the allocated size of the specified UMP memory, in bytes. + */ +UMP_KERNEL_API_EXPORT unsigned long ump_dd_size_get(ump_dd_handle mem); + + +/** + * Adds an extra reference to the specified UMP memory. + * + * This function adds an extra reference to the specified UMP memory. This function should + * be used every time a UMP memory handle is duplicated, that is, assigned to another ump_dd_handle + * variable. The function @ref ump_dd_reference_release "ump_dd_reference_release" must then be used + * to release each copy of the UMP memory handle. + * + * @note You are not required to call @ref ump_dd_reference_add "ump_dd_reference_add" + * for UMP handles returned from + * @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id", + * because these handles are already reference counted by this function. + * + * @note There is a user space equivalent function called @ref ump_reference_add "ump_reference_add" + * + * @see ump_reference_add + * + * @param mem Handle to UMP memory. + */ +UMP_KERNEL_API_EXPORT void ump_dd_reference_add(ump_dd_handle mem); + + +/** + * Releases a reference from the specified UMP memory. + * + * This function should be called once for every reference to the UMP memory handle. + * When the last reference is released, all resources associated with this UMP memory + * handle are freed. + * + * @note There is a user space equivalent function called @ref ump_reference_release "ump_reference_release" + * + * @see ump_reference_release + * + * @param mem Handle to UMP memory. + */ +UMP_KERNEL_API_EXPORT void ump_dd_reference_release(ump_dd_handle mem); + + +#ifdef __cplusplus +} +#endif + + +/** @} */ /* end group ump_kernel_space_api */ + + +#endif /* __UMP_KERNEL_INTERFACE_H__ */ diff --git a/drivers/media/video/samsung/ump/include/ump_kernel_interface_ref_drv.h b/drivers/media/video/samsung/ump/include/ump_kernel_interface_ref_drv.h new file mode 100644 index 0000000..36c5c9d --- /dev/null +++ b/drivers/media/video/samsung/ump/include/ump_kernel_interface_ref_drv.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_interface.h + */ + +#ifndef __UMP_KERNEL_INTERFACE_REF_DRV_H__ +#define __UMP_KERNEL_INTERFACE_REF_DRV_H__ + +#include "ump_kernel_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Turn specified physical memory into UMP memory. */ +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block * blocks, unsigned long num_blocks); +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_get(ump_secure_id secure_id); +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_meminfo_set(ump_dd_handle memh, void* args); +UMP_KERNEL_API_EXPORT void *ump_dd_meminfo_get(ump_secure_id secure_id, void* args); +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_get_from_vaddr(unsigned long vaddr); + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_KERNEL_INTERFACE_REF_DRV_H__ */ diff --git a/drivers/media/video/samsung/ump/include/ump_kernel_platform.h b/drivers/media/video/samsung/ump/include/ump_kernel_platform.h new file mode 100644 index 0000000..4349605 --- /dev/null +++ b/drivers/media/video/samsung/ump/include/ump_kernel_platform.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_platform.h + * + * This file should define UMP_KERNEL_API_EXPORT, + * which dictates how the UMP kernel API should be exported/imported. + * Modify this file, if needed, to match your platform setup. + */ + +#ifndef __UMP_KERNEL_PLATFORM_H__ +#define __UMP_KERNEL_PLATFORM_H__ + +/** @addtogroup ump_kernel_space_api + * @{ */ + +/** + * A define which controls how UMP kernel space API functions are imported and exported. + * This define should be set by the implementor of the UMP API. + */ + +#if defined(_WIN32) + +#if defined(UMP_BUILDING_UMP_LIBRARY) +#define UMP_KERNEL_API_EXPORT __declspec(dllexport) +#else +#define UMP_KERNEL_API_EXPORT __declspec(dllimport) +#endif + +#else + +#define UMP_KERNEL_API_EXPORT + +#endif + + +/** @} */ /* end group ump_kernel_space_api */ + + +#endif /* __UMP_KERNEL_PLATFORM_H__ */ diff --git a/drivers/media/video/samsung/ump/linux/license/gpl/ump_kernel_license.h b/drivers/media/video/samsung/ump/linux/license/gpl/ump_kernel_license.h new file mode 100644 index 0000000..187e33b --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/license/gpl/ump_kernel_license.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_license.h + * Defines for the macro MODULE_LICENSE. + */ + +#ifndef __UMP_KERNEL_LICENSE_H__ +#define __UMP_KERNEL_LICENSE_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define UMP_KERNEL_LINUX_LICENSE "GPL" +#define UMP_LICENSE_IS_GPL 1 + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_KERNEL_LICENSE_H__ */ diff --git a/drivers/media/video/samsung/ump/linux/ump_ioctl.h b/drivers/media/video/samsung/ump/linux/ump_ioctl.h new file mode 100644 index 0000000..83bb2a4 --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_ioctl.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_IOCTL_H__ +#define __UMP_IOCTL_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <linux/types.h> +#include <linux/ioctl.h> + +#include <ump_uk_types.h> + +#ifndef __user +#define __user +#endif + + +/** + * @file UMP_ioctl.h + * This file describes the interface needed to use the Linux device driver. + * The interface is used by the userpace UMP driver. + */ + +#define UMP_IOCTL_NR 0x90 + + +#define UMP_IOC_QUERY_API_VERSION _IOR(UMP_IOCTL_NR, _UMP_IOC_QUERY_API_VERSION, _ump_uk_api_version_s) +#define UMP_IOC_ALLOCATE _IOWR(UMP_IOCTL_NR, _UMP_IOC_ALLOCATE, _ump_uk_allocate_s) +#define UMP_IOC_RELEASE _IOR(UMP_IOCTL_NR, _UMP_IOC_RELEASE, _ump_uk_release_s) +#define UMP_IOC_SIZE_GET _IOWR(UMP_IOCTL_NR, _UMP_IOC_SIZE_GET, _ump_uk_size_get_s) +#define UMP_IOC_MSYNC _IOW(UMP_IOCTL_NR, _UMP_IOC_MSYNC, _ump_uk_msync_s) +#ifdef CONFIG_ION_EXYNOS +#define UMP_IOC_ION_IMPORT _IOW(UMP_IOCTL_NR, _UMP_IOC_ION_IMPORT, _ump_uk_ion_import_s) +#endif +#ifdef CONFIG_DMA_SHARED_BUFFER +#define UMP_IOC_DMABUF_IMPORT _IOW(UMP_IOCTL_NR, _UMP_IOC_DMABUF_IMPORT,\ + struct ump_uk_dmabuf) +#endif + +#define UMP_IOC_CACHE_OPERATIONS_CONTROL _IOW(UMP_IOCTL_NR, _UMP_IOC_CACHE_OPERATIONS_CONTROL, _ump_uk_cache_operations_control_s) +#define UMP_IOC_SWITCH_HW_USAGE _IOW(UMP_IOCTL_NR, _UMP_IOC_SWITCH_HW_USAGE, _ump_uk_switch_hw_usage_s) +#define UMP_IOC_LOCK _IOW(UMP_IOCTL_NR, _UMP_IOC_LOCK, _ump_uk_lock_s) +#define UMP_IOC_UNLOCK _IOW(UMP_IOCTL_NR, _UMP_IOC_UNLOCK, _ump_uk_unlock_s) + + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_IOCTL_H__ */ diff --git a/drivers/media/video/samsung/ump/linux/ump_kernel_linux.c b/drivers/media/video/samsung/ump/linux/ump_kernel_linux.c new file mode 100644 index 0000000..a358a3c --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_kernel_linux.c @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> /* kernel module definitions */ +#include <linux/fs.h> /* file system operations */ +#include <linux/cdev.h> /* character device definitions */ +#include <linux/ioport.h> /* request_mem_region */ +#include <linux/mm.h> /* memory management functions and types */ +#include <asm/uaccess.h> /* user space access */ +#include <asm/atomic.h> +#include <linux/device.h> +#include <linux/debugfs.h> + +#include "arch/config.h" /* Configuration for current platform. The symlinc for arch is set by Makefile */ +#include "ump_ioctl.h" +#include "ump_kernel_common.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_interface_ref_drv.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" +#include "ump_kernel_memory_backend_os.h" +#include "ump_kernel_memory_backend_dedicated.h" +#include "ump_kernel_license.h" + +#include "ump_osk.h" +#include "ump_ukk.h" +#include "ump_uk_types.h" +#include "ump_ukk_wrappers.h" +#include "ump_ukk_ref_wrappers.h" + +#ifdef CONFIG_ION_EXYNOS +#include <linux/ion.h> +extern struct ion_device *ion_exynos; +struct ion_client *ion_client_ump = NULL; +#endif + +/* Module parameter to control log level */ +int ump_debug_level = 2; +module_param(ump_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(ump_debug_level, "Higher number, more dmesg output"); + +/* By default the module uses any available major, but it's possible to set it at load time to a specific number */ +int ump_major = 243; +module_param(ump_major, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_major, "Device major number"); + +/* Name of the UMP device driver */ +static char ump_dev_name[] = "ump"; /* should be const, but the functions we call requires non-cost */ + + +#if UMP_LICENSE_IS_GPL +static struct dentry *ump_debugfs_dir = NULL; +#endif + +/* + * The data which we attached to each virtual memory mapping request we get. + * Each memory mapping has a reference to the UMP memory it maps. + * We release this reference when the last memory mapping is unmapped. + */ +typedef struct ump_vma_usage_tracker +{ + int references; + ump_dd_handle handle; +} ump_vma_usage_tracker; + +struct ump_device +{ + struct cdev cdev; +#if UMP_LICENSE_IS_GPL + struct class * ump_class; +#endif +}; + +/* The global variable containing the global device data */ +static struct ump_device ump_device; + + +/* Forward declare static functions */ +static int ump_file_open(struct inode *inode, struct file *filp); +static int ump_file_release(struct inode *inode, struct file *filp); +#ifdef HAVE_UNLOCKED_IOCTL +static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#else +static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#endif +static int ump_file_mmap(struct file * filp, struct vm_area_struct * vma); + +#if defined(CONFIG_VIDEO_UMP) +extern int map_errcode( _mali_osk_errcode_t err ); +#endif + +/* This variable defines the file operations this UMP device driver offer */ +static struct file_operations ump_fops = +{ + .owner = THIS_MODULE, + .open = ump_file_open, + .release = ump_file_release, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = ump_file_ioctl, +#else + .ioctl = ump_file_ioctl, +#endif + .mmap = ump_file_mmap +}; + + +/* This function is called by Linux to initialize this module. + * All we do is initialize the UMP device driver. + */ +static int ump_initialize_module(void) +{ + _mali_osk_errcode_t err; + + DBG_MSG(2, ("Inserting UMP device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__)); + + err = ump_kernel_constructor(); + if (_MALI_OSK_ERR_OK != err) + { + MSG_ERR(("UMP device driver init failed\n")); + return map_errcode(err); + } + + MSG(("UMP device driver %s loaded\n", SVN_REV_STRING)); + return 0; +} + + + +/* + * This function is called by Linux to unload/terminate/exit/cleanup this module. + * All we do is terminate the UMP device driver. + */ +static void ump_cleanup_module(void) +{ +#ifdef CONFIG_ION_EXYNOS + if (ion_client_ump) + ion_client_destroy(ion_client_ump); +#endif + + DBG_MSG(2, ("Unloading UMP device driver\n")); + ump_kernel_destructor(); + DBG_MSG(2, ("Module unloaded\n")); +} + + + +static ssize_t ump_memory_used_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + size_t r; + u32 mem = _ump_ukk_report_memory_usage(); + + r = snprintf(buf, 64, "%u\n", mem); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static const struct file_operations ump_memory_usage_fops = { + .owner = THIS_MODULE, + .read = ump_memory_used_read, +}; + +/* + * Initialize the UMP device driver. + */ +int ump_kernel_device_initialize(void) +{ + int err; + dev_t dev = 0; +#if UMP_LICENSE_IS_GPL + ump_debugfs_dir = debugfs_create_dir(ump_dev_name, NULL); + if (ERR_PTR(-ENODEV) == ump_debugfs_dir) + { + ump_debugfs_dir = NULL; + } + else + { + debugfs_create_file("memory_usage", 0400, ump_debugfs_dir, NULL, &ump_memory_usage_fops); + } +#endif + + if (0 == ump_major) + { + /* auto select a major */ + err = alloc_chrdev_region(&dev, 0, 1, ump_dev_name); + ump_major = MAJOR(dev); + } + else + { + /* use load time defined major number */ + dev = MKDEV(ump_major, 0); + err = register_chrdev_region(dev, 1, ump_dev_name); + } + + if (0 == err) + { + memset(&ump_device, 0, sizeof(ump_device)); + + /* initialize our char dev data */ + cdev_init(&ump_device.cdev, &ump_fops); + ump_device.cdev.owner = THIS_MODULE; + ump_device.cdev.ops = &ump_fops; + + /* register char dev with the kernel */ + err = cdev_add(&ump_device.cdev, dev, 1/*count*/); + if (0 == err) + { + +#if UMP_LICENSE_IS_GPL + ump_device.ump_class = class_create(THIS_MODULE, ump_dev_name); + if (IS_ERR(ump_device.ump_class)) + { + err = PTR_ERR(ump_device.ump_class); + } + else + { + struct device * mdev; + mdev = device_create(ump_device.ump_class, NULL, dev, NULL, ump_dev_name); + if (!IS_ERR(mdev)) + { + return 0; + } + + err = PTR_ERR(mdev); + } + cdev_del(&ump_device.cdev); +#else + return 0; +#endif + } + + unregister_chrdev_region(dev, 1); + } + + return err; +} + + + +/* + * Terminate the UMP device driver + */ +void ump_kernel_device_terminate(void) +{ + dev_t dev = MKDEV(ump_major, 0); + +#if UMP_LICENSE_IS_GPL + device_destroy(ump_device.ump_class, dev); + class_destroy(ump_device.ump_class); +#endif + + /* unregister char device */ + cdev_del(&ump_device.cdev); + + /* free major */ + unregister_chrdev_region(dev, 1); + +#if UMP_LICENSE_IS_GPL + if(ump_debugfs_dir) + debugfs_remove_recursive(ump_debugfs_dir); +#endif +} + +/* + * Open a new session. User space has called open() on us. + */ +static int ump_file_open(struct inode *inode, struct file *filp) +{ + struct ump_session_data * session_data; + _mali_osk_errcode_t err; + + /* input validation */ + if (0 != MINOR(inode->i_rdev)) + { + MSG_ERR(("Minor not zero in ump_file_open()\n")); + return -ENODEV; + } + + /* Call the OS-Independent UMP Open function */ + err = _ump_ukk_open((void**) &session_data ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("Ump failed to open a new session\n")); + return map_errcode( err ); + } + + filp->private_data = (void*)session_data; + filp->f_pos = 0; + + return 0; /* success */ +} + + + +/* + * Close a session. User space has called close() or crashed/terminated. + */ +static int ump_file_release(struct inode *inode, struct file *filp) +{ + _mali_osk_errcode_t err; + + err = _ump_ukk_close((void**) &filp->private_data ); + if( _MALI_OSK_ERR_OK != err ) + { + return map_errcode( err ); + } + + return 0; /* success */ +} + + + +/* + * Handle IOCTL requests. + */ +#ifdef HAVE_UNLOCKED_IOCTL +static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + int err = -ENOTTY; + void __user * argument; + struct ump_session_data * session_data; + +#ifndef HAVE_UNLOCKED_IOCTL + (void)inode; /* inode not used */ +#endif + + session_data = (struct ump_session_data *)filp->private_data; + if (NULL == session_data) + { + MSG_ERR(("No session data attached to file object\n")); + return -ENOTTY; + } + + /* interpret the argument as a user pointer to something */ + argument = (void __user *)arg; + + switch (cmd) + { + case UMP_IOC_QUERY_API_VERSION: + err = ump_get_api_version_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_ALLOCATE : + err = ump_allocate_wrapper((u32 __user *)argument, session_data); + break; +#ifdef CONFIG_ION_EXYNOS + case UMP_IOC_ION_IMPORT: + err = ump_ion_import_wrapper((u32 __user *)argument, session_data); + break; +#endif +#ifdef CONFIG_DMA_SHARED_BUFFER + case UMP_IOC_DMABUF_IMPORT: + err = ump_dmabuf_import_wrapper((u32 __user *)argument, + session_data); + break; +#endif + case UMP_IOC_RELEASE: + err = ump_release_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_SIZE_GET: + err = ump_size_get_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_MSYNC: + err = ump_msync_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_CACHE_OPERATIONS_CONTROL: + err = ump_cache_operations_control_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_SWITCH_HW_USAGE: + err = ump_switch_hw_usage_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_LOCK: + err = ump_lock_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_UNLOCK: + err = ump_unlock_wrapper((u32 __user *)argument, session_data); + break; + + default: + DBG_MSG(1, ("No handler for IOCTL. cmd: 0x%08x, arg: 0x%08lx\n", cmd, arg)); + err = -EFAULT; + break; + } + + return err; +} + +#ifndef CONFIG_VIDEO_UMP +int map_errcode( _mali_osk_errcode_t err ) +{ + switch(err) + { + case _MALI_OSK_ERR_OK : return 0; + case _MALI_OSK_ERR_FAULT: return -EFAULT; + case _MALI_OSK_ERR_INVALID_FUNC: return -ENOTTY; + case _MALI_OSK_ERR_INVALID_ARGS: return -EINVAL; + case _MALI_OSK_ERR_NOMEM: return -ENOMEM; + case _MALI_OSK_ERR_TIMEOUT: return -ETIMEDOUT; + case _MALI_OSK_ERR_RESTARTSYSCALL: return -ERESTARTSYS; + case _MALI_OSK_ERR_ITEM_NOT_FOUND: return -ENOENT; + default: return -EFAULT; + } +} +#endif + +/* + * Handle from OS to map specified virtual memory to specified UMP memory. + */ +static int ump_file_mmap(struct file * filp, struct vm_area_struct * vma) +{ + _ump_uk_map_mem_s args; + _mali_osk_errcode_t err; + struct ump_session_data * session_data; + + /* Validate the session data */ + session_data = (struct ump_session_data *)filp->private_data; + if (NULL == session_data || NULL == session_data->cookies_map->table->mappings) + { + MSG_ERR(("mmap() called without any session data available\n")); + return -EFAULT; + } + + /* Re-pack the arguments that mmap() packed for us */ + args.ctx = session_data; + args.phys_addr = 0; + args.size = vma->vm_end - vma->vm_start; + args._ukk_private = vma; + args.secure_id = vma->vm_pgoff; + args.is_cached = 0; + + if (!(vma->vm_flags & VM_SHARED)) + { + args.is_cached = 1; + vma->vm_flags = vma->vm_flags | VM_SHARED | VM_MAYSHARE ; + DBG_MSG(3, ("UMP Map function: Forcing the CPU to use cache\n")); + } + /* By setting this flag, during a process fork; the child process will not have the parent UMP mappings */ + vma->vm_flags |= VM_DONTCOPY; + + DBG_MSG(4, ("UMP vma->flags: %x\n", vma->vm_flags )); + + /* Call the common mmap handler */ + err = _ump_ukk_map_mem( &args ); + if ( _MALI_OSK_ERR_OK != err) + { + MSG_ERR(("_ump_ukk_map_mem() failed in function ump_file_mmap()")); + return map_errcode( err ); + } + + return 0; /* success */ +} + +/* Export UMP kernel space API functions */ +EXPORT_SYMBOL(ump_dd_secure_id_get); +EXPORT_SYMBOL(ump_dd_handle_create_from_secure_id); +EXPORT_SYMBOL(ump_dd_phys_block_count_get); +EXPORT_SYMBOL(ump_dd_phys_block_get); +EXPORT_SYMBOL(ump_dd_phys_blocks_get); +EXPORT_SYMBOL(ump_dd_size_get); +EXPORT_SYMBOL(ump_dd_reference_add); +EXPORT_SYMBOL(ump_dd_reference_release); +EXPORT_SYMBOL(ump_dd_meminfo_get); +EXPORT_SYMBOL(ump_dd_meminfo_set); +EXPORT_SYMBOL(ump_dd_handle_get_from_vaddr); + +/* Export our own extended kernel space allocator */ +EXPORT_SYMBOL(ump_dd_handle_create_from_phys_blocks); + +/* Setup init and exit functions for this module */ +//module_init(ump_initialize_module); +arch_initcall(ump_initialize_module); +module_exit(ump_cleanup_module); + +/* And some module informatio */ +MODULE_LICENSE(UMP_KERNEL_LINUX_LICENSE); +MODULE_AUTHOR("ARM Ltd."); +MODULE_VERSION(SVN_REV_STRING); diff --git a/drivers/media/video/samsung/ump/linux/ump_kernel_linux.h b/drivers/media/video/samsung/ump/linux/ump_kernel_linux.h new file mode 100644 index 0000000..4985bb7 --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_kernel_linux.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_LINUX_H__ +#define __UMP_KERNEL_LINUX_H__ + +int ump_kernel_device_initialize(void); +void ump_kernel_device_terminate(void); + + +#endif /* __UMP_KERNEL_H__ */ diff --git a/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_dedicated.c b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_dedicated.c new file mode 100644 index 0000000..82c16cc --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_dedicated.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* needed to detect kernel version specific code */ +#include <linux/version.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include <linux/semaphore.h> +#else /* pre 2.6.26 the file was in the arch specific location */ +#include <asm/semaphore.h> +#endif + +#include <linux/mm.h> +#include <linux/slab.h> +#include <asm/atomic.h> +#include <linux/vmalloc.h> +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend.h" + + + +#define UMP_BLOCK_SIZE (256UL * 1024UL) /* 256kB, remember to keep the ()s */ + + + +typedef struct block_info +{ + struct block_info * next; +} block_info; + + + +typedef struct block_allocator +{ + struct semaphore mutex; + block_info * all_blocks; + block_info * first_free; + u32 base; + u32 num_blocks; + u32 num_free; +} block_allocator; + + +static void block_allocator_shutdown(ump_memory_backend * backend); +static int block_allocator_allocate(void* ctx, ump_dd_mem * mem); +static void block_allocator_release(void * ctx, ump_dd_mem * handle); +static inline u32 get_phys(block_allocator * allocator, block_info * block); +static u32 block_allocator_stat(struct ump_memory_backend *backend); + + + +/* + * Create dedicated memory backend + */ +ump_memory_backend * ump_block_allocator_create(u32 base_address, u32 size) +{ + ump_memory_backend * backend; + block_allocator * allocator; + u32 usable_size; + u32 num_blocks; + + usable_size = (size + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1); + num_blocks = usable_size / UMP_BLOCK_SIZE; + + if (0 == usable_size) + { + DBG_MSG(1, ("Memory block of size %u is unusable\n", size)); + return NULL; + } + + DBG_MSG(5, ("Creating dedicated UMP memory backend. Base address: 0x%08x, size: 0x%08x\n", base_address, size)); + DBG_MSG(6, ("%u usable bytes which becomes %u blocks\n", usable_size, num_blocks)); + + backend = kzalloc(sizeof(ump_memory_backend), GFP_KERNEL); + if (NULL != backend) + { + allocator = kmalloc(sizeof(block_allocator), GFP_KERNEL); + if (NULL != allocator) + { + allocator->all_blocks = kmalloc(sizeof(block_allocator) * num_blocks, GFP_KERNEL); + if (NULL != allocator->all_blocks) + { + int i; + + allocator->first_free = NULL; + allocator->num_blocks = num_blocks; + allocator->num_free = num_blocks; + allocator->base = base_address; + sema_init(&allocator->mutex, 1); + + for (i = 0; i < num_blocks; i++) + { + allocator->all_blocks[i].next = allocator->first_free; + allocator->first_free = &allocator->all_blocks[i]; + } + + backend->ctx = allocator; + backend->allocate = block_allocator_allocate; + backend->release = block_allocator_release; + backend->shutdown = block_allocator_shutdown; + backend->stat = block_allocator_stat; + backend->pre_allocate_physical_check = NULL; + backend->adjust_to_mali_phys = NULL; + backend->get = NULL; + backend->set = NULL; + + return backend; + } + kfree(allocator); + } + kfree(backend); + } + + return NULL; +} + + + +/* + * Destroy specified dedicated memory backend + */ +static void block_allocator_shutdown(ump_memory_backend * backend) +{ + block_allocator * allocator; + + BUG_ON(!backend); + BUG_ON(!backend->ctx); + + allocator = (block_allocator*)backend->ctx; + + DBG_MSG_IF(1, allocator->num_free != allocator->num_blocks, ("%u blocks still in use during shutdown\n", allocator->num_blocks - allocator->num_free)); + + kfree(allocator->all_blocks); + kfree(allocator); + kfree(backend); +} + + + +static int block_allocator_allocate(void* ctx, ump_dd_mem * mem) +{ + block_allocator * allocator; + u32 left; + block_info * last_allocated = NULL; + int i = 0; + + BUG_ON(!ctx); + BUG_ON(!mem); + + allocator = (block_allocator*)ctx; + left = mem->size_bytes; + + BUG_ON(!left); + BUG_ON(!&allocator->mutex); + + mem->nr_blocks = ((left + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1)) / UMP_BLOCK_SIZE; + mem->block_array = (ump_dd_physical_block*)vmalloc(sizeof(ump_dd_physical_block) * mem->nr_blocks); + if (NULL == mem->block_array) + { + MSG_ERR(("Failed to allocate block array\n")); + return 0; + } + + if (down_interruptible(&allocator->mutex)) + { + MSG_ERR(("Could not get mutex to do block_allocate\n")); + return 0; + } + + mem->size_bytes = 0; + + while ((left > 0) && (allocator->first_free)) + { + block_info * block; + + block = allocator->first_free; + allocator->first_free = allocator->first_free->next; + block->next = last_allocated; + last_allocated = block; + allocator->num_free--; + + mem->block_array[i].addr = get_phys(allocator, block); + mem->block_array[i].size = UMP_BLOCK_SIZE; + mem->size_bytes += UMP_BLOCK_SIZE; + + i++; + + if (left < UMP_BLOCK_SIZE) left = 0; + else left -= UMP_BLOCK_SIZE; + } + + if (left) + { + block_info * block; + /* release all memory back to the pool */ + while (last_allocated) + { + block = last_allocated->next; + last_allocated->next = allocator->first_free; + allocator->first_free = last_allocated; + last_allocated = block; + allocator->num_free++; + } + + vfree(mem->block_array); + mem->backend_info = NULL; + mem->block_array = NULL; + + DBG_MSG(4, ("Could not find a mem-block for the allocation.\n")); + up(&allocator->mutex); + + return 0; + } + + mem->backend_info = last_allocated; + + up(&allocator->mutex); + mem->is_cached=0; + + return 1; +} + + + +static void block_allocator_release(void * ctx, ump_dd_mem * handle) +{ + block_allocator * allocator; + block_info * block, * next; + + BUG_ON(!ctx); + BUG_ON(!handle); + + allocator = (block_allocator*)ctx; + block = (block_info*)handle->backend_info; + BUG_ON(!block); + + if (down_interruptible(&allocator->mutex)) + { + MSG_ERR(("Allocator release: Failed to get mutex - memory leak\n")); + return; + } + + while (block) + { + next = block->next; + + BUG_ON( (block < allocator->all_blocks) || (block > (allocator->all_blocks + allocator->num_blocks))); + + block->next = allocator->first_free; + allocator->first_free = block; + allocator->num_free++; + + block = next; + } + DBG_MSG(3, ("%d blocks free after release call\n", allocator->num_free)); + up(&allocator->mutex); + + vfree(handle->block_array); + handle->block_array = NULL; +} + + + +/* + * Helper function for calculating the physical base adderss of a memory block + */ +static inline u32 get_phys(block_allocator * allocator, block_info * block) +{ + return allocator->base + ((block - allocator->all_blocks) * UMP_BLOCK_SIZE); +} + +static u32 block_allocator_stat(struct ump_memory_backend *backend) +{ + block_allocator *allocator; + BUG_ON(!backend); + allocator = (block_allocator*)backend->ctx; + BUG_ON(!allocator); + + return (allocator->num_blocks - allocator->num_free)* UMP_BLOCK_SIZE; +} diff --git a/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_dedicated.h b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_dedicated.h new file mode 100644 index 0000000..ca8faae --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_dedicated.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_backend_dedicated.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ + +#include "ump_kernel_memory_backend.h" + +ump_memory_backend * ump_block_allocator_create(u32 base_address, u32 size); + +#endif /* __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ */ + diff --git a/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_os.c b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_os.c new file mode 100644 index 0000000..8f6a9b3 --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_os.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* needed to detect kernel version specific code */ +#include <linux/version.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include <linux/semaphore.h> +#else /* pre 2.6.26 the file was in the arch specific location */ +#include <asm/semaphore.h> +#endif + +#include <linux/dma-mapping.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <asm/atomic.h> +#include <linux/vmalloc.h> +#include <asm/cacheflush.h> +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend.h" + + + +typedef struct os_allocator +{ + struct semaphore mutex; + u32 num_pages_max; /**< Maximum number of pages to allocate from the OS */ + u32 num_pages_allocated; /**< Number of pages allocated from the OS */ +} os_allocator; + + + +static void os_free(void* ctx, ump_dd_mem * descriptor); +static int os_allocate(void* ctx, ump_dd_mem * descriptor); +static void os_memory_backend_destroy(ump_memory_backend * backend); +static u32 os_stat(struct ump_memory_backend *backend); + + + +/* + * Create OS memory backend + */ +ump_memory_backend * ump_os_memory_backend_create(const int max_allocation) +{ + ump_memory_backend * backend; + os_allocator * info; + + info = kmalloc(sizeof(os_allocator), GFP_KERNEL); + if (NULL == info) + { + return NULL; + } + + info->num_pages_max = max_allocation >> PAGE_SHIFT; + info->num_pages_allocated = 0; + + sema_init(&info->mutex, 1); + + backend = kmalloc(sizeof(ump_memory_backend), GFP_KERNEL); + if (NULL == backend) + { + kfree(info); + return NULL; + } + + backend->ctx = info; + backend->allocate = os_allocate; + backend->release = os_free; + backend->shutdown = os_memory_backend_destroy; + backend->stat = os_stat; + backend->pre_allocate_physical_check = NULL; + backend->adjust_to_mali_phys = NULL; + backend->get = NULL; + backend->set = NULL; + + return backend; +} + + + +/* + * Destroy specified OS memory backend + */ +static void os_memory_backend_destroy(ump_memory_backend * backend) +{ + os_allocator * info = (os_allocator*)backend->ctx; + + DBG_MSG_IF(1, 0 != info->num_pages_allocated, ("%d pages still in use during shutdown\n", info->num_pages_allocated)); + + kfree(info); + kfree(backend); +} + + + +/* + * Allocate UMP memory + */ +static int os_allocate(void* ctx, ump_dd_mem * descriptor) +{ + u32 left; + os_allocator * info; + int pages_allocated = 0; + int is_cached; + + BUG_ON(!descriptor); + BUG_ON(!ctx); + + info = (os_allocator*)ctx; + left = descriptor->size_bytes; + is_cached = descriptor->is_cached; + + if (down_interruptible(&info->mutex)) + { + DBG_MSG(1, ("Failed to get mutex in os_free\n")); + return 0; /* failure */ + } + + descriptor->backend_info = NULL; + descriptor->nr_blocks = ((left + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) >> PAGE_SHIFT; + + DBG_MSG(5, ("Allocating page array. Size: %lu\n", descriptor->nr_blocks * sizeof(ump_dd_physical_block))); + + descriptor->block_array = (ump_dd_physical_block *)vmalloc(sizeof(ump_dd_physical_block) * descriptor->nr_blocks); + if (NULL == descriptor->block_array) + { + up(&info->mutex); + DBG_MSG(1, ("Block array could not be allocated\n")); + return 0; /* failure */ + } + + while (left > 0) + { + struct page * new_page; + + if (is_cached) + { + new_page = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_NORETRY | __GFP_NOWARN ); + } else + { + new_page = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_NORETRY | __GFP_NOWARN | __GFP_COLD); + } + if (NULL == new_page) + { + MSG_ERR(("UMP memory allocated: Out of Memory !!\n")); + break; + } + + /* Ensure page caches are flushed. */ + if ( is_cached ) + { + descriptor->block_array[pages_allocated].addr = page_to_phys(new_page); + descriptor->block_array[pages_allocated].size = PAGE_SIZE; + } else + { + descriptor->block_array[pages_allocated].addr = dma_map_page(NULL, new_page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL ); + descriptor->block_array[pages_allocated].size = PAGE_SIZE; + } + + DBG_MSG(5, ("Allocated page 0x%08lx cached: %d\n", descriptor->block_array[pages_allocated].addr, is_cached)); + + if (left < PAGE_SIZE) + { + left = 0; + } + else + { + left -= PAGE_SIZE; + } + + pages_allocated++; + } + + DBG_MSG(5, ("Alloce for ID:%2d got %d pages, cached: %d\n", descriptor->secure_id, pages_allocated)); + + if (left) + { + DBG_MSG(1, ("Failed to allocate needed pages\n")); + DBG_MSG(1, ("UMP memory allocated: %d kB Configured maximum OS memory usage: %d kB\n", + (pages_allocated * _MALI_OSK_CPU_PAGE_SIZE)/1024, (info->num_pages_max* _MALI_OSK_CPU_PAGE_SIZE)/1024)); + + while(pages_allocated) + { + pages_allocated--; + if ( !is_cached ) + { + dma_unmap_page(NULL, descriptor->block_array[pages_allocated].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + } + __free_page(pfn_to_page(descriptor->block_array[pages_allocated].addr >> PAGE_SHIFT) ); + } + + up(&info->mutex); + + return 0; /* failure */ + } + + info->num_pages_allocated += pages_allocated; + + DBG_MSG(6, ("%d out of %d pages now allocated\n", info->num_pages_allocated, info->num_pages_max)); + + up(&info->mutex); + + return 1; /* success*/ +} + + +/* + * Free specified UMP memory + */ +static void os_free(void* ctx, ump_dd_mem * descriptor) +{ + os_allocator * info; + int i; + + BUG_ON(!ctx); + BUG_ON(!descriptor); + + info = (os_allocator*)ctx; + + BUG_ON(descriptor->nr_blocks > info->num_pages_allocated); + + if (down_interruptible(&info->mutex)) + { + DBG_MSG(1, ("Failed to get mutex in os_free\n")); + return; + } + + DBG_MSG(5, ("Releasing %lu OS pages\n", descriptor->nr_blocks)); + + info->num_pages_allocated -= descriptor->nr_blocks; + + up(&info->mutex); + + for ( i = 0; i < descriptor->nr_blocks; i++) + { + DBG_MSG(6, ("Freeing physical page. Address: 0x%08lx\n", descriptor->block_array[i].addr)); + if ( ! descriptor->is_cached) + { + dma_unmap_page(NULL, descriptor->block_array[i].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + } + __free_page(pfn_to_page(descriptor->block_array[i].addr>>PAGE_SHIFT) ); + } + + vfree(descriptor->block_array); +} + + +static u32 os_stat(struct ump_memory_backend *backend) +{ + os_allocator *info; + info = (os_allocator*)backend->ctx; + return info->num_pages_allocated * _MALI_OSK_MALI_PAGE_SIZE; +} diff --git a/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_os.h b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_os.h new file mode 100644 index 0000000..6f7e610 --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_os.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_backend_os.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_OS_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_OS_H__ + +#include "ump_kernel_memory_backend.h" + +ump_memory_backend * ump_os_memory_backend_create(const int max_allocation); + +#endif /* __UMP_KERNEL_MEMORY_BACKEND_OS_H__ */ + diff --git a/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_vcm.c b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_vcm.c new file mode 100644 index 0000000..46797ea --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_vcm.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* create by boojin.kim@samsung.com */ +/* needed to detect kernel version specific code */ +#include <linux/version.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include <linux/semaphore.h> +#else /* pre 2.6.26 the file was in the arch specific location */ +#include <asm/semaphore.h> +#endif + +#include <linux/mm.h> +#include <linux/slab.h> +#include <asm/atomic.h> +#include <linux/vmalloc.h> +#include <asm/cacheflush.h> +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend.h" +#include "ump_kernel_interface_ref_drv.h" +#include "ump_kernel_memory_backend_vcm.h" +#include "../common/ump_uk_types.h" +#include <linux/vcm-drv.h> +#include <plat/s5p-vcm.h> +#include <linux/dma-mapping.h> + +#define UMP_REF_DRV_UK_VCM_DEV_G2D 12 + +typedef struct ump_vcm { + struct vcm *vcm; + struct vcm_res *vcm_res; + unsigned int dev_id; +} ump_vcm; + +typedef struct vcm_allocator { + struct semaphore mutex; + u32 num_vcm_blocks; +} vcm_allocator; + +static void ump_vcm_free(void* ctx, ump_dd_mem * descriptor); +static int ump_vcm_allocate(void* ctx, ump_dd_mem * descriptor); +static void *vcm_res_get(ump_dd_mem *mem, void* args); +static void vcm_attr_set(ump_dd_mem *mem, void* args); +static int vcm_mem_allocator(vcm_allocator *info, ump_dd_mem *descriptor); +static void vcm_memory_backend_destroy(ump_memory_backend * backend); + + +/* + * Create VCM memory backend + */ +ump_memory_backend * ump_vcm_memory_backend_create(const int max_allocation) +{ + ump_memory_backend * backend; + vcm_allocator * info; + + info = kmalloc(sizeof(vcm_allocator), GFP_KERNEL); + if (NULL == info) + { + return NULL; + } + + info->num_vcm_blocks = 0; + + + sema_init(&info->mutex, 1); + + backend = kmalloc(sizeof(ump_memory_backend), GFP_KERNEL); + if (NULL == backend) + { + kfree(info); + return NULL; + } + + backend->ctx = info; + backend->allocate = ump_vcm_allocate; + backend->release = ump_vcm_free; + backend->shutdown = vcm_memory_backend_destroy; + backend->pre_allocate_physical_check = NULL; + backend->adjust_to_mali_phys = NULL; + + backend->get = vcm_res_get; + backend->set = vcm_attr_set; + + + return backend; +} + +/* + * Destroy specified VCM memory backend + */ +static void vcm_memory_backend_destroy(ump_memory_backend * backend) +{ + vcm_allocator * info = (vcm_allocator*)backend->ctx; +#if 0 + DBG_MSG_IF(1, 0 != info->num_pages_allocated, ("%d pages still in use during shutdown\n", info->num_pages_allocated)); +#endif + kfree(info); + kfree(backend); +} + +/* + * Allocate UMP memory + */ +static int ump_vcm_allocate(void *ctx, ump_dd_mem * descriptor) +{ + int ret; /* success */ + vcm_allocator *info; + struct ump_vcm *ump_vcm; + + BUG_ON(!descriptor); + BUG_ON(!ctx); + + info = (vcm_allocator*)ctx; + + ump_vcm = kmalloc(sizeof(struct ump_vcm), GFP_KERNEL); + if (NULL == ump_vcm) + { + return 0; + } + + ump_vcm->dev_id = (int)descriptor->backend_info & ~UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE; + + if(ump_vcm->dev_id == UMP_REF_DRV_UK_CONSTRAINT_NONE) { /* None */ + ump_vcm->dev_id = UMP_REF_DRV_UK_VCM_DEV_G2D; /* this ID is G2D */ + } + else if(ump_vcm->dev_id == UMP_REF_DRV_UK_CONSTRAINT_PHYSICALLY_LINEAR) { /* Physical Linear */ + return 0; + } + else { /* Other VCM */ + ump_vcm->dev_id -= 2; + } + + DBG_MSG(5, ("Device ID for VCM : %d\n", ump_vcm->dev_id)); + ump_vcm->vcm = vcm_find_vcm(ump_vcm->dev_id); + + if (!ump_vcm->vcm) + { + return 0; + } + descriptor->backend_info = (void*)ump_vcm; + + if (down_interruptible(&info->mutex)) { + DBG_MSG(1, ("Failed to get mutex in ump_vcm_allocate\n")); + return 0; /* failure */ + } + + ret = vcm_mem_allocator(info, descriptor); + up(&info->mutex); + + return ret; /* success */ +} + +static int vcm_mem_allocator(vcm_allocator *info, ump_dd_mem *descriptor) +{ + unsigned long num_blocks; + int i; + struct vcm_phys *phys; + struct vcm_phys_part *part; + int size_total = 0; + struct ump_vcm *ump_vcm; + + ump_vcm = (struct ump_vcm*)descriptor->backend_info; + + ump_vcm->vcm_res = + vcm_make_binding(ump_vcm->vcm, descriptor->size_bytes, + ump_vcm->dev_id, 0); + + phys = ump_vcm->vcm_res->phys; + part = phys->parts; + num_blocks = phys->count; + + DBG_MSG(5, + ("Allocating page array. Size: %lu, VCM Reservation : 0x%x\n", + phys->count * sizeof(ump_dd_physical_block), + ump_vcm->vcm_res->start)); + + /* Now, make a copy of the block information supplied by the user */ + descriptor->block_array = + (ump_dd_physical_block *) vmalloc(sizeof(ump_dd_physical_block) * + num_blocks); + + if (NULL == descriptor->block_array) { + vfree(descriptor->block_array); + DBG_MSG(1, ("Could not allocate a mem handle for function.\n")); + return 0; /* failure */ + } + + for (i = 0; i < num_blocks; i++) { + descriptor->block_array[i].addr = part->start; + descriptor->block_array[i].size = part->size; + + dmac_unmap_area(phys_to_virt(part->start), part->size, DMA_FROM_DEVICE); + outer_inv_range(part->start, part->start + part->size); + + ++part; + size_total += descriptor->block_array[i].size; + DBG_MSG(6, + ("UMP memory created with VCM. addr 0x%x, size: 0x%x\n", + descriptor->block_array[i].addr, + descriptor->block_array[i].size)); + } + + descriptor->size_bytes = size_total; + descriptor->nr_blocks = num_blocks; + descriptor->ctx = NULL; + + info->num_vcm_blocks += num_blocks; + return 1; +} + +/* + * Free specified UMP memory + */ +static void ump_vcm_free(void *ctx, ump_dd_mem * descriptor) +{ + struct ump_vcm *ump_vcm; + vcm_allocator *info; + + BUG_ON(!descriptor); + BUG_ON(!ctx); + + ump_vcm = (struct ump_vcm*)descriptor->backend_info; + info = (vcm_allocator*)ctx; + + BUG_ON(descriptor->nr_blocks > info->num_vcm_blocks); + + if (down_interruptible(&info->mutex)) { + DBG_MSG(1, ("Failed to get mutex in ump_vcm_free\n")); + return; + } + + DBG_MSG(5, ("Releasing %lu VCM pages\n", descriptor->nr_blocks)); + + info->num_vcm_blocks -= descriptor->nr_blocks; + + up(&info->mutex); + + DBG_MSG(6, ("Freeing physical page by VCM\n")); + vcm_destroy_binding(ump_vcm->vcm_res); + ump_vcm->vcm = NULL; + ump_vcm->vcm_res = NULL; + + kfree(ump_vcm); + vfree(descriptor->block_array); +} + +static void *vcm_res_get(ump_dd_mem *mem, void *args) +{ + struct ump_vcm *ump_vcm; + enum vcm_dev_id vcm_id; + + ump_vcm = (struct ump_vcm*)mem->backend_info; + vcm_id = (enum vcm_dev_id)args; + + if (vcm_reservation_in_vcm + (vcm_find_vcm(vcm_id), ump_vcm->vcm_res) + == S5PVCM_RES_NOT_IN_VCM) + return NULL; + else + return ump_vcm->vcm_res; +} + +static void vcm_attr_set(ump_dd_mem *mem, void *args) +{ + struct ump_vcm *ump_vcm, *ump_vcmh; + + ump_vcm = (struct ump_vcm*)args; + + ump_vcmh = kmalloc(sizeof(struct ump_vcm), GFP_KERNEL); + if (NULL == ump_vcmh) + { + return; + } + + ump_vcmh->dev_id = ump_vcm->dev_id; + ump_vcmh->vcm = ump_vcm->vcm; + ump_vcmh->vcm_res = ump_vcm->vcm_res; + + mem->backend_info= (void*)ump_vcmh; + + return; +} diff --git a/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_vcm.h b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_vcm.h new file mode 100644 index 0000000..c1ead0d --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_kernel_memory_backend_vcm.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_backend_vcm.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_VCM_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_VCM_H__ + +#include "ump_kernel_memory_backend.h" + +ump_memory_backend * ump_vcm_memory_backend_create(const int max_allocation); + +#endif /* __UMP_KERNEL_MEMORY_BACKEND_VCM_H__ */ diff --git a/drivers/media/video/samsung/ump/linux/ump_memory_backend.c b/drivers/media/video/samsung/ump/linux/ump_memory_backend.c new file mode 100644 index 0000000..d067cfe --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_memory_backend.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> /* kernel module definitions */ +#include <linux/ioport.h> /* request_mem_region */ + +#include "arch/config.h" /* Configuration for current platform. The symlink for arch is set by Makefile */ + +#include "ump_osk.h" +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend_os.h" +#include "ump_kernel_memory_backend_dedicated.h" + +/* Configure which dynamic memory allocator to use */ +int ump_backend = ARCH_UMP_BACKEND_DEFAULT; +module_param(ump_backend, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_backend, "0 = dedicated memory backend (default), 1 = OS memory backend"); + +/* The base address of the memory block for the dedicated memory backend */ +unsigned int ump_memory_address = ARCH_UMP_MEMORY_ADDRESS_DEFAULT; +module_param(ump_memory_address, uint, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_memory_address, "The physical address to map for the dedicated memory backend"); + +/* The size of the memory block for the dedicated memory backend */ +unsigned int ump_memory_size = ARCH_UMP_MEMORY_SIZE_DEFAULT; +module_param(ump_memory_size, uint, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_memory_size, "The size of fixed memory to map in the dedicated memory backend"); + +ump_memory_backend* ump_memory_backend_create ( void ) +{ + ump_memory_backend * backend = NULL; + + /* Create the dynamic memory allocator backend */ + if (0 == ump_backend) + { + DBG_MSG(2, ("Using dedicated memory backend\n")); + + DBG_MSG(2, ("Requesting dedicated memory: 0x%08x, size: %u\n", ump_memory_address, ump_memory_size)); + /* Ask the OS if we can use the specified physical memory */ + if (NULL == request_mem_region(ump_memory_address, ump_memory_size, "UMP Memory")) + { + MSG_ERR(("Failed to request memory region (0x%08X - 0x%08X). Is Mali DD already loaded?\n", ump_memory_address, ump_memory_address + ump_memory_size - 1)); + return NULL; + } + backend = ump_block_allocator_create(ump_memory_address, ump_memory_size); + } + else if (1 == ump_backend) + { + DBG_MSG(2, ("Using OS memory backend, allocation limit: %d\n", ump_memory_size)); + backend = ump_os_memory_backend_create(ump_memory_size); + } +#ifdef CONFIG_UMP_VCM_ALLOC + else if (2 == ump_backend) + { + DBG_MSG(2, ("Using VCM memory backend, allocation limit: %d\n", ump_memory_size)); + backend = ump_vcm_memory_backend_create(ump_memory_size); + } +#endif + + return backend; +} + +void ump_memory_backend_destroy( void ) +{ + if (0 == ump_backend) + { + DBG_MSG(2, ("Releasing dedicated memory: 0x%08x\n", ump_memory_address)); + release_mem_region(ump_memory_address, ump_memory_size); + } +} diff --git a/drivers/media/video/samsung/ump/linux/ump_osk_atomics.c b/drivers/media/video/samsung/ump/linux/ump_osk_atomics.c new file mode 100644 index 0000000..b117d99 --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_osk_atomics.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_atomics.c + * Implementation of the OS abstraction layer for the UMP kernel device driver + */ + +#include "ump_osk.h" +#include <asm/atomic.h> + +int _ump_osk_atomic_dec_and_read( _mali_osk_atomic_t *atom ) +{ + return atomic_dec_return((atomic_t *)&atom->u.val); +} + +int _ump_osk_atomic_inc_and_read( _mali_osk_atomic_t *atom ) +{ + return atomic_inc_return((atomic_t *)&atom->u.val); +} diff --git a/drivers/media/video/samsung/ump/linux/ump_osk_low_level_mem.c b/drivers/media/video/samsung/ump/linux/ump_osk_low_level_mem.c new file mode 100644 index 0000000..8ae169a --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_osk_low_level_mem.c @@ -0,0 +1,485 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_memory.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +/* needed to detect kernel version specific code */ +#include <linux/version.h> + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" +#include <linux/module.h> /* kernel module definitions */ +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include <asm/memory.h> +#include <asm/uaccess.h> /* to verify pointers from user space */ +#include <asm/cacheflush.h> +#include <linux/dma-mapping.h> + +typedef struct ump_vma_usage_tracker +{ + atomic_t references; + ump_memory_allocation *descriptor; +} ump_vma_usage_tracker; + +static void ump_vma_open(struct vm_area_struct * vma); +static void ump_vma_close(struct vm_area_struct * vma); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf); +#else +static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address); +#endif + +static struct vm_operations_struct ump_vm_ops = +{ + .open = ump_vma_open, + .close = ump_vma_close, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + .fault = ump_cpu_page_fault_handler +#else + .nopfn = ump_cpu_page_fault_handler +#endif +}; + +/* + * Page fault for VMA region + * This should never happen since we always map in the entire virtual memory range. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf) +#else +static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + void __user * address; + address = vmf->virtual_address; +#endif + MSG_ERR(("Page-fault in UMP memory region caused by the CPU\n")); + MSG_ERR(("VMA: 0x%08lx, virtual address: 0x%08lx\n", (unsigned long)vma, address)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + return VM_FAULT_SIGBUS; +#else + return NOPFN_SIGBUS; +#endif +} + +static void ump_vma_open(struct vm_area_struct * vma) +{ + ump_vma_usage_tracker * vma_usage_tracker; + int new_val; + + vma_usage_tracker = (ump_vma_usage_tracker*)vma->vm_private_data; + BUG_ON(NULL == vma_usage_tracker); + + new_val = atomic_inc_return(&vma_usage_tracker->references); + + DBG_MSG(4, ("VMA open, VMA reference count incremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); +} + +static void ump_vma_close(struct vm_area_struct * vma) +{ + ump_vma_usage_tracker * vma_usage_tracker; + _ump_uk_unmap_mem_s args; + int new_val; + + vma_usage_tracker = (ump_vma_usage_tracker*)vma->vm_private_data; + BUG_ON(NULL == vma_usage_tracker); + + new_val = atomic_dec_return(&vma_usage_tracker->references); + + DBG_MSG(4, ("VMA close, VMA reference count decremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); + + if (0 == new_val) + { + ump_memory_allocation * descriptor; + + descriptor = vma_usage_tracker->descriptor; + + args.ctx = descriptor->ump_session; + args.cookie = descriptor->cookie; + args.mapping = descriptor->mapping; + args.size = descriptor->size; + + args._ukk_private = NULL; /** @note unused */ + + DBG_MSG(4, ("No more VMA references left, releasing UMP memory\n")); + _ump_ukk_unmap_mem( & args ); + + /* vma_usage_tracker is free()d by _ump_osk_mem_mapregion_term() */ + } +} + +_mali_osk_errcode_t _ump_osk_mem_mapregion_init( ump_memory_allocation * descriptor ) +{ + ump_vma_usage_tracker * vma_usage_tracker; + struct vm_area_struct *vma; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + vma_usage_tracker = kmalloc(sizeof(ump_vma_usage_tracker), GFP_KERNEL); + if (NULL == vma_usage_tracker) + { + DBG_MSG(1, ("Failed to allocate memory for ump_vma_usage_tracker in _mali_osk_mem_mapregion_init\n")); + return -_MALI_OSK_ERR_FAULT; + } + + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + if (NULL == vma ) + { + kfree(vma_usage_tracker); + return _MALI_OSK_ERR_FAULT; + } + + vma->vm_private_data = vma_usage_tracker; + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + + if (0==descriptor->is_cached) + { + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + } + DBG_MSG(3, ("Mapping with page_prot: 0x%x\n", vma->vm_page_prot )); + + /* Setup the functions which handle further VMA handling */ + vma->vm_ops = &ump_vm_ops; + + /* Do the va range allocation - in this case, it was done earlier, so we copy in that information */ + descriptor->mapping = (void __user*)vma->vm_start; + + atomic_set(&vma_usage_tracker->references, 1); /*this can later be increased if process is forked, see ump_vma_open() */ + vma_usage_tracker->descriptor = descriptor; + + return _MALI_OSK_ERR_OK; +} + +void _ump_osk_mem_mapregion_term( ump_memory_allocation * descriptor ) +{ + struct vm_area_struct* vma; + ump_vma_usage_tracker * vma_usage_tracker; + + if (NULL == descriptor) return; + + /* Linux does the right thing as part of munmap to remove the mapping + * All that remains is that we remove the vma_usage_tracker setup in init() */ + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + + vma_usage_tracker = vma->vm_private_data; + + /* We only get called if mem_mapregion_init succeeded */ + kfree(vma_usage_tracker); + return; +} + +_mali_osk_errcode_t _ump_osk_mem_mapregion_map( ump_memory_allocation * descriptor, u32 offset, u32 * phys_addr, unsigned long size ) +{ + struct vm_area_struct *vma; + _mali_osk_errcode_t retval; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + + if (NULL == vma ) return _MALI_OSK_ERR_FAULT; + + retval = remap_pfn_range( vma, ((u32)descriptor->mapping) + offset, (*phys_addr) >> PAGE_SHIFT, size, vma->vm_page_prot) ? _MALI_OSK_ERR_FAULT : _MALI_OSK_ERR_OK;; + + DBG_MSG(4, ("Mapping virtual to physical memory. ID: %u, vma: 0x%08lx, virtual addr:0x%08lx, physical addr: 0x%08lx, size:%lu, prot:0x%x, vm_flags:0x%x RETVAL: 0x%x\n", + ump_dd_secure_id_get(descriptor->handle), + (unsigned long)vma, + (unsigned long)(vma->vm_start + offset), + (unsigned long)*phys_addr, + size, + (unsigned int)vma->vm_page_prot, vma->vm_flags, retval)); + + return retval; +} + +static u32 _ump_osk_virt_to_phys_start(ump_dd_mem * mem, u32 start, u32 address, int *index) +{ + int i; + u32 offset = address - start; + ump_dd_physical_block *block; + u32 sum = 0; + + for (i=0; i<mem->nr_blocks; i++) { + block = &mem->block_array[i]; + sum += block->size; + if (sum > offset) { + *index = i; + DBG_MSG(3, ("_ump_osk_virt_to_phys : index : %d, virtual 0x%x, phys 0x%x\n", i, address, (u32)block->addr + offset - (sum -block->size))); + return (u32)block->addr + offset - (sum -block->size); + } + } + + return _MALI_OSK_ERR_FAULT; +} + +static u32 _ump_osk_virt_to_phys_end(ump_dd_mem * mem, u32 start, u32 address, int *index) +{ + int i; + u32 offset = address - start; + ump_dd_physical_block *block; + u32 sum = 0; + + for (i=0; i<mem->nr_blocks; i++) { + block = &mem->block_array[i]; + sum += block->size; + if (sum >= offset) { + *index = i; + DBG_MSG(3, ("_ump_osk_virt_to_phys : index : %d, virtual 0x%x, phys 0x%x\n", i, address, (u32)block->addr + offset - (sum -block->size))); + return (u32)block->addr + offset - (sum -block->size); + } + } + + return _MALI_OSK_ERR_FAULT; +} + +static void _ump_osk_msync_with_virt(ump_dd_mem * mem, ump_uk_msync_op op, u32 start, u32 address, u32 size) +{ + int start_index, end_index; + u32 start_p, end_p; + + DBG_MSG(3, ("Cache flush with user virtual address. start : 0x%x, end : 0x%x, address 0x%x, size 0x%x\n", start, start+mem->size_bytes, address, size)); + + start_p = _ump_osk_virt_to_phys_start(mem, start, address, &start_index); + end_p = _ump_osk_virt_to_phys_end(mem, start, address+size, &end_index); + + if (start_index==end_index) { + if (op == _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE) + outer_flush_range(start_p, end_p); + else + outer_clean_range(start_p, end_p); + } else { + ump_dd_physical_block *block; + int i; + + for (i=start_index; i<=end_index; i++) { + block = &mem->block_array[i]; + + if (i == start_index) { + if (op == _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE) { + outer_flush_range(start_p, block->addr+block->size); + } else { + outer_clean_range(start_p, block->addr+block->size); + } + } + else if (i == end_index) { + if (op == _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE) { + outer_flush_range(block->addr, end_p); + } else { + outer_clean_range(block->addr, end_p); + } + break; + } + else { + if (op == _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE) { + outer_flush_range(block->addr, block->addr+block->size); + } else { + outer_clean_range(block->addr, block->addr+block->size); + } + } + } + } + return; +} + +static void level1_cache_flush_all(void) +{ + DBG_MSG(4, ("UMP[xx] Flushing complete L1 cache\n")); + __cpuc_flush_kern_all(); +} + +void _ump_osk_msync( ump_dd_mem * mem, void * virt, u32 offset, u32 size, ump_uk_msync_op op, ump_session_data * session_data ) +{ + int i; + const void *start_v, *end_v; + + /* Flush L1 using virtual address, the entire range in one go. + * Only flush if user space process has a valid write mapping on given address. */ + if( (mem) && (virt!=NULL) && (access_ok(VERIFY_WRITE, virt, size)) ) + { + start_v = (void *)virt; + end_v = (void *)(start_v + size - 1); + /* There is no dmac_clean_range, so the L1 is always flushed, + * also for UMP_MSYNC_CLEAN. */ + if (size >= SZ_64K) + flush_all_cpu_caches(); + else + dmac_flush_range(start_v, end_v); + + DBG_MSG(3, ("UMP[%02u] Flushing CPU L1 Cache. Cpu address: %x-%x\n", mem->secure_id, start_v,end_v)); + } + else + { + if (session_data) + { + if (op == _UMP_UK_MSYNC_FLUSH_L1 ) + { + DBG_MSG(4, ("UMP Pending L1 cache flushes: %d\n", session_data->has_pending_level1_cache_flush)); + session_data->has_pending_level1_cache_flush = 0; + level1_cache_flush_all(); + return; + } + else + { + if (session_data->cache_operations_ongoing) + { + session_data->has_pending_level1_cache_flush++; + DBG_MSG(4, ("UMP[%02u] Defering the L1 flush. Nr pending:%d\n", mem->secure_id, session_data->has_pending_level1_cache_flush) ); + } + else + { + /* Flushing the L1 cache for each switch_user() if ump_cache_operations_control(START) is not called */ + level1_cache_flush_all(); + } + } + } + else + { + DBG_MSG(4, ("Unkown state %s %d\n", __FUNCTION__, __LINE__)); + level1_cache_flush_all(); + } + } + + if ( NULL == mem ) return; + + if ( mem->size_bytes==size) + { + DBG_MSG(3, ("UMP[%02u] Flushing CPU L2 Cache\n",mem->secure_id)); + } + else + { + DBG_MSG(3, ("UMP[%02u] Flushing CPU L2 Cache. Blocks:%u, TotalSize:%u. FlushSize:%u Offset:0x%x FirstPaddr:0x%08x\n", + mem->secure_id, mem->nr_blocks, mem->size_bytes, size, offset, mem->block_array[0].addr)); + } + + + /* Flush L2 using physical addresses, block for block. */ + if ((virt!=NULL) && (mem->size_bytes >= SZ_1M)) { + if (op == _UMP_UK_MSYNC_CLEAN) + outer_clean_all(); + else if ((op == _UMP_UK_MSYNC_INVALIDATE) || (op == _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE)) + outer_flush_all(); + return; + } + + for (i=0 ; i < mem->nr_blocks; i++) + { + u32 start_p, end_p; + ump_dd_physical_block *block; + block = &mem->block_array[i]; + + if(offset >= block->size) + { + offset -= block->size; + continue; + } + + if(offset) + { + start_p = (u32)block->addr + offset; + /* We'll zero the offset later, after using it to calculate end_p. */ + } + else + { + start_p = (u32)block->addr; + } + + if(size < block->size - offset) + { + end_p = start_p + size - 1; + size = 0; + } + else + { + if(offset) + { + end_p = start_p + (block->size - offset - 1); + size -= block->size - offset; + offset = 0; + } + else + { + end_p = start_p + block->size - 1; + size -= block->size; + } + } + + switch(op) + { + case _UMP_UK_MSYNC_CLEAN: + outer_clean_range(start_p, end_p); + break; + case _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE: + outer_flush_range(start_p, end_p); + break; + case _UMP_UK_MSYNC_INVALIDATE: + outer_inv_range(start_p, end_p); + break; + default: + break; + } + + if(0 == size) + { + /* Nothing left to flush. */ + break; + } + } + + return; +} + +void _ump_osk_mem_mapregion_get( ump_dd_mem ** mem, unsigned long vaddr) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + ump_vma_usage_tracker * vma_usage_tracker; + ump_memory_allocation *descriptor; + ump_dd_handle handle; + + DBG_MSG(3, ("_ump_osk_mem_mapregion_get: vaddr 0x%08lx\n", vaddr)); + + down_read(&mm->mmap_sem); + vma = find_vma(mm, vaddr); + up_read(&mm->mmap_sem); + if(!vma) + { + DBG_MSG(3, ("Not found VMA\n")); + *mem = NULL; + return; + } + DBG_MSG(4, ("Get vma: 0x%08lx vma->vm_start: 0x%08lx\n", (unsigned long)vma, vma->vm_start)); + + vma_usage_tracker = (struct ump_vma_usage_tracker*)vma->vm_private_data; + if(vma_usage_tracker == NULL) + { + DBG_MSG(3, ("Not found vma_usage_tracker\n")); + *mem = NULL; + return; + } + + descriptor = (struct ump_memory_allocation*)vma_usage_tracker->descriptor; + handle = (ump_dd_handle)descriptor->handle; + + DBG_MSG(3, ("Get handle: 0x%08lx\n", handle)); + *mem = (ump_dd_mem*)handle; +} + diff --git a/drivers/media/video/samsung/ump/linux/ump_osk_misc.c b/drivers/media/video/samsung/ump/linux/ump_osk_misc.c new file mode 100644 index 0000000..3be6fed --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_osk_misc.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_misc.c + * Implementation of the OS abstraction layer for the UMP kernel device driver + */ + + +#include "ump_osk.h" + +#include <linux/kernel.h> +#include "ump_kernel_linux.h" + +/* is called from ump_kernel_constructor in common code */ +_mali_osk_errcode_t _ump_osk_init( void ) +{ + if (0 != ump_kernel_device_initialize()) + { + return _MALI_OSK_ERR_FAULT; + } + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_osk_term( void ) +{ + ump_kernel_device_terminate(); + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/media/video/samsung/ump/linux/ump_ukk_ref_wrappers.c b/drivers/media/video/samsung/ump/linux/ump_ukk_ref_wrappers.c new file mode 100644 index 0000000..a6691ed --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_ukk_ref_wrappers.c @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.c + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation + */ + + +#include <asm/uaccess.h> /* user space access */ + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" + +#if defined(CONFIG_ION_EXYNOS) || defined(CONFIG_DMA_SHARED_BUFFER) +#include <linux/scatterlist.h> +#include "ump_kernel_interface_ref_drv.h" +#include "mali_osk_list.h" +#ifdef CONFIG_ION_EXYNOS +#include <linux/ion.h> +#include "../../../../../gpu/ion/ion_priv.h" +extern struct ion_device *ion_exynos; +extern struct ion_client *ion_client_ump; +#endif +#ifdef CONFIG_DMA_SHARED_BUFFER +#include <linux/dma-buf.h> +#endif +#endif + +/* + * IOCTL operation; Allocate UMP memory + */ +int ump_allocate_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_allocate_s user_interaction; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_allocate()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_allocate()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + err = _ump_ukk_allocate( &user_interaction ); + if( _MALI_OSK_ERR_OK != err ) + { + DBG_MSG(1, ("_ump_ukk_allocate() failed in ump_ioctl_allocate()\n")); + return map_errcode(err); + } + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + /* If the copy fails then we should release the memory. We can use the IOCTL release to accomplish this */ + _ump_uk_release_s release_args; + + MSG_ERR(("copy_to_user() failed in ump_ioctl_allocate()\n")); + + release_args.ctx = (void *) session_data; + release_args.secure_id = user_interaction.secure_id; + + err = _ump_ukk_release( &release_args ); + if(_MALI_OSK_ERR_OK != err) + { + MSG_ERR(("_ump_ukk_release() also failed when trying to release newly allocated memory in ump_ioctl_allocate()\n")); + } + + return -EFAULT; + } + + return 0; /* success */ +} +#ifdef CONFIG_ION_EXYNOS +/* + * IOCTL operation; Import fd to UMP memory + */ +int ump_ion_import_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_ion_import_s user_interaction; + ump_dd_handle *ump_handle; + ump_dd_physical_block * blocks; + unsigned long num_blocks; + struct ion_handle *ion_hnd; + struct scatterlist *sg; + struct scatterlist *sg_ion; + unsigned long i = 0; + + ump_session_memory_list_element * session_memory_element = NULL; + if (ion_client_ump==NULL) + ion_client_ump = ion_client_create(ion_exynos, -1, "ump"); + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_allocate()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_allocate()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + /* translate fd to secure ID*/ + ion_hnd = ion_import_fd(ion_client_ump, user_interaction.ion_fd); + sg_ion = ion_map_dma(ion_client_ump,ion_hnd); + + blocks = (ump_dd_physical_block*)_mali_osk_malloc(sizeof(ump_dd_physical_block)*1024); + + if (NULL == blocks) { + MSG_ERR(("Failed to allocate blocks in ump_ioctl_allocate()\n")); + return -ENOMEM; + } + + sg = sg_ion; + do { + blocks[i].addr = sg_phys(sg); + blocks[i].size = sg_dma_len(sg); + i++; + if (i>=1024) { + _mali_osk_free(blocks); + MSG_ERR(("ion_import fail() in ump_ioctl_allocate()\n")); + return -EFAULT; + } + sg = sg_next(sg); + } while(sg); + + num_blocks = i; + + /* Initialize the session_memory_element, and add it to the session object */ + session_memory_element = _mali_osk_calloc( 1, sizeof(ump_session_memory_list_element)); + + if (NULL == session_memory_element) + { + _mali_osk_free(blocks); + DBG_MSG(1, ("Failed to allocate ump_session_memory_list_element in ump_ioctl_allocate()\n")); + return -EFAULT; + } + + ump_handle = ump_dd_handle_create_from_phys_blocks(blocks, num_blocks); + if (UMP_DD_HANDLE_INVALID == ump_handle) + { + _mali_osk_free(session_memory_element); + _mali_osk_free(blocks); + DBG_MSG(1, ("Failed to allocate ump_session_memory_list_element in ump_ioctl_allocate()\n")); + return -EFAULT; + } + + session_memory_element->mem = (ump_dd_mem*)ump_handle; + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_list_add(&(session_memory_element->list), &(session_data->list_head_session_memory_list)); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + ion_unmap_dma(ion_client_ump,ion_hnd); + ion_free(ion_client_ump, ion_hnd); + + _mali_osk_free(blocks); + + user_interaction.secure_id = ump_dd_secure_id_get(ump_handle); + user_interaction.size = ump_dd_size_get(ump_handle); + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + /* If the copy fails then we should release the memory. We can use the IOCTL release to accomplish this */ + + MSG_ERR(("copy_to_user() failed in ump_ioctl_allocate()\n")); + + return -EFAULT; + } + return 0; /* success */ +} +#endif + +#ifdef CONFIG_DMA_SHARED_BUFFER +int ump_dmabuf_import_wrapper(u32 __user *argument, + struct ump_session_data *session_data) +{ + ump_session_memory_list_element *session = NULL; + struct ump_uk_dmabuf ump_dmabuf; + ump_dd_handle *ump_handle; + ump_dd_physical_block *blocks; + struct dma_buf_attachment *attach; + struct dma_buf *dma_buf; + struct sg_table *sgt; + struct scatterlist *sgl; + unsigned long block_size; + /* FIXME */ + struct device dev; + unsigned int i = 0, npages; + int ret; + + /* Sanity check input parameters */ + if (!argument || !session_data) { + MSG_ERR(("NULL parameter.\n")); + return -EINVAL; + } + + if (copy_from_user(&ump_dmabuf, argument, + sizeof(struct ump_uk_dmabuf))) { + MSG_ERR(("copy_from_user() failed.\n")); + return -EFAULT; + } + + dma_buf = dma_buf_get(ump_dmabuf.fd); + if (IS_ERR(dma_buf)) + return PTR_ERR(dma_buf); + + /* + * check whether dma_buf imported already exists or not. + * + * TODO + * if already imported then dma_buf_put() should be called + * and then just return dma_buf imported. + */ + + attach = dma_buf_attach(dma_buf, &dev); + if (IS_ERR(attach)) { + ret = PTR_ERR(attach); + goto err_dma_buf_put; + } + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + goto err_dma_buf_detach; + } + + npages = sgt->nents; + + /* really need? */ + ump_dmabuf.ctx = (void *)session_data; + + block_size = sizeof(ump_dd_physical_block) * npages; + + blocks = (ump_dd_physical_block *)_mali_osk_malloc(block_size); + sgl = sgt->sgl; + + while (i < npages) { + blocks[i].addr = sg_phys(sgl); + blocks[i].size = sg_dma_len(sgl); + sgl = sg_next(sgl); + i++; + } + + /* + * Initialize the session memory list element, and add it + * to the session object + */ + session = _mali_osk_calloc(1, sizeof(*session)); + if (!session) { + DBG_MSG(1, ("Failed to allocate session.\n")); + ret = -EFAULT; + goto err_free_block; + } + + ump_handle = ump_dd_handle_create_from_phys_blocks(blocks, i); + if (UMP_DD_HANDLE_INVALID == ump_handle) { + DBG_MSG(1, ("Failed to create ump handle.\n")); + ret = -EFAULT; + goto err_free_session; + } + + session->mem = (ump_dd_mem *)ump_handle; + + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_list_add(&(session->list), + &(session_data->list_head_session_memory_list)); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + _mali_osk_free(blocks); + + ump_dmabuf.ump_handle = (uint32_t)ump_handle; + ump_dmabuf.size = ump_dd_size_get(ump_handle); + + if (copy_to_user(argument, &ump_dmabuf, + sizeof(struct ump_uk_dmabuf))) { + MSG_ERR(("copy_to_user() failed.\n")); + ret = -EFAULT; + goto err_release_ump_handle; + } + + return 0; + +err_release_ump_handle: + ump_dd_reference_release(ump_handle); +err_free_session: + _mali_osk_free(session); +err_free_block: + _mali_osk_free(blocks); + dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); +err_dma_buf_detach: + dma_buf_detach(dma_buf, attach); +err_dma_buf_put: + dma_buf_put(dma_buf); + return ret; +} +#endif diff --git a/drivers/media/video/samsung/ump/linux/ump_ukk_ref_wrappers.h b/drivers/media/video/samsung/ump/linux/ump_ukk_ref_wrappers.h new file mode 100644 index 0000000..416a584 --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_ukk_ref_wrappers.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.h + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation + */ + +#ifndef __UMP_UKK_REF_WRAPPERS_H__ +#define __UMP_UKK_REF_WRAPPERS_H__ + +#include <linux/kernel.h> +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +int ump_allocate_wrapper(u32 __user * argument, struct ump_session_data * session_data); + +#ifdef CONFIG_ION_EXYNOS +int ump_ion_import_wrapper(u32 __user * argument, struct ump_session_data * session_data); +#endif + +#ifdef CONFIG_DMA_SHARED_BUFFER +int ump_dmabuf_import_wrapper(u32 __user *argument, + struct ump_session_data *session_data); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UKK_REF_WRAPPERS_H__ */ diff --git a/drivers/media/video/samsung/ump/linux/ump_ukk_wrappers.c b/drivers/media/video/samsung/ump/linux/ump_ukk_wrappers.c new file mode 100644 index 0000000..780f311 --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_ukk_wrappers.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.c + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls + */ + +#include <asm/uaccess.h> /* user space access */ + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" + +/* + * IOCTL operation; Negotiate version of IOCTL API + */ +int ump_get_api_version_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_api_version_s version_info; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_get_api_version()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&version_info, argument, sizeof(version_info))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + version_info.ctx = (void*) session_data; + err = _ump_uku_get_api_version( &version_info ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("_ump_uku_get_api_version() failed in ump_ioctl_get_api_version()\n")); + return map_errcode(err); + } + + version_info.ctx = NULL; + + /* Copy ouput data back to user space */ + if (0 != copy_to_user(argument, &version_info, sizeof(version_info))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + return 0; /* success */ +} + + +/* + * IOCTL operation; Release reference to specified UMP memory. + */ +int ump_release_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_release_s release_args; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_release()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&release_args, argument, sizeof(release_args))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + release_args.ctx = (void*) session_data; + err = _ump_ukk_release( &release_args ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("_ump_ukk_release() failed in ump_ioctl_release()\n")); + return map_errcode(err); + } + + + return 0; /* success */ +} + +/* + * IOCTL operation; Return size for specified UMP memory. + */ +int ump_size_get_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_size_get_s user_interaction; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_size_get()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + err = _ump_ukk_size_get( &user_interaction ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("_ump_ukk_size_get() failed in ump_ioctl_size_get()\n")); + return map_errcode(err); + } + + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_size_get()\n")); + return -EFAULT; + } + + return 0; /* success */ +} + +/* + * IOCTL operation; Do cache maintenance on specified UMP memory. + */ +int ump_msync_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_msync_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_msync()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_msync( &user_interaction ); + + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_msync()\n")); + return -EFAULT; + } + + return 0; /* success */ +} +int ump_cache_operations_control_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_cache_operations_control_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_cache_operations_control()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_cache_operations_control((_ump_uk_cache_operations_control_s*) &user_interaction ); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_cache_operations_control()\n")); + return -EFAULT; + } +#endif + return 0; /* success */ +} + +int ump_switch_hw_usage_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_switch_hw_usage_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_switch_hw_usage( &user_interaction ); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } +#endif + return 0; /* success */ +} + +int ump_lock_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_lock_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_lock( &user_interaction ); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } +#endif + + return 0; /* success */ +} + +int ump_unlock_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_unlock_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_unlock( &user_interaction ); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } +#endif + + return 0; /* success */ +} diff --git a/drivers/media/video/samsung/ump/linux/ump_ukk_wrappers.h b/drivers/media/video/samsung/ump/linux/ump_ukk_wrappers.h new file mode 100644 index 0000000..e87a903 --- /dev/null +++ b/drivers/media/video/samsung/ump/linux/ump_ukk_wrappers.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.h + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls + */ + +#ifndef __UMP_UKK_WRAPPERS_H__ +#define __UMP_UKK_WRAPPERS_H__ + +#include <linux/kernel.h> +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +int ump_get_api_version_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_release_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_size_get_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_msync_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_cache_operations_control_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_switch_hw_usage_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_lock_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_unlock_wrapper(u32 __user * argument, struct ump_session_data * session_data); + + + + +#ifdef __cplusplus +} +#endif + + + +#endif /* __UMP_UKK_WRAPPERS_H__ */ |