All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/3] ALSA: hda/tas2781: Add tas2781 driver for SPI.
@ 2024-04-09  2:48 Baojun Xu
  2024-04-09  2:48 ` [PATCH v2 1/3] ALSA: hda/tas2781: Modification for add " Baojun Xu
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Baojun Xu @ 2024-04-09  2:48 UTC (permalink / raw)
  To: tiwai
  Cc: robh+dt, andriy.shevchenko, lgirdwood, perex,
	pierre-louis.bossart, kevin-lu, shenghao-ding, navada,
	alsa-devel, linux-kernel, liam.r.girdwood, yung-chuan.liao,
	baojun.xu, broonie, soyer

This patch was used to add TAS2781 devices on SPI support in sound/pci/hda.
It use ACPI node descript about parameters of TAS2781 on SPI, it like:
    Scope (_SB.PC00.SPI0)
    {
        Device (GSPK)
        {
            Name (_HID, "TXNW2781")  // _HID: Hardware ID
            Method (_CRS, 0, NotSerialized)
            {
                Name (RBUF, ResourceTemplate ()
                {
                    SpiSerialBusV2 (...)
                    SpiSerialBusV2 (...)
                }
            }
        }
    }

And in platform/x86/serial-multi-instantiate.c, those spi devices will be
added into system as a single SPI device, so TAS2781 SPI driver will
probe twice for every single SPI device. And driver will also parser
mono DSP firmware binary and RCA binary for itself.

Signed-off-by: Baojun Xu <baojun.xu@ti.com>

Baojun Xu (3):
  ALSA: hda/tas2781: Modification for add tas2781 driver for SPI
  ALSA: hda/tas2781: Main code of tas2781 driver for SPI
  ALSA: hda/tas2781: Firmware load for tas2781 driver for SPI

 drivers/acpi/scan.c                           |    1 +
 .../platform/x86/serial-multi-instantiate.c   |   10 +
 sound/pci/hda/Kconfig                         |   15 +
 sound/pci/hda/Makefile                        |    2 +
 sound/pci/hda/patch_realtek.c                 |   15 +
 sound/pci/hda/tas2781-spi.h                   |  162 ++
 sound/pci/hda/tas2781_hda_spi.c               | 1336 ++++++++++
 sound/pci/hda/tas2781_spi_fwlib.c             | 2289 +++++++++++++++++
 8 files changed, 3830 insertions(+)
 create mode 100644 sound/pci/hda/tas2781-spi.h
 create mode 100644 sound/pci/hda/tas2781_hda_spi.c
 create mode 100644 sound/pci/hda/tas2781_spi_fwlib.c

-- 
2.40.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 1/3] ALSA: hda/tas2781: Modification for add tas2781 driver for SPI
  2024-04-09  2:48 [PATCH v2 0/3] ALSA: hda/tas2781: Add tas2781 driver for SPI Baojun Xu
@ 2024-04-09  2:48 ` Baojun Xu
  2024-04-09 13:02   ` Andy Shevchenko
  2024-04-09  2:48 ` [PATCH v2 2/3] ALSA: hda/tas2781: Main code of " Baojun Xu
  2024-04-09  2:48 ` [PATCH v2 3/3] ALSA: hda/tas2781: Firmware load for " Baojun Xu
  2 siblings, 1 reply; 13+ messages in thread
From: Baojun Xu @ 2024-04-09  2:48 UTC (permalink / raw)
  To: tiwai
  Cc: robh+dt, andriy.shevchenko, lgirdwood, perex,
	pierre-louis.bossart, kevin-lu, shenghao-ding, navada,
	alsa-devel, linux-kernel, liam.r.girdwood, yung-chuan.liao,
	baojun.xu, broonie, soyer

Integrate tas2781 configs for HP Laptops. Every tas2781 in the laptop
will work as a single speaker on SPI bus. The code support realtek as
the primary codec.

Signed-off-by: Baojun Xu <baojun.xu@ti.com>

---
 - Change depends on SPI to SPI_MASTER in sound/pci/hda/Kconfig
 - Add select SND_HDA_SCODEC_COMPONENT in sound/pci/hda/Kconfig
 - Change comp_generic_fixup() 5th parameter from "-%s:00" to
   "-%s:00-tas2781-hda.%d" in sound/pci/hda/patch_realtek.c
 - Change chain_id from ALC285_FIXUP_THINKPAD_HEADSET_JACK to
   ALC285_FIXUP_HP_GPIO_LED in sound/pci/hda/patch_realtek.c
 - Remove project name "Gemtree"
---
 drivers/acpi/scan.c                             |  1 +
 drivers/platform/x86/serial-multi-instantiate.c | 10 ++++++++++
 sound/pci/hda/Kconfig                           | 15 +++++++++++++++
 sound/pci/hda/Makefile                          |  2 ++
 sound/pci/hda/patch_realtek.c                   | 15 +++++++++++++++
 5 files changed, 43 insertions(+)

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 7c157bf92695..8166343f26d0 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1766,6 +1766,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
 		{"INT33FE", },
 		{"INT3515", },
 		/* Non-conforming _HID for Cirrus Logic already released */
+		{"TXNW2781", },
 		{"CLSA0100", },
 		{"CLSA0101", },
 	/*
diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c
index 97b9c6392230..f54636b576d1 100644
--- a/drivers/platform/x86/serial-multi-instantiate.c
+++ b/drivers/platform/x86/serial-multi-instantiate.c
@@ -368,6 +368,15 @@ static const struct smi_node cs35l57_hda = {
 	.bus_type = SMI_AUTO_DETECT,
 };
 
+static const struct smi_node tas2781_hda = {
+	.instances = {
+		{ "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+		{ "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+		{}
+	},
+	.bus_type = SMI_AUTO_DETECT,
+};
+
 /*
  * Note new device-ids must also be added to ignore_serial_bus_ids in
  * drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
@@ -383,6 +392,7 @@ static const struct acpi_device_id smi_acpi_ids[] = {
 	/* Non-conforming _HID for Cirrus Logic already released */
 	{ "CLSA0100", (unsigned long)&cs35l41_hda },
 	{ "CLSA0101", (unsigned long)&cs35l41_hda },
+	{ "TXNW2781", (unsigned long)&tas2781_hda },
 	{ }
 };
 MODULE_DEVICE_TABLE(acpi, smi_acpi_ids);
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index f806636242ee..f25ad9ea51d9 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -202,6 +202,21 @@ config SND_HDA_SCODEC_TAS2781_I2C
 comment "Set to Y if you want auto-loading the side codec driver"
 	depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m
 
+config SND_HDA_SCODEC_TAS2781_SPI
+	tristate "Build TAS2781 HD-audio side codec support for SPI Bus"
+	depends on SPI_MASTER
+	depends on ACPI
+	depends on EFI
+	depends on SND_SOC
+	select SND_HDA_SCODEC_COMPONENT
+	select CRC32_SARWATE
+	help
+	  Say Y or M here to include TAS2781 SPI HD-audio side codec support
+	  in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+	depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m
+
 config SND_HDA_CODEC_REALTEK
 	tristate "Build Realtek HD-audio codec support"
 	select SND_HDA_GENERIC
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 13e04e1f65de..9785cddb4845 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -39,6 +39,7 @@ snd-hda-scodec-cs35l56-spi-objs :=	cs35l56_hda_spi.o
 snd-hda-cs-dsp-ctls-objs :=		hda_cs_dsp_ctl.o
 snd-hda-scodec-component-objs :=	hda_component.o
 snd-hda-scodec-tas2781-i2c-objs :=	tas2781_hda_i2c.o
+snd-hda-scodec-tas2781-spi-objs :=	tas2781_hda_spi.o tas2781_spi_fwlib.o
 
 # common driver
 obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
@@ -70,6 +71,7 @@ obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o
 obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o
 obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o
 obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o
+obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o
 
 # this must be the last entry after codec drivers;
 # otherwise the codec patches won't be hooked before the PCI probe
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index cdcb28aa9d7b..4945309f45ae 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -6913,6 +6913,13 @@ static void tas2781_fixup_i2c(struct hda_codec *cdc,
 	comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1);
 }
 
+static void tas2781_fixup_spi(struct hda_codec *cdc,
+	const struct hda_fixup *fix, int action)
+{
+	comp_generic_fixup(cdc, action, "spi", "TXNW2781",
+		"-%s:00-tas2781-hda.%d", 2);
+}
+
 static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc,
 	const struct hda_fixup *fix, int action)
 {
@@ -7451,6 +7458,7 @@ enum {
 	ALC236_FIXUP_DELL_DUAL_CODECS,
 	ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
 	ALC287_FIXUP_TAS2781_I2C,
+	ALC245_FIXUP_TAS2781_SPI_2,
 	ALC287_FIXUP_YOGA7_14ARB7_I2C,
 	ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
 	ALC245_FIXUP_HP_X360_MUTE_LEDS,
@@ -9614,6 +9622,12 @@ static const struct hda_fixup alc269_fixups[] = {
 		.chained = true,
 		.chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
 	},
+	[ALC245_FIXUP_TAS2781_SPI_2] = {
+		.type = HDA_FIXUP_FUNC,
+		.v.func = tas2781_fixup_spi,
+		.chained = true,
+		.chain_id = ALC285_FIXUP_HP_GPIO_LED,
+	},
 	[ALC287_FIXUP_YOGA7_14ARB7_I2C] = {
 		.type = HDA_FIXUP_FUNC,
 		.v.func = yoga7_14arb7_fixup_i2c,
@@ -10038,6 +10052,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
 	SND_PCI_QUIRK(0x103c, 0x8b8d, "HP", ALC236_FIXUP_HP_GPIO_LED),
 	SND_PCI_QUIRK(0x103c, 0x8b8f, "HP", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
 	SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+	SND_PCI_QUIRK(0x103c, 0x8b93, "HP", ALC245_FIXUP_TAS2781_SPI_2),
 	SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
 	SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
 	SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 2/3] ALSA: hda/tas2781: Main code of tas2781 driver for SPI
  2024-04-09  2:48 [PATCH v2 0/3] ALSA: hda/tas2781: Add tas2781 driver for SPI Baojun Xu
  2024-04-09  2:48 ` [PATCH v2 1/3] ALSA: hda/tas2781: Modification for add " Baojun Xu
@ 2024-04-09  2:48 ` Baojun Xu
  2024-04-09 13:21   ` Andy Shevchenko
  2024-04-09 17:00   ` kernel test robot
  2024-04-09  2:48 ` [PATCH v2 3/3] ALSA: hda/tas2781: Firmware load for " Baojun Xu
  2 siblings, 2 replies; 13+ messages in thread
From: Baojun Xu @ 2024-04-09  2:48 UTC (permalink / raw)
  To: tiwai
  Cc: robh+dt, andriy.shevchenko, lgirdwood, perex,
	pierre-louis.bossart, kevin-lu, shenghao-ding, navada,
	alsa-devel, linux-kernel, liam.r.girdwood, yung-chuan.liao,
	baojun.xu, broonie, soyer

Main source code for tas2781 driver for SPI.

Signed-off-by: Baojun Xu <baojun.xu@ti.com>

---
v2:
 - Update information in file header of tas2781-spi.h.
 - Remove useless micro define.
 - Change TASDEVICE_I2CChecksum to TASDEVICE_Checksum
 - Remove enum control_bus as current code support SPI only.
 - Remove device define as current code support TAS2781 only.
 - Remove spi_device **spi_devs in structure tasdevice_priv.
 - Remove cal_binaryname in structure tasdevice_priv.
 - Remove ndev in structure tasdevice_priv.
 - Change isacpi and  isspi, replace by control_bus(I2C or SPI).
 - Remove void tasdevice_spi_dsp_remove(void *context).
 - Add acpi_device and irq in structure tas2781_hda in tas2781_hda_spi.c.
 - Remove parameter chn in all of registers access functions.
 - Add tascodec_spi_init().
 - Add chip select gpio set for SPI device.
 - Change tasdevice_tuning_switch() to tasdevice_spi_tuning_switch().
 - Change device offset from tas_priv->ndev to tas_priv->index.
 - Change tasdevice_dsp_remove() to tasdevice_spi_dsp_remove().
 - tasdevice_prmg_load() to tasdevice_spi_prmg_load().
 - Change tasdevice_config_info_remove() to
   tasdevice_spi_config_info_remove().
 - Add one dummy byte shift when read from page 2~127, or not book 0.
 - Change tasdevice_spi_change_chn_book() to tasdevice_spi_switch_book()
 - Perform put_device(physdev) before error return.
 - Change calibration data id to 2781 from 2783 in EFI
 - Change calibration data structure name in comments.
---
 sound/pci/hda/tas2781-spi.h     |  162 ++++
 sound/pci/hda/tas2781_hda_spi.c | 1336 +++++++++++++++++++++++++++++++
 2 files changed, 1498 insertions(+)
 create mode 100644 sound/pci/hda/tas2781-spi.h
 create mode 100644 sound/pci/hda/tas2781_hda_spi.c

