diff --git a/asoc/codecs/audio-ext-clk-up.c b/asoc/codecs/audio-ext-clk-up.c index b6cb2f6b4c5a..ba72fd8f6107 100644 --- a/asoc/codecs/audio-ext-clk-up.c +++ b/asoc/codecs/audio-ext-clk-up.c @@ -21,6 +21,7 @@ #include #include #include +#include #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; diff --git a/asoc/codecs/bolero/bolero-cdc.c b/asoc/codecs/bolero/bolero-cdc.c index a0822ebdec0c..07918a89aa51 100644 --- a/asoc/codecs/bolero/bolero-cdc.c +++ b/asoc/codecs/bolero/bolero-cdc.c @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include #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"}, {} diff --git a/asoc/codecs/bolero/bolero-cdc.h b/asoc/codecs/bolero/bolero-cdc.h index 36d17f5ab521..a00c06530c2e 100644 --- a/asoc/codecs/bolero/bolero-cdc.h +++ b/asoc/codecs/bolero/bolero-cdc.h @@ -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; } diff --git a/asoc/codecs/bolero/internal.h b/asoc/codecs/bolero/internal.h index 0129c395f1b9..947dd546b028 100644 --- a/asoc/codecs/bolero/internal.h +++ b/asoc/codecs/bolero/internal.h @@ -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; diff --git a/asoc/codecs/bolero/va-macro.c b/asoc/codecs/bolero/va-macro.c index 199f75c19075..820c4dfd857b 100644 --- a/asoc/codecs/bolero/va-macro.c +++ b/asoc/codecs/bolero/va-macro.c @@ -20,9 +20,12 @@ #include #include #include +#include #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, diff --git a/asoc/sm6150.c b/asoc/sm6150.c index 307ef0c6321d..65aaa9b84a30 100644 --- a/asoc/sm6150.c +++ b/asoc/sm6150.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #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: diff --git a/dsp/q6core.c b/dsp/q6core.c index f66fc842b52c..d4ea98466308 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 @@ 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); diff --git a/include/dsp/q6core.h b/include/dsp/q6core.h index 1fa0d1e98a9f..e1f8b97dbb90 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 @@ 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) { diff --git a/soc/pinctrl-lpi.c b/soc/pinctrl-lpi.c index 21d51fb0ee22..f6628bb860e4 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 #include #include +#include #include +#include #include #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,