2 * ALSA ASoC Machine Driver for Allo Piano DAC Plus Subwoofer
4 * Author: Baswaraj K <jaikumar@cem-solutions.net>
6 * based on code by Daniel Matuschek <info@crazy-audio.com>
7 * based on code by Florian Meier <florian.meier@koalo.de>
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.
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.
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
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"
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
;
41 static const char * const allo_piano_mode_texts
[] = {
47 static const SOC_ENUM_SINGLE_DECL(allo_piano_mode_enum
,
48 0, 0, allo_piano_mode_texts
);
50 static const char * const allo_piano_dsp_low_pass_texts
[] = {
68 static const SOC_ENUM_SINGLE_DECL(allo_piano_enum
,
69 0, 0, allo_piano_dsp_low_pass_texts
);
71 static int snd_allo_piano_dsp_program(struct snd_soc_pcm_runtime
*rtd
,
72 unsigned int mode
, unsigned int rate
, unsigned int lowpass
)
74 const struct firmware
*fw
;
75 char firmware_name
[40];
80 else if (rate
<= 68000)
82 else if (rate
<= 92000)
84 else if (rate
<= 136000)
86 else if (rate
<= 184000)
91 /* same configuration loaded */
92 if ((rate
== set_rate
) && (lowpass
== set_lowpass
)
93 && (mode
== set_mode
))
99 if (mode
== 0) { /* 2.0 */
100 snd_soc_write(rtd
->codec_dais
[1]->codec
,
104 snd_soc_write(rtd
->codec_dais
[1]->codec
,
108 set_lowpass
= lowpass
;
110 for (dac
= 0; dac
< rtd
->num_codecs
; dac
++) {
111 struct dsp_code
*dsp_code_read
;
113 struct snd_soc_codec
*codec
= rtd
->codec_dais
[dac
]->codec
;
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
);
120 sprintf(firmware_name
,
121 "alloPiano/2.%d/allo-piano-dsp-%d-%d-%d.bin",
122 set_mode
, rate
, ((set_lowpass
* 10) + 60), dac
);
125 dev_info(codec
->dev
, "Dsp Firmware File Name: %s\n",
128 ret
= request_firmware(&fw
, firmware_name
, codec
->dev
);
130 dev_err(codec
->dev
, "Error: AlloPiano Firmware %s missing. %d\n",
135 while (i
< (fw
->size
- 1)) {
136 dsp_code_read
= (struct dsp_code
*)&fw
->data
[i
];
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),
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
),
153 "Failed to write Register: %d\n", ret
);
158 release_firmware(fw
);
163 release_firmware(fw
);
167 static int snd_allo_piano_mode_get(struct snd_kcontrol
*kcontrol
,
168 struct snd_ctl_elem_value
*ucontrol
)
170 ucontrol
->value
.integer
.value
[0] = set_mode
;
174 static int snd_allo_piano_mode_put(struct snd_kcontrol
*kcontrol
,
175 struct snd_ctl_elem_value
*ucontrol
)
177 return(snd_allo_piano_dsp_program(rtd_glb
,
178 ucontrol
->value
.integer
.value
[0],
179 set_rate
, set_lowpass
));
182 static int snd_allo_piano_lowpass_get(struct snd_kcontrol
*kcontrol
,
183 struct snd_ctl_elem_value
*ucontrol
)
185 ucontrol
->value
.integer
.value
[0] = set_lowpass
;
189 static int snd_allo_piano_lowpass_put(struct snd_kcontrol
*kcontrol
,
190 struct snd_ctl_elem_value
*ucontrol
)
192 return(snd_allo_piano_dsp_program(rtd_glb
,
194 ucontrol
->value
.integer
.value
[0]));
197 static int pcm512x_get_reg_sub(struct snd_kcontrol
*kcontrol
,
198 struct snd_ctl_elem_value
*ucontrol
)
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;
205 left_val
= snd_soc_read(rtd_glb
->codec_dais
[1]->codec
,
206 PCM512x_DIGITAL_VOLUME_2
);
210 right_val
= snd_soc_read(rtd_glb
->codec_dais
[1]->codec
,
211 PCM512x_DIGITAL_VOLUME_3
);
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
;
223 static int pcm512x_set_reg_sub(struct snd_kcontrol
*kcontrol
,
224 struct snd_ctl_elem_value
*ucontrol
)
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
);
232 ret
= snd_soc_write(rtd_glb
->codec_dais
[1]->codec
,
233 PCM512x_DIGITAL_VOLUME_2
, (~left_val
));
237 ret
= snd_soc_write(rtd_glb
->codec_dais
[1]->codec
,
238 PCM512x_DIGITAL_VOLUME_3
, (~right_val
));
245 static int pcm512x_get_reg_sub_switch(struct snd_kcontrol
*kcontrol
,
246 struct snd_ctl_elem_value
*ucontrol
)
250 val
= snd_soc_read(rtd_glb
->codec_dais
[1]->codec
, PCM512x_MUTE
);
254 ucontrol
->value
.integer
.value
[0] = (val
& 0x10) ? 0 : 1;
255 ucontrol
->value
.integer
.value
[1] = (val
& 0x01) ? 0 : 1;
260 static int pcm512x_set_reg_sub_switch(struct snd_kcontrol
*kcontrol
,
261 struct snd_ctl_elem_value
*ucontrol
)
263 unsigned int left_val
= (ucontrol
->value
.integer
.value
[0]);
264 unsigned int right_val
= (ucontrol
->value
.integer
.value
[1]);
267 ret
= snd_soc_write(rtd_glb
->codec_dais
[1]->codec
, PCM512x_MUTE
,
268 ~((left_val
& 0x01)<<4 | (right_val
& 0x01)));
276 static const DECLARE_TLV_DB_SCALE(digital_tlv_sub
, -10350, 50, 1);
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
),
284 SOC_ENUM_EXT("Lowpass", allo_piano_enum
,
285 snd_allo_piano_lowpass_get
,
286 snd_allo_piano_lowpass_put
),
288 SOC_DOUBLE_R_EXT_TLV("Subwoofer Digital Playback Volume",
289 PCM512x_DIGITAL_VOLUME_2
,
290 PCM512x_DIGITAL_VOLUME_3
, 0, 255, 1,
295 SOC_DOUBLE_EXT("Subwoofer Digital Playback Switch",
298 PCM512x_RQMR_SHIFT
, 1, 1,
299 pcm512x_get_reg_sub_switch
,
300 pcm512x_set_reg_sub_switch
),
303 static int snd_allo_piano_dac_init(struct snd_soc_pcm_runtime
*rtd
)
307 if (digital_gain_0db_limit
) {
309 struct snd_soc_card
*card
= rtd
->card
;
311 ret
= snd_soc_limit_volume(card
, "Digital Playback Volume",
314 dev_warn(card
->dev
, "Failed to set volume limit: %d\n",
321 static int snd_allo_piano_dac_hw_params(struct snd_pcm_substream
*substream
,
322 struct snd_pcm_hw_params
*params
)
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
;
332 rtd_glb
= rtd
; /* TODO */
333 if (digital_gain_0db_limit
) {
334 ret
= snd_soc_limit_volume(card
,
335 "Subwoofer Digital Playback Volume", 207);
337 dev_warn(card
->dev
, "Failed to set volume limit: %d\n",
340 ret
= snd_allo_piano_dsp_program(rtd
, set_mode
, rate
, set_lowpass
);
344 ret
= snd_soc_dai_set_bclk_ratio(cpu_dai
, sample_bits
* 2);
349 /* machine stream operations */
350 static struct snd_soc_ops snd_allo_piano_dac_ops
= {
351 .hw_params
= snd_allo_piano_dac_hw_params
,
354 static struct snd_soc_dai_link_component allo_piano_2_1_codecs
[] = {
356 .dai_name
= "pcm512x-hifi",
359 .dai_name
= "pcm512x-hifi",
363 static struct snd_soc_dai_link snd_allo_piano_dac_dai
[] = {
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
,
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
,
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
),
389 static int snd_allo_piano_dac_probe(struct platform_device
*pdev
)
392 struct snd_soc_card
*card
= &snd_allo_piano_dac
;
394 card
->dev
= &pdev
->dev
;
395 snd_allo_piano_dac
.dev
= &pdev
->dev
;
397 if (pdev
->dev
.of_node
) {
398 struct device_node
*i2s_node
;
399 struct snd_soc_dai_link
*dai
;
401 dai
= &snd_allo_piano_dac_dai
[0];
402 i2s_node
= of_parse_phandle(pdev
->dev
.of_node
,
403 "i2s-controller", 0);
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
;
412 digital_gain_0db_limit
=
413 !of_property_read_bool(pdev
->dev
.of_node
,
414 "allo,24db_digital_gain");
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
) {
420 "Property 'audio-codec' missing or invalid\n");
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
) {
428 "Property 'audio-codec' missing or invalid\n");
433 ret
= snd_soc_register_card(&snd_allo_piano_dac
);
435 dev_err(&pdev
->dev
, "snd_soc_register_card() failed: %d\n",
441 static int snd_allo_piano_dac_remove(struct platform_device
*pdev
)
443 return snd_soc_unregister_card(&snd_allo_piano_dac
);
446 static const struct of_device_id snd_allo_piano_dac_of_match
[] = {
447 { .compatible
= "allo,piano-dac-plus", },
451 MODULE_DEVICE_TABLE(of
, snd_allo_piano_dac_of_match
);
453 static struct platform_driver snd_allo_piano_dac_driver
= {
455 .name
= "snd-allo-piano-dac-plus",
456 .owner
= THIS_MODULE
,
457 .of_match_table
= snd_allo_piano_dac_of_match
,
459 .probe
= snd_allo_piano_dac_probe
,
460 .remove
= snd_allo_piano_dac_remove
,
463 module_platform_driver(snd_allo_piano_dac_driver
);
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");