Commit 6f399115 authored by Peter Ujfalusi's avatar Peter Ujfalusi Committed by Liam Girdwood
Browse files

ASoC: tpa6130a2: Support for limiting gain



Add support for platform dependent gain limiting on the
tpa6130a2 (and tpa6140a2) Headset amplifier.

Signed-off-by: default avatarPeter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: default avatarLiam Girdwood <lrg@slimlogic.co.uk>
parent 5193d62f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ enum tpa_model {
struct tpa6130a2_platform_data {
	enum tpa_model id;
	int power_gpio;
	int limit_gain;
};

#endif
+68 −8
Original line number Diff line number Diff line
@@ -46,6 +46,9 @@ static const char *tpa6140a2_supply_names[TPA6130A2_NUM_SUPPLIES] = {
	"AVdd",
};

#define TPA6130A2_GAIN_MAX	0x3f
#define TPA6140A2_GAIN_MAX	0x1f

/* This struct is used to save the context */
struct tpa6130a2_data {
	struct mutex mutex;
@@ -54,6 +57,7 @@ struct tpa6130a2_data {
	int power_gpio;
	unsigned char power_state;
	enum tpa_model id;
	int gain_limit;
};

static int tpa6130a2_i2c_read(int reg)
@@ -176,6 +180,40 @@ exit:
	return ret;
}

static int tpa6130a2_info_volsw(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_info *uinfo)
{
	struct soc_mixer_control *mc =
		(struct soc_mixer_control *)kcontrol->private_value;
	struct tpa6130a2_data *data;

	BUG_ON(tpa6130a2_client == NULL);
	data = i2c_get_clientdata(tpa6130a2_client);

	mutex_lock(&data->mutex);
	switch (mc->reg) {
	case TPA6130A2_REG_VOL_MUTE:
		if (data->gain_limit != mc->max)
			mc->max = data->gain_limit;
		break;
	default:
		dev_err(&tpa6130a2_client->dev,
			"Invalid register: 0x02%x\n", mc->reg);
		goto out;
	}
	if (unlikely(mc->max == 1))
		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
	else
		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;

	uinfo->count = 1;
	uinfo->value.integer.min = 0;
	uinfo->value.integer.max = mc->max;
out:
	mutex_unlock(&data->mutex);
	return 0;
}

static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
@@ -239,6 +277,15 @@ static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol,
	return 1;
}

#define SOC_SINGLE_EXT_TLV_TPA(xname, xreg, xshift, xmax, xinvert, tlv_array) \
{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
	.tlv.p = (tlv_array), \
	.info = tpa6130a2_info_volsw, \
	.get = tpa6130a2_get_reg, .put = tpa6130a2_set_reg, \
	.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }

/*
 * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
 * down in gain.
@@ -258,9 +305,8 @@ static const unsigned int tpa6130_tlv[] = {
};

static const struct snd_kcontrol_new tpa6130a2_controls[] = {
	SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
		       TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
		       tpa6130a2_get_reg, tpa6130a2_set_reg,
	SOC_SINGLE_EXT_TLV_TPA("TPA6130A2 Headphone Playback Volume",
			TPA6130A2_REG_VOL_MUTE, 0, TPA6130A2_GAIN_MAX, 0,
			tpa6130_tlv),
};

@@ -272,9 +318,8 @@ static const unsigned int tpa6140_tlv[] = {
};

static const struct snd_kcontrol_new tpa6140a2_controls[] = {
	SOC_SINGLE_EXT_TLV("TPA6140A2 Headphone Playback Volume",
		       TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0,
		       tpa6130a2_get_reg, tpa6130a2_set_reg,
	SOC_SINGLE_EXT_TLV_TPA("TPA6140A2 Headphone Playback Volume",
			TPA6130A2_REG_VOL_MUTE, 1, TPA6140A2_GAIN_MAX, 0,
			tpa6140_tlv),
};

@@ -454,16 +499,31 @@ static int __devinit tpa6130a2_probe(struct i2c_client *client,
	case TPA6130A2:
		for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
			data->supplies[i].supply = tpa6130a2_supply_names[i];
		if (pdata->limit_gain > 0 &&
		    pdata->limit_gain < TPA6130A2_GAIN_MAX)
			data->gain_limit = pdata->limit_gain;
		else
			data->gain_limit = TPA6130A2_GAIN_MAX;
		break;
	case TPA6140A2:
		for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
			data->supplies[i].supply = tpa6140a2_supply_names[i];;
		if (pdata->limit_gain > 0 &&
		    pdata->limit_gain < TPA6140A2_GAIN_MAX)
			data->gain_limit = pdata->limit_gain;
		else
			data->gain_limit = TPA6140A2_GAIN_MAX;
		break;
	default:
		dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
			 pdata->id);
		for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
			data->supplies[i].supply = tpa6130a2_supply_names[i];
		if (pdata->limit_gain > 0 &&
		    pdata->limit_gain < TPA6130A2_GAIN_MAX)
			data->gain_limit = pdata->limit_gain;
		else
			data->gain_limit = TPA6130A2_GAIN_MAX;
	}

	ret = regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),