171bb8a1bSVinod Koul // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 271bb8a1bSVinod Koul // Copyright(c) 2015-17 Intel Corporation. 371bb8a1bSVinod Koul 471bb8a1bSVinod Koul /* 571bb8a1bSVinod Koul * Soundwire Intel Master Driver 671bb8a1bSVinod Koul */ 771bb8a1bSVinod Koul 871bb8a1bSVinod Koul #include <linux/acpi.h> 979ee6631SPierre-Louis Bossart #include <linux/debugfs.h> 1071bb8a1bSVinod Koul #include <linux/delay.h> 114abbd783SPaul Gortmaker #include <linux/module.h> 1271bb8a1bSVinod Koul #include <linux/interrupt.h> 13df72b719SPierre-Louis Bossart #include <linux/io.h> 1429a269c6SPierre-Louis Bossart #include <linux/auxiliary_bus.h> 1537a2d22bSVinod Koul #include <sound/pcm_params.h> 16ab2c9132SRander Wang #include <linux/pm_runtime.h> 1737a2d22bSVinod Koul #include <sound/soc.h> 1871bb8a1bSVinod Koul #include <linux/soundwire/sdw_registers.h> 1971bb8a1bSVinod Koul #include <linux/soundwire/sdw.h> 2071bb8a1bSVinod Koul #include <linux/soundwire/sdw_intel.h> 2171bb8a1bSVinod Koul #include "cadence_master.h" 2279ee6631SPierre-Louis Bossart #include "bus.h" 2371bb8a1bSVinod Koul #include "intel.h" 2471bb8a1bSVinod Koul 251f2dcf3aSPierre-Louis Bossart /* IDA min selected to avoid conflicts with HDaudio/iDISP SDI values */ 261f2dcf3aSPierre-Louis Bossart #define INTEL_DEV_NUM_IDA_MIN 4 271f2dcf3aSPierre-Louis Bossart 28ebf878edSPierre-Louis Bossart #define INTEL_MASTER_SUSPEND_DELAY_MS 3000 29ff560946SPierre-Louis Bossart #define INTEL_MASTER_RESET_ITERATIONS 10 30ebf878edSPierre-Louis Bossart 31ebf878edSPierre-Louis Bossart /* 32ebf878edSPierre-Louis Bossart * debug/config flags for the Intel SoundWire Master. 33ebf878edSPierre-Louis Bossart * 34ebf878edSPierre-Louis Bossart * Since we may have multiple masters active, we can have up to 8 35ebf878edSPierre-Louis Bossart * flags reused in each byte, with master0 using the ls-byte, etc. 36ebf878edSPierre-Louis Bossart */ 37ebf878edSPierre-Louis Bossart 38ebf878edSPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME BIT(0) 39ebf878edSPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP BIT(1) 40a2d9c161SPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE BIT(2) 41857a7c42SPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_MULTI_LINK BIT(3) 42ebf878edSPierre-Louis Bossart 43ebf878edSPierre-Louis Bossart static int md_flags; 44ebf878edSPierre-Louis Bossart module_param_named(sdw_md_flags, md_flags, int, 0444); 45ebf878edSPierre-Louis Bossart MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)"); 46ebf878edSPierre-Louis Bossart 47c46302ecSVinod Koul enum intel_pdi_type { 48c46302ecSVinod Koul INTEL_PDI_IN = 0, 49c46302ecSVinod Koul INTEL_PDI_OUT = 1, 50c46302ecSVinod Koul INTEL_PDI_BD = 2, 51c46302ecSVinod Koul }; 52c46302ecSVinod Koul 5371bb8a1bSVinod Koul #define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns) 5471bb8a1bSVinod Koul 5571bb8a1bSVinod Koul /* 5671bb8a1bSVinod Koul * Read, write helpers for HW registers 5771bb8a1bSVinod Koul */ 5871bb8a1bSVinod Koul static inline int intel_readl(void __iomem *base, int offset) 5971bb8a1bSVinod Koul { 6071bb8a1bSVinod Koul return readl(base + offset); 6171bb8a1bSVinod Koul } 6271bb8a1bSVinod Koul 6371bb8a1bSVinod Koul static inline void intel_writel(void __iomem *base, int offset, int value) 6471bb8a1bSVinod Koul { 6571bb8a1bSVinod Koul writel(value, base + offset); 6671bb8a1bSVinod Koul } 6771bb8a1bSVinod Koul 6871bb8a1bSVinod Koul static inline u16 intel_readw(void __iomem *base, int offset) 6971bb8a1bSVinod Koul { 7071bb8a1bSVinod Koul return readw(base + offset); 7171bb8a1bSVinod Koul } 7271bb8a1bSVinod Koul 7371bb8a1bSVinod Koul static inline void intel_writew(void __iomem *base, int offset, u16 value) 7471bb8a1bSVinod Koul { 7571bb8a1bSVinod Koul writew(value, base + offset); 7671bb8a1bSVinod Koul } 7771bb8a1bSVinod Koul 787d2845d5SPierre-Louis Bossart static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target) 7971bb8a1bSVinod Koul { 8071bb8a1bSVinod Koul int timeout = 10; 8171bb8a1bSVinod Koul u32 reg_read; 8271bb8a1bSVinod Koul 8371bb8a1bSVinod Koul do { 8471bb8a1bSVinod Koul reg_read = readl(base + offset); 857d2845d5SPierre-Louis Bossart if ((reg_read & mask) == target) 8671bb8a1bSVinod Koul return 0; 8771bb8a1bSVinod Koul 8871bb8a1bSVinod Koul timeout--; 897d2845d5SPierre-Louis Bossart usleep_range(50, 100); 9071bb8a1bSVinod Koul } while (timeout != 0); 9171bb8a1bSVinod Koul 9271bb8a1bSVinod Koul return -EAGAIN; 9371bb8a1bSVinod Koul } 9471bb8a1bSVinod Koul 957d2845d5SPierre-Louis Bossart static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask) 967d2845d5SPierre-Louis Bossart { 977d2845d5SPierre-Louis Bossart writel(value, base + offset); 987d2845d5SPierre-Louis Bossart return intel_wait_bit(base, offset, mask, 0); 997d2845d5SPierre-Louis Bossart } 1007d2845d5SPierre-Louis Bossart 10171bb8a1bSVinod Koul static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) 10271bb8a1bSVinod Koul { 10371bb8a1bSVinod Koul writel(value, base + offset); 1047d2845d5SPierre-Louis Bossart return intel_wait_bit(base, offset, mask, mask); 10571bb8a1bSVinod Koul } 10671bb8a1bSVinod Koul 10771bb8a1bSVinod Koul /* 10879ee6631SPierre-Louis Bossart * debugfs 10979ee6631SPierre-Louis Bossart */ 11079ee6631SPierre-Louis Bossart #ifdef CONFIG_DEBUG_FS 11179ee6631SPierre-Louis Bossart 11279ee6631SPierre-Louis Bossart #define RD_BUF (2 * PAGE_SIZE) 11379ee6631SPierre-Louis Bossart 11479ee6631SPierre-Louis Bossart static ssize_t intel_sprintf(void __iomem *mem, bool l, 11579ee6631SPierre-Louis Bossart char *buf, size_t pos, unsigned int reg) 11679ee6631SPierre-Louis Bossart { 11779ee6631SPierre-Louis Bossart int value; 11879ee6631SPierre-Louis Bossart 11979ee6631SPierre-Louis Bossart if (l) 12079ee6631SPierre-Louis Bossart value = intel_readl(mem, reg); 12179ee6631SPierre-Louis Bossart else 12279ee6631SPierre-Louis Bossart value = intel_readw(mem, reg); 12379ee6631SPierre-Louis Bossart 12479ee6631SPierre-Louis Bossart return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value); 12579ee6631SPierre-Louis Bossart } 12679ee6631SPierre-Louis Bossart 12779ee6631SPierre-Louis Bossart static int intel_reg_show(struct seq_file *s_file, void *data) 12879ee6631SPierre-Louis Bossart { 12979ee6631SPierre-Louis Bossart struct sdw_intel *sdw = s_file->private; 1302523486bSPierre-Louis Bossart void __iomem *s = sdw->link_res->shim; 1312523486bSPierre-Louis Bossart void __iomem *a = sdw->link_res->alh; 13279ee6631SPierre-Louis Bossart char *buf; 13379ee6631SPierre-Louis Bossart ssize_t ret; 13479ee6631SPierre-Louis Bossart int i, j; 13579ee6631SPierre-Louis Bossart unsigned int links, reg; 13679ee6631SPierre-Louis Bossart 13779ee6631SPierre-Louis Bossart buf = kzalloc(RD_BUF, GFP_KERNEL); 13879ee6631SPierre-Louis Bossart if (!buf) 13979ee6631SPierre-Louis Bossart return -ENOMEM; 14079ee6631SPierre-Louis Bossart 1417f817068SPierre-Louis Bossart links = intel_readl(s, SDW_SHIM_LCAP) & SDW_SHIM_LCAP_LCOUNT_MASK; 14279ee6631SPierre-Louis Bossart 14379ee6631SPierre-Louis Bossart ret = scnprintf(buf, RD_BUF, "Register Value\n"); 14479ee6631SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n"); 14579ee6631SPierre-Louis Bossart 14679ee6631SPierre-Louis Bossart for (i = 0; i < links; i++) { 14779ee6631SPierre-Louis Bossart reg = SDW_SHIM_LCAP + i * 4; 14879ee6631SPierre-Louis Bossart ret += intel_sprintf(s, true, buf, ret, reg); 14979ee6631SPierre-Louis Bossart } 15079ee6631SPierre-Louis Bossart 15179ee6631SPierre-Louis Bossart for (i = 0; i < links; i++) { 15279ee6631SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i); 15379ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i)); 15479ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i)); 15579ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i)); 15679ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i)); 15779ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i)); 15879ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i)); 15979ee6631SPierre-Louis Bossart 16079ee6631SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\n PCMSyCH registers\n"); 16179ee6631SPierre-Louis Bossart 16279ee6631SPierre-Louis Bossart /* 16379ee6631SPierre-Louis Bossart * the value 10 is the number of PDIs. We will need a 16479ee6631SPierre-Louis Bossart * cleanup to remove hard-coded Intel configurations 16579ee6631SPierre-Louis Bossart * from cadence_master.c 16679ee6631SPierre-Louis Bossart */ 16779ee6631SPierre-Louis Bossart for (j = 0; j < 10; j++) { 16879ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, 16979ee6631SPierre-Louis Bossart SDW_SHIM_PCMSYCHM(i, j)); 17079ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, 17179ee6631SPierre-Louis Bossart SDW_SHIM_PCMSYCHC(i, j)); 17279ee6631SPierre-Louis Bossart } 173c27ce5c9SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\n IOCTL, CTMCTL\n"); 17479ee6631SPierre-Louis Bossart 17579ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i)); 17679ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i)); 17779ee6631SPierre-Louis Bossart } 17879ee6631SPierre-Louis Bossart 17979ee6631SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\nWake registers\n"); 18079ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN); 18179ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS); 18279ee6631SPierre-Louis Bossart 18379ee6631SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH STRMzCFG\n"); 18479ee6631SPierre-Louis Bossart for (i = 0; i < SDW_ALH_NUM_STREAMS; i++) 18579ee6631SPierre-Louis Bossart ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i)); 18679ee6631SPierre-Louis Bossart 18779ee6631SPierre-Louis Bossart seq_printf(s_file, "%s", buf); 18879ee6631SPierre-Louis Bossart kfree(buf); 18979ee6631SPierre-Louis Bossart 19079ee6631SPierre-Louis Bossart return 0; 19179ee6631SPierre-Louis Bossart } 19279ee6631SPierre-Louis Bossart DEFINE_SHOW_ATTRIBUTE(intel_reg); 19379ee6631SPierre-Louis Bossart 1940f9138e7SPierre-Louis Bossart static int intel_set_m_datamode(void *data, u64 value) 1950f9138e7SPierre-Louis Bossart { 1960f9138e7SPierre-Louis Bossart struct sdw_intel *sdw = data; 1970f9138e7SPierre-Louis Bossart struct sdw_bus *bus = &sdw->cdns.bus; 1980f9138e7SPierre-Louis Bossart 1990f9138e7SPierre-Louis Bossart if (value > SDW_PORT_DATA_MODE_STATIC_1) 2000f9138e7SPierre-Louis Bossart return -EINVAL; 2010f9138e7SPierre-Louis Bossart 2020f9138e7SPierre-Louis Bossart /* Userspace changed the hardware state behind the kernel's back */ 2030f9138e7SPierre-Louis Bossart add_taint(TAINT_USER, LOCKDEP_STILL_OK); 2040f9138e7SPierre-Louis Bossart 2050f9138e7SPierre-Louis Bossart bus->params.m_data_mode = value; 2060f9138e7SPierre-Louis Bossart 2070f9138e7SPierre-Louis Bossart return 0; 2080f9138e7SPierre-Louis Bossart } 2090f9138e7SPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(intel_set_m_datamode_fops, NULL, 2100f9138e7SPierre-Louis Bossart intel_set_m_datamode, "%llu\n"); 2110f9138e7SPierre-Louis Bossart 2120f9138e7SPierre-Louis Bossart static int intel_set_s_datamode(void *data, u64 value) 2130f9138e7SPierre-Louis Bossart { 2140f9138e7SPierre-Louis Bossart struct sdw_intel *sdw = data; 2150f9138e7SPierre-Louis Bossart struct sdw_bus *bus = &sdw->cdns.bus; 2160f9138e7SPierre-Louis Bossart 2170f9138e7SPierre-Louis Bossart if (value > SDW_PORT_DATA_MODE_STATIC_1) 2180f9138e7SPierre-Louis Bossart return -EINVAL; 2190f9138e7SPierre-Louis Bossart 2200f9138e7SPierre-Louis Bossart /* Userspace changed the hardware state behind the kernel's back */ 2210f9138e7SPierre-Louis Bossart add_taint(TAINT_USER, LOCKDEP_STILL_OK); 2220f9138e7SPierre-Louis Bossart 2230f9138e7SPierre-Louis Bossart bus->params.s_data_mode = value; 2240f9138e7SPierre-Louis Bossart 2250f9138e7SPierre-Louis Bossart return 0; 2260f9138e7SPierre-Louis Bossart } 2270f9138e7SPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(intel_set_s_datamode_fops, NULL, 2280f9138e7SPierre-Louis Bossart intel_set_s_datamode, "%llu\n"); 2290f9138e7SPierre-Louis Bossart 23079ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw) 23179ee6631SPierre-Louis Bossart { 23279ee6631SPierre-Louis Bossart struct dentry *root = sdw->cdns.bus.debugfs; 23379ee6631SPierre-Louis Bossart 23479ee6631SPierre-Louis Bossart if (!root) 23579ee6631SPierre-Louis Bossart return; 23679ee6631SPierre-Louis Bossart 23779ee6631SPierre-Louis Bossart sdw->debugfs = debugfs_create_dir("intel-sdw", root); 23879ee6631SPierre-Louis Bossart 23979ee6631SPierre-Louis Bossart debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw, 24079ee6631SPierre-Louis Bossart &intel_reg_fops); 24179ee6631SPierre-Louis Bossart 2420f9138e7SPierre-Louis Bossart debugfs_create_file("intel-m-datamode", 0200, sdw->debugfs, sdw, 2430f9138e7SPierre-Louis Bossart &intel_set_m_datamode_fops); 2440f9138e7SPierre-Louis Bossart 2450f9138e7SPierre-Louis Bossart debugfs_create_file("intel-s-datamode", 0200, sdw->debugfs, sdw, 2460f9138e7SPierre-Louis Bossart &intel_set_s_datamode_fops); 2470f9138e7SPierre-Louis Bossart 24879ee6631SPierre-Louis Bossart sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs); 24979ee6631SPierre-Louis Bossart } 25079ee6631SPierre-Louis Bossart 25179ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw) 25279ee6631SPierre-Louis Bossart { 25379ee6631SPierre-Louis Bossart debugfs_remove_recursive(sdw->debugfs); 25479ee6631SPierre-Louis Bossart } 25579ee6631SPierre-Louis Bossart #else 25679ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw) {} 25779ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw) {} 25879ee6631SPierre-Louis Bossart #endif /* CONFIG_DEBUG_FS */ 25979ee6631SPierre-Louis Bossart 26079ee6631SPierre-Louis Bossart /* 26171bb8a1bSVinod Koul * shim ops 26271bb8a1bSVinod Koul */ 2634a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */ 2644a17c441SPierre-Louis Bossart static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw) 26571bb8a1bSVinod Koul { 2662523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 26771bb8a1bSVinod Koul unsigned int link_id = sdw->instance; 2684a17c441SPierre-Louis Bossart u16 ioctl; 26971bb8a1bSVinod Koul 27071bb8a1bSVinod Koul /* Switch to MIP from Glue logic */ 27171bb8a1bSVinod Koul ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); 27271bb8a1bSVinod Koul 27371bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_DOE); 27471bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 2754a17c441SPierre-Louis Bossart usleep_range(10, 15); 27671bb8a1bSVinod Koul 27771bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_DO); 27871bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 2794a17c441SPierre-Louis Bossart usleep_range(10, 15); 28071bb8a1bSVinod Koul 28171bb8a1bSVinod Koul ioctl |= (SDW_SHIM_IOCTL_MIF); 28271bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 2834a17c441SPierre-Louis Bossart usleep_range(10, 15); 28471bb8a1bSVinod Koul 28571bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_BKE); 28671bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_COE); 28771bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 2884a17c441SPierre-Louis Bossart usleep_range(10, 15); 2894a17c441SPierre-Louis Bossart 2904a17c441SPierre-Louis Bossart /* at this point Master IP has full control of the I/Os */ 2914a17c441SPierre-Louis Bossart } 2924a17c441SPierre-Louis Bossart 2934a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */ 2944a17c441SPierre-Louis Bossart static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw) 2954a17c441SPierre-Louis Bossart { 2964a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 2974a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 2984a17c441SPierre-Louis Bossart u16 ioctl; 2994a17c441SPierre-Louis Bossart 3004a17c441SPierre-Louis Bossart /* Glue logic */ 3014a17c441SPierre-Louis Bossart ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); 3024a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_BKE; 3034a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_COE; 3044a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 3054a17c441SPierre-Louis Bossart usleep_range(10, 15); 3064a17c441SPierre-Louis Bossart 3074a17c441SPierre-Louis Bossart ioctl &= ~(SDW_SHIM_IOCTL_MIF); 3084a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 3094a17c441SPierre-Louis Bossart usleep_range(10, 15); 3104a17c441SPierre-Louis Bossart 3114a17c441SPierre-Louis Bossart /* at this point Integration Glue has full control of the I/Os */ 3124a17c441SPierre-Louis Bossart } 3134a17c441SPierre-Louis Bossart 314b81bcdb4SPierre-Louis Bossart /* this needs to be called with shim_lock */ 315b81bcdb4SPierre-Louis Bossart static void intel_shim_init(struct sdw_intel *sdw) 3164a17c441SPierre-Louis Bossart { 3174a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 3184a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 3194a17c441SPierre-Louis Bossart u16 ioctl = 0, act = 0; 3204a17c441SPierre-Louis Bossart 3214a17c441SPierre-Louis Bossart /* Initialize Shim */ 3224a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_BKE; 3234a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 3244a17c441SPierre-Louis Bossart usleep_range(10, 15); 3254a17c441SPierre-Louis Bossart 3264a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_WPDD; 3274a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 3284a17c441SPierre-Louis Bossart usleep_range(10, 15); 3294a17c441SPierre-Louis Bossart 3304a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_DO; 3314a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 3324a17c441SPierre-Louis Bossart usleep_range(10, 15); 3334a17c441SPierre-Louis Bossart 3344a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_DOE; 3354a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 3364a17c441SPierre-Louis Bossart usleep_range(10, 15); 3374a17c441SPierre-Louis Bossart 3384a17c441SPierre-Louis Bossart intel_shim_glue_to_master_ip(sdw); 33971bb8a1bSVinod Koul 340f067c925SVinod Koul u16p_replace_bits(&act, 0x1, SDW_SHIM_CTMCTL_DOAIS); 34171bb8a1bSVinod Koul act |= SDW_SHIM_CTMCTL_DACTQE; 34271bb8a1bSVinod Koul act |= SDW_SHIM_CTMCTL_DODS; 34371bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act); 3444a17c441SPierre-Louis Bossart usleep_range(10, 15); 34571bb8a1bSVinod Koul } 34671bb8a1bSVinod Koul 3470f3c54c2SPierre-Louis Bossart static int intel_shim_check_wake(struct sdw_intel *sdw) 3480f3c54c2SPierre-Louis Bossart { 3490f3c54c2SPierre-Louis Bossart void __iomem *shim; 3500f3c54c2SPierre-Louis Bossart u16 wake_sts; 35171bb8a1bSVinod Koul 3520f3c54c2SPierre-Louis Bossart shim = sdw->link_res->shim; 3530f3c54c2SPierre-Louis Bossart wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); 3540f3c54c2SPierre-Louis Bossart 3550f3c54c2SPierre-Louis Bossart return wake_sts & BIT(sdw->instance); 35671bb8a1bSVinod Koul } 35771bb8a1bSVinod Koul 358ab2c9132SRander Wang static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) 3594a17c441SPierre-Louis Bossart { 3604a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 3614a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 3624a17c441SPierre-Louis Bossart u16 wake_en, wake_sts; 3634a17c441SPierre-Louis Bossart 3644a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 3654a17c441SPierre-Louis Bossart wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); 3664a17c441SPierre-Louis Bossart 3674a17c441SPierre-Louis Bossart if (wake_enable) { 3684a17c441SPierre-Louis Bossart /* Enable the wakeup */ 3694a17c441SPierre-Louis Bossart wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id); 3704a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); 3714a17c441SPierre-Louis Bossart } else { 3724a17c441SPierre-Louis Bossart /* Disable the wake up interrupt */ 3734a17c441SPierre-Louis Bossart wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); 3744a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); 3754a17c441SPierre-Louis Bossart 3764a17c441SPierre-Louis Bossart /* Clear wake status */ 3774a17c441SPierre-Louis Bossart wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); 3783957db3aSLibin Yang wake_sts |= (SDW_SHIM_WAKESTS_STATUS << link_id); 3793957db3aSLibin Yang intel_writew(shim, SDW_SHIM_WAKESTS, wake_sts); 3804a17c441SPierre-Louis Bossart } 3814a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 3824a17c441SPierre-Louis Bossart } 3834a17c441SPierre-Louis Bossart 384bc872947SPierre-Louis Bossart static int intel_link_power_up(struct sdw_intel *sdw) 385bc872947SPierre-Louis Bossart { 386bc872947SPierre-Louis Bossart unsigned int link_id = sdw->instance; 387bc872947SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 388bc872947SPierre-Louis Bossart u32 *shim_mask = sdw->link_res->shim_mask; 389bc872947SPierre-Louis Bossart struct sdw_bus *bus = &sdw->cdns.bus; 390bc872947SPierre-Louis Bossart struct sdw_master_prop *prop = &bus->prop; 391bc872947SPierre-Louis Bossart u32 spa_mask, cpa_mask; 392bc872947SPierre-Louis Bossart u32 link_control; 393bc872947SPierre-Louis Bossart int ret = 0; 394bc872947SPierre-Louis Bossart u32 syncprd; 395bc872947SPierre-Louis Bossart u32 sync_reg; 396bc872947SPierre-Louis Bossart 397bc872947SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 398bc872947SPierre-Louis Bossart 399bc872947SPierre-Louis Bossart /* 400bc872947SPierre-Louis Bossart * The hardware relies on an internal counter, typically 4kHz, 401bc872947SPierre-Louis Bossart * to generate the SoundWire SSP - which defines a 'safe' 402bc872947SPierre-Louis Bossart * synchronization point between commands and audio transport 403bc872947SPierre-Louis Bossart * and allows for multi link synchronization. The SYNCPRD value 404bc872947SPierre-Louis Bossart * is only dependent on the oscillator clock provided to 405bc872947SPierre-Louis Bossart * the IP, so adjust based on _DSD properties reported in DSDT 406bc872947SPierre-Louis Bossart * tables. The values reported are based on either 24MHz 407bc872947SPierre-Louis Bossart * (CNL/CML) or 38.4 MHz (ICL/TGL+). 408bc872947SPierre-Louis Bossart */ 409bc872947SPierre-Louis Bossart if (prop->mclk_freq % 6000000) 410bc872947SPierre-Louis Bossart syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; 411bc872947SPierre-Louis Bossart else 412bc872947SPierre-Louis Bossart syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; 413bc872947SPierre-Louis Bossart 414bc872947SPierre-Louis Bossart if (!*shim_mask) { 415bc872947SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, "powering up all links\n"); 416bc872947SPierre-Louis Bossart 417bc872947SPierre-Louis Bossart /* we first need to program the SyncPRD/CPU registers */ 418bc872947SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, 419bc872947SPierre-Louis Bossart "first link up, programming SYNCPRD\n"); 420bc872947SPierre-Louis Bossart 421bc872947SPierre-Louis Bossart /* set SyncPRD period */ 422bc872947SPierre-Louis Bossart sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 423bc872947SPierre-Louis Bossart u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD); 424bc872947SPierre-Louis Bossart 425bc872947SPierre-Louis Bossart /* Set SyncCPU bit */ 426bc872947SPierre-Louis Bossart sync_reg |= SDW_SHIM_SYNC_SYNCCPU; 427bc872947SPierre-Louis Bossart intel_writel(shim, SDW_SHIM_SYNC, sync_reg); 428bc872947SPierre-Louis Bossart 429bc872947SPierre-Louis Bossart /* Link power up sequence */ 430bc872947SPierre-Louis Bossart link_control = intel_readl(shim, SDW_SHIM_LCTL); 431bc872947SPierre-Louis Bossart 432bc872947SPierre-Louis Bossart /* only power-up enabled links */ 433bc872947SPierre-Louis Bossart spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask); 434bc872947SPierre-Louis Bossart cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); 435bc872947SPierre-Louis Bossart 436bc872947SPierre-Louis Bossart link_control |= spa_mask; 437bc872947SPierre-Louis Bossart 438bc872947SPierre-Louis Bossart ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); 439bc872947SPierre-Louis Bossart if (ret < 0) { 440bc872947SPierre-Louis Bossart dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret); 441bc872947SPierre-Louis Bossart goto out; 442bc872947SPierre-Louis Bossart } 443bc872947SPierre-Louis Bossart 444bc872947SPierre-Louis Bossart /* SyncCPU will change once link is active */ 445bc872947SPierre-Louis Bossart ret = intel_wait_bit(shim, SDW_SHIM_SYNC, 446bc872947SPierre-Louis Bossart SDW_SHIM_SYNC_SYNCCPU, 0); 447bc872947SPierre-Louis Bossart if (ret < 0) { 448bc872947SPierre-Louis Bossart dev_err(sdw->cdns.dev, 449bc872947SPierre-Louis Bossart "Failed to set SHIM_SYNC: %d\n", ret); 450bc872947SPierre-Louis Bossart goto out; 451bc872947SPierre-Louis Bossart } 452bc872947SPierre-Louis Bossart } 453bc872947SPierre-Louis Bossart 454bc872947SPierre-Louis Bossart *shim_mask |= BIT(link_id); 455bc872947SPierre-Louis Bossart 456bc872947SPierre-Louis Bossart sdw->cdns.link_up = true; 457b81bcdb4SPierre-Louis Bossart 458b81bcdb4SPierre-Louis Bossart intel_shim_init(sdw); 459b81bcdb4SPierre-Louis Bossart 460bc872947SPierre-Louis Bossart out: 461bc872947SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 462bc872947SPierre-Louis Bossart 463bc872947SPierre-Louis Bossart return ret; 464bc872947SPierre-Louis Bossart } 465bc872947SPierre-Louis Bossart 4669b3b4b3fSPierre-Louis Bossart static int intel_link_power_down(struct sdw_intel *sdw) 4674a17c441SPierre-Louis Bossart { 4685ee74eb2SPierre-Louis Bossart u32 link_control, spa_mask, cpa_mask; 4694a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 4704a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 4714a17c441SPierre-Louis Bossart u32 *shim_mask = sdw->link_res->shim_mask; 4724a17c441SPierre-Louis Bossart int ret = 0; 4734a17c441SPierre-Louis Bossart 4744a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 4754a17c441SPierre-Louis Bossart 4764a17c441SPierre-Louis Bossart if (!(*shim_mask & BIT(link_id))) 4774a17c441SPierre-Louis Bossart dev_err(sdw->cdns.dev, 4784a17c441SPierre-Louis Bossart "%s: Unbalanced power-up/down calls\n", __func__); 4794a17c441SPierre-Louis Bossart 480ea6942daSPierre-Louis Bossart sdw->cdns.link_up = false; 481ea6942daSPierre-Louis Bossart 482ea6942daSPierre-Louis Bossart intel_shim_master_ip_to_glue(sdw); 483ea6942daSPierre-Louis Bossart 4844a17c441SPierre-Louis Bossart *shim_mask &= ~BIT(link_id); 4854a17c441SPierre-Louis Bossart 4865ee74eb2SPierre-Louis Bossart if (!*shim_mask) { 4875ee74eb2SPierre-Louis Bossart 48863198aaaSPierre-Louis Bossart dev_dbg(sdw->cdns.dev, "powering down all links\n"); 4895ee74eb2SPierre-Louis Bossart 4905ee74eb2SPierre-Louis Bossart /* Link power down sequence */ 4915ee74eb2SPierre-Louis Bossart link_control = intel_readl(shim, SDW_SHIM_LCTL); 4925ee74eb2SPierre-Louis Bossart 4935ee74eb2SPierre-Louis Bossart /* only power-down enabled links */ 4943b4979caSVinod Koul spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, ~sdw->link_res->link_mask); 4953b4979caSVinod Koul cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); 4965ee74eb2SPierre-Louis Bossart 4975ee74eb2SPierre-Louis Bossart link_control &= spa_mask; 4985ee74eb2SPierre-Louis Bossart 4995ee74eb2SPierre-Louis Bossart ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); 500ea6942daSPierre-Louis Bossart if (ret < 0) { 501ea6942daSPierre-Louis Bossart dev_err(sdw->cdns.dev, "%s: could not power down link\n", __func__); 502ea6942daSPierre-Louis Bossart 503ea6942daSPierre-Louis Bossart /* 504ea6942daSPierre-Louis Bossart * we leave the sdw->cdns.link_up flag as false since we've disabled 505ea6942daSPierre-Louis Bossart * the link at this point and cannot handle interrupts any longer. 506ea6942daSPierre-Louis Bossart */ 507ea6942daSPierre-Louis Bossart } 5085ee74eb2SPierre-Louis Bossart } 5095ee74eb2SPierre-Louis Bossart 5104a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 5114a17c441SPierre-Louis Bossart 5124a17c441SPierre-Louis Bossart return ret; 5135ee74eb2SPierre-Louis Bossart } 5144a17c441SPierre-Louis Bossart 51502629e45SPierre-Louis Bossart static void intel_shim_sync_arm(struct sdw_intel *sdw) 51602629e45SPierre-Louis Bossart { 51702629e45SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 51802629e45SPierre-Louis Bossart u32 sync_reg; 51902629e45SPierre-Louis Bossart 52002629e45SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 52102629e45SPierre-Louis Bossart 52202629e45SPierre-Louis Bossart /* update SYNC register */ 52302629e45SPierre-Louis Bossart sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 52402629e45SPierre-Louis Bossart sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance); 52502629e45SPierre-Louis Bossart intel_writel(shim, SDW_SHIM_SYNC, sync_reg); 52602629e45SPierre-Louis Bossart 52702629e45SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 52802629e45SPierre-Louis Bossart } 52902629e45SPierre-Louis Bossart 530437e3289SPierre-Louis Bossart static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw) 531437e3289SPierre-Louis Bossart { 532437e3289SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 533437e3289SPierre-Louis Bossart u32 sync_reg; 534437e3289SPierre-Louis Bossart int ret; 535437e3289SPierre-Louis Bossart 536437e3289SPierre-Louis Bossart /* Read SYNC register */ 537437e3289SPierre-Louis Bossart sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 538437e3289SPierre-Louis Bossart 539437e3289SPierre-Louis Bossart /* 540437e3289SPierre-Louis Bossart * Set SyncGO bit to synchronously trigger a bank switch for 541437e3289SPierre-Louis Bossart * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all 542437e3289SPierre-Louis Bossart * the Masters. 543437e3289SPierre-Louis Bossart */ 544437e3289SPierre-Louis Bossart sync_reg |= SDW_SHIM_SYNC_SYNCGO; 545437e3289SPierre-Louis Bossart 546437e3289SPierre-Louis Bossart ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, 547437e3289SPierre-Louis Bossart SDW_SHIM_SYNC_SYNCGO); 548437e3289SPierre-Louis Bossart 549437e3289SPierre-Louis Bossart if (ret < 0) 550437e3289SPierre-Louis Bossart dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret); 55171bb8a1bSVinod Koul 55271bb8a1bSVinod Koul return ret; 55371bb8a1bSVinod Koul } 55471bb8a1bSVinod Koul 555857a7c42SPierre-Louis Bossart static int intel_shim_sync_go(struct sdw_intel *sdw) 556857a7c42SPierre-Louis Bossart { 557857a7c42SPierre-Louis Bossart int ret; 558857a7c42SPierre-Louis Bossart 559857a7c42SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 560857a7c42SPierre-Louis Bossart 561857a7c42SPierre-Louis Bossart ret = intel_shim_sync_go_unlocked(sdw); 562857a7c42SPierre-Louis Bossart 563857a7c42SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 564857a7c42SPierre-Louis Bossart 565857a7c42SPierre-Louis Bossart return ret; 566857a7c42SPierre-Louis Bossart } 567857a7c42SPierre-Louis Bossart 56837a2d22bSVinod Koul /* 56937a2d22bSVinod Koul * PDI routines 57037a2d22bSVinod Koul */ 57137a2d22bSVinod Koul static void intel_pdi_init(struct sdw_intel *sdw, 57237a2d22bSVinod Koul struct sdw_cdns_stream_config *config) 57337a2d22bSVinod Koul { 5742523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 57537a2d22bSVinod Koul unsigned int link_id = sdw->instance; 57663a6aa96SPierre-Louis Bossart int pcm_cap; 57737a2d22bSVinod Koul 57837a2d22bSVinod Koul /* PCM Stream Capability */ 57937a2d22bSVinod Koul pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id)); 58037a2d22bSVinod Koul 5813b4979caSVinod Koul config->pcm_bd = FIELD_GET(SDW_SHIM_PCMSCAP_BSS, pcm_cap); 5823b4979caSVinod Koul config->pcm_in = FIELD_GET(SDW_SHIM_PCMSCAP_ISS, pcm_cap); 5833b4979caSVinod Koul config->pcm_out = FIELD_GET(SDW_SHIM_PCMSCAP_OSS, pcm_cap); 58437a2d22bSVinod Koul 585121f4361SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n", 586121f4361SPierre-Louis Bossart config->pcm_bd, config->pcm_in, config->pcm_out); 58737a2d22bSVinod Koul } 58837a2d22bSVinod Koul 58937a2d22bSVinod Koul static int 59063a6aa96SPierre-Louis Bossart intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num) 59137a2d22bSVinod Koul { 5922523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 59337a2d22bSVinod Koul unsigned int link_id = sdw->instance; 59437a2d22bSVinod Koul int count; 59537a2d22bSVinod Koul 59637a2d22bSVinod Koul count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); 59718046335SPierre-Louis Bossart 59818046335SPierre-Louis Bossart /* 59918046335SPierre-Louis Bossart * WORKAROUND: on all existing Intel controllers, pdi 60018046335SPierre-Louis Bossart * number 2 reports channel count as 1 even though it 60118046335SPierre-Louis Bossart * supports 8 channels. Performing hardcoding for pdi 60218046335SPierre-Louis Bossart * number 2. 60318046335SPierre-Louis Bossart */ 60418046335SPierre-Louis Bossart if (pdi_num == 2) 60518046335SPierre-Louis Bossart count = 7; 60618046335SPierre-Louis Bossart 60737a2d22bSVinod Koul /* zero based values for channel count in register */ 60837a2d22bSVinod Koul count++; 60937a2d22bSVinod Koul 61037a2d22bSVinod Koul return count; 61137a2d22bSVinod Koul } 61237a2d22bSVinod Koul 61337a2d22bSVinod Koul static int intel_pdi_get_ch_update(struct sdw_intel *sdw, 61437a2d22bSVinod Koul struct sdw_cdns_pdi *pdi, 61537a2d22bSVinod Koul unsigned int num_pdi, 61663a6aa96SPierre-Louis Bossart unsigned int *num_ch) 61737a2d22bSVinod Koul { 61837a2d22bSVinod Koul int i, ch_count = 0; 61937a2d22bSVinod Koul 62037a2d22bSVinod Koul for (i = 0; i < num_pdi; i++) { 62163a6aa96SPierre-Louis Bossart pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num); 62237a2d22bSVinod Koul ch_count += pdi->ch_count; 62337a2d22bSVinod Koul pdi++; 62437a2d22bSVinod Koul } 62537a2d22bSVinod Koul 62637a2d22bSVinod Koul *num_ch = ch_count; 62737a2d22bSVinod Koul return 0; 62837a2d22bSVinod Koul } 62937a2d22bSVinod Koul 63037a2d22bSVinod Koul static int intel_pdi_stream_ch_update(struct sdw_intel *sdw, 63163a6aa96SPierre-Louis Bossart struct sdw_cdns_streams *stream) 63237a2d22bSVinod Koul { 63337a2d22bSVinod Koul intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd, 63463a6aa96SPierre-Louis Bossart &stream->num_ch_bd); 63537a2d22bSVinod Koul 63637a2d22bSVinod Koul intel_pdi_get_ch_update(sdw, stream->in, stream->num_in, 63763a6aa96SPierre-Louis Bossart &stream->num_ch_in); 63837a2d22bSVinod Koul 63937a2d22bSVinod Koul intel_pdi_get_ch_update(sdw, stream->out, stream->num_out, 64063a6aa96SPierre-Louis Bossart &stream->num_ch_out); 64137a2d22bSVinod Koul 64237a2d22bSVinod Koul return 0; 64337a2d22bSVinod Koul } 64437a2d22bSVinod Koul 64537a2d22bSVinod Koul static int intel_pdi_ch_update(struct sdw_intel *sdw) 64637a2d22bSVinod Koul { 64763a6aa96SPierre-Louis Bossart intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm); 64837a2d22bSVinod Koul 64937a2d22bSVinod Koul return 0; 65037a2d22bSVinod Koul } 65137a2d22bSVinod Koul 65237a2d22bSVinod Koul static void 65337a2d22bSVinod Koul intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) 65437a2d22bSVinod Koul { 6552523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 65637a2d22bSVinod Koul unsigned int link_id = sdw->instance; 65737a2d22bSVinod Koul int pdi_conf = 0; 65837a2d22bSVinod Koul 659c134f914SPierre-Louis Bossart /* the Bulk and PCM streams are not contiguous */ 660c134f914SPierre-Louis Bossart pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; 661c134f914SPierre-Louis Bossart if (pdi->num >= 2) 662c134f914SPierre-Louis Bossart pdi->intel_alh_id += 2; 66337a2d22bSVinod Koul 66437a2d22bSVinod Koul /* 66537a2d22bSVinod Koul * Program stream parameters to stream SHIM register 66637a2d22bSVinod Koul * This is applicable for PCM stream only. 66737a2d22bSVinod Koul */ 66837a2d22bSVinod Koul if (pdi->type != SDW_STREAM_PCM) 66937a2d22bSVinod Koul return; 67037a2d22bSVinod Koul 67137a2d22bSVinod Koul if (pdi->dir == SDW_DATA_DIR_RX) 67237a2d22bSVinod Koul pdi_conf |= SDW_SHIM_PCMSYCM_DIR; 67337a2d22bSVinod Koul else 67437a2d22bSVinod Koul pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR); 67537a2d22bSVinod Koul 676f067c925SVinod Koul u32p_replace_bits(&pdi_conf, pdi->intel_alh_id, SDW_SHIM_PCMSYCM_STREAM); 677f067c925SVinod Koul u32p_replace_bits(&pdi_conf, pdi->l_ch_num, SDW_SHIM_PCMSYCM_LCHN); 678f067c925SVinod Koul u32p_replace_bits(&pdi_conf, pdi->h_ch_num, SDW_SHIM_PCMSYCM_HCHN); 67937a2d22bSVinod Koul 68037a2d22bSVinod Koul intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf); 68137a2d22bSVinod Koul } 68237a2d22bSVinod Koul 68337a2d22bSVinod Koul static void 68437a2d22bSVinod Koul intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) 68537a2d22bSVinod Koul { 6862523486bSPierre-Louis Bossart void __iomem *alh = sdw->link_res->alh; 68737a2d22bSVinod Koul unsigned int link_id = sdw->instance; 68837a2d22bSVinod Koul unsigned int conf; 68937a2d22bSVinod Koul 690c134f914SPierre-Louis Bossart /* the Bulk and PCM streams are not contiguous */ 691c134f914SPierre-Louis Bossart pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; 692c134f914SPierre-Louis Bossart if (pdi->num >= 2) 693c134f914SPierre-Louis Bossart pdi->intel_alh_id += 2; 69437a2d22bSVinod Koul 69537a2d22bSVinod Koul /* Program Stream config ALH register */ 69637a2d22bSVinod Koul conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id)); 69737a2d22bSVinod Koul 698f067c925SVinod Koul u32p_replace_bits(&conf, SDW_ALH_STRMZCFG_DMAT_VAL, SDW_ALH_STRMZCFG_DMAT); 699f067c925SVinod Koul u32p_replace_bits(&conf, pdi->ch_count - 1, SDW_ALH_STRMZCFG_CHN); 70037a2d22bSVinod Koul 70137a2d22bSVinod Koul intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); 70237a2d22bSVinod Koul } 70337a2d22bSVinod Koul 7044b206d34SRander Wang static int intel_params_stream(struct sdw_intel *sdw, 705b86947b5SPierre-Louis Bossart int stream, 706c46302ecSVinod Koul struct snd_soc_dai *dai, 7074b206d34SRander Wang struct snd_pcm_hw_params *hw_params, 7084b206d34SRander Wang int link_id, int alh_stream_id) 709c46302ecSVinod Koul { 7102523486bSPierre-Louis Bossart struct sdw_intel_link_res *res = sdw->link_res; 7114b206d34SRander Wang struct sdw_intel_stream_params_data params_data; 71205c8afe4SPierre-Louis Bossart 713b86947b5SPierre-Louis Bossart params_data.stream = stream; /* direction */ 7144b206d34SRander Wang params_data.dai = dai; 7154b206d34SRander Wang params_data.hw_params = hw_params; 7164b206d34SRander Wang params_data.link_id = link_id; 7174b206d34SRander Wang params_data.alh_stream_id = alh_stream_id; 718c46302ecSVinod Koul 7194b206d34SRander Wang if (res->ops && res->ops->params_stream && res->dev) 7204b206d34SRander Wang return res->ops->params_stream(res->dev, 7214b206d34SRander Wang ¶ms_data); 722c46302ecSVinod Koul return -EIO; 723c46302ecSVinod Koul } 724c46302ecSVinod Koul 725eff346f2SPierre-Louis Bossart static int intel_free_stream(struct sdw_intel *sdw, 726b86947b5SPierre-Louis Bossart int stream, 727eff346f2SPierre-Louis Bossart struct snd_soc_dai *dai, 728eff346f2SPierre-Louis Bossart int link_id) 729eff346f2SPierre-Louis Bossart { 730eff346f2SPierre-Louis Bossart struct sdw_intel_link_res *res = sdw->link_res; 731eff346f2SPierre-Louis Bossart struct sdw_intel_stream_free_data free_data; 732eff346f2SPierre-Louis Bossart 733b86947b5SPierre-Louis Bossart free_data.stream = stream; /* direction */ 734eff346f2SPierre-Louis Bossart free_data.dai = dai; 735eff346f2SPierre-Louis Bossart free_data.link_id = link_id; 736eff346f2SPierre-Louis Bossart 737eff346f2SPierre-Louis Bossart if (res->ops && res->ops->free_stream && res->dev) 738eff346f2SPierre-Louis Bossart return res->ops->free_stream(res->dev, 739eff346f2SPierre-Louis Bossart &free_data); 740eff346f2SPierre-Louis Bossart 741eff346f2SPierre-Louis Bossart return 0; 742eff346f2SPierre-Louis Bossart } 743eff346f2SPierre-Louis Bossart 744c46302ecSVinod Koul /* 74530246e2dSShreyas NC * bank switch routines 74630246e2dSShreyas NC */ 74730246e2dSShreyas NC 748b3ad31f3SPierre-Louis Bossart static int intel_pre_bank_switch(struct sdw_intel *sdw) 74930246e2dSShreyas NC { 750b3ad31f3SPierre-Louis Bossart struct sdw_cdns *cdns = &sdw->cdns; 751b3ad31f3SPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 75230246e2dSShreyas NC 75330246e2dSShreyas NC /* Write to register only for multi-link */ 75430246e2dSShreyas NC if (!bus->multi_link) 75530246e2dSShreyas NC return 0; 75630246e2dSShreyas NC 75702629e45SPierre-Louis Bossart intel_shim_sync_arm(sdw); 75830246e2dSShreyas NC 75930246e2dSShreyas NC return 0; 76030246e2dSShreyas NC } 76130246e2dSShreyas NC 762b3ad31f3SPierre-Louis Bossart static int intel_post_bank_switch(struct sdw_intel *sdw) 76330246e2dSShreyas NC { 764b3ad31f3SPierre-Louis Bossart struct sdw_cdns *cdns = &sdw->cdns; 765b3ad31f3SPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 7662523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 76730246e2dSShreyas NC int sync_reg, ret; 76830246e2dSShreyas NC 76930246e2dSShreyas NC /* Write to register only for multi-link */ 77030246e2dSShreyas NC if (!bus->multi_link) 77130246e2dSShreyas NC return 0; 77230246e2dSShreyas NC 7734a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 7744a17c441SPierre-Louis Bossart 77530246e2dSShreyas NC /* Read SYNC register */ 77630246e2dSShreyas NC sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 77730246e2dSShreyas NC 77830246e2dSShreyas NC /* 77930246e2dSShreyas NC * post_bank_switch() ops is called from the bus in loop for 78030246e2dSShreyas NC * all the Masters in the steam with the expectation that 78130246e2dSShreyas NC * we trigger the bankswitch for the only first Master in the list 78230246e2dSShreyas NC * and do nothing for the other Masters 78330246e2dSShreyas NC * 78430246e2dSShreyas NC * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master. 78530246e2dSShreyas NC */ 7864a17c441SPierre-Louis Bossart if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) { 7874a17c441SPierre-Louis Bossart ret = 0; 7884a17c441SPierre-Louis Bossart goto unlock; 7894a17c441SPierre-Louis Bossart } 79030246e2dSShreyas NC 791437e3289SPierre-Louis Bossart ret = intel_shim_sync_go_unlocked(sdw); 7924a17c441SPierre-Louis Bossart unlock: 7934a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 79430246e2dSShreyas NC 79530246e2dSShreyas NC if (ret < 0) 79617ed5befSPierre-Louis Bossart dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret); 79730246e2dSShreyas NC 79830246e2dSShreyas NC return ret; 79930246e2dSShreyas NC } 80030246e2dSShreyas NC 80130246e2dSShreyas NC /* 802c46302ecSVinod Koul * DAI routines 803c46302ecSVinod Koul */ 804c46302ecSVinod Koul 8055e7484d0SRander Wang static int intel_startup(struct snd_pcm_substream *substream, 8065e7484d0SRander Wang struct snd_soc_dai *dai) 8075e7484d0SRander Wang { 808ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 809ebf878edSPierre-Louis Bossart int ret; 810ebf878edSPierre-Louis Bossart 811fff1fd9cSPierre-Louis Bossart ret = pm_runtime_resume_and_get(cdns->dev); 812ebf878edSPierre-Louis Bossart if (ret < 0 && ret != -EACCES) { 813ebf878edSPierre-Louis Bossart dev_err_ratelimited(cdns->dev, 814fff1fd9cSPierre-Louis Bossart "pm_runtime_resume_and_get failed in %s, ret %d\n", 815ebf878edSPierre-Louis Bossart __func__, ret); 816ebf878edSPierre-Louis Bossart return ret; 817ebf878edSPierre-Louis Bossart } 818ff16d1e5SPierre-Louis Bossart return 0; 8195e7484d0SRander Wang } 8205e7484d0SRander Wang 821c46302ecSVinod Koul static int intel_hw_params(struct snd_pcm_substream *substream, 822c46302ecSVinod Koul struct snd_pcm_hw_params *params, 823c46302ecSVinod Koul struct snd_soc_dai *dai) 824c46302ecSVinod Koul { 825c46302ecSVinod Koul struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 826c46302ecSVinod Koul struct sdw_intel *sdw = cdns_to_intel(cdns); 827e0767e39SPierre-Louis Bossart struct sdw_cdns_dai_runtime *dai_runtime; 82857a34790SPierre-Louis Bossart struct sdw_cdns_pdi *pdi; 829c46302ecSVinod Koul struct sdw_stream_config sconfig; 830c46302ecSVinod Koul struct sdw_port_config *pconfig; 83157a34790SPierre-Louis Bossart int ch, dir; 83257a34790SPierre-Louis Bossart int ret; 833c46302ecSVinod Koul 8347dddead7SPierre-Louis Bossart dai_runtime = cdns->dai_runtime_array[dai->id]; 835e0767e39SPierre-Louis Bossart if (!dai_runtime) 836c46302ecSVinod Koul return -EIO; 837c46302ecSVinod Koul 838c46302ecSVinod Koul ch = params_channels(params); 839c46302ecSVinod Koul if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 840c46302ecSVinod Koul dir = SDW_DATA_DIR_RX; 841c46302ecSVinod Koul else 842c46302ecSVinod Koul dir = SDW_DATA_DIR_TX; 843c46302ecSVinod Koul 8441b53385eSBard Liao pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); 845c46302ecSVinod Koul 84657a34790SPierre-Louis Bossart if (!pdi) { 847c46302ecSVinod Koul ret = -EINVAL; 84857a34790SPierre-Louis Bossart goto error; 849c46302ecSVinod Koul } 85057a34790SPierre-Louis Bossart 85157a34790SPierre-Louis Bossart /* do run-time configurations for SHIM, ALH and PDI/PORT */ 85257a34790SPierre-Louis Bossart intel_pdi_shim_configure(sdw, pdi); 85357a34790SPierre-Louis Bossart intel_pdi_alh_configure(sdw, pdi); 85457a34790SPierre-Louis Bossart sdw_cdns_config_stream(cdns, ch, dir, pdi); 85557a34790SPierre-Louis Bossart 856a5a0239cSBard Liao /* store pdi and hw_params, may be needed in prepare step */ 857e0767e39SPierre-Louis Bossart dai_runtime->paused = false; 858e0767e39SPierre-Louis Bossart dai_runtime->suspended = false; 859e0767e39SPierre-Louis Bossart dai_runtime->pdi = pdi; 860e0767e39SPierre-Louis Bossart dai_runtime->hw_params = params; 861c46302ecSVinod Koul 862c46302ecSVinod Koul /* Inform DSP about PDI stream number */ 863b86947b5SPierre-Louis Bossart ret = intel_params_stream(sdw, substream->stream, dai, params, 8644b206d34SRander Wang sdw->instance, 86557a34790SPierre-Louis Bossart pdi->intel_alh_id); 866c46302ecSVinod Koul if (ret) 86757a34790SPierre-Louis Bossart goto error; 868c46302ecSVinod Koul 869c46302ecSVinod Koul sconfig.direction = dir; 870c46302ecSVinod Koul sconfig.ch_count = ch; 871c46302ecSVinod Koul sconfig.frame_rate = params_rate(params); 872e0767e39SPierre-Louis Bossart sconfig.type = dai_runtime->stream_type; 873c46302ecSVinod Koul 874c46302ecSVinod Koul sconfig.bps = snd_pcm_format_width(params_format(params)); 875c46302ecSVinod Koul 876c46302ecSVinod Koul /* Port configuration */ 877235ae89bSZheng Yongjun pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL); 878c46302ecSVinod Koul if (!pconfig) { 879c46302ecSVinod Koul ret = -ENOMEM; 88057a34790SPierre-Louis Bossart goto error; 881c46302ecSVinod Koul } 882c46302ecSVinod Koul 88357a34790SPierre-Louis Bossart pconfig->num = pdi->num; 88457a34790SPierre-Louis Bossart pconfig->ch_mask = (1 << ch) - 1; 885c46302ecSVinod Koul 886c46302ecSVinod Koul ret = sdw_stream_add_master(&cdns->bus, &sconfig, 887e0767e39SPierre-Louis Bossart pconfig, 1, dai_runtime->stream); 88857a34790SPierre-Louis Bossart if (ret) 88917ed5befSPierre-Louis Bossart dev_err(cdns->dev, "add master to stream failed:%d\n", ret); 890c46302ecSVinod Koul 891c46302ecSVinod Koul kfree(pconfig); 89257a34790SPierre-Louis Bossart error: 893c46302ecSVinod Koul return ret; 894c46302ecSVinod Koul } 895c46302ecSVinod Koul 89627b198f4SRander Wang static int intel_prepare(struct snd_pcm_substream *substream, 89727b198f4SRander Wang struct snd_soc_dai *dai) 89827b198f4SRander Wang { 899a5a0239cSBard Liao struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 900a5a0239cSBard Liao struct sdw_intel *sdw = cdns_to_intel(cdns); 901e0767e39SPierre-Louis Bossart struct sdw_cdns_dai_runtime *dai_runtime; 902a5a0239cSBard Liao int ch, dir; 903244eb888SPierre-Louis Bossart int ret = 0; 90427b198f4SRander Wang 9057dddead7SPierre-Louis Bossart dai_runtime = cdns->dai_runtime_array[dai->id]; 906e0767e39SPierre-Louis Bossart if (!dai_runtime) { 907e0767e39SPierre-Louis Bossart dev_err(dai->dev, "failed to get dai runtime in %s\n", 90827b198f4SRander Wang __func__); 90927b198f4SRander Wang return -EIO; 91027b198f4SRander Wang } 91127b198f4SRander Wang 912e0767e39SPierre-Louis Bossart if (dai_runtime->suspended) { 913e0767e39SPierre-Louis Bossart dai_runtime->suspended = false; 914a5a0239cSBard Liao 915a5a0239cSBard Liao /* 916a5a0239cSBard Liao * .prepare() is called after system resume, where we 917a5a0239cSBard Liao * need to reinitialize the SHIM/ALH/Cadence IP. 918a5a0239cSBard Liao * .prepare() is also called to deal with underflows, 919a5a0239cSBard Liao * but in those cases we cannot touch ALH/SHIM 920a5a0239cSBard Liao * registers 921a5a0239cSBard Liao */ 922a5a0239cSBard Liao 923a5a0239cSBard Liao /* configure stream */ 924e0767e39SPierre-Louis Bossart ch = params_channels(dai_runtime->hw_params); 925a5a0239cSBard Liao if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 926a5a0239cSBard Liao dir = SDW_DATA_DIR_RX; 927a5a0239cSBard Liao else 928a5a0239cSBard Liao dir = SDW_DATA_DIR_TX; 929a5a0239cSBard Liao 930e0767e39SPierre-Louis Bossart intel_pdi_shim_configure(sdw, dai_runtime->pdi); 931e0767e39SPierre-Louis Bossart intel_pdi_alh_configure(sdw, dai_runtime->pdi); 932e0767e39SPierre-Louis Bossart sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi); 933a5a0239cSBard Liao 934a5a0239cSBard Liao /* Inform DSP about PDI stream number */ 935b86947b5SPierre-Louis Bossart ret = intel_params_stream(sdw, substream->stream, dai, 936e0767e39SPierre-Louis Bossart dai_runtime->hw_params, 937a5a0239cSBard Liao sdw->instance, 938e0767e39SPierre-Louis Bossart dai_runtime->pdi->intel_alh_id); 939a5a0239cSBard Liao } 940a5a0239cSBard Liao 941a5a0239cSBard Liao return ret; 94227b198f4SRander Wang } 94327b198f4SRander Wang 944c46302ecSVinod Koul static int 945c46302ecSVinod Koul intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 946c46302ecSVinod Koul { 947c46302ecSVinod Koul struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 948eff346f2SPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 949e0767e39SPierre-Louis Bossart struct sdw_cdns_dai_runtime *dai_runtime; 950c46302ecSVinod Koul int ret; 951c46302ecSVinod Koul 9527dddead7SPierre-Louis Bossart dai_runtime = cdns->dai_runtime_array[dai->id]; 953e0767e39SPierre-Louis Bossart if (!dai_runtime) 954c46302ecSVinod Koul return -EIO; 955c46302ecSVinod Koul 956244eb888SPierre-Louis Bossart /* 957244eb888SPierre-Louis Bossart * The sdw stream state will transition to RELEASED when stream-> 958244eb888SPierre-Louis Bossart * master_list is empty. So the stream state will transition to 959244eb888SPierre-Louis Bossart * DEPREPARED for the first cpu-dai and to RELEASED for the last 960244eb888SPierre-Louis Bossart * cpu-dai. 961244eb888SPierre-Louis Bossart */ 962e0767e39SPierre-Louis Bossart ret = sdw_stream_remove_master(&cdns->bus, dai_runtime->stream); 963eff346f2SPierre-Louis Bossart if (ret < 0) { 96417ed5befSPierre-Louis Bossart dev_err(dai->dev, "remove master from stream %s failed: %d\n", 965e0767e39SPierre-Louis Bossart dai_runtime->stream->name, ret); 966c46302ecSVinod Koul return ret; 967c46302ecSVinod Koul } 968c46302ecSVinod Koul 969b86947b5SPierre-Louis Bossart ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); 970eff346f2SPierre-Louis Bossart if (ret < 0) { 9714e3ea93eSPierre-Louis Bossart dev_err(dai->dev, "intel_free_stream: failed %d\n", ret); 972eff346f2SPierre-Louis Bossart return ret; 973eff346f2SPierre-Louis Bossart } 974eff346f2SPierre-Louis Bossart 975e0767e39SPierre-Louis Bossart dai_runtime->hw_params = NULL; 976e0767e39SPierre-Louis Bossart dai_runtime->pdi = NULL; 977a5a0239cSBard Liao 978eff346f2SPierre-Louis Bossart return 0; 979eff346f2SPierre-Louis Bossart } 980eff346f2SPierre-Louis Bossart 981183c7687SPierre-Louis Bossart static void intel_shutdown(struct snd_pcm_substream *substream, 982183c7687SPierre-Louis Bossart struct snd_soc_dai *dai) 983183c7687SPierre-Louis Bossart { 984ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 985183c7687SPierre-Louis Bossart 986ebf878edSPierre-Louis Bossart pm_runtime_mark_last_busy(cdns->dev); 987ebf878edSPierre-Louis Bossart pm_runtime_put_autosuspend(cdns->dev); 988183c7687SPierre-Louis Bossart } 989183c7687SPierre-Louis Bossart 990c46302ecSVinod Koul static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, 991c46302ecSVinod Koul void *stream, int direction) 992c46302ecSVinod Koul { 99363a6aa96SPierre-Louis Bossart return cdns_set_sdw_stream(dai, stream, direction); 994c46302ecSVinod Koul } 995c46302ecSVinod Koul 99609553140SPierre-Louis Bossart static void *intel_get_sdw_stream(struct snd_soc_dai *dai, 99709553140SPierre-Louis Bossart int direction) 99809553140SPierre-Louis Bossart { 9997dddead7SPierre-Louis Bossart struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 1000e0767e39SPierre-Louis Bossart struct sdw_cdns_dai_runtime *dai_runtime; 100109553140SPierre-Louis Bossart 10027dddead7SPierre-Louis Bossart dai_runtime = cdns->dai_runtime_array[dai->id]; 1003e0767e39SPierre-Louis Bossart if (!dai_runtime) 100406dcb4e4SPierre-Louis Bossart return ERR_PTR(-EINVAL); 100509553140SPierre-Louis Bossart 1006e0767e39SPierre-Louis Bossart return dai_runtime->stream; 100709553140SPierre-Louis Bossart } 100809553140SPierre-Louis Bossart 10098ddeafb9SRanjani Sridharan static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) 10108ddeafb9SRanjani Sridharan { 10118ddeafb9SRanjani Sridharan struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 10128ddeafb9SRanjani Sridharan struct sdw_intel *sdw = cdns_to_intel(cdns); 10136d1c1a73SBard Liao struct sdw_intel_link_res *res = sdw->link_res; 1014e0767e39SPierre-Louis Bossart struct sdw_cdns_dai_runtime *dai_runtime; 10158ddeafb9SRanjani Sridharan int ret = 0; 10168ddeafb9SRanjani Sridharan 10176d1c1a73SBard Liao /* 10186d1c1a73SBard Liao * The .trigger callback is used to send required IPC to audio 10196d1c1a73SBard Liao * firmware. The .free_stream callback will still be called 10206d1c1a73SBard Liao * by intel_free_stream() in the TRIGGER_SUSPEND case. 10216d1c1a73SBard Liao */ 10226d1c1a73SBard Liao if (res->ops && res->ops->trigger) 10236d1c1a73SBard Liao res->ops->trigger(dai, cmd, substream->stream); 10246d1c1a73SBard Liao 10257dddead7SPierre-Louis Bossart dai_runtime = cdns->dai_runtime_array[dai->id]; 1026e0767e39SPierre-Louis Bossart if (!dai_runtime) { 1027e0767e39SPierre-Louis Bossart dev_err(dai->dev, "failed to get dai runtime in %s\n", 10288ddeafb9SRanjani Sridharan __func__); 10298ddeafb9SRanjani Sridharan return -EIO; 10308ddeafb9SRanjani Sridharan } 10318ddeafb9SRanjani Sridharan 10328ddeafb9SRanjani Sridharan switch (cmd) { 10338ddeafb9SRanjani Sridharan case SNDRV_PCM_TRIGGER_SUSPEND: 10348ddeafb9SRanjani Sridharan 10358ddeafb9SRanjani Sridharan /* 10368ddeafb9SRanjani Sridharan * The .prepare callback is used to deal with xruns and resume operations. 10378ddeafb9SRanjani Sridharan * In the case of xruns, the DMAs and SHIM registers cannot be touched, 10388ddeafb9SRanjani Sridharan * but for resume operations the DMAs and SHIM registers need to be initialized. 10398ddeafb9SRanjani Sridharan * the .trigger callback is used to track the suspend case only. 10408ddeafb9SRanjani Sridharan */ 10418ddeafb9SRanjani Sridharan 1042e0767e39SPierre-Louis Bossart dai_runtime->suspended = true; 10438ddeafb9SRanjani Sridharan 10448ddeafb9SRanjani Sridharan ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); 10458ddeafb9SRanjani Sridharan break; 10468ddeafb9SRanjani Sridharan 10478ddeafb9SRanjani Sridharan case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 1048e0767e39SPierre-Louis Bossart dai_runtime->paused = true; 10498ddeafb9SRanjani Sridharan break; 10508ddeafb9SRanjani Sridharan case SNDRV_PCM_TRIGGER_STOP: 10518ddeafb9SRanjani Sridharan case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 1052e0767e39SPierre-Louis Bossart dai_runtime->paused = false; 10538ddeafb9SRanjani Sridharan break; 10548ddeafb9SRanjani Sridharan default: 10558ddeafb9SRanjani Sridharan break; 10568ddeafb9SRanjani Sridharan } 10578ddeafb9SRanjani Sridharan 10588ddeafb9SRanjani Sridharan return ret; 10598ddeafb9SRanjani Sridharan } 10608ddeafb9SRanjani Sridharan 10613e9c9f90SPierre-Louis Bossart static int intel_component_probe(struct snd_soc_component *component) 10623e9c9f90SPierre-Louis Bossart { 10633e9c9f90SPierre-Louis Bossart int ret; 10643e9c9f90SPierre-Louis Bossart 10653e9c9f90SPierre-Louis Bossart /* 10663e9c9f90SPierre-Louis Bossart * make sure the device is pm_runtime_active before initiating 10673e9c9f90SPierre-Louis Bossart * bus transactions during the card registration. 10683e9c9f90SPierre-Louis Bossart * We use pm_runtime_resume() here, without taking a reference 10693e9c9f90SPierre-Louis Bossart * and releasing it immediately. 10703e9c9f90SPierre-Louis Bossart */ 10713e9c9f90SPierre-Louis Bossart ret = pm_runtime_resume(component->dev); 10723e9c9f90SPierre-Louis Bossart if (ret < 0 && ret != -EACCES) 10733e9c9f90SPierre-Louis Bossart return ret; 10743e9c9f90SPierre-Louis Bossart 10753e9c9f90SPierre-Louis Bossart return 0; 10763e9c9f90SPierre-Louis Bossart } 10773e9c9f90SPierre-Louis Bossart 10788ddeafb9SRanjani Sridharan static int intel_component_dais_suspend(struct snd_soc_component *component) 10798ddeafb9SRanjani Sridharan { 10808ddeafb9SRanjani Sridharan struct snd_soc_dai *dai; 10818ddeafb9SRanjani Sridharan 10828ddeafb9SRanjani Sridharan /* 10838ddeafb9SRanjani Sridharan * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core 10848ddeafb9SRanjani Sridharan * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state. 10858ddeafb9SRanjani Sridharan * Since the component suspend is called last, we can trap this corner case 10868ddeafb9SRanjani Sridharan * and force the DAIs to release their resources. 10878ddeafb9SRanjani Sridharan */ 10888ddeafb9SRanjani Sridharan for_each_component_dais(component, dai) { 10898ddeafb9SRanjani Sridharan struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 10908ddeafb9SRanjani Sridharan struct sdw_intel *sdw = cdns_to_intel(cdns); 1091e0767e39SPierre-Louis Bossart struct sdw_cdns_dai_runtime *dai_runtime; 10928ddeafb9SRanjani Sridharan int ret; 10938ddeafb9SRanjani Sridharan 10947dddead7SPierre-Louis Bossart dai_runtime = cdns->dai_runtime_array[dai->id]; 10958ddeafb9SRanjani Sridharan 1096e0767e39SPierre-Louis Bossart if (!dai_runtime) 10978ddeafb9SRanjani Sridharan continue; 10988ddeafb9SRanjani Sridharan 1099e0767e39SPierre-Louis Bossart if (dai_runtime->suspended) 11008ddeafb9SRanjani Sridharan continue; 11018ddeafb9SRanjani Sridharan 1102e0767e39SPierre-Louis Bossart if (dai_runtime->paused) { 1103e0767e39SPierre-Louis Bossart dai_runtime->suspended = true; 11048ddeafb9SRanjani Sridharan 11057dddead7SPierre-Louis Bossart ret = intel_free_stream(sdw, dai_runtime->direction, dai, sdw->instance); 11068ddeafb9SRanjani Sridharan if (ret < 0) 11078ddeafb9SRanjani Sridharan return ret; 11088ddeafb9SRanjani Sridharan } 11098ddeafb9SRanjani Sridharan } 11108ddeafb9SRanjani Sridharan 11118ddeafb9SRanjani Sridharan return 0; 11128ddeafb9SRanjani Sridharan } 11138ddeafb9SRanjani Sridharan 1114b1635596SJulia Lawall static const struct snd_soc_dai_ops intel_pcm_dai_ops = { 11155e7484d0SRander Wang .startup = intel_startup, 1116c46302ecSVinod Koul .hw_params = intel_hw_params, 111727b198f4SRander Wang .prepare = intel_prepare, 1118c46302ecSVinod Koul .hw_free = intel_hw_free, 11198ddeafb9SRanjani Sridharan .trigger = intel_trigger, 1120183c7687SPierre-Louis Bossart .shutdown = intel_shutdown, 1121e8444560SPierre-Louis Bossart .set_stream = intel_pcm_set_sdw_stream, 1122e8444560SPierre-Louis Bossart .get_stream = intel_get_sdw_stream, 1123c46302ecSVinod Koul }; 1124c46302ecSVinod Koul 1125c46302ecSVinod Koul static const struct snd_soc_component_driver dai_component = { 1126c46302ecSVinod Koul .name = "soundwire", 11273e9c9f90SPierre-Louis Bossart .probe = intel_component_probe, 1128ca682020SCharles Keepax .suspend = intel_component_dais_suspend, 1129ca682020SCharles Keepax .legacy_dai_naming = 1, 1130c46302ecSVinod Koul }; 1131c46302ecSVinod Koul 1132c46302ecSVinod Koul static int intel_create_dai(struct sdw_cdns *cdns, 1133c46302ecSVinod Koul struct snd_soc_dai_driver *dais, 1134c46302ecSVinod Koul enum intel_pdi_type type, 113563a6aa96SPierre-Louis Bossart u32 num, u32 off, u32 max_ch) 1136c46302ecSVinod Koul { 1137c46302ecSVinod Koul int i; 1138c46302ecSVinod Koul 1139c46302ecSVinod Koul if (num == 0) 1140c46302ecSVinod Koul return 0; 1141c46302ecSVinod Koul 1142c46302ecSVinod Koul /* TODO: Read supported rates/formats from hardware */ 1143c46302ecSVinod Koul for (i = off; i < (off + num); i++) { 1144bf6d6e68SPierre-Louis Bossart dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL, 1145bf6d6e68SPierre-Louis Bossart "SDW%d Pin%d", 1146c46302ecSVinod Koul cdns->instance, i); 1147c46302ecSVinod Koul if (!dais[i].name) 1148c46302ecSVinod Koul return -ENOMEM; 1149c46302ecSVinod Koul 1150c46302ecSVinod Koul if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { 1151c46302ecSVinod Koul dais[i].playback.channels_min = 1; 1152c46302ecSVinod Koul dais[i].playback.channels_max = max_ch; 1153c46302ecSVinod Koul dais[i].playback.rates = SNDRV_PCM_RATE_48000; 1154c46302ecSVinod Koul dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE; 1155c46302ecSVinod Koul } 1156c46302ecSVinod Koul 1157c46302ecSVinod Koul if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { 115839194128SSrinivas Kandagatla dais[i].capture.channels_min = 1; 115939194128SSrinivas Kandagatla dais[i].capture.channels_max = max_ch; 1160c46302ecSVinod Koul dais[i].capture.rates = SNDRV_PCM_RATE_48000; 1161c46302ecSVinod Koul dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; 1162c46302ecSVinod Koul } 1163c46302ecSVinod Koul 1164c46302ecSVinod Koul dais[i].ops = &intel_pcm_dai_ops; 1165c46302ecSVinod Koul } 1166c46302ecSVinod Koul 1167c46302ecSVinod Koul return 0; 1168c46302ecSVinod Koul } 1169c46302ecSVinod Koul 1170c46302ecSVinod Koul static int intel_register_dai(struct sdw_intel *sdw) 1171c46302ecSVinod Koul { 11727dddead7SPierre-Louis Bossart struct sdw_cdns_dai_runtime **dai_runtime_array; 117330cbae66SPierre-Louis Bossart struct sdw_cdns_stream_config config; 1174c46302ecSVinod Koul struct sdw_cdns *cdns = &sdw->cdns; 1175c46302ecSVinod Koul struct sdw_cdns_streams *stream; 1176c46302ecSVinod Koul struct snd_soc_dai_driver *dais; 1177c46302ecSVinod Koul int num_dai, ret, off = 0; 1178c46302ecSVinod Koul 117930cbae66SPierre-Louis Bossart /* Read the PDI config and initialize cadence PDI */ 118030cbae66SPierre-Louis Bossart intel_pdi_init(sdw, &config); 118130cbae66SPierre-Louis Bossart ret = sdw_cdns_pdi_init(cdns, config); 118230cbae66SPierre-Louis Bossart if (ret) 118330cbae66SPierre-Louis Bossart return ret; 118430cbae66SPierre-Louis Bossart 118530cbae66SPierre-Louis Bossart intel_pdi_ch_update(sdw); 118630cbae66SPierre-Louis Bossart 1187c46302ecSVinod Koul /* DAIs are created based on total number of PDIs supported */ 118863a6aa96SPierre-Louis Bossart num_dai = cdns->pcm.num_pdi; 1189c46302ecSVinod Koul 11907dddead7SPierre-Louis Bossart dai_runtime_array = devm_kcalloc(cdns->dev, num_dai, 11917dddead7SPierre-Louis Bossart sizeof(struct sdw_cdns_dai_runtime *), 11927dddead7SPierre-Louis Bossart GFP_KERNEL); 11937dddead7SPierre-Louis Bossart if (!dai_runtime_array) 11947dddead7SPierre-Louis Bossart return -ENOMEM; 11957dddead7SPierre-Louis Bossart cdns->dai_runtime_array = dai_runtime_array; 11967dddead7SPierre-Louis Bossart 1197c46302ecSVinod Koul dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); 1198c46302ecSVinod Koul if (!dais) 1199c46302ecSVinod Koul return -ENOMEM; 1200c46302ecSVinod Koul 1201c46302ecSVinod Koul /* Create PCM DAIs */ 1202c46302ecSVinod Koul stream = &cdns->pcm; 1203c46302ecSVinod Koul 1204cf924962SBard Liao ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in, 120563a6aa96SPierre-Louis Bossart off, stream->num_ch_in); 1206c46302ecSVinod Koul if (ret) 1207c46302ecSVinod Koul return ret; 1208c46302ecSVinod Koul 1209c46302ecSVinod Koul off += cdns->pcm.num_in; 12101215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out, 121163a6aa96SPierre-Louis Bossart off, stream->num_ch_out); 1212c46302ecSVinod Koul if (ret) 1213c46302ecSVinod Koul return ret; 1214c46302ecSVinod Koul 1215c46302ecSVinod Koul off += cdns->pcm.num_out; 12161215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd, 121763a6aa96SPierre-Louis Bossart off, stream->num_ch_bd); 1218c46302ecSVinod Koul if (ret) 1219c46302ecSVinod Koul return ret; 1220c46302ecSVinod Koul 122154f391ddSPierre-Louis Bossart return devm_snd_soc_register_component(cdns->dev, &dai_component, 1222c46302ecSVinod Koul dais, num_dai); 1223c46302ecSVinod Koul } 1224c46302ecSVinod Koul 12258d875da7SPierre-Louis Bossart static int intel_start_bus(struct sdw_intel *sdw) 12268d875da7SPierre-Louis Bossart { 12278d875da7SPierre-Louis Bossart struct device *dev = sdw->cdns.dev; 12288d875da7SPierre-Louis Bossart struct sdw_cdns *cdns = &sdw->cdns; 12298d875da7SPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 12308d875da7SPierre-Louis Bossart int ret; 12318d875da7SPierre-Louis Bossart 12328d875da7SPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 12338d875da7SPierre-Louis Bossart if (ret < 0) { 12348d875da7SPierre-Louis Bossart dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret); 12358d875da7SPierre-Louis Bossart return ret; 12368d875da7SPierre-Louis Bossart } 12378d875da7SPierre-Louis Bossart 12388d875da7SPierre-Louis Bossart /* 12398d875da7SPierre-Louis Bossart * follow recommended programming flows to avoid timeouts when 12408d875da7SPierre-Louis Bossart * gsync is enabled 12418d875da7SPierre-Louis Bossart */ 12428d875da7SPierre-Louis Bossart if (bus->multi_link) 12438d875da7SPierre-Louis Bossart intel_shim_sync_arm(sdw); 12448d875da7SPierre-Louis Bossart 12458d875da7SPierre-Louis Bossart ret = sdw_cdns_init(cdns); 12468d875da7SPierre-Louis Bossart if (ret < 0) { 12478d875da7SPierre-Louis Bossart dev_err(dev, "%s: unable to initialize Cadence IP: %d\n", __func__, ret); 12488d875da7SPierre-Louis Bossart goto err_interrupt; 12498d875da7SPierre-Louis Bossart } 12508d875da7SPierre-Louis Bossart 12518d875da7SPierre-Louis Bossart ret = sdw_cdns_exit_reset(cdns); 12528d875da7SPierre-Louis Bossart if (ret < 0) { 12538d875da7SPierre-Louis Bossart dev_err(dev, "%s: unable to exit bus reset sequence: %d\n", __func__, ret); 12548d875da7SPierre-Louis Bossart goto err_interrupt; 12558d875da7SPierre-Louis Bossart } 12568d875da7SPierre-Louis Bossart 12578d875da7SPierre-Louis Bossart if (bus->multi_link) { 12588d875da7SPierre-Louis Bossart ret = intel_shim_sync_go(sdw); 12598d875da7SPierre-Louis Bossart if (ret < 0) { 12608d875da7SPierre-Louis Bossart dev_err(dev, "%s: sync go failed: %d\n", __func__, ret); 12618d875da7SPierre-Louis Bossart goto err_interrupt; 12628d875da7SPierre-Louis Bossart } 12638d875da7SPierre-Louis Bossart } 12648d875da7SPierre-Louis Bossart sdw_cdns_check_self_clearing_bits(cdns, __func__, 12658d875da7SPierre-Louis Bossart true, INTEL_MASTER_RESET_ITERATIONS); 12668d875da7SPierre-Louis Bossart 12678d875da7SPierre-Louis Bossart return 0; 12688d875da7SPierre-Louis Bossart 12698d875da7SPierre-Louis Bossart err_interrupt: 12708d875da7SPierre-Louis Bossart sdw_cdns_enable_interrupt(cdns, false); 12718d875da7SPierre-Louis Bossart return ret; 12728d875da7SPierre-Louis Bossart } 12738d875da7SPierre-Louis Bossart 12748d875da7SPierre-Louis Bossart static int intel_start_bus_after_reset(struct sdw_intel *sdw) 12758d875da7SPierre-Louis Bossart { 12768d875da7SPierre-Louis Bossart struct device *dev = sdw->cdns.dev; 12778d875da7SPierre-Louis Bossart struct sdw_cdns *cdns = &sdw->cdns; 12788d875da7SPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 12798d875da7SPierre-Louis Bossart bool clock_stop0; 12808d875da7SPierre-Louis Bossart int status; 12818d875da7SPierre-Louis Bossart int ret; 12828d875da7SPierre-Louis Bossart 12838d875da7SPierre-Louis Bossart /* 12848d875da7SPierre-Louis Bossart * An exception condition occurs for the CLK_STOP_BUS_RESET 12858d875da7SPierre-Louis Bossart * case if one or more masters remain active. In this condition, 12868d875da7SPierre-Louis Bossart * all the masters are powered on for they are in the same power 12878d875da7SPierre-Louis Bossart * domain. Master can preserve its context for clock stop0, so 12888d875da7SPierre-Louis Bossart * there is no need to clear slave status and reset bus. 12898d875da7SPierre-Louis Bossart */ 12908d875da7SPierre-Louis Bossart clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns); 12918d875da7SPierre-Louis Bossart 12928d875da7SPierre-Louis Bossart if (!clock_stop0) { 12938d875da7SPierre-Louis Bossart 12948d875da7SPierre-Louis Bossart /* 12958d875da7SPierre-Louis Bossart * make sure all Slaves are tagged as UNATTACHED and 12968d875da7SPierre-Louis Bossart * provide reason for reinitialization 12978d875da7SPierre-Louis Bossart */ 12988d875da7SPierre-Louis Bossart 12998d875da7SPierre-Louis Bossart status = SDW_UNATTACH_REQUEST_MASTER_RESET; 13008d875da7SPierre-Louis Bossart sdw_clear_slave_status(bus, status); 13018d875da7SPierre-Louis Bossart 13028d875da7SPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 13038d875da7SPierre-Louis Bossart if (ret < 0) { 13048d875da7SPierre-Louis Bossart dev_err(dev, "cannot enable interrupts during resume\n"); 13058d875da7SPierre-Louis Bossart return ret; 13068d875da7SPierre-Louis Bossart } 13078d875da7SPierre-Louis Bossart 13088d875da7SPierre-Louis Bossart /* 13098d875da7SPierre-Louis Bossart * follow recommended programming flows to avoid 13108d875da7SPierre-Louis Bossart * timeouts when gsync is enabled 13118d875da7SPierre-Louis Bossart */ 13128d875da7SPierre-Louis Bossart if (bus->multi_link) 13138d875da7SPierre-Louis Bossart intel_shim_sync_arm(sdw); 13148d875da7SPierre-Louis Bossart 13158d875da7SPierre-Louis Bossart /* 13168d875da7SPierre-Louis Bossart * Re-initialize the IP since it was powered-off 13178d875da7SPierre-Louis Bossart */ 13188d875da7SPierre-Louis Bossart sdw_cdns_init(&sdw->cdns); 13198d875da7SPierre-Louis Bossart 13208d875da7SPierre-Louis Bossart } else { 13218d875da7SPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 13228d875da7SPierre-Louis Bossart if (ret < 0) { 13238d875da7SPierre-Louis Bossart dev_err(dev, "cannot enable interrupts during resume\n"); 13248d875da7SPierre-Louis Bossart return ret; 13258d875da7SPierre-Louis Bossart } 13268d875da7SPierre-Louis Bossart } 13278d875da7SPierre-Louis Bossart 13288d875da7SPierre-Louis Bossart ret = sdw_cdns_clock_restart(cdns, !clock_stop0); 13298d875da7SPierre-Louis Bossart if (ret < 0) { 13308d875da7SPierre-Louis Bossart dev_err(dev, "unable to restart clock during resume\n"); 13318d875da7SPierre-Louis Bossart goto err_interrupt; 13328d875da7SPierre-Louis Bossart } 13338d875da7SPierre-Louis Bossart 13348d875da7SPierre-Louis Bossart if (!clock_stop0) { 13358d875da7SPierre-Louis Bossart ret = sdw_cdns_exit_reset(cdns); 13368d875da7SPierre-Louis Bossart if (ret < 0) { 13378d875da7SPierre-Louis Bossart dev_err(dev, "unable to exit bus reset sequence during resume\n"); 13388d875da7SPierre-Louis Bossart goto err_interrupt; 13398d875da7SPierre-Louis Bossart } 13408d875da7SPierre-Louis Bossart 13418d875da7SPierre-Louis Bossart if (bus->multi_link) { 13428d875da7SPierre-Louis Bossart ret = intel_shim_sync_go(sdw); 13438d875da7SPierre-Louis Bossart if (ret < 0) { 13448d875da7SPierre-Louis Bossart dev_err(sdw->cdns.dev, "sync go failed during resume\n"); 13458d875da7SPierre-Louis Bossart goto err_interrupt; 13468d875da7SPierre-Louis Bossart } 13478d875da7SPierre-Louis Bossart } 13488d875da7SPierre-Louis Bossart } 13498d875da7SPierre-Louis Bossart sdw_cdns_check_self_clearing_bits(cdns, __func__, true, INTEL_MASTER_RESET_ITERATIONS); 13508d875da7SPierre-Louis Bossart 13518d875da7SPierre-Louis Bossart return 0; 13528d875da7SPierre-Louis Bossart 13538d875da7SPierre-Louis Bossart err_interrupt: 13548d875da7SPierre-Louis Bossart sdw_cdns_enable_interrupt(cdns, false); 13558d875da7SPierre-Louis Bossart return ret; 13568d875da7SPierre-Louis Bossart } 13578d875da7SPierre-Louis Bossart 13588d875da7SPierre-Louis Bossart static void intel_check_clock_stop(struct sdw_intel *sdw) 13598d875da7SPierre-Louis Bossart { 13608d875da7SPierre-Louis Bossart struct device *dev = sdw->cdns.dev; 13618d875da7SPierre-Louis Bossart bool clock_stop0; 13628d875da7SPierre-Louis Bossart 13638d875da7SPierre-Louis Bossart clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns); 13648d875da7SPierre-Louis Bossart if (!clock_stop0) 13658d875da7SPierre-Louis Bossart dev_err(dev, "%s: invalid configuration, clock was not stopped\n", __func__); 13668d875da7SPierre-Louis Bossart } 13678d875da7SPierre-Louis Bossart 13688d875da7SPierre-Louis Bossart static int intel_start_bus_after_clock_stop(struct sdw_intel *sdw) 13698d875da7SPierre-Louis Bossart { 13708d875da7SPierre-Louis Bossart struct device *dev = sdw->cdns.dev; 13718d875da7SPierre-Louis Bossart struct sdw_cdns *cdns = &sdw->cdns; 13728d875da7SPierre-Louis Bossart int ret; 13738d875da7SPierre-Louis Bossart 13748d875da7SPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 13758d875da7SPierre-Louis Bossart if (ret < 0) { 13768d875da7SPierre-Louis Bossart dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret); 13778d875da7SPierre-Louis Bossart return ret; 13788d875da7SPierre-Louis Bossart } 13798d875da7SPierre-Louis Bossart 13808d875da7SPierre-Louis Bossart ret = sdw_cdns_clock_restart(cdns, false); 13818d875da7SPierre-Louis Bossart if (ret < 0) { 13828d875da7SPierre-Louis Bossart dev_err(dev, "%s: unable to restart clock: %d\n", __func__, ret); 13838d875da7SPierre-Louis Bossart sdw_cdns_enable_interrupt(cdns, false); 13848d875da7SPierre-Louis Bossart return ret; 13858d875da7SPierre-Louis Bossart } 13868d875da7SPierre-Louis Bossart 13878d875da7SPierre-Louis Bossart sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime no_quirks", 13888d875da7SPierre-Louis Bossart true, INTEL_MASTER_RESET_ITERATIONS); 13898d875da7SPierre-Louis Bossart 13908d875da7SPierre-Louis Bossart return 0; 13918d875da7SPierre-Louis Bossart } 13928d875da7SPierre-Louis Bossart 1393503ae285SPierre-Louis Bossart static int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop) 1394503ae285SPierre-Louis Bossart { 1395503ae285SPierre-Louis Bossart struct device *dev = sdw->cdns.dev; 1396503ae285SPierre-Louis Bossart struct sdw_cdns *cdns = &sdw->cdns; 1397503ae285SPierre-Louis Bossart bool wake_enable = false; 1398503ae285SPierre-Louis Bossart int ret; 1399503ae285SPierre-Louis Bossart 1400503ae285SPierre-Louis Bossart if (clock_stop) { 1401503ae285SPierre-Louis Bossart ret = sdw_cdns_clock_stop(cdns, true); 1402503ae285SPierre-Louis Bossart if (ret < 0) 1403503ae285SPierre-Louis Bossart dev_err(dev, "%s: cannot stop clock: %d\n", __func__, ret); 1404503ae285SPierre-Louis Bossart else 1405503ae285SPierre-Louis Bossart wake_enable = true; 1406503ae285SPierre-Louis Bossart } 1407503ae285SPierre-Louis Bossart 1408503ae285SPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, false); 1409503ae285SPierre-Louis Bossart if (ret < 0) { 1410503ae285SPierre-Louis Bossart dev_err(dev, "%s: cannot disable interrupts: %d\n", __func__, ret); 1411503ae285SPierre-Louis Bossart return ret; 1412503ae285SPierre-Louis Bossart } 1413503ae285SPierre-Louis Bossart 1414503ae285SPierre-Louis Bossart ret = intel_link_power_down(sdw); 1415503ae285SPierre-Louis Bossart if (ret) { 1416503ae285SPierre-Louis Bossart dev_err(dev, "%s: Link power down failed: %d\n", __func__, ret); 1417503ae285SPierre-Louis Bossart return ret; 1418503ae285SPierre-Louis Bossart } 1419503ae285SPierre-Louis Bossart 1420503ae285SPierre-Louis Bossart intel_shim_wake(sdw, wake_enable); 1421503ae285SPierre-Louis Bossart 1422503ae285SPierre-Louis Bossart return 0; 1423503ae285SPierre-Louis Bossart } 1424503ae285SPierre-Louis Bossart 1425b3ad31f3SPierre-Louis Bossart const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops = { 1426fb2dc6a0SPierre-Louis Bossart .debugfs_init = intel_debugfs_init, 1427fb2dc6a0SPierre-Louis Bossart .debugfs_exit = intel_debugfs_exit, 1428fb2dc6a0SPierre-Louis Bossart 1429b6234bccSPierre-Louis Bossart .register_dai = intel_register_dai, 1430b6234bccSPierre-Louis Bossart 14313db0c5a6SPierre-Louis Bossart .check_clock_stop = intel_check_clock_stop, 14323db0c5a6SPierre-Louis Bossart .start_bus = intel_start_bus, 14333db0c5a6SPierre-Louis Bossart .start_bus_after_reset = intel_start_bus_after_reset, 14343db0c5a6SPierre-Louis Bossart .start_bus_after_clock_stop = intel_start_bus_after_clock_stop, 14353db0c5a6SPierre-Louis Bossart .stop_bus = intel_stop_bus, 14363db0c5a6SPierre-Louis Bossart 1437*49c9ff45SPierre-Louis Bossart .link_power_up = intel_link_power_up, 1438*49c9ff45SPierre-Louis Bossart .link_power_down = intel_link_power_down, 1439*49c9ff45SPierre-Louis Bossart 1440b3ad31f3SPierre-Louis Bossart .pre_bank_switch = intel_pre_bank_switch, 1441b3ad31f3SPierre-Louis Bossart .post_bank_switch = intel_post_bank_switch, 1442b3ad31f3SPierre-Louis Bossart }; 1443b3ad31f3SPierre-Louis Bossart EXPORT_SYMBOL_NS(sdw_intel_cnl_hw_ops, SOUNDWIRE_INTEL); 1444b3ad31f3SPierre-Louis Bossart 1445b3ad31f3SPierre-Louis Bossart static int generic_pre_bank_switch(struct sdw_bus *bus) 1446b3ad31f3SPierre-Louis Bossart { 1447b3ad31f3SPierre-Louis Bossart struct sdw_cdns *cdns = bus_to_cdns(bus); 1448b3ad31f3SPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 1449b3ad31f3SPierre-Louis Bossart 1450b3ad31f3SPierre-Louis Bossart return sdw->link_res->hw_ops->pre_bank_switch(sdw); 1451b3ad31f3SPierre-Louis Bossart } 1452b3ad31f3SPierre-Louis Bossart 1453b3ad31f3SPierre-Louis Bossart static int generic_post_bank_switch(struct sdw_bus *bus) 1454b3ad31f3SPierre-Louis Bossart { 1455b3ad31f3SPierre-Louis Bossart struct sdw_cdns *cdns = bus_to_cdns(bus); 1456b3ad31f3SPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 1457b3ad31f3SPierre-Louis Bossart 1458b3ad31f3SPierre-Louis Bossart return sdw->link_res->hw_ops->post_bank_switch(sdw); 1459b3ad31f3SPierre-Louis Bossart } 1460b3ad31f3SPierre-Louis Bossart 1461085f4aceSPierre-Louis Bossart static int sdw_master_read_intel_prop(struct sdw_bus *bus) 1462085f4aceSPierre-Louis Bossart { 1463085f4aceSPierre-Louis Bossart struct sdw_master_prop *prop = &bus->prop; 1464085f4aceSPierre-Louis Bossart struct fwnode_handle *link; 1465085f4aceSPierre-Louis Bossart char name[32]; 1466395713d8SPierre-Louis Bossart u32 quirk_mask; 1467085f4aceSPierre-Louis Bossart 1468085f4aceSPierre-Louis Bossart /* Find master handle */ 1469085f4aceSPierre-Louis Bossart snprintf(name, sizeof(name), 1470085f4aceSPierre-Louis Bossart "mipi-sdw-link-%d-subproperties", bus->link_id); 1471085f4aceSPierre-Louis Bossart 1472085f4aceSPierre-Louis Bossart link = device_get_named_child_node(bus->dev, name); 1473085f4aceSPierre-Louis Bossart if (!link) { 1474085f4aceSPierre-Louis Bossart dev_err(bus->dev, "Master node %s not found\n", name); 1475085f4aceSPierre-Louis Bossart return -EIO; 1476085f4aceSPierre-Louis Bossart } 1477085f4aceSPierre-Louis Bossart 1478085f4aceSPierre-Louis Bossart fwnode_property_read_u32(link, 1479085f4aceSPierre-Louis Bossart "intel-sdw-ip-clock", 1480085f4aceSPierre-Louis Bossart &prop->mclk_freq); 1481395713d8SPierre-Louis Bossart 1482a19efb52SBard Liao /* the values reported by BIOS are the 2x clock, not the bus clock */ 1483a19efb52SBard Liao prop->mclk_freq /= 2; 1484a19efb52SBard Liao 1485395713d8SPierre-Louis Bossart fwnode_property_read_u32(link, 1486395713d8SPierre-Louis Bossart "intel-quirk-mask", 1487395713d8SPierre-Louis Bossart &quirk_mask); 1488395713d8SPierre-Louis Bossart 1489395713d8SPierre-Louis Bossart if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) 1490395713d8SPierre-Louis Bossart prop->hw_disabled = true; 1491395713d8SPierre-Louis Bossart 1492bb877bebSBard Liao prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH | 1493bb877bebSBard Liao SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY; 1494bb877bebSBard Liao 1495085f4aceSPierre-Louis Bossart return 0; 1496085f4aceSPierre-Louis Bossart } 1497085f4aceSPierre-Louis Bossart 149871bb8a1bSVinod Koul static int intel_prop_read(struct sdw_bus *bus) 149971bb8a1bSVinod Koul { 150071bb8a1bSVinod Koul /* Initialize with default handler to read all DisCo properties */ 150171bb8a1bSVinod Koul sdw_master_read_prop(bus); 150271bb8a1bSVinod Koul 1503085f4aceSPierre-Louis Bossart /* read Intel-specific properties */ 1504085f4aceSPierre-Louis Bossart sdw_master_read_intel_prop(bus); 1505085f4aceSPierre-Louis Bossart 150671bb8a1bSVinod Koul return 0; 150771bb8a1bSVinod Koul } 150871bb8a1bSVinod Koul 1509c91605f4SShreyas NC static struct sdw_master_ops sdw_intel_ops = { 15100b59d4c9SPierre-Louis Bossart .read_prop = intel_prop_read, 1511f6594cdfSPierre-Louis Bossart .override_adr = sdw_dmi_override_adr, 1512c91605f4SShreyas NC .xfer_msg = cdns_xfer_msg, 1513c91605f4SShreyas NC .xfer_msg_defer = cdns_xfer_msg_defer, 1514c91605f4SShreyas NC .reset_page_addr = cdns_reset_page_addr, 151507abeff1SVinod Koul .set_bus_conf = cdns_bus_conf, 1516b3ad31f3SPierre-Louis Bossart .pre_bank_switch = generic_pre_bank_switch, 1517b3ad31f3SPierre-Louis Bossart .post_bank_switch = generic_post_bank_switch, 1518133547a1SPierre-Louis Bossart .read_ping_status = cdns_read_ping_status, 1519c91605f4SShreyas NC }; 1520c91605f4SShreyas NC 152171bb8a1bSVinod Koul /* 152229a269c6SPierre-Louis Bossart * probe and init (aux_dev_id argument is required by function prototype but not used) 152371bb8a1bSVinod Koul */ 152429a269c6SPierre-Louis Bossart static int intel_link_probe(struct auxiliary_device *auxdev, 152529a269c6SPierre-Louis Bossart const struct auxiliary_device_id *aux_dev_id) 152629a269c6SPierre-Louis Bossart 152771bb8a1bSVinod Koul { 152829a269c6SPierre-Louis Bossart struct device *dev = &auxdev->dev; 152929a269c6SPierre-Louis Bossart struct sdw_intel_link_dev *ldev = auxiliary_dev_to_sdw_intel_link_dev(auxdev); 153071bb8a1bSVinod Koul struct sdw_intel *sdw; 153183e129afSPierre-Louis Bossart struct sdw_cdns *cdns; 1532b6109dd6SPierre-Louis Bossart struct sdw_bus *bus; 153371bb8a1bSVinod Koul int ret; 153471bb8a1bSVinod Koul 1535b6109dd6SPierre-Louis Bossart sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL); 153671bb8a1bSVinod Koul if (!sdw) 153771bb8a1bSVinod Koul return -ENOMEM; 153871bb8a1bSVinod Koul 153983e129afSPierre-Louis Bossart cdns = &sdw->cdns; 154083e129afSPierre-Louis Bossart bus = &cdns->bus; 154171bb8a1bSVinod Koul 154229a269c6SPierre-Louis Bossart sdw->instance = auxdev->id; 154329a269c6SPierre-Louis Bossart sdw->link_res = &ldev->link_res; 154483e129afSPierre-Louis Bossart cdns->dev = dev; 154583e129afSPierre-Louis Bossart cdns->registers = sdw->link_res->registers; 154683e129afSPierre-Louis Bossart cdns->instance = sdw->instance; 154783e129afSPierre-Louis Bossart cdns->msg_count = 0; 154883e129afSPierre-Louis Bossart 154929a269c6SPierre-Louis Bossart bus->link_id = auxdev->id; 15501f2dcf3aSPierre-Louis Bossart bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN; 155113c30a75SSjoerd Simons bus->clk_stop_timeout = 1; 155271bb8a1bSVinod Koul 155383e129afSPierre-Louis Bossart sdw_cdns_probe(cdns); 155471bb8a1bSVinod Koul 15550b59d4c9SPierre-Louis Bossart /* Set ops */ 1556b6109dd6SPierre-Louis Bossart bus->ops = &sdw_intel_ops; 155771bb8a1bSVinod Koul 1558b6109dd6SPierre-Louis Bossart /* set driver data, accessed by snd_soc_dai_get_drvdata() */ 15593edac08eSDavid E. Box auxiliary_set_drvdata(auxdev, cdns); 156071bb8a1bSVinod Koul 15619026118fSBard Liao /* use generic bandwidth allocation algorithm */ 15629026118fSBard Liao sdw->cdns.bus.compute_params = sdw_compute_params; 15639026118fSBard Liao 15646d9f2dadSPierre-Louis Bossart /* avoid resuming from pm_runtime suspend if it's not required */ 15656d9f2dadSPierre-Louis Bossart dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND); 15666d9f2dadSPierre-Louis Bossart 1567b6109dd6SPierre-Louis Bossart ret = sdw_bus_master_add(bus, dev, dev->fwnode); 156871bb8a1bSVinod Koul if (ret) { 1569b6109dd6SPierre-Louis Bossart dev_err(dev, "sdw_bus_master_add fail: %d\n", ret); 15709e3d47fbSPierre-Louis Bossart return ret; 157171bb8a1bSVinod Koul } 157271bb8a1bSVinod Koul 15736d2c6669SPierre-Louis Bossart if (bus->prop.hw_disabled) 1574b6109dd6SPierre-Louis Bossart dev_info(dev, 1575b6109dd6SPierre-Louis Bossart "SoundWire master %d is disabled, will be ignored\n", 1576b6109dd6SPierre-Louis Bossart bus->link_id); 15770ef2986eSPierre-Louis Bossart /* 15780ef2986eSPierre-Louis Bossart * Ignore BIOS err_threshold, it's a really bad idea when dealing 15790ef2986eSPierre-Louis Bossart * with multiple hardware synchronized links 15800ef2986eSPierre-Louis Bossart */ 15810ef2986eSPierre-Louis Bossart bus->prop.err_threshold = 0; 15826d2c6669SPierre-Louis Bossart 15836d2c6669SPierre-Louis Bossart return 0; 15846d2c6669SPierre-Louis Bossart } 15856d2c6669SPierre-Louis Bossart 158629a269c6SPierre-Louis Bossart int intel_link_startup(struct auxiliary_device *auxdev) 15876d2c6669SPierre-Louis Bossart { 158829a269c6SPierre-Louis Bossart struct device *dev = &auxdev->dev; 15893edac08eSDavid E. Box struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev); 15906d2c6669SPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 15916d2c6669SPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1592ebf878edSPierre-Louis Bossart int link_flags; 1593857a7c42SPierre-Louis Bossart bool multi_link; 1594caf68819SPierre-Louis Bossart u32 clock_stop_quirks; 15956d2c6669SPierre-Louis Bossart int ret; 15966d2c6669SPierre-Louis Bossart 15976d2c6669SPierre-Louis Bossart if (bus->prop.hw_disabled) { 15986d2c6669SPierre-Louis Bossart dev_info(dev, 15996d2c6669SPierre-Louis Bossart "SoundWire master %d is disabled, ignoring\n", 16006d2c6669SPierre-Louis Bossart sdw->instance); 1601395713d8SPierre-Louis Bossart return 0; 1602395713d8SPierre-Louis Bossart } 1603395713d8SPierre-Louis Bossart 1604857a7c42SPierre-Louis Bossart link_flags = md_flags >> (bus->link_id * 8); 1605857a7c42SPierre-Louis Bossart multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); 1606857a7c42SPierre-Louis Bossart if (!multi_link) { 1607857a7c42SPierre-Louis Bossart dev_dbg(dev, "Multi-link is disabled\n"); 1608857a7c42SPierre-Louis Bossart } else { 160994eed661SPierre-Louis Bossart /* 161094eed661SPierre-Louis Bossart * hardware-based synchronization is required regardless 161194eed661SPierre-Louis Bossart * of the number of segments used by a stream: SSP-based 161294eed661SPierre-Louis Bossart * synchronization is gated by gsync when the multi-master 161394eed661SPierre-Louis Bossart * mode is set. 161494eed661SPierre-Louis Bossart */ 161594eed661SPierre-Louis Bossart bus->hw_sync_min_links = 1; 1616857a7c42SPierre-Louis Bossart } 16178d875da7SPierre-Louis Bossart bus->multi_link = multi_link; 1618857a7c42SPierre-Louis Bossart 1619857a7c42SPierre-Louis Bossart /* Initialize shim, controller */ 1620*49c9ff45SPierre-Louis Bossart ret = sdw_intel_link_power_up(sdw); 162171bb8a1bSVinod Koul if (ret) 162271bb8a1bSVinod Koul goto err_init; 162371bb8a1bSVinod Koul 1624c46302ecSVinod Koul /* Register DAIs */ 1625b6234bccSPierre-Louis Bossart ret = sdw_intel_register_dai(sdw); 1626c46302ecSVinod Koul if (ret) { 1627b6109dd6SPierre-Louis Bossart dev_err(dev, "DAI registration failed: %d\n", ret); 16288d875da7SPierre-Louis Bossart goto err_power_up; 1629c46302ecSVinod Koul } 1630c46302ecSVinod Koul 1631fb2dc6a0SPierre-Louis Bossart sdw_intel_debugfs_init(sdw); 163279ee6631SPierre-Louis Bossart 16338d875da7SPierre-Louis Bossart /* start bus */ 16343db0c5a6SPierre-Louis Bossart ret = sdw_intel_start_bus(sdw); 16358d875da7SPierre-Louis Bossart if (ret) { 16368d875da7SPierre-Louis Bossart dev_err(dev, "bus start failed: %d\n", ret); 16378d875da7SPierre-Louis Bossart goto err_power_up; 163871bb8a1bSVinod Koul } 163971bb8a1bSVinod Koul 1640ebf878edSPierre-Louis Bossart /* Enable runtime PM */ 1641ebf878edSPierre-Louis Bossart if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) { 1642ebf878edSPierre-Louis Bossart pm_runtime_set_autosuspend_delay(dev, 1643ebf878edSPierre-Louis Bossart INTEL_MASTER_SUSPEND_DELAY_MS); 1644ebf878edSPierre-Louis Bossart pm_runtime_use_autosuspend(dev); 1645ebf878edSPierre-Louis Bossart pm_runtime_mark_last_busy(dev); 1646ebf878edSPierre-Louis Bossart 1647ebf878edSPierre-Louis Bossart pm_runtime_set_active(dev); 1648ebf878edSPierre-Louis Bossart pm_runtime_enable(dev); 1649ebf878edSPierre-Louis Bossart } 1650ebf878edSPierre-Louis Bossart 1651caf68819SPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1652caf68819SPierre-Louis Bossart if (clock_stop_quirks & SDW_INTEL_CLK_STOP_NOT_ALLOWED) { 1653caf68819SPierre-Louis Bossart /* 1654caf68819SPierre-Louis Bossart * To keep the clock running we need to prevent 1655caf68819SPierre-Louis Bossart * pm_runtime suspend from happening by increasing the 1656caf68819SPierre-Louis Bossart * reference count. 1657caf68819SPierre-Louis Bossart * This quirk is specified by the parent PCI device in 1658caf68819SPierre-Louis Bossart * case of specific latency requirements. It will have 1659caf68819SPierre-Louis Bossart * no effect if pm_runtime is disabled by the user via 1660caf68819SPierre-Louis Bossart * a module parameter for testing purposes. 1661caf68819SPierre-Louis Bossart */ 1662caf68819SPierre-Louis Bossart pm_runtime_get_noresume(dev); 1663caf68819SPierre-Louis Bossart } 1664caf68819SPierre-Louis Bossart 1665a2d9c161SPierre-Louis Bossart /* 1666a2d9c161SPierre-Louis Bossart * The runtime PM status of Slave devices is "Unsupported" 1667a2d9c161SPierre-Louis Bossart * until they report as ATTACHED. If they don't, e.g. because 1668a2d9c161SPierre-Louis Bossart * there are no Slave devices populated or if the power-on is 1669a2d9c161SPierre-Louis Bossart * delayed or dependent on a power switch, the Master will 1670a2d9c161SPierre-Louis Bossart * remain active and prevent its parent from suspending. 1671a2d9c161SPierre-Louis Bossart * 1672a2d9c161SPierre-Louis Bossart * Conditionally force the pm_runtime core to re-evaluate the 1673a2d9c161SPierre-Louis Bossart * Master status in the absence of any Slave activity. A quirk 1674a2d9c161SPierre-Louis Bossart * is provided to e.g. deal with Slaves that may be powered on 1675a2d9c161SPierre-Louis Bossart * with a delay. A more complete solution would require the 1676a2d9c161SPierre-Louis Bossart * definition of Master properties. 1677a2d9c161SPierre-Louis Bossart */ 1678a2d9c161SPierre-Louis Bossart if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) 1679a2d9c161SPierre-Louis Bossart pm_runtime_idle(dev); 1680a2d9c161SPierre-Louis Bossart 1681e4401abbSPierre-Louis Bossart sdw->startup_done = true; 168271bb8a1bSVinod Koul return 0; 168371bb8a1bSVinod Koul 16848d875da7SPierre-Louis Bossart err_power_up: 1685*49c9ff45SPierre-Louis Bossart sdw_intel_link_power_down(sdw); 168671bb8a1bSVinod Koul err_init: 168771bb8a1bSVinod Koul return ret; 168871bb8a1bSVinod Koul } 168971bb8a1bSVinod Koul 169029a269c6SPierre-Louis Bossart static void intel_link_remove(struct auxiliary_device *auxdev) 169171bb8a1bSVinod Koul { 16923edac08eSDavid E. Box struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev); 169383e129afSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 169483e129afSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1695b6109dd6SPierre-Louis Bossart 1696caf68819SPierre-Louis Bossart /* 1697caf68819SPierre-Louis Bossart * Since pm_runtime is already disabled, we don't decrease 1698caf68819SPierre-Louis Bossart * the refcount when the clock_stop_quirk is 1699caf68819SPierre-Louis Bossart * SDW_INTEL_CLK_STOP_NOT_ALLOWED 1700caf68819SPierre-Louis Bossart */ 1701b6109dd6SPierre-Louis Bossart if (!bus->prop.hw_disabled) { 1702fb2dc6a0SPierre-Louis Bossart sdw_intel_debugfs_exit(sdw); 170383e129afSPierre-Louis Bossart sdw_cdns_enable_interrupt(cdns, false); 1704395713d8SPierre-Louis Bossart } 1705b6109dd6SPierre-Louis Bossart sdw_bus_master_delete(bus); 170671bb8a1bSVinod Koul } 170771bb8a1bSVinod Koul 170829a269c6SPierre-Louis Bossart int intel_link_process_wakeen_event(struct auxiliary_device *auxdev) 1709ab2c9132SRander Wang { 171029a269c6SPierre-Louis Bossart struct device *dev = &auxdev->dev; 171171bb8a1bSVinod Koul struct sdw_intel *sdw; 1712ab2c9132SRander Wang struct sdw_bus *bus; 171371bb8a1bSVinod Koul 17143edac08eSDavid E. Box sdw = auxiliary_get_drvdata(auxdev); 1715ab2c9132SRander Wang bus = &sdw->cdns.bus; 171671bb8a1bSVinod Koul 1717e4401abbSPierre-Louis Bossart if (bus->prop.hw_disabled || !sdw->startup_done) { 1718e4401abbSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", 1719e4401abbSPierre-Louis Bossart bus->link_id); 1720ab2c9132SRander Wang return 0; 172171bb8a1bSVinod Koul } 1722ab2c9132SRander Wang 17230f3c54c2SPierre-Louis Bossart if (!intel_shim_check_wake(sdw)) 1724ab2c9132SRander Wang return 0; 1725ab2c9132SRander Wang 1726ab2c9132SRander Wang /* disable WAKEEN interrupt ASAP to prevent interrupt flood */ 1727ab2c9132SRander Wang intel_shim_wake(sdw, false); 1728ab2c9132SRander Wang 1729ab2c9132SRander Wang /* 1730ab2c9132SRander Wang * resume the Master, which will generate a bus reset and result in 1731ab2c9132SRander Wang * Slaves re-attaching and be re-enumerated. The SoundWire physical 1732ab2c9132SRander Wang * device which generated the wake will trigger an interrupt, which 1733ab2c9132SRander Wang * will in turn cause the corresponding Linux Slave device to be 1734ab2c9132SRander Wang * resumed and the Slave codec driver to check the status. 1735ab2c9132SRander Wang */ 1736ab2c9132SRander Wang pm_request_resume(dev); 173771bb8a1bSVinod Koul 173871bb8a1bSVinod Koul return 0; 173971bb8a1bSVinod Koul } 174071bb8a1bSVinod Koul 17419b3b4b3fSPierre-Louis Bossart /* 17429b3b4b3fSPierre-Louis Bossart * PM calls 17439b3b4b3fSPierre-Louis Bossart */ 17449b3b4b3fSPierre-Louis Bossart 1745029bfd1cSPierre-Louis Bossart static int intel_resume_child_device(struct device *dev, void *data) 1746029bfd1cSPierre-Louis Bossart { 1747029bfd1cSPierre-Louis Bossart int ret; 1748029bfd1cSPierre-Louis Bossart struct sdw_slave *slave = dev_to_sdw_dev(dev); 1749029bfd1cSPierre-Louis Bossart 1750029bfd1cSPierre-Louis Bossart if (!slave->probed) { 175163198aaaSPierre-Louis Bossart dev_dbg(dev, "skipping device, no probed driver\n"); 1752029bfd1cSPierre-Louis Bossart return 0; 1753029bfd1cSPierre-Louis Bossart } 1754029bfd1cSPierre-Louis Bossart if (!slave->dev_num_sticky) { 175563198aaaSPierre-Louis Bossart dev_dbg(dev, "skipping device, never detected on bus\n"); 1756029bfd1cSPierre-Louis Bossart return 0; 1757029bfd1cSPierre-Louis Bossart } 1758029bfd1cSPierre-Louis Bossart 1759029bfd1cSPierre-Louis Bossart ret = pm_request_resume(dev); 1760029bfd1cSPierre-Louis Bossart if (ret < 0) 1761029bfd1cSPierre-Louis Bossart dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret); 1762029bfd1cSPierre-Louis Bossart 1763029bfd1cSPierre-Louis Bossart return ret; 1764029bfd1cSPierre-Louis Bossart } 1765029bfd1cSPierre-Louis Bossart 1766029bfd1cSPierre-Louis Bossart static int __maybe_unused intel_pm_prepare(struct device *dev) 1767029bfd1cSPierre-Louis Bossart { 1768029bfd1cSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 1769029bfd1cSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 1770029bfd1cSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1771029bfd1cSPierre-Louis Bossart u32 clock_stop_quirks; 17729283b6f9SPierre-Louis Bossart int ret; 1773029bfd1cSPierre-Louis Bossart 1774029bfd1cSPierre-Louis Bossart if (bus->prop.hw_disabled || !sdw->startup_done) { 1775029bfd1cSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", 1776029bfd1cSPierre-Louis Bossart bus->link_id); 1777029bfd1cSPierre-Louis Bossart return 0; 1778029bfd1cSPierre-Louis Bossart } 1779029bfd1cSPierre-Louis Bossart 1780029bfd1cSPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1781029bfd1cSPierre-Louis Bossart 1782029bfd1cSPierre-Louis Bossart if (pm_runtime_suspended(dev) && 1783029bfd1cSPierre-Louis Bossart pm_runtime_suspended(dev->parent) && 1784029bfd1cSPierre-Louis Bossart ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) || 1785029bfd1cSPierre-Louis Bossart !clock_stop_quirks)) { 1786029bfd1cSPierre-Louis Bossart /* 1787029bfd1cSPierre-Louis Bossart * if we've enabled clock stop, and the parent is suspended, the SHIM registers 1788029bfd1cSPierre-Louis Bossart * are not accessible and the shim wake cannot be disabled. 1789029bfd1cSPierre-Louis Bossart * The only solution is to resume the entire bus to full power 1790029bfd1cSPierre-Louis Bossart */ 1791029bfd1cSPierre-Louis Bossart 1792029bfd1cSPierre-Louis Bossart /* 1793029bfd1cSPierre-Louis Bossart * If any operation in this block fails, we keep going since we don't want 1794029bfd1cSPierre-Louis Bossart * to prevent system suspend from happening and errors should be recoverable 1795029bfd1cSPierre-Louis Bossart * on resume. 1796029bfd1cSPierre-Louis Bossart */ 1797029bfd1cSPierre-Louis Bossart 1798029bfd1cSPierre-Louis Bossart /* 1799029bfd1cSPierre-Louis Bossart * first resume the device for this link. This will also by construction 1800029bfd1cSPierre-Louis Bossart * resume the PCI parent device. 1801029bfd1cSPierre-Louis Bossart */ 1802029bfd1cSPierre-Louis Bossart ret = pm_request_resume(dev); 1803029bfd1cSPierre-Louis Bossart if (ret < 0) { 1804029bfd1cSPierre-Louis Bossart dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret); 1805029bfd1cSPierre-Louis Bossart return 0; 1806029bfd1cSPierre-Louis Bossart } 1807029bfd1cSPierre-Louis Bossart 1808029bfd1cSPierre-Louis Bossart /* 1809029bfd1cSPierre-Louis Bossart * Continue resuming the entire bus (parent + child devices) to exit 1810029bfd1cSPierre-Louis Bossart * the clock stop mode. If there are no devices connected on this link 1811029bfd1cSPierre-Louis Bossart * this is a no-op. 1812029bfd1cSPierre-Louis Bossart * The resume to full power could have been implemented with a .prepare 1813029bfd1cSPierre-Louis Bossart * step in SoundWire codec drivers. This would however require a lot 1814029bfd1cSPierre-Louis Bossart * of code to handle an Intel-specific corner case. It is simpler in 1815029bfd1cSPierre-Louis Bossart * practice to add a loop at the link level. 1816029bfd1cSPierre-Louis Bossart */ 1817029bfd1cSPierre-Louis Bossart ret = device_for_each_child(bus->dev, NULL, intel_resume_child_device); 1818029bfd1cSPierre-Louis Bossart 1819029bfd1cSPierre-Louis Bossart if (ret < 0) 1820029bfd1cSPierre-Louis Bossart dev_err(dev, "%s: intel_resume_child_device failed: %d\n", __func__, ret); 1821029bfd1cSPierre-Louis Bossart } 1822029bfd1cSPierre-Louis Bossart 1823029bfd1cSPierre-Louis Bossart return 0; 1824029bfd1cSPierre-Louis Bossart } 1825029bfd1cSPierre-Louis Bossart 1826f046b233SBard Liao static int __maybe_unused intel_suspend(struct device *dev) 18279b3b4b3fSPierre-Louis Bossart { 18289b3b4b3fSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 18299b3b4b3fSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 18309b3b4b3fSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1831e4be9facSPierre-Louis Bossart u32 clock_stop_quirks; 18329b3b4b3fSPierre-Louis Bossart int ret; 18339b3b4b3fSPierre-Louis Bossart 1834e4401abbSPierre-Louis Bossart if (bus->prop.hw_disabled || !sdw->startup_done) { 1835e4401abbSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", 18369b3b4b3fSPierre-Louis Bossart bus->link_id); 18379b3b4b3fSPierre-Louis Bossart return 0; 18389b3b4b3fSPierre-Louis Bossart } 18399b3b4b3fSPierre-Louis Bossart 1840b61b8b37SPierre-Louis Bossart if (pm_runtime_suspended(dev)) { 184163198aaaSPierre-Louis Bossart dev_dbg(dev, "pm_runtime status: suspended\n"); 1842b61b8b37SPierre-Louis Bossart 1843e4be9facSPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1844e4be9facSPierre-Louis Bossart 1845029bfd1cSPierre-Louis Bossart if ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) || 1846029bfd1cSPierre-Louis Bossart !clock_stop_quirks) { 1847e4be9facSPierre-Louis Bossart 1848029bfd1cSPierre-Louis Bossart if (pm_runtime_suspended(dev->parent)) { 1849e4be9facSPierre-Louis Bossart /* 1850029bfd1cSPierre-Louis Bossart * paranoia check: this should not happen with the .prepare 1851029bfd1cSPierre-Louis Bossart * resume to full power 1852e4be9facSPierre-Louis Bossart */ 1853029bfd1cSPierre-Louis Bossart dev_err(dev, "%s: invalid config: parent is suspended\n", __func__); 1854029bfd1cSPierre-Louis Bossart } else { 1855e4be9facSPierre-Louis Bossart intel_shim_wake(sdw, false); 1856e4be9facSPierre-Louis Bossart } 1857029bfd1cSPierre-Louis Bossart } 1858e4be9facSPierre-Louis Bossart 1859b61b8b37SPierre-Louis Bossart return 0; 1860b61b8b37SPierre-Louis Bossart } 1861b61b8b37SPierre-Louis Bossart 18623db0c5a6SPierre-Louis Bossart ret = sdw_intel_stop_bus(sdw, false); 18639b3b4b3fSPierre-Louis Bossart if (ret < 0) { 1864503ae285SPierre-Louis Bossart dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret); 18659b3b4b3fSPierre-Louis Bossart return ret; 18669b3b4b3fSPierre-Louis Bossart } 18679b3b4b3fSPierre-Louis Bossart 18689b3b4b3fSPierre-Louis Bossart return 0; 18699b3b4b3fSPierre-Louis Bossart } 18709b3b4b3fSPierre-Louis Bossart 187117e0da0bSArnd Bergmann static int __maybe_unused intel_suspend_runtime(struct device *dev) 1872ebf878edSPierre-Louis Bossart { 1873ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 1874ebf878edSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 1875ebf878edSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1876a320f41eSPierre-Louis Bossart u32 clock_stop_quirks; 1877ebf878edSPierre-Louis Bossart int ret; 1878ebf878edSPierre-Louis Bossart 1879e4401abbSPierre-Louis Bossart if (bus->prop.hw_disabled || !sdw->startup_done) { 1880e4401abbSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", 1881ebf878edSPierre-Louis Bossart bus->link_id); 1882ebf878edSPierre-Louis Bossart return 0; 1883ebf878edSPierre-Louis Bossart } 1884ebf878edSPierre-Louis Bossart 1885a320f41eSPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1886a320f41eSPierre-Louis Bossart 1887a320f41eSPierre-Louis Bossart if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { 18883db0c5a6SPierre-Louis Bossart ret = sdw_intel_stop_bus(sdw, false); 1889ebf878edSPierre-Louis Bossart if (ret < 0) { 1890503ae285SPierre-Louis Bossart dev_err(dev, "%s: cannot stop bus during teardown: %d\n", 1891503ae285SPierre-Louis Bossart __func__, ret); 1892ebf878edSPierre-Louis Bossart return ret; 1893ebf878edSPierre-Louis Bossart } 1894503ae285SPierre-Louis Bossart } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || !clock_stop_quirks) { 18953db0c5a6SPierre-Louis Bossart ret = sdw_intel_stop_bus(sdw, true); 18966626a616SRander Wang if (ret < 0) { 1897503ae285SPierre-Louis Bossart dev_err(dev, "%s: cannot stop bus during clock_stop: %d\n", 1898503ae285SPierre-Louis Bossart __func__, ret); 18996626a616SRander Wang return ret; 19006626a616SRander Wang } 1901a320f41eSPierre-Louis Bossart } else { 1902a320f41eSPierre-Louis Bossart dev_err(dev, "%s clock_stop_quirks %x unsupported\n", 1903a320f41eSPierre-Louis Bossart __func__, clock_stop_quirks); 1904a320f41eSPierre-Louis Bossart ret = -EINVAL; 1905a320f41eSPierre-Louis Bossart } 1906a320f41eSPierre-Louis Bossart 1907a320f41eSPierre-Louis Bossart return ret; 1908ebf878edSPierre-Louis Bossart } 1909ebf878edSPierre-Louis Bossart 1910f046b233SBard Liao static int __maybe_unused intel_resume(struct device *dev) 19119b3b4b3fSPierre-Louis Bossart { 19129b3b4b3fSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 19139b3b4b3fSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 19149b3b4b3fSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1915a2d9c161SPierre-Louis Bossart int link_flags; 19169b3b4b3fSPierre-Louis Bossart int ret; 19179b3b4b3fSPierre-Louis Bossart 1918e4401abbSPierre-Louis Bossart if (bus->prop.hw_disabled || !sdw->startup_done) { 1919e4401abbSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", 19209b3b4b3fSPierre-Louis Bossart bus->link_id); 19219b3b4b3fSPierre-Louis Bossart return 0; 19229b3b4b3fSPierre-Louis Bossart } 19239b3b4b3fSPierre-Louis Bossart 1924857a7c42SPierre-Louis Bossart link_flags = md_flags >> (bus->link_id * 8); 1925857a7c42SPierre-Louis Bossart 1926b61b8b37SPierre-Louis Bossart if (pm_runtime_suspended(dev)) { 192763198aaaSPierre-Louis Bossart dev_dbg(dev, "pm_runtime status was suspended, forcing active\n"); 1928b61b8b37SPierre-Louis Bossart 1929b61b8b37SPierre-Louis Bossart /* follow required sequence from runtime_pm.rst */ 1930b61b8b37SPierre-Louis Bossart pm_runtime_disable(dev); 1931b61b8b37SPierre-Louis Bossart pm_runtime_set_active(dev); 1932b61b8b37SPierre-Louis Bossart pm_runtime_mark_last_busy(dev); 1933b61b8b37SPierre-Louis Bossart pm_runtime_enable(dev); 1934a2d9c161SPierre-Louis Bossart 1935a2d9c161SPierre-Louis Bossart link_flags = md_flags >> (bus->link_id * 8); 1936857a7c42SPierre-Louis Bossart 1937a2d9c161SPierre-Louis Bossart if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) 1938a2d9c161SPierre-Louis Bossart pm_runtime_idle(dev); 1939b61b8b37SPierre-Louis Bossart } 1940b61b8b37SPierre-Louis Bossart 1941*49c9ff45SPierre-Louis Bossart ret = sdw_intel_link_power_up(sdw); 19429b3b4b3fSPierre-Louis Bossart if (ret) { 19434e3ea93eSPierre-Louis Bossart dev_err(dev, "%s failed: %d\n", __func__, ret); 19449b3b4b3fSPierre-Louis Bossart return ret; 19459b3b4b3fSPierre-Louis Bossart } 19469b3b4b3fSPierre-Louis Bossart 194799b6a30fSPierre-Louis Bossart /* 194899b6a30fSPierre-Louis Bossart * make sure all Slaves are tagged as UNATTACHED and provide 194999b6a30fSPierre-Louis Bossart * reason for reinitialization 195099b6a30fSPierre-Louis Bossart */ 195199b6a30fSPierre-Louis Bossart sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); 195299b6a30fSPierre-Louis Bossart 19533db0c5a6SPierre-Louis Bossart ret = sdw_intel_start_bus(sdw); 19549b3b4b3fSPierre-Louis Bossart if (ret < 0) { 19558d875da7SPierre-Louis Bossart dev_err(dev, "cannot start bus during resume\n"); 1956*49c9ff45SPierre-Louis Bossart sdw_intel_link_power_down(sdw); 19579b3b4b3fSPierre-Louis Bossart return ret; 19589b3b4b3fSPierre-Louis Bossart } 19599b3b4b3fSPierre-Louis Bossart 1960857a7c42SPierre-Louis Bossart /* 1961cb1e6d59SPierre-Louis Bossart * after system resume, the pm_runtime suspend() may kick in 1962cb1e6d59SPierre-Louis Bossart * during the enumeration, before any children device force the 1963cb1e6d59SPierre-Louis Bossart * master device to remain active. Using pm_runtime_get() 1964cb1e6d59SPierre-Louis Bossart * routines is not really possible, since it'd prevent the 1965cb1e6d59SPierre-Louis Bossart * master from suspending. 1966cb1e6d59SPierre-Louis Bossart * A reasonable compromise is to update the pm_runtime 1967cb1e6d59SPierre-Louis Bossart * counters and delay the pm_runtime suspend by several 1968cb1e6d59SPierre-Louis Bossart * seconds, by when all enumeration should be complete. 1969cb1e6d59SPierre-Louis Bossart */ 1970cb1e6d59SPierre-Louis Bossart pm_runtime_mark_last_busy(dev); 1971cb1e6d59SPierre-Louis Bossart 19728d875da7SPierre-Louis Bossart return 0; 19739b3b4b3fSPierre-Louis Bossart } 19749b3b4b3fSPierre-Louis Bossart 197517e0da0bSArnd Bergmann static int __maybe_unused intel_resume_runtime(struct device *dev) 1976ebf878edSPierre-Louis Bossart { 1977ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 1978ebf878edSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 1979ebf878edSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1980a320f41eSPierre-Louis Bossart u32 clock_stop_quirks; 1981ebf878edSPierre-Louis Bossart int ret; 1982ebf878edSPierre-Louis Bossart 1983e4401abbSPierre-Louis Bossart if (bus->prop.hw_disabled || !sdw->startup_done) { 1984e4401abbSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", 1985ebf878edSPierre-Louis Bossart bus->link_id); 1986ebf878edSPierre-Louis Bossart return 0; 1987ebf878edSPierre-Louis Bossart } 1988ebf878edSPierre-Louis Bossart 1989e286472cSPierre-Louis Bossart /* unconditionally disable WAKEEN interrupt */ 1990e286472cSPierre-Louis Bossart intel_shim_wake(sdw, false); 1991e286472cSPierre-Louis Bossart 1992a320f41eSPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1993a320f41eSPierre-Louis Bossart 1994a320f41eSPierre-Louis Bossart if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { 1995*49c9ff45SPierre-Louis Bossart ret = sdw_intel_link_power_up(sdw); 1996ebf878edSPierre-Louis Bossart if (ret) { 19978d875da7SPierre-Louis Bossart dev_err(dev, "%s: power_up failed after teardown: %d\n", __func__, ret); 1998ebf878edSPierre-Louis Bossart return ret; 1999ebf878edSPierre-Louis Bossart } 2000ebf878edSPierre-Louis Bossart 200199b6a30fSPierre-Louis Bossart /* 200299b6a30fSPierre-Louis Bossart * make sure all Slaves are tagged as UNATTACHED and provide 200399b6a30fSPierre-Louis Bossart * reason for reinitialization 200499b6a30fSPierre-Louis Bossart */ 200599b6a30fSPierre-Louis Bossart sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); 200699b6a30fSPierre-Louis Bossart 20073db0c5a6SPierre-Louis Bossart ret = sdw_intel_start_bus(sdw); 2008ebf878edSPierre-Louis Bossart if (ret < 0) { 20098d875da7SPierre-Louis Bossart dev_err(dev, "%s: cannot start bus after teardown: %d\n", __func__, ret); 2010*49c9ff45SPierre-Louis Bossart sdw_intel_link_power_down(sdw); 2011ebf878edSPierre-Louis Bossart return ret; 2012ebf878edSPierre-Louis Bossart } 2013ebf878edSPierre-Louis Bossart 2014ff560946SPierre-Louis Bossart 20156626a616SRander Wang } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) { 2016*49c9ff45SPierre-Louis Bossart ret = sdw_intel_link_power_up(sdw); 20176626a616SRander Wang if (ret) { 20188d875da7SPierre-Louis Bossart dev_err(dev, "%s: power_up failed after bus reset: %d\n", __func__, ret); 20196626a616SRander Wang return ret; 20206626a616SRander Wang } 20216626a616SRander Wang 20223db0c5a6SPierre-Louis Bossart ret = sdw_intel_start_bus_after_reset(sdw); 20236626a616SRander Wang if (ret < 0) { 20248d875da7SPierre-Louis Bossart dev_err(dev, "%s: cannot start bus after reset: %d\n", __func__, ret); 2025*49c9ff45SPierre-Louis Bossart sdw_intel_link_power_down(sdw); 20266626a616SRander Wang return ret; 20276626a616SRander Wang } 202861fb830bSPierre-Louis Bossart } else if (!clock_stop_quirks) { 2029f748f34eSPierre-Louis Bossart 20303db0c5a6SPierre-Louis Bossart sdw_intel_check_clock_stop(sdw); 2031f748f34eSPierre-Louis Bossart 2032*49c9ff45SPierre-Louis Bossart ret = sdw_intel_link_power_up(sdw); 203361fb830bSPierre-Louis Bossart if (ret) { 20348d875da7SPierre-Louis Bossart dev_err(dev, "%s: power_up failed: %d\n", __func__, ret); 203561fb830bSPierre-Louis Bossart return ret; 203661fb830bSPierre-Louis Bossart } 203761fb830bSPierre-Louis Bossart 20383db0c5a6SPierre-Louis Bossart ret = sdw_intel_start_bus_after_clock_stop(sdw); 203961fb830bSPierre-Louis Bossart if (ret < 0) { 20408d875da7SPierre-Louis Bossart dev_err(dev, "%s: cannot start bus after clock stop: %d\n", __func__, ret); 2041*49c9ff45SPierre-Louis Bossart sdw_intel_link_power_down(sdw); 204261fb830bSPierre-Louis Bossart return ret; 204361fb830bSPierre-Louis Bossart } 2044a320f41eSPierre-Louis Bossart } else { 20458d875da7SPierre-Louis Bossart dev_err(dev, "%s: clock_stop_quirks %x unsupported\n", 2046a320f41eSPierre-Louis Bossart __func__, clock_stop_quirks); 2047a320f41eSPierre-Louis Bossart ret = -EINVAL; 2048a320f41eSPierre-Louis Bossart } 2049ebf878edSPierre-Louis Bossart 2050ebf878edSPierre-Louis Bossart return ret; 2051ebf878edSPierre-Louis Bossart } 2052ebf878edSPierre-Louis Bossart 20539b3b4b3fSPierre-Louis Bossart static const struct dev_pm_ops intel_pm = { 2054029bfd1cSPierre-Louis Bossart .prepare = intel_pm_prepare, 20559b3b4b3fSPierre-Louis Bossart SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) 2056ebf878edSPierre-Louis Bossart SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL) 20579b3b4b3fSPierre-Louis Bossart }; 20589b3b4b3fSPierre-Louis Bossart 205929a269c6SPierre-Louis Bossart static const struct auxiliary_device_id intel_link_id_table[] = { 206029a269c6SPierre-Louis Bossart { .name = "soundwire_intel.link" }, 206129a269c6SPierre-Louis Bossart {}, 206271bb8a1bSVinod Koul }; 206329a269c6SPierre-Louis Bossart MODULE_DEVICE_TABLE(auxiliary, intel_link_id_table); 206471bb8a1bSVinod Koul 206529a269c6SPierre-Louis Bossart static struct auxiliary_driver sdw_intel_drv = { 206629a269c6SPierre-Louis Bossart .probe = intel_link_probe, 206729a269c6SPierre-Louis Bossart .remove = intel_link_remove, 206829a269c6SPierre-Louis Bossart .driver = { 206929a269c6SPierre-Louis Bossart /* auxiliary_driver_register() sets .name to be the modname */ 207029a269c6SPierre-Louis Bossart .pm = &intel_pm, 207129a269c6SPierre-Louis Bossart }, 207229a269c6SPierre-Louis Bossart .id_table = intel_link_id_table 207329a269c6SPierre-Louis Bossart }; 207429a269c6SPierre-Louis Bossart module_auxiliary_driver(sdw_intel_drv); 207571bb8a1bSVinod Koul 207671bb8a1bSVinod Koul MODULE_LICENSE("Dual BSD/GPL"); 207729a269c6SPierre-Louis Bossart MODULE_DESCRIPTION("Intel Soundwire Link Driver"); 2078