xref: /openbmc/linux/sound/soc/samsung/odroid.c (revision b5c16a24)
1 /*
2  * Copyright (C) 2017 Samsung Electronics Co., Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8 
9 #include <linux/clk.h>
10 #include <linux/clk-provider.h>
11 #include <linux/of.h>
12 #include <linux/of_device.h>
13 #include <linux/module.h>
14 #include <sound/soc.h>
15 #include <sound/pcm_params.h>
16 #include "i2s.h"
17 #include "i2s-regs.h"
18 
19 struct odroid_priv {
20 	struct snd_soc_card card;
21 	struct clk *clk_i2s_bus;
22 	struct clk *sclk_i2s;
23 
24 	/* Spinlock protecting fields below */
25 	spinlock_t lock;
26 	unsigned int be_sample_rate;
27 	bool be_active;
28 };
29 
30 static int odroid_card_fe_startup(struct snd_pcm_substream *substream)
31 {
32 	struct snd_pcm_runtime *runtime = substream->runtime;
33 
34 	snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
35 
36 	return 0;
37 }
38 
39 static int odroid_card_fe_hw_params(struct snd_pcm_substream *substream,
40 				      struct snd_pcm_hw_params *params)
41 {
42 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
43 	struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
44 	unsigned long flags;
45 	int ret = 0;
46 
47 	spin_lock_irqsave(&priv->lock, flags);
48 	if (priv->be_active && priv->be_sample_rate != params_rate(params))
49 		ret = -EINVAL;
50 	spin_unlock_irqrestore(&priv->lock, flags);
51 
52 	return ret;
53 }
54 
55 static const struct snd_soc_ops odroid_card_fe_ops = {
56 	.startup = odroid_card_fe_startup,
57 	.hw_params = odroid_card_fe_hw_params,
58 };
59 
60 static int odroid_card_be_hw_params(struct snd_pcm_substream *substream,
61 				      struct snd_pcm_hw_params *params)
62 {
63 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
64 	struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
65 	unsigned int pll_freq, rclk_freq, rfs;
66 	unsigned long flags;
67 	int ret;
68 
69 	switch (params_rate(params)) {
70 	case 64000:
71 		pll_freq = 196608001U;
72 		rfs = 384;
73 		break;
74 	case 44100:
75 	case 88200:
76 		pll_freq = 180633609U;
77 		rfs = 512;
78 		break;
79 	case 32000:
80 	case 48000:
81 	case 96000:
82 		pll_freq = 196608001U;
83 		rfs = 512;
84 		break;
85 	default:
86 		return -EINVAL;
87 	}
88 
89 	ret = clk_set_rate(priv->clk_i2s_bus, pll_freq / 2 + 1);
90 	if (ret < 0)
91 		return ret;
92 
93 	/*
94 	 *  We add 1 to the rclk_freq value in order to avoid too low clock
95 	 *  frequency values due to the EPLL output frequency not being exact
96 	 *  multiple of the audio sampling rate.
97 	 */
98 	rclk_freq = params_rate(params) * rfs + 1;
99 
100 	ret = clk_set_rate(priv->sclk_i2s, rclk_freq);
101 	if (ret < 0)
102 		return ret;
103 
104 	if (rtd->num_codecs > 1) {
105 		struct snd_soc_dai *codec_dai = rtd->codec_dais[1];
106 
107 		ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk_freq,
108 					     SND_SOC_CLOCK_IN);
109 		if (ret < 0)
110 			return ret;
111 	}
112 
113 	spin_lock_irqsave(&priv->lock, flags);
114 	priv->be_sample_rate = params_rate(params);
115 	spin_unlock_irqrestore(&priv->lock, flags);
116 
117 	return 0;
118 }
119 
120 static int odroid_card_be_trigger(struct snd_pcm_substream *substream, int cmd)
121 {
122 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
123 	struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
124 	unsigned long flags;
125 
126 	spin_lock_irqsave(&priv->lock, flags);
127 
128 	switch (cmd) {
129 	case SNDRV_PCM_TRIGGER_START:
130 	case SNDRV_PCM_TRIGGER_RESUME:
131 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
132 		priv->be_active = true;
133 		break;
134 
135 	case SNDRV_PCM_TRIGGER_STOP:
136 	case SNDRV_PCM_TRIGGER_SUSPEND:
137 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
138 		priv->be_active = false;
139 		break;
140 	}
141 
142 	spin_unlock_irqrestore(&priv->lock, flags);
143 
144 	return 0;
145 }
146 
147 static const struct snd_soc_ops odroid_card_be_ops = {
148 	.hw_params = odroid_card_be_hw_params,
149 	.trigger = odroid_card_be_trigger,
150 };
151 
152 static struct snd_soc_dai_link odroid_card_dais[] = {
153 	{
154 		/* Primary FE <-> BE link */
155 		.codec_name = "snd-soc-dummy",
156 		.codec_dai_name = "snd-soc-dummy-dai",
157 		.ops = &odroid_card_fe_ops,
158 		.name = "Primary",
159 		.stream_name = "Primary",
160 		.platform_name = "3830000.i2s",
161 		.dynamic = 1,
162 		.dpcm_playback = 1,
163 	}, {
164 		/* BE <-> CODECs link */
165 		.name = "I2S Mixer",
166 		.cpu_name = "snd-soc-dummy",
167 		.cpu_dai_name = "snd-soc-dummy-dai",
168 		.platform_name = "snd-soc-dummy",
169 		.ops = &odroid_card_be_ops,
170 		.no_pcm = 1,
171 		.dpcm_playback = 1,
172 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
173 				SND_SOC_DAIFMT_CBS_CFS,
174 	}, {
175 		/* Secondary FE <-> BE link */
176 		.playback_only = 1,
177 		.codec_name = "snd-soc-dummy",
178 		.codec_dai_name = "snd-soc-dummy-dai",
179 		.ops = &odroid_card_fe_ops,
180 		.name = "Secondary",
181 		.stream_name = "Secondary",
182 		.platform_name = "samsung-i2s-sec",
183 		.dynamic = 1,
184 		.dpcm_playback = 1,
185 	}
186 };
187 
188 static int odroid_audio_probe(struct platform_device *pdev)
189 {
190 	struct device *dev = &pdev->dev;
191 	struct device_node *cpu, *cpu_dai, *codec;
192 	struct odroid_priv *priv;
193 	struct snd_soc_card *card;
194 	struct snd_soc_dai_link *link, *codec_link;
195 	int num_pcms, ret, i;
196 	struct of_phandle_args args = {};
197 
198 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
199 	if (!priv)
200 		return -ENOMEM;
201 
202 	card = &priv->card;
203 	card->dev = dev;
204 
205 	card->owner = THIS_MODULE;
206 	card->fully_routed = true;
207 
208 	spin_lock_init(&priv->lock);
209 	snd_soc_card_set_drvdata(card, priv);
210 
211 	ret = snd_soc_of_parse_card_name(card, "model");
212 	if (ret < 0)
213 		return ret;
214 
215 	if (of_property_read_bool(dev->of_node, "samsung,audio-widgets")) {
216 		ret = snd_soc_of_parse_audio_simple_widgets(card,
217 						"samsung,audio-widgets");
218 		if (ret < 0)
219 			return ret;
220 	}
221 
222 	if (of_property_read_bool(dev->of_node, "samsung,audio-routing")) {
223 		ret = snd_soc_of_parse_audio_routing(card,
224 						"samsung,audio-routing");
225 		if (ret < 0)
226 			return ret;
227 	}
228 
229 	card->dai_link = odroid_card_dais;
230 	card->num_links = ARRAY_SIZE(odroid_card_dais);
231 
232 	cpu = of_get_child_by_name(dev->of_node, "cpu");
233 	codec = of_get_child_by_name(dev->of_node, "codec");
234 	link = card->dai_link;
235 	codec_link = &card->dai_link[1];
236 
237 	/*
238 	 * For backwards compatibility create the secondary CPU DAI link only
239 	 * if there are 2 CPU DAI entries in the cpu sound-dai property in DT.
240 	 */
241 	num_pcms = of_count_phandle_with_args(cpu, "sound-dai",
242 					      "#sound-dai-cells");
243 	if (num_pcms == 1)
244 		card->num_links--;
245 
246 	for (i = 0; i < num_pcms; i++, link += 2) {
247 		ret = of_parse_phandle_with_args(cpu, "sound-dai",
248 						 "#sound-dai-cells", i, &args);
249 		if (ret < 0)
250 			return ret;
251 
252 		if (!args.np) {
253 			dev_err(dev, "sound-dai property parse error: %d\n", ret);
254 			return -EINVAL;
255 		}
256 
257 		ret = snd_soc_get_dai_name(&args, &link->cpu_dai_name);
258 		of_node_put(args.np);
259 
260 		if (ret < 0)
261 			return ret;
262 	}
263 
264 	cpu_dai = of_parse_phandle(cpu, "sound-dai", 0);
265 	of_node_put(cpu);
266 	of_node_put(codec);
267 
268 	ret = snd_soc_of_get_dai_link_codecs(dev, codec, codec_link);
269 	if (ret < 0)
270 		goto err_put_codec_n;
271 
272 	/* Set capture capability only for boards with the MAX98090 CODEC */
273 	if (codec_link->num_codecs > 1) {
274 		card->dai_link[0].dpcm_capture = 1;
275 		card->dai_link[1].dpcm_capture = 1;
276 	}
277 
278 	priv->sclk_i2s = of_clk_get_by_name(cpu_dai, "i2s_opclk1");
279 	if (IS_ERR(priv->sclk_i2s)) {
280 		ret = PTR_ERR(priv->sclk_i2s);
281 		goto err_put_codec_n;
282 	}
283 
284 	priv->clk_i2s_bus = of_clk_get_by_name(cpu_dai, "iis");
285 	if (IS_ERR(priv->clk_i2s_bus)) {
286 		ret = PTR_ERR(priv->clk_i2s_bus);
287 		goto err_put_sclk;
288 	}
289 	of_node_put(cpu_dai);
290 
291 	ret = devm_snd_soc_register_card(dev, card);
292 	if (ret < 0) {
293 		dev_err(dev, "snd_soc_register_card() failed: %d\n", ret);
294 		goto err_put_clk_i2s;
295 	}
296 
297 	return 0;
298 
299 err_put_clk_i2s:
300 	clk_put(priv->clk_i2s_bus);
301 err_put_sclk:
302 	clk_put(priv->sclk_i2s);
303 err_put_codec_n:
304 	snd_soc_of_put_dai_link_codecs(codec_link);
305 	return ret;
306 }
307 
308 static int odroid_audio_remove(struct platform_device *pdev)
309 {
310 	struct odroid_priv *priv = platform_get_drvdata(pdev);
311 
312 	snd_soc_of_put_dai_link_codecs(&priv->card.dai_link[1]);
313 	clk_put(priv->sclk_i2s);
314 	clk_put(priv->clk_i2s_bus);
315 
316 	return 0;
317 }
318 
319 static const struct of_device_id odroid_audio_of_match[] = {
320 	{ .compatible	= "hardkernel,odroid-xu3-audio" },
321 	{ .compatible	= "hardkernel,odroid-xu4-audio" },
322 	{ .compatible	= "samsung,odroid-xu3-audio" },
323 	{ .compatible	= "samsung,odroid-xu4-audio" },
324 	{ },
325 };
326 MODULE_DEVICE_TABLE(of, odroid_audio_of_match);
327 
328 static struct platform_driver odroid_audio_driver = {
329 	.driver = {
330 		.name		= "odroid-audio",
331 		.of_match_table	= odroid_audio_of_match,
332 		.pm		= &snd_soc_pm_ops,
333 	},
334 	.probe	= odroid_audio_probe,
335 	.remove	= odroid_audio_remove,
336 };
337 module_platform_driver(odroid_audio_driver);
338 
339 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
340 MODULE_DESCRIPTION("Odroid XU3/XU4 audio support");
341 MODULE_LICENSE("GPL v2");
342