ASoC: Intel: cnl: Add cnl dsp functions and registers
This adds Cannonlake specific registers and support for CNL dsp related library functions for programming the registers to power up/down dsp cores, set/unset reset states for each core, enable/disable ipc interrupts and few wrappers to be called from elsewhere. Signed-off-by: Guneshwor Singh <guneshwor.o.singh@intel.com> Acked-By: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Родитель
4147a6e59a
Коммит
a838dcc286
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* cnl-sst-dsp.c - CNL SST library generic function
|
||||
*
|
||||
* Copyright (C) 2016-17, Intel Corporation.
|
||||
* Author: Guneshwor Singh <guneshwor.o.singh@intel.com>
|
||||
*
|
||||
* Modified from:
|
||||
* SKL SST library generic function
|
||||
* Copyright (C) 2014-15, Intel Corporation.
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* 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 <linux/device.h>
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-ipc.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
#include "cnl-sst-dsp.h"
|
||||
|
||||
/* various timeout values */
|
||||
#define CNL_DSP_PU_TO 50
|
||||
#define CNL_DSP_PD_TO 50
|
||||
#define CNL_DSP_RESET_TO 50
|
||||
|
||||
static int
|
||||
cnl_dsp_core_set_reset_state(struct sst_dsp *ctx, unsigned int core_mask)
|
||||
{
|
||||
/* update bits */
|
||||
sst_dsp_shim_update_bits_unlocked(ctx,
|
||||
CNL_ADSP_REG_ADSPCS, CNL_ADSPCS_CRST(core_mask),
|
||||
CNL_ADSPCS_CRST(core_mask));
|
||||
|
||||
/* poll with timeout to check if operation successful */
|
||||
return sst_dsp_register_poll(ctx,
|
||||
CNL_ADSP_REG_ADSPCS,
|
||||
CNL_ADSPCS_CRST(core_mask),
|
||||
CNL_ADSPCS_CRST(core_mask),
|
||||
CNL_DSP_RESET_TO,
|
||||
"Set reset");
|
||||
}
|
||||
|
||||
static int
|
||||
cnl_dsp_core_unset_reset_state(struct sst_dsp *ctx, unsigned int core_mask)
|
||||
{
|
||||
/* update bits */
|
||||
sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPCS,
|
||||
CNL_ADSPCS_CRST(core_mask), 0);
|
||||
|
||||
/* poll with timeout to check if operation successful */
|
||||
return sst_dsp_register_poll(ctx,
|
||||
CNL_ADSP_REG_ADSPCS,
|
||||
CNL_ADSPCS_CRST(core_mask),
|
||||
0,
|
||||
CNL_DSP_RESET_TO,
|
||||
"Unset reset");
|
||||
}
|
||||
|
||||
static bool is_cnl_dsp_core_enable(struct sst_dsp *ctx, unsigned int core_mask)
|
||||
{
|
||||
int val;
|
||||
bool is_enable;
|
||||
|
||||
val = sst_dsp_shim_read_unlocked(ctx, CNL_ADSP_REG_ADSPCS);
|
||||
|
||||
is_enable = (val & CNL_ADSPCS_CPA(core_mask)) &&
|
||||
(val & CNL_ADSPCS_SPA(core_mask)) &&
|
||||
!(val & CNL_ADSPCS_CRST(core_mask)) &&
|
||||
!(val & CNL_ADSPCS_CSTALL(core_mask));
|
||||
|
||||
dev_dbg(ctx->dev, "DSP core(s) enabled? %d: core_mask %#x\n",
|
||||
is_enable, core_mask);
|
||||
|
||||
return is_enable;
|
||||
}
|
||||
|
||||
static int cnl_dsp_reset_core(struct sst_dsp *ctx, unsigned int core_mask)
|
||||
{
|
||||
/* stall core */
|
||||
sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPCS,
|
||||
CNL_ADSPCS_CSTALL(core_mask),
|
||||
CNL_ADSPCS_CSTALL(core_mask));
|
||||
|
||||
/* set reset state */
|
||||
return cnl_dsp_core_set_reset_state(ctx, core_mask);
|
||||
}
|
||||
|
||||
static int cnl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* unset reset state */
|
||||
ret = cnl_dsp_core_unset_reset_state(ctx, core_mask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* run core */
|
||||
sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPCS,
|
||||
CNL_ADSPCS_CSTALL(core_mask), 0);
|
||||
|
||||
if (!is_cnl_dsp_core_enable(ctx, core_mask)) {
|
||||
cnl_dsp_reset_core(ctx, core_mask);
|
||||
dev_err(ctx->dev, "DSP core mask %#x enable failed\n",
|
||||
core_mask);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cnl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask)
|
||||
{
|
||||
/* update bits */
|
||||
sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPCS,
|
||||
CNL_ADSPCS_SPA(core_mask),
|
||||
CNL_ADSPCS_SPA(core_mask));
|
||||
|
||||
/* poll with timeout to check if operation successful */
|
||||
return sst_dsp_register_poll(ctx, CNL_ADSP_REG_ADSPCS,
|
||||
CNL_ADSPCS_CPA(core_mask),
|
||||
CNL_ADSPCS_CPA(core_mask),
|
||||
CNL_DSP_PU_TO,
|
||||
"Power up");
|
||||
}
|
||||
|
||||
static int cnl_dsp_core_power_down(struct sst_dsp *ctx, unsigned int core_mask)
|
||||
{
|
||||
/* update bits */
|
||||
sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPCS,
|
||||
CNL_ADSPCS_SPA(core_mask), 0);
|
||||
|
||||
/* poll with timeout to check if operation successful */
|
||||
return sst_dsp_register_poll(ctx,
|
||||
CNL_ADSP_REG_ADSPCS,
|
||||
CNL_ADSPCS_CPA(core_mask),
|
||||
0,
|
||||
CNL_DSP_PD_TO,
|
||||
"Power down");
|
||||
}
|
||||
|
||||
int cnl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* power up */
|
||||
ret = cnl_dsp_core_power_up(ctx, core_mask);
|
||||
if (ret < 0) {
|
||||
dev_dbg(ctx->dev, "DSP core mask %#x power up failed",
|
||||
core_mask);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return cnl_dsp_start_core(ctx, core_mask);
|
||||
}
|
||||
|
||||
int cnl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = cnl_dsp_reset_core(ctx, core_mask);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "DSP core mask %#x reset failed\n",
|
||||
core_mask);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* power down core*/
|
||||
ret = cnl_dsp_core_power_down(ctx, core_mask);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "DSP core mask %#x power down failed\n",
|
||||
core_mask);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (is_cnl_dsp_core_enable(ctx, core_mask)) {
|
||||
dev_err(ctx->dev, "DSP core mask %#x disable failed\n",
|
||||
core_mask);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
irqreturn_t cnl_dsp_sst_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct sst_dsp *ctx = dev_id;
|
||||
u32 val;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
spin_lock(&ctx->spinlock);
|
||||
|
||||
val = sst_dsp_shim_read_unlocked(ctx, CNL_ADSP_REG_ADSPIS);
|
||||
ctx->intr_status = val;
|
||||
|
||||
if (val == 0xffffffff) {
|
||||
spin_unlock(&ctx->spinlock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (val & CNL_ADSPIS_IPC) {
|
||||
cnl_ipc_int_disable(ctx);
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
spin_unlock(&ctx->spinlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cnl_dsp_free(struct sst_dsp *dsp)
|
||||
{
|
||||
cnl_ipc_int_disable(dsp);
|
||||
|
||||
free_irq(dsp->irq, dsp);
|
||||
cnl_ipc_op_int_disable(dsp);
|
||||
cnl_dsp_disable_core(dsp, SKL_DSP_CORE0_MASK);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cnl_dsp_free);
|
||||
|
||||
void cnl_ipc_int_enable(struct sst_dsp *ctx)
|
||||
{
|
||||
sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_ADSPIC,
|
||||
CNL_ADSPIC_IPC, CNL_ADSPIC_IPC);
|
||||
}
|
||||
|
||||
void cnl_ipc_int_disable(struct sst_dsp *ctx)
|
||||
{
|
||||
sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPIC,
|
||||
CNL_ADSPIC_IPC, 0);
|
||||
}
|
||||
|
||||
void cnl_ipc_op_int_enable(struct sst_dsp *ctx)
|
||||
{
|
||||
/* enable IPC DONE interrupt */
|
||||
sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_HIPCCTL,
|
||||
CNL_ADSP_REG_HIPCCTL_DONE,
|
||||
CNL_ADSP_REG_HIPCCTL_DONE);
|
||||
|
||||
/* enable IPC BUSY interrupt */
|
||||
sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_HIPCCTL,
|
||||
CNL_ADSP_REG_HIPCCTL_BUSY,
|
||||
CNL_ADSP_REG_HIPCCTL_BUSY);
|
||||
}
|
||||
|
||||
void cnl_ipc_op_int_disable(struct sst_dsp *ctx)
|
||||
{
|
||||
/* disable IPC DONE interrupt */
|
||||
sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_HIPCCTL,
|
||||
CNL_ADSP_REG_HIPCCTL_DONE, 0);
|
||||
|
||||
/* disable IPC BUSY interrupt */
|
||||
sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_HIPCCTL,
|
||||
CNL_ADSP_REG_HIPCCTL_BUSY, 0);
|
||||
}
|
||||
|
||||
bool cnl_ipc_int_status(struct sst_dsp *ctx)
|
||||
{
|
||||
return sst_dsp_shim_read_unlocked(ctx, CNL_ADSP_REG_ADSPIS) &
|
||||
CNL_ADSPIS_IPC;
|
||||
}
|
||||
|
||||
void cnl_ipc_free(struct sst_generic_ipc *ipc)
|
||||
{
|
||||
cnl_ipc_op_int_disable(ipc->dsp);
|
||||
sst_ipc_fini(ipc);
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Cannonlake SST DSP Support
|
||||
*
|
||||
* Copyright (C) 2016-17, Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __CNL_SST_DSP_H__
|
||||
#define __CNL_SST_DSP_H__
|
||||
|
||||
struct sst_dsp;
|
||||
struct skl_sst;
|
||||
struct sst_dsp_device;
|
||||
struct sst_generic_ipc;
|
||||
|
||||
/* Intel HD Audio General DSP Registers */
|
||||
#define CNL_ADSP_GEN_BASE 0x0
|
||||
#define CNL_ADSP_REG_ADSPCS (CNL_ADSP_GEN_BASE + 0x04)
|
||||
#define CNL_ADSP_REG_ADSPIC (CNL_ADSP_GEN_BASE + 0x08)
|
||||
#define CNL_ADSP_REG_ADSPIS (CNL_ADSP_GEN_BASE + 0x0c)
|
||||
|
||||
/* Intel HD Audio Inter-Processor Communication Registers */
|
||||
#define CNL_ADSP_IPC_BASE 0xc0
|
||||
#define CNL_ADSP_REG_HIPCTDR (CNL_ADSP_IPC_BASE + 0x00)
|
||||
#define CNL_ADSP_REG_HIPCTDA (CNL_ADSP_IPC_BASE + 0x04)
|
||||
#define CNL_ADSP_REG_HIPCTDD (CNL_ADSP_IPC_BASE + 0x08)
|
||||
#define CNL_ADSP_REG_HIPCIDR (CNL_ADSP_IPC_BASE + 0x10)
|
||||
#define CNL_ADSP_REG_HIPCIDA (CNL_ADSP_IPC_BASE + 0x14)
|
||||
#define CNL_ADSP_REG_HIPCIDD (CNL_ADSP_IPC_BASE + 0x18)
|
||||
#define CNL_ADSP_REG_HIPCCTL (CNL_ADSP_IPC_BASE + 0x28)
|
||||
|
||||
/* HIPCTDR */
|
||||
#define CNL_ADSP_REG_HIPCTDR_BUSY BIT(31)
|
||||
|
||||
/* HIPCTDA */
|
||||
#define CNL_ADSP_REG_HIPCTDA_DONE BIT(31)
|
||||
|
||||
/* HIPCIDR */
|
||||
#define CNL_ADSP_REG_HIPCIDR_BUSY BIT(31)
|
||||
|
||||
/* HIPCIDA */
|
||||
#define CNL_ADSP_REG_HIPCIDA_DONE BIT(31)
|
||||
|
||||
/* CNL HIPCCTL */
|
||||
#define CNL_ADSP_REG_HIPCCTL_DONE BIT(1)
|
||||
#define CNL_ADSP_REG_HIPCCTL_BUSY BIT(0)
|
||||
|
||||
/* CNL HIPCT */
|
||||
#define CNL_ADSP_REG_HIPCT_BUSY BIT(31)
|
||||
|
||||
/* Intel HD Audio SRAM Window 1 */
|
||||
#define CNL_ADSP_SRAM1_BASE 0xa0000
|
||||
|
||||
#define CNL_ADSP_MMIO_LEN 0x10000
|
||||
|
||||
#define CNL_ADSP_W0_STAT_SZ 0x1000
|
||||
|
||||
#define CNL_ADSP_W0_UP_SZ 0x1000
|
||||
|
||||
#define CNL_ADSP_W1_SZ 0x1000
|
||||
|
||||
#define CNL_FW_STS_MASK 0xf
|
||||
|
||||
#define CNL_ADSPIC_IPC 0x1
|
||||
#define CNL_ADSPIS_IPC 0x1
|
||||
|
||||
#define CNL_DSP_CORES 4
|
||||
#define CNL_DSP_CORES_MASK ((1 << CNL_DSP_CORES) - 1)
|
||||
|
||||
/* core reset - asserted high */
|
||||
#define CNL_ADSPCS_CRST_SHIFT 0
|
||||
#define CNL_ADSPCS_CRST(x) (x << CNL_ADSPCS_CRST_SHIFT)
|
||||
|
||||
/* core run/stall - when set to 1 core is stalled */
|
||||
#define CNL_ADSPCS_CSTALL_SHIFT 8
|
||||
#define CNL_ADSPCS_CSTALL(x) (x << CNL_ADSPCS_CSTALL_SHIFT)
|
||||
|
||||
/* set power active - when set to 1 turn core on */
|
||||
#define CNL_ADSPCS_SPA_SHIFT 16
|
||||
#define CNL_ADSPCS_SPA(x) (x << CNL_ADSPCS_SPA_SHIFT)
|
||||
|
||||
/* current power active - power status of cores, set by hardware */
|
||||
#define CNL_ADSPCS_CPA_SHIFT 24
|
||||
#define CNL_ADSPCS_CPA(x) (x << CNL_ADSPCS_CPA_SHIFT)
|
||||
|
||||
int cnl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core);
|
||||
int cnl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core);
|
||||
irqreturn_t cnl_dsp_sst_interrupt(int irq, void *dev_id);
|
||||
void cnl_dsp_free(struct sst_dsp *dsp);
|
||||
|
||||
void cnl_ipc_int_enable(struct sst_dsp *ctx);
|
||||
void cnl_ipc_int_disable(struct sst_dsp *ctx);
|
||||
void cnl_ipc_op_int_enable(struct sst_dsp *ctx);
|
||||
void cnl_ipc_op_int_disable(struct sst_dsp *ctx);
|
||||
bool cnl_ipc_int_status(struct sst_dsp *ctx);
|
||||
void cnl_ipc_free(struct sst_generic_ipc *ipc);
|
||||
|
||||
#endif /*__CNL_SST_DSP_H__*/
|
Загрузка…
Ссылка в новой задаче