Merge "ASoC: wcd937x: Fix pop after PDR"
diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild
index 3e5fc12..48e0431 100644
--- a/asoc/codecs/Kbuild
+++ b/asoc/codecs/Kbuild
@@ -98,6 +98,7 @@
endif
ifdef CONFIG_SND_SOC_WCD9XXX_V2
+ WCD9XXX_OBJS += wcd-clsh.o
WCD9XXX_OBJS += wcd9xxx-common-v2.o
WCD9XXX_OBJS += wcd9xxx-resmgr-v2.o
WCD9XXX_OBJS += wcdcal-hwdep.o
diff --git a/asoc/codecs/audio-ext-clk-up.c b/asoc/codecs/audio-ext-clk-up.c
index b6cb2f6..ba72fd8 100644
--- a/asoc/codecs/audio-ext-clk-up.c
+++ b/asoc/codecs/audio-ext-clk-up.c
@@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <dt-bindings/clock/qcom,audio-ext-clk.h>
#include <dsp/q6afe-v2.h>
+#include <dsp/q6core.h>
#include "audio-ext-clk-up.h"
enum {
@@ -33,6 +34,7 @@
AUDIO_EXT_CLK_LPASS5,
AUDIO_EXT_CLK_LPASS6,
AUDIO_EXT_CLK_LPASS7,
+ AUDIO_EXT_CLK_LPASS_NPA_RSC_ISLAND,
AUDIO_EXT_CLK_LPASS_MAX,
AUDIO_EXT_CLK_MAX = AUDIO_EXT_CLK_LPASS_MAX,
};
@@ -55,6 +57,7 @@
struct afe_clk_set clk_cfg;
struct audio_ext_clk audio_clk;
const char *clk_name;
+ uint32_t npa_rsc_client_handle;
};
static inline struct audio_ext_clk_priv *to_audio_clk(struct clk_hw *hw)
@@ -141,12 +144,58 @@
return 0;
}
+static int lpass_npa_rsc_prepare(struct clk_hw *hw)
+{
+ struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
+ int ret;
+
+ if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS_NPA_RSC_ISLAND) &&
+ (clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) {
+ if (q6core_get_avcs_api_version_per_service(
+ APRV2_IDS_SERVICE_ID_ADSP_CORE_V) >= AVCS_API_VERSION_V4) {
+ ret = q6core_request_island_transition(
+ clk_priv->npa_rsc_client_handle, false);
+ if (ret < 0) {
+ pr_err("%s q6core_request_island_transition failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void lpass_npa_rsc_unprepare(struct clk_hw *hw)
+{
+ struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
+ int ret = 0;
+
+ if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS_NPA_RSC_ISLAND) &&
+ (clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) {
+ if (q6core_get_avcs_api_version_per_service(
+ APRV2_IDS_SERVICE_ID_ADSP_CORE_V) >= AVCS_API_VERSION_V4) {
+ ret = q6core_request_island_transition(
+ clk_priv->npa_rsc_client_handle, true);
+ if (ret < 0) {
+ pr_err("%s q6core_request_island_transition failed %d\n",
+ __func__, ret);
+ }
+ }
+ }
+}
+
static const struct clk_ops audio_ext_clk_ops = {
.prepare = audio_ext_clk_prepare,
.unprepare = audio_ext_clk_unprepare,
.get_parent = audio_ext_clk_get_parent,
};
+static const struct clk_ops lpass_npa_rsc_ops = {
+ .prepare = lpass_npa_rsc_prepare,
+ .unprepare = lpass_npa_rsc_unprepare,
+};
+
static const char * const audio_ext_pmi_div_clk[] = {
"qpnp_clkdiv_1",
"pms405_div_clk1",
@@ -274,6 +323,15 @@
},
},
},
+ {
+ .pnctrl_info = {NULL},
+ .fact = {
+ .hw.init = &(struct clk_init_data){
+ .name = "lpass_npa_rsc_island_clk",
+ .ops = &lpass_npa_rsc_ops,
+ },
+ },
+ },
};
static int audio_get_pinctrl(struct platform_device *pdev)
@@ -476,11 +534,34 @@
return ret;
}
+ if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS_NPA_RSC_ISLAND) {
+ if (q6core_get_avcs_api_version_per_service(
+ APRV2_IDS_SERVICE_ID_ADSP_CORE_V) >= AVCS_API_VERSION_V4) {
+ ret = q6core_create_lpass_npa_client(
+ AVCS_SLEEP_NODE_ISLAND_TRANSITION_RESOURCE_ID,
+ "lpass_npa_rsc_mgr",
+ &clk_priv->npa_rsc_client_handle);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: q6core_create_lpass_npa_client is failed %d\n",
+ __func__, ret);
+ audio_put_pinctrl(pdev);
+ return ret;
+ }
+ }
+ }
return 0;
}
static int audio_ref_clk_remove(struct platform_device *pdev)
{
+ struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev);
+
+ if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS_NPA_RSC_ISLAND) {
+ if (q6core_get_avcs_api_version_per_service(
+ APRV2_IDS_SERVICE_ID_ADSP_CORE_V) >= AVCS_API_VERSION_V4)
+ q6core_destroy_lpass_npa_client(
+ clk_priv->npa_rsc_client_handle);
+ }
audio_put_pinctrl(pdev);
return 0;
diff --git a/asoc/codecs/bolero/bolero-cdc.c b/asoc/codecs/bolero/bolero-cdc.c
index dcf688e..bd3c02f 100644
--- a/asoc/codecs/bolero/bolero-cdc.c
+++ b/asoc/codecs/bolero/bolero-cdc.c
@@ -18,7 +18,9 @@
#include <linux/printk.h>
#include <linux/delay.h>
#include <linux/kernel.h>
+#include <linux/clk.h>
#include <soc/snd_event.h>
+#include <linux/pm_runtime.h>
#include "bolero-cdc.h"
#include "internal.h"
@@ -30,6 +32,9 @@
static struct snd_soc_codec_driver bolero;
+/* pm runtime auto suspend timer in msecs */
+#define BOLERO_AUTO_SUSPEND_DELAY 1500 /* delay in msec */
+
/* MCLK_MUX table for all macros */
static u16 bolero_mclk_mux_tbl[MAX_MACRO][MCLK_MUX_MAX] = {
{TX_MACRO, VA_MACRO},
@@ -67,6 +72,8 @@
"%s: SSR in progress, exit\n", __func__);
goto err;
}
+
+ pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev);
current_mclk_mux_macro =
priv->current_mclk_mux_macro[macro_id];
if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) {
@@ -88,6 +95,8 @@
priv->macro_params[current_mclk_mux_macro].mclk_fn(
priv->macro_params[current_mclk_mux_macro].dev, false);
err:
+ pm_runtime_mark_last_busy(priv->macro_params[VA_MACRO].dev);
+ pm_runtime_put_autosuspend(priv->macro_params[VA_MACRO].dev);
mutex_unlock(&priv->clk_lock);
return ret;
}
@@ -104,6 +113,7 @@
"%s: SSR in progress, exit\n", __func__);
goto err;
}
+ ret = pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev);
current_mclk_mux_macro =
priv->current_mclk_mux_macro[macro_id];
if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) {
@@ -125,6 +135,8 @@
priv->macro_params[current_mclk_mux_macro].mclk_fn(
priv->macro_params[current_mclk_mux_macro].dev, false);
err:
+ pm_runtime_mark_last_busy(priv->macro_params[VA_MACRO].dev);
+ pm_runtime_put_autosuspend(priv->macro_params[VA_MACRO].dev);
mutex_unlock(&priv->clk_lock);
return ret;
}
@@ -344,6 +356,9 @@
priv->macro_params[macro_id].dev = dev;
priv->current_mclk_mux_macro[macro_id] =
bolero_mclk_mux_tbl[macro_id][MCLK_MUX0];
+ if (macro_id == TX_MACRO)
+ priv->macro_params[macro_id].reg_wake_irq = ops->reg_wake_irq;
+
priv->num_dais += ops->num_dais;
priv->num_macros_registered++;
priv->macros_supported[macro_id] = true;
@@ -403,6 +418,9 @@
priv->macro_params[macro_id].mclk_fn = NULL;
priv->macro_params[macro_id].event_handler = NULL;
priv->macro_params[macro_id].dev = NULL;
+ if (macro_id == TX_MACRO)
+ priv->macro_params[macro_id].reg_wake_irq = NULL;
+
priv->num_dais -= priv->macro_params[macro_id].num_dais;
priv->num_macros_registered--;
@@ -664,6 +682,37 @@
}
EXPORT_SYMBOL(bolero_info_create_codec_entry);
+/**
+ * bolero_register_wake_irq - Register wake irq of Tx macro
+ *
+ * @codec: codec ptr.
+ * @ipc_wakeup: bool to identify ipc_wakeup to be used or HW interrupt line.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int bolero_register_wake_irq(struct snd_soc_codec *codec, u32 ipc_wakeup)
+{
+ struct bolero_priv *priv = NULL;
+
+ if (!codec)
+ return -EINVAL;
+
+ priv = snd_soc_codec_get_drvdata(codec);
+ if (!priv)
+ return -EINVAL;
+
+ if (!bolero_is_valid_codec_dev(priv->dev)) {
+ dev_err(codec->dev, "%s: invalid codec\n", __func__);
+ return -EINVAL;
+ }
+
+ if (priv->macro_params[TX_MACRO].reg_wake_irq)
+ priv->macro_params[TX_MACRO].reg_wake_irq(codec, ipc_wakeup);
+
+ return 0;
+}
+EXPORT_SYMBOL(bolero_register_wake_irq);
+
static int bolero_soc_codec_probe(struct snd_soc_codec *codec)
{
struct bolero_priv *priv = dev_get_drvdata(codec->dev);
@@ -814,6 +863,7 @@
struct bolero_priv *priv;
u32 num_macros = 0;
int ret;
+ struct clk *lpass_npa_rsc_island = NULL;
priv = devm_kzalloc(&pdev->dev, sizeof(struct bolero_priv),
GFP_KERNEL);
@@ -862,6 +912,17 @@
bolero_add_child_devices);
schedule_work(&priv->bolero_add_child_devices_work);
+ /* Register LPASS NPA resource */
+ lpass_npa_rsc_island = devm_clk_get(&pdev->dev, "island_lpass_npa_rsc");
+ if (IS_ERR(lpass_npa_rsc_island)) {
+ ret = PTR_ERR(lpass_npa_rsc_island);
+ dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
+ __func__, "island_lpass_npa_rsc", ret);
+ lpass_npa_rsc_island = NULL;
+ ret = 0;
+ }
+ priv->lpass_npa_rsc_island = lpass_npa_rsc_island;
+
return 0;
}
@@ -878,6 +939,41 @@
return 0;
}
+int bolero_runtime_resume(struct device *dev)
+{
+ struct bolero_priv *priv = dev_get_drvdata(dev->parent);
+ int ret = 0;
+
+ if (priv->lpass_npa_rsc_island == NULL) {
+ dev_dbg(dev, "%s: Invalid lpass npa rsc node\n", __func__);
+ return 0;
+ }
+
+ ret = clk_prepare_enable(priv->lpass_npa_rsc_island);
+ if (ret < 0)
+ dev_err(dev, "%s:lpass npa rsc island enable failed\n",
+ __func__);
+
+ pm_runtime_set_autosuspend_delay(priv->dev, BOLERO_AUTO_SUSPEND_DELAY);
+ return 0;
+}
+EXPORT_SYMBOL(bolero_runtime_resume);
+
+int bolero_runtime_suspend(struct device *dev)
+{
+ struct bolero_priv *priv = dev_get_drvdata(dev->parent);
+
+ mutex_lock(&priv->clk_lock);
+ if (priv->lpass_npa_rsc_island != NULL)
+ clk_disable_unprepare(priv->lpass_npa_rsc_island);
+ else
+ dev_dbg(dev, "%s: Invalid lpass npa rsc node\n",
+ __func__);
+ mutex_unlock(&priv->clk_lock);
+ return 0;
+}
+EXPORT_SYMBOL(bolero_runtime_suspend);
+
static const struct of_device_id bolero_dt_match[] = {
{.compatible = "qcom,bolero-codec"},
{}
diff --git a/asoc/codecs/bolero/bolero-cdc.h b/asoc/codecs/bolero/bolero-cdc.h
index aa52491..a00c065 100644
--- a/asoc/codecs/bolero/bolero-cdc.h
+++ b/asoc/codecs/bolero/bolero-cdc.h
@@ -45,7 +45,8 @@
BOLERO_MACRO_EVT_IMPED_FALSE, /* for imped false */
BOLERO_MACRO_EVT_SSR_DOWN,
BOLERO_MACRO_EVT_SSR_UP,
- BOLERO_MACRO_EVT_WAIT_VA_CLK_RESET
+ BOLERO_MACRO_EVT_WAIT_VA_CLK_RESET,
+ BOLERO_MACRO_EVT_REG_WAKE_IRQ
};
struct macro_ops {
@@ -57,6 +58,7 @@
int (*mclk_fn)(struct device *dev, bool enable);
int (*event_handler)(struct snd_soc_codec *codec, u16 event,
u32 data);
+ int (*reg_wake_irq)(struct snd_soc_codec *codec, u32 data);
char __iomem *io_base;
};
@@ -71,7 +73,10 @@
int bolero_info_create_codec_entry(
struct snd_info_entry *codec_root,
struct snd_soc_codec *codec);
+int bolero_register_wake_irq(struct snd_soc_codec *codec, u32 data);
void bolero_clear_amic_tx_hold(struct device *dev, u16 adc_n);
+int bolero_runtime_resume(struct device *dev);
+int bolero_runtime_suspend(struct device *dev);
#else
static inline int bolero_register_macro(struct device *dev,
u16 macro_id,
@@ -107,5 +112,16 @@
static inline void bolero_clear_amic_tx_hold(struct device *dev, u16 adc_n)
{
}
+
+static inline int bolero_register_wake_irq(struct snd_soc_codec *codec,
+ u32 data)
+static inline int bolero_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+static int bolero_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
#endif /* CONFIG_SND_SOC_BOLERO */
#endif /* BOLERO_CDC_H */
diff --git a/asoc/codecs/bolero/internal.h b/asoc/codecs/bolero/internal.h
index 89d3cad..71750fb 100644
--- a/asoc/codecs/bolero/internal.h
+++ b/asoc/codecs/bolero/internal.h
@@ -65,6 +65,7 @@
u16 current_mclk_mux_macro[MAX_MACRO];
struct work_struct bolero_add_child_devices_work;
u32 version;
+ struct clk *lpass_npa_rsc_island;
/* Entry for version info */
struct snd_info_entry *entry;
diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c
index f5ad8d2..fcfd951 100644
--- a/asoc/codecs/bolero/rx-macro.c
+++ b/asoc/codecs/bolero/rx-macro.c
@@ -345,6 +345,7 @@
bool is_ear_mode_on;
bool dev_up;
bool hph_pwr_mode;
+ bool hph_hd2_mode;
u16 mclk_mux;
struct mutex mclk_lock;
struct mutex swr_clk_lock;
@@ -433,6 +434,10 @@
static const struct soc_enum rx_macro_ear_mode_enum =
SOC_ENUM_SINGLE_EXT(2, rx_macro_ear_mode_text);
+static const char *const rx_macro_hph_hd2_mode_text[] = {"OFF", "ON"};
+static const struct soc_enum rx_macro_hph_hd2_mode_enum =
+ SOC_ENUM_SINGLE_EXT(2, rx_macro_hph_hd2_mode_text);
+
static const char *const rx_macro_hph_pwr_mode_text[] = {"ULP", "LoHIFI"};
static const struct soc_enum rx_macro_hph_pwr_mode_enum =
SOC_ENUM_SINGLE_EXT(2, rx_macro_hph_pwr_mode_text);
@@ -1541,12 +1546,12 @@
switch (interp_idx) {
case INTERP_HPHL:
- hd2_scale_reg = BOLERO_CDC_RX_RX1_RX_PATH_SEC3;
- hd2_enable_reg = BOLERO_CDC_RX_RX1_RX_PATH_CFG0;
+ hd2_scale_reg = BOLERO_CDC_RX_RX0_RX_PATH_SEC3;
+ hd2_enable_reg = BOLERO_CDC_RX_RX0_RX_PATH_CFG0;
break;
case INTERP_HPHR:
- hd2_scale_reg = BOLERO_CDC_RX_RX2_RX_PATH_SEC3;
- hd2_enable_reg = BOLERO_CDC_RX_RX2_RX_PATH_CFG0;
+ hd2_scale_reg = BOLERO_CDC_RX_RX1_RX_PATH_SEC3;
+ hd2_enable_reg = BOLERO_CDC_RX_RX1_RX_PATH_CFG0;
break;
}
@@ -1695,6 +1700,34 @@
return 0;
}
+static int rx_macro_get_hph_hd2_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct device *rx_dev = NULL;
+ struct rx_macro_priv *rx_priv = NULL;
+
+ if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__))
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = rx_priv->hph_hd2_mode;
+ return 0;
+}
+
+static int rx_macro_put_hph_hd2_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct device *rx_dev = NULL;
+ struct rx_macro_priv *rx_priv = NULL;
+
+ if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__))
+ return -EINVAL;
+
+ rx_priv->hph_hd2_mode = ucontrol->value.integer.value[0];
+ return 0;
+}
+
static int rx_macro_get_hph_pwr_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1942,18 +1975,15 @@
struct rx_macro_priv *rx_priv,
u16 interp_idx, int event)
{
- u8 hph_dly_mask = 0;
u16 hph_lut_bypass_reg = 0;
u16 hph_comp_ctrl7 = 0;
switch (interp_idx) {
case INTERP_HPHL:
- hph_dly_mask = 1;
hph_lut_bypass_reg = BOLERO_CDC_RX_TOP_HPHL_COMP_LUT;
hph_comp_ctrl7 = BOLERO_CDC_RX_COMPANDER0_CTL7;
break;
case INTERP_HPHR:
- hph_dly_mask = 2;
hph_lut_bypass_reg = BOLERO_CDC_RX_TOP_HPHR_COMP_LUT;
hph_comp_ctrl7 = BOLERO_CDC_RX_COMPANDER1_CTL7;
break;
@@ -1962,8 +1992,6 @@
}
if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) {
- snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_TEST0,
- hph_dly_mask, 0x0);
if (interp_idx == INTERP_HPHL) {
if (rx_priv->is_ear_mode_on)
snd_soc_update_bits(codec,
@@ -1983,8 +2011,6 @@
}
if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
- snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_TEST0,
- hph_dly_mask, hph_dly_mask);
snd_soc_update_bits(codec, BOLERO_CDC_RX_RX0_RX_PATH_CFG1,
0x02, 0x00);
snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00);
@@ -2018,7 +2044,8 @@
snd_soc_update_bits(codec, main_reg, 0x20, 0x20);
rx_macro_idle_detect_control(codec, rx_priv,
interp_idx, event);
- rx_macro_hd2_control(codec, interp_idx, event);
+ if (rx_priv->hph_hd2_mode)
+ rx_macro_hd2_control(codec, interp_idx, event);
rx_macro_hphdelay_lutbypass(codec, rx_priv, interp_idx,
event);
rx_macro_config_compander(codec, rx_priv,
@@ -2045,7 +2072,8 @@
event);
rx_macro_hphdelay_lutbypass(codec, rx_priv, interp_idx,
event);
- rx_macro_hd2_control(codec, interp_idx, event);
+ if (rx_priv->hph_hd2_mode)
+ rx_macro_hd2_control(codec, interp_idx, event);
rx_macro_idle_detect_control(codec, rx_priv,
interp_idx, event);
/* Clk Disable */
@@ -2411,6 +2439,9 @@
SOC_ENUM_EXT("RX_EAR Mode", rx_macro_ear_mode_enum,
rx_macro_get_ear_mode, rx_macro_put_ear_mode),
+ SOC_ENUM_EXT("RX_HPH HD2 Mode", rx_macro_hph_hd2_mode_enum,
+ rx_macro_get_hph_hd2_mode, rx_macro_put_hph_hd2_mode),
+
SOC_ENUM_EXT("RX_HPH_PWR_MODE", rx_macro_hph_pwr_mode_enum,
rx_macro_get_hph_pwr_mode, rx_macro_put_hph_pwr_mode),
diff --git a/asoc/codecs/bolero/tx-macro.c b/asoc/codecs/bolero/tx-macro.c
index a16f778..e58cbd5 100644
--- a/asoc/codecs/bolero/tx-macro.c
+++ b/asoc/codecs/bolero/tx-macro.c
@@ -331,6 +331,24 @@
return 0;
}
+static int tx_macro_reg_wake_irq(struct snd_soc_codec *codec,
+ u32 data)
+{
+ struct device *tx_dev = NULL;
+ struct tx_macro_priv *tx_priv = NULL;
+ u32 ipc_wakeup = data;
+ int ret = 0;
+
+ if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__))
+ return -EINVAL;
+
+ ret = swrm_wcd_notify(
+ tx_priv->swr_ctrl_data[0].tx_swr_pdev,
+ SWR_REGISTER_WAKE_IRQ, &ipc_wakeup);
+
+ return ret;
+}
+
static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work)
{
struct delayed_work *hpf_delayed_work = NULL;
@@ -1683,6 +1701,7 @@
ops->num_dais = ARRAY_SIZE(tx_macro_dai);
ops->mclk_fn = tx_macro_mclk_ctrl;
ops->event_handler = tx_macro_event_handler;
+ ops->reg_wake_irq = tx_macro_reg_wake_irq;
}
static int tx_macro_probe(struct platform_device *pdev)
diff --git a/asoc/codecs/bolero/va-macro.c b/asoc/codecs/bolero/va-macro.c
index 199f75c..820c4df 100644
--- a/asoc/codecs/bolero/va-macro.c
+++ b/asoc/codecs/bolero/va-macro.c
@@ -20,9 +20,12 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
+#include <linux/pm_runtime.h>
#include "bolero-cdc.h"
#include "bolero-cdc-registers.h"
+/* pm runtime auto suspend timer in msecs */
+#define VA_AUTO_SUSPEND_DELAY 1500 /* delay in msec */
#define VA_MACRO_MAX_OFFSET 0x1000
#define VA_MACRO_NUM_DECIMATORS 8
@@ -1617,6 +1620,10 @@
dev_err(&pdev->dev, "%s: register macro failed\n", __func__);
goto reg_macro_fail;
}
+ pm_runtime_set_autosuspend_delay(&pdev->dev, VA_AUTO_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
return ret;
reg_macro_fail:
@@ -1632,7 +1639,8 @@
if (!va_priv)
return -EINVAL;
-
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
bolero_unregister_macro(&pdev->dev, VA_MACRO);
mutex_destroy(&va_priv->mclk_lock);
return 0;
@@ -1644,10 +1652,19 @@
{}
};
+static const struct dev_pm_ops bolero_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(
+ bolero_runtime_suspend,
+ bolero_runtime_resume,
+ NULL
+ )
+};
+
static struct platform_driver va_macro_driver = {
.driver = {
.name = "va_macro",
.owner = THIS_MODULE,
+ .pm = &bolero_dev_pm_ops,
.of_match_table = va_macro_dt_match,
},
.probe = va_macro_probe,
diff --git a/asoc/codecs/wcd-clsh.c b/asoc/codecs/wcd-clsh.c
new file mode 100644
index 0000000..d119b83
--- /dev/null
+++ b/asoc/codecs/wcd-clsh.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <asoc/wcd9xxx_registers.h>
+#include "wcd-clsh.h"
+
+#define WCD_USLEEP_RANGE 50
+
+static void (*clsh_state_fp[NUM_CLSH_STATES])(struct snd_soc_codec *,
+ struct wcd_clsh_cdc_info *,
+ u8 req_state, bool en, int mode);
+
+static const char *mode_to_str(int mode)
+{
+ switch (mode) {
+ case CLS_H_NORMAL:
+ return WCD_CLSH_STRINGIFY(CLS_H_NORMAL);
+ case CLS_H_HIFI:
+ return WCD_CLSH_STRINGIFY(CLS_H_HIFI);
+ case CLS_H_LOHIFI:
+ return WCD_CLSH_STRINGIFY(CLS_H_LOHIFI);
+ case CLS_H_LP:
+ return WCD_CLSH_STRINGIFY(CLS_H_LP);
+ case CLS_H_ULP:
+ return WCD_CLSH_STRINGIFY(CLS_H_ULP);
+ case CLS_AB:
+ return WCD_CLSH_STRINGIFY(CLS_AB);
+ case CLS_AB_HIFI:
+ return WCD_CLSH_STRINGIFY(CLS_AB_HIFI);
+ default:
+ return WCD_CLSH_STRINGIFY(CLS_H_INVALID);
+ };
+}
+
+static const char *state_to_str(u8 state, char *buf, size_t buflen)
+{
+ int i;
+ int cnt = 0;
+ /*
+ * This array of strings should match with enum wcd_clsh_state_bit.
+ */
+ static const char *const states[] = {
+ "STATE_EAR",
+ "STATE_HPH_L",
+ "STATE_HPH_R",
+ "STATE_AUX",
+ };
+
+ if (state == WCD_CLSH_STATE_IDLE) {
+ snprintf(buf, buflen, "[STATE_IDLE]");
+ goto done;
+ }
+
+ buf[0] = '\0';
+ for (i = 0; i < ARRAY_SIZE(states); i++) {
+ if (!(state & (1 << i)))
+ continue;
+ cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf,
+ buf[0] == '\0' ? "[" : "|",
+ states[i]);
+ }
+ if (cnt > 0)
+ strlcat(buf + cnt, "]", buflen);
+
+done:
+ if (buf[0] == '\0')
+ snprintf(buf, buflen, "[STATE_UNKNOWN]");
+ return buf;
+}
+
+static inline int wcd_clsh_get_int_mode(struct wcd_clsh_cdc_info *clsh_d,
+ int clsh_state)
+{
+ int mode;
+
+ if ((clsh_state != WCD_CLSH_STATE_EAR) &&
+ (clsh_state != WCD_CLSH_STATE_HPHL) &&
+ (clsh_state != WCD_CLSH_STATE_HPHR) &&
+ (clsh_state != WCD_CLSH_STATE_AUX))
+ mode = CLS_NONE;
+ else
+ mode = clsh_d->interpolator_modes[ffs(clsh_state)];
+
+ return mode;
+}
+
+static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_info *clsh_d,
+ int clsh_state, int mode)
+{
+ if ((clsh_state != WCD_CLSH_STATE_EAR) &&
+ (clsh_state != WCD_CLSH_STATE_HPHL) &&
+ (clsh_state != WCD_CLSH_STATE_HPHR) &&
+ (clsh_state != WCD_CLSH_STATE_AUX))
+ return;
+
+ clsh_d->interpolator_modes[ffs(clsh_state)] = mode;
+}
+
+static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec,
+ int mode)
+{
+ if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+ mode == CLS_AB_HIFI)
+ snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+ 0x08, 0x08); /* set to HIFI */
+ else
+ snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+ 0x08, 0x00); /* set to default */
+}
+
+static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec,
+ int mode)
+{
+ if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+ mode == CLS_AB_HIFI) {
+ snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
+ 0xF0, 0x80);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+ 0x04, 0x00); /* set to Default */
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
+ 0xF0, 0x70);
+ }
+}
+
+static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec,
+ int mode, bool enable)
+{
+ if (enable) {
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
+ 0xE0, 0xA0);
+ /* 100usec delay is needed as per HW requirement */
+ usleep_range(100, 110);
+ snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_3,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_2,
+ 0xFF, 0x1C);
+ if (mode == CLS_H_LOHIFI) {
+ snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
+ 0x20, 0x20);
+ snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+ 0xF0, 0xC0);
+ snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
+ 0x0E, 0x02);
+ }
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
+ 0x20, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+ 0xF0, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
+ 0x0E, 0x06);
+ }
+}
+
+static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *clsh_d,
+ int mode,
+ bool enable)
+{
+ /* enable/disable buck */
+ if ((enable && (++clsh_d->buck_users == 1)) ||
+ (!enable && (--clsh_d->buck_users == 0))) {
+ snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+ (1 << 7), (enable << 7));
+ /*
+ * 500us sleep is required after buck enable/disable
+ * as per HW requirement
+ */
+ usleep_range(500, 510);
+ if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP ||
+ mode == CLS_H_HIFI || mode == CLS_H_LP)
+ snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_3,
+ 0x02, 0x00);
+
+ snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_2, 0xFF, 0x3A);
+ /* 500usec delay is needed as per HW requirement */
+ usleep_range(500, 500 + WCD_USLEEP_RANGE);
+ }
+ dev_dbg(codec->dev, "%s: buck_users %d, enable %d, mode: %s\n",
+ __func__, clsh_d->buck_users, enable, mode_to_str(mode));
+}
+
+static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *clsh_d,
+ int mode,
+ bool enable)
+{
+ /* enable/disable flyback */
+ if ((enable && (++clsh_d->flyback_users == 1)) ||
+ (!enable && (--clsh_d->flyback_users == 0))) {
+ snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+ (1 << 6), (enable << 6));
+ /*
+ * 100us sleep is required after flyback enable/disable
+ * as per HW requirement
+ */
+ usleep_range(100, 110);
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
+ 0xE0, 0xE0);
+ /* 500usec delay is needed as per HW requirement */
+ usleep_range(500, 500 + WCD_USLEEP_RANGE);
+ }
+ dev_dbg(codec->dev, "%s: flyback_users %d, enable %d, mode: %s\n",
+ __func__, clsh_d->flyback_users, enable, mode_to_str(mode));
+}
+
+static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec,
+ int mode)
+{
+ u8 val = 0;
+
+ switch (mode) {
+ case CLS_H_NORMAL:
+ case CLS_H_LOHIFI:
+ val = 0x00;
+ break;
+ case CLS_AB:
+ case CLS_H_ULP:
+ val = 0x0C;
+ break;
+ case CLS_AB_HIFI:
+ case CLS_H_HIFI:
+ val = 0x08;
+ break;
+ case CLS_H_LP:
+ val = 0x04;
+ break;
+ default:
+ dev_err(codec->dev, "%s:Invalid mode %d\n", __func__, mode);
+ return;
+ };
+
+ snd_soc_update_bits(codec, WCD9XXX_ANA_HPH, 0x0C, val);
+}
+
+static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode)
+{
+
+ snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A);
+ snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0xF0, 0xA0);
+ /* Sleep needed to avoid click and pop as per HW requirement */
+ usleep_range(100, 110);
+}
+
+static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_codec *codec,
+ int mode)
+{
+ snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+ 0x02, 0x00);
+}
+
+static void wcd_clsh_state_ear_aux(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+}
+
+static void wcd_clsh_state_hph_aux(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+}
+
+static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+}
+
+static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+}
+
+static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (mode == CLS_H_NORMAL) {
+ dev_dbg(codec->dev, "%s: Normal mode not applicable for hph_r\n",
+ __func__);
+ return;
+ }
+
+ if (is_enable) {
+ wcd_clsh_set_buck_regulator_mode(codec, mode);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_force_iq_ctl(codec, mode, true);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_flyback_current(codec, mode);
+ wcd_clsh_set_buck_mode(codec, mode);
+ wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_hph_mode(codec, mode);
+ } else {
+ wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
+
+ /* buck and flyback set to default mode and disable */
+ wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false);
+ wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (mode == CLS_H_NORMAL) {
+ dev_dbg(codec->dev, "%s: Normal mode not applicable for hph_l\n",
+ __func__);
+ return;
+ }
+
+ if (is_enable) {
+ wcd_clsh_set_buck_regulator_mode(codec, mode);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_force_iq_ctl(codec, mode, true);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_flyback_current(codec, mode);
+ wcd_clsh_set_buck_mode(codec, mode);
+ wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_hph_mode(codec, mode);
+ } else {
+ wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
+
+ /* set buck and flyback to Default Mode */
+ wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false);
+ wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_aux(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (is_enable) {
+ wcd_clsh_set_buck_mode(codec, mode);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_flyback_current(codec, mode);
+ wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+ } else {
+ wcd_clsh_buck_ctrl(codec, clsh_d, mode, false);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false);
+ wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_ear(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (is_enable) {
+ wcd_clsh_set_buck_regulator_mode(codec, mode);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_force_iq_ctl(codec, mode, true);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_flyback_current(codec, mode);
+ wcd_clsh_set_buck_mode(codec, mode);
+ wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_hph_mode(codec, mode);
+ } else {
+ wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
+
+ /* set buck and flyback to Default Mode */
+ wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false);
+ wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_err(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ char msg[128];
+
+ dev_err(codec->dev,
+ "%s Wrong request for class H state machine requested to %s %s\n",
+ __func__, is_enable ? "enable" : "disable",
+ state_to_str(req_state, msg, sizeof(msg)));
+}
+
+/*
+ * Function: wcd_clsh_is_state_valid
+ * Params: state
+ * Description:
+ * Provides information on valid states of Class H configuration
+ */
+static bool wcd_clsh_is_state_valid(u8 state)
+{
+ switch (state) {
+ case WCD_CLSH_STATE_IDLE:
+ case WCD_CLSH_STATE_EAR:
+ case WCD_CLSH_STATE_HPHL:
+ case WCD_CLSH_STATE_HPHR:
+ case WCD_CLSH_STATE_HPH_ST:
+ case WCD_CLSH_STATE_AUX:
+ case WCD_CLSH_STATE_HPHL_AUX:
+ case WCD_CLSH_STATE_HPHR_AUX:
+ case WCD_CLSH_STATE_HPH_ST_AUX:
+ case WCD_CLSH_STATE_EAR_AUX:
+ return true;
+ default:
+ return false;
+ };
+}
+
+/*
+ * Function: wcd_cls_h_fsm
+ * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event
+ * Description:
+ * This function handles PRE DAC and POST DAC conditions of different devices
+ * and updates class H configuration of different combination of devices
+ * based on validity of their states. cdc_clsh_d will contain current
+ * class h state information
+ */
+void wcd_cls_h_fsm(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *cdc_clsh_d,
+ u8 clsh_event, u8 req_state,
+ int int_mode)
+{
+ u8 old_state, new_state;
+ char msg0[128], msg1[128];
+
+ switch (clsh_event) {
+ case WCD_CLSH_EVENT_PRE_DAC:
+ old_state = cdc_clsh_d->state;
+ new_state = old_state | req_state;
+
+ if (!wcd_clsh_is_state_valid(new_state)) {
+ dev_err(codec->dev,
+ "%s: Class-H not a valid new state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0, sizeof(msg0)));
+ return;
+ }
+ if (new_state == old_state) {
+ dev_err(codec->dev,
+ "%s: Class-H already in requested state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0, sizeof(msg0)));
+ return;
+ }
+ cdc_clsh_d->state = new_state;
+ wcd_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode);
+ (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state,
+ CLSH_REQ_ENABLE, int_mode);
+ dev_dbg(codec->dev,
+ "%s: ClassH state transition from %s to %s\n",
+ __func__, state_to_str(old_state, msg0, sizeof(msg0)),
+ state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1)));
+ break;
+ case WCD_CLSH_EVENT_POST_PA:
+ old_state = cdc_clsh_d->state;
+ new_state = old_state & (~req_state);
+ if (new_state < NUM_CLSH_STATES) {
+ if (!wcd_clsh_is_state_valid(old_state)) {
+ dev_err(codec->dev,
+ "%s:Invalid old state:%s\n",
+ __func__,
+ state_to_str(old_state, msg0,
+ sizeof(msg0)));
+ return;
+ }
+ if (new_state == old_state) {
+ dev_err(codec->dev,
+ "%s: Class-H already in requested state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0,
+ sizeof(msg0)));
+ return;
+ }
+ (*clsh_state_fp[old_state]) (codec, cdc_clsh_d,
+ req_state, CLSH_REQ_DISABLE,
+ int_mode);
+ cdc_clsh_d->state = new_state;
+ wcd_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE);
+ dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n",
+ __func__, state_to_str(old_state, msg0,
+ sizeof(msg0)),
+ state_to_str(cdc_clsh_d->state, msg1,
+ sizeof(msg1)));
+ }
+ break;
+ };
+}
+EXPORT_SYMBOL(wcd_cls_h_fsm);
+
+/*
+ * wcd_cls_h_init: Called to init clsh info
+ *
+ * @clsh: pointer for clsh state information.
+ */
+void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh)
+{
+ int i;
+
+ clsh->state = WCD_CLSH_STATE_IDLE;
+
+ for (i = 0; i < NUM_CLSH_STATES; i++)
+ clsh_state_fp[i] = wcd_clsh_state_err;
+
+ clsh_state_fp[WCD_CLSH_STATE_EAR] = wcd_clsh_state_ear;
+ clsh_state_fp[WCD_CLSH_STATE_HPHL] = wcd_clsh_state_hph_l;
+ clsh_state_fp[WCD_CLSH_STATE_HPHR] = wcd_clsh_state_hph_r;
+ clsh_state_fp[WCD_CLSH_STATE_HPH_ST] = wcd_clsh_state_hph_st;
+ clsh_state_fp[WCD_CLSH_STATE_AUX] = wcd_clsh_state_aux;
+ clsh_state_fp[WCD_CLSH_STATE_HPHL_AUX] = wcd_clsh_state_hph_aux;
+ clsh_state_fp[WCD_CLSH_STATE_HPHR_AUX] = wcd_clsh_state_hph_aux;
+ clsh_state_fp[WCD_CLSH_STATE_HPH_ST_AUX] =
+ wcd_clsh_state_hph_aux;
+ clsh_state_fp[WCD_CLSH_STATE_EAR_AUX] = wcd_clsh_state_ear_aux;
+ clsh_state_fp[WCD_CLSH_STATE_HPHL_EAR] = wcd_clsh_state_hph_ear;
+ clsh_state_fp[WCD_CLSH_STATE_HPHR_EAR] = wcd_clsh_state_hph_ear;
+ clsh_state_fp[WCD_CLSH_STATE_HPH_ST_EAR] = wcd_clsh_state_hph_ear;
+ /* Set interpolaotr modes to NONE */
+ wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_EAR, CLS_NONE);
+ wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHL, CLS_NONE);
+ wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHR, CLS_NONE);
+ wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_AUX, CLS_NONE);
+ clsh->flyback_users = 0;
+ clsh->buck_users = 0;
+}
+EXPORT_SYMBOL(wcd_cls_h_init);
+
+MODULE_DESCRIPTION("WCD Class-H Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/asoc/codecs/wcd-clsh.h b/asoc/codecs/wcd-clsh.h
new file mode 100644
index 0000000..df305bc
--- /dev/null
+++ b/asoc/codecs/wcd-clsh.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WCD_CLSH
+#define _WCD_CLSH
+
+#include <linux/stringify.h>
+
+#define WCD_CLSH_STRINGIFY(s) __stringify(s)
+#define CLSH_REQ_ENABLE true
+#define CLSH_REQ_DISABLE false
+
+#define WCD_CLSH_EVENT_PRE_DAC 0x01
+#define WCD_CLSH_EVENT_POST_PA 0x02
+/*
+ * Basic states for Class H state machine.
+ * represented as a bit mask within a u8 data type
+ * bit 0: EAR mode
+ * bit 1: HPH Left mode
+ * bit 2: HPH Right mode
+ * bit 3: AUX mode
+ */
+#define WCD_CLSH_STATE_IDLE 0x00
+#define WCD_CLSH_STATE_EAR (0x01 << 0)
+#define WCD_CLSH_STATE_HPHL (0x01 << 1)
+#define WCD_CLSH_STATE_HPHR (0x01 << 2)
+#define WCD_CLSH_STATE_AUX (0x01 << 3)
+
+/*
+ * Though number of CLSH states is 4, max state should be 5
+ * because state array index starts from 1.
+ */
+#define WCD_CLSH_STATE_MAX 5
+#define NUM_CLSH_STATES (0x01 << WCD_CLSH_STATE_MAX)
+
+/* Derived State: Bits 1 and 2 should be set for Headphone stereo */
+#define WCD_CLSH_STATE_HPH_ST (WCD_CLSH_STATE_HPHL | \
+ WCD_CLSH_STATE_HPHR)
+
+#define WCD_CLSH_STATE_HPHL_AUX (WCD_CLSH_STATE_HPHL | \
+ WCD_CLSH_STATE_AUX)
+#define WCD_CLSH_STATE_HPHR_AUX (WCD_CLSH_STATE_HPHR | \
+ WCD_CLSH_STATE_AUX)
+#define WCD_CLSH_STATE_HPH_ST_AUX (WCD_CLSH_STATE_HPH_ST | \
+ WCD_CLSH_STATE_AUX)
+#define WCD_CLSH_STATE_EAR_AUX (WCD_CLSH_STATE_EAR | \
+ WCD_CLSH_STATE_AUX)
+#define WCD_CLSH_STATE_HPHL_EAR (WCD_CLSH_STATE_HPHL | \
+ WCD_CLSH_STATE_EAR)
+#define WCD_CLSH_STATE_HPHR_EAR (WCD_CLSH_STATE_HPHR | \
+ WCD_CLSH_STATE_EAR)
+#define WCD_CLSH_STATE_HPH_ST_EAR (WCD_CLSH_STATE_HPH_ST | \
+ WCD_CLSH_STATE_EAR)
+
+enum {
+ CLS_H_NORMAL = 0, /* Class-H Default */
+ CLS_H_HIFI, /* Class-H HiFi */
+ CLS_H_LP, /* Class-H Low Power */
+ CLS_AB, /* Class-AB Low HIFI*/
+ CLS_H_LOHIFI, /* LoHIFI */
+ CLS_H_ULP, /* Ultra Low power */
+ CLS_AB_HIFI, /* Class-AB */
+ CLS_NONE, /* None of the above modes */
+};
+
+/* Class H data that the codec driver will maintain */
+struct wcd_clsh_cdc_info {
+ u8 state;
+ int flyback_users;
+ int buck_users;
+ int interpolator_modes[WCD_CLSH_STATE_MAX];
+};
+
+#ifdef CONFIG_SND_SOC_WCD9XXX_V2
+extern void wcd_cls_h_fsm(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *cdc_clsh_d,
+ u8 clsh_event, u8 req_state,
+ int int_mode);
+
+extern void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh);
+#else
+extern void wcd_cls_h_fsm(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_info *cdc_clsh_d,
+ u8 clsh_event, u8 req_state,
+ int int_mode)
+{
+}
+
+extern void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh)
+{
+}
+#endif /* CONFIG_SND_SOC_WCD9XXX_V2 */
+
+#endif
diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c
index 44f741e..0ca7dbb 100644
--- a/asoc/codecs/wcd934x/wcd934x.c
+++ b/asoc/codecs/wcd934x/wcd934x.c
@@ -8939,6 +8939,8 @@
static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil)
{
+ if (!tavil)
+ return;
mutex_lock(&tavil->power_lock);
dev_dbg(tavil->dev, "%s: Entering power gating function, %d\n",
__func__, tavil->power_active_ref);
@@ -10298,6 +10300,8 @@
snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture");
snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback");
snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "WDMA3_OUT");
+
if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback");
snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX");
diff --git a/asoc/codecs/wcd937x/internal.h b/asoc/codecs/wcd937x/internal.h
index c0f96f1..d712129 100644
--- a/asoc/codecs/wcd937x/internal.h
+++ b/asoc/codecs/wcd937x/internal.h
@@ -13,6 +13,7 @@
#ifndef _WCD937X_INTERNAL_H
#define _WCD937X_INTERNAL_H
+#include "../wcd-clsh.h"
#include "../wcd-mbhc-v2.h"
#include "asoc/wcd-irq.h"
#include "wcd937x-mbhc.h"
@@ -54,6 +55,8 @@
s32 dmic_0_1_clk_cnt;
s32 dmic_2_3_clk_cnt;
s32 dmic_4_5_clk_cnt;
+ /* class h specific info */
+ struct wcd_clsh_cdc_info clsh_info;
/* mbhc module */
struct wcd937x_mbhc *mbhc;
@@ -136,8 +139,8 @@
enum {
/* INTR_CTRL_INT_MASK_0 */
- WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET = 0,
- WCD937X_IRQ_MBHC_BUTTON_PRESS_DET,
+ WCD937X_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+ WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET,
WCD937X_IRQ_MBHC_ELECT_INS_REM_DET,
WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
WCD937X_IRQ_MBHC_SW_DET,
diff --git a/asoc/codecs/wcd937x/wcd937x.c b/asoc/codecs/wcd937x/wcd937x.c
index 34e4b1b..0d88e67 100644
--- a/asoc/codecs/wcd937x/wcd937x.c
+++ b/asoc/codecs/wcd937x/wcd937x.c
@@ -118,6 +118,10 @@
snd_soc_update_bits(codec, WCD937X_ANA_BIAS, 0x40, 0x40);
usleep_range(10000, 10010);
snd_soc_update_bits(codec, WCD937X_ANA_BIAS, 0x40, 0x00);
+ snd_soc_update_bits(codec, WCD937X_HPH_OCP_CTL, 0xFF, 0x3A);
+ snd_soc_update_bits(codec, WCD937X_RX_OCP_CTL, 0x0F, 0x02);
+ snd_soc_update_bits(codec, WCD937X_HPH_R_TEST, 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD937X_HPH_L_TEST, 0x01, 0x01);
return 0;
}
@@ -297,7 +301,6 @@
static int wcd937x_rx_clk_enable(struct snd_soc_codec *codec)
{
-
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
if (wcd937x->rx_clk_cnt == 0) {
@@ -324,10 +327,12 @@
{
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+ if (wcd937x->rx_clk_cnt == 0) {
+ dev_dbg(wcd937x->dev, "%s:clk already disabled\n", __func__);
+ return 0;
+ }
wcd937x->rx_clk_cnt--;
if (wcd937x->rx_clk_cnt == 0) {
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, 0x40, 0x00);
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, 0x80, 0x00);
snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, 0x01, 0x00);
snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL,
0x02, 0x00);
@@ -368,6 +373,7 @@
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+ int hph_mode = wcd937x->hph_mode;
int ret = 0;
dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
@@ -384,8 +390,14 @@
0x80, 0x00);
break;
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
- 0x0F, 0x02);
+ if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI)
+ snd_soc_update_bits(codec,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0F, 0x02);
+ else if (hph_mode == CLS_H_LOHIFI)
+ snd_soc_update_bits(codec,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0F, 0x06);
if (wcd937x->comp1_enable) {
snd_soc_update_bits(codec,
WCD937X_DIGITAL_CDC_COMP_CTL_0,
@@ -402,8 +414,15 @@
usleep_range(5000, 5010);
snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1,
0x02, 0x00);
+ wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHL,
+ hph_mode);
break;
case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0F, 0x01);
ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
wcd937x->rx_swr_dev->dev_num,
false);
@@ -419,6 +438,7 @@
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+ int hph_mode = wcd937x->hph_mode;
int ret = 0;
@@ -436,8 +456,14 @@
0x80, 0x00);
break;
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
- 0x0F, 0x02);
+ if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI)
+ snd_soc_update_bits(codec,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ 0x0F, 0x02);
+ else if (hph_mode == CLS_H_LOHIFI)
+ snd_soc_update_bits(codec,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ 0x0F, 0x06);
if (wcd937x->comp2_enable) {
snd_soc_update_bits(codec,
WCD937X_DIGITAL_CDC_COMP_CTL_0,
@@ -454,8 +480,15 @@
usleep_range(5000, 5010);
snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1,
0x02, 0x00);
+ wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHR,
+ hph_mode);
break;
case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+ 0x0F, 0x01);
ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
wcd937x->rx_swr_dev->dev_num,
false);
@@ -471,6 +504,7 @@
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+ int hph_mode = wcd937x->hph_mode;
int ret = 0;
dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
@@ -483,11 +517,29 @@
0x04, 0x04);
snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
0x01, 0x01);
+ if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI)
+ snd_soc_update_bits(codec,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0F, 0x02);
+ else if (hph_mode == CLS_H_LOHIFI)
+ snd_soc_update_bits(codec,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0F, 0x06);
snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_COMP_CTL_0,
0x02, 0x02);
usleep_range(5000, 5010);
+ wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_EAR,
+ hph_mode);
+
break;
case SND_SOC_DAPM_POST_PMD:
+ if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_LOHIFI ||
+ hph_mode == CLS_H_HIFI)
+ snd_soc_update_bits(codec,
+ WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+ 0x0F, 0x01);
ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
wcd937x->rx_swr_dev->dev_num,
false);
@@ -503,6 +555,7 @@
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+ int hph_mode = wcd937x->hph_mode;
int ret = 0;
dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
@@ -517,6 +570,11 @@
0x04, 0x04);
snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_AUX_GAIN_CTL,
0x01, 0x01);
+ wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_AUX,
+ hph_mode);
+
break;
case SND_SOC_DAPM_POST_PMD:
ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
@@ -538,6 +596,7 @@
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
int ret = 0;
+ int hph_mode = wcd937x->hph_mode;
dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
w->name, event);
@@ -554,8 +613,9 @@
usleep_range(7000, 7010);
snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1,
0x02, 0x02);
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
- 0x02, 0x02);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+ snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
if (wcd937x->update_wcd_event)
wcd937x->update_wcd_event(wcd937x->handle,
WCD_BOLERO_EVT_RX_MUTE,
@@ -576,6 +636,10 @@
WCD_EVENT_POST_HPHR_PA_OFF,
&wcd937x->mbhc->wcd_mbhc);
snd_soc_update_bits(codec, WCD937X_ANA_HPH, 0x10, 0x00);
+ wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHR,
+ hph_mode);
break;
};
return ret;
@@ -588,6 +652,7 @@
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
int ret = 0;
+ int hph_mode = wcd937x->hph_mode;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -601,8 +666,9 @@
usleep_range(7000, 7010);
snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1,
0x02, 0x02);
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
- 0x02, 0x02);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+ snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
if (wcd937x->update_wcd_event)
wcd937x->update_wcd_event(wcd937x->handle,
WCD_BOLERO_EVT_RX_MUTE,
@@ -623,6 +689,10 @@
WCD_EVENT_POST_HPHL_PA_OFF,
&wcd937x->mbhc->wcd_mbhc);
snd_soc_update_bits(codec, WCD937X_ANA_HPH, 0x20, 0x00);
+ wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHL,
+ hph_mode);
break;
};
return ret;
@@ -634,6 +704,7 @@
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+ int hph_mode = wcd937x->hph_mode;
int ret = 0;
dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
@@ -641,19 +712,15 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
- 0x80, 0x80);
- usleep_range(500, 510);
- snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A);
- usleep_range(500, 510);
ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
wcd937x->rx_swr_dev->dev_num,
true);
break;
case SND_SOC_DAPM_POST_PMU:
usleep_range(1000, 1010);
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
- 0x20, 0x20);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+ snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
if (wcd937x->update_wcd_event)
wcd937x->update_wcd_event(wcd937x->handle,
WCD_BOLERO_EVT_RX_MUTE,
@@ -668,6 +735,10 @@
case SND_SOC_DAPM_POST_PMD:
usleep_range(1000, 1010);
usleep_range(1000, 1010);
+ wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_AUX,
+ hph_mode);
break;
};
return ret;
@@ -679,6 +750,7 @@
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+ int hph_mode = wcd937x->hph_mode;
int ret = 0;
dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
@@ -686,19 +758,15 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
- 0x08, 0x08);
- usleep_range(500, 510);
- snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A);
- usleep_range(500, 510);
ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
wcd937x->rx_swr_dev->dev_num,
true);
break;
case SND_SOC_DAPM_POST_PMU:
usleep_range(6000, 6010);
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
- 0x02, 0x02);
+ if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+ snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
if (wcd937x->update_wcd_event)
wcd937x->update_wcd_event(wcd937x->handle,
WCD_BOLERO_EVT_RX_MUTE,
@@ -712,16 +780,44 @@
break;
case SND_SOC_DAPM_POST_PMD:
usleep_range(7000, 7010);
+ wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_EAR,
+ hph_mode);
break;
};
return ret;
}
+static int wcd937x_enable_clsh(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+ int mode = wcd937x->hph_mode;
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
+ w->name, event);
+
+ if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP ||
+ mode == CLS_H_HIFI || mode == CLS_H_LP) {
+ wcd937x_rx_connect_port(codec, CLSH,
+ SND_SOC_DAPM_EVENT_ON(event));
+ if (SND_SOC_DAPM_EVENT_OFF(event))
+ ret = swr_slvdev_datapath_control(
+ wcd937x->rx_swr_dev,
+ wcd937x->rx_swr_dev->dev_num,
+ false);
+ }
+ return ret;
+}
+
static int wcd937x_enable_rx1(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
-
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
@@ -730,26 +826,6 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_4,
- 0xF0, 0x80);
- snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2,
- 0xE0, 0xA0);
- snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_3,
- 0x02, 0x02);
- snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2,
- 0xFF, 0x1C);
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
- 0x40, 0x40);
- usleep_range(100, 110);
- snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2,
- 0xE0, 0xE0);
- usleep_range(100, 110);
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
- 0x80, 0x80);
- usleep_range(500, 510);
- snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A);
- usleep_range(500, 510);
-
wcd937x_rx_connect_port(codec, HPH_L, true);
if (wcd937x->comp1_enable)
wcd937x_rx_connect_port(codec, COMP_L, true);
@@ -765,6 +841,7 @@
};
return 0;
}
+
static int wcd937x_enable_rx2(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -776,24 +853,6 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_4,
- 0xF0, 0x80);
- snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2,
- 0xE0, 0xA0);
- snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_3, 0x02, 0x02);
- snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x1C);
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
- 0x40, 0x40);
- usleep_range(100, 110);
- snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2,
- 0xE0, 0xE0);
- usleep_range(100, 110);
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
- 0x80, 0x80);
- usleep_range(500, 510);
- snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A);
- usleep_range(500, 510);
-
wcd937x_rx_connect_port(codec, HPH_R, true);
if (wcd937x->comp2_enable)
wcd937x_rx_connect_port(codec, COMP_R, true);
@@ -823,16 +882,6 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_2,
- 0xE0, 0xA0);
- snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_3, 0x02, 0x02);
- snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x1C);
- snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
- 0x40, 0x40);
- usleep_range(100, 110);
- snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_2,
- 0xE0, 0xE0);
- usleep_range(100, 110);
wcd937x_rx_connect_port(codec, LO, true);
break;
case SND_SOC_DAPM_POST_PMD:
@@ -1594,6 +1643,10 @@
wcd937x_codec_enable_vdd_buck,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("CLS_H_PORT", SND_SOC_NOPM, 0, 0,
+ wcd937x_enable_clsh,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
/*rx widgets*/
SND_SOC_DAPM_PGA_E("EAR PGA", WCD937X_ANA_EAR, 7, 0, NULL, 0,
wcd937x_codec_enable_ear_pa,
@@ -1781,6 +1834,11 @@
{"HPHR", NULL, "VDD_BUCK"},
{"HPHL", NULL, "VDD_BUCK"},
{"AUX", NULL, "VDD_BUCK"},
+
+ {"EAR", NULL, "CLS_H_PORT"},
+ {"HPHR", NULL, "CLS_H_PORT"},
+ {"HPHL", NULL, "CLS_H_PORT"},
+ {"AUX", NULL, "CLS_H_PORT"},
};
static const struct snd_soc_dapm_route wcd9375_audio_map[] = {
@@ -1953,6 +2011,7 @@
snd_soc_dapm_ignore_suspend(dapm, "HPHR");
snd_soc_dapm_sync(dapm);
+ wcd_cls_h_init(&wcd937x->clsh_info);
wcd937x_init_reg(codec);
if (wcd937x->variant == WCD9375_VARIANT) {
diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c
index 171337a..9516867 100644
--- a/asoc/msm-dai-q6-v2.c
+++ b/asoc/msm-dai-q6-v2.c
@@ -3231,8 +3231,9 @@
sizeof(struct asm_aac_dec_cfg_v2_t));
break;
case DEC_FMT_SBC:
- case DEC_FMT_MP3:
- /* No decoder specific data available */
+ memcpy(&dai_data->dec_config.data,
+ ucontrol->value.bytes.data + format_size,
+ sizeof(struct asm_sbc_dec_cfg_t));
break;
default:
pr_debug("%s: Default decoder config for %d format: Expect abr_dec_cfg\n",
diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c
index 1dccecd..5da9795 100644
--- a/asoc/msm-pcm-q6-v2.c
+++ b/asoc/msm-pcm-q6-v2.c
@@ -686,6 +686,7 @@
if (!prtd->audio_client) {
pr_info("%s: Could not allocate memory\n", __func__);
kfree(prtd);
+ prtd = NULL;
return -ENOMEM;
}
@@ -1174,6 +1175,12 @@
}
prtd = substream->runtime->private_data;
+ if (prtd == NULL) {
+ pr_err("%s prtd is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
if (prtd->audio_client == NULL) {
pr_err("%s prtd is null.\n", __func__);
ret = -EINVAL;
diff --git a/asoc/sdm845.c b/asoc/sdm845.c
index 392ac85..3fe4023 100644
--- a/asoc/sdm845.c
+++ b/asoc/sdm845.c
@@ -116,19 +116,6 @@
AUX_PCM_MAX,
};
-enum {
- PCM_I2S_SEL_PRIM = 0,
- PCM_I2S_SEL_SEC,
- PCM_I2S_SEL_TERT,
- PCM_I2S_SEL_QUAT,
- PCM_I2S_SEL_MAX,
-};
-
-struct mi2s_aux_pcm_common_conf {
- struct mutex lock;
- void *pcm_i2s_sel_vt_addr;
-};
-
struct mi2s_conf {
struct mutex lock;
u32 ref_cnt;
@@ -142,11 +129,6 @@
Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT
};
-struct auxpcm_conf {
- struct mutex lock;
- u32 ref_cnt;
-};
-
struct dev_config {
u32 sample_rate;
u32 bit_format;
@@ -584,9 +566,7 @@
}
};
-static struct mi2s_aux_pcm_common_conf mi2s_auxpcm_conf[PCM_I2S_SEL_MAX];
static struct mi2s_conf mi2s_intf_conf[MI2S_MAX];
-static struct auxpcm_conf auxpcm_intf_conf[AUX_PCM_MAX];
static int slim_get_sample_rate_val(int sample_rate)
{
@@ -4116,83 +4096,6 @@
return ret;
}
-static int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream)
-{
- int ret = 0;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int index = cpu_dai->id - 1;
-
- dev_dbg(rtd->card->dev,
- "%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
- __func__, substream->name, substream->stream,
- cpu_dai->name, cpu_dai->id);
-
- if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
- ret = -EINVAL;
- dev_err(rtd->card->dev,
- "%s: CPU DAI id (%d) out of range\n",
- __func__, cpu_dai->id);
- goto err;
- }
-
- mutex_lock(&auxpcm_intf_conf[index].lock);
- if (++auxpcm_intf_conf[index].ref_cnt == 1) {
- if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
- mutex_lock(&mi2s_auxpcm_conf[index].lock);
- iowrite32(1,
- mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
- mutex_unlock(&mi2s_auxpcm_conf[index].lock);
- } else {
- dev_err(rtd->card->dev,
- "%s lpaif_tert_muxsel_virt_addr is NULL\n",
- __func__);
- ret = -EINVAL;
- }
- }
- if (ret < 0)
- auxpcm_intf_conf[index].ref_cnt--;
-
- mutex_unlock(&auxpcm_intf_conf[index].lock);
-
-err:
- return ret;
-}
-
-static void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- int index = rtd->cpu_dai->id - 1;
-
- dev_dbg(rtd->card->dev,
- "%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
- __func__,
- substream->name, substream->stream,
- rtd->cpu_dai->name, rtd->cpu_dai->id);
-
- if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
- dev_err(rtd->card->dev,
- "%s: CPU DAI id (%d) out of range\n",
- __func__, rtd->cpu_dai->id);
- return;
- }
-
- mutex_lock(&auxpcm_intf_conf[index].lock);
- if (--auxpcm_intf_conf[index].ref_cnt == 0) {
- if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
- mutex_lock(&mi2s_auxpcm_conf[index].lock);
- iowrite32(0,
- mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
- mutex_unlock(&mi2s_auxpcm_conf[index].lock);
- } else {
- dev_err(rtd->card->dev,
- "%s lpaif_tert_muxsel_virt_addr is NULL\n",
- __func__);
- }
- }
- mutex_unlock(&auxpcm_intf_conf[index].lock);
-}
-
static int msm_get_port_id(int be_id)
{
int afe_port_id;
@@ -4723,18 +4626,7 @@
__func__, ret);
goto clean_up;
}
- if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
- mutex_lock(&mi2s_auxpcm_conf[index].lock);
- iowrite32(0,
- mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
- mutex_unlock(&mi2s_auxpcm_conf[index].lock);
- } else {
- dev_err(rtd->card->dev,
- "%s lpaif_muxsel_virt_addr is NULL for dai %d\n",
- __func__, index);
- ret = -EINVAL;
- goto clk_off;
- }
+
ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
if (ret < 0) {
pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n",
@@ -4792,11 +4684,6 @@
.shutdown = msm_mi2s_snd_shutdown,
};
-static struct snd_soc_ops msm_aux_pcm_be_ops = {
- .startup = msm_aux_pcm_snd_startup,
- .shutdown = msm_aux_pcm_snd_shutdown,
-};
-
static struct snd_soc_ops msm_be_ops = {
.hw_params = msm_snd_hw_params,
};
@@ -5412,6 +5299,7 @@
.codec_name = "tavil_codec",
.codec_dai_name = "tavil_rx2",
.ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ops = &msm_slimbus_2_be_ops,
},
@@ -6084,7 +5972,6 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_pmdown_time = 1,
.ignore_suspend = 1,
- .ops = &msm_aux_pcm_be_ops,
},
{
.name = LPASS_BE_AUXPCM_TX,
@@ -6099,7 +5986,6 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_pmdown_time = 1,
.ignore_suspend = 1,
- .ops = &msm_aux_pcm_be_ops,
},
/* Secondary AUX PCM Backend DAI Links */
{
@@ -6115,7 +6001,6 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_pmdown_time = 1,
.ignore_suspend = 1,
- .ops = &msm_aux_pcm_be_ops,
},
{
.name = LPASS_BE_SEC_AUXPCM_TX,
@@ -6130,7 +6015,6 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = 1,
.ignore_pmdown_time = 1,
- .ops = &msm_aux_pcm_be_ops,
},
/* Tertiary AUX PCM Backend DAI Links */
{
@@ -6146,7 +6030,6 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_pmdown_time = 1,
.ignore_suspend = 1,
- .ops = &msm_aux_pcm_be_ops,
},
{
.name = LPASS_BE_TERT_AUXPCM_TX,
@@ -6161,7 +6044,6 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = 1,
.ignore_pmdown_time = 1,
- .ops = &msm_aux_pcm_be_ops,
},
/* Quaternary AUX PCM Backend DAI Links */
{
@@ -6177,7 +6059,6 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_pmdown_time = 1,
.ignore_suspend = 1,
- .ops = &msm_aux_pcm_be_ops,
},
{
.name = LPASS_BE_QUAT_AUXPCM_TX,
@@ -6192,7 +6073,6 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = 1,
.ignore_pmdown_time = 1,
- .ops = &msm_aux_pcm_be_ops,
},
};
@@ -6822,41 +6702,15 @@
static void msm_i2s_auxpcm_init(struct platform_device *pdev)
{
- struct resource *muxsel;
int count;
u32 mi2s_master_slave[MI2S_MAX];
int ret;
- char *str[PCM_I2S_SEL_MAX] = {
- "lpaif_pri_mode_muxsel",
- "lpaif_sec_mode_muxsel",
- "lpaif_tert_mode_muxsel",
- "lpaif_quat_mode_muxsel"
- };
for (count = 0; count < MI2S_MAX; count++) {
mutex_init(&mi2s_intf_conf[count].lock);
mi2s_intf_conf[count].ref_cnt = 0;
}
- for (count = 0; count < AUX_PCM_MAX; count++) {
- mutex_init(&auxpcm_intf_conf[count].lock);
- auxpcm_intf_conf[count].ref_cnt = 0;
- }
-
- for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
- mutex_init(&mi2s_auxpcm_conf[count].lock);
- mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL;
- }
-
- for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
- muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- str[count]);
- if (muxsel) {
- mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr
- = ioremap(muxsel->start, resource_size(muxsel));
- }
- }
-
ret = of_property_read_u32_array(pdev->dev.of_node,
"qcom,msm-mi2s-master",
mi2s_master_slave, MI2S_MAX);
@@ -6875,21 +6729,6 @@
{
int count;
- for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
- if (mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr !=
- NULL) {
- iounmap(
- mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr);
- mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL;
- }
- mutex_destroy(&mi2s_auxpcm_conf[count].lock);
- }
-
- for (count = 0; count < AUX_PCM_MAX; count++) {
- mutex_destroy(&auxpcm_intf_conf[count].lock);
- auxpcm_intf_conf[count].ref_cnt = 0;
- }
-
for (count = 0; count < MI2S_MAX; count++) {
mutex_destroy(&mi2s_intf_conf[count].lock);
mi2s_intf_conf[count].ref_cnt = 0;
diff --git a/asoc/sm6150.c b/asoc/sm6150.c
index 307ef0c..65aaa9b 100644
--- a/asoc/sm6150.c
+++ b/asoc/sm6150.c
@@ -30,6 +30,7 @@
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <soc/snd_event.h>
+#include <soc/qcom/socinfo.h>
#include <dsp/q6afe-v2.h>
#include <dsp/q6core.h>
#include "device_event.h"
@@ -79,6 +80,9 @@
#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */
#define MSM_HIFI_ON 1
+#define SM6150_SOC_VERSION_1_0 0x00010000
+#define SM6150_SOC_MSM_ID 0x163
+
enum {
SLIM_RX_0 = 0,
SLIM_RX_1,
@@ -4925,6 +4929,19 @@
pdata->codec_root = entry;
}
bolero_info_create_codec_entry(pdata->codec_root, codec);
+ /*
+ * SM6150 MSM 1.0 doesn't have hardware wake up interrupt line
+ * from AOSS to APSS. So, it uses SW workaround and listens to
+ * interrupt from AFE over IPC.
+ * Check for MSM version and MSM ID and register wake irq
+ * accordingly to provide compatibility to all chipsets.
+ */
+ if (socinfo_get_id() == SM6150_SOC_MSM_ID &&
+ socinfo_get_version() == SM6150_SOC_VERSION_1_0)
+ bolero_register_wake_irq(codec, true);
+ else
+ bolero_register_wake_irq(codec, false);
+
codec_reg_done = true;
return 0;
err:
diff --git a/dsp/q6adm.c b/dsp/q6adm.c
index b9c11ac..c664b59 100644
--- a/dsp/q6adm.c
+++ b/dsp/q6adm.c
@@ -644,7 +644,7 @@
index = index + ch_mixer->input_channels[channel_index];
ret = adm_populate_channel_weight(&adm_pspd_params[index],
ch_mixer, channel_index);
- if (!ret) {
+ if (ret) {
pr_err("%s: fail to get channel weight with error %d\n",
__func__, ret);
goto fail_cmd;
@@ -1389,7 +1389,7 @@
*/
if ((payload_size >= struct_size + data_size) &&
(ARRAY_SIZE(adm_get_parameters) > idx) &&
- (ARRAY_SIZE(adm_get_parameters) >= idx + 1 + data_size)) {
+ (ARRAY_SIZE(adm_get_parameters) > idx + 1 + data_size)) {
pr_debug("%s: Received parameter data in band\n",
__func__);
/*
diff --git a/dsp/q6afe.c b/dsp/q6afe.c
index 0d5721e..2810beb 100644
--- a/dsp/q6afe.c
+++ b/dsp/q6afe.c
@@ -3541,6 +3541,10 @@
media_type.sample_rate =
cfg->data.aac_config.sample_rate;
break;
+ case ASM_MEDIA_FMT_SBC:
+ media_type.sample_rate =
+ cfg->data.sbc_config.sample_rate;
+ break;
default:
media_type.sample_rate =
afe_config.slim_sch.sample_rate;
diff --git a/dsp/q6core.c b/dsp/q6core.c
index f66fc84..d4ea984 100644
--- a/dsp/q6core.c
+++ b/dsp/q6core.c
@@ -32,7 +32,7 @@
#define TIMEOUT_MS 1000
/*
- * AVS bring up in the modem is optimitized for the new
+ * AVS bring up in the modem is optimized for the new
* Sub System Restart design and 100 milliseconds timeout
* is sufficient to make sure the Q6 will be ready.
*/
@@ -67,6 +67,8 @@
wait_queue_head_t mdf_map_resp_wait;
wait_queue_head_t cmd_req_wait;
wait_queue_head_t avcs_fwk_ver_req_wait;
+ wait_queue_head_t lpass_npa_rsc_wait;
+ u32 lpass_npa_rsc_rsp_rcvd;
u32 bus_bw_resp_received;
u32 mdf_map_resp_received;
enum cmd_flags {
@@ -84,6 +86,7 @@
struct cal_type_data *cal_data[CORE_MAX_CAL];
uint32_t mem_map_cal_handle;
uint32_t mdf_mem_map_cal_handle;
+ uint32_t npa_client_handle;
int32_t adsp_status;
int32_t avs_state;
struct q6core_avcs_ver_info q6core_avcs_ver_info;
@@ -321,6 +324,15 @@
"AVCS_CMD_UNLOAD_TOPO_MODULES",
adsp_err_get_err_str(payload1[1]));
break;
+ case AVCS_CMD_DESTROY_LPASS_NPA_CLIENT:
+ case AVCS_CMD_REQUEST_LPASS_NPA_RESOURCES:
+ pr_debug("%s: Cmd = AVCS_CMD_CREATE_LPASS_NPA_CLIENT/AVCS_CMD_DESTROY_LPASS_NPA_CLIENT status[%s]\n",
+ __func__, adsp_err_get_err_str(payload1[1]));
+ /* ADSP status to match Linux error standard */
+ q6core_lcl.adsp_status = -payload1[1];
+ q6core_lcl.lpass_npa_rsc_rsp_rcvd = 1;
+ wake_up(&q6core_lcl.lpass_npa_rsc_wait);
+ break;
default:
pr_err("%s: Invalid cmd rsp[0x%x][0x%x] opcode %d\n",
__func__,
@@ -355,6 +367,15 @@
wake_up(&q6core_lcl.bus_bw_req_wait);
}
break;
+ case AVCS_CMDRSP_CREATE_LPASS_NPA_CLIENT:
+ payload1 = data->payload;
+ pr_debug("%s: AVCS_CMDRSP_CREATE_LPASS_NPA_CLIENT handle %d\n",
+ __func__, payload1[1]);
+ q6core_lcl.adsp_status = payload1[0];
+ q6core_lcl.npa_client_handle = payload1[1];
+ q6core_lcl.lpass_npa_rsc_rsp_rcvd = 1;
+ wake_up(&q6core_lcl.lpass_npa_rsc_wait);
+ break;
case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
payload1 = data->payload;
q6core_lcl.param = payload1[0];
@@ -933,6 +954,201 @@
}
EXPORT_SYMBOL(q6core_is_adsp_ready);
+int q6core_create_lpass_npa_client(uint32_t node_id, char *client_name,
+ uint32_t *client_handle)
+{
+ struct avcs_cmd_create_lpass_npa_client_t create_lpass_npa_client;
+ struct avcs_cmd_create_lpass_npa_client_t *cmd_ptr =
+ &create_lpass_npa_client;
+ int ret = 0;
+
+ if (!client_name) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&(q6core_lcl.cmd_lock));
+
+ memset(cmd_ptr, 0, sizeof(create_lpass_npa_client));
+
+ cmd_ptr->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cmd_ptr->hdr.pkt_size = sizeof(create_lpass_npa_client);
+ cmd_ptr->hdr.src_port = 0;
+ cmd_ptr->hdr.dest_port = 0;
+ cmd_ptr->hdr.token = 0;
+ cmd_ptr->hdr.opcode = AVCS_CMD_CREATE_LPASS_NPA_CLIENT;
+ cmd_ptr->node_id = AVCS_SLEEP_ISLAND_CORE_DRIVER_NODE_ID;
+ strlcpy(cmd_ptr->client_name, client_name,
+ sizeof(cmd_ptr->client_name));
+
+ pr_debug("%s: create lpass npa client opcode[0x%x] node id[0x%x]\n",
+ __func__, cmd_ptr->hdr.opcode, cmd_ptr->node_id);
+
+ *client_handle = 0;
+ q6core_lcl.adsp_status = 0;
+ q6core_lcl.lpass_npa_rsc_rsp_rcvd = 0;
+ ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) cmd_ptr);
+ if (ret < 0) {
+ pr_err("%s: create lpass npa client failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(q6core_lcl.lpass_npa_rsc_wait,
+ (q6core_lcl.lpass_npa_rsc_rsp_rcvd == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: timeout. waited for create lpass npa rsc client\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ goto done;
+ } else {
+ /* set ret to 0 as no timeout happened */
+ ret = 0;
+ }
+
+ if (q6core_lcl.adsp_status < 0) {
+ pr_err("%s: DSP returned error %d\n",
+ __func__, q6core_lcl.adsp_status);
+ ret = q6core_lcl.adsp_status;
+ goto done;
+ }
+
+ *client_handle = q6core_lcl.npa_client_handle;
+ pr_debug("%s: q6core_lcl.npa_client_handle %d\n", __func__,
+ q6core_lcl.npa_client_handle);
+done:
+ mutex_unlock(&q6core_lcl.cmd_lock);
+ return ret;
+}
+EXPORT_SYMBOL(q6core_create_lpass_npa_client);
+
+int q6core_destroy_lpass_npa_client(uint32_t client_handle)
+{
+ struct avcs_cmd_destroy_lpass_npa_client_t destroy_lpass_npa_client;
+ struct avcs_cmd_destroy_lpass_npa_client_t *cmd_ptr =
+ &destroy_lpass_npa_client;
+ int ret = 0;
+
+ mutex_lock(&(q6core_lcl.cmd_lock));
+
+ memset(cmd_ptr, 0, sizeof(destroy_lpass_npa_client));
+
+ cmd_ptr->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cmd_ptr->hdr.pkt_size = sizeof(destroy_lpass_npa_client);
+ cmd_ptr->hdr.src_port = 0;
+ cmd_ptr->hdr.dest_port = 0;
+ cmd_ptr->hdr.token = 0;
+ cmd_ptr->hdr.opcode = AVCS_CMD_DESTROY_LPASS_NPA_CLIENT;
+ cmd_ptr->client_handle = client_handle;
+
+ pr_debug("%s: dstry lpass npa client opcode[0x%x] client hdl[0x%x]\n",
+ __func__, cmd_ptr->hdr.opcode, cmd_ptr->client_handle);
+
+ q6core_lcl.adsp_status = 0;
+ q6core_lcl.lpass_npa_rsc_rsp_rcvd = 0;
+ ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) cmd_ptr);
+ if (ret < 0) {
+ pr_err("%s: destroy lpass npa client failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(q6core_lcl.lpass_npa_rsc_wait,
+ (q6core_lcl.lpass_npa_rsc_rsp_rcvd == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: timeout. waited for destroy lpass npa rsc client\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ goto done;
+ } else {
+ /* set ret to 0 as no timeout happened */
+ ret = 0;
+ }
+
+ if (q6core_lcl.adsp_status < 0) {
+ pr_err("%s: DSP returned error %d\n",
+ __func__, q6core_lcl.adsp_status);
+ ret = q6core_lcl.adsp_status;
+ }
+done:
+ mutex_unlock(&q6core_lcl.cmd_lock);
+ return ret;
+}
+EXPORT_SYMBOL(q6core_destroy_lpass_npa_client);
+
+int q6core_request_island_transition(uint32_t client_handle,
+ uint32_t island_allow_mode)
+{
+ struct avcs_sleep_node_island_transition_config_t island_tsn_cfg;
+ struct avcs_sleep_node_island_transition_config_t *cmd_ptr =
+ &island_tsn_cfg;
+ int ret = 0;
+
+ mutex_lock(&(q6core_lcl.cmd_lock));
+
+ memset(cmd_ptr, 0, sizeof(island_tsn_cfg));
+
+ cmd_ptr->req_lpass_npa_rsc.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cmd_ptr->req_lpass_npa_rsc.hdr.pkt_size = sizeof(island_tsn_cfg);
+ cmd_ptr->req_lpass_npa_rsc.hdr.src_port = 0;
+ cmd_ptr->req_lpass_npa_rsc.hdr.dest_port = 0;
+ cmd_ptr->req_lpass_npa_rsc.hdr.token = 0;
+ cmd_ptr->req_lpass_npa_rsc.hdr.opcode =
+ AVCS_CMD_REQUEST_LPASS_NPA_RESOURCES;
+ cmd_ptr->req_lpass_npa_rsc.client_handle = client_handle;
+ cmd_ptr->req_lpass_npa_rsc.resource_id =
+ AVCS_SLEEP_NODE_ISLAND_TRANSITION_RESOURCE_ID;
+ cmd_ptr->island_allow_mode = island_allow_mode;
+
+ pr_debug("%s: req islnd tnsn opcode[0x%x] island_allow_mode[0x%x]\n",
+ __func__, cmd_ptr->req_lpass_npa_rsc.hdr.opcode,
+ cmd_ptr->island_allow_mode);
+
+ q6core_lcl.adsp_status = 0;
+ q6core_lcl.lpass_npa_rsc_rsp_rcvd = 0;
+ ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) cmd_ptr);
+ if (ret < 0) {
+ pr_err("%s: island tnsn cmd send failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(q6core_lcl.lpass_npa_rsc_wait,
+ (q6core_lcl.lpass_npa_rsc_rsp_rcvd == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: timeout. waited for island lpass npa rsc req\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ goto done;
+ } else {
+ /* set ret to 0 as no timeout happened */
+ ret = 0;
+ }
+
+ if (q6core_lcl.adsp_status < 0) {
+ pr_err("%s: DSP returned error %d\n",
+ __func__, q6core_lcl.adsp_status);
+ ret = q6core_lcl.adsp_status;
+ }
+done:
+ mutex_unlock(&q6core_lcl.cmd_lock);
+ return ret;
+}
+EXPORT_SYMBOL(q6core_request_island_transition);
+
int q6core_map_memory_regions(phys_addr_t *buf_add, uint32_t mempool_id,
uint32_t *bufsz, uint32_t bufcnt, uint32_t *map_handle)
{
@@ -1671,6 +1887,7 @@
init_waitqueue_head(&q6core_lcl.cmd_req_wait);
init_waitqueue_head(&q6core_lcl.avcs_fwk_ver_req_wait);
init_waitqueue_head(&q6core_lcl.mdf_map_resp_wait);
+ init_waitqueue_head(&q6core_lcl.lpass_npa_rsc_wait);
q6core_lcl.cmd_resp_received_flag = FLAG_NONE;
mutex_init(&q6core_lcl.cmd_lock);
mutex_init(&q6core_lcl.ver_lock);
diff --git a/include/asoc/wcd9xxx_registers.h b/include/asoc/wcd9xxx_registers.h
new file mode 100644
index 0000000..928dfcc
--- /dev/null
+++ b/include/asoc/wcd9xxx_registers.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WCD9XXX_REGISTERS_H
+#define _WCD9XXX_REGISTERS_H
+
+#define WCD9XXX_BASE_ADDRESS 0x3000
+
+#define WCD9XXX_ANA_RX_SUPPLIES (WCD9XXX_BASE_ADDRESS+0x008)
+#define WCD9XXX_ANA_HPH (WCD9XXX_BASE_ADDRESS+0x009)
+#define WCD9XXX_CLASSH_MODE_2 (WCD9XXX_BASE_ADDRESS+0x098)
+#define WCD9XXX_CLASSH_MODE_3 (WCD9XXX_BASE_ADDRESS+0x099)
+#define WCD9XXX_FLYBACK_VNEG_CTRL_4 (WCD9XXX_BASE_ADDRESS+0x0A8)
+#define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2 (WCD9XXX_BASE_ADDRESS+0x0AF)
+#define WCD9XXX_RX_BIAS_HPH_LOWPOWER (WCD9XXX_BASE_ADDRESS+0x0BF)
+#define WCD9XXX_RX_BIAS_FLYB_BUFF (WCD9XXX_BASE_ADDRESS+0x0C7)
+#define WCD9XXX_HPH_PA_CTL1 (WCD9XXX_BASE_ADDRESS+0x0D1)
+#define WCD9XXX_HPH_NEW_INT_PA_MISC2 (WCD9XXX_BASE_ADDRESS+0x138)
+
+#endif
diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h
index eb529dc..9ac7281 100644
--- a/include/dsp/apr_audio-v2.h
+++ b/include/dsp/apr_audio-v2.h
@@ -4311,9 +4311,23 @@
* #ASM_MEDIA_FMT_SBC media format.
*/
struct asm_sbc_dec_cfg_t {
- /* All configuration is extracted from the stream */
-} __packed;
+ uint16_t channels;
+ /*
+ * Number of channels present in the SBC stream.
+ *
+ * @values
+ * - 1 -- Mono
+ * - 2 -- Stereo
+ */
+ uint32_t sample_rate;
+ /*
+ * Number of samples per second.
+ *
+ * @values 8000, 11025, 12000, 16000, 22050, 24000, 32000,
+ * 44100, 48000, 64000, 88200, 96000 Hz
+ */
+} __packed;
/*
* Payload of the MP3 decoder configuration parameters in the
* #ASM_MEDIA_FMT_MP3 media format.
diff --git a/include/dsp/q6core.h b/include/dsp/q6core.h
index 1fa0d1e..e1f8b97 100644
--- a/include/dsp/q6core.h
+++ b/include/dsp/q6core.h
@@ -19,6 +19,8 @@
#define AVCS_CMD_ADSP_EVENT_GET_STATE 0x0001290C
#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE 0x0001290D
+#define AVCS_API_VERSION_V4 4
+#define APRV2_IDS_SERVICE_ID_ADSP_CORE_V (0x3)
bool q6core_is_adsp_ready(void);
@@ -150,7 +152,7 @@
} __packed;
-#define AVCS_CMD_DEREGISTER_TOPOLOGIES 0x0001292a
+#define AVCS_CMD_DEREGISTER_TOPOLOGIES 0x0001292a
/* The payload for the AVCS_CMD_DEREGISTER_TOPOLOGIES command */
struct avcs_cmd_deregister_topologies {
@@ -195,6 +197,99 @@
uint32_t topology_id;
} __packed;
+/* This command allows a remote client(HLOS) creates a client to LPASS NPA
+ * resource node. Currently, this command supports only the NPA Sleep resource
+ * "/island/core/drivers" node ID.
+ */
+#define AVCS_CMD_CREATE_LPASS_NPA_CLIENT 0x00012985
+
+#define AVCS_SLEEP_ISLAND_CORE_DRIVER_NODE_ID 0x00000001
+
+struct avcs_cmd_create_lpass_npa_client_t {
+ struct apr_hdr hdr;
+ uint32_t node_id;
+ /* Unique ID of the NPA node.
+ * @values
+ * - #AVCS_SLEEP_ISLAND_CORE_DRIVER_NODE_ID
+ */
+
+ char client_name[16];
+ /* Client name, up to a maximum of sixteen characters.*/
+};
+
+/* In response to the #AVCS_CMD_CREATE_LPASS_NPA_CLIENT command, the AVCS
+ * returns the handle to remote HLOS client.
+ */
+#define AVCS_CMDRSP_CREATE_LPASS_NPA_CLIENT 0x00012986
+
+struct avcs_cmdrsp_create_lpass_npa_client_t {
+ uint32_t status;
+ /* Status message (error code).
+ * @values
+ * - ADSP_EOK -- Create was successful
+ * - ADSP_EFAILED -- Create failed
+ */
+
+ uint32_t client_handle;
+ /* Handle of the client.*/
+};
+
+/* The remote HLOS client use this command to issue the request to the npa
+ * resource. Remote client(HLOS) must send the valid npa client handle and
+ * resource id info.
+ */
+#define AVCS_CMD_REQUEST_LPASS_NPA_RESOURCES 0x00012987
+
+#define AVCS_SLEEP_NODE_ISLAND_TRANSITION_RESOURCE_ID 0x00000001
+
+#define SLEEP_RESTRICT_ISLAND 0x0
+#define SLEEP_ALLOW_ISLAND 0x1
+
+/* Immediately following this structure is the resource request configuration
+ * data payload. Payload varies depend on the resource_id requested.
+ * Currently supported only island transition payload.
+ */
+struct avcs_cmd_request_lpass_npa_resources_t {
+ struct apr_hdr hdr;
+ uint32_t client_handle;
+ /* Handle of the client.
+ * @values
+ * - Valid uint32 number
+ */
+
+ uint32_t resource_id;
+ /* Unique ID of the NPA resource ID.
+ * @values
+ * - #AVCS_SLEEP_NODE_ISLAND_TRANSITION_RESOURCE_ID
+ */
+};
+
+/* This structure contains the sleep node resource payload data.
+ */
+struct avcs_sleep_node_island_transition_config_t {
+ struct avcs_cmd_request_lpass_npa_resources_t req_lpass_npa_rsc;
+ uint32_t island_allow_mode;
+ /* Specifies the island state.
+ * @values
+ * - #SLEEP_RESTRICT_ISLAND
+ * - #SLEEP_ALLOW_ISLAND
+ */
+};
+
+/* This command allows remote client(HLOS) to destroy the npa node client
+ * handle, which is created using the #AVCS_CMD_CREATE_LPASS_NPA_CLIENT command.
+ * Remote client(HLOS) must send the valid npa client handle.
+ */
+#define AVCS_CMD_DESTROY_LPASS_NPA_CLIENT 0x00012988
+
+struct avcs_cmd_destroy_lpass_npa_client_t {
+ struct apr_hdr hdr;
+ uint32_t client_handle;
+ /* Handle of the client.
+ * @values
+ * - Valid uint32 number
+ */
+};
int q6core_map_memory_regions(phys_addr_t *buf_add, uint32_t mempool_id,
uint32_t *bufsz, uint32_t bufcnt, uint32_t *map_handle);
@@ -212,6 +307,12 @@
int32_t q6core_load_unload_topo_modules(uint32_t topology_id,
bool preload_type);
+int q6core_create_lpass_npa_client(uint32_t node_id, char *client_name,
+ uint32_t *client_handle);
+int q6core_destroy_lpass_npa_client(uint32_t client_handle);
+int q6core_request_island_transition(uint32_t client_handle,
+ uint32_t island_allow_mode);
+
#if IS_ENABLED(CONFIG_USE_Q6_32CH_SUPPORT)
static inline bool q6core_use_Q6_32ch_support(void)
{
diff --git a/include/soc/swr-wcd.h b/include/soc/swr-wcd.h
index 8ea8616..7eaad27 100644
--- a/include/soc/swr-wcd.h
+++ b/include/soc/swr-wcd.h
@@ -26,6 +26,7 @@
SWR_CLK_FREQ,
SWR_DEVICE_SSR_DOWN,
SWR_DEVICE_SSR_UP,
+ SWR_REGISTER_WAKE_IRQ,
};
struct swr_mstr_port {
diff --git a/soc/pinctrl-lpi.c b/soc/pinctrl-lpi.c
index 21d51fb..f6628bb 100644
--- a/soc/pinctrl-lpi.c
+++ b/soc/pinctrl-lpi.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -21,12 +21,16 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/clk.h>
#include <soc/snd_event.h>
+#include <linux/pm_runtime.h>
#include <dsp/audio_notifier.h>
#include "core.h"
#include "pinctrl-utils.h"
+#define LPI_AUTO_SUSPEND_DELAY 1500 /* delay in msec */
+
#define LPI_ADDRESS_SIZE 0x20000
#define LPI_GPIO_REG_VAL_CTL 0x00
@@ -97,6 +101,7 @@
struct pinctrl_dev *ctrl;
struct gpio_chip chip;
char __iomem *base;
+ struct clk *lpass_npa_rsc_island;
};
static const char *const lpi_gpio_groups[] = {
@@ -128,11 +133,14 @@
__func__);
return 0;
}
+ pm_runtime_get_sync(lpi_dev);
ret = ioread32(pad->base + pad->offset + addr);
if (ret < 0)
pr_err("%s: read 0x%x failed\n", __func__, addr);
+ pm_runtime_mark_last_busy(lpi_dev);
+ pm_runtime_put_autosuspend(lpi_dev);
return ret;
}
@@ -144,8 +152,12 @@
__func__);
return 0;
}
+ pm_runtime_get_sync(lpi_dev);
iowrite32(val, pad->base + pad->offset + addr);
+
+ pm_runtime_mark_last_busy(lpi_dev);
+ pm_runtime_put_autosuspend(lpi_dev);
return 0;
}
@@ -499,6 +511,7 @@
int ret, npins, i;
char __iomem *lpi_base;
u32 reg;
+ struct clk *lpass_npa_rsc_island = NULL;
ret = of_property_read_u32(dev->of_node, "reg", ®);
if (ret < 0) {
@@ -608,6 +621,22 @@
goto err_snd_evt;
}
+ /* Register LPASS NPA resource */
+ lpass_npa_rsc_island = devm_clk_get(&pdev->dev, "island_lpass_npa_rsc");
+ if (IS_ERR(lpass_npa_rsc_island)) {
+ ret = PTR_ERR(lpass_npa_rsc_island);
+ dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
+ __func__, "island_lpass_npa_rsc", ret);
+ lpass_npa_rsc_island = NULL;
+ ret = 0;
+ }
+ state->lpass_npa_rsc_island = lpass_npa_rsc_island;
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, LPI_AUTO_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
return 0;
err_snd_evt:
@@ -621,6 +650,8 @@
static int lpi_pinctrl_remove(struct platform_device *pdev)
{
struct lpi_gpio_state *state = platform_get_drvdata(pdev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
snd_event_client_deregister(&pdev->dev);
audio_notifier_deregister("lpi_tlmm");
@@ -635,9 +666,49 @@
MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match);
+int lpi_pinctrl_runtime_resume(struct device *dev)
+{
+ struct lpi_gpio_state *state = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (state->lpass_npa_rsc_island == NULL) {
+ dev_dbg(dev, "%s: Invalid lpass npa rsc node\n", __func__);
+ return 0;
+ }
+
+ ret = clk_prepare_enable(state->lpass_npa_rsc_island);
+ if (ret < 0) {
+ dev_err(dev, "%s:lpass npa rsc island enable failed\n",
+ __func__);
+ }
+ pm_runtime_set_autosuspend_delay(dev, LPI_AUTO_SUSPEND_DELAY);
+ return 0;
+}
+
+int lpi_pinctrl_runtime_suspend(struct device *dev)
+{
+ struct lpi_gpio_state *state = dev_get_drvdata(dev);
+
+ if (state->lpass_npa_rsc_island == NULL) {
+ dev_dbg(dev, "%s: Invalid lpass npa rsc node\n", __func__);
+ return 0;
+ }
+ clk_disable_unprepare(state->lpass_npa_rsc_island);
+ return 0;
+}
+
+static const struct dev_pm_ops lpi_pinctrl_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(
+ lpi_pinctrl_runtime_suspend,
+ lpi_pinctrl_runtime_resume,
+ NULL
+ )
+};
+
static struct platform_driver lpi_pinctrl_driver = {
.driver = {
.name = "qcom-lpi-pinctrl",
+ .pm = &lpi_pinctrl_dev_pm_ops,
.of_match_table = lpi_pinctrl_of_match,
},
.probe = lpi_pinctrl_probe,
diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c
index df373ab..efb20f7 100644
--- a/soc/swr-mstr-ctrl.c
+++ b/soc/swr-mstr-ctrl.c
@@ -41,6 +41,8 @@
((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24))
#define SWR_INVALID_PARAM 0xFF
+#define SWR_HSTOP_MAX_VAL 0xF
+#define SWR_HSTART_MIN_VAL 0x0
#define SWRM_INTERRUPT_STATUS_MASK 0x1FDFD
/* pm runtime auto suspend timer in msecs */
@@ -534,6 +536,10 @@
dev_err(&master->dev, "%s: swrm is NULL\n", __func__);
return -EINVAL;
}
+ if (!dev_num) {
+ dev_err(&master->dev, "%s: invalid slave dev num\n", __func__);
+ return -EINVAL;
+ }
mutex_lock(&swrm->devlock);
if (!swrm->dev_up) {
mutex_unlock(&swrm->devlock);
@@ -542,11 +548,7 @@
mutex_unlock(&swrm->devlock);
pm_runtime_get_sync(swrm->dev);
- if (dev_num)
- ret = swrm_cmd_fifo_rd_cmd(swrm, &val, dev_num, 0, reg_addr,
- len);
- else
- val = swr_master_read(swrm, reg_addr);
+ ret = swrm_cmd_fifo_rd_cmd(swrm, &val, dev_num, 0, reg_addr, len);
if (!ret)
*reg_val = (u8)val;
@@ -567,6 +569,10 @@
dev_err(&master->dev, "%s: swrm is NULL\n", __func__);
return -EINVAL;
}
+ if (!dev_num) {
+ dev_err(&master->dev, "%s: invalid slave dev num\n", __func__);
+ return -EINVAL;
+ }
mutex_lock(&swrm->devlock);
if (!swrm->dev_up) {
mutex_unlock(&swrm->devlock);
@@ -575,10 +581,7 @@
mutex_unlock(&swrm->devlock);
pm_runtime_get_sync(swrm->dev);
- if (dev_num)
- ret = swrm_cmd_fifo_wr_cmd(swrm, reg_val, dev_num, 0, reg_addr);
- else
- swr_master_write(swrm, reg_addr, reg_val);
+ ret = swrm_cmd_fifo_wr_cmd(swrm, reg_val, dev_num, 0, reg_addr);
pm_runtime_put_autosuspend(swrm->dev);
pm_runtime_mark_last_busy(swrm->dev);
@@ -926,6 +929,10 @@
reg[len] = SWRM_DP_PORT_HCTRL_BANK(i + 1, bank);
hparams = (mport->hstop << 4) | mport->hstart;
val[len++] = hparams;
+ } else {
+ reg[len] = SWRM_DP_PORT_HCTRL_BANK(i + 1, bank);
+ hparams = (SWR_HSTOP_MAX_VAL << 4) | SWR_HSTART_MIN_VAL;
+ val[len++] = hparams;
}
if (mport->blk_pack_mode != SWR_INVALID_PARAM) {
reg[len] = SWRM_DP_BLOCK_CTRL3_BANK(i + 1, bank);
@@ -1414,6 +1421,32 @@
return ret;
}
+static irqreturn_t swrm_wakeup_interrupt(int irq, void *dev)
+{
+ struct swr_mstr_ctrl *swrm = dev;
+ int ret = IRQ_HANDLED;
+
+ if (!swrm || !(swrm->dev)) {
+ pr_err("%s: swrm or dev is null\n", __func__);
+ return IRQ_NONE;
+ }
+ mutex_lock(&swrm->devlock);
+ if (!swrm->dev_up) {
+ if (swrm->wake_irq > 0)
+ disable_irq_nosync(swrm->wake_irq);
+ mutex_unlock(&swrm->devlock);
+ return ret;
+ }
+ mutex_unlock(&swrm->devlock);
+ if (swrm->wake_irq > 0)
+ disable_irq_nosync(swrm->wake_irq);
+ pm_runtime_get_sync(swrm->dev);
+ pm_runtime_mark_last_busy(swrm->dev);
+ pm_runtime_put_autosuspend(swrm->dev);
+
+ return ret;
+}
+
static void swrm_wakeup_work(struct work_struct *work)
{
struct swr_mstr_ctrl *swrm;
@@ -1585,8 +1618,8 @@
schedule_work(&(swrm->dc_presence_work));
break;
case SWR_WAKE_IRQ_EVENT:
- if (swrm->wakeup_req && !swrm->wakeup_triggered) {
- swrm->wakeup_triggered = true;
+ if (swrm->ipc_wakeup && !swrm->ipc_wakeup_triggered) {
+ swrm->ipc_wakeup_triggered = true;
schedule_work(&swrm->wakeup_work);
}
break;
@@ -1762,6 +1795,8 @@
swrm->mclk_freq = MCLK_FREQ;
swrm->dev_up = true;
swrm->state = SWR_MSTR_UP;
+ swrm->ipc_wakeup = false;
+ swrm->ipc_wakeup_triggered = false;
init_completion(&swrm->reset);
init_completion(&swrm->broadcast);
init_completion(&swrm->clk_off_complete);
@@ -1789,11 +1824,6 @@
}
}
- if (of_property_read_u32(swrm->dev->of_node,
- "qcom,swr-wakeup-required", &swrm->wakeup_req)) {
- swrm->wakeup_req = false;
- }
-
if (swrm->reg_irq) {
ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm,
SWR_IRQ_REGISTER);
@@ -1903,6 +1933,9 @@
swrm, SWR_IRQ_FREE);
else if (swrm->irq)
free_irq(swrm->irq, swrm);
+ else if (swrm->wake_irq > 0)
+ free_irq(swrm->wake_irq, swrm);
+
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
swr_unregister_master(&swrm->master);
@@ -1944,9 +1977,10 @@
if ((swrm->state == SWR_MSTR_DOWN) ||
(swrm->state == SWR_MSTR_SSR && swrm->dev_up)) {
- if (swrm->clk_stop_mode0_supp && swrm->wakeup_req) {
- msm_aud_evt_blocking_notifier_call_chain(
- SWR_WAKE_IRQ_DEREGISTER, (void *)swrm);
+ if (swrm->clk_stop_mode0_supp) {
+ if (swrm->ipc_wakeup)
+ msm_aud_evt_blocking_notifier_call_chain(
+ SWR_WAKE_IRQ_DEREGISTER, (void *)swrm);
}
if (swrm_clk_request(swrm, true))
@@ -2025,10 +2059,14 @@
}
swrm_clk_request(swrm, false);
- if (swrm->clk_stop_mode0_supp && swrm->wakeup_req) {
- msm_aud_evt_blocking_notifier_call_chain(
- SWR_WAKE_IRQ_REGISTER, (void *)swrm);
- swrm->wakeup_triggered = false;
+ if (swrm->clk_stop_mode0_supp) {
+ if (swrm->wake_irq > 0) {
+ enable_irq(swrm->wake_irq);
+ } else if (swrm->ipc_wakeup) {
+ msm_aud_evt_blocking_notifier_call_chain(
+ SWR_WAKE_IRQ_REGISTER, (void *)swrm);
+ swrm->ipc_wakeup_triggered = false;
+ }
}
}
@@ -2064,6 +2102,35 @@
return 0;
}
+int swrm_register_wake_irq(struct swr_mstr_ctrl *swrm)
+{
+ int ret = 0;
+
+ if (!swrm->ipc_wakeup) {
+ swrm->wake_irq = platform_get_irq_byname(swrm->pdev,
+ "swr_wake_irq");
+ if (swrm->wake_irq < 0) {
+ dev_err(swrm->dev,
+ "%s() error getting wake irq handle: %d\n",
+ __func__, swrm->wake_irq);
+ return -EINVAL;
+ }
+
+ ret = request_threaded_irq(swrm->wake_irq, NULL,
+ swrm_wakeup_interrupt,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "swr_wake_irq", swrm);
+ if (ret) {
+ dev_err(swrm->dev, "%s: Failed to request irq %d\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+ /* Disable wake irq - enable it after clock stop */
+ disable_irq(swrm->wake_irq);
+ }
+ return ret;
+}
+
/**
* swrm_wcd_notify - parent device can notify to soundwire master through
* this function
@@ -2110,9 +2177,10 @@
break;
case SWR_DEVICE_SSR_UP:
/* wait for clk voting to be zero */
+ reinit_completion(&swrm->clk_off_complete);
if (swrm->clk_ref_count &&
!wait_for_completion_timeout(&swrm->clk_off_complete,
- (1 * HZ/100)))
+ msecs_to_jiffies(200)))
dev_err(swrm->dev, "%s: clock voting not zero\n",
__func__);
@@ -2132,6 +2200,13 @@
break;
case SWR_DEVICE_UP:
dev_dbg(swrm->dev, "%s: swr master up called\n", __func__);
+ mutex_lock(&swrm->devlock);
+ if (!swrm->dev_up) {
+ dev_dbg(swrm->dev, "SSR not complete yet\n");
+ mutex_unlock(&swrm->devlock);
+ return -EBUSY;
+ }
+ mutex_unlock(&swrm->devlock);
mutex_lock(&swrm->mlock);
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
@@ -2181,6 +2256,21 @@
mutex_unlock(&swrm->mlock);
}
break;
+ case SWR_REGISTER_WAKE_IRQ:
+ if (!data) {
+ dev_err(swrm->dev, "%s: reg wake irq data is NULL\n",
+ __func__);
+ ret = -EINVAL;
+ } else {
+ mutex_lock(&swrm->mlock);
+ swrm->ipc_wakeup = *(u32 *)data;
+ ret = swrm_register_wake_irq(swrm);
+ if (ret)
+ dev_err(swrm->dev, "%s: register wake_irq failed\n",
+ __func__);
+ mutex_unlock(&swrm->mlock);
+ }
+ break;
default:
dev_err(swrm->dev, "%s: swr master unknown id %d\n",
__func__, id);
diff --git a/soc/swr-mstr-ctrl.h b/soc/swr-mstr-ctrl.h
index 4a40fa3..b3e358a 100644
--- a/soc/swr-mstr-ctrl.h
+++ b/soc/swr-mstr-ctrl.h
@@ -131,6 +131,7 @@
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
void *data), void *swr_handle, int type);
int irq;
+ int wake_irq;
int version;
int mclk_freq;
u32 num_dev;
@@ -152,9 +153,9 @@
int swr_irq;
u32 clk_stop_mode0_supp;
struct work_struct wakeup_work;
- u32 wakeup_req;
+ u32 ipc_wakeup;
bool dev_up;
- bool wakeup_triggered;
+ bool ipc_wakeup_triggered;
};
#endif /* _SWR_WCD_CTRL_H */