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 .dpcm_playback = 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 .dpcm_capture = 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 .dpcm_playback = 1, 69 .dpcm_capture = 1, 70 SND_SOC_DAILINK_REG(codec), 71 }, 72 }; 73 74 static struct snd_soc_card mt7986_wm8960_card = { 75 .name = "mt7986-wm8960", 76 .owner = THIS_MODULE, 77 .dai_link = mt7986_wm8960_dai_links, 78 .num_links = ARRAY_SIZE(mt7986_wm8960_dai_links), 79 .controls = mt7986_wm8960_controls, 80 .num_controls = ARRAY_SIZE(mt7986_wm8960_controls), 81 .dapm_widgets = mt7986_wm8960_widgets, 82 .num_dapm_widgets = ARRAY_SIZE(mt7986_wm8960_widgets), 83 }; 84 85 static int mt7986_wm8960_machine_probe(struct platform_device *pdev) 86 { 87 struct snd_soc_card *card = &mt7986_wm8960_card; 88 struct snd_soc_dai_link *dai_link; 89 struct device_node *platform, *codec; 90 struct device_node *platform_dai_node, *codec_dai_node; 91 int ret, i; 92 93 card->dev = &pdev->dev; 94 95 platform = of_get_child_by_name(pdev->dev.of_node, "platform"); 96 97 if (platform) { 98 platform_dai_node = of_parse_phandle(platform, "sound-dai", 0); 99 of_node_put(platform); 100 101 if (!platform_dai_node) { 102 dev_err(&pdev->dev, "Failed to parse platform/sound-dai property\n"); 103 return -EINVAL; 104 } 105 } else { 106 dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); 107 return -EINVAL; 108 } 109 110 for_each_card_prelinks(card, i, dai_link) { 111 if (dai_link->platforms->name) 112 continue; 113 dai_link->platforms->of_node = platform_dai_node; 114 } 115 116 codec = of_get_child_by_name(pdev->dev.of_node, "codec"); 117 118 if (codec) { 119 codec_dai_node = of_parse_phandle(codec, "sound-dai", 0); 120 of_node_put(codec); 121 122 if (!codec_dai_node) { 123 of_node_put(platform_dai_node); 124 dev_err(&pdev->dev, "Failed to parse codec/sound-dai property\n"); 125 return -EINVAL; 126 } 127 } else { 128 of_node_put(platform_dai_node); 129 dev_err(&pdev->dev, "Property 'codec' missing or invalid\n"); 130 return -EINVAL; 131 } 132 133 for_each_card_prelinks(card, i, dai_link) { 134 if (dai_link->codecs->name) 135 continue; 136 dai_link->codecs->of_node = codec_dai_node; 137 } 138 139 ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); 140 if (ret) { 141 dev_err(&pdev->dev, "Failed to parse audio-routing: %d\n", ret); 142 goto err_of_node_put; 143 } 144 145 ret = devm_snd_soc_register_card(&pdev->dev, card); 146 if (ret) { 147 dev_err(&pdev->dev, "%s snd_soc_register_card fail: %d\n", __func__, ret); 148 goto err_of_node_put; 149 } 150 151 err_of_node_put: 152 of_node_put(platform_dai_node); 153 of_node_put(codec_dai_node); 154 return ret; 155 } 156 157 static const struct of_device_id mt7986_wm8960_machine_dt_match[] = { 158 {.compatible = "mediatek,mt7986-wm8960-sound"}, 159 { /* sentinel */ } 160 }; 161 MODULE_DEVICE_TABLE(of, mt7986_wm8960_machine_dt_match); 162 163 static struct platform_driver mt7986_wm8960_machine = { 164 .driver = { 165 .name = "mt7986-wm8960", 166 .of_match_table = mt7986_wm8960_machine_dt_match, 167 }, 168 .probe = mt7986_wm8960_machine_probe, 169 }; 170 171 module_platform_driver(mt7986_wm8960_machine); 172 173 /* Module information */ 174 MODULE_DESCRIPTION("MT7986 WM8960 ALSA SoC machine driver"); 175 MODULE_AUTHOR("Vic Wu <vic.wu@mediatek.com>"); 176 MODULE_LICENSE("GPL"); 177 MODULE_ALIAS("mt7986 wm8960 soc card"); 178