mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-13 00:00:00 -04:00
asoc: lahaina: Add ext clk source support
For audio interfaces to be sourced from an external clock, one needs to configure clock attributes such as the clock root and RCG registers for operation at an appropriate clock frequency. Add support for this by registering for a callback with Q6AFE driver which during clock enable, provides clock attribute values and toggles the GPIO pin sourcing the external clock. Change-Id: I64a849e045a13dedd876b6a82c727a0e0610cbb7 Signed-off-by: Abhilash Sathyanarayana <quic_abhisath@quicinc.com>
This commit is contained in:
parent
789301acc7
commit
981ef85c0e
3 changed files with 767 additions and 10 deletions
755
asoc/lahaina.c
755
asoc/lahaina.c
|
|
@ -182,6 +182,51 @@ enum {
|
|||
AFE_LOOPBACK_TX_IDX_MAX,
|
||||
};
|
||||
|
||||
#define MCLK_CFG_CELLS 5
|
||||
struct ext_mclk_freq_cfg {
|
||||
u32 clk_freq;
|
||||
u32 div2x;
|
||||
u32 m;
|
||||
u32 n;
|
||||
u32 d;
|
||||
};
|
||||
|
||||
/* Coupled with "qcom,ext-mclk-srcs" DTSI property */
|
||||
#define MCLK_SRCS_CELLS 3
|
||||
struct ext_mclk_src_cfg {
|
||||
u32 clk_id;
|
||||
u32 clk_root;
|
||||
u32 gpio_idx;
|
||||
};
|
||||
|
||||
struct ext_mclk_cfg_info {
|
||||
u32 mclk_freq;
|
||||
const char *prop;
|
||||
struct ext_mclk_freq_cfg *mclk_cfg;
|
||||
u32 num_mclk_cfg;
|
||||
};
|
||||
|
||||
/* Coupled with "qcom,ext-mclk-oe-cfg" DTSI property */
|
||||
#define MCLK_OE_CFG_CELLS 3
|
||||
struct ext_mclk_oe_cfg {
|
||||
char __iomem *reg;
|
||||
uint32_t reset_val;
|
||||
uint32_t active_val;
|
||||
};
|
||||
|
||||
struct ext_mclk_gpio_info {
|
||||
struct device_node *gpio_p; /* used by pinctrl API */
|
||||
struct ext_mclk_oe_cfg oe_cfg;
|
||||
uint32_t ref_cnt;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct ext_mclk_src_info {
|
||||
u32 clk_id;
|
||||
u32 clk_root;
|
||||
struct ext_mclk_gpio_info *gpio_info;
|
||||
};
|
||||
|
||||
struct msm_asoc_mach_data {
|
||||
struct snd_info_entry *codec_root;
|
||||
int usbc_en2_gpio; /* used by gpio driver API */
|
||||
|
|
@ -195,6 +240,9 @@ struct msm_asoc_mach_data {
|
|||
struct pinctrl *usbc_en2_gpio_p; /* used by pinctrl API */
|
||||
struct device_node *hph_en1_gpio_p; /* used by pinctrl API */
|
||||
struct device_node *hph_en0_gpio_p; /* used by pinctrl API */
|
||||
bool supports_ext_mclk;
|
||||
struct ext_mclk_src_info *ext_mclk_srcs;
|
||||
u32 num_ext_mclk_srcs;
|
||||
bool is_afe_config_done;
|
||||
struct device_node *fsa_handle;
|
||||
struct clk *lpass_audio_hw_vote;
|
||||
|
|
@ -227,6 +275,19 @@ struct dev_config {
|
|||
u32 channels;
|
||||
};
|
||||
|
||||
static bool ext_mclk_enable;
|
||||
static uint32_t num_ext_mclk_gpios;
|
||||
static struct ext_mclk_gpio_info *ext_mclk_gpio_info;
|
||||
static struct ext_mclk_src_cfg *ext_mclk_src_info;
|
||||
static struct ext_mclk_cfg_info ext_mclk_freq_info[MCLK_FREQ_MAX] = {
|
||||
[MCLK_FREQ_11P2896_MHZ] = {11289600, "ext-mclk-cfg-11p2896", NULL, 0},
|
||||
[MCLK_FREQ_12P288_MHZ] = {12288000, "ext-mclk-cfg-12p288", NULL, 0},
|
||||
[MCLK_FREQ_16P384_MHZ] = {16384000, "ext-mclk-cfg-16p384", NULL, 0},
|
||||
[MCLK_FREQ_19P200_MHZ] = {19200000, "ext-mclk-cfg-19p200", NULL, 0},
|
||||
[MCLK_FREQ_22P5792_MHZ] = {22579200, "ext-mclk-cfg-22p5792", NULL, 0},
|
||||
[MCLK_FREQ_24P576_MHZ] = {24576000, "ext-mclk-cfg-24p576", NULL, 0},
|
||||
};
|
||||
|
||||
/* Default configuration of slimbus channels */
|
||||
static struct dev_config slim_rx_cfg[] = {
|
||||
[SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
|
||||
|
|
@ -1027,6 +1088,227 @@ static cpumask_t audio_cpu_map = CPU_MASK_NONE;
|
|||
static struct dev_pm_qos_request *msm_audio_req;
|
||||
static unsigned int qos_client_active_cnt;
|
||||
|
||||
static int lahaina_audio_vote(struct snd_soc_card *card, bool enable)
|
||||
{
|
||||
struct msm_asoc_mach_data *pdata = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (!card) {
|
||||
pr_err("%s: sound card is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata = snd_soc_card_get_drvdata(card);
|
||||
if (!pdata || !pdata->lpass_audio_hw_vote) {
|
||||
pr_err("%s: lpass audio hw voting not supported\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Locking and reference counting is handled by the underlying clock
|
||||
* framework.
|
||||
*/
|
||||
if (enable) {
|
||||
ret = clk_prepare_enable(pdata->lpass_audio_hw_vote);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "%s: audio vote error: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
clk_disable_unprepare(pdata->lpass_audio_hw_vote);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lahaina_populate_ext_mclk_cfg(struct ext_mclk_cfg_info *freq_cfg,
|
||||
uint32_t mclk_freq,
|
||||
struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg)
|
||||
{
|
||||
struct ext_mclk_freq_cfg *mclk_cfg = NULL;
|
||||
uint32_t mclk_cfg_entries = 0;
|
||||
enum afe_mclk_freq freq = MCLK_FREQ_MIN;
|
||||
int i = 0;
|
||||
|
||||
if (!freq_cfg || !dyn_mclk_cfg)
|
||||
return -EINVAL;
|
||||
|
||||
for (freq = MCLK_FREQ_MIN; freq < MCLK_FREQ_MAX; freq++) {
|
||||
if (freq_cfg[freq].mclk_freq == mclk_freq)
|
||||
break;
|
||||
}
|
||||
|
||||
if (freq == MCLK_FREQ_MAX) {
|
||||
pr_err("%s: unsupported mclk freq: %u\n", __func__, mclk_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!freq_cfg[freq].mclk_cfg ||
|
||||
!freq_cfg[freq].num_mclk_cfg) {
|
||||
pr_err("%s: cfg table unavailable for mclk freq: %u\n",
|
||||
__func__, mclk_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mclk_cfg = freq_cfg[freq].mclk_cfg;
|
||||
mclk_cfg_entries = freq_cfg[freq].num_mclk_cfg;
|
||||
|
||||
for (i = 0; i < mclk_cfg_entries; i++) {
|
||||
if (mclk_cfg[i].clk_freq == dyn_mclk_cfg->clk_freq_in_hz) {
|
||||
dyn_mclk_cfg->divider_2x = mclk_cfg[i].div2x;
|
||||
dyn_mclk_cfg->m = mclk_cfg[i].m;
|
||||
dyn_mclk_cfg->n = mclk_cfg[i].n;
|
||||
dyn_mclk_cfg->d = mclk_cfg[i].d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == mclk_cfg_entries) {
|
||||
pr_err("%s: requested output mclk freq %u is not supported\n",
|
||||
__func__, dyn_mclk_cfg->clk_freq_in_hz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lahaina_handle_ext_mclk_gpio(struct snd_soc_card *card,
|
||||
struct ext_mclk_gpio_info *gpio_info,
|
||||
uint32_t enable)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!gpio_info)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&gpio_info->lock);
|
||||
if (enable) {
|
||||
if (++gpio_info->ref_cnt == 1) {
|
||||
ret = msm_cdc_pinctrl_select_active_state(gpio_info->gpio_p);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: couldn't activate mclk pinctrl\n",
|
||||
__func__);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (gpio_info->oe_cfg.reg != NULL) {
|
||||
ret = lahaina_audio_vote(card, true);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: HW voting failed, ret: %d\n",
|
||||
__func__, ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
iowrite32(gpio_info->oe_cfg.active_val,
|
||||
gpio_info->oe_cfg.reg);
|
||||
|
||||
lahaina_audio_vote(card, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (--gpio_info->ref_cnt == 0) {
|
||||
ret = msm_cdc_pinctrl_select_sleep_state(gpio_info->gpio_p);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: couldn't sleep mclk pinctrl\n",
|
||||
__func__);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (gpio_info->oe_cfg.reg != NULL) {
|
||||
ret = lahaina_audio_vote(card, true);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: HW voting failed, ret: %d\n",
|
||||
__func__, ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
iowrite32(gpio_info->oe_cfg.reset_val,
|
||||
gpio_info->oe_cfg.reg);
|
||||
|
||||
lahaina_audio_vote(card, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&gpio_info->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lahaina_enable_and_get_mclk_cfg(void *private_data, uint32_t enable,
|
||||
uint32_t mclk_freq,
|
||||
struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg)
|
||||
{
|
||||
struct snd_soc_card *card = (struct snd_soc_card *)private_data;
|
||||
struct msm_asoc_mach_data *pdata = NULL;
|
||||
struct ext_mclk_src_info *ext_mclk_src = NULL;
|
||||
int i = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!card || !dyn_mclk_cfg)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ext_mclk_enable) {
|
||||
dev_err(card->dev, "%s: ext mclk support not enabled on the platform\n",
|
||||
__func__);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
pdata = snd_soc_card_get_drvdata(card);
|
||||
if (!pdata || !pdata->ext_mclk_srcs || !pdata->num_ext_mclk_srcs)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < pdata->num_ext_mclk_srcs; i++) {
|
||||
if (pdata->ext_mclk_srcs[i].clk_id == dyn_mclk_cfg->clk_id)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == pdata->num_ext_mclk_srcs) {
|
||||
dev_err(card->dev, "%s: unsupported clk id for ext mclk support: %u\n",
|
||||
__func__, dyn_mclk_cfg->clk_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ext_mclk_src = &pdata->ext_mclk_srcs[i];
|
||||
if (!ext_mclk_src) {
|
||||
dev_err(card->dev, "%s: ext mclk src/clk cfg unavailable for mclk: %u\n",
|
||||
__func__, pdata->ext_mclk_srcs[i].clk_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Populate clk root */
|
||||
dyn_mclk_cfg->clk_root = (uint16_t) ext_mclk_src->clk_root;
|
||||
|
||||
/* Populate div2x, M, N, D values */
|
||||
ret = lahaina_populate_ext_mclk_cfg(ext_mclk_freq_info, mclk_freq,
|
||||
dyn_mclk_cfg);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: unable to populate ext mclk cfg, ret: %d\n", __func__, ret);
|
||||
goto reset;
|
||||
}
|
||||
|
||||
/* Enable/disable ext mclk GPIO */
|
||||
ret = lahaina_handle_ext_mclk_gpio(card, ext_mclk_src->gpio_info, enable);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: unable to enable/disable ext mclk gpio, ret: %d\n",
|
||||
__func__, ret);
|
||||
goto reset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
reset:
|
||||
/* Reset clk cfg */
|
||||
dyn_mclk_cfg->divider_2x = 0;
|
||||
dyn_mclk_cfg->m = 0;
|
||||
dyn_mclk_cfg->n = 0;
|
||||
dyn_mclk_cfg->d = 0;
|
||||
dyn_mclk_cfg->clk_root = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void msm_audio_add_qos_request(void)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -4742,6 +5024,25 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
|
|||
aux_pcm_tx_sample_rate_put),
|
||||
};
|
||||
|
||||
static int msm_ext_mclk_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = ext_mclk_enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_ext_mclk_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ext_mclk_enable = (bool) ucontrol->value.integer.value[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new msm_ext_mclk_controls[] = {
|
||||
SOC_SINGLE_BOOL_EXT("EXT MCLK Enable", 0,
|
||||
msm_ext_mclk_get, msm_ext_mclk_put),
|
||||
};
|
||||
|
||||
static int msm_ext_disp_get_idx_from_beid(int32_t be_id)
|
||||
{
|
||||
int idx;
|
||||
|
|
@ -8806,6 +9107,425 @@ static void parse_cps_configuration(struct platform_device *pdev,
|
|||
}
|
||||
}
|
||||
|
||||
static int msm_parse_ext_mclk_gpios(struct snd_soc_card *card,
|
||||
struct ext_mclk_gpio_info **ext_mclk_gpios)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t len = 0;
|
||||
uint32_t num_gpios = 0;
|
||||
uint32_t cells = 0;
|
||||
struct device_node *np = NULL;
|
||||
struct ext_mclk_gpio_info *gpio_info = NULL;
|
||||
u32 ext_mclk_oe_cfg_arr[MCLK_OE_CFG_CELLS];
|
||||
int i = 0;
|
||||
|
||||
if (!card || !card->dev || !card->dev->of_node)
|
||||
return -EINVAL;
|
||||
|
||||
np = card->dev->of_node;
|
||||
|
||||
if (!of_get_property(np, "qcom,ext-mclk-gpios", &len)) {
|
||||
dev_err(card->dev, "%s: ext mclk gpios not found in DT\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!len) {
|
||||
dev_err(card->dev, "%s: invalid ext mclk gpios configuration in DT\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
num_gpios = len / sizeof(uint32_t);
|
||||
dev_dbg(card->dev, "%d ext mclk gpios found\n", num_gpios);
|
||||
|
||||
gpio_info = devm_kzalloc(card->dev,
|
||||
num_gpios * sizeof(struct ext_mclk_gpio_info),
|
||||
GFP_KERNEL);
|
||||
if (!gpio_info)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_gpios; i++) {
|
||||
mutex_init(&gpio_info[i].lock);
|
||||
gpio_info[i].ref_cnt = 0;
|
||||
gpio_info[i].gpio_p = of_parse_phandle(np, "qcom,ext-mclk-gpios", i);
|
||||
if (!gpio_info[i].gpio_p) {
|
||||
dev_err(card->dev, "ext mclk gpio %d device node is NULL", i);
|
||||
ret = -EINVAL;
|
||||
goto free_gpio_info;
|
||||
}
|
||||
|
||||
/* Parse ext clk OE register info from DT if it's a LPI GPIO */
|
||||
if (of_property_read_bool(gpio_info[i].gpio_p, "qcom,lpi-gpios")) {
|
||||
|
||||
ret = of_property_read_u32(gpio_info[i].gpio_p, "#ext-mclk-oe-cfg-cells", &cells);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: ext mclk oe cfg cells not found in DT\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto free_gpio_info;
|
||||
}
|
||||
|
||||
if (cells != MCLK_OE_CFG_CELLS) {
|
||||
dev_err(card->dev, "%s: invalid ext-mclk-oe-cfg-cells in DT\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto free_gpio_info;
|
||||
};
|
||||
|
||||
ret = of_property_read_u32_array(gpio_info[i].gpio_p,
|
||||
"qcom,ext-mclk-oe-cfg",
|
||||
ext_mclk_oe_cfg_arr,
|
||||
cells);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "could not find qcom,ext-mclk-oe-cfg DT entry for lpi-gpio");
|
||||
ret = -EINVAL;
|
||||
goto free_gpio_info;
|
||||
}
|
||||
|
||||
gpio_info[i].oe_cfg.reg = devm_ioremap(card->dev,
|
||||
ext_mclk_oe_cfg_arr[0], 0x4);
|
||||
if (!gpio_info[i].oe_cfg.reg) {
|
||||
dev_err(card->dev, "%s: failed to remap ext mclk OE reg 0x%x",
|
||||
__func__, ext_mclk_oe_cfg_arr[0]);
|
||||
ret = -ENOMEM;
|
||||
goto free_gpio_info;
|
||||
}
|
||||
gpio_info[i].oe_cfg.reset_val = ext_mclk_oe_cfg_arr[1];
|
||||
gpio_info[i].oe_cfg.active_val = ext_mclk_oe_cfg_arr[2];
|
||||
dev_dbg(card->dev, "%s: oe_cfg[%d].reg = 0x%x, reset = %u, active = %u\n",
|
||||
__func__, i, ext_mclk_oe_cfg_arr[0], gpio_info[i].oe_cfg.reset_val,
|
||||
gpio_info[i].oe_cfg.active_val);
|
||||
} else {
|
||||
gpio_info[i].oe_cfg.reg = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
num_ext_mclk_gpios = num_gpios;
|
||||
*ext_mclk_gpios = gpio_info;
|
||||
dev_dbg(card->dev, "%s: ext mclk gpios probe successful\n", __func__);
|
||||
return 0;
|
||||
|
||||
free_gpio_info:
|
||||
for (; i >= 0; i--) {
|
||||
mutex_destroy(&gpio_info[i].lock);
|
||||
of_node_put(gpio_info[i].gpio_p);
|
||||
}
|
||||
devm_kfree(card->dev, gpio_info);
|
||||
gpio_info = NULL;
|
||||
|
||||
*ext_mclk_gpios = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_parse_ext_mclk_srcs(struct snd_soc_card *card,
|
||||
struct ext_mclk_src_cfg **ext_mclk_src,
|
||||
uint32_t *n_srcs)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ext_mclk_src_cfg *src_cfg = NULL;
|
||||
struct device_node *np = NULL;
|
||||
uint32_t len = 0;
|
||||
uint32_t num_srcs = 0;
|
||||
uint32_t cells = 0;
|
||||
|
||||
if (!card || !card->dev || !card->dev->of_node)
|
||||
return -EINVAL;
|
||||
|
||||
np = card->dev->of_node;
|
||||
|
||||
if (!of_get_property(np, "qcom,ext-mclk-srcs", &len)) {
|
||||
dev_err(card->dev, "%s: ext mclk srcs cfg not found in DT\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "#ext-mclk-srcs-cells", &cells);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: ext mclk srcs cells not found in DT\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!len || (len % (cells * sizeof(uint32_t))) ||
|
||||
(cells != MCLK_SRCS_CELLS)) {
|
||||
dev_err(card->dev, "%s: invalid ext mclk srcs cfg in DT\n", __func__);
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
num_srcs = len / (cells * sizeof(uint32_t));
|
||||
|
||||
src_cfg = devm_kzalloc(card->dev,
|
||||
num_srcs * sizeof(struct ext_mclk_src_cfg),
|
||||
GFP_KERNEL);
|
||||
if (!src_cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_property_read_u32_array(np, "qcom,ext-mclk-srcs", (u32 *)src_cfg,
|
||||
cells * num_srcs);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: could not find %s entry in dt\n",
|
||||
__func__, "qcom,ext-mclk-srcs");
|
||||
ret = -EINVAL;
|
||||
goto free_mclk_array;
|
||||
}
|
||||
|
||||
*ext_mclk_src = src_cfg;
|
||||
*n_srcs = num_srcs;
|
||||
dev_dbg(card->dev, "%s: ext mclk srcs probe successful\n", __func__);
|
||||
return 0;
|
||||
|
||||
free_mclk_array:
|
||||
devm_kfree(card->dev, src_cfg);
|
||||
src_cfg = NULL;
|
||||
|
||||
*ext_mclk_src = NULL;
|
||||
*n_srcs = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_parse_dt_ext_mclk_cfg(struct snd_soc_card *card,
|
||||
enum afe_mclk_freq freq)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ext_mclk_freq_cfg *mclk_cfg = NULL;
|
||||
uint32_t len = 0;
|
||||
uint32_t num_cfg = 0;
|
||||
uint32_t cells = 0;
|
||||
int i = 0;
|
||||
struct device_node *np = NULL;
|
||||
uint32_t *freq_cfg_tbl = NULL;
|
||||
|
||||
if (!card || !card->dev || !card->dev->of_node)
|
||||
return -EINVAL;
|
||||
|
||||
np = card->dev->of_node;
|
||||
|
||||
if (!of_get_property(np, ext_mclk_freq_info[freq].prop, &len)) {
|
||||
dev_dbg(card->dev, "%s: ext mclk cfg for %s not found in DT\n",
|
||||
__func__, ext_mclk_freq_info[freq].prop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "#ext-mclk-cfg-cells", &cells);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: ext mclk cfg cells not found in DT\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!len || (len % (cells * sizeof(uint32_t))) ||
|
||||
(cells != MCLK_CFG_CELLS)) {
|
||||
dev_err(card->dev, "%s: invalid mclk configuration in DT\n", __func__);
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
num_cfg = len / (cells * sizeof(uint32_t));
|
||||
mclk_cfg = devm_kzalloc(card->dev, num_cfg * sizeof(struct ext_mclk_freq_cfg),
|
||||
GFP_KERNEL);
|
||||
if (!mclk_cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
freq_cfg_tbl = devm_kzalloc(card->dev, cells * num_cfg * sizeof(uint32_t),
|
||||
GFP_KERNEL);
|
||||
if (!freq_cfg_tbl) {
|
||||
ret = -ENOMEM;
|
||||
goto free_mclk_cfg;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(np, ext_mclk_freq_info[freq].prop,
|
||||
freq_cfg_tbl, cells * num_cfg);
|
||||
if (ret)
|
||||
goto free_freq_cfg_tbl;
|
||||
|
||||
dev_dbg(card->dev, "table for %u freq\n",
|
||||
ext_mclk_freq_info[freq].mclk_freq);
|
||||
|
||||
for (i = 0; i < num_cfg; i++) {
|
||||
memcpy(&mclk_cfg[i], &freq_cfg_tbl[i * cells],
|
||||
sizeof(uint32_t) * cells);
|
||||
dev_dbg(card->dev,
|
||||
"clk freq (Hz): %u, div2x: %u, m: %u, n: %u, d: %u\n",
|
||||
mclk_cfg[i].clk_freq, mclk_cfg[i].div2x, mclk_cfg[i].m,
|
||||
mclk_cfg[i].n, mclk_cfg[i].d);
|
||||
}
|
||||
|
||||
ext_mclk_freq_info[freq].mclk_cfg = mclk_cfg;
|
||||
ext_mclk_freq_info[freq].num_mclk_cfg = num_cfg;
|
||||
|
||||
devm_kfree(card->dev, freq_cfg_tbl);
|
||||
freq_cfg_tbl = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
free_freq_cfg_tbl:
|
||||
devm_kfree(card->dev, freq_cfg_tbl);
|
||||
freq_cfg_tbl = NULL;
|
||||
free_mclk_cfg:
|
||||
devm_kfree(card->dev, mclk_cfg);
|
||||
mclk_cfg = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_parse_ext_mclk_freq_cfg(struct snd_soc_card *card)
|
||||
{
|
||||
int ret = 0;
|
||||
int i = MCLK_FREQ_MIN;
|
||||
|
||||
if (!card || !card->dev || !card->dev->of_node)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = MCLK_FREQ_MIN; i < MCLK_FREQ_MAX; i++) {
|
||||
ret = msm_parse_dt_ext_mclk_cfg(card, i);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(card->dev, "%s: ext mclk freq probe successful!\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lahaina_ext_mclk_cfg_deinit(struct snd_soc_card *card)
|
||||
{
|
||||
struct msm_asoc_mach_data *pdata = NULL;
|
||||
int i = 0;
|
||||
enum afe_mclk_freq freq = MCLK_FREQ_MIN;
|
||||
|
||||
if (!card || !card->dev)
|
||||
return;
|
||||
|
||||
pdata = (struct msm_asoc_mach_data *) snd_soc_card_get_drvdata(card);
|
||||
if (!pdata)
|
||||
return;
|
||||
|
||||
afe_unregister_ext_mclk_cb();
|
||||
|
||||
for (i = 0; i < pdata->num_ext_mclk_srcs; i++)
|
||||
pdata->ext_mclk_srcs[i].gpio_info = NULL;
|
||||
|
||||
pdata->num_ext_mclk_srcs = 0;
|
||||
|
||||
if (ext_mclk_gpio_info) {
|
||||
for (i = 0; i < num_ext_mclk_gpios; i++) {
|
||||
mutex_destroy(&ext_mclk_gpio_info[i].lock);
|
||||
of_node_put(ext_mclk_gpio_info[i].gpio_p);
|
||||
}
|
||||
devm_kfree(card->dev, ext_mclk_gpio_info);
|
||||
ext_mclk_gpio_info = NULL;
|
||||
}
|
||||
|
||||
if (ext_mclk_src_info) {
|
||||
devm_kfree(card->dev, ext_mclk_src_info);
|
||||
ext_mclk_src_info = NULL;
|
||||
}
|
||||
|
||||
for (freq = MCLK_FREQ_MIN; freq < MCLK_FREQ_MAX; freq++) {
|
||||
if (ext_mclk_freq_info[freq].mclk_cfg)
|
||||
devm_kfree(card->dev, ext_mclk_freq_info[freq].mclk_cfg);
|
||||
ext_mclk_freq_info[freq].mclk_cfg = NULL;
|
||||
ext_mclk_freq_info[freq].num_mclk_cfg = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int lahaina_ext_mclk_init_controls(struct snd_soc_card *card)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!card) {
|
||||
pr_err("%s: sound card is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_soc_add_card_controls(card, msm_ext_mclk_controls,
|
||||
ARRAY_SIZE(msm_ext_mclk_controls));
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "%s: add_card_controls failed for ext mclk ctls: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lahaina_ext_mclk_cfg_init(struct snd_soc_card *card)
|
||||
{
|
||||
int ret = 0;
|
||||
struct msm_asoc_mach_data *pdata = NULL;
|
||||
uint32_t num_ext_mclk_srcs = 0;
|
||||
int i = 0;
|
||||
|
||||
if (!card || !card->dev || !card->dev->of_node)
|
||||
return -EINVAL;
|
||||
|
||||
pdata = (struct msm_asoc_mach_data *) snd_soc_card_get_drvdata(card);
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
ret = msm_parse_ext_mclk_gpios(card, &ext_mclk_gpio_info);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: unable to parse ext mclk gpios, ret: %d\n",
|
||||
__func__, ret);
|
||||
goto deinit;
|
||||
}
|
||||
|
||||
ret = msm_parse_ext_mclk_srcs(card, &ext_mclk_src_info,
|
||||
&num_ext_mclk_srcs);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: unable to parse ext mclk srcs, ret: %d\n",
|
||||
__func__, ret);
|
||||
goto deinit;
|
||||
}
|
||||
|
||||
ret = msm_parse_ext_mclk_freq_cfg(card);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: unable to parse ext mclk freq cfg, ret: %d\n",
|
||||
__func__, ret);
|
||||
goto deinit;
|
||||
}
|
||||
|
||||
pdata->num_ext_mclk_srcs = num_ext_mclk_srcs;
|
||||
|
||||
if (pdata->num_ext_mclk_srcs) {
|
||||
pdata->ext_mclk_srcs = devm_kzalloc(card->dev,
|
||||
num_ext_mclk_srcs * sizeof(struct ext_mclk_src_info),
|
||||
GFP_KERNEL);
|
||||
if (!pdata->ext_mclk_srcs) {
|
||||
pdata->num_ext_mclk_srcs = 0;
|
||||
ret = -ENOMEM;
|
||||
goto deinit;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_ext_mclk_srcs; i++) {
|
||||
pdata->ext_mclk_srcs[i].clk_id = ext_mclk_src_info[i].clk_id;
|
||||
pdata->ext_mclk_srcs[i].clk_root = ext_mclk_src_info[i].clk_root;
|
||||
pdata->ext_mclk_srcs[i].gpio_info = &ext_mclk_gpio_info[ext_mclk_src_info[i].gpio_idx];
|
||||
|
||||
dev_dbg(card->dev, "%s: clk id: 0x%x clk root: 0x%x gpio idx: %d\n",
|
||||
__func__, pdata->ext_mclk_srcs[i].clk_id,
|
||||
pdata->ext_mclk_srcs[i].clk_root,
|
||||
ext_mclk_src_info[i].gpio_idx);
|
||||
}
|
||||
|
||||
ret = lahaina_ext_mclk_init_controls(card);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: Could not init ext mclk mixer ctls, ret: %d\n",
|
||||
__func__, ret);
|
||||
goto deinit;
|
||||
}
|
||||
|
||||
ret = afe_register_ext_mclk_cb(lahaina_enable_and_get_mclk_cfg,
|
||||
(void *)card);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "%s: Could not register afe ext mclk cb, ret: %d\n",
|
||||
__func__, ret);
|
||||
goto deinit;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(card->dev, "%s: ext mclk init successful!\n", __func__);
|
||||
|
||||
return 0;
|
||||
|
||||
deinit:
|
||||
lahaina_ext_mclk_cfg_deinit(card);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_asoc_machine_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = NULL;
|
||||
|
|
@ -8864,15 +9584,15 @@ static int msm_asoc_machine_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
/* Get maximum WSA device count for this platform */
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,wsa-max-devs", &pdata->wsa_max_devs);
|
||||
if (ret) {
|
||||
dev_info(&pdev->dev,
|
||||
"%s: wsa-max-devs property missing in DT %s, ret = %d\n",
|
||||
__func__, pdev->dev.of_node->full_name, ret);
|
||||
pdata->wsa_max_devs = 0;
|
||||
}
|
||||
/* Get maximum WSA device count for this platform */
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,wsa-max-devs", &pdata->wsa_max_devs);
|
||||
if (ret) {
|
||||
dev_info(&pdev->dev,
|
||||
"%s: wsa-max-devs property missing in DT %s, ret = %d\n",
|
||||
__func__, pdev->dev.of_node->full_name, ret);
|
||||
pdata->wsa_max_devs = 0;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, card);
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
|
|
@ -8887,6 +9607,19 @@ static int msm_asoc_machine_probe(struct platform_device *pdev)
|
|||
dev_info(&pdev->dev, "%s: Sound card %s registered\n",
|
||||
__func__, card->name);
|
||||
|
||||
/* Enable ext clk support ONLY after sound card enumeration and
|
||||
* registration has occurred with the internal clock
|
||||
*/
|
||||
if (of_property_read_bool(pdev->dev.of_node, "qcom,supports-ext-mclk")) {
|
||||
ret = lahaina_ext_mclk_cfg_init(card);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: ext mclk cfg init from DT failed: %d\n",
|
||||
__func__, ret);
|
||||
goto err;
|
||||
}
|
||||
pdata->supports_ext_mclk = 1;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "qcom,tdm-max-slots",
|
||||
&pdata->tdm_max_slots);
|
||||
if (ret) {
|
||||
|
|
@ -9029,7 +9762,11 @@ err:
|
|||
static int msm_asoc_machine_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct msm_asoc_mach_data *pdata = NULL;
|
||||
|
||||
pdata = snd_soc_card_get_drvdata(card);
|
||||
if (pdata && pdata->supports_ext_mclk)
|
||||
lahaina_ext_mclk_cfg_deinit(card);
|
||||
snd_event_master_deregister(&pdev->dev);
|
||||
snd_soc_unregister_card(card);
|
||||
msm_i2s_auxpcm_deinit();
|
||||
|
|
|
|||
|
|
@ -136,6 +136,10 @@ static int afe_loopback_tx_port_id = -1;
|
|||
static struct msm_pcm_channel_mixer ec_ref_chmix_cfg[MSM_FRONTEND_DAI_MAX];
|
||||
static struct msm_ec_ref_port_cfg ec_ref_port_cfg;
|
||||
|
||||
static int32_t mclk_cfg_be_idx;
|
||||
static int32_t mclk_cfg_src_id;
|
||||
static uint32_t mclk_cfg_freq;
|
||||
|
||||
#define WEIGHT_0_DB 0x4000
|
||||
/* all the FEs which can support channel mixer */
|
||||
static struct msm_pcm_channel_mixer channel_mixer[MSM_FRONTEND_DAI_MM_SIZE];
|
||||
|
|
@ -42813,6 +42817,16 @@ static const struct snd_kcontrol_new
|
|||
},
|
||||
};
|
||||
|
||||
static int msm_routing_get_mclk_src_cfg(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = mclk_cfg_be_idx;
|
||||
ucontrol->value.integer.value[1] = mclk_cfg_src_id;
|
||||
ucontrol->value.integer.value[2] = mclk_cfg_freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_routing_put_mclk_src_cfg(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
|
|
@ -42825,6 +42839,10 @@ static int msm_routing_put_mclk_src_cfg(struct snd_kcontrol *kcontrol,
|
|||
mclk_src_id = ucontrol->value.integer.value[1];
|
||||
mclk_freq = ucontrol->value.integer.value[2];
|
||||
|
||||
mclk_cfg_be_idx = be_idx;
|
||||
mclk_cfg_src_id = mclk_src_id;
|
||||
mclk_cfg_freq = mclk_freq;
|
||||
|
||||
if (be_idx < 0 || be_idx >= MSM_BACKEND_DAI_MAX) {
|
||||
pr_err("%s: Invalid be id %d\n", __func__, be_idx);
|
||||
return -EINVAL;
|
||||
|
|
@ -42854,7 +42872,8 @@ static int msm_routing_put_mclk_src_cfg(struct snd_kcontrol *kcontrol,
|
|||
|
||||
static const struct snd_kcontrol_new mclk_src_controls[] = {
|
||||
SOC_SINGLE_MULTI_EXT("MCLK_SRC CFG", SND_SOC_NOPM, 0, 24576000, 0, 3,
|
||||
NULL, msm_routing_put_mclk_src_cfg),
|
||||
msm_routing_get_mclk_src_cfg,
|
||||
msm_routing_put_mclk_src_cfg),
|
||||
};
|
||||
|
||||
static int msm_routing_stereo_channel_reverse_control_get(
|
||||
|
|
|
|||
|
|
@ -419,6 +419,7 @@ enum afe_mclk_freq {
|
|||
MCLK_FREQ_11P2896_MHZ = MCLK_FREQ_MIN,
|
||||
MCLK_FREQ_12P288_MHZ,
|
||||
MCLK_FREQ_16P384_MHZ,
|
||||
MCLK_FREQ_19P200_MHZ,
|
||||
MCLK_FREQ_22P5792_MHZ,
|
||||
MCLK_FREQ_24P576_MHZ,
|
||||
MCLK_FREQ_MAX,
|
||||
|
|
|
|||
Loading…
Reference in a new issue