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 const 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 max_98373_dai_link(dev, ctx->amp_link); 217 break; 218 case CODEC_NAU8318: 219 nau8318_set_dai_link(ctx->amp_link); 220 break; 221 case CODEC_RT1015P: 222 sof_rt1015p_dai_link(ctx->amp_link); 223 break; 224 case CODEC_RT1019P: 225 sof_rt1019p_dai_link(ctx->amp_link); 226 break; 227 default: 228 dev_err(dev, "invalid amp type %d\n", ctx->amp_type); 229 return -EINVAL; 230 } 231 232 return 0; 233 } 234 235 static int sof_audio_probe(struct platform_device *pdev) 236 { 237 struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; 238 struct sof_card_private *ctx; 239 int ret; 240 241 if (pdev->id_entry && pdev->id_entry->driver_data) 242 sof_nau8825_quirk = (unsigned long)pdev->id_entry->driver_data; 243 244 dev_dbg(&pdev->dev, "sof_nau8825_quirk = %lx\n", sof_nau8825_quirk); 245 246 /* initialize ctx with board quirk */ 247 ctx = sof_intel_board_get_ctx(&pdev->dev, sof_nau8825_quirk); 248 if (!ctx) 249 return -ENOMEM; 250 251 if (mach->mach_params.codec_mask & IDISP_CODEC_MASK) 252 ctx->hdmi.idisp_codec = true; 253 254 /* update dai_link */ 255 ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_nau8825, ctx); 256 if (ret) 257 return ret; 258 259 /* update codec_conf */ 260 switch (ctx->amp_type) { 261 case CODEC_MAX98373: 262 max_98373_set_codec_conf(&sof_audio_card_nau8825); 263 break; 264 case CODEC_RT1015P: 265 sof_rt1015p_codec_conf(&sof_audio_card_nau8825); 266 break; 267 case CODEC_MAX98360A: 268 case CODEC_NAU8318: 269 case CODEC_RT1019P: 270 case CODEC_NONE: 271 /* no codec conf required */ 272 break; 273 default: 274 dev_err(&pdev->dev, "invalid amp type %d\n", ctx->amp_type); 275 return -EINVAL; 276 } 277 278 sof_audio_card_nau8825.dev = &pdev->dev; 279 280 /* set platform name for each dailink */ 281 ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_nau8825, 282 mach->mach_params.platform); 283 if (ret) 284 return ret; 285 286 snd_soc_card_set_drvdata(&sof_audio_card_nau8825, ctx); 287 288 return devm_snd_soc_register_card(&pdev->dev, 289 &sof_audio_card_nau8825); 290 } 291 292 static const struct platform_device_id board_ids[] = { 293 { 294 .name = "adl_rt1019p_8825", 295 .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) | 296 SOF_SSP_PORT_AMP(2) | 297 SOF_NUM_IDISP_HDMI(4)), 298 }, 299 { 300 .name = "adl_nau8825_def", 301 .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) | 302 SOF_SSP_PORT_AMP(1) | 303 SOF_NUM_IDISP_HDMI(4) | 304 SOF_SSP_PORT_BT_OFFLOAD(2) | 305 SOF_BT_OFFLOAD_PRESENT), 306 }, 307 { 308 .name = "rpl_nau8825_def", 309 .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(0) | 310 SOF_SSP_PORT_AMP(1) | 311 SOF_NUM_IDISP_HDMI(4) | 312 SOF_SSP_PORT_BT_OFFLOAD(2) | 313 SOF_BT_OFFLOAD_PRESENT), 314 }, 315 { 316 .name = "mtl_nau8825_def", 317 .driver_data = (kernel_ulong_t)(SOF_SSP_PORT_CODEC(2) | 318 SOF_SSP_PORT_AMP(0) | 319 SOF_SSP_PORT_BT_OFFLOAD(1) | 320 SOF_BT_OFFLOAD_PRESENT), 321 }, 322 { } 323 }; 324 MODULE_DEVICE_TABLE(platform, board_ids); 325 326 static struct platform_driver sof_audio = { 327 .probe = sof_audio_probe, 328 .driver = { 329 .name = "sof_nau8825", 330 .pm = &snd_soc_pm_ops, 331 }, 332 .id_table = board_ids, 333 }; 334 module_platform_driver(sof_audio) 335 336 /* Module information */ 337 MODULE_DESCRIPTION("SOF Audio Machine driver for NAU8825"); 338 MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>"); 339 MODULE_AUTHOR("Mac Chiang <mac.chiang@intel.com>"); 340 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>"); 341 MODULE_LICENSE("GPL"); 342 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); 343 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); 344 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_NUVOTON_COMMON); 345 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_REALTEK_COMMON); 346