Received: by 2002:a05:6358:9144:b0:117:f937:c515 with SMTP id r4csp3896357rwr; Sun, 7 May 2023 22:53:56 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7pjXrEpS5F/Hk+FHVwz67xtPNaQVaXo7we2bw9ab6T21eR9e1I2Jb+JBGltOhY6Z/FjJCz X-Received: by 2002:a05:6a20:1610:b0:f0:6f7f:4f2b with SMTP id l16-20020a056a20161000b000f06f7f4f2bmr12038087pzj.1.1683525235819; Sun, 07 May 2023 22:53:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1683525235; cv=none; d=google.com; s=arc-20160816; b=Vp6SX+QrQLpxtuyskQOZGDCMchDEVLlN75x+BPumDvTpOktGiLuWXD2YZPoY7Fln8+ cx+wKEhFPedJKVWMedYK2YUw10xqO/9v4zY1hjzq+hIr6hJGrreh7mEQHm1c09rmuK3j KwKtAHPHnKucLqePew0oRBxiADlM8xBRwPlYhXopXj5I5GaETAMdpvW//G7c9G3Qd6tt COsBoq5K1t4E3QsdrhcBsjlVEjx+/2FT8tc7WkNGOq6q8BbhFwWBJ5DBbyYVJOzHBoNF tJuYjQmcPzToIT7IQZV1CWB/MYJ4vKZY+NYpUKW2M1BgrHqdhozeJRRwiIqTkHai0MqC 8x0Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from; bh=72kgj0ekTU/pCZFaTr37yHJ0idXSHOd7dQJyH8TxH88=; b=gbEAzxEJP/nE0hJDPsNwW8WGmdvjafxEWua6S4WoDTiIct8CPfkBoX48Bn0lzy+OJF mR8Lhdhbh4k+7vQNbmoSB+E5iNCqqG6dhnQvbbUi0celJaQxOVWfdv+jZ20XMrReQQDg gljDAUDhl7nWlhr5JeeRTv1ajZ+EwMpcOUi2MsaeIisWrfxQwkOkTdvT4WOIqezft+RJ E+nvxLk2T9/QIVCkROmUIiwWGspF8lEDhevrRrPYLjfy8ruhwWoz4X/Fs+dTeDm/WPXc 9TB07JcD80tC74kLRghcaommjEs1OxjZ/bi3cyoA3gyBjIhiX/qhsZ2DJGax5/X4p1dE DweA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id s18-20020a639252000000b00513162c223bsi7051789pgn.298.2023.05.07.22.53.43; Sun, 07 May 2023 22:53:55 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232656AbjEHFqT (ORCPT + 99 others); Mon, 8 May 2023 01:46:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54756 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231631AbjEHFqQ (ORCPT ); Mon, 8 May 2023 01:46:16 -0400 Received: from n169-111.mail.139.com (n169-111.mail.139.com [120.232.169.111]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B620418173; Sun, 7 May 2023 22:45:53 -0700 (PDT) X-RM-TagInfo: emlType=0 X-RM-SPAM: X-RM-SPAM-FLAG: 00000000 Received: from localhost.localdomain (unknown[117.136.79.133]) by rmsmtp-lg-appmail-13-12002 (RichMail) with SMTP id 2ee264588c7d0c4-34377; Mon, 08 May 2023 13:45:39 +0800 (CST) X-RM-TRANSID: 2ee264588c7d0c4-34377 From: Shenghao Ding <13916275206@139.com> To: broonie@kernel.org, devicetree@vger.kernel.org, krzysztof.kozlowski+dt@linaro.org, robh+dt@kernel.org, lgirdwood@gmail.com, perex@perex.cz, pierre-louis.bossart@linux.intel.com Cc: kevin-lu@ti.com, shenghao-ding@ti.com, alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, x1077012@ti.com, peeyush@ti.com, navada@ti.com, gentuser@gmail.com, Ryan_Chu@wistron.com, Sam_Wu@wistron.com, Shenghao Ding <13916275206@139.com> Subject: [PATCH v2 3/5] ASoC: tas2781: firmware lib Date: Mon, 8 May 2023 13:45:30 +0800 Message-Id: <20230508054530.738-1-13916275206@139.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-1.7 required=5.0 tests=BAYES_00, FREEMAIL_ENVFROM_END_DIGIT,FREEMAIL_FROM,FROM_LOCAL_DIGITS, FROM_LOCAL_HEX,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Create tas2781 firmware lib. Signed-off-by: Shenghao Ding <13916275206@139.com> --- Changes in v2: - fixed issue | Reported-by: kernel test robot | Link: https://lore.kernel.org/oe-kbuild-all/202305022033.LiI7Ojm4-lkp@intel.com/ - Move back to codecs folder, this firmware lib is special for the audio cases Changes to be committed: new file: sound/soc/codecs/tas2781-fmwlib.c --- sound/soc/codecs/tas2781-fmwlib.c | 2383 +++++++++++++++++++++++++++++ 1 file changed, 2383 insertions(+) create mode 100644 sound/soc/codecs/tas2781-fmwlib.c diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c new file mode 100644 index 000000000000..b79d2ef7c77d --- /dev/null +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -0,0 +1,2383 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// tasdevice-fmw.c -- TASDEVICE firmware support +// +// Copyright 2023 Texas Instruments, Inc. +// +// Author: Shenghao Ding + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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 + +struct tas_crc { + unsigned char offset; + unsigned char len; +}; + +const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = { + 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4 +}; + +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(struct tasdevice_config_info), GFP_KERNEL); + if (!cfg_info) { + *status = -ENOMEM; + goto out; + } + + if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) { + if (config_offset + 64 > (int)config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add conf: Out of boundary\n"); + goto out; + } + config_offset += 64; + } + + if (config_offset + 4 > (int)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 = + be32_to_cpup((__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(struct tasdev_blk_data *), 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); + break; + } + bk_da[i] = kzalloc(sizeof(struct tasdev_blk_data), GFP_KERNEL); + if (!bk_da[i]) { + *status = -ENOMEM; + break; + } + + bk_da[i]->dev_idx = config_data[config_offset]; + config_offset++; + + bk_da[i]->block_type = config_data[config_offset]; + config_offset++; + + if (bk_da[i]->block_type == TASDEVICE_BIN_BLK_PRE_POWER_UP) { + if (bk_da[i]->dev_idx == 0) + cfg_info->active_dev = 1; + else + cfg_info->active_dev = 1 << + (bk_da[i]->dev_idx - 1); + + } + bk_da[i]->yram_checksum = + be16_to_cpup((__be16 *)&config_data[config_offset]); + config_offset += 2; + bk_da[i]->block_size = + be32_to_cpup((__be32 *)&config_data[config_offset]); + config_offset += 4; + + bk_da[i]->n_subblks = + be32_to_cpup((__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); + break; + } + /* 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; + goto out; + } + + config_offset += bk_da[i]->block_size; + cfg_info->real_nblocks += 1; + } + +out: + return cfg_info; +} + +int tasdevice_rca_parser(void *context, const struct firmware *fmw) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_config_info **cfg_info; + struct tasdevice_rca_hdr *fw_hdr; + struct tasdevice_rca *rca; + unsigned int total_config_sz = 0; + unsigned char *buf; + int offset = 0; + int ret = 0; + int i; + + rca = &(tas_priv->rcabin); + fw_hdr = &(rca->fw_hdr); + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "Failed to read %s\n", + tas_priv->rca_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + buf = (unsigned char *)fmw->data; + + fw_hdr->img_sz = be32_to_cpup((__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; + ret = -EINVAL; + goto out; + } + + fw_hdr->checksum = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + fw_hdr->binary_version_num = be32_to_cpup((__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; + ret = -EINVAL; + goto out; + } + offset += 4; + fw_hdr->drv_fw_version = be32_to_cpup((__be32 *)&buf[offset]); + offset += 8; + fw_hdr->plat_type = buf[offset]; + offset += 1; + fw_hdr->dev_family = buf[offset]; + offset += 1; + fw_hdr->reserve = buf[offset]; + offset += 1; + fw_hdr->ndev = buf[offset]; + offset += 1; + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "ndev(%u) in rcabin mismatch ndev(%u) in DTS\n", + fw_hdr->ndev, tas_priv->ndev); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) { + dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n"); + ret = -EINVAL; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++) + fw_hdr->devs[i] = buf[offset]; + + fw_hdr->nconfig = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) { + fw_hdr->config_size[i] = be32_to_cpup((__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 error!\n"); + ret = -EINVAL; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL); + if (!cfg_info) { + ret = -ENOMEM; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + 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; + goto out; + } + offset += (int)fw_hdr->config_size[i]; + } +out: + return ret; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_rca_parser, SND_SOC_TAS2781_FMWLIB); + +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__); + offset = -EINVAL; + goto out; + } + + /* convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + block->type = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + block->is_pchksum_present = data[offset]; + offset++; + + block->pchksum = data[offset]; + offset++; + + block->is_ychksum_present = data[offset]; + offset++; + + block->ychksum = data[offset]; + offset++; + + block->blk_size = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + block->nr_subblocks = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + if (offset + block->blk_size > fmw->size) { + dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__); + offset = -EINVAL; + goto out; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL); + if (!block->data) { + offset = -ENOMEM; + goto out; + } + offset += block->blk_size; + +out: + 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__); + offset = -EINVAL; + goto out; + } + img_data->nr_blk = be32_to_cpup((__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) { + offset = -ENOMEM; + goto out; + } + + 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) { + offset = -EINVAL; + break; + } + } + +out: + 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__); + offset = -EINVAL; + goto out; + } + /*skip 72 unused byts*/ + offset += 72; + + offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + 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__); + offset = -EINVAL; + goto out; + } + 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) + goto out; + } + +out: + return offset; +} + +static int fw_parse_variable_header_kernel( + struct tasdevice_priv *tas_priv, const struct firmware *fmw, + int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + struct tasdevice_prog *program; + struct tasdevice_config *config; + const unsigned char *buf = fmw->data; + unsigned short max_confs; + unsigned int i; + + if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + fw_hdr->device_family = be16_to_cpup((__be16 *)&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s:not TAS device\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 2; + fw_hdr->device = be16_to_cpup((__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); + offset = -EINVAL; + goto out; + } + offset += 2; + fw_hdr->ndev = deviceNumber[fw_hdr->device]; + + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n", + __func__, fw_hdr->ndev, tas_priv->ndev); + offset = -EINVAL; + goto out; + } + + tas_fmw->nr_programs = be32_to_cpup((__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"); + offset = -EINVAL; + goto out; + } + + tas_fmw->programs = kcalloc(tas_fmw->nr_programs, + sizeof(struct tasdevice_prog), GFP_KERNEL); + if (!tas_fmw->programs) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &(tas_fmw->programs[i]); + program->prog_size = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + } + + /* Skip the unused prog_size */ + offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs); + + tas_fmw->nr_configurations = be32_to_cpup((__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 = (fw_hdr->ndev >= 4) ? + TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS : + 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__); + offset = -EINVAL; + goto out; + } + + if (offset + 4 * max_confs > fmw->size) { + dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__); + offset = -EINVAL; + goto out; + } + + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(struct tasdevice_config), GFP_KERNEL); + if (!tas_fmw->configs) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + config = &(tas_fmw->configs[i]); + config->cfg_size = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + } + + /* Skip the unused configs */ + offset += 4 * (max_confs - tas_fmw->nr_programs); + +out: + return offset; +} + +static int tasdevice_process_block(void *context, unsigned char *data, + unsigned char dev_idx, int sublocksize) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + int subblk_offset, chn, chnend, rc; + unsigned char subblk_typ = data[1]; + int blktyp = dev_idx & 0xC0; + int idx = dev_idx & 0x3F; + bool is_err = false; + + if (idx) { + chn = idx - 1; + chnend = idx; + } else { + if (tas_priv->set_global_mode) { + chn = tas_priv->ndev; + chnend = tas_priv->ndev + 1; + } else { + chn = 0; + chnend = tas_priv->ndev; + } + } + + for (; chn < chnend; chn++) { + if (!tas_priv->set_global_mode && + tas_priv->tasdevice[chn].is_loading == false) + continue; + + is_err = false; + subblk_offset = 2; + switch (subblk_typ) { + case TASDEVICE_CMD_SING_W: { + int i; + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + + subblk_offset += 2; + if (subblk_offset + 4 * len > sublocksize) { + dev_err(tas_priv->dev, + "process_block: Out of boundary\n"); + is_err = true; + break; + } + + for (i = 0; i < len; i++) { + rc = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + data[subblk_offset + 3]); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "process_block: single write error\n"); + } + subblk_offset += 4; + } + } + break; + case TASDEVICE_CMD_BURST: { + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + + subblk_offset += 2; + if (subblk_offset + 4 + len > sublocksize) { + dev_err(tas_priv->dev, + "%s: BST Out of boundary\n", + __func__); + is_err = true; + break; + } + if (len % 4) { + dev_err(tas_priv->dev, + "%s:Bst-len(%u)not div by 4\n", + __func__, len); + break; + } + + rc = tasdevice_dev_bulk_write(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + &(data[subblk_offset + 4]), len); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "%s: bulk_write error = %d\n", + __func__, rc); + } + subblk_offset += (len + 4); + } + break; + case TASDEVICE_CMD_DELAY: { + unsigned int sleep_time = 0; + + if (subblk_offset + 2 > sublocksize) { + dev_err(tas_priv->dev, + "%s: delay Out of boundary\n", + __func__); + is_err = true; + break; + } + sleep_time = be16_to_cpup((__be16 *)&data[2]) * 1000; + usleep_range(sleep_time, sleep_time + 50); + subblk_offset += 2; + } + break; + case TASDEVICE_CMD_FIELD_W: + if (subblk_offset + 6 > sublocksize) { + dev_err(tas_priv->dev, + "%s: bit write Out of boundary\n", + __func__); + is_err = true; + break; + } + rc = tasdevice_dev_update_bits(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset + 2], + data[subblk_offset + 3], + data[subblk_offset + 4]), + data[subblk_offset + 1], + data[subblk_offset + 5]); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "%s: update_bits error = %d\n", + __func__, rc); + } + subblk_offset += 6; + break; + default: + break; + } + if (is_err == true && blktyp != 0) { + if (blktyp == 0x80) { + tas_priv->tasdevice[chn].cur_prog = -1; + tas_priv->tasdevice[chn].cur_conf = -1; + } else + tas_priv->tasdevice[chn].cur_conf = -1; + } + } + + return subblk_offset; +} + +void tasdevice_select_cfg_blk(void *pContext, int conf_no, + unsigned char block_type) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) pContext; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdev_blk_data **blk_data; + int j, k, chn, chnend; + + if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) { + dev_err(tas_priv->dev, "conf_no should be not more than %u\n", + rca->ncfgs); + return; + } + blk_data = cfg_info[conf_no]->blk_data; + + for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) { + unsigned int length = 0, rc = 0; + + if (block_type > 5 || block_type < 2) { + dev_err(tas_priv->dev, + "block_type should be in range from 2 to 5\n"); + break; + } + if (block_type != blk_data[j]->block_type) + continue; + + for (k = 0; k < (int)blk_data[j]->n_subblks; k++) { + if (blk_data[j]->dev_idx) { + chn = blk_data[j]->dev_idx - 1; + chnend = blk_data[j]->dev_idx; + } else { + chn = 0; + chnend = tas_priv->ndev; + } + for (; chn < chnend; chn++) + tas_priv->tasdevice[chn].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); + } +} +EXPORT_SYMBOL_NS_GPL(tasdevice_select_cfg_blk, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_load_block_kernel( + struct tasdevice_priv *tasdevice, struct tasdev_blk *block) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tasdevice->fmw->fw_hdr); + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr); + const unsigned int blk_size = block->blk_size; + unsigned int i, length; + unsigned char *data = block->data; + unsigned char dev_idx = 0; + + if (fw_fixed_hdr->ppcver >= PPC3_VERSION) { + switch (block->type) { + case MAIN_ALL_DEVICES_1X: + dev_idx = 0x80; + break; + case MAIN_DEVICE_A_1X: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A_1X: + case PRE_DEVICE_A_1X: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B_1X: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B_1X: + case PRE_DEVICE_B_1X: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C_1X: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C_1X: + case PRE_DEVICE_C_1X: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D_1X: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D_1X: + case PRE_DEVICE_D_1X: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } else { + switch (block->type) { + case MAIN_ALL_DEVICES: + dev_idx = 0|0x80; + break; + case MAIN_DEVICE_A: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } + + for (i = 0, length = 0; i < block->nr_subblocks; i++) { + int rc = tasdevice_process_block(tasdevice, data + length, + dev_idx, blk_size - length); + if (rc < 0) { + dev_err(tasdevice->dev, + "%s: %u %u sublock write error\n", + __func__, length, blk_size); + break; + } + length += (unsigned int)rc; + if (blk_size < length) { + dev_err(tasdevice->dev, "%s: %u %u out of boundary\n", + __func__, length, blk_size); + break; + } + } + + return 0; +} + +static int fw_parse_variable_hdr(struct tasdevice_priv + *tas_priv, struct tasdevice_dspfw_hdr *fw_hdr, + const struct firmware *fmw, int offset) +{ + const unsigned char *buf = fmw->data; + int len = strlen((char *)&buf[offset]); + + len++; + + if (offset + len + 8 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + + offset += len; + + fw_hdr->device_family = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s: not TAS device\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 4; + + fw_hdr->device = be32_to_cpup((__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); + offset = -EINVAL; + goto out; + } + offset += 4; + fw_hdr->ndev = deviceNumber[fw_hdr->device]; + +out: + 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); + if (offset < 0) + goto out; + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n", + __func__, fw_hdr->ndev, tas_priv->ndev); + offset = -EINVAL; + } + +out: + 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__); + offset = -EINVAL; + goto out; + } + block->type = be32_to_cpup((__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"); + offset = -EINVAL; + goto out; + } + 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 = be32_to_cpup((__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); + offset = -EINVAL; + goto out; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], n, GFP_KERNEL); + if (!block->data) { + offset = -ENOMEM; + goto out; + } + offset += n; + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_data(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = (unsigned char *)fmw->data; + struct tasdev_blk *blk; + unsigned int i; + int n; + + if (offset + 64 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Name error\n", __func__); + offset = -EINVAL; + goto out; + } + 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__); + offset = -EINVAL; + goto out; + } + offset += n; + img_data->nr_blk = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(struct tasdev_blk), GFP_KERNEL); + if (!img_data->dev_blks) { + offset = -ENOMEM; + goto out; + } + 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) { + offset = -EINVAL; + goto out; + } + } + +out: + 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__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_programs = be16_to_cpup((__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__); + goto out; + } + + tas_fmw->programs = + kcalloc(tas_fmw->nr_programs, sizeof(struct tasdevice_prog), + GFP_KERNEL); + if (!tas_fmw->programs) { + offset = -ENOMEM; + goto out; + } + 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__); + offset = -EINVAL; + goto out; + } + 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"); + offset = -EINVAL; + goto out; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw, + offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_configuration_data( + struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + struct tasdevice_config *config; + unsigned int i; + int n; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_configurations = be16_to_cpup((__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*/ + goto out; + } + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(struct tasdevice_config), GFP_KERNEL); + if (!tas_fmw->configs) { + offset = -ENOMEM; + goto out; + } + 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"); + offset = -EINVAL; + goto out; + } + 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"); + offset = -EINVAL; + goto out; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &(config->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static bool check_inpage_yram_rg(struct tas_crc *cd, + unsigned char reg, unsigned char len) +{ + bool in = false; + + + if (reg <= TAS2781_YRAM5_END_REG && + reg >= TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_END_REG) + cd->len = TAS2781_YRAM5_END_REG - reg + 1; + else + cd->len = len; + cd->offset = reg; + in = true; + } else if (reg < TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_START_REG) { + cd->offset = TAS2781_YRAM5_START_REG; + cd->len = len - TAS2781_YRAM5_START_REG + reg; + in = true; + } + } + + return in; +} + +static bool check_inpage_yram_bk1(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (page == TAS2781_YRAM1_PAGE) { + if (reg >= TAS2781_YRAM1_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg + len > TAS2781_YRAM1_START_REG) { + cd->offset = TAS2781_YRAM1_START_REG; + cd->len = len - TAS2781_YRAM1_START_REG + reg; + in = true; + } + } else if (page == TAS2781_YRAM3_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + + return in; +} + +/* Return Code: + * true -- the registers are in the inpage yram + * false -- the registers are NOT in the inpage yram + */ +static bool check_inpage_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1) { + in = check_inpage_yram_bk1(cd, page, reg, len); + goto end; + } + if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + +end: + return in; +} + +static bool check_inblock_yram_bk(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if ((page >= TAS2781_YRAM4_START_PAGE && + page <= TAS2781_YRAM4_END_PAGE) || + (page >= TAS2781_YRAM2_START_PAGE && + page <= TAS2781_YRAM2_END_PAGE)) { + if (reg <= TAS2781_YRAM2_END_REG && + reg >= TAS2781_YRAM2_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg < TAS2781_YRAM2_START_REG) { + if (reg + len - 1 >= TAS2781_YRAM2_START_REG) { + cd->offset = TAS2781_YRAM2_START_REG; + cd->len = reg + len - TAS2781_YRAM2_START_REG; + in = true; + } + } + } + + return in; +} + +/* Return Code: + * true -- the registers are in the inblock yram + * false -- the registers are NOT in the inblock yram + */ +static bool check_inblock_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2) + in = check_inblock_yram_bk(cd, page, reg, len); + + return in; +} + +static bool check_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in; + + in = check_inpage_yram(cd, book, page, reg, len); + if (in) + goto end; + in = check_inblock_yram(cd, book, page, reg, len); + +end: + return in; +} + +static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice, + enum channel chn, unsigned char book, unsigned char page, + unsigned char reg, unsigned int len) +{ + struct tas_crc crc_data; + unsigned char crc_chksum = 0; + unsigned char nBuf1[128]; + int ret = 0; + int i; + bool in; + + if ((reg + len - 1) > 127) { + ret = -EINVAL; + dev_err(tasdevice->dev, "firmware error\n"); + goto end; + } + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (reg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (len == 4)) { + /*DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, len); + if (!in) + goto end; + + if (len == 1) { + dev_err(tasdevice->dev, "firmware error\n"); + ret = -EINVAL; + goto end; + } + + ret = tasdevice_dev_bulk_read(tasdevice, chn, + 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, + enum channel chl, unsigned char book, unsigned char page, + unsigned char reg, unsigned char val) +{ + struct tas_crc crc_data; + unsigned int nData1; + int ret = 0; + bool in; + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (reg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (reg <= (TASDEVICE_PAGE_REG( + TAS2781_SA_COEFF_SWAP_REG) + 4))) { + /*DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, 1); + if (!in) + goto end; + ret = tasdevice_dev_read(tasdevice, chl, + TASDEVICE_REG(book, page, reg), &nData1); + if (ret < 0) + goto end; + + if (nData1 != val) { + dev_err(tasdevice->dev, + "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n", + book, page, reg, val, nData1); + tasdevice->tasdevice[chl].err_code |= ERROR_YRAM_CRCCHK; + ret = -EAGAIN; + goto end; + } + + ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0); + +end: + return ret; +} + +static void set_err_prg_cfg(unsigned int type, struct tasdevice *dev) +{ + if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A) + || (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C) + || (type == MAIN_DEVICE_D)) + dev->cur_prog = -1; + else + dev->cur_conf = -1; +} + +static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn, 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, chn, book, page, reg, + len); + else + ret = do_singlereg_checksum(tas_priv, chn, book, page, reg, + val); + + if (ret > 0) { + *crc_chksum += (unsigned char)ret; + goto end; + } + + if (ret != -EAGAIN) + goto end; + + block->nr_retry--; + if (block->nr_retry > 0) + goto end; + + set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]); + +end: + return ret; +} + +static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn, 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_dev_bulk_write(tas_priv, chn, + TASDEVICE_REG(book, page, reg), data + 3, len); + if (ret < 0) + goto end; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, chn, + book, page, reg, len, 0, crc_chksum); + } else { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(book, page, reg), data[3]); + if (ret < 0) + goto end; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, chn, 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; + } + +end: + return ret; +} + +static int tasdev_block_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn) +{ + unsigned int nr_value; + int ret; + + ret = tasdevice_dev_read(tas_priv, chn, TASDEVICE_I2CChecksum, + &nr_value); + if (ret < 0) { + dev_err(tas_priv->dev, "%s: Chn %d\n", __func__, chn); + set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]); + goto end; + } + + if ((nr_value & 0xff) != block->pchksum) { + dev_err(tas_priv->dev, "%s: Blk PChkSum Chn %d ", __func__, + chn); + dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n", + block->pchksum, (nr_value & 0xff)); + tas_priv->tasdevice[chn].err_code |= ERROR_PRAM_CRCCHK; + ret = -EAGAIN; + block->nr_retry--; + + if (block->nr_retry <= 0) + set_err_prg_cfg(block->type, + &tas_priv->tasdevice[chn]); + } else + tas_priv->tasdevice[chn].err_code &= ~ERROR_PRAM_CRCCHK; + +end: + return ret; +} + +static int tasdev_load_blk(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn) +{ + unsigned int sleep_time; + unsigned int len; + unsigned int nr_cmds; + unsigned char *data = block->data; + unsigned char crc_chksum = 0; + unsigned char offset; + unsigned char book; + unsigned char page; + unsigned char val; + int ret = 0; + + while (block->nr_retry > 0) { + if (block->is_pchksum_present) { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_I2CChecksum, 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_dev_write(tas_priv, chn, + TASDEVICE_REG(book, page, offset), + val); + if (ret < 0) + goto end; + if (block->is_ychksum_present) { + ret = tasdev_bytes_chksum(tas_priv, + block, chn, book, page, offset, + 1, val, &crc_chksum); + if (ret < 0) + break; + } + continue; + } + /*sleep command*/ + if (offset == 0x81) { + /*book -- data[0] page -- data[1]*/ + sleep_time = ((book << 8) + page)*1000; + usleep_range(sleep_time, sleep_time + 50); + continue; + } + /*Multiple bytes write*/ + if (offset == 0x85) { + data += 4; + len = (book << 8) + page; + book = data[0]; + page = data[1]; + offset = data[2]; + ret = tasdev_multibytes_wr(tas_priv, + block, chn, 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, chn); + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) /*err in current device, skip it*/ + break; + } + + if (block->is_ychksum_present) { + /* TBD, open it when FW ready */ + dev_err(tas_priv->dev, + "Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n", + block->ychksum, crc_chksum); + + tas_priv->tasdevice[chn].err_code &= + ~ERROR_YRAM_CRCCHK; + ret = 0; + } + /*skip current blk*/ + break; + } + +end: + return ret; +} + +static int tasdevice_load_block(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block) +{ + int chnend = 0; + int ret = 0; + int chn = 0; + int rc = 0; + + switch (block->type) { + case MAIN_ALL_DEVICES: + chn = 0; + chnend = tas_priv->ndev; + break; + case MAIN_DEVICE_A: + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + chn = 0; + chnend = 1; + break; + case MAIN_DEVICE_B: + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + chn = 1; + chnend = 2; + break; + case MAIN_DEVICE_C: + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + chn = 2; + chnend = 3; + break; + case MAIN_DEVICE_D: + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + chn = 3; + chnend = 4; + break; + default: + dev_dbg(tas_priv->dev, "load blk: Other Type = 0x%02x\n", + block->type); + break; + } + + for (; chn < chnend; chn++) { + block->nr_retry = 6; + if (tas_priv->tasdevice[chn].is_loading == false) + continue; + ret = tasdev_load_blk(tas_priv, block, chn); + if (ret < 0) + dev_err(tas_priv->dev, "dev %d, Blk (%d) load error\n", + chn, block->type); + rc |= ret; + } + + return rc; +} + +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); + 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 = be32_to_cpup((__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 = be32_to_cpup((__be32 *)&buf[offset]); + offset += 8; + fw_fixed_hdr->drv_ver = be32_to_cpup((__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) + goto out; + 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; + } + +out: + 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__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_calibrations = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + if (tas_fmw->nr_calibrations != 1) { + dev_err(tas_priv->dev, + "%s: only support one calibraiton(%d)!\n", + __func__, tas_fmw->nr_calibrations); + goto out; + } + + tas_fmw->calibrations = kcalloc(tas_fmw->nr_calibrations, + sizeof(struct tasdevice_calibration), GFP_KERNEL); + if (!tas_fmw->calibrations) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "Calibrations error\n"); + offset = -EINVAL; + goto out; + } + 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"); + offset = -EINVAL; + goto out; + } + offset += n; + + offset = fw_parse_data(tas_fmw, &(calibration->dev_data), fmw, + offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +int tas2781_load_calibration(void *context, char *file_name, + enum channel i) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + struct tasdevice *tasdev = &(tas_priv->tasdevice[i]); + 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_info(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); + goto out; + } + fmw.size = fw_entry->size; + fmw.data = fw_entry->data; + + tas_fmw = tasdev->cali_data_fmw = kzalloc(sizeof(struct tasdevice_fw), + GFP_KERNEL); + if (!tasdev->cali_data_fmw) { + ret = -ENOMEM; + goto out; + } + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, "fw_parse_header EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, + "%s: fw_parse_variable_header_cal EXIT!\n", __func__); + ret = offset; + goto out; + } + offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n"); + ret = offset; + goto out; + } + +out: + if (fw_entry) + release_firmware(fw_entry); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_dspfw_ready(const struct firmware *fmw, + void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr; + struct tasdevice_fw *tas_fmw; + int offset = 0; + int ret = 0; + + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n", + __func__, tas_priv->coef_binaryname); + ret = -EINVAL; + goto out; + } + + tas_priv->fmw = kzalloc(sizeof(struct tasdevice_fw), GFP_KERNEL); + if (!tas_priv->fmw) { + ret = -ENOMEM; + goto out; + } + tas_fmw = tas_priv->fmw; + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset); + + if (offset == -EINVAL) { + ret = -EINVAL; + goto out; + } + 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: + 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) + goto out; + break; + } + + offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset); + if (offset < 0) { + ret = offset; + goto out; + } + offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw, + offset); + if (offset < 0) { + ret = offset; + goto out; + } + offset = tas_priv->fw_parse_configuration_data(tas_priv, + tas_fmw, fmw, offset); + if (offset < 0) + ret = offset; + +out: + return ret; +} + +int tasdevice_dsp_parser(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + const struct firmware *fw_entry; + int ret; + + ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, + tas_priv->dev); + if (ret) { + dev_err(tas_priv->dev, "%s: load %s error\n", __func__, + tas_priv->coef_binaryname); + goto out; + } + + ret = tasdevice_dspfw_ready(fw_entry, tas_priv); + release_firmware(fw_entry); + fw_entry = NULL; + +out: + return ret; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, SND_SOC_TAS2781_FMWLIB); + +static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw) +{ + struct tasdevice_calibration *calibration; + struct tasdev_blk *block; + struct tasdevice_data *im; + unsigned int blks; + int i; + + if (!tas_fmw->calibrations) + goto out; + + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + calibration = &(tas_fmw->calibrations[i]); + if (!calibration) + continue; + + im = &(calibration->dev_data); + + if (!im->dev_blks) + continue; + + for (blks = 0; blks < im->nr_blk; blks++) { + block = &(im->dev_blks[blks]); + if (!block) + continue; + kfree(block->data); + } + kfree(im->dev_blks); + } + kfree(tas_fmw->calibrations); +out: + kfree(tas_fmw); +} + +void tasdevice_calbin_remove(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice *tasdev; + int i; + + if (!tas_priv) + return; + + for (i = 0; i < tas_priv->ndev; i++) { + tasdev = &(tas_priv->tasdevice[i]); + if (!tasdev->cali_data_fmw) + continue; + tas2781_clear_calfirmware(tasdev->cali_data_fmw); + tasdev->cali_data_fmw = NULL; + } +} +EXPORT_SYMBOL_NS_GPL(tasdevice_calbin_remove, SND_SOC_TAS2781_FMWLIB); + +void tasdevice_config_info_remove(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **ci = rca->cfg_info; + int i, j; + + if (!ci) + return; + for (i = 0; i < rca->ncfgs; i++) { + if (!ci[i]) + continue; + if (ci[i]->blk_data) { + for (j = 0; j < (int)ci[i]->real_nblocks; j++) { + if (!ci[i]->blk_data[j]) + continue; + kfree(ci[i]->blk_data[j]->regdata); + kfree(ci[i]->blk_data[j]); + } + kfree(ci[i]->blk_data); + } + kfree(ci[i]); + } + kfree(ci); +} +EXPORT_SYMBOL_NS_GPL(tasdevice_config_info_remove, SND_SOC_TAS2781_FMWLIB); + +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_select_tuningprm_cfg(void *context, int prm_no, + int cfg_no, int rca_conf_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + struct tasdevice_config *conf; + int prog_status = 0; + int status, i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + 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); + goto out; + } + + 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); + goto out; + } + + 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); + goto out; + } + + conf = &(tas_fmw->configs[cfg_no]); + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (cfg_info[rca_conf_no]->active_dev & (1 << i)) { + if (tas_priv->tasdevice[i].cur_prog != prm_no) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + } else + tas_priv->tasdevice[i].is_loading = false; + tas_priv->tasdevice[i].is_loaderr = false; + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) { + struct tasdevice_fw *cal_fmw = + tas_priv->tasdevice[i].cali_data_fmw; + + if (cal_fmw) { + struct tasdevice_calibration + *cal = cal_fmw->calibrations; + + if (cal) + load_calib_data(tas_priv, + &(cal->dev_data)); + } + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + } + + for (i = 0, status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_conf != cfg_no + && (cfg_info[rca_conf_no]->active_dev & (1 << i)) + && (tas_priv->tasdevice[i].is_loaderr == false)) { + status++; + tas_priv->tasdevice[i].is_loading = true; + } else + tas_priv->tasdevice[i].is_loading = false; + } + + if (status) { + status = 0; + tasdevice_load_data(tas_priv, &(conf->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) { + status |= 1 << (i + 4); + continue; + } else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) + tas_priv->tasdevice[i].cur_conf = cfg_no; + } + } else + dev_err(tas_priv->dev, + "%s: No device is in active in conf %d\n", + __func__, rca_conf_no); + + status |= cfg_info[rca_conf_no]->active_dev; + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_select_tuningprm_cfg, + SND_SOC_TAS2781_FMWLIB); + +int tasdevice_prmg_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + int prog_status = 0; + int i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + 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); + goto out; + } + + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_prog != prm_no) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + if (tas_priv->set_global_mode) + tas_priv->set_global_mode(tas_priv); +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, SND_SOC_TAS2781_FMWLIB); + +int tasdevice_prmg_calibdata_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + int prog_status = 0; + int i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + 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); + goto out; + } + + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_prog != prm_no) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + tas_priv->tasdevice[i].is_loaderr = false; + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) { + struct tasdevice_fw *cal_fmw = + tas_priv->tasdevice[i].cali_data_fmw; + + if (cal_fmw) { + struct tasdevice_calibration *cal = + cal_fmw->calibrations; + + if (cal) + load_calib_data(tas_priv, + &(cal->dev_data)); + } + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + } + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_calibdata_load, + SND_SOC_TAS2781_FMWLIB); + +void tasdevice_tuning_switch(void *context, int state) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + int profile_cfg_id = 0; + + 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) + /*bypass all in rca is profile id 0*/ + profile_cfg_id = RCA_CONFIGID_BYPASS_ALL; + else { + /*dsp mode or tuning mode*/ + profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + tasdevice_select_tuningprm_cfg(tas_priv, + tas_priv->cur_prog, tas_priv->cur_conf, + profile_cfg_id); + if (tas_priv->set_global_mode) + tas_priv->set_global_mode(tas_priv); + } + + tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_POWER_UP); + } else + tasdevice_select_cfg_blk(tas_priv, + tas_priv->rcabin.profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_SHUTDOWN); +} +EXPORT_SYMBOL_NS_GPL(tasdevice_tuning_switch, + SND_SOC_TAS2781_FMWLIB); + +MODULE_DESCRIPTION("Texas Firmware Support"); +MODULE_AUTHOR("Shenghao Ding, TI, "); +MODULE_LICENSE("GPL"); -- 2.34.1