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 | |
25 | static void bcm2835_audio_lock(struct bcm2835_audio_instance *instance) |
26 | { |
27 | mutex_lock(&instance->vchi_mutex); |
28 | vchiq_use_service(instance: instance->alsa_stream->chip->vchi_ctx->instance, |
29 | service: instance->service_handle); |
30 | } |
31 | |
32 | static void bcm2835_audio_unlock(struct bcm2835_audio_instance *instance) |
33 | { |
34 | vchiq_release_service(instance: instance->alsa_stream->chip->vchi_ctx->instance, |
35 | service: instance->service_handle); |
36 | mutex_unlock(lock: &instance->vchi_mutex); |
37 | } |
38 | |
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(x: &instance->msg_avail_comp); |
47 | } |
48 | |
49 | status = vchiq_queue_kernel_message(instance: instance->alsa_stream->chip->vchi_ctx->instance, |
50 | handle: instance->service_handle, data: m, size: 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(x: &instance->msg_avail_comp, |
60 | timeout: msecs_to_jiffies(m: 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 | |
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 | |
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: &m, wait); |
92 | } |
93 | |
94 | static int audio_vchi_callback(struct vchiq_instance *vchiq_instance, |
95 | enum vchiq_reason reason, |
96 | struct vchiq_header *, |
97 | unsigned int handle, void *userdata) |
98 | { |
99 | struct bcm2835_audio_instance *instance = vchiq_get_service_userdata(instance: vchiq_instance, |
100 | service: handle); |
101 | struct vc_audio_msg *m; |
102 | |
103 | if (reason != VCHIQ_MESSAGE_AVAILABLE) |
104 | return 0; |
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(alsa_stream: instance->alsa_stream, |
116 | size: m->complete.count); |
117 | } else { |
118 | dev_err(instance->dev, "unexpected callback type=%d\n" , m->type); |
119 | } |
120 | |
121 | vchiq_release_message(instance: vchiq_instance, service: instance->service_handle, header); |
122 | return 0; |
123 | } |
124 | |
125 | static int |
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(instance: vchiq_instance, params: ¶ms, |
140 | pservice: &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: instance->alsa_stream->chip->vchi_ctx->instance, |
151 | service: instance->service_handle); |
152 | |
153 | return 0; |
154 | } |
155 | |
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: instance->alsa_stream->chip->vchi_ctx->instance, |
162 | service: instance->service_handle); |
163 | |
164 | /* Close all VCHI service connections */ |
165 | status = vchiq_close_service(instance: instance->alsa_stream->chip->vchi_ctx->instance, |
166 | service: 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(lock: &instance->vchi_mutex); |
174 | } |
175 | |
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(pinstance: &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(instance: vchi_ctx->instance); |
189 | if (ret) { |
190 | dev_dbg(dev, "failed to connect VCHI instance (ret=%d)\n" , |
191 | ret); |
192 | |
193 | kfree(objp: vchi_ctx->instance); |
194 | vchi_ctx->instance = NULL; |
195 | |
196 | return -EIO; |
197 | } |
198 | |
199 | return 0; |
200 | } |
201 | |
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 | |
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(size: 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(vchiq_instance: vchi_ctx->instance, |
226 | instance); |
227 | if (err < 0) |
228 | goto free_instance; |
229 | |
230 | err = bcm2835_audio_send_simple(instance, type: VC_AUDIO_MSG_TYPE_OPEN, |
231 | wait: false); |
232 | if (err < 0) |
233 | goto deinit; |
234 | |
235 | bcm2835_audio_lock(instance); |
236 | vchiq_get_peer_version(instance: vchi_ctx->instance, handle: instance->service_handle, |
237 | peer_version: &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(objp: instance); |
251 | return err; |
252 | } |
253 | |
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(instance: alsa_stream->instance, m: &m, wait: true); |
267 | } |
268 | |
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(instance: alsa_stream->instance, m: &m, wait: true); |
287 | } |
288 | |
289 | int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream) |
290 | { |
291 | return bcm2835_audio_send_simple(instance: alsa_stream->instance, |
292 | type: VC_AUDIO_MSG_TYPE_START, wait: false); |
293 | } |
294 | |
295 | int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream) |
296 | { |
297 | return bcm2835_audio_send_simple(instance: alsa_stream->instance, |
298 | type: VC_AUDIO_MSG_TYPE_STOP, wait: false); |
299 | } |
300 | |
301 | /* FIXME: this doesn't seem working as expected for "draining" */ |
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(instance: alsa_stream->instance, m: &m, wait: false); |
310 | } |
311 | |
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(instance: alsa_stream->instance, |
318 | type: VC_AUDIO_MSG_TYPE_CLOSE, wait: true); |
319 | |
320 | /* Stop the audio service */ |
321 | vc_vchi_audio_deinit(instance); |
322 | alsa_stream->instance = NULL; |
323 | kfree(objp: instance); |
324 | |
325 | return err; |
326 | } |
327 | |
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: &m, wait: 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(instance: vchiq_instance, service: instance->service_handle, data: src, size: count, |
356 | NULL, mode: 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(instance: vchiq_instance, |
362 | handle: instance->service_handle, data: src, size: 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 | |