diff options
author | Peter Ujfalusi <peter.ujfalusi@nokia.com> | 2009-01-05 02:54:57 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-01-05 12:47:17 -0500 |
commit | 2e72f8e3716bc3bbf4c9b5b987fb5ab3223f60bf (patch) | |
tree | f568500ae1737e5c11376ad9b65d706c9da18874 | |
parent | 796123368871e4a838dc0dfd5dbc3cd8981ef429 (diff) |
ASoC: New enum type: value_enum
This patch introduces a new enum type.
In this enum type each enumerated items referred with a value.
This new enum type can handle enums encoded in bitfield, or any other
weird ways. twl4030 codec has several mux selection register, where the
input/output mux is coded in a bitfield. With the normal enum type this type
of mux can not be handled in a clean way.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r-- | include/sound/soc-dapm.h | 15 | ||||
-rw-r--r-- | include/sound/soc.h | 30 | ||||
-rw-r--r-- | sound/soc/soc-core.c | 107 | ||||
-rw-r--r-- | sound/soc/soc-dapm.c | 197 |
4 files changed, 346 insertions, 3 deletions
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 7ee2f70ca42e..4af1083e3287 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h | |||
@@ -85,6 +85,10 @@ | |||
85 | #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ | 85 | #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ |
86 | { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ | 86 | { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ |
87 | .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} | 87 | .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} |
88 | #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ | ||
89 | { .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \ | ||
90 | .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ | ||
91 | .num_kcontrols = 1} | ||
88 | 92 | ||
89 | /* path domain with event - event handler must return 0 for success */ | 93 | /* path domain with event - event handler must return 0 for success */ |
90 | #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ | 94 | #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ |
@@ -172,6 +176,12 @@ | |||
172 | .get = snd_soc_dapm_get_enum_double, \ | 176 | .get = snd_soc_dapm_get_enum_double, \ |
173 | .put = snd_soc_dapm_put_enum_double, \ | 177 | .put = snd_soc_dapm_put_enum_double, \ |
174 | .private_value = (unsigned long)&xenum } | 178 | .private_value = (unsigned long)&xenum } |
179 | #define SOC_DAPM_VALUE_ENUM(xname, xenum) \ | ||
180 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
181 | .info = snd_soc_info_value_enum_double, \ | ||
182 | .get = snd_soc_dapm_get_value_enum_double, \ | ||
183 | .put = snd_soc_dapm_put_value_enum_double, \ | ||
184 | .private_value = (unsigned long)&xenum } | ||
175 | 185 | ||
176 | /* dapm stream operations */ | 186 | /* dapm stream operations */ |
177 | #define SND_SOC_DAPM_STREAM_NOP 0x0 | 187 | #define SND_SOC_DAPM_STREAM_NOP 0x0 |
@@ -214,6 +224,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, | |||
214 | struct snd_ctl_elem_value *ucontrol); | 224 | struct snd_ctl_elem_value *ucontrol); |
215 | int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, | 225 | int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, |
216 | struct snd_ctl_elem_value *ucontrol); | 226 | struct snd_ctl_elem_value *ucontrol); |
227 | int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, | ||
228 | struct snd_ctl_elem_value *ucontrol); | ||
229 | int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, | ||
230 | struct snd_ctl_elem_value *ucontrol); | ||
217 | int snd_soc_dapm_new_control(struct snd_soc_codec *codec, | 231 | int snd_soc_dapm_new_control(struct snd_soc_codec *codec, |
218 | const struct snd_soc_dapm_widget *widget); | 232 | const struct snd_soc_dapm_widget *widget); |
219 | int snd_soc_dapm_new_controls(struct snd_soc_codec *codec, | 233 | int snd_soc_dapm_new_controls(struct snd_soc_codec *codec, |
@@ -247,6 +261,7 @@ enum snd_soc_dapm_type { | |||
247 | snd_soc_dapm_input = 0, /* input pin */ | 261 | snd_soc_dapm_input = 0, /* input pin */ |
248 | snd_soc_dapm_output, /* output pin */ | 262 | snd_soc_dapm_output, /* output pin */ |
249 | snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ | 263 | snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ |
264 | snd_soc_dapm_value_mux, /* selects 1 analog signal from many inputs */ | ||
250 | snd_soc_dapm_mixer, /* mixes several analog signals together */ | 265 | snd_soc_dapm_mixer, /* mixes several analog signals together */ |
251 | snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */ | 266 | snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */ |
252 | snd_soc_dapm_adc, /* analog to digital converter */ | 267 | snd_soc_dapm_adc, /* analog to digital converter */ |
diff --git a/include/sound/soc.h b/include/sound/soc.h index f86e455d3828..9b930d342116 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h | |||
@@ -94,11 +94,22 @@ | |||
94 | SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts) | 94 | SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts) |
95 | #define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \ | 95 | #define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \ |
96 | { .max = xmax, .texts = xtexts } | 96 | { .max = xmax, .texts = xtexts } |
97 | #define SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xmax, xtexts, xvalues) \ | ||
98 | { .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ | ||
99 | .mask = xmask, .max = xmax, .texts = xtexts, .values = xvalues} | ||
100 | #define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xmax, xtexts, xvalues) \ | ||
101 | SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xmax, xtexts, xvalues) | ||
97 | #define SOC_ENUM(xname, xenum) \ | 102 | #define SOC_ENUM(xname, xenum) \ |
98 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ | 103 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ |
99 | .info = snd_soc_info_enum_double, \ | 104 | .info = snd_soc_info_enum_double, \ |
100 | .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \ | 105 | .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \ |
101 | .private_value = (unsigned long)&xenum } | 106 | .private_value = (unsigned long)&xenum } |
107 | #define SOC_VALUE_ENUM(xname, xenum) \ | ||
108 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ | ||
109 | .info = snd_soc_info_value_enum_double, \ | ||
110 | .get = snd_soc_get_value_enum_double, \ | ||
111 | .put = snd_soc_put_value_enum_double, \ | ||
112 | .private_value = (unsigned long)&xenum } | ||
102 | #define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\ | 113 | #define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\ |
103 | xhandler_get, xhandler_put) \ | 114 | xhandler_get, xhandler_put) \ |
104 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | 115 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ |
@@ -200,6 +211,12 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, | |||
200 | struct snd_ctl_elem_value *ucontrol); | 211 | struct snd_ctl_elem_value *ucontrol); |
201 | int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, | 212 | int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, |
202 | struct snd_ctl_elem_value *ucontrol); | 213 | struct snd_ctl_elem_value *ucontrol); |
214 | int snd_soc_info_value_enum_double(struct snd_kcontrol *kcontrol, | ||
215 | struct snd_ctl_elem_info *uinfo); | ||
216 | int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol, | ||
217 | struct snd_ctl_elem_value *ucontrol); | ||
218 | int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, | ||
219 | struct snd_ctl_elem_value *ucontrol); | ||
203 | int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, | 220 | int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, |
204 | struct snd_ctl_elem_info *uinfo); | 221 | struct snd_ctl_elem_info *uinfo); |
205 | int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, | 222 | int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, |
@@ -406,6 +423,19 @@ struct soc_enum { | |||
406 | void *dapm; | 423 | void *dapm; |
407 | }; | 424 | }; |
408 | 425 | ||
426 | /* semi enumerated kcontrol */ | ||
427 | struct soc_value_enum { | ||
428 | unsigned short reg; | ||
429 | unsigned short reg2; | ||
430 | unsigned char shift_l; | ||
431 | unsigned char shift_r; | ||
432 | unsigned int max; | ||
433 | unsigned int mask; | ||
434 | const char **texts; | ||
435 | const unsigned int *values; | ||
436 | void *dapm; | ||
437 | }; | ||
438 | |||
409 | #include <sound/soc-dai.h> | 439 | #include <sound/soc-dai.h> |
410 | 440 | ||
411 | #endif | 441 | #endif |
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f73c1341437c..6cbe7e82f238 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
@@ -1585,6 +1585,113 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, | |||
1585 | EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); | 1585 | EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); |
1586 | 1586 | ||
1587 | /** | 1587 | /** |
1588 | * snd_soc_info_value_enum_double - semi enumerated double mixer info callback | ||
1589 | * @kcontrol: mixer control | ||
1590 | * @uinfo: control element information | ||
1591 | * | ||
1592 | * Callback to provide information about a double semi enumerated | ||
1593 | * mixer control. | ||
1594 | * | ||
1595 | * Semi enumerated mixer: the enumerated items are referred as values. Can be | ||
1596 | * used for handling bitfield coded enumeration for example. | ||
1597 | * | ||
1598 | * Returns 0 for success. | ||
1599 | */ | ||
1600 | int snd_soc_info_value_enum_double(struct snd_kcontrol *kcontrol, | ||
1601 | struct snd_ctl_elem_info *uinfo) | ||
1602 | { | ||
1603 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
1604 | kcontrol->private_value; | ||
1605 | |||
1606 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
1607 | uinfo->count = e->shift_l == e->shift_r ? 1 : 2; | ||
1608 | uinfo->value.enumerated.items = e->max; | ||
1609 | |||
1610 | if (uinfo->value.enumerated.item > e->max - 1) | ||
1611 | uinfo->value.enumerated.item = e->max - 1; | ||
1612 | strcpy(uinfo->value.enumerated.name, | ||
1613 | e->texts[uinfo->value.enumerated.item]); | ||
1614 | return 0; | ||
1615 | } | ||
1616 | EXPORT_SYMBOL_GPL(snd_soc_info_value_enum_double); | ||
1617 | |||
1618 | /** | ||
1619 | * snd_soc_get_value_enum_double - semi enumerated double mixer get callback | ||
1620 | * @kcontrol: mixer control | ||
1621 | * @ucontrol: control element information | ||
1622 | * | ||
1623 | * Callback to get the value of a double semi enumerated mixer. | ||
1624 | * | ||
1625 | * Semi enumerated mixer: the enumerated items are referred as values. Can be | ||
1626 | * used for handling bitfield coded enumeration for example. | ||
1627 | * | ||
1628 | * Returns 0 for success. | ||
1629 | */ | ||
1630 | int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol, | ||
1631 | struct snd_ctl_elem_value *ucontrol) | ||
1632 | { | ||
1633 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
1634 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
1635 | kcontrol->private_value; | ||
1636 | unsigned short reg_val, val, mux; | ||
1637 | |||
1638 | reg_val = snd_soc_read(codec, e->reg); | ||
1639 | val = (reg_val >> e->shift_l) & e->mask; | ||
1640 | for (mux = 0; mux < e->max; mux++) { | ||
1641 | if (val == e->values[mux]) | ||
1642 | break; | ||
1643 | } | ||
1644 | ucontrol->value.enumerated.item[0] = mux; | ||
1645 | if (e->shift_l != e->shift_r) { | ||
1646 | val = (reg_val >> e->shift_r) & e->mask; | ||
1647 | for (mux = 0; mux < e->max; mux++) { | ||
1648 | if (val == e->values[mux]) | ||
1649 | break; | ||
1650 | } | ||
1651 | ucontrol->value.enumerated.item[1] = mux; | ||
1652 | } | ||
1653 | |||
1654 | return 0; | ||
1655 | } | ||
1656 | EXPORT_SYMBOL_GPL(snd_soc_get_value_enum_double); | ||
1657 | |||
1658 | /** | ||
1659 | * snd_soc_put_value_enum_double - semi enumerated double mixer put callback | ||
1660 | * @kcontrol: mixer control | ||
1661 | * @ucontrol: control element information | ||
1662 | * | ||
1663 | * Callback to set the value of a double semi enumerated mixer. | ||
1664 | * | ||
1665 | * Semi enumerated mixer: the enumerated items are referred as values. Can be | ||
1666 | * used for handling bitfield coded enumeration for example. | ||
1667 | * | ||
1668 | * Returns 0 for success. | ||
1669 | */ | ||
1670 | int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, | ||
1671 | struct snd_ctl_elem_value *ucontrol) | ||
1672 | { | ||
1673 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
1674 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
1675 | kcontrol->private_value; | ||
1676 | unsigned short val; | ||
1677 | unsigned short mask; | ||
1678 | |||
1679 | if (ucontrol->value.enumerated.item[0] > e->max - 1) | ||
1680 | return -EINVAL; | ||
1681 | val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l; | ||
1682 | mask = e->mask << e->shift_l; | ||
1683 | if (e->shift_l != e->shift_r) { | ||
1684 | if (ucontrol->value.enumerated.item[1] > e->max - 1) | ||
1685 | return -EINVAL; | ||
1686 | val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r; | ||
1687 | mask |= e->mask << e->shift_r; | ||
1688 | } | ||
1689 | |||
1690 | return snd_soc_update_bits(codec, e->reg, mask, val); | ||
1691 | } | ||
1692 | EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); | ||
1693 | |||
1694 | /** | ||
1588 | * snd_soc_info_enum_ext - external enumerated single mixer info callback | 1695 | * snd_soc_info_enum_ext - external enumerated single mixer info callback |
1589 | * @kcontrol: mixer control | 1696 | * @kcontrol: mixer control |
1590 | * @uinfo: control element information | 1697 | * @uinfo: control element information |
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6c79ca6df0bf..ad0d801677c1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -53,13 +53,15 @@ | |||
53 | /* dapm power sequences - make this per codec in the future */ | 53 | /* dapm power sequences - make this per codec in the future */ |
54 | static int dapm_up_seq[] = { | 54 | static int dapm_up_seq[] = { |
55 | snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic, | 55 | snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic, |
56 | snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga, | 56 | snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac, |
57 | snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post | 57 | snd_soc_dapm_mixer, snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, |
58 | snd_soc_dapm_spk, snd_soc_dapm_post | ||
58 | }; | 59 | }; |
59 | static int dapm_down_seq[] = { | 60 | static int dapm_down_seq[] = { |
60 | snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, | 61 | snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, |
61 | snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic, | 62 | snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic, |
62 | snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post | 63 | snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_value_mux, |
64 | snd_soc_dapm_post | ||
63 | }; | 65 | }; |
64 | 66 | ||
65 | static int dapm_status = 1; | 67 | static int dapm_status = 1; |
@@ -134,6 +136,25 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, | |||
134 | } | 136 | } |
135 | } | 137 | } |
136 | break; | 138 | break; |
139 | case snd_soc_dapm_value_mux: { | ||
140 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
141 | w->kcontrols[i].private_value; | ||
142 | int val, item; | ||
143 | |||
144 | val = snd_soc_read(w->codec, e->reg); | ||
145 | val = (val >> e->shift_l) & e->mask; | ||
146 | for (item = 0; item < e->max; item++) { | ||
147 | if (val == e->values[item]) | ||
148 | break; | ||
149 | } | ||
150 | |||
151 | p->connect = 0; | ||
152 | for (i = 0; i < e->max; i++) { | ||
153 | if (!(strcmp(p->name, e->texts[i])) && item == i) | ||
154 | p->connect = 1; | ||
155 | } | ||
156 | } | ||
157 | break; | ||
137 | /* does not effect routing - always connected */ | 158 | /* does not effect routing - always connected */ |
138 | case snd_soc_dapm_pga: | 159 | case snd_soc_dapm_pga: |
139 | case snd_soc_dapm_output: | 160 | case snd_soc_dapm_output: |
@@ -179,6 +200,30 @@ static int dapm_connect_mux(struct snd_soc_codec *codec, | |||
179 | return -ENODEV; | 200 | return -ENODEV; |
180 | } | 201 | } |
181 | 202 | ||
203 | /* connect value_mux widget to it's interconnecting audio paths */ | ||
204 | static int dapm_connect_value_mux(struct snd_soc_codec *codec, | ||
205 | struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, | ||
206 | struct snd_soc_dapm_path *path, const char *control_name, | ||
207 | const struct snd_kcontrol_new *kcontrol) | ||
208 | { | ||
209 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
210 | kcontrol->private_value; | ||
211 | int i; | ||
212 | |||
213 | for (i = 0; i < e->max; i++) { | ||
214 | if (!(strcmp(control_name, e->texts[i]))) { | ||
215 | list_add(&path->list, &codec->dapm_paths); | ||
216 | list_add(&path->list_sink, &dest->sources); | ||
217 | list_add(&path->list_source, &src->sinks); | ||
218 | path->name = (char *)e->texts[i]; | ||
219 | dapm_set_path_status(dest, path, 0); | ||
220 | return 0; | ||
221 | } | ||
222 | } | ||
223 | |||
224 | return -ENODEV; | ||
225 | } | ||
226 | |||
182 | /* connect mixer widget to it's interconnecting audio paths */ | 227 | /* connect mixer widget to it's interconnecting audio paths */ |
183 | static int dapm_connect_mixer(struct snd_soc_codec *codec, | 228 | static int dapm_connect_mixer(struct snd_soc_codec *codec, |
184 | struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, | 229 | struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, |
@@ -653,6 +698,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action) | |||
653 | case snd_soc_dapm_vmid: | 698 | case snd_soc_dapm_vmid: |
654 | continue; | 699 | continue; |
655 | case snd_soc_dapm_mux: | 700 | case snd_soc_dapm_mux: |
701 | case snd_soc_dapm_value_mux: | ||
656 | case snd_soc_dapm_output: | 702 | case snd_soc_dapm_output: |
657 | case snd_soc_dapm_input: | 703 | case snd_soc_dapm_input: |
658 | case snd_soc_dapm_switch: | 704 | case snd_soc_dapm_switch: |
@@ -728,6 +774,45 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, | |||
728 | return 0; | 774 | return 0; |
729 | } | 775 | } |
730 | 776 | ||
777 | /* test and update the power status of a value_mux widget */ | ||
778 | static int dapm_value_mux_update_power(struct snd_soc_dapm_widget *widget, | ||
779 | struct snd_kcontrol *kcontrol, int mask, | ||
780 | int mux, int val, struct soc_value_enum *e) | ||
781 | { | ||
782 | struct snd_soc_dapm_path *path; | ||
783 | int found = 0; | ||
784 | |||
785 | if (widget->id != snd_soc_dapm_value_mux) | ||
786 | return -ENODEV; | ||
787 | |||
788 | if (!snd_soc_test_bits(widget->codec, e->reg, mask, val)) | ||
789 | return 0; | ||
790 | |||
791 | /* find dapm widget path assoc with kcontrol */ | ||
792 | list_for_each_entry(path, &widget->codec->dapm_paths, list) { | ||
793 | if (path->kcontrol != kcontrol) | ||
794 | continue; | ||
795 | |||
796 | if (!path->name || !e->texts[mux]) | ||
797 | continue; | ||
798 | |||
799 | found = 1; | ||
800 | /* we now need to match the string in the enum to the path */ | ||
801 | if (!(strcmp(path->name, e->texts[mux]))) | ||
802 | path->connect = 1; /* new connection */ | ||
803 | else | ||
804 | path->connect = 0; /* old connection must be | ||
805 | powered down */ | ||
806 | } | ||
807 | |||
808 | if (found) { | ||
809 | dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP); | ||
810 | dump_dapm(widget->codec, "mux power update"); | ||
811 | } | ||
812 | |||
813 | return 0; | ||
814 | } | ||
815 | |||
731 | /* test and update the power status of a mixer or switch widget */ | 816 | /* test and update the power status of a mixer or switch widget */ |
732 | static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, | 817 | static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, |
733 | struct snd_kcontrol *kcontrol, int reg, | 818 | struct snd_kcontrol *kcontrol, int reg, |
@@ -965,6 +1050,12 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, | |||
965 | if (ret != 0) | 1050 | if (ret != 0) |
966 | goto err; | 1051 | goto err; |
967 | break; | 1052 | break; |
1053 | case snd_soc_dapm_value_mux: | ||
1054 | ret = dapm_connect_value_mux(codec, wsource, wsink, path, | ||
1055 | control, &wsink->kcontrols[0]); | ||
1056 | if (ret != 0) | ||
1057 | goto err; | ||
1058 | break; | ||
968 | case snd_soc_dapm_switch: | 1059 | case snd_soc_dapm_switch: |
969 | case snd_soc_dapm_mixer: | 1060 | case snd_soc_dapm_mixer: |
970 | ret = dapm_connect_mixer(codec, wsource, wsink, path, control); | 1061 | ret = dapm_connect_mixer(codec, wsource, wsink, path, control); |
@@ -1047,6 +1138,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) | |||
1047 | dapm_new_mixer(codec, w); | 1138 | dapm_new_mixer(codec, w); |
1048 | break; | 1139 | break; |
1049 | case snd_soc_dapm_mux: | 1140 | case snd_soc_dapm_mux: |
1141 | case snd_soc_dapm_value_mux: | ||
1050 | dapm_new_mux(codec, w); | 1142 | dapm_new_mux(codec, w); |
1051 | break; | 1143 | break; |
1052 | case snd_soc_dapm_adc: | 1144 | case snd_soc_dapm_adc: |
@@ -1274,6 +1366,105 @@ out: | |||
1274 | EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); | 1366 | EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); |
1275 | 1367 | ||
1276 | /** | 1368 | /** |
1369 | * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get | ||
1370 | * callback | ||
1371 | * @kcontrol: mixer control | ||
1372 | * @ucontrol: control element information | ||
1373 | * | ||
1374 | * Callback to get the value of a dapm semi enumerated double mixer control. | ||
1375 | * | ||
1376 | * Semi enumerated mixer: the enumerated items are referred as values. Can be | ||
1377 | * used for handling bitfield coded enumeration for example. | ||
1378 | * | ||
1379 | * Returns 0 for success. | ||
1380 | */ | ||
1381 | int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, | ||
1382 | struct snd_ctl_elem_value *ucontrol) | ||
1383 | { | ||
1384 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); | ||
1385 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
1386 | kcontrol->private_value; | ||
1387 | unsigned short reg_val, val, mux; | ||
1388 | |||
1389 | reg_val = snd_soc_read(widget->codec, e->reg); | ||
1390 | val = (reg_val >> e->shift_l) & e->mask; | ||
1391 | for (mux = 0; mux < e->max; mux++) { | ||
1392 | if (val == e->values[mux]) | ||
1393 | break; | ||
1394 | } | ||
1395 | ucontrol->value.enumerated.item[0] = mux; | ||
1396 | if (e->shift_l != e->shift_r) { | ||
1397 | val = (reg_val >> e->shift_r) & e->mask; | ||
1398 | for (mux = 0; mux < e->max; mux++) { | ||
1399 | if (val == e->values[mux]) | ||
1400 | break; | ||
1401 | } | ||
1402 | ucontrol->value.enumerated.item[1] = mux; | ||
1403 | } | ||
1404 | |||
1405 | return 0; | ||
1406 | } | ||
1407 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double); | ||
1408 | |||
1409 | /** | ||
1410 | * snd_soc_dapm_put_value_enum_double - dapm semi enumerated double mixer set | ||
1411 | * callback | ||
1412 | * @kcontrol: mixer control | ||
1413 | * @ucontrol: control element information | ||
1414 | * | ||
1415 | * Callback to set the value of a dapm semi enumerated double mixer control. | ||
1416 | * | ||
1417 | * Semi enumerated mixer: the enumerated items are referred as values. Can be | ||
1418 | * used for handling bitfield coded enumeration for example. | ||
1419 | * | ||
1420 | * Returns 0 for success. | ||
1421 | */ | ||
1422 | int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, | ||
1423 | struct snd_ctl_elem_value *ucontrol) | ||
1424 | { | ||
1425 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); | ||
1426 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
1427 | kcontrol->private_value; | ||
1428 | unsigned short val, mux; | ||
1429 | unsigned short mask; | ||
1430 | int ret = 0; | ||
1431 | |||
1432 | if (ucontrol->value.enumerated.item[0] > e->max - 1) | ||
1433 | return -EINVAL; | ||
1434 | mux = ucontrol->value.enumerated.item[0]; | ||
1435 | val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l; | ||
1436 | mask = e->mask << e->shift_l; | ||
1437 | if (e->shift_l != e->shift_r) { | ||
1438 | if (ucontrol->value.enumerated.item[1] > e->max - 1) | ||
1439 | return -EINVAL; | ||
1440 | val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r; | ||
1441 | mask |= e->mask << e->shift_r; | ||
1442 | } | ||
1443 | |||
1444 | mutex_lock(&widget->codec->mutex); | ||
1445 | widget->value = val; | ||
1446 | dapm_value_mux_update_power(widget, kcontrol, mask, mux, val, e); | ||
1447 | if (widget->event) { | ||
1448 | if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { | ||
1449 | ret = widget->event(widget, | ||
1450 | kcontrol, SND_SOC_DAPM_PRE_REG); | ||
1451 | if (ret < 0) | ||
1452 | goto out; | ||
1453 | } | ||
1454 | ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); | ||
1455 | if (widget->event_flags & SND_SOC_DAPM_POST_REG) | ||
1456 | ret = widget->event(widget, | ||
1457 | kcontrol, SND_SOC_DAPM_POST_REG); | ||
1458 | } else | ||
1459 | ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); | ||
1460 | |||
1461 | out: | ||
1462 | mutex_unlock(&widget->codec->mutex); | ||
1463 | return ret; | ||
1464 | } | ||
1465 | EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); | ||
1466 | |||
1467 | /** | ||
1277 | * snd_soc_dapm_new_control - create new dapm control | 1468 | * snd_soc_dapm_new_control - create new dapm control |
1278 | * @codec: audio codec | 1469 | * @codec: audio codec |
1279 | * @widget: widget template | 1470 | * @widget: widget template |