1 | /* |
2 | Copyright (c) 2016, Raspberry Pi (Trading) Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the following conditions are met: |
7 | * Redistributions of source code must retain the above copyright |
8 | notice, this list of conditions and the following disclaimer. |
9 | * Redistributions in binary form must reproduce the above copyright |
10 | notice, this list of conditions and the following disclaimer in the |
11 | documentation and/or other materials provided with the distribution. |
12 | * Neither the name of the copyright holder nor the |
13 | names of its contributors may be used to endorse or promote products |
14 | derived from this software without specific prior written permission. |
15 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | #include <string.h> |
29 | #include <stdio.h> |
30 | #include <stdarg.h> |
31 | #include <ctype.h> |
32 | #include <assert.h> |
33 | |
34 | #include "vchost.h" |
35 | |
36 | #include "interface/vcos/vcos.h" |
37 | #include "interface/vcos/vcos_logging.h" |
38 | #include "vcinclude/common.h" |
39 | #include "vc_vchi_gpuserv.h" |
40 | #include "vchiq.h" |
41 | #include "interface/vcos/vcos_stdbool.h" |
42 | |
43 | // VCOS logging category |
44 | static VCOS_LOG_CAT_T vcos_log_category; |
45 | #define VCOS_LOG_CATEGORY (&vcos_log_category) |
46 | |
47 | static VCHIQ_INSTANCE_T gpuserv_client_vchiq_instance; |
48 | static VCOS_ONCE_T gpuserv_client_once = VCOS_ONCE_INIT; |
49 | |
50 | /****************************************************************************** |
51 | Local types and defines. |
52 | ******************************************************************************/ |
53 | #define GPUSERV_MAX_LENGTH 512 |
54 | typedef struct { |
55 | VCHIQ_SERVICE_HANDLE_T service; |
56 | VCOS_MUTEX_T lock; |
57 | int initialised; |
58 | VCOS_EVENT_T message_available_event; |
59 | int refcount; |
60 | } GPUSERV_SERVICE_T; |
61 | |
62 | static GPUSERV_SERVICE_T gpuserv_client; |
63 | |
64 | |
65 | /****************************************************************************** |
66 | Static function. |
67 | ******************************************************************************/ |
68 | static VCHIQ_STATUS_T gpuserv_callback( VCHIQ_REASON_T reason, |
69 | VCHIQ_HEADER_T *, |
70 | VCHIQ_SERVICE_HANDLE_T vchiq_handle, |
71 | void *bulk_userdata ); |
72 | |
73 | static void init_once(void) |
74 | { |
75 | vcos_mutex_create(&gpuserv_client.lock, VCOS_FUNCTION); |
76 | } |
77 | |
78 | /****************************************************************************** |
79 | NAME |
80 | |
81 | vc_gpuserv_init |
82 | |
83 | SYNOPSIS |
84 | int32_t vc_gpuserv_init( void ) |
85 | |
86 | FUNCTION |
87 | Initialise the gpu service for use. A negative return value |
88 | indicates failure (which may mean it has not been started on VideoCore). |
89 | |
90 | RETURNS |
91 | zero on success |
92 | ******************************************************************************/ |
93 | |
94 | int32_t vc_gpuserv_init( void ) |
95 | { |
96 | VCHIQ_SERVICE_PARAMS_T vchiq_params; |
97 | VCHIQ_STATUS_T vchiq_status; |
98 | |
99 | vcos_once(&gpuserv_client_once, init_once); |
100 | |
101 | vcos_mutex_lock(&gpuserv_client.lock); |
102 | |
103 | if (gpuserv_client.refcount++ > 0) |
104 | { |
105 | /* Already initialised so nothing to do */ |
106 | vcos_mutex_unlock(&gpuserv_client.lock); |
107 | return VCOS_SUCCESS; |
108 | } |
109 | |
110 | vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_TRACE); |
111 | vcos_log_register("gpuserv" , VCOS_LOG_CATEGORY); |
112 | |
113 | vcos_log_trace("%s: starting initialisation" , VCOS_FUNCTION); |
114 | |
115 | /* Initialise a VCHIQ instance */ |
116 | vchiq_status = vchiq_initialise(&gpuserv_client_vchiq_instance); |
117 | if (vchiq_status != VCHIQ_SUCCESS) |
118 | { |
119 | vcos_log_error("%s: failed to initialise vchiq: %d" , VCOS_FUNCTION, vchiq_status); |
120 | goto error; |
121 | } |
122 | |
123 | vchiq_status = vchiq_connect(gpuserv_client_vchiq_instance); |
124 | if (vchiq_status != VCHIQ_SUCCESS) |
125 | { |
126 | vcos_log_error("%s: failed to connect to vchiq: %d" , VCOS_FUNCTION, vchiq_status); |
127 | goto error; |
128 | } |
129 | |
130 | memset(&vchiq_params, 0, sizeof(vchiq_params)); |
131 | vchiq_params.fourcc = VCHIQ_MAKE_FOURCC('G','P','U','S'); |
132 | vchiq_params.callback = gpuserv_callback; |
133 | vchiq_params.userdata = NULL; |
134 | vchiq_params.version = 1; |
135 | vchiq_params.version_min = 1; |
136 | |
137 | vchiq_status = vchiq_open_service(gpuserv_client_vchiq_instance, &vchiq_params, &gpuserv_client.service); |
138 | if (vchiq_status != VCHIQ_SUCCESS) |
139 | { |
140 | vcos_log_error("%s: could not open vchiq service: %d" , VCOS_FUNCTION, vchiq_status); |
141 | goto error; |
142 | } |
143 | vcos_mutex_unlock(&gpuserv_client.lock); |
144 | return 0; |
145 | error: |
146 | vcos_mutex_unlock(&gpuserv_client.lock); |
147 | return -1; |
148 | } |
149 | |
150 | /****************************************************************************** |
151 | NAME |
152 | |
153 | vc_gpuserv_deinit |
154 | |
155 | SYNOPSIS |
156 | void vc_gpuserv_init( void ) |
157 | |
158 | FUNCTION |
159 | Deinitialise the gpu service. Should be called when gpu_service is no longer required |
160 | |
161 | RETURNS |
162 | zero on success |
163 | ******************************************************************************/ |
164 | |
165 | void vc_gpuserv_deinit( void ) |
166 | { |
167 | vcos_mutex_lock(&gpuserv_client.lock); |
168 | |
169 | if (gpuserv_client.refcount > 0 && --gpuserv_client.refcount == 0) |
170 | { |
171 | vchi_service_close(gpuserv_client.service); |
172 | gpuserv_client.service = 0; |
173 | } |
174 | vcos_mutex_unlock(&gpuserv_client.lock); |
175 | } |
176 | |
177 | /****************************************************************************** |
178 | NAME |
179 | gpuserv_callback |
180 | |
181 | SYNOPSIS |
182 | void gpuserv_callback( VCHIQ_REASON_T reason, |
183 | VCHIQ_HEADER_T *header, |
184 | VCHIQ_SERVICE_HANDLE_T service, |
185 | void *bulk_userdata ) |
186 | FUNCTION |
187 | VCHIQ callback |
188 | |
189 | RETURNS |
190 | zero on success |
191 | ******************************************************************************/ |
192 | static VCHIQ_STATUS_T gpuserv_callback( VCHIQ_REASON_T reason, |
193 | VCHIQ_HEADER_T *, |
194 | VCHIQ_SERVICE_HANDLE_T service, |
195 | void *bulk_userdata ) |
196 | { |
197 | // reason is one of VCHIQ_MESSAGE_AVAILABLE, VCHIQ_BULK_TRANSMIT_DONE, VCHIQ_BULK_RECEIVE_DONE |
198 | switch (reason) |
199 | { |
200 | case VCHIQ_MESSAGE_AVAILABLE: |
201 | { |
202 | struct gpu_callback_s *c = (struct gpu_callback_s *)header->data; |
203 | if (c->func) |
204 | c->func(c->cookie); |
205 | vchiq_release_message(service, header); |
206 | break; |
207 | } |
208 | default: |
209 | ; |
210 | } |
211 | return 0; // Releases any command message (VCHIQ_MESSAGE_AVAILABLE), ignored otherwise |
212 | } |
213 | |
214 | /****************************************************************************** |
215 | NAME |
216 | vc_gpuserv_execute_code |
217 | |
218 | SYNOPSIS |
219 | int32_t vc_gpuserv_execute_code(int num_jobs, struct gpu_job_s jobs[]) |
220 | |
221 | FUNCTION |
222 | Submit a list of VPU/QPU jobs to be exeected by GPU |
223 | |
224 | RETURNS |
225 | zero on success |
226 | ******************************************************************************/ |
227 | #define MAX_JOBS 8 |
228 | int32_t vc_gpuserv_execute_code(int num_jobs, struct gpu_job_s jobs[]) |
229 | { |
230 | VCHIQ_ELEMENT_T elements[MAX_JOBS]; |
231 | int i; |
232 | |
233 | // hack: temporarily allow calling this function without calling vc_gpuserv_init |
234 | // will be removed later |
235 | if (!gpuserv_client.service) |
236 | { |
237 | vc_gpuserv_init(); |
238 | vcos_log_error("%s: called without calling vc_gpuserv_init" , VCOS_FUNCTION); |
239 | } |
240 | |
241 | if (!gpuserv_client.service) |
242 | { |
243 | vcos_log_error("%s: vchiq service not initialised" , VCOS_FUNCTION); |
244 | return -1; |
245 | } |
246 | if (num_jobs > MAX_JOBS) |
247 | return -1; |
248 | |
249 | for (i=0; i<num_jobs; i++) |
250 | { |
251 | elements[i].data = jobs + i; |
252 | elements[i].size = sizeof *jobs; |
253 | } |
254 | if (vchiq_queue_message(gpuserv_client.service, elements, num_jobs) != VCHIQ_SUCCESS) |
255 | { |
256 | goto error_exit; |
257 | } |
258 | return 0; |
259 | error_exit: |
260 | return -1; |
261 | } |
262 | |