[alsa-devel] [PATCH] ASoC: tlv320aic32x4: Add SPI support
Jeremy McDermond
nh6z at nh6z.net
Fri Apr 15 20:30:59 CEST 2016
The TLV320AIC32x4 support the control channel over either SPI or I2C.
Added the proper interfacing with regmap to make the driver able to
handle either possibility.
Signed-off-by: Jeremy McDermond <nh6z at nh6z.net>
---
sound/soc/codecs/Kconfig | 5 +-
sound/soc/codecs/tlv320aic32x4.c | 181 +++++++++++++++++++++++++++++++++++----
2 files changed, 165 insertions(+), 21 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 907c804..1ddf7b8 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -129,7 +129,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TLV320AIC31XX if I2C
- select SND_SOC_TLV320AIC32X4 if I2C
+ select SND_SOC_TLV320AIC32X4 if SND_SOC_I2C_AND_SPI
select SND_SOC_TLV320AIC3X if I2C
select SND_SOC_TPA6130A2 if I2C
select SND_SOC_TLV320DAC33 if I2C
@@ -768,7 +768,8 @@ config SND_SOC_TLV320AIC31XX
select REGMAP_I2C
config SND_SOC_TLV320AIC32X4
- tristate
+ tristate "Texas Instruments TLV320AIC32x4 CODECs"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_TLV320AIC3X
tristate "Texas Instruments TLV320AIC3x CODECs"
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index f2d3191..1c888b7 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -31,6 +31,7 @@
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
+#include <linux/spi/spi.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/clk.h>
@@ -287,15 +288,6 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = {
},
};
-static const struct regmap_config aic32x4_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = AIC32X4_RMICPGAVOL,
- .ranges = aic32x4_regmap_pages,
- .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages),
-};
-
static inline int aic32x4_get_divs(int mclk, int rate)
{
int i;
@@ -668,7 +660,7 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
};
static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
- struct device_node *np)
+ struct device_node *np, struct device *dev)
{
aic32x4->swapdacs = false;
aic32x4->micpga_routing = 0;
@@ -777,6 +769,23 @@ error_ldo:
return ret;
}
+static const struct of_device_id aic32x4_of_id[] = {
+ { .compatible = "ti,tlv320aic32x4", },
+ { /* senitel */ }
+};
+MODULE_DEVICE_TABLE(of, aic32x4_of_id);
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+static const struct regmap_config aic32x4_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AIC32X4_RMICPGAVOL,
+ .ranges = aic32x4_regmap_pages,
+ .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages),
+};
+
static int aic32x4_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -790,7 +799,7 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
if (aic32x4 == NULL)
return -ENOMEM;
- aic32x4->regmap = devm_regmap_init_i2c(i2c, &aic32x4_regmap);
+ aic32x4->regmap = devm_regmap_init_i2c(i2c, &aic32x4_i2c_regmap);
if (IS_ERR(aic32x4->regmap))
return PTR_ERR(aic32x4->regmap);
@@ -802,7 +811,7 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
aic32x4->micpga_routing = pdata->micpga_routing;
aic32x4->rstn_gpio = pdata->rstn_gpio;
} else if (np) {
- ret = aic32x4_parse_dt(aic32x4, np);
+ ret = aic32x4_parse_dt(aic32x4, np, &i2c->dev);
if (ret) {
dev_err(&i2c->dev, "Failed to parse DT node\n");
return ret;
@@ -862,12 +871,6 @@ static const struct i2c_device_id aic32x4_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
-static const struct of_device_id aic32x4_of_id[] = {
- { .compatible = "ti,tlv320aic32x4", },
- { /* senitel */ }
-};
-MODULE_DEVICE_TABLE(of, aic32x4_of_id);
-
static struct i2c_driver aic32x4_i2c_driver = {
.driver = {
.name = "tlv320aic32x4",
@@ -877,8 +880,148 @@ static struct i2c_driver aic32x4_i2c_driver = {
.remove = aic32x4_i2c_remove,
.id_table = aic32x4_i2c_id,
};
+#endif
-module_i2c_driver(aic32x4_i2c_driver);
+#if defined(CONFIG_SPI_MASTER)
+
+static const struct regmap_config aic32x4_spi_regmap = {
+ .reg_bits = 7,
+ .pad_bits = 1,
+ .val_bits = 8,
+ .read_flag_mask = 0x01,
+
+ .max_register = AIC32X4_RMICPGAVOL,
+ .ranges = aic32x4_regmap_pages,
+ .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages),
+};
+
+static int aic32x4_spi_probe(struct spi_device *spi)
+{
+ struct aic32x4_pdata *pdata = spi->dev.platform_data;
+ struct aic32x4_priv *aic32x4;
+ struct device_node *np = spi->dev.of_node;
+ int ret;
+
+ aic32x4 = devm_kzalloc(&spi->dev, sizeof(struct aic32x4_priv),
+ GFP_KERNEL);
+ if (aic32x4 == NULL)
+ return -ENOMEM;
+
+ aic32x4->regmap = devm_regmap_init_spi(spi, &aic32x4_spi_regmap);
+ if (IS_ERR(aic32x4->regmap))
+ return PTR_ERR(aic32x4->regmap);
+
+ spi_set_drvdata(spi, aic32x4);
+
+ if (pdata) {
+ aic32x4->power_cfg = pdata->power_cfg;
+ aic32x4->swapdacs = pdata->swapdacs;
+ aic32x4->micpga_routing = pdata->micpga_routing;
+ aic32x4->rstn_gpio = pdata->rstn_gpio;
+ } else if (np) {
+ ret = aic32x4_parse_dt(aic32x4, np, &spi->dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to parse DT node\n");
+ return ret;
+ }
+ } else {
+ aic32x4->power_cfg = 0;
+ aic32x4->swapdacs = false;
+ aic32x4->micpga_routing = 0;
+ aic32x4->rstn_gpio = -1;
+ }
+
+ aic32x4->mclk = devm_clk_get(&spi->dev, "mclk");
+ if (IS_ERR(aic32x4->mclk)) {
+ dev_err(&spi->dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n");
+ return PTR_ERR(aic32x4->mclk);
+ }
+
+ if (gpio_is_valid(aic32x4->rstn_gpio)) {
+ ret = devm_gpio_request_one(&spi->dev, aic32x4->rstn_gpio,
+ GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn");
+ if (ret != 0)
+ return ret;
+ }
+
+ ret = aic32x4_setup_regulators(&spi->dev, aic32x4);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to setup regulators\n");
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_aic32x4, &aic32x4_dai, 1);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register codec\n");
+ aic32x4_disable_regulators(aic32x4);
+ return ret;
+ }
+
+ spi_set_drvdata(spi, aic32x4);
+
+ return 0;
+}
+
+static int aic32x4_spi_remove(struct spi_device *dev)
+{
+ struct aic32x4_priv *aic32x4 = spi_get_drvdata(dev);
+
+ aic32x4_disable_regulators(aic32x4);
+
+ snd_soc_unregister_codec(&dev->dev);
+ return 0;
+}
+
+static const struct spi_device_id aic32x4_spi_id[] = {
+ { "tlv320aic32x4", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, aic32x4_spi_id);
+
+static struct spi_driver aic32x4_spi_driver = {
+ .driver = {
+ .name = "tlv320aic32x4",
+ .owner = THIS_MODULE,
+ .of_match_table = aic32x4_of_id,
+ },
+ .probe = aic32x4_spi_probe,
+ .remove = aic32x4_spi_remove,
+ .id_table = aic32x4_spi_id,
+};
+#endif
+
+static int __init aic32x4_modinit(void)
+{
+ int ret = 0;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&aic32x4_i2c_driver);
+ if (ret)
+ pr_err("Failed to register tlv320aic32x4 I2C driver: %d\n",
+ ret);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&aic32x4_spi_driver);
+ if (ret)
+ pr_err("Failed to register tlv320aic32x4 SPI driver: %d\n",
+ ret);
+#endif
+
+ return ret;
+}
+module_init(aic32x4_modinit);
+
+static void __exit aic32x4_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&aic32x4_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&aic32x4_spi_driver);
+#endif
+}
+module_exit(aic32x4_exit);
MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver");
MODULE_AUTHOR("Javier Martin <javier.martin at vista-silicon.com>");
--
2.5.0
More information about the Alsa-devel
mailing list