[alsa-devel] [PATCH 00/10] ASoC: Intel: Skylake: Add support for D0i3
This series adds support for low power D0i3 mode which lets audio controller perform stream and non streaming usage scenarios while being in lower power D0i3 state.
In this series, first we modify the sst_ipc_tx_message() API to check if the DSP is in lower powered D0i3 mode or not and if so bring it to active before sending any messages. This is followed by a new API sst_ipc_tx_message_nopm() to send message without checking the new PM method.
Then we start adding D0i3 refcounting which decides the entry to D0i3 mode based on capability. This is derived from topology data. The new IPC to enter D0i3 is also required.
Finally we remove unwarranted pci device enabling calls on suspend.
Jayachandran B (3): ASoC: Intel: Skylake: Add D0iX callbacks ASoC: Intel: Skylake: Flush pending D0i3 request on suspend ASoC: Intel: Skylake: remove pci device enabling calls on suspend
Pardha Saradhi K (2): ASoC: Intel: Skylake: Add D0iX IPCs ASoC: Intel: Skylake: Add support for programming D0i3C
Vinod Koul (5): ASoC: Intel: Add check_dsp_lp_on callback on IPC ASoC: Intel: Add sst_ipc_tx_message_nopm ASoC: Intel: Skylake: Add support for LPMode ASoC: Intel: Skylake: Add support for specifying D0i3 configuration ASoC: Intel: Skylake: Add D0i3 mode ref counting
include/uapi/sound/snd_sst_tokens.h | 8 +- sound/soc/intel/common/sst-ipc.c | 27 +++++- sound/soc/intel/common/sst-ipc.h | 4 + sound/soc/intel/skylake/bxt-sst.c | 145 +++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-messages.c | 30 ++++++- sound/soc/intel/skylake/skl-pcm.c | 10 +++ sound/soc/intel/skylake/skl-sst-dsp.h | 12 +++ sound/soc/intel/skylake/skl-sst-ipc.c | 69 +++++++++++++++- sound/soc/intel/skylake/skl-sst-ipc.h | 36 +++++++- sound/soc/intel/skylake/skl-topology.c | 47 +++++++++++ sound/soc/intel/skylake/skl-topology.h | 11 +++ sound/soc/intel/skylake/skl.c | 59 +++++++++++++- sound/soc/intel/skylake/skl.h | 6 ++ 13 files changed, 456 insertions(+), 8 deletions(-)
Some controllers support power modes which can't communicate using IPC.
So add a callback to check and wake DSP before sending IPC and then put to sleep if it is in these power modes.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/common/sst-ipc.c | 19 ++++++++++++++++++- sound/soc/intel/common/sst-ipc.h | 1 + 2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index a12c7bb08d3b..6ac9a8484635 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -190,8 +190,25 @@ static void ipc_tx_msgs(struct kthread_work *work) int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) { - return ipc_tx_message(ipc, header, tx_data, tx_bytes, + int ret; + + /* + * DSP maybe in lower power active state, so + * check if the DSP supports DSP lp On method + * if so invoke that before sending IPC + */ + if (ipc->ops.check_dsp_lp_on) + if (ipc->ops.check_dsp_lp_on(ipc->dsp, true)) + return -EIO; + + ret = ipc_tx_message(ipc, header, tx_data, tx_bytes, rx_data, rx_bytes, 1); + + if (ipc->ops.check_dsp_lp_on) + if (ipc->ops.check_dsp_lp_on(ipc->dsp, false)) + return -EIO; + + return ret; } EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait);
diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h index ceb7e468a3fa..46871a5dff1d 100644 --- a/sound/soc/intel/common/sst-ipc.h +++ b/sound/soc/intel/common/sst-ipc.h @@ -52,6 +52,7 @@ struct sst_plat_ipc_ops { void (*tx_data_copy)(struct ipc_message *, char *, size_t); u64 (*reply_msg_match)(u64 header, u64 *mask); bool (*is_dsp_busy)(struct sst_dsp *dsp); + int (*check_dsp_lp_on)(struct sst_dsp *dsp, bool state); };
/* SST generic IPC data */
Add a IPC API to send IPC without waking up DSP.
This is helpful when the DSP is already in woken up state(not in any low power mode) and an IPC is to be sent.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/common/sst-ipc.c | 8 ++++++++ sound/soc/intel/common/sst-ipc.h | 3 +++ 2 files changed, 11 insertions(+)
diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index 6ac9a8484635..1c9919d8b70a 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -220,6 +220,14 @@ int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, } EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait);
+int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) +{ + return ipc_tx_message(ipc, header, tx_data, tx_bytes, + rx_data, rx_bytes, 1); +} +EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nopm); + struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, u64 header) { diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h index 46871a5dff1d..f4aab1b3789a 100644 --- a/sound/soc/intel/common/sst-ipc.h +++ b/sound/soc/intel/common/sst-ipc.h @@ -82,6 +82,9 @@ int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes);
+int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes); + struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, u64 header);
On Mon, Sep 26, 2016 at 11:05:29AM +0530, Vinod Koul wrote:
Add a IPC API to send IPC without waking up DSP.
This is helpful when the DSP is already in woken up state(not in any low power mode) and an IPC is to be sent.
Why is the IPC API for cases where the DSP needs to be woken not implemented in terms of this one?
On Fri, Oct 28, 2016 at 07:05:34PM +0100, Mark Brown wrote:
On Mon, Sep 26, 2016 at 11:05:29AM +0530, Vinod Koul wrote:
Add a IPC API to send IPC without waking up DSP.
This is helpful when the DSP is already in woken up state(not in any low power mode) and an IPC is to be sent.
Why is the IPC API for cases where the DSP needs to be woken not implemented in terms of this one?
So driver can send an IPC message but it doesn't know if the device is active or in low powered mode, so it checks...
But in case of later, the check yields another IPC message to bring up the DSP, then send the original message. Having same API do both causes circular dependency, so we ended up doing a nopm variant which is used to only wake up or put into low power state.
Thanks
The patch
ASoC: Intel: Add sst_ipc_tx_message_nopm
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 80a0df18191ab50eee7da3cd56cecdd55b7ab148 Mon Sep 17 00:00:00 2001
From: Vinod Koul vinod.koul@intel.com Date: Thu, 3 Nov 2016 17:07:14 +0530 Subject: [PATCH] ASoC: Intel: Add sst_ipc_tx_message_nopm
If the DSP is in low power mode, it needs to be woken up by a "wake" IPC to set it into the D0 state before we can send any other IPC command.
The call flow is that the driver calls sst_ipc_tx_message_wait() to send any IPC and this call checks if the device is in low power mode and in that case we need to send the wake IPC.
So add a new IPC nopm variant which can be called from driver and doesn't check for power state (as we already know that) and avoids circular dependency of again checking power state.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/intel/common/sst-ipc.c | 8 ++++++++ sound/soc/intel/common/sst-ipc.h | 3 +++ 2 files changed, 11 insertions(+)
diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index 45a85190a081..2c1b3159ac1a 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -220,6 +220,14 @@ int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, } EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait);
+int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) +{ + return ipc_tx_message(ipc, header, tx_data, tx_bytes, + rx_data, rx_bytes, 1); +} +EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nopm); + struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, u64 header) { diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h index 46871a5dff1d..f4aab1b3789a 100644 --- a/sound/soc/intel/common/sst-ipc.h +++ b/sound/soc/intel/common/sst-ipc.h @@ -82,6 +82,9 @@ int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes);
+int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes); + struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, u64 header);
From: Pardha Saradhi K pardha.saradhi.kesapragada@intel.com
To set D0iX, driver needs to tell the DSP to go into D0iX mode by sending "Set D0iX IPC". Add support for that IPC.
Signed-off-by: Pardha Saradhi K pardha.saradhi.kesapragada@intel.com Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/skylake/skl-sst-ipc.c | 43 ++++++++++++++++++++++++++++++++++- sound/soc/intel/skylake/skl-sst-ipc.h | 10 ++++++++ 2 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 0bd01e62622c..95120771a4f6 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -172,6 +172,17 @@ << IPC_INITIAL_BLOCK_SHIFT) #define IPC_INITIAL_BLOCK_CLEAR ~(IPC_INITIAL_BLOCK_MASK \ << IPC_INITIAL_BLOCK_SHIFT) +/* Set D0ix IPC extension register */ +#define IPC_D0IX_WAKE_SHIFT 0 +#define IPC_D0IX_WAKE_MASK 0x1 +#define IPC_D0IX_WAKE(x) (((x) & IPC_D0IX_WAKE_MASK) \ + << IPC_D0IX_WAKE_SHIFT) + +#define IPC_D0IX_STREAMING_SHIFT 1 +#define IPC_D0IX_STREAMING_MASK 0x1 +#define IPC_D0IX_STREAMING(x) (((x) & IPC_D0IX_STREAMING_MASK) \ + << IPC_D0IX_STREAMING_SHIFT) +
enum skl_ipc_msg_target { IPC_FW_GEN_MSG = 0, @@ -258,7 +269,8 @@ enum skl_ipc_module_msg { IPC_MOD_LARGE_CONFIG_SET = 4, IPC_MOD_BIND = 5, IPC_MOD_UNBIND = 6, - IPC_MOD_SET_DX = 7 + IPC_MOD_SET_DX = 7, + IPC_MOD_SET_D0IX = 8 };
static void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, @@ -931,3 +943,32 @@ int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, return ret; } EXPORT_SYMBOL_GPL(skl_sst_ipc_load_library); + +int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, struct skl_ipc_d0ix_msg *msg) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MOD_SET_D0IX); + header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MOD_ID(msg->module_id); + + header.extension = IPC_D0IX_WAKE(msg->wake); + header.extension |= IPC_D0IX_STREAMING(msg->streaming); + + dev_dbg(ipc->dev, "In %s primary=%x ext=%x\n", __func__, + header.primary, header.extension); + + /* + * Use the nopm IPC here as we dont want it checking for D0iX + */ + ret = sst_ipc_tx_message_nopm(ipc, *ipc_header, NULL, 0, NULL, 0); + if (ret < 0) + dev_err(ipc->dev, "ipc: set d0ix failed, err %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_set_d0ix); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 0334ed4af031..1ae265d8ee08 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -111,6 +111,13 @@ struct skl_ipc_large_config_msg { u32 param_data_size; };
+struct skl_ipc_d0ix_msg { + u32 module_id; + u32 instance_id; + u8 streaming; + u8 wake; +}; + #define SKL_IPC_BOOT_MSECS 3000
#define SKL_IPC_D3_MASK 0 @@ -155,6 +162,9 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, u8 dma_id, u8 table_id);
+int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, + struct skl_ipc_d0ix_msg *msg); + void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); void skl_ipc_op_int_disable(struct sst_dsp *ctx);
On Mon, Sep 26, 2016 at 11:05:30AM +0530, Vinod Koul wrote:
To set D0iX, driver needs to tell the DSP to go into D0iX mode by sending "Set D0iX IPC". Add support for that IPC.
What is "D0iX"?
On Fri, Oct 28, 2016 at 07:06:28PM +0100, Mark Brown wrote:
On Mon, Sep 26, 2016 at 11:05:30AM +0530, Vinod Koul wrote:
To set D0iX, driver needs to tell the DSP to go into D0iX mode by sending "Set D0iX IPC". Add support for that IPC.
What is "D0iX"?
It is an intermediate state between D0 and D3. It is low powered state where DSP is functional but with reduced functionality to reduce power consumed.
I will add this in changelog
Thanks
The patch
ASoC: Intel: Skylake: Add D0iX IPCs
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 41b7523f192bdf3804e3e18a61f91244e4a0cb25 Mon Sep 17 00:00:00 2001
From: Pardha Saradhi K pardha.saradhi.kesapragada@intel.com Date: Thu, 3 Nov 2016 17:07:15 +0530 Subject: [PATCH] ASoC: Intel: Skylake: Add D0iX IPCs
The audio DSP supports intermediate power states between D0 and D3 states. These states are D0i0 and D0i3 states.
Collectively we refer these two states as D0iX states.
To set or wake up from these states, driver also needs to send an IPC "Set D0iX IPC" before doing anything else.
Add support for this new IPC messages.
Signed-off-by: Pardha Saradhi K pardha.saradhi.kesapragada@intel.com Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/intel/skylake/skl-sst-ipc.c | 43 ++++++++++++++++++++++++++++++++++- sound/soc/intel/skylake/skl-sst-ipc.h | 10 ++++++++ 2 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 797cf4053235..087b8d6e7186 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -172,6 +172,17 @@ << IPC_INITIAL_BLOCK_SHIFT) #define IPC_INITIAL_BLOCK_CLEAR ~(IPC_INITIAL_BLOCK_MASK \ << IPC_INITIAL_BLOCK_SHIFT) +/* Set D0ix IPC extension register */ +#define IPC_D0IX_WAKE_SHIFT 0 +#define IPC_D0IX_WAKE_MASK 0x1 +#define IPC_D0IX_WAKE(x) (((x) & IPC_D0IX_WAKE_MASK) \ + << IPC_D0IX_WAKE_SHIFT) + +#define IPC_D0IX_STREAMING_SHIFT 1 +#define IPC_D0IX_STREAMING_MASK 0x1 +#define IPC_D0IX_STREAMING(x) (((x) & IPC_D0IX_STREAMING_MASK) \ + << IPC_D0IX_STREAMING_SHIFT) +
enum skl_ipc_msg_target { IPC_FW_GEN_MSG = 0, @@ -258,7 +269,8 @@ enum skl_ipc_module_msg { IPC_MOD_LARGE_CONFIG_SET = 4, IPC_MOD_BIND = 5, IPC_MOD_UNBIND = 6, - IPC_MOD_SET_DX = 7 + IPC_MOD_SET_DX = 7, + IPC_MOD_SET_D0IX = 8 };
static void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, @@ -931,3 +943,32 @@ int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, return ret; } EXPORT_SYMBOL_GPL(skl_sst_ipc_load_library); + +int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, struct skl_ipc_d0ix_msg *msg) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MOD_SET_D0IX); + header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MOD_ID(msg->module_id); + + header.extension = IPC_D0IX_WAKE(msg->wake); + header.extension |= IPC_D0IX_STREAMING(msg->streaming); + + dev_dbg(ipc->dev, "In %s primary=%x ext=%x\n", __func__, + header.primary, header.extension); + + /* + * Use the nopm IPC here as we dont want it checking for D0iX + */ + ret = sst_ipc_tx_message_nopm(ipc, *ipc_header, NULL, 0, NULL, 0); + if (ret < 0) + dev_err(ipc->dev, "ipc: set d0ix failed, err %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_set_d0ix); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 0334ed4af031..1ae265d8ee08 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -111,6 +111,13 @@ struct skl_ipc_large_config_msg { u32 param_data_size; };
+struct skl_ipc_d0ix_msg { + u32 module_id; + u32 instance_id; + u8 streaming; + u8 wake; +}; + #define SKL_IPC_BOOT_MSECS 3000
#define SKL_IPC_D3_MASK 0 @@ -155,6 +162,9 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, u8 dma_id, u8 table_id);
+int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, + struct skl_ipc_d0ix_msg *msg); + void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); void skl_ipc_op_int_disable(struct sst_dsp *ctx);
From: Pardha Saradhi K pardha.saradhi.kesapragada@intel.com
To set the controller in D0i3 mode, the driver needs to set D0i3C register after DSP is quiesced. Since the D0iX entry/exit is done by IPC, add this as callback so that it can be invoked from IPC module.
Signed-off-by: Pardha Saradhi K pardha.saradhi.kesapragada@intel.com Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/skylake/skl-pcm.c | 1 + sound/soc/intel/skylake/skl-sst-ipc.h | 3 +++ sound/soc/intel/skylake/skl.c | 47 +++++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.h | 5 ++++ 4 files changed, 56 insertions(+)
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index c7cdcba04c5d..792b989733bf 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1197,6 +1197,7 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform) return ret; } skl_populate_modules(skl); + skl->skl_sst->update_d0i3c = skl_update_d0i3c; } pm_runtime_mark_last_busy(platform->dev); pm_runtime_put_autosuspend(platform->dev); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 1ae265d8ee08..ef2182d21934 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -83,6 +83,9 @@ struct skl_sst {
/* tplg manifest */ struct skl_dfw_manifest manifest; + + /* Callback to update D0i3C register */ + void (*update_d0i3c)(struct device *dev, bool enable); };
struct skl_ipc_init_instance_msg { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 2989c164dafe..b9209af89915 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -26,6 +26,7 @@ #include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/firmware.h> +#include <linux/delay.h> #include <sound/pcm.h> #include "../common/sst-acpi.h" #include <sound/hda_register.h> @@ -109,6 +110,52 @@ static int skl_init_chip(struct hdac_bus *bus, bool full_reset) return ret; }
+void skl_update_d0i3c(struct device *dev, bool enable) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct hdac_bus *bus = ebus_to_hbus(ebus); + u8 reg; + int timeout = 50; + + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + /* Do not write to D0I3C until command in progress bit is cleared */ + while ((reg & AZX_REG_VS_D0I3C_CIP) && --timeout) { + udelay(10); + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + } + + /* Highly unlikely. But if it happens, flag error explicitly */ + if (!timeout) { + dev_err(bus->dev, "Before D0I3C update: D0I3C CIP timeout\n"); + return; + } + + if (enable) + reg = reg | AZX_REG_VS_D0I3C_I3; + else + reg = reg & (~AZX_REG_VS_D0I3C_I3); + + snd_hdac_chip_writeb(bus, VS_D0I3C, reg); + + timeout = 50; + /* Wait for cmd in progress to be cleared before exiting the function */ + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + while ((reg & AZX_REG_VS_D0I3C_CIP) && --timeout) { + udelay(10); + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + } + + /* Highly unlikely. But if it happens, flag error explicitly */ + if (!timeout) { + dev_err(bus->dev, "After D0I3C update: D0I3C CIP timeout\n"); + return; + } + + dev_dbg(bus->dev, "D0I3C register = 0x%x\n", + snd_hdac_chip_readb(bus, VS_D0I3C)); +} + /* called from IRQ */ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) { diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 5d4fbb094c48..88ba54ba5f72 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -52,6 +52,9 @@ #define AZX_PGCTL_LSRMD_MASK (1 << 4) #define AZX_PCIREG_CGCTL 0x48 #define AZX_CGCTL_MISCBDCGE_MASK (1 << 6) +/* D0I3C Register fields */ +#define AZX_REG_VS_D0I3C_CIP 0x1 /* Command in progress */ +#define AZX_REG_VS_D0I3C_I3 0x4 /* D0i3 enable */
struct skl_dsp_resource { u32 max_mcps; @@ -125,4 +128,6 @@ int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); void skl_cleanup_resources(struct skl *skl); const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id); +void skl_update_d0i3c(struct device *dev, bool enable); + #endif /* __SOUND_SOC_SKL_H */
The patch
ASoC: Intel: Skylake: Add support for programming D0i3C
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From a26a3f53e3d2bfeb666ca31b5f86c65a1816eb89 Mon Sep 17 00:00:00 2001
From: Pardha Saradhi K pardha.saradhi.kesapragada@intel.com Date: Thu, 3 Nov 2016 17:07:16 +0530 Subject: [PATCH] ASoC: Intel: Skylake: Add support for programming D0i3C
To set the controller in D0i3 mode, the driver needs to set D0i3C register after DSP is quiesced. Since the D0iX entry/exit is done by IPC, add this as callback so that it can be invoked from IPC module.
Signed-off-by: Pardha Saradhi K pardha.saradhi.kesapragada@intel.com Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/intel/skylake/skl-pcm.c | 1 + sound/soc/intel/skylake/skl-sst-ipc.h | 3 +++ sound/soc/intel/skylake/skl.c | 47 +++++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.h | 5 ++++ 4 files changed, 56 insertions(+)
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index c966b40da180..b69e05ec6844 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1211,6 +1211,7 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform) return ret; } skl_populate_modules(skl); + skl->skl_sst->update_d0i3c = skl_update_d0i3c; } pm_runtime_mark_last_busy(platform->dev); pm_runtime_put_autosuspend(platform->dev); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 1ae265d8ee08..ef2182d21934 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -83,6 +83,9 @@ struct skl_sst {
/* tplg manifest */ struct skl_dfw_manifest manifest; + + /* Callback to update D0i3C register */ + void (*update_d0i3c)(struct device *dev, bool enable); };
struct skl_ipc_init_instance_msg { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 2989c164dafe..b9209af89915 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -26,6 +26,7 @@ #include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/firmware.h> +#include <linux/delay.h> #include <sound/pcm.h> #include "../common/sst-acpi.h" #include <sound/hda_register.h> @@ -109,6 +110,52 @@ static int skl_init_chip(struct hdac_bus *bus, bool full_reset) return ret; }
+void skl_update_d0i3c(struct device *dev, bool enable) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct hdac_bus *bus = ebus_to_hbus(ebus); + u8 reg; + int timeout = 50; + + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + /* Do not write to D0I3C until command in progress bit is cleared */ + while ((reg & AZX_REG_VS_D0I3C_CIP) && --timeout) { + udelay(10); + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + } + + /* Highly unlikely. But if it happens, flag error explicitly */ + if (!timeout) { + dev_err(bus->dev, "Before D0I3C update: D0I3C CIP timeout\n"); + return; + } + + if (enable) + reg = reg | AZX_REG_VS_D0I3C_I3; + else + reg = reg & (~AZX_REG_VS_D0I3C_I3); + + snd_hdac_chip_writeb(bus, VS_D0I3C, reg); + + timeout = 50; + /* Wait for cmd in progress to be cleared before exiting the function */ + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + while ((reg & AZX_REG_VS_D0I3C_CIP) && --timeout) { + udelay(10); + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + } + + /* Highly unlikely. But if it happens, flag error explicitly */ + if (!timeout) { + dev_err(bus->dev, "After D0I3C update: D0I3C CIP timeout\n"); + return; + } + + dev_dbg(bus->dev, "D0I3C register = 0x%x\n", + snd_hdac_chip_readb(bus, VS_D0I3C)); +} + /* called from IRQ */ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) { diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 5d4fbb094c48..88ba54ba5f72 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -52,6 +52,9 @@ #define AZX_PGCTL_LSRMD_MASK (1 << 4) #define AZX_PCIREG_CGCTL 0x48 #define AZX_CGCTL_MISCBDCGE_MASK (1 << 6) +/* D0I3C Register fields */ +#define AZX_REG_VS_D0I3C_CIP 0x1 /* Command in progress */ +#define AZX_REG_VS_D0I3C_I3 0x4 /* D0i3 enable */
struct skl_dsp_resource { u32 max_mcps; @@ -125,4 +128,6 @@ int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); void skl_cleanup_resources(struct skl *skl); const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id); +void skl_update_d0i3c(struct device *dev, bool enable); + #endif /* __SOUND_SOC_SKL_H */
From: Jayachandran B jayachandran.b@intel.com
The driver needs two DSP callback, one to set D0i0 (active) and D0i3 (low-power) states.
Add these callbacks in dsp ops and implement them for broxton platforms.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/skylake/bxt-sst.c | 140 ++++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-sst-dsp.h | 12 +++ sound/soc/intel/skylake/skl-sst-ipc.h | 19 +++++ 3 files changed, 171 insertions(+)
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 1d251d59bcb9..fed818fe2e69 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -43,6 +43,9 @@
#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000
+/* Delay before scheduling D0i3 entry */ +#define BXT_D0I3_DELAY 5000 + static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) { return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); @@ -288,6 +291,141 @@ sst_load_base_firmware_failed: return ret; }
+/* + * Decide the D0i3 state that can be targeted based on the usecase + * ref counts and DSP state + * + * Decision Matrix: (X= dont care; state = target state) + * + * DSP state != SKL_DSP_RUNNING ; state = no d0i3 + * + * DSP state == SKL_DSP_RUNNING , the following matrix applies + * non_d0i3 >0; streaming =X; non_streaming =X; state = no d0i3 + * non_d0i3 =X; streaming =0; non_streaming =0; state = no d0i3 + * non_d0i3 =0; streaming >0; non_streaming =X; state = streaming d0i3 + * non_d0i3 =0; streaming =0; non_streaming =X; state = non-streaming d0i3 + */ +static int bxt_d0i3_target_state(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + struct skl_d0i3_data *d0i3 = &skl->d0i3; + + if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING) + return SKL_DSP_D0I3_NONE; + + if (d0i3->non_d0i3) + return SKL_DSP_D0I3_NONE; + else if (d0i3->streaming) + return SKL_DSP_D0I3_STREAMING; + else if (d0i3->non_streaming) + return SKL_DSP_D0I3_NON_STREAMING; + else + return SKL_DSP_D0I3_NONE; +} + +static void bxt_set_dsp_D0i3(struct work_struct *work) +{ + int ret; + struct skl_ipc_d0ix_msg msg; + struct skl_sst *skl = container_of(work, + struct skl_sst, d0i3.work.work); + struct sst_dsp *ctx = skl->dsp; + struct skl_d0i3_data *d0i3 = &skl->d0i3; + int target_state; + + dev_dbg(ctx->dev, "In %s:\n", __func__); + + /* D0i3 entry allowed only if core 0 alone is running */ + if (skl_dsp_get_enabled_cores(ctx) != SKL_DSP_CORE0_MASK) { + dev_warn(ctx->dev, + "D0i3 allowed when only core0 running:Exit\n"); + return; + } + + target_state = bxt_d0i3_target_state(ctx); + if (target_state == SKL_DSP_D0I3_NONE) + return; + + msg.instance_id = 0; + msg.module_id = 0; + msg.wake = 1; + msg.streaming = 0; + if (target_state == SKL_DSP_D0I3_STREAMING) + msg.streaming = 1; + + ret = skl_ipc_set_d0ix(&skl->ipc, &msg); + + if (ret < 0) { + dev_err(ctx->dev, "Failed to set DSP to D0i3 state\n"); + return; + } + + /* Set Vendor specific register D0I3C.I3 to enable D0i3*/ + if (skl->update_d0i3c) + skl->update_d0i3c(skl->dev, true); + + d0i3->state = target_state; + skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING_D0I3; +} + +static int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + struct skl_d0i3_data *d0i3 = &skl->d0i3; + + /* Schedule D0i3 only if the usecase ref counts are appropriate */ + if (bxt_d0i3_target_state(ctx) != SKL_DSP_D0I3_NONE) { + + dev_dbg(ctx->dev, "%s: Schedule D0i3\n", __func__); + + schedule_delayed_work(&d0i3->work, + msecs_to_jiffies(BXT_D0I3_DELAY)); + } + + return 0; +} + +static int bxt_set_dsp_D0i0(struct sst_dsp *ctx) +{ + int ret; + struct skl_ipc_d0ix_msg msg; + struct skl_sst *skl = ctx->thread_context; + + dev_dbg(ctx->dev, "In %s:\n", __func__); + + /* First Cancel any pending attempt to put DSP to D0i3 */ + cancel_delayed_work_sync(&skl->d0i3.work); + + /* If DSP is currently in D0i3, bring it to D0i0 */ + if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING_D0I3) + return 0; + + dev_dbg(ctx->dev, "Set DSP to D0i0\n"); + + msg.instance_id = 0; + msg.module_id = 0; + msg.streaming = 0; + msg.wake = 0; + + if (skl->d0i3.state == SKL_DSP_D0I3_STREAMING) + msg.streaming = 1; + + /* Clear Vendor specific register D0I3C.I3 to disable D0i3*/ + if (skl->update_d0i3c) + skl->update_d0i3c(skl->dev, false); + + ret = skl_ipc_set_d0ix(&skl->ipc, &msg); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set DSP to D0i0\n"); + return ret; + } + + skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING; + skl->d0i3.state = SKL_DSP_D0I3_NONE; + + return 0; +} + static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) { struct skl_sst *skl = ctx->thread_context; @@ -414,6 +552,8 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) static struct skl_dsp_fw_ops bxt_fw_ops = { .set_state_D0 = bxt_set_dsp_D0, .set_state_D3 = bxt_set_dsp_D3, + .set_state_D0i3 = bxt_schedule_dsp_D0i3, + .set_state_D0i0 = bxt_set_dsp_D0i0, .load_fw = bxt_load_base_firmware, .get_fw_errcode = bxt_get_errorcode, .load_library = bxt_load_library, diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index b9e71d051fb1..7c272ba0f4b5 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -126,11 +126,21 @@ struct sst_dsp_device; #define SKL_ADSPCS_CPA_SHIFT 24 #define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT)
+/* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, + /* Running in D0i3 state; can be in streaming or non-streaming D0i3 */ + SKL_DSP_RUNNING_D0I3, /* Running in D0i3 state*/ SKL_DSP_RESET, };
+/* D0i3 substates */ +enum skl_dsp_d0i3_states { + SKL_DSP_D0I3_NONE = -1, /* No D0i3 */ + SKL_DSP_D0I3_NON_STREAMING = 0, + SKL_DSP_D0I3_STREAMING = 1, +}; + struct skl_dsp_fw_ops { int (*load_fw)(struct sst_dsp *ctx); /* FW module parser/loader */ @@ -139,6 +149,8 @@ struct skl_dsp_fw_ops { int (*parse_fw)(struct sst_dsp *ctx); int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id); int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id); + int (*set_state_D0i3)(struct sst_dsp *ctx); + int (*set_state_D0i0)(struct sst_dsp *ctx); unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name); int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index ef2182d21934..a45c42046b64 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -53,6 +53,23 @@ struct skl_dsp_cores { int usage_count[SKL_DSP_CORES_MAX]; };
+/** + * skl_d0i3_data: skl D0i3 counters data struct + * + * @streaming: Count of usecases that can attempt streaming D0i3 + * @non_streaming: Count of usecases that can attempt non-streaming D0i3 + * @non_d0i3: Count of usecases that cannot attempt D0i3 + * @state: current state + * @work: D0i3 worker thread + */ +struct skl_d0i3_data { + int streaming; + int non_streaming; + int non_d0i3; + enum skl_dsp_d0i3_states state; + struct delayed_work work; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -86,6 +103,8 @@ struct skl_sst {
/* Callback to update D0i3C register */ void (*update_d0i3c)(struct device *dev, bool enable); + + struct skl_d0i3_data d0i3; };
struct skl_ipc_init_instance_msg {
The patch
ASoC: Intel: Skylake: Add D0iX callbacks
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 5bb4cd46ace5f220fbc34370e7fe9ba515ead9f8 Mon Sep 17 00:00:00 2001
From: Jayachandran B jayachandran.b@intel.com Date: Thu, 3 Nov 2016 17:07:17 +0530 Subject: [PATCH] ASoC: Intel: Skylake: Add D0iX callbacks
The driver needs two DSP callback, one to set D0i0 (active) and D0i3 (low-power) states.
Add these callbacks in dsp ops and implement them for broxton platforms.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/intel/skylake/bxt-sst.c | 140 ++++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-sst-dsp.h | 12 +++ sound/soc/intel/skylake/skl-sst-ipc.h | 19 +++++ 3 files changed, 171 insertions(+)
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 1d251d59bcb9..fed818fe2e69 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -43,6 +43,9 @@
#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000
+/* Delay before scheduling D0i3 entry */ +#define BXT_D0I3_DELAY 5000 + static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) { return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); @@ -288,6 +291,141 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) return ret; }
+/* + * Decide the D0i3 state that can be targeted based on the usecase + * ref counts and DSP state + * + * Decision Matrix: (X= dont care; state = target state) + * + * DSP state != SKL_DSP_RUNNING ; state = no d0i3 + * + * DSP state == SKL_DSP_RUNNING , the following matrix applies + * non_d0i3 >0; streaming =X; non_streaming =X; state = no d0i3 + * non_d0i3 =X; streaming =0; non_streaming =0; state = no d0i3 + * non_d0i3 =0; streaming >0; non_streaming =X; state = streaming d0i3 + * non_d0i3 =0; streaming =0; non_streaming =X; state = non-streaming d0i3 + */ +static int bxt_d0i3_target_state(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + struct skl_d0i3_data *d0i3 = &skl->d0i3; + + if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING) + return SKL_DSP_D0I3_NONE; + + if (d0i3->non_d0i3) + return SKL_DSP_D0I3_NONE; + else if (d0i3->streaming) + return SKL_DSP_D0I3_STREAMING; + else if (d0i3->non_streaming) + return SKL_DSP_D0I3_NON_STREAMING; + else + return SKL_DSP_D0I3_NONE; +} + +static void bxt_set_dsp_D0i3(struct work_struct *work) +{ + int ret; + struct skl_ipc_d0ix_msg msg; + struct skl_sst *skl = container_of(work, + struct skl_sst, d0i3.work.work); + struct sst_dsp *ctx = skl->dsp; + struct skl_d0i3_data *d0i3 = &skl->d0i3; + int target_state; + + dev_dbg(ctx->dev, "In %s:\n", __func__); + + /* D0i3 entry allowed only if core 0 alone is running */ + if (skl_dsp_get_enabled_cores(ctx) != SKL_DSP_CORE0_MASK) { + dev_warn(ctx->dev, + "D0i3 allowed when only core0 running:Exit\n"); + return; + } + + target_state = bxt_d0i3_target_state(ctx); + if (target_state == SKL_DSP_D0I3_NONE) + return; + + msg.instance_id = 0; + msg.module_id = 0; + msg.wake = 1; + msg.streaming = 0; + if (target_state == SKL_DSP_D0I3_STREAMING) + msg.streaming = 1; + + ret = skl_ipc_set_d0ix(&skl->ipc, &msg); + + if (ret < 0) { + dev_err(ctx->dev, "Failed to set DSP to D0i3 state\n"); + return; + } + + /* Set Vendor specific register D0I3C.I3 to enable D0i3*/ + if (skl->update_d0i3c) + skl->update_d0i3c(skl->dev, true); + + d0i3->state = target_state; + skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING_D0I3; +} + +static int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + struct skl_d0i3_data *d0i3 = &skl->d0i3; + + /* Schedule D0i3 only if the usecase ref counts are appropriate */ + if (bxt_d0i3_target_state(ctx) != SKL_DSP_D0I3_NONE) { + + dev_dbg(ctx->dev, "%s: Schedule D0i3\n", __func__); + + schedule_delayed_work(&d0i3->work, + msecs_to_jiffies(BXT_D0I3_DELAY)); + } + + return 0; +} + +static int bxt_set_dsp_D0i0(struct sst_dsp *ctx) +{ + int ret; + struct skl_ipc_d0ix_msg msg; + struct skl_sst *skl = ctx->thread_context; + + dev_dbg(ctx->dev, "In %s:\n", __func__); + + /* First Cancel any pending attempt to put DSP to D0i3 */ + cancel_delayed_work_sync(&skl->d0i3.work); + + /* If DSP is currently in D0i3, bring it to D0i0 */ + if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING_D0I3) + return 0; + + dev_dbg(ctx->dev, "Set DSP to D0i0\n"); + + msg.instance_id = 0; + msg.module_id = 0; + msg.streaming = 0; + msg.wake = 0; + + if (skl->d0i3.state == SKL_DSP_D0I3_STREAMING) + msg.streaming = 1; + + /* Clear Vendor specific register D0I3C.I3 to disable D0i3*/ + if (skl->update_d0i3c) + skl->update_d0i3c(skl->dev, false); + + ret = skl_ipc_set_d0ix(&skl->ipc, &msg); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set DSP to D0i0\n"); + return ret; + } + + skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING; + skl->d0i3.state = SKL_DSP_D0I3_NONE; + + return 0; +} + static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) { struct skl_sst *skl = ctx->thread_context; @@ -414,6 +552,8 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) static struct skl_dsp_fw_ops bxt_fw_ops = { .set_state_D0 = bxt_set_dsp_D0, .set_state_D3 = bxt_set_dsp_D3, + .set_state_D0i3 = bxt_schedule_dsp_D0i3, + .set_state_D0i0 = bxt_set_dsp_D0i0, .load_fw = bxt_load_base_firmware, .get_fw_errcode = bxt_get_errorcode, .load_library = bxt_load_library, diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index b9e71d051fb1..7c272ba0f4b5 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -126,11 +126,21 @@ struct sst_dsp_device; #define SKL_ADSPCS_CPA_SHIFT 24 #define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT)
+/* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, + /* Running in D0i3 state; can be in streaming or non-streaming D0i3 */ + SKL_DSP_RUNNING_D0I3, /* Running in D0i3 state*/ SKL_DSP_RESET, };
+/* D0i3 substates */ +enum skl_dsp_d0i3_states { + SKL_DSP_D0I3_NONE = -1, /* No D0i3 */ + SKL_DSP_D0I3_NON_STREAMING = 0, + SKL_DSP_D0I3_STREAMING = 1, +}; + struct skl_dsp_fw_ops { int (*load_fw)(struct sst_dsp *ctx); /* FW module parser/loader */ @@ -139,6 +149,8 @@ struct skl_dsp_fw_ops { int (*parse_fw)(struct sst_dsp *ctx); int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id); int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id); + int (*set_state_D0i3)(struct sst_dsp *ctx); + int (*set_state_D0i0)(struct sst_dsp *ctx); unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name); int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index ef2182d21934..a45c42046b64 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -53,6 +53,23 @@ struct skl_dsp_cores { int usage_count[SKL_DSP_CORES_MAX]; };
+/** + * skl_d0i3_data: skl D0i3 counters data struct + * + * @streaming: Count of usecases that can attempt streaming D0i3 + * @non_streaming: Count of usecases that can attempt non-streaming D0i3 + * @non_d0i3: Count of usecases that cannot attempt D0i3 + * @state: current state + * @work: D0i3 worker thread + */ +struct skl_d0i3_data { + int streaming; + int non_streaming; + int non_d0i3; + enum skl_dsp_d0i3_states state; + struct delayed_work work; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -86,6 +103,8 @@ struct skl_sst {
/* Callback to update D0i3C register */ void (*update_d0i3c)(struct device *dev, bool enable); + + struct skl_d0i3_data d0i3; };
struct skl_ipc_init_instance_msg {
For D0i3, we need to tell DSP to run the pipelines in LP mode. This information is kept in topology and passed to driver as an attribute for pipe.
So add a new tuple for lpmode and program the pipe based on value set.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/uapi/sound/snd_sst_tokens.h | 5 ++++- sound/soc/intel/skylake/skl-messages.c | 3 ++- sound/soc/intel/skylake/skl-sst-ipc.c | 9 ++++++++- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- sound/soc/intel/skylake/skl-topology.c | 5 +++++ sound/soc/intel/skylake/skl-topology.h | 1 + 6 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index 1ee2e943d66a..f4b8b34de519 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -157,6 +157,8 @@ * * %SKL_TKN_STR_LIB_NAME: Specifies the library name * + * %SKL_TKN_U32_PMODE: Specifies the power mode for pipe + * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest */ @@ -208,7 +210,8 @@ enum SKL_TKNS { SKL_TKN_U32_PROC_DOMAIN, SKL_TKN_U32_LIB_COUNT, SKL_TKN_STR_LIB_NAME, - SKL_TKN_MAX = SKL_TKN_STR_LIB_NAME, + SKL_TKN_U32_PMODE, + SKL_TKN_MAX = SKL_TKN_U32_PMODE, };
#endif diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 805b7f2173f3..87fc647fa04c 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1042,7 +1042,8 @@ int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe) dev_dbg(ctx->dev, "%s: pipe_id = %d\n", __func__, pipe->ppl_id);
ret = skl_ipc_create_pipeline(&ctx->ipc, pipe->memory_pages, - pipe->pipe_priority, pipe->ppl_id); + pipe->pipe_priority, pipe->ppl_id, + pipe->lp_mode); if (ret < 0) { dev_err(ctx->dev, "Failed to create pipeline\n"); return ret; diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 95120771a4f6..f30512bba44e 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -81,6 +81,11 @@ #define IPC_INSTANCE_ID(x) (((x) & IPC_INSTANCE_ID_MASK) \ << IPC_INSTANCE_ID_SHIFT)
+#define IPC_PPL_LP_MODE_SHIFT 0 +#define IPC_PPL_LP_MODE_MASK 0x1 +#define IPC_PPL_LP_MODE(x) (((x) & IPC_PPL_LP_MODE_MASK) \ + << IPC_PPL_LP_MODE_SHIFT) + /* Set pipeline state message */ #define IPC_PPL_STATE_SHIFT 0 #define IPC_PPL_STATE_MASK 0x1F @@ -559,7 +564,7 @@ void skl_ipc_free(struct sst_generic_ipc *ipc) }
int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc, - u16 ppl_mem_size, u8 ppl_type, u8 instance_id) + u16 ppl_mem_size, u8 ppl_type, u8 instance_id, u8 lp_mode) { struct skl_ipc_header header = {0}; u64 *ipc_header = (u64 *)(&header); @@ -572,6 +577,8 @@ int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc, header.primary |= IPC_PPL_TYPE(ppl_type); header.primary |= IPC_PPL_MEM_SIZE(ppl_mem_size);
+ header.extension = IPC_PPL_LP_MODE(lp_mode); + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); if (ret < 0) { diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index a45c42046b64..83bf1689f62e 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -148,7 +148,7 @@ struct skl_ipc_d0ix_msg { irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context);
int skl_ipc_create_pipeline(struct sst_generic_ipc *sst_ipc, - u16 ppl_mem_size, u8 ppl_type, u8 instance_id); + u16 ppl_mem_size, u8 ppl_type, u8 instance_id, u8 lp_mode);
int skl_ipc_delete_pipeline(struct sst_generic_ipc *sst_ipc, u8 instance_id);
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b5b1934d8550..9a7a008f6689 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1519,6 +1519,10 @@ static int skl_tplg_fill_pipe_tkn(struct device *dev, pipe->memory_pages = tkn_val; break;
+ case SKL_TKN_U32_PMODE: + pipe->lp_mode = tkn_val; + break; + default: dev_err(dev, "Token not handled %d\n", tkn); return -EINVAL; @@ -1841,6 +1845,7 @@ static int skl_tplg_get_token(struct device *dev, case SKL_TKN_U32_PIPE_CONN_TYPE: case SKL_TKN_U32_PIPE_PRIORITY: case SKL_TKN_U32_PIPE_MEM_PGS: + case SKL_TKN_U32_PMODE: if (is_pipe_exists) { ret = skl_tplg_fill_pipe_tkn(dev, mconfig->pipe, tkn_elem->token, tkn_elem->value); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index a519360f42a6..11fd0a4b6603 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -279,6 +279,7 @@ struct skl_pipe { u8 pipe_priority; u16 conn_type; u32 memory_pages; + u8 lp_mode; struct skl_pipe_params *p_params; enum skl_pipe_state state; struct list_head w_list;
The patch
ASoC: Intel: Skylake: Add support for LPMode
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 8a0cb2360ddb941e0a2fbe33d400d2985e4f2fff Mon Sep 17 00:00:00 2001
From: Vinod Koul vinod.koul@intel.com Date: Thu, 3 Nov 2016 17:07:18 +0530 Subject: [PATCH] ASoC: Intel: Skylake: Add support for LPMode
For D0i3, we need to tell DSP to run the pipelines in LP mode. This information is kept in topology and passed to driver as an attribute for pipe.
So add a new tuple for lpmode and program the pipe based on value set.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Mark Brown broonie@kernel.org --- include/uapi/sound/snd_sst_tokens.h | 5 ++++- sound/soc/intel/skylake/skl-messages.c | 3 ++- sound/soc/intel/skylake/skl-sst-ipc.c | 9 ++++++++- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- sound/soc/intel/skylake/skl-topology.c | 5 +++++ sound/soc/intel/skylake/skl-topology.h | 1 + 6 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index 1ee2e943d66a..f4b8b34de519 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -157,6 +157,8 @@ * * %SKL_TKN_STR_LIB_NAME: Specifies the library name * + * %SKL_TKN_U32_PMODE: Specifies the power mode for pipe + * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest */ @@ -208,7 +210,8 @@ enum SKL_TKNS { SKL_TKN_U32_PROC_DOMAIN, SKL_TKN_U32_LIB_COUNT, SKL_TKN_STR_LIB_NAME, - SKL_TKN_MAX = SKL_TKN_STR_LIB_NAME, + SKL_TKN_U32_PMODE, + SKL_TKN_MAX = SKL_TKN_U32_PMODE, };
#endif diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 805b7f2173f3..87fc647fa04c 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1042,7 +1042,8 @@ int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe) dev_dbg(ctx->dev, "%s: pipe_id = %d\n", __func__, pipe->ppl_id);
ret = skl_ipc_create_pipeline(&ctx->ipc, pipe->memory_pages, - pipe->pipe_priority, pipe->ppl_id); + pipe->pipe_priority, pipe->ppl_id, + pipe->lp_mode); if (ret < 0) { dev_err(ctx->dev, "Failed to create pipeline\n"); return ret; diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 087b8d6e7186..734408a34a24 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -81,6 +81,11 @@ #define IPC_INSTANCE_ID(x) (((x) & IPC_INSTANCE_ID_MASK) \ << IPC_INSTANCE_ID_SHIFT)
+#define IPC_PPL_LP_MODE_SHIFT 0 +#define IPC_PPL_LP_MODE_MASK 0x1 +#define IPC_PPL_LP_MODE(x) (((x) & IPC_PPL_LP_MODE_MASK) \ + << IPC_PPL_LP_MODE_SHIFT) + /* Set pipeline state message */ #define IPC_PPL_STATE_SHIFT 0 #define IPC_PPL_STATE_MASK 0x1F @@ -559,7 +564,7 @@ void skl_ipc_free(struct sst_generic_ipc *ipc) }
int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc, - u16 ppl_mem_size, u8 ppl_type, u8 instance_id) + u16 ppl_mem_size, u8 ppl_type, u8 instance_id, u8 lp_mode) { struct skl_ipc_header header = {0}; u64 *ipc_header = (u64 *)(&header); @@ -572,6 +577,8 @@ int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc, header.primary |= IPC_PPL_TYPE(ppl_type); header.primary |= IPC_PPL_MEM_SIZE(ppl_mem_size);
+ header.extension = IPC_PPL_LP_MODE(lp_mode); + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); if (ret < 0) { diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index a45c42046b64..83bf1689f62e 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -148,7 +148,7 @@ struct skl_ipc_d0ix_msg { irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context);
int skl_ipc_create_pipeline(struct sst_generic_ipc *sst_ipc, - u16 ppl_mem_size, u8 ppl_type, u8 instance_id); + u16 ppl_mem_size, u8 ppl_type, u8 instance_id, u8 lp_mode);
int skl_ipc_delete_pipeline(struct sst_generic_ipc *sst_ipc, u8 instance_id);
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b5b1934d8550..9a7a008f6689 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1519,6 +1519,10 @@ static int skl_tplg_fill_pipe_tkn(struct device *dev, pipe->memory_pages = tkn_val; break;
+ case SKL_TKN_U32_PMODE: + pipe->lp_mode = tkn_val; + break; + default: dev_err(dev, "Token not handled %d\n", tkn); return -EINVAL; @@ -1841,6 +1845,7 @@ static int skl_tplg_get_token(struct device *dev, case SKL_TKN_U32_PIPE_CONN_TYPE: case SKL_TKN_U32_PIPE_PRIORITY: case SKL_TKN_U32_PIPE_MEM_PGS: + case SKL_TKN_U32_PMODE: if (is_pipe_exists) { ret = skl_tplg_fill_pipe_tkn(dev, mconfig->pipe, tkn_elem->token, tkn_elem->value); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index a519360f42a6..11fd0a4b6603 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -279,6 +279,7 @@ struct skl_pipe { u8 pipe_priority; u16 conn_type; u32 memory_pages; + u8 lp_mode; struct skl_pipe_params *p_params; enum skl_pipe_state state; struct list_head w_list;
Not all use cases can support Doi3. Only certain use cases like hot word detection, deep buffering can support D0i3 based on resource requirement.
So, pass the D0i3 capability for the FE/BE copier using topology. This will be used to take a decision for D0i3 mode entry/exit.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/uapi/sound/snd_sst_tokens.h | 5 ++++- sound/soc/intel/skylake/skl-topology.c | 4 ++++ sound/soc/intel/skylake/skl-topology.h | 7 +++++++ 3 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index f4b8b34de519..93392bedcc58 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -159,6 +159,8 @@ * * %SKL_TKN_U32_PMODE: Specifies the power mode for pipe * + * %SKL_TKL_U32_D0I3_CAPS: Specifies the D0i3 capability for module + * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest */ @@ -211,7 +213,8 @@ enum SKL_TKNS { SKL_TKN_U32_LIB_COUNT, SKL_TKN_STR_LIB_NAME, SKL_TKN_U32_PMODE, - SKL_TKN_MAX = SKL_TKN_U32_PMODE, + SKL_TKL_U32_D0I3_CAPS, + SKL_TKN_MAX = SKL_TKL_U32_D0I3_CAPS, };
#endif diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 9a7a008f6689..86597d491982 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1830,6 +1830,10 @@ static int skl_tplg_get_token(struct device *dev, mconfig->converter = tkn_elem->value; break;
+ case SKL_TKL_U32_D0I3_CAPS: + mconfig->d0i3_caps = tkn_elem->value; + break; + case SKL_TKN_U32_PIPE_ID: ret = skl_tplg_add_pipe(dev, mconfig, skl, tkn_elem); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 11fd0a4b6603..38809f6d20d6 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -294,6 +294,12 @@ enum skl_module_state { SKL_MODULE_UNLOADED = 4, };
+enum d0i3_capability { + SKL_D0I3_NONE = 0, + SKL_D0I3_STREAMING = 1, + SKL_D0I3_NON_STREAMING = 2, +}; + struct skl_module_cfg { u8 guid[16]; struct skl_module_inst_id id; @@ -320,6 +326,7 @@ struct skl_module_cfg { u32 converter; u32 vbus_id; u32 mem_pages; + enum d0i3_capability d0i3_caps; struct skl_module_pin *m_in_pin; struct skl_module_pin *m_out_pin; enum skl_module_type m_type;
The patch
ASoC: Intel: Skylake: Add support for specifying D0i3 configuration
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 6bd9dcf339ebb292fa149ee0e52ea3c9d9822553 Mon Sep 17 00:00:00 2001
From: Vinod Koul vinod.koul@intel.com Date: Thu, 3 Nov 2016 17:07:19 +0530 Subject: [PATCH] ASoC: Intel: Skylake: Add support for specifying D0i3 configuration
Not all use cases can support Doi3. Only certain use cases like hot word detection, deep buffering can support D0i3 based on resource requirement.
So, pass the D0i3 capability for the FE/BE copier using topology. This will be used to take a decision for D0i3 mode entry/exit.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Mark Brown broonie@kernel.org --- include/uapi/sound/snd_sst_tokens.h | 5 ++++- sound/soc/intel/skylake/skl-topology.c | 4 ++++ sound/soc/intel/skylake/skl-topology.h | 7 +++++++ 3 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index f4b8b34de519..93392bedcc58 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -159,6 +159,8 @@ * * %SKL_TKN_U32_PMODE: Specifies the power mode for pipe * + * %SKL_TKL_U32_D0I3_CAPS: Specifies the D0i3 capability for module + * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest */ @@ -211,7 +213,8 @@ enum SKL_TKNS { SKL_TKN_U32_LIB_COUNT, SKL_TKN_STR_LIB_NAME, SKL_TKN_U32_PMODE, - SKL_TKN_MAX = SKL_TKN_U32_PMODE, + SKL_TKL_U32_D0I3_CAPS, + SKL_TKN_MAX = SKL_TKL_U32_D0I3_CAPS, };
#endif diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 9a7a008f6689..86597d491982 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1830,6 +1830,10 @@ static int skl_tplg_get_token(struct device *dev, mconfig->converter = tkn_elem->value; break;
+ case SKL_TKL_U32_D0I3_CAPS: + mconfig->d0i3_caps = tkn_elem->value; + break; + case SKL_TKN_U32_PIPE_ID: ret = skl_tplg_add_pipe(dev, mconfig, skl, tkn_elem); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 11fd0a4b6603..38809f6d20d6 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -294,6 +294,12 @@ enum skl_module_state { SKL_MODULE_UNLOADED = 4, };
+enum d0i3_capability { + SKL_D0I3_NONE = 0, + SKL_D0I3_STREAMING = 1, + SKL_D0I3_NON_STREAMING = 2, +}; + struct skl_module_cfg { u8 guid[16]; struct skl_module_inst_id id; @@ -320,6 +326,7 @@ struct skl_module_cfg { u32 converter; u32 vbus_id; u32 mem_pages; + enum d0i3_capability d0i3_caps; struct skl_module_pin *m_in_pin; struct skl_module_pin *m_out_pin; enum skl_module_type m_type;
For device opened/closed, we check the D0i3 capability for the device and invoke skl_tplg_d0i3_get/put, which counts the use case based on the mode supported.
These counters are then used to decide if the device can enter D0i3 mode of streaming or non-streaming or no D0i3.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/skylake/bxt-sst.c | 5 +++++ sound/soc/intel/skylake/skl-pcm.c | 9 ++++++++ sound/soc/intel/skylake/skl-sst-ipc.c | 17 +++++++++++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 2 ++ sound/soc/intel/skylake/skl-topology.c | 38 ++++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 3 +++ 6 files changed, 74 insertions(+)
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index fed818fe2e69..1f9f33d34000 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -610,10 +610,15 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, if (ret) return ret;
+ /* set the D0i3 check */ + skl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0; + skl->cores.count = 2; skl->boot_complete = false; init_waitqueue_head(&skl->boot_wait); skl->is_first_boot = true; + INIT_DELAYED_WORK(&skl->d0i3.work, bxt_set_dsp_D0i3); + skl->d0i3.state = SKL_DSP_D0I3_NONE;
if (dsp) *dsp = skl; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 792b989733bf..082a79513ff5 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -144,6 +144,8 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, struct hdac_ext_stream *stream; struct snd_pcm_runtime *runtime = substream->runtime; struct skl_dma_params *dma_params; + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_module_cfg *mconfig;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
@@ -177,6 +179,9 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, skl_set_suspend_active(substream, dai, true); snd_pcm_set_sync(substream);
+ mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + skl_tplg_d0i3_get(skl, mconfig->d0i3_caps); + return 0; }
@@ -302,6 +307,7 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct skl_dma_params *dma_params = NULL; struct skl *skl = ebus_to_skl(ebus); + struct skl_module_cfg *mconfig;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
@@ -325,6 +331,9 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, skl->skl_sst->miscbdcg_disabled = false; }
+ mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + skl_tplg_d0i3_put(skl, mconfig->d0i3_caps); + kfree(dma_params); }
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index f30512bba44e..3646e7b6ff4c 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -306,6 +306,23 @@ static void skl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) header->primary | SKL_ADSP_REG_HIPCI_BUSY); }
+int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state) +{ + int ret; + + /* check D0i3 support */ + if (!dsp->fw_ops.set_state_D0i0) + return 0; + + /* Attempt D0i0 or D0i3 based on state */ + if (state) + ret = dsp->fw_ops.set_state_D0i0(dsp); + else + ret = dsp->fw_ops.set_state_D0i3(dsp); + + return ret; +} + static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc, u64 ipc_header) { diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 83bf1689f62e..0568f2e8fc57 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -187,6 +187,8 @@ int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, struct skl_ipc_d0ix_msg *msg);
+int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state); + void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); void skl_ipc_op_int_disable(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 86597d491982..bd313c907b20 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -36,6 +36,44 @@ #define SKL_IN_DIR_BIT_MASK BIT(0) #define SKL_PIN_COUNT_MASK GENMASK(7, 4)
+void skl_tplg_d0i3_get(struct skl *skl, enum d0i3_capability caps) +{ + struct skl_d0i3_data *d0i3 = &skl->skl_sst->d0i3; + + switch (caps) { + case SKL_D0I3_NONE: + d0i3->non_d0i3++; + break; + + case SKL_D0I3_STREAMING: + d0i3->streaming++; + break; + + case SKL_D0I3_NON_STREAMING: + d0i3->non_streaming++; + break; + } +} + +void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps) +{ + struct skl_d0i3_data *d0i3 = &skl->skl_sst->d0i3; + + switch (caps) { + case SKL_D0I3_NONE: + d0i3->non_d0i3--; + break; + + case SKL_D0I3_STREAMING: + d0i3->streaming--; + break; + + case SKL_D0I3_NON_STREAMING: + d0i3->non_streaming--; + break; + } +} + /* * SKL DSP driver modelling uses only few DAPM widgets so for rest we will * ignore. This helpers checks if the SKL driver handles this widget type diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 38809f6d20d6..3b962d84bcb8 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -369,6 +369,9 @@ struct skl_module_cfg *skl_tplg_fe_get_cpr_module( int skl_tplg_update_pipe_params(struct device *dev, struct skl_module_cfg *mconfig, struct skl_pipe_params *params);
+void skl_tplg_d0i3_get(struct skl *skl, enum d0i3_capability caps); +void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps); + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe);
int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
The patch
ASoC: Intel: Skylake: Add D0i3 mode ref counting
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From a83e3b4c4454ecdc473cf09aa1c9d1705623c4cc Mon Sep 17 00:00:00 2001
From: Vinod Koul vinod.koul@intel.com Date: Thu, 3 Nov 2016 17:07:20 +0530 Subject: [PATCH] ASoC: Intel: Skylake: Add D0i3 mode ref counting
For device opened/closed, we check the D0i3 capability for the device and invoke skl_tplg_d0i3_get/put, which counts the use case based on the mode supported.
These counters are then used to decide if the device can enter D0i3 mode of streaming or non-streaming or no D0i3.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/intel/skylake/bxt-sst.c | 5 +++++ sound/soc/intel/skylake/skl-pcm.c | 9 ++++++++ sound/soc/intel/skylake/skl-sst-ipc.c | 17 +++++++++++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 2 ++ sound/soc/intel/skylake/skl-topology.c | 38 ++++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 3 +++ 6 files changed, 74 insertions(+)
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index fed818fe2e69..1f9f33d34000 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -610,10 +610,15 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, if (ret) return ret;
+ /* set the D0i3 check */ + skl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0; + skl->cores.count = 2; skl->boot_complete = false; init_waitqueue_head(&skl->boot_wait); skl->is_first_boot = true; + INIT_DELAYED_WORK(&skl->d0i3.work, bxt_set_dsp_D0i3); + skl->d0i3.state = SKL_DSP_D0I3_NONE;
if (dsp) *dsp = skl; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index b69e05ec6844..84b5101e6ca6 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -144,6 +144,8 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, struct hdac_ext_stream *stream; struct snd_pcm_runtime *runtime = substream->runtime; struct skl_dma_params *dma_params; + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_module_cfg *mconfig;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
@@ -177,6 +179,9 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, skl_set_suspend_active(substream, dai, true); snd_pcm_set_sync(substream);
+ mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + skl_tplg_d0i3_get(skl, mconfig->d0i3_caps); + return 0; }
@@ -302,6 +307,7 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct skl_dma_params *dma_params = NULL; struct skl *skl = ebus_to_skl(ebus); + struct skl_module_cfg *mconfig;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
@@ -325,6 +331,9 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, skl->skl_sst->miscbdcg_disabled = false; }
+ mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + skl_tplg_d0i3_put(skl, mconfig->d0i3_caps); + kfree(dma_params); }
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 734408a34a24..594623a4b4cd 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -306,6 +306,23 @@ static void skl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) header->primary | SKL_ADSP_REG_HIPCI_BUSY); }
+int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state) +{ + int ret; + + /* check D0i3 support */ + if (!dsp->fw_ops.set_state_D0i0) + return 0; + + /* Attempt D0i0 or D0i3 based on state */ + if (state) + ret = dsp->fw_ops.set_state_D0i0(dsp); + else + ret = dsp->fw_ops.set_state_D0i3(dsp); + + return ret; +} + static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc, u64 ipc_header) { diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 83bf1689f62e..0568f2e8fc57 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -187,6 +187,8 @@ int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, struct skl_ipc_d0ix_msg *msg);
+int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state); + void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); void skl_ipc_op_int_disable(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 86597d491982..bd313c907b20 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -36,6 +36,44 @@ #define SKL_IN_DIR_BIT_MASK BIT(0) #define SKL_PIN_COUNT_MASK GENMASK(7, 4)
+void skl_tplg_d0i3_get(struct skl *skl, enum d0i3_capability caps) +{ + struct skl_d0i3_data *d0i3 = &skl->skl_sst->d0i3; + + switch (caps) { + case SKL_D0I3_NONE: + d0i3->non_d0i3++; + break; + + case SKL_D0I3_STREAMING: + d0i3->streaming++; + break; + + case SKL_D0I3_NON_STREAMING: + d0i3->non_streaming++; + break; + } +} + +void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps) +{ + struct skl_d0i3_data *d0i3 = &skl->skl_sst->d0i3; + + switch (caps) { + case SKL_D0I3_NONE: + d0i3->non_d0i3--; + break; + + case SKL_D0I3_STREAMING: + d0i3->streaming--; + break; + + case SKL_D0I3_NON_STREAMING: + d0i3->non_streaming--; + break; + } +} + /* * SKL DSP driver modelling uses only few DAPM widgets so for rest we will * ignore. This helpers checks if the SKL driver handles this widget type diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 38809f6d20d6..3b962d84bcb8 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -369,6 +369,9 @@ struct skl_module_cfg *skl_tplg_fe_get_cpr_module( int skl_tplg_update_pipe_params(struct device *dev, struct skl_module_cfg *mconfig, struct skl_pipe_params *params);
+void skl_tplg_d0i3_get(struct skl *skl, enum d0i3_capability caps); +void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps); + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe);
int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
From: Jayachandran B jayachandran.b@intel.com
While going to suspend, if we have any pending D0i3 work scheduled, flush that and force the DSP to goto D0i3 mode before going to suspend.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/skylake/skl-messages.c | 27 +++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.c | 10 ++++++++++ sound/soc/intel/skylake/skl.h | 1 + 3 files changed, 38 insertions(+)
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 87fc647fa04c..4ae021aabc3a 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -294,6 +294,33 @@ int skl_free_dsp(struct skl *skl) return 0; }
+/* + * In the case of "suspend_active" i.e, the Audio IP being active + * during system suspend, immediately excecute any pending D0i3 work + * before suspending. This is needed for the IP to work in low power + * mode during system suspend. In the case of normal suspend, cancel + * any pending D0i3 work. + */ +int skl_suspend_late_dsp(struct skl *skl) +{ + struct skl_sst *ctx = skl->skl_sst; + struct delayed_work *dwork; + + if (!ctx) + return 0; + + dwork = &ctx->d0i3.work; + + if (dwork->work.func) { + if (skl->supend_active) + flush_delayed_work(dwork); + else + cancel_delayed_work_sync(dwork); + } + + return 0; +} + int skl_suspend_dsp(struct skl *skl) { struct skl_sst *ctx = skl->skl_sst; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index b9209af89915..ff08c8ec93a1 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -228,6 +228,15 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect) return 0; }
+static int skl_suspend_late(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct skl *skl = ebus_to_skl(ebus); + + return skl_suspend_late_dsp(skl); +} + #ifdef CONFIG_PM static int _skl_suspend(struct hdac_ext_bus *ebus) { @@ -392,6 +401,7 @@ static int skl_runtime_resume(struct device *dev) static const struct dev_pm_ops skl_pm = { SET_SYSTEM_SLEEP_PM_OPS(skl_suspend, skl_resume) SET_RUNTIME_PM_OPS(skl_runtime_suspend, skl_runtime_resume, NULL) + .suspend_late = skl_suspend_late, };
/* diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 88ba54ba5f72..4986e3929dd3 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -124,6 +124,7 @@ int skl_get_dmic_geo(struct skl *skl); int skl_nhlt_update_topology_bin(struct skl *skl); int skl_init_dsp(struct skl *skl); int skl_free_dsp(struct skl *skl); +int skl_suspend_late_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); void skl_cleanup_resources(struct skl *skl);
The patch
ASoC: Intel: Skylake: Flush pending D0i3 request on suspend
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 8b4a133c6145a34618c770117b65b33f1aa993aa Mon Sep 17 00:00:00 2001
From: Jayachandran B jayachandran.b@intel.com Date: Thu, 3 Nov 2016 17:07:21 +0530 Subject: [PATCH] ASoC: Intel: Skylake: Flush pending D0i3 request on suspend
While going to suspend, if we have any pending D0i3 work scheduled, flush that and force the DSP to goto D0i3 mode before going to suspend.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/intel/skylake/skl-messages.c | 27 +++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.c | 10 ++++++++++ sound/soc/intel/skylake/skl.h | 1 + 3 files changed, 38 insertions(+)
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 87fc647fa04c..4ae021aabc3a 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -294,6 +294,33 @@ int skl_free_dsp(struct skl *skl) return 0; }
+/* + * In the case of "suspend_active" i.e, the Audio IP being active + * during system suspend, immediately excecute any pending D0i3 work + * before suspending. This is needed for the IP to work in low power + * mode during system suspend. In the case of normal suspend, cancel + * any pending D0i3 work. + */ +int skl_suspend_late_dsp(struct skl *skl) +{ + struct skl_sst *ctx = skl->skl_sst; + struct delayed_work *dwork; + + if (!ctx) + return 0; + + dwork = &ctx->d0i3.work; + + if (dwork->work.func) { + if (skl->supend_active) + flush_delayed_work(dwork); + else + cancel_delayed_work_sync(dwork); + } + + return 0; +} + int skl_suspend_dsp(struct skl *skl) { struct skl_sst *ctx = skl->skl_sst; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index ed59783e9846..61a484888cfa 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -228,6 +228,15 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect) return 0; }
+static int skl_suspend_late(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct skl *skl = ebus_to_skl(ebus); + + return skl_suspend_late_dsp(skl); +} + #ifdef CONFIG_PM static int _skl_suspend(struct hdac_ext_bus *ebus) { @@ -390,6 +399,7 @@ static int skl_runtime_resume(struct device *dev) static const struct dev_pm_ops skl_pm = { SET_SYSTEM_SLEEP_PM_OPS(skl_suspend, skl_resume) SET_RUNTIME_PM_OPS(skl_runtime_suspend, skl_runtime_resume, NULL) + .suspend_late = skl_suspend_late, };
/* diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 88ba54ba5f72..4986e3929dd3 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -124,6 +124,7 @@ int skl_get_dmic_geo(struct skl *skl); int skl_nhlt_update_topology_bin(struct skl *skl); int skl_init_dsp(struct skl *skl); int skl_free_dsp(struct skl *skl); +int skl_suspend_late_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); void skl_cleanup_resources(struct skl *skl);
From: Jayachandran B jayachandran.b@intel.com
We were invoking pci_disable_device() while going to suspend-to-idle and pci_enable_device() while coming back to active state.
Turns out that we do not need these calls as we only need system to be wake capable when in suspend-to-idle state. The wake capability is already done by enable_irq_wake() calls, so remove these unwanted calls in driver.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/skylake/skl.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index ff08c8ec93a1..61a484888cfa 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -299,7 +299,6 @@ static int skl_suspend(struct device *dev)
enable_irq_wake(bus->irq); pci_save_state(pci); - pci_disable_device(pci); } else { ret = _skl_suspend(ebus); if (ret < 0) @@ -342,7 +341,6 @@ static int skl_resume(struct device *dev) */ if (skl->supend_active) { pci_restore_state(pci); - ret = pci_enable_device(pci); snd_hdac_ext_bus_link_power_up_all(ebus); disable_irq_wake(bus->irq); /*
The patch
ASoC: Intel: Skylake: remove pci device enabling calls on suspend
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 68d03a3aa2747e1a33231950d2c8369f1cef4244 Mon Sep 17 00:00:00 2001
From: Jayachandran B jayachandran.b@intel.com Date: Thu, 3 Nov 2016 17:07:22 +0530 Subject: [PATCH] ASoC: Intel: Skylake: remove pci device enabling calls on suspend
We were invoking pci_disable_device() while going to suspend-to-idle and pci_enable_device() while coming back to active state.
Turns out that we do not need these calls as we only need system to be wake capable when in suspend-to-idle state. The wake capability is already done by enable_irq_wake() calls, so remove these unwanted calls in driver.
Signed-off-by: Jayachandran B jayachandran.b@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/intel/skylake/skl.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index b9209af89915..ed59783e9846 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -290,7 +290,6 @@ static int skl_suspend(struct device *dev)
enable_irq_wake(bus->irq); pci_save_state(pci); - pci_disable_device(pci); } else { ret = _skl_suspend(ebus); if (ret < 0) @@ -333,7 +332,6 @@ static int skl_resume(struct device *dev) */ if (skl->supend_active) { pci_restore_state(pci); - ret = pci_enable_device(pci); snd_hdac_ext_bus_link_power_up_all(ebus); disable_irq_wake(bus->irq); /*
participants (2)
-
Mark Brown
-
Vinod Koul