summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/tas2781-i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/tas2781-i2c.c')
-rw-r--r--sound/soc/codecs/tas2781-i2c.c377
1 files changed, 320 insertions, 57 deletions
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index a730ab6ad4e3..c40d8f754d89 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -2,7 +2,7 @@
//
// ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier
//
-// Copyright (C) 2022 - 2024 Texas Instruments Incorporated
+// Copyright (C) 2022 - 2025 Texas Instruments Incorporated
// https://www.ti.com
//
// The TAS2563/TAS2781 driver implements a flexible and configurable
@@ -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>
@@ -28,6 +31,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tas2781.h>
+#include <sound/tas2781-comlib-i2c.h>
#include <sound/tlv.h>
#include <sound/tas2563-tlv.h>
#include <sound/tas2781-tlv.h>
@@ -349,13 +353,31 @@ static int calib_data_get(struct tasdevice_priv *tas_priv, int reg,
return rc;
}
+static int partial_cali_data_update(int *reg, int j)
+{
+ switch (tas2781_cali_start_reg[j].reg) {
+ case 0:
+ return reg[0];
+ case TAS2781_PRM_PLT_FLAG_REG:
+ return reg[1];
+ case TAS2781_PRM_SINEGAIN_REG:
+ return reg[2];
+ case TAS2781_PRM_SINEGAIN2_REG:
+ return reg[3];
+ default:
+ return 0;
+ }
+}
+
static void sngl_calib_start(struct tasdevice_priv *tas_priv, int i,
int *reg, unsigned char *dat)
{
struct tasdevice *tasdev = tas_priv->tasdevice;
struct bulk_reg_val *p = tasdev[i].cali_data_backup;
+ struct bulk_reg_val *t = &tasdev[i].alp_cali_bckp;
const int sum = ARRAY_SIZE(tas2781_cali_start_reg);
- int j;
+ unsigned char val[4];
+ int j, r;
if (p == NULL)
return;
@@ -370,30 +392,23 @@ static void sngl_calib_start(struct tasdevice_priv *tas_priv, int i,
tasdevice_dev_read(tas_priv, i, p[j].reg,
(int *)&p[j].val[0]);
} else {
- switch (tas2781_cali_start_reg[j].reg) {
- case 0: {
- if (!reg[0])
- continue;
- p[j].reg = reg[0];
- }
- break;
- case TAS2781_PRM_PLT_FLAG_REG:
- p[j].reg = reg[1];
- break;
- case TAS2781_PRM_SINEGAIN_REG:
- p[j].reg = reg[2];
- break;
- case TAS2781_PRM_SINEGAIN2_REG:
- p[j].reg = reg[3];
- break;
+ if (!tas_priv->dspbin_typ) {
+ r = partial_cali_data_update(reg, j);
+ if (r)
+ p[j].reg = r;
}
- tasdevice_dev_bulk_read(tas_priv, i, p[j].reg,
- p[j].val, 4);
+
+ if (p[j].reg)
+ tasdevice_dev_bulk_read(tas_priv, i, p[j].reg,
+ p[j].val, 4);
}
}
+ if (tas_priv->dspbin_typ == TASDEV_ALPHA)
+ tasdevice_dev_bulk_read(tas_priv, i, t->reg, t->val, 4);
+
/* Update the setting for calibration */
- for (j = 0; j < sum - 2; j++) {
+ for (j = 0; j < sum - 4; j++) {
if (p[j].val_len == 1) {
if (p[j].is_locked)
tasdevice_dev_write(tas_priv, i,
@@ -401,17 +416,33 @@ static void sngl_calib_start(struct tasdevice_priv *tas_priv, int i,
TAS2781_TEST_PAGE_UNLOCK);
tasdevice_dev_write(tas_priv, i, p[j].reg,
tas2781_cali_start_reg[j].val[0]);
- } else {
- if (!p[j].reg)
- continue;
- tasdevice_dev_bulk_write(tas_priv, i, p[j].reg,
- (unsigned char *)
- tas2781_cali_start_reg[j].val, 4);
}
}
- tasdevice_dev_bulk_write(tas_priv, i, p[j].reg, &dat[1], 4);
- tasdevice_dev_bulk_write(tas_priv, i, p[j + 1].reg, &dat[5], 4);
+ if (tas_priv->dspbin_typ == TASDEV_ALPHA) {
+ val[0] = 0x00;
+ val[1] = 0x00;
+ val[2] = 0x21;
+ val[3] = 0x8e;
+ } else {
+ val[0] = tas2781_cali_start_reg[j].val[0];
+ val[1] = tas2781_cali_start_reg[j].val[1];
+ val[2] = tas2781_cali_start_reg[j].val[2];
+ val[3] = tas2781_cali_start_reg[j].val[3];
+ }
+ tasdevice_dev_bulk_write(tas_priv, i, p[j].reg, val, 4);
+ tasdevice_dev_bulk_write(tas_priv, i, p[j + 1].reg,
+ (unsigned char *)tas2781_cali_start_reg[j + 1].val, 4);
+ tasdevice_dev_bulk_write(tas_priv, i, p[j + 2].reg, &dat[1], 4);
+ tasdevice_dev_bulk_write(tas_priv, i, p[j + 3].reg, &dat[5], 4);
+ if (tas_priv->dspbin_typ == TASDEV_ALPHA) {
+ val[0] = 0x00;
+ val[1] = 0x00;
+ val[2] = 0x2a;
+ val[3] = 0x0b;
+
+ tasdevice_dev_bulk_read(tas_priv, i, t->reg, val, 4);
+ }
}
static int tas2781_calib_start_put(struct snd_kcontrol *kcontrol,
@@ -452,14 +483,15 @@ static int tas2781_calib_start_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static void tas2781_calib_stop_put(struct tasdevice_priv *tas_priv)
+static void tas2781_calib_stop_put(struct tasdevice_priv *priv)
{
const int sum = ARRAY_SIZE(tas2781_cali_start_reg);
int i, j;
- for (i = 0; i < tas_priv->ndev; i++) {
- struct tasdevice *tasdev = tas_priv->tasdevice;
+ for (i = 0; i < priv->ndev; i++) {
+ struct tasdevice *tasdev = priv->tasdevice;
struct bulk_reg_val *p = tasdev[i].cali_data_backup;
+ struct bulk_reg_val *t = &tasdev[i].alp_cali_bckp;
if (p == NULL)
continue;
@@ -467,18 +499,21 @@ static void tas2781_calib_stop_put(struct tasdevice_priv *tas_priv)
for (j = 0; j < sum; j++) {
if (p[j].val_len == 1) {
if (p[j].is_locked)
- tasdevice_dev_write(tas_priv, i,
+ tasdevice_dev_write(priv, i,
TAS2781_TEST_UNLOCK_REG,
TAS2781_TEST_PAGE_UNLOCK);
- tasdevice_dev_write(tas_priv, i, p[j].reg,
+ tasdevice_dev_write(priv, i, p[j].reg,
p[j].val[0]);
} else {
if (!p[j].reg)
continue;
- tasdevice_dev_bulk_write(tas_priv, i, p[j].reg,
+ tasdevice_dev_bulk_write(priv, i, p[j].reg,
p[j].val, 4);
}
}
+
+ if (priv->dspbin_typ == TASDEV_ALPHA)
+ tasdevice_dev_bulk_write(priv, i, t->reg, t->val, 4);
}
}
@@ -590,16 +625,20 @@ static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol,
i += 2;
priv->is_user_space_calidata = true;
- p->r0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
- i += 3;
- p->r0_low_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
- i += 3;
- p->invr0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
- i += 3;
- p->pow_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
- i += 3;
- p->tlimit_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
- i += 3;
+ if (priv->dspbin_typ == TASDEV_BASIC) {
+ p->r0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->r0_low_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->invr0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->pow_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->tlimit_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ } else {
+ i += 15;
+ }
memcpy(dst, &src[i], cali_data->total_sz);
return 1;
@@ -646,12 +685,19 @@ static int tasdev_tf_data_get(struct snd_kcontrol *kcontrol,
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *) kcontrol->private_value;
unsigned char *dst = ucontrol->value.bytes.data;
- unsigned int reg;
+ unsigned int reg = TAS2781_RUNTIME_RE_REG_TF;
+
+ if (tas_priv->chip_id == TAS2781) {
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
- if (tas_priv->chip_id == TAS2781)
reg = TAS2781_RUNTIME_RE_REG_TF;
- else
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->tf_reg[0], p->tf_reg[1],
+ p->tf_reg[2]);
+ } else {
reg = TAS2563_RUNTIME_RE_REG_TF;
+ }
guard(mutex)(&tas_priv->codec_lock);
dst[0] = bytes_ext->max;
@@ -666,12 +712,19 @@ static int tasdev_re_data_get(struct snd_kcontrol *kcontrol,
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *) kcontrol->private_value;
unsigned char *dst = ucontrol->value.bytes.data;
- unsigned int reg;
+ unsigned int reg = TAS2781_RUNTIME_RE_REG;
- if (tas_priv->chip_id == TAS2781)
- reg = TAS2781_RUNTIME_RE_REG;
- else
+ if (tas_priv->chip_id == TAS2781) {
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
+
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->r0_reg[0], p->r0_reg[1],
+ p->r0_reg[2]);
+ } else {
reg = TAS2563_RUNTIME_RE_REG;
+ }
+
guard(mutex)(&tas_priv->codec_lock);
dst[0] = bytes_ext->max;
return calib_data_get(tas_priv, reg, &dst[1]);
@@ -705,11 +758,16 @@ static int tasdev_XMA1_data_get(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *) kcontrol->private_value;
unsigned char *dst = ucontrol->value.bytes.data;
unsigned int reg = TASDEVICE_XM_A1_REG;
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->a1_reg[0], p->a1_reg[1], p->a1_reg[2]);
+
guard(mutex)(&tas_priv->codec_lock);
dst[0] = bytes_ext->max;
return calib_data_get(tas_priv, reg, &dst[1]);
@@ -720,11 +778,16 @@ static int tasdev_XMA2_data_get(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *) kcontrol->private_value;
unsigned char *dst = ucontrol->value.bytes.data;
unsigned int reg = TASDEVICE_XM_A2_REG;
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->a2_reg[0], p->a2_reg[1], p->a2_reg[2]);
+
guard(mutex)(&tas_priv->codec_lock);
dst[0] = bytes_ext->max;
return calib_data_get(tas_priv, reg, &dst[1]);
@@ -1172,10 +1235,51 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
nr_controls < mix_index ? nr_controls : mix_index);
}
+static void cali_reg_update(struct bulk_reg_val *p,
+ struct fct_param_address *t)
+{
+ const int sum = ARRAY_SIZE(tas2781_cali_start_reg);
+ int reg, j;
+
+ for (j = 0; j < sum; j++) {
+ switch (tas2781_cali_start_reg[j].reg) {
+ case 0:
+ reg = TASDEVICE_REG(t->thr[0], t->thr[1], t->thr[2]);
+ break;
+ case TAS2781_PRM_PLT_FLAG_REG:
+ reg = TASDEVICE_REG(t->plt_flg[0], t->plt_flg[1],
+ t->plt_flg[2]);
+ break;
+ case TAS2781_PRM_SINEGAIN_REG:
+ reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1],
+ t->sin_gn[2]);
+ break;
+ case TAS2781_PRM_SINEGAIN2_REG:
+ reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1],
+ t->sin_gn[2]);
+ break;
+ default:
+ reg = 0;
+ break;
+ }
+ if (reg)
+ p[j].reg = reg;
+ }
+}
+
+static void alpa_cali_update(struct bulk_reg_val *p,
+ struct fct_param_address *t)
+{
+ p->is_locked = false;
+ p->reg = TASDEVICE_REG(t->thr2[0], t->thr2[1], t->thr2[2]);
+ p->val_len = 4;
+}
+
static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
{
struct calidata *cali_data = &priv->cali_data;
struct tasdevice *tasdev = priv->tasdevice;
+ struct tasdevice_fw *fmw = priv->fmw;
struct soc_bytes_ext *ext_cali_data;
struct snd_kcontrol_new *cali_ctrls;
unsigned int nctrls;
@@ -1191,14 +1295,25 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
}
if (priv->chip_id == TAS2781) {
+ struct fct_param_address *t = &(fmw->fct_par_addr);
+
cali_ctrls = (struct snd_kcontrol_new *)tas2781_cali_controls;
nctrls = ARRAY_SIZE(tas2781_cali_controls);
for (i = 0; i < priv->ndev; i++) {
- tasdev[i].cali_data_backup =
+ struct bulk_reg_val *p;
+
+ p = tasdev[i].cali_data_backup =
kmemdup(tas2781_cali_start_reg,
sizeof(tas2781_cali_start_reg), GFP_KERNEL);
if (!tasdev[i].cali_data_backup)
return -ENOMEM;
+ if (priv->dspbin_typ) {
+ cali_reg_update(p, t);
+ if (priv->dspbin_typ == TASDEV_ALPHA) {
+ p = &tasdev[i].alp_cali_bckp;
+ alpa_cali_update(p, t);
+ }
+ }
}
} else {
cali_ctrls = (struct snd_kcontrol_new *)tas2563_cali_controls;
@@ -1260,8 +1375,6 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
(cali_data->cali_dat_sz_per_dev + 1) + 1 + 15 + 1;
priv->cali_data.total_sz = priv->ndev *
(cali_data->cali_dat_sz_per_dev + 1);
- priv->cali_data.data = devm_kzalloc(priv->dev,
- ext_cali_data->max, GFP_KERNEL);
cali_ctrls[i].name = cali_name;
cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
cali_ctrls[i].info = snd_soc_bytes_info_ext;
@@ -1313,10 +1426,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;
@@ -1390,14 +1643,24 @@ 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. */
tasdevice_dsp_remove(tas_priv);
}
mutex_unlock(&tas_priv->codec_lock);
- if (fmw)
- release_firmware(fmw);
+ release_firmware(fmw);
}
static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w,