[alsa-devel] [PATCH] ASoC: tlv320aic32x4: Add SPI support

Jeremy McDermond nh6z at nh6z.net
Sat Apr 16 03:10:06 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