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 15static 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 20static const struct snd_kcontrol_new mt7986_wm8960_controls[] = { 21 SOC_DAPM_PIN_SWITCH("Headphone"), 22 SOC_DAPM_PIN_SWITCH("AMIC"), 23}; 24 25SND_SOC_DAILINK_DEFS(playback, 26 DAILINK_COMP_ARRAY(COMP_CPU("DL1")), 27 DAILINK_COMP_ARRAY(COMP_DUMMY()), 28 DAILINK_COMP_ARRAY(COMP_EMPTY())); 29 30SND_SOC_DAILINK_DEFS(capture, 31 DAILINK_COMP_ARRAY(COMP_CPU("UL1")), 32 DAILINK_COMP_ARRAY(COMP_DUMMY()), 33 DAILINK_COMP_ARRAY(COMP_EMPTY())); 34 35SND_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 40static 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 74static 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 85static 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_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__); 148 goto err_of_node_put; 149 } 150 151err_of_node_put: 152 of_node_put(platform_dai_node); 153 of_node_put(codec_dai_node); 154 return ret; 155} 156 157static const struct of_device_id mt7986_wm8960_machine_dt_match[] = { 158 {.compatible = "mediatek,mt7986-wm8960-sound"}, 159 { /* sentinel */ } 160}; 161MODULE_DEVICE_TABLE(of, mt7986_wm8960_machine_dt_match); 162 163static 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 171module_platform_driver(mt7986_wm8960_machine); 172 173/* Module information */ 174MODULE_DESCRIPTION("MT7986 WM8960 ALSA SoC machine driver"); 175MODULE_AUTHOR("Vic Wu <vic.wu@mediatek.com>"); 176MODULE_LICENSE("GPL"); 177MODULE_ALIAS("mt7986 wm8960 soc card"); 178