diff --git a/asoc/lahaina.c b/asoc/lahaina.c index 82b18769c339..536f1738eb22 100644 --- a/asoc/lahaina.c +++ b/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(); diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 053262d07c87..6767b2c2b564 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -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( diff --git a/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h index 3056afc991fb..be75f2c37148 100644 --- a/include/dsp/q6afe-v2.h +++ b/include/dsp/q6afe-v2.h @@ -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,