All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ASoC: tas5805m: Add TAS5805M amplifier driver
@ 2020-05-05 10:36 Leslie Hsia(夏邦進_Pegatron)
  2020-05-05 11:15 ` Mark Brown
  0 siblings, 1 reply; 2+ messages in thread
From: Leslie Hsia(夏邦進_Pegatron) @ 2020-05-05 10:36 UTC (permalink / raw)
  To: Mark Brown
  Cc: knaack.h, lars, pmeerw, linux-iio, linux-kernel,
	Hermes Hsieh(謝旻劭_Pegatron),
	jesse.sung, jic23

From: Leslie Hsia <Leslie_Hsia@pegatroncorp.com>

This is initial amplifier driver for TAS5805M.

Signed-off-by: Leslie Hsia <Leslie_Hsia@pegatroncorp.com>
---
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@pegatroncorp.com>
+
+#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@pegatroncorp.com>");
+MODULE_AUTHOR("Andy Liu <andy-liu@ti.com>");
+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.

本電子郵件及其附件可能含有機密或依法受特殊管制之資訊,僅供本電子郵件之受文者使用。台端如非本電子郵件之受文者或誤收本電子郵件,請立即回覆郵件通知寄件人,並銷毀本電子郵件之所有複本及附件。任何未經授權而使用、揭露、散佈或複製本電子郵件或其附件之行為,皆嚴格禁止 。


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] ASoC: tas5805m: Add TAS5805M amplifier driver
  2020-05-05 10:36 [PATCH] ASoC: tas5805m: Add TAS5805M amplifier driver Leslie Hsia(夏邦進_Pegatron)
@ 2020-05-05 11:15 ` Mark Brown
  0 siblings, 0 replies; 2+ messages in thread
From: Mark Brown @ 2020-05-05 11:15 UTC (permalink / raw)
  To: Leslie Hsia(夏邦進_Pegatron)
  Cc: knaack.h, lars, pmeerw, linux-iio, linux-kernel,
	Hermes Hsieh(謝旻劭_Pegatron),
	jesse.sung, jic23

[-- Attachment #1: Type: text/plain, Size: 3046 bytes --]

On Tue, May 05, 2020 at 10:36:29AM +0000, Leslie Hsia(夏邦進_Pegatron) wrote:

> +struct tas5805m_priv {
> +       struct regmap *regmap;
> +       /* mutex for getting the mutex and release */
> +       struct mutex lock;
> +};

This actually appears to be for device initialization somehow - the
comment isn't super enlightening.  It's not clear to me that there are
any potential races here - the PM stuff and device probe and removal are
already locked further up the stack.

> +/* 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 },
> +};

You should use the chip defaults unless the configuration is so obvious
that almost all users would want the same setting.  This avoids the
kernel having to take decisions about which use case to support, a
volume that makes sense for one system may not make sense for others.

> +/* 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);

regmap has support for paging, you should probably describe the pages in
the regmap config.  Right now I don't think things are going to work
well since there are no pages described but caching is enabled which
means that regmap will cache values that are getting paged out.

> +static int tas5805m_i2c_remove(struct i2c_client *i2c)
> +{
> +       return 0;
> +}

You can just delete empty functions like this, if things can safely be
left empty then they can just be removed entirely.

> +MODULE_DEVICE_TABLE(acpi, tas5805m_acpi_match);
> +#else
> +#define st_accel_acpi_match NULL
> +#endif

This is redundant, ACPI_PTR() won't reference the value unless ACPI is
enabled.

> +#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,

Use SET_SYSTEM_SLEEP_PM_OPS() and remove the else case above.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-05-05 11:15 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-05 10:36 [PATCH] ASoC: tas5805m: Add TAS5805M amplifier driver Leslie Hsia(夏邦進_Pegatron)
2020-05-05 11:15 ` Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.