@@ -101,6 +101,34 @@
#define ADSPIC_IPC 1
#define ADSPIS_IPC 1
+/* ADSPCS - Audio DSP Control & Status */
+#define DSP_CORES 1
+#define DSP_CORE0_MASK 1
+#define DSP_CORES_MASK ((1 << DSP_CORES) - 1)
+
+/* Core Reset - asserted high */
+#define ADSPCS_CRST_SHIFT 0
+#define ADSPCS_CRST_MASK (DSP_CORES_MASK << ADSPCS_CRST_SHIFT)
+#define ADSPCS_CRST(x) ((x << ADSPCS_CRST_SHIFT) & ADSPCS_CRST_MASK)
+
+/* Core run/stall - when set to '1' core is stalled */
+#define ADSPCS_CSTALL_SHIFT 8
+#define ADSPCS_CSTALL_MASK (DSP_CORES_MASK << ADSPCS_CSTALL_SHIFT)
+#define ADSPCS_CSTALL(x) ((x << ADSPCS_CSTALL_SHIFT) & ADSPCS_CSTALL_MASK)
+
+/* Set Power Active - when set to '1' turn cores on */
+#define ADSPCS_SPA_SHIFT 16
+#define ADSPCS_SPA_MASK (DSP_CORES_MASK << ADSPCS_SPA_SHIFT)
+#define ADSPCS_SPA(x) ((x << ADSPCS_SPA_SHIFT) & ADSPCS_SPA_MASK)
+
+/* Current Power Active - power status of cores, set by hardware */
+#define ADSPCS_CPA_SHIFT 24
+#define ADSPCS_CPA_MASK (DSP_CORES_MASK << ADSPCS_CPA_SHIFT)
+#define ADSPCS_CPA(x) ((x << ADSPCS_CPA_SHIFT) & ADSPCS_CPA_MASK)
+
+#define SST_DSP_POWER_D0 0x0 /* full On */
+#define SST_DSP_POWER_D3 0x3 /* Off */
+
struct ssth_window {
void __iomem *w0stat;
void __iomem *w0up;
@@ -34,6 +34,11 @@
#include <sound/soc-hda-sst-dsp.h>
#include <sound/soc-hda-sst-ipc.h>
+#define DSP_CORE_POWER_UP_TIMEOUT 50
+#define DSP_CORE_POWER_DOWN_TIMEOUT 50
+#define DSP_CORE_SET_RESET_STATE_TIMEOUT 50
+#define DSP_CORE_UNSET_RESET_STATE_TIMEOUT 50
+
u8 ssth_readb_traced(struct ssth_lib *dsp, u32 offset)
{
u8 val;
@@ -135,5 +140,350 @@ void ssth_mailbox_read(struct ssth_lib *ctx, void *msg, size_t bytes)
_ssth_memcpy_fromio_32(msg, ctx->window.w0up, bytes);
}
+int ssth_register_poll(struct ssth_lib *ctx, u32 offset, u32 mask,
+ u32 expected_value, u32 timeout, char *operation)
+{
+ int time = 0;
+ int ret = 0;
+ u32 reg;
+
+ dev_dbg(ctx->dev, "In %s\n", __func__);
+
+ /* check if set state successful */
+ for (time = 0; time < timeout; time++) {
+ if ((ssth_readl_alt(ctx, offset) & mask) == expected_value)
+ break;
+
+ mdelay(1);
+ }
+ reg = ssth_readl_alt(ctx, offset);
+ dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
+ (time < timeout) ? "successful" : "timedout");
+ ret = time < timeout ? 0 : -ETIME;
+
+ return ret;
+}
+
+static int ssth_dsp_core_set_reset_state(struct ssth_lib *ctx)
+{
+ int ret = 0;
+
+ dev_dbg(ctx->dev, "In %s\n", __func__);
+
+ /* update bits */
+ ssth_updatel_locked(ctx, ADSPCS, CRST_MASK,
+ ADSPCS_CRST(DSP_CORES_MASK));
+
+ /* poll with timeout to check if operation successful */
+ ret = ssth_register_poll(ctx,
+ HDA_ADSP_REG_ADSPCS,
+ ADSPCS_CRST_MASK,
+ ADSPCS_CRST(DSP_CORES_MASK),
+ DSP_CORE_SET_RESET_STATE_TIMEOUT,
+ "Set reset");
+ if ((ssth_readl(ctx, ADSPCS) & ADSPCS_CRST(DSP_CORES_MASK)) !=
+ ADSPCS_CRST(DSP_CORES_MASK)) {
+ dev_err(ctx->dev, "Set reset state failed\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int ssth_dsp_core_unset_reset_state(struct ssth_lib *ctx)
+{
+ int ret = 0;
+
+ dev_dbg(ctx->dev, "In %s\n", __func__);
+
+ /* update bits */
+ ssth_updatel_locked(ctx, ADSPCS, CRST_MASK, 0);
+
+ /* poll with timeout to check if operation successful */
+ ret = ssth_register_poll(ctx,
+ HDA_ADSP_REG_ADSPCS,
+ ADSPCS_CRST_MASK,
+ 0,
+ DSP_CORE_UNSET_RESET_STATE_TIMEOUT,
+ "Unset reset");
+
+ if ((ssth_readl(ctx, ADSPCS) & ADSPCS_CRST(DSP_CORES_MASK)) != 0) {
+ dev_err(ctx->dev, "Unset reset state failed\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int ssth_dsp_core_power_up(struct ssth_lib *ctx)
+{
+ int ret = 0;
+
+ dev_dbg(ctx->dev, "In %s\n", __func__);
+ /* update bits */
+ ssth_updatel_locked(ctx,
+ ADSPCS, SPA_MASK, ADSPCS_SPA(DSP_CORES_MASK));
+
+ /* poll with timeout to check if operation successful */
+ ret = ssth_register_poll(ctx,
+ HDA_ADSP_REG_ADSPCS,
+ ADSPCS_CPA_MASK,
+ ADSPCS_CPA(DSP_CORES_MASK),
+ DSP_CORE_POWER_UP_TIMEOUT,
+ "Power up");
+
+ if ((ssth_readl(ctx, ADSPCS) & ADSPCS_CPA(DSP_CORES_MASK)) !=
+ ADSPCS_CPA(DSP_CORES_MASK)) {
+ dev_err(ctx->dev, "DSP core power up failed\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int ssth_dsp_core_power_down(struct ssth_lib *ctx)
+{
+ int ret = 0;
+
+ dev_dbg(ctx->dev, "In %s\n", __func__);
+
+ /* update bits */
+ ssth_updatel_locked(ctx, ADSPCS, SPA_MASK, 0);
+
+ /* poll with timeout to check if operation successful */
+ ret = ssth_register_poll(ctx,
+ HDA_ADSP_REG_ADSPCS,
+ ADSPCS_SPA_MASK,
+ 0,
+ DSP_CORE_POWER_DOWN_TIMEOUT,
+ "Power down");
+
+ return ret;
+}
+
+static bool ssth_is_dsp_core_enable(struct ssth_lib *ctx)
+{
+ int val = 0;
+ bool is_enable;
+
+ val = ssth_readl(ctx, ADSPCS);
+
+ is_enable = ((val & ADSPCS_CPA(DSP_CORES_MASK)) &&
+ (val & ADSPCS_SPA(DSP_CORES_MASK)) &&
+ !(val & ADSPCS_CRST(DSP_CORES_MASK)) &&
+ !(val & ADSPCS_CSTALL(DSP_CORES_MASK)));
+
+ dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable);
+ return is_enable;
+}
+
+
+int ssth_enable_dsp_core(struct ssth_lib *ctx)
+{
+ int ret = 0;
+
+ dev_dbg(ctx->dev, "In %s\n", __func__);
+
+ /* power up */
+ ret = ssth_dsp_core_power_up(ctx);
+ if (ret < 0) {
+ dev_dbg(ctx->dev, "dsp core power up failed\n");
+ return ret;
+ }
+
+ /* unset reset state */
+ ret = ssth_dsp_core_unset_reset_state(ctx);
+ if (ret < 0) {
+ dev_dbg(ctx->dev, "dsp core reset failed\n");
+ return ret;
+ }
+
+ /* run core */
+ dev_dbg(ctx->dev, "run core...\n");
+ ssth_writel(ctx, ADSPCS, ssth_readl(ctx, ADSPCS) &
+ ~ADSPCS_CSTALL(DSP_CORES_MASK));
+
+ if (!ssth_is_dsp_core_enable(ctx)) {
+ dev_err(ctx->dev, "DSP core enable failed\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+int ssth_disable_dsp_core(struct ssth_lib *ctx)
+{
+ int ret = 0;
+
+ dev_dbg(ctx->dev, "In %s\n", __func__);
+
+ /* stall core */
+ ssth_writel(ctx, ADSPCS, ssth_readl(ctx, ADSPCS) |
+ ADSPCS_CSTALL(DSP_CORES_MASK));
+
+ /* set reset state */
+ ret = ssth_dsp_core_set_reset_state(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp core reset failed\n");
+ return ret;
+ }
+
+ /* power down core*/
+ ret = ssth_dsp_core_power_down(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp core power down failed\n");
+ return ret;
+ }
+
+ if (ssth_is_dsp_core_enable(ctx)) {
+ dev_err(ctx->dev, "DSP core disable failed\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int ssth_reset_dsp_core(struct ssth_lib *ctx)
+{
+ int ret = 0;
+
+ dev_dbg(ctx->dev, "In %s\n", __func__);
+
+ /* stall core */
+ ssth_writel(ctx, ADSPCS, ssth_readl(ctx, ADSPCS) &
+ ADSPCS_CSTALL(DSP_CORES_MASK));
+
+ /* set reset state */
+ ret = ssth_dsp_core_set_reset_state(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp reset failed\n");
+ return ret;
+ }
+
+ /* unset reset state */
+ ret = ssth_dsp_core_unset_reset_state(ctx);
+ if (ret < 0) {
+ dev_dbg(ctx->dev, "dsp unset reset failes\n");
+ return ret;
+ }
+
+ /* run core */
+ dev_dbg(ctx->dev, "run core...\n");
+ ssth_writel(ctx, ADSPCS, ssth_readl(ctx, ADSPCS) &
+ ~ADSPCS_CSTALL(DSP_CORES_MASK));
+
+ if (ssth_is_dsp_core_enable(ctx)) {
+ dev_err(ctx->dev, "DSP core reset failed\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+int ssth_boot_dsp(struct ssth_lib *ctx)
+{
+ int ret = 0;
+
+ dev_dbg(ctx->dev, "In %s\n", __func__);
+
+ if (ssth_is_dsp_core_enable(ctx)) {
+ /* Dsp core powered up - simply reset core */
+ dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n");
+ ret = ssth_reset_dsp_core(ctx);
+ } else {
+ /*disable and enable to make sure DSP is invalid state */
+ ret = ssth_disable_dsp_core(ctx);
+
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp disable core failes\n");
+ return ret;
+ }
+ ret = ssth_enable_dsp_core(ctx);
+ }
+
+ return ret;
+}
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t ssth_interrupt(int irq, void *dev_id)
+{
+ struct ssth_lib *ctx = (struct ssth_lib *) dev_id;
+ u32 val;
+ irqreturn_t result = IRQ_NONE;
+
+ spin_lock(&ctx->reg_lock);
+
+ val = ssth_readl(ctx, ADSPIS);
+
+ if (val & ADSPIS_IPC) {
+ ssth_ipc_int_disable(ctx);
+ queue_work(ctx->intr_wq, &ctx->ipc_process_msg_work);
+ result = IRQ_HANDLED;
+ }
+
+ spin_unlock(&ctx->reg_lock);
+ return result;
+}
+
+static int ssth_acquire_irq(struct ssth_lib *ctx)
+{
+ if (request_threaded_irq(ctx->irq, ssth_interrupt,
+ NULL, IRQF_SHARED, KBUILD_MODNAME, ctx)) {
+ dev_err(ctx->dev, "unable to grab threaded IRQ %d, disabling device\n", ctx->irq);
+ return -1;
+ }
+ return 0;
+}
+
+int ssth_dsp_init(struct ssth_lib *ctx)
+{
+ int ret = 0;
+
+ mutex_init(&ctx->sst_lock);
+ spin_lock_init(&ctx->reg_lock);
+
+ dev_dbg(ctx->dev, "In %s\n", __func__);
+
+ /* initialize IPC */
+ ctx->ipc = ssth_ipc_init(ctx->dev, ctx);
+ if (ctx->ipc == NULL)
+ ret = -ENODEV;
+
+ /* Now let's request the IRQ */
+ ssth_acquire_irq(ctx);
+
+ return ret;
+}
+
+int ssth_dsp_free0(struct ssth_lib *dsp)
+{
+ int ret = 0;
+
+ dev_dbg(dsp->dev, "In %s\n", __func__);
+
+ ssth_ipc_int_disable(dsp);
+
+ free_irq(dsp->irq, dsp);
+ ssth_ipc_free(dsp->ipc);
+ ssth_disable_dsp_core(dsp);
+ kfree(dsp);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ssth_dsp_free0);
+
+bool ssth_dsp_is_running(struct ssth_lib *ctx)
+{
+ bool ret = 0;
+
+ mutex_lock(&ctx->sst_lock);
+ ret = (ctx->sst_state == SST_DSP_RUNNING) ? 1 : 0;
+ mutex_unlock(&ctx->sst_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ssth_dsp_is_running);
+
MODULE_DESCRIPTION("HDA SST/IPC Library");
MODULE_LICENSE("GPL v2");