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