]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - sound/soc/bcm/hifiberry_digi.c
ASoC: BCM:Add support for HiFiBerry Digi. Driver is based on the patched WM8804 driver.
[mirror_ubuntu-artful-kernel.git] / sound / soc / bcm / hifiberry_digi.c
1 /*
2 * ASoC Driver for HifiBerry Digi
3 *
4 * Author: Daniel Matuschek <info@crazy-audio.com>
5 * based on the HifiBerry DAC driver by Florian Meier <florian.meier@koalo.de>
6 * Copyright 2013
7 *
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.
11 *
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.
16 */
17
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20
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>
27
28 #include "../codecs/wm8804.h"
29
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");
33
34 #define CLK_44EN_RATE 22579200UL
35 #define CLK_48EN_RATE 24576000UL
36
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;
40
41 static int samplerate=44100;
42
43 static uint32_t snd_rpi_hifiberry_digi_enable_clock(int sample_rate)
44 {
45 switch (sample_rate) {
46 case 11025:
47 case 22050:
48 case 44100:
49 case 88200:
50 case 176400:
51 gpiod_set_value_cansleep(snd_rpi_hifiberry_clk44gpio, 1);
52 gpiod_set_value_cansleep(snd_rpi_hifiberry_clk48gpio, 0);
53 return CLK_44EN_RATE;
54 default:
55 gpiod_set_value_cansleep(snd_rpi_hifiberry_clk48gpio, 1);
56 gpiod_set_value_cansleep(snd_rpi_hifiberry_clk44gpio, 0);
57 return CLK_48EN_RATE;
58 }
59 }
60
61
62 static int snd_rpi_hifiberry_digi_init(struct snd_soc_pcm_runtime *rtd)
63 {
64 struct snd_soc_codec *codec = rtd->codec;
65
66 /* enable TX output */
67 snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0);
68
69 /* Initialize Digi+ Pro hardware */
70 if (snd_rpi_hifiberry_is_digipro) {
71 struct snd_soc_dai_link *dai = rtd->dai_link;
72
73 dai->name = "HiFiBerry Digi+ Pro";
74 dai->stream_name = "HiFiBerry Digi+ Pro HiFi";
75 }
76
77 return 0;
78 }
79
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);
85 return 0;
86 }
87
88 static void snd_rpi_hifiberry_digi_shutdown(struct snd_pcm_substream *substream) {
89 /* turn off output */
90 if (auto_shutdown_output) {
91 /* turn off 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);
95 }
96 }
97
98
99 static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream *substream,
100 struct snd_pcm_hw_params *params)
101 {
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;
106
107 int sysclk = 27000000; /* This is fixed on this board */
108
109 long mclk_freq=0;
110 int mclk_div=1;
111 int sampling_freq=1;
112
113 int ret;
114
115 samplerate = params_rate(params);
116
117 if (samplerate<=96000) {
118 mclk_freq=samplerate*256;
119 mclk_div=WM8804_MCLKDIV_256FS;
120 } else {
121 mclk_freq=samplerate*128;
122 mclk_div=WM8804_MCLKDIV_128FS;
123 }
124
125 if (snd_rpi_hifiberry_is_digipro)
126 sysclk = snd_rpi_hifiberry_digi_enable_clock(samplerate);
127
128 switch (samplerate) {
129 case 32000:
130 sampling_freq=0x03;
131 break;
132 case 44100:
133 sampling_freq=0x00;
134 break;
135 case 48000:
136 sampling_freq=0x02;
137 break;
138 case 88200:
139 sampling_freq=0x08;
140 break;
141 case 96000:
142 sampling_freq=0x0a;
143 break;
144 case 176400:
145 sampling_freq=0x0c;
146 break;
147 case 192000:
148 sampling_freq=0x0e;
149 break;
150 default:
151 dev_err(codec->dev,
152 "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n",
153 samplerate);
154 }
155
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);
158
159 ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL,
160 sysclk, SND_SOC_CLOCK_OUT);
161
162 if (ret < 0) {
163 dev_err(codec->dev,
164 "Failed to set WM8804 SYSCLK: %d\n", ret);
165 return ret;
166 }
167
168 /* Enable TX output */
169 snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0);
170
171 /* Power on */
172 snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0);
173
174 /* set sampling frequency status bits */
175 snd_soc_update_bits(codec, WM8804_SPDTX4, 0x0f, sampling_freq);
176
177 return snd_soc_dai_set_bclk_ratio(cpu_dai,64);
178 }
179
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,
185 };
186
187 static struct snd_soc_dai_link snd_rpi_hifiberry_digi_dai[] = {
188 {
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,
199 },
200 };
201
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),
209 };
210
211 static int snd_rpi_hifiberry_digi_probe(struct platform_device *pdev)
212 {
213 int ret = 0;
214
215 snd_rpi_hifiberry_digi.dev = &pdev->dev;
216
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);
222
223 if (i2s_node) {
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;
228 }
229
230 snd_rpi_hifiberry_is_digipro = 1;
231
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;
236
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;
241
242 }
243
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);
247
248 return ret;
249 }
250
251 static int snd_rpi_hifiberry_digi_remove(struct platform_device *pdev)
252 {
253 return snd_soc_unregister_card(&snd_rpi_hifiberry_digi);
254 }
255
256 static const struct of_device_id snd_rpi_hifiberry_digi_of_match[] = {
257 { .compatible = "hifiberry,hifiberry-digi", },
258 {},
259 };
260 MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_digi_of_match);
261
262 static struct platform_driver snd_rpi_hifiberry_digi_driver = {
263 .driver = {
264 .name = "snd-hifiberry-digi",
265 .owner = THIS_MODULE,
266 .of_match_table = snd_rpi_hifiberry_digi_of_match,
267 },
268 .probe = snd_rpi_hifiberry_digi_probe,
269 .remove = snd_rpi_hifiberry_digi_remove,
270 };
271
272 module_platform_driver(snd_rpi_hifiberry_digi_driver);
273
274 MODULE_AUTHOR("Daniel Matuschek <info@crazy-audio.com>");
275 MODULE_DESCRIPTION("ASoC Driver for HifiBerry Digi");
276 MODULE_LICENSE("GPL v2");