1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ALSA SoC WL1273 codec driver |
4 | * |
5 | * Author: Matti Aaltonen, <matti.j.aaltonen@nokia.com> |
6 | * |
7 | * Copyright: (C) 2010, 2011 Nokia Corporation |
8 | */ |
9 | |
10 | #include <linux/mfd/wl1273-core.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/module.h> |
13 | #include <sound/pcm.h> |
14 | #include <sound/pcm_params.h> |
15 | #include <sound/soc.h> |
16 | #include <sound/initval.h> |
17 | |
18 | #include "wl1273.h" |
19 | |
20 | enum wl1273_mode { WL1273_MODE_BT, WL1273_MODE_FM_RX, WL1273_MODE_FM_TX }; |
21 | |
22 | /* codec private data */ |
23 | struct wl1273_priv { |
24 | enum wl1273_mode mode; |
25 | struct wl1273_core *core; |
26 | unsigned int channels; |
27 | }; |
28 | |
29 | static int snd_wl1273_fm_set_i2s_mode(struct wl1273_core *core, |
30 | int rate, int width) |
31 | { |
32 | struct device *dev = &core->client->dev; |
33 | int r = 0; |
34 | u16 mode; |
35 | |
36 | dev_dbg(dev, "rate: %d\n" , rate); |
37 | dev_dbg(dev, "width: %d\n" , width); |
38 | |
39 | mutex_lock(&core->lock); |
40 | |
41 | mode = core->i2s_mode & ~WL1273_IS2_WIDTH & ~WL1273_IS2_RATE; |
42 | |
43 | switch (rate) { |
44 | case 48000: |
45 | mode |= WL1273_IS2_RATE_48K; |
46 | break; |
47 | case 44100: |
48 | mode |= WL1273_IS2_RATE_44_1K; |
49 | break; |
50 | case 32000: |
51 | mode |= WL1273_IS2_RATE_32K; |
52 | break; |
53 | case 22050: |
54 | mode |= WL1273_IS2_RATE_22_05K; |
55 | break; |
56 | case 16000: |
57 | mode |= WL1273_IS2_RATE_16K; |
58 | break; |
59 | case 12000: |
60 | mode |= WL1273_IS2_RATE_12K; |
61 | break; |
62 | case 11025: |
63 | mode |= WL1273_IS2_RATE_11_025; |
64 | break; |
65 | case 8000: |
66 | mode |= WL1273_IS2_RATE_8K; |
67 | break; |
68 | default: |
69 | dev_err(dev, "Sampling rate: %d not supported\n" , rate); |
70 | r = -EINVAL; |
71 | goto out; |
72 | } |
73 | |
74 | switch (width) { |
75 | case 16: |
76 | mode |= WL1273_IS2_WIDTH_32; |
77 | break; |
78 | case 20: |
79 | mode |= WL1273_IS2_WIDTH_40; |
80 | break; |
81 | case 24: |
82 | mode |= WL1273_IS2_WIDTH_48; |
83 | break; |
84 | case 25: |
85 | mode |= WL1273_IS2_WIDTH_50; |
86 | break; |
87 | case 30: |
88 | mode |= WL1273_IS2_WIDTH_60; |
89 | break; |
90 | case 32: |
91 | mode |= WL1273_IS2_WIDTH_64; |
92 | break; |
93 | case 40: |
94 | mode |= WL1273_IS2_WIDTH_80; |
95 | break; |
96 | case 48: |
97 | mode |= WL1273_IS2_WIDTH_96; |
98 | break; |
99 | case 64: |
100 | mode |= WL1273_IS2_WIDTH_128; |
101 | break; |
102 | default: |
103 | dev_err(dev, "Data width: %d not supported\n" , width); |
104 | r = -EINVAL; |
105 | goto out; |
106 | } |
107 | |
108 | dev_dbg(dev, "WL1273_I2S_DEF_MODE: 0x%04x\n" , WL1273_I2S_DEF_MODE); |
109 | dev_dbg(dev, "core->i2s_mode: 0x%04x\n" , core->i2s_mode); |
110 | dev_dbg(dev, "mode: 0x%04x\n" , mode); |
111 | |
112 | if (core->i2s_mode != mode) { |
113 | r = core->write(core, WL1273_I2S_MODE_CONFIG_SET, mode); |
114 | if (r) |
115 | goto out; |
116 | |
117 | core->i2s_mode = mode; |
118 | r = core->write(core, WL1273_AUDIO_ENABLE, |
119 | WL1273_AUDIO_ENABLE_I2S); |
120 | if (r) |
121 | goto out; |
122 | } |
123 | out: |
124 | mutex_unlock(lock: &core->lock); |
125 | |
126 | return r; |
127 | } |
128 | |
129 | static int snd_wl1273_fm_set_channel_number(struct wl1273_core *core, |
130 | int channel_number) |
131 | { |
132 | struct device *dev = &core->client->dev; |
133 | int r = 0; |
134 | |
135 | dev_dbg(dev, "%s\n" , __func__); |
136 | |
137 | mutex_lock(&core->lock); |
138 | |
139 | if (core->channel_number == channel_number) |
140 | goto out; |
141 | |
142 | if (channel_number == 1 && core->mode == WL1273_MODE_RX) |
143 | r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO); |
144 | else if (channel_number == 1 && core->mode == WL1273_MODE_TX) |
145 | r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO); |
146 | else if (channel_number == 2 && core->mode == WL1273_MODE_RX) |
147 | r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO); |
148 | else if (channel_number == 2 && core->mode == WL1273_MODE_TX) |
149 | r = core->write(core, WL1273_MONO_SET, WL1273_TX_STEREO); |
150 | else |
151 | r = -EINVAL; |
152 | out: |
153 | mutex_unlock(lock: &core->lock); |
154 | |
155 | return r; |
156 | } |
157 | |
158 | static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol, |
159 | struct snd_ctl_elem_value *ucontrol) |
160 | { |
161 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
162 | struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(c: component); |
163 | |
164 | ucontrol->value.enumerated.item[0] = wl1273->mode; |
165 | |
166 | return 0; |
167 | } |
168 | |
169 | /* |
170 | * TODO: Implement the audio routing in the driver. Now this control |
171 | * only indicates the setting that has been done elsewhere (in the user |
172 | * space). |
173 | */ |
174 | static const char * const wl1273_audio_route[] = { "Bt" , "FmRx" , "FmTx" }; |
175 | |
176 | static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, |
177 | struct snd_ctl_elem_value *ucontrol) |
178 | { |
179 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
180 | struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(c: component); |
181 | |
182 | if (wl1273->mode == ucontrol->value.enumerated.item[0]) |
183 | return 0; |
184 | |
185 | /* Do not allow changes while stream is running */ |
186 | if (snd_soc_component_active(component)) |
187 | return -EPERM; |
188 | |
189 | if (ucontrol->value.enumerated.item[0] >= ARRAY_SIZE(wl1273_audio_route)) |
190 | return -EINVAL; |
191 | |
192 | wl1273->mode = ucontrol->value.enumerated.item[0]; |
193 | |
194 | return 1; |
195 | } |
196 | |
197 | static SOC_ENUM_SINGLE_EXT_DECL(wl1273_enum, wl1273_audio_route); |
198 | |
199 | static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol, |
200 | struct snd_ctl_elem_value *ucontrol) |
201 | { |
202 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
203 | struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(c: component); |
204 | |
205 | dev_dbg(component->dev, "%s: enter.\n" , __func__); |
206 | |
207 | ucontrol->value.enumerated.item[0] = wl1273->core->audio_mode; |
208 | |
209 | return 0; |
210 | } |
211 | |
212 | static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol, |
213 | struct snd_ctl_elem_value *ucontrol) |
214 | { |
215 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
216 | struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(c: component); |
217 | int val, r = 0; |
218 | |
219 | dev_dbg(component->dev, "%s: enter.\n" , __func__); |
220 | |
221 | val = ucontrol->value.enumerated.item[0]; |
222 | if (wl1273->core->audio_mode == val) |
223 | return 0; |
224 | |
225 | r = wl1273->core->set_audio(wl1273->core, val); |
226 | if (r < 0) |
227 | return r; |
228 | |
229 | return 1; |
230 | } |
231 | |
232 | static const char * const wl1273_audio_strings[] = { "Digital" , "Analog" }; |
233 | |
234 | static SOC_ENUM_SINGLE_EXT_DECL(wl1273_audio_enum, wl1273_audio_strings); |
235 | |
236 | static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol, |
237 | struct snd_ctl_elem_value *ucontrol) |
238 | { |
239 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
240 | struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(c: component); |
241 | |
242 | dev_dbg(component->dev, "%s: enter.\n" , __func__); |
243 | |
244 | ucontrol->value.integer.value[0] = wl1273->core->volume; |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol, |
250 | struct snd_ctl_elem_value *ucontrol) |
251 | { |
252 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
253 | struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(c: component); |
254 | int r; |
255 | |
256 | dev_dbg(component->dev, "%s: enter.\n" , __func__); |
257 | |
258 | r = wl1273->core->set_volume(wl1273->core, |
259 | ucontrol->value.integer.value[0]); |
260 | if (r) |
261 | return r; |
262 | |
263 | return 1; |
264 | } |
265 | |
266 | static const struct snd_kcontrol_new wl1273_controls[] = { |
267 | SOC_ENUM_EXT("Codec Mode" , wl1273_enum, |
268 | snd_wl1273_get_audio_route, snd_wl1273_set_audio_route), |
269 | SOC_ENUM_EXT("Audio Switch" , wl1273_audio_enum, |
270 | snd_wl1273_fm_audio_get, snd_wl1273_fm_audio_put), |
271 | SOC_SINGLE_EXT("Volume" , 0, 0, WL1273_MAX_VOLUME, 0, |
272 | snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put), |
273 | }; |
274 | |
275 | static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = { |
276 | SND_SOC_DAPM_INPUT("RX" ), |
277 | |
278 | SND_SOC_DAPM_OUTPUT("TX" ), |
279 | }; |
280 | |
281 | static const struct snd_soc_dapm_route wl1273_dapm_routes[] = { |
282 | { "Capture" , NULL, "RX" }, |
283 | |
284 | { "TX" , NULL, "Playback" }, |
285 | }; |
286 | |
287 | static int wl1273_startup(struct snd_pcm_substream *substream, |
288 | struct snd_soc_dai *dai) |
289 | { |
290 | struct snd_soc_component *component = dai->component; |
291 | struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(c: component); |
292 | |
293 | switch (wl1273->mode) { |
294 | case WL1273_MODE_BT: |
295 | snd_pcm_hw_constraint_single(runtime: substream->runtime, |
296 | SNDRV_PCM_HW_PARAM_RATE, val: 8000); |
297 | snd_pcm_hw_constraint_single(runtime: substream->runtime, |
298 | SNDRV_PCM_HW_PARAM_CHANNELS, val: 1); |
299 | break; |
300 | case WL1273_MODE_FM_RX: |
301 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
302 | pr_err("Cannot play in RX mode.\n" ); |
303 | return -EINVAL; |
304 | } |
305 | break; |
306 | case WL1273_MODE_FM_TX: |
307 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
308 | pr_err("Cannot capture in TX mode.\n" ); |
309 | return -EINVAL; |
310 | } |
311 | break; |
312 | default: |
313 | return -EINVAL; |
314 | } |
315 | |
316 | return 0; |
317 | } |
318 | |
319 | static int wl1273_hw_params(struct snd_pcm_substream *substream, |
320 | struct snd_pcm_hw_params *params, |
321 | struct snd_soc_dai *dai) |
322 | { |
323 | struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(c: dai->component); |
324 | struct wl1273_core *core = wl1273->core; |
325 | unsigned int rate, width, r; |
326 | |
327 | if (params_width(p: params) != 16) { |
328 | dev_err(dai->dev, "%d bits/sample not supported\n" , |
329 | params_width(params)); |
330 | return -EINVAL; |
331 | } |
332 | |
333 | rate = params_rate(p: params); |
334 | width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; |
335 | |
336 | if (wl1273->mode == WL1273_MODE_BT) { |
337 | if (rate != 8000) { |
338 | pr_err("Rate %d not supported.\n" , params_rate(params)); |
339 | return -EINVAL; |
340 | } |
341 | |
342 | if (params_channels(p: params) != 1) { |
343 | pr_err("Only mono supported.\n" ); |
344 | return -EINVAL; |
345 | } |
346 | |
347 | return 0; |
348 | } |
349 | |
350 | if (wl1273->mode == WL1273_MODE_FM_TX && |
351 | substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
352 | pr_err("Only playback supported with TX.\n" ); |
353 | return -EINVAL; |
354 | } |
355 | |
356 | if (wl1273->mode == WL1273_MODE_FM_RX && |
357 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
358 | pr_err("Only capture supported with RX.\n" ); |
359 | return -EINVAL; |
360 | } |
361 | |
362 | if (wl1273->mode != WL1273_MODE_FM_RX && |
363 | wl1273->mode != WL1273_MODE_FM_TX) { |
364 | pr_err("Unexpected mode: %d.\n" , wl1273->mode); |
365 | return -EINVAL; |
366 | } |
367 | |
368 | r = snd_wl1273_fm_set_i2s_mode(core, rate, width); |
369 | if (r) |
370 | return r; |
371 | |
372 | wl1273->channels = params_channels(p: params); |
373 | r = snd_wl1273_fm_set_channel_number(core, channel_number: wl1273->channels); |
374 | if (r) |
375 | return r; |
376 | |
377 | return 0; |
378 | } |
379 | |
380 | static const struct snd_soc_dai_ops wl1273_dai_ops = { |
381 | .startup = wl1273_startup, |
382 | .hw_params = wl1273_hw_params, |
383 | }; |
384 | |
385 | static struct snd_soc_dai_driver wl1273_dai = { |
386 | .name = "wl1273-fm" , |
387 | .playback = { |
388 | .stream_name = "Playback" , |
389 | .channels_min = 1, |
390 | .channels_max = 2, |
391 | .rates = SNDRV_PCM_RATE_8000_48000, |
392 | .formats = SNDRV_PCM_FMTBIT_S16_LE}, |
393 | .capture = { |
394 | .stream_name = "Capture" , |
395 | .channels_min = 1, |
396 | .channels_max = 2, |
397 | .rates = SNDRV_PCM_RATE_8000_48000, |
398 | .formats = SNDRV_PCM_FMTBIT_S16_LE}, |
399 | .ops = &wl1273_dai_ops, |
400 | }; |
401 | |
402 | /* Audio interface format for the soc_card driver */ |
403 | int wl1273_get_format(struct snd_soc_component *component, unsigned int *fmt) |
404 | { |
405 | struct wl1273_priv *wl1273; |
406 | |
407 | if (component == NULL || fmt == NULL) |
408 | return -EINVAL; |
409 | |
410 | wl1273 = snd_soc_component_get_drvdata(c: component); |
411 | |
412 | switch (wl1273->mode) { |
413 | case WL1273_MODE_FM_RX: |
414 | case WL1273_MODE_FM_TX: |
415 | *fmt = SND_SOC_DAIFMT_I2S | |
416 | SND_SOC_DAIFMT_NB_NF | |
417 | SND_SOC_DAIFMT_CBP_CFP; |
418 | |
419 | break; |
420 | case WL1273_MODE_BT: |
421 | *fmt = SND_SOC_DAIFMT_DSP_A | |
422 | SND_SOC_DAIFMT_IB_NF | |
423 | SND_SOC_DAIFMT_CBP_CFP; |
424 | |
425 | break; |
426 | default: |
427 | return -EINVAL; |
428 | } |
429 | |
430 | return 0; |
431 | } |
432 | EXPORT_SYMBOL_GPL(wl1273_get_format); |
433 | |
434 | static int wl1273_probe(struct snd_soc_component *component) |
435 | { |
436 | struct wl1273_core **core = component->dev->platform_data; |
437 | struct wl1273_priv *wl1273; |
438 | |
439 | dev_dbg(component->dev, "%s.\n" , __func__); |
440 | |
441 | if (!core) { |
442 | dev_err(component->dev, "Platform data is missing.\n" ); |
443 | return -EINVAL; |
444 | } |
445 | |
446 | wl1273 = kzalloc(size: sizeof(struct wl1273_priv), GFP_KERNEL); |
447 | if (!wl1273) |
448 | return -ENOMEM; |
449 | |
450 | wl1273->mode = WL1273_MODE_BT; |
451 | wl1273->core = *core; |
452 | |
453 | snd_soc_component_set_drvdata(c: component, data: wl1273); |
454 | |
455 | return 0; |
456 | } |
457 | |
458 | static void wl1273_remove(struct snd_soc_component *component) |
459 | { |
460 | struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(c: component); |
461 | |
462 | dev_dbg(component->dev, "%s\n" , __func__); |
463 | kfree(objp: wl1273); |
464 | } |
465 | |
466 | static const struct snd_soc_component_driver soc_component_dev_wl1273 = { |
467 | .probe = wl1273_probe, |
468 | .remove = wl1273_remove, |
469 | .controls = wl1273_controls, |
470 | .num_controls = ARRAY_SIZE(wl1273_controls), |
471 | .dapm_widgets = wl1273_dapm_widgets, |
472 | .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets), |
473 | .dapm_routes = wl1273_dapm_routes, |
474 | .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes), |
475 | .idle_bias_on = 1, |
476 | .use_pmdown_time = 1, |
477 | .endianness = 1, |
478 | }; |
479 | |
480 | static int wl1273_platform_probe(struct platform_device *pdev) |
481 | { |
482 | return devm_snd_soc_register_component(dev: &pdev->dev, |
483 | component_driver: &soc_component_dev_wl1273, |
484 | dai_drv: &wl1273_dai, num_dai: 1); |
485 | } |
486 | |
487 | MODULE_ALIAS("platform:wl1273-codec" ); |
488 | |
489 | static struct platform_driver wl1273_platform_driver = { |
490 | .driver = { |
491 | .name = "wl1273-codec" , |
492 | }, |
493 | .probe = wl1273_platform_probe, |
494 | }; |
495 | |
496 | module_platform_driver(wl1273_platform_driver); |
497 | |
498 | MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>" ); |
499 | MODULE_DESCRIPTION("ASoC WL1273 codec driver" ); |
500 | MODULE_LICENSE("GPL" ); |
501 | |