drivers: video: rockchip: vcodec_dma_map_sg maybe fail
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / vcodec / vcodec_iommu_ops.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/slab.h>
18
19 #include "vcodec_iommu_ops.h"
20
21 static
22 struct vcodec_iommu_session_info *vcodec_iommu_get_session_info
23         (struct vcodec_iommu_info *iommu_info, struct vpu_session *session)
24 {
25         struct vcodec_iommu_session_info *session_info = NULL, *n;
26
27         list_for_each_entry_safe(session_info, n, &iommu_info->session_list,
28                                  head) {
29                 if (session_info->session == session)
30                         return session_info;
31         }
32
33         return NULL;
34 }
35
36 int vcodec_iommu_create(struct vcodec_iommu_info *iommu_info)
37 {
38         if (!iommu_info || !iommu_info->ops->create)
39                 return -EINVAL;
40
41         return iommu_info->ops->create(iommu_info);
42 }
43
44 int vcodec_iommu_import(struct vcodec_iommu_info *iommu_info,
45                         struct vpu_session *session, int fd)
46 {
47         struct vcodec_iommu_session_info *session_info = NULL;
48
49         if (!iommu_info || !iommu_info->ops->import || !session)
50                 return -EINVAL;
51
52         session_info = vcodec_iommu_get_session_info(iommu_info, session);
53         if (!session_info) {
54                 session_info = kzalloc(sizeof(*session_info), GFP_KERNEL);
55                 if (!session_info)
56                         return -ENOMEM;
57
58                 INIT_LIST_HEAD(&session_info->head);
59                 INIT_LIST_HEAD(&session_info->buffer_list);
60                 mutex_init(&session_info->list_mutex);
61                 session_info->max_idx = 0;
62                 session_info->session = session;
63                 session_info->mmu_dev = iommu_info->mmu_dev;
64                 session_info->dev = iommu_info->dev;
65                 session_info->iommu_info = iommu_info;
66                 session_info->buffer_nums = 0;
67                 mutex_lock(&iommu_info->list_mutex);
68                 list_add_tail(&session_info->head, &iommu_info->session_list);
69                 mutex_unlock(&iommu_info->list_mutex);
70         }
71
72         session_info->debug_level = iommu_info->debug_level;
73
74         return iommu_info->ops->import(session_info, fd);
75 }
76
77 int vcodec_iommu_free(struct vcodec_iommu_info *iommu_info,
78                       struct vpu_session *session, int idx)
79 {
80         struct vcodec_iommu_session_info *session_info = NULL;
81
82         session_info = vcodec_iommu_get_session_info(iommu_info, session);
83
84         if (!iommu_info || !iommu_info->ops->free || !session_info)
85                 return -EINVAL;
86
87         return iommu_info->ops->free(session_info, idx);
88 }
89
90 int vcodec_iommu_free_fd(struct vcodec_iommu_info *iommu_info,
91                          struct vpu_session *session, int fd)
92 {
93         struct vcodec_iommu_session_info *session_info = NULL;
94
95         session_info = vcodec_iommu_get_session_info(iommu_info, session);
96
97         if (!iommu_info || !iommu_info->ops->free_fd || !session_info)
98                 return -EINVAL;
99
100         return iommu_info->ops->free_fd(session_info, fd);
101 }
102
103 void *vcodec_iommu_map_kernel(struct vcodec_iommu_info *iommu_info,
104                                  struct vpu_session *session, int idx)
105 {
106         struct vcodec_iommu_session_info *session_info = NULL;
107
108         session_info = vcodec_iommu_get_session_info(iommu_info, session);
109
110         if (!iommu_info || !iommu_info->ops->map_kernel || !session_info)
111                 return NULL;
112
113         return iommu_info->ops->map_kernel(session_info, idx);
114 }
115
116 int vcodec_iommu_unmap_kernel(struct vcodec_iommu_info *iommu_info,
117                               struct vpu_session *session, int idx)
118 {
119         struct vcodec_iommu_session_info *session_info = NULL;
120
121         session_info = vcodec_iommu_get_session_info(iommu_info, session);
122
123         if (!iommu_info || !iommu_info->ops->unmap_kernel || !session_info)
124                 return -EINVAL;
125
126         return iommu_info->ops->unmap_kernel(session_info, idx);
127 }
128
129 int vcodec_iommu_map_iommu(struct vcodec_iommu_info *iommu_info,
130                            struct vpu_session *session,
131                            int idx, unsigned long *iova,
132                            unsigned long *size)
133 {
134         struct vcodec_iommu_session_info *session_info = NULL;
135
136         session_info = vcodec_iommu_get_session_info(iommu_info, session);
137
138         if (!iommu_info || !iommu_info->ops->map_iommu || !session_info)
139                 return -EINVAL;
140
141         return iommu_info->ops->map_iommu(session_info, idx, iova, size);
142 }
143
144 int vcodec_iommu_unmap_iommu(struct vcodec_iommu_info *iommu_info,
145                              struct vpu_session *session, int idx)
146 {
147         struct vcodec_iommu_session_info *session_info = NULL;
148
149         session_info = vcodec_iommu_get_session_info(iommu_info, session);
150
151         if (!iommu_info || !iommu_info->ops->unmap_iommu || !session_info)
152                 return -EINVAL;
153
154         return iommu_info->ops->unmap_iommu(session_info, idx);
155 }
156
157 int vcodec_iommu_destroy(struct vcodec_iommu_info *iommu_info)
158 {
159         if (!iommu_info || !iommu_info->ops->destroy)
160                 return -EINVAL;
161
162         return iommu_info->ops->destroy(iommu_info);
163 }
164
165 void vcodec_iommu_dump(struct vcodec_iommu_info *iommu_info,
166                        struct vpu_session *session)
167 {
168         struct vcodec_iommu_session_info *session_info = NULL;
169
170         session_info = vcodec_iommu_get_session_info(iommu_info, session);
171
172         if (!iommu_info || !iommu_info->ops->dump || !session_info)
173                 return;
174
175         iommu_info->ops->dump(session_info);
176 }
177
178 void vcodec_iommu_clear(struct vcodec_iommu_info *iommu_info,
179                         struct vpu_session *session)
180 {
181         struct vcodec_iommu_session_info *session_info = NULL;
182
183         session_info = vcodec_iommu_get_session_info(iommu_info, session);
184
185         if (!iommu_info || !iommu_info->ops->clear || !session_info)
186                 return;
187
188         iommu_info->ops->clear(session_info);
189
190         mutex_lock(&iommu_info->list_mutex);
191         list_del_init(&session_info->head);
192         kfree(session_info);
193         mutex_unlock(&iommu_info->list_mutex);
194 }
195
196 int vcodec_iommu_attach(struct vcodec_iommu_info *iommu_info)
197 {
198         if (!iommu_info || !iommu_info->ops->attach)
199                 return 0;
200
201         return iommu_info->ops->attach(iommu_info);
202 }
203
204 void vcodec_iommu_detach(struct vcodec_iommu_info *iommu_info)
205 {
206         if (!iommu_info || !iommu_info->ops->detach)
207                 return;
208
209         return iommu_info->ops->detach(iommu_info);
210 }
211
212 struct vcodec_iommu_info *
213 vcodec_iommu_info_create(struct device *dev,
214                          struct device *mmu_dev,
215                          int alloc_type)
216 {
217         struct vcodec_iommu_info *iommu_info = NULL;
218
219         iommu_info = kzalloc(sizeof(*iommu_info), GFP_KERNEL);
220         if (!iommu_info)
221                 return NULL;
222
223         iommu_info->dev = dev;
224         INIT_LIST_HEAD(&iommu_info->session_list);
225         mutex_init(&iommu_info->list_mutex);
226         mutex_init(&iommu_info->iommu_mutex);
227         switch (alloc_type) {
228 #ifdef CONFIG_DRM
229         case ALLOCATOR_USE_DRM:
230                 vcodec_iommu_drm_set_ops(iommu_info);
231                 break;
232 #endif
233 #ifdef CONFIG_ION
234         case ALLOCATOR_USE_ION:
235                 vcodec_iommu_ion_set_ops(iommu_info);
236                 break;
237 #endif
238         default:
239                 iommu_info->ops = NULL;
240                 break;
241         }
242
243         iommu_info->mmu_dev = mmu_dev;
244
245         vcodec_iommu_create(iommu_info);
246
247         return iommu_info;
248 }
249
250 int vcodec_iommu_info_destroy(struct vcodec_iommu_info *iommu_info)
251 {
252         vcodec_iommu_destroy(iommu_info);
253         kfree(iommu_info);
254
255         return 0;
256 }