diff --git a/sound/pci/hda/tas2781-spi.h b/sound/pci/hda/tas2781-spi.h
new file mode 100644
index 000000000000..2540bce175d1
--- /dev/null
+++ b/sound/pci/hda/tas2781-spi.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+//
+// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2024 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2781 driver implements a flexible and configurable
+// algo coefficient setting for TAS2781 chips.
+//
+// Author: Baojun Xu <baojun.xu@ti.com>
+//
+
+#ifndef __TAS2781_SPI_H__
+#define __TAS2781_SPI_H__
+
+#include <sound/tas2781-dsp.h>
+
+#define TASDEVICE_RATES			(SNDRV_PCM_RATE_44100 |\
+	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+	SNDRV_PCM_RATE_88200)
+
+#define TASDEVICE_FORMATS		(SNDRV_PCM_FMTBIT_S16_LE | \
+	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/*PAGE Control Register (available in page0 of each book) */
+#define TASDEVICE_PAGE_SELECT		0x00
+#define TASDEVICE_BOOKCTL_PAGE		0x00
+#define TASDEVICE_BOOKCTL_REG		127
+#define TASDEVICE_BOOK_ID(reg)		(reg / (256 * 128))
+#define TASDEVICE_PAGE_ID(reg)		((reg % (256 * 128)) / 128)
+#define TASDEVICE_PAGE_REG(reg)		((reg % (256 * 128)) % 128)
+#define TASDEVICE_PGRG(reg)		(reg % (256 * 128))
+#define TASDEVICE_REG(book, page, reg)	(((book * 256 * 128) + \
+					(page * 128)) + reg)
+
+/*Software Reset */
+#define TAS2781_REG_SWRESET		TASDEVICE_REG(0x0, 0X0, 0x01)
+#define TAS2781_REG_SWRESET_RESET	BIT(0)
+
+/*I2C Checksum */
+#define TASDEVICE_Checksum		TASDEVICE_REG(0x0, 0x0, 0x7E)
+
+/* Volume control */
+#define TAS2781_DVC_LVL			TASDEVICE_REG(0x0, 0x0, 0x1A)
+#define TAS2781_AMP_LEVEL		TASDEVICE_REG(0x0, 0x0, 0x03)
+#define TAS2781_AMP_LEVEL_MASK		GENMASK(5, 1)
+
+#define TASDEVICE_CMD_SING_W		0x1
+#define TASDEVICE_CMD_BURST		0x2
+#define TASDEVICE_CMD_DELAY		0x3
+#define TASDEVICE_CMD_FIELD_W		0x4
+
+#define TAS2781_SPI_MAX_FREQ		4000000
+
+enum device_catlog_id {
+	HP = 0,
+	OTHERS
+};
+
+struct tasdevice {
+	struct tasdevice_fw *cali_data_fmw;
+	unsigned int dev_addr;
+	unsigned int err_code;
+	unsigned char cur_book;
+	short cur_prog;
+	short cur_conf;
+	bool is_loading;
+	bool is_loaderr;
+};
+
+struct tasdevice_irqinfo {
+	int irq_gpio;
+	int irq;
+};
+
+struct calidata {
+	unsigned char *data;
+	unsigned long total_sz;
+};
+
+struct tasdevice_priv {
+	struct tasdevice tasdevice;
+	struct tasdevice_irqinfo irq_info;
+	struct tasdevice_rca rcabin;
+	struct calidata cali_data;
+	struct tasdevice_fw *fmw;
+	struct gpio_desc *reset;
+	struct gpio_desc *chipselect;
+	struct mutex codec_lock;
+	struct regmap *regmap;
+	struct device *dev;
+	struct tm tm;
+
+	enum device_catlog_id catlog_id;
+	const char *acpi_subsystem_id;
+	unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE];
+	unsigned char coef_binaryname[64];
+	unsigned char rca_binaryname[64];
+	unsigned char dev_name[32];
+	unsigned int magic_num;
+	unsigned int chip_id;
+	unsigned int sysclk;
+
+	int cur_prog;
+	int cur_conf;
+	int fw_state;
+	int index;
+	void *client;
+	void *codec;
+	bool force_fwload_status;
+	bool playback_started;
+	unsigned int global_addr;
+
+	int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv,
+		const struct firmware *fmw, int offset);
+	int (*fw_parse_program_data)(struct tasdevice_priv *tas_priv,
+		struct tasdevice_fw *tas_fmw,
+		const struct firmware *fmw, int offset);
+	int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_priv,
+		struct tasdevice_fw *tas_fmw,
+		const struct firmware *fmw, int offset);
+	int (*tasdevice_load_block)(struct tasdevice_priv *tas_priv,
+		struct tasdev_blk *block);
+
+	int (*save_calibration)(struct tasdevice_priv *tas_priv);
+	void (*apply_calibration)(struct tasdevice_priv *tas_priv);
+};
+
+int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
+	struct module *module,
+	void (*cont)(const struct firmware *fw, void *context));
+int tasdevice_save_calibration(struct tasdevice_priv *tas_priv);
+void tasdevice_apply_calibration(struct tasdevice_priv *tas_priv);
+void tasdevice_remove(struct tasdevice_priv *tas_priv);
+int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
+	unsigned int reg, unsigned int *value);
+int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv,
+	unsigned int reg, unsigned int value);
+int tasdevice_spi_dev_bulk_write(
+	struct tasdevice_priv *tas_priv, unsigned int reg,
+	unsigned char *p_data, unsigned int n_length);
+int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
+	unsigned int reg, unsigned char *p_data, unsigned int n_length);
+int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tasdevice,
+	unsigned int reg, unsigned int mask, unsigned int value);
+
+void tasdevice_spi_select_cfg_blk(void *context, int conf_no,
+	unsigned char block_type);
+void tasdevice_spi_config_info_remove(void *context);
+int tasdevice_spi_dsp_parser(void *context);
+int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw);
+void tasdevice_spi_dsp_remove(void *context);
+void tasdevice_spi_calbin_remove(void *context);
+int tasdevice_spi_select_tuningprm_cfg(void *context, int prm,
+	int cfg_no, int rca_conf_no);
+int tasdevice_spi_prmg_load(void *context, int prm_no);
+int tasdevice_spi_prmg_calibdata_load(void *context, int prm_no);
+void tasdevice_spi_tuning_switch(void *context, int state);
+int tas2781_spi_load_calibration(void *context, char *file_name,
+	unsigned short i);
+#endif /* __TAS2781_SPI_H__ */
diff --git a/sound/pci/hda/tas2781_hda_spi.c b/sound/pci/hda/tas2781_hda_spi.c
new file mode 100644
index 000000000000..0ac3757ac259
--- /dev/null
+++ b/sound/pci/hda/tas2781_hda_spi.c
@@ -0,0 +1,1336 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA SPI driver
+//
+// Copyright 2024 Texas Instruments, Inc.
+//
+// Author: Baojun Xu <baojun.xu@ti.com>
+
+#include <linux/acpi.h>
+#include <linux/crc8.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/firmware.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/hda_codec.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/tas2781-tlv.h>
+
+#include "tas2781-spi.h"
+
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_component.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+
+#define TASDEVICE_CRC8_POLYNOMIAL	0x4d
+
+#define TASDEVICE_SPEAKER_CALIBRATION_SIZE	20
+
+/* No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD
+ * Define two controls, one is Volume control callbacks, the other is
+ * flag setting control callbacks.
+ */
+
+/* Volume control callbacks for tas2781 */
+#define ACARD_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \
+	xhandler_get, xhandler_put, tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = (xname),\
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw_range, \
+	.get = xhandler_get, .put = xhandler_put, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = xreg, .rreg = xreg, .shift = xshift, \
+		 .rshift = xshift, .min = xmin, .max = xmax, \
+		 .invert = xinvert} }
+
+/* Flag control callbacks for tas2781 */
+#define ACARD_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, \
+	.info = snd_ctl_boolean_mono_info, \
+	.get = xhandler_get, .put = xhandler_put, \
+	.private_value = xdata }
+
+
+enum calib_data {
+	R0_VAL = 0,
+	INV_R0,
+	R0LOW,
+	POWER,
+	TLIM,
+	CALIB_MAX
+};
+
+struct tas2781_hda {
+	struct device *dev;
+	struct gpio_desc *cs_gpio;
+	struct gpio_desc *reset_gpio;
+	struct tasdevice_priv *priv;
+	struct acpi_device *dacpi;
+	struct snd_kcontrol *dsp_prog_ctl;
+	struct snd_kcontrol *dsp_conf_ctl;
+	struct snd_kcontrol *prof_ctl;
+	struct snd_kcontrol *snd_ctls[3];
+
+	int irq;
+};
+
+static const struct regmap_range_cfg tasdevice_ranges[] = {
+	{
+		.range_min = 0,
+		.range_max = 256 * 128,
+		.selector_reg = TASDEVICE_PAGE_SELECT,
+		.selector_mask = 0xff,
+		.selector_shift = 0,
+		.window_start = 0,
+		.window_len = 128,
+	},
+};
+
+static const struct regmap_config tasdevice_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+	.ranges = tasdevice_ranges,
+	.num_ranges = ARRAY_SIZE(tasdevice_ranges),
+	.max_register = 256 * 128,
+};
+
+
+static void tas2781_spi_reset(struct tasdevice_priv *tas_dev);
+static int tasdevice_spi_init(struct tasdevice_priv *tas_priv);
+static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv,
+	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
+static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv,
+	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
+static int tasdevice_spi_digital_putvol(struct tasdevice_priv *tas_priv,
+	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
+static int tasdevice_spi_digital_getvol(struct tasdevice_priv *tas_priv,
+	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
+
+static int tasdevice_spi_switch_book(struct tasdevice_priv *tas_priv,
+	int book)
+{
+	struct tasdevice *tasdev = &tas_priv->tasdevice;
+	struct regmap *map = tas_priv->regmap;
+	int ret = 0;
+
+	if (tasdev->cur_book != book) {
+		/* Change to page 0 before book change. */
+		ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
+		if (ret < 0) {
+			dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+			return ret;
+		}
+		ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
+		if (ret < 0)
+			dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+		tasdev->cur_book = book;
+	}
+
+	return ret;
+}
+
+int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
+	unsigned int reg, unsigned int *val)
+{
+	struct regmap *map = tas_priv->regmap;
+	int ret;
+
+	ret = tasdevice_spi_switch_book(tas_priv, TASDEVICE_BOOK_ID(reg));
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * In our TAS2781 SPI mode, if read from other book (not book 0),
+	 * or read from page number larger than 1 in book 0, one byte more
+	 * read is needed, and first byte is a dummy byte, need to be ignored.
+	 */
+	if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
+		unsigned char data[2];
+
+		ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, 2);
+		*val = data[1];
+	} else {
+		ret = regmap_read(map, TASDEVICE_PGRG(reg), val);
+	}
+	if (ret < 0)
+		dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_spi_dev_read);
+
+int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv,
+	unsigned int reg, unsigned int value)
+{
+	struct regmap *map = tas_priv->regmap;
+	int ret;
+
+	ret = tasdevice_spi_switch_book(tas_priv, TASDEVICE_BOOK_ID(reg));
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_write(map, TASDEVICE_PGRG(reg), value);
+	if (ret < 0)
+		dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_spi_dev_write);
+
+int tasdevice_spi_dev_bulk_write(struct tasdevice_priv *tas_priv,
+	unsigned int reg, unsigned char *data, unsigned int len)
+{
+	struct regmap *map = tas_priv->regmap;
+	int ret;
+
+	ret = tasdevice_spi_switch_book(tas_priv, TASDEVICE_BOOK_ID(reg));
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_bulk_write(map, TASDEVICE_PGRG(reg), data, len);
+	if (ret < 0)
+		dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_spi_dev_bulk_write);
+
+int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
+	unsigned int reg, unsigned char *data, unsigned int len)
+{
+	struct regmap *map = tas_priv->regmap;
+	int ret;
+
+	ret = tasdevice_spi_switch_book(tas_priv, TASDEVICE_BOOK_ID(reg));
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * In our TAS2781 SPI mode, if read from other book (not book 0),
+	 * or read from page number larger than 1 in book 0, one byte more
+	 * read is needed, and first byte is a dummy byte, need to be ignored.
+	 */
+	if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
+		unsigned char buf[129];
+
+		ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg),
+			buf, len + 1);
+		memcpy(data, buf + 1, len);
+	} else {
+		ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len);
+	}
+	if (ret < 0)
+		dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_spi_dev_bulk_read);
+
+int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tas_priv,
+	unsigned int reg, unsigned int mask, unsigned int value)
+{
+	struct regmap *map = tas_priv->regmap;
+	int ret;
+
+	ret = tasdevice_spi_switch_book(tas_priv, TASDEVICE_BOOK_ID(reg));
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_update_bits(map, TASDEVICE_PGRG(reg), mask, value);
+	if (ret < 0)
+		dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_spi_dev_update_bits);
+
+static void tas2781_spi_reset(struct tasdevice_priv *tas_dev)
+{
+	int ret;
+
+	if (tas_dev->reset) {
+		gpiod_set_value_cansleep(tas_dev->reset, 0);
+		usleep_range(500, 1000);
+		gpiod_set_value_cansleep(tas_dev->reset, 1);
+	} else {
+		ret = tasdevice_spi_dev_write(tas_dev,
+			TAS2781_REG_SWRESET, TAS2781_REG_SWRESET_RESET);
+		if (ret < 0)
+			dev_err(tas_dev->dev, "dev sw-reset fail, %d\n", ret);
+	}
+	usleep_range(1000, 1050);
+}
+
+static int tascodec_spi_init(struct tasdevice_priv *tas_priv, void *codec,
+	struct module *module,
+	void (*cont)(const struct firmware *fw, void *context))
+{
+	int ret = 0;
+
+	/*
+	 * Codec Lock Hold to ensure that codec_probe and firmware parsing and
+	 * loading do not simultaneously execute.
+	 */
+	mutex_lock(&tas_priv->codec_lock);
+
+	scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin",
+		tas_priv->dev_name, tas_priv->index);
+	crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
+	tas_priv->codec = codec;
+	ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
+		tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
+		cont);
+	if (ret)
+		dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n",
+			ret);
+
+	/* Codec Lock Release*/
+	mutex_unlock(&tas_priv->codec_lock);
+	return ret;
+}
+
+static int tasdevice_spi_init(struct tasdevice_priv *tas_priv)
+{
+	tas_priv->cur_prog = -1;
+	tas_priv->cur_conf = -1;
+
+	tas_priv->tasdevice.cur_book = -1;
+	tas_priv->tasdevice.cur_prog = -1;
+	tas_priv->tasdevice.cur_conf = -1;
+
+	mutex_init(&tas_priv->codec_lock);
+
+	return 0;
+}
+
+static int tasdevice_clamp(int val, int max, unsigned int invert)
+{
+	if (val > max)
+		val = max;
+	if (invert)
+		val = max - val;
+	if (val < 0)
+		val = 0;
+	return val;
+}
+
+static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv,
+	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+	unsigned int invert = mc->invert;
+	unsigned char mask;
+	int max = mc->max;
+	int val, ret;
+
+	mask = (1 << fls(max)) - 1;
+	mask <<= mc->shift;
+	val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
+	ret = tasdevice_spi_dev_update_bits(tas_priv,
+		mc->reg, mask, (unsigned int)(val << mc->shift));
+	if (ret)
+		dev_err(tas_priv->dev, "set AMP vol error in dev %d\n",
+			tas_priv->index);
+
+	return ret;
+}
+
+static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv,
+	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+	unsigned int invert = mc->invert;
+	unsigned char mask = 0;
+	int max = mc->max;
+	int ret = 0;
+	int val;
+
+	/* Read the primary device */
+	ret = tasdevice_spi_dev_read(tas_priv, mc->reg, &val);
+	if (ret) {
+		dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
+		return ret;
+	}
+
+	mask = (1 << fls(max)) - 1;
+	mask <<= mc->shift;
+	val = (val & mask) >> mc->shift;
+	val = tasdevice_clamp(val, max, invert);
+	ucontrol->value.integer.value[0] = val;
+
+	return ret;
+
+}
+
+static int tasdevice_spi_digital_putvol(struct tasdevice_priv *tas_priv,
+	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+	unsigned int invert = mc->invert;
+	int max = mc->max;
+	int val, ret;
+
+	val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
+
+	ret = tasdevice_spi_dev_write(tas_priv, mc->reg, (unsigned int)val);
+	if (ret)
+		dev_err(tas_priv->dev, "set digital vol err in dev %d\n",
+			tas_priv->index);
+
+	return ret;
+
+}
+
+static int tasdevice_spi_digital_getvol(struct tasdevice_priv *tas_priv,
+	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+	unsigned int invert = mc->invert;
+	int max = mc->max;
+	int ret, val;
+
+	/* Read the primary device as the whole */
+	ret = tasdevice_spi_dev_read(tas_priv, mc->reg, &val);
+	if (ret) {
+		dev_err(tas_priv->dev, "%s, get digital vol err\n", __func__);
+		return ret;
+	}
+
+	val = tasdevice_clamp(val, max, invert);
+	ucontrol->value.integer.value[0] = val;
+
+	return ret;
+
+}
+
+static int tas2781_read_acpi(struct tas2781_hda *tas_hda, const char *hid,
+	int id)
+{
+	struct tasdevice_priv *p = tas_hda->priv;
+	struct gpio_desc *cs_gpiod;
+	struct acpi_device *adev;
+	struct device *physdev;
+	struct spi_device *spi;
+	u32 values[HDA_MAX_COMPONENTS];
+	const char *sub, *property;
+	size_t nval;
+	int ret, i;
+
+	adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+	if (!adev) {
+		dev_err(p->dev,
+			"Failed to find an ACPI device for %s\n", hid);
+		return -ENODEV;
+	}
+
+	strscpy(p->dev_name, hid, sizeof(p->dev_name));
+	tas_hda->dacpi = adev;
+	physdev = get_device(acpi_get_first_physical_node(adev));
+
+	/* No side-effect to the playback even if subsystem_id is NULL*/
+	sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
+	if (IS_ERR(sub))
+		sub = NULL;
+
+	p->acpi_subsystem_id = sub;
+
+	property = "ti,dev-index";
+	ret = device_property_count_u32(physdev, property);
+	if (ret <= 0)
+		goto err;
+
+	if (ret > ARRAY_SIZE(values)) {
+		ret = -EINVAL;
+		goto err;
+	}
+	nval = ret;
+
+	ret = device_property_read_u32_array(physdev, property, values, nval);
+	if (ret)
+		goto err;
+
+	p->index = 0xFF;
+	for (i = 0; i < nval; i++) {
+		if (values[i] == id) {
+			p->index = i;
+			break;
+		}
+	}
+	/*
+	 * It's not an error for the ID to be missing: for I2C there can be
+	 * an alias address that is not a real device. So reject silently.
+	 */
+	if (p->index == 0xFF) {
+		dev_dbg(p->dev, "No index found in %s\n", property);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	p->reset = devm_gpiod_get_index_optional(physdev,
+		"reset", p->index, GPIOD_OUT_LOW);
+	if (IS_ERR(p->reset)) {
+		ret = PTR_ERR(p->reset);
+
+		/*
+		 * If RESET is shared the first amp to probe will grab the
+		 * reset line and reset all the amps
+		 */
+		if (ret != -EBUSY) {
+			dev_err_probe(p->dev, ret,
+				"Failed to get reset GPIO\n");
+			goto err;
+		}
+
+		dev_info(p->dev, "Reset GPIO busy, assume shared reset\n");
+		p->reset = NULL;
+	}
+
+	/*
+	 * Manually set the Chip Select for the second amp <cs_gpio_index>
+	 * in the node.
+	 * This is only supported for systems with 2 amps, since we cannot
+	 * expand the default number of chip selects without using cs-gpios
+	 * The CS GPIO must be set high prior to communicating with the
+	 * first amp (which uses a native chip select), to ensure the second
+	 * amp does not clash with the first.
+	 */
+	if (IS_ENABLED(CONFIG_SPI)) {
+		spi = to_spi_device(p->dev);
+
+		/*
+		 * This is obtained using driver_gpios, since only one GPIO
+		 * for CS exists, this can be obtained using index 0.
+		 */
+		cs_gpiod = devm_gpiod_get_index_optional(physdev, "cs",
+			p->index, GPIOD_OUT_LOW);
+		if (IS_ERR(cs_gpiod)) {
+			dev_err(p->dev,
+				"Unable to get CS GPIO descriptor\n");
+			ret = PTR_ERR(cs_gpiod);
+			goto err;
+		}
+		if (id == 1) {
+			spi_set_csgpiod(spi, 0, cs_gpiod);
+			p->chipselect = cs_gpiod;
+		} else {
+			gpiod_set_value_cansleep(cs_gpiod, true);
+			gpiod_put(cs_gpiod);
+		}
+		spi_setup(spi);
+	}
+	put_device(physdev);
+
+	return 0;
+
+err:
+	dev_err(p->dev, "read acpi error, ret: %d\n", ret);
+	put_device(physdev);
+	acpi_dev_put(adev);
+
+	return ret;
+}
+
+static void tas2781_hda_playback_hook(struct device *dev, int action)
+{
+	struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+	switch (action) {
+	case HDA_GEN_PCM_ACT_OPEN:
+		pm_runtime_get_sync(dev);
+		mutex_lock(&tas_hda->priv->codec_lock);
+		tasdevice_spi_tuning_switch(tas_hda->priv, 0);
+		mutex_unlock(&tas_hda->priv->codec_lock);
+		break;
+	case HDA_GEN_PCM_ACT_CLOSE:
+		mutex_lock(&tas_hda->priv->codec_lock);
+		tasdevice_spi_tuning_switch(tas_hda->priv, 1);
+		mutex_unlock(&tas_hda->priv->codec_lock);
+
+		pm_runtime_mark_last_busy(dev);
+		pm_runtime_put_autosuspend(dev);
+		break;
+	default:
+		dev_dbg(tas_hda->dev, "Playback action not supported: %d\n",
+			action);
+		break;
+	}
+}
+
+static int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1;
+
+	return 0;
+}
+
+static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id;
+
+	return 0;
+}
+
+static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+	int nr_profile = ucontrol->value.integer.value[0];
+	int max = tas_priv->rcabin.ncfgs - 1;
+	int val, ret = 0;
+
+	val = clamp(nr_profile, 0, max);
+
+	if (tas_priv->rcabin.profile_cfg_id != val) {
+		tas_priv->rcabin.profile_cfg_id = val;
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+	struct tasdevice_fw *tas_fw = tas_priv->fmw;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = tas_fw->nr_programs - 1;
+
+	return 0;
+}
+
+static int tasdevice_info_config(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+	struct tasdevice_fw *tas_fw = tas_priv->fmw;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = tas_fw->nr_configurations - 1;
+
+	return 0;
+}
+
+static int tasdevice_program_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = tas_priv->cur_prog;
+
+	return 0;
+}
+
+static int tasdevice_program_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+	struct tasdevice_fw *tas_fw = tas_priv->fmw;
+	int nr_program = ucontrol->value.integer.value[0];
+	int max = tas_fw->nr_programs - 1;
+	int val, ret = 0;
+
+	val = clamp(nr_program, 0, max);
+
+	if (tas_priv->cur_prog != val) {
+		tas_priv->cur_prog = val;
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static int tasdevice_config_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = tas_priv->cur_conf;
+
+	return 0;
+}
+
+static int tasdevice_config_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+	struct tasdevice_fw *tas_fw = tas_priv->fmw;
+	int nr_config = ucontrol->value.integer.value[0];
+	int max = tas_fw->nr_configurations - 1;
+	int val, ret = 0;
+
+	val = clamp(nr_config, 0, max);
+
+	if (tas_priv->cur_conf != val) {
+		tas_priv->cur_conf = val;
+		ret = 1;
+	}
+
+	return ret;
+}
+
+/*
+ * tas2781_digital_getvol - get the volum control
+ * @kcontrol: control pointer
+ * @ucontrol: User data
+ * Customer Kcontrol for tas2781 is primarily for regmap booking, paging
+ * depends on internal regmap mechanism.
+ * tas2781 contains book and page two-level register map, especially
+ * book switching will set the register BXXP00R7F, after switching to the
+ * correct book, then leverage the mechanism for paging to access the
+ * register.
+ */
+static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+
+	return tasdevice_spi_digital_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+
+	return tasdevice_spi_amp_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+
+	/* The check of the given value is in tasdevice_digital_putvol. */
+	return tasdevice_spi_digital_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+
+	/* The check of the given value is in tasdevice_amp_putvol. */
+	return tasdevice_spi_amp_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status;
+	dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+			tas_priv->force_fwload_status ? "ON" : "OFF");
+
+	return 0;
+}
+
+static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+	bool change, val = (bool)ucontrol->value.integer.value[0];
+
+	if (tas_priv->force_fwload_status == val)
+		change = false;
+	else {
+		change = true;
+		tas_priv->force_fwload_status = val;
+	}
+	dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+		tas_priv->force_fwload_status ? "ON" : "OFF");
+
+	return change;
+}
+
+static const struct snd_kcontrol_new tas2781_snd_controls[] = {
+	ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL,
+		1, 0, 20, 0, tas2781_amp_getvol,
+		tas2781_amp_putvol, amp_vol_tlv),
+	ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL,
+		0, 0, 200, 1, tas2781_digital_getvol,
+		tas2781_digital_putvol, dvc_tlv),
+	ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
+		tas2781_force_fwload_get, tas2781_force_fwload_put),
+};
+
+static const struct snd_kcontrol_new tas2781_prof_ctrl = {
+	.name = "Speaker Profile Id",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.info = tasdevice_info_profile,
+	.get = tasdevice_get_profile_id,
+	.put = tasdevice_set_profile_id,
+};
+
+static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl = {
+	.name = "Speaker Program Id",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.info = tasdevice_info_programs,
+	.get = tasdevice_program_get,
+	.put = tasdevice_program_put,
+};
+
+static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl = {
+	.name = "Speaker Config Id",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.info = tasdevice_info_config,
+	.get = tasdevice_config_get,
+	.put = tasdevice_config_put,
+};
+
+static void tas2781_apply_calib(struct tasdevice_priv *tas_priv)
+{
+	static const unsigned char page_array[CALIB_MAX] = {
+		0x17, 0x18, 0x18, 0x13, 0x18
+	};
+	static const unsigned char rgno_array[CALIB_MAX] = {
+		0x74, 0x0c, 0x14, 0x70, 0x7c
+	};
+	unsigned char *data;
+	int j, rc;
+
+	data = tas_priv->cali_data.data +
+		tas_priv->index * TASDEVICE_SPEAKER_CALIBRATION_SIZE;
+	for (j = 0; j < CALIB_MAX; j++) {
+		rc = tasdevice_spi_dev_bulk_write(tas_priv,
+			TASDEVICE_REG(0, page_array[j], rgno_array[j]),
+			&(data[4 * j]), 4);
+		if (rc < 0)
+			dev_err(tas_priv->dev,
+				"chn %d calib %d bulk_wr err = %d\n",
+				tas_priv->index, j, rc);
+	}
+
+}
+
+/*
+ * Update the calibration data, including speaker impedance, f0, etc,
+ * into algo. Calibrate data is done by manufacturer in the factory.
+ * These data are used by Algo for calculating the speaker temperature,
+ * speaker membrane excursion and f0 in real time during playback.
+ * Calibration data format in EFI is V2, since 2024.
+ */
+static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
+{
+	efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d,
+		0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3);
+	static efi_char16_t efi_name[] = L"CALI_DATA";
+	struct tm *tm = &tas_priv->tm;
+	unsigned int attr, crc;
+	unsigned int *tmp_val;
+	efi_status_t status;
+
+	tas_priv->cali_data.total_sz = 0;
+	/* Get real size of UEFI variable */
+	status = efi.get_variable(efi_name, &efi_guid, &attr,
+		&tas_priv->cali_data.total_sz, tas_priv->cali_data.data);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		/* Allocate data buffer of data_size bytes */
+		tas_priv->cali_data.data = devm_kzalloc(tas_priv->dev,
+			tas_priv->cali_data.total_sz, GFP_KERNEL);
+		if (!tas_priv->cali_data.data)
+			return -ENOMEM;
+		/* Get variable contents into buffer */
+		status = efi.get_variable(efi_name, &efi_guid, &attr,
+			&tas_priv->cali_data.total_sz,
+			tas_priv->cali_data.data);
+	}
+	if (status != EFI_SUCCESS)
+		return -EINVAL;
+
+	tmp_val = (unsigned int *)tas_priv->cali_data.data;
+	if (tmp_val[0] == 2781) {
+		/*
+		 * New features were added in calibrated Data V2:
+		 *     1. Added some the fields to store the link_id and
+		 *     uniqie_id for multi-link solutions
+		 *     2. Support flexible number of devices instead of
+		 *     fixed one in V1.
+		 * Layout of calibrated data V2 in UEFI(total 256 bytes):
+		 *     ChipID (2783, 4 bytes)
+		 *     Device-Sum (4 bytes)
+		 *     TimeStamp of Calibration (4 bytes)
+		 *     for (i = 0; i < Device-Sum; i++) {
+		 *             Device #i index_info () {
+		 *                     SDW link id (2bytes)
+		 *                     SDW unique_id (2bytes)
+		 *             }
+		 *             Calibrated Data of Device #i (20 bytes)
+		 *     }
+		 *     CRC (4 bytes)
+		 *     Reserved (the rest)
+		 */
+		crc = crc32(~0, tas_priv->cali_data.data,
+			3 + tmp_val[0] * 6) ^ ~0;
+		dev_dbg(tas_priv->dev, "cali crc 0x%08x PK tmp_val 0x%08x\n",
+			crc, tmp_val[3 + tmp_val[0] * 6]);
+
+		if (crc == tmp_val[3+tmp_val[0]*6]) {
+			time64_to_tm(tmp_val[2], 0, tm);
+			dev_dbg(tas_priv->dev, "%4ld-%2d-%2d, %2d:%2d:%2d\n",
+				tm->tm_year, tm->tm_mon, tm->tm_mday,
+				tm->tm_hour, tm->tm_min, tm->tm_sec);
+			tasdevice_apply_calibration(tas_priv);
+		} else {
+			tas_priv->cali_data.total_sz = 0;
+		}
+	} else {
+		/*
+		 * Calibration data is in V1 format.
+		 * struct cali_data {
+		 *      char cali_data[20];
+		 * }
+		 *
+		 * struct {
+		 *      struct cali_data cali_data[4];
+		 *      int  TimeStamp of Calibration (4 bytes)
+		 *      int CRC (4 bytes)
+		 * } ueft;
+		 */
+		crc = crc32(~0, tas_priv->cali_data.data, 84) ^ ~0;
+		dev_dbg(tas_priv->dev, "cali crc 0x%08x PK tmp_val 0x%08x\n",
+			crc, tmp_val[21]);
+
+		if (crc == tmp_val[21]) {
+			time64_to_tm(tmp_val[20], 0, tm);
+			dev_dbg(tas_priv->dev, "%4ld-%2d-%2d, %2d:%2d:%2d\n",
+				tm->tm_year, tm->tm_mon, tm->tm_mday,
+				tm->tm_hour, tm->tm_min, tm->tm_sec);
+			tasdevice_apply_calibration(tas_priv);
+		} else
+			tas_priv->cali_data.total_sz = 0;
+	}
+	return 0;
+}
+
+static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda)
+{
+	struct hda_codec *codec = tas_hda->priv->codec;
+
+	if (tas_hda->dsp_prog_ctl)
+		snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl);
+
+	if (tas_hda->dsp_conf_ctl)
+		snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl);
+
+	for (int i = ARRAY_SIZE(tas_hda->snd_ctls) - 1; i >= 0; i--)
+		if (tas_hda->snd_ctls[i])
+			snd_ctl_remove(codec->card, tas_hda->snd_ctls[i]);
+
+	if (tas_hda->prof_ctl)
+		snd_ctl_remove(codec->card, tas_hda->prof_ctl);
+}
+
+static void tasdev_fw_ready(const struct firmware *fmw, void *context)
+{
+	struct tasdevice_priv *tas_priv = context;
+	struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
+	struct hda_codec *codec = tas_priv->codec;
+	int i, ret;
+
+	pm_runtime_get_sync(tas_priv->dev);
+	mutex_lock(&tas_priv->codec_lock);
+
+	ret = tasdevice_spi_rca_parser(tas_priv, fmw);
+	if (ret)
+		goto out;
+
+	/* Add control one time only. */
+	if (tas_priv->index == 0) {
+		tas_hda->prof_ctl = snd_ctl_new1(&tas2781_prof_ctrl, tas_priv);
+		ret = snd_ctl_add(codec->card, tas_hda->prof_ctl);
+		if (ret) {
+			dev_err(tas_priv->dev,
+				"Failed to add KControl %s = %d\n",
+				tas2781_prof_ctrl.name, ret);
+			goto out;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(tas2781_snd_controls); i++) {
+			tas_hda->snd_ctls[i] =
+				snd_ctl_new1(&tas2781_snd_controls[i],
+				tas_priv);
+			ret = snd_ctl_add(codec->card, tas_hda->snd_ctls[i]);
+			if (ret) {
+				dev_err(tas_priv->dev,
+					"Failed to add KControl %s = %d\n",
+					tas2781_snd_controls[i].name, ret);
+				goto out;
+			}
+		}
+	}
+
+	tasdevice_spi_dsp_remove(tas_priv);
+
+	tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+	scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%08X-%01d.bin",
+		codec->core.subsystem_id, tas_priv->index);
+	ret = tasdevice_spi_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;
+	}
+
+	/* Add control one time only. */
+	if (tas_priv->index == 0) {
+		tas_hda->dsp_prog_ctl = snd_ctl_new1(&tas2781_dsp_prog_ctrl,
+			tas_priv);
+		ret = snd_ctl_add(codec->card, tas_hda->dsp_prog_ctl);
+		if (ret) {
+			dev_err(tas_priv->dev,
+				"Failed to add KControl %s = %d\n",
+				tas2781_dsp_prog_ctrl.name, ret);
+			goto out;
+		}
+
+		tas_hda->dsp_conf_ctl = snd_ctl_new1(&tas2781_dsp_conf_ctrl,
+			tas_priv);
+		ret = snd_ctl_add(codec->card, tas_hda->dsp_conf_ctl);
+		if (ret) {
+			dev_err(tas_priv->dev,
+				"Failed to add KControl %s = %d\n",
+				tas2781_dsp_conf_ctrl.name, ret);
+			goto out;
+		}
+	}
+
+	tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+	tasdevice_spi_prmg_load(tas_priv, 0);
+	if (tas_priv->fmw->nr_programs > 0)
+		tas_priv->cur_prog = 0;
+	if (tas_priv->fmw->nr_configurations > 0)
+		tas_priv->cur_conf = 0;
+
+	/*
+	 * If calibrated data occurs error, dsp will still works with default
+	 * calibrated data inside algo.
+	 */
+	tasdevice_save_calibration(tas_priv);
+
+	tasdevice_spi_tuning_switch(tas_hda->priv, 0);
+
+out:
+	mutex_unlock(&tas_hda->priv->codec_lock);
+	if (fmw)
+		release_firmware(fmw);
+	pm_runtime_mark_last_busy(tas_hda->dev);
+	pm_runtime_put_autosuspend(tas_hda->dev);
+}
+
+static int tas2781_hda_bind(struct device *dev, struct device *master,
+	void *master_data)
+{
+	struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+	struct hda_component *comps = master_data;
+	struct hda_codec *codec;
+	unsigned int subid;
+	int ret;
+
+	if (!comps || tas_hda->priv->index < 0 ||
+		tas_hda->priv->index >= HDA_MAX_COMPONENTS)
+		return -EINVAL;
+
+	comps = &comps[tas_hda->priv->index];
+	if (comps->dev)
+		return -EBUSY;
+
+	codec = comps->codec;
+	subid = codec->core.subsystem_id >> 16;
+
+	switch (subid) {
+	case 0x103C:
+		tas_hda->priv->catlog_id = HP;
+		break;
+	default:
+		tas_hda->priv->catlog_id = OTHERS;
+		break;
+	}
+
+	pm_runtime_get_sync(dev);
+
+	comps->dev = dev;
+
+	strscpy(comps->name, dev_name(dev), sizeof(comps->name));
+
+	ret = tascodec_spi_init(tas_hda->priv, codec, THIS_MODULE,
+		tasdev_fw_ready);
+	if (!ret)
+		comps->playback_hook = tas2781_hda_playback_hook;
+
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+
+	return ret;
+}
+
+static void tas2781_hda_unbind(struct device *dev,
+	struct device *master, void *master_data)
+{
+	struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+	struct hda_component *comps = master_data;
+
+	comps = &comps[tas_hda->priv->index];
+
+	if (comps->dev == dev) {
+		comps->dev = NULL;
+		memset(comps->name, 0, sizeof(comps->name));
+		comps->playback_hook = NULL;
+	}
+
+	tas2781_hda_remove_controls(tas_hda);
+
+	tasdevice_spi_config_info_remove(tas_hda->priv);
+	tasdevice_spi_dsp_remove(tas_hda->priv);
+
+	tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static const struct component_ops tas2781_hda_comp_ops = {
+	.bind = tas2781_hda_bind,
+	.unbind = tas2781_hda_unbind,
+};
+
+static void tas2781_hda_remove(struct device *dev)
+{
+	struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+	pm_runtime_get_sync(tas_hda->dev);
+	pm_runtime_disable(tas_hda->dev);
+
+	component_del(tas_hda->dev, &tas2781_hda_comp_ops);
+
+	pm_runtime_put_noidle(tas_hda->dev);
+
+	tasdevice_remove(tas_hda->priv);
+}
+
+static int tas2781_hda_spi_probe(struct spi_device *spi)
+{
+	struct tasdevice_priv *tas_priv;
+	struct tas2781_hda *tas_hda;
+	const char *device_name;
+	int ret;
+
+	tas_hda = devm_kzalloc(&spi->dev, sizeof(*tas_hda), GFP_KERNEL);
+	if (!tas_hda)
+		return -ENOMEM;
+
+	tas_hda->dev = &spi->dev;
+
+	tas_priv = devm_kzalloc(&spi->dev, sizeof(*tas_priv), GFP_KERNEL);
+	if (!tas_priv)
+		goto err;
+	tas_priv->dev = &spi->dev;
+	tas_priv->client = (void *)spi;
+	tas_hda->priv = tas_priv;
+
+	if (strstr(dev_name(&spi->dev), "TIAS2781")) {
+		device_name = "TIAS2781";
+		tas_priv->save_calibration = tas2781_save_calibration;
+		tas_priv->apply_calibration = tas2781_apply_calib;
+	} else
+		goto err;
+
+	tas_priv->irq_info.irq = spi->irq;
+	dev_set_drvdata(&spi->dev, tas_hda);
+	ret = tas2781_read_acpi(tas_hda, device_name,
+		spi_get_chipselect(spi, 0));
+	if (ret)
+		return dev_err_probe(tas_hda->dev, ret,
+			"Platform not supported\n");
+
+	tas_priv->regmap = devm_regmap_init_spi(spi, &tasdevice_regmap);
+	if (IS_ERR(tas_priv->regmap)) {
+		ret = PTR_ERR(tas_priv->regmap);
+		dev_err(tas_priv->dev, "Failed to allocate regmap: %d\n",
+			ret);
+		goto err;
+	}
+	ret = tasdevice_spi_init(tas_hda->priv);
+	if (ret)
+		goto err;
+
+	pm_runtime_set_autosuspend_delay(tas_hda->dev, 3000);
+	pm_runtime_use_autosuspend(tas_hda->dev);
+	pm_runtime_mark_last_busy(tas_hda->dev);
+	pm_runtime_set_active(tas_hda->dev);
+	pm_runtime_get_noresume(tas_hda->dev);
+	pm_runtime_enable(tas_hda->dev);
+
+	pm_runtime_put_autosuspend(tas_hda->dev);
+
+	tas2781_spi_reset(tas_priv);
+
+	ret = component_add(tas_hda->dev, &tas2781_hda_comp_ops);
+	if (ret) {
+		dev_err(tas_hda->dev, "Register component failed: %d\n", ret);
+		pm_runtime_disable(tas_hda->dev);
+	}
+
+err:
+	if (ret)
+		tas2781_hda_remove(&spi->dev);
+	return ret;
+}
+
+static void tas2781_hda_spi_remove(struct spi_device *spi)
+{
+	tas2781_hda_remove(&spi->dev);
+}
+
+static int tas2781_runtime_suspend(struct device *dev)
+{
+	struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+	dev_dbg(tas_hda->dev, "Runtime Suspend\n");
+
+	mutex_lock(&tas_hda->priv->codec_lock);
+
+	if (tas_hda->priv->playback_started) {
+		tasdevice_spi_tuning_switch(tas_hda->priv, 1);
+		tas_hda->priv->playback_started = false;
+	}
+
+	tas_hda->priv->tasdevice.cur_book = -1;
+	tas_hda->priv->tasdevice.cur_prog = -1;
+	tas_hda->priv->tasdevice.cur_conf = -1;
+
+	mutex_unlock(&tas_hda->priv->codec_lock);
+
+	return 0;
+}
+
+static int tas2781_runtime_resume(struct device *dev)
+{
+	struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+	dev_dbg(tas_hda->dev, "Runtime Resume\n");
+
+	mutex_lock(&tas_hda->priv->codec_lock);
+
+	tasdevice_spi_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog);
+
+	/*
+	 * If calibrated data occurs error, dsp will still works with default
+	 * calibrated data inside algo.
+	 */
+	tasdevice_apply_calibration(tas_hda->priv);
+
+	mutex_unlock(&tas_hda->priv->codec_lock);
+
+	return 0;
+}
+
+static int tas2781_system_suspend(struct device *dev)
+{
+	struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+	int ret;
+
+	dev_dbg(tas_hda->priv->dev, "System Suspend\n");
+
+	ret = pm_runtime_force_suspend(dev);
+	if (ret)
+		return ret;
+
+	/* Shutdown chip before system suspend */
+	tasdevice_spi_tuning_switch(tas_hda->priv, 1);
+
+	/*
+	 * Reset GPIO may be shared, so cannot reset here.
+	 * However beyond this point, amps may be powered down.
+	 */
+	return 0;
+}
+
+static int tas2781_system_resume(struct device *dev)
+{
+	struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+	int ret;
+
+	ret = pm_runtime_force_resume(dev);
+	if (ret)
+		return ret;
+
+	mutex_lock(&tas_hda->priv->codec_lock);
+
+	tas_hda->priv->tasdevice.cur_book = -1;
+	tas_hda->priv->tasdevice.cur_prog = -1;
+	tas_hda->priv->tasdevice.cur_conf = -1;
+
+	tas2781_spi_reset(tas_hda->priv);
+	tasdevice_spi_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog);
+
+	/*
+	 * If calibrated data occurs error, dsp will still work with default
+	 * calibrated data inside algo.
+	 */
+	tasdevice_apply_calibration(tas_hda->priv);
+	mutex_unlock(&tas_hda->priv->codec_lock);
+
+	return 0;
+}
+
+static const struct dev_pm_ops tas2781_hda_pm_ops = {
+	RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL)
+	SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume)
+};
+
+static const struct spi_device_id tas2781_hda_spi_id[] = {
+	{ "tas2781-hda", 0 },
+	{}
+};
+
+static const struct acpi_device_id tas2781_acpi_hda_match[] = {
+	{"TIAS2781", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match);
+
+static struct spi_driver tas2781_hda_spi_driver = {
+	.driver = {
+		.name		= "tas2781-hda",
+		.acpi_match_table = tas2781_acpi_hda_match,
+		.pm		= &tas2781_hda_pm_ops,
+	},
+	.id_table	= tas2781_hda_spi_id,
+	.probe		= tas2781_hda_spi_probe,
+	.remove		= tas2781_hda_spi_remove,
+};
+module_spi_driver(tas2781_hda_spi_driver);
+
+MODULE_DESCRIPTION("TAS2781 HDA SPI Driver");
+MODULE_AUTHOR("Baojun, Xu, <baojun.xug@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB);
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 3/3] ALSA: hda/tas2781: Firmware load for tas2781 driver for SPI
  2024-04-09  2:48 [PATCH v2 0/3] ALSA: hda/tas2781: Add tas2781 driver for SPI Baojun Xu
  2024-04-09  2:48 ` [PATCH v2 1/3] ALSA: hda/tas2781: Modification for add " Baojun Xu
  2024-04-09  2:48 ` [PATCH v2 2/3] ALSA: hda/tas2781: Main code of " Baojun Xu
@ 2024-04-09  2:48 ` Baojun Xu
  2024-04-09 13:26   ` Andy Shevchenko
  2024-04-09 18:26   ` kernel test robot
  2 siblings, 2 replies; 13+ messages in thread
From: Baojun Xu @ 2024-04-09  2:48 UTC (permalink / raw)
  To: tiwai
  Cc: robh+dt, andriy.shevchenko, lgirdwood, perex,
	pierre-louis.bossart, kevin-lu, shenghao-ding, navada,
	alsa-devel, linux-kernel, liam.r.girdwood, yung-chuan.liao,
	baojun.xu, broonie, soyer

Firmware download and parser lib for tas2781, it work for spi
device with a single firmware binary file.

Signed-off-by: Baojun Xu <baojun.xu@ti.com>

---
v2:
 - Remove file name in tas2781_spi_fwlib.c.
 - Remove #include <linux/i2c.h> as it not needed in SPI mode.
 - Change TAB to speace in micro define in tas2781_spi_fwlib.c.
 - Change to up-case for DSP.
 - Change all of multi-line comments format, empty first line.
 - Change all of sizeof(struct xx) to sizeof(*xx).
 - Remove Explicit casting for variables compare to avoid possible fault.
 - Return directly without goto.
 - Change 1 << xx to BIT(xx).
 - Remove deviceNumber[] as current SPI device will support one device only.
 - Add memory free before return error.
 - Change "buf[offset]; offset += 1" to buf[offset++].
 - Remove some debug information print.
 - Change firmware binary to single device mode.
 - Change all bexx_to_cpup() to get_unaligned_bexx().
 - Remove ndev process as current SPI device support single device only.
 - Remove chn and chnend for single device support only.
 - Change all of registers read/write function, remove parameter chn.
 - Create new function for single write, burst write, delay, field write
   in tasdevice_process_block().
---
 sound/pci/hda/tas2781_spi_fwlib.c | 2289 +++++++++++++++++++++++++++++
 1 file changed, 2289 insertions(+)
 create mode 100644 sound/pci/hda/tas2781_spi_fwlib.c

diff --git a/sound/pci/hda/tas2781_spi_fwlib.c b/sound/pci/hda/tas2781_spi_fwlib.c
new file mode 100644
index 000000000000..4deb89547ef4
--- /dev/null
+++ b/sound/pci/hda/tas2781_spi_fwlib.c
@@ -0,0 +1,2289 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA SPI driver
+//
+// Copyright 2024 Texas Instruments, Inc.
+//
+// Author: Baojun Xu <baojun.xu@ti.com>
+
+#include <asm/unaligned.h>
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "tas2781-spi.h"
+
+#define OFFSET_ERROR_BIT			0x40000000
+
+#define ERROR_PRAM_CRCCHK			0x0000000
+#define ERROR_YRAM_CRCCHK			0x0000001
+#define PPC_DRIVER_CRCCHK			0x00000200
+
+#define TAS2781_SA_COEFF_SWAP_REG		TASDEVICE_REG(0, 0x35, 0x2c)
+#define TAS2781_YRAM_BOOK1			140
+#define TAS2781_YRAM1_PAGE			42
+#define TAS2781_YRAM1_START_REG			88
+
+#define TAS2781_YRAM2_START_PAGE		43
+#define TAS2781_YRAM2_END_PAGE			49
+#define TAS2781_YRAM2_START_REG			8
+#define TAS2781_YRAM2_END_REG			127
+
+#define TAS2781_YRAM3_PAGE			50
+#define TAS2781_YRAM3_START_REG			8
+#define TAS2781_YRAM3_END_REG			27
+
+/*should not include B0_P53_R44-R47 */
+#define TAS2781_YRAM_BOOK2			0
+#define TAS2781_YRAM4_START_PAGE		50
+#define TAS2781_YRAM4_END_PAGE			60
+
+#define TAS2781_YRAM5_PAGE			61
+#define TAS2781_YRAM5_START_REG			TAS2781_YRAM3_START_REG
+#define TAS2781_YRAM5_END_REG			TAS2781_YRAM3_END_REG
+
+#define TASDEVICE_MAXPROGRAM_NUM_KERNEL			5
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS	64
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL			10
+#define MAIN_ALL_DEVICES_1X				0x01
+#define MAIN_DEVICE_A_1X				0x02
+#define MAIN_DEVICE_B_1X				0x03
+#define MAIN_DEVICE_C_1X				0x04
+#define MAIN_DEVICE_D_1X				0x05
+#define COEFF_DEVICE_A_1X				0x12
+#define COEFF_DEVICE_B_1X				0x13
+#define COEFF_DEVICE_C_1X				0x14
+#define COEFF_DEVICE_D_1X				0x15
+#define PRE_DEVICE_A_1X					0x22
+#define PRE_DEVICE_B_1X					0x23
+#define PRE_DEVICE_C_1X					0x24
+#define PRE_DEVICE_D_1X					0x25
+#define PRE_SOFTWARE_RESET_DEVICE_A			0x41
+#define PRE_SOFTWARE_RESET_DEVICE_B			0x42
+#define PRE_SOFTWARE_RESET_DEVICE_C			0x43
+#define PRE_SOFTWARE_RESET_DEVICE_D			0x44
+#define POST_SOFTWARE_RESET_DEVICE_A			0x45
+#define POST_SOFTWARE_RESET_DEVICE_B			0x46
+#define POST_SOFTWARE_RESET_DEVICE_C			0x47
+#define POST_SOFTWARE_RESET_DEVICE_D			0x48
+
+struct tas_crc {
+	unsigned char offset;
+	unsigned char len;
+};
+
+struct blktyp_devidx_map {
+	unsigned char blktyp;
+	unsigned char dev_idx;
+};
+
+/* fixed m68k compiling issue: mapping table can save code field */
+static const struct blktyp_devidx_map ppc3_tas2781_mapping_table[] = {
+	{ MAIN_ALL_DEVICES_1X, 0x80 },
+	{ MAIN_DEVICE_A_1X, 0x81 },
+	{ COEFF_DEVICE_A_1X, 0xC1 },
+	{ PRE_DEVICE_A_1X, 0xC1 },
+	{ PRE_SOFTWARE_RESET_DEVICE_A, 0xC1 },
+	{ POST_SOFTWARE_RESET_DEVICE_A, 0xC1 },
+	{ MAIN_DEVICE_B_1X, 0x82 },
+	{ COEFF_DEVICE_B_1X, 0xC2 },
+	{ PRE_DEVICE_B_1X, 0xC2 },
+	{ PRE_SOFTWARE_RESET_DEVICE_B, 0xC2 },
+	{ POST_SOFTWARE_RESET_DEVICE_B, 0xC2 },
+	{ MAIN_DEVICE_C_1X, 0x83 },
+	{ COEFF_DEVICE_C_1X, 0xC3 },
+	{ PRE_DEVICE_C_1X, 0xC3 },
+	{ PRE_SOFTWARE_RESET_DEVICE_C, 0xC3 },
+	{ POST_SOFTWARE_RESET_DEVICE_C, 0xC3 },
+	{ MAIN_DEVICE_D_1X, 0x84 },
+	{ COEFF_DEVICE_D_1X, 0xC4 },
+	{ PRE_DEVICE_D_1X, 0xC4 },
+	{ PRE_SOFTWARE_RESET_DEVICE_D, 0xC4 },
+	{ POST_SOFTWARE_RESET_DEVICE_D, 0xC4 },
+};
+
+static const struct blktyp_devidx_map ppc3_mapping_table[] = {
+	{ MAIN_ALL_DEVICES_1X, 0x80 },
+	{ MAIN_DEVICE_A_1X, 0x81 },
+	{ COEFF_DEVICE_A_1X, 0xC1 },
+	{ PRE_DEVICE_A_1X, 0xC1 },
+	{ MAIN_DEVICE_B_1X, 0x82 },
+	{ COEFF_DEVICE_B_1X, 0xC2 },
+	{ PRE_DEVICE_B_1X, 0xC2 },
+	{ MAIN_DEVICE_C_1X, 0x83 },
+	{ COEFF_DEVICE_C_1X, 0xC3 },
+	{ PRE_DEVICE_C_1X, 0xC3 },
+	{ MAIN_DEVICE_D_1X, 0x84 },
+	{ COEFF_DEVICE_D_1X, 0xC4 },
+	{ PRE_DEVICE_D_1X, 0xC4 },
+};
+
+static const struct blktyp_devidx_map non_ppc3_mapping_table[] = {
+	{ MAIN_ALL_DEVICES, 0x80 },
+	{ MAIN_DEVICE_A, 0x81 },
+	{ COEFF_DEVICE_A, 0xC1 },
+	{ PRE_DEVICE_A, 0xC1 },
+	{ MAIN_DEVICE_B, 0x82 },
+	{ COEFF_DEVICE_B, 0xC2 },
+	{ PRE_DEVICE_B, 0xC2 },
+	{ MAIN_DEVICE_C, 0x83 },
+	{ COEFF_DEVICE_C, 0xC3 },
+	{ PRE_DEVICE_C, 0xC3 },
+	{ MAIN_DEVICE_D, 0x84 },
+	{ COEFF_DEVICE_D, 0xC4 },
+	{ PRE_DEVICE_D, 0xC4 },
+};
+
+static struct tasdevice_config_info *tasdevice_add_config(
+	struct tasdevice_priv *tas_priv, unsigned char *config_data,
+	unsigned int config_size, int *status)
+{
+	struct tasdevice_config_info *cfg_info;
+	struct tasdev_blk_data **bk_da;
+	unsigned int config_offset = 0;
+	unsigned int i;
+
+	/*
+	 * In most projects are many audio cases, such as music, handfree,
+	 * receiver, games, audio-to-haptics, PMIC record, bypass mode,
+	 * portrait, landscape, etc. Even in multiple audios, one or
+	 * two of the chips will work for the special case, such as
+	 * ultrasonic application. In order to support these variable-numbers
+	 * of audio cases, flexible configs have been introduced in the
+	 * DSP firmware.
+	 */
+	cfg_info = kzalloc(sizeof(*cfg_info), GFP_KERNEL);
+	if (!cfg_info) {
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) {
+		if ((config_offset + 64) > config_size) {
+			*status = -EINVAL;
+			dev_err(tas_priv->dev, "add conf: Out of boundary\n");
+			goto out;
+		}
+		config_offset += 64;
+	}
+
+	if ((config_offset + 4) > config_size) {
+		*status = -EINVAL;
+		dev_err(tas_priv->dev, "add config: Out of boundary\n");
+		goto out;
+	}
+
+	/*
+	 * convert data[offset], data[offset + 1], data[offset + 2] and
+	 * data[offset + 3] into host
+	 */
+	cfg_info->nblocks =
+		get_unaligned_be32(&config_data[config_offset]);
+	config_offset += 4;
+
+	/*
+	 * Several kinds of dsp/algorithm firmwares can run on tas2781,
+	 * the number and size of blk are not fixed and different among
+	 * these firmwares.
+	 */
+	bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks,
+		sizeof(*bk_da), GFP_KERNEL);
+	if (!bk_da) {
+		*status = -ENOMEM;
+		goto out;
+	}
+	cfg_info->real_nblocks = 0;
+	for (i = 0; i < cfg_info->nblocks; i++) {
+		if (config_offset + 12 > config_size) {
+			*status = -EINVAL;
+			dev_err(tas_priv->dev,
+				"%s: Out of boundary: i = %d nblocks = %u!\n",
+				__func__, i, cfg_info->nblocks);
+			goto out1;
+		}
+		bk_da[i] = kzalloc(sizeof(*bk_da[i]), GFP_KERNEL);
+		if (!bk_da[i]) {
+			*status = -ENOMEM;
+			goto out1;
+		}
+
+		bk_da[i]->dev_idx = config_data[config_offset];
+		config_offset++;
+
+		bk_da[i]->block_type = config_data[config_offset];
+		config_offset++;
+
+		if (bk_da[i]->block_type == TASDEVICE_BIN_BLK_PRE_POWER_UP) {
+			if (bk_da[i]->dev_idx == 0)
+				cfg_info->active_dev =
+					BIT(tas_priv->index) - 1;
+			else
+				cfg_info->active_dev |=
+					(BIT(bk_da[i]->dev_idx) - 1);
+
+		}
+		bk_da[i]->yram_checksum =
+			get_unaligned_be16(&config_data[config_offset]);
+		config_offset += 2;
+		bk_da[i]->block_size =
+			get_unaligned_be32(&config_data[config_offset]);
+		config_offset += 4;
+
+		bk_da[i]->n_subblks =
+			get_unaligned_be32(&config_data[config_offset]);
+
+		config_offset += 4;
+
+		if (config_offset + bk_da[i]->block_size > config_size) {
+			*status = -EINVAL;
+			dev_err(tas_priv->dev,
+				"%s: Out of boundary: i = %d blks = %u!\n",
+				__func__, i, cfg_info->nblocks);
+			goto out1;
+		}
+		/* instead of kzalloc+memcpy */
+		bk_da[i]->regdata = kmemdup(&config_data[config_offset],
+			bk_da[i]->block_size, GFP_KERNEL);
+		if (!bk_da[i]->regdata) {
+			*status = -ENOMEM;
+			i++;
+			goto out1;
+		}
+
+		config_offset += bk_da[i]->block_size;
+		cfg_info->real_nblocks += 1;
+	}
+
+	return cfg_info;
+out1:
+	for (int j = 0; j < i; j++)
+		kfree(bk_da[j]);
+	kfree(bk_da);
+out:
+	kfree(cfg_info);
+	return NULL;
+}
+
+int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw)
+{
+	struct tasdevice_priv *tas_priv = context;
+	struct tasdevice_config_info **cfg_info;
+	struct tasdevice_rca_hdr *fw_hdr;
+	struct tasdevice_rca *rca;
+	unsigned int total_config_sz = 0;
+	unsigned char *buf;
+	int offset = 0, ret = 0, i;
+
+	rca = &(tas_priv->rcabin);
+	fw_hdr = &(rca->fw_hdr);
+	if (!fmw || !fmw->data) {
+		dev_err(tas_priv->dev, "Failed to read %s\n",
+			tas_priv->rca_binaryname);
+		tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+		return -EINVAL;
+	}
+	buf = (unsigned char *)fmw->data;
+	fw_hdr->img_sz = get_unaligned_be32(&buf[offset]);
+	offset += 4;
+	if (fw_hdr->img_sz != fmw->size) {
+		dev_err(tas_priv->dev,
+			"File size not match, %d %u", (int)fmw->size,
+			fw_hdr->img_sz);
+		tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+		return -EINVAL;
+	}
+
+	fw_hdr->checksum = get_unaligned_be32(&buf[offset]);
+	offset += 4;
+	fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]);
+	if (fw_hdr->binary_version_num < 0x103) {
+		dev_err(tas_priv->dev, "File version 0x%04x is too low",
+			fw_hdr->binary_version_num);
+		tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+		return -EINVAL;
+	}
+	offset += 4;
+	fw_hdr->drv_fw_version = get_unaligned_be32(&buf[offset]);
+	offset += 8;
+	fw_hdr->plat_type = buf[offset++];
+	fw_hdr->dev_family = buf[offset++];
+	fw_hdr->reserve = buf[offset++];
+	fw_hdr->ndev = buf[offset++];
+	if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) {
+		dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n");
+		tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+		return -EINVAL;
+	}
+
+	for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++)
+		fw_hdr->devs[i] = buf[offset];
+
+	fw_hdr->nconfig = get_unaligned_be32(&buf[offset]);
+	offset += 4;
+
+	for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) {
+		fw_hdr->config_size[i] = get_unaligned_be32(&buf[offset]);
+		offset += 4;
+		total_config_sz += fw_hdr->config_size[i];
+	}
+
+	if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) {
+		dev_err(tas_priv->dev, "Bin file err %d - %d != %d!\n",
+			fw_hdr->img_sz, total_config_sz, (int)offset);
+		tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+		return -EINVAL;
+	}
+
+	cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL);
+	if (!cfg_info) {
+		tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+		return -ENOMEM;
+	}
+	rca->cfg_info = cfg_info;
+	rca->ncfgs = 0;
+	for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+		rca->ncfgs += 1;
+		cfg_info[i] = tasdevice_add_config(tas_priv, &buf[offset],
+			fw_hdr->config_size[i], &ret);
+		if (ret) {
+			tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+			return ret;
+		}
+		offset += (int)fw_hdr->config_size[i];
+	}
+
+	return ret;
+}
+
+/* fixed m68k compiling issue: mapping table can save code field */
+static unsigned char map_dev_idx(struct tasdevice_fw *tas_fmw,
+	struct tasdev_blk *block)
+{
+	struct blktyp_devidx_map *p =
+		(struct blktyp_devidx_map *)non_ppc3_mapping_table;
+	struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+	struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+	int i, n = ARRAY_SIZE(non_ppc3_mapping_table);
+	unsigned char dev_idx = 0;
+
+	if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781) {
+		p = (struct blktyp_devidx_map *)ppc3_tas2781_mapping_table;
+		n = ARRAY_SIZE(ppc3_tas2781_mapping_table);
+	} else if (fw_fixed_hdr->ppcver >= PPC3_VERSION) {
+		p = (struct blktyp_devidx_map *)ppc3_mapping_table;
+		n = ARRAY_SIZE(ppc3_mapping_table);
+	}
+
+	for (i = 0; i < n; i++) {
+		if (block->type == p[i].blktyp) {
+			dev_idx = p[i].dev_idx;
+			break;
+		}
+	}
+
+	return dev_idx;
+}
+
+static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw,
+	struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+	const unsigned char *data = fmw->data;
+
+	if (offset + 16 > fmw->size) {
+		dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * Convert data[offset], data[offset + 1], data[offset + 2] and
+	 * data[offset + 3] into host.
+	 */
+	block->type = get_unaligned_be32(&data[offset]);
+	offset += 4;
+
+	block->is_pchksum_present = data[offset++];
+	block->pchksum = data[offset++];
+	block->is_ychksum_present = data[offset++];
+	block->ychksum = data[offset++];
+	block->blk_size = get_unaligned_be32(&data[offset]);
+	offset += 4;
+	block->nr_subblocks = get_unaligned_be32(&data[offset]);
+	offset += 4;
+
+	/*
+	 * Fixed m68k compiling issue:
+	 * 1. mapping table can save code field.
+	 * 2. storing the dev_idx as a member of block can reduce unnecessary
+	 *    time and system resource comsumption of dev_idx mapping every
+	 *    time the block data writing to the dsp.
+	 */
+	block->dev_idx = map_dev_idx(tas_fmw, block);
+
+	if (offset + block->blk_size > fmw->size) {
+		dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__);
+		return -EINVAL;
+	}
+	/* instead of kzalloc+memcpy */
+	block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL);
+	if (!block->data)
+		return -ENOMEM;
+
+	offset += block->blk_size;
+
+	return offset;
+}
+
+static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw,
+	struct tasdevice_data *img_data, const struct firmware *fmw,
+	int offset)
+{
+	const unsigned char *data = fmw->data;
+	struct tasdev_blk *blk;
+	unsigned int i;
+
+	if (offset + 4 > fmw->size) {
+		dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+		return -EINVAL;
+	}
+	img_data->nr_blk = get_unaligned_be32(&data[offset]);
+	offset += 4;
+
+	img_data->dev_blks = kcalloc(img_data->nr_blk,
+		sizeof(struct tasdev_blk), GFP_KERNEL);
+	if (!img_data->dev_blks)
+		return -ENOMEM;
+
+	for (i = 0; i < img_data->nr_blk; i++) {
+		blk = &(img_data->dev_blks[i]);
+		offset = fw_parse_block_data_kernel(
+			tas_fmw, blk, fmw, offset);
+		if (offset < 0) {
+			kfree(img_data->dev_blks);
+			return -EINVAL;
+		}
+	}
+
+	return offset;
+}
+
+static int fw_parse_program_data_kernel(
+	struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw,
+	const struct firmware *fmw, int offset)
+{
+	struct tasdevice_prog *program;
+	unsigned int i;
+
+	for (i = 0; i < tas_fmw->nr_programs; i++) {
+		program = &(tas_fmw->programs[i]);
+		if (offset + 72 > fmw->size) {
+			dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+			return -EINVAL;
+		}
+		/*skip 72 unused byts*/
+		offset += 72;
+
+		offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data),
+			fmw, offset);
+		if (offset < 0)
+			break;
+	}
+
+	return offset;
+}
+
+static int fw_parse_configuration_data_kernel(
+	struct tasdevice_priv *tas_priv,
+	struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+	const unsigned char *data = fmw->data;
+	struct tasdevice_config *config;
+	unsigned int i;
+
+	for (i = 0; i < tas_fmw->nr_configurations; i++) {
+		config = &(tas_fmw->configs[i]);
+		if (offset + 80 > fmw->size) {
+			dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+			return -EINVAL;
+		}
+		memcpy(config->name, &data[offset], 64);
+		/*skip extra 16 bytes*/
+		offset += 80;
+
+		offset = fw_parse_data_kernel(tas_fmw, &(config->dev_data),
+			fmw, offset);
+		if (offset < 0)
+			break;
+	}
+
+	return offset;
+}
+
+static int fw_parse_variable_header_kernel(
+	struct tasdevice_priv *tas_priv, const struct firmware *fmw,
+	int offset)
+{
+	struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+	struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+	struct tasdevice_prog *program;
+	struct tasdevice_config *config;
+	const unsigned char *buf = fmw->data;
+	unsigned short max_confs;
+	unsigned int i;
+
+	if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) {
+		dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+		return -EINVAL;
+	}
+	fw_hdr->device_family = get_unaligned_be16(&buf[offset]);
+	if (fw_hdr->device_family != 0) {
+		dev_err(tas_priv->dev, "%s:not TAS device\n", __func__);
+		return -EINVAL;
+	}
+	offset += 2;
+	fw_hdr->device = get_unaligned_be16(&buf[offset]);
+	if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+		fw_hdr->device == 6) {
+		dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device);
+		return -EINVAL;
+	}
+	offset += 2;
+
+	tas_fmw->nr_programs = get_unaligned_be32(&buf[offset]);
+	offset += 4;
+
+	if (tas_fmw->nr_programs == 0 || tas_fmw->nr_programs >
+		TASDEVICE_MAXPROGRAM_NUM_KERNEL) {
+		dev_err(tas_priv->dev, "mnPrograms is invalid\n");
+		return -EINVAL;
+	}
+
+	tas_fmw->programs = kcalloc(tas_fmw->nr_programs,
+		sizeof(*tas_fmw->programs), GFP_KERNEL);
+	if (!tas_fmw->programs)
+		return -ENOMEM;
+
+	for (i = 0; i < tas_fmw->nr_programs; i++) {
+		program = &(tas_fmw->programs[i]);
+		program->prog_size = get_unaligned_be32(&buf[offset]);
+		offset += 4;
+	}
+
+	/* Skip the unused prog_size */
+	offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs);
+
+	tas_fmw->nr_configurations = get_unaligned_be32(&buf[offset]);
+	offset += 4;
+
+	/*
+	 * The max number of config in firmware greater than 4 pieces of
+	 * tas2781s is different from the one lower than 4 pieces of
+	 * tas2781s.
+	 */
+	max_confs = TASDEVICE_MAXCONFIG_NUM_KERNEL;
+	if (tas_fmw->nr_configurations == 0 ||
+		tas_fmw->nr_configurations > max_confs) {
+		dev_err(tas_priv->dev, "%s: Conf is invalid\n", __func__);
+		kfree(tas_fmw->programs);
+		return -EINVAL;
+	}
+
+	if (offset + 4 * max_confs > fmw->size) {
+		dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__);
+		kfree(tas_fmw->programs);
+		return -EINVAL;
+	}
+
+	tas_fmw->configs = kcalloc(tas_fmw->nr_configurations,
+		sizeof(*tas_fmw->configs), GFP_KERNEL);
+	if (!tas_fmw->configs) {
+		kfree(tas_fmw->programs);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < tas_fmw->nr_programs; i++) {
+		config = &(tas_fmw->configs[i]);
+		config->cfg_size = get_unaligned_be32(&buf[offset]);
+		offset += 4;
+	}
+
+	/* Skip the unused configs */
+	offset += 4 * (max_confs - tas_fmw->nr_programs);
+
+	return offset;
+}
+
+static int tasdevice_single_byte_wr(void *context, unsigned char *data,
+	int sublocksize)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+	unsigned short len = get_unaligned_be16(&data[2]);
+	int i, subblk_offset, rc;
+
+	subblk_offset = 4;
+	if (subblk_offset + 4 * len > sublocksize) {
+		dev_err(tas_priv->dev, "process_block: Out of boundary\n");
+		return 0;
+	}
+
+	for (i = 0; i < len; i++) {
+		rc = tasdevice_spi_dev_write(tas_priv,
+			TASDEVICE_REG(data[subblk_offset],
+			data[subblk_offset + 1], data[subblk_offset + 2]),
+			data[subblk_offset + 3]);
+		if (rc < 0) {
+			dev_err(tas_priv->dev,
+				"process_block: single write error\n");
+			subblk_offset |= OFFSET_ERROR_BIT;
+		}
+		subblk_offset += 4;
+	}
+
+	return subblk_offset;
+}
+
+static int tasdevice_burst_wr(void *context, unsigned char *data,
+	int sublocksize)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+	unsigned short len = get_unaligned_be16(&data[2]);
+	int subblk_offset, rc;
+
+	subblk_offset = 4;
+	if (subblk_offset + 4 + len > sublocksize) {
+		dev_err(tas_priv->dev, "%s: BST Out of boundary\n", __func__);
+		subblk_offset |= OFFSET_ERROR_BIT;
+	}
+	if (len % 4) {
+		dev_err(tas_priv->dev, "%s:Bst-len(%u)not div by 4\n",
+			__func__, len);
+		subblk_offset |= OFFSET_ERROR_BIT;
+	}
+
+	rc = tasdevice_spi_dev_bulk_write(tas_priv,
+		TASDEVICE_REG(data[subblk_offset], data[subblk_offset + 1],
+		data[subblk_offset + 2]), &(data[subblk_offset + 4]), len);
+	if (rc < 0) {
+		dev_err(tas_priv->dev, "%s: bulk_write error = %d\n",
+			__func__, rc);
+		subblk_offset |= OFFSET_ERROR_BIT;
+	}
+	subblk_offset += (len + 4);
+
+	return subblk_offset;
+}
+
+static int tasdevice_delay(void *context, unsigned char *data,
+	int sublocksize)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+	unsigned int sleep_time, subblk_offset = 2;
+
+	if (subblk_offset + 2 > sublocksize) {
+		dev_err(tas_priv->dev, "%s: delay Out of boundary\n",
+			__func__);
+		subblk_offset |= OFFSET_ERROR_BIT;
+	}
+	sleep_time = get_unaligned_be16(&data[2]) * 1000;
+	usleep_range(sleep_time, sleep_time + 50);
+	subblk_offset += 2;
+
+	return subblk_offset;
+}
+
+static int tasdevice_field_wr(void *context, unsigned char *data,
+	int sublocksize)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+	int rc, subblk_offset = 2;
+
+	if (subblk_offset + 6 > sublocksize) {
+		dev_err(tas_priv->dev, "%s: bit write Out of boundary\n",
+			__func__);
+		subblk_offset |= OFFSET_ERROR_BIT;
+	}
+	rc = tasdevice_spi_dev_update_bits(tas_priv,
+		TASDEVICE_REG(data[subblk_offset + 2],
+		data[subblk_offset + 3],
+		data[subblk_offset + 4]),
+		data[subblk_offset + 1],
+		data[subblk_offset + 5]);
+	if (rc < 0) {
+		dev_err(tas_priv->dev, "%s: update_bits error = %d\n",
+			__func__, rc);
+		subblk_offset |= OFFSET_ERROR_BIT;
+	}
+	subblk_offset += 6;
+
+	return subblk_offset;
+}
+
+static int tasdevice_process_block(void *context, unsigned char *data,
+	unsigned char dev_idx, int sublocksize)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+	int blktyp = dev_idx & 0xC0, subblk_offset;
+	unsigned char subblk_typ = data[1];
+
+	if (tas_priv->tasdevice.is_loading == false)
+		return sublocksize;
+
+	subblk_offset = 2;
+	switch (subblk_typ) {
+	case TASDEVICE_CMD_SING_W:
+		subblk_offset = tasdevice_single_byte_wr(tas_priv, data,
+			sublocksize);
+		break;
+	case TASDEVICE_CMD_BURST:
+		subblk_offset = tasdevice_burst_wr(tas_priv, data,
+			sublocksize);
+		break;
+	case TASDEVICE_CMD_DELAY:
+		subblk_offset = tasdevice_delay(tas_priv, data, sublocksize);
+		break;
+	case TASDEVICE_CMD_FIELD_W:
+		subblk_offset = tasdevice_field_wr(tas_priv, data,
+			sublocksize);
+		break;
+	default:
+		break;
+	}
+	if (((subblk_offset & OFFSET_ERROR_BIT) != 0) && blktyp != 0) {
+		if (blktyp == 0x80) {
+			tas_priv->tasdevice.cur_prog = -1;
+			tas_priv->tasdevice.cur_conf = -1;
+		} else
+			tas_priv->tasdevice.cur_conf = -1;
+	}
+	subblk_offset &= ~OFFSET_ERROR_BIT;
+
+	return subblk_offset;
+}
+
+void tasdevice_spi_select_cfg_blk(void *pContext, int conf_no,
+	unsigned char block_type)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) pContext;
+	struct tasdevice_rca *rca = &(tas_priv->rcabin);
+	struct tasdevice_config_info **cfg_info = rca->cfg_info;
+	struct tasdev_blk_data **blk_data;
+	int j, k;
+
+	if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) {
+		dev_err(tas_priv->dev, "conf_no should be not more than %u\n",
+			rca->ncfgs);
+		return;
+	}
+	blk_data = cfg_info[conf_no]->blk_data;
+
+	for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) {
+		unsigned int length = 0, rc = 0;
+
+		if (block_type > 5 || block_type < 2) {
+			dev_err(tas_priv->dev,
+				"block_type should be in range from 2 to 5\n");
+			break;
+		}
+		if (block_type != blk_data[j]->block_type)
+			continue;
+
+		for (k = 0; k < (int)blk_data[j]->n_subblks; k++) {
+			tas_priv->tasdevice.is_loading = true;
+
+			rc = tasdevice_process_block(tas_priv,
+				blk_data[j]->regdata + length,
+				blk_data[j]->dev_idx,
+				blk_data[j]->block_size - length);
+			length += rc;
+			if (blk_data[j]->block_size < length) {
+				dev_err(tas_priv->dev,
+					"%s: %u %u out of boundary\n",
+					__func__, length,
+					blk_data[j]->block_size);
+				break;
+			}
+		}
+		if (length != blk_data[j]->block_size)
+			dev_err(tas_priv->dev, "%s: %u %u size is not same\n",
+				__func__, length, blk_data[j]->block_size);
+	}
+}
+
+static int tasdevice_load_block_kernel(
+	struct tasdevice_priv *tasdevice, struct tasdev_blk *block)
+{
+	const unsigned int blk_size = block->blk_size;
+	unsigned int i, length;
+	unsigned char *data = block->data;
+
+	for (i = 0, length = 0; i < block->nr_subblocks; i++) {
+		int rc = tasdevice_process_block(tasdevice, data + length,
+			block->dev_idx, blk_size - length);
+		if (rc < 0) {
+			dev_err(tasdevice->dev,
+				"%s: %u %u sublock write error\n",
+				__func__, length, blk_size);
+			break;
+		}
+		length += (unsigned int)rc;
+		if (blk_size < length) {
+			dev_err(tasdevice->dev, "%s: %u %u out of boundary\n",
+				__func__, length, blk_size);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int fw_parse_variable_hdr(struct tasdevice_priv
+	*tas_priv, struct tasdevice_dspfw_hdr *fw_hdr,
+	const struct firmware *fmw, int offset)
+{
+	const unsigned char *buf = fmw->data;
+	int len = strlen((char *)&buf[offset]);
+
+	len++;
+
+	if (offset + len + 8 > fmw->size) {
+		dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+		return -EINVAL;
+	}
+
+	offset += len;
+
+	fw_hdr->device_family = get_unaligned_be32(&buf[offset]);
+	if (fw_hdr->device_family != 0) {
+		dev_err(tas_priv->dev, "%s: not TAS device\n", __func__);
+		return -EINVAL;
+	}
+	offset += 4;
+
+	fw_hdr->device = get_unaligned_be32(&buf[offset]);
+	if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+		fw_hdr->device == 6) {
+		dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device);
+		return -EINVAL;
+	}
+	offset += 4;
+	fw_hdr->ndev = 1;
+
+	return offset;
+}
+
+static int fw_parse_variable_header_git(struct tasdevice_priv
+	*tas_priv, const struct firmware *fmw, int offset)
+{
+	struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+	struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+
+	offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset);
+
+	return offset;
+}
+
+static int fw_parse_block_data(struct tasdevice_fw *tas_fmw,
+	struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+	unsigned char *data = (unsigned char *)fmw->data;
+	int n;
+
+	if (offset + 8 > fmw->size) {
+		dev_err(tas_fmw->dev, "%s: Type error\n", __func__);
+		return -EINVAL;
+	}
+	block->type = get_unaligned_be32(&data[offset]);
+	offset += 4;
+
+	if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) {
+		if (offset + 8 > fmw->size) {
+			dev_err(tas_fmw->dev, "PChkSumPresent error\n");
+			return -EINVAL;
+		}
+		block->is_pchksum_present = data[offset];
+		offset++;
+
+		block->pchksum = data[offset];
+		offset++;
+
+		block->is_ychksum_present = data[offset];
+		offset++;
+
+		block->ychksum = data[offset];
+		offset++;
+	} else {
+		block->is_pchksum_present = 0;
+		block->is_ychksum_present = 0;
+	}
+
+	block->nr_cmds = get_unaligned_be32(&data[offset]);
+	offset += 4;
+
+	n = block->nr_cmds * 4;
+	if (offset + n > fmw->size) {
+		dev_err(tas_fmw->dev,
+			"%s: File Size(%lu) error offset = %d n = %d\n",
+			__func__, (unsigned long)fmw->size, offset, n);
+		return -EINVAL;
+	}
+	/* instead of kzalloc+memcpy */
+	block->data = kmemdup(&data[offset], n, GFP_KERNEL);
+	if (!block->data)
+		return -ENOMEM;
+
+	offset += n;
+
+	return offset;
+}
+
+/*
+ * When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_data(struct tasdevice_fw *tas_fmw,
+	struct tasdevice_data *img_data, const struct firmware *fmw,
+	int offset)
+{
+	const unsigned char *data = (unsigned char *)fmw->data;
+	struct tasdev_blk *blk;
+	unsigned int i;
+	int n;
+
+	if (offset + 64 > fmw->size) {
+		dev_err(tas_fmw->dev, "%s: Name error\n", __func__);
+		return -EINVAL;
+	}
+	memcpy(img_data->name, &data[offset], 64);
+	offset += 64;
+
+	n = strlen((char *)&data[offset]);
+	n++;
+	if (offset + n + 2 > fmw->size) {
+		dev_err(tas_fmw->dev, "%s: Description error\n", __func__);
+		return -EINVAL;
+	}
+	offset += n;
+	img_data->nr_blk = get_unaligned_be16(&data[offset]);
+	offset += 2;
+
+	img_data->dev_blks = kcalloc(img_data->nr_blk,
+		sizeof(*img_data->dev_blks), GFP_KERNEL);
+	if (!img_data->dev_blks)
+		return -ENOMEM;
+
+	for (i = 0; i < img_data->nr_blk; i++) {
+		blk = &(img_data->dev_blks[i]);
+		offset = fw_parse_block_data(tas_fmw, blk, fmw, offset);
+		if (offset < 0)
+			return -EINVAL;
+	}
+
+	return offset;
+}
+
+/*
+ * When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_program_data(struct tasdevice_priv *tas_priv,
+	struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+	unsigned char *buf = (unsigned char *)fmw->data;
+	struct tasdevice_prog *program;
+	int i;
+
+	if (offset + 2 > fmw->size) {
+		dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+		return -EINVAL;
+	}
+	tas_fmw->nr_programs = get_unaligned_be16(&buf[offset]);
+	offset += 2;
+
+	if (tas_fmw->nr_programs == 0) {
+		/*Not error in calibration Data file, return directly*/
+		dev_info(tas_priv->dev, "%s: No Programs data, maybe calbin\n",
+			__func__);
+		return offset;
+	}
+
+	tas_fmw->programs =
+		kcalloc(tas_fmw->nr_programs, sizeof(*tas_fmw->programs),
+			GFP_KERNEL);
+	if (!tas_fmw->programs)
+		return -ENOMEM;
+
+	for (i = 0; i < tas_fmw->nr_programs; i++) {
+		int n = 0;
+
+		program = &(tas_fmw->programs[i]);
+		if (offset + 64 > fmw->size) {
+			dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+			return -EINVAL;
+		}
+		offset += 64;
+
+		n = strlen((char *)&buf[offset]);
+		/* skip '\0' and 5 unused bytes */
+		n += 6;
+		if (offset + n > fmw->size) {
+			dev_err(tas_priv->dev, "Description err\n");
+			return -EINVAL;
+		}
+
+		offset += n;
+
+		offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw,
+			offset);
+		if (offset < 0)
+			return offset;
+	}
+
+	return offset;
+}
+
+/*
+ * When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_configuration_data(
+	struct tasdevice_priv *tas_priv,
+	struct tasdevice_fw *tas_fmw,
+	const struct firmware *fmw, int offset)
+{
+	unsigned char *data = (unsigned char *)fmw->data;
+	struct tasdevice_config *config;
+	unsigned int i;
+	int n;
+
+	if (offset + 2 > fmw->size) {
+		dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+		return -EINVAL;
+	}
+	tas_fmw->nr_configurations = get_unaligned_be16(&data[offset]);
+	offset += 2;
+
+	if (tas_fmw->nr_configurations == 0) {
+		dev_err(tas_priv->dev, "%s: Conf is zero\n", __func__);
+		/*Not error for calibration Data file, return directly*/
+		return offset;
+	}
+	tas_fmw->configs = kcalloc(tas_fmw->nr_configurations,
+			sizeof(*tas_fmw->configs), GFP_KERNEL);
+	if (!tas_fmw->configs)
+		return -ENOMEM;
+	for (i = 0; i < tas_fmw->nr_configurations; i++) {
+		config = &(tas_fmw->configs[i]);
+		if (offset + 64 > fmw->size) {
+			dev_err(tas_priv->dev, "File Size err\n");
+			return -EINVAL;
+		}
+		memcpy(config->name, &data[offset], 64);
+		offset += 64;
+
+		n = strlen((char *)&data[offset]);
+		n += 15;
+		if (offset + n > fmw->size) {
+			dev_err(tas_priv->dev, "Description err\n");
+			return -EINVAL;
+		}
+
+		offset += n;
+
+		offset = fw_parse_data(tas_fmw, &(config->dev_data),
+			fmw, offset);
+		if (offset < 0)
+			break;
+	}
+
+	return offset;
+}
+
+static bool check_inpage_yram_rg(struct tas_crc *cd,
+	unsigned char reg, unsigned char len)
+{
+	bool in = false;
+
+	if (reg <= TAS2781_YRAM5_END_REG &&
+		reg >= TAS2781_YRAM5_START_REG) {
+		if (reg + len > TAS2781_YRAM5_END_REG)
+			cd->len = TAS2781_YRAM5_END_REG - reg + 1;
+		else
+			cd->len = len;
+		cd->offset = reg;
+		in = true;
+	} else if (reg < TAS2781_YRAM5_START_REG) {
+		if (reg + len > TAS2781_YRAM5_START_REG) {
+			cd->offset = TAS2781_YRAM5_START_REG;
+			cd->len = len - TAS2781_YRAM5_START_REG + reg;
+			in = true;
+		}
+	}
+
+	return in;
+}
+
+static bool check_inpage_yram_bk1(struct tas_crc *cd,
+	unsigned char page, unsigned char reg, unsigned char len)
+{
+	bool in = false;
+
+	if (page == TAS2781_YRAM1_PAGE) {
+		if (reg >= TAS2781_YRAM1_START_REG) {
+			cd->offset = reg;
+			cd->len = len;
+			in = true;
+		} else if (reg + len > TAS2781_YRAM1_START_REG) {
+			cd->offset = TAS2781_YRAM1_START_REG;
+			cd->len = len - TAS2781_YRAM1_START_REG + reg;
+			in = true;
+		}
+	} else if (page == TAS2781_YRAM3_PAGE)
+		in = check_inpage_yram_rg(cd, reg, len);
+
+	return in;
+}
+
+/*
+ * Return Code:
+ * true -- the registers are in the inpage yram
+ * false -- the registers are NOT in the inpage yram
+ */
+static bool check_inpage_yram(struct tas_crc *cd, unsigned char book,
+	unsigned char page, unsigned char reg, unsigned char len)
+{
+	bool in = false;
+
+	if (book == TAS2781_YRAM_BOOK1) {
+		in = check_inpage_yram_bk1(cd, page, reg, len);
+		goto end;
+	}
+	if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE)
+		in = check_inpage_yram_rg(cd, reg, len);
+
+end:
+	return in;
+}
+
+static bool check_inblock_yram_bk(struct tas_crc *cd,
+	unsigned char page, unsigned char reg, unsigned char len)
+{
+	bool in = false;
+
+	if ((page >= TAS2781_YRAM4_START_PAGE &&
+		page <= TAS2781_YRAM4_END_PAGE) ||
+		(page >= TAS2781_YRAM2_START_PAGE &&
+		page <= TAS2781_YRAM2_END_PAGE)) {
+		if (reg <= TAS2781_YRAM2_END_REG &&
+			reg >= TAS2781_YRAM2_START_REG) {
+			cd->offset = reg;
+			cd->len = len;
+			in = true;
+		} else if (reg < TAS2781_YRAM2_START_REG) {
+			if (reg + len - 1 >= TAS2781_YRAM2_START_REG) {
+				cd->offset = TAS2781_YRAM2_START_REG;
+				cd->len = reg + len - TAS2781_YRAM2_START_REG;
+				in = true;
+			}
+		}
+	}
+
+	return in;
+}
+
+/*
+ * Return Code:
+ * true -- the registers are in the inblock yram
+ * false -- the registers are NOT in the inblock yram
+ */
+static bool check_inblock_yram(struct tas_crc *cd, unsigned char book,
+	unsigned char page, unsigned char reg, unsigned char len)
+{
+	bool in = false;
+
+	if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2)
+		in = check_inblock_yram_bk(cd, page, reg, len);
+
+	return in;
+}
+
+static bool check_yram(struct tas_crc *cd, unsigned char book,
+	unsigned char page, unsigned char reg, unsigned char len)
+{
+	bool in;
+
+	in = check_inpage_yram(cd, book, page, reg, len);
+	if (in)
+		goto end;
+	in = check_inblock_yram(cd, book, page, reg, len);
+
+end:
+	return in;
+}
+
+static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice,
+	unsigned char book, unsigned char page,
+	unsigned char reg, unsigned int len)
+{
+	struct tas_crc crc_data;
+	unsigned char crc_chksum = 0;
+	unsigned char nBuf1[128];
+	int ret = 0;
+	int i;
+	bool in;
+
+	if ((reg + len - 1) > 127) {
+		ret = -EINVAL;
+		dev_err(tasdevice->dev, "firmware error\n");
+		goto end;
+	}
+
+	if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+		&& (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+		&& (reg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+		&& (len == 4)) {
+		/*DSP swap command, pass */
+		ret = 0;
+		goto end;
+	}
+
+	in = check_yram(&crc_data, book, page, reg, len);
+	if (!in)
+		goto end;
+
+	if (len == 1) {
+		dev_err(tasdevice->dev, "firmware error\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	ret = tasdevice_spi_dev_bulk_read(tasdevice,
+		TASDEVICE_REG(book, page, crc_data.offset),
+		nBuf1, crc_data.len);
+	if (ret < 0)
+		goto end;
+
+	for (i = 0; i < crc_data.len; i++) {
+		if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+			&& (page == TASDEVICE_PAGE_ID(
+			TAS2781_SA_COEFF_SWAP_REG))
+			&& ((i + crc_data.offset)
+			>= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+			&& ((i + crc_data.offset)
+			<= (TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)
+			+ 4)))
+			/*DSP swap command, bypass */
+			continue;
+		else
+			crc_chksum += crc8(tasdevice->crc8_lkp_tbl, &nBuf1[i],
+				1, 0);
+	}
+
+	ret = crc_chksum;
+
+end:
+	return ret;
+}
+
+static int do_singlereg_checksum(struct tasdevice_priv *tasdevice,
+	unsigned char book, unsigned char page,
+	unsigned char reg, unsigned char val)
+{
+	struct tas_crc crc_data;
+	unsigned int nData1;
+	int ret = 0;
+	bool in;
+
+	if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+		&& (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+		&& (reg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+		&& (reg <= (TASDEVICE_PAGE_REG(
+		TAS2781_SA_COEFF_SWAP_REG) + 4))) {
+		/*DSP swap command, pass */
+		return 0;
+	}
+
+	in = check_yram(&crc_data, book, page, reg, 1);
+	if (!in)
+		return 0;
+	ret = tasdevice_spi_dev_read(tasdevice,
+		TASDEVICE_REG(book, page, reg), &nData1);
+	if (ret < 0)
+		return ret;
+
+	if (nData1 != val) {
+		dev_err(tasdevice->dev,
+			"B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+			book, page, reg, val, nData1);
+		tasdevice->tasdevice.err_code |= ERROR_YRAM_CRCCHK;
+		return -EAGAIN;
+	}
+
+	ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0);
+
+	return ret;
+}
+
+static void set_err_prg_cfg(unsigned int type, struct tasdevice *dev)
+{
+	if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A)
+		|| (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C)
+		|| (type == MAIN_DEVICE_D))
+		dev->cur_prog = -1;
+	else
+		dev->cur_conf = -1;
+}
+
+static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv,
+	struct tasdev_blk *block, unsigned char book,
+	unsigned char page, unsigned char reg, unsigned int len,
+	unsigned char val, unsigned char *crc_chksum)
+{
+	int ret;
+
+	if (len > 1)
+		ret = tasdev_multibytes_chksum(tas_priv, book, page, reg,
+			len);
+	else
+		ret = do_singlereg_checksum(tas_priv, book, page, reg, val);
+
+	if (ret > 0) {
+		*crc_chksum += (unsigned char)ret;
+		goto end;
+	}
+
+	if (ret != -EAGAIN)
+		goto end;
+
+	block->nr_retry--;
+	if (block->nr_retry > 0)
+		goto end;
+
+	set_err_prg_cfg(block->type, &tas_priv->tasdevice);
+
+end:
+	return ret;
+}
+
+static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv,
+	struct tasdev_blk *block, unsigned char book,
+	unsigned char page, unsigned char reg, unsigned char *data,
+	unsigned int len, unsigned int *nr_cmds,
+	unsigned char *crc_chksum)
+{
+	int ret;
+
+	if (len > 1) {
+		ret = tasdevice_spi_dev_bulk_write(tas_priv,
+			TASDEVICE_REG(book, page, reg), data + 3, len);
+		if (ret < 0)
+			return ret;
+		if (block->is_ychksum_present)
+			ret = tasdev_bytes_chksum(tas_priv, block,
+				book, page, reg, len, 0, crc_chksum);
+	} else {
+		ret = tasdevice_spi_dev_write(tas_priv,
+			TASDEVICE_REG(book, page, reg), data[3]);
+		if (ret < 0)
+			return ret;
+		if (block->is_ychksum_present)
+			ret = tasdev_bytes_chksum(tas_priv, block, book,
+				page, reg, 1, data[3], crc_chksum);
+	}
+
+	if (!block->is_ychksum_present || ret >= 0) {
+		*nr_cmds += 1;
+		if (len >= 2)
+			*nr_cmds += ((len - 2) / 4) + 1;
+	}
+
+	return ret;
+}
+
+static int tasdev_block_chksum(struct tasdevice_priv *tas_priv,
+	struct tasdev_blk *block)
+{
+	unsigned int nr_value;
+	int ret;
+
+	ret = tasdevice_spi_dev_read(tas_priv, TASDEVICE_Checksum, &nr_value);
+	if (ret < 0) {
+		dev_err(tas_priv->dev, "%s: read error %d.\n", __func__, ret);
+		set_err_prg_cfg(block->type, &tas_priv->tasdevice);
+		return ret;
+	}
+
+	if ((nr_value & 0xff) != block->pchksum) {
+		dev_err(tas_priv->dev, "%s: PChkSum err %d ", __func__, ret);
+		dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n",
+			block->pchksum, (nr_value & 0xff));
+		tas_priv->tasdevice.err_code |= ERROR_PRAM_CRCCHK;
+		ret = -EAGAIN;
+		block->nr_retry--;
+
+		if (block->nr_retry <= 0)
+			set_err_prg_cfg(block->type,
+				&tas_priv->tasdevice);
+	} else {
+		tas_priv->tasdevice.err_code &= ~ERROR_PRAM_CRCCHK;
+	}
+
+	return ret;
+}
+
+static int tasdev_load_blk(struct tasdevice_priv *tas_priv,
+	struct tasdev_blk *block)
+{
+	unsigned int sleep_time, len, nr_cmds;
+	unsigned char *data = block->data;
+	unsigned char crc_chksum = 0;
+	unsigned char offset, book, page, val;
+	int ret = 0;
+
+	while (block->nr_retry > 0) {
+		if (block->is_pchksum_present) {
+			ret = tasdevice_spi_dev_write(tas_priv,
+				TASDEVICE_Checksum, 0);
+			if (ret < 0)
+				break;
+		}
+
+		if (block->is_ychksum_present)
+			crc_chksum = 0;
+
+		nr_cmds = 0;
+
+		while (nr_cmds < block->nr_cmds) {
+			data = block->data + nr_cmds * 4;
+
+			book = data[0];
+			page = data[1];
+			offset = data[2];
+			val = data[3];
+
+			nr_cmds++;
+			/*Single byte write*/
+			if (offset <= 0x7F) {
+				ret = tasdevice_spi_dev_write(tas_priv,
+					TASDEVICE_REG(book, page, offset),
+					val);
+				if (ret < 0)
+					break;
+				if (block->is_ychksum_present) {
+					ret = tasdev_bytes_chksum(tas_priv,
+						block, book, page, offset,
+						1, val, &crc_chksum);
+					if (ret < 0)
+						break;
+				}
+				continue;
+			}
+			/*sleep command*/
+			if (offset == 0x81) {
+				/*book -- data[0] page -- data[1]*/
+				sleep_time = ((book << 8) + page)*1000;
+				usleep_range(sleep_time, sleep_time + 50);
+				continue;
+			}
+			/*Multiple bytes write*/
+			if (offset == 0x85) {
+				data += 4;
+				len = (book << 8) + page;
+				book = data[0];
+				page = data[1];
+				offset = data[2];
+				ret = tasdev_multibytes_wr(tas_priv,
+					block, book, page, offset, data,
+					len, &nr_cmds, &crc_chksum);
+				if (ret < 0)
+					break;
+			}
+		}
+		if (ret == -EAGAIN) {
+			if (block->nr_retry > 0)
+				continue;
+		} else if (ret < 0) /*err in current device, skip it*/
+			break;
+
+		if (block->is_pchksum_present) {
+			ret = tasdev_block_chksum(tas_priv, block);
+			if (ret == -EAGAIN) {
+				if (block->nr_retry > 0)
+					continue;
+			} else if (ret < 0) /*err in current device, skip it*/
+				break;
+		}
+
+		if (block->is_ychksum_present) {
+			/* TBD, open it when FW ready */
+			dev_err(tas_priv->dev,
+				"Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n",
+				block->ychksum, crc_chksum);
+
+			tas_priv->tasdevice.err_code &=
+				~ERROR_YRAM_CRCCHK;
+			ret = 0;
+		}
+		/*skip current blk*/
+		break;
+	}
+
+	return ret;
+}
+
+static int tasdevice_load_block(struct tasdevice_priv *tas_priv,
+	struct tasdev_blk *block)
+{
+	int ret = 0;
+
+	block->nr_retry = 6;
+	if (tas_priv->tasdevice.is_loading == false)
+		return 0;
+	ret = tasdev_load_blk(tas_priv, block);
+	if (ret < 0)
+		dev_err(tas_priv->dev, "Blk (%d) load error\n", block->type);
+
+	return ret;
+}
+
+static int dspfw_default_callback(struct tasdevice_priv *tas_priv,
+	unsigned int drv_ver, unsigned int ppcver)
+{
+	int rc = 0;
+
+	if (drv_ver == 0x100) {
+		if (ppcver >= PPC3_VERSION) {
+			tas_priv->fw_parse_variable_header =
+				fw_parse_variable_header_kernel;
+			tas_priv->fw_parse_program_data =
+				fw_parse_program_data_kernel;
+			tas_priv->fw_parse_configuration_data =
+				fw_parse_configuration_data_kernel;
+			tas_priv->tasdevice_load_block =
+				tasdevice_load_block_kernel;
+		} else {
+			switch (ppcver) {
+			case 0x00:
+				tas_priv->fw_parse_variable_header =
+					fw_parse_variable_header_git;
+				tas_priv->fw_parse_program_data =
+					fw_parse_program_data;
+				tas_priv->fw_parse_configuration_data =
+					fw_parse_configuration_data;
+				tas_priv->tasdevice_load_block =
+					tasdevice_load_block;
+				break;
+			default:
+				dev_err(tas_priv->dev,
+					"%s: PPCVer must be 0x0 or 0x%02x",
+					__func__, PPC3_VERSION);
+				dev_err(tas_priv->dev, " Current:0x%02x\n",
+					ppcver);
+				rc = -EINVAL;
+				break;
+			}
+		}
+	} else {
+		dev_err(tas_priv->dev,
+			"DrvVer must be 0x0, 0x230 or above 0x230 ");
+		dev_err(tas_priv->dev, "current is 0x%02x\n", drv_ver);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static int load_calib_data(struct tasdevice_priv *tas_priv,
+	struct tasdevice_data *dev_data)
+{
+	struct tasdev_blk *block;
+	unsigned int i;
+	int ret = 0;
+
+	for (i = 0; i < dev_data->nr_blk; i++) {
+		block = &(dev_data->dev_blks[i]);
+		ret = tasdevice_load_block(tas_priv, block);
+		if (ret < 0)
+			break;
+	}
+
+	return ret;
+}
+
+static int fw_parse_header(struct tasdevice_priv *tas_priv,
+	struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+	struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+	struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+	static const unsigned char magic_number[] = {0x35, 0x35, 0x35, 0x32};
+	const unsigned char *buf = (unsigned char *)fmw->data;
+
+	if (offset + 92 > fmw->size) {
+		dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+		offset = -EINVAL;
+		goto out;
+	}
+	if (memcmp(&buf[offset], magic_number, 4)) {
+		dev_err(tas_priv->dev, "%s: Magic num NOT match\n", __func__);
+		offset = -EINVAL;
+		goto out;
+	}
+	offset += 4;
+
+	/*
+	 * Convert data[offset], data[offset + 1], data[offset + 2] and
+	 * data[offset + 3] into host
+	 */
+	fw_fixed_hdr->fwsize = get_unaligned_be32(&buf[offset]);
+	offset += 4;
+	if (fw_fixed_hdr->fwsize != fmw->size) {
+		dev_err(tas_priv->dev, "File size not match, %lu %u",
+			(unsigned long)fmw->size, fw_fixed_hdr->fwsize);
+		offset = -EINVAL;
+		goto out;
+	}
+	offset += 4;
+	fw_fixed_hdr->ppcver = get_unaligned_be32(&buf[offset]);
+	offset += 8;
+	fw_fixed_hdr->drv_ver = get_unaligned_be32(&buf[offset]);
+	offset += 72;
+
+out:
+	return offset;
+}
+
+static int fw_parse_variable_hdr_cal(struct tasdevice_priv *tas_priv,
+	struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+	struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+
+	offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset);
+	if (offset < 0)
+		return offset;
+	if (fw_hdr->ndev != 1) {
+		dev_err(tas_priv->dev,
+			"%s: calbin must be 1, but currently ndev(%u)\n",
+			__func__, fw_hdr->ndev);
+		offset = -EINVAL;
+	}
+
+	return offset;
+}
+
+/*
+ * When calibrated data parsing error occurs, DSP can still work with default
+ * calibrated data, memory resource related to calibrated data will be
+ * released in the tasdevice_codec_remove.
+ */
+static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv,
+	struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+	struct tasdevice_calibration *calibration;
+	unsigned char *data = (unsigned char *)fmw->data;
+	unsigned int i, n;
+
+	if (offset + 2 > fmw->size) {
+		dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__);
+		return -EINVAL;
+	}
+	tas_fmw->nr_calibrations = get_unaligned_be16(&data[offset]);
+	offset += 2;
+
+	if (tas_fmw->nr_calibrations != 1) {
+		dev_err(tas_priv->dev,
+			"%s: only supports one calibration (%d)!\n",
+			__func__, tas_fmw->nr_calibrations);
+		return offset;
+	}
+
+	tas_fmw->calibrations = kcalloc(tas_fmw->nr_calibrations,
+		sizeof(*tas_fmw->calibrations), GFP_KERNEL);
+	if (!tas_fmw->calibrations)
+		return -ENOMEM;
+	for (i = 0; i < tas_fmw->nr_calibrations; i++) {
+		if (offset + 64 > fmw->size) {
+			dev_err(tas_priv->dev, "Calibrations error\n");
+			return -EINVAL;
+		}
+		calibration = &(tas_fmw->calibrations[i]);
+		offset += 64;
+
+		n = strlen((char *)&data[offset]);
+		/* skip '\0' and 2 unused bytes */
+		n += 3;
+		if (offset + n > fmw->size) {
+			dev_err(tas_priv->dev, "Description err\n");
+			return -EINVAL;
+		}
+		offset += n;
+
+		offset = fw_parse_data(tas_fmw, &(calibration->dev_data), fmw,
+			offset);
+		if (offset < 0)
+			return offset;
+	}
+
+	return offset;
+}
+
+int tas2781_spi_load_calibration(void *context, char *file_name,
+	unsigned short i)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+	struct tasdevice *tasdev = &(tas_priv->tasdevice);
+	const struct firmware *fw_entry;
+	struct tasdevice_fw *tas_fmw;
+	struct firmware fmw;
+	int offset = 0;
+	int ret;
+
+	ret = request_firmware(&fw_entry, file_name, tas_priv->dev);
+	if (ret) {
+		dev_err(tas_priv->dev, "%s: Request firmware %s failed\n",
+			__func__, file_name);
+		goto out;
+	}
+
+	if (!fw_entry->size) {
+		dev_err(tas_priv->dev, "%s: file read error: size = %lu\n",
+			__func__, (unsigned long)fw_entry->size);
+		ret = -EINVAL;
+		goto out;
+	}
+	fmw.size = fw_entry->size;
+	fmw.data = fw_entry->data;
+
+	tas_fmw = tasdev->cali_data_fmw = kzalloc(sizeof(*tas_fmw),
+		GFP_KERNEL);
+	if (!tasdev->cali_data_fmw) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	tas_fmw->dev = tas_priv->dev;
+	offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset);
+	if (offset == -EINVAL) {
+		dev_err(tas_priv->dev, "fw_parse_header EXIT!\n");
+		ret = offset;
+		goto out;
+	}
+	offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset);
+	if (offset == -EINVAL) {
+		dev_err(tas_priv->dev,
+			"%s: fw_parse_variable_header_cal EXIT!\n", __func__);
+		ret = offset;
+		goto out;
+	}
+	offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset);
+	if (offset < 0) {
+		dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n");
+		ret = offset;
+		goto out;
+	}
+	offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset);
+	if (offset < 0) {
+		dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n");
+		ret = offset;
+		goto out;
+	}
+	offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset);
+	if (offset < 0) {
+		dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n");
+		ret = offset;
+		goto out;
+	}
+
+out:
+	if (fw_entry)
+		release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int tasdevice_dspfw_ready(const struct firmware *fmw, void *context)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+	struct tasdevice_fw_fixed_hdr *fw_fixed_hdr;
+	struct tasdevice_fw *tas_fmw;
+	int offset = 0, ret = 0;
+
+	if (!fmw || !fmw->data) {
+		dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n",
+			__func__, tas_priv->coef_binaryname);
+		return -EINVAL;
+	}
+
+	tas_priv->fmw = kzalloc(sizeof(*tas_priv->fmw), GFP_KERNEL);
+	if (!tas_priv->fmw)
+		return -ENOMEM;
+	tas_fmw = tas_priv->fmw;
+	tas_fmw->dev = tas_priv->dev;
+	offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset);
+
+	if (offset == -EINVAL)
+		return -EINVAL;
+
+	fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr);
+	/* Support different versions of firmware */
+	switch (fw_fixed_hdr->drv_ver) {
+	case 0x301:
+	case 0x302:
+	case 0x502:
+	case 0x503:
+		tas_priv->fw_parse_variable_header =
+			fw_parse_variable_header_kernel;
+		tas_priv->fw_parse_program_data =
+			fw_parse_program_data_kernel;
+		tas_priv->fw_parse_configuration_data =
+			fw_parse_configuration_data_kernel;
+		tas_priv->tasdevice_load_block =
+			tasdevice_load_block_kernel;
+		break;
+	case 0x202:
+	case 0x400:
+		tas_priv->fw_parse_variable_header =
+			fw_parse_variable_header_git;
+		tas_priv->fw_parse_program_data =
+			fw_parse_program_data;
+		tas_priv->fw_parse_configuration_data =
+			fw_parse_configuration_data;
+		tas_priv->tasdevice_load_block =
+			tasdevice_load_block;
+		break;
+	default:
+		ret = dspfw_default_callback(tas_priv,
+			fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver);
+		if (ret)
+			return ret;
+		break;
+	}
+
+	offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset);
+	if (offset < 0)
+		return offset;
+
+	offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw,
+		offset);
+	if (offset < 0)
+		return offset;
+
+	offset = tas_priv->fw_parse_configuration_data(tas_priv,
+		tas_fmw, fmw, offset);
+	if (offset < 0)
+		ret = offset;
+
+	return ret;
+}
+
+int tasdevice_spi_dsp_parser(void *context)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context;
+	const struct firmware *fw_entry;
+	int ret;
+
+	ret = request_firmware(&fw_entry, tas_priv->coef_binaryname,
+		tas_priv->dev);
+	if (ret) {
+		dev_err(tas_priv->dev, "%s: load %s error\n", __func__,
+			tas_priv->coef_binaryname);
+		return ret;
+	}
+
+	ret = tasdevice_dspfw_ready(fw_entry, tas_priv);
+	release_firmware(fw_entry);
+	fw_entry = NULL;
+
+	return ret;
+}
+
+static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog)
+{
+	struct tasdevice_data *tas_dt;
+	struct tasdev_blk *blk;
+	unsigned int i;
+
+	if (!prog)
+		return;
+
+	tas_dt = &(prog->dev_data);
+
+	if (!tas_dt->dev_blks)
+		return;
+
+	for (i = 0; i < tas_dt->nr_blk; i++) {
+		blk = &(tas_dt->dev_blks[i]);
+		kfree(blk->data);
+	}
+	kfree(tas_dt->dev_blks);
+}
+
+static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog,
+	unsigned short nr)
+{
+	int i;
+
+	for (i = 0; i < nr; i++)
+		tasdev_dsp_prog_blk_remove(&prog[i]);
+	kfree(prog);
+}
+
+static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg)
+{
+	struct tasdevice_data *tas_dt;
+	struct tasdev_blk *blk;
+	unsigned int i;
+
+	if (cfg) {
+		tas_dt = &(cfg->dev_data);
+
+		if (!tas_dt->dev_blks)
+			return;
+
+		for (i = 0; i < tas_dt->nr_blk; i++) {
+			blk = &(tas_dt->dev_blks[i]);
+			kfree(blk->data);
+		}
+		kfree(tas_dt->dev_blks);
+	}
+}
+
+static void tasdev_dsp_cfg_remove(struct tasdevice_config *config,
+	unsigned short nr)
+{
+	int i;
+
+	for (i = 0; i < nr; i++)
+		tasdev_dsp_cfg_blk_remove(&config[i]);
+	kfree(config);
+}
+
+void tasdevice_spi_dsp_remove(void *context)
+{
+	struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+	struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+
+	if (!tas_dev->fmw)
+		return;
+
+	if (tas_fmw->programs)
+		tasdev_dsp_prog_remove(tas_fmw->programs,
+			tas_fmw->nr_programs);
+	if (tas_fmw->configs)
+		tasdev_dsp_cfg_remove(tas_fmw->configs,
+			tas_fmw->nr_configurations);
+	kfree(tas_fmw);
+	tas_dev->fmw = NULL;
+}
+
+static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw)
+{
+	struct tasdevice_calibration *calibration;
+	struct tasdevice_data *im;
+	struct tasdev_blk *block;
+	unsigned int blks;
+	int i;
+
+	if (!tas_fmw->calibrations)
+		goto out;
+
+	for (i = 0; i < tas_fmw->nr_calibrations; i++) {
+		calibration = &(tas_fmw->calibrations[i]);
+		if (!calibration)
+			continue;
+
+		im = &(calibration->dev_data);
+
+		if (!im->dev_blks)
+			continue;
+
+		for (blks = 0; blks < im->nr_blk; blks++) {
+			block = &(im->dev_blks[blks]);
+			if (!block)
+				continue;
+			kfree(block->data);
+		}
+		kfree(im->dev_blks);
+	}
+	kfree(tas_fmw->calibrations);
+out:
+	kfree(tas_fmw);
+}
+
+void tasdevice_spi_calbin_remove(void *context)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+	struct tasdevice *tasdev;
+
+	if (!tas_priv)
+		return;
+
+	tasdev = &(tas_priv->tasdevice);
+	if (tasdev->cali_data_fmw) {
+		tas2781_clear_calfirmware(tasdev->cali_data_fmw);
+		tasdev->cali_data_fmw = NULL;
+	}
+}
+
+void tasdevice_spi_config_info_remove(void *context)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+	struct tasdevice_rca *rca = &(tas_priv->rcabin);
+	struct tasdevice_config_info **ci = rca->cfg_info;
+	int i, j;
+
+	if (!ci)
+		return;
+	for (i = 0; i < rca->ncfgs; i++) {
+		if (!ci[i])
+			continue;
+		if (ci[i]->blk_data) {
+			for (j = 0; j < (int)ci[i]->real_nblocks; j++) {
+				if (!ci[i]->blk_data[j])
+					continue;
+				kfree(ci[i]->blk_data[j]->regdata);
+				kfree(ci[i]->blk_data[j]);
+			}
+			kfree(ci[i]->blk_data);
+		}
+		kfree(ci[i]);
+	}
+	kfree(ci);
+}
+
+static int tasdevice_load_data(struct tasdevice_priv *tas_priv,
+	struct tasdevice_data *dev_data)
+{
+	struct tasdev_blk *block;
+	unsigned int i;
+	int ret = 0;
+
+	for (i = 0; i < dev_data->nr_blk; i++) {
+		block = &(dev_data->dev_blks[i]);
+		ret = tas_priv->tasdevice_load_block(tas_priv, block);
+		if (ret < 0)
+			break;
+	}
+
+	return ret;
+}
+
+int tasdevice_spi_select_tuningprm_cfg(void *context, int prm_no,
+	int cfg_no, int rca_conf_no)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+	struct tasdevice_rca *rca = &(tas_priv->rcabin);
+	struct tasdevice_config_info **cfg_info = rca->cfg_info;
+	struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+	struct tasdevice_prog *program;
+	struct tasdevice_config *conf;
+	int prog_status = 0;
+	int status;
+
+	if (!tas_fmw) {
+		dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+		return 0;
+	}
+
+	if (cfg_no >= tas_fmw->nr_configurations) {
+		dev_err(tas_priv->dev,
+			"%s: cfg(%d) is not in range of conf %u\n",
+			__func__, cfg_no, tas_fmw->nr_configurations);
+		return 0;
+	}
+
+	if (prm_no >= tas_fmw->nr_programs) {
+		dev_err(tas_priv->dev,
+			"%s: prm(%d) is not in range of Programs %u\n",
+			__func__, prm_no, tas_fmw->nr_programs);
+		return 0;
+	}
+
+	if (rca_conf_no >= rca->ncfgs || rca_conf_no < 0 ||
+		!cfg_info) {
+		dev_err(tas_priv->dev,
+			"conf_no:%d should be in range from 0 to %u\n",
+			rca_conf_no, rca->ncfgs-1);
+		return 0;
+	}
+
+	if (cfg_info[rca_conf_no]->active_dev & 1) {
+		if (prm_no >= 0	&& (tas_priv->tasdevice.cur_prog != prm_no
+			|| tas_priv->force_fwload_status)) {
+			tas_priv->tasdevice.cur_conf = -1;
+			tas_priv->tasdevice.is_loading = true;
+			prog_status++;
+		}
+	} else {
+		tas_priv->tasdevice.is_loading = false;
+	}
+
+	if (prog_status) {
+		program = &(tas_fmw->programs[prm_no]);
+		tasdevice_load_data(tas_priv, &(program->dev_data));
+		if (tas_priv->tasdevice.is_loaderr == false
+			&& tas_priv->tasdevice.is_loading == true) {
+			struct tasdevice_fw *cal_fmw =
+				tas_priv->tasdevice.cali_data_fmw;
+
+			if (cal_fmw) {
+				struct tasdevice_calibration
+					*cal = cal_fmw->calibrations;
+
+				if (cal)
+					load_calib_data(tas_priv,
+						&(cal->dev_data));
+			}
+			tas_priv->tasdevice.cur_prog = prm_no;
+		}
+
+	}
+
+	if (cfg_no >= 0
+		&& (tas_priv->tasdevice.cur_conf != cfg_no)
+		&& (cfg_info[rca_conf_no]->active_dev & 1)
+		&& (tas_priv->tasdevice.is_loaderr == false)) {
+		status++;
+		tas_priv->tasdevice.is_loading = true;
+	} else {
+		tas_priv->tasdevice.is_loading = false;
+	}
+
+	if (status) {
+		conf = &(tas_fmw->configs[cfg_no]);
+		status = 0;
+		tasdevice_load_data(tas_priv, &(conf->dev_data));
+		if (tas_priv->tasdevice.is_loaderr == true) {
+			status |= 1 << 4;
+		} else if (tas_priv->tasdevice.is_loaderr == false
+			&& tas_priv->tasdevice.is_loading == true) {
+			tas_priv->tasdevice.cur_conf = cfg_no;
+		}
+	} else
+		dev_dbg(tas_priv->dev, "%s: Unneeded loading dsp conf %d\n",
+			__func__, cfg_no);
+
+	status |= cfg_info[rca_conf_no]->active_dev;
+
+	return prog_status;
+}
+
+int tasdevice_spi_prmg_load(void *context, int prm_no)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+	struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+	struct tasdevice_prog *program;
+	int prog_status = 0;
+
+	if (!tas_fmw) {
+		dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+		return 0;
+	}
+
+	if (prm_no >= tas_fmw->nr_programs) {
+		dev_err(tas_priv->dev,
+			"%s: prm(%d) is not in range of Programs %u\n",
+			__func__, prm_no, tas_fmw->nr_programs);
+		return 0;
+	}
+
+	if (prm_no >= 0 && tas_priv->tasdevice.cur_prog != prm_no) {
+		tas_priv->tasdevice.cur_conf = -1;
+		tas_priv->tasdevice.is_loading = true;
+		prog_status++;
+	}
+
+	if (prog_status) {
+		program = &(tas_fmw->programs[prm_no]);
+		tasdevice_load_data(tas_priv, &(program->dev_data));
+		if ((tas_priv->tasdevice.is_loaderr == false)
+			&& (tas_priv->tasdevice.is_loading == true))
+			tas_priv->tasdevice.cur_prog = prm_no;
+	}
+
+	return prog_status;
+}
+
+int tasdevice_spi_prmg_calibdata_load(void *context, int prm_no)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+	struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+	struct tasdevice_prog *program;
+	int prog_status = 0;
+
+	if (!tas_fmw) {
+		dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+		return 0;
+	}
+
+	if (prm_no >= tas_fmw->nr_programs) {
+		dev_err(tas_priv->dev,
+			"%s: prm(%d) is not in range of Programs %u\n",
+			__func__, prm_no, tas_fmw->nr_programs);
+		return 0;
+	}
+
+	if (prm_no >= 0 && tas_priv->tasdevice.cur_prog != prm_no) {
+		tas_priv->tasdevice.cur_conf = -1;
+		tas_priv->tasdevice.is_loading = true;
+		prog_status++;
+	}
+	tas_priv->tasdevice.is_loaderr = false;
+
+	if (prog_status) {
+		program = &(tas_fmw->programs[prm_no]);
+		tasdevice_load_data(tas_priv, &(program->dev_data));
+		if (tas_priv->tasdevice.is_loaderr == false
+			&& tas_priv->tasdevice.is_loading == true) {
+			struct tasdevice_fw *cal_fmw =
+				tas_priv->tasdevice.cali_data_fmw;
+
+			if (cal_fmw) {
+				struct tasdevice_calibration *cal =
+					cal_fmw->calibrations;
+
+				if (cal)
+					load_calib_data(tas_priv,
+						&(cal->dev_data));
+			}
+			tas_priv->tasdevice.cur_prog = prm_no;
+		}
+
+	}
+
+	return prog_status;
+}
+
+void tasdevice_spi_tuning_switch(void *context, int state)
+{
+	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+	struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+	int profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
+
+	if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) {
+		dev_err(tas_priv->dev, "DSP bin file not loaded\n");
+		return;
+	}
+
+	if (state == 0) {
+		if (tas_priv->cur_prog < tas_fmw->nr_programs) {
+			/*dsp mode or tuning mode*/
+			profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
+			tasdevice_spi_select_tuningprm_cfg(tas_priv,
+				tas_priv->cur_prog, tas_priv->cur_conf,
+				profile_cfg_id);
+		}
+
+		tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id,
+			TASDEVICE_BIN_BLK_PRE_POWER_UP);
+	} else
+		tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id,
+			TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+}
+
+MODULE_DESCRIPTION("Texas Firmware Support");
+MODULE_AUTHOR("Baojun Xu, TI, <baojun.xu@ti.com>");
+MODULE_LICENSE("GPL");
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 1/3] ALSA: hda/tas2781: Modification for add tas2781 driver for SPI
  2024-04-09  2:48 ` [PATCH v2 1/3] ALSA: hda/tas2781: Modification for add " Baojun Xu
@ 2024-04-09 13:02   ` Andy Shevchenko
  2024-04-16  7:45     ` [EXTERNAL] " Xu, Baojun
  0 siblings, 1 reply; 13+ messages in thread
From: Andy Shevchenko @ 2024-04-09 13:02 UTC (permalink / raw)
  To: Baojun Xu
  Cc: tiwai, robh+dt, lgirdwood, perex, pierre-louis.bossart, kevin-lu,
	shenghao-ding, navada, alsa-devel, linux-kernel, liam.r.girdwood,
	yung-chuan.liao, broonie, soyer

On Tue, Apr 09, 2024 at 10:48:13AM +0800, Baojun Xu wrote:
> Integrate tas2781 configs for HP Laptops. Every tas2781 in the laptop
> will work as a single speaker on SPI bus. The code support realtek as

Realtek

> the primary codec.

...

>  		{"INT33FE", },
>  		{"INT3515", },
>  		/* Non-conforming _HID for Cirrus Logic already released */
> +		{"TXNW2781", },

This seems wrong. The ID here is correct from ACPI specification perspective.
Can you elaborate why you think it's "non-conforming _HID"?

>  		{"CLSA0100", },
>  		{"CLSA0101", },

...

>  	/* Non-conforming _HID for Cirrus Logic already released */
>  	{ "CLSA0100", (unsigned long)&cs35l41_hda },
>  	{ "CLSA0101", (unsigned long)&cs35l41_hda },
> +	{ "TXNW2781", (unsigned long)&tas2781_hda },

Ditto.

>  	{ }

...

> @@ -39,6 +39,7 @@ snd-hda-scodec-cs35l56-spi-objs :=	cs35l56_hda_spi.o
>  snd-hda-cs-dsp-ctls-objs :=		hda_cs_dsp_ctl.o
>  snd-hda-scodec-component-objs :=	hda_component.o
>  snd-hda-scodec-tas2781-i2c-objs :=	tas2781_hda_i2c.o
> +snd-hda-scodec-tas2781-spi-objs :=	tas2781_hda_spi.o tas2781_spi_fwlib.o

Actually these 'objs' has to be 'y', can you fix it in the prerequisite patch?

Also wondering why fwlib is only a requirement for SPI. How does I²C work?

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/3] ALSA: hda/tas2781: Main code of tas2781 driver for SPI
  2024-04-09  2:48 ` [PATCH v2 2/3] ALSA: hda/tas2781: Main code of " Baojun Xu
@ 2024-04-09 13:21   ` Andy Shevchenko
  2024-04-18  5:12     ` [EXTERNAL] " Xu, Baojun
  2024-04-09 17:00   ` kernel test robot
  1 sibling, 1 reply; 13+ messages in thread
