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

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
...@@ -31,6 +31,7 @@ enum tpa_model { ...@@ -31,6 +31,7 @@ enum tpa_model {
struct tpa6130a2_platform_data { struct tpa6130a2_platform_data {
enum tpa_model id; enum tpa_model id;
int power_gpio; int power_gpio;
int limit_gain;
}; };
#endif #endif
...@@ -46,6 +46,9 @@ static const char *tpa6140a2_supply_names[TPA6130A2_NUM_SUPPLIES] = { ...@@ -46,6 +46,9 @@ static const char *tpa6140a2_supply_names[TPA6130A2_NUM_SUPPLIES] = {
"AVdd", "AVdd",
}; };
#define TPA6130A2_GAIN_MAX 0x3f
#define TPA6140A2_GAIN_MAX 0x1f
/* This struct is used to save the context */ /* This struct is used to save the context */
struct tpa6130a2_data { struct tpa6130a2_data {
struct mutex mutex; struct mutex mutex;
...@@ -54,6 +57,7 @@ struct tpa6130a2_data { ...@@ -54,6 +57,7 @@ struct tpa6130a2_data {
int power_gpio; int power_gpio;
unsigned char power_state; unsigned char power_state;
enum tpa_model id; enum tpa_model id;
int gain_limit;
}; };
static int tpa6130a2_i2c_read(int reg) static int tpa6130a2_i2c_read(int reg)
...@@ -176,6 +180,40 @@ static int tpa6130a2_power(int power) ...@@ -176,6 +180,40 @@ static int tpa6130a2_power(int power)
return ret; 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, static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
...@@ -239,6 +277,15 @@ static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol, ...@@ -239,6 +277,15 @@ static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol,
return 1; 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 * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
* down in gain. * down in gain.
...@@ -258,9 +305,8 @@ static const unsigned int tpa6130_tlv[] = { ...@@ -258,9 +305,8 @@ static const unsigned int tpa6130_tlv[] = {
}; };
static const struct snd_kcontrol_new tpa6130a2_controls[] = { static const struct snd_kcontrol_new tpa6130a2_controls[] = {
SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume", SOC_SINGLE_EXT_TLV_TPA("TPA6130A2 Headphone Playback Volume",
TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0, TPA6130A2_REG_VOL_MUTE, 0, TPA6130A2_GAIN_MAX, 0,
tpa6130a2_get_reg, tpa6130a2_set_reg,
tpa6130_tlv), tpa6130_tlv),
}; };
...@@ -272,9 +318,8 @@ static const unsigned int tpa6140_tlv[] = { ...@@ -272,9 +318,8 @@ static const unsigned int tpa6140_tlv[] = {
}; };
static const struct snd_kcontrol_new tpa6140a2_controls[] = { static const struct snd_kcontrol_new tpa6140a2_controls[] = {
SOC_SINGLE_EXT_TLV("TPA6140A2 Headphone Playback Volume", SOC_SINGLE_EXT_TLV_TPA("TPA6140A2 Headphone Playback Volume",
TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0, TPA6130A2_REG_VOL_MUTE, 1, TPA6140A2_GAIN_MAX, 0,
tpa6130a2_get_reg, tpa6130a2_set_reg,
tpa6140_tlv), tpa6140_tlv),
}; };
...@@ -454,16 +499,31 @@ static int __devinit tpa6130a2_probe(struct i2c_client *client, ...@@ -454,16 +499,31 @@ static int __devinit tpa6130a2_probe(struct i2c_client *client,
case TPA6130A2: case TPA6130A2:
for (i = 0; i < ARRAY_SIZE(data->supplies); i++) for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
data->supplies[i].supply = tpa6130a2_supply_names[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; break;
case TPA6140A2: case TPA6140A2:
for (i = 0; i < ARRAY_SIZE(data->supplies); i++) for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
data->supplies[i].supply = tpa6140a2_supply_names[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; break;
default: default:
dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n", dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
pdata->id); pdata->id);
for (i = 0; i < ARRAY_SIZE(data->supplies); i++) for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
data->supplies[i].supply = tpa6130a2_supply_names[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), ret = regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment