18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// ASoC DPCM Machine driver for Baytrail / Cherrytrail platforms with 48c2ecf20Sopenharmony_ci// CX2072X codec 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/acpi.h> 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <sound/pcm.h> 148c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 158c2ecf20Sopenharmony_ci#include <sound/jack.h> 168c2ecf20Sopenharmony_ci#include <sound/soc.h> 178c2ecf20Sopenharmony_ci#include <sound/soc-acpi.h> 188c2ecf20Sopenharmony_ci#include "../../codecs/cx2072x.h" 198c2ecf20Sopenharmony_ci#include "../atom/sst-atom-controls.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget byt_cht_cx2072x_widgets[] = { 228c2ecf20Sopenharmony_ci SND_SOC_DAPM_HP("Headphone", NULL), 238c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Headset Mic", NULL), 248c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Int Mic", NULL), 258c2ecf20Sopenharmony_ci SND_SOC_DAPM_SPK("Ext Spk", NULL), 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route byt_cht_cx2072x_audio_map[] = { 298c2ecf20Sopenharmony_ci /* External Speakers: HFL, HFR */ 308c2ecf20Sopenharmony_ci {"Headphone", NULL, "PORTA"}, 318c2ecf20Sopenharmony_ci {"Ext Spk", NULL, "PORTG"}, 328c2ecf20Sopenharmony_ci {"PORTC", NULL, "Int Mic"}, 338c2ecf20Sopenharmony_ci {"PORTD", NULL, "Headset Mic"}, 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci {"Playback", NULL, "ssp2 Tx"}, 368c2ecf20Sopenharmony_ci {"ssp2 Tx", NULL, "codec_out0"}, 378c2ecf20Sopenharmony_ci {"ssp2 Tx", NULL, "codec_out1"}, 388c2ecf20Sopenharmony_ci {"codec_in0", NULL, "ssp2 Rx"}, 398c2ecf20Sopenharmony_ci {"codec_in1", NULL, "ssp2 Rx"}, 408c2ecf20Sopenharmony_ci {"ssp2 Rx", NULL, "Capture"}, 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new byt_cht_cx2072x_controls[] = { 448c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headphone"), 458c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headset Mic"), 468c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Int Mic"), 478c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Ext Spk"), 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct snd_soc_jack byt_cht_cx2072x_headset; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* Headset jack detection DAPM pins */ 538c2ecf20Sopenharmony_cistatic struct snd_soc_jack_pin byt_cht_cx2072x_headset_pins[] = { 548c2ecf20Sopenharmony_ci { 558c2ecf20Sopenharmony_ci .pin = "Headset Mic", 568c2ecf20Sopenharmony_ci .mask = SND_JACK_MICROPHONE, 578c2ecf20Sopenharmony_ci }, 588c2ecf20Sopenharmony_ci { 598c2ecf20Sopenharmony_ci .pin = "Headphone", 608c2ecf20Sopenharmony_ci .mask = SND_JACK_HEADPHONE, 618c2ecf20Sopenharmony_ci }, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic const struct acpi_gpio_params byt_cht_cx2072x_headset_gpios; 658c2ecf20Sopenharmony_cistatic const struct acpi_gpio_mapping byt_cht_cx2072x_acpi_gpios[] = { 668c2ecf20Sopenharmony_ci { "headset-gpios", &byt_cht_cx2072x_headset_gpios, 1 }, 678c2ecf20Sopenharmony_ci {}, 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct snd_soc_card *card = rtd->card; 738c2ecf20Sopenharmony_ci struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component; 748c2ecf20Sopenharmony_ci int ret; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (devm_acpi_dev_add_driver_gpios(codec->dev, 778c2ecf20Sopenharmony_ci byt_cht_cx2072x_acpi_gpios)) 788c2ecf20Sopenharmony_ci dev_warn(rtd->dev, "Unable to add GPIO mapping table\n"); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci card->dapm.idle_bias_off = true; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* set the default PLL rate, the clock is handled by the codec driver */ 838c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), CX2072X_MCLK_EXTERNAL_PLL, 848c2ecf20Sopenharmony_ci 19200000, SND_SOC_CLOCK_IN); 858c2ecf20Sopenharmony_ci if (ret) { 868c2ecf20Sopenharmony_ci dev_err(rtd->dev, "Could not set sysclk\n"); 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ret = snd_soc_card_jack_new(card, "Headset", 918c2ecf20Sopenharmony_ci SND_JACK_HEADSET | SND_JACK_BTN_0, 928c2ecf20Sopenharmony_ci &byt_cht_cx2072x_headset, 938c2ecf20Sopenharmony_ci byt_cht_cx2072x_headset_pins, 948c2ecf20Sopenharmony_ci ARRAY_SIZE(byt_cht_cx2072x_headset_pins)); 958c2ecf20Sopenharmony_ci if (ret) 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci snd_soc_component_set_jack(codec, &byt_cht_cx2072x_headset, NULL); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci snd_soc_dai_set_bclk_ratio(asoc_rtd_to_codec(rtd, 0), 50); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd, 1068c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct snd_interval *rate = 1098c2ecf20Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 1108c2ecf20Sopenharmony_ci struct snd_interval *channels = 1118c2ecf20Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); 1128c2ecf20Sopenharmony_ci int ret; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* The DSP will covert the FE rate to 48k, stereo, 24bits */ 1158c2ecf20Sopenharmony_ci rate->min = rate->max = 48000; 1168c2ecf20Sopenharmony_ci channels->min = channels->max = 2; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* set SSP2 to 24-bit */ 1198c2ecf20Sopenharmony_ci params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * Default mode for SSP configuration is TDM 4 slot, override config 1238c2ecf20Sopenharmony_ci * with explicit setting to I2S 2ch 24-bit. The word length is set with 1248c2ecf20Sopenharmony_ci * dai_set_tdm_slot() since there is no other API exposed 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), 1278c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_I2S | 1288c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_NB_NF | 1298c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS); 1308c2ecf20Sopenharmony_ci if (ret < 0) { 1318c2ecf20Sopenharmony_ci dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); 1328c2ecf20Sopenharmony_ci return ret; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24); 1368c2ecf20Sopenharmony_ci if (ret < 0) { 1378c2ecf20Sopenharmony_ci dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); 1388c2ecf20Sopenharmony_ci return ret; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int byt_cht_cx2072x_aif1_startup(struct snd_pcm_substream *substream) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci return snd_pcm_hw_constraint_single(substream->runtime, 1478c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 48000); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic struct snd_soc_ops byt_cht_cx2072x_aif1_ops = { 1518c2ecf20Sopenharmony_ci .startup = byt_cht_cx2072x_aif1_startup, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(dummy, 1558c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_DUMMY())); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(media, 1588c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai"))); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(deepbuffer, 1618c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai"))); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(ssp2, 1648c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port"))); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(cx2072x, 1678c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("i2c-14F10720:00", "cx2072x-hifi"))); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(platform, 1708c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform"))); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link byt_cht_cx2072x_dais[] = { 1738c2ecf20Sopenharmony_ci [MERR_DPCM_AUDIO] = { 1748c2ecf20Sopenharmony_ci .name = "Audio Port", 1758c2ecf20Sopenharmony_ci .stream_name = "Audio", 1768c2ecf20Sopenharmony_ci .nonatomic = true, 1778c2ecf20Sopenharmony_ci .dynamic = 1, 1788c2ecf20Sopenharmony_ci .dpcm_playback = 1, 1798c2ecf20Sopenharmony_ci .dpcm_capture = 1, 1808c2ecf20Sopenharmony_ci .ops = &byt_cht_cx2072x_aif1_ops, 1818c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(media, dummy, platform), 1828c2ecf20Sopenharmony_ci }, 1838c2ecf20Sopenharmony_ci [MERR_DPCM_DEEP_BUFFER] = { 1848c2ecf20Sopenharmony_ci .name = "Deep-Buffer Audio Port", 1858c2ecf20Sopenharmony_ci .stream_name = "Deep-Buffer Audio", 1868c2ecf20Sopenharmony_ci .nonatomic = true, 1878c2ecf20Sopenharmony_ci .dynamic = 1, 1888c2ecf20Sopenharmony_ci .dpcm_playback = 1, 1898c2ecf20Sopenharmony_ci .ops = &byt_cht_cx2072x_aif1_ops, 1908c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(deepbuffer, dummy, platform), 1918c2ecf20Sopenharmony_ci }, 1928c2ecf20Sopenharmony_ci /* back ends */ 1938c2ecf20Sopenharmony_ci { 1948c2ecf20Sopenharmony_ci .name = "SSP2-Codec", 1958c2ecf20Sopenharmony_ci .id = 0, 1968c2ecf20Sopenharmony_ci .no_pcm = 1, 1978c2ecf20Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 1988c2ecf20Sopenharmony_ci | SND_SOC_DAIFMT_CBS_CFS, 1998c2ecf20Sopenharmony_ci .init = byt_cht_cx2072x_init, 2008c2ecf20Sopenharmony_ci .be_hw_params_fixup = byt_cht_cx2072x_fixup, 2018c2ecf20Sopenharmony_ci .nonatomic = true, 2028c2ecf20Sopenharmony_ci .dpcm_playback = 1, 2038c2ecf20Sopenharmony_ci .dpcm_capture = 1, 2048c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(ssp2, cx2072x, platform), 2058c2ecf20Sopenharmony_ci }, 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) 2098c2ecf20Sopenharmony_ci/* use space before codec name to simplify card ID, and simplify driver name */ 2108c2ecf20Sopenharmony_ci#define CARD_NAME "bytcht cx2072x" /* card name will be 'sof-bytcht cx2072x' */ 2118c2ecf20Sopenharmony_ci#define DRIVER_NAME "SOF" 2128c2ecf20Sopenharmony_ci#else 2138c2ecf20Sopenharmony_ci#define CARD_NAME "bytcht-cx2072x" 2148c2ecf20Sopenharmony_ci#define DRIVER_NAME NULL /* card name will be used for driver name */ 2158c2ecf20Sopenharmony_ci#endif 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* SoC card */ 2188c2ecf20Sopenharmony_cistatic struct snd_soc_card byt_cht_cx2072x_card = { 2198c2ecf20Sopenharmony_ci .name = CARD_NAME, 2208c2ecf20Sopenharmony_ci .driver_name = DRIVER_NAME, 2218c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2228c2ecf20Sopenharmony_ci .dai_link = byt_cht_cx2072x_dais, 2238c2ecf20Sopenharmony_ci .num_links = ARRAY_SIZE(byt_cht_cx2072x_dais), 2248c2ecf20Sopenharmony_ci .dapm_widgets = byt_cht_cx2072x_widgets, 2258c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(byt_cht_cx2072x_widgets), 2268c2ecf20Sopenharmony_ci .dapm_routes = byt_cht_cx2072x_audio_map, 2278c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(byt_cht_cx2072x_audio_map), 2288c2ecf20Sopenharmony_ci .controls = byt_cht_cx2072x_controls, 2298c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(byt_cht_cx2072x_controls), 2308c2ecf20Sopenharmony_ci}; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic char codec_name[SND_ACPI_I2C_ID_LEN]; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int snd_byt_cht_cx2072x_probe(struct platform_device *pdev) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct snd_soc_acpi_mach *mach; 2378c2ecf20Sopenharmony_ci struct acpi_device *adev; 2388c2ecf20Sopenharmony_ci int dai_index = 0; 2398c2ecf20Sopenharmony_ci int i, ret; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci byt_cht_cx2072x_card.dev = &pdev->dev; 2428c2ecf20Sopenharmony_ci mach = dev_get_platdata(&pdev->dev); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* fix index of codec dai */ 2458c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(byt_cht_cx2072x_dais); i++) { 2468c2ecf20Sopenharmony_ci if (!strcmp(byt_cht_cx2072x_dais[i].codecs->name, 2478c2ecf20Sopenharmony_ci "i2c-14F10720:00")) { 2488c2ecf20Sopenharmony_ci dai_index = i; 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* fixup codec name based on HID */ 2548c2ecf20Sopenharmony_ci adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); 2558c2ecf20Sopenharmony_ci if (adev) { 2568c2ecf20Sopenharmony_ci snprintf(codec_name, sizeof(codec_name), "i2c-%s", 2578c2ecf20Sopenharmony_ci acpi_dev_name(adev)); 2588c2ecf20Sopenharmony_ci put_device(&adev->dev); 2598c2ecf20Sopenharmony_ci byt_cht_cx2072x_dais[dai_index].codecs->name = codec_name; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* override plaform name, if required */ 2638c2ecf20Sopenharmony_ci ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_cx2072x_card, 2648c2ecf20Sopenharmony_ci mach->mach_params.platform); 2658c2ecf20Sopenharmony_ci if (ret) 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return devm_snd_soc_register_card(&pdev->dev, &byt_cht_cx2072x_card); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic struct platform_driver snd_byt_cht_cx2072x_driver = { 2728c2ecf20Sopenharmony_ci .driver = { 2738c2ecf20Sopenharmony_ci .name = "bytcht_cx2072x", 2748c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) 2758c2ecf20Sopenharmony_ci .pm = &snd_soc_pm_ops, 2768c2ecf20Sopenharmony_ci#endif 2778c2ecf20Sopenharmony_ci }, 2788c2ecf20Sopenharmony_ci .probe = snd_byt_cht_cx2072x_probe, 2798c2ecf20Sopenharmony_ci}; 2808c2ecf20Sopenharmony_cimodule_platform_driver(snd_byt_cht_cx2072x_driver); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver"); 2838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2848c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:bytcht_cx2072x"); 285