Merge "ASoC: msm8226: Enable Lineout1/2 for speaker on SKUF"
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index c799ffb..b618597 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -661,7 +661,9 @@
prim-gpio-tert : Primary AUXPCM shares GPIOs with Tertiary MI2S
Optional Properties:
-- qcom,us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
+- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
+- qcom,cdc-lineout-spkr-gpios : GPIO which controls external PAs to enable Lineout1/2 speaker
+- qcom,cdc-vdd-spkr-gpios : GPIO which controls PA for VDD speaker
Example:
@@ -675,4 +677,6 @@
qcom,prim-auxpcm-gpio-dout = <&msmgpio 66 0>;
qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
qcom,cdc-us-euro-gpios = <&msmgpio 69 0>;
+ qcom,cdc-lineout-spkr-gpios = <&pm8226_gpios 2 0>;
+ qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 3 0>;
};
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index 043a998..d3ba3d5 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -36,12 +36,20 @@
#define BTSCO_RATE_8KHZ 8000
#define BTSCO_RATE_16KHZ 16000
+/* It takes about 13ms for Class-D PAs to ramp-up */
+#define EXT_CLASS_D_EN_DELAY 13000
+#define EXT_CLASS_D_DIS_DELAY 3000
+#define EXT_CLASS_D_DELAY_DELTA 2000
+
#define WCD9XXX_MBHC_DEF_BUTTONS 8
#define WCD9XXX_MBHC_DEF_RLOADS 5
#define TAPAN_EXT_CLK_RATE 9600000
#define NUM_OF_AUXPCM_GPIOS 4
+#define LO_1_SPK_AMP 0x1
+#define LO_2_SPK_AMP 0x2
+
static int msm8226_auxpcm_rate = 8000;
static atomic_t auxpcm_rsc_ref;
static const char *const auxpcm_rate_text[] = {"rate_8000", "rate_16000"};
@@ -116,6 +124,7 @@
SLIM_4_TX_1 = 150, /* In-call musid delivery TX */
};
+static int msm8226_ext_spk_pamp;
static int msm_slim_0_rx_ch = 1;
static int msm_slim_0_tx_ch = 1;
@@ -125,6 +134,7 @@
static struct mutex cdc_mclk_mutex;
static struct clk *codec_clk;
static int clk_users;
+static int ext_spk_amp_gpio = -1;
static int vdd_spkr_gpio = -1;
static int msm_proxy_rx_ch = 2;
@@ -189,6 +199,99 @@
return 0;
}
+static void msm8226_ext_spk_power_amp_enable(u32 enable)
+{
+ if (enable) {
+ gpio_direction_output(ext_spk_amp_gpio, enable);
+ /* time takes enable the external power amplifier */
+ usleep_range(EXT_CLASS_D_EN_DELAY,
+ EXT_CLASS_D_EN_DELAY + EXT_CLASS_D_DELAY_DELTA);
+ } else {
+ gpio_direction_output(ext_spk_amp_gpio, enable);
+ /* time takes disable the external power amplifier */
+ usleep_range(EXT_CLASS_D_DIS_DELAY,
+ EXT_CLASS_D_DIS_DELAY + EXT_CLASS_D_DELAY_DELTA);
+ }
+
+ pr_debug("%s: %s external speaker PAs.\n", __func__,
+ enable ? "Enable" : "Disable");
+}
+
+static void msm8226_ext_spk_power_amp_on(u32 spk)
+{
+ if (gpio_is_valid(ext_spk_amp_gpio)) {
+ if (spk & (LO_1_SPK_AMP | LO_2_SPK_AMP)) {
+ pr_debug("%s:Enable left and right speakers case spk = 0x%x\n",
+ __func__, spk);
+
+ msm8226_ext_spk_pamp |= spk;
+
+ if ((msm8226_ext_spk_pamp & LO_1_SPK_AMP) &&
+ (msm8226_ext_spk_pamp & LO_2_SPK_AMP))
+ if (ext_spk_amp_gpio >= 0) {
+ pr_debug("%s enable power", __func__);
+ msm8226_ext_spk_power_amp_enable(1);
+ }
+ } else {
+ pr_err("%s: Invalid external speaker ampl. spk = 0x%x\n",
+ __func__, spk);
+ }
+ }
+}
+
+static void msm8226_ext_spk_power_amp_off(u32 spk)
+{
+ if (gpio_is_valid(ext_spk_amp_gpio)) {
+ if (spk & (LO_1_SPK_AMP | LO_2_SPK_AMP)) {
+ pr_debug("%s Disable left and right speakers case spk = 0x%08x",
+ __func__, spk);
+
+ msm8226_ext_spk_pamp &= ~spk;
+
+ if (!msm8226_ext_spk_pamp) {
+ if (ext_spk_amp_gpio >= 0) {
+ pr_debug("%s disable power", __func__);
+ msm8226_ext_spk_power_amp_enable(0);
+ }
+ msm8226_ext_spk_pamp = 0;
+ }
+ } else {
+ pr_err("%s: ERROR : Invalid Ext Spk Ampl. spk = 0x%08x\n",
+ __func__, spk);
+ }
+ }
+}
+
+static int msm8226_ext_spkramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ pr_debug("%s()\n", __func__);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (!strncmp(w->name, "Lineout_1 amp", 14))
+ msm8226_ext_spk_power_amp_on(LO_1_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_2 amp", 14))
+ msm8226_ext_spk_power_amp_on(LO_2_SPK_AMP);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ } else {
+ if (!strncmp(w->name, "Lineout_1 amp", 14))
+ msm8226_ext_spk_power_amp_off(LO_1_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_2 amp", 14))
+ msm8226_ext_spk_power_amp_off(LO_2_SPK_AMP);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static int msm8226_vdd_spkr_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -230,6 +333,9 @@
SND_SOC_DAPM_MIC("Digital Mic5", NULL),
SND_SOC_DAPM_MIC("Digital Mic6", NULL),
+ SND_SOC_DAPM_SPK("Lineout_1 amp", msm8226_ext_spkramp_event),
+ SND_SOC_DAPM_SPK("Lineout_2 amp", msm8226_ext_spkramp_event),
+
SND_SOC_DAPM_SUPPLY("EXT_VDD_SPKR", SND_SOC_NOPM, 0, 0,
msm8226_vdd_spkr_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
@@ -1437,7 +1543,7 @@
pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,cdc-us-euro-gpios", 0);
if (pdata->us_euro_gpio < 0) {
- dev_info(&pdev->dev,
+ dev_dbg(&pdev->dev,
"property %s in node %s not found %d\n",
"qcom,cdc-us-euro-gpios", pdev->dev.of_node->full_name,
pdata->us_euro_gpio);
@@ -1527,7 +1633,7 @@
vdd_spkr_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,cdc-vdd-spkr-gpios", 0);
if (vdd_spkr_gpio < 0) {
- dev_err(&pdev->dev,
+ dev_dbg(&pdev->dev,
"Looking up %s property in node %s failed %d\n",
"qcom, cdc-vdd-spkr-gpios",
pdev->dev.of_node->full_name, vdd_spkr_gpio);
@@ -1542,19 +1648,38 @@
}
}
+ ext_spk_amp_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,cdc-lineout-spkr-gpios", 0);
+ if (ext_spk_amp_gpio < 0) {
+ dev_err(&pdev->dev,
+ "Looking up %s property in node %s failed %d\n",
+ "qcom, cdc-lineout-spkr-gpios",
+ pdev->dev.of_node->full_name, ext_spk_amp_gpio);
+ } else {
+ ret = gpio_request(ext_spk_amp_gpio,
+ "TAPAN_CODEC_LINEOUT_SPKR");
+ if (ret) {
+ /* GPIO to enable EXT AMP exists, but failed request */
+ dev_err(card->dev,
+ "%s: Failed to request tapan amp spkr gpio %d\n",
+ __func__, ext_spk_amp_gpio);
+ goto err_vdd_spkr;
+ }
+ }
+
mbhc_cfg.gpio_level_insert = of_property_read_bool(pdev->dev.of_node,
"qcom,headset-jack-type-NO");
msm8226_setup_hs_jack(pdev, pdata);
ret = msm8226_prepare_codec_mclk(card);
if (ret)
- goto err_vdd_spkr;
+ goto err_lineout_spkr;
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
ret);
- goto err_vdd_spkr;
+ goto err_lineout_spkr;
}
mutex_init(&cdc_mclk_mutex);
@@ -1564,7 +1689,7 @@
dev_err(&pdev->dev, "Looking up %s property in node %s failed",
"qcom,prim-auxpcm-gpio-set",
pdev->dev.of_node->full_name);
- goto err_vdd_spkr;
+ goto err_lineout_spkr;
}
if (!strcmp(auxpcm_pri_gpio_set, "prim-gpio-prim")) {
lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_PRI_MODE_MUXSEL, 4);
@@ -1574,16 +1699,22 @@
dev_err(&pdev->dev, "Invalid value %s for AUXPCM GPIO set\n",
auxpcm_pri_gpio_set);
ret = -EINVAL;
- goto err_vdd_spkr;
+ goto err_lineout_spkr;
}
if (lpaif_pri_muxsel_virt_addr == NULL) {
pr_err("%s Pri muxsel virt addr is null\n", __func__);
ret = -EINVAL;
- goto err_vdd_spkr;
+ goto err_lineout_spkr;
}
return 0;
+err_lineout_spkr:
+ if (ext_spk_amp_gpio >= 0) {
+ gpio_free(ext_spk_amp_gpio);
+ ext_spk_amp_gpio = -1;
+ }
+
err_vdd_spkr:
if (vdd_spkr_gpio >= 0) {
gpio_free(vdd_spkr_gpio);
@@ -1607,11 +1738,15 @@
struct msm8226_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
gpio_free(pdata->mclk_gpio);
- gpio_free(vdd_spkr_gpio);
+ if (vdd_spkr_gpio >= 0)
+ gpio_free(vdd_spkr_gpio);
+ if (ext_spk_amp_gpio >= 0)
+ gpio_free(ext_spk_amp_gpio);
if (pdata->us_euro_gpio > 0)
gpio_free(pdata->us_euro_gpio);
vdd_spkr_gpio = -1;
+ ext_spk_amp_gpio = -1;
snd_soc_unregister_card(card);
return 0;