/* * Copyright 2014 NXP Semiconductors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 or later * as published by the Free Software Foundation. */ #include "tfa_internal.h" #include "tfa_service.h" #include "tfa_container.h" #include "config.h" #include "tfa.h" #include "tfa_dsp_fw.h" #include "tfa98xx_tfafieldnames.h" /* module globals */ static int tfa98xx_cnt_verbose; static struct tfa_container *g_cont; /* container file */ static int g_devs = -1; /* nr of devices TODO use direct access to cont? */ static struct tfa_device_list *g_dev[TFACONT_MAXDEVS]; static int g_profs[TFACONT_MAXDEVS]; static int g_liveds[TFACONT_MAXDEVS]; static struct tfa_profile_list *g_prof[TFACONT_MAXDEVS][TFACONT_MAXPROFS]; static struct tfa_livedata_list *g_lived[TFACONT_MAXDEVS][TFACONT_MAXPROFS]; static int nxp_tfa_vstep[TFACONT_MAXDEVS]; #define ERROR_STRING "!ERROR!" #define NONE_STRING "NONE" #define UNDEF_STRING "Undefined string" static int partial_enable; /* defines */ #define MODULE_BIQUADFILTERBANK 2 #define BIQUAD_COEFF_SIZE 6 static void cont_get_devs(struct tfa_container *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)); } void tfa_set_partial_update(int enp) { partial_enable = enp; } /* * check the container file and set module global */ enum tfa_error tfa_load_cnt(void *cnt, int length) { struct tfa_container *cntbuf = (struct tfa_container *)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])) != params_hdr) { pr_err("wrong header type: 0x%02x 0x%02x\n", cntbuf->id[0], cntbuf->id[1]); return tfa_error_container; } if (cntbuf->size == 0) { pr_err("data size is 0\n"); return tfa_error_container; } /* check CRC */ if (tfa_cont_crc_check_container(cntbuf)) { pr_err("CRC error\n"); return tfa_error_container; } /* check sub version level */ if ((cntbuf->subversion[1] == 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_cont_verbose(int level) { tfa98xx_cnt_verbose = level; if (tfa98xx_cnt_verbose) pr_debug("%s: level:%d\n", __func__, level); } /* start count from 1, 0 is invalid */ void tfa_cont_set_current_vstep(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 tfa_cont_get_current_vstep(int channel) { if (channel < TFACONT_MAXDEVS) return nxp_tfa_vstep[channel]-1; pr_err("channel nr %d>%d\n", channel, TFACONT_MAXDEVS-1); return TFA_ERROR; } struct tfa_container *tfa98xx_get_cnt(void) { return g_cont; } /* * Dump the contents of the file header */ void tfa_cont_show_header(struct tfa_header *hdr) { char _id[2]; pr_debug("File header\n"); _id[1] = hdr->id >> 8; _id[0] = hdr->id & 0xff; pr_debug("\tid:%.2s version:%.2s subversion:%.2s\n", _id, hdr->version, hdr->subversion); pr_debug("\tsize:%d CRC:0x%08x\n", hdr->size, hdr->crc); pr_debug("\tcustomer:%.8s application:%.8s type:%.8s\n", hdr->customer, hdr->application, hdr->type); } /* * return device list dsc from index */ struct tfa_device_list * tfa_cont_get_dev_list(struct tfa_container *cont, int dev_idx) { uint8_t *base = (uint8_t *) cont; if ((dev_idx < 0) || (dev_idx >= cont->ndev)) return NULL; if (cont->index[dev_idx].type != dsc_device) return NULL; base += cont->index[dev_idx].offset; return (struct tfa_device_list *) base; } /* * get the Nth profile for the Nth device */ struct tfa_profile_list * tfa_cont_get_dev_prof_list(struct tfa_container *cont, int dev_idx, int prof_idx) { struct tfa_device_list *dev; int idx, hit; uint8_t *base = (uint8_t *) cont; struct tfa_profile_list *prof_list = NULL; dev = tfa_cont_get_dev_list(cont, dev_idx); if (!dev) return NULL; for (idx = 0, hit = 0; idx < dev->length; idx++) { if (dev->list[idx].type != dsc_profile) continue; if (prof_idx != hit++) continue; prof_list = (struct tfa_profile_list *) (dev->list[idx].offset + base); break; } return prof_list; } /* * get the Nth lifedata for the Nth device */ struct tfa_livedata_list * tfa_cont_get_dev_livedata_list(struct tfa_container *cont, int dev_idx, int lifedata_idx) { struct tfa_device_list *dev; int idx, hit; uint8_t *base = (uint8_t *) cont; struct tfa_livedata_list *livedata_list = NULL; dev = tfa_cont_get_dev_list(cont, dev_idx); if (!dev) return NULL; for (idx = 0, hit = 0; idx < dev->length; idx++) { if (dev->list[idx].type != dsc_livedata) continue; if (lifedata_idx != hit++) continue; livedata_list = (struct tfa_livedata_list *) (dev->list[idx].offset + base); break; } return livedata_list; } /* * Get the max volume step associated with Nth profile for the Nth device */ int tfa_cont_get_max_vstep(int dev_idx, int prof_idx) { struct tfa_volume_step2_file *vp; struct tfa_volume_step_max2_file *vp3; int vstep_count = 0; vp = (struct tfa_volume_step2_file *) tfa_cont_get_file_data(dev_idx, prof_idx, volstep_hdr); 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 tfa_volume_step_max2_file *) tfa_cont_get_file_data(dev_idx, prof_idx, volstep_hdr); if (vp3) vstep_count = vp3->nr_of_vsteps; } else { /* this is max1*/ if (vp) vstep_count = vp->vsteps; } return vstep_count; } /** * Get the file contents associated with the device or profile * Search within the device tree, if not found, search within the profile * tree. There can only be one type of file within profile or device. */ struct tfa_file_dsc * tfa_cont_get_file_data(int dev_idx, int prof_idx, enum tfa_header_type type) { struct tfa_device_list *dev; struct tfa_profile_list *prof; struct tfa_file_dsc *file; struct tfa_header *hdr; unsigned int i; if (g_cont == NULL) { pr_err("invalid pointer to container file\n"); return NULL; } dev = tfa_cont_get_dev_list(g_cont, dev_idx); if (dev == NULL) { pr_err("invalid pointer to container file device list\n"); return NULL; } /* process the device list until a file type is encountered */ for (i = 0; i < dev->length; i++) { if (dev->list[i].type == dsc_file) { file = (struct tfa_file_dsc *) (dev->list[i].offset+(uint8_t *)g_cont); if (file != NULL) { hdr = (struct tfa_header *)file->data; /* check for file type */ if (hdr->id == type) { /* pr_debug("%s: file found of type " * "%d in device %s\n", * __func__, type, * tfa_cont_device_name(dev_idx)); */ return (struct tfa_file_dsc *) &file->data; } } } } /* File not found in device tree. * So, look in the profile list until the file type is encountered */ prof = tfa_cont_get_dev_prof_list(g_cont, dev_idx, prof_idx); if (prof == NULL) { pr_err("invalid pointer to container file profile list\n"); return NULL; } for (i = 0; i < prof->length; i++) { if (prof->list[i].type == dsc_file) { file = (struct tfa_file_dsc *) (prof->list[i].offset + (uint8_t *)g_cont); if (file != NULL) { hdr = (struct tfa_header *)file->data; if (hdr != NULL) { /* check for file type */ if (hdr->id == type) { /* pr_debug("%s: file found of " * "type %d in profile %s\n", * __func__, type, * tfa_cont_profile_name * (dev_idx, prof_idx)); */ return (struct tfa_file_dsc *) &file->data; } } } } } if (tfa98xx_cnt_verbose) pr_debug("%s: no file found of type %d\n", __func__, type); return NULL; } /* * fill globals */ static void cont_get_devs(struct tfa_container *cont) { struct tfa_profile_list *prof; struct tfa_livedata_list *lived; int i, j; int count; /* get nr of devlists+1 */ for (i = 0; i < cont->ndev; i++) g_dev[i] = tfa_cont_get_dev_list(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 = tfa_cont_get_dev_prof_list(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 = tfa_cont_get_dev_livedata_list(cont, i, j)) != NULL) { count++; g_lived[i][j++] = lived; } g_liveds[i] = count; /* count the nr of livedata per device */ } } /* * write a parameter file to the device */ static enum tfa98xx_error tfa_cont_write_vstep(int dev_idx, struct tfa_volume_step2_file *vp, int vstep) { enum tfa98xx_error err; unsigned short vol; if (vstep < vp->vsteps) { /* vol = (unsigned short)(voldB / (-0.5f)); */ vol = (unsigned short) (-2 * float_to_int (*((uint32_t *)&vp->vstep[vstep].attenuation))); if (vol > 255) /* restricted to 8 bits */ vol = 255; err = tfa98xx_set_volume_level(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 struct tfa_volume_step_message_info * tfa_cont_get_msg_info_from_reg(struct tfa_volume_step_register_info *reg_info) { char *p = (char *)reg_info; p += sizeof(reg_info->nr_of_registers) + (reg_info->nr_of_registers * sizeof(uint32_t)); return (struct tfa_volume_step_message_info *)p; } static int tfa_cont_get_msg_len(struct tfa_volume_step_message_info *msg_info) { return (msg_info->message_length.b[0] << 16) + (msg_info->message_length.b[1] << 8) + msg_info->message_length.b[2]; } static struct tfa_volume_step_message_info * tfa_cont_get_next_msg_info(struct tfa_volume_step_message_info *msg_info) { char *p = (char *)msg_info; int msgLen = tfa_cont_get_msg_len(msg_info); int type = msg_info->message_type; p += sizeof(msg_info->message_type) + sizeof(msg_info->message_length); if (type == 3) p += msgLen; else p += msgLen * 3; return (struct tfa_volume_step_message_info *)p; } static struct tfa_volume_step_register_info * tfa_cont_get_next_reg_from_end_info( struct tfa_volume_step_message_info *msg_info) { char *p = (char *)msg_info; p += sizeof(msg_info->nr_of_messages); return (struct tfa_volume_step_register_info *)p; } static struct tfa_volume_step_register_info* tfa_cont_get_reg_for_vstep(struct tfa_volume_step_max2_file *vp, int idx) { int i, j, nrMessage; struct tfa_volume_step_register_info *reg_info = (struct tfa_volume_step_register_info *)vp->vsteps_bin; struct tfa_volume_step_message_info *msg_info = NULL; for (i = 0; i < idx; i++) { msg_info = tfa_cont_get_msg_info_from_reg(reg_info); nrMessage = msg_info->nr_of_messages; for (j = 0; j < nrMessage; j++) msg_info = tfa_cont_get_next_msg_info(msg_info); reg_info = tfa_cont_get_next_reg_from_end_info(msg_info); } return reg_info; } struct tfa_partial_msg_block { uint8_t offset; uint16_t change; uint8_t update[16][3]; } __packed; static enum tfa98xx_error tfa_cont_write_vstepMax2_One(int dev_idx, struct tfa_volume_step_message_info *new_msg, struct tfa_volume_step_message_info *old_msg, int enable_partial_update) { enum tfa98xx_error err = TFA98XX_ERROR_OK; int len = (tfa_cont_get_msg_len(new_msg) - 1) * 3; char *buf = (char *)new_msg->parameter_data; uint8_t *partial = NULL; int partial_size = 0; #if defined(TFADSP_DSP_BUFFER_POOL) int buffer_p_index = -1, partial_p_index = -1; #endif uint8_t cmdid[3]; int use_partial_coeff = 0; if (enable_partial_update) { if (new_msg->message_type != old_msg->message_type) { pr_debug("Message type differ - Disable Partial Update\n"); enable_partial_update = 0; } else if (tfa_cont_get_msg_len(new_msg) != tfa_cont_get_msg_len(old_msg)) { pr_debug("Message Length differ - Disable Partial Update\n"); enable_partial_update = 0; } } if ((enable_partial_update) && (new_msg->message_type == 1)) { /* No patial updates for message type 1 (Coefficients) */ enable_partial_update = 0; if ((tfa98xx_dev_revision(dev_idx) & 0xff) == 0x88) use_partial_coeff = 1; } /* Change Message Len to the actual buffer len */ memcpy(cmdid, new_msg->cmd_id, sizeof(cmdid)); /* The algoparams and mbdrc msg id will be changed * to the reset type when SBSL=0 * if SBSL=1 the msg will remain unchanged. * It's up to the tuning engineer to choose the 'without_reset' * types inside the vstep. * In other words: the reset msg is applied during SBSL==0 * else it remains unchanged. */ pr_info("%s: is_cold %d\n", __func__, handles_local[dev_idx].is_cold); /* if (TFA_GET_BF(dev_idx, SBSL) == 0) { */ if (handles_local[dev_idx].is_cold == 1) { uint8_t org_cmd = cmdid[2]; if ((new_msg->message_type == 0) && (cmdid[2] != SB_PARAM_SET_ALGO_PARAMS)) /* SB_PARAM_SET_ALGO_PARAMS_WITHOUT_RESET */ { pr_debug("P-ID for SetAlgoParams modified! cmdid[2]=0x%2x (to 0x00)\n", cmdid[2]); cmdid[2] = SB_PARAM_SET_ALGO_PARAMS; } else if ((new_msg->message_type == 2) && (cmdid[2] != SB_PARAM_SET_MBDRC)) /* SB_PARAM_SET_MBDRC_WITHOUT_RESET */ { pr_debug("P-ID for SetMBDrc modified! cmdid[2]=0x%2x (to 0x07)\n", cmdid[2]); cmdid[2] = SB_PARAM_SET_MBDRC; } if (org_cmd != cmdid[2]) pr_info("P-ID: cmdid[2]=0x%02x to 0x%02x\n", org_cmd, cmdid[2]); } /* * +sizeof(struct tfa_partial_msg_block) will allow to fit one * additonnal partial block If the partial update goes over the len of * a regular message ,we can safely write our block and check afterward * that we are over the size of a usual update */ if (enable_partial_update) { partial_size = (sizeof(uint8_t) * len) + sizeof(struct tfa_partial_msg_block); #if defined(TFADSP_DSP_BUFFER_POOL) partial_p_index = tfa98xx_buffer_pool_access (dev_idx, -1, partial_size, POOL_GET); if (partial_p_index != -1) { pr_debug("%s: allocated from buffer_pool[%d] for %d bytes\n", __func__, partial_p_index, partial_size); partial = (uint8_t *) (handles_local[dev_idx] .buf_pool[partial_p_index].pool); } else { partial = kmalloc(partial_size, GFP_KERNEL); } #else partial = kmalloc(partial_size, GFP_KERNEL); #endif /* TFADSP_DSP_BUFFER_POOL */ } if (partial) { uint8_t offset = 0, i = 0; uint16_t *change; uint8_t *n = new_msg->parameter_data; uint8_t *o = old_msg->parameter_data; uint8_t *p = partial; uint8_t *trim = partial; /* set dspFiltersReset */ *p++ = 0x02; *p++ = 0x00; *p++ = 0x00; while ((o < (old_msg->parameter_data + len)) && (p < (partial + len - 3))) { if ((offset == 0xff) || (memcmp(n, o, 3 * sizeof(uint8_t)))) { *p++ = offset; change = (uint16_t *)p; *change = 0; p += 2; for (i = 0; (i < 16) && (o < (old_msg->parameter_data + len)); i++, n += 3, o += 3) { if (memcmp(n, o, 3 * sizeof(uint8_t))) { *change |= BIT(i); memcpy(p, n, 3); p += 3; trim = p; } } offset = 0; *change = cpu_to_be16(*change); } else { n += 3; o += 3; offset++; } } if (trim == partial) { pr_debug("No Change in message - discarding %d bytes\n", len); len = 0; } else if (trim < (partial + len - 3)) { pr_debug("Using partial update: %d -> %d bytes\n", len, (int)(trim-partial + 3)); /* Add the termination marker */ memset(trim, 0x00, 3); trim += 3; /* Signal This will be a partial update */ cmdid[2] |= BIT(6); buf = (char *)partial; len = (int)(trim - partial); } else { pr_debug("Partial too big - use regular update\n"); } } else { if (!enable_partial_update) pr_debug("Partial update - Not enabled\n"); else /* partial == NULL */ pr_err("Partial update memory error - Disabling\n"); } if (use_partial_coeff) { err = dsp_partial_coefficients (dev_idx, old_msg->parameter_data, new_msg->parameter_data); } else if (len) { uint8_t *buffer; pr_debug("Command-ID used: 0x%02x%02x%02x\n", cmdid[0], cmdid[1], cmdid[2]); #if defined(TFADSP_DSP_BUFFER_POOL) buffer_p_index = tfa98xx_buffer_pool_access (dev_idx, -1, 3 + len, POOL_GET); if (buffer_p_index != -1) { pr_debug("%s: allocated from buffer_pool[%d] for %d bytes\n", __func__, buffer_p_index, 3 + len); buffer = (char *)(handles_local[dev_idx] .buf_pool[buffer_p_index].pool); } else { buffer = kmalloc(3 + len, GFP_KERNEL); if (buffer == NULL) goto tfa_cont_write_vstepMax2_One_error_exit; } #else buffer = kmalloc(3 + len, GFP_KERNEL); if (buffer == NULL) goto tfa_cont_write_vstepMax2_One_error_exit; #endif /* TFADSP_DSP_BUFFER_POOL */ memcpy(&buffer[0], cmdid, 3); memcpy(&buffer[3], buf, len); err = dsp_msg(dev_idx, 3 + len, (char *)buffer); #if defined(TFADSP_DSP_BUFFER_POOL) if (buffer_p_index != -1) { tfa98xx_buffer_pool_access (dev_idx, buffer_p_index, 0, POOL_RETURN); } else { kfree(buffer); } #else kfree(buffer); #endif /* TFADSP_DSP_BUFFER_POOL */ } tfa_cont_write_vstepMax2_One_error_exit: #if defined(TFADSP_DSP_BUFFER_POOL) if (partial_p_index != -1) { tfa98xx_buffer_pool_access (dev_idx, partial_p_index, 0, POOL_RETURN); } else { kfree(partial); } #else kfree(partial); #endif /* TFADSP_DSP_BUFFER_POOL */ return err; } static enum tfa98xx_error tfa_cont_write_vstepMax2(int dev_idx, struct tfa_volume_step_max2_file *vp, int vstep_idx, int vstep_msg_idx) { enum tfa98xx_error err = TFA98XX_ERROR_OK; static struct tfa_volume_step_register_info *p_reg_info; struct tfa_volume_step_register_info *reg_info = NULL; struct tfa_volume_step_message_info *msg_info = NULL, *p_msg_info = NULL; struct tfa_bitfield bit_f; int i, nr_messages, enp = partial_enable; if (vstep_idx >= vp->nr_of_vsteps) { pr_debug("Volumestep %d is not available\n", vstep_idx); return TFA98XX_ERROR_BAD_PARAMETER; } if (p_reg_info == NULL) { pr_debug("Inital vstep write\n"); enp = 0; } reg_info = tfa_cont_get_reg_for_vstep(vp, vstep_idx); msg_info = tfa_cont_get_msg_info_from_reg(reg_info); nr_messages = msg_info->nr_of_messages; if (enp) { p_msg_info = tfa_cont_get_msg_info_from_reg(p_reg_info); if (nr_messages != p_msg_info->nr_of_messages) { pr_debug("Message different - Disable partial update\n"); enp = 0; } } for (i = 0; i < nr_messages; i++) { /* Messagetype(3) is Smartstudio Info! Dont send this! */ if (msg_info->message_type == 3) { pr_debug("Skipping Message Type 3\n"); /* message_length is in bytes */ msg_info = tfa_cont_get_next_msg_info(msg_info); if (enp) p_msg_info = tfa_cont_get_next_msg_info (p_msg_info); continue; } /* If no vstepMsgIndex is passed on, * all message needs to be send */ if ((vstep_msg_idx >= TFA_MAX_VSTEP_MSG_MARKER) || (vstep_msg_idx == i)) { err = tfa_cont_write_vstepMax2_One (dev_idx, msg_info, p_msg_info, enp); if (err != TFA98XX_ERROR_OK) { /* * Force a full update for the next write * As the current status of the DSP is unknown */ p_reg_info = NULL; return err; } } msg_info = tfa_cont_get_next_msg_info(msg_info); if (enp) p_msg_info = tfa_cont_get_next_msg_info(p_msg_info); } p_reg_info = reg_info; for (i = 0; i < reg_info->nr_of_registers * 2; i++) { /* Byte swap the datasheetname */ bit_f.field = (uint16_t)(reg_info->register_info[i] >> 8) | (reg_info->register_info[i] << 8); i++; bit_f.value = (uint16_t)reg_info->register_info[i] >> 8; err = tfa_run_write_bitfield(dev_idx, bit_f); 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 tfa_cont_write_drc_file(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 cmd_id + payload to dsp */ err = 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 tfa_cont_write_file(int dev_idx, struct tfa_file_dsc *file, int vstep_idx, int vstep_msg_idx) { enum tfa98xx_error err = TFA98XX_ERROR_OK; struct tfa_header *hdr = (struct tfa_header *)file->data; char *data_buf; enum tfa_header_type type; int size, temp_index; if (tfa98xx_cnt_verbose) tfa_cont_show_header(hdr); type = (enum tfa_header_type) hdr->id; switch (type) { case msg_hdr: /* generic DSP message */ size = hdr->size - sizeof(struct tfa_msg_file); /* write temp stored in driver */ data_buf = (char *)((struct tfa_msg_file *)hdr)->data; if (data_buf[1] == 0x80 && data_buf[2] == 0x14 && handles_local[dev_idx].temp != 0xffff) { temp_index = (1 + 2 + dev_idx) * 3; pr_info("%s: temp in msg 0x%02x%02x%02x", __func__, data_buf[temp_index], data_buf[temp_index + 1], data_buf[temp_index + 2]); data_buf[temp_index] = (char)((handles_local[dev_idx].temp & 0xff0000) >> 16); data_buf[temp_index + 1] = (char)((handles_local[dev_idx].temp & 0x00ff00) >> 8); data_buf[temp_index + 2] = (char)(handles_local[dev_idx].temp & 0x0000ff); pr_info("%s: temp from driver 0x%02x%02x%02x", __func__, data_buf[temp_index], data_buf[temp_index + 1], data_buf[temp_index + 2]); } err = dsp_msg(dev_idx, size, (const char *)((struct tfa_msg_file *)hdr)->data); break; case volstep_hdr: if (tfa98xx_dev_family(dev_idx) == 2) err = tfa_cont_write_vstepMax2 (dev_idx, (struct tfa_volume_step_max2_file *)hdr, vstep_idx, vstep_msg_idx); else err = tfa_cont_write_vstep (dev_idx, (struct tfa_volume_step2_file *)hdr, vstep_idx); /* If writing the vstep was succesfull, set new current vstep */ if (err == TFA98XX_ERROR_OK) tfa_cont_set_current_vstep(dev_idx, vstep_idx); break; case speaker_hdr: if (tfa98xx_dev_family(dev_idx) == 2) { /* Remove header and xml_id */ size = hdr->size - sizeof(struct tfa_spk_header) - sizeof(struct tfa_fw_ver); err = dsp_msg (dev_idx, size, (const char *) (((struct tfa_speaker_file *)hdr)->data + (sizeof(struct tfa_fw_ver)))); } else { size = hdr->size - sizeof(struct tfa_speaker_file); err = tfa98xx_dsp_write_speaker_parameters (dev_idx, size, (const unsigned char *) ((struct tfa_speaker_file *)hdr)->data); } break; case preset_hdr: size = hdr->size - sizeof(struct tfa_preset_file); err = tfa98xx_dsp_write_preset (dev_idx, size, (const unsigned char *) ((struct tfa_preset_file *)hdr)->data); break; case equalizer_hdr: err = tfa_cont_write_filterbank (dev_idx, ((struct tfa_equalizer_file *)hdr)->filter); break; case patch_hdr: size = hdr->size - sizeof(struct tfa_patch_file); /* total length */ err = tfa_dsp_patch(dev_idx, size, (const unsigned char *) ((struct tfa_patch_file *)hdr)->data); break; case config_hdr: size = hdr->size - sizeof(struct tfa_config_file); err = tfa98xx_dsp_write_config (dev_idx, size, (const unsigned char *) ((struct tfa_config_file *)hdr)->data); break; case drc_hdr: if (hdr->version[0] == NXPTFA_DR3_VERSION) { /* Size is total size - hdrsize(36) - xmlversion(3) */ size = hdr->size - sizeof(struct tfa_drc_file2); err = tfa_cont_write_drc_file (dev_idx, size, ((struct tfa_drc_file2 *)hdr)->data); } else { /* * The DRC file is split as: * 36 bytes for generic header * (customer, application, and type) * 127x3 (381) bytes first block contains * the device and sample rate * independent settings * 127x3 (381) bytes block * the device and sample rate * specific values. * The second block can always be recalculated * from the first block, * if vlsCal and the sample rate are known. */ /* = hdr->size - sizeof(struct tfa_drc_file); */ size = 381; /* fixed size for first block */ /* 381 is done to only send 2nd part of the drc block */ err = tfa98xx_dsp_write_drc (dev_idx, size, ((const unsigned char *) ((struct tfa_drc_file *)hdr)->data+381)); } break; case info_hdr: /* Ignore */ break; default: pr_err("Header is of unknown type: 0x%x\n", type); return TFA98XX_ERROR_BAD_PARAMETER; } return err; } /** * get the 1st of this dsc type this devicelist */ struct tfa_desc_ptr * tfa_cnt_get_dsc(struct tfa_container *cnt, enum tfa_descriptor_type type, int dev_idx) { struct tfa_device_list *dev = tfa_cont_device(dev_idx); struct tfa_desc_ptr *this; int i; if (!dev) return NULL; /* process the list until a the type is encountered */ for (i = 0; i < dev->length; i++) if (dev->list[i].type == (uint32_t)type) { this = (struct tfa_desc_ptr *) (dev->list[i].offset+(uint8_t *)cnt); return this; } return NULL; } /** * get the device type from the patch in this devicelist * - find the patch file for this devidx * - return the devid from the patch or 0 if not found */ int tfa_cont_get_devid(struct tfa_container *cnt, int dev_idx) { struct tfa_patch_file *patchfile; struct tfa_desc_ptr *patchdsc; uint8_t *patchheader; unsigned short devid, checkaddress; int checkvalue; patchdsc = tfa_cnt_get_dsc(cnt, dsc_patch, dev_idx); patchdsc += 2; /* first the filename dsc and filesize, so skip them */ patchfile = (struct tfa_patch_file *)patchdsc; patchheader = patchfile->data; checkaddress = (patchheader[1] << 8) + patchheader[2]; checkvalue = (patchheader[3] << 16) + (patchheader[4] << 8) + patchheader[5]; devid = patchheader[0]; if (checkaddress == 0xFFFF && checkvalue != 0xFFFFFF && checkvalue != 0) { devid = patchheader[5] << 8 | patchheader[0]; /* full revid */ } return devid; } /* * get the slave for the device if it exists */ enum tfa98xx_error tfa_cont_get_slave(int dev_idx, uint8_t *slave_addr) { struct tfa_device_list *dev = tfa_cont_device(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 tfa_run_write_bitfield(tfa98xx_handle_t dev_idx, struct tfa_bitfield bf) { enum tfa98xx_error error; uint16_t value; union { uint16_t field; struct tfa_bf_enum bf_enum; } bf_uni; value = bf.value; bf_uni.field = bf.field; /* print all the bitfield writing */ if (tfa98xx_cnt_verbose) pr_debug("bitfield: %s=0x%x (0x%x[%d..%d]=0x%x)\n", tfa_cont_bf_name (bf_uni.field, tfa98xx_dev_revision(dev_idx)), value, bf_uni.bf_enum.address, bf_uni.bf_enum.pos, bf_uni.bf_enum.pos+bf_uni.bf_enum.len, value); error = tfa_set_bf(dev_idx, bf_uni.field, value); return error; } /* * read a bit field */ enum tfa98xx_error tfa_run_read_bitfield(tfa98xx_handle_t dev_idx, struct tfa_bitfield *bf) { enum tfa98xx_error error; union { uint16_t field; struct tfa_bf_enum bf_enum; } bf_uni; uint16_t regvalue, msk; bf_uni.field = bf->field; error = reg_read (dev_idx, (unsigned char)(bf_uni.bf_enum.address), ®value); if (error) return error; msk = ((1<<(bf_uni.bf_enum.len+1))-1) << bf_uni.bf_enum.pos; regvalue &= msk; bf->value = regvalue >> bf_uni.bf_enum.pos; return error; } /* * dsp mem direct write */ enum tfa98xx_error tfa_run_write_dsp_mem(tfa98xx_handle_t dev, struct tfa_dsp_mem *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 = mem_write (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 tfa_run_write_filter(tfa98xx_handle_t dev, union tfa_cont_biquad *bq) { enum tfa98xx_error error = TFA98XX_ERROR_OK; enum tfa98xx_dmem dmem; uint16_t address; uint8_t data[3*3+sizeof(bq->aa.bytes)]; int i, channel = 0, runs = 1; int8_t saved_index = bq->aa.index; /* This is used to set back index */ /* Channel=1 is primary, Channel=2 is secondary*/ if (bq->aa.index > 100) { bq->aa.index -= 100; channel = 2; } else { if (bq->aa.index > 50) { bq->aa.index -= 50; channel = 1; } else if (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 == TFA98XX_DMEM_ERR) { pr_debug("Warning: No memory location found to write filter settings! Filter settings are skipped!\n"); /* Don't exit with an error here, * We could continue without problems */ return TFA98XX_ERROR_OK; } /* send a DSP memory message * that targets the devices specific memory for the filter * msg params: which_mem, start_offset, num_words */ memset(data, 0, 3*3); data[2] = dmem; /* output[0] = which_mem */ data[4] = address >> 8; /* output[1] = start_offset */ data[5] = address & 0xff; data[8] = sizeof(bq->aa.bytes)/3; /*output[2] = num_words */ /* payload */ memcpy(&data[9], bq->aa.bytes, sizeof(bq->aa.bytes)); if (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); } } #if defined(FLOAT_COMPATIBLE) /* floating-point error from cross-complier compatiblity */ if (tfa98xx_cnt_verbose) { char buf[50]; if (bq->aa.index == 13) { snprintf(buf, 50, "%d,%.0f,%.2f", bq->in.type, bq->in.cut_off_freq, bq->in.leakage); } else if (bq->aa.index >= 10 && bq->aa.index <= 12) { snprintf(buf, 50, "%d,%.0f,%.1f,%.1f", bq->aa.type, bq->aa.cut_off_freq, bq->aa.ripple_db, bq->aa.rolloff); } else { strlcpy(buf, "unsupported filter index", 50); } pr_debug("=%s\n", buf); } #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 tfa_run_write_register(tfa98xx_handle_t handle, struct tfa_reg_patch *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 = reg_read(handle, reg->address, &value); if (error) return error; value &= ~reg->mask; newvalue = reg->value & reg->mask; value |= newvalue; error = reg_write(handle, reg->address, value); return error; } /* * return the bitfield */ struct tfa_bitfield tfa_cont_dsc2bf(struct tfa_desc_ptr dsc) { uint32_t *ptr = (uint32_t *) (&dsc); union { struct tfa_bitfield 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 tfa_cont_write_regs_dev(int dev_idx) { struct tfa_device_list *dev = tfa_cont_device(dev_idx); struct tfa_bitfield *bit_f; int i; enum tfa98xx_error err = TFA98XX_ERROR_OK; if (!dev) return TFA98XX_ERROR_BAD_PARAMETER; /* process the list until a patch, file of profile is encountered */ for (i = 0; i < dev->length; i++) { if (dev->list[i].type == dsc_patch || dev->list[i].type == dsc_file || dev->list[i].type == dsc_profile) break; if (dev->list[i].type == dsc_bit_field) { bit_f = (struct tfa_bitfield *) (dev->list[i].offset+(uint8_t *)g_cont); err = tfa_run_write_bitfield(dev_idx, *bit_f); } if (dev->list[i].type == dsc_register) err = tfa_run_write_register (dev_idx, (struct tfa_reg_patch *) (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 tfa_cont_write_regs_prof(int dev_idx, int prof_idx) { struct tfa_profile_list *prof = tfa_cont_profile(dev_idx, prof_idx); struct tfa_bitfield *bitf; unsigned int i; enum tfa98xx_error err = TFA98XX_ERROR_OK; if (!prof) return TFA98XX_ERROR_BAD_PARAMETER; if (tfa98xx_cnt_verbose) pr_debug("----- profile: %s (%d) -----\n", tfa_cont_get_string(&prof->name), prof_idx); /* process the list until the end of profile or the default section */ for (i = 0; i < prof->length; i++) { /* write values before default section when we switch profile */ if (prof->list[i].type == dsc_default) break; if (prof->list[i].type == dsc_bit_field) { bitf = (struct tfa_bitfield *) (prof->list[i].offset+(uint8_t *)g_cont); err = tfa_run_write_bitfield(dev_idx, *bitf); } if (prof->list[i].type == dsc_register) err = tfa_run_write_register (dev_idx, (struct tfa_reg_patch *) (prof->list[i].offset+(char *)g_cont)); if (err) break; } return err; } /* write patchfile in the devicelist to the target */ enum tfa98xx_error tfa_cont_write_patch(int dev_idx) { enum tfa98xx_error err = TFA98XX_ERROR_OK; struct tfa_device_list *dev = tfa_cont_device(dev_idx); struct tfa_file_dsc *file; struct tfa_patch_file *patchfile; int size, i; if (!dev) return TFA98XX_ERROR_BAD_PARAMETER; /* process the list until a patch is encountered */ for (i = 0; i < dev->length; i++) if (dev->list[i].type == dsc_patch) { file = (struct tfa_file_dsc *) (dev->list[i].offset+(uint8_t *)g_cont); patchfile = (struct tfa_patch_file *)&file->data; if (tfa98xx_cnt_verbose) tfa_cont_show_header(&patchfile->hdr); size = patchfile->hdr.size - sizeof(struct tfa_patch_file); /* size is total length */ err = tfa_dsp_patch (dev_idx, size, (const unsigned char *)patchfile->data); if (err) return err; } return TFA98XX_ERROR_OK; } /* write all param files in the devicelist to the target */ enum tfa98xx_error tfa_cont_write_files(int dev_idx) { struct tfa_device_list *dev = tfa_cont_device(dev_idx); struct tfa_file_dsc *file; struct tfa_cmd *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 == dsc_file) { file = (struct tfa_file_dsc *) (dev->list[i].offset+(uint8_t *)g_cont); if (tfa_cont_write_file (dev_idx, file, 0, TFA_MAX_VSTEP_MSG_MARKER)) { return TFA98XX_ERROR_BAD_PARAMETER; } } if (dev->list[i].type == dsc_set_input_select || dev->list[i].type == dsc_set_output_select || dev->list[i].type == dsc_set_program_config || dev->list[i].type == dsc_set_lag_w || dev->list[i].type == dsc_set_gains || dev->list[i].type == dsc_set_vbat_factors || dev->list[i].type == dsc_set_senses_cal || dev->list[i].type == dsc_set_senses_delay || dev->list[i].type == dsc_set_mb_drc) { create_dsp_buffer_msg ((struct tfa_msg *) (dev->list[i].offset + (char *)g_cont), buffer, &size); if (tfa98xx_cnt_verbose) { pr_debug("command: %s=0x%02x%02x%02x\n", tfa_cont_get_command_string (dev->list[i].type), (unsigned char)buffer[0], (unsigned char)buffer[1], (unsigned char)buffer[2]); } err = dsp_msg(dev_idx, size, buffer); } if (dev->list[i].type == dsc_cmd) { size = *(uint16_t *) (dev->list[i].offset + (char *)g_cont); err = dsp_msg (dev_idx, size, dev->list[i].offset+2 + (char *)g_cont); if (tfa98xx_cnt_verbose) { cmd = (struct tfa_cmd *) (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 == dsc_cf_mem) err = tfa_run_write_dsp_mem (dev_idx, (struct tfa_dsp_mem *) (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 tfa_cont_write_files_prof(int dev_idx, int prof_idx, int vstep_idx) { enum tfa98xx_error err = TFA98XX_ERROR_OK; struct tfa_profile_list *prof = tfa_cont_profile(dev_idx, prof_idx); char buffer[(MEMTRACK_MAX_WORDS * 3) + 3] = {0}; /* every word requires 3 bytes, and 3 is the msg */ unsigned int i; struct tfa_file_dsc *file; struct tfa_patch_file *patchfile; int size; if (!prof) return TFA98XX_ERROR_BAD_PARAMETER; /* process the list and write all files */ for (i = 0; i < prof->length; i++) { switch (prof->list[i].type) { case dsc_file: file = (struct tfa_file_dsc *) (prof->list[i].offset + (uint8_t *)g_cont); err = tfa_cont_write_file (dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER); break; case dsc_patch: file = (struct tfa_file_dsc *) (prof->list[i].offset + (uint8_t *)g_cont); patchfile = (struct tfa_patch_file *) &file->data; if (tfa98xx_cnt_verbose) tfa_cont_show_header(&patchfile->hdr); size = patchfile->hdr.size - sizeof(struct tfa_patch_file); /* size is total length */ err = tfa_dsp_patch (dev_idx, size, (const unsigned char *) patchfile->data); break; case dsc_cf_mem: err = tfa_run_write_dsp_mem (dev_idx, (struct tfa_dsp_mem *) (prof->list[i].offset + (uint8_t *)g_cont)); break; case dsc_set_input_select: case dsc_set_output_select: case dsc_set_program_config: case dsc_set_lag_w: case dsc_set_gains: case dsc_set_vbat_factors: case dsc_set_senses_cal: case dsc_set_senses_delay: case dsc_set_mb_drc: create_dsp_buffer_msg ((struct tfa_msg *) (prof->list[i].offset + (uint8_t *)g_cont), buffer, &size); if (tfa98xx_cnt_verbose) { pr_debug("command: %s=0x%02x%02x%02x\n", tfa_cont_get_command_string (prof->list[i].type), (unsigned char)buffer[0], (unsigned char)buffer[1], (unsigned char)buffer[2]); } err = dsp_msg(dev_idx, size, buffer); break; default: /* ignore any other type */ break; } } return err; } enum tfa98xx_error tfa_cont_write_item(int dev_idx, struct tfa_desc_ptr *dsc) { enum tfa98xx_error err = TFA98XX_ERROR_OK; /* struct tfa_file_dsc *file; */ struct tfa_reg_patch *reg; struct tfa_mode *cas; struct tfa_bitfield *bitf; switch (dsc->type) { case dsc_default: case dsc_device: /* ignore */ case dsc_profile: /* profile list */ break; case dsc_register: /* register patch */ reg = (struct tfa_reg_patch *)(dsc->offset+(uint8_t *)g_cont); return tfa_run_write_register(dev_idx, reg); /* pr_debug("$0x%2x=0x%02x,0x%02x\n", * reg->address, reg->mask, reg->value); */ break; case dsc_string: /* ascii: zero terminated string */ pr_debug(";string: %s\n", tfa_cont_get_string(dsc)); break; case dsc_file: /* filename + file contents */ case dsc_patch: break; case dsc_mode: cas = (struct tfa_mode *)(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 dsc_cf_mem: err = tfa_run_write_dsp_mem (dev_idx, (struct tfa_dsp_mem *) (dsc->offset+(uint8_t *)g_cont)); break; case dsc_bit_field: bitf = (struct tfa_bitfield *)(dsc->offset+(uint8_t *)g_cont); return tfa_run_write_bitfield(dev_idx, *bitf); case dsc_filter: return tfa_run_write_filter (dev_idx, (union tfa_cont_biquad *) (dsc->offset+(uint8_t *)g_cont)); } 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; struct tfa_profile_list *prof = tfa_cont_profile(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", tfa_cont_get_string(&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; msleep_interruptible(10); } if (i == 0) { if (tfa98xx_cnt_verbose) pr_err("Unable to write filters, CLKS=0\n"); return TFA98XX_ERROR_STATE_TIMED_OUT; } /* process the list until the end of profile or default section */ for (i = 0; i < prof->length; i++) if (prof->list[i].type == dsc_filter) if (tfa_cont_write_item(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) { struct tfa_bitfield *bitf; unsigned int i; struct tfa_device_list *dev; struct tfa_profile_list *prof; int fs_profile = -1; dev = tfa_cont_device(dev_idx); if (!dev) return 0; prof = tfa_cont_profile(dev_idx, prof_idx); if (!prof) return 0; /* Check profile fields first */ for (i = 0; i < prof->length; i++) { if (prof->list[i].type == dsc_default) break; /* check for profile settingd (AUDFS) */ if (prof->list[i].type == dsc_bit_field) { bitf = (struct tfa_bitfield *) (prof->list[i].offset+(uint8_t *)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", __func__, 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 == dsc_patch || dev->list[i].type == dsc_file || dev->list[i].type == dsc_profile) break; if (dev->list[i].type == dsc_bit_field) { bitf = (struct tfa_bitfield *) (dev->list[i].offset+(uint8_t *)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", __func__, 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, struct tfa_profile_list *prof, struct tfa_profile_list *previous_prof, int fs_previous_profile) { enum tfa98xx_error err = TFA98XX_ERROR_OK; struct tfa_bitfield *bitf; unsigned int i; int fs_default_profile = 8; /* default is 48kHz */ int fs_next_profile = 8; /* default is 48kHz */ /* ---------- default settings previous profile ---------- */ for (i = 0; i < previous_prof->length; i++) { /* Search for the default section */ if (i == 0) { while (previous_prof->list[i].type != dsc_default && i < previous_prof->length) { i++; } i++; } /* Only if we found the default section search for AUDFS */ if (i < previous_prof->length) { if (previous_prof->list[i].type == dsc_bit_field) { bitf = (struct tfa_bitfield *) (previous_prof->list[i].offset + (uint8_t *)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++) { /* write the values before the default section */ if (prof->list[i].type == dsc_default) break; /* search for AUDFS */ if (prof->list[i].type == dsc_bit_field) { bitf = (struct tfa_bitfield *) (prof->list[i].offset+(uint8_t *)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 AUDFS * from the previous profile we 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 tfa_cont_write_profile(int dev_idx, int prof_idx, int vstep_idx) { enum tfa98xx_error err = TFA98XX_ERROR_OK; struct tfa_profile_list *prof = tfa_cont_profile(dev_idx, prof_idx); struct tfa_profile_list *previous_prof = tfa_cont_profile (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; struct tfa_file_dsc *file; struct tfa_cmd *cmd; uint8_t slave_address = 0; int size = 0, fs_previous_profile = 8; /* default fs is 48kHz*/ int dev_family = tfa98xx_dev_family(dev_idx); if (!prof || !previous_prof) { pr_err("Error trying to get the (previous) swprofile\n"); return TFA98XX_ERROR_BAD_PARAMETER; } if (tfa98xx_cnt_verbose) { tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n", tfa_cont_device_name(dev_idx), tfa_cont_profile_name(dev_idx, prof_idx), vstep_idx); } /* Get the slave */ err = tfa_cont_get_slave(dev_idx, &slave_address); /* We only make a power cycle when 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", tfa_cont_get_string(&prof->name), tfa_cont_get_string(&previous_prof->name)); } } else { /* mute */ tfa_run_mute(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 (((dev_family == 2) && (TFA_GET_BF(dev_idx, MANSTATE) == 9)) || (dev_family != 2)) { err = tfa98xx_powerdown(dev_idx, 1); if (err) return err; } 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", tfa_cont_get_string(&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 != dsc_default && i < previous_prof->length) i++; i++; } /* Only if we found the default section try writing the items */ if (i < previous_prof->length) if (tfa_cont_write_item (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", tfa_cont_get_string(&prof->name), prof_idx); /* set new settings */ for (i = 0; i < prof->length; i++) { /* Remember where we currently are with writing items*/ j = i; /* write values before default section when we switch profile */ /* process and write all non-file items */ switch (prof->list[i].type) { case dsc_file: case dsc_patch: case dsc_set_input_select: case dsc_set_output_select: case dsc_set_program_config: case dsc_set_lag_w: case dsc_set_gains: case dsc_set_vbat_factors: case dsc_set_senses_cal: case dsc_set_senses_delay: case dsc_set_mb_drc: case dsc_cmd: case dsc_filter: case dsc_default: /* When one of these files are found, we exit */ i = prof->length; break; default: err = tfa_cont_write_item (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 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 != dsc_default && i < previous_prof->length) { i++; } i++; } /* Only if we found default section try writing file */ if (i < previous_prof->length) { char type = previous_prof->list[i].type; if (type == dsc_file || type == dsc_patch) { /* Only write this once */ if (tfa98xx_cnt_verbose && k == 0) { pr_debug("------ files default profile: %s (%d) ----------\n", tfa_cont_get_string (&previous_prof->name), prof_idx); k++; } file = (struct tfa_file_dsc *) (previous_prof->list[i].offset + (uint8_t *)g_cont); err = tfa_cont_write_file (dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER); } } } } if (tfa98xx_cnt_verbose) { pr_debug("------ files new profile: %s (%d) --------\n", tfa_cont_get_string(&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++) { /* write values before default section when we switch profile */ if (prof->list[i].type == dsc_default) break; switch (prof->list[i].type) { case dsc_file: case dsc_patch: file = (struct tfa_file_dsc *) (prof->list[i].offset + (uint8_t *)g_cont); err = tfa_cont_write_file (dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER); break; case dsc_set_input_select: case dsc_set_output_select: case dsc_set_program_config: case dsc_set_lag_w: case dsc_set_gains: case dsc_set_vbat_factors: case dsc_set_senses_cal: case dsc_set_senses_delay: case dsc_set_mb_drc: create_dsp_buffer_msg ((struct tfa_msg *) (prof->list[i].offset + (char *)g_cont), buffer, &size); err = dsp_msg(dev_idx, size, buffer); if (tfa98xx_cnt_verbose) pr_debug("command: %s=0x%02x%02x%02x\n", tfa_cont_get_command_string (prof->list[i].type), (unsigned char)buffer[0], (unsigned char)buffer[1], (unsigned char)buffer[2]); break; case dsc_cmd: size = *(uint16_t *) (prof->list[i].offset + (char *)g_cont); err = dsp_msg (dev_idx, size, prof->list[i].offset + 2 + (char *)g_cont); if (tfa98xx_cnt_verbose) { cmd = (struct tfa_cmd *) (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: /* write bitfield, register, xmem after files */ if (tfa_cont_write_item (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) && (slave_address > 10)) { 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 tfa_cont_write_files_vstep(int dev_idx, int prof_idx, int vstep_idx) { struct tfa_profile_list *prof = tfa_cont_profile(dev_idx, prof_idx); unsigned int i; struct tfa_file_dsc *file; struct tfa_header *hdr; enum tfa_header_type type; enum tfa98xx_error err = TFA98XX_ERROR_OK; if (!prof) return TFA98XX_ERROR_BAD_PARAMETER; if (tfa98xx_cnt_verbose) tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n", tfa_cont_device_name(dev_idx), tfa_cont_profile_name(dev_idx, prof_idx), vstep_idx); /* write vstep file only! */ for (i = 0; i < prof->length; i++) { if (prof->list[i].type == dsc_file) { file = (struct tfa_file_dsc *) (prof->list[i].offset + (uint8_t *)g_cont); hdr = (struct tfa_header *)file->data; type = (enum tfa_header_type) hdr->id; switch (type) { case volstep_hdr: if (tfa_cont_write_file (dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER)) return TFA98XX_ERROR_BAD_PARAMETER; break; default: break; } } } return err; } char *tfa_cont_get_string(struct tfa_desc_ptr *dsc) { if (dsc->type != dsc_string) return UNDEF_STRING; 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) */ mem_read(handle, 529, 1, &value_p); mem_read(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 *tfa_cont_get_command_string(uint32_t type) { if (type == dsc_set_input_select) return "SetInputSelector"; else if (type == dsc_set_output_select) return "SetOutputSelector"; else if (type == dsc_set_program_config) return "SetProgramConfig"; else if (type == dsc_set_lag_w) return "SetLagW"; else if (type == dsc_set_gains) return "SetGains"; else if (type == dsc_set_vbat_factors) return "SetvBatFactors"; else if (type == dsc_set_senses_cal) return "SetSensesCal"; else if (type == dsc_set_senses_delay) return "SetSensesDelay"; else if (type == dsc_set_mb_drc) return "SetMBDrc"; else if (type == dsc_filter) return "filter"; else return UNDEF_STRING; } /* * Get the name of the device at a certain index in the container file * return device name */ char *tfa_cont_device_name(int dev_idx) { struct tfa_device_list *dev; if (dev_idx >= tfa98xx_cnt_max_device()) return ERROR_STRING; dev = tfa_cont_device(dev_idx); if (dev == NULL) return ERROR_STRING; return tfa_cont_get_string(&dev->name); } /* * Get the application name from the container file application field * note that the input stringbuffer should be sizeof(application field)+1 * */ int tfa_cont_get_app_name(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 tfa_cont_get_cal_profile(int dev_idx) { int prof, nprof, cal_idx = -2; if ((dev_idx < 0) || (dev_idx >= tfa98xx_cnt_max_device())) return TFA_ERROR; nprof = tfa_cont_max_profile(dev_idx); /* search for the calibration profile in the list of profiles */ for (prof = 0; prof < nprof; prof++) { if (strnstr(tfa_cont_profile_name(dev_idx, prof), ".cal", strlen(tfa_cont_profile_name(dev_idx, prof))) != NULL) { cal_idx = prof; pr_debug("Using calibration profile: '%s'\n", tfa_cont_profile_name(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 tfa_cont_is_tap_profile(int dev_idx, int prof_idx) { if ((dev_idx < 0) || (dev_idx >= tfa98xx_cnt_max_device())) return TFA_ERROR; /* Check if next profile is tap profile */ if (strnstr(tfa_cont_profile_name(dev_idx, prof_idx), ".tap", strlen(tfa_cont_profile_name(dev_idx, prof_idx))) != NULL) { pr_debug("Using Tap profile: '%s'\n", tfa_cont_profile_name(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 *tfa_cont_profile_name(int dev_idx, int prof_idx) { struct tfa_profile_list *prof; if ((dev_idx < 0) || (dev_idx >= tfa98xx_cnt_max_device())) return ERROR_STRING; if ((prof_idx < 0) || (prof_idx >= tfa_cont_max_profile(dev_idx))) return NONE_STRING; /* the Nth profiles for this device */ prof = tfa_cont_get_dev_prof_list(g_cont, dev_idx, prof_idx); return tfa_cont_get_string(&prof->name); } /* * return 1st profile list */ struct tfa_profile_list * tfa_cont_get_1st_prof_list(struct tfa_container *cont) { struct tfa_profile_list *prof; uint8_t *b = (uint8_t *) cont; int maxdev = 0; struct tfa_device_list *dev; /* get nr of devlists */ maxdev = cont->ndev; /* get last devlist */ dev = tfa_cont_get_dev_list(cont, maxdev - 1); if (dev == NULL) return NULL; /* the 1st profile starts after the last device list */ b = (uint8_t *) dev + sizeof(struct tfa_device_list) + dev->length * (sizeof(struct tfa_desc_ptr)); prof = (struct tfa_profile_list *) b; return prof; } /* * return 1st livedata list */ struct tfa_livedata_list * tfa_cont_get_1st_livedata_list(struct tfa_container *cont) { struct tfa_livedata_list *ldata; struct tfa_profile_list *prof; struct tfa_device_list *dev; uint8_t *b = (uint8_t *) cont; int maxdev, maxprof; /* get nr of devlists+1 */ maxdev = cont->ndev; /* get nr of proflists */ maxprof = cont->nprof; /* get last devlist */ dev = tfa_cont_get_dev_list(cont, maxdev - 1); if (dev == NULL) return NULL; /* the 1st livedata starts after the last device list */ b = (uint8_t *) dev + sizeof(struct tfa_device_list) + dev->length * (sizeof(struct tfa_desc_ptr)); while (maxprof != 0) { /* get last proflist */ prof = (struct tfa_profile_list *) b; b += sizeof(struct tfa_profile_list) + ((prof->length-1) * (sizeof(struct tfa_desc_ptr))); maxprof--; } /* Else the marker falls off */ b += 4; /* bytes */ ldata = (struct tfa_livedata_list *) b; return ldata; } enum tfa98xx_error tfa_cont_open(int dev_idx) { return tfa98xx_open((tfa98xx_handle_t)dev_idx); } enum tfa98xx_error tfa_cont_close(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 < TFACONT_MAXDEVS) ? g_cont->ndev : TFACONT_MAXDEVS) : 0; } EXPORT_SYMBOL(tfa98xx_cnt_max_device); /* * 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] == NULL) continue; if (g_dev[idx]->dev == slave_addr) return idx; } return TFA_ERROR; } /* * 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 */ struct tfa_device_list *tfa_cont_device(int dev_idx) { if (dev_idx < g_devs) { if (g_dev[dev_idx] == NULL) return NULL; return g_dev[dev_idx]; } /* pr_err("Devlist index too high:%d!", idx); */ return NULL; } /* * return the per device profile count */ int tfa_cont_max_profile(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 */ struct tfa_profile_list *tfa_cont_next_profile(struct tfa_profile_list *prof) { uint8_t *this, *next; /* byte pointers for byte pointer arithmetic */ struct tfa_profile_list *nextprof; int listlength; /* total length of list in bytes */ if (prof == NULL) return NULL; if (prof->id != TFA_PROFID) return NULL; /* invalid input */ this = (uint8_t *)prof; /* nr of items in the list, length includes name dsc so - 1*/ listlength = (prof->length - 1)*sizeof(struct tfa_desc_ptr); /* the sizeof(struct tfa_profile_list) includes the list[0] length */ next = this + listlength + sizeof(struct tfa_profile_list); /* - sizeof(struct tfa_desc_ptr); */ nextprof = (struct tfa_profile_list *)next; if (nextprof->id != TFA_PROFID) return NULL; return nextprof; } /* * return the next livedata */ struct tfa_livedata_list * tfa_cont_next_livedata(struct tfa_livedata_list *livedata) { struct tfa_livedata_list *nextlivedata = (struct tfa_livedata_list *) ((char *)livedata + (livedata->length * 4) + sizeof(struct tfa_livedata_list) - 4); if (nextlivedata->id == TFA_LIVEDATAID) return nextlivedata; return NULL; } /* * return the device list pointer */ struct tfa_profile_list *tfa_cont_profile(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 non zero value on error */ int tfa_cont_crc_check_container(struct tfa_container *cont) { uint8_t *base; size_t size; uint32_t crc; base = (uint8_t *)&cont->crc + 4; /* ptr to bytes following the CRC field */ size = (size_t)(cont->size - (base - (uint8_t *)cont)); /* nr of bytes following the CRC field */ crc = ~crc32_le(~0u, base, size); return crc != cont->crc; } /** * Create a buffer which can be used to send to the dsp. */ void create_dsp_buffer_msg(struct tfa_msg *msg, char *buffer, int *size) { int i, j = 0; /* Copy cmd_id. Remember that the cmd_id is reversed */ buffer[0] = msg->cmd_id[2]; buffer[1] = msg->cmd_id[1]; buffer[2] = msg->cmd_id[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]) { struct tfa_features *features; int i; struct tfa_device_list *dev = tfa_cont_device(dev_idx); /* Init values in case no keyword is defined in cnt file: */ *hw_feature_register = -1; sw_feature_register[0] = -1; sw_feature_register[1] = -1; if (dev == NULL) return; /* process the device list */ for (i = 0; i < dev->length; i++) { if (dev->list[i].type == dsc_features) { features = (struct tfa_features *) (dev->list[i].offset+(uint8_t *)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 current_value, 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 */ current_value = (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 (current_value + delta < 15) { TFA_SET_BF_VOLATILE (dev_idx, DCMCC, current_value + delta); if (tfa98xx_cnt_verbose) pr_debug("Max coil current is set to: %d\n", current_value + 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 (current_value - delta > 0) { TFA_SET_BF_VOLATILE (dev_idx, DCMCC, current_value - delta); if (tfa98xx_cnt_verbose) pr_debug("Max coil current is set to: %d\n", current_value - 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; struct tfa_profile_list *prof = tfa_cont_profile(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++) { /* write values before default section */ if (prof->list[i].type == dsc_default) break; /* write all filter settings */ if (prof->list[i].type == dsc_filter) { if (tfa_cont_write_item (dev_idx, &prof->list[i]) != TFA98XX_ERROR_OK) return err; } } return err; } int tfa_tib_dsp_msgblob(int devidx, int length, const char *buffer) { uint8_t *buf = (uint8_t *)buffer; static uint8_t *blob = 0, *blobptr; #if defined(TFADSP_DSP_BUFFER_POOL) static int blob_p_index = -1; #endif static int total; /* No data found*/ #if defined(TFADSP_DSP_BUFFER_POOL) if (length == -1 && blob == 0) #else if (devidx == -1 && blob == 0) #endif { return TFA_ERROR; } #if defined(TFADSP_DSP_BUFFER_POOL) if (length == -1) #else if (devidx == -1) #endif { blob[2] = (uint8_t)(total >> 8); /* msb */ blob[3] = (uint8_t)total; /* lsb */ total += 4; memcpy(buf, blob, total); /* + header: 'mm' | size */ #if defined(TFADSP_DSP_BUFFER_POOL) if (blob_p_index != -1) { tfa98xx_buffer_pool_access (devidx, blob_p_index, 0, POOL_RETURN); blob_p_index = -1; } else { kfree(blob); } #else kfree(blob); #endif /* TFADSP_DSP_BUFFER_POOL */ blob = 0; /* Set back to 0 otherwise no new malloc is done! */ return total; } if (length == -2) { #if defined(TFADSP_DSP_BUFFER_POOL) if (blob_p_index != -1) { tfa98xx_buffer_pool_access (devidx, blob_p_index, 0, POOL_RETURN); blob_p_index = -1; } else { kfree(blob); } #else kfree(blob); #endif /* TFADSP_DSP_BUFFER_POOL */ blob = 0; /* Set back to 0 otherwise no new malloc is done! */ return 0; } if (blob == 0) { if (tfa98xx_cnt_verbose) pr_debug("%s, Creating the multi-message\n", __func__); #if defined(TFADSP_DSP_BUFFER_POOL) blob_p_index = tfa98xx_buffer_pool_access (devidx, -1, 64*1024, POOL_GET); if (blob_p_index != -1) { pr_debug("%s: allocated from buffer_pool[%d]\n", __func__, blob_p_index); blob = (uint8_t *)(handles_local[devidx] .buf_pool[blob_p_index].pool); } else { blob = kmalloc(64*1024, GFP_KERNEL); /* max length is 64k */ if (blob == NULL) goto msgblob_error_exit; } #else blob = kmalloc(64*1024, GFP_KERNEL); /* max length is 64k */ if (blob == NULL) goto msgblob_error_exit; #endif /* TFADSP_DSP_BUFFER_POOL */ blobptr = blob; *blobptr++ = 'm'; /* 'mm' = multi message */ *blobptr++ = 'm'; blobptr += 2; /* size comes here */ total = 0; if (tfa98xx_cnt_verbose) pr_debug("\n"); } if (tfa98xx_cnt_verbose) pr_debug("%s, id:0x%02x%02x%02x, length:%d\n", __func__, buf[0], buf[1], buf[2], length); *blobptr++ = (uint8_t)(length >> 8); /* msb */ *blobptr++ = (uint8_t)length; /* lsb */ memcpy(blobptr, buf, length); blobptr += length; total += length+2; /* +counters */ /* SetRe25 message is always the last message of the multi-msg */ pr_debug("%s: length (%d), [0]=0x%x-[1]=0x%x-[2]=0x%x\n", __func__, length, buf[0], buf[1], buf[2]); /* if (buf[1] == 0x81 && buf[2] == SB_PARAM_SET_RE25C) { */ if ((buf[0] == SB_PARAM_SET_RE25C && buf[1] == 0x81 && buf[2] == 0x00) || (buf[0] == FW_PAR_ID_GET_STATUS_CHANGE && buf[1] == 0x80) || (buf[0] == FW_PAR_ID_GET_TAG && buf[1] == 0x80) || (buf[0] == FW_PAR_ID_GET_API_VERSION && buf[1] == 0x80) || (buf[0] == FW_PAR_ID_GET_MEMTRACK && buf[1] == 0x80) || (buf[0] == SB_PARAM_GET_ALGO_PARAMS && buf[1] == 0x81 && buf[2] == 0x04) || (buf[0] == BFB_PAR_ID_GET_COEFS && buf[1] == 0x82) || (buf[0] == SB_PARAM_GET_RE25C && buf[1] == 0x81 && buf[2] == 0x04)) { pr_debug("%s: found last message - sending: buf[0]=%d\n", __func__, buf[0]); return 1; /* 1 means last message is done! */ } if ((buf[0] == SB_PARAM_SET_DATA_LOGGER && buf[1] == 0x81) || (buf[0] == SB_PARAM_GET_DATA_LOGGER && buf[1] == 0x81)) { pr_debug("%s: found blackbox message - sending: buf[0]=%d\n", __func__, buf[0]); return 1; /* 1 means last message is done! */ } return 0; msgblob_error_exit: pr_debug("%s: can not allocate memory\n", __func__); return TFA98XX_ERROR_FAIL; }