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 |
