]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - sound/soc/intel/sst-haswell-dsp.c
Merge remote-tracking branches 'asoc/topic/wm8991', 'asoc/topic/wm8993', 'asoc/topic...
[karo-tx-linux.git] / sound / soc / intel / sst-haswell-dsp.c
index 4b6c163c10ff3ac3d207a360bb6eaef66f68fea2..57039b00efc222481f872e2c0426c85cdf84391d 100644 (file)
 #define SST_LP_SHIM_OFFSET     0xE7000
 #define SST_WPT_IRAM_OFFSET    0xA0000
 #define SST_LP_IRAM_OFFSET     0x80000
+#define SST_WPT_DSP_DRAM_OFFSET        0x400000
+#define SST_WPT_DSP_IRAM_OFFSET        0x00000
+#define SST_LPT_DSP_DRAM_OFFSET        0x400000
+#define SST_LPT_DSP_IRAM_OFFSET        0x00000
 
 #define SST_SHIM_PM_REG                0x84
 
@@ -86,9 +90,8 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 {
        struct dma_block_info *block;
        struct sst_module *mod;
-       struct sst_module_data block_data;
        struct sst_module_template template;
-       int count;
+       int count, ret;
        void __iomem *ram;
 
        /* TODO: allowed module types need to be configurable */
@@ -109,13 +112,9 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 
        memset(&template, 0, sizeof(template));
        template.id = module->type;
-       template.entry = module->entry_point;
-       template.p.size = module->info.persistent_size;
-       template.p.type = SST_MEM_DRAM;
-       template.p.data_type = SST_DATA_P;
-       template.s.size = module->info.scratch_size;
-       template.s.type = SST_MEM_DRAM;
-       template.s.data_type = SST_DATA_S;
+       template.entry = module->entry_point - 4;
+       template.persistent_size = module->info.persistent_size;
+       template.scratch_size = module->info.scratch_size;
 
        mod = sst_module_new(fw, &template, NULL);
        if (mod == NULL)
@@ -135,14 +134,14 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                switch (block->type) {
                case SST_HSW_IRAM:
                        ram = dsp->addr.lpe;
-                       block_data.offset =
+                       mod->offset =
                                block->ram_offset + dsp->addr.iram_offset;
-                       block_data.type = SST_MEM_IRAM;
+                       mod->type = SST_MEM_IRAM;
                        break;
                case SST_HSW_DRAM:
                        ram = dsp->addr.lpe;
-                       block_data.offset = block->ram_offset;
-                       block_data.type = SST_MEM_DRAM;
+                       mod->offset = block->ram_offset;
+                       mod->type = SST_MEM_DRAM;
                        break;
                default:
                        dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n",
@@ -151,30 +150,34 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                        return -EINVAL;
                }
 
-               block_data.size = block->size;
-               block_data.data_type = SST_DATA_M;
-               block_data.data = (void *)block + sizeof(*block);
-               block_data.data_offset = block_data.data - fw->dma_buf;
+               mod->size = block->size;
+               mod->data = (void *)block + sizeof(*block);
+               mod->data_offset = mod->data - fw->dma_buf;
 
-               dev_dbg(dsp->dev, "copy firmware block %d type 0x%x "
+               dev_dbg(dsp->dev, "module block %d type 0x%x "
                        "size 0x%x ==> ram %p offset 0x%x\n",
-                       count, block->type, block->size, ram,
+                       count, mod->type, block->size, ram,
                        block->ram_offset);
 
-               sst_module_insert_fixed_block(mod, &block_data);
+               ret = sst_module_alloc_blocks(mod);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: could not allocate blocks for module %d\n",
+                               count);
+                       sst_module_free(mod);
+                       return ret;
+               }
 
                block = (void *)block + sizeof(*block) + block->size;
        }
+
        return 0;
 }
 
 static int hsw_parse_fw_image(struct sst_fw *sst_fw)
 {
        struct fw_header *header;
-       struct sst_module *scratch;
        struct fw_module_header *module;
        struct sst_dsp *dsp = sst_fw->dsp;
-       struct sst_hsw *hsw = sst_fw->private;
        int ret, count;
 
        /* Read the header information from the data pointer */
@@ -204,12 +207,8 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw)
                module = (void *)module + sizeof(*module) + module->mod_size;
        }
 
-       /* allocate persistent/scratch mem regions */
-       scratch = sst_mem_block_alloc_scratch(dsp);
-       if (scratch == NULL)
-               return -ENOMEM;
-
-       sst_hsw_set_scratch_module(hsw, scratch);
+       /* allocate scratch mem regions */
+       sst_block_alloc_scratch(dsp);
 
        return 0;
 }
