upload tizen1.0 source
[kernel/linux-2.6.36.git] / drivers / media / video / samsung / ump / linux / ump_kernel_memory_backend_vcm.c
1 /*
2  * Copyright (C) 2010 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 /* create by boojin.kim@samsung.com */
12 /* needed to detect kernel version specific code */
13 #include <linux/version.h>
14
15 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
16 #include <linux/semaphore.h>
17 #else /* pre 2.6.26 the file was in the arch specific location */
18 #include <asm/semaphore.h>
19 #endif
20
21 #include <linux/mm.h>
22 #include <linux/slab.h>
23 #include <asm/atomic.h>
24 #include <linux/vmalloc.h>
25 #include <asm/cacheflush.h>
26 #include "ump_kernel_common.h"
27 #include "ump_kernel_memory_backend.h"
28 #include "ump_kernel_interface_ref_drv.h"
29 #include "ump_kernel_memory_backend_vcm.h"
30 #include "../common/ump_uk_types.h"
31 #include <linux/vcm-drv.h>
32 #include <plat/s5p-vcm.h>
33 #include <linux/dma-mapping.h>
34
35 #define UMP_REF_DRV_UK_VCM_DEV_G2D 10
36
37 typedef struct ump_vcm {
38         struct vcm *vcm;
39         struct vcm_res  *vcm_res;
40         unsigned int dev_id;
41 } ump_vcm;
42
43 typedef struct vcm_allocator {
44         struct semaphore mutex;
45         u32 num_vcm_blocks;
46 } vcm_allocator;
47
48 static void ump_vcm_free(void* ctx, ump_dd_mem * descriptor);
49 static int ump_vcm_allocate(void* ctx, ump_dd_mem * descriptor);
50 static void *vcm_res_get(ump_dd_mem *mem, void* args);
51 static void vcm_attr_set(ump_dd_mem *mem, void* args);
52 static int vcm_mem_allocator(vcm_allocator *info, ump_dd_mem *descriptor);
53 static void vcm_memory_backend_destroy(ump_memory_backend * backend);
54
55
56 /*
57  * Create VCM memory backend
58  */
59 ump_memory_backend * ump_vcm_memory_backend_create(const int max_allocation)
60 {
61         ump_memory_backend * backend;
62         vcm_allocator * info;
63
64         info = kmalloc(sizeof(vcm_allocator), GFP_KERNEL);
65         if (NULL == info)
66         {
67                 return NULL;
68         }
69         
70         info->num_vcm_blocks = 0;
71         
72
73         sema_init(&info->mutex, 1);
74
75         backend = kmalloc(sizeof(ump_memory_backend), GFP_KERNEL);
76         if (NULL == backend)
77         {
78                 kfree(info);
79                 return NULL;
80         }
81
82         backend->ctx = info;
83         backend->allocate = ump_vcm_allocate;
84         backend->release = ump_vcm_free;
85         backend->shutdown = vcm_memory_backend_destroy;
86         backend->pre_allocate_physical_check = NULL;
87         backend->adjust_to_mali_phys = NULL;
88         
89         backend->get = vcm_res_get;
90         backend->set = vcm_attr_set;
91
92
93         return backend;
94 }
95
96 /*
97  * Destroy specified VCM memory backend
98  */
99 static void vcm_memory_backend_destroy(ump_memory_backend * backend)
100 {
101         vcm_allocator * info = (vcm_allocator*)backend->ctx;
102 #if 0
103         DBG_MSG_IF(1, 0 != info->num_pages_allocated, ("%d pages still in use during shutdown\n", info->num_pages_allocated));
104 #endif
105         kfree(info);
106         kfree(backend);
107 }
108
109 /*
110  * Allocate UMP memory
111  */
112 static int ump_vcm_allocate(void *ctx, ump_dd_mem * descriptor)
113 {
114         int ret;                /* success */
115         vcm_allocator *info;
116         struct ump_vcm *ump_vcm;
117
118         BUG_ON(!descriptor);
119         BUG_ON(!ctx);
120
121         info = (vcm_allocator*)ctx;
122
123         ump_vcm = kmalloc(sizeof(struct ump_vcm), GFP_KERNEL);
124         if (NULL == ump_vcm)
125         {
126                 return 0;
127         }
128
129         ump_vcm->dev_id = (int)descriptor->backend_info & ~UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE;
130
131         if(ump_vcm->dev_id == UMP_REF_DRV_UK_CONSTRAINT_NONE) {         /* None */
132                 ump_vcm->dev_id = UMP_REF_DRV_UK_VCM_DEV_G2D;   /* this ID is G2D */
133         }
134         else if(ump_vcm->dev_id == UMP_REF_DRV_UK_CONSTRAINT_PHYSICALLY_LINEAR) { /* Physical Linear */
135                 return 0;
136         }
137         else {                          /* Other VCM */
138                 ump_vcm->dev_id -= 2;
139         }
140         
141         DBG_MSG(5, ("Device ID for VCM : %d\n", ump_vcm->dev_id));
142         ump_vcm->vcm = vcm_find_vcm(ump_vcm->dev_id);
143
144         if (!ump_vcm->vcm)
145         {
146                 return 0;
147         }
148         descriptor->backend_info = (void*)ump_vcm;
149         
150         if (down_interruptible(&info->mutex)) {
151                 DBG_MSG(1, ("Failed to get mutex in ump_vcm_allocate\n"));
152                 return 0;       /* failure */
153         }
154
155         ret = vcm_mem_allocator(info, descriptor);
156         up(&info->mutex);
157
158         return ret;             /* success */
159 }
160
161 static int vcm_mem_allocator(vcm_allocator *info, ump_dd_mem *descriptor)
162 {
163         unsigned long num_blocks;
164         int i;
165         struct vcm_phys *phys;
166         struct vcm_phys_part *part;
167         int size_total = 0;
168         struct ump_vcm *ump_vcm;
169
170         ump_vcm = (struct ump_vcm*)descriptor->backend_info;
171         
172         ump_vcm->vcm_res =
173             vcm_make_binding(ump_vcm->vcm, descriptor->size_bytes,
174             ump_vcm->dev_id, 0);
175
176         phys = ump_vcm->vcm_res->phys;
177         part = phys->parts;
178         num_blocks = phys->count;
179
180         DBG_MSG(5,
181                 ("Allocating page array. Size: %lu, VCM Reservation : 0x%x\n",
182                  phys->count * sizeof(ump_dd_physical_block),
183                  ump_vcm->vcm_res->start));
184
185         /* Now, make a copy of the block information supplied by the user */
186         descriptor->block_array =
187             (ump_dd_physical_block *) vmalloc(sizeof(ump_dd_physical_block) *
188                                               num_blocks);
189
190         if (NULL == descriptor->block_array) {
191                 vfree(descriptor->block_array);
192                 DBG_MSG(1, ("Could not allocate a mem handle for function.\n"));
193                 return 0; /* failure */
194         }
195
196         for (i = 0; i < num_blocks; i++) {
197                 descriptor->block_array[i].addr = part->start;
198                 descriptor->block_array[i].size = part->size;
199
200                 dmac_unmap_area(phys_to_virt(part->start), part->size, DMA_FROM_DEVICE);
201                 outer_inv_range(part->start, part->start + part->size);
202
203                 ++part;
204                 size_total += descriptor->block_array[i].size;
205                 DBG_MSG(6,
206                         ("UMP memory created with VCM. addr 0x%x, size: 0x%x\n",
207                          descriptor->block_array[i].addr,
208                          descriptor->block_array[i].size));
209         }
210
211         descriptor->size_bytes = size_total;
212         descriptor->nr_blocks = num_blocks;
213         descriptor->ctx = NULL;
214
215         info->num_vcm_blocks += num_blocks;
216         return 1;
217 }
218
219 /*
220  * Free specified UMP memory
221  */
222 static void ump_vcm_free(void *ctx, ump_dd_mem * descriptor)
223 {
224         struct ump_vcm *ump_vcm;
225         vcm_allocator *info;
226
227         BUG_ON(!descriptor);
228         BUG_ON(!ctx);
229
230         ump_vcm = (struct ump_vcm*)descriptor->backend_info;
231         info = (vcm_allocator*)ctx;
232
233         BUG_ON(descriptor->nr_blocks > info->num_vcm_blocks);
234
235         if (down_interruptible(&info->mutex)) {
236                 DBG_MSG(1, ("Failed to get mutex in ump_vcm_free\n"));
237                 return;
238         }
239
240         DBG_MSG(5, ("Releasing %lu VCM pages\n", descriptor->nr_blocks));
241
242         info->num_vcm_blocks -= descriptor->nr_blocks;
243
244         up(&info->mutex);
245
246         DBG_MSG(6, ("Freeing physical page by VCM\n"));
247         vcm_destroy_binding(ump_vcm->vcm_res);
248         ump_vcm->vcm = NULL;
249         ump_vcm->vcm_res = NULL;
250
251         kfree(ump_vcm);
252         vfree(descriptor->block_array);
253 }
254
255 static void *vcm_res_get(ump_dd_mem *mem, void *args)
256 {
257         struct ump_vcm *ump_vcm;
258         enum vcm_dev_id vcm_id;
259
260         ump_vcm = (struct ump_vcm*)mem->backend_info;
261         vcm_id = (enum vcm_dev_id)args;
262
263         if (vcm_reservation_in_vcm
264                 (vcm_find_vcm(vcm_id), ump_vcm->vcm_res)
265                 == S5PVCM_RES_NOT_IN_VCM)
266                 return NULL;
267         else
268                 return ump_vcm->vcm_res;
269 }
270
271 static void vcm_attr_set(ump_dd_mem *mem, void *args)
272 {
273         struct ump_vcm *ump_vcm, *ump_vcmh;
274
275         ump_vcm = (struct ump_vcm*)args;
276
277         ump_vcmh = kmalloc(sizeof(struct ump_vcm), GFP_KERNEL);
278         if (NULL == ump_vcmh)
279         {
280                 return;
281         }
282
283         ump_vcmh->dev_id = ump_vcm->dev_id;
284         ump_vcmh->vcm = ump_vcm->vcm;
285         ump_vcmh->vcm_res = ump_vcm->vcm_res;
286
287         mem->backend_info= (void*)ump_vcmh;
288
289         return;
290 }