1 /* 2 * simple-card-core.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_node *node, 102 struct device_node *dai_of_node, 103 struct asoc_simple_dai *simple_dai) 104 { 105 struct clk *clk; 106 u32 val; 107 108 /* 109 * Parse dai->sysclk come from "clocks = <&xxx>" 110 * (if system has common clock) 111 * or "system-clock-frequency = <xxx>" 112 * or device's module clock. 113 */ 114 clk = of_clk_get(node, 0); 115 if (!IS_ERR(clk)) { 116 simple_dai->sysclk = clk_get_rate(clk); 117 simple_dai->clk = clk; 118 } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) { 119 simple_dai->sysclk = val; 120 } else { 121 clk = of_clk_get(dai_of_node, 0); 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 if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) 199 return -EINVAL; 200 201 /* Assumes platform == cpu */ 202 if (!dai_link->platform_of_node) 203 dai_link->platform_of_node = dai_link->cpu_of_node; 204 205 return 0; 206 } 207 EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_dailink); 208 209 void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link, 210 int is_single_links) 211 { 212 /* 213 * In soc_bind_dai_link() will check cpu name after 214 * of_node matching if dai_link has cpu_dai_name. 215 * but, it will never match if name was created by 216 * fmt_single_name() remove cpu_dai_name if cpu_args 217 * was 0. See: 218 * fmt_single_name() 219 * fmt_multiple_name() 220 */ 221 if (is_single_links) 222 dai_link->cpu_dai_name = NULL; 223 } 224 EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_cpu); 225 226 int asoc_simple_card_clean_reference(struct snd_soc_card *card) 227 { 228 struct snd_soc_dai_link *dai_link; 229 int num_links; 230 231 for (num_links = 0, dai_link = card->dai_link; 232 num_links < card->num_links; 233 num_links++, dai_link++) { 234 of_node_put(dai_link->cpu_of_node); 235 of_node_put(dai_link->codec_of_node); 236 } 237 return 0; 238 } 239 EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference); 240 241 /* Module information */ 242 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 243 MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); 244 MODULE_LICENSE("GPL v2"); 245