From: Andy Shevchenko @ 2024-04-09 13:21 UTC (permalink / raw)
  To: Baojun Xu
  Cc: tiwai, robh+dt, lgirdwood, perex, pierre-louis.bossart, kevin-lu,
	shenghao-ding, navada, alsa-devel, linux-kernel, liam.r.girdwood,
	yung-chuan.liao, broonie, soyer

On Tue, Apr 09, 2024 at 10:48:14AM +0800, Baojun Xu wrote:
> Main source code for tas2781 driver for SPI.

...

> +#ifndef __TAS2781_SPI_H__
> +#define __TAS2781_SPI_H__

+ bits.h
+ mutex.h
+ time.h? (for struct tm)
+ types.h

struct calidata is from?..

> +#include <sound/tas2781-dsp.h>

Not sure how this is being used.

Also some forward declarations:

+ struct device;
+ struct firmware;
+ struct gpio_desc;
+ struct regmap;

(I might missed something)

...

> +#define TASDEVICE_RATES			(SNDRV_PCM_RATE_44100 |\
> +	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
> +	SNDRV_PCM_RATE_88200)

For lines likes this, the formatting can be

#define TASDEVICE_RATES							      \
	(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
	 SNDRV_PCM_RATE_88200)

which from my p.o.v. looks better.

