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> 1471bb8a1bSVinod Koul #include <linux/platform_device.h> 1537a2d22bSVinod Koul #include <sound/pcm_params.h> 16ab2c9132SRander Wang #include <linux/pm_runtime.h> 1737a2d22bSVinod Koul #include <sound/soc.h> 1871bb8a1bSVinod Koul #include <linux/soundwire/sdw_registers.h> 1971bb8a1bSVinod Koul #include <linux/soundwire/sdw.h> 2071bb8a1bSVinod Koul #include <linux/soundwire/sdw_intel.h> 2171bb8a1bSVinod Koul #include "cadence_master.h" 2279ee6631SPierre-Louis Bossart #include "bus.h" 2371bb8a1bSVinod Koul #include "intel.h" 2471bb8a1bSVinod Koul 25ebf878edSPierre-Louis Bossart #define INTEL_MASTER_SUSPEND_DELAY_MS 3000 26ebf878edSPierre-Louis Bossart 27ebf878edSPierre-Louis Bossart /* 28ebf878edSPierre-Louis Bossart * debug/config flags for the Intel SoundWire Master. 29ebf878edSPierre-Louis Bossart * 30ebf878edSPierre-Louis Bossart * Since we may have multiple masters active, we can have up to 8 31ebf878edSPierre-Louis Bossart * flags reused in each byte, with master0 using the ls-byte, etc. 32ebf878edSPierre-Louis Bossart */ 33ebf878edSPierre-Louis Bossart 34ebf878edSPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME BIT(0) 35ebf878edSPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP BIT(1) 36a2d9c161SPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE BIT(2) 37857a7c42SPierre-Louis Bossart #define SDW_INTEL_MASTER_DISABLE_MULTI_LINK BIT(3) 38ebf878edSPierre-Louis Bossart 39ebf878edSPierre-Louis Bossart static int md_flags; 40ebf878edSPierre-Louis Bossart module_param_named(sdw_md_flags, md_flags, int, 0444); 41ebf878edSPierre-Louis Bossart MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)"); 42ebf878edSPierre-Louis Bossart 4371bb8a1bSVinod Koul /* Intel SHIM Registers Definition */ 4471bb8a1bSVinod Koul #define SDW_SHIM_LCAP 0x0 4571bb8a1bSVinod Koul #define SDW_SHIM_LCTL 0x4 4671bb8a1bSVinod Koul #define SDW_SHIM_IPPTR 0x8 4771bb8a1bSVinod Koul #define SDW_SHIM_SYNC 0xC 4871bb8a1bSVinod Koul 497cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLSCAP(x) (0x010 + 0x60 * (x)) 507cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS0CM(x) (0x012 + 0x60 * (x)) 517cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS1CM(x) (0x014 + 0x60 * (x)) 527cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS2CM(x) (0x016 + 0x60 * (x)) 537cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTLS3CM(x) (0x018 + 0x60 * (x)) 547cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSCAP(x) (0x020 + 0x60 * (x)) 5571bb8a1bSVinod Koul 567cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSYCHM(x, y) (0x022 + (0x60 * (x)) + (0x2 * (y))) 577cc6e315SPierre-Louis Bossart #define SDW_SHIM_PCMSYCHC(x, y) (0x042 + (0x60 * (x)) + (0x2 * (y))) 587cc6e315SPierre-Louis Bossart #define SDW_SHIM_PDMSCAP(x) (0x062 + 0x60 * (x)) 597cc6e315SPierre-Louis Bossart #define SDW_SHIM_IOCTL(x) (0x06C + 0x60 * (x)) 607cc6e315SPierre-Louis Bossart #define SDW_SHIM_CTMCTL(x) (0x06E + 0x60 * (x)) 6171bb8a1bSVinod Koul 6271bb8a1bSVinod Koul #define SDW_SHIM_WAKEEN 0x190 6371bb8a1bSVinod Koul #define SDW_SHIM_WAKESTS 0x192 6471bb8a1bSVinod Koul 6571bb8a1bSVinod Koul #define SDW_SHIM_LCTL_SPA BIT(0) 665ee74eb2SPierre-Louis Bossart #define SDW_SHIM_LCTL_SPA_MASK GENMASK(3, 0) 6771bb8a1bSVinod Koul #define SDW_SHIM_LCTL_CPA BIT(8) 685ee74eb2SPierre-Louis Bossart #define SDW_SHIM_LCTL_CPA_MASK GENMASK(11, 8) 6971bb8a1bSVinod Koul 704a17c441SPierre-Louis Bossart #define SDW_SHIM_SYNC_SYNCPRD_VAL_24 (24000 / SDW_CADENCE_GSYNC_KHZ - 1) 714a17c441SPierre-Louis Bossart #define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4 (38400 / SDW_CADENCE_GSYNC_KHZ - 1) 7271bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCPRD GENMASK(14, 0) 7371bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCCPU BIT(15) 7471bb8a1bSVinod Koul #define SDW_SHIM_SYNC_CMDSYNC_MASK GENMASK(19, 16) 7571bb8a1bSVinod Koul #define SDW_SHIM_SYNC_CMDSYNC BIT(16) 7671bb8a1bSVinod Koul #define SDW_SHIM_SYNC_SYNCGO BIT(24) 7771bb8a1bSVinod Koul 7871bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_ISS GENMASK(3, 0) 7971bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_OSS GENMASK(7, 4) 8071bb8a1bSVinod Koul #define SDW_SHIM_PCMSCAP_BSS GENMASK(12, 8) 8171bb8a1bSVinod Koul 8271bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_LCHN GENMASK(3, 0) 8371bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_HCHN GENMASK(7, 4) 8471bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_STREAM GENMASK(13, 8) 8571bb8a1bSVinod Koul #define SDW_SHIM_PCMSYCM_DIR BIT(15) 8671bb8a1bSVinod Koul 8771bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_ISS GENMASK(3, 0) 8871bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_OSS GENMASK(7, 4) 8971bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_BSS GENMASK(12, 8) 9071bb8a1bSVinod Koul #define SDW_SHIM_PDMSCAP_CPSS GENMASK(15, 13) 9171bb8a1bSVinod Koul 9271bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_MIF BIT(0) 9371bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_CO BIT(1) 9471bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_COE BIT(2) 9571bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DO BIT(3) 9671bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DOE BIT(4) 9771bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_BKE BIT(5) 9871bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_WPDD BIT(6) 9971bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_CIBD BIT(8) 10071bb8a1bSVinod Koul #define SDW_SHIM_IOCTL_DIBD BIT(9) 10171bb8a1bSVinod Koul 10271bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DACTQE BIT(0) 10371bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DODS BIT(1) 10471bb8a1bSVinod Koul #define SDW_SHIM_CTMCTL_DOAIS GENMASK(4, 3) 10571bb8a1bSVinod Koul 10671bb8a1bSVinod Koul #define SDW_SHIM_WAKEEN_ENABLE BIT(0) 10771bb8a1bSVinod Koul #define SDW_SHIM_WAKESTS_STATUS BIT(0) 10871bb8a1bSVinod Koul 10971bb8a1bSVinod Koul /* Intel ALH Register definitions */ 1107cc6e315SPierre-Louis Bossart #define SDW_ALH_STRMZCFG(x) (0x000 + (0x4 * (x))) 11179ee6631SPierre-Louis Bossart #define SDW_ALH_NUM_STREAMS 64 11271bb8a1bSVinod Koul 11371bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_DMAT_VAL 0x3 11471bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) 11571bb8a1bSVinod Koul #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) 11671bb8a1bSVinod Koul 117c46302ecSVinod Koul enum intel_pdi_type { 118c46302ecSVinod Koul INTEL_PDI_IN = 0, 119c46302ecSVinod Koul INTEL_PDI_OUT = 1, 120c46302ecSVinod Koul INTEL_PDI_BD = 2, 121c46302ecSVinod Koul }; 122c46302ecSVinod Koul 12371bb8a1bSVinod Koul #define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns) 12471bb8a1bSVinod Koul 12571bb8a1bSVinod Koul /* 12671bb8a1bSVinod Koul * Read, write helpers for HW registers 12771bb8a1bSVinod Koul */ 12871bb8a1bSVinod Koul static inline int intel_readl(void __iomem *base, int offset) 12971bb8a1bSVinod Koul { 13071bb8a1bSVinod Koul return readl(base + offset); 13171bb8a1bSVinod Koul } 13271bb8a1bSVinod Koul 13371bb8a1bSVinod Koul static inline void intel_writel(void __iomem *base, int offset, int value) 13471bb8a1bSVinod Koul { 13571bb8a1bSVinod Koul writel(value, base + offset); 13671bb8a1bSVinod Koul } 13771bb8a1bSVinod Koul 13871bb8a1bSVinod Koul static inline u16 intel_readw(void __iomem *base, int offset) 13971bb8a1bSVinod Koul { 14071bb8a1bSVinod Koul return readw(base + offset); 14171bb8a1bSVinod Koul } 14271bb8a1bSVinod Koul 14371bb8a1bSVinod Koul static inline void intel_writew(void __iomem *base, int offset, u16 value) 14471bb8a1bSVinod Koul { 14571bb8a1bSVinod Koul writew(value, base + offset); 14671bb8a1bSVinod Koul } 14771bb8a1bSVinod Koul 1487d2845d5SPierre-Louis Bossart static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target) 14971bb8a1bSVinod Koul { 15071bb8a1bSVinod Koul int timeout = 10; 15171bb8a1bSVinod Koul u32 reg_read; 15271bb8a1bSVinod Koul 15371bb8a1bSVinod Koul do { 15471bb8a1bSVinod Koul reg_read = readl(base + offset); 1557d2845d5SPierre-Louis Bossart if ((reg_read & mask) == target) 15671bb8a1bSVinod Koul return 0; 15771bb8a1bSVinod Koul 15871bb8a1bSVinod Koul timeout--; 1597d2845d5SPierre-Louis Bossart usleep_range(50, 100); 16071bb8a1bSVinod Koul } while (timeout != 0); 16171bb8a1bSVinod Koul 16271bb8a1bSVinod Koul return -EAGAIN; 16371bb8a1bSVinod Koul } 16471bb8a1bSVinod Koul 1657d2845d5SPierre-Louis Bossart static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask) 1667d2845d5SPierre-Louis Bossart { 1677d2845d5SPierre-Louis Bossart writel(value, base + offset); 1687d2845d5SPierre-Louis Bossart return intel_wait_bit(base, offset, mask, 0); 1697d2845d5SPierre-Louis Bossart } 1707d2845d5SPierre-Louis Bossart 17171bb8a1bSVinod Koul static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) 17271bb8a1bSVinod Koul { 17371bb8a1bSVinod Koul writel(value, base + offset); 1747d2845d5SPierre-Louis Bossart return intel_wait_bit(base, offset, mask, mask); 17571bb8a1bSVinod Koul } 17671bb8a1bSVinod Koul 17771bb8a1bSVinod Koul /* 17879ee6631SPierre-Louis Bossart * debugfs 17979ee6631SPierre-Louis Bossart */ 18079ee6631SPierre-Louis Bossart #ifdef CONFIG_DEBUG_FS 18179ee6631SPierre-Louis Bossart 18279ee6631SPierre-Louis Bossart #define RD_BUF (2 * PAGE_SIZE) 18379ee6631SPierre-Louis Bossart 18479ee6631SPierre-Louis Bossart static ssize_t intel_sprintf(void __iomem *mem, bool l, 18579ee6631SPierre-Louis Bossart char *buf, size_t pos, unsigned int reg) 18679ee6631SPierre-Louis Bossart { 18779ee6631SPierre-Louis Bossart int value; 18879ee6631SPierre-Louis Bossart 18979ee6631SPierre-Louis Bossart if (l) 19079ee6631SPierre-Louis Bossart value = intel_readl(mem, reg); 19179ee6631SPierre-Louis Bossart else 19279ee6631SPierre-Louis Bossart value = intel_readw(mem, reg); 19379ee6631SPierre-Louis Bossart 19479ee6631SPierre-Louis Bossart return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value); 19579ee6631SPierre-Louis Bossart } 19679ee6631SPierre-Louis Bossart 19779ee6631SPierre-Louis Bossart static int intel_reg_show(struct seq_file *s_file, void *data) 19879ee6631SPierre-Louis Bossart { 19979ee6631SPierre-Louis Bossart struct sdw_intel *sdw = s_file->private; 2002523486bSPierre-Louis Bossart void __iomem *s = sdw->link_res->shim; 2012523486bSPierre-Louis Bossart void __iomem *a = sdw->link_res->alh; 20279ee6631SPierre-Louis Bossart char *buf; 20379ee6631SPierre-Louis Bossart ssize_t ret; 20479ee6631SPierre-Louis Bossart int i, j; 20579ee6631SPierre-Louis Bossart unsigned int links, reg; 20679ee6631SPierre-Louis Bossart 20779ee6631SPierre-Louis Bossart buf = kzalloc(RD_BUF, GFP_KERNEL); 20879ee6631SPierre-Louis Bossart if (!buf) 20979ee6631SPierre-Louis Bossart return -ENOMEM; 21079ee6631SPierre-Louis Bossart 21179ee6631SPierre-Louis Bossart links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0); 21279ee6631SPierre-Louis Bossart 21379ee6631SPierre-Louis Bossart ret = scnprintf(buf, RD_BUF, "Register Value\n"); 21479ee6631SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n"); 21579ee6631SPierre-Louis Bossart 21679ee6631SPierre-Louis Bossart for (i = 0; i < links; i++) { 21779ee6631SPierre-Louis Bossart reg = SDW_SHIM_LCAP + i * 4; 21879ee6631SPierre-Louis Bossart ret += intel_sprintf(s, true, buf, ret, reg); 21979ee6631SPierre-Louis Bossart } 22079ee6631SPierre-Louis Bossart 22179ee6631SPierre-Louis Bossart for (i = 0; i < links; i++) { 22279ee6631SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i); 22379ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i)); 22479ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i)); 22579ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i)); 22679ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i)); 22779ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i)); 22879ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i)); 22979ee6631SPierre-Louis Bossart 23079ee6631SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\n PCMSyCH registers\n"); 23179ee6631SPierre-Louis Bossart 23279ee6631SPierre-Louis Bossart /* 23379ee6631SPierre-Louis Bossart * the value 10 is the number of PDIs. We will need a 23479ee6631SPierre-Louis Bossart * cleanup to remove hard-coded Intel configurations 23579ee6631SPierre-Louis Bossart * from cadence_master.c 23679ee6631SPierre-Louis Bossart */ 23779ee6631SPierre-Louis Bossart for (j = 0; j < 10; j++) { 23879ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, 23979ee6631SPierre-Louis Bossart SDW_SHIM_PCMSYCHM(i, j)); 24079ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, 24179ee6631SPierre-Louis Bossart SDW_SHIM_PCMSYCHC(i, j)); 24279ee6631SPierre-Louis Bossart } 24379ee6631SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\n PDMSCAP, IOCTL, CTMCTL\n"); 24479ee6631SPierre-Louis Bossart 24579ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i)); 24679ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i)); 24779ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i)); 24879ee6631SPierre-Louis Bossart } 24979ee6631SPierre-Louis Bossart 25079ee6631SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\nWake registers\n"); 25179ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN); 25279ee6631SPierre-Louis Bossart ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS); 25379ee6631SPierre-Louis Bossart 25479ee6631SPierre-Louis Bossart ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH STRMzCFG\n"); 25579ee6631SPierre-Louis Bossart for (i = 0; i < SDW_ALH_NUM_STREAMS; i++) 25679ee6631SPierre-Louis Bossart ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i)); 25779ee6631SPierre-Louis Bossart 25879ee6631SPierre-Louis Bossart seq_printf(s_file, "%s", buf); 25979ee6631SPierre-Louis Bossart kfree(buf); 26079ee6631SPierre-Louis Bossart 26179ee6631SPierre-Louis Bossart return 0; 26279ee6631SPierre-Louis Bossart } 26379ee6631SPierre-Louis Bossart DEFINE_SHOW_ATTRIBUTE(intel_reg); 26479ee6631SPierre-Louis Bossart 2650f9138e7SPierre-Louis Bossart static int intel_set_m_datamode(void *data, u64 value) 2660f9138e7SPierre-Louis Bossart { 2670f9138e7SPierre-Louis Bossart struct sdw_intel *sdw = data; 2680f9138e7SPierre-Louis Bossart struct sdw_bus *bus = &sdw->cdns.bus; 2690f9138e7SPierre-Louis Bossart 2700f9138e7SPierre-Louis Bossart if (value > SDW_PORT_DATA_MODE_STATIC_1) 2710f9138e7SPierre-Louis Bossart return -EINVAL; 2720f9138e7SPierre-Louis Bossart 2730f9138e7SPierre-Louis Bossart /* Userspace changed the hardware state behind the kernel's back */ 2740f9138e7SPierre-Louis Bossart add_taint(TAINT_USER, LOCKDEP_STILL_OK); 2750f9138e7SPierre-Louis Bossart 2760f9138e7SPierre-Louis Bossart bus->params.m_data_mode = value; 2770f9138e7SPierre-Louis Bossart 2780f9138e7SPierre-Louis Bossart return 0; 2790f9138e7SPierre-Louis Bossart } 2800f9138e7SPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(intel_set_m_datamode_fops, NULL, 2810f9138e7SPierre-Louis Bossart intel_set_m_datamode, "%llu\n"); 2820f9138e7SPierre-Louis Bossart 2830f9138e7SPierre-Louis Bossart static int intel_set_s_datamode(void *data, u64 value) 2840f9138e7SPierre-Louis Bossart { 2850f9138e7SPierre-Louis Bossart struct sdw_intel *sdw = data; 2860f9138e7SPierre-Louis Bossart struct sdw_bus *bus = &sdw->cdns.bus; 2870f9138e7SPierre-Louis Bossart 2880f9138e7SPierre-Louis Bossart if (value > SDW_PORT_DATA_MODE_STATIC_1) 2890f9138e7SPierre-Louis Bossart return -EINVAL; 2900f9138e7SPierre-Louis Bossart 2910f9138e7SPierre-Louis Bossart /* Userspace changed the hardware state behind the kernel's back */ 2920f9138e7SPierre-Louis Bossart add_taint(TAINT_USER, LOCKDEP_STILL_OK); 2930f9138e7SPierre-Louis Bossart 2940f9138e7SPierre-Louis Bossart bus->params.s_data_mode = value; 2950f9138e7SPierre-Louis Bossart 2960f9138e7SPierre-Louis Bossart return 0; 2970f9138e7SPierre-Louis Bossart } 2980f9138e7SPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(intel_set_s_datamode_fops, NULL, 2990f9138e7SPierre-Louis Bossart intel_set_s_datamode, "%llu\n"); 3000f9138e7SPierre-Louis Bossart 30179ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw) 30279ee6631SPierre-Louis Bossart { 30379ee6631SPierre-Louis Bossart struct dentry *root = sdw->cdns.bus.debugfs; 30479ee6631SPierre-Louis Bossart 30579ee6631SPierre-Louis Bossart if (!root) 30679ee6631SPierre-Louis Bossart return; 30779ee6631SPierre-Louis Bossart 30879ee6631SPierre-Louis Bossart sdw->debugfs = debugfs_create_dir("intel-sdw", root); 30979ee6631SPierre-Louis Bossart 31079ee6631SPierre-Louis Bossart debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw, 31179ee6631SPierre-Louis Bossart &intel_reg_fops); 31279ee6631SPierre-Louis Bossart 3130f9138e7SPierre-Louis Bossart debugfs_create_file("intel-m-datamode", 0200, sdw->debugfs, sdw, 3140f9138e7SPierre-Louis Bossart &intel_set_m_datamode_fops); 3150f9138e7SPierre-Louis Bossart 3160f9138e7SPierre-Louis Bossart debugfs_create_file("intel-s-datamode", 0200, sdw->debugfs, sdw, 3170f9138e7SPierre-Louis Bossart &intel_set_s_datamode_fops); 3180f9138e7SPierre-Louis Bossart 31979ee6631SPierre-Louis Bossart sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs); 32079ee6631SPierre-Louis Bossart } 32179ee6631SPierre-Louis Bossart 32279ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw) 32379ee6631SPierre-Louis Bossart { 32479ee6631SPierre-Louis Bossart debugfs_remove_recursive(sdw->debugfs); 32579ee6631SPierre-Louis Bossart } 32679ee6631SPierre-Louis Bossart #else 32779ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw) {} 32879ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw) {} 32979ee6631SPierre-Louis Bossart #endif /* CONFIG_DEBUG_FS */ 33079ee6631SPierre-Louis Bossart 33179ee6631SPierre-Louis Bossart /* 33271bb8a1bSVinod Koul * shim ops 33371bb8a1bSVinod Koul */ 33471bb8a1bSVinod Koul 33571bb8a1bSVinod Koul static int intel_link_power_up(struct sdw_intel *sdw) 33671bb8a1bSVinod Koul { 33771bb8a1bSVinod Koul unsigned int link_id = sdw->instance; 3382523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 3394a17c441SPierre-Louis Bossart u32 *shim_mask = sdw->link_res->shim_mask; 3404a17c441SPierre-Louis Bossart struct sdw_bus *bus = &sdw->cdns.bus; 3414a17c441SPierre-Louis Bossart struct sdw_master_prop *prop = &bus->prop; 3425ee74eb2SPierre-Louis Bossart u32 spa_mask, cpa_mask; 3435ee74eb2SPierre-Louis Bossart u32 link_control; 3444a17c441SPierre-Louis Bossart int ret = 0; 3454a17c441SPierre-Louis Bossart u32 syncprd; 3464a17c441SPierre-Louis Bossart u32 sync_reg; 3474a17c441SPierre-Louis Bossart 3484a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 3494a17c441SPierre-Louis Bossart 3504a17c441SPierre-Louis Bossart /* 3514a17c441SPierre-Louis Bossart * The hardware relies on an internal counter, typically 4kHz, 3524a17c441SPierre-Louis Bossart * to generate the SoundWire SSP - which defines a 'safe' 3534a17c441SPierre-Louis Bossart * synchronization point between commands and audio transport 3544a17c441SPierre-Louis Bossart * and allows for multi link synchronization. The SYNCPRD value 3554a17c441SPierre-Louis Bossart * is only dependent on the oscillator clock provided to 3564a17c441SPierre-Louis Bossart * the IP, so adjust based on _DSD properties reported in DSDT 3574a17c441SPierre-Louis Bossart * tables. The values reported are based on either 24MHz 3584a17c441SPierre-Louis Bossart * (CNL/CML) or 38.4 MHz (ICL/TGL+). 3594a17c441SPierre-Louis Bossart */ 3604a17c441SPierre-Louis Bossart if (prop->mclk_freq % 6000000) 3614a17c441SPierre-Louis Bossart syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; 3624a17c441SPierre-Louis Bossart else 3634a17c441SPierre-Louis Bossart syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; 3644a17c441SPierre-Louis Bossart 3654a17c441SPierre-Louis Bossart if (!*shim_mask) { 3665ee74eb2SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, "%s: powering up all links\n", __func__); 3675ee74eb2SPierre-Louis Bossart 3684a17c441SPierre-Louis Bossart /* we first need to program the SyncPRD/CPU registers */ 3694a17c441SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, 3704a17c441SPierre-Louis Bossart "%s: first link up, programming SYNCPRD\n", __func__); 3714a17c441SPierre-Louis Bossart 3724a17c441SPierre-Louis Bossart /* set SyncPRD period */ 3734a17c441SPierre-Louis Bossart sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 374f067c925SVinod Koul u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD); 3754a17c441SPierre-Louis Bossart 3764a17c441SPierre-Louis Bossart /* Set SyncCPU bit */ 3774a17c441SPierre-Louis Bossart sync_reg |= SDW_SHIM_SYNC_SYNCCPU; 3784a17c441SPierre-Louis Bossart intel_writel(shim, SDW_SHIM_SYNC, sync_reg); 37971bb8a1bSVinod Koul 38071bb8a1bSVinod Koul /* Link power up sequence */ 38171bb8a1bSVinod Koul link_control = intel_readl(shim, SDW_SHIM_LCTL); 3825ee74eb2SPierre-Louis Bossart 3835ee74eb2SPierre-Louis Bossart /* only power-up enabled links */ 3843b4979caSVinod Koul spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask); 3853b4979caSVinod Koul cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); 3865ee74eb2SPierre-Louis Bossart 38771bb8a1bSVinod Koul link_control |= spa_mask; 38871bb8a1bSVinod Koul 38971bb8a1bSVinod Koul ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); 3904a17c441SPierre-Louis Bossart if (ret < 0) { 3914a17c441SPierre-Louis Bossart dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret); 3924a17c441SPierre-Louis Bossart goto out; 39371bb8a1bSVinod Koul } 39471bb8a1bSVinod Koul 3954a17c441SPierre-Louis Bossart /* SyncCPU will change once link is active */ 3964a17c441SPierre-Louis Bossart ret = intel_wait_bit(shim, SDW_SHIM_SYNC, 3974a17c441SPierre-Louis Bossart SDW_SHIM_SYNC_SYNCCPU, 0); 3984a17c441SPierre-Louis Bossart if (ret < 0) { 3994a17c441SPierre-Louis Bossart dev_err(sdw->cdns.dev, 4004a17c441SPierre-Louis Bossart "Failed to set SHIM_SYNC: %d\n", ret); 4014a17c441SPierre-Louis Bossart goto out; 4024a17c441SPierre-Louis Bossart } 4034a17c441SPierre-Louis Bossart } 4044a17c441SPierre-Louis Bossart 4054a17c441SPierre-Louis Bossart *shim_mask |= BIT(link_id); 4064a17c441SPierre-Louis Bossart 4074a17c441SPierre-Louis Bossart sdw->cdns.link_up = true; 4084a17c441SPierre-Louis Bossart out: 4094a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 4104a17c441SPierre-Louis Bossart 4114a17c441SPierre-Louis Bossart return ret; 4124a17c441SPierre-Louis Bossart } 4134a17c441SPierre-Louis Bossart 4144a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */ 4154a17c441SPierre-Louis Bossart static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw) 41671bb8a1bSVinod Koul { 4172523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 41871bb8a1bSVinod Koul unsigned int link_id = sdw->instance; 4194a17c441SPierre-Louis Bossart u16 ioctl; 42071bb8a1bSVinod Koul 42171bb8a1bSVinod Koul /* Switch to MIP from Glue logic */ 42271bb8a1bSVinod Koul ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); 42371bb8a1bSVinod Koul 42471bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_DOE); 42571bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4264a17c441SPierre-Louis Bossart usleep_range(10, 15); 42771bb8a1bSVinod Koul 42871bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_DO); 42971bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4304a17c441SPierre-Louis Bossart usleep_range(10, 15); 43171bb8a1bSVinod Koul 43271bb8a1bSVinod Koul ioctl |= (SDW_SHIM_IOCTL_MIF); 43371bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4344a17c441SPierre-Louis Bossart usleep_range(10, 15); 43571bb8a1bSVinod Koul 43671bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_BKE); 43771bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_COE); 43871bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4394a17c441SPierre-Louis Bossart usleep_range(10, 15); 4404a17c441SPierre-Louis Bossart 4414a17c441SPierre-Louis Bossart /* at this point Master IP has full control of the I/Os */ 4424a17c441SPierre-Louis Bossart } 4434a17c441SPierre-Louis Bossart 4444a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */ 4454a17c441SPierre-Louis Bossart static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw) 4464a17c441SPierre-Louis Bossart { 4474a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 4484a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 4494a17c441SPierre-Louis Bossart u16 ioctl; 4504a17c441SPierre-Louis Bossart 4514a17c441SPierre-Louis Bossart /* Glue logic */ 4524a17c441SPierre-Louis Bossart ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); 4534a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_BKE; 4544a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_COE; 4554a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4564a17c441SPierre-Louis Bossart usleep_range(10, 15); 4574a17c441SPierre-Louis Bossart 4584a17c441SPierre-Louis Bossart ioctl &= ~(SDW_SHIM_IOCTL_MIF); 4594a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4604a17c441SPierre-Louis Bossart usleep_range(10, 15); 4614a17c441SPierre-Louis Bossart 4624a17c441SPierre-Louis Bossart /* at this point Integration Glue has full control of the I/Os */ 4634a17c441SPierre-Louis Bossart } 4644a17c441SPierre-Louis Bossart 4654a17c441SPierre-Louis Bossart static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop) 4664a17c441SPierre-Louis Bossart { 4674a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 4684a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 4694a17c441SPierre-Louis Bossart int ret = 0; 4704a17c441SPierre-Louis Bossart u16 ioctl = 0, act = 0; 4714a17c441SPierre-Louis Bossart 4724a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 4734a17c441SPierre-Louis Bossart 4744a17c441SPierre-Louis Bossart /* Initialize Shim */ 4754a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_BKE; 4764a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4774a17c441SPierre-Louis Bossart usleep_range(10, 15); 4784a17c441SPierre-Louis Bossart 4794a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_WPDD; 4804a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4814a17c441SPierre-Louis Bossart usleep_range(10, 15); 4824a17c441SPierre-Louis Bossart 4834a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_DO; 4844a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4854a17c441SPierre-Louis Bossart usleep_range(10, 15); 4864a17c441SPierre-Louis Bossart 4874a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_DOE; 4884a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4894a17c441SPierre-Louis Bossart usleep_range(10, 15); 4904a17c441SPierre-Louis Bossart 4914a17c441SPierre-Louis Bossart intel_shim_glue_to_master_ip(sdw); 49271bb8a1bSVinod Koul 493f067c925SVinod Koul u16p_replace_bits(&act, 0x1, SDW_SHIM_CTMCTL_DOAIS); 49471bb8a1bSVinod Koul act |= SDW_SHIM_CTMCTL_DACTQE; 49571bb8a1bSVinod Koul act |= SDW_SHIM_CTMCTL_DODS; 49671bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act); 4974a17c441SPierre-Louis Bossart usleep_range(10, 15); 49871bb8a1bSVinod Koul 4994a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 50071bb8a1bSVinod Koul 50171bb8a1bSVinod Koul return ret; 50271bb8a1bSVinod Koul } 50371bb8a1bSVinod Koul 504ab2c9132SRander Wang static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) 5054a17c441SPierre-Louis Bossart { 5064a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 5074a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 5084a17c441SPierre-Louis Bossart u16 wake_en, wake_sts; 5094a17c441SPierre-Louis Bossart 5104a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 5114a17c441SPierre-Louis Bossart wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); 5124a17c441SPierre-Louis Bossart 5134a17c441SPierre-Louis Bossart if (wake_enable) { 5144a17c441SPierre-Louis Bossart /* Enable the wakeup */ 5154a17c441SPierre-Louis Bossart wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id); 5164a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); 5174a17c441SPierre-Louis Bossart } else { 5184a17c441SPierre-Louis Bossart /* Disable the wake up interrupt */ 5194a17c441SPierre-Louis Bossart wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); 5204a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); 5214a17c441SPierre-Louis Bossart 5224a17c441SPierre-Louis Bossart /* Clear wake status */ 5234a17c441SPierre-Louis Bossart wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); 5244a17c441SPierre-Louis Bossart wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id); 5254a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts); 5264a17c441SPierre-Louis Bossart } 5274a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 5284a17c441SPierre-Louis Bossart } 5294a17c441SPierre-Louis Bossart 5309b3b4b3fSPierre-Louis Bossart static int intel_link_power_down(struct sdw_intel *sdw) 5314a17c441SPierre-Louis Bossart { 5325ee74eb2SPierre-Louis Bossart u32 link_control, spa_mask, cpa_mask; 5334a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 5344a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 5354a17c441SPierre-Louis Bossart u32 *shim_mask = sdw->link_res->shim_mask; 5364a17c441SPierre-Louis Bossart int ret = 0; 5374a17c441SPierre-Louis Bossart 5384a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 5394a17c441SPierre-Louis Bossart 5404a17c441SPierre-Louis Bossart intel_shim_master_ip_to_glue(sdw); 5414a17c441SPierre-Louis Bossart 5424a17c441SPierre-Louis Bossart if (!(*shim_mask & BIT(link_id))) 5434a17c441SPierre-Louis Bossart dev_err(sdw->cdns.dev, 5444a17c441SPierre-Louis Bossart "%s: Unbalanced power-up/down calls\n", __func__); 5454a17c441SPierre-Louis Bossart 5464a17c441SPierre-Louis Bossart *shim_mask &= ~BIT(link_id); 5474a17c441SPierre-Louis Bossart 5485ee74eb2SPierre-Louis Bossart if (!*shim_mask) { 5495ee74eb2SPierre-Louis Bossart 5505ee74eb2SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, "%s: powering down all links\n", __func__); 5515ee74eb2SPierre-Louis Bossart 5525ee74eb2SPierre-Louis Bossart /* Link power down sequence */ 5535ee74eb2SPierre-Louis Bossart link_control = intel_readl(shim, SDW_SHIM_LCTL); 5545ee74eb2SPierre-Louis Bossart 5555ee74eb2SPierre-Louis Bossart /* only power-down enabled links */ 5563b4979caSVinod Koul spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, ~sdw->link_res->link_mask); 5573b4979caSVinod Koul cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); 5585ee74eb2SPierre-Louis Bossart 5595ee74eb2SPierre-Louis Bossart link_control &= spa_mask; 5605ee74eb2SPierre-Louis Bossart 5615ee74eb2SPierre-Louis Bossart ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); 5625ee74eb2SPierre-Louis Bossart } 5635ee74eb2SPierre-Louis Bossart 5645ee74eb2SPierre-Louis Bossart link_control = intel_readl(shim, SDW_SHIM_LCTL); 5655ee74eb2SPierre-Louis Bossart 5664a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 5674a17c441SPierre-Louis Bossart 5685ee74eb2SPierre-Louis Bossart if (ret < 0) { 5695ee74eb2SPierre-Louis Bossart dev_err(sdw->cdns.dev, "%s: could not power down link\n", __func__); 5705ee74eb2SPierre-Louis Bossart 5714a17c441SPierre-Louis Bossart return ret; 5725ee74eb2SPierre-Louis Bossart } 5734a17c441SPierre-Louis Bossart 5744a17c441SPierre-Louis Bossart sdw->cdns.link_up = false; 5754a17c441SPierre-Louis Bossart return 0; 5764a17c441SPierre-Louis Bossart } 5774a17c441SPierre-Louis Bossart 57802629e45SPierre-Louis Bossart static void intel_shim_sync_arm(struct sdw_intel *sdw) 57902629e45SPierre-Louis Bossart { 58002629e45SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 58102629e45SPierre-Louis Bossart u32 sync_reg; 58202629e45SPierre-Louis Bossart 58302629e45SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 58402629e45SPierre-Louis Bossart 58502629e45SPierre-Louis Bossart /* update SYNC register */ 58602629e45SPierre-Louis Bossart sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 58702629e45SPierre-Louis Bossart sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance); 58802629e45SPierre-Louis Bossart intel_writel(shim, SDW_SHIM_SYNC, sync_reg); 58902629e45SPierre-Louis Bossart 59002629e45SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 59102629e45SPierre-Louis Bossart } 59202629e45SPierre-Louis Bossart 593437e3289SPierre-Louis Bossart static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw) 594437e3289SPierre-Louis Bossart { 595437e3289SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 596437e3289SPierre-Louis Bossart u32 sync_reg; 597437e3289SPierre-Louis Bossart int ret; 598437e3289SPierre-Louis Bossart 599437e3289SPierre-Louis Bossart /* Read SYNC register */ 600437e3289SPierre-Louis Bossart sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 601437e3289SPierre-Louis Bossart 602437e3289SPierre-Louis Bossart /* 603437e3289SPierre-Louis Bossart * Set SyncGO bit to synchronously trigger a bank switch for 604437e3289SPierre-Louis Bossart * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all 605437e3289SPierre-Louis Bossart * the Masters. 606437e3289SPierre-Louis Bossart */ 607437e3289SPierre-Louis Bossart sync_reg |= SDW_SHIM_SYNC_SYNCGO; 608437e3289SPierre-Louis Bossart 609437e3289SPierre-Louis Bossart ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, 610437e3289SPierre-Louis Bossart SDW_SHIM_SYNC_SYNCGO); 611437e3289SPierre-Louis Bossart 612437e3289SPierre-Louis Bossart if (ret < 0) 613437e3289SPierre-Louis Bossart dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret); 61471bb8a1bSVinod Koul 61571bb8a1bSVinod Koul return ret; 61671bb8a1bSVinod Koul } 61771bb8a1bSVinod Koul 618857a7c42SPierre-Louis Bossart static int intel_shim_sync_go(struct sdw_intel *sdw) 619857a7c42SPierre-Louis Bossart { 620857a7c42SPierre-Louis Bossart int ret; 621857a7c42SPierre-Louis Bossart 622857a7c42SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 623857a7c42SPierre-Louis Bossart 624857a7c42SPierre-Louis Bossart ret = intel_shim_sync_go_unlocked(sdw); 625857a7c42SPierre-Louis Bossart 626857a7c42SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 627857a7c42SPierre-Louis Bossart 628857a7c42SPierre-Louis Bossart return ret; 629857a7c42SPierre-Louis Bossart } 630857a7c42SPierre-Louis Bossart 63137a2d22bSVinod Koul /* 63237a2d22bSVinod Koul * PDI routines 63337a2d22bSVinod Koul */ 63437a2d22bSVinod Koul static void intel_pdi_init(struct sdw_intel *sdw, 63537a2d22bSVinod Koul struct sdw_cdns_stream_config *config) 63637a2d22bSVinod Koul { 6372523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 63837a2d22bSVinod Koul unsigned int link_id = sdw->instance; 63937a2d22bSVinod Koul int pcm_cap, pdm_cap; 64037a2d22bSVinod Koul 64137a2d22bSVinod Koul /* PCM Stream Capability */ 64237a2d22bSVinod Koul pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id)); 64337a2d22bSVinod Koul 6443b4979caSVinod Koul config->pcm_bd = FIELD_GET(SDW_SHIM_PCMSCAP_BSS, pcm_cap); 6453b4979caSVinod Koul config->pcm_in = FIELD_GET(SDW_SHIM_PCMSCAP_ISS, pcm_cap); 6463b4979caSVinod Koul config->pcm_out = FIELD_GET(SDW_SHIM_PCMSCAP_OSS, pcm_cap); 64737a2d22bSVinod Koul 648121f4361SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n", 649121f4361SPierre-Louis Bossart config->pcm_bd, config->pcm_in, config->pcm_out); 650121f4361SPierre-Louis Bossart 65137a2d22bSVinod Koul /* PDM Stream Capability */ 65237a2d22bSVinod Koul pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); 65337a2d22bSVinod Koul 6543b4979caSVinod Koul config->pdm_bd = FIELD_GET(SDW_SHIM_PDMSCAP_BSS, pdm_cap); 6553b4979caSVinod Koul config->pdm_in = FIELD_GET(SDW_SHIM_PDMSCAP_ISS, pdm_cap); 6563b4979caSVinod Koul config->pdm_out = FIELD_GET(SDW_SHIM_PDMSCAP_OSS, pdm_cap); 657121f4361SPierre-Louis Bossart 658121f4361SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, "PDM cap bd:%d in:%d out:%d\n", 659121f4361SPierre-Louis Bossart config->pdm_bd, config->pdm_in, config->pdm_out); 66037a2d22bSVinod Koul } 66137a2d22bSVinod Koul 66237a2d22bSVinod Koul static int 66337a2d22bSVinod Koul intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm) 66437a2d22bSVinod Koul { 6652523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 66637a2d22bSVinod Koul unsigned int link_id = sdw->instance; 66737a2d22bSVinod Koul int count; 66837a2d22bSVinod Koul 66937a2d22bSVinod Koul if (pcm) { 67037a2d22bSVinod Koul count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); 67118046335SPierre-Louis Bossart 67218046335SPierre-Louis Bossart /* 67318046335SPierre-Louis Bossart * WORKAROUND: on all existing Intel controllers, pdi 67418046335SPierre-Louis Bossart * number 2 reports channel count as 1 even though it 67518046335SPierre-Louis Bossart * supports 8 channels. Performing hardcoding for pdi 67618046335SPierre-Louis Bossart * number 2. 67718046335SPierre-Louis Bossart */ 67818046335SPierre-Louis Bossart if (pdi_num == 2) 67918046335SPierre-Louis Bossart count = 7; 68018046335SPierre-Louis Bossart 68137a2d22bSVinod Koul } else { 68237a2d22bSVinod Koul count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); 6833b4979caSVinod Koul count = FIELD_GET(SDW_SHIM_PDMSCAP_CPSS, count); 68437a2d22bSVinod Koul } 68537a2d22bSVinod Koul 68637a2d22bSVinod Koul /* zero based values for channel count in register */ 68737a2d22bSVinod Koul count++; 68837a2d22bSVinod Koul 68937a2d22bSVinod Koul return count; 69037a2d22bSVinod Koul } 69137a2d22bSVinod Koul 69237a2d22bSVinod Koul static int intel_pdi_get_ch_update(struct sdw_intel *sdw, 69337a2d22bSVinod Koul struct sdw_cdns_pdi *pdi, 69437a2d22bSVinod Koul unsigned int num_pdi, 69537a2d22bSVinod Koul unsigned int *num_ch, bool pcm) 69637a2d22bSVinod Koul { 69737a2d22bSVinod Koul int i, ch_count = 0; 69837a2d22bSVinod Koul 69937a2d22bSVinod Koul for (i = 0; i < num_pdi; i++) { 70037a2d22bSVinod Koul pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm); 70137a2d22bSVinod Koul ch_count += pdi->ch_count; 70237a2d22bSVinod Koul pdi++; 70337a2d22bSVinod Koul } 70437a2d22bSVinod Koul 70537a2d22bSVinod Koul *num_ch = ch_count; 70637a2d22bSVinod Koul return 0; 70737a2d22bSVinod Koul } 70837a2d22bSVinod Koul 70937a2d22bSVinod Koul static int intel_pdi_stream_ch_update(struct sdw_intel *sdw, 71037a2d22bSVinod Koul struct sdw_cdns_streams *stream, bool pcm) 71137a2d22bSVinod Koul { 71237a2d22bSVinod Koul intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd, 71337a2d22bSVinod Koul &stream->num_ch_bd, pcm); 71437a2d22bSVinod Koul 71537a2d22bSVinod Koul intel_pdi_get_ch_update(sdw, stream->in, stream->num_in, 71637a2d22bSVinod Koul &stream->num_ch_in, pcm); 71737a2d22bSVinod Koul 71837a2d22bSVinod Koul intel_pdi_get_ch_update(sdw, stream->out, stream->num_out, 71937a2d22bSVinod Koul &stream->num_ch_out, pcm); 72037a2d22bSVinod Koul 72137a2d22bSVinod Koul return 0; 72237a2d22bSVinod Koul } 72337a2d22bSVinod Koul 72437a2d22bSVinod Koul static int intel_pdi_ch_update(struct sdw_intel *sdw) 72537a2d22bSVinod Koul { 72637a2d22bSVinod Koul /* First update PCM streams followed by PDM streams */ 72737a2d22bSVinod Koul intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true); 72837a2d22bSVinod Koul intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false); 72937a2d22bSVinod Koul 73037a2d22bSVinod Koul return 0; 73137a2d22bSVinod Koul } 73237a2d22bSVinod Koul 73337a2d22bSVinod Koul static void 73437a2d22bSVinod Koul intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) 73537a2d22bSVinod Koul { 7362523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 73737a2d22bSVinod Koul unsigned int link_id = sdw->instance; 73837a2d22bSVinod Koul int pdi_conf = 0; 73937a2d22bSVinod Koul 740c134f914SPierre-Louis Bossart /* the Bulk and PCM streams are not contiguous */ 741c134f914SPierre-Louis Bossart pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; 742c134f914SPierre-Louis Bossart if (pdi->num >= 2) 743c134f914SPierre-Louis Bossart pdi->intel_alh_id += 2; 74437a2d22bSVinod Koul 74537a2d22bSVinod Koul /* 74637a2d22bSVinod Koul * Program stream parameters to stream SHIM register 74737a2d22bSVinod Koul * This is applicable for PCM stream only. 74837a2d22bSVinod Koul */ 74937a2d22bSVinod Koul if (pdi->type != SDW_STREAM_PCM) 75037a2d22bSVinod Koul return; 75137a2d22bSVinod Koul 75237a2d22bSVinod Koul if (pdi->dir == SDW_DATA_DIR_RX) 75337a2d22bSVinod Koul pdi_conf |= SDW_SHIM_PCMSYCM_DIR; 75437a2d22bSVinod Koul else 75537a2d22bSVinod Koul pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR); 75637a2d22bSVinod Koul 757f067c925SVinod Koul u32p_replace_bits(&pdi_conf, pdi->intel_alh_id, SDW_SHIM_PCMSYCM_STREAM); 758f067c925SVinod Koul u32p_replace_bits(&pdi_conf, pdi->l_ch_num, SDW_SHIM_PCMSYCM_LCHN); 759f067c925SVinod Koul u32p_replace_bits(&pdi_conf, pdi->h_ch_num, SDW_SHIM_PCMSYCM_HCHN); 76037a2d22bSVinod Koul 76137a2d22bSVinod Koul intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf); 76237a2d22bSVinod Koul } 76337a2d22bSVinod Koul 76437a2d22bSVinod Koul static void 76537a2d22bSVinod Koul intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) 76637a2d22bSVinod Koul { 7672523486bSPierre-Louis Bossart void __iomem *alh = sdw->link_res->alh; 76837a2d22bSVinod Koul unsigned int link_id = sdw->instance; 76937a2d22bSVinod Koul unsigned int conf; 77037a2d22bSVinod Koul 771c134f914SPierre-Louis Bossart /* the Bulk and PCM streams are not contiguous */ 772c134f914SPierre-Louis Bossart pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; 773c134f914SPierre-Louis Bossart if (pdi->num >= 2) 774c134f914SPierre-Louis Bossart pdi->intel_alh_id += 2; 77537a2d22bSVinod Koul 77637a2d22bSVinod Koul /* Program Stream config ALH register */ 77737a2d22bSVinod Koul conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id)); 77837a2d22bSVinod Koul 779f067c925SVinod Koul u32p_replace_bits(&conf, SDW_ALH_STRMZCFG_DMAT_VAL, SDW_ALH_STRMZCFG_DMAT); 780f067c925SVinod Koul u32p_replace_bits(&conf, pdi->ch_count - 1, SDW_ALH_STRMZCFG_CHN); 78137a2d22bSVinod Koul 78237a2d22bSVinod Koul intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); 78337a2d22bSVinod Koul } 78437a2d22bSVinod Koul 7854b206d34SRander Wang static int intel_params_stream(struct sdw_intel *sdw, 786c46302ecSVinod Koul struct snd_pcm_substream *substream, 787c46302ecSVinod Koul struct snd_soc_dai *dai, 7884b206d34SRander Wang struct snd_pcm_hw_params *hw_params, 7894b206d34SRander Wang int link_id, int alh_stream_id) 790c46302ecSVinod Koul { 7912523486bSPierre-Louis Bossart struct sdw_intel_link_res *res = sdw->link_res; 7924b206d34SRander Wang struct sdw_intel_stream_params_data params_data; 79305c8afe4SPierre-Louis Bossart 7944b206d34SRander Wang params_data.substream = substream; 7954b206d34SRander Wang params_data.dai = dai; 7964b206d34SRander Wang params_data.hw_params = hw_params; 7974b206d34SRander Wang params_data.link_id = link_id; 7984b206d34SRander Wang params_data.alh_stream_id = alh_stream_id; 799c46302ecSVinod Koul 8004b206d34SRander Wang if (res->ops && res->ops->params_stream && res->dev) 8014b206d34SRander Wang return res->ops->params_stream(res->dev, 8024b206d34SRander Wang ¶ms_data); 803c46302ecSVinod Koul return -EIO; 804c46302ecSVinod Koul } 805c46302ecSVinod Koul 806eff346f2SPierre-Louis Bossart static int intel_free_stream(struct sdw_intel *sdw, 807eff346f2SPierre-Louis Bossart struct snd_pcm_substream *substream, 808eff346f2SPierre-Louis Bossart struct snd_soc_dai *dai, 809eff346f2SPierre-Louis Bossart int link_id) 810eff346f2SPierre-Louis Bossart { 811eff346f2SPierre-Louis Bossart struct sdw_intel_link_res *res = sdw->link_res; 812eff346f2SPierre-Louis Bossart struct sdw_intel_stream_free_data free_data; 813eff346f2SPierre-Louis Bossart 814eff346f2SPierre-Louis Bossart free_data.substream = substream; 815eff346f2SPierre-Louis Bossart free_data.dai = dai; 816eff346f2SPierre-Louis Bossart free_data.link_id = link_id; 817eff346f2SPierre-Louis Bossart 818eff346f2SPierre-Louis Bossart if (res->ops && res->ops->free_stream && res->dev) 819eff346f2SPierre-Louis Bossart return res->ops->free_stream(res->dev, 820eff346f2SPierre-Louis Bossart &free_data); 821eff346f2SPierre-Louis Bossart 822eff346f2SPierre-Louis Bossart return 0; 823eff346f2SPierre-Louis Bossart } 824eff346f2SPierre-Louis Bossart 825c46302ecSVinod Koul /* 82630246e2dSShreyas NC * bank switch routines 82730246e2dSShreyas NC */ 82830246e2dSShreyas NC 82930246e2dSShreyas NC static int intel_pre_bank_switch(struct sdw_bus *bus) 83030246e2dSShreyas NC { 83130246e2dSShreyas NC struct sdw_cdns *cdns = bus_to_cdns(bus); 83230246e2dSShreyas NC struct sdw_intel *sdw = cdns_to_intel(cdns); 83330246e2dSShreyas NC 83430246e2dSShreyas NC /* Write to register only for multi-link */ 83530246e2dSShreyas NC if (!bus->multi_link) 83630246e2dSShreyas NC return 0; 83730246e2dSShreyas NC 83802629e45SPierre-Louis Bossart intel_shim_sync_arm(sdw); 83930246e2dSShreyas NC 84030246e2dSShreyas NC return 0; 84130246e2dSShreyas NC } 84230246e2dSShreyas NC 84330246e2dSShreyas NC static int intel_post_bank_switch(struct sdw_bus *bus) 84430246e2dSShreyas NC { 84530246e2dSShreyas NC struct sdw_cdns *cdns = bus_to_cdns(bus); 84630246e2dSShreyas NC struct sdw_intel *sdw = cdns_to_intel(cdns); 8472523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 84830246e2dSShreyas NC int sync_reg, ret; 84930246e2dSShreyas NC 85030246e2dSShreyas NC /* Write to register only for multi-link */ 85130246e2dSShreyas NC if (!bus->multi_link) 85230246e2dSShreyas NC return 0; 85330246e2dSShreyas NC 8544a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 8554a17c441SPierre-Louis Bossart 85630246e2dSShreyas NC /* Read SYNC register */ 85730246e2dSShreyas NC sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 85830246e2dSShreyas NC 85930246e2dSShreyas NC /* 86030246e2dSShreyas NC * post_bank_switch() ops is called from the bus in loop for 86130246e2dSShreyas NC * all the Masters in the steam with the expectation that 86230246e2dSShreyas NC * we trigger the bankswitch for the only first Master in the list 86330246e2dSShreyas NC * and do nothing for the other Masters 86430246e2dSShreyas NC * 86530246e2dSShreyas NC * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master. 86630246e2dSShreyas NC */ 8674a17c441SPierre-Louis Bossart if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) { 8684a17c441SPierre-Louis Bossart ret = 0; 8694a17c441SPierre-Louis Bossart goto unlock; 8704a17c441SPierre-Louis Bossart } 87130246e2dSShreyas NC 872437e3289SPierre-Louis Bossart ret = intel_shim_sync_go_unlocked(sdw); 8734a17c441SPierre-Louis Bossart unlock: 8744a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 87530246e2dSShreyas NC 87630246e2dSShreyas NC if (ret < 0) 87717ed5befSPierre-Louis Bossart dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret); 87830246e2dSShreyas NC 87930246e2dSShreyas NC return ret; 88030246e2dSShreyas NC } 88130246e2dSShreyas NC 88230246e2dSShreyas NC /* 883c46302ecSVinod Koul * DAI routines 884c46302ecSVinod Koul */ 885c46302ecSVinod Koul 8865e7484d0SRander Wang static int intel_startup(struct snd_pcm_substream *substream, 8875e7484d0SRander Wang struct snd_soc_dai *dai) 8885e7484d0SRander Wang { 889ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 890ebf878edSPierre-Louis Bossart int ret; 891ebf878edSPierre-Louis Bossart 892ebf878edSPierre-Louis Bossart ret = pm_runtime_get_sync(cdns->dev); 893ebf878edSPierre-Louis Bossart if (ret < 0 && ret != -EACCES) { 894ebf878edSPierre-Louis Bossart dev_err_ratelimited(cdns->dev, 895ebf878edSPierre-Louis Bossart "pm_runtime_get_sync failed in %s, ret %d\n", 896ebf878edSPierre-Louis Bossart __func__, ret); 897ebf878edSPierre-Louis Bossart pm_runtime_put_noidle(cdns->dev); 898ebf878edSPierre-Louis Bossart return ret; 899ebf878edSPierre-Louis Bossart } 900ff16d1e5SPierre-Louis Bossart return 0; 9015e7484d0SRander Wang } 9025e7484d0SRander Wang 903c46302ecSVinod Koul static int intel_hw_params(struct snd_pcm_substream *substream, 904c46302ecSVinod Koul struct snd_pcm_hw_params *params, 905c46302ecSVinod Koul struct snd_soc_dai *dai) 906c46302ecSVinod Koul { 907c46302ecSVinod Koul struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 908c46302ecSVinod Koul struct sdw_intel *sdw = cdns_to_intel(cdns); 909c46302ecSVinod Koul struct sdw_cdns_dma_data *dma; 91057a34790SPierre-Louis Bossart struct sdw_cdns_pdi *pdi; 911c46302ecSVinod Koul struct sdw_stream_config sconfig; 912c46302ecSVinod Koul struct sdw_port_config *pconfig; 91357a34790SPierre-Louis Bossart int ch, dir; 91457a34790SPierre-Louis Bossart int ret; 915c46302ecSVinod Koul bool pcm = true; 916c46302ecSVinod Koul 917c46302ecSVinod Koul dma = snd_soc_dai_get_dma_data(dai, substream); 918c46302ecSVinod Koul if (!dma) 919c46302ecSVinod Koul return -EIO; 920c46302ecSVinod Koul 921c46302ecSVinod Koul ch = params_channels(params); 922c46302ecSVinod Koul if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 923c46302ecSVinod Koul dir = SDW_DATA_DIR_RX; 924c46302ecSVinod Koul else 925c46302ecSVinod Koul dir = SDW_DATA_DIR_TX; 926c46302ecSVinod Koul 92757a34790SPierre-Louis Bossart if (dma->stream_type == SDW_STREAM_PDM) 928c46302ecSVinod Koul pcm = false; 929c46302ecSVinod Koul 93057a34790SPierre-Louis Bossart if (pcm) 9311b53385eSBard Liao pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); 93257a34790SPierre-Louis Bossart else 9331b53385eSBard Liao pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir, dai->id); 934c46302ecSVinod Koul 93557a34790SPierre-Louis Bossart if (!pdi) { 936c46302ecSVinod Koul ret = -EINVAL; 93757a34790SPierre-Louis Bossart goto error; 938c46302ecSVinod Koul } 93957a34790SPierre-Louis Bossart 94057a34790SPierre-Louis Bossart /* do run-time configurations for SHIM, ALH and PDI/PORT */ 94157a34790SPierre-Louis Bossart intel_pdi_shim_configure(sdw, pdi); 94257a34790SPierre-Louis Bossart intel_pdi_alh_configure(sdw, pdi); 94357a34790SPierre-Louis Bossart sdw_cdns_config_stream(cdns, ch, dir, pdi); 94457a34790SPierre-Louis Bossart 945a5a0239cSBard Liao /* store pdi and hw_params, may be needed in prepare step */ 946a5a0239cSBard Liao dma->suspended = false; 947a5a0239cSBard Liao dma->pdi = pdi; 948a5a0239cSBard Liao dma->hw_params = params; 949c46302ecSVinod Koul 950c46302ecSVinod Koul /* Inform DSP about PDI stream number */ 9514b206d34SRander Wang ret = intel_params_stream(sdw, substream, dai, params, 9524b206d34SRander Wang sdw->instance, 95357a34790SPierre-Louis Bossart pdi->intel_alh_id); 954c46302ecSVinod Koul if (ret) 95557a34790SPierre-Louis Bossart goto error; 956c46302ecSVinod Koul 957c46302ecSVinod Koul sconfig.direction = dir; 958c46302ecSVinod Koul sconfig.ch_count = ch; 959c46302ecSVinod Koul sconfig.frame_rate = params_rate(params); 960c46302ecSVinod Koul sconfig.type = dma->stream_type; 961c46302ecSVinod Koul 962c46302ecSVinod Koul if (dma->stream_type == SDW_STREAM_PDM) { 963c46302ecSVinod Koul sconfig.frame_rate *= 50; 964c46302ecSVinod Koul sconfig.bps = 1; 965c46302ecSVinod Koul } else { 966c46302ecSVinod Koul sconfig.bps = snd_pcm_format_width(params_format(params)); 967c46302ecSVinod Koul } 968c46302ecSVinod Koul 969c46302ecSVinod Koul /* Port configuration */ 97057a34790SPierre-Louis Bossart pconfig = kcalloc(1, sizeof(*pconfig), GFP_KERNEL); 971c46302ecSVinod Koul if (!pconfig) { 972c46302ecSVinod Koul ret = -ENOMEM; 97357a34790SPierre-Louis Bossart goto error; 974c46302ecSVinod Koul } 975c46302ecSVinod Koul 97657a34790SPierre-Louis Bossart pconfig->num = pdi->num; 97757a34790SPierre-Louis Bossart pconfig->ch_mask = (1 << ch) - 1; 978c46302ecSVinod Koul 979c46302ecSVinod Koul ret = sdw_stream_add_master(&cdns->bus, &sconfig, 98057a34790SPierre-Louis Bossart pconfig, 1, dma->stream); 98157a34790SPierre-Louis Bossart if (ret) 98217ed5befSPierre-Louis Bossart dev_err(cdns->dev, "add master to stream failed:%d\n", ret); 983c46302ecSVinod Koul 984c46302ecSVinod Koul kfree(pconfig); 98557a34790SPierre-Louis Bossart error: 986c46302ecSVinod Koul return ret; 987c46302ecSVinod Koul } 988c46302ecSVinod Koul 98927b198f4SRander Wang static int intel_prepare(struct snd_pcm_substream *substream, 99027b198f4SRander Wang struct snd_soc_dai *dai) 99127b198f4SRander Wang { 992a5a0239cSBard Liao struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 993a5a0239cSBard Liao struct sdw_intel *sdw = cdns_to_intel(cdns); 99427b198f4SRander Wang struct sdw_cdns_dma_data *dma; 995a5a0239cSBard Liao int ch, dir; 996244eb888SPierre-Louis Bossart int ret = 0; 99727b198f4SRander Wang 99827b198f4SRander Wang dma = snd_soc_dai_get_dma_data(dai, substream); 99927b198f4SRander Wang if (!dma) { 100027b198f4SRander Wang dev_err(dai->dev, "failed to get dma data in %s", 100127b198f4SRander Wang __func__); 100227b198f4SRander Wang return -EIO; 100327b198f4SRander Wang } 100427b198f4SRander Wang 1005a5a0239cSBard Liao if (dma->suspended) { 1006a5a0239cSBard Liao dma->suspended = false; 1007a5a0239cSBard Liao 1008a5a0239cSBard Liao /* 1009a5a0239cSBard Liao * .prepare() is called after system resume, where we 1010a5a0239cSBard Liao * need to reinitialize the SHIM/ALH/Cadence IP. 1011a5a0239cSBard Liao * .prepare() is also called to deal with underflows, 1012a5a0239cSBard Liao * but in those cases we cannot touch ALH/SHIM 1013a5a0239cSBard Liao * registers 1014a5a0239cSBard Liao */ 1015a5a0239cSBard Liao 1016a5a0239cSBard Liao /* configure stream */ 1017a5a0239cSBard Liao ch = params_channels(dma->hw_params); 1018a5a0239cSBard Liao if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 1019a5a0239cSBard Liao dir = SDW_DATA_DIR_RX; 1020a5a0239cSBard Liao else 1021a5a0239cSBard Liao dir = SDW_DATA_DIR_TX; 1022a5a0239cSBard Liao 1023a5a0239cSBard Liao intel_pdi_shim_configure(sdw, dma->pdi); 1024a5a0239cSBard Liao intel_pdi_alh_configure(sdw, dma->pdi); 1025a5a0239cSBard Liao sdw_cdns_config_stream(cdns, ch, dir, dma->pdi); 1026a5a0239cSBard Liao 1027a5a0239cSBard Liao /* Inform DSP about PDI stream number */ 1028a5a0239cSBard Liao ret = intel_params_stream(sdw, substream, dai, 1029a5a0239cSBard Liao dma->hw_params, 1030a5a0239cSBard Liao sdw->instance, 1031a5a0239cSBard Liao dma->pdi->intel_alh_id); 1032a5a0239cSBard Liao } 1033a5a0239cSBard Liao 1034a5a0239cSBard Liao return ret; 103527b198f4SRander Wang } 103627b198f4SRander Wang 1037c46302ecSVinod Koul static int 1038c46302ecSVinod Koul intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 1039c46302ecSVinod Koul { 1040c46302ecSVinod Koul struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 1041eff346f2SPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 1042c46302ecSVinod Koul struct sdw_cdns_dma_data *dma; 1043c46302ecSVinod Koul int ret; 1044c46302ecSVinod Koul 1045c46302ecSVinod Koul dma = snd_soc_dai_get_dma_data(dai, substream); 1046c46302ecSVinod Koul if (!dma) 1047c46302ecSVinod Koul return -EIO; 1048c46302ecSVinod Koul 1049244eb888SPierre-Louis Bossart /* 1050244eb888SPierre-Louis Bossart * The sdw stream state will transition to RELEASED when stream-> 1051244eb888SPierre-Louis Bossart * master_list is empty. So the stream state will transition to 1052244eb888SPierre-Louis Bossart * DEPREPARED for the first cpu-dai and to RELEASED for the last 1053244eb888SPierre-Louis Bossart * cpu-dai. 1054244eb888SPierre-Louis Bossart */ 1055c46302ecSVinod Koul ret = sdw_stream_remove_master(&cdns->bus, dma->stream); 1056eff346f2SPierre-Louis Bossart if (ret < 0) { 105717ed5befSPierre-Louis Bossart dev_err(dai->dev, "remove master from stream %s failed: %d\n", 1058c46302ecSVinod Koul dma->stream->name, ret); 1059c46302ecSVinod Koul return ret; 1060c46302ecSVinod Koul } 1061c46302ecSVinod Koul 1062eff346f2SPierre-Louis Bossart ret = intel_free_stream(sdw, substream, dai, sdw->instance); 1063eff346f2SPierre-Louis Bossart if (ret < 0) { 1064eff346f2SPierre-Louis Bossart dev_err(dai->dev, "intel_free_stream: failed %d", ret); 1065eff346f2SPierre-Louis Bossart return ret; 1066eff346f2SPierre-Louis Bossart } 1067eff346f2SPierre-Louis Bossart 1068a5a0239cSBard Liao dma->hw_params = NULL; 1069a5a0239cSBard Liao dma->pdi = NULL; 1070a5a0239cSBard Liao 1071eff346f2SPierre-Louis Bossart return 0; 1072eff346f2SPierre-Louis Bossart } 1073eff346f2SPierre-Louis Bossart 1074183c7687SPierre-Louis Bossart static void intel_shutdown(struct snd_pcm_substream *substream, 1075183c7687SPierre-Louis Bossart struct snd_soc_dai *dai) 1076183c7687SPierre-Louis Bossart { 1077ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 1078183c7687SPierre-Louis Bossart 1079ebf878edSPierre-Louis Bossart pm_runtime_mark_last_busy(cdns->dev); 1080ebf878edSPierre-Louis Bossart pm_runtime_put_autosuspend(cdns->dev); 1081183c7687SPierre-Louis Bossart } 1082183c7687SPierre-Louis Bossart 1083a5a0239cSBard Liao static int intel_component_dais_suspend(struct snd_soc_component *component) 1084a5a0239cSBard Liao { 1085a5a0239cSBard Liao struct sdw_cdns_dma_data *dma; 1086a5a0239cSBard Liao struct snd_soc_dai *dai; 1087a5a0239cSBard Liao 1088a5a0239cSBard Liao for_each_component_dais(component, dai) { 1089a5a0239cSBard Liao /* 1090a5a0239cSBard Liao * we don't have a .suspend dai_ops, and we don't have access 1091a5a0239cSBard Liao * to the substream, so let's mark both capture and playback 1092a5a0239cSBard Liao * DMA contexts as suspended 1093a5a0239cSBard Liao */ 1094a5a0239cSBard Liao dma = dai->playback_dma_data; 1095a5a0239cSBard Liao if (dma) 1096a5a0239cSBard Liao dma->suspended = true; 1097a5a0239cSBard Liao 1098a5a0239cSBard Liao dma = dai->capture_dma_data; 1099a5a0239cSBard Liao if (dma) 1100a5a0239cSBard Liao dma->suspended = true; 1101a5a0239cSBard Liao } 1102a5a0239cSBard Liao 1103a5a0239cSBard Liao return 0; 1104a5a0239cSBard Liao } 1105a5a0239cSBard Liao 1106c46302ecSVinod Koul static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, 1107c46302ecSVinod Koul void *stream, int direction) 1108c46302ecSVinod Koul { 1109c46302ecSVinod Koul return cdns_set_sdw_stream(dai, stream, true, direction); 1110c46302ecSVinod Koul } 1111c46302ecSVinod Koul 1112c46302ecSVinod Koul static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, 1113c46302ecSVinod Koul void *stream, int direction) 1114c46302ecSVinod Koul { 1115c46302ecSVinod Koul return cdns_set_sdw_stream(dai, stream, false, direction); 1116c46302ecSVinod Koul } 1117c46302ecSVinod Koul 111809553140SPierre-Louis Bossart static void *intel_get_sdw_stream(struct snd_soc_dai *dai, 111909553140SPierre-Louis Bossart int direction) 112009553140SPierre-Louis Bossart { 112109553140SPierre-Louis Bossart struct sdw_cdns_dma_data *dma; 112209553140SPierre-Louis Bossart 112309553140SPierre-Louis Bossart if (direction == SNDRV_PCM_STREAM_PLAYBACK) 112409553140SPierre-Louis Bossart dma = dai->playback_dma_data; 112509553140SPierre-Louis Bossart else 112609553140SPierre-Louis Bossart dma = dai->capture_dma_data; 112709553140SPierre-Louis Bossart 112809553140SPierre-Louis Bossart if (!dma) 112906dcb4e4SPierre-Louis Bossart return ERR_PTR(-EINVAL); 113009553140SPierre-Louis Bossart 113109553140SPierre-Louis Bossart return dma->stream; 113209553140SPierre-Louis Bossart } 113309553140SPierre-Louis Bossart 1134b1635596SJulia Lawall static const struct snd_soc_dai_ops intel_pcm_dai_ops = { 11355e7484d0SRander Wang .startup = intel_startup, 1136c46302ecSVinod Koul .hw_params = intel_hw_params, 113727b198f4SRander Wang .prepare = intel_prepare, 1138c46302ecSVinod Koul .hw_free = intel_hw_free, 1139183c7687SPierre-Louis Bossart .shutdown = intel_shutdown, 1140c46302ecSVinod Koul .set_sdw_stream = intel_pcm_set_sdw_stream, 114109553140SPierre-Louis Bossart .get_sdw_stream = intel_get_sdw_stream, 1142c46302ecSVinod Koul }; 1143c46302ecSVinod Koul 1144b1635596SJulia Lawall static const struct snd_soc_dai_ops intel_pdm_dai_ops = { 11455e7484d0SRander Wang .startup = intel_startup, 1146c46302ecSVinod Koul .hw_params = intel_hw_params, 114727b198f4SRander Wang .prepare = intel_prepare, 1148c46302ecSVinod Koul .hw_free = intel_hw_free, 1149183c7687SPierre-Louis Bossart .shutdown = intel_shutdown, 1150c46302ecSVinod Koul .set_sdw_stream = intel_pdm_set_sdw_stream, 115109553140SPierre-Louis Bossart .get_sdw_stream = intel_get_sdw_stream, 1152c46302ecSVinod Koul }; 1153c46302ecSVinod Koul 1154c46302ecSVinod Koul static const struct snd_soc_component_driver dai_component = { 1155c46302ecSVinod Koul .name = "soundwire", 1156a5a0239cSBard Liao .suspend = intel_component_dais_suspend 1157c46302ecSVinod Koul }; 1158c46302ecSVinod Koul 1159c46302ecSVinod Koul static int intel_create_dai(struct sdw_cdns *cdns, 1160c46302ecSVinod Koul struct snd_soc_dai_driver *dais, 1161c46302ecSVinod Koul enum intel_pdi_type type, 1162c46302ecSVinod Koul u32 num, u32 off, u32 max_ch, bool pcm) 1163c46302ecSVinod Koul { 1164c46302ecSVinod Koul int i; 1165c46302ecSVinod Koul 1166c46302ecSVinod Koul if (num == 0) 1167c46302ecSVinod Koul return 0; 1168c46302ecSVinod Koul 1169c46302ecSVinod Koul /* TODO: Read supported rates/formats from hardware */ 1170c46302ecSVinod Koul for (i = off; i < (off + num); i++) { 1171bf6d6e68SPierre-Louis Bossart dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL, 1172bf6d6e68SPierre-Louis Bossart "SDW%d Pin%d", 1173c46302ecSVinod Koul cdns->instance, i); 1174c46302ecSVinod Koul if (!dais[i].name) 1175c46302ecSVinod Koul return -ENOMEM; 1176c46302ecSVinod Koul 1177c46302ecSVinod Koul if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { 1178c46302ecSVinod Koul dais[i].playback.channels_min = 1; 1179c46302ecSVinod Koul dais[i].playback.channels_max = max_ch; 1180c46302ecSVinod Koul dais[i].playback.rates = SNDRV_PCM_RATE_48000; 1181c46302ecSVinod Koul dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE; 1182c46302ecSVinod Koul } 1183c46302ecSVinod Koul 1184c46302ecSVinod Koul if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { 118539194128SSrinivas Kandagatla dais[i].capture.channels_min = 1; 118639194128SSrinivas Kandagatla dais[i].capture.channels_max = max_ch; 1187c46302ecSVinod Koul dais[i].capture.rates = SNDRV_PCM_RATE_48000; 1188c46302ecSVinod Koul dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; 1189c46302ecSVinod Koul } 1190c46302ecSVinod Koul 1191c46302ecSVinod Koul if (pcm) 1192c46302ecSVinod Koul dais[i].ops = &intel_pcm_dai_ops; 1193c46302ecSVinod Koul else 1194c46302ecSVinod Koul dais[i].ops = &intel_pdm_dai_ops; 1195c46302ecSVinod Koul } 1196c46302ecSVinod Koul 1197c46302ecSVinod Koul return 0; 1198c46302ecSVinod Koul } 1199c46302ecSVinod Koul 1200c46302ecSVinod Koul static int intel_register_dai(struct sdw_intel *sdw) 1201c46302ecSVinod Koul { 1202c46302ecSVinod Koul struct sdw_cdns *cdns = &sdw->cdns; 1203c46302ecSVinod Koul struct sdw_cdns_streams *stream; 1204c46302ecSVinod Koul struct snd_soc_dai_driver *dais; 1205c46302ecSVinod Koul int num_dai, ret, off = 0; 1206c46302ecSVinod Koul 1207c46302ecSVinod Koul /* DAIs are created based on total number of PDIs supported */ 1208c46302ecSVinod Koul num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi; 1209c46302ecSVinod Koul 1210c46302ecSVinod Koul dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); 1211c46302ecSVinod Koul if (!dais) 1212c46302ecSVinod Koul return -ENOMEM; 1213c46302ecSVinod Koul 1214c46302ecSVinod Koul /* Create PCM DAIs */ 1215c46302ecSVinod Koul stream = &cdns->pcm; 1216c46302ecSVinod Koul 1217cf924962SBard Liao ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in, 12181215daeeSVinod Koul off, stream->num_ch_in, true); 1219c46302ecSVinod Koul if (ret) 1220c46302ecSVinod Koul return ret; 1221c46302ecSVinod Koul 1222c46302ecSVinod Koul off += cdns->pcm.num_in; 12231215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out, 12241215daeeSVinod Koul off, stream->num_ch_out, true); 1225c46302ecSVinod Koul if (ret) 1226c46302ecSVinod Koul return ret; 1227c46302ecSVinod Koul 1228c46302ecSVinod Koul off += cdns->pcm.num_out; 12291215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd, 12301215daeeSVinod Koul off, stream->num_ch_bd, true); 1231c46302ecSVinod Koul if (ret) 1232c46302ecSVinod Koul return ret; 1233c46302ecSVinod Koul 1234c46302ecSVinod Koul /* Create PDM DAIs */ 1235c46302ecSVinod Koul stream = &cdns->pdm; 1236c46302ecSVinod Koul off += cdns->pcm.num_bd; 12371215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pdm.num_in, 12381215daeeSVinod Koul off, stream->num_ch_in, false); 1239c46302ecSVinod Koul if (ret) 1240c46302ecSVinod Koul return ret; 1241c46302ecSVinod Koul 1242c46302ecSVinod Koul off += cdns->pdm.num_in; 12431215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pdm.num_out, 12441215daeeSVinod Koul off, stream->num_ch_out, false); 1245c46302ecSVinod Koul if (ret) 1246c46302ecSVinod Koul return ret; 1247c46302ecSVinod Koul 1248cf924962SBard Liao off += cdns->pdm.num_out; 12491215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pdm.num_bd, 12501215daeeSVinod Koul off, stream->num_ch_bd, false); 1251c46302ecSVinod Koul if (ret) 1252c46302ecSVinod Koul return ret; 1253c46302ecSVinod Koul 1254c46302ecSVinod Koul return snd_soc_register_component(cdns->dev, &dai_component, 1255c46302ecSVinod Koul dais, num_dai); 1256c46302ecSVinod Koul } 1257c46302ecSVinod Koul 1258085f4aceSPierre-Louis Bossart static int sdw_master_read_intel_prop(struct sdw_bus *bus) 1259085f4aceSPierre-Louis Bossart { 1260085f4aceSPierre-Louis Bossart struct sdw_master_prop *prop = &bus->prop; 1261085f4aceSPierre-Louis Bossart struct fwnode_handle *link; 1262085f4aceSPierre-Louis Bossart char name[32]; 1263395713d8SPierre-Louis Bossart u32 quirk_mask; 1264085f4aceSPierre-Louis Bossart 1265085f4aceSPierre-Louis Bossart /* Find master handle */ 1266085f4aceSPierre-Louis Bossart snprintf(name, sizeof(name), 1267085f4aceSPierre-Louis Bossart "mipi-sdw-link-%d-subproperties", bus->link_id); 1268085f4aceSPierre-Louis Bossart 1269085f4aceSPierre-Louis Bossart link = device_get_named_child_node(bus->dev, name); 1270085f4aceSPierre-Louis Bossart if (!link) { 1271085f4aceSPierre-Louis Bossart dev_err(bus->dev, "Master node %s not found\n", name); 1272085f4aceSPierre-Louis Bossart return -EIO; 1273085f4aceSPierre-Louis Bossart } 1274085f4aceSPierre-Louis Bossart 1275085f4aceSPierre-Louis Bossart fwnode_property_read_u32(link, 1276085f4aceSPierre-Louis Bossart "intel-sdw-ip-clock", 1277085f4aceSPierre-Louis Bossart &prop->mclk_freq); 1278395713d8SPierre-Louis Bossart 1279a19efb52SBard Liao /* the values reported by BIOS are the 2x clock, not the bus clock */ 1280a19efb52SBard Liao prop->mclk_freq /= 2; 1281a19efb52SBard Liao 1282395713d8SPierre-Louis Bossart fwnode_property_read_u32(link, 1283395713d8SPierre-Louis Bossart "intel-quirk-mask", 1284395713d8SPierre-Louis Bossart &quirk_mask); 1285395713d8SPierre-Louis Bossart 1286395713d8SPierre-Louis Bossart if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) 1287395713d8SPierre-Louis Bossart prop->hw_disabled = true; 1288395713d8SPierre-Louis Bossart 1289085f4aceSPierre-Louis Bossart return 0; 1290085f4aceSPierre-Louis Bossart } 1291085f4aceSPierre-Louis Bossart 129271bb8a1bSVinod Koul static int intel_prop_read(struct sdw_bus *bus) 129371bb8a1bSVinod Koul { 129471bb8a1bSVinod Koul /* Initialize with default handler to read all DisCo properties */ 129571bb8a1bSVinod Koul sdw_master_read_prop(bus); 129671bb8a1bSVinod Koul 1297085f4aceSPierre-Louis Bossart /* read Intel-specific properties */ 1298085f4aceSPierre-Louis Bossart sdw_master_read_intel_prop(bus); 1299085f4aceSPierre-Louis Bossart 130071bb8a1bSVinod Koul return 0; 130171bb8a1bSVinod Koul } 130271bb8a1bSVinod Koul 1303c91605f4SShreyas NC static struct sdw_master_ops sdw_intel_ops = { 1304c91605f4SShreyas NC .read_prop = sdw_master_read_prop, 1305c91605f4SShreyas NC .xfer_msg = cdns_xfer_msg, 1306c91605f4SShreyas NC .xfer_msg_defer = cdns_xfer_msg_defer, 1307c91605f4SShreyas NC .reset_page_addr = cdns_reset_page_addr, 130807abeff1SVinod Koul .set_bus_conf = cdns_bus_conf, 130930246e2dSShreyas NC .pre_bank_switch = intel_pre_bank_switch, 131030246e2dSShreyas NC .post_bank_switch = intel_post_bank_switch, 1311c91605f4SShreyas NC }; 1312c91605f4SShreyas NC 1313dfbe642dSPierre-Louis Bossart static int intel_init(struct sdw_intel *sdw) 1314dfbe642dSPierre-Louis Bossart { 13154a17c441SPierre-Louis Bossart bool clock_stop; 13164a17c441SPierre-Louis Bossart 1317dfbe642dSPierre-Louis Bossart /* Initialize shim and controller */ 1318dfbe642dSPierre-Louis Bossart intel_link_power_up(sdw); 13194a17c441SPierre-Louis Bossart 13204a17c441SPierre-Louis Bossart clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns); 13214a17c441SPierre-Louis Bossart 13224a17c441SPierre-Louis Bossart intel_shim_init(sdw, clock_stop); 13234a17c441SPierre-Louis Bossart 13244a17c441SPierre-Louis Bossart return 0; 1325dfbe642dSPierre-Louis Bossart } 1326dfbe642dSPierre-Louis Bossart 132771bb8a1bSVinod Koul /* 132871bb8a1bSVinod Koul * probe and init 132971bb8a1bSVinod Koul */ 1330b6109dd6SPierre-Louis Bossart static int intel_master_probe(struct platform_device *pdev) 133171bb8a1bSVinod Koul { 1332b6109dd6SPierre-Louis Bossart struct device *dev = &pdev->dev; 133371bb8a1bSVinod Koul struct sdw_intel *sdw; 133483e129afSPierre-Louis Bossart struct sdw_cdns *cdns; 1335b6109dd6SPierre-Louis Bossart struct sdw_bus *bus; 133671bb8a1bSVinod Koul int ret; 133771bb8a1bSVinod Koul 1338b6109dd6SPierre-Louis Bossart sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL); 133971bb8a1bSVinod Koul if (!sdw) 134071bb8a1bSVinod Koul return -ENOMEM; 134171bb8a1bSVinod Koul 134283e129afSPierre-Louis Bossart cdns = &sdw->cdns; 134383e129afSPierre-Louis Bossart bus = &cdns->bus; 134471bb8a1bSVinod Koul 134571bb8a1bSVinod Koul sdw->instance = pdev->id; 1346b6109dd6SPierre-Louis Bossart sdw->link_res = dev_get_platdata(dev); 134783e129afSPierre-Louis Bossart cdns->dev = dev; 134883e129afSPierre-Louis Bossart cdns->registers = sdw->link_res->registers; 134983e129afSPierre-Louis Bossart cdns->instance = sdw->instance; 135083e129afSPierre-Louis Bossart cdns->msg_count = 0; 135183e129afSPierre-Louis Bossart 1352b6109dd6SPierre-Louis Bossart bus->link_id = pdev->id; 135371bb8a1bSVinod Koul 135483e129afSPierre-Louis Bossart sdw_cdns_probe(cdns); 135571bb8a1bSVinod Koul 135671bb8a1bSVinod Koul /* Set property read ops */ 1357c91605f4SShreyas NC sdw_intel_ops.read_prop = intel_prop_read; 1358b6109dd6SPierre-Louis Bossart bus->ops = &sdw_intel_ops; 135971bb8a1bSVinod Koul 1360b6109dd6SPierre-Louis Bossart /* set driver data, accessed by snd_soc_dai_get_drvdata() */ 136183e129afSPierre-Louis Bossart dev_set_drvdata(dev, cdns); 136271bb8a1bSVinod Koul 13639026118fSBard Liao /* use generic bandwidth allocation algorithm */ 13649026118fSBard Liao sdw->cdns.bus.compute_params = sdw_compute_params; 13659026118fSBard Liao 1366b6109dd6SPierre-Louis Bossart ret = sdw_bus_master_add(bus, dev, dev->fwnode); 136771bb8a1bSVinod Koul if (ret) { 1368b6109dd6SPierre-Louis Bossart dev_err(dev, "sdw_bus_master_add fail: %d\n", ret); 13699e3d47fbSPierre-Louis Bossart return ret; 137071bb8a1bSVinod Koul } 137171bb8a1bSVinod Koul 13726d2c6669SPierre-Louis Bossart if (bus->prop.hw_disabled) 1373b6109dd6SPierre-Louis Bossart dev_info(dev, 1374b6109dd6SPierre-Louis Bossart "SoundWire master %d is disabled, will be ignored\n", 1375b6109dd6SPierre-Louis Bossart bus->link_id); 13760ef2986eSPierre-Louis Bossart /* 13770ef2986eSPierre-Louis Bossart * Ignore BIOS err_threshold, it's a really bad idea when dealing 13780ef2986eSPierre-Louis Bossart * with multiple hardware synchronized links 13790ef2986eSPierre-Louis Bossart */ 13800ef2986eSPierre-Louis Bossart bus->prop.err_threshold = 0; 13816d2c6669SPierre-Louis Bossart 13826d2c6669SPierre-Louis Bossart return 0; 13836d2c6669SPierre-Louis Bossart } 13846d2c6669SPierre-Louis Bossart 13856d2c6669SPierre-Louis Bossart int intel_master_startup(struct platform_device *pdev) 13866d2c6669SPierre-Louis Bossart { 13876d2c6669SPierre-Louis Bossart struct sdw_cdns_stream_config config; 13886d2c6669SPierre-Louis Bossart struct device *dev = &pdev->dev; 13896d2c6669SPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 13906d2c6669SPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 13916d2c6669SPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1392ebf878edSPierre-Louis Bossart int link_flags; 1393857a7c42SPierre-Louis Bossart bool multi_link; 1394caf68819SPierre-Louis Bossart u32 clock_stop_quirks; 13956d2c6669SPierre-Louis Bossart int ret; 13966d2c6669SPierre-Louis Bossart 13976d2c6669SPierre-Louis Bossart if (bus->prop.hw_disabled) { 13986d2c6669SPierre-Louis Bossart dev_info(dev, 13996d2c6669SPierre-Louis Bossart "SoundWire master %d is disabled, ignoring\n", 14006d2c6669SPierre-Louis Bossart sdw->instance); 1401395713d8SPierre-Louis Bossart return 0; 1402395713d8SPierre-Louis Bossart } 1403395713d8SPierre-Louis Bossart 1404857a7c42SPierre-Louis Bossart link_flags = md_flags >> (bus->link_id * 8); 1405857a7c42SPierre-Louis Bossart multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); 1406857a7c42SPierre-Louis Bossart if (!multi_link) { 1407857a7c42SPierre-Louis Bossart dev_dbg(dev, "Multi-link is disabled\n"); 1408857a7c42SPierre-Louis Bossart bus->multi_link = false; 1409857a7c42SPierre-Louis Bossart } else { 141094eed661SPierre-Louis Bossart /* 141194eed661SPierre-Louis Bossart * hardware-based synchronization is required regardless 141294eed661SPierre-Louis Bossart * of the number of segments used by a stream: SSP-based 141394eed661SPierre-Louis Bossart * synchronization is gated by gsync when the multi-master 141494eed661SPierre-Louis Bossart * mode is set. 141594eed661SPierre-Louis Bossart */ 1416857a7c42SPierre-Louis Bossart bus->multi_link = true; 141794eed661SPierre-Louis Bossart bus->hw_sync_min_links = 1; 1418857a7c42SPierre-Louis Bossart } 1419857a7c42SPierre-Louis Bossart 1420857a7c42SPierre-Louis Bossart /* Initialize shim, controller */ 1421dfbe642dSPierre-Louis Bossart ret = intel_init(sdw); 142271bb8a1bSVinod Koul if (ret) 142371bb8a1bSVinod Koul goto err_init; 142471bb8a1bSVinod Koul 142537a2d22bSVinod Koul /* Read the PDI config and initialize cadence PDI */ 142637a2d22bSVinod Koul intel_pdi_init(sdw, &config); 142783e129afSPierre-Louis Bossart ret = sdw_cdns_pdi_init(cdns, config); 142871bb8a1bSVinod Koul if (ret) 142971bb8a1bSVinod Koul goto err_init; 143071bb8a1bSVinod Koul 143137a2d22bSVinod Koul intel_pdi_ch_update(sdw); 143237a2d22bSVinod Koul 143383e129afSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 143471bb8a1bSVinod Koul if (ret < 0) { 1435b6109dd6SPierre-Louis Bossart dev_err(dev, "cannot enable interrupts\n"); 143671bb8a1bSVinod Koul goto err_init; 143771bb8a1bSVinod Koul } 143871bb8a1bSVinod Koul 1439857a7c42SPierre-Louis Bossart /* 1440857a7c42SPierre-Louis Bossart * follow recommended programming flows to avoid timeouts when 1441857a7c42SPierre-Louis Bossart * gsync is enabled 1442857a7c42SPierre-Louis Bossart */ 1443857a7c42SPierre-Louis Bossart if (multi_link) 1444857a7c42SPierre-Louis Bossart intel_shim_sync_arm(sdw); 1445857a7c42SPierre-Louis Bossart 1446857a7c42SPierre-Louis Bossart ret = sdw_cdns_init(cdns); 1447857a7c42SPierre-Louis Bossart if (ret < 0) { 1448857a7c42SPierre-Louis Bossart dev_err(dev, "unable to initialize Cadence IP\n"); 1449857a7c42SPierre-Louis Bossart goto err_interrupt; 1450857a7c42SPierre-Louis Bossart } 1451857a7c42SPierre-Louis Bossart 145283e129afSPierre-Louis Bossart ret = sdw_cdns_exit_reset(cdns); 145349ea07d3SPierre-Louis Bossart if (ret < 0) { 1454b6109dd6SPierre-Louis Bossart dev_err(dev, "unable to exit bus reset sequence\n"); 14559e3d47fbSPierre-Louis Bossart goto err_interrupt; 145649ea07d3SPierre-Louis Bossart } 145749ea07d3SPierre-Louis Bossart 1458857a7c42SPierre-Louis Bossart if (multi_link) { 1459857a7c42SPierre-Louis Bossart ret = intel_shim_sync_go(sdw); 1460857a7c42SPierre-Louis Bossart if (ret < 0) { 1461857a7c42SPierre-Louis Bossart dev_err(dev, "sync go failed: %d\n", ret); 1462857a7c42SPierre-Louis Bossart goto err_interrupt; 1463857a7c42SPierre-Louis Bossart } 1464857a7c42SPierre-Louis Bossart } 1465857a7c42SPierre-Louis Bossart 1466c46302ecSVinod Koul /* Register DAIs */ 1467c46302ecSVinod Koul ret = intel_register_dai(sdw); 1468c46302ecSVinod Koul if (ret) { 1469b6109dd6SPierre-Louis Bossart dev_err(dev, "DAI registration failed: %d\n", ret); 1470b6109dd6SPierre-Louis Bossart snd_soc_unregister_component(dev); 14719e3d47fbSPierre-Louis Bossart goto err_interrupt; 1472c46302ecSVinod Koul } 1473c46302ecSVinod Koul 147479ee6631SPierre-Louis Bossart intel_debugfs_init(sdw); 147579ee6631SPierre-Louis Bossart 1476ebf878edSPierre-Louis Bossart /* Enable runtime PM */ 1477ebf878edSPierre-Louis Bossart if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) { 1478ebf878edSPierre-Louis Bossart pm_runtime_set_autosuspend_delay(dev, 1479ebf878edSPierre-Louis Bossart INTEL_MASTER_SUSPEND_DELAY_MS); 1480ebf878edSPierre-Louis Bossart pm_runtime_use_autosuspend(dev); 1481ebf878edSPierre-Louis Bossart pm_runtime_mark_last_busy(dev); 1482ebf878edSPierre-Louis Bossart 1483ebf878edSPierre-Louis Bossart pm_runtime_set_active(dev); 1484ebf878edSPierre-Louis Bossart pm_runtime_enable(dev); 1485ebf878edSPierre-Louis Bossart } 1486ebf878edSPierre-Louis Bossart 1487caf68819SPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1488caf68819SPierre-Louis Bossart if (clock_stop_quirks & SDW_INTEL_CLK_STOP_NOT_ALLOWED) { 1489caf68819SPierre-Louis Bossart /* 1490caf68819SPierre-Louis Bossart * To keep the clock running we need to prevent 1491caf68819SPierre-Louis Bossart * pm_runtime suspend from happening by increasing the 1492caf68819SPierre-Louis Bossart * reference count. 1493caf68819SPierre-Louis Bossart * This quirk is specified by the parent PCI device in 1494caf68819SPierre-Louis Bossart * case of specific latency requirements. It will have 1495caf68819SPierre-Louis Bossart * no effect if pm_runtime is disabled by the user via 1496caf68819SPierre-Louis Bossart * a module parameter for testing purposes. 1497caf68819SPierre-Louis Bossart */ 1498caf68819SPierre-Louis Bossart pm_runtime_get_noresume(dev); 1499caf68819SPierre-Louis Bossart } 1500caf68819SPierre-Louis Bossart 1501a2d9c161SPierre-Louis Bossart /* 1502a2d9c161SPierre-Louis Bossart * The runtime PM status of Slave devices is "Unsupported" 1503a2d9c161SPierre-Louis Bossart * until they report as ATTACHED. If they don't, e.g. because 1504a2d9c161SPierre-Louis Bossart * there are no Slave devices populated or if the power-on is 1505a2d9c161SPierre-Louis Bossart * delayed or dependent on a power switch, the Master will 1506a2d9c161SPierre-Louis Bossart * remain active and prevent its parent from suspending. 1507a2d9c161SPierre-Louis Bossart * 1508a2d9c161SPierre-Louis Bossart * Conditionally force the pm_runtime core to re-evaluate the 1509a2d9c161SPierre-Louis Bossart * Master status in the absence of any Slave activity. A quirk 1510a2d9c161SPierre-Louis Bossart * is provided to e.g. deal with Slaves that may be powered on 1511a2d9c161SPierre-Louis Bossart * with a delay. A more complete solution would require the 1512a2d9c161SPierre-Louis Bossart * definition of Master properties. 1513a2d9c161SPierre-Louis Bossart */ 1514a2d9c161SPierre-Louis Bossart if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) 1515a2d9c161SPierre-Louis Bossart pm_runtime_idle(dev); 1516a2d9c161SPierre-Louis Bossart 151771bb8a1bSVinod Koul return 0; 151871bb8a1bSVinod Koul 15199e3d47fbSPierre-Louis Bossart err_interrupt: 152083e129afSPierre-Louis Bossart sdw_cdns_enable_interrupt(cdns, false); 152171bb8a1bSVinod Koul err_init: 152271bb8a1bSVinod Koul return ret; 152371bb8a1bSVinod Koul } 152471bb8a1bSVinod Koul 1525b6109dd6SPierre-Louis Bossart static int intel_master_remove(struct platform_device *pdev) 152671bb8a1bSVinod Koul { 1527b6109dd6SPierre-Louis Bossart struct device *dev = &pdev->dev; 152883e129afSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 152983e129afSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 153083e129afSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1531b6109dd6SPierre-Louis Bossart 1532caf68819SPierre-Louis Bossart /* 1533caf68819SPierre-Louis Bossart * Since pm_runtime is already disabled, we don't decrease 1534caf68819SPierre-Louis Bossart * the refcount when the clock_stop_quirk is 1535caf68819SPierre-Louis Bossart * SDW_INTEL_CLK_STOP_NOT_ALLOWED 1536caf68819SPierre-Louis Bossart */ 1537b6109dd6SPierre-Louis Bossart if (!bus->prop.hw_disabled) { 153879ee6631SPierre-Louis Bossart intel_debugfs_exit(sdw); 153983e129afSPierre-Louis Bossart sdw_cdns_enable_interrupt(cdns, false); 1540b6109dd6SPierre-Louis Bossart snd_soc_unregister_component(dev); 1541395713d8SPierre-Louis Bossart } 1542b6109dd6SPierre-Louis Bossart sdw_bus_master_delete(bus); 154371bb8a1bSVinod Koul 154471bb8a1bSVinod Koul return 0; 154571bb8a1bSVinod Koul } 154671bb8a1bSVinod Koul 1547ab2c9132SRander Wang int intel_master_process_wakeen_event(struct platform_device *pdev) 1548ab2c9132SRander Wang { 1549ab2c9132SRander Wang struct device *dev = &pdev->dev; 155071bb8a1bSVinod Koul struct sdw_intel *sdw; 1551ab2c9132SRander Wang struct sdw_bus *bus; 1552ab2c9132SRander Wang void __iomem *shim; 1553ab2c9132SRander Wang u16 wake_sts; 155471bb8a1bSVinod Koul 155571bb8a1bSVinod Koul sdw = platform_get_drvdata(pdev); 1556ab2c9132SRander Wang bus = &sdw->cdns.bus; 155771bb8a1bSVinod Koul 1558ab2c9132SRander Wang if (bus->prop.hw_disabled) { 1559ab2c9132SRander Wang dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", bus->link_id); 1560ab2c9132SRander Wang return 0; 156171bb8a1bSVinod Koul } 1562ab2c9132SRander Wang 1563ab2c9132SRander Wang shim = sdw->link_res->shim; 1564ab2c9132SRander Wang wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); 1565ab2c9132SRander Wang 1566ab2c9132SRander Wang if (!(wake_sts & BIT(sdw->instance))) 1567ab2c9132SRander Wang return 0; 1568ab2c9132SRander Wang 1569ab2c9132SRander Wang /* disable WAKEEN interrupt ASAP to prevent interrupt flood */ 1570ab2c9132SRander Wang intel_shim_wake(sdw, false); 1571ab2c9132SRander Wang 1572ab2c9132SRander Wang /* 1573ab2c9132SRander Wang * resume the Master, which will generate a bus reset and result in 1574ab2c9132SRander Wang * Slaves re-attaching and be re-enumerated. The SoundWire physical 1575ab2c9132SRander Wang * device which generated the wake will trigger an interrupt, which 1576ab2c9132SRander Wang * will in turn cause the corresponding Linux Slave device to be 1577ab2c9132SRander Wang * resumed and the Slave codec driver to check the status. 1578ab2c9132SRander Wang */ 1579ab2c9132SRander Wang pm_request_resume(dev); 158071bb8a1bSVinod Koul 158171bb8a1bSVinod Koul return 0; 158271bb8a1bSVinod Koul } 158371bb8a1bSVinod Koul 15849b3b4b3fSPierre-Louis Bossart /* 15859b3b4b3fSPierre-Louis Bossart * PM calls 15869b3b4b3fSPierre-Louis Bossart */ 15879b3b4b3fSPierre-Louis Bossart 1588f046b233SBard Liao static int __maybe_unused intel_suspend(struct device *dev) 15899b3b4b3fSPierre-Louis Bossart { 15909b3b4b3fSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 15919b3b4b3fSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 15929b3b4b3fSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1593e4be9facSPierre-Louis Bossart u32 clock_stop_quirks; 15949b3b4b3fSPierre-Louis Bossart int ret; 15959b3b4b3fSPierre-Louis Bossart 15969b3b4b3fSPierre-Louis Bossart if (bus->prop.hw_disabled) { 15979b3b4b3fSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", 15989b3b4b3fSPierre-Louis Bossart bus->link_id); 15999b3b4b3fSPierre-Louis Bossart return 0; 16009b3b4b3fSPierre-Louis Bossart } 16019b3b4b3fSPierre-Louis Bossart 1602b61b8b37SPierre-Louis Bossart if (pm_runtime_suspended(dev)) { 1603b61b8b37SPierre-Louis Bossart dev_dbg(dev, "%s: pm_runtime status: suspended\n", __func__); 1604b61b8b37SPierre-Louis Bossart 1605e4be9facSPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1606e4be9facSPierre-Louis Bossart 1607e4be9facSPierre-Louis Bossart if ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || 1608e4be9facSPierre-Louis Bossart !clock_stop_quirks) && 1609e4be9facSPierre-Louis Bossart !pm_runtime_suspended(dev->parent)) { 1610e4be9facSPierre-Louis Bossart 1611e4be9facSPierre-Louis Bossart /* 1612e4be9facSPierre-Louis Bossart * if we've enabled clock stop, and the parent 1613e4be9facSPierre-Louis Bossart * is still active, disable shim wake. The 1614e4be9facSPierre-Louis Bossart * SHIM registers are not accessible if the 1615e4be9facSPierre-Louis Bossart * parent is already pm_runtime suspended so 1616e4be9facSPierre-Louis Bossart * it's too late to change that configuration 1617e4be9facSPierre-Louis Bossart */ 1618e4be9facSPierre-Louis Bossart 1619e4be9facSPierre-Louis Bossart intel_shim_wake(sdw, false); 1620e4be9facSPierre-Louis Bossart } 1621e4be9facSPierre-Louis Bossart 1622b61b8b37SPierre-Louis Bossart return 0; 1623b61b8b37SPierre-Louis Bossart } 1624b61b8b37SPierre-Louis Bossart 16259b3b4b3fSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, false); 16269b3b4b3fSPierre-Louis Bossart if (ret < 0) { 16279b3b4b3fSPierre-Louis Bossart dev_err(dev, "cannot disable interrupts on suspend\n"); 16289b3b4b3fSPierre-Louis Bossart return ret; 16299b3b4b3fSPierre-Louis Bossart } 16309b3b4b3fSPierre-Louis Bossart 16319b3b4b3fSPierre-Louis Bossart ret = intel_link_power_down(sdw); 16329b3b4b3fSPierre-Louis Bossart if (ret) { 16339b3b4b3fSPierre-Louis Bossart dev_err(dev, "Link power down failed: %d", ret); 16349b3b4b3fSPierre-Louis Bossart return ret; 16359b3b4b3fSPierre-Louis Bossart } 16369b3b4b3fSPierre-Louis Bossart 16379b3b4b3fSPierre-Louis Bossart intel_shim_wake(sdw, false); 16389b3b4b3fSPierre-Louis Bossart 16399b3b4b3fSPierre-Louis Bossart return 0; 16409b3b4b3fSPierre-Louis Bossart } 16419b3b4b3fSPierre-Louis Bossart 1642*17e0da0bSArnd Bergmann static int __maybe_unused intel_suspend_runtime(struct device *dev) 1643ebf878edSPierre-Louis Bossart { 1644ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 1645ebf878edSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 1646ebf878edSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1647a320f41eSPierre-Louis Bossart u32 clock_stop_quirks; 1648ebf878edSPierre-Louis Bossart int ret; 1649ebf878edSPierre-Louis Bossart 1650ebf878edSPierre-Louis Bossart if (bus->prop.hw_disabled) { 1651ebf878edSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", 1652ebf878edSPierre-Louis Bossart bus->link_id); 1653ebf878edSPierre-Louis Bossart return 0; 1654ebf878edSPierre-Louis Bossart } 1655ebf878edSPierre-Louis Bossart 1656a320f41eSPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1657a320f41eSPierre-Louis Bossart 1658a320f41eSPierre-Louis Bossart if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { 1659a320f41eSPierre-Louis Bossart 1660ebf878edSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, false); 1661ebf878edSPierre-Louis Bossart if (ret < 0) { 1662ebf878edSPierre-Louis Bossart dev_err(dev, "cannot disable interrupts on suspend\n"); 1663ebf878edSPierre-Louis Bossart return ret; 1664ebf878edSPierre-Louis Bossart } 1665ebf878edSPierre-Louis Bossart 1666ebf878edSPierre-Louis Bossart ret = intel_link_power_down(sdw); 1667ebf878edSPierre-Louis Bossart if (ret) { 1668ebf878edSPierre-Louis Bossart dev_err(dev, "Link power down failed: %d", ret); 1669ebf878edSPierre-Louis Bossart return ret; 1670ebf878edSPierre-Louis Bossart } 1671ebf878edSPierre-Louis Bossart 1672ebf878edSPierre-Louis Bossart intel_shim_wake(sdw, false); 1673ebf878edSPierre-Louis Bossart 167461fb830bSPierre-Louis Bossart } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || 167561fb830bSPierre-Louis Bossart !clock_stop_quirks) { 16766626a616SRander Wang ret = sdw_cdns_clock_stop(cdns, true); 16776626a616SRander Wang if (ret < 0) { 16786626a616SRander Wang dev_err(dev, "cannot enable clock stop on suspend\n"); 16796626a616SRander Wang return ret; 16806626a616SRander Wang } 16816626a616SRander Wang 16826626a616SRander Wang ret = sdw_cdns_enable_interrupt(cdns, false); 16836626a616SRander Wang if (ret < 0) { 16846626a616SRander Wang dev_err(dev, "cannot disable interrupts on suspend\n"); 16856626a616SRander Wang return ret; 16866626a616SRander Wang } 16876626a616SRander Wang 16886626a616SRander Wang ret = intel_link_power_down(sdw); 16896626a616SRander Wang if (ret) { 16906626a616SRander Wang dev_err(dev, "Link power down failed: %d", ret); 16916626a616SRander Wang return ret; 16926626a616SRander Wang } 16936626a616SRander Wang 16946626a616SRander Wang intel_shim_wake(sdw, true); 1695a320f41eSPierre-Louis Bossart } else { 1696a320f41eSPierre-Louis Bossart dev_err(dev, "%s clock_stop_quirks %x unsupported\n", 1697a320f41eSPierre-Louis Bossart __func__, clock_stop_quirks); 1698a320f41eSPierre-Louis Bossart ret = -EINVAL; 1699a320f41eSPierre-Louis Bossart } 1700a320f41eSPierre-Louis Bossart 1701a320f41eSPierre-Louis Bossart return ret; 1702ebf878edSPierre-Louis Bossart } 1703ebf878edSPierre-Louis Bossart 1704f046b233SBard Liao static int __maybe_unused intel_resume(struct device *dev) 17059b3b4b3fSPierre-Louis Bossart { 17069b3b4b3fSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 17079b3b4b3fSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 17089b3b4b3fSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1709a2d9c161SPierre-Louis Bossart int link_flags; 1710857a7c42SPierre-Louis Bossart bool multi_link; 17119b3b4b3fSPierre-Louis Bossart int ret; 17129b3b4b3fSPierre-Louis Bossart 17139b3b4b3fSPierre-Louis Bossart if (bus->prop.hw_disabled) { 17149b3b4b3fSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", 17159b3b4b3fSPierre-Louis Bossart bus->link_id); 17169b3b4b3fSPierre-Louis Bossart return 0; 17179b3b4b3fSPierre-Louis Bossart } 17189b3b4b3fSPierre-Louis Bossart 1719857a7c42SPierre-Louis Bossart link_flags = md_flags >> (bus->link_id * 8); 1720857a7c42SPierre-Louis Bossart multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); 1721857a7c42SPierre-Louis Bossart 1722b61b8b37SPierre-Louis Bossart if (pm_runtime_suspended(dev)) { 1723b61b8b37SPierre-Louis Bossart dev_dbg(dev, "%s: pm_runtime status was suspended, forcing active\n", __func__); 1724b61b8b37SPierre-Louis Bossart 1725b61b8b37SPierre-Louis Bossart /* follow required sequence from runtime_pm.rst */ 1726b61b8b37SPierre-Louis Bossart pm_runtime_disable(dev); 1727b61b8b37SPierre-Louis Bossart pm_runtime_set_active(dev); 1728b61b8b37SPierre-Louis Bossart pm_runtime_mark_last_busy(dev); 1729b61b8b37SPierre-Louis Bossart pm_runtime_enable(dev); 1730a2d9c161SPierre-Louis Bossart 1731a2d9c161SPierre-Louis Bossart link_flags = md_flags >> (bus->link_id * 8); 1732857a7c42SPierre-Louis Bossart 1733a2d9c161SPierre-Louis Bossart if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) 1734a2d9c161SPierre-Louis Bossart pm_runtime_idle(dev); 1735b61b8b37SPierre-Louis Bossart } 1736b61b8b37SPierre-Louis Bossart 17379b3b4b3fSPierre-Louis Bossart ret = intel_init(sdw); 17389b3b4b3fSPierre-Louis Bossart if (ret) { 17399b3b4b3fSPierre-Louis Bossart dev_err(dev, "%s failed: %d", __func__, ret); 17409b3b4b3fSPierre-Louis Bossart return ret; 17419b3b4b3fSPierre-Louis Bossart } 17429b3b4b3fSPierre-Louis Bossart 174399b6a30fSPierre-Louis Bossart /* 174499b6a30fSPierre-Louis Bossart * make sure all Slaves are tagged as UNATTACHED and provide 174599b6a30fSPierre-Louis Bossart * reason for reinitialization 174699b6a30fSPierre-Louis Bossart */ 174799b6a30fSPierre-Louis Bossart sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); 174899b6a30fSPierre-Louis Bossart 17499b3b4b3fSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 17509b3b4b3fSPierre-Louis Bossart if (ret < 0) { 17519b3b4b3fSPierre-Louis Bossart dev_err(dev, "cannot enable interrupts during resume\n"); 17529b3b4b3fSPierre-Louis Bossart return ret; 17539b3b4b3fSPierre-Louis Bossart } 17549b3b4b3fSPierre-Louis Bossart 1755857a7c42SPierre-Louis Bossart /* 1756857a7c42SPierre-Louis Bossart * follow recommended programming flows to avoid timeouts when 1757857a7c42SPierre-Louis Bossart * gsync is enabled 1758857a7c42SPierre-Louis Bossart */ 1759857a7c42SPierre-Louis Bossart if (multi_link) 1760857a7c42SPierre-Louis Bossart intel_shim_sync_arm(sdw); 1761857a7c42SPierre-Louis Bossart 1762857a7c42SPierre-Louis Bossart ret = sdw_cdns_init(&sdw->cdns); 1763857a7c42SPierre-Louis Bossart if (ret < 0) { 1764857a7c42SPierre-Louis Bossart dev_err(dev, "unable to initialize Cadence IP during resume\n"); 1765857a7c42SPierre-Louis Bossart return ret; 1766857a7c42SPierre-Louis Bossart } 1767857a7c42SPierre-Louis Bossart 17689b3b4b3fSPierre-Louis Bossart ret = sdw_cdns_exit_reset(cdns); 17699b3b4b3fSPierre-Louis Bossart if (ret < 0) { 17709b3b4b3fSPierre-Louis Bossart dev_err(dev, "unable to exit bus reset sequence during resume\n"); 17719b3b4b3fSPierre-Louis Bossart return ret; 17729b3b4b3fSPierre-Louis Bossart } 17739b3b4b3fSPierre-Louis Bossart 1774857a7c42SPierre-Louis Bossart if (multi_link) { 1775857a7c42SPierre-Louis Bossart ret = intel_shim_sync_go(sdw); 1776857a7c42SPierre-Louis Bossart if (ret < 0) { 1777857a7c42SPierre-Louis Bossart dev_err(dev, "sync go failed during resume\n"); 1778857a7c42SPierre-Louis Bossart return ret; 1779857a7c42SPierre-Louis Bossart } 1780857a7c42SPierre-Louis Bossart } 1781857a7c42SPierre-Louis Bossart 1782cb1e6d59SPierre-Louis Bossart /* 1783cb1e6d59SPierre-Louis Bossart * after system resume, the pm_runtime suspend() may kick in 1784cb1e6d59SPierre-Louis Bossart * during the enumeration, before any children device force the 1785cb1e6d59SPierre-Louis Bossart * master device to remain active. Using pm_runtime_get() 1786cb1e6d59SPierre-Louis Bossart * routines is not really possible, since it'd prevent the 1787cb1e6d59SPierre-Louis Bossart * master from suspending. 1788cb1e6d59SPierre-Louis Bossart * A reasonable compromise is to update the pm_runtime 1789cb1e6d59SPierre-Louis Bossart * counters and delay the pm_runtime suspend by several 1790cb1e6d59SPierre-Louis Bossart * seconds, by when all enumeration should be complete. 1791cb1e6d59SPierre-Louis Bossart */ 1792cb1e6d59SPierre-Louis Bossart pm_runtime_mark_last_busy(dev); 1793cb1e6d59SPierre-Louis Bossart 17949b3b4b3fSPierre-Louis Bossart return ret; 17959b3b4b3fSPierre-Louis Bossart } 17969b3b4b3fSPierre-Louis Bossart 1797*17e0da0bSArnd Bergmann static int __maybe_unused intel_resume_runtime(struct device *dev) 1798ebf878edSPierre-Louis Bossart { 1799ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 1800ebf878edSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 1801ebf878edSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1802a320f41eSPierre-Louis Bossart u32 clock_stop_quirks; 180308abad9fSRander Wang bool clock_stop0; 1804857a7c42SPierre-Louis Bossart int link_flags; 1805857a7c42SPierre-Louis Bossart bool multi_link; 180608abad9fSRander Wang int status; 1807ebf878edSPierre-Louis Bossart int ret; 1808ebf878edSPierre-Louis Bossart 1809ebf878edSPierre-Louis Bossart if (bus->prop.hw_disabled) { 1810ebf878edSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", 1811ebf878edSPierre-Louis Bossart bus->link_id); 1812ebf878edSPierre-Louis Bossart return 0; 1813ebf878edSPierre-Louis Bossart } 1814ebf878edSPierre-Louis Bossart 1815857a7c42SPierre-Louis Bossart link_flags = md_flags >> (bus->link_id * 8); 1816857a7c42SPierre-Louis Bossart multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); 1817857a7c42SPierre-Louis Bossart 1818a320f41eSPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1819a320f41eSPierre-Louis Bossart 1820a320f41eSPierre-Louis Bossart if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { 1821ebf878edSPierre-Louis Bossart ret = intel_init(sdw); 1822ebf878edSPierre-Louis Bossart if (ret) { 1823ebf878edSPierre-Louis Bossart dev_err(dev, "%s failed: %d", __func__, ret); 1824ebf878edSPierre-Louis Bossart return ret; 1825ebf878edSPierre-Louis Bossart } 1826ebf878edSPierre-Louis Bossart 182799b6a30fSPierre-Louis Bossart /* 182899b6a30fSPierre-Louis Bossart * make sure all Slaves are tagged as UNATTACHED and provide 182999b6a30fSPierre-Louis Bossart * reason for reinitialization 183099b6a30fSPierre-Louis Bossart */ 183199b6a30fSPierre-Louis Bossart sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); 183299b6a30fSPierre-Louis Bossart 1833ebf878edSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 1834ebf878edSPierre-Louis Bossart if (ret < 0) { 1835ebf878edSPierre-Louis Bossart dev_err(dev, "cannot enable interrupts during resume\n"); 1836ebf878edSPierre-Louis Bossart return ret; 1837ebf878edSPierre-Louis Bossart } 1838ebf878edSPierre-Louis Bossart 1839857a7c42SPierre-Louis Bossart /* 1840857a7c42SPierre-Louis Bossart * follow recommended programming flows to avoid 1841857a7c42SPierre-Louis Bossart * timeouts when gsync is enabled 1842857a7c42SPierre-Louis Bossart */ 1843857a7c42SPierre-Louis Bossart if (multi_link) 1844857a7c42SPierre-Louis Bossart intel_shim_sync_arm(sdw); 1845857a7c42SPierre-Louis Bossart 1846857a7c42SPierre-Louis Bossart ret = sdw_cdns_init(&sdw->cdns); 1847857a7c42SPierre-Louis Bossart if (ret < 0) { 1848857a7c42SPierre-Louis Bossart dev_err(dev, "unable to initialize Cadence IP during resume\n"); 1849857a7c42SPierre-Louis Bossart return ret; 1850857a7c42SPierre-Louis Bossart } 1851857a7c42SPierre-Louis Bossart 1852ebf878edSPierre-Louis Bossart ret = sdw_cdns_exit_reset(cdns); 1853ebf878edSPierre-Louis Bossart if (ret < 0) { 1854ebf878edSPierre-Louis Bossart dev_err(dev, "unable to exit bus reset sequence during resume\n"); 1855ebf878edSPierre-Louis Bossart return ret; 1856ebf878edSPierre-Louis Bossart } 1857857a7c42SPierre-Louis Bossart 1858857a7c42SPierre-Louis Bossart if (multi_link) { 1859857a7c42SPierre-Louis Bossart ret = intel_shim_sync_go(sdw); 1860857a7c42SPierre-Louis Bossart if (ret < 0) { 1861857a7c42SPierre-Louis Bossart dev_err(dev, "sync go failed during resume\n"); 1862857a7c42SPierre-Louis Bossart return ret; 1863857a7c42SPierre-Louis Bossart } 1864857a7c42SPierre-Louis Bossart } 18656626a616SRander Wang } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) { 18666626a616SRander Wang ret = intel_init(sdw); 18676626a616SRander Wang if (ret) { 18686626a616SRander Wang dev_err(dev, "%s failed: %d", __func__, ret); 18696626a616SRander Wang return ret; 18706626a616SRander Wang } 18716626a616SRander Wang 18726626a616SRander Wang /* 187308abad9fSRander Wang * An exception condition occurs for the CLK_STOP_BUS_RESET 187408abad9fSRander Wang * case if one or more masters remain active. In this condition, 187508abad9fSRander Wang * all the masters are powered on for they are in the same power 187608abad9fSRander Wang * domain. Master can preserve its context for clock stop0, so 187708abad9fSRander Wang * there is no need to clear slave status and reset bus. 187808abad9fSRander Wang */ 187908abad9fSRander Wang clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns); 188008abad9fSRander Wang 1881857a7c42SPierre-Louis Bossart if (!clock_stop0) { 1882857a7c42SPierre-Louis Bossart 1883857a7c42SPierre-Louis Bossart /* 18846626a616SRander Wang * make sure all Slaves are tagged as UNATTACHED and 18856626a616SRander Wang * provide reason for reinitialization 18866626a616SRander Wang */ 1887857a7c42SPierre-Louis Bossart 188808abad9fSRander Wang status = SDW_UNATTACH_REQUEST_MASTER_RESET; 188908abad9fSRander Wang sdw_clear_slave_status(bus, status); 18906626a616SRander Wang 18916626a616SRander Wang ret = sdw_cdns_enable_interrupt(cdns, true); 18926626a616SRander Wang if (ret < 0) { 18936626a616SRander Wang dev_err(dev, "cannot enable interrupts during resume\n"); 18946626a616SRander Wang return ret; 18956626a616SRander Wang } 18966626a616SRander Wang 1897d78071b4SPierre-Louis Bossart /* 1898d78071b4SPierre-Louis Bossart * follow recommended programming flows to avoid 1899d78071b4SPierre-Louis Bossart * timeouts when gsync is enabled 1900d78071b4SPierre-Louis Bossart */ 1901d78071b4SPierre-Louis Bossart if (multi_link) 1902d78071b4SPierre-Louis Bossart intel_shim_sync_arm(sdw); 1903d78071b4SPierre-Louis Bossart 1904d78071b4SPierre-Louis Bossart /* 1905d78071b4SPierre-Louis Bossart * Re-initialize the IP since it was powered-off 1906d78071b4SPierre-Louis Bossart */ 1907d78071b4SPierre-Louis Bossart sdw_cdns_init(&sdw->cdns); 1908d78071b4SPierre-Louis Bossart 1909d78071b4SPierre-Louis Bossart } else { 1910d78071b4SPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 1911d78071b4SPierre-Louis Bossart if (ret < 0) { 1912d78071b4SPierre-Louis Bossart dev_err(dev, "cannot enable interrupts during resume\n"); 1913d78071b4SPierre-Louis Bossart return ret; 1914d78071b4SPierre-Louis Bossart } 1915d78071b4SPierre-Louis Bossart } 1916d78071b4SPierre-Louis Bossart 191708abad9fSRander Wang ret = sdw_cdns_clock_restart(cdns, !clock_stop0); 19186626a616SRander Wang if (ret < 0) { 19196626a616SRander Wang dev_err(dev, "unable to restart clock during resume\n"); 19206626a616SRander Wang return ret; 19216626a616SRander Wang } 1922d78071b4SPierre-Louis Bossart 1923d78071b4SPierre-Louis Bossart if (!clock_stop0) { 1924d78071b4SPierre-Louis Bossart ret = sdw_cdns_exit_reset(cdns); 1925d78071b4SPierre-Louis Bossart if (ret < 0) { 1926d78071b4SPierre-Louis Bossart dev_err(dev, "unable to exit bus reset sequence during resume\n"); 1927d78071b4SPierre-Louis Bossart return ret; 1928d78071b4SPierre-Louis Bossart } 1929d78071b4SPierre-Louis Bossart 1930d78071b4SPierre-Louis Bossart if (multi_link) { 1931d78071b4SPierre-Louis Bossart ret = intel_shim_sync_go(sdw); 1932d78071b4SPierre-Louis Bossart if (ret < 0) { 1933d78071b4SPierre-Louis Bossart dev_err(sdw->cdns.dev, "sync go failed during resume\n"); 1934d78071b4SPierre-Louis Bossart return ret; 1935d78071b4SPierre-Louis Bossart } 1936d78071b4SPierre-Louis Bossart } 1937d78071b4SPierre-Louis Bossart } 193861fb830bSPierre-Louis Bossart } else if (!clock_stop_quirks) { 1939f748f34eSPierre-Louis Bossart 1940f748f34eSPierre-Louis Bossart clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns); 1941f748f34eSPierre-Louis Bossart if (!clock_stop0) 1942f748f34eSPierre-Louis Bossart dev_err(dev, "%s invalid configuration, clock was not stopped", __func__); 1943f748f34eSPierre-Louis Bossart 194461fb830bSPierre-Louis Bossart ret = intel_init(sdw); 194561fb830bSPierre-Louis Bossart if (ret) { 194661fb830bSPierre-Louis Bossart dev_err(dev, "%s failed: %d", __func__, ret); 194761fb830bSPierre-Louis Bossart return ret; 194861fb830bSPierre-Louis Bossart } 194961fb830bSPierre-Louis Bossart 195061fb830bSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 195161fb830bSPierre-Louis Bossart if (ret < 0) { 195261fb830bSPierre-Louis Bossart dev_err(dev, "cannot enable interrupts during resume\n"); 195361fb830bSPierre-Louis Bossart return ret; 195461fb830bSPierre-Louis Bossart } 195561fb830bSPierre-Louis Bossart 195661fb830bSPierre-Louis Bossart ret = sdw_cdns_clock_restart(cdns, false); 195761fb830bSPierre-Louis Bossart if (ret < 0) { 195861fb830bSPierre-Louis Bossart dev_err(dev, "unable to resume master during resume\n"); 195961fb830bSPierre-Louis Bossart return ret; 196061fb830bSPierre-Louis Bossart } 1961a320f41eSPierre-Louis Bossart } else { 1962a320f41eSPierre-Louis Bossart dev_err(dev, "%s clock_stop_quirks %x unsupported\n", 1963a320f41eSPierre-Louis Bossart __func__, clock_stop_quirks); 1964a320f41eSPierre-Louis Bossart ret = -EINVAL; 1965a320f41eSPierre-Louis Bossart } 1966ebf878edSPierre-Louis Bossart 1967ebf878edSPierre-Louis Bossart return ret; 1968ebf878edSPierre-Louis Bossart } 1969ebf878edSPierre-Louis Bossart 19709b3b4b3fSPierre-Louis Bossart static const struct dev_pm_ops intel_pm = { 19719b3b4b3fSPierre-Louis Bossart SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) 1972ebf878edSPierre-Louis Bossart SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL) 19739b3b4b3fSPierre-Louis Bossart }; 19749b3b4b3fSPierre-Louis Bossart 197571bb8a1bSVinod Koul static struct platform_driver sdw_intel_drv = { 1976b6109dd6SPierre-Louis Bossart .probe = intel_master_probe, 1977b6109dd6SPierre-Louis Bossart .remove = intel_master_remove, 197871bb8a1bSVinod Koul .driver = { 19796d2c6669SPierre-Louis Bossart .name = "intel-sdw", 19809b3b4b3fSPierre-Louis Bossart .pm = &intel_pm, 19819b3b4b3fSPierre-Louis Bossart } 198271bb8a1bSVinod Koul }; 198371bb8a1bSVinod Koul 198471bb8a1bSVinod Koul module_platform_driver(sdw_intel_drv); 198571bb8a1bSVinod Koul 198671bb8a1bSVinod Koul MODULE_LICENSE("Dual BSD/GPL"); 19876d2c6669SPierre-Louis Bossart MODULE_ALIAS("platform:intel-sdw"); 198871bb8a1bSVinod Koul MODULE_DESCRIPTION("Intel Soundwire Master Driver"); 1989