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