1 // SPDX-License-Identifier: GPL-2.0-only 2 // Copyright(c) 2019 Intel Corporation. 3 4 /* 5 * Intel SOF Machine driver for Dialog headphone codec 6 */ 7 8 #include <linux/input.h> 9 #include <linux/module.h> 10 #include <sound/jack.h> 11 #include <sound/pcm.h> 12 #include <sound/pcm_params.h> 13 #include <linux/platform_device.h> 14 #include <sound/soc.h> 15 #include <sound/soc-acpi.h> 16 #include <sound/sof.h> 17 #include "../../codecs/da7219.h" 18 #include "sof_board_helpers.h" 19 #include "sof_maxim_common.h" 20 21 /* Driver-specific board quirks: from bit 0 to 7 */ 22 #define SOF_DA7219_JSL_BOARD BIT(0) 23 #define SOF_DA7219_MCLK_EN BIT(1) 24 25 #define DIALOG_CODEC_DAI "da7219-hifi" 26 27 static int platform_clock_control(struct snd_soc_dapm_widget *w, 28 struct snd_kcontrol *k, int event) 29 { 30 struct snd_soc_dapm_context *dapm = w->dapm; 31 struct snd_soc_card *card = dapm->card; 32 struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); 33 struct snd_soc_dai *codec_dai; 34 int ret = 0; 35 36 if (ctx->da7219.pll_bypass) 37 return ret; 38 39 /* PLL SRM mode */ 40 codec_dai = snd_soc_card_get_codec_dai(card, DIALOG_CODEC_DAI); 41 if (!codec_dai) { 42 dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n"); 43 return -EIO; 44 } 45 46 if (SND_SOC_DAPM_EVENT_OFF(event)) { 47 ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 48 0, 0); 49 if (ret) 50 dev_err(card->dev, "failed to stop PLL: %d\n", ret); 51 } else if (SND_SOC_DAPM_EVENT_ON(event)) { 52 dev_dbg(card->dev, "pll srm mode\n"); 53 54 ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL_SRM, 55 0, DA7219_PLL_FREQ_OUT_98304); 56 if (ret) 57 dev_err(card->dev, "failed to start PLL: %d\n", ret); 58 } 59 60 return ret; 61 } 62 63 static const struct snd_kcontrol_new controls[] = { 64 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 65 SOC_DAPM_PIN_SWITCH("Headset Mic"), 66 SOC_DAPM_PIN_SWITCH("Line Out"), 67 }; 68 69 static const struct snd_soc_dapm_widget widgets[] = { 70 SND_SOC_DAPM_HP("Headphone Jack", NULL), 71 SND_SOC_DAPM_MIC("Headset Mic", NULL), 72 SND_SOC_DAPM_LINE("Line Out", NULL), 73 74 SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, 75 platform_clock_control, SND_SOC_DAPM_POST_PMD | 76 SND_SOC_DAPM_PRE_PMU), 77 }; 78 79 static const struct snd_soc_dapm_route audio_map[] = { 80 { "Headphone Jack", NULL, "HPL" }, 81 { "Headphone Jack", NULL, "HPR" }, 82 83 { "MIC", NULL, "Headset Mic" }, 84 85 { "Headphone Jack", NULL, "Platform Clock" }, 86 { "Headset Mic", NULL, "Platform Clock" }, 87 { "Line Out", NULL, "Platform Clock" }, 88 }; 89 90 static struct snd_soc_jack_pin jack_pins[] = { 91 { 92 .pin = "Headphone Jack", 93 .mask = SND_JACK_HEADPHONE, 94 }, 95 { 96 .pin = "Headset Mic", 97 .mask = SND_JACK_MICROPHONE, 98 }, 99 { 100 .pin = "Line Out", 101 .mask = SND_JACK_LINEOUT, 102 }, 103 }; 104 105 static int da7219_codec_init(struct snd_soc_pcm_runtime *rtd) 106 { 107 struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); 108 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 109 struct snd_soc_component *component = codec_dai->component; 110 struct snd_soc_jack *jack = &ctx->headset_jack; 111 int mclk_rate, ret; 112 113 mclk_rate = sof_dai_get_mclk(rtd); 114 if (mclk_rate <= 0) { 115 dev_err(rtd->dev, "invalid mclk freq %d\n", mclk_rate); 116 return -EINVAL; 117 } 118 119 ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, mclk_rate, 120 SND_SOC_CLOCK_IN); 121 if (ret) { 122 dev_err(rtd->dev, "fail to set sysclk, ret %d\n", ret); 123 return ret; 124 } 125 126 /* 127 * Use PLL bypass mode if MCLK is available, be sure to set the 128 * frequency of MCLK to 12.288 or 24.576MHz on topology side. 129 */ 130 if (ctx->da7219.mclk_en && 131 (mclk_rate == 12288000 || mclk_rate == 24576000)) { 132 /* PLL bypass mode */ 133 dev_dbg(rtd->dev, "pll bypass mode, mclk rate %d\n", mclk_rate); 134 135 ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0); 136 if (ret) { 137 dev_err(rtd->dev, "fail to set pll, ret %d\n", ret); 138 return ret; 139 } 140 141 ctx->da7219.pll_bypass = true; 142 } 143 144 /* 145 * Headset buttons map to the google Reference headset. 146 * These can be configured by userspace. 147 */ 148 ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", 149 SND_JACK_HEADSET | SND_JACK_BTN_0 | 150 SND_JACK_BTN_1 | SND_JACK_BTN_2 | 151 SND_JACK_BTN_3 | SND_JACK_LINEOUT, 152 jack, jack_pins, ARRAY_SIZE(jack_pins)); 153 if (ret) { 154 dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); 155 return ret; 156 } 157 158 snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); 159 snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); 160 snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); 161 snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); 162 163 ret = snd_soc_component_set_jack(component, jack, NULL); 164 if (ret) { 165 dev_err(rtd->dev, "fail to set component jack, ret %d\n", ret); 166 return ret; 167 } 168 169 return ret; 170 } 171 172 static void da7219_codec_exit(struct snd_soc_pcm_runtime *rtd) 173 { 174 struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; 175 176 snd_soc_component_set_jack(component, NULL, NULL); 177 } 178 179 static int max98373_hw_params(struct snd_pcm_substream *substream, 180 struct snd_pcm_hw_params *params) 181 { 182 struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream); 183 int ret, j; 184 185 for (j = 0; j < runtime->dai_link->num_codecs; j++) { 186 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, j); 187 188 if (!strcmp(codec_dai->component->name, MAX_98373_DEV0_NAME)) { 189 /* vmon_slot_no = 0 imon_slot_no = 1 for TX slots */ 190 ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 3, 4, 16); 191 if (ret < 0) { 192 dev_err(runtime->dev, "DEV0 TDM slot err:%d\n", ret); 193 return ret; 194 } 195 } 196 if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) { 197 /* vmon_slot_no = 2 imon_slot_no = 3 for TX slots */ 198 ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC, 3, 4, 16); 199 if (ret < 0) { 200 dev_err(runtime->dev, "DEV1 TDM slot err:%d\n", ret); 201 return ret; 202 } 203 } 204 } 205 206 return 0; 207 } 208 209 static const struct snd_soc_ops max98373_ops = { 210 .hw_params = max98373_hw_params, 211 }; 212 213 static int card_late_probe(struct snd_soc_card *card) 214 { 215 return sof_intel_board_card_late_probe(card); 216 } 217 218 static struct snd_soc_card card_da7219 = { 219 .name = "da7219", /* the sof- prefix is added by the core */ 220 .owner = THIS_MODULE, 221 .controls = controls, 222 .num_controls = ARRAY_SIZE(controls), 223 .dapm_widgets = widgets, 224 .num_dapm_widgets = ARRAY_SIZE(widgets), 225 .dapm_routes = audio_map, 226 .num_dapm_routes = ARRAY_SIZE(audio_map), 227 .fully_routed = true, 228 .late_probe = card_late_probe, 229 }; 230 231 static struct snd_soc_dai_link_component da7219_component[] = { 232 { 233 .name = "i2c-DLGS7219:00", 234 .dai_name = DIALOG_CODEC_DAI, 235 } 236 }; 237 238 static int 239 sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card, 240 struct sof_card_private *ctx) 241 { 242 int ret; 243 244 ret = sof_intel_board_set_dai_link(dev, card, ctx); 245 if (ret) 246 return ret; 247 248 if (!ctx->codec_link) { 249 dev_err(dev, "codec link not available"); 250 return -EINVAL; 251 } 252 253 /* codec-specific fields for headphone codec */ 254 ctx->codec_link->codecs = da7219_component; 255 ctx->codec_link->num_codecs = ARRAY_SIZE(da7219_component); 256 ctx->codec_link->init = da7219_codec_init; 257 ctx->codec_link->exit = da7219_codec_exit; 258 259 if (ctx->amp_type == CODEC_NONE) 260 return 0; 261 262 if (!ctx->amp_link) { 263 dev_err(dev, "amp link not available"); 264 return -EINVAL; 265 } 266 267 /* codec-specific fields for speaker amplifier */ 268 switch (ctx->amp_type) { 269 case CODEC_MAX98360A: 270 max_98360a_dai_link(ctx->amp_link); 271 break; 272 case CODEC_MAX98373: 273 ctx->amp_link->codecs = max_98373_components; 274 ctx->amp_link->num_codecs = ARRAY_SIZE(max_98373_components); 275 ctx->amp_link->init = max_98373_spk_codec_init; 276 if (ctx->da7219.is_jsl_board) { 277 ctx->amp_link->ops = &max98373_ops; /* use local ops */ 278 } else { 279 /* TBD: implement the amp for later platform */ 280 dev_err(dev, "max98373 not support yet\n"); 281 return -EINVAL; 282 } 283 break; 284 default: 285 dev_err(dev, "invalid amp type %d\n", ctx->amp_type); 286 return -EINVAL; 287 } 288 289 return 0; 290 } 291 292 #define JSL_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_AMP, \ 293 SOF_LINK_CODEC, \ 294 SOF_LINK_DMIC01, \ 295 SOF_LINK_IDISP_HDMI, \ 296 SOF_LINK_DMIC16K, \ 297 SOF_LINK_NONE, \ 298 SOF_LINK_NONE) 299 300 static int audio_probe(struct platform_device *pdev) 301 { 302 struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; 303 struct sof_card_private *ctx; 304 unsigned long board_quirk = 0; 305 int ret; 306 307 if (pdev->id_entry && pdev->id_entry->driver_data) 308 board_quirk = (unsigned long)pdev->id_entry->driver_data; 309 310 dev_dbg(&pdev->dev, "board_quirk = %lx\n", board_quirk); 311 312 /* initialize ctx with board quirk */ 313 ctx = sof_intel_board_get_ctx(&pdev->dev, board_quirk); 314 if (!ctx) 315 return -ENOMEM; 316 317 if (mach->mach_params.codec_mask & IDISP_CODEC_MASK) 318 ctx->hdmi.idisp_codec = true; 319 320 if (board_quirk & SOF_DA7219_JSL_BOARD) { 321 ctx->da7219.is_jsl_board = true; 322 323 /* overwrite the DAI link order for JSL boards */ 324 ctx->link_order_overwrite = JSL_LINK_ORDER; 325 326 /* backward-compatible with existing devices */ 327 switch (ctx->amp_type) { 328 case CODEC_MAX98360A: 329 card_da7219.name = devm_kstrdup(&pdev->dev, 330 "da7219max98360a", 331 GFP_KERNEL); 332 break; 333 case CODEC_MAX98373: 334 card_da7219.name = devm_kstrdup(&pdev->dev, "da7219max", 335 GFP_KERNEL); 336 break; 337 default: 338 break; 339 } 340 } 341 342 if (board_quirk & SOF_DA7219_MCLK_EN) 343 ctx->da7219.mclk_en = true; 344 345 /* update dai_link */ 346 ret = sof_card_dai_links_create(&pdev->dev, &card_da7219, ctx); 347 if (ret) 348 return ret; 349 350 /* update codec_conf */ 351 switch (ctx->amp_type) { 352 case CODEC_MAX98373: 353 max_98373_set_codec_conf(&card_da7219); 354 break; 355 case CODEC_MAX98360A: 356 case CODEC_NONE: 357 /* no codec conf required */ 358 break; 359 default: 360 dev_err(&pdev->dev, "invalid amp type %d\n", ctx->amp_type); 361 return -EINVAL; 362 } 363 364 card_da7219.dev = &pdev->dev; 365 366 ret = snd_soc_fixup_dai_links_platform_name(&card_da7219, 367 mach->mach_params.platform); 368 if (ret) 369 return ret; 370 371 snd_soc_card_set_drvdata(&card_da7219, ctx); 372 373 return devm_snd_soc_register_card(&pdev->dev, &card_da7219); 374 } 375 376 static const struct platform_device_id board_ids[] = { 377 { 378 .name = "jsl_da7219_def", 379 .driver_data = (kernel_ulong_t)(SOF_DA7219_JSL_BOARD | 380 SOF_SSP_PORT_CODEC(0) | 381 SOF_SSP_PORT_AMP(1)), 382 }, 383 { 384 .name = "adl_da7219_def", 385 .driver_data = (kernel_ulong_t)(SOF_DA7219_MCLK_EN | 386 SOF_SSP_PORT_CODEC(0) | 387 SOF_SSP_PORT_AMP(1) | 388 SOF_NUM_IDISP_HDMI(4) | 389 SOF_SSP_PORT_BT_OFFLOAD(2) | 390 SOF_BT_OFFLOAD_PRESENT), 391 }, 392 { 393 .name = "rpl_da7219_def", 394 .driver_data = (kernel_ulong_t)(SOF_DA7219_MCLK_EN | 395 SOF_SSP_PORT_CODEC(0) | 396 SOF_SSP_PORT_AMP(1) | 397 SOF_NUM_IDISP_HDMI(4) | 398 SOF_SSP_PORT_BT_OFFLOAD(2) | 399 SOF_BT_OFFLOAD_PRESENT), 400 }, 401 { 402 .name = "mtl_da7219_def", 403 .driver_data = (kernel_ulong_t)(SOF_DA7219_MCLK_EN | 404 SOF_SSP_PORT_CODEC(2) | 405 SOF_SSP_PORT_AMP(0) | 406 SOF_SSP_PORT_BT_OFFLOAD(1) | 407 SOF_BT_OFFLOAD_PRESENT), 408 }, 409 { } 410 }; 411 MODULE_DEVICE_TABLE(platform, board_ids); 412 413 static struct platform_driver audio = { 414 .probe = audio_probe, 415 .driver = { 416 .name = "sof_da7219", 417 .pm = &snd_soc_pm_ops, 418 }, 419 .id_table = board_ids, 420 }; 421 module_platform_driver(audio) 422 423 /* Module information */ 424 MODULE_DESCRIPTION("ASoC Intel(R) SOF Machine driver for Dialog codec"); 425 MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>"); 426 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>"); 427 MODULE_LICENSE("GPL v2"); 428 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); 429 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); 430