Merge remote-tracking branch 'asoc/topic/tlv320aic3x' into asoc-next

This commit is contained in:
Mark Brown 2013-10-24 11:24:16 +01:00
commit 3caa28274e
2 changed files with 139 additions and 121 deletions

View file

@ -24,10 +24,36 @@ Optional properties:
3 - MICBIAS output is connected to AVDD, 3 - MICBIAS output is connected to AVDD,
If this node is not mentioned or if the value is incorrect, then MicBias If this node is not mentioned or if the value is incorrect, then MicBias
is powered down. is powered down.
- AVDD-supply, IOVDD-supply, DRVDD-supply, DVDD-supply : power supplies for the
device as covered in Documentation/devicetree/bindings/regulator/regulator.txt
CODEC output pins:
* LLOUT
* RLOUT
* MONO_LOUT
* HPLOUT
* HPROUT
* HPLCOM
* HPRCOM
CODEC input pins:
* MIC3L
* MIC3R
* LINE1L
* LINE2L
* LINE1R
* LINE2R
The pins can be used in referring sound node's audio-routing property.
Example: Example:
tlv320aic3x: tlv320aic3x@1b { tlv320aic3x: tlv320aic3x@1b {
compatible = "ti,tlv320aic3x"; compatible = "ti,tlv320aic3x";
reg = <0x1b>; reg = <0x1b>;
AVDD-supply = <&regulator>;
IOVDD-supply = <&regulator>;
DRVDD-supply = <&regulator>;
DVDD-supply = <&regulator>;
}; };

View file

