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