/*
 * 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 __MALI_KERNEL_MEMORY_ENGINE_H__
#define  __MALI_KERNEL_MEMORY_ENGINE_H__

typedef void * mali_allocation_engine;

typedef enum { MALI_MEM_ALLOC_FINISHED, MALI_MEM_ALLOC_PARTIAL, MALI_MEM_ALLOC_NONE, MALI_MEM_ALLOC_INTERNAL_FAILURE } mali_physical_memory_allocation_result;

typedef struct mali_physical_memory_allocation
{
	void (*release)(void * ctx, void * handle); /**< Function to call on to release the physical memory */
	void * ctx;
	void * handle;
	struct mali_physical_memory_allocation * next;
} mali_physical_memory_allocation;

struct mali_page_table_block;

typedef struct mali_page_table_block
{
	void (*release)(struct mali_page_table_block *page_table_block);
	void * ctx;
	void * handle;
	u32 size; /**< In bytes, should be a multiple of MALI_MMU_PAGE_SIZE to avoid internal fragementation */
	u32 phys_base; /**< Mali physical address */
	mali_io_address mapping;
} mali_page_table_block;


/** @addtogroup _mali_osk_low_level_memory
 * @{ */

typedef enum
{
	MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE = 0x1,
	MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE     = 0x2,
} mali_memory_allocation_flag;

/**
 * Supplying this 'magic' physical address requests that the OS allocate the
 * physical address at page commit time, rather than committing a specific page
 */
#define MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC ((u32)(-1))

typedef struct mali_memory_allocation
{
	/* Information about the allocation */
	void * mapping; /**< CPU virtual address where the memory is mapped at */
	u32 mali_address; /**< The Mali seen address of the memory allocation */
	u32 size; /**< Size of the allocation */
	u32 permission; /**< Permission settings */
	mali_memory_allocation_flag flags;

	_mali_osk_lock_t * lock;

	/* Manager specific information pointers */
	void * mali_addr_mapping_info; /**< Mali address allocation specific info */
	void * process_addr_mapping_info; /**< Mapping manager specific info */

	mali_physical_memory_allocation physical_allocation;

	_mali_osk_list_t list; /**< List for linking together memory allocations into the session's memory head */
} mali_memory_allocation;
/** @} */ /* end group _mali_osk_low_level_memory */


typedef struct mali_physical_memory_allocator
{
	mali_physical_memory_allocation_result (*allocate)(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info);
	mali_physical_memory_allocation_result (*allocate_page_table_block)(void * ctx, mali_page_table_block * block); /* MALI_MEM_ALLOC_PARTIAL not allowed */
	void (*destroy)(struct mali_physical_memory_allocator * allocator);
	u32 (*stat)(struct mali_physical_memory_allocator * allocator);
	void * ctx;
	const char * name; /**< Descriptive name for use in mali_allocation_engine_report_allocators, or NULL */
	u32 alloc_order; /**< Order in which the allocations should happen */
	struct mali_physical_memory_allocator * next;
} mali_physical_memory_allocator;

typedef struct mali_kernel_mem_address_manager
{
	_mali_osk_errcode_t (*allocate)(mali_memory_allocation *); /**< Function to call to reserve an address */
	void (*release)(mali_memory_allocation *); /**< Function to call to free the address allocated */

	 /**
	  * Function called for each physical sub allocation.
	  * Called for each physical block allocated by the physical memory manager.
	  * @param[in] descriptor The memory descriptor in question
	  * @param[in] off Offset from the start of range
	  * @param[in,out] phys_addr A pointer to the physical address of the start of the
	  * physical block. When *phys_addr == MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC
	  * is used, this requests the function to allocate the physical page
	  * itself, and return it through the pointer provided.
	  * @param[in] size Length in bytes of the physical block
	  * @return _MALI_OSK_ERR_OK on success.
	  * A value of type _mali_osk_errcode_t other than _MALI_OSK_ERR_OK indicates failure.
	  * Specifically, _MALI_OSK_ERR_UNSUPPORTED indicates that the function
	  * does not support allocating physical pages itself.
	  */
	 _mali_osk_errcode_t (*map_physical)(mali_memory_allocation * descriptor, u32 offset, u32 *phys_addr, u32 size);

	 /**
	  * Function called to remove a physical sub allocation.
	  * Called on error paths where one of the address managers fails.
	  *
	  * @note this is optional. For address managers where this is not
	  * implemented, the value of this member is NULL. The memory engine
	  * currently does not require the mali address manager to be able to
	  * unmap individual pages, but the process address manager must have this
	  * capability.
	  *
	  * @param[in] descriptor The memory descriptor in question
	  * @param[in] off Offset from the start of range
	  * @param[in] size Length in bytes of the physical block
	  * @param[in] flags flags to use on a per-page basis. For OS-allocated
	  * physical pages, this must include _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR.
	  * @return _MALI_OSK_ERR_OK on success.
	  * A value of type _mali_osk_errcode_t other than _MALI_OSK_ERR_OK indicates failure.
	  */
	void (*unmap_physical)(mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t flags);

} mali_kernel_mem_address_manager;

mali_allocation_engine mali_allocation_engine_create(mali_kernel_mem_address_manager * mali_address_manager, mali_kernel_mem_address_manager * process_address_manager);

void mali_allocation_engine_destroy(mali_allocation_engine engine);

int mali_allocation_engine_allocate_memory(mali_allocation_engine engine, mali_memory_allocation * descriptor, mali_physical_memory_allocator * physical_provider, _mali_osk_list_t *tracking_list );
void mali_allocation_engine_release_memory(mali_allocation_engine engine, mali_memory_allocation * descriptor);

int mali_allocation_engine_map_physical(mali_allocation_engine engine, mali_memory_allocation * descriptor, u32 offset, u32 phys, u32 cpu_usage_adjust, u32 size);
void mali_allocation_engine_unmap_physical(mali_allocation_engine engine, mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t unmap_flags);

int mali_allocation_engine_allocate_page_tables(mali_allocation_engine, mali_page_table_block * descriptor, mali_physical_memory_allocator * physical_provider);

void mali_allocation_engine_report_allocators(mali_physical_memory_allocator * physical_provider);

u32 mali_allocation_engine_memory_usage(mali_physical_memory_allocator *allocator);

#endif /* __MALI_KERNEL_MEMORY_ENGINE_H__ */
