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