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
10struct 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
21static bool force_bulk;
22module_param(force_bulk, bool, 0444);
23MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio");
24
25static void bcm2835_audio_lock(struct bcm2835_audio_instance *instance)
26{
27 mutex_lock(&instance->vchi_mutex);
28 vchiq_use_service(instance: instance->alsa_stream->chip->vchi_ctx->instance,
29 service: instance->service_handle);
30}
31
32static void bcm2835_audio_unlock(struct bcm2835_audio_instance *instance)
33{
34 vchiq_release_service(instance: instance->alsa_stream->chip->vchi_ctx->instance,
35 service: instance->service_handle);
36 mutex_unlock(lock: &instance->vchi_mutex);
37}
38
39static 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(x: &instance->msg_avail_comp);
47 }
48
49 status = vchiq_queue_kernel_message(instance: instance->alsa_stream->chip->vchi_ctx->instance,
50 handle: instance->service_handle, data: m, size: 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(x: &instance->msg_avail_comp,
60 timeout: msecs_to_jiffies(m: 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
75static 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
86static 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: &m, wait);
92}
93
94static 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(instance: vchiq_instance,
100 service: 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(alsa_stream: instance->alsa_stream,
116 size: m->complete.count);
117 } else {
118 dev_err(instance->dev, "unexpected callback type=%d\n", m->type);
119 }
120
121 vchiq_release_message(instance: vchiq_instance, service: instance->service_handle, header);
122 return 0;
123}
124
125static int
126vc_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(instance: vchiq_instance, params: &params,
140 pservice: &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: instance->alsa_stream->chip->vchi_ctx->instance,
151 service: instance->service_handle);
152
153 return 0;
154}
155
156static 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: instance->alsa_stream->chip->vchi_ctx->instance,
162 service: instance->service_handle);
163
164 /* Close all VCHI service connections */
165 status = vchiq_close_service(instance: instance->alsa_stream->chip->vchi_ctx->instance,
166 service: 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(lock: &instance->vchi_mutex);
174}
175
176int 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(pinstance: &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(instance: vchi_ctx->instance);
189 if (ret) {
190 dev_dbg(dev, "failed to connect VCHI instance (ret=%d)\n",
191 ret);
192
193 kfree(objp: vchi_ctx->instance);
194 vchi_ctx->instance = NULL;
195
196 return -EIO;
197 }
198
199 return 0;
200}
201
202void 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
210int 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(size: 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(vchiq_instance: vchi_ctx->instance,
226 instance);
227 if (err < 0)
228 goto free_instance;
229
230 err = bcm2835_audio_send_simple(instance, type: VC_AUDIO_MSG_TYPE_OPEN,
231 wait: false);
232 if (err < 0)
233 goto deinit;
234
235 bcm2835_audio_lock(instance);
236 vchiq_get_peer_version(instance: vchi_ctx->instance, handle: instance->service_handle,
237 peer_version: &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(objp: instance);
251 return err;
252}
253
254int 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(instance: alsa_stream->instance, m: &m, wait: true);
267}
268
269int 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(instance: alsa_stream->instance, m: &m, wait: true);
287}
288
289int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream)
290{
291 return bcm2835_audio_send_simple(instance: alsa_stream->instance,
292 type: VC_AUDIO_MSG_TYPE_START, wait: false);
293}
294
295int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream)
296{
297 return bcm2835_audio_send_simple(instance: alsa_stream->instance,
298 type: VC_AUDIO_MSG_TYPE_STOP, wait: false);
299}
300
301/* FIXME: this doesn't seem working as expected for "draining" */
302int 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(instance: alsa_stream->instance, m: &m, wait: false);
310}
311
312int 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(instance: alsa_stream->instance,
318 type: VC_AUDIO_MSG_TYPE_CLOSE, wait: true);
319
320 /* Stop the audio service */
321 vc_vchi_audio_deinit(instance);
322 alsa_stream->instance = NULL;
323 kfree(objp: instance);
324
325 return err;
326}
327
328int 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: &m, wait: 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(instance: vchiq_instance, service: instance->service_handle, data: src, size: count,
356 NULL, mode: 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(instance: vchiq_instance,
362 handle: instance->service_handle, data: src, size: 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

source code of linux/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c