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