@@ -248,8 +247,94 @@ static irqreturn_t hsw_irq(int irq, void *context)
        return ret;
 }
 
-static void hsw_boot(struct sst_dsp *sst)
+static void hsw_set_dsp_D3(struct sst_dsp *sst)
+{
+       u32 val;
+       u32 reg;
+
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* enable power gating and switch off DRAM & IRAM blocks */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       val |= SST_VDRTCL0_DSRAMPGE_MASK |
+               SST_VDRTCL0_ISRAMPGE_MASK;
+       val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD);
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+       /* switch off audio PLL */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_APLLSE_MASK;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* disable MCLK(clkctl.smos = 0) */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
+               SST_CLKCTL_MASK, 0);
+
+       /* Set D3 state, delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_PMCS);
+       val |= SST_PMCS_PS_MASK;
+       writel(val, sst->addr.pci_cfg + SST_PMCS);
+       udelay(50);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
+}
+
+static void hsw_reset(struct sst_dsp *sst)
 {
+       /* put DSP into reset and stall */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+               SST_CSR_RST | SST_CSR_STALL,
+               SST_CSR_RST | SST_CSR_STALL);
+
+       /* keep in reset for 10ms */
+       mdelay(10);
+
+       /* take DSP out of reset and keep stalled for FW loading */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+               SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+}
+
+static int hsw_set_dsp_D0(struct sst_dsp *sst)
+{
+       int tries = 10;
+       u32 reg;
+
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* Disable D3PG (VDRTCTL0.D3PGD = 1) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       reg |= SST_VDRTCL0_D3PGD;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+       /* Set D0 state */
+       reg = readl(sst->addr.pci_cfg + SST_PMCS);
+       reg &= ~SST_PMCS_PS_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_PMCS);
+
+       /* check that ADSP shim is enabled */
+       while (tries--) {
+               reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK;
+               if (reg == 0)
+                       goto finish;
+
+               msleep(1);
+       }
+
+       return -ENODEV;
+
+finish:
        /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
                SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
@@ -264,34 +349,96 @@ static void hsw_boot(struct sst_dsp *sst)
                SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
                SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
 
+       /* Stall and reset core, set CSR */
+       hsw_reset(sst);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
+       /* switch on audio PLL */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~SST_VDRTCL2_APLLSE_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* set default power gating control, enable power gating control for all blocks. that is,
+       can't be accessed, please enable each block before accessing. */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+
        /* disable DMA finish function for SSP0 & SSP1 */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
                SST_CSR2_SDFD_SSP1);
 
-       /* enable DMA engine 0,1 all channels to access host memory */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_HMDC,
-               SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff),
-               SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff));
+       /* set on-demond mode on engine 0,1 for all channels */
+       sst_dsp_shim_update_bits(sst, SST_HMDC,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+       /* Enable Interrupt from both sides */
+       sst_dsp_shim_update_bits(sst, SST_IMRX, (SST_IMRX_BUSY | SST_IMRX_DONE),
+                                0x0);
+       sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY |
+                               SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0);
+
+       /* clear IPC registers */
+       sst_dsp_shim_write(sst, SST_IPCX, 0x0);
+       sst_dsp_shim_write(sst, SST_IPCD, 0x0);
+       sst_dsp_shim_write(sst, 0x80, 0x6);
+       sst_dsp_shim_write(sst, 0xe0, 0x300a);
+
+       return 0;
+}
 
-       /* disable all clock gating */
-       writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
+static void hsw_boot(struct sst_dsp *sst)
+{
+       /* set oportunistic mode on engine 0,1 for all channels */
+       sst_dsp_shim_update_bits(sst, SST_HMDC,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, 0);
 
        /* set DSP to RUN */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0);
 }
 
