diff options
author | Adam Thomson <Adam.Thomson@diasemi.com> | 2012-05-31 10:18:01 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-06-03 08:06:41 -0400 |
commit | 6c9d8cf6372ed2995a3d982f5c1f966e842101cc (patch) | |
tree | 1a71ce7d37e0183e7e01968e64774711d444d10e | |
parent | bc92657a11c0982783979bbb84ceaf58ba222124 (diff) |
ASoC: core: Add single controls with specified range of values
Control type added for cases where a specific range of values
within a register are required for control.
Added convenience macros:
SOC_SINGLE_RANGE
SOC_SINGLE_RANGE_TLV
Added accessor implementations:
snd_soc_info_volsw_range
snd_soc_put_volsw_range
snd_soc_get_volsw_range
Signed-off-by: Michal Hajduk <Michal.Hajduk@diasemi.com>
Signed-off-by: Adam Thomson <Adam.Thomson@diasemi.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r-- | include/sound/soc.h | 23 | ||||
-rw-r--r-- | sound/soc/soc-core.c | 98 |
2 files changed, 121 insertions, 0 deletions
diff --git a/include/sound/soc.h b/include/sound/soc.h index 23c4efbe13a6..e4348d25fca3 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h | |||
@@ -47,6 +47,13 @@ | |||
47 | .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ | 47 | .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ |
48 | .put = snd_soc_put_volsw, \ | 48 | .put = snd_soc_put_volsw, \ |
49 | .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } | 49 | .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } |
50 | #define SOC_SINGLE_RANGE(xname, xreg, xshift, xmin, xmax, xinvert) \ | ||
51 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ | ||
52 | .info = snd_soc_info_volsw_range, .get = snd_soc_get_volsw_range, \ | ||
53 | .put = snd_soc_put_volsw_range, \ | ||
54 | .private_value = (unsigned long)&(struct soc_mixer_control) \ | ||
55 | {.reg = xreg, .shift = xshift, .min = xmin,\ | ||
56 | .max = xmax, .platform_max = xmax, .invert = xinvert} } | ||
50 | #define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ | 57 | #define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ |
51 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | 58 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ |
52 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | 59 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ |
@@ -67,6 +74,16 @@ | |||
67 | {.reg = xreg, .rreg = xreg, \ | 74 | {.reg = xreg, .rreg = xreg, \ |
68 | .shift = xshift, .rshift = xshift, \ | 75 | .shift = xshift, .rshift = xshift, \ |
69 | .max = xmax, .min = xmin} } | 76 | .max = xmax, .min = xmin} } |
77 | #define SOC_SINGLE_RANGE_TLV(xname, xreg, xshift, xmin, xmax, xinvert, tlv_array) \ | ||
78 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ | ||
79 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | ||
80 | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ | ||
81 | .tlv.p = (tlv_array), \ | ||
82 | .info = snd_soc_info_volsw_range, \ | ||
83 | .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \ | ||
84 | .private_value = (unsigned long)&(struct soc_mixer_control) \ | ||
85 | {.reg = xreg, .shift = xshift, .min = xmin,\ | ||
86 | .max = xmax, .platform_max = xmax, .invert = xinvert} } | ||
70 | #define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \ | 87 | #define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \ |
71 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ | 88 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ |
72 | .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ | 89 | .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ |
@@ -460,6 +477,12 @@ int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, | |||
460 | struct snd_ctl_elem_value *ucontrol); | 477 | struct snd_ctl_elem_value *ucontrol); |
461 | int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, | 478 | int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, |
462 | struct snd_ctl_elem_value *ucontrol); | 479 | struct snd_ctl_elem_value *ucontrol); |
480 | int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, | ||
481 | struct snd_ctl_elem_info *uinfo); | ||
482 | int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, | ||
483 | struct snd_ctl_elem_value *ucontrol); | ||
484 | int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, | ||
485 | struct snd_ctl_elem_value *ucontrol); | ||
463 | int snd_soc_limit_volume(struct snd_soc_codec *codec, | 486 | int snd_soc_limit_volume(struct snd_soc_codec *codec, |
464 | const char *name, int max); | 487 | const char *name, int max); |
465 | int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, | 488 | int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, |
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ec8350570346..3d803f3cd272 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
@@ -2792,6 +2792,104 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, | |||
2792 | EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); | 2792 | EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); |
2793 | 2793 | ||
2794 | /** | 2794 | /** |
2795 | * snd_soc_info_volsw_range - single mixer info callback with range. | ||
2796 | * @kcontrol: mixer control | ||
2797 | * @uinfo: control element information | ||
2798 | * | ||
2799 | * Callback to provide information, within a range, about a single | ||
2800 | * mixer control. | ||
2801 | * | ||
2802 | * returns 0 for success. | ||
2803 | */ | ||
2804 | int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, | ||
2805 | struct snd_ctl_elem_info *uinfo) | ||
2806 | { | ||
2807 | struct soc_mixer_control *mc = | ||
2808 | (struct soc_mixer_control *)kcontrol->private_value; | ||
2809 | int platform_max; | ||
2810 | int min = mc->min; | ||
2811 | |||
2812 | if (!mc->platform_max) | ||
2813 | mc->platform_max = mc->max; | ||
2814 | platform_max = mc->platform_max; | ||
2815 | |||
2816 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
2817 | uinfo->count = 1; | ||
2818 | uinfo->value.integer.min = 0; | ||
2819 | uinfo->value.integer.max = platform_max - min; | ||
2820 | |||
2821 | return 0; | ||
2822 | } | ||
2823 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range); | ||
2824 | |||
2825 | /** | ||
2826 | * snd_soc_put_volsw_range - single mixer put value callback with range. | ||
2827 | * @kcontrol: mixer control | ||
2828 | * @ucontrol: control element information | ||
2829 | * | ||
2830 | * Callback to set the value, within a range, for a single mixer control. | ||
2831 | * | ||
2832 | * Returns 0 for success. | ||
2833 | */ | ||
2834 | int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, | ||
2835 | struct snd_ctl_elem_value *ucontrol) | ||
2836 | { | ||
2837 | struct soc_mixer_control *mc = | ||
2838 | (struct soc_mixer_control *)kcontrol->private_value; | ||
2839 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
2840 | unsigned int reg = mc->reg; | ||
2841 | unsigned int shift = mc->shift; | ||
2842 | int min = mc->min; | ||
2843 | int max = mc->max; | ||
2844 | unsigned int mask = (1 << fls(max)) - 1; | ||
2845 | unsigned int invert = mc->invert; | ||
2846 | unsigned int val, val_mask; | ||
2847 | |||
2848 | val = ((ucontrol->value.integer.value[0] + min) & mask); | ||
2849 | if (invert) | ||
2850 | val = max - val; | ||
2851 | val_mask = mask << shift; | ||
2852 | val = val << shift; | ||
2853 | |||
2854 | return snd_soc_update_bits_locked(codec, reg, val_mask, val); | ||
2855 | } | ||
2856 | EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range); | ||
2857 | |||
2858 | /** | ||
2859 | * snd_soc_get_volsw_range - single mixer get callback with range | ||
2860 | * @kcontrol: mixer control | ||
2861 | * @ucontrol: control element information | ||
2862 | * | ||
2863 | * Callback to get the value, within a range, of a single mixer control. | ||
2864 | * | ||
2865 | * Returns 0 for success. | ||
2866 | */ | ||
2867 | int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, | ||
2868 | struct snd_ctl_elem_value *ucontrol) | ||
2869 | { | ||
2870 | struct soc_mixer_control *mc = | ||
2871 | (struct soc_mixer_control *)kcontrol->private_value; | ||
2872 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
2873 | unsigned int reg = mc->reg; | ||
2874 | unsigned int shift = mc->shift; | ||
2875 | int min = mc->min; | ||
2876 | int max = mc->max; | ||
2877 | unsigned int mask = (1 << fls(max)) - 1; | ||
2878 | unsigned int invert = mc->invert; | ||
2879 | |||
2880 | ucontrol->value.integer.value[0] = | ||
2881 | (snd_soc_read(codec, reg) >> shift) & mask; | ||
2882 | if (invert) | ||
2883 | ucontrol->value.integer.value[0] = | ||
2884 | max - ucontrol->value.integer.value[0]; | ||
2885 | ucontrol->value.integer.value[0] = | ||
2886 | ucontrol->value.integer.value[0] - min; | ||
2887 | |||
2888 | return 0; | ||
2889 | } | ||
2890 | EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); | ||
2891 | |||
2892 | /** | ||
2795 | * snd_soc_limit_volume - Set new limit to an existing volume control. | 2893 | * snd_soc_limit_volume - Set new limit to an existing volume control. |
2796 | * | 2894 | * |
2797 | * @codec: where to look for the control | 2895 | * @codec: where to look for the control |