1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Copyright(c) 2022 Intel Corporation. All rights reserved.
4
5 /*
6 * sof_ssp_amp.c - ASoc Machine driver for Intel platforms
7 * with RT1308/CS35L41 codec.
8 */
9
10 #include <linux/acpi.h>
11 #include <linux/delay.h>
12 #include <linux/dmi.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <sound/core.h>
16 #include <sound/jack.h>
17 #include <sound/pcm.h>
18 #include <sound/pcm_params.h>
19 #include <sound/sof.h>
20 #include "sof_board_helpers.h"
21 #include "sof_realtek_common.h"
22 #include "sof_cirrus_common.h"
23 #include "sof_ssp_common.h"
24
25 /* SSP port ID for speaker amplifier */
26 #define SOF_AMPLIFIER_SSP(quirk) ((quirk) & GENMASK(3, 0))
27 #define SOF_AMPLIFIER_SSP_MASK (GENMASK(3, 0))
28
29 /* HDMI capture*/
30 #define SOF_HDMI_CAPTURE_SSP_MASK_SHIFT 4
31 #define SOF_HDMI_CAPTURE_SSP_MASK_MASK (GENMASK(9, 4))
32 #define SOF_HDMI_CAPTURE_SSP_MASK(quirk) \
33 (((quirk) << SOF_HDMI_CAPTURE_SSP_MASK_SHIFT) & SOF_HDMI_CAPTURE_SSP_MASK_MASK)
34
35 /* HDMI playback */
36 #define SOF_HDMI_PLAYBACK_PRESENT BIT(13)
37 #define SOF_NO_OF_HDMI_PLAYBACK_SHIFT 14
38 #define SOF_NO_OF_HDMI_PLAYBACK_MASK (GENMASK(16, 14))
39 #define SOF_NO_OF_HDMI_PLAYBACK(quirk) \
40 (((quirk) << SOF_NO_OF_HDMI_PLAYBACK_SHIFT) & SOF_NO_OF_HDMI_PLAYBACK_MASK)
41
42 /* BT audio offload */
43 #define SOF_SSP_BT_OFFLOAD_PRESENT BIT(17)
44 #define SOF_BT_OFFLOAD_SSP_SHIFT 18
45 #define SOF_BT_OFFLOAD_SSP_MASK (GENMASK(20, 18))
46 #define SOF_BT_OFFLOAD_SSP(quirk) \
47 (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK)
48
49 /* Default: SSP2 */
50 static unsigned long sof_ssp_amp_quirk = SOF_AMPLIFIER_SSP(2);
51
52 static const struct dmi_system_id chromebook_platforms[] = {
53 {
54 .ident = "Google Chromebooks",
55 .matches = {
56 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
57 }
58 },
59 {},
60 };
61
sof_card_late_probe(struct snd_soc_card * card)62 static int sof_card_late_probe(struct snd_soc_card *card)
63 {
64 return sof_intel_board_card_late_probe(card);
65 }
66
67 static struct snd_soc_card sof_ssp_amp_card = {
68 .name = "ssp_amp",
69 .owner = THIS_MODULE,
70 .fully_routed = true,
71 .late_probe = sof_card_late_probe,
72 };
73
74 /* BE ID defined in sof-tgl-rt1308-hdmi-ssp.m4 */
75 #define HDMI_IN_BE_ID 0
76 #define SPK_BE_ID 2
77 #define DMIC01_BE_ID 3
78 #define DMIC16K_BE_ID 4
79 #define INTEL_HDMI_BE_ID 5
80
81 static struct snd_soc_dai_link *
sof_card_dai_links_create(struct device * dev,enum sof_ssp_codec amp_type,int ssp_amp,int dmic_be_num,int hdmi_num,bool idisp_codec)82 sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
83 int ssp_amp, int dmic_be_num, int hdmi_num,
84 bool idisp_codec)
85 {
86 struct snd_soc_dai_link *links;
87 int i;
88 int id = 0;
89 int ret;
90 bool fixed_be = false;
91 int be_id;
92 unsigned long ssp_mask_hdmi_in;
93
94 links = devm_kcalloc(dev, sof_ssp_amp_card.num_links,
95 sizeof(struct snd_soc_dai_link), GFP_KERNEL);
96 if (!links)
97 return NULL;
98
99 /* HDMI-In SSP */
100 ssp_mask_hdmi_in = (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_SSP_MASK_MASK) >>
101 SOF_HDMI_CAPTURE_SSP_MASK_SHIFT;
102
103 if (ssp_mask_hdmi_in) {
104 int port = 0;
105
106 /* the topology supports HDMI-IN uses fixed BE ID for DAI links */
107 fixed_be = true;
108
109 be_id = HDMI_IN_BE_ID;
110 for_each_set_bit(port, &ssp_mask_hdmi_in, 32) {
111 ret = sof_intel_board_set_hdmi_in_link(dev, &links[id],
112 be_id, port);
113 if (ret)
114 return NULL;
115
116 id++;
117 be_id++;
118 }
119 }
120
121 /* codec SSP */
122 if (amp_type != CODEC_NONE) {
123 be_id = fixed_be ? SPK_BE_ID : id;
124 ret = sof_intel_board_set_ssp_amp_link(dev, &links[id], be_id,
125 amp_type, ssp_amp);
126 if (ret)
127 return NULL;
128
129 /* codec-specific fields */
130 switch (amp_type) {
131 case CODEC_CS35L41:
132 cs35l41_set_dai_link(&links[id]);
133 break;
134 case CODEC_RT1308:
135 sof_rt1308_dai_link(&links[id]);
136 break;
137 default:
138 dev_err(dev, "invalid amp type %d\n", amp_type);
139 return NULL;
140 }
141
142 id++;
143 }
144
145 /* dmic */
146 if (dmic_be_num > 0) {
147 /* at least we have dmic01 */
148 be_id = fixed_be ? DMIC01_BE_ID : id;
149 ret = sof_intel_board_set_dmic_link(dev, &links[id], be_id,
150 SOF_DMIC_01);
151 if (ret)
152 return NULL;
153
154 id++;
155 }
156
157 if (dmic_be_num > 1) {
158 /* set up 2 BE links at most */
159 be_id = fixed_be ? DMIC16K_BE_ID : id;
160 ret = sof_intel_board_set_dmic_link(dev, &links[id], be_id,
161 SOF_DMIC_16K);
162 if (ret)
163 return NULL;
164
165 id++;
166 }
167
168 /* HDMI playback */
169 for (i = 1; i <= hdmi_num; i++) {
170 be_id = fixed_be ? (INTEL_HDMI_BE_ID + i - 1) : id;
171 ret = sof_intel_board_set_intel_hdmi_link(dev, &links[id], be_id,
172 i, idisp_codec);
173 if (ret)
174 return NULL;
175
176 id++;
177 }
178
179 /* BT audio offload */
180 if (sof_ssp_amp_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) {
181 int port = (sof_ssp_amp_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
182 SOF_BT_OFFLOAD_SSP_SHIFT;
183
184 ret = sof_intel_board_set_bt_link(dev, &links[id], id, port);
185 if (ret)
186 return NULL;
187
188 id++;
189 }
190
191 return links;
192 }
193
sof_ssp_amp_probe(struct platform_device * pdev)194 static int sof_ssp_amp_probe(struct platform_device *pdev)
195 {
196 struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
197 struct snd_soc_dai_link *dai_links;
198 struct sof_card_private *ctx;
199 int ret;
200
201 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
202 if (!ctx)
203 return -ENOMEM;
204
205 if (pdev->id_entry && pdev->id_entry->driver_data)
206 sof_ssp_amp_quirk = (unsigned long)pdev->id_entry->driver_data;
207
208 ctx->amp_type = sof_ssp_detect_amp_type(&pdev->dev);
209
210 if (dmi_check_system(chromebook_platforms) || mach->mach_params.dmic_num > 0)
211 ctx->dmic_be_num = 2;
212 else
213 ctx->dmic_be_num = 0;
214
215 /* port number/mask of peripherals attached to ssp interface */
216 ctx->ssp_mask_hdmi_in = (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_SSP_MASK_MASK) >>
217 SOF_HDMI_CAPTURE_SSP_MASK_SHIFT;
218
219 ctx->ssp_bt = (sof_ssp_amp_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
220 SOF_BT_OFFLOAD_SSP_SHIFT;
221
222 ctx->ssp_amp = sof_ssp_amp_quirk & SOF_AMPLIFIER_SSP_MASK;
223
224 /* set number of dai links */
225 sof_ssp_amp_card.num_links = ctx->dmic_be_num;
226
227 if (ctx->amp_type != CODEC_NONE)
228 sof_ssp_amp_card.num_links++;
229
230 if (ctx->ssp_mask_hdmi_in)
231 sof_ssp_amp_card.num_links += hweight32(ctx->ssp_mask_hdmi_in);
232
233 if (sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT) {
234 ctx->hdmi_num = (sof_ssp_amp_quirk & SOF_NO_OF_HDMI_PLAYBACK_MASK) >>
235 SOF_NO_OF_HDMI_PLAYBACK_SHIFT;
236 /* default number of HDMI DAI's */
237 if (!ctx->hdmi_num)
238 ctx->hdmi_num = 3;
239
240 if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
241 ctx->hdmi.idisp_codec = true;
242
243 sof_ssp_amp_card.num_links += ctx->hdmi_num;
244 } else {
245 ctx->hdmi_num = 0;
246 }
247
248 if (sof_ssp_amp_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) {
249 ctx->bt_offload_present = true;
250 sof_ssp_amp_card.num_links++;
251 }
252
253 dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type,
254 ctx->ssp_amp, ctx->dmic_be_num,
255 ctx->hdmi_num,
256 ctx->hdmi.idisp_codec);
257 if (!dai_links)
258 return -ENOMEM;
259
260 sof_ssp_amp_card.dai_link = dai_links;
261
262 /* update codec_conf */
263 switch (ctx->amp_type) {
264 case CODEC_CS35L41:
265 cs35l41_set_codec_conf(&sof_ssp_amp_card);
266 break;
267 case CODEC_NONE:
268 case CODEC_RT1308:
269 /* no codec conf required */
270 break;
271 default:
272 dev_err(&pdev->dev, "invalid amp type %d\n", ctx->amp_type);
273 return -EINVAL;
274 }
275
276 sof_ssp_amp_card.dev = &pdev->dev;
277
278 /* set platform name for each dailink */
279 ret = snd_soc_fixup_dai_links_platform_name(&sof_ssp_amp_card,
280 mach->mach_params.platform);
281 if (ret)
282 return ret;
283
284 snd_soc_card_set_drvdata(&sof_ssp_amp_card, ctx);
285
286 return devm_snd_soc_register_card(&pdev->dev, &sof_ssp_amp_card);
287 }
288
289 static const struct platform_device_id board_ids[] = {
290 {
291 .name = "sof_ssp_amp",
292 },
293 {
294 .name = "tgl_rt1308_hdmi_ssp",
295 .driver_data = (kernel_ulong_t)(SOF_AMPLIFIER_SSP(2) |
296 SOF_HDMI_CAPTURE_SSP_MASK(0x22)),
297 /* SSP 1 and SSP 5 are used for HDMI IN */
298 },
299 {
300 .name = "adl_cs35l41",
301 .driver_data = (kernel_ulong_t)(SOF_AMPLIFIER_SSP(1) |
302 SOF_NO_OF_HDMI_PLAYBACK(4) |
303 SOF_HDMI_PLAYBACK_PRESENT |
304 SOF_BT_OFFLOAD_SSP(2) |
305 SOF_SSP_BT_OFFLOAD_PRESENT),
306 },
307 {
308 .name = "adl_lt6911_hdmi_ssp",
309 .driver_data = (kernel_ulong_t)(SOF_HDMI_CAPTURE_SSP_MASK(0x5) |
310 /* SSP 0 and SSP 2 are used for HDMI IN */
311 SOF_NO_OF_HDMI_PLAYBACK(3) |
312 SOF_HDMI_PLAYBACK_PRESENT),
313 },
314 {
315 .name = "rpl_lt6911_hdmi_ssp",
316 .driver_data = (kernel_ulong_t)(SOF_HDMI_CAPTURE_SSP_MASK(0x5) |
317 /* SSP 0 and SSP 2 are used for HDMI IN */
318 SOF_NO_OF_HDMI_PLAYBACK(3) |
319 SOF_HDMI_PLAYBACK_PRESENT),
320 },
321 {
322 .name = "mtl_lt6911_hdmi_ssp",
323 .driver_data = (kernel_ulong_t)(SOF_HDMI_CAPTURE_SSP_MASK(0x5) |
324 /* SSP 0 and SSP 2 are used for HDMI IN */
325 SOF_NO_OF_HDMI_PLAYBACK(3) |
326 SOF_HDMI_PLAYBACK_PRESENT),
327 },
328 { }
329 };
330 MODULE_DEVICE_TABLE(platform, board_ids);
331
332 static struct platform_driver sof_ssp_amp_driver = {
333 .probe = sof_ssp_amp_probe,
334 .driver = {
335 .name = "sof_ssp_amp",
336 .pm = &snd_soc_pm_ops,
337 },
338 .id_table = board_ids,
339 };
340 module_platform_driver(sof_ssp_amp_driver);
341
342 MODULE_DESCRIPTION("ASoC Intel(R) SOF Amplifier Machine driver");
343 MODULE_AUTHOR("Balamurugan C <balamurugan.c@intel.com>");
344 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
345 MODULE_LICENSE("GPL");
346 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
347 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_REALTEK_COMMON);
348 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_CIRRUS_COMMON);
349 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_SSP_COMMON);
350