video: rockchip: iep: add drm support
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / iep / iep_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 "iep_iommu_ops.h"
32
33 struct iep_ion_buffer {
34         struct list_head list;
35         struct ion_handle *handle;
36         int index;
37 };
38
39 struct iep_iommu_ion_info {
40         struct ion_client *ion_client;
41         bool attached;
42 };
43
44 static struct iep_ion_buffer *
45 iep_ion_get_buffer_no_lock(struct iep_iommu_session_info *session_info,
46                            int idx)
47 {
48         struct iep_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 iep_ion_clear_session(struct iep_iommu_session_info *session_info)
61 {
62         /* do nothing */
63 }
64
65 static int iep_ion_attach(struct iep_iommu_info *iommu_info)
66 {
67         struct iep_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 iep_ion_detach(struct iep_iommu_info *iommu_info)
87 {
88         struct iep_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 iep_ion_destroy(struct iep_iommu_info *iommu_info)
104 {
105         struct iep_iommu_ion_info *ion_info = iommu_info->private;
106
107         iep_ion_detach(iommu_info);
108         kfree(ion_info);
109         iommu_info->private = NULL;
110
111         return 0;
112 }
113
114 static int
115 iep_ion_free(struct iep_iommu_session_info *session_info, int idx)
116 {
117         struct iep_ion_buffer *ion_buffer;
118
119         mutex_lock(&session_info->list_mutex);
120         ion_buffer = iep_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 iep_ion_unmap_iommu(struct iep_iommu_session_info *session_info, int idx)
138 {
139         struct iep_ion_buffer *ion_buffer;
140         struct iep_iommu_info *iommu_info = session_info->iommu_info;
141         struct iep_iommu_ion_info *ion_info = iommu_info->private;
142
143         mutex_lock(&session_info->list_mutex);
144         ion_buffer = iep_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 iep_ion_map_iommu(struct iep_iommu_session_info *session_info, int idx,
160                   unsigned long *iova, unsigned long *size)
161 {
162         struct iep_ion_buffer *ion_buffer;
163         struct device *dev = session_info->dev;
164         struct iep_iommu_info *iommu_info = session_info->iommu_info;
165         struct iep_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 = iep_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 iep_ion_import(struct iep_iommu_session_info *session_info, int fd)
193 {
194         struct iep_ion_buffer *ion_buffer = NULL;
195         struct iep_iommu_info *iommu_info = session_info->iommu_info;
196         struct iep_iommu_ion_info *ion_info = iommu_info->private;
197
198         ion_buffer = kzalloc(sizeof(*ion_buffer), GFP_KERNEL);
199         if (!ion_buffer)
200                 return -ENOMEM;
201
202         ion_buffer->handle = ion_import_dma_buf(ion_info->ion_client, fd);
203
204         INIT_LIST_HEAD(&ion_buffer->list);
205         mutex_lock(&session_info->list_mutex);
206         ion_buffer->index = session_info->max_idx;
207         list_add_tail(&ion_buffer->list, &session_info->buffer_list);
208         session_info->max_idx++;
209         if ((session_info->max_idx & 0xfffffff) == 0)
210                 session_info->max_idx = 0;
211         mutex_unlock(&session_info->list_mutex);
212
213         return ion_buffer->index;
214 }
215
216 static int iep_ion_create(struct iep_iommu_info *iommu_info)
217 {
218         struct iep_iommu_ion_info *ion_info;
219
220         iommu_info->private = kmalloc(sizeof(*ion_info), GFP_KERNEL);
221
222         ion_info = iommu_info->private;
223         if (!ion_info)
224                 return -ENOMEM;
225
226         ion_info->ion_client = rockchip_ion_client_create("vpu");
227         ion_info->attached = false;
228
229         iep_ion_attach(iommu_info);
230
231         return IS_ERR(ion_info->ion_client) ? -1 : 0;
232 }
233
234 static struct iep_iommu_ops ion_ops = {
235         .create = iep_ion_create,
236         .destroy = iep_ion_destroy,
237         .import = iep_ion_import,
238         .free = iep_ion_free,
239         .free_fd = NULL,
240         .map_iommu = iep_ion_map_iommu,
241         .unmap_iommu = iep_ion_unmap_iommu,
242         .dump = NULL,
243         .attach = iep_ion_attach,
244         .detach = iep_ion_detach,
245         .clear = iep_ion_clear_session,
246 };
247
248 /*
249  * we do not manage the ref number ourselves,
250  * since ion will help us to do that. what we
251  * need to do is just map/unmap and import/free
252  * every time
253  */
254 void iep_iommu_ion_set_ops(struct iep_iommu_info *iommu_info)
255 {
256         if (!iommu_info)
257                 return;
258         iommu_info->ops = &ion_ops;
259 }