Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/meson/axg-frddr.c
26436 views
1
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
2
//
3
// Copyright (c) 2018 BayLibre, SAS.
4
// Author: Jerome Brunet <[email protected]>
5
6
/*
7
* This driver implements the frontend playback DAI of AXG and G12A based SoCs
8
*/
9
10
#include <linux/bitfield.h>
11
#include <linux/clk.h>
12
#include <linux/regmap.h>
13
#include <linux/module.h>
14
#include <linux/of_platform.h>
15
#include <sound/pcm_params.h>
16
#include <sound/soc.h>
17
#include <sound/soc-dai.h>
18
19
#include "axg-fifo.h"
20
21
#define CTRL0_FRDDR_PP_MODE BIT(30)
22
#define CTRL0_SEL1_EN_SHIFT 3
23
#define CTRL0_SEL2_SHIFT 4
24
#define CTRL0_SEL2_EN_SHIFT 7
25
#define CTRL0_SEL3_SHIFT 8
26
#define CTRL0_SEL3_EN_SHIFT 11
27
#define CTRL1_FRDDR_FORCE_FINISH BIT(12)
28
#define CTRL2_SEL1_SHIFT 0
29
#define CTRL2_SEL1_EN_SHIFT 4
30
#define CTRL2_SEL2_SHIFT 8
31
#define CTRL2_SEL2_EN_SHIFT 12
32
#define CTRL2_SEL3_SHIFT 16
33
#define CTRL2_SEL3_EN_SHIFT 20
34
35
static int g12a_frddr_dai_prepare(struct snd_pcm_substream *substream,
36
struct snd_soc_dai *dai)
37
{
38
struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
39
40
/* Reset the read pointer to the FIFO_INIT_ADDR */
41
regmap_update_bits(fifo->map, FIFO_CTRL1,
42
CTRL1_FRDDR_FORCE_FINISH, 0);
43
regmap_update_bits(fifo->map, FIFO_CTRL1,
44
CTRL1_FRDDR_FORCE_FINISH, CTRL1_FRDDR_FORCE_FINISH);
45
regmap_update_bits(fifo->map, FIFO_CTRL1,
46
CTRL1_FRDDR_FORCE_FINISH, 0);
47
48
return 0;
49
}
50
51
static int axg_frddr_dai_hw_params(struct snd_pcm_substream *substream,
52
struct snd_pcm_hw_params *params,
53
struct snd_soc_dai *dai)
54
{
55
struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
56
unsigned int period, depth, val;
57
58
period = params_period_bytes(params);
59
60
/* Trim the FIFO depth if the period is small to improve latency */
61
depth = min(period, fifo->depth);
62
val = (depth / AXG_FIFO_BURST) - 1;
63
regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_FRDDR_DEPTH,
64
FIELD_PREP(CTRL1_FRDDR_DEPTH, val));
65
66
return 0;
67
}
68
69
static int axg_frddr_dai_startup(struct snd_pcm_substream *substream,
70
struct snd_soc_dai *dai)
71
{
72
struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
73
int ret;
74
75
/* Enable pclk to access registers and clock the fifo ip */
76
ret = clk_prepare_enable(fifo->pclk);
77
if (ret)
78
return ret;
79
80
/* Apply single buffer mode to the interface */
81
regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_FRDDR_PP_MODE, 0);
82
83
return 0;
84
}
85
86
static void axg_frddr_dai_shutdown(struct snd_pcm_substream *substream,
87
struct snd_soc_dai *dai)
88
{
89
struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
90
91
clk_disable_unprepare(fifo->pclk);
92
}
93
94
static int axg_frddr_pcm_new(struct snd_soc_pcm_runtime *rtd,
95
struct snd_soc_dai *dai)
96
{
97
return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_PLAYBACK);
98
}
99
100
static const struct snd_soc_dai_ops axg_frddr_ops = {
101
.hw_params = axg_frddr_dai_hw_params,
102
.startup = axg_frddr_dai_startup,
103
.shutdown = axg_frddr_dai_shutdown,
104
.pcm_new = axg_frddr_pcm_new,
105
};
106
107
static struct snd_soc_dai_driver axg_frddr_dai_drv = {
108
.name = "FRDDR",
109
.playback = {
110
.stream_name = "Playback",
111
.channels_min = 1,
112
.channels_max = AXG_FIFO_CH_MAX,
113
.rates = SNDRV_PCM_RATE_CONTINUOUS,
114
.rate_min = 5515,
115
.rate_max = 768000,
116
.formats = AXG_FIFO_FORMATS,
117
},
118
.ops = &axg_frddr_ops,
119
};
120
121
static const char * const axg_frddr_sel_texts[] = {
122
"OUT 0", "OUT 1", "OUT 2", "OUT 3", "OUT 4", "OUT 5", "OUT 6", "OUT 7",
123
};
124
125
static SOC_ENUM_SINGLE_DECL(axg_frddr_sel_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT,
126
axg_frddr_sel_texts);
127
128
static const struct snd_kcontrol_new axg_frddr_out_demux =
129
SOC_DAPM_ENUM("Output Sink", axg_frddr_sel_enum);
130
131
static const struct snd_soc_dapm_widget axg_frddr_dapm_widgets[] = {
132
SND_SOC_DAPM_DEMUX("SINK SEL", SND_SOC_NOPM, 0, 0,
133
&axg_frddr_out_demux),
134
SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0),
135
SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0),
136
SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0),
137
SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0),
138
SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0),
139
SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0),
140
SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0),
141
SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0),
142
};
143
144
static const struct snd_soc_dapm_route axg_frddr_dapm_routes[] = {
145
{ "SINK SEL", NULL, "Playback" },
146
{ "OUT 0", "OUT 0", "SINK SEL" },
147
{ "OUT 1", "OUT 1", "SINK SEL" },
148
{ "OUT 2", "OUT 2", "SINK SEL" },
149
{ "OUT 3", "OUT 3", "SINK SEL" },
150
{ "OUT 4", "OUT 4", "SINK SEL" },
151
{ "OUT 5", "OUT 5", "SINK SEL" },
152
{ "OUT 6", "OUT 6", "SINK SEL" },
153
{ "OUT 7", "OUT 7", "SINK SEL" },
154
};
155
156
static const struct snd_soc_component_driver axg_frddr_component_drv = {
157
.dapm_widgets = axg_frddr_dapm_widgets,
158
.num_dapm_widgets = ARRAY_SIZE(axg_frddr_dapm_widgets),
159
.dapm_routes = axg_frddr_dapm_routes,
160
.num_dapm_routes = ARRAY_SIZE(axg_frddr_dapm_routes),
161
.open = axg_fifo_pcm_open,
162
.close = axg_fifo_pcm_close,
163
.hw_params = axg_fifo_pcm_hw_params,
164
.hw_free = axg_fifo_pcm_hw_free,
165
.pointer = axg_fifo_pcm_pointer,
166
.trigger = axg_fifo_pcm_trigger,
167
.legacy_dai_naming = 1,
168
};
169
170
static const struct axg_fifo_match_data axg_frddr_match_data = {
171
.field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23),
172
.component_drv = &axg_frddr_component_drv,
173
.dai_drv = &axg_frddr_dai_drv
174
};
175
176
static const struct snd_soc_dai_ops g12a_frddr_ops = {
177
.prepare = g12a_frddr_dai_prepare,
178
.hw_params = axg_frddr_dai_hw_params,
179
.startup = axg_frddr_dai_startup,
180
.shutdown = axg_frddr_dai_shutdown,
181
.pcm_new = axg_frddr_pcm_new,
182
};
183
184
static struct snd_soc_dai_driver g12a_frddr_dai_drv = {
185
.name = "FRDDR",
186
.playback = {
187
.stream_name = "Playback",
188
.channels_min = 1,
189
.channels_max = AXG_FIFO_CH_MAX,
190
.rates = SNDRV_PCM_RATE_CONTINUOUS,
191
.rate_min = 5515,
192
.rate_max = 768000,
193
.formats = AXG_FIFO_FORMATS,
194
},
195
.ops = &g12a_frddr_ops,
196
};
197
198
static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel1_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT,
199
axg_frddr_sel_texts);
200
static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel2_enum, FIFO_CTRL0, CTRL0_SEL2_SHIFT,
201
axg_frddr_sel_texts);
202
static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel3_enum, FIFO_CTRL0, CTRL0_SEL3_SHIFT,
203
axg_frddr_sel_texts);
204
205
static const struct snd_kcontrol_new g12a_frddr_out1_demux =
206
SOC_DAPM_ENUM("Output Src 1", g12a_frddr_sel1_enum);
207
static const struct snd_kcontrol_new g12a_frddr_out2_demux =
208
SOC_DAPM_ENUM("Output Src 2", g12a_frddr_sel2_enum);
209
static const struct snd_kcontrol_new g12a_frddr_out3_demux =
210
SOC_DAPM_ENUM("Output Src 3", g12a_frddr_sel3_enum);
211
212
static const struct snd_kcontrol_new g12a_frddr_out1_enable =
213
SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0,
214
CTRL0_SEL1_EN_SHIFT, 1, 0);
215
static const struct snd_kcontrol_new g12a_frddr_out2_enable =
216
SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0,
217
CTRL0_SEL2_EN_SHIFT, 1, 0);
218
static const struct snd_kcontrol_new g12a_frddr_out3_enable =
219
SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0,
220
CTRL0_SEL3_EN_SHIFT, 1, 0);
221
222
static const struct snd_soc_dapm_widget g12a_frddr_dapm_widgets[] = {
223
SND_SOC_DAPM_AIF_OUT("SRC 1", NULL, 0, SND_SOC_NOPM, 0, 0),
224
SND_SOC_DAPM_AIF_OUT("SRC 2", NULL, 0, SND_SOC_NOPM, 0, 0),
225
SND_SOC_DAPM_AIF_OUT("SRC 3", NULL, 0, SND_SOC_NOPM, 0, 0),
226
SND_SOC_DAPM_SWITCH("SRC 1 EN", SND_SOC_NOPM, 0, 0,
227
&g12a_frddr_out1_enable),
228
SND_SOC_DAPM_SWITCH("SRC 2 EN", SND_SOC_NOPM, 0, 0,
229
&g12a_frddr_out2_enable),
230
SND_SOC_DAPM_SWITCH("SRC 3 EN", SND_SOC_NOPM, 0, 0,
231
&g12a_frddr_out3_enable),
232
SND_SOC_DAPM_DEMUX("SINK 1 SEL", SND_SOC_NOPM, 0, 0,
233
&g12a_frddr_out1_demux),
234
SND_SOC_DAPM_DEMUX("SINK 2 SEL", SND_SOC_NOPM, 0, 0,
235
&g12a_frddr_out2_demux),
236
SND_SOC_DAPM_DEMUX("SINK 3 SEL", SND_SOC_NOPM, 0, 0,
237
&g12a_frddr_out3_demux),
238
SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0),
239
SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0),
240
SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0),
241
SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0),
242
SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0),
243
SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0),
244
SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0),
245
SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0),
246
};
247
248
static const struct snd_soc_dapm_route g12a_frddr_dapm_routes[] = {
249
{ "SRC 1", NULL, "Playback" },
250
{ "SRC 2", NULL, "Playback" },
251
{ "SRC 3", NULL, "Playback" },
252
{ "SRC 1 EN", "Switch", "SRC 1" },
253
{ "SRC 2 EN", "Switch", "SRC 2" },
254
{ "SRC 3 EN", "Switch", "SRC 3" },
255
{ "SINK 1 SEL", NULL, "SRC 1 EN" },
256
{ "SINK 2 SEL", NULL, "SRC 2 EN" },
257
{ "SINK 3 SEL", NULL, "SRC 3 EN" },
258
{ "OUT 0", "OUT 0", "SINK 1 SEL" },
259
{ "OUT 1", "OUT 1", "SINK 1 SEL" },
260
{ "OUT 2", "OUT 2", "SINK 1 SEL" },
261
{ "OUT 3", "OUT 3", "SINK 1 SEL" },
262
{ "OUT 4", "OUT 4", "SINK 1 SEL" },
263
{ "OUT 5", "OUT 5", "SINK 1 SEL" },
264
{ "OUT 6", "OUT 6", "SINK 1 SEL" },
265
{ "OUT 7", "OUT 7", "SINK 1 SEL" },
266
{ "OUT 0", "OUT 0", "SINK 2 SEL" },
267
{ "OUT 1", "OUT 1", "SINK 2 SEL" },
268
{ "OUT 2", "OUT 2", "SINK 2 SEL" },
269
{ "OUT 3", "OUT 3", "SINK 2 SEL" },
270
{ "OUT 4", "OUT 4", "SINK 2 SEL" },
271
{ "OUT 5", "OUT 5", "SINK 2 SEL" },
272
{ "OUT 6", "OUT 6", "SINK 2 SEL" },
273
{ "OUT 7", "OUT 7", "SINK 2 SEL" },
274
{ "OUT 0", "OUT 0", "SINK 3 SEL" },
275
{ "OUT 1", "OUT 1", "SINK 3 SEL" },
276
{ "OUT 2", "OUT 2", "SINK 3 SEL" },
277
{ "OUT 3", "OUT 3", "SINK 3 SEL" },
278
{ "OUT 4", "OUT 4", "SINK 3 SEL" },
279
{ "OUT 5", "OUT 5", "SINK 3 SEL" },
280
{ "OUT 6", "OUT 6", "SINK 3 SEL" },
281
{ "OUT 7", "OUT 7", "SINK 3 SEL" },
282
};
283
284
static const struct snd_soc_component_driver g12a_frddr_component_drv = {
285
.dapm_widgets = g12a_frddr_dapm_widgets,
286
.num_dapm_widgets = ARRAY_SIZE(g12a_frddr_dapm_widgets),
287
.dapm_routes = g12a_frddr_dapm_routes,
288
.num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes),
289
.open = axg_fifo_pcm_open,
290
.close = axg_fifo_pcm_close,
291
.hw_params = g12a_fifo_pcm_hw_params,
292
.hw_free = axg_fifo_pcm_hw_free,
293
.pointer = axg_fifo_pcm_pointer,
294
.trigger = axg_fifo_pcm_trigger,
295
.legacy_dai_naming = 1,
296
};
297
298
static const struct axg_fifo_match_data g12a_frddr_match_data = {
299
.field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23),
300
.component_drv = &g12a_frddr_component_drv,
301
.dai_drv = &g12a_frddr_dai_drv
302
};
303
304
/* On SM1, the output selection in on CTRL2 */
305
static const struct snd_kcontrol_new sm1_frddr_out1_enable =
306
SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2,
307
CTRL2_SEL1_EN_SHIFT, 1, 0);
308
static const struct snd_kcontrol_new sm1_frddr_out2_enable =
309
SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2,
310
CTRL2_SEL2_EN_SHIFT, 1, 0);
311
static const struct snd_kcontrol_new sm1_frddr_out3_enable =
312
SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2,
313
CTRL2_SEL3_EN_SHIFT, 1, 0);
314
315
static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel1_enum, FIFO_CTRL2, CTRL2_SEL1_SHIFT,
316
axg_frddr_sel_texts);
317
static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel2_enum, FIFO_CTRL2, CTRL2_SEL2_SHIFT,
318
axg_frddr_sel_texts);
319
static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel3_enum, FIFO_CTRL2, CTRL2_SEL3_SHIFT,
320
axg_frddr_sel_texts);
321
322
static const struct snd_kcontrol_new sm1_frddr_out1_demux =
323
SOC_DAPM_ENUM("Output Src 1", sm1_frddr_sel1_enum);
324
static const struct snd_kcontrol_new sm1_frddr_out2_demux =
325
SOC_DAPM_ENUM("Output Src 2", sm1_frddr_sel2_enum);
326
static const struct snd_kcontrol_new sm1_frddr_out3_demux =
327
SOC_DAPM_ENUM("Output Src 3", sm1_frddr_sel3_enum);
328
329
static const struct snd_soc_dapm_widget sm1_frddr_dapm_widgets[] = {
330
SND_SOC_DAPM_AIF_OUT("SRC 1", NULL, 0, SND_SOC_NOPM, 0, 0),
331
SND_SOC_DAPM_AIF_OUT("SRC 2", NULL, 0, SND_SOC_NOPM, 0, 0),
332
SND_SOC_DAPM_AIF_OUT("SRC 3", NULL, 0, SND_SOC_NOPM, 0, 0),
333
SND_SOC_DAPM_SWITCH("SRC 1 EN", SND_SOC_NOPM, 0, 0,
334
&sm1_frddr_out1_enable),
335
SND_SOC_DAPM_SWITCH("SRC 2 EN", SND_SOC_NOPM, 0, 0,
336
&sm1_frddr_out2_enable),
337
SND_SOC_DAPM_SWITCH("SRC 3 EN", SND_SOC_NOPM, 0, 0,
338
&sm1_frddr_out3_enable),
339
SND_SOC_DAPM_DEMUX("SINK 1 SEL", SND_SOC_NOPM, 0, 0,
340
&sm1_frddr_out1_demux),
341
SND_SOC_DAPM_DEMUX("SINK 2 SEL", SND_SOC_NOPM, 0, 0,
342
&sm1_frddr_out2_demux),
343
SND_SOC_DAPM_DEMUX("SINK 3 SEL", SND_SOC_NOPM, 0, 0,
344
&sm1_frddr_out3_demux),
345
SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0),
346
SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0),
347
SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0),
348
SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0),
349
SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0),
350
SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0),
351
SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0),
352
SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0),
353
};
354
355
static const struct snd_soc_component_driver sm1_frddr_component_drv = {
356
.dapm_widgets = sm1_frddr_dapm_widgets,
357
.num_dapm_widgets = ARRAY_SIZE(sm1_frddr_dapm_widgets),
358
.dapm_routes = g12a_frddr_dapm_routes,
359
.num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes),
360
.open = axg_fifo_pcm_open,
361
.close = axg_fifo_pcm_close,
362
.hw_params = g12a_fifo_pcm_hw_params,
363
.hw_free = axg_fifo_pcm_hw_free,
364
.pointer = axg_fifo_pcm_pointer,
365
.trigger = axg_fifo_pcm_trigger,
366
.legacy_dai_naming = 1,
367
};
368
369
static const struct axg_fifo_match_data sm1_frddr_match_data = {
370
.field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23),
371
.component_drv = &sm1_frddr_component_drv,
372
.dai_drv = &g12a_frddr_dai_drv
373
};
374
375
static const struct of_device_id axg_frddr_of_match[] = {
376
{
377
.compatible = "amlogic,axg-frddr",
378
.data = &axg_frddr_match_data,
379
}, {
380
.compatible = "amlogic,g12a-frddr",
381
.data = &g12a_frddr_match_data,
382
}, {
383
.compatible = "amlogic,sm1-frddr",
384
.data = &sm1_frddr_match_data,
385
}, {}
386
};
387
MODULE_DEVICE_TABLE(of, axg_frddr_of_match);
388
389
static struct platform_driver axg_frddr_pdrv = {
390
.probe = axg_fifo_probe,
391
.driver = {
392
.name = "axg-frddr",
393
.of_match_table = axg_frddr_of_match,
394
},
395
};
396
module_platform_driver(axg_frddr_pdrv);
397
398
MODULE_DESCRIPTION("Amlogic AXG/G12A playback fifo driver");
399
MODULE_AUTHOR("Jerome Brunet <[email protected]>");
400
MODULE_LICENSE("GPL v2");
401
402