]>
Commit | Line | Data |
---|---|---|
6c9ca7fa | 1 | /* |
2 | * ASoC Driver for RRA DigiDAC1 | |
3 | * Copyright 2016 | |
4 | * Author: José M. Tasende <vintage@redrocksaudio.es> | |
5 | * based on the HifiBerry DAC driver by Florian Meier <florian.meier@koalo.de> | |
6 | * and the Wolfson card driver by Nikesh Oswal, <Nikesh.Oswal@wolfsonmicro.com> | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * version 2 as published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/module.h> | |
18 | #include <linux/platform_device.h> | |
19 | #include <linux/i2c.h> | |
20 | #include <sound/core.h> | |
21 | #include <sound/pcm.h> | |
22 | #include <sound/pcm_params.h> | |
23 | #include <sound/soc.h> | |
24 | #include <sound/jack.h> | |
25 | #include <sound/soc-dapm.h> | |
26 | #include <sound/tlv.h> | |
27 | #include <linux/regulator/consumer.h> | |
28 | ||
29 | #include "../codecs/wm8804.h" | |
30 | #include "../codecs/wm8741.h" | |
31 | ||
32 | #define WM8741_NUM_SUPPLIES 2 | |
33 | ||
34 | /* codec private data */ | |
35 | struct wm8741_priv { | |
36 | struct wm8741_platform_data pdata; | |
37 | struct regmap *regmap; | |
38 | struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES]; | |
39 | unsigned int sysclk; | |
40 | const struct snd_pcm_hw_constraint_list *sysclk_constraints; | |
41 | }; | |
42 | ||
43 | static int samplerate = 44100; | |
44 | ||
45 | /* New Alsa Controls not exposed by original wm8741 codec driver */ | |
46 | /* in actual driver the att. adjustment is wrong because */ | |
47 | /* this DAC has a coarse attenuation register with 4dB steps */ | |
48 | /* and a fine level register with 0.125dB steps */ | |
49 | /* each register has 32 steps so combining both we have 1024 steps */ | |
50 | /* of 0.125 dB. */ | |
51 | /* The original level controls from driver are removed at startup */ | |
52 | /* and replaced by the corrected ones. */ | |
53 | /* The same wm8741 driver can be used for wm8741 and wm8742 devices */ | |
54 | ||
55 | static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, 0, 13, 0); | |
56 | static const DECLARE_TLV_DB_SCALE(dac_tlv_coarse, -12700, 400, 1); | |
57 | static const char *w8741_dither[4] = {"Off", "RPDF", "TPDF", "HPDF"}; | |
58 | static const char *w8741_filter[5] = { | |
59 | "Type 1", "Type 2", "Type 3", "Type 4", "Type 5"}; | |
60 | static const char *w8741_switch[2] = {"Off", "On"}; | |
61 | static const struct soc_enum w8741_enum[] = { | |
62 | SOC_ENUM_SINGLE(WM8741_MODE_CONTROL_2, 0, 4, w8741_dither),/* dithering type */ | |
63 | SOC_ENUM_SINGLE(WM8741_FILTER_CONTROL, 0, 5, w8741_filter),/* filter type */ | |
64 | SOC_ENUM_SINGLE(WM8741_FORMAT_CONTROL, 6, 2, w8741_switch),/* phase invert */ | |
65 | SOC_ENUM_SINGLE(WM8741_VOLUME_CONTROL, 0, 2, w8741_switch),/* volume ramp */ | |
66 | SOC_ENUM_SINGLE(WM8741_VOLUME_CONTROL, 3, 2, w8741_switch),/* soft mute */ | |
67 | }; | |
68 | ||
69 | static const struct snd_kcontrol_new w8741_snd_controls_stereo[] = { | |
70 | SOC_DOUBLE_R_TLV("DAC Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, | |
71 | WM8741_DACRLSB_ATTENUATION, 0, 31, 1, dac_tlv_fine), | |
72 | SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8741_DACLMSB_ATTENUATION, | |
73 | WM8741_DACRMSB_ATTENUATION, 0, 31, 1, dac_tlv_coarse), | |
74 | SOC_ENUM("DAC Dither", w8741_enum[0]), | |
75 | SOC_ENUM("DAC Digital Filter", w8741_enum[1]), | |
76 | SOC_ENUM("DAC Phase Invert", w8741_enum[2]), | |
77 | SOC_ENUM("DAC Volume Ramp", w8741_enum[3]), | |
78 | SOC_ENUM("DAC Soft Mute", w8741_enum[4]), | |
79 | }; | |
80 | ||
81 | static const struct snd_kcontrol_new w8741_snd_controls_mono_left[] = { | |
82 | SOC_SINGLE_TLV("DAC Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, | |
83 | 0, 31, 0, dac_tlv_fine), | |
84 | SOC_SINGLE_TLV("Digital Playback Volume", WM8741_DACLMSB_ATTENUATION, | |
85 | 0, 31, 1, dac_tlv_coarse), | |
86 | SOC_ENUM("DAC Dither", w8741_enum[0]), | |
87 | SOC_ENUM("DAC Digital Filter", w8741_enum[1]), | |
88 | SOC_ENUM("DAC Phase Invert", w8741_enum[2]), | |
89 | SOC_ENUM("DAC Volume Ramp", w8741_enum[3]), | |
90 | SOC_ENUM("DAC Soft Mute", w8741_enum[4]), | |
91 | }; | |
92 | ||
93 | static const struct snd_kcontrol_new w8741_snd_controls_mono_right[] = { | |
94 | SOC_SINGLE_TLV("DAC Fine Playback Volume", WM8741_DACRLSB_ATTENUATION, | |
95 | 0, 31, 0, dac_tlv_fine), | |
96 | SOC_SINGLE_TLV("Digital Playback Volume", WM8741_DACRMSB_ATTENUATION, | |
97 | 0, 31, 1, dac_tlv_coarse), | |
98 | SOC_ENUM("DAC Dither", w8741_enum[0]), | |
99 | SOC_ENUM("DAC Digital Filter", w8741_enum[1]), | |
100 | SOC_ENUM("DAC Phase Invert", w8741_enum[2]), | |
101 | SOC_ENUM("DAC Volume Ramp", w8741_enum[3]), | |
102 | SOC_ENUM("DAC Soft Mute", w8741_enum[4]), | |
103 | }; | |
104 | ||
105 | static int w8741_add_controls(struct snd_soc_codec *codec) | |
106 | { | |
107 | struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); | |
108 | ||
109 | switch (wm8741->pdata.diff_mode) { | |
110 | case WM8741_DIFF_MODE_STEREO: | |
111 | case WM8741_DIFF_MODE_STEREO_REVERSED: | |
112 | snd_soc_add_codec_controls(codec, | |
113 | w8741_snd_controls_stereo, | |
114 | ARRAY_SIZE(w8741_snd_controls_stereo)); | |
115 | break; | |
116 | case WM8741_DIFF_MODE_MONO_LEFT: | |
117 | snd_soc_add_codec_controls(codec, | |
118 | w8741_snd_controls_mono_left, | |
119 | ARRAY_SIZE(w8741_snd_controls_mono_left)); | |
120 | break; | |
121 | case WM8741_DIFF_MODE_MONO_RIGHT: | |
122 | snd_soc_add_codec_controls(codec, | |
123 | w8741_snd_controls_mono_right, | |
124 | ARRAY_SIZE(w8741_snd_controls_mono_right)); | |
125 | break; | |
126 | default: | |
127 | return -EINVAL; | |
128 | } | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | static int digidac1_soundcard_init(struct snd_soc_pcm_runtime *rtd) | |
134 | { | |
135 | struct snd_soc_codec *codec = rtd->codec; | |
136 | struct snd_soc_card *card = rtd->card; | |
137 | struct snd_soc_pcm_runtime *wm8741_rtd; | |
138 | struct snd_soc_codec *wm8741_codec; | |
139 | struct snd_card *sound_card = card->snd_card; | |
140 | struct snd_kcontrol *kctl; | |
141 | int ret; | |
142 | ||
143 | wm8741_rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name); | |
144 | if (!wm8741_rtd) { | |
145 | dev_warn(card->dev, "digidac1_soundcard_init: couldn't get wm8741 rtd\n"); | |
146 | return -EFAULT; | |
147 | } | |
148 | wm8741_codec = wm8741_rtd->codec; | |
149 | ret = w8741_add_controls(wm8741_codec); | |
150 | if (ret < 0) | |
151 | dev_warn(card->dev, "Failed to add new wm8741 controls: %d\n", | |
152 | ret); | |
153 | ||
154 | /* enable TX output */ | |
155 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); | |
156 | ||
157 | kctl = snd_soc_card_get_kcontrol(card, | |
158 | "Playback Volume"); | |
159 | if (kctl) { | |
160 | kctl->vd[0].access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | |
161 | snd_ctl_remove(sound_card, kctl); | |
162 | } | |
163 | kctl = snd_soc_card_get_kcontrol(card, | |
164 | "Fine Playback Volume"); | |
165 | if (kctl) { | |
166 | kctl->vd[0].access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | |
167 | snd_ctl_remove(sound_card, kctl); | |
168 | } | |
169 | return 0; | |
170 | } | |
171 | ||
172 | static int digidac1_soundcard_startup(struct snd_pcm_substream *substream) | |
173 | { | |
174 | /* turn on wm8804 digital output */ | |
175 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
176 | struct snd_soc_codec *codec = rtd->codec; | |
177 | struct snd_soc_card *card = rtd->card; | |
178 | struct snd_soc_pcm_runtime *wm8741_rtd; | |
179 | struct snd_soc_codec *wm8741_codec; | |
180 | ||
181 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x3c, 0x00); | |
182 | wm8741_rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name); | |
183 | if (!wm8741_rtd) { | |
184 | dev_warn(card->dev, "digidac1_soundcard_startup: couldn't get WM8741 rtd\n"); | |
185 | return -EFAULT; | |
186 | } | |
187 | wm8741_codec = wm8741_rtd->codec; | |
188 | ||
189 | /* latch wm8741 level */ | |
190 | snd_soc_update_bits(wm8741_codec, WM8741_DACLLSB_ATTENUATION, | |
191 | WM8741_UPDATELL, WM8741_UPDATELL); | |
192 | snd_soc_update_bits(wm8741_codec, WM8741_DACLMSB_ATTENUATION, | |
193 | WM8741_UPDATELM, WM8741_UPDATELM); | |
194 | snd_soc_update_bits(wm8741_codec, WM8741_DACRLSB_ATTENUATION, | |
195 | WM8741_UPDATERL, WM8741_UPDATERL); | |
196 | snd_soc_update_bits(wm8741_codec, WM8741_DACRMSB_ATTENUATION, | |
197 | WM8741_UPDATERM, WM8741_UPDATERM); | |
198 | ||
199 | return 0; | |
200 | } | |
201 | ||
202 | static void digidac1_soundcard_shutdown(struct snd_pcm_substream *substream) | |
203 | { | |
204 | /* turn off wm8804 digital output */ | |
205 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
206 | struct snd_soc_codec *codec = rtd->codec; | |
207 | ||
208 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x3c, 0x3c); | |
209 | } | |
210 | ||
211 | static int digidac1_soundcard_hw_params(struct snd_pcm_substream *substream, | |
212 | struct snd_pcm_hw_params *params) | |
213 | { | |
214 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
215 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | |
216 | struct snd_soc_codec *codec = rtd->codec; | |
217 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | |
218 | struct snd_soc_card *card = rtd->card; | |
219 | struct snd_soc_pcm_runtime *wm8741_rtd; | |
220 | struct snd_soc_codec *wm8741_codec; | |
221 | ||
222 | int sysclk = 27000000; | |
223 | long mclk_freq = 0; | |
224 | int mclk_div = 1; | |
225 | int sampling_freq = 1; | |
226 | int ret; | |
227 | ||
228 | wm8741_rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name); | |
229 | if (!wm8741_rtd) { | |
230 | dev_warn(card->dev, "digidac1_soundcard_hw_params: couldn't get WM8741 rtd\n"); | |
231 | return -EFAULT; | |
232 | } | |
233 | wm8741_codec = wm8741_rtd->codec; | |
234 | samplerate = params_rate(params); | |
235 | ||
236 | if (samplerate <= 96000) { | |
237 | mclk_freq = samplerate*256; | |
238 | mclk_div = WM8804_MCLKDIV_256FS; | |
239 | } else { | |
240 | mclk_freq = samplerate*128; | |
241 | mclk_div = WM8804_MCLKDIV_128FS; | |
242 | } | |
243 | ||
244 | switch (samplerate) { | |
245 | case 32000: | |
246 | sampling_freq = 0x03; | |
247 | break; | |
248 | case 44100: | |
249 | sampling_freq = 0x00; | |
250 | break; | |
251 | case 48000: | |
252 | sampling_freq = 0x02; | |
253 | break; | |
254 | case 88200: | |
255 | sampling_freq = 0x08; | |
256 | break; | |
257 | case 96000: | |
258 | sampling_freq = 0x0a; | |
259 | break; | |
260 | case 176400: | |
261 | sampling_freq = 0x0c; | |
262 | break; | |
263 | case 192000: | |
264 | sampling_freq = 0x0e; | |
265 | break; | |
266 | default: | |
267 | dev_err(codec->dev, | |
268 | "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n", | |
269 | samplerate); | |
270 | } | |
271 | ||
272 | snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div); | |
273 | snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq); | |
274 | ||
275 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL, | |
276 | sysclk, SND_SOC_CLOCK_OUT); | |
277 | if (ret < 0) { | |
278 | dev_err(codec->dev, | |
279 | "Failed to set WM8804 SYSCLK: %d\n", ret); | |
280 | return ret; | |
281 | } | |
282 | /* Enable wm8804 TX output */ | |
283 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); | |
284 | ||
285 | /* wm8804 Power on */ | |
286 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0); | |
287 | ||
288 | /* wm8804 set sampling frequency status bits */ | |
289 | snd_soc_update_bits(codec, WM8804_SPDTX4, 0x0f, sampling_freq); | |
290 | ||
291 | /* Now update wm8741 registers for the correct oversampling */ | |
292 | if (samplerate <= 48000) | |
293 | snd_soc_update_bits(wm8741_codec, WM8741_MODE_CONTROL_1, | |
294 | WM8741_OSR_MASK, 0x00); | |
295 | else if (samplerate <= 96000) | |
296 | snd_soc_update_bits(wm8741_codec, WM8741_MODE_CONTROL_1, | |
297 | WM8741_OSR_MASK, 0x20); | |
298 | else | |
299 | snd_soc_update_bits(wm8741_codec, WM8741_MODE_CONTROL_1, | |
300 | WM8741_OSR_MASK, 0x40); | |
301 | ||
302 | /* wm8741 bit size */ | |
303 | switch (params_width(params)) { | |
304 | case 16: | |
305 | snd_soc_update_bits(wm8741_codec, WM8741_FORMAT_CONTROL, | |
306 | WM8741_IWL_MASK, 0x00); | |
307 | break; | |
308 | case 20: | |
309 | snd_soc_update_bits(wm8741_codec, WM8741_FORMAT_CONTROL, | |
310 | WM8741_IWL_MASK, 0x01); | |
311 | break; | |
312 | case 24: | |
313 | snd_soc_update_bits(wm8741_codec, WM8741_FORMAT_CONTROL, | |
314 | WM8741_IWL_MASK, 0x02); | |
315 | break; | |
316 | case 32: | |
317 | snd_soc_update_bits(wm8741_codec, WM8741_FORMAT_CONTROL, | |
318 | WM8741_IWL_MASK, 0x03); | |
319 | break; | |
320 | default: | |
321 | dev_dbg(codec->dev, "wm8741_hw_params: Unsupported bit size param = %d", | |
322 | params_width(params)); | |
323 | return -EINVAL; | |
324 | } | |
325 | ||
326 | return snd_soc_dai_set_bclk_ratio(cpu_dai, 64); | |
327 | } | |
328 | /* machine stream operations */ | |
329 | static struct snd_soc_ops digidac1_soundcard_ops = { | |
330 | .hw_params = digidac1_soundcard_hw_params, | |
331 | .startup = digidac1_soundcard_startup, | |
332 | .shutdown = digidac1_soundcard_shutdown, | |
333 | }; | |
334 | ||
335 | static struct snd_soc_dai_link digidac1_soundcard_dai[] = { | |
336 | { | |
337 | .name = "RRA DigiDAC1", | |
338 | .stream_name = "RRA DigiDAC1 HiFi", | |
339 | .cpu_dai_name = "bcm2708-i2s.0", | |
340 | .codec_dai_name = "wm8804-spdif", | |
341 | .platform_name = "bcm2708-i2s.0", | |
342 | .codec_name = "wm8804.1-003b", | |
343 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | |
344 | SND_SOC_DAIFMT_CBM_CFM, | |
345 | .ops = &digidac1_soundcard_ops, | |
346 | .init = digidac1_soundcard_init, | |
347 | }, | |
348 | { | |
349 | .name = "RRA DigiDAC11", | |
350 | .stream_name = "RRA DigiDAC11 HiFi", | |
351 | .cpu_dai_name = "wm8804-spdif", | |
352 | .codec_dai_name = "wm8741", | |
353 | .codec_name = "wm8741.1-001a", | |
354 | .dai_fmt = SND_SOC_DAIFMT_I2S | |
355 | | SND_SOC_DAIFMT_NB_NF | |
356 | | SND_SOC_DAIFMT_CBS_CFS, | |
357 | }, | |
358 | }; | |
359 | ||
360 | /* audio machine driver */ | |
361 | static struct snd_soc_card digidac1_soundcard = { | |
362 | .name = "digidac1-soundcard", | |
363 | .owner = THIS_MODULE, | |
364 | .dai_link = digidac1_soundcard_dai, | |
365 | .num_links = ARRAY_SIZE(digidac1_soundcard_dai), | |
366 | }; | |
367 | ||
368 | static int digidac1_soundcard_probe(struct platform_device *pdev) | |
369 | { | |
370 | int ret = 0; | |
371 | ||
372 | digidac1_soundcard.dev = &pdev->dev; | |
373 | ||
374 | if (pdev->dev.of_node) { | |
375 | struct device_node *i2s_node; | |
376 | struct snd_soc_dai_link *dai = &digidac1_soundcard_dai[0]; | |
377 | ||
378 | i2s_node = of_parse_phandle(pdev->dev.of_node, | |
379 | "i2s-controller", 0); | |
380 | ||
381 | if (i2s_node) { | |
382 | dai->cpu_dai_name = NULL; | |
383 | dai->cpu_of_node = i2s_node; | |
384 | dai->platform_name = NULL; | |
385 | dai->platform_of_node = i2s_node; | |
386 | } | |
387 | } | |
388 | ||
389 | ret = snd_soc_register_card(&digidac1_soundcard); | |
49dbf1af | 390 | if (ret && ret != -EPROBE_DEFER) |
6c9ca7fa | 391 | dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", |
49dbf1af | 392 | ret); |
6c9ca7fa | 393 | |
394 | return ret; | |
395 | } | |
396 | ||
397 | static int digidac1_soundcard_remove(struct platform_device *pdev) | |
398 | { | |
399 | return snd_soc_unregister_card(&digidac1_soundcard); | |
400 | } | |
401 | ||
402 | static const struct of_device_id digidac1_soundcard_of_match[] = { | |
403 | { .compatible = "rra,digidac1-soundcard", }, | |
404 | {}, | |
405 | }; | |
406 | MODULE_DEVICE_TABLE(of, digidac1_soundcard_of_match); | |
407 | ||
408 | static struct platform_driver digidac1_soundcard_driver = { | |
409 | .driver = { | |
410 | .name = "digidac1-audio", | |
411 | .owner = THIS_MODULE, | |
412 | .of_match_table = digidac1_soundcard_of_match, | |
413 | }, | |
414 | .probe = digidac1_soundcard_probe, | |
415 | .remove = digidac1_soundcard_remove, | |
416 | }; | |
417 | ||
418 | module_platform_driver(digidac1_soundcard_driver); | |
419 | ||
420 | MODULE_AUTHOR("José M. Tasende <vintage@redrocksaudio.es>"); | |
421 | MODULE_DESCRIPTION("ASoC Driver for RRA DigiDAC1"); | |
422 | MODULE_LICENSE("GPL v2"); |