arm: tegra: nvmap: Update nvmap_alloc api.
[linux-2.6.git] / drivers / video / tegra / host / nvhost_job.c
1 /*
2  * drivers/video/tegra/host/nvhost_job.c
3  *
4  * Tegra Graphics Host Job
5  *
6  * Copyright (c) 2010-2012, NVIDIA Corporation.
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <linux/slab.h>
22 #include <linux/kref.h>
23 #include <linux/err.h>
24 #include <linux/vmalloc.h>
25 #include <mach/nvmap.h>
26 #include "nvhost_channel.h"
27 #include "nvhost_job.h"
28 #include "dev.h"
29
30 /* Magic to use to fill freed handle slots */
31 #define BAD_MAGIC 0xdeadbeef
32
33 static int job_size(struct nvhost_submit_hdr_ext *hdr)
34 {
35         int num_pins = hdr ? (hdr->num_relocs + hdr->num_cmdbufs)*2 : 0;
36         int num_waitchks = hdr ? hdr->num_waitchks : 0;
37
38         return sizeof(struct nvhost_job)
39                         + num_pins * sizeof(struct nvmap_pinarray_elem)
40                         + num_pins * sizeof(struct nvmap_handle *)
41                         + num_waitchks * sizeof(struct nvhost_waitchk);
42 }
43
44 static int gather_size(int num_cmdbufs)
45 {
46         return num_cmdbufs * sizeof(struct nvhost_channel_gather);
47 }
48
49 static void free_gathers(struct nvhost_job *job)
50 {
51         if (job->gathers) {
52                 nvmap_munmap(job->gather_mem, job->gathers);
53                 job->gathers = NULL;
54         }
55         if (job->gather_mem) {
56                 nvmap_free(job->nvmap, job->gather_mem);
57                 job->gather_mem = NULL;
58         }
59 }
60
61 static int alloc_gathers(struct nvhost_job *job,
62                 int num_cmdbufs)
63 {
64         int err = 0;
65
66         job->gather_mem = NULL;
67         job->gathers = NULL;
68         job->gather_mem_size = 0;
69
70         if (num_cmdbufs) {
71                 /* Allocate memory */
72                 job->gather_mem = nvmap_alloc(job->nvmap,
73                                 gather_size(num_cmdbufs),
74                                 32, NVMAP_HANDLE_CACHEABLE, 0);
75                 if (IS_ERR_OR_NULL(job->gather_mem)) {
76                         err = PTR_ERR(job->gather_mem);
77                         job->gather_mem = NULL;
78                         goto error;
79                 }
80                 job->gather_mem_size = gather_size(num_cmdbufs);
81
82                 /* Map memory to kernel */
83                 job->gathers = nvmap_mmap(job->gather_mem);
84                 if (IS_ERR_OR_NULL(job->gathers)) {
85                         err = PTR_ERR(job->gathers);
86                         job->gathers = NULL;
87                         goto error;
88                 }
89         }
90
91         return 0;
92
93 error:
94         free_gathers(job);
95         return err;
96 }
97
98 static int realloc_gathers(struct nvhost_job *oldjob,
99                 struct nvhost_job *newjob,
100                 int num_cmdbufs)
101 {
102         int err = 0;
103
104         /* Check if we can reuse gather buffer */
105         if (oldjob->gather_mem_size < gather_size(num_cmdbufs)
106                         || oldjob->nvmap != newjob->nvmap) {
107                 free_gathers(oldjob);
108                 err = alloc_gathers(newjob, num_cmdbufs);
109         } else {
110                 newjob->gather_mem = oldjob->gather_mem;
111                 newjob->gathers = oldjob->gathers;
112                 newjob->gather_mem_size = oldjob->gather_mem_size;
113
114                 oldjob->gather_mem = NULL;
115                 oldjob->gathers = NULL;
116                 oldjob->gather_mem_size = 0;
117         }
118         return err;
119 }
120
121 static void init_fields(struct nvhost_job *job,
122                 struct nvhost_submit_hdr_ext *hdr,
123                 int priority, int clientid)
124 {
125         int num_pins = hdr ? (hdr->num_relocs + hdr->num_cmdbufs)*2 : 0;
126         int num_waitchks = hdr ? hdr->num_waitchks : 0;
127         void *mem = job;
128
129         /* First init state to zero */
130         job->num_gathers = 0;
131         job->num_pins = 0;
132         job->num_unpins = 0;
133         job->num_waitchk = 0;
134         job->waitchk_mask = 0;
135         job->syncpt_id = 0;
136         job->syncpt_incrs = 0;
137         job->syncpt_end = 0;
138         job->priority = priority;
139         job->clientid = clientid;
140         job->null_kickoff = false;
141         job->first_get = 0;
142         job->num_slots = 0;
143
144         /* Redistribute memory to the structs */
145         mem += sizeof(struct nvhost_job);
146         if (num_pins) {
147                 job->pinarray = mem;
148                 mem += num_pins * sizeof(struct nvmap_pinarray_elem);
149                 job->unpins = mem;
150                 mem += num_pins * sizeof(struct nvmap_handle *);
151         } else {
152                 job->pinarray = NULL;
153                 job->unpins = NULL;
154         }
155
156         job->waitchk = num_waitchks ? mem : NULL;
157
158         /* Copy information from header */
159         if (hdr) {
160                 job->waitchk_mask = hdr->waitchk_mask;
161                 job->syncpt_id = hdr->syncpt_id;
162                 job->syncpt_incrs = hdr->syncpt_incrs;
163         }
164 }
165
166 struct nvhost_job *nvhost_job_alloc(struct nvhost_channel *ch,
167                 struct nvhost_hwctx *hwctx,
168                 struct nvhost_submit_hdr_ext *hdr,
169                 struct nvmap_client *nvmap,
170                 int priority,
171                 int clientid)
172 {
173         struct nvhost_job *job = NULL;
174         int num_cmdbufs = hdr ? hdr->num_cmdbufs : 0;
175         int err = 0;
176
177         job = vzalloc(job_size(hdr));
178         if (!job)
179                 goto error;
180
181         kref_init(&job->ref);
182         job->ch = ch;
183         job->hwctx = hwctx;
184         job->nvmap = nvmap ? nvmap_client_get(nvmap) : NULL;
185
186         err = alloc_gathers(job, num_cmdbufs);
187         if (err)
188                 goto error;
189
190         init_fields(job, hdr, priority, clientid);
191
192         return job;
193
194 error:
195         if (job)
196                 nvhost_job_put(job);
197         return NULL;
198 }
199
200 struct nvhost_job *nvhost_job_realloc(
201                 struct nvhost_job *oldjob,
202                 struct nvhost_submit_hdr_ext *hdr,
203                 struct nvmap_client *nvmap,
204                 int priority, int clientid)
205 {
206         struct nvhost_job *newjob = NULL;
207         int num_cmdbufs = hdr ? hdr->num_cmdbufs : 0;
208         int err = 0;
209
210         newjob = vzalloc(job_size(hdr));
211         if (!newjob)
212                 goto error;
213         kref_init(&newjob->ref);
214         newjob->ch = oldjob->ch;
215         newjob->hwctx = oldjob->hwctx;
216         newjob->timeout = oldjob->timeout;
217         newjob->nvmap = nvmap ? nvmap_client_get(nvmap) : NULL;
218
219         err = realloc_gathers(oldjob, newjob, num_cmdbufs);
220         if (err)
221                 goto error;
222
223         nvhost_job_put(oldjob);
224
225         init_fields(newjob, hdr, priority, clientid);
226
227         return newjob;
228
229 error:
230         if (newjob)
231                 nvhost_job_put(newjob);
232         if (oldjob)
233                 nvhost_job_put(oldjob);
234         return NULL;
235 }
236
237 void nvhost_job_get(struct nvhost_job *job)
238 {
239         kref_get(&job->ref);
240 }
241
242 static void job_free(struct kref *ref)
243 {
244         struct nvhost_job *job = container_of(ref, struct nvhost_job, ref);
245
246         if (job->gathers)
247                 nvmap_munmap(job->gather_mem, job->gathers);
248         if (job->gather_mem)
249                 nvmap_free(job->nvmap, job->gather_mem);
250         if (job->nvmap)
251                 nvmap_client_put(job->nvmap);
252         vfree(job);
253 }
254
255 void nvhost_job_put(struct nvhost_job *job)
256 {
257         kref_put(&job->ref, job_free);
258 }
259
260 void nvhost_job_add_gather(struct nvhost_job *job,
261                 u32 mem_id, u32 words, u32 offset)
262 {
263         struct nvmap_pinarray_elem *pin;
264         struct nvhost_channel_gather *cur_gather =
265                         &job->gathers[job->num_gathers];
266
267         pin = &job->pinarray[job->num_pins++];
268         pin->patch_mem = (u32)nvmap_ref_to_handle(job->gather_mem);
269         pin->patch_offset = (void *)&(cur_gather->mem) - (void *)job->gathers;
270         pin->pin_mem = nvmap_convert_handle_u2k(mem_id);
271         pin->pin_offset = offset;
272         cur_gather->words = words;
273         cur_gather->mem_id = mem_id;
274         cur_gather->offset = offset;
275         job->num_gathers += 1;
276 }
277
278 int nvhost_job_pin(struct nvhost_job *job)
279 {
280         int err = 0;
281
282         /* pin mem handles and patch physical addresses */
283         job->num_unpins = nvmap_pin_array(job->nvmap,
284                                 nvmap_ref_to_handle(job->gather_mem),
285                                 job->pinarray, job->num_pins,
286                                 job->unpins);
287         if (job->num_unpins < 0)
288                 err = job->num_unpins;
289
290         return err;
291 }
292
293 void nvhost_job_unpin(struct nvhost_job *job)
294 {
295         nvmap_unpin_handles(job->nvmap, job->unpins,
296                         job->num_unpins);
297         memset(job->unpins, BAD_MAGIC,
298                         job->num_unpins * sizeof(struct nvmap_handle *));
299 }
300
301 /**
302  * Debug routine used to dump job entries
303  */
304 void nvhost_job_dump(struct device *dev, struct nvhost_job *job)
305 {
306         dev_dbg(dev, "    SYNCPT_ID   %d\n",
307                 job->syncpt_id);
308         dev_dbg(dev, "    SYNCPT_VAL  %d\n",
309                 job->syncpt_end);
310         dev_dbg(dev, "    FIRST_GET   0x%x\n",
311                 job->first_get);
312         dev_dbg(dev, "    TIMEOUT     %d\n",
313                 job->timeout);
314         dev_dbg(dev, "    CTX 0x%p\n",
315                 job->hwctx);
316         dev_dbg(dev, "    NUM_SLOTS   %d\n",
317                 job->num_slots);
318         dev_dbg(dev, "    NUM_HANDLES %d\n",
319                 job->num_unpins);
320 }