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