xref: /kernel/linux/linux-5.10/sound/soc/meson/aiu.c (revision 8c2ecf20)
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
21static const char * const aiu_spdif_encode_sel_texts[] = {
22	"SPDIF", "I2S",
23};
24
25static 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
29static const struct snd_kcontrol_new aiu_spdif_encode_mux =
30	SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum);
31
32static 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
37static 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
44int aiu_of_xlate_dai_name(struct snd_soc_component *component,
45			  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
74static int aiu_cpu_of_xlate_dai_name(struct snd_soc_component *component,
75				     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
81static 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
89static 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
96static 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
108static 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
171static 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
178static 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
202static const char * const aiu_i2s_ids[] = {
203	[PCLK]	= "i2s_pclk",
204	[AOCLK]	= "i2s_aoclk",
205	[MCLK]	= "i2s_mclk",
206	[MIXER]	= "i2s_mixer",
207};
208
209static const char * const aiu_spdif_ids[] = {
210	[PCLK]	= "spdif_pclk",
211	[AOCLK]	= "spdif_aoclk",
212	[MCLK]	= "spdif_mclk_sel"
213};
214
215static 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
265static 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;
340err:
341	snd_soc_unregister_component(dev);
342	return ret;
343}
344
345static int aiu_remove(struct platform_device *pdev)
346{
347	snd_soc_unregister_component(&pdev->dev);
348
349	return 0;
350}
351
352static const struct aiu_platform_data aiu_gxbb_pdata = {
353	.has_acodec = false,
354	.has_clk_ctrl_more_i2s_div = true,
355};
356
357static const struct aiu_platform_data aiu_gxl_pdata = {
358	.has_acodec = true,
359	.has_clk_ctrl_more_i2s_div = true,
360};
361
362static const struct aiu_platform_data aiu_meson8_pdata = {
363	.has_acodec = false,
364	.has_clk_ctrl_more_i2s_div = false,
365};
366
367static 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};
374MODULE_DEVICE_TABLE(of, aiu_of_match);
375
376static 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};
384module_platform_driver(aiu_pdrv);
385
386MODULE_DESCRIPTION("Meson AIU Driver");
387MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
388MODULE_LICENSE("GPL v2");
389