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 }; 144 145 static const struct snd_soc_dapm_widget sof_widgets[] = { 146 SND_SOC_DAPM_HP("Headphone Jack", NULL), 147 SND_SOC_DAPM_MIC("Headset Mic", NULL), 148 }; 149 150 static const struct snd_soc_dapm_route sof_map[] = { 151 /* HP jack connectors - unknown if we have jack detection */ 152 { "Headphone Jack", NULL, "HPOL" }, 153 { "Headphone Jack", NULL, "HPOR" }, 154 155 /* other jacks */ 156 { "MIC", NULL, "Headset Mic" }, 157 }; 158 159 /* sof audio machine driver for nau8825 codec */ 160 static struct snd_soc_card sof_audio_card_nau8825 = { 161 .name = "nau8825", /* the sof- prefix is added by the core */ 162 .owner = THIS_MODULE, 163 .controls = sof_controls, 164 .num_controls = ARRAY_SIZE(sof_controls), 165 .dapm_widgets = sof_widgets, 166 .num_dapm_widgets = ARRAY_SIZE(sof_widgets), 167 .dapm_routes = sof_map, 168 .num_dapm_routes = ARRAY_SIZE(sof_map), 169 .fully_routed = true, 170 .late_probe = sof_card_late_probe, 171 }; 172 173 static struct snd_soc_dai_link_component nau8825_component[] = { 174 { 175 .name = "i2c-10508825:00", 176 .dai_name = "nau8825-hifi", 177 } 178 }; 179 180 static int 181 sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card, 182 struct sof_card_private *ctx) 183 { 184 int ret; 185 186 ret = sof_intel_board_set_dai_link(dev, card, ctx); 187 if (ret) 188 return ret; 189 190 if (!ctx->codec_link) { 191 dev_err(dev, "codec link not available"); 192 return -EINVAL; 193 } 194 195 /* codec-specific fields for headphone codec */ 196 ctx->codec_link->codecs = nau8825_component; 197 ctx->codec_link->num_codecs = ARRAY_SIZE(nau8825_component); 198 ctx->codec_link->init = sof_nau8825_codec_init; 199 ctx->codec_link->exit = sof_nau8825_codec_exit; 200 ctx->codec_link->ops = &sof_nau8825_ops; 201 202 if (ctx->amp_type == CODEC_NONE) 203 return 0; 204 205 if (!ctx->amp_link) { 206 dev_err(dev, "amp link not available"); 207 return -EINVAL; 208 } 209 210 /* codec-specific fields for speaker amplifier */ 211 switch (ctx->amp_type) { 212 case CODEC_MAX98360A: 213 max_98360a_dai_link(ctx->amp_link); 214 break; 215 case CODEC_MAX98373: 216 ctx->amp_link->codecs = max_98373_components; 217 ctx->amp_link->num_codecs = ARRAY_SIZE(max_98373_components); 218 ctx->amp_link->init = max_98373_spk_codec_init; 219 ctx->amp_link->ops = &max_98373_ops; 220 break; 221 case CODEC_NAU8318: 222 nau8318_set_dai_link(ctx->amp_link); 223 break; 224 case CODEC_RT1015P: 225 sof_rt1015p_dai_link(ctx->amp_link); 226 break; 227 case CODEC_RT1019P: 228 sof_rt1019p_dai_link(ctx->amp_link); 229 break; 230 default: 231 dev_err(dev, "invalid amp type %d\n", ctx->amp_type); 232 return -EINVAL; 233 } 234 235 return 0; 236 } 237 238 static int sof_audio_probe(struct platform_device *pdev) 239 { 240 struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; 241 struct sof_card_private *ctx; 242 int ret; 243 244 if (pdev->id_entry && pdev->id_entry->driver_data) 245 sof_nau8825_quirk = (unsigned long)pdev->id_entry->driver_data; 246 247 dev_dbg(&pdev->dev, "sof_nau8825_quirk = %lx\n", sof_nau8825_quirk); 248 249 /* initialize ctx with board quirk */ 250 ctx = sof_intel_board_get_ctx(&pdev->dev, sof_nau8825_quirk); 251 if (!ctx) 252 return -ENOMEM; 253 254 if (mach->mach_params.codec_mask & IDISP_CODEC_MASK) 255 ctx->hdmi.idisp_codec = true; 256 257 /* update dai_link */ 258 ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_nau8825, ctx); 259 if (ret) 260 return ret; 261 262 /* update codec_conf */ 263 switch (ctx->amp_type) { 264 case CODEC_MAX98373: 265 max_98373_set_codec_conf(&sof_audio_card_nau8825); 266 break; 267 case CODEC_RT1015P: 268 sof_rt1015p_codec_conf(&sof_audio_card_nau8825); 269 break; 270 case CODEC_MAX98360A: 271 case CODEC_NAU8318: 272 case CODEC_RT1019P: 273 case CODEC_NONE: 274 /* no codec conf required */ 275 break; 276 default: 277 dev_err(&pdev->dev, "invalid amp type %d\n", ctx->amp_type); 278 return -EINVAL; 279 } 280 281 sof_audio_card_nau8825.dev = &pdev->dev; 282 283 /* set platform name for each dailink */ 284 ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_nau8825, 285 mach->mach_params.platform); 286 if (ret) 287 return ret; 288 289 snd_soc_card_set_drvdata(&sof_audio_card_nau8825, ctx); 290 291 return devm_snd_soc_register_card(&pdev->dev, 292 &sof_audio_card_nau8825); 293 } 294 295 static const struct platform_device_id board_ids[] = { 296 { 297 .name = "adl_rt1019p_8825", 298 .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) | 299 SOF_SSP_PORT_AMP(2) | 300 SOF_NUM_IDISP_HDMI(4)), 301 }, 302 { 303 .name = "adl_nau8825_def", 304 .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) | 305 SOF_SSP_PORT_AMP(1) | 306 SOF_NUM_IDISP_HDMI(4) | 307 SOF_SSP_PORT_BT_OFFLOAD(2) | 308 SOF_BT_OFFLOAD_PRESENT), 309 }, 310 { 311 .name = "rpl_nau8825_def", 312 .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) | 313 SOF_SSP_PORT_AMP(1) | 314 SOF_NUM_IDISP_HDMI(4) | 315 SOF_SSP_PORT_BT_OFFLOAD(2) | 316 SOF_BT_OFFLOAD_PRESENT), 317 }, 318 { 319 .name = "mtl_nau8825_def", 320 .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(2) | 321 SOF_SSP_PORT_AMP(0) | 322 SOF_SSP_PORT_BT_OFFLOAD(1) | 323 SOF_BT_OFFLOAD_PRESENT), 324 }, 325 { } 326 }; 327 MODULE_DEVICE_TABLE(platform, board_ids); 328 329 static struct platform_driver sof_audio = { 330 .probe = sof_audio_probe, 331 .driver = { 332 .name = "sof_nau8825", 333 .pm = &snd_soc_pm_ops, 334 }, 335 .id_table = board_ids, 336 }; 337 module_platform_driver(sof_audio) 338 339 /* Module information */ 340 MODULE_DESCRIPTION("SOF Audio Machine driver for NAU8825"); 341 MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>"); 342 MODULE_AUTHOR("Mac Chiang <mac.chiang@intel.com>"); 343 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>"); 344 MODULE_LICENSE("GPL"); 345 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); 346 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); 347 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_NUVOTON_COMMON); 348 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_REALTEK_COMMON); 349