xref: /openbmc/linux/sound/soc/samsung/odroid.c (revision 9a8f3203)
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 /* DAPM routes for backward compatibility with old DTS */
153 static const struct snd_soc_dapm_route odroid_dapm_routes[] = {
154 	{ "I2S Playback", NULL, "Mixer DAI TX" },
155 	{ "HiFi Playback", NULL, "Mixer DAI TX" },
156 };
157 
158 static struct snd_soc_dai_link odroid_card_dais[] = {
159 	{
160 		/* Primary FE <-> BE link */
161 		.codec_name = "snd-soc-dummy",
162 		.codec_dai_name = "snd-soc-dummy-dai",
163 		.ops = &odroid_card_fe_ops,
164 		.name = "Primary",
165 		.stream_name = "Primary",
166 		.platform_name = "3830000.i2s",
167 		.dynamic = 1,
168 		.dpcm_playback = 1,
169 	}, {
170 		/* BE <-> CODECs link */
171 		.name = "I2S Mixer",
172 		.cpu_name = "snd-soc-dummy",
173 		.cpu_dai_name = "snd-soc-dummy-dai",
174 		.platform_name = "snd-soc-dummy",
175 		.ops = &odroid_card_be_ops,
176 		.no_pcm = 1,
177 		.dpcm_playback = 1,
178 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
179 				SND_SOC_DAIFMT_CBS_CFS,
180 	}, {
181 		/* Secondary FE <-> BE link */
182 		.playback_only = 1,
183 		.codec_name = "snd-soc-dummy",
184 		.codec_dai_name = "snd-soc-dummy-dai",
185 		.ops = &odroid_card_fe_ops,
186 		.name = "Secondary",
187 		.stream_name = "Secondary",
188 		.platform_name = "3830000.i2s-sec",
189 		.dynamic = 1,
190 		.dpcm_playback = 1,
191 	}
192 };
193 
194 static int odroid_audio_probe(struct platform_device *pdev)
195 {
196 	struct device *dev = &pdev->dev;
197 	struct device_node *cpu_dai = NULL;
198 	struct device_node *cpu, *codec;
199 	struct odroid_priv *priv;
200 	struct snd_soc_card *card;
201 	struct snd_soc_dai_link *link, *codec_link;
202 	int num_pcms, ret, i;
203 	struct of_phandle_args args = {};
204 
205 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
206 	if (!priv)
207 		return -ENOMEM;
208 
209 	card = &priv->card;
210 	card->dev = dev;
211 
212 	card->owner = THIS_MODULE;
213 	card->fully_routed = true;
214 
215 	spin_lock_init(&priv->lock);
216 	snd_soc_card_set_drvdata(card, priv);
217 
218 	ret = snd_soc_of_parse_card_name(card, "model");
219 	if (ret < 0)
220 		return ret;
221 
222 	if (of_property_read_bool(dev->of_node, "samsung,audio-widgets")) {
223 		ret = snd_soc_of_parse_audio_simple_widgets(card,
224 						"samsung,audio-widgets");
225 		if (ret < 0)
226 			return ret;
227 	}
228 
229 	if (of_property_read_bool(dev->of_node, "samsung,audio-routing")) {
230 		ret = snd_soc_of_parse_audio_routing(card,
231 						"samsung,audio-routing");
232 		if (ret < 0)
233 			return ret;
234 	}
235 
236 	card->dai_link = odroid_card_dais;
237 	card->num_links = ARRAY_SIZE(odroid_card_dais);
238 
239 	cpu = of_get_child_by_name(dev->of_node, "cpu");
240 	codec = of_get_child_by_name(dev->of_node, "codec");
241 	link = card->dai_link;
242 	codec_link = &card->dai_link[1];
243 
244 	/*
245 	 * For backwards compatibility create the secondary CPU DAI link only
246 	 * if there are 2 CPU DAI entries in the cpu sound-dai property in DT.
247 	 * Also add required DAPM routes not available in old DTS.
248 	 */
249 	num_pcms = of_count_phandle_with_args(cpu, "sound-dai",
250 					      "#sound-dai-cells");
251 	if (num_pcms == 1) {
252 		card->dapm_routes = odroid_dapm_routes;
253 		card->num_dapm_routes = ARRAY_SIZE(odroid_dapm_routes);
254 		card->num_links--;
255 	}
256 
257 	for (i = 0; i < num_pcms; i++, link += 2) {
258 		ret = of_parse_phandle_with_args(cpu, "sound-dai",
259 						 "#sound-dai-cells", i, &args);
260 		if (ret < 0)
261 			break;
262 
263 		if (!args.np) {
264 			dev_err(dev, "sound-dai property parse error: %d\n", ret);
265 			ret = -EINVAL;
266 			break;
267 		}
268 
269 		ret = snd_soc_get_dai_name(&args, &link->cpu_dai_name);
270 		of_node_put(args.np);
271 
272 		if (ret < 0)
273 			break;
274 	}
275 	if (ret == 0) {
276 		cpu_dai = of_parse_phandle(cpu, "sound-dai", 0);
277 		if (!cpu_dai)
278 			ret = -EINVAL;
279 	}
280 
281 	of_node_put(cpu);
282 	of_node_put(codec);
283 	if (ret < 0)
284 		return ret;
285 
286 	ret = snd_soc_of_get_dai_link_codecs(dev, codec, codec_link);
287 	if (ret < 0)
288 		goto err_put_cpu_dai;
289 
290 	/* Set capture capability only for boards with the MAX98090 CODEC */
291 	if (codec_link->num_codecs > 1) {
292 		card->dai_link[0].dpcm_capture = 1;
293 		card->dai_link[1].dpcm_capture = 1;
294 	}
295 
296 	priv->sclk_i2s = of_clk_get_by_name(cpu_dai, "i2s_opclk1");
297 	if (IS_ERR(priv->sclk_i2s)) {
298 		ret = PTR_ERR(priv->sclk_i2s);
299 		goto err_put_cpu_dai;
300 	}
301 
302 	priv->clk_i2s_bus = of_clk_get_by_name(cpu_dai, "iis");
303 	if (IS_ERR(priv->clk_i2s_bus)) {
304 		ret = PTR_ERR(priv->clk_i2s_bus);
305 		goto err_put_sclk;
306 	}
307 	of_node_put(cpu_dai);
308 
309 	ret = devm_snd_soc_register_card(dev, card);
310 	if (ret < 0) {
311 		dev_err(dev, "snd_soc_register_card() failed: %d\n", ret);
312 		goto err_put_clk_i2s;
313 	}
314 
315 	return 0;
316 
317 err_put_clk_i2s:
318 	clk_put(priv->clk_i2s_bus);
319 err_put_sclk:
320 	clk_put(priv->sclk_i2s);
321 err_put_cpu_dai:
322 	of_node_put(cpu_dai);
323 	snd_soc_of_put_dai_link_codecs(codec_link);
324 	return ret;
325 }
326 
327 static int odroid_audio_remove(struct platform_device *pdev)
328 {
329 	struct odroid_priv *priv = platform_get_drvdata(pdev);
330 
331 	snd_soc_of_put_dai_link_codecs(&priv->card.dai_link[1]);
332 	clk_put(priv->sclk_i2s);
333 	clk_put(priv->clk_i2s_bus);
334 
335 	return 0;
336 }
337 
338 static const struct of_device_id odroid_audio_of_match[] = {
339 	{ .compatible	= "hardkernel,odroid-xu3-audio" },
340 	{ .compatible	= "hardkernel,odroid-xu4-audio" },
341 	{ .compatible	= "samsung,odroid-xu3-audio" },
342 	{ .compatible	= "samsung,odroid-xu4-audio" },
343 	{ },
344 };
345 MODULE_DEVICE_TABLE(of, odroid_audio_of_match);
346 
347 static struct platform_driver odroid_audio_driver = {
348 	.driver = {
349 		.name		= "odroid-audio",
350 		.of_match_table	= odroid_audio_of_match,
351 		.pm		= &snd_soc_pm_ops,
352 	},
353 	.probe	= odroid_audio_probe,
354 	.remove	= odroid_audio_remove,
355 };
356 module_platform_driver(odroid_audio_driver);
357 
358 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
359 MODULE_DESCRIPTION("Odroid XU3/XU4 audio support");
360 MODULE_LICENSE("GPL v2");
361