diff options
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 164 |
1 files changed, 143 insertions, 21 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 89eae93445c..f7a13f72052 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/debugfs.h> | 35 | #include <linux/debugfs.h> |
36 | #include <linux/pm_runtime.h> | 36 | #include <linux/pm_runtime.h> |
37 | #include <linux/regulator/consumer.h> | 37 | #include <linux/regulator/consumer.h> |
38 | #include <linux/clk.h> | ||
38 | #include <linux/slab.h> | 39 | #include <linux/slab.h> |
39 | #include <sound/core.h> | 40 | #include <sound/core.h> |
40 | #include <sound/pcm.h> | 41 | #include <sound/pcm.h> |
@@ -51,6 +52,7 @@ static int dapm_up_seq[] = { | |||
51 | [snd_soc_dapm_pre] = 0, | 52 | [snd_soc_dapm_pre] = 0, |
52 | [snd_soc_dapm_supply] = 1, | 53 | [snd_soc_dapm_supply] = 1, |
53 | [snd_soc_dapm_regulator_supply] = 1, | 54 | [snd_soc_dapm_regulator_supply] = 1, |
55 | [snd_soc_dapm_clock_supply] = 1, | ||
54 | [snd_soc_dapm_micbias] = 2, | 56 | [snd_soc_dapm_micbias] = 2, |
55 | [snd_soc_dapm_dai_link] = 2, | 57 | [snd_soc_dapm_dai_link] = 2, |
56 | [snd_soc_dapm_dai] = 3, | 58 | [snd_soc_dapm_dai] = 3, |
@@ -92,6 +94,7 @@ static int dapm_down_seq[] = { | |||
92 | [snd_soc_dapm_aif_out] = 10, | 94 | [snd_soc_dapm_aif_out] = 10, |
93 | [snd_soc_dapm_dai] = 10, | 95 | [snd_soc_dapm_dai] = 10, |
94 | [snd_soc_dapm_dai_link] = 11, | 96 | [snd_soc_dapm_dai_link] = 11, |
97 | [snd_soc_dapm_clock_supply] = 12, | ||
95 | [snd_soc_dapm_regulator_supply] = 12, | 98 | [snd_soc_dapm_regulator_supply] = 12, |
96 | [snd_soc_dapm_supply] = 12, | 99 | [snd_soc_dapm_supply] = 12, |
97 | [snd_soc_dapm_post] = 13, | 100 | [snd_soc_dapm_post] = 13, |
@@ -391,6 +394,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, | |||
391 | case snd_soc_dapm_vmid: | 394 | case snd_soc_dapm_vmid: |
392 | case snd_soc_dapm_supply: | 395 | case snd_soc_dapm_supply: |
393 | case snd_soc_dapm_regulator_supply: | 396 | case snd_soc_dapm_regulator_supply: |
397 | case snd_soc_dapm_clock_supply: | ||
394 | case snd_soc_dapm_aif_in: | 398 | case snd_soc_dapm_aif_in: |
395 | case snd_soc_dapm_aif_out: | 399 | case snd_soc_dapm_aif_out: |
396 | case snd_soc_dapm_dai: | 400 | case snd_soc_dapm_dai: |
@@ -764,6 +768,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, | |||
764 | switch (widget->id) { | 768 | switch (widget->id) { |
765 | case snd_soc_dapm_supply: | 769 | case snd_soc_dapm_supply: |
766 | case snd_soc_dapm_regulator_supply: | 770 | case snd_soc_dapm_regulator_supply: |
771 | case snd_soc_dapm_clock_supply: | ||
767 | return 0; | 772 | return 0; |
768 | default: | 773 | default: |
769 | break; | 774 | break; |
@@ -850,6 +855,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, | |||
850 | switch (widget->id) { | 855 | switch (widget->id) { |
851 | case snd_soc_dapm_supply: | 856 | case snd_soc_dapm_supply: |
852 | case snd_soc_dapm_regulator_supply: | 857 | case snd_soc_dapm_regulator_supply: |
858 | case snd_soc_dapm_clock_supply: | ||
853 | return 0; | 859 | return 0; |
854 | default: | 860 | default: |
855 | break; | 861 | break; |
@@ -996,6 +1002,27 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, | |||
996 | } | 1002 | } |
997 | EXPORT_SYMBOL_GPL(dapm_regulator_event); | 1003 | EXPORT_SYMBOL_GPL(dapm_regulator_event); |
998 | 1004 | ||
1005 | /* | ||
1006 | * Handler for clock supply widget. | ||
1007 | */ | ||
1008 | int dapm_clock_event(struct snd_soc_dapm_widget *w, | ||
1009 | struct snd_kcontrol *kcontrol, int event) | ||
1010 | { | ||
1011 | if (!w->clk) | ||
1012 | return -EIO; | ||
1013 | |||
1014 | #ifdef CONFIG_HAVE_CLK | ||
1015 | if (SND_SOC_DAPM_EVENT_ON(event)) { | ||
1016 | return clk_enable(w->clk); | ||
1017 | } else { | ||
1018 | clk_disable(w->clk); | ||
1019 | return 0; | ||
1020 | } | ||
1021 | #endif | ||
1022 | return 0; | ||
1023 | } | ||
1024 | EXPORT_SYMBOL_GPL(dapm_clock_event); | ||
1025 | |||
999 | static int dapm_widget_power_check(struct snd_soc_dapm_widget *w) | 1026 | static int dapm_widget_power_check(struct snd_soc_dapm_widget *w) |
1000 | { | 1027 | { |
1001 | if (w->power_checked) | 1028 | if (w->power_checked) |
@@ -1487,6 +1514,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, | |||
1487 | switch (w->id) { | 1514 | switch (w->id) { |
1488 | case snd_soc_dapm_supply: | 1515 | case snd_soc_dapm_supply: |
1489 | case snd_soc_dapm_regulator_supply: | 1516 | case snd_soc_dapm_regulator_supply: |
1517 | case snd_soc_dapm_clock_supply: | ||
1490 | /* Supplies can't affect their outputs, only their inputs */ | 1518 | /* Supplies can't affect their outputs, only their inputs */ |
1491 | break; | 1519 | break; |
1492 | default: | 1520 | default: |
@@ -1587,6 +1615,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) | |||
1587 | break; | 1615 | break; |
1588 | case snd_soc_dapm_supply: | 1616 | case snd_soc_dapm_supply: |
1589 | case snd_soc_dapm_regulator_supply: | 1617 | case snd_soc_dapm_regulator_supply: |
1618 | case snd_soc_dapm_clock_supply: | ||
1590 | case snd_soc_dapm_micbias: | 1619 | case snd_soc_dapm_micbias: |
1591 | if (d->target_bias_level < SND_SOC_BIAS_STANDBY) | 1620 | if (d->target_bias_level < SND_SOC_BIAS_STANDBY) |
1592 | d->target_bias_level = SND_SOC_BIAS_STANDBY; | 1621 | d->target_bias_level = SND_SOC_BIAS_STANDBY; |
@@ -1941,6 +1970,7 @@ static ssize_t dapm_widget_show(struct device *dev, | |||
1941 | case snd_soc_dapm_mixer_named_ctl: | 1970 | case snd_soc_dapm_mixer_named_ctl: |
1942 | case snd_soc_dapm_supply: | 1971 | case snd_soc_dapm_supply: |
1943 | case snd_soc_dapm_regulator_supply: | 1972 | case snd_soc_dapm_regulator_supply: |
1973 | case snd_soc_dapm_clock_supply: | ||
1944 | if (w->name) | 1974 | if (w->name) |
1945 | count += sprintf(buf + count, "%s: %s\n", | 1975 | count += sprintf(buf + count, "%s: %s\n", |
1946 | w->name, w->power ? "On":"Off"); | 1976 | w->name, w->power ? "On":"Off"); |
@@ -2187,6 +2217,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, | |||
2187 | case snd_soc_dapm_post: | 2217 | case snd_soc_dapm_post: |
2188 | case snd_soc_dapm_supply: | 2218 | case snd_soc_dapm_supply: |
2189 | case snd_soc_dapm_regulator_supply: | 2219 | case snd_soc_dapm_regulator_supply: |
2220 | case snd_soc_dapm_clock_supply: | ||
2190 | case snd_soc_dapm_aif_in: | 2221 | case snd_soc_dapm_aif_in: |
2191 | case snd_soc_dapm_aif_out: | 2222 | case snd_soc_dapm_aif_out: |
2192 | case snd_soc_dapm_dai: | 2223 | case snd_soc_dapm_dai: |
@@ -2221,6 +2252,10 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, | |||
2221 | path->connect = 0; | 2252 | path->connect = 0; |
2222 | return 0; | 2253 | return 0; |
2223 | } | 2254 | } |
2255 | |||
2256 | dapm_mark_dirty(wsource, "Route added"); | ||
2257 | dapm_mark_dirty(wsink, "Route added"); | ||
2258 | |||
2224 | return 0; | 2259 | return 0; |
2225 | 2260 | ||
2226 | err: | 2261 | err: |
@@ -2230,6 +2265,59 @@ err: | |||
2230 | return ret; | 2265 | return ret; |
2231 | } | 2266 | } |
2232 | 2267 | ||
2268 | static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, | ||
2269 | const struct snd_soc_dapm_route *route) | ||
2270 | { | ||
2271 | struct snd_soc_dapm_path *path, *p; | ||
2272 | const char *sink; | ||
2273 | const char *source; | ||
2274 | char prefixed_sink[80]; | ||
2275 | char prefixed_source[80]; | ||
2276 | |||
2277 | if (route->control) { | ||
2278 | dev_err(dapm->dev, | ||
2279 | "Removal of routes with controls not supported\n"); | ||
2280 | return -EINVAL; | ||
2281 | } | ||
2282 | |||
2283 | if (dapm->codec && dapm->codec->name_prefix) { | ||
2284 | snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", | ||
2285 | dapm->codec->name_prefix, route->sink); | ||
2286 | sink = prefixed_sink; | ||
2287 | snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", | ||
2288 | dapm->codec->name_prefix, route->source); | ||
2289 | source = prefixed_source; | ||
2290 | } else { | ||
2291 | sink = route->sink; | ||
2292 | source = route->source; | ||
2293 | } | ||
2294 | |||
2295 | path = NULL; | ||
2296 | list_for_each_entry(p, &dapm->card->paths, list) { | ||
2297 | if (strcmp(p->source->name, source) != 0) | ||
2298 | continue; | ||
2299 | if (strcmp(p->sink->name, sink) != 0) | ||
2300 | continue; | ||
2301 | path = p; | ||
2302 | break; | ||
2303 | } | ||
2304 | |||
2305 | if (path) { | ||
2306 | dapm_mark_dirty(path->source, "Route removed"); | ||
2307 | dapm_mark_dirty(path->sink, "Route removed"); | ||
2308 | |||
2309 | list_del(&path->list); | ||
2310 | list_del(&path->list_sink); | ||
2311 | list_del(&path->list_source); | ||
2312 | kfree(path); | ||
2313 | } else { | ||
2314 | dev_warn(dapm->dev, "Route %s->%s does not exist\n", | ||
2315 | source, sink); | ||
2316 | } | ||
2317 | |||
2318 | return 0; | ||
2319 | } | ||
2320 | |||
2233 | /** | 2321 | /** |
2234 | * snd_soc_dapm_add_routes - Add routes between DAPM widgets | 2322 | * snd_soc_dapm_add_routes - Add routes between DAPM widgets |
2235 | * @dapm: DAPM context | 2323 | * @dapm: DAPM context |
@@ -2246,15 +2334,15 @@ err: | |||
2246 | int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, | 2334 | int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, |
2247 | const struct snd_soc_dapm_route *route, int num) | 2335 | const struct snd_soc_dapm_route *route, int num) |
2248 | { | 2336 | { |
2249 | int i, ret = 0; | 2337 | int i, r, ret = 0; |
2250 | 2338 | ||
2251 | mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); | 2339 | mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); |
2252 | for (i = 0; i < num; i++) { | 2340 | for (i = 0; i < num; i++) { |
2253 | ret = snd_soc_dapm_add_route(dapm, route); | 2341 | r = snd_soc_dapm_add_route(dapm, route); |
2254 | if (ret < 0) { | 2342 | if (r < 0) { |
2255 | dev_err(dapm->dev, "Failed to add route %s->%s\n", | 2343 | dev_err(dapm->dev, "Failed to add route %s->%s\n", |
2256 | route->source, route->sink); | 2344 | route->source, route->sink); |
2257 | break; | 2345 | ret = r; |
2258 | } | 2346 | } |
2259 | route++; | 2347 | route++; |
2260 | } | 2348 | } |
@@ -2264,6 +2352,30 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, | |||
2264 | } | 2352 | } |
2265 | EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); | 2353 | EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); |
2266 | 2354 | ||
2355 | /** | ||
2356 | * snd_soc_dapm_del_routes - Remove routes between DAPM widgets | ||
2357 | * @dapm: DAPM context | ||
2358 | * @route: audio routes | ||
2359 | * @num: number of routes | ||
2360 | * | ||
2361 | * Removes routes from the DAPM context. | ||
2362 | */ | ||
2363 | int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, | ||
2364 | const struct snd_soc_dapm_route *route, int num) | ||
2365 | { | ||
2366 | int i, ret = 0; | ||
2367 | |||
2368 | mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); | ||
2369 | for (i = 0; i < num; i++) { | ||
2370 | snd_soc_dapm_del_route(dapm, route); | ||
2371 | route++; | ||
2372 | } | ||
2373 | mutex_unlock(&dapm->card->dapm_mutex); | ||
2374 | |||
2375 | return ret; | ||
2376 | } | ||
2377 | EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes); | ||
2378 | |||
2267 | static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, | 2379 | static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, |
2268 | const struct snd_soc_dapm_route *route) | 2380 | const struct snd_soc_dapm_route *route) |
2269 | { | 2381 | { |
@@ -2434,23 +2546,20 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, | |||
2434 | (struct soc_mixer_control *)kcontrol->private_value; | 2546 | (struct soc_mixer_control *)kcontrol->private_value; |
2435 | unsigned int reg = mc->reg; | 2547 | unsigned int reg = mc->reg; |
2436 | unsigned int shift = mc->shift; | 2548 | unsigned int shift = mc->shift; |
2437 | unsigned int rshift = mc->rshift; | ||
2438 | int max = mc->max; | 2549 | int max = mc->max; |
2439 | unsigned int invert = mc->invert; | ||
2440 | unsigned int mask = (1 << fls(max)) - 1; | 2550 | unsigned int mask = (1 << fls(max)) - 1; |
2551 | unsigned int invert = mc->invert; | ||
2552 | |||
2553 | if (snd_soc_volsw_is_stereo(mc)) | ||
2554 | dev_warn(widget->dapm->dev, | ||
2555 | "Control '%s' is stereo, which is not supported\n", | ||
2556 | kcontrol->id.name); | ||
2441 | 2557 | ||
2442 | ucontrol->value.integer.value[0] = | 2558 | ucontrol->value.integer.value[0] = |
2443 | (snd_soc_read(widget->codec, reg) >> shift) & mask; | 2559 | (snd_soc_read(widget->codec, reg) >> shift) & mask; |
2444 | if (shift != rshift) | 2560 | if (invert) |
2445 | ucontrol->value.integer.value[1] = | ||
2446 | (snd_soc_read(widget->codec, reg) >> rshift) & mask; | ||
2447 | if (invert) { | ||
2448 | ucontrol->value.integer.value[0] = | 2561 | ucontrol->value.integer.value[0] = |
2449 | max - ucontrol->value.integer.value[0]; | 2562 | max - ucontrol->value.integer.value[0]; |
2450 | if (shift != rshift) | ||
2451 | ucontrol->value.integer.value[1] = | ||
2452 | max - ucontrol->value.integer.value[1]; | ||
2453 | } | ||
2454 | 2563 | ||
2455 | return 0; | 2564 | return 0; |
2456 | } | 2565 | } |
@@ -2484,20 +2593,19 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, | |||
2484 | struct snd_soc_dapm_update update; | 2593 | struct snd_soc_dapm_update update; |
2485 | int wi; | 2594 | int wi; |
2486 | 2595 | ||
2596 | if (snd_soc_volsw_is_stereo(mc)) | ||
2597 | dev_warn(widget->dapm->dev, | ||
2598 | "Control '%s' is stereo, which is not supported\n", | ||
2599 | kcontrol->id.name); | ||
2600 | |||
2487 | val = (ucontrol->value.integer.value[0] & mask); | 2601 | val = (ucontrol->value.integer.value[0] & mask); |
2602 | connect = !!val; | ||
2488 | 2603 | ||
2489 | if (invert) | 2604 | if (invert) |
2490 | val = max - val; | 2605 | val = max - val; |
2491 | mask = mask << shift; | 2606 | mask = mask << shift; |
2492 | val = val << shift; | 2607 | val = val << shift; |
2493 | 2608 | ||
2494 | if (val) | ||
2495 | /* new connection */ | ||
2496 | connect = invert ? 0 : 1; | ||
2497 | else | ||
2498 | /* old connection must be powered down */ | ||
2499 | connect = invert ? 1 : 0; | ||
2500 | |||
2501 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); | 2609 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); |
2502 | 2610 | ||
2503 | change = snd_soc_test_bits(widget->codec, reg, mask, val); | 2611 | change = snd_soc_test_bits(widget->codec, reg, mask, val); |
@@ -2873,6 +2981,19 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, | |||
2873 | return NULL; | 2981 | return NULL; |
2874 | } | 2982 | } |
2875 | break; | 2983 | break; |
2984 | case snd_soc_dapm_clock_supply: | ||
2985 | #ifdef CONFIG_CLKDEV_LOOKUP | ||
2986 | w->clk = devm_clk_get(dapm->dev, w->name); | ||
2987 | if (IS_ERR(w->clk)) { | ||
2988 | ret = PTR_ERR(w->clk); | ||
2989 | dev_err(dapm->dev, "Failed to request %s: %d\n", | ||
2990 | w->name, ret); | ||
2991 | return NULL; | ||
2992 | } | ||
2993 | #else | ||
2994 | return NULL; | ||
2995 | #endif | ||
2996 | break; | ||
2876 | default: | 2997 | default: |
2877 | break; | 2998 | break; |
2878 | } | 2999 | } |
@@ -2924,6 +3045,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, | |||
2924 | break; | 3045 | break; |
2925 | case snd_soc_dapm_supply: | 3046 | case snd_soc_dapm_supply: |
2926 | case snd_soc_dapm_regulator_supply: | 3047 | case snd_soc_dapm_regulator_supply: |
3048 | case snd_soc_dapm_clock_supply: | ||
2927 | w->power_check = dapm_supply_check_power; | 3049 | w->power_check = dapm_supply_check_power; |
2928 | break; | 3050 | break; |
2929 | case snd_soc_dapm_dai: | 3051 | case snd_soc_dapm_dai: |