Загрузка данных
/*
* custom function to get of PCM playback volume
*
* dac volume register
* 15-------------8-7--------------0
* | R channel vol | L channel vol |
* -------------------------------
*
* PCM volume with 0.5017 dB steps from 0 to -90 dB
*
* register values map to dB
* 0x3B and less = Reserved
* 0x3C = 0 dB
* 0x3D = -0.5 dB
* 0xF0 = -90 dB
* 0xFC and greater = Muted
*
* register value map to userspace value
*
* register value 0x3c(0dB) 0xf0(-90dB)0xfc
* ------------------------------
* userspace value 0xc0 0
*/
static int dac_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int reg;
int l;
int r;
reg = snd_soc_component_read(component, SGTL5000_CHIP_DAC_VOL);
/* get left channel volume */
l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT;
/* get right channel volume */
r = (reg & SGTL5000_DAC_VOL_RIGHT_MASK) >> SGTL5000_DAC_VOL_RIGHT_SHIFT;
/* make sure value fall in (0x3c,0xfc) */
l = clamp(l, 0x3c, 0xfc);
r = clamp(r, 0x3c, 0xfc);
/* invert it and map to userspace value */
l = 0xfc - l;
r = 0xfc - r;
ucontrol->value.integer.value[0] = l;
ucontrol->value.integer.value[1] = r;
return 0;
}
/*
* custom function to put of PCM playback volume
*
* dac volume register
* 15-------------8-7--------------0
* | R channel vol | L channel vol |
* -------------------------------
*
* PCM volume with 0.5017 dB steps from 0 to -90 dB
*
* register values map to dB
* 0x3B and less = Reserved
* 0x3C = 0 dB
* 0x3D = -0.5 dB
* 0xF0 = -90 dB
* 0xFC and greater = Muted
*
* userspace value map to register value
*
* userspace value 0xc0 0
* ------------------------------
* register value 0x3c(0dB) 0xf0(-90dB)0xfc
*/
static int dac_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int reg;
int l;
int r;
l = ucontrol->value.integer.value[0];
r = ucontrol->value.integer.value[1];
/* make sure userspace volume fall in (0, 0xfc-0x3c) */
l = clamp(l, 0, 0xfc - 0x3c);
r = clamp(r, 0, 0xfc - 0x3c);
/* invert it, get the value can be set to register */
l = 0xfc - l;
r = 0xfc - r;
/* shift to get the register value */
reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT |
r << SGTL5000_DAC_VOL_RIGHT_SHIFT;
snd_soc_component_write(component, SGTL5000_CHIP_DAC_VOL, reg);
return 0;
}
/*
* custom function to get AVC threshold
*
* The threshold dB is calculated by rearranging the calculation from the
* avc_put_threshold function: register_value = 10^(dB/20) * 0.636 * 2^15 ==>
* dB = ( fls(register_value) - 14.347 ) * 6.02
*
* As this calculation is expensive and the threshold dB values may not exceed
* 0 to 96 we use pre-calculated values.
*/
static int avc_get_threshold(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int db, i;
u16 reg = snd_soc_component_read(component, SGTL5000_DAP_AVC_THRESHOLD);
/* register value 0 => -96dB */
if (!reg) {
ucontrol->value.integer.value[0] = 96;
ucontrol->value.integer.value[1] = 96;
return 0;
}
/* get dB from register value (rounded down) */
for (i = 0; avc_thr_db2reg[i] > reg; i++)
;
db = i;
ucontrol->value.integer.value[0] = db;
ucontrol->value.integer.value[1] = db;
return 0;
}
/*
* custom function to put AVC threshold
*
* The register value is calculated by following formula:
* register_value = 10^(dB/20) * 0.636 * 2^15
* As this calculation is expensive and the threshold dB values may not exceed
* 0 to 96 we use pre-calculated values.
*/
static int avc_put_threshold(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int db;
u16 reg;
db = (int)ucontrol->value.integer.value[0];
if (db < 0 || db > 96)
return -EINVAL;
reg = avc_thr_db2reg[db];
snd_soc_component_write(component, SGTL5000_DAP_AVC_THRESHOLD, reg);
return 0;
}
static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0);
/* tlv for mic gain, 0db 20db 30db 40db */
static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0)
);
/* tlv for DAP channels, 0% - 100% - 200% */
static const DECLARE_TLV_DB_SCALE(dap_volume, 0, 1, 0);
/* tlv for bass bands, -11.75db to 12.0db, step .25db */
static const DECLARE_TLV_DB_SCALE(bass_band, -1175, 25, 0);
/* tlv for hp volume, -51.5db to 12.0db, step .5db */
static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
/* tlv for lineout volume, 31 steps of .5db each */
static const DECLARE_TLV_DB_SCALE(lineout_volume, -1550, 50, 0);
/* tlv for dap avc max gain, 0db, 6db, 12db */
static const DECLARE_TLV_DB_SCALE(avc_max_gain, 0, 600, 0);
/* tlv for dap avc threshold, */
static const DECLARE_TLV_DB_MINMAX(avc_threshold, 0, 9600);
static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
/* SOC_DOUBLE_S8_TLV with invert */
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = dac_info_volsw,
.get = dac_get_volsw,
.put = dac_put_volsw,
},
SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0),
SOC_SINGLE_TLV("Capture Attenuate Switch (-6dB)",
SGTL5000_CHIP_ANA_ADC_CTRL,
8, 1, 0, capture_6db_attenuate),
SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0),
SOC_SINGLE("Capture Switch", SGTL5000_CHIP_ANA_CTRL, 0, 1, 1),
SOC_DOUBLE_TLV("Headphone Playback Volume",
SGTL5000_CHIP_ANA_HP_CTRL,
0, 8,
0x7f, 1,
headphone_volume),
SOC_SINGLE("Headphone Playback Switch", SGTL5000_CHIP_ANA_CTRL,
4, 1, 1),
SOC_SINGLE("Headphone Playback ZC Switch", SGTL5000_CHIP_ANA_CTRL,
5, 1, 0),
SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL,
0, 3, 0, mic_gain_tlv),
SOC_DOUBLE_TLV("Lineout Playback Volume",
SGTL5000_CHIP_LINE_OUT_VOL,
SGTL5000_LINE_OUT_VOL_LEFT_SHIFT,
SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT,
0x1f, 1,
lineout_volume),
SOC_SINGLE("Lineout Playback Switch", SGTL5000_CHIP_ANA_CTRL, 8, 1, 1),
SOC_SINGLE_TLV("DAP Main channel", SGTL5000_DAP_MAIN_CHAN,
0, 0xffff, 0, dap_volume),
SOC_SINGLE_TLV("DAP Mix channel", SGTL5000_DAP_MIX_CHAN,
0, 0xffff, 0, dap_volume),
/* Automatic Volume Control (DAP AVC) */
SOC_SINGLE("AVC Switch", SGTL5000_DAP_AVC_CTRL, 0, 1, 0),
SOC_SINGLE("AVC Hard Limiter Switch", SGTL5000_DAP_AVC_CTRL, 5, 1, 0),
SOC_SINGLE_TLV("AVC Max Gain Volume", SGTL5000_DAP_AVC_CTRL, 12, 2, 0,
avc_max_gain),
SOC_SINGLE("AVC Integrator Response", SGTL5000_DAP_AVC_CTRL, 8, 3, 0),
SOC_SINGLE_EXT_TLV("AVC Threshold Volume", SGTL5000_DAP_AVC_THRESHOLD,
0, 96, 0, avc_get_threshold, avc_put_threshold,
avc_threshold),
SOC_SINGLE_TLV("BASS 0", SGTL5000_DAP_EQ_BASS_BAND0,
0, 0x5F, 0, bass_band),
SOC_SINGLE_TLV("BASS 1", SGTL5000_DAP_EQ_BASS_BAND1,
0, 0x5F, 0, bass_band),
SOC_SINGLE_TLV("BASS 2", SGTL5000_DAP_EQ_BASS_BAND2,
0, 0x5F, 0, bass_band),
SOC_SINGLE_TLV("BASS 3", SGTL5000_DAP_EQ_BASS_BAND3,
0, 0x5F, 0, bass_band),
SOC_SINGLE_TLV("BASS 4", SGTL5000_DAP_EQ_BASS_BAND4,
0, 0x5F, 0, bass_band),
};