1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * bytcht_es8316.c - ASoc Machine driver for Intel Baytrail/Cherrytrail 4 * platforms with Everest ES8316 SoC 5 * 6 * Copyright (C) 2017 Endless Mobile, Inc. 7 * Authors: David Yang <yangxiaohua@everest-semi.com>, 8 * Daniel Drake <drake@endlessm.com> 9 * 10 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 11 * 12 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 13 */ 14 #include <linux/acpi.h> 15 #include <linux/clk.h> 16 #include <linux/device.h> 17 #include <linux/dmi.h> 18 #include <linux/gpio/consumer.h> 19 #include <linux/i2c.h> 20 #include <linux/init.h> 21 #include <linux/input.h> 22 #include <linux/module.h> 23 #include <linux/platform_device.h> 24 #include <linux/slab.h> 25 #include <sound/jack.h> 26 #include <sound/pcm.h> 27 #include <sound/pcm_params.h> 28 #include <sound/soc.h> 29 #include <sound/soc-acpi.h> 30 #include "../atom/sst-atom-controls.h" 31 #include "../common/soc-intel-quirks.h" 32 33 /* jd-inv + terminating entry */ 34 #define MAX_NO_PROPS 2 35 36 struct byt_cht_es8316_private { 37 struct clk *mclk; 38 struct snd_soc_jack jack; 39 struct gpio_desc *speaker_en_gpio; 40 struct device *codec_dev; 41 bool speaker_en; 42 }; 43 44 enum { 45 BYT_CHT_ES8316_INTMIC_IN1_MAP, 46 BYT_CHT_ES8316_INTMIC_IN2_MAP, 47 }; 48 49 #define BYT_CHT_ES8316_MAP(quirk) ((quirk) & GENMASK(3, 0)) 50 #define BYT_CHT_ES8316_SSP0 BIT(16) 51 #define BYT_CHT_ES8316_MONO_SPEAKER BIT(17) 52 #define BYT_CHT_ES8316_JD_INVERTED BIT(18) 53 54 static unsigned long quirk; 55 56 static int quirk_override = -1; 57 module_param_named(quirk, quirk_override, int, 0444); 58 MODULE_PARM_DESC(quirk, "Board-specific quirk override"); 59 60 static void log_quirks(struct device *dev) 61 { 62 if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN1_MAP) 63 dev_info(dev, "quirk IN1_MAP enabled"); 64 if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN2_MAP) 65 dev_info(dev, "quirk IN2_MAP enabled"); 66 if (quirk & BYT_CHT_ES8316_SSP0) 67 dev_info(dev, "quirk SSP0 enabled"); 68 if (quirk & BYT_CHT_ES8316_MONO_SPEAKER) 69 dev_info(dev, "quirk MONO_SPEAKER enabled\n"); 70 if (quirk & BYT_CHT_ES8316_JD_INVERTED) 71 dev_info(dev, "quirk JD_INVERTED enabled\n"); 72 } 73 74 static int byt_cht_es8316_speaker_power_event(struct snd_soc_dapm_widget *w, 75 struct snd_kcontrol *kcontrol, int event) 76 { 77 struct snd_soc_card *card = w->dapm->card; 78 struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); 79 80 if (SND_SOC_DAPM_EVENT_ON(event)) 81 priv->speaker_en = true; 82 else 83 priv->speaker_en = false; 84 85 gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en); 86 87 return 0; 88 } 89 90 static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = { 91 SND_SOC_DAPM_SPK("Speaker", NULL), 92 SND_SOC_DAPM_HP("Headphone", NULL), 93 SND_SOC_DAPM_MIC("Headset Mic", NULL), 94 SND_SOC_DAPM_MIC("Internal Mic", NULL), 95 96 SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0, 97 byt_cht_es8316_speaker_power_event, 98 SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), 99 }; 100 101 static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = { 102 {"Headphone", NULL, "HPOL"}, 103 {"Headphone", NULL, "HPOR"}, 104 105 /* 106 * There is no separate speaker output instead the speakers are muxed to 107 * the HP outputs. The mux is controlled by the "Speaker Power" supply. 108 */ 109 {"Speaker", NULL, "HPOL"}, 110 {"Speaker", NULL, "HPOR"}, 111 {"Speaker", NULL, "Speaker Power"}, 112 }; 113 114 static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in1_map[] = { 115 {"MIC1", NULL, "Internal Mic"}, 116 {"MIC2", NULL, "Headset Mic"}, 117 }; 118 119 static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in2_map[] = { 120 {"MIC2", NULL, "Internal Mic"}, 121 {"MIC1", NULL, "Headset Mic"}, 122 }; 123 124 static const struct snd_soc_dapm_route byt_cht_es8316_ssp0_map[] = { 125 {"Playback", NULL, "ssp0 Tx"}, 126 {"ssp0 Tx", NULL, "modem_out"}, 127 {"modem_in", NULL, "ssp0 Rx"}, 128 {"ssp0 Rx", NULL, "Capture"}, 129 }; 130 131 static const struct snd_soc_dapm_route byt_cht_es8316_ssp2_map[] = { 132 {"Playback", NULL, "ssp2 Tx"}, 133 {"ssp2 Tx", NULL, "codec_out0"}, 134 {"ssp2 Tx", NULL, "codec_out1"}, 135 {"codec_in0", NULL, "ssp2 Rx" }, 136 {"codec_in1", NULL, "ssp2 Rx" }, 137 {"ssp2 Rx", NULL, "Capture"}, 138 }; 139 140 static const struct snd_kcontrol_new byt_cht_es8316_controls[] = { 141 SOC_DAPM_PIN_SWITCH("Speaker"), 142 SOC_DAPM_PIN_SWITCH("Headphone"), 143 SOC_DAPM_PIN_SWITCH("Headset Mic"), 144 SOC_DAPM_PIN_SWITCH("Internal Mic"), 145 }; 146 147 static struct snd_soc_jack_pin byt_cht_es8316_jack_pins[] = { 148 { 149 .pin = "Headphone", 150 .mask = SND_JACK_HEADPHONE, 151 }, 152 { 153 .pin = "Headset Mic", 154 .mask = SND_JACK_MICROPHONE, 155 }, 156 }; 157 158 static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) 159 { 160 struct snd_soc_component *codec = asoc_rtd_to_codec(runtime, 0)->component; 161 struct snd_soc_card *card = runtime->card; 162 struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); 163 const struct snd_soc_dapm_route *custom_map; 164 int num_routes; 165 int ret; 166 167 card->dapm.idle_bias_off = true; 168 169 switch (BYT_CHT_ES8316_MAP(quirk)) { 170 case BYT_CHT_ES8316_INTMIC_IN1_MAP: 171 default: 172 custom_map = byt_cht_es8316_intmic_in1_map; 173 num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in1_map); 174 break; 175 case BYT_CHT_ES8316_INTMIC_IN2_MAP: 176 custom_map = byt_cht_es8316_intmic_in2_map; 177 num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in2_map); 178 break; 179 } 180 ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); 181 if (ret) 182 return ret; 183 184 if (quirk & BYT_CHT_ES8316_SSP0) { 185 custom_map = byt_cht_es8316_ssp0_map; 186 num_routes = ARRAY_SIZE(byt_cht_es8316_ssp0_map); 187 } else { 188 custom_map = byt_cht_es8316_ssp2_map; 189 num_routes = ARRAY_SIZE(byt_cht_es8316_ssp2_map); 190 } 191 ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); 192 if (ret) 193 return ret; 194 195 /* 196 * The firmware might enable the clock at boot (this information 197 * may or may not be reflected in the enable clock register). 198 * To change the rate we must disable the clock first to cover these 199 * cases. Due to common clock framework restrictions that do not allow 200 * to disable a clock that has not been enabled, we need to enable 201 * the clock first. 202 */ 203 ret = clk_prepare_enable(priv->mclk); 204 if (!ret) 205 clk_disable_unprepare(priv->mclk); 206 207 ret = clk_set_rate(priv->mclk, 19200000); 208 if (ret) 209 dev_err(card->dev, "unable to set MCLK rate\n"); 210 211 ret = clk_prepare_enable(priv->mclk); 212 if (ret) 213 dev_err(card->dev, "unable to enable MCLK\n"); 214 215 ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(runtime, 0), 0, 19200000, 216 SND_SOC_CLOCK_IN); 217 if (ret < 0) { 218 dev_err(card->dev, "can't set codec clock %d\n", ret); 219 return ret; 220 } 221 222 ret = snd_soc_card_jack_new(card, "Headset", 223 SND_JACK_HEADSET | SND_JACK_BTN_0, 224 &priv->jack, byt_cht_es8316_jack_pins, 225 ARRAY_SIZE(byt_cht_es8316_jack_pins)); 226 if (ret) { 227 dev_err(card->dev, "jack creation failed %d\n", ret); 228 return ret; 229 } 230 231 snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); 232 snd_soc_component_set_jack(codec, &priv->jack, NULL); 233 234 return 0; 235 } 236 237 static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd, 238 struct snd_pcm_hw_params *params) 239 { 240 struct snd_interval *rate = hw_param_interval(params, 241 SNDRV_PCM_HW_PARAM_RATE); 242 struct snd_interval *channels = hw_param_interval(params, 243 SNDRV_PCM_HW_PARAM_CHANNELS); 244 int ret, bits; 245 246 /* The DSP will covert the FE rate to 48k, stereo */ 247 rate->min = rate->max = 48000; 248 channels->min = channels->max = 2; 249 250 if (quirk & BYT_CHT_ES8316_SSP0) { 251 /* set SSP0 to 16-bit */ 252 params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); 253 bits = 16; 254 } else { 255 /* set SSP2 to 24-bit */ 256 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); 257 bits = 24; 258 } 259 260 /* 261 * Default mode for SSP configuration is TDM 4 slot, override config 262 * with explicit setting to I2S 2ch 24-bit. The word length is set with 263 * dai_set_tdm_slot() since there is no other API exposed 264 */ 265 ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), 266 SND_SOC_DAIFMT_I2S | 267 SND_SOC_DAIFMT_NB_NF | 268 SND_SOC_DAIFMT_CBC_CFC 269 ); 270 if (ret < 0) { 271 dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); 272 return ret; 273 } 274 275 ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, bits); 276 if (ret < 0) { 277 dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); 278 return ret; 279 } 280 281 return 0; 282 } 283 284 static int byt_cht_es8316_aif1_startup(struct snd_pcm_substream *substream) 285 { 286 return snd_pcm_hw_constraint_single(substream->runtime, 287 SNDRV_PCM_HW_PARAM_RATE, 48000); 288 } 289 290 static const struct snd_soc_ops byt_cht_es8316_aif1_ops = { 291 .startup = byt_cht_es8316_aif1_startup, 292 }; 293 294 SND_SOC_DAILINK_DEF(dummy, 295 DAILINK_COMP_ARRAY(COMP_DUMMY())); 296 297 SND_SOC_DAILINK_DEF(media, 298 DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai"))); 299 300 SND_SOC_DAILINK_DEF(deepbuffer, 301 DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai"))); 302 303 SND_SOC_DAILINK_DEF(ssp2_port, 304 DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port"))); 305 SND_SOC_DAILINK_DEF(ssp2_codec, 306 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8316:00", "ES8316 HiFi"))); 307 308 SND_SOC_DAILINK_DEF(platform, 309 DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform"))); 310 311 static struct snd_soc_dai_link byt_cht_es8316_dais[] = { 312 [MERR_DPCM_AUDIO] = { 313 .name = "Audio Port", 314 .stream_name = "Audio", 315 .nonatomic = true, 316 .dynamic = 1, 317 .dpcm_playback = 1, 318 .dpcm_capture = 1, 319 .ops = &byt_cht_es8316_aif1_ops, 320 SND_SOC_DAILINK_REG(media, dummy, platform), 321 }, 322 323 [MERR_DPCM_DEEP_BUFFER] = { 324 .name = "Deep-Buffer Audio Port", 325 .stream_name = "Deep-Buffer Audio", 326 .nonatomic = true, 327 .dynamic = 1, 328 .dpcm_playback = 1, 329 .ops = &byt_cht_es8316_aif1_ops, 330 SND_SOC_DAILINK_REG(deepbuffer, dummy, platform), 331 }, 332 333 /* back ends */ 334 { 335 .name = "SSP2-Codec", 336 .id = 0, 337 .no_pcm = 1, 338 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 339 | SND_SOC_DAIFMT_CBC_CFC, 340 .be_hw_params_fixup = byt_cht_es8316_codec_fixup, 341 .dpcm_playback = 1, 342 .dpcm_capture = 1, 343 .init = byt_cht_es8316_init, 344 SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform), 345 }, 346 }; 347 348 349 /* SoC card */ 350 static char codec_name[SND_ACPI_I2C_ID_LEN]; 351 #if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES) 352 static char long_name[50]; /* = "bytcht-es8316-*-spk-*-mic" */ 353 #endif 354 static char components_string[32]; /* = "cfg-spk:* cfg-mic:* */ 355 356 static int byt_cht_es8316_suspend(struct snd_soc_card *card) 357 { 358 struct snd_soc_component *component; 359 360 for_each_card_components(card, component) { 361 if (!strcmp(component->name, codec_name)) { 362 dev_dbg(component->dev, "disabling jack detect before suspend\n"); 363 snd_soc_component_set_jack(component, NULL, NULL); 364 break; 365 } 366 } 367 368 return 0; 369 } 370 371 static int byt_cht_es8316_resume(struct snd_soc_card *card) 372 { 373 struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); 374 struct snd_soc_component *component; 375 376 for_each_card_components(card, component) { 377 if (!strcmp(component->name, codec_name)) { 378 dev_dbg(component->dev, "re-enabling jack detect after resume\n"); 379 snd_soc_component_set_jack(component, &priv->jack, NULL); 380 break; 381 } 382 } 383 384 /* 385 * Some Cherry Trail boards with an ES8316 codec have a bug in their 386 * ACPI tables where the MSSL1680 touchscreen's _PS0 and _PS3 methods 387 * wrongly also set the speaker-enable GPIO to 1/0. Testing has shown 388 * that this really is a bug and the GPIO has no influence on the 389 * touchscreen at all. 390 * 391 * The silead.c touchscreen driver does not support runtime suspend, so 392 * the GPIO can only be changed underneath us during a system suspend. 393 * This resume() function runs from a pm complete() callback, and thus 394 * is guaranteed to run after the touchscreen driver/ACPI-subsys has 395 * brought the touchscreen back up again (and thus changed the GPIO). 396 * 397 * So to work around this we pass GPIOD_FLAGS_BIT_NONEXCLUSIVE when 398 * requesting the GPIO and we set its value here to undo any changes 399 * done by the touchscreen's broken _PS0 ACPI method. 400 */ 401 gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en); 402 403 return 0; 404 } 405 406 /* use space before codec name to simplify card ID, and simplify driver name */ 407 #define SOF_CARD_NAME "bytcht es8316" /* card name will be 'sof-bytcht es8316' */ 408 #define SOF_DRIVER_NAME "SOF" 409 410 #define CARD_NAME "bytcht-es8316" 411 #define DRIVER_NAME NULL /* card name will be used for driver name */ 412 413 static struct snd_soc_card byt_cht_es8316_card = { 414 .owner = THIS_MODULE, 415 .dai_link = byt_cht_es8316_dais, 416 .num_links = ARRAY_SIZE(byt_cht_es8316_dais), 417 .dapm_widgets = byt_cht_es8316_widgets, 418 .num_dapm_widgets = ARRAY_SIZE(byt_cht_es8316_widgets), 419 .dapm_routes = byt_cht_es8316_audio_map, 420 .num_dapm_routes = ARRAY_SIZE(byt_cht_es8316_audio_map), 421 .controls = byt_cht_es8316_controls, 422 .num_controls = ARRAY_SIZE(byt_cht_es8316_controls), 423 .fully_routed = true, 424 .suspend_pre = byt_cht_es8316_suspend, 425 .resume_post = byt_cht_es8316_resume, 426 }; 427 428 static const struct acpi_gpio_params first_gpio = { 0, 0, false }; 429 430 static const struct acpi_gpio_mapping byt_cht_es8316_gpios[] = { 431 { "speaker-enable-gpios", &first_gpio, 1 }, 432 { }, 433 }; 434 435 /* Please keep this list alphabetically sorted */ 436 static const struct dmi_system_id byt_cht_es8316_quirk_table[] = { 437 { /* Irbis NB41 */ 438 .matches = { 439 DMI_MATCH(DMI_SYS_VENDOR, "IRBIS"), 440 DMI_MATCH(DMI_PRODUCT_NAME, "NB41"), 441 }, 442 .driver_data = (void *)(BYT_CHT_ES8316_SSP0 443 | BYT_CHT_ES8316_INTMIC_IN2_MAP 444 | BYT_CHT_ES8316_JD_INVERTED), 445 }, 446 { /* Teclast X98 Plus II */ 447 .matches = { 448 DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"), 449 DMI_MATCH(DMI_PRODUCT_NAME, "X98 Plus II"), 450 }, 451 .driver_data = (void *)(BYT_CHT_ES8316_INTMIC_IN1_MAP 452 | BYT_CHT_ES8316_JD_INVERTED), 453 }, 454 {} 455 }; 456 457 static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) 458 { 459 struct device *dev = &pdev->dev; 460 static const char * const mic_name[] = { "in1", "in2" }; 461 struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); 462 struct property_entry props[MAX_NO_PROPS] = {}; 463 struct byt_cht_es8316_private *priv; 464 const struct dmi_system_id *dmi_id; 465 struct fwnode_handle *fwnode; 466 const char *platform_name; 467 struct acpi_device *adev; 468 struct device *codec_dev; 469 bool sof_parent; 470 unsigned int cnt = 0; 471 int dai_index = 0; 472 int i; 473 int ret = 0; 474 475 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 476 if (!priv) 477 return -ENOMEM; 478 479 /* fix index of codec dai */ 480 for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) { 481 if (!strcmp(byt_cht_es8316_dais[i].codecs->name, 482 "i2c-ESSX8316:00")) { 483 dai_index = i; 484 break; 485 } 486 } 487 488 /* fixup codec name based on HID */ 489 adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); 490 if (adev) { 491 snprintf(codec_name, sizeof(codec_name), 492 "i2c-%s", acpi_dev_name(adev)); 493 put_device(&adev->dev); 494 byt_cht_es8316_dais[dai_index].codecs->name = codec_name; 495 } else { 496 dev_err(dev, "Error cannot find '%s' dev\n", mach->id); 497 return -ENXIO; 498 } 499 500 /* override plaform name, if required */ 501 byt_cht_es8316_card.dev = dev; 502 platform_name = mach->mach_params.platform; 503 504 ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_es8316_card, 505 platform_name); 506 if (ret) 507 return ret; 508 509 /* Check for BYTCR or other platform and setup quirks */ 510 dmi_id = dmi_first_match(byt_cht_es8316_quirk_table); 511 if (dmi_id) { 512 quirk = (unsigned long)dmi_id->driver_data; 513 } else if (soc_intel_is_byt() && 514 mach->mach_params.acpi_ipc_irq_index == 0) { 515 /* On BYTCR default to SSP0, internal-mic-in2-map, mono-spk */ 516 quirk = BYT_CHT_ES8316_SSP0 | BYT_CHT_ES8316_INTMIC_IN2_MAP | 517 BYT_CHT_ES8316_MONO_SPEAKER; 518 } else { 519 /* Others default to internal-mic-in1-map, mono-speaker */ 520 quirk = BYT_CHT_ES8316_INTMIC_IN1_MAP | 521 BYT_CHT_ES8316_MONO_SPEAKER; 522 } 523 if (quirk_override != -1) { 524 dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n", 525 quirk, quirk_override); 526 quirk = quirk_override; 527 } 528 log_quirks(dev); 529 530 if (quirk & BYT_CHT_ES8316_SSP0) 531 byt_cht_es8316_dais[dai_index].cpus->dai_name = "ssp0-port"; 532 533 /* get the clock */ 534 priv->mclk = devm_clk_get(dev, "pmc_plt_clk_3"); 535 if (IS_ERR(priv->mclk)) 536 return dev_err_probe(dev, PTR_ERR(priv->mclk), "clk_get pmc_plt_clk_3 failed\n"); 537 538 /* get speaker enable GPIO */ 539 codec_dev = acpi_get_first_physical_node(adev); 540 if (!codec_dev) 541 return -EPROBE_DEFER; 542 priv->codec_dev = get_device(codec_dev); 543 544 if (quirk & BYT_CHT_ES8316_JD_INVERTED) 545 props[cnt++] = PROPERTY_ENTRY_BOOL("everest,jack-detect-inverted"); 546 547 if (cnt) { 548 fwnode = fwnode_create_software_node(props, NULL); 549 if (IS_ERR(fwnode)) { 550 put_device(codec_dev); 551 return PTR_ERR(fwnode); 552 } 553 554 ret = device_add_software_node(codec_dev, to_software_node(fwnode)); 555 556 fwnode_handle_put(fwnode); 557 558 if (ret) { 559 put_device(codec_dev); 560 return ret; 561 } 562 } 563 564 devm_acpi_dev_add_driver_gpios(codec_dev, byt_cht_es8316_gpios); 565 priv->speaker_en_gpio = 566 gpiod_get_optional(codec_dev, "speaker-enable", 567 /* see comment in byt_cht_es8316_resume() */ 568 GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); 569 if (IS_ERR(priv->speaker_en_gpio)) { 570 ret = dev_err_probe(dev, PTR_ERR(priv->speaker_en_gpio), 571 "get speaker GPIO failed\n"); 572 goto err_put_codec; 573 } 574 575 snprintf(components_string, sizeof(components_string), 576 "cfg-spk:%s cfg-mic:%s", 577 (quirk & BYT_CHT_ES8316_MONO_SPEAKER) ? "1" : "2", 578 mic_name[BYT_CHT_ES8316_MAP(quirk)]); 579 byt_cht_es8316_card.components = components_string; 580 #if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES) 581 snprintf(long_name, sizeof(long_name), "bytcht-es8316-%s-spk-%s-mic", 582 (quirk & BYT_CHT_ES8316_MONO_SPEAKER) ? "mono" : "stereo", 583 mic_name[BYT_CHT_ES8316_MAP(quirk)]); 584 byt_cht_es8316_card.long_name = long_name; 585 #endif 586 587 sof_parent = snd_soc_acpi_sof_parent(dev); 588 589 /* set card and driver name */ 590 if (sof_parent) { 591 byt_cht_es8316_card.name = SOF_CARD_NAME; 592 byt_cht_es8316_card.driver_name = SOF_DRIVER_NAME; 593 } else { 594 byt_cht_es8316_card.name = CARD_NAME; 595 byt_cht_es8316_card.driver_name = DRIVER_NAME; 596 } 597 598 /* set pm ops */ 599 if (sof_parent) 600 dev->driver->pm = &snd_soc_pm_ops; 601 602 /* register the soc card */ 603 snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv); 604 605 ret = devm_snd_soc_register_card(dev, &byt_cht_es8316_card); 606 if (ret) { 607 gpiod_put(priv->speaker_en_gpio); 608 dev_err(dev, "snd_soc_register_card failed: %d\n", ret); 609 goto err_put_codec; 610 } 611 platform_set_drvdata(pdev, &byt_cht_es8316_card); 612 return 0; 613 614 err_put_codec: 615 device_remove_software_node(priv->codec_dev); 616 put_device(priv->codec_dev); 617 return ret; 618 } 619 620 static int snd_byt_cht_es8316_mc_remove(struct platform_device *pdev) 621 { 622 struct snd_soc_card *card = platform_get_drvdata(pdev); 623 struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); 624 625 gpiod_put(priv->speaker_en_gpio); 626 device_remove_software_node(priv->codec_dev); 627 put_device(priv->codec_dev); 628 return 0; 629 } 630 631 static struct platform_driver snd_byt_cht_es8316_mc_driver = { 632 .driver = { 633 .name = "bytcht_es8316", 634 }, 635 .probe = snd_byt_cht_es8316_mc_probe, 636 .remove = snd_byt_cht_es8316_mc_remove, 637 }; 638 639 module_platform_driver(snd_byt_cht_es8316_mc_driver); 640 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver"); 641 MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>"); 642 MODULE_LICENSE("GPL v2"); 643 MODULE_ALIAS("platform:bytcht_es8316"); 644