| 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 | |