blob: 3a62eb9ef69722cc19a32ef0f0c4ddde1499ec5c [file] [log] [blame]
/*
*Copyright 2014 NXP Semiconductors
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
*http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/
#include "tfa_service.h"
#include "tfa_container.h"
#include "config.h"
#include "tfa.h"
#include "tfa_dsp_fw.h"
#include "tfa98xx_tfafieldnames.h"
#define FORCE_DSP_SETTING /*MCH_TO_TEST**/
/* module globals */
static int tfa98xx_cnt_verbose;
static nxpTfaContainer_t *g_cont=NULL; /* container file */
static int g_devs=-1; // nr of devices TODO use direct access to cont?
static nxpTfaDeviceList_t *g_dev[TFACONT_MAXDEVS];
static int g_profs[TFACONT_MAXDEVS];
static int g_liveds[TFACONT_MAXDEVS];
static nxpTfaProfileList_t *g_prof[TFACONT_MAXDEVS][TFACONT_MAXPROFS];
static nxpTfaLiveDataList_t *g_lived[TFACONT_MAXDEVS][TFACONT_MAXPROFS];
static int nxp_tfa_vstep[TFACONT_MAXDEVS];
static char errorname[] = "!ERROR!";
static char nonename[] = "NONE";
static void cont_get_devs(nxpTfaContainer_t *cont);
static int float_to_int(uint32_t x)
{
unsigned e = (0x7F + 31) - ((*(unsigned *) &x & 0x7F800000) >> 23);
unsigned m = 0x80000000 | (*(unsigned *) &x << 8);
return -(int)((m >> e) & -(e < 32));
}
/*
* check the container file and set module global
*/
enum tfa_error tfa_load_cnt(void *cnt, int length) {
nxpTfaContainer_t *cntbuf = (nxpTfaContainer_t *)cnt;
g_cont = NULL;
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])) != paramsHdr ) {
pr_err("wrong header type: 0x%02x 0x%02x\n", cntbuf->id[0],g_cont->id[1]);
return tfa_error_container;
}
/* check CRC */
if ( tfaContCrcCheckContainer(cntbuf)) {
pr_err("CRC error\n");
return tfa_error_container;
}
/* check sub version level */
if ( (cntbuf->subversion[1] == NXPTFA_PM_SUBVERSION) &&
(cntbuf->subversion[0] == '0') ) {
g_cont = cntbuf;
cont_get_devs(g_cont);
} else {
pr_err("container sub-version not supported: %c%c\n",
cntbuf->subversion[0], cntbuf->subversion[1]);
return tfa_error_container;
}
return tfa_error_ok;
}
void tfa_deinit(void)
{
g_cont = NULL;
g_devs = -1;
}
/*
* Set the debug option
*/
void tfa_cnt_verbose(int level) {
tfa98xx_cnt_verbose = level;
}
/* start count from 1, 0 is invalid */
void tfaContSetCurrentVstep(int channel, int vstep_idx) {
if( channel<TFACONT_MAXDEVS)
nxp_tfa_vstep[channel] = vstep_idx+1;
else
pr_err("channel nr %d>%d\n", channel, TFACONT_MAXDEVS-1);
}
/* start count from 1, 0 is invalid */
int tfaContGetCurrentVstep(int channel) {
if( channel<TFACONT_MAXDEVS)
return nxp_tfa_vstep[channel]-1;
pr_err("channel nr %d>%d\n", channel, TFACONT_MAXDEVS-1);
return -1;
}
nxpTfaContainer_t * tfa98xx_get_cnt(void) {
return g_cont;
}
/*
* Dump the contents of the file header
*/
void tfaContShowHeader(nxpTfaHeader_t *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
*/
nxpTfaDeviceList_t *tfaContGetDevList(nxpTfaContainer_t * cont, int dev_idx)
{
uint8_t *base = (uint8_t *) cont;
if ( (dev_idx < 0) || (dev_idx >= cont->ndev))
return NULL;
if (cont->index[dev_idx].type != dscDevice)
return NULL;
base += cont->index[dev_idx].offset;
return (nxpTfaDeviceList_t *) base;
}
/*
* get the Nth profile for the Nth device
*/
nxpTfaProfileList_t *tfaContGetDevProfList(nxpTfaContainer_t * cont, int devIdx,
int profIdx)
{
nxpTfaDeviceList_t *dev;
int idx, hit;
uint8_t *base = (uint8_t *) cont;
dev = tfaContGetDevList(cont, devIdx);
if (dev) {
for (idx = 0, hit = 0; idx < dev->length; idx++) {
if (dev->list[idx].type == dscProfile) {
if (profIdx == hit++)
return (nxpTfaProfileList_t *) (dev->
list
[idx].
offset +
base);
}
}
}
return NULL;
}
/*
* get the Nth lifedata for the Nth device
*/
nxpTfaLiveDataList_t *tfaContGetDevLiveDataList(nxpTfaContainer_t * cont, int devIdx,
int lifeDataIdx)
{
nxpTfaDeviceList_t *dev;
int idx, hit;
uint8_t *base = (uint8_t *) cont;
dev = tfaContGetDevList(cont, devIdx);
if (dev) {
for (idx = 0, hit = 0; idx < dev->length; idx++) {
if (dev->list[idx].type == dscLiveData) {
if (lifeDataIdx == hit++)
return (nxpTfaLiveDataList_t *)
(dev->list[idx].offset + base);
}
}
}
return NULL;
}
/*
* Get the max volume step associated with Nth profile for the Nth device
*/
int tfacont_get_max_vstep(int dev_idx, int prof_idx) {
nxpTfaVolumeStep2File_t *vp;
struct nxpTfaVolumeStepMax2File *vp3;
int vstep_count = 0;
vp = (nxpTfaVolumeStep2File_t *) tfacont_getfiledata(dev_idx, prof_idx, volstepHdr);
if (vp == NULL)
return 0;
/* check the header type to load different NrOfVStep appropriately */
if (tfa98xx_dev_family(dev_idx) == 2) {
/* this is actually tfa2, so re-read the buffer*/
vp3 = (struct nxpTfaVolumeStepMax2File *)
tfacont_getfiledata(dev_idx, prof_idx, volstepHdr);
if ( vp3 ) {
vstep_count = vp3->NrOfVsteps;
}
} 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.
*/
nxpTfaFileDsc_t *tfacont_getfiledata(int dev_idx, int prof_idx, enum nxpTfaHeaderType type)
{
nxpTfaDeviceList_t *dev;
nxpTfaProfileList_t *prof;
nxpTfaFileDsc_t *file;
nxpTfaHeader_t *hdr;
unsigned int i;
if( g_cont==0 )
return NULL;
dev = tfaContGetDevList(g_cont, dev_idx);
if( dev==0 )
return NULL;
/* process the device list until a file type is encountered */
for(i=0;i<dev->length;i++) {
if ( dev->list[i].type == dscFile ) {
file = (nxpTfaFileDsc_t *)(dev->list[i].offset+(uint8_t *)g_cont);
hdr = (nxpTfaHeader_t *)file->data;
/* check for file type */
if ( hdr->id == type) {
//pr_debug("%s: file found of type %d in device %s \n", __FUNCTION__, type, tfaContDeviceName(devIdx));
return (nxpTfaFileDsc_t *)&file->data;
}
}
}
/* File not found in device tree.
* So, look in the profile list until the file type is encountered
*/
prof=tfaContGetDevProfList(g_cont, dev_idx, prof_idx);
for(i=0;i<prof->length;i++) {
if (prof->list[i].type == dscFile) {
file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)g_cont);
hdr= (nxpTfaHeader_t *)file->data;
/* check for file type */
if ( hdr->id == type) {
//pr_debug("%s: file found of type %d in profile %s\n", __FUNCTION__, type, tfaContProfileName(devIdx, profIdx));
return (nxpTfaFileDsc_t *)&file->data;
}
}
}
if ( tfa98xx_cnt_verbose )
pr_debug("%s: no file found of type %d\n", __FUNCTION__, type);
return NULL;
}
/*
* fill globals
*/
static void cont_get_devs(nxpTfaContainer_t *cont) {
nxpTfaProfileList_t *prof;
nxpTfaLiveDataList_t *liveD;
int i,j;
int count;
// get nr of devlists+1
for(i=0 ; i < cont->ndev ; i++) {
g_dev[i] = tfaContGetDevList(cont, i); // cache it
}
g_devs=cont->ndev;
// walk through devices and get the profile lists
for (i = 0; i < g_devs; i++) {
j=0;
count=0;
while ((prof = tfaContGetDevProfList(cont, i, j)) != NULL) {
count++;
g_prof[i][j++] = prof;
}
g_profs[i] = count; // count the nr of profiles per device
}
g_devs=cont->ndev;
// walk through devices and get the livedata lists
for (i = 0; i < g_devs; i++) {
j=0;
count=0;
while ((liveD = tfaContGetDevLiveDataList(cont, i, j)) != NULL) {
count++;
g_lived[i][j++] = liveD;
}
g_liveds[i] = count; // count the nr of livedata per device
}
}
static char nostring[]="Undefined string";
//TODO add to API
#define MODULE_BIQUADFILTERBANK 2
#define BIQUAD_COEFF_SIZE 6
/*
* write a parameter file to the device
*/
static enum Tfa98xx_Error tfaContWriteVstep(int dev_idx, nxpTfaVolumeStep2File_t *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(dev_idx, vol);
if (err != Tfa98xx_Error_Ok)
return err;
err = tfa98xx_dsp_write_preset( dev_idx, sizeof(vp->vstep[0].preset), vp->vstep[vstep].preset);
if (err != Tfa98xx_Error_Ok)
return err;
err = tfa_cont_write_filterbank(dev_idx, vp->vstep[vstep].filter);
} else {
pr_err("Incorrect volume given. The value vstep[%d] >= %d\n", nxp_tfa_vstep[dev_idx] , vp->vsteps);
err = Tfa98xx_Error_Bad_Parameter;
}
if ( tfa98xx_cnt_verbose ) pr_debug("vstep[%d][%d]\n", dev_idx, vstep);
return err;
}
static enum Tfa98xx_Error tfaContWriteVstepMax2(int dev_idx, nxpTfaVolumeStepMax2File_t *vp, int vstep_idx, int vstep_msg_idx)
{
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
struct nxpTfaVolumeStepRegisterInfo *regInfo = {0};
struct nxpTfaVolumeStepMessageInfo *msgInfo = {0};
nxpTfaBitfield_t bitF;
int msgLength=0, i, j, size=0, nrMessages, modified=0;
uint8_t cmdid_changed[3];
if(vstep_idx >= vp->NrOfVsteps) {
pr_debug("Volumestep %d is not available \n", vstep_idx);
return Tfa98xx_Error_Bad_Parameter;
}
for(i=0; i<=vstep_idx; i++) {
regInfo = (struct nxpTfaVolumeStepRegisterInfo*)(vp->vstepsBin + size);
msgInfo = (struct nxpTfaVolumeStepMessageInfo*)(vp->vstepsBin+
(regInfo->NrOfRegisters * sizeof(uint32_t)+sizeof(regInfo->NrOfRegisters)+size));
nrMessages = msgInfo->NrOfMessages;
for(j=0; j<nrMessages; j++) {
/* location of message j, from vstep i */
msgInfo = (struct nxpTfaVolumeStepMessageInfo*)(vp->vstepsBin+
(regInfo->NrOfRegisters * sizeof(uint32_t)+sizeof(regInfo->NrOfRegisters)+size));
/* message length */
msgLength = ( (msgInfo->MessageLength.b[0] << 16) + (msgInfo->MessageLength.b[1] << 8) + msgInfo->MessageLength.b[2]);
if (i == vstep_idx) {
/* If no vstepMsgIndex is passed on, all message needs to be send */
if ((vstep_msg_idx >= TFA_MAX_VSTEP_MSG_MARKER) || (vstep_msg_idx == j)) {
/*
* 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.
*/
if(TFA_GET_BF(dev_idx, SBSL) == 0) {
if(msgInfo->MessageType == 0) { /* If the messagetype(0) is AlgoParams */
/* Only do this when not set already */
if(msgInfo->CmdId[2] != SB_PARAM_SET_ALGO_PARAMS) {
cmdid_changed[0] = msgInfo->CmdId[0];
cmdid_changed[1] = msgInfo->CmdId[1];
cmdid_changed[2] = SB_PARAM_SET_ALGO_PARAMS;
modified = 1;
}
} else if(msgInfo->MessageType == 2) { /* If the messagetype(2) is MBDrc */
/* Only do this when not set already */
if(msgInfo->CmdId[2] != SB_PARAM_SET_MBDRC) {
cmdid_changed[0] = msgInfo->CmdId[0];
cmdid_changed[1] = msgInfo->CmdId[1];
cmdid_changed[2] = SB_PARAM_SET_MBDRC;
modified = 1;
}
}
}
/* Messagetype(3) is Smartstudio Info! Dont send this! */
if(msgInfo->MessageType != 3) {
if(modified == 1) {
if (tfa98xx_cnt_verbose) {
if(cmdid_changed[2] == SB_PARAM_SET_ALGO_PARAMS)
pr_debug("P-ID for SetAlgoParams modified!: ");
else
pr_debug("P-ID for SetMBDrc modified!: ");
pr_debug("Command-ID used: 0x%02x%02x%02x \n",
cmdid_changed[0], cmdid_changed[1], cmdid_changed[2]);
}
/* Send payload to dsp (Remove 1 from the length for cmdid) */
err = tfa_dsp_msg_id(dev_idx, (msgLength-1) * 3, (const char *)msgInfo->ParameterData, cmdid_changed);
if (err != Tfa98xx_Error_Ok)
return err;
} else {
/* Send cmdId + payload to dsp */
err = tfa_dsp_msg(dev_idx, msgLength * 3,(const char *)msgInfo->CmdId);
if (err != Tfa98xx_Error_Ok)
return err;
}
/* Set back to zero every time */
modified = 0;
}
}
}
if(msgInfo->MessageType == 3) {
/* MessageLength is in bytes */
size += sizeof(msgInfo->MessageType) + sizeof(msgInfo->MessageLength) + msgLength;
} else {
/* MessageLength is in words (3 bytes) */
size += sizeof(msgInfo->MessageType) + sizeof(msgInfo->MessageLength) + sizeof(msgInfo->CmdId) + ((msgLength-1) * 3);
}
}
size += sizeof(regInfo->NrOfRegisters) + (regInfo->NrOfRegisters * sizeof(uint32_t)) + sizeof(msgInfo->NrOfMessages);
}
if (regInfo->NrOfRegisters == 0) {
pr_debug("No registers in selected vstep (%d)!\n", vstep_idx);
return Tfa98xx_Error_Bad_Parameter;
}
for(i=0; i<regInfo->NrOfRegisters*2; i++) {
/* Byte swap the datasheetname */
bitF.field = (uint16_t)(regInfo->registerInfo[i]>>8) | (regInfo->registerInfo[i]<<8);
i++;
bitF.value = (uint16_t)regInfo->registerInfo[i]>>8;
err = tfaRunWriteBitfield(dev_idx , bitF);
if (err != Tfa98xx_Error_Ok)
return err;
}
/* Save the current vstep */
tfa_set_swvstep(dev_idx, (unsigned short)vstep_idx);
return err;
}
/*
* Write DRC message to the dsp
* If needed modify the cmd-id
*/
enum Tfa98xx_Error tfaContWriteDrcFile(int dev_idx, int size, uint8_t data[])
{
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
uint8_t cmdid_changed[3], modified = 0;
if(TFA_GET_BF(dev_idx, SBSL) == 0) {
/* Only do this when not set already */
if(data[2] != SB_PARAM_SET_MBDRC) {
cmdid_changed[0] = data[0];
cmdid_changed[1] = data[1];
cmdid_changed[2] = SB_PARAM_SET_MBDRC;
modified = 1;
if (tfa98xx_cnt_verbose) {
pr_debug("P-ID for SetMBDrc modified!: ");
pr_debug("Command-ID used: 0x%02x%02x%02x \n",
cmdid_changed[0], cmdid_changed[1], cmdid_changed[2]);
}
}
}
if(modified == 1) {
/* Send payload to dsp (Remove 3 from the length for cmdid) */
err = tfa_dsp_msg_id(dev_idx, size-3, (const char *)data, cmdid_changed);
} else {
/* Send cmdId + payload to dsp */
err = tfa_dsp_msg(dev_idx, size, (const char *)data);
}
return err;
}
/*
* 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 tfaContWriteFile(int dev_idx, nxpTfaFileDsc_t *file, int vstep_idx, int vstep_msg_idx)
{
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
nxpTfaHeader_t *hdr = (nxpTfaHeader_t *)file->data;
nxpTfaHeaderType_t type;
int size;
if ( tfa98xx_cnt_verbose ) {
tfaContShowHeader(hdr);
}
type = (nxpTfaHeaderType_t) hdr->id;
switch (type) {
case msgHdr: /* generic DSP message */
size = hdr->size - sizeof(nxpTfaMsgFile_t);
err = tfa_dsp_msg(dev_idx, size, (const char *)((nxpTfaMsgFile_t *)hdr)->data);
break;
case volstepHdr:
if (tfa98xx_dev_family(dev_idx) == 2) {
err = tfaContWriteVstepMax2(dev_idx, (nxpTfaVolumeStepMax2File_t *)hdr, vstep_idx, vstep_msg_idx);
} else {
err = tfaContWriteVstep(dev_idx, (nxpTfaVolumeStep2File_t *)hdr, vstep_idx);
}
/* If writing the vstep was succesfull, set new current vstep */
if(err == Tfa98xx_Error_Ok) {
tfaContSetCurrentVstep(dev_idx, vstep_idx);
}
break;
case speakerHdr:
if (tfa98xx_dev_family(dev_idx) == 2) {
/* Remove header and xml_id */
size = hdr->size - sizeof(struct nxpTfaSpkHeader) - sizeof(struct nxpTfaFWVer);
err = tfa_dsp_msg(dev_idx, size,
(const char *)(((nxpTfaSpeakerFile_t *)hdr)->data + (sizeof(struct nxpTfaFWVer))));
} else {
size = hdr->size - sizeof(nxpTfaSpeakerFile_t);
err = tfa98xx_dsp_write_speaker_parameters( dev_idx, size,
(const unsigned char *)((nxpTfaSpeakerFile_t *)hdr)->data);
}
break;
case presetHdr:
size = hdr->size - sizeof(nxpTfaPreset_t);
err = tfa98xx_dsp_write_preset( dev_idx, size, (const unsigned char *)((nxpTfaPreset_t *)hdr)->data);
break;
case equalizerHdr:
err = tfa_cont_write_filterbank(dev_idx, ((nxpTfaEqualizerFile_t *)hdr)->filter);
break;
case patchHdr:
size = hdr->size - sizeof(nxpTfaPatch_t ); // size is total length
err = tfa_dsp_patch(dev_idx, size, (const unsigned char *) ((nxpTfaPatch_t *)hdr)->data);
break;
case configHdr:
{
#ifdef FORCE_DSP_SETTING /*MCH_TO_TEST**/
bool momentarycfe= false;
u16 cfe= (uint16_t)TFA_GET_BF(dev_idx,CFE);
if(!cfe)
{
momentarycfe = true;
TFA_SET_BF(dev_idx, CFE,1);
}
#endif /*MCH_TO_TEST**/
size = hdr->size - sizeof(nxpTfaConfig_t);
err = tfa98xx_dsp_write_config(dev_idx, size, (const unsigned char *)((nxpTfaConfig_t *)hdr)->data);
#ifdef FORCE_DSP_SETTING /*MCH_TO_TEST**/
if (momentarycfe)
{
momentarycfe = false;
TFA_SET_BF(dev_idx, CFE,0);
}
#endif /*MCH_TO_TEST**/
}
break;
case drcHdr:
if(hdr->version[0] == NXPTFA_DR3_VERSION) {
/* Size is total size - hdrsize(36) - xmlversion(3) */
size = hdr->size - sizeof(nxpTfaDrc2_t);
err = tfaContWriteDrcFile(dev_idx, size, ((nxpTfaDrc2_t *)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(nxpTfaDrc_t);
size = 381; /* fixed size for first block */
//+381 is done to only send the second part of the drc block
err = tfa98xx_dsp_write_drc( dev_idx, size, ((const unsigned char *)((nxpTfaDrc_t *)hdr)->data+381));
}
break;
case infoHdr:
/* 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
*/
nxpTfaDescPtr_t *tfa_cnt_get_dsc(nxpTfaContainer_t *cnt, nxpTfaDescriptorType_t type, int dev_idx)
{
nxpTfaDeviceList_t *dev = tfaContDevice (dev_idx);
nxpTfaDescPtr_t *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 = (nxpTfaDescPtr_t *)(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_cnt_get_devid(nxpTfaContainer_t *cnt, int dev_idx) {
nxpTfaPatch_t *patchfile;
nxpTfaDescPtr_t *patchdsc;
uint8_t *patchheader;
unsigned short devid, checkaddress;
int checkvalue;
patchdsc = tfa_cnt_get_dsc(cnt, dscPatch, dev_idx);
patchdsc += 2; /* first the filename dsc and filesize, so skip them */
patchfile = (nxpTfaPatch_t *)patchdsc;
if (patchfile==NULL)
return 0;
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 slave for the device if it exists
*/
enum Tfa98xx_Error tfaContGetSlave(int dev_idx, uint8_t *slave_addr) {
nxpTfaDeviceList_t *dev = tfaContDevice (dev_idx);
if (dev==0 ) {
return Tfa98xx_Error_Bad_Parameter;
}
*slave_addr = dev->dev;
return Tfa98xx_Error_Ok;
}
/*
* write a bit field
*/
enum Tfa98xx_Error tfaRunWriteBitfield(Tfa98xx_handle_t dev_idx, nxpTfaBitfield_t bf) {
enum Tfa98xx_Error error;
uint16_t value;
union {
uint16_t field;
nxpTfaBfEnum_t Enum;
} bfUni;
value=bf.value;
bfUni.field = bf.field;
#ifdef TFA_DEBUG
if ( tfa98xx_cnt_verbose )
pr_debug("bitfield: %s=%d (0x%x[%d..%d]=0x%x)\n", tfaContBfName(bfUni.field, tfa98xx_dev_revision(dev_idx)), value,
bfUni.Enum.address, bfUni.Enum.pos, bfUni.Enum.pos+bfUni.Enum.len, value);
#endif
error = tfa_set_bf(dev_idx, bfUni.field, value);
return error;
}
/*
* read a bit field
*/
enum Tfa98xx_Error tfaRunReadBitfield(Tfa98xx_handle_t dev_idx, nxpTfaBitfield_t *bf) {
enum Tfa98xx_Error error;
union {
uint16_t field;
nxpTfaBfEnum_t Enum;
} bfUni;
uint16_t regvalue, msk;
bfUni.field = bf->field;
error = tfa98xx_read_register16(dev_idx, (unsigned char)(bfUni.Enum.address), &regvalue);
if (error) return error;
msk = ((1<<(bfUni.Enum.len+1))-1)<<bfUni.Enum.pos;
regvalue &= msk;
bf->value = regvalue>>bfUni.Enum.pos;
return error;
}
/*
dsp mem direct write
*/
enum Tfa98xx_Error tfaRunWriteDspMem(Tfa98xx_handle_t dev, nxpTfaDspMem_t *cfmem)
{
enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
int i;
for(i=0;i<cfmem->size;i++) {
if ( tfa98xx_cnt_verbose )
pr_debug("dsp mem (%d): 0x%02x=0x%04x\n", cfmem->type, cfmem->address, cfmem->words[i]);
error = tfa98xx_dsp_write_mem_word(dev, 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
*/
enum Tfa98xx_Error tfaRunWriteFilter(Tfa98xx_handle_t dev, nxpTfaContBiquad_t *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 the 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(tfa98xx_dev_family(dev) == 2) {
runs=2;
}
if ( tfa98xx_cnt_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(dev, bq->aa.index, &address, channel);
if(dmem<0)
return Tfa98xx_Error_Bad_Parameter;
/* 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 */
memcpy( &data[9], bq->aa.bytes, sizeof(bq->aa.bytes)); /* payload */
if(tfa98xx_dev_family(dev) == 2) {
error = tfa_dsp_cmd_id_write(dev, MODULE_FRAMEWORK, FW_PAR_ID_SET_MEMORY, sizeof(data), data);
} else {
error = tfa_dsp_cmd_id_write(dev, MODULE_FRAMEWORK, 4 /* param */ , sizeof(data), data);
}
}
#ifdef TFA_DEBUG
if ( tfa98xx_cnt_verbose ) {
if (bq->aa.index==13) {
pr_debug("=%d,%.0f,%.2f \n",
bq->in.type, bq->in.cutOffFreq, bq->in.leakage);
} else if(bq->aa.index >= 10 && bq->aa.index <= 12) {
pr_debug("=%d,%.0f,%.1f,%.1f \n", bq->aa.type,
bq->aa.cutOffFreq, bq->aa.rippleDb, bq->aa.rolloff);
} else {
pr_debug("= unsupported filter index \n");
}
}
#endif
/* 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
*/
enum Tfa98xx_Error tfaRunWriteRegister(Tfa98xx_handle_t handle, nxpTfaRegpatch_t *reg)
{
enum Tfa98xx_Error error;
uint16_t value,newvalue;
if ( tfa98xx_cnt_verbose )
pr_debug("register: 0x%02x=0x%04x (msk=0x%04x)\n", reg->address, reg->value, reg->mask);
error = tfa98xx_read_register16(handle, reg->address, &value);
if (error) return error;
value &= ~reg->mask;
newvalue = reg->value & reg->mask;
value |= newvalue;
error = tfa98xx_write_register16(handle, reg->address, value);
return error;
}
/*
* return the bitfield
*/
nxpTfaBitfield_t tfaContDsc2Bf(nxpTfaDescPtr_t dsc) {
uint32_t *ptr = (uint32_t *) (&dsc);
union {
nxpTfaBitfield_t bf;
uint32_t num;
} num_bf;
num_bf.num = *ptr; // & TFA_BITFIELDDSCMSK;
return num_bf.bf;
}
// write reg and bitfield items in the devicelist to the target
enum Tfa98xx_Error tfaContWriteRegsDev(int dev_idx) {
nxpTfaDeviceList_t *dev = tfaContDevice (dev_idx);
nxpTfaBitfield_t *bitF;
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 == dscPatch ||
dev->list[i].type ==dscFile ||
dev->list[i].type ==dscProfile ) break;
if ( dev->list[i].type == dscBitfield) {
bitF = (nxpTfaBitfield_t *)( dev->list[i].offset+(uint8_t *)g_cont);
err = tfaRunWriteBitfield(dev_idx , *bitF);
}
if ( dev->list[i].type == dscRegister ) {
err = tfaRunWriteRegister( dev_idx, (nxpTfaRegpatch_t *)
( dev->list[i].offset+(char*)g_cont));
}
if ( err ) break;
}
return err;
}
// write reg and bitfield items in the profilelist the target
enum Tfa98xx_Error tfaContWriteRegsProf(int dev_idx, int prof_idx) {
nxpTfaProfileList_t *prof = tfaContProfile( dev_idx, prof_idx);
nxpTfaBitfield_t *bitf;
unsigned int i;
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
if ( !prof ) {
return Tfa98xx_Error_Bad_Parameter;
}
if ( tfa98xx_cnt_verbose )
pr_debug("----- profile: %s (%d) -----\n", tfaContGetString(&prof->name), prof_idx);
/* process the list until the end of the profile or the default section */
for(i=0;i<prof->length;i++) {
/* We only want to write the values before the default section when we switch profile */
if(prof->list[i].type == dscDefault)
break;
if ( prof->list[i].type == dscBitfield) {
bitf = (nxpTfaBitfield_t *)( prof->list[i].offset+(uint8_t *)g_cont);
err = tfaRunWriteBitfield(dev_idx , *bitf);
}
if ( prof->list[i].type == dscRegister ) {
err = tfaRunWriteRegister( dev_idx, (nxpTfaRegpatch_t *)( !prof->list[i].offset+g_cont));
}
if ( err ) break;
}
return err;
}
// write patchfile in the devicelist to the target
enum Tfa98xx_Error tfaContWritePatch(int dev_idx) {
nxpTfaDeviceList_t *dev = tfaContDevice(dev_idx);
nxpTfaFileDsc_t *file;
nxpTfaPatch_t *patchfile;
int size;
int 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 == dscPatch ) {
file = (nxpTfaFileDsc_t *)(dev->list[i].offset+(uint8_t *)g_cont);
patchfile =(nxpTfaPatch_t *)&file->data;
if ( tfa98xx_cnt_verbose ) tfaContShowHeader(&patchfile->hdr);
size = patchfile->hdr.size - sizeof(nxpTfaPatch_t ); // size is total length
return tfa_dsp_patch(dev_idx, size, (const unsigned char *) patchfile->data);
}
}
return Tfa98xx_Error_Bad_Parameter; // patch not in the list
}
// write all param files in the devicelist to the target
enum Tfa98xx_Error tfaContWriteFiles(int dev_idx) {
nxpTfaDeviceList_t *dev = tfaContDevice(dev_idx);
nxpTfaFileDsc_t *file;
nxpTfaCmd_t *cmd;
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;
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 == dscFile ) {
file = (nxpTfaFileDsc_t *)(dev->list[i].offset+(uint8_t *)g_cont);
if ( tfaContWriteFile(dev_idx, file, 0 , TFA_MAX_VSTEP_MSG_MARKER) ){
return Tfa98xx_Error_Bad_Parameter;
}
}
if ( dev->list[i].type == dscSetInputSelect ||
dev->list[i].type == dscSetOutputSelect ||
dev->list[i].type == dscSetProgramConfig ||
dev->list[i].type == dscSetLagW ||
dev->list[i].type == dscSetGains ||
dev->list[i].type == dscSetvBatFactors ||
dev->list[i].type == dscSetSensesCal ||
dev->list[i].type == dscSetSensesDelay ||
dev->list[i].type == dscSetMBDrc ) {
create_dsp_buffer_msg((nxpTfaMsg_t *)
( dev->list[i].offset+(char*)g_cont), buffer, &size);
err = tfa_dsp_msg(dev_idx, size, buffer);
if ( tfa98xx_cnt_verbose ) {
pr_debug("command: %s=0x%02x%02x%02x \n",
tfaContGetCommandString(dev->list[i].type),
(unsigned char)buffer[0], (unsigned char)buffer[1], (unsigned char)buffer[2]);
}
}
if ( dev->list[i].type == dscCmd ) {
size = *(uint16_t *)(dev->list[i].offset+(char*)g_cont);
err = tfa_dsp_msg(dev_idx, size, dev->list[i].offset+2+(char*)g_cont);
if ( tfa98xx_cnt_verbose ) {
cmd = (nxpTfaCmd_t *)(dev->list[i].offset+(uint8_t *)g_cont);
pr_debug("Writing cmd=0x%02x%02x%02x \n", cmd->value[0], cmd->value[1], cmd->value[2]);
}
}
if (err != Tfa98xx_Error_Ok)
break;
if ( dev->list[i].type == dscCfMem ) {
err = tfaRunWriteDspMem(dev_idx, (nxpTfaDspMem_t *)(dev->list[i].offset+(uint8_t *)g_cont));
}
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 tfaContWriteFilesProf(int dev_idx, int prof_idx, int vstep_idx) {
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
nxpTfaProfileList_t *prof = tfaContProfile(dev_idx, prof_idx);
unsigned int i;
nxpTfaFileDsc_t *file;
nxpTfaPatch_t *patchfile;
int size;
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 dscFile:
file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)g_cont);
err = tfaContWriteFile(dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER);
break;
case dscFilter:
/* Filters are not written during coldstart
* Since calibration, SBSL and many other actions can overwrite them again
* They are written during operating mode (tfa_start)
*/
//err = tfaRunWriteFilter(dev_idx, (nxpTfaContBiquad_t *)(prof->list[i].offset+(uint8_t *)g_cont));
break;
case dscPatch:
file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)g_cont);
patchfile =(nxpTfaPatch_t *)&file->data;
if ( tfa98xx_cnt_verbose ) tfaContShowHeader(&patchfile->hdr);
size = patchfile->hdr.size - sizeof(nxpTfaPatch_t ); // size is total length
err = tfa_dsp_patch(dev_idx, size, (const unsigned char *) patchfile->data);
break;
case dscCfMem:
err = tfaRunWriteDspMem(dev_idx, (nxpTfaDspMem_t *)(prof->list[i].offset+(uint8_t *)g_cont));
break;
default:
/* ignore any other type */
break;
}
}
return err;
}
enum Tfa98xx_Error tfaContWriteItem(int dev_idx, nxpTfaDescPtr_t * dsc) {
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
//nxpTfaFileDsc_t *file;
nxpTfaRegpatch_t *reg;
nxpTfaMode_t *cas;
nxpTfaBitfield_t *bitf;
switch (dsc->type) {
case dscDefault:
case dscDevice: // ignore
case dscProfile: // profile list
break;
case dscRegister: // register patch
reg = (nxpTfaRegpatch_t *)(dsc->offset+(uint8_t *)g_cont);
return tfaRunWriteRegister(dev_idx, reg);
//pr_debug("$0x%2x=0x%02x,0x%02x\n", reg->address, reg->mask, reg->value);
break;
case dscString: // ascii: zero terminated string
pr_debug(";string: %s\n", tfaContGetString(dsc));
break;
case dscFile: // filename + file contents
case dscPatch:
break;
case dscMode:
cas = (nxpTfaMode_t *)(dsc->offset+(uint8_t *)g_cont);
if(cas->value == Tfa98xx_Mode_RCV)
tfa98xx_select_mode(dev_idx, Tfa98xx_Mode_RCV);
else
tfa98xx_select_mode(dev_idx, Tfa98xx_Mode_Normal);
break;
case dscCfMem:
err = tfaRunWriteDspMem(dev_idx, (nxpTfaDspMem_t *)(dsc->offset+(uint8_t *)g_cont));
break;
case dscBitfield:
bitf = (nxpTfaBitfield_t *)(dsc->offset+(uint8_t *)g_cont);
return tfaRunWriteBitfield(dev_idx , *bitf);
break;
case dscFilter:
return tfaRunWriteFilter(dev_idx, (nxpTfaContBiquad_t *)(dsc->offset+(uint8_t *)g_cont));
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(int dev_idx, int prof_idx) {
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
nxpTfaProfileList_t *prof = tfaContProfile(dev_idx, prof_idx);
unsigned int i;
int status;
if ( !prof ) {
return Tfa98xx_Error_Bad_Parameter;
}
if ( tfa98xx_cnt_verbose ) {
pr_debug("----- profile: %s (%d) -----\n", tfaContGetString(&prof->name), prof_idx);
pr_debug("Waiting for CLKS... \n");
}
for(i=10; i>0; i--) {
err = tfa98xx_dsp_system_stable(dev_idx, &status);
if(status)
break;
else
msleep_interruptible(10);
}
if(i==0) {
if ( tfa98xx_cnt_verbose )
pr_err("Unable to write filters, CLKS=0 \n");
return Tfa98xx_Error_StateTimedOut;
}
/* process the list until the end of the profile or the default section */
for(i=0;i<prof->length;i++) {
if ( prof->list[i].type == dscFilter ) {
if (tfaContWriteItem(dev_idx, &prof->list[i]) != Tfa98xx_Error_Ok)
return Tfa98xx_Error_Bad_Parameter;
}
}
return err;
}
unsigned int tfa98xx_get_profile_sr(int dev_idx, unsigned int prof_idx)
{
nxpTfaBitfield_t *bitf;
unsigned int i;
nxpTfaDeviceList_t *dev;
nxpTfaProfileList_t *prof;
int fs_profile = -1;
dev = tfaContDevice (dev_idx);
if (!dev)
return 0;
prof = tfaContProfile(dev_idx, prof_idx);
if (!prof)
return 0;
/* Check profile fields first */
for(i = 0; i < prof->length; i++) {
if(prof->list[i].type == dscDefault)
break;
/* check for profile settingd (AUDFS) */
if ( prof->list[i].type == dscBitfield ) {
bitf = (nxpTfaBitfield_t *)(prof->list[i].offset+(uint8_t *)g_cont);
if(bitf->field == TFA_FAM(dev_idx, AUDFS)) {
fs_profile = bitf->value;
break;
}
}
}
pr_debug("%s - profile fs: 0x%x = %dHz (%d - %d)\n", __FUNCTION__, fs_profile,
tfa98xx_sr_from_field(fs_profile),
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 == dscPatch ||
dev->list[i].type ==dscFile ||
dev->list[i].type ==dscProfile ) break;
if ( dev->list[i].type == dscBitfield) {
bitf = (nxpTfaBitfield_t *)( dev->list[i].offset+(uint8_t *)g_cont);
if(bitf->field == TFA_FAM(dev_idx, AUDFS)) {
fs_profile = bitf->value;
break;
}
}
/* Ignore register case */
}
pr_debug("%s - default fs: 0x%x = %dHz (%d - %d)\n", __FUNCTION__, fs_profile,
tfa98xx_sr_from_field(fs_profile),
dev_idx, prof_idx);
if (fs_profile != -1)
return tfa98xx_sr_from_field(fs_profile);
return 48000;
}
enum Tfa98xx_Error get_sample_rate_info(int dev_idx, nxpTfaProfileList_t *prof, nxpTfaProfileList_t *previous_prof, int fs_previous_profile)
{
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
nxpTfaBitfield_t *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 != dscDefault && 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 == dscBitfield ) {
bitf = (nxpTfaBitfield_t *)(previous_prof->list[i].offset+(uint8_t *)g_cont);
if(bitf->field == TFA_FAM(dev_idx, AUDFS)) {
fs_default_profile = bitf->value;
break;
}
}
}
}
/* ---------- settings next profile ---------- */
for(i=0;i<prof->length;i++) {
/* We only want to write the values before the default section */
if(prof->list[i].type == dscDefault)
break;
/* search for AUDFS */
if ( prof->list[i].type == dscBitfield ) {
bitf = (nxpTfaBitfield_t *)(prof->list[i].offset+(uint8_t *)g_cont);
if(bitf->field == TFA_FAM(dev_idx, 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 ( tfa98xx_cnt_verbose )
pr_debug("Writing delay tables for AUDFS=%d \n", fs_next_profile);
/* If the AUDFS from the next profile is not the same as
* the AUDFS from the default we need to write new delay tables
*/
err = tfa98xx_dsp_write_tables(dev_idx, fs_next_profile);
} else if(fs_default_profile != fs_previous_profile) {
if ( tfa98xx_cnt_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
* the AUDFS from the default profile is not the same as the AUDFS
* from the previous profile we also need to write new delay tables
*/
err = tfa98xx_dsp_write_tables(dev_idx, 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 tfaContWriteProfile(int dev_idx, int prof_idx, int vstep_idx) {
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
nxpTfaProfileList_t *prof = tfaContProfile(dev_idx, prof_idx);
nxpTfaProfileList_t *previous_prof = tfaContProfile(dev_idx, tfa_get_swprof(dev_idx));
char buffer[(MEMTRACK_MAX_WORDS * 3) + 3] = {0}; //every word requires 3 bytes, and 3 is the msg
unsigned int i, k=0, j=0, tries=0;
nxpTfaFileDsc_t *file;
nxpTfaCmd_t *cmd;
int size = 0, ready, fs_previous_profile = 8; /* default fs is 48kHz*/
if ( !prof )
return Tfa98xx_Error_Bad_Parameter;
if ( tfa98xx_cnt_verbose ) {
tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n", tfaContDeviceName(dev_idx),
tfaContProfileName(dev_idx,prof_idx),vstep_idx);
}
/* We only make a power cycle when the profiles are not in the same group */
if (prof->group == previous_prof->group && prof->group != 0) {
if ( tfa98xx_cnt_verbose ) {
pr_debug("The new profile (%s) is in the same group as the current profile (%s) \n",
tfaContGetString(&prof->name), tfaContGetString(&previous_prof->name));
}
} else {
/* mute */
tfaRunMute(dev_idx);
/* Get current sample rate before we start switching */
fs_previous_profile = TFA_GET_BF(dev_idx, AUDFS);
/* clear SBSL to make sure we stay in initCF state */
if(tfa98xx_dev_family(dev_idx) == 2) {
TFA_SET_BF_VOLATILE(dev_idx, SBSL, 0);
}
/* When we switch profile we first power down the subsystem
* This should only be done when we are in operating mode
* */
if(TFA_GET_BF(dev_idx, MANSTATE) == 9) {
err = tfa98xx_powerdown(dev_idx, 1);
if (err) return err;
/* Wait until we are in PLL powerdown */
do {
err = tfa98xx_dsp_system_stable(dev_idx, &ready);
if (!ready)
break;
else
msleep_interruptible(10); /* wait 10ms to avoid busload */
tries++;
} while (tries <= 100);
if (tries > 100) {
pr_debug("Wait for PLL powerdown timed out!\n");
return Tfa98xx_Error_StateTimedOut;
}
} else {
pr_debug("No need to go to powerdown now \n");
}
}
/* set all bitfield settings */
/* First set all default settings */
if (tfa98xx_cnt_verbose) {
pr_debug("---------- default settings profile: %s (%d) ---------- \n",
tfaContGetString(&previous_prof->name), tfa_get_swprof(dev_idx));
if(tfa98xx_dev_family(dev_idx) == 2)
err = show_current_state(dev_idx);
}
/* 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 != dscDefault && i < previous_prof->length) {
i++;
}
i++;
}
/* Only if we found the default section try writing the items */
if(i < previous_prof->length) {
if ( tfaContWriteItem(dev_idx, &previous_prof->list[i]) != Tfa98xx_Error_Ok )
return Tfa98xx_Error_Bad_Parameter;
}
}
if ( tfa98xx_cnt_verbose )
pr_debug("---------- new settings profile: %s (%d) ---------- \n",
tfaContGetString(&prof->name), prof_idx);
/* set new settings */
for(i=0;i<prof->length;i++) {
/* Remember where we currently are with writing items*/
j = i;
/* We only want to write the values before the default section when we switch profile */
/* process and write all non-file items */
switch (prof->list[i].type) {
case dscFile:
case dscPatch:
case dscSetInputSelect:
case dscSetOutputSelect:
case dscSetProgramConfig:
case dscSetLagW:
case dscSetGains:
case dscSetvBatFactors:
case dscSetSensesCal:
case dscSetSensesDelay:
case dscSetMBDrc:
case dscCmd:
case dscFilter:
case dscDefault:
/* When one of these files are found, we exit */
i = prof->length;
break;
default:
err = tfaContWriteItem(dev_idx, &prof->list[i]);
if ( err != Tfa98xx_Error_Ok )
return Tfa98xx_Error_Bad_Parameter;
break;
}
}
if (prof->group != previous_prof->group || prof->group == 0) {
if(tfa98xx_dev_family(dev_idx) == 2)
TFA_SET_BF_VOLATILE(dev_idx, MANSCONF, 1);
/* Leave powerdown state */
err = tfa_cf_powerup(dev_idx);
if (err) return err;
if (tfa98xx_cnt_verbose && tfa98xx_dev_family(dev_idx) == 2)
err = show_current_state(dev_idx);
if (tfa98xx_dev_family(dev_idx) == 2) {
/* Reset SBSL to 0 (workaround of enbl_powerswitch=0) */
TFA_SET_BF_VOLATILE(dev_idx, SBSL, 0);
/* Sending commands to DSP we need to make sure RST is 0 (otherwise we get no response)*/
TFA_SET_BF(dev_idx, RST, 0);
}
}
/* Check if there are sample rate changes */
err = get_sample_rate_info(dev_idx, prof, previous_prof, fs_previous_profile);
if (err) return err;
/* Write files from previous profile (default section)
* Should only be used for the patch&trap patch (file)
*/
if(tfa98xx_dev_family(dev_idx) == 2) {
for(i=0;i<previous_prof->length;i++) {
/* Search for the default section */
if(i == 0) {
while(previous_prof->list[i].type != dscDefault && i < previous_prof->length) {
i++;
}
i++;
}
/* Only if we found the default section try writing the file */
if(i < previous_prof->length) {
if(previous_prof->list[i].type == dscFile || previous_prof->list[i].type == dscPatch) {
/* Only write this once */
if ( tfa98xx_cnt_verbose && k==0) {
pr_debug("---------- files default profile: %s (%d) ---------- \n",
tfaContGetString(&previous_prof->name), prof_idx);
k++;
}
file = (nxpTfaFileDsc_t *)(previous_prof->list[i].offset+(uint8_t *)g_cont);
err = tfaContWriteFile(dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER);
}
}
}
}
if ( tfa98xx_cnt_verbose) {
pr_debug("---------- files new profile: %s (%d) ---------- \n",
tfaContGetString(&prof->name), prof_idx);
}
/* write everything until end or the default section starts
* Start where we currenly left */
for(i=j;i<prof->length;i++) {
/* We only want to write the values before the default section when we switch profile */
if(prof->list[i].type == dscDefault)
break;
switch (prof->list[i].type) {
case dscFile:
case dscPatch:
file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)g_cont);
err = tfaContWriteFile(dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER);
break;
case dscSetInputSelect:
case dscSetOutputSelect:
case dscSetProgramConfig:
case dscSetLagW:
case dscSetGains:
case dscSetvBatFactors:
case dscSetSensesCal:
case dscSetSensesDelay:
case dscSetMBDrc:
create_dsp_buffer_msg((nxpTfaMsg_t *)
( prof->list[i].offset+(char*)g_cont), buffer, &size);
err = tfa_dsp_msg(dev_idx, size, buffer);
if ( tfa98xx_cnt_verbose )
pr_debug("command: %s=0x%02x%02x%02x \n",
tfaContGetCommandString(prof->list[i].type),
(unsigned char)buffer[0], (unsigned char)buffer[1], (unsigned char)buffer[2]);
break;
case dscCmd:
size = *(uint16_t *)(prof->list[i].offset+(char*)g_cont);
err = tfa_dsp_msg(dev_idx, size, prof->list[i].offset+2+(char*)g_cont);
if ( tfa98xx_cnt_verbose ) {
cmd = (nxpTfaCmd_t *)(prof->list[i].offset+(uint8_t *)g_cont);
pr_debug("Writing cmd=0x%02x%02x%02x \n", cmd->value[0], cmd->value[1], cmd->value[2]);
}
break;
default:
/* This allows us to write bitfield, registers or xmem after files */
if (tfaContWriteItem(dev_idx, &prof->list[i]) != Tfa98xx_Error_Ok)
return Tfa98xx_Error_Bad_Parameter;
break;
}
if (err != Tfa98xx_Error_Ok)
return err;
}
if ((prof->group != previous_prof->group || prof->group == 0) && tfa98xx_dev_family(dev_idx) == 2) {
if (TFA_GET_BF(dev_idx, REFCKSEL) == 0) {
/* set SBSL to go to operation mode */
TFA_SET_BF_VOLATILE(dev_idx, SBSL, 1);
}
}
return err;
}
/*
* process only vstep in the profilelist
*
*/
enum Tfa98xx_Error tfaContWriteFilesVstep(int dev_idx, int prof_idx, int vstep_idx)
{
nxpTfaProfileList_t *prof = tfaContProfile(dev_idx, prof_idx);
unsigned int i;
nxpTfaFileDsc_t *file;
nxpTfaHeader_t *hdr;
nxpTfaHeaderType_t type;
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
if ( !prof )
return Tfa98xx_Error_Bad_Parameter;
if ( tfa98xx_cnt_verbose )
tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n", tfaContDeviceName(dev_idx),
tfaContProfileName(dev_idx,prof_idx),vstep_idx);
/* write vstep file only! */
for(i=0;i<prof->length;i++) {
if ( prof->list[i].type == dscFile ) {
file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)g_cont);
hdr = (nxpTfaHeader_t *)file->data;
type = (nxpTfaHeaderType_t) hdr->id;
switch (type) {
case volstepHdr:
if ( tfaContWriteFile(dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER) )
return Tfa98xx_Error_Bad_Parameter;
break;
default:
break;
}
}
}
return err;
}
char *tfaContGetString(nxpTfaDescPtr_t * dsc)
{
if ( dsc->type != dscString)
return nostring;
return dsc->offset+(char*)g_cont;
}
void individual_calibration_results(Tfa98xx_handle_t handle)
{
int value_P, value_S;
/* Read the calibration result in xmem (529=primary channel) (530=secondary channel) */
tfa98xx_dsp_read_mem(handle, 529, 1, &value_P);
tfa98xx_dsp_read_mem(handle, 530, 1, &value_S);
if(value_P != 1 && value_S != 1)
pr_debug("Calibration failed on both channels! \n");
else if(value_P != 1) {
pr_debug("Calibration failed on Primary (Left) channel! \n");
TFA_SET_BF_VOLATILE(handle, SSLEFTE, 0); /* Disable the sound for the left speaker */
}
else if(value_S != 1) {
pr_debug("Calibration failed on Secondary (Right) channel! \n");
TFA_SET_BF_VOLATILE(handle, SSRIGHTE, 0); /* Disable the sound for the right speaker */
}
TFA_SET_BF_VOLATILE(handle, AMPINSEL, 0); /* Set amplifier input to TDM */
TFA_SET_BF_VOLATILE(handle, SBSL, 1);
}
char *tfaContGetCommandString(uint32_t type)
{
if(type == dscSetInputSelect)
return "setInputSelector";
else if(type == dscSetOutputSelect)
return "setOutputSelector";
else if(type == dscSetProgramConfig)
return "setProgramConfig";
else if(type == dscSetLagW)
return "setLagW";
else if(type == dscSetGains)
return "setGains";
else if(type == dscSetvBatFactors)
return "setvBatFactors";
else if(type == dscSetSensesCal)
return "setSensesCal";
else if(type == dscSetSensesDelay)
return "setSensesDelay";
else if(type == dscSetMBDrc)
return "setMBDrc";
else if(type == dscFilter)
return "filter";
else
return nostring;
}
/*
* Get the name of the device at a certain index in the container file
* return device name
*/
char *tfaContDeviceName(int dev_idx) {
nxpTfaDeviceList_t *dev;
if ( dev_idx >= tfa98xx_cnt_max_device() )
return errorname;
if ( (dev = tfaContDevice(dev_idx)) == NULL )
return errorname;
return tfaContGetString(&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_cnt_get_app_name(char *name)
{
unsigned int i;
int len = 0;
for(i=0; i<sizeof(g_cont->application); i++) {
if (isalnum(g_cont->application[i])) /* copy char if valid */
name[len++] = g_cont->application[i];
if (g_cont->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 tfaContGetCalProfile(int dev_idx) {
int prof, nprof, cal_idx = -2;
if ( (dev_idx < 0) || (dev_idx >= tfa98xx_cnt_max_device()) )
return -1;
nprof = tfaContMaxProfile(dev_idx);
/* search for the calibration profile in the list of profiles */
for (prof = 0; prof < nprof; prof++) {
if(strstr(tfaContProfileName(dev_idx, prof), ".cal") != NULL) {
cal_idx = prof;
pr_debug("Using calibration profile: '%s'\n", tfaContProfileName(dev_idx, prof));
break;
}
}
return cal_idx;
}
/**
* Is the profile a tap profile ?
* @param dev_idx the index of the device
* @param prof_idx the index of the profile
* @return 1 if the profile is a tap profile or 0 if not
*/
int tfaContIsTapProfile(int dev_idx, int prof_idx)
{
int nprof;
if ( (dev_idx < 0) || (dev_idx >= tfa98xx_cnt_max_device()) )
return -1;
nprof = tfaContMaxProfile(dev_idx);
/* Check if next profile is tap profile */
if (strstr(tfaContProfileName(dev_idx, prof_idx), ".tap") != NULL) {
pr_debug("Using Tap profile: '%s'\n", tfaContProfileName(dev_idx, prof_idx));
return 1;
}
return 0;
}
/*
* Get the name of the profile at certain index for a device in the container file
* return profile name
*/
char *tfaContProfileName(int dev_idx, int prof_idx) {
nxpTfaProfileList_t *prof;
if ( (dev_idx < 0) || (dev_idx >= tfa98xx_cnt_max_device()) )
return errorname;
if ( (prof_idx < 0) || (prof_idx >= tfaContMaxProfile(dev_idx)) )
return nonename;
// the Nth profiles for this device
prof=tfaContGetDevProfList(g_cont, dev_idx, prof_idx);
return tfaContGetString(&prof->name);
}
/*
* return 1st profile list
*/
nxpTfaProfileList_t *tfaContGet1stProfList(nxpTfaContainer_t * cont)
{
nxpTfaProfileList_t *prof;
uint8_t *b = (uint8_t *) cont;
int maxdev = 0;
nxpTfaDeviceList_t *dev;
// get nr of devlists
maxdev = cont->ndev;
// get last devlist
dev = tfaContGetDevList(cont, maxdev - 1);
if(dev == NULL)
return NULL;
// the 1st profile starts after the last device list
b = (uint8_t *) dev + sizeof(nxpTfaDeviceList_t) + dev->length * (sizeof(nxpTfaDescPtr_t));
prof = (nxpTfaProfileList_t *) b;
return prof;
}
/*
* return 1st livedata list
*/
nxpTfaLiveDataList_t *tfaContGet1stLiveDataList(nxpTfaContainer_t * cont)
{
nxpTfaLiveDataList_t *ldata;
nxpTfaProfileList_t *prof;
nxpTfaDeviceList_t *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 = tfaContGetDevList(cont, maxdev - 1);
// the 1st livedata starts after the last device list
b = (uint8_t *) dev + sizeof(nxpTfaDeviceList_t) +
dev->length * (sizeof(nxpTfaDescPtr_t));
while(maxprof != 0) {
// get last proflist
prof = (nxpTfaProfileList_t *) b;
b += sizeof(nxpTfaProfileList_t) +
((prof->length-1) * (sizeof(nxpTfaDescPtr_t)));
maxprof--;
}
/* Else the marker falls off */
b += 4; //bytes
ldata = (nxpTfaLiveDataList_t *) b;
return ldata;
}
enum Tfa98xx_Error tfaContOpen(int dev_idx)
{
return tfa98xx_open((Tfa98xx_handle_t)dev_idx);
}
enum Tfa98xx_Error tfaContClose(int dev_idx)
{
return tfa98xx_close(dev_idx);
}
/*
* return the device count in the container file
*/
int tfa98xx_cnt_max_device(void) {
return g_cont !=NULL ? g_cont->ndev : 0;
}
/*
* lookup slave and return device index
*/
int tfa98xx_cnt_slave2idx(int slave_addr) {
int idx;
for(idx=0;idx<g_devs;idx++) {
if (g_dev[idx]->dev == slave_addr )
return idx;
}
return -1;
}
/*
* lookup slave and return device revid
*/
int tfa98xx_cnt_slave2revid(int slave_addr) {
int idx = tfa98xx_cnt_slave2idx(slave_addr);
uint16_t revid;
if (idx<0)
return idx;
/* note that the device must have been opened before */
revid = tfa98xx_get_device_revision(idx);
/* quick check for valid contents */
return (revid&0xFF) >= 0x12 ? revid : -1 ;
}
/*
* return the device list pointer
*/
nxpTfaDeviceList_t *tfaContDevice(int dev_idx) {
if(dev_idx < g_devs)
return g_dev[dev_idx];
//pr_err("Devlist index too high:%d!", idx);
return NULL;
}
/*
* return the per device profile count
*/
int tfaContMaxProfile(int dev_idx) {
if ( dev_idx >= g_devs) {
//pr_err("Devlist index too high:%d!", ndev);
return 0;
}
return g_profs[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
*/
nxpTfaProfileList_t* tfaContNextProfile(nxpTfaProfileList_t* prof) {
uint8_t *this, *next; /* byte pointers for byte pointer arithmetic */
nxpTfaProfileList_t* 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(nxpTfaDescPtr_t);
/* the sizeof(nxpTfaProfileList_t) includes the list[0] length */
next = this + listlength + sizeof(nxpTfaProfileList_t);// - sizeof(nxpTfaDescPtr_t);
nextprof = (nxpTfaProfileList_t *)next;
if (nextprof->ID != TFA_PROFID)
return NULL;
return nextprof;
}
/*
* return the next livedata
*/
nxpTfaLiveDataList_t* tfaContNextLiveData(nxpTfaLiveDataList_t* livedata) {
nxpTfaLiveDataList_t* nextlivedata = (nxpTfaLiveDataList_t *)( (char*)livedata + (livedata->length*4) +
sizeof(nxpTfaLiveDataList_t) -4);
if (nextlivedata->ID == TFA_LIVEDATAID)
return nextlivedata;
return NULL;
}
/*
* return the device list pointer
*/
nxpTfaProfileList_t* tfaContProfile(int dev_idx, int prof_ipx) {
if ( dev_idx >= g_devs) {
//pr_err("Devlist index too high:%d!", ndev);
return NULL;
}
if ( prof_ipx >= g_profs[dev_idx]) {
//pr_err("Proflist index too high:%d!", nprof);
return NULL;
}
return g_prof[dev_idx][prof_ipx];
}
/*
* check CRC for container
* CRC is calculated over the bytes following the CRC field
*
* return 0 on error
*/
int tfaContCrcCheckContainer(nxpTfaContainer_t *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;
}
/**
* Create a buffer which can be used to send to the dsp.
*/
void create_dsp_buffer_msg(nxpTfaMsg_t *msg, char *buffer, int *size) //TODO cleanup
{
int i, j = 0;
/* Copy cmdId. Remember that the cmdId is reversed */
buffer[0] = msg->cmdId[2];
buffer[1] = msg->cmdId[1];
buffer[2] = msg->cmdId[0];
/* Copy the data to the buffer */
for(i=3; i<3+(msg->msg_size*3); i++) {
buffer[i] = (uint8_t) ((msg->data[j] >> 16) & 0xffff);
i++;
buffer[i] = (uint8_t) ((msg->data[j] >> 8) & 0xff);
i++;
buffer[i] = (uint8_t) (msg->data[j] & 0xff);
j++;
}
*size = (3+(msg->msg_size*3)) * sizeof(char);
}
void get_all_features_from_cnt(Tfa98xx_handle_t dev_idx, int *hw_feature_register, int sw_feature_register[2]) {
nxpTfaFeatures_t *features;
int i;
nxpTfaDeviceList_t *dev = tfaContDevice(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 == dscFeatures) {
features = (nxpTfaFeatures_t *)(dev->list[i].offset+(uint8_t *)g_cont);
*hw_feature_register = features->value[0];
sw_feature_register[0] = features->value[1];
sw_feature_register[1] = features->value[2];
break;
}
}
}
/* wrapper function */
void get_hw_features_from_cnt(Tfa98xx_handle_t dev_idx, int *hw_feature_register)
{
int sw_feature_register[2];
get_all_features_from_cnt(dev_idx, hw_feature_register, sw_feature_register);
}
/* wrapper function */
void get_sw_features_from_cnt(Tfa98xx_handle_t dev_idx, int sw_feature_register[2])
{
int hw_feature_register;
get_all_features_from_cnt(dev_idx, &hw_feature_register, sw_feature_register);
}
/* Factory trimming for the Boost converter */
void tfa_factory_trimmer(Tfa98xx_handle_t dev_idx)
{
unsigned short currentValue, delta;
int result;
/* Factory trimming for the Boost converter */
/* check if there is a correction needed */
result = TFA_GET_BF(dev_idx, DCMCCAPI);
if (result) {
/* Get currentvalue of DCMCC and the Delta value */
currentValue = (unsigned short)TFA_GET_BF(dev_idx, DCMCC);
delta = (unsigned short)TFA_GET_BF(dev_idx, USERDEF);
/* check the sign bit (+/-) */
result = TFA_GET_BF(dev_idx, DCMCCSB);
if (result == 0) {
/* Do not exceed the maximum value of 15 */
if(currentValue + delta < 15) {
TFA_SET_BF_VOLATILE(dev_idx, DCMCC, currentValue + delta);
if (tfa98xx_cnt_verbose)
pr_debug("Max coil current is set to: %d \n", currentValue + delta);
} else {
TFA_SET_BF_VOLATILE(dev_idx, DCMCC, 15);
if (tfa98xx_cnt_verbose)
pr_debug("Max coil current is set to: 15 \n");
}
} else if (result == 1) {
/* Do not exceed the minimum value of 0 */
if(currentValue - delta > 0) {
TFA_SET_BF_VOLATILE(dev_idx, DCMCC, currentValue - delta);
if (tfa98xx_cnt_verbose)
pr_debug("Max coil current is set to: %d \n", currentValue - delta);
} else {
TFA_SET_BF_VOLATILE(dev_idx, DCMCC, 0);
if (tfa98xx_cnt_verbose)
pr_debug("Max coil current is set to: 0 \n");
}
}
}
}
enum Tfa98xx_Error tfa_set_filters(int dev_idx, int prof_idx) {
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
nxpTfaProfileList_t *prof = tfaContProfile(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(dev_idx, PWDN) == 1)
return Tfa98xx_Error_Ok;
/* loop the profile to find filter settings */
for(i=0;i<prof->length;i++) {
/* We only want to write the values before the default section */
if(prof->list[i].type == dscDefault)
break;
/* write all filter settings */
if ( prof->list[i].type == dscFilter) {
if (tfaContWriteItem(dev_idx, &prof->list[i]) != Tfa98xx_Error_Ok)
return err;
}
}
return err;
}