1/*
2Copyright (c) 2016, Raspberry Pi (Trading) Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, 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
44static VCOS_LOG_CAT_T vcos_log_category;
45#define VCOS_LOG_CATEGORY (&vcos_log_category)
46
47static VCHIQ_INSTANCE_T gpuserv_client_vchiq_instance;
48static VCOS_ONCE_T gpuserv_client_once = VCOS_ONCE_INIT;
49
50/******************************************************************************
51Local types and defines.
52******************************************************************************/
53#define GPUSERV_MAX_LENGTH 512
54typedef 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
62static GPUSERV_SERVICE_T gpuserv_client;
63
64
65/******************************************************************************
66Static function.
67******************************************************************************/
68static VCHIQ_STATUS_T gpuserv_callback( VCHIQ_REASON_T reason,
69 VCHIQ_HEADER_T *header,
70 VCHIQ_SERVICE_HANDLE_T vchiq_handle,
71 void *bulk_userdata );
72
73static void init_once(void)
74{
75 vcos_mutex_create(&gpuserv_client.lock, VCOS_FUNCTION);
76}
77
78/******************************************************************************
79NAME
80
81 vc_gpuserv_init
82
83SYNOPSIS
84 int32_t vc_gpuserv_init( void )
85
86FUNCTION
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
90RETURNS
91 zero on success
92******************************************************************************/
93
94int32_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;
145error:
146 vcos_mutex_unlock(&gpuserv_client.lock);
147 return -1;
148}
149
150/******************************************************************************
151NAME
152
153 vc_gpuserv_deinit
154
155SYNOPSIS
156 void vc_gpuserv_init( void )
157
158FUNCTION
159 Deinitialise the gpu service. Should be called when gpu_service is no longer required
160
161RETURNS
162 zero on success
163******************************************************************************/
164
165void 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/******************************************************************************
178NAME
179 gpuserv_callback
180
181SYNOPSIS
182 void gpuserv_callback( VCHIQ_REASON_T reason,
183 VCHIQ_HEADER_T *header,
184 VCHIQ_SERVICE_HANDLE_T service,
185 void *bulk_userdata )
186FUNCTION
187 VCHIQ callback
188
189RETURNS
190 zero on success
191******************************************************************************/
192static VCHIQ_STATUS_T gpuserv_callback( VCHIQ_REASON_T reason,
193 VCHIQ_HEADER_T *header,
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/******************************************************************************
215NAME
216 vc_gpuserv_execute_code
217
218SYNOPSIS
219 int32_t vc_gpuserv_execute_code(int num_jobs, struct gpu_job_s jobs[])
220
221FUNCTION
222 Submit a list of VPU/QPU jobs to be exeected by GPU
223
224RETURNS
225 zero on success
226******************************************************************************/
227#define MAX_JOBS 8
228int32_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