Merge remote-tracking branch 'asoc/topic/intel' into asoc-next
Цей коміт міститься в:
@@ -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,
|
||||
@@ -470,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;
|
||||
|
@@ -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;
|
||||
@@ -500,16 +527,14 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
|
||||
int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
|
||||
{
|
||||
struct skl_dma_control *dma_ctrl;
|
||||
struct skl_i2s_config_blob config_blob;
|
||||
struct skl_ipc_large_config_msg msg = {0};
|
||||
int err = 0;
|
||||
|
||||
|
||||
/*
|
||||
* if blob size is same as capablity size, then no dma control
|
||||
* present so return
|
||||
* if blob size zero, then return
|
||||
*/
|
||||
if (mconfig->formats_config.caps_size == sizeof(config_blob))
|
||||
if (mconfig->formats_config.caps_size == 0)
|
||||
return 0;
|
||||
|
||||
msg.large_param_id = DMA_CONTROL_ID;
|
||||
@@ -523,7 +548,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
|
||||
dma_ctrl->node_id = skl_get_node_id(ctx, mconfig);
|
||||
|
||||
/* size in dwords */
|
||||
dma_ctrl->config_length = sizeof(config_blob) / 4;
|
||||
dma_ctrl->config_length = mconfig->formats_config.caps_size / 4;
|
||||
|
||||
memcpy(dma_ctrl->config_data, mconfig->formats_config.caps,
|
||||
mconfig->formats_config.caps_size);
|
||||
@@ -531,7 +556,6 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
|
||||
err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl);
|
||||
|
||||
kfree(dma_ctrl);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1042,7 +1066,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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1031,10 +1040,24 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer
|
||||
(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
unsigned int pos;
|
||||
|
||||
/* use the position buffer as default */
|
||||
pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
|
||||
/*
|
||||
* Use DPIB for Playback stream as the periodic DMA Position-in-
|
||||
* Buffer Writes may be scheduled at the same time or later than
|
||||
* the MSI and does not guarantee to reflect the Position of the
|
||||
* last buffer that was transferred. Whereas DPIB register in
|
||||
* HAD space reflects the actual data that is transferred.
|
||||
* Use the position buffer for capture, as DPIB write gets
|
||||
* completed earlier than the actual data written to the DDR.
|
||||
*/
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
pos = readl(ebus->bus.remap_addr + AZX_REG_VS_SDXDPIB_XBASE +
|
||||
(AZX_REG_VS_SDXDPIB_XINTERVAL *
|
||||
hdac_stream(hstream)->index));
|
||||
else
|
||||
pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
|
||||
|
||||
if (pos >= hdac_stream(hstream)->bufsize)
|
||||
pos = 0;
|
||||
@@ -1197,6 +1220,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);
|
||||
|
@@ -17,7 +17,6 @@
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/delay.h>
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.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);
|
||||
|
@@ -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
|
||||
@@ -172,6 +177,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 +274,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,
|
||||
@@ -289,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)
|
||||
{
|
||||
@@ -464,7 +498,7 @@ irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context)
|
||||
skl_ipc_int_enable(dsp);
|
||||
|
||||
/* continue to send any remaining messages... */
|
||||
kthread_queue_work(&ipc->kworker, &ipc->kwork);
|
||||
schedule_work(&ipc->kwork);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@@ -547,7 +581,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);
|
||||
@@ -560,6 +594,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) {
|
||||
@@ -931,3 +967,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);
|
||||
|
@@ -16,7 +16,6 @@
|
||||
#ifndef __SKL_IPC_H
|
||||
#define __SKL_IPC_H
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include "../common/sst-ipc.h"
|
||||
|
||||
@@ -53,6 +52,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;
|
||||
@@ -83,6 +99,11 @@ 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_d0i3_data d0i3;
|
||||
};
|
||||
|
||||
struct skl_ipc_init_instance_msg {
|
||||
@@ -111,6 +132,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
|
||||
@@ -119,7 +147,7 @@ struct skl_ipc_large_config_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);
|
||||
|
||||
@@ -155,6 +183,11 @@ 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);
|
||||
|
||||
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);
|
||||
|
@@ -179,7 +179,7 @@ static inline int skl_getid_32(struct uuid_module *module, u64 *val,
|
||||
index = ffz(mask_val);
|
||||
pvt_id = index + word1_mask + word2_mask;
|
||||
if (pvt_id <= (max_inst - 1)) {
|
||||
*val |= 1 << (index + word1_mask);
|
||||
*val |= 1ULL << (index + word1_mask);
|
||||
return pvt_id;
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
@@ -1519,6 +1557,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;
|
||||
@@ -1826,6 +1868,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);
|
||||
@@ -1841,6 +1887,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);
|
||||
|
@@ -113,23 +113,6 @@ struct skl_cpr_gtw_cfg {
|
||||
u32 config_data[1];
|
||||
} __packed;
|
||||
|
||||
struct skl_i2s_config_blob {
|
||||
u32 gateway_attrib;
|
||||
u32 tdm_ts_group[8];
|
||||
u32 ssc0;
|
||||
u32 ssc1;
|
||||
u32 sscto;
|
||||
u32 sspsp;
|
||||
u32 sstsa;
|
||||
u32 ssrsa;
|
||||
u32 ssc2;
|
||||
u32 sspsp2;
|
||||
u32 ssc3;
|
||||
u32 ssioc;
|
||||
u32 mdivc;
|
||||
u32 mdivr;
|
||||
} __packed;
|
||||
|
||||
struct skl_dma_control {
|
||||
u32 node_id;
|
||||
u32 config_length;
|
||||
@@ -279,6 +262,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;
|
||||
@@ -293,6 +277,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;
|
||||
@@ -319,6 +309,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;
|
||||
@@ -361,6 +352,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);
|
||||
|
@@ -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)
|
||||
{
|
||||
@@ -181,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)
|
||||
{
|
||||
@@ -243,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)
|
||||
@@ -286,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);
|
||||
/*
|
||||
@@ -345,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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@@ -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;
|
||||
@@ -121,8 +124,11 @@ 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);
|
||||
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 */
|
||||
|
Посилання в новій задачі
Заблокувати користувача