1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright 2011 Broadcom Corporation. All rights reserved. */
3
4 #include <linux/slab.h>
5 #include <linux/module.h>
6 #include <linux/completion.h>
7 #include "bcm2835.h"
8 #include "vc_vchi_audioserv_defs.h"
9
10 struct bcm2835_audio_instance {
11 struct device *dev;
12 unsigned int service_handle;
13 struct completion msg_avail_comp;
14 struct mutex vchi_mutex; /* Serialize vchiq access */
15 struct bcm2835_alsa_stream *alsa_stream;
16 int result;
17 unsigned int max_packet;
18 short peer_version;
19 };
20
21 static bool force_bulk;
22 module_param(force_bulk, bool, 0444);
23 MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio");
24
bcm2835_audio_lock(struct bcm2835_audio_instance * instance)25 static void bcm2835_audio_lock(struct bcm2835_audio_instance *instance)
26 {
27 mutex_lock(&instance->vchi_mutex);
28 vchiq_use_service(instance->alsa_stream->chip->vchi_ctx->instance,
29 instance->service_handle);
30 }
31
bcm2835_audio_unlock(struct bcm2835_audio_instance * instance)32 static void bcm2835_audio_unlock(struct bcm2835_audio_instance *instance)
33 {
34 vchiq_release_service(instance->alsa_stream->chip->vchi_ctx->instance,
35 instance->service_handle);
36 mutex_unlock(&instance->vchi_mutex);
37 }
38
bcm2835_audio_send_msg_locked(struct bcm2835_audio_instance * instance,struct vc_audio_msg * m,bool wait)39 static int bcm2835_audio_send_msg_locked(struct bcm2835_audio_instance *instance,
40 struct vc_audio_msg *m, bool wait)
41 {
42 int status;
43
44 if (wait) {
45 instance->result = -1;
46 init_completion(&instance->msg_avail_comp);
47 }
48
49 status = vchiq_queue_kernel_message(instance->alsa_stream->chip->vchi_ctx->instance,
50 instance->service_handle, m, sizeof(*m));
51 if (status) {
52 dev_err(instance->dev,
53 "vchi message queue failed: %d, msg=%d\n",
54 status, m->type);
55 return -EIO;
56 }
57
58 if (wait) {
59 if (!wait_for_completion_timeout(&instance->msg_avail_comp,
60 msecs_to_jiffies(10 * 1000))) {
61 dev_err(instance->dev,
62 "vchi message timeout, msg=%d\n", m->type);
63 return -ETIMEDOUT;
64 } else if (instance->result) {
65 dev_err(instance->dev,
66 "vchi message response error:%d, msg=%d\n",
67 instance->result, m->type);
68 return -EIO;
69 }
70 }
71
72 return 0;
73 }
74
bcm2835_audio_send_msg(struct bcm2835_audio_instance * instance,struct vc_audio_msg * m,bool wait)75 static int bcm2835_audio_send_msg(struct bcm2835_audio_instance *instance,
76 struct vc_audio_msg *m, bool wait)
77 {
78 int err;
79
80 bcm2835_audio_lock(instance);
81 err = bcm2835_audio_send_msg_locked(instance, m, wait);
82 bcm2835_audio_unlock(instance);
83 return err;
84 }
85
bcm2835_audio_send_simple(struct bcm2835_audio_instance * instance,int type,bool wait)86 static int bcm2835_audio_send_simple(struct bcm2835_audio_instance *instance,
87 int type, bool wait)
88 {
89 struct vc_audio_msg m = { .type = type };
90
91 return bcm2835_audio_send_msg(instance, &m, wait);
92 }
93
audio_vchi_callback(struct vchiq_instance * vchiq_instance,enum vchiq_reason reason,struct vchiq_header * header,unsigned int handle,void * userdata)94 static int audio_vchi_callback(struct vchiq_instance *vchiq_instance,
95 enum vchiq_reason reason,
96 struct vchiq_header *header,
97 unsigned int handle, void *userdata)
98 {
99 struct bcm2835_audio_instance *instance = vchiq_get_service_userdata(vchiq_instance,
100 handle);
101 struct vc_audio_msg *m;
102
103 if (reason != VCHIQ_MESSAGE_AVAILABLE)
104 return 0;
105
106 m = (void *)header->data;
107 if (m->type == VC_AUDIO_MSG_TYPE_RESULT) {
108 instance->result = m->result.success;
109 complete(&instance->msg_avail_comp);
110 } else if (m->type == VC_AUDIO_MSG_TYPE_COMPLETE) {
111 if (m->complete.cookie1 != VC_AUDIO_WRITE_COOKIE1 ||
112 m->complete.cookie2 != VC_AUDIO_WRITE_COOKIE2)
113 dev_err(instance->dev, "invalid cookie\n");
114 else
115 bcm2835_playback_fifo(instance->alsa_stream,
116 m->complete.count);
117 } else {
118 dev_err(instance->dev, "unexpected callback type=%d\n", m->type);
119 }
120
121 vchiq_release_message(vchiq_instance, instance->service_handle, header);
122 return 0;
123 }
124
125 static int
vc_vchi_audio_init(struct vchiq_instance * vchiq_instance,struct bcm2835_audio_instance * instance)126 vc_vchi_audio_init(struct vchiq_instance *vchiq_instance,
127 struct bcm2835_audio_instance *instance)
128 {
129 struct vchiq_service_params_kernel params = {
130 .version = VC_AUDIOSERV_VER,
131 .version_min = VC_AUDIOSERV_MIN_VER,
132 .fourcc = VCHIQ_MAKE_FOURCC('A', 'U', 'D', 'S'),
133 .callback = audio_vchi_callback,
134 .userdata = instance,
135 };
136 int status;
137
138 /* Open the VCHI service connections */
139 status = vchiq_open_service(vchiq_instance, ¶ms,
140 &instance->service_handle);
141
142 if (status) {
143 dev_err(instance->dev,
144 "failed to open VCHI service connection (status=%d)\n",
145 status);
146 return -EPERM;
147 }
148
149 /* Finished with the service for now */
150 vchiq_release_service(instance->alsa_stream->chip->vchi_ctx->instance,
151 instance->service_handle);
152
153 return 0;
154 }
155
vc_vchi_audio_deinit(struct bcm2835_audio_instance * instance)156 static void vc_vchi_audio_deinit(struct bcm2835_audio_instance *instance)
157 {
158 int status;
159
160 mutex_lock(&instance->vchi_mutex);
161 vchiq_use_service(instance->alsa_stream->chip->vchi_ctx->instance,
162 instance->service_handle);
163
164 /* Close all VCHI service connections */
165 status = vchiq_close_service(instance->alsa_stream->chip->vchi_ctx->instance,
166 instance->service_handle);
167 if (status) {
168 dev_err(instance->dev,
169 "failed to close VCHI service connection (status=%d)\n",
170 status);
171 }
172
173 mutex_unlock(&instance->vchi_mutex);
174 }
175
bcm2835_new_vchi_ctx(struct device * dev,struct bcm2835_vchi_ctx * vchi_ctx)176 int bcm2835_new_vchi_ctx(struct device *dev, struct bcm2835_vchi_ctx *vchi_ctx)
177 {
178 int ret;
179
180 /* Initialize and create a VCHI connection */
181 ret = vchiq_initialise(&vchi_ctx->instance);
182 if (ret) {
183 dev_err(dev, "failed to initialise VCHI instance (ret=%d)\n",
184 ret);
185 return -EIO;
186 }
187
188 ret = vchiq_connect(vchi_ctx->instance);
189 if (ret) {
190 dev_dbg(dev, "failed to connect VCHI instance (ret=%d)\n",
191 ret);
192
193 kfree(vchi_ctx->instance);
194 vchi_ctx->instance = NULL;
195
196 return -EIO;
197 }
198
199 return 0;
200 }
201
bcm2835_free_vchi_ctx(struct bcm2835_vchi_ctx * vchi_ctx)202 void bcm2835_free_vchi_ctx(struct bcm2835_vchi_ctx *vchi_ctx)
203 {
204 /* Close the VCHI connection - it will also free vchi_ctx->instance */
205 WARN_ON(vchiq_shutdown(vchi_ctx->instance));
206
207 vchi_ctx->instance = NULL;
208 }
209
bcm2835_audio_open(struct bcm2835_alsa_stream * alsa_stream)210 int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream)
211 {
212 struct bcm2835_vchi_ctx *vchi_ctx = alsa_stream->chip->vchi_ctx;
213 struct bcm2835_audio_instance *instance;
214 int err;
215
216 /* Allocate memory for this instance */
217 instance = kzalloc(sizeof(*instance), GFP_KERNEL);
218 if (!instance)
219 return -ENOMEM;
220 mutex_init(&instance->vchi_mutex);
221 instance->dev = alsa_stream->chip->dev;
222 instance->alsa_stream = alsa_stream;
223 alsa_stream->instance = instance;
224
225 err = vc_vchi_audio_init(vchi_ctx->instance,
226 instance);
227 if (err < 0)
228 goto free_instance;
229
230 err = bcm2835_audio_send_simple(instance, VC_AUDIO_MSG_TYPE_OPEN,
231 false);
232 if (err < 0)
233 goto deinit;
234
235 bcm2835_audio_lock(instance);
236 vchiq_get_peer_version(vchi_ctx->instance, instance->service_handle,
237 &instance->peer_version);
238 bcm2835_audio_unlock(instance);
239 if (instance->peer_version < 2 || force_bulk)
240 instance->max_packet = 0; /* bulk transfer */
241 else
242 instance->max_packet = 4000;
243
244 return 0;
245
246 deinit:
247 vc_vchi_audio_deinit(instance);
248 free_instance:
249 alsa_stream->instance = NULL;
250 kfree(instance);
251 return err;
252 }
253
bcm2835_audio_set_ctls(struct bcm2835_alsa_stream * alsa_stream)254 int bcm2835_audio_set_ctls(struct bcm2835_alsa_stream *alsa_stream)
255 {
256 struct bcm2835_chip *chip = alsa_stream->chip;
257 struct vc_audio_msg m = {};
258
259 m.type = VC_AUDIO_MSG_TYPE_CONTROL;
260 m.control.dest = chip->dest;
261 if (!chip->mute)
262 m.control.volume = CHIP_MIN_VOLUME;
263 else
264 m.control.volume = alsa2chip(chip->volume);
265
266 return bcm2835_audio_send_msg(alsa_stream->instance, &m, true);
267 }
268
bcm2835_audio_set_params(struct bcm2835_alsa_stream * alsa_stream,unsigned int channels,unsigned int samplerate,unsigned int bps)269 int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream,
270 unsigned int channels, unsigned int samplerate,
271 unsigned int bps)
272 {
273 struct vc_audio_msg m = {
274 .type = VC_AUDIO_MSG_TYPE_CONFIG,
275 .config.channels = channels,
276 .config.samplerate = samplerate,
277 .config.bps = bps,
278 };
279 int err;
280
281 /* resend ctls - alsa_stream may not have been open when first send */
282 err = bcm2835_audio_set_ctls(alsa_stream);
283 if (err)
284 return err;
285
286 return bcm2835_audio_send_msg(alsa_stream->instance, &m, true);
287 }
288
bcm2835_audio_start(struct bcm2835_alsa_stream * alsa_stream)289 int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream)
290 {
291 return bcm2835_audio_send_simple(alsa_stream->instance,
292 VC_AUDIO_MSG_TYPE_START, false);
293 }
294
bcm2835_audio_stop(struct bcm2835_alsa_stream * alsa_stream)295 int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream)
296 {
297 return bcm2835_audio_send_simple(alsa_stream->instance,
298 VC_AUDIO_MSG_TYPE_STOP, false);
299 }
300
301 /* FIXME: this doesn't seem working as expected for "draining" */
bcm2835_audio_drain(struct bcm2835_alsa_stream * alsa_stream)302 int bcm2835_audio_drain(struct bcm2835_alsa_stream *alsa_stream)
303 {
304 struct vc_audio_msg m = {
305 .type = VC_AUDIO_MSG_TYPE_STOP,
306 .stop.draining = 1,
307 };
308
309 return bcm2835_audio_send_msg(alsa_stream->instance, &m, false);
310 }
311
bcm2835_audio_close(struct bcm2835_alsa_stream * alsa_stream)312 int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream)
313 {
314 struct bcm2835_audio_instance *instance = alsa_stream->instance;
315 int err;
316
317 err = bcm2835_audio_send_simple(alsa_stream->instance,
318 VC_AUDIO_MSG_TYPE_CLOSE, true);
319
320 /* Stop the audio service */
321 vc_vchi_audio_deinit(instance);
322 alsa_stream->instance = NULL;
323 kfree(instance);
324
325 return err;
326 }
327
bcm2835_audio_write(struct bcm2835_alsa_stream * alsa_stream,unsigned int size,void * src)328 int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream,
329 unsigned int size, void *src)
330 {
331 struct bcm2835_audio_instance *instance = alsa_stream->instance;
332 struct bcm2835_vchi_ctx *vchi_ctx = alsa_stream->chip->vchi_ctx;
333 struct vchiq_instance *vchiq_instance = vchi_ctx->instance;
334 struct vc_audio_msg m = {
335 .type = VC_AUDIO_MSG_TYPE_WRITE,
336 .write.count = size,
337 .write.max_packet = instance->max_packet,
338 .write.cookie1 = VC_AUDIO_WRITE_COOKIE1,
339 .write.cookie2 = VC_AUDIO_WRITE_COOKIE2,
340 };
341 unsigned int count;
342 int err, status;
343
344 if (!size)
345 return 0;
346
347 bcm2835_audio_lock(instance);
348 err = bcm2835_audio_send_msg_locked(instance, &m, false);
349 if (err < 0)
350 goto unlock;
351
352 count = size;
353 if (!instance->max_packet) {
354 /* Send the message to the videocore */
355 status = vchiq_bulk_transmit(vchiq_instance, instance->service_handle, src, count,
356 NULL, VCHIQ_BULK_MODE_BLOCKING);
357 } else {
358 while (count > 0) {
359 int bytes = min(instance->max_packet, count);
360
361 status = vchiq_queue_kernel_message(vchiq_instance,
362 instance->service_handle, src, bytes);
363 src += bytes;
364 count -= bytes;
365 }
366 }
367
368 if (status) {
369 dev_err(instance->dev,
370 "failed on %d bytes transfer (status=%d)\n",
371 size, status);
372 err = -EIO;
373 }
374
375 unlock:
376 bcm2835_audio_unlock(instance);
377 return err;
378 }
379