xref: /linux/drivers/soundwire/intel.c (revision 49c9ff45991a5a62e040c8b43c89a9ab38a0a91f)
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>
1429a269c6SPierre-Louis Bossart #include <linux/auxiliary_bus.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 
251f2dcf3aSPierre-Louis Bossart /* IDA min selected to avoid conflicts with HDaudio/iDISP SDI values */
261f2dcf3aSPierre-Louis Bossart #define INTEL_DEV_NUM_IDA_MIN           4
271f2dcf3aSPierre-Louis Bossart 
28ebf878edSPierre-Louis Bossart #define INTEL_MASTER_SUSPEND_DELAY_MS	3000
29ff560946SPierre-Louis Bossart #define INTEL_MASTER_RESET_ITERATIONS	10
30ebf878edSPierre-Louis Bossart 
31ebf878edSPierre-Louis Bossart /*
32ebf878edSPierre-Louis Bossart  * debug/config flags for the Intel SoundWire Master.
33ebf878edSPierre-Louis Bossart  *
34ebf878edSPierre-Louis Bossart  * Since we may have multiple masters active, we can have up to 8
35ebf878edSPierre-Louis Bossart  * flags reused in each byte, with master0 using the ls-byte, etc.
36ebf878edSPierre-Louis Bossart  */
37ebf878edSPierre-Louis Bossart 
38ebf878edSPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME		BIT(0)
39ebf878edSPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP		BIT(1)
40a2d9c161SPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE	BIT(2)
41857a7c42SPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_MULTI_LINK		BIT(3)
42ebf878edSPierre-Louis Bossart 
43ebf878edSPierre-Louis Bossart static int md_flags;
44ebf878edSPierre-Louis Bossart module_param_named(sdw_md_flags, md_flags, int, 0444);
45ebf878edSPierre-Louis Bossart MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)");
46ebf878edSPierre-Louis Bossart 
47c46302ecSVinod Koul enum intel_pdi_type {
48c46302ecSVinod Koul 	INTEL_PDI_IN = 0,
49c46302ecSVinod Koul 	INTEL_PDI_OUT = 1,
50c46302ecSVinod Koul 	INTEL_PDI_BD = 2,
51c46302ecSVinod Koul };
52c46302ecSVinod Koul 
5371bb8a1bSVinod Koul #define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
5471bb8a1bSVinod Koul 
5571bb8a1bSVinod Koul /*
5671bb8a1bSVinod Koul  * Read, write helpers for HW registers
5771bb8a1bSVinod Koul  */
5871bb8a1bSVinod Koul static inline int intel_readl(void __iomem *base, int offset)
5971bb8a1bSVinod Koul {
6071bb8a1bSVinod Koul 	return readl(base + offset);
6171bb8a1bSVinod Koul }
6271bb8a1bSVinod Koul 
6371bb8a1bSVinod Koul static inline void intel_writel(void __iomem *base, int offset, int value)
6471bb8a1bSVinod Koul {
6571bb8a1bSVinod Koul 	writel(value, base + offset);
6671bb8a1bSVinod Koul }
6771bb8a1bSVinod Koul 
6871bb8a1bSVinod Koul static inline u16 intel_readw(void __iomem *base, int offset)
6971bb8a1bSVinod Koul {
7071bb8a1bSVinod Koul 	return readw(base + offset);
7171bb8a1bSVinod Koul }
7271bb8a1bSVinod Koul 
7371bb8a1bSVinod Koul static inline void intel_writew(void __iomem *base, int offset, u16 value)
7471bb8a1bSVinod Koul {
7571bb8a1bSVinod Koul 	writew(value, base + offset);
7671bb8a1bSVinod Koul }
7771bb8a1bSVinod Koul 
787d2845d5SPierre-Louis Bossart static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
7971bb8a1bSVinod Koul {
8071bb8a1bSVinod Koul 	int timeout = 10;
8171bb8a1bSVinod Koul 	u32 reg_read;
8271bb8a1bSVinod Koul 
8371bb8a1bSVinod Koul 	do {
8471bb8a1bSVinod Koul 		reg_read = readl(base + offset);
857d2845d5SPierre-Louis Bossart 		if ((reg_read & mask) == target)
8671bb8a1bSVinod Koul 			return 0;
8771bb8a1bSVinod Koul 
8871bb8a1bSVinod Koul 		timeout--;
897d2845d5SPierre-Louis Bossart 		usleep_range(50, 100);
9071bb8a1bSVinod Koul 	} while (timeout != 0);
9171bb8a1bSVinod Koul 
9271bb8a1bSVinod Koul 	return -EAGAIN;
9371bb8a1bSVinod Koul }
9471bb8a1bSVinod Koul 
957d2845d5SPierre-Louis Bossart static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask)
967d2845d5SPierre-Louis Bossart {
977d2845d5SPierre-Louis Bossart 	writel(value, base + offset);
987d2845d5SPierre-Louis Bossart 	return intel_wait_bit(base, offset, mask, 0);
997d2845d5SPierre-Louis Bossart }
1007d2845d5SPierre-Louis Bossart 
10171bb8a1bSVinod Koul static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask)
10271bb8a1bSVinod Koul {
10371bb8a1bSVinod Koul 	writel(value, base + offset);
1047d2845d5SPierre-Louis Bossart 	return intel_wait_bit(base, offset, mask, mask);
10571bb8a1bSVinod Koul }
10671bb8a1bSVinod Koul 
10771bb8a1bSVinod Koul /*
10879ee6631SPierre-Louis Bossart  * debugfs
10979ee6631SPierre-Louis Bossart  */
11079ee6631SPierre-Louis Bossart #ifdef CONFIG_DEBUG_FS
11179ee6631SPierre-Louis Bossart 
11279ee6631SPierre-Louis Bossart #define RD_BUF (2 * PAGE_SIZE)
11379ee6631SPierre-Louis Bossart 
11479ee6631SPierre-Louis Bossart static ssize_t intel_sprintf(void __iomem *mem, bool l,
11579ee6631SPierre-Louis Bossart 			     char *buf, size_t pos, unsigned int reg)
11679ee6631SPierre-Louis Bossart {
11779ee6631SPierre-Louis Bossart 	int value;
11879ee6631SPierre-Louis Bossart 
11979ee6631SPierre-Louis Bossart 	if (l)
12079ee6631SPierre-Louis Bossart 		value = intel_readl(mem, reg);
12179ee6631SPierre-Louis Bossart 	else
12279ee6631SPierre-Louis Bossart 		value = intel_readw(mem, reg);
12379ee6631SPierre-Louis Bossart 
12479ee6631SPierre-Louis Bossart 	return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value);
12579ee6631SPierre-Louis Bossart }
12679ee6631SPierre-Louis Bossart 
12779ee6631SPierre-Louis Bossart static int intel_reg_show(struct seq_file *s_file, void *data)
12879ee6631SPierre-Louis Bossart {
12979ee6631SPierre-Louis Bossart 	struct sdw_intel *sdw = s_file->private;
1302523486bSPierre-Louis Bossart 	void __iomem *s = sdw->link_res->shim;
1312523486bSPierre-Louis Bossart 	void __iomem *a = sdw->link_res->alh;
13279ee6631SPierre-Louis Bossart 	char *buf;
13379ee6631SPierre-Louis Bossart 	ssize_t ret;
13479ee6631SPierre-Louis Bossart 	int i, j;
13579ee6631SPierre-Louis Bossart 	unsigned int links, reg;
13679ee6631SPierre-Louis Bossart 
13779ee6631SPierre-Louis Bossart 	buf = kzalloc(RD_BUF, GFP_KERNEL);
13879ee6631SPierre-Louis Bossart 	if (!buf)
13979ee6631SPierre-Louis Bossart 		return -ENOMEM;
14079ee6631SPierre-Louis Bossart 
1417f817068SPierre-Louis Bossart 	links = intel_readl(s, SDW_SHIM_LCAP) & SDW_SHIM_LCAP_LCOUNT_MASK;
14279ee6631SPierre-Louis Bossart 
14379ee6631SPierre-Louis Bossart 	ret = scnprintf(buf, RD_BUF, "Register  Value\n");
14479ee6631SPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n");
14579ee6631SPierre-Louis Bossart 
14679ee6631SPierre-Louis Bossart 	for (i = 0; i < links; i++) {
14779ee6631SPierre-Louis Bossart 		reg = SDW_SHIM_LCAP + i * 4;
14879ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, true, buf, ret, reg);
14979ee6631SPierre-Louis Bossart 	}
15079ee6631SPierre-Louis Bossart 
15179ee6631SPierre-Louis Bossart 	for (i = 0; i < links; i++) {
15279ee6631SPierre-Louis Bossart 		ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i);
15379ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i));
15479ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i));
15579ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i));
15679ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i));
15779ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i));
15879ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i));
15979ee6631SPierre-Louis Bossart 
16079ee6631SPierre-Louis Bossart 		ret += scnprintf(buf + ret, RD_BUF - ret, "\n PCMSyCH registers\n");
16179ee6631SPierre-Louis Bossart 
16279ee6631SPierre-Louis Bossart 		/*
16379ee6631SPierre-Louis Bossart 		 * the value 10 is the number of PDIs. We will need a
16479ee6631SPierre-Louis Bossart 		 * cleanup to remove hard-coded Intel configurations
16579ee6631SPierre-Louis Bossart 		 * from cadence_master.c
16679ee6631SPierre-Louis Bossart 		 */
16779ee6631SPierre-Louis Bossart 		for (j = 0; j < 10; j++) {
16879ee6631SPierre-Louis Bossart 			ret += intel_sprintf(s, false, buf, ret,
16979ee6631SPierre-Louis Bossart 					SDW_SHIM_PCMSYCHM(i, j));
17079ee6631SPierre-Louis Bossart 			ret += intel_sprintf(s, false, buf, ret,
17179ee6631SPierre-Louis Bossart 					SDW_SHIM_PCMSYCHC(i, j));
17279ee6631SPierre-Louis Bossart 		}
173c27ce5c9SPierre-Louis Bossart 		ret += scnprintf(buf + ret, RD_BUF - ret, "\n IOCTL, CTMCTL\n");
17479ee6631SPierre-Louis Bossart 
17579ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i));
17679ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i));
17779ee6631SPierre-Louis Bossart 	}
17879ee6631SPierre-Louis Bossart 
17979ee6631SPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nWake registers\n");
18079ee6631SPierre-Louis Bossart 	ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN);
18179ee6631SPierre-Louis Bossart 	ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS);
18279ee6631SPierre-Louis Bossart 
18379ee6631SPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH STRMzCFG\n");
18479ee6631SPierre-Louis Bossart 	for (i = 0; i < SDW_ALH_NUM_STREAMS; i++)
18579ee6631SPierre-Louis Bossart 		ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i));
18679ee6631SPierre-Louis Bossart 
18779ee6631SPierre-Louis Bossart 	seq_printf(s_file, "%s", buf);
18879ee6631SPierre-Louis Bossart 	kfree(buf);
18979ee6631SPierre-Louis Bossart 
19079ee6631SPierre-Louis Bossart 	return 0;
19179ee6631SPierre-Louis Bossart }
19279ee6631SPierre-Louis Bossart DEFINE_SHOW_ATTRIBUTE(intel_reg);
19379ee6631SPierre-Louis Bossart 
1940f9138e7SPierre-Louis Bossart static int intel_set_m_datamode(void *data, u64 value)
1950f9138e7SPierre-Louis Bossart {
1960f9138e7SPierre-Louis Bossart 	struct sdw_intel *sdw = data;
1970f9138e7SPierre-Louis Bossart 	struct sdw_bus *bus = &sdw->cdns.bus;
1980f9138e7SPierre-Louis Bossart 
1990f9138e7SPierre-Louis Bossart 	if (value > SDW_PORT_DATA_MODE_STATIC_1)
2000f9138e7SPierre-Louis Bossart 		return -EINVAL;
2010f9138e7SPierre-Louis Bossart 
2020f9138e7SPierre-Louis Bossart 	/* Userspace changed the hardware state behind the kernel's back */
2030f9138e7SPierre-Louis Bossart 	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
2040f9138e7SPierre-Louis Bossart 
2050f9138e7SPierre-Louis Bossart 	bus->params.m_data_mode = value;
2060f9138e7SPierre-Louis Bossart 
2070f9138e7SPierre-Louis Bossart 	return 0;
2080f9138e7SPierre-Louis Bossart }
2090f9138e7SPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(intel_set_m_datamode_fops, NULL,
2100f9138e7SPierre-Louis Bossart 			 intel_set_m_datamode, "%llu\n");
2110f9138e7SPierre-Louis Bossart 
2120f9138e7SPierre-Louis Bossart static int intel_set_s_datamode(void *data, u64 value)
2130f9138e7SPierre-Louis Bossart {
2140f9138e7SPierre-Louis Bossart 	struct sdw_intel *sdw = data;
2150f9138e7SPierre-Louis Bossart 	struct sdw_bus *bus = &sdw->cdns.bus;
2160f9138e7SPierre-Louis Bossart 
2170f9138e7SPierre-Louis Bossart 	if (value > SDW_PORT_DATA_MODE_STATIC_1)
2180f9138e7SPierre-Louis Bossart 		return -EINVAL;
2190f9138e7SPierre-Louis Bossart 
2200f9138e7SPierre-Louis Bossart 	/* Userspace changed the hardware state behind the kernel's back */
2210f9138e7SPierre-Louis Bossart 	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
2220f9138e7SPierre-Louis Bossart 
2230f9138e7SPierre-Louis Bossart 	bus->params.s_data_mode = value;
2240f9138e7SPierre-Louis Bossart 
2250f9138e7SPierre-Louis Bossart 	return 0;
2260f9138e7SPierre-Louis Bossart }
2270f9138e7SPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(intel_set_s_datamode_fops, NULL,
2280f9138e7SPierre-Louis Bossart 			 intel_set_s_datamode, "%llu\n");
2290f9138e7SPierre-Louis Bossart 
23079ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw)
23179ee6631SPierre-Louis Bossart {
23279ee6631SPierre-Louis Bossart 	struct dentry *root = sdw->cdns.bus.debugfs;
23379ee6631SPierre-Louis Bossart 
23479ee6631SPierre-Louis Bossart 	if (!root)
23579ee6631SPierre-Louis Bossart 		return;
23679ee6631SPierre-Louis Bossart 
23779ee6631SPierre-Louis Bossart 	sdw->debugfs = debugfs_create_dir("intel-sdw", root);
23879ee6631SPierre-Louis Bossart 
23979ee6631SPierre-Louis Bossart 	debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw,
24079ee6631SPierre-Louis Bossart 			    &intel_reg_fops);
24179ee6631SPierre-Louis Bossart 
2420f9138e7SPierre-Louis Bossart 	debugfs_create_file("intel-m-datamode", 0200, sdw->debugfs, sdw,
2430f9138e7SPierre-Louis Bossart 			    &intel_set_m_datamode_fops);
2440f9138e7SPierre-Louis Bossart 
2450f9138e7SPierre-Louis Bossart 	debugfs_create_file("intel-s-datamode", 0200, sdw->debugfs, sdw,
2460f9138e7SPierre-Louis Bossart 			    &intel_set_s_datamode_fops);
2470f9138e7SPierre-Louis Bossart 
24879ee6631SPierre-Louis Bossart 	sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs);
24979ee6631SPierre-Louis Bossart }
25079ee6631SPierre-Louis Bossart 
25179ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw)
25279ee6631SPierre-Louis Bossart {
25379ee6631SPierre-Louis Bossart 	debugfs_remove_recursive(sdw->debugfs);
25479ee6631SPierre-Louis Bossart }
25579ee6631SPierre-Louis Bossart #else
25679ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw) {}
25779ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw) {}
25879ee6631SPierre-Louis Bossart #endif /* CONFIG_DEBUG_FS */
25979ee6631SPierre-Louis Bossart 
26079ee6631SPierre-Louis Bossart /*
26171bb8a1bSVinod Koul  * shim ops
26271bb8a1bSVinod Koul  */
2634a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */
2644a17c441SPierre-Louis Bossart static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw)
26571bb8a1bSVinod Koul {
2662523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
26771bb8a1bSVinod Koul 	unsigned int link_id = sdw->instance;
2684a17c441SPierre-Louis Bossart 	u16 ioctl;
26971bb8a1bSVinod Koul 
27071bb8a1bSVinod Koul 	/* Switch to MIP from Glue logic */
27171bb8a1bSVinod Koul 	ioctl = intel_readw(shim,  SDW_SHIM_IOCTL(link_id));
27271bb8a1bSVinod Koul 
27371bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_DOE);
27471bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
2754a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
27671bb8a1bSVinod Koul 
27771bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_DO);
27871bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
2794a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
28071bb8a1bSVinod Koul 
28171bb8a1bSVinod Koul 	ioctl |= (SDW_SHIM_IOCTL_MIF);
28271bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
2834a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
28471bb8a1bSVinod Koul 
28571bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_BKE);
28671bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_COE);
28771bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
2884a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
2894a17c441SPierre-Louis Bossart 
2904a17c441SPierre-Louis Bossart 	/* at this point Master IP has full control of the I/Os */
2914a17c441SPierre-Louis Bossart }
2924a17c441SPierre-Louis Bossart 
2934a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */
2944a17c441SPierre-Louis Bossart static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw)
2954a17c441SPierre-Louis Bossart {
2964a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
2974a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
2984a17c441SPierre-Louis Bossart 	u16 ioctl;
2994a17c441SPierre-Louis Bossart 
3004a17c441SPierre-Louis Bossart 	/* Glue logic */
3014a17c441SPierre-Louis Bossart 	ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id));
3024a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_BKE;
3034a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_COE;
3044a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
3054a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
3064a17c441SPierre-Louis Bossart 
3074a17c441SPierre-Louis Bossart 	ioctl &= ~(SDW_SHIM_IOCTL_MIF);
3084a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
3094a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
3104a17c441SPierre-Louis Bossart 
3114a17c441SPierre-Louis Bossart 	/* at this point Integration Glue has full control of the I/Os */
3124a17c441SPierre-Louis Bossart }
3134a17c441SPierre-Louis Bossart 
314b81bcdb4SPierre-Louis Bossart /* this needs to be called with shim_lock */
315b81bcdb4SPierre-Louis Bossart static void intel_shim_init(struct sdw_intel *sdw)
3164a17c441SPierre-Louis Bossart {
3174a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
3184a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
3194a17c441SPierre-Louis Bossart 	u16 ioctl = 0, act = 0;
3204a17c441SPierre-Louis Bossart 
3214a17c441SPierre-Louis Bossart 	/* Initialize Shim */
3224a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_BKE;
3234a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
3244a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
3254a17c441SPierre-Louis Bossart 
3264a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_WPDD;
3274a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
3284a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
3294a17c441SPierre-Louis Bossart 
3304a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_DO;
3314a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
3324a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
3334a17c441SPierre-Louis Bossart 
3344a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_DOE;
3354a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
3364a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
3374a17c441SPierre-Louis Bossart 
3384a17c441SPierre-Louis Bossart 	intel_shim_glue_to_master_ip(sdw);
33971bb8a1bSVinod Koul 
340f067c925SVinod Koul 	u16p_replace_bits(&act, 0x1, SDW_SHIM_CTMCTL_DOAIS);
34171bb8a1bSVinod Koul 	act |= SDW_SHIM_CTMCTL_DACTQE;
34271bb8a1bSVinod Koul 	act |= SDW_SHIM_CTMCTL_DODS;
34371bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act);
3444a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
34571bb8a1bSVinod Koul }
34671bb8a1bSVinod Koul 
3470f3c54c2SPierre-Louis Bossart static int intel_shim_check_wake(struct sdw_intel *sdw)
3480f3c54c2SPierre-Louis Bossart {
3490f3c54c2SPierre-Louis Bossart 	void __iomem *shim;
3500f3c54c2SPierre-Louis Bossart 	u16 wake_sts;
35171bb8a1bSVinod Koul 
3520f3c54c2SPierre-Louis Bossart 	shim = sdw->link_res->shim;
3530f3c54c2SPierre-Louis Bossart 	wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
3540f3c54c2SPierre-Louis Bossart 
3550f3c54c2SPierre-Louis Bossart 	return wake_sts & BIT(sdw->instance);
35671bb8a1bSVinod Koul }
35771bb8a1bSVinod Koul 
358ab2c9132SRander Wang static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
3594a17c441SPierre-Louis Bossart {
3604a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
3614a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
3624a17c441SPierre-Louis Bossart 	u16 wake_en, wake_sts;
3634a17c441SPierre-Louis Bossart 
3644a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
3654a17c441SPierre-Louis Bossart 	wake_en = intel_readw(shim, SDW_SHIM_WAKEEN);
3664a17c441SPierre-Louis Bossart 
3674a17c441SPierre-Louis Bossart 	if (wake_enable) {
3684a17c441SPierre-Louis Bossart 		/* Enable the wakeup */
3694a17c441SPierre-Louis Bossart 		wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
3704a17c441SPierre-Louis Bossart 		intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
3714a17c441SPierre-Louis Bossart 	} else {
3724a17c441SPierre-Louis Bossart 		/* Disable the wake up interrupt */
3734a17c441SPierre-Louis Bossart 		wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id);
3744a17c441SPierre-Louis Bossart 		intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
3754a17c441SPierre-Louis Bossart 
3764a17c441SPierre-Louis Bossart 		/* Clear wake status */
3774a17c441SPierre-Louis Bossart 		wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
3783957db3aSLibin Yang 		wake_sts |= (SDW_SHIM_WAKESTS_STATUS << link_id);
3793957db3aSLibin Yang 		intel_writew(shim, SDW_SHIM_WAKESTS, wake_sts);
3804a17c441SPierre-Louis Bossart 	}
3814a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
3824a17c441SPierre-Louis Bossart }
3834a17c441SPierre-Louis Bossart 
384bc872947SPierre-Louis Bossart static int intel_link_power_up(struct sdw_intel *sdw)
385bc872947SPierre-Louis Bossart {
386bc872947SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
387bc872947SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
388bc872947SPierre-Louis Bossart 	u32 *shim_mask = sdw->link_res->shim_mask;
389bc872947SPierre-Louis Bossart 	struct sdw_bus *bus = &sdw->cdns.bus;
390bc872947SPierre-Louis Bossart 	struct sdw_master_prop *prop = &bus->prop;
391bc872947SPierre-Louis Bossart 	u32 spa_mask, cpa_mask;
392bc872947SPierre-Louis Bossart 	u32 link_control;
393bc872947SPierre-Louis Bossart 	int ret = 0;
394bc872947SPierre-Louis Bossart 	u32 syncprd;
395bc872947SPierre-Louis Bossart 	u32 sync_reg;
396bc872947SPierre-Louis Bossart 
397bc872947SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
398bc872947SPierre-Louis Bossart 
399bc872947SPierre-Louis Bossart 	/*
400bc872947SPierre-Louis Bossart 	 * The hardware relies on an internal counter, typically 4kHz,
401bc872947SPierre-Louis Bossart 	 * to generate the SoundWire SSP - which defines a 'safe'
402bc872947SPierre-Louis Bossart 	 * synchronization point between commands and audio transport
403bc872947SPierre-Louis Bossart 	 * and allows for multi link synchronization. The SYNCPRD value
404bc872947SPierre-Louis Bossart 	 * is only dependent on the oscillator clock provided to
405bc872947SPierre-Louis Bossart 	 * the IP, so adjust based on _DSD properties reported in DSDT
406bc872947SPierre-Louis Bossart 	 * tables. The values reported are based on either 24MHz
407bc872947SPierre-Louis Bossart 	 * (CNL/CML) or 38.4 MHz (ICL/TGL+).
408bc872947SPierre-Louis Bossart 	 */
409bc872947SPierre-Louis Bossart 	if (prop->mclk_freq % 6000000)
410bc872947SPierre-Louis Bossart 		syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;
411bc872947SPierre-Louis Bossart 	else
412bc872947SPierre-Louis Bossart 		syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24;
413bc872947SPierre-Louis Bossart 
414bc872947SPierre-Louis Bossart 	if (!*shim_mask) {
415bc872947SPierre-Louis Bossart 		dev_dbg(sdw->cdns.dev, "powering up all links\n");
416bc872947SPierre-Louis Bossart 
417bc872947SPierre-Louis Bossart 		/* we first need to program the SyncPRD/CPU registers */
418bc872947SPierre-Louis Bossart 		dev_dbg(sdw->cdns.dev,
419bc872947SPierre-Louis Bossart 			"first link up, programming SYNCPRD\n");
420bc872947SPierre-Louis Bossart 
421bc872947SPierre-Louis Bossart 		/* set SyncPRD period */
422bc872947SPierre-Louis Bossart 		sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
423bc872947SPierre-Louis Bossart 		u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD);
424bc872947SPierre-Louis Bossart 
425bc872947SPierre-Louis Bossart 		/* Set SyncCPU bit */
426bc872947SPierre-Louis Bossart 		sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
427bc872947SPierre-Louis Bossart 		intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
428bc872947SPierre-Louis Bossart 
429bc872947SPierre-Louis Bossart 		/* Link power up sequence */
430bc872947SPierre-Louis Bossart 		link_control = intel_readl(shim, SDW_SHIM_LCTL);
431bc872947SPierre-Louis Bossart 
432bc872947SPierre-Louis Bossart 		/* only power-up enabled links */
433bc872947SPierre-Louis Bossart 		spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask);
434bc872947SPierre-Louis Bossart 		cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
435bc872947SPierre-Louis Bossart 
436bc872947SPierre-Louis Bossart 		link_control |=  spa_mask;
437bc872947SPierre-Louis Bossart 
438bc872947SPierre-Louis Bossart 		ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
439bc872947SPierre-Louis Bossart 		if (ret < 0) {
440bc872947SPierre-Louis Bossart 			dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret);
441bc872947SPierre-Louis Bossart 			goto out;
442bc872947SPierre-Louis Bossart 		}
443bc872947SPierre-Louis Bossart 
444bc872947SPierre-Louis Bossart 		/* SyncCPU will change once link is active */
445bc872947SPierre-Louis Bossart 		ret = intel_wait_bit(shim, SDW_SHIM_SYNC,
446bc872947SPierre-Louis Bossart 				     SDW_SHIM_SYNC_SYNCCPU, 0);
447bc872947SPierre-Louis Bossart 		if (ret < 0) {
448bc872947SPierre-Louis Bossart 			dev_err(sdw->cdns.dev,
449bc872947SPierre-Louis Bossart 				"Failed to set SHIM_SYNC: %d\n", ret);
450bc872947SPierre-Louis Bossart 			goto out;
451bc872947SPierre-Louis Bossart 		}
452bc872947SPierre-Louis Bossart 	}
453bc872947SPierre-Louis Bossart 
454bc872947SPierre-Louis Bossart 	*shim_mask |= BIT(link_id);
455bc872947SPierre-Louis Bossart 
456bc872947SPierre-Louis Bossart 	sdw->cdns.link_up = true;
457b81bcdb4SPierre-Louis Bossart 
458b81bcdb4SPierre-Louis Bossart 	intel_shim_init(sdw);
459b81bcdb4SPierre-Louis Bossart 
460bc872947SPierre-Louis Bossart out:
461bc872947SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
462bc872947SPierre-Louis Bossart 
463bc872947SPierre-Louis Bossart 	return ret;
464bc872947SPierre-Louis Bossart }
465bc872947SPierre-Louis Bossart 
4669b3b4b3fSPierre-Louis Bossart static int intel_link_power_down(struct sdw_intel *sdw)
4674a17c441SPierre-Louis Bossart {
4685ee74eb2SPierre-Louis Bossart 	u32 link_control, spa_mask, cpa_mask;
4694a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
4704a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
4714a17c441SPierre-Louis Bossart 	u32 *shim_mask = sdw->link_res->shim_mask;
4724a17c441SPierre-Louis Bossart 	int ret = 0;
4734a17c441SPierre-Louis Bossart 
4744a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
4754a17c441SPierre-Louis Bossart 
4764a17c441SPierre-Louis Bossart 	if (!(*shim_mask & BIT(link_id)))
4774a17c441SPierre-Louis Bossart 		dev_err(sdw->cdns.dev,
4784a17c441SPierre-Louis Bossart 			"%s: Unbalanced power-up/down calls\n", __func__);
4794a17c441SPierre-Louis Bossart 
480ea6942daSPierre-Louis Bossart 	sdw->cdns.link_up = false;
481ea6942daSPierre-Louis Bossart 
482ea6942daSPierre-Louis Bossart 	intel_shim_master_ip_to_glue(sdw);
483ea6942daSPierre-Louis Bossart 
4844a17c441SPierre-Louis Bossart 	*shim_mask &= ~BIT(link_id);
4854a17c441SPierre-Louis Bossart 
4865ee74eb2SPierre-Louis Bossart 	if (!*shim_mask) {
4875ee74eb2SPierre-Louis Bossart 
48863198aaaSPierre-Louis Bossart 		dev_dbg(sdw->cdns.dev, "powering down all links\n");
4895ee74eb2SPierre-Louis Bossart 
4905ee74eb2SPierre-Louis Bossart 		/* Link power down sequence */
4915ee74eb2SPierre-Louis Bossart 		link_control = intel_readl(shim, SDW_SHIM_LCTL);
4925ee74eb2SPierre-Louis Bossart 
4935ee74eb2SPierre-Louis Bossart 		/* only power-down enabled links */
4943b4979caSVinod Koul 		spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, ~sdw->link_res->link_mask);
4953b4979caSVinod Koul 		cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
4965ee74eb2SPierre-Louis Bossart 
4975ee74eb2SPierre-Louis Bossart 		link_control &=  spa_mask;
4985ee74eb2SPierre-Louis Bossart 
4995ee74eb2SPierre-Louis Bossart 		ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
500ea6942daSPierre-Louis Bossart 		if (ret < 0) {
501ea6942daSPierre-Louis Bossart 			dev_err(sdw->cdns.dev, "%s: could not power down link\n", __func__);
502ea6942daSPierre-Louis Bossart 
503ea6942daSPierre-Louis Bossart 			/*
504ea6942daSPierre-Louis Bossart 			 * we leave the sdw->cdns.link_up flag as false since we've disabled
505ea6942daSPierre-Louis Bossart 			 * the link at this point and cannot handle interrupts any longer.
506ea6942daSPierre-Louis Bossart 			 */
507ea6942daSPierre-Louis Bossart 		}
5085ee74eb2SPierre-Louis Bossart 	}
5095ee74eb2SPierre-Louis Bossart 
5104a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
5114a17c441SPierre-Louis Bossart 
5124a17c441SPierre-Louis Bossart 	return ret;
5135ee74eb2SPierre-Louis Bossart }
5144a17c441SPierre-Louis Bossart 
51502629e45SPierre-Louis Bossart static void intel_shim_sync_arm(struct sdw_intel *sdw)
51602629e45SPierre-Louis Bossart {
51702629e45SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
51802629e45SPierre-Louis Bossart 	u32 sync_reg;
51902629e45SPierre-Louis Bossart 
52002629e45SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
52102629e45SPierre-Louis Bossart 
52202629e45SPierre-Louis Bossart 	/* update SYNC register */
52302629e45SPierre-Louis Bossart 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
52402629e45SPierre-Louis Bossart 	sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance);
52502629e45SPierre-Louis Bossart 	intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
52602629e45SPierre-Louis Bossart 
52702629e45SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
52802629e45SPierre-Louis Bossart }
52902629e45SPierre-Louis Bossart 
530437e3289SPierre-Louis Bossart static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw)
531437e3289SPierre-Louis Bossart {
532437e3289SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
533437e3289SPierre-Louis Bossart 	u32 sync_reg;
534437e3289SPierre-Louis Bossart 	int ret;
535437e3289SPierre-Louis Bossart 
536437e3289SPierre-Louis Bossart 	/* Read SYNC register */
537437e3289SPierre-Louis Bossart 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
538437e3289SPierre-Louis Bossart 
539437e3289SPierre-Louis Bossart 	/*
540437e3289SPierre-Louis Bossart 	 * Set SyncGO bit to synchronously trigger a bank switch for
541437e3289SPierre-Louis Bossart 	 * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all
542437e3289SPierre-Louis Bossart 	 * the Masters.
543437e3289SPierre-Louis Bossart 	 */
544437e3289SPierre-Louis Bossart 	sync_reg |= SDW_SHIM_SYNC_SYNCGO;
545437e3289SPierre-Louis Bossart 
546437e3289SPierre-Louis Bossart 	ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
547437e3289SPierre-Louis Bossart 			      SDW_SHIM_SYNC_SYNCGO);
548437e3289SPierre-Louis Bossart 
549437e3289SPierre-Louis Bossart 	if (ret < 0)
550437e3289SPierre-Louis Bossart 		dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret);
55171bb8a1bSVinod Koul 
55271bb8a1bSVinod Koul 	return ret;
55371bb8a1bSVinod Koul }
55471bb8a1bSVinod Koul 
555857a7c42SPierre-Louis Bossart static int intel_shim_sync_go(struct sdw_intel *sdw)
556857a7c42SPierre-Louis Bossart {
557857a7c42SPierre-Louis Bossart 	int ret;
558857a7c42SPierre-Louis Bossart 
559857a7c42SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
560857a7c42SPierre-Louis Bossart 
561857a7c42SPierre-Louis Bossart 	ret = intel_shim_sync_go_unlocked(sdw);
562857a7c42SPierre-Louis Bossart 
563857a7c42SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
564857a7c42SPierre-Louis Bossart 
565857a7c42SPierre-Louis Bossart 	return ret;
566857a7c42SPierre-Louis Bossart }
567857a7c42SPierre-Louis Bossart 
56837a2d22bSVinod Koul /*
56937a2d22bSVinod Koul  * PDI routines
57037a2d22bSVinod Koul  */
57137a2d22bSVinod Koul static void intel_pdi_init(struct sdw_intel *sdw,
57237a2d22bSVinod Koul 			   struct sdw_cdns_stream_config *config)
57337a2d22bSVinod Koul {
5742523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
57537a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
57663a6aa96SPierre-Louis Bossart 	int pcm_cap;
57737a2d22bSVinod Koul 
57837a2d22bSVinod Koul 	/* PCM Stream Capability */
57937a2d22bSVinod Koul 	pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id));
58037a2d22bSVinod Koul 
5813b4979caSVinod Koul 	config->pcm_bd = FIELD_GET(SDW_SHIM_PCMSCAP_BSS, pcm_cap);
5823b4979caSVinod Koul 	config->pcm_in = FIELD_GET(SDW_SHIM_PCMSCAP_ISS, pcm_cap);
5833b4979caSVinod Koul 	config->pcm_out = FIELD_GET(SDW_SHIM_PCMSCAP_OSS, pcm_cap);
58437a2d22bSVinod Koul 
585121f4361SPierre-Louis Bossart 	dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n",
586121f4361SPierre-Louis Bossart 		config->pcm_bd, config->pcm_in, config->pcm_out);
58737a2d22bSVinod Koul }
58837a2d22bSVinod Koul 
58937a2d22bSVinod Koul static int
59063a6aa96SPierre-Louis Bossart intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num)
59137a2d22bSVinod Koul {
5922523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
59337a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
59437a2d22bSVinod Koul 	int count;
59537a2d22bSVinod Koul 
59637a2d22bSVinod Koul 	count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num));
59718046335SPierre-Louis Bossart 
59818046335SPierre-Louis Bossart 	/*
59918046335SPierre-Louis Bossart 	 * WORKAROUND: on all existing Intel controllers, pdi
60018046335SPierre-Louis Bossart 	 * number 2 reports channel count as 1 even though it
60118046335SPierre-Louis Bossart 	 * supports 8 channels. Performing hardcoding for pdi
60218046335SPierre-Louis Bossart 	 * number 2.
60318046335SPierre-Louis Bossart 	 */
60418046335SPierre-Louis Bossart 	if (pdi_num == 2)
60518046335SPierre-Louis Bossart 		count = 7;
60618046335SPierre-Louis Bossart 
60737a2d22bSVinod Koul 	/* zero based values for channel count in register */
60837a2d22bSVinod Koul 	count++;
60937a2d22bSVinod Koul 
61037a2d22bSVinod Koul 	return count;
61137a2d22bSVinod Koul }
61237a2d22bSVinod Koul 
61337a2d22bSVinod Koul static int intel_pdi_get_ch_update(struct sdw_intel *sdw,
61437a2d22bSVinod Koul 				   struct sdw_cdns_pdi *pdi,
61537a2d22bSVinod Koul 				   unsigned int num_pdi,
61663a6aa96SPierre-Louis Bossart 				   unsigned int *num_ch)
61737a2d22bSVinod Koul {
61837a2d22bSVinod Koul 	int i, ch_count = 0;
61937a2d22bSVinod Koul 
62037a2d22bSVinod Koul 	for (i = 0; i < num_pdi; i++) {
62163a6aa96SPierre-Louis Bossart 		pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num);
62237a2d22bSVinod Koul 		ch_count += pdi->ch_count;
62337a2d22bSVinod Koul 		pdi++;
62437a2d22bSVinod Koul 	}
62537a2d22bSVinod Koul 
62637a2d22bSVinod Koul 	*num_ch = ch_count;
62737a2d22bSVinod Koul 	return 0;
62837a2d22bSVinod Koul }
62937a2d22bSVinod Koul 
63037a2d22bSVinod Koul static int intel_pdi_stream_ch_update(struct sdw_intel *sdw,
63163a6aa96SPierre-Louis Bossart 				      struct sdw_cdns_streams *stream)
63237a2d22bSVinod Koul {
63337a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd,
63463a6aa96SPierre-Louis Bossart 				&stream->num_ch_bd);
63537a2d22bSVinod Koul 
63637a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->in, stream->num_in,
63763a6aa96SPierre-Louis Bossart 				&stream->num_ch_in);
63837a2d22bSVinod Koul 
63937a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->out, stream->num_out,
64063a6aa96SPierre-Louis Bossart 				&stream->num_ch_out);
64137a2d22bSVinod Koul 
64237a2d22bSVinod Koul 	return 0;
64337a2d22bSVinod Koul }
64437a2d22bSVinod Koul 
64537a2d22bSVinod Koul static int intel_pdi_ch_update(struct sdw_intel *sdw)
64637a2d22bSVinod Koul {
64763a6aa96SPierre-Louis Bossart 	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm);
64837a2d22bSVinod Koul 
64937a2d22bSVinod Koul 	return 0;
65037a2d22bSVinod Koul }
65137a2d22bSVinod Koul 
65237a2d22bSVinod Koul static void
65337a2d22bSVinod Koul intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
65437a2d22bSVinod Koul {
6552523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
65637a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
65737a2d22bSVinod Koul 	int pdi_conf = 0;
65837a2d22bSVinod Koul 
659c134f914SPierre-Louis Bossart 	/* the Bulk and PCM streams are not contiguous */
660c134f914SPierre-Louis Bossart 	pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
661c134f914SPierre-Louis Bossart 	if (pdi->num >= 2)
662c134f914SPierre-Louis Bossart 		pdi->intel_alh_id += 2;
66337a2d22bSVinod Koul 
66437a2d22bSVinod Koul 	/*
66537a2d22bSVinod Koul 	 * Program stream parameters to stream SHIM register
66637a2d22bSVinod Koul 	 * This is applicable for PCM stream only.
66737a2d22bSVinod Koul 	 */
66837a2d22bSVinod Koul 	if (pdi->type != SDW_STREAM_PCM)
66937a2d22bSVinod Koul 		return;
67037a2d22bSVinod Koul 
67137a2d22bSVinod Koul 	if (pdi->dir == SDW_DATA_DIR_RX)
67237a2d22bSVinod Koul 		pdi_conf |= SDW_SHIM_PCMSYCM_DIR;
67337a2d22bSVinod Koul 	else
67437a2d22bSVinod Koul 		pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR);
67537a2d22bSVinod Koul 
676f067c925SVinod Koul 	u32p_replace_bits(&pdi_conf, pdi->intel_alh_id, SDW_SHIM_PCMSYCM_STREAM);
677f067c925SVinod Koul 	u32p_replace_bits(&pdi_conf, pdi->l_ch_num, SDW_SHIM_PCMSYCM_LCHN);
678f067c925SVinod Koul 	u32p_replace_bits(&pdi_conf, pdi->h_ch_num, SDW_SHIM_PCMSYCM_HCHN);
67937a2d22bSVinod Koul 
68037a2d22bSVinod Koul 	intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf);
68137a2d22bSVinod Koul }
68237a2d22bSVinod Koul 
68337a2d22bSVinod Koul static void
68437a2d22bSVinod Koul intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
68537a2d22bSVinod Koul {
6862523486bSPierre-Louis Bossart 	void __iomem *alh = sdw->link_res->alh;
68737a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
68837a2d22bSVinod Koul 	unsigned int conf;
68937a2d22bSVinod Koul 
690c134f914SPierre-Louis Bossart 	/* the Bulk and PCM streams are not contiguous */
691c134f914SPierre-Louis Bossart 	pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
692c134f914SPierre-Louis Bossart 	if (pdi->num >= 2)
693c134f914SPierre-Louis Bossart 		pdi->intel_alh_id += 2;
69437a2d22bSVinod Koul 
69537a2d22bSVinod Koul 	/* Program Stream config ALH register */
69637a2d22bSVinod Koul 	conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id));
69737a2d22bSVinod Koul 
698f067c925SVinod Koul 	u32p_replace_bits(&conf, SDW_ALH_STRMZCFG_DMAT_VAL, SDW_ALH_STRMZCFG_DMAT);
699f067c925SVinod Koul 	u32p_replace_bits(&conf, pdi->ch_count - 1, SDW_ALH_STRMZCFG_CHN);
70037a2d22bSVinod Koul 
70137a2d22bSVinod Koul 	intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
70237a2d22bSVinod Koul }
70337a2d22bSVinod Koul 
7044b206d34SRander Wang static int intel_params_stream(struct sdw_intel *sdw,
705b86947b5SPierre-Louis Bossart 			       int stream,
706c46302ecSVinod Koul 			       struct snd_soc_dai *dai,
7074b206d34SRander Wang 			       struct snd_pcm_hw_params *hw_params,
7084b206d34SRander Wang 			       int link_id, int alh_stream_id)
709c46302ecSVinod Koul {
7102523486bSPierre-Louis Bossart 	struct sdw_intel_link_res *res = sdw->link_res;
7114b206d34SRander Wang 	struct sdw_intel_stream_params_data params_data;
71205c8afe4SPierre-Louis Bossart 
713b86947b5SPierre-Louis Bossart 	params_data.stream = stream; /* direction */
7144b206d34SRander Wang 	params_data.dai = dai;
7154b206d34SRander Wang 	params_data.hw_params = hw_params;
7164b206d34SRander Wang 	params_data.link_id = link_id;
7174b206d34SRander Wang 	params_data.alh_stream_id = alh_stream_id;
718c46302ecSVinod Koul 
7194b206d34SRander Wang 	if (res->ops && res->ops->params_stream && res->dev)
7204b206d34SRander Wang 		return res->ops->params_stream(res->dev,
7214b206d34SRander Wang 					       &params_data);
722c46302ecSVinod Koul 	return -EIO;
723c46302ecSVinod Koul }
724c46302ecSVinod Koul 
725eff346f2SPierre-Louis Bossart static int intel_free_stream(struct sdw_intel *sdw,
726b86947b5SPierre-Louis Bossart 			     int stream,
727eff346f2SPierre-Louis Bossart 			     struct snd_soc_dai *dai,
728eff346f2SPierre-Louis Bossart 			     int link_id)
729eff346f2SPierre-Louis Bossart {
730eff346f2SPierre-Louis Bossart 	struct sdw_intel_link_res *res = sdw->link_res;
731eff346f2SPierre-Louis Bossart 	struct sdw_intel_stream_free_data free_data;
732eff346f2SPierre-Louis Bossart 
733b86947b5SPierre-Louis Bossart 	free_data.stream = stream; /* direction */
734eff346f2SPierre-Louis Bossart 	free_data.dai = dai;
735eff346f2SPierre-Louis Bossart 	free_data.link_id = link_id;
736eff346f2SPierre-Louis Bossart 
737eff346f2SPierre-Louis Bossart 	if (res->ops && res->ops->free_stream && res->dev)
738eff346f2SPierre-Louis Bossart 		return res->ops->free_stream(res->dev,
739eff346f2SPierre-Louis Bossart 					     &free_data);
740eff346f2SPierre-Louis Bossart 
741eff346f2SPierre-Louis Bossart 	return 0;
742eff346f2SPierre-Louis Bossart }
743eff346f2SPierre-Louis Bossart 
744c46302ecSVinod Koul /*
74530246e2dSShreyas NC  * bank switch routines
74630246e2dSShreyas NC  */
74730246e2dSShreyas NC 
748b3ad31f3SPierre-Louis Bossart static int intel_pre_bank_switch(struct sdw_intel *sdw)
74930246e2dSShreyas NC {
750b3ad31f3SPierre-Louis Bossart 	struct sdw_cdns *cdns = &sdw->cdns;
751b3ad31f3SPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
75230246e2dSShreyas NC 
75330246e2dSShreyas NC 	/* Write to register only for multi-link */
75430246e2dSShreyas NC 	if (!bus->multi_link)
75530246e2dSShreyas NC 		return 0;
75630246e2dSShreyas NC 
75702629e45SPierre-Louis Bossart 	intel_shim_sync_arm(sdw);
75830246e2dSShreyas NC 
75930246e2dSShreyas NC 	return 0;
76030246e2dSShreyas NC }
76130246e2dSShreyas NC 
762b3ad31f3SPierre-Louis Bossart static int intel_post_bank_switch(struct sdw_intel *sdw)
76330246e2dSShreyas NC {
764b3ad31f3SPierre-Louis Bossart 	struct sdw_cdns *cdns = &sdw->cdns;
765b3ad31f3SPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
7662523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
76730246e2dSShreyas NC 	int sync_reg, ret;
76830246e2dSShreyas NC 
76930246e2dSShreyas NC 	/* Write to register only for multi-link */
77030246e2dSShreyas NC 	if (!bus->multi_link)
77130246e2dSShreyas NC 		return 0;
77230246e2dSShreyas NC 
7734a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
7744a17c441SPierre-Louis Bossart 
77530246e2dSShreyas NC 	/* Read SYNC register */
77630246e2dSShreyas NC 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
77730246e2dSShreyas NC 
77830246e2dSShreyas NC 	/*
77930246e2dSShreyas NC 	 * post_bank_switch() ops is called from the bus in loop for
78030246e2dSShreyas NC 	 * all the Masters in the steam with the expectation that
78130246e2dSShreyas NC 	 * we trigger the bankswitch for the only first Master in the list
78230246e2dSShreyas NC 	 * and do nothing for the other Masters
78330246e2dSShreyas NC 	 *
78430246e2dSShreyas NC 	 * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
78530246e2dSShreyas NC 	 */
7864a17c441SPierre-Louis Bossart 	if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) {
7874a17c441SPierre-Louis Bossart 		ret = 0;
7884a17c441SPierre-Louis Bossart 		goto unlock;
7894a17c441SPierre-Louis Bossart 	}
79030246e2dSShreyas NC 
791437e3289SPierre-Louis Bossart 	ret = intel_shim_sync_go_unlocked(sdw);
7924a17c441SPierre-Louis Bossart unlock:
7934a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
79430246e2dSShreyas NC 
79530246e2dSShreyas NC 	if (ret < 0)
79617ed5befSPierre-Louis Bossart 		dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret);
79730246e2dSShreyas NC 
79830246e2dSShreyas NC 	return ret;
79930246e2dSShreyas NC }
80030246e2dSShreyas NC 
80130246e2dSShreyas NC /*
802c46302ecSVinod Koul  * DAI routines
803c46302ecSVinod Koul  */
804c46302ecSVinod Koul 
8055e7484d0SRander Wang static int intel_startup(struct snd_pcm_substream *substream,
8065e7484d0SRander Wang 			 struct snd_soc_dai *dai)
8075e7484d0SRander Wang {
808ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
809ebf878edSPierre-Louis Bossart 	int ret;
810ebf878edSPierre-Louis Bossart 
811fff1fd9cSPierre-Louis Bossart 	ret = pm_runtime_resume_and_get(cdns->dev);
812ebf878edSPierre-Louis Bossart 	if (ret < 0 && ret != -EACCES) {
813ebf878edSPierre-Louis Bossart 		dev_err_ratelimited(cdns->dev,
814fff1fd9cSPierre-Louis Bossart 				    "pm_runtime_resume_and_get failed in %s, ret %d\n",
815ebf878edSPierre-Louis Bossart 				    __func__, ret);
816ebf878edSPierre-Louis Bossart 		return ret;
817ebf878edSPierre-Louis Bossart 	}
818ff16d1e5SPierre-Louis Bossart 	return 0;
8195e7484d0SRander Wang }
8205e7484d0SRander Wang 
821c46302ecSVinod Koul static int intel_hw_params(struct snd_pcm_substream *substream,
822c46302ecSVinod Koul 			   struct snd_pcm_hw_params *params,
823c46302ecSVinod Koul 			   struct snd_soc_dai *dai)
824c46302ecSVinod Koul {
825c46302ecSVinod Koul 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
826c46302ecSVinod Koul 	struct sdw_intel *sdw = cdns_to_intel(cdns);
827e0767e39SPierre-Louis Bossart 	struct sdw_cdns_dai_runtime *dai_runtime;
82857a34790SPierre-Louis Bossart 	struct sdw_cdns_pdi *pdi;
829c46302ecSVinod Koul 	struct sdw_stream_config sconfig;
830c46302ecSVinod Koul 	struct sdw_port_config *pconfig;
83157a34790SPierre-Louis Bossart 	int ch, dir;
83257a34790SPierre-Louis Bossart 	int ret;
833c46302ecSVinod Koul 
8347dddead7SPierre-Louis Bossart 	dai_runtime = cdns->dai_runtime_array[dai->id];
835e0767e39SPierre-Louis Bossart 	if (!dai_runtime)
836c46302ecSVinod Koul 		return -EIO;
837c46302ecSVinod Koul 
838c46302ecSVinod Koul 	ch = params_channels(params);
839c46302ecSVinod Koul 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
840c46302ecSVinod Koul 		dir = SDW_DATA_DIR_RX;
841c46302ecSVinod Koul 	else
842c46302ecSVinod Koul 		dir = SDW_DATA_DIR_TX;
843c46302ecSVinod Koul 
8441b53385eSBard Liao 	pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id);
845c46302ecSVinod Koul 
84657a34790SPierre-Louis Bossart 	if (!pdi) {
847c46302ecSVinod Koul 		ret = -EINVAL;
84857a34790SPierre-Louis Bossart 		goto error;
849c46302ecSVinod Koul 	}
85057a34790SPierre-Louis Bossart 
85157a34790SPierre-Louis Bossart 	/* do run-time configurations for SHIM, ALH and PDI/PORT */
85257a34790SPierre-Louis Bossart 	intel_pdi_shim_configure(sdw, pdi);
85357a34790SPierre-Louis Bossart 	intel_pdi_alh_configure(sdw, pdi);
85457a34790SPierre-Louis Bossart 	sdw_cdns_config_stream(cdns, ch, dir, pdi);
85557a34790SPierre-Louis Bossart 
856a5a0239cSBard Liao 	/* store pdi and hw_params, may be needed in prepare step */
857e0767e39SPierre-Louis Bossart 	dai_runtime->paused = false;
858e0767e39SPierre-Louis Bossart 	dai_runtime->suspended = false;
859e0767e39SPierre-Louis Bossart 	dai_runtime->pdi = pdi;
860e0767e39SPierre-Louis Bossart 	dai_runtime->hw_params = params;
861c46302ecSVinod Koul 
862c46302ecSVinod Koul 	/* Inform DSP about PDI stream number */
863b86947b5SPierre-Louis Bossart 	ret = intel_params_stream(sdw, substream->stream, dai, params,
8644b206d34SRander Wang 				  sdw->instance,
86557a34790SPierre-Louis Bossart 				  pdi->intel_alh_id);
866c46302ecSVinod Koul 	if (ret)
86757a34790SPierre-Louis Bossart 		goto error;
868c46302ecSVinod Koul 
869c46302ecSVinod Koul 	sconfig.direction = dir;
870c46302ecSVinod Koul 	sconfig.ch_count = ch;
871c46302ecSVinod Koul 	sconfig.frame_rate = params_rate(params);
872e0767e39SPierre-Louis Bossart 	sconfig.type = dai_runtime->stream_type;
873c46302ecSVinod Koul 
874c46302ecSVinod Koul 	sconfig.bps = snd_pcm_format_width(params_format(params));
875c46302ecSVinod Koul 
876c46302ecSVinod Koul 	/* Port configuration */
877235ae89bSZheng Yongjun 	pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
878c46302ecSVinod Koul 	if (!pconfig) {
879c46302ecSVinod Koul 		ret =  -ENOMEM;
88057a34790SPierre-Louis Bossart 		goto error;
881c46302ecSVinod Koul 	}
882c46302ecSVinod Koul 
88357a34790SPierre-Louis Bossart 	pconfig->num = pdi->num;
88457a34790SPierre-Louis Bossart 	pconfig->ch_mask = (1 << ch) - 1;
885c46302ecSVinod Koul 
886c46302ecSVinod Koul 	ret = sdw_stream_add_master(&cdns->bus, &sconfig,
887e0767e39SPierre-Louis Bossart 				    pconfig, 1, dai_runtime->stream);
88857a34790SPierre-Louis Bossart 	if (ret)
88917ed5befSPierre-Louis Bossart 		dev_err(cdns->dev, "add master to stream failed:%d\n", ret);
890c46302ecSVinod Koul 
891c46302ecSVinod Koul 	kfree(pconfig);
89257a34790SPierre-Louis Bossart error:
893c46302ecSVinod Koul 	return ret;
894c46302ecSVinod Koul }
895c46302ecSVinod Koul 
89627b198f4SRander Wang static int intel_prepare(struct snd_pcm_substream *substream,
89727b198f4SRander Wang 			 struct snd_soc_dai *dai)
89827b198f4SRander Wang {
899a5a0239cSBard Liao 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
900a5a0239cSBard Liao 	struct sdw_intel *sdw = cdns_to_intel(cdns);
901e0767e39SPierre-Louis Bossart 	struct sdw_cdns_dai_runtime *dai_runtime;
902a5a0239cSBard Liao 	int ch, dir;
903244eb888SPierre-Louis Bossart 	int ret = 0;
90427b198f4SRander Wang 
9057dddead7SPierre-Louis Bossart 	dai_runtime = cdns->dai_runtime_array[dai->id];
906e0767e39SPierre-Louis Bossart 	if (!dai_runtime) {
907e0767e39SPierre-Louis Bossart 		dev_err(dai->dev, "failed to get dai runtime in %s\n",
90827b198f4SRander Wang 			__func__);
90927b198f4SRander Wang 		return -EIO;
91027b198f4SRander Wang 	}
91127b198f4SRander Wang 
912e0767e39SPierre-Louis Bossart 	if (dai_runtime->suspended) {
913e0767e39SPierre-Louis Bossart 		dai_runtime->suspended = false;
914a5a0239cSBard Liao 
915a5a0239cSBard Liao 		/*
916a5a0239cSBard Liao 		 * .prepare() is called after system resume, where we
917a5a0239cSBard Liao 		 * need to reinitialize the SHIM/ALH/Cadence IP.
918a5a0239cSBard Liao 		 * .prepare() is also called to deal with underflows,
919a5a0239cSBard Liao 		 * but in those cases we cannot touch ALH/SHIM
920a5a0239cSBard Liao 		 * registers
921a5a0239cSBard Liao 		 */
922a5a0239cSBard Liao 
923a5a0239cSBard Liao 		/* configure stream */
924e0767e39SPierre-Louis Bossart 		ch = params_channels(dai_runtime->hw_params);
925a5a0239cSBard Liao 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
926a5a0239cSBard Liao 			dir = SDW_DATA_DIR_RX;
927a5a0239cSBard Liao 		else
928a5a0239cSBard Liao 			dir = SDW_DATA_DIR_TX;
929a5a0239cSBard Liao 
930e0767e39SPierre-Louis Bossart 		intel_pdi_shim_configure(sdw, dai_runtime->pdi);
931e0767e39SPierre-Louis Bossart 		intel_pdi_alh_configure(sdw, dai_runtime->pdi);
932e0767e39SPierre-Louis Bossart 		sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi);
933a5a0239cSBard Liao 
934a5a0239cSBard Liao 		/* Inform DSP about PDI stream number */
935b86947b5SPierre-Louis Bossart 		ret = intel_params_stream(sdw, substream->stream, dai,
936e0767e39SPierre-Louis Bossart 					  dai_runtime->hw_params,
937a5a0239cSBard Liao 					  sdw->instance,
938e0767e39SPierre-Louis Bossart 					  dai_runtime->pdi->intel_alh_id);
939a5a0239cSBard Liao 	}
940a5a0239cSBard Liao 
941a5a0239cSBard Liao 	return ret;
94227b198f4SRander Wang }
94327b198f4SRander Wang 
944c46302ecSVinod Koul static int
945c46302ecSVinod Koul intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
946c46302ecSVinod Koul {
947c46302ecSVinod Koul 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
948eff346f2SPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
949e0767e39SPierre-Louis Bossart 	struct sdw_cdns_dai_runtime *dai_runtime;
950c46302ecSVinod Koul 	int ret;
951c46302ecSVinod Koul 
9527dddead7SPierre-Louis Bossart 	dai_runtime = cdns->dai_runtime_array[dai->id];
953e0767e39SPierre-Louis Bossart 	if (!dai_runtime)
954c46302ecSVinod Koul 		return -EIO;
955c46302ecSVinod Koul 
956244eb888SPierre-Louis Bossart 	/*
957244eb888SPierre-Louis Bossart 	 * The sdw stream state will transition to RELEASED when stream->
958244eb888SPierre-Louis Bossart 	 * master_list is empty. So the stream state will transition to
959244eb888SPierre-Louis Bossart 	 * DEPREPARED for the first cpu-dai and to RELEASED for the last
960244eb888SPierre-Louis Bossart 	 * cpu-dai.
961244eb888SPierre-Louis Bossart 	 */
962e0767e39SPierre-Louis Bossart 	ret = sdw_stream_remove_master(&cdns->bus, dai_runtime->stream);
963eff346f2SPierre-Louis Bossart 	if (ret < 0) {
96417ed5befSPierre-Louis Bossart 		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
965e0767e39SPierre-Louis Bossart 			dai_runtime->stream->name, ret);
966c46302ecSVinod Koul 		return ret;
967c46302ecSVinod Koul 	}
968c46302ecSVinod Koul 
969b86947b5SPierre-Louis Bossart 	ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance);
970eff346f2SPierre-Louis Bossart 	if (ret < 0) {
9714e3ea93eSPierre-Louis Bossart 		dev_err(dai->dev, "intel_free_stream: failed %d\n", ret);
972eff346f2SPierre-Louis Bossart 		return ret;
973eff346f2SPierre-Louis Bossart 	}
974eff346f2SPierre-Louis Bossart 
975e0767e39SPierre-Louis Bossart 	dai_runtime->hw_params = NULL;
976e0767e39SPierre-Louis Bossart 	dai_runtime->pdi = NULL;
977a5a0239cSBard Liao 
978eff346f2SPierre-Louis Bossart 	return 0;
979eff346f2SPierre-Louis Bossart }
980eff346f2SPierre-Louis Bossart 
981183c7687SPierre-Louis Bossart static void intel_shutdown(struct snd_pcm_substream *substream,
982183c7687SPierre-Louis Bossart 			   struct snd_soc_dai *dai)
983183c7687SPierre-Louis Bossart {
984ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
985183c7687SPierre-Louis Bossart 
986ebf878edSPierre-Louis Bossart 	pm_runtime_mark_last_busy(cdns->dev);
987ebf878edSPierre-Louis Bossart 	pm_runtime_put_autosuspend(cdns->dev);
988183c7687SPierre-Louis Bossart }
989183c7687SPierre-Louis Bossart 
990c46302ecSVinod Koul static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
991c46302ecSVinod Koul 				    void *stream, int direction)
992c46302ecSVinod Koul {
99363a6aa96SPierre-Louis Bossart 	return cdns_set_sdw_stream(dai, stream, direction);
994c46302ecSVinod Koul }
995c46302ecSVinod Koul 
99609553140SPierre-Louis Bossart static void *intel_get_sdw_stream(struct snd_soc_dai *dai,
99709553140SPierre-Louis Bossart 				  int direction)
99809553140SPierre-Louis Bossart {
9997dddead7SPierre-Louis Bossart 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
1000e0767e39SPierre-Louis Bossart 	struct sdw_cdns_dai_runtime *dai_runtime;
100109553140SPierre-Louis Bossart 
10027dddead7SPierre-Louis Bossart 	dai_runtime = cdns->dai_runtime_array[dai->id];
1003e0767e39SPierre-Louis Bossart 	if (!dai_runtime)
100406dcb4e4SPierre-Louis Bossart 		return ERR_PTR(-EINVAL);
100509553140SPierre-Louis Bossart 
1006e0767e39SPierre-Louis Bossart 	return dai_runtime->stream;
100709553140SPierre-Louis Bossart }
100809553140SPierre-Louis Bossart 
10098ddeafb9SRanjani Sridharan static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
10108ddeafb9SRanjani Sridharan {
10118ddeafb9SRanjani Sridharan 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
10128ddeafb9SRanjani Sridharan 	struct sdw_intel *sdw = cdns_to_intel(cdns);
10136d1c1a73SBard Liao 	struct sdw_intel_link_res *res = sdw->link_res;
1014e0767e39SPierre-Louis Bossart 	struct sdw_cdns_dai_runtime *dai_runtime;
10158ddeafb9SRanjani Sridharan 	int ret = 0;
10168ddeafb9SRanjani Sridharan 
10176d1c1a73SBard Liao 	/*
10186d1c1a73SBard Liao 	 * The .trigger callback is used to send required IPC to audio
10196d1c1a73SBard Liao 	 * firmware. The .free_stream callback will still be called
10206d1c1a73SBard Liao 	 * by intel_free_stream() in the TRIGGER_SUSPEND case.
10216d1c1a73SBard Liao 	 */
10226d1c1a73SBard Liao 	if (res->ops && res->ops->trigger)
10236d1c1a73SBard Liao 		res->ops->trigger(dai, cmd, substream->stream);
10246d1c1a73SBard Liao 
10257dddead7SPierre-Louis Bossart 	dai_runtime = cdns->dai_runtime_array[dai->id];
1026e0767e39SPierre-Louis Bossart 	if (!dai_runtime) {
1027e0767e39SPierre-Louis Bossart 		dev_err(dai->dev, "failed to get dai runtime in %s\n",
10288ddeafb9SRanjani Sridharan 			__func__);
10298ddeafb9SRanjani Sridharan 		return -EIO;
10308ddeafb9SRanjani Sridharan 	}
10318ddeafb9SRanjani Sridharan 
10328ddeafb9SRanjani Sridharan 	switch (cmd) {
10338ddeafb9SRanjani Sridharan 	case SNDRV_PCM_TRIGGER_SUSPEND:
10348ddeafb9SRanjani Sridharan 
10358ddeafb9SRanjani Sridharan 		/*
10368ddeafb9SRanjani Sridharan 		 * The .prepare callback is used to deal with xruns and resume operations.
10378ddeafb9SRanjani Sridharan 		 * In the case of xruns, the DMAs and SHIM registers cannot be touched,
10388ddeafb9SRanjani Sridharan 		 * but for resume operations the DMAs and SHIM registers need to be initialized.
10398ddeafb9SRanjani Sridharan 		 * the .trigger callback is used to track the suspend case only.
10408ddeafb9SRanjani Sridharan 		 */
10418ddeafb9SRanjani Sridharan 
1042e0767e39SPierre-Louis Bossart 		dai_runtime->suspended = true;
10438ddeafb9SRanjani Sridharan 
10448ddeafb9SRanjani Sridharan 		ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance);
10458ddeafb9SRanjani Sridharan 		break;
10468ddeafb9SRanjani Sridharan 
10478ddeafb9SRanjani Sridharan 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
1048e0767e39SPierre-Louis Bossart 		dai_runtime->paused = true;
10498ddeafb9SRanjani Sridharan 		break;
10508ddeafb9SRanjani Sridharan 	case SNDRV_PCM_TRIGGER_STOP:
10518ddeafb9SRanjani Sridharan 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
1052e0767e39SPierre-Louis Bossart 		dai_runtime->paused = false;
10538ddeafb9SRanjani Sridharan 		break;
10548ddeafb9SRanjani Sridharan 	default:
10558ddeafb9SRanjani Sridharan 		break;
10568ddeafb9SRanjani Sridharan 	}
10578ddeafb9SRanjani Sridharan 
10588ddeafb9SRanjani Sridharan 	return ret;
10598ddeafb9SRanjani Sridharan }
10608ddeafb9SRanjani Sridharan 
10613e9c9f90SPierre-Louis Bossart static int intel_component_probe(struct snd_soc_component *component)
10623e9c9f90SPierre-Louis Bossart {
10633e9c9f90SPierre-Louis Bossart 	int ret;
10643e9c9f90SPierre-Louis Bossart 
10653e9c9f90SPierre-Louis Bossart 	/*
10663e9c9f90SPierre-Louis Bossart 	 * make sure the device is pm_runtime_active before initiating
10673e9c9f90SPierre-Louis Bossart 	 * bus transactions during the card registration.
10683e9c9f90SPierre-Louis Bossart 	 * We use pm_runtime_resume() here, without taking a reference
10693e9c9f90SPierre-Louis Bossart 	 * and releasing it immediately.
10703e9c9f90SPierre-Louis Bossart 	 */
10713e9c9f90SPierre-Louis Bossart 	ret = pm_runtime_resume(component->dev);
10723e9c9f90SPierre-Louis Bossart 	if (ret < 0 && ret != -EACCES)
10733e9c9f90SPierre-Louis Bossart 		return ret;
10743e9c9f90SPierre-Louis Bossart 
10753e9c9f90SPierre-Louis Bossart 	return 0;
10763e9c9f90SPierre-Louis Bossart }
10773e9c9f90SPierre-Louis Bossart 
10788ddeafb9SRanjani Sridharan static int intel_component_dais_suspend(struct snd_soc_component *component)
10798ddeafb9SRanjani Sridharan {
10808ddeafb9SRanjani Sridharan 	struct snd_soc_dai *dai;
10818ddeafb9SRanjani Sridharan 
10828ddeafb9SRanjani Sridharan 	/*
10838ddeafb9SRanjani Sridharan 	 * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core
10848ddeafb9SRanjani Sridharan 	 * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state.
10858ddeafb9SRanjani Sridharan 	 * Since the component suspend is called last, we can trap this corner case
10868ddeafb9SRanjani Sridharan 	 * and force the DAIs to release their resources.
10878ddeafb9SRanjani Sridharan 	 */
10888ddeafb9SRanjani Sridharan 	for_each_component_dais(component, dai) {
10898ddeafb9SRanjani Sridharan 		struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
10908ddeafb9SRanjani Sridharan 		struct sdw_intel *sdw = cdns_to_intel(cdns);
1091e0767e39SPierre-Louis Bossart 		struct sdw_cdns_dai_runtime *dai_runtime;
10928ddeafb9SRanjani Sridharan 		int ret;
10938ddeafb9SRanjani Sridharan 
10947dddead7SPierre-Louis Bossart 		dai_runtime = cdns->dai_runtime_array[dai->id];
10958ddeafb9SRanjani Sridharan 
1096e0767e39SPierre-Louis Bossart 		if (!dai_runtime)
10978ddeafb9SRanjani Sridharan 			continue;
10988ddeafb9SRanjani Sridharan 
1099e0767e39SPierre-Louis Bossart 		if (dai_runtime->suspended)
11008ddeafb9SRanjani Sridharan 			continue;
11018ddeafb9SRanjani Sridharan 
1102e0767e39SPierre-Louis Bossart 		if (dai_runtime->paused) {
1103e0767e39SPierre-Louis Bossart 			dai_runtime->suspended = true;
11048ddeafb9SRanjani Sridharan 
11057dddead7SPierre-Louis Bossart 			ret = intel_free_stream(sdw, dai_runtime->direction, dai, sdw->instance);
11068ddeafb9SRanjani Sridharan 			if (ret < 0)
11078ddeafb9SRanjani Sridharan 				return ret;
11088ddeafb9SRanjani Sridharan 		}
11098ddeafb9SRanjani Sridharan 	}
11108ddeafb9SRanjani Sridharan 
11118ddeafb9SRanjani Sridharan 	return 0;
11128ddeafb9SRanjani Sridharan }
11138ddeafb9SRanjani Sridharan 
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,
1118c46302ecSVinod Koul 	.hw_free = intel_hw_free,
11198ddeafb9SRanjani Sridharan 	.trigger = intel_trigger,
1120183c7687SPierre-Louis Bossart 	.shutdown = intel_shutdown,
1121e8444560SPierre-Louis Bossart 	.set_stream = intel_pcm_set_sdw_stream,
1122e8444560SPierre-Louis Bossart 	.get_stream = intel_get_sdw_stream,
1123c46302ecSVinod Koul };
1124c46302ecSVinod Koul 
1125c46302ecSVinod Koul static const struct snd_soc_component_driver dai_component = {
1126c46302ecSVinod Koul 	.name			= "soundwire",
11273e9c9f90SPierre-Louis Bossart 	.probe			= intel_component_probe,
1128ca682020SCharles Keepax 	.suspend		= intel_component_dais_suspend,
1129ca682020SCharles Keepax 	.legacy_dai_naming	= 1,
1130c46302ecSVinod Koul };
1131c46302ecSVinod Koul 
1132c46302ecSVinod Koul static int intel_create_dai(struct sdw_cdns *cdns,
1133c46302ecSVinod Koul 			    struct snd_soc_dai_driver *dais,
1134c46302ecSVinod Koul 			    enum intel_pdi_type type,
113563a6aa96SPierre-Louis Bossart 			    u32 num, u32 off, u32 max_ch)
1136c46302ecSVinod Koul {
1137c46302ecSVinod Koul 	int i;
1138c46302ecSVinod Koul 
1139c46302ecSVinod Koul 	if (num == 0)
1140c46302ecSVinod Koul 		return 0;
1141c46302ecSVinod Koul 
1142c46302ecSVinod Koul 	 /* TODO: Read supported rates/formats from hardware */
1143c46302ecSVinod Koul 	for (i = off; i < (off + num); i++) {
1144bf6d6e68SPierre-Louis Bossart 		dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL,
1145bf6d6e68SPierre-Louis Bossart 					      "SDW%d Pin%d",
1146c46302ecSVinod Koul 					      cdns->instance, i);
1147c46302ecSVinod Koul 		if (!dais[i].name)
1148c46302ecSVinod Koul 			return -ENOMEM;
1149c46302ecSVinod Koul 
1150c46302ecSVinod Koul 		if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) {
1151c46302ecSVinod Koul 			dais[i].playback.channels_min = 1;
1152c46302ecSVinod Koul 			dais[i].playback.channels_max = max_ch;
1153c46302ecSVinod Koul 			dais[i].playback.rates = SNDRV_PCM_RATE_48000;
1154c46302ecSVinod Koul 			dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
1155c46302ecSVinod Koul 		}
1156c46302ecSVinod Koul 
1157c46302ecSVinod Koul 		if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) {
115839194128SSrinivas Kandagatla 			dais[i].capture.channels_min = 1;
115939194128SSrinivas Kandagatla 			dais[i].capture.channels_max = max_ch;
1160c46302ecSVinod Koul 			dais[i].capture.rates = SNDRV_PCM_RATE_48000;
1161c46302ecSVinod Koul 			dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
1162c46302ecSVinod Koul 		}
1163c46302ecSVinod Koul 
1164c46302ecSVinod Koul 		dais[i].ops = &intel_pcm_dai_ops;
1165c46302ecSVinod Koul 	}
1166c46302ecSVinod Koul 
1167c46302ecSVinod Koul 	return 0;
1168c46302ecSVinod Koul }
1169c46302ecSVinod Koul 
1170c46302ecSVinod Koul static int intel_register_dai(struct sdw_intel *sdw)
1171c46302ecSVinod Koul {
11727dddead7SPierre-Louis Bossart 	struct sdw_cdns_dai_runtime **dai_runtime_array;
117330cbae66SPierre-Louis Bossart 	struct sdw_cdns_stream_config config;
1174c46302ecSVinod Koul 	struct sdw_cdns *cdns = &sdw->cdns;
1175c46302ecSVinod Koul 	struct sdw_cdns_streams *stream;
1176c46302ecSVinod Koul 	struct snd_soc_dai_driver *dais;
1177c46302ecSVinod Koul 	int num_dai, ret, off = 0;
1178c46302ecSVinod Koul 
117930cbae66SPierre-Louis Bossart 	/* Read the PDI config and initialize cadence PDI */
118030cbae66SPierre-Louis Bossart 	intel_pdi_init(sdw, &config);
118130cbae66SPierre-Louis Bossart 	ret = sdw_cdns_pdi_init(cdns, config);
118230cbae66SPierre-Louis Bossart 	if (ret)
118330cbae66SPierre-Louis Bossart 		return ret;
118430cbae66SPierre-Louis Bossart 
118530cbae66SPierre-Louis Bossart 	intel_pdi_ch_update(sdw);
118630cbae66SPierre-Louis Bossart 
1187c46302ecSVinod Koul 	/* DAIs are created based on total number of PDIs supported */
118863a6aa96SPierre-Louis Bossart 	num_dai = cdns->pcm.num_pdi;
1189c46302ecSVinod Koul 
11907dddead7SPierre-Louis Bossart 	dai_runtime_array = devm_kcalloc(cdns->dev, num_dai,
11917dddead7SPierre-Louis Bossart 					 sizeof(struct sdw_cdns_dai_runtime *),
11927dddead7SPierre-Louis Bossart 					 GFP_KERNEL);
11937dddead7SPierre-Louis Bossart 	if (!dai_runtime_array)
11947dddead7SPierre-Louis Bossart 		return -ENOMEM;
11957dddead7SPierre-Louis Bossart 	cdns->dai_runtime_array = dai_runtime_array;
11967dddead7SPierre-Louis Bossart 
1197c46302ecSVinod Koul 	dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL);
1198c46302ecSVinod Koul 	if (!dais)
1199c46302ecSVinod Koul 		return -ENOMEM;
1200c46302ecSVinod Koul 
1201c46302ecSVinod Koul 	/* Create PCM DAIs */
1202c46302ecSVinod Koul 	stream = &cdns->pcm;
1203c46302ecSVinod Koul 
1204cf924962SBard Liao 	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in,
120563a6aa96SPierre-Louis Bossart 			       off, stream->num_ch_in);
1206c46302ecSVinod Koul 	if (ret)
1207c46302ecSVinod Koul 		return ret;
1208c46302ecSVinod Koul 
1209c46302ecSVinod Koul 	off += cdns->pcm.num_in;
12101215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out,
121163a6aa96SPierre-Louis Bossart 			       off, stream->num_ch_out);
1212c46302ecSVinod Koul 	if (ret)
1213c46302ecSVinod Koul 		return ret;
1214c46302ecSVinod Koul 
1215c46302ecSVinod Koul 	off += cdns->pcm.num_out;
12161215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd,
121763a6aa96SPierre-Louis Bossart 			       off, stream->num_ch_bd);
1218c46302ecSVinod Koul 	if (ret)
1219c46302ecSVinod Koul 		return ret;
1220c46302ecSVinod Koul 
122154f391ddSPierre-Louis Bossart 	return devm_snd_soc_register_component(cdns->dev, &dai_component,
1222c46302ecSVinod Koul 					       dais, num_dai);
1223c46302ecSVinod Koul }
1224c46302ecSVinod Koul 
12258d875da7SPierre-Louis Bossart static int intel_start_bus(struct sdw_intel *sdw)
12268d875da7SPierre-Louis Bossart {
12278d875da7SPierre-Louis Bossart 	struct device *dev = sdw->cdns.dev;
12288d875da7SPierre-Louis Bossart 	struct sdw_cdns *cdns = &sdw->cdns;
12298d875da7SPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
12308d875da7SPierre-Louis Bossart 	int ret;
12318d875da7SPierre-Louis Bossart 
12328d875da7SPierre-Louis Bossart 	ret = sdw_cdns_enable_interrupt(cdns, true);
12338d875da7SPierre-Louis Bossart 	if (ret < 0) {
12348d875da7SPierre-Louis Bossart 		dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret);
12358d875da7SPierre-Louis Bossart 		return ret;
12368d875da7SPierre-Louis Bossart 	}
12378d875da7SPierre-Louis Bossart 
12388d875da7SPierre-Louis Bossart 	/*
12398d875da7SPierre-Louis Bossart 	 * follow recommended programming flows to avoid timeouts when
12408d875da7SPierre-Louis Bossart 	 * gsync is enabled
12418d875da7SPierre-Louis Bossart 	 */
12428d875da7SPierre-Louis Bossart 	if (bus->multi_link)
12438d875da7SPierre-Louis Bossart 		intel_shim_sync_arm(sdw);
12448d875da7SPierre-Louis Bossart 
12458d875da7SPierre-Louis Bossart 	ret = sdw_cdns_init(cdns);
12468d875da7SPierre-Louis Bossart 	if (ret < 0) {
12478d875da7SPierre-Louis Bossart 		dev_err(dev, "%s: unable to initialize Cadence IP: %d\n", __func__, ret);
12488d875da7SPierre-Louis Bossart 		goto err_interrupt;
12498d875da7SPierre-Louis Bossart 	}
12508d875da7SPierre-Louis Bossart 
12518d875da7SPierre-Louis Bossart 	ret = sdw_cdns_exit_reset(cdns);
12528d875da7SPierre-Louis Bossart 	if (ret < 0) {
12538d875da7SPierre-Louis Bossart 		dev_err(dev, "%s: unable to exit bus reset sequence: %d\n", __func__, ret);
12548d875da7SPierre-Louis Bossart 		goto err_interrupt;
12558d875da7SPierre-Louis Bossart 	}
12568d875da7SPierre-Louis Bossart 
12578d875da7SPierre-Louis Bossart 	if (bus->multi_link) {
12588d875da7SPierre-Louis Bossart 		ret = intel_shim_sync_go(sdw);
12598d875da7SPierre-Louis Bossart 		if (ret < 0) {
12608d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: sync go failed: %d\n", __func__, ret);
12618d875da7SPierre-Louis Bossart 			goto err_interrupt;
12628d875da7SPierre-Louis Bossart 		}
12638d875da7SPierre-Louis Bossart 	}
12648d875da7SPierre-Louis Bossart 	sdw_cdns_check_self_clearing_bits(cdns, __func__,
12658d875da7SPierre-Louis Bossart 					  true, INTEL_MASTER_RESET_ITERATIONS);
12668d875da7SPierre-Louis Bossart 
12678d875da7SPierre-Louis Bossart 	return 0;
12688d875da7SPierre-Louis Bossart 
12698d875da7SPierre-Louis Bossart err_interrupt:
12708d875da7SPierre-Louis Bossart 	sdw_cdns_enable_interrupt(cdns, false);
12718d875da7SPierre-Louis Bossart 	return ret;
12728d875da7SPierre-Louis Bossart }
12738d875da7SPierre-Louis Bossart 
12748d875da7SPierre-Louis Bossart static int intel_start_bus_after_reset(struct sdw_intel *sdw)
12758d875da7SPierre-Louis Bossart {
12768d875da7SPierre-Louis Bossart 	struct device *dev = sdw->cdns.dev;
12778d875da7SPierre-Louis Bossart 	struct sdw_cdns *cdns = &sdw->cdns;
12788d875da7SPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
12798d875da7SPierre-Louis Bossart 	bool clock_stop0;
12808d875da7SPierre-Louis Bossart 	int status;
12818d875da7SPierre-Louis Bossart 	int ret;
12828d875da7SPierre-Louis Bossart 
12838d875da7SPierre-Louis Bossart 	/*
12848d875da7SPierre-Louis Bossart 	 * An exception condition occurs for the CLK_STOP_BUS_RESET
12858d875da7SPierre-Louis Bossart 	 * case if one or more masters remain active. In this condition,
12868d875da7SPierre-Louis Bossart 	 * all the masters are powered on for they are in the same power
12878d875da7SPierre-Louis Bossart 	 * domain. Master can preserve its context for clock stop0, so
12888d875da7SPierre-Louis Bossart 	 * there is no need to clear slave status and reset bus.
12898d875da7SPierre-Louis Bossart 	 */
12908d875da7SPierre-Louis Bossart 	clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
12918d875da7SPierre-Louis Bossart 
12928d875da7SPierre-Louis Bossart 	if (!clock_stop0) {
12938d875da7SPierre-Louis Bossart 
12948d875da7SPierre-Louis Bossart 		/*
12958d875da7SPierre-Louis Bossart 		 * make sure all Slaves are tagged as UNATTACHED and
12968d875da7SPierre-Louis Bossart 		 * provide reason for reinitialization
12978d875da7SPierre-Louis Bossart 		 */
12988d875da7SPierre-Louis Bossart 
12998d875da7SPierre-Louis Bossart 		status = SDW_UNATTACH_REQUEST_MASTER_RESET;
13008d875da7SPierre-Louis Bossart 		sdw_clear_slave_status(bus, status);
13018d875da7SPierre-Louis Bossart 
13028d875da7SPierre-Louis Bossart 		ret = sdw_cdns_enable_interrupt(cdns, true);
13038d875da7SPierre-Louis Bossart 		if (ret < 0) {
13048d875da7SPierre-Louis Bossart 			dev_err(dev, "cannot enable interrupts during resume\n");
13058d875da7SPierre-Louis Bossart 			return ret;
13068d875da7SPierre-Louis Bossart 		}
13078d875da7SPierre-Louis Bossart 
13088d875da7SPierre-Louis Bossart 		/*
13098d875da7SPierre-Louis Bossart 		 * follow recommended programming flows to avoid
13108d875da7SPierre-Louis Bossart 		 * timeouts when gsync is enabled
13118d875da7SPierre-Louis Bossart 		 */
13128d875da7SPierre-Louis Bossart 		if (bus->multi_link)
13138d875da7SPierre-Louis Bossart 			intel_shim_sync_arm(sdw);
13148d875da7SPierre-Louis Bossart 
13158d875da7SPierre-Louis Bossart 		/*
13168d875da7SPierre-Louis Bossart 		 * Re-initialize the IP since it was powered-off
13178d875da7SPierre-Louis Bossart 		 */
13188d875da7SPierre-Louis Bossart 		sdw_cdns_init(&sdw->cdns);
13198d875da7SPierre-Louis Bossart 
13208d875da7SPierre-Louis Bossart 	} else {
13218d875da7SPierre-Louis Bossart 		ret = sdw_cdns_enable_interrupt(cdns, true);
13228d875da7SPierre-Louis Bossart 		if (ret < 0) {
13238d875da7SPierre-Louis Bossart 			dev_err(dev, "cannot enable interrupts during resume\n");
13248d875da7SPierre-Louis Bossart 			return ret;
13258d875da7SPierre-Louis Bossart 		}
13268d875da7SPierre-Louis Bossart 	}
13278d875da7SPierre-Louis Bossart 
13288d875da7SPierre-Louis Bossart 	ret = sdw_cdns_clock_restart(cdns, !clock_stop0);
13298d875da7SPierre-Louis Bossart 	if (ret < 0) {
13308d875da7SPierre-Louis Bossart 		dev_err(dev, "unable to restart clock during resume\n");
13318d875da7SPierre-Louis Bossart 		goto err_interrupt;
13328d875da7SPierre-Louis Bossart 	}
13338d875da7SPierre-Louis Bossart 
13348d875da7SPierre-Louis Bossart 	if (!clock_stop0) {
13358d875da7SPierre-Louis Bossart 		ret = sdw_cdns_exit_reset(cdns);
13368d875da7SPierre-Louis Bossart 		if (ret < 0) {
13378d875da7SPierre-Louis Bossart 			dev_err(dev, "unable to exit bus reset sequence during resume\n");
13388d875da7SPierre-Louis Bossart 			goto err_interrupt;
13398d875da7SPierre-Louis Bossart 		}
13408d875da7SPierre-Louis Bossart 
13418d875da7SPierre-Louis Bossart 		if (bus->multi_link) {
13428d875da7SPierre-Louis Bossart 			ret = intel_shim_sync_go(sdw);
13438d875da7SPierre-Louis Bossart 			if (ret < 0) {
13448d875da7SPierre-Louis Bossart 				dev_err(sdw->cdns.dev, "sync go failed during resume\n");
13458d875da7SPierre-Louis Bossart 				goto err_interrupt;
13468d875da7SPierre-Louis Bossart 			}
13478d875da7SPierre-Louis Bossart 		}
13488d875da7SPierre-Louis Bossart 	}
13498d875da7SPierre-Louis Bossart 	sdw_cdns_check_self_clearing_bits(cdns, __func__, true, INTEL_MASTER_RESET_ITERATIONS);
13508d875da7SPierre-Louis Bossart 
13518d875da7SPierre-Louis Bossart 	return 0;
13528d875da7SPierre-Louis Bossart 
13538d875da7SPierre-Louis Bossart err_interrupt:
13548d875da7SPierre-Louis Bossart 	sdw_cdns_enable_interrupt(cdns, false);
13558d875da7SPierre-Louis Bossart 	return ret;
13568d875da7SPierre-Louis Bossart }
13578d875da7SPierre-Louis Bossart 
13588d875da7SPierre-Louis Bossart static void intel_check_clock_stop(struct sdw_intel *sdw)
13598d875da7SPierre-Louis Bossart {
13608d875da7SPierre-Louis Bossart 	struct device *dev = sdw->cdns.dev;
13618d875da7SPierre-Louis Bossart 	bool clock_stop0;
13628d875da7SPierre-Louis Bossart 
13638d875da7SPierre-Louis Bossart 	clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
13648d875da7SPierre-Louis Bossart 	if (!clock_stop0)
13658d875da7SPierre-Louis Bossart 		dev_err(dev, "%s: invalid configuration, clock was not stopped\n", __func__);
13668d875da7SPierre-Louis Bossart }
13678d875da7SPierre-Louis Bossart 
13688d875da7SPierre-Louis Bossart static int intel_start_bus_after_clock_stop(struct sdw_intel *sdw)
13698d875da7SPierre-Louis Bossart {
13708d875da7SPierre-Louis Bossart 	struct device *dev = sdw->cdns.dev;
13718d875da7SPierre-Louis Bossart 	struct sdw_cdns *cdns = &sdw->cdns;
13728d875da7SPierre-Louis Bossart 	int ret;
13738d875da7SPierre-Louis Bossart 
13748d875da7SPierre-Louis Bossart 	ret = sdw_cdns_enable_interrupt(cdns, true);
13758d875da7SPierre-Louis Bossart 	if (ret < 0) {
13768d875da7SPierre-Louis Bossart 		dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret);
13778d875da7SPierre-Louis Bossart 		return ret;
13788d875da7SPierre-Louis Bossart 	}
13798d875da7SPierre-Louis Bossart 
13808d875da7SPierre-Louis Bossart 	ret = sdw_cdns_clock_restart(cdns, false);
13818d875da7SPierre-Louis Bossart 	if (ret < 0) {
13828d875da7SPierre-Louis Bossart 		dev_err(dev, "%s: unable to restart clock: %d\n", __func__, ret);
13838d875da7SPierre-Louis Bossart 		sdw_cdns_enable_interrupt(cdns, false);
13848d875da7SPierre-Louis Bossart 		return ret;
13858d875da7SPierre-Louis Bossart 	}
13868d875da7SPierre-Louis Bossart 
13878d875da7SPierre-Louis Bossart 	sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime no_quirks",
13888d875da7SPierre-Louis Bossart 					  true, INTEL_MASTER_RESET_ITERATIONS);
13898d875da7SPierre-Louis Bossart 
13908d875da7SPierre-Louis Bossart 	return 0;
13918d875da7SPierre-Louis Bossart }
13928d875da7SPierre-Louis Bossart 
1393503ae285SPierre-Louis Bossart static int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop)
1394503ae285SPierre-Louis Bossart {
1395503ae285SPierre-Louis Bossart 	struct device *dev = sdw->cdns.dev;
1396503ae285SPierre-Louis Bossart 	struct sdw_cdns *cdns = &sdw->cdns;
1397503ae285SPierre-Louis Bossart 	bool wake_enable = false;
1398503ae285SPierre-Louis Bossart 	int ret;
1399503ae285SPierre-Louis Bossart 
1400503ae285SPierre-Louis Bossart 	if (clock_stop) {
1401503ae285SPierre-Louis Bossart 		ret = sdw_cdns_clock_stop(cdns, true);
1402503ae285SPierre-Louis Bossart 		if (ret < 0)
1403503ae285SPierre-Louis Bossart 			dev_err(dev, "%s: cannot stop clock: %d\n", __func__, ret);
1404503ae285SPierre-Louis Bossart 		else
1405503ae285SPierre-Louis Bossart 			wake_enable = true;
1406503ae285SPierre-Louis Bossart 	}
1407503ae285SPierre-Louis Bossart 
1408503ae285SPierre-Louis Bossart 	ret = sdw_cdns_enable_interrupt(cdns, false);
1409503ae285SPierre-Louis Bossart 	if (ret < 0) {
1410503ae285SPierre-Louis Bossart 		dev_err(dev, "%s: cannot disable interrupts: %d\n", __func__, ret);
1411503ae285SPierre-Louis Bossart 		return ret;
1412503ae285SPierre-Louis Bossart 	}
1413503ae285SPierre-Louis Bossart 
1414503ae285SPierre-Louis Bossart 	ret = intel_link_power_down(sdw);
1415503ae285SPierre-Louis Bossart 	if (ret) {
1416503ae285SPierre-Louis Bossart 		dev_err(dev, "%s: Link power down failed: %d\n", __func__, ret);
1417503ae285SPierre-Louis Bossart 		return ret;
1418503ae285SPierre-Louis Bossart 	}
1419503ae285SPierre-Louis Bossart 
1420503ae285SPierre-Louis Bossart 	intel_shim_wake(sdw, wake_enable);
1421503ae285SPierre-Louis Bossart 
1422503ae285SPierre-Louis Bossart 	return 0;
1423503ae285SPierre-Louis Bossart }
1424503ae285SPierre-Louis Bossart 
1425b3ad31f3SPierre-Louis Bossart const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops = {
1426fb2dc6a0SPierre-Louis Bossart 	.debugfs_init = intel_debugfs_init,
1427fb2dc6a0SPierre-Louis Bossart 	.debugfs_exit = intel_debugfs_exit,
1428fb2dc6a0SPierre-Louis Bossart 
1429b6234bccSPierre-Louis Bossart 	.register_dai = intel_register_dai,
1430b6234bccSPierre-Louis Bossart 
14313db0c5a6SPierre-Louis Bossart 	.check_clock_stop = intel_check_clock_stop,
14323db0c5a6SPierre-Louis Bossart 	.start_bus = intel_start_bus,
14333db0c5a6SPierre-Louis Bossart 	.start_bus_after_reset = intel_start_bus_after_reset,
14343db0c5a6SPierre-Louis Bossart 	.start_bus_after_clock_stop = intel_start_bus_after_clock_stop,
14353db0c5a6SPierre-Louis Bossart 	.stop_bus = intel_stop_bus,
14363db0c5a6SPierre-Louis Bossart 
1437*49c9ff45SPierre-Louis Bossart 	.link_power_up = intel_link_power_up,
1438*49c9ff45SPierre-Louis Bossart 	.link_power_down = intel_link_power_down,
1439*49c9ff45SPierre-Louis Bossart 
1440b3ad31f3SPierre-Louis Bossart 	.pre_bank_switch = intel_pre_bank_switch,
1441b3ad31f3SPierre-Louis Bossart 	.post_bank_switch = intel_post_bank_switch,
1442b3ad31f3SPierre-Louis Bossart };
1443b3ad31f3SPierre-Louis Bossart EXPORT_SYMBOL_NS(sdw_intel_cnl_hw_ops, SOUNDWIRE_INTEL);
1444b3ad31f3SPierre-Louis Bossart 
1445b3ad31f3SPierre-Louis Bossart static int generic_pre_bank_switch(struct sdw_bus *bus)
1446b3ad31f3SPierre-Louis Bossart {
1447b3ad31f3SPierre-Louis Bossart 	struct sdw_cdns *cdns = bus_to_cdns(bus);
1448b3ad31f3SPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1449b3ad31f3SPierre-Louis Bossart 
1450b3ad31f3SPierre-Louis Bossart 	return sdw->link_res->hw_ops->pre_bank_switch(sdw);
1451b3ad31f3SPierre-Louis Bossart }
1452b3ad31f3SPierre-Louis Bossart 
1453b3ad31f3SPierre-Louis Bossart static int generic_post_bank_switch(struct sdw_bus *bus)
1454b3ad31f3SPierre-Louis Bossart {
1455b3ad31f3SPierre-Louis Bossart 	struct sdw_cdns *cdns = bus_to_cdns(bus);
1456b3ad31f3SPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1457b3ad31f3SPierre-Louis Bossart 
1458b3ad31f3SPierre-Louis Bossart 	return sdw->link_res->hw_ops->post_bank_switch(sdw);
1459b3ad31f3SPierre-Louis Bossart }
1460b3ad31f3SPierre-Louis Bossart 
1461085f4aceSPierre-Louis Bossart static int sdw_master_read_intel_prop(struct sdw_bus *bus)
1462085f4aceSPierre-Louis Bossart {
1463085f4aceSPierre-Louis Bossart 	struct sdw_master_prop *prop = &bus->prop;
1464085f4aceSPierre-Louis Bossart 	struct fwnode_handle *link;
1465085f4aceSPierre-Louis Bossart 	char name[32];
1466395713d8SPierre-Louis Bossart 	u32 quirk_mask;
1467085f4aceSPierre-Louis Bossart 
1468085f4aceSPierre-Louis Bossart 	/* Find master handle */
1469085f4aceSPierre-Louis Bossart 	snprintf(name, sizeof(name),
1470085f4aceSPierre-Louis Bossart 		 "mipi-sdw-link-%d-subproperties", bus->link_id);
1471085f4aceSPierre-Louis Bossart 
1472085f4aceSPierre-Louis Bossart 	link = device_get_named_child_node(bus->dev, name);
1473085f4aceSPierre-Louis Bossart 	if (!link) {
1474085f4aceSPierre-Louis Bossart 		dev_err(bus->dev, "Master node %s not found\n", name);
1475085f4aceSPierre-Louis Bossart 		return -EIO;
1476085f4aceSPierre-Louis Bossart 	}
1477085f4aceSPierre-Louis Bossart 
1478085f4aceSPierre-Louis Bossart 	fwnode_property_read_u32(link,
1479085f4aceSPierre-Louis Bossart 				 "intel-sdw-ip-clock",
1480085f4aceSPierre-Louis Bossart 				 &prop->mclk_freq);
1481395713d8SPierre-Louis Bossart 
1482a19efb52SBard Liao 	/* the values reported by BIOS are the 2x clock, not the bus clock */
1483a19efb52SBard Liao 	prop->mclk_freq /= 2;
1484a19efb52SBard Liao 
1485395713d8SPierre-Louis Bossart 	fwnode_property_read_u32(link,
1486395713d8SPierre-Louis Bossart 				 "intel-quirk-mask",
1487395713d8SPierre-Louis Bossart 				 &quirk_mask);
1488395713d8SPierre-Louis Bossart 
1489395713d8SPierre-Louis Bossart 	if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
1490395713d8SPierre-Louis Bossart 		prop->hw_disabled = true;
1491395713d8SPierre-Louis Bossart 
1492bb877bebSBard Liao 	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
1493bb877bebSBard Liao 		SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
1494bb877bebSBard Liao 
1495085f4aceSPierre-Louis Bossart 	return 0;
1496085f4aceSPierre-Louis Bossart }
1497085f4aceSPierre-Louis Bossart 
149871bb8a1bSVinod Koul static int intel_prop_read(struct sdw_bus *bus)
149971bb8a1bSVinod Koul {
150071bb8a1bSVinod Koul 	/* Initialize with default handler to read all DisCo properties */
150171bb8a1bSVinod Koul 	sdw_master_read_prop(bus);
150271bb8a1bSVinod Koul 
1503085f4aceSPierre-Louis Bossart 	/* read Intel-specific properties */
1504085f4aceSPierre-Louis Bossart 	sdw_master_read_intel_prop(bus);
1505085f4aceSPierre-Louis Bossart 
150671bb8a1bSVinod Koul 	return 0;
150771bb8a1bSVinod Koul }
150871bb8a1bSVinod Koul 
1509c91605f4SShreyas NC static struct sdw_master_ops sdw_intel_ops = {
15100b59d4c9SPierre-Louis Bossart 	.read_prop = intel_prop_read,
1511f6594cdfSPierre-Louis Bossart 	.override_adr = sdw_dmi_override_adr,
1512c91605f4SShreyas NC 	.xfer_msg = cdns_xfer_msg,
1513c91605f4SShreyas NC 	.xfer_msg_defer = cdns_xfer_msg_defer,
1514c91605f4SShreyas NC 	.reset_page_addr = cdns_reset_page_addr,
151507abeff1SVinod Koul 	.set_bus_conf = cdns_bus_conf,
1516b3ad31f3SPierre-Louis Bossart 	.pre_bank_switch = generic_pre_bank_switch,
1517b3ad31f3SPierre-Louis Bossart 	.post_bank_switch = generic_post_bank_switch,
1518133547a1SPierre-Louis Bossart 	.read_ping_status = cdns_read_ping_status,
1519c91605f4SShreyas NC };
1520c91605f4SShreyas NC 
152171bb8a1bSVinod Koul /*
152229a269c6SPierre-Louis Bossart  * probe and init (aux_dev_id argument is required by function prototype but not used)
152371bb8a1bSVinod Koul  */
152429a269c6SPierre-Louis Bossart static int intel_link_probe(struct auxiliary_device *auxdev,
152529a269c6SPierre-Louis Bossart 			    const struct auxiliary_device_id *aux_dev_id)
152629a269c6SPierre-Louis Bossart 
152771bb8a1bSVinod Koul {
152829a269c6SPierre-Louis Bossart 	struct device *dev = &auxdev->dev;
152929a269c6SPierre-Louis Bossart 	struct sdw_intel_link_dev *ldev = auxiliary_dev_to_sdw_intel_link_dev(auxdev);
153071bb8a1bSVinod Koul 	struct sdw_intel *sdw;
153183e129afSPierre-Louis Bossart 	struct sdw_cdns *cdns;
1532b6109dd6SPierre-Louis Bossart 	struct sdw_bus *bus;
153371bb8a1bSVinod Koul 	int ret;
153471bb8a1bSVinod Koul 
1535b6109dd6SPierre-Louis Bossart 	sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL);
153671bb8a1bSVinod Koul 	if (!sdw)
153771bb8a1bSVinod Koul 		return -ENOMEM;
153871bb8a1bSVinod Koul 
153983e129afSPierre-Louis Bossart 	cdns = &sdw->cdns;
154083e129afSPierre-Louis Bossart 	bus = &cdns->bus;
154171bb8a1bSVinod Koul 
154229a269c6SPierre-Louis Bossart 	sdw->instance = auxdev->id;
154329a269c6SPierre-Louis Bossart 	sdw->link_res = &ldev->link_res;
154483e129afSPierre-Louis Bossart 	cdns->dev = dev;
154583e129afSPierre-Louis Bossart 	cdns->registers = sdw->link_res->registers;
154683e129afSPierre-Louis Bossart 	cdns->instance = sdw->instance;
154783e129afSPierre-Louis Bossart 	cdns->msg_count = 0;
154883e129afSPierre-Louis Bossart 
154929a269c6SPierre-Louis Bossart 	bus->link_id = auxdev->id;
15501f2dcf3aSPierre-Louis Bossart 	bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN;
155113c30a75SSjoerd Simons 	bus->clk_stop_timeout = 1;
155271bb8a1bSVinod Koul 
155383e129afSPierre-Louis Bossart 	sdw_cdns_probe(cdns);
155471bb8a1bSVinod Koul 
15550b59d4c9SPierre-Louis Bossart 	/* Set ops */
1556b6109dd6SPierre-Louis Bossart 	bus->ops = &sdw_intel_ops;
155771bb8a1bSVinod Koul 
1558b6109dd6SPierre-Louis Bossart 	/* set driver data, accessed by snd_soc_dai_get_drvdata() */
15593edac08eSDavid E. Box 	auxiliary_set_drvdata(auxdev, cdns);
156071bb8a1bSVinod Koul 
15619026118fSBard Liao 	/* use generic bandwidth allocation algorithm */
15629026118fSBard Liao 	sdw->cdns.bus.compute_params = sdw_compute_params;
15639026118fSBard Liao 
15646d9f2dadSPierre-Louis Bossart 	/* avoid resuming from pm_runtime suspend if it's not required */
15656d9f2dadSPierre-Louis Bossart 	dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND);
15666d9f2dadSPierre-Louis Bossart 
1567b6109dd6SPierre-Louis Bossart 	ret = sdw_bus_master_add(bus, dev, dev->fwnode);
156871bb8a1bSVinod Koul 	if (ret) {
1569b6109dd6SPierre-Louis Bossart 		dev_err(dev, "sdw_bus_master_add fail: %d\n", ret);
15709e3d47fbSPierre-Louis Bossart 		return ret;
157171bb8a1bSVinod Koul 	}
157271bb8a1bSVinod Koul 
15736d2c6669SPierre-Louis Bossart 	if (bus->prop.hw_disabled)
1574b6109dd6SPierre-Louis Bossart 		dev_info(dev,
1575b6109dd6SPierre-Louis Bossart 			 "SoundWire master %d is disabled, will be ignored\n",
1576b6109dd6SPierre-Louis Bossart 			 bus->link_id);
15770ef2986eSPierre-Louis Bossart 	/*
15780ef2986eSPierre-Louis Bossart 	 * Ignore BIOS err_threshold, it's a really bad idea when dealing
15790ef2986eSPierre-Louis Bossart 	 * with multiple hardware synchronized links
15800ef2986eSPierre-Louis Bossart 	 */
15810ef2986eSPierre-Louis Bossart 	bus->prop.err_threshold = 0;
15826d2c6669SPierre-Louis Bossart 
15836d2c6669SPierre-Louis Bossart 	return 0;
15846d2c6669SPierre-Louis Bossart }
15856d2c6669SPierre-Louis Bossart 
158629a269c6SPierre-Louis Bossart int intel_link_startup(struct auxiliary_device *auxdev)
15876d2c6669SPierre-Louis Bossart {
158829a269c6SPierre-Louis Bossart 	struct device *dev = &auxdev->dev;
15893edac08eSDavid E. Box 	struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev);
15906d2c6669SPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
15916d2c6669SPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1592ebf878edSPierre-Louis Bossart 	int link_flags;
1593857a7c42SPierre-Louis Bossart 	bool multi_link;
1594caf68819SPierre-Louis Bossart 	u32 clock_stop_quirks;
15956d2c6669SPierre-Louis Bossart 	int ret;
15966d2c6669SPierre-Louis Bossart 
15976d2c6669SPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
15986d2c6669SPierre-Louis Bossart 		dev_info(dev,
15996d2c6669SPierre-Louis Bossart 			 "SoundWire master %d is disabled, ignoring\n",
16006d2c6669SPierre-Louis Bossart 			 sdw->instance);
1601395713d8SPierre-Louis Bossart 		return 0;
1602395713d8SPierre-Louis Bossart 	}
1603395713d8SPierre-Louis Bossart 
1604857a7c42SPierre-Louis Bossart 	link_flags = md_flags >> (bus->link_id * 8);
1605857a7c42SPierre-Louis Bossart 	multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
1606857a7c42SPierre-Louis Bossart 	if (!multi_link) {
1607857a7c42SPierre-Louis Bossart 		dev_dbg(dev, "Multi-link is disabled\n");
1608857a7c42SPierre-Louis Bossart 	} else {
160994eed661SPierre-Louis Bossart 		/*
161094eed661SPierre-Louis Bossart 		 * hardware-based synchronization is required regardless
161194eed661SPierre-Louis Bossart 		 * of the number of segments used by a stream: SSP-based
161294eed661SPierre-Louis Bossart 		 * synchronization is gated by gsync when the multi-master
161394eed661SPierre-Louis Bossart 		 * mode is set.
161494eed661SPierre-Louis Bossart 		 */
161594eed661SPierre-Louis Bossart 		bus->hw_sync_min_links = 1;
1616857a7c42SPierre-Louis Bossart 	}
16178d875da7SPierre-Louis Bossart 	bus->multi_link = multi_link;
1618857a7c42SPierre-Louis Bossart 
1619857a7c42SPierre-Louis Bossart 	/* Initialize shim, controller */
1620*49c9ff45SPierre-Louis Bossart 	ret = sdw_intel_link_power_up(sdw);
162171bb8a1bSVinod Koul 	if (ret)
162271bb8a1bSVinod Koul 		goto err_init;
162371bb8a1bSVinod Koul 
1624c46302ecSVinod Koul 	/* Register DAIs */
1625b6234bccSPierre-Louis Bossart 	ret = sdw_intel_register_dai(sdw);
1626c46302ecSVinod Koul 	if (ret) {
1627b6109dd6SPierre-Louis Bossart 		dev_err(dev, "DAI registration failed: %d\n", ret);
16288d875da7SPierre-Louis Bossart 		goto err_power_up;
1629c46302ecSVinod Koul 	}
1630c46302ecSVinod Koul 
1631fb2dc6a0SPierre-Louis Bossart 	sdw_intel_debugfs_init(sdw);
163279ee6631SPierre-Louis Bossart 
16338d875da7SPierre-Louis Bossart 	/* start bus */
16343db0c5a6SPierre-Louis Bossart 	ret = sdw_intel_start_bus(sdw);
16358d875da7SPierre-Louis Bossart 	if (ret) {
16368d875da7SPierre-Louis Bossart 		dev_err(dev, "bus start failed: %d\n", ret);
16378d875da7SPierre-Louis Bossart 		goto err_power_up;
163871bb8a1bSVinod Koul 	}
163971bb8a1bSVinod Koul 
1640ebf878edSPierre-Louis Bossart 	/* Enable runtime PM */
1641ebf878edSPierre-Louis Bossart 	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) {
1642ebf878edSPierre-Louis Bossart 		pm_runtime_set_autosuspend_delay(dev,
1643ebf878edSPierre-Louis Bossart 						 INTEL_MASTER_SUSPEND_DELAY_MS);
1644ebf878edSPierre-Louis Bossart 		pm_runtime_use_autosuspend(dev);
1645ebf878edSPierre-Louis Bossart 		pm_runtime_mark_last_busy(dev);
1646ebf878edSPierre-Louis Bossart 
1647ebf878edSPierre-Louis Bossart 		pm_runtime_set_active(dev);
1648ebf878edSPierre-Louis Bossart 		pm_runtime_enable(dev);
1649ebf878edSPierre-Louis Bossart 	}
1650ebf878edSPierre-Louis Bossart 
1651caf68819SPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1652caf68819SPierre-Louis Bossart 	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_NOT_ALLOWED) {
1653caf68819SPierre-Louis Bossart 		/*
1654caf68819SPierre-Louis Bossart 		 * To keep the clock running we need to prevent
1655caf68819SPierre-Louis Bossart 		 * pm_runtime suspend from happening by increasing the
1656caf68819SPierre-Louis Bossart 		 * reference count.
1657caf68819SPierre-Louis Bossart 		 * This quirk is specified by the parent PCI device in
1658caf68819SPierre-Louis Bossart 		 * case of specific latency requirements. It will have
1659caf68819SPierre-Louis Bossart 		 * no effect if pm_runtime is disabled by the user via
1660caf68819SPierre-Louis Bossart 		 * a module parameter for testing purposes.
1661caf68819SPierre-Louis Bossart 		 */
1662caf68819SPierre-Louis Bossart 		pm_runtime_get_noresume(dev);
1663caf68819SPierre-Louis Bossart 	}
1664caf68819SPierre-Louis Bossart 
1665a2d9c161SPierre-Louis Bossart 	/*
1666a2d9c161SPierre-Louis Bossart 	 * The runtime PM status of Slave devices is "Unsupported"
1667a2d9c161SPierre-Louis Bossart 	 * until they report as ATTACHED. If they don't, e.g. because
1668a2d9c161SPierre-Louis Bossart 	 * there are no Slave devices populated or if the power-on is
1669a2d9c161SPierre-Louis Bossart 	 * delayed or dependent on a power switch, the Master will
1670a2d9c161SPierre-Louis Bossart 	 * remain active and prevent its parent from suspending.
1671a2d9c161SPierre-Louis Bossart 	 *
1672a2d9c161SPierre-Louis Bossart 	 * Conditionally force the pm_runtime core to re-evaluate the
1673a2d9c161SPierre-Louis Bossart 	 * Master status in the absence of any Slave activity. A quirk
1674a2d9c161SPierre-Louis Bossart 	 * is provided to e.g. deal with Slaves that may be powered on
1675a2d9c161SPierre-Louis Bossart 	 * with a delay. A more complete solution would require the
1676a2d9c161SPierre-Louis Bossart 	 * definition of Master properties.
1677a2d9c161SPierre-Louis Bossart 	 */
1678a2d9c161SPierre-Louis Bossart 	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
1679a2d9c161SPierre-Louis Bossart 		pm_runtime_idle(dev);
1680a2d9c161SPierre-Louis Bossart 
1681e4401abbSPierre-Louis Bossart 	sdw->startup_done = true;
168271bb8a1bSVinod Koul 	return 0;
168371bb8a1bSVinod Koul 
16848d875da7SPierre-Louis Bossart err_power_up:
1685*49c9ff45SPierre-Louis Bossart 	sdw_intel_link_power_down(sdw);
168671bb8a1bSVinod Koul err_init:
168771bb8a1bSVinod Koul 	return ret;
168871bb8a1bSVinod Koul }
168971bb8a1bSVinod Koul 
169029a269c6SPierre-Louis Bossart static void intel_link_remove(struct auxiliary_device *auxdev)
169171bb8a1bSVinod Koul {
16923edac08eSDavid E. Box 	struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev);
169383e129afSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
169483e129afSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1695b6109dd6SPierre-Louis Bossart 
1696caf68819SPierre-Louis Bossart 	/*
1697caf68819SPierre-Louis Bossart 	 * Since pm_runtime is already disabled, we don't decrease
1698caf68819SPierre-Louis Bossart 	 * the refcount when the clock_stop_quirk is
1699caf68819SPierre-Louis Bossart 	 * SDW_INTEL_CLK_STOP_NOT_ALLOWED
1700caf68819SPierre-Louis Bossart 	 */
1701b6109dd6SPierre-Louis Bossart 	if (!bus->prop.hw_disabled) {
1702fb2dc6a0SPierre-Louis Bossart 		sdw_intel_debugfs_exit(sdw);
170383e129afSPierre-Louis Bossart 		sdw_cdns_enable_interrupt(cdns, false);
1704395713d8SPierre-Louis Bossart 	}
1705b6109dd6SPierre-Louis Bossart 	sdw_bus_master_delete(bus);
170671bb8a1bSVinod Koul }
170771bb8a1bSVinod Koul 
170829a269c6SPierre-Louis Bossart int intel_link_process_wakeen_event(struct auxiliary_device *auxdev)
1709ab2c9132SRander Wang {
171029a269c6SPierre-Louis Bossart 	struct device *dev = &auxdev->dev;
171171bb8a1bSVinod Koul 	struct sdw_intel *sdw;
1712ab2c9132SRander Wang 	struct sdw_bus *bus;
171371bb8a1bSVinod Koul 
17143edac08eSDavid E. Box 	sdw = auxiliary_get_drvdata(auxdev);
1715ab2c9132SRander Wang 	bus = &sdw->cdns.bus;
171671bb8a1bSVinod Koul 
1717e4401abbSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1718e4401abbSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
1719e4401abbSPierre-Louis Bossart 			bus->link_id);
1720ab2c9132SRander Wang 		return 0;
172171bb8a1bSVinod Koul 	}
1722ab2c9132SRander Wang 
17230f3c54c2SPierre-Louis Bossart 	if (!intel_shim_check_wake(sdw))
1724ab2c9132SRander Wang 		return 0;
1725ab2c9132SRander Wang 
1726ab2c9132SRander Wang 	/* disable WAKEEN interrupt ASAP to prevent interrupt flood */
1727ab2c9132SRander Wang 	intel_shim_wake(sdw, false);
1728ab2c9132SRander Wang 
1729ab2c9132SRander Wang 	/*
1730ab2c9132SRander Wang 	 * resume the Master, which will generate a bus reset and result in
1731ab2c9132SRander Wang 	 * Slaves re-attaching and be re-enumerated. The SoundWire physical
1732ab2c9132SRander Wang 	 * device which generated the wake will trigger an interrupt, which
1733ab2c9132SRander Wang 	 * will in turn cause the corresponding Linux Slave device to be
1734ab2c9132SRander Wang 	 * resumed and the Slave codec driver to check the status.
1735ab2c9132SRander Wang 	 */
1736ab2c9132SRander Wang 	pm_request_resume(dev);
173771bb8a1bSVinod Koul 
173871bb8a1bSVinod Koul 	return 0;
173971bb8a1bSVinod Koul }
174071bb8a1bSVinod Koul 
17419b3b4b3fSPierre-Louis Bossart /*
17429b3b4b3fSPierre-Louis Bossart  * PM calls
17439b3b4b3fSPierre-Louis Bossart  */
17449b3b4b3fSPierre-Louis Bossart 
1745029bfd1cSPierre-Louis Bossart static int intel_resume_child_device(struct device *dev, void *data)
1746029bfd1cSPierre-Louis Bossart {
1747029bfd1cSPierre-Louis Bossart 	int ret;
1748029bfd1cSPierre-Louis Bossart 	struct sdw_slave *slave = dev_to_sdw_dev(dev);
1749029bfd1cSPierre-Louis Bossart 
1750029bfd1cSPierre-Louis Bossart 	if (!slave->probed) {
175163198aaaSPierre-Louis Bossart 		dev_dbg(dev, "skipping device, no probed driver\n");
1752029bfd1cSPierre-Louis Bossart 		return 0;
1753029bfd1cSPierre-Louis Bossart 	}
1754029bfd1cSPierre-Louis Bossart 	if (!slave->dev_num_sticky) {
175563198aaaSPierre-Louis Bossart 		dev_dbg(dev, "skipping device, never detected on bus\n");
1756029bfd1cSPierre-Louis Bossart 		return 0;
1757029bfd1cSPierre-Louis Bossart 	}
1758029bfd1cSPierre-Louis Bossart 
1759029bfd1cSPierre-Louis Bossart 	ret = pm_request_resume(dev);
1760029bfd1cSPierre-Louis Bossart 	if (ret < 0)
1761029bfd1cSPierre-Louis Bossart 		dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret);
1762029bfd1cSPierre-Louis Bossart 
1763029bfd1cSPierre-Louis Bossart 	return ret;
1764029bfd1cSPierre-Louis Bossart }
1765029bfd1cSPierre-Louis Bossart 
1766029bfd1cSPierre-Louis Bossart static int __maybe_unused intel_pm_prepare(struct device *dev)
1767029bfd1cSPierre-Louis Bossart {
1768029bfd1cSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
1769029bfd1cSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1770029bfd1cSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1771029bfd1cSPierre-Louis Bossart 	u32 clock_stop_quirks;
17729283b6f9SPierre-Louis Bossart 	int ret;
1773029bfd1cSPierre-Louis Bossart 
1774029bfd1cSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1775029bfd1cSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
1776029bfd1cSPierre-Louis Bossart 			bus->link_id);
1777029bfd1cSPierre-Louis Bossart 		return 0;
1778029bfd1cSPierre-Louis Bossart 	}
1779029bfd1cSPierre-Louis Bossart 
1780029bfd1cSPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1781029bfd1cSPierre-Louis Bossart 
1782029bfd1cSPierre-Louis Bossart 	if (pm_runtime_suspended(dev) &&
1783029bfd1cSPierre-Louis Bossart 	    pm_runtime_suspended(dev->parent) &&
1784029bfd1cSPierre-Louis Bossart 	    ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) ||
1785029bfd1cSPierre-Louis Bossart 	     !clock_stop_quirks)) {
1786029bfd1cSPierre-Louis Bossart 		/*
1787029bfd1cSPierre-Louis Bossart 		 * if we've enabled clock stop, and the parent is suspended, the SHIM registers
1788029bfd1cSPierre-Louis Bossart 		 * are not accessible and the shim wake cannot be disabled.
1789029bfd1cSPierre-Louis Bossart 		 * The only solution is to resume the entire bus to full power
1790029bfd1cSPierre-Louis Bossart 		 */
1791029bfd1cSPierre-Louis Bossart 
1792029bfd1cSPierre-Louis Bossart 		/*
1793029bfd1cSPierre-Louis Bossart 		 * If any operation in this block fails, we keep going since we don't want
1794029bfd1cSPierre-Louis Bossart 		 * to prevent system suspend from happening and errors should be recoverable
1795029bfd1cSPierre-Louis Bossart 		 * on resume.
1796029bfd1cSPierre-Louis Bossart 		 */
1797029bfd1cSPierre-Louis Bossart 
1798029bfd1cSPierre-Louis Bossart 		/*
1799029bfd1cSPierre-Louis Bossart 		 * first resume the device for this link. This will also by construction
1800029bfd1cSPierre-Louis Bossart 		 * resume the PCI parent device.
1801029bfd1cSPierre-Louis Bossart 		 */
1802029bfd1cSPierre-Louis Bossart 		ret = pm_request_resume(dev);
1803029bfd1cSPierre-Louis Bossart 		if (ret < 0) {
1804029bfd1cSPierre-Louis Bossart 			dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret);
1805029bfd1cSPierre-Louis Bossart 			return 0;
1806029bfd1cSPierre-Louis Bossart 		}
1807029bfd1cSPierre-Louis Bossart 
1808029bfd1cSPierre-Louis Bossart 		/*
1809029bfd1cSPierre-Louis Bossart 		 * Continue resuming the entire bus (parent + child devices) to exit
1810029bfd1cSPierre-Louis Bossart 		 * the clock stop mode. If there are no devices connected on this link
1811029bfd1cSPierre-Louis Bossart 		 * this is a no-op.
1812029bfd1cSPierre-Louis Bossart 		 * The resume to full power could have been implemented with a .prepare
1813029bfd1cSPierre-Louis Bossart 		 * step in SoundWire codec drivers. This would however require a lot
1814029bfd1cSPierre-Louis Bossart 		 * of code to handle an Intel-specific corner case. It is simpler in
1815029bfd1cSPierre-Louis Bossart 		 * practice to add a loop at the link level.
1816029bfd1cSPierre-Louis Bossart 		 */
1817029bfd1cSPierre-Louis Bossart 		ret = device_for_each_child(bus->dev, NULL, intel_resume_child_device);
1818029bfd1cSPierre-Louis Bossart 
1819029bfd1cSPierre-Louis Bossart 		if (ret < 0)
1820029bfd1cSPierre-Louis Bossart 			dev_err(dev, "%s: intel_resume_child_device failed: %d\n", __func__, ret);
1821029bfd1cSPierre-Louis Bossart 	}
1822029bfd1cSPierre-Louis Bossart 
1823029bfd1cSPierre-Louis Bossart 	return 0;
1824029bfd1cSPierre-Louis Bossart }
1825029bfd1cSPierre-Louis Bossart 
1826f046b233SBard Liao static int __maybe_unused intel_suspend(struct device *dev)
18279b3b4b3fSPierre-Louis Bossart {
18289b3b4b3fSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
18299b3b4b3fSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
18309b3b4b3fSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1831e4be9facSPierre-Louis Bossart 	u32 clock_stop_quirks;
18329b3b4b3fSPierre-Louis Bossart 	int ret;
18339b3b4b3fSPierre-Louis Bossart 
1834e4401abbSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1835e4401abbSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
18369b3b4b3fSPierre-Louis Bossart 			bus->link_id);
18379b3b4b3fSPierre-Louis Bossart 		return 0;
18389b3b4b3fSPierre-Louis Bossart 	}
18399b3b4b3fSPierre-Louis Bossart 
1840b61b8b37SPierre-Louis Bossart 	if (pm_runtime_suspended(dev)) {
184163198aaaSPierre-Louis Bossart 		dev_dbg(dev, "pm_runtime status: suspended\n");
1842b61b8b37SPierre-Louis Bossart 
1843e4be9facSPierre-Louis Bossart 		clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1844e4be9facSPierre-Louis Bossart 
1845029bfd1cSPierre-Louis Bossart 		if ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) ||
1846029bfd1cSPierre-Louis Bossart 		    !clock_stop_quirks) {
1847e4be9facSPierre-Louis Bossart 
1848029bfd1cSPierre-Louis Bossart 			if (pm_runtime_suspended(dev->parent)) {
1849e4be9facSPierre-Louis Bossart 				/*
1850029bfd1cSPierre-Louis Bossart 				 * paranoia check: this should not happen with the .prepare
1851029bfd1cSPierre-Louis Bossart 				 * resume to full power
1852e4be9facSPierre-Louis Bossart 				 */
1853029bfd1cSPierre-Louis Bossart 				dev_err(dev, "%s: invalid config: parent is suspended\n", __func__);
1854029bfd1cSPierre-Louis Bossart 			} else {
1855e4be9facSPierre-Louis Bossart 				intel_shim_wake(sdw, false);
1856e4be9facSPierre-Louis Bossart 			}
1857029bfd1cSPierre-Louis Bossart 		}
1858e4be9facSPierre-Louis Bossart 
1859b61b8b37SPierre-Louis Bossart 		return 0;
1860b61b8b37SPierre-Louis Bossart 	}
1861b61b8b37SPierre-Louis Bossart 
18623db0c5a6SPierre-Louis Bossart 	ret = sdw_intel_stop_bus(sdw, false);
18639b3b4b3fSPierre-Louis Bossart 	if (ret < 0) {
1864503ae285SPierre-Louis Bossart 		dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret);
18659b3b4b3fSPierre-Louis Bossart 		return ret;
18669b3b4b3fSPierre-Louis Bossart 	}
18679b3b4b3fSPierre-Louis Bossart 
18689b3b4b3fSPierre-Louis Bossart 	return 0;
18699b3b4b3fSPierre-Louis Bossart }
18709b3b4b3fSPierre-Louis Bossart 
187117e0da0bSArnd Bergmann static int __maybe_unused intel_suspend_runtime(struct device *dev)
1872ebf878edSPierre-Louis Bossart {
1873ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
1874ebf878edSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1875ebf878edSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1876a320f41eSPierre-Louis Bossart 	u32 clock_stop_quirks;
1877ebf878edSPierre-Louis Bossart 	int ret;
1878ebf878edSPierre-Louis Bossart 
1879e4401abbSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1880e4401abbSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
1881ebf878edSPierre-Louis Bossart 			bus->link_id);
1882ebf878edSPierre-Louis Bossart 		return 0;
1883ebf878edSPierre-Louis Bossart 	}
1884ebf878edSPierre-Louis Bossart 
1885a320f41eSPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1886a320f41eSPierre-Louis Bossart 
1887a320f41eSPierre-Louis Bossart 	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
18883db0c5a6SPierre-Louis Bossart 		ret = sdw_intel_stop_bus(sdw, false);
1889ebf878edSPierre-Louis Bossart 		if (ret < 0) {
1890503ae285SPierre-Louis Bossart 			dev_err(dev, "%s: cannot stop bus during teardown: %d\n",
1891503ae285SPierre-Louis Bossart 				__func__, ret);
1892ebf878edSPierre-Louis Bossart 			return ret;
1893ebf878edSPierre-Louis Bossart 		}
1894503ae285SPierre-Louis Bossart 	} else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || !clock_stop_quirks) {
18953db0c5a6SPierre-Louis Bossart 		ret = sdw_intel_stop_bus(sdw, true);
18966626a616SRander Wang 		if (ret < 0) {
1897503ae285SPierre-Louis Bossart 			dev_err(dev, "%s: cannot stop bus during clock_stop: %d\n",
1898503ae285SPierre-Louis Bossart 				__func__, ret);
18996626a616SRander Wang 			return ret;
19006626a616SRander Wang 		}
1901a320f41eSPierre-Louis Bossart 	} else {
1902a320f41eSPierre-Louis Bossart 		dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
1903a320f41eSPierre-Louis Bossart 			__func__, clock_stop_quirks);
1904a320f41eSPierre-Louis Bossart 		ret = -EINVAL;
1905a320f41eSPierre-Louis Bossart 	}
1906a320f41eSPierre-Louis Bossart 
1907a320f41eSPierre-Louis Bossart 	return ret;
1908ebf878edSPierre-Louis Bossart }
1909ebf878edSPierre-Louis Bossart 
1910f046b233SBard Liao static int __maybe_unused intel_resume(struct device *dev)
19119b3b4b3fSPierre-Louis Bossart {
19129b3b4b3fSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
19139b3b4b3fSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
19149b3b4b3fSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1915a2d9c161SPierre-Louis Bossart 	int link_flags;
19169b3b4b3fSPierre-Louis Bossart 	int ret;
19179b3b4b3fSPierre-Louis Bossart 
1918e4401abbSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1919e4401abbSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
19209b3b4b3fSPierre-Louis Bossart 			bus->link_id);
19219b3b4b3fSPierre-Louis Bossart 		return 0;
19229b3b4b3fSPierre-Louis Bossart 	}
19239b3b4b3fSPierre-Louis Bossart 
1924857a7c42SPierre-Louis Bossart 	link_flags = md_flags >> (bus->link_id * 8);
1925857a7c42SPierre-Louis Bossart 
1926b61b8b37SPierre-Louis Bossart 	if (pm_runtime_suspended(dev)) {
192763198aaaSPierre-Louis Bossart 		dev_dbg(dev, "pm_runtime status was suspended, forcing active\n");
1928b61b8b37SPierre-Louis Bossart 
1929b61b8b37SPierre-Louis Bossart 		/* follow required sequence from runtime_pm.rst */
1930b61b8b37SPierre-Louis Bossart 		pm_runtime_disable(dev);
1931b61b8b37SPierre-Louis Bossart 		pm_runtime_set_active(dev);
1932b61b8b37SPierre-Louis Bossart 		pm_runtime_mark_last_busy(dev);
1933b61b8b37SPierre-Louis Bossart 		pm_runtime_enable(dev);
1934a2d9c161SPierre-Louis Bossart 
1935a2d9c161SPierre-Louis Bossart 		link_flags = md_flags >> (bus->link_id * 8);
1936857a7c42SPierre-Louis Bossart 
1937a2d9c161SPierre-Louis Bossart 		if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
1938a2d9c161SPierre-Louis Bossart 			pm_runtime_idle(dev);
1939b61b8b37SPierre-Louis Bossart 	}
1940b61b8b37SPierre-Louis Bossart 
1941*49c9ff45SPierre-Louis Bossart 	ret = sdw_intel_link_power_up(sdw);
19429b3b4b3fSPierre-Louis Bossart 	if (ret) {
19434e3ea93eSPierre-Louis Bossart 		dev_err(dev, "%s failed: %d\n", __func__, ret);
19449b3b4b3fSPierre-Louis Bossart 		return ret;
19459b3b4b3fSPierre-Louis Bossart 	}
19469b3b4b3fSPierre-Louis Bossart 
194799b6a30fSPierre-Louis Bossart 	/*
194899b6a30fSPierre-Louis Bossart 	 * make sure all Slaves are tagged as UNATTACHED and provide
194999b6a30fSPierre-Louis Bossart 	 * reason for reinitialization
195099b6a30fSPierre-Louis Bossart 	 */
195199b6a30fSPierre-Louis Bossart 	sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
195299b6a30fSPierre-Louis Bossart 
19533db0c5a6SPierre-Louis Bossart 	ret = sdw_intel_start_bus(sdw);
19549b3b4b3fSPierre-Louis Bossart 	if (ret < 0) {
19558d875da7SPierre-Louis Bossart 		dev_err(dev, "cannot start bus during resume\n");
1956*49c9ff45SPierre-Louis Bossart 		sdw_intel_link_power_down(sdw);
19579b3b4b3fSPierre-Louis Bossart 		return ret;
19589b3b4b3fSPierre-Louis Bossart 	}
19599b3b4b3fSPierre-Louis Bossart 
1960857a7c42SPierre-Louis Bossart 	/*
1961cb1e6d59SPierre-Louis Bossart 	 * after system resume, the pm_runtime suspend() may kick in
1962cb1e6d59SPierre-Louis Bossart 	 * during the enumeration, before any children device force the
1963cb1e6d59SPierre-Louis Bossart 	 * master device to remain active.  Using pm_runtime_get()
1964cb1e6d59SPierre-Louis Bossart 	 * routines is not really possible, since it'd prevent the
1965cb1e6d59SPierre-Louis Bossart 	 * master from suspending.
1966cb1e6d59SPierre-Louis Bossart 	 * A reasonable compromise is to update the pm_runtime
1967cb1e6d59SPierre-Louis Bossart 	 * counters and delay the pm_runtime suspend by several
1968cb1e6d59SPierre-Louis Bossart 	 * seconds, by when all enumeration should be complete.
1969cb1e6d59SPierre-Louis Bossart 	 */
1970cb1e6d59SPierre-Louis Bossart 	pm_runtime_mark_last_busy(dev);
1971cb1e6d59SPierre-Louis Bossart 
19728d875da7SPierre-Louis Bossart 	return 0;
19739b3b4b3fSPierre-Louis Bossart }
19749b3b4b3fSPierre-Louis Bossart 
197517e0da0bSArnd Bergmann static int __maybe_unused intel_resume_runtime(struct device *dev)
1976ebf878edSPierre-Louis Bossart {
1977ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
1978ebf878edSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1979ebf878edSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1980a320f41eSPierre-Louis Bossart 	u32 clock_stop_quirks;
1981ebf878edSPierre-Louis Bossart 	int ret;
1982ebf878edSPierre-Louis Bossart 
1983e4401abbSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1984e4401abbSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
1985ebf878edSPierre-Louis Bossart 			bus->link_id);
1986ebf878edSPierre-Louis Bossart 		return 0;
1987ebf878edSPierre-Louis Bossart 	}
1988ebf878edSPierre-Louis Bossart 
1989e286472cSPierre-Louis Bossart 	/* unconditionally disable WAKEEN interrupt */
1990e286472cSPierre-Louis Bossart 	intel_shim_wake(sdw, false);
1991e286472cSPierre-Louis Bossart 
1992a320f41eSPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1993a320f41eSPierre-Louis Bossart 
1994a320f41eSPierre-Louis Bossart 	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
1995*49c9ff45SPierre-Louis Bossart 		ret = sdw_intel_link_power_up(sdw);
1996ebf878edSPierre-Louis Bossart 		if (ret) {
19978d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: power_up failed after teardown: %d\n", __func__, ret);
1998ebf878edSPierre-Louis Bossart 			return ret;
1999ebf878edSPierre-Louis Bossart 		}
2000ebf878edSPierre-Louis Bossart 
200199b6a30fSPierre-Louis Bossart 		/*
200299b6a30fSPierre-Louis Bossart 		 * make sure all Slaves are tagged as UNATTACHED and provide
200399b6a30fSPierre-Louis Bossart 		 * reason for reinitialization
200499b6a30fSPierre-Louis Bossart 		 */
200599b6a30fSPierre-Louis Bossart 		sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
200699b6a30fSPierre-Louis Bossart 
20073db0c5a6SPierre-Louis Bossart 		ret = sdw_intel_start_bus(sdw);
2008ebf878edSPierre-Louis Bossart 		if (ret < 0) {
20098d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: cannot start bus after teardown: %d\n", __func__, ret);
2010*49c9ff45SPierre-Louis Bossart 			sdw_intel_link_power_down(sdw);
2011ebf878edSPierre-Louis Bossart 			return ret;
2012ebf878edSPierre-Louis Bossart 		}
2013ebf878edSPierre-Louis Bossart 
2014ff560946SPierre-Louis Bossart 
20156626a616SRander Wang 	} else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) {
2016*49c9ff45SPierre-Louis Bossart 		ret = sdw_intel_link_power_up(sdw);
20176626a616SRander Wang 		if (ret) {
20188d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: power_up failed after bus reset: %d\n", __func__, ret);
20196626a616SRander Wang 			return ret;
20206626a616SRander Wang 		}
20216626a616SRander Wang 
20223db0c5a6SPierre-Louis Bossart 		ret = sdw_intel_start_bus_after_reset(sdw);
20236626a616SRander Wang 		if (ret < 0) {
20248d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: cannot start bus after reset: %d\n", __func__, ret);
2025*49c9ff45SPierre-Louis Bossart 			sdw_intel_link_power_down(sdw);
20266626a616SRander Wang 			return ret;
20276626a616SRander Wang 		}
202861fb830bSPierre-Louis Bossart 	} else if (!clock_stop_quirks) {
2029f748f34eSPierre-Louis Bossart 
20303db0c5a6SPierre-Louis Bossart 		sdw_intel_check_clock_stop(sdw);
2031f748f34eSPierre-Louis Bossart 
2032*49c9ff45SPierre-Louis Bossart 		ret = sdw_intel_link_power_up(sdw);
203361fb830bSPierre-Louis Bossart 		if (ret) {
20348d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: power_up failed: %d\n", __func__, ret);
203561fb830bSPierre-Louis Bossart 			return ret;
203661fb830bSPierre-Louis Bossart 		}
203761fb830bSPierre-Louis Bossart 
20383db0c5a6SPierre-Louis Bossart 		ret = sdw_intel_start_bus_after_clock_stop(sdw);
203961fb830bSPierre-Louis Bossart 		if (ret < 0) {
20408d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: cannot start bus after clock stop: %d\n", __func__, ret);
2041*49c9ff45SPierre-Louis Bossart 			sdw_intel_link_power_down(sdw);
204261fb830bSPierre-Louis Bossart 			return ret;
204361fb830bSPierre-Louis Bossart 		}
2044a320f41eSPierre-Louis Bossart 	} else {
20458d875da7SPierre-Louis Bossart 		dev_err(dev, "%s: clock_stop_quirks %x unsupported\n",
2046a320f41eSPierre-Louis Bossart 			__func__, clock_stop_quirks);
2047a320f41eSPierre-Louis Bossart 		ret = -EINVAL;
2048a320f41eSPierre-Louis Bossart 	}
2049ebf878edSPierre-Louis Bossart 
2050ebf878edSPierre-Louis Bossart 	return ret;
2051ebf878edSPierre-Louis Bossart }
2052ebf878edSPierre-Louis Bossart 
20539b3b4b3fSPierre-Louis Bossart static const struct dev_pm_ops intel_pm = {
2054029bfd1cSPierre-Louis Bossart 	.prepare = intel_pm_prepare,
20559b3b4b3fSPierre-Louis Bossart 	SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume)
2056ebf878edSPierre-Louis Bossart 	SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL)
20579b3b4b3fSPierre-Louis Bossart };
20589b3b4b3fSPierre-Louis Bossart 
205929a269c6SPierre-Louis Bossart static const struct auxiliary_device_id intel_link_id_table[] = {
206029a269c6SPierre-Louis Bossart 	{ .name = "soundwire_intel.link" },
206129a269c6SPierre-Louis Bossart 	{},
206271bb8a1bSVinod Koul };
206329a269c6SPierre-Louis Bossart MODULE_DEVICE_TABLE(auxiliary, intel_link_id_table);
206471bb8a1bSVinod Koul 
206529a269c6SPierre-Louis Bossart static struct auxiliary_driver sdw_intel_drv = {
206629a269c6SPierre-Louis Bossart 	.probe = intel_link_probe,
206729a269c6SPierre-Louis Bossart 	.remove = intel_link_remove,
206829a269c6SPierre-Louis Bossart 	.driver = {
206929a269c6SPierre-Louis Bossart 		/* auxiliary_driver_register() sets .name to be the modname */
207029a269c6SPierre-Louis Bossart 		.pm = &intel_pm,
207129a269c6SPierre-Louis Bossart 	},
207229a269c6SPierre-Louis Bossart 	.id_table = intel_link_id_table
207329a269c6SPierre-Louis Bossart };
207429a269c6SPierre-Louis Bossart module_auxiliary_driver(sdw_intel_drv);
207571bb8a1bSVinod Koul 
207671bb8a1bSVinod Koul MODULE_LICENSE("Dual BSD/GPL");
207729a269c6SPierre-Louis Bossart MODULE_DESCRIPTION("Intel Soundwire Link Driver");
2078