diff options
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 152 |
1 files changed, 84 insertions, 68 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f9d100bc8479..efbd0b37810a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -2,8 +2,7 @@ | |||
2 | * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management | 2 | * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management |
3 | * | 3 | * |
4 | * Copyright 2005 Wolfson Microelectronics PLC. | 4 | * Copyright 2005 Wolfson Microelectronics PLC. |
5 | * Author: Liam Girdwood | 5 | * Author: Liam Girdwood <lrg@slimlogic.co.uk> |
6 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
7 | * | 6 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 8 | * under the terms of the GNU General Public License as published by the |
@@ -38,6 +37,7 @@ | |||
38 | #include <linux/bitops.h> | 37 | #include <linux/bitops.h> |
39 | #include <linux/platform_device.h> | 38 | #include <linux/platform_device.h> |
40 | #include <linux/jiffies.h> | 39 | #include <linux/jiffies.h> |
40 | #include <linux/debugfs.h> | ||
41 | #include <sound/core.h> | 41 | #include <sound/core.h> |
42 | #include <sound/pcm.h> | 42 | #include <sound/pcm.h> |
43 | #include <sound/pcm_params.h> | 43 | #include <sound/pcm_params.h> |
@@ -67,7 +67,9 @@ static int dapm_status = 1; | |||
67 | module_param(dapm_status, int, 0); | 67 | module_param(dapm_status, int, 0); |
68 | MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries"); | 68 | MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries"); |
69 | 69 | ||
70 | static unsigned int pop_time; | 70 | static struct dentry *asoc_debugfs; |
71 | |||
72 | static u32 pop_time; | ||
71 | 73 | ||
72 | static void pop_wait(void) | 74 | static void pop_wait(void) |
73 | { | 75 | { |
@@ -104,10 +106,13 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, | |||
104 | case snd_soc_dapm_switch: | 106 | case snd_soc_dapm_switch: |
105 | case snd_soc_dapm_mixer: { | 107 | case snd_soc_dapm_mixer: { |
106 | int val; | 108 | int val; |
107 | int reg = w->kcontrols[i].private_value & 0xff; | 109 | struct soc_mixer_control *mc = (struct soc_mixer_control *) |
108 | int shift = (w->kcontrols[i].private_value >> 8) & 0x0f; | 110 | w->kcontrols[i].private_value; |
109 | int mask = (w->kcontrols[i].private_value >> 16) & 0xff; | 111 | unsigned int reg = mc->reg; |
110 | int invert = (w->kcontrols[i].private_value >> 24) & 0x01; | 112 | unsigned int shift = mc->shift; |
113 | int max = mc->max; | ||
114 | unsigned int mask = (1 << fls(max)) - 1; | ||
115 | unsigned int invert = mc->invert; | ||
111 | 116 | ||
112 | val = snd_soc_read(w->codec, reg); | 117 | val = snd_soc_read(w->codec, reg); |
113 | val = (val >> shift) & mask; | 118 | val = (val >> shift) & mask; |
@@ -122,13 +127,13 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, | |||
122 | struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; | 127 | struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; |
123 | int val, item, bitmask; | 128 | int val, item, bitmask; |
124 | 129 | ||
125 | for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) | 130 | for (bitmask = 1; bitmask < e->max; bitmask <<= 1) |
126 | ; | 131 | ; |
127 | val = snd_soc_read(w->codec, e->reg); | 132 | val = snd_soc_read(w->codec, e->reg); |
128 | item = (val >> e->shift_l) & (bitmask - 1); | 133 | item = (val >> e->shift_l) & (bitmask - 1); |
129 | 134 | ||
130 | p->connect = 0; | 135 | p->connect = 0; |
131 | for (i = 0; i < e->mask; i++) { | 136 | for (i = 0; i < e->max; i++) { |
132 | if (!(strcmp(p->name, e->texts[i])) && item == i) | 137 | if (!(strcmp(p->name, e->texts[i])) && item == i) |
133 | p->connect = 1; | 138 | p->connect = 1; |
134 | } | 139 | } |
@@ -165,7 +170,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec, | |||
165 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 170 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
166 | int i; | 171 | int i; |
167 | 172 | ||
168 | for (i = 0; i < e->mask; i++) { | 173 | for (i = 0; i < e->max; i++) { |
169 | if (!(strcmp(control_name, e->texts[i]))) { | 174 | if (!(strcmp(control_name, e->texts[i]))) { |
170 | list_add(&path->list, &codec->dapm_paths); | 175 | list_add(&path->list, &codec->dapm_paths); |
171 | list_add(&path->list_sink, &dest->sources); | 176 | list_add(&path->list_sink, &dest->sources); |
@@ -247,16 +252,19 @@ static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power) | |||
247 | return 0; | 252 | return 0; |
248 | 253 | ||
249 | if (widget->num_kcontrols && k) { | 254 | if (widget->num_kcontrols && k) { |
250 | int reg = k->private_value & 0xff; | 255 | struct soc_mixer_control *mc = |
251 | int shift = (k->private_value >> 8) & 0x0f; | 256 | (struct soc_mixer_control *)k->private_value; |
252 | int mask = (k->private_value >> 16) & 0xff; | 257 | unsigned int reg = mc->reg; |
253 | int invert = (k->private_value >> 24) & 0x01; | 258 | unsigned int shift = mc->shift; |
259 | int max = mc->max; | ||
260 | unsigned int mask = (1 << fls(max)) - 1; | ||
261 | unsigned int invert = mc->invert; | ||
254 | 262 | ||
255 | if (power) { | 263 | if (power) { |
256 | int i; | 264 | int i; |
257 | /* power up has happended, increase volume to last level */ | 265 | /* power up has happended, increase volume to last level */ |
258 | if (invert) { | 266 | if (invert) { |
259 | for (i = mask; i > widget->saved_value; i--) | 267 | for (i = max; i > widget->saved_value; i--) |
260 | snd_soc_update_bits(widget->codec, reg, mask, i); | 268 | snd_soc_update_bits(widget->codec, reg, mask, i); |
261 | } else { | 269 | } else { |
262 | for (i = 0; i < widget->saved_value; i++) | 270 | for (i = 0; i < widget->saved_value; i++) |
@@ -684,7 +692,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action) | |||
684 | /* test and update the power status of a mux widget */ | 692 | /* test and update the power status of a mux widget */ |
685 | static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, | 693 | static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, |
686 | struct snd_kcontrol *kcontrol, int mask, | 694 | struct snd_kcontrol *kcontrol, int mask, |
687 | int val, struct soc_enum* e) | 695 | int mux, int val, struct soc_enum *e) |
688 | { | 696 | { |
689 | struct snd_soc_dapm_path *path; | 697 | struct snd_soc_dapm_path *path; |
690 | int found = 0; | 698 | int found = 0; |
@@ -700,12 +708,12 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, | |||
700 | if (path->kcontrol != kcontrol) | 708 | if (path->kcontrol != kcontrol) |
701 | continue; | 709 | continue; |
702 | 710 | ||
703 | if (!path->name || ! e->texts[val]) | 711 | if (!path->name || !e->texts[mux]) |
704 | continue; | 712 | continue; |
705 | 713 | ||
706 | found = 1; | 714 | found = 1; |
707 | /* we now need to match the string in the enum to the path */ | 715 | /* we now need to match the string in the enum to the path */ |
708 | if (!(strcmp(path->name, e->texts[val]))) | 716 | if (!(strcmp(path->name, e->texts[mux]))) |
709 | path->connect = 1; /* new connection */ | 717 | path->connect = 1; /* new connection */ |
710 | else | 718 | else |
711 | path->connect = 0; /* old connection must be powered down */ | 719 | path->connect = 0; /* old connection must be powered down */ |
@@ -811,51 +819,35 @@ static ssize_t dapm_widget_show(struct device *dev, | |||
811 | 819 | ||
812 | static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL); | 820 | static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL); |
813 | 821 | ||
814 | /* pop/click delay times */ | ||
815 | static ssize_t dapm_pop_time_show(struct device *dev, | ||
816 | struct device_attribute *attr, char *buf) | ||
817 | { | ||
818 | return sprintf(buf, "%d\n", pop_time); | ||
819 | } | ||
820 | |||
821 | static ssize_t dapm_pop_time_store(struct device *dev, | ||
822 | struct device_attribute *attr, | ||
823 | const char *buf, size_t count) | ||
824 | |||
825 | { | ||
826 | unsigned long val; | ||
827 | |||
828 | if (strict_strtoul(buf, 10, &val) >= 0) | ||
829 | pop_time = val; | ||
830 | else | ||
831 | printk(KERN_ERR "Unable to parse pop_time setting\n"); | ||
832 | |||
833 | return count; | ||
834 | } | ||
835 | |||
836 | static DEVICE_ATTR(dapm_pop_time, 0744, dapm_pop_time_show, | ||
837 | dapm_pop_time_store); | ||
838 | |||
839 | int snd_soc_dapm_sys_add(struct device *dev) | 822 | int snd_soc_dapm_sys_add(struct device *dev) |
840 | { | 823 | { |
841 | int ret = 0; | 824 | int ret = 0; |
842 | 825 | ||
843 | if (dapm_status) { | 826 | if (!dapm_status) |
844 | ret = device_create_file(dev, &dev_attr_dapm_widget); | 827 | return 0; |
845 | 828 | ||
846 | if (ret == 0) | 829 | ret = device_create_file(dev, &dev_attr_dapm_widget); |
847 | ret = device_create_file(dev, &dev_attr_dapm_pop_time); | 830 | if (ret != 0) |
848 | } | 831 | return ret; |
849 | 832 | ||
850 | return ret; | 833 | asoc_debugfs = debugfs_create_dir("asoc", NULL); |
834 | if (!IS_ERR(asoc_debugfs)) | ||
835 | debugfs_create_u32("dapm_pop_time", 0744, asoc_debugfs, | ||
836 | &pop_time); | ||
837 | else | ||
838 | asoc_debugfs = NULL; | ||
839 | |||
840 | return 0; | ||
851 | } | 841 | } |
852 | 842 | ||
853 | static void snd_soc_dapm_sys_remove(struct device *dev) | 843 | static void snd_soc_dapm_sys_remove(struct device *dev) |
854 | { | 844 | { |
855 | if (dapm_status) { | 845 | if (dapm_status) { |
856 | device_remove_file(dev, &dev_attr_dapm_pop_time); | ||
857 | device_remove_file(dev, &dev_attr_dapm_widget); | 846 | device_remove_file(dev, &dev_attr_dapm_widget); |
858 | } | 847 | } |
848 | |||
849 | if (asoc_debugfs) | ||
850 | debugfs_remove_recursive(asoc_debugfs); | ||
859 | } | 851 | } |
860 | 852 | ||
861 | /* free all dapm widgets and resources */ | 853 | /* free all dapm widgets and resources */ |
@@ -1133,12 +1125,14 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, | |||
1133 | struct snd_ctl_elem_value *ucontrol) | 1125 | struct snd_ctl_elem_value *ucontrol) |
1134 | { | 1126 | { |
1135 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); | 1127 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); |
1136 | int reg = kcontrol->private_value & 0xff; | 1128 | struct soc_mixer_control *mc = |
1137 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1129 | (struct soc_mixer_control *)kcontrol->private_value; |
1138 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1130 | unsigned int reg = mc->reg; |
1139 | int max = (kcontrol->private_value >> 16) & 0xff; | 1131 | unsigned int shift = mc->shift; |
1140 | int invert = (kcontrol->private_value >> 24) & 0x01; | 1132 | unsigned int rshift = mc->rshift; |
1141 | int mask = (1 << fls(max)) - 1; | 1133 | int max = mc->max; |
1134 | unsigned int invert = mc->invert; | ||
1135 | unsigned int mask = (1 << fls(max)) - 1; | ||
1142 | 1136 | ||
1143 | /* return the saved value if we are powered down */ | 1137 | /* return the saved value if we are powered down */ |
1144 | if (widget->id == snd_soc_dapm_pga && !widget->power) { | 1138 | if (widget->id == snd_soc_dapm_pga && !widget->power) { |
@@ -1176,12 +1170,14 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, | |||
1176 | struct snd_ctl_elem_value *ucontrol) | 1170 | struct snd_ctl_elem_value *ucontrol) |
1177 | { | 1171 | { |
1178 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); | 1172 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); |
1179 | int reg = kcontrol->private_value & 0xff; | 1173 | struct soc_mixer_control *mc = |
1180 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1174 | (struct soc_mixer_control *)kcontrol->private_value; |
1181 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1175 | unsigned int reg = mc->reg; |
1182 | int max = (kcontrol->private_value >> 16) & 0xff; | 1176 | unsigned int shift = mc->shift; |
1183 | int mask = (1 << fls(max)) - 1; | 1177 | unsigned int rshift = mc->rshift; |
1184 | int invert = (kcontrol->private_value >> 24) & 0x01; | 1178 | int max = mc->max; |
1179 | unsigned int mask = (1 << fls(max)) - 1; | ||
1180 | unsigned int invert = mc->invert; | ||
1185 | unsigned short val, val2, val_mask; | 1181 | unsigned short val, val2, val_mask; |
1186 | int ret; | 1182 | int ret; |
1187 | 1183 | ||
@@ -1248,7 +1244,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, | |||
1248 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 1244 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
1249 | unsigned short val, bitmask; | 1245 | unsigned short val, bitmask; |
1250 | 1246 | ||
1251 | for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) | 1247 | for (bitmask = 1; bitmask < e->max; bitmask <<= 1) |
1252 | ; | 1248 | ; |
1253 | val = snd_soc_read(widget->codec, e->reg); | 1249 | val = snd_soc_read(widget->codec, e->reg); |
1254 | ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); | 1250 | ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); |
@@ -1278,15 +1274,15 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, | |||
1278 | unsigned short mask, bitmask; | 1274 | unsigned short mask, bitmask; |
1279 | int ret = 0; | 1275 | int ret = 0; |
1280 | 1276 | ||
1281 | for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) | 1277 | for (bitmask = 1; bitmask < e->max; bitmask <<= 1) |
1282 | ; | 1278 | ; |
1283 | if (ucontrol->value.enumerated.item[0] > e->mask - 1) | 1279 | if (ucontrol->value.enumerated.item[0] > e->max - 1) |
1284 | return -EINVAL; | 1280 | return -EINVAL; |
1285 | mux = ucontrol->value.enumerated.item[0]; | 1281 | mux = ucontrol->value.enumerated.item[0]; |
1286 | val = mux << e->shift_l; | 1282 | val = mux << e->shift_l; |
1287 | mask = (bitmask - 1) << e->shift_l; | 1283 | mask = (bitmask - 1) << e->shift_l; |
1288 | if (e->shift_l != e->shift_r) { | 1284 | if (e->shift_l != e->shift_r) { |
1289 | if (ucontrol->value.enumerated.item[1] > e->mask - 1) | 1285 | if (ucontrol->value.enumerated.item[1] > e->max - 1) |
1290 | return -EINVAL; | 1286 | return -EINVAL; |
1291 | val |= ucontrol->value.enumerated.item[1] << e->shift_r; | 1287 | val |= ucontrol->value.enumerated.item[1] << e->shift_r; |
1292 | mask |= (bitmask - 1) << e->shift_r; | 1288 | mask |= (bitmask - 1) << e->shift_r; |
@@ -1294,7 +1290,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, | |||
1294 | 1290 | ||
1295 | mutex_lock(&widget->codec->mutex); | 1291 | mutex_lock(&widget->codec->mutex); |
1296 | widget->value = val; | 1292 | widget->value = val; |
1297 | dapm_mux_update_power(widget, kcontrol, mask, mux, e); | 1293 | dapm_mux_update_power(widget, kcontrol, mask, mux, val, e); |
1298 | if (widget->event) { | 1294 | if (widget->event) { |
1299 | if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { | 1295 | if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { |
1300 | ret = widget->event(widget, | 1296 | ret = widget->event(widget, |
@@ -1487,6 +1483,26 @@ int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin) | |||
1487 | EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); | 1483 | EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); |
1488 | 1484 | ||
1489 | /** | 1485 | /** |
1486 | * snd_soc_dapm_nc_pin - permanently disable pin. | ||
1487 | * @codec: SoC codec | ||
1488 | * @pin: pin name | ||
1489 | * | ||
1490 | * Marks the specified pin as being not connected, disabling it along | ||
1491 | * any parent or child widgets. At present this is identical to | ||
1492 | * snd_soc_dapm_disable_pin() but in future it will be extended to do | ||
1493 | * additional things such as disabling controls which only affect | ||
1494 | * paths through the pin. | ||
1495 | * | ||
1496 | * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to | ||
1497 | * do any widget power switching. | ||
1498 | */ | ||
1499 | int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin) | ||
1500 | { | ||
1501 | return snd_soc_dapm_set_pin(codec, pin, 0); | ||
1502 | } | ||
1503 | EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); | ||
1504 | |||
1505 | /** | ||
1490 | * snd_soc_dapm_get_pin_status - get audio pin status | 1506 | * snd_soc_dapm_get_pin_status - get audio pin status |
1491 | * @codec: audio codec | 1507 | * @codec: audio codec |
1492 | * @pin: audio signal pin endpoint (or start point) | 1508 | * @pin: audio signal pin endpoint (or start point) |
@@ -1524,6 +1540,6 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev) | |||
1524 | EXPORT_SYMBOL_GPL(snd_soc_dapm_free); | 1540 | EXPORT_SYMBOL_GPL(snd_soc_dapm_free); |
1525 | 1541 | ||
1526 | /* Module information */ | 1542 | /* Module information */ |
1527 | MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); | 1543 | MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); |
1528 | MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); | 1544 | MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); |
1529 | MODULE_LICENSE("GPL"); | 1545 | MODULE_LICENSE("GPL"); |