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