diff --git a/Documentation/devicetree/bindings/sound/tas2560-algo.txt b/Documentation/devicetree/bindings/sound/tas2560-algo.txt new file mode 100644 index 000000000000..0de051b46676 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tas2560-algo.txt @@ -0,0 +1,14 @@ +Tas2560 algo driver + +Required properties: + + - compatible : "ti,tas2560-algo" + + - tas2560-port-id : afe port id for the mi2s used for tas2560. + +Example: + +ti,tas2560-algo { + compatible = "ti,tas2560-algo"; + ti,tas2560-port-id = <0x1016>, <0x1017>; +}; diff --git a/include/sound/tas2560_algo.h b/include/sound/tas2560_algo.h new file mode 100644 index 000000000000..cc32681ad37f --- /dev/null +++ b/include/sound/tas2560_algo.h @@ -0,0 +1,130 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments Inc. +** +** This program is free software; you can redistribute it and/or modify it under +** the terms of the GNU General Public License as published by the Free Software +** Foundation; version 2. +** +** 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 +** File: +** tas2560_algo.h +** +** Description: +** Parameter IDs, Control IDs and Functions to communicate with tas2560 +** algo Module present in AFE. +** +** ============================================================================ +*/ + +#ifndef _TAS2560_ALGO_H +#define _TAS2560_ALGO_H + +#include +#include +#include +#include + +/* Below 4 IDs should be same as in CAPIv2 tas2560 algo Module */ +#define AFE_PARAM_ID_TAS2560_ALGO_RX_ENABLE 0x00012D13 +#define AFE_PARAM_ID_TAS2560_ALGO_TX_ENABLE 0x00012D14 +#define AFE_PARAM_ID_TAS2560_ALGO_RX_CFG 0x00012D15 +#define AFE_PARAM_ID_TAS2560_ALGO_TX_CFG 0x00012D16 + +/* Below 2 IDs should be same as in CAPIv2 tas2560 algo Module */ +#define AFE_TAS2560_ALGO_MODULE_RX 0x1000FC00 +#define AFE_TAS2560_ALGO_MODULE_TX 0x1000FD00 + +#define MAX_TAS2560_ALGO_PARAM_INDEX 600 + +#define TAS2560_ALGO_GET_PARAM 1 +#define TAS2560_ALGO_SET_PARAM 0 +#define TAS2560_ALGO_PAYLOAD_SIZE 14 +#define TAS2560_ALGO_RX_PORT_ID 0x1016 /*Quinary Port-Rx*/ +#define TAS2560_ALGO_TX_PORT_ID 0x1017 /*Quinary Port-Tx*/ +/*I2C Slave addresses, 0x98 is used in Mono-Case*/ +#define SLAVE1 0x98 +#define SLAVE2 0x9A +#define SLAVE3 0x9C +#define SLAVE4 0x9E +#define SLAVE_BASE 0x96 +#define GET_SLAVE(X) ((X * 2) + SLAVE_BASE) + +/*(Page,Offset) -> Parameter Index Formula*/ +#define TAS2560_ALGO_GET_IDX(X1, X2) \ + ((X1 - 1) * 30 + (X2 - 8) / 4) +/*Special parameter ranges from 3810 to 3899 +* (these are handled in capiv2 framework and doesn't reach core algorithm) +*/ +#define TAS2560_ALGO_IS_SPL_IDX(X) \ + ((((X) >= 3810) && ((X) < 3899)) ? 1 : 0) +/*Param Id to be sent to capiv2 library +* |31 . . . . 24|23 . . . . . 24|15 . . . . . . . . . . 0| +* |Instance ID |Length |Parameter Index | +*/ +#define TAS2560_ALGO_CALC_PIDX(X1, X2, X3, X4) \ + (X1 | X2 | (X3 << 16) | (((X4 - SLAVE_BASE) / 2) << 24)) +#define TAS2560_ALGO_MIN(X1, X2) \ + ((X1 < X2) ? (X1) : (X2)) + +#define TAS2560_ALGO_CMD 0 +#define TAS2560_ALGO_CMD_SET_RE 1 + +/*Param ID to bypass algorithm dynamically*/ +#define TAS2560_ALGO_CFG_ENABLE 415 + +/*Param ID to disable/enable FB*/ +#define TAS2560_ALGO_FB 101 +#define TAS2560_ALGO_FB_ENABLE (0xc0000000) +#define TAS2560_ALGO_FB_DISABLE (0x40000000) +#define TAS2560_ALGO_SWAP 413 + + +/*Special parameter ranges from 3810 to 3899 +* (these are handled in capiv2 framework and doesn't reach core algorithm) +*/ +/*Read F0*/ +#define TAS2560_ALGO_GET_F0 3810 +/*Read Q*/ +#define TAS2560_ALGO_GET_Q 3811 +/* Tv */ +#define TAS2560_ALGO_GET_TV 3812 +/*Read Rdc*/ +#define TAS2560_ALGO_GET_RDC 3813 +/*Start Command for Calibration*/ +#define TAS2560_ALGO_CALIB_START_RDC 3814 +/*Stop Command for Calibration*/ +#define TAS2560_ALGO_CALIB_STOP_RDC 3815 +/*Set Calibrated Rdc*/ +#define TAS2560_ALGO_CFG_SET_RE 3816 +/*Start Command for F0,Q test*/ +#define TAS2560_ALGO_CALIB_START_F0_Q 3817 +/*Stop Command for F0,Q test*/ +#define TAS2560_ALGO_CALIB_STOP_F0_Q 3818 +/*Set Profile(Music(0),Ringtone(1)...etc)*/ +#define TAS2560_ALGO_PROFILE 3819 + + +struct afe_tas2560_algo_params_t { + uint32_t payload[TAS2560_ALGO_PAYLOAD_SIZE]; +} __packed; + +struct afe_tas2560_algo_set_config_t { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 data; +} __packed; + +struct afe_tas2560_algo_get_config_t { + struct apr_hdr hdr; + struct afe_port_cmd_get_param_v2 param; + struct afe_port_param_data_v2 data; +} __packed; + +int tas2560_algo_routing_init(struct snd_soc_pcm_runtime *rtd); + +int afe_tas2560_algo_ctrl(u8 *data, u32 param_id, + u32 module_id, u8 dir, u8 size); +#endif diff --git a/sound/soc/codecs/Android.mk b/sound/soc/codecs/Android.mk index daca267bde87..c933450725bb 100644 --- a/sound/soc/codecs/Android.mk +++ b/sound/soc/codecs/Android.mk @@ -71,3 +71,15 @@ LOCAL_MODULE := tas2560.ko LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := tas2560_algo.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := tas2560_calib.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/sound/soc/codecs/Kbuild b/sound/soc/codecs/Kbuild index f5729e9591b4..6146a98acb02 100644 --- a/sound/soc/codecs/Kbuild +++ b/sound/soc/codecs/Kbuild @@ -15,7 +15,7 @@ EXTRA_CFLAGS += -I$(TOP)/kernel/techpack/audio/include/uapi endif EXTRA_CFLAGS += -DCONFIG_SND_SOC_AOV_TRIGGER -DCONFIG_SND_SOC_OPALUM -EXTRA_CFLAGS += -DCONFIG_TAS2560_REGMAP -DCONFIG_TAS2560_CODEC -DCONFIG_TAS2560_MISC +EXTRA_CFLAGS += -DCONFIG_TAS2560_REGMAP -DCONFIG_TAS2560_CODEC -DCONFIG_TAS2560_MISC -DCONFIG_SND_SOC_TAS2560 ifeq ($(KDIR),) PLMA_HOSTSW_DIR=$(realpath -f $(srctree)/$(src)) @@ -71,3 +71,9 @@ obj-m += snd_soc_tfa9874.o tas2560-objs = tas2560-core.o tas2560-misc.o tas2560-codec.o tas2560-regmap.o obj-m += tas2560.o + +tas2560_algo-objs = tas2560-algo.o +obj-m += tas2560_algo.o + +tas2560_calib-objs = tas2560-calib.o +obj-m += tas2560_calib.o diff --git a/sound/soc/codecs/tas2560-algo.c b/sound/soc/codecs/tas2560-algo.c new file mode 100644 index 000000000000..769e6fad40d6 --- /dev/null +++ b/sound/soc/codecs/tas2560-algo.c @@ -0,0 +1,830 @@ +/* Copyright (c) 2012-2017, 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. + */ + +#ifdef CONFIG_SND_SOC_TAS2560 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mutex routing_lock; +/*Port IDs to be read from dts*/ +static uint32_t tx_port_id = TAS2560_ALGO_TX_PORT_ID; +static uint32_t rx_port_id = TAS2560_ALGO_RX_PORT_ID; +/*Not much significance and can be removed in future*/ +static uint8_t calib_status; +static int32_t force_algo_disable; + +/*Pointer to hold the data received from DSP +* TODO:Decide whether it needs memory or pointer is sufficient +*/ +struct afe_tas2560_algo_params_t *tas2560_algo_payload; + +/*Call back to receive data from DSP*/ +static int32_t tas2560_algo_afe_callback(struct apr_client_data *data) +{ + if (!data) { + pr_err("TAS2560_ALGO:%s Invalid param data\n", __func__); + return -EINVAL; + } + + if (data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V2) { + uint32_t *payload32 = data->payload; + + if ((payload32[1] == AFE_TAS2560_ALGO_MODULE_RX) || + (payload32[1] == AFE_TAS2560_ALGO_MODULE_TX)) { + tas2560_algo_payload = + (struct afe_tas2560_algo_params_t *)&payload32[4]; + } + } + return 0; +} + +/*AFE set param to DSP*/ +static int tas2560_algo_afe_set_param(uint32_t param_id, uint32_t module_id, + struct afe_tas2560_algo_set_config_t *arg1) +{ + int result = 0; + int index = 0; + int size = 0; + uint32_t port_id = rx_port_id; + struct afe_tas2560_algo_set_config_t *config = NULL; + struct afe_tas2560_algo_params_t *data_payload = NULL; + + if (module_id == AFE_TAS2560_ALGO_MODULE_TX) + port_id = tx_port_id; + + if ((q6audio_validate_port(port_id) < 0)) { + pr_err("TAS2560_ALGO:%s invalid port %d", __func__, port_id); + return -EINVAL; + } + + index = afe_get_port_index(port_id); + + /* Allocate memory for the message */ + size = sizeof(struct afe_tas2560_algo_set_config_t) + + sizeof(struct afe_tas2560_algo_params_t); + config = kzalloc(size, GFP_KERNEL); + if (config == NULL) { + pr_err("TAS2560_ALGO:%s Memory allocation failed!\n", + __func__); + return -ENOMEM; + } + data_payload = (struct afe_tas2560_algo_params_t *) + ((u8 *)config + sizeof(struct afe_tas2560_algo_set_config_t)); + + /* Configure actual parameter settings */ + if (arg1) + memcpy(data_payload->payload, arg1, + sizeof(struct afe_tas2560_algo_params_t)); + else + pr_err("TAS2560_ALGO:%s arg1 Null pointer received\n", + __func__); + + /* Set header section */ + config->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config->hdr.pkt_size = size; + config->hdr.src_svc = APR_SVC_AFE; + config->hdr.src_domain = APR_DOMAIN_APPS; + config->hdr.src_port = 0; + config->hdr.dest_svc = APR_SVC_AFE; + config->hdr.dest_domain = APR_DOMAIN_ADSP; + config->hdr.dest_port = 0; + config->hdr.token = index; + config->hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + + /* Set param section */ + config->param.port_id = port_id; + config->param.payload_size = + sizeof(struct afe_port_param_data_v2) + + sizeof(struct afe_tas2560_algo_params_t); + config->param.payload_address_lsw = 0; + config->param.payload_address_msw = 0; + config->param.mem_map_handle = 0; + + /* Set data section */ + config->data.module_id = module_id; + config->data.param_id = param_id; + config->data.param_size = sizeof(struct afe_tas2560_algo_params_t); + config->data.reserved = 0; + + result = tas2560_algo_afe_apr_send_pkt(config, index); + if (result) { + pr_debug("TAS2560_ALGO:%s port %d failed with code %d\n", + __func__, port_id, result); + } else { + pr_debug("TAS2560_ALGO:%s packet 0x%08X to module 0x%08X\n", + __func__, param_id, module_id); + } + kfree(config); + return result; +} + +/*AFE get param to DSP*/ +static int tas2560_algo_afe_get_param(uint32_t param_id, uint32_t module_id) +{ + int result = 0; + int index = 0; + int size = 0; + uint32_t port_id = rx_port_id; + struct afe_tas2560_algo_get_config_t *config = NULL; + + if (module_id == AFE_TAS2560_ALGO_MODULE_TX) + port_id = tx_port_id; + + if ((q6audio_validate_port(port_id) < 0)) { + pr_err("TAS2560_ALGO:%s invalid port %d", __func__, port_id); + return -EINVAL; + } + + index = afe_get_port_index(port_id); + + /* Allocate memory for the message */ + size = sizeof(struct afe_tas2560_algo_get_config_t); + config = kzalloc(size, GFP_KERNEL); + if (config == NULL) { + pr_err("TAS2560_ALGO:%s Memory allocation failed!\n", + __func__); + return -ENOMEM; + } + + /* Set header section */ + config->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config->hdr.pkt_size = size; + config->hdr.src_svc = APR_SVC_AFE; + config->hdr.src_domain = APR_DOMAIN_APPS; + config->hdr.src_port = 0; + config->hdr.dest_svc = APR_SVC_AFE; + config->hdr.dest_domain = APR_DOMAIN_ADSP; + config->hdr.dest_port = 0; + config->hdr.token = index; + config->hdr.opcode = AFE_PORT_CMD_GET_PARAM_V2; + + /* Set param section */ + config->param.port_id = port_id; + config->param.payload_size = + sizeof(struct afe_port_param_data_v2); + config->param.payload_address_lsw = 0; + config->param.payload_address_msw = 0; + config->param.mem_map_handle = 0; + config->param.module_id = module_id; + config->param.param_id = param_id; + + /* Set data section */ + config->data.module_id = module_id; + config->data.param_id = param_id; + config->data.reserved = 0; + + /* payload and param size section */ + config->param.payload_size += + sizeof(struct afe_tas2560_algo_params_t); + config->data.param_size = + sizeof(struct afe_tas2560_algo_params_t); + + result = tas2560_algo_afe_apr_send_pkt(config, index); + if (result) { + pr_debug("TAS2560_ALGO:%s for port %d failed with code %d\n", + __func__, port_id, result); + } else { + pr_debug("TAS2560_ALGO:%s packet 0x%08X to module 0x%08X\n", + __func__, param_id, module_id); + } + kfree(config); + return result; +} + +/*Wrapper to handle get set params*/ +static int tas2560_algo_get_set(u8 *user_data, uint32_t param_id, + uint32_t module_id, uint8_t get_set, int32_t length) +{ + int32_t ret = 0; + + switch (get_set) { + case TAS2560_ALGO_SET_PARAM: + pr_err("TAS2560_ALGO:%s set param\n", __func__); + ret = tas2560_algo_afe_set_param(param_id, module_id, + (struct afe_tas2560_algo_set_config_t *)user_data); + break; + case TAS2560_ALGO_GET_PARAM: + pr_err("TAS2560_ALGO:%s get param\n", __func__); + tas2560_algo_payload = NULL; + tas2560_algo_afe_set_callback(tas2560_algo_afe_callback); + ret = tas2560_algo_afe_get_param(param_id, module_id); + if ((user_data) && (tas2560_algo_payload)) + memcpy(user_data, tas2560_algo_payload, + TAS2560_ALGO_MIN(length, + sizeof(tas2560_algo_payload))); + else + pr_err("TAS2560_ALGO:%s GetParam Failed\n", + __func__); + break; + default: + pr_err("TAS2560_ALGO:%s Invalid case\n", __func__); + ret = -EINVAL; + break; + } + return ret; +} +/*Wrapper to control(with mutex) the get set params*/ +int afe_tas2560_algo_ctrl(u8 *data, u32 param_id, u32 module_id, u8 dir, + u8 size) +{ + int32_t ret = 0; + + mutex_lock(&routing_lock); + ret = tas2560_algo_get_set(data, param_id, module_id, dir, size); + mutex_unlock(&routing_lock); + + return ret; +} +EXPORT_SYMBOL(afe_tas2560_algo_ctrl); +/* FB Info */ + +/*Control:1 Start/Stop commands for Rdc-Calibration/F0-Q-test */ +static const char * const tas2560_algo_calib_text[] = { + "NONE", + "CALIB_START_RDC", + "CALIB_STOP_RDC", + "CALIB_START_F0_Q", + "CALIB_STOP_F0_Q" +}; + +static const struct soc_enum tas2560_algo_calib_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_calib_text), + tas2560_algo_calib_text) +}; +/*Can be ignored*/ +static int tas2560_algo_calib_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = calib_status; + pr_err("TAS2560_ALGO:%s data %d\n", __func__, calib_status); + return 0; +} + +static int tas2560_algo_calib_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + ((int32_t *)buff)[0] = 1; + calib_status = ucontrol->value.integer.value[0]; + switch (calib_status) { + case 0: + pr_err("TAS2560_ALGO:%s NONE\n", __func__); + break; + case 1: + paramid = TAS2560_ALGO_CALC_PIDX(paramid, + TAS2560_ALGO_CALIB_START_RDC, 1, SLAVE1); + pr_err("TAS2560_ALGO:%s CALIB_START_RDC paramid 0x%x\n", + __func__, paramid); + break; + case 2: + paramid = TAS2560_ALGO_CALC_PIDX(paramid, + TAS2560_ALGO_CALIB_STOP_RDC, 1, SLAVE1); + pr_err("TAS2560_ALGO:%s CALIB_STOP_RDC paramid 0x%x\n", + __func__, paramid); + break; + case 3: + pr_err("TAS2560_ALGO:%s CALIB_START_F0_Q Not Implemented\n", + __func__); + break; + case 4: + pr_err("TAS2560_ALGO:%s CALIB_STOP_F0_Q Not Implemented\n", + __func__); + break; + default: + pr_err("TAS2560_ALGO:%s Invalid Case 0x%x\n", __func__, + paramid); + ret = -EINVAL; + break; + } + + if (paramid) + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + return ret; +} + +/*Control:2 Command to get Rdc result after Rdc-Calibration*/ +static int tas2560_algo_get_re(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + int32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_GET_RDC, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + + pr_err("TAS2560_ALGO:%s Rdc 0x%x\n", __func__, + (unsigned int)ucontrol->value.integer.value[0]); + return ret; +} + +/*Control:3 Command to get F0 result after F0-Q-test*/ +static int tas2560_algo_get_f0(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_GET_F0, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + + pr_err("TAS2560_ALGO:%s F0 0x%x\n", __func__, + (unsigned int)ucontrol->value.integer.value[0]); + return ret; +} + +/*Control:4 Command to get Q result after F0-Q-test*/ +static int tas2560_algo_get_q(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_GET_Q, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + + pr_err("TAS2560_ALGO:%s Q 0x%x\n", __func__, + (unsigned int)ucontrol->value.integer.value[0]); + return ret; +} + +/*Control:5 Command to bypass algorithm dynaically*/ +static const char * const tas2560_algo_text[] = { + "DISABLE", + "ENABLE" +}; + +static const struct soc_enum tas2560_algo_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_text), tas2560_algo_text) +}; + + +static int tas2560_algo_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_CFG_ENABLE, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + pr_err("TAS2560_ALGO:%s status data %d", __func__, + ((int32_t *)buff)[0]); + return ret; +} + +static int tas2560_algo_set_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + ((int32_t *)buff)[0] = ucontrol->value.integer.value[0]; + pr_err("TAS2560_ALGO:%s status data %d", __func__, + ((int32_t *)buff)[0]); + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_CFG_ENABLE, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + return ret; +} + +/*Control:6 Command to set calibrated Rdc*/ +static int tas2560_algo_set_cal(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_TX; + u8 buff[56] = {0}; + u8 *ptr = buff; + u8 re_count = 0, i = 0; + + /*If algo is forced to be disable, we do nothing here*/ + if (force_algo_disable) { + pr_err("TAS2560_ALGO,%s,the algo is force disabled,%d\n", + __func__, force_algo_disable); + return ret; + } + + /*Count is ignored as it is mono*/ + pr_err("TAS2560_ALGO:%s [0] = %ld [1] = %ld\n", __func__, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]); + re_count = ucontrol->value.integer.value[0]; + + /*Sending TX parameters + *TODO:Values of left-I,right-V or left-V,right-I + *can be made generic by reading from dts file + */ + pr_err("TAS2560_ALGO:%s Sending Tx-FB info", __func__); + ret = afe_spk_prot_feed_back_cfg(tx_port_id, rx_port_id, + 1, 0, 1); + + if (!ret) { + pr_err("TAS2560_ALGO:%s Sending Tx-Enable", __func__); + paramid = AFE_PARAM_ID_TAS2560_ALGO_TX_ENABLE; + ((int32_t *)buff)[0] = 1; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + } else + pr_err("TAS2560_ALGO:%s Sending Tx-Enable already enabled\n", + __func__); + + /*Sending RX parameters*/ + moduleid = AFE_TAS2560_ALGO_MODULE_RX; + pr_err("TAS2560_ALGO:%s Sending Rx-Enable data", __func__); + paramid = AFE_PARAM_ID_TAS2560_ALGO_RX_ENABLE; + ((int32_t *)buff)[0] = 1; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + if (!ret) { + pr_err("TAS2560_ALGO:%s Sending Rx-Cfg", __func__); + paramid = AFE_PARAM_ID_TAS2560_ALGO_RX_CFG; + ((int32_t *)buff)[0] = 1; + afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + + pr_err("TAS2560_ALGO:%s Sending Rdc", __func__); + /*TODO:Note multi channel send Rdc is not verified*/ + for (i = 0; i < re_count; i++) { + paramid = TAS2560_ALGO_CALC_PIDX(0, + TAS2560_ALGO_CFG_SET_RE, 1, GET_SLAVE(i+1)); + ((int32_t *)buff)[0] = + ucontrol->value.integer.value[i+1]; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + } + } else + pr_err("TAS2560_ALGO:%s Sending Rx-Enable failed", __func__); + return ret; +} + +/*Control:7 Command to Enable FF module*/ +static const char * const tas2560_algo_ff_module_text[] = { + "DISABLE", + "ENABLE" +}; + +static const struct soc_enum tas2560_algo_ff_module_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_ff_module_text), + tas2560_algo_ff_module_text) +}; + + +static int tas2560_algo_get_ff_module(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + int32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = AFE_PARAM_ID_TAS2560_ALGO_RX_ENABLE; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + pr_err("TAS2560_ALGO:%s Recieving Rx-Enable data %d", __func__, + ((int32_t *)buff)[0]); + return ret; +} + +static int tas2560_algo_set_ff_module(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + ((int32_t *)buff)[0] = ucontrol->value.integer.value[0]; + pr_err("TAS2560_ALGO:%s Sending Rx-Enable data %d", __func__, + ((int32_t *)buff)[0]); + + if ((force_algo_disable) && (((int32_t *)buff)[0])) { + pr_err("TAS2560_ALGO:%s,try to enable ff while force disabled", + __func__); + return ret; + } + + paramid = AFE_PARAM_ID_TAS2560_ALGO_RX_ENABLE; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + + return ret; +} +/*Control:8 Command to Enable FB module*/ +static const char * const tas2560_algo_fb_module_text[] = { + "DISABLE", + "ENABLE" +}; + +static const struct soc_enum tas2560_algo_fb_module_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_fb_module_text), + tas2560_algo_fb_module_text) +}; + + +static int tas2560_algo_get_fb_module(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + int32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_TX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = AFE_PARAM_ID_TAS2560_ALGO_TX_ENABLE; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + pr_err("TAS2560_ALGO:%s Recieving Tx-Enable data %d", __func__, + ((int32_t *)buff)[0]); + return ret; +} + +static int tas2560_algo_set_fb_module(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_TX; + u8 buff[56] = {0}; + u8 *ptr = buff; + ((int32_t *)buff)[0] = ucontrol->value.integer.value[0]; + + pr_err("TAS2560_ALGO:%s Sending Tx-Enable data %d", __func__, + ((int32_t *)buff)[0]); + + if ((force_algo_disable) && (((int32_t *)buff)[0])) { + pr_err("TAS2560_ALGO:%s,try to enable fb while force disabled", + __func__); + return ret; + } + + paramid = AFE_PARAM_ID_TAS2560_ALGO_TX_ENABLE; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + + return ret; +} +/*Control:9 Command to Set Profile*/ +/*TODO:Profile count can be extended, no implementation restrictions*/ +static const char * const tas2560_algo_profile_text[] = { + "NORMAL", + "VOICE", + "RING", + "SONIFICATION", + "CALIBRATION" +}; + +static const struct soc_enum tas2560_algo_profile_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_profile_text), + tas2560_algo_profile_text) +}; + + +static int tas2560_algo_get_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + int32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_PROFILE, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + pr_debug("TAS2560_ALGO:%s %ld\n", __func__, + ucontrol->value.integer.value[0]); + + return ret; +} + +static int tas2560_algo_set_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + ((int32_t *)buff)[0] = ucontrol->value.integer.value[0]; + pr_debug("TAS2560_ALGO: %s %ld\n", + __func__, ucontrol->value.integer.value[0]); + paramid = TAS2560_ALGO_CALC_PIDX(paramid, + TAS2560_ALGO_PROFILE, 1, SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + return ret; +} + +/*Control:10 Command to disable TAS2560 ALGO to be enabled by audio hal, +* used only for factory mode +*/ +static const char * const tas2560_algo_disable_text[] = { + "FALSE", + "TRUE" +}; + +static const struct soc_enum tas2560_algo_disable_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_disable_text), + tas2560_algo_disable_text) +}; + + +static int tas2560_algo_get_disable(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = force_algo_disable; + pr_err("TAS2560_ALGO:%s force_algo_disable=%d", + __func__, force_algo_disable); + return 0; +} + +static int tas2560_algo_set_disable(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + force_algo_disable = ucontrol->value.integer.value[0]; + pr_err("TAS2560_ALGO:%s force_algo_disable=%d", + __func__, force_algo_disable); + return 0; +} + +static int tas2560_algo_get_tv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}, *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, + TAS2560_ALGO_GET_TV, 1, SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, + paramid, moduleid, TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + + pr_err("TAS2560_ALGO:%s TV 0x%x\n", __func__, + (unsigned int)ucontrol->value.integer.value[0]); + return ret; +} + +const struct snd_kcontrol_new tas2560_algo_filter_mixer_controls[] = { + SOC_ENUM_EXT("TAS2560_ALGO_CALIB", tas2560_algo_calib_enum[0], + tas2560_algo_calib_get, tas2560_algo_calib_set), + SOC_SINGLE_EXT("TAS2560_ALGO_GET_RE", SND_SOC_NOPM, 0, 0x7FFFFFFF, 0, + tas2560_algo_get_re, NULL), + SOC_SINGLE_EXT("TAS2560_ALGO_GET_F0", SND_SOC_NOPM, 0, 0x7FFFFFFF, 0, + tas2560_algo_get_f0, NULL), + SOC_SINGLE_EXT("TAS2560_ALGO_GET_Q", SND_SOC_NOPM, 0, 0x7FFFFFFF, 0, + tas2560_algo_get_q, NULL), + SOC_SINGLE_EXT("TAS2560_ALGO_GET_TV", SND_SOC_NOPM, 0, 0x7FFFFFFF, 0, + tas2560_algo_get_tv, NULL), + SOC_SINGLE_MULTI_EXT("TAS2560_ALGO_CMD_SEND_CAL", SND_SOC_NOPM, 0, + 0x7FFFFFFF, 0, 5, NULL, tas2560_algo_set_cal), + SOC_ENUM_EXT("TAS2560_ALGO_STATUS", tas2560_algo_enum[0], + tas2560_algo_get_status, tas2560_algo_set_status), + SOC_ENUM_EXT("TAS2560_ALGO_FF_MODULE", tas2560_algo_ff_module_enum[0], + tas2560_algo_get_ff_module, tas2560_algo_set_ff_module), + SOC_ENUM_EXT("TAS2560_ALGO_FB_MODULE", tas2560_algo_fb_module_enum[0], + tas2560_algo_get_fb_module, tas2560_algo_set_fb_module), + SOC_ENUM_EXT("TAS2560_ALGO_PROFILE", tas2560_algo_profile_enum[0], + tas2560_algo_get_profile, tas2560_algo_set_profile), + SOC_ENUM_EXT("TAS2560_ALGO_DISABLE", tas2560_algo_disable_enum[0], + tas2560_algo_get_disable, tas2560_algo_set_disable) +}; + +/* +* tas2560_algo_routing_init +* - register mixer controls +*/ + +int tas2560_algo_routing_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + + /* add the codec controls here */ + mutex_init(&routing_lock); + + snd_soc_add_codec_controls(codec, tas2560_algo_filter_mixer_controls, + ARRAY_SIZE(tas2560_algo_filter_mixer_controls)); + + return 0; +} +EXPORT_SYMBOL(tas2560_algo_routing_init); + +static int tas2560_algo_probe(struct platform_device *pdev) +{ + int ret = 0; + uint32_t num_entries = 0; + uint32_t *val_array = NULL; + + pr_debug("TAS2560_ALGO:%s Enter.\n", __func__); + + mutex_init(&routing_lock); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + + num_entries = of_property_count_u32_elems(pdev->dev.of_node, + "ti,tas2560-port-id"); + + if (num_entries > 0) { + val_array = kcalloc(num_entries, sizeof(uint32_t), GFP_KERNEL); + if (!val_array) { + pr_err("TAS2560_ALGO:%s malloc fail\n", __func__); + ret = -ENOMEM; + goto err; + } + ret = of_property_read_u32_array(pdev->dev.of_node, + "ti,tas2560-port-id", val_array, num_entries); + rx_port_id = (uint32_t)val_array[0]; + tx_port_id = (uint32_t)val_array[1]; + pr_err("TAS2560_ALGO:%s rx_port_id = 0x%x tx_port_id = 0x%x\n", + __func__, rx_port_id, tx_port_id); + } + kfree(val_array); +err: + return ret; +} + +static int tas2560_algo_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id tas2560_algo_of_match[] = { + { .compatible = "ti,tas2560-algo" }, + { }, +}; +MODULE_DEVICE_TABLE(of, tas2560_algo_of_match); + +static struct platform_driver tas2560_algo_driver = { + .probe = tas2560_algo_probe, + .remove = tas2560_algo_remove, + .driver = { + .name = "tas2560-algo", + .of_match_table = tas2560_algo_of_match, + }, +}; + +static int __init tas2560_algo_init(void) +{ + return platform_driver_register(&tas2560_algo_driver); +} +module_init(tas2560_algo_init); + +static void __exit tas2560_algo_exit(void) +{ + platform_driver_unregister(&tas2560_algo_driver); +} +module_exit(tas2560_algo_exit); + + +MODULE_DESCRIPTION("TAS2560 ALSA SOC Smart Amplifier algo driver"); +MODULE_LICENSE("GPL v2"); +#endif /* CONFIG_SND_SOC_TAS2560 */ + diff --git a/sound/soc/codecs/tas2560-calib.c b/sound/soc/codecs/tas2560-calib.c new file mode 100644 index 000000000000..176b0357a0c5 --- /dev/null +++ b/sound/soc/codecs/tas2560-calib.c @@ -0,0 +1,200 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments Inc. +** +** This program is free software; you can redistribute it and/or modify it under +** the terms of the GNU General Public License as published by the Free Software +** Foundation; version 2. +** +** 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 +** File: +** tas2560-calib.c +** +** Description: +** Dsp control driver for Texas Instruments TAS2560 High Performance Smart +** Amplifier Algorithm running as CAPIv2 Module in AFE. +** +** ============================================================================= +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Holds the Packet data required for processing */ +struct tas_dsp_pkt { + u8 slave_id; + u8 book; + u8 page; + u8 offset; + u8 data[TAS2560_ALGO_PAYLOAD_SIZE * sizeof(u32)]; +}; + +static int tas_params_ctrl(uint8_t *input, u8 dir, u8 count) +{ + u32 length = count / sizeof(u32); + u32 paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u32 index; + int ret = 0; + int special_index = 0; + struct tas_dsp_pkt *ppacket; + + ppacket = kmalloc(sizeof(struct tas_dsp_pkt), + GFP_KERNEL); + if (!ppacket) + return -ENOMEM; + + memset(ppacket, 0, sizeof(struct tas_dsp_pkt)); + ret = copy_from_user(ppacket, input, sizeof(struct tas_dsp_pkt)); + if (ret) { + pr_err("TAS2560_ALGO:%s Error copying from user\n", __func__); + kfree(ppacket); + return -EFAULT; + } + index = TAS2560_ALGO_GET_IDX(ppacket->page, ppacket->offset); + special_index = TAS2560_ALGO_IS_SPL_IDX(index); + if (special_index == 0) { + if ((index < 0 || index > MAX_TAS2560_ALGO_PARAM_INDEX)) { + pr_err("TAS2560_ALGO:%s invalid index = %d\n", + __func__, index); + kfree(ppacket); + return -EINVAL; + } + } + pr_info("TAS2560_ALGO:%s valid index = %d special = %s\n", + __func__, index, special_index ? "Yes" : "No"); + + /* speakers are differentiated by slave-ids and instance + * combination(channel) + */ + if ((ppacket->slave_id == SLAVE1) || (ppacket->slave_id == SLAVE2)) + paramid = TAS2560_ALGO_CALC_PIDX(paramid, + index, length, ppacket->slave_id); + else + pr_err("TAS2560_ALGO:%s Wrong slaveid = %x\n", + __func__, ppacket->slave_id); + + /* + * Note: In any case calculated paramid should not match with + * default parameters from ACDB + */ + if (paramid == AFE_PARAM_ID_TAS2560_ALGO_RX_ENABLE || + paramid == AFE_PARAM_ID_TAS2560_ALGO_TX_ENABLE || + paramid == AFE_PARAM_ID_TAS2560_ALGO_RX_CFG || + paramid == AFE_PARAM_ID_TAS2560_ALGO_TX_CFG) { + pr_err("TAS2560_ALGO:%s %s Slave 0x%x params failed\n", + __func__, + dir == TAS2560_ALGO_GET_PARAM ? "get" : "set", + ppacket->slave_id); + kfree(ppacket); + return -EINVAL; + } + ret = afe_tas2560_algo_ctrl(ppacket->data, paramid, moduleid, + dir, length * sizeof(u32)); + if (ret) + pr_err("TAS2560_ALGO:%s %s Slave 0x%x param control failed\n", + __func__, + dir == TAS2560_ALGO_GET_PARAM ? "get" : "set", + ppacket->slave_id); + else + pr_info("TAS2560_ALGO:%s Algo control returned %d\n", + __func__, ret); + + if (dir == TAS2560_ALGO_GET_PARAM) { + ret = copy_to_user(input, ppacket, sizeof(struct tas_dsp_pkt)); + if (ret) { + pr_err("TAS2560_ALGO:%s Error copying to user\n", + __func__); + ret = -EFAULT; + } + } + pr_info("TAS2560_ALGO:%s ppacket.data 0x%x\n", __func__, + ((u32 *)(ppacket->data))[0]); + kfree(ppacket); + return ret; +} + +static int tas_calib_open(struct inode *inode, struct file *fd) +{ + return 0; +} + +static ssize_t tas_calib_write(struct file *file, + const char __user *buffer, size_t count, loff_t *offp) +{ + int rc = 0; + + rc = tas_params_ctrl((uint8_t *)buffer, TAS2560_ALGO_SET_PARAM, count); + return rc; +} + +static ssize_t tas_calib_read(struct file *file, char __user *buffer, + size_t count, loff_t *ptr) +{ + int rc; + + rc = tas_params_ctrl((uint8_t *)buffer, TAS2560_ALGO_GET_PARAM, count); + /*No need of retry*/ + if (rc < 0) + count = rc; + return count; +} + +static long tas_calib_ioctl(struct file *filp, uint cmd, ulong arg) +{ + return 0; +} + +static int tas_calib_release(struct inode *inode, struct file *fd) +{ + return 0; +} + +const struct file_operations tas_calib_fops = { + .owner = THIS_MODULE, + .open = tas_calib_open, + .write = tas_calib_write, + .read = tas_calib_read, + .release = tas_calib_release, + .unlocked_ioctl = tas_calib_ioctl, +}; + +static struct miscdevice tas_calib_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tas_calib", + .fops = &tas_calib_fops, +}; + +static int __init tas_calib_init(void) +{ + int rc; + + pr_err("TAS2560_ALGO:%s", __func__); + rc = misc_register(&tas_calib_misc); + if (rc) + pr_err("TAS2560_ALGO:%s register calib failed\n", __func__); + return rc; +} + +static void __exit tas_calib_exit(void) +{ + misc_deregister(&tas_calib_misc); +} + +module_init(tas_calib_init); +module_exit(tas_calib_exit); + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DSP Driver To Control SmartAmp Algorithm");