xref: /linux/drivers/soundwire/intel.c (revision ea6942dad4b2a7e1735aa0f10f3d0b04b847750f)
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 
25ebf878edSPierre-Louis Bossart #define INTEL_MASTER_SUSPEND_DELAY_MS	3000
26ff560946SPierre-Louis Bossart #define INTEL_MASTER_RESET_ITERATIONS	10
27ebf878edSPierre-Louis Bossart 
28ebf878edSPierre-Louis Bossart /*
29ebf878edSPierre-Louis Bossart  * debug/config flags for the Intel SoundWire Master.
30ebf878edSPierre-Louis Bossart  *
31ebf878edSPierre-Louis Bossart  * Since we may have multiple masters active, we can have up to 8
32ebf878edSPierre-Louis Bossart  * flags reused in each byte, with master0 using the ls-byte, etc.
33ebf878edSPierre-Louis Bossart  */
34ebf878edSPierre-Louis Bossart 
35ebf878edSPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME		BIT(0)
36ebf878edSPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP		BIT(1)
37a2d9c161SPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE	BIT(2)
38857a7c42SPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_MULTI_LINK		BIT(3)
39ebf878edSPierre-Louis Bossart 
40ebf878edSPierre-Louis Bossart static int md_flags;
41ebf878edSPierre-Louis Bossart module_param_named(sdw_md_flags, md_flags, int, 0444);
42ebf878edSPierre-Louis Bossart MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)");
43ebf878edSPierre-Louis Bossart 
4471bb8a1bSVinod Koul /* Intel SHIM Registers Definition */
4571bb8a1bSVinod Koul #define SDW_SHIM_LCAP			0x0
4671bb8a1bSVinod Koul #define SDW_SHIM_LCTL			0x4
4771bb8a1bSVinod Koul #define SDW_SHIM_IPPTR			0x8
4871bb8a1bSVinod Koul #define SDW_SHIM_SYNC			0xC
4971bb8a1bSVinod Koul 
507cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLSCAP(x)		(0x010 + 0x60 * (x))
517cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS0CM(x)		(0x012 + 0x60 * (x))
527cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS1CM(x)		(0x014 + 0x60 * (x))
537cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS2CM(x)		(0x016 + 0x60 * (x))
547cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS3CM(x)		(0x018 + 0x60 * (x))
557cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSCAP(x)		(0x020 + 0x60 * (x))
5671bb8a1bSVinod Koul 
577cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSYCHM(x, y)		(0x022 + (0x60 * (x)) + (0x2 * (y)))
587cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSYCHC(x, y)		(0x042 + (0x60 * (x)) + (0x2 * (y)))
597cc6e315SPierre-Louis Bossart #define SDW_SHIM_PDMSCAP(x)		(0x062 + 0x60 * (x))
607cc6e315SPierre-Louis Bossart #define SDW_SHIM_IOCTL(x)		(0x06C + 0x60 * (x))
617cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTMCTL(x)		(0x06E + 0x60 * (x))
6271bb8a1bSVinod Koul 
6371bb8a1bSVinod Koul #define SDW_SHIM_WAKEEN			0x190
6471bb8a1bSVinod Koul #define SDW_SHIM_WAKESTS		0x192
6571bb8a1bSVinod Koul 
6671bb8a1bSVinod Koul #define SDW_SHIM_LCTL_SPA		BIT(0)
675ee74eb2SPierre-Louis Bossart #define SDW_SHIM_LCTL_SPA_MASK		GENMASK(3, 0)
6871bb8a1bSVinod Koul #define SDW_SHIM_LCTL_CPA		BIT(8)
695ee74eb2SPierre-Louis Bossart #define SDW_SHIM_LCTL_CPA_MASK		GENMASK(11, 8)
7071bb8a1bSVinod Koul 
714a17c441SPierre-Louis Bossart #define SDW_SHIM_SYNC_SYNCPRD_VAL_24	(24000 / SDW_CADENCE_GSYNC_KHZ - 1)
724a17c441SPierre-Louis Bossart #define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4	(38400 / SDW_CADENCE_GSYNC_KHZ - 1)
7371bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCPRD		GENMASK(14, 0)
7471bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCCPU		BIT(15)
7571bb8a1bSVinod Koul #define SDW_SHIM_SYNC_CMDSYNC_MASK	GENMASK(19, 16)
7671bb8a1bSVinod Koul #define SDW_SHIM_SYNC_CMDSYNC		BIT(16)
7771bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCGO		BIT(24)
7871bb8a1bSVinod Koul 
7971bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_ISS		GENMASK(3, 0)
8071bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_OSS		GENMASK(7, 4)
8171bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_BSS		GENMASK(12, 8)
8271bb8a1bSVinod Koul 
8371bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_LCHN		GENMASK(3, 0)
8471bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_HCHN		GENMASK(7, 4)
8571bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_STREAM		GENMASK(13, 8)
8671bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_DIR		BIT(15)
8771bb8a1bSVinod Koul 
8871bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_ISS		GENMASK(3, 0)
8971bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_OSS		GENMASK(7, 4)
9071bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_BSS		GENMASK(12, 8)
9171bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_CPSS		GENMASK(15, 13)
9271bb8a1bSVinod Koul 
9371bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_MIF		BIT(0)
9471bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_CO		BIT(1)
9571bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_COE		BIT(2)
9671bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DO		BIT(3)
9771bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DOE		BIT(4)
9871bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_BKE		BIT(5)
9971bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_WPDD		BIT(6)
10071bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_CIBD		BIT(8)
10171bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DIBD		BIT(9)
10271bb8a1bSVinod Koul 
10371bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DACTQE		BIT(0)
10471bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DODS		BIT(1)
10571bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DOAIS		GENMASK(4, 3)
10671bb8a1bSVinod Koul 
10771bb8a1bSVinod Koul #define SDW_SHIM_WAKEEN_ENABLE		BIT(0)
10871bb8a1bSVinod Koul #define SDW_SHIM_WAKESTS_STATUS		BIT(0)
10971bb8a1bSVinod Koul 
11071bb8a1bSVinod Koul /* Intel ALH Register definitions */
1117cc6e315SPierre-Louis Bossart #define SDW_ALH_STRMZCFG(x)		(0x000 + (0x4 * (x)))
11279ee6631SPierre-Louis Bossart #define SDW_ALH_NUM_STREAMS		64
11371bb8a1bSVinod Koul 
11471bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_DMAT_VAL	0x3
11571bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_DMAT		GENMASK(7, 0)
11671bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_CHN		GENMASK(19, 16)
11771bb8a1bSVinod Koul 
118c46302ecSVinod Koul enum intel_pdi_type {
119c46302ecSVinod Koul 	INTEL_PDI_IN = 0,
120c46302ecSVinod Koul 	INTEL_PDI_OUT = 1,
121c46302ecSVinod Koul 	INTEL_PDI_BD = 2,
122c46302ecSVinod Koul };
123c46302ecSVinod Koul 
12471bb8a1bSVinod Koul #define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
12571bb8a1bSVinod Koul 
12671bb8a1bSVinod Koul /*
12771bb8a1bSVinod Koul  * Read, write helpers for HW registers
12871bb8a1bSVinod Koul  */
12971bb8a1bSVinod Koul static inline int intel_readl(void __iomem *base, int offset)
13071bb8a1bSVinod Koul {
13171bb8a1bSVinod Koul 	return readl(base + offset);
13271bb8a1bSVinod Koul }
13371bb8a1bSVinod Koul 
13471bb8a1bSVinod Koul static inline void intel_writel(void __iomem *base, int offset, int value)
13571bb8a1bSVinod Koul {
13671bb8a1bSVinod Koul 	writel(value, base + offset);
13771bb8a1bSVinod Koul }
13871bb8a1bSVinod Koul 
13971bb8a1bSVinod Koul static inline u16 intel_readw(void __iomem *base, int offset)
14071bb8a1bSVinod Koul {
14171bb8a1bSVinod Koul 	return readw(base + offset);
14271bb8a1bSVinod Koul }
14371bb8a1bSVinod Koul 
14471bb8a1bSVinod Koul static inline void intel_writew(void __iomem *base, int offset, u16 value)
14571bb8a1bSVinod Koul {
14671bb8a1bSVinod Koul 	writew(value, base + offset);
14771bb8a1bSVinod Koul }
14871bb8a1bSVinod Koul 
1497d2845d5SPierre-Louis Bossart static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
15071bb8a1bSVinod Koul {
15171bb8a1bSVinod Koul 	int timeout = 10;
15271bb8a1bSVinod Koul 	u32 reg_read;
15371bb8a1bSVinod Koul 
15471bb8a1bSVinod Koul 	do {
15571bb8a1bSVinod Koul 		reg_read = readl(base + offset);
1567d2845d5SPierre-Louis Bossart 		if ((reg_read & mask) == target)
15771bb8a1bSVinod Koul 			return 0;
15871bb8a1bSVinod Koul 
15971bb8a1bSVinod Koul 		timeout--;
1607d2845d5SPierre-Louis Bossart 		usleep_range(50, 100);
16171bb8a1bSVinod Koul 	} while (timeout != 0);
16271bb8a1bSVinod Koul 
16371bb8a1bSVinod Koul 	return -EAGAIN;
16471bb8a1bSVinod Koul }
16571bb8a1bSVinod Koul 
1667d2845d5SPierre-Louis Bossart static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask)
1677d2845d5SPierre-Louis Bossart {
1687d2845d5SPierre-Louis Bossart 	writel(value, base + offset);
1697d2845d5SPierre-Louis Bossart 	return intel_wait_bit(base, offset, mask, 0);
1707d2845d5SPierre-Louis Bossart }
1717d2845d5SPierre-Louis Bossart 
17271bb8a1bSVinod Koul static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask)
17371bb8a1bSVinod Koul {
17471bb8a1bSVinod Koul 	writel(value, base + offset);
1757d2845d5SPierre-Louis Bossart 	return intel_wait_bit(base, offset, mask, mask);
17671bb8a1bSVinod Koul }
17771bb8a1bSVinod Koul 
17871bb8a1bSVinod Koul /*
17979ee6631SPierre-Louis Bossart  * debugfs
18079ee6631SPierre-Louis Bossart  */
18179ee6631SPierre-Louis Bossart #ifdef CONFIG_DEBUG_FS
18279ee6631SPierre-Louis Bossart 
18379ee6631SPierre-Louis Bossart #define RD_BUF (2 * PAGE_SIZE)
18479ee6631SPierre-Louis Bossart 
18579ee6631SPierre-Louis Bossart static ssize_t intel_sprintf(void __iomem *mem, bool l,
18679ee6631SPierre-Louis Bossart 			     char *buf, size_t pos, unsigned int reg)
18779ee6631SPierre-Louis Bossart {
18879ee6631SPierre-Louis Bossart 	int value;
18979ee6631SPierre-Louis Bossart 
19079ee6631SPierre-Louis Bossart 	if (l)
19179ee6631SPierre-Louis Bossart 		value = intel_readl(mem, reg);
19279ee6631SPierre-Louis Bossart 	else
19379ee6631SPierre-Louis Bossart 		value = intel_readw(mem, reg);
19479ee6631SPierre-Louis Bossart 
19579ee6631SPierre-Louis Bossart 	return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value);
19679ee6631SPierre-Louis Bossart }
19779ee6631SPierre-Louis Bossart 
19879ee6631SPierre-Louis Bossart static int intel_reg_show(struct seq_file *s_file, void *data)
19979ee6631SPierre-Louis Bossart {
20079ee6631SPierre-Louis Bossart 	struct sdw_intel *sdw = s_file->private;
2012523486bSPierre-Louis Bossart 	void __iomem *s = sdw->link_res->shim;
2022523486bSPierre-Louis Bossart 	void __iomem *a = sdw->link_res->alh;
20379ee6631SPierre-Louis Bossart 	char *buf;
20479ee6631SPierre-Louis Bossart 	ssize_t ret;
20579ee6631SPierre-Louis Bossart 	int i, j;
20679ee6631SPierre-Louis Bossart 	unsigned int links, reg;
20779ee6631SPierre-Louis Bossart 
20879ee6631SPierre-Louis Bossart 	buf = kzalloc(RD_BUF, GFP_KERNEL);
20979ee6631SPierre-Louis Bossart 	if (!buf)
21079ee6631SPierre-Louis Bossart 		return -ENOMEM;
21179ee6631SPierre-Louis Bossart 
21279ee6631SPierre-Louis Bossart 	links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0);
21379ee6631SPierre-Louis Bossart 
21479ee6631SPierre-Louis Bossart 	ret = scnprintf(buf, RD_BUF, "Register  Value\n");
21579ee6631SPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n");
21679ee6631SPierre-Louis Bossart 
21779ee6631SPierre-Louis Bossart 	for (i = 0; i < links; i++) {
21879ee6631SPierre-Louis Bossart 		reg = SDW_SHIM_LCAP + i * 4;
21979ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, true, buf, ret, reg);
22079ee6631SPierre-Louis Bossart 	}
22179ee6631SPierre-Louis Bossart 
22279ee6631SPierre-Louis Bossart 	for (i = 0; i < links; i++) {
22379ee6631SPierre-Louis Bossart 		ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i);
22479ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i));
22579ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i));
22679ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i));
22779ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i));
22879ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i));
22979ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i));
23079ee6631SPierre-Louis Bossart 
23179ee6631SPierre-Louis Bossart 		ret += scnprintf(buf + ret, RD_BUF - ret, "\n PCMSyCH registers\n");
23279ee6631SPierre-Louis Bossart 
23379ee6631SPierre-Louis Bossart 		/*
23479ee6631SPierre-Louis Bossart 		 * the value 10 is the number of PDIs. We will need a
23579ee6631SPierre-Louis Bossart 		 * cleanup to remove hard-coded Intel configurations
23679ee6631SPierre-Louis Bossart 		 * from cadence_master.c
23779ee6631SPierre-Louis Bossart 		 */
23879ee6631SPierre-Louis Bossart 		for (j = 0; j < 10; j++) {
23979ee6631SPierre-Louis Bossart 			ret += intel_sprintf(s, false, buf, ret,
24079ee6631SPierre-Louis Bossart 					SDW_SHIM_PCMSYCHM(i, j));
24179ee6631SPierre-Louis Bossart 			ret += intel_sprintf(s, false, buf, ret,
24279ee6631SPierre-Louis Bossart 					SDW_SHIM_PCMSYCHC(i, j));
24379ee6631SPierre-Louis Bossart 		}
24479ee6631SPierre-Louis Bossart 		ret += scnprintf(buf + ret, RD_BUF - ret, "\n PDMSCAP, IOCTL, CTMCTL\n");
24579ee6631SPierre-Louis Bossart 
24679ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i));
24779ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i));
24879ee6631SPierre-Louis Bossart 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i));
24979ee6631SPierre-Louis Bossart 	}
25079ee6631SPierre-Louis Bossart 
25179ee6631SPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nWake registers\n");
25279ee6631SPierre-Louis Bossart 	ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN);
25379ee6631SPierre-Louis Bossart 	ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS);
25479ee6631SPierre-Louis Bossart 
25579ee6631SPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH STRMzCFG\n");
25679ee6631SPierre-Louis Bossart 	for (i = 0; i < SDW_ALH_NUM_STREAMS; i++)
25779ee6631SPierre-Louis Bossart 		ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i));
25879ee6631SPierre-Louis Bossart 
25979ee6631SPierre-Louis Bossart 	seq_printf(s_file, "%s", buf);
26079ee6631SPierre-Louis Bossart 	kfree(buf);
26179ee6631SPierre-Louis Bossart 
26279ee6631SPierre-Louis Bossart 	return 0;
26379ee6631SPierre-Louis Bossart }
26479ee6631SPierre-Louis Bossart DEFINE_SHOW_ATTRIBUTE(intel_reg);
26579ee6631SPierre-Louis Bossart 
2660f9138e7SPierre-Louis Bossart static int intel_set_m_datamode(void *data, u64 value)
2670f9138e7SPierre-Louis Bossart {
2680f9138e7SPierre-Louis Bossart 	struct sdw_intel *sdw = data;
2690f9138e7SPierre-Louis Bossart 	struct sdw_bus *bus = &sdw->cdns.bus;
2700f9138e7SPierre-Louis Bossart 
2710f9138e7SPierre-Louis Bossart 	if (value > SDW_PORT_DATA_MODE_STATIC_1)
2720f9138e7SPierre-Louis Bossart 		return -EINVAL;
2730f9138e7SPierre-Louis Bossart 
2740f9138e7SPierre-Louis Bossart 	/* Userspace changed the hardware state behind the kernel's back */
2750f9138e7SPierre-Louis Bossart 	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
2760f9138e7SPierre-Louis Bossart 
2770f9138e7SPierre-Louis Bossart 	bus->params.m_data_mode = value;
2780f9138e7SPierre-Louis Bossart 
2790f9138e7SPierre-Louis Bossart 	return 0;
2800f9138e7SPierre-Louis Bossart }
2810f9138e7SPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(intel_set_m_datamode_fops, NULL,
2820f9138e7SPierre-Louis Bossart 			 intel_set_m_datamode, "%llu\n");
2830f9138e7SPierre-Louis Bossart 
2840f9138e7SPierre-Louis Bossart static int intel_set_s_datamode(void *data, u64 value)
2850f9138e7SPierre-Louis Bossart {
2860f9138e7SPierre-Louis Bossart 	struct sdw_intel *sdw = data;
2870f9138e7SPierre-Louis Bossart 	struct sdw_bus *bus = &sdw->cdns.bus;
2880f9138e7SPierre-Louis Bossart 
2890f9138e7SPierre-Louis Bossart 	if (value > SDW_PORT_DATA_MODE_STATIC_1)
2900f9138e7SPierre-Louis Bossart 		return -EINVAL;
2910f9138e7SPierre-Louis Bossart 
2920f9138e7SPierre-Louis Bossart 	/* Userspace changed the hardware state behind the kernel's back */
2930f9138e7SPierre-Louis Bossart 	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
2940f9138e7SPierre-Louis Bossart 
2950f9138e7SPierre-Louis Bossart 	bus->params.s_data_mode = value;
2960f9138e7SPierre-Louis Bossart 
2970f9138e7SPierre-Louis Bossart 	return 0;
2980f9138e7SPierre-Louis Bossart }
2990f9138e7SPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(intel_set_s_datamode_fops, NULL,
3000f9138e7SPierre-Louis Bossart 			 intel_set_s_datamode, "%llu\n");
3010f9138e7SPierre-Louis Bossart 
30279ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw)
30379ee6631SPierre-Louis Bossart {
30479ee6631SPierre-Louis Bossart 	struct dentry *root = sdw->cdns.bus.debugfs;
30579ee6631SPierre-Louis Bossart 
30679ee6631SPierre-Louis Bossart 	if (!root)
30779ee6631SPierre-Louis Bossart 		return;
30879ee6631SPierre-Louis Bossart 
30979ee6631SPierre-Louis Bossart 	sdw->debugfs = debugfs_create_dir("intel-sdw", root);
31079ee6631SPierre-Louis Bossart 
31179ee6631SPierre-Louis Bossart 	debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw,
31279ee6631SPierre-Louis Bossart 			    &intel_reg_fops);
31379ee6631SPierre-Louis Bossart 
3140f9138e7SPierre-Louis Bossart 	debugfs_create_file("intel-m-datamode", 0200, sdw->debugfs, sdw,
3150f9138e7SPierre-Louis Bossart 			    &intel_set_m_datamode_fops);
3160f9138e7SPierre-Louis Bossart 
3170f9138e7SPierre-Louis Bossart 	debugfs_create_file("intel-s-datamode", 0200, sdw->debugfs, sdw,
3180f9138e7SPierre-Louis Bossart 			    &intel_set_s_datamode_fops);
3190f9138e7SPierre-Louis Bossart 
32079ee6631SPierre-Louis Bossart 	sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs);
32179ee6631SPierre-Louis Bossart }
32279ee6631SPierre-Louis Bossart 
32379ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw)
32479ee6631SPierre-Louis Bossart {
32579ee6631SPierre-Louis Bossart 	debugfs_remove_recursive(sdw->debugfs);
32679ee6631SPierre-Louis Bossart }
32779ee6631SPierre-Louis Bossart #else
32879ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw) {}
32979ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw) {}
33079ee6631SPierre-Louis Bossart #endif /* CONFIG_DEBUG_FS */
33179ee6631SPierre-Louis Bossart 
33279ee6631SPierre-Louis Bossart /*
33371bb8a1bSVinod Koul  * shim ops
33471bb8a1bSVinod Koul  */
33571bb8a1bSVinod Koul 
33671bb8a1bSVinod Koul static int intel_link_power_up(struct sdw_intel *sdw)
33771bb8a1bSVinod Koul {
33871bb8a1bSVinod Koul 	unsigned int link_id = sdw->instance;
3392523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
3404a17c441SPierre-Louis Bossart 	u32 *shim_mask = sdw->link_res->shim_mask;
3414a17c441SPierre-Louis Bossart 	struct sdw_bus *bus = &sdw->cdns.bus;
3424a17c441SPierre-Louis Bossart 	struct sdw_master_prop *prop = &bus->prop;
3435ee74eb2SPierre-Louis Bossart 	u32 spa_mask, cpa_mask;
3445ee74eb2SPierre-Louis Bossart 	u32 link_control;
3454a17c441SPierre-Louis Bossart 	int ret = 0;
3464a17c441SPierre-Louis Bossart 	u32 syncprd;
3474a17c441SPierre-Louis Bossart 	u32 sync_reg;
3484a17c441SPierre-Louis Bossart 
3494a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
3504a17c441SPierre-Louis Bossart 
3514a17c441SPierre-Louis Bossart 	/*
3524a17c441SPierre-Louis Bossart 	 * The hardware relies on an internal counter, typically 4kHz,
3534a17c441SPierre-Louis Bossart 	 * to generate the SoundWire SSP - which defines a 'safe'
3544a17c441SPierre-Louis Bossart 	 * synchronization point between commands and audio transport
3554a17c441SPierre-Louis Bossart 	 * and allows for multi link synchronization. The SYNCPRD value
3564a17c441SPierre-Louis Bossart 	 * is only dependent on the oscillator clock provided to
3574a17c441SPierre-Louis Bossart 	 * the IP, so adjust based on _DSD properties reported in DSDT
3584a17c441SPierre-Louis Bossart 	 * tables. The values reported are based on either 24MHz
3594a17c441SPierre-Louis Bossart 	 * (CNL/CML) or 38.4 MHz (ICL/TGL+).
3604a17c441SPierre-Louis Bossart 	 */
3614a17c441SPierre-Louis Bossart 	if (prop->mclk_freq % 6000000)
3624a17c441SPierre-Louis Bossart 		syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;
3634a17c441SPierre-Louis Bossart 	else
3644a17c441SPierre-Louis Bossart 		syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24;
3654a17c441SPierre-Louis Bossart 
3664a17c441SPierre-Louis Bossart 	if (!*shim_mask) {
3675ee74eb2SPierre-Louis Bossart 		dev_dbg(sdw->cdns.dev, "%s: powering up all links\n", __func__);
3685ee74eb2SPierre-Louis Bossart 
3694a17c441SPierre-Louis Bossart 		/* we first need to program the SyncPRD/CPU registers */
3704a17c441SPierre-Louis Bossart 		dev_dbg(sdw->cdns.dev,
3714a17c441SPierre-Louis Bossart 			"%s: first link up, programming SYNCPRD\n", __func__);
3724a17c441SPierre-Louis Bossart 
3734a17c441SPierre-Louis Bossart 		/* set SyncPRD period */
3744a17c441SPierre-Louis Bossart 		sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
375f067c925SVinod Koul 		u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD);
3764a17c441SPierre-Louis Bossart 
3774a17c441SPierre-Louis Bossart 		/* Set SyncCPU bit */
3784a17c441SPierre-Louis Bossart 		sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
3794a17c441SPierre-Louis Bossart 		intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
38071bb8a1bSVinod Koul 
38171bb8a1bSVinod Koul 		/* Link power up sequence */
38271bb8a1bSVinod Koul 		link_control = intel_readl(shim, SDW_SHIM_LCTL);
3835ee74eb2SPierre-Louis Bossart 
3845ee74eb2SPierre-Louis Bossart 		/* only power-up enabled links */
3853b4979caSVinod Koul 		spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask);
3863b4979caSVinod Koul 		cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
3875ee74eb2SPierre-Louis Bossart 
38871bb8a1bSVinod Koul 		link_control |=  spa_mask;
38971bb8a1bSVinod Koul 
39071bb8a1bSVinod Koul 		ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
3914a17c441SPierre-Louis Bossart 		if (ret < 0) {
3924a17c441SPierre-Louis Bossart 			dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret);
3934a17c441SPierre-Louis Bossart 			goto out;
39471bb8a1bSVinod Koul 		}
39571bb8a1bSVinod Koul 
3964a17c441SPierre-Louis Bossart 		/* SyncCPU will change once link is active */
3974a17c441SPierre-Louis Bossart 		ret = intel_wait_bit(shim, SDW_SHIM_SYNC,
3984a17c441SPierre-Louis Bossart 				     SDW_SHIM_SYNC_SYNCCPU, 0);
3994a17c441SPierre-Louis Bossart 		if (ret < 0) {
4004a17c441SPierre-Louis Bossart 			dev_err(sdw->cdns.dev,
4014a17c441SPierre-Louis Bossart 				"Failed to set SHIM_SYNC: %d\n", ret);
4024a17c441SPierre-Louis Bossart 			goto out;
4034a17c441SPierre-Louis Bossart 		}
4044a17c441SPierre-Louis Bossart 	}
4054a17c441SPierre-Louis Bossart 
4064a17c441SPierre-Louis Bossart 	*shim_mask |= BIT(link_id);
4074a17c441SPierre-Louis Bossart 
4084a17c441SPierre-Louis Bossart 	sdw->cdns.link_up = true;
4094a17c441SPierre-Louis Bossart out:
4104a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
4114a17c441SPierre-Louis Bossart 
4124a17c441SPierre-Louis Bossart 	return ret;
4134a17c441SPierre-Louis Bossart }
4144a17c441SPierre-Louis Bossart 
4154a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */
4164a17c441SPierre-Louis Bossart static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw)
41771bb8a1bSVinod Koul {
4182523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
41971bb8a1bSVinod Koul 	unsigned int link_id = sdw->instance;
4204a17c441SPierre-Louis Bossart 	u16 ioctl;
42171bb8a1bSVinod Koul 
42271bb8a1bSVinod Koul 	/* Switch to MIP from Glue logic */
42371bb8a1bSVinod Koul 	ioctl = intel_readw(shim,  SDW_SHIM_IOCTL(link_id));
42471bb8a1bSVinod Koul 
42571bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_DOE);
42671bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4274a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
42871bb8a1bSVinod Koul 
42971bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_DO);
43071bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4314a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
43271bb8a1bSVinod Koul 
43371bb8a1bSVinod Koul 	ioctl |= (SDW_SHIM_IOCTL_MIF);
43471bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4354a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
43671bb8a1bSVinod Koul 
43771bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_BKE);
43871bb8a1bSVinod Koul 	ioctl &= ~(SDW_SHIM_IOCTL_COE);
43971bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4404a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4414a17c441SPierre-Louis Bossart 
4424a17c441SPierre-Louis Bossart 	/* at this point Master IP has full control of the I/Os */
4434a17c441SPierre-Louis Bossart }
4444a17c441SPierre-Louis Bossart 
4454a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */
4464a17c441SPierre-Louis Bossart static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw)
4474a17c441SPierre-Louis Bossart {
4484a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
4494a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
4504a17c441SPierre-Louis Bossart 	u16 ioctl;
4514a17c441SPierre-Louis Bossart 
4524a17c441SPierre-Louis Bossart 	/* Glue logic */
4534a17c441SPierre-Louis Bossart 	ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id));
4544a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_BKE;
4554a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_COE;
4564a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4574a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4584a17c441SPierre-Louis Bossart 
4594a17c441SPierre-Louis Bossart 	ioctl &= ~(SDW_SHIM_IOCTL_MIF);
4604a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4614a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4624a17c441SPierre-Louis Bossart 
4634a17c441SPierre-Louis Bossart 	/* at this point Integration Glue has full control of the I/Os */
4644a17c441SPierre-Louis Bossart }
4654a17c441SPierre-Louis Bossart 
4664a17c441SPierre-Louis Bossart static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop)
4674a17c441SPierre-Louis Bossart {
4684a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
4694a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
4704a17c441SPierre-Louis Bossart 	int ret = 0;
4714a17c441SPierre-Louis Bossart 	u16 ioctl = 0, act = 0;
4724a17c441SPierre-Louis Bossart 
4734a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
4744a17c441SPierre-Louis Bossart 
4754a17c441SPierre-Louis Bossart 	/* Initialize Shim */
4764a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_BKE;
4774a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4784a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4794a17c441SPierre-Louis Bossart 
4804a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_WPDD;
4814a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4824a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4834a17c441SPierre-Louis Bossart 
4844a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_DO;
4854a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4864a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4874a17c441SPierre-Louis Bossart 
4884a17c441SPierre-Louis Bossart 	ioctl |= SDW_SHIM_IOCTL_DOE;
4894a17c441SPierre-Louis Bossart 	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4904a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
4914a17c441SPierre-Louis Bossart 
4924a17c441SPierre-Louis Bossart 	intel_shim_glue_to_master_ip(sdw);
49371bb8a1bSVinod Koul 
494f067c925SVinod Koul 	u16p_replace_bits(&act, 0x1, SDW_SHIM_CTMCTL_DOAIS);
49571bb8a1bSVinod Koul 	act |= SDW_SHIM_CTMCTL_DACTQE;
49671bb8a1bSVinod Koul 	act |= SDW_SHIM_CTMCTL_DODS;
49771bb8a1bSVinod Koul 	intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act);
4984a17c441SPierre-Louis Bossart 	usleep_range(10, 15);
49971bb8a1bSVinod Koul 
5004a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
50171bb8a1bSVinod Koul 
50271bb8a1bSVinod Koul 	return ret;
50371bb8a1bSVinod Koul }
50471bb8a1bSVinod Koul 
505ab2c9132SRander Wang static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
5064a17c441SPierre-Louis Bossart {
5074a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
5084a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
5094a17c441SPierre-Louis Bossart 	u16 wake_en, wake_sts;
5104a17c441SPierre-Louis Bossart 
5114a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
5124a17c441SPierre-Louis Bossart 	wake_en = intel_readw(shim, SDW_SHIM_WAKEEN);
5134a17c441SPierre-Louis Bossart 
5144a17c441SPierre-Louis Bossart 	if (wake_enable) {
5154a17c441SPierre-Louis Bossart 		/* Enable the wakeup */
5164a17c441SPierre-Louis Bossart 		wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
5174a17c441SPierre-Louis Bossart 		intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
5184a17c441SPierre-Louis Bossart 	} else {
5194a17c441SPierre-Louis Bossart 		/* Disable the wake up interrupt */
5204a17c441SPierre-Louis Bossart 		wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id);
5214a17c441SPierre-Louis Bossart 		intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
5224a17c441SPierre-Louis Bossart 
5234a17c441SPierre-Louis Bossart 		/* Clear wake status */
5244a17c441SPierre-Louis Bossart 		wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
5254a17c441SPierre-Louis Bossart 		wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
5264a17c441SPierre-Louis Bossart 		intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts);
5274a17c441SPierre-Louis Bossart 	}
5284a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
5294a17c441SPierre-Louis Bossart }
5304a17c441SPierre-Louis Bossart 
5319b3b4b3fSPierre-Louis Bossart static int intel_link_power_down(struct sdw_intel *sdw)
5324a17c441SPierre-Louis Bossart {
5335ee74eb2SPierre-Louis Bossart 	u32 link_control, spa_mask, cpa_mask;
5344a17c441SPierre-Louis Bossart 	unsigned int link_id = sdw->instance;
5354a17c441SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
5364a17c441SPierre-Louis Bossart 	u32 *shim_mask = sdw->link_res->shim_mask;
5374a17c441SPierre-Louis Bossart 	int ret = 0;
5384a17c441SPierre-Louis Bossart 
5394a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
5404a17c441SPierre-Louis Bossart 
5414a17c441SPierre-Louis Bossart 	if (!(*shim_mask & BIT(link_id)))
5424a17c441SPierre-Louis Bossart 		dev_err(sdw->cdns.dev,
5434a17c441SPierre-Louis Bossart 			"%s: Unbalanced power-up/down calls\n", __func__);
5444a17c441SPierre-Louis Bossart 
545*ea6942daSPierre-Louis Bossart 	sdw->cdns.link_up = false;
546*ea6942daSPierre-Louis Bossart 
547*ea6942daSPierre-Louis Bossart 	intel_shim_master_ip_to_glue(sdw);
548*ea6942daSPierre-Louis Bossart 
5494a17c441SPierre-Louis Bossart 	*shim_mask &= ~BIT(link_id);
5504a17c441SPierre-Louis Bossart 
5515ee74eb2SPierre-Louis Bossart 	if (!*shim_mask) {
5525ee74eb2SPierre-Louis Bossart 
5535ee74eb2SPierre-Louis Bossart 		dev_dbg(sdw->cdns.dev, "%s: powering down all links\n", __func__);
5545ee74eb2SPierre-Louis Bossart 
5555ee74eb2SPierre-Louis Bossart 		/* Link power down sequence */
5565ee74eb2SPierre-Louis Bossart 		link_control = intel_readl(shim, SDW_SHIM_LCTL);
5575ee74eb2SPierre-Louis Bossart 
5585ee74eb2SPierre-Louis Bossart 		/* only power-down enabled links */
5593b4979caSVinod Koul 		spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, ~sdw->link_res->link_mask);
5603b4979caSVinod Koul 		cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
5615ee74eb2SPierre-Louis Bossart 
5625ee74eb2SPierre-Louis Bossart 		link_control &=  spa_mask;
5635ee74eb2SPierre-Louis Bossart 
5645ee74eb2SPierre-Louis Bossart 		ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
565*ea6942daSPierre-Louis Bossart 		if (ret < 0) {
566*ea6942daSPierre-Louis Bossart 			dev_err(sdw->cdns.dev, "%s: could not power down link\n", __func__);
567*ea6942daSPierre-Louis Bossart 
568*ea6942daSPierre-Louis Bossart 			/*
569*ea6942daSPierre-Louis Bossart 			 * we leave the sdw->cdns.link_up flag as false since we've disabled
570*ea6942daSPierre-Louis Bossart 			 * the link at this point and cannot handle interrupts any longer.
571*ea6942daSPierre-Louis Bossart 			 */
572*ea6942daSPierre-Louis Bossart 		}
5735ee74eb2SPierre-Louis Bossart 	}
5745ee74eb2SPierre-Louis Bossart 
5754a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
5764a17c441SPierre-Louis Bossart 
5774a17c441SPierre-Louis Bossart 	return ret;
5785ee74eb2SPierre-Louis Bossart }
5794a17c441SPierre-Louis Bossart 
58002629e45SPierre-Louis Bossart static void intel_shim_sync_arm(struct sdw_intel *sdw)
58102629e45SPierre-Louis Bossart {
58202629e45SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
58302629e45SPierre-Louis Bossart 	u32 sync_reg;
58402629e45SPierre-Louis Bossart 
58502629e45SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
58602629e45SPierre-Louis Bossart 
58702629e45SPierre-Louis Bossart 	/* update SYNC register */
58802629e45SPierre-Louis Bossart 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
58902629e45SPierre-Louis Bossart 	sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance);
59002629e45SPierre-Louis Bossart 	intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
59102629e45SPierre-Louis Bossart 
59202629e45SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
59302629e45SPierre-Louis Bossart }
59402629e45SPierre-Louis Bossart 
595437e3289SPierre-Louis Bossart static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw)
596437e3289SPierre-Louis Bossart {
597437e3289SPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
598437e3289SPierre-Louis Bossart 	u32 sync_reg;
599437e3289SPierre-Louis Bossart 	int ret;
600437e3289SPierre-Louis Bossart 
601437e3289SPierre-Louis Bossart 	/* Read SYNC register */
602437e3289SPierre-Louis Bossart 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
603437e3289SPierre-Louis Bossart 
604437e3289SPierre-Louis Bossart 	/*
605437e3289SPierre-Louis Bossart 	 * Set SyncGO bit to synchronously trigger a bank switch for
606437e3289SPierre-Louis Bossart 	 * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all
607437e3289SPierre-Louis Bossart 	 * the Masters.
608437e3289SPierre-Louis Bossart 	 */
609437e3289SPierre-Louis Bossart 	sync_reg |= SDW_SHIM_SYNC_SYNCGO;
610437e3289SPierre-Louis Bossart 
611437e3289SPierre-Louis Bossart 	ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
612437e3289SPierre-Louis Bossart 			      SDW_SHIM_SYNC_SYNCGO);
613437e3289SPierre-Louis Bossart 
614437e3289SPierre-Louis Bossart 	if (ret < 0)
615437e3289SPierre-Louis Bossart 		dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret);
61671bb8a1bSVinod Koul 
61771bb8a1bSVinod Koul 	return ret;
61871bb8a1bSVinod Koul }
61971bb8a1bSVinod Koul 
620857a7c42SPierre-Louis Bossart static int intel_shim_sync_go(struct sdw_intel *sdw)
621857a7c42SPierre-Louis Bossart {
622857a7c42SPierre-Louis Bossart 	int ret;
623857a7c42SPierre-Louis Bossart 
624857a7c42SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
625857a7c42SPierre-Louis Bossart 
626857a7c42SPierre-Louis Bossart 	ret = intel_shim_sync_go_unlocked(sdw);
627857a7c42SPierre-Louis Bossart 
628857a7c42SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
629857a7c42SPierre-Louis Bossart 
630857a7c42SPierre-Louis Bossart 	return ret;
631857a7c42SPierre-Louis Bossart }
632857a7c42SPierre-Louis Bossart 
63337a2d22bSVinod Koul /*
63437a2d22bSVinod Koul  * PDI routines
63537a2d22bSVinod Koul  */
63637a2d22bSVinod Koul static void intel_pdi_init(struct sdw_intel *sdw,
63737a2d22bSVinod Koul 			   struct sdw_cdns_stream_config *config)
63837a2d22bSVinod Koul {
6392523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
64037a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
64137a2d22bSVinod Koul 	int pcm_cap, pdm_cap;
64237a2d22bSVinod Koul 
64337a2d22bSVinod Koul 	/* PCM Stream Capability */
64437a2d22bSVinod Koul 	pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id));
64537a2d22bSVinod Koul 
6463b4979caSVinod Koul 	config->pcm_bd = FIELD_GET(SDW_SHIM_PCMSCAP_BSS, pcm_cap);
6473b4979caSVinod Koul 	config->pcm_in = FIELD_GET(SDW_SHIM_PCMSCAP_ISS, pcm_cap);
6483b4979caSVinod Koul 	config->pcm_out = FIELD_GET(SDW_SHIM_PCMSCAP_OSS, pcm_cap);
64937a2d22bSVinod Koul 
650121f4361SPierre-Louis Bossart 	dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n",
651121f4361SPierre-Louis Bossart 		config->pcm_bd, config->pcm_in, config->pcm_out);
652121f4361SPierre-Louis Bossart 
65337a2d22bSVinod Koul 	/* PDM Stream Capability */
65437a2d22bSVinod Koul 	pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
65537a2d22bSVinod Koul 
6563b4979caSVinod Koul 	config->pdm_bd = FIELD_GET(SDW_SHIM_PDMSCAP_BSS, pdm_cap);
6573b4979caSVinod Koul 	config->pdm_in = FIELD_GET(SDW_SHIM_PDMSCAP_ISS, pdm_cap);
6583b4979caSVinod Koul 	config->pdm_out = FIELD_GET(SDW_SHIM_PDMSCAP_OSS, pdm_cap);
659121f4361SPierre-Louis Bossart 
660121f4361SPierre-Louis Bossart 	dev_dbg(sdw->cdns.dev, "PDM cap bd:%d in:%d out:%d\n",
661121f4361SPierre-Louis Bossart 		config->pdm_bd, config->pdm_in, config->pdm_out);
66237a2d22bSVinod Koul }
66337a2d22bSVinod Koul 
66437a2d22bSVinod Koul static int
66537a2d22bSVinod Koul intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm)
66637a2d22bSVinod Koul {
6672523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
66837a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
66937a2d22bSVinod Koul 	int count;
67037a2d22bSVinod Koul 
67137a2d22bSVinod Koul 	if (pcm) {
67237a2d22bSVinod Koul 		count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num));
67318046335SPierre-Louis Bossart 
67418046335SPierre-Louis Bossart 		/*
67518046335SPierre-Louis Bossart 		 * WORKAROUND: on all existing Intel controllers, pdi
67618046335SPierre-Louis Bossart 		 * number 2 reports channel count as 1 even though it
67718046335SPierre-Louis Bossart 		 * supports 8 channels. Performing hardcoding for pdi
67818046335SPierre-Louis Bossart 		 * number 2.
67918046335SPierre-Louis Bossart 		 */
68018046335SPierre-Louis Bossart 		if (pdi_num == 2)
68118046335SPierre-Louis Bossart 			count = 7;
68218046335SPierre-Louis Bossart 
68337a2d22bSVinod Koul 	} else {
68437a2d22bSVinod Koul 		count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
6853b4979caSVinod Koul 		count = FIELD_GET(SDW_SHIM_PDMSCAP_CPSS, count);
68637a2d22bSVinod Koul 	}
68737a2d22bSVinod Koul 
68837a2d22bSVinod Koul 	/* zero based values for channel count in register */
68937a2d22bSVinod Koul 	count++;
69037a2d22bSVinod Koul 
69137a2d22bSVinod Koul 	return count;
69237a2d22bSVinod Koul }
69337a2d22bSVinod Koul 
69437a2d22bSVinod Koul static int intel_pdi_get_ch_update(struct sdw_intel *sdw,
69537a2d22bSVinod Koul 				   struct sdw_cdns_pdi *pdi,
69637a2d22bSVinod Koul 				   unsigned int num_pdi,
69737a2d22bSVinod Koul 				   unsigned int *num_ch, bool pcm)
69837a2d22bSVinod Koul {
69937a2d22bSVinod Koul 	int i, ch_count = 0;
70037a2d22bSVinod Koul 
70137a2d22bSVinod Koul 	for (i = 0; i < num_pdi; i++) {
70237a2d22bSVinod Koul 		pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm);
70337a2d22bSVinod Koul 		ch_count += pdi->ch_count;
70437a2d22bSVinod Koul 		pdi++;
70537a2d22bSVinod Koul 	}
70637a2d22bSVinod Koul 
70737a2d22bSVinod Koul 	*num_ch = ch_count;
70837a2d22bSVinod Koul 	return 0;
70937a2d22bSVinod Koul }
71037a2d22bSVinod Koul 
71137a2d22bSVinod Koul static int intel_pdi_stream_ch_update(struct sdw_intel *sdw,
71237a2d22bSVinod Koul 				      struct sdw_cdns_streams *stream, bool pcm)
71337a2d22bSVinod Koul {
71437a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd,
71537a2d22bSVinod Koul 				&stream->num_ch_bd, pcm);
71637a2d22bSVinod Koul 
71737a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->in, stream->num_in,
71837a2d22bSVinod Koul 				&stream->num_ch_in, pcm);
71937a2d22bSVinod Koul 
72037a2d22bSVinod Koul 	intel_pdi_get_ch_update(sdw, stream->out, stream->num_out,
72137a2d22bSVinod Koul 				&stream->num_ch_out, pcm);
72237a2d22bSVinod Koul 
72337a2d22bSVinod Koul 	return 0;
72437a2d22bSVinod Koul }
72537a2d22bSVinod Koul 
72637a2d22bSVinod Koul static int intel_pdi_ch_update(struct sdw_intel *sdw)
72737a2d22bSVinod Koul {
72837a2d22bSVinod Koul 	/* First update PCM streams followed by PDM streams */
72937a2d22bSVinod Koul 	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true);
73037a2d22bSVinod Koul 	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false);
73137a2d22bSVinod Koul 
73237a2d22bSVinod Koul 	return 0;
73337a2d22bSVinod Koul }
73437a2d22bSVinod Koul 
73537a2d22bSVinod Koul static void
73637a2d22bSVinod Koul intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
73737a2d22bSVinod Koul {
7382523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
73937a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
74037a2d22bSVinod Koul 	int pdi_conf = 0;
74137a2d22bSVinod Koul 
742c134f914SPierre-Louis Bossart 	/* the Bulk and PCM streams are not contiguous */
743c134f914SPierre-Louis Bossart 	pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
744c134f914SPierre-Louis Bossart 	if (pdi->num >= 2)
745c134f914SPierre-Louis Bossart 		pdi->intel_alh_id += 2;
74637a2d22bSVinod Koul 
74737a2d22bSVinod Koul 	/*
74837a2d22bSVinod Koul 	 * Program stream parameters to stream SHIM register
74937a2d22bSVinod Koul 	 * This is applicable for PCM stream only.
75037a2d22bSVinod Koul 	 */
75137a2d22bSVinod Koul 	if (pdi->type != SDW_STREAM_PCM)
75237a2d22bSVinod Koul 		return;
75337a2d22bSVinod Koul 
75437a2d22bSVinod Koul 	if (pdi->dir == SDW_DATA_DIR_RX)
75537a2d22bSVinod Koul 		pdi_conf |= SDW_SHIM_PCMSYCM_DIR;
75637a2d22bSVinod Koul 	else
75737a2d22bSVinod Koul 		pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR);
75837a2d22bSVinod Koul 
759f067c925SVinod Koul 	u32p_replace_bits(&pdi_conf, pdi->intel_alh_id, SDW_SHIM_PCMSYCM_STREAM);
760f067c925SVinod Koul 	u32p_replace_bits(&pdi_conf, pdi->l_ch_num, SDW_SHIM_PCMSYCM_LCHN);
761f067c925SVinod Koul 	u32p_replace_bits(&pdi_conf, pdi->h_ch_num, SDW_SHIM_PCMSYCM_HCHN);
76237a2d22bSVinod Koul 
76337a2d22bSVinod Koul 	intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf);
76437a2d22bSVinod Koul }
76537a2d22bSVinod Koul 
76637a2d22bSVinod Koul static void
76737a2d22bSVinod Koul intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
76837a2d22bSVinod Koul {
7692523486bSPierre-Louis Bossart 	void __iomem *alh = sdw->link_res->alh;
77037a2d22bSVinod Koul 	unsigned int link_id = sdw->instance;
77137a2d22bSVinod Koul 	unsigned int conf;
77237a2d22bSVinod Koul 
773c134f914SPierre-Louis Bossart 	/* the Bulk and PCM streams are not contiguous */
774c134f914SPierre-Louis Bossart 	pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
775c134f914SPierre-Louis Bossart 	if (pdi->num >= 2)
776c134f914SPierre-Louis Bossart 		pdi->intel_alh_id += 2;
77737a2d22bSVinod Koul 
77837a2d22bSVinod Koul 	/* Program Stream config ALH register */
77937a2d22bSVinod Koul 	conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id));
78037a2d22bSVinod Koul 
781f067c925SVinod Koul 	u32p_replace_bits(&conf, SDW_ALH_STRMZCFG_DMAT_VAL, SDW_ALH_STRMZCFG_DMAT);
782f067c925SVinod Koul 	u32p_replace_bits(&conf, pdi->ch_count - 1, SDW_ALH_STRMZCFG_CHN);
78337a2d22bSVinod Koul 
78437a2d22bSVinod Koul 	intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
78537a2d22bSVinod Koul }
78637a2d22bSVinod Koul 
7874b206d34SRander Wang static int intel_params_stream(struct sdw_intel *sdw,
788c46302ecSVinod Koul 			       struct snd_pcm_substream *substream,
789c46302ecSVinod Koul 			       struct snd_soc_dai *dai,
7904b206d34SRander Wang 			       struct snd_pcm_hw_params *hw_params,
7914b206d34SRander Wang 			       int link_id, int alh_stream_id)
792c46302ecSVinod Koul {
7932523486bSPierre-Louis Bossart 	struct sdw_intel_link_res *res = sdw->link_res;
7944b206d34SRander Wang 	struct sdw_intel_stream_params_data params_data;
79505c8afe4SPierre-Louis Bossart 
7964b206d34SRander Wang 	params_data.substream = substream;
7974b206d34SRander Wang 	params_data.dai = dai;
7984b206d34SRander Wang 	params_data.hw_params = hw_params;
7994b206d34SRander Wang 	params_data.link_id = link_id;
8004b206d34SRander Wang 	params_data.alh_stream_id = alh_stream_id;
801c46302ecSVinod Koul 
8024b206d34SRander Wang 	if (res->ops && res->ops->params_stream && res->dev)
8034b206d34SRander Wang 		return res->ops->params_stream(res->dev,
8044b206d34SRander Wang 					       &params_data);
805c46302ecSVinod Koul 	return -EIO;
806c46302ecSVinod Koul }
807c46302ecSVinod Koul 
808eff346f2SPierre-Louis Bossart static int intel_free_stream(struct sdw_intel *sdw,
809eff346f2SPierre-Louis Bossart 			     struct snd_pcm_substream *substream,
810eff346f2SPierre-Louis Bossart 			     struct snd_soc_dai *dai,
811eff346f2SPierre-Louis Bossart 			     int link_id)
812eff346f2SPierre-Louis Bossart {
813eff346f2SPierre-Louis Bossart 	struct sdw_intel_link_res *res = sdw->link_res;
814eff346f2SPierre-Louis Bossart 	struct sdw_intel_stream_free_data free_data;
815eff346f2SPierre-Louis Bossart 
816eff346f2SPierre-Louis Bossart 	free_data.substream = substream;
817eff346f2SPierre-Louis Bossart 	free_data.dai = dai;
818eff346f2SPierre-Louis Bossart 	free_data.link_id = link_id;
819eff346f2SPierre-Louis Bossart 
820eff346f2SPierre-Louis Bossart 	if (res->ops && res->ops->free_stream && res->dev)
821eff346f2SPierre-Louis Bossart 		return res->ops->free_stream(res->dev,
822eff346f2SPierre-Louis Bossart 					     &free_data);
823eff346f2SPierre-Louis Bossart 
824eff346f2SPierre-Louis Bossart 	return 0;
825eff346f2SPierre-Louis Bossart }
826eff346f2SPierre-Louis Bossart 
827c46302ecSVinod Koul /*
82830246e2dSShreyas NC  * bank switch routines
82930246e2dSShreyas NC  */
83030246e2dSShreyas NC 
83130246e2dSShreyas NC static int intel_pre_bank_switch(struct sdw_bus *bus)
83230246e2dSShreyas NC {
83330246e2dSShreyas NC 	struct sdw_cdns *cdns = bus_to_cdns(bus);
83430246e2dSShreyas NC 	struct sdw_intel *sdw = cdns_to_intel(cdns);
83530246e2dSShreyas NC 
83630246e2dSShreyas NC 	/* Write to register only for multi-link */
83730246e2dSShreyas NC 	if (!bus->multi_link)
83830246e2dSShreyas NC 		return 0;
83930246e2dSShreyas NC 
84002629e45SPierre-Louis Bossart 	intel_shim_sync_arm(sdw);
84130246e2dSShreyas NC 
84230246e2dSShreyas NC 	return 0;
84330246e2dSShreyas NC }
84430246e2dSShreyas NC 
84530246e2dSShreyas NC static int intel_post_bank_switch(struct sdw_bus *bus)
84630246e2dSShreyas NC {
84730246e2dSShreyas NC 	struct sdw_cdns *cdns = bus_to_cdns(bus);
84830246e2dSShreyas NC 	struct sdw_intel *sdw = cdns_to_intel(cdns);
8492523486bSPierre-Louis Bossart 	void __iomem *shim = sdw->link_res->shim;
85030246e2dSShreyas NC 	int sync_reg, ret;
85130246e2dSShreyas NC 
85230246e2dSShreyas NC 	/* Write to register only for multi-link */
85330246e2dSShreyas NC 	if (!bus->multi_link)
85430246e2dSShreyas NC 		return 0;
85530246e2dSShreyas NC 
8564a17c441SPierre-Louis Bossart 	mutex_lock(sdw->link_res->shim_lock);
8574a17c441SPierre-Louis Bossart 
85830246e2dSShreyas NC 	/* Read SYNC register */
85930246e2dSShreyas NC 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
86030246e2dSShreyas NC 
86130246e2dSShreyas NC 	/*
86230246e2dSShreyas NC 	 * post_bank_switch() ops is called from the bus in loop for
86330246e2dSShreyas NC 	 * all the Masters in the steam with the expectation that
86430246e2dSShreyas NC 	 * we trigger the bankswitch for the only first Master in the list
86530246e2dSShreyas NC 	 * and do nothing for the other Masters
86630246e2dSShreyas NC 	 *
86730246e2dSShreyas NC 	 * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
86830246e2dSShreyas NC 	 */
8694a17c441SPierre-Louis Bossart 	if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) {
8704a17c441SPierre-Louis Bossart 		ret = 0;
8714a17c441SPierre-Louis Bossart 		goto unlock;
8724a17c441SPierre-Louis Bossart 	}
87330246e2dSShreyas NC 
874437e3289SPierre-Louis Bossart 	ret = intel_shim_sync_go_unlocked(sdw);
8754a17c441SPierre-Louis Bossart unlock:
8764a17c441SPierre-Louis Bossart 	mutex_unlock(sdw->link_res->shim_lock);
87730246e2dSShreyas NC 
87830246e2dSShreyas NC 	if (ret < 0)
87917ed5befSPierre-Louis Bossart 		dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret);
88030246e2dSShreyas NC 
88130246e2dSShreyas NC 	return ret;
88230246e2dSShreyas NC }
88330246e2dSShreyas NC 
88430246e2dSShreyas NC /*
885c46302ecSVinod Koul  * DAI routines
886c46302ecSVinod Koul  */
887c46302ecSVinod Koul 
8885e7484d0SRander Wang static int intel_startup(struct snd_pcm_substream *substream,
8895e7484d0SRander Wang 			 struct snd_soc_dai *dai)
8905e7484d0SRander Wang {
891ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
892ebf878edSPierre-Louis Bossart 	int ret;
893ebf878edSPierre-Louis Bossart 
894ebf878edSPierre-Louis Bossart 	ret = pm_runtime_get_sync(cdns->dev);
895ebf878edSPierre-Louis Bossart 	if (ret < 0 && ret != -EACCES) {
896ebf878edSPierre-Louis Bossart 		dev_err_ratelimited(cdns->dev,
897ebf878edSPierre-Louis Bossart 				    "pm_runtime_get_sync failed in %s, ret %d\n",
898ebf878edSPierre-Louis Bossart 				    __func__, ret);
899ebf878edSPierre-Louis Bossart 		pm_runtime_put_noidle(cdns->dev);
900ebf878edSPierre-Louis Bossart 		return ret;
901ebf878edSPierre-Louis Bossart 	}
902ff16d1e5SPierre-Louis Bossart 	return 0;
9035e7484d0SRander Wang }
9045e7484d0SRander Wang 
905c46302ecSVinod Koul static int intel_hw_params(struct snd_pcm_substream *substream,
906c46302ecSVinod Koul 			   struct snd_pcm_hw_params *params,
907c46302ecSVinod Koul 			   struct snd_soc_dai *dai)
908c46302ecSVinod Koul {
909c46302ecSVinod Koul 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
910c46302ecSVinod Koul 	struct sdw_intel *sdw = cdns_to_intel(cdns);
911c46302ecSVinod Koul 	struct sdw_cdns_dma_data *dma;
91257a34790SPierre-Louis Bossart 	struct sdw_cdns_pdi *pdi;
913c46302ecSVinod Koul 	struct sdw_stream_config sconfig;
914c46302ecSVinod Koul 	struct sdw_port_config *pconfig;
91557a34790SPierre-Louis Bossart 	int ch, dir;
91657a34790SPierre-Louis Bossart 	int ret;
917c46302ecSVinod Koul 	bool pcm = true;
918c46302ecSVinod Koul 
919c46302ecSVinod Koul 	dma = snd_soc_dai_get_dma_data(dai, substream);
920c46302ecSVinod Koul 	if (!dma)
921c46302ecSVinod Koul 		return -EIO;
922c46302ecSVinod Koul 
923c46302ecSVinod Koul 	ch = params_channels(params);
924c46302ecSVinod Koul 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
925c46302ecSVinod Koul 		dir = SDW_DATA_DIR_RX;
926c46302ecSVinod Koul 	else
927c46302ecSVinod Koul 		dir = SDW_DATA_DIR_TX;
928c46302ecSVinod Koul 
92957a34790SPierre-Louis Bossart 	if (dma->stream_type == SDW_STREAM_PDM)
930c46302ecSVinod Koul 		pcm = false;
931c46302ecSVinod Koul 
93257a34790SPierre-Louis Bossart 	if (pcm)
9331b53385eSBard Liao 		pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id);
93457a34790SPierre-Louis Bossart 	else
9351b53385eSBard Liao 		pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir, dai->id);
936c46302ecSVinod Koul 
93757a34790SPierre-Louis Bossart 	if (!pdi) {
938c46302ecSVinod Koul 		ret = -EINVAL;
93957a34790SPierre-Louis Bossart 		goto error;
940c46302ecSVinod Koul 	}
94157a34790SPierre-Louis Bossart 
94257a34790SPierre-Louis Bossart 	/* do run-time configurations for SHIM, ALH and PDI/PORT */
94357a34790SPierre-Louis Bossart 	intel_pdi_shim_configure(sdw, pdi);
94457a34790SPierre-Louis Bossart 	intel_pdi_alh_configure(sdw, pdi);
94557a34790SPierre-Louis Bossart 	sdw_cdns_config_stream(cdns, ch, dir, pdi);
94657a34790SPierre-Louis Bossart 
947a5a0239cSBard Liao 	/* store pdi and hw_params, may be needed in prepare step */
948a5a0239cSBard Liao 	dma->suspended = false;
949a5a0239cSBard Liao 	dma->pdi = pdi;
950a5a0239cSBard Liao 	dma->hw_params = params;
951c46302ecSVinod Koul 
952c46302ecSVinod Koul 	/* Inform DSP about PDI stream number */
9534b206d34SRander Wang 	ret = intel_params_stream(sdw, substream, dai, params,
9544b206d34SRander Wang 				  sdw->instance,
95557a34790SPierre-Louis Bossart 				  pdi->intel_alh_id);
956c46302ecSVinod Koul 	if (ret)
95757a34790SPierre-Louis Bossart 		goto error;
958c46302ecSVinod Koul 
959c46302ecSVinod Koul 	sconfig.direction = dir;
960c46302ecSVinod Koul 	sconfig.ch_count = ch;
961c46302ecSVinod Koul 	sconfig.frame_rate = params_rate(params);
962c46302ecSVinod Koul 	sconfig.type = dma->stream_type;
963c46302ecSVinod Koul 
964c46302ecSVinod Koul 	if (dma->stream_type == SDW_STREAM_PDM) {
965c46302ecSVinod Koul 		sconfig.frame_rate *= 50;
966c46302ecSVinod Koul 		sconfig.bps = 1;
967c46302ecSVinod Koul 	} else {
968c46302ecSVinod Koul 		sconfig.bps = snd_pcm_format_width(params_format(params));
969c46302ecSVinod Koul 	}
970c46302ecSVinod Koul 
971c46302ecSVinod Koul 	/* Port configuration */
972235ae89bSZheng Yongjun 	pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
973c46302ecSVinod Koul 	if (!pconfig) {
974c46302ecSVinod Koul 		ret =  -ENOMEM;
97557a34790SPierre-Louis Bossart 		goto error;
976c46302ecSVinod Koul 	}
977c46302ecSVinod Koul 
97857a34790SPierre-Louis Bossart 	pconfig->num = pdi->num;
97957a34790SPierre-Louis Bossart 	pconfig->ch_mask = (1 << ch) - 1;
980c46302ecSVinod Koul 
981c46302ecSVinod Koul 	ret = sdw_stream_add_master(&cdns->bus, &sconfig,
98257a34790SPierre-Louis Bossart 				    pconfig, 1, dma->stream);
98357a34790SPierre-Louis Bossart 	if (ret)
98417ed5befSPierre-Louis Bossart 		dev_err(cdns->dev, "add master to stream failed:%d\n", ret);
985c46302ecSVinod Koul 
986c46302ecSVinod Koul 	kfree(pconfig);
98757a34790SPierre-Louis Bossart error:
988c46302ecSVinod Koul 	return ret;
989c46302ecSVinod Koul }
990c46302ecSVinod Koul 
99127b198f4SRander Wang static int intel_prepare(struct snd_pcm_substream *substream,
99227b198f4SRander Wang 			 struct snd_soc_dai *dai)
99327b198f4SRander Wang {
994a5a0239cSBard Liao 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
995a5a0239cSBard Liao 	struct sdw_intel *sdw = cdns_to_intel(cdns);
99627b198f4SRander Wang 	struct sdw_cdns_dma_data *dma;
997a5a0239cSBard Liao 	int ch, dir;
998244eb888SPierre-Louis Bossart 	int ret = 0;
99927b198f4SRander Wang 
100027b198f4SRander Wang 	dma = snd_soc_dai_get_dma_data(dai, substream);
100127b198f4SRander Wang 	if (!dma) {
10024e3ea93eSPierre-Louis Bossart 		dev_err(dai->dev, "failed to get dma data in %s\n",
100327b198f4SRander Wang 			__func__);
100427b198f4SRander Wang 		return -EIO;
100527b198f4SRander Wang 	}
100627b198f4SRander Wang 
1007a5a0239cSBard Liao 	if (dma->suspended) {
1008a5a0239cSBard Liao 		dma->suspended = false;
1009a5a0239cSBard Liao 
1010a5a0239cSBard Liao 		/*
1011a5a0239cSBard Liao 		 * .prepare() is called after system resume, where we
1012a5a0239cSBard Liao 		 * need to reinitialize the SHIM/ALH/Cadence IP.
1013a5a0239cSBard Liao 		 * .prepare() is also called to deal with underflows,
1014a5a0239cSBard Liao 		 * but in those cases we cannot touch ALH/SHIM
1015a5a0239cSBard Liao 		 * registers
1016a5a0239cSBard Liao 		 */
1017a5a0239cSBard Liao 
1018a5a0239cSBard Liao 		/* configure stream */
1019a5a0239cSBard Liao 		ch = params_channels(dma->hw_params);
1020a5a0239cSBard Liao 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
1021a5a0239cSBard Liao 			dir = SDW_DATA_DIR_RX;
1022a5a0239cSBard Liao 		else
1023a5a0239cSBard Liao 			dir = SDW_DATA_DIR_TX;
1024a5a0239cSBard Liao 
1025a5a0239cSBard Liao 		intel_pdi_shim_configure(sdw, dma->pdi);
1026a5a0239cSBard Liao 		intel_pdi_alh_configure(sdw, dma->pdi);
1027a5a0239cSBard Liao 		sdw_cdns_config_stream(cdns, ch, dir, dma->pdi);
1028a5a0239cSBard Liao 
1029a5a0239cSBard Liao 		/* Inform DSP about PDI stream number */
1030a5a0239cSBard Liao 		ret = intel_params_stream(sdw, substream, dai,
1031a5a0239cSBard Liao 					  dma->hw_params,
1032a5a0239cSBard Liao 					  sdw->instance,
1033a5a0239cSBard Liao 					  dma->pdi->intel_alh_id);
1034a5a0239cSBard Liao 	}
1035a5a0239cSBard Liao 
1036a5a0239cSBard Liao 	return ret;
103727b198f4SRander Wang }
103827b198f4SRander Wang 
1039c46302ecSVinod Koul static int
1040c46302ecSVinod Koul intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
1041c46302ecSVinod Koul {
1042c46302ecSVinod Koul 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
1043eff346f2SPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1044c46302ecSVinod Koul 	struct sdw_cdns_dma_data *dma;
1045c46302ecSVinod Koul 	int ret;
1046c46302ecSVinod Koul 
1047c46302ecSVinod Koul 	dma = snd_soc_dai_get_dma_data(dai, substream);
1048c46302ecSVinod Koul 	if (!dma)
1049c46302ecSVinod Koul 		return -EIO;
1050c46302ecSVinod Koul 
1051244eb888SPierre-Louis Bossart 	/*
1052244eb888SPierre-Louis Bossart 	 * The sdw stream state will transition to RELEASED when stream->
1053244eb888SPierre-Louis Bossart 	 * master_list is empty. So the stream state will transition to
1054244eb888SPierre-Louis Bossart 	 * DEPREPARED for the first cpu-dai and to RELEASED for the last
1055244eb888SPierre-Louis Bossart 	 * cpu-dai.
1056244eb888SPierre-Louis Bossart 	 */
1057c46302ecSVinod Koul 	ret = sdw_stream_remove_master(&cdns->bus, dma->stream);
1058eff346f2SPierre-Louis Bossart 	if (ret < 0) {
105917ed5befSPierre-Louis Bossart 		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
1060c46302ecSVinod Koul 			dma->stream->name, ret);
1061c46302ecSVinod Koul 		return ret;
1062c46302ecSVinod Koul 	}
1063c46302ecSVinod Koul 
1064eff346f2SPierre-Louis Bossart 	ret = intel_free_stream(sdw, substream, dai, sdw->instance);
1065eff346f2SPierre-Louis Bossart 	if (ret < 0) {
10664e3ea93eSPierre-Louis Bossart 		dev_err(dai->dev, "intel_free_stream: failed %d\n", ret);
1067eff346f2SPierre-Louis Bossart 		return ret;
1068eff346f2SPierre-Louis Bossart 	}
1069eff346f2SPierre-Louis Bossart 
1070a5a0239cSBard Liao 	dma->hw_params = NULL;
1071a5a0239cSBard Liao 	dma->pdi = NULL;
1072a5a0239cSBard Liao 
1073eff346f2SPierre-Louis Bossart 	return 0;
1074eff346f2SPierre-Louis Bossart }
1075eff346f2SPierre-Louis Bossart 
1076183c7687SPierre-Louis Bossart static void intel_shutdown(struct snd_pcm_substream *substream,
1077183c7687SPierre-Louis Bossart 			   struct snd_soc_dai *dai)
1078183c7687SPierre-Louis Bossart {
1079ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
1080183c7687SPierre-Louis Bossart 
1081ebf878edSPierre-Louis Bossart 	pm_runtime_mark_last_busy(cdns->dev);
1082ebf878edSPierre-Louis Bossart 	pm_runtime_put_autosuspend(cdns->dev);
1083183c7687SPierre-Louis Bossart }
1084183c7687SPierre-Louis Bossart 
1085a5a0239cSBard Liao static int intel_component_dais_suspend(struct snd_soc_component *component)
1086a5a0239cSBard Liao {
1087a5a0239cSBard Liao 	struct sdw_cdns_dma_data *dma;
1088a5a0239cSBard Liao 	struct snd_soc_dai *dai;
1089a5a0239cSBard Liao 
1090a5a0239cSBard Liao 	for_each_component_dais(component, dai) {
1091a5a0239cSBard Liao 		/*
1092a5a0239cSBard Liao 		 * we don't have a .suspend dai_ops, and we don't have access
1093a5a0239cSBard Liao 		 * to the substream, so let's mark both capture and playback
1094a5a0239cSBard Liao 		 * DMA contexts as suspended
1095a5a0239cSBard Liao 		 */
1096a5a0239cSBard Liao 		dma = dai->playback_dma_data;
1097a5a0239cSBard Liao 		if (dma)
1098a5a0239cSBard Liao 			dma->suspended = true;
1099a5a0239cSBard Liao 
1100a5a0239cSBard Liao 		dma = dai->capture_dma_data;
1101a5a0239cSBard Liao 		if (dma)
1102a5a0239cSBard Liao 			dma->suspended = true;
1103a5a0239cSBard Liao 	}
1104a5a0239cSBard Liao 
1105a5a0239cSBard Liao 	return 0;
1106a5a0239cSBard Liao }
1107a5a0239cSBard Liao 
1108c46302ecSVinod Koul static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
1109c46302ecSVinod Koul 				    void *stream, int direction)
1110c46302ecSVinod Koul {
1111c46302ecSVinod Koul 	return cdns_set_sdw_stream(dai, stream, true, direction);
1112c46302ecSVinod Koul }
1113c46302ecSVinod Koul 
1114c46302ecSVinod Koul static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai,
1115c46302ecSVinod Koul 				    void *stream, int direction)
1116c46302ecSVinod Koul {
1117c46302ecSVinod Koul 	return cdns_set_sdw_stream(dai, stream, false, direction);
1118c46302ecSVinod Koul }
1119c46302ecSVinod Koul 
112009553140SPierre-Louis Bossart static void *intel_get_sdw_stream(struct snd_soc_dai *dai,
112109553140SPierre-Louis Bossart 				  int direction)
112209553140SPierre-Louis Bossart {
112309553140SPierre-Louis Bossart 	struct sdw_cdns_dma_data *dma;
112409553140SPierre-Louis Bossart 
112509553140SPierre-Louis Bossart 	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
112609553140SPierre-Louis Bossart 		dma = dai->playback_dma_data;
112709553140SPierre-Louis Bossart 	else
112809553140SPierre-Louis Bossart 		dma = dai->capture_dma_data;
112909553140SPierre-Louis Bossart 
113009553140SPierre-Louis Bossart 	if (!dma)
113106dcb4e4SPierre-Louis Bossart 		return ERR_PTR(-EINVAL);
113209553140SPierre-Louis Bossart 
113309553140SPierre-Louis Bossart 	return dma->stream;
113409553140SPierre-Louis Bossart }
113509553140SPierre-Louis Bossart 
1136b1635596SJulia Lawall static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
11375e7484d0SRander Wang 	.startup = intel_startup,
1138c46302ecSVinod Koul 	.hw_params = intel_hw_params,
113927b198f4SRander Wang 	.prepare = intel_prepare,
1140c46302ecSVinod Koul 	.hw_free = intel_hw_free,
1141183c7687SPierre-Louis Bossart 	.shutdown = intel_shutdown,
1142c46302ecSVinod Koul 	.set_sdw_stream = intel_pcm_set_sdw_stream,
114309553140SPierre-Louis Bossart 	.get_sdw_stream = intel_get_sdw_stream,
1144c46302ecSVinod Koul };
1145c46302ecSVinod Koul 
1146b1635596SJulia Lawall static const struct snd_soc_dai_ops intel_pdm_dai_ops = {
11475e7484d0SRander Wang 	.startup = intel_startup,
1148c46302ecSVinod Koul 	.hw_params = intel_hw_params,
114927b198f4SRander Wang 	.prepare = intel_prepare,
1150c46302ecSVinod Koul 	.hw_free = intel_hw_free,
1151183c7687SPierre-Louis Bossart 	.shutdown = intel_shutdown,
1152c46302ecSVinod Koul 	.set_sdw_stream = intel_pdm_set_sdw_stream,
115309553140SPierre-Louis Bossart 	.get_sdw_stream = intel_get_sdw_stream,
1154c46302ecSVinod Koul };
1155c46302ecSVinod Koul 
1156c46302ecSVinod Koul static const struct snd_soc_component_driver dai_component = {
1157c46302ecSVinod Koul 	.name           = "soundwire",
1158a5a0239cSBard Liao 	.suspend	= intel_component_dais_suspend
1159c46302ecSVinod Koul };
1160c46302ecSVinod Koul 
1161c46302ecSVinod Koul static int intel_create_dai(struct sdw_cdns *cdns,
1162c46302ecSVinod Koul 			    struct snd_soc_dai_driver *dais,
1163c46302ecSVinod Koul 			    enum intel_pdi_type type,
1164c46302ecSVinod Koul 			    u32 num, u32 off, u32 max_ch, bool pcm)
1165c46302ecSVinod Koul {
1166c46302ecSVinod Koul 	int i;
1167c46302ecSVinod Koul 
1168c46302ecSVinod Koul 	if (num == 0)
1169c46302ecSVinod Koul 		return 0;
1170c46302ecSVinod Koul 
1171c46302ecSVinod Koul 	 /* TODO: Read supported rates/formats from hardware */
1172c46302ecSVinod Koul 	for (i = off; i < (off + num); i++) {
1173bf6d6e68SPierre-Louis Bossart 		dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL,
1174bf6d6e68SPierre-Louis Bossart 					      "SDW%d Pin%d",
1175c46302ecSVinod Koul 					      cdns->instance, i);
1176c46302ecSVinod Koul 		if (!dais[i].name)
1177c46302ecSVinod Koul 			return -ENOMEM;
1178c46302ecSVinod Koul 
1179c46302ecSVinod Koul 		if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) {
1180c46302ecSVinod Koul 			dais[i].playback.channels_min = 1;
1181c46302ecSVinod Koul 			dais[i].playback.channels_max = max_ch;
1182c46302ecSVinod Koul 			dais[i].playback.rates = SNDRV_PCM_RATE_48000;
1183c46302ecSVinod Koul 			dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
1184c46302ecSVinod Koul 		}
1185c46302ecSVinod Koul 
1186c46302ecSVinod Koul 		if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) {
118739194128SSrinivas Kandagatla 			dais[i].capture.channels_min = 1;
118839194128SSrinivas Kandagatla 			dais[i].capture.channels_max = max_ch;
1189c46302ecSVinod Koul 			dais[i].capture.rates = SNDRV_PCM_RATE_48000;
1190c46302ecSVinod Koul 			dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
1191c46302ecSVinod Koul 		}
1192c46302ecSVinod Koul 
1193c46302ecSVinod Koul 		if (pcm)
1194c46302ecSVinod Koul 			dais[i].ops = &intel_pcm_dai_ops;
1195c46302ecSVinod Koul 		else
1196c46302ecSVinod Koul 			dais[i].ops = &intel_pdm_dai_ops;
1197c46302ecSVinod Koul 	}
1198c46302ecSVinod Koul 
1199c46302ecSVinod Koul 	return 0;
1200c46302ecSVinod Koul }
1201c46302ecSVinod Koul 
1202c46302ecSVinod Koul static int intel_register_dai(struct sdw_intel *sdw)
1203c46302ecSVinod Koul {
1204c46302ecSVinod Koul 	struct sdw_cdns *cdns = &sdw->cdns;
1205c46302ecSVinod Koul 	struct sdw_cdns_streams *stream;
1206c46302ecSVinod Koul 	struct snd_soc_dai_driver *dais;
1207c46302ecSVinod Koul 	int num_dai, ret, off = 0;
1208c46302ecSVinod Koul 
1209c46302ecSVinod Koul 	/* DAIs are created based on total number of PDIs supported */
1210c46302ecSVinod Koul 	num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi;
1211c46302ecSVinod Koul 
1212c46302ecSVinod Koul 	dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL);
1213c46302ecSVinod Koul 	if (!dais)
1214c46302ecSVinod Koul 		return -ENOMEM;
1215c46302ecSVinod Koul 
1216c46302ecSVinod Koul 	/* Create PCM DAIs */
1217c46302ecSVinod Koul 	stream = &cdns->pcm;
1218c46302ecSVinod Koul 
1219cf924962SBard Liao 	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in,
12201215daeeSVinod Koul 			       off, stream->num_ch_in, true);
1221c46302ecSVinod Koul 	if (ret)
1222c46302ecSVinod Koul 		return ret;
1223c46302ecSVinod Koul 
1224c46302ecSVinod Koul 	off += cdns->pcm.num_in;
12251215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out,
12261215daeeSVinod Koul 			       off, stream->num_ch_out, true);
1227c46302ecSVinod Koul 	if (ret)
1228c46302ecSVinod Koul 		return ret;
1229c46302ecSVinod Koul 
1230c46302ecSVinod Koul 	off += cdns->pcm.num_out;
12311215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd,
12321215daeeSVinod Koul 			       off, stream->num_ch_bd, true);
1233c46302ecSVinod Koul 	if (ret)
1234c46302ecSVinod Koul 		return ret;
1235c46302ecSVinod Koul 
1236c46302ecSVinod Koul 	/* Create PDM DAIs */
1237c46302ecSVinod Koul 	stream = &cdns->pdm;
1238c46302ecSVinod Koul 	off += cdns->pcm.num_bd;
12391215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pdm.num_in,
12401215daeeSVinod Koul 			       off, stream->num_ch_in, false);
1241c46302ecSVinod Koul 	if (ret)
1242c46302ecSVinod Koul 		return ret;
1243c46302ecSVinod Koul 
1244c46302ecSVinod Koul 	off += cdns->pdm.num_in;
12451215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pdm.num_out,
12461215daeeSVinod Koul 			       off, stream->num_ch_out, false);
1247c46302ecSVinod Koul 	if (ret)
1248c46302ecSVinod Koul 		return ret;
1249c46302ecSVinod Koul 
1250cf924962SBard Liao 	off += cdns->pdm.num_out;
12511215daeeSVinod Koul 	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pdm.num_bd,
12521215daeeSVinod Koul 			       off, stream->num_ch_bd, false);
1253c46302ecSVinod Koul 	if (ret)
1254c46302ecSVinod Koul 		return ret;
1255c46302ecSVinod Koul 
1256c46302ecSVinod Koul 	return snd_soc_register_component(cdns->dev, &dai_component,
1257c46302ecSVinod Koul 					  dais, num_dai);
1258c46302ecSVinod Koul }
1259c46302ecSVinod Koul 
1260085f4aceSPierre-Louis Bossart static int sdw_master_read_intel_prop(struct sdw_bus *bus)
1261085f4aceSPierre-Louis Bossart {
1262085f4aceSPierre-Louis Bossart 	struct sdw_master_prop *prop = &bus->prop;
1263085f4aceSPierre-Louis Bossart 	struct fwnode_handle *link;
1264085f4aceSPierre-Louis Bossart 	char name[32];
1265395713d8SPierre-Louis Bossart 	u32 quirk_mask;
1266085f4aceSPierre-Louis Bossart 
1267085f4aceSPierre-Louis Bossart 	/* Find master handle */
1268085f4aceSPierre-Louis Bossart 	snprintf(name, sizeof(name),
1269085f4aceSPierre-Louis Bossart 		 "mipi-sdw-link-%d-subproperties", bus->link_id);
1270085f4aceSPierre-Louis Bossart 
1271085f4aceSPierre-Louis Bossart 	link = device_get_named_child_node(bus->dev, name);
1272085f4aceSPierre-Louis Bossart 	if (!link) {
1273085f4aceSPierre-Louis Bossart 		dev_err(bus->dev, "Master node %s not found\n", name);
1274085f4aceSPierre-Louis Bossart 		return -EIO;
1275085f4aceSPierre-Louis Bossart 	}
1276085f4aceSPierre-Louis Bossart 
1277085f4aceSPierre-Louis Bossart 	fwnode_property_read_u32(link,
1278085f4aceSPierre-Louis Bossart 				 "intel-sdw-ip-clock",
1279085f4aceSPierre-Louis Bossart 				 &prop->mclk_freq);
1280395713d8SPierre-Louis Bossart 
1281a19efb52SBard Liao 	/* the values reported by BIOS are the 2x clock, not the bus clock */
1282a19efb52SBard Liao 	prop->mclk_freq /= 2;
1283a19efb52SBard Liao 
1284395713d8SPierre-Louis Bossart 	fwnode_property_read_u32(link,
1285395713d8SPierre-Louis Bossart 				 "intel-quirk-mask",
1286395713d8SPierre-Louis Bossart 				 &quirk_mask);
1287395713d8SPierre-Louis Bossart 
1288395713d8SPierre-Louis Bossart 	if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
1289395713d8SPierre-Louis Bossart 		prop->hw_disabled = true;
1290395713d8SPierre-Louis Bossart 
1291bb877bebSBard Liao 	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
1292bb877bebSBard Liao 		SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
1293bb877bebSBard Liao 
1294085f4aceSPierre-Louis Bossart 	return 0;
1295085f4aceSPierre-Louis Bossart }
1296085f4aceSPierre-Louis Bossart 
129771bb8a1bSVinod Koul static int intel_prop_read(struct sdw_bus *bus)
129871bb8a1bSVinod Koul {
129971bb8a1bSVinod Koul 	/* Initialize with default handler to read all DisCo properties */
130071bb8a1bSVinod Koul 	sdw_master_read_prop(bus);
130171bb8a1bSVinod Koul 
1302085f4aceSPierre-Louis Bossart 	/* read Intel-specific properties */
1303085f4aceSPierre-Louis Bossart 	sdw_master_read_intel_prop(bus);
1304085f4aceSPierre-Louis Bossart 
130571bb8a1bSVinod Koul 	return 0;
130671bb8a1bSVinod Koul }
130771bb8a1bSVinod Koul 
1308c91605f4SShreyas NC static struct sdw_master_ops sdw_intel_ops = {
1309c91605f4SShreyas NC 	.read_prop = sdw_master_read_prop,
1310f6594cdfSPierre-Louis Bossart 	.override_adr = sdw_dmi_override_adr,
1311c91605f4SShreyas NC 	.xfer_msg = cdns_xfer_msg,
1312c91605f4SShreyas NC 	.xfer_msg_defer = cdns_xfer_msg_defer,
1313c91605f4SShreyas NC 	.reset_page_addr = cdns_reset_page_addr,
131407abeff1SVinod Koul 	.set_bus_conf = cdns_bus_conf,
131530246e2dSShreyas NC 	.pre_bank_switch = intel_pre_bank_switch,
131630246e2dSShreyas NC 	.post_bank_switch = intel_post_bank_switch,
1317c91605f4SShreyas NC };
1318c91605f4SShreyas NC 
1319dfbe642dSPierre-Louis Bossart static int intel_init(struct sdw_intel *sdw)
1320dfbe642dSPierre-Louis Bossart {
13214a17c441SPierre-Louis Bossart 	bool clock_stop;
13224a17c441SPierre-Louis Bossart 
1323dfbe642dSPierre-Louis Bossart 	/* Initialize shim and controller */
1324dfbe642dSPierre-Louis Bossart 	intel_link_power_up(sdw);
13254a17c441SPierre-Louis Bossart 
13264a17c441SPierre-Louis Bossart 	clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns);
13274a17c441SPierre-Louis Bossart 
13284a17c441SPierre-Louis Bossart 	intel_shim_init(sdw, clock_stop);
13294a17c441SPierre-Louis Bossart 
13304a17c441SPierre-Louis Bossart 	return 0;
1331dfbe642dSPierre-Louis Bossart }
1332dfbe642dSPierre-Louis Bossart 
133371bb8a1bSVinod Koul /*
133429a269c6SPierre-Louis Bossart  * probe and init (aux_dev_id argument is required by function prototype but not used)
133571bb8a1bSVinod Koul  */
133629a269c6SPierre-Louis Bossart static int intel_link_probe(struct auxiliary_device *auxdev,
133729a269c6SPierre-Louis Bossart 			    const struct auxiliary_device_id *aux_dev_id)
133829a269c6SPierre-Louis Bossart 
133971bb8a1bSVinod Koul {
134029a269c6SPierre-Louis Bossart 	struct device *dev = &auxdev->dev;
134129a269c6SPierre-Louis Bossart 	struct sdw_intel_link_dev *ldev = auxiliary_dev_to_sdw_intel_link_dev(auxdev);
134271bb8a1bSVinod Koul 	struct sdw_intel *sdw;
134383e129afSPierre-Louis Bossart 	struct sdw_cdns *cdns;
1344b6109dd6SPierre-Louis Bossart 	struct sdw_bus *bus;
134571bb8a1bSVinod Koul 	int ret;
134671bb8a1bSVinod Koul 
1347b6109dd6SPierre-Louis Bossart 	sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL);
134871bb8a1bSVinod Koul 	if (!sdw)
134971bb8a1bSVinod Koul 		return -ENOMEM;
135071bb8a1bSVinod Koul 
135183e129afSPierre-Louis Bossart 	cdns = &sdw->cdns;
135283e129afSPierre-Louis Bossart 	bus = &cdns->bus;
135371bb8a1bSVinod Koul 
135429a269c6SPierre-Louis Bossart 	sdw->instance = auxdev->id;
135529a269c6SPierre-Louis Bossart 	sdw->link_res = &ldev->link_res;
135683e129afSPierre-Louis Bossart 	cdns->dev = dev;
135783e129afSPierre-Louis Bossart 	cdns->registers = sdw->link_res->registers;
135883e129afSPierre-Louis Bossart 	cdns->instance = sdw->instance;
135983e129afSPierre-Louis Bossart 	cdns->msg_count = 0;
136083e129afSPierre-Louis Bossart 
136129a269c6SPierre-Louis Bossart 	bus->link_id = auxdev->id;
136271bb8a1bSVinod Koul 
136383e129afSPierre-Louis Bossart 	sdw_cdns_probe(cdns);
136471bb8a1bSVinod Koul 
136571bb8a1bSVinod Koul 	/* Set property read ops */
1366c91605f4SShreyas NC 	sdw_intel_ops.read_prop = intel_prop_read;
1367b6109dd6SPierre-Louis Bossart 	bus->ops = &sdw_intel_ops;
136871bb8a1bSVinod Koul 
1369b6109dd6SPierre-Louis Bossart 	/* set driver data, accessed by snd_soc_dai_get_drvdata() */
137083e129afSPierre-Louis Bossart 	dev_set_drvdata(dev, cdns);
137171bb8a1bSVinod Koul 
13729026118fSBard Liao 	/* use generic bandwidth allocation algorithm */
13739026118fSBard Liao 	sdw->cdns.bus.compute_params = sdw_compute_params;
13749026118fSBard Liao 
1375b6109dd6SPierre-Louis Bossart 	ret = sdw_bus_master_add(bus, dev, dev->fwnode);
137671bb8a1bSVinod Koul 	if (ret) {
1377b6109dd6SPierre-Louis Bossart 		dev_err(dev, "sdw_bus_master_add fail: %d\n", ret);
13789e3d47fbSPierre-Louis Bossart 		return ret;
137971bb8a1bSVinod Koul 	}
138071bb8a1bSVinod Koul 
13816d2c6669SPierre-Louis Bossart 	if (bus->prop.hw_disabled)
1382b6109dd6SPierre-Louis Bossart 		dev_info(dev,
1383b6109dd6SPierre-Louis Bossart 			 "SoundWire master %d is disabled, will be ignored\n",
1384b6109dd6SPierre-Louis Bossart 			 bus->link_id);
13850ef2986eSPierre-Louis Bossart 	/*
13860ef2986eSPierre-Louis Bossart 	 * Ignore BIOS err_threshold, it's a really bad idea when dealing
13870ef2986eSPierre-Louis Bossart 	 * with multiple hardware synchronized links
13880ef2986eSPierre-Louis Bossart 	 */
13890ef2986eSPierre-Louis Bossart 	bus->prop.err_threshold = 0;
13906d2c6669SPierre-Louis Bossart 
13916d2c6669SPierre-Louis Bossart 	return 0;
13926d2c6669SPierre-Louis Bossart }
13936d2c6669SPierre-Louis Bossart 
139429a269c6SPierre-Louis Bossart int intel_link_startup(struct auxiliary_device *auxdev)
13956d2c6669SPierre-Louis Bossart {
13966d2c6669SPierre-Louis Bossart 	struct sdw_cdns_stream_config config;
139729a269c6SPierre-Louis Bossart 	struct device *dev = &auxdev->dev;
13986d2c6669SPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
13996d2c6669SPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
14006d2c6669SPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1401ebf878edSPierre-Louis Bossart 	int link_flags;
1402857a7c42SPierre-Louis Bossart 	bool multi_link;
1403caf68819SPierre-Louis Bossart 	u32 clock_stop_quirks;
14046d2c6669SPierre-Louis Bossart 	int ret;
14056d2c6669SPierre-Louis Bossart 
14066d2c6669SPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
14076d2c6669SPierre-Louis Bossart 		dev_info(dev,
14086d2c6669SPierre-Louis Bossart 			 "SoundWire master %d is disabled, ignoring\n",
14096d2c6669SPierre-Louis Bossart 			 sdw->instance);
1410395713d8SPierre-Louis Bossart 		return 0;
1411395713d8SPierre-Louis Bossart 	}
1412395713d8SPierre-Louis Bossart 
1413857a7c42SPierre-Louis Bossart 	link_flags = md_flags >> (bus->link_id * 8);
1414857a7c42SPierre-Louis Bossart 	multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
1415857a7c42SPierre-Louis Bossart 	if (!multi_link) {
1416857a7c42SPierre-Louis Bossart 		dev_dbg(dev, "Multi-link is disabled\n");
1417857a7c42SPierre-Louis Bossart 		bus->multi_link = false;
1418857a7c42SPierre-Louis Bossart 	} else {
141994eed661SPierre-Louis Bossart 		/*
142094eed661SPierre-Louis Bossart 		 * hardware-based synchronization is required regardless
142194eed661SPierre-Louis Bossart 		 * of the number of segments used by a stream: SSP-based
142294eed661SPierre-Louis Bossart 		 * synchronization is gated by gsync when the multi-master
142394eed661SPierre-Louis Bossart 		 * mode is set.
142494eed661SPierre-Louis Bossart 		 */
1425857a7c42SPierre-Louis Bossart 		bus->multi_link = true;
142694eed661SPierre-Louis Bossart 		bus->hw_sync_min_links = 1;
1427857a7c42SPierre-Louis Bossart 	}
1428857a7c42SPierre-Louis Bossart 
1429857a7c42SPierre-Louis Bossart 	/* Initialize shim, controller */
1430dfbe642dSPierre-Louis Bossart 	ret = intel_init(sdw);
143171bb8a1bSVinod Koul 	if (ret)
143271bb8a1bSVinod Koul 		goto err_init;
143371bb8a1bSVinod Koul 
143437a2d22bSVinod Koul 	/* Read the PDI config and initialize cadence PDI */
143537a2d22bSVinod Koul 	intel_pdi_init(sdw, &config);
143683e129afSPierre-Louis Bossart 	ret = sdw_cdns_pdi_init(cdns, config);
143771bb8a1bSVinod Koul 	if (ret)
143871bb8a1bSVinod Koul 		goto err_init;
143971bb8a1bSVinod Koul 
144037a2d22bSVinod Koul 	intel_pdi_ch_update(sdw);
144137a2d22bSVinod Koul 
144283e129afSPierre-Louis Bossart 	ret = sdw_cdns_enable_interrupt(cdns, true);
144371bb8a1bSVinod Koul 	if (ret < 0) {
1444b6109dd6SPierre-Louis Bossart 		dev_err(dev, "cannot enable interrupts\n");
144571bb8a1bSVinod Koul 		goto err_init;
144671bb8a1bSVinod Koul 	}
144771bb8a1bSVinod Koul 
1448857a7c42SPierre-Louis Bossart 	/*
1449857a7c42SPierre-Louis Bossart 	 * follow recommended programming flows to avoid timeouts when
1450857a7c42SPierre-Louis Bossart 	 * gsync is enabled
1451857a7c42SPierre-Louis Bossart 	 */
1452857a7c42SPierre-Louis Bossart 	if (multi_link)
1453857a7c42SPierre-Louis Bossart 		intel_shim_sync_arm(sdw);
1454857a7c42SPierre-Louis Bossart 
1455857a7c42SPierre-Louis Bossart 	ret = sdw_cdns_init(cdns);
1456857a7c42SPierre-Louis Bossart 	if (ret < 0) {
1457857a7c42SPierre-Louis Bossart 		dev_err(dev, "unable to initialize Cadence IP\n");
1458857a7c42SPierre-Louis Bossart 		goto err_interrupt;
1459857a7c42SPierre-Louis Bossart 	}
1460857a7c42SPierre-Louis Bossart 
146183e129afSPierre-Louis Bossart 	ret = sdw_cdns_exit_reset(cdns);
146249ea07d3SPierre-Louis Bossart 	if (ret < 0) {
1463b6109dd6SPierre-Louis Bossart 		dev_err(dev, "unable to exit bus reset sequence\n");
14649e3d47fbSPierre-Louis Bossart 		goto err_interrupt;
146549ea07d3SPierre-Louis Bossart 	}
146649ea07d3SPierre-Louis Bossart 
1467857a7c42SPierre-Louis Bossart 	if (multi_link) {
1468857a7c42SPierre-Louis Bossart 		ret = intel_shim_sync_go(sdw);
1469857a7c42SPierre-Louis Bossart 		if (ret < 0) {
1470857a7c42SPierre-Louis Bossart 			dev_err(dev, "sync go failed: %d\n", ret);
1471857a7c42SPierre-Louis Bossart 			goto err_interrupt;
1472857a7c42SPierre-Louis Bossart 		}
1473857a7c42SPierre-Louis Bossart 	}
1474ff560946SPierre-Louis Bossart 	sdw_cdns_check_self_clearing_bits(cdns, __func__,
1475ff560946SPierre-Louis Bossart 					  true, INTEL_MASTER_RESET_ITERATIONS);
1476857a7c42SPierre-Louis Bossart 
1477c46302ecSVinod Koul 	/* Register DAIs */
1478c46302ecSVinod Koul 	ret = intel_register_dai(sdw);
1479c46302ecSVinod Koul 	if (ret) {
1480b6109dd6SPierre-Louis Bossart 		dev_err(dev, "DAI registration failed: %d\n", ret);
1481b6109dd6SPierre-Louis Bossart 		snd_soc_unregister_component(dev);
14829e3d47fbSPierre-Louis Bossart 		goto err_interrupt;
1483c46302ecSVinod Koul 	}
1484c46302ecSVinod Koul 
148579ee6631SPierre-Louis Bossart 	intel_debugfs_init(sdw);
148679ee6631SPierre-Louis Bossart 
1487ebf878edSPierre-Louis Bossart 	/* Enable runtime PM */
1488ebf878edSPierre-Louis Bossart 	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) {
1489ebf878edSPierre-Louis Bossart 		pm_runtime_set_autosuspend_delay(dev,
1490ebf878edSPierre-Louis Bossart 						 INTEL_MASTER_SUSPEND_DELAY_MS);
1491ebf878edSPierre-Louis Bossart 		pm_runtime_use_autosuspend(dev);
1492ebf878edSPierre-Louis Bossart 		pm_runtime_mark_last_busy(dev);
1493ebf878edSPierre-Louis Bossart 
1494ebf878edSPierre-Louis Bossart 		pm_runtime_set_active(dev);
1495ebf878edSPierre-Louis Bossart 		pm_runtime_enable(dev);
1496ebf878edSPierre-Louis Bossart 	}
1497ebf878edSPierre-Louis Bossart 
1498caf68819SPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1499caf68819SPierre-Louis Bossart 	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_NOT_ALLOWED) {
1500caf68819SPierre-Louis Bossart 		/*
1501caf68819SPierre-Louis Bossart 		 * To keep the clock running we need to prevent
1502caf68819SPierre-Louis Bossart 		 * pm_runtime suspend from happening by increasing the
1503caf68819SPierre-Louis Bossart 		 * reference count.
1504caf68819SPierre-Louis Bossart 		 * This quirk is specified by the parent PCI device in
1505caf68819SPierre-Louis Bossart 		 * case of specific latency requirements. It will have
1506caf68819SPierre-Louis Bossart 		 * no effect if pm_runtime is disabled by the user via
1507caf68819SPierre-Louis Bossart 		 * a module parameter for testing purposes.
1508caf68819SPierre-Louis Bossart 		 */
1509caf68819SPierre-Louis Bossart 		pm_runtime_get_noresume(dev);
1510caf68819SPierre-Louis Bossart 	}
1511caf68819SPierre-Louis Bossart 
1512a2d9c161SPierre-Louis Bossart 	/*
1513a2d9c161SPierre-Louis Bossart 	 * The runtime PM status of Slave devices is "Unsupported"
1514a2d9c161SPierre-Louis Bossart 	 * until they report as ATTACHED. If they don't, e.g. because
1515a2d9c161SPierre-Louis Bossart 	 * there are no Slave devices populated or if the power-on is
1516a2d9c161SPierre-Louis Bossart 	 * delayed or dependent on a power switch, the Master will
1517a2d9c161SPierre-Louis Bossart 	 * remain active and prevent its parent from suspending.
1518a2d9c161SPierre-Louis Bossart 	 *
1519a2d9c161SPierre-Louis Bossart 	 * Conditionally force the pm_runtime core to re-evaluate the
1520a2d9c161SPierre-Louis Bossart 	 * Master status in the absence of any Slave activity. A quirk
1521a2d9c161SPierre-Louis Bossart 	 * is provided to e.g. deal with Slaves that may be powered on
1522a2d9c161SPierre-Louis Bossart 	 * with a delay. A more complete solution would require the
1523a2d9c161SPierre-Louis Bossart 	 * definition of Master properties.
1524a2d9c161SPierre-Louis Bossart 	 */
1525a2d9c161SPierre-Louis Bossart 	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
1526a2d9c161SPierre-Louis Bossart 		pm_runtime_idle(dev);
1527a2d9c161SPierre-Louis Bossart 
152871bb8a1bSVinod Koul 	return 0;
152971bb8a1bSVinod Koul 
15309e3d47fbSPierre-Louis Bossart err_interrupt:
153183e129afSPierre-Louis Bossart 	sdw_cdns_enable_interrupt(cdns, false);
153271bb8a1bSVinod Koul err_init:
153371bb8a1bSVinod Koul 	return ret;
153471bb8a1bSVinod Koul }
153571bb8a1bSVinod Koul 
153629a269c6SPierre-Louis Bossart static void intel_link_remove(struct auxiliary_device *auxdev)
153771bb8a1bSVinod Koul {
153829a269c6SPierre-Louis Bossart 	struct device *dev = &auxdev->dev;
153983e129afSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
154083e129afSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
154183e129afSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1542b6109dd6SPierre-Louis Bossart 
1543caf68819SPierre-Louis Bossart 	/*
1544caf68819SPierre-Louis Bossart 	 * Since pm_runtime is already disabled, we don't decrease
1545caf68819SPierre-Louis Bossart 	 * the refcount when the clock_stop_quirk is
1546caf68819SPierre-Louis Bossart 	 * SDW_INTEL_CLK_STOP_NOT_ALLOWED
1547caf68819SPierre-Louis Bossart 	 */
1548b6109dd6SPierre-Louis Bossart 	if (!bus->prop.hw_disabled) {
154979ee6631SPierre-Louis Bossart 		intel_debugfs_exit(sdw);
155083e129afSPierre-Louis Bossart 		sdw_cdns_enable_interrupt(cdns, false);
1551b6109dd6SPierre-Louis Bossart 		snd_soc_unregister_component(dev);
1552395713d8SPierre-Louis Bossart 	}
1553b6109dd6SPierre-Louis Bossart 	sdw_bus_master_delete(bus);
155471bb8a1bSVinod Koul }
155571bb8a1bSVinod Koul 
155629a269c6SPierre-Louis Bossart int intel_link_process_wakeen_event(struct auxiliary_device *auxdev)
1557ab2c9132SRander Wang {
155829a269c6SPierre-Louis Bossart 	struct device *dev = &auxdev->dev;
155971bb8a1bSVinod Koul 	struct sdw_intel *sdw;
1560ab2c9132SRander Wang 	struct sdw_bus *bus;
1561ab2c9132SRander Wang 	void __iomem *shim;
1562ab2c9132SRander Wang 	u16 wake_sts;
156371bb8a1bSVinod Koul 
156429a269c6SPierre-Louis Bossart 	sdw = dev_get_drvdata(dev);
1565ab2c9132SRander Wang 	bus = &sdw->cdns.bus;
156671bb8a1bSVinod Koul 
1567ab2c9132SRander Wang 	if (bus->prop.hw_disabled) {
1568ab2c9132SRander Wang 		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", bus->link_id);
1569ab2c9132SRander Wang 		return 0;
157071bb8a1bSVinod Koul 	}
1571ab2c9132SRander Wang 
1572ab2c9132SRander Wang 	shim = sdw->link_res->shim;
1573ab2c9132SRander Wang 	wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
1574ab2c9132SRander Wang 
1575ab2c9132SRander Wang 	if (!(wake_sts & BIT(sdw->instance)))
1576ab2c9132SRander Wang 		return 0;
1577ab2c9132SRander Wang 
1578ab2c9132SRander Wang 	/* disable WAKEEN interrupt ASAP to prevent interrupt flood */
1579ab2c9132SRander Wang 	intel_shim_wake(sdw, false);
1580ab2c9132SRander Wang 
1581ab2c9132SRander Wang 	/*
1582ab2c9132SRander Wang 	 * resume the Master, which will generate a bus reset and result in
1583ab2c9132SRander Wang 	 * Slaves re-attaching and be re-enumerated. The SoundWire physical
1584ab2c9132SRander Wang 	 * device which generated the wake will trigger an interrupt, which
1585ab2c9132SRander Wang 	 * will in turn cause the corresponding Linux Slave device to be
1586ab2c9132SRander Wang 	 * resumed and the Slave codec driver to check the status.
1587ab2c9132SRander Wang 	 */
1588ab2c9132SRander Wang 	pm_request_resume(dev);
158971bb8a1bSVinod Koul 
159071bb8a1bSVinod Koul 	return 0;
159171bb8a1bSVinod Koul }
159271bb8a1bSVinod Koul 
15939b3b4b3fSPierre-Louis Bossart /*
15949b3b4b3fSPierre-Louis Bossart  * PM calls
15959b3b4b3fSPierre-Louis Bossart  */
15969b3b4b3fSPierre-Louis Bossart 
1597f046b233SBard Liao static int __maybe_unused intel_suspend(struct device *dev)
15989b3b4b3fSPierre-Louis Bossart {
15999b3b4b3fSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
16009b3b4b3fSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
16019b3b4b3fSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1602e4be9facSPierre-Louis Bossart 	u32 clock_stop_quirks;
16039b3b4b3fSPierre-Louis Bossart 	int ret;
16049b3b4b3fSPierre-Louis Bossart 
16059b3b4b3fSPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
16069b3b4b3fSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
16079b3b4b3fSPierre-Louis Bossart 			bus->link_id);
16089b3b4b3fSPierre-Louis Bossart 		return 0;
16099b3b4b3fSPierre-Louis Bossart 	}
16109b3b4b3fSPierre-Louis Bossart 
1611b61b8b37SPierre-Louis Bossart 	if (pm_runtime_suspended(dev)) {
1612b61b8b37SPierre-Louis Bossart 		dev_dbg(dev, "%s: pm_runtime status: suspended\n", __func__);
1613b61b8b37SPierre-Louis Bossart 
1614e4be9facSPierre-Louis Bossart 		clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1615e4be9facSPierre-Louis Bossart 
1616e4be9facSPierre-Louis Bossart 		if ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET ||
1617e4be9facSPierre-Louis Bossart 		     !clock_stop_quirks) &&
1618e4be9facSPierre-Louis Bossart 		    !pm_runtime_suspended(dev->parent)) {
1619e4be9facSPierre-Louis Bossart 
1620e4be9facSPierre-Louis Bossart 			/*
1621e4be9facSPierre-Louis Bossart 			 * if we've enabled clock stop, and the parent
1622e4be9facSPierre-Louis Bossart 			 * is still active, disable shim wake. The
1623e4be9facSPierre-Louis Bossart 			 * SHIM registers are not accessible if the
1624e4be9facSPierre-Louis Bossart 			 * parent is already pm_runtime suspended so
1625e4be9facSPierre-Louis Bossart 			 * it's too late to change that configuration
1626e4be9facSPierre-Louis Bossart 			 */
1627e4be9facSPierre-Louis Bossart 
1628e4be9facSPierre-Louis Bossart 			intel_shim_wake(sdw, false);
1629e4be9facSPierre-Louis Bossart 		}
1630e4be9facSPierre-Louis Bossart 
1631b61b8b37SPierre-Louis Bossart 		return 0;
1632b61b8b37SPierre-Louis Bossart 	}
1633b61b8b37SPierre-Louis Bossart 
16349b3b4b3fSPierre-Louis Bossart 	ret = sdw_cdns_enable_interrupt(cdns, false);
16359b3b4b3fSPierre-Louis Bossart 	if (ret < 0) {
16369b3b4b3fSPierre-Louis Bossart 		dev_err(dev, "cannot disable interrupts on suspend\n");
16379b3b4b3fSPierre-Louis Bossart 		return ret;
16389b3b4b3fSPierre-Louis Bossart 	}
16399b3b4b3fSPierre-Louis Bossart 
16409b3b4b3fSPierre-Louis Bossart 	ret = intel_link_power_down(sdw);
16419b3b4b3fSPierre-Louis Bossart 	if (ret) {
16424e3ea93eSPierre-Louis Bossart 		dev_err(dev, "Link power down failed: %d\n", ret);
16439b3b4b3fSPierre-Louis Bossart 		return ret;
16449b3b4b3fSPierre-Louis Bossart 	}
16459b3b4b3fSPierre-Louis Bossart 
16469b3b4b3fSPierre-Louis Bossart 	intel_shim_wake(sdw, false);
16479b3b4b3fSPierre-Louis Bossart 
16489b3b4b3fSPierre-Louis Bossart 	return 0;
16499b3b4b3fSPierre-Louis Bossart }
16509b3b4b3fSPierre-Louis Bossart 
165117e0da0bSArnd Bergmann static int __maybe_unused intel_suspend_runtime(struct device *dev)
1652ebf878edSPierre-Louis Bossart {
1653ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
1654ebf878edSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1655ebf878edSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1656a320f41eSPierre-Louis Bossart 	u32 clock_stop_quirks;
1657ebf878edSPierre-Louis Bossart 	int ret;
1658ebf878edSPierre-Louis Bossart 
1659ebf878edSPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
1660ebf878edSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
1661ebf878edSPierre-Louis Bossart 			bus->link_id);
1662ebf878edSPierre-Louis Bossart 		return 0;
1663ebf878edSPierre-Louis Bossart 	}
1664ebf878edSPierre-Louis Bossart 
1665a320f41eSPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1666a320f41eSPierre-Louis Bossart 
1667a320f41eSPierre-Louis Bossart 	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
1668a320f41eSPierre-Louis Bossart 
1669ebf878edSPierre-Louis Bossart 		ret = sdw_cdns_enable_interrupt(cdns, false);
1670ebf878edSPierre-Louis Bossart 		if (ret < 0) {
1671ebf878edSPierre-Louis Bossart 			dev_err(dev, "cannot disable interrupts on suspend\n");
1672ebf878edSPierre-Louis Bossart 			return ret;
1673ebf878edSPierre-Louis Bossart 		}
1674ebf878edSPierre-Louis Bossart 
1675ebf878edSPierre-Louis Bossart 		ret = intel_link_power_down(sdw);
1676ebf878edSPierre-Louis Bossart 		if (ret) {
16774e3ea93eSPierre-Louis Bossart 			dev_err(dev, "Link power down failed: %d\n", ret);
1678ebf878edSPierre-Louis Bossart 			return ret;
1679ebf878edSPierre-Louis Bossart 		}
1680ebf878edSPierre-Louis Bossart 
1681ebf878edSPierre-Louis Bossart 		intel_shim_wake(sdw, false);
1682ebf878edSPierre-Louis Bossart 
168361fb830bSPierre-Louis Bossart 	} else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET ||
168461fb830bSPierre-Louis Bossart 		   !clock_stop_quirks) {
1685ee3db942SBard Liao 		bool wake_enable = true;
1686ee3db942SBard Liao 
16876626a616SRander Wang 		ret = sdw_cdns_clock_stop(cdns, true);
16886626a616SRander Wang 		if (ret < 0) {
16896626a616SRander Wang 			dev_err(dev, "cannot enable clock stop on suspend\n");
1690ee3db942SBard Liao 			wake_enable = false;
16916626a616SRander Wang 		}
16926626a616SRander Wang 
16936626a616SRander Wang 		ret = sdw_cdns_enable_interrupt(cdns, false);
16946626a616SRander Wang 		if (ret < 0) {
16956626a616SRander Wang 			dev_err(dev, "cannot disable interrupts on suspend\n");
16966626a616SRander Wang 			return ret;
16976626a616SRander Wang 		}
16986626a616SRander Wang 
16996626a616SRander Wang 		ret = intel_link_power_down(sdw);
17006626a616SRander Wang 		if (ret) {
17014e3ea93eSPierre-Louis Bossart 			dev_err(dev, "Link power down failed: %d\n", ret);
17026626a616SRander Wang 			return ret;
17036626a616SRander Wang 		}
17046626a616SRander Wang 
1705ee3db942SBard Liao 		intel_shim_wake(sdw, wake_enable);
1706a320f41eSPierre-Louis Bossart 	} else {
1707a320f41eSPierre-Louis Bossart 		dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
1708a320f41eSPierre-Louis Bossart 			__func__, clock_stop_quirks);
1709a320f41eSPierre-Louis Bossart 		ret = -EINVAL;
1710a320f41eSPierre-Louis Bossart 	}
1711a320f41eSPierre-Louis Bossart 
1712a320f41eSPierre-Louis Bossart 	return ret;
1713ebf878edSPierre-Louis Bossart }
1714ebf878edSPierre-Louis Bossart 
1715f046b233SBard Liao static int __maybe_unused intel_resume(struct device *dev)
17169b3b4b3fSPierre-Louis Bossart {
17179b3b4b3fSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
17189b3b4b3fSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
17199b3b4b3fSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1720a2d9c161SPierre-Louis Bossart 	int link_flags;
1721857a7c42SPierre-Louis Bossart 	bool multi_link;
17229b3b4b3fSPierre-Louis Bossart 	int ret;
17239b3b4b3fSPierre-Louis Bossart 
17249b3b4b3fSPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
17259b3b4b3fSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
17269b3b4b3fSPierre-Louis Bossart 			bus->link_id);
17279b3b4b3fSPierre-Louis Bossart 		return 0;
17289b3b4b3fSPierre-Louis Bossart 	}
17299b3b4b3fSPierre-Louis Bossart 
1730857a7c42SPierre-Louis Bossart 	link_flags = md_flags >> (bus->link_id * 8);
1731857a7c42SPierre-Louis Bossart 	multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
1732857a7c42SPierre-Louis Bossart 
1733b61b8b37SPierre-Louis Bossart 	if (pm_runtime_suspended(dev)) {
1734b61b8b37SPierre-Louis Bossart 		dev_dbg(dev, "%s: pm_runtime status was suspended, forcing active\n", __func__);
1735b61b8b37SPierre-Louis Bossart 
1736b61b8b37SPierre-Louis Bossart 		/* follow required sequence from runtime_pm.rst */
1737b61b8b37SPierre-Louis Bossart 		pm_runtime_disable(dev);
1738b61b8b37SPierre-Louis Bossart 		pm_runtime_set_active(dev);
1739b61b8b37SPierre-Louis Bossart 		pm_runtime_mark_last_busy(dev);
1740b61b8b37SPierre-Louis Bossart 		pm_runtime_enable(dev);
1741a2d9c161SPierre-Louis Bossart 
1742a2d9c161SPierre-Louis Bossart 		link_flags = md_flags >> (bus->link_id * 8);
1743857a7c42SPierre-Louis Bossart 
1744a2d9c161SPierre-Louis Bossart 		if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
1745a2d9c161SPierre-Louis Bossart 			pm_runtime_idle(dev);
1746b61b8b37SPierre-Louis Bossart 	}
1747b61b8b37SPierre-Louis Bossart 
17489b3b4b3fSPierre-Louis Bossart 	ret = intel_init(sdw);
17499b3b4b3fSPierre-Louis Bossart 	if (ret) {
17504e3ea93eSPierre-Louis Bossart 		dev_err(dev, "%s failed: %d\n", __func__, ret);
17519b3b4b3fSPierre-Louis Bossart 		return ret;
17529b3b4b3fSPierre-Louis Bossart 	}
17539b3b4b3fSPierre-Louis Bossart 
175499b6a30fSPierre-Louis Bossart 	/*
175599b6a30fSPierre-Louis Bossart 	 * make sure all Slaves are tagged as UNATTACHED and provide
175699b6a30fSPierre-Louis Bossart 	 * reason for reinitialization
175799b6a30fSPierre-Louis Bossart 	 */
175899b6a30fSPierre-Louis Bossart 	sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
175999b6a30fSPierre-Louis Bossart 
17609b3b4b3fSPierre-Louis Bossart 	ret = sdw_cdns_enable_interrupt(cdns, true);
17619b3b4b3fSPierre-Louis Bossart 	if (ret < 0) {
17629b3b4b3fSPierre-Louis Bossart 		dev_err(dev, "cannot enable interrupts during resume\n");
17639b3b4b3fSPierre-Louis Bossart 		return ret;
17649b3b4b3fSPierre-Louis Bossart 	}
17659b3b4b3fSPierre-Louis Bossart 
1766857a7c42SPierre-Louis Bossart 	/*
1767857a7c42SPierre-Louis Bossart 	 * follow recommended programming flows to avoid timeouts when
1768857a7c42SPierre-Louis Bossart 	 * gsync is enabled
1769857a7c42SPierre-Louis Bossart 	 */
1770857a7c42SPierre-Louis Bossart 	if (multi_link)
1771857a7c42SPierre-Louis Bossart 		intel_shim_sync_arm(sdw);
1772857a7c42SPierre-Louis Bossart 
1773857a7c42SPierre-Louis Bossart 	ret = sdw_cdns_init(&sdw->cdns);
1774857a7c42SPierre-Louis Bossart 	if (ret < 0) {
1775857a7c42SPierre-Louis Bossart 		dev_err(dev, "unable to initialize Cadence IP during resume\n");
1776857a7c42SPierre-Louis Bossart 		return ret;
1777857a7c42SPierre-Louis Bossart 	}
1778857a7c42SPierre-Louis Bossart 
17799b3b4b3fSPierre-Louis Bossart 	ret = sdw_cdns_exit_reset(cdns);
17809b3b4b3fSPierre-Louis Bossart 	if (ret < 0) {
17819b3b4b3fSPierre-Louis Bossart 		dev_err(dev, "unable to exit bus reset sequence during resume\n");
17829b3b4b3fSPierre-Louis Bossart 		return ret;
17839b3b4b3fSPierre-Louis Bossart 	}
17849b3b4b3fSPierre-Louis Bossart 
1785857a7c42SPierre-Louis Bossart 	if (multi_link) {
1786857a7c42SPierre-Louis Bossart 		ret = intel_shim_sync_go(sdw);
1787857a7c42SPierre-Louis Bossart 		if (ret < 0) {
1788857a7c42SPierre-Louis Bossart 			dev_err(dev, "sync go failed during resume\n");
1789857a7c42SPierre-Louis Bossart 			return ret;
1790857a7c42SPierre-Louis Bossart 		}
1791857a7c42SPierre-Louis Bossart 	}
1792ff560946SPierre-Louis Bossart 	sdw_cdns_check_self_clearing_bits(cdns, __func__,
1793ff560946SPierre-Louis Bossart 					  true, INTEL_MASTER_RESET_ITERATIONS);
1794857a7c42SPierre-Louis Bossart 
1795cb1e6d59SPierre-Louis Bossart 	/*
1796cb1e6d59SPierre-Louis Bossart 	 * after system resume, the pm_runtime suspend() may kick in
1797cb1e6d59SPierre-Louis Bossart 	 * during the enumeration, before any children device force the
1798cb1e6d59SPierre-Louis Bossart 	 * master device to remain active.  Using pm_runtime_get()
1799cb1e6d59SPierre-Louis Bossart 	 * routines is not really possible, since it'd prevent the
1800cb1e6d59SPierre-Louis Bossart 	 * master from suspending.
1801cb1e6d59SPierre-Louis Bossart 	 * A reasonable compromise is to update the pm_runtime
1802cb1e6d59SPierre-Louis Bossart 	 * counters and delay the pm_runtime suspend by several
1803cb1e6d59SPierre-Louis Bossart 	 * seconds, by when all enumeration should be complete.
1804cb1e6d59SPierre-Louis Bossart 	 */
1805cb1e6d59SPierre-Louis Bossart 	pm_runtime_mark_last_busy(dev);
1806cb1e6d59SPierre-Louis Bossart 
18079b3b4b3fSPierre-Louis Bossart 	return ret;
18089b3b4b3fSPierre-Louis Bossart }
18099b3b4b3fSPierre-Louis Bossart 
181017e0da0bSArnd Bergmann static int __maybe_unused intel_resume_runtime(struct device *dev)
1811ebf878edSPierre-Louis Bossart {
1812ebf878edSPierre-Louis Bossart 	struct sdw_cdns *cdns = dev_get_drvdata(dev);
1813ebf878edSPierre-Louis Bossart 	struct sdw_intel *sdw = cdns_to_intel(cdns);
1814ebf878edSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
1815a320f41eSPierre-Louis Bossart 	u32 clock_stop_quirks;
181608abad9fSRander Wang 	bool clock_stop0;
1817857a7c42SPierre-Louis Bossart 	int link_flags;
1818857a7c42SPierre-Louis Bossart 	bool multi_link;
181908abad9fSRander Wang 	int status;
1820ebf878edSPierre-Louis Bossart 	int ret;
1821ebf878edSPierre-Louis Bossart 
1822ebf878edSPierre-Louis Bossart 	if (bus->prop.hw_disabled) {
1823ebf878edSPierre-Louis Bossart 		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
1824ebf878edSPierre-Louis Bossart 			bus->link_id);
1825ebf878edSPierre-Louis Bossart 		return 0;
1826ebf878edSPierre-Louis Bossart 	}
1827ebf878edSPierre-Louis Bossart 
1828857a7c42SPierre-Louis Bossart 	link_flags = md_flags >> (bus->link_id * 8);
1829857a7c42SPierre-Louis Bossart 	multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
1830857a7c42SPierre-Louis Bossart 
1831a320f41eSPierre-Louis Bossart 	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1832a320f41eSPierre-Louis Bossart 
1833a320f41eSPierre-Louis Bossart 	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
1834ebf878edSPierre-Louis Bossart 		ret = intel_init(sdw);
1835ebf878edSPierre-Louis Bossart 		if (ret) {
18364e3ea93eSPierre-Louis Bossart 			dev_err(dev, "%s failed: %d\n", __func__, ret);
1837ebf878edSPierre-Louis Bossart 			return ret;
1838ebf878edSPierre-Louis Bossart 		}
1839ebf878edSPierre-Louis Bossart 
184099b6a30fSPierre-Louis Bossart 		/*
184199b6a30fSPierre-Louis Bossart 		 * make sure all Slaves are tagged as UNATTACHED and provide
184299b6a30fSPierre-Louis Bossart 		 * reason for reinitialization
184399b6a30fSPierre-Louis Bossart 		 */
184499b6a30fSPierre-Louis Bossart 		sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
184599b6a30fSPierre-Louis Bossart 
1846ebf878edSPierre-Louis Bossart 		ret = sdw_cdns_enable_interrupt(cdns, true);
1847ebf878edSPierre-Louis Bossart 		if (ret < 0) {
1848ebf878edSPierre-Louis Bossart 			dev_err(dev, "cannot enable interrupts during resume\n");
1849ebf878edSPierre-Louis Bossart 			return ret;
1850ebf878edSPierre-Louis Bossart 		}
1851ebf878edSPierre-Louis Bossart 
1852857a7c42SPierre-Louis Bossart 		/*
1853857a7c42SPierre-Louis Bossart 		 * follow recommended programming flows to avoid
1854857a7c42SPierre-Louis Bossart 		 * timeouts when gsync is enabled
1855857a7c42SPierre-Louis Bossart 		 */
1856857a7c42SPierre-Louis Bossart 		if (multi_link)
1857857a7c42SPierre-Louis Bossart 			intel_shim_sync_arm(sdw);
1858857a7c42SPierre-Louis Bossart 
1859857a7c42SPierre-Louis Bossart 		ret = sdw_cdns_init(&sdw->cdns);
1860857a7c42SPierre-Louis Bossart 		if (ret < 0) {
1861857a7c42SPierre-Louis Bossart 			dev_err(dev, "unable to initialize Cadence IP during resume\n");
1862857a7c42SPierre-Louis Bossart 			return ret;
1863857a7c42SPierre-Louis Bossart 		}
1864857a7c42SPierre-Louis Bossart 
1865ebf878edSPierre-Louis Bossart 		ret = sdw_cdns_exit_reset(cdns);
1866ebf878edSPierre-Louis Bossart 		if (ret < 0) {
1867ebf878edSPierre-Louis Bossart 			dev_err(dev, "unable to exit bus reset sequence during resume\n");
1868ebf878edSPierre-Louis Bossart 			return ret;
1869ebf878edSPierre-Louis Bossart 		}
1870857a7c42SPierre-Louis Bossart 
1871857a7c42SPierre-Louis Bossart 		if (multi_link) {
1872857a7c42SPierre-Louis Bossart 			ret = intel_shim_sync_go(sdw);
1873857a7c42SPierre-Louis Bossart 			if (ret < 0) {
1874857a7c42SPierre-Louis Bossart 				dev_err(dev, "sync go failed during resume\n");
1875857a7c42SPierre-Louis Bossart 				return ret;
1876857a7c42SPierre-Louis Bossart 			}
1877857a7c42SPierre-Louis Bossart 		}
1878ff560946SPierre-Louis Bossart 		sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime TEARDOWN",
1879ff560946SPierre-Louis Bossart 						  true, INTEL_MASTER_RESET_ITERATIONS);
1880ff560946SPierre-Louis Bossart 
18816626a616SRander Wang 	} else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) {
18826626a616SRander Wang 		ret = intel_init(sdw);
18836626a616SRander Wang 		if (ret) {
18844e3ea93eSPierre-Louis Bossart 			dev_err(dev, "%s failed: %d\n", __func__, ret);
18856626a616SRander Wang 			return ret;
18866626a616SRander Wang 		}
18876626a616SRander Wang 
18886626a616SRander Wang 		/*
188908abad9fSRander Wang 		 * An exception condition occurs for the CLK_STOP_BUS_RESET
189008abad9fSRander Wang 		 * case if one or more masters remain active. In this condition,
189108abad9fSRander Wang 		 * all the masters are powered on for they are in the same power
189208abad9fSRander Wang 		 * domain. Master can preserve its context for clock stop0, so
189308abad9fSRander Wang 		 * there is no need to clear slave status and reset bus.
189408abad9fSRander Wang 		 */
189508abad9fSRander Wang 		clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
189608abad9fSRander Wang 
1897857a7c42SPierre-Louis Bossart 		if (!clock_stop0) {
1898857a7c42SPierre-Louis Bossart 
1899857a7c42SPierre-Louis Bossart 			/*
19006626a616SRander Wang 			 * make sure all Slaves are tagged as UNATTACHED and
19016626a616SRander Wang 			 * provide reason for reinitialization
19026626a616SRander Wang 			 */
1903857a7c42SPierre-Louis Bossart 
190408abad9fSRander Wang 			status = SDW_UNATTACH_REQUEST_MASTER_RESET;
190508abad9fSRander Wang 			sdw_clear_slave_status(bus, status);
19066626a616SRander Wang 
19076626a616SRander Wang 			ret = sdw_cdns_enable_interrupt(cdns, true);
19086626a616SRander Wang 			if (ret < 0) {
19096626a616SRander Wang 				dev_err(dev, "cannot enable interrupts during resume\n");
19106626a616SRander Wang 				return ret;
19116626a616SRander Wang 			}
19126626a616SRander Wang 
1913d78071b4SPierre-Louis Bossart 			/*
1914d78071b4SPierre-Louis Bossart 			 * follow recommended programming flows to avoid
1915d78071b4SPierre-Louis Bossart 			 * timeouts when gsync is enabled
1916d78071b4SPierre-Louis Bossart 			 */
1917d78071b4SPierre-Louis Bossart 			if (multi_link)
1918d78071b4SPierre-Louis Bossart 				intel_shim_sync_arm(sdw);
1919d78071b4SPierre-Louis Bossart 
1920d78071b4SPierre-Louis Bossart 			/*
1921d78071b4SPierre-Louis Bossart 			 * Re-initialize the IP since it was powered-off
1922d78071b4SPierre-Louis Bossart 			 */
1923d78071b4SPierre-Louis Bossart 			sdw_cdns_init(&sdw->cdns);
1924d78071b4SPierre-Louis Bossart 
1925d78071b4SPierre-Louis Bossart 		} else {
1926d78071b4SPierre-Louis Bossart 			ret = sdw_cdns_enable_interrupt(cdns, true);
1927d78071b4SPierre-Louis Bossart 			if (ret < 0) {
1928d78071b4SPierre-Louis Bossart 				dev_err(dev, "cannot enable interrupts during resume\n");
1929d78071b4SPierre-Louis Bossart 				return ret;
1930d78071b4SPierre-Louis Bossart 			}
1931d78071b4SPierre-Louis Bossart 		}
1932d78071b4SPierre-Louis Bossart 
193308abad9fSRander Wang 		ret = sdw_cdns_clock_restart(cdns, !clock_stop0);
19346626a616SRander Wang 		if (ret < 0) {
19356626a616SRander Wang 			dev_err(dev, "unable to restart clock during resume\n");
19366626a616SRander Wang 			return ret;
19376626a616SRander Wang 		}
1938d78071b4SPierre-Louis Bossart 
1939d78071b4SPierre-Louis Bossart 		if (!clock_stop0) {
1940d78071b4SPierre-Louis Bossart 			ret = sdw_cdns_exit_reset(cdns);
1941d78071b4SPierre-Louis Bossart 			if (ret < 0) {
1942d78071b4SPierre-Louis Bossart 				dev_err(dev, "unable to exit bus reset sequence during resume\n");
1943d78071b4SPierre-Louis Bossart 				return ret;
1944d78071b4SPierre-Louis Bossart 			}
1945d78071b4SPierre-Louis Bossart 
1946d78071b4SPierre-Louis Bossart 			if (multi_link) {
1947d78071b4SPierre-Louis Bossart 				ret = intel_shim_sync_go(sdw);
1948d78071b4SPierre-Louis Bossart 				if (ret < 0) {
1949d78071b4SPierre-Louis Bossart 					dev_err(sdw->cdns.dev, "sync go failed during resume\n");
1950d78071b4SPierre-Louis Bossart 					return ret;
1951d78071b4SPierre-Louis Bossart 				}
1952d78071b4SPierre-Louis Bossart 			}
1953d78071b4SPierre-Louis Bossart 		}
1954ff560946SPierre-Louis Bossart 		sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime BUS_RESET",
1955ff560946SPierre-Louis Bossart 						  true, INTEL_MASTER_RESET_ITERATIONS);
1956ff560946SPierre-Louis Bossart 
195761fb830bSPierre-Louis Bossart 	} else if (!clock_stop_quirks) {
1958f748f34eSPierre-Louis Bossart 
1959f748f34eSPierre-Louis Bossart 		clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
1960f748f34eSPierre-Louis Bossart 		if (!clock_stop0)
1961f748f34eSPierre-Louis Bossart 			dev_err(dev, "%s invalid configuration, clock was not stopped", __func__);
1962f748f34eSPierre-Louis Bossart 
196361fb830bSPierre-Louis Bossart 		ret = intel_init(sdw);
196461fb830bSPierre-Louis Bossart 		if (ret) {
19654e3ea93eSPierre-Louis Bossart 			dev_err(dev, "%s failed: %d\n", __func__, ret);
196661fb830bSPierre-Louis Bossart 			return ret;
196761fb830bSPierre-Louis Bossart 		}
196861fb830bSPierre-Louis Bossart 
196961fb830bSPierre-Louis Bossart 		ret = sdw_cdns_enable_interrupt(cdns, true);
197061fb830bSPierre-Louis Bossart 		if (ret < 0) {
197161fb830bSPierre-Louis Bossart 			dev_err(dev, "cannot enable interrupts during resume\n");
197261fb830bSPierre-Louis Bossart 			return ret;
197361fb830bSPierre-Louis Bossart 		}
197461fb830bSPierre-Louis Bossart 
197561fb830bSPierre-Louis Bossart 		ret = sdw_cdns_clock_restart(cdns, false);
197661fb830bSPierre-Louis Bossart 		if (ret < 0) {
197761fb830bSPierre-Louis Bossart 			dev_err(dev, "unable to resume master during resume\n");
197861fb830bSPierre-Louis Bossart 			return ret;
197961fb830bSPierre-Louis Bossart 		}
1980ff560946SPierre-Louis Bossart 
1981ff560946SPierre-Louis Bossart 		sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime no_quirks",
1982ff560946SPierre-Louis Bossart 						  true, INTEL_MASTER_RESET_ITERATIONS);
1983a320f41eSPierre-Louis Bossart 	} else {
1984a320f41eSPierre-Louis Bossart 		dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
1985a320f41eSPierre-Louis Bossart 			__func__, clock_stop_quirks);
1986a320f41eSPierre-Louis Bossart 		ret = -EINVAL;
1987a320f41eSPierre-Louis Bossart 	}
1988ebf878edSPierre-Louis Bossart 
1989ebf878edSPierre-Louis Bossart 	return ret;
1990ebf878edSPierre-Louis Bossart }
1991ebf878edSPierre-Louis Bossart 
19929b3b4b3fSPierre-Louis Bossart static const struct dev_pm_ops intel_pm = {
19939b3b4b3fSPierre-Louis Bossart 	SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume)
1994ebf878edSPierre-Louis Bossart 	SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL)
19959b3b4b3fSPierre-Louis Bossart };
19969b3b4b3fSPierre-Louis Bossart 
199729a269c6SPierre-Louis Bossart static const struct auxiliary_device_id intel_link_id_table[] = {
199829a269c6SPierre-Louis Bossart 	{ .name = "soundwire_intel.link" },
199929a269c6SPierre-Louis Bossart 	{},
200071bb8a1bSVinod Koul };
200129a269c6SPierre-Louis Bossart MODULE_DEVICE_TABLE(auxiliary, intel_link_id_table);
200271bb8a1bSVinod Koul 
200329a269c6SPierre-Louis Bossart static struct auxiliary_driver sdw_intel_drv = {
200429a269c6SPierre-Louis Bossart 	.probe = intel_link_probe,
200529a269c6SPierre-Louis Bossart 	.remove = intel_link_remove,
200629a269c6SPierre-Louis Bossart 	.driver = {
200729a269c6SPierre-Louis Bossart 		/* auxiliary_driver_register() sets .name to be the modname */
200829a269c6SPierre-Louis Bossart 		.pm = &intel_pm,
200929a269c6SPierre-Louis Bossart 	},
201029a269c6SPierre-Louis Bossart 	.id_table = intel_link_id_table
201129a269c6SPierre-Louis Bossart };
201229a269c6SPierre-Louis Bossart module_auxiliary_driver(sdw_intel_drv);
201371bb8a1bSVinod Koul 
201471bb8a1bSVinod Koul MODULE_LICENSE("Dual BSD/GPL");
201529a269c6SPierre-Louis Bossart MODULE_DESCRIPTION("Intel Soundwire Link Driver");
2016