Загрузка данных


/*
 * 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),
};