1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * APBridge ALSA SoC dummy codec driver |
4 | * Copyright 2016 Google Inc. |
5 | * Copyright 2016 Linaro Ltd. |
6 | */ |
7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> |
9 | #include <linux/pm_runtime.h> |
10 | #include <sound/soc.h> |
11 | #include <sound/pcm_params.h> |
12 | #include <uapi/linux/input.h> |
13 | |
14 | #include "audio_codec.h" |
15 | #include "audio_apbridgea.h" |
16 | #include "audio_manager.h" |
17 | #include "audio_helper.h" |
18 | |
19 | static struct gbaudio_codec_info *gbcodec; |
20 | |
21 | static struct gbaudio_data_connection * |
22 | find_data(struct gbaudio_module_info *module, int id) |
23 | { |
24 | struct gbaudio_data_connection *data; |
25 | |
26 | list_for_each_entry(data, &module->data_list, list) { |
27 | if (id == data->id) |
28 | return data; |
29 | } |
30 | return NULL; |
31 | } |
32 | |
33 | static struct gbaudio_stream_params * |
34 | find_dai_stream_params(struct gbaudio_codec_info *codec, int id, int stream) |
35 | { |
36 | struct gbaudio_codec_dai *dai; |
37 | |
38 | list_for_each_entry(dai, &codec->dai_list, list) { |
39 | if (dai->id == id) |
40 | return &dai->params[stream]; |
41 | } |
42 | return NULL; |
43 | } |
44 | |
45 | static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, |
46 | struct gbaudio_module_info *module, int id) |
47 | { |
48 | int module_state, ret = 0; |
49 | u16 data_cport, i2s_port, cportid; |
50 | u8 sig_bits, channels; |
51 | u32 format, rate; |
52 | struct gbaudio_data_connection *data; |
53 | struct gbaudio_stream_params *params; |
54 | |
55 | /* find the dai */ |
56 | data = find_data(module, id); |
57 | if (!data) { |
58 | dev_err(module->dev, "%d:DATA connection missing\n" , id); |
59 | return -ENODEV; |
60 | } |
61 | module_state = data->state[SNDRV_PCM_STREAM_PLAYBACK]; |
62 | |
63 | params = find_dai_stream_params(codec, id, stream: SNDRV_PCM_STREAM_PLAYBACK); |
64 | if (!params) { |
65 | dev_err(codec->dev, "Failed to fetch dai_stream pointer\n" ); |
66 | return -EINVAL; |
67 | } |
68 | |
69 | /* register cport */ |
70 | if (module_state < GBAUDIO_CODEC_STARTUP) { |
71 | i2s_port = 0; /* fixed for now */ |
72 | cportid = data->connection->hd_cport_id; |
73 | ret = gb_audio_apbridgea_register_cport(connection: data->connection, |
74 | i2s_port, cportid, |
75 | AUDIO_APBRIDGEA_DIRECTION_TX); |
76 | if (ret) { |
77 | dev_err_ratelimited(module->dev, "reg_cport failed:%d\n" , ret); |
78 | return ret; |
79 | } |
80 | data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_STARTUP; |
81 | dev_dbg(module->dev, "Dynamic Register %d DAI\n" , cportid); |
82 | } |
83 | |
84 | /* hw_params */ |
85 | if (module_state < GBAUDIO_CODEC_HWPARAMS) { |
86 | format = params->format; |
87 | channels = params->channels; |
88 | rate = params->rate; |
89 | sig_bits = params->sig_bits; |
90 | data_cport = data->connection->intf_cport_id; |
91 | ret = gb_audio_gb_set_pcm(connection: module->mgmt_connection, data_cport, |
92 | format, rate, channels, sig_bits); |
93 | if (ret) { |
94 | dev_err_ratelimited(module->dev, "set_pcm failed:%d\n" , ret); |
95 | return ret; |
96 | } |
97 | data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_HWPARAMS; |
98 | dev_dbg(module->dev, "Dynamic hw_params %d DAI\n" , data_cport); |
99 | } |
100 | |
101 | /* prepare */ |
102 | if (module_state < GBAUDIO_CODEC_PREPARE) { |
103 | data_cport = data->connection->intf_cport_id; |
104 | ret = gb_audio_gb_set_tx_data_size(connection: module->mgmt_connection, |
105 | data_cport, size: 192); |
106 | if (ret) { |
107 | dev_err_ratelimited(module->dev, |
108 | "set_tx_data_size failed:%d\n" , |
109 | ret); |
110 | return ret; |
111 | } |
112 | ret = gb_audio_gb_activate_tx(connection: module->mgmt_connection, data_cport); |
113 | if (ret) { |
114 | dev_err_ratelimited(module->dev, |
115 | "activate_tx failed:%d\n" , ret); |
116 | return ret; |
117 | } |
118 | data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_PREPARE; |
119 | dev_dbg(module->dev, "Dynamic prepare %d DAI\n" , data_cport); |
120 | } |
121 | |
122 | return 0; |
123 | } |
124 | |
125 | static int gbaudio_module_disable_tx(struct gbaudio_module_info *module, int id) |
126 | { |
127 | int ret; |
128 | u16 data_cport, cportid, i2s_port; |
129 | int module_state; |
130 | struct gbaudio_data_connection *data; |
131 | |
132 | /* find the dai */ |
133 | data = find_data(module, id); |
134 | if (!data) { |
135 | dev_err(module->dev, "%d:DATA connection missing\n" , id); |
136 | return -ENODEV; |
137 | } |
138 | module_state = data->state[SNDRV_PCM_STREAM_PLAYBACK]; |
139 | |
140 | if (module_state > GBAUDIO_CODEC_HWPARAMS) { |
141 | data_cport = data->connection->intf_cport_id; |
142 | ret = gb_audio_gb_deactivate_tx(connection: module->mgmt_connection, |
143 | data_cport); |
144 | if (ret) { |
145 | dev_err_ratelimited(module->dev, |
146 | "deactivate_tx failed:%d\n" , ret); |
147 | return ret; |
148 | } |
149 | dev_dbg(module->dev, "Dynamic deactivate %d DAI\n" , data_cport); |
150 | data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_HWPARAMS; |
151 | } |
152 | |
153 | if (module_state > GBAUDIO_CODEC_SHUTDOWN) { |
154 | i2s_port = 0; /* fixed for now */ |
155 | cportid = data->connection->hd_cport_id; |
156 | ret = gb_audio_apbridgea_unregister_cport(connection: data->connection, |
157 | i2s_port, cportid, |
158 | AUDIO_APBRIDGEA_DIRECTION_TX); |
159 | if (ret) { |
160 | dev_err_ratelimited(module->dev, |
161 | "unregister_cport failed:%d\n" , ret); |
162 | return ret; |
163 | } |
164 | dev_dbg(module->dev, "Dynamic Unregister %d DAI\n" , cportid); |
165 | data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_SHUTDOWN; |
166 | } |
167 | |
168 | return 0; |
169 | } |
170 | |
171 | static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, |
172 | struct gbaudio_module_info *module, int id) |
173 | { |
174 | int module_state, ret = 0; |
175 | u16 data_cport, i2s_port, cportid; |
176 | u8 sig_bits, channels; |
177 | u32 format, rate; |
178 | struct gbaudio_data_connection *data; |
179 | struct gbaudio_stream_params *params; |
180 | |
181 | /* find the dai */ |
182 | data = find_data(module, id); |
183 | if (!data) { |
184 | dev_err(module->dev, "%d:DATA connection missing\n" , id); |
185 | return -ENODEV; |
186 | } |
187 | module_state = data->state[SNDRV_PCM_STREAM_CAPTURE]; |
188 | |
189 | params = find_dai_stream_params(codec, id, stream: SNDRV_PCM_STREAM_CAPTURE); |
190 | if (!params) { |
191 | dev_err(codec->dev, "Failed to fetch dai_stream pointer\n" ); |
192 | return -EINVAL; |
193 | } |
194 | |
195 | /* register cport */ |
196 | if (module_state < GBAUDIO_CODEC_STARTUP) { |
197 | i2s_port = 0; /* fixed for now */ |
198 | cportid = data->connection->hd_cport_id; |
199 | ret = gb_audio_apbridgea_register_cport(connection: data->connection, |
200 | i2s_port, cportid, |
201 | AUDIO_APBRIDGEA_DIRECTION_RX); |
202 | if (ret) { |
203 | dev_err_ratelimited(module->dev, "reg_cport failed:%d\n" , ret); |
204 | return ret; |
205 | } |
206 | data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_STARTUP; |
207 | dev_dbg(module->dev, "Dynamic Register %d DAI\n" , cportid); |
208 | } |
209 | |
210 | /* hw_params */ |
211 | if (module_state < GBAUDIO_CODEC_HWPARAMS) { |
212 | format = params->format; |
213 | channels = params->channels; |
214 | rate = params->rate; |
215 | sig_bits = params->sig_bits; |
216 | data_cport = data->connection->intf_cport_id; |
217 | ret = gb_audio_gb_set_pcm(connection: module->mgmt_connection, data_cport, |
218 | format, rate, channels, sig_bits); |
219 | if (ret) { |
220 | dev_err_ratelimited(module->dev, "set_pcm failed:%d\n" , ret); |
221 | return ret; |
222 | } |
223 | data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_HWPARAMS; |
224 | dev_dbg(module->dev, "Dynamic hw_params %d DAI\n" , data_cport); |
225 | } |
226 | |
227 | /* prepare */ |
228 | if (module_state < GBAUDIO_CODEC_PREPARE) { |
229 | data_cport = data->connection->intf_cport_id; |
230 | ret = gb_audio_gb_set_rx_data_size(connection: module->mgmt_connection, |
231 | data_cport, size: 192); |
232 | if (ret) { |
233 | dev_err_ratelimited(module->dev, |
234 | "set_rx_data_size failed:%d\n" , |
235 | ret); |
236 | return ret; |
237 | } |
238 | ret = gb_audio_gb_activate_rx(connection: module->mgmt_connection, |
239 | data_cport); |
240 | if (ret) { |
241 | dev_err_ratelimited(module->dev, |
242 | "activate_rx failed:%d\n" , ret); |
243 | return ret; |
244 | } |
245 | data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_PREPARE; |
246 | dev_dbg(module->dev, "Dynamic prepare %d DAI\n" , data_cport); |
247 | } |
248 | |
249 | return 0; |
250 | } |
251 | |
252 | static int gbaudio_module_disable_rx(struct gbaudio_module_info *module, int id) |
253 | { |
254 | int ret; |
255 | u16 data_cport, cportid, i2s_port; |
256 | int module_state; |
257 | struct gbaudio_data_connection *data; |
258 | |
259 | /* find the dai */ |
260 | data = find_data(module, id); |
261 | if (!data) { |
262 | dev_err(module->dev, "%d:DATA connection missing\n" , id); |
263 | return -ENODEV; |
264 | } |
265 | module_state = data->state[SNDRV_PCM_STREAM_CAPTURE]; |
266 | |
267 | if (module_state > GBAUDIO_CODEC_HWPARAMS) { |
268 | data_cport = data->connection->intf_cport_id; |
269 | ret = gb_audio_gb_deactivate_rx(connection: module->mgmt_connection, |
270 | data_cport); |
271 | if (ret) { |
272 | dev_err_ratelimited(module->dev, |
273 | "deactivate_rx failed:%d\n" , ret); |
274 | return ret; |
275 | } |
276 | dev_dbg(module->dev, "Dynamic deactivate %d DAI\n" , data_cport); |
277 | data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_HWPARAMS; |
278 | } |
279 | |
280 | if (module_state > GBAUDIO_CODEC_SHUTDOWN) { |
281 | i2s_port = 0; /* fixed for now */ |
282 | cportid = data->connection->hd_cport_id; |
283 | ret = gb_audio_apbridgea_unregister_cport(connection: data->connection, |
284 | i2s_port, cportid, |
285 | AUDIO_APBRIDGEA_DIRECTION_RX); |
286 | if (ret) { |
287 | dev_err_ratelimited(module->dev, |
288 | "unregister_cport failed:%d\n" , ret); |
289 | return ret; |
290 | } |
291 | dev_dbg(module->dev, "Dynamic Unregister %d DAI\n" , cportid); |
292 | data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_SHUTDOWN; |
293 | } |
294 | |
295 | return 0; |
296 | } |
297 | |
298 | int gbaudio_module_update(struct gbaudio_codec_info *codec, |
299 | struct snd_soc_dapm_widget *w, |
300 | struct gbaudio_module_info *module, int enable) |
301 | { |
302 | int dai_id, ret; |
303 | char intf_name[NAME_SIZE], dir[NAME_SIZE]; |
304 | |
305 | dev_dbg(module->dev, "%s:Module update %s sequence\n" , w->name, |
306 | enable ? "Enable" : "Disable" ); |
307 | |
308 | if ((w->id != snd_soc_dapm_aif_in) && (w->id != snd_soc_dapm_aif_out)) { |
309 | dev_dbg(codec->dev, "No action required for %s\n" , w->name); |
310 | return 0; |
311 | } |
312 | |
313 | /* parse dai_id from AIF widget's stream_name */ |
314 | ret = sscanf(w->sname, "%s %d %s" , intf_name, &dai_id, dir); |
315 | if (ret < 3) { |
316 | dev_err(codec->dev, "Error while parsing dai_id for %s\n" , w->name); |
317 | return -EINVAL; |
318 | } |
319 | |
320 | mutex_lock(&codec->lock); |
321 | if (w->id == snd_soc_dapm_aif_in) { |
322 | if (enable) |
323 | ret = gbaudio_module_enable_tx(codec, module, id: dai_id); |
324 | else |
325 | ret = gbaudio_module_disable_tx(module, id: dai_id); |
326 | } else if (w->id == snd_soc_dapm_aif_out) { |
327 | if (enable) |
328 | ret = gbaudio_module_enable_rx(codec, module, id: dai_id); |
329 | else |
330 | ret = gbaudio_module_disable_rx(module, id: dai_id); |
331 | } |
332 | |
333 | mutex_unlock(lock: &codec->lock); |
334 | |
335 | return ret; |
336 | } |
337 | EXPORT_SYMBOL(gbaudio_module_update); |
338 | |
339 | /* |
340 | * codec DAI ops |
341 | */ |
342 | static int gbcodec_startup(struct snd_pcm_substream *substream, |
343 | struct snd_soc_dai *dai) |
344 | { |
345 | struct gbaudio_codec_info *codec = dev_get_drvdata(dev: dai->dev); |
346 | struct gbaudio_stream_params *params; |
347 | |
348 | mutex_lock(&codec->lock); |
349 | |
350 | if (list_empty(head: &codec->module_list)) { |
351 | dev_err(codec->dev, "No codec module available\n" ); |
352 | mutex_unlock(lock: &codec->lock); |
353 | return -ENODEV; |
354 | } |
355 | |
356 | params = find_dai_stream_params(codec, id: dai->id, stream: substream->stream); |
357 | if (!params) { |
358 | dev_err(codec->dev, "Failed to fetch dai_stream pointer\n" ); |
359 | mutex_unlock(lock: &codec->lock); |
360 | return -EINVAL; |
361 | } |
362 | params->state = GBAUDIO_CODEC_STARTUP; |
363 | mutex_unlock(lock: &codec->lock); |
364 | /* to prevent suspend in case of active audio */ |
365 | pm_stay_awake(dev: dai->dev); |
366 | |
367 | return 0; |
368 | } |
369 | |
370 | static void gbcodec_shutdown(struct snd_pcm_substream *substream, |
371 | struct snd_soc_dai *dai) |
372 | { |
373 | struct gbaudio_codec_info *codec = dev_get_drvdata(dev: dai->dev); |
374 | struct gbaudio_stream_params *params; |
375 | |
376 | mutex_lock(&codec->lock); |
377 | |
378 | if (list_empty(head: &codec->module_list)) |
379 | dev_info(codec->dev, "No codec module available during shutdown\n" ); |
380 | |
381 | params = find_dai_stream_params(codec, id: dai->id, stream: substream->stream); |
382 | if (!params) { |
383 | dev_err(codec->dev, "Failed to fetch dai_stream pointer\n" ); |
384 | mutex_unlock(lock: &codec->lock); |
385 | return; |
386 | } |
387 | params->state = GBAUDIO_CODEC_SHUTDOWN; |
388 | mutex_unlock(lock: &codec->lock); |
389 | pm_relax(dev: dai->dev); |
390 | } |
391 | |
392 | static int gbcodec_hw_params(struct snd_pcm_substream *substream, |
393 | struct snd_pcm_hw_params *hwparams, |
394 | struct snd_soc_dai *dai) |
395 | { |
396 | int ret; |
397 | u8 sig_bits, channels; |
398 | u32 format, rate; |
399 | struct gbaudio_module_info *module; |
400 | struct gbaudio_data_connection *data; |
401 | struct gb_bundle *bundle; |
402 | struct gbaudio_codec_info *codec = dev_get_drvdata(dev: dai->dev); |
403 | struct gbaudio_stream_params *params; |
404 | |
405 | mutex_lock(&codec->lock); |
406 | |
407 | if (list_empty(head: &codec->module_list)) { |
408 | dev_err(codec->dev, "No codec module available\n" ); |
409 | mutex_unlock(lock: &codec->lock); |
410 | return -ENODEV; |
411 | } |
412 | |
413 | /* |
414 | * assuming, currently only 48000 Hz, 16BIT_LE, stereo |
415 | * is supported, validate params before configuring codec |
416 | */ |
417 | if (params_channels(p: hwparams) != 2) { |
418 | dev_err(dai->dev, "Invalid channel count:%d\n" , |
419 | params_channels(hwparams)); |
420 | mutex_unlock(lock: &codec->lock); |
421 | return -EINVAL; |
422 | } |
423 | channels = params_channels(p: hwparams); |
424 | |
425 | if (params_rate(p: hwparams) != 48000) { |
426 | dev_err(dai->dev, "Invalid sampling rate:%d\n" , |
427 | params_rate(hwparams)); |
428 | mutex_unlock(lock: &codec->lock); |
429 | return -EINVAL; |
430 | } |
431 | rate = GB_AUDIO_PCM_RATE_48000; |
432 | |
433 | if (params_format(p: hwparams) != SNDRV_PCM_FORMAT_S16_LE) { |
434 | dev_err(dai->dev, "Invalid format:%d\n" , params_format(hwparams)); |
435 | mutex_unlock(lock: &codec->lock); |
436 | return -EINVAL; |
437 | } |
438 | format = GB_AUDIO_PCM_FMT_S16_LE; |
439 | |
440 | /* find the data connection */ |
441 | list_for_each_entry(module, &codec->module_list, list) { |
442 | data = find_data(module, id: dai->id); |
443 | if (data) |
444 | break; |
445 | } |
446 | |
447 | if (!data) { |
448 | dev_err(dai->dev, "DATA connection missing\n" ); |
449 | mutex_unlock(lock: &codec->lock); |
450 | return -EINVAL; |
451 | } |
452 | |
453 | params = find_dai_stream_params(codec, id: dai->id, stream: substream->stream); |
454 | if (!params) { |
455 | dev_err(codec->dev, "Failed to fetch dai_stream pointer\n" ); |
456 | mutex_unlock(lock: &codec->lock); |
457 | return -EINVAL; |
458 | } |
459 | |
460 | bundle = to_gb_bundle(module->dev); |
461 | ret = gb_pm_runtime_get_sync(bundle); |
462 | if (ret) { |
463 | mutex_unlock(lock: &codec->lock); |
464 | return ret; |
465 | } |
466 | |
467 | ret = gb_audio_apbridgea_set_config(connection: data->connection, i2s_port: 0, |
468 | AUDIO_APBRIDGEA_PCM_FMT_16, |
469 | AUDIO_APBRIDGEA_PCM_RATE_48000, |
470 | mclk_freq: 6144000); |
471 | if (ret) { |
472 | dev_err_ratelimited(dai->dev, "%d: Error during set_config\n" , |
473 | ret); |
474 | gb_pm_runtime_put_noidle(bundle); |
475 | mutex_unlock(lock: &codec->lock); |
476 | return ret; |
477 | } |
478 | |
479 | gb_pm_runtime_put_noidle(bundle); |
480 | |
481 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
482 | sig_bits = dai->driver->playback.sig_bits; |
483 | else |
484 | sig_bits = dai->driver->capture.sig_bits; |
485 | |
486 | params->state = GBAUDIO_CODEC_HWPARAMS; |
487 | params->format = format; |
488 | params->rate = rate; |
489 | params->channels = channels; |
490 | params->sig_bits = sig_bits; |
491 | |
492 | mutex_unlock(lock: &codec->lock); |
493 | return 0; |
494 | } |
495 | |
496 | static int gbcodec_prepare(struct snd_pcm_substream *substream, |
497 | struct snd_soc_dai *dai) |
498 | { |
499 | int ret; |
500 | struct gbaudio_module_info *module = NULL, *iter; |
501 | struct gbaudio_data_connection *data; |
502 | struct gb_bundle *bundle; |
503 | struct gbaudio_codec_info *codec = dev_get_drvdata(dev: dai->dev); |
504 | struct gbaudio_stream_params *params; |
505 | |
506 | mutex_lock(&codec->lock); |
507 | |
508 | if (list_empty(head: &codec->module_list)) { |
509 | dev_err(codec->dev, "No codec module available\n" ); |
510 | mutex_unlock(lock: &codec->lock); |
511 | return -ENODEV; |
512 | } |
513 | |
514 | list_for_each_entry(iter, &codec->module_list, list) { |
515 | /* find the dai */ |
516 | data = find_data(module: iter, id: dai->id); |
517 | if (data) { |
518 | module = iter; |
519 | break; |
520 | } |
521 | } |
522 | if (!data) { |
523 | dev_err(dai->dev, "DATA connection missing\n" ); |
524 | mutex_unlock(lock: &codec->lock); |
525 | return -ENODEV; |
526 | } |
527 | |
528 | params = find_dai_stream_params(codec, id: dai->id, stream: substream->stream); |
529 | if (!params) { |
530 | dev_err(codec->dev, "Failed to fetch dai_stream pointer\n" ); |
531 | mutex_unlock(lock: &codec->lock); |
532 | return -EINVAL; |
533 | } |
534 | |
535 | bundle = to_gb_bundle(module->dev); |
536 | ret = gb_pm_runtime_get_sync(bundle); |
537 | if (ret) { |
538 | mutex_unlock(lock: &codec->lock); |
539 | return ret; |
540 | } |
541 | |
542 | switch (substream->stream) { |
543 | case SNDRV_PCM_STREAM_PLAYBACK: |
544 | ret = gb_audio_apbridgea_set_tx_data_size(connection: data->connection, i2s_port: 0, size: 192); |
545 | break; |
546 | case SNDRV_PCM_STREAM_CAPTURE: |
547 | ret = gb_audio_apbridgea_set_rx_data_size(connection: data->connection, i2s_port: 0, size: 192); |
548 | break; |
549 | } |
550 | if (ret) { |
551 | gb_pm_runtime_put_noidle(bundle); |
552 | mutex_unlock(lock: &codec->lock); |
553 | dev_err_ratelimited(dai->dev, "set_data_size failed:%d\n" , ret); |
554 | return ret; |
555 | } |
556 | |
557 | gb_pm_runtime_put_noidle(bundle); |
558 | |
559 | params->state = GBAUDIO_CODEC_PREPARE; |
560 | mutex_unlock(lock: &codec->lock); |
561 | return 0; |
562 | } |
563 | |
564 | static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) |
565 | { |
566 | int ret; |
567 | struct gbaudio_data_connection *data; |
568 | struct gbaudio_module_info *module = NULL, *iter; |
569 | struct gb_bundle *bundle; |
570 | struct gbaudio_codec_info *codec = dev_get_drvdata(dev: dai->dev); |
571 | struct gbaudio_stream_params *params; |
572 | |
573 | dev_dbg(dai->dev, "Mute:%d, Direction:%s\n" , mute, |
574 | stream ? "CAPTURE" : "PLAYBACK" ); |
575 | |
576 | mutex_lock(&codec->lock); |
577 | |
578 | params = find_dai_stream_params(codec, id: dai->id, stream); |
579 | if (!params) { |
580 | dev_err(codec->dev, "Failed to fetch dai_stream pointer\n" ); |
581 | mutex_unlock(lock: &codec->lock); |
582 | return -EINVAL; |
583 | } |
584 | |
585 | if (list_empty(head: &codec->module_list)) { |
586 | dev_err(codec->dev, "No codec module available\n" ); |
587 | if (mute) { |
588 | params->state = GBAUDIO_CODEC_STOP; |
589 | ret = 0; |
590 | } else { |
591 | ret = -ENODEV; |
592 | } |
593 | mutex_unlock(lock: &codec->lock); |
594 | return ret; |
595 | } |
596 | |
597 | list_for_each_entry(iter, &codec->module_list, list) { |
598 | /* find the dai */ |
599 | data = find_data(module: iter, id: dai->id); |
600 | if (data) { |
601 | module = iter; |
602 | break; |
603 | } |
604 | } |
605 | if (!data) { |
606 | dev_err(dai->dev, "%s DATA connection missing\n" , |
607 | dai->name); |
608 | mutex_unlock(lock: &codec->lock); |
609 | return -ENODEV; |
610 | } |
611 | |
612 | bundle = to_gb_bundle(module->dev); |
613 | ret = gb_pm_runtime_get_sync(bundle); |
614 | if (ret) { |
615 | mutex_unlock(lock: &codec->lock); |
616 | return ret; |
617 | } |
618 | |
619 | if (!mute && !stream) {/* start playback */ |
620 | ret = gb_audio_apbridgea_prepare_tx(connection: data->connection, i2s_port: 0); |
621 | if (!ret) |
622 | ret = gb_audio_apbridgea_start_tx(connection: data->connection, i2s_port: 0, timestamp: 0); |
623 | params->state = GBAUDIO_CODEC_START; |
624 | } else if (!mute && stream) {/* start capture */ |
625 | ret = gb_audio_apbridgea_prepare_rx(connection: data->connection, i2s_port: 0); |
626 | if (!ret) |
627 | ret = gb_audio_apbridgea_start_rx(connection: data->connection, i2s_port: 0); |
628 | params->state = GBAUDIO_CODEC_START; |
629 | } else if (mute && !stream) {/* stop playback */ |
630 | ret = gb_audio_apbridgea_stop_tx(connection: data->connection, i2s_port: 0); |
631 | if (!ret) |
632 | ret = gb_audio_apbridgea_shutdown_tx(connection: data->connection, i2s_port: 0); |
633 | params->state = GBAUDIO_CODEC_STOP; |
634 | } else if (mute && stream) {/* stop capture */ |
635 | ret = gb_audio_apbridgea_stop_rx(connection: data->connection, i2s_port: 0); |
636 | if (!ret) |
637 | ret = gb_audio_apbridgea_shutdown_rx(connection: data->connection, i2s_port: 0); |
638 | params->state = GBAUDIO_CODEC_STOP; |
639 | } else { |
640 | ret = -EINVAL; |
641 | } |
642 | |
643 | if (ret) |
644 | dev_err_ratelimited(dai->dev, |
645 | "%s:Error during %s %s stream:%d\n" , |
646 | module->name, mute ? "Mute" : "Unmute" , |
647 | stream ? "Capture" : "Playback" , ret); |
648 | |
649 | gb_pm_runtime_put_noidle(bundle); |
650 | mutex_unlock(lock: &codec->lock); |
651 | return ret; |
652 | } |
653 | |
654 | static const struct snd_soc_dai_ops gbcodec_dai_ops = { |
655 | .startup = gbcodec_startup, |
656 | .shutdown = gbcodec_shutdown, |
657 | .hw_params = gbcodec_hw_params, |
658 | .prepare = gbcodec_prepare, |
659 | .mute_stream = gbcodec_mute_stream, |
660 | }; |
661 | |
662 | static struct snd_soc_dai_driver gbaudio_dai[] = { |
663 | { |
664 | .name = "apb-i2s0" , |
665 | .id = 0, |
666 | .playback = { |
667 | .stream_name = "I2S 0 Playback" , |
668 | .rates = SNDRV_PCM_RATE_48000, |
669 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
670 | .rate_max = 48000, |
671 | .rate_min = 48000, |
672 | .channels_min = 1, |
673 | .channels_max = 2, |
674 | .sig_bits = 16, |
675 | }, |
676 | .capture = { |
677 | .stream_name = "I2S 0 Capture" , |
678 | .rates = SNDRV_PCM_RATE_48000, |
679 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
680 | .rate_max = 48000, |
681 | .rate_min = 48000, |
682 | .channels_min = 1, |
683 | .channels_max = 2, |
684 | .sig_bits = 16, |
685 | }, |
686 | .ops = &gbcodec_dai_ops, |
687 | }, |
688 | }; |
689 | |
690 | static int gbaudio_init_jack(struct gbaudio_module_info *module, |
691 | struct snd_soc_card *card) |
692 | { |
693 | int ret; |
694 | struct gbaudio_jack *jack, *n; |
695 | struct snd_soc_jack_pin *headset, *button; |
696 | |
697 | if (!module->jack_mask) |
698 | return 0; |
699 | |
700 | snprintf(buf: module->jack_name, NAME_SIZE, fmt: "GB %d Headset Jack" , |
701 | module->dev_id); |
702 | |
703 | headset = devm_kzalloc(dev: module->dev, size: sizeof(*headset), GFP_KERNEL); |
704 | if (!headset) |
705 | return -ENOMEM; |
706 | |
707 | headset->pin = module->jack_name; |
708 | headset->mask = module->jack_mask; |
709 | ret = snd_soc_card_jack_new_pins(card, id: module->jack_name, |
710 | type: module->jack_mask, |
711 | jack: &module->headset.jack, pins: headset, num_pins: 1); |
712 | if (ret) { |
713 | dev_err(module->dev, "Failed to create new jack\n" ); |
714 | return ret; |
715 | } |
716 | |
717 | /* Add to module's jack list */ |
718 | list_add(new: &module->headset.list, head: &module->jack_list); |
719 | |
720 | if (!module->button_mask) |
721 | return 0; |
722 | |
723 | snprintf(buf: module->button_name, NAME_SIZE, fmt: "GB %d Button Jack" , |
724 | module->dev_id); |
725 | button = devm_kzalloc(dev: module->dev, size: sizeof(*button), GFP_KERNEL); |
726 | if (!button) { |
727 | ret = -ENOMEM; |
728 | goto free_jacks; |
729 | } |
730 | |
731 | button->pin = module->button_name; |
732 | button->mask = module->button_mask; |
733 | ret = snd_soc_card_jack_new_pins(card, id: module->button_name, |
734 | type: module->button_mask, |
735 | jack: &module->button.jack, |
736 | pins: button, num_pins: 1); |
737 | if (ret) { |
738 | dev_err(module->dev, "Failed to create button jack\n" ); |
739 | goto free_jacks; |
740 | } |
741 | |
742 | /* Add to module's jack list */ |
743 | list_add(new: &module->button.list, head: &module->jack_list); |
744 | |
745 | /* |
746 | * Currently, max 4 buttons are supported with following key mapping |
747 | * BTN_0 = KEY_MEDIA |
748 | * BTN_1 = KEY_VOICECOMMAND |
749 | * BTN_2 = KEY_VOLUMEUP |
750 | * BTN_3 = KEY_VOLUMEDOWN |
751 | */ |
752 | |
753 | if (module->button_mask & SND_JACK_BTN_0) { |
754 | ret = snd_jack_set_key(jack: module->button.jack.jack, type: SND_JACK_BTN_0, |
755 | KEY_MEDIA); |
756 | if (ret) { |
757 | dev_err(module->dev, "Failed to set BTN_0\n" ); |
758 | goto free_jacks; |
759 | } |
760 | } |
761 | |
762 | if (module->button_mask & SND_JACK_BTN_1) { |
763 | ret = snd_jack_set_key(jack: module->button.jack.jack, type: SND_JACK_BTN_1, |
764 | KEY_VOICECOMMAND); |
765 | if (ret) { |
766 | dev_err(module->dev, "Failed to set BTN_1\n" ); |
767 | goto free_jacks; |
768 | } |
769 | } |
770 | |
771 | if (module->button_mask & SND_JACK_BTN_2) { |
772 | ret = snd_jack_set_key(jack: module->button.jack.jack, type: SND_JACK_BTN_2, |
773 | KEY_VOLUMEUP); |
774 | if (ret) { |
775 | dev_err(module->dev, "Failed to set BTN_2\n" ); |
776 | goto free_jacks; |
777 | } |
778 | } |
779 | |
780 | if (module->button_mask & SND_JACK_BTN_3) { |
781 | ret = snd_jack_set_key(jack: module->button.jack.jack, type: SND_JACK_BTN_3, |
782 | KEY_VOLUMEDOWN); |
783 | if (ret) { |
784 | dev_err(module->dev, "Failed to set BTN_0\n" ); |
785 | goto free_jacks; |
786 | } |
787 | } |
788 | |
789 | /* FIXME |
790 | * verify if this is really required |
791 | set_bit(INPUT_PROP_NO_DUMMY_RELEASE, |
792 | module->button.jack.jack->input_dev->propbit); |
793 | */ |
794 | |
795 | return 0; |
796 | |
797 | free_jacks: |
798 | list_for_each_entry_safe(jack, n, &module->jack_list, list) { |
799 | snd_device_free(card: card->snd_card, device_data: jack->jack.jack); |
800 | list_del(entry: &jack->list); |
801 | } |
802 | |
803 | return ret; |
804 | } |
805 | |
806 | int gbaudio_register_module(struct gbaudio_module_info *module) |
807 | { |
808 | int ret; |
809 | struct snd_soc_component *comp; |
810 | struct gbaudio_jack *jack = NULL; |
811 | |
812 | if (!gbcodec) { |
813 | dev_err(module->dev, "GB Codec not yet probed\n" ); |
814 | return -EAGAIN; |
815 | } |
816 | |
817 | comp = gbcodec->component; |
818 | |
819 | mutex_lock(&gbcodec->register_mutex); |
820 | |
821 | if (module->num_dais) { |
822 | dev_err(gbcodec->dev, |
823 | "%d:DAIs not supported via gbcodec driver\n" , |
824 | module->num_dais); |
825 | mutex_unlock(lock: &gbcodec->register_mutex); |
826 | return -EINVAL; |
827 | } |
828 | |
829 | ret = gbaudio_init_jack(module, card: comp->card); |
830 | if (ret) { |
831 | mutex_unlock(lock: &gbcodec->register_mutex); |
832 | return ret; |
833 | } |
834 | |
835 | if (module->dapm_widgets) |
836 | snd_soc_dapm_new_controls(dapm: &comp->dapm, widget: module->dapm_widgets, |
837 | num: module->num_dapm_widgets); |
838 | if (module->controls) |
839 | snd_soc_add_component_controls(component: comp, controls: module->controls, |
840 | num_controls: module->num_controls); |
841 | if (module->dapm_routes) |
842 | snd_soc_dapm_add_routes(dapm: &comp->dapm, route: module->dapm_routes, |
843 | num: module->num_dapm_routes); |
844 | |
845 | /* card already instantiated, create widgets here only */ |
846 | if (comp->card->instantiated) { |
847 | gbaudio_dapm_link_component_dai_widgets(card: comp->card, dapm: &comp->dapm); |
848 | #ifdef CONFIG_SND_JACK |
849 | /* |
850 | * register jack devices for this module |
851 | * from codec->jack_list |
852 | */ |
853 | list_for_each_entry(jack, &module->jack_list, list) { |
854 | snd_device_register(card: comp->card->snd_card, |
855 | device_data: jack->jack.jack); |
856 | } |
857 | #endif |
858 | } |
859 | |
860 | mutex_lock(&gbcodec->lock); |
861 | list_add(new: &module->list, head: &gbcodec->module_list); |
862 | mutex_unlock(lock: &gbcodec->lock); |
863 | |
864 | if (comp->card->instantiated) |
865 | ret = snd_soc_dapm_new_widgets(card: comp->card); |
866 | dev_dbg(comp->dev, "Registered %s module\n" , module->name); |
867 | |
868 | mutex_unlock(lock: &gbcodec->register_mutex); |
869 | return ret; |
870 | } |
871 | EXPORT_SYMBOL(gbaudio_register_module); |
872 | |
873 | static void gbaudio_codec_clean_data_tx(struct gbaudio_data_connection *data) |
874 | { |
875 | u16 i2s_port, cportid; |
876 | int ret; |
877 | |
878 | if (list_is_singular(head: &gbcodec->module_list)) { |
879 | ret = gb_audio_apbridgea_stop_tx(connection: data->connection, i2s_port: 0); |
880 | if (ret) |
881 | return; |
882 | ret = gb_audio_apbridgea_shutdown_tx(connection: data->connection, i2s_port: 0); |
883 | if (ret) |
884 | return; |
885 | } |
886 | i2s_port = 0; /* fixed for now */ |
887 | cportid = data->connection->hd_cport_id; |
888 | ret = gb_audio_apbridgea_unregister_cport(connection: data->connection, |
889 | i2s_port, cportid, |
890 | AUDIO_APBRIDGEA_DIRECTION_TX); |
891 | data->state[0] = GBAUDIO_CODEC_SHUTDOWN; |
892 | } |
893 | |
894 | static void gbaudio_codec_clean_data_rx(struct gbaudio_data_connection *data) |
895 | { |
896 | u16 i2s_port, cportid; |
897 | int ret; |
898 | |
899 | if (list_is_singular(head: &gbcodec->module_list)) { |
900 | ret = gb_audio_apbridgea_stop_rx(connection: data->connection, i2s_port: 0); |
901 | if (ret) |
902 | return; |
903 | ret = gb_audio_apbridgea_shutdown_rx(connection: data->connection, i2s_port: 0); |
904 | if (ret) |
905 | return; |
906 | } |
907 | i2s_port = 0; /* fixed for now */ |
908 | cportid = data->connection->hd_cport_id; |
909 | ret = gb_audio_apbridgea_unregister_cport(connection: data->connection, |
910 | i2s_port, cportid, |
911 | AUDIO_APBRIDGEA_DIRECTION_RX); |
912 | data->state[1] = GBAUDIO_CODEC_SHUTDOWN; |
913 | } |
914 | |
915 | static void gbaudio_codec_cleanup(struct gbaudio_module_info *module) |
916 | { |
917 | struct gbaudio_data_connection *data; |
918 | int pb_state, cap_state; |
919 | |
920 | dev_dbg(gbcodec->dev, "%s: removed, cleanup APBridge\n" , module->name); |
921 | list_for_each_entry(data, &module->data_list, list) { |
922 | pb_state = data->state[0]; |
923 | cap_state = data->state[1]; |
924 | |
925 | if (pb_state > GBAUDIO_CODEC_SHUTDOWN) |
926 | gbaudio_codec_clean_data_tx(data); |
927 | |
928 | if (cap_state > GBAUDIO_CODEC_SHUTDOWN) |
929 | gbaudio_codec_clean_data_rx(data); |
930 | } |
931 | } |
932 | |
933 | void gbaudio_unregister_module(struct gbaudio_module_info *module) |
934 | { |
935 | struct snd_soc_component *comp = gbcodec->component; |
936 | struct gbaudio_jack *jack, *n; |
937 | int mask; |
938 | |
939 | dev_dbg(comp->dev, "Unregister %s module\n" , module->name); |
940 | |
941 | mutex_lock(&gbcodec->register_mutex); |
942 | mutex_lock(&gbcodec->lock); |
943 | gbaudio_codec_cleanup(module); |
944 | list_del(entry: &module->list); |
945 | dev_dbg(comp->dev, "Process Unregister %s module\n" , module->name); |
946 | mutex_unlock(lock: &gbcodec->lock); |
947 | |
948 | #ifdef CONFIG_SND_JACK |
949 | /* free jack devices for this module jack_list */ |
950 | list_for_each_entry_safe(jack, n, &module->jack_list, list) { |
951 | if (jack == &module->headset) |
952 | mask = GBCODEC_JACK_MASK; |
953 | else if (jack == &module->button) |
954 | mask = GBCODEC_JACK_BUTTON_MASK; |
955 | else |
956 | mask = 0; |
957 | if (mask) { |
958 | dev_dbg(module->dev, "Report %s removal\n" , |
959 | jack->jack.jack->id); |
960 | snd_soc_jack_report(jack: &jack->jack, status: 0, mask); |
961 | snd_device_free(card: comp->card->snd_card, |
962 | device_data: jack->jack.jack); |
963 | list_del(entry: &jack->list); |
964 | } |
965 | } |
966 | #endif |
967 | |
968 | if (module->dapm_routes) { |
969 | dev_dbg(comp->dev, "Removing %d routes\n" , |
970 | module->num_dapm_routes); |
971 | snd_soc_dapm_del_routes(dapm: &comp->dapm, route: module->dapm_routes, |
972 | num: module->num_dapm_routes); |
973 | } |
974 | if (module->controls) { |
975 | dev_dbg(comp->dev, "Removing %d controls\n" , |
976 | module->num_controls); |
977 | /* release control semaphore */ |
978 | gbaudio_remove_component_controls(component: comp, controls: module->controls, |
979 | num_controls: module->num_controls); |
980 | } |
981 | if (module->dapm_widgets) { |
982 | dev_dbg(comp->dev, "Removing %d widgets\n" , |
983 | module->num_dapm_widgets); |
984 | gbaudio_dapm_free_controls(dapm: &comp->dapm, widget: module->dapm_widgets, |
985 | num: module->num_dapm_widgets); |
986 | } |
987 | |
988 | dev_dbg(comp->dev, "Unregistered %s module\n" , module->name); |
989 | |
990 | mutex_unlock(lock: &gbcodec->register_mutex); |
991 | } |
992 | EXPORT_SYMBOL(gbaudio_unregister_module); |
993 | |
994 | /* |
995 | * component driver ops |
996 | */ |
997 | static int gbcodec_probe(struct snd_soc_component *comp) |
998 | { |
999 | int i; |
1000 | struct gbaudio_codec_info *info; |
1001 | struct gbaudio_codec_dai *dai; |
1002 | |
1003 | info = devm_kzalloc(dev: comp->dev, size: sizeof(*info), GFP_KERNEL); |
1004 | if (!info) |
1005 | return -ENOMEM; |
1006 | |
1007 | info->dev = comp->dev; |
1008 | INIT_LIST_HEAD(list: &info->module_list); |
1009 | mutex_init(&info->lock); |
1010 | mutex_init(&info->register_mutex); |
1011 | INIT_LIST_HEAD(list: &info->dai_list); |
1012 | |
1013 | /* init dai_list used to maintain runtime stream info */ |
1014 | for (i = 0; i < ARRAY_SIZE(gbaudio_dai); i++) { |
1015 | dai = devm_kzalloc(dev: comp->dev, size: sizeof(*dai), GFP_KERNEL); |
1016 | if (!dai) |
1017 | return -ENOMEM; |
1018 | dai->id = gbaudio_dai[i].id; |
1019 | list_add(new: &dai->list, head: &info->dai_list); |
1020 | } |
1021 | |
1022 | info->component = comp; |
1023 | snd_soc_component_set_drvdata(c: comp, data: info); |
1024 | gbcodec = info; |
1025 | |
1026 | device_init_wakeup(dev: comp->dev, enable: 1); |
1027 | return 0; |
1028 | } |
1029 | |
1030 | static int gbcodec_write(struct snd_soc_component *comp, unsigned int reg, |
1031 | unsigned int value) |
1032 | { |
1033 | return 0; |
1034 | } |
1035 | |
1036 | static unsigned int gbcodec_read(struct snd_soc_component *comp, |
1037 | unsigned int reg) |
1038 | { |
1039 | return 0; |
1040 | } |
1041 | |
1042 | static const struct snd_soc_component_driver soc_codec_dev_gbaudio = { |
1043 | .probe = gbcodec_probe, |
1044 | .read = gbcodec_read, |
1045 | .write = gbcodec_write, |
1046 | }; |
1047 | |
1048 | #ifdef CONFIG_PM |
1049 | static int gbaudio_codec_suspend(struct device *dev) |
1050 | { |
1051 | dev_dbg(dev, "%s: suspend\n" , __func__); |
1052 | return 0; |
1053 | } |
1054 | |
1055 | static int gbaudio_codec_resume(struct device *dev) |
1056 | { |
1057 | dev_dbg(dev, "%s: resume\n" , __func__); |
1058 | return 0; |
1059 | } |
1060 | |
1061 | static const struct dev_pm_ops gbaudio_codec_pm_ops = { |
1062 | .suspend = gbaudio_codec_suspend, |
1063 | .resume = gbaudio_codec_resume, |
1064 | }; |
1065 | #endif |
1066 | |
1067 | static int gbaudio_codec_probe(struct platform_device *pdev) |
1068 | { |
1069 | return devm_snd_soc_register_component(dev: &pdev->dev, |
1070 | component_driver: &soc_codec_dev_gbaudio, |
1071 | dai_drv: gbaudio_dai, ARRAY_SIZE(gbaudio_dai)); |
1072 | } |
1073 | |
1074 | static const struct of_device_id greybus_asoc_machine_of_match[] = { |
1075 | { .compatible = "toshiba,apb-dummy-codec" , }, |
1076 | {}, |
1077 | }; |
1078 | |
1079 | static struct platform_driver gbaudio_codec_driver = { |
1080 | .driver = { |
1081 | .name = "apb-dummy-codec" , |
1082 | #ifdef CONFIG_PM |
1083 | .pm = &gbaudio_codec_pm_ops, |
1084 | #endif |
1085 | .of_match_table = greybus_asoc_machine_of_match, |
1086 | }, |
1087 | .probe = gbaudio_codec_probe, |
1088 | }; |
1089 | module_platform_driver(gbaudio_codec_driver); |
1090 | |
1091 | MODULE_DESCRIPTION("APBridge ALSA SoC dummy codec driver" ); |
1092 | MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>" ); |
1093 | MODULE_LICENSE("GPL v2" ); |
1094 | MODULE_ALIAS("platform:apb-dummy-codec" ); |
1095 | |