xref: /openbmc/linux/sound/soc/meson/aiu.c (revision c4f7ac64)
1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // Copyright (c) 2020 BayLibre, SAS.
4 // Author: Jerome Brunet <jbrunet@baylibre.com>
5 
6 #include <linux/bitfield.h>
7 #include <linux/clk.h>
8 #include <linux/module.h>
9 #include <linux/of_platform.h>
10 #include <linux/regmap.h>
11 #include <linux/reset.h>
12 #include <sound/soc.h>
13 #include <sound/soc-dai.h>
14 
15 #include <dt-bindings/sound/meson-aiu.h>
16 #include "aiu.h"
17 #include "aiu-fifo.h"
18 
19 #define AIU_I2S_MISC_958_SRC_SHIFT 3
20 
21 static const char * const aiu_spdif_encode_sel_texts[] = {
22 	"SPDIF", "I2S",
23 };
24 
25 static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC,
26 			    AIU_I2S_MISC_958_SRC_SHIFT,
27 			    aiu_spdif_encode_sel_texts);
28 
29 static const struct snd_kcontrol_new aiu_spdif_encode_mux =
30 	SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum);
31 
32 static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = {
33 	SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0,
34 			 &aiu_spdif_encode_mux),
35 };
36 
37 static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = {
38 	{ "I2S Encoder Playback", NULL, "I2S FIFO Playback" },
39 	{ "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" },
40 	{ "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" },
41 	{ "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" },
42 };
43 
44 int aiu_of_xlate_dai_name(struct snd_soc_component *component,
45 			  const struct of_phandle_args *args,
46 			  const char **dai_name,
47 			  unsigned int component_id)
48 {
49 	struct snd_soc_dai *dai;
50 	int id;
51 
52 	if (args->args_count != 2)
53 		return -EINVAL;
54 
55 	if (args->args[0] != component_id)
56 		return -EINVAL;
57 
58 	id = args->args[1];
59 
60 	if (id < 0 || id >= component->num_dai)
61 		return -EINVAL;
62 
63 	for_each_component_dais(component, dai) {
64 		if (id == 0)
65 			break;
66 		id--;
67 	}
68 
69 	*dai_name = dai->driver->name;
70 
71 	return 0;
72 }
73 
74 static int aiu_cpu_of_xlate_dai_name(struct snd_soc_component *component,
75 				     const struct of_phandle_args *args,
76 				     const char **dai_name)
77 {
78 	return aiu_of_xlate_dai_name(component, args, dai_name, AIU_CPU);
79 }
80 
81 static int aiu_cpu_component_probe(struct snd_soc_component *component)
82 {
83 	struct aiu *aiu = snd_soc_component_get_drvdata(component);
84 
85 	/* Required for the SPDIF Source control operation */
86 	return clk_prepare_enable(aiu->i2s.clks[PCLK].clk);
87 }
88 
89 static void aiu_cpu_component_remove(struct snd_soc_component *component)
90 {
91 	struct aiu *aiu = snd_soc_component_get_drvdata(component);
92 
93 	clk_disable_unprepare(aiu->i2s.clks[PCLK].clk);
94 }
95 
96 static const struct snd_soc_component_driver aiu_cpu_component = {
97 	.name			= "AIU CPU",
98 	.dapm_widgets		= aiu_cpu_dapm_widgets,
99 	.num_dapm_widgets	= ARRAY_SIZE(aiu_cpu_dapm_widgets),
100 	.dapm_routes		= aiu_cpu_dapm_routes,
101 	.num_dapm_routes	= ARRAY_SIZE(aiu_cpu_dapm_routes),
102 	.of_xlate_dai_name	= aiu_cpu_of_xlate_dai_name,
103 	.pointer		= aiu_fifo_pointer,
104 	.probe			= aiu_cpu_component_probe,
105 	.remove			= aiu_cpu_component_remove,
106 };
107 
108 static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = {
109 	[CPU_I2S_FIFO] = {
110 		.name = "I2S FIFO",
111 		.playback = {
112 			.stream_name	= "I2S FIFO Playback",
113 			.channels_min	= 2,
114 			.channels_max	= 8,
115 			.rates		= SNDRV_PCM_RATE_CONTINUOUS,
116 			.rate_min	= 5512,
117 			.rate_max	= 192000,
118 			.formats	= AIU_FORMATS,
119 		},
120 		.ops		= &aiu_fifo_i2s_dai_ops,
121 		.pcm_new	= aiu_fifo_pcm_new,
122 		.probe		= aiu_fifo_i2s_dai_probe,
123 		.remove		= aiu_fifo_dai_remove,
124 	},
125 	[CPU_SPDIF_FIFO] = {
126 		.name = "SPDIF FIFO",
127 		.playback = {
128 			.stream_name	= "SPDIF FIFO Playback",
129 			.channels_min	= 2,
130 			.channels_max	= 2,
131 			.rates		= SNDRV_PCM_RATE_CONTINUOUS,
132 			.rate_min	= 5512,
133 			.rate_max	= 192000,
134 			.formats	= AIU_FORMATS,
135 		},
136 		.ops		= &aiu_fifo_spdif_dai_ops,
137 		.pcm_new	= aiu_fifo_pcm_new,
138 		.probe		= aiu_fifo_spdif_dai_probe,
139 		.remove		= aiu_fifo_dai_remove,
140 	},
141 	[CPU_I2S_ENCODER] = {
142 		.name = "I2S Encoder",
143 		.playback = {
144 			.stream_name = "I2S Encoder Playback",
145 			.channels_min = 2,
146 			.channels_max = 8,
147 			.rates = SNDRV_PCM_RATE_8000_192000,
148 			.formats = AIU_FORMATS,
149 		},
150 		.ops = &aiu_encoder_i2s_dai_ops,
151 	},
152 	[CPU_SPDIF_ENCODER] = {
153 		.name = "SPDIF Encoder",
154 		.playback = {
155 			.stream_name = "SPDIF Encoder Playback",
156 			.channels_min = 2,
157 			.channels_max = 2,
158 			.rates = (SNDRV_PCM_RATE_32000  |
159 				  SNDRV_PCM_RATE_44100  |
160 				  SNDRV_PCM_RATE_48000  |
161 				  SNDRV_PCM_RATE_88200  |
162 				  SNDRV_PCM_RATE_96000  |
163 				  SNDRV_PCM_RATE_176400 |
164 				  SNDRV_PCM_RATE_192000),
165 			.formats = AIU_FORMATS,
166 		},
167 		.ops = &aiu_encoder_spdif_dai_ops,
168 	}
169 };
170 
171 static const struct regmap_config aiu_regmap_cfg = {
172 	.reg_bits	= 32,
173 	.val_bits	= 32,
174 	.reg_stride	= 4,
175 	.max_register	= 0x2ac,
176 };
177 
178 static int aiu_clk_bulk_get(struct device *dev,
179 			    const char * const *ids,
180 			    unsigned int num,
181 			    struct aiu_interface *interface)
182 {
183 	struct clk_bulk_data *clks;
184 	int i, ret;
185 
186 	clks = devm_kcalloc(dev, num, sizeof(*clks), GFP_KERNEL);
187 	if (!clks)
188 		return -ENOMEM;
189 
190 	for (i = 0; i < num; i++)
191 		clks[i].id = ids[i];
192 
193 	ret = devm_clk_bulk_get(dev, num, clks);
194 	if (ret < 0)
195 		return ret;
196 
197 	interface->clks = clks;
198 	interface->clk_num = num;
199 	return 0;
200 }
201 
202 static const char * const aiu_i2s_ids[] = {
203 	[PCLK]	= "i2s_pclk",
204 	[AOCLK]	= "i2s_aoclk",
205 	[MCLK]	= "i2s_mclk",
206 	[MIXER]	= "i2s_mixer",
207 };
208 
209 static const char * const aiu_spdif_ids[] = {
210 	[PCLK]	= "spdif_pclk",
211 	[AOCLK]	= "spdif_aoclk",
212 	[MCLK]	= "spdif_mclk_sel"
213 };
214 
215 static int aiu_clk_get(struct device *dev)
216 {
217 	struct aiu *aiu = dev_get_drvdata(dev);
218 	int ret;
219 
220 	aiu->pclk = devm_clk_get(dev, "pclk");
221 	if (IS_ERR(aiu->pclk)) {
222 		if (PTR_ERR(aiu->pclk) != -EPROBE_DEFER)
223 			dev_err(dev, "Can't get the aiu pclk\n");
224 		return PTR_ERR(aiu->pclk);
225 	}
226 
227 	aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk");
228 	if (IS_ERR(aiu->spdif_mclk)) {
229 		if (PTR_ERR(aiu->spdif_mclk) != -EPROBE_DEFER)
230 			dev_err(dev, "Can't get the aiu spdif master clock\n");
231 		return PTR_ERR(aiu->spdif_mclk);
232 	}
233 
234 	ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids),
235 			       &aiu->i2s);
236 	if (ret) {
237 		if (ret != -EPROBE_DEFER)
238 			dev_err(dev, "Can't get the i2s clocks\n");
239 		return ret;
240 	}
241 
242 	ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids),
243 			       &aiu->spdif);
244 	if (ret) {
245 		if (ret != -EPROBE_DEFER)
246 			dev_err(dev, "Can't get the spdif clocks\n");
247 		return ret;
248 	}
249 
250 	ret = clk_prepare_enable(aiu->pclk);
251 	if (ret) {
252 		dev_err(dev, "peripheral clock enable failed\n");
253 		return ret;
254 	}
255 
256 	ret = devm_add_action_or_reset(dev,
257 				       (void(*)(void *))clk_disable_unprepare,
258 				       aiu->pclk);
259 	if (ret)
260 		dev_err(dev, "failed to add reset action on pclk");
261 
262 	return ret;
263 }
264 
265 static int aiu_probe(struct platform_device *pdev)
266 {
267 	struct device *dev = &pdev->dev;
268 	void __iomem *regs;
269 	struct regmap *map;
270 	struct aiu *aiu;
271 	int ret;
272 
273 	aiu = devm_kzalloc(dev, sizeof(*aiu), GFP_KERNEL);
274 	if (!aiu)
275 		return -ENOMEM;
276 
277 	aiu->platform = device_get_match_data(dev);
278 	if (!aiu->platform)
279 		return -ENODEV;
280 
281 	platform_set_drvdata(pdev, aiu);
282 
283 	ret = device_reset(dev);
284 	if (ret) {
285 		if (ret != -EPROBE_DEFER)
286 			dev_err(dev, "Failed to reset device\n");
287 		return ret;
288 	}
289 
290 	regs = devm_platform_ioremap_resource(pdev, 0);
291 	if (IS_ERR(regs))
292 		return PTR_ERR(regs);
293 
294 	map = devm_regmap_init_mmio(dev, regs, &aiu_regmap_cfg);
295 	if (IS_ERR(map)) {
296 		dev_err(dev, "failed to init regmap: %ld\n",
297 			PTR_ERR(map));
298 		return PTR_ERR(map);
299 	}
300 
301 	aiu->i2s.irq = platform_get_irq_byname(pdev, "i2s");
302 	if (aiu->i2s.irq < 0)
303 		return aiu->i2s.irq;
304 
305 	aiu->spdif.irq = platform_get_irq_byname(pdev, "spdif");
306 	if (aiu->spdif.irq < 0)
307 		return aiu->spdif.irq;
308 
309 	ret = aiu_clk_get(dev);
310 	if (ret)
311 		return ret;
312 
313 	/* Register the cpu component of the aiu */
314 	ret = snd_soc_register_component(dev, &aiu_cpu_component,
315 					 aiu_cpu_dai_drv,
316 					 ARRAY_SIZE(aiu_cpu_dai_drv));
317 	if (ret) {
318 		dev_err(dev, "Failed to register cpu component\n");
319 		return ret;
320 	}
321 
322 	/* Register the hdmi codec control component */
323 	ret = aiu_hdmi_ctrl_register_component(dev);
324 	if (ret) {
325 		dev_err(dev, "Failed to register hdmi control component\n");
326 		goto err;
327 	}
328 
329 	/* Register the internal dac control component on gxl */
330 	if (aiu->platform->has_acodec) {
331 		ret = aiu_acodec_ctrl_register_component(dev);
332 		if (ret) {
333 			dev_err(dev,
334 			    "Failed to register acodec control component\n");
335 			goto err;
336 		}
337 	}
338 
339 	return 0;
340 err:
341 	snd_soc_unregister_component(dev);
342 	return ret;
343 }
344 
345 static int aiu_remove(struct platform_device *pdev)
346 {
347 	snd_soc_unregister_component(&pdev->dev);
348 
349 	return 0;
350 }
351 
352 static const struct aiu_platform_data aiu_gxbb_pdata = {
353 	.has_acodec = false,
354 	.has_clk_ctrl_more_i2s_div = true,
355 };
356 
357 static const struct aiu_platform_data aiu_gxl_pdata = {
358 	.has_acodec = true,
359 	.has_clk_ctrl_more_i2s_div = true,
360 };
361 
362 static const struct aiu_platform_data aiu_meson8_pdata = {
363 	.has_acodec = false,
364 	.has_clk_ctrl_more_i2s_div = false,
365 };
366 
367 static const struct of_device_id aiu_of_match[] = {
368 	{ .compatible = "amlogic,aiu-gxbb", .data = &aiu_gxbb_pdata },
369 	{ .compatible = "amlogic,aiu-gxl", .data = &aiu_gxl_pdata },
370 	{ .compatible = "amlogic,aiu-meson8", .data = &aiu_meson8_pdata },
371 	{ .compatible = "amlogic,aiu-meson8b", .data = &aiu_meson8_pdata },
372 	{}
373 };
374 MODULE_DEVICE_TABLE(of, aiu_of_match);
375 
376 static struct platform_driver aiu_pdrv = {
377 	.probe = aiu_probe,
378 	.remove = aiu_remove,
379 	.driver = {
380 		.name = "meson-aiu",
381 		.of_match_table = aiu_of_match,
382 	},
383 };
384 module_platform_driver(aiu_pdrv);
385 
386 MODULE_DESCRIPTION("Meson AIU Driver");
387 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
388 MODULE_LICENSE("GPL v2");
389