Merge "ASoC: rouleur: Change hph and ear gain according to soc capacity"
diff --git a/asoc/codecs/bolero/bolero-cdc.c b/asoc/codecs/bolero/bolero-cdc.c
index 233a0b4..173343c 100644
--- a/asoc/codecs/bolero/bolero-cdc.c
+++ b/asoc/codecs/bolero/bolero-cdc.c
@@ -232,6 +232,12 @@
 				priv->component,
 				BOLERO_MACRO_EVT_BCS_CLK_OFF, data);
 		break;
+	case WCD_BOLERO_EVT_RX_PA_GAIN_UPDATE:
+		if (priv->macro_params[RX_MACRO].event_handler)
+			priv->macro_params[RX_MACRO].event_handler(
+				priv->component,
+				BOLERO_MACRO_EVT_RX_PA_GAIN_UPDATE, data);
+		break;
 	default:
 		dev_err(priv->dev, "%s: Invalid event %d trigger from wcd\n",
 			__func__, event);
diff --git a/asoc/codecs/bolero/bolero-cdc.h b/asoc/codecs/bolero/bolero-cdc.h
index 7e3468a..bcd4a25 100644
--- a/asoc/codecs/bolero/bolero-cdc.h
+++ b/asoc/codecs/bolero/bolero-cdc.h
@@ -55,6 +55,7 @@
 	BOLERO_MACRO_EVT_BCS_CLK_OFF,
 	BOLERO_MACRO_EVT_SSR_GFMUX_UP,
 	BOLERO_MACRO_EVT_PRE_SSR_UP,
+	BOLERO_MACRO_EVT_RX_PA_GAIN_UPDATE,
 };
 
 enum {
diff --git a/asoc/codecs/bolero/internal.h b/asoc/codecs/bolero/internal.h
index d3a072c..4d098da 100644
--- a/asoc/codecs/bolero/internal.h
+++ b/asoc/codecs/bolero/internal.h
@@ -33,6 +33,7 @@
 	WCD_BOLERO_EVT_IMPED_FALSE,  /* for imped false */
 	WCD_BOLERO_EVT_RX_COMPANDER_SOFT_RST,
 	WCD_BOLERO_EVT_BCS_CLK_OFF,
+	WCD_BOLERO_EVT_RX_PA_GAIN_UPDATE,
 };
 
 struct wcd_ctrl_platform_data {
diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c
index c858d2c..f79e984 100644
--- a/asoc/codecs/bolero/rx-macro.c
+++ b/asoc/codecs/bolero/rx-macro.c
@@ -77,6 +77,11 @@
 #define RX_MACRO_EC_MIX_TX1_MASK 0x0f
 #define RX_MACRO_EC_MIX_TX2_MASK 0x0f
 
+#define RX_MACRO_GAIN_MAX_VAL 0x28
+#define RX_MACRO_GAIN_VAL_UNITY 0x0
+/* Define macros to increase PA Gain by half */
+#define RX_MACRO_MOD_GAIN (RX_MACRO_GAIN_VAL_UNITY + 6)
+
 #define COMP_MAX_COEFF 25
 
 struct wcd_imped_val {
@@ -452,6 +457,8 @@
 	struct rx_macro_bcl_pmic_params bcl_pmic_params;
 	u16 clk_id;
 	u16 default_clk_id;
+	int8_t rx0_gain_val;
+	int8_t rx1_gain_val;
 };
 
 static struct snd_soc_dai_driver rx_macro_dai[];
@@ -1416,6 +1423,46 @@
 	case BOLERO_MACRO_EVT_CLK_RESET:
 		bolero_rsc_clk_reset(rx_dev, RX_CORE_CLK);
 		break;
+	case BOLERO_MACRO_EVT_RX_PA_GAIN_UPDATE:
+		rx_priv->rx0_gain_val = snd_soc_component_read32(component,
+					BOLERO_CDC_RX_RX0_RX_VOL_CTL);
+		rx_priv->rx1_gain_val = snd_soc_component_read32(component,
+					BOLERO_CDC_RX_RX1_RX_VOL_CTL);
+		if (data) {
+			/* Reduce gain by half only if its greater than -6DB */
+			if ((rx_priv->rx0_gain_val >= RX_MACRO_GAIN_VAL_UNITY)
+			&& (rx_priv->rx0_gain_val <= RX_MACRO_GAIN_MAX_VAL))
+				snd_soc_component_update_bits(component,
+					BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xFF,
+					(rx_priv->rx0_gain_val -
+					 RX_MACRO_MOD_GAIN));
+			if ((rx_priv->rx1_gain_val >= RX_MACRO_GAIN_VAL_UNITY)
+			&& (rx_priv->rx1_gain_val <= RX_MACRO_GAIN_MAX_VAL))
+				snd_soc_component_update_bits(component,
+					BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xFF,
+					(rx_priv->rx1_gain_val -
+					 RX_MACRO_MOD_GAIN));
+		}
+		else {
+			/* Reset gain value to default */
+			if ((rx_priv->rx0_gain_val >=
+			    (RX_MACRO_GAIN_VAL_UNITY - RX_MACRO_MOD_GAIN)) &&
+			    (rx_priv->rx0_gain_val <= (RX_MACRO_GAIN_MAX_VAL -
+			    RX_MACRO_MOD_GAIN)))
+				snd_soc_component_update_bits(component,
+					BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xFF,
+					(rx_priv->rx0_gain_val +
+					 RX_MACRO_MOD_GAIN));
+			if ((rx_priv->rx1_gain_val >=
+			    (RX_MACRO_GAIN_VAL_UNITY - RX_MACRO_MOD_GAIN)) &&
+			    (rx_priv->rx1_gain_val <= (RX_MACRO_GAIN_MAX_VAL -
+			    RX_MACRO_MOD_GAIN)))
+				snd_soc_component_update_bits(component,
+					BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xFF,
+					(rx_priv->rx1_gain_val +
+					 RX_MACRO_MOD_GAIN));
+		}
+		break;
 	}
 done:
 	return ret;
