1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * mtk-soundcard-driver.c -- MediaTek soundcard driver common 4 * 5 * Copyright (c) 2022 MediaTek Inc. 6 * Author: Trevor Wu <trevor.wu@mediatek.com> 7 */ 8 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <sound/soc.h> 12 13 #include "mtk-soundcard-driver.h" 14 15 static int set_card_codec_info(struct snd_soc_card *card, 16 struct device_node *sub_node, 17 struct snd_soc_dai_link *dai_link) 18 { 19 struct device *dev = card->dev; 20 struct device_node *codec_node; 21 int ret; 22 23 codec_node = of_get_child_by_name(sub_node, "codec"); 24 if (!codec_node) { 25 dev_dbg(dev, "%s no specified codec: setting dummy.\n", dai_link->name); 26 27 dai_link->codecs = &snd_soc_dummy_dlc; 28 dai_link->num_codecs = 1; 29 dai_link->dynamic = 1; 30 return 0; 31 } 32 33 /* set card codec info */ 34 ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link); 35 36 of_node_put(codec_node); 37 38 if (ret < 0) 39 return dev_err_probe(dev, ret, "%s: codec dai not found\n", 40 dai_link->name); 41 42 return 0; 43 } 44 45 static int set_dailink_daifmt(struct snd_soc_card *card, 46 struct device_node *sub_node, 47 struct snd_soc_dai_link *dai_link) 48 { 49 unsigned int daifmt; 50 const char *str; 51 int ret; 52 struct { 53 char *name; 54 unsigned int val; 55 } of_clk_table[] = { 56 { "cpu", SND_SOC_DAIFMT_CBC_CFC }, 57 { "codec", SND_SOC_DAIFMT_CBP_CFP }, 58 }; 59 60 daifmt = snd_soc_daifmt_parse_format(sub_node, NULL); 61 if (daifmt) { 62 dai_link->dai_fmt &= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; 63 dai_link->dai_fmt |= daifmt; 64 } 65 66 /* 67 * check "mediatek,clk-provider = xxx" 68 * SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK area 69 */ 70 ret = of_property_read_string(sub_node, "mediatek,clk-provider", &str); 71 if (ret == 0) { 72 int i; 73 74 for (i = 0; i < ARRAY_SIZE(of_clk_table); i++) { 75 if (strcmp(str, of_clk_table[i].name) == 0) { 76 dai_link->dai_fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; 77 dai_link->dai_fmt |= of_clk_table[i].val; 78 break; 79 } 80 } 81 } 82 83 return 0; 84 } 85 86 int parse_dai_link_info(struct snd_soc_card *card) 87 { 88 struct device *dev = card->dev; 89 struct device_node *sub_node; 90 struct snd_soc_dai_link *dai_link; 91 const char *dai_link_name; 92 int ret, i; 93 94 /* Loop over all the dai link sub nodes */ 95 for_each_available_child_of_node(dev->of_node, sub_node) { 96 if (of_property_read_string(sub_node, "link-name", 97 &dai_link_name)) { 98 of_node_put(sub_node); 99 return -EINVAL; 100 } 101 102 for_each_card_prelinks(card, i, dai_link) { 103 if (!strcmp(dai_link_name, dai_link->name)) 104 break; 105 } 106 107 if (i >= card->num_links) { 108 of_node_put(sub_node); 109 return -EINVAL; 110 } 111 112 ret = set_card_codec_info(card, sub_node, dai_link); 113 if (ret < 0) { 114 of_node_put(sub_node); 115 return ret; 116 } 117 118 ret = set_dailink_daifmt(card, sub_node, dai_link); 119 if (ret < 0) { 120 of_node_put(sub_node); 121 return ret; 122 } 123 } 124 125 return 0; 126 } 127 EXPORT_SYMBOL_GPL(parse_dai_link_info); 128 129 void clean_card_reference(struct snd_soc_card *card) 130 { 131 struct snd_soc_dai_link *dai_link; 132 int i; 133 134 /* release codec reference gotten by set_card_codec_info */ 135 for_each_card_prelinks(card, i, dai_link) 136 snd_soc_of_put_dai_link_codecs(dai_link); 137 } 138 EXPORT_SYMBOL_GPL(clean_card_reference); 139