...

> +#define TAS2781_SPI_MAX_FREQ		4000000

4 * HZZ_PER_MHZ ?

...

> +enum device_catlog_id {

Too generic name.

> +	HP = 0,
> +	OTHERS

Ditto. Please, add namespace.

> +};

...

> +#include <linux/acpi.h>
> +#include <linux/crc8.h>
> +#include <linux/crc32.h>
> +#include <linux/efi.h>
> +#include <linux/firmware.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>

+ property.h

> +#include <linux/regmap.h>
> +#include <linux/spi/spi.h>
> +#include <sound/hda_codec.h>
> +#include <sound/soc.h>
> +#include <sound/tlv.h>
> +#include <sound/tas2781-tlv.h>

...

> +/* No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD
> + * Define two controls, one is Volume control callbacks, the other is
> + * flag setting control callbacks.
> + */

/*
 * Follow the example of the multi-line
 * comments style as in this comment. Apply
 * it to all your comments.
 */

...

> +	.private_value = (unsigned long)&(struct soc_mixer_control) \
> +		{.reg = xreg, .rreg = xreg, .shift = xshift, \
> +		 .rshift = xshift, .min = xmin, .max = xmax, \
> +		 .invert = xinvert} }

It is unreadable.

	.private_value = (unsigned long)&(struct soc_mixer_control) {	      \
		.reg = xreg, .rreg = xreg, .shift = xshift, .rshift = xshift, \
		.min = xmin, .max = xmax, .invert = xinvert,		      \
	},								      \


See the difference?

Please, apply this to all twisted macros:
- logical split
- leaving trailing commas
- better formatting

...

> +		.range_max = 256 * 128,

Perhaps you want to define this as it's used a lot in the C and header files.

...

> +static const struct regmap_config tasdevice_regmap = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.cache_type = REGCACHE_NONE,
> +	.ranges = tasdevice_ranges,
> +	.num_ranges = ARRAY_SIZE(tasdevice_ranges),
> +	.max_register = 256 * 128,
> +};
> +
> +

One too many blank lines.

> +static void tas2781_spi_reset(struct tasdevice_priv *tas_dev);
> +static int tasdevice_spi_init(struct tasdevice_priv *tas_priv);
> +static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv,
> +	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
> +static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv,
> +	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
> +static int tasdevice_spi_digital_putvol(struct tasdevice_priv *tas_priv,
> +	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);
> +static int tasdevice_spi_digital_getvol(struct tasdevice_priv *tas_priv,
> +	struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc);

Why do you need forward declarations here?

...

> +static int tasdevice_spi_switch_book(struct tasdevice_priv *tas_priv,
> +	int book)

Can be on a single line.

> +{
> +	struct tasdevice *tasdev = &tas_priv->tasdevice;
> +	struct regmap *map = tas_priv->regmap;
> +	int ret = 0;
> +
> +	if (tasdev->cur_book != book) {
> +		/* Change to page 0 before book change. */
> +		ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
> +		if (ret < 0) {
> +			dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
> +			return ret;
> +		}
> +		ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
> +		if (ret < 0)
> +			dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);

Non-fatal error?

> +		tasdev->cur_book = book;
> +	}
> +
> +	return ret;
> +}

...

> +int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
> +	unsigned int reg, unsigned int *val)
> +{
> +	struct regmap *map = tas_priv->regmap;
> +	int ret;
> +
> +	ret = tasdevice_spi_switch_book(tas_priv, TASDEVICE_BOOK_ID(reg));
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * In our TAS2781 SPI mode, if read from other book (not book 0),
> +	 * or read from page number larger than 1 in book 0, one byte more
> +	 * read is needed, and first byte is a dummy byte, need to be ignored.
> +	 */
> +	if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
> +		unsigned char data[2];
> +
> +		ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, 2);

sizeof(data) ?

> +		*val = data[1];
> +	} else {
> +		ret = regmap_read(map, TASDEVICE_PGRG(reg), val);
> +	}
> +	if (ret < 0)
> +		dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(tasdevice_spi_dev_read);

No namespace, why?

...

> +	return ret;
> +
> +}

Here and in other places, one too many blank lines.

...

> +	static const unsigned char page_array[CALIB_MAX] = {
> +		0x17, 0x18, 0x18, 0x13, 0x18

Leave trailing comma, can be helpful in the future.

> +	};
> +	static const unsigned char rgno_array[CALIB_MAX] = {
> +		0x74, 0x0c, 0x14, 0x70, 0x7c

Ditto.

> +	};


...

> +	data = tas_priv->cali_data.data +
> +		tas_priv->index * TASDEVICE_SPEAKER_CALIBRATION_SIZE;
> +	for (j = 0; j < CALIB_MAX; j++) {
> +		rc = tasdevice_spi_dev_bulk_write(tas_priv,
> +			TASDEVICE_REG(0, page_array[j], rgno_array[j]),
> +			&(data[4 * j]), 4);
> +		if (rc < 0)
> +			dev_err(tas_priv->dev,
> +				"chn %d calib %d bulk_wr err = %d\n",
> +				tas_priv->index, j, rc);

The indentation here and in some other places is a mess. Please,
take your time to split these in more readable way.

> +	}
> +

Again, too many unneeded blank lines in the code.

> +}

...

I stopped here as there are already enough for next version.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 3/3] ALSA: hda/tas2781: Firmware load for tas2781 driver for SPI
  2024-04-09  2:48 ` [PATCH v2 3/3] ALSA: hda/tas2781: Firmware load for " Baojun Xu
@ 2024-04-09 13:26   ` Andy Shevchenko
  2024-04-09 18:26   ` kernel test robot
  1 sibling, 0 replies; 13+ messages in thread
From: Andy Shevchenko @ 2024-04-09 13:26 UTC (permalink / raw)
  To: Baojun Xu
  Cc: tiwai, robh+dt, lgirdwood, perex, pierre-louis.bossart, kevin-lu,
	shenghao-ding, navada, alsa-devel, linux-kernel, liam.r.girdwood,
	yung-chuan.liao, broonie, soyer

On Tue, Apr 09, 2024 at 10:48:15AM +0800, Baojun Xu wrote:
> Firmware download and parser lib for tas2781, it work for spi
> device with a single firmware binary file.

I believe this also can benefit from comments given against previous patches.

...

> +		im = &(calibration->dev_data);

Unneeded parentheses.

> +
> +		if (!im->dev_blks)
> +			continue;
> +
> +		for (blks = 0; blks < im->nr_blk; blks++) {
> +			block = &(im->dev_blks[blks]);
> +			if (!block)
> +				continue;
> +			kfree(block->data);
> +		}
> +		kfree(im->dev_blks);
> +	}
> +	kfree(tas_fmw->calibrations);
> +out:
> +	kfree(tas_fmw);

It may gain if you use cleanup.h from day 1.

> +}
> +
> +void tasdevice_spi_calbin_remove(void *context)
> +{
> +	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;

Casting is not needed.

> +	struct tasdevice *tasdev;

> +	if (!tas_priv)
> +		return;

How is this not a dead code?

> +	tasdev = &(tas_priv->tasdevice);
> +	if (tasdev->cali_data_fmw) {
> +		tas2781_clear_calfirmware(tasdev->cali_data_fmw);
> +		tasdev->cali_data_fmw = NULL;
> +	}
> +}

...

> +void tasdevice_spi_config_info_remove(void *context)
> +{
> +	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
> +	struct tasdevice_rca *rca = &(tas_priv->rcabin);
> +	struct tasdevice_config_info **ci = rca->cfg_info;
> +	int i, j;
> +
> +	if (!ci)
> +		return;
> +	for (i = 0; i < rca->ncfgs; i++) {
> +		if (!ci[i])
> +			continue;
> +		if (ci[i]->blk_data) {
> +			for (j = 0; j < (int)ci[i]->real_nblocks; j++) {

Oh, explicit castings should be _rarely_ used. What's the problem with making j
to be the same type as real_nblocks?

> +				if (!ci[i]->blk_data[j])
> +					continue;
> +				kfree(ci[i]->blk_data[j]->regdata);
> +				kfree(ci[i]->blk_data[j]);
> +			}
> +			kfree(ci[i]->blk_data);
> +		}
> +		kfree(ci[i]);
> +	}
> +	kfree(ci);
> +}

...

> +	if (cfg_no >= 0
> +		&& (tas_priv->tasdevice.cur_conf != cfg_no)
> +		&& (cfg_info[rca_conf_no]->active_dev & 1)
> +		&& (tas_priv->tasdevice.is_loaderr == false)) {

This is unparsable. Please, use postfix style and proper indentation.

	if (foo &&
	    bar ...) {
		...stuff...;
	}

> +		status++;
> +		tas_priv->tasdevice.is_loading = true;
> +	} else {
> +		tas_priv->tasdevice.is_loading = false;
> +	}

...

> +	if (state == 0) {
> +		if (tas_priv->cur_prog < tas_fmw->nr_programs) {
> +			/*dsp mode or tuning mode*/
> +			profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
> +			tasdevice_spi_select_tuningprm_cfg(tas_priv,
> +				tas_priv->cur_prog, tas_priv->cur_conf,
> +				profile_cfg_id);
> +		}
> +
> +		tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id,
> +			TASDEVICE_BIN_BLK_PRE_POWER_UP);
> +	} else
> +		tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id,
> +			TASDEVICE_BIN_BLK_PRE_SHUTDOWN);

Out of a sudden different style (no {} in 'else' branch). Try to be consistent
in style everywhere.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/3] ALSA: hda/tas2781: Main code of tas2781 driver for SPI
  2024-04-09  2:48 ` [PATCH v2 2/3] ALSA: hda/tas2781: Main code of " Baojun Xu
  2024-04-09 13:21   ` Andy Shevchenko
@ 2024-04-09 17:00   ` kernel test robot
  1 sibling, 0 replies; 13+ messages in thread
From: kernel test robot @ 2024-04-09 17:00 UTC (permalink / raw)
  To: Baojun Xu, tiwai
  Cc: llvm, oe-kbuild-all, robh+dt, andriy.shevchenko, lgirdwood,
	perex, pierre-louis.bossart, kevin-lu, shenghao-ding, navada,
	alsa-devel, linux-kernel, liam.r.girdwood, yung-chuan.liao,
	baojun.xu, broonie, soyer

Hi Baojun,

kernel test robot noticed the following build warnings:

[auto build test WARNING on tiwai-sound/for-next]
[also build test WARNING on tiwai-sound/for-linus rafael-pm/linux-next linus/master v6.9-rc3 next-20240409]
[cannot apply to rafael-pm/acpi-bus rafael-pm/devprop]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Baojun-Xu/ALSA-hda-tas2781-Modification-for-add-tas2781-driver-for-SPI/20240409-105140
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next
patch link:    https://lore.kernel.org/r/20240409024816.1180-3-baojun.xu%40ti.com
patch subject: [PATCH v2 2/3] ALSA: hda/tas2781: Main code of tas2781 driver for SPI
config: arm64-allyesconfig (https://download.01.org/0day-ci/archive/20240410/202404100015.FAgrEOeO-lkp@intel.com/config)
compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 8b3b4a92adee40483c27f26c478a384cd69c6f05)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240410/202404100015.FAgrEOeO-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202404100015.FAgrEOeO-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from sound/pci/hda/tas2781_hda_spi.c:9:
   In file included from include/linux/acpi.h:39:
   In file included from include/acpi/acpi_io.h:7:
   In file included from arch/arm64/include/asm/acpi.h:14:
   In file included from include/linux/memblock.h:12:
   In file included from include/linux/mm.h:2208:
   include/linux/vmstat.h:508:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     508 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     509 |                            item];
         |                            ~~~~
   include/linux/vmstat.h:515:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     515 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     516 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:522:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     522 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
   include/linux/vmstat.h:527:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     527 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     528 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:536:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     536 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     537 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
>> sound/pci/hda/tas2781_hda_spi.c:1159:6: warning: variable 'ret' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
    1159 |         if (strstr(dev_name(&spi->dev), "TIAS2781")) {
         |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   sound/pci/hda/tas2781_hda_spi.c:1203:6: note: uninitialized use occurs here
    1203 |         if (ret)
         |             ^~~
   sound/pci/hda/tas2781_hda_spi.c:1159:2: note: remove the 'if' if its condition is always true
    1159 |         if (strstr(dev_name(&spi->dev), "TIAS2781")) {
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1160 |                 device_name = "TIAS2781";
    1161 |                 tas_priv->save_calibration = tas2781_save_calibration;
    1162 |                 tas_priv->apply_calibration = tas2781_apply_calib;
    1163 |         } else
         |           ~~~~
    1164 |                 goto err;
         |                 ~~~~~~~~
>> sound/pci/hda/tas2781_hda_spi.c:1153:6: warning: variable 'ret' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
    1153 |         if (!tas_priv)
         |             ^~~~~~~~~
   sound/pci/hda/tas2781_hda_spi.c:1203:6: note: uninitialized use occurs here
    1203 |         if (ret)
         |             ^~~
   sound/pci/hda/tas2781_hda_spi.c:1153:2: note: remove the 'if' if its condition is always false
    1153 |         if (!tas_priv)
         |         ^~~~~~~~~~~~~~
    1154 |                 goto err;
         |                 ~~~~~~~~
   sound/pci/hda/tas2781_hda_spi.c:1144:9: note: initialize the variable 'ret' to silence this warning
    1144 |         int ret;
         |                ^
         |                 = 0
   7 warnings generated.


vim +1159 sound/pci/hda/tas2781_hda_spi.c

  1138	
  1139	static int tas2781_hda_spi_probe(struct spi_device *spi)
  1140	{
  1141		struct tasdevice_priv *tas_priv;
  1142		struct tas2781_hda *tas_hda;
  1143		const char *device_name;
  1144		int ret;
  1145	
  1146		tas_hda = devm_kzalloc(&spi->dev, sizeof(*tas_hda), GFP_KERNEL);
  1147		if (!tas_hda)
  1148			return -ENOMEM;
  1149	
  1150		tas_hda->dev = &spi->dev;
  1151	
  1152		tas_priv = devm_kzalloc(&spi->dev, sizeof(*tas_priv), GFP_KERNEL);
> 1153		if (!tas_priv)
  1154			goto err;
  1155		tas_priv->dev = &spi->dev;
  1156		tas_priv->client = (void *)spi;
  1157		tas_hda->priv = tas_priv;
  1158	
> 1159		if (strstr(dev_name(&spi->dev), "TIAS2781")) {
  1160			device_name = "TIAS2781";
  1161			tas_priv->save_calibration = tas2781_save_calibration;
  1162			tas_priv->apply_calibration = tas2781_apply_calib;
  1163		} else
  1164			goto err;
  1165	
  1166		tas_priv->irq_info.irq = spi->irq;
  1167		dev_set_drvdata(&spi->dev, tas_hda);
  1168		ret = tas2781_read_acpi(tas_hda, device_name,
  1169			spi_get_chipselect(spi, 0));
  1170		if (ret)
  1171			return dev_err_probe(tas_hda->dev, ret,
  1172				"Platform not supported\n");
  1173	
  1174		tas_priv->regmap = devm_regmap_init_spi(spi, &tasdevice_regmap);
  1175		if (IS_ERR(tas_priv->regmap)) {
  1176			ret = PTR_ERR(tas_priv->regmap);
  1177			dev_err(tas_priv->dev, "Failed to allocate regmap: %d\n",
  1178				ret);
  1179			goto err;
  1180		}
  1181		ret = tasdevice_spi_init(tas_hda->priv);
  1182		if (ret)
  1183			goto err;
  1184	
  1185		pm_runtime_set_autosuspend_delay(tas_hda->dev, 3000);
  1186		pm_runtime_use_autosuspend(tas_hda->dev);
  1187		pm_runtime_mark_last_busy(tas_hda->dev);
  1188		pm_runtime_set_active(tas_hda->dev);
  1189		pm_runtime_get_noresume(tas_hda->dev);
  1190		pm_runtime_enable(tas_hda->dev);
  1191	
  1192		pm_runtime_put_autosuspend(tas_hda->dev);
  1193	
  1194		tas2781_spi_reset(tas_priv);
  1195	
  1196		ret = component_add(tas_hda->dev, &tas2781_hda_comp_ops);
  1197		if (ret) {
  1198			dev_err(tas_hda->dev, "Register component failed: %d\n", ret);
  1199			pm_runtime_disable(tas_hda->dev);
  1200		}
  1201	
  1202	err:
  1203		if (ret)
  1204			tas2781_hda_remove(&spi->dev);
  1205		return ret;
  1206	}
  1207	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 3/3] ALSA: hda/tas2781: Firmware load for tas2781 driver for SPI
  2024-04-09  2:48 ` [PATCH v2 3/3] ALSA: hda/tas2781: Firmware load for " Baojun Xu
  2024-04-09 13:26   ` Andy Shevchenko
@ 2024-04-09 18:26   ` kernel test robot
  1 sibling, 0 replies; 13+ messages in thread
From: kernel test robot @ 2024-04-09 18:26 UTC (permalink / raw)
  To: Baojun Xu, tiwai
  Cc: llvm, oe-kbuild-all, robh+dt, andriy.shevchenko, lgirdwood,
	perex, pierre-louis.bossart, kevin-lu, shenghao-ding, navada,
	alsa-devel, linux-kernel, liam.r.girdwood, yung-chuan.liao,
	baojun.xu, broonie, soyer

Hi Baojun,

kernel test robot noticed the following build warnings:

[auto build test WARNING on tiwai-sound/for-next]
[also build test WARNING on tiwai-sound/for-linus rafael-pm/linux-next linus/master v6.9-rc3 next-20240409]
[cannot apply to rafael-pm/acpi-bus rafael-pm/devprop]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Baojun-Xu/ALSA-hda-tas2781-Modification-for-add-tas2781-driver-for-SPI/20240409-105140
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next
patch link:    https://lore.kernel.org/r/20240409024816.1180-4-baojun.xu%40ti.com
patch subject: [PATCH v2 3/3] ALSA: hda/tas2781: Firmware load for tas2781 driver for SPI
config: arm64-allyesconfig (https://download.01.org/0day-ci/archive/20240410/202404100229.tkI9vGOu-lkp@intel.com/config)
compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 8b3b4a92adee40483c27f26c478a384cd69c6f05)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240410/202404100229.tkI9vGOu-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202404100229.tkI9vGOu-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from sound/pci/hda/tas2781_spi_fwlib.c:17:
   In file included from include/sound/pcm_params.h:10:
   In file included from include/sound/pcm.h:15:
   In file included from include/linux/mm.h:2208:
   include/linux/vmstat.h:508:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     508 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     509 |                            item];
         |                            ~~~~
   include/linux/vmstat.h:515:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     515 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     516 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:522:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     522 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
   include/linux/vmstat.h:527:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     527 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     528 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:536:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     536 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     537 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
>> sound/pci/hda/tas2781_spi_fwlib.c:2149:3: warning: variable 'status' is uninitialized when used here [-Wuninitialized]
    2149 |                 status++;
         |                 ^~~~~~
   sound/pci/hda/tas2781_spi_fwlib.c:2084:12: note: initialize the variable 'status' to silence this warning
    2084 |         int status;
         |                   ^
         |                    = 0
   6 warnings generated.


vim +/status +2149 sound/pci/hda/tas2781_spi_fwlib.c

  2073	
  2074	int tasdevice_spi_select_tuningprm_cfg(void *context, int prm_no,
  2075		int cfg_no, int rca_conf_no)
  2076	{
  2077		struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
  2078		struct tasdevice_rca *rca = &(tas_priv->rcabin);
  2079		struct tasdevice_config_info **cfg_info = rca->cfg_info;
  2080		struct tasdevice_fw *tas_fmw = tas_priv->fmw;
  2081		struct tasdevice_prog *program;
  2082		struct tasdevice_config *conf;
  2083		int prog_status = 0;
  2084		int status;
  2085	
  2086		if (!tas_fmw) {
  2087			dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
  2088			return 0;
  2089		}
  2090	
  2091		if (cfg_no >= tas_fmw->nr_configurations) {
  2092			dev_err(tas_priv->dev,
  2093				"%s: cfg(%d) is not in range of conf %u\n",
  2094				__func__, cfg_no, tas_fmw->nr_configurations);
  2095			return 0;
  2096		}
  2097	
  2098		if (prm_no >= tas_fmw->nr_programs) {
  2099			dev_err(tas_priv->dev,
  2100				"%s: prm(%d) is not in range of Programs %u\n",
  2101				__func__, prm_no, tas_fmw->nr_programs);
  2102			return 0;
  2103		}
  2104	
  2105		if (rca_conf_no >= rca->ncfgs || rca_conf_no < 0 ||
  2106			!cfg_info) {
  2107			dev_err(tas_priv->dev,
  2108				"conf_no:%d should be in range from 0 to %u\n",
  2109				rca_conf_no, rca->ncfgs-1);
  2110			return 0;
  2111		}
  2112	
  2113		if (cfg_info[rca_conf_no]->active_dev & 1) {
  2114			if (prm_no >= 0	&& (tas_priv->tasdevice.cur_prog != prm_no
  2115				|| tas_priv->force_fwload_status)) {
  2116				tas_priv->tasdevice.cur_conf = -1;
  2117				tas_priv->tasdevice.is_loading = true;
  2118				prog_status++;
  2119			}
  2120		} else {
  2121			tas_priv->tasdevice.is_loading = false;
  2122		}
  2123	
  2124		if (prog_status) {
  2125			program = &(tas_fmw->programs[prm_no]);
  2126			tasdevice_load_data(tas_priv, &(program->dev_data));
  2127			if (tas_priv->tasdevice.is_loaderr == false
  2128				&& tas_priv->tasdevice.is_loading == true) {
  2129				struct tasdevice_fw *cal_fmw =
  2130					tas_priv->tasdevice.cali_data_fmw;
  2131	
  2132				if (cal_fmw) {
  2133					struct tasdevice_calibration
  2134						*cal = cal_fmw->calibrations;
  2135	
  2136					if (cal)
  2137						load_calib_data(tas_priv,
  2138							&(cal->dev_data));
  2139				}
  2140				tas_priv->tasdevice.cur_prog = prm_no;
  2141			}
  2142	
  2143		}
  2144	
  2145		if (cfg_no >= 0
  2146			&& (tas_priv->tasdevice.cur_conf != cfg_no)
  2147			&& (cfg_info[rca_conf_no]->active_dev & 1)
  2148			&& (tas_priv->tasdevice.is_loaderr == false)) {
> 2149			status++;
  2150			tas_priv->tasdevice.is_loading = true;
  2151		} else {
  2152			tas_priv->tasdevice.is_loading = false;
  2153		}
  2154	
  2155		if (status) {
  2156			conf = &(tas_fmw->configs[cfg_no]);
  2157			status = 0;
  2158			tasdevice_load_data(tas_priv, &(conf->dev_data));
  2159			if (tas_priv->tasdevice.is_loaderr == true) {
  2160				status |= 1 << 4;
  2161			} else if (tas_priv->tasdevice.is_loaderr == false
  2162				&& tas_priv->tasdevice.is_loading == true) {
  2163				tas_priv->tasdevice.cur_conf = cfg_no;
  2164			}
  2165		} else
  2166			dev_dbg(tas_priv->dev, "%s: Unneeded loading dsp conf %d\n",
  2167				__func__, cfg_no);
  2168	
  2169		status |= cfg_info[rca_conf_no]->active_dev;
  2170	
  2171		return prog_status;
  2172	}
  2173	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [EXTERNAL] Re: [PATCH v2 1/3] ALSA: hda/tas2781: Modification for add tas2781 driver for SPI
  2024-04-09 13:02   ` Andy Shevchenko
@ 2024-04-16  7:45     ` Xu, Baojun
  2024-04-16 14:02       ` Andy Shevchenko
  0 siblings, 1 reply; 13+ messages in thread
From: Xu, Baojun @ 2024-04-16  7:45 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: tiwai, robh+dt, lgirdwood, perex, pierre-louis.bossart, Lu,
	Kevin, Ding, Shenghao, Navada Kanyana, Mukund, alsa-devel,
	linux-kernel, liam.r.girdwood, yung-chuan.liao, broonie, soyer,
	13916275206

Hi Andy,

Thansk for your suggestions, answer in line.
> From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Sent: 09 April 2024 21:02
> To: Xu, Baojun
> Cc: tiwai@suse.de; robh+dt@kernel.org; lgirdwood@gmail.com; perex@perex.cz; pierre-louis.bossart@linux.intel.com; Lu, Kevin; Ding, Shenghao; Navada Kanyana, Mukund; alsa-devel@alsa-project.org; linux-kernel@vger.kernel.org; liam.r.girdwood@intel.com; yung-chuan.liao@linux.intel.com; broonie@kernel.org; soyer@irl.hu
> Subject: [EXTERNAL] Re: [PATCH v2 1/3] ALSA: hda/tas2781: Modification for add tas2781 driver for SPI
> 
> On Tue, Apr 09, 2024 at 10: 48: 13AM +0800, Baojun Xu wrote: > Integrate tas2781 configs for HP Laptops. Every tas2781 in the laptop > will work as a single speaker on SPI bus. The code support realtek as Realtek > the primary codec. 
> ZjQcmQRYFpfptBannerStart
> This message was sent from outside of Texas Instruments.
> Do not click links or open attachments unless you recognize the source of this email and know the content is safe. If you wish to report this message to IT Security, please forward the message as an attachment to phishing@list.ti.com
> 
> ZjQcmQRYFpfptBannerEnd
> 
> On Tue, Apr 09, 2024 at 10:48:13AM +0800, Baojun Xu wrote:
> > Integrate tas2781 configs for HP Laptops. Every tas2781 in the laptop
> > will work as a single speaker on SPI bus. The code support realtek as
> 
> Realtek
> 
> > the primary codec.
> 
> ...
> 
> >               {"INT33FE", },
> >               {"INT3515", },
> >               /* Non-conforming _HID for Cirrus Logic already released */
> > +             {"TXNW2781", },
> 
> This seems wrong. The ID here is correct from ACPI specification perspective.
> Can you elaborate why you think it's "non-conforming _HID"?

Just put into middle of array, should no effective to functions.
Ok, I will change it's position in next patch.
> 
> >               {"CLSA0100", },
> >               {"CLSA0101", },
> > 
> > ...
> 
> >       /* Non-conforming _HID for Cirrus Logic already released */
> >       { "CLSA0100", (unsigned long)&cs35l41_hda },
> >       { "CLSA0101", (unsigned long)&cs35l41_hda },
> > +     { "TXNW2781", (unsigned long)&tas2781_hda },
> 
> Ditto.
> 
> >       { }
> 
> ...
> 
> > @@ -39,6 +39,7 @@ snd-hda-scodec-cs35l56-spi-objs :=  cs35l56_hda_spi.o
> >  snd-hda-cs-dsp-ctls-objs :=          hda_cs_dsp_ctl.o
> >  snd-hda-scodec-component-objs :=     hda_component.o
> >  snd-hda-scodec-tas2781-i2c-objs :=   tas2781_hda_i2c.o
> > +snd-hda-scodec-tas2781-spi-objs :=   tas2781_hda_spi.o tas2781_spi_fwlib.o
> 
> Actually these 'objs' has to be 'y', can you fix it in the prerequisite patch?
> 

Do you mean set CONFIG_SND_HDA_SCODEC_TAS2781_SPI=y in .config?
It's m now.

> Also wondering why fwlib is only a requirement for SPI. How does I²C work?

Because in I2C mode, one probed device driver will support all devices,
firmware binary is only one file, include all of devices.
But in SPI mode, multi driver probed, so we use single firmware binary for
every spi device.

> 
> --
> With Best Regards,
> Andy Shevchenko
> 
> 
> 

Best Regards
Jim





^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [EXTERNAL] Re: [PATCH v2 1/3] ALSA: hda/tas2781: Modification for add tas2781 driver for SPI
  2024-04-16  7:45     ` [EXTERNAL] " Xu, Baojun
@ 2024-04-16 14:02       ` Andy Shevchenko
  0 siblings, 0 replies; 13+ messages in thread
From: Andy Shevchenko @ 2024-04-16 14:02 UTC (permalink / raw)
  To: Xu, Baojun
  Cc: tiwai, robh+dt, lgirdwood, perex, pierre-louis.bossart, Lu,
	Kevin, Ding, Shenghao, Navada Kanyana, Mukund, alsa-devel,
	linux-kernel, liam.r.girdwood, yung-chuan.liao, broonie, soyer,
	13916275206

On Tue, Apr 16, 2024 at 07:45:21AM +0000, Xu, Baojun wrote:
> > From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> > Sent: 09 April 2024 21:02
> > On Tue, Apr 09, 2024 at 10: 48: 13AM +0800, Baojun Xu wrote:
> > On Tue, Apr 09, 2024 at 10:48:13AM +0800, Baojun Xu wrote:

...

> > > @@ -39,6 +39,7 @@ snd-hda-scodec-cs35l56-spi-objs :=  cs35l56_hda_spi.o
> > >  snd-hda-cs-dsp-ctls-objs :=          hda_cs_dsp_ctl.o
> > >  snd-hda-scodec-component-objs :=     hda_component.o
> > >  snd-hda-scodec-tas2781-i2c-objs :=   tas2781_hda_i2c.o
> > > +snd-hda-scodec-tas2781-spi-objs :=   tas2781_hda_spi.o tas2781_spi_fwlib.o
> > 
> > Actually these 'objs' has to be 'y', can you fix it in the prerequisite patch?
> 
> Do you mean set CONFIG_SND_HDA_SCODEC_TAS2781_SPI=y in .config?

No. I mean the Kconfig syntax in use. -objs is for user space tools. Kernel
code should use -y in this case.

> It's m now.
> 
> > Also wondering why fwlib is only a requirement for SPI. How does I²C work?
> 
> Because in I2C mode, one probed device driver will support all devices,
> firmware binary is only one file, include all of devices.
> But in SPI mode, multi driver probed, so we use single firmware binary for
> every spi device.

But does I²C version still need the firmware? Can't the FW handling be factored
out to a single module for both?

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [EXTERNAL] Re: [PATCH v2 2/3] ALSA: hda/tas2781: Main code of tas2781 driver for SPI
  2024-04-09 13:21   ` Andy Shevchenko
@ 2024-04-18  5:12     ` Xu, Baojun
  2024-04-18 11:53       ` Andy Shevchenko
  0 siblings, 1 reply; 13+ messages in thread
From: Xu, Baojun @ 2024-04-18  5:12 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: tiwai, robh+dt, lgirdwood, perex, pierre-louis.bossart, Lu,
	Kevin, Ding, Shenghao, Navada Kanyana, Mukund, alsa-devel,
	linux-kernel, liam.r.girdwood, yung-chuan.liao, broonie, soyer

Hi Andy,

Thanks for your valuable suggestions.

> From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Sent: 09 April 2024 21:21
> To: Xu, Baojun
> Cc: tiwai@suse.de; robh+dt@kernel.org; lgirdwood@gmail.com; perex@perex.cz; pierre-louis.bossart@linux.intel.com; Lu, Kevin; Ding, Shenghao; Navada Kanyana, Mukund; alsa-devel@alsa-project.org; linux-kernel@vger.kernel.org; liam.r.girdwood@intel.com; yung-chuan.liao@linux.intel.com; broonie@kernel.org; soyer@irl.hu
> Subject: [EXTERNAL] Re: [PATCH v2 2/3] ALSA: hda/tas2781: Main code of tas2781 driver for SPI
> 
> On Tue, Apr 09, 2024 at 10: 48: 14AM +0800, Baojun Xu wrote: > Main source code for tas2781 driver for SPI. .. . > +#ifndef __TAS2781_SPI_H__ > +#define __TAS2781_SPI_H__ + bits. h + mutex. h + time. h? (for struct tm) + types. h struct calidata
> ZjQcmQRYFpfptBannerStart
> This message was sent from outside of Texas Instruments.
> Do not click links or open attachments unless you recognize the source of this email and know the content is safe. If you wish to report this message to IT Security, please forward the message as an attachment to phishing@list.ti.com
> 
> ZjQcmQRYFpfptBannerEnd
> 
> On Tue, Apr 09, 2024 at 10:48:14AM +0800, Baojun Xu wrote:
> > Main source code for tas2781 driver for SPI.
> 
> ...
> 
> > +#ifndef __TAS2781_SPI_H__
> > +#define __TAS2781_SPI_H__
> 
> + bits.h
> + mutex.h
> + time.h? (for struct tm)
> + types.h
> 
> struct calidata is from?..
> 
> > +#include <sound/tas2781-dsp.h>
> 
> Not sure how this is being used.

Was used for firmware binary file parser, all of file format information
was defined in this header file. It can be shared between SPI & I2C. 
> 
> Also some forward declarations:
> 
> + struct device;
> + struct firmware;
> + struct gpio_desc;
> + struct regmap;
> 
> ...
> 
> > +#define TAS2781_SPI_MAX_FREQ         4000000
> 
> 4 * HZZ_PER_MHZ ?
> 
Not found it, will use HZ_PER_MHZ in next patch.
> ...
> 
> --
> With Best Regards,
> Andy Shevchenko
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [EXTERNAL] Re: [PATCH v2 2/3] ALSA: hda/tas2781: Main code of tas2781 driver for SPI
  2024-04-18  5:12     ` [EXTERNAL] " Xu, Baojun
@ 2024-04-18 11:53       ` Andy Shevchenko
  0 siblings, 0 replies; 13+ messages in thread
From: Andy Shevchenko @ 2024-04-18 11:53 UTC (permalink / raw)
  To: Xu, Baojun
  Cc: tiwai, robh+dt, lgirdwood, perex, pierre-louis.bossart, Lu,
	Kevin, Ding, Shenghao, Navada Kanyana, Mukund, alsa-devel,
	linux-kernel, liam.r.girdwood, yung-chuan.liao, broonie, soyer

On Thu, Apr 18, 2024 at 05:12:03AM +0000, Xu, Baojun wrote:
> > From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> > Sent: 09 April 2024 21:21
> > To: Xu, Baojun
> > On Tue, Apr 09, 2024 at 10: 48: 14AM +0800, Baojun Xu wrote:
> > On Tue, Apr 09, 2024 at 10:48:14AM +0800, Baojun Xu wrote:

...

> > > +#ifndef __TAS2781_SPI_H__
> > > +#define __TAS2781_SPI_H__
> > 
> > + bits.h
> > + mutex.h
> > + time.h? (for struct tm)
> > + types.h
> > 
> > struct calidata is from?..
> > 
> > > +#include <sound/tas2781-dsp.h>
> > 
> > Not sure how this is being used.
> 
> Was used for firmware binary file parser, all of file format information
> was defined in this header file. It can be shared between SPI & I2C.

_This header_ file seems not using anything from it, does it?

> > Also some forward declarations:
> > 
> > + struct device;
> > + struct firmware;
> > + struct gpio_desc;
> > + struct regmap;

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2024-04-18 11:53 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-09  2:48 [PATCH v2 0/3] ALSA: hda/tas2781: Add tas2781 driver for SPI Baojun Xu
2024-04-09  2:48 ` [PATCH v2 1/3] ALSA: hda/tas2781: Modification for add " Baojun Xu
2024-04-09 13:02   ` Andy Shevchenko
2024-04-16  7:45     ` [EXTERNAL] " Xu, Baojun
2024-04-16 14:02       ` Andy Shevchenko
2024-04-09  2:48 ` [PATCH v2 2/3] ALSA: hda/tas2781: Main code of " Baojun Xu
2024-04-09 13:21   ` Andy Shevchenko
2024-04-18  5:12     ` [EXTERNAL] " Xu, Baojun
2024-04-18 11:53       ` Andy Shevchenko
2024-04-09 17:00   ` kernel test robot
2024-04-09  2:48 ` [PATCH v2 3/3] ALSA: hda/tas2781: Firmware load for " Baojun Xu
2024-04-09 13:26   ` Andy Shevchenko
2024-04-09 18:26   ` kernel test robot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.