18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * bytcht-da7213.c - ASoc Machine driver for Intel Baytrail and 48c2ecf20Sopenharmony_ci * Cherrytrail-based platforms, with Dialog DA7213 codec 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2017 Intel Corporation 78c2ecf20Sopenharmony_ci * Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/acpi.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <sound/pcm.h> 198c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 208c2ecf20Sopenharmony_ci#include <sound/soc.h> 218c2ecf20Sopenharmony_ci#include <sound/soc-acpi.h> 228c2ecf20Sopenharmony_ci#include "../../codecs/da7213.h" 238c2ecf20Sopenharmony_ci#include "../atom/sst-atom-controls.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new controls[] = { 268c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headphone Jack"), 278c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Headset Mic"), 288c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Mic"), 298c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Aux In"), 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget dapm_widgets[] = { 338c2ecf20Sopenharmony_ci SND_SOC_DAPM_HP("Headphone Jack", NULL), 348c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Headset Mic", NULL), 358c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Mic", NULL), 368c2ecf20Sopenharmony_ci SND_SOC_DAPM_LINE("Aux In", NULL), 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route audio_map[] = { 408c2ecf20Sopenharmony_ci {"Headphone Jack", NULL, "HPL"}, 418c2ecf20Sopenharmony_ci {"Headphone Jack", NULL, "HPR"}, 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci {"AUXL", NULL, "Aux In"}, 448c2ecf20Sopenharmony_ci {"AUXR", NULL, "Aux In"}, 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* Assume Mic1 is linked to Headset and Mic2 to on-board mic */ 478c2ecf20Sopenharmony_ci {"MIC1", NULL, "Headset Mic"}, 488c2ecf20Sopenharmony_ci {"MIC2", NULL, "Mic"}, 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* SOC-codec link */ 518c2ecf20Sopenharmony_ci {"ssp2 Tx", NULL, "codec_out0"}, 528c2ecf20Sopenharmony_ci {"ssp2 Tx", NULL, "codec_out1"}, 538c2ecf20Sopenharmony_ci {"codec_in0", NULL, "ssp2 Rx"}, 548c2ecf20Sopenharmony_ci {"codec_in1", NULL, "ssp2 Rx"}, 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci {"Playback", NULL, "ssp2 Tx"}, 578c2ecf20Sopenharmony_ci {"ssp2 Rx", NULL, "Capture"}, 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int codec_fixup(struct snd_soc_pcm_runtime *rtd, 618c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci int ret; 648c2ecf20Sopenharmony_ci struct snd_interval *rate = hw_param_interval(params, 658c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE); 668c2ecf20Sopenharmony_ci struct snd_interval *channels = hw_param_interval(params, 678c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* The DSP will convert the FE rate to 48k, stereo, 24bits */ 708c2ecf20Sopenharmony_ci rate->min = rate->max = 48000; 718c2ecf20Sopenharmony_ci channels->min = channels->max = 2; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* set SSP2 to 24-bit */ 748c2ecf20Sopenharmony_ci params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * Default mode for SSP configuration is TDM 4 slot, override config 788c2ecf20Sopenharmony_ci * with explicit setting to I2S 2ch 24-bit. The word length is set with 798c2ecf20Sopenharmony_ci * dai_set_tdm_slot() since there is no other API exposed 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), 828c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_I2S | 838c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_NB_NF | 848c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS); 858c2ecf20Sopenharmony_ci if (ret < 0) { 868c2ecf20Sopenharmony_ci dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24); 918c2ecf20Sopenharmony_ci if (ret < 0) { 928c2ecf20Sopenharmony_ci dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); 938c2ecf20Sopenharmony_ci return ret; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int aif1_startup(struct snd_pcm_substream *substream) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci return snd_pcm_hw_constraint_single(substream->runtime, 1028c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 48000); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int aif1_hw_params(struct snd_pcm_substream *substream, 1068c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1098c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 1108c2ecf20Sopenharmony_ci int ret; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_sysclk(codec_dai, DA7213_CLKSRC_MCLK, 1138c2ecf20Sopenharmony_ci 19200000, SND_SOC_CLOCK_IN); 1148c2ecf20Sopenharmony_ci if (ret < 0) 1158c2ecf20Sopenharmony_ci dev_err(codec_dai->dev, "can't set codec sysclk configuration\n"); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_pll(codec_dai, 0, 1188c2ecf20Sopenharmony_ci DA7213_SYSCLK_PLL_SRM, 0, DA7213_PLL_FREQ_OUT_98304000); 1198c2ecf20Sopenharmony_ci if (ret < 0) { 1208c2ecf20Sopenharmony_ci dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret); 1218c2ecf20Sopenharmony_ci return -EIO; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int aif1_hw_free(struct snd_pcm_substream *substream) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1308c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 1318c2ecf20Sopenharmony_ci int ret; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_pll(codec_dai, 0, 1348c2ecf20Sopenharmony_ci DA7213_SYSCLK_MCLK, 0, 0); 1358c2ecf20Sopenharmony_ci if (ret < 0) { 1368c2ecf20Sopenharmony_ci dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret); 1378c2ecf20Sopenharmony_ci return -EIO; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const struct snd_soc_ops aif1_ops = { 1448c2ecf20Sopenharmony_ci .startup = aif1_startup, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic const struct snd_soc_ops ssp2_ops = { 1488c2ecf20Sopenharmony_ci .hw_params = aif1_hw_params, 1498c2ecf20Sopenharmony_ci .hw_free = aif1_hw_free, 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(dummy, 1548c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_DUMMY())); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(media, 1578c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai"))); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(deepbuffer, 1608c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai"))); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(ssp2_port, 1638c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port"))); 1648c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(ssp2_codec, 1658c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7213:00", 1668c2ecf20Sopenharmony_ci "da7213-hifi"))); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(platform, 1698c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform"))); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link dailink[] = { 1728c2ecf20Sopenharmony_ci [MERR_DPCM_AUDIO] = { 1738c2ecf20Sopenharmony_ci .name = "Audio Port", 1748c2ecf20Sopenharmony_ci .stream_name = "Audio", 1758c2ecf20Sopenharmony_ci .nonatomic = true, 1768c2ecf20Sopenharmony_ci .dynamic = 1, 1778c2ecf20Sopenharmony_ci .dpcm_playback = 1, 1788c2ecf20Sopenharmony_ci .dpcm_capture = 1, 1798c2ecf20Sopenharmony_ci .ops = &aif1_ops, 1808c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(media, dummy, platform), 1818c2ecf20Sopenharmony_ci }, 1828c2ecf20Sopenharmony_ci [MERR_DPCM_DEEP_BUFFER] = { 1838c2ecf20Sopenharmony_ci .name = "Deep-Buffer Audio Port", 1848c2ecf20Sopenharmony_ci .stream_name = "Deep-Buffer Audio", 1858c2ecf20Sopenharmony_ci .nonatomic = true, 1868c2ecf20Sopenharmony_ci .dynamic = 1, 1878c2ecf20Sopenharmony_ci .dpcm_playback = 1, 1888c2ecf20Sopenharmony_ci .ops = &aif1_ops, 1898c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(deepbuffer, dummy, platform), 1908c2ecf20Sopenharmony_ci }, 1918c2ecf20Sopenharmony_ci /* CODEC<->CODEC link */ 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 .be_hw_params_fixup = codec_fixup, 2008c2ecf20Sopenharmony_ci .nonatomic = true, 2018c2ecf20Sopenharmony_ci .dpcm_playback = 1, 2028c2ecf20Sopenharmony_ci .dpcm_capture = 1, 2038c2ecf20Sopenharmony_ci .ops = &ssp2_ops, 2048c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, 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 da7213" /* card name will be 'sof-bytcht da7213' */ 2118c2ecf20Sopenharmony_ci#define DRIVER_NAME "SOF" 2128c2ecf20Sopenharmony_ci#else 2138c2ecf20Sopenharmony_ci#define CARD_NAME "bytcht-da7213" 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 bytcht_da7213_card = { 2198c2ecf20Sopenharmony_ci .name = CARD_NAME, 2208c2ecf20Sopenharmony_ci .driver_name = DRIVER_NAME, 2218c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2228c2ecf20Sopenharmony_ci .dai_link = dailink, 2238c2ecf20Sopenharmony_ci .num_links = ARRAY_SIZE(dailink), 2248c2ecf20Sopenharmony_ci .controls = controls, 2258c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(controls), 2268c2ecf20Sopenharmony_ci .dapm_widgets = dapm_widgets, 2278c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), 2288c2ecf20Sopenharmony_ci .dapm_routes = audio_map, 2298c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(audio_map), 2308c2ecf20Sopenharmony_ci}; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic char codec_name[SND_ACPI_I2C_ID_LEN]; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int bytcht_da7213_probe(struct platform_device *pdev) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct snd_soc_card *card; 2378c2ecf20Sopenharmony_ci struct snd_soc_acpi_mach *mach; 2388c2ecf20Sopenharmony_ci const char *platform_name; 2398c2ecf20Sopenharmony_ci struct acpi_device *adev; 2408c2ecf20Sopenharmony_ci int dai_index = 0; 2418c2ecf20Sopenharmony_ci int ret_val = 0; 2428c2ecf20Sopenharmony_ci int i; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci mach = pdev->dev.platform_data; 2458c2ecf20Sopenharmony_ci card = &bytcht_da7213_card; 2468c2ecf20Sopenharmony_ci card->dev = &pdev->dev; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* fix index of codec dai */ 2498c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dailink); i++) { 2508c2ecf20Sopenharmony_ci if (!strcmp(dailink[i].codecs->name, "i2c-DLGS7213:00")) { 2518c2ecf20Sopenharmony_ci dai_index = i; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* fixup codec name based on HID */ 2578c2ecf20Sopenharmony_ci adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); 2588c2ecf20Sopenharmony_ci if (adev) { 2598c2ecf20Sopenharmony_ci snprintf(codec_name, sizeof(codec_name), 2608c2ecf20Sopenharmony_ci "i2c-%s", acpi_dev_name(adev)); 2618c2ecf20Sopenharmony_ci put_device(&adev->dev); 2628c2ecf20Sopenharmony_ci dailink[dai_index].codecs->name = codec_name; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* override plaform name, if required */ 2668c2ecf20Sopenharmony_ci platform_name = mach->mach_params.platform; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci ret_val = snd_soc_fixup_dai_links_platform_name(card, platform_name); 2698c2ecf20Sopenharmony_ci if (ret_val) 2708c2ecf20Sopenharmony_ci return ret_val; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ret_val = devm_snd_soc_register_card(&pdev->dev, card); 2738c2ecf20Sopenharmony_ci if (ret_val) { 2748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2758c2ecf20Sopenharmony_ci "snd_soc_register_card failed %d\n", ret_val); 2768c2ecf20Sopenharmony_ci return ret_val; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, card); 2798c2ecf20Sopenharmony_ci return ret_val; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic struct platform_driver bytcht_da7213_driver = { 2838c2ecf20Sopenharmony_ci .driver = { 2848c2ecf20Sopenharmony_ci .name = "bytcht_da7213", 2858c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) 2868c2ecf20Sopenharmony_ci .pm = &snd_soc_pm_ops, 2878c2ecf20Sopenharmony_ci#endif 2888c2ecf20Sopenharmony_ci }, 2898c2ecf20Sopenharmony_ci .probe = bytcht_da7213_probe, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_cimodule_platform_driver(bytcht_da7213_driver); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail+DA7213 Machine driver"); 2948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pierre-Louis Bossart"); 2958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2968c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:bytcht_da7213"); 297