Commit | Line | Data |
---|---|---|
abd3147e KM |
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 | */ | |
bb6fc620 | 10 | #include <linux/clk.h> |
1f85e118 | 11 | #include <linux/module.h> |
abd3147e KM |
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); | |
1db3312e KM |
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); | |
fc55c9b5 KM |
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); | |
1f85e118 | 100 | |
bb6fc620 KM |
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 | ||
ae30a694 KM |
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 | ||
21ba62f8 KM |
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 | ||
c262c9ab KM |
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 | ||
983cebd6 KM |
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 | ||
0f4e0711 KM |
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 | ||
1f85e118 KM |
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"); |