ASoC: tas2781: Add a debugfs node for acoustic tuning

"Acoustic Tuning" debugfs 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>
Link: https://patch.msgid.link/20250507094616.210-1-shenghao-ding@ti.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Shenghao Ding 2025-05-07 17:46:15 +08:00 committed by Mark Brown
parent 0fa382a4f5
commit d75d38dc46
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 177 additions and 0 deletions

View File

@ -159,10 +159,33 @@ struct calidata {
unsigned int cali_dat_sz_per_dev;
};
/*
* To enable CONFIG_SND_SOC_TAS2781_ACOUST_I2C will create a bridge to the
* acoustic tuning tool which can tune the chips' acoustic effect. Due to the
* whole directly exposing the registers, there exist some potential risks. So
* this define is invisible in Kconfig, anyone who wants to use acoustic tool
* have to edit the source manually.
*/
#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
#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_ACOUST_I2C
struct acoustic_data acou_data;
#endif
struct tasdevice_fw *fmw;
struct gpio_desc *speaker_id;
struct gpio_desc *reset;

View File

@ -14,6 +14,9 @@
//
#include <linux/crc8.h>
#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
#include <linux/debugfs.h>
#endif
#include <linux/firmware.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
@ -1422,10 +1425,150 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
nctrls < i ? nctrls : i);
}
#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
/*
* 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_ACOUST_I2C
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;
@ -1499,6 +1642,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_ACOUST_I2C
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. */