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 */
28 static unsigned long sof_ssp_amp_quirk = SOF_SSP_PORT_AMP(2);
29
30 static 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
sof_card_late_probe(struct snd_soc_card * card)40 static int sof_card_late_probe(struct snd_soc_card *card)
41 {
42 return sof_intel_board_card_late_probe(card);
43 }
44
45 static 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
77 static int
sof_card_dai_links_create(struct device * dev,struct snd_soc_card * card,struct sof_card_private * ctx)78 sof_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
sof_ssp_amp_probe(struct platform_device * pdev)111 static 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
177 static 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 };
221 MODULE_DEVICE_TABLE(platform, board_ids);
222
223 static 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 };
231 module_platform_driver(sof_ssp_amp_driver);
232
233 MODULE_DESCRIPTION("ASoC Intel(R) SOF Amplifier Machine driver");
234 MODULE_AUTHOR("Balamurugan C <balamurugan.c@intel.com>");
235 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
236 MODULE_LICENSE("GPL");
237 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
238 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_REALTEK_COMMON);
239 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_CIRRUS_COMMON);
240