| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved. |
| */ |
| #include <linux/slab.h> |
| #include <linux/fs.h> |
| #include <linux/module.h> |
| #include <linux/miscdevice.h> |
| #include <linux/uaccess.h> |
| #include <linux/mutex.h> |
| #include <dsp/audio_cal_utils.h> |
| |
| static int unmap_memory(struct cal_type_data *cal_type, |
| struct cal_block_data *cal_block); |
| |
| size_t get_cal_info_size(int32_t cal_type) |
| { |
| size_t size = 0; |
| size_t size1 = 0, size2 = 0; |
| |
| switch (cal_type) { |
| case CVP_VOC_RX_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_voc_top); |
| break; |
| case CVP_VOC_TX_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_voc_top); |
| break; |
| case CVP_VOCPROC_STATIC_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_vocproc); |
| break; |
| case CVP_VOCPROC_DYNAMIC_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_vocvol); |
| break; |
| case CVS_VOCSTRM_STATIC_CAL_TYPE: |
| size = 0; |
| break; |
| case CVP_VOCDEV_CFG_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_vocdev_cfg); |
| break; |
| case CVP_VOCPROC_STATIC_COL_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_voc_col); |
| break; |
| case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_voc_col); |
| break; |
| case CVS_VOCSTRM_STATIC_COL_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_voc_col); |
| break; |
| case ADM_TOPOLOGY_CAL_TYPE: |
| case ADM_LSM_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_adm_top); |
| break; |
| case ADM_CUST_TOPOLOGY_CAL_TYPE: |
| case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE: |
| size = 0; |
| break; |
| case ADM_AUDPROC_CAL_TYPE: |
| case ADM_LSM_AUDPROC_CAL_TYPE: |
| case ADM_LSM_AUDPROC_PERSISTENT_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_audproc); |
| break; |
| case ADM_AUDVOL_CAL_TYPE: |
| case ADM_RTAC_AUDVOL_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_audvol); |
| break; |
| case ASM_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_asm_top); |
| break; |
| case ASM_CUST_TOPOLOGY_CAL_TYPE: |
| size = 0; |
| break; |
| case ASM_AUDSTRM_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_audstrm); |
| break; |
| case AFE_TOPOLOGY_CAL_TYPE: |
| case AFE_LSM_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_afe_top); |
| break; |
| case AFE_CUST_TOPOLOGY_CAL_TYPE: |
| size = 0; |
| break; |
| case AFE_COMMON_RX_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_afe); |
| break; |
| case AFE_COMMON_TX_CAL_TYPE: |
| case AFE_LSM_TX_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_afe); |
| break; |
| case AFE_FB_SPKR_PROT_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_spk_prot_cfg); |
| break; |
| case AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE: |
| /* |
| * Since get and set parameter structures are different in size |
| * use the maximum size of get and set parameter structure |
| */ |
| size1 = max(sizeof(struct audio_cal_info_sp_th_vi_ftm_cfg), |
| sizeof(struct audio_cal_info_sp_th_vi_param)); |
| size2 = max(sizeof(struct audio_cal_info_sp_th_vi_v_vali_cfg), |
| sizeof(struct audio_cal_info_sp_th_vi_v_vali_param)); |
| size = max(size1, size2); |
| break; |
| case AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE: |
| /* |
| * Since get and set parameter structures are different in size |
| * use the maximum size of get and set parameter structure |
| */ |
| size = max(sizeof(struct audio_cal_info_sp_ex_vi_ftm_cfg), |
| sizeof(struct audio_cal_info_sp_ex_vi_param)); |
| break; |
| case AFE_ANC_CAL_TYPE: |
| size = 0; |
| break; |
| case AFE_AANC_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_aanc); |
| break; |
| case AFE_HW_DELAY_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_hw_delay); |
| break; |
| case AFE_SIDETONE_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_sidetone); |
| break; |
| case AFE_SIDETONE_IIR_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_sidetone_iir); |
| break; |
| case LSM_CUST_TOPOLOGY_CAL_TYPE: |
| size = 0; |
| break; |
| case LSM_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_lsm_top); |
| break; |
| case ULP_LSM_TOPOLOGY_ID_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_lsm_top); |
| break; |
| case LSM_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_lsm); |
| break; |
| case ADM_RTAC_INFO_CAL_TYPE: |
| size = 0; |
| break; |
| case VOICE_RTAC_INFO_CAL_TYPE: |
| size = 0; |
| break; |
| case ADM_RTAC_APR_CAL_TYPE: |
| size = 0; |
| break; |
| case ASM_RTAC_APR_CAL_TYPE: |
| size = 0; |
| break; |
| case VOICE_RTAC_APR_CAL_TYPE: |
| size = 0; |
| break; |
| case MAD_CAL_TYPE: |
| size = 0; |
| break; |
| case ULP_AFE_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_afe); |
| break; |
| case ULP_LSM_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_lsm); |
| break; |
| case AUDIO_CORE_METAINFO_CAL_TYPE: |
| size = sizeof(struct audio_cal_info_metainfo); |
| break; |
| case SRS_TRUMEDIA_CAL_TYPE: |
| size = 0; |
| break; |
| default: |
| pr_err("%s:Invalid cal type %d!", |
| __func__, cal_type); |
| } |
| return size; |
| } |
| |
| size_t get_user_cal_type_size(int32_t cal_type) |
| { |
| size_t size = 0; |
| |
| switch (cal_type) { |
| case CVP_VOC_RX_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_voc_top); |
| break; |
| case CVP_VOC_TX_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_voc_top); |
| break; |
| case CVP_VOCPROC_STATIC_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_vocproc); |
| break; |
| case CVP_VOCPROC_DYNAMIC_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_vocvol); |
| break; |
| case CVS_VOCSTRM_STATIC_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_basic); |
| break; |
| case CVP_VOCDEV_CFG_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_vocdev_cfg); |
| break; |
| case CVP_VOCPROC_STATIC_COL_CAL_TYPE: |
| case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE: |
| case CVS_VOCSTRM_STATIC_COL_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_voc_col); |
| break; |
| case ADM_TOPOLOGY_CAL_TYPE: |
| case ADM_LSM_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_adm_top); |
| break; |
| case ADM_CUST_TOPOLOGY_CAL_TYPE: |
| case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_basic); |
| break; |
| case ADM_AUDPROC_CAL_TYPE: |
| case ADM_LSM_AUDPROC_CAL_TYPE: |
| case ADM_LSM_AUDPROC_PERSISTENT_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_audproc); |
| break; |
| case ADM_AUDVOL_CAL_TYPE: |
| case ADM_RTAC_AUDVOL_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_audvol); |
| break; |
| case ASM_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_asm_top); |
| break; |
| case ASM_CUST_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_basic); |
| break; |
| case ASM_AUDSTRM_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_audstrm); |
| break; |
| case AFE_TOPOLOGY_CAL_TYPE: |
| case AFE_LSM_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_afe_top); |
| break; |
| case AFE_CUST_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_basic); |
| break; |
| case AFE_COMMON_RX_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_afe); |
| break; |
| case AFE_COMMON_TX_CAL_TYPE: |
| case AFE_LSM_TX_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_afe); |
| break; |
| case AFE_FB_SPKR_PROT_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_fb_spk_prot_cfg); |
| break; |
| case AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE: |
| /* |
| * Since get and set parameter structures are different in size |
| * use the maximum size of get and set parameter structure |
| */ |
| size = max(sizeof(struct audio_cal_type_sp_th_vi_ftm_cfg), |
| sizeof(struct audio_cal_type_sp_th_vi_param)); |
| break; |
| case AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE: |
| /* |
| * Since get and set parameter structures are different in size |
| * use the maximum size of get and set parameter structure |
| */ |
| size = max(sizeof(struct audio_cal_type_sp_ex_vi_ftm_cfg), |
| sizeof(struct audio_cal_type_sp_ex_vi_param)); |
| break; |
| case AFE_ANC_CAL_TYPE: |
| size = 0; |
| break; |
| case AFE_AANC_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_aanc); |
| break; |
| case AFE_HW_DELAY_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_hw_delay); |
| break; |
| case AFE_SIDETONE_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_sidetone); |
| break; |
| case AFE_SIDETONE_IIR_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_sidetone_iir); |
| break; |
| case LSM_CUST_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_basic); |
| break; |
| case LSM_TOPOLOGY_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_lsm_top); |
| break; |
| case ULP_LSM_TOPOLOGY_ID_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_lsm_top); |
| break; |
| case LSM_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_lsm); |
| break; |
| case ADM_RTAC_INFO_CAL_TYPE: |
| size = 0; |
| break; |
| case VOICE_RTAC_INFO_CAL_TYPE: |
| size = 0; |
| break; |
| case ADM_RTAC_APR_CAL_TYPE: |
| size = 0; |
| break; |
| case ASM_RTAC_APR_CAL_TYPE: |
| size = 0; |
| break; |
| case VOICE_RTAC_APR_CAL_TYPE: |
| size = 0; |
| break; |
| case MAD_CAL_TYPE: |
| size = 0; |
| break; |
| case ULP_AFE_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_afe); |
| break; |
| case ULP_LSM_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_lsm); |
| break; |
| case AUDIO_CORE_METAINFO_CAL_TYPE: |
| size = sizeof(struct audio_cal_type_metainfo); |
| break; |
| case SRS_TRUMEDIA_CAL_TYPE: |
| size = 0; |
| break; |
| default: |
| pr_err("%s:Invalid cal type %d!", |
| __func__, cal_type); |
| } |
| return size; |
| } |
| |
| int32_t cal_utils_get_cal_type_version(void *cal_type_data) |
| { |
| struct audio_cal_type_basic *data = NULL; |
| |
| data = (struct audio_cal_type_basic *)cal_type_data; |
| |
| return data->cal_hdr.version; |
| } |
| |
| static struct cal_type_data *create_cal_type_data( |
| struct cal_type_info *info) |
| { |
| struct cal_type_data *cal_type = NULL; |
| |
| if ((info->reg.cal_type < 0) || |
| (info->reg.cal_type >= MAX_CAL_TYPES)) { |
| pr_err("%s: cal type %d is Invalid!\n", |
| __func__, info->reg.cal_type); |
| goto done; |
| } |
| |
| if (info->cal_util_callbacks.match_block == NULL) { |
| pr_err("%s: cal type %d no method to match blocks!\n", |
| __func__, info->reg.cal_type); |
| goto done; |
| } |
| |
| cal_type = kmalloc(sizeof(*cal_type), GFP_KERNEL); |
| if (cal_type == NULL) |
| goto done; |
| |
| INIT_LIST_HEAD(&cal_type->cal_blocks); |
| mutex_init(&cal_type->lock); |
| memcpy(&cal_type->info, info, |
| sizeof(cal_type->info)); |
| done: |
| return cal_type; |
| } |
| |
| /** |
| * cal_utils_create_cal_types |
| * |
| * @num_cal_types: number of types |
| * @cal_type: pointer to the cal types pointer |
| * @info: pointer to info |
| * |
| * Returns 0 on success, EINVAL otherwise |
| */ |
| int cal_utils_create_cal_types(int num_cal_types, |
| struct cal_type_data **cal_type, |
| struct cal_type_info *info) |
| { |
| int ret = 0; |
| int i; |
| |
| pr_debug("%s\n", __func__); |
| |
| if (cal_type == NULL) { |
| pr_err("%s: cal_type is NULL!\n", __func__); |
| ret = -EINVAL; |
| goto done; |
| } else if (info == NULL) { |
| pr_err("%s: info is NULL!\n", __func__); |
| ret = -EINVAL; |
| goto done; |
| } else if ((num_cal_types <= 0) || |
| (num_cal_types > MAX_CAL_TYPES)) { |
| pr_err("%s: num_cal_types of %d is Invalid!\n", |
| __func__, num_cal_types); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| for (i = 0; i < num_cal_types; i++) { |
| if ((info[i].reg.cal_type < 0) || |
| (info[i].reg.cal_type >= MAX_CAL_TYPES)) { |
| pr_err("%s: cal type %d at index %d is Invalid!\n", |
| __func__, info[i].reg.cal_type, i); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| cal_type[i] = create_cal_type_data(&info[i]); |
| if (cal_type[i] == NULL) { |
| pr_err("%s: Could not allocate cal_type of index %d!\n", |
| __func__, i); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| ret = audio_cal_register(1, &info[i].reg); |
| if (ret < 0) { |
| pr_err("%s: audio_cal_register failed, ret = %d!\n", |
| __func__, ret); |
| ret = -EINVAL; |
| goto done; |
| } |
| pr_debug("%s: cal type %d at index %d!\n", |
| __func__, info[i].reg.cal_type, i); |
| } |
| done: |
| return ret; |
| } |
| EXPORT_SYMBOL(cal_utils_create_cal_types); |
| |
| static void delete_cal_block(struct cal_block_data *cal_block) |
| { |
| pr_debug("%s\n", __func__); |
| |
| if (cal_block == NULL) |
| goto done; |
| |
| list_del(&cal_block->list); |
| kfree(cal_block->client_info); |
| cal_block->client_info = NULL; |
| kfree(cal_block->cal_info); |
| cal_block->cal_info = NULL; |
| if (cal_block->map_data.dma_buf != NULL) { |
| msm_audio_ion_free(cal_block->map_data.dma_buf); |
| cal_block->map_data.dma_buf = NULL; |
| } |
| kfree(cal_block); |
| done: |
| return; |
| } |
| |
| static void destroy_all_cal_blocks(struct cal_type_data *cal_type) |
| { |
| int ret = 0; |
| struct list_head *ptr, *next; |
| struct cal_block_data *cal_block; |
| |
| list_for_each_safe(ptr, next, |
| &cal_type->cal_blocks) { |
| |
| cal_block = list_entry(ptr, |
| struct cal_block_data, list); |
| |
| ret = unmap_memory(cal_type, cal_block); |
| if (ret < 0) { |
| pr_err("%s: unmap_memory failed, cal type %d, ret = %d!\n", |
| __func__, |
| cal_type->info.reg.cal_type, |
| ret); |
| } |
| delete_cal_block(cal_block); |
| cal_block = NULL; |
| } |
| } |
| |
| static void destroy_cal_type_data(struct cal_type_data *cal_type) |
| { |
| if (cal_type == NULL) |
| goto done; |
| |
| destroy_all_cal_blocks(cal_type); |
| list_del(&cal_type->cal_blocks); |
| kfree(cal_type); |
| done: |
| return; |
| } |
| |
| /** |
| * cal_utils_destroy_cal_types - |
| * Destroys cal types and deregister from cal info |
| * |
| * @num_cal_types: number of cal types |
| * @cal_type: cal type pointer with cal info |
| * |
| */ |
| void cal_utils_destroy_cal_types(int num_cal_types, |
| struct cal_type_data **cal_type) |
| { |
| int i; |
| |
| pr_debug("%s\n", __func__); |
| |
| if (cal_type == NULL) { |
| pr_err("%s: cal_type is NULL!\n", __func__); |
| goto done; |
| } else if ((num_cal_types <= 0) || |
| (num_cal_types > MAX_CAL_TYPES)) { |
| pr_err("%s: num_cal_types of %d is Invalid!\n", |
| __func__, num_cal_types); |
| goto done; |
| } |
| |
| for (i = 0; i < num_cal_types; i++) { |
| audio_cal_deregister(1, &cal_type[i]->info.reg); |
| destroy_cal_type_data(cal_type[i]); |
| cal_type[i] = NULL; |
| } |
| done: |
| return; |
| } |
| EXPORT_SYMBOL(cal_utils_destroy_cal_types); |
| |
| /** |
| * cal_utils_get_only_cal_block |
| * |
| * @cal_type: pointer to the cal type |
| * |
| * Returns cal_block structure |
| */ |
| struct cal_block_data *cal_utils_get_only_cal_block( |
| struct cal_type_data *cal_type) |
| { |
| struct list_head *ptr, *next; |
| struct cal_block_data *cal_block = NULL; |
| |
| if (cal_type == NULL) |
| goto done; |
| |
| list_for_each_safe(ptr, next, |
| &cal_type->cal_blocks) { |
| |
| cal_block = list_entry(ptr, |
| struct cal_block_data, list); |
| break; |
| } |
| done: |
| return cal_block; |
| } |
| EXPORT_SYMBOL(cal_utils_get_only_cal_block); |
| |
| /** |
| * cal_utils_get_only_cal_block |
| * |
| * @cal_block: pointer to cal block struct |
| * @user_data: pointer to user data |
| * |
| * Returns true on match |
| */ |
| bool cal_utils_match_buf_num(struct cal_block_data *cal_block, |
| void *user_data) |
| { |
| bool ret = false; |
| struct audio_cal_type_basic *data = user_data; |
| |
| if (cal_block->buffer_number == data->cal_hdr.buffer_number) |
| ret = true; |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(cal_utils_match_buf_num); |
| |
| static struct cal_block_data *get_matching_cal_block( |
| struct cal_type_data *cal_type, |
| void *data) |
| { |
| struct list_head *ptr, *next; |
| struct cal_block_data *cal_block = NULL; |
| |
| list_for_each_safe(ptr, next, |
| &cal_type->cal_blocks) { |
| |
| cal_block = list_entry(ptr, |
| struct cal_block_data, list); |
| |
| if (cal_type->info.cal_util_callbacks. |
| match_block(cal_block, data)) |
| return cal_block; |
| } |
| |
| return NULL; |
| } |
| |
| static int cal_block_ion_alloc(struct cal_block_data *cal_block) |
| { |
| int ret = 0; |
| |
| if (cal_block == NULL) { |
| pr_err("%s: cal_block is NULL!\n", __func__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| ret = msm_audio_ion_import(&cal_block->map_data.dma_buf, |
| cal_block->map_data.ion_map_handle, |
| NULL, 0, |
| &cal_block->cal_data.paddr, |
| &cal_block->map_data.map_size, |
| &cal_block->cal_data.kvaddr); |
| if (ret) { |
| pr_err("%s: audio ION import failed, rc = %d\n", |
| __func__, ret); |
| ret = -ENOMEM; |
| goto done; |
| } |
| done: |
| return ret; |
| } |
| |
| static struct cal_block_data *create_cal_block(struct cal_type_data *cal_type, |
| struct audio_cal_type_basic *basic_cal, |
| size_t client_info_size, void *client_info) |
| { |
| struct cal_block_data *cal_block = NULL; |
| |
| if (cal_type == NULL) { |
| pr_err("%s: cal_type is NULL!\n", __func__); |
| goto done; |
| } else if (basic_cal == NULL) { |
| pr_err("%s: basic_cal is NULL!\n", __func__); |
| goto done; |
| } |
| |
| cal_block = kzalloc(sizeof(*cal_block), |
| GFP_KERNEL); |
| if (cal_block == NULL) |
| goto done; |
| |
| INIT_LIST_HEAD(&cal_block->list); |
| |
| cal_block->map_data.ion_map_handle = basic_cal->cal_data.mem_handle; |
| if (basic_cal->cal_data.mem_handle > 0) { |
| if (cal_block_ion_alloc(cal_block)) { |
| pr_err("%s: cal_block_ion_alloc failed!\n", |
| __func__); |
| goto err; |
| } |
| } |
| if (client_info_size > 0) { |
| cal_block->client_info_size = client_info_size; |
| cal_block->client_info = kmalloc(client_info_size, GFP_KERNEL); |
| if (cal_block->client_info == NULL) { |
| pr_err("%s: could not allocats client_info!\n", |
| __func__); |
| goto err; |
| } |
| if (client_info != NULL) |
| memcpy(cal_block->client_info, client_info, |
| client_info_size); |
| } |
| |
| cal_block->cal_info = kzalloc( |
| get_cal_info_size(cal_type->info.reg.cal_type), |
| GFP_KERNEL); |
| if (cal_block->cal_info == NULL) { |
| pr_err("%s: could not allocats cal_info!\n", |
| __func__); |
| goto err; |
| } |
| cal_block->buffer_number = basic_cal->cal_hdr.buffer_number; |
| list_add_tail(&cal_block->list, &cal_type->cal_blocks); |
| pr_debug("%s: created block for cal type %d, buf num %d, map handle %d, map size %zd paddr 0x%pK!\n", |
| __func__, cal_type->info.reg.cal_type, |
| cal_block->buffer_number, |
| cal_block->map_data.ion_map_handle, |
| cal_block->map_data.map_size, |
| &cal_block->cal_data.paddr); |
| done: |
| return cal_block; |
| err: |
| kfree(cal_block->cal_info); |
| cal_block->cal_info = NULL; |
| kfree(cal_block->client_info); |
| cal_block->client_info = NULL; |
| kfree(cal_block); |
| cal_block = NULL; |
| return cal_block; |
| } |
| |
| void cal_utils_clear_cal_block_q6maps(int num_cal_types, |
| struct cal_type_data **cal_type) |
| { |
| int i = 0; |
| struct list_head *ptr, *next; |
| struct cal_block_data *cal_block; |
| |
| pr_debug("%s\n", __func__); |
| |
| if (cal_type == NULL) { |
| pr_err("%s: cal_type is NULL!\n", __func__); |
| goto done; |
| } else if ((num_cal_types <= 0) || |
| (num_cal_types > MAX_CAL_TYPES)) { |
| pr_err("%s: num_cal_types of %d is Invalid!\n", |
| __func__, num_cal_types); |
| goto done; |
| } |
| |
| for (; i < num_cal_types; i++) { |
| if (cal_type[i] == NULL) |
| continue; |
| |
| mutex_lock(&cal_type[i]->lock); |
| list_for_each_safe(ptr, next, |
| &cal_type[i]->cal_blocks) { |
| |
| cal_block = list_entry(ptr, |
| struct cal_block_data, list); |
| |
| cal_block->map_data.q6map_handle = 0; |
| } |
| mutex_unlock(&cal_type[i]->lock); |
| } |
| done: |
| return; |
| } |
| |
| |
| |
| static int realloc_memory(struct cal_block_data *cal_block) |
| { |
| int ret = 0; |
| |
| msm_audio_ion_free(cal_block->map_data.dma_buf); |
| cal_block->map_data.dma_buf = NULL; |
| cal_block->cal_data.size = 0; |
| |
| ret = cal_block_ion_alloc(cal_block); |
| if (ret < 0) |
| pr_err("%s: realloc_memory failed!\n", |
| __func__); |
| return ret; |
| } |
| |
| static int map_memory(struct cal_type_data *cal_type, |
| struct cal_block_data *cal_block) |
| { |
| int ret = 0; |
| |
| |
| if (cal_type->info.cal_util_callbacks.map_cal != NULL) { |
| if ((cal_block->map_data.ion_map_handle < 0) || |
| (cal_block->map_data.map_size <= 0) || |
| (cal_block->map_data.q6map_handle != 0)) { |
| goto done; |
| } |
| |
| pr_debug("%s: cal type %d call map\n", |
| __func__, cal_type->info.reg.cal_type); |
| ret = cal_type->info.cal_util_callbacks. |
| map_cal(cal_type->info.reg.cal_type, cal_block); |
| if (ret < 0) { |
| pr_err("%s: map_cal failed, cal type %d, ret = %d!\n", |
| __func__, cal_type->info.reg.cal_type, |
| ret); |
| goto done; |
| } |
| } |
| done: |
| return ret; |
| } |
| |
| static int unmap_memory(struct cal_type_data *cal_type, |
| struct cal_block_data *cal_block) |
| { |
| int ret = 0; |
| |
| if (cal_type->info.cal_util_callbacks.unmap_cal != NULL) { |
| if ((cal_block->map_data.ion_map_handle < 0) || |
| (cal_block->map_data.map_size <= 0) || |
| (cal_block->map_data.q6map_handle == 0)) { |
| goto done; |
| } |
| pr_debug("%s: cal type %d call unmap\n", |
| __func__, cal_type->info.reg.cal_type); |
| ret = cal_type->info.cal_util_callbacks. |
| unmap_cal(cal_type->info.reg.cal_type, cal_block); |
| if (ret < 0) { |
| pr_err("%s: unmap_cal failed, cal type %d, ret = %d!\n", |
| __func__, cal_type->info.reg.cal_type, |
| ret); |
| goto done; |
| } |
| } |
| done: |
| return ret; |
| } |
| |
| /** |
| * cal_utils_alloc_cal |
| * |
| * @data_size: size of data to allocate |
| * @data: data pointer |
| * @cal_type: pointer to the cal type |
| * @client_info_size: client info size |
| * @client_info: pointer to client info |
| * |
| * Returns 0 on success, appropriate error code otherwise |
| */ |
| int cal_utils_alloc_cal(size_t data_size, void *data, |
| struct cal_type_data *cal_type, |
| size_t client_info_size, void *client_info) |
| { |
| int ret = 0; |
| struct cal_block_data *cal_block; |
| struct audio_cal_type_alloc *alloc_data = data; |
| |
| pr_debug("%s\n", __func__); |
| |
| if (cal_type == NULL) { |
| pr_err("%s: cal_type is NULL!\n", |
| __func__); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (data_size < sizeof(struct audio_cal_type_alloc)) { |
| pr_err("%s: data_size of %zd does not equal alloc struct size of %zd!\n", |
| __func__, data_size, |
| sizeof(struct audio_cal_type_alloc)); |
| ret = -EINVAL; |
| goto done; |
| } |
| if ((client_info_size > 0) && (client_info == NULL)) { |
| pr_err("%s: User info pointer is NULL but size is %zd!\n", |
| __func__, client_info_size); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if (alloc_data->cal_data.mem_handle < 0) { |
| pr_err("%s: mem_handle %d invalid!\n", |
| __func__, alloc_data->cal_data.mem_handle); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| mutex_lock(&cal_type->lock); |
| |
| cal_block = get_matching_cal_block(cal_type, |
| data); |
| if (cal_block != NULL) { |
| ret = unmap_memory(cal_type, cal_block); |
| if (ret < 0) |
| goto err; |
| ret = realloc_memory(cal_block); |
| if (ret < 0) |
| goto err; |
| } else { |
| cal_block = create_cal_block(cal_type, |
| (struct audio_cal_type_basic *)alloc_data, |
| client_info_size, client_info); |
| if (cal_block == NULL) { |
| pr_err("%s: create_cal_block failed for %d!\n", |
| __func__, alloc_data->cal_data.mem_handle); |
| ret = -EINVAL; |
| goto err; |
| } |
| } |
| |
| ret = map_memory(cal_type, cal_block); |
| if (ret < 0) |
| goto err; |
| err: |
| mutex_unlock(&cal_type->lock); |
| done: |
| return ret; |
| } |
| EXPORT_SYMBOL(cal_utils_alloc_cal); |
| |
| /** |
| * cal_utils_dealloc_cal |
| * |
| * @data_size: size of data to allocate |
| * @data: data pointer |
| * @cal_type: pointer to the cal type |
| * |
| * Returns 0 on success, appropriate error code otherwise |
| */ |
| int cal_utils_dealloc_cal(size_t data_size, void *data, |
| struct cal_type_data *cal_type) |
| { |
| int ret = 0; |
| struct cal_block_data *cal_block; |
| struct audio_cal_type_dealloc *dealloc_data = data; |
| |
| pr_debug("%s\n", __func__); |
| |
| |
| if (cal_type == NULL) { |
| pr_err("%s: cal_type is NULL!\n", |
| __func__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if (data_size < sizeof(struct audio_cal_type_dealloc)) { |
| pr_err("%s: data_size of %zd does not equal struct size of %zd!\n", |
| __func__, data_size, |
| sizeof(struct audio_cal_type_dealloc)); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if ((dealloc_data->cal_data.mem_handle == -1) && |
| (dealloc_data->cal_hdr.buffer_number == ALL_CAL_BLOCKS)) { |
| destroy_all_cal_blocks(cal_type); |
| goto done; |
| } |
| |
| if (dealloc_data->cal_data.mem_handle < 0) { |
| pr_err("%s: mem_handle %d invalid!\n", |
| __func__, dealloc_data->cal_data.mem_handle); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| mutex_lock(&cal_type->lock); |
| cal_block = get_matching_cal_block( |
| cal_type, |
| data); |
| if (cal_block == NULL) { |
| pr_err("%s: allocation does not exist for %d!\n", |
| __func__, dealloc_data->cal_data.mem_handle); |
| ret = -EINVAL; |
| goto err; |
| } |
| |
| ret = unmap_memory(cal_type, cal_block); |
| if (ret < 0) |
| goto err; |
| |
| delete_cal_block(cal_block); |
| err: |
| mutex_unlock(&cal_type->lock); |
| done: |
| return ret; |
| } |
| EXPORT_SYMBOL(cal_utils_dealloc_cal); |
| |
| /** |
| * cal_utils_set_cal |
| * |
| * @data_size: size of data to allocate |
| * @data: data pointer |
| * @cal_type: pointer to the cal type |
| * @client_info_size: client info size |
| * @client_info: pointer to client info |
| * |
| * Returns 0 on success, appropriate error code otherwise |
| */ |
| int cal_utils_set_cal(size_t data_size, void *data, |
| struct cal_type_data *cal_type, |
| size_t client_info_size, void *client_info) |
| { |
| int ret = 0; |
| struct cal_block_data *cal_block; |
| struct audio_cal_type_basic *basic_data = data; |
| |
| pr_debug("%s\n", __func__); |
| |
| if (cal_type == NULL) { |
| pr_err("%s: cal_type is NULL!\n", |
| __func__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if ((client_info_size > 0) && (client_info == NULL)) { |
| pr_err("%s: User info pointer is NULL but size is %zd!\n", |
| __func__, client_info_size); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if ((data_size > get_user_cal_type_size( |
| cal_type->info.reg.cal_type)) || (data_size < 0)) { |
| pr_err("%s: cal_type %d, data_size of %zd is invalid, expecting %zd!\n", |
| __func__, cal_type->info.reg.cal_type, data_size, |
| get_user_cal_type_size(cal_type->info.reg.cal_type)); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| mutex_lock(&cal_type->lock); |
| cal_block = get_matching_cal_block( |
| cal_type, |
| data); |
| if (cal_block == NULL) { |
| if (basic_data->cal_data.mem_handle > 0) { |
| pr_err("%s: allocation does not exist for %d!\n", |
| __func__, basic_data->cal_data.mem_handle); |
| ret = -EINVAL; |
| goto err; |
| } else { |
| cal_block = create_cal_block( |
| cal_type, |
| basic_data, |
| client_info_size, client_info); |
| if (cal_block == NULL) { |
| pr_err("%s: create_cal_block failed for cal type %d!\n", |
| __func__, |
| cal_type->info.reg.cal_type); |
| ret = -EINVAL; |
| goto err; |
| } |
| } |
| } |
| |
| ret = map_memory(cal_type, cal_block); |
| if (ret < 0) |
| goto err; |
| |
| cal_block->cal_data.size = basic_data->cal_data.cal_size; |
| |
| if (client_info_size > 0) { |
| memcpy(cal_block->client_info, |
| client_info, |
| client_info_size); |
| } |
| |
| memcpy(cal_block->cal_info, |
| ((uint8_t *)data + sizeof(struct audio_cal_type_basic)), |
| data_size - sizeof(struct audio_cal_type_basic)); |
| |
| /* reset buffer stale flag */ |
| cal_block->cal_stale = false; |
| |
| err: |
| mutex_unlock(&cal_type->lock); |
| done: |
| return ret; |
| } |
| EXPORT_SYMBOL(cal_utils_set_cal); |
| |
| /** |
| * cal_utils_mark_cal_used |
| * |
| * @cal_block: pointer to cal block |
| */ |
| void cal_utils_mark_cal_used(struct cal_block_data *cal_block) |
| { |
| if (cal_block) |
| cal_block->cal_stale = true; |
| } |
| EXPORT_SYMBOL(cal_utils_mark_cal_used); |
| |
| /** |
| * cal_utils_is_cal_stale |
| * |
| * @cal_block: pointer to cal block |
| * |
| * Returns true if cal block is stale, false otherwise |
| */ |
| bool cal_utils_is_cal_stale(struct cal_block_data *cal_block) |
| { |
| if ((cal_block) && (cal_block->cal_stale)) |
| return true; |
| |
| return false; |
| } |
| EXPORT_SYMBOL(cal_utils_is_cal_stale); |