diff options
-rw-r--r-- | sound/soc/soc-dapm.c | 141 |
1 files changed, 112 insertions, 29 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 32e7af9b93d5..27dd02e57b31 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -330,6 +330,11 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, | |||
330 | case snd_soc_dapm_mixer_named_ctl: | 330 | case snd_soc_dapm_mixer_named_ctl: |
331 | mc = (struct soc_mixer_control *)kcontrol->private_value; | 331 | mc = (struct soc_mixer_control *)kcontrol->private_value; |
332 | 332 | ||
333 | if (mc->autodisable && snd_soc_volsw_is_stereo(mc)) | ||
334 | dev_warn(widget->dapm->dev, | ||
335 | "ASoC: Unsupported stereo autodisable control '%s'\n", | ||
336 | ctrl_name); | ||
337 | |||
333 | if (mc->autodisable) { | 338 | if (mc->autodisable) { |
334 | struct snd_soc_dapm_widget template; | 339 | struct snd_soc_dapm_widget template; |
335 | 340 | ||
@@ -723,7 +728,8 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, | |||
723 | } | 728 | } |
724 | 729 | ||
725 | /* set up initial codec paths */ | 730 | /* set up initial codec paths */ |
726 | static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i) | 731 | static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i, |
732 | int nth_path) | ||
727 | { | 733 | { |
728 | struct soc_mixer_control *mc = (struct soc_mixer_control *) | 734 | struct soc_mixer_control *mc = (struct soc_mixer_control *) |
729 | p->sink->kcontrol_news[i].private_value; | 735 | p->sink->kcontrol_news[i].private_value; |
@@ -736,7 +742,25 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i) | |||
736 | 742 | ||
737 | if (reg != SND_SOC_NOPM) { | 743 | if (reg != SND_SOC_NOPM) { |
738 | soc_dapm_read(p->sink->dapm, reg, &val); | 744 | soc_dapm_read(p->sink->dapm, reg, &val); |
739 | val = (val >> shift) & mask; | 745 | /* |
746 | * The nth_path argument allows this function to know | ||
747 | * which path of a kcontrol it is setting the initial | ||
748 | * status for. Ideally this would support any number | ||
749 | * of paths and channels. But since kcontrols only come | ||
750 | * in mono and stereo variants, we are limited to 2 | ||
751 | * channels. | ||
752 | * | ||
753 | * The following code assumes for stereo controls the | ||
754 | * first path is the left channel, and all remaining | ||
755 | * paths are the right channel. | ||
756 | */ | ||
757 | if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) { | ||
758 | if (reg != mc->rreg) | ||
759 | soc_dapm_read(p->sink->dapm, mc->rreg, &val); | ||
760 | val = (val >> mc->rshift) & mask; | ||
761 | } else { | ||
762 | val = (val >> shift) & mask; | ||
763 | } | ||
740 | if (invert) | 764 | if (invert) |
741 | val = max - val; | 765 | val = max - val; |
742 | p->connect = !!val; | 766 | p->connect = !!val; |
@@ -749,13 +773,13 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i) | |||
749 | static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, | 773 | static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, |
750 | struct snd_soc_dapm_path *path, const char *control_name) | 774 | struct snd_soc_dapm_path *path, const char *control_name) |
751 | { | 775 | { |
752 | int i; | 776 | int i, nth_path = 0; |
753 | 777 | ||
754 | /* search for mixer kcontrol */ | 778 | /* search for mixer kcontrol */ |
755 | for (i = 0; i < path->sink->num_kcontrols; i++) { | 779 | for (i = 0; i < path->sink->num_kcontrols; i++) { |
756 | if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { | 780 | if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { |
757 | path->name = path->sink->kcontrol_news[i].name; | 781 | path->name = path->sink->kcontrol_news[i].name; |
758 | dapm_set_mixer_path_status(path, i); | 782 | dapm_set_mixer_path_status(path, i, nth_path++); |
759 | return 0; | 783 | return 0; |
760 | } | 784 | } |
761 | } | 785 | } |
@@ -2186,7 +2210,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); | |||
2186 | 2210 | ||
2187 | /* test and update the power status of a mixer or switch widget */ | 2211 | /* test and update the power status of a mixer or switch widget */ |
2188 | static int soc_dapm_mixer_update_power(struct snd_soc_card *card, | 2212 | static int soc_dapm_mixer_update_power(struct snd_soc_card *card, |
2189 | struct snd_kcontrol *kcontrol, int connect) | 2213 | struct snd_kcontrol *kcontrol, |
2214 | int connect, int rconnect) | ||
2190 | { | 2215 | { |
2191 | struct snd_soc_dapm_path *path; | 2216 | struct snd_soc_dapm_path *path; |
2192 | int found = 0; | 2217 | int found = 0; |
@@ -2195,8 +2220,33 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, | |||
2195 | 2220 | ||
2196 | /* find dapm widget path assoc with kcontrol */ | 2221 | /* find dapm widget path assoc with kcontrol */ |
2197 | dapm_kcontrol_for_each_path(path, kcontrol) { | 2222 | dapm_kcontrol_for_each_path(path, kcontrol) { |
2223 | /* | ||
2224 | * Ideally this function should support any number of | ||
2225 | * paths and channels. But since kcontrols only come | ||
2226 | * in mono and stereo variants, we are limited to 2 | ||
2227 | * channels. | ||
2228 | * | ||
2229 | * The following code assumes for stereo controls the | ||
2230 | * first path (when 'found == 0') is the left channel, | ||
2231 | * and all remaining paths (when 'found == 1') are the | ||
2232 | * right channel. | ||
2233 | * | ||
2234 | * A stereo control is signified by a valid 'rconnect' | ||
2235 | * value, either 0 for unconnected, or >= 0 for connected. | ||
2236 | * This is chosen instead of using snd_soc_volsw_is_stereo, | ||
2237 | * so that the behavior of snd_soc_dapm_mixer_update_power | ||
2238 | * doesn't change even when the kcontrol passed in is | ||
2239 | * stereo. | ||
2240 | * | ||
2241 | * It passes 'connect' as the path connect status for | ||
2242 | * the left channel, and 'rconnect' for the right | ||
2243 | * channel. | ||
2244 | */ | ||
2245 | if (found && rconnect >= 0) | ||
2246 | soc_dapm_connect_path(path, rconnect, "mixer update"); | ||
2247 | else | ||
2248 | soc_dapm_connect_path(path, connect, "mixer update"); | ||
2198 | found = 1; | 2249 | found = 1; |
2199 | soc_dapm_connect_path(path, connect, "mixer update"); | ||
2200 | } | 2250 | } |
2201 | 2251 | ||
2202 | if (found) | 2252 | if (found) |
@@ -2214,7 +2264,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, | |||
2214 | 2264 | ||
2215 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); | 2265 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); |
2216 | card->update = update; | 2266 | card->update = update; |
2217 | ret = soc_dapm_mixer_update_power(card, kcontrol, connect); | 2267 | ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1); |
2218 | card->update = NULL; | 2268 | card->update = NULL; |
2219 | mutex_unlock(&card->dapm_mutex); | 2269 | mutex_unlock(&card->dapm_mutex); |
2220 | if (ret > 0) | 2270 | if (ret > 0) |
@@ -3039,22 +3089,28 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, | |||
3039 | int reg = mc->reg; | 3089 | int reg = mc->reg; |
3040 | unsigned int shift = mc->shift; | 3090 | unsigned int shift = mc->shift; |
3041 | int max = mc->max; | 3091 | int max = mc->max; |
3092 | unsigned int width = fls(max); | ||
3042 | unsigned int mask = (1 << fls(max)) - 1; | 3093 | unsigned int mask = (1 << fls(max)) - 1; |
3043 | unsigned int invert = mc->invert; | 3094 | unsigned int invert = mc->invert; |
3044 | unsigned int val; | 3095 | unsigned int reg_val, val, rval = 0; |
3045 | int ret = 0; | 3096 | int ret = 0; |
3046 | 3097 | ||
3047 | if (snd_soc_volsw_is_stereo(mc)) | ||
3048 | dev_warn(dapm->dev, | ||
3049 | "ASoC: Control '%s' is stereo, which is not supported\n", | ||
3050 | kcontrol->id.name); | ||
3051 | |||
3052 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); | 3098 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); |
3053 | if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) { | 3099 | if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) { |
3054 | ret = soc_dapm_read(dapm, reg, &val); | 3100 | ret = soc_dapm_read(dapm, reg, ®_val); |
3055 | val = (val >> shift) & mask; | 3101 | val = (reg_val >> shift) & mask; |
3102 | |||
3103 | if (ret == 0 && reg != mc->rreg) | ||
3104 | ret = soc_dapm_read(dapm, mc->rreg, ®_val); | ||
3105 | |||
3106 | if (snd_soc_volsw_is_stereo(mc)) | ||
3107 | rval = (reg_val >> mc->rshift) & mask; | ||
3056 | } else { | 3108 | } else { |
3057 | val = dapm_kcontrol_get_value(kcontrol); | 3109 | reg_val = dapm_kcontrol_get_value(kcontrol); |
3110 | val = reg_val & mask; | ||
3111 | |||
3112 | if (snd_soc_volsw_is_stereo(mc)) | ||
3113 | rval = (reg_val >> width) & mask; | ||
3058 | } | 3114 | } |
3059 | mutex_unlock(&card->dapm_mutex); | 3115 | mutex_unlock(&card->dapm_mutex); |
3060 | 3116 | ||
@@ -3066,6 +3122,13 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, | |||
3066 | else | 3122 | else |
3067 | ucontrol->value.integer.value[0] = val; | 3123 | ucontrol->value.integer.value[0] = val; |
3068 | 3124 | ||
3125 | if (snd_soc_volsw_is_stereo(mc)) { | ||
3126 | if (invert) | ||
3127 | ucontrol->value.integer.value[1] = max - rval; | ||
3128 | else | ||
3129 | ucontrol->value.integer.value[1] = rval; | ||
3130 | } | ||
3131 | |||
3069 | return ret; | 3132 | return ret; |
3070 | } | 3133 | } |
3071 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); | 3134 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); |
@@ -3089,46 +3152,66 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, | |||
3089 | int reg = mc->reg; | 3152 | int reg = mc->reg; |
3090 | unsigned int shift = mc->shift; | 3153 | unsigned int shift = mc->shift; |
3091 | int max = mc->max; | 3154 | int max = mc->max; |
3092 | unsigned int mask = (1 << fls(max)) - 1; | 3155 | unsigned int width = fls(max); |
3156 | unsigned int mask = (1 << width) - 1; | ||
3093 | unsigned int invert = mc->invert; | 3157 | unsigned int invert = mc->invert; |
3094 | unsigned int val; | 3158 | unsigned int val, rval = 0; |
3095 | int connect, change, reg_change = 0; | 3159 | int connect, rconnect = -1, change, reg_change = 0; |
3096 | struct snd_soc_dapm_update update = { NULL }; | 3160 | struct snd_soc_dapm_update update = { NULL }; |
3097 | int ret = 0; | 3161 | int ret = 0; |
3098 | 3162 | ||
3099 | if (snd_soc_volsw_is_stereo(mc)) | ||
3100 | dev_warn(dapm->dev, | ||
3101 | "ASoC: Control '%s' is stereo, which is not supported\n", | ||
3102 | kcontrol->id.name); | ||
3103 | |||
3104 | val = (ucontrol->value.integer.value[0] & mask); | 3163 | val = (ucontrol->value.integer.value[0] & mask); |
3105 | connect = !!val; | 3164 | connect = !!val; |
3106 | 3165 | ||
3107 | if (invert) | 3166 | if (invert) |
3108 | val = max - val; | 3167 | val = max - val; |
3109 | 3168 | ||
3169 | if (snd_soc_volsw_is_stereo(mc)) { | ||
3170 | rval = (ucontrol->value.integer.value[1] & mask); | ||
3171 | rconnect = !!rval; | ||
3172 | if (invert) | ||
3173 | rval = max - rval; | ||
3174 | } | ||
3175 | |||
3110 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); | 3176 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); |
3111 | 3177 | ||
3112 | change = dapm_kcontrol_set_value(kcontrol, val); | 3178 | /* This assumes field width < (bits in unsigned int / 2) */ |
3179 | if (width > sizeof(unsigned int) * 8 / 2) | ||
3180 | dev_warn(dapm->dev, | ||
3181 | "ASoC: control %s field width limit exceeded\n", | ||
3182 | kcontrol->id.name); | ||
3183 | change = dapm_kcontrol_set_value(kcontrol, val | (rval << width)); | ||
3113 | 3184 | ||
3114 | if (reg != SND_SOC_NOPM) { | 3185 | if (reg != SND_SOC_NOPM) { |
3115 | mask = mask << shift; | ||
3116 | val = val << shift; | 3186 | val = val << shift; |
3187 | rval = rval << mc->rshift; | ||
3188 | |||
3189 | reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val); | ||
3117 | 3190 | ||
3118 | reg_change = soc_dapm_test_bits(dapm, reg, mask, val); | 3191 | if (snd_soc_volsw_is_stereo(mc)) |
3192 | reg_change |= soc_dapm_test_bits(dapm, mc->rreg, | ||
3193 | mask << mc->rshift, | ||
3194 | rval); | ||
3119 | } | 3195 | } |
3120 | 3196 | ||
3121 | if (change || reg_change) { | 3197 | if (change || reg_change) { |
3122 | if (reg_change) { | 3198 | if (reg_change) { |
3199 | if (snd_soc_volsw_is_stereo(mc)) { | ||
3200 | update.has_second_set = true; | ||
3201 | update.reg2 = mc->rreg; | ||
3202 | update.mask2 = mask << mc->rshift; | ||
3203 | update.val2 = rval; | ||
3204 | } | ||
3123 | update.kcontrol = kcontrol; | 3205 | update.kcontrol = kcontrol; |
3124 | update.reg = reg; | 3206 | update.reg = reg; |
3125 | update.mask = mask; | 3207 | update.mask = mask << shift; |
3126 | update.val = val; | 3208 | update.val = val; |
3127 | card->update = &update; | 3209 | card->update = &update; |
3128 | } | 3210 | } |
3129 | change |= reg_change; | 3211 | change |= reg_change; |
3130 | 3212 | ||
3131 | ret = soc_dapm_mixer_update_power(card, kcontrol, connect); | 3213 | ret = soc_dapm_mixer_update_power(card, kcontrol, connect, |
3214 | rconnect); | ||
3132 | 3215 | ||
3133 | card->update = NULL; | 3216 | card->update = NULL; |
3134 | } | 3217 | } |