1 /* 2 * simple-card-utils.c 3 * 4 * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 #include <linux/clk.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <sound/simple_card_utils.h> 14 15 int asoc_simple_card_parse_daifmt(struct device *dev, 16 struct device_node *node, 17 struct device_node *codec, 18 char *prefix, 19 unsigned int *retfmt) 20 { 21 struct device_node *bitclkmaster = NULL; 22 struct device_node *framemaster = NULL; 23 int prefix_len = prefix ? strlen(prefix) : 0; 24 unsigned int daifmt; 25 26 daifmt = snd_soc_of_parse_daifmt(node, prefix, 27 &bitclkmaster, &framemaster); 28 daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; 29 30 if (prefix_len && !bitclkmaster && !framemaster) { 31 /* 32 * No dai-link level and master setting was not found from 33 * sound node level, revert back to legacy DT parsing and 34 * take the settings from codec node. 35 */ 36 dev_dbg(dev, "Revert to legacy daifmt parsing\n"); 37 38 daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) | 39 (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK); 40 } else { 41 if (codec == bitclkmaster) 42 daifmt |= (codec == framemaster) ? 43 SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; 44 else 45 daifmt |= (codec == framemaster) ? 46 SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; 47 } 48 49 of_node_put(bitclkmaster); 50 of_node_put(framemaster); 51 52 *retfmt = daifmt; 53 54 return 0; 55 } 56 EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt); 57 58 int asoc_simple_card_set_dailink_name(struct device *dev, 59 struct snd_soc_dai_link *dai_link, 60 const char *fmt, ...) 61 { 62 va_list ap; 63 char *name = NULL; 64 int ret = -ENOMEM; 65 66 va_start(ap, fmt); 67 name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap); 68 va_end(ap); 69 70 if (name) { 71 ret = 0; 72 73 dai_link->name = name; 74 dai_link->stream_name = name; 75 } 76 77 return ret; 78 } 79 EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name); 80 81 int asoc_simple_card_parse_card_name(struct snd_soc_card *card, 82 char *prefix) 83 { 84 char prop[128]; 85 int ret; 86 87 snprintf(prop, sizeof(prop), "%sname", prefix); 88 89 /* Parse the card name from DT */ 90 ret = snd_soc_of_parse_card_name(card, prop); 91 if (ret < 0) 92 return ret; 93 94 if (!card->name && card->dai_link) 95 card->name = card->dai_link->name; 96 97 return 0; 98 } 99 EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); 100 101 int asoc_simple_card_parse_clk(struct device *dev, 102 struct device_node *node, 103 struct device_node *dai_of_node, 104 struct asoc_simple_dai *simple_dai) 105 { 106 struct clk *clk; 107 u32 val; 108 109 /* 110 * Parse dai->sysclk come from "clocks = <&xxx>" 111 * (if system has common clock) 112 * or "system-clock-frequency = <xxx>" 113 * or device's module clock. 114 */ 115 clk = devm_get_clk_from_child(dev, node, NULL); 116 if (!IS_ERR(clk)) { 117 simple_dai->sysclk = clk_get_rate(clk); 118 } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) { 119 simple_dai->sysclk = val; 120 } else { 121 clk = devm_get_clk_from_child(dev, dai_of_node, NULL); 122 if (!IS_ERR(clk)) 123 simple_dai->sysclk = clk_get_rate(clk); 124 } 125 126 return 0; 127 } 128 EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk); 129 130 int asoc_simple_card_parse_dai(struct device_node *node, 131 struct device_node **dai_of_node, 132 const char **dai_name, 133 const char *list_name, 134 const char *cells_name, 135 int *is_single_link) 136 { 137 struct of_phandle_args args; 138 int ret; 139 140 if (!node) 141 return 0; 142 143 /* 144 * Get node via "sound-dai = <&phandle port>" 145 * it will be used as xxx_of_node on soc_bind_dai_link() 146 */ 147 ret = of_parse_phandle_with_args(node, list_name, cells_name, 0, &args); 148 if (ret) 149 return ret; 150 151 /* Get dai->name */ 152 if (dai_name) { 153 ret = snd_soc_of_get_dai_name(node, dai_name); 154 if (ret < 0) 155 return ret; 156 } 157 158 *dai_of_node = args.np; 159 160 if (is_single_link) 161 *is_single_link = !args.args_count; 162 163 return 0; 164 } 165 EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); 166 167 int asoc_simple_card_init_dai(struct snd_soc_dai *dai, 168 struct asoc_simple_dai *simple_dai) 169 { 170 int ret; 171 172 if (simple_dai->sysclk) { 173 ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, 0); 174 if (ret && ret != -ENOTSUPP) { 175 dev_err(dai->dev, "simple-card: set_sysclk error\n"); 176 return ret; 177 } 178 } 179 180 if (simple_dai->slots) { 181 ret = snd_soc_dai_set_tdm_slot(dai, 182 simple_dai->tx_slot_mask, 183 simple_dai->rx_slot_mask, 184 simple_dai->slots, 185 simple_dai->slot_width); 186 if (ret && ret != -ENOTSUPP) { 187 dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); 188 return ret; 189 } 190 } 191 192 return 0; 193 } 194 EXPORT_SYMBOL_GPL(asoc_simple_card_init_dai); 195 196 int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link) 197 { 198 /* Assumes platform == cpu */ 199 if (!dai_link->platform_of_node) 200 dai_link->platform_of_node = dai_link->cpu_of_node; 201 202 return 0; 203 } 204 EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_dailink); 205 206 void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link, 207 int is_single_links) 208 { 209 /* 210 * In soc_bind_dai_link() will check cpu name after 211 * of_node matching if dai_link has cpu_dai_name. 212 * but, it will never match if name was created by 213 * fmt_single_name() remove cpu_dai_name if cpu_args 214 * was 0. See: 215 * fmt_single_name() 216 * fmt_multiple_name() 217 */ 218 if (is_single_links) 219 dai_link->cpu_dai_name = NULL; 220 } 221 EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_cpu); 222 223 int asoc_simple_card_clean_reference(struct snd_soc_card *card) 224 { 225 struct snd_soc_dai_link *dai_link; 226 int num_links; 227 228 for (num_links = 0, dai_link = card->dai_link; 229 num_links < card->num_links; 230 num_links++, dai_link++) { 231 of_node_put(dai_link->cpu_of_node); 232 of_node_put(dai_link->codec_of_node); 233 } 234 return 0; 235 } 236 EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference); 237 238 /* Module information */ 239 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 240 MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); 241 MODULE_LICENSE("GPL v2"); 242