ASoC: Intel: haswell: Fix power transition refactor
authorCezary Rojewski <cezary.rojewski@intel.com>
Tue, 1 Sep 2020 15:30:41 +0000 (17:30 +0200)
committerMark Brown <broonie@kernel.org>
Tue, 1 Sep 2020 16:00:45 +0000 (17:00 +0100)
While addressing existing power-cycle limitations for
sound/soc/intel/haswell solution, change brings regression for standard
audio userspace flows e.g.: when using PulseAudio.

Occasional sound-card initialization fail is still better than
permanent audio distortions, so revert the change.

Fixes: 8ec7d6043263 ("ASoC: Intel: haswell: Power transition refactor")
Reported-by: Christian Bundy <christianbundy@fraction.io>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20200901153041.14771-1-cezary.rojewski@intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/intel/haswell/sst-haswell-dsp.c

index de80e19..88c3f63 100644 (file)
@@ -243,92 +243,45 @@ static irqreturn_t hsw_irq(int irq, void *context)
        return ret;
 }
 
-#define CSR_DEFAULT_VALUE 0x8480040E
-#define ISC_DEFAULT_VALUE 0x0
-#define ISD_DEFAULT_VALUE 0x0
-#define IMC_DEFAULT_VALUE 0x7FFF0003
-#define IMD_DEFAULT_VALUE 0x7FFF0003
-#define IPCC_DEFAULT_VALUE 0x0
-#define IPCD_DEFAULT_VALUE 0x0
-#define CLKCTL_DEFAULT_VALUE 0x7FF
-#define CSR2_DEFAULT_VALUE 0x0
-#define LTR_CTRL_DEFAULT_VALUE 0x0
-#define HMD_CTRL_DEFAULT_VALUE 0x0
-
-static void hsw_set_shim_defaults(struct sst_dsp *sst)
-{
-       sst_dsp_shim_write_unlocked(sst, SST_CSR, CSR_DEFAULT_VALUE);
-       sst_dsp_shim_write_unlocked(sst, SST_ISRX, ISC_DEFAULT_VALUE);
-       sst_dsp_shim_write_unlocked(sst, SST_ISRD, ISD_DEFAULT_VALUE);
-       sst_dsp_shim_write_unlocked(sst, SST_IMRX, IMC_DEFAULT_VALUE);
-       sst_dsp_shim_write_unlocked(sst, SST_IMRD, IMD_DEFAULT_VALUE);
-       sst_dsp_shim_write_unlocked(sst, SST_IPCX, IPCC_DEFAULT_VALUE);
-       sst_dsp_shim_write_unlocked(sst, SST_IPCD, IPCD_DEFAULT_VALUE);
-       sst_dsp_shim_write_unlocked(sst, SST_CLKCTL, CLKCTL_DEFAULT_VALUE);
-       sst_dsp_shim_write_unlocked(sst, SST_CSR2, CSR2_DEFAULT_VALUE);
-       sst_dsp_shim_write_unlocked(sst, SST_LTRC, LTR_CTRL_DEFAULT_VALUE);
-       sst_dsp_shim_write_unlocked(sst, SST_HMDC, HMD_CTRL_DEFAULT_VALUE);
-}
-
-/* all clock-gating minus DCLCGE and DTCGE */
-#define SST_VDRTCL2_CG_OTHER   0xB7D
-
 static void hsw_set_dsp_D3(struct sst_dsp *sst)
 {
+       u32 val;
        u32 reg;
 
-       /* disable clock core gating */
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
        reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
-       reg &= ~(SST_VDRTCL2_DCLCGE);
+       reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
        writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
 
-       /* stall, reset and set 24MHz XOSC */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-                       SST_CSR_24MHZ_LPCS | SST_CSR_STALL | SST_CSR_RST,
-                       SST_CSR_24MHZ_LPCS | SST_CSR_STALL | SST_CSR_RST);
-
-       /* DRAM power gating all */
-       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
-       reg |= SST_VDRTCL0_ISRAMPGE_MASK |
-               SST_VDRTCL0_DSRAMPGE_MASK;
-       reg &= ~(SST_VDRTCL0_D3SRAMPGD);
-       reg |= SST_VDRTCL0_D3PGD;
-       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
-       udelay(50);
+       /* 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);
 
-       /* PLL shutdown enable */
-       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
-       reg |= SST_VDRTCL2_APLLSE_MASK;
-       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+       /* 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 */
+       /* disable MCLK(clkctl.smos = 0) */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
-                       SST_CLKCTL_MASK, 0);
-
-       /* switch clock gating */
-       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
-       reg |= SST_VDRTCL2_CG_OTHER;
-       reg &= ~(SST_VDRTCL2_DTCGE);
-       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
-       /* enable DTCGE separatelly */
-       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
-       reg |= SST_VDRTCL2_DTCGE;
-       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+               SST_CLKCTL_MASK, 0);
 
