2 * ALSA ASoC Machine Driver for Allo Boss DAC
4 * Author: Baswaraj K <jaikumar@cem-solutions.net>
6 * based on code by Daniel Matuschek, Stuart MacLean <stuart@hifiberry.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>
21 #include <linux/clk.h>
22 #include <linux/delay.h>
24 #include <sound/core.h>
25 #include <sound/pcm.h>
26 #include <sound/pcm_params.h>
27 #include <sound/soc.h>
28 #include "../codecs/pcm512x.h"
30 #define ALLO_BOSS_NOCLOCK 0
31 #define ALLO_BOSS_CLK44EN 1
32 #define ALLO_BOSS_CLK48EN 2
35 struct regmap
*regmap
;
39 /* Clock rate of CLK44EN attached to GPIO6 pin */
40 #define CLK_44EN_RATE 45158400UL
41 /* Clock rate of CLK48EN attached to GPIO3 pin */
42 #define CLK_48EN_RATE 49152000UL
45 static bool snd_soc_allo_boss_master
;
46 static bool digital_gain_0db_limit
= true;
48 static void snd_allo_boss_select_clk(struct snd_soc_codec
*codec
,
52 case ALLO_BOSS_NOCLOCK
:
53 snd_soc_update_bits(codec
, PCM512x_GPIO_CONTROL_1
, 0x24, 0x00);
55 case ALLO_BOSS_CLK44EN
:
56 snd_soc_update_bits(codec
, PCM512x_GPIO_CONTROL_1
, 0x24, 0x20);
58 case ALLO_BOSS_CLK48EN
:
59 snd_soc_update_bits(codec
, PCM512x_GPIO_CONTROL_1
, 0x24, 0x04);
64 static void snd_allo_boss_clk_gpio(struct snd_soc_codec
*codec
)
66 snd_soc_update_bits(codec
, PCM512x_GPIO_EN
, 0x24, 0x24);
67 snd_soc_update_bits(codec
, PCM512x_GPIO_OUTPUT_3
, 0x0f, 0x02);
68 snd_soc_update_bits(codec
, PCM512x_GPIO_OUTPUT_6
, 0x0f, 0x02);
71 static bool snd_allo_boss_is_sclk(struct snd_soc_codec
*codec
)
75 sck
= snd_soc_read(codec
, PCM512x_RATE_DET_4
);
76 return (!(sck
& 0x40));
79 static bool snd_allo_boss_is_sclk_sleep(
80 struct snd_soc_codec
*codec
)
83 return snd_allo_boss_is_sclk(codec
);
86 static bool snd_allo_boss_is_master_card(struct snd_soc_codec
*codec
)
88 bool isClk44EN
, isClk48En
, isNoClk
;
90 snd_allo_boss_clk_gpio(codec
);
92 snd_allo_boss_select_clk(codec
, ALLO_BOSS_CLK44EN
);
93 isClk44EN
= snd_allo_boss_is_sclk_sleep(codec
);
95 snd_allo_boss_select_clk(codec
, ALLO_BOSS_NOCLOCK
);
96 isNoClk
= snd_allo_boss_is_sclk_sleep(codec
);
98 snd_allo_boss_select_clk(codec
, ALLO_BOSS_CLK48EN
);
99 isClk48En
= snd_allo_boss_is_sclk_sleep(codec
);
101 return (isClk44EN
&& isClk48En
&& !isNoClk
);
104 static int snd_allo_boss_clk_for_rate(int sample_rate
)
108 switch (sample_rate
) {
114 type
= ALLO_BOSS_CLK44EN
;
117 type
= ALLO_BOSS_CLK48EN
;
123 static void snd_allo_boss_set_sclk(struct snd_soc_codec
*codec
,
126 struct pcm512x_priv
*pcm512x
= snd_soc_codec_get_drvdata(codec
);
128 if (!IS_ERR(pcm512x
->sclk
)) {
131 ctype
= snd_allo_boss_clk_for_rate(sample_rate
);
132 clk_set_rate(pcm512x
->sclk
, (ctype
== ALLO_BOSS_CLK44EN
)
133 ? CLK_44EN_RATE
: CLK_48EN_RATE
);
134 snd_allo_boss_select_clk(codec
, ctype
);
138 static int snd_allo_boss_init(struct snd_soc_pcm_runtime
*rtd
)
140 struct snd_soc_codec
*codec
= rtd
->codec
;
141 struct pcm512x_priv
*priv
;
144 snd_soc_allo_boss_master
= false;
146 snd_soc_allo_boss_master
=
147 snd_allo_boss_is_master_card(codec
);
149 if (snd_soc_allo_boss_master
) {
150 struct snd_soc_dai_link
*dai
= rtd
->dai_link
;
152 dai
->name
= "BossDAC";
153 dai
->stream_name
= "BossDAC";
154 dai
->dai_fmt
= SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
155 | SND_SOC_DAIFMT_CBM_CFM
;
157 snd_soc_update_bits(codec
, PCM512x_BCLK_LRCLK_CFG
, 0x31, 0x11);
158 snd_soc_update_bits(codec
, PCM512x_MASTER_MODE
, 0x03, 0x03);
159 snd_soc_update_bits(codec
, PCM512x_MASTER_CLKDIV_2
, 0x7f, 63);
161 priv
= snd_soc_codec_get_drvdata(codec
);
162 priv
->sclk
= ERR_PTR(-ENOENT
);
165 snd_soc_update_bits(codec
, PCM512x_GPIO_EN
, 0x08, 0x08);
166 snd_soc_update_bits(codec
, PCM512x_GPIO_OUTPUT_4
, 0x0f, 0x02);
167 snd_soc_update_bits(codec
, PCM512x_GPIO_CONTROL_1
, 0x08, 0x08);
169 if (digital_gain_0db_limit
) {
171 struct snd_soc_card
*card
= rtd
->card
;
173 ret
= snd_soc_limit_volume(card
, "Digital Playback Volume",
176 dev_warn(card
->dev
, "Failed to set volume limit: %d\n",
183 static int snd_allo_boss_update_rate_den(
184 struct snd_pcm_substream
*substream
, struct snd_pcm_hw_params
*params
)
186 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
187 struct snd_soc_codec
*codec
= rtd
->codec
;
188 struct pcm512x_priv
*pcm512x
= snd_soc_codec_get_drvdata(codec
);
189 struct snd_ratnum
*rats_no_pll
;
190 unsigned int num
= 0, den
= 0;
193 rats_no_pll
= devm_kzalloc(rtd
->dev
, sizeof(*rats_no_pll
), GFP_KERNEL
);
197 rats_no_pll
->num
= clk_get_rate(pcm512x
->sclk
) / 64;
198 rats_no_pll
->den_min
= 1;
199 rats_no_pll
->den_max
= 128;
200 rats_no_pll
->den_step
= 1;
202 err
= snd_interval_ratnum(hw_param_interval(params
,
203 SNDRV_PCM_HW_PARAM_RATE
), 1, rats_no_pll
, &num
, &den
);
204 if (err
>= 0 && den
) {
205 params
->rate_num
= num
;
206 params
->rate_den
= den
;
209 devm_kfree(rtd
->dev
, rats_no_pll
);
213 static int snd_allo_boss_set_bclk_ratio_pro(
214 struct snd_soc_dai
*cpu_dai
, struct snd_pcm_hw_params
*params
)
216 int bratio
= snd_pcm_format_physical_width(params_format(params
))
217 * params_channels(params
);
218 return snd_soc_dai_set_bclk_ratio(cpu_dai
, bratio
);
221 static int snd_allo_boss_hw_params(
222 struct snd_pcm_substream
*substream
, struct snd_pcm_hw_params
*params
)
225 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
226 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
227 unsigned int sample_bits
=
228 snd_pcm_format_physical_width(params_format(params
));
230 if (snd_soc_allo_boss_master
) {
231 struct snd_soc_codec
*codec
= rtd
->codec
;
233 snd_allo_boss_set_sclk(codec
,
234 params_rate(params
));
236 ret
= snd_allo_boss_set_bclk_ratio_pro(cpu_dai
,
239 ret
= snd_allo_boss_update_rate_den(
242 if (snd_allo_boss_is_sclk(rtd
->codec
)) {
243 snd_soc_update_bits(rtd
->codec
, PCM512x_PLL_EN
,
245 snd_soc_update_bits(rtd
->codec
, PCM512x_PLL_REF
,
246 PCM512x_SREF
, PCM512x_SREF_BCK
);
247 dev_dbg(rtd
->codec
->dev
,
248 "Setting BCLK as input clock and Enable PLL\n");
250 snd_soc_update_bits(rtd
->codec
, PCM512x_PLL_EN
,
252 snd_soc_update_bits(rtd
->codec
, PCM512x_PLL_REF
,
253 PCM512x_SREF
, PCM512x_SREF_SCK
);
255 dev_dbg(rtd
->codec
->dev
,
256 "Setting SCLK as input clock and disabled PLL\n");
259 ret
= snd_soc_dai_set_bclk_ratio(cpu_dai
, sample_bits
* 2);
264 static int snd_allo_boss_startup(
265 struct snd_pcm_substream
*substream
)
267 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
268 struct snd_soc_codec
*codec
= rtd
->codec
;
270 snd_soc_update_bits(codec
, PCM512x_GPIO_CONTROL_1
, 0x08, 0x08);
274 static void snd_allo_boss_shutdown(
275 struct snd_pcm_substream
*substream
)
277 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
278 struct snd_soc_codec
*codec
= rtd
->codec
;
280 snd_soc_update_bits(codec
, PCM512x_GPIO_CONTROL_1
, 0x08, 0x00);
283 /* machine stream operations */
284 static struct snd_soc_ops snd_allo_boss_ops
= {
285 .hw_params
= snd_allo_boss_hw_params
,
286 .startup
= snd_allo_boss_startup
,
287 .shutdown
= snd_allo_boss_shutdown
,
290 static struct snd_soc_dai_link snd_allo_boss_dai
[] = {
293 .stream_name
= "Boss DAC HiFi",
294 .cpu_dai_name
= "bcm2708-i2s.0",
295 .codec_dai_name
= "pcm512x-hifi",
296 .platform_name
= "bcm2708-i2s.0",
297 .codec_name
= "pcm512x.1-004d",
298 .dai_fmt
= SND_SOC_DAIFMT_I2S
|
299 SND_SOC_DAIFMT_NB_NF
|
300 SND_SOC_DAIFMT_CBS_CFS
,
301 .ops
= &snd_allo_boss_ops
,
302 .init
= snd_allo_boss_init
,
306 /* audio machine driver */
307 static struct snd_soc_card snd_allo_boss
= {
309 .owner
= THIS_MODULE
,
310 .dai_link
= snd_allo_boss_dai
,
311 .num_links
= ARRAY_SIZE(snd_allo_boss_dai
),
314 static int snd_allo_boss_probe(struct platform_device
*pdev
)
318 snd_allo_boss
.dev
= &pdev
->dev
;
320 if (pdev
->dev
.of_node
) {
321 struct device_node
*i2s_node
;
322 struct snd_soc_dai_link
*dai
;
324 dai
= &snd_allo_boss_dai
[0];
325 i2s_node
= of_parse_phandle(pdev
->dev
.of_node
,
326 "i2s-controller", 0);
329 dai
->cpu_dai_name
= NULL
;
330 dai
->cpu_of_node
= i2s_node
;
331 dai
->platform_name
= NULL
;
332 dai
->platform_of_node
= i2s_node
;
335 digital_gain_0db_limit
= !of_property_read_bool(
336 pdev
->dev
.of_node
, "allo,24db_digital_gain");
337 slave
= of_property_read_bool(pdev
->dev
.of_node
,
341 ret
= snd_soc_register_card(&snd_allo_boss
);
344 "snd_soc_register_card() failed: %d\n", ret
);
349 static int snd_allo_boss_remove(struct platform_device
*pdev
)
351 return snd_soc_unregister_card(&snd_allo_boss
);
354 static const struct of_device_id snd_allo_boss_of_match
[] = {
355 { .compatible
= "allo,boss-dac", },
358 MODULE_DEVICE_TABLE(of
, snd_allo_boss_of_match
);
360 static struct platform_driver snd_allo_boss_driver
= {
362 .name
= "snd-allo-boss-dac",
363 .owner
= THIS_MODULE
,
364 .of_match_table
= snd_allo_boss_of_match
,
366 .probe
= snd_allo_boss_probe
,
367 .remove
= snd_allo_boss_remove
,
370 module_platform_driver(snd_allo_boss_driver
);
372 MODULE_AUTHOR("Baswaraj K <jaikumar@cem-solutions.net>");
373 MODULE_DESCRIPTION("ALSA ASoC Machine Driver for Allo Boss DAC");
374 MODULE_LICENSE("GPL v2");