Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2//
3// Copyright(c) 2022 Intel Corporation
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
24/* Driver-specific board quirks: from bit 0 to 7 */
25#define SOF_HDMI_PLAYBACK_PRESENT BIT(0)
26
27/* Default: SSP2 */
28static unsigned long sof_ssp_amp_quirk = SOF_SSP_PORT_AMP(2);
29
30static const struct dmi_system_id chromebook_platforms[] = {
31 {
32 .ident = "Google Chromebooks",
33 .matches = {
34 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
35 }
36 },
37 {},
38};
39
40static int sof_card_late_probe(struct snd_soc_card *card)
41{
42 return sof_intel_board_card_late_probe(card);
43}
44
45static struct snd_soc_card sof_ssp_amp_card = {
46 .name = "ssp_amp",
47 .owner = THIS_MODULE,
48 .fully_routed = true,
49 .late_probe = sof_card_late_probe,
50};
51
52/* BE ID defined in sof-tgl-rt1308-hdmi-ssp.m4 */
53#define HDMI_IN_BE_ID 0
54#define SPK_BE_ID 2
55#define DMIC01_BE_ID 3
56#define INTEL_HDMI_BE_ID 5
57/* extra BE links to support no-hdmi-in boards */
58#define DMIC16K_BE_ID 4
59#define BT_OFFLOAD_BE_ID 8
60
61#define SSP_AMP_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_HDMI_IN, \
62 SOF_LINK_AMP, \
63 SOF_LINK_DMIC01, \
64 SOF_LINK_DMIC16K, \
65 SOF_LINK_IDISP_HDMI, \
66 SOF_LINK_BT_OFFLOAD, \
67 SOF_LINK_NONE)
68
69#define SSP_AMP_LINK_IDS SOF_LINK_ORDER(HDMI_IN_BE_ID, \
70 SPK_BE_ID, \
71 DMIC01_BE_ID, \
72 DMIC16K_BE_ID, \
73 INTEL_HDMI_BE_ID, \
74 BT_OFFLOAD_BE_ID, \
75 0)
76
77static int
78sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
79 struct sof_card_private *ctx)
80{
81 int ret;
82
83 ret = sof_intel_board_set_dai_link(dev, card, ctx);
84 if (ret)
85 return ret;
86
87 if (ctx->amp_type == CODEC_NONE)
88 return 0;
89
90 if (!ctx->amp_link) {
91 dev_err(dev, "amp link not available");
92 return -EINVAL;
93 }
94
95 /* codec-specific fields for speaker amplifier */
96 switch (ctx->amp_type) {
97 case CODEC_CS35L41:
98 cs35l41_set_dai_link(ctx->amp_link);
99 break;
100 case CODEC_RT1308:
101 sof_rt1308_dai_link(ctx->amp_link);
102 break;
103 default:
104 dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
105 return -EINVAL;
106 }
107
108 return 0;
109}
110
111static int sof_ssp_amp_probe(struct platform_device *pdev)
112{
113 struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
114 struct sof_card_private *ctx;
115 int ret;
116
117 if (pdev->id_entry && pdev->id_entry->driver_data)
118 sof_ssp_amp_quirk = (unsigned long)pdev->id_entry->driver_data;
119
120 dev_dbg(&pdev->dev, "sof_ssp_amp_quirk = %lx\n", sof_ssp_amp_quirk);
121
122 /* initialize ctx with board quirk */
123 ctx = sof_intel_board_get_ctx(&pdev->dev, sof_ssp_amp_quirk);
124 if (!ctx)
125 return -ENOMEM;
126
127 if (!dmi_check_system(chromebook_platforms) &&
128 (mach->mach_params.dmic_num == 0))
129 ctx->dmic_be_num = 0;
130
131 if (sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT) {
132 if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
133 ctx->hdmi.idisp_codec = true;
134 } else {
135 ctx->hdmi_num = 0;
136 }
137
138 ctx->link_order_overwrite = SSP_AMP_LINK_ORDER;
139
140 if (ctx->ssp_mask_hdmi_in) {
141 /* the topology supports HDMI-IN uses fixed BE ID for DAI links */
142 ctx->link_id_overwrite = SSP_AMP_LINK_IDS;
143 }
144
145 /* update dai_link */
146 ret = sof_card_dai_links_create(&pdev->dev, &sof_ssp_amp_card, ctx);
147 if (ret)
148 return ret;
149
150 /* update codec_conf */
151 switch (ctx->amp_type) {
152 case CODEC_CS35L41:
153 cs35l41_set_codec_conf(&sof_ssp_amp_card);
154 break;
155 case CODEC_RT1308:
156 case CODEC_NONE:
157 /* no codec conf required */
158 break;
159 default:
160 dev_err(&pdev->dev, "invalid amp type %d\n", ctx->amp_type);
161 return -EINVAL;
162 }
163
164 sof_ssp_amp_card.dev = &pdev->dev;
165
166 /* set platform name for each dailink */
167 ret = snd_soc_fixup_dai_links_platform_name(&sof_ssp_amp_card,
168 mach->mach_params.platform);
169 if (ret)
170 return ret;
171
172 snd_soc_card_set_drvdata(&sof_ssp_amp_card, ctx);
173
174 return devm_snd_soc_register_card(&pdev->dev, &sof_ssp_amp_card);
175}
176
177static const struct platform_device_id board_ids[] = {
178 {
179 .name = "sof_ssp_amp",
180 },
181 {
182 .name = "tgl_rt1308_hdmi_ssp",
183 .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_AMP(2) |
184 SOF_SSP_MASK_HDMI_CAPTURE(0x22)),
185 /* SSP 1 and SSP 5 are used for HDMI IN */
186 },
187 {
188 .name = "adl_cs35l41",
189 .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_AMP(1) |
190 SOF_NUM_IDISP_HDMI(4) |
191 SOF_HDMI_PLAYBACK_PRESENT |
192 SOF_SSP_PORT_BT_OFFLOAD(2) |
193 SOF_BT_OFFLOAD_PRESENT),
194 },
195 {
196 .name = "adl_lt6911_hdmi_ssp",
197 .driver_data = (kernel_ulong_t)(SOF_SSP_MASK_HDMI_CAPTURE(0x5) |
198 /* SSP 0 and SSP 2 are used for HDMI IN */
199 SOF_HDMI_PLAYBACK_PRESENT),
200 },
201 {
202 .name = "rpl_lt6911_hdmi_ssp",
203 .driver_data = (kernel_ulong_t)(SOF_SSP_MASK_HDMI_CAPTURE(0x5) |
204 /* SSP 0 and SSP 2 are used for HDMI IN */
205 SOF_HDMI_PLAYBACK_PRESENT),
206 },
207 {
208 .name = "mtl_lt6911_hdmi_ssp",
209 .driver_data = (kernel_ulong_t)(SOF_SSP_MASK_HDMI_CAPTURE(0x5) |
210 /* SSP 0 and SSP 2 are used for HDMI IN */
211 SOF_HDMI_PLAYBACK_PRESENT),
212 },
213 {
214 .name = "arl_lt6911_hdmi_ssp",
215 .driver_data = (kernel_ulong_t)(SOF_SSP_MASK_HDMI_CAPTURE(0x5) |
216 /* SSP 0 and SSP 2 are used for HDMI IN */
217 SOF_HDMI_PLAYBACK_PRESENT),
218 },
219 { }
220};
221MODULE_DEVICE_TABLE(platform, board_ids);
222
223static struct platform_driver sof_ssp_amp_driver = {
224 .probe = sof_ssp_amp_probe,
225 .driver = {
226 .name = "sof_ssp_amp",
227 .pm = &snd_soc_pm_ops,
228 },
229 .id_table = board_ids,
230};
231module_platform_driver(sof_ssp_amp_driver);
232
233MODULE_DESCRIPTION("ASoC Intel(R) SOF Amplifier Machine driver");
234MODULE_AUTHOR("Balamurugan C <balamurugan.c@intel.com>");
235MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
236MODULE_LICENSE("GPL");
237MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_BOARD_HELPERS");
238MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_REALTEK_COMMON");
239MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_CIRRUS_COMMON");
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 */
50static unsigned long sof_ssp_amp_quirk = SOF_AMPLIFIER_SSP(2);
51
52static 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
62static int sof_card_late_probe(struct snd_soc_card *card)
63{
64 return sof_intel_board_card_late_probe(card);
65}
66
67static 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
81static struct snd_soc_dai_link *
82sof_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
194static 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
289static 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};
330MODULE_DEVICE_TABLE(platform, board_ids);
331
332static 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};
340module_platform_driver(sof_ssp_amp_driver);
341
342MODULE_DESCRIPTION("ASoC Intel(R) SOF Amplifier Machine driver");
343MODULE_AUTHOR("Balamurugan C <balamurugan.c@intel.com>");
344MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
345MODULE_LICENSE("GPL");
346MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
347MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_REALTEK_COMMON);
348MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_CIRRUS_COMMON);
349MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_SSP_COMMON);