-static void hsw_reset(struct sst_dsp *sst)
+static void hsw_stall(struct sst_dsp *sst)
+{
+       /* stall DSP */
+       sst_dsp_shim_update_bits(sst, SST_CSR,
+               SST_CSR_24MHZ_LPCS | SST_CSR_STALL,
+               SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
+}
+
+static void hsw_sleep(struct sst_dsp *sst)
 {
+       dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n");
+
        /* put DSP into reset and stall */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-               SST_CSR_RST | SST_CSR_STALL, SST_CSR_RST | SST_CSR_STALL);
+       sst_dsp_shim_update_bits(sst, SST_CSR,
+               SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL,
+               SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
 
-       /* keep in reset for 10ms */
-       mdelay(10);
+       hsw_set_dsp_D3(sst);
+       dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
+}
 
-       /* take DSP out of reset and keep stalled for FW loading */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-               SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+static int hsw_wake(struct sst_dsp *sst)
+{
+       int ret;
+
+       dev_dbg(sst->dev, "HSW_PM dsp runtime resume\n");
+
+       ret = hsw_set_dsp_D0(sst);
+       if (ret < 0)
+               return ret;
+
+       dev_dbg(sst->dev, "HSW_PM dsp runtime resume exit\n");
+
+       return 0;
 }
 
 struct sst_adsp_memregion {
@@ -396,6 +543,11 @@ static int hsw_block_enable(struct sst_mem_block *block)
        dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n",
                block->type, block->index, block->offset);
 
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val &= ~SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
        val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
        bit = hsw_block_get_bit(block);
        writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0);
@@ -403,6 +555,13 @@ static int hsw_block_enable(struct sst_mem_block *block)
        /* wait 18 DSP clock ticks */
        udelay(10);
 
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
        /*add a dummy read before the SRAM block is written, otherwise the writing may miss bytes sometimes.*/
        sst_mem_block_dummy_read(block);
        return 0;
@@ -420,10 +579,26 @@ static int hsw_block_disable(struct sst_mem_block *block)
        dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n",
                block->type, block->index, block->offset);
 
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val &= ~SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+
        val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
        bit = hsw_block_get_bit(block);
        writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
 
+       /* wait 18 DSP clock ticks */
+       udelay(10);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
        return 0;
 }
 
@@ -432,27 +607,6 @@ static struct sst_block_ops sst_hsw_ops = {
        .disable = hsw_block_disable,
 };
 
-static int hsw_enable_shim(struct sst_dsp *sst)
-{
-       int tries = 10;
-       u32 reg;
-
-       /* enable shim */
-       reg = readl(sst->addr.pci_cfg + SST_SHIM_PM_REG);
-       writel(reg & ~0x3, sst->addr.pci_cfg + SST_SHIM_PM_REG);
-
-       /* check that ADSP shim is enabled */
-       while (tries--) {
-               reg = sst_dsp_shim_read_unlocked(sst, SST_CSR);
-               if (reg != 0xffffffff)
-                       return 0;
-
-               msleep(1);
-       }
-
-       return -ENODEV;
-}
-
 static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
 {
        const struct sst_adsp_memregion *region;
@@ -467,12 +621,16 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
                region = lp_region;
                region_count = ARRAY_SIZE(lp_region);
                sst->addr.iram_offset = SST_LP_IRAM_OFFSET;
+               sst->addr.dsp_iram_offset = SST_LPT_DSP_IRAM_OFFSET;
+               sst->addr.dsp_dram_offset = SST_LPT_DSP_DRAM_OFFSET;
                sst->addr.shim_offset = SST_LP_SHIM_OFFSET;
                break;
        case SST_DEV_ID_WILDCAT_POINT:
                region = wpt_region;
                region_count = ARRAY_SIZE(wpt_region);
                sst->addr.iram_offset = SST_WPT_IRAM_OFFSET;
+               sst->addr.dsp_iram_offset = SST_WPT_DSP_IRAM_OFFSET;
+               sst->addr.dsp_dram_offset = SST_WPT_DSP_DRAM_OFFSET;
                sst->addr.shim_offset = SST_WPT_SHIM_OFFSET;
                break;
        default:
@@ -487,7 +645,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
        }
 
        /* enable the DSP SHIM */
-       ret = hsw_enable_shim(sst);
+       ret = hsw_set_dsp_D0(sst);
        if (ret < 0) {
                dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n");
                return ret;
@@ -497,10 +655,6 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
        if (ret)
                return ret;
 
-       /* Enable Interrupt from both sides */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, 0x3, 0x0);
-       sst_dsp_shim_update_bits_unlocked(sst, SST_IMRD,
-               (0x3 | 0x1 << 16 | 0x3 << 21), 0x0);
 
        /* register DSP memory blocks - ideally we should get this from ACPI */
        for (i = 0; i < region_count; i++) {
@@ -532,6 +686,9 @@ static void hsw_free(struct sst_dsp *sst)
 struct sst_ops haswell_ops = {
        .reset = hsw_reset,
        .boot = hsw_boot,
+       .stall = hsw_stall,
+       .wake = hsw_wake,
+       .sleep = hsw_sleep,
        .write = sst_shim32_write,
        .read = sst_shim32_read,
        .write64 = sst_shim32_write64,