From: Leslie Hsia <Leslie_Hsia@xxxxxxxxxxxxxxxx> This is initial amplifier driver for TAS5805M. Signed-off-by: Leslie Hsia <Leslie_Hsia@xxxxxxxxxxxxxxxx> --- diff -uprN -X linux-4.15-vanilla/Documentation/dontdiff linux-4.15-vanilla/sound/soc/codecs/Kconfig linux-4.15/sound/soc/codecs/Kconfig --- linux-4.15-vanilla/sound/soc/codecs/Kconfig 2020-03-12 21:21:05.179091822 +0800 +++ linux-4.15/sound/soc/codecs/Kconfig 2020-04-30 15:33:05.687598540 +0800 @@ -193,6 +193,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TAS5086 if I2C select SND_SOC_TAS571X if I2C select SND_SOC_TAS5720 if I2C + select SND_SOC_TAS5805M if I2C select SND_SOC_TAS6424 if I2C select SND_SOC_TDA7419 if I2C select SND_SOC_TFA9879 if I2C @@ -1207,6 +1208,10 @@ config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C +config SND_SOC_TAS5805M + tristate "Texas Instruments TAS5805M Mono Audio amplifier" + depends on I2C + config SND_SOC_TAS571X tristate "Texas Instruments TAS571x power amplifiers" depends on I2C diff -uprN -X linux-4.15-vanilla/Documentation/dontdiff linux-4.15-vanilla/sound/soc/codecs/Makefile linux-4.15/sound/soc/codecs/Makefile --- linux-4.15-vanilla/sound/soc/codecs/Makefile 2020-03-12 21:21:05.179091822 +0800 +++ linux-4.15/sound/soc/codecs/Makefile 2020-04-30 15:33:09.975593688 +0800 @@ -294,6 +294,7 @@ snd-soc-simple-amplifier-objs := simple- snd-soc-tpa6130a2-objs := tpa6130a2.o snd-soc-tas2552-objs := tas2552.o snd-soc-tas2562-objs := tas2562.o +snd-soc-tas5805m-objs := tas5805m.o obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o @@ -499,6 +500,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-so obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o +obj-$(CONFIG_SND_SOC_TAS5805M) += snd-soc-tas5805m.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o diff -uprN -X linux-4.15-vanilla/Documentation/dontdiff linux-4.15-vanilla/sound/soc/codecs/tas5805m.c linux-4.15/sound/soc/codecs/tas5805m.c --- linux-4.15-vanilla/sound/soc/codecs/tas5805m.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.15/sound/soc/codecs/tas5805m.c 2020-05-05 17:26:09.894332647 +0800 @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// tas5805m.c -- TAS5805M audio amplifier I2C driver +// +// Copyright (c) 2020 Pegatron Corp. +// +// Author: Leslie Hsia <Leslie_Hsia@xxxxxxxxxxxxxxxx> + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <sound/soc.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#define TAS5805M_REG_DEV_RESET 0x00 +#define TAS5805M_REG_DEV_CTL2 0x03 +#define TAS5805M_REG_DEV_BOOK 0x7F +#define TAS5805M_REG_VOL_CTL 0x4C +#define TAS5805M_DEV_STAT_CTL_MSK (BIT(1) | BIT(0)) +#define TAS5805M_DEV_STAT_DSLEEP 0x00 +#define TAS5805M_DEV_STAT_SLEEP 0x01 +#define TAS5805M_DEV_STAT_HIZ 0x02 +#define TAS5805M_DEV_STAT_PLAY 0x03 +#define TAS5805M_DEV_STAT_RESET 0x00 +#define TAS5805M_DIG_VOL_DB 0x3D +#define TAS5805M_DEV_BOOK_DEFAULT_PAGE 0x00 +#define TAS5805M_DRV_NAME "TAS5805M" +#define TAS5805M_AMP_DEV_NAME "TAS5805M" + +enum ti_ampfilier_type { + TAS5805M, +}; + +struct tas5805m_priv { + struct regmap *regmap; + /* mutex for getting the mutex and release */ + struct mutex lock; +}; + +const struct regmap_config tas5805m_regmap = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +/* Initialize the TAS5805M and set the volume to -6.5db, + * and set it to Play mode. + */ +static const struct reg_sequence tas5805m_init_dsp[] = { + { TAS5805M_REG_DEV_CTL2, TAS5805M_DEV_STAT_DSLEEP }, + { TAS5805M_REG_DEV_CTL2, TAS5805M_DEV_STAT_HIZ }, + { TAS5805M_REG_DEV_CTL2, TAS5805M_DEV_STAT_DSLEEP }, + /* set volume to -6.5dB */ + { TAS5805M_REG_VOL_CTL, TAS5805M_DIG_VOL_DB }, + { TAS5805M_REG_DEV_CTL2, TAS5805M_DEV_STAT_PLAY }, +}; + +/* Setting the TAS5805M state and save the config in the default page. */ +static int tas5805m_set_device_state(struct tas5805m_priv *tas5805m, + int state) +{ + int ret = 0; + + ret = regmap_write(tas5805m->regmap, TAS5805M_REG_DEV_RESET, + TAS5805M_DEV_STAT_RESET); + if (ret != 0) + return -EINVAL; + + /* Saving the config to the default page of the default book */ + ret = regmap_write(tas5805m->regmap, + TAS5805M_REG_DEV_BOOK, + TAS5805M_DEV_BOOK_DEFAULT_PAGE); + if (ret != 0) + return -EINVAL; + + ret = regmap_write(tas5805m->regmap, + TAS5805M_REG_DEV_RESET, + TAS5805M_DEV_STAT_RESET); + if (ret != 0) + return -EINVAL; + + ret = regmap_update_bits(tas5805m->regmap, + TAS5805M_REG_DEV_CTL2, + TAS5805M_DEV_STAT_CTL_MSK, state); + if (ret != 0) + return -EINVAL; + + return ret; +} + +/* Reset the TAS5805M and initialize TAS5805M again.*/ +static int tas5805m_reset(struct device *dev, + struct tas5805m_priv *priv) +{ + int ret = 0; + + ret = tas5805m_set_device_state(priv, TAS5805M_DEV_STAT_DSLEEP); + if (ret != 0) { + dev_err(dev, "Failed to set TAS5805M to Deep Sleep state: %d\n", + ret); + return -EINVAL; + } + + ret = tas5805m_set_device_state(priv, TAS5805M_DEV_STAT_HIZ); + if (ret != 0) { + dev_err(dev, "Failed to set TAS5805M to Hi-Z state: %d\n", ret); + return -EINVAL; + } + + /* Load the settings */ + ret = regmap_register_patch(priv->regmap, + tas5805m_init_dsp, + ARRAY_SIZE(tas5805m_init_dsp)); + if (ret != 0) { + dev_err(dev, "Failed to initialize TAS5805M: %d\n", ret); + return -EINVAL; + } + + return ret; +} + +static int tas5805m_probe(struct device *dev, struct regmap *regmap) +{ + struct tas5805m_priv *tas5805m; + + tas5805m = devm_kzalloc(dev, sizeof(struct tas5805m_priv), GFP_KERNEL); + if (!tas5805m) + return -ENOMEM; + + dev_set_drvdata(dev, tas5805m); + tas5805m->regmap = regmap; + + mutex_init(&tas5805m->lock); + + tas5805m_reset(dev, tas5805m); + + return 0; +} + +static int tas5805m_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tas5805m_priv *priv; + struct device *dev = &i2c->dev; + struct regmap_config config = tas5805m_regmap; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + + if (!priv) + return -ENOMEM; + + priv->regmap = devm_regmap_init_i2c(i2c, &config); + + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, priv); + + return tas5805m_probe(&i2c->dev, priv->regmap); +} + +static int tas5805m_i2c_remove(struct i2c_client *i2c) +{ + return 0; +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id tas5805m_acpi_match[] = { + {"TXNM5805", TAS5805M}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, tas5805m_acpi_match); +#else +#define st_accel_acpi_match NULL +#endif + +static const struct i2c_device_id tas5805m_i2c_id[] = { + { TAS5805M_AMP_DEV_NAME, TAS5805M }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, tas5805m_i2c_id); + +/* Shutdown procedures, configure the TAS5805M_REG_DEV_CTL2 register + * via i2c control port, and bring down power supplies. + */ +#ifdef CONFIG_PM_SLEEP +static int tas5805m_dsp_power_suspend(struct device *dev) +{ + int ret; + struct tas5805m_priv *tas5805m; + + tas5805m = dev_get_drvdata(dev); + if (!tas5805m) + return -ENOMEM; + + mutex_lock(&tas5805m->lock); + ret = tas5805m_set_device_state(tas5805m, TAS5805M_DEV_STAT_HIZ); + if (ret != 0) { + dev_err(dev, "Failed to set TAS5805M to Hi-Z state: %d\n", ret); + return -EINVAL; + } + + mutex_unlock(&tas5805m->lock); + + return 0; +} + +/* Startup procedures, + * configure the pin with proper settings for i2c device address, + * binre up power supplies, set the device into Hiz state + * and enable DSP via i2c control port, + * initialize the DSP coefficient, then set the device to Play mode + */ +static int tas5805m_dsp_power_resume(struct device *dev) +{ + struct tas5805m_priv *tas5805m; + + tas5805m = dev_get_drvdata(dev); + if (!tas5805m) + return -ENOMEM; + + mutex_lock(&tas5805m->lock); + tas5805m_reset(dev, tas5805m); + mutex_unlock(&tas5805m->lock); + + return 0; +} + +static const struct dev_pm_ops tas5805m_dsp_power_pm = { + .suspend = tas5805m_dsp_power_suspend, + .resume = tas5805m_dsp_power_resume, +}; +#define tas5805m_dsp_power_pm_ops (&tas5805m_dsp_power_pm) +#else +#define tas5805m_dsp_power_pm_ops NULL +#endif + +static struct i2c_driver tas5805m_i2c_driver = { + .driver = { + .name = TAS5805M_DRV_NAME, + .acpi_match_table = ACPI_PTR(tas5805m_acpi_match), + .pm = tas5805m_dsp_power_pm_ops, + }, + .probe = tas5805m_i2c_probe, + .remove = tas5805m_i2c_remove, + .id_table = tas5805m_i2c_id, +}; + +module_i2c_driver(tas5805m_i2c_driver); + +MODULE_AUTHOR("Leslie Hsia <Leslie_Hsia@xxxxxxxxxxxxxxxx>"); +MODULE_AUTHOR("Andy Liu <andy-liu@xxxxxx>"); +MODULE_DESCRIPTION("TAS5805M Audio Amplifier I2C Driver"); +MODULE_LICENSE("GPL v2"); This e-mail and its attachment may contain information that is confidential or privileged, and are solely for the use of the individual to whom this e-mail is addressed. If you are not the intended recipient or have received it accidentally, please immediately notify the sender by reply e-mail and destroy all copies of this email and its attachment. Please be advised that any unauthorized use, disclosure, distribution or copying of this email or its attachment is strictly prohibited. 本電子郵件及其附件可能含有機密或依法受特殊管制之資訊,僅供本電子郵件之受文者使用。台端如非本電子郵件之受文者或誤收本電子郵件,請立即回覆郵件通知寄件人,並銷毀本電子郵件之所有複本及附件。任何未經授權而使用、揭露、散佈或複製本電子郵件或其附件之行為,皆嚴格禁止 。