From de67f4c0cf87d7e083745ef8809a64439d2920d7 Mon Sep 17 00:00:00 2001 From: Kristoffer KARLSSON Date: Mon, 10 Oct 2011 10:24:47 +0200 Subject: Ux500 ASoC: Add new generic ALSA Control types Added the definition and implementation for: * Signed multi register controls (1,2,4 and 8) * Signed array cache control * Strobe bit control Both multi register- and array control types have full support for signed longs (64bit) ST-Ericsson Linux next: NA ST-Ericsson ID: 325656 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I3041fe115bc349bfddc3870c160e9087f21f6197 Change-Id: I955b7248b1f81fbbd331cf41edc696c865d61758 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32987 Reviewed-by: Kristoffer KARLSSON Tested-by: Kristoffer KARLSSON Reviewed-by: QABUILD Reviewed-by: Roger NILSSON1 --- sound/soc/codecs/ab8500_audio.c | 170 ++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/ab8500_audio.h | 78 ++++++++++++++++++ 2 files changed, 248 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/ab8500_audio.c b/sound/soc/codecs/ab8500_audio.c index 1773dc24a56..ad2fb60c6ab 100644 --- a/sound/soc/codecs/ab8500_audio.c +++ b/sound/soc/codecs/ab8500_audio.c @@ -175,6 +175,14 @@ static const u8 ab8500_reg_cache[AB8500_CACHEREGNUM] = { static struct snd_soc_codec *ab8500_codec; +/* Signed multi register array controls. */ +struct soc_smra_control { + unsigned int *reg; + const unsigned int rcount, count, invert; + long min, max; + const char **texts; + long *values; +}; /* Reads an arbitrary register from the ab8500 chip. */ @@ -280,6 +288,168 @@ static inline int ab8500_codec_update_reg_audio(struct snd_soc_codec *codec, return ab8500_codec_write_reg_audio(codec, reg, new); } +/* Generic soc info for signed register controls. */ +int snd_soc_info_s(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_smra_control *smra = + (struct soc_smra_control *)kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = smra->count; + uinfo->value.integer.min = smra->min; + uinfo->value.integer.max = smra->max; + + return 0; +} + +/* Generic soc get for signed multi register controls. */ +int snd_soc_get_smr(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_smra_control *smra = + (struct soc_smra_control *)kcontrol->private_value; + unsigned int *reg = smra->reg; + unsigned int rcount = smra->rcount; + long min = smra->min; + long max = smra->max; + unsigned int invert = smra->invert; + unsigned long mask = abs(min) | abs(max); + long value = 0; + int i, rvalue; + + for (i = 0; i < rcount; i++) { + rvalue = snd_soc_read(codec, reg[i]) & REG_MASK_ALL; + value |= rvalue << (8 * (rcount - i - 1)); + } + value &= mask; + if (min < 0 && value > max) + value |= ~mask; + if (invert) + value = ~value; + ucontrol->value.integer.value[0] = value; + + return 0; +} + +/* Generic soc put for signed multi register controls. */ +int snd_soc_put_smr(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_smra_control *smra = + (struct soc_smra_control *)kcontrol->private_value; + unsigned int *reg = smra->reg; + unsigned int rcount = smra->rcount; + long min = smra->min; + long max = smra->max; + unsigned int invert = smra->invert; + unsigned long mask = abs(min) | abs(max); + long value = ucontrol->value.integer.value[0]; + int i, rvalue, err; + + if (invert) + value = ~value; + if (value > max) + value = max; + else if (value < min) + value = min; + value &= mask; + for (i = 0; i < rcount; i++) { + rvalue = (value >> (8 * (rcount - i - 1))) & REG_MASK_ALL; + err = snd_soc_write(codec, reg[i], rvalue); + if (err < 0) + return 0; + } + + return 1; +} + +/* Generic soc get for signed array controls. */ +static int snd_soc_get_sa(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_smra_control *smra = + (struct soc_smra_control *)kcontrol->private_value; + long *values = smra->values; + unsigned int count = smra->count; + unsigned int idx; + + for (idx = 0; idx < count; idx++) + ucontrol->value.integer.value[idx] = values[idx]; + + return 0; +} + +/* Generic soc put for signed array controls. */ +static int snd_soc_put_sa(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_smra_control *smra = + (struct soc_smra_control *) kcontrol->private_value; + long *values = smra->values; + unsigned int count = smra->count; + long min = smra->min; + long max = smra->max; + unsigned int idx; + long value; + + for (idx = 0; idx < count; idx++) { + value = ucontrol->value.integer.value[idx]; + if (value > max) + value = max; + else if (value < min) + value = min; + values[idx] = value; + } + + return 0; +} + +/* Generic soc get for enum strobe controls. */ +int snd_soc_get_enum_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = + (struct soc_enum *)kcontrol->private_value; + unsigned int reg = e->reg; + unsigned int bit = e->shift_l; + unsigned int invert = e->shift_r != 0; + unsigned int value = snd_soc_read(codec, reg) & BMASK(bit); + + if (bit != 0 && value != 0) + value = value >> bit; + ucontrol->value.enumerated.item[0] = value ^ invert; + + return 0; +} + +/* Generic soc put for enum strobe controls. */ +int snd_soc_put_enum_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = + (struct soc_enum *)kcontrol->private_value; + unsigned int reg = e->reg; + unsigned int bit = e->shift_l; + unsigned int invert = e->shift_r != 0; + unsigned int strobe = ucontrol->value.enumerated.item[0] != 0; + unsigned int set_mask = REG_MASK_NONE; + unsigned int clr_mask = REG_MASK_NONE; + unsigned int err; + + if (strobe ^ invert) + set_mask = BMASK(bit); + else + clr_mask = BMASK(bit); + err = snd_soc_update_bits_locked(codec, reg, clr_mask, set_mask); + if (err < 0) + return err; + return snd_soc_update_bits_locked(codec, reg, set_mask, clr_mask); +} + static const char *enum_ena_dis[] = {"Enabled", "Disabled"}; static const char *enum_dis_ena[] = {"Disabled", "Enabled"}; diff --git a/sound/soc/codecs/ab8500_audio.h b/sound/soc/codecs/ab8500_audio.h index 30c011878cb..2d659c58746 100644 --- a/sound/soc/codecs/ab8500_audio.h +++ b/sound/soc/codecs/ab8500_audio.h @@ -39,6 +39,84 @@ enum ab8500_audio_dapm_path { }; bool ab8500_audio_dapm_path_active(enum ab8500_audio_dapm_path dapm_path); +#define SOC_SINGLE_VALUE_S1R(xreg0, xcount, xmin, xmax, xinvert) \ + ((unsigned long)&(struct soc_smra_control) \ + { .reg = ((unsigned int[]){ xreg0 }), \ + .rcount = 1, .count = xcount, \ + .invert = xinvert, .min = xmin, .max = xmax}) + +#define SOC_SINGLE_VALUE_S2R(xreg0, xreg1, xcount, xmin, xmax, xinvert) \ + ((unsigned long)&(struct soc_smra_control) \ + {.reg = ((unsigned int[]){ xreg0, xreg1 }), \ + .rcount = 2, .count = xcount, \ + .min = xmin, .max = xmax, .invert = xinvert}) + +#define SOC_SINGLE_VALUE_S4R(xreg0, xreg1, xreg2, xreg3, \ + xcount, xmin, xmax, xinvert) \ + ((unsigned long)&(struct soc_smra_control) \ + {.reg = ((unsigned int[]){ xreg0, xreg1, xreg2, xreg3 }), \ + .rcount = 4, .count = xcount, \ + .min = xmin, .max = xmax, .invert = xinvert}) + +#define SOC_SINGLE_VALUE_S8R(xreg0, xreg1, xreg2, xreg3, \ + xreg4, xreg5, xreg6, xreg7, xcount, xmin, xmax, xinvert) \ + ((unsigned long)&(struct soc_smra_control) \ + {.reg = ((unsigned int[]){ xreg0, xreg1, xreg2, xreg3, \ + xreg4, xreg5, xreg6, xreg7 }), \ + .rcount = 8, .count = xcount, \ + .min = xmin, .max = xmax, .invert = xinvert}) + +#define SOC_MULTIPLE_VALUE_SA(xvalues, xcount, xmin, xmax, xinvert) \ + ((unsigned long)&(struct soc_smra_control) \ + {.values = xvalues, .rcount = 1, .count = xcount, \ + .min = xmin, .max = xmax, .invert = xinvert}) + +#define SOC_ENUM_STROBE_DECL(name, xreg, xbit, xinvert, xtexts) \ + struct soc_enum name = SOC_ENUM_DOUBLE(xreg, xbit, \ + xinvert, 2, xtexts) + +/* Extended SOC macros */ + +#define SOC_SINGLE_S1R(xname, reg0, min, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_soc_info_s, .get = snd_soc_get_smr, .put = snd_soc_put_smr, \ + .private_value = SOC_SINGLE_VALUE_S1R(reg0, 1, min, max, invert) } + +#define SOC_SINGLE_S2R(xname, reg0, reg1, min, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_soc_info_s, .get = snd_soc_get_smr, .put = snd_soc_put_smr, \ + .private_value = SOC_SINGLE_VALUE_S2R(reg0, reg1, 1, min, max, invert) } + +#define SOC_SINGLE_S4R(xname, reg0, reg1, reg2, reg3, min, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_soc_info_s, .get = snd_soc_get_smr, .put = snd_soc_put_smr, \ + .private_value = SOC_SINGLE_VALUE_S4R(reg0, reg1, reg2, reg3, \ + 1, min, max, invert) } + +#define SOC_SINGLE_S8R(xname, reg0, reg1, reg2, reg3, \ + reg4, reg5, reg6, reg7, min, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_soc_info_s, .get = snd_soc_get_smr, .put = snd_soc_put_smr, \ + .private_value = SOC_SINGLE_VALUE_S4R(reg0, reg1, reg2, reg3, \ + reg4, reg5, reg6, reg7\ + 1, min, max, invert) } + +#define SOC_MULTIPLE_SA(xname, values, min, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_soc_info_s, .get = snd_soc_get_sa, .put = snd_soc_put_sa, \ + .private_value = SOC_MULTIPLE_VALUE_SA(values, ARRAY_SIZE(values), \ + min, max, invert) } + +#define SOC_ENUM_STROBE(xname, enum) \ + SOC_ENUM_EXT(xname, xenum, \ + snd_soc_get_enum_strobe, \ + snd_soc_put_enum_strobe) + /* AB8500 audio bank (0x0d) register definitions */ #define REG_POWERUP 0x00 -- cgit v1.2.3