@@ -3825,6 +3872,8 @@
 		return ret;
 	}
 	rx_priv->dev_up = true;
+	rx_priv->rx0_gain_val = 0;
+	rx_priv->rx1_gain_val = 0;
 	snd_soc_dapm_ignore_suspend(dapm, "RX_MACRO_AIF1 Playback");
 	snd_soc_dapm_ignore_suspend(dapm, "RX_MACRO_AIF2 Playback");
 	snd_soc_dapm_ignore_suspend(dapm, "RX_MACRO_AIF3 Playback");
diff --git a/asoc/codecs/msm-cdc-supply.c b/asoc/codecs/msm-cdc-supply.c
index 0d49c28..98639f3 100644
--- a/asoc/codecs/msm-cdc-supply.c
+++ b/asoc/codecs/msm-cdc-supply.c
@@ -173,6 +173,52 @@
 EXPORT_SYMBOL(msm_cdc_is_ondemand_supply);
 
 /*
+ * msm_cdc_set_supply_min_voltage:
+ *	Set min supply voltage for particular supply
+ *
+ * @dev: pointer to codec device
+ * @supplies: pointer to regulator bulk data
+ * @cdc_vreg: pointer to platform regulator data
+ * @num_supplies: number of supplies
+ * @supply_name: Supply name to change voltage for
+ * @vval_min: Min voltage to be set in uV
+ * @override_min_vol: True if override min voltage from default
+ * Return error code if unable to set voltage
+ */
+int msm_cdc_set_supply_min_voltage(struct device *dev,
+				    struct regulator_bulk_data *supplies,
+				    struct cdc_regulator *cdc_vreg,
+				    int num_supplies, char *supply_name,
+				    int vval_min, bool override_min_vol)
+{
+	int rc = 0, i;
+
+	if ((!supply_name) || (!supplies)) {
+		pr_err("%s: either dev or supplies or cdc_vreg is NULL\n",
+				__func__);
+		return -EINVAL;
+	}
+	/* input parameter validation */
+	rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
+	if (rc)
+		return rc;
+	for (i = 0; i < num_supplies; i++) {
+		if (!strcmp(cdc_vreg[i].name, supply_name)) {
+			if (override_min_vol)
+				regulator_set_voltage(supplies[i].consumer,
+					vval_min, cdc_vreg[i].max_uV);
+			else
+				regulator_set_voltage(supplies[i].consumer,
+				    cdc_vreg[i].min_uV, cdc_vreg[i].max_uV);
+			break;
+		}
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_cdc_set_supply_min_voltage);
+
+/*
  * msm_cdc_disable_ondemand_supply:
  *	Disable codec ondemand supply
  *
diff --git a/asoc/codecs/rouleur/internal.h b/asoc/codecs/rouleur/internal.h
index cc1500b..b1d21fa 100644
--- a/asoc/codecs/rouleur/internal.h
+++ b/asoc/codecs/rouleur/internal.h
@@ -85,6 +85,9 @@
 	struct mutex main_bias_lock;
 	bool dev_up;
 	bool usbc_hs_status;
+	struct notifier_block psy_nb;
+	struct work_struct soc_eval_work;
+	bool low_soc;
 };
 
 struct rouleur_micbias_setting {
@@ -131,6 +134,7 @@
 	WCD_BOLERO_EVT_IMPED_FALSE,	/* for imped false */
 	WCD_BOLERO_EVT_RX_COMPANDER_SOFT_RST,
 	WCD_BOLERO_EVT_BCS_CLK_OFF,
+	WCD_BOLERO_EVT_RX_PA_GAIN_UPDATE, /* To reduce PA gain for low SoC */
 };
 
 enum {
diff --git a/asoc/codecs/rouleur/rouleur.c b/asoc/codecs/rouleur/rouleur.c
index 8ce51a3..9066bd8 100644
--- a/asoc/codecs/rouleur/rouleur.c
+++ b/asoc/codecs/rouleur/rouleur.c
@@ -26,6 +26,7 @@
 #include <asoc/msm-cdc-pinctrl.h>
 #include <dt-bindings/sound/audio-codec-port-types.h>
 #include <asoc/msm-cdc-supply.h>
+#include <linux/power_supply.h>
 
 #define DRV_NAME "rouleur_codec"
 
@@ -35,6 +36,8 @@
 #define ROULEUR_VERSION_ENTRY_SIZE 32
 
 #define NUM_ATTEMPTS 5
+#define SOC_THRESHOLD_LEVEL 25
+#define LOW_SOC_MBIAS_REG_MIN_VOLTAGE 2850000
 
 enum {
 	CODEC_TX = 0,
@@ -2000,6 +2003,107 @@
 	return rc;
 }
 
+static int rouleur_battery_supply_cb(struct notifier_block *nb,
+			unsigned long event, void *data)
+{
+	struct power_supply *psy = data;
+	struct rouleur_priv *rouleur =
+		container_of(nb, struct rouleur_priv, psy_nb);
+
+	if (strcmp(psy->desc->name, "battery"))
+		return NOTIFY_OK;
+	queue_work(system_freezable_wq, &rouleur->soc_eval_work);
+
+	return NOTIFY_OK;
+}
+
+static int rouleur_read_battery_soc(struct rouleur_priv *rouleur, int *soc_val)
+{
+	static struct power_supply *batt_psy;
+	union power_supply_propval ret = {0,};
+	int err = 0;
+
+	*soc_val = 100;
+	if (!batt_psy)
+		batt_psy = power_supply_get_by_name("battery");
+	if (batt_psy) {
+		err = power_supply_get_property(batt_psy,
+				POWER_SUPPLY_PROP_CAPACITY, &ret);
+		if (err) {
+			pr_err("%s: battery SoC read error:%d\n",
+				__func__, err);
+			return err;
+		}
+		*soc_val = ret.intval;
+	}
+	pr_debug("%s: soc:%d\n", __func__, *soc_val);
+
+	return err;
+}
+
+static void rouleur_evaluate_soc(struct work_struct *work)
+{
+	struct rouleur_priv *rouleur =
+		container_of(work, struct rouleur_priv, soc_eval_work);
+	int soc_val = 0, ret = 0;
+	struct rouleur_pdata *pdata = NULL;
+
+	pdata = dev_get_platdata(rouleur->dev);
+	if (!pdata) {
+		dev_err(rouleur->dev, "%s: pdata is NULL\n", __func__);
+		return;
+	}
+
+	if (rouleur_read_battery_soc(rouleur, &soc_val) < 0) {
+		dev_err(rouleur->dev, "%s unable to read battery SoC\n",
+			__func__);
+		return;
+	}
+
+	if (soc_val < SOC_THRESHOLD_LEVEL) {
+		dev_dbg(rouleur->dev,
+			"%s battery SoC less than threshold soc_val = %d\n",
+			__func__, soc_val);
+		/* Reduce PA Gain by 6DB for low SoC */
+		if (rouleur->update_wcd_event)
+			rouleur->update_wcd_event(rouleur->handle,
+					WCD_BOLERO_EVT_RX_PA_GAIN_UPDATE,
+					true);
+		rouleur->low_soc = true;
+		ret = msm_cdc_set_supply_min_voltage(rouleur->dev,
+						 rouleur->supplies,
+						 pdata->regulator,
+						 pdata->num_supplies,
+						 "cdc-vdd-mic-bias",
+						 LOW_SOC_MBIAS_REG_MIN_VOLTAGE,
+						 true);
+		if (ret < 0)
+			dev_err(rouleur->dev,
+				"%s unable to set mbias min voltage\n",
+				__func__);
+	} else {
+		if (rouleur->low_soc == true) {
+			/* Reset PA Gain to default for normal SoC */
+			if (rouleur->update_wcd_event)
+				rouleur->update_wcd_event(rouleur->handle,
+					WCD_BOLERO_EVT_RX_PA_GAIN_UPDATE,
+					false);
+			ret = msm_cdc_set_supply_min_voltage(rouleur->dev,
+						rouleur->supplies,
+						pdata->regulator,
+						pdata->num_supplies,
+						"cdc-vdd-mic-bias",
+						LOW_SOC_MBIAS_REG_MIN_VOLTAGE,
+						false);
+			if (ret < 0)
+				dev_err(rouleur->dev,
+					"%s unable to set mbias min voltage\n",
+					__func__);
+			rouleur->low_soc = false;
+		}
+	}
+}
+
 static int rouleur_soc_codec_probe(struct snd_soc_component *component)
 {
 	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
@@ -2070,7 +2174,16 @@
 			return ret;
 		}
 	}
