• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2012, 2013 Thierry Reding
3  * Copyright © 2013 Erik Faye-Lund
4  * Copyright © 2014 NVIDIA Corporation
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h"
27 #endif
28 
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "util_math.h"
34 #include "private.h"
35 
36 #define HOST1X_OPCODE_NONINCR(offset, count) \
37     ((0x2 << 28) | (((offset) & 0xfff) << 16) | ((count) & 0xffff))
38 
39 static inline unsigned int
drm_tegra_pushbuf_get_offset(struct drm_tegra_pushbuf * pushbuf,uint32_t * ptr)40 drm_tegra_pushbuf_get_offset(struct drm_tegra_pushbuf *pushbuf, uint32_t *ptr)
41 {
42     return ptr - pushbuf->start;
43 }
44 
drm_tegra_pushbuf_free(struct drm_tegra_pushbuf * pushbuf)45 void drm_tegra_pushbuf_free(struct drm_tegra_pushbuf *pushbuf)
46 {
47     if (pushbuf->start)
48         free(pushbuf->start);
49 
50     free(pushbuf);
51 }
52 
53 /**
54  * drm_tegra_pushbuf_begin() - prepare push buffer for a series of pushes
55  * @pushbuf: push buffer
56  * @words: maximum number of words in series of pushes to follow
57  */
58 drm_public int
drm_tegra_pushbuf_begin(struct drm_tegra_pushbuf * pushbuf,unsigned int words,uint32_t ** ptrp)59 drm_tegra_pushbuf_begin(struct drm_tegra_pushbuf *pushbuf,
60                         unsigned int words, uint32_t **ptrp)
61 {
62     struct drm_tegra_job *job = pushbuf->job;
63     unsigned long offset;
64     size_t size;
65     void *ptr;
66 
67     if (pushbuf->ptr + words >= pushbuf->end) {
68         words = pushbuf->end - pushbuf->start + words;
69         size = ALIGN(words * 4, job->page_size);
70         offset = pushbuf->ptr - pushbuf->start;
71 
72         ptr = realloc(pushbuf->start, size);
73         if (!ptr)
74             return -ENOMEM;
75 
76         pushbuf->start = ptr;
77         pushbuf->end = pushbuf->start + size / 4;
78         pushbuf->ptr = pushbuf->start + offset;
79     }
80 
81     if (ptrp)
82         *ptrp = pushbuf->ptr;
83 
84     return 0;
85 }
86 
87 drm_public int
drm_tegra_pushbuf_end(struct drm_tegra_pushbuf * pushbuf,uint32_t * ptr)88 drm_tegra_pushbuf_end(struct drm_tegra_pushbuf *pushbuf, uint32_t *ptr)
89 {
90     struct drm_tegra_submit_cmd *command;
91 
92     command = drm_tegra_job_add_command(pushbuf->job,
93                                         DRM_TEGRA_SUBMIT_CMD_GATHER_UPTR,
94                                         0);
95     if (!command)
96         return -ENOMEM;
97 
98     command->gather_uptr.words = ptr - pushbuf->start;
99     pushbuf->ptr = ptr;
100 
101     return 0;
102 }
103 
104 drm_public int
drm_tegra_pushbuf_wait(struct drm_tegra_pushbuf * pushbuf,struct drm_tegra_syncpoint * syncpt,uint32_t value)105 drm_tegra_pushbuf_wait(struct drm_tegra_pushbuf *pushbuf,
106                        struct drm_tegra_syncpoint *syncpt,
107                        uint32_t value)
108 {
109     struct drm_tegra_submit_cmd *command;
110 
111     command = drm_tegra_job_add_command(pushbuf->job,
112                                         DRM_TEGRA_SUBMIT_CMD_WAIT_SYNCPT,
113                                         0);
114     if (!command)
115         return -ENOMEM;
116 
117     command->wait_syncpt.id = syncpt->id;
118     command->wait_syncpt.value = value;
119 
120     return 0;
121 }
122 
123 drm_public int
drm_tegra_pushbuf_relocate(struct drm_tegra_pushbuf * pushbuf,uint32_t ** ptrp,struct drm_tegra_mapping * target,unsigned long offset,unsigned int shift,uint32_t flags)124 drm_tegra_pushbuf_relocate(struct drm_tegra_pushbuf *pushbuf, uint32_t **ptrp,
125                            struct drm_tegra_mapping *target,
126                            unsigned long offset, unsigned int shift,
127                            uint32_t flags)
128 {
129     struct drm_tegra_submit_buf *buffers, *buffer;
130     struct drm_tegra_job *job = pushbuf->job;
131     size_t size;
132 
133     size = (job->num_buffers + 1) * sizeof(*buffer);
134 
135     buffers = realloc(job->buffers, size);
136     if (!buffers)
137         return -ENOMEM;
138 
139     buffer = &buffers[job->num_buffers];
140 
141     memset(buffer, 0, sizeof(*buffer));
142     buffer->mapping = target->id;
143     buffer->flags = flags;
144     buffer->reloc.target_offset = offset;
145     buffer->reloc.gather_offset_words = drm_tegra_pushbuf_get_offset(pushbuf,
146                                                                      *ptrp);
147     buffer->reloc.shift = shift;
148 
149     *(*ptrp)++ = 0xdeadbeef;
150 
151     job->buffers = buffers;
152     job->num_buffers++;
153 
154     return 0;
155 }
156 
157 drm_public int
drm_tegra_pushbuf_sync(struct drm_tegra_pushbuf * pushbuf,struct drm_tegra_syncpoint * syncpt,unsigned int count)158 drm_tegra_pushbuf_sync(struct drm_tegra_pushbuf *pushbuf,
159                        struct drm_tegra_syncpoint *syncpt,
160                        unsigned int count)
161 {
162     struct drm_tegra_job *job = pushbuf->job;
163 
164     job->syncpt.increments += count;
165     job->syncpt.id = syncpt->id;
166 
167     return 0;
168 }
169 
170 drm_public int
drm_tegra_pushbuf_sync_cond(struct drm_tegra_pushbuf * pushbuf,uint32_t ** ptrp,struct drm_tegra_syncpoint * syncpt,enum drm_tegra_sync_cond cond)171 drm_tegra_pushbuf_sync_cond(struct drm_tegra_pushbuf *pushbuf, uint32_t **ptrp,
172                             struct drm_tegra_syncpoint *syncpt,
173                             enum drm_tegra_sync_cond cond)
174 {
175     struct drm_tegra_channel *channel = pushbuf->job->channel;
176 
177     if (cond >= DRM_TEGRA_SYNC_COND_MAX)
178         return -EINVAL;
179 
180     *(*ptrp)++ = HOST1X_OPCODE_NONINCR(0x0, 0x1);
181     *(*ptrp)++ = cond << channel->cond_shift | syncpt->id;
182 
183     return drm_tegra_pushbuf_sync(pushbuf, syncpt, 1);
184 }
185