diff options
author | Peter Ujfalusi <peter.ujfalusi@nokia.com> | 2008-11-24 06:49:38 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2008-11-24 09:05:40 -0500 |
commit | b0bd53a7399f65e2d1b37cd44c5003e55b886c1e (patch) | |
tree | b79033f663fd326322c7c91b01c57724f6ee4c7d /sound/soc/codecs | |
parent | 0d33ea0b0f954dddd3996597c663c111249d4df9 (diff) |
ASoC: TWL4030: Add helper function for output gain controls
Some of the gain controls in TWL (mostly those which are associated with
the outputs) are implemented in an interesting way:
0x0 : Power down (mute)
0x1 : 6dB
0x2 : 0 dB
0x3 : -6 dB
Inverting not going to help with these.
Custom volsw and volsw_2r get/put functions to handle these gains.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r-- | sound/soc/codecs/twl4030.c | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 91effd341c0b..413623147891 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
@@ -191,6 +191,163 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) | |||
191 | } | 191 | } |
192 | 192 | ||
193 | /* | 193 | /* |
194 | * Some of the gain controls in TWL (mostly those which are associated with | ||
195 | * the outputs) are implemented in an interesting way: | ||
196 | * 0x0 : Power down (mute) | ||
197 | * 0x1 : 6dB | ||
198 | * 0x2 : 0 dB | ||
199 | * 0x3 : -6 dB | ||
200 | * Inverting not going to help with these. | ||
201 | * Custom volsw and volsw_2r get/put functions to handle these gain bits. | ||
202 | */ | ||
203 | #define SOC_DOUBLE_TLV_TWL4030(xname, xreg, shift_left, shift_right, xmax,\ | ||
204 | xinvert, tlv_array) \ | ||
205 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ | ||
206 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | ||
207 | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ | ||
208 | .tlv.p = (tlv_array), \ | ||
209 | .info = snd_soc_info_volsw, \ | ||
210 | .get = snd_soc_get_volsw_twl4030, \ | ||
211 | .put = snd_soc_put_volsw_twl4030, \ | ||
212 | .private_value = (unsigned long)&(struct soc_mixer_control) \ | ||
213 | {.reg = xreg, .shift = shift_left, .rshift = shift_right,\ | ||
214 | .max = xmax, .invert = xinvert} } | ||
215 | #define SOC_DOUBLE_R_TLV_TWL4030(xname, reg_left, reg_right, xshift, xmax,\ | ||
216 | xinvert, tlv_array) \ | ||
217 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ | ||
218 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | ||
219 | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ | ||
220 | .tlv.p = (tlv_array), \ | ||
221 | .info = snd_soc_info_volsw_2r, \ | ||
222 | .get = snd_soc_get_volsw_r2_twl4030,\ | ||
223 | .put = snd_soc_put_volsw_r2_twl4030, \ | ||
224 | .private_value = (unsigned long)&(struct soc_mixer_control) \ | ||
225 | {.reg = reg_left, .rreg = reg_right, .shift = xshift, \ | ||
226 | .max = xmax, .invert = xinvert} } | ||
227 | #define SOC_SINGLE_TLV_TWL4030(xname, xreg, xshift, xmax, xinvert, tlv_array) \ | ||
228 | SOC_DOUBLE_TLV_TWL4030(xname, xreg, xshift, xshift, xmax, \ | ||
229 | xinvert, tlv_array) | ||
230 | |||
231 | static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol, | ||
232 | struct snd_ctl_elem_value *ucontrol) | ||
233 | { | ||
234 | struct soc_mixer_control *mc = | ||
235 | (struct soc_mixer_control *)kcontrol->private_value; | ||
236 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
237 | unsigned int reg = mc->reg; | ||
238 | unsigned int shift = mc->shift; | ||
239 | unsigned int rshift = mc->rshift; | ||
240 | int max = mc->max; | ||
241 | int mask = (1 << fls(max)) - 1; | ||
242 | |||
243 | ucontrol->value.integer.value[0] = | ||
244 | (snd_soc_read(codec, reg) >> shift) & mask; | ||
245 | if (ucontrol->value.integer.value[0]) | ||
246 | ucontrol->value.integer.value[0] = | ||
247 | max + 1 - ucontrol->value.integer.value[0]; | ||
248 | |||
249 | if (shift != rshift) { | ||
250 | ucontrol->value.integer.value[1] = | ||
251 | (snd_soc_read(codec, reg) >> rshift) & mask; | ||
252 | if (ucontrol->value.integer.value[1]) | ||
253 | ucontrol->value.integer.value[1] = | ||
254 | max + 1 - ucontrol->value.integer.value[1]; | ||
255 | } | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol, | ||
261 | struct snd_ctl_elem_value *ucontrol) | ||
262 | { | ||
263 | struct soc_mixer_control *mc = | ||
264 | (struct soc_mixer_control *)kcontrol->private_value; | ||
265 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
266 | unsigned int reg = mc->reg; | ||
267 | unsigned int shift = mc->shift; | ||
268 | unsigned int rshift = mc->rshift; | ||
269 | int max = mc->max; | ||
270 | int mask = (1 << fls(max)) - 1; | ||
271 | unsigned short val, val2, val_mask; | ||
272 | |||
273 | val = (ucontrol->value.integer.value[0] & mask); | ||
274 | |||
275 | val_mask = mask << shift; | ||
276 | if (val) | ||
277 | val = max + 1 - val; | ||
278 | val = val << shift; | ||
279 | if (shift != rshift) { | ||
280 | val2 = (ucontrol->value.integer.value[1] & mask); | ||
281 | val_mask |= mask << rshift; | ||
282 | if (val2) | ||
283 | val2 = max + 1 - val2; | ||
284 | val |= val2 << rshift; | ||
285 | } | ||
286 | return snd_soc_update_bits(codec, reg, val_mask, val); | ||
287 | } | ||
288 | |||
289 | static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, | ||
290 | struct snd_ctl_elem_value *ucontrol) | ||
291 | { | ||
292 | struct soc_mixer_control *mc = | ||
293 | (struct soc_mixer_control *)kcontrol->private_value; | ||
294 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
295 | unsigned int reg = mc->reg; | ||
296 | unsigned int reg2 = mc->rreg; | ||
297 | unsigned int shift = mc->shift; | ||
298 | int max = mc->max; | ||
299 | int mask = (1<<fls(max))-1; | ||
300 | |||
301 | ucontrol->value.integer.value[0] = | ||
302 | (snd_soc_read(codec, reg) >> shift) & mask; | ||
303 | ucontrol->value.integer.value[1] = | ||
304 | (snd_soc_read(codec, reg2) >> shift) & mask; | ||
305 | |||
306 | if (ucontrol->value.integer.value[0]) | ||
307 | ucontrol->value.integer.value[0] = | ||
308 | max + 1 - ucontrol->value.integer.value[0]; | ||
309 | if (ucontrol->value.integer.value[1]) | ||
310 | ucontrol->value.integer.value[1] = | ||
311 | max + 1 - ucontrol->value.integer.value[1]; | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, | ||
317 | struct snd_ctl_elem_value *ucontrol) | ||
318 | { | ||
319 | struct soc_mixer_control *mc = | ||
320 | (struct soc_mixer_control *)kcontrol->private_value; | ||
321 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
322 | unsigned int reg = mc->reg; | ||
323 | unsigned int reg2 = mc->rreg; | ||
324 | unsigned int shift = mc->shift; | ||
325 | int max = mc->max; | ||
326 | int mask = (1 << fls(max)) - 1; | ||
327 | int err; | ||
328 | unsigned short val, val2, val_mask; | ||
329 | |||
330 | val_mask = mask << shift; | ||
331 | val = (ucontrol->value.integer.value[0] & mask); | ||
332 | val2 = (ucontrol->value.integer.value[1] & mask); | ||
333 | |||
334 | if (val) | ||
335 | val = max + 1 - val; | ||
336 | if (val2) | ||
337 | val2 = max + 1 - val2; | ||
338 | |||
339 | val = val << shift; | ||
340 | val2 = val2 << shift; | ||
341 | |||
342 | err = snd_soc_update_bits(codec, reg, val_mask, val); | ||
343 | if (err < 0) | ||
344 | return err; | ||
345 | |||
346 | err = snd_soc_update_bits(codec, reg2, val_mask, val2); | ||
347 | return err; | ||
348 | } | ||
349 | |||
350 | /* | ||
194 | * FGAIN volume control: | 351 | * FGAIN volume control: |
195 | * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) | 352 | * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) |
196 | */ | 353 | */ |