asoc: afe: Add afe dyn mclk kcontrol

Add afe dyn mclk kcontrol to support change external clk.

Change-Id: I90f0a4a6a350ec261f216aa53bfa2f7306987bc1
Signed-off-by: Jing Wang <quic_jingwa@quicinc.com>
This commit is contained in:
Jing Wang 2021-12-09 15:28:47 +05:30
parent 3f10f8fe5f
commit c11441a914
4 changed files with 315 additions and 2 deletions

View File

@ -481,6 +481,8 @@ static bool afe_port_logging_item[IDX_TDM_MAX];
static int afe_port_loggging_control_added;
static int afe_dyn_mclk_control_added;
static DEFINE_MUTEX(tdm_mutex);
static atomic_t tdm_group_ref[IDX_GROUP_TDM_MAX];
@ -522,6 +524,51 @@ static struct afe_clk_set tdm_clk_set = {
0,
};
static int clk_id_index;
static int clk_root_index;
static int clk_attri_index;
static int global_dyn_mclk_cfg_portid;
struct afe_param_id_clock_set_v2_t global_dyn_mclk_cfg = {
.clk_set_minor_version = Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
.clk_id = Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT,
.clk_freq_in_hz = 12288000,
.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
.clk_root = Q6AFE_LPASS_MCLK_IN0,
.divider_2x = AFE_CLOCK_DEFAULT_INTEGER_DIVIDER,
.m = AFE_CLOCK_DEFAULT_M_VALUE,
.n = AFE_CLOCK_DEFAULT_N_VALUE,
.d = AFE_CLOCK_DEFAULT_D_VALUE,
.enable = 0,
};
static int afe_dyn_clk_root_enum[] = {
Q6AFE_LPASS_MCLK_IN0,
Q6AFE_LPASS_MCLK_IN1,
};
static int afe_dyn_clk_attri_enum[] = {
Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND,
Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR,
};
static int afe_dyn_clk_id_enum[] = {
Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT,
Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT,
Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT,
Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT,
Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_IBIT, Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_EBIT,
Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT, Q6AFE_LPASS_CLK_ID_PRI_PCM_EBIT,
Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT, Q6AFE_LPASS_CLK_ID_SEC_PCM_EBIT,
Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT, Q6AFE_LPASS_CLK_ID_TER_PCM_EBIT,
Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT, Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT,
Q6AFE_LPASS_CLK_ID_MCLK_1, Q6AFE_LPASS_CLK_ID_MCLK_2,
Q6AFE_LPASS_CLK_ID_MCLK_3, Q6AFE_LPASS_CLK_ID_MCLK_4,
Q6AFE_LPASS_CLK_ID_MCLK_5, Q6AFE_LPASS_CLK_ID_AHB_HDMI_INPUT,
Q6AFE_LPASS_CLK_ID_SPDIF_CORE
};
static int msm_dai_q6_get_tdm_clk_ref(u16 id)
{
switch (id) {
@ -10371,14 +10418,168 @@ static int msm_pcm_add_afe_port_logging_control(struct snd_soc_dai *dai)
return rc;
}
static int get_global_clk_root(int value)
{
if ((value < 0) || (value > ARRAY_SIZE(afe_dyn_clk_root_enum) - 1)) {
pr_err("%s, %d set clk_root range failed\n", __func__, __LINE__);
return global_dyn_mclk_cfg.clk_root;
}
clk_root_index = value;
return afe_dyn_clk_root_enum[clk_root_index];
}
static int get_global_clk_attri(int value)
{
if ((value < 0) || (value > ARRAY_SIZE(afe_dyn_clk_attri_enum) - 1)) {
pr_err("%s, %d set clk_attri range failed\n", __func__, __LINE__);
return global_dyn_mclk_cfg.clk_attri;
}
clk_attri_index = value;
return afe_dyn_clk_attri_enum[clk_attri_index];
}
static int get_global_clk_id(int value)
{
if ((value < 0) || (value > ARRAY_SIZE(afe_dyn_clk_id_enum) - 1)) {
pr_err("%s, %d set clk_id range failed\n", __func__, __LINE__);
return global_dyn_mclk_cfg.clk_id;
}
clk_id_index = value;
return afe_dyn_clk_id_enum[clk_id_index];
}
static int msm_pcm_afe_dyn_mclk_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *ucontrol)
{
ucontrol->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
/* 11 int values:clk_set_minor_version, clk_id, clk_freq_in_hz, clk_attri,
* clk_root, enable, divider_2x, m, n, d
*/
ucontrol->count = 11;
ucontrol->value.integer.min = 0;
ucontrol->value.integer.max = INT_MAX;
/* Valid range is all positive values to support above controls */
pr_debug("%s,%d\n", __func__, __LINE__);
return 0;
}
static int msm_pcm_afe_dyn_mclk_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = global_dyn_mclk_cfg_portid;
ucontrol->value.integer.value[1] = clk_id_index;
ucontrol->value.integer.value[2] = global_dyn_mclk_cfg.clk_freq_in_hz;
ucontrol->value.integer.value[3] = clk_attri_index;
ucontrol->value.integer.value[4] = clk_root_index;
ucontrol->value.integer.value[5] = global_dyn_mclk_cfg.enable;
ucontrol->value.integer.value[6] = global_dyn_mclk_cfg.divider_2x;
ucontrol->value.integer.value[7] = global_dyn_mclk_cfg.m;
ucontrol->value.integer.value[8] = global_dyn_mclk_cfg.n;
ucontrol->value.integer.value[9] = global_dyn_mclk_cfg.d;
ucontrol->value.integer.value[10] = global_dyn_mclk_cfg.clk_set_minor_version;
pr_debug("%s:%d.... portid:%d, clk_id:%d, clk_freq_in_hz:%d, clk_attri:%d, clk_root:%d, enable:%d, divider_2x:%d, m:%d, n:%d, d:%d, version:%d\n",
__func__, __LINE__, global_dyn_mclk_cfg_portid, global_dyn_mclk_cfg.clk_id,
global_dyn_mclk_cfg.clk_freq_in_hz, global_dyn_mclk_cfg.clk_attri,
global_dyn_mclk_cfg.clk_root, global_dyn_mclk_cfg.enable,
global_dyn_mclk_cfg.divider_2x, global_dyn_mclk_cfg.m, global_dyn_mclk_cfg.n,
global_dyn_mclk_cfg.d, global_dyn_mclk_cfg.clk_set_minor_version);
return 0;
}
static int msm_pcm_afe_dyn_mclk_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret = -EINVAL;
pr_debug("%s: enter\n", __func__);
global_dyn_mclk_cfg_portid = ucontrol->value.integer.value[0];
global_dyn_mclk_cfg.clk_id = get_global_clk_id(ucontrol->value.integer.value[1]);
if (ucontrol->value.integer.value[2] >= 0)
global_dyn_mclk_cfg.clk_freq_in_hz = ucontrol->value.integer.value[2];
global_dyn_mclk_cfg.clk_attri = get_global_clk_attri(ucontrol->value.integer.value[3]);
global_dyn_mclk_cfg.clk_root = get_global_clk_root(ucontrol->value.integer.value[4]);
if ((ucontrol->value.integer.value[5] <= 1) && (ucontrol->value.integer.value[5] >= 0))
global_dyn_mclk_cfg.enable = ucontrol->value.integer.value[5];
global_dyn_mclk_cfg.divider_2x = ucontrol->value.integer.value[6];
global_dyn_mclk_cfg.m = ucontrol->value.integer.value[7];
global_dyn_mclk_cfg.n = ucontrol->value.integer.value[8];
global_dyn_mclk_cfg.d = ucontrol->value.integer.value[9];
pr_debug("%s:%d.... portid:%d, clk_id_index:%d, clk_freq_in_hz:%d, clk_attri_index:%d, clk_root_index:%d, enable:%d, divider_2x:%d, m:%d, n:%d, d:%d, version:%d\n",
__func__, __LINE__, ucontrol->value.integer.value[0],
ucontrol->value.integer.value[1],
ucontrol->value.integer.value[2], ucontrol->value.integer.value[3],
ucontrol->value.integer.value[4], ucontrol->value.integer.value[5],
ucontrol->value.integer.value[6], ucontrol->value.integer.value[7],
ucontrol->value.integer.value[8], ucontrol->value.integer.value[9]);
ret = afe_set_lpass_clk_cfg_ext_mclk_v2(global_dyn_mclk_cfg_portid,
&global_dyn_mclk_cfg, 0);
if (ret)
pr_err("%s: AFE port logging setting for port 0x%x failed %d\n",
__func__, global_dyn_mclk_cfg_portid, ret);
return ret;
}
static int msm_pcm_add_afe_dyn_mclk_control(struct snd_soc_dai *dai)
{
int rc = 0;
struct snd_kcontrol_new *knew = NULL;
struct snd_kcontrol *kctl = NULL;
const char *afe_dyn_mclk_ctl_name = "AFE_dyn_switch_mclk_source";
/* Add AFE port logging controls */
knew = kzalloc(sizeof(struct snd_kcontrol_new), GFP_KERNEL);
if (!knew)
return -ENOMEM;
knew->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
knew->info = msm_pcm_afe_dyn_mclk_ctl_info;
knew->get = msm_pcm_afe_dyn_mclk_ctl_get;
knew->put = msm_pcm_afe_dyn_mclk_ctl_put;
knew->name = afe_dyn_mclk_ctl_name;
knew->private_value = dai->id;
kctl = snd_ctl_new1(knew, knew);
if (!kctl) {
kfree(knew);
return -ENOMEM;
}
rc = snd_ctl_add(dai->component->card->snd_card, kctl);
if (rc < 0)
pr_err("%s: err add AFE dyn mclk control, DAI = %s\n",
__func__, dai->name);
return rc;
}
int jitter_cleaner_afe_enable_mclk_and_get_info_cb_func(void *private_data,
uint32_t enable, uint32_t mclk_freq,
struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg)
{
pr_debug("%s,%d\n", __func__, __LINE__);
return 0;
}
static int msm_dai_q6_dai_tdm_probe(struct snd_soc_dai *dai)
{
int rc = 0;
int port_idx = 0;
struct msm_dai_q6_tdm_dai_data *tdm_dai_data = NULL;
struct snd_kcontrol *data_format_kcontrol = NULL;
struct snd_kcontrol *header_type_kcontrol = NULL;
struct snd_kcontrol *header_kcontrol = NULL;
int port_idx = 0;
const struct snd_kcontrol_new *data_format_ctrl = NULL;
const struct snd_kcontrol_new *header_type_ctrl = NULL;
const struct snd_kcontrol_new *header_ctrl = NULL;
@ -10459,6 +10660,26 @@ static int msm_dai_q6_dai_tdm_probe(struct snd_soc_dai *dai)
afe_port_loggging_control_added = 1;
}
/* add AFE dyn mclk controls */
if (!afe_dyn_mclk_control_added) {
rc = msm_pcm_add_afe_dyn_mclk_control(dai);
if (rc < 0) {
dev_err(dai->dev, "%s: add AFE dyn mclk control failed DAI: %s\n",
__func__, dai->name);
goto rtn;
}
afe_dyn_mclk_control_added = 1;
rc = afe_register_ext_mclk_cb(jitter_cleaner_afe_enable_mclk_and_get_info_cb_func,
(void *)&global_dyn_mclk_cfg);
if (rc < 0) {
dev_err(dai->dev, "%s: add AFE afe_register_ext_mclk_cb failed : %s\n",
__func__, dai->name);
goto rtn;
}
}
if (tdm_dai_data->is_island_dai)
rc = msm_dai_q6_add_island_mx_ctls(
dai->component->card->snd_card,

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/slab.h>
#include <linux/debugfs.h>
@ -9792,7 +9793,6 @@ static int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg,
dyn_mclk_cfg.clk_id = cfg->clk_id;
dyn_mclk_cfg.clk_attri = cfg->clk_attri;
dyn_mclk_cfg.enable = cfg->enable;
pr_debug("%s: Minor version =0x%x clk id = %d\n", __func__,
dyn_mclk_cfg.clk_set_minor_version, dyn_mclk_cfg.clk_id);
pr_debug("%s: clk freq (Hz) = %d, clk attri = 0x%x\n", __func__,
@ -9831,6 +9831,83 @@ stop_mclk:
return ret;
}
int afe_set_lpass_clk_cfg_ext_mclk_v2(int index, struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg,
uint32_t mclk_freq)
{
struct param_hdr_v3 param_hdr;
int ret = 0;
if (!dyn_mclk_cfg) {
pr_err("%s: clock cfg is NULL\n", __func__);
ret = -EINVAL;
return ret;
}
if (index < 0 || index >= AFE_MAX_PORTS) {
pr_err("%s: index[%d] invalid!\n", __func__, index);
return -EINVAL;
}
memset(&param_hdr, 0, sizeof(param_hdr));
param_hdr.module_id = AFE_MODULE_CLOCK_SET;
param_hdr.instance_id = INSTANCE_ID_0;
param_hdr.param_id = AFE_PARAM_ID_CLOCK_SET_V2;
param_hdr.param_size = sizeof(struct afe_param_id_clock_set_v2_t);
if (afe_ext_mclk.ext_mclk_cb) {
ret = afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data,
dyn_mclk_cfg->enable, mclk_freq, dyn_mclk_cfg);
if (ret) {
pr_err_ratelimited("%s: get mclk cfg failed %d\n",
__func__, ret);
return ret;
}
} else {
pr_err_ratelimited("%s: mclk callback not registered\n", __func__);
return -EINVAL;
}
dyn_mclk_cfg->clk_set_minor_version = 1;
pr_debug("%s: Minor version =0x%x clk id = %d\n", __func__,
dyn_mclk_cfg->clk_set_minor_version, dyn_mclk_cfg->clk_id);
pr_debug("%s: clk freq (Hz) = %d, clk attri = 0x%x\n", __func__,
dyn_mclk_cfg->clk_freq_in_hz, dyn_mclk_cfg->clk_attri);
pr_debug("%s: clk root = 0x%x clk enable = 0x%x\n", __func__,
dyn_mclk_cfg->clk_root, dyn_mclk_cfg->enable);
pr_debug("%s: divider_2x =%d m = %d n = %d, d =%d\n", __func__,
dyn_mclk_cfg->divider_2x, dyn_mclk_cfg->m, dyn_mclk_cfg->n,
dyn_mclk_cfg->d);
ret = afe_q6_interface_prepare();
if (ret != 0) {
pr_err_ratelimited("%s: Q6 interface prepare failed %d\n",
__func__, ret);
goto stop_mclk;
}
mutex_lock(&this_afe.afe_cmd_lock);
ret = q6afe_svc_pack_and_set_param_in_band(index, param_hdr,
(u8 *) dyn_mclk_cfg);
if (ret < 0)
pr_err_ratelimited("%s: ext MCLK clk cfg failed with ret %d\n",
__func__, ret);
mutex_unlock(&this_afe.afe_cmd_lock);
if (ret >= 0)
return ret;
stop_mclk:
if (afe_ext_mclk.ext_mclk_cb && dyn_mclk_cfg->enable) {
afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data,
dyn_mclk_cfg->enable, mclk_freq, dyn_mclk_cfg);
}
return ret;
}
EXPORT_SYMBOL(afe_set_lpass_clk_cfg_ext_mclk_v2);
/**
* afe_set_lpass_clk_cfg - Set AFE clk config
*

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
@ -12209,6 +12210,9 @@ struct afe_param_id_clip_bank_sel {
/* Supported LPASS CLK root*/
#define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0
#define Q6AFE_LPASS_MCLK_IN0 1
#define Q6AFE_LPASS_MCLK_IN1 2
enum afe_lpass_clk_mode {
Q6AFE_LPASS_MODE_BOTH_INVALID,
Q6AFE_LPASS_MODE_CLK1_VALID,
@ -12362,6 +12366,8 @@ enum afe_lpass_clk_mode {
/* Clock ID for AHB HDMI input */
#define Q6AFE_LPASS_CLK_ID_AHB_HDMI_INPUT 0x400
#define Q6AFE_LPASS_CLK_ID_SPDIF_CORE 0x000
/* Clock ID for the primary SPDIF output core. */
#define AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_OUTPUT_CORE 0x500
/* Clock ID for the secondary SPDIF output core. */
@ -12448,6 +12454,12 @@ struct afe_clk_set {
#define AFE_PARAM_ID_CLOCK_SET_V2 0x000102E6
#define AFE_CLOCK_SET_CLOCK_ROOT_DEFAULT 0x2
#define AFE_CLOCK_DEFAULT_INTEGER_DIVIDER 0x0
#define AFE_CLOCK_DEFAULT_M_VALUE 0x1
#define AFE_CLOCK_DEFAULT_N_VALUE 0x2
#define AFE_CLOCK_DEFAULT_D_VALUE 0x1
#define AFE_API_VERSION_CLOCK_SET_V2 0x1
struct afe_param_id_clock_set_v2_t {

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __Q6AFE_V2_H__
#define __Q6AFE_V2_H__
@ -592,6 +593,8 @@ int afe_port_send_logging_cfg(u16 port_id,
struct afe_param_id_port_data_log_disable_t *log_disable);
int afe_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats,
u16 port);
int afe_set_lpass_clk_cfg_ext_mclk_v2(int index,
struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg, uint32_t mclk_freq);
int afe_get_sp_rx_tmax_xmax_logging_data(
struct afe_sp_rx_tmax_xmax_logging_param *xt_logging,
u16 port_id);