2 * ASoC Driver for HifiBerry Digi
4 * Author: Daniel Matuschek <info@crazy-audio.com>
5 * based on the HifiBerry DAC driver by Florian Meier <florian.meier@koalo.de>
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.
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.
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
21 #include <sound/core.h>
22 #include <sound/pcm.h>
23 #include <sound/pcm_params.h>
24 #include <sound/soc.h>
25 #include <sound/jack.h>
26 #include <linux/gpio/consumer.h>
28 #include "../codecs/wm8804.h"
30 static short int auto_shutdown_output
= 0;
31 module_param(auto_shutdown_output
, short, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
);
32 MODULE_PARM_DESC(auto_shutdown_output
, "Shutdown SP/DIF output if playback is stopped");
34 #define CLK_44EN_RATE 22579200UL
35 #define CLK_48EN_RATE 24576000UL
37 static bool snd_rpi_hifiberry_is_digipro
;
38 static struct gpio_desc
*snd_rpi_hifiberry_clk44gpio
;
39 static struct gpio_desc
*snd_rpi_hifiberry_clk48gpio
;
41 static int samplerate
=44100;
43 static uint32_t snd_rpi_hifiberry_digi_enable_clock(int sample_rate
)
45 switch (sample_rate
) {
51 gpiod_set_value_cansleep(snd_rpi_hifiberry_clk44gpio
, 1);
52 gpiod_set_value_cansleep(snd_rpi_hifiberry_clk48gpio
, 0);
55 gpiod_set_value_cansleep(snd_rpi_hifiberry_clk48gpio
, 1);
56 gpiod_set_value_cansleep(snd_rpi_hifiberry_clk44gpio
, 0);
62 static int snd_rpi_hifiberry_digi_init(struct snd_soc_pcm_runtime
*rtd
)
64 struct snd_soc_codec
*codec
= rtd
->codec
;
66 /* enable TX output */
67 snd_soc_update_bits(codec
, WM8804_PWRDN
, 0x4, 0x0);
69 /* Initialize Digi+ Pro hardware */
70 if (snd_rpi_hifiberry_is_digipro
) {
71 struct snd_soc_dai_link
*dai
= rtd
->dai_link
;
73 dai
->name
= "HiFiBerry Digi+ Pro";
74 dai
->stream_name
= "HiFiBerry Digi+ Pro HiFi";
80 static int snd_rpi_hifiberry_digi_startup(struct snd_pcm_substream
*substream
) {
81 /* turn on digital output */
82 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
83 struct snd_soc_codec
*codec
= rtd
->codec
;
84 snd_soc_update_bits(codec
, WM8804_PWRDN
, 0x3c, 0x00);
88 static void snd_rpi_hifiberry_digi_shutdown(struct snd_pcm_substream
*substream
) {
90 if (auto_shutdown_output
) {
92 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
93 struct snd_soc_codec
*codec
= rtd
->codec
;
94 snd_soc_update_bits(codec
, WM8804_PWRDN
, 0x3c, 0x3c);
99 static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream
*substream
,
100 struct snd_pcm_hw_params
*params
)
102 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
103 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
104 struct snd_soc_codec
*codec
= rtd
->codec
;
105 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
107 int sysclk
= 27000000; /* This is fixed on this board */
115 samplerate
= params_rate(params
);
117 if (samplerate
<=96000) {
118 mclk_freq
=samplerate
*256;
119 mclk_div
=WM8804_MCLKDIV_256FS
;
121 mclk_freq
=samplerate
*128;
122 mclk_div
=WM8804_MCLKDIV_128FS
;
125 if (snd_rpi_hifiberry_is_digipro
)
126 sysclk
= snd_rpi_hifiberry_digi_enable_clock(samplerate
);
128 switch (samplerate
) {
152 "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n",
156 snd_soc_dai_set_clkdiv(codec_dai
, WM8804_MCLK_DIV
, mclk_div
);
157 snd_soc_dai_set_pll(codec_dai
, 0, 0, sysclk
, mclk_freq
);
159 ret
= snd_soc_dai_set_sysclk(codec_dai
, WM8804_TX_CLKSRC_PLL
,
160 sysclk
, SND_SOC_CLOCK_OUT
);
164 "Failed to set WM8804 SYSCLK: %d\n", ret
);
168 /* Enable TX output */
169 snd_soc_update_bits(codec
, WM8804_PWRDN
, 0x4, 0x0);
172 snd_soc_update_bits(codec
, WM8804_PWRDN
, 0x9, 0);
174 /* set sampling frequency status bits */
175 snd_soc_update_bits(codec
, WM8804_SPDTX4
, 0x0f, sampling_freq
);
177 return snd_soc_dai_set_bclk_ratio(cpu_dai
,64);
180 /* machine stream operations */
181 static struct snd_soc_ops snd_rpi_hifiberry_digi_ops
= {
182 .hw_params
= snd_rpi_hifiberry_digi_hw_params
,
183 .startup
= snd_rpi_hifiberry_digi_startup
,
184 .shutdown
= snd_rpi_hifiberry_digi_shutdown
,
187 static struct snd_soc_dai_link snd_rpi_hifiberry_digi_dai
[] = {
189 .name
= "HifiBerry Digi",
190 .stream_name
= "HifiBerry Digi HiFi",
191 .cpu_dai_name
= "bcm2708-i2s.0",
192 .codec_dai_name
= "wm8804-spdif",
193 .platform_name
= "bcm2708-i2s.0",
194 .codec_name
= "wm8804.1-003b",
195 .dai_fmt
= SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
|
196 SND_SOC_DAIFMT_CBM_CFM
,
197 .ops
= &snd_rpi_hifiberry_digi_ops
,
198 .init
= snd_rpi_hifiberry_digi_init
,
202 /* audio machine driver */
203 static struct snd_soc_card snd_rpi_hifiberry_digi
= {
204 .name
= "snd_rpi_hifiberry_digi",
205 .driver_name
= "HifiberryDigi",
206 .owner
= THIS_MODULE
,
207 .dai_link
= snd_rpi_hifiberry_digi_dai
,
208 .num_links
= ARRAY_SIZE(snd_rpi_hifiberry_digi_dai
),
211 static int snd_rpi_hifiberry_digi_probe(struct platform_device
*pdev
)
215 snd_rpi_hifiberry_digi
.dev
= &pdev
->dev
;
217 if (pdev
->dev
.of_node
) {
218 struct device_node
*i2s_node
;
219 struct snd_soc_dai_link
*dai
= &snd_rpi_hifiberry_digi_dai
[0];
220 i2s_node
= of_parse_phandle(pdev
->dev
.of_node
,
221 "i2s-controller", 0);
224 dai
->cpu_dai_name
= NULL
;
225 dai
->cpu_of_node
= i2s_node
;
226 dai
->platform_name
= NULL
;
227 dai
->platform_of_node
= i2s_node
;
230 snd_rpi_hifiberry_is_digipro
= 1;
232 snd_rpi_hifiberry_clk44gpio
=
233 devm_gpiod_get(&pdev
->dev
, "clock44", GPIOD_OUT_LOW
);
234 if (IS_ERR(snd_rpi_hifiberry_clk44gpio
))
235 snd_rpi_hifiberry_is_digipro
= 0;
237 snd_rpi_hifiberry_clk48gpio
=
238 devm_gpiod_get(&pdev
->dev
, "clock48", GPIOD_OUT_LOW
);
239 if (IS_ERR(snd_rpi_hifiberry_clk48gpio
))
240 snd_rpi_hifiberry_is_digipro
= 0;
244 ret
= snd_soc_register_card(&snd_rpi_hifiberry_digi
);
245 if (ret
&& ret
!= -EPROBE_DEFER
)
246 dev_err(&pdev
->dev
, "snd_soc_register_card() failed: %d\n", ret
);
251 static int snd_rpi_hifiberry_digi_remove(struct platform_device
*pdev
)
253 return snd_soc_unregister_card(&snd_rpi_hifiberry_digi
);
256 static const struct of_device_id snd_rpi_hifiberry_digi_of_match
[] = {
257 { .compatible
= "hifiberry,hifiberry-digi", },
260 MODULE_DEVICE_TABLE(of
, snd_rpi_hifiberry_digi_of_match
);
262 static struct platform_driver snd_rpi_hifiberry_digi_driver
= {
264 .name
= "snd-hifiberry-digi",
265 .owner
= THIS_MODULE
,
266 .of_match_table
= snd_rpi_hifiberry_digi_of_match
,
268 .probe
= snd_rpi_hifiberry_digi_probe
,
269 .remove
= snd_rpi_hifiberry_digi_remove
,
272 module_platform_driver(snd_rpi_hifiberry_digi_driver
);
274 MODULE_AUTHOR("Daniel Matuschek <info@crazy-audio.com>");
275 MODULE_DESCRIPTION("ASoC Driver for HifiBerry Digi");
276 MODULE_LICENSE("GPL v2");