18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
28c2ecf20Sopenharmony_ci// Copyright(c) 2015-17 Intel Corporation.
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci/*
58c2ecf20Sopenharmony_ci * Soundwire Intel Master Driver
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/acpi.h>
98c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
178c2ecf20Sopenharmony_ci#include <sound/soc.h>
188c2ecf20Sopenharmony_ci#include <linux/soundwire/sdw_registers.h>
198c2ecf20Sopenharmony_ci#include <linux/soundwire/sdw.h>
208c2ecf20Sopenharmony_ci#include <linux/soundwire/sdw_intel.h>
218c2ecf20Sopenharmony_ci#include "cadence_master.h"
228c2ecf20Sopenharmony_ci#include "bus.h"
238c2ecf20Sopenharmony_ci#include "intel.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define INTEL_MASTER_SUSPEND_DELAY_MS	3000
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * debug/config flags for the Intel SoundWire Master.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Since we may have multiple masters active, we can have up to 8
318c2ecf20Sopenharmony_ci * flags reused in each byte, with master0 using the ls-byte, etc.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME		BIT(0)
358c2ecf20Sopenharmony_ci#define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP		BIT(1)
368c2ecf20Sopenharmony_ci#define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE	BIT(2)
378c2ecf20Sopenharmony_ci#define SDW_INTEL_MASTER_DISABLE_MULTI_LINK		BIT(3)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int md_flags;
408c2ecf20Sopenharmony_cimodule_param_named(sdw_md_flags, md_flags, int, 0444);
418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)");
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* Intel SHIM Registers Definition */
448c2ecf20Sopenharmony_ci#define SDW_SHIM_LCAP			0x0
458c2ecf20Sopenharmony_ci#define SDW_SHIM_LCTL			0x4
468c2ecf20Sopenharmony_ci#define SDW_SHIM_IPPTR			0x8
478c2ecf20Sopenharmony_ci#define SDW_SHIM_SYNC			0xC
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define SDW_SHIM_CTLSCAP(x)		(0x010 + 0x60 * (x))
508c2ecf20Sopenharmony_ci#define SDW_SHIM_CTLS0CM(x)		(0x012 + 0x60 * (x))
518c2ecf20Sopenharmony_ci#define SDW_SHIM_CTLS1CM(x)		(0x014 + 0x60 * (x))
528c2ecf20Sopenharmony_ci#define SDW_SHIM_CTLS2CM(x)		(0x016 + 0x60 * (x))
538c2ecf20Sopenharmony_ci#define SDW_SHIM_CTLS3CM(x)		(0x018 + 0x60 * (x))
548c2ecf20Sopenharmony_ci#define SDW_SHIM_PCMSCAP(x)		(0x020 + 0x60 * (x))
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define SDW_SHIM_PCMSYCHM(x, y)		(0x022 + (0x60 * (x)) + (0x2 * (y)))
578c2ecf20Sopenharmony_ci#define SDW_SHIM_PCMSYCHC(x, y)		(0x042 + (0x60 * (x)) + (0x2 * (y)))
588c2ecf20Sopenharmony_ci#define SDW_SHIM_PDMSCAP(x)		(0x062 + 0x60 * (x))
598c2ecf20Sopenharmony_ci#define SDW_SHIM_IOCTL(x)		(0x06C + 0x60 * (x))
608c2ecf20Sopenharmony_ci#define SDW_SHIM_CTMCTL(x)		(0x06E + 0x60 * (x))
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define SDW_SHIM_WAKEEN			0x190
638c2ecf20Sopenharmony_ci#define SDW_SHIM_WAKESTS		0x192
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define SDW_SHIM_LCTL_SPA		BIT(0)
668c2ecf20Sopenharmony_ci#define SDW_SHIM_LCTL_SPA_MASK		GENMASK(3, 0)
678c2ecf20Sopenharmony_ci#define SDW_SHIM_LCTL_CPA		BIT(8)
688c2ecf20Sopenharmony_ci#define SDW_SHIM_LCTL_CPA_MASK		GENMASK(11, 8)
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define SDW_SHIM_SYNC_SYNCPRD_VAL_24	(24000 / SDW_CADENCE_GSYNC_KHZ - 1)
718c2ecf20Sopenharmony_ci#define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4	(38400 / SDW_CADENCE_GSYNC_KHZ - 1)
728c2ecf20Sopenharmony_ci#define SDW_SHIM_SYNC_SYNCPRD		GENMASK(14, 0)
738c2ecf20Sopenharmony_ci#define SDW_SHIM_SYNC_SYNCCPU		BIT(15)
748c2ecf20Sopenharmony_ci#define SDW_SHIM_SYNC_CMDSYNC_MASK	GENMASK(19, 16)
758c2ecf20Sopenharmony_ci#define SDW_SHIM_SYNC_CMDSYNC		BIT(16)
768c2ecf20Sopenharmony_ci#define SDW_SHIM_SYNC_SYNCGO		BIT(24)
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#define SDW_SHIM_PCMSCAP_ISS		GENMASK(3, 0)
798c2ecf20Sopenharmony_ci#define SDW_SHIM_PCMSCAP_OSS		GENMASK(7, 4)
808c2ecf20Sopenharmony_ci#define SDW_SHIM_PCMSCAP_BSS		GENMASK(12, 8)
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define SDW_SHIM_PCMSYCM_LCHN		GENMASK(3, 0)
838c2ecf20Sopenharmony_ci#define SDW_SHIM_PCMSYCM_HCHN		GENMASK(7, 4)
848c2ecf20Sopenharmony_ci#define SDW_SHIM_PCMSYCM_STREAM		GENMASK(13, 8)
858c2ecf20Sopenharmony_ci#define SDW_SHIM_PCMSYCM_DIR		BIT(15)
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#define SDW_SHIM_PDMSCAP_ISS		GENMASK(3, 0)
888c2ecf20Sopenharmony_ci#define SDW_SHIM_PDMSCAP_OSS		GENMASK(7, 4)
898c2ecf20Sopenharmony_ci#define SDW_SHIM_PDMSCAP_BSS		GENMASK(12, 8)
908c2ecf20Sopenharmony_ci#define SDW_SHIM_PDMSCAP_CPSS		GENMASK(15, 13)
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define SDW_SHIM_IOCTL_MIF		BIT(0)
938c2ecf20Sopenharmony_ci#define SDW_SHIM_IOCTL_CO		BIT(1)
948c2ecf20Sopenharmony_ci#define SDW_SHIM_IOCTL_COE		BIT(2)
958c2ecf20Sopenharmony_ci#define SDW_SHIM_IOCTL_DO		BIT(3)
968c2ecf20Sopenharmony_ci#define SDW_SHIM_IOCTL_DOE		BIT(4)
978c2ecf20Sopenharmony_ci#define SDW_SHIM_IOCTL_BKE		BIT(5)
988c2ecf20Sopenharmony_ci#define SDW_SHIM_IOCTL_WPDD		BIT(6)
998c2ecf20Sopenharmony_ci#define SDW_SHIM_IOCTL_CIBD		BIT(8)
1008c2ecf20Sopenharmony_ci#define SDW_SHIM_IOCTL_DIBD		BIT(9)
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#define SDW_SHIM_CTMCTL_DACTQE		BIT(0)
1038c2ecf20Sopenharmony_ci#define SDW_SHIM_CTMCTL_DODS		BIT(1)
1048c2ecf20Sopenharmony_ci#define SDW_SHIM_CTMCTL_DOAIS		GENMASK(4, 3)
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci#define SDW_SHIM_WAKEEN_ENABLE		BIT(0)
1078c2ecf20Sopenharmony_ci#define SDW_SHIM_WAKESTS_STATUS		BIT(0)
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/* Intel ALH Register definitions */
1108c2ecf20Sopenharmony_ci#define SDW_ALH_STRMZCFG(x)		(0x000 + (0x4 * (x)))
1118c2ecf20Sopenharmony_ci#define SDW_ALH_NUM_STREAMS		64
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#define SDW_ALH_STRMZCFG_DMAT_VAL	0x3
1148c2ecf20Sopenharmony_ci#define SDW_ALH_STRMZCFG_DMAT		GENMASK(7, 0)
1158c2ecf20Sopenharmony_ci#define SDW_ALH_STRMZCFG_CHN		GENMASK(19, 16)
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cienum intel_pdi_type {
1188c2ecf20Sopenharmony_ci	INTEL_PDI_IN = 0,
1198c2ecf20Sopenharmony_ci	INTEL_PDI_OUT = 1,
1208c2ecf20Sopenharmony_ci	INTEL_PDI_BD = 2,
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/*
1268c2ecf20Sopenharmony_ci * Read, write helpers for HW registers
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_cistatic inline int intel_readl(void __iomem *base, int offset)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	return readl(base + offset);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic inline void intel_writel(void __iomem *base, int offset, int value)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	writel(value, base + offset);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic inline u16 intel_readw(void __iomem *base, int offset)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	return readw(base + offset);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic inline void intel_writew(void __iomem *base, int offset, u16 value)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	writew(value, base + offset);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	int timeout = 10;
1518c2ecf20Sopenharmony_ci	u32 reg_read;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	do {
1548c2ecf20Sopenharmony_ci		reg_read = readl(base + offset);
1558c2ecf20Sopenharmony_ci		if ((reg_read & mask) == target)
1568c2ecf20Sopenharmony_ci			return 0;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		timeout--;
1598c2ecf20Sopenharmony_ci		usleep_range(50, 100);
1608c2ecf20Sopenharmony_ci	} while (timeout != 0);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return -EAGAIN;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	writel(value, base + offset);
1688c2ecf20Sopenharmony_ci	return intel_wait_bit(base, offset, mask, 0);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	writel(value, base + offset);
1748c2ecf20Sopenharmony_ci	return intel_wait_bit(base, offset, mask, mask);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/*
1788c2ecf20Sopenharmony_ci * debugfs
1798c2ecf20Sopenharmony_ci */
1808c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci#define RD_BUF (2 * PAGE_SIZE)
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic ssize_t intel_sprintf(void __iomem *mem, bool l,
1858c2ecf20Sopenharmony_ci			     char *buf, size_t pos, unsigned int reg)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	int value;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (l)
1908c2ecf20Sopenharmony_ci		value = intel_readl(mem, reg);
1918c2ecf20Sopenharmony_ci	else
1928c2ecf20Sopenharmony_ci		value = intel_readw(mem, reg);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int intel_reg_show(struct seq_file *s_file, void *data)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = s_file->private;
2008c2ecf20Sopenharmony_ci	void __iomem *s = sdw->link_res->shim;
2018c2ecf20Sopenharmony_ci	void __iomem *a = sdw->link_res->alh;
2028c2ecf20Sopenharmony_ci	char *buf;
2038c2ecf20Sopenharmony_ci	ssize_t ret;
2048c2ecf20Sopenharmony_ci	int i, j;
2058c2ecf20Sopenharmony_ci	unsigned int links, reg;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	buf = kzalloc(RD_BUF, GFP_KERNEL);
2088c2ecf20Sopenharmony_ci	if (!buf)
2098c2ecf20Sopenharmony_ci		return -ENOMEM;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	ret = scnprintf(buf, RD_BUF, "Register  Value\n");
2148c2ecf20Sopenharmony_ci	ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n");
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	for (i = 0; i < links; i++) {
2178c2ecf20Sopenharmony_ci		reg = SDW_SHIM_LCAP + i * 4;
2188c2ecf20Sopenharmony_ci		ret += intel_sprintf(s, true, buf, ret, reg);
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	for (i = 0; i < links; i++) {
2228c2ecf20Sopenharmony_ci		ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i);
2238c2ecf20Sopenharmony_ci		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i));
2248c2ecf20Sopenharmony_ci		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i));
2258c2ecf20Sopenharmony_ci		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i));
2268c2ecf20Sopenharmony_ci		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i));
2278c2ecf20Sopenharmony_ci		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i));
2288c2ecf20Sopenharmony_ci		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i));
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		ret += scnprintf(buf + ret, RD_BUF - ret, "\n PCMSyCH registers\n");
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		/*
2338c2ecf20Sopenharmony_ci		 * the value 10 is the number of PDIs. We will need a
2348c2ecf20Sopenharmony_ci		 * cleanup to remove hard-coded Intel configurations
2358c2ecf20Sopenharmony_ci		 * from cadence_master.c
2368c2ecf20Sopenharmony_ci		 */
2378c2ecf20Sopenharmony_ci		for (j = 0; j < 10; j++) {
2388c2ecf20Sopenharmony_ci			ret += intel_sprintf(s, false, buf, ret,
2398c2ecf20Sopenharmony_ci					SDW_SHIM_PCMSYCHM(i, j));
2408c2ecf20Sopenharmony_ci			ret += intel_sprintf(s, false, buf, ret,
2418c2ecf20Sopenharmony_ci					SDW_SHIM_PCMSYCHC(i, j));
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci		ret += scnprintf(buf + ret, RD_BUF - ret, "\n PDMSCAP, IOCTL, CTMCTL\n");
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i));
2468c2ecf20Sopenharmony_ci		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i));
2478c2ecf20Sopenharmony_ci		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i));
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	ret += scnprintf(buf + ret, RD_BUF - ret, "\nWake registers\n");
2518c2ecf20Sopenharmony_ci	ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN);
2528c2ecf20Sopenharmony_ci	ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH STRMzCFG\n");
2558c2ecf20Sopenharmony_ci	for (i = 0; i < SDW_ALH_NUM_STREAMS; i++)
2568c2ecf20Sopenharmony_ci		ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i));
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	seq_printf(s_file, "%s", buf);
2598c2ecf20Sopenharmony_ci	kfree(buf);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	return 0;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(intel_reg);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic int intel_set_m_datamode(void *data, u64 value)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = data;
2688c2ecf20Sopenharmony_ci	struct sdw_bus *bus = &sdw->cdns.bus;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (value > SDW_PORT_DATA_MODE_STATIC_1)
2718c2ecf20Sopenharmony_ci		return -EINVAL;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* Userspace changed the hardware state behind the kernel's back */
2748c2ecf20Sopenharmony_ci	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	bus->params.m_data_mode = value;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return 0;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(intel_set_m_datamode_fops, NULL,
2818c2ecf20Sopenharmony_ci			 intel_set_m_datamode, "%llu\n");
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic int intel_set_s_datamode(void *data, u64 value)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = data;
2868c2ecf20Sopenharmony_ci	struct sdw_bus *bus = &sdw->cdns.bus;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (value > SDW_PORT_DATA_MODE_STATIC_1)
2898c2ecf20Sopenharmony_ci		return -EINVAL;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* Userspace changed the hardware state behind the kernel's back */
2928c2ecf20Sopenharmony_ci	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	bus->params.s_data_mode = value;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return 0;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(intel_set_s_datamode_fops, NULL,
2998c2ecf20Sopenharmony_ci			 intel_set_s_datamode, "%llu\n");
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void intel_debugfs_init(struct sdw_intel *sdw)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct dentry *root = sdw->cdns.bus.debugfs;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (!root)
3068c2ecf20Sopenharmony_ci		return;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	sdw->debugfs = debugfs_create_dir("intel-sdw", root);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw,
3118c2ecf20Sopenharmony_ci			    &intel_reg_fops);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	debugfs_create_file("intel-m-datamode", 0200, sdw->debugfs, sdw,
3148c2ecf20Sopenharmony_ci			    &intel_set_m_datamode_fops);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	debugfs_create_file("intel-s-datamode", 0200, sdw->debugfs, sdw,
3178c2ecf20Sopenharmony_ci			    &intel_set_s_datamode_fops);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic void intel_debugfs_exit(struct sdw_intel *sdw)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	debugfs_remove_recursive(sdw->debugfs);
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci#else
3278c2ecf20Sopenharmony_cistatic void intel_debugfs_init(struct sdw_intel *sdw) {}
3288c2ecf20Sopenharmony_cistatic void intel_debugfs_exit(struct sdw_intel *sdw) {}
3298c2ecf20Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci/*
3328c2ecf20Sopenharmony_ci * shim ops
3338c2ecf20Sopenharmony_ci */
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int intel_link_power_up(struct sdw_intel *sdw)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	unsigned int link_id = sdw->instance;
3388c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
3398c2ecf20Sopenharmony_ci	u32 *shim_mask = sdw->link_res->shim_mask;
3408c2ecf20Sopenharmony_ci	struct sdw_bus *bus = &sdw->cdns.bus;
3418c2ecf20Sopenharmony_ci	struct sdw_master_prop *prop = &bus->prop;
3428c2ecf20Sopenharmony_ci	u32 spa_mask, cpa_mask;
3438c2ecf20Sopenharmony_ci	u32 link_control;
3448c2ecf20Sopenharmony_ci	int ret = 0;
3458c2ecf20Sopenharmony_ci	u32 syncprd;
3468c2ecf20Sopenharmony_ci	u32 sync_reg;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	mutex_lock(sdw->link_res->shim_lock);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	/*
3518c2ecf20Sopenharmony_ci	 * The hardware relies on an internal counter, typically 4kHz,
3528c2ecf20Sopenharmony_ci	 * to generate the SoundWire SSP - which defines a 'safe'
3538c2ecf20Sopenharmony_ci	 * synchronization point between commands and audio transport
3548c2ecf20Sopenharmony_ci	 * and allows for multi link synchronization. The SYNCPRD value
3558c2ecf20Sopenharmony_ci	 * is only dependent on the oscillator clock provided to
3568c2ecf20Sopenharmony_ci	 * the IP, so adjust based on _DSD properties reported in DSDT
3578c2ecf20Sopenharmony_ci	 * tables. The values reported are based on either 24MHz
3588c2ecf20Sopenharmony_ci	 * (CNL/CML) or 38.4 MHz (ICL/TGL+).
3598c2ecf20Sopenharmony_ci	 */
3608c2ecf20Sopenharmony_ci	if (prop->mclk_freq % 6000000)
3618c2ecf20Sopenharmony_ci		syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;
3628c2ecf20Sopenharmony_ci	else
3638c2ecf20Sopenharmony_ci		syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (!*shim_mask) {
3668c2ecf20Sopenharmony_ci		dev_dbg(sdw->cdns.dev, "%s: powering up all links\n", __func__);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		/* we first need to program the SyncPRD/CPU registers */
3698c2ecf20Sopenharmony_ci		dev_dbg(sdw->cdns.dev,
3708c2ecf20Sopenharmony_ci			"%s: first link up, programming SYNCPRD\n", __func__);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		/* set SyncPRD period */
3738c2ecf20Sopenharmony_ci		sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
3748c2ecf20Sopenharmony_ci		u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		/* Set SyncCPU bit */
3778c2ecf20Sopenharmony_ci		sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
3788c2ecf20Sopenharmony_ci		intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		/* Link power up sequence */
3818c2ecf20Sopenharmony_ci		link_control = intel_readl(shim, SDW_SHIM_LCTL);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		/* only power-up enabled links */
3848c2ecf20Sopenharmony_ci		spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask);
3858c2ecf20Sopenharmony_ci		cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		link_control |=  spa_mask;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
3908c2ecf20Sopenharmony_ci		if (ret < 0) {
3918c2ecf20Sopenharmony_ci			dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret);
3928c2ecf20Sopenharmony_ci			goto out;
3938c2ecf20Sopenharmony_ci		}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		/* SyncCPU will change once link is active */
3968c2ecf20Sopenharmony_ci		ret = intel_wait_bit(shim, SDW_SHIM_SYNC,
3978c2ecf20Sopenharmony_ci				     SDW_SHIM_SYNC_SYNCCPU, 0);
3988c2ecf20Sopenharmony_ci		if (ret < 0) {
3998c2ecf20Sopenharmony_ci			dev_err(sdw->cdns.dev,
4008c2ecf20Sopenharmony_ci				"Failed to set SHIM_SYNC: %d\n", ret);
4018c2ecf20Sopenharmony_ci			goto out;
4028c2ecf20Sopenharmony_ci		}
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	*shim_mask |= BIT(link_id);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	sdw->cdns.link_up = true;
4088c2ecf20Sopenharmony_ciout:
4098c2ecf20Sopenharmony_ci	mutex_unlock(sdw->link_res->shim_lock);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return ret;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci/* this needs to be called with shim_lock */
4158c2ecf20Sopenharmony_cistatic void intel_shim_glue_to_master_ip(struct sdw_intel *sdw)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
4188c2ecf20Sopenharmony_ci	unsigned int link_id = sdw->instance;
4198c2ecf20Sopenharmony_ci	u16 ioctl;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* Switch to MIP from Glue logic */
4228c2ecf20Sopenharmony_ci	ioctl = intel_readw(shim,  SDW_SHIM_IOCTL(link_id));
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	ioctl &= ~(SDW_SHIM_IOCTL_DOE);
4258c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4268c2ecf20Sopenharmony_ci	usleep_range(10, 15);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	ioctl &= ~(SDW_SHIM_IOCTL_DO);
4298c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4308c2ecf20Sopenharmony_ci	usleep_range(10, 15);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	ioctl |= (SDW_SHIM_IOCTL_MIF);
4338c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4348c2ecf20Sopenharmony_ci	usleep_range(10, 15);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	ioctl &= ~(SDW_SHIM_IOCTL_BKE);
4378c2ecf20Sopenharmony_ci	ioctl &= ~(SDW_SHIM_IOCTL_COE);
4388c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4398c2ecf20Sopenharmony_ci	usleep_range(10, 15);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* at this point Master IP has full control of the I/Os */
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci/* this needs to be called with shim_lock */
4458c2ecf20Sopenharmony_cistatic void intel_shim_master_ip_to_glue(struct sdw_intel *sdw)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	unsigned int link_id = sdw->instance;
4488c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
4498c2ecf20Sopenharmony_ci	u16 ioctl;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	/* Glue logic */
4528c2ecf20Sopenharmony_ci	ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id));
4538c2ecf20Sopenharmony_ci	ioctl |= SDW_SHIM_IOCTL_BKE;
4548c2ecf20Sopenharmony_ci	ioctl |= SDW_SHIM_IOCTL_COE;
4558c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4568c2ecf20Sopenharmony_ci	usleep_range(10, 15);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	ioctl &= ~(SDW_SHIM_IOCTL_MIF);
4598c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4608c2ecf20Sopenharmony_ci	usleep_range(10, 15);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	/* at this point Integration Glue has full control of the I/Os */
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int intel_shim_init(struct sdw_intel *sdw, bool clock_stop)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
4688c2ecf20Sopenharmony_ci	unsigned int link_id = sdw->instance;
4698c2ecf20Sopenharmony_ci	int ret = 0;
4708c2ecf20Sopenharmony_ci	u16 ioctl = 0, act = 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	mutex_lock(sdw->link_res->shim_lock);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* Initialize Shim */
4758c2ecf20Sopenharmony_ci	ioctl |= SDW_SHIM_IOCTL_BKE;
4768c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4778c2ecf20Sopenharmony_ci	usleep_range(10, 15);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	ioctl |= SDW_SHIM_IOCTL_WPDD;
4808c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4818c2ecf20Sopenharmony_ci	usleep_range(10, 15);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	ioctl |= SDW_SHIM_IOCTL_DO;
4848c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4858c2ecf20Sopenharmony_ci	usleep_range(10, 15);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	ioctl |= SDW_SHIM_IOCTL_DOE;
4888c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
4898c2ecf20Sopenharmony_ci	usleep_range(10, 15);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	intel_shim_glue_to_master_ip(sdw);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	u16p_replace_bits(&act, 0x1, SDW_SHIM_CTMCTL_DOAIS);
4948c2ecf20Sopenharmony_ci	act |= SDW_SHIM_CTMCTL_DACTQE;
4958c2ecf20Sopenharmony_ci	act |= SDW_SHIM_CTMCTL_DODS;
4968c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act);
4978c2ecf20Sopenharmony_ci	usleep_range(10, 15);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	mutex_unlock(sdw->link_res->shim_lock);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	return ret;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
5078c2ecf20Sopenharmony_ci	unsigned int link_id = sdw->instance;
5088c2ecf20Sopenharmony_ci	u16 wake_en, wake_sts;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	mutex_lock(sdw->link_res->shim_lock);
5118c2ecf20Sopenharmony_ci	wake_en = intel_readw(shim, SDW_SHIM_WAKEEN);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	if (wake_enable) {
5148c2ecf20Sopenharmony_ci		/* Enable the wakeup */
5158c2ecf20Sopenharmony_ci		wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
5168c2ecf20Sopenharmony_ci		intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
5178c2ecf20Sopenharmony_ci	} else {
5188c2ecf20Sopenharmony_ci		/* Disable the wake up interrupt */
5198c2ecf20Sopenharmony_ci		wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id);
5208c2ecf20Sopenharmony_ci		intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci		/* Clear wake status */
5238c2ecf20Sopenharmony_ci		wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
5248c2ecf20Sopenharmony_ci		wake_sts |= (SDW_SHIM_WAKESTS_STATUS << link_id);
5258c2ecf20Sopenharmony_ci		intel_writew(shim, SDW_SHIM_WAKESTS, wake_sts);
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci	mutex_unlock(sdw->link_res->shim_lock);
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic int intel_link_power_down(struct sdw_intel *sdw)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	u32 link_control, spa_mask, cpa_mask;
5338c2ecf20Sopenharmony_ci	unsigned int link_id = sdw->instance;
5348c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
5358c2ecf20Sopenharmony_ci	u32 *shim_mask = sdw->link_res->shim_mask;
5368c2ecf20Sopenharmony_ci	int ret = 0;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	mutex_lock(sdw->link_res->shim_lock);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	if (!(*shim_mask & BIT(link_id)))
5418c2ecf20Sopenharmony_ci		dev_err(sdw->cdns.dev,
5428c2ecf20Sopenharmony_ci			"%s: Unbalanced power-up/down calls\n", __func__);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	sdw->cdns.link_up = false;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	intel_shim_master_ip_to_glue(sdw);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	*shim_mask &= ~BIT(link_id);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (!*shim_mask) {
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		dev_dbg(sdw->cdns.dev, "%s: powering down all links\n", __func__);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		/* Link power down sequence */
5558c2ecf20Sopenharmony_ci		link_control = intel_readl(shim, SDW_SHIM_LCTL);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci		/* only power-down enabled links */
5588c2ecf20Sopenharmony_ci		spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, ~sdw->link_res->link_mask);
5598c2ecf20Sopenharmony_ci		cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci		link_control &=  spa_mask;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci		ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
5648c2ecf20Sopenharmony_ci		if (ret < 0) {
5658c2ecf20Sopenharmony_ci			dev_err(sdw->cdns.dev, "%s: could not power down link\n", __func__);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci			/*
5688c2ecf20Sopenharmony_ci			 * we leave the sdw->cdns.link_up flag as false since we've disabled
5698c2ecf20Sopenharmony_ci			 * the link at this point and cannot handle interrupts any longer.
5708c2ecf20Sopenharmony_ci			 */
5718c2ecf20Sopenharmony_ci		}
5728c2ecf20Sopenharmony_ci	}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	link_control = intel_readl(shim, SDW_SHIM_LCTL);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	mutex_unlock(sdw->link_res->shim_lock);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	return ret;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic void intel_shim_sync_arm(struct sdw_intel *sdw)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
5848c2ecf20Sopenharmony_ci	u32 sync_reg;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	mutex_lock(sdw->link_res->shim_lock);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	/* update SYNC register */
5898c2ecf20Sopenharmony_ci	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
5908c2ecf20Sopenharmony_ci	sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance);
5918c2ecf20Sopenharmony_ci	intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	mutex_unlock(sdw->link_res->shim_lock);
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic int intel_shim_sync_go_unlocked(struct sdw_intel *sdw)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
5998c2ecf20Sopenharmony_ci	u32 sync_reg;
6008c2ecf20Sopenharmony_ci	int ret;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	/* Read SYNC register */
6038c2ecf20Sopenharmony_ci	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	/*
6068c2ecf20Sopenharmony_ci	 * Set SyncGO bit to synchronously trigger a bank switch for
6078c2ecf20Sopenharmony_ci	 * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all
6088c2ecf20Sopenharmony_ci	 * the Masters.
6098c2ecf20Sopenharmony_ci	 */
6108c2ecf20Sopenharmony_ci	sync_reg |= SDW_SHIM_SYNC_SYNCGO;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
6138c2ecf20Sopenharmony_ci			      SDW_SHIM_SYNC_SYNCGO);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (ret < 0)
6168c2ecf20Sopenharmony_ci		dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	return ret;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_cistatic int intel_shim_sync_go(struct sdw_intel *sdw)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	int ret;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	mutex_lock(sdw->link_res->shim_lock);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	ret = intel_shim_sync_go_unlocked(sdw);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	mutex_unlock(sdw->link_res->shim_lock);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	return ret;
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci/*
6358c2ecf20Sopenharmony_ci * PDI routines
6368c2ecf20Sopenharmony_ci */
6378c2ecf20Sopenharmony_cistatic void intel_pdi_init(struct sdw_intel *sdw,
6388c2ecf20Sopenharmony_ci			   struct sdw_cdns_stream_config *config)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
6418c2ecf20Sopenharmony_ci	unsigned int link_id = sdw->instance;
6428c2ecf20Sopenharmony_ci	int pcm_cap, pdm_cap;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	/* PCM Stream Capability */
6458c2ecf20Sopenharmony_ci	pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id));
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	config->pcm_bd = FIELD_GET(SDW_SHIM_PCMSCAP_BSS, pcm_cap);
6488c2ecf20Sopenharmony_ci	config->pcm_in = FIELD_GET(SDW_SHIM_PCMSCAP_ISS, pcm_cap);
6498c2ecf20Sopenharmony_ci	config->pcm_out = FIELD_GET(SDW_SHIM_PCMSCAP_OSS, pcm_cap);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n",
6528c2ecf20Sopenharmony_ci		config->pcm_bd, config->pcm_in, config->pcm_out);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	/* PDM Stream Capability */
6558c2ecf20Sopenharmony_ci	pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	config->pdm_bd = FIELD_GET(SDW_SHIM_PDMSCAP_BSS, pdm_cap);
6588c2ecf20Sopenharmony_ci	config->pdm_in = FIELD_GET(SDW_SHIM_PDMSCAP_ISS, pdm_cap);
6598c2ecf20Sopenharmony_ci	config->pdm_out = FIELD_GET(SDW_SHIM_PDMSCAP_OSS, pdm_cap);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	dev_dbg(sdw->cdns.dev, "PDM cap bd:%d in:%d out:%d\n",
6628c2ecf20Sopenharmony_ci		config->pdm_bd, config->pdm_in, config->pdm_out);
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_cistatic int
6668c2ecf20Sopenharmony_ciintel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm)
6678c2ecf20Sopenharmony_ci{
6688c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
6698c2ecf20Sopenharmony_ci	unsigned int link_id = sdw->instance;
6708c2ecf20Sopenharmony_ci	int count;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	if (pcm) {
6738c2ecf20Sopenharmony_ci		count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num));
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci		/*
6768c2ecf20Sopenharmony_ci		 * WORKAROUND: on all existing Intel controllers, pdi
6778c2ecf20Sopenharmony_ci		 * number 2 reports channel count as 1 even though it
6788c2ecf20Sopenharmony_ci		 * supports 8 channels. Performing hardcoding for pdi
6798c2ecf20Sopenharmony_ci		 * number 2.
6808c2ecf20Sopenharmony_ci		 */
6818c2ecf20Sopenharmony_ci		if (pdi_num == 2)
6828c2ecf20Sopenharmony_ci			count = 7;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	} else {
6858c2ecf20Sopenharmony_ci		count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
6868c2ecf20Sopenharmony_ci		count = FIELD_GET(SDW_SHIM_PDMSCAP_CPSS, count);
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	/* zero based values for channel count in register */
6908c2ecf20Sopenharmony_ci	count++;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return count;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic int intel_pdi_get_ch_update(struct sdw_intel *sdw,
6968c2ecf20Sopenharmony_ci				   struct sdw_cdns_pdi *pdi,
6978c2ecf20Sopenharmony_ci				   unsigned int num_pdi,
6988c2ecf20Sopenharmony_ci				   unsigned int *num_ch, bool pcm)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	int i, ch_count = 0;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	for (i = 0; i < num_pdi; i++) {
7038c2ecf20Sopenharmony_ci		pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm);
7048c2ecf20Sopenharmony_ci		ch_count += pdi->ch_count;
7058c2ecf20Sopenharmony_ci		pdi++;
7068c2ecf20Sopenharmony_ci	}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	*num_ch = ch_count;
7098c2ecf20Sopenharmony_ci	return 0;
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic int intel_pdi_stream_ch_update(struct sdw_intel *sdw,
7138c2ecf20Sopenharmony_ci				      struct sdw_cdns_streams *stream, bool pcm)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd,
7168c2ecf20Sopenharmony_ci				&stream->num_ch_bd, pcm);
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	intel_pdi_get_ch_update(sdw, stream->in, stream->num_in,
7198c2ecf20Sopenharmony_ci				&stream->num_ch_in, pcm);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	intel_pdi_get_ch_update(sdw, stream->out, stream->num_out,
7228c2ecf20Sopenharmony_ci				&stream->num_ch_out, pcm);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	return 0;
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_cistatic int intel_pdi_ch_update(struct sdw_intel *sdw)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	/* First update PCM streams followed by PDM streams */
7308c2ecf20Sopenharmony_ci	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true);
7318c2ecf20Sopenharmony_ci	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	return 0;
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic void
7378c2ecf20Sopenharmony_ciintel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
7408c2ecf20Sopenharmony_ci	unsigned int link_id = sdw->instance;
7418c2ecf20Sopenharmony_ci	int pdi_conf = 0;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	/* the Bulk and PCM streams are not contiguous */
7448c2ecf20Sopenharmony_ci	pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
7458c2ecf20Sopenharmony_ci	if (pdi->num >= 2)
7468c2ecf20Sopenharmony_ci		pdi->intel_alh_id += 2;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	/*
7498c2ecf20Sopenharmony_ci	 * Program stream parameters to stream SHIM register
7508c2ecf20Sopenharmony_ci	 * This is applicable for PCM stream only.
7518c2ecf20Sopenharmony_ci	 */
7528c2ecf20Sopenharmony_ci	if (pdi->type != SDW_STREAM_PCM)
7538c2ecf20Sopenharmony_ci		return;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (pdi->dir == SDW_DATA_DIR_RX)
7568c2ecf20Sopenharmony_ci		pdi_conf |= SDW_SHIM_PCMSYCM_DIR;
7578c2ecf20Sopenharmony_ci	else
7588c2ecf20Sopenharmony_ci		pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	u32p_replace_bits(&pdi_conf, pdi->intel_alh_id, SDW_SHIM_PCMSYCM_STREAM);
7618c2ecf20Sopenharmony_ci	u32p_replace_bits(&pdi_conf, pdi->l_ch_num, SDW_SHIM_PCMSYCM_LCHN);
7628c2ecf20Sopenharmony_ci	u32p_replace_bits(&pdi_conf, pdi->h_ch_num, SDW_SHIM_PCMSYCM_HCHN);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf);
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_cistatic void
7688c2ecf20Sopenharmony_ciintel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	void __iomem *alh = sdw->link_res->alh;
7718c2ecf20Sopenharmony_ci	unsigned int link_id = sdw->instance;
7728c2ecf20Sopenharmony_ci	unsigned int conf;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	/* the Bulk and PCM streams are not contiguous */
7758c2ecf20Sopenharmony_ci	pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
7768c2ecf20Sopenharmony_ci	if (pdi->num >= 2)
7778c2ecf20Sopenharmony_ci		pdi->intel_alh_id += 2;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	/* Program Stream config ALH register */
7808c2ecf20Sopenharmony_ci	conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id));
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	u32p_replace_bits(&conf, SDW_ALH_STRMZCFG_DMAT_VAL, SDW_ALH_STRMZCFG_DMAT);
7838c2ecf20Sopenharmony_ci	u32p_replace_bits(&conf, pdi->ch_count - 1, SDW_ALH_STRMZCFG_CHN);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic int intel_params_stream(struct sdw_intel *sdw,
7898c2ecf20Sopenharmony_ci			       struct snd_pcm_substream *substream,
7908c2ecf20Sopenharmony_ci			       struct snd_soc_dai *dai,
7918c2ecf20Sopenharmony_ci			       struct snd_pcm_hw_params *hw_params,
7928c2ecf20Sopenharmony_ci			       int link_id, int alh_stream_id)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct sdw_intel_link_res *res = sdw->link_res;
7958c2ecf20Sopenharmony_ci	struct sdw_intel_stream_params_data params_data;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	params_data.substream = substream;
7988c2ecf20Sopenharmony_ci	params_data.dai = dai;
7998c2ecf20Sopenharmony_ci	params_data.hw_params = hw_params;
8008c2ecf20Sopenharmony_ci	params_data.link_id = link_id;
8018c2ecf20Sopenharmony_ci	params_data.alh_stream_id = alh_stream_id;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	if (res->ops && res->ops->params_stream && res->dev)
8048c2ecf20Sopenharmony_ci		return res->ops->params_stream(res->dev,
8058c2ecf20Sopenharmony_ci					       &params_data);
8068c2ecf20Sopenharmony_ci	return -EIO;
8078c2ecf20Sopenharmony_ci}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_cistatic int intel_free_stream(struct sdw_intel *sdw,
8108c2ecf20Sopenharmony_ci			     struct snd_pcm_substream *substream,
8118c2ecf20Sopenharmony_ci			     struct snd_soc_dai *dai,
8128c2ecf20Sopenharmony_ci			     int link_id)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	struct sdw_intel_link_res *res = sdw->link_res;
8158c2ecf20Sopenharmony_ci	struct sdw_intel_stream_free_data free_data;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	free_data.substream = substream;
8188c2ecf20Sopenharmony_ci	free_data.dai = dai;
8198c2ecf20Sopenharmony_ci	free_data.link_id = link_id;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	if (res->ops && res->ops->free_stream && res->dev)
8228c2ecf20Sopenharmony_ci		return res->ops->free_stream(res->dev,
8238c2ecf20Sopenharmony_ci					     &free_data);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	return 0;
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci/*
8298c2ecf20Sopenharmony_ci * bank switch routines
8308c2ecf20Sopenharmony_ci */
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_cistatic int intel_pre_bank_switch(struct sdw_bus *bus)
8338c2ecf20Sopenharmony_ci{
8348c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = bus_to_cdns(bus);
8358c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = cdns_to_intel(cdns);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	/* Write to register only for multi-link */
8388c2ecf20Sopenharmony_ci	if (!bus->multi_link)
8398c2ecf20Sopenharmony_ci		return 0;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	intel_shim_sync_arm(sdw);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	return 0;
8448c2ecf20Sopenharmony_ci}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_cistatic int intel_post_bank_switch(struct sdw_bus *bus)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = bus_to_cdns(bus);
8498c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = cdns_to_intel(cdns);
8508c2ecf20Sopenharmony_ci	void __iomem *shim = sdw->link_res->shim;
8518c2ecf20Sopenharmony_ci	int sync_reg, ret;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	/* Write to register only for multi-link */
8548c2ecf20Sopenharmony_ci	if (!bus->multi_link)
8558c2ecf20Sopenharmony_ci		return 0;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	mutex_lock(sdw->link_res->shim_lock);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	/* Read SYNC register */
8608c2ecf20Sopenharmony_ci	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	/*
8638c2ecf20Sopenharmony_ci	 * post_bank_switch() ops is called from the bus in loop for
8648c2ecf20Sopenharmony_ci	 * all the Masters in the steam with the expectation that
8658c2ecf20Sopenharmony_ci	 * we trigger the bankswitch for the only first Master in the list
8668c2ecf20Sopenharmony_ci	 * and do nothing for the other Masters
8678c2ecf20Sopenharmony_ci	 *
8688c2ecf20Sopenharmony_ci	 * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
8698c2ecf20Sopenharmony_ci	 */
8708c2ecf20Sopenharmony_ci	if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) {
8718c2ecf20Sopenharmony_ci		ret = 0;
8728c2ecf20Sopenharmony_ci		goto unlock;
8738c2ecf20Sopenharmony_ci	}
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	ret = intel_shim_sync_go_unlocked(sdw);
8768c2ecf20Sopenharmony_ciunlock:
8778c2ecf20Sopenharmony_ci	mutex_unlock(sdw->link_res->shim_lock);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (ret < 0)
8808c2ecf20Sopenharmony_ci		dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	return ret;
8838c2ecf20Sopenharmony_ci}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci/*
8868c2ecf20Sopenharmony_ci * DAI routines
8878c2ecf20Sopenharmony_ci */
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_cistatic int intel_startup(struct snd_pcm_substream *substream,
8908c2ecf20Sopenharmony_ci			 struct snd_soc_dai *dai)
8918c2ecf20Sopenharmony_ci{
8928c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
8938c2ecf20Sopenharmony_ci	int ret;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	ret = pm_runtime_get_sync(cdns->dev);
8968c2ecf20Sopenharmony_ci	if (ret < 0 && ret != -EACCES) {
8978c2ecf20Sopenharmony_ci		dev_err_ratelimited(cdns->dev,
8988c2ecf20Sopenharmony_ci				    "pm_runtime_get_sync failed in %s, ret %d\n",
8998c2ecf20Sopenharmony_ci				    __func__, ret);
9008c2ecf20Sopenharmony_ci		pm_runtime_put_noidle(cdns->dev);
9018c2ecf20Sopenharmony_ci		return ret;
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci	return 0;
9048c2ecf20Sopenharmony_ci}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cistatic int intel_hw_params(struct snd_pcm_substream *substream,
9078c2ecf20Sopenharmony_ci			   struct snd_pcm_hw_params *params,
9088c2ecf20Sopenharmony_ci			   struct snd_soc_dai *dai)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
9118c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = cdns_to_intel(cdns);
9128c2ecf20Sopenharmony_ci	struct sdw_cdns_dma_data *dma;
9138c2ecf20Sopenharmony_ci	struct sdw_cdns_pdi *pdi;
9148c2ecf20Sopenharmony_ci	struct sdw_stream_config sconfig;
9158c2ecf20Sopenharmony_ci	struct sdw_port_config *pconfig;
9168c2ecf20Sopenharmony_ci	int ch, dir;
9178c2ecf20Sopenharmony_ci	int ret;
9188c2ecf20Sopenharmony_ci	bool pcm = true;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	dma = snd_soc_dai_get_dma_data(dai, substream);
9218c2ecf20Sopenharmony_ci	if (!dma)
9228c2ecf20Sopenharmony_ci		return -EIO;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	ch = params_channels(params);
9258c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
9268c2ecf20Sopenharmony_ci		dir = SDW_DATA_DIR_RX;
9278c2ecf20Sopenharmony_ci	else
9288c2ecf20Sopenharmony_ci		dir = SDW_DATA_DIR_TX;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	if (dma->stream_type == SDW_STREAM_PDM)
9318c2ecf20Sopenharmony_ci		pcm = false;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	if (pcm)
9348c2ecf20Sopenharmony_ci		pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id);
9358c2ecf20Sopenharmony_ci	else
9368c2ecf20Sopenharmony_ci		pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir, dai->id);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	if (!pdi) {
9398c2ecf20Sopenharmony_ci		ret = -EINVAL;
9408c2ecf20Sopenharmony_ci		goto error;
9418c2ecf20Sopenharmony_ci	}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/* do run-time configurations for SHIM, ALH and PDI/PORT */
9448c2ecf20Sopenharmony_ci	intel_pdi_shim_configure(sdw, pdi);
9458c2ecf20Sopenharmony_ci	intel_pdi_alh_configure(sdw, pdi);
9468c2ecf20Sopenharmony_ci	sdw_cdns_config_stream(cdns, ch, dir, pdi);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	/* store pdi and hw_params, may be needed in prepare step */
9498c2ecf20Sopenharmony_ci	dma->suspended = false;
9508c2ecf20Sopenharmony_ci	dma->pdi = pdi;
9518c2ecf20Sopenharmony_ci	dma->hw_params = params;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	/* Inform DSP about PDI stream number */
9548c2ecf20Sopenharmony_ci	ret = intel_params_stream(sdw, substream, dai, params,
9558c2ecf20Sopenharmony_ci				  sdw->instance,
9568c2ecf20Sopenharmony_ci				  pdi->intel_alh_id);
9578c2ecf20Sopenharmony_ci	if (ret)
9588c2ecf20Sopenharmony_ci		goto error;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	sconfig.direction = dir;
9618c2ecf20Sopenharmony_ci	sconfig.ch_count = ch;
9628c2ecf20Sopenharmony_ci	sconfig.frame_rate = params_rate(params);
9638c2ecf20Sopenharmony_ci	sconfig.type = dma->stream_type;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	if (dma->stream_type == SDW_STREAM_PDM) {
9668c2ecf20Sopenharmony_ci		sconfig.frame_rate *= 50;
9678c2ecf20Sopenharmony_ci		sconfig.bps = 1;
9688c2ecf20Sopenharmony_ci	} else {
9698c2ecf20Sopenharmony_ci		sconfig.bps = snd_pcm_format_width(params_format(params));
9708c2ecf20Sopenharmony_ci	}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	/* Port configuration */
9738c2ecf20Sopenharmony_ci	pconfig = kcalloc(1, sizeof(*pconfig), GFP_KERNEL);
9748c2ecf20Sopenharmony_ci	if (!pconfig) {
9758c2ecf20Sopenharmony_ci		ret =  -ENOMEM;
9768c2ecf20Sopenharmony_ci		goto error;
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	pconfig->num = pdi->num;
9808c2ecf20Sopenharmony_ci	pconfig->ch_mask = (1 << ch) - 1;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	ret = sdw_stream_add_master(&cdns->bus, &sconfig,
9838c2ecf20Sopenharmony_ci				    pconfig, 1, dma->stream);
9848c2ecf20Sopenharmony_ci	if (ret)
9858c2ecf20Sopenharmony_ci		dev_err(cdns->dev, "add master to stream failed:%d\n", ret);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	kfree(pconfig);
9888c2ecf20Sopenharmony_cierror:
9898c2ecf20Sopenharmony_ci	return ret;
9908c2ecf20Sopenharmony_ci}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_cistatic int intel_prepare(struct snd_pcm_substream *substream,
9938c2ecf20Sopenharmony_ci			 struct snd_soc_dai *dai)
9948c2ecf20Sopenharmony_ci{
9958c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
9968c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = cdns_to_intel(cdns);
9978c2ecf20Sopenharmony_ci	struct sdw_cdns_dma_data *dma;
9988c2ecf20Sopenharmony_ci	int ch, dir;
9998c2ecf20Sopenharmony_ci	int ret = 0;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	dma = snd_soc_dai_get_dma_data(dai, substream);
10028c2ecf20Sopenharmony_ci	if (!dma) {
10038c2ecf20Sopenharmony_ci		dev_err(dai->dev, "failed to get dma data in %s",
10048c2ecf20Sopenharmony_ci			__func__);
10058c2ecf20Sopenharmony_ci		return -EIO;
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	if (dma->suspended) {
10098c2ecf20Sopenharmony_ci		dma->suspended = false;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci		/*
10128c2ecf20Sopenharmony_ci		 * .prepare() is called after system resume, where we
10138c2ecf20Sopenharmony_ci		 * need to reinitialize the SHIM/ALH/Cadence IP.
10148c2ecf20Sopenharmony_ci		 * .prepare() is also called to deal with underflows,
10158c2ecf20Sopenharmony_ci		 * but in those cases we cannot touch ALH/SHIM
10168c2ecf20Sopenharmony_ci		 * registers
10178c2ecf20Sopenharmony_ci		 */
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci		/* configure stream */
10208c2ecf20Sopenharmony_ci		ch = params_channels(dma->hw_params);
10218c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
10228c2ecf20Sopenharmony_ci			dir = SDW_DATA_DIR_RX;
10238c2ecf20Sopenharmony_ci		else
10248c2ecf20Sopenharmony_ci			dir = SDW_DATA_DIR_TX;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci		intel_pdi_shim_configure(sdw, dma->pdi);
10278c2ecf20Sopenharmony_ci		intel_pdi_alh_configure(sdw, dma->pdi);
10288c2ecf20Sopenharmony_ci		sdw_cdns_config_stream(cdns, ch, dir, dma->pdi);
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci		/* Inform DSP about PDI stream number */
10318c2ecf20Sopenharmony_ci		ret = intel_params_stream(sdw, substream, dai,
10328c2ecf20Sopenharmony_ci					  dma->hw_params,
10338c2ecf20Sopenharmony_ci					  sdw->instance,
10348c2ecf20Sopenharmony_ci					  dma->pdi->intel_alh_id);
10358c2ecf20Sopenharmony_ci	}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	return ret;
10388c2ecf20Sopenharmony_ci}
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_cistatic int
10418c2ecf20Sopenharmony_ciintel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
10428c2ecf20Sopenharmony_ci{
10438c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
10448c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = cdns_to_intel(cdns);
10458c2ecf20Sopenharmony_ci	struct sdw_cdns_dma_data *dma;
10468c2ecf20Sopenharmony_ci	int ret;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	dma = snd_soc_dai_get_dma_data(dai, substream);
10498c2ecf20Sopenharmony_ci	if (!dma)
10508c2ecf20Sopenharmony_ci		return -EIO;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	/*
10538c2ecf20Sopenharmony_ci	 * The sdw stream state will transition to RELEASED when stream->
10548c2ecf20Sopenharmony_ci	 * master_list is empty. So the stream state will transition to
10558c2ecf20Sopenharmony_ci	 * DEPREPARED for the first cpu-dai and to RELEASED for the last
10568c2ecf20Sopenharmony_ci	 * cpu-dai.
10578c2ecf20Sopenharmony_ci	 */
10588c2ecf20Sopenharmony_ci	ret = sdw_stream_remove_master(&cdns->bus, dma->stream);
10598c2ecf20Sopenharmony_ci	if (ret < 0) {
10608c2ecf20Sopenharmony_ci		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
10618c2ecf20Sopenharmony_ci			dma->stream->name, ret);
10628c2ecf20Sopenharmony_ci		return ret;
10638c2ecf20Sopenharmony_ci	}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	ret = intel_free_stream(sdw, substream, dai, sdw->instance);
10668c2ecf20Sopenharmony_ci	if (ret < 0) {
10678c2ecf20Sopenharmony_ci		dev_err(dai->dev, "intel_free_stream: failed %d", ret);
10688c2ecf20Sopenharmony_ci		return ret;
10698c2ecf20Sopenharmony_ci	}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	dma->hw_params = NULL;
10728c2ecf20Sopenharmony_ci	dma->pdi = NULL;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	return 0;
10758c2ecf20Sopenharmony_ci}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_cistatic void intel_shutdown(struct snd_pcm_substream *substream,
10788c2ecf20Sopenharmony_ci			   struct snd_soc_dai *dai)
10798c2ecf20Sopenharmony_ci{
10808c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(cdns->dev);
10838c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(cdns->dev);
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic int intel_component_dais_suspend(struct snd_soc_component *component)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	struct sdw_cdns_dma_data *dma;
10898c2ecf20Sopenharmony_ci	struct snd_soc_dai *dai;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	for_each_component_dais(component, dai) {
10928c2ecf20Sopenharmony_ci		/*
10938c2ecf20Sopenharmony_ci		 * we don't have a .suspend dai_ops, and we don't have access
10948c2ecf20Sopenharmony_ci		 * to the substream, so let's mark both capture and playback
10958c2ecf20Sopenharmony_ci		 * DMA contexts as suspended
10968c2ecf20Sopenharmony_ci		 */
10978c2ecf20Sopenharmony_ci		dma = dai->playback_dma_data;
10988c2ecf20Sopenharmony_ci		if (dma)
10998c2ecf20Sopenharmony_ci			dma->suspended = true;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci		dma = dai->capture_dma_data;
11028c2ecf20Sopenharmony_ci		if (dma)
11038c2ecf20Sopenharmony_ci			dma->suspended = true;
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	return 0;
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cistatic int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
11108c2ecf20Sopenharmony_ci				    void *stream, int direction)
11118c2ecf20Sopenharmony_ci{
11128c2ecf20Sopenharmony_ci	return cdns_set_sdw_stream(dai, stream, true, direction);
11138c2ecf20Sopenharmony_ci}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_cistatic int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai,
11168c2ecf20Sopenharmony_ci				    void *stream, int direction)
11178c2ecf20Sopenharmony_ci{
11188c2ecf20Sopenharmony_ci	return cdns_set_sdw_stream(dai, stream, false, direction);
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_cistatic void *intel_get_sdw_stream(struct snd_soc_dai *dai,
11228c2ecf20Sopenharmony_ci				  int direction)
11238c2ecf20Sopenharmony_ci{
11248c2ecf20Sopenharmony_ci	struct sdw_cdns_dma_data *dma;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
11278c2ecf20Sopenharmony_ci		dma = dai->playback_dma_data;
11288c2ecf20Sopenharmony_ci	else
11298c2ecf20Sopenharmony_ci		dma = dai->capture_dma_data;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	if (!dma)
11328c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	return dma->stream;
11358c2ecf20Sopenharmony_ci}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops intel_pcm_dai_ops = {
11388c2ecf20Sopenharmony_ci	.startup = intel_startup,
11398c2ecf20Sopenharmony_ci	.hw_params = intel_hw_params,
11408c2ecf20Sopenharmony_ci	.prepare = intel_prepare,
11418c2ecf20Sopenharmony_ci	.hw_free = intel_hw_free,
11428c2ecf20Sopenharmony_ci	.shutdown = intel_shutdown,
11438c2ecf20Sopenharmony_ci	.set_stream = intel_pcm_set_sdw_stream,
11448c2ecf20Sopenharmony_ci	.get_stream = intel_get_sdw_stream,
11458c2ecf20Sopenharmony_ci};
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops intel_pdm_dai_ops = {
11488c2ecf20Sopenharmony_ci	.startup = intel_startup,
11498c2ecf20Sopenharmony_ci	.hw_params = intel_hw_params,
11508c2ecf20Sopenharmony_ci	.prepare = intel_prepare,
11518c2ecf20Sopenharmony_ci	.hw_free = intel_hw_free,
11528c2ecf20Sopenharmony_ci	.shutdown = intel_shutdown,
11538c2ecf20Sopenharmony_ci	.set_stream = intel_pdm_set_sdw_stream,
11548c2ecf20Sopenharmony_ci	.get_stream = intel_get_sdw_stream,
11558c2ecf20Sopenharmony_ci};
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver dai_component = {
11588c2ecf20Sopenharmony_ci	.name           = "soundwire",
11598c2ecf20Sopenharmony_ci	.suspend	= intel_component_dais_suspend
11608c2ecf20Sopenharmony_ci};
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_cistatic int intel_create_dai(struct sdw_cdns *cdns,
11638c2ecf20Sopenharmony_ci			    struct snd_soc_dai_driver *dais,
11648c2ecf20Sopenharmony_ci			    enum intel_pdi_type type,
11658c2ecf20Sopenharmony_ci			    u32 num, u32 off, u32 max_ch, bool pcm)
11668c2ecf20Sopenharmony_ci{
11678c2ecf20Sopenharmony_ci	int i;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	if (num == 0)
11708c2ecf20Sopenharmony_ci		return 0;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	 /* TODO: Read supported rates/formats from hardware */
11738c2ecf20Sopenharmony_ci	for (i = off; i < (off + num); i++) {
11748c2ecf20Sopenharmony_ci		dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL,
11758c2ecf20Sopenharmony_ci					      "SDW%d Pin%d",
11768c2ecf20Sopenharmony_ci					      cdns->instance, i);
11778c2ecf20Sopenharmony_ci		if (!dais[i].name)
11788c2ecf20Sopenharmony_ci			return -ENOMEM;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci		if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) {
11818c2ecf20Sopenharmony_ci			dais[i].playback.channels_min = 1;
11828c2ecf20Sopenharmony_ci			dais[i].playback.channels_max = max_ch;
11838c2ecf20Sopenharmony_ci			dais[i].playback.rates = SNDRV_PCM_RATE_48000;
11848c2ecf20Sopenharmony_ci			dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
11858c2ecf20Sopenharmony_ci		}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci		if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) {
11888c2ecf20Sopenharmony_ci			dais[i].capture.channels_min = 1;
11898c2ecf20Sopenharmony_ci			dais[i].capture.channels_max = max_ch;
11908c2ecf20Sopenharmony_ci			dais[i].capture.rates = SNDRV_PCM_RATE_48000;
11918c2ecf20Sopenharmony_ci			dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
11928c2ecf20Sopenharmony_ci		}
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci		if (pcm)
11958c2ecf20Sopenharmony_ci			dais[i].ops = &intel_pcm_dai_ops;
11968c2ecf20Sopenharmony_ci		else
11978c2ecf20Sopenharmony_ci			dais[i].ops = &intel_pdm_dai_ops;
11988c2ecf20Sopenharmony_ci	}
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	return 0;
12018c2ecf20Sopenharmony_ci}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_cistatic int intel_register_dai(struct sdw_intel *sdw)
12048c2ecf20Sopenharmony_ci{
12058c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = &sdw->cdns;
12068c2ecf20Sopenharmony_ci	struct sdw_cdns_streams *stream;
12078c2ecf20Sopenharmony_ci	struct snd_soc_dai_driver *dais;
12088c2ecf20Sopenharmony_ci	int num_dai, ret, off = 0;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	/* DAIs are created based on total number of PDIs supported */
12118c2ecf20Sopenharmony_ci	num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL);
12148c2ecf20Sopenharmony_ci	if (!dais)
12158c2ecf20Sopenharmony_ci		return -ENOMEM;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	/* Create PCM DAIs */
12188c2ecf20Sopenharmony_ci	stream = &cdns->pcm;
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in,
12218c2ecf20Sopenharmony_ci			       off, stream->num_ch_in, true);
12228c2ecf20Sopenharmony_ci	if (ret)
12238c2ecf20Sopenharmony_ci		return ret;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	off += cdns->pcm.num_in;
12268c2ecf20Sopenharmony_ci	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out,
12278c2ecf20Sopenharmony_ci			       off, stream->num_ch_out, true);
12288c2ecf20Sopenharmony_ci	if (ret)
12298c2ecf20Sopenharmony_ci		return ret;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	off += cdns->pcm.num_out;
12328c2ecf20Sopenharmony_ci	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd,
12338c2ecf20Sopenharmony_ci			       off, stream->num_ch_bd, true);
12348c2ecf20Sopenharmony_ci	if (ret)
12358c2ecf20Sopenharmony_ci		return ret;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	/* Create PDM DAIs */
12388c2ecf20Sopenharmony_ci	stream = &cdns->pdm;
12398c2ecf20Sopenharmony_ci	off += cdns->pcm.num_bd;
12408c2ecf20Sopenharmony_ci	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pdm.num_in,
12418c2ecf20Sopenharmony_ci			       off, stream->num_ch_in, false);
12428c2ecf20Sopenharmony_ci	if (ret)
12438c2ecf20Sopenharmony_ci		return ret;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	off += cdns->pdm.num_in;
12468c2ecf20Sopenharmony_ci	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pdm.num_out,
12478c2ecf20Sopenharmony_ci			       off, stream->num_ch_out, false);
12488c2ecf20Sopenharmony_ci	if (ret)
12498c2ecf20Sopenharmony_ci		return ret;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	off += cdns->pdm.num_out;
12528c2ecf20Sopenharmony_ci	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pdm.num_bd,
12538c2ecf20Sopenharmony_ci			       off, stream->num_ch_bd, false);
12548c2ecf20Sopenharmony_ci	if (ret)
12558c2ecf20Sopenharmony_ci		return ret;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	return snd_soc_register_component(cdns->dev, &dai_component,
12588c2ecf20Sopenharmony_ci					  dais, num_dai);
12598c2ecf20Sopenharmony_ci}
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_cistatic int sdw_master_read_intel_prop(struct sdw_bus *bus)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	struct sdw_master_prop *prop = &bus->prop;
12648c2ecf20Sopenharmony_ci	struct fwnode_handle *link;
12658c2ecf20Sopenharmony_ci	char name[32];
12668c2ecf20Sopenharmony_ci	u32 quirk_mask;
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	/* Find master handle */
12698c2ecf20Sopenharmony_ci	snprintf(name, sizeof(name),
12708c2ecf20Sopenharmony_ci		 "mipi-sdw-link-%d-subproperties", bus->link_id);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	link = device_get_named_child_node(bus->dev, name);
12738c2ecf20Sopenharmony_ci	if (!link) {
12748c2ecf20Sopenharmony_ci		dev_err(bus->dev, "Master node %s not found\n", name);
12758c2ecf20Sopenharmony_ci		return -EIO;
12768c2ecf20Sopenharmony_ci	}
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	fwnode_property_read_u32(link,
12798c2ecf20Sopenharmony_ci				 "intel-sdw-ip-clock",
12808c2ecf20Sopenharmony_ci				 &prop->mclk_freq);
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	/* the values reported by BIOS are the 2x clock, not the bus clock */
12838c2ecf20Sopenharmony_ci	prop->mclk_freq /= 2;
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	fwnode_property_read_u32(link,
12868c2ecf20Sopenharmony_ci				 "intel-quirk-mask",
12878c2ecf20Sopenharmony_ci				 &quirk_mask);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
12908c2ecf20Sopenharmony_ci		prop->hw_disabled = true;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	return 0;
12938c2ecf20Sopenharmony_ci}
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_cistatic int intel_prop_read(struct sdw_bus *bus)
12968c2ecf20Sopenharmony_ci{
12978c2ecf20Sopenharmony_ci	/* Initialize with default handler to read all DisCo properties */
12988c2ecf20Sopenharmony_ci	sdw_master_read_prop(bus);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	/* read Intel-specific properties */
13018c2ecf20Sopenharmony_ci	sdw_master_read_intel_prop(bus);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	return 0;
13048c2ecf20Sopenharmony_ci}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_cistatic struct sdw_master_ops sdw_intel_ops = {
13078c2ecf20Sopenharmony_ci	.read_prop = sdw_master_read_prop,
13088c2ecf20Sopenharmony_ci	.xfer_msg = cdns_xfer_msg,
13098c2ecf20Sopenharmony_ci	.xfer_msg_defer = cdns_xfer_msg_defer,
13108c2ecf20Sopenharmony_ci	.reset_page_addr = cdns_reset_page_addr,
13118c2ecf20Sopenharmony_ci	.set_bus_conf = cdns_bus_conf,
13128c2ecf20Sopenharmony_ci	.pre_bank_switch = intel_pre_bank_switch,
13138c2ecf20Sopenharmony_ci	.post_bank_switch = intel_post_bank_switch,
13148c2ecf20Sopenharmony_ci};
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_cistatic int intel_init(struct sdw_intel *sdw)
13178c2ecf20Sopenharmony_ci{
13188c2ecf20Sopenharmony_ci	bool clock_stop;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	/* Initialize shim and controller */
13218c2ecf20Sopenharmony_ci	intel_link_power_up(sdw);
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns);
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	intel_shim_init(sdw, clock_stop);
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	return 0;
13288c2ecf20Sopenharmony_ci}
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci/*
13318c2ecf20Sopenharmony_ci * probe and init
13328c2ecf20Sopenharmony_ci */
13338c2ecf20Sopenharmony_cistatic int intel_master_probe(struct platform_device *pdev)
13348c2ecf20Sopenharmony_ci{
13358c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
13368c2ecf20Sopenharmony_ci	struct sdw_intel *sdw;
13378c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns;
13388c2ecf20Sopenharmony_ci	struct sdw_bus *bus;
13398c2ecf20Sopenharmony_ci	int ret;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL);
13428c2ecf20Sopenharmony_ci	if (!sdw)
13438c2ecf20Sopenharmony_ci		return -ENOMEM;
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	cdns = &sdw->cdns;
13468c2ecf20Sopenharmony_ci	bus = &cdns->bus;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	sdw->instance = pdev->id;
13498c2ecf20Sopenharmony_ci	sdw->link_res = dev_get_platdata(dev);
13508c2ecf20Sopenharmony_ci	cdns->dev = dev;
13518c2ecf20Sopenharmony_ci	cdns->registers = sdw->link_res->registers;
13528c2ecf20Sopenharmony_ci	cdns->instance = sdw->instance;
13538c2ecf20Sopenharmony_ci	cdns->msg_count = 0;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	bus->link_id = pdev->id;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	sdw_cdns_probe(cdns);
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	/* Set property read ops */
13608c2ecf20Sopenharmony_ci	sdw_intel_ops.read_prop = intel_prop_read;
13618c2ecf20Sopenharmony_ci	bus->ops = &sdw_intel_ops;
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	/* set driver data, accessed by snd_soc_dai_get_drvdata() */
13648c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, cdns);
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	/* use generic bandwidth allocation algorithm */
13678c2ecf20Sopenharmony_ci	sdw->cdns.bus.compute_params = sdw_compute_params;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	ret = sdw_bus_master_add(bus, dev, dev->fwnode);
13708c2ecf20Sopenharmony_ci	if (ret) {
13718c2ecf20Sopenharmony_ci		dev_err(dev, "sdw_bus_master_add fail: %d\n", ret);
13728c2ecf20Sopenharmony_ci		return ret;
13738c2ecf20Sopenharmony_ci	}
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	if (bus->prop.hw_disabled)
13768c2ecf20Sopenharmony_ci		dev_info(dev,
13778c2ecf20Sopenharmony_ci			 "SoundWire master %d is disabled, will be ignored\n",
13788c2ecf20Sopenharmony_ci			 bus->link_id);
13798c2ecf20Sopenharmony_ci	/*
13808c2ecf20Sopenharmony_ci	 * Ignore BIOS err_threshold, it's a really bad idea when dealing
13818c2ecf20Sopenharmony_ci	 * with multiple hardware synchronized links
13828c2ecf20Sopenharmony_ci	 */
13838c2ecf20Sopenharmony_ci	bus->prop.err_threshold = 0;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	return 0;
13868c2ecf20Sopenharmony_ci}
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ciint intel_master_startup(struct platform_device *pdev)
13898c2ecf20Sopenharmony_ci{
13908c2ecf20Sopenharmony_ci	struct sdw_cdns_stream_config config;
13918c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
13928c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = dev_get_drvdata(dev);
13938c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = cdns_to_intel(cdns);
13948c2ecf20Sopenharmony_ci	struct sdw_bus *bus = &cdns->bus;
13958c2ecf20Sopenharmony_ci	int link_flags;
13968c2ecf20Sopenharmony_ci	bool multi_link;
13978c2ecf20Sopenharmony_ci	u32 clock_stop_quirks;
13988c2ecf20Sopenharmony_ci	int ret;
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	if (bus->prop.hw_disabled) {
14018c2ecf20Sopenharmony_ci		dev_info(dev,
14028c2ecf20Sopenharmony_ci			 "SoundWire master %d is disabled, ignoring\n",
14038c2ecf20Sopenharmony_ci			 sdw->instance);
14048c2ecf20Sopenharmony_ci		return 0;
14058c2ecf20Sopenharmony_ci	}
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	link_flags = md_flags >> (bus->link_id * 8);
14088c2ecf20Sopenharmony_ci	multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
14098c2ecf20Sopenharmony_ci	if (!multi_link) {
14108c2ecf20Sopenharmony_ci		dev_dbg(dev, "Multi-link is disabled\n");
14118c2ecf20Sopenharmony_ci		bus->multi_link = false;
14128c2ecf20Sopenharmony_ci	} else {
14138c2ecf20Sopenharmony_ci		/*
14148c2ecf20Sopenharmony_ci		 * hardware-based synchronization is required regardless
14158c2ecf20Sopenharmony_ci		 * of the number of segments used by a stream: SSP-based
14168c2ecf20Sopenharmony_ci		 * synchronization is gated by gsync when the multi-master
14178c2ecf20Sopenharmony_ci		 * mode is set.
14188c2ecf20Sopenharmony_ci		 */
14198c2ecf20Sopenharmony_ci		bus->multi_link = true;
14208c2ecf20Sopenharmony_ci		bus->hw_sync_min_links = 1;
14218c2ecf20Sopenharmony_ci	}
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	/* Initialize shim, controller */
14248c2ecf20Sopenharmony_ci	ret = intel_init(sdw);
14258c2ecf20Sopenharmony_ci	if (ret)
14268c2ecf20Sopenharmony_ci		goto err_init;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	/* Read the PDI config and initialize cadence PDI */
14298c2ecf20Sopenharmony_ci	intel_pdi_init(sdw, &config);
14308c2ecf20Sopenharmony_ci	ret = sdw_cdns_pdi_init(cdns, config);
14318c2ecf20Sopenharmony_ci	if (ret)
14328c2ecf20Sopenharmony_ci		goto err_init;
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	intel_pdi_ch_update(sdw);
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	ret = sdw_cdns_enable_interrupt(cdns, true);
14378c2ecf20Sopenharmony_ci	if (ret < 0) {
14388c2ecf20Sopenharmony_ci		dev_err(dev, "cannot enable interrupts\n");
14398c2ecf20Sopenharmony_ci		goto err_init;
14408c2ecf20Sopenharmony_ci	}
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	/*
14438c2ecf20Sopenharmony_ci	 * follow recommended programming flows to avoid timeouts when
14448c2ecf20Sopenharmony_ci	 * gsync is enabled
14458c2ecf20Sopenharmony_ci	 */
14468c2ecf20Sopenharmony_ci	if (multi_link)
14478c2ecf20Sopenharmony_ci		intel_shim_sync_arm(sdw);
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	ret = sdw_cdns_init(cdns);
14508c2ecf20Sopenharmony_ci	if (ret < 0) {
14518c2ecf20Sopenharmony_ci		dev_err(dev, "unable to initialize Cadence IP\n");
14528c2ecf20Sopenharmony_ci		goto err_interrupt;
14538c2ecf20Sopenharmony_ci	}
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	ret = sdw_cdns_exit_reset(cdns);
14568c2ecf20Sopenharmony_ci	if (ret < 0) {
14578c2ecf20Sopenharmony_ci		dev_err(dev, "unable to exit bus reset sequence\n");
14588c2ecf20Sopenharmony_ci		goto err_interrupt;
14598c2ecf20Sopenharmony_ci	}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	if (multi_link) {
14628c2ecf20Sopenharmony_ci		ret = intel_shim_sync_go(sdw);
14638c2ecf20Sopenharmony_ci		if (ret < 0) {
14648c2ecf20Sopenharmony_ci			dev_err(dev, "sync go failed: %d\n", ret);
14658c2ecf20Sopenharmony_ci			goto err_interrupt;
14668c2ecf20Sopenharmony_ci		}
14678c2ecf20Sopenharmony_ci	}
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	/* Register DAIs */
14708c2ecf20Sopenharmony_ci	ret = intel_register_dai(sdw);
14718c2ecf20Sopenharmony_ci	if (ret) {
14728c2ecf20Sopenharmony_ci		dev_err(dev, "DAI registration failed: %d\n", ret);
14738c2ecf20Sopenharmony_ci		goto err_interrupt;
14748c2ecf20Sopenharmony_ci	}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	intel_debugfs_init(sdw);
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	/* Enable runtime PM */
14798c2ecf20Sopenharmony_ci	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) {
14808c2ecf20Sopenharmony_ci		pm_runtime_set_autosuspend_delay(dev,
14818c2ecf20Sopenharmony_ci						 INTEL_MASTER_SUSPEND_DELAY_MS);
14828c2ecf20Sopenharmony_ci		pm_runtime_use_autosuspend(dev);
14838c2ecf20Sopenharmony_ci		pm_runtime_mark_last_busy(dev);
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci		pm_runtime_set_active(dev);
14868c2ecf20Sopenharmony_ci		pm_runtime_enable(dev);
14878c2ecf20Sopenharmony_ci	}
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
14908c2ecf20Sopenharmony_ci	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_NOT_ALLOWED) {
14918c2ecf20Sopenharmony_ci		/*
14928c2ecf20Sopenharmony_ci		 * To keep the clock running we need to prevent
14938c2ecf20Sopenharmony_ci		 * pm_runtime suspend from happening by increasing the
14948c2ecf20Sopenharmony_ci		 * reference count.
14958c2ecf20Sopenharmony_ci		 * This quirk is specified by the parent PCI device in
14968c2ecf20Sopenharmony_ci		 * case of specific latency requirements. It will have
14978c2ecf20Sopenharmony_ci		 * no effect if pm_runtime is disabled by the user via
14988c2ecf20Sopenharmony_ci		 * a module parameter for testing purposes.
14998c2ecf20Sopenharmony_ci		 */
15008c2ecf20Sopenharmony_ci		pm_runtime_get_noresume(dev);
15018c2ecf20Sopenharmony_ci	}
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	/*
15048c2ecf20Sopenharmony_ci	 * The runtime PM status of Slave devices is "Unsupported"
15058c2ecf20Sopenharmony_ci	 * until they report as ATTACHED. If they don't, e.g. because
15068c2ecf20Sopenharmony_ci	 * there are no Slave devices populated or if the power-on is
15078c2ecf20Sopenharmony_ci	 * delayed or dependent on a power switch, the Master will
15088c2ecf20Sopenharmony_ci	 * remain active and prevent its parent from suspending.
15098c2ecf20Sopenharmony_ci	 *
15108c2ecf20Sopenharmony_ci	 * Conditionally force the pm_runtime core to re-evaluate the
15118c2ecf20Sopenharmony_ci	 * Master status in the absence of any Slave activity. A quirk
15128c2ecf20Sopenharmony_ci	 * is provided to e.g. deal with Slaves that may be powered on
15138c2ecf20Sopenharmony_ci	 * with a delay. A more complete solution would require the
15148c2ecf20Sopenharmony_ci	 * definition of Master properties.
15158c2ecf20Sopenharmony_ci	 */
15168c2ecf20Sopenharmony_ci	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
15178c2ecf20Sopenharmony_ci		pm_runtime_idle(dev);
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	return 0;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_cierr_interrupt:
15228c2ecf20Sopenharmony_ci	sdw_cdns_enable_interrupt(cdns, false);
15238c2ecf20Sopenharmony_cierr_init:
15248c2ecf20Sopenharmony_ci	return ret;
15258c2ecf20Sopenharmony_ci}
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_cistatic int intel_master_remove(struct platform_device *pdev)
15288c2ecf20Sopenharmony_ci{
15298c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
15308c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = dev_get_drvdata(dev);
15318c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = cdns_to_intel(cdns);
15328c2ecf20Sopenharmony_ci	struct sdw_bus *bus = &cdns->bus;
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	/*
15358c2ecf20Sopenharmony_ci	 * Since pm_runtime is already disabled, we don't decrease
15368c2ecf20Sopenharmony_ci	 * the refcount when the clock_stop_quirk is
15378c2ecf20Sopenharmony_ci	 * SDW_INTEL_CLK_STOP_NOT_ALLOWED
15388c2ecf20Sopenharmony_ci	 */
15398c2ecf20Sopenharmony_ci	if (!bus->prop.hw_disabled) {
15408c2ecf20Sopenharmony_ci		intel_debugfs_exit(sdw);
15418c2ecf20Sopenharmony_ci		sdw_cdns_enable_interrupt(cdns, false);
15428c2ecf20Sopenharmony_ci		snd_soc_unregister_component(dev);
15438c2ecf20Sopenharmony_ci	}
15448c2ecf20Sopenharmony_ci	sdw_bus_master_delete(bus);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	return 0;
15478c2ecf20Sopenharmony_ci}
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ciint intel_master_process_wakeen_event(struct platform_device *pdev)
15508c2ecf20Sopenharmony_ci{
15518c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
15528c2ecf20Sopenharmony_ci	struct sdw_intel *sdw;
15538c2ecf20Sopenharmony_ci	struct sdw_bus *bus;
15548c2ecf20Sopenharmony_ci	void __iomem *shim;
15558c2ecf20Sopenharmony_ci	u16 wake_sts;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	sdw = platform_get_drvdata(pdev);
15588c2ecf20Sopenharmony_ci	bus = &sdw->cdns.bus;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	if (bus->prop.hw_disabled) {
15618c2ecf20Sopenharmony_ci		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", bus->link_id);
15628c2ecf20Sopenharmony_ci		return 0;
15638c2ecf20Sopenharmony_ci	}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	shim = sdw->link_res->shim;
15668c2ecf20Sopenharmony_ci	wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	if (!(wake_sts & BIT(sdw->instance)))
15698c2ecf20Sopenharmony_ci		return 0;
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci	/* disable WAKEEN interrupt ASAP to prevent interrupt flood */
15728c2ecf20Sopenharmony_ci	intel_shim_wake(sdw, false);
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	/*
15758c2ecf20Sopenharmony_ci	 * resume the Master, which will generate a bus reset and result in
15768c2ecf20Sopenharmony_ci	 * Slaves re-attaching and be re-enumerated. The SoundWire physical
15778c2ecf20Sopenharmony_ci	 * device which generated the wake will trigger an interrupt, which
15788c2ecf20Sopenharmony_ci	 * will in turn cause the corresponding Linux Slave device to be
15798c2ecf20Sopenharmony_ci	 * resumed and the Slave codec driver to check the status.
15808c2ecf20Sopenharmony_ci	 */
15818c2ecf20Sopenharmony_ci	pm_request_resume(dev);
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	return 0;
15848c2ecf20Sopenharmony_ci}
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci/*
15878c2ecf20Sopenharmony_ci * PM calls
15888c2ecf20Sopenharmony_ci */
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_cistatic int __maybe_unused intel_suspend(struct device *dev)
15938c2ecf20Sopenharmony_ci{
15948c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = dev_get_drvdata(dev);
15958c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = cdns_to_intel(cdns);
15968c2ecf20Sopenharmony_ci	struct sdw_bus *bus = &cdns->bus;
15978c2ecf20Sopenharmony_ci	u32 clock_stop_quirks;
15988c2ecf20Sopenharmony_ci	int ret;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	if (bus->prop.hw_disabled) {
16018c2ecf20Sopenharmony_ci		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
16028c2ecf20Sopenharmony_ci			bus->link_id);
16038c2ecf20Sopenharmony_ci		return 0;
16048c2ecf20Sopenharmony_ci	}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	if (pm_runtime_suspended(dev)) {
16078c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s: pm_runtime status: suspended\n", __func__);
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci		clock_stop_quirks = sdw->link_res->clock_stop_quirks;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci		if ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET ||
16128c2ecf20Sopenharmony_ci		     !clock_stop_quirks) &&
16138c2ecf20Sopenharmony_ci		    !pm_runtime_suspended(dev->parent)) {
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci			/*
16168c2ecf20Sopenharmony_ci			 * if we've enabled clock stop, and the parent
16178c2ecf20Sopenharmony_ci			 * is still active, disable shim wake. The
16188c2ecf20Sopenharmony_ci			 * SHIM registers are not accessible if the
16198c2ecf20Sopenharmony_ci			 * parent is already pm_runtime suspended so
16208c2ecf20Sopenharmony_ci			 * it's too late to change that configuration
16218c2ecf20Sopenharmony_ci			 */
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci			intel_shim_wake(sdw, false);
16248c2ecf20Sopenharmony_ci		}
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci		return 0;
16278c2ecf20Sopenharmony_ci	}
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	ret = sdw_cdns_enable_interrupt(cdns, false);
16308c2ecf20Sopenharmony_ci	if (ret < 0) {
16318c2ecf20Sopenharmony_ci		dev_err(dev, "cannot disable interrupts on suspend\n");
16328c2ecf20Sopenharmony_ci		return ret;
16338c2ecf20Sopenharmony_ci	}
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	ret = intel_link_power_down(sdw);
16368c2ecf20Sopenharmony_ci	if (ret) {
16378c2ecf20Sopenharmony_ci		dev_err(dev, "Link power down failed: %d", ret);
16388c2ecf20Sopenharmony_ci		return ret;
16398c2ecf20Sopenharmony_ci	}
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	intel_shim_wake(sdw, false);
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	return 0;
16448c2ecf20Sopenharmony_ci}
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_cistatic int intel_suspend_runtime(struct device *dev)
16478c2ecf20Sopenharmony_ci{
16488c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = dev_get_drvdata(dev);
16498c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = cdns_to_intel(cdns);
16508c2ecf20Sopenharmony_ci	struct sdw_bus *bus = &cdns->bus;
16518c2ecf20Sopenharmony_ci	u32 clock_stop_quirks;
16528c2ecf20Sopenharmony_ci	int ret;
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	if (bus->prop.hw_disabled) {
16558c2ecf20Sopenharmony_ci		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
16568c2ecf20Sopenharmony_ci			bus->link_id);
16578c2ecf20Sopenharmony_ci		return 0;
16588c2ecf20Sopenharmony_ci	}
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci		ret = sdw_cdns_enable_interrupt(cdns, false);
16658c2ecf20Sopenharmony_ci		if (ret < 0) {
16668c2ecf20Sopenharmony_ci			dev_err(dev, "cannot disable interrupts on suspend\n");
16678c2ecf20Sopenharmony_ci			return ret;
16688c2ecf20Sopenharmony_ci		}
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci		ret = intel_link_power_down(sdw);
16718c2ecf20Sopenharmony_ci		if (ret) {
16728c2ecf20Sopenharmony_ci			dev_err(dev, "Link power down failed: %d", ret);
16738c2ecf20Sopenharmony_ci			return ret;
16748c2ecf20Sopenharmony_ci		}
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci		intel_shim_wake(sdw, false);
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	} else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET ||
16798c2ecf20Sopenharmony_ci		   !clock_stop_quirks) {
16808c2ecf20Sopenharmony_ci		ret = sdw_cdns_clock_stop(cdns, true);
16818c2ecf20Sopenharmony_ci		if (ret < 0) {
16828c2ecf20Sopenharmony_ci			dev_err(dev, "cannot enable clock stop on suspend\n");
16838c2ecf20Sopenharmony_ci			return ret;
16848c2ecf20Sopenharmony_ci		}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci		ret = sdw_cdns_enable_interrupt(cdns, false);
16878c2ecf20Sopenharmony_ci		if (ret < 0) {
16888c2ecf20Sopenharmony_ci			dev_err(dev, "cannot disable interrupts on suspend\n");
16898c2ecf20Sopenharmony_ci			return ret;
16908c2ecf20Sopenharmony_ci		}
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci		ret = intel_link_power_down(sdw);
16938c2ecf20Sopenharmony_ci		if (ret) {
16948c2ecf20Sopenharmony_ci			dev_err(dev, "Link power down failed: %d", ret);
16958c2ecf20Sopenharmony_ci			return ret;
16968c2ecf20Sopenharmony_ci		}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci		intel_shim_wake(sdw, true);
16998c2ecf20Sopenharmony_ci	} else {
17008c2ecf20Sopenharmony_ci		dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
17018c2ecf20Sopenharmony_ci			__func__, clock_stop_quirks);
17028c2ecf20Sopenharmony_ci		ret = -EINVAL;
17038c2ecf20Sopenharmony_ci	}
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	return ret;
17068c2ecf20Sopenharmony_ci}
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_cistatic int __maybe_unused intel_resume(struct device *dev)
17098c2ecf20Sopenharmony_ci{
17108c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = dev_get_drvdata(dev);
17118c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = cdns_to_intel(cdns);
17128c2ecf20Sopenharmony_ci	struct sdw_bus *bus = &cdns->bus;
17138c2ecf20Sopenharmony_ci	int link_flags;
17148c2ecf20Sopenharmony_ci	bool multi_link;
17158c2ecf20Sopenharmony_ci	int ret;
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	if (bus->prop.hw_disabled) {
17188c2ecf20Sopenharmony_ci		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
17198c2ecf20Sopenharmony_ci			bus->link_id);
17208c2ecf20Sopenharmony_ci		return 0;
17218c2ecf20Sopenharmony_ci	}
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	link_flags = md_flags >> (bus->link_id * 8);
17248c2ecf20Sopenharmony_ci	multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	if (pm_runtime_suspended(dev)) {
17278c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s: pm_runtime status was suspended, forcing active\n", __func__);
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci		/* follow required sequence from runtime_pm.rst */
17308c2ecf20Sopenharmony_ci		pm_runtime_disable(dev);
17318c2ecf20Sopenharmony_ci		pm_runtime_set_active(dev);
17328c2ecf20Sopenharmony_ci		pm_runtime_mark_last_busy(dev);
17338c2ecf20Sopenharmony_ci		pm_runtime_enable(dev);
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci		link_flags = md_flags >> (bus->link_id * 8);
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci		if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
17388c2ecf20Sopenharmony_ci			pm_runtime_idle(dev);
17398c2ecf20Sopenharmony_ci	}
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci	ret = intel_init(sdw);
17428c2ecf20Sopenharmony_ci	if (ret) {
17438c2ecf20Sopenharmony_ci		dev_err(dev, "%s failed: %d", __func__, ret);
17448c2ecf20Sopenharmony_ci		return ret;
17458c2ecf20Sopenharmony_ci	}
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci	/*
17488c2ecf20Sopenharmony_ci	 * make sure all Slaves are tagged as UNATTACHED and provide
17498c2ecf20Sopenharmony_ci	 * reason for reinitialization
17508c2ecf20Sopenharmony_ci	 */
17518c2ecf20Sopenharmony_ci	sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	ret = sdw_cdns_enable_interrupt(cdns, true);
17548c2ecf20Sopenharmony_ci	if (ret < 0) {
17558c2ecf20Sopenharmony_ci		dev_err(dev, "cannot enable interrupts during resume\n");
17568c2ecf20Sopenharmony_ci		return ret;
17578c2ecf20Sopenharmony_ci	}
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	/*
17608c2ecf20Sopenharmony_ci	 * follow recommended programming flows to avoid timeouts when
17618c2ecf20Sopenharmony_ci	 * gsync is enabled
17628c2ecf20Sopenharmony_ci	 */
17638c2ecf20Sopenharmony_ci	if (multi_link)
17648c2ecf20Sopenharmony_ci		intel_shim_sync_arm(sdw);
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	ret = sdw_cdns_init(&sdw->cdns);
17678c2ecf20Sopenharmony_ci	if (ret < 0) {
17688c2ecf20Sopenharmony_ci		dev_err(dev, "unable to initialize Cadence IP during resume\n");
17698c2ecf20Sopenharmony_ci		return ret;
17708c2ecf20Sopenharmony_ci	}
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	ret = sdw_cdns_exit_reset(cdns);
17738c2ecf20Sopenharmony_ci	if (ret < 0) {
17748c2ecf20Sopenharmony_ci		dev_err(dev, "unable to exit bus reset sequence during resume\n");
17758c2ecf20Sopenharmony_ci		return ret;
17768c2ecf20Sopenharmony_ci	}
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	if (multi_link) {
17798c2ecf20Sopenharmony_ci		ret = intel_shim_sync_go(sdw);
17808c2ecf20Sopenharmony_ci		if (ret < 0) {
17818c2ecf20Sopenharmony_ci			dev_err(dev, "sync go failed during resume\n");
17828c2ecf20Sopenharmony_ci			return ret;
17838c2ecf20Sopenharmony_ci		}
17848c2ecf20Sopenharmony_ci	}
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	/*
17878c2ecf20Sopenharmony_ci	 * after system resume, the pm_runtime suspend() may kick in
17888c2ecf20Sopenharmony_ci	 * during the enumeration, before any children device force the
17898c2ecf20Sopenharmony_ci	 * master device to remain active.  Using pm_runtime_get()
17908c2ecf20Sopenharmony_ci	 * routines is not really possible, since it'd prevent the
17918c2ecf20Sopenharmony_ci	 * master from suspending.
17928c2ecf20Sopenharmony_ci	 * A reasonable compromise is to update the pm_runtime
17938c2ecf20Sopenharmony_ci	 * counters and delay the pm_runtime suspend by several
17948c2ecf20Sopenharmony_ci	 * seconds, by when all enumeration should be complete.
17958c2ecf20Sopenharmony_ci	 */
17968c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci	return ret;
17998c2ecf20Sopenharmony_ci}
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_cistatic int intel_resume_runtime(struct device *dev)
18028c2ecf20Sopenharmony_ci{
18038c2ecf20Sopenharmony_ci	struct sdw_cdns *cdns = dev_get_drvdata(dev);
18048c2ecf20Sopenharmony_ci	struct sdw_intel *sdw = cdns_to_intel(cdns);
18058c2ecf20Sopenharmony_ci	struct sdw_bus *bus = &cdns->bus;
18068c2ecf20Sopenharmony_ci	u32 clock_stop_quirks;
18078c2ecf20Sopenharmony_ci	bool clock_stop0;
18088c2ecf20Sopenharmony_ci	int link_flags;
18098c2ecf20Sopenharmony_ci	bool multi_link;
18108c2ecf20Sopenharmony_ci	int status;
18118c2ecf20Sopenharmony_ci	int ret;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	if (bus->prop.hw_disabled) {
18148c2ecf20Sopenharmony_ci		dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
18158c2ecf20Sopenharmony_ci			bus->link_id);
18168c2ecf20Sopenharmony_ci		return 0;
18178c2ecf20Sopenharmony_ci	}
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	link_flags = md_flags >> (bus->link_id * 8);
18208c2ecf20Sopenharmony_ci	multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
18258c2ecf20Sopenharmony_ci		ret = intel_init(sdw);
18268c2ecf20Sopenharmony_ci		if (ret) {
18278c2ecf20Sopenharmony_ci			dev_err(dev, "%s failed: %d", __func__, ret);
18288c2ecf20Sopenharmony_ci			return ret;
18298c2ecf20Sopenharmony_ci		}
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci		/*
18328c2ecf20Sopenharmony_ci		 * make sure all Slaves are tagged as UNATTACHED and provide
18338c2ecf20Sopenharmony_ci		 * reason for reinitialization
18348c2ecf20Sopenharmony_ci		 */
18358c2ecf20Sopenharmony_ci		sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci		ret = sdw_cdns_enable_interrupt(cdns, true);
18388c2ecf20Sopenharmony_ci		if (ret < 0) {
18398c2ecf20Sopenharmony_ci			dev_err(dev, "cannot enable interrupts during resume\n");
18408c2ecf20Sopenharmony_ci			return ret;
18418c2ecf20Sopenharmony_ci		}
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci		/*
18448c2ecf20Sopenharmony_ci		 * follow recommended programming flows to avoid
18458c2ecf20Sopenharmony_ci		 * timeouts when gsync is enabled
18468c2ecf20Sopenharmony_ci		 */
18478c2ecf20Sopenharmony_ci		if (multi_link)
18488c2ecf20Sopenharmony_ci			intel_shim_sync_arm(sdw);
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci		ret = sdw_cdns_init(&sdw->cdns);
18518c2ecf20Sopenharmony_ci		if (ret < 0) {
18528c2ecf20Sopenharmony_ci			dev_err(dev, "unable to initialize Cadence IP during resume\n");
18538c2ecf20Sopenharmony_ci			return ret;
18548c2ecf20Sopenharmony_ci		}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci		ret = sdw_cdns_exit_reset(cdns);
18578c2ecf20Sopenharmony_ci		if (ret < 0) {
18588c2ecf20Sopenharmony_ci			dev_err(dev, "unable to exit bus reset sequence during resume\n");
18598c2ecf20Sopenharmony_ci			return ret;
18608c2ecf20Sopenharmony_ci		}
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci		if (multi_link) {
18638c2ecf20Sopenharmony_ci			ret = intel_shim_sync_go(sdw);
18648c2ecf20Sopenharmony_ci			if (ret < 0) {
18658c2ecf20Sopenharmony_ci				dev_err(dev, "sync go failed during resume\n");
18668c2ecf20Sopenharmony_ci				return ret;
18678c2ecf20Sopenharmony_ci			}
18688c2ecf20Sopenharmony_ci		}
18698c2ecf20Sopenharmony_ci	} else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) {
18708c2ecf20Sopenharmony_ci		ret = intel_init(sdw);
18718c2ecf20Sopenharmony_ci		if (ret) {
18728c2ecf20Sopenharmony_ci			dev_err(dev, "%s failed: %d", __func__, ret);
18738c2ecf20Sopenharmony_ci			return ret;
18748c2ecf20Sopenharmony_ci		}
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci		/*
18778c2ecf20Sopenharmony_ci		 * An exception condition occurs for the CLK_STOP_BUS_RESET
18788c2ecf20Sopenharmony_ci		 * case if one or more masters remain active. In this condition,
18798c2ecf20Sopenharmony_ci		 * all the masters are powered on for they are in the same power
18808c2ecf20Sopenharmony_ci		 * domain. Master can preserve its context for clock stop0, so
18818c2ecf20Sopenharmony_ci		 * there is no need to clear slave status and reset bus.
18828c2ecf20Sopenharmony_ci		 */
18838c2ecf20Sopenharmony_ci		clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci		if (!clock_stop0) {
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci			/*
18888c2ecf20Sopenharmony_ci			 * make sure all Slaves are tagged as UNATTACHED and
18898c2ecf20Sopenharmony_ci			 * provide reason for reinitialization
18908c2ecf20Sopenharmony_ci			 */
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci			status = SDW_UNATTACH_REQUEST_MASTER_RESET;
18938c2ecf20Sopenharmony_ci			sdw_clear_slave_status(bus, status);
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci			ret = sdw_cdns_enable_interrupt(cdns, true);
18968c2ecf20Sopenharmony_ci			if (ret < 0) {
18978c2ecf20Sopenharmony_ci				dev_err(dev, "cannot enable interrupts during resume\n");
18988c2ecf20Sopenharmony_ci				return ret;
18998c2ecf20Sopenharmony_ci			}
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci			/*
19028c2ecf20Sopenharmony_ci			 * follow recommended programming flows to avoid
19038c2ecf20Sopenharmony_ci			 * timeouts when gsync is enabled
19048c2ecf20Sopenharmony_ci			 */
19058c2ecf20Sopenharmony_ci			if (multi_link)
19068c2ecf20Sopenharmony_ci				intel_shim_sync_arm(sdw);
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci			/*
19098c2ecf20Sopenharmony_ci			 * Re-initialize the IP since it was powered-off
19108c2ecf20Sopenharmony_ci			 */
19118c2ecf20Sopenharmony_ci			sdw_cdns_init(&sdw->cdns);
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci		} else {
19148c2ecf20Sopenharmony_ci			ret = sdw_cdns_enable_interrupt(cdns, true);
19158c2ecf20Sopenharmony_ci			if (ret < 0) {
19168c2ecf20Sopenharmony_ci				dev_err(dev, "cannot enable interrupts during resume\n");
19178c2ecf20Sopenharmony_ci				return ret;
19188c2ecf20Sopenharmony_ci			}
19198c2ecf20Sopenharmony_ci		}
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci		ret = sdw_cdns_clock_restart(cdns, !clock_stop0);
19228c2ecf20Sopenharmony_ci		if (ret < 0) {
19238c2ecf20Sopenharmony_ci			dev_err(dev, "unable to restart clock during resume\n");
19248c2ecf20Sopenharmony_ci			return ret;
19258c2ecf20Sopenharmony_ci		}
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci		if (!clock_stop0) {
19288c2ecf20Sopenharmony_ci			ret = sdw_cdns_exit_reset(cdns);
19298c2ecf20Sopenharmony_ci			if (ret < 0) {
19308c2ecf20Sopenharmony_ci				dev_err(dev, "unable to exit bus reset sequence during resume\n");
19318c2ecf20Sopenharmony_ci				return ret;
19328c2ecf20Sopenharmony_ci			}
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci			if (multi_link) {
19358c2ecf20Sopenharmony_ci				ret = intel_shim_sync_go(sdw);
19368c2ecf20Sopenharmony_ci				if (ret < 0) {
19378c2ecf20Sopenharmony_ci					dev_err(sdw->cdns.dev, "sync go failed during resume\n");
19388c2ecf20Sopenharmony_ci					return ret;
19398c2ecf20Sopenharmony_ci				}
19408c2ecf20Sopenharmony_ci			}
19418c2ecf20Sopenharmony_ci		}
19428c2ecf20Sopenharmony_ci	} else if (!clock_stop_quirks) {
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci		clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
19458c2ecf20Sopenharmony_ci		if (!clock_stop0)
19468c2ecf20Sopenharmony_ci			dev_err(dev, "%s invalid configuration, clock was not stopped", __func__);
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci		ret = intel_init(sdw);
19498c2ecf20Sopenharmony_ci		if (ret) {
19508c2ecf20Sopenharmony_ci			dev_err(dev, "%s failed: %d", __func__, ret);
19518c2ecf20Sopenharmony_ci			return ret;
19528c2ecf20Sopenharmony_ci		}
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci		ret = sdw_cdns_enable_interrupt(cdns, true);
19558c2ecf20Sopenharmony_ci		if (ret < 0) {
19568c2ecf20Sopenharmony_ci			dev_err(dev, "cannot enable interrupts during resume\n");
19578c2ecf20Sopenharmony_ci			return ret;
19588c2ecf20Sopenharmony_ci		}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci		ret = sdw_cdns_clock_restart(cdns, false);
19618c2ecf20Sopenharmony_ci		if (ret < 0) {
19628c2ecf20Sopenharmony_ci			dev_err(dev, "unable to resume master during resume\n");
19638c2ecf20Sopenharmony_ci			return ret;
19648c2ecf20Sopenharmony_ci		}
19658c2ecf20Sopenharmony_ci	} else {
19668c2ecf20Sopenharmony_ci		dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
19678c2ecf20Sopenharmony_ci			__func__, clock_stop_quirks);
19688c2ecf20Sopenharmony_ci		ret = -EINVAL;
19698c2ecf20Sopenharmony_ci	}
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	return ret;
19728c2ecf20Sopenharmony_ci}
19738c2ecf20Sopenharmony_ci
19748c2ecf20Sopenharmony_ci#endif
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_cistatic const struct dev_pm_ops intel_pm = {
19778c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume)
19788c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL)
19798c2ecf20Sopenharmony_ci};
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_cistatic struct platform_driver sdw_intel_drv = {
19828c2ecf20Sopenharmony_ci	.probe = intel_master_probe,
19838c2ecf20Sopenharmony_ci	.remove = intel_master_remove,
19848c2ecf20Sopenharmony_ci	.driver = {
19858c2ecf20Sopenharmony_ci		.name = "intel-sdw",
19868c2ecf20Sopenharmony_ci		.pm = &intel_pm,
19878c2ecf20Sopenharmony_ci	}
19888c2ecf20Sopenharmony_ci};
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_cimodule_platform_driver(sdw_intel_drv);
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
19938c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:intel-sdw");
19948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Soundwire Master Driver");
1995