kernel_samsung_a34x-permissive/sound/soc/codecs/tfa9878/tfa_container.c
2024-04-28 15:49:01 +02:00

3083 lines
78 KiB
C
Executable file

/*
* 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), &regvalue);
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;
}