summaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@nokia.com>2010-05-06 10:37:18 +0300
committerLiam Girdwood <lrg@slimlogic.co.uk>2010-05-06 14:58:20 +0100
commit6f3991152f20933b77eff30413e893bf1a15e578 (patch)
treec34ec1a5e9cc707de055972e78100feb491ad816 /sound/soc
parent5193d62f1824cdfd72b5523be2b1cdb8049225ad (diff)
ASoC: tpa6130a2: Support for limiting gain
Add support for platform dependent gain limiting on the tpa6130a2 (and tpa6140a2) Headset amplifier. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/codecs/tpa6130a2.c76
1 files changed, 68 insertions, 8 deletions
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
index 0cf3e3862e7..31f67b527ca 100644
--- a/sound/soc/codecs/tpa6130a2.c
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -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,10 +305,9 @@ 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,
- tpa6130_tlv),
+ SOC_SINGLE_EXT_TLV_TPA("TPA6130A2 Headphone Playback Volume",
+ TPA6130A2_REG_VOL_MUTE, 0, TPA6130A2_GAIN_MAX, 0,
+ tpa6130_tlv),
};
static const unsigned int tpa6140_tlv[] = {
@@ -272,10 +318,9 @@ 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,
- tpa6140_tlv),
+ 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),