[PATCH v4] ASoC: tas2781: Add a debufs node for acoustic tuning

"Acoustic Tuning" debufs node is a bridge to the acoustic tuning tool which can tune the chips' acoustic effect.
Signed-off-by: Shenghao Ding shenghao-ding@ti.com
--- v4: - Add name_prefix as part of the name of debugfs node in case of multiple devices. v3: - Remove unnessary indentation. - Add a special build CONFIG_SND_SOC_TAS2781_I2C_ACOUST for acoustic tuning debufs node v2: - Implement the "Acoustic Tuning" as debugfs node instead of kcontrol. v1: - Add "Acoustic Tuning" kcontrol for acoustic tuning. --- include/sound/tas2781.h | 16 ++++ sound/soc/codecs/Kconfig | 16 ++++ sound/soc/codecs/tas2781-i2c.c | 154 +++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+)
diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index 8192a94d783a..88f45e458936 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -161,10 +161,26 @@ struct calidata { unsigned int cali_dat_sz_per_dev; };
+#ifdef CONFIG_SND_SOC_TAS2781_I2C_ACOUST +#define TASDEV_DATA_PAYLOAD_SIZE 128 +struct acoustic_data { + unsigned char len; + unsigned char id; + unsigned char addr; + unsigned char book; + unsigned char page; + unsigned char reg; + unsigned char data[TASDEV_DATA_PAYLOAD_SIZE]; +}; +#endif + struct tasdevice_priv { struct tasdevice tasdevice[TASDEVICE_MAX_CHANNELS]; struct tasdevice_rca rcabin; struct calidata cali_data; +#ifdef CONFIG_SND_SOC_TAS2781_I2C_ACOUST + struct acoustic_data acou_data; +#endif struct tasdevice_fw *fmw; struct gpio_desc *speaker_id; struct gpio_desc *reset; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index fe10aaf9856e..498c63eb6837 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2016,6 +2016,22 @@ config SND_SOC_TAS2781_I2C algo coefficient setting, for one, two or even multiple TAS2781 chips.
+if SND_SOC_TAS2781_I2C + +config SND_SOC_TAS2781_I2C_ACOUST + tristate "Acoustic Tuning tool for TAS2781" + depends on DEBUG_FS + default n + help + Enable debufs node for acoustic tuning for TAS2563/TAS2781, which + is a bridge between the acoustic tuning tool and chips. With this + node, acoustic can be tuned on chips freely from user space. + Customers also can develop his own acoustic tool with this node. + + If unsure select "N". + +endif ## SND_SOC_TAS2781_I2C + config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 9a329c561a53..059a46bde2d1 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -14,6 +14,9 @@ //
#include <linux/crc8.h> +#ifdef CONFIG_SND_SOC_TAS2781_I2C_ACOUST +#include <linux/debugfs.h> +#endif #include <linux/firmware.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> @@ -1423,10 +1426,150 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) nctrls < i ? nctrls : i); }
+#ifdef CONFIG_SND_SOC_TAS2781_I2C_ACOUST +/* + * This debugfs node is a bridge to the acoustic tuning application + * tool which can tune the chips' acoustic effect. + * + * package structure for PPC3 communications: + * Pkg len (1 byte) + * Pkg id (1 byte, 'r' or 'w') + * Dev id (1 byte, i2c address) + * Book id (1 byte) + * Page id (1 byte) + * Reg id (1 byte) + * switch (pkg id) { + * case 'w': + * 1 byte, length of data to read + * case 'r': + * data payload (1~128 bytes) + * } + */ +static ssize_t acoustic_ctl_read(struct file *file, char __user *to, + size_t count, loff_t *ppos) +{ + struct snd_soc_component *comp = file->private_data; + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp); + struct acoustic_data *p = &tas_priv->acou_data; + int ret = -1; + + if (p->id == 'r' && p->len == count && count <= sizeof(*p)) + ret = simple_read_from_buffer(to, count, ppos, p, p->len); + else + dev_err(tas_priv->dev, "Not ready for get.\n"); + return ret; +} + +static ssize_t acoustic_ctl_write(struct file *file, + const char __user *from, size_t count, loff_t *ppos) +{ + struct snd_soc_component *comp = file->private_data; + struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); + struct acoustic_data *p = &priv->acou_data; + unsigned int max_pkg_len = sizeof(*p); + unsigned char *src; + int j, len, reg, val; + unsigned short chn; + int ret = -1; + + if (count > sizeof(*p)) { + dev_err(priv->dev, "count(%u) is larger than max(%u).\n", + (unsigned int)count, max_pkg_len); + return ret; + } + + src = memdup_user(from, count); + if (IS_ERR(src)) + return PTR_ERR(src); + + if (src[0] > max_pkg_len && src[0] != count) { + dev_err(priv->dev, "pkg(%u), max(%u), count(%u) dismatch.\n", + src[0], max_pkg_len, (unsigned int)count); + ret = 0; + goto exit; + } + + switch (src[1]) { + case 'r': + /* length of data to read */ + len = src[6]; + break; + case 'w': + /* Skip 6 bytes for package type and register address */ + len = src[0] - 6; + break; + default: + dev_err(priv->dev, "%s Wrong code %02x.\n", __func__, src[1]); + ret = 0; + goto exit; + } + + if (len < 1) { + dev_err(priv->dev, "pkg fmt invalid %02x.\n", len); + ret = 0; + goto exit; + } + + for (j = 0; j < priv->ndev; j++) + if (src[2] == priv->tasdevice[j].dev_addr) { + chn = j; + break; + } + if (j >= priv->ndev) { + dev_err(priv->dev, "no such device 0x%02x.\n", src[2]); + ret = 0; + goto exit; + } + + reg = TASDEVICE_REG(src[3], src[4], src[5]); + + guard(mutex)(&priv->codec_lock); + + if (src[1] == 'w') { + if (len > 1) + ret = tasdevice_dev_bulk_write(priv, chn, reg, + &src[6], len); + else + ret = tasdevice_dev_write(priv, chn, reg, src[6]); + } else { + struct acoustic_data *p = &priv->acou_data; + + memcpy(p, src, 6); + if (len > 1) { + ret = tasdevice_dev_bulk_read(priv, chn, reg, + p->data, len); + } else { + ret = tasdevice_dev_read(priv, chn, reg, &val); + p->data[0] = val; + } + p->len = len + 6; + } + + if (ret) + dev_err(priv->dev, "i2c communication error.\n"); + else + ret = count; +exit: + kfree(src); + return ret; +} + +static const struct file_operations acoustic_ctl_fops = { + .open = simple_open, + .read = acoustic_ctl_read, + .write = acoustic_ctl_write, +}; +#endif + static void tasdevice_fw_ready(const struct firmware *fmw, void *context) { struct tasdevice_priv *tas_priv = context; +#ifdef CONFIG_SND_SOC_TAS2781_I2C_ACOUST + struct snd_soc_component *comp = tas_priv->codec; + struct dentry *debugfs_root = comp->debugfs_root; + char *acoustic_debugfs_node; +#endif int ret = 0; int i;
@@ -1500,6 +1643,17 @@ static void tasdevice_fw_ready(const struct firmware *fmw,
tasdevice_prmg_load(tas_priv, 0); tas_priv->cur_prog = 0; + +#ifdef CONFIG_SND_SOC_TAS2781_I2C_ACOUST + if (tas_priv->name_prefix) + acoustic_debugfs_node = devm_kasprintf(tas_priv->dev, + GFP_KERNEL, "%s_acoustic_ctl", tas_priv->name_prefix); + else + acoustic_debugfs_node = devm_kstrdup(tas_priv->dev, + "acoustic_ctl", GFP_KERNEL); + debugfs_create_file(acoustic_debugfs_node, 0644, debugfs_root, + comp, &acoustic_ctl_fops); +#endif out: if (tas_priv->fw_state == TASDEVICE_RCA_FW_OK) { /* If DSP FW fail, DSP kcontrol won't be created. */

On Mon, May 05, 2025 at 09:19:49AM +0800, Shenghao Ding wrote:
"Acoustic Tuning" debufs node is a bridge to the acoustic tuning tool which can tune the chips' acoustic effect.
include/sound/tas2781.h | 16 ++++ sound/soc/codecs/Kconfig | 16 ++++ sound/soc/codecs/tas2781-i2c.c | 154 +++++++++++++++++++++++++++++++++
One of the key things about the define that's used for the regmap API version of this is that it's not visible in Kconfig, you have to edit the kernel source due to the whole directly exposing the registers things. It'd be too easy for a distro or something to just turn the define on by mistake otherwise. That's why it's a define in the code instead. The code itself looks fine.

Thanks for your comments, Broonie.
-----Original Message----- From: Mark Brown broonie@kernel.org Sent: Monday, May 5, 2025 6:35 PM To: Ding, Shenghao shenghao-ding@ti.com Cc: andriy.shevchenko@linux.intel.com; tiwai@suse.de; 13916275206@139.com; 13564923607@139.com; alsa-devel@alsa- project.org; Xu, Baojun baojun.xu@ti.com Subject: [EXTERNAL] Re: [PATCH v4] ASoC: tas2781: Add a debufs node for acoustic tuning
On Mon, May 05, 2025 at 09:19:49AM +0800, Shenghao Ding wrote:
"Acoustic Tuning" debufs node is a bridge to the acoustic tuning tool which can tune the chips' acoustic effect.
include/sound/tas2781.h | 16 ++++ sound/soc/codecs/Kconfig | 16 ++++ sound/soc/codecs/tas2781-i2c.c | 154 +++++++++++++++++++++++++++++++++
One of the key things about the define that's used for the regmap API version of this is that it's not visible in Kconfig, you have to edit the kernel source due to the whole directly exposing the registers things. It'd be too easy for a distro or something to just turn the define on by mistake otherwise. That's why it's a define in the code instead. The code itself looks fine.
I need to drop the following setting in Kconfig, right? +if SND_SOC_TAS2781_I2C + +config SND_SOC_TAS2781_I2C_ACOUST ......................... +endif ## SND_SOC_TAS2781_I2C +
As to REGMAP API, when enabling SND_SOC_TAS2781_COMLIB_I2C will select REGMAP_I2C. And enabling SND_SOC_TAS2781_I2C will select SND_SOC_TAS2781_COMLIB_I2C. So whether SND_SOC_TAS2781_I2C_ACOUST is enabled or not, REGMAP_I2C is selected.

On Tue, May 06, 2025 at 07:37:01AM +0000, Ding, Shenghao wrote:
One of the key things about the define that's used for the regmap API version of this is that it's not visible in Kconfig, you have to edit the kernel source due to the whole directly exposing the registers things. It'd be too easy for a distro or something to just turn the define on by mistake otherwise. That's why it's a define in the code instead. The code itself looks fine.
I need to drop the following setting in Kconfig, right? +if SND_SOC_TAS2781_I2C
+config SND_SOC_TAS2781_I2C_ACOUST ......................... +endif ## SND_SOC_TAS2781_I2C
Yes.
As to REGMAP API, when enabling SND_SOC_TAS2781_COMLIB_I2C will select REGMAP_I2C. And enabling SND_SOC_TAS2781_I2C will select SND_SOC_TAS2781_COMLIB_I2C. So whether SND_SOC_TAS2781_I2C_ACOUST is enabled or not, REGMAP_I2C is selected.
Yes, the selecting of regmap is no problem - I was referring to the regmap debugfs interface for writing to registers here.
participants (3)
-
Ding, Shenghao
-
Mark Brown
-
Shenghao Ding