-       /* set shim defaults */
-       hsw_set_shim_defaults(sst);
-
-       /* set D3 */
-       reg = readl(sst->addr.pci_cfg + SST_PMCS);
-       reg |= SST_PMCS_PS_MASK;
-       writel(reg, sst->addr.pci_cfg + SST_PMCS);
+       /* 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 clock core gating */
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
        reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
-       reg |= SST_VDRTCL2_DCLCGE;
+       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)
@@ -346,62 +299,75 @@ static void hsw_reset(struct sst_dsp *sst)
                SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
 }
 
-/* recommended CSR state for power-up */
-#define SST_CSR_D0_MASK (0x18A09C0C | SST_CSR_DCS_MASK)
-
 static int hsw_set_dsp_D0(struct sst_dsp *sst)
 {
-       u32 reg;
+       int tries = 10;
+       u32 reg, fw_dump_bit;
 
-       /* disable clock core gating */
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
        reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
-       reg &= ~(SST_VDRTCL2_DCLCGE);
+       reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
        writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
 
-       /* switch clock gating */
-       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
-       reg |= SST_VDRTCL2_CG_OTHER;
-       reg &= ~(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 */
+       /* Set D0 state */
        reg = readl(sst->addr.pci_cfg + SST_PMCS);
-       reg &= ~(SST_PMCS_PS_MASK);
+       reg &= ~SST_PMCS_PS_MASK;
        writel(reg, sst->addr.pci_cfg + SST_PMCS);
 
-       /* DRAM power gating none */
-       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
-       reg &= ~(SST_VDRTCL0_ISRAMPGE_MASK |
-               SST_VDRTCL0_DSRAMPGE_MASK);
-       reg |= SST_VDRTCL0_D3SRAMPGD;
-       reg |= SST_VDRTCL0_D3PGD;
-       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
-       mdelay(10);
+       /* 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;
 
-       /* set shim defaults */
-       hsw_set_shim_defaults(sst);
+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);
+
+       /* stall DSP core, set clk to 192/96Mhz */
+       sst_dsp_shim_update_bits_unlocked(sst,
+               SST_CSR, SST_CSR_STALL | SST_CSR_DCS_MASK,
+               SST_CSR_STALL | SST_CSR_DCS(4));
 
-       /* restore MCLK */
+       /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
-                       SST_CLKCTL_MASK, SST_CLKCTL_MASK);
+               SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
+               SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
 
-       /* PLL shutdown disable */
+       /* 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_APLLSE_MASK);
+       reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
        writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
 
-       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-                       SST_CSR_D0_MASK, SST_CSR_SBCS0 | SST_CSR_SBCS1 |
-                       SST_CSR_STALL | SST_CSR_DCS(4));
        udelay(50);
 
-       /* enable clock core gating */
+       /* switch on audio PLL */
        reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
-       reg |= SST_VDRTCL2_DCLCGE;
+       reg &= ~SST_VDRTCL2_APLLSE_MASK;
        writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
 
-       /* clear reset */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_RST, 0);
+       /* 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;
+       /* for D0, always enable the block(DSRAM[0]) used for FW dump */
+       fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT;
+       writel(reg & ~fw_dump_bit, 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,
@@ -418,6 +384,12 @@ static int hsw_set_dsp_D0(struct sst_dsp *sst)
        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;
 }
 
@@ -443,6 +415,11 @@ 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(sst, SST_CSR,
+               SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL,
+               SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
+
        hsw_set_dsp_D3(sst);
        dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
 }