aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/twl4030.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r--sound/soc/codecs/twl4030.c157
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
231static 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
260static 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
289static 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
316static 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 */