| /* |
| * 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 int e, m; |
| |
| e = (0x7F + 31) - ((*(unsigned int *)&x & 0x7F800000) >> 23); |
| m = 0x80000000 | (*(unsigned int *)&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("Initial vstep write\n"); |
| enp = 0; |
| } |
| |
| reg_info = tfa_cont_get_reg_for_vstep(vp, vstep_idx); |
| |
| msg_info = tfa_cont_get_msg_info_from_reg(reg_info); |
| nr_messages = msg_info->nr_of_messages; |
| |
| if (enp) { |
| p_msg_info = tfa_cont_get_msg_info_from_reg(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; |
| |
| pr_info("%s: start (type %d)\n", __func__, type); |
| |
| 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 succesful, 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); |
| #if defined(USE_TFA9896) |
| if (handles_local[dev_idx].config_crc == hdr->crc |
| && handles_local[dev_idx].config_size == size) { |
| pr_info("%s: skip writing config, duplicated\n", |
| __func__); |
| break; |
| } |
| #endif |
| err = tfa98xx_dsp_write_config |
| (dev_idx, size, |
| (const unsigned char *) |
| ((struct tfa_config_file *)hdr)->data); |
| #if defined(USE_TFA9896) |
| if (err == TFA98XX_ERROR_OK) { |
| handles_local[dev_idx].config_crc = hdr->crc; |
| handles_local[dev_idx].config_size = size; |
| } |
| #endif |
| 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; |
| } |
| |
| pr_info("%s: end\n", __func__); |
| |
| 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 compatibility */ |
| 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_cmd *cmd; |
| 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; |
| 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: |
| /* 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 setting (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; |
| } |
| |
| unsigned int tfa98xx_get_profile_chsa(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 chsa_profile = -1; |
| |
| /* bypass case in Max1 */ |
| if (tfa98xx_dev_family(dev_idx) != 1) |
| return 0; |
| |
| 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 setting (CHSA) */ |
| if (prof->list[i].type == dsc_bit_field) { |
| bitf = (struct tfa_bitfield *) |
| (prof->list[i].offset+(uint8_t *)g_cont); |
| if (bitf->field == TFA1_BF_CHSA) { |
| chsa_profile = bitf->value; |
| break; |
| } |
| } |
| } |
| |
| pr_debug("%s - profile chsa: 0x%x (%d - %d)\n", __func__, |
| chsa_profile, dev_idx, prof_idx); |
| if (chsa_profile != -1) |
| return chsa_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 == TFA1_BF_CHSA) { |
| chsa_profile = bitf->value; |
| break; |
| } |
| } |
| /* Ignore register case */ |
| } |
| |
| pr_debug("%s - default chsa: 0x%x (%d - %d)\n", __func__, |
| chsa_profile, dev_idx, prof_idx); |
| if (chsa_profile != -1) |
| return chsa_profile; |
| |
| return tfa_get_bf(dev_idx, TFA1_BF_CHSA); |
| } |
| |
| 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 (handles_local[0].ext_dsp) { /* check only master device */ |
| /* put SetRe25 message to indicate all messages are send */ |
| pr_info("%s: tfa_set_calibration_values\n", __func__); |
| tfa_set_swprof(dev_idx, (unsigned short)prof_idx); |
| err = tfa_set_calibration_values(dev_idx); |
| if (err) |
| pr_info("%s: set calibration values error = %d\n", |
| __func__, 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; |
| } |
| |
| /** |
| * Is the profile specific to device ? |
| * @param dev_idx the index of the device |
| * @param prof_idx the index of the profile |
| * @return 1 if the profile belongs to device or 0 if not |
| */ |
| int tfa_cont_is_dev_specific_profile(int dev_idx, int prof_idx) |
| { |
| char dev_substring[100] = {0}; |
| char *pch; |
| int prof_name_len; |
| |
| if ((dev_idx < 0) || (dev_idx >= tfa98xx_cnt_max_device())) |
| return 0; |
| |
| prof_name_len = strlen(tfa_cont_profile_name(dev_idx, prof_idx)); |
| pch = strchr(tfa_cont_profile_name(dev_idx, prof_idx), '.'); |
| if (!pch) |
| return 0; |
| |
| snprintf(dev_substring, 100, ".%s", tfa_cont_device_name(dev_idx)); |
| if (prof_name_len < strlen(dev_substring)) |
| return 0; |
| |
| /* Check if next profile is tap profile */ |
| if (strnstr(tfa_cont_profile_name(dev_idx, prof_idx), |
| dev_substring, prof_name_len) != NULL) { |
| pr_debug("dev profile: '%s' of device '%s'\n", |
| tfa_cont_profile_name(dev_idx, prof_idx), |
| dev_substring); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Get the name of the profile at certain index for a device |
| * in the container file |
| * return profile name |
| */ |
| char *tfa_cont_profile_name(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_MEMORY && buf[1] == 0x80) |
| || (buf[0] == FW_PAR_ID_GLOBAL_GET_INFO && buf[1] == 0x80) |
| || (buf[0] == FW_PAR_ID_GET_FEATURE_INFO && buf[1] == 0x80) |
| || (buf[0] == FW_PAR_ID_GET_MEMTRACK && 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_STATUS_CHANGE && buf[1] == 0x80) |
| || (buf[0] == BFB_PAR_ID_GET_COEFS && buf[1] == 0x82) |
| || (buf[0] == BFB_PAR_ID_GET_CONFIG && buf[1] == 0x82)) { |
| 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_GET_ALGO_PARAMS && buf[1] == 0x81) |
| || (buf[0] == SB_PARAM_GET_LAGW && buf[1] == 0x81) |
| || (buf[0] == SB_PARAM_GET_RE25C && buf[1] == 0x81) |
| || (buf[0] == SB_PARAM_GET_LSMODEL && buf[1] == 0x81) |
| || (buf[0] == SB_PARAM_GET_MBDRC && buf[1] == 0x81) |
| || (buf[0] == SB_PARAM_GET_MBDRC_DYNAMICS && buf[1] == 0x81) |
| || (buf[0] == SB_PARAM_GET_EXCURSION_FILTERS && buf[1] == 0x81) |
| || (buf[0] == SB_PARAM_GET_TAG && buf[1] == 0x81) |
| || (buf[0] == SB_PARAM_GET_STATE && buf[1] == 0x81) |
| || (buf[0] == SB_PARAM_GET_XMODEL && buf[1] == 0x81) |
| || (buf[0] == SB_PARAM_GET_XMODEL_COEFFS && buf[1] == 0x81)) { |
| pr_debug("%s: found last message - sending: buf[0]=%d CC=%d\n", |
| __func__, buf[0], buf[2]); |
| return 1; /* 1 means last message is done! with CC check */ |
| } |
| |
| /* SB_PARAM_SET_DATA_LOGGER to be handled at initializing */ |
| if (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; |
| } |