]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c
96d3083e8add4489be95d68ef9d3c6b0aa37b840
[linux.git] / drivers / staging / vc04_services / bcm2835-audio / bcm2835-vchiq.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright 2011 Broadcom Corporation.  All rights reserved. */
3
4 #include <linux/device.h>
5 #include <sound/core.h>
6 #include <sound/initval.h>
7 #include <sound/pcm.h>
8 #include <linux/io.h>
9 #include <linux/interrupt.h>
10 #include <linux/fs.h>
11 #include <linux/file.h>
12 #include <linux/mm.h>
13 #include <linux/syscalls.h>
14 #include <linux/uaccess.h>
15 #include <linux/slab.h>
16 #include <linux/delay.h>
17 #include <linux/atomic.h>
18 #include <linux/module.h>
19 #include <linux/completion.h>
20
21 #include "bcm2835.h"
22
23 /* ---- Include Files -------------------------------------------------------- */
24
25 #include "vc_vchi_audioserv_defs.h"
26
27 /* ---- Private Constants and Types ------------------------------------------ */
28
29 #define BCM2835_AUDIO_STOP           0
30 #define BCM2835_AUDIO_START          1
31 #define BCM2835_AUDIO_WRITE          2
32
33 /* Logging macros (for remapping to other logging mechanisms, i.e., printf) */
34 #ifdef AUDIO_DEBUG_ENABLE
35 #define LOG_ERR(fmt, arg...)   pr_err("%s:%d " fmt, __func__, __LINE__, ##arg)
36 #define LOG_WARN(fmt, arg...)  pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
37 #define LOG_INFO(fmt, arg...)  pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
38 #define LOG_DBG(fmt, arg...)   pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
39 #else
40 #define LOG_ERR(fmt, arg...)   pr_err("%s:%d " fmt, __func__, __LINE__, ##arg)
41 #define LOG_WARN(fmt, arg...)    no_printk(fmt, ##arg)
42 #define LOG_INFO(fmt, arg...)    no_printk(fmt, ##arg)
43 #define LOG_DBG(fmt, arg...)     no_printk(fmt, ##arg)
44 #endif
45
46 struct bcm2835_audio_instance {
47         VCHI_SERVICE_HANDLE_T vchi_handle;
48         struct completion msg_avail_comp;
49         struct mutex vchi_mutex;
50         struct bcm2835_alsa_stream *alsa_stream;
51         int result;
52         unsigned int max_packet;
53         short peer_version;
54 };
55
56 static bool force_bulk;
57
58 /* ---- Private Variables ---------------------------------------------------- */
59
60 /* ---- Private Function Prototypes ------------------------------------------ */
61
62 /* ---- Private Functions ---------------------------------------------------- */
63
64 static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream);
65 static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream);
66 static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream,
67                                       unsigned int count, void *src);
68
69 static void bcm2835_audio_lock(struct bcm2835_audio_instance *instance)
70 {
71         mutex_lock(&instance->vchi_mutex);
72         vchi_service_use(instance->vchi_handle);
73 }
74
75 static void bcm2835_audio_unlock(struct bcm2835_audio_instance *instance)
76 {
77         vchi_service_release(instance->vchi_handle);
78         mutex_unlock(&instance->vchi_mutex);
79 }
80
81 static int bcm2835_audio_send_msg_locked(struct bcm2835_audio_instance *instance,
82                                          struct vc_audio_msg *m, bool wait)
83 {
84         int status;
85
86         if (wait) {
87                 instance->result = -1;
88                 init_completion(&instance->msg_avail_comp);
89         }
90
91         status = vchi_queue_kernel_message(instance->vchi_handle,
92                                            m, sizeof(*m));
93         if (status) {
94                 LOG_ERR("vchi message queue failed: %d, msg=%d\n",
95                         status, m->type);
96                 return -EIO;
97         }
98
99         if (wait) {
100                 if (!wait_for_completion_timeout(&instance->msg_avail_comp,
101                                                  msecs_to_jiffies(10 * 1000))) {
102                         LOG_ERR("vchi message timeout, msg=%d\n", m->type);
103                         return -ETIMEDOUT;
104                 } else if (instance->result) {
105                         LOG_ERR("vchi message response error:%d, msg=%d\n",
106                                 instance->result, m->type);
107                         return -EIO;
108                 }
109         }
110
111         return 0;
112 }
113
114 static int bcm2835_audio_send_msg(struct bcm2835_audio_instance *instance,
115                                   struct vc_audio_msg *m, bool wait)
116 {
117         int err;
118
119         bcm2835_audio_lock(instance);
120         err = bcm2835_audio_send_msg_locked(instance, m, wait);
121         bcm2835_audio_unlock(instance);
122         return err;
123 }
124
125 static int bcm2835_audio_send_simple(struct bcm2835_audio_instance *instance,
126                                      int type, bool wait)
127 {
128         struct vc_audio_msg m = { .type = type };
129
130         return bcm2835_audio_send_msg(instance, &m, wait);
131 }
132
133 static const u32 BCM2835_AUDIO_WRITE_COOKIE1 = ('B' << 24 | 'C' << 16 |
134                                                 'M' << 8  | 'A');
135 static const u32 BCM2835_AUDIO_WRITE_COOKIE2 = ('D' << 24 | 'A' << 16 |
136                                                 'T' << 8  | 'A');
137
138 struct bcm2835_audio_work {
139         struct work_struct my_work;
140         struct bcm2835_alsa_stream *alsa_stream;
141         int cmd;
142         void *src;
143         unsigned int count;
144 };
145
146 static void my_wq_function(struct work_struct *work)
147 {
148         struct bcm2835_audio_work *w =
149                 container_of(work, struct bcm2835_audio_work, my_work);
150         int ret = -9;
151
152         switch (w->cmd) {
153         case BCM2835_AUDIO_START:
154                 ret = bcm2835_audio_start_worker(w->alsa_stream);
155                 break;
156         case BCM2835_AUDIO_STOP:
157                 ret = bcm2835_audio_stop_worker(w->alsa_stream);
158                 break;
159         case BCM2835_AUDIO_WRITE:
160                 ret = bcm2835_audio_write_worker(w->alsa_stream, w->count,
161                                                  w->src);
162                 break;
163         default:
164                 LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->cmd);
165                 break;
166         }
167         kfree((void *)work);
168 }
169
170 int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream)
171 {
172         struct bcm2835_audio_work *work;
173
174         work = kmalloc(sizeof(*work), GFP_ATOMIC);
175         /*--- Queue some work (item 1) ---*/
176         if (!work) {
177                 LOG_ERR(" .. Error: NULL work kmalloc\n");
178                 return -ENOMEM;
179         }
180         INIT_WORK(&work->my_work, my_wq_function);
181         work->alsa_stream = alsa_stream;
182         work->cmd = BCM2835_AUDIO_START;
183         if (!queue_work(alsa_stream->my_wq, &work->my_work)) {
184                 kfree(work);
185                 return -EBUSY;
186         }
187         return 0;
188 }
189
190 int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream)
191 {
192         struct bcm2835_audio_work *work;
193
194         work = kmalloc(sizeof(*work), GFP_ATOMIC);
195         /*--- Queue some work (item 1) ---*/
196         if (!work) {
197                 LOG_ERR(" .. Error: NULL work kmalloc\n");
198                 return -ENOMEM;
199         }
200         INIT_WORK(&work->my_work, my_wq_function);
201         work->alsa_stream = alsa_stream;
202         work->cmd = BCM2835_AUDIO_STOP;
203         if (!queue_work(alsa_stream->my_wq, &work->my_work)) {
204                 kfree(work);
205                 return -EBUSY;
206         }
207         return 0;
208 }
209
210 int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream,
211                         unsigned int count, void *src)
212 {
213         struct bcm2835_audio_work *work;
214
215         work = kmalloc(sizeof(*work), GFP_ATOMIC);
216         /*--- Queue some work (item 1) ---*/
217         if (!work) {
218                 LOG_ERR(" .. Error: NULL work kmalloc\n");
219                 return -ENOMEM;
220         }
221         INIT_WORK(&work->my_work, my_wq_function);
222         work->alsa_stream = alsa_stream;
223         work->cmd = BCM2835_AUDIO_WRITE;
224         work->src = src;
225         work->count = count;
226         if (!queue_work(alsa_stream->my_wq, &work->my_work)) {
227                 kfree(work);
228                 return -EBUSY;
229         }
230         return 0;
231 }
232
233 static void my_workqueue_quit(struct bcm2835_alsa_stream *alsa_stream)
234 {
235         flush_workqueue(alsa_stream->my_wq);
236         destroy_workqueue(alsa_stream->my_wq);
237         alsa_stream->my_wq = NULL;
238 }
239
240 static void audio_vchi_callback(void *param,
241                                 const VCHI_CALLBACK_REASON_T reason,
242                                 void *msg_handle)
243 {
244         struct bcm2835_audio_instance *instance = param;
245         int status;
246         int msg_len;
247         struct vc_audio_msg m;
248
249         if (reason != VCHI_CALLBACK_MSG_AVAILABLE)
250                 return;
251
252         if (!instance) {
253                 LOG_ERR(" .. instance is null\n");
254                 BUG();
255                 return;
256         }
257         if (!instance->vchi_handle) {
258                 LOG_ERR(" .. instance->vchi_handle is null\n");
259                 BUG();
260                 return;
261         }
262         status = vchi_msg_dequeue(instance->vchi_handle,
263                                   &m, sizeof(m), &msg_len, VCHI_FLAGS_NONE);
264         if (m.type == VC_AUDIO_MSG_TYPE_RESULT) {
265                 LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n",
266                         instance, m.u.result.success);
267                 instance->result = m.u.result.success;
268                 complete(&instance->msg_avail_comp);
269         } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
270                 struct bcm2835_alsa_stream *alsa_stream = instance->alsa_stream;
271
272                 LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_COMPLETE, complete=%d\n",
273                         instance, m.u.complete.count);
274                 if (m.u.complete.cookie1 != BCM2835_AUDIO_WRITE_COOKIE1 ||
275                     m.u.complete.cookie2 != BCM2835_AUDIO_WRITE_COOKIE2)
276                         LOG_ERR(" .. response is corrupt\n");
277                 else if (alsa_stream) {
278                         atomic_add(m.u.complete.count,
279                                    &alsa_stream->retrieved);
280                         bcm2835_playback_fifo(alsa_stream);
281                 } else {
282                         LOG_ERR(" .. unexpected alsa_stream=%p\n",
283                                 alsa_stream);
284                 }
285         } else {
286                 LOG_ERR(" .. unexpected m.type=%d\n", m.type);
287         }
288 }
289
290 static struct bcm2835_audio_instance *
291 vc_vchi_audio_init(VCHI_INSTANCE_T vchi_instance,
292                    VCHI_CONNECTION_T *vchi_connection)
293 {
294         SERVICE_CREATION_T params = {
295                 .version                = VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER),
296                 .service_id             = VC_AUDIO_SERVER_NAME,
297                 .connection             = vchi_connection,
298                 .rx_fifo_size           = 0,
299                 .tx_fifo_size           = 0,
300                 .callback               = audio_vchi_callback,
301                 .want_unaligned_bulk_rx = 1, //TODO: remove VCOS_FALSE
302                 .want_unaligned_bulk_tx = 1, //TODO: remove VCOS_FALSE
303                 .want_crc               = 0
304         };
305         struct bcm2835_audio_instance *instance;
306         int status;
307
308         /* Allocate memory for this instance */
309         instance = kzalloc(sizeof(*instance), GFP_KERNEL);
310         if (!instance)
311                 return ERR_PTR(-ENOMEM);
312
313         /* Create a lock for exclusive, serialized VCHI connection access */
314         mutex_init(&instance->vchi_mutex);
315         /* Open the VCHI service connections */
316         params.callback_param = instance,
317
318         status = vchi_service_open(vchi_instance, &params,
319                                    &instance->vchi_handle);
320
321         if (status) {
322                 LOG_ERR("%s: failed to open VCHI service connection (status=%d)\n",
323                         __func__, status);
324                 kfree(instance);
325                 return ERR_PTR(-EPERM);
326         }
327
328         /* Finished with the service for now */
329         vchi_service_release(instance->vchi_handle);
330
331         return instance;
332 }
333
334 static int vc_vchi_audio_deinit(struct bcm2835_audio_instance *instance)
335 {
336         int status;
337
338         mutex_lock(&instance->vchi_mutex);
339         vchi_service_use(instance->vchi_handle);
340
341         /* Close all VCHI service connections */
342         status = vchi_service_close(instance->vchi_handle);
343         if (status) {
344                 LOG_DBG("%s: failed to close VCHI service connection (status=%d)\n",
345                         __func__, status);
346         }
347
348         mutex_unlock(&instance->vchi_mutex);
349
350         kfree(instance);
351
352         return 0;
353 }
354
355 int bcm2835_new_vchi_ctx(struct bcm2835_vchi_ctx *vchi_ctx)
356 {
357         int ret;
358
359         /* Initialize and create a VCHI connection */
360         ret = vchi_initialise(&vchi_ctx->vchi_instance);
361         if (ret) {
362                 LOG_ERR("%s: failed to initialise VCHI instance (ret=%d)\n",
363                         __func__, ret);
364
365                 return -EIO;
366         }
367
368         ret = vchi_connect(NULL, 0, vchi_ctx->vchi_instance);
369         if (ret) {
370                 LOG_ERR("%s: failed to connect VCHI instance (ret=%d)\n",
371                         __func__, ret);
372
373                 kfree(vchi_ctx->vchi_instance);
374                 vchi_ctx->vchi_instance = NULL;
375
376                 return -EIO;
377         }
378
379         return 0;
380 }
381
382 void bcm2835_free_vchi_ctx(struct bcm2835_vchi_ctx *vchi_ctx)
383 {
384         /* Close the VCHI connection - it will also free vchi_instance */
385         WARN_ON(vchi_disconnect(vchi_ctx->vchi_instance));
386
387         vchi_ctx->vchi_instance = NULL;
388 }
389
390 static int bcm2835_audio_open_connection(struct bcm2835_alsa_stream *alsa_stream)
391 {
392         struct bcm2835_audio_instance *instance =
393                 (struct bcm2835_audio_instance *)alsa_stream->instance;
394         struct bcm2835_vchi_ctx *vhci_ctx = alsa_stream->chip->vchi_ctx;
395
396         /* Initialize an instance of the audio service */
397         instance = vc_vchi_audio_init(vhci_ctx->vchi_instance,
398                                       vhci_ctx->vchi_connection);
399
400         if (IS_ERR(instance))
401                 return PTR_ERR(instance);
402
403         instance->alsa_stream = alsa_stream;
404         alsa_stream->instance = instance;
405
406         return 0;
407 }
408
409 int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream)
410 {
411         struct bcm2835_audio_instance *instance;
412         int err;
413
414         alsa_stream->my_wq = alloc_workqueue("my_queue", WQ_HIGHPRI, 1);
415         if (!alsa_stream->my_wq)
416                 return -ENOMEM;
417
418         err = bcm2835_audio_open_connection(alsa_stream);
419         if (err < 0)
420                 goto free_wq;
421
422         instance = alsa_stream->instance;
423
424         err = bcm2835_audio_send_simple(instance, VC_AUDIO_MSG_TYPE_OPEN,
425                                         false);
426         if (err < 0)
427                 goto deinit;
428
429         bcm2835_audio_lock(instance);
430         vchi_get_peer_version(instance->vchi_handle, &instance->peer_version);
431         bcm2835_audio_unlock(instance);
432         if (instance->peer_version < 2 || force_bulk)
433                 instance->max_packet = 0; /* bulk transfer */
434         else
435                 instance->max_packet = 4000;
436
437         return 0;
438
439  deinit:
440         vc_vchi_audio_deinit(instance);
441  free_wq:
442         destroy_workqueue(alsa_stream->my_wq);
443         return err;
444 }
445
446 int bcm2835_audio_set_ctls(struct bcm2835_alsa_stream *alsa_stream)
447 {
448         struct bcm2835_chip *chip = alsa_stream->chip;
449         struct vc_audio_msg m = {};
450
451         m.type = VC_AUDIO_MSG_TYPE_CONTROL;
452         m.u.control.dest = chip->dest;
453         if (!chip->mute)
454                 m.u.control.volume = CHIP_MIN_VOLUME;
455         else
456                 m.u.control.volume = alsa2chip(chip->volume);
457
458         return bcm2835_audio_send_msg(alsa_stream->instance, &m, true);
459 }
460
461 int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream,
462                              unsigned int channels, unsigned int samplerate,
463                              unsigned int bps)
464 {
465         struct vc_audio_msg m = {
466                  .type = VC_AUDIO_MSG_TYPE_CONFIG,
467                  .u.config.channels = channels,
468                  .u.config.samplerate = samplerate,
469                  .u.config.bps = bps,
470         };
471         int err;
472
473         /* resend ctls - alsa_stream may not have been open when first send */
474         err = bcm2835_audio_set_ctls(alsa_stream);
475         if (err)
476                 return err;
477
478         return bcm2835_audio_send_msg(alsa_stream->instance, &m, true);
479 }
480
481 static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream)
482 {
483         return bcm2835_audio_send_simple(alsa_stream->instance,
484                                          VC_AUDIO_MSG_TYPE_START, false);
485 }
486
487 static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream)
488 {
489         return bcm2835_audio_send_simple(alsa_stream->instance,
490                                          VC_AUDIO_MSG_TYPE_STOP, false);
491 }
492
493 int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream)
494 {
495         struct bcm2835_audio_instance *instance = alsa_stream->instance;
496         int err;
497
498         my_workqueue_quit(alsa_stream);
499
500         err = bcm2835_audio_send_simple(alsa_stream->instance,
501                                         VC_AUDIO_MSG_TYPE_CLOSE, true);
502
503         /* Stop the audio service */
504         vc_vchi_audio_deinit(instance);
505         alsa_stream->instance = NULL;
506
507         return err;
508 }
509
510 static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream,
511                                       unsigned int size, void *src)
512 {
513         struct bcm2835_audio_instance *instance = alsa_stream->instance;
514         struct vc_audio_msg m = {
515                 .type = VC_AUDIO_MSG_TYPE_WRITE,
516                 .u.write.count = size,
517                 .u.write.max_packet = instance->max_packet,
518                 .u.write.cookie1 = BCM2835_AUDIO_WRITE_COOKIE1,
519                 .u.write.cookie2 = BCM2835_AUDIO_WRITE_COOKIE2,
520         };
521         unsigned int count;
522         int err, status;
523
524         if (!size)
525                 return 0;
526
527         bcm2835_audio_lock(instance);
528         err = bcm2835_audio_send_msg_locked(instance, &m, false);
529         if (err < 0)
530                 goto unlock;
531
532         count = size;
533         if (!instance->max_packet) {
534                 /* Send the message to the videocore */
535                 status = vchi_bulk_queue_transmit(instance->vchi_handle,
536                                                   src, count,
537                                                   VCHI_FLAGS_BLOCK_UNTIL_DATA_READ,
538                                                   NULL);
539         } else {
540                 while (count > 0) {
541                         int bytes = min(instance->max_packet, count);
542
543                         status = vchi_queue_kernel_message(instance->vchi_handle,
544                                                            src, bytes);
545                         src += bytes;
546                         count -= bytes;
547                 }
548         }
549
550         if (status) {
551                 LOG_ERR("failed on %d bytes transfer (status=%d)\n",
552                         size, status);
553                 err = -EIO;
554         }
555
556  unlock:
557         bcm2835_audio_unlock(instance);
558         return err;
559 }
560
561 unsigned int bcm2835_audio_retrieve_buffers(struct bcm2835_alsa_stream *alsa_stream)
562 {
563         unsigned int count = atomic_read(&alsa_stream->retrieved);
564
565         atomic_sub(count, &alsa_stream->retrieved);
566         return count;
567 }
568
569 module_param(force_bulk, bool, 0444);
570 MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio");