[PATCH v1] ALSA SoC: Texas Instruments TAS2781 Audio Smart Amp
The TAS2781 driver implements a flexible and configurable register setting for one, two, even multiple TAS2781 chips. All the register setting are in a bin file. Almost no specific register setting can be found in the code.
Signed-off-by: Kevin Lu luminlong@139.com --- sound/soc/codecs/Kconfig | 13 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tas2781-dsp.c | 2483 ++++++++++++++++++++++++++++++++ sound/soc/codecs/tas2781-dsp.h | 213 +++ sound/soc/codecs/tas2781-i2c.c | 2143 +++++++++++++++++++++++++++ sound/soc/codecs/tas2781.h | 208 +++ 6 files changed, 5062 insertions(+) create mode 100644 sound/soc/codecs/tas2781-dsp.c create mode 100644 sound/soc/codecs/tas2781-dsp.h create mode 100644 sound/soc/codecs/tas2781-i2c.c create mode 100644 sound/soc/codecs/tas2781.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7022e6286..31d2d9594 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -222,6 +222,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TAS2764 imply SND_SOC_TAS2770 imply SND_SOC_TAS2780 + imply SND_SOC_TAS2781 imply SND_SOC_TAS5086 imply SND_SOC_TAS571X imply SND_SOC_TAS5720 @@ -1573,6 +1574,18 @@ config SND_SOC_TAS2780 Enable support for Texas Instruments TAS2780 high-efficiency digital input mono Class-D audio power amplifiers.
+config SND_SOC_TAS2781 + tristate "Texas Instruments TAS2781 speaker amplifier" + depends on I2C + select REGMAP_I2C + help + Enable support for Texas Instruments TAS2781 Smart Amplifier + Digital input mono Class-D and DSP-inside audio power amplifiers. + Note the TAS2781 driver implements a flexible and configurable + register setting, for one, two, even multiple TAS2781 chips. + All the register setting are in a bin file. Almost no specific + register setting can be found in the code. + config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 9170ee144..088fed9e7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -354,6 +354,7 @@ snd-soc-tas2552-objs := tas2552.o snd-soc-tas2562-objs := tas2562.o snd-soc-tas2764-objs := tas2764.o snd-soc-tas2780-objs := tas2780.o +snd-soc-tas2781-objs := tas2781-i2c.o tas2781-dsp.o # Mux snd-soc-simple-mux-objs := simple-mux.o
@@ -604,6 +605,7 @@ obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o +obj-$(CONFIG_SND_SOC_TAS2781) += snd-soc-tas2781.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o diff --git a/sound/soc/codecs/tas2781-dsp.c b/sound/soc/codecs/tas2781-dsp.c new file mode 100644 index 000000000..56c7efd30 --- /dev/null +++ b/sound/soc/codecs/tas2781-dsp.c @@ -0,0 +1,2483 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier + * + * Copyright (C) 2022 - 2023 Texas Instruments Incorporated + * https://www.ti.com + * + * The TAS2781 driver implements a flexible and configurable register setting + * for one, two, even multiple TAS2781 chips. All the register setting are in + * a bin file. Almost no specific register setting can be found in the code. + * + * Author: Shenghao Ding shenghao-ding@ti.com + * Kevin Lu kevin-lu@ti.com + */ + +#include <linux/regmap.h> +#include <linux/i2c.h> +#include <linux/string.h> +#include <linux/crc8.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/firmware.h> + +#include "tas2781.h" + +#define TAS2781_CAL_BIN_PATH ("/lib/firmware/") + +#define ERROR_PRAM_CRCCHK (0x0000000) +#define ERROR_YRAM_CRCCHK (0x0000001) +#define BINFILEDOCVER (0) +#define DRVFWVER (1) +#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 (8) +#define TAS2781_YRAM5_END_REG (27) + +#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) + +struct TYCRC { + unsigned char mnOffset; + unsigned char mnLen; +}; + +const unsigned int BinFileformatVerInfo[][2] = { + {0x100, 0x100}, + {0x110, 0x200}, + {0x200, 0x300}, + {0x210, 0x310}, + {0x230, 0x320}, + {0x300, 0x400} +}; + +const char *devicefamily[1] = { + "TAS Devices" }; + +const char *devicelist[TASDEVICE_DSP_TAS_MAX_DEVICE] = { + "TAS2555", + "TAS2555 Stereo", + "TAS2557 Mono", + "TAS2557 Dual Mono", + "TAS2559", + "TAS2563", + NULL, + "TAS2563 Dual Mono", + "TAS2563 Quad", + "TAS2563 2.1", + "TAS2781", + "TAS2781 Stereo", + "TAS2781 2.1", + "TAS2781 Quad" +}; + +const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = { + 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4 +}; + +static int fw_parse_block_data_kernel(struct TFirmware *pFirmware, + struct TBlock *pBlock, const struct firmware *pFW, int offset) +{ + const unsigned char *pData = pFW->data; + + if (offset + 4 > pFW->size) { + dev_err(pFirmware->dev, "%s: File Size error\n", __func__); + offset = -1; + goto out; + } + pBlock->mnType = SMS_HTONL(pData[offset], + pData[offset + 1], pData[offset + 2], + pData[offset + 3]); + offset += 4; + + if (offset + 1 > pFW->size) { + dev_err(pFirmware->dev, "%s: mbPChkSumPresent error\n", + __func__); + offset = -1; + goto out; + } + pBlock->mbPChkSumPresent = pData[offset]; + offset++; + + if (offset + 1 > pFW->size) { + dev_err(pFirmware->dev, "%s: mnPChkSum error\n", __func__); + offset = -1; + goto out; + } + pBlock->mnPChkSum = pData[offset]; + offset++; + + if (offset + 1 > pFW->size) { + dev_err(pFirmware->dev, "%s: mbYChkSumPresent error\n", + __func__); + offset = -1; + goto out; + } + pBlock->mbYChkSumPresent = pData[offset]; + offset++; + + if (offset + 1 > pFW->size) { + dev_err(pFirmware->dev, "%s: mnYChkSum error\n", __func__); + offset = -1; + goto out; + } + pBlock->mnYChkSum = pData[offset]; + offset++; + + if (offset + 4 > pFW->size) { + dev_err(pFirmware->dev, "%s: blk_size error\n", __func__); + offset = -1; + goto out; + } + pBlock->blk_size = SMS_HTONL(pData[offset], + pData[offset + 1], pData[offset + 2], + pData[offset + 3]); + offset += 4; + + if (offset + 4 > pFW->size) { + dev_err(pFirmware->dev, "%s: nSublocks error\n", __func__); + offset = -1; + goto out; + } + pBlock->nSublocks = SMS_HTONL(pData[offset], + pData[offset + 1], pData[offset + 2], pData[offset + 3]); + offset += 4; + + pBlock->mpData = kzalloc(pBlock->blk_size, GFP_KERNEL); + if (pBlock->mpData == NULL) { + offset = -1; + goto out; + } + memcpy(pBlock->mpData, &pData[offset], pBlock->blk_size); + offset += pBlock->blk_size; +out: + return offset; +} + +static int fw_parse_data_kernel(struct TFirmware *pFirmware, + struct TData *pImageData, const struct firmware *pFW, int offset) +{ + const unsigned char *pData = pFW->data; + unsigned int nBlock; + + if (offset + 4 > pFW->size) { + dev_err(pFirmware->dev, "%s: File Size error\n", __func__); + offset = -1; + goto out; + } + pImageData->mnBlocks = SMS_HTONL(pData[offset], + pData[offset + 1], pData[offset + 2], pData[offset + 3]); + offset += 4; + + pImageData->mpBlocks = + kcalloc(pImageData->mnBlocks, sizeof(struct TBlock), + GFP_KERNEL); + if (pImageData->mpBlocks == NULL) { + dev_err(pFirmware->dev, "%s: FW memory failed!\n", __func__); + goto out; + } + + for (nBlock = 0; nBlock < pImageData->mnBlocks; nBlock++) { + offset = fw_parse_block_data_kernel(pFirmware, + &(pImageData->mpBlocks[nBlock]), pFW, offset); + if (offset < 0) { + offset = -1; + goto out; + } + } +out: + return offset; +} + +int fw_parse_program_data_kernel(struct tasdevice_priv *tas_dev, + struct TFirmware *pFirmware, + const struct firmware *pFW, int offset) +{ + struct TProgram *pProgram; + const unsigned char *buf = pFW->data; + unsigned int nProgram = 0; + + for (nProgram = 0; nProgram < pFirmware->mnPrograms; nProgram++) { + pProgram = &(pFirmware->mpPrograms[nProgram]); + if (offset + 64 > pFW->size) { + dev_err(tas_dev->dev, "%s: mpName error\n", + __func__); + offset = -1; + goto out; + } + memcpy(pProgram->mpName, &buf[offset], 64); + offset += 64; + + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnAppMode error\n", + __func__); + offset = -1; + goto out; + } + pProgram->mnAppMode = buf[offset]; + offset++; + + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnPDMI2SMode error\n", + __func__); + offset = -1; + goto out; + } + pProgram->mnPDMI2SMode = buf[offset]; + offset++; + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnISnsPD error\n", + __func__); + offset = -1; + goto out; + } + pProgram->mnISnsPD = buf[offset]; + offset++; + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnVSnsPD error\n", + __func__); + offset = -1; + goto out; + } + pProgram->mnVSnsPD = buf[offset]; + offset++; + //skip 3-byte reserved + offset += 3; + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnPowerLDG error\n", + __func__); + offset = -1; + goto out; + } + pProgram->mnPowerLDG = buf[offset]; + offset++; + + offset = fw_parse_data_kernel(pFirmware, &(pProgram->mData), + pFW, offset); + if (offset < 0) + goto out; + } +out: + return offset; +} + +int fw_parse_configuration_data_kernel( + struct tasdevice_priv *tas_dev, + struct TFirmware *pFirmware, const struct firmware *pFW, int offset) +{ + const unsigned char *pData = pFW->data; + + unsigned int nConfiguration; + struct TConfiguration *pConfiguration; + + for (nConfiguration = 0; nConfiguration < pFirmware->mnConfigurations; + nConfiguration++) { + pConfiguration = + &(pFirmware->mpConfigurations[nConfiguration]); + if (offset + 64 > pFW->size) { + dev_err(tas_dev->dev, "%s: mpName error\n", + __func__); + offset = -1; + goto out; + } + memcpy(pConfiguration->mpName, &pData[offset], 64); + offset += 64; + + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, + "%s: mnDevice_orientation error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mnDevice_orientation = pData[offset]; + offset++; + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnDevices error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mnDevices = pData[offset + 1]; + offset += 1; + + if (offset + 2 > pFW->size) { + dev_err(tas_dev->dev, "%s: mProgram error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mProgram = SMS_HTONS(pData[offset], + pData[offset + 1]); + offset += 2; + + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnSamplingRate error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mnSamplingRate = SMS_HTONL(pData[offset], + pData[offset + 1], pData[offset + 2], + pData[offset + 3]); + offset += 4; + + if (offset + 2 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnPLLSrc error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mnPLLSrc = SMS_HTONS(pData[offset], + pData[offset + 1]); + offset += 2; + + if (offset + 2 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnFsRate error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mnFsRate = SMS_HTONS(pData[offset], + pData[offset + 1]); + offset += 2; + + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnPLLSrcRate error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mnPLLSrcRate = SMS_HTONL(pData[offset], + pData[offset + 1], pData[offset + 2], + pData[offset + 3]); + offset += 4; + offset = fw_parse_data_kernel(pFirmware, + &(pConfiguration->mData), pFW, offset); + if (offset < 0) + goto out; + } +out: + return offset; +} + +int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_dev, + const struct firmware *pFW, int offset) +{ + struct TFirmware *pFirmware = tas_dev->mpFirmware; + struct tasdevice_dspfw_hdr *pFw_hdr = &(pFirmware->fw_hdr); + const unsigned char *buf = pFW->data; + struct TProgram *pProgram; + struct TConfiguration *pConfiguration; + unsigned int nProgram = 0, nConfiguration = 0; + unsigned short maxConf = TASDEVICE_MAXCONFIG_NUM_KERNEL; + + if (offset + 2 > pFW->size) { + dev_err(tas_dev->dev, "%s: File Size error\n", __func__); + offset = -1; + goto out; + } + pFw_hdr->mnDeviceFamily = SMS_HTONS(buf[offset], buf[offset + 1]); + if (pFw_hdr->mnDeviceFamily != 0) { + dev_err(tas_dev->dev, "ERROR:%s:not TAS device\n", __func__); + offset = -1; + goto out; + } + offset += 2; + if (offset + 2 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnDevice error\n", __func__); + offset = -1; + goto out; + } + pFw_hdr->mnDevice = SMS_HTONS(buf[offset], buf[offset + 1]); + if (pFw_hdr->mnDevice >= TASDEVICE_DSP_TAS_MAX_DEVICE || + pFw_hdr->mnDevice == 6) { + dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n", + __func__, pFw_hdr->mnDevice); + offset = -1; + goto out; + } + offset += 2; + pFw_hdr->ndev = deviceNumber[pFw_hdr->mnDevice]; + + if (pFw_hdr->ndev != tas_dev->ndev) { + dev_err(tas_dev->dev, + "%s: ndev(%u) in dspbin dismatch ndev(%u) in DTS\n", + __func__, pFw_hdr->ndev, tas_dev->ndev); + offset = -1; + goto out; + } + + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnPrograms error\n", __func__); + offset = -1; + goto out; + } + pFirmware->mnPrograms = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + offset += 4; + + if (pFirmware->mnPrograms == 0 || pFirmware->mnPrograms > + TASDEVICE_MAXPROGRAM_NUM_KERNEL) { + dev_err(tas_dev->dev, "%s: mnPrograms is invalid\n", __func__); + offset = -1; + goto out; + } + + if (offset + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > pFW->size) { + dev_err(tas_dev->dev, "%s: mpPrograms error\n", __func__); + offset = -1; + goto out; + } + + pFirmware->mpPrograms = + kcalloc(pFirmware->mnPrograms, + sizeof(struct TProgram), GFP_KERNEL); + if (pFirmware->mpPrograms == NULL) { + dev_err(tas_dev->dev, "%s: mpPrograms memory failed!\n", + __func__); + offset = -1; + goto out; + } + + for (nProgram = 0; nProgram < pFirmware->mnPrograms; nProgram++) { + pProgram = &(pFirmware->mpPrograms[nProgram]); + pProgram->prog_size = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + pFirmware->cfg_start_offset += pProgram->prog_size; + offset += 4; + } + offset += (4 * (5 - nProgram)); + + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnConfigurations error\n", + __func__); + offset = -1; + goto out; + } + pFirmware->mnConfigurations = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + offset += 4; + maxConf = (pFw_hdr->ndev >= 4) ? + TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS : + TASDEVICE_MAXCONFIG_NUM_KERNEL; + if (pFirmware->mnConfigurations == 0 || + pFirmware->mnConfigurations > maxConf) { + dev_err(tas_dev->dev, "%s: mnConfigurations is invalid\n", + __func__); + offset = -1; + goto out; + } + + if (offset + 4 * maxConf > pFW->size) { + dev_err(tas_dev->dev, "%s: mpConfigurations error\n", + __func__); + offset = -1; + goto out; + } + + pFirmware->mpConfigurations = kcalloc(pFirmware->mnConfigurations, + sizeof(struct TConfiguration), GFP_KERNEL); + if (pFirmware->mpConfigurations == NULL) { + offset = -1; + goto out; + } + + for (nConfiguration = 0; nConfiguration < pFirmware->mnPrograms; + nConfiguration++) { + pConfiguration = + &(pFirmware->mpConfigurations[nConfiguration]); + pConfiguration->cfg_size = SMS_HTONL(buf[offset], + buf[offset + 1], buf[offset + 2], buf[offset + 3]); + offset += 4; + } + + offset += (4 * (maxConf - nConfiguration)); + pFirmware->prog_start_offset = offset; + pFirmware->cfg_start_offset += offset; +out: + return offset; +} + +int tasdevice_load_block_kernel(struct tasdevice_priv *pTAS2781, + struct TBlock *pBlock) +{ + int nResult = 0; + + unsigned char *pData = pBlock->mpData; + unsigned int i = 0, length = 0; + const unsigned int blk_size = pBlock->blk_size; + unsigned char dev_idx = 0; + struct tasdevice_dspfw_hdr *pFw_hdr = &(pTAS2781->mpFirmware->fw_hdr); + struct tasdevice_fw_fixed_hdr *pFw_fixed_hdr = &(pFw_hdr->mnFixedHdr); + + if (pFw_fixed_hdr->mnPPCVersion >= PPC3_VERSION) { + switch (pBlock->mnType) { + case MAIN_ALL_DEVICES_1X: + dev_idx = 0|0x80; + break; + case MAIN_DEVICE_A_1X: + dev_idx = 1|0x80; + break; + case COEFF_DEVICE_A_1X: + case PRE_DEVICE_A_1X: + dev_idx = 1|0xC0; + break; + case MAIN_DEVICE_B_1X: + dev_idx = 2|0x80; + break; + case COEFF_DEVICE_B_1X: + case PRE_DEVICE_B_1X: + dev_idx = 2|0xC0; + break; + case MAIN_DEVICE_C_1X: + dev_idx = 3|0x80; + break; + case COEFF_DEVICE_C_1X: + case PRE_DEVICE_C_1X: + dev_idx = 3|0xC0; + break; + case MAIN_DEVICE_D_1X: + dev_idx = 4|0x80; + break; + case COEFF_DEVICE_D_1X: + case PRE_DEVICE_D_1X: + dev_idx = 4|0xC0; + break; + default: + dev_info(pTAS2781->dev, + "%s: TAS2781 load block: Other Type = 0x%02x\n", + __func__, pBlock->mnType); + break; + } + } else { + switch (pBlock->mnType) { + case MAIN_ALL_DEVICES: + dev_idx = 0|0x80; + break; + case MAIN_DEVICE_A: + dev_idx = 1|0x80; + break; + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + dev_idx = 1|0xC0; + break; + case MAIN_DEVICE_B: + dev_idx = 2|0x80; + break; + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + dev_idx = 2|0xC0; + break; + case MAIN_DEVICE_C: + dev_idx = 3|0x80; + break; + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + dev_idx = 3|0xC0; + break; + case MAIN_DEVICE_D: + dev_idx = 4|0x80; + break; + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + dev_idx = 4|0xC0; + break; + default: + dev_info(pTAS2781->dev, + "%s: TAS2781 load block: Other Type = 0x%02x\n", + __func__, + pBlock->mnType); + break; + } + } + + for (i = 0; i < pBlock->nSublocks; i++) { + int rc = tasdevice_process_block(pTAS2781, pData + length, + dev_idx, blk_size - length); + if (rc < 0) { + dev_err(pTAS2781->dev, + "%s: ERROR:%u %u sublock write error\n", + __func__, length, blk_size); + break; + } + length += (unsigned int)rc; + if (blk_size < length) { + dev_err(pTAS2781->dev, + "%s: ERROR:%u %u out of memory\n", + __func__, length, blk_size); + break; + } + } + + return nResult; +} + +int fw_parse_variable_header_git(struct tasdevice_priv *tas_dev, + const struct firmware *pFW, int offset) +{ + const unsigned char *buf = pFW->data; + struct TFirmware *pFirmware = tas_dev->mpFirmware; + struct tasdevice_dspfw_hdr *pFw_hdr = &(pFirmware->fw_hdr); + int i = strlen((char *)&buf[offset]); + + i++; + + if (offset + i > pFW->size) { + dev_err(tas_dev->dev, "%s: File Size error\n", __func__); + offset = -1; + goto out; + } + + pFw_hdr->mpDescription = kmemdup(&buf[offset], i, GFP_KERNEL); + if (pFw_hdr->mpDescription == NULL) + goto out; + offset += i; + + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: File Size error\n", __func__); + offset = -1; + goto out; + } + pFw_hdr->mnDeviceFamily = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + if (pFw_hdr->mnDeviceFamily != 0) { + dev_err(tas_dev->dev, "ERROR:%s: not TAS device\n", __func__); + offset = -1; + goto out; + } + offset += 4; + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: File Size error\n", __func__); + offset = -1; + goto out; + } + pFw_hdr->mnDevice = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + if (pFw_hdr->mnDevice >= TASDEVICE_DSP_TAS_MAX_DEVICE || + pFw_hdr->mnDevice == 6) { + dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n", + __func__, pFw_hdr->mnDevice); + offset = -1; + goto out; + } + offset += 4; + pFw_hdr->ndev = deviceNumber[pFw_hdr->mnDevice]; + if (pFw_hdr->ndev != tas_dev->ndev) { + dev_err(tas_dev->dev, + "%s: ndev(%u) in dspbin dismatch ndev(%u) in DTS\n", + __func__, pFw_hdr->ndev, + tas_dev->ndev); + offset = -1; + } + +out: + return offset; +} + +int fw_parse_variable_header_cal(struct tasdevice_priv *tas_dev, + struct TFirmware *pCalFirmware, const struct firmware *pFW, int offset) +{ + const unsigned char *buf = pFW->data; + struct tasdevice_dspfw_hdr *pFw_hdr = &(pCalFirmware->fw_hdr); + int i = strlen((char *)&buf[offset]); + + i++; + + if (offset + i > pFW->size) { + dev_err(tas_dev->dev, "%s: File Size error\n", __func__); + offset = -1; + goto out; + } + + pFw_hdr->mpDescription = kmemdup(&buf[offset], i, GFP_KERNEL); + if (pFw_hdr->mpDescription == NULL) + goto out; + offset += i; + + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnDeviceFamily error\n", __func__); + offset = -1; + goto out; + } + pFw_hdr->mnDeviceFamily = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + if (pFw_hdr->mnDeviceFamily != 0) { + dev_err(tas_dev->dev, "ERROR:%s: not TAS device\n", __func__); + offset = -1; + goto out; + } + offset += 4; + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnDevice error\n", __func__); + offset = -1; + goto out; + } + pFw_hdr->mnDevice = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + if (pFw_hdr->mnDevice >= TASDEVICE_DSP_TAS_MAX_DEVICE || + pFw_hdr->mnDevice == 6) { + dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n", + __func__, pFw_hdr->mnDevice); + offset = -1; + goto out; + } + offset += 4; + pFw_hdr->ndev = deviceNumber[pFw_hdr->mnDevice]; + if (pFw_hdr->ndev != 1) { + dev_err(tas_dev->dev, + "%s: calbin must be 1, but currently ndev(%u)\n", + __func__, pFw_hdr->ndev); + offset = -1; + } + +out: + return offset; +} + +static inline void tas2781_clear_Calfirmware(struct TFirmware + *mpCalFirmware) +{ + int i = 0; + unsigned int nBlock = 0; + + if (mpCalFirmware->mpCalibrations) { + struct TCalibration *pCalibration; + + for (i = 0; i < mpCalFirmware->mnCalibrations; i++) { + pCalibration = &(mpCalFirmware->mpCalibrations[i]); + if (pCalibration) { + struct TData *pImageData = + &(pCalibration->mData); + + if (pImageData->mpBlocks) { + struct TBlock *pBlock; + + for (nBlock = 0; nBlock < + pImageData->mnBlocks; + nBlock++) { + pBlock = + &(pImageData->mpBlocks[nBlock]); + kfree(pBlock->mpData); + } + kfree(pImageData->mpBlocks); + } + kfree(pCalibration->mpDescription); + } + } + kfree(mpCalFirmware->mpCalibrations); + } + kfree(mpCalFirmware); +} + +static int fw_parse_block_data(struct TFirmware *pFirmware, + struct TBlock *pBlock, const struct firmware *pFW, int offset) +{ + unsigned char *pData = (unsigned char *)pFW->data; + int n; + + if (offset + 4 > pFW->size) { + dev_err(pFirmware->dev, "%s: mnType error\n", __func__); + offset = -1; + goto out; + } + pBlock->mnType = SMS_HTONL(pData[offset], + pData[offset + 1], pData[offset + 2], pData[offset + 3]); + offset += 4; + + if (pFirmware->fw_hdr.mnFixedHdr.mnDriverVersion >= + PPC_DRIVER_CRCCHK) { + if (offset + 1 > pFW->size) { + dev_err(pFirmware->dev, "%s: mbPChkSumPresent error\n", + __func__); + offset = -1; + goto out; + } + pBlock->mbPChkSumPresent = pData[offset]; + offset++; + + if (offset + 1 > pFW->size) { + dev_err(pFirmware->dev, "%s: mnPChkSum error\n", + __func__); + offset = -1; + goto out; + } + pBlock->mnPChkSum = pData[offset]; + offset++; + + if (offset + 1 > pFW->size) { + dev_err(pFirmware->dev, "%s: mbYChkSumPresent error\n", + __func__); + offset = -1; + goto out; + } + pBlock->mbYChkSumPresent = pData[offset]; + offset++; + + if (offset + 1 > pFW->size) { + dev_err(pFirmware->dev, "%s: mnYChkSum error\n", + __func__); + offset = -1; + goto out; + } + pBlock->mnYChkSum = pData[offset]; + offset++; + } else { + pBlock->mbPChkSumPresent = 0; + pBlock->mbYChkSumPresent = 0; + } + if (offset + 4 > pFW->size) { + dev_err(pFirmware->dev, "%s: mnCommands error\n", __func__); + offset = -1; + goto out; + } + pBlock->mnCommands = SMS_HTONL(pData[offset], + pData[offset + 1], pData[offset + 2], pData[offset + 3]); + offset += 4; + + n = pBlock->mnCommands * 4; + if (offset + n > pFW->size) { + dev_err(pFirmware->dev, + "%s: File Size(%u) error offset = %d n = %d\n", + __func__, pFW->size, offset, n); + offset = -1; + goto out; + } + pBlock->mpData = kmemdup(&pData[offset], n, GFP_KERNEL); + if (pBlock->mpData == NULL) { + offset = -1; + goto out; + } + offset += n; +out: + return offset; +} + +static int fw_parse_data(struct TFirmware *pFirmware, + struct TData *pImageData, const struct firmware *pFW, int offset) +{ + const unsigned char *pData = (unsigned char *)pFW->data; + int n = 0; + unsigned int nBlock; + + if (offset + 64 > pFW->size) { + dev_err(pFirmware->dev, "%s: mpName error\n", __func__); + n = -1; + goto out; + } + memcpy(pImageData->mpName, &pData[offset], 64); + offset += 64; + + n = strlen((char *)&pData[offset]); + n++; + if (offset + n > pFW->size) { + dev_err(pFirmware->dev, "%s: mpDescription error\n", __func__); + offset = -1; + goto out; + } + pImageData->mpDescription = kmemdup(pData, n, GFP_KERNEL); + if (pImageData->mpDescription == NULL) + goto out; + offset += n; + + if (offset + 2 > pFW->size) { + dev_err(pFirmware->dev, "%s: mnBlocks error\n", __func__); + offset = -1; + goto out; + } + pImageData->mnBlocks = SMS_HTONS(pData[offset], pData[offset + 1]); + offset += 2; + + pImageData->mpBlocks = + kcalloc(pImageData->mnBlocks, sizeof(struct TBlock), + GFP_KERNEL); + if (pImageData->mpBlocks == NULL) { + dev_err(pFirmware->dev, "%s: FW memory failed!\n", __func__); + goto out; + } + for (nBlock = 0; nBlock < pImageData->mnBlocks; nBlock++) { + offset = fw_parse_block_data(pFirmware, + &(pImageData->mpBlocks[nBlock]), pFW, offset); + if (offset < 0) { + offset = -1; + goto out; + } + } +out: + return offset; +} + +static int fw_parse_calibration_data(struct tasdevice_priv *tas_dev, + struct TFirmware *pFirmware, const struct firmware *pFW, int offset) +{ + unsigned char *pData = (unsigned char *)pFW->data; + unsigned int n = 0; + unsigned int nCalibration = 0; + struct TCalibration *pCalibration = NULL; + + if (offset + 2 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnCalibrations error\n", __func__); + offset = -1; + goto out; + } + pFirmware->mnCalibrations = SMS_HTONS(pData[offset], + pData[offset + 1]); + offset += 2; + + if (pFirmware->mnCalibrations != 1) { + dev_err(tas_dev->dev, + "%s: only support one calibraiton(%d)!\n", + __func__, pFirmware->mnCalibrations); + goto out; + } + + pFirmware->mpCalibrations = + kcalloc(pFirmware->mnCalibrations, sizeof(struct TCalibration), + GFP_KERNEL); + if (pFirmware->mpCalibrations == NULL) { + dev_err(tas_dev->dev, "%s: mpCalibrations memory failed!\n", + __func__); + offset = -1; + goto out; + } + for (nCalibration = 0; nCalibration < pFirmware->mnCalibrations; + nCalibration++) { + if (offset + 64 > pFW->size) { + dev_err(tas_dev->dev, "%s: mpCalibrations error\n", + __func__); + offset = -1; + goto out; + } + pCalibration = &(pFirmware->mpCalibrations[nCalibration]); + memcpy(pCalibration->mpName, &pData[offset], 64); + offset += 64; + + n = strlen((char *)&pData[offset]); + n++; + if (offset + n > pFW->size) { + dev_err(tas_dev->dev, "%s: mpDescription error\n", + __func__); + offset = -1; + goto out; + } + pCalibration->mpDescription = kmemdup(&pData[offset], n, + GFP_KERNEL); + if (pCalibration->mpDescription == NULL) { + offset = -1; + goto out; + } + offset += n; + + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, + "%s: mnProgram error, offset = %d\n", __func__, + offset); + offset = -1; + goto out; + } + pCalibration->mnProgram = pData[offset]; + offset++; + + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, + "%s: mnConfiguration error, offset = %d\n", + __func__, + offset); + offset = -1; + goto out; + } + pCalibration->mnConfiguration = pData[offset]; + offset++; + + offset = fw_parse_data(pFirmware, &(pCalibration->mData), pFW, + offset); + if (offset < 0) + goto out; + } + +out: + + return offset; +} + +static int fw_parse_program_data(struct tasdevice_priv *tas_dev, + struct TFirmware *pFirmware, const struct firmware *pFW, int offset) +{ + struct TProgram *pProgram; + unsigned char *buf = (unsigned char *)pFW->data; + int nProgram = 0; + + if (offset + 2 > pFW->size) { + dev_err(tas_dev->dev, "%s: File Size error\n", __func__); + offset = -1; + goto out; + } + pFirmware->mnPrograms = SMS_HTONS(buf[offset], buf[offset + 1]); + offset += 2; + + if (pFirmware->mnPrograms == 0) { + dev_err(tas_dev->dev, "%s: mnPrograms is null, maybe calbin\n", + __func__); + //Do not "offset = -1;", because of calbin + goto out; + } + + pFirmware->mpPrograms = + kcalloc(pFirmware->mnPrograms, sizeof(struct TProgram), + GFP_KERNEL); + if (pFirmware->mpPrograms == NULL) { + offset = -1; + goto out; + } + for (nProgram = 0; nProgram < pFirmware->mnPrograms; nProgram++) { + int n = 0; + + pProgram = &(pFirmware->mpPrograms[nProgram]); + if (offset + 64 > pFW->size) { + dev_err(tas_dev->dev, "%s: mpName error\n", __func__); + offset = -1; + goto out; + } + memcpy(pProgram->mpName, &buf[offset], 64); + offset += 64; + + n = strlen((char *)&buf[offset]); + n++; + if (offset + n > pFW->size) { + dev_err(tas_dev->dev, "%s: mpDescription error\n", + __func__); + offset = -1; + goto out; + } + pProgram->mpDescription = kmemdup(&buf[offset], n, GFP_KERNEL); + if (pProgram->mpDescription == NULL) { + offset = -1; + goto out; + } + + offset += n; + + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnAppMode error\n", + __func__); + offset = -1; + goto out; + } + pProgram->mnAppMode = buf[offset]; + offset++; + + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnPDMI2SMode error\n", + __func__); + offset = -1; + goto out; + } + pProgram->mnPDMI2SMode = buf[offset]; + offset++; + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnISnsPD error\n", + __func__); + offset = -1; + goto out; + } + pProgram->mnISnsPD = buf[offset]; + offset++; + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnVSnsPD error\n", + __func__); + offset = -1; + goto out; + } + pProgram->mnVSnsPD = buf[offset]; + offset++; + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnPowerLDG error\n", + __func__); + offset = -1; + goto out; + } + pProgram->mnPowerLDG = buf[offset]; + offset++; + + offset = fw_parse_data(pFirmware, &(pProgram->mData), pFW, + offset); + if (offset < 0) + goto out; + } +out: + return offset; +} + +static int fw_parse_configuration_data( + struct tasdevice_priv *tas_dev, + struct TFirmware *pFirmware, + const struct firmware *pFW, int offset) +{ + unsigned char *pData = (unsigned char *)pFW->data; + int n; + unsigned int nConfiguration; + struct TConfiguration *pConfiguration; + + if (offset + 2 > pFW->size) { + dev_err(tas_dev->dev, "%s: File Size error\n", __func__); + offset = -1; + goto out; + } + pFirmware->mnConfigurations = SMS_HTONS(pData[offset], + pData[offset + 1]); + offset += 2; + + if (pFirmware->mnConfigurations == 0) { + dev_err(tas_dev->dev, "%s: mnConfigurations is zero\n", + __func__); + //Do not "offset = -1;", because of calbin + goto out; + } + pFirmware->mpConfigurations = + kcalloc(pFirmware->mnConfigurations, + sizeof(struct TConfiguration), GFP_KERNEL); + + for (nConfiguration = 0; nConfiguration < pFirmware->mnConfigurations; + nConfiguration++) { + pConfiguration = + &(pFirmware->mpConfigurations[nConfiguration]); + if (offset + 64 > pFW->size) { + dev_err(tas_dev->dev, "%s: File Size error\n", + __func__); + offset = -1; + goto out; + } + memcpy(pConfiguration->mpName, &pData[offset], 64); + offset += 64; + + n = strlen((char *)&pData[offset]); + n++; + if (offset + n > pFW->size) { + dev_err(tas_dev->dev, "%s: mpDescription error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mpDescription = kmemdup(&pData[offset], n, + GFP_KERNEL); + + if (pConfiguration->mpDescription == NULL) { + dev_err(tas_dev->dev, "%s: FW memory failed!\n", + __func__); + goto out; + } + offset += n; + if (offset + 2 > pFW->size) { + dev_err(tas_dev->dev, + "%s: mnDevice_orientation error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mnDevice_orientation = pData[offset]; + + pConfiguration->mnDevices = pData[offset + 1]; + offset += 2; + + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mProgram error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mProgram = pData[offset]; + offset++; + + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnSamplingRate error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mnSamplingRate = SMS_HTONL(pData[offset], + pData[offset + 1], pData[offset + 2], + pData[offset + 3]); + offset += 4; + + if (offset + 1 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnPLLSrc error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mnPLLSrc = pData[offset]; + offset++; + + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnPLLSrcRate error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mnPLLSrcRate = SMS_HTONL(pData[offset], + pData[offset + 1], pData[offset + 2], + pData[offset + 3]); + offset += 4; + + if (offset + 2 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnFsRate error\n", + __func__); + offset = -1; + goto out; + } + pConfiguration->mnFsRate = SMS_HTONS(pData[offset], + pData[offset + 1]); + offset += 2; + + offset = fw_parse_data(pFirmware, &(pConfiguration->mData), + pFW, offset); + if (offset < 0) + goto out; + } +out: + return offset; +} + +static int fw_parse_header(struct tasdevice_priv *tas_dev, + struct TFirmware *pFirmware, const struct firmware *pFW, int offset) +{ + struct tasdevice_dspfw_hdr *pFw_hdr = &(pFirmware->fw_hdr); + struct tasdevice_fw_fixed_hdr *pFw_fixed_hdr = &(pFw_hdr->mnFixedHdr); + const unsigned char *buf = (unsigned char *)pFW->data; + int i = 0; + unsigned char pMagicNumber[] = { 0x35, 0x35, 0x35, 0x32 }; + + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + if (memcmp(&buf[offset], pMagicNumber, 4)) { + dev_err(tas_dev->dev, "%s: Magic number doesn't match", + __func__); + offset = -EINVAL; + goto out; + } + pFw_fixed_hdr->mnMagicNumber = SMS_HTONL(buf[offset], + buf[offset + 1], buf[offset + 2], buf[offset + 3]); + offset += 4; + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnFWSize error\n", __func__); + offset = -1; + goto out; + } + pFw_fixed_hdr->mnFWSize = SMS_HTONL(buf[offset], + buf[offset + 1], buf[offset + 2], buf[offset + 3]); + offset += 4; + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: File Size error\n", __func__); + offset = -1; + goto out; + } + if (pFw_fixed_hdr->mnFWSize != pFW->size) { + dev_err(tas_dev->dev, "File size not match, %d %d", pFW->size, + pFw_fixed_hdr->mnFWSize); + offset = -1; + goto out; + } + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnChecksum error\n", __func__); + offset = -1; + goto out; + } + pFw_fixed_hdr->mnChecksum = SMS_HTONL(buf[offset], + buf[offset + 1], buf[offset + 2], buf[offset + 3]); + offset += 4; + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnPPCVersion error\n", __func__); + offset = -1; + goto out; + } + pFw_fixed_hdr->mnPPCVersion = SMS_HTONL(buf[offset], + buf[offset + 1], buf[offset + 2], buf[offset + 3]); + offset += 4; + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnFWVersion error\n", __func__); + offset = -1; + goto out; + } + pFw_fixed_hdr->mnFWVersion = SMS_HTONL(buf[offset], + buf[offset + 1], buf[offset + 2], buf[offset + 3]); + offset += 4; + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnDriverVersion error\n", __func__); + offset = -1; + goto out; + } + pFw_fixed_hdr->mnDriverVersion = SMS_HTONL(buf[offset], + buf[offset + 1], buf[offset + 2], buf[offset + 3]); + offset += 4; + if (offset + 4 > pFW->size) { + dev_err(tas_dev->dev, "%s: mnTimeStamp error\n", __func__); + offset = -1; + goto out; + } + for (i = 0; i < sizeof(BinFileformatVerInfo) / + sizeof(BinFileformatVerInfo[0]); i++) { + if (BinFileformatVerInfo[i][DRVFWVER] == + pFw_fixed_hdr->mnDriverVersion) { + pFw_hdr->mnBinFileDocVer = + BinFileformatVerInfo[i][BINFILEDOCVER]; + break; + } + } + pFw_fixed_hdr->mnTimeStamp = SMS_HTONL(buf[offset], + buf[offset + 1], buf[offset + 2], buf[offset + 3]); + offset += 4; + if (offset + 64 > pFW->size) { + dev_err(tas_dev->dev, "%s: mpDDCName error\n", __func__); + offset = -1; + goto out; + } + memcpy(pFw_fixed_hdr->mpDDCName, &buf[offset], 64); + offset += 64; + + out: + return offset; +} + +static const unsigned char crc8_lookup_table[CRC8_TABLE_SIZE] = { + 0x00, 0x4D, 0x9A, 0xD7, 0x79, 0x34, 0xE3, 0xAE, + 0xF2, 0xBF, 0x68, 0x25, 0x8B, 0xC6, 0x11, 0x5C, + 0xA9, 0xE4, 0x33, 0x7E, 0xD0, 0x9D, 0x4A, 0x07, + 0x5B, 0x16, 0xC1, 0x8C, 0x22, 0x6F, 0xB8, 0xF5, + 0x1F, 0x52, 0x85, 0xC8, 0x66, 0x2B, 0xFC, 0xB1, + 0xED, 0xA0, 0x77, 0x3A, 0x94, 0xD9, 0x0E, 0x43, + 0xB6, 0xFB, 0x2C, 0x61, 0xCF, 0x82, 0x55, 0x18, + 0x44, 0x09, 0xDE, 0x93, 0x3D, 0x70, 0xA7, 0xEA, + 0x3E, 0x73, 0xA4, 0xE9, 0x47, 0x0A, 0xDD, 0x90, + 0xCC, 0x81, 0x56, 0x1B, 0xB5, 0xF8, 0x2F, 0x62, + 0x97, 0xDA, 0x0D, 0x40, 0xEE, 0xA3, 0x74, 0x39, + 0x65, 0x28, 0xFF, 0xB2, 0x1C, 0x51, 0x86, 0xCB, + 0x21, 0x6C, 0xBB, 0xF6, 0x58, 0x15, 0xC2, 0x8F, + 0xD3, 0x9E, 0x49, 0x04, 0xAA, 0xE7, 0x30, 0x7D, + 0x88, 0xC5, 0x12, 0x5F, 0xF1, 0xBC, 0x6B, 0x26, + 0x7A, 0x37, 0xE0, 0xAD, 0x03, 0x4E, 0x99, 0xD4, + 0x7C, 0x31, 0xE6, 0xAB, 0x05, 0x48, 0x9F, 0xD2, + 0x8E, 0xC3, 0x14, 0x59, 0xF7, 0xBA, 0x6D, 0x20, + 0xD5, 0x98, 0x4F, 0x02, 0xAC, 0xE1, 0x36, 0x7B, + 0x27, 0x6A, 0xBD, 0xF0, 0x5E, 0x13, 0xC4, 0x89, + 0x63, 0x2E, 0xF9, 0xB4, 0x1A, 0x57, 0x80, 0xCD, + 0x91, 0xDC, 0x0B, 0x46, 0xE8, 0xA5, 0x72, 0x3F, + 0xCA, 0x87, 0x50, 0x1D, 0xB3, 0xFE, 0x29, 0x64, + 0x38, 0x75, 0xA2, 0xEF, 0x41, 0x0C, 0xDB, 0x96, + 0x42, 0x0F, 0xD8, 0x95, 0x3B, 0x76, 0xA1, 0xEC, + 0xB0, 0xFD, 0x2A, 0x67, 0xC9, 0x84, 0x53, 0x1E, + 0xEB, 0xA6, 0x71, 0x3C, 0x92, 0xDF, 0x08, 0x45, + 0x19, 0x54, 0x83, 0xCE, 0x60, 0x2D, 0xFA, 0xB7, + 0x5D, 0x10, 0xC7, 0x8A, 0x24, 0x69, 0xBE, 0xF3, + 0xAF, 0xE2, 0x35, 0x78, 0xD6, 0x9B, 0x4C, 0x01, + 0xF4, 0xB9, 0x6E, 0x23, 0x8D, 0xC0, 0x17, 0x5A, + 0x06, 0x4B, 0x9C, 0xD1, 0x7F, 0x32, 0xE5, 0xA8 +}; + +static int isInPageYRAM(struct tasdevice_priv *pTAS2781, + struct TYCRC *pCRCData, + unsigned char nBook, unsigned char nPage, + unsigned char nReg, unsigned char len) +{ + int nResult = 0; + + if (nBook == TAS2781_YRAM_BOOK1) { + if (nPage == TAS2781_YRAM1_PAGE) { + if (nReg >= TAS2781_YRAM1_START_REG) { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = len; + nResult = 1; + } else if ((nReg + len) > TAS2781_YRAM1_START_REG) { + pCRCData->mnOffset = TAS2781_YRAM1_START_REG; + pCRCData->mnLen = + len - (TAS2781_YRAM1_START_REG - nReg); + nResult = 1; + } else + nResult = 0; + } else if (nPage == TAS2781_YRAM3_PAGE) { + if (nReg > TAS2781_YRAM3_END_REG) { + nResult = 0; + } else if (nReg >= TAS2781_YRAM3_START_REG) { + if ((nReg + len) > TAS2781_YRAM3_END_REG) { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = + TAS2781_YRAM3_END_REG - nReg + 1; + nResult = 1; + } else { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = len; + nResult = 1; + } + } else { + if ((nReg + (len-1)) < + TAS2781_YRAM3_START_REG) + nResult = 0; + else { + pCRCData->mnOffset = + TAS2781_YRAM3_START_REG; + pCRCData->mnLen = + len - (TAS2781_YRAM3_START_REG - nReg); + nResult = 1; + } + } + } + } else if (nBook == + TAS2781_YRAM_BOOK2) { + if (nPage == TAS2781_YRAM5_PAGE) { + if (nReg > TAS2781_YRAM5_END_REG) { + nResult = 0; + } else if (nReg >= TAS2781_YRAM5_START_REG) { + if ((nReg + len) > TAS2781_YRAM5_END_REG) { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = + TAS2781_YRAM5_END_REG - nReg + 1; + nResult = 1; + } else { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = len; + nResult = 1; + } + } else { + if ((nReg + (len-1)) < + TAS2781_YRAM5_START_REG) + nResult = 0; + else { + pCRCData->mnOffset = + TAS2781_YRAM5_START_REG; + pCRCData->mnLen = + len - (TAS2781_YRAM5_START_REG - nReg); + nResult = 1; + } + } + } + } else + nResult = 0; + + return nResult; +} + +static int isInBlockYRAM(struct tasdevice_priv *pTAS2781, + struct TYCRC *pCRCData, + unsigned char nBook, unsigned char nPage, + unsigned char nReg, unsigned char len) +{ + int nResult = 0; + + if (nBook == TAS2781_YRAM_BOOK1) { + if (nPage < TAS2781_YRAM2_START_PAGE) + nResult = 0; + else if (nPage <= TAS2781_YRAM2_END_PAGE) { + if (nReg > TAS2781_YRAM2_END_REG) + nResult = 0; + else if (nReg >= TAS2781_YRAM2_START_REG) { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = len; + nResult = 1; + } else { + if ((nReg + (len-1)) < + TAS2781_YRAM2_START_REG) + nResult = 0; + else { + pCRCData->mnOffset = + TAS2781_YRAM2_START_REG; + pCRCData->mnLen = + nReg + len - TAS2781_YRAM2_START_REG; + nResult = 1; + } + } + } else + nResult = 0; + } else if (nBook == + TAS2781_YRAM_BOOK2) { + if (nPage < TAS2781_YRAM4_START_PAGE) + nResult = 0; + else if (nPage <= TAS2781_YRAM4_END_PAGE) { + if (nReg > TAS2781_YRAM2_END_REG) + nResult = 0; + else if (nReg >= TAS2781_YRAM2_START_REG) { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = len; + nResult = 1; + } else { + if ((nReg + (len-1)) + < TAS2781_YRAM2_START_REG) + nResult = 0; + else { + pCRCData->mnOffset = + TAS2781_YRAM2_START_REG; + pCRCData->mnLen = + nReg + len - TAS2781_YRAM2_START_REG; + nResult = 1; + } + } + } else + nResult = 0; + } else + nResult = 0; + + return nResult; +} + +static int isYRAM(struct tasdevice_priv *pTAS2781, struct TYCRC *pCRCData, + unsigned char nBook, unsigned char nPage, + unsigned char nReg, unsigned char len) +{ + int nResult = 0; + + nResult = isInPageYRAM(pTAS2781, pCRCData, nBook, nPage, nReg, len); + if (nResult == 0) + nResult = isInBlockYRAM(pTAS2781, pCRCData, nBook, + nPage, nReg, len); + + return nResult; +} + +/* + * crc8-calculate a crc8 over the given input data. + * + * table: crc table used for calculation. + * pdata: pointer to data buffer. + * nbytes: number of bytes in data buffer. + * crc: previous returned crc8 value. + */ +static u8 ti_crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, + size_t nbytes, u8 crc) +{ + /*loop over the buffer data */ + while (nbytes-- > 0) + crc = table[(crc ^ *(pdata += 1)) & 0xff]; + + return crc; +} + +static int doSingleRegCheckSum(struct tasdevice_priv *pTAS2781, + enum channel chl, + unsigned char nBook, unsigned char nPage, + unsigned char nReg, unsigned char nValue) +{ + int nResult = 0; + struct TYCRC sCRCData; + unsigned int nData1 = 0; + + if ((nBook == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (nPage == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (nReg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (nReg <= (TASDEVICE_PAGE_REG( + TAS2781_SA_COEFF_SWAP_REG) + 4))) { + /*DSP swap command, pass */ + nResult = 0; + goto end; + } + + nResult = isYRAM(pTAS2781, &sCRCData, nBook, nPage, nReg, 1); + if (nResult == 1) { + nResult = pTAS2781->read(pTAS2781, chl, + TASDEVICE_REG(nBook, nPage, nReg), &nData1); + if (nResult < 0) + goto end; + + if (nData1 != nValue) { + dev_err(pTAS2781->dev, + "error2, B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n", + nBook, nPage, nReg, + nValue, nData1); + nResult = -EAGAIN; + pTAS2781->tasdevice[chl].mnErrCode |= + ERROR_YRAM_CRCCHK; + goto end; + } + + if (nData1 != nValue) { + dev_err(pTAS2781->dev, + "error2, B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n", + nBook, nPage, nReg, + nValue, nData1); + nResult = -EAGAIN; + goto end; + } + + nResult = ti_crc8(crc8_lookup_table, &nValue, 1, 0); + } + +end: + return nResult; +} + +static int doMultiRegCheckSum(struct tasdevice_priv *pTAS2781, + enum channel chn, unsigned char nBook, unsigned char nPage, + unsigned char nReg, unsigned int len) +{ + int nResult = 0, i = 0; + unsigned char nCRCChkSum = 0; + unsigned char nBuf1[128] = {0}; + struct TYCRC TCRCData; + + if ((nReg + len-1) > 127) { + nResult = -EINVAL; + dev_err(pTAS2781->dev, "firmware error\n"); + goto end; + } + + if ((nBook == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (nPage == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (nReg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (len == 4)) { + /*DSP swap command, pass */ + nResult = 0; + goto end; + } + + nResult = isYRAM(pTAS2781, &TCRCData, nBook, nPage, nReg, len); + dev_info(pTAS2781->dev, + "isYRAM: nBook 0x%x, nPage 0x%x, nReg 0x%x\n", + nBook, nPage, nReg); + dev_info(pTAS2781->dev, + "isYRAM: TCRCData.mnLen 0x%x, len 0x%x, nResult %d\n", + TCRCData.mnLen, len, nResult); + dev_info(pTAS2781->dev, "TCRCData.mnOffset %x\n", TCRCData.mnOffset); + if (nResult == 1) { + if (len == 1) { + dev_err(pTAS2781->dev, "firmware error\n"); + nResult = -EINVAL; + goto end; + } else { + nResult = pTAS2781->bulk_read(pTAS2781, chn, + TASDEVICE_REG(nBook, nPage, TCRCData.mnOffset), + nBuf1, TCRCData.mnLen); + if (nResult < 0) + goto end; + + for (i = 0; i < TCRCData.mnLen; i++) { + if ((nBook == TASDEVICE_BOOK_ID( + TAS2781_SA_COEFF_SWAP_REG)) + && (nPage == TASDEVICE_PAGE_ID( + TAS2781_SA_COEFF_SWAP_REG)) + && ((i + TCRCData.mnOffset) + >= TASDEVICE_PAGE_REG( + TAS2781_SA_COEFF_SWAP_REG)) + && ((i + TCRCData.mnOffset) + <= (TASDEVICE_PAGE_REG( + TAS2781_SA_COEFF_SWAP_REG) + + 4))) { + /*DSP swap command, bypass */ + continue; + } else + nCRCChkSum += + ti_crc8(crc8_lookup_table, &nBuf1[i], + 1, 0); + } + + nResult = nCRCChkSum; + } + } + +end: + return nResult; +} + +static int tasdevice_load_block(struct tasdevice_priv *tas_dev, + struct TBlock *pBlock) +{ + int nResult = 0; + unsigned int nCommand = 0; + unsigned char nBook = 0; + unsigned char nPage = 0; + unsigned char nOffset = 0; + unsigned char nData = 0; + unsigned int nLength = 0; + unsigned int nSleep = 0; + unsigned char nCRCChkSum = 0; + unsigned int nValue = 0; + int nRetry = 6; + unsigned char *pData = pBlock->mpData; + int chn = 0, chnend = 0; + + dev_info(tas_dev->dev, + "TAS2781 load block: Type = %d, commands = %d\n", + pBlock->mnType, pBlock->mnCommands); + switch (pBlock->mnType) { + case MAIN_ALL_DEVICES: + chn = 0; + chnend = tas_dev->ndev; + break; + case MAIN_DEVICE_A: + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + chn = 0; + chnend = 1; + break; + case MAIN_DEVICE_B: + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + chn = 1; + chnend = 2; + break; + case MAIN_DEVICE_C: + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + chn = 2; + chnend = 3; + break; + case MAIN_DEVICE_D: + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + chn = 3; + chnend = 4; + break; + default: + dev_info(tas_dev->dev, + "TAS2781 load block: Other Type = 0x%02x\n", + pBlock->mnType); + break; + } + + for (; chn < chnend; chn++) { + if (tas_dev->tasdevice[chn].bLoading == false) + continue; +start: + if (pBlock->mbPChkSumPresent) { + nResult = tas_dev->write(tas_dev, chn, + TASDEVICE_I2CChecksum, 0); + if (nResult < 0) + goto end; + } + + if (pBlock->mbYChkSumPresent) + nCRCChkSum = 0; + + nCommand = 0; + + while (nCommand < pBlock->mnCommands) { + pData = pBlock->mpData + nCommand * 4; + + nBook = pData[0]; + nPage = pData[1]; + nOffset = pData[2]; + nData = pData[3]; + + nCommand++; + + if (nOffset <= 0x7F) { + nResult = tas_dev->write(tas_dev, chn, + TASDEVICE_REG(nBook, nPage, nOffset), + nData); + if (nResult < 0) + goto end; + if (pBlock->mbYChkSumPresent) { + nResult = doSingleRegCheckSum(tas_dev, + chn, nBook, nPage, nOffset, + nData); + if (nResult < 0) + goto check; + nCRCChkSum += (unsigned char)nResult; + } + } else if (nOffset == 0x81) { + nSleep = (nBook << 8) + nPage; + msleep(nSleep); + } else if (nOffset == 0x85) { + pData += 4; + nLength = (nBook << 8) + nPage; + nBook = pData[0]; + nPage = pData[1]; + nOffset = pData[2]; + if (nLength > 1) { + nResult = tas_dev->bulk_write(tas_dev, + chn, TASDEVICE_REG(nBook, + nPage, nOffset), pData + 3, + nLength); + if (nResult < 0) + goto end; + if (pBlock->mbYChkSumPresent) { + nResult = doMultiRegCheckSum( + tas_dev, chn, nBook, + nPage, nOffset, + nLength); + if (nResult < 0) + goto check; + nCRCChkSum += + (unsigned char)nResult; + } + } else { + nResult = tas_dev->write(tas_dev, chn, + TASDEVICE_REG(nBook, nPage, + nOffset), + pData[3]); + if (nResult < 0) + goto end; + if (pBlock->mbYChkSumPresent) { + nResult = doSingleRegCheckSum( + tas_dev, chn, nBook, + nPage, nOffset, + pData[3]); + if (nResult < 0) + goto check; + nCRCChkSum += + (unsigned char)nResult; + } + } + + nCommand++; + if (nLength >= 2) + nCommand += ((nLength - 2) / 4) + 1; + } + } + if (pBlock->mbPChkSumPresent) { + nResult = tas_dev->read(tas_dev, chn, + TASDEVICE_I2CChecksum, &nValue); + if (nResult < 0) { + dev_err(tas_dev->dev, "%s: Channel %d\n", + __func__, chn); + goto check; + } + if ((nValue&0xff) != pBlock->mnPChkSum) { + dev_err(tas_dev->dev, + "Block PChkSum Channel %d Error: FW = 0x%x, Reg = 0x%x\n", + chn, pBlock->mnPChkSum, (nValue&0xff)); + nResult = -EAGAIN; + tas_dev->tasdevice[chn].mnErrCode |= + ERROR_PRAM_CRCCHK; + goto check; + } + nResult = 0; + tas_dev->tasdevice[chn].mnErrCode &= + ~ERROR_PRAM_CRCCHK; + dev_info(tas_dev->dev, "Block[0x%02x] PChkSum match\n", + pBlock->mnType); + } + + if (pBlock->mbYChkSumPresent) { + //TBD, open it when FW ready + dev_err(tas_dev->dev, "Block YChkSum: FW = 0x%x, YCRC = 0x%x\n", + pBlock->mnYChkSum, + nCRCChkSum); + + tas_dev->tasdevice[chn].mnErrCode &= + ~ERROR_YRAM_CRCCHK; + nResult = 0; + dev_info(tas_dev->dev, + "Block[0x%x] YChkSum match\n", pBlock->mnType); + } +check: + if (nResult == -EAGAIN) { + nRetry--; + if (nRetry > 0) + goto start; + else { + if ((pBlock->mnType == MAIN_ALL_DEVICES) + || (pBlock->mnType == MAIN_DEVICE_A) + || (pBlock->mnType == MAIN_DEVICE_B) + || (pBlock->mnType == MAIN_DEVICE_C) + || (pBlock->mnType == MAIN_DEVICE_D)) { + tas_dev->tasdevice[chn].mnCurrentProgram + = -1; + } else { + tas_dev->tasdevice[chn].mnCurrentConfiguration + = -1; + } + nRetry = 6; + } + } + } +end: + if (nResult < 0) { + dev_err(tas_dev->dev, "Block (%d) load error\n", + pBlock->mnType); + } + return nResult; +} + + +static int tasdevice_load_data(struct tasdevice_priv *tas_dev, + struct TData *pData) +{ + int nResult = 0; + unsigned int nBlock = 0; + struct TBlock *pBlock = NULL; + + dev_info(tas_dev->dev, "%s: TAS2781 load data: %s, Blocks = %d\n", + __func__, + pData->mpName, pData->mnBlocks); + + for (nBlock = 0; nBlock < pData->mnBlocks; nBlock++) { + pBlock = &(pData->mpBlocks[nBlock]); + nResult = tas_dev->tasdevice_load_block(tas_dev, pBlock); + if (nResult < 0) + break; + } + + return nResult; +} + +static int tasdevice_load_calibrated_data( + struct tasdevice_priv *tas_dev, struct TData *pData) +{ + int nResult = 0; + unsigned int nBlock = 0; + struct TBlock *pBlock = NULL; + + dev_info(tas_dev->dev, "%s: TAS2781 load data: %s, Blocks = %d\n", + __func__, + pData->mpName, pData->mnBlocks); + + for (nBlock = 0; nBlock < pData->mnBlocks; nBlock++) { + pBlock = &(pData->mpBlocks[nBlock]); + nResult = tasdevice_load_block(tas_dev, pBlock); + if (nResult < 0) + break; + } + + return nResult; +} + +int tas2781_load_calibration(void *pContext, + char *pFileName, enum channel i) +{ + int ret = 0, nSize = 0, offset = 0; + loff_t pos = 0; + struct file *filp = NULL; + struct firmware FW; + const struct firmware *fw_entry = NULL; + char *data = NULL; + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *)pContext; + struct Ttasdevice *pTasdev = &(tas_dev->tasdevice[i]); + struct TFirmware *mpCalFirmware = NULL; + char pHint[256]; + + ret = request_firmware(&fw_entry, pFileName, tas_dev->dev); + if (!ret) { + if (!fw_entry->size) { + dev_err(tas_dev->dev, + "%s: file read error: size = %d\n", + __func__, fw_entry->size); + goto out; + } + FW.size = fw_entry->size; + FW.data = fw_entry->data; + dev_info(tas_dev->dev, + "%s: file = %s, file size %zd\n", + __func__, pFileName, fw_entry->size); + } else { + dev_info(tas_dev->dev, + "%s: Request firmware failed, try flip_open()\n", + __func__); + + scnprintf(pHint, sizeof(pHint), "%s%s\n", + TAS2781_CAL_BIN_PATH, pFileName); + filp = filp_open(pHint, O_RDONLY, 664); + if (!IS_ERR_OR_NULL(filp)) { + FW.size = i_size_read(file_inode(filp)); + dev_info(tas_dev->dev, + "%s: file = %s, file size %ld\n", + __func__, pHint, (long)FW.size); + data = kmalloc(FW.size, GFP_KERNEL); + if (data == NULL) { + dev_err(tas_dev->dev, "%s: malloc error\n", + __func__); + goto out; + } + nSize = (int)kernel_read(filp, data, FW.size, &pos); + if (!nSize) { + dev_err(tas_dev->dev, + "%s: file read error: size = %d\n", + __func__, nSize); + goto out; + } + dev_info(tas_dev->dev, "read filed nSize = %d\n", + nSize); + FW.data = data; + } else { + dev_err(tas_dev->dev, + "%s: cannot open calibration file: %s\n", + __func__, pHint); + goto out; + } + } + + mpCalFirmware = pTasdev->mpCalFirmware = kcalloc(1, + sizeof(struct TFirmware), GFP_KERNEL); + if (pTasdev->mpCalFirmware == NULL) { + dev_err(tas_dev->dev, "%s: FW memory failed!\n", __func__); + ret = -1; + goto out; + } + mpCalFirmware->dev = tas_dev->dev; + offset = fw_parse_header(tas_dev, mpCalFirmware, &FW, offset); + if (offset == -1) { + dev_err(tas_dev->dev, "%s: fw_parse_header EXIT!\n", __func__); + goto out; + } + offset = fw_parse_variable_header_cal(tas_dev, mpCalFirmware, &FW, + offset); + if (offset == -1) { + dev_err(tas_dev->dev, + "%s: fw_parse_variable_header_cal EXIT!\n", __func__); + goto out; + } + offset = fw_parse_program_data(tas_dev, mpCalFirmware, &FW, offset); + if (offset == -1) { + dev_err(tas_dev->dev, "%s: fw_parse_program_data EXIT!\n", + __func__); + goto out; + } + offset = fw_parse_configuration_data(tas_dev, mpCalFirmware, &FW, + offset); + if (offset == -1) { + dev_err(tas_dev->dev, + "%s: fw_parse_configuration_data EXIT!\n", __func__); + goto out; + } + offset = fw_parse_calibration_data(tas_dev, + mpCalFirmware, &FW, offset); + if (offset == -1) { + dev_err(tas_dev->dev, "%s: fw_parse_calibration_data EXIT!\n", + __func__); + goto out; + } + pTasdev->mbCalibrationLoaded = true; +out: + if (!IS_ERR_OR_NULL(filp)) { + filp_close(filp, NULL); + kfree(data); + } + if (fw_entry) { + release_firmware(fw_entry); + fw_entry = NULL; + } + return ret; +} + +int tasdevice_dspfw_ready(const void *pVoid, void *pContext) +{ + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext; + const struct firmware *pFW = (const struct firmware *)pVoid; + struct TFirmware *pFirmware = NULL; + struct tasdevice_fw_fixed_hdr *pFw_fixed_hdr = NULL; + int offset = 0, ret = 0; + + if (!pFW || !pFW->data) { + dev_err(tas_dev->dev, "%s: Failed to read firmware %s\n", + __func__, tas_dev->dsp_binaryname); + ret = -1; + goto out; + } + + tas_dev->mpFirmware = kcalloc(1, + sizeof(struct TFirmware), GFP_KERNEL); + if (tas_dev->mpFirmware == NULL) { + ret = -1; + goto out; + } + pFirmware = tas_dev->mpFirmware; + pFirmware->dev = tas_dev->dev; + offset = fw_parse_header(tas_dev, pFirmware, pFW, offset); + + if (offset == -1) + goto out; + pFw_fixed_hdr = &(pFirmware->fw_hdr.mnFixedHdr); + switch (pFw_fixed_hdr->mnDriverVersion) { + case 0x301: + case 0x302: + case 0x502: + tas_dev->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_dev->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_dev->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_dev->tasdevice_load_block = + tasdevice_load_block_kernel; + pFirmware->bKernelFormat = true; + break; + case 0x202: + case 0x400: + tas_dev->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_dev->fw_parse_program_data = + fw_parse_program_data; + tas_dev->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_dev->tasdevice_load_block = + tasdevice_load_block; + pFirmware->bKernelFormat = false; + break; + default: + if (pFw_fixed_hdr->mnDriverVersion == 0x100) { + if (pFw_fixed_hdr->mnPPCVersion >= PPC3_VERSION) { + tas_dev->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_dev->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_dev->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_dev->tasdevice_load_block = + tasdevice_load_block_kernel; + tas_dev->fw_parse_calibration_data = NULL; + } else { + switch (pFw_fixed_hdr->mnPPCVersion) { + case 0x00: + tas_dev->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_dev->fw_parse_program_data = + fw_parse_program_data; + tas_dev->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_dev->fw_parse_calibration_data = + fw_parse_calibration_data; + tas_dev->tasdevice_load_block = + tasdevice_load_block; + break; + default: + dev_err(tas_dev->dev, + "%s: PPCVersion must be 0x0 or 0x%02x Current:0x%02x\n", + __func__, PPC3_VERSION, + pFw_fixed_hdr->mnPPCVersion); + offset = -1; + break; + } + } + } else { + dev_err(tas_dev->dev, + "%s: DriverVersion must be 0x0, 0x230 or above 0x230:0x%02x\n", + __func__, + pFw_fixed_hdr->mnDriverVersion); + offset = -1; + } + break; + } + + offset = tas_dev->fw_parse_variable_header(tas_dev, pFW, offset); + if (offset == -1) + goto out; + + offset = tas_dev->fw_parse_program_data(tas_dev, pFirmware, pFW, + offset); + if (offset < 0) { + ret = -1; + goto out; + } + offset = tas_dev->fw_parse_configuration_data(tas_dev, + pFirmware, pFW, offset); + if (offset < 0) + ret = -1; + +out: + return ret; +} + +void tasdevice_calbin_remove(void *pContext) +{ + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext; + struct Ttasdevice *pTasdev = NULL; + int i = 0; + + if (tas_dev) { + for (i = 0; i < tas_dev->ndev; i++) { + pTasdev = &(tas_dev->tasdevice[i]); + if (pTasdev->mpCalFirmware) { + tas2781_clear_Calfirmware( + pTasdev->mpCalFirmware); + pTasdev->mpCalFirmware = NULL; + } + } + } +} + +void tasdevice_dsp_remove(void *pContext) +{ + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext; + int i = 0; + + if (tas_dev) { + if (tas_dev->mpFirmware) { + struct TFirmware *pFirmware = tas_dev->mpFirmware; + + if (pFirmware->mpPrograms) { + struct TProgram *pProgram; + + for (i = 0; i < pFirmware->mnPrograms; i++) { + pProgram = &(pFirmware->mpPrograms[i]); + if (pProgram) { + struct TData *pImageData = + &(pProgram->mData); + + if (pImageData->mpBlocks) { + struct TBlock *pBlock; + unsigned int nBlock; + + for (nBlock = 0; + nBlock < + pImageData->mnBlocks; + nBlock++) { + pBlock = + &(pImageData->mpBlocks[nBlock]); + kfree(pBlock->mpData); + } + kfree(pImageData->mpBlocks); + } + kfree(pProgram->mpDescription); + } + } + kfree(pFirmware->mpPrograms); + } + + if (pFirmware->mpConfigurations) { + struct TConfiguration *pConfig; + + for (i = 0; i < pFirmware->mnConfigurations; + i++) { + pConfig = + &(pFirmware->mpConfigurations[i]); + if (pConfig) { + struct TData *pImageData = + &(pConfig->mData); + + if (pImageData->mpBlocks) { + struct TBlock *pBlock; + unsigned int nBlock; + + for (nBlock = 0; + nBlock < + pImageData->mnBlocks; + nBlock++) { + pBlock = + &(pImageData->mpBlocks[nBlock]); + kfree(pBlock->mpData); + } + kfree(pImageData->mpBlocks); + } + kfree(pConfig->mpDescription); + } + } + kfree(pFirmware->mpConfigurations); + } + kfree(pFirmware->fw_hdr.mpDescription); + kfree(pFirmware); + tas_dev->mpFirmware = NULL; + } + } +} + +void tasdevice_select_tuningprm_cfg(void *pContext, int prm_no, + int cfg_no, int regbin_conf_no) +{ + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext; + struct tasdevice_regbin *regbin = &(tas_dev->mtRegbin); + struct tasdevice_config_info **cfg_info = regbin->cfg_info; + struct TFirmware *pFirmware = tas_dev->mpFirmware; + struct TConfiguration *pConfigurations = NULL; + struct TProgram *pProgram = NULL; + int i = 0; + int status = 0; + + if (pFirmware == NULL) { + dev_err(tas_dev->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (cfg_no >= pFirmware->mnConfigurations) { + dev_err(tas_dev->dev, + "%s: cfg(%d) is not in range of conf %u\n", + __func__, cfg_no, pFirmware->mnConfigurations); + goto out; + } + + if (prm_no >= pFirmware->mnPrograms || prm_no == 1) { + dev_err(tas_dev->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, pFirmware->mnPrograms); + goto out; + } + + if (regbin_conf_no > regbin->ncfgs || regbin_conf_no < 0 || + cfg_info == NULL) { + dev_err(tas_dev->dev, + "conf_no:%d should be in range from 0 to %u\n", + regbin_conf_no, regbin->ncfgs-1); + goto out; + } else { + dev_info(tas_dev->dev, "%s: regbin_profile_conf_id = %d\n", + __func__, regbin_conf_no); + } + + tas_dev->mnCurrentConfiguration = cfg_no; + tas_dev->mnCurrentProgram = prm_no; + + pConfigurations = &(pFirmware->mpConfigurations[cfg_no]); + for (i = 0; i < tas_dev->ndev; i++) { + if (cfg_info[regbin_conf_no]->active_dev & (1 << i)) { + if (tas_dev->tasdevice[i].mnCurrentProgram != prm_no) { + tas_dev->tasdevice[i].mnCurrentConfiguration + = -1; + tas_dev->tasdevice[i].bLoading = true; + status++; + } + } else + tas_dev->tasdevice[i].bLoading = false; + tas_dev->tasdevice[i].bLoaderr = false; + } + + if (status) { + pProgram = &(pFirmware->mpPrograms[prm_no]); + tasdevice_load_data(tas_dev, &(pProgram->mData)); + for (i = 0; i < tas_dev->ndev; i++) { + if (tas_dev->tasdevice[i].bLoaderr == true) + continue; + else if (tas_dev->tasdevice[i].bLoaderr == false + && tas_dev->tasdevice[i].bLoading == true) { + struct TFirmware *pCalFirmware = + tas_dev->tasdevice[i].mpCalFirmware; + + if (pCalFirmware) { + struct TCalibration *pCalibration = + pCalFirmware->mpCalibrations; + + if (pCalibration) + tasdevice_load_calibrated_data( + tas_dev, + &(pCalibration->mData)); + } + tas_dev->tasdevice[i].mnCurrentProgram + = prm_no; + } + } + } + + if (tas_dev->mbCalibrationLoaded == false) { + for (i = 0; i < tas_dev->ndev; i++) + tas_dev->set_calibration(tas_dev, i, 0x100); + tas_dev->mbCalibrationLoaded = true; + /* No wise to reload calibrationdata everytime, + * this code will work once even if calibrated + * data still failed to be got + */ + } + + status = 0; + for (i = 0; i < tas_dev->ndev; i++) { + dev_info(tas_dev->dev, "%s,fun %d,%d,%d\n", __func__, + tas_dev->tasdevice[i].mnCurrentConfiguration, + cfg_info[regbin_conf_no]->active_dev, + tas_dev->tasdevice[i].bLoaderr); + if (tas_dev->tasdevice[i].mnCurrentConfiguration != cfg_no + && (cfg_info[regbin_conf_no]->active_dev & (1 << i)) + && (tas_dev->tasdevice[i].bLoaderr == false)) { + status++; + tas_dev->tasdevice[i].bLoading = true; + } else + tas_dev->tasdevice[i].bLoading = false; + } + + if (status) { + status = 0; + tasdevice_load_data(tas_dev, &(pConfigurations->mData)); + for (i = 0; i < tas_dev->ndev; i++) { + if (tas_dev->tasdevice[i].bLoaderr == true) { + status |= 1 << (i + 4); + continue; + } else if (tas_dev->tasdevice[i].bLoaderr == false + && tas_dev->tasdevice[i].bLoading == true) { + tas_dev->tasdevice[i].mnCurrentConfiguration + = cfg_no; + tas_dev->tasdevice[i].bDSPBypass = false; + } + } + } else { + dev_err(tas_dev->dev, + "%s: No device is in active in conf %d\n", + __func__, regbin_conf_no); + } + + status |= cfg_info[regbin_conf_no]->active_dev; + dev_info(tas_dev->dev, "%s: DSP mode: load status is %08x\n", + __func__, status); +out: + return; +} + +int tas2781_set_calibration(void *pContext, enum channel i, + int nCalibration) +{ + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext; + int nResult = 0; + struct Ttasdevice *pTasdev = &(tas_dev->tasdevice[i]); + struct TFirmware *pCalFirmware = pTasdev->mpCalFirmware; + + if ((!tas_dev->mpFirmware->mpPrograms) + || (!tas_dev->mpFirmware->mpConfigurations)) { + dev_err(tas_dev->dev, "%s, Firmware not loaded\n\r", __func__); + nResult = 0; + goto out; + } + + if (nCalibration == 0xFF || (nCalibration == 0x100 + && pTasdev->mbCalibrationLoaded == false)) { + if (pCalFirmware) { + pTasdev->mbCalibrationLoaded = false; + tas2781_clear_Calfirmware(pCalFirmware); + pCalFirmware = NULL; + } + + scnprintf(tas_dev->cal_binaryname[i], 64, "%s_cal_0x%02x.bin", + tas_dev->dev_name, tas_dev->tasdevice[i].mnDevAddr); + nResult = tas2781_load_calibration(tas_dev, + tas_dev->cal_binaryname[i], i); + if (nResult != 0) { + dev_err(tas_dev->dev, + "%s: load %s error, no-side effect for playback\n", + __func__, tas_dev->cal_binaryname[i]); + nResult = 0; + } + } + pTasdev->bLoading = true; + pTasdev->bLoaderr = false; + + if (pCalFirmware) { + struct TCalibration *pCalibration = + pCalFirmware->mpCalibrations; + + if (pCalibration) + tasdevice_load_calibrated_data(tas_dev, + &(pCalibration->mData)); + } else + dev_err(tas_dev->dev, + "%s: No calibrated data for device %d\n", __func__, i); + +out: + return nResult; +} diff --git a/sound/soc/codecs/tas2781-dsp.h b/sound/soc/codecs/tas2781-dsp.h new file mode 100644 index 000000000..f7b689ad6 --- /dev/null +++ b/sound/soc/codecs/tas2781-dsp.h @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier + * + * Copyright (C) 2022 - 2023 Texas Instruments Incorporated + * https://www.ti.com + * + * The TAS2781 driver implements a flexible and configurable register setting + * for one, two, even multiple TAS2781 chips. All the register setting are in + * a bin file. Almost no specific register setting can be found in the code. + * + * Author: Shenghao Ding shenghao-ding@ti.com + * Kevin Lu kevin-lu@ti.com + */ + +#ifndef __TASDEVICE_DSP_H__ +#define __TASDEVICE_DSP_H__ + +#define MAIN_ALL_DEVICES (0x0d) +#define MAIN_DEVICE_A (0x01) +#define MAIN_DEVICE_B (0x08) +#define MAIN_DEVICE_C (0x10) +#define MAIN_DEVICE_D (0x14) +#define COEFF_DEVICE_A (0x03) +#define COEFF_DEVICE_B (0x0a) +#define COEFF_DEVICE_C (0x11) +#define COEFF_DEVICE_D (0x15) +#define PRE_DEVICE_A (0x04) +#define PRE_DEVICE_B (0x0b) +#define PRE_DEVICE_C (0x12) +#define PRE_DEVICE_D (0x16) + +#define PPC3_VERSION 0x4100 +#define REGBIN_CONFIGID_BYPASS_ALL (0) +#define TASDEVICE_DEVICE_SUM (8) +#define TASDEVICE_CONFIG_SUM (64) + +enum channel { + TopLeftChn = 0x00, + TopRightChn = 0x01, + BottomLeftChn = 0x02, + BottomRightChn = 0x03, + MaxChn, +}; + +enum tasdevice_dsp_dev_idx { + TASDEVICE_DSP_TAS_2555 = 0, + TASDEVICE_DSP_TAS_2555_STEREO, + TASDEVICE_DSP_TAS_2557_MONO, + TASDEVICE_DSP_TAS_2557_DUAL_MONO, + TASDEVICE_DSP_TAS_2559, + TASDEVICE_DSP_TAS_2563, + TASDEVICE_DSP_TAS_2563_DUAL_MONO = 7, + TASDEVICE_DSP_TAS_2563_QUAD, + TASDEVICE_DSP_TAS_2563_21, + TASDEVICE_DSP_TAS_2781, + TASDEVICE_DSP_TAS_2781_DUAL_MONO, + TASDEVICE_DSP_TAS_2781_21, + TASDEVICE_DSP_TAS_2781_QUAD, + TASDEVICE_DSP_TAS_MAX_DEVICE +}; + +struct tasdevice_fw_fixed_hdr { + unsigned int mnMagicNumber; + unsigned int mnFWSize; + unsigned int mnChecksum; + unsigned int mnPPCVersion; + unsigned int mnFWVersion; + unsigned int mnDriverVersion; + unsigned int mnTimeStamp; + char mpDDCName[64]; +}; + +struct tasdevice_dspfw_hdr { + struct tasdevice_fw_fixed_hdr mnFixedHdr; + unsigned int mnBinFileDocVer; + char *mpDescription; + unsigned short mnDeviceFamily; + unsigned short mnDevice; + unsigned char ndev; +}; + +struct TBlock { + unsigned int mnType; + unsigned char mbPChkSumPresent; + unsigned char mnPChkSum; + unsigned char mbYChkSumPresent; + unsigned char mnYChkSum; + unsigned int mnCommands; + unsigned int blk_size; + unsigned int nSublocks; + unsigned char *mpData; +}; + +struct TData { + char mpName[64]; + char *mpDescription; + unsigned int mnBlocks; + struct TBlock *mpBlocks; +}; +struct TProgram { + unsigned int prog_size; + char mpName[64]; + char *mpDescription; + unsigned char mnAppMode; + unsigned char mnPDMI2SMode; + unsigned char mnISnsPD; + unsigned char mnVSnsPD; + unsigned char mnPowerLDG; + struct TData mData; +}; + +struct TConfiguration { + unsigned int cfg_size; + char mpName[64]; + char *mpDescription; + unsigned char mnDevice_orientation; + unsigned char mnDevices; + unsigned int mProgram; + unsigned int mnSamplingRate; + unsigned short mnPLLSrc; + unsigned int mnPLLSrcRate; + unsigned int mnFsRate; + struct TData mData; +}; + +struct TCalibration { + char mpName[64]; + char *mpDescription; + unsigned int mnProgram; + unsigned int mnConfiguration; + struct TData mData; +}; + +struct TFirmware { + struct tasdevice_dspfw_hdr fw_hdr; + unsigned int prog_start_offset; + unsigned short mnPrograms; + struct TProgram *mpPrograms; + unsigned int cfg_start_offset; + unsigned short mnConfigurations; + struct TConfiguration *mpConfigurations; + unsigned short mnCalibrations; + struct TCalibration *mpCalibrations; + bool bKernelFormat; + struct device *dev; +}; + +enum tasdevice_dsp_fw_state { + TASDEVICE_DSP_FW_NONE = 0, + TASDEVICE_DSP_FW_PENDING, + TASDEVICE_DSP_FW_FAIL, + TASDEVICE_DSP_FW_ALL_OK, +}; + +enum tasdevice_bin_blk_type { + TASDEVICE_BIN_BLK_COEFF = 1, + TASDEVICE_BIN_BLK_POST_POWER_UP, + TASDEVICE_BIN_BLK_PRE_SHUTDOWN, + TASDEVICE_BIN_BLK_PRE_POWER_UP, + TASDEVICE_BIN_BLK_POST_SHUTDOWN +}; + +struct tasdevice_regbin_hdr { + unsigned int img_sz; + unsigned int checksum; + unsigned int binary_version_num; + unsigned int drv_fw_version; + unsigned int timestamp; + unsigned char plat_type; + unsigned char dev_family; + unsigned char reserve; + unsigned char ndev; + unsigned char devs[TASDEVICE_DEVICE_SUM]; + unsigned int nconfig; + unsigned int config_size[TASDEVICE_CONFIG_SUM]; +}; + +struct tasdevice_block_data { + unsigned char dev_idx; + unsigned char block_type; + unsigned short yram_checksum; + unsigned int block_size; + unsigned int nSublocks; + unsigned char *regdata; +}; + +struct tasdevice_config_info { + char mpName[64]; + unsigned int nblocks; + unsigned int real_nblocks; + unsigned char active_dev; + struct tasdevice_block_data **blk_data; +}; + +struct tasdevice_regbin { + struct tasdevice_regbin_hdr fw_hdr; + int ncfgs; + struct tasdevice_config_info **cfg_info; + int profile_cfg_id; +}; + +int tasdevice_dspfw_ready(const void *pVoid, void *pContext); +void tasdevice_dsp_remove(void *pContext); +void tasdevice_calbin_remove(void *pContext); +int tas2781_load_calibration(void *tas_dev, char *pFileName, + enum channel i); +int tas2781_set_calibration(void *pContext, enum channel i, + int nCalibration); +void tasdevice_select_tuningprm_cfg(void *pContext, int prm, + int cfg_no, int regbin_conf_no); + +#endif diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c new file mode 100644 index 000000000..3ee254777 --- /dev/null +++ b/sound/soc/codecs/tas2781-i2c.c @@ -0,0 +1,2143 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier + * + * Copyright (C) 2022 - 2023 Texas Instruments Incorporated + * https://www.ti.com + * + * The TAS2781 driver implements a flexible and configurable register setting + * for one, two, even multiple TAS2781 chips. All the register setting are in + * a bin file. Almost no specific register setting can be found in the code. + * + * Author: Shenghao Ding shenghao-ding@ti.com + * Kevin Lu kevin-lu@ti.com + */ + +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/firmware.h> +#include <linux/of_gpio.h> +#include <linux/interrupt.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> + +#include "tas2781.h" + +/* max. length of a alsa mixer control name */ +#define MAX_CONTROL_NAME (48) +#define TASDEVICE_CLK_DIR_IN (0) +#define TASDEVICE_CLK_DIR_OUT (1) + +#define TASDEVICE_IRQ_DET_TIMEOUT (30000) +#define TASDEVICE_IRQ_DET_CNT_LIMIT (500) +#define TAS2781_REG_INT_LTCH0 TASDEVICE_REG(0X0, 0x0, 0x49) +#define TAS2781_REG_INT_LTCH1 TASDEVICE_REG(0X0, 0x0, 0x4A) +#define TAS2781_REG_INT_LTCH1_0 TASDEVICE_REG(0X0, 0x0, 0x4B) +#define TAS2781_REG_INT_LTCH2 TASDEVICE_REG(0X0, 0x0, 0x4F) +#define TAS2781_REG_INT_LTCH3 TASDEVICE_REG(0X0, 0x0, 0x50) +#define TAS2781_REG_INT_LTCH4 TASDEVICE_REG(0X0, 0x0, 0x51) + +const char *blocktype[5] = { + "COEFF", + "POST_POWER_UP", + "PRE_SHUTDOWN", + "PRE_POWER_UP", + "POST_SHUTDOWN" +}; + +static const char * const dts_dev_addr_tag[] = { + "ti,topleft-channel", + "ti,topright-channel", + "ti,bottomleft-channel", + "ti,bottomright-channel" +}; + +static const char *dts_rst_tag = "ti,%s-gpio%d"; + +static const char *dts_glb_addr_tag = "ti,global-address"; + +static const struct i2c_device_id tasdevice_id[] = { + { "audev", GENERAL_AUDEV }, + { "tas2781", TAS2781 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tasdevice_id); + +static bool tasdevice_volatile(struct device *dev, unsigned int reg) +{ + return true; +} + +static bool tasdevice_writeable(struct device *dev, unsigned int reg) +{ + return true; +} + +static const struct regmap_config tasdevice_regmap = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = tasdevice_writeable, + .volatile_reg = tasdevice_volatile, + .cache_type = REGCACHE_FLAT, + .max_register = 1 * 128, +}; + +static const struct of_device_id tasdevice_of_match[] = { + { .compatible = "ti,audev" }, + { .compatible = "ti,tas2781" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, tasdevice_of_match); + +int tasdevice_process_block(void *pContext, + unsigned char *data, unsigned char dev_idx, int sublocksize) +{ + struct tasdevice_priv *tas_dev = + (struct tasdevice_priv *)pContext; + unsigned char subblk_typ = data[1]; + int subblk_offset = 2; + int chn = 0, chnend = 0; + int rc = 0; + int blktyp = dev_idx & 0xC0, idx = dev_idx & 0x3F; + bool bError = false; + + if (idx) { + chn = idx-1; + chnend = idx; + } else { + if (tas_dev->glb_addr.mnDevAddr) { + chn = tas_dev->ndev; + chnend = tas_dev->ndev + 1; + } else { + chn = 0; + chnend = tas_dev->ndev; + } + } + + for (; chn < chnend; chn++) { + if (tas_dev->glb_addr.mnDevAddr == 0 && + tas_dev->tasdevice[chn].bLoading == false) + continue; + + bError = false; + subblk_offset = 2; + switch (subblk_typ) { + case TASDEVICE_CMD_SING_W: { + int i = 0; + unsigned short len = SMS_HTONS(data[2], data[3]); + + subblk_offset += 2; + if (subblk_offset + 4 * len > sublocksize) { + dev_err(tas_dev->dev, + "process_block: Out of memory\n"); + bError = true; + break; + } + + for (i = 0; i < len; i++) { + rc = tasdevice_dev_write(tas_dev, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + data[subblk_offset + 3]); + if (rc < 0) { + bError = true; + dev_err(tas_dev->dev, + "process_block: single write error\n"); + } + subblk_offset += 4; + } + } + break; + case TASDEVICE_CMD_BURST: { + unsigned short len = SMS_HTONS(data[2], data[3]); + + subblk_offset += 2; + if (subblk_offset + 4 + len > sublocksize) { + dev_err(tas_dev->dev, + "process_block: BURST Out of memory\n"); + bError = true; + break; + } + if (len % 4) { + dev_err(tas_dev->dev, + "process_block: Burst len(%u) can be divided by 4\n", + len); + break; + } + + rc = tasdevice_dev_bulk_write(tas_dev, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + &(data[subblk_offset + 4]), len); + if (rc < 0) { + bError = true; + dev_err(tas_dev->dev, + "process_block: bulk_write error = %d\n", + rc); + } + subblk_offset += (len + 4); + } + break; + case TASDEVICE_CMD_DELAY: { + unsigned short delay_time = 0; + + if (subblk_offset + 2 > sublocksize) { + dev_err(tas_dev->dev, + "process_block: deley Out of memory\n"); + bError = true; + break; + } + delay_time = SMS_HTONS(data[2], data[3]); + usleep_range(delay_time*1000, delay_time*1000); + subblk_offset += 2; + } + break; + case TASDEVICE_CMD_FIELD_W: + if (subblk_offset + 6 > sublocksize) { + dev_err(tas_dev->dev, + "process_block: bit write Out of memory\n"); + bError = true; + break; + } + rc = tasdevice_dev_update_bits(tas_dev, chn, + 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) { + bError = true; + dev_err(tas_dev->dev, + "process_block: update_bits error = %d\n", rc); + } + subblk_offset += 6; + break; + default: + break; + }; + if (bError == true && blktyp != 0) { + if (blktyp == 0x80) { + tas_dev->tasdevice[chn].mnCurrentProgram = -1; + tas_dev->tasdevice[chn].mnCurrentConfiguration = + -1; + } else + tas_dev->tasdevice[chn].mnCurrentConfiguration = + -1; + } + } + return subblk_offset; +} + +void tasdevice_select_cfg_blk(void *pContext, int conf_no, + unsigned char block_type) +{ + struct tasdevice_priv *tas_dev = + (struct tasdevice_priv *) pContext; + struct tasdevice_regbin *regbin = &(tas_dev->mtRegbin); + struct tasdevice_config_info **cfg_info = regbin->cfg_info; + int j = 0, k = 0, chn = 0, chnend = 0; + + if (conf_no >= regbin->ncfgs || conf_no < 0 || NULL == cfg_info) { + dev_err(tas_dev->dev, + "conf_no should be not more than %u\n", + regbin->ncfgs); + goto out; + } else + dev_info(tas_dev->dev, + "select_cfg_blk: profile_conf_id = %d\n", + conf_no); + + 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_dev->dev, + "ERROR!!!block_type should be in range from 2 to 5\n"); + goto out; + } + if (block_type != cfg_info[conf_no]->blk_data[j]->block_type) + continue; + dev_info(tas_dev->dev, + "select_cfg_blk: conf %d, block type:%s\t device idx = 0x%02x\n", + conf_no, blocktype[cfg_info[conf_no]->blk_data[j] + ->block_type-1], cfg_info[conf_no]->blk_data[j] + ->dev_idx); + + for (k = 0; k < (int)cfg_info[conf_no]->blk_data[j] + ->nSublocks; k++) { + if (cfg_info[conf_no]->blk_data[j]->dev_idx) { + chn = + cfg_info[conf_no]->blk_data[j]->dev_idx + - 1; + chnend = + cfg_info[conf_no]->blk_data[j]->dev_idx; + } else { + chn = 0; + chnend = tas_dev->ndev; + } + for (; chn < chnend; chn++) + tas_dev->tasdevice[chn].bLoading = true; + + rc = tasdevice_process_block(tas_dev, + cfg_info[conf_no]->blk_data[j]->regdata + + length, + cfg_info[conf_no]->blk_data[j]->dev_idx, + cfg_info[conf_no]->blk_data[j]->block_size - + length); + length += rc; + if (cfg_info[conf_no]->blk_data[j]->block_size < + length) { + dev_err(tas_dev->dev, + "select_cfg_blk: ERROR:%u %u out of memory\n", + length, + cfg_info[conf_no]->blk_data[j]->block_size); + break; + } + } + if (length != cfg_info[conf_no]->blk_data[j]->block_size) + dev_err(tas_dev->dev, + "select_cfg_blk: ERROR: %u %u size is not same\n", + length, + cfg_info[conf_no]->blk_data[j]->block_size); + + } + +out: + return; +} + +static struct tasdevice_config_info *tasdevice_add_config( + void *pContext, unsigned char *config_data, + unsigned int config_size) +{ + struct tasdevice_priv *tas_dev = + (struct tasdevice_priv *)pContext; + struct tasdevice_config_info *cfg_info = NULL; + int config_offset = 0, i = 0; + + cfg_info = kzalloc( + sizeof(struct tasdevice_config_info), GFP_KERNEL); + if (!cfg_info) + goto out; + + if (tas_dev->mtRegbin.fw_hdr.binary_version_num >= 0x105) { + if (config_offset + 64 > (int)config_size) { + dev_err(tas_dev->dev, + "add config: Out of memory\n"); + goto out; + } + memcpy(cfg_info->mpName, &config_data[config_offset], 64); + config_offset += 64; + } + + if (config_offset + 4 > (int)config_size) { + dev_err(tas_dev->dev, + "add config: Out of memory\n"); + goto out; + } + cfg_info->nblocks = + SMS_HTONL(config_data[config_offset], + config_data[config_offset + 1], + config_data[config_offset + 2], config_data[config_offset + 3]); + config_offset += 4; + + cfg_info->blk_data = kcalloc( + cfg_info->nblocks, sizeof(struct tasdevice_block_data *), + GFP_KERNEL); + if (!cfg_info->blk_data) { + dev_err(tas_dev->dev, + "add config: blk_data alloc failed!\n"); + goto out; + } + cfg_info->real_nblocks = 0; + for (i = 0; i < (int)cfg_info->nblocks; i++) { + if (config_offset + 12 > config_size) { + dev_err(tas_dev->dev, + "add config: Out of memory: i = %d nblocks = %u!\n", + i, cfg_info->nblocks); + break; + } + cfg_info->blk_data[i] = kzalloc( + sizeof(struct tasdevice_block_data), GFP_KERNEL); + if (!cfg_info->blk_data[i]) + break; + + cfg_info->blk_data[i]->dev_idx = config_data[config_offset]; + config_offset++; + + cfg_info->blk_data[i]->block_type = config_data[config_offset]; + config_offset++; + + if (cfg_info->blk_data[i]->block_type == + TASDEVICE_BIN_BLK_PRE_POWER_UP) { + if (cfg_info->blk_data[i]->dev_idx == 0) { + cfg_info->active_dev = 1; + } else { + cfg_info->active_dev = + 1 << + (cfg_info->blk_data[i]->dev_idx - 1); + } + } + cfg_info->blk_data[i]->yram_checksum = + SMS_HTONS(config_data[config_offset], + config_data[config_offset + 1]); + config_offset += 2; + cfg_info->blk_data[i]->block_size = + SMS_HTONL(config_data[config_offset], + config_data[config_offset + 1], + config_data[config_offset + 2], + config_data[config_offset + 3]); + config_offset += 4; + + cfg_info->blk_data[i]->nSublocks = + SMS_HTONL(config_data[config_offset], + config_data[config_offset + 1], + config_data[config_offset + 2], + config_data[config_offset + 3]); + + config_offset += 4; + cfg_info->blk_data[i]->regdata = kzalloc( + cfg_info->blk_data[i]->block_size, GFP_KERNEL); + if (cfg_info->blk_data[i]->regdata == 0) { + dev_err(tas_dev->dev, + "add config: regdata alloc failed!\n"); + goto out; + } + if (config_offset + cfg_info->blk_data[i]->block_size + > config_size) { + dev_err(tas_dev->dev, + "add config: block_size Out of memory: i = %d nblocks = %u!\n", + i, + cfg_info->nblocks); + break; + } + memcpy(cfg_info->blk_data[i]->regdata, + &config_data[config_offset], + cfg_info->blk_data[i]->block_size); + config_offset += cfg_info->blk_data[i]->block_size; + cfg_info->real_nblocks += 1; + } +out: + return cfg_info; +} +static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_dev = + snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int val; + int ret = 0; + + /* Read the primary device as the whole */ + ret = tasdevice_dev_read(tas_dev, 0, mc->reg, &val); + if (ret) { + dev_err(tas_dev->dev, + "%s, get digital vol error\n", + __func__); + goto out; + } + val = (val > mc->max) ? mc->max : val; + val = mc->invert ? mc->max - val : val; + ucontrol->value.integer.value[0] = val; + +out: + return ret; +} + +static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_dev = + snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int val; + int i, ret = 0; + + val = ucontrol->value.integer.value[0]; + val = (val > mc->max) ? mc->max : val; + val = mc->invert ? mc->max - val : val; + val = (val < 0) ? 0 : val; + if (tas_dev->tas2781_set_global != NULL) { + ret = tasdevice_dev_write(tas_dev, tas_dev->ndev, + mc->reg, val); + if (ret) + dev_err(tas_dev->dev, + "%s, set digital vol error in global mode\n", + __func__); + } else { + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_write(tas_dev, i, + mc->reg, val); + if (ret) + dev_err(tas_dev->dev, + "%s, set digital vol error in device %d\n", + __func__, i); + } + } + + return ret; +} + +static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_dev = + snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int val; + unsigned char mask = 0; + int ret = 0; + + /* Read the primary device */ + ret = tasdevice_dev_read(tas_dev, 0, mc->reg, &val); + if (ret) { + dev_err(tas_dev->dev, + "%s, get AMP vol error\n", + __func__); + goto out; + } + + mask = (1 << fls(mc->max)) - 1; + mask <<= mc->shift; + val = (val & mask) >> mc->shift; + val = (val > mc->max) ? mc->max : val; + val = mc->invert ? mc->max - val : val; + ucontrol->value.integer.value[0] = val; + +out: + return ret; +} + +static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_dev = + snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int val; + int i, ret = 0; + unsigned char mask = 0; + + mask = (1 << fls(mc->max)) - 1; + mask <<= mc->shift; + val = ucontrol->value.integer.value[0]; + val = (val > mc->max) ? mc->max : val; + val = mc->invert ? mc->max - val : val; + val = (val < 0) ? 0 : val; + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_update_bits(tas_dev, i, + mc->reg, + mask, + val << mc->shift); + if (ret) + dev_err(tas_dev->dev, + "%s, set AMP vol error in device %d\n", + __func__, i); + } + + return ret; +} + +static const DECLARE_TLV_DB_SCALE(dvc_tlv, -10000, 100, 0); +static const DECLARE_TLV_DB_SCALE(amp_vol_tlv, 1100, 50, 0); + +static const struct snd_kcontrol_new tas2781_snd_controls[] = { + SOC_SINGLE_RANGE_EXT_TLV("Amp Gain Volume", TAS2781_AMP_LEVEL, + 1, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, amp_vol_tlv), + SOC_SINGLE_RANGE_EXT_TLV("Digital Volume Control", TAS2781_DVC_LVL, + 0, 0, 200, 1, tas2781_digital_getvol, + tas2781_digital_putvol, dvc_tlv), +}; + +static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *p_tasdevice = + snd_soc_component_get_drvdata(codec); + + /* Codec Lock Hold*/ + mutex_lock(&p_tasdevice->codec_lock); + p_tasdevice->mtRegbin.profile_cfg_id = + ucontrol->value.integer.value[0]; + /* Codec Lock Release*/ + mutex_unlock(&p_tasdevice->codec_lock); + + return 0; +} + +static int tasdevice_info_programs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *p_tasdevice = + snd_soc_component_get_drvdata(codec); + struct TFirmware *Tfw = p_tasdevice->mpFirmware; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + /* Codec Lock Hold*/ + mutex_lock(&p_tasdevice->codec_lock); + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)Tfw->mnPrograms; + /* Codec Lock Release*/ + mutex_unlock(&p_tasdevice->codec_lock); + return 0; +} + +static int tasdevice_info_configurations( + struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *p_tasdevice = + snd_soc_component_get_drvdata(codec); + struct TFirmware *Tfw = p_tasdevice->mpFirmware; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + /* Codec Lock Hold*/ + mutex_lock(&p_tasdevice->codec_lock); + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)Tfw->mnConfigurations - 1; + /* Codec Lock Release*/ + mutex_unlock(&p_tasdevice->codec_lock); + + return 0; +} + +static int tasdevice_info_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *p_tasdevice = + snd_soc_component_get_drvdata(codec); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + /* Codec Lock Hold*/ + mutex_lock(&p_tasdevice->codec_lock); + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = max(0, p_tasdevice->mtRegbin.ncfgs); + /* Codec Lock Release*/ + mutex_unlock(&p_tasdevice->codec_lock); + return 0; +} + +static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *p_tasdevice + = snd_soc_component_get_drvdata(codec); + + /* Codec Lock Hold*/ + mutex_lock(&p_tasdevice->codec_lock); + ucontrol->value.integer.value[0] = + p_tasdevice->mtRegbin.profile_cfg_id; + /* Codec Lock Release*/ + mutex_unlock(&p_tasdevice->codec_lock); + + return 0; +} + +static int tasdevice_create_controls(struct tasdevice_priv *tas_dev) +{ + int nr_controls = 1, ret = 0, mix_index = 0; + char *name = NULL; + struct snd_kcontrol_new *tasdevice_profile_controls = NULL; + + tasdevice_profile_controls = devm_kzalloc(tas_dev->dev, + nr_controls * sizeof(tasdevice_profile_controls[0]), + GFP_KERNEL); + if (tasdevice_profile_controls == NULL) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the active profile */ + name = devm_kzalloc(tas_dev->dev, + MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + scnprintf(name, MAX_CONTROL_NAME, "TASDEVICE Profile id"); + tasdevice_profile_controls[mix_index].name = name; + tasdevice_profile_controls[mix_index].iface = + SNDRV_CTL_ELEM_IFACE_MIXER; + tasdevice_profile_controls[mix_index].info = + tasdevice_info_profile; + tasdevice_profile_controls[mix_index].get = + tasdevice_get_profile_id; + tasdevice_profile_controls[mix_index].put = + tasdevice_set_profile_id; + mix_index++; + + ret = snd_soc_add_component_controls(tas_dev->codec, + tasdevice_profile_controls, + nr_controls < mix_index ? nr_controls : mix_index); + + tas_dev->tas_ctrl.nr_controls = + nr_controls < mix_index ? nr_controls : mix_index; +out: + return ret; +} + +static int tasdevice_program_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(pKcontrol); + struct tasdevice_priv *pTAS2781 = snd_soc_component_get_drvdata(codec); + + mutex_lock(&pTAS2781->codec_lock); + pValue->value.integer.value[0] = pTAS2781->mnCurrentProgram; + mutex_unlock(&pTAS2781->codec_lock); + return 0; +} + +static int tasdevice_program_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(pKcontrol); + struct tasdevice_priv *pTAS2781 = snd_soc_component_get_drvdata(codec); + unsigned int nProgram = pValue->value.integer.value[0]; + + mutex_lock(&pTAS2781->codec_lock); + pTAS2781->mnCurrentProgram = nProgram; + mutex_unlock(&pTAS2781->codec_lock); + return 0; +} + +static int tasdevice_configuration_get( + struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ + + struct snd_soc_component *codec + = snd_soc_kcontrol_component(pKcontrol); + struct tasdevice_priv *pTAS2781 = snd_soc_component_get_drvdata(codec); + + mutex_lock(&pTAS2781->codec_lock); + pValue->value.integer.value[0] = pTAS2781->mnCurrentConfiguration; + mutex_unlock(&pTAS2781->codec_lock); + return 0; +} + +static int tasdevice_configuration_put( + struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(pKcontrol); + struct tasdevice_priv *pTAS2781 = snd_soc_component_get_drvdata(codec); + unsigned int nConfiguration = pValue->value.integer.value[0]; + + mutex_lock(&pTAS2781->codec_lock); + pTAS2781->mnCurrentConfiguration = nConfiguration; + mutex_unlock(&pTAS2781->codec_lock); + return 0; +} + +static int tasdevice_dsp_create_control( + struct tasdevice_priv *tas_dev) +{ + int nr_controls = 2, ret = 0, mix_index = 0; + char *program_name = NULL; + char *configuration_name = NULL; + struct snd_kcontrol_new *tasdevice_dsp_controls = NULL; + + tasdevice_dsp_controls = devm_kzalloc(tas_dev->dev, + nr_controls * sizeof(tasdevice_dsp_controls[0]), + GFP_KERNEL); + if (tasdevice_dsp_controls == NULL) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the active profile */ + program_name = devm_kzalloc(tas_dev->dev, + MAX_CONTROL_NAME, GFP_KERNEL); + configuration_name = devm_kzalloc(tas_dev->dev, + MAX_CONTROL_NAME, GFP_KERNEL); + if (!program_name || !configuration_name) { + ret = -ENOMEM; + goto out; + } + + scnprintf(program_name, MAX_CONTROL_NAME, "Program"); + tasdevice_dsp_controls[mix_index].name = program_name; + tasdevice_dsp_controls[mix_index].iface = + SNDRV_CTL_ELEM_IFACE_MIXER; + tasdevice_dsp_controls[mix_index].info = + tasdevice_info_programs; + tasdevice_dsp_controls[mix_index].get = + tasdevice_program_get; + tasdevice_dsp_controls[mix_index].put = + tasdevice_program_put; + mix_index++; + + scnprintf(configuration_name, MAX_CONTROL_NAME, "Configuration"); + tasdevice_dsp_controls[mix_index].name = configuration_name; + tasdevice_dsp_controls[mix_index].iface = + SNDRV_CTL_ELEM_IFACE_MIXER; + tasdevice_dsp_controls[mix_index].info = + tasdevice_info_configurations; + tasdevice_dsp_controls[mix_index].get = + tasdevice_configuration_get; + tasdevice_dsp_controls[mix_index].put = + tasdevice_configuration_put; + mix_index++; + + ret = snd_soc_add_component_controls(tas_dev->codec, + tasdevice_dsp_controls, + nr_controls < mix_index ? nr_controls : mix_index); + + tas_dev->tas_ctrl.nr_controls += nr_controls; +out: + return ret; +} + +void tasdevice_regbin_ready(const struct firmware *pFW, + void *pContext) +{ + struct tasdevice_priv *tas_dev = + (struct tasdevice_priv *) pContext; + struct tasdevice_regbin *regbin = NULL; + struct tasdevice_regbin_hdr *fw_hdr = NULL; + struct tasdevice_config_info **cfg_info = NULL; + const struct firmware *fw_entry = NULL; + unsigned char *buf = NULL; + int offset = 0, i = 0; + unsigned int total_config_sz = 0; + int ret = 0; + + if (tas_dev == NULL) { + dev_err(tas_dev->dev, + "tasdev: regbin_ready: handle is NULL\n"); + return; + } + mutex_lock(&tas_dev->codec_lock); + regbin = &(tas_dev->mtRegbin); + fw_hdr = &(regbin->fw_hdr); + if (unlikely(!pFW) || unlikely(!pFW->data)) { + dev_err(tas_dev->dev, + "Failed to read %s, no side - effect on driver running\n", + tas_dev->regbin_binaryname); + ret = -1; + goto out; + } + buf = (unsigned char *)pFW->data; + + dev_info(tas_dev->dev, "tasdev: regbin_ready start\n"); + fw_hdr->img_sz = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + offset += 4; + if (fw_hdr->img_sz != pFW->size) { + dev_err(tas_dev->dev, + "File size not match, %d %u", (int)pFW->size, + fw_hdr->img_sz); + ret = -1; + goto out; + } + + fw_hdr->checksum = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + offset += 4; + fw_hdr->binary_version_num = SMS_HTONL(buf[offset], + buf[offset + 1], buf[offset + 2], buf[offset + 3]); + if (fw_hdr->binary_version_num < 0x103) { + dev_err(tas_dev->dev, + "File version 0x%04x is too low", + fw_hdr->binary_version_num); + ret = -1; + goto out; + } + offset += 4; + fw_hdr->drv_fw_version = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + offset += 4; + fw_hdr->timestamp = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + offset += 4; + fw_hdr->plat_type = buf[offset]; + offset += 1; + fw_hdr->dev_family = buf[offset]; + offset += 1; + fw_hdr->reserve = buf[offset]; + offset += 1; + fw_hdr->ndev = buf[offset]; + offset += 1; + if (fw_hdr->ndev != tas_dev->ndev) { + dev_err(tas_dev->dev, + "ndev(%u) from Regbin and ndev(%u) from DTS does not match\n", + fw_hdr->ndev, + tas_dev->ndev); + ret = -1; + goto out; + } + if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) { + dev_err(tas_dev->dev, + "regbin_ready: Out of Memory!\n"); + ret = -1; + goto out; + } + + for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++) + fw_hdr->devs[i] = buf[offset]; + + fw_hdr->nconfig = SMS_HTONL(buf[offset], buf[offset + 1], + buf[offset + 2], buf[offset + 3]); + offset += 4; + dev_info(tas_dev->dev, "nconfig = %u\n", fw_hdr->nconfig); + for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) { + fw_hdr->config_size[i] = SMS_HTONL(buf[offset], + buf[offset + 1], buf[offset + 2], buf[offset + 3]); + offset += 4; + total_config_sz += fw_hdr->config_size[i]; + } + dev_info(tas_dev->dev, + "img_sz = %u total_config_sz = %u offset = %d\n", + fw_hdr->img_sz, total_config_sz, offset); + if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) { + dev_err(tas_dev->dev, "Bin file error!\n"); + ret = -1; + goto out; + } + cfg_info = kcalloc(fw_hdr->nconfig, + sizeof(struct tasdevice_config_info *), + GFP_KERNEL); + + if (!cfg_info) { + ret = -1; + dev_err(tas_dev->dev, "nconfig Memory alloc failed!\n"); + goto out; + } + regbin->cfg_info = cfg_info; + regbin->ncfgs = 0; + for (i = 0; i < (int)fw_hdr->nconfig; i++) { + cfg_info[i] = tasdevice_add_config(pContext, &buf[offset], + fw_hdr->config_size[i]); + if (!cfg_info[i]) { + ret = -1; + dev_err(tas_dev->dev, + "add_config Memory alloc failed!\n"); + break; + } + offset += (int)fw_hdr->config_size[i]; + regbin->ncfgs += 1; + } + tasdevice_create_controls(tas_dev); + + tasdevice_dsp_remove(tas_dev); + tasdevice_calbin_remove(tas_dev); + tas_dev->fw_state = TASDEVICE_DSP_FW_PENDING; + scnprintf(tas_dev->dsp_binaryname, 64, "%s_dsp.bin", + tas_dev->dev_name); + ret = request_firmware(&fw_entry, tas_dev->dsp_binaryname, + tas_dev->dev); + if (!ret) { + ret = tasdevice_dspfw_ready(fw_entry, tas_dev); + release_firmware(fw_entry); + fw_entry = NULL; + } else { + tas_dev->fw_state = TASDEVICE_DSP_FW_FAIL; + dev_err(tas_dev->dev, "%s: load %s error\n", __func__, + tas_dev->dsp_binaryname); + goto out; + } + tasdevice_dsp_create_control(tas_dev); + + tas_dev->fw_state = TASDEVICE_DSP_FW_ALL_OK; + tas_dev->mbCalibrationLoaded = true; + for (i = 0; i < tas_dev->ndev; i++) { + scnprintf(tas_dev->cal_binaryname[i], 64, "%s_cal_0x%02x.bin", + tas_dev->dev_name, tas_dev->tasdevice[i].mnDevAddr); + ret = tas2781_load_calibration(tas_dev, + tas_dev->cal_binaryname[i], i); + if (ret != 0) { + dev_err(tas_dev->dev, + "%s: load %s error, no-side effect for playback\n", + __func__, + tas_dev->cal_binaryname[i]); + ret = 0; + tas_dev->mbCalibrationLoaded = false; + } + } + +out: + mutex_unlock(&tas_dev->codec_lock); + if (pFW) + release_firmware(pFW); + dev_info(tas_dev->dev, "Firmware init complete\n"); +} + +void tasdevice_config_info_remove(void *pContext) +{ + struct tasdevice_priv *tas_dev = + (struct tasdevice_priv *) pContext; + struct tasdevice_regbin *regbin = &(tas_dev->mtRegbin); + struct tasdevice_config_info **cfg_info = regbin->cfg_info; + int i = 0, j = 0; + + mutex_lock(&tas_dev->dev_lock); + if (cfg_info) { + for (i = 0; i < regbin->ncfgs; i++) { + if (cfg_info[i]) { + for (j = 0; j < (int)cfg_info[i]->real_nblocks; + j++) { + kfree( + cfg_info[i]->blk_data[j]->regdata); + kfree(cfg_info[i]->blk_data[j]); + } + kfree(cfg_info[i]->blk_data); + kfree(cfg_info[i]); + } + } + kfree(cfg_info); + } + mutex_unlock(&tas_dev->dev_lock); +} + +static int tasdevice_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dev_info(tas_dev->dev, "SND_SOC_DAPM_POST_PMU\n"); + break; + case SND_SOC_DAPM_PRE_PMD: + dev_info(tas_dev->dev, "SND_SOC_DAPM_PRE_PMD\n"); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, + tasdevice_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON") +}; + +static const struct snd_soc_dapm_route tasdevice_audio_map[] = { + {"DAC", NULL, "ASI"}, + {"OUT", NULL, "DAC"}, + {"ASI OUT", NULL, "DMIC"} +}; + +static int tasdevice_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); + int ret = 0; + + if (tas_dev->fw_state != TASDEVICE_DSP_FW_ALL_OK) { + dev_err(tas_dev->dev, "DSP bin file not loaded\n"); + ret = -EINVAL; + } + return ret; +} + +static int tasdevice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct tasdevice_priv *tas_dev = snd_soc_dai_get_drvdata(dai); + unsigned int fsrate; + unsigned int slot_width; + int bclk_rate; + int rc = 0; + + dev_info(tas_dev->dev, "%s: %s\n", + __func__, substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + "Playback":"Capture"); + + fsrate = params_rate(params); + switch (fsrate) { + case 48000: + break; + case 44100: + break; + default: + dev_err(tas_dev->dev, + "%s: incorrect sample rate = %u\n", + __func__, fsrate); + rc = -EINVAL; + goto out; + } + + slot_width = params_width(params); + switch (slot_width) { + case 16: + break; + case 20: + break; + case 24: + break; + case 32: + break; + default: + dev_err(tas_dev->dev, + "%s: incorrect slot width = %u\n", + __func__, slot_width); + rc = -EINVAL; + goto out; + } + + bclk_rate = snd_soc_params_to_bclk(params); + if (bclk_rate < 0) { + dev_err(tas_dev->dev, + "%s: incorrect bclk rate = %d\n", + __func__, bclk_rate); + rc = bclk_rate; + goto out; + } + dev_info(tas_dev->dev, + "%s: BCLK rate = %d Channel = %d Sample rate = %u slot width = %u\n", + __func__, bclk_rate, params_channels(params), + fsrate, slot_width); +out: + return rc; +} + +static void tasdevice_enable_irq( + struct tasdevice_priv *tas_dev, bool enable) +{ + struct irq_desc *desc = NULL; + + if (enable) { + if (tas_dev->mIrqInfo.mb_irq_enable) + return; + if (gpio_is_valid(tas_dev->mIrqInfo.mn_irq_gpio)) { + desc = irq_to_desc(tas_dev->mIrqInfo.mn_irq); + if (desc && desc->depth > 0) + enable_irq(tas_dev->mIrqInfo.mn_irq); + else + dev_info(tas_dev->dev, + "### irq already enabled\n"); + } + tas_dev->mIrqInfo.mb_irq_enable = true; + } else { + if (gpio_is_valid(tas_dev->mIrqInfo.mn_irq_gpio)) + disable_irq_nosync(tas_dev->mIrqInfo.mn_irq); + tas_dev->mIrqInfo.mb_irq_enable = false; + } +} + +static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct tasdevice_priv *tas_dev = snd_soc_dai_get_drvdata(codec_dai); + + dev_info(tas_dev->dev, + "%s: clk_id = %d, freq = %u, CLK direction %s\n", + __func__, clk_id, freq, + dir == TASDEVICE_CLK_DIR_OUT ? "OUT":"IN"); + + return 0; +} + +static void powercontrol_routine(struct work_struct *work) +{ + struct tasdevice_priv *tas_dev = + container_of(work, struct tasdevice_priv, + powercontrol_work.work); + struct TFirmware *pFw = NULL; + int profile_cfg_id = 0; + + mutex_lock(&tas_dev->codec_lock); + /*mnCurrentProgram != 0 is dsp mode or tuning mode*/ + if (tas_dev->mnCurrentProgram) { + /*bypass all in regbin is profile id 0*/ + profile_cfg_id = REGBIN_CONFIGID_BYPASS_ALL; + } else { + profile_cfg_id = tas_dev->mtRegbin.profile_cfg_id; + pFw = tas_dev->mpFirmware; + dev_info(tas_dev->dev, "%s: %s\n", __func__, + pFw->mpConfigurations[tas_dev->mnCurrentConfiguration] + .mpName); + tasdevice_select_tuningprm_cfg(tas_dev, + tas_dev->mnCurrentProgram, + tas_dev->mnCurrentConfiguration, + profile_cfg_id); + if (tas_dev->tas2781_set_global != NULL) + tas_dev->tas2781_set_global(tas_dev); + } + tasdevice_select_cfg_blk(tas_dev, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_POWER_UP); + + if (tas_dev->chip_id != GENERAL_AUDEV) + tasdevice_enable_irq(tas_dev, true); + mutex_unlock(&tas_dev->codec_lock); +} + +static void tasdevice_set_power_state( + struct tasdevice_priv *tas_dev, int state) +{ + switch (state) { + case 0: + schedule_delayed_work(&tas_dev->powercontrol_work, + msecs_to_jiffies(20)); + break; + default: + if (!(tas_dev->pstream || tas_dev->cstream)) { + if (tas_dev->chip_id != GENERAL_AUDEV) + tasdevice_enable_irq(tas_dev, false); + tasdevice_select_cfg_blk(tas_dev, + tas_dev->mtRegbin.profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_SHUTDOWN); + } + break; + } +} + +static int tasdevice_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *codec = dai->component; + struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); + + /* Codec Lock Hold */ + mutex_lock(&tas_dev->codec_lock); + + if (mute) { + /* stop DSP only when both playback and capture streams + * are deactivated + */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + tas_dev->pstream = 0; + else + tas_dev->cstream = 0; + if (tas_dev->pstream != 0 || tas_dev->cstream != 0) + goto out; + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + tas_dev->pstream = 1; + else + tas_dev->cstream = 1; + + } + tasdevice_set_power_state(tas_dev, mute); +out: + /* Codec Lock Release*/ + mutex_unlock(&tas_dev->codec_lock); + + return 0; +} + +static const struct snd_soc_dai_ops tasdevice_dai_ops = { + .startup = tasdevice_startup, + .hw_params = tasdevice_hw_params, + .set_sysclk = tasdevice_set_dai_sysclk, + .mute_stream = tasdevice_mute, +}; + +static struct snd_soc_dai_driver tasdevice_dai_driver[] = { + { + .name = "tas2781_codec", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 4, + .rates = TASDEVICE_RATES, + .formats = TASDEVICE_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TASDEVICE_RATES, + .formats = TASDEVICE_FORMATS, + }, + .ops = &tasdevice_dai_ops, + .symmetric_rate = 1, + }, +}; + +static int tasdevice_codec_probe( + struct snd_soc_component *codec) +{ + struct tasdevice_priv *tas_dev = + snd_soc_component_get_drvdata(codec); + int ret; + + /* Codec Lock Hold */ + mutex_lock(&tas_dev->codec_lock); + + tas_dev->codec = codec; + scnprintf(tas_dev->regbin_binaryname, 64, "%s_regbin.bin", + tas_dev->dev_name); + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + tas_dev->regbin_binaryname, tas_dev->dev, GFP_KERNEL, tas_dev, + tasdevice_regbin_ready); + if (ret) + dev_err(tas_dev->dev, + "%s: request_firmware_nowait error:0x%08x\n", + __func__, ret); + + /* Codec Lock Release*/ + mutex_unlock(&tas_dev->codec_lock); + + if (tas_dev->tas2781_reset != NULL) + tas_dev->tas2781_reset(tas_dev); + if (tas_dev->tas2781_set_global != NULL) + tas_dev->tas2781_set_global(tas_dev); + + return ret; +} + +static void tasdevice_deinit(void *pContext) +{ + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) pContext; + + tasdevice_config_info_remove(tas_dev); + tasdevice_dsp_remove(tas_dev); + tasdevice_calbin_remove(tas_dev); + tas_dev->fw_state = TASDEVICE_DSP_FW_PENDING; +} + +static void tasdevice_codec_remove( + struct snd_soc_component *codec) +{ + struct tasdevice_priv *tas_dev = + snd_soc_component_get_drvdata(codec); + /* Codec Lock Hold */ + mutex_lock(&tas_dev->codec_lock); + tasdevice_deinit(tas_dev); + /* Codec Lock Release*/ + mutex_unlock(&tas_dev->codec_lock); + + return; + +} + +static const struct snd_soc_component_driver + soc_codec_driver_tasdevice = { + .probe = tasdevice_codec_probe, + .remove = tasdevice_codec_remove, + .controls = tas2781_snd_controls, + .num_controls = ARRAY_SIZE(tas2781_snd_controls), + .dapm_widgets = tasdevice_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets), + .dapm_routes = tasdevice_audio_map, + .num_dapm_routes = ARRAY_SIZE(tasdevice_audio_map), + .idle_bias_on = 1, + .endianness = 1, +}; + +static void irq_work_routine(struct work_struct *pWork) +{ + struct tasdevice_priv *tas_dev = + container_of(pWork, struct tasdevice_priv, + mIrqInfo.irq_work.work); + + mutex_lock(&tas_dev->codec_lock); + if (tas_dev->mb_runtime_suspend) { + dev_info(tas_dev->dev, "%s, Runtime Suspended\n", __func__); + goto end; + } + /*Logical Layer IRQ function, return is ignored*/ + if (tas_dev->irq_work_func) + tas_dev->irq_work_func(tas_dev); + else + dev_info(tas_dev->dev, + "%s, irq_work_func is NULL\n", __func__); +end: + mutex_unlock(&tas_dev->codec_lock); +} + +static void tas2781_irq_work_func(struct tasdevice_priv *tas_dev) +{ + int rc = 0; + unsigned int reg_val = 0, array_size = 0, i = 0, ndev = 0; + unsigned int int_reg_array[] = { + TAS2781_REG_INT_LTCH0, + TAS2781_REG_INT_LTCH1, + TAS2781_REG_INT_LTCH1_0, + TAS2781_REG_INT_LTCH2, + TAS2781_REG_INT_LTCH3, + TAS2781_REG_INT_LTCH4}; + + tasdevice_enable_irq(tas_dev, false); + + array_size = ARRAY_SIZE(int_reg_array); + + for (ndev = 0; ndev < tas_dev->ndev; ndev++) { + for (i = 0; i < array_size; i++) { + rc = tasdevice_dev_read(tas_dev, + ndev, int_reg_array[i], ®_val); + if (!rc) + dev_info(tas_dev->dev, + "INT STATUS REG 0x%04x=0x%02x\n", + int_reg_array[i], reg_val); + else + dev_err(tas_dev->dev, + "Read Reg 0x%04x error(rc=%d)\n", + int_reg_array[i], rc); + } + } + +} + +static irqreturn_t tasdevice_irq_handler(int irq, + void *dev_id) +{ + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *)dev_id; +; + /* get IRQ status after 100 ms */ + schedule_delayed_work(&tas_dev->mIrqInfo.irq_work, + msecs_to_jiffies(100)); + return IRQ_HANDLED; +} + +int tasdevice_parse_dt(struct tasdevice_priv *tas_dev) +{ + struct device_node *np = tas_dev->dev->of_node; + int rc = 0, i = 0, ndev = 0; + char buf[32]; + + + for (i = 0, ndev = 0; i < MaxChn; i++) { + rc = of_property_read_u32(np, dts_dev_addr_tag[i], + &(tas_dev->tasdevice[ndev].mnDevAddr)); + if (rc) + dev_err(tas_dev->dev, + "Looking up %s property in node %s failed %d\n", + dts_dev_addr_tag[i], + np->full_name, rc); + else { + dev_dbg(tas_dev->dev, "%s=0x%02x", dts_dev_addr_tag[i], + tas_dev->tasdevice[ndev].mnDevAddr); + ndev++; + } + } + + rc = of_property_read_u32(np, dts_glb_addr_tag, + &(tas_dev->glb_addr.mnDevAddr)); + if (rc) { + dev_err(tas_dev->dev, + "Looking up %s property in node %s failed %d\n", + dts_glb_addr_tag, np->full_name, rc); + tas_dev->glb_addr.mnDevAddr = 0; + } + tas_dev->ndev = (ndev == 0) ? 1 : ndev; + + for (i = 0, ndev = 0; i < tas_dev->ndev; i++) { + scnprintf(buf, sizeof(buf), dts_rst_tag, "reset", i); + tas_dev->tasdevice[ndev].mnResetGpio = of_get_named_gpio(np, + buf, 0); + if (gpio_is_valid(tas_dev->tasdevice[ndev].mnResetGpio)) { + rc = gpio_request(tas_dev->tasdevice[ndev].mnResetGpio, + buf); + if (!rc) { + gpio_direction_output( + tas_dev->tasdevice[ndev].mnResetGpio, + 1); + dev_info(tas_dev->dev, "%s = %d", buf, + tas_dev->tasdevice[ndev].mnResetGpio); + ndev++; + } else + dev_err(tas_dev->dev, + "%s: Failed to request dev[%d] gpio %d\n", + __func__, ndev, + tas_dev->tasdevice[ndev].mnResetGpio); + } else + dev_err(tas_dev->dev, + "Looking up %s property in node %s failed %d\n", + buf, np->full_name, + tas_dev->tasdevice[ndev].mnResetGpio); + } + strcpy(tas_dev->dev_name, tasdevice_id[tas_dev->chip_id].name); + if (tas_dev->chip_id != GENERAL_AUDEV) { + tas_dev->mIrqInfo.mn_irq_gpio = of_get_named_gpio(np, + "ti,irq-gpio", 0); + if (gpio_is_valid(tas_dev->mIrqInfo.mn_irq_gpio)) { + dev_dbg(tas_dev->dev, "irq-gpio = %d", + tas_dev->mIrqInfo.mn_irq_gpio); + INIT_DELAYED_WORK(&tas_dev->mIrqInfo.irq_work, + irq_work_routine); + + rc = gpio_request(tas_dev->mIrqInfo.mn_irq_gpio, + "AUDEV-IRQ"); + if (!rc) { + gpio_direction_input( + tas_dev->mIrqInfo.mn_irq_gpio); + + tas_dev->mIrqInfo.mn_irq = + gpio_to_irq( + tas_dev->mIrqInfo.mn_irq_gpio); + dev_info(tas_dev->dev, + "irq = %d\n", + tas_dev->mIrqInfo.mn_irq); + + rc = request_threaded_irq( + tas_dev->mIrqInfo.mn_irq, + tasdevice_irq_handler, + NULL, IRQF_TRIGGER_FALLING| + IRQF_ONESHOT, + SMARTAMP_MODULE_NAME, tas_dev); + if (!rc) + disable_irq_nosync( + tas_dev->mIrqInfo.mn_irq); + else + dev_err(tas_dev->dev, + "request_irq failed, %d\n", + rc); + } else + dev_err(tas_dev->dev, + "%s: GPIO %d request error\n", + __func__, + tas_dev->mIrqInfo.mn_irq_gpio); + } else + dev_err(tas_dev->dev, + "Looking up irq-gpio property in node %s failed %d\n", + np->full_name, + tas_dev->mIrqInfo.mn_irq_gpio); + } + + if (tas_dev->chip_id != GENERAL_AUDEV && rc == 0) { + if (tas_dev->chip_id == TAS2781) + tas_dev->irq_work_func = tas2781_irq_work_func; + else + dev_info(tas_dev->dev, "%s: No match irq_work_func\n", + __func__); + } + + return 0; +} + +static int tasdevice_regmap_write( + struct tasdevice_priv *tas_dev, + unsigned int reg, unsigned int value) +{ + int nResult = 0; + int retry_count = TASDEVICE_RETRY_COUNT; + + while (retry_count--) { + nResult = regmap_write(tas_dev->regmap, reg, + value); + if (nResult >= 0) + break; + usleep_range(5000, 5050); + } + if (retry_count == -1) + return TASDEVICE_ERROR_FAILED; + else + return 0; +} + +static int tasdevice_regmap_bulk_write( + struct tasdevice_priv *tas_dev, unsigned int reg, + unsigned char *pData, unsigned int nLength) +{ + int nResult = 0; + int retry_count = TASDEVICE_RETRY_COUNT; + + while (retry_count--) { + nResult = regmap_bulk_write(tas_dev->regmap, reg, + pData, nLength); + if (nResult >= 0) + break; + usleep_range(5000, 5050); + } + if (retry_count == -1) + return TASDEVICE_ERROR_FAILED; + else + return 0; +} + +static int tasdevice_regmap_read( + struct tasdevice_priv *tas_dev, + unsigned int reg, unsigned int *value) +{ + int nResult = 0; + int retry_count = TASDEVICE_RETRY_COUNT; + + while (retry_count--) { + nResult = regmap_read(tas_dev->regmap, reg, + value); + if (nResult >= 0) + break; + usleep_range(5000, 5050); + } + if (retry_count == -1) + return TASDEVICE_ERROR_FAILED; + else + return 0; +} + +static int tasdevice_regmap_bulk_read( + struct tasdevice_priv *tas_dev, unsigned int reg, + unsigned char *pData, unsigned int nLength) +{ + int nResult = 0; + int retry_count = TASDEVICE_RETRY_COUNT; + + while (retry_count--) { + nResult = regmap_bulk_read(tas_dev->regmap, reg, + pData, nLength); + if (nResult >= 0) + break; + usleep_range(5000, 5050); + } + if (retry_count == -1) + return TASDEVICE_ERROR_FAILED; + else + return 0; +} + +static int tasdevice_regmap_update_bits( + struct tasdevice_priv *tas_dev, unsigned int reg, + unsigned int mask, unsigned int value) +{ + int nResult = 0; + int retry_count = TASDEVICE_RETRY_COUNT; + + while (retry_count--) { + nResult = regmap_update_bits(tas_dev->regmap, reg, + mask, value); + if (nResult >= 0) + break; + usleep_range(5000, 5050); + } + if (retry_count == -1) + return TASDEVICE_ERROR_FAILED; + else + return 0; +} + +static int tasdevice_change_chn_book_page( + struct tasdevice_priv *tas_dev, enum channel chn, + int book, int page) +{ + int n_result = 0; + struct i2c_client *pClient = + (struct i2c_client *)tas_dev->client; + + if (chn < tas_dev->ndev) { + if (tas_dev->glb_addr.ref_cnt != 0) { + tas_dev->glb_addr.ref_cnt = 0; + tas_dev->glb_addr.mnBkPg.mnBook = -1; + tas_dev->glb_addr.mnBkPg.mnPage = -1; + } + pClient->addr = tas_dev->tasdevice[chn].mnDevAddr; + if (tas_dev->tasdevice[chn].mnBkPg.mnBook != book) { + n_result = tasdevice_regmap_write(tas_dev, + TASDEVICE_BOOKCTL_PAGE, 0); + if (n_result < 0) { + dev_err(tas_dev->dev, "%s, ERROR, E=%d\n", + __func__, n_result); + goto out; + } + tas_dev->tasdevice[chn].mnBkPg.mnPage = 0; + n_result = tasdevice_regmap_write(tas_dev, + TASDEVICE_BOOKCTL_REG, book); + if (n_result < 0) { + dev_err(tas_dev->dev, "%s, ERROR, E=%d\n", + __func__, n_result); + goto out; + } + tas_dev->tasdevice[chn].mnBkPg.mnBook = book; + } + + if (tas_dev->tasdevice[chn].mnBkPg.mnPage != page) { + n_result = tasdevice_regmap_write(tas_dev, + TASDEVICE_BOOKCTL_PAGE, page); + if (n_result < 0) { + dev_err(tas_dev->dev, "%s, ERROR, E=%d\n", + __func__, n_result); + goto out; + } + tas_dev->tasdevice[chn].mnBkPg.mnPage = page; + } + } else if (chn == tas_dev->ndev) { + int i = 0; + + if (tas_dev->glb_addr.ref_cnt == 0) + for (i = 0; i < tas_dev->ndev; i++) { + tas_dev->tasdevice[i].mnBkPg.mnBook + = -1; + tas_dev->tasdevice[i].mnBkPg.mnPage + = -1; + } + pClient->addr = tas_dev->glb_addr.mnDevAddr; + if (tas_dev->glb_addr.mnBkPg.mnBook != book) { + n_result = tasdevice_regmap_write(tas_dev, + TASDEVICE_BOOKCTL_PAGE, 0); + if (n_result < 0) { + dev_err(tas_dev->dev, + "%s, 0ERROR, E=%d\n", + __func__, n_result); + goto out; + } + tas_dev->glb_addr.mnBkPg.mnPage = 0; + n_result = tasdevice_regmap_write(tas_dev, + TASDEVICE_BOOKCTL_REG, book); + if (n_result < 0) { + dev_err(tas_dev->dev, + "%s, book%xERROR, E=%d\n", + __func__, book, n_result); + goto out; + } + tas_dev->glb_addr.mnBkPg.mnBook = book; + } + + if (tas_dev->glb_addr.mnBkPg.mnPage != page) { + n_result = tasdevice_regmap_write(tas_dev, + TASDEVICE_BOOKCTL_PAGE, page); + if (n_result < 0) { + dev_err(tas_dev->dev, + "%s, page%xERROR, E=%d\n", + __func__, page, n_result); + goto out; + } + tas_dev->glb_addr.mnBkPg.mnPage = page; + } + tas_dev->glb_addr.ref_cnt++; + } else + dev_err(tas_dev->dev, + "%s, ERROR, no such channel(%d)\n", + __func__, chn); + +out: + return n_result; +} + +int tasdevice_dev_read(struct tasdevice_priv *tas_dev, + enum channel chn, unsigned int reg, unsigned int *pValue) +{ + int n_result = 0; + + mutex_lock(&tas_dev->dev_lock); + if (chn < tas_dev->ndev) { + n_result = tasdevice_change_chn_book_page(tas_dev, chn, + TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg)); + if (n_result < 0) + goto out; + + n_result = tasdevice_regmap_read(tas_dev, + TASDEVICE_PAGE_REG(reg), pValue); + if (n_result < 0) + dev_err(tas_dev->dev, "%s, ERROR,E=%d\n", + __func__, n_result); + else + dev_dbg(tas_dev->dev, + "%s: chn:0x%02x:BOOK:PAGE:REG 0x%02x:0x%02x:0x%02x, 0x%02x\n", + __func__, + tas_dev->tasdevice[chn].mnDevAddr, + TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg), + TASDEVICE_PAGE_REG(reg), *pValue); + } else { + + dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n", + __func__, chn); + } + +out: + mutex_unlock(&tas_dev->dev_lock); + return n_result; +} + +int tasdevice_dev_write(struct tasdevice_priv *tas_dev, + enum channel chn, unsigned int reg, unsigned int value) +{ + int n_result = 0; + + mutex_lock(&tas_dev->dev_lock); + if (chn <= tas_dev->ndev) { + n_result = tasdevice_change_chn_book_page(tas_dev, chn, + TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg)); + if (n_result < 0) + goto out; + + n_result = tasdevice_regmap_write(tas_dev, + TASDEVICE_PAGE_REG(reg), value); + if (n_result < 0) + dev_err(tas_dev->dev, "%s, ERROR, E=%d\n", + __func__, n_result); + else { + dev_dbg(tas_dev->dev, + "%s: %s-0x%02x:BOOK:PAGE:REG 0x%02x:0x%02x:0x%02x, VAL: 0x%02x\n", + __func__, (chn == tas_dev->ndev)?"glb":"chn", + (chn == tas_dev->ndev) ? + tas_dev->glb_addr.mnDevAddr + : tas_dev->tasdevice[chn].mnDevAddr, + TASDEVICE_BOOK_ID(reg), + TASDEVICE_PAGE_ID(reg), + TASDEVICE_PAGE_REG(reg), value); + } + } else + dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n", + __func__, chn); +out: + mutex_unlock(&tas_dev->dev_lock); + return n_result; +} + +int tasdevice_dev_bulk_write( + struct tasdevice_priv *tas_dev, enum channel chn, + unsigned int reg, unsigned char *p_data, + unsigned int n_length) +{ + int n_result = 0; + + mutex_lock(&tas_dev->dev_lock); + if (chn <= tas_dev->ndev) { + n_result = tasdevice_change_chn_book_page(tas_dev, chn, + TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg)); + if (n_result < 0) + goto out; + + n_result = tasdevice_regmap_bulk_write(tas_dev, + TASDEVICE_PAGE_REG(reg), p_data, n_length); + if (n_result < 0) + dev_err(tas_dev->dev, "%s, ERROR, E=%d\n", + __func__, n_result); + else { + dev_dbg(tas_dev->dev, + "%s: %s-0x%02x:BOOK:PAGE:REG 0x%02x:0x%02x: 0x%02x, len: 0x%02x\n", + __func__, + (chn == tas_dev->ndev)?"glb":"chn", + (chn == tas_dev->ndev) ? + tas_dev->glb_addr.mnDevAddr + : tas_dev->tasdevice[chn].mnDevAddr, + TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg), + TASDEVICE_PAGE_REG(reg), n_length); + } + } else + dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n", + __func__, chn); +out: + mutex_unlock(&tas_dev->dev_lock); + return n_result; +} + +int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_dev, + enum channel chn, unsigned int reg, unsigned char *p_data, + unsigned int n_length) +{ + int n_result = 0; + + mutex_lock(&tas_dev->dev_lock); + if (chn < tas_dev->ndev) { + n_result = tasdevice_change_chn_book_page(tas_dev, chn, + TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg)); + if (n_result < 0) + goto out; + + n_result = tasdevice_regmap_bulk_read(tas_dev, + TASDEVICE_PAGE_REG(reg), p_data, n_length); + if (n_result < 0) + dev_err(tas_dev->dev, "%s, ERROR, E=%d\n", + __func__, n_result); + else + dev_dbg(tas_dev->dev, + "%s: chn0x%02x:BOOK:PAGE:REG 0x%02x:0x%02x: 0x%02x, len: 0x%02x\n", + __func__, + tas_dev->tasdevice[chn].mnDevAddr, + TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg), + TASDEVICE_PAGE_REG(reg), n_length); + } else + dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n", + __func__, chn); + +out: + mutex_unlock(&tas_dev->dev_lock); + return n_result; +} + +int tasdevice_dev_update_bits( + struct tasdevice_priv *tas_dev, enum channel chn, + unsigned int reg, unsigned int mask, unsigned int value) +{ + int n_result = 0; + struct i2c_client *pClient = + (struct i2c_client *)tas_dev->client; + + mutex_lock(&tas_dev->dev_lock); + if (chn < tas_dev->ndev) { + n_result = tasdevice_change_chn_book_page(tas_dev, chn, + TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg)); + if (n_result < 0) + goto out; + pClient->addr = tas_dev->tasdevice[chn].mnDevAddr; + n_result = tasdevice_regmap_update_bits(tas_dev, + TASDEVICE_PAGE_REG(reg), mask, value); + if (n_result < 0) + dev_err(tas_dev->dev, "%s, ERROR, E=%d\n", + __func__, n_result); + else + dev_dbg(tas_dev->dev, + "%s: chn0x%02x:BOOK:PAGE:REG 0x%02x:0x%02x:0x%02x, mask: 0x%02x, val: 0x%02x\n", + __func__, tas_dev->tasdevice[chn].mnDevAddr, + TASDEVICE_BOOK_ID(reg), TASDEVICE_PAGE_ID(reg), + TASDEVICE_PAGE_REG(reg), mask, value); + } else { + dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n", + __func__, chn); + n_result = -1; + } + +out: + mutex_unlock(&tas_dev->dev_lock); + return n_result; +} + +static void tas2781_reset(struct tasdevice_priv *tas_dev) +{ + int ret = 0; + int i = 0; + + for (; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_write(tas_dev, i, + TAS2871_REG_SWRESET, + TAS2871_REG_SWRESET_RESET); + if (ret < 0) { + dev_err(tas_dev->dev, "%s: chn %d reset fail, %d\n", + __func__, i, ret); + continue; + } + } + usleep_range(1000, 1050); +} + +static void tas2781_set_global(struct tasdevice_priv *tas_dev) +{ + int i = 0; + int ret = 0; + + for (; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_update_bits(tas_dev, i, + TAS2871_MISC_CFG2, + TAS2871_GLOBAL_MASK, 0x02); + if (ret < 0) { + dev_err(tas_dev->dev, "%s: chn %d set global fail, %d\n", + __func__, i, ret); + continue; + } + } +} + +static int tasdevice_init(struct tasdevice_priv *tas_dev) +{ + int nResult = 0, i = 0; + + tas_dev->mnCurrentProgram = -1; + tas_dev->mnCurrentConfiguration = -1; + + for (i = 0; i < tas_dev->ndev; i++) { + tas_dev->tasdevice[i].mnBkPg.mnBook = -1; + tas_dev->tasdevice[i].mnBkPg.mnPage = -1; + tas_dev->tasdevice[i].mnCurrentProgram = -1; + tas_dev->tasdevice[i].mnCurrentConfiguration = -1; + } + mutex_init(&tas_dev->dev_lock); + tas_dev->read = tasdevice_dev_read; + tas_dev->write = tasdevice_dev_write; + tas_dev->bulk_read = tasdevice_dev_bulk_read; + tas_dev->bulk_write = tasdevice_dev_bulk_write; + tas_dev->set_calibration = tas2781_set_calibration; + tas_dev->tas2781_reset = tas2781_reset; + if (tas_dev->glb_addr.mnDevAddr != 0 + && tas_dev->glb_addr.mnDevAddr < 0x7F) + tas_dev->tas2781_set_global = tas2781_set_global; + dev_set_drvdata(tas_dev->dev, tas_dev); + + INIT_DELAYED_WORK(&tas_dev->powercontrol_work, + powercontrol_routine); + + mutex_init(&tas_dev->codec_lock); + nResult = devm_snd_soc_register_component(tas_dev->dev, + &soc_codec_driver_tasdevice, + tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver)); + if (nResult) + dev_err(tas_dev->dev, "%s: codec register error:0x%08x\n", + __func__, nResult); + + INIT_DELAYED_WORK(&tas_dev->mIrqInfo.irq_work, irq_work_routine); + tas_dev->mIrqInfo.mb_irq_enable = false; + + dev_info(tas_dev->dev, "i2c register success\n"); + + return nResult; +} + +void tasdevice_remove(struct tasdevice_priv *tas_dev) +{ + int i = 0; + + for (i = 0; i < tas_dev->mtRstGPIOs.ndev; i++) { + if (gpio_is_valid(tas_dev->mtRstGPIOs.mnResetGpio[i])) + gpio_free(tas_dev->mtRstGPIOs.mnResetGpio[i]); + } + + for (i = 0; i < tas_dev->ndev; i++) { + if (gpio_is_valid(tas_dev->tasdevice[i].mnIRQGPIO)) + gpio_free(tas_dev->tasdevice[i].mnIRQGPIO); + } + + if (delayed_work_pending(&tas_dev->mIrqInfo.irq_work)) { + dev_info(tas_dev->dev, "cancel IRQ work\n"); + cancel_delayed_work(&tas_dev->mIrqInfo.irq_work); + } + cancel_delayed_work_sync(&tas_dev->mIrqInfo.irq_work); + + mutex_destroy(&tas_dev->dev_lock); + mutex_destroy(&tas_dev->codec_lock); + snd_soc_unregister_component(tas_dev->dev); +} + +static int tasdevice_pm_suspend(struct device *dev) +{ + struct tasdevice_priv *tas_dev = dev_get_drvdata(dev); + + if (!tas_dev) { + dev_err(tas_dev->dev, "%s: drvdata is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&tas_dev->codec_lock); + + tas_dev->mb_runtime_suspend = true; + + if (tas_dev->chip_id != GENERAL_AUDEV) { + if (delayed_work_pending(&tas_dev->mIrqInfo.irq_work)) { + dev_dbg(tas_dev->dev, "cancel IRQ work\n"); + cancel_delayed_work_sync(&tas_dev->mIrqInfo.irq_work); + } + } + mutex_unlock(&tas_dev->codec_lock); + return 0; +} + +static int tasdevice_pm_resume(struct device *dev) +{ + struct tasdevice_priv *tas_dev = dev_get_drvdata(dev); + + if (!tas_dev) + return -EINVAL; + + mutex_lock(&tas_dev->codec_lock); + tas_dev->mb_runtime_suspend = false; + mutex_unlock(&tas_dev->codec_lock); + return 0; +} + +const struct dev_pm_ops tasdevice_pm_ops = { + .suspend = tasdevice_pm_suspend, + .resume = tasdevice_pm_resume +}; + +static int tasdevice_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tasdevice_priv *tas_dev = NULL; + int ret = 0; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, + "%s: I2C check failed\n", __func__); + ret = -ENODEV; + goto out; + } + + tas_dev = devm_kzalloc(&i2c->dev, sizeof(*tas_dev), GFP_KERNEL); + if (!tas_dev) { + ret = -ENOMEM; + goto out; + } + tas_dev->dev = &i2c->dev; + tas_dev->client = (void *)i2c; + tas_dev->chip_id = id->driver_data; + + if (i2c->dev.of_node) + ret = tasdevice_parse_dt(tas_dev); + else { + dev_err(tas_dev->dev, "No DTS info\n"); + goto out; + } + + tas_dev->regmap = devm_regmap_init_i2c(i2c, + &tasdevice_regmap); + if (IS_ERR(tas_dev->regmap)) { + ret = PTR_ERR(tas_dev->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + ret = tasdevice_init(tas_dev); + +out: + if (ret < 0 && tas_dev != NULL) + tasdevice_remove(tas_dev); + return ret; + +} + +static void tasdevice_i2c_remove(struct i2c_client *pClient) +{ + struct tasdevice_priv *tas_dev = i2c_get_clientdata(pClient); + + if (tas_dev) + tasdevice_remove(tas_dev); + +} + +static struct i2c_driver tasdevice_i2c_driver = { + .driver = { + .name = "tas2781-codec", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tasdevice_of_match), + .pm = &tasdevice_pm_ops, + }, + .probe = tasdevice_i2c_probe, + .remove = tasdevice_i2c_remove, + .id_table = tasdevice_id, +}; + +module_i2c_driver(tasdevice_i2c_driver); + +MODULE_AUTHOR("Shenghao Ding shenghao-ding@ti.com"); +MODULE_AUTHOR("Kevin Lu kevin-lu@ti.com"); +MODULE_DESCRIPTION("ASoC TAS2781 Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2781.h b/sound/soc/codecs/tas2781.h new file mode 100644 index 000000000..db4ec752b --- /dev/null +++ b/sound/soc/codecs/tas2781.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier + * + * Copyright (C) 2022 - 2023 Texas Instruments Incorporated + * https://www.ti.com + * + * The TAS2781 driver implements a flexible and configurable register setting + * for one, two, even multiple TAS2781 chips. All the register setting are in + * a bin file. Almost no specific register setting can be found in the code. + * + * Author: Shenghao Ding shenghao-ding@ti.com + * Kevin Lu kevin-lu@ti.com + */ + +#ifndef __TAS2781_H__ +#define __TAS2781_H__ + +#include "tas2781-dsp.h" + +#define SMARTAMP_MODULE_NAME ("tas2781") +#define MAX_LENGTH (128) + +#define TASDEVICE_RETRY_COUNT (3) +#define TASDEVICE_ERROR_FAILED (-2) + +#define TASDEVICE_RATES (SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_88200) +#define TASDEVICE_MAX_CHANNELS (8) + +#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_REG(book, page, reg) (((book * 256 * 128) + \ + (page * 128)) + reg) + + /*Software Reset */ +#define TAS2871_REG_SWRESET TASDEVICE_REG(0x0, 0X0, 0x02) +#define TAS2871_REG_SWRESET_RESET (0x1 << 0) + + /* Enable Global addresses */ +#define TAS2871_MISC_CFG2 TASDEVICE_REG(0x0, 0X0, 0x07) +#define TAS2871_GLOBAL_MASK (0x1 << 1) + +#define SMS_HTONS(a, b) ((((a)&0x00FF)<<8) | \ + ((b)&0x00FF)) +#define SMS_HTONL(a, b, c, d) ((((a)&0x000000FF)<<24) |\ + (((b)&0x000000FF)<<16) | \ + (((c)&0x000000FF)<<8) | \ + ((d)&0x000000FF)) + + /*I2C Checksum */ +#define TASDEVICE_I2CChecksum 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) + +enum audio_device { + GENERAL_AUDEV = 0, + TAS2781 = 1, +}; + +struct smartpa_gpio_info { + unsigned char ndev; + int mnResetGpio[MaxChn]; +}; + +#define SMS_HTONS(a, b) ((((a)&0x00FF)<<8) | ((b)&0x00FF)) +#define SMS_HTONL(a, b, c, d) ((((a)&0x000000FF)<<24) | \ + (((b)&0x000000FF)<<16) | (((c)&0x000000FF)<<8) | \ + ((d)&0x000000FF)) + +struct Tbookpage { + unsigned char mnBook; + unsigned char mnPage; +}; + +struct Ttasdevice { + unsigned int mnDevAddr; + unsigned int mnErrCode; + short mnCurrentProgram; + short mnCurrentConfiguration; + short mnCurrentRegConf; + int mnIRQGPIO; + int mnResetGpio; + int mn_irq; + int PowerStatus; + bool bDSPBypass; + bool bIrq_enabled; + bool bLoading; + bool bLoaderr; + bool mbCalibrationLoaded; + struct Tbookpage mnBkPg; + struct TFirmware *mpCalFirmware; +}; + +struct global_addr { + struct Tbookpage mnBkPg; + unsigned int mnDevAddr; + int ref_cnt; +}; + +struct tas_control { + struct snd_kcontrol_new *tasdevice_profile_controls; + int nr_controls; +}; + +struct tasdevice_irqinfo { + int mn_irq_gpio; + int mn_irq; + struct delayed_work irq_work; + bool mb_irq_enable; +}; + +struct tasdevice_priv { + struct device *dev; + void *client; + struct regmap *regmap; + struct mutex codec_lock; + struct mutex dev_lock; + struct Ttasdevice tasdevice[MaxChn]; + struct TFirmware *mpFirmware; + struct tasdevice_regbin mtRegbin; + struct smartpa_gpio_info mtRstGPIOs; + struct tasdevice_irqinfo mIrqInfo; + struct tas_control tas_ctrl; + struct global_addr glb_addr; + int mnCurrentProgram; + int mnCurrentConfiguration; + unsigned int chip_id; + int (*read)(struct tasdevice_priv *tas_dev, enum channel chn, + unsigned int reg, unsigned int *pValue); + int (*write)(struct tasdevice_priv *tas_dev, enum channel chn, + unsigned int reg, unsigned int Value); + int (*bulk_read)(struct tasdevice_priv *tas_dev, enum channel chn, + unsigned int reg, unsigned char *pData, unsigned int len); + int (*bulk_write)(struct tasdevice_priv *tas_dev, enum channel chn, + unsigned int reg, unsigned char *pData, unsigned int len); + int (*set_calibration)(void *tas_dev, enum channel chl, + int calibration); + void (*tas2781_reset)(struct tasdevice_priv *tas_dev); + void (*tas2781_set_global)(struct tasdevice_priv *tas_dev); + int (*fw_parse_variable_header)(struct tasdevice_priv *tas_dev, + const struct firmware *pFW, int offset); + int (*fw_parse_program_data)(struct tasdevice_priv *tas_dev, + struct TFirmware *pFirmware, + const struct firmware *pFW, int offset); + int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_dev, + struct TFirmware *pFirmware, + const struct firmware *pFW, int offset); + int (*tasdevice_load_block)(struct tasdevice_priv *tas_dev, + struct TBlock *pBlock); + int (*fw_parse_calibration_data)(struct tasdevice_priv *tas_dev, + struct TFirmware *pFirmware, + const struct firmware *pFW, int offset); + void (*irq_work_func)(struct tasdevice_priv *tas_dev); + int fw_state; + unsigned int magic_num; + unsigned char ndev; + unsigned char dev_name[32]; + unsigned char regbin_binaryname[64]; + unsigned char dsp_binaryname[64]; + unsigned char cal_binaryname[MaxChn][64]; + bool mb_runtime_suspend; + struct delayed_work powercontrol_work; + void *codec; + int sysclk; + int pstream; + int cstream; + bool mbCalibrationLoaded; +}; + +int tasdevice_dev_read(struct tasdevice_priv *pPcmdev, + enum channel chn, unsigned int reg, unsigned int *pValue); +int tasdevice_process_block(void *pContext, + unsigned char *data, unsigned char dev_idx, int sublocksize); +int tasdevice_dev_write(struct tasdevice_priv *pPcmdev, + enum channel chn, unsigned int reg, unsigned int value); + +int tasdevice_dev_bulk_write( + struct tasdevice_priv *pPcmdev, enum channel chn, + unsigned int reg, unsigned char *p_data, unsigned int n_length); + +int tasdevice_dev_bulk_read(struct tasdevice_priv *pPcmdev, + enum channel chn, unsigned int reg, unsigned char *p_data, + unsigned int n_length); + +int tasdevice_dev_update_bits( + struct tasdevice_priv *pPcmdev, enum channel chn, + unsigned int reg, unsigned int mask, unsigned int value); + +#endif /*__TAS2781_H__ */
Hi Kevin,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on broonie-sound/for-next] [also build test WARNING on linus/master v6.1 next-20221220] [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/Kevin-Lu/ALSA-SoC-Texas-Instr... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20221220151157.2247-1-luminlong%40139.com patch subject: [PATCH v1] ALSA SoC: Texas Instruments TAS2781 Audio Smart Amp config: s390-allyesconfig compiler: s390-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/de2ce420cebecc02e8b8f1eda156c6... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Kevin-Lu/ALSA-SoC-Texas-Instruments-TAS2781-Audio-Smart-Amp/20221220-231421 git checkout de2ce420cebecc02e8b8f1eda156c623f9e8bc37 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=s390 olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=s390 SHELL=/bin/bash sound/soc/codecs/
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
All warnings (new ones prefixed by >>):
sound/soc/codecs/tas2781-dsp.c:226:5: warning: no previous prototype for 'fw_parse_program_data_kernel' [-Wmissing-prototypes] 226 | int fw_parse_program_data_kernel(struct tasdevice_priv *tas_dev, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ sound/soc/codecs/tas2781-dsp.c:298:5: warning: no previous prototype for 'fw_parse_configuration_data_kernel' [-Wmissing-prototypes] 298 | int fw_parse_configuration_data_kernel( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sound/soc/codecs/tas2781-dsp.c:398:5: warning: no previous prototype for 'fw_parse_variable_header_kernel' [-Wmissing-prototypes] 398 | int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_dev, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sound/soc/codecs/tas2781-dsp.c:536:5: warning: no previous prototype for 'tasdevice_load_block_kernel' [-Wmissing-prototypes] 536 | int tasdevice_load_block_kernel(struct tasdevice_priv *pTAS2781, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ sound/soc/codecs/tas2781-dsp.c:650:5: warning: no previous prototype for 'fw_parse_variable_header_git' [-Wmissing-prototypes] 650 | int fw_parse_variable_header_git(struct tasdevice_priv *tas_dev, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ sound/soc/codecs/tas2781-dsp.c:712:5: warning: no previous prototype for 'fw_parse_variable_header_cal' [-Wmissing-prototypes] 712 | int fw_parse_variable_header_cal(struct tasdevice_priv *tas_dev, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from include/linux/device.h:15, from include/linux/acpi.h:15, from include/linux/i2c.h:13, from sound/soc/codecs/tas2781-dsp.c:17: sound/soc/codecs/tas2781-dsp.c: In function 'fw_parse_block_data':
sound/soc/codecs/tas2781-dsp.c:875:25: warning: format '%u' expects argument of type 'unsigned int', but argument 4 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
875 | "%s: File Size(%u) error offset = %d n = %d\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:144:56: note: in expansion of macro 'dev_fmt' 144 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:874:17: note: in expansion of macro 'dev_err' 874 | dev_err(pFirmware->dev, | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:875:41: note: format string is defined here 875 | "%s: File Size(%u) error offset = %d n = %d\n", | ~^ | | | unsigned int | %lu sound/soc/codecs/tas2781-dsp.c: In function 'fw_parse_header':
sound/soc/codecs/tas2781-dsp.c:1312:39: warning: format '%d' expects argument of type 'int', but argument 3 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
1312 | dev_err(tas_dev->dev, "File size not match, %d %d", pFW->size, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:144:56: note: in expansion of macro 'dev_fmt' 144 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:1312:17: note: in expansion of macro 'dev_err' 1312 | dev_err(tas_dev->dev, "File size not match, %d %d", pFW->size, | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:1312:62: note: format string is defined here 1312 | dev_err(tas_dev->dev, "File size not match, %d %d", pFW->size, | ~^ | | | int | %ld sound/soc/codecs/tas2781-dsp.c: In function 'tas2781_load_calibration': sound/soc/codecs/tas2781-dsp.c:1986:33: warning: format '%d' expects argument of type 'int', but argument 4 has type 'size_t' {aka 'long unsigned int'} [-Wformat=] 1986 | "%s: file read error: size = %d\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:144:56: note: in expansion of macro 'dev_fmt' 144 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:1985:25: note: in expansion of macro 'dev_err' 1985 | dev_err(tas_dev->dev, | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:1986:63: note: format string is defined here 1986 | "%s: file read error: size = %d\n", | ~^ | | | int | %ld
vim +875 sound/soc/codecs/tas2781-dsp.c
806 807 static int fw_parse_block_data(struct TFirmware *pFirmware, 808 struct TBlock *pBlock, const struct firmware *pFW, int offset) 809 { 810 unsigned char *pData = (unsigned char *)pFW->data; 811 int n; 812 813 if (offset + 4 > pFW->size) { 814 dev_err(pFirmware->dev, "%s: mnType error\n", __func__); 815 offset = -1; 816 goto out; 817 } 818 pBlock->mnType = SMS_HTONL(pData[offset], 819 pData[offset + 1], pData[offset + 2], pData[offset + 3]); 820 offset += 4; 821 822 if (pFirmware->fw_hdr.mnFixedHdr.mnDriverVersion >= 823 PPC_DRIVER_CRCCHK) { 824 if (offset + 1 > pFW->size) { 825 dev_err(pFirmware->dev, "%s: mbPChkSumPresent error\n", 826 __func__); 827 offset = -1; 828 goto out; 829 } 830 pBlock->mbPChkSumPresent = pData[offset]; 831 offset++; 832 833 if (offset + 1 > pFW->size) { 834 dev_err(pFirmware->dev, "%s: mnPChkSum error\n", 835 __func__); 836 offset = -1; 837 goto out; 838 } 839 pBlock->mnPChkSum = pData[offset]; 840 offset++; 841 842 if (offset + 1 > pFW->size) { 843 dev_err(pFirmware->dev, "%s: mbYChkSumPresent error\n", 844 __func__); 845 offset = -1; 846 goto out; 847 } 848 pBlock->mbYChkSumPresent = pData[offset]; 849 offset++; 850 851 if (offset + 1 > pFW->size) { 852 dev_err(pFirmware->dev, "%s: mnYChkSum error\n", 853 __func__); 854 offset = -1; 855 goto out; 856 } 857 pBlock->mnYChkSum = pData[offset]; 858 offset++; 859 } else { 860 pBlock->mbPChkSumPresent = 0; 861 pBlock->mbYChkSumPresent = 0; 862 } 863 if (offset + 4 > pFW->size) { 864 dev_err(pFirmware->dev, "%s: mnCommands error\n", __func__); 865 offset = -1; 866 goto out; 867 } 868 pBlock->mnCommands = SMS_HTONL(pData[offset], 869 pData[offset + 1], pData[offset + 2], pData[offset + 3]); 870 offset += 4; 871 872 n = pBlock->mnCommands * 4; 873 if (offset + n > pFW->size) { 874 dev_err(pFirmware->dev,
875 "%s: File Size(%u) error offset = %d n = %d\n",
876 __func__, pFW->size, offset, n); 877 offset = -1; 878 goto out; 879 } 880 pBlock->mpData = kmemdup(&pData[offset], n, GFP_KERNEL); 881 if (pBlock->mpData == NULL) { 882 offset = -1; 883 goto out; 884 } 885 offset += n; 886 out: 887 return offset; 888 } 889
Hi Kevin,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on broonie-sound/for-next] [also build test WARNING on linus/master v6.1 next-20221220] [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/Kevin-Lu/ALSA-SoC-Texas-Instr... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20221220151157.2247-1-luminlong%40139.com patch subject: [PATCH v1] ALSA SoC: Texas Instruments TAS2781 Audio Smart Amp config: arm-randconfig-r046-20221220 compiler: clang version 16.0.0 (https://github.com/llvm/llvm-project 98b13979fb05f3ed288a900deb843e7b27589e58) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # install arm cross compiling tool for clang build # apt-get install binutils-arm-linux-gnueabi # https://github.com/intel-lab-lkp/linux/commit/de2ce420cebecc02e8b8f1eda156c6... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Kevin-Lu/ALSA-SoC-Texas-Instruments-TAS2781-Audio-Smart-Amp/20221220-231421 git checkout de2ce420cebecc02e8b8f1eda156c623f9e8bc37 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash sound/soc/codecs/
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
All warnings (new ones prefixed by >>):
sound/soc/codecs/tas2781-i2c.c:241:6: warning: no previous prototype for function 'tasdevice_select_cfg_blk' [-Wmissing-prototypes]
void tasdevice_select_cfg_blk(void *pContext, int conf_no, ^ sound/soc/codecs/tas2781-i2c.c:241:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void tasdevice_select_cfg_blk(void *pContext, int conf_no, ^ static
sound/soc/codecs/tas2781-i2c.c:831:6: warning: no previous prototype for function 'tasdevice_regbin_ready' [-Wmissing-prototypes]
void tasdevice_regbin_ready(const struct firmware *pFW, ^ sound/soc/codecs/tas2781-i2c.c:831:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void tasdevice_regbin_ready(const struct firmware *pFW, ^ static
sound/soc/codecs/tas2781-i2c.c:1005:6: warning: no previous prototype for function 'tasdevice_config_info_remove' [-Wmissing-prototypes]
void tasdevice_config_info_remove(void *pContext) ^ sound/soc/codecs/tas2781-i2c.c:1005:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void tasdevice_config_info_remove(void *pContext) ^ static
sound/soc/codecs/tas2781-i2c.c:1433:5: warning: no previous prototype for function 'tasdevice_parse_dt' [-Wmissing-prototypes]
int tasdevice_parse_dt(struct tasdevice_priv *tas_dev) ^ sound/soc/codecs/tas2781-i2c.c:1433:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int tasdevice_parse_dt(struct tasdevice_priv *tas_dev) ^ static
sound/soc/codecs/tas2781-i2c.c:2005:6: warning: no previous prototype for function 'tasdevice_remove' [-Wmissing-prototypes]
void tasdevice_remove(struct tasdevice_priv *tas_dev) ^ sound/soc/codecs/tas2781-i2c.c:2005:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void tasdevice_remove(struct tasdevice_priv *tas_dev) ^ static 5 warnings generated. --
sound/soc/codecs/tas2781-dsp.c:226:5: warning: no previous prototype for function 'fw_parse_program_data_kernel' [-Wmissing-prototypes]
int fw_parse_program_data_kernel(struct tasdevice_priv *tas_dev, ^ sound/soc/codecs/tas2781-dsp.c:226:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int fw_parse_program_data_kernel(struct tasdevice_priv *tas_dev, ^ static
sound/soc/codecs/tas2781-dsp.c:298:5: warning: no previous prototype for function 'fw_parse_configuration_data_kernel' [-Wmissing-prototypes]
int fw_parse_configuration_data_kernel( ^ sound/soc/codecs/tas2781-dsp.c:298:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int fw_parse_configuration_data_kernel( ^ static
sound/soc/codecs/tas2781-dsp.c:398:5: warning: no previous prototype for function 'fw_parse_variable_header_kernel' [-Wmissing-prototypes]
int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_dev, ^ sound/soc/codecs/tas2781-dsp.c:398:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_dev, ^ static
sound/soc/codecs/tas2781-dsp.c:536:5: warning: no previous prototype for function 'tasdevice_load_block_kernel' [-Wmissing-prototypes]
int tasdevice_load_block_kernel(struct tasdevice_priv *pTAS2781, ^ sound/soc/codecs/tas2781-dsp.c:536:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int tasdevice_load_block_kernel(struct tasdevice_priv *pTAS2781, ^ static
sound/soc/codecs/tas2781-dsp.c:650:5: warning: no previous prototype for function 'fw_parse_variable_header_git' [-Wmissing-prototypes]
int fw_parse_variable_header_git(struct tasdevice_priv *tas_dev, ^ sound/soc/codecs/tas2781-dsp.c:650:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int fw_parse_variable_header_git(struct tasdevice_priv *tas_dev, ^ static
sound/soc/codecs/tas2781-dsp.c:712:5: warning: no previous prototype for function 'fw_parse_variable_header_cal' [-Wmissing-prototypes]
int fw_parse_variable_header_cal(struct tasdevice_priv *tas_dev, ^ sound/soc/codecs/tas2781-dsp.c:712:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int fw_parse_variable_header_cal(struct tasdevice_priv *tas_dev, ^ static 6 warnings generated.
vim +/tasdevice_select_cfg_blk +241 sound/soc/codecs/tas2781-i2c.c
240
241 void tasdevice_select_cfg_blk(void *pContext, int conf_no,
242 unsigned char block_type) 243 { 244 struct tasdevice_priv *tas_dev = 245 (struct tasdevice_priv *) pContext; 246 struct tasdevice_regbin *regbin = &(tas_dev->mtRegbin); 247 struct tasdevice_config_info **cfg_info = regbin->cfg_info; 248 int j = 0, k = 0, chn = 0, chnend = 0; 249 250 if (conf_no >= regbin->ncfgs || conf_no < 0 || NULL == cfg_info) { 251 dev_err(tas_dev->dev, 252 "conf_no should be not more than %u\n", 253 regbin->ncfgs); 254 goto out; 255 } else 256 dev_info(tas_dev->dev, 257 "select_cfg_blk: profile_conf_id = %d\n", 258 conf_no); 259 260 for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) { 261 unsigned int length = 0, rc = 0; 262 263 if (block_type > 5 || block_type < 2) { 264 dev_err(tas_dev->dev, 265 "ERROR!!!block_type should be in range from 2 to 5\n"); 266 goto out; 267 } 268 if (block_type != cfg_info[conf_no]->blk_data[j]->block_type) 269 continue; 270 dev_info(tas_dev->dev, 271 "select_cfg_blk: conf %d, block type:%s\t device idx = 0x%02x\n", 272 conf_no, blocktype[cfg_info[conf_no]->blk_data[j] 273 ->block_type-1], cfg_info[conf_no]->blk_data[j] 274 ->dev_idx); 275 276 for (k = 0; k < (int)cfg_info[conf_no]->blk_data[j] 277 ->nSublocks; k++) { 278 if (cfg_info[conf_no]->blk_data[j]->dev_idx) { 279 chn = 280 cfg_info[conf_no]->blk_data[j]->dev_idx 281 - 1; 282 chnend = 283 cfg_info[conf_no]->blk_data[j]->dev_idx; 284 } else { 285 chn = 0; 286 chnend = tas_dev->ndev; 287 } 288 for (; chn < chnend; chn++) 289 tas_dev->tasdevice[chn].bLoading = true; 290 291 rc = tasdevice_process_block(tas_dev, 292 cfg_info[conf_no]->blk_data[j]->regdata + 293 length, 294 cfg_info[conf_no]->blk_data[j]->dev_idx, 295 cfg_info[conf_no]->blk_data[j]->block_size - 296 length); 297 length += rc; 298 if (cfg_info[conf_no]->blk_data[j]->block_size < 299 length) { 300 dev_err(tas_dev->dev, 301 "select_cfg_blk: ERROR:%u %u out of memory\n", 302 length, 303 cfg_info[conf_no]->blk_data[j]->block_size); 304 break; 305 } 306 } 307 if (length != cfg_info[conf_no]->blk_data[j]->block_size) 308 dev_err(tas_dev->dev, 309 "select_cfg_blk: ERROR: %u %u size is not same\n", 310 length, 311 cfg_info[conf_no]->blk_data[j]->block_size); 312 313 } 314 315 out: 316 return; 317 } 318
Hi Kevin,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on broonie-sound/for-next] [also build test ERROR on linus/master v6.1 next-20221220] [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/Kevin-Lu/ALSA-SoC-Texas-Instr... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20221220151157.2247-1-luminlong%40139.com patch subject: [PATCH v1] ALSA SoC: Texas Instruments TAS2781 Audio Smart Amp config: powerpc-allmodconfig compiler: powerpc-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/de2ce420cebecc02e8b8f1eda156c6... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Kevin-Lu/ALSA-SoC-Texas-Instruments-TAS2781-Audio-Smart-Amp/20221220-231421 git checkout de2ce420cebecc02e8b8f1eda156c623f9e8bc37 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=powerpc olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=powerpc SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
All errors (new ones prefixed by >>, old ones prefixed by <<):
ERROR: modpost: "irq_to_desc" [sound/soc/codecs/snd-soc-tas2781.ko] undefined!
On Tue, Dec 20, 2022 at 11:11:57PM +0800, Kevin Lu wrote:
The TAS2781 driver implements a flexible and configurable register setting for one, two, even multiple TAS2781 chips. All the register setting are in a bin file. Almost no specific register setting can be found in the code.
This driver needs a very substantial rework to fit within the relevant kernel frameworks, even from a coding style level it's clearly not well integrated kernel code and the integration with several kernel frameworks all look to need rework.
+++ b/sound/soc/codecs/tas2781-dsp.c @@ -0,0 +1,2483 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
Please make the entire comment a C++ one so things look more intentional.
- Copyright (C) 2022 - 2023 Texas Instruments Incorporated
Given that it's still 2022 I rather doubt the 2023 copyright, though I guess we'll see some changes in 2023...
- The TAS2781 driver implements a flexible and configurable register setting
- for one, two, even multiple TAS2781 chips. All the register setting are in
- a bin file. Almost no specific register setting can be found in the code.
This isn't how we generally do drivers in Linux, we expect the device settings to be controlled via the ALSA userspace and kernel APIs. Downloadable firmwares are of course OK, and there are things like coefficient data which are naturally binary data, but normal device control should be done via the standard interfaces. Some of this does look like it might fit with firmware stuff but I'm not clear that it's all of it and the code generally doesn't look like it's trying very hard to fit within the standard kernel frameworks.
Given this I've not read too far into the driver, just skimmed it.
+#define TAS2781_CAL_BIN_PATH ("/lib/firmware/")
Let request_firmware() figure this out.
+struct TYCRC {
+const unsigned int BinFileformatVerInfo[][2] = {
+static int fw_parse_block_data_kernel(struct TFirmware *pFirmware,
- struct TBlock *pBlock, const struct firmware *pFW, int offset)
Please follow the kernel coding style for naming things, this makes it quite hard to read a lot of the code.
+static const unsigned char crc8_lookup_table[CRC8_TABLE_SIZE] = {
- 0x00, 0x4D, 0x9A, 0xD7, 0x79, 0x34, 0xE3, 0xAE,
We have lib/crc8.c.
+static const struct i2c_device_id tasdevice_id[] = {
- { "audev", GENERAL_AUDEV },
audev?
+static bool tasdevice_volatile(struct device *dev, unsigned int reg) +{
- return true;
+}
+static bool tasdevice_writeable(struct device *dev, unsigned int reg) +{
- return true;
+}
These are pointless, remove them.
+static const struct regmap_config tasdevice_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .writeable_reg = tasdevice_writeable,
- .volatile_reg = tasdevice_volatile,
- .cache_type = REGCACHE_FLAT,
- .max_register = 1 * 128,
+};
Given that the device has pages and books which aren't modelled here I can't see how a cache is going to work well, though since the driver marks every register as volatile it'll never do anything.
+static const struct of_device_id tasdevice_of_match[] = {
- { .compatible = "ti,audev" },
- { .compatible = "ti,tas2781" },
- {},
+};
This is adding a DT binding, the DT binding must be documented but is not.
+static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+{
This is just a straightforward register read, I don't see why it's not using the standard helpers.
- if (tas_dev->tas2781_set_global != NULL) {
ret = tasdevice_dev_write(tas_dev, tas_dev->ndev,
mc->reg, val);
if (ret)
dev_err(tas_dev->dev,
"%s, set digital vol error in global mode\n",
__func__);
- } else {
for (i = 0; i < tas_dev->ndev; i++) {
This appears to be trying to open code some mechanism for tying multiple devices together. Don't do that, if that feature is needed implement it at the framework level rather than trying to open code it in the driver.
The function is also failing to generate events, running mixer-test on a card with the device included should show this and potentially other issues.
+static int tasdevice_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
This doesn't actually appear to interact with the device which means it won't actually do anything. This is a problem with a lot of the callbacks.
+static void tasdevice_enable_irq(
- struct tasdevice_priv *tas_dev, bool enable)
+{
- struct irq_desc *desc = NULL;
- if (enable) {
if (tas_dev->mIrqInfo.mb_irq_enable)
return;
if (gpio_is_valid(tas_dev->mIrqInfo.mn_irq_gpio)) {
desc = irq_to_desc(tas_dev->mIrqInfo.mn_irq);
if (desc && desc->depth > 0)
enable_irq(tas_dev->mIrqInfo.mn_irq);
else
Drivers really shouldn't be peering inside irq descriptors, if you need to do this it generally indicates that whatever is done is a bad idea.
dev_info(tas_dev->dev,
"### irq already enabled\n");
Please don't spam the logs in normal operation, this is a problem throughout the driver.
+static int tasdevice_mute(struct snd_soc_dai *dai, int mute, int stream) +{
- tasdevice_set_power_state(tas_dev, mute);
mute is not a power management function, it should mute and unmute only. Power managment should be integrated with DAPM.
+static irqreturn_t tasdevice_irq_handler(int irq,
- void *dev_id)
+{
- struct tasdevice_priv *tas_dev = (struct tasdevice_priv *)dev_id;
+;
- /* get IRQ status after 100 ms */
- schedule_delayed_work(&tas_dev->mIrqInfo.irq_work,
msecs_to_jiffies(100));
- return IRQ_HANDLED;
+}
Why this delay?
- rc = of_property_read_u32(np, dts_glb_addr_tag,
&(tas_dev->glb_addr.mnDevAddr));
- if (rc) {
Please just put the DT properties into the code directly rather than adding a layer of indirection liek this, look at what other drivers do.
+static int tasdevice_regmap_write(
- struct tasdevice_priv *tas_dev,
- unsigned int reg, unsigned int value)
+{
- int nResult = 0;
- int retry_count = TASDEVICE_RETRY_COUNT;
- while (retry_count--) {
nResult = regmap_write(tas_dev->regmap, reg,
value);
if (nResult >= 0)
break;
usleep_range(5000, 5050);
Is the device really so unstable that it always needs multiple attempts to do any I/O?
+static int tasdevice_change_chn_book_page(
- struct tasdevice_priv *tas_dev, enum channel chn,
- int book, int page)
+{
regmap has generic support for register paging, use it.
Hi Kevin,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on broonie-sound/for-next] [also build test ERROR on linus/master v6.1 next-20221220] [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/Kevin-Lu/ALSA-SoC-Texas-Instr... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20221220151157.2247-1-luminlong%40139.com patch subject: [PATCH v1] ALSA SoC: Texas Instruments TAS2781 Audio Smart Amp config: arm-randconfig-r036-20221219 compiler: clang version 16.0.0 (https://github.com/llvm/llvm-project 98b13979fb05f3ed288a900deb843e7b27589e58) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # install arm cross compiling tool for clang build # apt-get install binutils-arm-linux-gnueabi # https://github.com/intel-lab-lkp/linux/commit/de2ce420cebecc02e8b8f1eda156c6... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Kevin-Lu/ALSA-SoC-Texas-Instruments-TAS2781-Audio-Smart-Amp/20221220-231421 git checkout de2ce420cebecc02e8b8f1eda156c623f9e8bc37 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
All error/warnings (new ones prefixed by >>, old ones prefixed by <<):
ERROR: modpost: "irq_to_desc" [sound/soc/codecs/snd-soc-tas2781.ko] undefined!
--
sound/soc/codecs/tas2781-i2c.c:241:6: warning: no previous prototype for function 'tasdevice_select_cfg_blk' [-Wmissing-prototypes]
void tasdevice_select_cfg_blk(void *pContext, int conf_no, ^ sound/soc/codecs/tas2781-i2c.c:241:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void tasdevice_select_cfg_blk(void *pContext, int conf_no, ^ static
sound/soc/codecs/tas2781-i2c.c:831:6: warning: no previous prototype for function 'tasdevice_regbin_ready' [-Wmissing-prototypes]
void tasdevice_regbin_ready(const struct firmware *pFW, ^ sound/soc/codecs/tas2781-i2c.c:831:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void tasdevice_regbin_ready(const struct firmware *pFW, ^ static
sound/soc/codecs/tas2781-i2c.c:1005:6: warning: no previous prototype for function 'tasdevice_config_info_remove' [-Wmissing-prototypes]
void tasdevice_config_info_remove(void *pContext) ^ sound/soc/codecs/tas2781-i2c.c:1005:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void tasdevice_config_info_remove(void *pContext) ^ static
sound/soc/codecs/tas2781-i2c.c:1433:5: warning: no previous prototype for function 'tasdevice_parse_dt' [-Wmissing-prototypes]
int tasdevice_parse_dt(struct tasdevice_priv *tas_dev) ^ sound/soc/codecs/tas2781-i2c.c:1433:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int tasdevice_parse_dt(struct tasdevice_priv *tas_dev) ^ static
sound/soc/codecs/tas2781-i2c.c:2005:6: warning: no previous prototype for function 'tasdevice_remove' [-Wmissing-prototypes]
void tasdevice_remove(struct tasdevice_priv *tas_dev) ^ sound/soc/codecs/tas2781-i2c.c:2005:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void tasdevice_remove(struct tasdevice_priv *tas_dev) ^ static 5 warnings generated. --
sound/soc/codecs/tas2781-dsp.c:226:5: warning: no previous prototype for function 'fw_parse_program_data_kernel' [-Wmissing-prototypes]
int fw_parse_program_data_kernel(struct tasdevice_priv *tas_dev, ^ sound/soc/codecs/tas2781-dsp.c:226:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int fw_parse_program_data_kernel(struct tasdevice_priv *tas_dev, ^ static
sound/soc/codecs/tas2781-dsp.c:298:5: warning: no previous prototype for function 'fw_parse_configuration_data_kernel' [-Wmissing-prototypes]
int fw_parse_configuration_data_kernel( ^ sound/soc/codecs/tas2781-dsp.c:298:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int fw_parse_configuration_data_kernel( ^ static
sound/soc/codecs/tas2781-dsp.c:398:5: warning: no previous prototype for function 'fw_parse_variable_header_kernel' [-Wmissing-prototypes]
int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_dev, ^ sound/soc/codecs/tas2781-dsp.c:398:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_dev, ^ static
sound/soc/codecs/tas2781-dsp.c:536:5: warning: no previous prototype for function 'tasdevice_load_block_kernel' [-Wmissing-prototypes]
int tasdevice_load_block_kernel(struct tasdevice_priv *pTAS2781, ^ sound/soc/codecs/tas2781-dsp.c:536:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int tasdevice_load_block_kernel(struct tasdevice_priv *pTAS2781, ^ static
sound/soc/codecs/tas2781-dsp.c:650:5: warning: no previous prototype for function 'fw_parse_variable_header_git' [-Wmissing-prototypes]
int fw_parse_variable_header_git(struct tasdevice_priv *tas_dev, ^ sound/soc/codecs/tas2781-dsp.c:650:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int fw_parse_variable_header_git(struct tasdevice_priv *tas_dev, ^ static
sound/soc/codecs/tas2781-dsp.c:712:5: warning: no previous prototype for function 'fw_parse_variable_header_cal' [-Wmissing-prototypes]
int fw_parse_variable_header_cal(struct tasdevice_priv *tas_dev, ^ sound/soc/codecs/tas2781-dsp.c:712:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int fw_parse_variable_header_cal(struct tasdevice_priv *tas_dev, ^ static 6 warnings generated.
Hi Kevin,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on broonie-sound/for-next] [also build test WARNING on linus/master v6.1 next-20221220] [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/Kevin-Lu/ALSA-SoC-Texas-Instr... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20221220151157.2247-1-luminlong%40139.com patch subject: [PATCH v1] ALSA SoC: Texas Instruments TAS2781 Audio Smart Amp config: arm64-randconfig-r033-20221219 compiler: aarch64-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/de2ce420cebecc02e8b8f1eda156c6... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Kevin-Lu/ALSA-SoC-Texas-Instruments-TAS2781-Audio-Smart-Amp/20221220-231421 git checkout de2ce420cebecc02e8b8f1eda156c623f9e8bc37 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm64 olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash arch/arm64/net/ kernel/bpf/ sound/soc/codecs/
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
All warnings (new ones prefixed by >>):
sound/soc/codecs/tas2781-i2c.c:241:6: warning: no previous prototype for 'tasdevice_select_cfg_blk' [-Wmissing-prototypes]
241 | void tasdevice_select_cfg_blk(void *pContext, int conf_no, | ^~~~~~~~~~~~~~~~~~~~~~~~
sound/soc/codecs/tas2781-i2c.c:831:6: warning: no previous prototype for 'tasdevice_regbin_ready' [-Wmissing-prototypes]
831 | void tasdevice_regbin_ready(const struct firmware *pFW, | ^~~~~~~~~~~~~~~~~~~~~~
sound/soc/codecs/tas2781-i2c.c:1005:6: warning: no previous prototype for 'tasdevice_config_info_remove' [-Wmissing-prototypes]
1005 | void tasdevice_config_info_remove(void *pContext) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
sound/soc/codecs/tas2781-i2c.c:1433:5: warning: no previous prototype for 'tasdevice_parse_dt' [-Wmissing-prototypes]
1433 | int tasdevice_parse_dt(struct tasdevice_priv *tas_dev) | ^~~~~~~~~~~~~~~~~~
sound/soc/codecs/tas2781-i2c.c:2005:6: warning: no previous prototype for 'tasdevice_remove' [-Wmissing-prototypes]
2005 | void tasdevice_remove(struct tasdevice_priv *tas_dev) | ^~~~~~~~~~~~~~~~ --
sound/soc/codecs/tas2781-dsp.c:226:5: warning: no previous prototype for 'fw_parse_program_data_kernel' [-Wmissing-prototypes]
226 | int fw_parse_program_data_kernel(struct tasdevice_priv *tas_dev, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
sound/soc/codecs/tas2781-dsp.c:298:5: warning: no previous prototype for 'fw_parse_configuration_data_kernel' [-Wmissing-prototypes]
298 | int fw_parse_configuration_data_kernel( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sound/soc/codecs/tas2781-dsp.c:398:5: warning: no previous prototype for 'fw_parse_variable_header_kernel' [-Wmissing-prototypes]
398 | int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_dev, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sound/soc/codecs/tas2781-dsp.c:536:5: warning: no previous prototype for 'tasdevice_load_block_kernel' [-Wmissing-prototypes]
536 | int tasdevice_load_block_kernel(struct tasdevice_priv *pTAS2781, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~
sound/soc/codecs/tas2781-dsp.c:650:5: warning: no previous prototype for 'fw_parse_variable_header_git' [-Wmissing-prototypes]
650 | int fw_parse_variable_header_git(struct tasdevice_priv *tas_dev, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
sound/soc/codecs/tas2781-dsp.c:712:5: warning: no previous prototype for 'fw_parse_variable_header_cal' [-Wmissing-prototypes]
712 | int fw_parse_variable_header_cal(struct tasdevice_priv *tas_dev, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from include/linux/device.h:15, from include/linux/acpi.h:15, from include/linux/i2c.h:13, from sound/soc/codecs/tas2781-dsp.c:17: sound/soc/codecs/tas2781-dsp.c: In function 'fw_parse_block_data':
sound/soc/codecs/tas2781-dsp.c:875:25: warning: format '%u' expects argument of type 'unsigned int', but argument 4 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
875 | "%s: File Size(%u) error offset = %d n = %d\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:144:56: note: in expansion of macro 'dev_fmt' 144 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:874:17: note: in expansion of macro 'dev_err' 874 | dev_err(pFirmware->dev, | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:875:41: note: format string is defined here 875 | "%s: File Size(%u) error offset = %d n = %d\n", | ~^ | | | unsigned int | %lu sound/soc/codecs/tas2781-dsp.c: In function 'fw_parse_header':
sound/soc/codecs/tas2781-dsp.c:1312:39: warning: format '%d' expects argument of type 'int', but argument 3 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
1312 | dev_err(tas_dev->dev, "File size not match, %d %d", pFW->size, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:144:56: note: in expansion of macro 'dev_fmt' 144 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:1312:17: note: in expansion of macro 'dev_err' 1312 | dev_err(tas_dev->dev, "File size not match, %d %d", pFW->size, | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:1312:62: note: format string is defined here 1312 | dev_err(tas_dev->dev, "File size not match, %d %d", pFW->size, | ~^ | | | int | %ld sound/soc/codecs/tas2781-dsp.c: In function 'tas2781_load_calibration': sound/soc/codecs/tas2781-dsp.c:1986:33: warning: format '%d' expects argument of type 'int', but argument 4 has type 'size_t' {aka 'long unsigned int'} [-Wformat=] 1986 | "%s: file read error: size = %d\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:144:56: note: in expansion of macro 'dev_fmt' 144 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:1985:25: note: in expansion of macro 'dev_err' 1985 | dev_err(tas_dev->dev, | ^~~~~~~ sound/soc/codecs/tas2781-dsp.c:1986:63: note: format string is defined here 1986 | "%s: file read error: size = %d\n", | ~^ | | | int | %ld
vim +/tasdevice_select_cfg_blk +241 sound/soc/codecs/tas2781-i2c.c
240
241 void tasdevice_select_cfg_blk(void *pContext, int conf_no,
242 unsigned char block_type) 243 { 244 struct tasdevice_priv *tas_dev = 245 (struct tasdevice_priv *) pContext; 246 struct tasdevice_regbin *regbin = &(tas_dev->mtRegbin); 247 struct tasdevice_config_info **cfg_info = regbin->cfg_info; 248 int j = 0, k = 0, chn = 0, chnend = 0; 249 250 if (conf_no >= regbin->ncfgs || conf_no < 0 || NULL == cfg_info) { 251 dev_err(tas_dev->dev, 252 "conf_no should be not more than %u\n", 253 regbin->ncfgs); 254 goto out; 255 } else 256 dev_info(tas_dev->dev, 257 "select_cfg_blk: profile_conf_id = %d\n", 258 conf_no); 259 260 for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) { 261 unsigned int length = 0, rc = 0; 262 263 if (block_type > 5 || block_type < 2) { 264 dev_err(tas_dev->dev, 265 "ERROR!!!block_type should be in range from 2 to 5\n"); 266 goto out; 267 } 268 if (block_type != cfg_info[conf_no]->blk_data[j]->block_type) 269 continue; 270 dev_info(tas_dev->dev, 271 "select_cfg_blk: conf %d, block type:%s\t device idx = 0x%02x\n", 272 conf_no, blocktype[cfg_info[conf_no]->blk_data[j] 273 ->block_type-1], cfg_info[conf_no]->blk_data[j] 274 ->dev_idx); 275 276 for (k = 0; k < (int)cfg_info[conf_no]->blk_data[j] 277 ->nSublocks; k++) { 278 if (cfg_info[conf_no]->blk_data[j]->dev_idx) { 279 chn = 280 cfg_info[conf_no]->blk_data[j]->dev_idx 281 - 1; 282 chnend = 283 cfg_info[conf_no]->blk_data[j]->dev_idx; 284 } else { 285 chn = 0; 286 chnend = tas_dev->ndev; 287 } 288 for (; chn < chnend; chn++) 289 tas_dev->tasdevice[chn].bLoading = true; 290 291 rc = tasdevice_process_block(tas_dev, 292 cfg_info[conf_no]->blk_data[j]->regdata + 293 length, 294 cfg_info[conf_no]->blk_data[j]->dev_idx, 295 cfg_info[conf_no]->blk_data[j]->block_size - 296 length); 297 length += rc; 298 if (cfg_info[conf_no]->blk_data[j]->block_size < 299 length) { 300 dev_err(tas_dev->dev, 301 "select_cfg_blk: ERROR:%u %u out of memory\n", 302 length, 303 cfg_info[conf_no]->blk_data[j]->block_size); 304 break; 305 } 306 } 307 if (length != cfg_info[conf_no]->blk_data[j]->block_size) 308 dev_err(tas_dev->dev, 309 "select_cfg_blk: ERROR: %u %u size is not same\n", 310 length, 311 cfg_info[conf_no]->blk_data[j]->block_size); 312 313 } 314 315 out: 316 return; 317 } 318
Hi Kevin,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on broonie-sound/for-next] [also build test WARNING on linus/master v6.1 next-20221220] [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/Kevin-Lu/ALSA-SoC-Texas-Instr... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20221220151157.2247-1-luminlong%40139.com patch subject: [PATCH v1] ALSA SoC: Texas Instruments TAS2781 Audio Smart Amp config: s390-randconfig-m041-20221218 compiler: s390-linux-gcc (GCC) 12.1.0
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
New smatch warnings: sound/soc/codecs/tas2781-i2c.c:224 tasdevice_process_block() warn: inconsistent indenting sound/soc/codecs/tas2781-i2c.c:476 tas2781_digital_putvol() warn: unsigned 'val' is never less than zero. sound/soc/codecs/tas2781-i2c.c:549 tas2781_amp_putvol() warn: unsigned 'val' is never less than zero. sound/soc/codecs/tas2781-i2c.c:846 tasdevice_regbin_ready() error: we previously assumed 'tas_dev' could be null (see line 845) sound/soc/codecs/tas2781-i2c.c:2035 tasdevice_pm_suspend() error: we previously assumed 'tas_dev' could be null (see line 2034) sound/soc/codecs/tas2781-dsp.c:1831 tasdevice_load_block() warn: inconsistent indenting sound/soc/codecs/tas2781-dsp.c:797 tas2781_clear_Calfirmware() warn: inconsistent indenting sound/soc/codecs/tas2781-dsp.c:2239 tasdevice_dsp_remove() warn: inconsistent indenting
Old smatch warnings: sound/soc/codecs/tas2781-dsp.c:1848 tasdevice_load_block() warn: inconsistent indenting sound/soc/codecs/tas2781-dsp.c:2243 tasdevice_dsp_remove() warn: inconsistent indenting sound/soc/codecs/tas2781-dsp.c:2253 tasdevice_dsp_remove() warn: inconsistent indenting sound/soc/codecs/tas2781-dsp.c:2274 tasdevice_dsp_remove() warn: inconsistent indenting
vim +224 sound/soc/codecs/tas2781-i2c.c
99 100 int tasdevice_process_block(void *pContext, 101 unsigned char *data, unsigned char dev_idx, int sublocksize) 102 { 103 struct tasdevice_priv *tas_dev = 104 (struct tasdevice_priv *)pContext; 105 unsigned char subblk_typ = data[1]; 106 int subblk_offset = 2; 107 int chn = 0, chnend = 0; 108 int rc = 0; 109 int blktyp = dev_idx & 0xC0, idx = dev_idx & 0x3F; 110 bool bError = false; 111 112 if (idx) { 113 chn = idx-1; 114 chnend = idx; 115 } else { 116 if (tas_dev->glb_addr.mnDevAddr) { 117 chn = tas_dev->ndev; 118 chnend = tas_dev->ndev + 1; 119 } else { 120 chn = 0; 121 chnend = tas_dev->ndev; 122 } 123 } 124 125 for (; chn < chnend; chn++) { 126 if (tas_dev->glb_addr.mnDevAddr == 0 && 127 tas_dev->tasdevice[chn].bLoading == false) 128 continue; 129 130 bError = false; 131 subblk_offset = 2; 132 switch (subblk_typ) { 133 case TASDEVICE_CMD_SING_W: { 134 int i = 0; 135 unsigned short len = SMS_HTONS(data[2], data[3]); 136 137 subblk_offset += 2; 138 if (subblk_offset + 4 * len > sublocksize) { 139 dev_err(tas_dev->dev, 140 "process_block: Out of memory\n"); 141 bError = true; 142 break; 143 } 144 145 for (i = 0; i < len; i++) { 146 rc = tasdevice_dev_write(tas_dev, chn, 147 TASDEVICE_REG(data[subblk_offset], 148 data[subblk_offset + 1], 149 data[subblk_offset + 2]), 150 data[subblk_offset + 3]); 151 if (rc < 0) { 152 bError = true; 153 dev_err(tas_dev->dev, 154 "process_block: single write error\n"); 155 } 156 subblk_offset += 4; 157 } 158 } 159 break; 160 case TASDEVICE_CMD_BURST: { 161 unsigned short len = SMS_HTONS(data[2], data[3]); 162 163 subblk_offset += 2; 164 if (subblk_offset + 4 + len > sublocksize) { 165 dev_err(tas_dev->dev, 166 "process_block: BURST Out of memory\n"); 167 bError = true; 168 break; 169 } 170 if (len % 4) { 171 dev_err(tas_dev->dev, 172 "process_block: Burst len(%u) can be divided by 4\n", 173 len); 174 break; 175 } 176 177 rc = tasdevice_dev_bulk_write(tas_dev, chn, 178 TASDEVICE_REG(data[subblk_offset], 179 data[subblk_offset + 1], 180 data[subblk_offset + 2]), 181 &(data[subblk_offset + 4]), len); 182 if (rc < 0) { 183 bError = true; 184 dev_err(tas_dev->dev, 185 "process_block: bulk_write error = %d\n", 186 rc); 187 } 188 subblk_offset += (len + 4); 189 } 190 break; 191 case TASDEVICE_CMD_DELAY: { 192 unsigned short delay_time = 0; 193 194 if (subblk_offset + 2 > sublocksize) { 195 dev_err(tas_dev->dev, 196 "process_block: deley Out of memory\n"); 197 bError = true; 198 break; 199 } 200 delay_time = SMS_HTONS(data[2], data[3]); 201 usleep_range(delay_time*1000, delay_time*1000); 202 subblk_offset += 2; 203 } 204 break; 205 case TASDEVICE_CMD_FIELD_W: 206 if (subblk_offset + 6 > sublocksize) { 207 dev_err(tas_dev->dev, 208 "process_block: bit write Out of memory\n"); 209 bError = true; 210 break; 211 } 212 rc = tasdevice_dev_update_bits(tas_dev, chn, 213 TASDEVICE_REG(data[subblk_offset + 2], 214 data[subblk_offset + 3], 215 data[subblk_offset + 4]), 216 data[subblk_offset + 1], 217 data[subblk_offset + 5]); 218 if (rc < 0) { 219 bError = true; 220 dev_err(tas_dev->dev, 221 "process_block: update_bits error = %d\n", rc); 222 } 223 subblk_offset += 6;
224 break;
225 default: 226 break; 227 }; 228 if (bError == true && blktyp != 0) { 229 if (blktyp == 0x80) { 230 tas_dev->tasdevice[chn].mnCurrentProgram = -1; 231 tas_dev->tasdevice[chn].mnCurrentConfiguration = 232 -1; 233 } else 234 tas_dev->tasdevice[chn].mnCurrentConfiguration = 235 -1; 236 } 237 } 238 return subblk_offset; 239 } 240
participants (3)
-
kernel test robot
-
Kevin Lu
-
Mark Brown