]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - sound/soc/bcm/allo-piano-dac-plus.c
Add support for Allo Piano DAC 2.1 plus add-on board for Raspberry Pi.
[mirror_ubuntu-zesty-kernel.git] / sound / soc / bcm / allo-piano-dac-plus.c
1 /*
2 * ALSA ASoC Machine Driver for Allo Piano DAC Plus Subwoofer
3 *
4 * Author: Baswaraj K <jaikumar@cem-solutions.net>
5 * Copyright 2016
6 * based on code by Daniel Matuschek <info@crazy-audio.com>
7 * based on code by Florian Meier <florian.meier@koalo.de>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * version 2 as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 */
18
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21
22 #include <sound/core.h>
23 #include <sound/pcm.h>
24 #include <sound/pcm_params.h>
25 #include <sound/soc.h>
26 #include <linux/firmware.h>
27 #include <linux/delay.h>
28 #include <sound/tlv.h>
29 #include "../codecs/pcm512x.h"
30
31 struct dsp_code {
32 char i2c_addr;
33 char offset;
34 char val;
35 };
36
37 static struct snd_soc_pcm_runtime *rtd_glb;
38 static bool digital_gain_0db_limit = true;
39 unsigned int set_lowpass, set_mode, set_rate, dsp_page_number;
40
41 static const char * const allo_piano_mode_texts[] = {
42 "2.0",
43 "2.1",
44 "2.2",
45 };
46
47 static const SOC_ENUM_SINGLE_DECL(allo_piano_mode_enum,
48 0, 0, allo_piano_mode_texts);
49
50 static const char * const allo_piano_dsp_low_pass_texts[] = {
51 "60",
52 "70",
53 "80",
54 "90",
55 "100",
56 "110",
57 "120",
58 "130",
59 "140",
60 "150",
61 "160",
62 "170",
63 "180",
64 "190",
65 "200",
66 };
67
68 static const SOC_ENUM_SINGLE_DECL(allo_piano_enum,
69 0, 0, allo_piano_dsp_low_pass_texts);
70
71 static int snd_allo_piano_dsp_program(struct snd_soc_pcm_runtime *rtd,
72 unsigned int mode, unsigned int rate, unsigned int lowpass)
73 {
74 const struct firmware *fw;
75 char firmware_name[40];
76 int ret = 0, dac = 0;
77
78 if (rate <= 46000)
79 rate = 44100;
80 else if (rate <= 68000)
81 rate = 48000;
82 else if (rate <= 92000)
83 rate = 88200;
84 else if (rate <= 136000)
85 rate = 96000;
86 else if (rate <= 184000)
87 rate = 176400;
88 else
89 rate = 192000;
90
91 /* same configuration loaded */
92 if ((rate == set_rate) && (lowpass == set_lowpass)
93 && (mode == set_mode))
94 return 1;
95
96 set_rate = rate;
97 set_mode = mode;
98
99 if (mode == 0) { /* 2.0 */
100 snd_soc_write(rtd->codec_dais[1]->codec,
101 PCM512x_MUTE, 0x11);
102 return 1;
103 } else {
104 snd_soc_write(rtd->codec_dais[1]->codec,
105 PCM512x_MUTE, 0x00);
106 }
107
108 set_lowpass = lowpass;
109
110 for (dac = 0; dac < rtd->num_codecs; dac++) {
111 struct dsp_code *dsp_code_read;
112 int i = 1;
113 struct snd_soc_codec *codec = rtd->codec_dais[dac]->codec;
114
115 if (dac == 0) { /* high */
116 sprintf(firmware_name,
117 "alloPiano/2.2/allo-piano-dsp-%d-%d-%d.bin",
118 rate, ((set_lowpass * 10) + 60), dac);
119 } else { /* low */
120 sprintf(firmware_name,
121 "alloPiano/2.%d/allo-piano-dsp-%d-%d-%d.bin",
122 set_mode, rate, ((set_lowpass * 10) + 60), dac);
123 }
124
125 dev_info(codec->dev, "Dsp Firmware File Name: %s\n",
126 firmware_name);
127
128 ret = request_firmware(&fw, firmware_name, codec->dev);
129 if (ret < 0) {
130 dev_err(codec->dev, "Error: AlloPiano Firmware %s missing. %d\n",
131 firmware_name, ret);
132 goto err;
133 }
134
135 while (i < (fw->size - 1)) {
136 dsp_code_read = (struct dsp_code *)&fw->data[i];
137
138 if (dsp_code_read->offset == 0) {
139 dsp_page_number = dsp_code_read->val;
140 ret = snd_soc_write(rtd->codec_dais[dac]->codec,
141 PCM512x_PAGE_BASE(0),
142 dsp_code_read->val);
143
144 } else if (dsp_code_read->offset != 0) {
145 ret = snd_soc_write(rtd->codec_dais[dac]->codec,
146 (PCM512x_PAGE_BASE(dsp_page_number) +
147 dsp_code_read->offset),
148 dsp_code_read->val);
149
150 }
151 if (ret < 0) {
152 dev_err(codec->dev,
153 "Failed to write Register: %d\n", ret);
154 goto err;
155 }
156 i = i + 3;
157 }
158 release_firmware(fw);
159 }
160 return 1;
161
162 err:
163 release_firmware(fw);
164 return ret;
165 }
166
167 static int snd_allo_piano_mode_get(struct snd_kcontrol *kcontrol,
168 struct snd_ctl_elem_value *ucontrol)
169 {
170 ucontrol->value.integer.value[0] = set_mode;
171 return 0;
172 }
173
174 static int snd_allo_piano_mode_put(struct snd_kcontrol *kcontrol,
175 struct snd_ctl_elem_value *ucontrol)
176 {
177 return(snd_allo_piano_dsp_program(rtd_glb,
178 ucontrol->value.integer.value[0],
179 set_rate, set_lowpass));
180 }
181
182 static int snd_allo_piano_lowpass_get(struct snd_kcontrol *kcontrol,
183 struct snd_ctl_elem_value *ucontrol)
184 {
185 ucontrol->value.integer.value[0] = set_lowpass;
186 return 0;
187 }
188
189 static int snd_allo_piano_lowpass_put(struct snd_kcontrol *kcontrol,
190 struct snd_ctl_elem_value *ucontrol)
191 {
192 return(snd_allo_piano_dsp_program(rtd_glb,
193 set_mode, set_rate,
194 ucontrol->value.integer.value[0]));
195 }
196
197 static int pcm512x_get_reg_sub(struct snd_kcontrol *kcontrol,
198 struct snd_ctl_elem_value *ucontrol)
199 {
200 struct soc_mixer_control *mc =
201 (struct soc_mixer_control *)kcontrol->private_value;
202 unsigned int left_val = 0;
203 unsigned int right_val = 0;
204
205 left_val = snd_soc_read(rtd_glb->codec_dais[1]->codec,
206 PCM512x_DIGITAL_VOLUME_2);
207 if (left_val < 0)
208 return left_val;
209
210 right_val = snd_soc_read(rtd_glb->codec_dais[1]->codec,
211 PCM512x_DIGITAL_VOLUME_3);
212 if (right_val < 0)
213 return right_val;
214
215 ucontrol->value.integer.value[0] =
216 (~(left_val >> mc->shift)) & mc->max;
217 ucontrol->value.integer.value[1] =
218 (~(right_val >> mc->shift)) & mc->max;
219
220 return 0;
221 }
222
223 static int pcm512x_set_reg_sub(struct snd_kcontrol *kcontrol,
224 struct snd_ctl_elem_value *ucontrol)
225 {
226 struct soc_mixer_control *mc =
227 (struct soc_mixer_control *)kcontrol->private_value;
228 unsigned int left_val = (ucontrol->value.integer.value[0] & mc->max);
229 unsigned int right_val = (ucontrol->value.integer.value[1] & mc->max);
230 int ret = 0;
231
232 ret = snd_soc_write(rtd_glb->codec_dais[1]->codec,
233 PCM512x_DIGITAL_VOLUME_2, (~left_val));
234 if (ret < 0)
235 return ret;
236
237 ret = snd_soc_write(rtd_glb->codec_dais[1]->codec,
238 PCM512x_DIGITAL_VOLUME_3, (~right_val));
239 if (ret < 0)
240 return ret;
241
242 return 1;
243 }
244
245 static int pcm512x_get_reg_sub_switch(struct snd_kcontrol *kcontrol,
246 struct snd_ctl_elem_value *ucontrol)
247 {
248 int val = 0;
249
250 val = snd_soc_read(rtd_glb->codec_dais[1]->codec, PCM512x_MUTE);
251 if (val < 0)
252 return val;
253
254 ucontrol->value.integer.value[0] = (val & 0x10) ? 0 : 1;
255 ucontrol->value.integer.value[1] = (val & 0x01) ? 0 : 1;
256
257 return val;
258 }
259
260 static int pcm512x_set_reg_sub_switch(struct snd_kcontrol *kcontrol,
261 struct snd_ctl_elem_value *ucontrol)
262 {
263 unsigned int left_val = (ucontrol->value.integer.value[0]);
264 unsigned int right_val = (ucontrol->value.integer.value[1]);
265 int ret = 0;
266
267 ret = snd_soc_write(rtd_glb->codec_dais[1]->codec, PCM512x_MUTE,
268 ~((left_val & 0x01)<<4 | (right_val & 0x01)));
269 if (ret < 0)
270 return ret;
271
272 return 1;
273
274 }
275
276 static const DECLARE_TLV_DB_SCALE(digital_tlv_sub, -10350, 50, 1);
277
278 static const struct snd_kcontrol_new allo_piano_controls[] = {
279 SOC_ENUM_EXT("Subwoofer mode",
280 allo_piano_mode_enum,
281 snd_allo_piano_mode_get,
282 snd_allo_piano_mode_put),
283
284 SOC_ENUM_EXT("Lowpass", allo_piano_enum,
285 snd_allo_piano_lowpass_get,
286 snd_allo_piano_lowpass_put),
287
288 SOC_DOUBLE_R_EXT_TLV("Subwoofer Digital Playback Volume",
289 PCM512x_DIGITAL_VOLUME_2,
290 PCM512x_DIGITAL_VOLUME_3, 0, 255, 1,
291 pcm512x_get_reg_sub,
292 pcm512x_set_reg_sub,
293 digital_tlv_sub),
294
295 SOC_DOUBLE_EXT("Subwoofer Digital Playback Switch",
296 PCM512x_MUTE,
297 PCM512x_RQML_SHIFT,
298 PCM512x_RQMR_SHIFT, 1, 1,
299 pcm512x_get_reg_sub_switch,
300 pcm512x_set_reg_sub_switch),
301 };
302
303 static int snd_allo_piano_dac_init(struct snd_soc_pcm_runtime *rtd)
304 {
305 rtd_glb = rtd;
306
307 if (digital_gain_0db_limit) {
308 int ret;
309 struct snd_soc_card *card = rtd->card;
310
311 ret = snd_soc_limit_volume(card, "Digital Playback Volume",
312 207);
313 if (ret < 0)
314 dev_warn(card->dev, "Failed to set volume limit: %d\n",
315 ret);
316 }
317
318 return 0;
319 }
320
321 static int snd_allo_piano_dac_hw_params(struct snd_pcm_substream *substream,
322 struct snd_pcm_hw_params *params)
323 {
324 struct snd_soc_pcm_runtime *rtd = substream->private_data;
325 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
326 unsigned int sample_bits =
327 snd_pcm_format_physical_width(params_format(params));
328 unsigned int rate = params_rate(params);
329 struct snd_soc_card *card = rtd->card;
330 int ret = 0;
331
332 rtd_glb = rtd; /* TODO */
333 if (digital_gain_0db_limit) {
334 ret = snd_soc_limit_volume(card,
335 "Subwoofer Digital Playback Volume", 207);
336 if (ret < 0)
337 dev_warn(card->dev, "Failed to set volume limit: %d\n",
338 ret);
339 }
340 ret = snd_allo_piano_dsp_program(rtd, set_mode, rate, set_lowpass);
341 if (ret < 0)
342 return ret;
343
344 ret = snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2);
345
346 return ret;
347 }
348
349 /* machine stream operations */
350 static struct snd_soc_ops snd_allo_piano_dac_ops = {
351 .hw_params = snd_allo_piano_dac_hw_params,
352 };
353
354 static struct snd_soc_dai_link_component allo_piano_2_1_codecs[] = {
355 {
356 .dai_name = "pcm512x-hifi",
357 },
358 {
359 .dai_name = "pcm512x-hifi",
360 },
361 };
362
363 static struct snd_soc_dai_link snd_allo_piano_dac_dai[] = {
364 {
365 .name = "PianoDACPlus",
366 .stream_name = "PianoDACPlus",
367 .cpu_dai_name = "bcm2708-i2s.0",
368 .platform_name = "bcm2708-i2s.0",
369 .codecs = allo_piano_2_1_codecs,
370 .num_codecs = 2,
371 .dai_fmt = SND_SOC_DAIFMT_I2S |
372 SND_SOC_DAIFMT_NB_NF |
373 SND_SOC_DAIFMT_CBS_CFS,
374 .ops = &snd_allo_piano_dac_ops,
375 .init = snd_allo_piano_dac_init,
376 },
377 };
378
379 /* audio machine driver */
380 static struct snd_soc_card snd_allo_piano_dac = {
381 .name = "PianoDACPlus",
382 .owner = THIS_MODULE,
383 .dai_link = snd_allo_piano_dac_dai,
384 .num_links = ARRAY_SIZE(snd_allo_piano_dac_dai),
385 .controls = allo_piano_controls,
386 .num_controls = ARRAY_SIZE(allo_piano_controls),
387 };
388
389 static int snd_allo_piano_dac_probe(struct platform_device *pdev)
390 {
391 int ret = 0, i = 0;
392 struct snd_soc_card *card = &snd_allo_piano_dac;
393
394 card->dev = &pdev->dev;
395 snd_allo_piano_dac.dev = &pdev->dev;
396
397 if (pdev->dev.of_node) {
398 struct device_node *i2s_node;
399 struct snd_soc_dai_link *dai;
400
401 dai = &snd_allo_piano_dac_dai[0];
402 i2s_node = of_parse_phandle(pdev->dev.of_node,
403 "i2s-controller", 0);
404 if (i2s_node) {
405 for (i = 0; i < card->num_links; i++) {
406 dai->cpu_dai_name = NULL;
407 dai->cpu_of_node = i2s_node;
408 dai->platform_name = NULL;
409 dai->platform_of_node = i2s_node;
410 }
411 }
412 digital_gain_0db_limit =
413 !of_property_read_bool(pdev->dev.of_node,
414 "allo,24db_digital_gain");
415
416 allo_piano_2_1_codecs[0].of_node =
417 of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
418 if (!allo_piano_2_1_codecs[0].of_node) {
419 dev_err(&pdev->dev,
420 "Property 'audio-codec' missing or invalid\n");
421 return -EINVAL;
422 }
423
424 allo_piano_2_1_codecs[1].of_node =
425 of_parse_phandle(pdev->dev.of_node, "audio-codec", 1);
426 if (!allo_piano_2_1_codecs[1].of_node) {
427 dev_err(&pdev->dev,
428 "Property 'audio-codec' missing or invalid\n");
429 return -EINVAL;
430 }
431 }
432
433 ret = snd_soc_register_card(&snd_allo_piano_dac);
434 if (ret < 0)
435 dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
436 ret);
437
438 return ret;
439 }
440
441 static int snd_allo_piano_dac_remove(struct platform_device *pdev)
442 {
443 return snd_soc_unregister_card(&snd_allo_piano_dac);
444 }
445
446 static const struct of_device_id snd_allo_piano_dac_of_match[] = {
447 { .compatible = "allo,piano-dac-plus", },
448 { /* sentinel */ },
449 };
450
451 MODULE_DEVICE_TABLE(of, snd_allo_piano_dac_of_match);
452
453 static struct platform_driver snd_allo_piano_dac_driver = {
454 .driver = {
455 .name = "snd-allo-piano-dac-plus",
456 .owner = THIS_MODULE,
457 .of_match_table = snd_allo_piano_dac_of_match,
458 },
459 .probe = snd_allo_piano_dac_probe,
460 .remove = snd_allo_piano_dac_remove,
461 };
462
463 module_platform_driver(snd_allo_piano_dac_driver);
464
465 MODULE_AUTHOR("Baswaraj K <jaikumar@cem-solutions.net>");
466 MODULE_DESCRIPTION("ALSA ASoC Machine Driver for Allo Piano DAC Plus");
467 MODULE_LICENSE("GPL v2");