forked from Minki/linux
ASoC: Intel: Add DSP init and boot up functionality for SKL
This patch adds code to enable, disable and boot DSP core. Also provide some helpers to reset and power up/down the core. Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com> Signed-off-by: Jeeja KP <jeeja.kp@intel.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
b81fd26359
commit
e973e31a02
@ -22,6 +22,8 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "../skylake/skl-sst-dsp.h"
|
||||
|
||||
struct sst_mem_block;
|
||||
struct sst_module;
|
||||
struct sst_fw;
|
||||
@ -306,6 +308,8 @@ struct sst_dsp {
|
||||
|
||||
/* SKL data */
|
||||
|
||||
struct skl_dsp_fw_ops fw_ops;
|
||||
int sst_state;
|
||||
u32 intr_status;
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,6 @@ snd-soc-skl-objs := skl.o skl-pcm.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
|
||||
|
||||
# Skylake IPC Support
|
||||
snd-soc-skl-ipc-objs := skl-sst-ipc.o
|
||||
snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
|
||||
|
337
sound/soc/intel/skylake/skl-sst-dsp.c
Normal file
337
sound/soc/intel/skylake/skl-sst-dsp.c
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* skl-sst-dsp.c - SKL SST library generic function
|
||||
*
|
||||
* Copyright (C) 2014-15, Intel Corporation.
|
||||
* Author:Rafal Redzimski <rafal.f.redzimski@intel.com>
|
||||
* Jeeja KP <jeeja.kp@intel.com>
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
#include <sound/pcm.h>
|
||||
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-ipc.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
#include "skl-sst-ipc.h"
|
||||
|
||||
/* various timeout values */
|
||||
#define SKL_DSP_PU_TO 50
|
||||
#define SKL_DSP_PD_TO 50
|
||||
#define SKL_DSP_RESET_TO 50
|
||||
|
||||
void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state)
|
||||
{
|
||||
mutex_lock(&ctx->mutex);
|
||||
ctx->sst_state = state;
|
||||
mutex_unlock(&ctx->mutex);
|
||||
}
|
||||
|
||||
static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* update bits */
|
||||
sst_dsp_shim_update_bits_unlocked(ctx,
|
||||
SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK,
|
||||
SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK));
|
||||
|
||||
/* poll with timeout to check if operation successful */
|
||||
ret = sst_dsp_register_poll(ctx,
|
||||
SKL_ADSP_REG_ADSPCS,
|
||||
SKL_ADSPCS_CRST_MASK,
|
||||
SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK),
|
||||
SKL_DSP_RESET_TO,
|
||||
"Set reset");
|
||||
if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
|
||||
SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) !=
|
||||
SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) {
|
||||
dev_err(ctx->dev, "Set reset state failed\n");
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(ctx->dev, "In %s\n", __func__);
|
||||
|
||||
/* update bits */
|
||||
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
|
||||
SKL_ADSPCS_CRST_MASK, 0);
|
||||
|
||||
/* poll with timeout to check if operation successful */
|
||||
ret = sst_dsp_register_poll(ctx,
|
||||
SKL_ADSP_REG_ADSPCS,
|
||||
SKL_ADSPCS_CRST_MASK,
|
||||
0,
|
||||
SKL_DSP_RESET_TO,
|
||||
"Unset reset");
|
||||
|
||||
if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
|
||||
SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != 0) {
|
||||
dev_err(ctx->dev, "Unset reset state failed\n");
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool is_skl_dsp_core_enable(struct sst_dsp *ctx)
|
||||
{
|
||||
int val;
|
||||
bool is_enable;
|
||||
|
||||
val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS);
|
||||
|
||||
is_enable = ((val & SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) &&
|
||||
(val & SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)) &&
|
||||
!(val & SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) &&
|
||||
!(val & SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)));
|
||||
|
||||
dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable);
|
||||
return is_enable;
|
||||
}
|
||||
|
||||
static int skl_dsp_reset_core(struct sst_dsp *ctx)
|
||||
{
|
||||
/* stall core */
|
||||
sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
|
||||
sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
|
||||
SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
|
||||
|
||||
/* set reset state */
|
||||
return skl_dsp_core_set_reset_state(ctx);
|
||||
}
|
||||
|
||||
static int skl_dsp_start_core(struct sst_dsp *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* unset reset state */
|
||||
ret = skl_dsp_core_unset_reset_state(ctx);
|
||||
if (ret < 0) {
|
||||
dev_dbg(ctx->dev, "dsp unset reset fails\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* run core */
|
||||
dev_dbg(ctx->dev, "run core...\n");
|
||||
sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
|
||||
sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
|
||||
~SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
|
||||
|
||||
if (!is_skl_dsp_core_enable(ctx)) {
|
||||
skl_dsp_reset_core(ctx);
|
||||
dev_err(ctx->dev, "DSP core enable failed\n");
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skl_dsp_core_power_up(struct sst_dsp *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* update bits */
|
||||
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
|
||||
SKL_ADSPCS_SPA_MASK, SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK));
|
||||
|
||||
/* poll with timeout to check if operation successful */
|
||||
ret = sst_dsp_register_poll(ctx,
|
||||
SKL_ADSP_REG_ADSPCS,
|
||||
SKL_ADSPCS_CPA_MASK,
|
||||
SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK),
|
||||
SKL_DSP_PU_TO,
|
||||
"Power up");
|
||||
|
||||
if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
|
||||
SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) !=
|
||||
SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) {
|
||||
dev_err(ctx->dev, "DSP core power up failed\n");
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skl_dsp_core_power_down(struct sst_dsp *ctx)
|
||||
{
|
||||
/* update bits */
|
||||
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
|
||||
SKL_ADSPCS_SPA_MASK, 0);
|
||||
|
||||
/* poll with timeout to check if operation successful */
|
||||
return sst_dsp_register_poll(ctx,
|
||||
SKL_ADSP_REG_ADSPCS,
|
||||
SKL_ADSPCS_SPA_MASK,
|
||||
0,
|
||||
SKL_DSP_PD_TO,
|
||||
"Power down");
|
||||
}
|
||||
|
||||
static int skl_dsp_enable_core(struct sst_dsp *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* power up */
|
||||
ret = skl_dsp_core_power_up(ctx);
|
||||
if (ret < 0) {
|
||||
dev_dbg(ctx->dev, "dsp core power up failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return skl_dsp_start_core(ctx);
|
||||
}
|
||||
|
||||
int skl_dsp_disable_core(struct sst_dsp *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = skl_dsp_reset_core(ctx);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "dsp core reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* power down core*/
|
||||
ret = skl_dsp_core_power_down(ctx);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "dsp core power down failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (is_skl_dsp_core_enable(ctx)) {
|
||||
dev_err(ctx->dev, "DSP core disable failed\n");
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int skl_dsp_boot(struct sst_dsp *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (is_skl_dsp_core_enable(ctx)) {
|
||||
dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n");
|
||||
ret = skl_dsp_reset_core(ctx);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "dsp reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = skl_dsp_start_core(ctx);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "dsp start failed\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
dev_dbg(ctx->dev, "disable and enable to make sure DSP is invalid state\n");
|
||||
ret = skl_dsp_disable_core(ctx);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "dsp disable core failes\n");
|
||||
return ret;
|
||||
}
|
||||
ret = skl_dsp_enable_core(ctx);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct sst_dsp *ctx = dev_id;
|
||||
u32 val;
|
||||
irqreturn_t result = IRQ_NONE;
|
||||
|
||||
spin_lock(&ctx->spinlock);
|
||||
|
||||
val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS);
|
||||
ctx->intr_status = val;
|
||||
|
||||
if (val & SKL_ADSPIS_IPC) {
|
||||
skl_ipc_int_disable(ctx);
|
||||
result = IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
spin_unlock(&ctx->spinlock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int skl_dsp_wake(struct sst_dsp *ctx)
|
||||
{
|
||||
return ctx->fw_ops.set_state_D0(ctx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_dsp_wake);
|
||||
|
||||
int skl_dsp_sleep(struct sst_dsp *ctx)
|
||||
{
|
||||
return ctx->fw_ops.set_state_D3(ctx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_dsp_sleep);
|
||||
|
||||
struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
|
||||
struct sst_dsp_device *sst_dev, int irq)
|
||||
{
|
||||
int ret;
|
||||
struct sst_dsp *sst;
|
||||
|
||||
sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
|
||||
if (sst == NULL)
|
||||
return NULL;
|
||||
|
||||
spin_lock_init(&sst->spinlock);
|
||||
mutex_init(&sst->mutex);
|
||||
sst->dev = dev;
|
||||
sst->sst_dev = sst_dev;
|
||||
sst->irq = irq;
|
||||
sst->ops = sst_dev->ops;
|
||||
sst->thread_context = sst_dev->thread_context;
|
||||
|
||||
/* Initialise SST Audio DSP */
|
||||
if (sst->ops->init) {
|
||||
ret = sst->ops->init(sst, NULL);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Register the ISR */
|
||||
ret = request_threaded_irq(sst->irq, sst->ops->irq_handler,
|
||||
sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
|
||||
if (ret) {
|
||||
dev_err(sst->dev, "unable to grab threaded IRQ %d, disabling device\n",
|
||||
sst->irq);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sst;
|
||||
}
|
||||
|
||||
void skl_dsp_free(struct sst_dsp *dsp)
|
||||
{
|
||||
skl_ipc_int_disable(dsp);
|
||||
|
||||
free_irq(dsp->irq, dsp);
|
||||
skl_dsp_disable_core(dsp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_dsp_free);
|
||||
|
||||
bool is_skl_dsp_running(struct sst_dsp *ctx)
|
||||
{
|
||||
return (ctx->sst_state == SKL_DSP_RUNNING);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(is_skl_dsp_running);
|
@ -16,6 +16,8 @@
|
||||
#ifndef __SKL_SST_DSP_H__
|
||||
#define __SKL_SST_DSP_H__
|
||||
|
||||
struct sst_dsp_device;
|
||||
|
||||
/* Intel HD Audio General DSP Registers */
|
||||
#define SKL_ADSP_GEN_BASE 0x0
|
||||
#define SKL_ADSP_REG_ADSPCS (SKL_ADSP_GEN_BASE + 0x04)
|
||||
@ -59,4 +61,59 @@
|
||||
#define SKL_ADSPIC_IPC 1
|
||||
#define SKL_ADSPIS_IPC 1
|
||||
|
||||
/* ADSPCS - Audio DSP Control & Status */
|
||||
#define SKL_DSP_CORES 1
|
||||
#define SKL_DSP_CORE0_MASK 1
|
||||
#define SKL_DSP_CORES_MASK ((1 << SKL_DSP_CORES) - 1)
|
||||
|
||||
/* Core Reset - asserted high */
|
||||
#define SKL_ADSPCS_CRST_SHIFT 0
|
||||
#define SKL_ADSPCS_CRST_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CRST_SHIFT)
|
||||
#define SKL_ADSPCS_CRST(x) ((x << SKL_ADSPCS_CRST_SHIFT) & SKL_ADSPCS_CRST_MASK)
|
||||
|
||||
/* Core run/stall - when set to '1' core is stalled */
|
||||
#define SKL_ADSPCS_CSTALL_SHIFT 8
|
||||
#define SKL_ADSPCS_CSTALL_MASK (SKL_DSP_CORES_MASK << \
|
||||
SKL_ADSPCS_CSTALL_SHIFT)
|
||||
#define SKL_ADSPCS_CSTALL(x) ((x << SKL_ADSPCS_CSTALL_SHIFT) & \
|
||||
SKL_ADSPCS_CSTALL_MASK)
|
||||
|
||||
/* Set Power Active - when set to '1' turn cores on */
|
||||
#define SKL_ADSPCS_SPA_SHIFT 16
|
||||
#define SKL_ADSPCS_SPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_SPA_SHIFT)
|
||||
#define SKL_ADSPCS_SPA(x) ((x << SKL_ADSPCS_SPA_SHIFT) & SKL_ADSPCS_SPA_MASK)
|
||||
|
||||
/* Current Power Active - power status of cores, set by hardware */
|
||||
#define SKL_ADSPCS_CPA_SHIFT 24
|
||||
#define SKL_ADSPCS_CPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CPA_SHIFT)
|
||||
#define SKL_ADSPCS_CPA(x) ((x << SKL_ADSPCS_CPA_SHIFT) & SKL_ADSPCS_CPA_MASK)
|
||||
|
||||
#define SST_DSP_POWER_D0 0x0 /* full On */
|
||||
#define SST_DSP_POWER_D3 0x3 /* Off */
|
||||
|
||||
enum skl_dsp_states {
|
||||
SKL_DSP_RUNNING = 1,
|
||||
SKL_DSP_RESET,
|
||||
};
|
||||
|
||||
struct skl_dsp_fw_ops {
|
||||
int (*load_fw)(struct sst_dsp *ctx);
|
||||
/* FW module parser/loader */
|
||||
int (*parse_fw)(struct sst_dsp *ctx);
|
||||
int (*set_state_D0)(struct sst_dsp *ctx);
|
||||
int (*set_state_D3)(struct sst_dsp *ctx);
|
||||
};
|
||||
|
||||
void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state);
|
||||
struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
|
||||
struct sst_dsp_device *sst_dev, int irq);
|
||||
int skl_dsp_disable_core(struct sst_dsp *ctx);
|
||||
bool is_skl_dsp_running(struct sst_dsp *ctx);
|
||||
irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id);
|
||||
int skl_dsp_wake(struct sst_dsp *ctx);
|
||||
int skl_dsp_sleep(struct sst_dsp *ctx);
|
||||
void skl_dsp_free(struct sst_dsp *dsp);
|
||||
|
||||
int skl_dsp_boot(struct sst_dsp *ctx);
|
||||
|
||||
#endif /*__SKL_SST_DSP_H__*/
|
||||
|
Loading…
Reference in New Issue
Block a user