]>
Commit | Line | Data |
---|---|---|
5bdc2c16 B |
1 | /* |
2 | * ALSA ASoC Machine Driver for Allo Boss DAC | |
3 | * | |
4 | * Author: Baswaraj K <jaikumar@cem-solutions.net> | |
78f28c4c B |
5 | * Copyright 2017 |
6 | * based on code by Daniel Matuschek, | |
7 | * Stuart MacLean <stuart@hifiberry.com> | |
5bdc2c16 B |
8 | * based on code by Florian Meier <florian.meier@koalo.de> |
9 | * | |
10 | * This program is free software; you can redistribute it and/or | |
11 | * modify it under the terms of the GNU General Public License | |
12 | * version 2 as published by the Free Software Foundation. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | */ | |
19 | ||
20 | #include <linux/module.h> | |
78f28c4c | 21 | #include <linux/gpio/consumer.h> |
5bdc2c16 B |
22 | #include <linux/platform_device.h> |
23 | #include <linux/clk.h> | |
24 | #include <linux/delay.h> | |
25 | ||
26 | #include <sound/core.h> | |
27 | #include <sound/pcm.h> | |
28 | #include <sound/pcm_params.h> | |
29 | #include <sound/soc.h> | |
30 | #include "../codecs/pcm512x.h" | |
31 | ||
32 | #define ALLO_BOSS_NOCLOCK 0 | |
33 | #define ALLO_BOSS_CLK44EN 1 | |
34 | #define ALLO_BOSS_CLK48EN 2 | |
35 | ||
36 | struct pcm512x_priv { | |
37 | struct regmap *regmap; | |
38 | struct clk *sclk; | |
39 | }; | |
40 | ||
78f28c4c B |
41 | static struct gpio_desc *mute_gpio; |
42 | ||
5bdc2c16 B |
43 | /* Clock rate of CLK44EN attached to GPIO6 pin */ |
44 | #define CLK_44EN_RATE 45158400UL | |
45 | /* Clock rate of CLK48EN attached to GPIO3 pin */ | |
46 | #define CLK_48EN_RATE 49152000UL | |
47 | ||
48 | static bool slave; | |
49 | static bool snd_soc_allo_boss_master; | |
50 | static bool digital_gain_0db_limit = true; | |
51 | ||
52 | static void snd_allo_boss_select_clk(struct snd_soc_codec *codec, | |
53 | int clk_id) | |
54 | { | |
55 | switch (clk_id) { | |
56 | case ALLO_BOSS_NOCLOCK: | |
57 | snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x24, 0x00); | |
58 | break; | |
59 | case ALLO_BOSS_CLK44EN: | |
60 | snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x24, 0x20); | |
61 | break; | |
62 | case ALLO_BOSS_CLK48EN: | |
63 | snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x24, 0x04); | |
64 | break; | |
65 | } | |
66 | } | |
67 | ||
68 | static void snd_allo_boss_clk_gpio(struct snd_soc_codec *codec) | |
69 | { | |
70 | snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x24, 0x24); | |
71 | snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_3, 0x0f, 0x02); | |
72 | snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_6, 0x0f, 0x02); | |
73 | } | |
74 | ||
75 | static bool snd_allo_boss_is_sclk(struct snd_soc_codec *codec) | |
76 | { | |
77 | int sck; | |
78 | ||
79 | sck = snd_soc_read(codec, PCM512x_RATE_DET_4); | |
80 | return (!(sck & 0x40)); | |
81 | } | |
82 | ||
83 | static bool snd_allo_boss_is_sclk_sleep( | |
84 | struct snd_soc_codec *codec) | |
85 | { | |
86 | msleep(2); | |
87 | return snd_allo_boss_is_sclk(codec); | |
88 | } | |
89 | ||
90 | static bool snd_allo_boss_is_master_card(struct snd_soc_codec *codec) | |
91 | { | |
92 | bool isClk44EN, isClk48En, isNoClk; | |
93 | ||
94 | snd_allo_boss_clk_gpio(codec); | |
95 | ||
96 | snd_allo_boss_select_clk(codec, ALLO_BOSS_CLK44EN); | |
97 | isClk44EN = snd_allo_boss_is_sclk_sleep(codec); | |
98 | ||
99 | snd_allo_boss_select_clk(codec, ALLO_BOSS_NOCLOCK); | |
100 | isNoClk = snd_allo_boss_is_sclk_sleep(codec); | |
101 | ||
102 | snd_allo_boss_select_clk(codec, ALLO_BOSS_CLK48EN); | |
103 | isClk48En = snd_allo_boss_is_sclk_sleep(codec); | |
104 | ||
105 | return (isClk44EN && isClk48En && !isNoClk); | |
106 | } | |
107 | ||
108 | static int snd_allo_boss_clk_for_rate(int sample_rate) | |
109 | { | |
110 | int type; | |
111 | ||
112 | switch (sample_rate) { | |
113 | case 11025: | |
114 | case 22050: | |
115 | case 44100: | |
116 | case 88200: | |
117 | case 176400: | |
78f28c4c | 118 | case 352800: |
5bdc2c16 B |
119 | type = ALLO_BOSS_CLK44EN; |
120 | break; | |
121 | default: | |
122 | type = ALLO_BOSS_CLK48EN; | |
123 | break; | |
124 | } | |
125 | return type; | |
126 | } | |
127 | ||
128 | static void snd_allo_boss_set_sclk(struct snd_soc_codec *codec, | |
129 | int sample_rate) | |
130 | { | |
131 | struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); | |
132 | ||
133 | if (!IS_ERR(pcm512x->sclk)) { | |
134 | int ctype; | |
135 | ||
136 | ctype = snd_allo_boss_clk_for_rate(sample_rate); | |
137 | clk_set_rate(pcm512x->sclk, (ctype == ALLO_BOSS_CLK44EN) | |
138 | ? CLK_44EN_RATE : CLK_48EN_RATE); | |
139 | snd_allo_boss_select_clk(codec, ctype); | |
140 | } | |
141 | } | |
142 | ||
143 | static int snd_allo_boss_init(struct snd_soc_pcm_runtime *rtd) | |
144 | { | |
145 | struct snd_soc_codec *codec = rtd->codec; | |
78f28c4c | 146 | struct pcm512x_priv *priv = snd_soc_codec_get_drvdata(codec); |
5bdc2c16 B |
147 | |
148 | if (slave) | |
149 | snd_soc_allo_boss_master = false; | |
150 | else | |
151 | snd_soc_allo_boss_master = | |
152 | snd_allo_boss_is_master_card(codec); | |
153 | ||
154 | if (snd_soc_allo_boss_master) { | |
155 | struct snd_soc_dai_link *dai = rtd->dai_link; | |
156 | ||
157 | dai->name = "BossDAC"; | |
78f28c4c | 158 | dai->stream_name = "Boss DAC HiFi [Master]"; |
5bdc2c16 B |
159 | dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
160 | | SND_SOC_DAIFMT_CBM_CFM; | |
161 | ||
162 | snd_soc_update_bits(codec, PCM512x_BCLK_LRCLK_CFG, 0x31, 0x11); | |
163 | snd_soc_update_bits(codec, PCM512x_MASTER_MODE, 0x03, 0x03); | |
164 | snd_soc_update_bits(codec, PCM512x_MASTER_CLKDIV_2, 0x7f, 63); | |
78f28c4c B |
165 | /* |
166 | * Default sclk to CLK_48EN_RATE, otherwise codec | |
167 | * pcm512x_dai_startup_master method could call | |
168 | * snd_pcm_hw_constraint_ratnums using CLK_44EN/64 | |
169 | * which will mask 384k sample rate. | |
170 | */ | |
171 | if (!IS_ERR(priv->sclk)) | |
172 | clk_set_rate(priv->sclk, CLK_48EN_RATE); | |
5bdc2c16 | 173 | } else { |
5bdc2c16 B |
174 | priv->sclk = ERR_PTR(-ENOENT); |
175 | } | |
176 | ||
177 | snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); | |
178 | snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); | |
179 | snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); | |
180 | ||
181 | if (digital_gain_0db_limit) { | |
182 | int ret; | |
183 | struct snd_soc_card *card = rtd->card; | |
184 | ||
185 | ret = snd_soc_limit_volume(card, "Digital Playback Volume", | |
186 | 207); | |
187 | if (ret < 0) | |
188 | dev_warn(card->dev, "Failed to set volume limit: %d\n", | |
189 | ret); | |
190 | } | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
195 | static int snd_allo_boss_update_rate_den( | |
196 | struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) | |
197 | { | |
198 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
199 | struct snd_soc_codec *codec = rtd->codec; | |
200 | struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); | |
201 | struct snd_ratnum *rats_no_pll; | |
202 | unsigned int num = 0, den = 0; | |
203 | int err; | |
204 | ||
205 | rats_no_pll = devm_kzalloc(rtd->dev, sizeof(*rats_no_pll), GFP_KERNEL); | |
206 | if (!rats_no_pll) | |
207 | return -ENOMEM; | |
208 | ||
209 | rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64; | |
210 | rats_no_pll->den_min = 1; | |
211 | rats_no_pll->den_max = 128; | |
212 | rats_no_pll->den_step = 1; | |
213 | ||
214 | err = snd_interval_ratnum(hw_param_interval(params, | |
215 | SNDRV_PCM_HW_PARAM_RATE), 1, rats_no_pll, &num, &den); | |
216 | if (err >= 0 && den) { | |
217 | params->rate_num = num; | |
218 | params->rate_den = den; | |
219 | } | |
220 | ||
221 | devm_kfree(rtd->dev, rats_no_pll); | |
222 | return 0; | |
223 | } | |
224 | ||
225 | static int snd_allo_boss_set_bclk_ratio_pro( | |
226 | struct snd_soc_dai *cpu_dai, struct snd_pcm_hw_params *params) | |
227 | { | |
228 | int bratio = snd_pcm_format_physical_width(params_format(params)) | |
229 | * params_channels(params); | |
230 | return snd_soc_dai_set_bclk_ratio(cpu_dai, bratio); | |
231 | } | |
232 | ||
78f28c4c B |
233 | static void snd_allo_boss_gpio_mute(struct snd_soc_card *card) |
234 | { | |
235 | if (mute_gpio) | |
236 | gpiod_set_value_cansleep(mute_gpio, 1); | |
237 | } | |
238 | ||
239 | static void snd_allo_boss_gpio_unmute(struct snd_soc_card *card) | |
240 | { | |
241 | if (mute_gpio) | |
242 | gpiod_set_value_cansleep(mute_gpio, 0); | |
243 | } | |
244 | ||
245 | static int snd_allo_boss_set_bias_level(struct snd_soc_card *card, | |
246 | struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) | |
247 | { | |
248 | struct snd_soc_pcm_runtime *rtd; | |
249 | struct snd_soc_dai *codec_dai; | |
250 | ||
251 | rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); | |
252 | codec_dai = rtd->codec_dai; | |
253 | ||
254 | if (dapm->dev != codec_dai->dev) | |
255 | return 0; | |
256 | ||
257 | switch (level) { | |
258 | case SND_SOC_BIAS_PREPARE: | |
259 | if (dapm->bias_level != SND_SOC_BIAS_STANDBY) | |
260 | break; | |
261 | /* UNMUTE DAC */ | |
262 | snd_allo_boss_gpio_unmute(card); | |
263 | break; | |
264 | ||
265 | case SND_SOC_BIAS_STANDBY: | |
266 | if (dapm->bias_level != SND_SOC_BIAS_PREPARE) | |
267 | break; | |
268 | /* MUTE DAC */ | |
269 | snd_allo_boss_gpio_mute(card); | |
270 | break; | |
271 | ||
272 | default: | |
273 | break; | |
274 | } | |
275 | ||
276 | return 0; | |
277 | } | |
278 | ||
5bdc2c16 B |
279 | static int snd_allo_boss_hw_params( |
280 | struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) | |
281 | { | |
282 | int ret = 0; | |
283 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
284 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | |
285 | unsigned int sample_bits = | |
286 | snd_pcm_format_physical_width(params_format(params)); | |
287 | ||
288 | if (snd_soc_allo_boss_master) { | |
289 | struct snd_soc_codec *codec = rtd->codec; | |
290 | ||
291 | snd_allo_boss_set_sclk(codec, | |
292 | params_rate(params)); | |
293 | ||
294 | ret = snd_allo_boss_set_bclk_ratio_pro(cpu_dai, | |
295 | params); | |
296 | if (!ret) | |
297 | ret = snd_allo_boss_update_rate_den( | |
298 | substream, params); | |
299 | } else { | |
5bdc2c16 B |
300 | ret = snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2); |
301 | } | |
302 | return ret; | |
303 | } | |
304 | ||
305 | static int snd_allo_boss_startup( | |
306 | struct snd_pcm_substream *substream) | |
307 | { | |
308 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
309 | struct snd_soc_codec *codec = rtd->codec; | |
78f28c4c | 310 | struct snd_soc_card *card = rtd->card; |
5bdc2c16 B |
311 | |
312 | snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); | |
78f28c4c B |
313 | snd_allo_boss_gpio_mute(card); |
314 | ||
315 | if (snd_soc_allo_boss_master) { | |
316 | struct pcm512x_priv *priv = snd_soc_codec_get_drvdata(codec); | |
317 | /* | |
318 | * Default sclk to CLK_48EN_RATE, otherwise codec | |
319 | * pcm512x_dai_startup_master method could call | |
320 | * snd_pcm_hw_constraint_ratnums using CLK_44EN/64 | |
321 | * which will mask 384k sample rate. | |
322 | */ | |
323 | if (!IS_ERR(priv->sclk)) | |
324 | clk_set_rate(priv->sclk, CLK_48EN_RATE); | |
325 | } | |
326 | ||
5bdc2c16 B |
327 | return 0; |
328 | } | |
329 | ||
330 | static void snd_allo_boss_shutdown( | |
331 | struct snd_pcm_substream *substream) | |
332 | { | |
333 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
334 | struct snd_soc_codec *codec = rtd->codec; | |
335 | ||
336 | snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x00); | |
337 | } | |
338 | ||
78f28c4c B |
339 | static int snd_allo_boss_prepare( |
340 | struct snd_pcm_substream *substream) | |
341 | { | |
342 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
343 | struct snd_soc_card *card = rtd->card; | |
344 | ||
345 | snd_allo_boss_gpio_unmute(card); | |
346 | return 0; | |
347 | } | |
5bdc2c16 B |
348 | /* machine stream operations */ |
349 | static struct snd_soc_ops snd_allo_boss_ops = { | |
350 | .hw_params = snd_allo_boss_hw_params, | |
351 | .startup = snd_allo_boss_startup, | |
352 | .shutdown = snd_allo_boss_shutdown, | |
78f28c4c | 353 | .prepare = snd_allo_boss_prepare, |
5bdc2c16 B |
354 | }; |
355 | ||
356 | static struct snd_soc_dai_link snd_allo_boss_dai[] = { | |
357 | { | |
358 | .name = "Boss DAC", | |
359 | .stream_name = "Boss DAC HiFi", | |
360 | .cpu_dai_name = "bcm2708-i2s.0", | |
361 | .codec_dai_name = "pcm512x-hifi", | |
362 | .platform_name = "bcm2708-i2s.0", | |
363 | .codec_name = "pcm512x.1-004d", | |
364 | .dai_fmt = SND_SOC_DAIFMT_I2S | | |
365 | SND_SOC_DAIFMT_NB_NF | | |
366 | SND_SOC_DAIFMT_CBS_CFS, | |
367 | .ops = &snd_allo_boss_ops, | |
368 | .init = snd_allo_boss_init, | |
369 | }, | |
370 | }; | |
371 | ||
372 | /* audio machine driver */ | |
373 | static struct snd_soc_card snd_allo_boss = { | |
374 | .name = "BossDAC", | |
375 | .owner = THIS_MODULE, | |
376 | .dai_link = snd_allo_boss_dai, | |
377 | .num_links = ARRAY_SIZE(snd_allo_boss_dai), | |
378 | }; | |
379 | ||
380 | static int snd_allo_boss_probe(struct platform_device *pdev) | |
381 | { | |
382 | int ret = 0; | |
383 | ||
384 | snd_allo_boss.dev = &pdev->dev; | |
385 | ||
386 | if (pdev->dev.of_node) { | |
387 | struct device_node *i2s_node; | |
388 | struct snd_soc_dai_link *dai; | |
389 | ||
390 | dai = &snd_allo_boss_dai[0]; | |
391 | i2s_node = of_parse_phandle(pdev->dev.of_node, | |
392 | "i2s-controller", 0); | |
393 | ||
394 | if (i2s_node) { | |
395 | dai->cpu_dai_name = NULL; | |
396 | dai->cpu_of_node = i2s_node; | |
397 | dai->platform_name = NULL; | |
398 | dai->platform_of_node = i2s_node; | |
399 | } | |
400 | ||
401 | digital_gain_0db_limit = !of_property_read_bool( | |
402 | pdev->dev.of_node, "allo,24db_digital_gain"); | |
403 | slave = of_property_read_bool(pdev->dev.of_node, | |
78f28c4c B |
404 | "allo,slave"); |
405 | ||
406 | mute_gpio = devm_gpiod_get_optional(&pdev->dev, "mute", | |
407 | GPIOD_OUT_LOW); | |
408 | if (IS_ERR(mute_gpio)) { | |
409 | ret = PTR_ERR(mute_gpio); | |
410 | dev_err(&pdev->dev, | |
411 | "failed to get mute gpio: %d\n", ret); | |
412 | return ret; | |
413 | } | |
5bdc2c16 | 414 | |
78f28c4c B |
415 | if (mute_gpio) |
416 | snd_allo_boss.set_bias_level = | |
417 | snd_allo_boss_set_bias_level; | |
5bdc2c16 | 418 | |
78f28c4c B |
419 | ret = snd_soc_register_card(&snd_allo_boss); |
420 | if (ret) { | |
421 | dev_err(&pdev->dev, | |
422 | "snd_soc_register_card() failed: %d\n", ret); | |
423 | return ret; | |
424 | } | |
425 | ||
426 | if (mute_gpio) | |
427 | snd_allo_boss_gpio_mute(&snd_allo_boss); | |
428 | ||
429 | return 0; | |
430 | } | |
431 | ||
432 | return -EINVAL; | |
5bdc2c16 B |
433 | } |
434 | ||
435 | static int snd_allo_boss_remove(struct platform_device *pdev) | |
436 | { | |
78f28c4c | 437 | snd_allo_boss_gpio_mute(&snd_allo_boss); |
5bdc2c16 B |
438 | return snd_soc_unregister_card(&snd_allo_boss); |
439 | } | |
440 | ||
441 | static const struct of_device_id snd_allo_boss_of_match[] = { | |
442 | { .compatible = "allo,boss-dac", }, | |
443 | { /* sentinel */ }, | |
444 | }; | |
445 | MODULE_DEVICE_TABLE(of, snd_allo_boss_of_match); | |
446 | ||
447 | static struct platform_driver snd_allo_boss_driver = { | |
448 | .driver = { | |
449 | .name = "snd-allo-boss-dac", | |
450 | .owner = THIS_MODULE, | |
451 | .of_match_table = snd_allo_boss_of_match, | |
452 | }, | |
453 | .probe = snd_allo_boss_probe, | |
454 | .remove = snd_allo_boss_remove, | |
455 | }; | |
456 | ||
457 | module_platform_driver(snd_allo_boss_driver); | |
458 | ||
459 | MODULE_AUTHOR("Baswaraj K <jaikumar@cem-solutions.net>"); | |
460 | MODULE_DESCRIPTION("ALSA ASoC Machine Driver for Allo Boss DAC"); | |
461 | MODULE_LICENSE("GPL v2"); |