1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * mt7986-wm8960.c -- MT7986-WM8960 ALSA SoC machine driver 4 * 5 * Copyright (c) 2023 MediaTek Inc. 6 * Authors: Vic Wu <vic.wu@mediatek.com> 7 * Maso Huang <maso.huang@mediatek.com> 8 */ 9 10 #include <linux/module.h> 11 #include <sound/soc.h> 12 13 #include "mt7986-afe-common.h" 14 15 static const struct snd_soc_dapm_widget mt7986_wm8960_widgets[] = { 16 SND_SOC_DAPM_HP("Headphone", NULL), 17 SND_SOC_DAPM_MIC("AMIC", NULL), 18 }; 19 20 static const struct snd_kcontrol_new mt7986_wm8960_controls[] = { 21 SOC_DAPM_PIN_SWITCH("Headphone"), 22 SOC_DAPM_PIN_SWITCH("AMIC"), 23 }; 24 25 SND_SOC_DAILINK_DEFS(playback, 26 DAILINK_COMP_ARRAY(COMP_CPU("DL1")), 27 DAILINK_COMP_ARRAY(COMP_DUMMY()), 28 DAILINK_COMP_ARRAY(COMP_EMPTY())); 29 30 SND_SOC_DAILINK_DEFS(capture, 31 DAILINK_COMP_ARRAY(COMP_CPU("UL1")), 32 DAILINK_COMP_ARRAY(COMP_DUMMY()), 33 DAILINK_COMP_ARRAY(COMP_EMPTY())); 34 35 SND_SOC_DAILINK_DEFS(codec, 36 DAILINK_COMP_ARRAY(COMP_CPU("ETDM")), 37 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")), 38 DAILINK_COMP_ARRAY(COMP_EMPTY())); 39 40 static struct snd_soc_dai_link mt7986_wm8960_dai_links[] = { 41 /* FE */ 42 { 43 .name = "wm8960-playback", 44 .stream_name = "wm8960-playback", 45 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 46 SND_SOC_DPCM_TRIGGER_POST}, 47 .dynamic = 1, 48 .playback_only = 1, 49 SND_SOC_DAILINK_REG(playback), 50 }, 51 { 52 .name = "wm8960-capture", 53 .stream_name = "wm8960-capture", 54 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 55 SND_SOC_DPCM_TRIGGER_POST}, 56 .dynamic = 1, 57 .capture_only = 1, 58 SND_SOC_DAILINK_REG(capture), 59 }, 60 /* BE */ 61 { 62 .name = "wm8960-codec", 63 .no_pcm = 1, 64 .dai_fmt = SND_SOC_DAIFMT_I2S | 65 SND_SOC_DAIFMT_NB_NF | 66 SND_SOC_DAIFMT_CBS_CFS | 67 SND_SOC_DAIFMT_GATED, 68 SND_SOC_DAILINK_REG(codec), 69 }, 70 }; 71 72 static struct snd_soc_card mt7986_wm8960_card = { 73 .name = "mt7986-wm8960", 74 .owner = THIS_MODULE, 75 .dai_link = mt7986_wm8960_dai_links, 76 .num_links = ARRAY_SIZE(mt7986_wm8960_dai_links), 77 .controls = mt7986_wm8960_controls, 78 .num_controls = ARRAY_SIZE(mt7986_wm8960_controls), 79 .dapm_widgets = mt7986_wm8960_widgets, 80 .num_dapm_widgets = ARRAY_SIZE(mt7986_wm8960_widgets), 81 }; 82 83 static int mt7986_wm8960_machine_probe(struct platform_device *pdev) 84 { 85 struct snd_soc_card *card = &mt7986_wm8960_card; 86 struct snd_soc_dai_link *dai_link; 87 struct device_node *platform, *codec; 88 struct device_node *platform_dai_node, *codec_dai_node; 89 int ret, i; 90 91 card->dev = &pdev->dev; 92 93 platform = of_get_child_by_name(pdev->dev.of_node, "platform"); 94 95 if (platform) { 96 platform_dai_node = of_parse_phandle(platform, "sound-dai", 0); 97 of_node_put(platform); 98 99 if (!platform_dai_node) { 100 dev_err(&pdev->dev, "Failed to parse platform/sound-dai property\n"); 101 return -EINVAL; 102 } 103 } else { 104 dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); 105 return -EINVAL; 106 } 107 108 for_each_card_prelinks(card, i, dai_link) { 109 if (dai_link->platforms->name) 110 continue; 111 dai_link->platforms->of_node = platform_dai_node; 112 } 113 114 codec = of_get_child_by_name(pdev->dev.of_node, "codec"); 115 116 if (codec) { 117 codec_dai_node = of_parse_phandle(codec, "sound-dai", 0); 118 of_node_put(codec); 119 120 if (!codec_dai_node) { 121 of_node_put(platform_dai_node); 122 dev_err(&pdev->dev, "Failed to parse codec/sound-dai property\n"); 123 return -EINVAL; 124 } 125 } else { 126 of_node_put(platform_dai_node); 127 dev_err(&pdev->dev, "Property 'codec' missing or invalid\n"); 128 return -EINVAL; 129 } 130 131 for_each_card_prelinks(card, i, dai_link) { 132 if (dai_link->codecs->name) 133 continue; 134 dai_link->codecs->of_node = codec_dai_node; 135 } 136 137 ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); 138 if (ret) { 139 dev_err(&pdev->dev, "Failed to parse audio-routing: %d\n", ret); 140 goto err_of_node_put; 141 } 142 143 ret = devm_snd_soc_register_card(&pdev->dev, card); 144 if (ret) { 145 dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__); 146 goto err_of_node_put; 147 } 148 149 err_of_node_put: 150 of_node_put(platform_dai_node); 151 of_node_put(codec_dai_node); 152 return ret; 153 } 154 155 static const struct of_device_id mt7986_wm8960_machine_dt_match[] = { 156 {.compatible = "mediatek,mt7986-wm8960-sound"}, 157 { /* sentinel */ } 158 }; 159 MODULE_DEVICE_TABLE(of, mt7986_wm8960_machine_dt_match); 160 161 static struct platform_driver mt7986_wm8960_machine = { 162 .driver = { 163 .name = "mt7986-wm8960", 164 .of_match_table = mt7986_wm8960_machine_dt_match, 165 }, 166 .probe = mt7986_wm8960_machine_probe, 167 }; 168 169 module_platform_driver(mt7986_wm8960_machine); 170 171 /* Module information */ 172 MODULE_DESCRIPTION("MT7986 WM8960 ALSA SoC machine driver"); 173 MODULE_AUTHOR("Vic Wu <vic.wu@mediatek.com>"); 174 MODULE_LICENSE("GPL"); 175 MODULE_ALIAS("mt7986 wm8960 soc card"); 176