diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 160c07699a8b72..3b9c57342cba69 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2103,6 +2103,7 @@ config SND_SOC_TAS2783_SDW tristate "Texas Instruments TAS2783 speaker amplifier (sdw)" depends on SOUNDWIRE depends on EFI + depends on SND_SOC_SDCA select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ select CRC32 diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index d62667957939b1..d1c0838e210d4e 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -35,11 +37,12 @@ #include "tas2783.h" #define TIMEOUT_FW_DL_MS (3000) -#define FW_DL_OFFSET 36 -#define FW_FL_HDR 12 +#define FW_DL_OFFSET 84 /* binary file information */ +#define FW_FL_HDR 20 /* minimum number of bytes in one chunk */ #define TAS2783_PROBE_TIMEOUT 5000 #define TAS2783_CALI_GUID EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, \ 0x09, 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92) +#define MIPI_INIT_TABLE "mipi-sdca-function-initialization-table" static const u32 tas2783_cali_reg[] = { TAS2783_CAL_R0, @@ -49,13 +52,29 @@ static const u32 tas2783_cali_reg[] = { TAS2783_CAL_TLIM, }; -struct bin_header_t { - u16 vendor_id; - u16 version; +struct tas_fw_hdr { + u32 size; + u32 version_offset; + u32 plt_id; + u32 ppc3_ver; + u32 timestamp; + u8 ddc_name[64]; +}; + +struct tas_fw_file { + u32 vendor_id; u32 file_id; + u32 version; u32 length; + u32 dest_addr; + u8 *fw_data; }; +struct raw_init_data { + __le32 addr; + u8 val; +} __packed; + struct calibration_data { u32 is_valid; unsigned long read_sz; @@ -83,6 +102,8 @@ struct tas2783_prv { wait_queue_head_t fw_wait; bool fw_dl_task_done; bool fw_dl_success; + const struct reg_sequence *init_data; + int num_init_data; }; static const struct reg_default tas2783_reg_default[] = { @@ -286,7 +307,7 @@ static const struct reg_default tas2783_reg_default[] = { }; static const struct reg_sequence tas2783_init_seq[] = { - REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0x00), 0x04), + REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0x00), 0x01), REG_SEQ0(0x00800418, 0x00), REG_SEQ0(0x00800419, 0x00), REG_SEQ0(0x0080041a, 0x00), @@ -296,60 +317,19 @@ static const struct reg_sequence tas2783_init_seq[] = { REG_SEQ0(0x0080042a, 0x00), REG_SEQ0(0x0080042b, 0x00), REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x1, 0x00), 0x00), - REG_SEQ0(0x0080005c, 0xD9), - REG_SEQ0(0x00800082, 0x20), - REG_SEQ0(0x008000a1, 0x00), - REG_SEQ0(0x00800097, 0xc8), - REG_SEQ0(0x00800099, 0x20), - REG_SEQ0(0x008000c7, 0xaa), - REG_SEQ0(0x008000b5, 0x74), - REG_SEQ0(0x00800082, 0x20), - REG_SEQ0(0x00807e8d, 0x0d), - REG_SEQ0(0x00807eb9, 0x53), - REG_SEQ0(0x00807ebe, 0x42), - REG_SEQ0(0x00807ec5, 0x37), - REG_SEQ0(0x00800066, 0x92), - REG_SEQ0(0x00800003, 0x28), REG_SEQ0(0x00800004, 0x21), REG_SEQ0(0x00800005, 0x41), REG_SEQ0(0x00800006, 0x00), REG_SEQ0(0x00800007, 0x20), - REG_SEQ0(0x0080000c, 0x10), - REG_SEQ0(0x00800013, 0x08), REG_SEQ0(0x00800015, 0x00), - REG_SEQ0(0x00800017, 0x80), - REG_SEQ0(0x0080001a, 0x00), - REG_SEQ0(0x0080001b, 0x22), - REG_SEQ0(0x0080001c, 0x36), - REG_SEQ0(0x0080001d, 0x01), - REG_SEQ0(0x0080001f, 0x00), - REG_SEQ0(0x00800020, 0x2e), - REG_SEQ0(0x00800034, 0x06), - REG_SEQ0(0x00800035, 0xb9), REG_SEQ0(0x00800036, 0xad), REG_SEQ0(0x00800037, 0xa8), - REG_SEQ0(0x00800038, 0x00), - REG_SEQ0(0x0080003b, 0xfc), - REG_SEQ0(0x0080003d, 0xdd), - REG_SEQ0(0x00800040, 0xf6), - REG_SEQ0(0x00800041, 0x14), - REG_SEQ0(0x0080005c, 0x19), - REG_SEQ0(0x0080005d, 0x80), - REG_SEQ0(0x00800063, 0x48), - REG_SEQ0(0x00800065, 0x08), - REG_SEQ0(0x00800067, 0x00), - REG_SEQ0(0x0080006a, 0x12), REG_SEQ0(0x0080006b, 0x7b), REG_SEQ0(0x0080006c, 0x00), REG_SEQ0(0x0080006d, 0x00), REG_SEQ0(0x0080006e, 0x1a), REG_SEQ0(0x0080006f, 0x00), - REG_SEQ0(0x00800070, 0x96), REG_SEQ0(0x00800071, 0x02), - REG_SEQ0(0x00800073, 0x08), - REG_SEQ0(0x00800075, 0xe0), - REG_SEQ0(0x0080007a, 0x60), - REG_SEQ0(0x008000bd, 0x00), REG_SEQ0(0x008000be, 0x00), REG_SEQ0(0x008000bf, 0x00), REG_SEQ0(0x008000c0, 0x00), @@ -357,17 +337,6 @@ static const struct reg_sequence tas2783_init_seq[] = { REG_SEQ0(0x008000c2, 0x00), REG_SEQ0(0x008000c3, 0x00), REG_SEQ0(0x008000c4, 0x00), - REG_SEQ0(0x008000c5, 0x00), - REG_SEQ0(0x00800008, 0x49), - REG_SEQ0(0x00800009, 0x02), - REG_SEQ0(0x0080000a, 0x1a), - REG_SEQ0(0x0080000d, 0x93), - REG_SEQ0(0x0080000e, 0x82), - REG_SEQ0(0x0080000f, 0x42), - REG_SEQ0(0x00800010, 0x84), - REG_SEQ0(0x00800014, 0x0a), - REG_SEQ0(0x00800016, 0x00), - REG_SEQ0(0x00800060, 0x21), }; static int tas2783_sdca_mbq_size(struct device *dev, u32 reg) @@ -676,7 +645,8 @@ static void tas2783_set_calib_params_to_device(struct tas2783_prv *tas_dev, u32 } if (device_num == dev_count) - dev_err(tas_dev->dev, "device not found\n"); + dev_err(tas_dev->dev, + "unique id not found in the calib data\n"); else dev_dbg(tas_dev->dev, "calib data update done\n"); } @@ -735,13 +705,28 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev) return ret; } -static s32 read_header(const u8 *data, struct bin_header_t *hdr) +static s32 tas_fw_read_hdr(const u8 *data, struct tas_fw_hdr *hdr) +{ + hdr->size = get_unaligned_le32(data); + hdr->version_offset = get_unaligned_le32(&data[4]); + hdr->plt_id = get_unaligned_le32(&data[8]); + hdr->ppc3_ver = get_unaligned_le32(&data[12]); + memcpy(hdr->ddc_name, &data[16], 64); + hdr->timestamp = get_unaligned_le32(&data[80]); + + return 84; +} + +static s32 tas_fw_get_next_file(const u8 *data, struct tas_fw_file *file) { - hdr->vendor_id = get_unaligned_le16(&data[0]); - hdr->file_id = get_unaligned_le32(&data[2]); - hdr->version = get_unaligned_le16(&data[6]); - hdr->length = get_unaligned_le32(&data[8]); - return 12; + file->vendor_id = get_unaligned_le32(&data[0]); + file->file_id = get_unaligned_le32(&data[4]); + file->version = get_unaligned_le32(&data[8]); + file->length = get_unaligned_le32(&data[12]); + file->dest_addr = get_unaligned_le32(&data[16]); + file->fw_data = (u8 *)&data[20]; + + return file->length + sizeof(u32) * 5; } static void tas2783_fw_ready(const struct firmware *fmw, void *context) @@ -749,65 +734,68 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context) struct tas2783_prv *tas_dev = (struct tas2783_prv *)context; const u8 *buf = NULL; - s32 offset = 0, img_sz, file_blk_size, ret; - struct bin_header_t hdr; + s32 img_sz, ret, cur_file; + s32 offset = 0; + + struct tas_fw_hdr *hdr __free(kfree) = kzalloc(sizeof(*hdr), GFP_KERNEL); + struct tas_fw_file *file __free(kfree) = kzalloc(sizeof(*file), GFP_KERNEL); + if (!file || !hdr) { + ret = -ENOMEM; + goto out; + } if (!fmw || !fmw->data) { - /* No firmware binary, devices will work in ROM mode. */ + /* firmware binary not found*/ dev_err(tas_dev->dev, - "Failed to read %s, no side-effect on driver running\n", + "Failed to read fw binary %s\n", tas_dev->rca_binaryname); ret = -EINVAL; goto out; } - mutex_lock(&tas_dev->pde_lock); img_sz = fmw->size; buf = fmw->data; - offset += FW_DL_OFFSET; - while (offset < (img_sz - FW_FL_HDR)) { - memset(&hdr, 0, sizeof(hdr)); - offset += read_header(&buf[offset], &hdr); - dev_dbg(tas_dev->dev, - "vndr=%d, file=%d, version=%d, len=%d, off=%d\n", - hdr.vendor_id, hdr.file_id, hdr.version, - hdr.length, offset); - /* size also includes the header */ - file_blk_size = hdr.length - FW_FL_HDR; - - switch (hdr.file_id) { - case 0: - ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral, - PRAM_ADDR_START, file_blk_size, - &buf[offset]); - if (ret < 0) - dev_err(tas_dev->dev, - "PRAM update failed: %d", ret); - break; - - case 1: - ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral, - YRAM_ADDR_START, file_blk_size, - &buf[offset]); - if (ret < 0) - dev_err(tas_dev->dev, - "YRAM update failed: %d", ret); + offset += tas_fw_read_hdr(buf, hdr); + if (hdr->size != img_sz) { + ret = -EINVAL; + dev_err(tas_dev->dev, "firmware size mismatch with header"); + goto out; + } - break; + if (img_sz < FW_DL_OFFSET) { + ret = -EINVAL; + dev_err(tas_dev->dev, "unexpected size, size is too small"); + goto out; + } - default: - ret = -EINVAL; - dev_err(tas_dev->dev, "Unsupported file"); + mutex_lock(&tas_dev->pde_lock); + while (offset < (img_sz - FW_FL_HDR)) { + offset += tas_fw_get_next_file(&buf[offset], file); + dev_dbg(tas_dev->dev, + "v=%d, fid=%d, ver=%d, len=%d, daddr=0x%x, fw=%p", + file->vendor_id, file->file_id, + file->version, file->length, + file->dest_addr, file->fw_data); + + ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral, + file->dest_addr, + file->length, + file->fw_data); + if (ret < 0) { + dev_err(tas_dev->dev, + "FW download failed: %d", ret); break; } - - if (ret == 0) - offset += file_blk_size; - else - break; + cur_file++; } mutex_unlock(&tas_dev->pde_lock); - tas2783_update_calibdata(tas_dev); + + if (cur_file == 0) { + dev_err(tas_dev->dev, "fw with no files"); + ret = -EINVAL; + } else { + tas2783_update_calibdata(tas_dev); + } out: if (!ret) @@ -1184,8 +1172,21 @@ static const struct dev_pm_ops tas2783_sdca_pm = { RUNTIME_PM_OPS(tas2783_sdca_dev_suspend, tas2783_sdca_dev_resume, NULL) }; +static struct pci_dev *tas_get_pci_dev(struct sdw_slave *peripheral) +{ + struct device *dev = &peripheral->dev; + + for (; dev; dev = dev->parent) + if (dev->bus == &pci_bus_type) + return to_pci_dev(dev); + + return NULL; +} + static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) { + struct pci_dev *pci; + struct sdw_bus *bus; struct tas2783_prv *tas_dev = dev_get_drvdata(dev); s32 ret; u8 unique_id = tas_dev->sdw_peripheral->id.unique_id; @@ -1193,10 +1194,27 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) if (tas_dev->hw_init) return 0; + pci = tas_get_pci_dev(slave); + if (!pci) { + dev_err(dev, "pci device id can't be read"); + return -EINVAL; + } + + bus = slave->bus; tas_dev->fw_dl_task_done = false; tas_dev->fw_dl_success = false; + + ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1); + if (ret) { + dev_err(dev, "sw reset failed, err=%d", ret); + return ret; + } + usleep_range(2000, 2200); + + /* subsystem_id-link_id-unique_id */ scnprintf(tas_dev->rca_binaryname, sizeof(tas_dev->rca_binaryname), - "tas2783-%01x.bin", unique_id); + "%04X-%1X-%1X.bin", pci->subsystem_device, bus->link_id, + unique_id); ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, tas_dev->rca_binaryname, tas_dev->dev, @@ -1214,9 +1232,19 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) dev_err(tas_dev->dev, "fw request, wait_event timeout\n"); ret = -EAGAIN; } else { - ret = regmap_multi_reg_write(tas_dev->regmap, tas2783_init_seq, - ARRAY_SIZE(tas2783_init_seq)); - tas_dev->hw_init = true; + if (tas_dev->num_init_data > 0) + ret = regmap_multi_reg_write(tas_dev->regmap, + tas_dev->init_data, + tas_dev->num_init_data); + else + ret = regmap_multi_reg_write(tas_dev->regmap, tas2783_init_seq, + ARRAY_SIZE(tas2783_init_seq)); + + if (ret) + dev_err(tas_dev->dev, + "init writes failed, err=%d", ret); + else + tas_dev->hw_init = true; } return ret; @@ -1260,6 +1288,73 @@ static void tas_remove(struct tas2783_prv *tas_dev) snd_soc_unregister_component(tas_dev->dev); } +static s32 tas_get_smartamp_init_data(struct sdw_slave *peripheral, + struct tas2783_prv *tas_dev) +{ + int num_init_data, i; + struct device *dev; + struct reg_sequence *init_data; + struct sdca_device_data *sdca_data; + struct sdca_function_desc *function; + struct raw_init_data *raw __free(kfree) = NULL; + + dev = &peripheral->dev; + sdca_data = &peripheral->sdca_data; + + dev_dbg(dev, "number of functions found %d", + sdca_data->num_functions); + + if (sdca_data->num_functions <= 0) + return -EINVAL; + + /* look for smartamp function */ + for (i = 0; i < sdca_data->num_functions; i++) { + if (sdca_data->function[i].type == + SDCA_FUNCTION_TYPE_SMART_AMP) + break; + } + if (i == sdca_data->num_functions) + return -EINVAL; + + function = &sdca_data->function[i]; + num_init_data = fwnode_property_count_u8(function->node, + MIPI_INIT_TABLE); + if (num_init_data <= 0) { + dev_dbg(dev, "error reading init table for tas2783 err=%d", + num_init_data); + return num_init_data; + } + + if (num_init_data % sizeof(*raw) != 0) { + dev_dbg(dev, "%s should be integer multiple of %lu", + MIPI_INIT_TABLE, sizeof(*raw)); + return -EINVAL; + } + + raw = kzalloc(num_init_data, GFP_KERNEL); + if (!raw) + return -ENOMEM; + + fwnode_property_read_u8_array(function->node, MIPI_INIT_TABLE, + (u8 *)raw, num_init_data); + + num_init_data /= sizeof(*raw); + init_data = devm_kcalloc(dev, num_init_data, sizeof(*init_data), + GFP_KERNEL); + if (!init_data) + return -ENOMEM; + + for (i = 0; i < num_init_data; i++) { + init_data[i].reg = le32_to_cpu(raw[i].addr); + init_data[i].def = raw[i].val; + } + + tas_dev->num_init_data = num_init_data; + tas_dev->init_data = init_data; + + return 0; +} + static s32 tas_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) { @@ -1272,6 +1367,8 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, return dev_err_probe(dev, -ENOMEM, "Failed devm_kzalloc"); + tas_get_smartamp_init_data(peripheral, tas_dev); + tas_dev->dev = dev; tas_dev->sdw_peripheral = peripheral; tas_dev->hw_init = false; @@ -1325,6 +1422,7 @@ static struct sdw_driver tas_sdw_driver = { }; module_sdw_driver(tas_sdw_driver); +MODULE_IMPORT_NS("SND_SOC_SDCA"); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("ASoC TAS2783 SoundWire Driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2783.h b/sound/soc/codecs/tas2783.h index 794333e0a35029..bf34319c9a9fb7 100644 --- a/sound/soc/codecs/tas2783.h +++ b/sound/soc/codecs/tas2783.h @@ -28,6 +28,7 @@ #define TASDEV_REG_SDW(book, page, reg) (((book) * 256 * 128) + \ 0x800000 + ((page) * 128) + (reg)) +#define TAS2783_SW_RESET TASDEV_REG_SDW(0x0, 0x00, 0x01) /* Volume control */ #define TAS2783_DVC_LVL TASDEV_REG_SDW(0x0, 0x00, 0x1A) #define TAS2783_AMP_LEVEL TASDEV_REG_SDW(0x0, 0x00, 0x03) diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index ec9fd8486c0534..f12d42986a758e 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -950,7 +950,7 @@ static const struct snd_soc_acpi_adr_device cs42l42_0_adr[] = { static const struct snd_soc_acpi_adr_device tas2783_0_adr[] = { { - .adr = 0x0000380102000001ull, + .adr = 0x00003c0102000001ull, .num_endpoints = 1, .endpoints = &spk_l_endpoint, .name_prefix = "tas2783-1" @@ -960,6 +960,18 @@ static const struct snd_soc_acpi_adr_device tas2783_0_adr[] = { .num_endpoints = 1, .endpoints = &spk_r_endpoint, .name_prefix = "tas2783-2" + }, + { + .adr = 0x00003d0102000001ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "tas2783-3" + }, + { + .adr = 0x00003a0102000001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "tas2783-4" } }; diff --git a/sound/soc/sdw_utils/soc_sdw_ti_amp.c b/sound/soc/sdw_utils/soc_sdw_ti_amp.c index f0011360ae9b6c..e1cfb388cb31f0 100644 --- a/sound/soc/sdw_utils/soc_sdw_ti_amp.c +++ b/sound/soc/sdw_utils/soc_sdw_ti_amp.c @@ -57,6 +57,10 @@ int asoc_sdw_ti_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, strscpy(speaker, "Left Spk", sizeof(speaker)); } else if (!strncmp(prefix, "tas2783-2", strlen("tas2783-2"))) { strscpy(speaker, "Right Spk", sizeof(speaker)); + } else if (!strncmp(prefix, "tas2783-3", strlen("tas2783-3"))) { + strscpy(speaker, "Left Spk2", sizeof(speaker)); + } else if (!strncmp(prefix, "tas2783-4", strlen("tas2783-4"))) { + strscpy(speaker, "Right Spk2", sizeof(speaker)); } else { ret = -EINVAL; dev_err(card->dev, "unhandled prefix %s", prefix); diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 5ef9c4e88078cc..c5723974c5d2bc 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -40,11 +40,25 @@ static const struct snd_soc_dapm_widget lr_spk_widgets[] = { SND_SOC_DAPM_SPK("Right Spk", NULL), }; +static const struct snd_soc_dapm_widget lr_4spk_widgets[] = { + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_SPK("Left Spk2", NULL), + SND_SOC_DAPM_SPK("Right Spk2", NULL), +}; + static const struct snd_kcontrol_new lr_spk_controls[] = { SOC_DAPM_PIN_SWITCH("Left Spk"), SOC_DAPM_PIN_SWITCH("Right Spk"), }; +static const struct snd_kcontrol_new lr_4spk_controls[] = { + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), + SOC_DAPM_PIN_SWITCH("Left Spk2"), + SOC_DAPM_PIN_SWITCH("Right Spk2"), +}; + static const struct snd_soc_dapm_widget rt700_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_MIC("AMIC", NULL), @@ -69,10 +83,10 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .init = asoc_sdw_ti_amp_init, .rtd_init = asoc_sdw_ti_spk_rtd_init, - .controls = lr_spk_controls, - .num_controls = ARRAY_SIZE(lr_spk_controls), - .widgets = lr_spk_widgets, - .num_widgets = ARRAY_SIZE(lr_spk_widgets), + .controls = lr_4spk_controls, + .num_controls = ARRAY_SIZE(lr_4spk_controls), + .widgets = lr_4spk_widgets, + .num_widgets = ARRAY_SIZE(lr_4spk_widgets), }, }, .dai_num = 1,