xref: /linux/drivers/soundwire/intel.c (revision 3db0c5a6a2832c7b4b40676299e4bbbe1a96bc8b)
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 
1431*3db0c5a6SPierre-Louis Bossart 	.check_clock_stop = intel_check_clock_stop,
1432*3db0c5a6SPierre-Louis Bossart 	.start_bus = intel_start_bus,
1433*3db0c5a6SPierre-Louis Bossart 	.start_bus_after_reset = intel_start_bus_after_reset,
1434*3db0c5a6SPierre-Louis Bossart 	.start_bus_after_clock_stop = intel_start_bus_after_clock_stop,
1435*3db0c5a6SPierre-Louis Bossart 	.stop_bus = intel_stop_bus,
1436*3db0c5a6SPierre-Louis Bossart 
1437b3ad31f3SPierre-Louis Bossart 	.pre_bank_switch = intel_pre_bank_switch,
1438b3ad31f3SPierre-Louis Bossart 	.post_bank_switch = intel_post_bank_switch,
1439b3ad31f3SPierre-Louis Bossart };
1440b3ad31f3SPierre-Louis Bossart EXPORT_SYMBOL_NS(sdw_intel_cnl_hw_ops, SOUNDWIRE_INTEL);
1441b3ad31f3SPierre-Louis Bossart 
1442b3ad31f3SPierre-Louis Bossart static int generic_pre_bank_switch(struct sdw_bus *bus)
1443b3ad31f3SPierre-Louis Bossart {
1444b3ad31f3SPierre-Louis Bossart 	struct sdw_cdns *cdns = bus_to_cdns(bus);
1445b3ad31f3SPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1446b3ad31f3SPierre-Louis Bossart 
1447b3ad31f3SPierre-Louis Bossart 	return sdw->link_res->hw_ops->pre_bank_switch(sdw);
1448b3ad31f3SPierre-Louis Bossart }
1449b3ad31f3SPierre-Louis Bossart 
1450b3ad31f3SPierre-Louis Bossart static int generic_post_bank_switch(struct sdw_bus *bus)
1451b3ad31f3SPierre-Louis Bossart {
1452b3ad31f3SPierre-Louis Bossart 	struct sdw_cdns *cdns = bus_to_cdns(bus);
1453b3ad31f3SPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1454b3ad31f3SPierre-Louis Bossart 
1455b3ad31f3SPierre-Louis Bossart 	return sdw->link_res->hw_ops->post_bank_switch(sdw);
1456b3ad31f3SPierre-Louis Bossart }
1457b3ad31f3SPierre-Louis Bossart 
1458085f4aceSPierre-Louis Bossart static int sdw_master_read_intel_prop(struct sdw_bus *bus)
1459085f4aceSPierre-Louis Bossart {
1460085f4aceSPierre-Louis Bossart 	struct sdw_master_prop *prop = &bus->prop;
1461085f4aceSPierre-Louis Bossart 	struct fwnode_handle *link;
1462085f4aceSPierre-Louis Bossart 	char name[32];
1463395713d8SPierre-Louis Bossart 	u32 quirk_mask;
1464085f4aceSPierre-Louis Bossart 
1465085f4aceSPierre-Louis Bossart 	/* Find master handle */
1466085f4aceSPierre-Louis Bossart 	snprintf(name, sizeof(name),
1467085f4aceSPierre-Louis Bossart 		 "mipi-sdw-link-%d-subproperties", bus->link_id);
1468085f4aceSPierre-Louis Bossart 
1469085f4aceSPierre-Louis Bossart 	link = device_get_named_child_node(bus->dev, name);
1470085f4aceSPierre-Louis Bossart 	if (!link) {
1471085f4aceSPierre-Louis Bossart 		dev_err(bus->dev, "Master node %s not found\n", name);
1472085f4aceSPierre-Louis Bossart 		return -EIO;
1473085f4aceSPierre-Louis Bossart 	}
1474085f4aceSPierre-Louis Bossart 
1475085f4aceSPierre-Louis Bossart 	fwnode_property_read_u32(link,
1476085f4aceSPierre-Louis Bossart 				 "intel-sdw-ip-clock",
1477085f4aceSPierre-Louis Bossart 				 &prop->mclk_freq);
1478395713d8SPierre-Louis Bossart 
1479a19efb52SBard Liao 	/* the values reported by BIOS are the 2x clock, not the bus clock */
1480a19efb52SBard Liao 	prop->mclk_freq /= 2;
1481a19efb52SBard Liao 
1482395713d8SPierre-Louis Bossart 	fwnode_property_read_u32(link,
1483395713d8SPierre-Louis Bossart 				 "intel-quirk-mask",
1484395713d8SPierre-Louis Bossart 				 &quirk_mask);
1485395713d8SPierre-Louis Bossart 
1486395713d8SPierre-Louis Bossart 	if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
1487395713d8SPierre-Louis Bossart 		prop->hw_disabled = true;
1488395713d8SPierre-Louis Bossart 
1489bb877bebSBard Liao 	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
1490bb877bebSBard Liao 		SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
1491bb877bebSBard Liao 
1492085f4aceSPierre-Louis Bossart 	return 0;
1493085f4aceSPierre-Louis Bossart }
1494085f4aceSPierre-Louis Bossart 
149571bb8a1bSVinod Koul static int intel_prop_read(struct sdw_bus *bus)
149671bb8a1bSVinod Koul {
149771bb8a1bSVinod Koul 	/* Initialize with default handler to read all DisCo properties */
149871bb8a1bSVinod Koul 	sdw_master_read_prop(bus);
149971bb8a1bSVinod Koul 
1500085f4aceSPierre-Louis Bossart 	/* read Intel-specific properties */
1501085f4aceSPierre-Louis Bossart 	sdw_master_read_intel_prop(bus);
1502085f4aceSPierre-Louis Bossart 
150371bb8a1bSVinod Koul 	return 0;
150471bb8a1bSVinod Koul }
150571bb8a1bSVinod Koul 
1506c91605f4SShreyas NC static struct sdw_master_ops sdw_intel_ops = {
15070b59d4c9SPierre-Louis Bossart 	.read_prop = intel_prop_read,
1508f6594cdfSPierre-Louis Bossart 	.override_adr = sdw_dmi_override_adr,
1509c91605f4SShreyas NC 	.xfer_msg = cdns_xfer_msg,
1510c91605f4SShreyas NC 	.xfer_msg_defer = cdns_xfer_msg_defer,
1511c91605f4SShreyas NC 	.reset_page_addr = cdns_reset_page_addr,
151207abeff1SVinod Koul 	.set_bus_conf = cdns_bus_conf,
1513b3ad31f3SPierre-Louis Bossart 	.pre_bank_switch = generic_pre_bank_switch,
1514b3ad31f3SPierre-Louis Bossart 	.post_bank_switch = generic_post_bank_switch,
1515133547a1SPierre-Louis Bossart 	.read_ping_status = cdns_read_ping_status,
1516c91605f4SShreyas NC };
1517c91605f4SShreyas NC 
151871bb8a1bSVinod Koul /*
151929a269c6SPierre-Louis Bossart  * probe and init (aux_dev_id argument is required by function prototype but not used)
152071bb8a1bSVinod Koul  */
152129a269c6SPierre-Louis Bossart static int intel_link_probe(struct auxiliary_device *auxdev,
152229a269c6SPierre-Louis Bossart 			    const struct auxiliary_device_id *aux_dev_id)
152329a269c6SPierre-Louis Bossart 
152471bb8a1bSVinod Koul {
152529a269c6SPierre-Louis Bossart 	struct device *dev = &auxdev->dev;
152629a269c6SPierre-Louis Bossart 	struct sdw_intel_link_dev *ldev = auxiliary_dev_to_sdw_intel_link_dev(auxdev);
152771bb8a1bSVinod Koul 	struct sdw_intel *sdw;
152883e129afSPierre-Louis Bossart 	struct sdw_cdns *cdns;
1529b6109dd6SPierre-Louis Bossart 	struct sdw_bus *bus;
153071bb8a1bSVinod Koul 	int ret;
153171bb8a1bSVinod Koul 
1532b6109dd6SPierre-Louis Bossart 	sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL);
153371bb8a1bSVinod Koul 	if (!sdw)
153471bb8a1bSVinod Koul 		return -ENOMEM;
153571bb8a1bSVinod Koul 
153683e129afSPierre-Louis Bossart 	cdns = &sdw->cdns;
153783e129afSPierre-Louis Bossart 	bus = &cdns->bus;
153871bb8a1bSVinod Koul 
153929a269c6SPierre-Louis Bossart 	sdw->instance = auxdev->id;
154029a269c6SPierre-Louis Bossart 	sdw->link_res = &ldev->link_res;
154183e129afSPierre-Louis Bossart 	cdns->dev = dev;
154283e129afSPierre-Louis Bossart 	cdns->registers = sdw->link_res->registers;
154383e129afSPierre-Louis Bossart 	cdns->instance = sdw->instance;
154483e129afSPierre-Louis Bossart 	cdns->msg_count = 0;
154583e129afSPierre-Louis Bossart 
154629a269c6SPierre-Louis Bossart 	bus->link_id = auxdev->id;
15471f2dcf3aSPierre-Louis Bossart 	bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN;
154813c30a75SSjoerd Simons 	bus->clk_stop_timeout = 1;
154971bb8a1bSVinod Koul 
155083e129afSPierre-Louis Bossart 	sdw_cdns_probe(cdns);
155171bb8a1bSVinod Koul 
15520b59d4c9SPierre-Louis Bossart 	/* Set ops */
1553b6109dd6SPierre-Louis Bossart 	bus->ops = &sdw_intel_ops;
155471bb8a1bSVinod Koul 
1555b6109dd6SPierre-Louis Bossart 	/* set driver data, accessed by snd_soc_dai_get_drvdata() */
15563edac08eSDavid E. Box 	auxiliary_set_drvdata(auxdev, cdns);
155771bb8a1bSVinod Koul 
15589026118fSBard Liao 	/* use generic bandwidth allocation algorithm */
15599026118fSBard Liao 	sdw->cdns.bus.compute_params = sdw_compute_params;
15609026118fSBard Liao 
15616d9f2dadSPierre-Louis Bossart 	/* avoid resuming from pm_runtime suspend if it's not required */
15626d9f2dadSPierre-Louis Bossart 	dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND);
15636d9f2dadSPierre-Louis Bossart 
1564b6109dd6SPierre-Louis Bossart 	ret = sdw_bus_master_add(bus, dev, dev->fwnode);
156571bb8a1bSVinod Koul 	if (ret) {
1566b6109dd6SPierre-Louis Bossart 		dev_err(dev, "sdw_bus_master_add fail: %d\n", ret);
15679e3d47fbSPierre-Louis Bossart 		return ret;
156871bb8a1bSVinod Koul 	}
156971bb8a1bSVinod Koul 
15706d2c6669SPierre-Louis Bossart 	if (bus->prop.hw_disabled)
1571b6109dd6SPierre-Louis Bossart 		dev_info(dev,
1572b6109dd6SPierre-Louis Bossart 			 "SoundWire master %d is disabled, will be ignored\n",
1573b6109dd6SPierre-Louis Bossart 			 bus->link_id);
15740ef2986eSPierre-Louis Bossart 	/*
15750ef2986eSPierre-Louis Bossart 	 * Ignore BIOS err_threshold, it's a really bad idea when dealing
15760ef2986eSPierre-Louis Bossart 	 * with multiple hardware synchronized links
15770ef2986eSPierre-Louis Bossart 	 */
15780ef2986eSPierre-Louis Bossart 	bus->prop.err_threshold = 0;
15796d2c6669SPierre-Louis Bossart 
15806d2c6669SPierre-Louis Bossart 	return 0;
15816d2c6669SPierre-Louis Bossart }
15826d2c6669SPierre-Louis Bossart 
158329a269c6SPierre-Louis Bossart int intel_link_startup(struct auxiliary_device *auxdev)
15846d2c6669SPierre-Louis Bossart {
158529a269c6SPierre-Louis Bossart 	struct device *dev = &auxdev->dev;
15863edac08eSDavid E. Box 	struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev);
15876d2c6669SPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
15886d2c6669SPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1589ebf878edSPierre-Louis Bossart 	int link_flags;
1590857a7c42SPierre-Louis Bossart 	bool multi_link;
1591caf68819SPierre-Louis Bossart 	u32 clock_stop_quirks;
15926d2c6669SPierre-Louis Bossart 	int ret;
15936d2c6669SPierre-Louis Bossart 
15946d2c6669SPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
15956d2c6669SPierre-Louis Bossart 		dev_info(dev,
15966d2c6669SPierre-Louis Bossart 			 "SoundWire master %d is disabled, ignoring\n",
15976d2c6669SPierre-Louis Bossart 			 sdw->instance);
1598395713d8SPierre-Louis Bossart 		return 0;
1599395713d8SPierre-Louis Bossart 	}
1600395713d8SPierre-Louis Bossart 
1601857a7c42SPierre-Louis Bossart 	link_flags = md_flags >> (bus->link_id * 8);
1602857a7c42SPierre-Louis Bossart 	multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
1603857a7c42SPierre-Louis Bossart 	if (!multi_link) {
1604857a7c42SPierre-Louis Bossart 		dev_dbg(dev, "Multi-link is disabled\n");
1605857a7c42SPierre-Louis Bossart 	} else {
160694eed661SPierre-Louis Bossart 		/*
160794eed661SPierre-Louis Bossart 		 * hardware-based synchronization is required regardless
160894eed661SPierre-Louis Bossart 		 * of the number of segments used by a stream: SSP-based
160994eed661SPierre-Louis Bossart 		 * synchronization is gated by gsync when the multi-master
161094eed661SPierre-Louis Bossart 		 * mode is set.
161194eed661SPierre-Louis Bossart 		 */
161294eed661SPierre-Louis Bossart 		bus->hw_sync_min_links = 1;
1613857a7c42SPierre-Louis Bossart 	}
16148d875da7SPierre-Louis Bossart 	bus->multi_link = multi_link;
1615857a7c42SPierre-Louis Bossart 
1616857a7c42SPierre-Louis Bossart 	/* Initialize shim, controller */
1617b81bcdb4SPierre-Louis Bossart 	ret = intel_link_power_up(sdw);
161871bb8a1bSVinod Koul 	if (ret)
161971bb8a1bSVinod Koul 		goto err_init;
162071bb8a1bSVinod Koul 
1621c46302ecSVinod Koul 	/* Register DAIs */
1622b6234bccSPierre-Louis Bossart 	ret = sdw_intel_register_dai(sdw);
1623c46302ecSVinod Koul 	if (ret) {
1624b6109dd6SPierre-Louis Bossart 		dev_err(dev, "DAI registration failed: %d\n", ret);
16258d875da7SPierre-Louis Bossart 		goto err_power_up;
1626c46302ecSVinod Koul 	}
1627c46302ecSVinod Koul 
1628fb2dc6a0SPierre-Louis Bossart 	sdw_intel_debugfs_init(sdw);
162979ee6631SPierre-Louis Bossart 
16308d875da7SPierre-Louis Bossart 	/* start bus */
1631*3db0c5a6SPierre-Louis Bossart 	ret = sdw_intel_start_bus(sdw);
16328d875da7SPierre-Louis Bossart 	if (ret) {
16338d875da7SPierre-Louis Bossart 		dev_err(dev, "bus start failed: %d\n", ret);
16348d875da7SPierre-Louis Bossart 		goto err_power_up;
163571bb8a1bSVinod Koul 	}
163671bb8a1bSVinod Koul 
1637ebf878edSPierre-Louis Bossart 	/* Enable runtime PM */
1638ebf878edSPierre-Louis Bossart 	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) {
1639ebf878edSPierre-Louis Bossart 		pm_runtime_set_autosuspend_delay(dev,
1640ebf878edSPierre-Louis Bossart 						 INTEL_MASTER_SUSPEND_DELAY_MS);
1641ebf878edSPierre-Louis Bossart 		pm_runtime_use_autosuspend(dev);
1642ebf878edSPierre-Louis Bossart 		pm_runtime_mark_last_busy(dev);
1643ebf878edSPierre-Louis Bossart 
1644ebf878edSPierre-Louis Bossart 		pm_runtime_set_active(dev);
1645ebf878edSPierre-Louis Bossart 		pm_runtime_enable(dev);
1646ebf878edSPierre-Louis Bossart 	}
1647ebf878edSPierre-Louis Bossart 
1648caf68819SPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1649caf68819SPierre-Louis Bossart 	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_NOT_ALLOWED) {
1650caf68819SPierre-Louis Bossart 		/*
1651caf68819SPierre-Louis Bossart 		 * To keep the clock running we need to prevent
1652caf68819SPierre-Louis Bossart 		 * pm_runtime suspend from happening by increasing the
1653caf68819SPierre-Louis Bossart 		 * reference count.
1654caf68819SPierre-Louis Bossart 		 * This quirk is specified by the parent PCI device in
1655caf68819SPierre-Louis Bossart 		 * case of specific latency requirements. It will have
1656caf68819SPierre-Louis Bossart 		 * no effect if pm_runtime is disabled by the user via
1657caf68819SPierre-Louis Bossart 		 * a module parameter for testing purposes.
1658caf68819SPierre-Louis Bossart 		 */
1659caf68819SPierre-Louis Bossart 		pm_runtime_get_noresume(dev);
1660caf68819SPierre-Louis Bossart 	}
1661caf68819SPierre-Louis Bossart 
1662a2d9c161SPierre-Louis Bossart 	/*
1663a2d9c161SPierre-Louis Bossart 	 * The runtime PM status of Slave devices is "Unsupported"
1664a2d9c161SPierre-Louis Bossart 	 * until they report as ATTACHED. If they don't, e.g. because
1665a2d9c161SPierre-Louis Bossart 	 * there are no Slave devices populated or if the power-on is
1666a2d9c161SPierre-Louis Bossart 	 * delayed or dependent on a power switch, the Master will
1667a2d9c161SPierre-Louis Bossart 	 * remain active and prevent its parent from suspending.
1668a2d9c161SPierre-Louis Bossart 	 *
1669a2d9c161SPierre-Louis Bossart 	 * Conditionally force the pm_runtime core to re-evaluate the
1670a2d9c161SPierre-Louis Bossart 	 * Master status in the absence of any Slave activity. A quirk
1671a2d9c161SPierre-Louis Bossart 	 * is provided to e.g. deal with Slaves that may be powered on
1672a2d9c161SPierre-Louis Bossart 	 * with a delay. A more complete solution would require the
1673a2d9c161SPierre-Louis Bossart 	 * definition of Master properties.
1674a2d9c161SPierre-Louis Bossart 	 */
1675a2d9c161SPierre-Louis Bossart 	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
1676a2d9c161SPierre-Louis Bossart 		pm_runtime_idle(dev);
1677a2d9c161SPierre-Louis Bossart 
1678e4401abbSPierre-Louis Bossart 	sdw->startup_done = true;
167971bb8a1bSVinod Koul 	return 0;
168071bb8a1bSVinod Koul 
16818d875da7SPierre-Louis Bossart err_power_up:
16828d875da7SPierre-Louis Bossart 	intel_link_power_down(sdw);
168371bb8a1bSVinod Koul err_init:
168471bb8a1bSVinod Koul 	return ret;
168571bb8a1bSVinod Koul }
168671bb8a1bSVinod Koul 
168729a269c6SPierre-Louis Bossart static void intel_link_remove(struct auxiliary_device *auxdev)
168871bb8a1bSVinod Koul {
16893edac08eSDavid E. Box 	struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev);
169083e129afSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
169183e129afSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1692b6109dd6SPierre-Louis Bossart 
1693caf68819SPierre-Louis Bossart 	/*
1694caf68819SPierre-Louis Bossart 	 * Since pm_runtime is already disabled, we don't decrease
1695caf68819SPierre-Louis Bossart 	 * the refcount when the clock_stop_quirk is
1696caf68819SPierre-Louis Bossart 	 * SDW_INTEL_CLK_STOP_NOT_ALLOWED
1697caf68819SPierre-Louis Bossart 	 */
1698b6109dd6SPierre-Louis Bossart 	if (!bus->prop.hw_disabled) {
1699fb2dc6a0SPierre-Louis Bossart 		sdw_intel_debugfs_exit(sdw);
170083e129afSPierre-Louis Bossart 		sdw_cdns_enable_interrupt(cdns, false);
1701395713d8SPierre-Louis Bossart 	}
1702b6109dd6SPierre-Louis Bossart 	sdw_bus_master_delete(bus);
170371bb8a1bSVinod Koul }
170471bb8a1bSVinod Koul 
170529a269c6SPierre-Louis Bossart int intel_link_process_wakeen_event(struct auxiliary_device *auxdev)
1706ab2c9132SRander Wang {
170729a269c6SPierre-Louis Bossart 	struct device *dev = &auxdev->dev;
170871bb8a1bSVinod Koul 	struct sdw_intel *sdw;
1709ab2c9132SRander Wang 	struct sdw_bus *bus;
171071bb8a1bSVinod Koul 
17113edac08eSDavid E. Box 	sdw = auxiliary_get_drvdata(auxdev);
1712ab2c9132SRander Wang 	bus = &sdw->cdns.bus;
171371bb8a1bSVinod Koul 
1714e4401abbSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1715e4401abbSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
1716e4401abbSPierre-Louis Bossart 			bus->link_id);
1717ab2c9132SRander Wang 		return 0;
171871bb8a1bSVinod Koul 	}
1719ab2c9132SRander Wang 
17200f3c54c2SPierre-Louis Bossart 	if (!intel_shim_check_wake(sdw))
1721ab2c9132SRander Wang 		return 0;
1722ab2c9132SRander Wang 
1723ab2c9132SRander Wang 	/* disable WAKEEN interrupt ASAP to prevent interrupt flood */
1724ab2c9132SRander Wang 	intel_shim_wake(sdw, false);
1725ab2c9132SRander Wang 
1726ab2c9132SRander Wang 	/*
1727ab2c9132SRander Wang 	 * resume the Master, which will generate a bus reset and result in
1728ab2c9132SRander Wang 	 * Slaves re-attaching and be re-enumerated. The SoundWire physical
1729ab2c9132SRander Wang 	 * device which generated the wake will trigger an interrupt, which
1730ab2c9132SRander Wang 	 * will in turn cause the corresponding Linux Slave device to be
1731ab2c9132SRander Wang 	 * resumed and the Slave codec driver to check the status.
1732ab2c9132SRander Wang 	 */
1733ab2c9132SRander Wang 	pm_request_resume(dev);
173471bb8a1bSVinod Koul 
173571bb8a1bSVinod Koul 	return 0;
173671bb8a1bSVinod Koul }
173771bb8a1bSVinod Koul 
17389b3b4b3fSPierre-Louis Bossart /*
17399b3b4b3fSPierre-Louis Bossart  * PM calls
17409b3b4b3fSPierre-Louis Bossart  */
17419b3b4b3fSPierre-Louis Bossart 
1742029bfd1cSPierre-Louis Bossart static int intel_resume_child_device(struct device *dev, void *data)
1743029bfd1cSPierre-Louis Bossart {
1744029bfd1cSPierre-Louis Bossart 	int ret;
1745029bfd1cSPierre-Louis Bossart 	struct sdw_slave *slave = dev_to_sdw_dev(dev);
1746029bfd1cSPierre-Louis Bossart 
1747029bfd1cSPierre-Louis Bossart 	if (!slave->probed) {
174863198aaaSPierre-Louis Bossart 		dev_dbg(dev, "skipping device, no probed driver\n");
1749029bfd1cSPierre-Louis Bossart 		return 0;
1750029bfd1cSPierre-Louis Bossart 	}
1751029bfd1cSPierre-Louis Bossart 	if (!slave->dev_num_sticky) {
175263198aaaSPierre-Louis Bossart 		dev_dbg(dev, "skipping device, never detected on bus\n");
1753029bfd1cSPierre-Louis Bossart 		return 0;
1754029bfd1cSPierre-Louis Bossart 	}
1755029bfd1cSPierre-Louis Bossart 
1756029bfd1cSPierre-Louis Bossart 	ret = pm_request_resume(dev);
1757029bfd1cSPierre-Louis Bossart 	if (ret < 0)
1758029bfd1cSPierre-Louis Bossart 		dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret);
1759029bfd1cSPierre-Louis Bossart 
1760029bfd1cSPierre-Louis Bossart 	return ret;
1761029bfd1cSPierre-Louis Bossart }
1762029bfd1cSPierre-Louis Bossart 
1763029bfd1cSPierre-Louis Bossart static int __maybe_unused intel_pm_prepare(struct device *dev)
1764029bfd1cSPierre-Louis Bossart {
1765029bfd1cSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
1766029bfd1cSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1767029bfd1cSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1768029bfd1cSPierre-Louis Bossart 	u32 clock_stop_quirks;
17699283b6f9SPierre-Louis Bossart 	int ret;
1770029bfd1cSPierre-Louis Bossart 
1771029bfd1cSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1772029bfd1cSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
1773029bfd1cSPierre-Louis Bossart 			bus->link_id);
1774029bfd1cSPierre-Louis Bossart 		return 0;
1775029bfd1cSPierre-Louis Bossart 	}
1776029bfd1cSPierre-Louis Bossart 
1777029bfd1cSPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1778029bfd1cSPierre-Louis Bossart 
1779029bfd1cSPierre-Louis Bossart 	if (pm_runtime_suspended(dev) &&
1780029bfd1cSPierre-Louis Bossart 	    pm_runtime_suspended(dev->parent) &&
1781029bfd1cSPierre-Louis Bossart 	    ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) ||
1782029bfd1cSPierre-Louis Bossart 	     !clock_stop_quirks)) {
1783029bfd1cSPierre-Louis Bossart 		/*
1784029bfd1cSPierre-Louis Bossart 		 * if we've enabled clock stop, and the parent is suspended, the SHIM registers
1785029bfd1cSPierre-Louis Bossart 		 * are not accessible and the shim wake cannot be disabled.
1786029bfd1cSPierre-Louis Bossart 		 * The only solution is to resume the entire bus to full power
1787029bfd1cSPierre-Louis Bossart 		 */
1788029bfd1cSPierre-Louis Bossart 
1789029bfd1cSPierre-Louis Bossart 		/*
1790029bfd1cSPierre-Louis Bossart 		 * If any operation in this block fails, we keep going since we don't want
1791029bfd1cSPierre-Louis Bossart 		 * to prevent system suspend from happening and errors should be recoverable
1792029bfd1cSPierre-Louis Bossart 		 * on resume.
1793029bfd1cSPierre-Louis Bossart 		 */
1794029bfd1cSPierre-Louis Bossart 
1795029bfd1cSPierre-Louis Bossart 		/*
1796029bfd1cSPierre-Louis Bossart 		 * first resume the device for this link. This will also by construction
1797029bfd1cSPierre-Louis Bossart 		 * resume the PCI parent device.
1798029bfd1cSPierre-Louis Bossart 		 */
1799029bfd1cSPierre-Louis Bossart 		ret = pm_request_resume(dev);
1800029bfd1cSPierre-Louis Bossart 		if (ret < 0) {
1801029bfd1cSPierre-Louis Bossart 			dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret);
1802029bfd1cSPierre-Louis Bossart 			return 0;
1803029bfd1cSPierre-Louis Bossart 		}
1804029bfd1cSPierre-Louis Bossart 
1805029bfd1cSPierre-Louis Bossart 		/*
1806029bfd1cSPierre-Louis Bossart 		 * Continue resuming the entire bus (parent + child devices) to exit
1807029bfd1cSPierre-Louis Bossart 		 * the clock stop mode. If there are no devices connected on this link
1808029bfd1cSPierre-Louis Bossart 		 * this is a no-op.
1809029bfd1cSPierre-Louis Bossart 		 * The resume to full power could have been implemented with a .prepare
1810029bfd1cSPierre-Louis Bossart 		 * step in SoundWire codec drivers. This would however require a lot
1811029bfd1cSPierre-Louis Bossart 		 * of code to handle an Intel-specific corner case. It is simpler in
1812029bfd1cSPierre-Louis Bossart 		 * practice to add a loop at the link level.
1813029bfd1cSPierre-Louis Bossart 		 */
1814029bfd1cSPierre-Louis Bossart 		ret = device_for_each_child(bus->dev, NULL, intel_resume_child_device);
1815029bfd1cSPierre-Louis Bossart 
1816029bfd1cSPierre-Louis Bossart 		if (ret < 0)
1817029bfd1cSPierre-Louis Bossart 			dev_err(dev, "%s: intel_resume_child_device failed: %d\n", __func__, ret);
1818029bfd1cSPierre-Louis Bossart 	}
1819029bfd1cSPierre-Louis Bossart 
1820029bfd1cSPierre-Louis Bossart 	return 0;
1821029bfd1cSPierre-Louis Bossart }
1822029bfd1cSPierre-Louis Bossart 
1823f046b233SBard Liao static int __maybe_unused intel_suspend(struct device *dev)
18249b3b4b3fSPierre-Louis Bossart {
18259b3b4b3fSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
18269b3b4b3fSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
18279b3b4b3fSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1828e4be9facSPierre-Louis Bossart 	u32 clock_stop_quirks;
18299b3b4b3fSPierre-Louis Bossart 	int ret;
18309b3b4b3fSPierre-Louis Bossart 
1831e4401abbSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1832e4401abbSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
18339b3b4b3fSPierre-Louis Bossart 			bus->link_id);
18349b3b4b3fSPierre-Louis Bossart 		return 0;
18359b3b4b3fSPierre-Louis Bossart 	}
18369b3b4b3fSPierre-Louis Bossart 
1837b61b8b37SPierre-Louis Bossart 	if (pm_runtime_suspended(dev)) {
183863198aaaSPierre-Louis Bossart 		dev_dbg(dev, "pm_runtime status: suspended\n");
1839b61b8b37SPierre-Louis Bossart 
1840e4be9facSPierre-Louis Bossart 		clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1841e4be9facSPierre-Louis Bossart 
1842029bfd1cSPierre-Louis Bossart 		if ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) ||
1843029bfd1cSPierre-Louis Bossart 		    !clock_stop_quirks) {
1844e4be9facSPierre-Louis Bossart 
1845029bfd1cSPierre-Louis Bossart 			if (pm_runtime_suspended(dev->parent)) {
1846e4be9facSPierre-Louis Bossart 				/*
1847029bfd1cSPierre-Louis Bossart 				 * paranoia check: this should not happen with the .prepare
1848029bfd1cSPierre-Louis Bossart 				 * resume to full power
1849e4be9facSPierre-Louis Bossart 				 */
1850029bfd1cSPierre-Louis Bossart 				dev_err(dev, "%s: invalid config: parent is suspended\n", __func__);
1851029bfd1cSPierre-Louis Bossart 			} else {
1852e4be9facSPierre-Louis Bossart 				intel_shim_wake(sdw, false);
1853e4be9facSPierre-Louis Bossart 			}
1854029bfd1cSPierre-Louis Bossart 		}
1855e4be9facSPierre-Louis Bossart 
1856b61b8b37SPierre-Louis Bossart 		return 0;
1857b61b8b37SPierre-Louis Bossart 	}
1858b61b8b37SPierre-Louis Bossart 
1859*3db0c5a6SPierre-Louis Bossart 	ret = sdw_intel_stop_bus(sdw, false);
18609b3b4b3fSPierre-Louis Bossart 	if (ret < 0) {
1861503ae285SPierre-Louis Bossart 		dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret);
18629b3b4b3fSPierre-Louis Bossart 		return ret;
18639b3b4b3fSPierre-Louis Bossart 	}
18649b3b4b3fSPierre-Louis Bossart 
18659b3b4b3fSPierre-Louis Bossart 	return 0;
18669b3b4b3fSPierre-Louis Bossart }
18679b3b4b3fSPierre-Louis Bossart 
186817e0da0bSArnd Bergmann static int __maybe_unused intel_suspend_runtime(struct device *dev)
1869ebf878edSPierre-Louis Bossart {
1870ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
1871ebf878edSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1872ebf878edSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1873a320f41eSPierre-Louis Bossart 	u32 clock_stop_quirks;
1874ebf878edSPierre-Louis Bossart 	int ret;
1875ebf878edSPierre-Louis Bossart 
1876e4401abbSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1877e4401abbSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
1878ebf878edSPierre-Louis Bossart 			bus->link_id);
1879ebf878edSPierre-Louis Bossart 		return 0;
1880ebf878edSPierre-Louis Bossart 	}
1881ebf878edSPierre-Louis Bossart 
1882a320f41eSPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1883a320f41eSPierre-Louis Bossart 
1884a320f41eSPierre-Louis Bossart 	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
1885*3db0c5a6SPierre-Louis Bossart 		ret = sdw_intel_stop_bus(sdw, false);
1886ebf878edSPierre-Louis Bossart 		if (ret < 0) {
1887503ae285SPierre-Louis Bossart 			dev_err(dev, "%s: cannot stop bus during teardown: %d\n",
1888503ae285SPierre-Louis Bossart 				__func__, ret);
1889ebf878edSPierre-Louis Bossart 			return ret;
1890ebf878edSPierre-Louis Bossart 		}
1891503ae285SPierre-Louis Bossart 	} else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || !clock_stop_quirks) {
1892*3db0c5a6SPierre-Louis Bossart 		ret = sdw_intel_stop_bus(sdw, true);
18936626a616SRander Wang 		if (ret < 0) {
1894503ae285SPierre-Louis Bossart 			dev_err(dev, "%s: cannot stop bus during clock_stop: %d\n",
1895503ae285SPierre-Louis Bossart 				__func__, ret);
18966626a616SRander Wang 			return ret;
18976626a616SRander Wang 		}
1898a320f41eSPierre-Louis Bossart 	} else {
1899a320f41eSPierre-Louis Bossart 		dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
1900a320f41eSPierre-Louis Bossart 			__func__, clock_stop_quirks);
1901a320f41eSPierre-Louis Bossart 		ret = -EINVAL;
1902a320f41eSPierre-Louis Bossart 	}
1903a320f41eSPierre-Louis Bossart 
1904a320f41eSPierre-Louis Bossart 	return ret;
1905ebf878edSPierre-Louis Bossart }
1906ebf878edSPierre-Louis Bossart 
1907f046b233SBard Liao static int __maybe_unused intel_resume(struct device *dev)
19089b3b4b3fSPierre-Louis Bossart {
19099b3b4b3fSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
19109b3b4b3fSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
19119b3b4b3fSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1912a2d9c161SPierre-Louis Bossart 	int link_flags;
19139b3b4b3fSPierre-Louis Bossart 	int ret;
19149b3b4b3fSPierre-Louis Bossart 
1915e4401abbSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1916e4401abbSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
19179b3b4b3fSPierre-Louis Bossart 			bus->link_id);
19189b3b4b3fSPierre-Louis Bossart 		return 0;
19199b3b4b3fSPierre-Louis Bossart 	}
19209b3b4b3fSPierre-Louis Bossart 
1921857a7c42SPierre-Louis Bossart 	link_flags = md_flags >> (bus->link_id * 8);
1922857a7c42SPierre-Louis Bossart 
1923b61b8b37SPierre-Louis Bossart 	if (pm_runtime_suspended(dev)) {
192463198aaaSPierre-Louis Bossart 		dev_dbg(dev, "pm_runtime status was suspended, forcing active\n");
1925b61b8b37SPierre-Louis Bossart 
1926b61b8b37SPierre-Louis Bossart 		/* follow required sequence from runtime_pm.rst */
1927b61b8b37SPierre-Louis Bossart 		pm_runtime_disable(dev);
1928b61b8b37SPierre-Louis Bossart 		pm_runtime_set_active(dev);
1929b61b8b37SPierre-Louis Bossart 		pm_runtime_mark_last_busy(dev);
1930b61b8b37SPierre-Louis Bossart 		pm_runtime_enable(dev);
1931a2d9c161SPierre-Louis Bossart 
1932a2d9c161SPierre-Louis Bossart 		link_flags = md_flags >> (bus->link_id * 8);
1933857a7c42SPierre-Louis Bossart 
1934a2d9c161SPierre-Louis Bossart 		if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
1935a2d9c161SPierre-Louis Bossart 			pm_runtime_idle(dev);
1936b61b8b37SPierre-Louis Bossart 	}
1937b61b8b37SPierre-Louis Bossart 
1938b81bcdb4SPierre-Louis Bossart 	ret = intel_link_power_up(sdw);
19399b3b4b3fSPierre-Louis Bossart 	if (ret) {
19404e3ea93eSPierre-Louis Bossart 		dev_err(dev, "%s failed: %d\n", __func__, ret);
19419b3b4b3fSPierre-Louis Bossart 		return ret;
19429b3b4b3fSPierre-Louis Bossart 	}
19439b3b4b3fSPierre-Louis Bossart 
194499b6a30fSPierre-Louis Bossart 	/*
194599b6a30fSPierre-Louis Bossart 	 * make sure all Slaves are tagged as UNATTACHED and provide
194699b6a30fSPierre-Louis Bossart 	 * reason for reinitialization
194799b6a30fSPierre-Louis Bossart 	 */
194899b6a30fSPierre-Louis Bossart 	sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
194999b6a30fSPierre-Louis Bossart 
1950*3db0c5a6SPierre-Louis Bossart 	ret = sdw_intel_start_bus(sdw);
19519b3b4b3fSPierre-Louis Bossart 	if (ret < 0) {
19528d875da7SPierre-Louis Bossart 		dev_err(dev, "cannot start bus during resume\n");
19538d875da7SPierre-Louis Bossart 		intel_link_power_down(sdw);
19549b3b4b3fSPierre-Louis Bossart 		return ret;
19559b3b4b3fSPierre-Louis Bossart 	}
19569b3b4b3fSPierre-Louis Bossart 
1957857a7c42SPierre-Louis Bossart 	/*
1958cb1e6d59SPierre-Louis Bossart 	 * after system resume, the pm_runtime suspend() may kick in
1959cb1e6d59SPierre-Louis Bossart 	 * during the enumeration, before any children device force the
1960cb1e6d59SPierre-Louis Bossart 	 * master device to remain active.  Using pm_runtime_get()
1961cb1e6d59SPierre-Louis Bossart 	 * routines is not really possible, since it'd prevent the
1962cb1e6d59SPierre-Louis Bossart 	 * master from suspending.
1963cb1e6d59SPierre-Louis Bossart 	 * A reasonable compromise is to update the pm_runtime
1964cb1e6d59SPierre-Louis Bossart 	 * counters and delay the pm_runtime suspend by several
1965cb1e6d59SPierre-Louis Bossart 	 * seconds, by when all enumeration should be complete.
1966cb1e6d59SPierre-Louis Bossart 	 */
1967cb1e6d59SPierre-Louis Bossart 	pm_runtime_mark_last_busy(dev);
1968cb1e6d59SPierre-Louis Bossart 
19698d875da7SPierre-Louis Bossart 	return 0;
19709b3b4b3fSPierre-Louis Bossart }
19719b3b4b3fSPierre-Louis Bossart 
197217e0da0bSArnd Bergmann static int __maybe_unused intel_resume_runtime(struct device *dev)
1973ebf878edSPierre-Louis Bossart {
1974ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
1975ebf878edSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1976ebf878edSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1977a320f41eSPierre-Louis Bossart 	u32 clock_stop_quirks;
1978ebf878edSPierre-Louis Bossart 	int ret;
1979ebf878edSPierre-Louis Bossart 
1980e4401abbSPierre-Louis Bossart 	if (bus->prop.hw_disabled || !sdw->startup_done) {
1981e4401abbSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
1982ebf878edSPierre-Louis Bossart 			bus->link_id);
1983ebf878edSPierre-Louis Bossart 		return 0;
1984ebf878edSPierre-Louis Bossart 	}
1985ebf878edSPierre-Louis Bossart 
1986e286472cSPierre-Louis Bossart 	/* unconditionally disable WAKEEN interrupt */
1987e286472cSPierre-Louis Bossart 	intel_shim_wake(sdw, false);
1988e286472cSPierre-Louis Bossart 
1989a320f41eSPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1990a320f41eSPierre-Louis Bossart 
1991a320f41eSPierre-Louis Bossart 	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
1992b81bcdb4SPierre-Louis Bossart 		ret = intel_link_power_up(sdw);
1993ebf878edSPierre-Louis Bossart 		if (ret) {
19948d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: power_up failed after teardown: %d\n", __func__, ret);
1995ebf878edSPierre-Louis Bossart 			return ret;
1996ebf878edSPierre-Louis Bossart 		}
1997ebf878edSPierre-Louis Bossart 
199899b6a30fSPierre-Louis Bossart 		/*
199999b6a30fSPierre-Louis Bossart 		 * make sure all Slaves are tagged as UNATTACHED and provide
200099b6a30fSPierre-Louis Bossart 		 * reason for reinitialization
200199b6a30fSPierre-Louis Bossart 		 */
200299b6a30fSPierre-Louis Bossart 		sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
200399b6a30fSPierre-Louis Bossart 
2004*3db0c5a6SPierre-Louis Bossart 		ret = sdw_intel_start_bus(sdw);
2005ebf878edSPierre-Louis Bossart 		if (ret < 0) {
20068d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: cannot start bus after teardown: %d\n", __func__, ret);
20078d875da7SPierre-Louis Bossart 			intel_link_power_down(sdw);
2008ebf878edSPierre-Louis Bossart 			return ret;
2009ebf878edSPierre-Louis Bossart 		}
2010ebf878edSPierre-Louis Bossart 
2011ff560946SPierre-Louis Bossart 
20126626a616SRander Wang 	} else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) {
2013b81bcdb4SPierre-Louis Bossart 		ret = intel_link_power_up(sdw);
20146626a616SRander Wang 		if (ret) {
20158d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: power_up failed after bus reset: %d\n", __func__, ret);
20166626a616SRander Wang 			return ret;
20176626a616SRander Wang 		}
20186626a616SRander Wang 
2019*3db0c5a6SPierre-Louis Bossart 		ret = sdw_intel_start_bus_after_reset(sdw);
20206626a616SRander Wang 		if (ret < 0) {
20218d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: cannot start bus after reset: %d\n", __func__, ret);
20228d875da7SPierre-Louis Bossart 			intel_link_power_down(sdw);
20236626a616SRander Wang 			return ret;
20246626a616SRander Wang 		}
202561fb830bSPierre-Louis Bossart 	} else if (!clock_stop_quirks) {
2026f748f34eSPierre-Louis Bossart 
2027*3db0c5a6SPierre-Louis Bossart 		sdw_intel_check_clock_stop(sdw);
2028f748f34eSPierre-Louis Bossart 
2029b81bcdb4SPierre-Louis Bossart 		ret = intel_link_power_up(sdw);
203061fb830bSPierre-Louis Bossart 		if (ret) {
20318d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: power_up failed: %d\n", __func__, ret);
203261fb830bSPierre-Louis Bossart 			return ret;
203361fb830bSPierre-Louis Bossart 		}
203461fb830bSPierre-Louis Bossart 
2035*3db0c5a6SPierre-Louis Bossart 		ret = sdw_intel_start_bus_after_clock_stop(sdw);
203661fb830bSPierre-Louis Bossart 		if (ret < 0) {
20378d875da7SPierre-Louis Bossart 			dev_err(dev, "%s: cannot start bus after clock stop: %d\n", __func__, ret);
20388d875da7SPierre-Louis Bossart 			intel_link_power_down(sdw);
203961fb830bSPierre-Louis Bossart 			return ret;
204061fb830bSPierre-Louis Bossart 		}
2041a320f41eSPierre-Louis Bossart 	} else {
20428d875da7SPierre-Louis Bossart 		dev_err(dev, "%s: clock_stop_quirks %x unsupported\n",
2043a320f41eSPierre-Louis Bossart 			__func__, clock_stop_quirks);
2044a320f41eSPierre-Louis Bossart 		ret = -EINVAL;
2045a320f41eSPierre-Louis Bossart 	}
2046ebf878edSPierre-Louis Bossart 
2047ebf878edSPierre-Louis Bossart 	return ret;
2048ebf878edSPierre-Louis Bossart }
2049ebf878edSPierre-Louis Bossart 
20509b3b4b3fSPierre-Louis Bossart static const struct dev_pm_ops intel_pm = {
2051029bfd1cSPierre-Louis Bossart 	.prepare = intel_pm_prepare,
20529b3b4b3fSPierre-Louis Bossart 	SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume)
2053ebf878edSPierre-Louis Bossart 	SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL)
20549b3b4b3fSPierre-Louis Bossart };
20559b3b4b3fSPierre-Louis Bossart 
205629a269c6SPierre-Louis Bossart static const struct auxiliary_device_id intel_link_id_table[] = {
205729a269c6SPierre-Louis Bossart 	{ .name = "soundwire_intel.link" },
205829a269c6SPierre-Louis Bossart 	{},
205971bb8a1bSVinod Koul };
206029a269c6SPierre-Louis Bossart MODULE_DEVICE_TABLE(auxiliary, intel_link_id_table);
206171bb8a1bSVinod Koul 
206229a269c6SPierre-Louis Bossart static struct auxiliary_driver sdw_intel_drv = {
206329a269c6SPierre-Louis Bossart 	.probe = intel_link_probe,
206429a269c6SPierre-Louis Bossart 	.remove = intel_link_remove,
206529a269c6SPierre-Louis Bossart 	.driver = {
206629a269c6SPierre-Louis Bossart 		/* auxiliary_driver_register() sets .name to be the modname */
206729a269c6SPierre-Louis Bossart 		.pm = &intel_pm,
206829a269c6SPierre-Louis Bossart 	},
206929a269c6SPierre-Louis Bossart 	.id_table = intel_link_id_table
207029a269c6SPierre-Louis Bossart };
207129a269c6SPierre-Louis Bossart module_auxiliary_driver(sdw_intel_drv);
207271bb8a1bSVinod Koul 
207371bb8a1bSVinod Koul MODULE_LICENSE("Dual BSD/GPL");
207429a269c6SPierre-Louis Bossart MODULE_DESCRIPTION("Intel Soundwire Link Driver");
2075