xref: /linux/sound/soc/intel/boards/sof_nau8825.c (revision 27aa58180473f81990f35238dc8aec40d34c778d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright(c) 2021 Intel Corporation.
3 // Copyright(c) 2021 Nuvoton Corporation.
4 
5 /*
6  * Intel SOF Machine Driver with Nuvoton headphone codec NAU8825
7  * and speaker codec RT1019P MAX98360a or MAX98373
8  */
9 #include <linux/i2c.h>
10 #include <linux/input.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/dmi.h>
14 #include <sound/core.h>
15 #include <sound/jack.h>
16 #include <sound/pcm.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
19 #include <sound/sof.h>
20 #include <sound/soc-acpi.h>
21 #include "../../codecs/nau8825.h"
22 #include "../common/soc-intel-quirks.h"
23 #include "sof_board_helpers.h"
24 #include "sof_realtek_common.h"
25 #include "sof_maxim_common.h"
26 #include "sof_nuvoton_common.h"
27 
28 static unsigned long sof_nau8825_quirk = SOF_SSP_PORT_CODEC(0);
29 
30 static struct snd_soc_jack_pin jack_pins[] = {
31 	{
32 		.pin    = "Headphone Jack",
33 		.mask   = SND_JACK_HEADPHONE,
34 	},
35 	{
36 		.pin    = "Headset Mic",
37 		.mask   = SND_JACK_MICROPHONE,
38 	},
39 };
40 
41 static int sof_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
42 {
43 	struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
44 	struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
45 	struct snd_soc_jack *jack = &ctx->headset_jack;
46 	int ret;
47 
48 	/*
49 	 * Headset buttons map to the google Reference headset.
50 	 * These can be configured by userspace.
51 	 */
52 	ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
53 					 SND_JACK_HEADSET | SND_JACK_BTN_0 |
54 					 SND_JACK_BTN_1 | SND_JACK_BTN_2 |
55 					 SND_JACK_BTN_3,
56 					 jack,
57 					 jack_pins,
58 					 ARRAY_SIZE(jack_pins));
59 	if (ret) {
60 		dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
61 		return ret;
62 	}
63 
64 	snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
65 	snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
66 	snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
67 	snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
68 
69 	ret = snd_soc_component_set_jack(component, jack, NULL);
70 	if (ret) {
71 		dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
72 		return ret;
73 	}
74 
75 	return ret;
76 };
77 
78 static void sof_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd)
79 {
80 	struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
81 
82 	snd_soc_component_set_jack(component, NULL, NULL);
83 }
84 
85 static int sof_nau8825_hw_params(struct snd_pcm_substream *substream,
86 				 struct snd_pcm_hw_params *params)
87 {
88 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
89 	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
90 	int clk_freq, ret;
91 
92 	clk_freq = sof_dai_get_bclk(rtd); /* BCLK freq */
93 
94 	if (clk_freq <= 0) {
95 		dev_err(rtd->dev, "get bclk freq failed: %d\n", clk_freq);
96 		return -EINVAL;
97 	}
98 
99 	/* Configure clock for codec */
100 	ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0,
101 				     SND_SOC_CLOCK_IN);
102 	if (ret < 0) {
103 		dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret);
104 		return ret;
105 	}
106 
107 	/* Configure pll for codec */
108 	ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq,
109 				  params_rate(params) * 256);
110 	if (ret < 0) {
111 		dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret);
112 		return ret;
113 	}
114 
115 	return ret;
116 }
117 
118 static struct snd_soc_ops sof_nau8825_ops = {
119 	.hw_params = sof_nau8825_hw_params,
120 };
121 
122 static int sof_card_late_probe(struct snd_soc_card *card)
123 {
124 	struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
125 	struct snd_soc_dapm_context *dapm = &card->dapm;
126 	int err;
127 
128 	if (ctx->amp_type == CODEC_MAX98373) {
129 		/* Disable Left and Right Spk pin after boot */
130 		snd_soc_dapm_disable_pin(dapm, "Left Spk");
131 		snd_soc_dapm_disable_pin(dapm, "Right Spk");
132 		err = snd_soc_dapm_sync(dapm);
133 		if (err < 0)
134 			return err;
135 	}
136 
137 	return sof_intel_board_card_late_probe(card);
138 }
139 
140 static const struct snd_kcontrol_new sof_controls[] = {
141 	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
142 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
143 	SOC_DAPM_PIN_SWITCH("Left Spk"),
144 	SOC_DAPM_PIN_SWITCH("Right Spk"),
145 };
146 
147 static const struct snd_soc_dapm_widget sof_widgets[] = {
148 	SND_SOC_DAPM_HP("Headphone Jack", NULL),
149 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
150 	SND_SOC_DAPM_SPK("Left Spk", NULL),
151 	SND_SOC_DAPM_SPK("Right Spk", NULL),
152 };
153 
154 static const struct snd_soc_dapm_route sof_map[] = {
155 	/* HP jack connectors - unknown if we have jack detection */
156 	{ "Headphone Jack", NULL, "HPOL" },
157 	{ "Headphone Jack", NULL, "HPOR" },
158 
159 	/* other jacks */
160 	{ "MIC", NULL, "Headset Mic" },
161 };
162 
163 /* sof audio machine driver for nau8825 codec */
164 static struct snd_soc_card sof_audio_card_nau8825 = {
165 	.name = "nau8825", /* the sof- prefix is added by the core */
166 	.owner = THIS_MODULE,
167 	.controls = sof_controls,
168 	.num_controls = ARRAY_SIZE(sof_controls),
169 	.dapm_widgets = sof_widgets,
170 	.num_dapm_widgets = ARRAY_SIZE(sof_widgets),
171 	.dapm_routes = sof_map,
172 	.num_dapm_routes = ARRAY_SIZE(sof_map),
173 	.fully_routed = true,
174 	.late_probe = sof_card_late_probe,
175 };
176 
177 static struct snd_soc_dai_link_component nau8825_component[] = {
178 	{
179 		.name = "i2c-10508825:00",
180 		.dai_name = "nau8825-hifi",
181 	}
182 };
183 
184 static int
185 sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
186 			  struct sof_card_private *ctx)
187 {
188 	int ret;
189 
190 	ret = sof_intel_board_set_dai_link(dev, card, ctx);
191 	if (ret)
192 		return ret;
193 
194 	if (!ctx->codec_link) {
195 		dev_err(dev, "codec link not available");
196 		return -EINVAL;
197 	}
198 
199 	/* codec-specific fields for headphone codec */
200 	ctx->codec_link->codecs = nau8825_component;
201 	ctx->codec_link->num_codecs = ARRAY_SIZE(nau8825_component);
202 	ctx->codec_link->init = sof_nau8825_codec_init;
203 	ctx->codec_link->exit = sof_nau8825_codec_exit;
204 	ctx->codec_link->ops = &sof_nau8825_ops;
205 
206 	if (ctx->amp_type == CODEC_NONE)
207 		return 0;
208 
209 	if (!ctx->amp_link) {
210 		dev_err(dev, "amp link not available");
211 		return -EINVAL;
212 	}
213 
214 	/* codec-specific fields for speaker amplifier */
215 	switch (ctx->amp_type) {
216 	case CODEC_MAX98360A:
217 		max_98360a_dai_link(ctx->amp_link);
218 		break;
219 	case CODEC_MAX98373:
220 		ctx->amp_link->codecs = max_98373_components;
221 		ctx->amp_link->num_codecs = ARRAY_SIZE(max_98373_components);
222 		ctx->amp_link->init = max_98373_spk_codec_init;
223 		ctx->amp_link->ops = &max_98373_ops;
224 		break;
225 	case CODEC_NAU8318:
226 		nau8318_set_dai_link(ctx->amp_link);
227 		break;
228 	case CODEC_RT1015P:
229 		sof_rt1015p_dai_link(ctx->amp_link);
230 		break;
231 	case CODEC_RT1019P:
232 		sof_rt1019p_dai_link(ctx->amp_link);
233 		break;
234 	default:
235 		dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
236 		return -EINVAL;
237 	}
238 
239 	return 0;
240 }
241 
242 static int sof_audio_probe(struct platform_device *pdev)
243 {
244 	struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
245 	struct sof_card_private *ctx;
246 	int ret;
247 
248 	if (pdev->id_entry && pdev->id_entry->driver_data)
249 		sof_nau8825_quirk = (unsigned long)pdev->id_entry->driver_data;
250 
251 	dev_dbg(&pdev->dev, "sof_nau8825_quirk = %lx\n", sof_nau8825_quirk);
252 
253 	/* initialize ctx with board quirk */
254 	ctx = sof_intel_board_get_ctx(&pdev->dev, sof_nau8825_quirk);
255 	if (!ctx)
256 		return -ENOMEM;
257 
258 	if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
259 		ctx->hdmi.idisp_codec = true;
260 
261 	/* update dai_link */
262 	ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_nau8825, ctx);
263 	if (ret)
264 		return ret;
265 
266 	/* update codec_conf */
267 	switch (ctx->amp_type) {
268 	case CODEC_MAX98373:
269 		max_98373_set_codec_conf(&sof_audio_card_nau8825);
270 		break;
271 	case CODEC_RT1015P:
272 		sof_rt1015p_codec_conf(&sof_audio_card_nau8825);
273 		break;
274 	case CODEC_MAX98360A:
275 	case CODEC_NAU8318:
276 	case CODEC_RT1019P:
277 	case CODEC_NONE:
278 		/* no codec conf required */
279 		break;
280 	default:
281 		dev_err(&pdev->dev, "invalid amp type %d\n", ctx->amp_type);
282 		return -EINVAL;
283 	}
284 
285 	sof_audio_card_nau8825.dev = &pdev->dev;
286 
287 	/* set platform name for each dailink */
288 	ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_nau8825,
289 						    mach->mach_params.platform);
290 	if (ret)
291 		return ret;
292 
293 	snd_soc_card_set_drvdata(&sof_audio_card_nau8825, ctx);
294 
295 	return devm_snd_soc_register_card(&pdev->dev,
296 					  &sof_audio_card_nau8825);
297 }
298 
299 static const struct platform_device_id board_ids[] = {
300 	{
301 		.name = "adl_rt1019p_8825",
302 		.driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) |
303 					SOF_SSP_PORT_AMP(2) |
304 					SOF_NUM_IDISP_HDMI(4)),
305 	},
306 	{
307 		.name = "adl_nau8825_def",
308 		.driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) |
309 					SOF_SSP_PORT_AMP(1) |
310 					SOF_NUM_IDISP_HDMI(4) |
311 					SOF_SSP_PORT_BT_OFFLOAD(2) |
312 					SOF_BT_OFFLOAD_PRESENT),
313 	},
314 	{
315 		.name = "rpl_nau8825_def",
316 		.driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) |
317 					SOF_SSP_PORT_AMP(1) |
318 					SOF_NUM_IDISP_HDMI(4) |
319 					SOF_SSP_PORT_BT_OFFLOAD(2) |
320 					SOF_BT_OFFLOAD_PRESENT),
321 	},
322 	{
323 		.name = "mtl_nau8825_def",
324 		.driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(2) |
325 					SOF_SSP_PORT_AMP(0) |
326 					SOF_SSP_PORT_BT_OFFLOAD(1) |
327 					SOF_BT_OFFLOAD_PRESENT),
328 	},
329 	{ }
330 };
331 MODULE_DEVICE_TABLE(platform, board_ids);
332 
333 static struct platform_driver sof_audio = {
334 	.probe = sof_audio_probe,
335 	.driver = {
336 		.name = "sof_nau8825",
337 		.pm = &snd_soc_pm_ops,
338 	},
339 	.id_table = board_ids,
340 };
341 module_platform_driver(sof_audio)
342 
343 /* Module information */
344 MODULE_DESCRIPTION("SOF Audio Machine driver for NAU8825");
345 MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
346 MODULE_AUTHOR("Mac Chiang <mac.chiang@intel.com>");
347 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
348 MODULE_LICENSE("GPL");
349 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS);
350 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON);
351 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_NUVOTON_COMMON);
352 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_REALTEK_COMMON);
353