@ -40,6 +40,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>
@ -72,9 +73,9 @@ struct aic3x_disable_nb {
/* codec private data */ /* codec private data */
struct aic3x_priv { struct aic3x_priv {
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
struct regmap *regmap;
struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES]; struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES];
struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES]; struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES];
enum snd_soc_control_type control_type;
struct aic3x_setup_data *setup; struct aic3x_setup_data *setup;
unsigned int sysclk; unsigned int sysclk;
struct list_head list; struct list_head list;
@ -90,41 +91,45 @@ struct aic3x_priv {
enum aic3x_micbias_voltage micbias_vg; enum aic3x_micbias_voltage micbias_vg;
}; };
/* static const struct reg_default aic3x_reg[] = {
* AIC3X register cache { 0, 0x00 }, { 1, 0x00 }, { 2, 0x00 }, { 3, 0x10 },
* We can't read the AIC3X register space when we are { 4, 0x04 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
* using 2 wire for device control, so we cache them instead. { 8, 0x00 }, { 9, 0x00 }, { 10, 0x00 }, { 11, 0x01 },
* There is no point in caching the reset register { 12, 0x00 }, { 13, 0x00 }, { 14, 0x00 }, { 15, 0x80 },
*/ { 16, 0x80 }, { 17, 0xff }, { 18, 0xff }, { 19, 0x78 },
static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { { 20, 0x78 }, { 21, 0x78 }, { 22, 0x78 }, { 23, 0x78 },
0x00, 0x00, 0x00, 0x10, /* 0 */ { 24, 0x78 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0xfe },
0x04, 0x00, 0x00, 0x00, /* 4 */ { 28, 0x00 }, { 29, 0x00 }, { 30, 0xfe }, { 31, 0x00 },
0x00, 0x00, 0x00, 0x01, /* 8 */ { 32, 0x18 }, { 33, 0x18 }, { 34, 0x00 }, { 35, 0x00 },
0x00, 0x00, 0x00, 0x80, /* 12 */ { 36, 0x00 }, { 37, 0x00 }, { 38, 0x00 }, { 39, 0x00 },
0x80, 0xff, 0xff, 0x78, /* 16 */ { 40, 0x00 }, { 41, 0x00 }, { 42, 0x00 }, { 43, 0x80 },
0x78, 0x78, 0x78, 0x78, /* 20 */ { 44, 0x80 }, { 45, 0x00 }, { 46, 0x00 }, { 47, 0x00 },
0x78, 0x00, 0x00, 0xfe, /* 24 */ { 48, 0x00 }, { 49, 0x00 }, { 50, 0x00 }, { 51, 0x04 },
0x00, 0x00, 0xfe, 0x00, /* 28 */ { 52, 0x00 }, { 53, 0x00 }, { 54, 0x00 }, { 55, 0x00 },
0x18, 0x18, 0x00, 0x00, /* 32 */ { 56, 0x00 }, { 57, 0x00 }, { 58, 0x04 }, { 59, 0x00 },
0x00, 0x00, 0x00, 0x00, /* 36 */ { 60, 0x00 }, { 61, 0x00 }, { 62, 0x00 }, { 63, 0x00 },
0x00, 0x00, 0x00, 0x80, /* 40 */ { 64, 0x00 }, { 65, 0x04 }, { 66, 0x00 }, { 67, 0x00 },
0x80, 0x00, 0x00, 0x00, /* 44 */ { 68, 0x00 }, { 69, 0x00 }, { 70, 0x00 }, { 71, 0x00 },
0x00, 0x00, 0x00, 0x04, /* 48 */ { 72, 0x04 }, { 73, 0x00 }, { 74, 0x00 }, { 75, 0x00 },
0x00, 0x00, 0x00, 0x00, /* 52 */ { 76, 0x00 }, { 77, 0x00 }, { 78, 0x00 }, { 79, 0x00 },
0x00, 0x00, 0x04, 0x00, /* 56 */ { 80, 0x00 }, { 81, 0x00 }, { 82, 0x00 }, { 83, 0x00 },
0x00, 0x00, 0x00, 0x00, /* 60 */ { 84, 0x00 }, { 85, 0x00 }, { 86, 0x00 }, { 87, 0x00 },
0x00, 0x04, 0x00, 0x00, /* 64 */ { 88, 0x00 }, { 89, 0x00 }, { 90, 0x00 }, { 91, 0x00 },
0x00, 0x00, 0x00, 0x00, /* 68 */ { 92, 0x00 }, { 93, 0x00 }, { 94, 0x00 }, { 95, 0x00 },
0x04, 0x00, 0x00, 0x00, /* 72 */ { 96, 0x00 }, { 97, 0x00 }, { 98, 0x00 }, { 99, 0x00 },
0x00, 0x00, 0x00, 0x00, /* 76 */ { 100, 0x00 }, { 101, 0x00 }, { 102, 0x02 }, { 103, 0x00 },
0x00, 0x00, 0x00, 0x00, /* 80 */ { 104, 0x00 }, { 105, 0x00 }, { 106, 0x00 }, { 107, 0x00 },
0x00, 0x00, 0x00, 0x00, /* 84 */ { 108, 0x00 }, { 109, 0x00 },
0x00, 0x00, 0x00, 0x00, /* 88 */ };
0x00, 0x00, 0x00, 0x00, /* 92 */
0x00, 0x00, 0x00, 0x00, /* 96 */ static const struct regmap_config aic3x_regmap = {
0x00, 0x00, 0x02, 0x00, /* 100 */ .reg_bits = 8,
0x00, 0x00, 0x00, 0x00, /* 104 */ .val_bits = 8,
0x00, 0x00, /* 108 */
.max_register = DAC_ICC_ADJ,
.reg_defaults = aic3x_reg,
.num_reg_defaults = ARRAY_SIZE(aic3x_reg),
.cache_type = REGCACHE_RBTREE,
}; };
#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
@ -828,12 +833,6 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets,
ARRAY_SIZE(aic3x_dapm_widgets));
/* set up audio path interconnects */
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
if (aic3x->model == AIC3X_MODEL_3007) { if (aic3x->model == AIC3X_MODEL_3007) {
snd_soc_dapm_new_controls(dapm, aic3007_dapm_widgets, snd_soc_dapm_new_controls(dapm, aic3007_dapm_widgets,
ARRAY_SIZE(aic3007_dapm_widgets)); ARRAY_SIZE(aic3007_dapm_widgets));
@ -1082,29 +1081,6 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0; return 0;
} }
static int aic3x_init_3007(struct snd_soc_codec *codec)
{
u8 tmp1, tmp2, *cache = codec->reg_cache;
/*
* There is no need to cache writes to undocumented page 0xD but
* respective page 0 register cache entries must be preserved
*/
tmp1 = cache[0xD];
tmp2 = cache[0x8];
/* Class-D speaker driver init; datasheet p. 46 */
snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x0D);
snd_soc_write(codec, 0xD, 0x0D);
snd_soc_write(codec, 0x8, 0x5C);
snd_soc_write(codec, 0x8, 0x5D);
snd_soc_write(codec, 0x8, 0x5C);
snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x00);
cache[0xD] = tmp1;
cache[0x8] = tmp2;
return 0;
}
static int aic3x_regulator_event(struct notifier_block *nb, static int aic3x_regulator_event(struct notifier_block *nb,
unsigned long event, void *data) unsigned long event, void *data)
{ {
@ -1119,7 +1095,7 @@ static int aic3x_regulator_event(struct notifier_block *nb,
*/ */
if (gpio_is_valid(aic3x->gpio_reset)) if (gpio_is_valid(aic3x->gpio_reset))
gpio_set_value(aic3x->gpio_reset, 0); gpio_set_value(aic3x->gpio_reset, 0);
aic3x->codec->cache_sync = 1; regcache_mark_dirty(aic3x->regmap);
} }
return 0; return 0;
@ -1128,8 +1104,7 @@ static int aic3x_regulator_event(struct notifier_block *nb,
static int aic3x_set_power(struct snd_soc_codec *codec, int power) static int aic3x_set_power(struct snd_soc_codec *codec, int power)
{ {
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
int i, ret; int ret;
u8 *cache = codec->reg_cache;
if (power) { if (power) {
ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies), ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies),
@ -1137,12 +1112,6 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)
if (ret) if (ret)
goto out; goto out;
aic3x->power = 1; aic3x->power = 1;
/*
* Reset release and cache sync is necessary only if some
* supply was off or if there were cached writes
*/
if (!codec->cache_sync)
goto out;
if (gpio_is_valid(aic3x->gpio_reset)) { if (gpio_is_valid(aic3x->gpio_reset)) {
udelay(1); udelay(1);
@ -1150,12 +1119,8 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)
} }
/* Sync reg_cache with the hardware */ /* Sync reg_cache with the hardware */
codec->cache_only = 0; regcache_cache_only(aic3x->regmap, false);
for (i = AIC3X_SAMPLE_RATE_SEL_REG; i < ARRAY_SIZE(aic3x_reg); i++) regcache_sync(aic3x->regmap);
snd_soc_write(codec, i, cache[i]);
if (aic3x->model == AIC3X_MODEL_3007)
aic3x_init_3007(codec);
codec->cache_sync = 0;
} else { } else {
/* /*
* Do soft reset to this codec instance in order to clear * Do soft reset to this codec instance in order to clear
@ -1163,10 +1128,10 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)
* remain on * remain on
*/ */
snd_soc_write(codec, AIC3X_RESET, SOFT_RESET); snd_soc_write(codec, AIC3X_RESET, SOFT_RESET);
codec->cache_sync = 1; regcache_mark_dirty(aic3x->regmap);
aic3x->power = 0; aic3x->power = 0;
/* HW writes are needless when bias is off */ /* HW writes are needless when bias is off */
codec->cache_only = 1; regcache_cache_only(aic3x->regmap, true);
ret = regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), ret = regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies),
aic3x->supplies); aic3x->supplies);
} }
@ -1321,7 +1286,6 @@ static int aic3x_init(struct snd_soc_codec *codec)
snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
if (aic3x->model == AIC3X_MODEL_3007) { if (aic3x->model == AIC3X_MODEL_3007) {
aic3x_init_3007(codec);
snd_soc_write(codec, CLASSD_CTRL, 0); snd_soc_write(codec, CLASSD_CTRL, 0);
} }
@ -1349,29 +1313,12 @@ static int aic3x_probe(struct snd_soc_codec *codec)
INIT_LIST_HEAD(&aic3x->list); INIT_LIST_HEAD(&aic3x->list);
aic3x->codec = codec; aic3x->codec = codec;
ret = snd_soc_codec_set_cache_io(codec, 8, 8, aic3x->control_type); ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
if (ret != 0) { if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret; return ret;
} }
if (gpio_is_valid(aic3x->gpio_reset) &&
!aic3x_is_shared_reset(aic3x)) {
ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset");
if (ret != 0)
goto err_gpio;
gpio_direction_output(aic3x->gpio_reset, 0);
}
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
aic3x->supplies[i].supply = aic3x_supply_names[i];
ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(aic3x->supplies),
aic3x->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
goto err_get;
}
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) { for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) {
aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event; aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event;
aic3x->disable_nb[i].aic3x = aic3x; aic3x->disable_nb[i].aic3x = aic3x;
@ -1385,7 +1332,7 @@ static int aic3x_probe(struct snd_soc_codec *codec)
} }
} }
codec->cache_only = 1; regcache_mark_dirty(aic3x->regmap);
aic3x_init(codec); aic3x_init(codec);
if (aic3x->setup) { if (aic3x->setup) {
@ -1396,8 +1343,6 @@ static int aic3x_probe(struct snd_soc_codec *codec)
(aic3x->setup->gpio_func[1] & 0xf) << 4); (aic3x->setup->gpio_func[1] & 0xf) << 4);
} }
snd_soc_add_codec_controls(codec, aic3x_snd_controls,
ARRAY_SIZE(aic3x_snd_controls));
if (aic3x->model == AIC3X_MODEL_3007) if (aic3x->model == AIC3X_MODEL_3007)
snd_soc_add_codec_controls(codec, &aic3x_classd_amp_gain_ctrl, 1); snd_soc_add_codec_controls(codec, &aic3x_classd_amp_gain_ctrl, 1);
@ -1428,12 +1373,6 @@ static int aic3x_probe(struct snd_soc_codec *codec)
while (i--) while (i--)
regulator_unregister_notifier(aic3x->supplies[i].consumer, regulator_unregister_notifier(aic3x->supplies[i].consumer,
&aic3x->disable_nb[i].nb); &aic3x->disable_nb[i].nb);
regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
err_get:
if (gpio_is_valid(aic3x->gpio_reset) &&
!aic3x_is_shared_reset(aic3x))
gpio_free(aic3x->gpio_reset);
err_gpio:
return ret; return ret;
} }
@ -1444,15 +1383,9 @@ static int aic3x_remove(struct snd_soc_codec *codec)
aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
list_del(&aic3x->list); list_del(&aic3x->list);
if (gpio_is_valid(aic3x->gpio_reset) &&
!aic3x_is_shared_reset(aic3x)) {
gpio_set_value(aic3x->gpio_reset, 0);
gpio_free(aic3x->gpio_reset);
}
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
regulator_unregister_notifier(aic3x->supplies[i].consumer, regulator_unregister_notifier(aic3x->supplies[i].consumer,
&aic3x->disable_nb[i].nb); &aic3x->disable_nb[i].nb);
regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
return 0; return 0;
} }
@ -1460,13 +1393,16 @@ static int aic3x_remove(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_dev_aic3x = { static struct snd_soc_codec_driver soc_codec_dev_aic3x = {
.set_bias_level = aic3x_set_bias_level, .set_bias_level = aic3x_set_bias_level,
.idle_bias_off = true, .idle_bias_off = true,
.reg_cache_size = ARRAY_SIZE(aic3x_reg),
.reg_word_size = sizeof(u8),
.reg_cache_default = aic3x_reg,
.probe = aic3x_probe, .probe = aic3x_probe,
.remove = aic3x_remove, .remove = aic3x_remove,
.suspend = aic3x_suspend, .suspend = aic3x_suspend,
.resume = aic3x_resume, .resume = aic3x_resume,
.controls = aic3x_snd_controls,
.num_controls = ARRAY_SIZE(aic3x_snd_controls),
.dapm_widgets = aic3x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(aic3x_dapm_widgets),
.dapm_routes = intercon,
.num_dapm_routes = ARRAY_SIZE(intercon),
}; };
/* /*
@ -1483,6 +1419,16 @@ static const struct i2c_device_id aic3x_i2c_id[] = {
}; };
MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
static const struct reg_default aic3007_class_d[] = {
/* Class-D speaker driver init; datasheet p. 46 */
{ AIC3X_PAGE_SELECT, 0x0D },
{ 0xD, 0x0D },
{ 0x8, 0x5C },
{ 0x8, 0x5D },
{ 0x8, 0x5C },
{ AIC3X_PAGE_SELECT, 0x00 },
};
/* /*
* If the i2c layer weren't so broken, we could pass this kind of data * If the i2c layer weren't so broken, we could pass this kind of data
* around * around
@ -1494,7 +1440,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
struct aic3x_priv *aic3x; struct aic3x_priv *aic3x;
struct aic3x_setup_data *ai3x_setup; struct aic3x_setup_data *ai3x_setup;
struct device_node *np = i2c->dev.of_node; struct device_node *np = i2c->dev.of_node;
int ret; int ret, i;
u32 value; u32 value;
aic3x = devm_kzalloc(&i2c->dev, sizeof(struct aic3x_priv), GFP_KERNEL); aic3x = devm_kzalloc(&i2c->dev, sizeof(struct aic3x_priv), GFP_KERNEL);
@ -1503,7 +1449,13 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
return -ENOMEM; return -ENOMEM;
} }
aic3x->control_type = SND_SOC_I2C; aic3x->regmap = devm_regmap_init_i2c(i2c, &aic3x_regmap);
if (IS_ERR(aic3x->regmap)) {
ret = PTR_ERR(aic3x->regmap);
return ret;
}
regcache_cache_only(aic3x->regmap, true);
i2c_set_clientdata(i2c, aic3x); i2c_set_clientdata(i2c, aic3x);
if (pdata) { if (pdata) {
@ -1555,14 +1507,54 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
aic3x->model = id->driver_data; aic3x->model = id->driver_data;
if (gpio_is_valid(aic3x->gpio_reset) &&
!aic3x_is_shared_reset(aic3x)) {
ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset");
if (ret != 0)
goto err;
gpio_direction_output(aic3x->gpio_reset, 0);
}
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
aic3x->supplies[i].supply = aic3x_supply_names[i];
ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(aic3x->supplies),
aic3x->supplies);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
goto err_gpio;
}
if (aic3x->model == AIC3X_MODEL_3007) {
ret = regmap_register_patch(aic3x->regmap, aic3007_class_d,
ARRAY_SIZE(aic3007_class_d));
if (ret != 0)
dev_err(&i2c->dev, "Failed to init class D: %d\n",
ret);
}
ret = snd_soc_register_codec(&i2c->dev, ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_aic3x, &aic3x_dai, 1); &soc_codec_dev_aic3x, &aic3x_dai, 1);
return ret; return ret;
err_gpio:
if (gpio_is_valid(aic3x->gpio_reset) &&
!aic3x_is_shared_reset(aic3x))
gpio_free(aic3x->gpio_reset);
err:
return ret;
} }
static int aic3x_i2c_remove(struct i2c_client *client) static int aic3x_i2c_remove(struct i2c_client *client)
{ {
struct aic3x_priv *aic3x = i2c_get_clientdata(client);
snd_soc_unregister_codec(&client->dev); snd_soc_unregister_codec(&client->dev);
if (gpio_is_valid(aic3x->gpio_reset) &&
!aic3x_is_shared_reset(aic3x)) {
gpio_set_value(aic3x->gpio_reset, 0);
gpio_free(aic3x->gpio_reset);
}
return 0; return 0;
} }