Commit a838dcc2 authored by Guneshwor Singh's avatar Guneshwor Singh Committed by Mark Brown
Browse files

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: default avatarGuneshwor Singh <guneshwor.o.singh@intel.com>
Acked-By: default avatarVinod Koul <vinod.koul@intel.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 4147a6e5
Loading
Loading
Loading
Loading
+274 −0
Original line number Diff line number Diff line
/*
 * 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);
}
+106 −0
Original line number Diff line number Diff line
/*
 * 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__*/