xref: /linux/sound/soc/intel/boards/sof_ssp_amp.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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