tizen 2.4 release
[kernel/linux-3.0.git] / drivers / gpu / arm / mali400 / ump / common / ump_kernel_ref_drv.c
1 /*
2  * Copyright (C) 2011-2012 ARM Limited. All rights reserved.
3  *
4  * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5  * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
6  *
7  * A copy of the licence is included with the program, and can also be obtained from Free Software
8  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
9  */
10
11 #include "mali_osk.h"
12 #include "mali_osk_list.h"
13 #include "ump_osk.h"
14 #include "ump_uk_types.h"
15
16 #include "ump_kernel_interface_ref_drv.h"
17 #include "ump_kernel_common.h"
18 #include "ump_kernel_descriptor_mapping.h"
19
20 #define UMP_MINIMUM_SIZE         4096
21 #define UMP_MINIMUM_SIZE_MASK    (~(UMP_MINIMUM_SIZE-1))
22 #define UMP_SIZE_ALIGN(x)        (((x)+UMP_MINIMUM_SIZE-1)&UMP_MINIMUM_SIZE_MASK)
23 #define UMP_ADDR_ALIGN_OFFSET(x) ((x)&(UMP_MINIMUM_SIZE-1))
24 static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor);
25
26 UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block * blocks, unsigned long num_blocks)
27 {
28         ump_dd_mem * mem;
29         unsigned long size_total = 0;
30         int map_id;
31         u32 i;
32
33         /* Go through the input blocks and verify that they are sane */
34         for (i=0; i < num_blocks; i++)
35         {
36                 unsigned long addr = blocks[i].addr;
37                 unsigned long size = blocks[i].size;
38
39                 DBG_MSG(5, ("Adding physical memory to new handle. Address: 0x%08lx, size: %lu\n", addr, size));
40                 size_total += blocks[i].size;
41
42                 if (0 != UMP_ADDR_ALIGN_OFFSET(addr))
43                 {
44                         MSG_ERR(("Trying to create UMP memory from unaligned physical address. Address: 0x%08lx\n", addr));
45                         return UMP_DD_HANDLE_INVALID;
46                 }
47
48                 if (0 != UMP_ADDR_ALIGN_OFFSET(size))
49                 {
50                         MSG_ERR(("Trying to create UMP memory with unaligned size. Size: %lu\n", size));
51                         return UMP_DD_HANDLE_INVALID;
52                 }
53         }
54
55         /* Allocate the ump_dd_mem struct for this allocation */
56         mem = _mali_osk_malloc(sizeof(*mem));
57         if (NULL == mem)
58         {
59                 DBG_MSG(1, ("Could not allocate ump_dd_mem in ump_dd_handle_create_from_phys_blocks()\n"));
60                 return UMP_DD_HANDLE_INVALID;
61         }
62
63         /* Find a secure ID for this allocation */
64         _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW);
65         map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*) mem);
66
67         if (map_id < 0)
68         {
69                 _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW);
70                 _mali_osk_free(mem);
71                 DBG_MSG(1, ("Failed to allocate secure ID in ump_dd_handle_create_from_phys_blocks()\n"));
72                 return UMP_DD_HANDLE_INVALID;
73         }
74
75         /* Now, make a copy of the block information supplied by the user */
76         mem->block_array = _mali_osk_malloc(sizeof(ump_dd_physical_block)* num_blocks);
77         if (NULL == mem->block_array)
78         {
79                 ump_descriptor_mapping_free(device.secure_id_map, map_id);
80                 _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW);
81                 _mali_osk_free(mem);
82                 DBG_MSG(1, ("Could not allocate a mem handle for function ump_dd_handle_create_from_phys_blocks().\n"));
83                 return UMP_DD_HANDLE_INVALID;
84         }
85
86         _mali_osk_memcpy(mem->block_array, blocks, sizeof(ump_dd_physical_block) * num_blocks);
87
88         /* And setup the rest of the ump_dd_mem struct */
89         _mali_osk_atomic_init(&mem->ref_count, 1);
90         mem->secure_id = (ump_secure_id)map_id;
91         mem->size_bytes = size_total;
92         mem->nr_blocks = num_blocks;
93         mem->backend_info = NULL;
94         mem->ctx = NULL;
95         mem->release_func = phys_blocks_release;
96         /* For now UMP handles created by ump_dd_handle_create_from_phys_blocks() is forced to be Uncached */
97         mem->is_cached = 0;
98         mem->hw_device = _UMP_UK_USED_BY_CPU;
99         mem->lock_usage = UMP_NOT_LOCKED;
100
101         _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW);
102         DBG_MSG(3, ("UMP memory created. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes));
103
104         return (ump_dd_handle)mem;
105 }
106
107 static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor)
108 {
109         _mali_osk_free(descriptor->block_array);
110         descriptor->block_array = NULL;
111 }
112
113 _mali_osk_errcode_t _ump_ukk_allocate( _ump_uk_allocate_s *user_interaction )
114 {
115         ump_session_data * session_data = NULL;
116         ump_dd_mem *new_allocation = NULL;
117         ump_session_memory_list_element * session_memory_element = NULL;
118         int map_id;
119
120         DEBUG_ASSERT_POINTER( user_interaction );
121         DEBUG_ASSERT_POINTER( user_interaction->ctx );
122
123         session_data = (ump_session_data *) user_interaction->ctx;
124
125         session_memory_element = _mali_osk_calloc( 1, sizeof(ump_session_memory_list_element));
126         if (NULL == session_memory_element)
127         {
128                 DBG_MSG(1, ("Failed to allocate ump_session_memory_list_element in ump_ioctl_allocate()\n"));
129                 return _MALI_OSK_ERR_NOMEM;
130         }
131
132
133         new_allocation = _mali_osk_calloc( 1, sizeof(ump_dd_mem));
134         if (NULL==new_allocation)
135         {
136                 _mali_osk_free(session_memory_element);
137                 DBG_MSG(1, ("Failed to allocate ump_dd_mem in _ump_ukk_allocate()\n"));
138                 return _MALI_OSK_ERR_NOMEM;
139         }
140
141         /* Create a secure ID for this allocation */
142         _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW);
143         map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*)new_allocation);
144
145         if (map_id < 0)
146         {
147                 _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW);
148                 _mali_osk_free(session_memory_element);
149                 _mali_osk_free(new_allocation);
150                 DBG_MSG(1, ("Failed to allocate secure ID in ump_ioctl_allocate()\n"));
151                 return - _MALI_OSK_ERR_INVALID_FUNC;
152         }
153
154         /* Initialize the part of the new_allocation that we know so for */
155         new_allocation->secure_id = (ump_secure_id)map_id;
156         _mali_osk_atomic_init(&new_allocation->ref_count,1);
157         if ( 0==(UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE & user_interaction->constraints) )
158                  new_allocation->is_cached = 0;
159         else new_allocation->is_cached = 1;
160
161         /* 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 */
162         if (0 == user_interaction->size)
163         {
164                 user_interaction->size = 1; /* emulate by actually allocating the minimum block size */
165         }
166
167         new_allocation->size_bytes = UMP_SIZE_ALIGN(user_interaction->size); /* Page align the size */
168         new_allocation->lock_usage = UMP_NOT_LOCKED;
169
170         /* Now, ask the active memory backend to do the actual memory allocation */
171         if (!device.backend->allocate( device.backend->ctx, new_allocation ) )
172         {
173                 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));
174                 ump_descriptor_mapping_free(device.secure_id_map, map_id);
175                 _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW);
176                 _mali_osk_free(new_allocation);
177                 _mali_osk_free(session_memory_element);
178                 return _MALI_OSK_ERR_INVALID_FUNC;
179         }
180         new_allocation->hw_device = _UMP_UK_USED_BY_CPU;
181         new_allocation->ctx = device.backend->ctx;
182         new_allocation->release_func = device.backend->release;
183
184         _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW);
185
186         /* Initialize the session_memory_element, and add it to the session object */
187         session_memory_element->mem = new_allocation;
188         _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW);
189         _mali_osk_list_add(&(session_memory_element->list), &(session_data->list_head_session_memory_list));
190         _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW);
191
192         user_interaction->secure_id = new_allocation->secure_id;
193         user_interaction->size = new_allocation->size_bytes;
194         DBG_MSG(3, ("UMP memory allocated. ID: %u, size: %lu\n", new_allocation->secure_id, new_allocation->size_bytes));
195
196         return _MALI_OSK_ERR_OK;
197 }