/* Copyright (c) 2015-2016, 2018, The Linux Foundation. * All rights reserved. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Author : Vijeth (vijeth.po@pathpartnertech.com) * Revised Date : 30-07-19 * Description : TI Smartamp algorithm control interface * */ #ifdef CONFIG_TAS25XX_ALGO #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tas25xx-algo.h" static uint32_t port; static uint8_t calibration_result[MAX_CHANNELS] = {STATUS_NONE}; static uint8_t validation_result[MAX_CHANNELS] = {STATUS_NONE}; static bool calibration_status; static bool validation_running_status; static uint32_t calib_re_hold[MAX_CHANNELS] = {0}; static uint32_t amb_temp_hold[MAX_CHANNELS] = {0}; static int32_t g_iv_vbat_fmt = IV_SENSE_FORMAT_NO_VBAT; static int32_t re_low[MAX_CHANNELS] = {0}; static int32_t re_high[MAX_CHANNELS] = {0}; /*Mutex to serialize DSP read/write commands*/ static struct mutex routing_lock; static struct tas25xx_algo *p_tas25xx_algo; /*Max value supported is 2^8*/ static uint8_t trans_val_to_user_m(uint32_t val, uint8_t qformat) { uint32_t ret = (uint32_t)(((long long)val * 1000) >> qformat) % 1000; return (uint8_t)(ret / 10); } /*Max value supported is 2^8*/ static uint8_t trans_val_to_user_i(uint32_t val, uint8_t qformat) { return ((val * 100) >> qformat) / 100; } static int afe_smartamp_get_set(u8 *user_data, uint32_t param_id, uint8_t get_set, uint32_t length) { int ret = 0; struct ti_smartpa_data resp_data; if (!p_tas25xx_algo) { pr_err("[TI-SmartPA:%s] memory not allocated yet for p_tas25xx_algo", __func__); } switch (get_set) { case TAS_SET_PARAM: memcpy(resp_data.payload, user_data, length); ret = ti_smartpa_write((void *)&resp_data, param_id, length); break; case TAS_GET_PARAM: memset(&resp_data, 0, sizeof(resp_data)); ret = ti_smartpa_read((void *)&resp_data, param_id, length); if (ret == 0) memcpy(user_data, resp_data.payload, length); break; default: goto fail_cmd; } fail_cmd: return ret; } /* Wrapper around set/get parameter, all set/get commands pass * through this wrapper */ int afe_smartamp_algo_ctrl(u8 *user_data, uint32_t param_id, uint8_t get_set, uint32_t length) { int ret = 0; mutex_lock(&routing_lock); ret = afe_smartamp_get_set(user_data, param_id, get_set, length); mutex_unlock(&routing_lock); return ret; } static uint8_t tas25xx_get_amb_temp(void) { struct power_supply *psy; union power_supply_propval value = {0}; psy = power_supply_get_by_name("battery"); if (!psy || !psy->desc || !psy->desc->get_property) { pr_err("[TI-SmartPA:%s] getting ambient temp failed, using default value %d", __func__, DEFAULT_AMBIENT_TEMP); return DEFAULT_AMBIENT_TEMP; } psy->desc->get_property(psy, POWER_SUPPLY_PROP_TEMP, &value); return DIV_ROUND_CLOSEST(value.intval, 10); } void tas25xx_update_big_data(void) { uint8_t iter = 0; uint32_t data[8] = {0}; uint32_t param_id = 0; int32_t ret = 0; if (!p_tas25xx_algo) { pr_err("[TI-SmartPA:%s] memory not allocated yet for p_tas25xx_algo", __func__); } for (iter = 0; iter < p_tas25xx_algo->spk_count; iter++) { /*Reset data*/ memset(data, 0, 8*sizeof(uint32_t)); param_id = (TAS_SA_EXC_TEMP_STAT)|((iter+1)<<24) |((sizeof(data)/sizeof(uint32_t))<<16); ret = afe_smartamp_algo_ctrl((u8 *)(&(data[0])), param_id , TAS_GET_PARAM, sizeof(data)); if (ret < 0) { pr_err("[TI-SmartPA:%s] Failed to get Excursion and Temperature Stats", __func__); } else { pr_err("[TI-SmartPA:%s] Emax[%d] %d(%02d.%02d), Tmax[%d] %d, EOcount[%d] %d, TOcount[%d] %d \n", __func__, iter, data[0], (int32_t)trans_val_to_user_i(data[0], QFORMAT31), (int32_t)trans_val_to_user_m(data[0], QFORMAT31), iter, data[1], iter, data[2], iter, data[3]); /*Update Excursion Data*/ p_tas25xx_algo->b_data[iter].exc_max = (data[0] > p_tas25xx_algo->b_data[iter].exc_max) ? data[0]:p_tas25xx_algo->b_data[iter].exc_max; p_tas25xx_algo->b_data[iter].exc_max_persist = (data[0] > p_tas25xx_algo->b_data[iter].exc_max_persist) ? data[0]:p_tas25xx_algo->b_data[iter].exc_max_persist; p_tas25xx_algo->b_data[iter].exc_over_count += data[2]; /*Update Temperature Data*/ p_tas25xx_algo->b_data[iter].temp_max = (data[1] > p_tas25xx_algo->b_data[iter].temp_max) ? data[1]:p_tas25xx_algo->b_data[iter].temp_max; p_tas25xx_algo->b_data[iter].temp_max_persist = (data[1] > p_tas25xx_algo->b_data[iter].temp_max_persist) ? data[1]:p_tas25xx_algo->b_data[iter].temp_max_persist; p_tas25xx_algo->b_data[iter].temp_over_count += data[3]; } memset(data, 0, 8*sizeof(uint32_t)); param_id = (TAS_SA_LE_FLAG_STATS)|((iter+1)<<24) |((sizeof(data)/sizeof(uint32_t))<<16); ret = afe_smartamp_algo_ctrl((u8 *)(&(data[0])), param_id, TAS_GET_PARAM, sizeof(data)); if (ret < 0) { pr_err("[TI-SmartPA:%s] Failed to get LE flag info\n", __func__); } else { u64 temp = 0; p_tas25xx_algo->b_le_flag[iter].le_flag_detected = data[0]; p_tas25xx_algo->b_le_flag[iter].le_flag_count = data[1]; temp = data[1] + p_tas25xx_algo->b_le_flag[iter].le_flag_count_persist; if ((temp - data[1]) == p_tas25xx_algo->b_le_flag[iter].le_flag_count_persist) p_tas25xx_algo->b_le_flag[iter].le_flag_count_persist = temp; else p_tas25xx_algo->b_le_flag[iter].le_flag_count_persist = U64_MAX; pr_info("[TI-SmartPA:%s] LE Flag Detection Ch=%d detected=%u, count=%u, Total Count=%llu\n", __func__, iter, p_tas25xx_algo->b_le_flag[iter].le_flag_detected, p_tas25xx_algo->b_le_flag[iter].le_flag_count, p_tas25xx_algo->b_le_flag[iter].le_flag_count_persist); } } } int tas25xx_update_calibration_limits(void) { uint8_t iter = 0; uint32_t param_id = 0; int32_t ret = 0; /*Reset Calibration Result*/ memset(calibration_result, STATUS_NONE, sizeof(uint8_t)*MAX_CHANNELS); for (iter = 0; iter < p_tas25xx_algo->spk_count; iter++) { /*Update Lower limit for calibration*/ re_low[iter] = 0;//Reset data to 0 param_id = ((TAS_SA_GET_RE_LOW)|((iter+1)<<24)|(1<<16)); ret = afe_smartamp_algo_ctrl((u8 *)&re_low[iter], param_id, TAS_GET_PARAM, sizeof(uint32_t)); if (ret < 0) { pr_err("[TI-SmartPA:%s]get re low fail channel no %d Exiting ..\n", __func__, iter); return ret; } re_low[iter] = re_low[iter] >> 8; /* Qformat 27 -> 19*/ /*Update Upper limit for calibration*/ re_high[iter] = 0;//Reset data to 0 param_id = ((TAS_SA_GET_RE_HIGH)|((iter+1)<<24)|(1<<16)); ret = afe_smartamp_algo_ctrl((u8 *)&re_high[iter], param_id, TAS_GET_PARAM, sizeof(uint32_t)); if (ret < 0) { pr_err("[TI-SmartPA:%s]get re high fail channel no %d Exiting ..\n", __func__, iter); return ret; } re_high[iter] = re_high[iter] >> 8; /* Qformat 27 -> 19*/ pr_err("[TI-SmartPA:%s] Channel No:%d, Rdc Limits(%02d.%02d ~ %02d.%02d) \n", __func__, iter, (int32_t)trans_val_to_user_i(re_low[iter], QFORMAT19), (int32_t)trans_val_to_user_m(re_low[iter], QFORMAT19), (int32_t)trans_val_to_user_i(re_high[iter], QFORMAT19), (int32_t)trans_val_to_user_m(re_high[iter], QFORMAT19)); } return 0; } int tas25xx_check_limits(uint8_t channel, int32_t rdc) { if ((rdc >= re_low[channel]) && (rdc <= re_high[channel])) { calibration_result[channel] = STATUS_SUCCESS; return 1; } calibration_result[channel] = STATUS_FAIL; return 0; } void tas25xx_send_algo_calibration(void) { uint32_t data = 0; uint32_t param_id = 0; uint8_t iter; int32_t ret = 0; if (!p_tas25xx_algo) pr_err("[TI-SmartPA:%s] memory not allocated yet for p_tas25xx_algo", __func__); for (iter = 0; iter < p_tas25xx_algo->spk_count; iter++) { if (p_tas25xx_algo->calib_update[iter]) { /*Set ambient temperature*/ data = p_tas25xx_algo->amb_temp[iter]; pr_info("[TI-SmartPA:%s] amb_temp[%d] %d", __func__, iter, data); param_id = (TAS_SA_SET_TCAL)|((iter+1)<<24)|(1<<16); ret = afe_smartamp_algo_ctrl((u8 *)&data, param_id, TAS_SET_PARAM, sizeof(uint32_t)); /*Set Re*/ data = p_tas25xx_algo->calib_re[iter]; pr_info("[TI-SmartPA:%s]calib_re[%d] is %02d.%02d (%d) \n", __func__, iter, (int32_t)trans_val_to_user_i(data, QFORMAT19), (int32_t)trans_val_to_user_m(data, QFORMAT19), (int32_t)data); param_id = (TAS_SA_SET_RE)|((iter+1)<<24)|(1<<16); ret = afe_smartamp_algo_ctrl((u8 *)&data, param_id, TAS_SET_PARAM, sizeof(uint32_t)); } } } bool tas25xx_set_iv_bit_fomat(int iv_data_with, int vbat, int update_now) { int32_t param_id; int32_t ret; bool success; if ((vbat == 1) && (iv_data_with == 12)) g_iv_vbat_fmt = IV_SENSE_FORMAT_12_BIT_WITH_8BIT_VBAT; else if ((vbat == 1) && (iv_data_with == 8)) g_iv_vbat_fmt = IV_SENSE_FORMAT_8_BIT_WITH_8BIT_VBAT; else g_iv_vbat_fmt = IV_SENSE_FORMAT_NO_VBAT; success = true; if (update_now) { param_id = (TAS_SA_IV_VBAT_FMT) | (1<<24) | (1<<16); pr_info("TI-SmartPA: %s: Sending IV,Vbat format %d\n", __func__, g_iv_vbat_fmt); ret = afe_smartamp_algo_ctrl((u8 *)&g_iv_vbat_fmt, param_id, TAS_SET_PARAM, sizeof(uint32_t)); if (ret < 0) { pr_err("TI-SmartPA: %s: Failed to set config for iv-vbat data format\n", __func__); success = false; } } return success; } static int tas25xx_save_calib_data(uint32_t *calib_rdc) { uint8_t iter = 0; int ret = 0; if (!calib_rdc) { pr_err("[TI-SmartPA:%s] argument is Null", __func__); ret = -1; return ret; } for (iter = 0; iter < p_tas25xx_algo->spk_count; iter++) { calib_re_hold[iter] = calib_rdc[iter]; amb_temp_hold[iter] = tas25xx_get_amb_temp(); } return 0; } /**********Calibration Related Codes Start******************/ static void calib_work_routine(struct work_struct *work) { uint8_t iter = 0, iter2 = 0; uint32_t data = 0; uint32_t param_id = 0; uint32_t calib_re[MAX_CHANNELS] = {0}; int32_t ret = 0; if (tas25xx_update_calibration_limits()) return; /*Get Re*/ for (iter2 = 0; iter2 < CALIB_RETRY_COUNT; iter2++) { for (iter = 0; iter < p_tas25xx_algo->spk_count; iter++) { if (calibration_result[iter] == STATUS_SUCCESS) continue; /*Calinration Init*/ data = 1;/*Value is ignored*/ param_id = (TAS_SA_CALIB_INIT)|((iter+1)<<24)|(1<<16); ret = afe_smartamp_algo_ctrl((u8 *)&data, param_id , TAS_SET_PARAM, sizeof(uint32_t)); if (ret < 0) { pr_err("[TI-SmartPA:%s] Init error. Exiting ..", __func__); return; } msleep(CALIB_TIME*1000); data = 0;//Reset data to 0 param_id = ((TAS_SA_GET_RE)|((iter+1)<<24)|(1<<16)); ret = afe_smartamp_algo_ctrl((u8 *)&data, param_id, TAS_GET_PARAM, sizeof(uint32_t)); if (ret < 0) { calibration_result[iter] = STATUS_FAIL; pr_info("[TI-SmartPA:%s]get re fail. Exiting ..\n", __func__); } else { calib_re[iter] = data; pr_info("[TI-SmartPA:%s]calib_re is %02d.%02d (%d) \n", __func__, (int32_t)trans_val_to_user_i(calib_re[iter], QFORMAT19), (int32_t)trans_val_to_user_m(calib_re[iter], QFORMAT19), (int32_t)calib_re[iter]); if (tas25xx_check_limits(iter, calib_re[iter])) pr_info("[TI-SmartPA:%s] Calibration Pass Channel No:%d", __func__, iter); } /*Calibration De-Init*/ data = 1;//Value is ignored param_id = (TAS_SA_CALIB_DEINIT)|((iter+1)<<24)|(1<<16); ret = afe_smartamp_algo_ctrl((u8 *)&data, param_id , TAS_SET_PARAM, sizeof(uint32_t)); /*Wait some time*/ msleep(200); } } tas25xx_save_calib_data(calib_re); calibration_status = 0; return; } static DEVICE_ATTR(calibration, 0664, tas25xx_calib_calibration_show, tas25xx_calib_calibration_store); static DEVICE_ATTR(cstatus, 0664, tas25xx_calib_status_show, NULL); static DEVICE_ATTR(rdc, 0664, tas25xx_calib_rdc_show, tas25xx_calib_rdc_store); static DEVICE_ATTR(temp, 0664, tas25xx_amb_temp_show, tas25xx_amb_temp_store); static DEVICE_ATTR(cstatus_r, 0664, tas25xx_calib_status_show, NULL); static DEVICE_ATTR(rdc_r, 0664, tas25xx_calib_rdc_show, tas25xx_calib_rdc_store); static DEVICE_ATTR(temp_r, 0664, tas25xx_amb_temp_show, tas25xx_amb_temp_store); static ssize_t tas25xx_calib_calibration_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int32_t ret = 0; int32_t start; ret = kstrtos32(buf, 10, &start); if (ret) { pr_err("[TI-SmartPA:%s] Invalid input", __func__); goto end; } if (start) { calibration_status = 1; /*Give time for algorithm to converge rdc*/ schedule_delayed_work(&p_tas25xx_algo->calib_work, msecs_to_jiffies(200)); } end: return size; } static ssize_t tas25xx_calib_calibration_show(struct device *dev, struct device_attribute *attr, char *buf) { /*Enough to check for only one channel*/ return snprintf(buf, MAX_STRING, "%s\n", (calibration_status) ? "Enabled" : "Disabled"); } static ssize_t tas25xx_calib_status_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; if (attr == &dev_attr_cstatus_r) ret = snprintf(buf, MAX_STRING, "%d", calibration_result[1]); else ret = snprintf(buf, MAX_STRING, "%d", calibration_result[0]); return ret; } static ssize_t tas25xx_calib_rdc_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; uint32_t calib_re = 0; if (attr == &dev_attr_rdc_r) { if (calibration_result[1] == STATUS_NONE) { if (p_tas25xx_algo->calib_update[1]) calib_re = p_tas25xx_algo->calib_re[1]; } else calib_re = calib_re_hold[1]; ret = snprintf(buf, MAX_STRING, "%02d.%02d", (int32_t)trans_val_to_user_i(calib_re, QFORMAT19), (int32_t)trans_val_to_user_m(calib_re, QFORMAT19)); } else { if (calibration_result[0] == STATUS_NONE) { if (p_tas25xx_algo->calib_update[0]) calib_re = p_tas25xx_algo->calib_re[0]; } else calib_re = calib_re_hold[0]; ret = snprintf(buf, MAX_STRING, "%02d.%02d", (int32_t)trans_val_to_user_i(calib_re, QFORMAT19), (int32_t)trans_val_to_user_m(calib_re, QFORMAT19)); } return ret; } static ssize_t tas25xx_calib_rdc_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { uint32_t data_flt[2]; int ret = 0; ret = sscanf(buf, "%d.%d", &(data_flt[0]), &(data_flt[1])); if (ret != 2) { pr_err("[TI-SmartPA:%s] read error\n", __func__); goto end; } if (attr == &dev_attr_rdc_r) p_tas25xx_algo->calib_re[1] = TRANSF_USER_TO_IMPED(data_flt[0], data_flt[1]); else p_tas25xx_algo->calib_re[0] = TRANSF_USER_TO_IMPED(data_flt[0], data_flt[1]); end: return size; } static ssize_t tas25xx_amb_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; uint32_t amb_temp = 0; if (attr == &dev_attr_temp_r) { if (calibration_result[1] == STATUS_NONE) { if (p_tas25xx_algo->calib_update[1]) amb_temp = p_tas25xx_algo->amb_temp[1]; } else amb_temp = amb_temp_hold[1]; ret = snprintf(buf, MAX_STRING, "%d", amb_temp); } else { if (calibration_result[0] == STATUS_NONE) { if (p_tas25xx_algo->calib_update[0]) amb_temp = p_tas25xx_algo->amb_temp[0]; } else amb_temp = amb_temp_hold[0]; ret = snprintf(buf, MAX_STRING, "%d", amb_temp); } return ret; } static ssize_t tas25xx_amb_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { uint32_t data; int ret = 0; ret = sscanf(buf, "%d", &data); if (ret != 1) { pr_err("[TI-SmartPA:%s] read error\n", __func__); goto end; } if (attr == &dev_attr_temp_r) { p_tas25xx_algo->amb_temp[1] = data; p_tas25xx_algo->calib_update[1] = true; } else { p_tas25xx_algo->amb_temp[0] = data; p_tas25xx_algo->calib_update[0] = true; } end: return size; } static struct attribute *tas25xx_calib_attr[] = { &dev_attr_calibration.attr, &dev_attr_cstatus.attr, &dev_attr_rdc.attr, &dev_attr_temp.attr, }; static struct attribute *tas25xx_calib_attr_r[] = { &dev_attr_cstatus_r.attr, &dev_attr_rdc_r.attr, &dev_attr_temp_r.attr, }; static struct attribute *tas25xx_calib_attr_m[ ARRAY_SIZE(tas25xx_calib_attr) + ARRAY_SIZE(tas25xx_calib_attr_r) + 1] = {NULL}; static struct attribute_group tas25xx_calib_attr_grp = { .attrs = tas25xx_calib_attr_m, }; /*******************Calibration Related Codes End***********************/ /*******************Validation Related Codes Start**********************/ static void valid_work_routine(struct work_struct *work) { uint8_t iter = 0; uint32_t data = 0; uint32_t param_id = 0; int32_t ret = 0; //Validation Status Check for (iter = 0; iter < p_tas25xx_algo->spk_count; iter++) { data = 0;//Reset data to 0 param_id = ((TAS_SA_GET_VALID_STATUS)|((iter+1)<<24)|(1<<16)); ret = afe_smartamp_algo_ctrl((u8 *)&data, param_id, TAS_GET_PARAM, sizeof(uint32_t)); if (ret < 0) { validation_result[iter] = STATUS_FAIL; pr_info("[TI-SmartPA:%s]status read failed \n", __func__); } else { if (data == VALIDATION_SUCCESS) { validation_result[iter] = STATUS_SUCCESS; } else { validation_result[iter] = STATUS_FAIL; } } pr_info("[TI-SmartPA:%s] Channel-%d", __func__, iter); pr_info("[TI-SmartPA:%s] validation_result %s(0x%x)\n", __func__, validation_result[iter] == STATUS_SUCCESS ? "Success":"Fail", (int32_t)data); /*De-Init the validation process*/ data = 0;//Value is ignored param_id = (TAS_SA_VALID_DEINIT)|((iter+1)<<24)|(1<<16); ret = afe_smartamp_algo_ctrl((u8 *)&data, param_id, TAS_SET_PARAM, sizeof(uint32_t)); } validation_running_status = 0; return; } static DEVICE_ATTR(validation, 0644, tas25xx_valid_validation_show, tas25xx_valid_validation_store); static DEVICE_ATTR(status, 0644, tas25xx_valid_status_show, NULL); static DEVICE_ATTR(status_r, 0644, tas25xx_valid_status_show, NULL); static ssize_t tas25xx_valid_validation_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { uint8_t iter = 0; uint32_t data = 0; uint32_t param_id = 0; int32_t start = 0; int32_t ret = 0; ret = kstrtos32(buf, 10, &start); if (ret) { pr_err("[TI-SmartPA:%s] Invalid input", __func__); goto end; } if (start) { //Init for (iter = 0; iter < p_tas25xx_algo->spk_count; iter++) { data = 1;/*Value is ignored*/ param_id = (TAS_SA_VALID_INIT)|((iter+1)<<24)|(1<<16); ret = afe_smartamp_algo_ctrl((u8 *)&data, param_id , TAS_SET_PARAM, sizeof(uint32_t)); if (ret < 0) { pr_err("[TI-SmartPA:%s] Init error. Exiting ..", __func__); goto end; } } //Give time for algorithm to converge V-sns level validation_running_status = 1; schedule_delayed_work(&p_tas25xx_algo->valid_work, msecs_to_jiffies(VALIDATION_TIME * 1000)); } end: return size; } static ssize_t tas25xx_valid_validation_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, MAX_STRING, "%s\n", (validation_running_status) ? "Enabled" : "Disabled"); } static ssize_t tas25xx_valid_status_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; if (attr == &dev_attr_status_r) ret = snprintf(buf, MAX_STRING, "%d", validation_result[1]); else ret = snprintf(buf, MAX_STRING, "%d", validation_result[0]); return ret; } static struct attribute *tas25xx_valid_attr[] = { &dev_attr_validation.attr, &dev_attr_status.attr, }; static struct attribute *tas25xx_valid_attr_r[] = { &dev_attr_status_r.attr, }; static struct attribute *tas25xx_valid_attr_m[ ARRAY_SIZE(tas25xx_valid_attr) + ARRAY_SIZE(tas25xx_valid_attr_r) + 1] = {NULL}; static struct attribute_group tas25xx_valid_attr_grp = { .attrs = tas25xx_valid_attr_m, }; /******************Validation Related Codes End***********************/ /******************BigData Related Codes Start************************/ static DEVICE_ATTR(exc_max, 0664, tas25xx_bd_exc_max_show, NULL); static DEVICE_ATTR(exc_max_persist, 0664, tas25xx_bd_exc_max_persist_show, NULL); static DEVICE_ATTR(exc_over_count, 0664, tas25xx_bd_exc_over_count_show, NULL); static DEVICE_ATTR(temp_max, 0664, tas25xx_bd_temp_max_show, NULL); static DEVICE_ATTR(temp_max_persist, 0664, tas25xx_bd_temp_max_persist_show, NULL); static DEVICE_ATTR(temp_over_count, 0664, tas25xx_bd_temp_over_count_show, NULL); static DEVICE_ATTR(exc_max_r, 0664, tas25xx_bd_exc_max_show, NULL); static DEVICE_ATTR(exc_max_persist_r, 0664, tas25xx_bd_exc_max_persist_show, NULL); static DEVICE_ATTR(exc_over_count_r, 0664, tas25xx_bd_exc_over_count_show, NULL); static DEVICE_ATTR(temp_max_r, 0664, tas25xx_bd_temp_max_show, NULL); static DEVICE_ATTR(temp_max_persist_r, 0664, tas25xx_bd_temp_max_persist_show, NULL); static DEVICE_ATTR(temp_over_count_r, 0664, tas25xx_bd_temp_over_count_show, NULL); static DEVICE_ATTR(le_flag_detected, 0664, tas25xx_le_flag_deteced_show, NULL); static DEVICE_ATTR(le_flag_count_persist, 0664, tas25xx_le_flag_count_persist_show, NULL); static DEVICE_ATTR(le_flag_count, 0664, tas25xx_le_flag_count_show, NULL); static DEVICE_ATTR(le_flag_detected_r, 0664, tas25xx_le_flag_deteced_show, NULL); static DEVICE_ATTR(le_flag_count_persist_r, 0664, tas25xx_le_flag_count_persist_show, NULL); static DEVICE_ATTR(le_flag_count_r, 0664, tas25xx_le_flag_count_show, NULL); static ssize_t tas25xx_le_flag_deteced_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; if (!p_tas25xx_algo) return -ENOMEM; if (attr == &dev_attr_le_flag_detected_r) { ret = snprintf(buf, MAX_STRING, "%d", p_tas25xx_algo->b_le_flag[1].le_flag_detected); } else { ret = snprintf(buf, MAX_STRING, "%d", p_tas25xx_algo->b_le_flag[0].le_flag_detected); } return ret; } static ssize_t tas25xx_le_flag_count_persist_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; if (!p_tas25xx_algo) return -ENOMEM; if (attr == &dev_attr_le_flag_count_persist_r) { ret = snprintf(buf, MAX_STRING, "%llu", p_tas25xx_algo->b_le_flag[1].le_flag_count_persist); } else { ret = snprintf(buf, MAX_STRING, "%llu", p_tas25xx_algo->b_le_flag[0].le_flag_count_persist); } return ret; } static ssize_t tas25xx_le_flag_count_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; if (!p_tas25xx_algo) return -ENOMEM; if (attr == &dev_attr_le_flag_count_r) { ret = snprintf(buf, MAX_STRING, "%lu", (unsigned long)p_tas25xx_algo->b_le_flag[1].le_flag_count); } else { ret = snprintf(buf, MAX_STRING, "%lu", (unsigned long)p_tas25xx_algo->b_le_flag[0].le_flag_count); } return ret; } static ssize_t tas25xx_bd_exc_max_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; if (attr == &dev_attr_exc_max_r) { ret = snprintf(buf, MAX_STRING, "%02d.%02d", (int32_t)trans_val_to_user_i(p_tas25xx_algo->b_data[1].exc_max, QFORMAT31), (int32_t)trans_val_to_user_m(p_tas25xx_algo->b_data[1].exc_max, QFORMAT31)); p_tas25xx_algo->b_data[1].exc_max = 0; } else { ret = snprintf(buf, MAX_STRING, "%02d.%02d", (int32_t)trans_val_to_user_i(p_tas25xx_algo->b_data[0].exc_max, QFORMAT31), (int32_t)trans_val_to_user_m(p_tas25xx_algo->b_data[0].exc_max, QFORMAT31)); p_tas25xx_algo->b_data[0].exc_max = 0; } return ret; } static ssize_t tas25xx_bd_exc_max_persist_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; if (attr == &dev_attr_exc_max_persist_r) { ret = snprintf(buf, MAX_STRING, "%02d.%02d", (int32_t)trans_val_to_user_i(p_tas25xx_algo->b_data[1].exc_max_persist, QFORMAT31), (int32_t)trans_val_to_user_m(p_tas25xx_algo->b_data[1].exc_max_persist, QFORMAT31)); } else { ret = snprintf(buf, MAX_STRING, "%02d.%02d", (int32_t)trans_val_to_user_i(p_tas25xx_algo->b_data[0].exc_max_persist, QFORMAT31), (int32_t)trans_val_to_user_m(p_tas25xx_algo->b_data[0].exc_max_persist, QFORMAT31)); } return ret; } static ssize_t tas25xx_bd_exc_over_count_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; if (attr == &dev_attr_exc_over_count_r) { ret = snprintf(buf, MAX_STRING, "%d", p_tas25xx_algo->b_data[1].exc_over_count); } else { ret = snprintf(buf, MAX_STRING, "%d", p_tas25xx_algo->b_data[0].exc_over_count); } return ret; } static ssize_t tas25xx_bd_temp_max_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; if (attr == &dev_attr_temp_max_r) { ret = snprintf(buf, MAX_STRING, "%d", p_tas25xx_algo->b_data[1].temp_max); p_tas25xx_algo->b_data[1].temp_max = 0; } else { ret = snprintf(buf, MAX_STRING, "%d", p_tas25xx_algo->b_data[0].temp_max); p_tas25xx_algo->b_data[0].temp_max = 0; } return ret; } static ssize_t tas25xx_bd_temp_max_persist_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; if (attr == &dev_attr_temp_max_persist_r) { ret = snprintf(buf, MAX_STRING, "%d", p_tas25xx_algo->b_data[1].temp_max_persist); } else { ret = snprintf(buf, MAX_STRING, "%d", p_tas25xx_algo->b_data[0].temp_max_persist); } return ret; } static ssize_t tas25xx_bd_temp_over_count_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; if (attr == &dev_attr_temp_over_count_r) { ret = snprintf(buf, MAX_STRING, "%d", p_tas25xx_algo->b_data[1].temp_over_count); } else { ret = snprintf(buf, MAX_STRING, "%d", p_tas25xx_algo->b_data[0].temp_over_count); } return ret; } static struct attribute *tas25xx_bd_attr[] = { &dev_attr_exc_max.attr, &dev_attr_exc_max_persist.attr, &dev_attr_exc_over_count.attr, &dev_attr_temp_max.attr, &dev_attr_temp_max_persist.attr, &dev_attr_temp_over_count.attr, &dev_attr_le_flag_detected.attr, &dev_attr_le_flag_count_persist.attr, &dev_attr_le_flag_count.attr, }; static struct attribute *tas25xx_bd_attr_r[] = { &dev_attr_exc_max_r.attr, &dev_attr_exc_max_persist_r.attr, &dev_attr_exc_over_count_r.attr, &dev_attr_temp_max_r.attr, &dev_attr_temp_max_persist_r.attr, &dev_attr_temp_over_count_r.attr, &dev_attr_le_flag_detected_r.attr, &dev_attr_le_flag_count_persist_r.attr, &dev_attr_le_flag_count_r.attr, }; static struct attribute *tas25xx_bd_attr_m[ ARRAY_SIZE(tas25xx_bd_attr) + ARRAY_SIZE(tas25xx_bd_attr_r) + 1] = {NULL}; static struct attribute_group tas25xx_bd_attr_grp = { .attrs = tas25xx_bd_attr_m, }; /*************BigData Related Codes End******************************/ static void clean_up_tas_sysfs(void) { if (p_tas25xx_algo) { if (p_tas25xx_algo->calib_dev) { sysfs_remove_group(&p_tas25xx_algo->calib_dev->kobj, &tas25xx_calib_attr_grp); device_destroy(p_tas25xx_algo->algo_class, 1); } if (p_tas25xx_algo->valid_dev) { sysfs_remove_group(&p_tas25xx_algo->valid_dev->kobj, &tas25xx_valid_attr_grp); device_destroy(p_tas25xx_algo->algo_class, 1); } if (p_tas25xx_algo->bd_dev) { sysfs_remove_group(&p_tas25xx_algo->bd_dev->kobj, &tas25xx_bd_attr_grp); device_destroy(p_tas25xx_algo->algo_class, 1); } if (p_tas25xx_algo->algo_class) class_destroy(p_tas25xx_algo->algo_class); kfree(p_tas25xx_algo); p_tas25xx_algo = NULL; } } static void update_dts_info(void) { if (!p_tas25xx_algo) { pr_err("[TI-SmartPA:%s] memory not allocated yet for p_tas25xx_algo", __func__); } p_tas25xx_algo->port = port; } void tas25xx_parse_algo_dt(struct device_node *np) { uint32_t data = 0; int32_t ret = 0; ret = of_property_read_u32(np, "ti,port_id", &data); if (ret) { pr_err("[TI-SmartPA:%s] Looking up %s property in node %s failed %d\n", __func__, "ti,port_id", np->full_name, ret); } else { port = data; pr_err("[TI-SmartPA:%s] ti,port_id=0x%x", __func__, data); } return; } void smartamp_add_algo(uint8_t channels) { int32_t ret = 0; pr_err("[TI-SmartPA:%s] Adding Smartamp algo functions, spk_count=%d", __func__, channels); mutex_init(&routing_lock); p_tas25xx_algo = kzalloc(sizeof(struct tas25xx_algo), GFP_KERNEL); if (p_tas25xx_algo == NULL) { pr_err("[TI-SmartPA:%s] No Memory", __func__); return; } p_tas25xx_algo->spk_count = channels; memcpy(tas25xx_calib_attr_m, tas25xx_calib_attr, sizeof(tas25xx_calib_attr)); memcpy(tas25xx_valid_attr_m, tas25xx_valid_attr, sizeof(tas25xx_valid_attr)); memcpy(tas25xx_bd_attr_m, tas25xx_bd_attr, sizeof(tas25xx_bd_attr)); if (channels == 2) { memcpy(tas25xx_calib_attr_m + ARRAY_SIZE(tas25xx_calib_attr), tas25xx_calib_attr_r, sizeof(tas25xx_calib_attr_r)); memcpy(tas25xx_valid_attr_m + ARRAY_SIZE(tas25xx_valid_attr), tas25xx_valid_attr_r, sizeof(tas25xx_valid_attr_r)); memcpy(tas25xx_bd_attr_m + ARRAY_SIZE(tas25xx_bd_attr), tas25xx_bd_attr_r, sizeof(tas25xx_bd_attr_r)); } update_dts_info(); p_tas25xx_algo->algo_class = class_create(THIS_MODULE, TAS25XX_SYSFS_CLASS_NAME); if (IS_ERR(p_tas25xx_algo->algo_class)) { ret = PTR_ERR(p_tas25xx_algo->algo_class); pr_err("[TI-SmartPA:%s] err class create\n", __func__); p_tas25xx_algo->algo_class = NULL; goto err_dev; } p_tas25xx_algo->calib_dev = device_create(p_tas25xx_algo->algo_class, NULL, 1, NULL, TAS25XX_CALIB_DIR_NAME); if (IS_ERR(p_tas25xx_algo->calib_dev)) { pr_err("[TI-SmartPA:%s]Failed to create calib_dev\n", __func__); ret = PTR_ERR(p_tas25xx_algo->calib_dev); p_tas25xx_algo->calib_dev = NULL; goto err_dev; } ret = sysfs_create_group(&p_tas25xx_algo->calib_dev->kobj, &tas25xx_calib_attr_grp); if (ret) { pr_err("[TI-SmartPA:%s]Failed to create sysfs group\n", __func__); goto err_dev; } INIT_DELAYED_WORK(&p_tas25xx_algo->calib_work, calib_work_routine); p_tas25xx_algo->valid_dev = device_create(p_tas25xx_algo->algo_class, NULL, 1, NULL, TAS25XX_VALID_DIR_NAME); if (IS_ERR(p_tas25xx_algo->valid_dev)) { pr_err("[TI-SmartPA:%s]Failed to create valid_dev\n", __func__); ret = PTR_ERR(p_tas25xx_algo->valid_dev); p_tas25xx_algo->valid_dev = NULL; goto err_dev; } ret = sysfs_create_group(&p_tas25xx_algo->valid_dev->kobj, &tas25xx_valid_attr_grp); if (ret) { pr_err("[TI-SmartPA:%s]Failed to create sysfs group\n", __func__); p_tas25xx_algo->valid_dev = NULL; goto err_dev; } INIT_DELAYED_WORK(&p_tas25xx_algo->valid_work, valid_work_routine); p_tas25xx_algo->bd_dev = device_create(p_tas25xx_algo->algo_class, NULL, 1, NULL, TAS25XX_BD_DIR_NAME); if (IS_ERR(p_tas25xx_algo->bd_dev)) { pr_err("[TI-SmartPA:%s]Failed to create bd_dev\n", __func__); ret = PTR_ERR(p_tas25xx_algo->bd_dev); p_tas25xx_algo->bd_dev = NULL; goto err_dev; } ret = sysfs_create_group(&p_tas25xx_algo->bd_dev->kobj, &tas25xx_bd_attr_grp); if (ret) { pr_err("[TI-SmartPA:%s]Failed to create sysfs group\n", __func__); p_tas25xx_algo->bd_dev = NULL; goto err_dev; } pr_info("[TI-SmartPA:%s] ret=%d", __func__, ret); return; err_dev: clean_up_tas_sysfs(); pr_err("[TI-SmartPA:%s] Error %d", __func__, ret); } void smartamp_remove_algo(void) { pr_info("[TI-SmartPA:%s] Removing Smartamp Algorithm functions", __func__); clean_up_tas_sysfs(); mutex_destroy(&routing_lock); } MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("TAS25XX Algorithm"); MODULE_LICENSE("GPL v2"); #endif /*CONFIG_TAS25XX_ALGO*/