+	rouleur->low_soc = false;
 	rouleur->dev_up = true;
+	/* Register notifier to change gain based on state of charge */
+	INIT_WORK(&rouleur->soc_eval_work, rouleur_evaluate_soc);
+	rouleur->psy_nb.notifier_call = rouleur_battery_supply_cb;
+	if (power_supply_reg_notifier(&rouleur->psy_nb) < 0)
+		dev_dbg(rouleur->dev,
+			"%s: could not register pwr supply notifier\n",
+			__func__);
+	queue_work(system_freezable_wq, &rouleur->soc_eval_work);
 done:
 	return ret;
 }
diff --git a/include/asoc/msm-cdc-supply.h b/include/asoc/msm-cdc-supply.h
index 336ec22..c5e4106 100644
--- a/include/asoc/msm-cdc-supply.h
+++ b/include/asoc/msm-cdc-supply.h
@@ -60,4 +60,10 @@
 				 struct cdc_regulator *cdc_vreg,
 				 int num_supplies,
 				 u32 vote_regulator_on_demand);
+extern int msm_cdc_set_supply_min_voltage(struct device *dev,
+				struct regulator_bulk_data *supplies,
+				struct cdc_regulator *cdc_vreg,
+				int num_supplies, char *supply_name,
+				int vval_min, bool override_min_vol);
+
 #endif