android_kernel_oneplus_msm8998/drivers/soc/qcom/qdsp6v2/audio-anc-dev-mgr.c
Derek Chen 6b9c57b3bc soc: msm: add sensor dsp framework support for anc
Add Sensor DSP framework support for Active
Engine Noise Cancellation (ANC).

CRs-fixed: 2153236
Signed-off-by: Derek Chen <chenche@codeaurora.org>
Change-Id: I4cd28ac1bbfd3fcd21174e0216c70cd664cfa319
2018-04-30 13:38:37 -07:00

1170 lines
30 KiB
C

/* Copyright (c) 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mfd/wcd9xxx/core.h>
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/of_device.h>
#include <linux/clk/msm-clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/apr_audio-v2.h>
#include <sound/q6afe-v2.h>
#include <sound/msm-dai-q6-v2.h>
#include <linux/qdsp6v2/audio-anc-dev-mgr.h>
#include <linux/qdsp6v2/sdsp_anc.h>
#define LPM_START_ADDR (0x9120000 + 60*1024)
#define LPM_LENGTH (4*1024)
enum {
ANC_DEV_PORT_REFS = 0,
ANC_DEV_PORT_ANC_SPKR,
ANC_DEV_PORT_ANC_MIC,
ANC_DEV_PORT_MAX,
};
struct anc_tdm_port_cfg_info {
u16 port_id;
struct afe_param_id_tdm_cfg port_cfg;
};
struct anc_tdm_group_set_info {
struct afe_param_id_group_device_tdm_cfg gp_cfg;
uint32_t num_tdm_group_ports;
struct afe_clk_set tdm_clk_set;
uint32_t clk_mode;
};
struct anc_dev_drv_info {
uint32_t state;
uint32_t rpm;
uint32_t bypass_mode;
uint32_t algo_module_id;
};
struct anc_dev_port_cfg_info {
uint32_t port_id;
uint32_t sample_rate;
uint32_t num_channels;
uint32_t bit_width;
};
static struct aud_msvc_param_id_dev_anc_mic_spkr_layout_info
anc_mic_spkr_layout;
static struct anc_dev_port_cfg_info anc_port_cfg[ANC_DEV_PORT_MAX];
static struct anc_tdm_group_set_info anc_dev_tdm_gp_set[IDX_GROUP_TDM_MAX];
static struct anc_tdm_port_cfg_info anc_dev_tdm_port_cfg[IDX_TDM_MAX];
static struct anc_dev_drv_info this_anc_dev_info;
static int anc_dev_get_free_tdm_gp_cfg_idx(void)
{
int idx = -1;
int i;
for (i = 0; i < IDX_GROUP_TDM_MAX; i++) {
if (anc_dev_tdm_gp_set[i].gp_cfg.group_id == 0) {
idx = i;
break;
}
}
return idx;
}
static int anc_dev_get_free_tdm_port_cfg_idx(void)
{
int idx = -1;
int i;
for (i = 0; i < IDX_TDM_MAX; i++) {
if (anc_dev_tdm_port_cfg[i].port_id == 0) {
idx = i;
break;
}
}
return idx;
}
static u16 get_group_id_from_port_id(int32_t port_id)
{
u16 gp_id = AFE_PORT_INVALID;
switch (port_id) {
case AFE_PORT_ID_PRIMARY_TDM_RX:
case AFE_PORT_ID_PRIMARY_TDM_RX_1:
case AFE_PORT_ID_PRIMARY_TDM_RX_2:
case AFE_PORT_ID_PRIMARY_TDM_RX_3:
case AFE_PORT_ID_PRIMARY_TDM_RX_4:
case AFE_PORT_ID_PRIMARY_TDM_RX_5:
case AFE_PORT_ID_PRIMARY_TDM_RX_6:
case AFE_PORT_ID_PRIMARY_TDM_RX_7:
gp_id = AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX;
break;
case AFE_PORT_ID_SECONDARY_TDM_RX:
case AFE_PORT_ID_SECONDARY_TDM_RX_1:
case AFE_PORT_ID_SECONDARY_TDM_RX_2:
case AFE_PORT_ID_SECONDARY_TDM_RX_3:
case AFE_PORT_ID_SECONDARY_TDM_RX_4:
case AFE_PORT_ID_SECONDARY_TDM_RX_5:
case AFE_PORT_ID_SECONDARY_TDM_RX_6:
case AFE_PORT_ID_SECONDARY_TDM_RX_7:
gp_id = AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX;
break;
case AFE_PORT_ID_TERTIARY_TDM_RX:
case AFE_PORT_ID_TERTIARY_TDM_RX_1:
case AFE_PORT_ID_TERTIARY_TDM_RX_2:
case AFE_PORT_ID_TERTIARY_TDM_RX_3:
case AFE_PORT_ID_TERTIARY_TDM_RX_4:
case AFE_PORT_ID_TERTIARY_TDM_RX_5:
case AFE_PORT_ID_TERTIARY_TDM_RX_6:
case AFE_PORT_ID_TERTIARY_TDM_RX_7:
gp_id = AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX;
break;
case AFE_PORT_ID_QUATERNARY_TDM_RX:
case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
gp_id = AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX;
break;
default:
break;
}
return gp_id;
}
static int anc_dev_get_matched_tdm_gp_cfg_idx(u16 gp_id)
{
int idx = -1;
int i;
for (i = 0; i < IDX_GROUP_TDM_MAX; i++) {
if (anc_dev_tdm_gp_set[i].gp_cfg.group_id == gp_id) {
idx = i;
break;
}
}
return idx;
}
static int anc_dev_get_matched_tdm_port_cfg_idx(u16 port_id)
{
int idx = -1;
int i;
for (i = 0; i < IDX_TDM_MAX; i++) {
if (anc_dev_tdm_port_cfg[i].port_id == port_id) {
idx = i;
break;
}
}
return idx;
}
static int anc_dev_tdm_set_clk(
struct anc_tdm_group_set_info *gp_set_data,
u16 port_id, bool enable)
{
int rc = 0;
switch (gp_set_data->gp_cfg.group_id) {
case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX:
case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX:
if (gp_set_data->clk_mode) {
gp_set_data->tdm_clk_set.clk_id =
Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT;
} else
gp_set_data->tdm_clk_set.clk_id =
Q6AFE_LPASS_CLK_ID_PRI_TDM_EBIT;
break;
case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX:
case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX:
if (gp_set_data->clk_mode) {
gp_set_data->tdm_clk_set.clk_id =
Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT;
} else
gp_set_data->tdm_clk_set.clk_id =
Q6AFE_LPASS_CLK_ID_SEC_TDM_EBIT;
break;
case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX:
case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX:
if (gp_set_data->clk_mode) {
gp_set_data->tdm_clk_set.clk_id =
Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT;
} else
gp_set_data->tdm_clk_set.clk_id =
Q6AFE_LPASS_CLK_ID_TER_TDM_EBIT;
break;
case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX:
case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX:
if (gp_set_data->clk_mode) {
gp_set_data->tdm_clk_set.clk_id =
Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT;
} else
gp_set_data->tdm_clk_set.clk_id =
Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT;
break;
default:
pr_err("%s: port id 0x%x not supported\n",
__func__, port_id);
return -EINVAL;
}
gp_set_data->tdm_clk_set.enable = enable;
rc = afe_set_lpass_clock_v2(port_id,
&gp_set_data->tdm_clk_set);
if (rc < 0)
pr_err("%s: afe lpass clock failed, err:%d\n",
__func__, rc);
return rc;
}
static int anc_dev_port_start(int32_t which_port)
{
int rc = 0;
int pt_idx;
struct afe_tdm_port_config tdm_cfg;
pt_idx =
anc_dev_get_matched_tdm_port_cfg_idx(anc_port_cfg[which_port].port_id);
if (pt_idx == -1) {
rc = -EINVAL;
goto rtn;
}
tdm_cfg.tdm = anc_dev_tdm_port_cfg[pt_idx].port_cfg;
tdm_cfg.tdm.num_channels = anc_port_cfg[which_port].num_channels;
tdm_cfg.tdm.sample_rate = anc_port_cfg[which_port].sample_rate;
tdm_cfg.tdm.bit_width = anc_port_cfg[which_port].bit_width;
tdm_cfg.tdm.nslots_per_frame = anc_port_cfg[which_port].num_channels;
tdm_cfg.tdm.slot_width = anc_port_cfg[which_port].bit_width;
tdm_cfg.tdm.slot_mask =
((1 << anc_port_cfg[which_port].num_channels) - 1);
pr_debug("%s: port_id %x num_channels %x bit_width %x sample_rate %x nslots_per_frame %x slot_width %x slot_mask %x!\n",
__func__,
anc_port_cfg[which_port].port_id,
tdm_cfg.tdm.num_channels,
tdm_cfg.tdm.bit_width,
tdm_cfg.tdm.sample_rate,
tdm_cfg.tdm.nslots_per_frame,
tdm_cfg.tdm.slot_width,
tdm_cfg.tdm.slot_mask);
rc = anc_if_tdm_port_start(anc_port_cfg[which_port].port_id,
&tdm_cfg);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to open ANC port from SDSP 0x%x\n",
__func__, anc_port_cfg[which_port].port_id);
goto rtn;
}
rtn:
return rc;
}
static int anc_dev_port_stop(int32_t which_port)
{
int rc = 0;
rc = anc_if_tdm_port_stop(anc_port_cfg[which_port].port_id);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to stop ANC port from SDSP 0x%x\n",
__func__, anc_port_cfg[which_port].port_id);
}
return rc;
}
int msm_anc_dev_set_info(void *info_p, int32_t anc_cmd)
{
int rc = 0;
switch (anc_cmd) {
case ANC_CMD_RPM: {
struct audio_anc_rpm_info *rpm_info_p =
(struct audio_anc_rpm_info *)info_p;
if (this_anc_dev_info.state)
rc = anc_if_set_rpm(
anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id,
rpm_info_p->rpm);
else
this_anc_dev_info.rpm = 0;
break;
}
case ANC_CMD_BYPASS_MODE: {
struct audio_anc_bypass_mode *bypass_mode_p =
(struct audio_anc_bypass_mode *)info_p;
if (this_anc_dev_info.state)
rc = anc_if_set_bypass_mode(
anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id,
bypass_mode_p->mode);
else
this_anc_dev_info.bypass_mode = bypass_mode_p->mode;
break;
}
case ANC_CMD_ALGO_MODULE: {
struct audio_anc_algo_module_info *module_info_p =
(struct audio_anc_algo_module_info *)info_p;
if (this_anc_dev_info.state)
rc = anc_if_set_algo_module_id(
anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id,
module_info_p->module_id);
else
this_anc_dev_info.algo_module_id =
module_info_p->module_id;
break;
}
}
return rc;
}
int msm_anc_dev_start(void)
{
int rc = 0;
u16 group_id;
int gp_idx, pt_idx;
union afe_port_group_config anc_dev_gp_cfg;
struct afe_tdm_port_config tdm_cfg;
pr_debug("%s: ANC devices start in!\n", __func__);
memset(&tdm_cfg, 0, sizeof(tdm_cfg));
/*
* Refs port for ADSP
* 1. enable clk
* 2. group cfg and enable
* 3. Refs port cfg and start
*/
group_id =
get_group_id_from_port_id(anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
gp_idx = anc_dev_get_matched_tdm_gp_cfg_idx(group_id);
if (gp_idx == -1) {
rc = -EINVAL;
pr_err("%s: anc_dev_get_matched_tdm_gp_cfg_idx() failed with group_id 0x%x\n",
__func__, group_id);
goto rtn;
} else {
rc = anc_dev_tdm_set_clk(&anc_dev_tdm_gp_set[gp_idx],
(u16)anc_port_cfg[ANC_DEV_PORT_REFS].port_id, true);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to enable AFE clk 0x%x\n",
__func__,
anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
goto rtn;
}
anc_dev_gp_cfg.tdm_cfg = anc_dev_tdm_gp_set[gp_idx].gp_cfg;
anc_dev_gp_cfg.tdm_cfg.group_device_cfg_minor_version =
AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG;
anc_dev_gp_cfg.tdm_cfg.num_channels =
anc_port_cfg[ANC_DEV_PORT_REFS].num_channels;
anc_dev_gp_cfg.tdm_cfg.bit_width =
anc_port_cfg[ANC_DEV_PORT_REFS].bit_width;
anc_dev_gp_cfg.tdm_cfg.sample_rate =
anc_port_cfg[ANC_DEV_PORT_REFS].sample_rate;
anc_dev_gp_cfg.tdm_cfg.nslots_per_frame =
anc_port_cfg[ANC_DEV_PORT_REFS].num_channels;
anc_dev_gp_cfg.tdm_cfg.slot_width =
anc_port_cfg[ANC_DEV_PORT_REFS].bit_width;
anc_dev_gp_cfg.tdm_cfg.slot_mask =
((1 << anc_port_cfg[ANC_DEV_PORT_REFS].num_channels) - 1);
pr_debug("%s: refs_port_id %x\n", __func__,
anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
pr_debug("%s: anc_dev_gp_cfg num_channels %x bit_width %x sample_rate %x nslots_per_frame %x slot_width %x slot_mask %x!\n",
__func__,
anc_dev_gp_cfg.tdm_cfg.num_channels,
anc_dev_gp_cfg.tdm_cfg.bit_width,
anc_dev_gp_cfg.tdm_cfg.sample_rate,
anc_dev_gp_cfg.tdm_cfg.nslots_per_frame,
anc_dev_gp_cfg.tdm_cfg.slot_width,
anc_dev_gp_cfg.tdm_cfg.slot_mask);
rc = afe_port_group_enable(group_id,
&anc_dev_gp_cfg, true);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to enable AFE group 0x%x\n",
__func__, group_id);
goto rtn;
}
pt_idx =
anc_dev_get_matched_tdm_port_cfg_idx(
anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
if (pt_idx == -1) {
rc = -EINVAL;
pr_err("%s: anc_dev_get_matched_tdm_port_cfg_idx() failed with port_id 0x%x\n",
__func__,
anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
goto rtn;
}
tdm_cfg.tdm = anc_dev_tdm_port_cfg[pt_idx].port_cfg;
tdm_cfg.tdm.num_channels =
anc_port_cfg[ANC_DEV_PORT_REFS].num_channels;
tdm_cfg.tdm.sample_rate =
anc_port_cfg[ANC_DEV_PORT_REFS].sample_rate;
tdm_cfg.tdm.bit_width =
anc_port_cfg[ANC_DEV_PORT_REFS].bit_width;
tdm_cfg.tdm.nslots_per_frame =
anc_dev_gp_cfg.tdm_cfg.nslots_per_frame;
tdm_cfg.tdm.slot_width = anc_dev_gp_cfg.tdm_cfg.slot_width;
tdm_cfg.tdm.slot_mask = anc_dev_gp_cfg.tdm_cfg.slot_mask;
rc = afe_tdm_port_start(anc_port_cfg[ANC_DEV_PORT_REFS].port_id,
&tdm_cfg,
anc_port_cfg[ANC_DEV_PORT_REFS].sample_rate, 0);
if (IS_ERR_VALUE(rc)) {
afe_port_group_enable(group_id,
&anc_dev_gp_cfg, false);
anc_dev_tdm_set_clk(&anc_dev_tdm_gp_set[gp_idx],
(u16)anc_port_cfg[ANC_DEV_PORT_REFS].port_id, false);
pr_err("%s: fail to open AFE port 0x%x\n",
__func__,
anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
goto rtn;
}
}
rc = anc_if_set_anc_mic_spkr_layout(
anc_port_cfg[ANC_DEV_PORT_REFS].port_id,
&anc_mic_spkr_layout);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to pass ANC MIC and SPKR layout info to SDSP 0x%x\n",
__func__,
anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
goto rtn;
}
rc = anc_if_share_resource(
anc_port_cfg[ANC_DEV_PORT_REFS].port_id, 4, 3,
LPM_START_ADDR, LPM_LENGTH);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to assign lpass resource to SDSP 0x%x\n",
__func__,
anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
goto rtn;
}
rc = anc_if_config_ref(anc_port_cfg[ANC_DEV_PORT_REFS].port_id,
anc_port_cfg[ANC_DEV_PORT_REFS].sample_rate,
anc_port_cfg[ANC_DEV_PORT_REFS].bit_width,
anc_port_cfg[ANC_DEV_PORT_REFS].num_channels);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to refs port cfg in SDSP 0x%x\n",
__func__,
anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
goto rtn;
}
if (this_anc_dev_info.algo_module_id != 0)
rc = anc_if_set_algo_module_id(
anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id,
this_anc_dev_info.algo_module_id);
if (this_anc_dev_info.bypass_mode != 0)
rc = anc_if_set_bypass_mode(
anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id,
this_anc_dev_info.bypass_mode);
group_id = get_group_id_from_port_id(
anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id);
gp_idx = anc_dev_get_matched_tdm_gp_cfg_idx(group_id);
if (gp_idx == -1) {
rc = -EINVAL;
pr_err("%s: anc_dev_get_matched_tdm_gp_cfg_idx() failed with group_id 0x%x\n",
__func__, group_id);
goto rtn;
} else {
rc = anc_dev_tdm_set_clk(&anc_dev_tdm_gp_set[gp_idx],
(u16)anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id, true);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to enable AFE clk 0x%x\n",
__func__,
anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id);
goto rtn;
}
}
rc = anc_dev_port_start(ANC_DEV_PORT_ANC_MIC);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to enable ANC MIC Port 0x%x\n",
__func__,
anc_port_cfg[ANC_DEV_PORT_ANC_MIC].port_id);
goto rtn;
}
rc = anc_dev_port_start(ANC_DEV_PORT_ANC_SPKR);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to enable ANC SPKR Port 0x%x\n",
__func__,
anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id);
goto rtn;
}
this_anc_dev_info.state = 1;
pr_debug("%s: ANC devices start successfully!\n", __func__);
rtn:
return rc;
}
int msm_anc_dev_stop(void)
{
int rc = 0;
u16 group_id;
int gp_idx;
anc_dev_port_stop(ANC_DEV_PORT_ANC_SPKR);
anc_dev_port_stop(ANC_DEV_PORT_ANC_MIC);
group_id = get_group_id_from_port_id(
anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id);
gp_idx = anc_dev_get_matched_tdm_gp_cfg_idx(group_id);
if (gp_idx == -1) {
rc = -EINVAL;
goto rtn;
} else {
rc = anc_dev_tdm_set_clk(&anc_dev_tdm_gp_set[gp_idx],
(u16)anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id, false);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to disable AFE clk 0x%x\n",
__func__,
anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id);
}
}
group_id =
get_group_id_from_port_id(anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
gp_idx = anc_dev_get_matched_tdm_gp_cfg_idx(group_id);
if (gp_idx == -1) {
rc = -EINVAL;
goto rtn;
}
afe_close(anc_port_cfg[ANC_DEV_PORT_REFS].port_id);
afe_port_group_enable(group_id, NULL, false);
anc_dev_tdm_set_clk(&anc_dev_tdm_gp_set[gp_idx],
(u16)anc_port_cfg[ANC_DEV_PORT_REFS].port_id, false);
this_anc_dev_info.state = 0;
this_anc_dev_info.algo_module_id = 0;
this_anc_dev_info.rpm = 0;
this_anc_dev_info.bypass_mode = 0;
pr_debug("%s: ANC devices stop successfully!\n", __func__);
rtn:
return rc;
}
static int msm_anc_tdm_dev_group_cfg_info(
struct platform_device *pdev,
struct device_node *ctx_node)
{
int rc = 0;
const uint32_t *port_id_array = NULL;
uint32_t num_tdm_group_ports = 0;
uint32_t array_length = 0;
int i = 0;
int gp_idx = anc_dev_get_free_tdm_gp_cfg_idx();
if ((gp_idx < 0) || (gp_idx > IDX_GROUP_TDM_MAX)) {
dev_err(&pdev->dev, "%s: could not get abaiable tdm group cfg slot\n",
__func__);
rc = -EINVAL;
goto rtn;
}
/* extract tdm group info into static */
rc = of_property_read_u32(ctx_node,
"qcom,msm-cpudai-tdm-group-id",
(u32 *)&anc_dev_tdm_gp_set[gp_idx].gp_cfg.group_id);
if (rc) {
dev_err(&pdev->dev, "%s: Group ID from DT file %s\n",
__func__, "qcom,msm-cpudai-tdm-group-id");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: dev_name: %s group_id: 0x%x\n",
__func__, dev_name(&pdev->dev),
anc_dev_tdm_gp_set[gp_idx].gp_cfg.group_id);
rc = of_property_read_u32(ctx_node,
"qcom,msm-cpudai-tdm-group-num-ports",
&num_tdm_group_ports);
if (rc) {
dev_err(&pdev->dev, "%s: Group Num Ports from DT file %s\n",
__func__, "qcom,msm-cpudai-tdm-group-num-ports");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: Group Num Ports from DT file 0x%x\n",
__func__, num_tdm_group_ports);
if (num_tdm_group_ports > AFE_GROUP_DEVICE_NUM_PORTS) {
dev_err(&pdev->dev, "%s Group Num Ports %d greater than Max %d\n",
__func__, num_tdm_group_ports,
AFE_GROUP_DEVICE_NUM_PORTS);
rc = -EINVAL;
goto rtn;
}
port_id_array = of_get_property(ctx_node,
"qcom,msm-cpudai-tdm-group-port-id",
&array_length);
if (port_id_array == NULL) {
dev_err(&pdev->dev, "%s port_id_array is not valid\n",
__func__);
rc = -EINVAL;
goto rtn;
}
if (array_length != sizeof(uint32_t) * num_tdm_group_ports) {
dev_err(&pdev->dev, "%s array_length is %d, expected is %zd\n",
__func__, array_length,
sizeof(uint32_t) * num_tdm_group_ports);
rc = -EINVAL;
goto rtn;
}
for (i = 0; i < num_tdm_group_ports; i++)
anc_dev_tdm_gp_set[gp_idx].gp_cfg.port_id[i] =
(u16)be32_to_cpu(port_id_array[i]);
/* Unused index should be filled with 0 or AFE_PORT_INVALID */
for (i = num_tdm_group_ports;
i < AFE_GROUP_DEVICE_NUM_PORTS; i++)
anc_dev_tdm_gp_set[gp_idx].gp_cfg.port_id[i] = AFE_PORT_INVALID;
anc_dev_tdm_gp_set[gp_idx].num_tdm_group_ports = num_tdm_group_ports;
/* extract tdm clk info into static */
rc = of_property_read_u32(ctx_node,
"qcom,msm-cpudai-tdm-clk-rate",
&anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_freq_in_hz);
if (rc) {
dev_err(&pdev->dev, "%s: Clk Rate from DT file %s\n",
__func__, "qcom,msm-cpudai-tdm-clk-rate");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: Clk Rate from DT file %d\n",
__func__,
anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_freq_in_hz);
anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_set_minor_version =
Q6AFE_LPASS_CLK_CONFIG_API_VERSION;
anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_attri =
Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO;
anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_root =
Q6AFE_LPASS_CLK_ROOT_DEFAULT;
/* extract tdm clk attribute into static */
if (of_find_property(ctx_node,
"qcom,msm-cpudai-tdm-clk-attribute", NULL)) {
rc = of_property_read_u16(ctx_node,
"qcom,msm-cpudai-tdm-clk-attribute",
&anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_attri);
if (rc) {
dev_err(&pdev->dev, "%s: No Clk attribute in DT file %s\n",
__func__,
"qcom,msm-cpudai-tdm-clk-attribute");
goto rtn;
}
} else {
dev_dbg(&pdev->dev, "%s: Clk Attribute from DT file %d\n",
__func__,
anc_dev_tdm_gp_set[gp_idx].tdm_clk_set.clk_attri);
}
/* extract tdm clk src master/slave info into static */
rc = of_property_read_u32(ctx_node,
"qcom,msm-cpudai-tdm-clk-internal",
&anc_dev_tdm_gp_set[gp_idx].clk_mode);
if (rc) {
dev_err(&pdev->dev, "%s: Clk id from DT file %s\n",
__func__, "qcom,msm-cpudai-tdm-clk-internal");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: Clk id from DT file %d\n",
__func__, anc_dev_tdm_gp_set[gp_idx].clk_mode);
rtn:
return rc;
}
static int msm_anc_tdm_dev_port_cfg_info(
struct platform_device *pdev,
struct device_node *ctx_node)
{
int rc = 0;
u32 tdm_dev_id = 0;
int pt_idx = anc_dev_get_free_tdm_port_cfg_idx();
struct device_node *tdm_parent_node = NULL;
if ((pt_idx < 0) || (pt_idx > IDX_TDM_MAX)) {
dev_err(&pdev->dev, "%s: could not get abaiable tdm port cfg slot\n",
__func__);
rc = -EINVAL;
goto rtn;
}
/* retrieve device/afe id */
rc = of_property_read_u32(ctx_node,
"qcom,msm-cpudai-tdm-dev-id",
&tdm_dev_id);
if (rc) {
dev_err(&pdev->dev, "%s: Device ID missing in DT file\n",
__func__);
goto rtn;
}
if ((tdm_dev_id < AFE_PORT_ID_TDM_PORT_RANGE_START) ||
(tdm_dev_id > AFE_PORT_ID_TDM_PORT_RANGE_END)) {
dev_err(&pdev->dev, "%s: Invalid TDM Device ID 0x%x in DT file\n",
__func__, tdm_dev_id);
rc = -ENXIO;
goto rtn;
}
anc_dev_tdm_port_cfg[pt_idx].port_id = tdm_dev_id;
dev_dbg(&pdev->dev, "%s: dev_name: %s dev_id: 0x%x\n",
__func__, dev_name(&pdev->dev), tdm_dev_id);
/* TDM CFG */
tdm_parent_node = of_get_parent(ctx_node);
rc = of_property_read_u32(tdm_parent_node,
"qcom,msm-cpudai-tdm-sync-mode",
(u32 *)&anc_dev_tdm_port_cfg[pt_idx].port_cfg.sync_mode);
if (rc) {
dev_err(&pdev->dev, "%s: Sync Mode from DT file %s\n",
__func__, "qcom,msm-cpudai-tdm-sync-mode");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: Sync Mode from DT file 0x%x\n",
__func__, anc_dev_tdm_port_cfg[pt_idx].port_cfg.sync_mode);
rc = of_property_read_u32(tdm_parent_node,
"qcom,msm-cpudai-tdm-sync-src",
(u32 *)&anc_dev_tdm_port_cfg[pt_idx].port_cfg.sync_src);
if (rc) {
dev_err(&pdev->dev, "%s: Sync Src from DT file %s\n",
__func__, "qcom,msm-cpudai-tdm-sync-src");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: Sync Src from DT file 0x%x\n",
__func__, anc_dev_tdm_port_cfg[pt_idx].port_cfg.sync_src);
rc = of_property_read_u32(tdm_parent_node,
"qcom,msm-cpudai-tdm-data-out",
(u32 *)&anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_data_out_enable);
if (rc) {
dev_err(&pdev->dev, "%s: Data Out from DT file %s\n",
__func__, "qcom,msm-cpudai-tdm-data-out");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: Data Out from DT file 0x%x\n",
__func__,
anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_data_out_enable);
rc = of_property_read_u32(tdm_parent_node,
"qcom,msm-cpudai-tdm-invert-sync",
(u32 *)&anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_invert_sync_pulse);
if (rc) {
dev_err(&pdev->dev, "%s: Invert Sync from DT file %s\n",
__func__, "qcom,msm-cpudai-tdm-invert-sync");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: Invert Sync from DT file 0x%x\n",
__func__,
anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_invert_sync_pulse);
rc = of_property_read_u32(tdm_parent_node,
"qcom,msm-cpudai-tdm-data-delay",
(u32 *)&anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_sync_data_delay);
if (rc) {
dev_err(&pdev->dev, "%s: Data Delay from DT file %s\n",
__func__, "qcom,msm-cpudai-tdm-data-delay");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: Data Delay from DT file 0x%x\n",
__func__,
anc_dev_tdm_port_cfg[pt_idx].port_cfg.ctrl_sync_data_delay);
/* TDM CFG -- set default */
anc_dev_tdm_port_cfg[pt_idx].port_cfg.data_format = AFE_LINEAR_PCM_DATA;
anc_dev_tdm_port_cfg[pt_idx].port_cfg.tdm_cfg_minor_version =
AFE_API_VERSION_TDM_CONFIG;
msm_anc_tdm_dev_group_cfg_info(pdev, tdm_parent_node);
return 0;
rtn:
return rc;
}
static int msm_anc_dev_probe(struct platform_device *pdev)
{
int rc = 0;
u32 port_id = 0;
const uint32_t *layout_array = NULL;
uint32_t num_anc_io = 0;
uint32_t array_length = 0;
int i = 0;
uint32_t sample_rate = 0;
uint32_t num_channels = 0;
uint32_t bit_width = 0;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,refs-port-id",
(u32 *)&port_id);
if (rc) {
dev_err(&pdev->dev, "%s: ANC refs-port-id DT file %s\n",
__func__, "qcom,refs-port-id");
goto rtn;
}
anc_port_cfg[ANC_DEV_PORT_REFS].port_id = port_id;
dev_dbg(&pdev->dev, "%s: refs-port-id 0x%x\n",
__func__, port_id);
port_id = 0;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,spkr-port-id",
(u32 *)&port_id);
if (rc) {
dev_err(&pdev->dev, "%s: ANC spkr-port-id DT file %s\n",
__func__, "qcom,spkr-port-id");
goto rtn;
}
anc_port_cfg[ANC_DEV_PORT_ANC_SPKR].port_id = port_id;
dev_dbg(&pdev->dev, "%s: spkr-port-id 0x%x\n",
__func__, port_id);
port_id = 0;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,mic-port-id",
(u32 *)&port_id);
if (rc) {
dev_err(&pdev->dev, "%s: ANC mic-port-id DT file %s\n",
__func__, "qcom,mic-port-id");
goto rtn;
}
anc_port_cfg[ANC_DEV_PORT_ANC_MIC].port_id = port_id;
dev_dbg(&pdev->dev, "%s: mic-port-id 0x%x\n",
__func__, port_id);
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,sample-rate",
(u32 *)&sample_rate);
if (rc) {
dev_err(&pdev->dev, "%s: ANC sample rate DT file %s\n",
__func__, "qcom,sample-rate");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: ANC sample rate 0x%x\n",
__func__, sample_rate);
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,num-channels",
(u32 *)&num_channels);
if (rc) {
dev_err(&pdev->dev, "%s: ANC num channels DT file %s\n",
__func__, "qcom,num-channels");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: ANC num channel 0x%x\n",
__func__, num_channels);
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,bit-width",
(u32 *)&bit_width);
if (rc) {
dev_err(&pdev->dev, "%s: ANC bit width DT file %s\n",
__func__, "qcom,bit-width");
goto rtn;
}
dev_dbg(&pdev->dev, "%s: ANC bit width 0x%x\n",
__func__, bit_width);
for (i = 0; i < ANC_DEV_PORT_MAX; i++) {
anc_port_cfg[i].sample_rate = sample_rate;
anc_port_cfg[i].num_channels = num_channels;
anc_port_cfg[i].bit_width = bit_width;
}
memset(&anc_mic_spkr_layout, 0, sizeof(anc_mic_spkr_layout));
anc_mic_spkr_layout.minor_version = 1;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,num-anc-mic",
(u32 *)&num_anc_io);
if (rc) {
dev_err(&pdev->dev, "%s: ANC num_anc_mic DT file %s\n",
__func__, "qcom,num-anc-mic");
goto rtn;
}
layout_array = of_get_property(pdev->dev.of_node,
"qcom,anc-mic-array",
&array_length);
if (layout_array == NULL) {
dev_err(&pdev->dev, "%s layout_array is not valid\n",
__func__);
rc = -EINVAL;
goto rtn;
}
if (array_length != sizeof(uint32_t) * num_anc_io) {
dev_err(&pdev->dev, "%s array_length is %d, expected is %zd\n",
__func__, array_length,
sizeof(uint32_t) * num_anc_io);
rc = -EINVAL;
goto rtn;
}
anc_mic_spkr_layout.num_anc_mic = num_anc_io;
for (i = 0; i < num_anc_io; i++)
anc_mic_spkr_layout.mic_layout_array[i] =
(u16)be32_to_cpu(layout_array[i]);
num_anc_io = 0;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,num-anc-spkr",
(u32 *)&num_anc_io);
if (rc) {
dev_err(&pdev->dev, "%s: ANC num_anc_mic DT file %s\n",
__func__, "qcom,num-anc-spkr");
goto rtn;
}
layout_array = of_get_property(pdev->dev.of_node,
"qcom,anc-spkr-array",
&array_length);
if (layout_array == NULL) {
dev_err(&pdev->dev, "%s layout_array is not valid\n",
__func__);
rc = -EINVAL;
goto rtn;
}
if (array_length != sizeof(uint32_t) * num_anc_io) {
dev_err(&pdev->dev, "%s array_length is %d, expected is %zd\n",
__func__, array_length,
sizeof(uint32_t) * num_anc_io);
rc = -EINVAL;
goto rtn;
}
anc_mic_spkr_layout.num_anc_spkr = num_anc_io;
for (i = 0; i < num_anc_io; i++)
anc_mic_spkr_layout.spkr_layout_array[i] =
(u16)be32_to_cpu(layout_array[i]);
dev_dbg(&pdev->dev, "%s: num_anc_mic 0x%x\n",
__func__, anc_mic_spkr_layout.num_anc_mic);
dev_dbg(&pdev->dev, "%s: num_anc_spkr 0x%x\n",
__func__, anc_mic_spkr_layout.num_anc_spkr);
num_anc_io = 0;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,num-add-mic-signal",
(u32 *)&num_anc_io);
if (rc) {
dev_err(&pdev->dev, "%s: ANC num_add_mic_signal DT file %s\n",
__func__, "qcom,num-add-mic-signal");
goto rtn;
}
anc_mic_spkr_layout.num_add_mic_signal = num_anc_io;
num_anc_io = 0;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,num-add-spkr-signal",
(u32 *)&num_anc_io);
if (rc) {
dev_err(&pdev->dev, "%s: ANC num_add_spkr_signal DT file %s\n",
__func__, "qcom,num-add-spkr-signal");
goto rtn;
}
anc_mic_spkr_layout.num_add_spkr_signal = num_anc_io;
dev_dbg(&pdev->dev, "%s: num_add_mic_signal 0x%x\n",
__func__, anc_mic_spkr_layout.num_add_mic_signal);
dev_dbg(&pdev->dev, "%s: num_add_spkr_signal 0x%x\n",
__func__, anc_mic_spkr_layout.num_add_spkr_signal);
/* TDM group CFG and TDM port CFG */
{
struct device_node *ctx_node = NULL;
ctx_node = of_parse_phandle(pdev->dev.of_node,
"qcom,refs-tdm-rx", 0);
if (!ctx_node) {
pr_err("%s Could not find refs-tdm-rx info\n",
__func__);
return -EINVAL;
}
rc = msm_anc_tdm_dev_port_cfg_info(pdev, ctx_node);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to probe TDM group info\n",
__func__);
}
ctx_node = of_parse_phandle(pdev->dev.of_node,
"qcom,spkr-tdm-rx", 0);
if (!ctx_node) {
pr_err("%s Could not find spkr-tdm-rx info\n",
__func__);
return -EINVAL;
}
rc = msm_anc_tdm_dev_port_cfg_info(pdev, ctx_node);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to probe TDM group info\n",
__func__);
}
ctx_node = of_parse_phandle(pdev->dev.of_node,
"qcom,mic-tdm-tx", 0);
if (!ctx_node) {
pr_err("%s Could not find mic-tdm-tx info\n",
__func__);
return -EINVAL;
}
rc = msm_anc_tdm_dev_port_cfg_info(pdev, ctx_node);
if (IS_ERR_VALUE(rc)) {
pr_err("%s: fail to probe TDM group info\n",
__func__);
}
}
rc = msm_anc_dev_create(pdev);
rtn:
return rc;
}
static int msm_anc_dev_remove(struct platform_device *pdev)
{
return msm_anc_dev_destroy(pdev);
}
static const struct of_device_id msm_anc_dev_dt_match[] = {
{ .compatible = "qcom,msm-ext-anc", },
{}
};
MODULE_DEVICE_TABLE(of, msm_anc_dev_dt_match);
static struct platform_driver msm_anc_dev = {
.probe = msm_anc_dev_probe,
.remove = msm_anc_dev_remove,
.driver = {
.name = "msm-ext-anc",
.owner = THIS_MODULE,
.of_match_table = msm_anc_dev_dt_match,
},
};
int msm_anc_dev_init(void)
{
int rc = 0;
memset(&anc_dev_tdm_gp_set, 0, sizeof(anc_dev_tdm_gp_set));
memset(&anc_dev_tdm_port_cfg, 0, sizeof(anc_dev_tdm_port_cfg));
memset(&anc_port_cfg, 0, sizeof(anc_port_cfg));
memset(&this_anc_dev_info, 0, sizeof(this_anc_dev_info));
rc = platform_driver_register(&msm_anc_dev);
if (rc)
pr_err("%s: fail to register msm ANC device driver\n",
__func__);
return rc;
}
int msm_anc_dev_deinit(void)
{
platform_driver_unregister(&msm_anc_dev);
return 0;
}