diff options
Diffstat (limited to 'sound/soc/generic/simple-card-utils.c')
-rw-r--r-- | sound/soc/generic/simple-card-utils.c | 198 |
1 files changed, 189 insertions, 9 deletions
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 343b291fc372..26d64fa40c9c 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c | |||
@@ -10,8 +10,49 @@ | |||
10 | #include <linux/clk.h> | 10 | #include <linux/clk.h> |
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/of.h> | 12 | #include <linux/of.h> |
13 | #include <linux/of_graph.h> | ||
13 | #include <sound/simple_card_utils.h> | 14 | #include <sound/simple_card_utils.h> |
14 | 15 | ||
16 | void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data, | ||
17 | struct snd_pcm_hw_params *params) | ||
18 | { | ||
19 | struct snd_interval *rate = hw_param_interval(params, | ||
20 | SNDRV_PCM_HW_PARAM_RATE); | ||
21 | struct snd_interval *channels = hw_param_interval(params, | ||
22 | SNDRV_PCM_HW_PARAM_CHANNELS); | ||
23 | |||
24 | if (data->convert_rate) | ||
25 | rate->min = | ||
26 | rate->max = data->convert_rate; | ||
27 | |||
28 | if (data->convert_channels) | ||
29 | channels->min = | ||
30 | channels->max = data->convert_channels; | ||
31 | } | ||
32 | EXPORT_SYMBOL_GPL(asoc_simple_card_convert_fixup); | ||
33 | |||
34 | void asoc_simple_card_parse_convert(struct device *dev, char *prefix, | ||
35 | struct asoc_simple_card_data *data) | ||
36 | { | ||
37 | struct device_node *np = dev->of_node; | ||
38 | char prop[128]; | ||
39 | |||
40 | if (!prefix) | ||
41 | prefix = ""; | ||
42 | |||
43 | /* sampling rate convert */ | ||
44 | snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate"); | ||
45 | of_property_read_u32(np, prop, &data->convert_rate); | ||
46 | |||
47 | /* channels transfer */ | ||
48 | snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels"); | ||
49 | of_property_read_u32(np, prop, &data->convert_channels); | ||
50 | |||
51 | dev_dbg(dev, "convert_rate %d\n", data->convert_rate); | ||
52 | dev_dbg(dev, "convert_channels %d\n", data->convert_channels); | ||
53 | } | ||
54 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_convert); | ||
55 | |||
15 | int asoc_simple_card_parse_daifmt(struct device *dev, | 56 | int asoc_simple_card_parse_daifmt(struct device *dev, |
16 | struct device_node *node, | 57 | struct device_node *node, |
17 | struct device_node *codec, | 58 | struct device_node *codec, |
@@ -20,14 +61,13 @@ int asoc_simple_card_parse_daifmt(struct device *dev, | |||
20 | { | 61 | { |
21 | struct device_node *bitclkmaster = NULL; | 62 | struct device_node *bitclkmaster = NULL; |
22 | struct device_node *framemaster = NULL; | 63 | struct device_node *framemaster = NULL; |
23 | int prefix_len = prefix ? strlen(prefix) : 0; | ||
24 | unsigned int daifmt; | 64 | unsigned int daifmt; |
25 | 65 | ||
26 | daifmt = snd_soc_of_parse_daifmt(node, prefix, | 66 | daifmt = snd_soc_of_parse_daifmt(node, prefix, |
27 | &bitclkmaster, &framemaster); | 67 | &bitclkmaster, &framemaster); |
28 | daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; | 68 | daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; |
29 | 69 | ||
30 | if (prefix_len && !bitclkmaster && !framemaster) { | 70 | if (!bitclkmaster && !framemaster) { |
31 | /* | 71 | /* |
32 | * No dai-link level and master setting was not found from | 72 | * No dai-link level and master setting was not found from |
33 | * sound node level, revert back to legacy DT parsing and | 73 | * sound node level, revert back to legacy DT parsing and |
@@ -51,6 +91,8 @@ int asoc_simple_card_parse_daifmt(struct device *dev, | |||
51 | 91 | ||
52 | *retfmt = daifmt; | 92 | *retfmt = daifmt; |
53 | 93 | ||
94 | dev_dbg(dev, "format : %04x\n", daifmt); | ||
95 | |||
54 | return 0; | 96 | return 0; |
55 | } | 97 | } |
56 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt); | 98 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt); |
@@ -72,6 +114,8 @@ int asoc_simple_card_set_dailink_name(struct device *dev, | |||
72 | 114 | ||
73 | dai_link->name = name; | 115 | dai_link->name = name; |
74 | dai_link->stream_name = name; | 116 | dai_link->stream_name = name; |
117 | |||
118 | dev_dbg(dev, "name : %s\n", name); | ||
75 | } | 119 | } |
76 | 120 | ||
77 | return ret; | 121 | return ret; |
@@ -81,27 +125,54 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name); | |||
81 | int asoc_simple_card_parse_card_name(struct snd_soc_card *card, | 125 | int asoc_simple_card_parse_card_name(struct snd_soc_card *card, |
82 | char *prefix) | 126 | char *prefix) |
83 | { | 127 | { |
84 | char prop[128]; | ||
85 | int ret; | 128 | int ret; |
86 | 129 | ||
87 | snprintf(prop, sizeof(prop), "%sname", prefix); | 130 | if (!prefix) |
131 | prefix = ""; | ||
88 | 132 | ||
89 | /* Parse the card name from DT */ | 133 | /* Parse the card name from DT */ |
90 | ret = snd_soc_of_parse_card_name(card, prop); | 134 | ret = snd_soc_of_parse_card_name(card, "label"); |
91 | if (ret < 0) | 135 | if (ret < 0) { |
92 | return ret; | 136 | char prop[128]; |
137 | |||
138 | snprintf(prop, sizeof(prop), "%sname", prefix); | ||
139 | ret = snd_soc_of_parse_card_name(card, prop); | ||
140 | if (ret < 0) | ||
141 | return ret; | ||
142 | } | ||
93 | 143 | ||
94 | if (!card->name && card->dai_link) | 144 | if (!card->name && card->dai_link) |
95 | card->name = card->dai_link->name; | 145 | card->name = card->dai_link->name; |
96 | 146 | ||
147 | dev_dbg(card->dev, "Card Name: %s\n", card->name ? card->name : ""); | ||
148 | |||
97 | return 0; | 149 | return 0; |
98 | } | 150 | } |
99 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); | 151 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); |
100 | 152 | ||
153 | static void asoc_simple_card_clk_register(struct asoc_simple_dai *dai, | ||
154 | struct clk *clk) | ||
155 | { | ||
156 | dai->clk = clk; | ||
157 | } | ||
158 | |||
159 | int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai) | ||
160 | { | ||
161 | return clk_prepare_enable(dai->clk); | ||
162 | } | ||
163 | EXPORT_SYMBOL_GPL(asoc_simple_card_clk_enable); | ||
164 | |||
165 | void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai) | ||
166 | { | ||
167 | clk_disable_unprepare(dai->clk); | ||
168 | } | ||
169 | EXPORT_SYMBOL_GPL(asoc_simple_card_clk_disable); | ||
170 | |||
101 | int asoc_simple_card_parse_clk(struct device *dev, | 171 | int asoc_simple_card_parse_clk(struct device *dev, |
102 | struct device_node *node, | 172 | struct device_node *node, |
103 | struct device_node *dai_of_node, | 173 | struct device_node *dai_of_node, |
104 | struct asoc_simple_dai *simple_dai) | 174 | struct asoc_simple_dai *simple_dai, |
175 | const char *name) | ||
105 | { | 176 | { |
106 | struct clk *clk; | 177 | struct clk *clk; |
107 | u32 val; | 178 | u32 val; |
@@ -115,7 +186,8 @@ int asoc_simple_card_parse_clk(struct device *dev, | |||
115 | clk = devm_get_clk_from_child(dev, node, NULL); | 186 | clk = devm_get_clk_from_child(dev, node, NULL); |
116 | if (!IS_ERR(clk)) { | 187 | if (!IS_ERR(clk)) { |
117 | simple_dai->sysclk = clk_get_rate(clk); | 188 | simple_dai->sysclk = clk_get_rate(clk); |
118 | simple_dai->clk = clk; | 189 | |
190 | asoc_simple_card_clk_register(simple_dai, clk); | ||
119 | } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) { | 191 | } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) { |
120 | simple_dai->sysclk = val; | 192 | simple_dai->sysclk = val; |
121 | } else { | 193 | } else { |
@@ -124,6 +196,8 @@ int asoc_simple_card_parse_clk(struct device *dev, | |||
124 | simple_dai->sysclk = clk_get_rate(clk); | 196 | simple_dai->sysclk = clk_get_rate(clk); |
125 | } | 197 | } |
126 | 198 | ||
199 | dev_dbg(dev, "%s : sysclk = %d\n", name, simple_dai->sysclk); | ||
200 | |||
127 | return 0; | 201 | return 0; |
128 | } | 202 | } |
129 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk); | 203 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk); |
@@ -165,6 +239,71 @@ int asoc_simple_card_parse_dai(struct device_node *node, | |||
165 | } | 239 | } |
166 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); | 240 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); |
167 | 241 | ||
242 | static int asoc_simple_card_get_dai_id(struct device_node *ep) | ||
243 | { | ||
244 | struct device_node *node; | ||
245 | struct device_node *endpoint; | ||
246 | int i, id; | ||
247 | int ret; | ||
248 | |||
249 | ret = snd_soc_get_dai_id(ep); | ||
250 | if (ret != -ENOTSUPP) | ||
251 | return ret; | ||
252 | |||
253 | node = of_graph_get_port_parent(ep); | ||
254 | |||
255 | /* | ||
256 | * Non HDMI sound case, counting port/endpoint on its DT | ||
257 | * is enough. Let's count it. | ||
258 | */ | ||
259 | i = 0; | ||
260 | id = -1; | ||
261 | for_each_endpoint_of_node(node, endpoint) { | ||
262 | if (endpoint == ep) | ||
263 | id = i; | ||
264 | i++; | ||
265 | } | ||
266 | if (id < 0) | ||
267 | return -ENODEV; | ||
268 | |||
269 | return id; | ||
270 | } | ||
271 | |||
272 | int asoc_simple_card_parse_graph_dai(struct device_node *ep, | ||
273 | struct device_node **dai_of_node, | ||
274 | const char **dai_name) | ||
275 | { | ||
276 | struct device_node *node; | ||
277 | struct of_phandle_args args; | ||
278 | int ret; | ||
279 | |||
280 | if (!ep) | ||
281 | return 0; | ||
282 | if (!dai_name) | ||
283 | return 0; | ||
284 | |||
285 | /* | ||
286 | * of_graph_get_port_parent() will call | ||
287 | * of_node_put(). So, call of_node_get() here | ||
288 | */ | ||
289 | of_node_get(ep); | ||
290 | node = of_graph_get_port_parent(ep); | ||
291 | |||
292 | /* Get dai->name */ | ||
293 | args.np = node; | ||
294 | args.args[0] = asoc_simple_card_get_dai_id(ep); | ||
295 | args.args_count = (of_graph_get_endpoint_count(node) > 1); | ||
296 | |||
297 | ret = snd_soc_get_dai_name(&args, dai_name); | ||
298 | if (ret < 0) | ||
299 | return ret; | ||
300 | |||
301 | *dai_of_node = node; | ||
302 | |||
303 | return 0; | ||
304 | } | ||
305 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai); | ||
306 | |||
168 | int asoc_simple_card_init_dai(struct snd_soc_dai *dai, | 307 | int asoc_simple_card_init_dai(struct snd_soc_dai *dai, |
169 | struct asoc_simple_dai *simple_dai) | 308 | struct asoc_simple_dai *simple_dai) |
170 | { | 309 | { |
@@ -236,6 +375,47 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card) | |||
236 | } | 375 | } |
237 | EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference); | 376 | EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference); |
238 | 377 | ||
378 | int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, | ||
379 | char *prefix, | ||
380 | int optional) | ||
381 | { | ||
382 | struct device_node *node = card->dev->of_node; | ||
383 | char prop[128]; | ||
384 | |||
385 | if (!prefix) | ||
386 | prefix = ""; | ||
387 | |||
388 | snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); | ||
389 | |||
390 | if (!of_property_read_bool(node, prop)) { | ||
391 | if (optional) | ||
392 | return 0; | ||
393 | return -EINVAL; | ||
394 | } | ||
395 | |||
396 | return snd_soc_of_parse_audio_routing(card, prop); | ||
397 | } | ||
398 | EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_routing); | ||
399 | |||
400 | int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card, | ||
401 | char *prefix) | ||
402 | { | ||
403 | struct device_node *node = card->dev->of_node; | ||
404 | char prop[128]; | ||
405 | |||
406 | if (!prefix) | ||
407 | prefix = ""; | ||
408 | |||
409 | snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); | ||
410 | |||
411 | if (of_property_read_bool(node, prop)) | ||
412 | return snd_soc_of_parse_audio_simple_widgets(card, prop); | ||
413 | |||
414 | /* no widgets is not error */ | ||
415 | return 0; | ||
416 | } | ||
417 | EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_widgets); | ||
418 | |||
239 | /* Module information */ | 419 | /* Module information */ |
240 | MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); | 420 | MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); |
241 | MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); | 421 | MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); |