From 7902b497b5cced89693f8814da3ef84511202a07 Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Thu, 6 Aug 2020 14:23:27 -0700 Subject: [PATCH] ASoC: lahaina: add support for CPS speaker protection Add support to parse static cps configuration from dt. Add support to send cps configuration for speaker protection usecases. Change-Id: I0166a378f24cedef07393bdbb8618df944c62984 Signed-off-by: Vignesh Kulothungan --- asoc/lahaina.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 221 insertions(+), 1 deletion(-) diff --git a/asoc/lahaina.c b/asoc/lahaina.c index 4a8a808871..40835c6100 100644 --- a/asoc/lahaina.c +++ b/asoc/lahaina.c @@ -73,7 +73,6 @@ #define WCD_MBHC_HS_V_MAX 1600 #define TDM_CHANNEL_MAX 8 -#define DEV_NAME_STR_LEN 32 #define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ @@ -83,6 +82,8 @@ #define WCN_CDC_SLIM_TX_CH_MAX 2 #define WCN_CDC_SLIM_TX_CH_MAX_LITO 3 +#define SWR_MAX_SLAVE_DEVICES 6 + enum { RX_PATH = 0, TX_PATH, @@ -195,6 +196,8 @@ struct msm_asoc_mach_data { u32 wsa_max_devs; u32 tdm_max_slots; /* Max TDM slots used */ int wcd_disabled; + int (*get_wsa_dev_num)(struct snd_soc_component*); + struct afe_cps_hw_intf_cfg cps_config; }; struct tdm_port { @@ -5017,6 +5020,106 @@ static int msm_snd_cdc_dma_startup(struct snd_pcm_substream *substream) return ret; } +static int send_cps_config(struct snd_soc_pcm_runtime *rtd, + u32 num_ch, u32 ch_mask) +{ + int i = 0; + int ret = 0; + int val = 0; + u8 dev_num = 0; + int param_size = 0; + int ch_configured = 0; + char wsa_cdc_name[DEV_NAME_STR_LEN]; + struct snd_soc_component *component = NULL; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + if (!pdata) { + pr_err("%s: pdata is NULL\n", __func__); + return -EINVAL; + } + + if (!num_ch) { + pr_err("%s: channel count is 0\n", __func__); + return -EINVAL; + } + + if (!pdata->get_wsa_dev_num) { + pr_err("%s: get_wsa_dev_num is NULL\n", __func__); + return -EINVAL; + } + + if (!pdata->cps_config.spkr_dep_cfg) { + pr_err("%s: spkr_dep_cfg is NULL\n", __func__); + return -EINVAL; + } + + if (!pdata->cps_config.hw_reg_cfg.lpass_wr_cmd_reg_phy_addr || + !pdata->cps_config.hw_reg_cfg.lpass_rd_cmd_reg_phy_addr || + !pdata->cps_config.hw_reg_cfg.lpass_rd_fifo_reg_phy_addr) { + pr_err("%s: cps static configuration is not set\n", __func__); + return -EINVAL; + } + + pdata->cps_config.lpass_hw_intf_cfg_mode = 1; + + while (ch_configured < num_ch) { + if (!(ch_mask & (1 << i))) { + i++; + continue; + } + + snprintf(wsa_cdc_name, sizeof(wsa_cdc_name), "wsa-codec.%d", + i+1); + + component = snd_soc_rtdcom_lookup(rtd, wsa_cdc_name); + if (!component) { + pr_err("%s: %s component is NULL\n", __func__, + wsa_cdc_name); + return -EINVAL; + } + + dev_num = pdata->get_wsa_dev_num(component); + if (dev_num < 0 || dev_num > SWR_MAX_SLAVE_DEVICES) { + pr_err("%s: invalid slave dev num : %d\n", __func__, + dev_num); + return -EINVAL; + } + + /* Clear stale dev num info */ + pdata->cps_config.spkr_dep_cfg[i].vbatt_pkd_reg_addr &= 0xFFFF; + pdata->cps_config.spkr_dep_cfg[i].temp_pkd_reg_addr &= 0xFFFF; + + val = 0; + + /* bits 20:23 carry swr device number */ + val |= dev_num << 20; + + /* bits 24:27 carry read length in bytes */ + val |= 1 << 24; + + /* Update dev num in packed reg addr */ + pdata->cps_config.spkr_dep_cfg[i].vbatt_pkd_reg_addr |= val; + pdata->cps_config.spkr_dep_cfg[i].temp_pkd_reg_addr |= val; + i++; + ch_configured++; + } + + param_size = sizeof(struct afe_cps_hw_intf_cfg) - + sizeof(pdata->cps_config.spkr_dep_cfg) + + (sizeof(struct lpass_swr_spkr_dep_cfg_t) + * pdata->cps_config.hw_reg_cfg.num_spkr); + + ret = afe_send_cps_config(msm_get_port_id(dai_link->id), + &pdata->cps_config, param_size); + if (ret) { + pr_err("%s: afe_send_cps_cfg failed\n", __func__); + } + + return ret; +} + static int msm_snd_cdc_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -5065,6 +5168,11 @@ static int msm_snd_cdc_dma_hw_params(struct snd_pcm_substream *substream, goto err; } + if (dai_link->id == MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0 || + dai_link->id == MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1) { + send_cps_config(rtd, user_set_rx_ch, + rx_ch_cdc_dma); + } } break; } @@ -7831,6 +7939,115 @@ static int msm_audio_ssr_register(struct device *dev) return ret; } +static void parse_cps_configuration(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata) +{ + int ret = 0; + int i = 0, j = 0; + u32 dt_values[MAX_CPS_LEVELS]; + + if (!pdev || !pdata || !pdata->wsa_max_devs) + return; + + pdata->get_wsa_dev_num = wsa883x_codec_get_dev_num; + pdata->cps_config.hw_reg_cfg.num_spkr = pdata->wsa_max_devs; + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,cps_reg_phy_addr", dt_values, + sizeof(dt_values)/sizeof(dt_values[0])); + if (ret) { + dev_dbg(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "qcom,cps_reg_phy_addr"); + } else { + pdata->cps_config.hw_reg_cfg.lpass_wr_cmd_reg_phy_addr = + dt_values[0]; + pdata->cps_config.hw_reg_cfg.lpass_rd_cmd_reg_phy_addr = + dt_values[1]; + pdata->cps_config.hw_reg_cfg.lpass_rd_fifo_reg_phy_addr = + dt_values[2]; + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,cps_threshold_levels", dt_values, + sizeof(dt_values)/sizeof(dt_values[0]) - 1); + if (ret) { + dev_dbg(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "qcom,cps_threshold_levels"); + } else { + pdata->cps_config.hw_reg_cfg.vbatt_lower2_threshold = + dt_values[0]; + pdata->cps_config.hw_reg_cfg.vbatt_lower1_threshold = + dt_values[1]; + } + + pdata->cps_config.spkr_dep_cfg = devm_kzalloc(&pdev->dev, + sizeof(struct lpass_swr_spkr_dep_cfg_t) + * pdata->wsa_max_devs, GFP_KERNEL); + if (!pdata->cps_config.spkr_dep_cfg) { + dev_err(&pdev->dev, "%s: spkr dep cfg alloc failed\n", __func__); + return; + } + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,cps_wsa_vbatt_temp_reg_addr", dt_values, + sizeof(dt_values)/sizeof(dt_values[0]) - 1); + if (ret) { + dev_dbg(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "qcom,cps_wsa_vbatt_temp_reg_addr"); + } else { + for (i = 0; i < pdata->wsa_max_devs; i++) { + pdata->cps_config.spkr_dep_cfg[i].vbatt_pkd_reg_addr = + dt_values[0]; + pdata->cps_config.spkr_dep_cfg[i].temp_pkd_reg_addr = + dt_values[1]; + } + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,cps_normal_values", dt_values, + sizeof(dt_values)/sizeof(dt_values[0])); + if (ret) { + dev_dbg(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "qcom,cps_normal_values"); + } else { + for (i = 0; i < pdata->wsa_max_devs; i++) { + for (j = 0; j < MAX_CPS_LEVELS; j++) { + pdata->cps_config.spkr_dep_cfg[i]. + value_normal_thrsd[j] = dt_values[j]; + } + } + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,cps_lower1_values", dt_values, + sizeof(dt_values)/sizeof(dt_values[0])); + if (ret) { + dev_dbg(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "qcom,cps_lower1_values"); + } else { + for (i = 0; i < pdata->wsa_max_devs; i++) { + for (j = 0; j < MAX_CPS_LEVELS; j++) { + pdata->cps_config.spkr_dep_cfg[i]. + value_low1_thrsd[j] = dt_values[j]; + } + } + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,cps_lower2_values", dt_values, + sizeof(dt_values)/sizeof(dt_values[0])); + if (ret) { + dev_dbg(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "qcom,cps_lower2_values"); + } else { + for (i = 0; i < pdata->wsa_max_devs; i++) { + for (j = 0; j < MAX_CPS_LEVELS; j++) { + pdata->cps_config.spkr_dep_cfg[i]. + value_low2_thrsd[j] = dt_values[j]; + } + } + } +} + static int msm_asoc_machine_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; @@ -8020,6 +8237,9 @@ static int msm_asoc_machine_probe(struct platform_device *pdev) for (index = PRIM_MI2S; index < MI2S_MAX; index++) atomic_set(&(pdata->mi2s_gpio_ref_count[index]), 0); + /* parse cps configuration from dt */ + parse_cps_configuration(pdev, pdata); + /* Register LPASS audio hw vote */ lpass_audio_hw_vote = devm_clk_get(&pdev->dev, "lpass_audio_hw_vote"); if (IS_ERR(lpass_audio_hw_vote)) {