mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-10 22:40:54 -04:00
Merge "soc: vote for LPASS NPA resource from LPI TLMM driver"
This commit is contained in:
commit
d72c877e31
9 changed files with 575 additions and 4 deletions
|
|
@ -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 @@ enum {
|
|||
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 audio_ext_clk_priv {
|
|||
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 @@ static u8 audio_ext_clk_get_parent(struct clk_hw *hw)
|
|||
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 @@ static struct audio_ext_clk audio_clk_array[] = {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.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 @@ static int audio_ref_clk_probe(struct platform_device *pdev)
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -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 @@ static int __bolero_reg_read(struct bolero_priv *priv,
|
|||
"%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 @@ static int __bolero_reg_read(struct bolero_priv *priv,
|
|||
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 @@ static int __bolero_reg_write(struct bolero_priv *priv,
|
|||
"%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 @@ static int __bolero_reg_write(struct bolero_priv *priv,
|
|||
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;
|
||||
}
|
||||
|
|
@ -850,6 +862,7 @@ static int bolero_probe(struct platform_device *pdev)
|
|||
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);
|
||||
|
|
@ -898,6 +911,17 @@ static int bolero_probe(struct platform_device *pdev)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
@ -914,6 +938,41 @@ static int bolero_remove(struct platform_device *pdev)
|
|||
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"},
|
||||
{}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ int bolero_info_create_codec_entry(
|
|||
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,
|
||||
|
|
@ -113,6 +115,11 @@ 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ struct bolero_priv {
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -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 @@ static int va_macro_probe(struct platform_device *pdev)
|
|||
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 @@ static int va_macro_remove(struct platform_device *pdev)
|
|||
|
||||
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 of_device_id va_macro_dt_match[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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 @@ static int msm_int_audrx_init(struct snd_soc_pcm_runtime *rtd)
|
|||
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:
|
||||
|
|
|
|||
219
dsp/q6core.c
219
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 @@ struct q6core_str {
|
|||
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 q6core_str {
|
|||
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 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv)
|
|||
"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 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv)
|
|||
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 @@ bail:
|
|||
}
|
||||
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 @@ int __init core_init(void)
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -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 @@ struct avcs_cmd_register_topologies {
|
|||
} __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 @@ struct avcs_cmd_load_unload_topo_modules {
|
|||
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 core_get_license_status(uint32_t module_id);
|
|||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 lpi_gpio_state {
|
|||
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 @@ static int lpi_gpio_read(struct lpi_gpio_pad *pad, unsigned int addr)
|
|||
__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 @@ static int lpi_gpio_write(struct lpi_gpio_pad *pad, unsigned int addr,
|
|||
__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 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
|
|||
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 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
|
|||
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 @@ err_chip:
|
|||
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 @@ static const struct of_device_id lpi_pinctrl_of_match[] = {
|
|||
|
||||
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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue