diff options
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 217 |
1 files changed, 96 insertions, 121 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7c28f401f436..03cb7c05ebec 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -98,7 +98,6 @@ static void pop_dbg(u32 pop_time, const char *fmt, ...) | |||
98 | 98 | ||
99 | if (pop_time) { | 99 | if (pop_time) { |
100 | vprintk(fmt, args); | 100 | vprintk(fmt, args); |
101 | pop_wait(pop_time); | ||
102 | } | 101 | } |
103 | 102 | ||
104 | va_end(args); | 103 | va_end(args); |
@@ -315,62 +314,14 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget) | |||
315 | pop_dbg(codec->pop_time, "pop test %s : %s in %d ms\n", | 314 | pop_dbg(codec->pop_time, "pop test %s : %s in %d ms\n", |
316 | widget->name, widget->power ? "on" : "off", | 315 | widget->name, widget->power ? "on" : "off", |
317 | codec->pop_time); | 316 | codec->pop_time); |
318 | snd_soc_write(codec, widget->reg, new); | ||
319 | pop_wait(codec->pop_time); | 317 | pop_wait(codec->pop_time); |
318 | snd_soc_write(codec, widget->reg, new); | ||
320 | } | 319 | } |
321 | pr_debug("reg %x old %x new %x change %d\n", widget->reg, | 320 | pr_debug("reg %x old %x new %x change %d\n", widget->reg, |
322 | old, new, change); | 321 | old, new, change); |
323 | return change; | 322 | return change; |
324 | } | 323 | } |
325 | 324 | ||
326 | /* ramps the volume up or down to minimise pops before or after a | ||
327 | * DAPM power event */ | ||
328 | static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power) | ||
329 | { | ||
330 | const struct snd_kcontrol_new *k = widget->kcontrols; | ||
331 | |||
332 | if (widget->muted && !power) | ||
333 | return 0; | ||
334 | if (!widget->muted && power) | ||
335 | return 0; | ||
336 | |||
337 | if (widget->num_kcontrols && k) { | ||
338 | struct soc_mixer_control *mc = | ||
339 | (struct soc_mixer_control *)k->private_value; | ||
340 | unsigned int reg = mc->reg; | ||
341 | unsigned int shift = mc->shift; | ||
342 | int max = mc->max; | ||
343 | unsigned int mask = (1 << fls(max)) - 1; | ||
344 | unsigned int invert = mc->invert; | ||
345 | |||
346 | if (power) { | ||
347 | int i; | ||
348 | /* power up has happended, increase volume to last level */ | ||
349 | if (invert) { | ||
350 | for (i = max; i > widget->saved_value; i--) | ||
351 | snd_soc_update_bits(widget->codec, reg, mask, i); | ||
352 | } else { | ||
353 | for (i = 0; i < widget->saved_value; i++) | ||
354 | snd_soc_update_bits(widget->codec, reg, mask, i); | ||
355 | } | ||
356 | widget->muted = 0; | ||
357 | } else { | ||
358 | /* power down is about to occur, decrease volume to mute */ | ||
359 | int val = snd_soc_read(widget->codec, reg); | ||
360 | int i = widget->saved_value = (val >> shift) & mask; | ||
361 | if (invert) { | ||
362 | for (; i < mask; i++) | ||
363 | snd_soc_update_bits(widget->codec, reg, mask, i); | ||
364 | } else { | ||
365 | for (; i > 0; i--) | ||
366 | snd_soc_update_bits(widget->codec, reg, mask, i); | ||
367 | } | ||
368 | widget->muted = 1; | ||
369 | } | ||
370 | } | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | /* create new dapm mixer control */ | 325 | /* create new dapm mixer control */ |
375 | static int dapm_new_mixer(struct snd_soc_codec *codec, | 326 | static int dapm_new_mixer(struct snd_soc_codec *codec, |
376 | struct snd_soc_dapm_widget *w) | 327 | struct snd_soc_dapm_widget *w) |
@@ -465,20 +416,10 @@ err: | |||
465 | static int dapm_new_pga(struct snd_soc_codec *codec, | 416 | static int dapm_new_pga(struct snd_soc_codec *codec, |
466 | struct snd_soc_dapm_widget *w) | 417 | struct snd_soc_dapm_widget *w) |
467 | { | 418 | { |
468 | struct snd_kcontrol *kcontrol; | 419 | if (w->num_kcontrols) |
469 | int ret = 0; | 420 | pr_err("asoc: PGA controls not supported: '%s'\n", w->name); |
470 | |||
471 | if (!w->num_kcontrols) | ||
472 | return -EINVAL; | ||
473 | 421 | ||
474 | kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name); | 422 | return 0; |
475 | ret = snd_ctl_add(codec->card, kcontrol); | ||
476 | if (ret < 0) { | ||
477 | printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name); | ||
478 | return ret; | ||
479 | } | ||
480 | |||
481 | return ret; | ||
482 | } | 423 | } |
483 | 424 | ||
484 | /* reset 'walked' bit for each dapm path */ | 425 | /* reset 'walked' bit for each dapm path */ |
@@ -490,6 +431,25 @@ static inline void dapm_clear_walk(struct snd_soc_codec *codec) | |||
490 | p->walked = 0; | 431 | p->walked = 0; |
491 | } | 432 | } |
492 | 433 | ||
434 | /* We implement power down on suspend by checking the power state of | ||
435 | * the ALSA card - when we are suspending the ALSA state for the card | ||
436 | * is set to D3. | ||
437 | */ | ||
438 | static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) | ||
439 | { | ||
440 | struct snd_soc_codec *codec = widget->codec; | ||
441 | |||
442 | switch (snd_power_get_state(codec->card)) { | ||
443 | case SNDRV_CTL_POWER_D3hot: | ||
444 | case SNDRV_CTL_POWER_D3cold: | ||
445 | if (widget->ignore_suspend) | ||
446 | pr_debug("%s ignoring suspend\n", widget->name); | ||
447 | return widget->ignore_suspend; | ||
448 | default: | ||
449 | return 1; | ||
450 | } | ||
451 | } | ||
452 | |||
493 | /* | 453 | /* |
494 | * Recursively check for a completed path to an active or physically connected | 454 | * Recursively check for a completed path to an active or physically connected |
495 | * output widget. Returns number of complete paths. | 455 | * output widget. Returns number of complete paths. |
@@ -506,7 +466,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) | |||
506 | case snd_soc_dapm_adc: | 466 | case snd_soc_dapm_adc: |
507 | case snd_soc_dapm_aif_out: | 467 | case snd_soc_dapm_aif_out: |
508 | if (widget->active) | 468 | if (widget->active) |
509 | return 1; | 469 | return snd_soc_dapm_suspend_check(widget); |
510 | default: | 470 | default: |
511 | break; | 471 | break; |
512 | } | 472 | } |
@@ -514,12 +474,12 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) | |||
514 | if (widget->connected) { | 474 | if (widget->connected) { |
515 | /* connected pin ? */ | 475 | /* connected pin ? */ |
516 | if (widget->id == snd_soc_dapm_output && !widget->ext) | 476 | if (widget->id == snd_soc_dapm_output && !widget->ext) |
517 | return 1; | 477 | return snd_soc_dapm_suspend_check(widget); |
518 | 478 | ||
519 | /* connected jack or spk ? */ | 479 | /* connected jack or spk ? */ |
520 | if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk || | 480 | if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk || |
521 | (widget->id == snd_soc_dapm_line && !list_empty(&widget->sources))) | 481 | (widget->id == snd_soc_dapm_line && !list_empty(&widget->sources))) |
522 | return 1; | 482 | return snd_soc_dapm_suspend_check(widget); |
523 | } | 483 | } |
524 | 484 | ||
525 | list_for_each_entry(path, &widget->sinks, list_source) { | 485 | list_for_each_entry(path, &widget->sinks, list_source) { |
@@ -552,7 +512,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) | |||
552 | case snd_soc_dapm_dac: | 512 | case snd_soc_dapm_dac: |
553 | case snd_soc_dapm_aif_in: | 513 | case snd_soc_dapm_aif_in: |
554 | if (widget->active) | 514 | if (widget->active) |
555 | return 1; | 515 | return snd_soc_dapm_suspend_check(widget); |
556 | default: | 516 | default: |
557 | break; | 517 | break; |
558 | } | 518 | } |
@@ -560,16 +520,16 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) | |||
560 | if (widget->connected) { | 520 | if (widget->connected) { |
561 | /* connected pin ? */ | 521 | /* connected pin ? */ |
562 | if (widget->id == snd_soc_dapm_input && !widget->ext) | 522 | if (widget->id == snd_soc_dapm_input && !widget->ext) |
563 | return 1; | 523 | return snd_soc_dapm_suspend_check(widget); |
564 | 524 | ||
565 | /* connected VMID/Bias for lower pops */ | 525 | /* connected VMID/Bias for lower pops */ |
566 | if (widget->id == snd_soc_dapm_vmid) | 526 | if (widget->id == snd_soc_dapm_vmid) |
567 | return 1; | 527 | return snd_soc_dapm_suspend_check(widget); |
568 | 528 | ||
569 | /* connected jack ? */ | 529 | /* connected jack ? */ |
570 | if (widget->id == snd_soc_dapm_mic || | 530 | if (widget->id == snd_soc_dapm_mic || |
571 | (widget->id == snd_soc_dapm_line && !list_empty(&widget->sinks))) | 531 | (widget->id == snd_soc_dapm_line && !list_empty(&widget->sinks))) |
572 | return 1; | 532 | return snd_soc_dapm_suspend_check(widget); |
573 | } | 533 | } |
574 | 534 | ||
575 | list_for_each_entry(path, &widget->sources, list_sink) { | 535 | list_for_each_entry(path, &widget->sources, list_sink) { |
@@ -634,16 +594,8 @@ static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w) | |||
634 | return ret; | 594 | return ret; |
635 | } | 595 | } |
636 | 596 | ||
637 | /* Lower PGA volume to reduce pops */ | ||
638 | if (w->id == snd_soc_dapm_pga && !w->power) | ||
639 | dapm_set_pga(w, w->power); | ||
640 | |||
641 | dapm_update_bits(w); | 597 | dapm_update_bits(w); |
642 | 598 | ||
643 | /* Raise PGA volume to reduce pops */ | ||
644 | if (w->id == snd_soc_dapm_pga && w->power) | ||
645 | dapm_set_pga(w, w->power); | ||
646 | |||
647 | /* power up post event */ | 599 | /* power up post event */ |
648 | if (w->power && w->event && | 600 | if (w->power && w->event && |
649 | (w->event_flags & SND_SOC_DAPM_POST_PMU)) { | 601 | (w->event_flags & SND_SOC_DAPM_POST_PMU)) { |
@@ -810,10 +762,6 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec, | |||
810 | pr_err("%s: pre event failed: %d\n", | 762 | pr_err("%s: pre event failed: %d\n", |
811 | w->name, ret); | 763 | w->name, ret); |
812 | } | 764 | } |
813 | |||
814 | /* Lower PGA volume to reduce pops */ | ||
815 | if (w->id == snd_soc_dapm_pga && !w->power) | ||
816 | dapm_set_pga(w, w->power); | ||
817 | } | 765 | } |
818 | 766 | ||
819 | if (reg >= 0) { | 767 | if (reg >= 0) { |
@@ -825,10 +773,6 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec, | |||
825 | } | 773 | } |
826 | 774 | ||
827 | list_for_each_entry(w, pending, power_list) { | 775 | list_for_each_entry(w, pending, power_list) { |
828 | /* Raise PGA volume to reduce pops */ | ||
829 | if (w->id == snd_soc_dapm_pga && w->power) | ||
830 | dapm_set_pga(w, w->power); | ||
831 | |||
832 | /* power up post event */ | 776 | /* power up post event */ |
833 | if (w->power && w->event && | 777 | if (w->power && w->event && |
834 | (w->event_flags & SND_SOC_DAPM_POST_PMU)) { | 778 | (w->event_flags & SND_SOC_DAPM_POST_PMU)) { |
@@ -973,19 +917,12 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | |||
973 | if (!w->power_check) | 917 | if (!w->power_check) |
974 | continue; | 918 | continue; |
975 | 919 | ||
976 | /* If we're suspending then pull down all the | 920 | if (!w->force) |
977 | * power. */ | ||
978 | switch (event) { | ||
979 | case SND_SOC_DAPM_STREAM_SUSPEND: | ||
980 | power = 0; | ||
981 | break; | ||
982 | |||
983 | default: | ||
984 | power = w->power_check(w); | 921 | power = w->power_check(w); |
985 | if (power) | 922 | else |
986 | sys_power = 1; | 923 | power = 1; |
987 | break; | 924 | if (power) |
988 | } | 925 | sys_power = 1; |
989 | 926 | ||
990 | if (w->power == power) | 927 | if (w->power == power) |
991 | continue; | 928 | continue; |
@@ -1076,6 +1013,7 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | |||
1076 | 1013 | ||
1077 | pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n", | 1014 | pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n", |
1078 | codec->pop_time); | 1015 | codec->pop_time); |
1016 | pop_wait(codec->pop_time); | ||
1079 | 1017 | ||
1080 | return 0; | 1018 | return 0; |
1081 | } | 1019 | } |
@@ -1338,6 +1276,9 @@ static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec, | |||
1338 | if (!strcmp(w->name, pin)) { | 1276 | if (!strcmp(w->name, pin)) { |
1339 | pr_debug("dapm: %s: pin %s\n", codec->name, pin); | 1277 | pr_debug("dapm: %s: pin %s\n", codec->name, pin); |
1340 | w->connected = status; | 1278 | w->connected = status; |
1279 | /* Allow disabling of forced pins */ | ||
1280 | if (status == 0) | ||
1281 | w->force = 0; | ||
1341 | return 0; | 1282 | return 0; |
1342 | } | 1283 | } |
1343 | } | 1284 | } |
@@ -1594,12 +1535,6 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, | |||
1594 | unsigned int invert = mc->invert; | 1535 | unsigned int invert = mc->invert; |
1595 | unsigned int mask = (1 << fls(max)) - 1; | 1536 | unsigned int mask = (1 << fls(max)) - 1; |
1596 | 1537 | ||
1597 | /* return the saved value if we are powered down */ | ||
1598 | if (widget->id == snd_soc_dapm_pga && !widget->power) { | ||
1599 | ucontrol->value.integer.value[0] = widget->saved_value; | ||
1600 | return 0; | ||
1601 | } | ||
1602 | |||
1603 | ucontrol->value.integer.value[0] = | 1538 | ucontrol->value.integer.value[0] = |
1604 | (snd_soc_read(widget->codec, reg) >> shift) & mask; | 1539 | (snd_soc_read(widget->codec, reg) >> shift) & mask; |
1605 | if (shift != rshift) | 1540 | if (shift != rshift) |
@@ -1659,13 +1594,6 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, | |||
1659 | mutex_lock(&widget->codec->mutex); | 1594 | mutex_lock(&widget->codec->mutex); |
1660 | widget->value = val; | 1595 | widget->value = val; |
1661 | 1596 | ||
1662 | /* save volume value if the widget is powered down */ | ||
1663 | if (widget->id == snd_soc_dapm_pga && !widget->power) { | ||
1664 | widget->saved_value = val; | ||
1665 | mutex_unlock(&widget->codec->mutex); | ||
1666 | return 1; | ||
1667 | } | ||
1668 | |||
1669 | if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) { | 1597 | if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) { |
1670 | if (val) | 1598 | if (val) |
1671 | /* new connection */ | 1599 | /* new connection */ |
@@ -2094,18 +2022,8 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, | |||
2094 | w->active = 0; | 2022 | w->active = 0; |
2095 | break; | 2023 | break; |
2096 | case SND_SOC_DAPM_STREAM_SUSPEND: | 2024 | case SND_SOC_DAPM_STREAM_SUSPEND: |
2097 | if (w->active) | ||
2098 | w->suspend = 1; | ||
2099 | w->active = 0; | ||
2100 | break; | ||
2101 | case SND_SOC_DAPM_STREAM_RESUME: | 2025 | case SND_SOC_DAPM_STREAM_RESUME: |
2102 | if (w->suspend) { | ||
2103 | w->active = 1; | ||
2104 | w->suspend = 0; | ||
2105 | } | ||
2106 | break; | ||
2107 | case SND_SOC_DAPM_STREAM_PAUSE_PUSH: | 2026 | case SND_SOC_DAPM_STREAM_PAUSE_PUSH: |
2108 | break; | ||
2109 | case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: | 2027 | case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: |
2110 | break; | 2028 | break; |
2111 | } | 2029 | } |
@@ -2135,6 +2053,36 @@ int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin) | |||
2135 | EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); | 2053 | EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); |
2136 | 2054 | ||
2137 | /** | 2055 | /** |
2056 | * snd_soc_dapm_force_enable_pin - force a pin to be enabled | ||
2057 | * @codec: SoC codec | ||
2058 | * @pin: pin name | ||
2059 | * | ||
2060 | * Enables input/output pin regardless of any other state. This is | ||
2061 | * intended for use with microphone bias supplies used in microphone | ||
2062 | * jack detection. | ||
2063 | * | ||
2064 | * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to | ||
2065 | * do any widget power switching. | ||
2066 | */ | ||
2067 | int snd_soc_dapm_force_enable_pin(struct snd_soc_codec *codec, const char *pin) | ||
2068 | { | ||
2069 | struct snd_soc_dapm_widget *w; | ||
2070 | |||
2071 | list_for_each_entry(w, &codec->dapm_widgets, list) { | ||
2072 | if (!strcmp(w->name, pin)) { | ||
2073 | pr_debug("dapm: %s: pin %s\n", codec->name, pin); | ||
2074 | w->connected = 1; | ||
2075 | w->force = 1; | ||
2076 | return 0; | ||
2077 | } | ||
2078 | } | ||
2079 | |||
2080 | pr_err("dapm: %s: configuring unknown pin %s\n", codec->name, pin); | ||
2081 | return -EINVAL; | ||
2082 | } | ||
2083 | EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin); | ||
2084 | |||
2085 | /** | ||
2138 | * snd_soc_dapm_disable_pin - disable pin. | 2086 | * snd_soc_dapm_disable_pin - disable pin. |
2139 | * @codec: SoC codec | 2087 | * @codec: SoC codec |
2140 | * @pin: pin name | 2088 | * @pin: pin name |
@@ -2192,6 +2140,33 @@ int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin) | |||
2192 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status); | 2140 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status); |
2193 | 2141 | ||
2194 | /** | 2142 | /** |
2143 | * snd_soc_dapm_ignore_suspend - ignore suspend status for DAPM endpoint | ||
2144 | * @codec: audio codec | ||
2145 | * @pin: audio signal pin endpoint (or start point) | ||
2146 | * | ||
2147 | * Mark the given endpoint or pin as ignoring suspend. When the | ||
2148 | * system is disabled a path between two endpoints flagged as ignoring | ||
2149 | * suspend will not be disabled. The path must already be enabled via | ||
2150 | * normal means at suspend time, it will not be turned on if it was not | ||
2151 | * already enabled. | ||
2152 | */ | ||
2153 | int snd_soc_dapm_ignore_suspend(struct snd_soc_codec *codec, const char *pin) | ||
2154 | { | ||
2155 | struct snd_soc_dapm_widget *w; | ||
2156 | |||
2157 | list_for_each_entry(w, &codec->dapm_widgets, list) { | ||
2158 | if (!strcmp(w->name, pin)) { | ||
2159 | w->ignore_suspend = 1; | ||
2160 | return 0; | ||
2161 | } | ||
2162 | } | ||
2163 | |||
2164 | pr_err("Unknown DAPM pin: %s\n", pin); | ||
2165 | return -EINVAL; | ||
2166 | } | ||
2167 | EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); | ||
2168 | |||
2169 | /** | ||
2195 | * snd_soc_dapm_free - free dapm resources | 2170 | * snd_soc_dapm_free - free dapm resources |
2196 | * @socdev: SoC device | 2171 | * @socdev: SoC device |
2197 | * | 2172 | * |