From 751d49a88b3a27fa80bd9e000b87a040a908d6d1 Mon Sep 17 00:00:00 2001 From: Junkai Cai Date: Fri, 21 May 2021 13:31:59 -0700 Subject: [PATCH] asoc: lpass-cdc: reduce WSA digital gain when cooling is enabled The digital gain will be adjusted lower than what userspace set during playback if the device temperature reach the threshold. Whenever digital gain is changed from userspace, codec will check the cooling state and adjust the gain. Change-Id: I52df0f96cf20b90a9bdad70b9c117eed82145fb2 Signed-off-by: Junkai Cai --- asoc/codecs/lpass-cdc/lpass-cdc-wsa-macro.c | 126 ++++++++++++++++--- asoc/codecs/lpass-cdc/lpass-cdc-wsa2-macro.c | 126 ++++++++++++++++--- 2 files changed, 224 insertions(+), 28 deletions(-) diff --git a/asoc/codecs/lpass-cdc/lpass-cdc-wsa-macro.c b/asoc/codecs/lpass-cdc/lpass-cdc-wsa-macro.c index 8ff3e0ce99..d18cd2756a 100644 --- a/asoc/codecs/lpass-cdc/lpass-cdc-wsa-macro.c +++ b/asoc/codecs/lpass-cdc/lpass-cdc-wsa-macro.c @@ -193,6 +193,18 @@ struct lpass_cdc_wsa_macro_swr_ctrl_data { struct platform_device *wsa_swr_pdev; }; +#define LPASS_CDC_WSA_MACRO_SET_VOLUME_TLV(xname, xreg, xmin, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ + .put = lpass_cdc_wsa_macro_set_digital_volume, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .rreg = xreg, \ + .min = xmin, .max = xmax, .platform_max = xmax, \ + .sign_bit = 7,} } + struct lpass_cdc_wsa_macro_swr_ctrl_platform_data { void *handle; /* holds codec private data */ int (*read)(void *handle, int reg); @@ -276,9 +288,11 @@ struct lpass_cdc_wsa_macro_priv { u16 default_clk_id; u32 pcm_rate_vi; int wsa_digital_mute_status[LPASS_CDC_WSA_MACRO_RX_MAX]; + u8 original_gain; struct thermal_cooling_device *tcdev; uint32_t thermal_cur_state; uint32_t thermal_max_state; + struct work_struct lpass_cdc_wsa_macro_cooling_work; }; static struct snd_soc_dai_driver lpass_cdc_wsa_macro_dai[]; @@ -1891,6 +1905,44 @@ static int lpass_cdc_wsa_macro_set_rx_mute_status(struct snd_kcontrol *kcontrol, return ret; } +static int lpass_cdc_wsa_macro_set_digital_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct device *wsa_dev = NULL; + struct lpass_cdc_wsa_macro_priv *wsa_priv = NULL; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + u8 gain = 0; + int ret = 0; + + if (!lpass_cdc_wsa_macro_get_data(component, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + if (!wsa_priv) { + pr_err("%s: priv is null for macro!\n", + __func__); + return -EINVAL; + } + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + wsa_priv->original_gain = (u8)snd_soc_component_read(wsa_priv->component, + mc->reg); + + if (wsa_priv->thermal_cur_state > 0) { + gain = (u8)(wsa_priv->original_gain - wsa_priv->thermal_cur_state); + snd_soc_component_update_bits(wsa_priv->component, + mc->reg, 0xFF, gain); + dev_dbg(wsa_priv->dev, + "%s: Current thermal state: %d, adjusted gain: %x\n", + __func__, wsa_priv->thermal_cur_state, gain); + } + + return ret; +} + static int lpass_cdc_wsa_macro_get_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2158,12 +2210,12 @@ static const struct snd_kcontrol_new lpass_cdc_wsa_macro_snd_controls[] = { LPASS_CDC_WSA_MACRO_SOFTCLIP1, 1, 0, lpass_cdc_wsa_macro_soft_clip_enable_get, lpass_cdc_wsa_macro_soft_clip_enable_put), - SOC_SINGLE_S8_TLV("WSA_RX0 Digital Volume", - LPASS_CDC_WSA_RX0_RX_VOL_CTL, - -84, 40, digital_gain), - SOC_SINGLE_S8_TLV("WSA_RX1 Digital Volume", - LPASS_CDC_WSA_RX1_RX_VOL_CTL, - -84, 40, digital_gain), + LPASS_CDC_WSA_MACRO_SET_VOLUME_TLV("WSA_RX0 Digital Volume", + LPASS_CDC_WSA_RX0_RX_VOL_CTL, + -84, 40, digital_gain), + LPASS_CDC_WSA_MACRO_SET_VOLUME_TLV("WSA_RX1 Digital Volume", + LPASS_CDC_WSA_RX1_RX_VOL_CTL, + -84, 40, digital_gain), SOC_SINGLE_EXT("WSA_RX0 Digital Mute", SND_SOC_NOPM, LPASS_CDC_WSA_MACRO_RX0, 1, 0, lpass_cdc_wsa_macro_get_rx_mute_status, lpass_cdc_wsa_macro_set_rx_mute_status), @@ -2747,7 +2799,6 @@ static int lpass_cdc_wsa_macro_set_cur_state( unsigned long state) { struct lpass_cdc_wsa_macro_priv *wsa_priv = cdev->devdata; - u8 gain = 0; if (!wsa_priv) { pr_err("%s: cdev->devdata is NULL\n", __func__); @@ -2759,15 +2810,11 @@ static int lpass_cdc_wsa_macro_set_cur_state( else wsa_priv->thermal_cur_state = wsa_priv->thermal_max_state; - gain = (u8)(gain - wsa_priv->thermal_cur_state); dev_dbg(wsa_priv->dev, - "%s: requested state:%d, actual state: %d, gain: %#x\n", - __func__, state, wsa_priv->thermal_cur_state, gain); + "%s: requested state:%d, actual state: %d\n", + __func__, state, wsa_priv->thermal_cur_state); - snd_soc_component_update_bits(wsa_priv->component, - LPASS_CDC_WSA_RX0_RX_VOL_CTL, 0xFF, gain); - snd_soc_component_update_bits(wsa_priv->component, - LPASS_CDC_WSA_RX1_RX_VOL_CTL, 0xFF, gain); + schedule_work(&wsa_priv->lpass_cdc_wsa_macro_cooling_work); return 0; } @@ -2958,6 +3005,55 @@ err: return; } +static void lpass_cdc_wsa_macro_cooling_adjust_gain(struct work_struct *work) +{ + struct lpass_cdc_wsa_macro_priv *wsa_priv; + struct snd_soc_dapm_context *dapm; + u8 gain = 0; + u32 ctl_reg; + + wsa_priv = container_of(work, struct lpass_cdc_wsa_macro_priv, + lpass_cdc_wsa_macro_cooling_work); + if (!wsa_priv) { + pr_err("%s: priv is null for macro!\n", + __func__); + return; + } + if (!wsa_priv->dev || !wsa_priv->dev->of_node) { + dev_err(wsa_priv->dev, + "%s: DT node for wsa_priv does not exist\n", __func__); + return; + } + + dapm = snd_soc_component_get_dapm(wsa_priv->component); + + /* Only adjust the volume when WSA clock is enabled */ + ctl_reg = snd_soc_component_read(wsa_priv->component, + LPASS_CDC_WSA_RX0_RX_PATH_CTL); + if (ctl_reg & 0x20) { + gain = (u8)(wsa_priv->original_gain - wsa_priv->thermal_cur_state); + snd_soc_component_update_bits(wsa_priv->component, + LPASS_CDC_WSA_RX0_RX_VOL_CTL, 0xFF, gain); + dev_dbg(wsa_priv->dev, + "%s: RX0 current thermal state: %d, adjusted gain: %#x\n", + __func__, wsa_priv->thermal_cur_state, gain); + } + + /* Only adjust the volume when WSA clock is enabled */ + ctl_reg = snd_soc_component_read(wsa_priv->component, + LPASS_CDC_WSA_RX1_RX_PATH_CTL); + if (ctl_reg & 0x20) { + gain = (u8)(wsa_priv->original_gain - wsa_priv->thermal_cur_state); + snd_soc_component_update_bits(wsa_priv->component, + LPASS_CDC_WSA_RX1_RX_VOL_CTL, 0xFF, gain); + dev_dbg(wsa_priv->dev, + "%s: RX1 current thermal state: %d, adjusted gain: %#x\n", + __func__, wsa_priv->thermal_cur_state, gain); + } + + return; +} + static void lpass_cdc_wsa_macro_init_ops(struct macro_ops *ops, char __iomem *wsa_io_base) { @@ -3037,6 +3133,8 @@ static int lpass_cdc_wsa_macro_probe(struct platform_device *pdev) wsa_priv->reset_swr = true; INIT_WORK(&wsa_priv->lpass_cdc_wsa_macro_add_child_devices_work, lpass_cdc_wsa_macro_add_child_devices); + INIT_WORK(&wsa_priv->lpass_cdc_wsa_macro_cooling_work, + lpass_cdc_wsa_macro_cooling_adjust_gain); wsa_priv->swr_plat_data.handle = (void *) wsa_priv; wsa_priv->swr_plat_data.read = NULL; wsa_priv->swr_plat_data.write = NULL; diff --git a/asoc/codecs/lpass-cdc/lpass-cdc-wsa2-macro.c b/asoc/codecs/lpass-cdc/lpass-cdc-wsa2-macro.c index 1993cfc0ae..0b03c91a17 100644 --- a/asoc/codecs/lpass-cdc/lpass-cdc-wsa2-macro.c +++ b/asoc/codecs/lpass-cdc/lpass-cdc-wsa2-macro.c @@ -193,6 +193,18 @@ struct lpass_cdc_wsa2_macro_swr_ctrl_data { struct platform_device *wsa2_swr_pdev; }; +#define LPASS_CDC_WSA2_MACRO_SET_VOLUME_TLV(xname, xreg, xmin, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ + .put = lpass_cdc_wsa2_macro_set_digital_volume, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .rreg = xreg, \ + .min = xmin, .max = xmax, .platform_max = xmax, \ + .sign_bit = 7,} } + struct lpass_cdc_wsa2_macro_swr_ctrl_platform_data { void *handle; /* holds codec private data */ int (*read)(void *handle, int reg); @@ -273,9 +285,11 @@ struct lpass_cdc_wsa2_macro_priv { u16 default_clk_id; u32 pcm_rate_vi; int wsa2_digital_mute_status[LPASS_CDC_WSA2_MACRO_RX_MAX]; + u8 original_gain; struct thermal_cooling_device *tcdev; uint32_t thermal_cur_state; uint32_t thermal_max_state; + struct work_struct lpass_cdc_wsa2_macro_cooling_work; }; static struct snd_soc_dai_driver lpass_cdc_wsa2_macro_dai[]; @@ -1890,6 +1904,44 @@ static int lpass_cdc_wsa2_macro_set_rx_mute_status(struct snd_kcontrol *kcontrol return ret; } +static int lpass_cdc_wsa2_macro_set_digital_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct device *wsa2_dev = NULL; + struct lpass_cdc_wsa2_macro_priv *wsa2_priv = NULL; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + u8 gain = 0; + int ret = 0; + + if (!lpass_cdc_wsa2_macro_get_data(component, &wsa2_dev, &wsa2_priv, __func__)) + return -EINVAL; + + if (!wsa2_priv) { + pr_err("%s: priv is null for macro!\n", + __func__); + return -EINVAL; + } + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + wsa2_priv->original_gain = (u8)snd_soc_component_read(wsa2_priv->component, + mc->reg); + + if (wsa2_priv->thermal_cur_state > 0) { + gain = (u8)(wsa2_priv->original_gain - wsa2_priv->thermal_cur_state); + snd_soc_component_update_bits(wsa2_priv->component, + mc->reg, 0xFF, gain); + dev_dbg(wsa2_priv->dev, + "%s: Current thermal state: %d, adjusted gain: %x\n", + __func__, wsa2_priv->thermal_cur_state, gain); + } + + return ret; +} + static int lpass_cdc_wsa2_macro_get_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2157,12 +2209,12 @@ static const struct snd_kcontrol_new lpass_cdc_wsa2_macro_snd_controls[] = { LPASS_CDC_WSA2_MACRO_SOFTCLIP1, 1, 0, lpass_cdc_wsa2_macro_soft_clip_enable_get, lpass_cdc_wsa2_macro_soft_clip_enable_put), - SOC_SINGLE_S8_TLV("WSA2_RX0 Digital Volume", - LPASS_CDC_WSA2_RX0_RX_VOL_CTL, - -84, 40, digital_gain), - SOC_SINGLE_S8_TLV("WSA2_RX1 Digital Volume", - LPASS_CDC_WSA2_RX1_RX_VOL_CTL, - -84, 40, digital_gain), + LPASS_CDC_WSA2_MACRO_SET_VOLUME_TLV("WSA2_RX0 Digital Volume", + LPASS_CDC_WSA2_RX0_RX_VOL_CTL, + -84, 40, digital_gain), + LPASS_CDC_WSA2_MACRO_SET_VOLUME_TLV("WSA2_RX1 Digital Volume", + LPASS_CDC_WSA2_RX1_RX_VOL_CTL, + -84, 40, digital_gain), SOC_SINGLE_EXT("WSA2_RX0 Digital Mute", SND_SOC_NOPM, LPASS_CDC_WSA2_MACRO_RX0, 1, 0, lpass_cdc_wsa2_macro_get_rx_mute_status, lpass_cdc_wsa2_macro_set_rx_mute_status), @@ -2745,7 +2797,6 @@ static int lpass_cdc_wsa2_macro_set_cur_state( unsigned long state) { struct lpass_cdc_wsa2_macro_priv *wsa2_priv = cdev->devdata; - u8 gain = 0; if (!wsa2_priv) { pr_err("%s: cdev->devdata is NULL\n", __func__); @@ -2757,15 +2808,11 @@ static int lpass_cdc_wsa2_macro_set_cur_state( else wsa2_priv->thermal_cur_state = wsa2_priv->thermal_max_state; - gain = (u8)(gain - wsa2_priv->thermal_cur_state); dev_dbg(wsa2_priv->dev, - "%s: requested state:%d, actual state: %d, gain: %#x\n", - __func__, state, wsa2_priv->thermal_cur_state, gain); + "%s: requested state:%d, actual state: %d\n", + __func__, state, wsa2_priv->thermal_cur_state); - snd_soc_component_update_bits(wsa2_priv->component, - LPASS_CDC_WSA2_RX0_RX_VOL_CTL, 0xFF, gain); - snd_soc_component_update_bits(wsa2_priv->component, - LPASS_CDC_WSA2_RX1_RX_VOL_CTL, 0xFF, gain); + schedule_work(&wsa2_priv->lpass_cdc_wsa2_macro_cooling_work); return 0; } @@ -2957,6 +3004,55 @@ err: return; } +static void lpass_cdc_wsa2_macro_cooling_adjust_gain(struct work_struct *work) +{ + struct lpass_cdc_wsa2_macro_priv *wsa2_priv; + struct snd_soc_dapm_context *dapm; + u8 gain = 0; + u32 ctl_reg; + + wsa2_priv = container_of(work, struct lpass_cdc_wsa2_macro_priv, + lpass_cdc_wsa2_macro_cooling_work); + if (!wsa2_priv) { + pr_err("%s: priv is null for macro!\n", + __func__); + return; + } + if (!wsa2_priv->dev || !wsa2_priv->dev->of_node) { + dev_err(wsa2_priv->dev, + "%s: DT node for wsa2_priv does not exist\n", __func__); + return; + } + + dapm = snd_soc_component_get_dapm(wsa2_priv->component); + + /* Only adjust the volume when WSA2 clock is enabled */ + ctl_reg = snd_soc_component_read(wsa2_priv->component, + LPASS_CDC_WSA2_RX0_RX_PATH_CTL); + if (ctl_reg & 0x20) { + gain = (u8)(wsa2_priv->original_gain - wsa2_priv->thermal_cur_state); + snd_soc_component_update_bits(wsa2_priv->component, + LPASS_CDC_WSA2_RX0_RX_VOL_CTL, 0xFF, gain); + dev_dbg(wsa2_priv->dev, + "%s: RX0 current thermal state: %d, adjusted gain: %#x\n", + __func__, wsa2_priv->thermal_cur_state, gain); + } + + /* Only adjust the volume when WSA2 clock is enabled */ + ctl_reg = snd_soc_component_read(wsa2_priv->component, + LPASS_CDC_WSA2_RX1_RX_PATH_CTL); + if (ctl_reg & 0x20) { + gain = (u8)(wsa2_priv->original_gain - wsa2_priv->thermal_cur_state); + snd_soc_component_update_bits(wsa2_priv->component, + LPASS_CDC_WSA2_RX1_RX_VOL_CTL, 0xFF, gain); + dev_dbg(wsa2_priv->dev, + "%s: RX1 current thermal state: %d, adjusted gain: %#x\n", + __func__, wsa2_priv->thermal_cur_state, gain); + } + + return; +} + static void lpass_cdc_wsa2_macro_init_ops(struct macro_ops *ops, char __iomem *wsa2_io_base) { @@ -3038,6 +3134,8 @@ static int lpass_cdc_wsa2_macro_probe(struct platform_device *pdev) wsa2_priv->reset_swr = true; INIT_WORK(&wsa2_priv->lpass_cdc_wsa2_macro_add_child_devices_work, lpass_cdc_wsa2_macro_add_child_devices); + INIT_WORK(&wsa2_priv->lpass_cdc_wsa2_macro_cooling_work, + lpass_cdc_wsa2_macro_cooling_adjust_gain); wsa2_priv->swr_plat_data.handle = (void *) wsa2_priv; wsa2_priv->swr_plat_data.read = NULL; wsa2_priv->swr_plat_data.write = NULL;