3083 lines
78 KiB
C
3083 lines
78 KiB
C
|
/*
|
||
|
* Copyright (C) 2014-2020 NXP Semiconductors, All Rights Reserved.
|
||
|
* Copyright 2020 GOODIX, All Rights Reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 as
|
||
|
* published by the Free Software Foundation.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "inc/tfa_container.h"
|
||
|
#include "inc/tfa.h"
|
||
|
#include "inc/tfa98xx_tfafieldnames.h"
|
||
|
#include "inc/tfa_internal.h"
|
||
|
#include "inc/config.h"
|
||
|
|
||
|
/* defines */
|
||
|
#define MODULE_BIQUADFILTERBANK 2
|
||
|
#define BIQUAD_COEFF_SIZE 6
|
||
|
|
||
|
#define TEMP_OFFSET ((1 + 2) * 3)
|
||
|
|
||
|
/* module globals */
|
||
|
static uint8_t gresp_address; /* in case of setting with option */
|
||
|
|
||
|
static int float_to_int(uint32_t x)
|
||
|
{
|
||
|
unsigned int e, m;
|
||
|
|
||
|
e = (unsigned int)((0x7f + 31)
|
||
|
- (int)((*(unsigned int *)&x & 0x7f800000) >> 23));
|
||
|
m = 0x80000000 | (*(unsigned int *)&x << 8);
|
||
|
|
||
|
return -(int)((m >> e) & -(e < 32));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* check the container file
|
||
|
*/
|
||
|
enum tfa_error tfa_load_cnt(void *cnt, int length)
|
||
|
{
|
||
|
struct tfa_container *cntbuf = (struct tfa_container *)cnt;
|
||
|
|
||
|
if (length > TFA_MAX_CNT_LENGTH) {
|
||
|
pr_err("incorrect length\n");
|
||
|
return tfa_error_container;
|
||
|
}
|
||
|
|
||
|
if (HDR(cntbuf->id[0], cntbuf->id[1]) == 0) {
|
||
|
pr_err("header is 0\n");
|
||
|
return tfa_error_container;
|
||
|
}
|
||
|
|
||
|
if ((HDR(cntbuf->id[0], cntbuf->id[1])) != params_hdr) {
|
||
|
pr_err("wrong header type: 0x%02x 0x%02x\n",
|
||
|
cntbuf->id[0], cntbuf->id[1]);
|
||
|
return tfa_error_container;
|
||
|
}
|
||
|
|
||
|
if (cntbuf->size == 0) {
|
||
|
pr_err("data size is 0\n");
|
||
|
return tfa_error_container;
|
||
|
}
|
||
|
|
||
|
/* check CRC */
|
||
|
if (tfa_cont_crc_check_container(cntbuf)) {
|
||
|
pr_err("CRC error\n");
|
||
|
return tfa_error_container;
|
||
|
}
|
||
|
|
||
|
/* check sub version level */
|
||
|
if ((cntbuf->subversion[1] != TFA_PM_SUBVERSION)
|
||
|
&& (cntbuf->subversion[0] != '0')) {
|
||
|
pr_err("container sub-version not supported: %c%c\n",
|
||
|
cntbuf->subversion[0], cntbuf->subversion[1]);
|
||
|
return tfa_error_container;
|
||
|
}
|
||
|
|
||
|
return tfa_error_ok;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Dump the contents of the file header
|
||
|
*/
|
||
|
void tfa_cont_show_header(struct tfa_header *hdr)
|
||
|
{
|
||
|
char _id[2];
|
||
|
|
||
|
pr_debug("File header\n");
|
||
|
|
||
|
_id[1] = hdr->id >> 8;
|
||
|
_id[0] = hdr->id & 0xff;
|
||
|
pr_debug("\tid:%.2s version:%.2s subversion:%.2s\n", _id,
|
||
|
hdr->version, hdr->subversion);
|
||
|
pr_debug("\tsize:%d CRC:0x%08x\n", hdr->size, hdr->crc);
|
||
|
pr_debug("\tcustomer:%.8s application:%.8s type:%.8s\n",
|
||
|
hdr->customer, hdr->application, hdr->type);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* return device list dsc from index
|
||
|
*/
|
||
|
struct tfa_device_list *tfa_cont_get_dev_list
|
||
|
(struct tfa_container *cont, int dev_idx)
|
||
|
{
|
||
|
uint8_t *base = (uint8_t *)cont;
|
||
|
|
||
|
if (cont == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
if ((dev_idx < 0) || (dev_idx >= cont->ndev))
|
||
|
return NULL;
|
||
|
|
||
|
if (cont->index[dev_idx].type != dsc_device)
|
||
|
return NULL;
|
||
|
|
||
|
base += cont->index[dev_idx].offset;
|
||
|
return (struct tfa_device_list *)base;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* get the Nth profile for the Nth device
|
||
|
*/
|
||
|
struct tfa_profile_list *tfa_cont_get_dev_prof_list
|
||
|
(struct tfa_container *cont, int dev_idx, int prof_idx)
|
||
|
{
|
||
|
struct tfa_device_list *dev;
|
||
|
int idx, hit;
|
||
|
uint8_t *base = (uint8_t *)cont;
|
||
|
|
||
|
dev = tfa_cont_get_dev_list(cont, dev_idx);
|
||
|
if (dev) {
|
||
|
for (idx = 0, hit = 0; idx < dev->length; idx++) {
|
||
|
if (dev->list[idx].type == dsc_profile) {
|
||
|
if (prof_idx == hit++)
|
||
|
return (struct tfa_profile_list *)
|
||
|
(dev->list[idx].offset + base);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* get the number of profiles for the Nth device
|
||
|
*/
|
||
|
int tfa_cnt_get_dev_nprof(struct tfa_device *tfa)
|
||
|
{
|
||
|
struct tfa_device_list *dev;
|
||
|
int idx, nprof = 0;
|
||
|
|
||
|
if (tfa->cnt == NULL)
|
||
|
return 0;
|
||
|
|
||
|
if ((tfa->dev_idx < 0) || (tfa->dev_idx >= tfa->cnt->ndev))
|
||
|
return 0;
|
||
|
|
||
|
dev = tfa_cont_get_dev_list(tfa->cnt, tfa->dev_idx);
|
||
|
if (dev) {
|
||
|
for (idx = 0; idx < dev->length; idx++) {
|
||
|
if (dev->list[idx].type == dsc_profile)
|
||
|
nprof++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nprof;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* get the Nth lifedata for the Nth device
|
||
|
*/
|
||
|
struct tfa_livedata_list *tfa_cont_get_dev_livedata_list
|
||
|
(struct tfa_container *cont, int dev_idx, int lifedata_idx)
|
||
|
{
|
||
|
struct tfa_device_list *dev;
|
||
|
int idx, hit;
|
||
|
uint8_t *base = (uint8_t *)cont;
|
||
|
|
||
|
dev = tfa_cont_get_dev_list(cont, dev_idx);
|
||
|
if (dev) {
|
||
|
for (idx = 0, hit = 0; idx < dev->length; idx++) {
|
||
|
if (dev->list[idx].type == dsc_livedata) {
|
||
|
if (lifedata_idx == hit++)
|
||
|
return (struct tfa_livedata_list *)
|
||
|
(dev->list[idx].offset + base);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get the max volume step associated with Nth profile for the Nth device
|
||
|
*/
|
||
|
int tfa_cont_get_max_vstep(struct tfa_device *tfa, int prof_idx)
|
||
|
{
|
||
|
struct tfa_volume_step2_file *vp;
|
||
|
struct tfa_volume_step_max2_file *vp3;
|
||
|
int vstep_count = 0;
|
||
|
|
||
|
vp = (struct tfa_volume_step2_file *)
|
||
|
tfa_cont_get_file_data(tfa, prof_idx, volstep_hdr);
|
||
|
if (vp == NULL)
|
||
|
return 0;
|
||
|
/* check the header type to load different NrOfVStep appropriately */
|
||
|
if (tfa->tfa_family == 2) {
|
||
|
/* this is actually tfa2, so re-read the buffer*/
|
||
|
vp3 = (struct tfa_volume_step_max2_file *)
|
||
|
tfa_cont_get_file_data(tfa, prof_idx, volstep_hdr);
|
||
|
if (vp3)
|
||
|
vstep_count = vp3->nr_of_vsteps;
|
||
|
} else {
|
||
|
/* this is max1*/
|
||
|
if (vp)
|
||
|
vstep_count = vp->vsteps;
|
||
|
}
|
||
|
|
||
|
return vstep_count;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the file contents associated with the device or profile
|
||
|
* Search within the device tree, if not found, search within the profile
|
||
|
* tree. There can only be one type of file within profile or device.
|
||
|
*/
|
||
|
struct tfa_file_dsc *tfa_cont_get_file_data(struct tfa_device *tfa,
|
||
|
int prof_idx, enum tfa_header_type type)
|
||
|
{
|
||
|
struct tfa_device_list *dev;
|
||
|
struct tfa_profile_list *prof;
|
||
|
struct tfa_file_dsc *file;
|
||
|
struct tfa_header *hdr;
|
||
|
unsigned int i;
|
||
|
|
||
|
if (tfa->cnt == NULL) {
|
||
|
pr_err("invalid pointer to container file\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
dev = tfa_cont_get_dev_list(tfa->cnt, tfa->dev_idx);
|
||
|
if (dev == NULL) {
|
||
|
pr_err("invalid pointer to container file device list\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* process the device list until a file type is encountered */
|
||
|
for (i = 0; i < dev->length; i++) {
|
||
|
if (dev->list[i].type == dsc_file) {
|
||
|
file = (struct tfa_file_dsc *)
|
||
|
(dev->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
if (file != NULL) {
|
||
|
hdr = (struct tfa_header *)file->data;
|
||
|
/* check for file type */
|
||
|
if (hdr->id == type) {
|
||
|
/* pr_debug("%s: file found of type "
|
||
|
* "%d in device %s\n",
|
||
|
* __func__, type,
|
||
|
* tfa_cont_device_name(tfa->cnt,
|
||
|
* tfa->dev_idx));
|
||
|
*/
|
||
|
return (struct tfa_file_dsc *)
|
||
|
&file->data;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* File not found in device tree.
|
||
|
* So, look in the profile list until the file type is encountered
|
||
|
*/
|
||
|
prof = tfa_cont_get_dev_prof_list(tfa->cnt, tfa->dev_idx, prof_idx);
|
||
|
if (prof == NULL) {
|
||
|
pr_err("invalid pointer to container file profile list\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < prof->length; i++) {
|
||
|
if (prof->list[i].type == dsc_file) {
|
||
|
file = (struct tfa_file_dsc *)
|
||
|
(prof->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
if (file != NULL) {
|
||
|
hdr = (struct tfa_header *)file->data;
|
||
|
if (hdr != NULL) {
|
||
|
/* check for file type */
|
||
|
if (hdr->id == type) {
|
||
|
/* pr_debug("%s: file found of "
|
||
|
* "type %d in profile %s\n",
|
||
|
* __func__, type,
|
||
|
* tfa_cont_profile_name
|
||
|
* (tfa->cnt, tfa->dev_idx,
|
||
|
* prof_idx));
|
||
|
*/
|
||
|
return (struct tfa_file_dsc *)
|
||
|
&file->data;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("%s: no file found of type %d\n", __func__, type);
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* write a parameter file to the device
|
||
|
*/
|
||
|
static enum tfa98xx_error tfa_cont_write_vstep
|
||
|
(struct tfa_device *tfa, struct tfa_volume_step2_file *vp, int vstep)
|
||
|
{
|
||
|
enum tfa98xx_error err;
|
||
|
unsigned short vol;
|
||
|
|
||
|
if (vstep < vp->vsteps) {
|
||
|
/* vol = (unsigned short)(voldB / (-0.5f)); */
|
||
|
vol = (unsigned short)
|
||
|
(-2 * float_to_int
|
||
|
(*((uint32_t *)&vp->vstep[vstep].attenuation)));
|
||
|
if (vol > 255) /* restricted to 8 bits */
|
||
|
vol = 255;
|
||
|
|
||
|
err = tfa98xx_set_volume_level(tfa, vol);
|
||
|
if (err != TFA98XX_ERROR_OK)
|
||
|
return err;
|
||
|
|
||
|
err = tfa98xx_dsp_write_preset(tfa,
|
||
|
sizeof(vp->vstep[0].preset),
|
||
|
vp->vstep[vstep].preset);
|
||
|
if (err != TFA98XX_ERROR_OK)
|
||
|
return err;
|
||
|
err = tfa_cont_write_filterbank(tfa,
|
||
|
vp->vstep[vstep].filter);
|
||
|
} else {
|
||
|
pr_err("Incorrect volume given. The value vstep[%d] >= %d\n",
|
||
|
vstep, vp->vsteps);
|
||
|
err = TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
}
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("vstep[%d][%d]\n", tfa->dev_idx, vstep);
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static struct tfa_volume_step_message_info *
|
||
|
tfa_cont_get_msg_info_from_reg(struct tfa_volume_step_register_info *reg_info)
|
||
|
{
|
||
|
char *p = (char *)reg_info;
|
||
|
|
||
|
p += sizeof(reg_info->nr_of_registers)
|
||
|
+ (reg_info->nr_of_registers * sizeof(uint32_t));
|
||
|
|
||
|
return (struct tfa_volume_step_message_info *)p;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
tfa_cont_get_msg_len(struct tfa_volume_step_message_info *msg_info)
|
||
|
{
|
||
|
return (msg_info->message_length.b[0] << 16)
|
||
|
+ (msg_info->message_length.b[1] << 8)
|
||
|
+ msg_info->message_length.b[2];
|
||
|
}
|
||
|
|
||
|
static struct tfa_volume_step_message_info *
|
||
|
tfa_cont_get_next_msg_info(struct tfa_volume_step_message_info *msg_info)
|
||
|
{
|
||
|
char *p = (char *)msg_info;
|
||
|
int msgLen = tfa_cont_get_msg_len(msg_info);
|
||
|
int type = msg_info->message_type;
|
||
|
|
||
|
p += sizeof(msg_info->message_type) + sizeof(msg_info->message_length);
|
||
|
if (type == 3)
|
||
|
p += msgLen;
|
||
|
else
|
||
|
p += msgLen * 3;
|
||
|
|
||
|
return (struct tfa_volume_step_message_info *)p;
|
||
|
}
|
||
|
|
||
|
static struct tfa_volume_step_register_info *
|
||
|
tfa_cont_get_next_reg_from_end_info
|
||
|
(struct tfa_volume_step_message_info *msg_info)
|
||
|
{
|
||
|
char *p = (char *)msg_info;
|
||
|
|
||
|
p += sizeof(msg_info->nr_of_messages);
|
||
|
return (struct tfa_volume_step_register_info *)p;
|
||
|
|
||
|
}
|
||
|
|
||
|
static struct tfa_volume_step_register_info *
|
||
|
tfa_cont_get_reg_for_vstep(struct tfa_volume_step_max2_file *vp, int idx)
|
||
|
{
|
||
|
int i, j, nrMessage;
|
||
|
|
||
|
struct tfa_volume_step_register_info *reg_info
|
||
|
= (struct tfa_volume_step_register_info *)vp->vsteps_bin;
|
||
|
struct tfa_volume_step_message_info *msg_info = NULL;
|
||
|
|
||
|
for (i = 0; i < idx; i++) {
|
||
|
msg_info = tfa_cont_get_msg_info_from_reg(reg_info);
|
||
|
nrMessage = msg_info->nr_of_messages;
|
||
|
|
||
|
for (j = 0; j < nrMessage; j++)
|
||
|
msg_info = tfa_cont_get_next_msg_info(msg_info);
|
||
|
reg_info = tfa_cont_get_next_reg_from_end_info(msg_info);
|
||
|
}
|
||
|
|
||
|
return reg_info;
|
||
|
}
|
||
|
|
||
|
#pragma pack(push, 1)
|
||
|
struct tfa_partial_msg_block {
|
||
|
uint8_t offset;
|
||
|
uint16_t change;
|
||
|
uint8_t update[16][3];
|
||
|
};
|
||
|
#pragma pack(pop)
|
||
|
|
||
|
static enum tfa98xx_error tfa_cont_write_vstep_max2_one
|
||
|
(struct tfa_device *tfa, struct tfa_volume_step_message_info *new_msg,
|
||
|
struct tfa_volume_step_message_info *old_msg, int enable_partial_update)
|
||
|
{
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
int len = (tfa_cont_get_msg_len(new_msg) - 1) * 3;
|
||
|
char *buf = (char *)new_msg->parameter_data;
|
||
|
uint8_t *partial = NULL;
|
||
|
uint8_t cmdid[3];
|
||
|
int use_partial_coeff = 0;
|
||
|
|
||
|
if (enable_partial_update) {
|
||
|
if (new_msg->message_type != old_msg->message_type) {
|
||
|
pr_debug("Message type differ - Disable Partial update\n");
|
||
|
enable_partial_update = 0;
|
||
|
} else if (tfa_cont_get_msg_len(new_msg)
|
||
|
!= tfa_cont_get_msg_len(old_msg)) {
|
||
|
pr_debug("Message Length differ - Disable Partial update\n");
|
||
|
enable_partial_update = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((enable_partial_update) && (new_msg->message_type == 1)) {
|
||
|
/* No patial updates for message type 1 (Coefficients) */
|
||
|
enable_partial_update = 0;
|
||
|
if ((tfa->rev & 0xff) == 0x88)
|
||
|
use_partial_coeff = 1;
|
||
|
else if ((tfa->rev & 0xff) == 0x13)
|
||
|
use_partial_coeff = 1;
|
||
|
}
|
||
|
|
||
|
/* Change Message Len to the actual buffer len */
|
||
|
memcpy(cmdid, new_msg->cmd_id, sizeof(cmdid));
|
||
|
|
||
|
/* The algoparams and mbdrc msg id will be changed
|
||
|
* to the reset type when SBSL=0
|
||
|
* if SBSL=1 the msg will remain unchanged.
|
||
|
* It's up to the tuning engineer to choose the 'without_reset'
|
||
|
* types inside the vstep.
|
||
|
* In other words: the reset msg is applied during SBSL==0
|
||
|
* else it remains unchanged.
|
||
|
*/
|
||
|
pr_info("%s: is_cold %d\n", __func__, tfa->is_cold);
|
||
|
|
||
|
if (tfa_needs_reset(tfa) == 1) {
|
||
|
if (new_msg->message_type == 0) {
|
||
|
if (cmdid[2] == SB_PARAM_SET_ALGO_PARAMS_WITHOUT_RESET)
|
||
|
cmdid[2] = SB_PARAM_SET_ALGO_PARAMS;
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("P-ID for SetAlgoParams modified!\n");
|
||
|
} else if (new_msg->message_type == 2) {
|
||
|
if (cmdid[2] == SB_PARAM_SET_MBDRC_WITHOUT_RESET)
|
||
|
cmdid[2] = SB_PARAM_SET_MBDRC;
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("P-ID for SetMBDrc modified!\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* +sizeof(struct tfa_partial_msg_block) will allow to fit one
|
||
|
* additonnal partial block If the partial update goes over the len of
|
||
|
* a regular message, we can safely write our block and check afterward
|
||
|
* that we are over the size of a usual update
|
||
|
*/
|
||
|
if (enable_partial_update) {
|
||
|
partial = kmem_cache_alloc(tfa->cachep, GFP_KERNEL);
|
||
|
if (!partial)
|
||
|
pr_debug("Partial update memory error - Disabling\n");
|
||
|
}
|
||
|
|
||
|
if (partial) {
|
||
|
uint8_t offset = 0, i = 0;
|
||
|
uint16_t *change;
|
||
|
uint8_t *n = new_msg->parameter_data;
|
||
|
uint8_t *o = old_msg->parameter_data;
|
||
|
uint8_t *p = partial;
|
||
|
uint8_t *trim = partial;
|
||
|
|
||
|
/* set dspFiltersReset */
|
||
|
*p++ = 0x02;
|
||
|
*p++ = 0x00;
|
||
|
*p++ = 0x00;
|
||
|
|
||
|
while ((o < (old_msg->parameter_data + len)) &&
|
||
|
(p < (partial + len - 3))) {
|
||
|
if ((offset == 0xff) ||
|
||
|
(memcmp(n, o, 3 * sizeof(uint8_t)))) {
|
||
|
*p++ = offset;
|
||
|
change = (uint16_t *)p;
|
||
|
*change = 0;
|
||
|
p += 2;
|
||
|
|
||
|
for (i = 0; (i < 16) &&
|
||
|
(o < (old_msg->parameter_data + len));
|
||
|
i++, n += 3, o += 3) {
|
||
|
if (memcmp(n, o,
|
||
|
3 * sizeof(uint8_t))) {
|
||
|
*change |= BIT(i);
|
||
|
memcpy(p, n, 3);
|
||
|
p += 3;
|
||
|
trim = p;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
offset = 0;
|
||
|
*change = cpu_to_be16(*change);
|
||
|
} else {
|
||
|
n += 3;
|
||
|
o += 3;
|
||
|
offset++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (trim == partial) {
|
||
|
pr_debug("No Change in message - discarding %d bytes\n",
|
||
|
len);
|
||
|
len = 0;
|
||
|
} else if (trim < (partial + len - 3)) {
|
||
|
pr_debug("Using partial update: %d -> %d bytes\n",
|
||
|
len, (int)(trim - partial + 3));
|
||
|
|
||
|
/* Add the termination marker */
|
||
|
memset(trim, 0x00, 3);
|
||
|
trim += 3;
|
||
|
|
||
|
/* Signal This will be a partial update */
|
||
|
cmdid[2] |= BIT(6);
|
||
|
buf = (char *)partial;
|
||
|
len = (int)(trim - partial);
|
||
|
} else {
|
||
|
pr_debug("Partial too big - use regular update\n");
|
||
|
}
|
||
|
} else {
|
||
|
if (!enable_partial_update)
|
||
|
pr_debug("Partial update - Not enabled\n");
|
||
|
else /* partial == NULL */
|
||
|
pr_err("Partial update memory error - Disabling\n");
|
||
|
}
|
||
|
|
||
|
if (use_partial_coeff) {
|
||
|
err = dsp_partial_coefficients(tfa,
|
||
|
old_msg->parameter_data, new_msg->parameter_data);
|
||
|
} else if (len) {
|
||
|
uint8_t *buffer;
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("Command-ID used: 0x%02x%02x%02x\n",
|
||
|
cmdid[0], cmdid[1], cmdid[2]);
|
||
|
|
||
|
buffer = kmem_cache_alloc(tfa->cachep, GFP_KERNEL);
|
||
|
if (buffer == NULL) {
|
||
|
err = TFA98XX_ERROR_FAIL;
|
||
|
} else {
|
||
|
memcpy(&buffer[0], cmdid, 3);
|
||
|
memcpy(&buffer[3], buf, len);
|
||
|
err = dsp_msg(tfa, 3 + len, (char *)buffer);
|
||
|
kmem_cache_free(tfa->cachep, buffer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (partial)
|
||
|
kmem_cache_free(tfa->cachep, partial);
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static enum tfa98xx_error tfa_cont_write_vstep_max2
|
||
|
(struct tfa_device *tfa, struct tfa_volume_step_max2_file *vp,
|
||
|
int vstep_idx, int vstep_msg_idx)
|
||
|
{
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
struct tfa_volume_step_register_info *reg_info = NULL;
|
||
|
struct tfa_volume_step_message_info *msg_info = NULL,
|
||
|
*p_msg_info = NULL;
|
||
|
struct tfa_bitfield bit_f = {0, 0};
|
||
|
int i, nr_messages, enp = tfa->partial_enable;
|
||
|
|
||
|
if (vstep_idx >= vp->nr_of_vsteps) {
|
||
|
pr_debug("Volumestep %d is not available\n", vstep_idx);
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
}
|
||
|
|
||
|
if (tfa->p_reg_info == NULL) {
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("Initial vstep write\n");
|
||
|
enp = 0;
|
||
|
}
|
||
|
|
||
|
reg_info = tfa_cont_get_reg_for_vstep(vp, vstep_idx);
|
||
|
|
||
|
msg_info = tfa_cont_get_msg_info_from_reg(reg_info);
|
||
|
nr_messages = msg_info->nr_of_messages;
|
||
|
|
||
|
if (enp) {
|
||
|
p_msg_info = tfa_cont_get_msg_info_from_reg(tfa->p_reg_info);
|
||
|
if (nr_messages != p_msg_info->nr_of_messages) {
|
||
|
pr_debug("Message different - Disable partial update\n");
|
||
|
enp = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < nr_messages; i++) {
|
||
|
/* Messagetype(3) is Smartstudio Info! Dont send this! */
|
||
|
if (msg_info->message_type == 3) {
|
||
|
pr_debug("Skipping Message Type 3\n");
|
||
|
/* message_length is in bytes */
|
||
|
msg_info = tfa_cont_get_next_msg_info(msg_info);
|
||
|
if (enp)
|
||
|
p_msg_info = tfa_cont_get_next_msg_info
|
||
|
(p_msg_info);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* If no vstepMsgIndex is passed on,
|
||
|
* all message needs to be send
|
||
|
*/
|
||
|
if ((vstep_msg_idx >= TFA_MAX_VSTEP_MSG_MARKER)
|
||
|
|| (vstep_msg_idx == i)) {
|
||
|
err = tfa_cont_write_vstep_max2_one
|
||
|
(tfa, msg_info, p_msg_info, enp);
|
||
|
if (err != TFA98XX_ERROR_OK) {
|
||
|
/*
|
||
|
* Force a full update for the next write
|
||
|
* As the current status of the DSP is unknown
|
||
|
*/
|
||
|
tfa->p_reg_info = NULL;
|
||
|
return err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
msg_info = tfa_cont_get_next_msg_info(msg_info);
|
||
|
if (enp)
|
||
|
p_msg_info = tfa_cont_get_next_msg_info(p_msg_info);
|
||
|
}
|
||
|
|
||
|
tfa->p_reg_info = reg_info;
|
||
|
|
||
|
for (i = 0; i < reg_info->nr_of_registers * 2; i++) {
|
||
|
/* Byte swap the datasheetname */
|
||
|
bit_f.field = (uint16_t)(reg_info->register_info[i] >> 8)
|
||
|
| (reg_info->register_info[i] << 8);
|
||
|
i++;
|
||
|
bit_f.value = (uint16_t)reg_info->register_info[i] >> 8;
|
||
|
err = tfa_run_write_bitfield(tfa, bit_f);
|
||
|
if (err != TFA98XX_ERROR_OK)
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/* Save the current vstep */
|
||
|
tfa_dev_set_swvstep(tfa, (unsigned short)vstep_idx);
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Write DRC message to the dsp
|
||
|
* If needed modify the cmd-id
|
||
|
*/
|
||
|
|
||
|
enum tfa98xx_error tfa_cont_write_drc_file(struct tfa_device *tfa,
|
||
|
int size, uint8_t data[])
|
||
|
{
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
uint8_t *msg = NULL;
|
||
|
|
||
|
msg = kmem_cache_alloc(tfa->cachep, GFP_KERNEL);
|
||
|
if (msg == NULL)
|
||
|
return TFA98XX_ERROR_FAIL;
|
||
|
memcpy(msg, data, size);
|
||
|
|
||
|
if (TFA_GET_BF(tfa, SBSL) == 0) {
|
||
|
/* Only do this when not set already */
|
||
|
if (msg[2] != SB_PARAM_SET_MBDRC) {
|
||
|
msg[2] = SB_PARAM_SET_MBDRC;
|
||
|
|
||
|
if (tfa->verbose) {
|
||
|
pr_debug("P-ID for SetMBDrc modified!: ");
|
||
|
pr_debug("Command-ID used: 0x%02x%02x%02x\n",
|
||
|
msg[0], msg[1], msg[2]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Send cmd_id + payload to dsp */
|
||
|
err = dsp_msg(tfa, size, (const char *)msg);
|
||
|
|
||
|
kmem_cache_free(tfa->cachep, msg);
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
enum tfa98xx_error tfa_cont_fw_api_check(struct tfa_device *tfa,
|
||
|
char *hdrstr)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
pr_info("%s: Expected FW API ver: %d.%d.%d.%d, Msg File ver: %d.%d.%d.%d\n",
|
||
|
__func__,
|
||
|
tfa->fw_itf_ver[0], tfa->fw_itf_ver[1],
|
||
|
tfa->fw_itf_ver[2], tfa->fw_itf_ver[3],
|
||
|
hdrstr[4], hdrstr[5], hdrstr[6], hdrstr[7]);
|
||
|
|
||
|
for (i = 0; i < 4; i++) {
|
||
|
if (tfa->fw_itf_ver[i] != hdrstr[i + 4])
|
||
|
/* +4 to skip "APIV" in msg file */
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
}
|
||
|
|
||
|
return TFA98XX_ERROR_OK;
|
||
|
}
|
||
|
|
||
|
static int tfa_cont_is_config_loaded(struct tfa_device *tfa)
|
||
|
{
|
||
|
if (tfa->ext_dsp != 1)
|
||
|
return 0; /* unrelated */
|
||
|
|
||
|
/* check if config is loaded at the first device:
|
||
|
* to write files only once
|
||
|
*/
|
||
|
|
||
|
if (tfa_count_status_flag(tfa, TFA_SET_DEVICE) > 1
|
||
|
|| tfa_count_status_flag(tfa, TFA_SET_CONFIG) > 0) {
|
||
|
pr_debug("%s: skip secondary device (%d)\n",
|
||
|
__func__, tfa->dev_idx);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* write a parameter file to the device
|
||
|
* The VstepIndex and VstepMsgIndex are only used to write
|
||
|
* a specific msg from the vstep file.
|
||
|
*/
|
||
|
enum tfa98xx_error tfa_cont_write_file(struct tfa_device *tfa,
|
||
|
struct tfa_file_dsc *file, int vstep_idx, int vstep_msg_idx)
|
||
|
{
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
struct tfa_header *hdr = (struct tfa_header *)file->data;
|
||
|
enum tfa_header_type type;
|
||
|
int size;
|
||
|
char sub_ver_string[8] = {0};
|
||
|
uint16_t subversion = 0;
|
||
|
int kerr;
|
||
|
char *data_buf;
|
||
|
int channel, temp_index = TEMP_OFFSET;
|
||
|
uint8_t org_cmd = 0xff;
|
||
|
|
||
|
if (tfa_cont_is_config_loaded(tfa))
|
||
|
return err;
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
tfa_cont_show_header(hdr);
|
||
|
|
||
|
type = (enum tfa_header_type)hdr->id;
|
||
|
if ((type == msg_hdr)
|
||
|
|| ((type == volstep_hdr) && (tfa->tfa_family == 2))) {
|
||
|
sub_ver_string[0] = hdr->subversion[0];
|
||
|
sub_ver_string[1] = hdr->subversion[1];
|
||
|
sub_ver_string[2] = '\0';
|
||
|
|
||
|
kerr = kstrtou16(sub_ver_string, 16, &subversion);
|
||
|
if (kerr < 0)
|
||
|
pr_err("%s: error in readaing subversion\n", __func__);
|
||
|
|
||
|
if ((subversion > 0)
|
||
|
&& (((hdr->customer[0]) == 'A')
|
||
|
&& ((hdr->customer[1]) == 'P')
|
||
|
&& ((hdr->customer[2]) == 'I')
|
||
|
&& ((hdr->customer[3]) == 'V'))) {
|
||
|
pr_debug("%s: msg subversion 0x%x, custom v%d.%d.%d.%d\n",
|
||
|
__func__, subversion,
|
||
|
hdr->customer[4],
|
||
|
hdr->customer[5],
|
||
|
hdr->customer[6],
|
||
|
hdr->customer[7]);
|
||
|
|
||
|
if (tfa->fw_itf_ver[0] == 0xff) {
|
||
|
err = tfa_get_fw_api_version(tfa,
|
||
|
(unsigned char *)&tfa->fw_itf_ver[0]);
|
||
|
if (err) {
|
||
|
pr_debug("[%s] cannot get FW API ver, error = %d\n",
|
||
|
__func__, err);
|
||
|
return err;
|
||
|
}
|
||
|
} else {
|
||
|
pr_debug("%s: checked - itf v%d.%d.%d.%d\n",
|
||
|
__func__,
|
||
|
tfa->fw_itf_ver[0],
|
||
|
tfa->fw_itf_ver[1],
|
||
|
tfa->fw_itf_ver[2],
|
||
|
tfa->fw_itf_ver[3]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch (type) {
|
||
|
case msg_hdr: /* generic DSP message */
|
||
|
size = hdr->size - sizeof(struct tfa_msg_file);
|
||
|
|
||
|
data_buf = (char *)((struct tfa_msg_file *)hdr)->data;
|
||
|
|
||
|
/* write temp stored in driver */
|
||
|
/* SetChipTempSelect */
|
||
|
if ((data_buf[1] == (0x80 | MODULE_FRAMEWORK))
|
||
|
&& data_buf[2] == FW_PAR_ID_SET_CHIP_TEMP_SELECTOR
|
||
|
&& tfa->temp != 0xffff) {
|
||
|
/* set index by skipping command and two parameters */
|
||
|
pr_info("%s: check temp in msg 0x%02x%02x%02x, @ 0x%02x\n",
|
||
|
__func__, data_buf[TEMP_OFFSET],
|
||
|
data_buf[TEMP_OFFSET + 1],
|
||
|
data_buf[TEMP_OFFSET + 2],
|
||
|
temp_index);
|
||
|
|
||
|
for (channel = 0; channel < MAX_CHANNELS; channel++) {
|
||
|
temp_index = TEMP_OFFSET + channel * 3;
|
||
|
data_buf[temp_index]
|
||
|
= (char)((tfa->temp & 0xff0000) >> 16);
|
||
|
data_buf[temp_index + 1]
|
||
|
= (char)((tfa->temp & 0x00ff00) >> 8);
|
||
|
data_buf[temp_index + 2]
|
||
|
= (char)(tfa->temp & 0x0000ff);
|
||
|
}
|
||
|
|
||
|
pr_info("%s: set temp from driver 0x%02x%02x%02x\n",
|
||
|
__func__, data_buf[TEMP_OFFSET],
|
||
|
data_buf[TEMP_OFFSET + 1],
|
||
|
data_buf[TEMP_OFFSET + 2]);
|
||
|
}
|
||
|
|
||
|
org_cmd = data_buf[2];
|
||
|
if ((tfa->is_configured > 0)
|
||
|
&& (data_buf[1] == (0x80 | MODULE_SPEAKERBOOST))) {
|
||
|
/* SB_PARAM_SET_ALGO_PARAMS_WITHOUT_RESET */
|
||
|
if (data_buf[2] == SB_PARAM_SET_ALGO_PARAMS)
|
||
|
data_buf[2]
|
||
|
= SB_PARAM_SET_ALGO_PARAMS_WITHOUT_RESET;
|
||
|
/* SB_PARAM_SET_MBDRC_WITHOUT_RESET */
|
||
|
if (data_buf[2] == SB_PARAM_SET_MBDRC)
|
||
|
data_buf[2]
|
||
|
= SB_PARAM_SET_MBDRC_WITHOUT_RESET;
|
||
|
|
||
|
if (org_cmd != data_buf[2])
|
||
|
pr_info("%s: cmd=0x%02x to 0x%02x (configured)\n",
|
||
|
__func__, org_cmd, data_buf[2]);
|
||
|
}
|
||
|
|
||
|
err = dsp_msg(tfa, size,
|
||
|
(const char *)((struct tfa_msg_file *)hdr)->data);
|
||
|
|
||
|
if (org_cmd != data_buf[2]) {
|
||
|
pr_info("%s: cmd=0x%02x to 0x%02x (restored)\n",
|
||
|
__func__, data_buf[2], org_cmd);
|
||
|
data_buf[2] = org_cmd;
|
||
|
}
|
||
|
|
||
|
/* Reset bypass if writing msg files */
|
||
|
if (err == TFA98XX_ERROR_OK)
|
||
|
tfa->is_bypass = 0;
|
||
|
break;
|
||
|
case volstep_hdr:
|
||
|
if (tfa->tfa_family == 2)
|
||
|
err = tfa_cont_write_vstep_max2(tfa,
|
||
|
(struct tfa_volume_step_max2_file *)hdr,
|
||
|
vstep_idx, vstep_msg_idx);
|
||
|
else
|
||
|
err = tfa_cont_write_vstep(tfa,
|
||
|
(struct tfa_volume_step2_file *)hdr,
|
||
|
vstep_idx);
|
||
|
|
||
|
/* If writing the vstep was successful, set new current vstep */
|
||
|
|
||
|
/* Reset bypass if writing vstep files */
|
||
|
if (err == TFA98XX_ERROR_OK)
|
||
|
tfa->is_bypass = 0;
|
||
|
break;
|
||
|
case speaker_hdr:
|
||
|
if (tfa->tfa_family == 2) {
|
||
|
/* Remove header and xml_id */
|
||
|
size = hdr->size - sizeof(struct tfa_spk_header)
|
||
|
- sizeof(struct tfa_fw_ver);
|
||
|
|
||
|
err = dsp_msg(tfa, size,
|
||
|
(const char *)
|
||
|
(((struct tfa_speaker_file *)hdr)->data
|
||
|
+ (sizeof(struct tfa_fw_ver))));
|
||
|
} else {
|
||
|
size = hdr->size - sizeof(struct tfa_speaker_file);
|
||
|
err = tfa98xx_dsp_write_speaker_parameters(tfa, size,
|
||
|
(const unsigned char *)
|
||
|
((struct tfa_speaker_file *)hdr)->data);
|
||
|
}
|
||
|
break;
|
||
|
case preset_hdr:
|
||
|
size = hdr->size - sizeof(struct tfa_preset_file);
|
||
|
err = tfa98xx_dsp_write_preset(tfa, size,
|
||
|
(const unsigned char *)
|
||
|
((struct tfa_preset_file *)hdr)->data);
|
||
|
break;
|
||
|
case equalizer_hdr:
|
||
|
err = tfa_cont_write_filterbank(tfa,
|
||
|
((struct tfa_equalizer_file *)hdr)->filter);
|
||
|
break;
|
||
|
case patch_hdr:
|
||
|
size = hdr->size - sizeof(struct tfa_patch_file);
|
||
|
/* total length */
|
||
|
err = tfa_dsp_patch(tfa, size,
|
||
|
(const unsigned char *)
|
||
|
((struct tfa_patch_file *)hdr)->data);
|
||
|
break;
|
||
|
case config_hdr:
|
||
|
size = hdr->size - sizeof(struct tfa_config_file);
|
||
|
err = tfa98xx_dsp_write_config(tfa, size,
|
||
|
(const unsigned char *)
|
||
|
((struct tfa_config_file *)hdr)->data);
|
||
|
break;
|
||
|
case drc_hdr:
|
||
|
if (hdr->version[0] == TFA_DR3_VERSION) {
|
||
|
/* Size is total size - hdrsize(36) - xmlversion(3) */
|
||
|
size = hdr->size - sizeof(struct tfa_drc_file2);
|
||
|
err = tfa_cont_write_drc_file(tfa, size,
|
||
|
((struct tfa_drc_file2 *)hdr)->data);
|
||
|
} else {
|
||
|
/*
|
||
|
* The DRC file is split as:
|
||
|
* 36 bytes for generic header
|
||
|
* (customer, application, and type)
|
||
|
* 127x3 (381) bytes first block contains
|
||
|
* the device and sample rate
|
||
|
* independent settings
|
||
|
* 127x3 (381) bytes block
|
||
|
* the device and sample rate
|
||
|
* specific values.
|
||
|
* The second block can always be recalculated
|
||
|
* from the first block,
|
||
|
* if vlsCal and the sample rate are known.
|
||
|
*/
|
||
|
/* size = hdr->size - sizeof(struct tfa_drc_file); */
|
||
|
size = 381; /* fixed size for first block */
|
||
|
|
||
|
/* +381 is done to only send 2nd part of drc block */
|
||
|
err = tfa98xx_dsp_write_drc(tfa, size,
|
||
|
((const unsigned char *)
|
||
|
((struct tfa_drc_file *)hdr)->data + 381));
|
||
|
}
|
||
|
break;
|
||
|
case info_hdr:
|
||
|
/* Ignore */
|
||
|
break;
|
||
|
default:
|
||
|
pr_err("Header is of unknown type: 0x%x\n", type);
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* get the 1st of this dsc type this devicelist
|
||
|
*/
|
||
|
static struct tfa_desc_ptr *tfa_cnt_get_dsc
|
||
|
(struct tfa_container *cnt, enum tfa_descriptor_type type, int dev_idx)
|
||
|
{
|
||
|
struct tfa_device_list *dev = tfa_cont_device(cnt, dev_idx);
|
||
|
struct tfa_desc_ptr *_this;
|
||
|
int i;
|
||
|
|
||
|
if (!dev)
|
||
|
return NULL;
|
||
|
|
||
|
/* process the list until a the type is encountered */
|
||
|
for (i = 0; i < dev->length; i++)
|
||
|
if (dev->list[i].type == (uint32_t)type) {
|
||
|
_this = (struct tfa_desc_ptr *)
|
||
|
(dev->list[i].offset + (uint8_t *)cnt);
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* get the device type from the patch in this devicelist
|
||
|
* - find the patch file for this devidx
|
||
|
* - return the devid from the patch or 0 if not found
|
||
|
*/
|
||
|
int tfa_cont_get_devid(struct tfa_container *cnt, int dev_idx)
|
||
|
{
|
||
|
struct tfa_patch_file *patchfile;
|
||
|
struct tfa_desc_ptr *patchdsc;
|
||
|
uint8_t *patchheader;
|
||
|
unsigned short devid, checkaddress;
|
||
|
int checkvalue;
|
||
|
|
||
|
patchdsc = tfa_cnt_get_dsc(cnt, dsc_patch, dev_idx);
|
||
|
if (!patchdsc) /* no patch for this device, assume non-i2c */
|
||
|
return 0;
|
||
|
|
||
|
patchdsc += 2; /* first the filename dsc and filesize, so skip them */
|
||
|
patchfile = (struct tfa_patch_file *)patchdsc;
|
||
|
|
||
|
patchheader = patchfile->data;
|
||
|
|
||
|
checkaddress = (patchheader[1] << 8) + patchheader[2];
|
||
|
checkvalue = (patchheader[3] << 16)
|
||
|
+ (patchheader[4] << 8) + patchheader[5];
|
||
|
|
||
|
devid = patchheader[0];
|
||
|
|
||
|
if (checkaddress == 0xFFFF
|
||
|
&& checkvalue != 0xFFFFFF && checkvalue != 0)
|
||
|
devid = patchheader[5] << 8
|
||
|
| patchheader[0]; /* full revid */
|
||
|
|
||
|
return devid;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* get the firmware version from the patch in this devicelist
|
||
|
*/
|
||
|
int tfa_cnt_get_patch_version(struct tfa_device *tfa)
|
||
|
{
|
||
|
struct tfa_patch_file *patchfile;
|
||
|
struct tfa_desc_ptr *patchdsc;
|
||
|
uint8_t *data;
|
||
|
int size, version;
|
||
|
|
||
|
if (tfa->cnt == NULL)
|
||
|
return TFA_ERROR;
|
||
|
|
||
|
patchdsc = tfa_cnt_get_dsc(tfa->cnt, dsc_patch, tfa->dev_idx);
|
||
|
patchdsc += 2; /* first the filename dsc and filesize, so skip them */
|
||
|
patchfile = (struct tfa_patch_file *)patchdsc;
|
||
|
|
||
|
size = patchfile->hdr.size - sizeof(struct tfa_patch_file);
|
||
|
data = patchfile->data;
|
||
|
|
||
|
version = (data[size - 3] << 16)
|
||
|
+ (data[size - 2] << 8) + data[size - 1];
|
||
|
|
||
|
return version;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* get the responder for the device if it exists
|
||
|
*/
|
||
|
enum tfa98xx_error tfa_cont_get_resp(struct tfa_device *tfa,
|
||
|
uint8_t *resp_addr)
|
||
|
{
|
||
|
struct tfa_device_list *dev = NULL;
|
||
|
|
||
|
/* Make sure the cnt file is loaded */
|
||
|
if (tfa->cnt != NULL)
|
||
|
dev = tfa_cont_device(tfa->cnt, tfa->dev_idx);
|
||
|
|
||
|
if (dev == NULL) {
|
||
|
/* Check if responder argument is used! */
|
||
|
if (gresp_address == 0)
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
|
||
|
*resp_addr = gresp_address;
|
||
|
|
||
|
return TFA98XX_ERROR_OK;
|
||
|
}
|
||
|
|
||
|
*resp_addr = dev->dev;
|
||
|
|
||
|
return TFA98XX_ERROR_OK;
|
||
|
}
|
||
|
|
||
|
/* without cnt, we can always have used the responder argument */
|
||
|
void tfa_cont_set_resp(uint8_t resp_addr)
|
||
|
{
|
||
|
gresp_address = resp_addr;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* lookup responder and return device index
|
||
|
*/
|
||
|
int tfa_cont_get_idx(struct tfa_device *tfa)
|
||
|
{
|
||
|
struct tfa_device_list *dev = NULL;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < tfa->cnt->ndev; i++) {
|
||
|
dev = tfa_cont_device(tfa->cnt, i);
|
||
|
if (dev->dev == tfa->resp_address)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (i == tfa->cnt->ndev)
|
||
|
return TFA_ERROR;
|
||
|
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
int tfa_cont_get_idx_tfadsp(struct tfa_device *tfa, int value)
|
||
|
{
|
||
|
struct tfa_device_list *dev = NULL;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < tfa->cnt->ndev; i++) {
|
||
|
dev = tfa_cont_device(tfa->cnt, i);
|
||
|
if (dev->dev == value)
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
return TFA_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* write a bit field
|
||
|
*/
|
||
|
enum tfa98xx_error tfa_run_write_bitfield(struct tfa_device *tfa,
|
||
|
struct tfa_bitfield bf)
|
||
|
{
|
||
|
enum tfa98xx_error error;
|
||
|
uint16_t value;
|
||
|
union {
|
||
|
uint16_t field;
|
||
|
struct tfa_bf_enum bf_enum;
|
||
|
} bf_uni;
|
||
|
|
||
|
value = bf.value;
|
||
|
bf_uni.field = bf.field;
|
||
|
error = tfa->dev_ops.set_bitfield(tfa, bf_uni.field, value);
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* read a bit field
|
||
|
*/
|
||
|
enum tfa98xx_error tfa_run_read_bitfield(struct tfa_device *tfa,
|
||
|
struct tfa_bitfield *bf)
|
||
|
{
|
||
|
enum tfa98xx_error error;
|
||
|
union {
|
||
|
uint16_t field;
|
||
|
struct tfa_bf_enum bf_enum;
|
||
|
} bf_uni;
|
||
|
uint16_t regvalue, msk;
|
||
|
|
||
|
bf_uni.field = bf->field;
|
||
|
|
||
|
error = tfa_reg_read(tfa,
|
||
|
(unsigned char)(bf_uni.bf_enum.address), ®value);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
msk = ((1 << (bf_uni.bf_enum.len + 1)) - 1) << bf_uni.bf_enum.pos;
|
||
|
|
||
|
regvalue &= msk;
|
||
|
bf->value = regvalue >> bf_uni.bf_enum.pos;
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* dsp mem direct write
|
||
|
*/
|
||
|
static enum tfa98xx_error tfa_run_write_dsp_mem(struct tfa_device *tfa,
|
||
|
struct tfa_dsp_mem *cfmem)
|
||
|
{
|
||
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < cfmem->size; i++) {
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("dsp mem (%d): 0x%02x=0x%04x\n",
|
||
|
cfmem->type, cfmem->address, cfmem->words[i]);
|
||
|
|
||
|
error = mem_write(tfa, cfmem->address++,
|
||
|
cfmem->words[i], cfmem->type);
|
||
|
if (error)
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* write filter payload to DSP
|
||
|
* note that the data is in an aligned union for all filter variants
|
||
|
* the aa data is used but it's the same for all of them
|
||
|
*/
|
||
|
static enum tfa98xx_error tfa_run_write_filter(struct tfa_device *tfa,
|
||
|
union tfa_cont_biquad *bq)
|
||
|
{
|
||
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
||
|
enum tfa98xx_dmem dmem;
|
||
|
uint16_t address;
|
||
|
uint8_t data[3 * 3 + sizeof(bq->aa.bytes)];
|
||
|
int i, channel = 0, runs = 1;
|
||
|
int8_t saved_index = bq->aa.index; /* This is used to set back index */
|
||
|
|
||
|
/* Channel=1 is primary, Channel=2 is secondary*/
|
||
|
if (bq->aa.index > 100) {
|
||
|
bq->aa.index -= 100;
|
||
|
channel = 2;
|
||
|
} else if (bq->aa.index > 50) {
|
||
|
bq->aa.index -= 50;
|
||
|
channel = 1;
|
||
|
} else if ((tfa->rev & 0xff) == 0x88) {
|
||
|
runs = 2;
|
||
|
}
|
||
|
|
||
|
if (tfa->verbose) {
|
||
|
if (channel == 2)
|
||
|
pr_debug("filter[%d,S]", bq->aa.index);
|
||
|
else if (channel == 1)
|
||
|
pr_debug("filter[%d,P]", bq->aa.index);
|
||
|
else
|
||
|
pr_debug("filter[%d]", bq->aa.index);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < runs; i++) {
|
||
|
if (runs == 2)
|
||
|
channel++;
|
||
|
|
||
|
/* get the target address for the filter on this device */
|
||
|
dmem = tfa98xx_filter_mem(tfa, bq->aa.index, &address, channel);
|
||
|
if (dmem == TFA98XX_DMEM_ERR) {
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("Warning: XFilter settings are applied via msg file (ini filter[x] format is skipped).\n");
|
||
|
/* Don't exit with an error here,
|
||
|
* We could continue without problems
|
||
|
*/
|
||
|
return TFA98XX_ERROR_OK;
|
||
|
}
|
||
|
|
||
|
/* send a DSP memory message
|
||
|
* that targets the devices specific memory for the filter
|
||
|
* msg params: which_mem, start_offset, num_words
|
||
|
*/
|
||
|
memset(data, 0, 3 * 3);
|
||
|
data[2] = dmem; /* output[0] = which_mem */
|
||
|
data[4] = address >> 8; /* output[1] = start_offset */
|
||
|
data[5] = address & 0xff;
|
||
|
data[8] = sizeof(bq->aa.bytes) / 3; /*output[2] = num_words */
|
||
|
/* payload */
|
||
|
memcpy(&data[9], bq->aa.bytes, sizeof(bq->aa.bytes));
|
||
|
|
||
|
if (tfa->tfa_family == 2)
|
||
|
error = tfa_dsp_cmd_id_write(tfa,
|
||
|
MODULE_FRAMEWORK, FW_PAR_ID_SET_MEMORY,
|
||
|
sizeof(data), data);
|
||
|
else
|
||
|
error = tfa_dsp_cmd_id_write(tfa,
|
||
|
MODULE_FRAMEWORK, 4 /* param */,
|
||
|
sizeof(data), data);
|
||
|
}
|
||
|
|
||
|
/* Because we can load the same filters multiple times
|
||
|
* For example: When we switch profile we re-write in operating mode.
|
||
|
* We then need to remember the index (primary, secondary or both)
|
||
|
*/
|
||
|
bq->aa.index = saved_index;
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* write the register based on the input address, value and mask
|
||
|
* only the part that is masked will be updated
|
||
|
*/
|
||
|
static enum tfa98xx_error tfa_run_write_register
|
||
|
(struct tfa_device *tfa, struct tfa_reg_patch *reg)
|
||
|
{
|
||
|
enum tfa98xx_error error;
|
||
|
uint16_t value, newvalue;
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("register: 0x%02x=0x%04x (msk=0x%04x)\n",
|
||
|
reg->address, reg->value, reg->mask);
|
||
|
|
||
|
error = tfa_reg_read(tfa, reg->address, &value);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
value &= ~reg->mask;
|
||
|
newvalue = reg->value & reg->mask;
|
||
|
|
||
|
value |= newvalue;
|
||
|
error = tfa_reg_write(tfa, reg->address, value);
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
/* write reg and bitfield items in the devicelist to the target */
|
||
|
enum tfa98xx_error tfa_cont_write_regs_dev(struct tfa_device *tfa)
|
||
|
{
|
||
|
struct tfa_device_list *dev
|
||
|
= tfa_cont_device(tfa->cnt, tfa->dev_idx);
|
||
|
struct tfa_bitfield *bit_f;
|
||
|
int i;
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
|
||
|
if (!dev)
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
|
||
|
/* process the list until a patch, file of profile is encountered */
|
||
|
for (i = 0; i < dev->length; i++) {
|
||
|
if (dev->list[i].type == dsc_patch
|
||
|
|| dev->list[i].type == dsc_file
|
||
|
|| dev->list[i].type == dsc_profile)
|
||
|
break;
|
||
|
|
||
|
if (dev->list[i].type == dsc_bit_field) {
|
||
|
bit_f = (struct tfa_bitfield *)
|
||
|
(dev->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
err = tfa_run_write_bitfield(tfa, *bit_f);
|
||
|
}
|
||
|
if (dev->list[i].type == dsc_register)
|
||
|
err = tfa_run_write_register(tfa,
|
||
|
(struct tfa_reg_patch *)
|
||
|
(dev->list[i].offset + (char *)tfa->cnt));
|
||
|
|
||
|
if (err)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/* write reg and bitfield items in the profilelist the target */
|
||
|
enum tfa98xx_error tfa_cont_write_regs_prof(struct tfa_device *tfa,
|
||
|
int prof_idx)
|
||
|
{
|
||
|
struct tfa_profile_list *prof
|
||
|
= tfa_cont_get_dev_prof_list(tfa->cnt, tfa->dev_idx, prof_idx);
|
||
|
struct tfa_bitfield *bitf;
|
||
|
unsigned int i;
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
|
||
|
if (!prof)
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("----- profile: %s (%d) -----\n",
|
||
|
tfa_cont_get_string(tfa->cnt, &prof->name), prof_idx);
|
||
|
|
||
|
/* process the list
|
||
|
* until the end of the profile or the default section
|
||
|
*/
|
||
|
for (i = 0; i < prof->length; i++) {
|
||
|
/* only to write the values before the default section
|
||
|
* when we switch profile
|
||
|
*/
|
||
|
if (prof->list[i].type == dsc_default)
|
||
|
break;
|
||
|
|
||
|
if (prof->list[i].type == dsc_bit_field) {
|
||
|
bitf = (struct tfa_bitfield *)
|
||
|
(prof->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
err = tfa_run_write_bitfield(tfa, *bitf);
|
||
|
}
|
||
|
if (prof->list[i].type == dsc_register)
|
||
|
err = tfa_run_write_register(tfa,
|
||
|
(struct tfa_reg_patch *)(prof->list[i].offset
|
||
|
+ (char *)tfa->cnt));
|
||
|
if (err)
|
||
|
break;
|
||
|
}
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/* write patchfile in the devicelist to the target */
|
||
|
enum tfa98xx_error tfa_cont_write_patch(struct tfa_device *tfa)
|
||
|
{
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
struct tfa_device_list *dev = tfa_cont_device(tfa->cnt, tfa->dev_idx);
|
||
|
struct tfa_file_dsc *file;
|
||
|
struct tfa_patch_file *patchfile;
|
||
|
int size, i;
|
||
|
|
||
|
if (!dev)
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
|
||
|
/* process the list until a patch is encountered */
|
||
|
for (i = 0; i < dev->length; i++) {
|
||
|
if (dev->list[i].type == dsc_patch) {
|
||
|
file = (struct tfa_file_dsc *)
|
||
|
(dev->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
patchfile = (struct tfa_patch_file *)&file->data;
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
tfa_cont_show_header(&patchfile->hdr);
|
||
|
|
||
|
/* size is total length */
|
||
|
size = patchfile->hdr.size
|
||
|
- sizeof(struct tfa_patch_file);
|
||
|
err = tfa_dsp_patch(tfa, size,
|
||
|
(const unsigned char *)patchfile->data);
|
||
|
if (err)
|
||
|
return err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TFA98XX_ERROR_OK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Create a buffer which can be used to send to the dsp.
|
||
|
*/
|
||
|
static void create_dsp_buffer_msg(struct tfa_device *tfa,
|
||
|
struct tfa_msg *msg, char *buffer, int *size)
|
||
|
{
|
||
|
int i, nr = 0;
|
||
|
|
||
|
(void)tfa;
|
||
|
|
||
|
/* Copy cmd_id. Remember that the cmd_id is reversed */
|
||
|
buffer[nr++] = msg->cmd_id[2];
|
||
|
buffer[nr++] = msg->cmd_id[1];
|
||
|
buffer[nr++] = msg->cmd_id[0];
|
||
|
|
||
|
/* Copy the data to the buffer */
|
||
|
for (i = 0; i < msg->msg_size; i++) {
|
||
|
buffer[nr++] = (uint8_t)((msg->data[i] >> 16) & 0xffff);
|
||
|
buffer[nr++] = (uint8_t)((msg->data[i] >> 8) & 0xff);
|
||
|
buffer[nr++] = (uint8_t)(msg->data[i] & 0xff);
|
||
|
}
|
||
|
|
||
|
*size = nr;
|
||
|
}
|
||
|
|
||
|
/* write all param files in the devicelist to the target */
|
||
|
enum tfa98xx_error tfa_cont_write_files(struct tfa_device *tfa)
|
||
|
{
|
||
|
struct tfa_device_list *dev = NULL;
|
||
|
int dev_idx_files = 0;
|
||
|
struct tfa_file_dsc *file;
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
char buffer[(MEMTRACK_MAX_WORDS * 3) + 3] = {0};
|
||
|
/* every word requires 3 bytes, and 3 is the msg */
|
||
|
int i, size = 0;
|
||
|
|
||
|
dev_idx_files = (tfa->dev_tfadsp == -1)
|
||
|
? tfa->dev_idx : tfa->dev_tfadsp;
|
||
|
dev = tfa_cont_device(tfa->cnt, dev_idx_files);
|
||
|
|
||
|
if (!dev)
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
|
||
|
/* process the list and write all files */
|
||
|
for (i = 0; i < dev->length; i++) {
|
||
|
if (dev->list[i].type == dsc_file) {
|
||
|
file = (struct tfa_file_dsc *)
|
||
|
(dev->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
if (tfa_cont_write_file
|
||
|
(tfa, file, 0, TFA_MAX_VSTEP_MSG_MARKER))
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
}
|
||
|
|
||
|
if (dev->list[i].type == dsc_set_input_select
|
||
|
|| dev->list[i].type == dsc_set_output_select
|
||
|
|| dev->list[i].type == dsc_set_program_config
|
||
|
|| dev->list[i].type == dsc_set_lag_w
|
||
|
|| dev->list[i].type == dsc_set_gains
|
||
|
|| dev->list[i].type == dsc_set_vbat_factors
|
||
|
|| dev->list[i].type == dsc_set_senses_cal
|
||
|
|| dev->list[i].type == dsc_set_senses_delay
|
||
|
|| dev->list[i].type == dsc_set_mb_drc
|
||
|
|| dev->list[i].type == dsc_set_fw_use_case
|
||
|
|| dev->list[i].type == dsc_set_vddp_config) {
|
||
|
if (tfa_cont_is_config_loaded(tfa))
|
||
|
continue;
|
||
|
|
||
|
create_dsp_buffer_msg(tfa, (struct tfa_msg *)
|
||
|
(dev->list[i].offset + (char *)tfa->cnt),
|
||
|
buffer, &size);
|
||
|
|
||
|
if (tfa->verbose) {
|
||
|
pr_debug("command: %s=0x%02x%02x%02x\n",
|
||
|
tfa_cont_get_command_string
|
||
|
(dev->list[i].type),
|
||
|
(unsigned char)buffer[0],
|
||
|
(unsigned char)buffer[1],
|
||
|
(unsigned char)buffer[2]);
|
||
|
}
|
||
|
|
||
|
err = dsp_msg(tfa, size, buffer);
|
||
|
}
|
||
|
|
||
|
if (dev->list[i].type == dsc_cmd) {
|
||
|
if (tfa_cont_is_config_loaded(tfa))
|
||
|
continue;
|
||
|
|
||
|
size = *(uint16_t *)
|
||
|
(dev->list[i].offset + (char *)tfa->cnt);
|
||
|
|
||
|
err = dsp_msg(tfa, size,
|
||
|
dev->list[i].offset + 2 + (char *)tfa->cnt);
|
||
|
|
||
|
if (tfa->verbose) {
|
||
|
const char *cmd_id = dev->list[i].offset
|
||
|
+ 2 + (char *)tfa->cnt;
|
||
|
|
||
|
pr_debug("Writing cmd=0x%02x%02x%02x\n",
|
||
|
(uint8_t)cmd_id[0],
|
||
|
(uint8_t)cmd_id[1],
|
||
|
(uint8_t)cmd_id[2]);
|
||
|
}
|
||
|
}
|
||
|
if (err != TFA98XX_ERROR_OK)
|
||
|
break;
|
||
|
|
||
|
if (dev->list[i].type == dsc_cf_mem)
|
||
|
err = tfa_run_write_dsp_mem(tfa,
|
||
|
(struct tfa_dsp_mem *)(dev->list[i].offset
|
||
|
+ (uint8_t *)tfa->cnt));
|
||
|
|
||
|
if (err != TFA98XX_ERROR_OK)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* write all param files in the profilelist to the target
|
||
|
* this is used during startup when maybe ACS is set
|
||
|
*/
|
||
|
enum tfa98xx_error tfa_cont_write_files_prof(struct tfa_device *tfa,
|
||
|
int prof_idx, int vstep_idx)
|
||
|
{
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
struct tfa_profile_list *prof = NULL;
|
||
|
int dev_idx_files = 0;
|
||
|
char buffer[(MEMTRACK_MAX_WORDS * 3) + 3] = {0};
|
||
|
/* every word requires 3 bytes, and 3 is the msg */
|
||
|
unsigned int i;
|
||
|
struct tfa_file_dsc *file;
|
||
|
struct tfa_patch_file *patchfile;
|
||
|
int size;
|
||
|
|
||
|
dev_idx_files = (tfa->dev_tfadsp == -1)
|
||
|
? tfa->dev_idx : tfa->dev_tfadsp;
|
||
|
prof = tfa_cont_get_dev_prof_list(tfa->cnt,
|
||
|
dev_idx_files, prof_idx);
|
||
|
|
||
|
if (!prof)
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
|
||
|
/* process the list and write all files */
|
||
|
for (i = 0; i < prof->length; i++) {
|
||
|
switch (prof->list[i].type) {
|
||
|
case dsc_file:
|
||
|
file = (struct tfa_file_dsc *)
|
||
|
(prof->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
err = tfa_cont_write_file(tfa, file,
|
||
|
vstep_idx, TFA_MAX_VSTEP_MSG_MARKER);
|
||
|
break;
|
||
|
case dsc_patch:
|
||
|
file = (struct tfa_file_dsc *)
|
||
|
(prof->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
patchfile = (struct tfa_patch_file *)&file->data;
|
||
|
if (tfa->verbose)
|
||
|
tfa_cont_show_header(&patchfile->hdr);
|
||
|
|
||
|
/* size is total length */
|
||
|
size = patchfile->hdr.size
|
||
|
- sizeof(struct tfa_patch_file);
|
||
|
err = tfa_dsp_patch(tfa, size,
|
||
|
(const unsigned char *)patchfile->data);
|
||
|
break;
|
||
|
case dsc_cf_mem:
|
||
|
err = tfa_run_write_dsp_mem(tfa,
|
||
|
(struct tfa_dsp_mem *)(prof->list[i].offset
|
||
|
+ (uint8_t *)tfa->cnt));
|
||
|
break;
|
||
|
case dsc_set_input_select:
|
||
|
case dsc_set_output_select:
|
||
|
case dsc_set_program_config:
|
||
|
case dsc_set_lag_w:
|
||
|
case dsc_set_gains:
|
||
|
case dsc_set_vbat_factors:
|
||
|
case dsc_set_senses_cal:
|
||
|
case dsc_set_senses_delay:
|
||
|
case dsc_set_mb_drc:
|
||
|
case dsc_set_fw_use_case:
|
||
|
case dsc_set_vddp_config:
|
||
|
if (tfa_cont_is_config_loaded(tfa))
|
||
|
break;
|
||
|
|
||
|
create_dsp_buffer_msg(tfa,
|
||
|
(struct tfa_msg *)(prof->list[i].offset
|
||
|
+ (uint8_t *)tfa->cnt), buffer, &size);
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("command: %s=0x%02x%02x%02x\n",
|
||
|
tfa_cont_get_command_string
|
||
|
(prof->list[i].type),
|
||
|
(unsigned char)buffer[0],
|
||
|
(unsigned char)buffer[1],
|
||
|
(unsigned char)buffer[2]);
|
||
|
|
||
|
err = dsp_msg(tfa, size, buffer);
|
||
|
|
||
|
/* Reset bypass if writing msg files */
|
||
|
if (err == TFA98XX_ERROR_OK)
|
||
|
tfa->is_bypass = 0;
|
||
|
break;
|
||
|
case dsc_cmd: /* to change set-commands per profile */
|
||
|
if (tfa_cont_is_config_loaded(tfa))
|
||
|
break;
|
||
|
|
||
|
size = *(uint16_t *)
|
||
|
(prof->list[i].offset
|
||
|
+ (char *)tfa->cnt);
|
||
|
err = dsp_msg(tfa, size,
|
||
|
prof->list[i].offset
|
||
|
+ 2 + (char *)tfa->cnt);
|
||
|
|
||
|
if (tfa->verbose) {
|
||
|
const char *cmd_id = (prof->list[i].offset
|
||
|
+ 2 + (char *)tfa->cnt);
|
||
|
pr_debug("Writing cmd=0x%02x%02x%02x\n",
|
||
|
(uint8_t)cmd_id[0],
|
||
|
(uint8_t)cmd_id[1],
|
||
|
(uint8_t)cmd_id[2]);
|
||
|
}
|
||
|
|
||
|
/* Reset bypass if writing msg files */
|
||
|
if (err == TFA98XX_ERROR_OK)
|
||
|
tfa->is_bypass = 0;
|
||
|
break;
|
||
|
default:
|
||
|
/* ignore any other type */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static enum tfa98xx_error tfa_cont_write_item
|
||
|
(struct tfa_device *tfa, struct tfa_desc_ptr *dsc)
|
||
|
{
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
struct tfa_reg_patch *reg;
|
||
|
struct tfa_mode *cas;
|
||
|
struct tfa_bitfield *bitf;
|
||
|
|
||
|
/* When no DSP should only write to HW registers. */
|
||
|
if (tfa->ext_dsp == 0
|
||
|
&& !(dsc->type == dsc_bit_field
|
||
|
|| dsc->type == dsc_register)) {
|
||
|
return TFA98XX_ERROR_OK;
|
||
|
}
|
||
|
|
||
|
switch (dsc->type) {
|
||
|
case dsc_default:
|
||
|
case dsc_device: /* ignore */
|
||
|
case dsc_profile: /* profile list */
|
||
|
break;
|
||
|
case dsc_register: /* register patch */
|
||
|
reg = (struct tfa_reg_patch *)
|
||
|
(dsc->offset + (uint8_t *)tfa->cnt);
|
||
|
/* pr_debug("$0x%2x=0x%02x,0x%02x\n",
|
||
|
* reg->address, reg->mask, reg->value);
|
||
|
*/
|
||
|
return tfa_run_write_register(tfa, reg);
|
||
|
case dsc_string: /* ascii: zero terminated string */
|
||
|
pr_debug(";string: %s\n",
|
||
|
tfa_cont_get_string(tfa->cnt, dsc));
|
||
|
break;
|
||
|
case dsc_file: /* filename + file contents */
|
||
|
case dsc_patch:
|
||
|
break;
|
||
|
case dsc_mode:
|
||
|
cas = (struct tfa_mode *)
|
||
|
(dsc->offset + (uint8_t *)tfa->cnt);
|
||
|
if (cas->value == TFA98XX_MODE_RCV)
|
||
|
tfa98xx_select_mode(tfa, TFA98XX_MODE_RCV);
|
||
|
else
|
||
|
tfa98xx_select_mode(tfa, TFA98XX_MODE_NORMAL);
|
||
|
break;
|
||
|
case dsc_cf_mem:
|
||
|
err = tfa_run_write_dsp_mem(tfa,
|
||
|
(struct tfa_dsp_mem *)
|
||
|
(dsc->offset + (uint8_t *)tfa->cnt));
|
||
|
break;
|
||
|
case dsc_bit_field:
|
||
|
bitf = (struct tfa_bitfield *)
|
||
|
(dsc->offset + (uint8_t *)tfa->cnt);
|
||
|
return tfa_run_write_bitfield(tfa, *bitf);
|
||
|
case dsc_filter:
|
||
|
return tfa_run_write_filter(tfa,
|
||
|
(union tfa_cont_biquad *)
|
||
|
(dsc->offset + (uint8_t *)tfa->cnt));
|
||
|
default:
|
||
|
/* ignore any other type */
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static unsigned int tfa98xx_sr_from_field(unsigned int field)
|
||
|
{
|
||
|
switch (field) {
|
||
|
case 0:
|
||
|
return 8000;
|
||
|
case 1:
|
||
|
return 11025;
|
||
|
case 2:
|
||
|
return 12000;
|
||
|
case 3:
|
||
|
return 16000;
|
||
|
case 4:
|
||
|
return 22050;
|
||
|
case 5:
|
||
|
return 24000;
|
||
|
case 6:
|
||
|
return 32000;
|
||
|
case 7:
|
||
|
return 44100;
|
||
|
case 8:
|
||
|
return 48000;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
enum tfa98xx_error tfa_write_filters(struct tfa_device *tfa, int prof_idx)
|
||
|
{
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
struct tfa_profile_list *prof
|
||
|
= tfa_cont_get_dev_prof_list(tfa->cnt, tfa->dev_idx, prof_idx);
|
||
|
unsigned int i;
|
||
|
int status;
|
||
|
|
||
|
if (!prof)
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
|
||
|
if (tfa->verbose) {
|
||
|
pr_debug("----- profile: %s (%d) -----\n",
|
||
|
tfa_cont_get_string(tfa->cnt, &prof->name), prof_idx);
|
||
|
pr_debug("Waiting for CLKS...\n");
|
||
|
}
|
||
|
|
||
|
for (i = 10; i > 0; i--) {
|
||
|
err = tfa98xx_dsp_system_stable(tfa, &status);
|
||
|
if (status)
|
||
|
break;
|
||
|
msleep_interruptible(10);
|
||
|
}
|
||
|
|
||
|
if (i == 0) {
|
||
|
if (tfa->verbose)
|
||
|
pr_err("Unable to write filters, CLKS=0\n");
|
||
|
|
||
|
return TFA98XX_ERROR_STATE_TIMED_OUT;
|
||
|
}
|
||
|
|
||
|
/* process the list until the end of profile or default section */
|
||
|
for (i = 0; i < prof->length; i++) {
|
||
|
if (prof->list[i].type == dsc_filter) {
|
||
|
if (tfa_cont_write_item(tfa, &prof->list[i])
|
||
|
!= TFA98XX_ERROR_OK)
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
unsigned int tfa98xx_get_profile_sr(struct tfa_device *tfa,
|
||
|
unsigned int prof_idx)
|
||
|
{
|
||
|
struct tfa_bitfield *bitf;
|
||
|
unsigned int i;
|
||
|
struct tfa_device_list *dev;
|
||
|
struct tfa_profile_list *prof;
|
||
|
int fs_profile = -1;
|
||
|
|
||
|
dev = tfa_cont_device(tfa->cnt, tfa->dev_idx);
|
||
|
if (!dev)
|
||
|
return 0;
|
||
|
|
||
|
if (prof_idx == -1) { /* refer to prof in other device */
|
||
|
if (tfa->profile != -1)
|
||
|
prof_idx = tfa->profile;
|
||
|
}
|
||
|
prof = tfa_cont_get_dev_prof_list(tfa->cnt, tfa->dev_idx, prof_idx);
|
||
|
if (!prof)
|
||
|
return 0;
|
||
|
|
||
|
/* Check profile fields first */
|
||
|
for (i = 0; i < prof->length; i++) {
|
||
|
if (prof->list[i].type == dsc_default)
|
||
|
break;
|
||
|
|
||
|
/* check for profile settingd (AUDFS) */
|
||
|
if (prof->list[i].type == dsc_bit_field) {
|
||
|
bitf = (struct tfa_bitfield *)
|
||
|
(prof->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
if (bitf->field == TFA_FAM(tfa, AUDFS)) {
|
||
|
fs_profile = bitf->value;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("%s - profile fs: 0x%x = %dHz (%d - %d)\n",
|
||
|
__func__, fs_profile,
|
||
|
tfa98xx_sr_from_field(fs_profile),
|
||
|
tfa->dev_idx, prof_idx);
|
||
|
|
||
|
if (fs_profile != -1)
|
||
|
return tfa98xx_sr_from_field(fs_profile);
|
||
|
|
||
|
/* Check for container default setting */
|
||
|
/* process the list until a patch, file of profile is encountered */
|
||
|
for (i = 0; i < dev->length; i++) {
|
||
|
if (dev->list[i].type == dsc_patch
|
||
|
|| dev->list[i].type == dsc_file
|
||
|
|| dev->list[i].type == dsc_profile)
|
||
|
break;
|
||
|
|
||
|
if (dev->list[i].type == dsc_bit_field) {
|
||
|
bitf = (struct tfa_bitfield *)
|
||
|
(dev->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
if (bitf->field == TFA_FAM(tfa, AUDFS)) {
|
||
|
fs_profile = bitf->value;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
/* Ignore register case */
|
||
|
}
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("%s - default fs: 0x%x = %dHz (%d - %d)\n",
|
||
|
__func__, fs_profile,
|
||
|
tfa98xx_sr_from_field(fs_profile),
|
||
|
tfa->dev_idx, prof_idx);
|
||
|
|
||
|
if (fs_profile != -1)
|
||
|
return tfa98xx_sr_from_field(fs_profile);
|
||
|
|
||
|
return 48000; /* default of HW */
|
||
|
}
|
||
|
|
||
|
unsigned int tfa98xx_get_cnt_bitfield(struct tfa_device *tfa,
|
||
|
uint16_t bitfield)
|
||
|
{
|
||
|
struct tfa_bitfield *bitf;
|
||
|
unsigned int i;
|
||
|
struct tfa_device_list *dev;
|
||
|
struct tfa_profile_list *prof;
|
||
|
int value = -1;
|
||
|
|
||
|
/* bypass case in Max2 */
|
||
|
if (tfa->tfa_family != 2)
|
||
|
return 0;
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("%s: dev %d, prof %d, bitfield 0x%04x\n",
|
||
|
__func__, tfa->dev_idx,
|
||
|
tfa->profile, bitfield);
|
||
|
|
||
|
dev = tfa_cont_device(tfa->cnt, tfa->dev_idx);
|
||
|
if (!dev)
|
||
|
return 0;
|
||
|
|
||
|
prof = tfa_cont_get_dev_prof_list(tfa->cnt,
|
||
|
tfa->dev_idx, tfa->profile);
|
||
|
if (prof) {
|
||
|
/* Check profile fields first */
|
||
|
for (i = 0; i < prof->length; i++) {
|
||
|
if (prof->list[i].type == dsc_default)
|
||
|
break;
|
||
|
|
||
|
/* check for profile setting */
|
||
|
if (prof->list[i].type == dsc_bit_field) {
|
||
|
bitf = (struct tfa_bitfield *)
|
||
|
(prof->list[i].offset
|
||
|
+ (uint8_t *)tfa->cnt);
|
||
|
if (bitf->field == bitfield)
|
||
|
value = bitf->value;
|
||
|
if (value != -1)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("%s - profile value: 0x%x (%d - %d)\n",
|
||
|
__func__, value,
|
||
|
tfa->dev_idx, tfa->profile);
|
||
|
if (value != -1)
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
/* Check for container default setting */
|
||
|
/* process the list until a patch, file of profile is encountered */
|
||
|
for (i = 0; i < dev->length; i++) {
|
||
|
if (dev->list[i].type == dsc_patch
|
||
|
|| dev->list[i].type == dsc_file
|
||
|
|| dev->list[i].type == dsc_profile)
|
||
|
break;
|
||
|
|
||
|
if (dev->list[i].type == dsc_bit_field) {
|
||
|
bitf = (struct tfa_bitfield *)
|
||
|
(dev->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
if (bitf->field == bitfield)
|
||
|
value = bitf->value;
|
||
|
if (value != -1)
|
||
|
break;
|
||
|
}
|
||
|
/* Ignore register case */
|
||
|
}
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("%s - default value: 0x%x (%d - %d)\n",
|
||
|
__func__, value,
|
||
|
tfa->dev_idx, tfa->profile);
|
||
|
if (value != -1)
|
||
|
return value;
|
||
|
|
||
|
value = tfa_get_bf(tfa, bitfield);
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("%s - current value: 0x%x\n",
|
||
|
__func__, value);
|
||
|
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
static enum tfa98xx_error get_sample_rate_info(struct tfa_device *tfa,
|
||
|
struct tfa_profile_list *prof, struct tfa_profile_list *previous_prof,
|
||
|
int fs_previous_profile)
|
||
|
{
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
struct tfa_bitfield *bitf;
|
||
|
unsigned int i;
|
||
|
int fs_default_profile = 8; /* default is 48kHz */
|
||
|
int fs_next_profile = 8; /* default is 48kHz */
|
||
|
|
||
|
/* ---------- default settings previous profile ---------- */
|
||
|
for (i = 0; i < previous_prof->length; i++) {
|
||
|
/* Search for the default section */
|
||
|
if (i == 0) {
|
||
|
while (previous_prof->list[i].type != dsc_default
|
||
|
&& i < previous_prof->length) {
|
||
|
i++;
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
/* Only if we found the default section search for AUDFS */
|
||
|
if (i < previous_prof->length) {
|
||
|
if (previous_prof->list[i].type == dsc_bit_field) {
|
||
|
bitf = (struct tfa_bitfield *)
|
||
|
(previous_prof->list[i].offset
|
||
|
+ (uint8_t *)tfa->cnt);
|
||
|
if (bitf->field == TFA_FAM(tfa, AUDFS)) {
|
||
|
fs_default_profile = bitf->value;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* ---------- settings next profile ---------- */
|
||
|
for (i = 0; i < prof->length; i++) {
|
||
|
/* only to write the values before default section */
|
||
|
if (prof->list[i].type == dsc_default)
|
||
|
break;
|
||
|
/* search for AUDFS */
|
||
|
if (prof->list[i].type == dsc_bit_field) {
|
||
|
bitf = (struct tfa_bitfield *)
|
||
|
(prof->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
if (bitf->field == TFA_FAM(tfa, AUDFS)) {
|
||
|
fs_next_profile = bitf->value;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Enable if needed for debugging!
|
||
|
* if (tfa98xx_cnt_verbose) {
|
||
|
* pr_debug("sample rate from the previous profile: %d\n",
|
||
|
* fs_previous_profile);
|
||
|
* pr_debug("sample rate in the default section: %d\n",
|
||
|
* fs_default_profile);
|
||
|
* pr_debug("sample rate for the next profile: %d\n",
|
||
|
* fs_next_profile);
|
||
|
* }
|
||
|
*/
|
||
|
|
||
|
if (fs_next_profile != fs_default_profile) {
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("Writing delay tables for AUDFS=%d\n",
|
||
|
fs_next_profile);
|
||
|
|
||
|
/* If AUDFS from the next profile is not the same as
|
||
|
* AUDFS from the default we need to write new delay tables
|
||
|
*/
|
||
|
err = tfa98xx_dsp_write_tables(tfa, fs_next_profile);
|
||
|
} else if (fs_default_profile != fs_previous_profile) {
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("Writing delay tables for AUDFS=%d\n",
|
||
|
fs_default_profile);
|
||
|
|
||
|
/* But if we do not have a new AUDFS in the next profile
|
||
|
* and AUDFS from the default profile is not the same
|
||
|
* as AUDFS from the previous profile
|
||
|
* we also need to write new delay tables
|
||
|
*/
|
||
|
err = tfa98xx_dsp_write_tables(tfa, fs_default_profile);
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* process all items in the profilelist
|
||
|
* NOTE an error return during processing will leave the device muted
|
||
|
*/
|
||
|
enum tfa98xx_error tfa_cont_write_profile(struct tfa_device *tfa,
|
||
|
int prof_idx, int vstep_idx)
|
||
|
{
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
int previous_prof_idx = tfa_dev_get_swprof(tfa);
|
||
|
struct tfa_profile_list *prof = NULL;
|
||
|
struct tfa_profile_list *previous_prof = NULL;
|
||
|
struct tfa_profile_list *prof_tfadsp = NULL;
|
||
|
struct tfa_profile_list *previous_prof_tfadsp = NULL;
|
||
|
char buffer[(MEMTRACK_MAX_WORDS * 4) + 4] = {0};
|
||
|
/* every word requires 3 or 4 bytes, and 3 or 4 is the msg */
|
||
|
unsigned int i, k = 0, j = 0;
|
||
|
struct tfa_file_dsc *file;
|
||
|
int size = 0, fs_previous_profile = 8; /* default fs is 48kHz */
|
||
|
int ready, tries = 0;
|
||
|
|
||
|
prof = tfa_cont_get_dev_prof_list(tfa->cnt,
|
||
|
tfa->dev_idx, prof_idx);
|
||
|
previous_prof = tfa_cont_get_dev_prof_list(tfa->cnt,
|
||
|
tfa->dev_idx, previous_prof_idx);
|
||
|
|
||
|
if (!prof || !previous_prof) {
|
||
|
pr_err("Error trying to get the (previous) swprofile\n");
|
||
|
err = TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
goto tfa_cont_write_profile_error_exit;
|
||
|
}
|
||
|
|
||
|
if (tfa->dev_tfadsp != -1) {
|
||
|
prof_tfadsp = tfa_cont_get_dev_prof_list(tfa->cnt,
|
||
|
tfa->dev_tfadsp, prof_idx);
|
||
|
previous_prof_tfadsp = tfa_cont_get_dev_prof_list(tfa->cnt,
|
||
|
tfa->dev_tfadsp, previous_prof_idx);
|
||
|
} else {
|
||
|
prof_tfadsp = prof;
|
||
|
previous_prof_tfadsp = previous_prof;
|
||
|
}
|
||
|
|
||
|
if (!prof_tfadsp || !previous_prof_tfadsp) {
|
||
|
pr_err("Error trying to get the (previous) swprofile for tfadsp\n");
|
||
|
err = TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
goto tfa_cont_write_profile_error_exit;
|
||
|
}
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n",
|
||
|
tfa_cont_device_name(tfa->cnt, tfa->dev_idx),
|
||
|
tfa_cont_profile_name(tfa->cnt, tfa->dev_idx,
|
||
|
prof_idx),
|
||
|
vstep_idx);
|
||
|
|
||
|
/* Get current sample rate before we start switching */
|
||
|
fs_previous_profile = TFA_GET_BF(tfa, AUDFS);
|
||
|
|
||
|
/* set all bitfield settings */
|
||
|
/* First set all default settings */
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("---------- default settings profile: %s (%d) ----------\n",
|
||
|
tfa_cont_get_string(tfa->cnt,
|
||
|
&previous_prof->name), previous_prof_idx);
|
||
|
|
||
|
err = tfa_show_current_state(tfa);
|
||
|
|
||
|
/* Loop profile length */
|
||
|
for (i = 0; i < previous_prof->length; i++) {
|
||
|
/* Search for the default section */
|
||
|
if (i == 0) {
|
||
|
while (previous_prof->list[i].type != dsc_default
|
||
|
&& i < previous_prof->length)
|
||
|
i++;
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
/* Only if we found the default section try writing the items */
|
||
|
if (i < previous_prof->length) {
|
||
|
if (tfa_cont_write_item(tfa,
|
||
|
&previous_prof->list[i])
|
||
|
!= TFA98XX_ERROR_OK) {
|
||
|
pr_err("%s: Error in writing default items!\n",
|
||
|
__func__);
|
||
|
err = TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
goto tfa_cont_write_profile_error_exit;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("---------- new settings profile: %s (%d) ----------\n",
|
||
|
tfa_cont_get_string(tfa->cnt,
|
||
|
&prof->name), prof_idx);
|
||
|
|
||
|
/* set new settings */
|
||
|
for (i = 0; i < prof->length; i++) {
|
||
|
/* only to write the values before default section
|
||
|
* when we switch profile
|
||
|
*/
|
||
|
if (prof->list[i].type == dsc_default)
|
||
|
break;
|
||
|
|
||
|
/* process and write all non-file items */
|
||
|
switch (prof->list[i].type) {
|
||
|
case dsc_file:
|
||
|
case dsc_patch:
|
||
|
case dsc_set_input_select:
|
||
|
case dsc_set_output_select:
|
||
|
case dsc_set_program_config:
|
||
|
case dsc_set_lag_w:
|
||
|
case dsc_set_gains:
|
||
|
case dsc_set_vbat_factors:
|
||
|
case dsc_set_senses_cal:
|
||
|
case dsc_set_senses_delay:
|
||
|
case dsc_set_mb_drc:
|
||
|
case dsc_set_fw_use_case:
|
||
|
case dsc_set_vddp_config:
|
||
|
case dsc_cmd:
|
||
|
case dsc_filter:
|
||
|
/* Skip files / commands and continue */
|
||
|
/* i = prof->length; */
|
||
|
break;
|
||
|
default:
|
||
|
/* Remember where we currently are with writing items*/
|
||
|
j = i;
|
||
|
|
||
|
err = tfa_cont_write_item(tfa, &prof->list[i]);
|
||
|
if (err != TFA98XX_ERROR_OK) {
|
||
|
pr_err("%s: Error in writing items!\n",
|
||
|
__func__);
|
||
|
err = TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
goto tfa_cont_write_profile_error_exit;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tfa_cont_is_standby_profile(tfa, prof_idx)) {
|
||
|
pr_info("%s: Keep power down without writing files, in standby profile!\n",
|
||
|
__func__);
|
||
|
|
||
|
err = tfa98xx_powerdown(tfa, 1);
|
||
|
if (err)
|
||
|
goto tfa_cont_write_profile_error_exit;
|
||
|
|
||
|
/* Wait until we are in PLL powerdown */
|
||
|
tries = 0;
|
||
|
do {
|
||
|
err = tfa98xx_dsp_system_stable(tfa, &ready);
|
||
|
if (!ready)
|
||
|
break;
|
||
|
|
||
|
/* wait 10ms to avoid busload */
|
||
|
msleep_interruptible(10);
|
||
|
tries++;
|
||
|
} while (tries <= TFA98XX_WAITPOWERUP_NTRIES);
|
||
|
|
||
|
if (tries > TFA98XX_WAITPOWERUP_NTRIES) {
|
||
|
pr_debug("Wait for PLL powerdown timed out!\n");
|
||
|
err = TFA98XX_ERROR_STATE_TIMED_OUT;
|
||
|
goto tfa_cont_write_profile_error_exit;
|
||
|
}
|
||
|
|
||
|
err = tfa_show_current_state(tfa);
|
||
|
goto tfa_cont_write_profile_error_exit;
|
||
|
}
|
||
|
|
||
|
/* Check if there are sample rate changes */
|
||
|
err = get_sample_rate_info(tfa, prof,
|
||
|
previous_prof, fs_previous_profile);
|
||
|
if (err) {
|
||
|
pr_err("%s: error in getting sr\n", __func__);
|
||
|
goto tfa_cont_write_profile_error_exit;
|
||
|
}
|
||
|
|
||
|
/* Write files from previous profile (default section)
|
||
|
* Should only be used for the patch & trap patch (file)
|
||
|
*/
|
||
|
if ((tfa->ext_dsp != 0) && (tfa->tfa_family == 2)) {
|
||
|
for (i = 0; i < previous_prof_tfadsp->length; i++) {
|
||
|
char type;
|
||
|
|
||
|
/* Search for the default section */
|
||
|
if (i == 0) {
|
||
|
while (previous_prof_tfadsp->list[i].type
|
||
|
!= dsc_default
|
||
|
&& i < previous_prof_tfadsp->length) {
|
||
|
i++;
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Only if we found the default section
|
||
|
* try writing the file
|
||
|
*/
|
||
|
if (i >= previous_prof_tfadsp->length)
|
||
|
break;
|
||
|
|
||
|
type = previous_prof_tfadsp->list[i].type;
|
||
|
if (type != dsc_file && type != dsc_patch)
|
||
|
continue;
|
||
|
|
||
|
/* Only write this once */
|
||
|
if (tfa->verbose && k == 0) {
|
||
|
pr_debug("---------- files default profile: %s (%d) ----------\n",
|
||
|
tfa_cont_get_string
|
||
|
(tfa->cnt, &previous_prof_tfadsp->name),
|
||
|
prof_idx);
|
||
|
k++;
|
||
|
}
|
||
|
file = (struct tfa_file_dsc *)
|
||
|
(previous_prof_tfadsp->list[i].offset
|
||
|
+ (uint8_t *)tfa->cnt);
|
||
|
err = tfa_cont_write_file(tfa,
|
||
|
file, vstep_idx,
|
||
|
TFA_MAX_VSTEP_MSG_MARKER);
|
||
|
}
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("---------- files new profile: %s (%d) ----------\n",
|
||
|
tfa_cont_get_string(tfa->cnt,
|
||
|
&prof_tfadsp->name), prof_idx);
|
||
|
}
|
||
|
|
||
|
if (tfa->dev_tfadsp != -1)
|
||
|
j = 0; /* reset to begin from the head */
|
||
|
|
||
|
/* write everything
|
||
|
* until end or the default section starts
|
||
|
*/
|
||
|
/* Start where we currenly left */
|
||
|
for (i = j; i < prof_tfadsp->length; i++) {
|
||
|
/* only to write the values
|
||
|
* before the default section when we switch profile
|
||
|
*/
|
||
|
if (prof_tfadsp->list[i].type == dsc_default)
|
||
|
break;
|
||
|
|
||
|
switch (prof_tfadsp->list[i].type) {
|
||
|
case dsc_file:
|
||
|
case dsc_patch:
|
||
|
/* For tiberius stereo 1 device does not have a dsp! */
|
||
|
if (tfa->ext_dsp == 0)
|
||
|
break;
|
||
|
if (tfa_cont_is_config_loaded(tfa))
|
||
|
break;
|
||
|
|
||
|
file = (struct tfa_file_dsc *)
|
||
|
(prof_tfadsp->list[i].offset
|
||
|
+ (uint8_t *)tfa->cnt);
|
||
|
err = tfa_cont_write_file(tfa, file,
|
||
|
vstep_idx, TFA_MAX_VSTEP_MSG_MARKER);
|
||
|
break;
|
||
|
case dsc_set_input_select:
|
||
|
case dsc_set_output_select:
|
||
|
case dsc_set_program_config:
|
||
|
case dsc_set_lag_w:
|
||
|
case dsc_set_gains:
|
||
|
case dsc_set_vbat_factors:
|
||
|
case dsc_set_senses_cal:
|
||
|
case dsc_set_senses_delay:
|
||
|
case dsc_set_mb_drc:
|
||
|
case dsc_set_fw_use_case:
|
||
|
case dsc_set_vddp_config:
|
||
|
/* For tiberius device 1 has no dsp! */
|
||
|
if (tfa->ext_dsp == 0)
|
||
|
break;
|
||
|
if (tfa_cont_is_config_loaded(tfa))
|
||
|
break;
|
||
|
|
||
|
create_dsp_buffer_msg(tfa,
|
||
|
(struct tfa_msg *)
|
||
|
(prof_tfadsp->list[i].offset
|
||
|
+ (char *)tfa->cnt),
|
||
|
buffer, &size);
|
||
|
err = dsp_msg(tfa, size, buffer);
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("command: %s=0x%02x%02x%02x\n",
|
||
|
tfa_cont_get_command_string
|
||
|
(prof_tfadsp->list[i].type),
|
||
|
(unsigned char)buffer[0],
|
||
|
(unsigned char)buffer[1],
|
||
|
(unsigned char)buffer[2]);
|
||
|
|
||
|
/* Reset bypass if writing msg files */
|
||
|
if (err == TFA98XX_ERROR_OK)
|
||
|
tfa->is_bypass = 0;
|
||
|
break;
|
||
|
case dsc_cmd:
|
||
|
/* For tiberius device 1 has no dsp! */
|
||
|
if (tfa->ext_dsp == 0)
|
||
|
break;
|
||
|
if (tfa_cont_is_config_loaded(tfa))
|
||
|
break;
|
||
|
|
||
|
size = *(uint16_t *)
|
||
|
(prof_tfadsp->list[i].offset
|
||
|
+ (char *)tfa->cnt);
|
||
|
err = dsp_msg(tfa, size,
|
||
|
prof_tfadsp->list[i].offset
|
||
|
+ 2 + (char *)tfa->cnt);
|
||
|
|
||
|
if (tfa->verbose) {
|
||
|
const char *cmd_id
|
||
|
= prof_tfadsp->list[i].offset
|
||
|
+ 2 + (char *)tfa->cnt;
|
||
|
pr_debug("Writing cmd=0x%02x%02x%02x\n",
|
||
|
(uint8_t)cmd_id[0],
|
||
|
(uint8_t)cmd_id[1],
|
||
|
(uint8_t)cmd_id[2]);
|
||
|
}
|
||
|
|
||
|
/* Reset bypass if writing msg files */
|
||
|
if (err == TFA98XX_ERROR_OK)
|
||
|
tfa->is_bypass = 0;
|
||
|
break;
|
||
|
default:
|
||
|
/*
|
||
|
* Already have written in non-tfadsp device:
|
||
|
* err = tfa_cont_write_item(tfa,
|
||
|
* &prof_tfadsp->list[i]);
|
||
|
*/
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (err != TFA98XX_ERROR_OK) {
|
||
|
pr_err("%s: Error in writing new files and more!\n",
|
||
|
__func__);
|
||
|
goto tfa_cont_write_profile_error_exit;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tfa_dev_set_swprof(tfa, (unsigned short)prof_idx);
|
||
|
|
||
|
/* put SetRe25C message to indicate all messages are sent */
|
||
|
if (tfa->ext_dsp == 1)
|
||
|
err = tfa_set_calibration_values_once(tfa);
|
||
|
|
||
|
tfa_cont_write_profile_error_exit:
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* process only vstep in the profilelist
|
||
|
*/
|
||
|
enum tfa98xx_error tfa_cont_write_files_vstep(struct tfa_device *tfa,
|
||
|
int prof_idx, int vstep_idx)
|
||
|
{
|
||
|
struct tfa_profile_list *prof
|
||
|
= tfa_cont_get_dev_prof_list(tfa->cnt, tfa->dev_idx, prof_idx);
|
||
|
unsigned int i;
|
||
|
struct tfa_file_dsc *file;
|
||
|
struct tfa_header *hdr;
|
||
|
enum tfa_header_type type;
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
|
||
|
if (!prof)
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
|
||
|
if (tfa->verbose)
|
||
|
tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n",
|
||
|
tfa_cont_device_name(tfa->cnt, tfa->dev_idx),
|
||
|
tfa_cont_profile_name(tfa->cnt, tfa->dev_idx,
|
||
|
prof_idx),
|
||
|
vstep_idx);
|
||
|
|
||
|
/* write vstep file only! */
|
||
|
for (i = 0; i < prof->length; i++) {
|
||
|
if (prof->list[i].type == dsc_file) {
|
||
|
file = (struct tfa_file_dsc *)
|
||
|
(prof->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
hdr = (struct tfa_header *)file->data;
|
||
|
type = (enum tfa_header_type)hdr->id;
|
||
|
|
||
|
switch (type) {
|
||
|
case volstep_hdr:
|
||
|
if (tfa_cont_write_file(tfa, file,
|
||
|
vstep_idx, TFA_MAX_VSTEP_MSG_MARKER))
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
char *tfa_cont_get_string(struct tfa_container *cnt, struct tfa_desc_ptr *dsc)
|
||
|
{
|
||
|
if (dsc->type != dsc_string)
|
||
|
return "Undefined string";
|
||
|
|
||
|
return dsc->offset + (char *)cnt;
|
||
|
}
|
||
|
|
||
|
char *tfa_cont_get_command_string(uint32_t type)
|
||
|
{
|
||
|
if (type == dsc_set_input_select)
|
||
|
return "SetInputSelector";
|
||
|
else if (type == dsc_set_output_select)
|
||
|
return "SetOutputSelector";
|
||
|
else if (type == dsc_set_program_config)
|
||
|
return "SetProgramConfig";
|
||
|
else if (type == dsc_set_lag_w)
|
||
|
return "SetLagW";
|
||
|
else if (type == dsc_set_gains)
|
||
|
return "SetGains";
|
||
|
else if (type == dsc_set_vbat_factors)
|
||
|
return "SetvBatFactors";
|
||
|
else if (type == dsc_set_senses_cal)
|
||
|
return "SetSensesCal";
|
||
|
else if (type == dsc_set_senses_delay)
|
||
|
return "SetSensesDelay";
|
||
|
else if (type == dsc_set_mb_drc)
|
||
|
return "SetMBDrc";
|
||
|
else if (type == dsc_set_fw_use_case)
|
||
|
return "SetFwkUseCase";
|
||
|
else if (type == dsc_set_vddp_config)
|
||
|
return "SetVddpConfig";
|
||
|
else if (type == dsc_filter)
|
||
|
return "filter";
|
||
|
else
|
||
|
return "Undefined string";
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get the name of the device at a certain index in the container file
|
||
|
* return device name
|
||
|
*/
|
||
|
char *tfa_cont_device_name(struct tfa_container *cnt, int dev_idx)
|
||
|
{
|
||
|
struct tfa_device_list *dev;
|
||
|
|
||
|
dev = tfa_cont_device(cnt, dev_idx);
|
||
|
if (dev == NULL)
|
||
|
return "!ERROR!";
|
||
|
|
||
|
return tfa_cont_get_string(cnt, &dev->name);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get the application name from the container file application field
|
||
|
* note that the input stringbuffer should be sizeof(application field)+1
|
||
|
*/
|
||
|
int tfa_cont_get_app_name(struct tfa_device *tfa, char *name)
|
||
|
{
|
||
|
unsigned int i;
|
||
|
int len = 0;
|
||
|
|
||
|
for (i = 0; i < sizeof(tfa->cnt->application); i++) {
|
||
|
if (isalnum(tfa->cnt->application[i])) /* copy char if valid */
|
||
|
name[len++] = tfa->cnt->application[i];
|
||
|
if (tfa->cnt->application[i] == '\0')
|
||
|
break;
|
||
|
}
|
||
|
name[len++] = '\0';
|
||
|
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get profile index of the calibration profile.
|
||
|
* Returns: (profile index) if found, (-2) if no
|
||
|
* calibration profile is found or (-1) on error
|
||
|
*/
|
||
|
int tfa_cont_get_cal_profile(struct tfa_device *tfa)
|
||
|
{
|
||
|
int prof, cal_idx = -2;
|
||
|
char prof_name[MAX_CONTROL_NAME] = {0};
|
||
|
|
||
|
if ((tfa->dev_idx < 0) || (tfa->dev_idx >= tfa->cnt->ndev))
|
||
|
return TFA_ERROR;
|
||
|
|
||
|
/* search for the calibration profile in the list of profiles */
|
||
|
for (prof = 0; prof < tfa->cnt->nprof; prof++) {
|
||
|
strlcpy(prof_name, tfa_cont_profile_name(tfa->cnt,
|
||
|
tfa->dev_idx, prof), MAX_CONTROL_NAME);
|
||
|
if (strnstr(prof_name, ".cal", strlen(prof_name)) != NULL) {
|
||
|
cal_idx = prof;
|
||
|
pr_debug("Using calibration profile: '%s'\n",
|
||
|
tfa_cont_profile_name(tfa->cnt,
|
||
|
tfa->dev_idx, prof));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pr_info("%s: cal_prof = %d", __func__, cal_idx);
|
||
|
|
||
|
return cal_idx;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Is the profile a tap profile
|
||
|
*/
|
||
|
int tfa_cont_is_tap_profile(struct tfa_device *tfa, int prof_idx)
|
||
|
{
|
||
|
char prof_name[MAX_CONTROL_NAME] = {0};
|
||
|
|
||
|
if ((tfa->dev_idx < 0) || (tfa->dev_idx >= tfa->cnt->ndev))
|
||
|
return TFA_ERROR;
|
||
|
|
||
|
strlcpy(prof_name, tfa_cont_profile_name(tfa->cnt,
|
||
|
tfa->dev_idx, prof_idx), MAX_CONTROL_NAME);
|
||
|
/* Check if next profile is tap profile */
|
||
|
if (strnstr(prof_name, ".tap", strlen(prof_name)) != NULL) {
|
||
|
pr_debug("Using Tap profile: '%s'\n",
|
||
|
tfa_cont_profile_name(tfa->cnt,
|
||
|
tfa->dev_idx, prof_idx));
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Is the profile a standby profile
|
||
|
*/
|
||
|
int tfa_cont_is_standby_profile(struct tfa_device *tfa, int prof_idx)
|
||
|
{
|
||
|
char prof_name[MAX_CONTROL_NAME] = {0};
|
||
|
|
||
|
if ((tfa->dev_idx < 0) || (tfa->dev_idx >= tfa->cnt->ndev))
|
||
|
return TFA_ERROR;
|
||
|
|
||
|
strlcpy(prof_name, tfa_cont_profile_name(tfa->cnt,
|
||
|
tfa->dev_idx, prof_idx), MAX_CONTROL_NAME);
|
||
|
/* Check if next profile is tap profile */
|
||
|
if (strnstr(prof_name, ".standby", strlen(prof_name)) != NULL) {
|
||
|
pr_debug("Using Standby profile: '%s'\n",
|
||
|
tfa_cont_profile_name(tfa->cnt,
|
||
|
tfa->dev_idx, prof_idx));
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Is the profile specific to device ?
|
||
|
* @param dev_idx the index of the device
|
||
|
* @param prof_idx the index of the profile
|
||
|
* @return 1 if the profile belongs to device or 0 if not
|
||
|
*/
|
||
|
int tfa_cont_is_dev_specific_profile(struct tfa_container *cnt,
|
||
|
int dev_idx, int prof_idx)
|
||
|
{
|
||
|
char *pch;
|
||
|
int prof_name_len;
|
||
|
char prof_name[MAX_CONTROL_NAME] = {0};
|
||
|
char dev_substring[MAX_CONTROL_NAME] = {0};
|
||
|
|
||
|
snprintf(prof_name, MAX_CONTROL_NAME, "%s",
|
||
|
tfa_cont_profile_name(cnt, dev_idx, prof_idx));
|
||
|
prof_name_len = strlen(prof_name);
|
||
|
|
||
|
pch = strnchr(prof_name, prof_name_len, '.');
|
||
|
if (!pch)
|
||
|
return 0;
|
||
|
|
||
|
snprintf(dev_substring, MAX_CONTROL_NAME,
|
||
|
".%s", tfa_cont_device_name(cnt, dev_idx));
|
||
|
if (prof_name_len < strlen(dev_substring))
|
||
|
return 0;
|
||
|
|
||
|
/* Check if next profile is tap profile */
|
||
|
pch = strnstr(prof_name, dev_substring, prof_name_len);
|
||
|
if (!pch) {
|
||
|
pr_debug("dev profile: '%s' of device '%s'\n",
|
||
|
prof_name, dev_substring);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get the name of the profile at certain index for a device
|
||
|
* in the container file
|
||
|
* return profile name
|
||
|
*/
|
||
|
char *tfa_cont_profile_name(struct tfa_container *cnt,
|
||
|
int dev_idx, int prof_idx)
|
||
|
{
|
||
|
struct tfa_profile_list *prof = NULL;
|
||
|
|
||
|
/* the Nth profiles for this device */
|
||
|
prof = tfa_cont_get_dev_prof_list(cnt, dev_idx, prof_idx);
|
||
|
|
||
|
/* If the index is out of bound */
|
||
|
if (prof == NULL)
|
||
|
return "NONE";
|
||
|
|
||
|
return tfa_cont_get_string(cnt, &prof->name);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* return 1st profile list
|
||
|
*/
|
||
|
struct tfa_profile_list *tfa_cont_get_1st_prof_list
|
||
|
(struct tfa_container *cont)
|
||
|
{
|
||
|
struct tfa_profile_list *prof;
|
||
|
uint8_t *b = (uint8_t *)cont;
|
||
|
|
||
|
int maxdev = 0;
|
||
|
struct tfa_device_list *dev;
|
||
|
|
||
|
/* get nr of devlists */
|
||
|
maxdev = cont->ndev;
|
||
|
/* get last devlist */
|
||
|
dev = tfa_cont_get_dev_list(cont, maxdev - 1);
|
||
|
if (dev == NULL)
|
||
|
return NULL;
|
||
|
/* the 1st profile starts after the last device list */
|
||
|
b = (uint8_t *) dev + sizeof(struct tfa_device_list)
|
||
|
+ dev->length * (sizeof(struct tfa_desc_ptr));
|
||
|
prof = (struct tfa_profile_list *)b;
|
||
|
return prof;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* return 1st livedata list
|
||
|
*/
|
||
|
struct tfa_livedata_list *tfa_cont_get_1st_livedata_list
|
||
|
(struct tfa_container *cont)
|
||
|
{
|
||
|
struct tfa_livedata_list *ldata;
|
||
|
struct tfa_profile_list *prof;
|
||
|
struct tfa_device_list *dev;
|
||
|
uint8_t *b = (uint8_t *)cont;
|
||
|
int maxdev, maxprof;
|
||
|
|
||
|
/* get nr of devlists+1 */
|
||
|
maxdev = cont->ndev;
|
||
|
/* get nr of proflists */
|
||
|
maxprof = cont->nprof;
|
||
|
|
||
|
/* get last devlist */
|
||
|
dev = tfa_cont_get_dev_list(cont, maxdev - 1);
|
||
|
if (dev == NULL)
|
||
|
return NULL;
|
||
|
/* the 1st livedata starts after the last device list */
|
||
|
b = (uint8_t *) dev + sizeof(struct tfa_device_list) +
|
||
|
dev->length * (sizeof(struct tfa_desc_ptr));
|
||
|
|
||
|
while (maxprof != 0) {
|
||
|
/* get last proflist */
|
||
|
prof = (struct tfa_profile_list *)b;
|
||
|
b += sizeof(struct tfa_profile_list) +
|
||
|
((prof->length - 1) * (sizeof(struct tfa_desc_ptr)));
|
||
|
maxprof--;
|
||
|
}
|
||
|
|
||
|
/* Else the marker falls off */
|
||
|
b += 4; /* bytes */
|
||
|
|
||
|
ldata = (struct tfa_livedata_list *)b;
|
||
|
return ldata;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* return the device list pointer
|
||
|
*/
|
||
|
struct tfa_device_list *tfa_cont_device(struct tfa_container *cnt,
|
||
|
int dev_idx)
|
||
|
{
|
||
|
return tfa_cont_get_dev_list(cnt, dev_idx);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* return the next profile:
|
||
|
* - assume that all profiles are adjacent
|
||
|
* - calculate the total length of the input
|
||
|
* - the input profile + its length is the next profile
|
||
|
*/
|
||
|
struct tfa_profile_list *tfa_cont_next_profile(struct tfa_profile_list *prof)
|
||
|
{
|
||
|
uint8_t *this, *next; /* byte pointers for byte pointer arithmetic */
|
||
|
struct tfa_profile_list *nextprof;
|
||
|
int listlength; /* total length of list in bytes */
|
||
|
|
||
|
if (prof == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
if (prof->id != TFA_PROFID)
|
||
|
return NULL; /* invalid input */
|
||
|
|
||
|
this = (uint8_t *)prof;
|
||
|
/* nr of items in the list, length includes name dsc so - 1*/
|
||
|
listlength = (prof->length - 1) * sizeof(struct tfa_desc_ptr);
|
||
|
/* the sizeof(struct tfa_profile_list) includes the list[0] length */
|
||
|
next = this + listlength + sizeof(struct tfa_profile_list);
|
||
|
/* - sizeof(struct tfa_desc_ptr); */
|
||
|
nextprof = (struct tfa_profile_list *)next;
|
||
|
|
||
|
if (nextprof->id != TFA_PROFID)
|
||
|
return NULL;
|
||
|
|
||
|
return nextprof;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* return the next livedata
|
||
|
*/
|
||
|
struct tfa_livedata_list *tfa_cont_next_livedata
|
||
|
(struct tfa_livedata_list *livedata)
|
||
|
{
|
||
|
struct tfa_livedata_list *nextlivedata
|
||
|
= (struct tfa_livedata_list *)
|
||
|
((char *)livedata + (livedata->length * 4)
|
||
|
+ sizeof(struct tfa_livedata_list) - 4);
|
||
|
|
||
|
if (nextlivedata->id == TFA_LIVEDATAID)
|
||
|
return nextlivedata;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* check CRC for container
|
||
|
* CRC is calculated over the bytes following the CRC field
|
||
|
* return non zero value on error
|
||
|
*/
|
||
|
int tfa_cont_crc_check_container(struct tfa_container *cont)
|
||
|
{
|
||
|
uint8_t *base;
|
||
|
size_t size;
|
||
|
uint32_t crc;
|
||
|
|
||
|
base = (uint8_t *)&cont->crc + 4;
|
||
|
/* ptr to bytes following the CRC field */
|
||
|
size = (size_t)(cont->size - (base - (uint8_t *)cont));
|
||
|
/* nr of bytes following the CRC field */
|
||
|
crc = ~crc32_le(~0u, base, size);
|
||
|
|
||
|
return crc != cont->crc;
|
||
|
}
|
||
|
|
||
|
static void tfa_get_all_features_from_cnt(struct tfa_device *tfa,
|
||
|
int *hw_feature_register, int sw_feature_register[2])
|
||
|
{
|
||
|
struct tfa_features *features;
|
||
|
int i;
|
||
|
|
||
|
struct tfa_device_list *dev = tfa_cont_device(tfa->cnt, tfa->dev_idx);
|
||
|
|
||
|
/* Init values in case no keyword is defined in cnt file: */
|
||
|
*hw_feature_register = -1;
|
||
|
sw_feature_register[0] = -1;
|
||
|
sw_feature_register[1] = -1;
|
||
|
|
||
|
if (dev == NULL)
|
||
|
return;
|
||
|
|
||
|
/* process the device list */
|
||
|
for (i = 0; i < dev->length; i++) {
|
||
|
if (dev->list[i].type == dsc_features) {
|
||
|
features = (struct tfa_features *)
|
||
|
(dev->list[i].offset + (uint8_t *)tfa->cnt);
|
||
|
*hw_feature_register = features->value[0];
|
||
|
sw_feature_register[0] = features->value[1];
|
||
|
sw_feature_register[1] = features->value[2];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* wrapper function */
|
||
|
void tfa_get_hw_features_from_cnt(struct tfa_device *tfa,
|
||
|
int *hw_feature_register)
|
||
|
{
|
||
|
int sw_feature_register[2];
|
||
|
|
||
|
tfa_get_all_features_from_cnt(tfa,
|
||
|
hw_feature_register, sw_feature_register);
|
||
|
}
|
||
|
|
||
|
/* wrapper function */
|
||
|
void tfa_get_sw_features_from_cnt(struct tfa_device *tfa,
|
||
|
int sw_feature_register[2])
|
||
|
{
|
||
|
int hw_feature_register;
|
||
|
|
||
|
tfa_get_all_features_from_cnt(tfa,
|
||
|
&hw_feature_register, sw_feature_register);
|
||
|
}
|
||
|
|
||
|
enum tfa98xx_error tfa98xx_factory_trimmer(struct tfa_device *tfa)
|
||
|
{
|
||
|
return (tfa->dev_ops.factory_trimmer)(tfa);
|
||
|
}
|
||
|
|
||
|
enum tfa98xx_error tfa_set_filters(struct tfa_device *tfa, int prof_idx)
|
||
|
{
|
||
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
||
|
struct tfa_profile_list *prof
|
||
|
= tfa_cont_get_dev_prof_list(tfa->cnt, tfa->dev_idx, prof_idx);
|
||
|
unsigned int i;
|
||
|
|
||
|
if (!prof)
|
||
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
||
|
|
||
|
/* If we are in powerdown there is no need to set filters */
|
||
|
if (TFA_GET_BF(tfa, PWDN) == 1)
|
||
|
return TFA98XX_ERROR_OK;
|
||
|
|
||
|
/* loop the profile to find filter settings */
|
||
|
for (i = 0; i < prof->length; i++) {
|
||
|
/* write values before default section */
|
||
|
if (prof->list[i].type == dsc_default)
|
||
|
break;
|
||
|
|
||
|
/* write all filter settings */
|
||
|
if (prof->list[i].type == dsc_filter) {
|
||
|
if (tfa_cont_write_item(tfa,
|
||
|
&prof->list[i]) != TFA98XX_ERROR_OK)
|
||
|
return err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
int tfa_tib_dsp_msgmulti(struct tfa_device *tfa,
|
||
|
int length, const char *buffer)
|
||
|
{
|
||
|
uint8_t *buf = (uint8_t *)buffer;
|
||
|
static enum tfa_blob_index idx = BLOB_INDEX_REGULAR; /* default */
|
||
|
static uint8_t *blob[BLOB_INDEX_MAX] = {NULL, NULL};
|
||
|
static uint8_t *blobptr[BLOB_INDEX_MAX] = {NULL, NULL};
|
||
|
static int blob_p_index[BLOB_INDEX_MAX] = {-1, -1};
|
||
|
static int total[BLOB_INDEX_MAX] = {0, 0};
|
||
|
int post_len = 0;
|
||
|
uint8_t cmd, cc;
|
||
|
|
||
|
/* checks for 24b_BE or 32_LE */
|
||
|
int len_word_in_bytes = (tfa->convert_dsp32) ? 4 : 3;
|
||
|
/* TODO: get rid of these magic constants
|
||
|
* max size should depend on the tfa device type
|
||
|
*/
|
||
|
/*
|
||
|
* int tfadsp_max_msg_size = (tfa->convert_dsp32)
|
||
|
* ? MAX_APR_MSG_SIZE : (MAX_APR_MSG_SIZE * 3 / 4);
|
||
|
* 5336 or 4000
|
||
|
*/
|
||
|
/* TEMPORARY, set 16KB to utilize dsp_msg_packet */
|
||
|
int tfadsp_max_msg_size = 16 * 1024;
|
||
|
|
||
|
/* set to blob for individual message */
|
||
|
if (tfa->individual_msg)
|
||
|
idx = BLOB_INDEX_INDIVIDUAL; /* until it's transferred */
|
||
|
|
||
|
/* Transfer buffered messages */
|
||
|
if (length == -1) {
|
||
|
int i, total_len = total[idx];
|
||
|
|
||
|
/* No data found */
|
||
|
if (blob[idx] == NULL || buf == NULL) {
|
||
|
pr_err("%s: %s is NULL (index %d)!\n", __func__,
|
||
|
(blob[idx] == NULL) ? "blob" : "buf", idx);
|
||
|
return TFA_ERROR;
|
||
|
}
|
||
|
|
||
|
pr_debug("%s: transfer blob (index %d)\n",
|
||
|
__func__, idx);
|
||
|
|
||
|
/* add total - specially merged fom legacy */
|
||
|
if (tfa->convert_dsp32) {
|
||
|
if (total_len <= 0xffff) {
|
||
|
blob[idx][2] = (uint8_t)
|
||
|
((total_len >> 8) & 0xff);
|
||
|
blob[idx][3] = (uint8_t)(total_len & 0xff);
|
||
|
}
|
||
|
} else {
|
||
|
if (total_len <= 0xff)
|
||
|
blob[idx][0] = (uint8_t)(total_len & 0xff);
|
||
|
}
|
||
|
/* set last length field to zero */
|
||
|
for (i = total_len;
|
||
|
i < (total_len + len_word_in_bytes); i++)
|
||
|
blob[idx][i] = 0;
|
||
|
|
||
|
total_len += len_word_in_bytes;
|
||
|
memcpy(buf, blob[idx], total_len);
|
||
|
|
||
|
if (blob_p_index[idx] != -1)
|
||
|
blob_p_index[idx] = tfa98xx_buffer_pool_access
|
||
|
(blob_p_index[idx], 0, &blob[idx], POOL_RETURN);
|
||
|
else
|
||
|
kfree(blob[idx]);
|
||
|
/* set blob to NULL pointer, to free memory */
|
||
|
blob[idx] = NULL;
|
||
|
|
||
|
/* reset to blob for regular message */
|
||
|
idx = BLOB_INDEX_REGULAR; /* default */
|
||
|
tfa->individual_msg = 0; /* reset flag */
|
||
|
|
||
|
return total_len;
|
||
|
}
|
||
|
|
||
|
/* Flush buffer */
|
||
|
if (length == -2) {
|
||
|
if (blob[idx] == NULL) {
|
||
|
pr_info("%s: already flushed - NULL (index %d)!\n",
|
||
|
__func__, idx);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
pr_debug("%s: flush blob (index %d)\n",
|
||
|
__func__, idx);
|
||
|
|
||
|
if (blob_p_index[idx] != -1)
|
||
|
blob_p_index[idx] = tfa98xx_buffer_pool_access
|
||
|
(blob_p_index[idx], 0, &blob[idx], POOL_RETURN);
|
||
|
else
|
||
|
kfree(blob[idx]);
|
||
|
/* set blob to NULL pointer, to free memory */
|
||
|
blob[idx] = NULL;
|
||
|
|
||
|
/* reset to blob for regular message */
|
||
|
idx = BLOB_INDEX_REGULAR; /* default */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Allocate buffer */
|
||
|
if (blob[idx] == NULL) {
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("%s, creating multi-message:\n", __func__);
|
||
|
|
||
|
pr_debug("%s: allocate blob (index %d)\n",
|
||
|
__func__, idx);
|
||
|
|
||
|
/* return if already allocated, to get a new one */
|
||
|
if (blob_p_index[idx] != -1)
|
||
|
tfa98xx_buffer_pool_access
|
||
|
(blob_p_index[idx], 0, &blob[idx], POOL_RETURN);
|
||
|
blob_p_index[idx] = tfa98xx_buffer_pool_access
|
||
|
(-1, 64 * 1024, &blob[idx], POOL_GET);
|
||
|
if (blob_p_index[idx] != -1) {
|
||
|
pr_debug("%s: allocated from buffer_pool[%d]\n",
|
||
|
__func__, blob_p_index[idx]);
|
||
|
} else {
|
||
|
blob[idx] = kmalloc(64 * 1024, GFP_KERNEL);
|
||
|
/* max length is 64k */
|
||
|
if (blob[idx] == NULL)
|
||
|
return TFA98XX_ERROR_FAIL;
|
||
|
}
|
||
|
|
||
|
/* add command ID for multi-msg = 0x008015 */
|
||
|
if (tfa->convert_dsp32) {
|
||
|
blob[idx][0] = FW_PAR_ID_SET_MULTI_MESSAGE;
|
||
|
blob[idx][1] = 0x80 | MODULE_FRAMEWORK;
|
||
|
blob[idx][2] = 0x0;
|
||
|
blob[idx][3] = 0x0;
|
||
|
} else {
|
||
|
blob[idx][0] = 0x0;
|
||
|
blob[idx][1] = 0x80 | MODULE_FRAMEWORK;
|
||
|
blob[idx][2] = FW_PAR_ID_SET_MULTI_MESSAGE;
|
||
|
}
|
||
|
pr_debug("%s: multi-msg (index %d) [0]=0x%x-[1]=0x%x-[2]=0x%x\n",
|
||
|
__func__, idx,
|
||
|
blob[idx][0], blob[idx][1], blob[idx][2]);
|
||
|
|
||
|
blobptr[idx] = blob[idx];
|
||
|
blobptr[idx] += len_word_in_bytes;
|
||
|
total[idx] = len_word_in_bytes;
|
||
|
}
|
||
|
|
||
|
/* check total message size after concatination */
|
||
|
post_len = total[idx] + length + (2 * len_word_in_bytes);
|
||
|
if (post_len > tfadsp_max_msg_size) {
|
||
|
pr_debug("%s: set buffer full for blob (index %d): %d >= max %d, current length: %d\n",
|
||
|
__func__, idx, post_len,
|
||
|
tfadsp_max_msg_size, total[idx]);
|
||
|
return TFA98XX_ERROR_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
|
||
|
if (buf == NULL) {
|
||
|
pr_err("%s: buf is NULL (index %d)!\n",
|
||
|
__func__, idx);
|
||
|
return TFA98XX_ERROR_FAIL;
|
||
|
}
|
||
|
|
||
|
/* Accumulate messages to buffer */
|
||
|
if (tfa->verbose)
|
||
|
pr_debug("%s, id:0x%02x%02x%02x, length:%d\n",
|
||
|
__func__, buf[0], buf[1], buf[2], length);
|
||
|
|
||
|
/* add length field (length in words) to the multi message */
|
||
|
if (tfa->convert_dsp32) {
|
||
|
*blobptr[idx]++ = (uint8_t) /* lsb */
|
||
|
((length / len_word_in_bytes) & 0xff);
|
||
|
*blobptr[idx]++ = (uint8_t) /* msb */
|
||
|
(((length / len_word_in_bytes) & 0xff00) >> 8);
|
||
|
*blobptr[idx]++ = 0x0;
|
||
|
*blobptr[idx]++ = 0x0;
|
||
|
} else {
|
||
|
*blobptr[idx]++ = 0x0;
|
||
|
*blobptr[idx]++ = (uint8_t) /* msb */
|
||
|
(((length / len_word_in_bytes) & 0xff00) >> 8);
|
||
|
*blobptr[idx]++ = (uint8_t) /* lsb */
|
||
|
((length / len_word_in_bytes) & 0xff);
|
||
|
}
|
||
|
memcpy(blobptr[idx], buf, length);
|
||
|
blobptr[idx] += length;
|
||
|
total[idx] += (length + len_word_in_bytes);
|
||
|
|
||
|
/* SetRe25 message is always the last message of the multi-msg */
|
||
|
pr_debug("%s: length (%d), [0]=0x%x-[1]=0x%x-[2]=0x%x\n",
|
||
|
__func__, length, buf[0], buf[1], buf[2]);
|
||
|
|
||
|
cmd = (tfa->convert_dsp32) ? buf[0] : buf[2];
|
||
|
cc = (tfa->convert_dsp32) ? buf[2] : buf[0];
|
||
|
|
||
|
if (idx == BLOB_INDEX_INDIVIDUAL) {
|
||
|
pr_debug("%s: set last message for individual call: module=%d cmd=%d (index %d)\n",
|
||
|
__func__, buf[1], cmd, idx);
|
||
|
return 1; /* 1 means last message is done! */
|
||
|
}
|
||
|
|
||
|
if (cmd == SB_PARAM_SET_RE25C && buf[1] == 0x81) {
|
||
|
pr_debug("%s: found last message - sending: Re25C cmd=%d (index %d)\n",
|
||
|
__func__, cmd, idx);
|
||
|
return 1; /* 1 means last message is done! */
|
||
|
}
|
||
|
|
||
|
if (cmd & 0x80) { /* *_GET_* command except 0xA6 / 0xA7 */
|
||
|
pr_debug("%s: found last message - sending: module=%d cmd=%d CC=%d (index %d)\n",
|
||
|
__func__, buf[1], cmd, cc, idx);
|
||
|
return 1; /* 1 means last message is done! with CC check */
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|