staging: ion: rockchip: support cma-heap
[firefly-linux-kernel-4.4.55.git] / drivers / staging / android / ion / rockchip / rockchip_ion.c
1 /*
2  * drivers/staging/android/ion/rockchip/rockchip_ion.c
3  *
4  * Copyright (C) 2014 ROCKCHIP, Inc.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 #include <linux/module.h>
18 #include <linux/platform_device.h>
19 #include <linux/slab.h>
20 #include <linux/uaccess.h>
21 #include <linux/compat.h>
22 #include <linux/dma-buf.h>
23 #include <linux/dma-contiguous.h>
24 #include <linux/memblock.h>
25 #include <linux/of_fdt.h>
26 #include <linux/of_gpio.h>
27 #include <linux/rockchip_ion.h>
28
29 #include "../ion_priv.h"
30
31 struct ion_device *rockchip_ion_dev;
32 static struct ion_heap **heaps;
33
34 struct ion_heap_desc {
35         unsigned int id;
36         enum ion_heap_type type;
37         const char *name;
38 };
39
40 static struct ion_heap_desc ion_heap_meta[] = {
41         {
42                 .id     = ION_HEAP_TYPE_SYSTEM,
43                 .type   = ION_HEAP_TYPE_SYSTEM,
44                 .name   = "system-heap",
45         }, {
46                 .id     = ION_HEAP_TYPE_CARVEOUT,
47                 .type   = ION_HEAP_TYPE_CARVEOUT,
48                 .name   = "carveout-heap",
49         }, {
50                 .id     = ION_HEAP_TYPE_DMA,
51                 .type   = ION_HEAP_TYPE_DMA,
52                 .name   = "cma-heap",
53         },
54 };
55
56 #ifdef CONFIG_COMPAT
57 struct compat_ion_phys_data {
58         compat_int_t handle;
59         compat_ulong_t phys;
60         compat_ulong_t size;
61 };
62
63 #define COMPAT_ION_IOC_GET_PHYS _IOWR(ION_IOC_ROCKCHIP_MAGIC, 0, \
64                                 struct compat_ion_phys_data)
65
66 static int compat_get_ion_phys_data(
67                 struct compat_ion_phys_data __user *data32,
68                 struct ion_phys_data __user *data)
69 {
70         compat_ulong_t l;
71         compat_int_t i;
72         int err;
73
74         err = get_user(i, &data32->handle);
75         err |= put_user(i, &data->handle);
76         err |= get_user(l, &data32->phys);
77         err |= put_user(l, &data->phys);
78         err |= get_user(l, &data32->size);
79         err |= put_user(l, &data->size);
80
81         return err;
82 };
83
84 static int compat_put_ion_phys_data(
85                 struct compat_ion_phys_data __user *data32,
86                 struct ion_phys_data __user *data)
87 {
88         compat_ulong_t l;
89         compat_int_t i;
90         int err;
91
92         err = get_user(i, &data->handle);
93         err |= put_user(i, &data32->handle);
94         err |= get_user(l, &data->phys);
95         err |= put_user(l, &data32->phys);
96         err |= get_user(l, &data->size);
97         err |= put_user(l, &data32->size);
98
99         return err;
100 };
101 #endif
102
103 static int rk_ion_get_phys(struct ion_client *client,
104                            unsigned long arg)
105 {
106         struct ion_phys_data data;
107         struct ion_handle *handle;
108         int ret;
109
110         if (copy_from_user(&data, (void __user *)arg,
111                            sizeof(struct ion_phys_data)))
112                 return -EFAULT;
113
114         handle = ion_handle_get_by_id(client, data.handle);
115         if (IS_ERR(handle))
116                 return PTR_ERR(handle);
117
118         ret = ion_phys(client, handle, &data.phys,
119                        (size_t *)&data.size);
120         ion_handle_put(handle);
121         if (ret < 0)
122                 return ret;
123         if (copy_to_user((void __user *)arg, &data,
124                          sizeof(struct ion_phys_data)))
125                 return -EFAULT;
126
127         return 0;
128 }
129
130 #ifdef CONFIG_COMPAT
131 static int compat_rk_ion_get_phys(struct ion_client *client,
132                                   unsigned long arg)
133 {
134         struct compat_ion_phys_data __user *data32;
135         struct ion_phys_data __user *data;
136         int err;
137         long ret;
138
139         data32 = compat_ptr(arg);
140         data = compat_alloc_user_space(sizeof(*data));
141         if (!data)
142                 return -EFAULT;
143
144         err = compat_get_ion_phys_data(data32, data);
145         if (err)
146                 return err;
147
148         ret = rk_ion_get_phys(client, (unsigned long)data);
149         err = compat_put_ion_phys_data(data32, data);
150
151         return ret ? ret : err;
152 }
153 #endif
154
155 static long rk_custom_ioctl(struct ion_client *client,
156                             unsigned int cmd,
157                             unsigned long arg)
158 {
159         int ret = 0;
160
161         switch (cmd) {
162 #ifdef CONFIG_COMPAT
163         case COMPAT_ION_IOC_GET_PHYS:
164                 ret = compat_rk_ion_get_phys(client, arg);
165                 break;
166 #endif
167         case ION_IOC_GET_PHYS:
168                 ret = rk_ion_get_phys(client, arg);
169                 break;
170         default:
171                 return -ENOTTY;
172         }
173
174         return ret;
175 }
176
177 /* Return result of step for heap array. */
178 static int rk_ion_of_heap(struct ion_platform_heap *myheap,
179                           struct device_node *node)
180 {
181         unsigned int reg[2] = {0,};
182         int itype;
183
184         for (itype = 0; itype < ARRAY_SIZE(ion_heap_meta); itype++) {
185                 if (strcmp(ion_heap_meta[itype].name, node->name))
186                         continue;
187
188                 myheap->name = node->name;
189                 myheap->align = SZ_1M;
190                 myheap->id = ion_heap_meta[itype].id;
191                 if (!strcmp("cma-heap", node->name)) {
192                         myheap->type = ION_HEAP_TYPE_DMA;
193                         if (!of_property_read_u32_array(node, "reg", reg, 2)) {
194                                 myheap->base = reg[0];
195                                 myheap->size = reg[1];
196                                 return 1;
197                         }
198                 }
199
200                 if (!strcmp("system-heap", node->name)) {
201                         myheap->type = ION_HEAP_TYPE_SYSTEM;
202                         return 1;
203                 }
204         }
205
206         return 0;
207 }
208
209 static struct ion_platform_data *rk_ion_of(struct device_node *node)
210 {
211         struct ion_platform_data *pdata;
212         int iheap = 0;
213         struct device_node *child;
214         struct ion_platform_heap *myheap;
215
216         pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
217         if (!pdata)
218                 return NULL;
219
220         pdata->nr = of_get_child_count(node);
221 again:
222         pdata->heaps = kcalloc(pdata->nr, sizeof(*myheap), GFP_KERNEL);
223         for_each_child_of_node(node, child) {
224                 iheap += rk_ion_of_heap(&pdata->heaps[iheap], child);
225         }
226
227         if (pdata->nr != iheap) {
228                 pdata->nr = iheap;
229                 iheap = 0;
230                 kfree(pdata->heaps);
231                 pr_err("%s: mismatch, repeating\n", __func__);
232                 goto again;
233         }
234
235         return pdata;
236 }
237
238 static int rk_ion_probe(struct platform_device *pdev)
239 {
240         int err;
241         int i;
242         struct ion_platform_data *pdata = pdev->dev.platform_data;
243         struct ion_device *idev;
244
245         if (!pdata) {
246                 pdata = rk_ion_of(pdev->dev.of_node);
247                 pdev->dev.platform_data = pdata;
248         }
249
250         heaps = kcalloc(pdata->nr, sizeof(*heaps), GFP_KERNEL);
251
252         idev = ion_device_create(rk_custom_ioctl);
253         if (IS_ERR_OR_NULL(idev)) {
254                 kfree(heaps);
255                 return PTR_ERR(idev);
256         }
257
258         rockchip_ion_dev = idev;
259
260         /* create the heaps as specified in the board file */
261         for (i = 0; i < pdata->nr; i++) {
262                 struct ion_platform_heap *heap_data = &pdata->heaps[i];
263
264                 heap_data->priv = &pdev->dev;
265                 heaps[i] = ion_heap_create(heap_data);
266                 if (IS_ERR_OR_NULL(heaps[i])) {
267                         err = PTR_ERR(heaps[i]);
268                         goto err;
269                 }
270                 pr_info("rockchip ion: success to create - %s\n",
271                         heaps[i]->name);
272                 ion_device_add_heap(idev, heaps[i]);
273         }
274         platform_set_drvdata(pdev, idev);
275
276         return 0;
277 err:
278         for (i = 0; i < pdata->nr; i++) {
279                 if (heaps[i])
280                         ion_heap_destroy(heaps[i]);
281         }
282
283         kfree(heaps);
284         return err;
285 }
286
287 static int rk_ion_remove(struct platform_device *pdev)
288 {
289         struct ion_platform_data *pdata = pdev->dev.platform_data;
290         struct ion_device *idev = platform_get_drvdata(pdev);
291         int i;
292
293         ion_device_destroy(idev);
294         for (i = 0; i < pdata->nr; i++)
295                 ion_heap_destroy(heaps[i]);
296
297         kfree(heaps);
298         return 0;
299 }
300
301 struct ion_client *rockchip_ion_client_create(const char *name)
302 {
303         if (!rockchip_ion_dev) {
304                 pr_err("rockchip ion idev is NULL\n");
305                 return NULL;
306         }
307
308         return ion_client_create(rockchip_ion_dev, name);
309 }
310 EXPORT_SYMBOL_GPL(rockchip_ion_client_create);
311
312 static const struct of_device_id rk_ion_match[] = {
313         { .compatible = "rockchip,ion", },
314         {}
315 };
316
317 static struct platform_driver ion_driver = {
318         .probe = rk_ion_probe,
319         .remove = rk_ion_remove,
320         .driver = {
321                 .name = "ion-rk",
322                 .owner = THIS_MODULE,
323                 .of_match_table = of_match_ptr(rk_ion_match),
324         },
325 };
326
327 static int __init rk_ion_init(void)
328 {
329         return platform_driver_register(&ion_driver);
330 }
331
332 static void __exit rk_ion_exit(void)
333 {
334         platform_driver_unregister(&ion_driver);
335 }
336
337 subsys_initcall(rk_ion_init);
338 module_exit(rk_ion_exit);
339
340 MODULE_AUTHOR("Meiyou.chen <cmy@rock-chips.com>");
341 MODULE_DESCRIPTION("ROCKCHIP Ion driver");
342 MODULE_LICENSE("GPL v2");
343 MODULE_DEVICE_TABLE(of, rk_ion_match);