xref: /linux/drivers/soundwire/intel.c (revision a320f41eac7b250c27db1c8071a984e584109ce1)
171bb8a1bSVinod Koul // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
271bb8a1bSVinod Koul // Copyright(c) 2015-17 Intel Corporation.
371bb8a1bSVinod Koul 
471bb8a1bSVinod Koul /*
571bb8a1bSVinod Koul  * Soundwire Intel Master Driver
671bb8a1bSVinod Koul  */
771bb8a1bSVinod Koul 
871bb8a1bSVinod Koul #include <linux/acpi.h>
979ee6631SPierre-Louis Bossart #include <linux/debugfs.h>
1071bb8a1bSVinod Koul #include <linux/delay.h>
114abbd783SPaul Gortmaker #include <linux/module.h>
1271bb8a1bSVinod Koul #include <linux/interrupt.h>
13df72b719SPierre-Louis Bossart #include <linux/io.h>
1471bb8a1bSVinod Koul #include <linux/platform_device.h>
1537a2d22bSVinod Koul #include <sound/pcm_params.h>
16ab2c9132SRander Wang #include <linux/pm_runtime.h>
1737a2d22bSVinod Koul #include <sound/soc.h>
1871bb8a1bSVinod Koul #include <linux/soundwire/sdw_registers.h>
1971bb8a1bSVinod Koul #include <linux/soundwire/sdw.h>
2071bb8a1bSVinod Koul #include <linux/soundwire/sdw_intel.h>
2171bb8a1bSVinod Koul #include "cadence_master.h"
2279ee6631SPierre-Louis Bossart #include "bus.h"
2371bb8a1bSVinod Koul #include "intel.h"
2471bb8a1bSVinod Koul 
25ebf878edSPierre-Louis Bossart #define INTEL_MASTER_SUSPEND_DELAY_MS	3000
26ebf878edSPierre-Louis Bossart 
27ebf878edSPierre-Louis Bossart /*
28ebf878edSPierre-Louis Bossart  * debug/config flags for the Intel SoundWire Master.
29ebf878edSPierre-Louis Bossart  *
30ebf878edSPierre-Louis Bossart  * Since we may have multiple masters active, we can have up to 8
31ebf878edSPierre-Louis Bossart  * flags reused in each byte, with master0 using the ls-byte, etc.
32ebf878edSPierre-Louis Bossart  */
33ebf878edSPierre-Louis Bossart 
34ebf878edSPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME		BIT(0)
35ebf878edSPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP		BIT(1)
36a2d9c161SPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE	BIT(2)
37ebf878edSPierre-Louis Bossart 
38ebf878edSPierre-Louis Bossart static int md_flags;
39ebf878edSPierre-Louis Bossart module_param_named(sdw_md_flags, md_flags, int, 0444);
40ebf878edSPierre-Louis Bossart MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)");
41ebf878edSPierre-Louis Bossart 
4271bb8a1bSVinod Koul /* Intel SHIM Registers Definition */
4371bb8a1bSVinod Koul #define SDW_SHIM_LCAP			0x0
4471bb8a1bSVinod Koul #define SDW_SHIM_LCTL			0x4
4571bb8a1bSVinod Koul #define SDW_SHIM_IPPTR			0x8
4671bb8a1bSVinod Koul #define SDW_SHIM_SYNC			0xC
4771bb8a1bSVinod Koul 
487cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLSCAP(x)		(0x010 + 0x60 * (x))
497cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS0CM(x)		(0x012 + 0x60 * (x))
507cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS1CM(x)		(0x014 + 0x60 * (x))
517cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS2CM(x)		(0x016 + 0x60 * (x))
527cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS3CM(x)		(0x018 + 0x60 * (x))
537cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSCAP(x)		(0x020 + 0x60 * (x))
5471bb8a1bSVinod Koul 
557cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSYCHM(x, y)		(0x022 + (0x60 * (x)) + (0x2 * (y)))
567cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSYCHC(x, y)		(0x042 + (0x60 * (x)) + (0x2 * (y)))
577cc6e315SPierre-Louis Bossart #define SDW_SHIM_PDMSCAP(x)		(0x062 + 0x60 * (x))
587cc6e315SPierre-Louis Bossart #define SDW_SHIM_IOCTL(x)		(0x06C + 0x60 * (x))
597cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTMCTL(x)		(0x06E + 0x60 * (x))
6071bb8a1bSVinod Koul 
6171bb8a1bSVinod Koul #define SDW_SHIM_WAKEEN			0x190
6271bb8a1bSVinod Koul #define SDW_SHIM_WAKESTS		0x192
6371bb8a1bSVinod Koul 
6471bb8a1bSVinod Koul #define SDW_SHIM_LCTL_SPA		BIT(0)
6571bb8a1bSVinod Koul #define SDW_SHIM_LCTL_CPA		BIT(8)
6671bb8a1bSVinod Koul 
674a17c441SPierre-Louis Bossart #define SDW_SHIM_SYNC_SYNCPRD_VAL_24	(24000 / SDW_CADENCE_GSYNC_KHZ - 1)
684a17c441SPierre-Louis Bossart #define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4	(38400 / SDW_CADENCE_GSYNC_KHZ - 1)
6971bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCPRD		GENMASK(14, 0)
7071bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCCPU		BIT(15)
7171bb8a1bSVinod Koul #define SDW_SHIM_SYNC_CMDSYNC_MASK	GENMASK(19, 16)
7271bb8a1bSVinod Koul #define SDW_SHIM_SYNC_CMDSYNC		BIT(16)
7371bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCGO		BIT(24)
7471bb8a1bSVinod Koul 
7571bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_ISS		GENMASK(3, 0)
7671bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_OSS		GENMASK(7, 4)
7771bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_BSS		GENMASK(12, 8)
7871bb8a1bSVinod Koul 
7971bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_LCHN		GENMASK(3, 0)
8071bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_HCHN		GENMASK(7, 4)
8171bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_STREAM		GENMASK(13, 8)
8271bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_DIR		BIT(15)
8371bb8a1bSVinod Koul 
8471bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_ISS		GENMASK(3, 0)
8571bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_OSS		GENMASK(7, 4)
8671bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_BSS		GENMASK(12, 8)
8771bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_CPSS		GENMASK(15, 13)
8871bb8a1bSVinod Koul 
8971bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_MIF		BIT(0)
9071bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_CO		BIT(1)
9171bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_COE		BIT(2)
9271bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DO		BIT(3)
9371bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DOE		BIT(4)
9471bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_BKE		BIT(5)
9571bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_WPDD		BIT(6)
9671bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_CIBD		BIT(8)
9771bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DIBD		BIT(9)
9871bb8a1bSVinod Koul 
9971bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DACTQE		BIT(0)
10071bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DODS		BIT(1)
10171bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DOAIS		GENMASK(4, 3)
10271bb8a1bSVinod Koul 
10371bb8a1bSVinod Koul #define SDW_SHIM_WAKEEN_ENABLE		BIT(0)
10471bb8a1bSVinod Koul #define SDW_SHIM_WAKESTS_STATUS		BIT(0)
10571bb8a1bSVinod Koul 
10671bb8a1bSVinod Koul /* Intel ALH Register definitions */
1077cc6e315SPierre-Louis Bossart #define SDW_ALH_STRMZCFG(x)		(0x000 + (0x4 * (x)))
10879ee6631SPierre-Louis Bossart #define SDW_ALH_NUM_STREAMS		64
10971bb8a1bSVinod Koul 
11071bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_DMAT_VAL	0x3
11171bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_DMAT		GENMASK(7, 0)
11271bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_CHN		GENMASK(19, 16)
11371bb8a1bSVinod Koul 
114c46302ecSVinod Koul enum intel_pdi_type {
115c46302ecSVinod Koul 	INTEL_PDI_IN = 0,
116c46302ecSVinod Koul 	INTEL_PDI_OUT = 1,
117c46302ecSVinod Koul 	INTEL_PDI_BD = 2,
118c46302ecSVinod Koul };
119c46302ecSVinod Koul 
12071bb8a1bSVinod Koul #define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
12171bb8a1bSVinod Koul 
12271bb8a1bSVinod Koul /*
12371bb8a1bSVinod Koul  * Read, write helpers for HW registers
12471bb8a1bSVinod Koul  */
12571bb8a1bSVinod Koul static inline int intel_readl(void __iomem *base, int offset)
12671bb8a1bSVinod Koul {
12771bb8a1bSVinod Koul 	return readl(base + offset);
12871bb8a1bSVinod Koul }
12971bb8a1bSVinod Koul 
13071bb8a1bSVinod Koul static inline void intel_writel(void __iomem *base, int offset, int value)
13171bb8a1bSVinod Koul {
13271bb8a1bSVinod Koul 	writel(value, base + offset);
13371bb8a1bSVinod Koul }
13471bb8a1bSVinod Koul 
13571bb8a1bSVinod Koul static inline u16 intel_readw(void __iomem *base, int offset)
13671bb8a1bSVinod Koul {
13771bb8a1bSVinod Koul 	return readw(base + offset);
13871bb8a1bSVinod Koul }
13971bb8a1bSVinod Koul 
14071bb8a1bSVinod Koul static inline void intel_writew(void __iomem *base, int offset, u16 value)
14171bb8a1bSVinod Koul {
14271bb8a1bSVinod Koul 	writew(value, base + offset);
14371bb8a1bSVinod Koul }
14471bb8a1bSVinod Koul 
1457d2845d5SPierre-Louis Bossart static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
14671bb8a1bSVinod Koul {
14771bb8a1bSVinod Koul 	int timeout = 10;
14871bb8a1bSVinod Koul 	u32 reg_read;
14971bb8a1bSVinod Koul 
15071bb8a1bSVinod Koul 	do {
15171bb8a1bSVinod Koul 		reg_read = readl(base + offset);
1527d2845d5SPierre-Louis Bossart 		if ((reg_read & mask) == target)
15371bb8a1bSVinod Koul 			return 0;
15471bb8a1bSVinod Koul 
15571bb8a1bSVinod Koul 		timeout--;
1567d2845d5SPierre-Louis Bossart 		usleep_range(50, 100);
15771bb8a1bSVinod Koul 	} while (timeout != 0);
15871bb8a1bSVinod Koul 
15971bb8a1bSVinod Koul 	return -EAGAIN;
16071bb8a1bSVinod Koul }
16171bb8a1bSVinod Koul 
1627d2845d5SPierre-Louis Bossart static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask)
1637d2845d5SPierre-Louis Bossart {
1647d2845d5SPierre-Louis Bossart 	writel(value, base + offset);
1657d2845d5SPierre-Louis Bossart 	return intel_wait_bit(base, offset, mask, 0);
1667d2845d5SPierre-Louis Bossart }
1677d2845d5SPierre-Louis Bossart 
16871bb8a1bSVinod Koul static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask)
16971bb8a1bSVinod Koul {
17071bb8a1bSVinod Koul 	writel(value, base + offset);
1717d2845d5SPierre-Louis Bossart 	return intel_wait_bit(base, offset, mask, mask);
17271bb8a1bSVinod Koul }
17371bb8a1bSVinod Koul 
17471bb8a1bSVinod Koul /*
17579ee6631SPierre-Louis Bossart  * debugfs
17679ee6631SPierre-Louis Bossart  */
17779ee6631SPierre-Louis Bossart #ifdef CONFIG_DEBUG_FS
17879ee6631SPierre-Louis Bossart 
17979ee6631SPierre-Louis Bossart #define RD_BUF (2 * PAGE_SIZE)
18079ee6631SPierre-Louis Bossart 
18179ee6631SPierre-Louis Bossart static ssize_t intel_sprintf(void __iomem *mem, bool l,
18279ee6631SPierre-Louis Bossart 			     char *buf, size_t pos, unsigned int reg)
18379ee6631SPierre-Louis Bossart {
18479ee6631SPierre-Louis Bossart 	int value;
18579ee6631SPierre-Louis Bossart 
18679ee6631SPierre-Louis Bossart 	if (l)
18779ee6631SPierre-Louis Bossart 		value = intel_readl(mem, reg);
18879ee6631SPierre-Louis Bossart 	else
18979ee6631SPierre-Louis Bossart 		value = intel_readw(mem, reg);
19079ee6631SPierre-Louis Bossart 
19179ee6631SPierre-Louis Bossart 	return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value);
19279ee6631SPierre-Louis Bossart }
19379ee6631SPierre-Louis Bossart 
19479ee6631SPierre-Louis Bossart static int intel_reg_show(struct seq_file *s_file, void *data)
19579ee6631SPierre-Louis Bossart {
19679ee6631SPierre-Louis Bossart 	struct sdw_intel *sdw = s_file->private;
1972523486bSPierre-Louis Bossart 	void __iomem *s = sdw->link_res->shim;
1982523486bSPierre-Louis Bossart 	void __iomem *a = sdw->link_res->alh;
19979ee6631SPierre-Louis Bossart 	char *buf;
20079ee6631SPierre-Louis Bossart 	ssize_t ret;
20179ee6631SPierre-Louis Bossart 	int i, j;
20279ee6631SPierre-Louis Bossart 	unsigned int links, reg;
20379ee6631SPierre-Louis Bossart 
20479ee6631SPierre-Louis Bossart 	buf = kzalloc(RD_BUF, GFP_KERNEL);
20579ee6631SPierre-Louis Bossart 	if (!buf)
20679ee6631SPierre-Louis Bossart 		return -ENOMEM;
20779ee6631SPierre-Louis Bossart 
20879ee6631SPierre-Louis Bossart 	links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0);
20979ee6631SPierre-Louis Bossart 
21079ee6631SPierre-Louis Bossart 	ret = scnprintf(buf, RD_BUF, "Register  Value\n");
21179ee6631SPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n");
21279ee6631SPierre-Louis Bossart 
21379ee6631SPierre-Louis Bossart 	for (i = 0; i < links; i++) {
21479ee6631SPierre-Louis Bossart 		reg = SDW_SHIM_LCAP + i * 4;
21579ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, true, buf, ret, reg);
21679ee6631SPierre-Louis Bossart 	}
21779ee6631SPierre-Louis Bossart 
21879ee6631SPierre-Louis Bossart 	for (i = 0; i < links; i++) {
21979ee6631SPierre-Louis Bossart 		ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i);
22079ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i));
22179ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i));
22279ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i));
22379ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i));
22479ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i));
22579ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i));
22679ee6631SPierre-Louis Bossart 
22779ee6631SPierre-Louis Bossart 		ret += scnprintf(buf + ret, RD_BUF - ret, "\n PCMSyCH registers\n");
22879ee6631SPierre-Louis Bossart 
22979ee6631SPierre-Louis Bossart 		/*
23079ee6631SPierre-Louis Bossart 		 * the value 10 is the number of PDIs. We will need a
23179ee6631SPierre-Louis Bossart 		 * cleanup to remove hard-coded Intel configurations
23279ee6631SPierre-Louis Bossart 		 * from cadence_master.c
23379ee6631SPierre-Louis Bossart 		 */
23479ee6631SPierre-Louis Bossart 		for (j = 0; j < 10; j++) {
23579ee6631SPierre-Louis Bossart 			ret += intel_sprintf(s, false, buf, ret,
23679ee6631SPierre-Louis Bossart 					SDW_SHIM_PCMSYCHM(i, j));
23779ee6631SPierre-Louis Bossart 			ret += intel_sprintf(s, false, buf, ret,
23879ee6631SPierre-Louis Bossart 					SDW_SHIM_PCMSYCHC(i, j));
23979ee6631SPierre-Louis Bossart 		}
24079ee6631SPierre-Louis Bossart 		ret += scnprintf(buf + ret, RD_BUF - ret, "\n PDMSCAP, IOCTL, CTMCTL\n");
24179ee6631SPierre-Louis Bossart 
24279ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i));
24379ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i));
24479ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i));
24579ee6631SPierre-Louis Bossart 	}
24679ee6631SPierre-Louis Bossart 
24779ee6631SPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nWake registers\n");
24879ee6631SPierre-Louis Bossart 	ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN);
24979ee6631SPierre-Louis Bossart 	ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS);
25079ee6631SPierre-Louis Bossart 
25179ee6631SPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH STRMzCFG\n");
25279ee6631SPierre-Louis Bossart 	for (i = 0; i < SDW_ALH_NUM_STREAMS; i++)
25379ee6631SPierre-Louis Bossart 		ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i));
25479ee6631SPierre-Louis Bossart 
25579ee6631SPierre-Louis Bossart 	seq_printf(s_file, "%s", buf);
25679ee6631SPierre-Louis Bossart 	kfree(buf);
25779ee6631SPierre-Louis Bossart 
25879ee6631SPierre-Louis Bossart 	return 0;
25979ee6631SPierre-Louis Bossart }
26079ee6631SPierre-Louis Bossart DEFINE_SHOW_ATTRIBUTE(intel_reg);
26179ee6631SPierre-Louis Bossart 
26279ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw)
26379ee6631SPierre-Louis Bossart {
26479ee6631SPierre-Louis Bossart 	struct dentry *root = sdw->cdns.bus.debugfs;
26579ee6631SPierre-Louis Bossart 
26679ee6631SPierre-Louis Bossart 	if (!root)
26779ee6631SPierre-Louis Bossart 		return;
26879ee6631SPierre-Louis Bossart 
26979ee6631SPierre-Louis Bossart 	sdw->debugfs = debugfs_create_dir("intel-sdw", root);
27079ee6631SPierre-Louis Bossart 
27179ee6631SPierre-Louis Bossart 	debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw,
27279ee6631SPierre-Louis Bossart 			    &intel_reg_fops);
27379ee6631SPierre-Louis Bossart 
27479ee6631SPierre-Louis Bossart 	sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs);
27579ee6631SPierre-Louis Bossart }
27679ee6631SPierre-Louis Bossart 
27779ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw)
27879ee6631SPierre-Louis Bossart {
27979ee6631SPierre-Louis Bossart 	debugfs_remove_recursive(sdw->debugfs);
28079ee6631SPierre-Louis Bossart }
28179ee6631SPierre-Louis Bossart #else
28279ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw) {}
28379ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw) {}
28479ee6631SPierre-Louis Bossart #endif /* CONFIG_DEBUG_FS */
28579ee6631SPierre-Louis Bossart 
28679ee6631SPierre-Louis Bossart /*
28771bb8a1bSVinod Koul  * shim ops
28871bb8a1bSVinod Koul  */
28971bb8a1bSVinod Koul 
29071bb8a1bSVinod Koul static int intel_link_power_up(struct sdw_intel *sdw)
29171bb8a1bSVinod Koul {
29271bb8a1bSVinod Koul 	unsigned int link_id = sdw->instance;
2932523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
2944a17c441SPierre-Louis Bossart 	u32 *shim_mask = sdw->link_res->shim_mask;
2954a17c441SPierre-Louis Bossart 	struct sdw_bus *bus = &sdw->cdns.bus;
2964a17c441SPierre-Louis Bossart 	struct sdw_master_prop *prop = &bus->prop;
29771bb8a1bSVinod Koul 	int spa_mask, cpa_mask;
2984a17c441SPierre-Louis Bossart 	int link_control;
2994a17c441SPierre-Louis Bossart 	int ret = 0;
3004a17c441SPierre-Louis Bossart 	u32 syncprd;
3014a17c441SPierre-Louis Bossart 	u32 sync_reg;
3024a17c441SPierre-Louis Bossart 
3034a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
3044a17c441SPierre-Louis Bossart 
3054a17c441SPierre-Louis Bossart 	/*
3064a17c441SPierre-Louis Bossart 	 * The hardware relies on an internal counter, typically 4kHz,
3074a17c441SPierre-Louis Bossart 	 * to generate the SoundWire SSP - which defines a 'safe'
3084a17c441SPierre-Louis Bossart 	 * synchronization point between commands and audio transport
3094a17c441SPierre-Louis Bossart 	 * and allows for multi link synchronization. The SYNCPRD value
3104a17c441SPierre-Louis Bossart 	 * is only dependent on the oscillator clock provided to
3114a17c441SPierre-Louis Bossart 	 * the IP, so adjust based on _DSD properties reported in DSDT
3124a17c441SPierre-Louis Bossart 	 * tables. The values reported are based on either 24MHz
3134a17c441SPierre-Louis Bossart 	 * (CNL/CML) or 38.4 MHz (ICL/TGL+).
3144a17c441SPierre-Louis Bossart 	 */
3154a17c441SPierre-Louis Bossart 	if (prop->mclk_freq % 6000000)
3164a17c441SPierre-Louis Bossart 		syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;
3174a17c441SPierre-Louis Bossart 	else
3184a17c441SPierre-Louis Bossart 		syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24;
3194a17c441SPierre-Louis Bossart 
3204a17c441SPierre-Louis Bossart 	if (!*shim_mask) {
3214a17c441SPierre-Louis Bossart 		/* we first need to program the SyncPRD/CPU registers */
3224a17c441SPierre-Louis Bossart 		dev_dbg(sdw->cdns.dev,
3234a17c441SPierre-Louis Bossart 			"%s: first link up, programming SYNCPRD\n", __func__);
3244a17c441SPierre-Louis Bossart 
3254a17c441SPierre-Louis Bossart 		/* set SyncPRD period */
3264a17c441SPierre-Louis Bossart 		sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
3274a17c441SPierre-Louis Bossart 		sync_reg |= (syncprd <<
3284a17c441SPierre-Louis Bossart 			     SDW_REG_SHIFT(SDW_SHIM_SYNC_SYNCPRD));
3294a17c441SPierre-Louis Bossart 
3304a17c441SPierre-Louis Bossart 		/* Set SyncCPU bit */
3314a17c441SPierre-Louis Bossart 		sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
3324a17c441SPierre-Louis Bossart 		intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
3334a17c441SPierre-Louis Bossart 	}
33471bb8a1bSVinod Koul 
33571bb8a1bSVinod Koul 	/* Link power up sequence */
33671bb8a1bSVinod Koul 	link_control = intel_readl(shim, SDW_SHIM_LCTL);
33771bb8a1bSVinod Koul 	spa_mask = (SDW_SHIM_LCTL_SPA << link_id);
33871bb8a1bSVinod Koul 	cpa_mask = (SDW_SHIM_LCTL_CPA << link_id);
33971bb8a1bSVinod Koul 	link_control |=  spa_mask;
34071bb8a1bSVinod Koul 
34171bb8a1bSVinod Koul 	ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
3424a17c441SPierre-Louis Bossart 	if (ret < 0) {
3434a17c441SPierre-Louis Bossart 		dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret);
3444a17c441SPierre-Louis Bossart 		goto out;
34571bb8a1bSVinod Koul 	}
34671bb8a1bSVinod Koul 
3474a17c441SPierre-Louis Bossart 	if (!*shim_mask) {
3484a17c441SPierre-Louis Bossart 		/* SyncCPU will change once link is active */
3494a17c441SPierre-Louis Bossart 		ret = intel_wait_bit(shim, SDW_SHIM_SYNC,
3504a17c441SPierre-Louis Bossart 				     SDW_SHIM_SYNC_SYNCCPU, 0);
3514a17c441SPierre-Louis Bossart 		if (ret < 0) {
3524a17c441SPierre-Louis Bossart 			dev_err(sdw->cdns.dev,
3534a17c441SPierre-Louis Bossart 				"Failed to set SHIM_SYNC: %d\n", ret);
3544a17c441SPierre-Louis Bossart 			goto out;
3554a17c441SPierre-Louis Bossart 		}
3564a17c441SPierre-Louis Bossart 	}
3574a17c441SPierre-Louis Bossart 
3584a17c441SPierre-Louis Bossart 	*shim_mask |= BIT(link_id);
3594a17c441SPierre-Louis Bossart 
3604a17c441SPierre-Louis Bossart 	sdw->cdns.link_up = true;
3614a17c441SPierre-Louis Bossart out:
3624a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
3634a17c441SPierre-Louis Bossart 
3644a17c441SPierre-Louis Bossart 	return ret;
3654a17c441SPierre-Louis Bossart }
3664a17c441SPierre-Louis Bossart 
3674a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */
3684a17c441SPierre-Louis Bossart static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw)
36971bb8a1bSVinod Koul {
3702523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
37171bb8a1bSVinod Koul 	unsigned int link_id = sdw->instance;
3724a17c441SPierre-Louis Bossart 	u16 ioctl;
37371bb8a1bSVinod Koul 
37471bb8a1bSVinod Koul 	/* Switch to MIP from Glue logic */
37571bb8a1bSVinod Koul 	ioctl = intel_readw(shim,  SDW_SHIM_IOCTL(link_id));
37671bb8a1bSVinod Koul 
37771bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_DOE);
37871bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
3794a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
38071bb8a1bSVinod Koul 
38171bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_DO);
38271bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
3834a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
38471bb8a1bSVinod Koul 
38571bb8a1bSVinod Koul 	ioctl |= (SDW_SHIM_IOCTL_MIF);
38671bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
3874a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
38871bb8a1bSVinod Koul 
38971bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_BKE);
39071bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_COE);
39171bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
3924a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
3934a17c441SPierre-Louis Bossart 
3944a17c441SPierre-Louis Bossart 	/* at this point Master IP has full control of the I/Os */
3954a17c441SPierre-Louis Bossart }
3964a17c441SPierre-Louis Bossart 
3974a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */
3984a17c441SPierre-Louis Bossart static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw)
3994a17c441SPierre-Louis Bossart {
4004a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
4014a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
4024a17c441SPierre-Louis Bossart 	u16 ioctl;
4034a17c441SPierre-Louis Bossart 
4044a17c441SPierre-Louis Bossart 	/* Glue logic */
4054a17c441SPierre-Louis Bossart 	ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id));
4064a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_BKE;
4074a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_COE;
4084a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4094a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4104a17c441SPierre-Louis Bossart 
4114a17c441SPierre-Louis Bossart 	ioctl &= ~(SDW_SHIM_IOCTL_MIF);
4124a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4134a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4144a17c441SPierre-Louis Bossart 
4154a17c441SPierre-Louis Bossart 	/* at this point Integration Glue has full control of the I/Os */
4164a17c441SPierre-Louis Bossart }
4174a17c441SPierre-Louis Bossart 
4184a17c441SPierre-Louis Bossart static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop)
4194a17c441SPierre-Louis Bossart {
4204a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
4214a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
4224a17c441SPierre-Louis Bossart 	int ret = 0;
4234a17c441SPierre-Louis Bossart 	u16 ioctl = 0, act = 0;
4244a17c441SPierre-Louis Bossart 
4254a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
4264a17c441SPierre-Louis Bossart 
4274a17c441SPierre-Louis Bossart 	/* Initialize Shim */
4284a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_BKE;
4294a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4304a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4314a17c441SPierre-Louis Bossart 
4324a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_WPDD;
4334a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4344a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4354a17c441SPierre-Louis Bossart 
4364a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_DO;
4374a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4384a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4394a17c441SPierre-Louis Bossart 
4404a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_DOE;
4414a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4424a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4434a17c441SPierre-Louis Bossart 
4444a17c441SPierre-Louis Bossart 	intel_shim_glue_to_master_ip(sdw);
44571bb8a1bSVinod Koul 
44671bb8a1bSVinod Koul 	act |= 0x1 << SDW_REG_SHIFT(SDW_SHIM_CTMCTL_DOAIS);
44771bb8a1bSVinod Koul 	act |= SDW_SHIM_CTMCTL_DACTQE;
44871bb8a1bSVinod Koul 	act |= SDW_SHIM_CTMCTL_DODS;
44971bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act);
4504a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
45171bb8a1bSVinod Koul 
4524a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
45371bb8a1bSVinod Koul 
45471bb8a1bSVinod Koul 	return ret;
45571bb8a1bSVinod Koul }
45671bb8a1bSVinod Koul 
457ab2c9132SRander Wang static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
4584a17c441SPierre-Louis Bossart {
4594a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
4604a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
4614a17c441SPierre-Louis Bossart 	u16 wake_en, wake_sts;
4624a17c441SPierre-Louis Bossart 
4634a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
4644a17c441SPierre-Louis Bossart 	wake_en = intel_readw(shim, SDW_SHIM_WAKEEN);
4654a17c441SPierre-Louis Bossart 
4664a17c441SPierre-Louis Bossart 	if (wake_enable) {
4674a17c441SPierre-Louis Bossart 		/* Enable the wakeup */
4684a17c441SPierre-Louis Bossart 		wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
4694a17c441SPierre-Louis Bossart 		intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
4704a17c441SPierre-Louis Bossart 	} else {
4714a17c441SPierre-Louis Bossart 		/* Disable the wake up interrupt */
4724a17c441SPierre-Louis Bossart 		wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id);
4734a17c441SPierre-Louis Bossart 		intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
4744a17c441SPierre-Louis Bossart 
4754a17c441SPierre-Louis Bossart 		/* Clear wake status */
4764a17c441SPierre-Louis Bossart 		wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
4774a17c441SPierre-Louis Bossart 		wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
4784a17c441SPierre-Louis Bossart 		intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts);
4794a17c441SPierre-Louis Bossart 	}
4804a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
4814a17c441SPierre-Louis Bossart }
4824a17c441SPierre-Louis Bossart 
4839b3b4b3fSPierre-Louis Bossart static int intel_link_power_down(struct sdw_intel *sdw)
4844a17c441SPierre-Louis Bossart {
4854a17c441SPierre-Louis Bossart 	int link_control, spa_mask, cpa_mask;
4864a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
4874a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
4884a17c441SPierre-Louis Bossart 	u32 *shim_mask = sdw->link_res->shim_mask;
4894a17c441SPierre-Louis Bossart 	int ret = 0;
4904a17c441SPierre-Louis Bossart 
4914a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
4924a17c441SPierre-Louis Bossart 
4934a17c441SPierre-Louis Bossart 	intel_shim_master_ip_to_glue(sdw);
4944a17c441SPierre-Louis Bossart 
4954a17c441SPierre-Louis Bossart 	/* Link power down sequence */
4964a17c441SPierre-Louis Bossart 	link_control = intel_readl(shim, SDW_SHIM_LCTL);
4974a17c441SPierre-Louis Bossart 	spa_mask = ~(SDW_SHIM_LCTL_SPA << link_id);
4984a17c441SPierre-Louis Bossart 	cpa_mask = (SDW_SHIM_LCTL_CPA << link_id);
4994a17c441SPierre-Louis Bossart 	link_control &=  spa_mask;
5004a17c441SPierre-Louis Bossart 
5014a17c441SPierre-Louis Bossart 	ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
5024a17c441SPierre-Louis Bossart 
5034a17c441SPierre-Louis Bossart 	if (!(*shim_mask & BIT(link_id)))
5044a17c441SPierre-Louis Bossart 		dev_err(sdw->cdns.dev,
5054a17c441SPierre-Louis Bossart 			"%s: Unbalanced power-up/down calls\n", __func__);
5064a17c441SPierre-Louis Bossart 
5074a17c441SPierre-Louis Bossart 	*shim_mask &= ~BIT(link_id);
5084a17c441SPierre-Louis Bossart 
5094a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
5104a17c441SPierre-Louis Bossart 
51171bb8a1bSVinod Koul 	if (ret < 0)
5124a17c441SPierre-Louis Bossart 		return ret;
5134a17c441SPierre-Louis Bossart 
5144a17c441SPierre-Louis Bossart 	sdw->cdns.link_up = false;
5154a17c441SPierre-Louis Bossart 	return 0;
5164a17c441SPierre-Louis Bossart }
5174a17c441SPierre-Louis Bossart 
51802629e45SPierre-Louis Bossart static void intel_shim_sync_arm(struct sdw_intel *sdw)
51902629e45SPierre-Louis Bossart {
52002629e45SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
52102629e45SPierre-Louis Bossart 	u32 sync_reg;
52202629e45SPierre-Louis Bossart 
52302629e45SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
52402629e45SPierre-Louis Bossart 
52502629e45SPierre-Louis Bossart 	/* update SYNC register */
52602629e45SPierre-Louis Bossart 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
52702629e45SPierre-Louis Bossart 	sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance);
52802629e45SPierre-Louis Bossart 	intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
52902629e45SPierre-Louis Bossart 
53002629e45SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
53102629e45SPierre-Louis Bossart }
53202629e45SPierre-Louis Bossart 
533437e3289SPierre-Louis Bossart static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw)
534437e3289SPierre-Louis Bossart {
535437e3289SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
536437e3289SPierre-Louis Bossart 	u32 sync_reg;
537437e3289SPierre-Louis Bossart 	int ret;
538437e3289SPierre-Louis Bossart 
539437e3289SPierre-Louis Bossart 	/* Read SYNC register */
540437e3289SPierre-Louis Bossart 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
541437e3289SPierre-Louis Bossart 
542437e3289SPierre-Louis Bossart 	/*
543437e3289SPierre-Louis Bossart 	 * Set SyncGO bit to synchronously trigger a bank switch for
544437e3289SPierre-Louis Bossart 	 * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all
545437e3289SPierre-Louis Bossart 	 * the Masters.
546437e3289SPierre-Louis Bossart 	 */
547437e3289SPierre-Louis Bossart 	sync_reg |= SDW_SHIM_SYNC_SYNCGO;
548437e3289SPierre-Louis Bossart 
549437e3289SPierre-Louis Bossart 	ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
550437e3289SPierre-Louis Bossart 			      SDW_SHIM_SYNC_SYNCGO);
551437e3289SPierre-Louis Bossart 
552437e3289SPierre-Louis Bossart 	if (ret < 0)
553437e3289SPierre-Louis Bossart 		dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret);
55471bb8a1bSVinod Koul 
55571bb8a1bSVinod Koul 	return ret;
55671bb8a1bSVinod Koul }
55771bb8a1bSVinod Koul 
55837a2d22bSVinod Koul /*
55937a2d22bSVinod Koul  * PDI routines
56037a2d22bSVinod Koul  */
56137a2d22bSVinod Koul static void intel_pdi_init(struct sdw_intel *sdw,
56237a2d22bSVinod Koul 			   struct sdw_cdns_stream_config *config)
56337a2d22bSVinod Koul {
5642523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
56537a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
56637a2d22bSVinod Koul 	int pcm_cap, pdm_cap;
56737a2d22bSVinod Koul 
56837a2d22bSVinod Koul 	/* PCM Stream Capability */
56937a2d22bSVinod Koul 	pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id));
57037a2d22bSVinod Koul 
57137a2d22bSVinod Koul 	config->pcm_bd = (pcm_cap & SDW_SHIM_PCMSCAP_BSS) >>
57237a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_BSS);
57337a2d22bSVinod Koul 	config->pcm_in = (pcm_cap & SDW_SHIM_PCMSCAP_ISS) >>
57437a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_ISS);
57537a2d22bSVinod Koul 	config->pcm_out = (pcm_cap & SDW_SHIM_PCMSCAP_OSS) >>
57637a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_OSS);
57737a2d22bSVinod Koul 
578121f4361SPierre-Louis Bossart 	dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n",
579121f4361SPierre-Louis Bossart 		config->pcm_bd, config->pcm_in, config->pcm_out);
580121f4361SPierre-Louis Bossart 
58137a2d22bSVinod Koul 	/* PDM Stream Capability */
58237a2d22bSVinod Koul 	pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
58337a2d22bSVinod Koul 
58437a2d22bSVinod Koul 	config->pdm_bd = (pdm_cap & SDW_SHIM_PDMSCAP_BSS) >>
58537a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_BSS);
58637a2d22bSVinod Koul 	config->pdm_in = (pdm_cap & SDW_SHIM_PDMSCAP_ISS) >>
58737a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_ISS);
58837a2d22bSVinod Koul 	config->pdm_out = (pdm_cap & SDW_SHIM_PDMSCAP_OSS) >>
58937a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_OSS);
590121f4361SPierre-Louis Bossart 
591121f4361SPierre-Louis Bossart 	dev_dbg(sdw->cdns.dev, "PDM cap bd:%d in:%d out:%d\n",
592121f4361SPierre-Louis Bossart 		config->pdm_bd, config->pdm_in, config->pdm_out);
59337a2d22bSVinod Koul }
59437a2d22bSVinod Koul 
59537a2d22bSVinod Koul static int
59637a2d22bSVinod Koul intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm)
59737a2d22bSVinod Koul {
5982523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
59937a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
60037a2d22bSVinod Koul 	int count;
60137a2d22bSVinod Koul 
60237a2d22bSVinod Koul 	if (pcm) {
60337a2d22bSVinod Koul 		count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num));
60418046335SPierre-Louis Bossart 
60518046335SPierre-Louis Bossart 		/*
60618046335SPierre-Louis Bossart 		 * WORKAROUND: on all existing Intel controllers, pdi
60718046335SPierre-Louis Bossart 		 * number 2 reports channel count as 1 even though it
60818046335SPierre-Louis Bossart 		 * supports 8 channels. Performing hardcoding for pdi
60918046335SPierre-Louis Bossart 		 * number 2.
61018046335SPierre-Louis Bossart 		 */
61118046335SPierre-Louis Bossart 		if (pdi_num == 2)
61218046335SPierre-Louis Bossart 			count = 7;
61318046335SPierre-Louis Bossart 
61437a2d22bSVinod Koul 	} else {
61537a2d22bSVinod Koul 		count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
61637a2d22bSVinod Koul 		count = ((count & SDW_SHIM_PDMSCAP_CPSS) >>
61737a2d22bSVinod Koul 					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_CPSS));
61837a2d22bSVinod Koul 	}
61937a2d22bSVinod Koul 
62037a2d22bSVinod Koul 	/* zero based values for channel count in register */
62137a2d22bSVinod Koul 	count++;
62237a2d22bSVinod Koul 
62337a2d22bSVinod Koul 	return count;
62437a2d22bSVinod Koul }
62537a2d22bSVinod Koul 
62637a2d22bSVinod Koul static int intel_pdi_get_ch_update(struct sdw_intel *sdw,
62737a2d22bSVinod Koul 				   struct sdw_cdns_pdi *pdi,
62837a2d22bSVinod Koul 				   unsigned int num_pdi,
62937a2d22bSVinod Koul 				   unsigned int *num_ch, bool pcm)
63037a2d22bSVinod Koul {
63137a2d22bSVinod Koul 	int i, ch_count = 0;
63237a2d22bSVinod Koul 
63337a2d22bSVinod Koul 	for (i = 0; i < num_pdi; i++) {
63437a2d22bSVinod Koul 		pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm);
63537a2d22bSVinod Koul 		ch_count += pdi->ch_count;
63637a2d22bSVinod Koul 		pdi++;
63737a2d22bSVinod Koul 	}
63837a2d22bSVinod Koul 
63937a2d22bSVinod Koul 	*num_ch = ch_count;
64037a2d22bSVinod Koul 	return 0;
64137a2d22bSVinod Koul }
64237a2d22bSVinod Koul 
64337a2d22bSVinod Koul static int intel_pdi_stream_ch_update(struct sdw_intel *sdw,
64437a2d22bSVinod Koul 				      struct sdw_cdns_streams *stream, bool pcm)
64537a2d22bSVinod Koul {
64637a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd,
64737a2d22bSVinod Koul 				&stream->num_ch_bd, pcm);
64837a2d22bSVinod Koul 
64937a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->in, stream->num_in,
65037a2d22bSVinod Koul 				&stream->num_ch_in, pcm);
65137a2d22bSVinod Koul 
65237a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->out, stream->num_out,
65337a2d22bSVinod Koul 				&stream->num_ch_out, pcm);
65437a2d22bSVinod Koul 
65537a2d22bSVinod Koul 	return 0;
65637a2d22bSVinod Koul }
65737a2d22bSVinod Koul 
65837a2d22bSVinod Koul static int intel_pdi_ch_update(struct sdw_intel *sdw)
65937a2d22bSVinod Koul {
66037a2d22bSVinod Koul 	/* First update PCM streams followed by PDM streams */
66137a2d22bSVinod Koul 	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true);
66237a2d22bSVinod Koul 	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false);
66337a2d22bSVinod Koul 
66437a2d22bSVinod Koul 	return 0;
66537a2d22bSVinod Koul }
66637a2d22bSVinod Koul 
66737a2d22bSVinod Koul static void
66837a2d22bSVinod Koul intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
66937a2d22bSVinod Koul {
6702523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
67137a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
67237a2d22bSVinod Koul 	int pdi_conf = 0;
67337a2d22bSVinod Koul 
674c134f914SPierre-Louis Bossart 	/* the Bulk and PCM streams are not contiguous */
675c134f914SPierre-Louis Bossart 	pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
676c134f914SPierre-Louis Bossart 	if (pdi->num >= 2)
677c134f914SPierre-Louis Bossart 		pdi->intel_alh_id += 2;
67837a2d22bSVinod Koul 
67937a2d22bSVinod Koul 	/*
68037a2d22bSVinod Koul 	 * Program stream parameters to stream SHIM register
68137a2d22bSVinod Koul 	 * This is applicable for PCM stream only.
68237a2d22bSVinod Koul 	 */
68337a2d22bSVinod Koul 	if (pdi->type != SDW_STREAM_PCM)
68437a2d22bSVinod Koul 		return;
68537a2d22bSVinod Koul 
68637a2d22bSVinod Koul 	if (pdi->dir == SDW_DATA_DIR_RX)
68737a2d22bSVinod Koul 		pdi_conf |= SDW_SHIM_PCMSYCM_DIR;
68837a2d22bSVinod Koul 	else
68937a2d22bSVinod Koul 		pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR);
69037a2d22bSVinod Koul 
69137a2d22bSVinod Koul 	pdi_conf |= (pdi->intel_alh_id <<
69237a2d22bSVinod Koul 			SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_STREAM));
69337a2d22bSVinod Koul 	pdi_conf |= (pdi->l_ch_num << SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_LCHN));
69437a2d22bSVinod Koul 	pdi_conf |= (pdi->h_ch_num << SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_HCHN));
69537a2d22bSVinod Koul 
69637a2d22bSVinod Koul 	intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf);
69737a2d22bSVinod Koul }
69837a2d22bSVinod Koul 
69937a2d22bSVinod Koul static void
70037a2d22bSVinod Koul intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
70137a2d22bSVinod Koul {
7022523486bSPierre-Louis Bossart 	void __iomem *alh = sdw->link_res->alh;
70337a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
70437a2d22bSVinod Koul 	unsigned int conf;
70537a2d22bSVinod Koul 
706c134f914SPierre-Louis Bossart 	/* the Bulk and PCM streams are not contiguous */
707c134f914SPierre-Louis Bossart 	pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
708c134f914SPierre-Louis Bossart 	if (pdi->num >= 2)
709c134f914SPierre-Louis Bossart 		pdi->intel_alh_id += 2;
71037a2d22bSVinod Koul 
71137a2d22bSVinod Koul 	/* Program Stream config ALH register */
71237a2d22bSVinod Koul 	conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id));
71337a2d22bSVinod Koul 
71437a2d22bSVinod Koul 	conf |= (SDW_ALH_STRMZCFG_DMAT_VAL <<
71537a2d22bSVinod Koul 			SDW_REG_SHIFT(SDW_ALH_STRMZCFG_DMAT));
71637a2d22bSVinod Koul 
71737a2d22bSVinod Koul 	conf |= ((pdi->ch_count - 1) <<
71837a2d22bSVinod Koul 			SDW_REG_SHIFT(SDW_ALH_STRMZCFG_CHN));
71937a2d22bSVinod Koul 
72037a2d22bSVinod Koul 	intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
72137a2d22bSVinod Koul }
72237a2d22bSVinod Koul 
7234b206d34SRander Wang static int intel_params_stream(struct sdw_intel *sdw,
724c46302ecSVinod Koul 			       struct snd_pcm_substream *substream,
725c46302ecSVinod Koul 			       struct snd_soc_dai *dai,
7264b206d34SRander Wang 			       struct snd_pcm_hw_params *hw_params,
7274b206d34SRander Wang 			       int link_id, int alh_stream_id)
728c46302ecSVinod Koul {
7292523486bSPierre-Louis Bossart 	struct sdw_intel_link_res *res = sdw->link_res;
7304b206d34SRander Wang 	struct sdw_intel_stream_params_data params_data;
73105c8afe4SPierre-Louis Bossart 
7324b206d34SRander Wang 	params_data.substream = substream;
7334b206d34SRander Wang 	params_data.dai = dai;
7344b206d34SRander Wang 	params_data.hw_params = hw_params;
7354b206d34SRander Wang 	params_data.link_id = link_id;
7364b206d34SRander Wang 	params_data.alh_stream_id = alh_stream_id;
737c46302ecSVinod Koul 
7384b206d34SRander Wang 	if (res->ops && res->ops->params_stream && res->dev)
7394b206d34SRander Wang 		return res->ops->params_stream(res->dev,
7404b206d34SRander Wang 					       &params_data);
741c46302ecSVinod Koul 	return -EIO;
742c46302ecSVinod Koul }
743c46302ecSVinod Koul 
744eff346f2SPierre-Louis Bossart static int intel_free_stream(struct sdw_intel *sdw,
745eff346f2SPierre-Louis Bossart 			     struct snd_pcm_substream *substream,
746eff346f2SPierre-Louis Bossart 			     struct snd_soc_dai *dai,
747eff346f2SPierre-Louis Bossart 			     int link_id)
748eff346f2SPierre-Louis Bossart {
749eff346f2SPierre-Louis Bossart 	struct sdw_intel_link_res *res = sdw->link_res;
750eff346f2SPierre-Louis Bossart 	struct sdw_intel_stream_free_data free_data;
751eff346f2SPierre-Louis Bossart 
752eff346f2SPierre-Louis Bossart 	free_data.substream = substream;
753eff346f2SPierre-Louis Bossart 	free_data.dai = dai;
754eff346f2SPierre-Louis Bossart 	free_data.link_id = link_id;
755eff346f2SPierre-Louis Bossart 
756eff346f2SPierre-Louis Bossart 	if (res->ops && res->ops->free_stream && res->dev)
757eff346f2SPierre-Louis Bossart 		return res->ops->free_stream(res->dev,
758eff346f2SPierre-Louis Bossart 					     &free_data);
759eff346f2SPierre-Louis Bossart 
760eff346f2SPierre-Louis Bossart 	return 0;
761eff346f2SPierre-Louis Bossart }
762eff346f2SPierre-Louis Bossart 
763c46302ecSVinod Koul /*
76430246e2dSShreyas NC  * bank switch routines
76530246e2dSShreyas NC  */
76630246e2dSShreyas NC 
76730246e2dSShreyas NC static int intel_pre_bank_switch(struct sdw_bus *bus)
76830246e2dSShreyas NC {
76930246e2dSShreyas NC 	struct sdw_cdns *cdns = bus_to_cdns(bus);
77030246e2dSShreyas NC 	struct sdw_intel *sdw = cdns_to_intel(cdns);
77130246e2dSShreyas NC 
77230246e2dSShreyas NC 	/* Write to register only for multi-link */
77330246e2dSShreyas NC 	if (!bus->multi_link)
77430246e2dSShreyas NC 		return 0;
77530246e2dSShreyas NC 
77602629e45SPierre-Louis Bossart 	intel_shim_sync_arm(sdw);
77730246e2dSShreyas NC 
77830246e2dSShreyas NC 	return 0;
77930246e2dSShreyas NC }
78030246e2dSShreyas NC 
78130246e2dSShreyas NC static int intel_post_bank_switch(struct sdw_bus *bus)
78230246e2dSShreyas NC {
78330246e2dSShreyas NC 	struct sdw_cdns *cdns = bus_to_cdns(bus);
78430246e2dSShreyas NC 	struct sdw_intel *sdw = cdns_to_intel(cdns);
7852523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
78630246e2dSShreyas NC 	int sync_reg, ret;
78730246e2dSShreyas NC 
78830246e2dSShreyas NC 	/* Write to register only for multi-link */
78930246e2dSShreyas NC 	if (!bus->multi_link)
79030246e2dSShreyas NC 		return 0;
79130246e2dSShreyas NC 
7924a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
7934a17c441SPierre-Louis Bossart 
79430246e2dSShreyas NC 	/* Read SYNC register */
79530246e2dSShreyas NC 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
79630246e2dSShreyas NC 
79730246e2dSShreyas NC 	/*
79830246e2dSShreyas NC 	 * post_bank_switch() ops is called from the bus in loop for
79930246e2dSShreyas NC 	 * all the Masters in the steam with the expectation that
80030246e2dSShreyas NC 	 * we trigger the bankswitch for the only first Master in the list
80130246e2dSShreyas NC 	 * and do nothing for the other Masters
80230246e2dSShreyas NC 	 *
80330246e2dSShreyas NC 	 * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
80430246e2dSShreyas NC 	 */
8054a17c441SPierre-Louis Bossart 	if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) {
8064a17c441SPierre-Louis Bossart 		ret = 0;
8074a17c441SPierre-Louis Bossart 		goto unlock;
8084a17c441SPierre-Louis Bossart 	}
80930246e2dSShreyas NC 
810437e3289SPierre-Louis Bossart 	ret = intel_shim_sync_go_unlocked(sdw);
8114a17c441SPierre-Louis Bossart unlock:
8124a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
81330246e2dSShreyas NC 
81430246e2dSShreyas NC 	if (ret < 0)
81517ed5befSPierre-Louis Bossart 		dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret);
81630246e2dSShreyas NC 
81730246e2dSShreyas NC 	return ret;
81830246e2dSShreyas NC }
81930246e2dSShreyas NC 
82030246e2dSShreyas NC /*
821c46302ecSVinod Koul  * DAI routines
822c46302ecSVinod Koul  */
823c46302ecSVinod Koul 
8245e7484d0SRander Wang static int intel_startup(struct snd_pcm_substream *substream,
8255e7484d0SRander Wang 			 struct snd_soc_dai *dai)
8265e7484d0SRander Wang {
827ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
828ebf878edSPierre-Louis Bossart 	int ret;
829ebf878edSPierre-Louis Bossart 
830ebf878edSPierre-Louis Bossart 	ret = pm_runtime_get_sync(cdns->dev);
831ebf878edSPierre-Louis Bossart 	if (ret < 0 && ret != -EACCES) {
832ebf878edSPierre-Louis Bossart 		dev_err_ratelimited(cdns->dev,
833ebf878edSPierre-Louis Bossart 				    "pm_runtime_get_sync failed in %s, ret %d\n",
834ebf878edSPierre-Louis Bossart 				    __func__, ret);
835ebf878edSPierre-Louis Bossart 		pm_runtime_put_noidle(cdns->dev);
836ebf878edSPierre-Louis Bossart 		return ret;
837ebf878edSPierre-Louis Bossart 	}
838ff16d1e5SPierre-Louis Bossart 	return 0;
8395e7484d0SRander Wang }
8405e7484d0SRander Wang 
841c46302ecSVinod Koul static int intel_hw_params(struct snd_pcm_substream *substream,
842c46302ecSVinod Koul 			   struct snd_pcm_hw_params *params,
843c46302ecSVinod Koul 			   struct snd_soc_dai *dai)
844c46302ecSVinod Koul {
845c46302ecSVinod Koul 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
846c46302ecSVinod Koul 	struct sdw_intel *sdw = cdns_to_intel(cdns);
847c46302ecSVinod Koul 	struct sdw_cdns_dma_data *dma;
84857a34790SPierre-Louis Bossart 	struct sdw_cdns_pdi *pdi;
849c46302ecSVinod Koul 	struct sdw_stream_config sconfig;
850c46302ecSVinod Koul 	struct sdw_port_config *pconfig;
85157a34790SPierre-Louis Bossart 	int ch, dir;
85257a34790SPierre-Louis Bossart 	int ret;
853c46302ecSVinod Koul 	bool pcm = true;
854c46302ecSVinod Koul 
855c46302ecSVinod Koul 	dma = snd_soc_dai_get_dma_data(dai, substream);
856c46302ecSVinod Koul 	if (!dma)
857c46302ecSVinod Koul 		return -EIO;
858c46302ecSVinod Koul 
859c46302ecSVinod Koul 	ch = params_channels(params);
860c46302ecSVinod Koul 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
861c46302ecSVinod Koul 		dir = SDW_DATA_DIR_RX;
862c46302ecSVinod Koul 	else
863c46302ecSVinod Koul 		dir = SDW_DATA_DIR_TX;
864c46302ecSVinod Koul 
86557a34790SPierre-Louis Bossart 	if (dma->stream_type == SDW_STREAM_PDM)
866c46302ecSVinod Koul 		pcm = false;
867c46302ecSVinod Koul 
86857a34790SPierre-Louis Bossart 	if (pcm)
8691b53385eSBard Liao 		pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id);
87057a34790SPierre-Louis Bossart 	else
8711b53385eSBard Liao 		pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir, dai->id);
872c46302ecSVinod Koul 
87357a34790SPierre-Louis Bossart 	if (!pdi) {
874c46302ecSVinod Koul 		ret = -EINVAL;
87557a34790SPierre-Louis Bossart 		goto error;
876c46302ecSVinod Koul 	}
87757a34790SPierre-Louis Bossart 
87857a34790SPierre-Louis Bossart 	/* do run-time configurations for SHIM, ALH and PDI/PORT */
87957a34790SPierre-Louis Bossart 	intel_pdi_shim_configure(sdw, pdi);
88057a34790SPierre-Louis Bossart 	intel_pdi_alh_configure(sdw, pdi);
88157a34790SPierre-Louis Bossart 	sdw_cdns_config_stream(cdns, ch, dir, pdi);
88257a34790SPierre-Louis Bossart 
883a5a0239cSBard Liao 	/* store pdi and hw_params, may be needed in prepare step */
884a5a0239cSBard Liao 	dma->suspended = false;
885a5a0239cSBard Liao 	dma->pdi = pdi;
886a5a0239cSBard Liao 	dma->hw_params = params;
887c46302ecSVinod Koul 
888c46302ecSVinod Koul 	/* Inform DSP about PDI stream number */
8894b206d34SRander Wang 	ret = intel_params_stream(sdw, substream, dai, params,
8904b206d34SRander Wang 				  sdw->instance,
89157a34790SPierre-Louis Bossart 				  pdi->intel_alh_id);
892c46302ecSVinod Koul 	if (ret)
89357a34790SPierre-Louis Bossart 		goto error;
894c46302ecSVinod Koul 
895c46302ecSVinod Koul 	sconfig.direction = dir;
896c46302ecSVinod Koul 	sconfig.ch_count = ch;
897c46302ecSVinod Koul 	sconfig.frame_rate = params_rate(params);
898c46302ecSVinod Koul 	sconfig.type = dma->stream_type;
899c46302ecSVinod Koul 
900c46302ecSVinod Koul 	if (dma->stream_type == SDW_STREAM_PDM) {
901c46302ecSVinod Koul 		sconfig.frame_rate *= 50;
902c46302ecSVinod Koul 		sconfig.bps = 1;
903c46302ecSVinod Koul 	} else {
904c46302ecSVinod Koul 		sconfig.bps = snd_pcm_format_width(params_format(params));
905c46302ecSVinod Koul 	}
906c46302ecSVinod Koul 
907c46302ecSVinod Koul 	/* Port configuration */
90857a34790SPierre-Louis Bossart 	pconfig = kcalloc(1, sizeof(*pconfig), GFP_KERNEL);
909c46302ecSVinod Koul 	if (!pconfig) {
910c46302ecSVinod Koul 		ret =  -ENOMEM;
91157a34790SPierre-Louis Bossart 		goto error;
912c46302ecSVinod Koul 	}
913c46302ecSVinod Koul 
91457a34790SPierre-Louis Bossart 	pconfig->num = pdi->num;
91557a34790SPierre-Louis Bossart 	pconfig->ch_mask = (1 << ch) - 1;
916c46302ecSVinod Koul 
917c46302ecSVinod Koul 	ret = sdw_stream_add_master(&cdns->bus, &sconfig,
91857a34790SPierre-Louis Bossart 				    pconfig, 1, dma->stream);
91957a34790SPierre-Louis Bossart 	if (ret)
92017ed5befSPierre-Louis Bossart 		dev_err(cdns->dev, "add master to stream failed:%d\n", ret);
921c46302ecSVinod Koul 
922c46302ecSVinod Koul 	kfree(pconfig);
92357a34790SPierre-Louis Bossart error:
924c46302ecSVinod Koul 	return ret;
925c46302ecSVinod Koul }
926c46302ecSVinod Koul 
92727b198f4SRander Wang static int intel_prepare(struct snd_pcm_substream *substream,
92827b198f4SRander Wang 			 struct snd_soc_dai *dai)
92927b198f4SRander Wang {
930a5a0239cSBard Liao 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
931a5a0239cSBard Liao 	struct sdw_intel *sdw = cdns_to_intel(cdns);
93227b198f4SRander Wang 	struct sdw_cdns_dma_data *dma;
933a5a0239cSBard Liao 	int ch, dir;
934a5a0239cSBard Liao 	int ret;
93527b198f4SRander Wang 
93627b198f4SRander Wang 	dma = snd_soc_dai_get_dma_data(dai, substream);
93727b198f4SRander Wang 	if (!dma) {
93827b198f4SRander Wang 		dev_err(dai->dev, "failed to get dma data in %s",
93927b198f4SRander Wang 			__func__);
94027b198f4SRander Wang 		return -EIO;
94127b198f4SRander Wang 	}
94227b198f4SRander Wang 
943a5a0239cSBard Liao 	if (dma->suspended) {
944a5a0239cSBard Liao 		dma->suspended = false;
945a5a0239cSBard Liao 
946a5a0239cSBard Liao 		/*
947a5a0239cSBard Liao 		 * .prepare() is called after system resume, where we
948a5a0239cSBard Liao 		 * need to reinitialize the SHIM/ALH/Cadence IP.
949a5a0239cSBard Liao 		 * .prepare() is also called to deal with underflows,
950a5a0239cSBard Liao 		 * but in those cases we cannot touch ALH/SHIM
951a5a0239cSBard Liao 		 * registers
952a5a0239cSBard Liao 		 */
953a5a0239cSBard Liao 
954a5a0239cSBard Liao 		/* configure stream */
955a5a0239cSBard Liao 		ch = params_channels(dma->hw_params);
956a5a0239cSBard Liao 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
957a5a0239cSBard Liao 			dir = SDW_DATA_DIR_RX;
958a5a0239cSBard Liao 		else
959a5a0239cSBard Liao 			dir = SDW_DATA_DIR_TX;
960a5a0239cSBard Liao 
961a5a0239cSBard Liao 		intel_pdi_shim_configure(sdw, dma->pdi);
962a5a0239cSBard Liao 		intel_pdi_alh_configure(sdw, dma->pdi);
963a5a0239cSBard Liao 		sdw_cdns_config_stream(cdns, ch, dir, dma->pdi);
964a5a0239cSBard Liao 
965a5a0239cSBard Liao 		/* Inform DSP about PDI stream number */
966a5a0239cSBard Liao 		ret = intel_params_stream(sdw, substream, dai,
967a5a0239cSBard Liao 					  dma->hw_params,
968a5a0239cSBard Liao 					  sdw->instance,
969a5a0239cSBard Liao 					  dma->pdi->intel_alh_id);
970a5a0239cSBard Liao 		if (ret)
971a5a0239cSBard Liao 			goto err;
972a5a0239cSBard Liao 	}
973a5a0239cSBard Liao 
974a5a0239cSBard Liao 	ret = sdw_prepare_stream(dma->stream);
975a5a0239cSBard Liao 
976a5a0239cSBard Liao err:
977a5a0239cSBard Liao 	return ret;
97827b198f4SRander Wang }
97927b198f4SRander Wang 
980973a8429SRander Wang static int intel_trigger(struct snd_pcm_substream *substream, int cmd,
981973a8429SRander Wang 			 struct snd_soc_dai *dai)
982973a8429SRander Wang {
983973a8429SRander Wang 	struct sdw_cdns_dma_data *dma;
984973a8429SRander Wang 	int ret;
985973a8429SRander Wang 
986973a8429SRander Wang 	dma = snd_soc_dai_get_dma_data(dai, substream);
987973a8429SRander Wang 	if (!dma) {
988973a8429SRander Wang 		dev_err(dai->dev, "failed to get dma data in %s", __func__);
989973a8429SRander Wang 		return -EIO;
990973a8429SRander Wang 	}
991973a8429SRander Wang 
992973a8429SRander Wang 	switch (cmd) {
993973a8429SRander Wang 	case SNDRV_PCM_TRIGGER_START:
994973a8429SRander Wang 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
995973a8429SRander Wang 	case SNDRV_PCM_TRIGGER_RESUME:
996973a8429SRander Wang 		ret = sdw_enable_stream(dma->stream);
997973a8429SRander Wang 		break;
998973a8429SRander Wang 
999973a8429SRander Wang 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
1000973a8429SRander Wang 	case SNDRV_PCM_TRIGGER_SUSPEND:
1001973a8429SRander Wang 	case SNDRV_PCM_TRIGGER_STOP:
1002973a8429SRander Wang 		ret = sdw_disable_stream(dma->stream);
1003973a8429SRander Wang 		break;
1004973a8429SRander Wang 
1005973a8429SRander Wang 	default:
1006973a8429SRander Wang 		ret = -EINVAL;
1007973a8429SRander Wang 		break;
1008973a8429SRander Wang 	}
1009973a8429SRander Wang 
1010973a8429SRander Wang 	if (ret)
1011973a8429SRander Wang 		dev_err(dai->dev,
1012973a8429SRander Wang 			"%s trigger %d failed: %d",
1013973a8429SRander Wang 			__func__, cmd, ret);
1014973a8429SRander Wang 	return ret;
1015973a8429SRander Wang }
1016973a8429SRander Wang 
1017c46302ecSVinod Koul static int
1018c46302ecSVinod Koul intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
1019c46302ecSVinod Koul {
1020c46302ecSVinod Koul 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
1021eff346f2SPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1022c46302ecSVinod Koul 	struct sdw_cdns_dma_data *dma;
1023c46302ecSVinod Koul 	int ret;
1024c46302ecSVinod Koul 
1025c46302ecSVinod Koul 	dma = snd_soc_dai_get_dma_data(dai, substream);
1026c46302ecSVinod Koul 	if (!dma)
1027c46302ecSVinod Koul 		return -EIO;
1028c46302ecSVinod Koul 
1029eff346f2SPierre-Louis Bossart 	ret = sdw_deprepare_stream(dma->stream);
1030eff346f2SPierre-Louis Bossart 	if (ret) {
1031eff346f2SPierre-Louis Bossart 		dev_err(dai->dev, "sdw_deprepare_stream: failed %d", ret);
1032eff346f2SPierre-Louis Bossart 		return ret;
1033eff346f2SPierre-Louis Bossart 	}
1034eff346f2SPierre-Louis Bossart 
1035c46302ecSVinod Koul 	ret = sdw_stream_remove_master(&cdns->bus, dma->stream);
1036eff346f2SPierre-Louis Bossart 	if (ret < 0) {
103717ed5befSPierre-Louis Bossart 		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
1038c46302ecSVinod Koul 			dma->stream->name, ret);
1039c46302ecSVinod Koul 		return ret;
1040c46302ecSVinod Koul 	}
1041c46302ecSVinod Koul 
1042eff346f2SPierre-Louis Bossart 	ret = intel_free_stream(sdw, substream, dai, sdw->instance);
1043eff346f2SPierre-Louis Bossart 	if (ret < 0) {
1044eff346f2SPierre-Louis Bossart 		dev_err(dai->dev, "intel_free_stream: failed %d", ret);
1045eff346f2SPierre-Louis Bossart 		return ret;
1046eff346f2SPierre-Louis Bossart 	}
1047eff346f2SPierre-Louis Bossart 
1048a5a0239cSBard Liao 	dma->hw_params = NULL;
1049a5a0239cSBard Liao 	dma->pdi = NULL;
1050a5a0239cSBard Liao 
1051eff346f2SPierre-Louis Bossart 	return 0;
1052eff346f2SPierre-Louis Bossart }
1053eff346f2SPierre-Louis Bossart 
1054183c7687SPierre-Louis Bossart static void intel_shutdown(struct snd_pcm_substream *substream,
1055183c7687SPierre-Louis Bossart 			   struct snd_soc_dai *dai)
1056183c7687SPierre-Louis Bossart {
1057ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
1058183c7687SPierre-Louis Bossart 
1059ebf878edSPierre-Louis Bossart 	pm_runtime_mark_last_busy(cdns->dev);
1060ebf878edSPierre-Louis Bossart 	pm_runtime_put_autosuspend(cdns->dev);
1061183c7687SPierre-Louis Bossart }
1062183c7687SPierre-Louis Bossart 
1063a5a0239cSBard Liao static int intel_component_dais_suspend(struct snd_soc_component *component)
1064a5a0239cSBard Liao {
1065a5a0239cSBard Liao 	struct sdw_cdns_dma_data *dma;
1066a5a0239cSBard Liao 	struct snd_soc_dai *dai;
1067a5a0239cSBard Liao 
1068a5a0239cSBard Liao 	for_each_component_dais(component, dai) {
1069a5a0239cSBard Liao 		/*
1070a5a0239cSBard Liao 		 * we don't have a .suspend dai_ops, and we don't have access
1071a5a0239cSBard Liao 		 * to the substream, so let's mark both capture and playback
1072a5a0239cSBard Liao 		 * DMA contexts as suspended
1073a5a0239cSBard Liao 		 */
1074a5a0239cSBard Liao 		dma = dai->playback_dma_data;
1075a5a0239cSBard Liao 		if (dma)
1076a5a0239cSBard Liao 			dma->suspended = true;
1077a5a0239cSBard Liao 
1078a5a0239cSBard Liao 		dma = dai->capture_dma_data;
1079a5a0239cSBard Liao 		if (dma)
1080a5a0239cSBard Liao 			dma->suspended = true;
1081a5a0239cSBard Liao 	}
1082a5a0239cSBard Liao 
1083a5a0239cSBard Liao 	return 0;
1084a5a0239cSBard Liao }
1085a5a0239cSBard Liao 
1086c46302ecSVinod Koul static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
1087c46302ecSVinod Koul 				    void *stream, int direction)
1088c46302ecSVinod Koul {
1089c46302ecSVinod Koul 	return cdns_set_sdw_stream(dai, stream, true, direction);
1090c46302ecSVinod Koul }
1091c46302ecSVinod Koul 
1092c46302ecSVinod Koul static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai,
1093c46302ecSVinod Koul 				    void *stream, int direction)
1094c46302ecSVinod Koul {
1095c46302ecSVinod Koul 	return cdns_set_sdw_stream(dai, stream, false, direction);
1096c46302ecSVinod Koul }
1097c46302ecSVinod Koul 
109809553140SPierre-Louis Bossart static void *intel_get_sdw_stream(struct snd_soc_dai *dai,
109909553140SPierre-Louis Bossart 				  int direction)
110009553140SPierre-Louis Bossart {
110109553140SPierre-Louis Bossart 	struct sdw_cdns_dma_data *dma;
110209553140SPierre-Louis Bossart 
110309553140SPierre-Louis Bossart 	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
110409553140SPierre-Louis Bossart 		dma = dai->playback_dma_data;
110509553140SPierre-Louis Bossart 	else
110609553140SPierre-Louis Bossart 		dma = dai->capture_dma_data;
110709553140SPierre-Louis Bossart 
110809553140SPierre-Louis Bossart 	if (!dma)
110909553140SPierre-Louis Bossart 		return NULL;
111009553140SPierre-Louis Bossart 
111109553140SPierre-Louis Bossart 	return dma->stream;
111209553140SPierre-Louis Bossart }
111309553140SPierre-Louis Bossart 
1114b1635596SJulia Lawall static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
11155e7484d0SRander Wang 	.startup = intel_startup,
1116c46302ecSVinod Koul 	.hw_params = intel_hw_params,
111727b198f4SRander Wang 	.prepare = intel_prepare,
1118973a8429SRander Wang 	.trigger = intel_trigger,
1119c46302ecSVinod Koul 	.hw_free = intel_hw_free,
1120183c7687SPierre-Louis Bossart 	.shutdown = intel_shutdown,
1121c46302ecSVinod Koul 	.set_sdw_stream = intel_pcm_set_sdw_stream,
112209553140SPierre-Louis Bossart 	.get_sdw_stream = intel_get_sdw_stream,
1123c46302ecSVinod Koul };
1124c46302ecSVinod Koul 
1125b1635596SJulia Lawall static const struct snd_soc_dai_ops intel_pdm_dai_ops = {
11265e7484d0SRander Wang 	.startup = intel_startup,
1127c46302ecSVinod Koul 	.hw_params = intel_hw_params,
112827b198f4SRander Wang 	.prepare = intel_prepare,
1129973a8429SRander Wang 	.trigger = intel_trigger,
1130c46302ecSVinod Koul 	.hw_free = intel_hw_free,
1131183c7687SPierre-Louis Bossart 	.shutdown = intel_shutdown,
1132c46302ecSVinod Koul 	.set_sdw_stream = intel_pdm_set_sdw_stream,
113309553140SPierre-Louis Bossart 	.get_sdw_stream = intel_get_sdw_stream,
1134c46302ecSVinod Koul };
1135c46302ecSVinod Koul 
1136c46302ecSVinod Koul static const struct snd_soc_component_driver dai_component = {
1137c46302ecSVinod Koul 	.name           = "soundwire",
1138a5a0239cSBard Liao 	.suspend	= intel_component_dais_suspend
1139c46302ecSVinod Koul };
1140c46302ecSVinod Koul 
1141c46302ecSVinod Koul static int intel_create_dai(struct sdw_cdns *cdns,
1142c46302ecSVinod Koul 			    struct snd_soc_dai_driver *dais,
1143c46302ecSVinod Koul 			    enum intel_pdi_type type,
1144c46302ecSVinod Koul 			    u32 num, u32 off, u32 max_ch, bool pcm)
1145c46302ecSVinod Koul {
1146c46302ecSVinod Koul 	int i;
1147c46302ecSVinod Koul 
1148c46302ecSVinod Koul 	if (num == 0)
1149c46302ecSVinod Koul 		return 0;
1150c46302ecSVinod Koul 
1151c46302ecSVinod Koul 	 /* TODO: Read supported rates/formats from hardware */
1152c46302ecSVinod Koul 	for (i = off; i < (off + num); i++) {
1153bf6d6e68SPierre-Louis Bossart 		dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL,
1154bf6d6e68SPierre-Louis Bossart 					      "SDW%d Pin%d",
1155c46302ecSVinod Koul 					      cdns->instance, i);
1156c46302ecSVinod Koul 		if (!dais[i].name)
1157c46302ecSVinod Koul 			return -ENOMEM;
1158c46302ecSVinod Koul 
1159c46302ecSVinod Koul 		if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) {
1160c46302ecSVinod Koul 			dais[i].playback.channels_min = 1;
1161c46302ecSVinod Koul 			dais[i].playback.channels_max = max_ch;
1162c46302ecSVinod Koul 			dais[i].playback.rates = SNDRV_PCM_RATE_48000;
1163c46302ecSVinod Koul 			dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
1164c46302ecSVinod Koul 		}
1165c46302ecSVinod Koul 
1166c46302ecSVinod Koul 		if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) {
116739194128SSrinivas Kandagatla 			dais[i].capture.channels_min = 1;
116839194128SSrinivas Kandagatla 			dais[i].capture.channels_max = max_ch;
1169c46302ecSVinod Koul 			dais[i].capture.rates = SNDRV_PCM_RATE_48000;
1170c46302ecSVinod Koul 			dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
1171c46302ecSVinod Koul 		}
1172c46302ecSVinod Koul 
1173c46302ecSVinod Koul 		if (pcm)
1174c46302ecSVinod Koul 			dais[i].ops = &intel_pcm_dai_ops;
1175c46302ecSVinod Koul 		else
1176c46302ecSVinod Koul 			dais[i].ops = &intel_pdm_dai_ops;
1177c46302ecSVinod Koul 	}
1178c46302ecSVinod Koul 
1179c46302ecSVinod Koul 	return 0;
1180c46302ecSVinod Koul }
1181c46302ecSVinod Koul 
1182c46302ecSVinod Koul static int intel_register_dai(struct sdw_intel *sdw)
1183c46302ecSVinod Koul {
1184c46302ecSVinod Koul 	struct sdw_cdns *cdns = &sdw->cdns;
1185c46302ecSVinod Koul 	struct sdw_cdns_streams *stream;
1186c46302ecSVinod Koul 	struct snd_soc_dai_driver *dais;
1187c46302ecSVinod Koul 	int num_dai, ret, off = 0;
1188c46302ecSVinod Koul 
1189c46302ecSVinod Koul 	/* DAIs are created based on total number of PDIs supported */
1190c46302ecSVinod Koul 	num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi;
1191c46302ecSVinod Koul 
1192c46302ecSVinod Koul 	dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL);
1193c46302ecSVinod Koul 	if (!dais)
1194c46302ecSVinod Koul 		return -ENOMEM;
1195c46302ecSVinod Koul 
1196c46302ecSVinod Koul 	/* Create PCM DAIs */
1197c46302ecSVinod Koul 	stream = &cdns->pcm;
1198c46302ecSVinod Koul 
1199cf924962SBard Liao 	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in,
12001215daeeSVinod Koul 			       off, stream->num_ch_in, true);
1201c46302ecSVinod Koul 	if (ret)
1202c46302ecSVinod Koul 		return ret;
1203c46302ecSVinod Koul 
1204c46302ecSVinod Koul 	off += cdns->pcm.num_in;
12051215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out,
12061215daeeSVinod Koul 			       off, stream->num_ch_out, true);
1207c46302ecSVinod Koul 	if (ret)
1208c46302ecSVinod Koul 		return ret;
1209c46302ecSVinod Koul 
1210c46302ecSVinod Koul 	off += cdns->pcm.num_out;
12111215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd,
12121215daeeSVinod Koul 			       off, stream->num_ch_bd, true);
1213c46302ecSVinod Koul 	if (ret)
1214c46302ecSVinod Koul 		return ret;
1215c46302ecSVinod Koul 
1216c46302ecSVinod Koul 	/* Create PDM DAIs */
1217c46302ecSVinod Koul 	stream = &cdns->pdm;
1218c46302ecSVinod Koul 	off += cdns->pcm.num_bd;
12191215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pdm.num_in,
12201215daeeSVinod Koul 			       off, stream->num_ch_in, false);
1221c46302ecSVinod Koul 	if (ret)
1222c46302ecSVinod Koul 		return ret;
1223c46302ecSVinod Koul 
1224c46302ecSVinod Koul 	off += cdns->pdm.num_in;
12251215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pdm.num_out,
12261215daeeSVinod Koul 			       off, stream->num_ch_out, false);
1227c46302ecSVinod Koul 	if (ret)
1228c46302ecSVinod Koul 		return ret;
1229c46302ecSVinod Koul 
1230cf924962SBard Liao 	off += cdns->pdm.num_out;
12311215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pdm.num_bd,
12321215daeeSVinod Koul 			       off, stream->num_ch_bd, false);
1233c46302ecSVinod Koul 	if (ret)
1234c46302ecSVinod Koul 		return ret;
1235c46302ecSVinod Koul 
1236c46302ecSVinod Koul 	return snd_soc_register_component(cdns->dev, &dai_component,
1237c46302ecSVinod Koul 					  dais, num_dai);
1238c46302ecSVinod Koul }
1239c46302ecSVinod Koul 
1240085f4aceSPierre-Louis Bossart static int sdw_master_read_intel_prop(struct sdw_bus *bus)
1241085f4aceSPierre-Louis Bossart {
1242085f4aceSPierre-Louis Bossart 	struct sdw_master_prop *prop = &bus->prop;
1243085f4aceSPierre-Louis Bossart 	struct fwnode_handle *link;
1244085f4aceSPierre-Louis Bossart 	char name[32];
1245395713d8SPierre-Louis Bossart 	u32 quirk_mask;
1246085f4aceSPierre-Louis Bossart 
1247085f4aceSPierre-Louis Bossart 	/* Find master handle */
1248085f4aceSPierre-Louis Bossart 	snprintf(name, sizeof(name),
1249085f4aceSPierre-Louis Bossart 		 "mipi-sdw-link-%d-subproperties", bus->link_id);
1250085f4aceSPierre-Louis Bossart 
1251085f4aceSPierre-Louis Bossart 	link = device_get_named_child_node(bus->dev, name);
1252085f4aceSPierre-Louis Bossart 	if (!link) {
1253085f4aceSPierre-Louis Bossart 		dev_err(bus->dev, "Master node %s not found\n", name);
1254085f4aceSPierre-Louis Bossart 		return -EIO;
1255085f4aceSPierre-Louis Bossart 	}
1256085f4aceSPierre-Louis Bossart 
1257085f4aceSPierre-Louis Bossart 	fwnode_property_read_u32(link,
1258085f4aceSPierre-Louis Bossart 				 "intel-sdw-ip-clock",
1259085f4aceSPierre-Louis Bossart 				 &prop->mclk_freq);
1260395713d8SPierre-Louis Bossart 
1261a19efb52SBard Liao 	/* the values reported by BIOS are the 2x clock, not the bus clock */
1262a19efb52SBard Liao 	prop->mclk_freq /= 2;
1263a19efb52SBard Liao 
1264395713d8SPierre-Louis Bossart 	fwnode_property_read_u32(link,
1265395713d8SPierre-Louis Bossart 				 "intel-quirk-mask",
1266395713d8SPierre-Louis Bossart 				 &quirk_mask);
1267395713d8SPierre-Louis Bossart 
1268395713d8SPierre-Louis Bossart 	if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
1269395713d8SPierre-Louis Bossart 		prop->hw_disabled = true;
1270395713d8SPierre-Louis Bossart 
1271085f4aceSPierre-Louis Bossart 	return 0;
1272085f4aceSPierre-Louis Bossart }
1273085f4aceSPierre-Louis Bossart 
127471bb8a1bSVinod Koul static int intel_prop_read(struct sdw_bus *bus)
127571bb8a1bSVinod Koul {
127671bb8a1bSVinod Koul 	/* Initialize with default handler to read all DisCo properties */
127771bb8a1bSVinod Koul 	sdw_master_read_prop(bus);
127871bb8a1bSVinod Koul 
1279085f4aceSPierre-Louis Bossart 	/* read Intel-specific properties */
1280085f4aceSPierre-Louis Bossart 	sdw_master_read_intel_prop(bus);
1281085f4aceSPierre-Louis Bossart 
128271bb8a1bSVinod Koul 	return 0;
128371bb8a1bSVinod Koul }
128471bb8a1bSVinod Koul 
1285c91605f4SShreyas NC static struct sdw_master_ops sdw_intel_ops = {
1286c91605f4SShreyas NC 	.read_prop = sdw_master_read_prop,
1287c91605f4SShreyas NC 	.xfer_msg = cdns_xfer_msg,
1288c91605f4SShreyas NC 	.xfer_msg_defer = cdns_xfer_msg_defer,
1289c91605f4SShreyas NC 	.reset_page_addr = cdns_reset_page_addr,
129007abeff1SVinod Koul 	.set_bus_conf = cdns_bus_conf,
129130246e2dSShreyas NC 	.pre_bank_switch = intel_pre_bank_switch,
129230246e2dSShreyas NC 	.post_bank_switch = intel_post_bank_switch,
1293c91605f4SShreyas NC };
1294c91605f4SShreyas NC 
1295dfbe642dSPierre-Louis Bossart static int intel_init(struct sdw_intel *sdw)
1296dfbe642dSPierre-Louis Bossart {
12974a17c441SPierre-Louis Bossart 	bool clock_stop;
12984a17c441SPierre-Louis Bossart 
1299dfbe642dSPierre-Louis Bossart 	/* Initialize shim and controller */
1300dfbe642dSPierre-Louis Bossart 	intel_link_power_up(sdw);
13014a17c441SPierre-Louis Bossart 
13024a17c441SPierre-Louis Bossart 	clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns);
13034a17c441SPierre-Louis Bossart 
13044a17c441SPierre-Louis Bossart 	intel_shim_init(sdw, clock_stop);
13054a17c441SPierre-Louis Bossart 
13064a17c441SPierre-Louis Bossart 	if (clock_stop)
13074a17c441SPierre-Louis Bossart 		return 0;
1308dfbe642dSPierre-Louis Bossart 
13097b174f24SRander Wang 	return sdw_cdns_init(&sdw->cdns);
1310dfbe642dSPierre-Louis Bossart }
1311dfbe642dSPierre-Louis Bossart 
131271bb8a1bSVinod Koul /*
131371bb8a1bSVinod Koul  * probe and init
131471bb8a1bSVinod Koul  */
1315b6109dd6SPierre-Louis Bossart static int intel_master_probe(struct platform_device *pdev)
131671bb8a1bSVinod Koul {
1317b6109dd6SPierre-Louis Bossart 	struct device *dev = &pdev->dev;
131871bb8a1bSVinod Koul 	struct sdw_intel *sdw;
131983e129afSPierre-Louis Bossart 	struct sdw_cdns *cdns;
1320b6109dd6SPierre-Louis Bossart 	struct sdw_bus *bus;
132171bb8a1bSVinod Koul 	int ret;
132271bb8a1bSVinod Koul 
1323b6109dd6SPierre-Louis Bossart 	sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL);
132471bb8a1bSVinod Koul 	if (!sdw)
132571bb8a1bSVinod Koul 		return -ENOMEM;
132671bb8a1bSVinod Koul 
132783e129afSPierre-Louis Bossart 	cdns = &sdw->cdns;
132883e129afSPierre-Louis Bossart 	bus = &cdns->bus;
132971bb8a1bSVinod Koul 
133071bb8a1bSVinod Koul 	sdw->instance = pdev->id;
1331b6109dd6SPierre-Louis Bossart 	sdw->link_res = dev_get_platdata(dev);
133283e129afSPierre-Louis Bossart 	cdns->dev = dev;
133383e129afSPierre-Louis Bossart 	cdns->registers = sdw->link_res->registers;
133483e129afSPierre-Louis Bossart 	cdns->instance = sdw->instance;
133583e129afSPierre-Louis Bossart 	cdns->msg_count = 0;
133683e129afSPierre-Louis Bossart 
1337b6109dd6SPierre-Louis Bossart 	bus->link_id = pdev->id;
133871bb8a1bSVinod Koul 
133983e129afSPierre-Louis Bossart 	sdw_cdns_probe(cdns);
134071bb8a1bSVinod Koul 
134171bb8a1bSVinod Koul 	/* Set property read ops */
1342c91605f4SShreyas NC 	sdw_intel_ops.read_prop = intel_prop_read;
1343b6109dd6SPierre-Louis Bossart 	bus->ops = &sdw_intel_ops;
134471bb8a1bSVinod Koul 
1345b6109dd6SPierre-Louis Bossart 	/* set driver data, accessed by snd_soc_dai_get_drvdata() */
134683e129afSPierre-Louis Bossart 	dev_set_drvdata(dev, cdns);
134771bb8a1bSVinod Koul 
1348b6109dd6SPierre-Louis Bossart 	ret = sdw_bus_master_add(bus, dev, dev->fwnode);
134971bb8a1bSVinod Koul 	if (ret) {
1350b6109dd6SPierre-Louis Bossart 		dev_err(dev, "sdw_bus_master_add fail: %d\n", ret);
13519e3d47fbSPierre-Louis Bossart 		return ret;
135271bb8a1bSVinod Koul 	}
135371bb8a1bSVinod Koul 
13546d2c6669SPierre-Louis Bossart 	if (bus->prop.hw_disabled)
1355b6109dd6SPierre-Louis Bossart 		dev_info(dev,
1356b6109dd6SPierre-Louis Bossart 			 "SoundWire master %d is disabled, will be ignored\n",
1357b6109dd6SPierre-Louis Bossart 			 bus->link_id);
13586d2c6669SPierre-Louis Bossart 
13596d2c6669SPierre-Louis Bossart 	return 0;
13606d2c6669SPierre-Louis Bossart }
13616d2c6669SPierre-Louis Bossart 
13626d2c6669SPierre-Louis Bossart int intel_master_startup(struct platform_device *pdev)
13636d2c6669SPierre-Louis Bossart {
13646d2c6669SPierre-Louis Bossart 	struct sdw_cdns_stream_config config;
13656d2c6669SPierre-Louis Bossart 	struct device *dev = &pdev->dev;
13666d2c6669SPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
13676d2c6669SPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
13686d2c6669SPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1369ebf878edSPierre-Louis Bossart 	int link_flags;
13706d2c6669SPierre-Louis Bossart 	int ret;
13716d2c6669SPierre-Louis Bossart 
13726d2c6669SPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
13736d2c6669SPierre-Louis Bossart 		dev_info(dev,
13746d2c6669SPierre-Louis Bossart 			 "SoundWire master %d is disabled, ignoring\n",
13756d2c6669SPierre-Louis Bossart 			 sdw->instance);
1376395713d8SPierre-Louis Bossart 		return 0;
1377395713d8SPierre-Louis Bossart 	}
1378395713d8SPierre-Louis Bossart 
1379dfbe642dSPierre-Louis Bossart 	/* Initialize shim, controller and Cadence IP */
1380dfbe642dSPierre-Louis Bossart 	ret = intel_init(sdw);
138171bb8a1bSVinod Koul 	if (ret)
138271bb8a1bSVinod Koul 		goto err_init;
138371bb8a1bSVinod Koul 
138437a2d22bSVinod Koul 	/* Read the PDI config and initialize cadence PDI */
138537a2d22bSVinod Koul 	intel_pdi_init(sdw, &config);
138683e129afSPierre-Louis Bossart 	ret = sdw_cdns_pdi_init(cdns, config);
138771bb8a1bSVinod Koul 	if (ret)
138871bb8a1bSVinod Koul 		goto err_init;
138971bb8a1bSVinod Koul 
139037a2d22bSVinod Koul 	intel_pdi_ch_update(sdw);
139137a2d22bSVinod Koul 
139283e129afSPierre-Louis Bossart 	ret = sdw_cdns_enable_interrupt(cdns, true);
139371bb8a1bSVinod Koul 	if (ret < 0) {
1394b6109dd6SPierre-Louis Bossart 		dev_err(dev, "cannot enable interrupts\n");
139571bb8a1bSVinod Koul 		goto err_init;
139671bb8a1bSVinod Koul 	}
139771bb8a1bSVinod Koul 
139883e129afSPierre-Louis Bossart 	ret = sdw_cdns_exit_reset(cdns);
139949ea07d3SPierre-Louis Bossart 	if (ret < 0) {
1400b6109dd6SPierre-Louis Bossart 		dev_err(dev, "unable to exit bus reset sequence\n");
14019e3d47fbSPierre-Louis Bossart 		goto err_interrupt;
140249ea07d3SPierre-Louis Bossart 	}
140349ea07d3SPierre-Louis Bossart 
1404c46302ecSVinod Koul 	/* Register DAIs */
1405c46302ecSVinod Koul 	ret = intel_register_dai(sdw);
1406c46302ecSVinod Koul 	if (ret) {
1407b6109dd6SPierre-Louis Bossart 		dev_err(dev, "DAI registration failed: %d\n", ret);
1408b6109dd6SPierre-Louis Bossart 		snd_soc_unregister_component(dev);
14099e3d47fbSPierre-Louis Bossart 		goto err_interrupt;
1410c46302ecSVinod Koul 	}
1411c46302ecSVinod Koul 
141279ee6631SPierre-Louis Bossart 	intel_debugfs_init(sdw);
141379ee6631SPierre-Louis Bossart 
1414ebf878edSPierre-Louis Bossart 	/* Enable runtime PM */
1415ebf878edSPierre-Louis Bossart 	link_flags = md_flags >> (bus->link_id * 8);
1416ebf878edSPierre-Louis Bossart 	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) {
1417ebf878edSPierre-Louis Bossart 		pm_runtime_set_autosuspend_delay(dev,
1418ebf878edSPierre-Louis Bossart 						 INTEL_MASTER_SUSPEND_DELAY_MS);
1419ebf878edSPierre-Louis Bossart 		pm_runtime_use_autosuspend(dev);
1420ebf878edSPierre-Louis Bossart 		pm_runtime_mark_last_busy(dev);
1421ebf878edSPierre-Louis Bossart 
1422ebf878edSPierre-Louis Bossart 		pm_runtime_set_active(dev);
1423ebf878edSPierre-Louis Bossart 		pm_runtime_enable(dev);
1424ebf878edSPierre-Louis Bossart 	}
1425ebf878edSPierre-Louis Bossart 
1426a2d9c161SPierre-Louis Bossart 	/*
1427a2d9c161SPierre-Louis Bossart 	 * The runtime PM status of Slave devices is "Unsupported"
1428a2d9c161SPierre-Louis Bossart 	 * until they report as ATTACHED. If they don't, e.g. because
1429a2d9c161SPierre-Louis Bossart 	 * there are no Slave devices populated or if the power-on is
1430a2d9c161SPierre-Louis Bossart 	 * delayed or dependent on a power switch, the Master will
1431a2d9c161SPierre-Louis Bossart 	 * remain active and prevent its parent from suspending.
1432a2d9c161SPierre-Louis Bossart 	 *
1433a2d9c161SPierre-Louis Bossart 	 * Conditionally force the pm_runtime core to re-evaluate the
1434a2d9c161SPierre-Louis Bossart 	 * Master status in the absence of any Slave activity. A quirk
1435a2d9c161SPierre-Louis Bossart 	 * is provided to e.g. deal with Slaves that may be powered on
1436a2d9c161SPierre-Louis Bossart 	 * with a delay. A more complete solution would require the
1437a2d9c161SPierre-Louis Bossart 	 * definition of Master properties.
1438a2d9c161SPierre-Louis Bossart 	 */
1439a2d9c161SPierre-Louis Bossart 	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
1440a2d9c161SPierre-Louis Bossart 		pm_runtime_idle(dev);
1441a2d9c161SPierre-Louis Bossart 
144271bb8a1bSVinod Koul 	return 0;
144371bb8a1bSVinod Koul 
14449e3d47fbSPierre-Louis Bossart err_interrupt:
144583e129afSPierre-Louis Bossart 	sdw_cdns_enable_interrupt(cdns, false);
144671bb8a1bSVinod Koul err_init:
144771bb8a1bSVinod Koul 	return ret;
144871bb8a1bSVinod Koul }
144971bb8a1bSVinod Koul 
1450b6109dd6SPierre-Louis Bossart static int intel_master_remove(struct platform_device *pdev)
145171bb8a1bSVinod Koul {
1452b6109dd6SPierre-Louis Bossart 	struct device *dev = &pdev->dev;
145383e129afSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
145483e129afSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
145583e129afSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1456b6109dd6SPierre-Louis Bossart 
1457b6109dd6SPierre-Louis Bossart 	if (!bus->prop.hw_disabled) {
145879ee6631SPierre-Louis Bossart 		intel_debugfs_exit(sdw);
145983e129afSPierre-Louis Bossart 		sdw_cdns_enable_interrupt(cdns, false);
1460b6109dd6SPierre-Louis Bossart 		snd_soc_unregister_component(dev);
1461395713d8SPierre-Louis Bossart 	}
1462b6109dd6SPierre-Louis Bossart 	sdw_bus_master_delete(bus);
146371bb8a1bSVinod Koul 
146471bb8a1bSVinod Koul 	return 0;
146571bb8a1bSVinod Koul }
146671bb8a1bSVinod Koul 
1467ab2c9132SRander Wang int intel_master_process_wakeen_event(struct platform_device *pdev)
1468ab2c9132SRander Wang {
1469ab2c9132SRander Wang 	struct device *dev = &pdev->dev;
147071bb8a1bSVinod Koul 	struct sdw_intel *sdw;
1471ab2c9132SRander Wang 	struct sdw_bus *bus;
1472ab2c9132SRander Wang 	void __iomem *shim;
1473ab2c9132SRander Wang 	u16 wake_sts;
147471bb8a1bSVinod Koul 
147571bb8a1bSVinod Koul 	sdw = platform_get_drvdata(pdev);
1476ab2c9132SRander Wang 	bus = &sdw->cdns.bus;
147771bb8a1bSVinod Koul 
1478ab2c9132SRander Wang 	if (bus->prop.hw_disabled) {
1479ab2c9132SRander Wang 		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", bus->link_id);
1480ab2c9132SRander Wang 		return 0;
148171bb8a1bSVinod Koul 	}
1482ab2c9132SRander Wang 
1483ab2c9132SRander Wang 	shim = sdw->link_res->shim;
1484ab2c9132SRander Wang 	wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
1485ab2c9132SRander Wang 
1486ab2c9132SRander Wang 	if (!(wake_sts & BIT(sdw->instance)))
1487ab2c9132SRander Wang 		return 0;
1488ab2c9132SRander Wang 
1489ab2c9132SRander Wang 	/* disable WAKEEN interrupt ASAP to prevent interrupt flood */
1490ab2c9132SRander Wang 	intel_shim_wake(sdw, false);
1491ab2c9132SRander Wang 
1492ab2c9132SRander Wang 	/*
1493ab2c9132SRander Wang 	 * resume the Master, which will generate a bus reset and result in
1494ab2c9132SRander Wang 	 * Slaves re-attaching and be re-enumerated. The SoundWire physical
1495ab2c9132SRander Wang 	 * device which generated the wake will trigger an interrupt, which
1496ab2c9132SRander Wang 	 * will in turn cause the corresponding Linux Slave device to be
1497ab2c9132SRander Wang 	 * resumed and the Slave codec driver to check the status.
1498ab2c9132SRander Wang 	 */
1499ab2c9132SRander Wang 	pm_request_resume(dev);
150071bb8a1bSVinod Koul 
150171bb8a1bSVinod Koul 	return 0;
150271bb8a1bSVinod Koul }
150371bb8a1bSVinod Koul 
15049b3b4b3fSPierre-Louis Bossart /*
15059b3b4b3fSPierre-Louis Bossart  * PM calls
15069b3b4b3fSPierre-Louis Bossart  */
15079b3b4b3fSPierre-Louis Bossart 
15089b3b4b3fSPierre-Louis Bossart #ifdef CONFIG_PM
15099b3b4b3fSPierre-Louis Bossart 
15109b3b4b3fSPierre-Louis Bossart static int intel_suspend(struct device *dev)
15119b3b4b3fSPierre-Louis Bossart {
15129b3b4b3fSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
15139b3b4b3fSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
15149b3b4b3fSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
15159b3b4b3fSPierre-Louis Bossart 	int ret;
15169b3b4b3fSPierre-Louis Bossart 
15179b3b4b3fSPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
15189b3b4b3fSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
15199b3b4b3fSPierre-Louis Bossart 			bus->link_id);
15209b3b4b3fSPierre-Louis Bossart 		return 0;
15219b3b4b3fSPierre-Louis Bossart 	}
15229b3b4b3fSPierre-Louis Bossart 
1523b61b8b37SPierre-Louis Bossart 	if (pm_runtime_suspended(dev)) {
1524b61b8b37SPierre-Louis Bossart 		dev_dbg(dev, "%s: pm_runtime status: suspended\n", __func__);
1525b61b8b37SPierre-Louis Bossart 
1526b61b8b37SPierre-Louis Bossart 		return 0;
1527b61b8b37SPierre-Louis Bossart 	}
1528b61b8b37SPierre-Louis Bossart 
15299b3b4b3fSPierre-Louis Bossart 	ret = sdw_cdns_enable_interrupt(cdns, false);
15309b3b4b3fSPierre-Louis Bossart 	if (ret < 0) {
15319b3b4b3fSPierre-Louis Bossart 		dev_err(dev, "cannot disable interrupts on suspend\n");
15329b3b4b3fSPierre-Louis Bossart 		return ret;
15339b3b4b3fSPierre-Louis Bossart 	}
15349b3b4b3fSPierre-Louis Bossart 
15359b3b4b3fSPierre-Louis Bossart 	ret = intel_link_power_down(sdw);
15369b3b4b3fSPierre-Louis Bossart 	if (ret) {
15379b3b4b3fSPierre-Louis Bossart 		dev_err(dev, "Link power down failed: %d", ret);
15389b3b4b3fSPierre-Louis Bossart 		return ret;
15399b3b4b3fSPierre-Louis Bossart 	}
15409b3b4b3fSPierre-Louis Bossart 
15419b3b4b3fSPierre-Louis Bossart 	intel_shim_wake(sdw, false);
15429b3b4b3fSPierre-Louis Bossart 
15439b3b4b3fSPierre-Louis Bossart 	return 0;
15449b3b4b3fSPierre-Louis Bossart }
15459b3b4b3fSPierre-Louis Bossart 
1546ebf878edSPierre-Louis Bossart static int intel_suspend_runtime(struct device *dev)
1547ebf878edSPierre-Louis Bossart {
1548ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
1549ebf878edSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1550ebf878edSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1551*a320f41eSPierre-Louis Bossart 	u32 clock_stop_quirks;
1552ebf878edSPierre-Louis Bossart 	int ret;
1553ebf878edSPierre-Louis Bossart 
1554ebf878edSPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
1555ebf878edSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
1556ebf878edSPierre-Louis Bossart 			bus->link_id);
1557ebf878edSPierre-Louis Bossart 		return 0;
1558ebf878edSPierre-Louis Bossart 	}
1559ebf878edSPierre-Louis Bossart 
1560*a320f41eSPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1561*a320f41eSPierre-Louis Bossart 
1562*a320f41eSPierre-Louis Bossart 	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
1563*a320f41eSPierre-Louis Bossart 
1564ebf878edSPierre-Louis Bossart 		ret = sdw_cdns_enable_interrupt(cdns, false);
1565ebf878edSPierre-Louis Bossart 		if (ret < 0) {
1566ebf878edSPierre-Louis Bossart 			dev_err(dev, "cannot disable interrupts on suspend\n");
1567ebf878edSPierre-Louis Bossart 			return ret;
1568ebf878edSPierre-Louis Bossart 		}
1569ebf878edSPierre-Louis Bossart 
1570ebf878edSPierre-Louis Bossart 		ret = intel_link_power_down(sdw);
1571ebf878edSPierre-Louis Bossart 		if (ret) {
1572ebf878edSPierre-Louis Bossart 			dev_err(dev, "Link power down failed: %d", ret);
1573ebf878edSPierre-Louis Bossart 			return ret;
1574ebf878edSPierre-Louis Bossart 		}
1575ebf878edSPierre-Louis Bossart 
1576ebf878edSPierre-Louis Bossart 		intel_shim_wake(sdw, false);
1577ebf878edSPierre-Louis Bossart 
1578*a320f41eSPierre-Louis Bossart 	} else {
1579*a320f41eSPierre-Louis Bossart 		dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
1580*a320f41eSPierre-Louis Bossart 			__func__, clock_stop_quirks);
1581*a320f41eSPierre-Louis Bossart 		ret = -EINVAL;
1582*a320f41eSPierre-Louis Bossart 	}
1583*a320f41eSPierre-Louis Bossart 
1584*a320f41eSPierre-Louis Bossart 	return ret;
1585ebf878edSPierre-Louis Bossart }
1586ebf878edSPierre-Louis Bossart 
15879b3b4b3fSPierre-Louis Bossart static int intel_resume(struct device *dev)
15889b3b4b3fSPierre-Louis Bossart {
15899b3b4b3fSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
15909b3b4b3fSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
15919b3b4b3fSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1592a2d9c161SPierre-Louis Bossart 	int link_flags;
15939b3b4b3fSPierre-Louis Bossart 	int ret;
15949b3b4b3fSPierre-Louis Bossart 
15959b3b4b3fSPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
15969b3b4b3fSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
15979b3b4b3fSPierre-Louis Bossart 			bus->link_id);
15989b3b4b3fSPierre-Louis Bossart 		return 0;
15999b3b4b3fSPierre-Louis Bossart 	}
16009b3b4b3fSPierre-Louis Bossart 
1601b61b8b37SPierre-Louis Bossart 	if (pm_runtime_suspended(dev)) {
1602b61b8b37SPierre-Louis Bossart 		dev_dbg(dev, "%s: pm_runtime status was suspended, forcing active\n", __func__);
1603b61b8b37SPierre-Louis Bossart 
1604b61b8b37SPierre-Louis Bossart 		/* follow required sequence from runtime_pm.rst */
1605b61b8b37SPierre-Louis Bossart 		pm_runtime_disable(dev);
1606b61b8b37SPierre-Louis Bossart 		pm_runtime_set_active(dev);
1607b61b8b37SPierre-Louis Bossart 		pm_runtime_mark_last_busy(dev);
1608b61b8b37SPierre-Louis Bossart 		pm_runtime_enable(dev);
1609a2d9c161SPierre-Louis Bossart 
1610a2d9c161SPierre-Louis Bossart 		link_flags = md_flags >> (bus->link_id * 8);
1611a2d9c161SPierre-Louis Bossart 		if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
1612a2d9c161SPierre-Louis Bossart 			pm_runtime_idle(dev);
1613b61b8b37SPierre-Louis Bossart 	}
1614b61b8b37SPierre-Louis Bossart 
16159b3b4b3fSPierre-Louis Bossart 	ret = intel_init(sdw);
16169b3b4b3fSPierre-Louis Bossart 	if (ret) {
16179b3b4b3fSPierre-Louis Bossart 		dev_err(dev, "%s failed: %d", __func__, ret);
16189b3b4b3fSPierre-Louis Bossart 		return ret;
16199b3b4b3fSPierre-Louis Bossart 	}
16209b3b4b3fSPierre-Louis Bossart 
162199b6a30fSPierre-Louis Bossart 	/*
162299b6a30fSPierre-Louis Bossart 	 * make sure all Slaves are tagged as UNATTACHED and provide
162399b6a30fSPierre-Louis Bossart 	 * reason for reinitialization
162499b6a30fSPierre-Louis Bossart 	 */
162599b6a30fSPierre-Louis Bossart 	sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
162699b6a30fSPierre-Louis Bossart 
16279b3b4b3fSPierre-Louis Bossart 	ret = sdw_cdns_enable_interrupt(cdns, true);
16289b3b4b3fSPierre-Louis Bossart 	if (ret < 0) {
16299b3b4b3fSPierre-Louis Bossart 		dev_err(dev, "cannot enable interrupts during resume\n");
16309b3b4b3fSPierre-Louis Bossart 		return ret;
16319b3b4b3fSPierre-Louis Bossart 	}
16329b3b4b3fSPierre-Louis Bossart 
16339b3b4b3fSPierre-Louis Bossart 	ret = sdw_cdns_exit_reset(cdns);
16349b3b4b3fSPierre-Louis Bossart 	if (ret < 0) {
16359b3b4b3fSPierre-Louis Bossart 		dev_err(dev, "unable to exit bus reset sequence during resume\n");
16369b3b4b3fSPierre-Louis Bossart 		return ret;
16379b3b4b3fSPierre-Louis Bossart 	}
16389b3b4b3fSPierre-Louis Bossart 
1639cb1e6d59SPierre-Louis Bossart 	/*
1640cb1e6d59SPierre-Louis Bossart 	 * after system resume, the pm_runtime suspend() may kick in
1641cb1e6d59SPierre-Louis Bossart 	 * during the enumeration, before any children device force the
1642cb1e6d59SPierre-Louis Bossart 	 * master device to remain active.  Using pm_runtime_get()
1643cb1e6d59SPierre-Louis Bossart 	 * routines is not really possible, since it'd prevent the
1644cb1e6d59SPierre-Louis Bossart 	 * master from suspending.
1645cb1e6d59SPierre-Louis Bossart 	 * A reasonable compromise is to update the pm_runtime
1646cb1e6d59SPierre-Louis Bossart 	 * counters and delay the pm_runtime suspend by several
1647cb1e6d59SPierre-Louis Bossart 	 * seconds, by when all enumeration should be complete.
1648cb1e6d59SPierre-Louis Bossart 	 */
1649cb1e6d59SPierre-Louis Bossart 	pm_runtime_mark_last_busy(dev);
1650cb1e6d59SPierre-Louis Bossart 
16519b3b4b3fSPierre-Louis Bossart 	return ret;
16529b3b4b3fSPierre-Louis Bossart }
16539b3b4b3fSPierre-Louis Bossart 
1654ebf878edSPierre-Louis Bossart static int intel_resume_runtime(struct device *dev)
1655ebf878edSPierre-Louis Bossart {
1656ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
1657ebf878edSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1658ebf878edSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1659*a320f41eSPierre-Louis Bossart 	u32 clock_stop_quirks;
1660ebf878edSPierre-Louis Bossart 	int ret;
1661ebf878edSPierre-Louis Bossart 
1662ebf878edSPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
1663ebf878edSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
1664ebf878edSPierre-Louis Bossart 			bus->link_id);
1665ebf878edSPierre-Louis Bossart 		return 0;
1666ebf878edSPierre-Louis Bossart 	}
1667ebf878edSPierre-Louis Bossart 
1668*a320f41eSPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1669*a320f41eSPierre-Louis Bossart 
1670*a320f41eSPierre-Louis Bossart 	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
1671ebf878edSPierre-Louis Bossart 		ret = intel_init(sdw);
1672ebf878edSPierre-Louis Bossart 		if (ret) {
1673ebf878edSPierre-Louis Bossart 			dev_err(dev, "%s failed: %d", __func__, ret);
1674ebf878edSPierre-Louis Bossart 			return ret;
1675ebf878edSPierre-Louis Bossart 		}
1676ebf878edSPierre-Louis Bossart 
167799b6a30fSPierre-Louis Bossart 		/*
167899b6a30fSPierre-Louis Bossart 		 * make sure all Slaves are tagged as UNATTACHED and provide
167999b6a30fSPierre-Louis Bossart 		 * reason for reinitialization
168099b6a30fSPierre-Louis Bossart 		 */
168199b6a30fSPierre-Louis Bossart 		sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
168299b6a30fSPierre-Louis Bossart 
1683ebf878edSPierre-Louis Bossart 		ret = sdw_cdns_enable_interrupt(cdns, true);
1684ebf878edSPierre-Louis Bossart 		if (ret < 0) {
1685ebf878edSPierre-Louis Bossart 			dev_err(dev, "cannot enable interrupts during resume\n");
1686ebf878edSPierre-Louis Bossart 			return ret;
1687ebf878edSPierre-Louis Bossart 		}
1688ebf878edSPierre-Louis Bossart 
1689ebf878edSPierre-Louis Bossart 		ret = sdw_cdns_exit_reset(cdns);
1690ebf878edSPierre-Louis Bossart 		if (ret < 0) {
1691ebf878edSPierre-Louis Bossart 			dev_err(dev, "unable to exit bus reset sequence during resume\n");
1692ebf878edSPierre-Louis Bossart 			return ret;
1693ebf878edSPierre-Louis Bossart 		}
1694*a320f41eSPierre-Louis Bossart 	} else {
1695*a320f41eSPierre-Louis Bossart 		dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
1696*a320f41eSPierre-Louis Bossart 			__func__, clock_stop_quirks);
1697*a320f41eSPierre-Louis Bossart 		ret = -EINVAL;
1698*a320f41eSPierre-Louis Bossart 	}
1699ebf878edSPierre-Louis Bossart 
1700ebf878edSPierre-Louis Bossart 	return ret;
1701ebf878edSPierre-Louis Bossart }
1702ebf878edSPierre-Louis Bossart 
17039b3b4b3fSPierre-Louis Bossart #endif
17049b3b4b3fSPierre-Louis Bossart 
17059b3b4b3fSPierre-Louis Bossart static const struct dev_pm_ops intel_pm = {
17069b3b4b3fSPierre-Louis Bossart 	SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume)
1707ebf878edSPierre-Louis Bossart 	SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL)
17089b3b4b3fSPierre-Louis Bossart };
17099b3b4b3fSPierre-Louis Bossart 
171071bb8a1bSVinod Koul static struct platform_driver sdw_intel_drv = {
1711b6109dd6SPierre-Louis Bossart 	.probe = intel_master_probe,
1712b6109dd6SPierre-Louis Bossart 	.remove = intel_master_remove,
171371bb8a1bSVinod Koul 	.driver = {
17146d2c6669SPierre-Louis Bossart 		.name = "intel-sdw",
17159b3b4b3fSPierre-Louis Bossart 		.pm = &intel_pm,
17169b3b4b3fSPierre-Louis Bossart 	}
171771bb8a1bSVinod Koul };
171871bb8a1bSVinod Koul 
171971bb8a1bSVinod Koul module_platform_driver(sdw_intel_drv);
172071bb8a1bSVinod Koul 
172171bb8a1bSVinod Koul MODULE_LICENSE("Dual BSD/GPL");
17226d2c6669SPierre-Louis Bossart MODULE_ALIAS("platform:intel-sdw");
172371bb8a1bSVinod Koul MODULE_DESCRIPTION("Intel Soundwire Master Driver");
1724