18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci// Copyright(c) 2018-2020 Intel Corporation. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* 58c2ecf20Sopenharmony_ci * Intel SOF Machine Driver for Intel platforms with TI PCM512x codec, 68c2ecf20Sopenharmony_ci * e.g. Up or Up2 with Hifiberry DAC+ HAT 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/dmi.h> 108c2ecf20Sopenharmony_ci#include <linux/i2c.h> 118c2ecf20Sopenharmony_ci#include <linux/input.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <sound/core.h> 168c2ecf20Sopenharmony_ci#include <sound/jack.h> 178c2ecf20Sopenharmony_ci#include <sound/pcm.h> 188c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 198c2ecf20Sopenharmony_ci#include <sound/soc.h> 208c2ecf20Sopenharmony_ci#include <sound/soc-acpi.h> 218c2ecf20Sopenharmony_ci#include "../../codecs/pcm512x.h" 228c2ecf20Sopenharmony_ci#include "../common/soc-intel-quirks.h" 238c2ecf20Sopenharmony_ci#include "hda_dsp_common.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define NAME_SIZE 32 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define SOF_PCM512X_SSP_CODEC(quirk) ((quirk) & GENMASK(3, 0)) 288c2ecf20Sopenharmony_ci#define SOF_PCM512X_SSP_CODEC_MASK (GENMASK(3, 0)) 298c2ecf20Sopenharmony_ci#define SOF_PCM512X_ENABLE_SSP_CAPTURE BIT(4) 308c2ecf20Sopenharmony_ci#define SOF_PCM512X_ENABLE_DMIC BIT(5) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define IDISP_CODEC_MASK 0x4 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Default: SSP5 */ 358c2ecf20Sopenharmony_cistatic unsigned long sof_pcm512x_quirk = 368c2ecf20Sopenharmony_ci SOF_PCM512X_SSP_CODEC(5) | 378c2ecf20Sopenharmony_ci SOF_PCM512X_ENABLE_SSP_CAPTURE | 388c2ecf20Sopenharmony_ci SOF_PCM512X_ENABLE_DMIC; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic bool is_legacy_cpu; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct sof_hdmi_pcm { 438c2ecf20Sopenharmony_ci struct list_head head; 448c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai; 458c2ecf20Sopenharmony_ci int device; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct sof_card_private { 498c2ecf20Sopenharmony_ci struct list_head hdmi_pcm_list; 508c2ecf20Sopenharmony_ci bool idisp_codec; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int sof_pcm512x_quirk_cb(const struct dmi_system_id *id) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci sof_pcm512x_quirk = (unsigned long)id->driver_data; 568c2ecf20Sopenharmony_ci return 1; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic const struct dmi_system_id sof_pcm512x_quirk_table[] = { 608c2ecf20Sopenharmony_ci { 618c2ecf20Sopenharmony_ci .callback = sof_pcm512x_quirk_cb, 628c2ecf20Sopenharmony_ci .matches = { 638c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), 648c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "UP-CHT01"), 658c2ecf20Sopenharmony_ci }, 668c2ecf20Sopenharmony_ci .driver_data = (void *)(SOF_PCM512X_SSP_CODEC(2)), 678c2ecf20Sopenharmony_ci }, 688c2ecf20Sopenharmony_ci {} 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); 748c2ecf20Sopenharmony_ci struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); 758c2ecf20Sopenharmony_ci struct sof_hdmi_pcm *pcm; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); 788c2ecf20Sopenharmony_ci if (!pcm) 798c2ecf20Sopenharmony_ci return -ENOMEM; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* dai_link id is 1:1 mapped to the PCM device */ 828c2ecf20Sopenharmony_ci pcm->device = rtd->dai_link->id; 838c2ecf20Sopenharmony_ci pcm->codec_dai = dai; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int sof_pcm512x_codec_init(struct snd_soc_pcm_runtime *rtd) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci snd_soc_component_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); 958c2ecf20Sopenharmony_ci snd_soc_component_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); 968c2ecf20Sopenharmony_ci snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, 978c2ecf20Sopenharmony_ci 0x08, 0x08); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int aif1_startup(struct snd_pcm_substream *substream) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1058c2ecf20Sopenharmony_ci struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, 1088c2ecf20Sopenharmony_ci 0x08, 0x08); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void aif1_shutdown(struct snd_pcm_substream *substream) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1168c2ecf20Sopenharmony_ci struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, 1198c2ecf20Sopenharmony_ci 0x08, 0x00); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic const struct snd_soc_ops sof_pcm512x_ops = { 1238c2ecf20Sopenharmony_ci .startup = aif1_startup, 1248c2ecf20Sopenharmony_ci .shutdown = aif1_shutdown, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link_component platform_component[] = { 1288c2ecf20Sopenharmony_ci { 1298c2ecf20Sopenharmony_ci /* name might be overridden during probe */ 1308c2ecf20Sopenharmony_ci .name = "0000:00:1f.3" 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int sof_card_late_probe(struct snd_soc_card *card) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); 1378c2ecf20Sopenharmony_ci struct sof_hdmi_pcm *pcm; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* HDMI is not supported by SOF on Baytrail/CherryTrail */ 1408c2ecf20Sopenharmony_ci if (is_legacy_cpu) 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (list_empty(&ctx->hdmi_pcm_list)) 1448c2ecf20Sopenharmony_ci return -EINVAL; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (!ctx->idisp_codec) 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new sof_controls[] = { 1558c2ecf20Sopenharmony_ci SOC_DAPM_PIN_SWITCH("Ext Spk"), 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget sof_widgets[] = { 1598c2ecf20Sopenharmony_ci SND_SOC_DAPM_SPK("Ext Spk", NULL), 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget dmic_widgets[] = { 1638c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("SoC DMIC", NULL), 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route sof_map[] = { 1678c2ecf20Sopenharmony_ci /* Speaker */ 1688c2ecf20Sopenharmony_ci {"Ext Spk", NULL, "OUTR"}, 1698c2ecf20Sopenharmony_ci {"Ext Spk", NULL, "OUTL"}, 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route dmic_map[] = { 1738c2ecf20Sopenharmony_ci /* digital mics */ 1748c2ecf20Sopenharmony_ci {"DMic", NULL, "SoC DMIC"}, 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int dmic_init(struct snd_soc_pcm_runtime *rtd) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct snd_soc_card *card = rtd->card; 1808c2ecf20Sopenharmony_ci int ret; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets, 1838c2ecf20Sopenharmony_ci ARRAY_SIZE(dmic_widgets)); 1848c2ecf20Sopenharmony_ci if (ret) { 1858c2ecf20Sopenharmony_ci dev_err(card->dev, "DMic widget addition failed: %d\n", ret); 1868c2ecf20Sopenharmony_ci /* Don't need to add routes if widget addition failed */ 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ret = snd_soc_dapm_add_routes(&card->dapm, dmic_map, 1918c2ecf20Sopenharmony_ci ARRAY_SIZE(dmic_map)); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (ret) 1948c2ecf20Sopenharmony_ci dev_err(card->dev, "DMic map addition failed: %d\n", ret); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return ret; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* sof audio machine driver for pcm512x codec */ 2008c2ecf20Sopenharmony_cistatic struct snd_soc_card sof_audio_card_pcm512x = { 2018c2ecf20Sopenharmony_ci .name = "pcm512x", 2028c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2038c2ecf20Sopenharmony_ci .controls = sof_controls, 2048c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(sof_controls), 2058c2ecf20Sopenharmony_ci .dapm_widgets = sof_widgets, 2068c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(sof_widgets), 2078c2ecf20Sopenharmony_ci .dapm_routes = sof_map, 2088c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(sof_map), 2098c2ecf20Sopenharmony_ci .fully_routed = true, 2108c2ecf20Sopenharmony_ci .late_probe = sof_card_late_probe, 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(pcm512x_component, 2148c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("i2c-104C5122:00", "pcm512x-hifi"))); 2158c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEF(dmic_component, 2168c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi"))); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, 2198c2ecf20Sopenharmony_ci int ssp_codec, 2208c2ecf20Sopenharmony_ci int dmic_be_num, 2218c2ecf20Sopenharmony_ci int hdmi_num, 2228c2ecf20Sopenharmony_ci bool idisp_codec) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *idisp_components; 2258c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *cpus; 2268c2ecf20Sopenharmony_ci struct snd_soc_dai_link *links; 2278c2ecf20Sopenharmony_ci int i, id = 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci links = devm_kcalloc(dev, sof_audio_card_pcm512x.num_links, 2308c2ecf20Sopenharmony_ci sizeof(struct snd_soc_dai_link), GFP_KERNEL); 2318c2ecf20Sopenharmony_ci cpus = devm_kcalloc(dev, sof_audio_card_pcm512x.num_links, 2328c2ecf20Sopenharmony_ci sizeof(struct snd_soc_dai_link_component), GFP_KERNEL); 2338c2ecf20Sopenharmony_ci if (!links || !cpus) 2348c2ecf20Sopenharmony_ci goto devm_err; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* codec SSP */ 2378c2ecf20Sopenharmony_ci links[id].name = devm_kasprintf(dev, GFP_KERNEL, 2388c2ecf20Sopenharmony_ci "SSP%d-Codec", ssp_codec); 2398c2ecf20Sopenharmony_ci if (!links[id].name) 2408c2ecf20Sopenharmony_ci goto devm_err; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci links[id].id = id; 2438c2ecf20Sopenharmony_ci links[id].codecs = pcm512x_component; 2448c2ecf20Sopenharmony_ci links[id].num_codecs = ARRAY_SIZE(pcm512x_component); 2458c2ecf20Sopenharmony_ci links[id].platforms = platform_component; 2468c2ecf20Sopenharmony_ci links[id].num_platforms = ARRAY_SIZE(platform_component); 2478c2ecf20Sopenharmony_ci links[id].init = sof_pcm512x_codec_init; 2488c2ecf20Sopenharmony_ci links[id].ops = &sof_pcm512x_ops; 2498c2ecf20Sopenharmony_ci links[id].nonatomic = true; 2508c2ecf20Sopenharmony_ci links[id].dpcm_playback = 1; 2518c2ecf20Sopenharmony_ci /* 2528c2ecf20Sopenharmony_ci * capture only supported with specific versions of the Hifiberry DAC+ 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci if (sof_pcm512x_quirk & SOF_PCM512X_ENABLE_SSP_CAPTURE) 2558c2ecf20Sopenharmony_ci links[id].dpcm_capture = 1; 2568c2ecf20Sopenharmony_ci links[id].no_pcm = 1; 2578c2ecf20Sopenharmony_ci links[id].cpus = &cpus[id]; 2588c2ecf20Sopenharmony_ci links[id].num_cpus = 1; 2598c2ecf20Sopenharmony_ci if (is_legacy_cpu) { 2608c2ecf20Sopenharmony_ci links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, 2618c2ecf20Sopenharmony_ci "ssp%d-port", 2628c2ecf20Sopenharmony_ci ssp_codec); 2638c2ecf20Sopenharmony_ci if (!links[id].cpus->dai_name) 2648c2ecf20Sopenharmony_ci goto devm_err; 2658c2ecf20Sopenharmony_ci } else { 2668c2ecf20Sopenharmony_ci links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, 2678c2ecf20Sopenharmony_ci "SSP%d Pin", 2688c2ecf20Sopenharmony_ci ssp_codec); 2698c2ecf20Sopenharmony_ci if (!links[id].cpus->dai_name) 2708c2ecf20Sopenharmony_ci goto devm_err; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci id++; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* dmic */ 2758c2ecf20Sopenharmony_ci if (dmic_be_num > 0) { 2768c2ecf20Sopenharmony_ci /* at least we have dmic01 */ 2778c2ecf20Sopenharmony_ci links[id].name = "dmic01"; 2788c2ecf20Sopenharmony_ci links[id].cpus = &cpus[id]; 2798c2ecf20Sopenharmony_ci links[id].cpus->dai_name = "DMIC01 Pin"; 2808c2ecf20Sopenharmony_ci links[id].init = dmic_init; 2818c2ecf20Sopenharmony_ci if (dmic_be_num > 1) { 2828c2ecf20Sopenharmony_ci /* set up 2 BE links at most */ 2838c2ecf20Sopenharmony_ci links[id + 1].name = "dmic16k"; 2848c2ecf20Sopenharmony_ci links[id + 1].cpus = &cpus[id + 1]; 2858c2ecf20Sopenharmony_ci links[id + 1].cpus->dai_name = "DMIC16k Pin"; 2868c2ecf20Sopenharmony_ci dmic_be_num = 2; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci for (i = 0; i < dmic_be_num; i++) { 2918c2ecf20Sopenharmony_ci links[id].id = id; 2928c2ecf20Sopenharmony_ci links[id].num_cpus = 1; 2938c2ecf20Sopenharmony_ci links[id].codecs = dmic_component; 2948c2ecf20Sopenharmony_ci links[id].num_codecs = ARRAY_SIZE(dmic_component); 2958c2ecf20Sopenharmony_ci links[id].platforms = platform_component; 2968c2ecf20Sopenharmony_ci links[id].num_platforms = ARRAY_SIZE(platform_component); 2978c2ecf20Sopenharmony_ci links[id].ignore_suspend = 1; 2988c2ecf20Sopenharmony_ci links[id].dpcm_capture = 1; 2998c2ecf20Sopenharmony_ci links[id].no_pcm = 1; 3008c2ecf20Sopenharmony_ci id++; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* HDMI */ 3048c2ecf20Sopenharmony_ci if (hdmi_num > 0) { 3058c2ecf20Sopenharmony_ci idisp_components = devm_kcalloc(dev, hdmi_num, 3068c2ecf20Sopenharmony_ci sizeof(struct snd_soc_dai_link_component), 3078c2ecf20Sopenharmony_ci GFP_KERNEL); 3088c2ecf20Sopenharmony_ci if (!idisp_components) 3098c2ecf20Sopenharmony_ci goto devm_err; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci for (i = 1; i <= hdmi_num; i++) { 3128c2ecf20Sopenharmony_ci links[id].name = devm_kasprintf(dev, GFP_KERNEL, 3138c2ecf20Sopenharmony_ci "iDisp%d", i); 3148c2ecf20Sopenharmony_ci if (!links[id].name) 3158c2ecf20Sopenharmony_ci goto devm_err; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci links[id].id = id; 3188c2ecf20Sopenharmony_ci links[id].cpus = &cpus[id]; 3198c2ecf20Sopenharmony_ci links[id].num_cpus = 1; 3208c2ecf20Sopenharmony_ci links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, 3218c2ecf20Sopenharmony_ci "iDisp%d Pin", i); 3228c2ecf20Sopenharmony_ci if (!links[id].cpus->dai_name) 3238c2ecf20Sopenharmony_ci goto devm_err; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * topology cannot be loaded if codec is missing, so 3278c2ecf20Sopenharmony_ci * use the dummy codec if needed 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci if (idisp_codec) { 3308c2ecf20Sopenharmony_ci idisp_components[i - 1].name = "ehdaudio0D2"; 3318c2ecf20Sopenharmony_ci idisp_components[i - 1].dai_name = 3328c2ecf20Sopenharmony_ci devm_kasprintf(dev, GFP_KERNEL, 3338c2ecf20Sopenharmony_ci "intel-hdmi-hifi%d", i); 3348c2ecf20Sopenharmony_ci } else { 3358c2ecf20Sopenharmony_ci idisp_components[i - 1].name = "snd-soc-dummy"; 3368c2ecf20Sopenharmony_ci idisp_components[i - 1].dai_name = "snd-soc-dummy-dai"; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci if (!idisp_components[i - 1].dai_name) 3398c2ecf20Sopenharmony_ci goto devm_err; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci links[id].codecs = &idisp_components[i - 1]; 3428c2ecf20Sopenharmony_ci links[id].num_codecs = 1; 3438c2ecf20Sopenharmony_ci links[id].platforms = platform_component; 3448c2ecf20Sopenharmony_ci links[id].num_platforms = ARRAY_SIZE(platform_component); 3458c2ecf20Sopenharmony_ci links[id].init = sof_hdmi_init; 3468c2ecf20Sopenharmony_ci links[id].dpcm_playback = 1; 3478c2ecf20Sopenharmony_ci links[id].no_pcm = 1; 3488c2ecf20Sopenharmony_ci id++; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return links; 3528c2ecf20Sopenharmony_cidevm_err: 3538c2ecf20Sopenharmony_ci return NULL; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int sof_audio_probe(struct platform_device *pdev) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; 3598c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_links; 3608c2ecf20Sopenharmony_ci struct sof_card_private *ctx; 3618c2ecf20Sopenharmony_ci int dmic_be_num, hdmi_num; 3628c2ecf20Sopenharmony_ci int ret, ssp_codec; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 3658c2ecf20Sopenharmony_ci if (!ctx) 3668c2ecf20Sopenharmony_ci return -ENOMEM; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci hdmi_num = 0; 3698c2ecf20Sopenharmony_ci if (soc_intel_is_byt() || soc_intel_is_cht()) { 3708c2ecf20Sopenharmony_ci is_legacy_cpu = true; 3718c2ecf20Sopenharmony_ci dmic_be_num = 0; 3728c2ecf20Sopenharmony_ci /* default quirk for legacy cpu */ 3738c2ecf20Sopenharmony_ci sof_pcm512x_quirk = SOF_PCM512X_SSP_CODEC(2); 3748c2ecf20Sopenharmony_ci } else { 3758c2ecf20Sopenharmony_ci dmic_be_num = 2; 3768c2ecf20Sopenharmony_ci if (mach->mach_params.common_hdmi_codec_drv && 3778c2ecf20Sopenharmony_ci (mach->mach_params.codec_mask & IDISP_CODEC_MASK)) 3788c2ecf20Sopenharmony_ci ctx->idisp_codec = true; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* links are always present in topology */ 3818c2ecf20Sopenharmony_ci hdmi_num = 3; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci dmi_check_system(sof_pcm512x_quirk_table); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "sof_pcm512x_quirk = %lx\n", sof_pcm512x_quirk); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci ssp_codec = sof_pcm512x_quirk & SOF_PCM512X_SSP_CODEC_MASK; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (!(sof_pcm512x_quirk & SOF_PCM512X_ENABLE_DMIC)) 3918c2ecf20Sopenharmony_ci dmic_be_num = 0; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* compute number of dai links */ 3948c2ecf20Sopenharmony_ci sof_audio_card_pcm512x.num_links = 1 + dmic_be_num + hdmi_num; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, 3978c2ecf20Sopenharmony_ci dmic_be_num, hdmi_num, 3988c2ecf20Sopenharmony_ci ctx->idisp_codec); 3998c2ecf20Sopenharmony_ci if (!dai_links) 4008c2ecf20Sopenharmony_ci return -ENOMEM; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci sof_audio_card_pcm512x.dai_link = dai_links; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ctx->hdmi_pcm_list); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci sof_audio_card_pcm512x.dev = &pdev->dev; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* set platform name for each dailink */ 4098c2ecf20Sopenharmony_ci ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_pcm512x, 4108c2ecf20Sopenharmony_ci mach->mach_params.platform); 4118c2ecf20Sopenharmony_ci if (ret) 4128c2ecf20Sopenharmony_ci return ret; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci snd_soc_card_set_drvdata(&sof_audio_card_pcm512x, ctx); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return devm_snd_soc_register_card(&pdev->dev, 4178c2ecf20Sopenharmony_ci &sof_audio_card_pcm512x); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int sof_pcm512x_remove(struct platform_device *pdev) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct snd_soc_card *card = platform_get_drvdata(pdev); 4238c2ecf20Sopenharmony_ci struct snd_soc_component *component = NULL; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci for_each_card_components(card, component) { 4268c2ecf20Sopenharmony_ci if (!strcmp(component->name, pcm512x_component[0].name)) { 4278c2ecf20Sopenharmony_ci snd_soc_component_set_jack(component, NULL, NULL); 4288c2ecf20Sopenharmony_ci break; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return 0; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic struct platform_driver sof_audio = { 4368c2ecf20Sopenharmony_ci .probe = sof_audio_probe, 4378c2ecf20Sopenharmony_ci .remove = sof_pcm512x_remove, 4388c2ecf20Sopenharmony_ci .driver = { 4398c2ecf20Sopenharmony_ci .name = "sof_pcm512x", 4408c2ecf20Sopenharmony_ci .pm = &snd_soc_pm_ops, 4418c2ecf20Sopenharmony_ci }, 4428c2ecf20Sopenharmony_ci}; 4438c2ecf20Sopenharmony_cimodule_platform_driver(sof_audio) 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ASoC Intel(R) SOF + PCM512x Machine driver"); 4468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pierre-Louis Bossart"); 4478c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 4488c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:sof_pcm512x"); 449