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