File Index
Files and directories
Sign in
FILE

bcm2835-vchiq.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c 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, &params,
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.