ARM64: DTS: Add rk3399-firefly uart4 device, node as /dev/ttyS1
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / vcodec / vcodec_iommu_ion.c
1 /*
2  * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
3  * author: Jung Zhao jung.zhao@rock-chips.com
4  *         Randy Li, randy.li@rock-chips.com
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/rockchip_ion.h>
18 #include <linux/rockchip-iovmm.h>
19 #include <linux/slab.h>
20 #include <linux/pm_runtime.h>
21 #include <linux/memblock.h>
22 #include <linux/module.h>
23 #include <linux/of_address.h>
24 #include <linux/of_graph.h>
25 #include <linux/component.h>
26 #include <linux/fence.h>
27 #include <linux/console.h>
28 #include <linux/kref.h>
29 #include <linux/fdtable.h>
30
31 #include "vcodec_iommu_ops.h"
32
33 struct vcodec_ion_buffer {
34         struct list_head list;
35         struct ion_handle *handle;
36         int index;
37 };
38
39 struct vcodec_iommu_ion_info {
40         struct ion_client *ion_client;
41         bool attached;
42 };
43
44 static struct vcodec_ion_buffer *
45 vcodec_ion_get_buffer_no_lock(struct vcodec_iommu_session_info *session_info,
46                               int idx)
47 {
48         struct vcodec_ion_buffer *ion_buffer = NULL, *n;
49
50         list_for_each_entry_safe(ion_buffer, n,
51                                  &session_info->buffer_list, list) {
52                 if (ion_buffer->index == idx)
53                         return ion_buffer;
54         }
55
56         return NULL;
57 }
58
59 static void
60 vcodec_ion_clear_session(struct vcodec_iommu_session_info *session_info)
61 {
62         /* do nothing */
63 }
64
65 static int vcodec_ion_attach(struct vcodec_iommu_info *iommu_info)
66 {
67         struct vcodec_iommu_ion_info *ion_info = iommu_info->private;
68         int ret;
69
70         mutex_lock(&iommu_info->iommu_mutex);
71
72         if (ion_info->attached) {
73                 mutex_unlock(&iommu_info->iommu_mutex);
74                 return 0;
75         }
76
77         rockchip_iovmm_activate(iommu_info->dev);
78
79         ion_info->attached = true;
80
81         mutex_unlock(&iommu_info->iommu_mutex);
82
83         return ret;
84 }
85
86 static void vcodec_ion_detach(struct vcodec_iommu_info *iommu_info)
87 {
88         struct vcodec_iommu_ion_info *ion_info = iommu_info->private;
89
90         mutex_lock(&iommu_info->iommu_mutex);
91
92         if (!ion_info->attached) {
93                 mutex_unlock(&iommu_info->iommu_mutex);
94                 return;
95         }
96
97         rockchip_iovmm_deactivate(iommu_info->dev);
98         ion_info->attached = false;
99
100         mutex_unlock(&iommu_info->iommu_mutex);
101 }
102
103 static int vcodec_ion_destroy(struct vcodec_iommu_info *iommu_info)
104 {
105         struct vcodec_iommu_ion_info *ion_info = iommu_info->private;
106
107         vcodec_ion_detach(iommu_info);
108         kfree(ion_info);
109         iommu_info->private = NULL;
110
111         return 0;
112 }
113
114 static int
115 vcodec_ion_free(struct vcodec_iommu_session_info *session_info, int idx)
116 {
117         struct vcodec_ion_buffer *ion_buffer;
118
119         mutex_lock(&session_info->list_mutex);
120         ion_buffer = vcodec_ion_get_buffer_no_lock(session_info, idx);
121
122         if (!ion_buffer) {
123                 mutex_unlock(&session_info->list_mutex);
124                 pr_err("%s can not find %d buffer in list\n", __func__, idx);
125
126                 return -EINVAL;
127         }
128
129         list_del_init(&ion_buffer->list);
130         mutex_unlock(&session_info->list_mutex);
131         kfree(ion_buffer);
132
133         return 0;
134 }
135
136 static int
137 vcodec_ion_unmap_iommu(struct vcodec_iommu_session_info *session_info, int idx)
138 {
139         struct vcodec_ion_buffer *ion_buffer;
140         struct vcodec_iommu_info *iommu_info = session_info->iommu_info;
141         struct vcodec_iommu_ion_info *ion_info = iommu_info->private;
142
143         mutex_lock(&session_info->list_mutex);
144         ion_buffer = vcodec_ion_get_buffer_no_lock(session_info, idx);
145         mutex_unlock(&session_info->list_mutex);
146
147         if (!ion_buffer) {
148                 pr_err("%s can not find %d buffer in list\n", __func__, idx);
149
150                 return -EINVAL;
151         }
152
153         ion_free(ion_info->ion_client, ion_buffer->handle);
154
155         return 0;
156 }
157
158 static int
159 vcodec_ion_map_iommu(struct vcodec_iommu_session_info *session_info, int idx,
160                      unsigned long *iova, unsigned long *size)
161 {
162         struct vcodec_ion_buffer *ion_buffer;
163         struct device *dev = session_info->dev;
164         struct vcodec_iommu_info *iommu_info = session_info->iommu_info;
165         struct vcodec_iommu_ion_info *ion_info = iommu_info->private;
166         int ret = 0;
167
168         /* Force to flush iommu table */
169         rockchip_iovmm_invalidate_tlb(session_info->dev);
170
171         mutex_lock(&session_info->list_mutex);
172         ion_buffer = vcodec_ion_get_buffer_no_lock(session_info, idx);
173         mutex_unlock(&session_info->list_mutex);
174
175         if (!ion_buffer) {
176                 pr_err("%s can not find %d buffer in list\n", __func__, idx);
177
178                 return -EINVAL;
179         }
180
181         if (session_info->mmu_dev)
182                 ret = ion_map_iommu(dev, ion_info->ion_client,
183                                     ion_buffer->handle, iova, size);
184         else
185                 ret = ion_phys(ion_info->ion_client, ion_buffer->handle,
186                                iova, (size_t *)size);
187
188         return ret;
189 }
190
191 static int
192 vcodec_ion_unmap_kernel(struct vcodec_iommu_session_info *session_info,
193                         int idx)
194 {
195         struct vcodec_ion_buffer *ion_buffer;
196
197         mutex_lock(&session_info->list_mutex);
198         ion_buffer = vcodec_ion_get_buffer_no_lock(session_info, idx);
199         mutex_unlock(&session_info->list_mutex);
200
201         if (!ion_buffer) {
202                 pr_err("%s can not find %d buffer in list\n", __func__, idx);
203
204                 return -EINVAL;
205         }
206
207         return 0;
208 }
209
210 static void *
211 vcodec_ion_map_kernel(struct vcodec_iommu_session_info *session_info, int idx)
212 {
213         struct vcodec_ion_buffer *ion_buffer;
214         struct vcodec_iommu_info *iommu_info = session_info->iommu_info;
215         struct vcodec_iommu_ion_info *ion_info = iommu_info->private;
216
217         rockchip_iovmm_invalidate_tlb(session_info->dev);
218
219         mutex_lock(&session_info->list_mutex);
220         ion_buffer = vcodec_ion_get_buffer_no_lock(session_info, idx);
221         mutex_unlock(&session_info->list_mutex);
222
223         if (!ion_buffer) {
224                 pr_err("%s can not find %d buffer in list\n", __func__, idx);
225
226                 return NULL;
227         }
228
229         return ion_map_kernel(ion_info->ion_client, ion_buffer->handle);
230 }
231
232 static int
233 vcodec_ion_import(struct vcodec_iommu_session_info *session_info, int fd)
234 {
235         struct vcodec_ion_buffer *ion_buffer = NULL;
236         struct vcodec_iommu_info *iommu_info = session_info->iommu_info;
237         struct vcodec_iommu_ion_info *ion_info = iommu_info->private;
238
239         ion_buffer = kzalloc(sizeof(*ion_buffer), GFP_KERNEL);
240         if (!ion_buffer)
241                 return -ENOMEM;
242
243         ion_buffer->handle = ion_import_dma_buf(ion_info->ion_client, fd);
244
245         INIT_LIST_HEAD(&ion_buffer->list);
246         mutex_lock(&session_info->list_mutex);
247         ion_buffer->index = session_info->max_idx;
248         list_add_tail(&ion_buffer->list, &session_info->buffer_list);
249         session_info->max_idx++;
250         if ((session_info->max_idx & 0xfffffff) == 0)
251                 session_info->max_idx = 0;
252         mutex_unlock(&session_info->list_mutex);
253
254         return ion_buffer->index;
255 }
256
257 static int vcodec_ion_create(struct vcodec_iommu_info *iommu_info)
258 {
259         struct vcodec_iommu_ion_info *ion_info;
260
261         iommu_info->private = kmalloc(sizeof(*ion_info), GFP_KERNEL);
262
263         ion_info = iommu_info->private;
264         if (!ion_info)
265                 return -ENOMEM;
266
267         ion_info->ion_client = rockchip_ion_client_create("vpu");
268         ion_info->attached = false;
269
270         vcodec_ion_attach(iommu_info);
271
272         return IS_ERR(ion_info->ion_client) ? -1 : 0;
273 }
274
275 static struct vcodec_iommu_ops ion_ops = {
276         .create = vcodec_ion_create,
277         .destroy = vcodec_ion_destroy,
278         .import = vcodec_ion_import,
279         .free = vcodec_ion_free,
280         .free_fd = NULL,
281         .map_kernel = vcodec_ion_map_kernel,
282         .unmap_kernel = vcodec_ion_unmap_kernel,
283         .map_iommu = vcodec_ion_map_iommu,
284         .unmap_iommu = vcodec_ion_unmap_iommu,
285         .dump = NULL,
286         .attach = vcodec_ion_attach,
287         .detach = vcodec_ion_detach,
288         .clear = vcodec_ion_clear_session,
289 };
290
291 /*
292  * we do not manage the ref number ourselves,
293  * since ion will help us to do that. what we
294  * need to do is just map/unmap and import/free
295  * every time
296  */
297 void vcodec_iommu_ion_set_ops(struct vcodec_iommu_info *iommu_info)
298 {
299         if (!iommu_info)
300                 return;
301         iommu_info->ops = &ion_ops;
302 }