diff options
Diffstat (limited to 'drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c')
-rw-r--r-- | drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c | 883 |
1 files changed, 202 insertions, 681 deletions
diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c index 868e2d6aaf1b..781754f36da7 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c @@ -1,190 +1,99 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright 2011 Broadcom Corporation. All rights reserved. */ -#include <linux/device.h> -#include <sound/core.h> -#include <sound/initval.h> -#include <sound/pcm.h> -#include <linux/io.h> -#include <linux/interrupt.h> -#include <linux/fs.h> -#include <linux/file.h> -#include <linux/mm.h> -#include <linux/syscalls.h> -#include <linux/uaccess.h> #include <linux/slab.h> -#include <linux/delay.h> -#include <linux/atomic.h> #include <linux/module.h> #include <linux/completion.h> - #include "bcm2835.h" - -/* ---- Include Files -------------------------------------------------------- */ - #include "vc_vchi_audioserv_defs.h" -/* ---- Private Constants and Types ------------------------------------------ */ - -#define BCM2835_AUDIO_STOP 0 -#define BCM2835_AUDIO_START 1 -#define BCM2835_AUDIO_WRITE 2 - -/* Logging macros (for remapping to other logging mechanisms, i.e., printf) */ -#ifdef AUDIO_DEBUG_ENABLE -#define LOG_ERR(fmt, arg...) pr_err("%s:%d " fmt, __func__, __LINE__, ##arg) -#define LOG_WARN(fmt, arg...) pr_info("%s:%d " fmt, __func__, __LINE__, ##arg) -#define LOG_INFO(fmt, arg...) pr_info("%s:%d " fmt, __func__, __LINE__, ##arg) -#define LOG_DBG(fmt, arg...) pr_info("%s:%d " fmt, __func__, __LINE__, ##arg) -#else -#define LOG_ERR(fmt, arg...) pr_err("%s:%d " fmt, __func__, __LINE__, ##arg) -#define LOG_WARN(fmt, arg...) no_printk(fmt, ##arg) -#define LOG_INFO(fmt, arg...) no_printk(fmt, ##arg) -#define LOG_DBG(fmt, arg...) no_printk(fmt, ##arg) -#endif - struct bcm2835_audio_instance { - unsigned int num_connections; - VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS]; + struct device *dev; + VCHI_SERVICE_HANDLE_T vchi_handle; struct completion msg_avail_comp; struct mutex vchi_mutex; struct bcm2835_alsa_stream *alsa_stream; int result; + unsigned int max_packet; short peer_version; }; static bool force_bulk; +module_param(force_bulk, bool, 0444); +MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio"); -/* ---- Private Variables ---------------------------------------------------- */ - -/* ---- Private Function Prototypes ------------------------------------------ */ - -/* ---- Private Functions ---------------------------------------------------- */ - -static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream); -static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream); -static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream, - unsigned int count, void *src); - -// Routine to send a message across a service - -static int -bcm2835_vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle, - void *data, - unsigned int size) +static void bcm2835_audio_lock(struct bcm2835_audio_instance *instance) { - return vchi_queue_kernel_message(handle, - data, - size); + mutex_lock(&instance->vchi_mutex); + vchi_service_use(instance->vchi_handle); } -static const u32 BCM2835_AUDIO_WRITE_COOKIE1 = ('B' << 24 | 'C' << 16 | - 'M' << 8 | 'A'); -static const u32 BCM2835_AUDIO_WRITE_COOKIE2 = ('D' << 24 | 'A' << 16 | - 'T' << 8 | 'A'); - -struct bcm2835_audio_work { - struct work_struct my_work; - struct bcm2835_alsa_stream *alsa_stream; - int cmd; - void *src; - unsigned int count; -}; - -static void my_wq_function(struct work_struct *work) +static void bcm2835_audio_unlock(struct bcm2835_audio_instance *instance) { - struct bcm2835_audio_work *w = - container_of(work, struct bcm2835_audio_work, my_work); - int ret = -9; - - switch (w->cmd) { - case BCM2835_AUDIO_START: - ret = bcm2835_audio_start_worker(w->alsa_stream); - break; - case BCM2835_AUDIO_STOP: - ret = bcm2835_audio_stop_worker(w->alsa_stream); - break; - case BCM2835_AUDIO_WRITE: - ret = bcm2835_audio_write_worker(w->alsa_stream, w->count, - w->src); - break; - default: - LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->cmd); - break; - } - kfree((void *)work); + vchi_service_release(instance->vchi_handle); + mutex_unlock(&instance->vchi_mutex); } -int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream) +static int bcm2835_audio_send_msg_locked(struct bcm2835_audio_instance *instance, + struct vc_audio_msg *m, bool wait) { - struct bcm2835_audio_work *work; + int status; - work = kmalloc(sizeof(*work), GFP_ATOMIC); - /*--- Queue some work (item 1) ---*/ - if (!work) { - LOG_ERR(" .. Error: NULL work kmalloc\n"); - return -ENOMEM; + if (wait) { + instance->result = -1; + init_completion(&instance->msg_avail_comp); } - INIT_WORK(&work->my_work, my_wq_function); - work->alsa_stream = alsa_stream; - work->cmd = BCM2835_AUDIO_START; - if (!queue_work(alsa_stream->my_wq, &work->my_work)) { - kfree(work); - return -EBUSY; - } - return 0; -} - -int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream) -{ - struct bcm2835_audio_work *work; - work = kmalloc(sizeof(*work), GFP_ATOMIC); - /*--- Queue some work (item 1) ---*/ - if (!work) { - LOG_ERR(" .. Error: NULL work kmalloc\n"); - return -ENOMEM; + status = vchi_queue_kernel_message(instance->vchi_handle, + m, sizeof(*m)); + if (status) { + dev_err(instance->dev, + "vchi message queue failed: %d, msg=%d\n", + status, m->type); + return -EIO; } - INIT_WORK(&work->my_work, my_wq_function); - work->alsa_stream = alsa_stream; - work->cmd = BCM2835_AUDIO_STOP; - if (!queue_work(alsa_stream->my_wq, &work->my_work)) { - kfree(work); - return -EBUSY; + + if (wait) { + if (!wait_for_completion_timeout(&instance->msg_avail_comp, + msecs_to_jiffies(10 * 1000))) { + dev_err(instance->dev, + "vchi message timeout, msg=%d\n", m->type); + return -ETIMEDOUT; + } else if (instance->result) { + dev_err(instance->dev, + "vchi message response error:%d, msg=%d\n", + instance->result, m->type); + return -EIO; + } } + return 0; } -int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream, - unsigned int count, void *src) +static int bcm2835_audio_send_msg(struct bcm2835_audio_instance *instance, + struct vc_audio_msg *m, bool wait) { - struct bcm2835_audio_work *work; + int err; - work = kmalloc(sizeof(*work), GFP_ATOMIC); - /*--- Queue some work (item 1) ---*/ - if (!work) { - LOG_ERR(" .. Error: NULL work kmalloc\n"); - return -ENOMEM; - } - INIT_WORK(&work->my_work, my_wq_function); - work->alsa_stream = alsa_stream; - work->cmd = BCM2835_AUDIO_WRITE; - work->src = src; - work->count = count; - if (!queue_work(alsa_stream->my_wq, &work->my_work)) { - kfree(work); - return -EBUSY; - } - return 0; + bcm2835_audio_lock(instance); + err = bcm2835_audio_send_msg_locked(instance, m, wait); + bcm2835_audio_unlock(instance); + return err; } -static void my_workqueue_quit(struct bcm2835_alsa_stream *alsa_stream) +static int bcm2835_audio_send_simple(struct bcm2835_audio_instance *instance, + int type, bool wait) { - flush_workqueue(alsa_stream->my_wq); - destroy_workqueue(alsa_stream->my_wq); - alsa_stream->my_wq = NULL; + struct vc_audio_msg m = { .type = type }; + + return bcm2835_audio_send_msg(instance, &m, wait); } +static const u32 BCM2835_AUDIO_WRITE_COOKIE1 = ('B' << 24 | 'C' << 16 | + 'M' << 8 | 'A'); +static const u32 BCM2835_AUDIO_WRITE_COOKIE2 = ('D' << 24 | 'A' << 16 | + 'T' << 8 | 'A'); + static void audio_vchi_callback(void *param, const VCHI_CALLBACK_REASON_T reason, void *msg_handle) @@ -197,172 +106,87 @@ static void audio_vchi_callback(void *param, if (reason != VCHI_CALLBACK_MSG_AVAILABLE) return; - if (!instance) { - LOG_ERR(" .. instance is null\n"); - BUG(); - return; - } - if (!instance->vchi_handle[0]) { - LOG_ERR(" .. instance->vchi_handle[0] is null\n"); - BUG(); - return; - } - status = vchi_msg_dequeue(instance->vchi_handle[0], + status = vchi_msg_dequeue(instance->vchi_handle, &m, sizeof(m), &msg_len, VCHI_FLAGS_NONE); if (m.type == VC_AUDIO_MSG_TYPE_RESULT) { - LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n", - instance, m.u.result.success); instance->result = m.u.result.success; complete(&instance->msg_avail_comp); } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) { - struct bcm2835_alsa_stream *alsa_stream = instance->alsa_stream; - - LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_COMPLETE, complete=%d\n", - instance, m.u.complete.count); if (m.u.complete.cookie1 != BCM2835_AUDIO_WRITE_COOKIE1 || m.u.complete.cookie2 != BCM2835_AUDIO_WRITE_COOKIE2) - LOG_ERR(" .. response is corrupt\n"); - else if (alsa_stream) { - atomic_add(m.u.complete.count, - &alsa_stream->retrieved); - bcm2835_playback_fifo(alsa_stream); - } else { - LOG_ERR(" .. unexpected alsa_stream=%p\n", - alsa_stream); - } + dev_err(instance->dev, "invalid cookie\n"); + else + bcm2835_playback_fifo(instance->alsa_stream, + m.u.complete.count); } else { - LOG_ERR(" .. unexpected m.type=%d\n", m.type); + dev_err(instance->dev, "unexpected callback type=%d\n", m.type); } } -static struct bcm2835_audio_instance * +static int vc_vchi_audio_init(VCHI_INSTANCE_T vchi_instance, - VCHI_CONNECTION_T **vchi_connections, - unsigned int num_connections) + struct bcm2835_audio_instance *instance) { - unsigned int i; - struct bcm2835_audio_instance *instance; + SERVICE_CREATION_T params = { + .version = VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER), + .service_id = VC_AUDIO_SERVER_NAME, + .callback = audio_vchi_callback, + .callback_param = instance, + }; int status; - int ret; - - LOG_DBG("%s: start", __func__); - if (num_connections > VCHI_MAX_NUM_CONNECTIONS) { - LOG_ERR("%s: unsupported number of connections %u (max=%u)\n", - __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS); - - return ERR_PTR(-EINVAL); - } - /* Allocate memory for this instance */ - instance = kzalloc(sizeof(*instance), GFP_KERNEL); - if (!instance) - return ERR_PTR(-ENOMEM); - - instance->num_connections = num_connections; - - /* Create a lock for exclusive, serialized VCHI connection access */ - mutex_init(&instance->vchi_mutex); /* Open the VCHI service connections */ - for (i = 0; i < num_connections; i++) { - SERVICE_CREATION_T params = { - .version = VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER), - .service_id = VC_AUDIO_SERVER_NAME, - .connection = vchi_connections[i], - .rx_fifo_size = 0, - .tx_fifo_size = 0, - .callback = audio_vchi_callback, - .callback_param = instance, - .want_unaligned_bulk_rx = 1, //TODO: remove VCOS_FALSE - .want_unaligned_bulk_tx = 1, //TODO: remove VCOS_FALSE - .want_crc = 0 - }; - - LOG_DBG("%s: about to open %i\n", __func__, i); - status = vchi_service_open(vchi_instance, ¶ms, - &instance->vchi_handle[i]); - - LOG_DBG("%s: opened %i: %p=%d\n", __func__, i, instance->vchi_handle[i], status); - if (status) { - LOG_ERR("%s: failed to open VCHI service connection (status=%d)\n", - __func__, status); - ret = -EPERM; - goto err_close_services; - } - /* Finished with the service for now */ - vchi_service_release(instance->vchi_handle[i]); - } - - LOG_DBG("%s: okay\n", __func__); - return instance; + status = vchi_service_open(vchi_instance, ¶ms, + &instance->vchi_handle); -err_close_services: - for (i = 0; i < instance->num_connections; i++) { - LOG_ERR("%s: closing %i: %p\n", __func__, i, instance->vchi_handle[i]); - if (instance->vchi_handle[i]) - vchi_service_close(instance->vchi_handle[i]); + if (status) { + dev_err(instance->dev, + "failed to open VCHI service connection (status=%d)\n", + status); + kfree(instance); + return -EPERM; } - kfree(instance); - LOG_ERR("%s: error\n", __func__); + /* Finished with the service for now */ + vchi_service_release(instance->vchi_handle); - return ERR_PTR(ret); + return 0; } -static int vc_vchi_audio_deinit(struct bcm2835_audio_instance *instance) +static void vc_vchi_audio_deinit(struct bcm2835_audio_instance *instance) { - unsigned int i; - - if (!instance) { - LOG_ERR("%s: invalid handle %p\n", __func__, instance); - - return -1; - } + int status; - LOG_DBG(" .. about to lock (%d)\n", instance->num_connections); - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } + mutex_lock(&instance->vchi_mutex); + vchi_service_use(instance->vchi_handle); /* Close all VCHI service connections */ - for (i = 0; i < instance->num_connections; i++) { - int status; - - LOG_DBG(" .. %i:closing %p\n", i, instance->vchi_handle[i]); - vchi_service_use(instance->vchi_handle[i]); - - status = vchi_service_close(instance->vchi_handle[i]); - if (status) { - LOG_DBG("%s: failed to close VCHI service connection (status=%d)\n", - __func__, status); - } + status = vchi_service_close(instance->vchi_handle); + if (status) { + dev_err(instance->dev, + "failed to close VCHI service connection (status=%d)\n", + status); } mutex_unlock(&instance->vchi_mutex); - - kfree(instance); - - return 0; } -int bcm2835_new_vchi_ctx(struct bcm2835_vchi_ctx *vchi_ctx) +int bcm2835_new_vchi_ctx(struct device *dev, struct bcm2835_vchi_ctx *vchi_ctx) { int ret; /* Initialize and create a VCHI connection */ ret = vchi_initialise(&vchi_ctx->vchi_instance); if (ret) { - LOG_ERR("%s: failed to initialise VCHI instance (ret=%d)\n", - __func__, ret); - + dev_err(dev, "failed to initialise VCHI instance (ret=%d)\n", + ret); return -EIO; } - ret = vchi_connect(NULL, 0, vchi_ctx->vchi_instance); + ret = vchi_connect(vchi_ctx->vchi_instance); if (ret) { - LOG_ERR("%s: failed to connect VCHI instance (ret=%d)\n", - __func__, ret); + dev_dbg(dev, "failed to connect VCHI instance (ret=%d)\n", + ret); kfree(vchi_ctx->vchi_instance); vchi_ctx->vchi_instance = NULL; @@ -381,473 +205,170 @@ void bcm2835_free_vchi_ctx(struct bcm2835_vchi_ctx *vchi_ctx) vchi_ctx->vchi_instance = NULL; } -static int bcm2835_audio_open_connection(struct bcm2835_alsa_stream *alsa_stream) -{ - struct bcm2835_audio_instance *instance = - (struct bcm2835_audio_instance *)alsa_stream->instance; - struct bcm2835_vchi_ctx *vhci_ctx = alsa_stream->chip->vchi_ctx; - - LOG_INFO("%s: start\n", __func__); - BUG_ON(instance); - if (instance) { - LOG_ERR("%s: VCHI instance already open (%p)\n", - __func__, instance); - instance->alsa_stream = alsa_stream; - alsa_stream->instance = instance; - return 0; - } - - /* Initialize an instance of the audio service */ - instance = vc_vchi_audio_init(vhci_ctx->vchi_instance, - &vhci_ctx->vchi_connection, 1); - - if (IS_ERR(instance)) { - LOG_ERR("%s: failed to initialize audio service\n", __func__); - - /* vchi_instance is retained for use the next time. */ - return PTR_ERR(instance); - } - - instance->alsa_stream = alsa_stream; - alsa_stream->instance = instance; - - LOG_DBG(" success !\n"); - - return 0; -} - int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream) { + struct bcm2835_vchi_ctx *vchi_ctx = alsa_stream->chip->vchi_ctx; struct bcm2835_audio_instance *instance; - struct vc_audio_msg m; - int status; - int ret; + int err; - alsa_stream->my_wq = alloc_workqueue("my_queue", WQ_HIGHPRI, 1); - if (!alsa_stream->my_wq) + /* Allocate memory for this instance */ + instance = kzalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) return -ENOMEM; + mutex_init(&instance->vchi_mutex); + instance->dev = alsa_stream->chip->dev; + instance->alsa_stream = alsa_stream; + alsa_stream->instance = instance; - ret = bcm2835_audio_open_connection(alsa_stream); - if (ret) - goto free_wq; - - instance = alsa_stream->instance; - LOG_DBG(" instance (%p)\n", instance); - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", instance->num_connections); - ret = -EINTR; - goto free_wq; - } - vchi_service_use(instance->vchi_handle[0]); - - m.type = VC_AUDIO_MSG_TYPE_OPEN; - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); - - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); - - ret = -1; - goto unlock; - } + err = vc_vchi_audio_init(vchi_ctx->vchi_instance, + instance); + if (err < 0) + goto free_instance; - ret = 0; + err = bcm2835_audio_send_simple(instance, VC_AUDIO_MSG_TYPE_OPEN, + false); + if (err < 0) + goto deinit; -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); + bcm2835_audio_lock(instance); + vchi_get_peer_version(instance->vchi_handle, &instance->peer_version); + bcm2835_audio_unlock(instance); + if (instance->peer_version < 2 || force_bulk) + instance->max_packet = 0; /* bulk transfer */ + else + instance->max_packet = 4000; -free_wq: - if (ret) - destroy_workqueue(alsa_stream->my_wq); + return 0; - return ret; + deinit: + vc_vchi_audio_deinit(instance); + free_instance: + alsa_stream->instance = NULL; + kfree(instance); + return err; } -static int bcm2835_audio_set_ctls_chan(struct bcm2835_alsa_stream *alsa_stream, - struct bcm2835_chip *chip) +int bcm2835_audio_set_ctls(struct bcm2835_alsa_stream *alsa_stream) { - struct vc_audio_msg m; - struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; - - LOG_INFO(" Setting ALSA dest(%d), volume(%d)\n", - chip->dest, chip->volume); - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - instance->result = -1; + struct bcm2835_chip *chip = alsa_stream->chip; + struct vc_audio_msg m = {}; m.type = VC_AUDIO_MSG_TYPE_CONTROL; m.u.control.dest = chip->dest; - m.u.control.volume = chip->volume; + if (!chip->mute) + m.u.control.volume = CHIP_MIN_VOLUME; + else + m.u.control.volume = alsa2chip(chip->volume); - /* Create the message available completion */ - init_completion(&instance->msg_avail_comp); - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); - - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); - - ret = -1; - goto unlock; - } - - /* We are expecting a reply from the videocore */ - wait_for_completion(&instance->msg_avail_comp); - - if (instance->result) { - LOG_ERR("%s: result=%d\n", __func__, instance->result); - - ret = -1; - goto unlock; - } - - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); - - return ret; -} - -int bcm2835_audio_set_ctls(struct bcm2835_chip *chip) -{ - int i; - int ret = 0; - - LOG_DBG(" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume); - - /* change ctls for all substreams */ - for (i = 0; i < MAX_SUBSTREAMS; i++) { - if (chip->avail_substreams & (1 << i)) { - if (!chip->alsa_stream[i]) { - LOG_DBG(" No ALSA stream available?! %i:%p (%x)\n", i, chip->alsa_stream[i], chip->avail_substreams); - ret = 0; - } else if (bcm2835_audio_set_ctls_chan(chip->alsa_stream[i], chip) != 0) { - LOG_ERR("Couldn't set the controls for stream %d\n", i); - ret = -1; - } else { - LOG_DBG(" Controls set for stream %d\n", i); - } - } - } - return ret; + return bcm2835_audio_send_msg(alsa_stream->instance, &m, true); } int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream, unsigned int channels, unsigned int samplerate, unsigned int bps) { - struct vc_audio_msg m; - struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; - - LOG_INFO(" Setting ALSA channels(%d), samplerate(%d), bits-per-sample(%d)\n", - channels, samplerate, bps); + struct vc_audio_msg m = { + .type = VC_AUDIO_MSG_TYPE_CONFIG, + .u.config.channels = channels, + .u.config.samplerate = samplerate, + .u.config.bps = bps, + }; + int err; /* resend ctls - alsa_stream may not have been open when first send */ - ret = bcm2835_audio_set_ctls_chan(alsa_stream, alsa_stream->chip); - if (ret) { - LOG_ERR(" Alsa controls not supported\n"); - return -EINVAL; - } - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - instance->result = -1; + err = bcm2835_audio_set_ctls(alsa_stream); + if (err) + return err; - m.type = VC_AUDIO_MSG_TYPE_CONFIG; - m.u.config.channels = channels; - m.u.config.samplerate = samplerate; - m.u.config.bps = bps; - - /* Create the message available completion */ - init_completion(&instance->msg_avail_comp); - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); - - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); - - ret = -1; - goto unlock; - } - - /* We are expecting a reply from the videocore */ - wait_for_completion(&instance->msg_avail_comp); - - if (instance->result) { - LOG_ERR("%s: result=%d", __func__, instance->result); - - ret = -1; - goto unlock; - } - - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); - - return ret; + return bcm2835_audio_send_msg(alsa_stream->instance, &m, true); } -int bcm2835_audio_setup(struct bcm2835_alsa_stream *alsa_stream) +int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream) { - - return 0; + return bcm2835_audio_send_simple(alsa_stream->instance, + VC_AUDIO_MSG_TYPE_START, false); } -static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream) +int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream) { - struct vc_audio_msg m; - struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - m.type = VC_AUDIO_MSG_TYPE_START; - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); - - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); - - ret = -1; - goto unlock; - } - - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); - return ret; + return bcm2835_audio_send_simple(alsa_stream->instance, + VC_AUDIO_MSG_TYPE_STOP, false); } -static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream) +int bcm2835_audio_drain(struct bcm2835_alsa_stream *alsa_stream) { - struct vc_audio_msg m; - struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - m.type = VC_AUDIO_MSG_TYPE_STOP; - m.u.stop.draining = alsa_stream->draining; - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); - - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); + struct vc_audio_msg m = { + .type = VC_AUDIO_MSG_TYPE_STOP, + .u.stop.draining = 1, + }; - ret = -1; - goto unlock; - } - - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); - return ret; + return bcm2835_audio_send_msg(alsa_stream->instance, &m, false); } int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream) { - struct vc_audio_msg m; struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; + int err; - my_workqueue_quit(alsa_stream); - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - m.type = VC_AUDIO_MSG_TYPE_CLOSE; - - /* Create the message available completion */ - init_completion(&instance->msg_avail_comp); - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); - - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); - ret = -1; - goto unlock; - } - - /* We are expecting a reply from the videocore */ - wait_for_completion(&instance->msg_avail_comp); - - if (instance->result) { - LOG_ERR("%s: failed result (result=%d)\n", - __func__, instance->result); - - ret = -1; - goto unlock; - } - - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); + err = bcm2835_audio_send_simple(alsa_stream->instance, + VC_AUDIO_MSG_TYPE_CLOSE, true); /* Stop the audio service */ vc_vchi_audio_deinit(instance); alsa_stream->instance = NULL; + kfree(instance); - return ret; + return err; } -static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream, - unsigned int count, void *src) +int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream, + unsigned int size, void *src) { - struct vc_audio_msg m; struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; - - LOG_INFO(" Writing %d bytes from %p\n", count, src); - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - if (instance->peer_version == 0 && - vchi_get_peer_version(instance->vchi_handle[0], &instance->peer_version) == 0) - LOG_DBG("%s: client version %d connected\n", __func__, instance->peer_version); - - m.type = VC_AUDIO_MSG_TYPE_WRITE; - m.u.write.count = count; - // old version uses bulk, new version uses control - m.u.write.max_packet = instance->peer_version < 2 || force_bulk ? 0 : 4000; - m.u.write.cookie1 = BCM2835_AUDIO_WRITE_COOKIE1; - m.u.write.cookie2 = BCM2835_AUDIO_WRITE_COOKIE2; - m.u.write.silence = src == NULL; - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); + struct vc_audio_msg m = { + .type = VC_AUDIO_MSG_TYPE_WRITE, + .u.write.count = size, + .u.write.max_packet = instance->max_packet, + .u.write.cookie1 = BCM2835_AUDIO_WRITE_COOKIE1, + .u.write.cookie2 = BCM2835_AUDIO_WRITE_COOKIE2, + }; + unsigned int count; + int err, status; - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); + if (!size) + return 0; - ret = -1; + bcm2835_audio_lock(instance); + err = bcm2835_audio_send_msg_locked(instance, &m, false); + if (err < 0) goto unlock; - } - if (!m.u.write.silence) { - if (!m.u.write.max_packet) { - /* Send the message to the videocore */ - status = vchi_bulk_queue_transmit(instance->vchi_handle[0], - src, count, - 0 * VCHI_FLAGS_BLOCK_UNTIL_QUEUED - + - 1 * VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, - NULL); - } else { - while (count > 0) { - int bytes = min_t(int, m.u.write.max_packet, count); - - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - src, bytes); - src = (char *)src + bytes; - count -= bytes; - } - } - if (status) { - LOG_ERR("%s: failed on vchi_bulk_queue_transmit (status=%d)\n", - __func__, status); - ret = -1; - goto unlock; + count = size; + if (!instance->max_packet) { + /* Send the message to the videocore */ + status = vchi_bulk_queue_transmit(instance->vchi_handle, + src, count, + VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, + NULL); + } else { + while (count > 0) { + int bytes = min(instance->max_packet, count); + + status = vchi_queue_kernel_message(instance->vchi_handle, + src, bytes); + src += bytes; + count -= bytes; } } - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); - return ret; -} - -/** - * Returns all buffers from arm->vc - */ -void bcm2835_audio_flush_buffers(struct bcm2835_alsa_stream *alsa_stream) -{ -} - -/** - * Forces VC to flush(drop) its filled playback buffers and - * return them the us. (VC->ARM) - */ -void bcm2835_audio_flush_playback_buffers(struct bcm2835_alsa_stream *alsa_stream) -{ -} -unsigned int bcm2835_audio_retrieve_buffers(struct bcm2835_alsa_stream *alsa_stream) -{ - unsigned int count = atomic_read(&alsa_stream->retrieved); + if (status) { + dev_err(instance->dev, + "failed on %d bytes transfer (status=%d)\n", + size, status); + err = -EIO; + } - atomic_sub(count, &alsa_stream->retrieved); - return count; + unlock: + bcm2835_audio_unlock(instance); + return err; } - -module_param(force_bulk, bool, 0444); -MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio"); |