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