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