kernel_samsung_a53x/sound/soc/codecs/tas2562/tas25xx-algo.c
2024-06-15 16:02:09 -03:00

1154 lines
32 KiB
C
Executable file

/* 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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/kthread.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <sound/smart_amp.h>
#include <linux/power_supply.h>
#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*/