Merge "soc: vote for LPASS NPA resource from LPI TLMM driver"

This commit is contained in:
Linux Build Service Account 2018-11-17 09:56:49 -08:00 • committed by Gerrit - the friendly Code Review server
commit d72c877e31
9 changed files with 575 additions and 4 deletions

View file

@ -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;

View file

@ -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"},
{}

View file

@ -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;
}

View file

@ -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;

View file

@ -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,

View file

@ -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:

View file

@ -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);

View file

@ -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)
{

View file

@ -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", &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,