Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/meson/aiu.c
26436 views
1
// SPDX-License-Identifier: GPL-2.0
2
//
3
// Copyright (c) 2020 BayLibre, SAS.
4
// Author: Jerome Brunet <[email protected]>
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
#ifdef CONFIG_DEBUG_FS
107
.debugfs_prefix = "cpu",
108
#endif
109
};
110
111
static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = {
112
[CPU_I2S_FIFO] = {
113
.name = "I2S FIFO",
114
.playback = {
115
.stream_name = "I2S FIFO Playback",
116
.channels_min = 2,
117
.channels_max = 8,
118
.rates = SNDRV_PCM_RATE_CONTINUOUS,
119
.rate_min = 5512,
120
.rate_max = 192000,
121
.formats = AIU_FORMATS,
122
},
123
.ops = &aiu_fifo_i2s_dai_ops,
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
},
138
[CPU_I2S_ENCODER] = {
139
.name = "I2S Encoder",
140
.playback = {
141
.stream_name = "I2S Encoder Playback",
142
.channels_min = 2,
143
.channels_max = 8,
144
.rates = SNDRV_PCM_RATE_8000_192000,
145
.formats = AIU_FORMATS,
146
},
147
.ops = &aiu_encoder_i2s_dai_ops,
148
},
149
[CPU_SPDIF_ENCODER] = {
150
.name = "SPDIF Encoder",
151
.playback = {
152
.stream_name = "SPDIF Encoder Playback",
153
.channels_min = 2,
154
.channels_max = 2,
155
.rates = (SNDRV_PCM_RATE_32000 |
156
SNDRV_PCM_RATE_44100 |
157
SNDRV_PCM_RATE_48000 |
158
SNDRV_PCM_RATE_88200 |
159
SNDRV_PCM_RATE_96000 |
160
SNDRV_PCM_RATE_176400 |
161
SNDRV_PCM_RATE_192000),
162
.formats = AIU_FORMATS,
163
},
164
.ops = &aiu_encoder_spdif_dai_ops,
165
}
166
};
167
168
static const struct regmap_config aiu_regmap_cfg = {
169
.reg_bits = 32,
170
.val_bits = 32,
171
.reg_stride = 4,
172
.max_register = 0x2ac,
173
};
174
175
static int aiu_clk_bulk_get(struct device *dev,
176
const char * const *ids,
177
unsigned int num,
178
struct aiu_interface *interface)
179
{
180
struct clk_bulk_data *clks;
181
int i, ret;
182
183
clks = devm_kcalloc(dev, num, sizeof(*clks), GFP_KERNEL);
184
if (!clks)
185
return -ENOMEM;
186
187
for (i = 0; i < num; i++)
188
clks[i].id = ids[i];
189
190
ret = devm_clk_bulk_get(dev, num, clks);
191
if (ret < 0)
192
return ret;
193
194
interface->clks = clks;
195
interface->clk_num = num;
196
return 0;
197
}
198
199
static const char * const aiu_i2s_ids[] = {
200
[PCLK] = "i2s_pclk",
201
[AOCLK] = "i2s_aoclk",
202
[MCLK] = "i2s_mclk",
203
[MIXER] = "i2s_mixer",
204
};
205
206
static const char * const aiu_spdif_ids[] = {
207
[PCLK] = "spdif_pclk",
208
[AOCLK] = "spdif_aoclk",
209
[MCLK] = "spdif_mclk_sel"
210
};
211
212
static int aiu_clk_get(struct device *dev)
213
{
214
struct aiu *aiu = dev_get_drvdata(dev);
215
struct clk *pclk;
216
int ret;
217
218
pclk = devm_clk_get_enabled(dev, "pclk");
219
if (IS_ERR(pclk))
220
return dev_err_probe(dev, PTR_ERR(pclk), "Can't get the aiu pclk\n");
221
222
aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk");
223
if (IS_ERR(aiu->spdif_mclk))
224
return dev_err_probe(dev, PTR_ERR(aiu->spdif_mclk),
225
"Can't get the aiu spdif master clock\n");
226
227
ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids),
228
&aiu->i2s);
229
if (ret)
230
return dev_err_probe(dev, ret, "Can't get the i2s clocks\n");
231
232
ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids),
233
&aiu->spdif);
234
if (ret)
235
return dev_err_probe(dev, ret, "Can't get the spdif clocks\n");
236
237
return ret;
238
}
239
240
static int aiu_probe(struct platform_device *pdev)
241
{
242
struct device *dev = &pdev->dev;
243
void __iomem *regs;
244
struct regmap *map;
245
struct aiu *aiu;
246
int ret;
247
248
aiu = devm_kzalloc(dev, sizeof(*aiu), GFP_KERNEL);
249
if (!aiu)
250
return -ENOMEM;
251
252
aiu->platform = device_get_match_data(dev);
253
if (!aiu->platform)
254
return -ENODEV;
255
256
platform_set_drvdata(pdev, aiu);
257
258
ret = device_reset(dev);
259
if (ret)
260
return dev_err_probe(dev, ret, "Failed to reset device\n");
261
262
regs = devm_platform_ioremap_resource(pdev, 0);
263
if (IS_ERR(regs))
264
return PTR_ERR(regs);
265
266
map = devm_regmap_init_mmio(dev, regs, &aiu_regmap_cfg);
267
if (IS_ERR(map)) {
268
dev_err(dev, "failed to init regmap: %ld\n",
269
PTR_ERR(map));
270
return PTR_ERR(map);
271
}
272
273
aiu->i2s.irq = platform_get_irq_byname(pdev, "i2s");
274
if (aiu->i2s.irq < 0)
275
return aiu->i2s.irq;
276
277
aiu->spdif.irq = platform_get_irq_byname(pdev, "spdif");
278
if (aiu->spdif.irq < 0)
279
return aiu->spdif.irq;
280
281
ret = aiu_clk_get(dev);
282
if (ret)
283
return ret;
284
285
/* Register the cpu component of the aiu */
286
ret = snd_soc_register_component(dev, &aiu_cpu_component,
287
aiu_cpu_dai_drv,
288
ARRAY_SIZE(aiu_cpu_dai_drv));
289
if (ret) {
290
dev_err(dev, "Failed to register cpu component\n");
291
return ret;
292
}
293
294
/* Register the hdmi codec control component */
295
ret = aiu_hdmi_ctrl_register_component(dev);
296
if (ret) {
297
dev_err(dev, "Failed to register hdmi control component\n");
298
goto err;
299
}
300
301
/* Register the internal dac control component on gxl */
302
if (aiu->platform->has_acodec) {
303
ret = aiu_acodec_ctrl_register_component(dev);
304
if (ret) {
305
dev_err(dev,
306
"Failed to register acodec control component\n");
307
goto err;
308
}
309
}
310
311
return 0;
312
err:
313
snd_soc_unregister_component(dev);
314
return ret;
315
}
316
317
static void aiu_remove(struct platform_device *pdev)
318
{
319
snd_soc_unregister_component(&pdev->dev);
320
}
321
322
static const struct aiu_platform_data aiu_gxbb_pdata = {
323
.has_acodec = false,
324
.has_clk_ctrl_more_i2s_div = true,
325
};
326
327
static const struct aiu_platform_data aiu_gxl_pdata = {
328
.has_acodec = true,
329
.has_clk_ctrl_more_i2s_div = true,
330
};
331
332
static const struct aiu_platform_data aiu_meson8_pdata = {
333
.has_acodec = false,
334
.has_clk_ctrl_more_i2s_div = false,
335
};
336
337
static const struct of_device_id aiu_of_match[] = {
338
{ .compatible = "amlogic,aiu-gxbb", .data = &aiu_gxbb_pdata },
339
{ .compatible = "amlogic,aiu-gxl", .data = &aiu_gxl_pdata },
340
{ .compatible = "amlogic,aiu-meson8", .data = &aiu_meson8_pdata },
341
{ .compatible = "amlogic,aiu-meson8b", .data = &aiu_meson8_pdata },
342
{}
343
};
344
MODULE_DEVICE_TABLE(of, aiu_of_match);
345
346
static struct platform_driver aiu_pdrv = {
347
.probe = aiu_probe,
348
.remove = aiu_remove,
349
.driver = {
350
.name = "meson-aiu",
351
.of_match_table = aiu_of_match,
352
},
353
};
354
module_platform_driver(aiu_pdrv);
355
356
MODULE_DESCRIPTION("Meson AIU Driver");
357
MODULE_AUTHOR("Jerome Brunet <[email protected]>");
358
MODULE_LICENSE("GPL v2");
359
360