| /* |
| *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), ®value); |
| 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; |
| } |