ASoC: core: Add signed multi register control
Added control type that can span multiple consecutive codec registers forming a single signed value in a MSB/LSB manner. The control dynamically adjusts to the register word size configured in driver. Added convenience macro. SOC_SINGLE_XR_SX Added accessor implementations. snd_soc_info_xr_sx snd_soc_get_xr_sx snd_soc_put_xr_sx Signed-off-by: Kristoffer KARLSSON <kristoffer.karlsson@stericsson.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
This commit is contained in:
parent
c1a4ecd921
commit
4183eed288
|
@ -210,6 +210,15 @@
|
||||||
{.base = xbase, .num_regs = xregs, \
|
{.base = xbase, .num_regs = xregs, \
|
||||||
.mask = xmask }) }
|
.mask = xmask }) }
|
||||||
|
|
||||||
|
#define SOC_SINGLE_XR_SX(xname, xregbase, xregcount, xnbits, \
|
||||||
|
xmin, xmax, xinvert) \
|
||||||
|
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
|
||||||
|
.info = snd_soc_info_xr_sx, .get = snd_soc_get_xr_sx, \
|
||||||
|
.put = snd_soc_put_xr_sx, \
|
||||||
|
.private_value = (unsigned long)&(struct soc_mreg_control) \
|
||||||
|
{.regbase = xregbase, .regcount = xregcount, .nbits = xnbits, \
|
||||||
|
.invert = xinvert, .min = xmin, .max = xmax} }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simplified versions of above macros, declaring a struct and calculating
|
* Simplified versions of above macros, declaring a struct and calculating
|
||||||
* ARRAY_SIZE internally
|
* ARRAY_SIZE internally
|
||||||
|
@ -446,7 +455,12 @@ int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_ctl_elem_value *ucontrol);
|
struct snd_ctl_elem_value *ucontrol);
|
||||||
int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
|
int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_ctl_elem_value *ucontrol);
|
struct snd_ctl_elem_value *ucontrol);
|
||||||
|
int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_info *uinfo);
|
||||||
|
int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol);
|
||||||
|
int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct snd_soc_reg_access - Describes whether a given register is
|
* struct snd_soc_reg_access - Describes whether a given register is
|
||||||
|
@ -932,6 +946,12 @@ struct soc_bytes {
|
||||||
u32 mask;
|
u32 mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* multi register control */
|
||||||
|
struct soc_mreg_control {
|
||||||
|
long min, max;
|
||||||
|
unsigned int regbase, regcount, nbits, invert;
|
||||||
|
};
|
||||||
|
|
||||||
/* enumerated kcontrol */
|
/* enumerated kcontrol */
|
||||||
struct soc_enum {
|
struct soc_enum {
|
||||||
unsigned short reg;
|
unsigned short reg;
|
||||||
|
|
|
@ -2891,6 +2891,124 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
|
EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* snd_soc_info_xr_sx - signed multi register info callback
|
||||||
|
* @kcontrol: mreg control
|
||||||
|
* @uinfo: control element information
|
||||||
|
*
|
||||||
|
* Callback to provide information of a control that can
|
||||||
|
* span multiple codec registers which together
|
||||||
|
* forms a single signed value in a MSB/LSB manner.
|
||||||
|
*
|
||||||
|
* Returns 0 for success.
|
||||||
|
*/
|
||||||
|
int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_info *uinfo)
|
||||||
|
{
|
||||||
|
struct soc_mreg_control *mc =
|
||||||
|
(struct soc_mreg_control *)kcontrol->private_value;
|
||||||
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||||
|
uinfo->count = 1;
|
||||||
|
uinfo->value.integer.min = mc->min;
|
||||||
|
uinfo->value.integer.max = mc->max;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* snd_soc_get_xr_sx - signed multi register get callback
|
||||||
|
* @kcontrol: mreg control
|
||||||
|
* @ucontrol: control element information
|
||||||
|
*
|
||||||
|
* Callback to get the value of a control that can span
|
||||||
|
* multiple codec registers which together forms a single
|
||||||
|
* signed value in a MSB/LSB manner. The control supports
|
||||||
|
* specifying total no of bits used to allow for bitfields
|
||||||
|
* across the multiple codec registers.
|
||||||
|
*
|
||||||
|
* Returns 0 for success.
|
||||||
|
*/
|
||||||
|
int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct soc_mreg_control *mc =
|
||||||
|
(struct soc_mreg_control *)kcontrol->private_value;
|
||||||
|
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||||
|
unsigned int regbase = mc->regbase;
|
||||||
|
unsigned int regcount = mc->regcount;
|
||||||
|
unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE;
|
||||||
|
unsigned int regwmask = (1<<regwshift)-1;
|
||||||
|
unsigned int invert = mc->invert;
|
||||||
|
unsigned long mask = (1UL<<mc->nbits)-1;
|
||||||
|
long min = mc->min;
|
||||||
|
long max = mc->max;
|
||||||
|
long val = 0;
|
||||||
|
unsigned long regval;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < regcount; i++) {
|
||||||
|
regval = snd_soc_read(codec, regbase+i) & regwmask;
|
||||||
|
val |= regval << (regwshift*(regcount-i-1));
|
||||||
|
}
|
||||||
|
val &= mask;
|
||||||
|
if (min < 0 && val > max)
|
||||||
|
val |= ~mask;
|
||||||
|
if (invert)
|
||||||
|
val = max - val;
|
||||||
|
ucontrol->value.integer.value[0] = val;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* snd_soc_put_xr_sx - signed multi register get callback
|
||||||
|
* @kcontrol: mreg control
|
||||||
|
* @ucontrol: control element information
|
||||||
|
*
|
||||||
|
* Callback to set the value of a control that can span
|
||||||
|
* multiple codec registers which together forms a single
|
||||||
|
* signed value in a MSB/LSB manner. The control supports
|
||||||
|
* specifying total no of bits used to allow for bitfields
|
||||||
|
* across the multiple codec registers.
|
||||||
|
*
|
||||||
|
* Returns 0 for success.
|
||||||
|
*/
|
||||||
|
int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct soc_mreg_control *mc =
|
||||||
|
(struct soc_mreg_control *)kcontrol->private_value;
|
||||||
|
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||||
|
unsigned int regbase = mc->regbase;
|
||||||
|
unsigned int regcount = mc->regcount;
|
||||||
|
unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE;
|
||||||
|
unsigned int regwmask = (1<<regwshift)-1;
|
||||||
|
unsigned int invert = mc->invert;
|
||||||
|
unsigned long mask = (1UL<<mc->nbits)-1;
|
||||||
|
long min = mc->min;
|
||||||
|
long max = mc->max;
|
||||||
|
long val = ucontrol->value.integer.value[0];
|
||||||
|
unsigned int i, regval, regmask;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (invert)
|
||||||
|
val = max - val;
|
||||||
|
val &= mask;
|
||||||
|
for (i = 0; i < regcount; i++) {
|
||||||
|
regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
|
||||||
|
regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
|
||||||
|
err = snd_soc_update_bits_locked(codec, regbase+i,
|
||||||
|
regmask, regval);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* snd_soc_dai_set_sysclk - configure DAI system or master clock.
|
* snd_soc_dai_set_sysclk - configure DAI system or master clock.
|
||||||
* @dai: DAI
|
* @dai: DAI
|
||||||
|
|
Loading…
Reference in New Issue