diff options
Diffstat (limited to 'sound/soc/codecs/tas2781-i2c.c')
-rw-r--r-- | sound/soc/codecs/tas2781-i2c.c | 1457 |
1 files changed, 1342 insertions, 115 deletions
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index b5abff230e43..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 - 2023 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> @@ -21,15 +24,81 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> +#include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/regmap.h> #include <linux/slab.h> #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> +#include <linux/unaligned.h> + +#define X2563_CL_STT_VAL(xreg, xval) \ +{ .reg = xreg, \ + .val = { xval }, \ + .val_len = 1, } + +#define X2563_CL_STT_4BYTS(xreg, byte0, byte1, byte2, byte3) \ +{ .reg = xreg, \ + .val = { byte0, byte1, byte2, byte3 }, \ + .val_len = 4, } + +static const struct bulk_reg_val tas2563_cali_start_reg[] = { + X2563_CL_STT_VAL(TAS2563_IDLE, 0x00), + X2563_CL_STT_4BYTS(TAS2563_PRM_ENFF_REG, 0x40, 0x00, 0x00, 0x00), + X2563_CL_STT_4BYTS(TAS2563_PRM_DISTCK_REG, 0x40, 0x00, 0x00, 0x00), + X2563_CL_STT_4BYTS(TAS2563_PRM_TE_SCTHR_REG, 0x7f, 0xff, 0xff, 0xff), + X2563_CL_STT_4BYTS(TAS2563_PRM_PLT_FLAG_REG, 0x40, 0x00, 0x00, 0x00), + X2563_CL_STT_4BYTS(TAS2563_PRM_SINEGAIN_REG, 0x0a, 0x3d, 0x70, 0xa4), + X2563_CL_STT_4BYTS(TAS2563_TE_TA1_REG, 0x00, 0x36, 0x91, 0x5e), + X2563_CL_STT_4BYTS(TAS2563_TE_TA1_AT_REG, 0x00, 0x36, 0x91, 0x5e), + X2563_CL_STT_4BYTS(TAS2563_TE_TA2_REG, 0x00, 0x06, 0xd3, 0x72), + X2563_CL_STT_4BYTS(TAS2563_TE_AT_REG, 0x00, 0x36, 0x91, 0x5e), + X2563_CL_STT_4BYTS(TAS2563_TE_DT_REG, 0x00, 0x36, 0x91, 0x5e), +}; + +#define X2781_CL_STT_VAL(xreg, xval, xlocked) \ +{ .reg = xreg, \ + .val = { xval }, \ + .val_len = 1, \ + .is_locked = xlocked, } + +#define X2781_CL_STT_4BYTS_UNLOCKED(xreg, byte0, byte1, byte2, byte3) \ +{ .reg = xreg, \ + .val = { byte0, byte1, byte2, byte3 }, \ + .val_len = 4, \ + .is_locked = false, } + +#define X2781_CL_STT_LEN_UNLOCKED(xreg) \ +{ .reg = xreg, \ + .val_len = 4, \ + .is_locked = false, } + +static const struct bulk_reg_val tas2781_cali_start_reg[] = { + X2781_CL_STT_VAL(TAS2781_PRM_INT_MASK_REG, 0xfe, false), + X2781_CL_STT_VAL(TAS2781_PRM_CLK_CFG_REG, 0xdd, false), + X2781_CL_STT_VAL(TAS2781_PRM_RSVD_REG, 0x20, false), + X2781_CL_STT_VAL(TAS2781_PRM_TEST_57_REG, 0x14, true), + X2781_CL_STT_VAL(TAS2781_PRM_TEST_62_REG, 0x45, true), + X2781_CL_STT_VAL(TAS2781_PRM_PVDD_UVLO_REG, 0x03, false), + X2781_CL_STT_VAL(TAS2781_PRM_CHNL_0_REG, 0xa8, false), + X2781_CL_STT_VAL(TAS2781_PRM_NG_CFG0_REG, 0xb9, false), + X2781_CL_STT_VAL(TAS2781_PRM_IDLE_CH_DET_REG, 0x92, false), + /* + * This register is pilot tone threshold, different with the + * calibration tool version, it will be updated in + * tas2781_calib_start_put(), set to 1mA. + */ + X2781_CL_STT_4BYTS_UNLOCKED(0, 0x00, 0x00, 0x00, 0x56), + X2781_CL_STT_4BYTS_UNLOCKED(TAS2781_PRM_PLT_FLAG_REG, + 0x40, 0x00, 0x00, 0x00), + X2781_CL_STT_LEN_UNLOCKED(TAS2781_PRM_SINEGAIN_REG), + X2781_CL_STT_LEN_UNLOCKED(TAS2781_PRM_SINEGAIN2_REG), +}; static const struct i2c_device_id tasdevice_id[] = { { "tas2563", TAS2563 }, @@ -103,7 +172,7 @@ static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, return tasdevice_amp_putvol(tas_priv, ucontrol, mc); } -static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, +static int tasdev_force_fwload_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = @@ -118,7 +187,7 @@ static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, return 0; } -static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, +static int tasdev_force_fwload_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = @@ -139,6 +208,708 @@ static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, return change; } +static int tasdev_cali_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + struct calidata *cali_data = &priv->cali_data; + struct cali_reg *p = &cali_data->cali_reg_array; + unsigned char *dst = ucontrol->value.bytes.data; + unsigned char *data = cali_data->data; + unsigned int i = 0; + unsigned int j, k; + int rc; + + guard(mutex)(&priv->codec_lock); + if (!priv->is_user_space_calidata) + return -1; + + if (!p->r0_reg) + return -1; + + dst[i++] = bytes_ext->max; + dst[i++] = 'r'; + + dst[i++] = TASDEVICE_BOOK_ID(p->r0_reg); + dst[i++] = TASDEVICE_PAGE_ID(p->r0_reg); + dst[i++] = TASDEVICE_PAGE_REG(p->r0_reg); + + dst[i++] = TASDEVICE_BOOK_ID(p->r0_low_reg); + dst[i++] = TASDEVICE_PAGE_ID(p->r0_low_reg); + dst[i++] = TASDEVICE_PAGE_REG(p->r0_low_reg); + + dst[i++] = TASDEVICE_BOOK_ID(p->invr0_reg); + dst[i++] = TASDEVICE_PAGE_ID(p->invr0_reg); + dst[i++] = TASDEVICE_PAGE_REG(p->invr0_reg); + + dst[i++] = TASDEVICE_BOOK_ID(p->pow_reg); + dst[i++] = TASDEVICE_PAGE_ID(p->pow_reg); + dst[i++] = TASDEVICE_PAGE_REG(p->pow_reg); + + dst[i++] = TASDEVICE_BOOK_ID(p->tlimit_reg); + dst[i++] = TASDEVICE_PAGE_ID(p->tlimit_reg); + dst[i++] = TASDEVICE_PAGE_REG(p->tlimit_reg); + + for (j = 0, k = 0; j < priv->ndev; j++) { + if (j == data[k]) { + dst[i++] = j; + k++; + } else { + dev_err(priv->dev, "chn %d device %u not match\n", + j, data[k]); + k += 21; + continue; + } + rc = tasdevice_dev_bulk_read(priv, j, p->r0_reg, &dst[i], 4); + if (rc < 0) { + dev_err(priv->dev, "chn %d r0_reg bulk_rd err = %d\n", + j, rc); + i += 20; + k += 20; + continue; + } + rc = memcmp(&dst[i], &data[k], 4); + if (rc != 0) + dev_dbg(priv->dev, "chn %d r0_data is not same\n", j); + k += 4; + i += 4; + rc = tasdevice_dev_bulk_read(priv, j, p->r0_low_reg, + &dst[i], 4); + if (rc < 0) { + dev_err(priv->dev, "chn %d r0_low bulk_rd err = %d\n", + j, rc); + i += 16; + k += 16; + continue; + } + rc = memcmp(&dst[i], &data[k], 4); + if (rc != 0) + dev_dbg(priv->dev, "chn %d r0_low is not same\n", j); + i += 4; + k += 4; + rc = tasdevice_dev_bulk_read(priv, j, p->invr0_reg, + &dst[i], 4); + if (rc < 0) { + dev_err(priv->dev, "chn %d invr0 bulk_rd err = %d\n", + j, rc); + i += 12; + k += 12; + continue; + } + rc = memcmp(&dst[i], &data[k], 4); + if (rc != 0) + dev_dbg(priv->dev, "chn %d invr0 is not same\n", j); + i += 4; + k += 4; + rc = tasdevice_dev_bulk_read(priv, j, p->pow_reg, &dst[i], 4); + if (rc < 0) { + dev_err(priv->dev, "chn %d pow_reg bulk_rd err = %d\n", + j, rc); + i += 8; + k += 8; + continue; + } + rc = memcmp(&dst[i], &data[k], 4); + if (rc != 0) + dev_dbg(priv->dev, "chn %d pow_reg is not same\n", j); + i += 4; + k += 4; + rc = tasdevice_dev_bulk_read(priv, j, p->tlimit_reg, + &dst[i], 4); + if (rc < 0) { + dev_err(priv->dev, "chn %d tlimit bulk_rd err = %d\n", + j, rc); + } + rc = memcmp(&dst[i], &data[k], 4); + if (rc != 0) + dev_dbg(priv->dev, "chn %d tlimit is not same\n", j); + i += 4; + k += 4; + } + return 0; +} + +static int calib_data_get(struct tasdevice_priv *tas_priv, int reg, + unsigned char *dst) +{ + struct i2c_client *clt = (struct i2c_client *)tas_priv->client; + struct tasdevice *tasdev = tas_priv->tasdevice; + int rc = -1; + int i; + + for (i = 0; i < tas_priv->ndev; i++) { + if (clt->addr == tasdev[i].dev_addr) { + /* First byte is the device index. */ + dst[0] = i; + rc = tasdevice_dev_bulk_read(tas_priv, i, reg, &dst[1], + 4); + break; + } + } + + 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); + unsigned char val[4]; + int j, r; + + if (p == NULL) + return; + + /* Store the current setting from the chip */ + for (j = 0; j < sum; j++) { + if (p[j].val_len == 1) { + if (p[j].is_locked) + tasdevice_dev_write(tas_priv, i, + TAS2781_TEST_UNLOCK_REG, + TAS2781_TEST_PAGE_UNLOCK); + tasdevice_dev_read(tas_priv, i, p[j].reg, + (int *)&p[j].val[0]); + } else { + if (!tas_priv->dspbin_typ) { + r = partial_cali_data_update(reg, j); + if (r) + p[j].reg = r; + } + + 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 - 4; j++) { + if (p[j].val_len == 1) { + if (p[j].is_locked) + tasdevice_dev_write(tas_priv, i, + TAS2781_TEST_UNLOCK_REG, + TAS2781_TEST_PAGE_UNLOCK); + tasdevice_dev_write(tas_priv, i, p[j].reg, + tas2781_cali_start_reg[j].val[0]); + } + } + + 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, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + unsigned char *dat = ucontrol->value.bytes.data; + int i, reg[4]; + int j = 0; + + guard(mutex)(&priv->codec_lock); + if (priv->chip_id != TAS2781 || bytes_ext->max != dat[0] || + dat[1] != 'r') { + dev_err(priv->dev, "%s: package fmt or chipid incorrect\n", + __func__); + return 0; + } + j += 2; + /* refresh pilot tone and SineGain register */ + for (i = 0; i < ARRAY_SIZE(reg); i++) { + reg[i] = TASDEVICE_REG(dat[j], dat[j + 1], dat[j + 2]); + j += 3; + } + + for (i = 0; i < priv->ndev; i++) { + int k = i * 9 + j; + + if (dat[k] != i) { + dev_err(priv->dev, "%s:no cal-setting for dev %d\n", + __func__, i); + continue; + } + sngl_calib_start(priv, i, reg, dat + k); + } + return 1; +} + +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 < 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; + + for (j = 0; j < sum; j++) { + if (p[j].val_len == 1) { + if (p[j].is_locked) + tasdevice_dev_write(priv, i, + TAS2781_TEST_UNLOCK_REG, + TAS2781_TEST_PAGE_UNLOCK); + tasdevice_dev_write(priv, i, p[j].reg, + p[j].val[0]); + } else { + if (!p[j].reg) + continue; + 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); + } +} + +static int tas2563_calib_start_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct bulk_reg_val *q = (struct bulk_reg_val *)tas2563_cali_start_reg; + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp); + const int sum = ARRAY_SIZE(tas2563_cali_start_reg); + int i, j; + + guard(mutex)(&tas_priv->codec_lock); + if (tas_priv->chip_id != TAS2563) + return -1; + + for (i = 0; i < tas_priv->ndev; i++) { + struct tasdevice *tasdev = tas_priv->tasdevice; + struct bulk_reg_val *p = tasdev[i].cali_data_backup; + + if (p == NULL) + continue; + for (j = 0; j < sum; j++) { + if (p[j].val_len == 1) + tasdevice_dev_read(tas_priv, + i, p[j].reg, + (unsigned int *)&p[j].val[0]); + else + tasdevice_dev_bulk_read(tas_priv, + i, p[j].reg, p[j].val, 4); + } + + for (j = 0; j < sum; j++) { + if (p[j].val_len == 1) + tasdevice_dev_write(tas_priv, i, p[j].reg, + q[j].val[0]); + else + tasdevice_dev_bulk_write(tas_priv, i, p[j].reg, + q[j].val, 4); + } + } + + return 1; +} + +static void tas2563_calib_stop_put(struct tasdevice_priv *tas_priv) +{ + const int sum = ARRAY_SIZE(tas2563_cali_start_reg); + int i, j; + + for (i = 0; i < tas_priv->ndev; i++) { + struct tasdevice *tasdev = tas_priv->tasdevice; + struct bulk_reg_val *p = tasdev[i].cali_data_backup; + + if (p == NULL) + continue; + + for (j = 0; j < sum; j++) { + if (p[j].val_len == 1) + tasdevice_dev_write(tas_priv, i, p[j].reg, + p[j].val[0]); + else + tasdevice_dev_bulk_write(tas_priv, i, p[j].reg, + p[j].val, 4); + } + } +} + +static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); + + guard(mutex)(&priv->codec_lock); + if (priv->chip_id == TAS2563) + tas2563_calib_stop_put(priv); + else + tas2781_calib_stop_put(priv); + + return 1; +} + +static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + struct calidata *cali_data = &priv->cali_data; + struct cali_reg *p = &cali_data->cali_reg_array; + unsigned char *src = ucontrol->value.bytes.data; + unsigned char *dst = cali_data->data; + int i = 0; + int j; + + guard(mutex)(&priv->codec_lock); + if (src[0] != bytes_ext->max || src[1] != 'r') { + dev_err(priv->dev, "%s: pkg fmt invalid\n", __func__); + return 0; + } + for (j = 0; j < priv->ndev; j++) { + if (src[17 + j * 21] != j) { + dev_err(priv->dev, "%s: pkg fmt invalid\n", __func__); + return 0; + } + } + i += 2; + priv->is_user_space_calidata = true; + + 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; +} + +static int tas2781_latch_reg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp); + struct i2c_client *clt = (struct i2c_client *)tas_priv->client; + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + struct tasdevice *tasdev = tas_priv->tasdevice; + unsigned char *dst = ucontrol->value.bytes.data; + int i, val, rc = -1; + + dst[0] = bytes_ext->max; + guard(mutex)(&tas_priv->codec_lock); + for (i = 0; i < tas_priv->ndev; i++) { + if (clt->addr == tasdev[i].dev_addr) { + /* First byte is the device index. */ + dst[1] = i; + rc = tasdevice_dev_read(tas_priv, i, + TAS2781_RUNTIME_LATCH_RE_REG, &val); + if (rc < 0) + dev_err(tas_priv->dev, "%s, get value error\n", + __func__); + else + dst[2] = val; + + break; + } + } + + return rc; +} + +static int tasdev_tf_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp); + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + unsigned char *dst = ucontrol->value.bytes.data; + 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); + + reg = TAS2781_RUNTIME_RE_REG_TF; + 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; + return calib_data_get(tas_priv, reg, &dst[1]); +} + +static int tasdev_re_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp); + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + unsigned char *dst = ucontrol->value.bytes.data; + unsigned int reg = TAS2781_RUNTIME_RE_REG; + + 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]); +} + +static int tasdev_r0_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp); + struct calidata *cali_data = &tas_priv->cali_data; + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + unsigned char *dst = ucontrol->value.bytes.data; + unsigned int reg; + + guard(mutex)(&tas_priv->codec_lock); + + if (tas_priv->chip_id == TAS2563) + reg = TAS2563_PRM_R0_REG; + else if (cali_data->cali_reg_array.r0_reg) + reg = cali_data->cali_reg_array.r0_reg; + else + return -1; + dst[0] = bytes_ext->max; + return calib_data_get(tas_priv, reg, &dst[1]); +} + +static int tasdev_XMA1_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + 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]); +} + +static int tasdev_XMA2_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + 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]); +} + +static int tasdev_nop_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int tas2563_digital_gain_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); + unsigned int l = 0, r = mc->max; + unsigned int target, ar_mid, mid, ar_l, ar_r; + unsigned int reg = mc->reg; + unsigned char data[4]; + int ret; + + mutex_lock(&tas_dev->codec_lock); + /* Read the primary device */ + ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); + if (ret) { + dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); + goto out; + } + + target = get_unaligned_be32(&data[0]); + + while (r > 1 + l) { + mid = (l + r) / 2; + ar_mid = get_unaligned_be32(tas2563_dvc_table[mid]); + if (target < ar_mid) + r = mid; + else + l = mid; + } + + ar_l = get_unaligned_be32(tas2563_dvc_table[l]); + ar_r = get_unaligned_be32(tas2563_dvc_table[r]); + + /* find out the member same as or closer to the current volume */ + ucontrol->value.integer.value[0] = + abs(target - ar_l) <= abs(target - ar_r) ? l : r; +out: + mutex_unlock(&tas_dev->codec_lock); + return 0; +} + +static int tas2563_digital_gain_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); + int vol = ucontrol->value.integer.value[0]; + int status = 0, max = mc->max, rc = 1; + int i, ret; + unsigned int reg = mc->reg; + unsigned int volrd, volwr; + unsigned char data[4]; + + vol = clamp(vol, 0, max); + mutex_lock(&tas_dev->codec_lock); + /* Read the primary device */ + ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); + if (ret) { + dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); + rc = -1; + goto out; + } + + volrd = get_unaligned_be32(&data[0]); + volwr = get_unaligned_be32(tas2563_dvc_table[vol]); + + if (volrd == volwr) { + rc = 0; + goto out; + } + + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_bulk_write(tas_dev, i, reg, + (unsigned char *)tas2563_dvc_table[vol], 4); + if (ret) { + dev_err(tas_dev->dev, + "%s, set digital vol error in dev %d\n", + __func__, i); + status |= BIT(i); + } + } + + if (status) + rc = -1; +out: + mutex_unlock(&tas_dev->codec_lock); + return rc; +} + +static const struct snd_kcontrol_new tasdevice_snd_controls[] = { + SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, + tasdev_force_fwload_get, tasdev_force_fwload_put), +}; + +static const struct snd_kcontrol_new tasdevice_cali_controls[] = { + SOC_SINGLE_EXT("Calibration Stop", SND_SOC_NOPM, 0, 1, 0, + tasdev_nop_get, tasdev_calib_stop_put), + SND_SOC_BYTES_EXT("Amp TF Data", 6, tasdev_tf_data_get, NULL), + SND_SOC_BYTES_EXT("Amp RE Data", 6, tasdev_re_data_get, NULL), + SND_SOC_BYTES_EXT("Amp R0 Data", 6, tasdev_r0_data_get, NULL), + SND_SOC_BYTES_EXT("Amp XMA1 Data", 6, tasdev_XMA1_data_get, NULL), + SND_SOC_BYTES_EXT("Amp XMA2 Data", 6, tasdev_XMA2_data_get, NULL), +}; + static const struct snd_kcontrol_new tas2781_snd_controls[] = { SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, 1, 0, 20, 0, tas2781_amp_getvol, @@ -146,8 +917,22 @@ static const struct snd_kcontrol_new tas2781_snd_controls[] = { SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL, 0, 0, 200, 1, tas2781_digital_getvol, tas2781_digital_putvol, dvc_tlv), - SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, - tas2781_force_fwload_get, tas2781_force_fwload_put), +}; + +static const struct snd_kcontrol_new tas2781_cali_controls[] = { + SND_SOC_BYTES_EXT("Amp Latch Data", 3, tas2781_latch_reg_get, NULL), +}; + +static const struct snd_kcontrol_new tas2563_snd_controls[] = { + SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2563_DVC_LVL, 0, + 0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0, + tas2563_digital_gain_get, tas2563_digital_gain_put, + tas2563_dvc_tlv), +}; + +static const struct snd_kcontrol_new tas2563_cali_controls[] = { + SOC_SINGLE_EXT("Calibration Start", SND_SOC_NOPM, 0, 1, 0, + tasdev_nop_get, tas2563_calib_start_put), }; static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, @@ -167,6 +952,31 @@ static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, return ret; } +static int tasdevice_info_active_num(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->ndev - 1; + + return 0; +} + +static int tasdevice_info_chip_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = TAS2563; + uinfo->value.integer.max = TAS2781; + + return 0; +} + static int tasdevice_info_programs(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -223,6 +1033,17 @@ static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, return 0; } +static int tasdevice_get_chip_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->chip_id; + + return 0; +} + static int tasdevice_create_control(struct tasdevice_priv *tas_priv) { struct snd_kcontrol_new *prof_ctrls; @@ -239,13 +1060,11 @@ static int tasdevice_create_control(struct tasdevice_priv *tas_priv) } /* Create a mixer item for selecting the active profile */ - name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - GFP_KERNEL); + name = devm_kstrdup(tas_priv->dev, "Speaker Profile Id", GFP_KERNEL); if (!name) { ret = -ENOMEM; goto out; } - scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "Speaker Profile Id"); prof_ctrls[mix_index].name = name; prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; prof_ctrls[mix_index].info = tasdevice_info_profile; @@ -316,37 +1135,61 @@ static int tasdevice_configuration_put( return ret; } -static int tasdevice_dsp_create_ctrls( - struct tasdevice_priv *tas_priv) +static int tasdevice_active_num_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct i2c_client *clt = (struct i2c_client *)tas_priv->client; + struct tasdevice *tasdev = tas_priv->tasdevice; + int i; + + for (i = 0; i < tas_priv->ndev; i++) { + if (clt->addr == tasdev[i].dev_addr) { + ucontrol->value.integer.value[0] = i; + return 0; + } + } + + return -1; +} + +static int tasdevice_active_num_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int dev_id = ucontrol->value.integer.value[0]; + int max = tas_priv->ndev - 1; + + dev_id = clamp(dev_id, 0, max); + + guard(mutex)(&tas_priv->codec_lock); + return tasdev_chn_switch(tas_priv, dev_id); +} + +static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv) { struct snd_kcontrol_new *dsp_ctrls; - char *prog_name, *conf_name; - int nr_controls = 2; + char *active_dev_num, *chip_id; + char *conf_name, *prog_name; + int nr_controls = 4; int mix_index = 0; - int ret; /* Alloc kcontrol via devm_kzalloc, which don't manually * free the kcontrol */ dsp_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, sizeof(dsp_ctrls[0]), GFP_KERNEL); - if (!dsp_ctrls) { - ret = -ENOMEM; - goto out; - } + if (!dsp_ctrls) + return -ENOMEM; - /* Create a mixer item for selecting the active profile */ - prog_name = devm_kzalloc(tas_priv->dev, - SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); - conf_name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + /* Create mixer items for selecting the active Program and Config */ + prog_name = devm_kstrdup(tas_priv->dev, "Speaker Program Id", GFP_KERNEL); - if (!prog_name || !conf_name) { - ret = -ENOMEM; - goto out; - } + if (!prog_name) + return -ENOMEM; - scnprintf(prog_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - "Speaker Program Id"); dsp_ctrls[mix_index].name = prog_name; dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; dsp_ctrls[mix_index].info = tasdevice_info_programs; @@ -354,8 +1197,11 @@ static int tasdevice_dsp_create_ctrls( dsp_ctrls[mix_index].put = tasdevice_program_put; mix_index++; - scnprintf(conf_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - "Speaker Config Id"); + conf_name = devm_kstrdup(tas_priv->dev, "Speaker Config Id", + GFP_KERNEL); + if (!conf_name) + return -ENOMEM; + dsp_ctrls[mix_index].name = conf_name; dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; dsp_ctrls[mix_index].info = tasdevice_info_configurations; @@ -363,40 +1209,414 @@ static int tasdevice_dsp_create_ctrls( dsp_ctrls[mix_index].put = tasdevice_configuration_put; mix_index++; - ret = snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls, + active_dev_num = devm_kstrdup(tas_priv->dev, "Activate Tasdevice Num", + GFP_KERNEL); + if (!active_dev_num) + return -ENOMEM; + + dsp_ctrls[mix_index].name = active_dev_num; + dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + dsp_ctrls[mix_index].info = tasdevice_info_active_num; + dsp_ctrls[mix_index].get = tasdevice_active_num_get; + dsp_ctrls[mix_index].put = tasdevice_active_num_put; + mix_index++; + + chip_id = devm_kstrdup(tas_priv->dev, "Tasdevice Chip Id", GFP_KERNEL); + if (!chip_id) + return -ENOMEM; + + dsp_ctrls[mix_index].name = chip_id; + dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + dsp_ctrls[mix_index].info = tasdevice_info_chip_id; + dsp_ctrls[mix_index].get = tasdevice_get_chip_id; + mix_index++; + + return snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls, nr_controls < mix_index ? nr_controls : mix_index); +} -out: +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; + char *cali_name; + int rc, i; + + rc = snd_soc_add_component_controls(priv->codec, + tasdevice_cali_controls, ARRAY_SIZE(tasdevice_cali_controls)); + if (rc < 0) { + dev_err(priv->dev, "%s: Add cali controls err rc = %d", + __func__, rc); + return rc; + } + + 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++) { + 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; + nctrls = ARRAY_SIZE(tas2563_cali_controls); + for (i = 0; i < priv->ndev; i++) { + tasdev[i].cali_data_backup = + kmemdup(tas2563_cali_start_reg, + sizeof(tas2563_cali_start_reg), GFP_KERNEL); + if (!tasdev[i].cali_data_backup) + return -ENOMEM; + } + } + + rc = snd_soc_add_component_controls(priv->codec, cali_ctrls, nctrls); + if (rc < 0) { + dev_err(priv->dev, "%s: Add chip cali ctrls err rc = %d", + __func__, rc); + return rc; + } + + /* index for cali_ctrls */ + i = 0; + if (priv->chip_id == TAS2781) + nctrls = 2; + else + nctrls = 1; + + /* + * Alloc kcontrol via devm_kzalloc(), which don't manually + * free the kcontrol。 + */ + cali_ctrls = devm_kcalloc(priv->dev, nctrls, + sizeof(cali_ctrls[0]), GFP_KERNEL); + if (!cali_ctrls) + return -ENOMEM; + + ext_cali_data = devm_kzalloc(priv->dev, sizeof(*ext_cali_data), + GFP_KERNEL); + if (!ext_cali_data) + return -ENOMEM; + + cali_name = devm_kstrdup(priv->dev, "Speaker Calibrated Data", + GFP_KERNEL); + if (!cali_name) + return -ENOMEM; + /* the number of calibrated data per tas2563/tas2781 */ + cali_data->cali_dat_sz_per_dev = 20; + /* + * Data structure for tas2563/tas2781 calibrated data: + * Pkg len (1 byte) + * Reg id (1 byte, constant 'r') + * book, page, register array for calibrated data (15 bytes) + * for (i = 0; i < Device-Sum; i++) { + * Device #i index_info (1 byte) + * Calibrated data for Device #i (20 bytes) + * } + */ + ext_cali_data->max = priv->ndev * + (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); + cali_ctrls[i].name = cali_name; + cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + cali_ctrls[i].info = snd_soc_bytes_info_ext; + cali_ctrls[i].get = tasdev_cali_data_get; + cali_ctrls[i].put = tasdev_cali_data_put; + cali_ctrls[i].private_value = (unsigned long)ext_cali_data; + i++; + + cali_data->data = devm_kzalloc(priv->dev, cali_data->total_sz, + GFP_KERNEL); + if (!cali_data->data) + return -ENOMEM; + + if (priv->chip_id == TAS2781) { + struct soc_bytes_ext *ext_cali_start; + char *cali_start_name; + + ext_cali_start = devm_kzalloc(priv->dev, + sizeof(*ext_cali_start), GFP_KERNEL); + if (!ext_cali_start) + return -ENOMEM; + + cali_start_name = devm_kstrdup(priv->dev, + "Calibration Start", GFP_KERNEL); + if (!cali_start_name) + return -ENOMEM; + /* + * package structure for tas2781 ftc start: + * Pkg len (1 byte) + * Reg id (1 byte, constant 'r') + * book, page, register for pilot threshold, pilot tone + * and sine gain (12 bytes) + * for (i = 0; i < Device-Sum; i++) { + * Device #i index_info (1 byte) + * Sine gain for Device #i (8 bytes) + * } + */ + ext_cali_start->max = 14 + priv->ndev * 9; + cali_ctrls[i].name = cali_start_name; + cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + cali_ctrls[i].info = snd_soc_bytes_info_ext; + cali_ctrls[i].put = tas2781_calib_start_put; + cali_ctrls[i].get = tasdev_nop_get; + cali_ctrls[i].private_value = (unsigned long)ext_cali_start; + i++; + } + + return snd_soc_add_component_controls(priv->codec, cali_ctrls, + 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; mutex_lock(&tas_priv->codec_lock); ret = tasdevice_rca_parser(tas_priv, fmw); - if (ret) + if (ret) { + tasdevice_config_info_remove(tas_priv); goto out; + } tasdevice_create_control(tas_priv); tasdevice_dsp_remove(tas_priv); tasdevice_calbin_remove(tas_priv); - tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; - scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", - tas_priv->dev_name); + /* + * The baseline is the RCA-only case, and then the code attempts to + * load DSP firmware but in case of failures just keep going, i.e. + * failing to load DSP firmware is NOT an error. + */ + tas_priv->fw_state = TASDEVICE_RCA_FW_OK; + if (tas_priv->name_prefix) + scnprintf(tas_priv->coef_binaryname, 64, "%s-%s_coef.bin", + tas_priv->name_prefix, tas_priv->dev_name); + else + scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", + tas_priv->dev_name); ret = tasdevice_dsp_parser(tas_priv); if (ret) { dev_err(tas_priv->dev, "dspfw load %s error\n", tas_priv->coef_binaryname); - tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; goto out; } - tasdevice_dsp_create_ctrls(tas_priv); + + /* + * If no dsp-related kcontrol created, the dsp resource will be freed. + */ + ret = tasdevice_dsp_create_ctrls(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "dsp controls error\n"); + goto out; + } + + ret = tasdevice_create_cali_ctrls(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "cali controls error\n"); + goto out; + } tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; @@ -404,8 +1624,15 @@ static void tasdevice_fw_ready(const struct firmware *fmw, * calibrated data inside algo. */ for (i = 0; i < tas_priv->ndev; i++) { - scnprintf(tas_priv->cal_binaryname[i], 64, "%s_cal_0x%02x.bin", - tas_priv->dev_name, tas_priv->tasdevice[i].dev_addr); + if (tas_priv->name_prefix) + scnprintf(tas_priv->cal_binaryname[i], 64, + "%s-%s_cal_0x%02x.bin", tas_priv->name_prefix, + tas_priv->dev_name, + tas_priv->tasdevice[i].dev_addr); + else + scnprintf(tas_priv->cal_binaryname[i], 64, + "%s_cal_0x%02x.bin", tas_priv->dev_name, + tas_priv->tasdevice[i].dev_addr); ret = tas2781_load_calibration(tas_priv, tas_priv->cal_binaryname[i], i); if (ret != 0) @@ -414,17 +1641,26 @@ static void tasdevice_fw_ready(const struct firmware *fmw, __func__, tas_priv->cal_binaryname[i]); } - tasdevice_prmg_calibdata_load(tas_priv, 0); + 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_DSP_FW_FAIL) { - /*If DSP FW fail, kcontrol won't be created */ - tasdevice_config_info_remove(tas_priv); + 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, @@ -452,13 +1688,13 @@ static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event), SND_SOC_DAPM_OUTPUT("OUT"), - SND_SOC_DAPM_INPUT("DMIC") + SND_SOC_DAPM_INPUT("DMIC"), }; static const struct snd_soc_dapm_route tasdevice_audio_map[] = { {"SPK", NULL, "ASI"}, {"OUT", NULL, "SPK"}, - {"ASI OUT", NULL, "DMIC"} + {"ASI OUT", NULL, "DMIC"}, }; static int tasdevice_startup(struct snd_pcm_substream *substream, @@ -466,14 +1702,14 @@ static int tasdevice_startup(struct snd_pcm_substream *substream, { struct snd_soc_component *codec = dai->component; struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); - int ret = 0; - if (tas_priv->fw_state != TASDEVICE_DSP_FW_ALL_OK) { - dev_err(tas_priv->dev, "DSP bin file not loaded\n"); - ret = -EINVAL; + switch (tas_priv->fw_state) { + case TASDEVICE_RCA_FW_OK: + case TASDEVICE_DSP_FW_ALL_OK: + return 0; + default: + return -EINVAL; } - - return ret; } static int tasdevice_hw_params(struct snd_pcm_substream *substream, @@ -483,7 +1719,6 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream, unsigned int slot_width; unsigned int fsrate; int bclk_rate; - int rc = 0; fsrate = params_rate(params); switch (fsrate) { @@ -493,8 +1728,7 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream, default: dev_err(tas_priv->dev, "%s: incorrect sample rate = %u\n", __func__, fsrate); - rc = -EINVAL; - goto out; + return -EINVAL; } slot_width = params_width(params); @@ -507,20 +1741,17 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream, default: dev_err(tas_priv->dev, "%s: incorrect slot width = %u\n", __func__, slot_width); - rc = -EINVAL; - goto out; + return -EINVAL; } bclk_rate = snd_soc_params_to_bclk(params); if (bclk_rate < 0) { dev_err(tas_priv->dev, "%s: incorrect bclk rate = %d\n", __func__, bclk_rate); - rc = bclk_rate; - goto out; + return bclk_rate; } -out: - return rc; + return 0; } static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai, @@ -541,7 +1772,7 @@ static const struct snd_soc_dai_ops tasdevice_dai_ops = { static struct snd_soc_dai_driver tasdevice_dai_driver[] = { { - .name = "tas2781_codec", + .name = "tasdev_codec", .id = 0, .playback = { .stream_name = "Playback", @@ -565,13 +1796,39 @@ static struct snd_soc_dai_driver tasdevice_dai_driver[] = { static int tasdevice_codec_probe(struct snd_soc_component *codec) { struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct snd_kcontrol_new *p; + unsigned int size; + int rc; + + switch (tas_priv->chip_id) { + case TAS2781: + p = (struct snd_kcontrol_new *)tas2781_snd_controls; + size = ARRAY_SIZE(tas2781_snd_controls); + break; + default: + p = (struct snd_kcontrol_new *)tas2563_snd_controls; + size = ARRAY_SIZE(tas2563_snd_controls); + } + + rc = snd_soc_add_component_controls(codec, p, size); + if (rc < 0) { + dev_err(tas_priv->dev, "%s: Add control err rc = %d", + __func__, rc); + return rc; + } + tas_priv->name_prefix = codec->name_prefix; return tascodec_init(tas_priv, codec, THIS_MODULE, tasdevice_fw_ready); } static void tasdevice_deinit(void *context) { struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice *tasdev = tas_priv->tasdevice; + int i; + + for (i = 0; i < tas_priv->ndev; i++) + kfree(tasdev[i].cali_data_backup); tasdevice_config_info_remove(tas_priv); tasdevice_dsp_remove(tas_priv); @@ -579,8 +1836,7 @@ static void tasdevice_deinit(void *context) tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; } -static void tasdevice_codec_remove( - struct snd_soc_component *codec) +static void tasdevice_codec_remove(struct snd_soc_component *codec) { struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); @@ -591,8 +1847,8 @@ static const struct snd_soc_component_driver soc_codec_driver_tasdevice = { .probe = tasdevice_codec_probe, .remove = tasdevice_codec_remove, - .controls = tas2781_snd_controls, - .num_controls = ARRAY_SIZE(tas2781_snd_controls), + .controls = tasdevice_snd_controls, + .num_controls = ARRAY_SIZE(tasdevice_snd_controls), .dapm_widgets = tasdevice_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets), .dapm_routes = tasdevice_audio_map, @@ -605,7 +1861,7 @@ static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv) { struct i2c_client *client = (struct i2c_client *)tas_priv->client; unsigned int dev_addrs[TASDEVICE_MAX_CHANNELS]; - int rc, i, ndev = 0; + int i, ndev = 0; if (tas_priv->isacpi) { ndev = device_property_read_u32_array(&client->dev, @@ -620,69 +1876,38 @@ static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv) "ti,audio-slots", dev_addrs, ndev); } - tas_priv->irq_info.irq_gpio = + tas_priv->irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(&client->dev), 0); - } else { + } else if (IS_ENABLED(CONFIG_OF)) { struct device_node *np = tas_priv->dev->of_node; -#ifdef CONFIG_OF - const __be32 *reg, *reg_end; - int len, sw, aw; - - aw = of_n_addr_cells(np); - sw = of_n_size_cells(np); - if (sw == 0) { - reg = (const __be32 *)of_get_property(np, - "reg", &len); - reg_end = reg + len/sizeof(*reg); - ndev = 0; - do { - dev_addrs[ndev] = of_read_number(reg, aw); - reg += aw; - ndev++; - } while (reg < reg_end); - } else { - ndev = 1; - dev_addrs[0] = client->addr; + u64 addr; + + for (i = 0; i < TASDEVICE_MAX_CHANNELS; i++) { + if (of_property_read_reg(np, i, &addr, NULL)) + break; + dev_addrs[ndev++] = addr; } -#else + + tas_priv->irq = of_irq_get(np, 0); + } else { ndev = 1; dev_addrs[0] = client->addr; -#endif - tas_priv->irq_info.irq_gpio = of_irq_get(np, 0); } tas_priv->ndev = ndev; for (i = 0; i < ndev; i++) tas_priv->tasdevice[i].dev_addr = dev_addrs[i]; tas_priv->reset = devm_gpiod_get_optional(&client->dev, - "reset-gpios", GPIOD_OUT_HIGH); + "reset", GPIOD_OUT_HIGH); if (IS_ERR(tas_priv->reset)) dev_err(tas_priv->dev, "%s Can't get reset GPIO\n", __func__); strcpy(tas_priv->dev_name, tasdevice_id[tas_priv->chip_id].name); - - if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) { - rc = gpio_request(tas_priv->irq_info.irq_gpio, - "AUDEV-IRQ"); - if (!rc) { - gpio_direction_input( - tas_priv->irq_info.irq_gpio); - - tas_priv->irq_info.irq = - gpio_to_irq(tas_priv->irq_info.irq_gpio); - } else - dev_err(tas_priv->dev, "%s: GPIO %d request error\n", - __func__, tas_priv->irq_info.irq_gpio); - } else - dev_err(tas_priv->dev, - "Looking up irq-gpio property failed %d\n", - tas_priv->irq_info.irq_gpio); } static int tasdevice_i2c_probe(struct i2c_client *i2c) { - const struct i2c_device_id *id = i2c_match_id(tasdevice_id, i2c); const struct acpi_device_id *acpi_id; struct tasdevice_priv *tas_priv; int ret; @@ -704,7 +1929,7 @@ static int tasdevice_i2c_probe(struct i2c_client *i2c) tas_priv->chip_id = acpi_id->driver_data; tas_priv->isacpi = true; } else { - tas_priv->chip_id = id ? id->driver_data : 0; + tas_priv->chip_id = (uintptr_t)i2c_get_match_data(i2c); tas_priv->isacpi = false; } @@ -714,6 +1939,8 @@ static int tasdevice_i2c_probe(struct i2c_client *i2c) if (ret) goto err; + tasdevice_reset(tas_priv); + ret = devm_snd_soc_register_component(tas_priv->dev, &soc_codec_driver_tasdevice, tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver)); @@ -746,7 +1973,7 @@ MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match); static struct i2c_driver tasdevice_i2c_driver = { .driver = { - .name = "tas2781-codec", + .name = "tasdev-codec", .of_match_table = of_match_ptr(tasdevice_of_match), #ifdef CONFIG_ACPI .acpi_match_table = ACPI_PTR(tasdevice_acpi_match), @@ -763,4 +1990,4 @@ MODULE_AUTHOR("Shenghao Ding <shenghao-ding@ti.com>"); MODULE_AUTHOR("Kevin Lu <kevin-lu@ti.com>"); MODULE_DESCRIPTION("ASoC TAS2781 Driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB); +MODULE_IMPORT_NS("SND_SOC_TAS2781_FMWLIB"); |