Browse Source

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 <junkai@codeaurora.org>
Junkai Cai 3 years ago
parent
commit
751d49a88b
2 changed files with 224 additions and 28 deletions
  1. 112 14
      asoc/codecs/lpass-cdc/lpass-cdc-wsa-macro.c
  2. 112 14
      asoc/codecs/lpass-cdc/lpass-cdc-wsa2-macro.c

+ 112 - 14
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;

+ 112 - 14
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;