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