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