1// SPDX-License-Identifier: GPL-2.0
2//
3// Socionext UniPhier AIO ALSA driver for PXs2.
4//
5// Copyright (c) 2018 Socionext Inc.
6
7#include <linux/module.h>
8
9#include "aio.h"
10
11static const struct uniphier_aio_spec uniphier_aio_pxs2[] = {
12	/* for Line PCM In, Pin:AI1Dx */
13	{
14		.name = AUD_NAME_PCMIN1,
15		.gname = AUD_GNAME_LINE,
16		.swm = {
17			.type  = PORT_TYPE_I2S,
18			.dir   = PORT_DIR_INPUT,
19			.rb    = { 16, 11, },
20			.ch    = { 16, 11, },
21			.iif   = { 0, 0, },
22			.iport = { 0, AUD_HW_PCMIN1, },
23		},
24	},
25
26	/* for Speaker/Headphone/Mic PCM In, Pin:AI2Dx */
27	{
28		.name = AUD_NAME_PCMIN2,
29		.gname = AUD_GNAME_AUX,
30		.swm = {
31			.type  = PORT_TYPE_I2S,
32			.dir   = PORT_DIR_INPUT,
33			.rb    = { 17, 12, },
34			.ch    = { 17, 12, },
35			.iif   = { 1, 1, },
36			.iport = { 1, AUD_HW_PCMIN2, },
37		},
38	},
39
40	/* for HDMI PCM Out, Pin:AO1Dx (inner) */
41	{
42		.name = AUD_NAME_HPCMOUT1,
43		.gname = AUD_GNAME_HDMI,
44		.swm = {
45			.type  = PORT_TYPE_I2S,
46			.dir   = PORT_DIR_OUTPUT,
47			.rb    = { 0, 0, },
48			.ch    = { 0, 0, },
49			.oif   = { 0, 0, },
50			.oport = { 3, AUD_HW_HPCMOUT1, },
51		},
52	},
53
54	/* for Line PCM Out, Pin:AO2Dx */
55	{
56		.name = AUD_NAME_PCMOUT1,
57		.gname = AUD_GNAME_LINE,
58		.swm = {
59			.type  = PORT_TYPE_I2S,
60			.dir   = PORT_DIR_OUTPUT,
61			.rb    = { 1, 1, },
62			.ch    = { 1, 1, },
63			.oif   = { 1, 1, },
64			.oport = { 0, AUD_HW_PCMOUT1, },
65		},
66	},
67
68	/* for Speaker/Headphone/Mic PCM Out, Pin:AO3Dx */
69	{
70		.name = AUD_NAME_PCMOUT2,
71		.gname = AUD_GNAME_AUX,
72		.swm = {
73			.type  = PORT_TYPE_I2S,
74			.dir   = PORT_DIR_OUTPUT,
75			.rb    = { 2, 2, },
76			.ch    = { 2, 2, },
77			.oif   = { 2, 2, },
78			.oport = { 1, AUD_HW_PCMOUT2, },
79		},
80	},
81
82	/* for HDMI Out, Pin:AO1IEC */
83	{
84		.name = AUD_NAME_HIECOUT1,
85		.swm = {
86			.type  = PORT_TYPE_SPDIF,
87			.dir   = PORT_DIR_OUTPUT,
88			.rb    = { 6, 4, },
89			.ch    = { 6, 4, },
90			.oif   = { 6, 4, },
91			.oport = { 12, AUD_HW_HIECOUT1, },
92		},
93	},
94
95	/* for HDMI Out, Pin:AO1IEC, Compress */
96	{
97		.name = AUD_NAME_HIECCOMPOUT1,
98		.swm = {
99			.type  = PORT_TYPE_SPDIF,
100			.dir   = PORT_DIR_OUTPUT,
101			.rb    = { 6, 4, },
102			.ch    = { 6, 4, },
103			.oif   = { 6, 4, },
104			.oport = { 12, AUD_HW_HIECOUT1, },
105		},
106	},
107
108	/* for S/PDIF Out, Pin:AO2IEC */
109	{
110		.name = AUD_NAME_IECOUT1,
111		.swm = {
112			.type  = PORT_TYPE_SPDIF,
113			.dir   = PORT_DIR_OUTPUT,
114			.rb    = { 7, 5, },
115			.ch    = { 7, 5, },
116			.oif   = { 7, 5, },
117			.oport = { 13, AUD_HW_IECOUT1, },
118		},
119	},
120
121	/* for S/PDIF Out, Pin:AO2IEC */
122	{
123		.name = AUD_NAME_IECCOMPOUT1,
124		.swm = {
125			.type  = PORT_TYPE_SPDIF,
126			.dir   = PORT_DIR_OUTPUT,
127			.rb    = { 7, 5, },
128			.ch    = { 7, 5, },
129			.oif   = { 7, 5, },
130			.oport = { 13, AUD_HW_IECOUT1, },
131		},
132	},
133};
134
135static const struct uniphier_aio_pll uniphier_aio_pll_pxs2[] = {
136	[AUD_PLL_A1]   = { .enable = true, },
137	[AUD_PLL_F1]   = { .enable = true, },
138	[AUD_PLL_A2]   = { .enable = true, },
139	[AUD_PLL_F2]   = { .enable = true, },
140	[AUD_PLL_APLL] = { .enable = true, },
141	[AUD_PLL_HSC0] = { .enable = true, },
142};
143
144static int uniphier_aio_pxs2_probe(struct snd_soc_dai *dai)
145{
146	int ret;
147
148	ret = uniphier_aio_dai_probe(dai);
149	if (ret < 0)
150		return ret;
151
152	ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000);
153	if (ret < 0)
154		return ret;
155	ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000);
156	if (ret < 0)
157		return ret;
158
159	ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800);
160	if (ret < 0)
161		return ret;
162	ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800);
163	if (ret < 0)
164		return ret;
165
166	return 0;
167}
168
169static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = {
170	{
171		.name    = AUD_GNAME_HDMI,
172		.probe   = uniphier_aio_pxs2_probe,
173		.remove  = uniphier_aio_dai_remove,
174		.playback = {
175			.stream_name = AUD_NAME_HPCMOUT1,
176			.formats     = SNDRV_PCM_FMTBIT_S32_LE,
177			.rates       = SNDRV_PCM_RATE_48000,
178			.channels_min = 2,
179			.channels_max = 2,
180		},
181		.ops = &uniphier_aio_i2s_ops,
182	},
183	{
184		.name    = AUD_GNAME_LINE,
185		.probe   = uniphier_aio_pxs2_probe,
186		.remove  = uniphier_aio_dai_remove,
187		.playback = {
188			.stream_name = AUD_NAME_PCMOUT1,
189			.formats     = SNDRV_PCM_FMTBIT_S32_LE,
190			.rates       = SNDRV_PCM_RATE_48000,
191			.channels_min = 2,
192			.channels_max = 2,
193		},
194		.capture = {
195			.stream_name = AUD_NAME_PCMIN1,
196			.formats     = SNDRV_PCM_FMTBIT_S32_LE,
197			.rates       = SNDRV_PCM_RATE_48000,
198			.channels_min = 2,
199			.channels_max = 2,
200		},
201		.ops = &uniphier_aio_i2s_ops,
202	},
203	{
204		.name    = AUD_GNAME_AUX,
205		.probe   = uniphier_aio_pxs2_probe,
206		.remove  = uniphier_aio_dai_remove,
207		.playback = {
208			.stream_name = AUD_NAME_PCMOUT2,
209			.formats     = SNDRV_PCM_FMTBIT_S32_LE,
210			.rates       = SNDRV_PCM_RATE_48000,
211			.channels_min = 2,
212			.channels_max = 2,
213		},
214		.capture = {
215			.stream_name = AUD_NAME_PCMIN2,
216			.formats     = SNDRV_PCM_FMTBIT_S32_LE,
217			.rates       = SNDRV_PCM_RATE_48000,
218			.channels_min = 2,
219			.channels_max = 2,
220		},
221		.ops = &uniphier_aio_i2s_ops,
222	},
223	{
224		.name    = AUD_NAME_HIECOUT1,
225		.probe   = uniphier_aio_pxs2_probe,
226		.remove  = uniphier_aio_dai_remove,
227		.playback = {
228			.stream_name = AUD_NAME_HIECOUT1,
229			.formats     = SNDRV_PCM_FMTBIT_S32_LE,
230			.rates       = SNDRV_PCM_RATE_48000,
231			.channels_min = 2,
232			.channels_max = 2,
233		},
234		.ops = &uniphier_aio_spdif_ops,
235	},
236	{
237		.name    = AUD_NAME_IECOUT1,
238		.probe   = uniphier_aio_pxs2_probe,
239		.remove  = uniphier_aio_dai_remove,
240		.playback = {
241			.stream_name = AUD_NAME_IECOUT1,
242			.formats     = SNDRV_PCM_FMTBIT_S32_LE,
243			.rates       = SNDRV_PCM_RATE_48000,
244			.channels_min = 2,
245			.channels_max = 2,
246		},
247		.ops = &uniphier_aio_spdif_ops,
248	},
249	{
250		.name    = AUD_NAME_HIECCOMPOUT1,
251		.probe   = uniphier_aio_pxs2_probe,
252		.remove  = uniphier_aio_dai_remove,
253		.compress_new = snd_soc_new_compress,
254		.playback = {
255			.stream_name = AUD_NAME_HIECCOMPOUT1,
256			.channels_min = 1,
257			.channels_max = 1,
258		},
259		.ops = &uniphier_aio_spdif_ops,
260	},
261	{
262		.name    = AUD_NAME_IECCOMPOUT1,
263		.probe   = uniphier_aio_pxs2_probe,
264		.remove  = uniphier_aio_dai_remove,
265		.compress_new = snd_soc_new_compress,
266		.playback = {
267			.stream_name = AUD_NAME_IECCOMPOUT1,
268			.channels_min = 1,
269			.channels_max = 1,
270		},
271		.ops = &uniphier_aio_spdif_ops,
272	},
273};
274
275static const struct uniphier_aio_chip_spec uniphier_aio_pxs2_spec = {
276	.specs     = uniphier_aio_pxs2,
277	.num_specs = ARRAY_SIZE(uniphier_aio_pxs2),
278	.dais      = uniphier_aio_dai_pxs2,
279	.num_dais  = ARRAY_SIZE(uniphier_aio_dai_pxs2),
280	.plls      = uniphier_aio_pll_pxs2,
281	.num_plls  = ARRAY_SIZE(uniphier_aio_pll_pxs2),
282	.addr_ext  = 0,
283};
284
285static const struct of_device_id uniphier_aio_of_match[] = {
286	{
287		.compatible = "socionext,uniphier-pxs2-aio",
288		.data = &uniphier_aio_pxs2_spec,
289	},
290	{},
291};
292MODULE_DEVICE_TABLE(of, uniphier_aio_of_match);
293
294static struct platform_driver uniphier_aio_driver = {
295	.driver = {
296		.name = "snd-uniphier-aio-pxs2",
297		.of_match_table = of_match_ptr(uniphier_aio_of_match),
298	},
299	.probe    = uniphier_aio_probe,
300	.remove   = uniphier_aio_remove,
301};
302module_platform_driver(uniphier_aio_driver);
303
304MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
305MODULE_DESCRIPTION("UniPhier PXs2 AIO driver.");
306MODULE_LICENSE("GPL v2");
307