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 ¶ms_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