Re: [PATCH v11] ASoc: tas2783: Add tas2783 codec driver

From: Amadeusz Sławiński
Date: Tue Mar 05 2024 - 11:09:24 EST


On 3/5/2024 2:26 PM, Shenghao Ding wrote:
The tas2783 is a smart audio amplifier with integrated MIPI SoundWire
interface (Version 1.2.1 compliant), I2C, and I2S/TDM interfaces designed
for portable applications. An on-chip DSP supports Texas Instruments
SmartAmp speaker protection algorithm. The integrated speaker voltage and
current sense provides for real-time monitoring of loudspeakers.

The ASoC component provides the majority of the functionality of the
device, all the audio functions.

Signed-off-by: Shenghao Ding <shenghao-ding@xxxxxx>

---

..

+
+static void tas2783_apply_calibv2(struct tasdevice_priv *tas_dev,
+ unsigned int *cali_data)
+{
+ const unsigned int arr_size = ARRAY_SIZE(tas2783_cali_reg);
+ struct regmap *map = tas_dev->regmap;
+ unsigned int dev_sum = cali_data[1], i, j, k;
+ u8 *cali_start;
+ u16 dev_info;
+ int ret;
+
+ if (!tas_dev->sdw_peripheral) {
+ dev_err(tas_dev->dev, "%s: peripheral doesn't exist.\n",
+ __func__);
+ return;
+ }
+
+ dev_info = tas_dev->sdw_peripheral->bus->link_id |
+ tas_dev->sdw_peripheral->id.unique_id << 16;
+
+ /*
+ * The area saving tas2783 calibrated data is specified by its
+ * unique_id offset. cali_start is the first address of current
+ * tas2783's calibrated data.
+ */
+ cali_start = (u8 *)&cali_data[3];
+ for (i = 0; i < dev_sum; i++) {
+ k = i * (arr_size + 1) + 3;
+ if (dev_info != cali_data[k]) {
+ for (j = 0; j < arr_size; j++) {
+ k = 4 * (k + 1 + j);
+ ret = regmap_bulk_write(map,
+ tas2783_cali_reg[j],
+ &cali_start[k], 4);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "Cali failed %x:%d\n",
+ tas2783_cali_reg[j], ret);
+ break;
+ }
+ }
+ break;
+ }
+ }

This seems a bit hard to read, any chance to do some reordering to make it more readable?

+}
+

..

+
+static int tasdevice_mute(struct snd_soc_dai *dai, int mute,
+ int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(component);
+ struct regmap *map = tas_dev->regmap;
+ int ret;
+
+ dev_dbg(tas_dev->dev, "%s: %d.\n", __func__, mute);
+
+ if (mute) {
+ if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = regmap_update_bits(map, TAS2873_REG_PWR_CTRL,
+ TAS2783_REG_AEF_MASK,
+ TAS2783_REG_AEF_INACTIVE);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "%s: Disable AEF failed.\n", __func__);
+ } else {
+ /* FU23 mute (0x40400108) */
+ ret = regmap_write(map,
+ SDW_SDCA_CTL(TAS2783_FUNC_TYPE_SMART_AMP,
+ TAS2783_SDCA_ENT_FU23,
+ TAS2783_SDCA_CTL_FU_MUTE, 0), 1);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s: FU23 mute failed.\n", __func__);
+ goto out;
+ }
+ /*
+ * Both playback and echo data will be shutdown in
+ * playback stream.
+ */
+ ret = regmap_update_bits(map, TAS2873_REG_PWR_CTRL,
+ TAS2783_REG_PWR_MODE_MASK |
+ TAS2783_REG_AEF_MASK,
+ TAS2783_REG_PWR_MODE_ACTIVE |
+ TAS2783_REG_PWR_MODE_SW_PWD);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s: PWR&AEF shutdown failed.\n",
+ __func__);
+ goto out;
+ }
+ tas_dev->pstream = false;
+ }
+ } else {
+ /* FU23 Unmute, 0x40400108. */
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = regmap_write(map,
+ SDW_SDCA_CTL(TAS2783_FUNC_TYPE_SMART_AMP,
+ TAS2783_SDCA_ENT_FU23,
+ TAS2783_SDCA_CTL_FU_MUTE, 0), 0);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s: FU23 Unmute failed.\n", __func__);
+ goto out;
+ }
+ ret = regmap_update_bits(map, TAS2873_REG_PWR_CTRL,
+ TAS2783_REG_PWR_MODE_MASK,
+ TAS2783_REG_PWR_MODE_ACTIVE);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s: PWR Unmute failed.\n", __func__);
+ goto out;
+ }
+ tas_dev->pstream = true;
+ } else {
+ /* Capture stream is the echo ref data for voice.
+ * Without playback, it can't be active.
+ */
+ if (tas_dev->pstream == true) {
+ ret = regmap_update_bits(map,
+ TAS2873_REG_PWR_CTRL,
+ TAS2783_REG_AEF_MASK,
+ TAS2783_REG_AEF_ACTIVE);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s: AEF enable failed.\n",
+ __func__);
+ goto out;
+ }
+ } else {
+ dev_err(tas_dev->dev,
+ "%s: No playback, no AEF!", __func__);
+ ret = -EINVAL;
+ }
+ }
+ }
+out:
+ if (ret)
+ dev_err(tas_dev->dev, "Mute or unmute %d failed %d.\n",
+ mute, ret);
+
+ return ret;
+}

Above function seem to be bit long, which also causes a lot of indentation, perhaps split it into mute and unmute helpers?

..

+
+static int tasdevice_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ struct sdw_dpn_prop *dpn;
+ unsigned long addr;
+ int nval, i, j;
+ u32 bit;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = BIT(2); /* BITMAP: 00000100 */
+ prop->sink_ports = BIT(1); /* BITMAP: 00000010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;

No need for separate j variable, you can reuse i here.

+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ return 0;
+}
+

..

+
+struct tasdevice_priv {
+ struct snd_soc_component *component;

Apart from being assigned this field seems to be unused.

+ struct sdw_slave *sdw_peripheral;
+ enum sdw_slave_status status;

This one seems to be only used in tasdevice_update_status()? Does it really need to be kept in struct?

+ struct sdw_bus_params params;

Unused?

+ struct regmap *regmap;
+ struct device *dev;
+ unsigned char dspfw_binaryname[TAS2783_DSPFW_FILENAME_LEN];

This one also seems weird, it is mainly needed when loading FW and could be local to tasdevice_comp_probe(), although there is one dev_warn which uses it outside of it, but pretty sure it could be dropped.

+ unsigned char dev_name[32];

Another unused field.

+ unsigned int chip_id;

Another one that only seems to be assigned.

+ bool pstream;
+ bool hw_init;
+ bool first_hw_init;
+};
+
+#endif /*__TAS2783_H__ */