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 26579ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw) 26679ee6631SPierre-Louis Bossart { 26779ee6631SPierre-Louis Bossart struct dentry *root = sdw->cdns.bus.debugfs; 26879ee6631SPierre-Louis Bossart 26979ee6631SPierre-Louis Bossart if (!root) 27079ee6631SPierre-Louis Bossart return; 27179ee6631SPierre-Louis Bossart 27279ee6631SPierre-Louis Bossart sdw->debugfs = debugfs_create_dir("intel-sdw", root); 27379ee6631SPierre-Louis Bossart 27479ee6631SPierre-Louis Bossart debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw, 27579ee6631SPierre-Louis Bossart &intel_reg_fops); 27679ee6631SPierre-Louis Bossart 27779ee6631SPierre-Louis Bossart sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs); 27879ee6631SPierre-Louis Bossart } 27979ee6631SPierre-Louis Bossart 28079ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw) 28179ee6631SPierre-Louis Bossart { 28279ee6631SPierre-Louis Bossart debugfs_remove_recursive(sdw->debugfs); 28379ee6631SPierre-Louis Bossart } 28479ee6631SPierre-Louis Bossart #else 28579ee6631SPierre-Louis Bossart static void intel_debugfs_init(struct sdw_intel *sdw) {} 28679ee6631SPierre-Louis Bossart static void intel_debugfs_exit(struct sdw_intel *sdw) {} 28779ee6631SPierre-Louis Bossart #endif /* CONFIG_DEBUG_FS */ 28879ee6631SPierre-Louis Bossart 28979ee6631SPierre-Louis Bossart /* 29071bb8a1bSVinod Koul * shim ops 29171bb8a1bSVinod Koul */ 29271bb8a1bSVinod Koul 29371bb8a1bSVinod Koul static int intel_link_power_up(struct sdw_intel *sdw) 29471bb8a1bSVinod Koul { 29571bb8a1bSVinod Koul unsigned int link_id = sdw->instance; 2962523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 2974a17c441SPierre-Louis Bossart u32 *shim_mask = sdw->link_res->shim_mask; 2984a17c441SPierre-Louis Bossart struct sdw_bus *bus = &sdw->cdns.bus; 2994a17c441SPierre-Louis Bossart struct sdw_master_prop *prop = &bus->prop; 3005ee74eb2SPierre-Louis Bossart u32 spa_mask, cpa_mask; 3015ee74eb2SPierre-Louis Bossart u32 link_control; 3024a17c441SPierre-Louis Bossart int ret = 0; 3034a17c441SPierre-Louis Bossart u32 syncprd; 3044a17c441SPierre-Louis Bossart u32 sync_reg; 3054a17c441SPierre-Louis Bossart 3064a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 3074a17c441SPierre-Louis Bossart 3084a17c441SPierre-Louis Bossart /* 3094a17c441SPierre-Louis Bossart * The hardware relies on an internal counter, typically 4kHz, 3104a17c441SPierre-Louis Bossart * to generate the SoundWire SSP - which defines a 'safe' 3114a17c441SPierre-Louis Bossart * synchronization point between commands and audio transport 3124a17c441SPierre-Louis Bossart * and allows for multi link synchronization. The SYNCPRD value 3134a17c441SPierre-Louis Bossart * is only dependent on the oscillator clock provided to 3144a17c441SPierre-Louis Bossart * the IP, so adjust based on _DSD properties reported in DSDT 3154a17c441SPierre-Louis Bossart * tables. The values reported are based on either 24MHz 3164a17c441SPierre-Louis Bossart * (CNL/CML) or 38.4 MHz (ICL/TGL+). 3174a17c441SPierre-Louis Bossart */ 3184a17c441SPierre-Louis Bossart if (prop->mclk_freq % 6000000) 3194a17c441SPierre-Louis Bossart syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; 3204a17c441SPierre-Louis Bossart else 3214a17c441SPierre-Louis Bossart syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; 3224a17c441SPierre-Louis Bossart 3234a17c441SPierre-Louis Bossart if (!*shim_mask) { 3245ee74eb2SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, "%s: powering up all links\n", __func__); 3255ee74eb2SPierre-Louis Bossart 3264a17c441SPierre-Louis Bossart /* we first need to program the SyncPRD/CPU registers */ 3274a17c441SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, 3284a17c441SPierre-Louis Bossart "%s: first link up, programming SYNCPRD\n", __func__); 3294a17c441SPierre-Louis Bossart 3304a17c441SPierre-Louis Bossart /* set SyncPRD period */ 3314a17c441SPierre-Louis Bossart sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 332*f067c925SVinod Koul u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD); 3334a17c441SPierre-Louis Bossart 3344a17c441SPierre-Louis Bossart /* Set SyncCPU bit */ 3354a17c441SPierre-Louis Bossart sync_reg |= SDW_SHIM_SYNC_SYNCCPU; 3364a17c441SPierre-Louis Bossart intel_writel(shim, SDW_SHIM_SYNC, sync_reg); 33771bb8a1bSVinod Koul 33871bb8a1bSVinod Koul /* Link power up sequence */ 33971bb8a1bSVinod Koul link_control = intel_readl(shim, SDW_SHIM_LCTL); 3405ee74eb2SPierre-Louis Bossart 3415ee74eb2SPierre-Louis Bossart /* only power-up enabled links */ 3423b4979caSVinod Koul spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask); 3433b4979caSVinod Koul cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); 3445ee74eb2SPierre-Louis Bossart 34571bb8a1bSVinod Koul link_control |= spa_mask; 34671bb8a1bSVinod Koul 34771bb8a1bSVinod Koul ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); 3484a17c441SPierre-Louis Bossart if (ret < 0) { 3494a17c441SPierre-Louis Bossart dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret); 3504a17c441SPierre-Louis Bossart goto out; 35171bb8a1bSVinod Koul } 35271bb8a1bSVinod Koul 3534a17c441SPierre-Louis Bossart /* SyncCPU will change once link is active */ 3544a17c441SPierre-Louis Bossart ret = intel_wait_bit(shim, SDW_SHIM_SYNC, 3554a17c441SPierre-Louis Bossart SDW_SHIM_SYNC_SYNCCPU, 0); 3564a17c441SPierre-Louis Bossart if (ret < 0) { 3574a17c441SPierre-Louis Bossart dev_err(sdw->cdns.dev, 3584a17c441SPierre-Louis Bossart "Failed to set SHIM_SYNC: %d\n", ret); 3594a17c441SPierre-Louis Bossart goto out; 3604a17c441SPierre-Louis Bossart } 3614a17c441SPierre-Louis Bossart } 3624a17c441SPierre-Louis Bossart 3634a17c441SPierre-Louis Bossart *shim_mask |= BIT(link_id); 3644a17c441SPierre-Louis Bossart 3654a17c441SPierre-Louis Bossart sdw->cdns.link_up = true; 3664a17c441SPierre-Louis Bossart out: 3674a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 3684a17c441SPierre-Louis Bossart 3694a17c441SPierre-Louis Bossart return ret; 3704a17c441SPierre-Louis Bossart } 3714a17c441SPierre-Louis Bossart 3724a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */ 3734a17c441SPierre-Louis Bossart static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw) 37471bb8a1bSVinod Koul { 3752523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 37671bb8a1bSVinod Koul unsigned int link_id = sdw->instance; 3774a17c441SPierre-Louis Bossart u16 ioctl; 37871bb8a1bSVinod Koul 37971bb8a1bSVinod Koul /* Switch to MIP from Glue logic */ 38071bb8a1bSVinod Koul ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); 38171bb8a1bSVinod Koul 38271bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_DOE); 38371bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 3844a17c441SPierre-Louis Bossart usleep_range(10, 15); 38571bb8a1bSVinod Koul 38671bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_DO); 38771bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 3884a17c441SPierre-Louis Bossart usleep_range(10, 15); 38971bb8a1bSVinod Koul 39071bb8a1bSVinod Koul ioctl |= (SDW_SHIM_IOCTL_MIF); 39171bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 3924a17c441SPierre-Louis Bossart usleep_range(10, 15); 39371bb8a1bSVinod Koul 39471bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_BKE); 39571bb8a1bSVinod Koul ioctl &= ~(SDW_SHIM_IOCTL_COE); 39671bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 3974a17c441SPierre-Louis Bossart usleep_range(10, 15); 3984a17c441SPierre-Louis Bossart 3994a17c441SPierre-Louis Bossart /* at this point Master IP has full control of the I/Os */ 4004a17c441SPierre-Louis Bossart } 4014a17c441SPierre-Louis Bossart 4024a17c441SPierre-Louis Bossart /* this needs to be called with shim_lock */ 4034a17c441SPierre-Louis Bossart static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw) 4044a17c441SPierre-Louis Bossart { 4054a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 4064a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 4074a17c441SPierre-Louis Bossart u16 ioctl; 4084a17c441SPierre-Louis Bossart 4094a17c441SPierre-Louis Bossart /* Glue logic */ 4104a17c441SPierre-Louis Bossart ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); 4114a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_BKE; 4124a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_COE; 4134a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4144a17c441SPierre-Louis Bossart usleep_range(10, 15); 4154a17c441SPierre-Louis Bossart 4164a17c441SPierre-Louis Bossart ioctl &= ~(SDW_SHIM_IOCTL_MIF); 4174a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4184a17c441SPierre-Louis Bossart usleep_range(10, 15); 4194a17c441SPierre-Louis Bossart 4204a17c441SPierre-Louis Bossart /* at this point Integration Glue has full control of the I/Os */ 4214a17c441SPierre-Louis Bossart } 4224a17c441SPierre-Louis Bossart 4234a17c441SPierre-Louis Bossart static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop) 4244a17c441SPierre-Louis Bossart { 4254a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 4264a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 4274a17c441SPierre-Louis Bossart int ret = 0; 4284a17c441SPierre-Louis Bossart u16 ioctl = 0, act = 0; 4294a17c441SPierre-Louis Bossart 4304a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 4314a17c441SPierre-Louis Bossart 4324a17c441SPierre-Louis Bossart /* Initialize Shim */ 4334a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_BKE; 4344a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4354a17c441SPierre-Louis Bossart usleep_range(10, 15); 4364a17c441SPierre-Louis Bossart 4374a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_WPDD; 4384a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4394a17c441SPierre-Louis Bossart usleep_range(10, 15); 4404a17c441SPierre-Louis Bossart 4414a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_DO; 4424a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4434a17c441SPierre-Louis Bossart usleep_range(10, 15); 4444a17c441SPierre-Louis Bossart 4454a17c441SPierre-Louis Bossart ioctl |= SDW_SHIM_IOCTL_DOE; 4464a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); 4474a17c441SPierre-Louis Bossart usleep_range(10, 15); 4484a17c441SPierre-Louis Bossart 4494a17c441SPierre-Louis Bossart intel_shim_glue_to_master_ip(sdw); 45071bb8a1bSVinod Koul 451*f067c925SVinod Koul u16p_replace_bits(&act, 0x1, SDW_SHIM_CTMCTL_DOAIS); 45271bb8a1bSVinod Koul act |= SDW_SHIM_CTMCTL_DACTQE; 45371bb8a1bSVinod Koul act |= SDW_SHIM_CTMCTL_DODS; 45471bb8a1bSVinod Koul intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act); 4554a17c441SPierre-Louis Bossart usleep_range(10, 15); 45671bb8a1bSVinod Koul 4574a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 45871bb8a1bSVinod Koul 45971bb8a1bSVinod Koul return ret; 46071bb8a1bSVinod Koul } 46171bb8a1bSVinod Koul 462ab2c9132SRander Wang static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) 4634a17c441SPierre-Louis Bossart { 4644a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 4654a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 4664a17c441SPierre-Louis Bossart u16 wake_en, wake_sts; 4674a17c441SPierre-Louis Bossart 4684a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 4694a17c441SPierre-Louis Bossart wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); 4704a17c441SPierre-Louis Bossart 4714a17c441SPierre-Louis Bossart if (wake_enable) { 4724a17c441SPierre-Louis Bossart /* Enable the wakeup */ 4734a17c441SPierre-Louis Bossart wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id); 4744a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); 4754a17c441SPierre-Louis Bossart } else { 4764a17c441SPierre-Louis Bossart /* Disable the wake up interrupt */ 4774a17c441SPierre-Louis Bossart wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); 4784a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); 4794a17c441SPierre-Louis Bossart 4804a17c441SPierre-Louis Bossart /* Clear wake status */ 4814a17c441SPierre-Louis Bossart wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); 4824a17c441SPierre-Louis Bossart wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id); 4834a17c441SPierre-Louis Bossart intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts); 4844a17c441SPierre-Louis Bossart } 4854a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 4864a17c441SPierre-Louis Bossart } 4874a17c441SPierre-Louis Bossart 4889b3b4b3fSPierre-Louis Bossart static int intel_link_power_down(struct sdw_intel *sdw) 4894a17c441SPierre-Louis Bossart { 4905ee74eb2SPierre-Louis Bossart u32 link_control, spa_mask, cpa_mask; 4914a17c441SPierre-Louis Bossart unsigned int link_id = sdw->instance; 4924a17c441SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 4934a17c441SPierre-Louis Bossart u32 *shim_mask = sdw->link_res->shim_mask; 4944a17c441SPierre-Louis Bossart int ret = 0; 4954a17c441SPierre-Louis Bossart 4964a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 4974a17c441SPierre-Louis Bossart 4984a17c441SPierre-Louis Bossart intel_shim_master_ip_to_glue(sdw); 4994a17c441SPierre-Louis Bossart 5004a17c441SPierre-Louis Bossart if (!(*shim_mask & BIT(link_id))) 5014a17c441SPierre-Louis Bossart dev_err(sdw->cdns.dev, 5024a17c441SPierre-Louis Bossart "%s: Unbalanced power-up/down calls\n", __func__); 5034a17c441SPierre-Louis Bossart 5044a17c441SPierre-Louis Bossart *shim_mask &= ~BIT(link_id); 5054a17c441SPierre-Louis Bossart 5065ee74eb2SPierre-Louis Bossart if (!*shim_mask) { 5075ee74eb2SPierre-Louis Bossart 5085ee74eb2SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, "%s: powering down all links\n", __func__); 5095ee74eb2SPierre-Louis Bossart 5105ee74eb2SPierre-Louis Bossart /* Link power down sequence */ 5115ee74eb2SPierre-Louis Bossart link_control = intel_readl(shim, SDW_SHIM_LCTL); 5125ee74eb2SPierre-Louis Bossart 5135ee74eb2SPierre-Louis Bossart /* only power-down enabled links */ 5143b4979caSVinod Koul spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, ~sdw->link_res->link_mask); 5153b4979caSVinod Koul cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); 5165ee74eb2SPierre-Louis Bossart 5175ee74eb2SPierre-Louis Bossart link_control &= spa_mask; 5185ee74eb2SPierre-Louis Bossart 5195ee74eb2SPierre-Louis Bossart ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); 5205ee74eb2SPierre-Louis Bossart } 5215ee74eb2SPierre-Louis Bossart 5225ee74eb2SPierre-Louis Bossart link_control = intel_readl(shim, SDW_SHIM_LCTL); 5235ee74eb2SPierre-Louis Bossart 5244a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 5254a17c441SPierre-Louis Bossart 5265ee74eb2SPierre-Louis Bossart if (ret < 0) { 5275ee74eb2SPierre-Louis Bossart dev_err(sdw->cdns.dev, "%s: could not power down link\n", __func__); 5285ee74eb2SPierre-Louis Bossart 5294a17c441SPierre-Louis Bossart return ret; 5305ee74eb2SPierre-Louis Bossart } 5314a17c441SPierre-Louis Bossart 5324a17c441SPierre-Louis Bossart sdw->cdns.link_up = false; 5334a17c441SPierre-Louis Bossart return 0; 5344a17c441SPierre-Louis Bossart } 5354a17c441SPierre-Louis Bossart 53602629e45SPierre-Louis Bossart static void intel_shim_sync_arm(struct sdw_intel *sdw) 53702629e45SPierre-Louis Bossart { 53802629e45SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 53902629e45SPierre-Louis Bossart u32 sync_reg; 54002629e45SPierre-Louis Bossart 54102629e45SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 54202629e45SPierre-Louis Bossart 54302629e45SPierre-Louis Bossart /* update SYNC register */ 54402629e45SPierre-Louis Bossart sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 54502629e45SPierre-Louis Bossart sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance); 54602629e45SPierre-Louis Bossart intel_writel(shim, SDW_SHIM_SYNC, sync_reg); 54702629e45SPierre-Louis Bossart 54802629e45SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 54902629e45SPierre-Louis Bossart } 55002629e45SPierre-Louis Bossart 551437e3289SPierre-Louis Bossart static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw) 552437e3289SPierre-Louis Bossart { 553437e3289SPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 554437e3289SPierre-Louis Bossart u32 sync_reg; 555437e3289SPierre-Louis Bossart int ret; 556437e3289SPierre-Louis Bossart 557437e3289SPierre-Louis Bossart /* Read SYNC register */ 558437e3289SPierre-Louis Bossart sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 559437e3289SPierre-Louis Bossart 560437e3289SPierre-Louis Bossart /* 561437e3289SPierre-Louis Bossart * Set SyncGO bit to synchronously trigger a bank switch for 562437e3289SPierre-Louis Bossart * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all 563437e3289SPierre-Louis Bossart * the Masters. 564437e3289SPierre-Louis Bossart */ 565437e3289SPierre-Louis Bossart sync_reg |= SDW_SHIM_SYNC_SYNCGO; 566437e3289SPierre-Louis Bossart 567437e3289SPierre-Louis Bossart ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, 568437e3289SPierre-Louis Bossart SDW_SHIM_SYNC_SYNCGO); 569437e3289SPierre-Louis Bossart 570437e3289SPierre-Louis Bossart if (ret < 0) 571437e3289SPierre-Louis Bossart dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret); 57271bb8a1bSVinod Koul 57371bb8a1bSVinod Koul return ret; 57471bb8a1bSVinod Koul } 57571bb8a1bSVinod Koul 576857a7c42SPierre-Louis Bossart static int intel_shim_sync_go(struct sdw_intel *sdw) 577857a7c42SPierre-Louis Bossart { 578857a7c42SPierre-Louis Bossart int ret; 579857a7c42SPierre-Louis Bossart 580857a7c42SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 581857a7c42SPierre-Louis Bossart 582857a7c42SPierre-Louis Bossart ret = intel_shim_sync_go_unlocked(sdw); 583857a7c42SPierre-Louis Bossart 584857a7c42SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 585857a7c42SPierre-Louis Bossart 586857a7c42SPierre-Louis Bossart return ret; 587857a7c42SPierre-Louis Bossart } 588857a7c42SPierre-Louis Bossart 58937a2d22bSVinod Koul /* 59037a2d22bSVinod Koul * PDI routines 59137a2d22bSVinod Koul */ 59237a2d22bSVinod Koul static void intel_pdi_init(struct sdw_intel *sdw, 59337a2d22bSVinod Koul struct sdw_cdns_stream_config *config) 59437a2d22bSVinod Koul { 5952523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 59637a2d22bSVinod Koul unsigned int link_id = sdw->instance; 59737a2d22bSVinod Koul int pcm_cap, pdm_cap; 59837a2d22bSVinod Koul 59937a2d22bSVinod Koul /* PCM Stream Capability */ 60037a2d22bSVinod Koul pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id)); 60137a2d22bSVinod Koul 6023b4979caSVinod Koul config->pcm_bd = FIELD_GET(SDW_SHIM_PCMSCAP_BSS, pcm_cap); 6033b4979caSVinod Koul config->pcm_in = FIELD_GET(SDW_SHIM_PCMSCAP_ISS, pcm_cap); 6043b4979caSVinod Koul config->pcm_out = FIELD_GET(SDW_SHIM_PCMSCAP_OSS, pcm_cap); 60537a2d22bSVinod Koul 606121f4361SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n", 607121f4361SPierre-Louis Bossart config->pcm_bd, config->pcm_in, config->pcm_out); 608121f4361SPierre-Louis Bossart 60937a2d22bSVinod Koul /* PDM Stream Capability */ 61037a2d22bSVinod Koul pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); 61137a2d22bSVinod Koul 6123b4979caSVinod Koul config->pdm_bd = FIELD_GET(SDW_SHIM_PDMSCAP_BSS, pdm_cap); 6133b4979caSVinod Koul config->pdm_in = FIELD_GET(SDW_SHIM_PDMSCAP_ISS, pdm_cap); 6143b4979caSVinod Koul config->pdm_out = FIELD_GET(SDW_SHIM_PDMSCAP_OSS, pdm_cap); 615121f4361SPierre-Louis Bossart 616121f4361SPierre-Louis Bossart dev_dbg(sdw->cdns.dev, "PDM cap bd:%d in:%d out:%d\n", 617121f4361SPierre-Louis Bossart config->pdm_bd, config->pdm_in, config->pdm_out); 61837a2d22bSVinod Koul } 61937a2d22bSVinod Koul 62037a2d22bSVinod Koul static int 62137a2d22bSVinod Koul intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm) 62237a2d22bSVinod Koul { 6232523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 62437a2d22bSVinod Koul unsigned int link_id = sdw->instance; 62537a2d22bSVinod Koul int count; 62637a2d22bSVinod Koul 62737a2d22bSVinod Koul if (pcm) { 62837a2d22bSVinod Koul count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); 62918046335SPierre-Louis Bossart 63018046335SPierre-Louis Bossart /* 63118046335SPierre-Louis Bossart * WORKAROUND: on all existing Intel controllers, pdi 63218046335SPierre-Louis Bossart * number 2 reports channel count as 1 even though it 63318046335SPierre-Louis Bossart * supports 8 channels. Performing hardcoding for pdi 63418046335SPierre-Louis Bossart * number 2. 63518046335SPierre-Louis Bossart */ 63618046335SPierre-Louis Bossart if (pdi_num == 2) 63718046335SPierre-Louis Bossart count = 7; 63818046335SPierre-Louis Bossart 63937a2d22bSVinod Koul } else { 64037a2d22bSVinod Koul count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); 6413b4979caSVinod Koul count = FIELD_GET(SDW_SHIM_PDMSCAP_CPSS, count); 64237a2d22bSVinod Koul } 64337a2d22bSVinod Koul 64437a2d22bSVinod Koul /* zero based values for channel count in register */ 64537a2d22bSVinod Koul count++; 64637a2d22bSVinod Koul 64737a2d22bSVinod Koul return count; 64837a2d22bSVinod Koul } 64937a2d22bSVinod Koul 65037a2d22bSVinod Koul static int intel_pdi_get_ch_update(struct sdw_intel *sdw, 65137a2d22bSVinod Koul struct sdw_cdns_pdi *pdi, 65237a2d22bSVinod Koul unsigned int num_pdi, 65337a2d22bSVinod Koul unsigned int *num_ch, bool pcm) 65437a2d22bSVinod Koul { 65537a2d22bSVinod Koul int i, ch_count = 0; 65637a2d22bSVinod Koul 65737a2d22bSVinod Koul for (i = 0; i < num_pdi; i++) { 65837a2d22bSVinod Koul pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm); 65937a2d22bSVinod Koul ch_count += pdi->ch_count; 66037a2d22bSVinod Koul pdi++; 66137a2d22bSVinod Koul } 66237a2d22bSVinod Koul 66337a2d22bSVinod Koul *num_ch = ch_count; 66437a2d22bSVinod Koul return 0; 66537a2d22bSVinod Koul } 66637a2d22bSVinod Koul 66737a2d22bSVinod Koul static int intel_pdi_stream_ch_update(struct sdw_intel *sdw, 66837a2d22bSVinod Koul struct sdw_cdns_streams *stream, bool pcm) 66937a2d22bSVinod Koul { 67037a2d22bSVinod Koul intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd, 67137a2d22bSVinod Koul &stream->num_ch_bd, pcm); 67237a2d22bSVinod Koul 67337a2d22bSVinod Koul intel_pdi_get_ch_update(sdw, stream->in, stream->num_in, 67437a2d22bSVinod Koul &stream->num_ch_in, pcm); 67537a2d22bSVinod Koul 67637a2d22bSVinod Koul intel_pdi_get_ch_update(sdw, stream->out, stream->num_out, 67737a2d22bSVinod Koul &stream->num_ch_out, pcm); 67837a2d22bSVinod Koul 67937a2d22bSVinod Koul return 0; 68037a2d22bSVinod Koul } 68137a2d22bSVinod Koul 68237a2d22bSVinod Koul static int intel_pdi_ch_update(struct sdw_intel *sdw) 68337a2d22bSVinod Koul { 68437a2d22bSVinod Koul /* First update PCM streams followed by PDM streams */ 68537a2d22bSVinod Koul intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true); 68637a2d22bSVinod Koul intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false); 68737a2d22bSVinod Koul 68837a2d22bSVinod Koul return 0; 68937a2d22bSVinod Koul } 69037a2d22bSVinod Koul 69137a2d22bSVinod Koul static void 69237a2d22bSVinod Koul intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) 69337a2d22bSVinod Koul { 6942523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 69537a2d22bSVinod Koul unsigned int link_id = sdw->instance; 69637a2d22bSVinod Koul int pdi_conf = 0; 69737a2d22bSVinod Koul 698c134f914SPierre-Louis Bossart /* the Bulk and PCM streams are not contiguous */ 699c134f914SPierre-Louis Bossart pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; 700c134f914SPierre-Louis Bossart if (pdi->num >= 2) 701c134f914SPierre-Louis Bossart pdi->intel_alh_id += 2; 70237a2d22bSVinod Koul 70337a2d22bSVinod Koul /* 70437a2d22bSVinod Koul * Program stream parameters to stream SHIM register 70537a2d22bSVinod Koul * This is applicable for PCM stream only. 70637a2d22bSVinod Koul */ 70737a2d22bSVinod Koul if (pdi->type != SDW_STREAM_PCM) 70837a2d22bSVinod Koul return; 70937a2d22bSVinod Koul 71037a2d22bSVinod Koul if (pdi->dir == SDW_DATA_DIR_RX) 71137a2d22bSVinod Koul pdi_conf |= SDW_SHIM_PCMSYCM_DIR; 71237a2d22bSVinod Koul else 71337a2d22bSVinod Koul pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR); 71437a2d22bSVinod Koul 715*f067c925SVinod Koul u32p_replace_bits(&pdi_conf, pdi->intel_alh_id, SDW_SHIM_PCMSYCM_STREAM); 716*f067c925SVinod Koul u32p_replace_bits(&pdi_conf, pdi->l_ch_num, SDW_SHIM_PCMSYCM_LCHN); 717*f067c925SVinod Koul u32p_replace_bits(&pdi_conf, pdi->h_ch_num, SDW_SHIM_PCMSYCM_HCHN); 71837a2d22bSVinod Koul 71937a2d22bSVinod Koul intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf); 72037a2d22bSVinod Koul } 72137a2d22bSVinod Koul 72237a2d22bSVinod Koul static void 72337a2d22bSVinod Koul intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) 72437a2d22bSVinod Koul { 7252523486bSPierre-Louis Bossart void __iomem *alh = sdw->link_res->alh; 72637a2d22bSVinod Koul unsigned int link_id = sdw->instance; 72737a2d22bSVinod Koul unsigned int conf; 72837a2d22bSVinod Koul 729c134f914SPierre-Louis Bossart /* the Bulk and PCM streams are not contiguous */ 730c134f914SPierre-Louis Bossart pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; 731c134f914SPierre-Louis Bossart if (pdi->num >= 2) 732c134f914SPierre-Louis Bossart pdi->intel_alh_id += 2; 73337a2d22bSVinod Koul 73437a2d22bSVinod Koul /* Program Stream config ALH register */ 73537a2d22bSVinod Koul conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id)); 73637a2d22bSVinod Koul 737*f067c925SVinod Koul u32p_replace_bits(&conf, SDW_ALH_STRMZCFG_DMAT_VAL, SDW_ALH_STRMZCFG_DMAT); 738*f067c925SVinod Koul u32p_replace_bits(&conf, pdi->ch_count - 1, SDW_ALH_STRMZCFG_CHN); 73937a2d22bSVinod Koul 74037a2d22bSVinod Koul intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); 74137a2d22bSVinod Koul } 74237a2d22bSVinod Koul 7434b206d34SRander Wang static int intel_params_stream(struct sdw_intel *sdw, 744c46302ecSVinod Koul struct snd_pcm_substream *substream, 745c46302ecSVinod Koul struct snd_soc_dai *dai, 7464b206d34SRander Wang struct snd_pcm_hw_params *hw_params, 7474b206d34SRander Wang int link_id, int alh_stream_id) 748c46302ecSVinod Koul { 7492523486bSPierre-Louis Bossart struct sdw_intel_link_res *res = sdw->link_res; 7504b206d34SRander Wang struct sdw_intel_stream_params_data params_data; 75105c8afe4SPierre-Louis Bossart 7524b206d34SRander Wang params_data.substream = substream; 7534b206d34SRander Wang params_data.dai = dai; 7544b206d34SRander Wang params_data.hw_params = hw_params; 7554b206d34SRander Wang params_data.link_id = link_id; 7564b206d34SRander Wang params_data.alh_stream_id = alh_stream_id; 757c46302ecSVinod Koul 7584b206d34SRander Wang if (res->ops && res->ops->params_stream && res->dev) 7594b206d34SRander Wang return res->ops->params_stream(res->dev, 7604b206d34SRander Wang ¶ms_data); 761c46302ecSVinod Koul return -EIO; 762c46302ecSVinod Koul } 763c46302ecSVinod Koul 764eff346f2SPierre-Louis Bossart static int intel_free_stream(struct sdw_intel *sdw, 765eff346f2SPierre-Louis Bossart struct snd_pcm_substream *substream, 766eff346f2SPierre-Louis Bossart struct snd_soc_dai *dai, 767eff346f2SPierre-Louis Bossart int link_id) 768eff346f2SPierre-Louis Bossart { 769eff346f2SPierre-Louis Bossart struct sdw_intel_link_res *res = sdw->link_res; 770eff346f2SPierre-Louis Bossart struct sdw_intel_stream_free_data free_data; 771eff346f2SPierre-Louis Bossart 772eff346f2SPierre-Louis Bossart free_data.substream = substream; 773eff346f2SPierre-Louis Bossart free_data.dai = dai; 774eff346f2SPierre-Louis Bossart free_data.link_id = link_id; 775eff346f2SPierre-Louis Bossart 776eff346f2SPierre-Louis Bossart if (res->ops && res->ops->free_stream && res->dev) 777eff346f2SPierre-Louis Bossart return res->ops->free_stream(res->dev, 778eff346f2SPierre-Louis Bossart &free_data); 779eff346f2SPierre-Louis Bossart 780eff346f2SPierre-Louis Bossart return 0; 781eff346f2SPierre-Louis Bossart } 782eff346f2SPierre-Louis Bossart 783c46302ecSVinod Koul /* 78430246e2dSShreyas NC * bank switch routines 78530246e2dSShreyas NC */ 78630246e2dSShreyas NC 78730246e2dSShreyas NC static int intel_pre_bank_switch(struct sdw_bus *bus) 78830246e2dSShreyas NC { 78930246e2dSShreyas NC struct sdw_cdns *cdns = bus_to_cdns(bus); 79030246e2dSShreyas NC struct sdw_intel *sdw = cdns_to_intel(cdns); 79130246e2dSShreyas NC 79230246e2dSShreyas NC /* Write to register only for multi-link */ 79330246e2dSShreyas NC if (!bus->multi_link) 79430246e2dSShreyas NC return 0; 79530246e2dSShreyas NC 79602629e45SPierre-Louis Bossart intel_shim_sync_arm(sdw); 79730246e2dSShreyas NC 79830246e2dSShreyas NC return 0; 79930246e2dSShreyas NC } 80030246e2dSShreyas NC 80130246e2dSShreyas NC static int intel_post_bank_switch(struct sdw_bus *bus) 80230246e2dSShreyas NC { 80330246e2dSShreyas NC struct sdw_cdns *cdns = bus_to_cdns(bus); 80430246e2dSShreyas NC struct sdw_intel *sdw = cdns_to_intel(cdns); 8052523486bSPierre-Louis Bossart void __iomem *shim = sdw->link_res->shim; 80630246e2dSShreyas NC int sync_reg, ret; 80730246e2dSShreyas NC 80830246e2dSShreyas NC /* Write to register only for multi-link */ 80930246e2dSShreyas NC if (!bus->multi_link) 81030246e2dSShreyas NC return 0; 81130246e2dSShreyas NC 8124a17c441SPierre-Louis Bossart mutex_lock(sdw->link_res->shim_lock); 8134a17c441SPierre-Louis Bossart 81430246e2dSShreyas NC /* Read SYNC register */ 81530246e2dSShreyas NC sync_reg = intel_readl(shim, SDW_SHIM_SYNC); 81630246e2dSShreyas NC 81730246e2dSShreyas NC /* 81830246e2dSShreyas NC * post_bank_switch() ops is called from the bus in loop for 81930246e2dSShreyas NC * all the Masters in the steam with the expectation that 82030246e2dSShreyas NC * we trigger the bankswitch for the only first Master in the list 82130246e2dSShreyas NC * and do nothing for the other Masters 82230246e2dSShreyas NC * 82330246e2dSShreyas NC * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master. 82430246e2dSShreyas NC */ 8254a17c441SPierre-Louis Bossart if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) { 8264a17c441SPierre-Louis Bossart ret = 0; 8274a17c441SPierre-Louis Bossart goto unlock; 8284a17c441SPierre-Louis Bossart } 82930246e2dSShreyas NC 830437e3289SPierre-Louis Bossart ret = intel_shim_sync_go_unlocked(sdw); 8314a17c441SPierre-Louis Bossart unlock: 8324a17c441SPierre-Louis Bossart mutex_unlock(sdw->link_res->shim_lock); 83330246e2dSShreyas NC 83430246e2dSShreyas NC if (ret < 0) 83517ed5befSPierre-Louis Bossart dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret); 83630246e2dSShreyas NC 83730246e2dSShreyas NC return ret; 83830246e2dSShreyas NC } 83930246e2dSShreyas NC 84030246e2dSShreyas NC /* 841c46302ecSVinod Koul * DAI routines 842c46302ecSVinod Koul */ 843c46302ecSVinod Koul 8445e7484d0SRander Wang static int intel_startup(struct snd_pcm_substream *substream, 8455e7484d0SRander Wang struct snd_soc_dai *dai) 8465e7484d0SRander Wang { 847ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 848ebf878edSPierre-Louis Bossart int ret; 849ebf878edSPierre-Louis Bossart 850ebf878edSPierre-Louis Bossart ret = pm_runtime_get_sync(cdns->dev); 851ebf878edSPierre-Louis Bossart if (ret < 0 && ret != -EACCES) { 852ebf878edSPierre-Louis Bossart dev_err_ratelimited(cdns->dev, 853ebf878edSPierre-Louis Bossart "pm_runtime_get_sync failed in %s, ret %d\n", 854ebf878edSPierre-Louis Bossart __func__, ret); 855ebf878edSPierre-Louis Bossart pm_runtime_put_noidle(cdns->dev); 856ebf878edSPierre-Louis Bossart return ret; 857ebf878edSPierre-Louis Bossart } 858ff16d1e5SPierre-Louis Bossart return 0; 8595e7484d0SRander Wang } 8605e7484d0SRander Wang 861c46302ecSVinod Koul static int intel_hw_params(struct snd_pcm_substream *substream, 862c46302ecSVinod Koul struct snd_pcm_hw_params *params, 863c46302ecSVinod Koul struct snd_soc_dai *dai) 864c46302ecSVinod Koul { 865c46302ecSVinod Koul struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 866c46302ecSVinod Koul struct sdw_intel *sdw = cdns_to_intel(cdns); 867c46302ecSVinod Koul struct sdw_cdns_dma_data *dma; 86857a34790SPierre-Louis Bossart struct sdw_cdns_pdi *pdi; 869c46302ecSVinod Koul struct sdw_stream_config sconfig; 870c46302ecSVinod Koul struct sdw_port_config *pconfig; 87157a34790SPierre-Louis Bossart int ch, dir; 87257a34790SPierre-Louis Bossart int ret; 873c46302ecSVinod Koul bool pcm = true; 874c46302ecSVinod Koul 875c46302ecSVinod Koul dma = snd_soc_dai_get_dma_data(dai, substream); 876c46302ecSVinod Koul if (!dma) 877c46302ecSVinod Koul return -EIO; 878c46302ecSVinod Koul 879c46302ecSVinod Koul ch = params_channels(params); 880c46302ecSVinod Koul if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 881c46302ecSVinod Koul dir = SDW_DATA_DIR_RX; 882c46302ecSVinod Koul else 883c46302ecSVinod Koul dir = SDW_DATA_DIR_TX; 884c46302ecSVinod Koul 88557a34790SPierre-Louis Bossart if (dma->stream_type == SDW_STREAM_PDM) 886c46302ecSVinod Koul pcm = false; 887c46302ecSVinod Koul 88857a34790SPierre-Louis Bossart if (pcm) 8891b53385eSBard Liao pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); 89057a34790SPierre-Louis Bossart else 8911b53385eSBard Liao pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir, dai->id); 892c46302ecSVinod Koul 89357a34790SPierre-Louis Bossart if (!pdi) { 894c46302ecSVinod Koul ret = -EINVAL; 89557a34790SPierre-Louis Bossart goto error; 896c46302ecSVinod Koul } 89757a34790SPierre-Louis Bossart 89857a34790SPierre-Louis Bossart /* do run-time configurations for SHIM, ALH and PDI/PORT */ 89957a34790SPierre-Louis Bossart intel_pdi_shim_configure(sdw, pdi); 90057a34790SPierre-Louis Bossart intel_pdi_alh_configure(sdw, pdi); 90157a34790SPierre-Louis Bossart sdw_cdns_config_stream(cdns, ch, dir, pdi); 90257a34790SPierre-Louis Bossart 903a5a0239cSBard Liao /* store pdi and hw_params, may be needed in prepare step */ 904a5a0239cSBard Liao dma->suspended = false; 905a5a0239cSBard Liao dma->pdi = pdi; 906a5a0239cSBard Liao dma->hw_params = params; 907c46302ecSVinod Koul 908c46302ecSVinod Koul /* Inform DSP about PDI stream number */ 9094b206d34SRander Wang ret = intel_params_stream(sdw, substream, dai, params, 9104b206d34SRander Wang sdw->instance, 91157a34790SPierre-Louis Bossart pdi->intel_alh_id); 912c46302ecSVinod Koul if (ret) 91357a34790SPierre-Louis Bossart goto error; 914c46302ecSVinod Koul 915c46302ecSVinod Koul sconfig.direction = dir; 916c46302ecSVinod Koul sconfig.ch_count = ch; 917c46302ecSVinod Koul sconfig.frame_rate = params_rate(params); 918c46302ecSVinod Koul sconfig.type = dma->stream_type; 919c46302ecSVinod Koul 920c46302ecSVinod Koul if (dma->stream_type == SDW_STREAM_PDM) { 921c46302ecSVinod Koul sconfig.frame_rate *= 50; 922c46302ecSVinod Koul sconfig.bps = 1; 923c46302ecSVinod Koul } else { 924c46302ecSVinod Koul sconfig.bps = snd_pcm_format_width(params_format(params)); 925c46302ecSVinod Koul } 926c46302ecSVinod Koul 927c46302ecSVinod Koul /* Port configuration */ 92857a34790SPierre-Louis Bossart pconfig = kcalloc(1, sizeof(*pconfig), GFP_KERNEL); 929c46302ecSVinod Koul if (!pconfig) { 930c46302ecSVinod Koul ret = -ENOMEM; 93157a34790SPierre-Louis Bossart goto error; 932c46302ecSVinod Koul } 933c46302ecSVinod Koul 93457a34790SPierre-Louis Bossart pconfig->num = pdi->num; 93557a34790SPierre-Louis Bossart pconfig->ch_mask = (1 << ch) - 1; 936c46302ecSVinod Koul 937c46302ecSVinod Koul ret = sdw_stream_add_master(&cdns->bus, &sconfig, 93857a34790SPierre-Louis Bossart pconfig, 1, dma->stream); 93957a34790SPierre-Louis Bossart if (ret) 94017ed5befSPierre-Louis Bossart dev_err(cdns->dev, "add master to stream failed:%d\n", ret); 941c46302ecSVinod Koul 942c46302ecSVinod Koul kfree(pconfig); 94357a34790SPierre-Louis Bossart error: 944c46302ecSVinod Koul return ret; 945c46302ecSVinod Koul } 946c46302ecSVinod Koul 94727b198f4SRander Wang static int intel_prepare(struct snd_pcm_substream *substream, 94827b198f4SRander Wang struct snd_soc_dai *dai) 94927b198f4SRander Wang { 950a5a0239cSBard Liao struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 951a5a0239cSBard Liao struct sdw_intel *sdw = cdns_to_intel(cdns); 95227b198f4SRander Wang struct sdw_cdns_dma_data *dma; 953a5a0239cSBard Liao int ch, dir; 954244eb888SPierre-Louis Bossart int ret = 0; 95527b198f4SRander Wang 95627b198f4SRander Wang dma = snd_soc_dai_get_dma_data(dai, substream); 95727b198f4SRander Wang if (!dma) { 95827b198f4SRander Wang dev_err(dai->dev, "failed to get dma data in %s", 95927b198f4SRander Wang __func__); 96027b198f4SRander Wang return -EIO; 96127b198f4SRander Wang } 96227b198f4SRander Wang 963a5a0239cSBard Liao if (dma->suspended) { 964a5a0239cSBard Liao dma->suspended = false; 965a5a0239cSBard Liao 966a5a0239cSBard Liao /* 967a5a0239cSBard Liao * .prepare() is called after system resume, where we 968a5a0239cSBard Liao * need to reinitialize the SHIM/ALH/Cadence IP. 969a5a0239cSBard Liao * .prepare() is also called to deal with underflows, 970a5a0239cSBard Liao * but in those cases we cannot touch ALH/SHIM 971a5a0239cSBard Liao * registers 972a5a0239cSBard Liao */ 973a5a0239cSBard Liao 974a5a0239cSBard Liao /* configure stream */ 975a5a0239cSBard Liao ch = params_channels(dma->hw_params); 976a5a0239cSBard Liao if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 977a5a0239cSBard Liao dir = SDW_DATA_DIR_RX; 978a5a0239cSBard Liao else 979a5a0239cSBard Liao dir = SDW_DATA_DIR_TX; 980a5a0239cSBard Liao 981a5a0239cSBard Liao intel_pdi_shim_configure(sdw, dma->pdi); 982a5a0239cSBard Liao intel_pdi_alh_configure(sdw, dma->pdi); 983a5a0239cSBard Liao sdw_cdns_config_stream(cdns, ch, dir, dma->pdi); 984a5a0239cSBard Liao 985a5a0239cSBard Liao /* Inform DSP about PDI stream number */ 986a5a0239cSBard Liao ret = intel_params_stream(sdw, substream, dai, 987a5a0239cSBard Liao dma->hw_params, 988a5a0239cSBard Liao sdw->instance, 989a5a0239cSBard Liao dma->pdi->intel_alh_id); 990a5a0239cSBard Liao } 991a5a0239cSBard Liao 992a5a0239cSBard Liao return ret; 99327b198f4SRander Wang } 99427b198f4SRander Wang 995c46302ecSVinod Koul static int 996c46302ecSVinod Koul intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 997c46302ecSVinod Koul { 998c46302ecSVinod Koul struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 999eff346f2SPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 1000c46302ecSVinod Koul struct sdw_cdns_dma_data *dma; 1001c46302ecSVinod Koul int ret; 1002c46302ecSVinod Koul 1003c46302ecSVinod Koul dma = snd_soc_dai_get_dma_data(dai, substream); 1004c46302ecSVinod Koul if (!dma) 1005c46302ecSVinod Koul return -EIO; 1006c46302ecSVinod Koul 1007244eb888SPierre-Louis Bossart /* 1008244eb888SPierre-Louis Bossart * The sdw stream state will transition to RELEASED when stream-> 1009244eb888SPierre-Louis Bossart * master_list is empty. So the stream state will transition to 1010244eb888SPierre-Louis Bossart * DEPREPARED for the first cpu-dai and to RELEASED for the last 1011244eb888SPierre-Louis Bossart * cpu-dai. 1012244eb888SPierre-Louis Bossart */ 1013c46302ecSVinod Koul ret = sdw_stream_remove_master(&cdns->bus, dma->stream); 1014eff346f2SPierre-Louis Bossart if (ret < 0) { 101517ed5befSPierre-Louis Bossart dev_err(dai->dev, "remove master from stream %s failed: %d\n", 1016c46302ecSVinod Koul dma->stream->name, ret); 1017c46302ecSVinod Koul return ret; 1018c46302ecSVinod Koul } 1019c46302ecSVinod Koul 1020eff346f2SPierre-Louis Bossart ret = intel_free_stream(sdw, substream, dai, sdw->instance); 1021eff346f2SPierre-Louis Bossart if (ret < 0) { 1022eff346f2SPierre-Louis Bossart dev_err(dai->dev, "intel_free_stream: failed %d", ret); 1023eff346f2SPierre-Louis Bossart return ret; 1024eff346f2SPierre-Louis Bossart } 1025eff346f2SPierre-Louis Bossart 1026a5a0239cSBard Liao dma->hw_params = NULL; 1027a5a0239cSBard Liao dma->pdi = NULL; 1028a5a0239cSBard Liao 1029eff346f2SPierre-Louis Bossart return 0; 1030eff346f2SPierre-Louis Bossart } 1031eff346f2SPierre-Louis Bossart 1032183c7687SPierre-Louis Bossart static void intel_shutdown(struct snd_pcm_substream *substream, 1033183c7687SPierre-Louis Bossart struct snd_soc_dai *dai) 1034183c7687SPierre-Louis Bossart { 1035ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 1036183c7687SPierre-Louis Bossart 1037ebf878edSPierre-Louis Bossart pm_runtime_mark_last_busy(cdns->dev); 1038ebf878edSPierre-Louis Bossart pm_runtime_put_autosuspend(cdns->dev); 1039183c7687SPierre-Louis Bossart } 1040183c7687SPierre-Louis Bossart 1041a5a0239cSBard Liao static int intel_component_dais_suspend(struct snd_soc_component *component) 1042a5a0239cSBard Liao { 1043a5a0239cSBard Liao struct sdw_cdns_dma_data *dma; 1044a5a0239cSBard Liao struct snd_soc_dai *dai; 1045a5a0239cSBard Liao 1046a5a0239cSBard Liao for_each_component_dais(component, dai) { 1047a5a0239cSBard Liao /* 1048a5a0239cSBard Liao * we don't have a .suspend dai_ops, and we don't have access 1049a5a0239cSBard Liao * to the substream, so let's mark both capture and playback 1050a5a0239cSBard Liao * DMA contexts as suspended 1051a5a0239cSBard Liao */ 1052a5a0239cSBard Liao dma = dai->playback_dma_data; 1053a5a0239cSBard Liao if (dma) 1054a5a0239cSBard Liao dma->suspended = true; 1055a5a0239cSBard Liao 1056a5a0239cSBard Liao dma = dai->capture_dma_data; 1057a5a0239cSBard Liao if (dma) 1058a5a0239cSBard Liao dma->suspended = true; 1059a5a0239cSBard Liao } 1060a5a0239cSBard Liao 1061a5a0239cSBard Liao return 0; 1062a5a0239cSBard Liao } 1063a5a0239cSBard Liao 1064c46302ecSVinod Koul static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, 1065c46302ecSVinod Koul void *stream, int direction) 1066c46302ecSVinod Koul { 1067c46302ecSVinod Koul return cdns_set_sdw_stream(dai, stream, true, direction); 1068c46302ecSVinod Koul } 1069c46302ecSVinod Koul 1070c46302ecSVinod Koul static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, 1071c46302ecSVinod Koul void *stream, int direction) 1072c46302ecSVinod Koul { 1073c46302ecSVinod Koul return cdns_set_sdw_stream(dai, stream, false, direction); 1074c46302ecSVinod Koul } 1075c46302ecSVinod Koul 107609553140SPierre-Louis Bossart static void *intel_get_sdw_stream(struct snd_soc_dai *dai, 107709553140SPierre-Louis Bossart int direction) 107809553140SPierre-Louis Bossart { 107909553140SPierre-Louis Bossart struct sdw_cdns_dma_data *dma; 108009553140SPierre-Louis Bossart 108109553140SPierre-Louis Bossart if (direction == SNDRV_PCM_STREAM_PLAYBACK) 108209553140SPierre-Louis Bossart dma = dai->playback_dma_data; 108309553140SPierre-Louis Bossart else 108409553140SPierre-Louis Bossart dma = dai->capture_dma_data; 108509553140SPierre-Louis Bossart 108609553140SPierre-Louis Bossart if (!dma) 108706dcb4e4SPierre-Louis Bossart return ERR_PTR(-EINVAL); 108809553140SPierre-Louis Bossart 108909553140SPierre-Louis Bossart return dma->stream; 109009553140SPierre-Louis Bossart } 109109553140SPierre-Louis Bossart 1092b1635596SJulia Lawall static const struct snd_soc_dai_ops intel_pcm_dai_ops = { 10935e7484d0SRander Wang .startup = intel_startup, 1094c46302ecSVinod Koul .hw_params = intel_hw_params, 109527b198f4SRander Wang .prepare = intel_prepare, 1096c46302ecSVinod Koul .hw_free = intel_hw_free, 1097183c7687SPierre-Louis Bossart .shutdown = intel_shutdown, 1098c46302ecSVinod Koul .set_sdw_stream = intel_pcm_set_sdw_stream, 109909553140SPierre-Louis Bossart .get_sdw_stream = intel_get_sdw_stream, 1100c46302ecSVinod Koul }; 1101c46302ecSVinod Koul 1102b1635596SJulia Lawall static const struct snd_soc_dai_ops intel_pdm_dai_ops = { 11035e7484d0SRander Wang .startup = intel_startup, 1104c46302ecSVinod Koul .hw_params = intel_hw_params, 110527b198f4SRander Wang .prepare = intel_prepare, 1106c46302ecSVinod Koul .hw_free = intel_hw_free, 1107183c7687SPierre-Louis Bossart .shutdown = intel_shutdown, 1108c46302ecSVinod Koul .set_sdw_stream = intel_pdm_set_sdw_stream, 110909553140SPierre-Louis Bossart .get_sdw_stream = intel_get_sdw_stream, 1110c46302ecSVinod Koul }; 1111c46302ecSVinod Koul 1112c46302ecSVinod Koul static const struct snd_soc_component_driver dai_component = { 1113c46302ecSVinod Koul .name = "soundwire", 1114a5a0239cSBard Liao .suspend = intel_component_dais_suspend 1115c46302ecSVinod Koul }; 1116c46302ecSVinod Koul 1117c46302ecSVinod Koul static int intel_create_dai(struct sdw_cdns *cdns, 1118c46302ecSVinod Koul struct snd_soc_dai_driver *dais, 1119c46302ecSVinod Koul enum intel_pdi_type type, 1120c46302ecSVinod Koul u32 num, u32 off, u32 max_ch, bool pcm) 1121c46302ecSVinod Koul { 1122c46302ecSVinod Koul int i; 1123c46302ecSVinod Koul 1124c46302ecSVinod Koul if (num == 0) 1125c46302ecSVinod Koul return 0; 1126c46302ecSVinod Koul 1127c46302ecSVinod Koul /* TODO: Read supported rates/formats from hardware */ 1128c46302ecSVinod Koul for (i = off; i < (off + num); i++) { 1129bf6d6e68SPierre-Louis Bossart dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL, 1130bf6d6e68SPierre-Louis Bossart "SDW%d Pin%d", 1131c46302ecSVinod Koul cdns->instance, i); 1132c46302ecSVinod Koul if (!dais[i].name) 1133c46302ecSVinod Koul return -ENOMEM; 1134c46302ecSVinod Koul 1135c46302ecSVinod Koul if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { 1136c46302ecSVinod Koul dais[i].playback.channels_min = 1; 1137c46302ecSVinod Koul dais[i].playback.channels_max = max_ch; 1138c46302ecSVinod Koul dais[i].playback.rates = SNDRV_PCM_RATE_48000; 1139c46302ecSVinod Koul dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE; 1140c46302ecSVinod Koul } 1141c46302ecSVinod Koul 1142c46302ecSVinod Koul if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { 114339194128SSrinivas Kandagatla dais[i].capture.channels_min = 1; 114439194128SSrinivas Kandagatla dais[i].capture.channels_max = max_ch; 1145c46302ecSVinod Koul dais[i].capture.rates = SNDRV_PCM_RATE_48000; 1146c46302ecSVinod Koul dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; 1147c46302ecSVinod Koul } 1148c46302ecSVinod Koul 1149c46302ecSVinod Koul if (pcm) 1150c46302ecSVinod Koul dais[i].ops = &intel_pcm_dai_ops; 1151c46302ecSVinod Koul else 1152c46302ecSVinod Koul dais[i].ops = &intel_pdm_dai_ops; 1153c46302ecSVinod Koul } 1154c46302ecSVinod Koul 1155c46302ecSVinod Koul return 0; 1156c46302ecSVinod Koul } 1157c46302ecSVinod Koul 1158c46302ecSVinod Koul static int intel_register_dai(struct sdw_intel *sdw) 1159c46302ecSVinod Koul { 1160c46302ecSVinod Koul struct sdw_cdns *cdns = &sdw->cdns; 1161c46302ecSVinod Koul struct sdw_cdns_streams *stream; 1162c46302ecSVinod Koul struct snd_soc_dai_driver *dais; 1163c46302ecSVinod Koul int num_dai, ret, off = 0; 1164c46302ecSVinod Koul 1165c46302ecSVinod Koul /* DAIs are created based on total number of PDIs supported */ 1166c46302ecSVinod Koul num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi; 1167c46302ecSVinod Koul 1168c46302ecSVinod Koul dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); 1169c46302ecSVinod Koul if (!dais) 1170c46302ecSVinod Koul return -ENOMEM; 1171c46302ecSVinod Koul 1172c46302ecSVinod Koul /* Create PCM DAIs */ 1173c46302ecSVinod Koul stream = &cdns->pcm; 1174c46302ecSVinod Koul 1175cf924962SBard Liao ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in, 11761215daeeSVinod Koul off, stream->num_ch_in, true); 1177c46302ecSVinod Koul if (ret) 1178c46302ecSVinod Koul return ret; 1179c46302ecSVinod Koul 1180c46302ecSVinod Koul off += cdns->pcm.num_in; 11811215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out, 11821215daeeSVinod Koul off, stream->num_ch_out, true); 1183c46302ecSVinod Koul if (ret) 1184c46302ecSVinod Koul return ret; 1185c46302ecSVinod Koul 1186c46302ecSVinod Koul off += cdns->pcm.num_out; 11871215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd, 11881215daeeSVinod Koul off, stream->num_ch_bd, true); 1189c46302ecSVinod Koul if (ret) 1190c46302ecSVinod Koul return ret; 1191c46302ecSVinod Koul 1192c46302ecSVinod Koul /* Create PDM DAIs */ 1193c46302ecSVinod Koul stream = &cdns->pdm; 1194c46302ecSVinod Koul off += cdns->pcm.num_bd; 11951215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pdm.num_in, 11961215daeeSVinod Koul off, stream->num_ch_in, false); 1197c46302ecSVinod Koul if (ret) 1198c46302ecSVinod Koul return ret; 1199c46302ecSVinod Koul 1200c46302ecSVinod Koul off += cdns->pdm.num_in; 12011215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pdm.num_out, 12021215daeeSVinod Koul off, stream->num_ch_out, false); 1203c46302ecSVinod Koul if (ret) 1204c46302ecSVinod Koul return ret; 1205c46302ecSVinod Koul 1206cf924962SBard Liao off += cdns->pdm.num_out; 12071215daeeSVinod Koul ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pdm.num_bd, 12081215daeeSVinod Koul off, stream->num_ch_bd, false); 1209c46302ecSVinod Koul if (ret) 1210c46302ecSVinod Koul return ret; 1211c46302ecSVinod Koul 1212c46302ecSVinod Koul return snd_soc_register_component(cdns->dev, &dai_component, 1213c46302ecSVinod Koul dais, num_dai); 1214c46302ecSVinod Koul } 1215c46302ecSVinod Koul 1216085f4aceSPierre-Louis Bossart static int sdw_master_read_intel_prop(struct sdw_bus *bus) 1217085f4aceSPierre-Louis Bossart { 1218085f4aceSPierre-Louis Bossart struct sdw_master_prop *prop = &bus->prop; 1219085f4aceSPierre-Louis Bossart struct fwnode_handle *link; 1220085f4aceSPierre-Louis Bossart char name[32]; 1221395713d8SPierre-Louis Bossart u32 quirk_mask; 1222085f4aceSPierre-Louis Bossart 1223085f4aceSPierre-Louis Bossart /* Find master handle */ 1224085f4aceSPierre-Louis Bossart snprintf(name, sizeof(name), 1225085f4aceSPierre-Louis Bossart "mipi-sdw-link-%d-subproperties", bus->link_id); 1226085f4aceSPierre-Louis Bossart 1227085f4aceSPierre-Louis Bossart link = device_get_named_child_node(bus->dev, name); 1228085f4aceSPierre-Louis Bossart if (!link) { 1229085f4aceSPierre-Louis Bossart dev_err(bus->dev, "Master node %s not found\n", name); 1230085f4aceSPierre-Louis Bossart return -EIO; 1231085f4aceSPierre-Louis Bossart } 1232085f4aceSPierre-Louis Bossart 1233085f4aceSPierre-Louis Bossart fwnode_property_read_u32(link, 1234085f4aceSPierre-Louis Bossart "intel-sdw-ip-clock", 1235085f4aceSPierre-Louis Bossart &prop->mclk_freq); 1236395713d8SPierre-Louis Bossart 1237a19efb52SBard Liao /* the values reported by BIOS are the 2x clock, not the bus clock */ 1238a19efb52SBard Liao prop->mclk_freq /= 2; 1239a19efb52SBard Liao 1240395713d8SPierre-Louis Bossart fwnode_property_read_u32(link, 1241395713d8SPierre-Louis Bossart "intel-quirk-mask", 1242395713d8SPierre-Louis Bossart &quirk_mask); 1243395713d8SPierre-Louis Bossart 1244395713d8SPierre-Louis Bossart if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) 1245395713d8SPierre-Louis Bossart prop->hw_disabled = true; 1246395713d8SPierre-Louis Bossart 1247085f4aceSPierre-Louis Bossart return 0; 1248085f4aceSPierre-Louis Bossart } 1249085f4aceSPierre-Louis Bossart 125071bb8a1bSVinod Koul static int intel_prop_read(struct sdw_bus *bus) 125171bb8a1bSVinod Koul { 125271bb8a1bSVinod Koul /* Initialize with default handler to read all DisCo properties */ 125371bb8a1bSVinod Koul sdw_master_read_prop(bus); 125471bb8a1bSVinod Koul 1255085f4aceSPierre-Louis Bossart /* read Intel-specific properties */ 1256085f4aceSPierre-Louis Bossart sdw_master_read_intel_prop(bus); 1257085f4aceSPierre-Louis Bossart 125871bb8a1bSVinod Koul return 0; 125971bb8a1bSVinod Koul } 126071bb8a1bSVinod Koul 1261c91605f4SShreyas NC static struct sdw_master_ops sdw_intel_ops = { 1262c91605f4SShreyas NC .read_prop = sdw_master_read_prop, 1263c91605f4SShreyas NC .xfer_msg = cdns_xfer_msg, 1264c91605f4SShreyas NC .xfer_msg_defer = cdns_xfer_msg_defer, 1265c91605f4SShreyas NC .reset_page_addr = cdns_reset_page_addr, 126607abeff1SVinod Koul .set_bus_conf = cdns_bus_conf, 126730246e2dSShreyas NC .pre_bank_switch = intel_pre_bank_switch, 126830246e2dSShreyas NC .post_bank_switch = intel_post_bank_switch, 1269c91605f4SShreyas NC }; 1270c91605f4SShreyas NC 1271dfbe642dSPierre-Louis Bossart static int intel_init(struct sdw_intel *sdw) 1272dfbe642dSPierre-Louis Bossart { 12734a17c441SPierre-Louis Bossart bool clock_stop; 12744a17c441SPierre-Louis Bossart 1275dfbe642dSPierre-Louis Bossart /* Initialize shim and controller */ 1276dfbe642dSPierre-Louis Bossart intel_link_power_up(sdw); 12774a17c441SPierre-Louis Bossart 12784a17c441SPierre-Louis Bossart clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns); 12794a17c441SPierre-Louis Bossart 12804a17c441SPierre-Louis Bossart intel_shim_init(sdw, clock_stop); 12814a17c441SPierre-Louis Bossart 12824a17c441SPierre-Louis Bossart return 0; 1283dfbe642dSPierre-Louis Bossart } 1284dfbe642dSPierre-Louis Bossart 128571bb8a1bSVinod Koul /* 128671bb8a1bSVinod Koul * probe and init 128771bb8a1bSVinod Koul */ 1288b6109dd6SPierre-Louis Bossart static int intel_master_probe(struct platform_device *pdev) 128971bb8a1bSVinod Koul { 1290b6109dd6SPierre-Louis Bossart struct device *dev = &pdev->dev; 129171bb8a1bSVinod Koul struct sdw_intel *sdw; 129283e129afSPierre-Louis Bossart struct sdw_cdns *cdns; 1293b6109dd6SPierre-Louis Bossart struct sdw_bus *bus; 129471bb8a1bSVinod Koul int ret; 129571bb8a1bSVinod Koul 1296b6109dd6SPierre-Louis Bossart sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL); 129771bb8a1bSVinod Koul if (!sdw) 129871bb8a1bSVinod Koul return -ENOMEM; 129971bb8a1bSVinod Koul 130083e129afSPierre-Louis Bossart cdns = &sdw->cdns; 130183e129afSPierre-Louis Bossart bus = &cdns->bus; 130271bb8a1bSVinod Koul 130371bb8a1bSVinod Koul sdw->instance = pdev->id; 1304b6109dd6SPierre-Louis Bossart sdw->link_res = dev_get_platdata(dev); 130583e129afSPierre-Louis Bossart cdns->dev = dev; 130683e129afSPierre-Louis Bossart cdns->registers = sdw->link_res->registers; 130783e129afSPierre-Louis Bossart cdns->instance = sdw->instance; 130883e129afSPierre-Louis Bossart cdns->msg_count = 0; 130983e129afSPierre-Louis Bossart 1310b6109dd6SPierre-Louis Bossart bus->link_id = pdev->id; 131171bb8a1bSVinod Koul 131283e129afSPierre-Louis Bossart sdw_cdns_probe(cdns); 131371bb8a1bSVinod Koul 131471bb8a1bSVinod Koul /* Set property read ops */ 1315c91605f4SShreyas NC sdw_intel_ops.read_prop = intel_prop_read; 1316b6109dd6SPierre-Louis Bossart bus->ops = &sdw_intel_ops; 131771bb8a1bSVinod Koul 1318b6109dd6SPierre-Louis Bossart /* set driver data, accessed by snd_soc_dai_get_drvdata() */ 131983e129afSPierre-Louis Bossart dev_set_drvdata(dev, cdns); 132071bb8a1bSVinod Koul 13219026118fSBard Liao /* use generic bandwidth allocation algorithm */ 13229026118fSBard Liao sdw->cdns.bus.compute_params = sdw_compute_params; 13239026118fSBard Liao 1324b6109dd6SPierre-Louis Bossart ret = sdw_bus_master_add(bus, dev, dev->fwnode); 132571bb8a1bSVinod Koul if (ret) { 1326b6109dd6SPierre-Louis Bossart dev_err(dev, "sdw_bus_master_add fail: %d\n", ret); 13279e3d47fbSPierre-Louis Bossart return ret; 132871bb8a1bSVinod Koul } 132971bb8a1bSVinod Koul 13306d2c6669SPierre-Louis Bossart if (bus->prop.hw_disabled) 1331b6109dd6SPierre-Louis Bossart dev_info(dev, 1332b6109dd6SPierre-Louis Bossart "SoundWire master %d is disabled, will be ignored\n", 1333b6109dd6SPierre-Louis Bossart bus->link_id); 13340ef2986eSPierre-Louis Bossart /* 13350ef2986eSPierre-Louis Bossart * Ignore BIOS err_threshold, it's a really bad idea when dealing 13360ef2986eSPierre-Louis Bossart * with multiple hardware synchronized links 13370ef2986eSPierre-Louis Bossart */ 13380ef2986eSPierre-Louis Bossart bus->prop.err_threshold = 0; 13396d2c6669SPierre-Louis Bossart 13406d2c6669SPierre-Louis Bossart return 0; 13416d2c6669SPierre-Louis Bossart } 13426d2c6669SPierre-Louis Bossart 13436d2c6669SPierre-Louis Bossart int intel_master_startup(struct platform_device *pdev) 13446d2c6669SPierre-Louis Bossart { 13456d2c6669SPierre-Louis Bossart struct sdw_cdns_stream_config config; 13466d2c6669SPierre-Louis Bossart struct device *dev = &pdev->dev; 13476d2c6669SPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 13486d2c6669SPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 13496d2c6669SPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1350ebf878edSPierre-Louis Bossart int link_flags; 1351857a7c42SPierre-Louis Bossart bool multi_link; 1352caf68819SPierre-Louis Bossart u32 clock_stop_quirks; 13536d2c6669SPierre-Louis Bossart int ret; 13546d2c6669SPierre-Louis Bossart 13556d2c6669SPierre-Louis Bossart if (bus->prop.hw_disabled) { 13566d2c6669SPierre-Louis Bossart dev_info(dev, 13576d2c6669SPierre-Louis Bossart "SoundWire master %d is disabled, ignoring\n", 13586d2c6669SPierre-Louis Bossart sdw->instance); 1359395713d8SPierre-Louis Bossart return 0; 1360395713d8SPierre-Louis Bossart } 1361395713d8SPierre-Louis Bossart 1362857a7c42SPierre-Louis Bossart link_flags = md_flags >> (bus->link_id * 8); 1363857a7c42SPierre-Louis Bossart multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); 1364857a7c42SPierre-Louis Bossart if (!multi_link) { 1365857a7c42SPierre-Louis Bossart dev_dbg(dev, "Multi-link is disabled\n"); 1366857a7c42SPierre-Louis Bossart bus->multi_link = false; 1367857a7c42SPierre-Louis Bossart } else { 136894eed661SPierre-Louis Bossart /* 136994eed661SPierre-Louis Bossart * hardware-based synchronization is required regardless 137094eed661SPierre-Louis Bossart * of the number of segments used by a stream: SSP-based 137194eed661SPierre-Louis Bossart * synchronization is gated by gsync when the multi-master 137294eed661SPierre-Louis Bossart * mode is set. 137394eed661SPierre-Louis Bossart */ 1374857a7c42SPierre-Louis Bossart bus->multi_link = true; 137594eed661SPierre-Louis Bossart bus->hw_sync_min_links = 1; 1376857a7c42SPierre-Louis Bossart } 1377857a7c42SPierre-Louis Bossart 1378857a7c42SPierre-Louis Bossart /* Initialize shim, controller */ 1379dfbe642dSPierre-Louis Bossart ret = intel_init(sdw); 138071bb8a1bSVinod Koul if (ret) 138171bb8a1bSVinod Koul goto err_init; 138271bb8a1bSVinod Koul 138337a2d22bSVinod Koul /* Read the PDI config and initialize cadence PDI */ 138437a2d22bSVinod Koul intel_pdi_init(sdw, &config); 138583e129afSPierre-Louis Bossart ret = sdw_cdns_pdi_init(cdns, config); 138671bb8a1bSVinod Koul if (ret) 138771bb8a1bSVinod Koul goto err_init; 138871bb8a1bSVinod Koul 138937a2d22bSVinod Koul intel_pdi_ch_update(sdw); 139037a2d22bSVinod Koul 139183e129afSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 139271bb8a1bSVinod Koul if (ret < 0) { 1393b6109dd6SPierre-Louis Bossart dev_err(dev, "cannot enable interrupts\n"); 139471bb8a1bSVinod Koul goto err_init; 139571bb8a1bSVinod Koul } 139671bb8a1bSVinod Koul 1397857a7c42SPierre-Louis Bossart /* 1398857a7c42SPierre-Louis Bossart * follow recommended programming flows to avoid timeouts when 1399857a7c42SPierre-Louis Bossart * gsync is enabled 1400857a7c42SPierre-Louis Bossart */ 1401857a7c42SPierre-Louis Bossart if (multi_link) 1402857a7c42SPierre-Louis Bossart intel_shim_sync_arm(sdw); 1403857a7c42SPierre-Louis Bossart 1404857a7c42SPierre-Louis Bossart ret = sdw_cdns_init(cdns); 1405857a7c42SPierre-Louis Bossart if (ret < 0) { 1406857a7c42SPierre-Louis Bossart dev_err(dev, "unable to initialize Cadence IP\n"); 1407857a7c42SPierre-Louis Bossart goto err_interrupt; 1408857a7c42SPierre-Louis Bossart } 1409857a7c42SPierre-Louis Bossart 141083e129afSPierre-Louis Bossart ret = sdw_cdns_exit_reset(cdns); 141149ea07d3SPierre-Louis Bossart if (ret < 0) { 1412b6109dd6SPierre-Louis Bossart dev_err(dev, "unable to exit bus reset sequence\n"); 14139e3d47fbSPierre-Louis Bossart goto err_interrupt; 141449ea07d3SPierre-Louis Bossart } 141549ea07d3SPierre-Louis Bossart 1416857a7c42SPierre-Louis Bossart if (multi_link) { 1417857a7c42SPierre-Louis Bossart ret = intel_shim_sync_go(sdw); 1418857a7c42SPierre-Louis Bossart if (ret < 0) { 1419857a7c42SPierre-Louis Bossart dev_err(dev, "sync go failed: %d\n", ret); 1420857a7c42SPierre-Louis Bossart goto err_interrupt; 1421857a7c42SPierre-Louis Bossart } 1422857a7c42SPierre-Louis Bossart } 1423857a7c42SPierre-Louis Bossart 1424c46302ecSVinod Koul /* Register DAIs */ 1425c46302ecSVinod Koul ret = intel_register_dai(sdw); 1426c46302ecSVinod Koul if (ret) { 1427b6109dd6SPierre-Louis Bossart dev_err(dev, "DAI registration failed: %d\n", ret); 1428b6109dd6SPierre-Louis Bossart snd_soc_unregister_component(dev); 14299e3d47fbSPierre-Louis Bossart goto err_interrupt; 1430c46302ecSVinod Koul } 1431c46302ecSVinod Koul 143279ee6631SPierre-Louis Bossart intel_debugfs_init(sdw); 143379ee6631SPierre-Louis Bossart 1434ebf878edSPierre-Louis Bossart /* Enable runtime PM */ 1435ebf878edSPierre-Louis Bossart if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) { 1436ebf878edSPierre-Louis Bossart pm_runtime_set_autosuspend_delay(dev, 1437ebf878edSPierre-Louis Bossart INTEL_MASTER_SUSPEND_DELAY_MS); 1438ebf878edSPierre-Louis Bossart pm_runtime_use_autosuspend(dev); 1439ebf878edSPierre-Louis Bossart pm_runtime_mark_last_busy(dev); 1440ebf878edSPierre-Louis Bossart 1441ebf878edSPierre-Louis Bossart pm_runtime_set_active(dev); 1442ebf878edSPierre-Louis Bossart pm_runtime_enable(dev); 1443ebf878edSPierre-Louis Bossart } 1444ebf878edSPierre-Louis Bossart 1445caf68819SPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1446caf68819SPierre-Louis Bossart if (clock_stop_quirks & SDW_INTEL_CLK_STOP_NOT_ALLOWED) { 1447caf68819SPierre-Louis Bossart /* 1448caf68819SPierre-Louis Bossart * To keep the clock running we need to prevent 1449caf68819SPierre-Louis Bossart * pm_runtime suspend from happening by increasing the 1450caf68819SPierre-Louis Bossart * reference count. 1451caf68819SPierre-Louis Bossart * This quirk is specified by the parent PCI device in 1452caf68819SPierre-Louis Bossart * case of specific latency requirements. It will have 1453caf68819SPierre-Louis Bossart * no effect if pm_runtime is disabled by the user via 1454caf68819SPierre-Louis Bossart * a module parameter for testing purposes. 1455caf68819SPierre-Louis Bossart */ 1456caf68819SPierre-Louis Bossart pm_runtime_get_noresume(dev); 1457caf68819SPierre-Louis Bossart } 1458caf68819SPierre-Louis Bossart 1459a2d9c161SPierre-Louis Bossart /* 1460a2d9c161SPierre-Louis Bossart * The runtime PM status of Slave devices is "Unsupported" 1461a2d9c161SPierre-Louis Bossart * until they report as ATTACHED. If they don't, e.g. because 1462a2d9c161SPierre-Louis Bossart * there are no Slave devices populated or if the power-on is 1463a2d9c161SPierre-Louis Bossart * delayed or dependent on a power switch, the Master will 1464a2d9c161SPierre-Louis Bossart * remain active and prevent its parent from suspending. 1465a2d9c161SPierre-Louis Bossart * 1466a2d9c161SPierre-Louis Bossart * Conditionally force the pm_runtime core to re-evaluate the 1467a2d9c161SPierre-Louis Bossart * Master status in the absence of any Slave activity. A quirk 1468a2d9c161SPierre-Louis Bossart * is provided to e.g. deal with Slaves that may be powered on 1469a2d9c161SPierre-Louis Bossart * with a delay. A more complete solution would require the 1470a2d9c161SPierre-Louis Bossart * definition of Master properties. 1471a2d9c161SPierre-Louis Bossart */ 1472a2d9c161SPierre-Louis Bossart if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) 1473a2d9c161SPierre-Louis Bossart pm_runtime_idle(dev); 1474a2d9c161SPierre-Louis Bossart 147571bb8a1bSVinod Koul return 0; 147671bb8a1bSVinod Koul 14779e3d47fbSPierre-Louis Bossart err_interrupt: 147883e129afSPierre-Louis Bossart sdw_cdns_enable_interrupt(cdns, false); 147971bb8a1bSVinod Koul err_init: 148071bb8a1bSVinod Koul return ret; 148171bb8a1bSVinod Koul } 148271bb8a1bSVinod Koul 1483b6109dd6SPierre-Louis Bossart static int intel_master_remove(struct platform_device *pdev) 148471bb8a1bSVinod Koul { 1485b6109dd6SPierre-Louis Bossart struct device *dev = &pdev->dev; 148683e129afSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 148783e129afSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 148883e129afSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1489b6109dd6SPierre-Louis Bossart 1490caf68819SPierre-Louis Bossart /* 1491caf68819SPierre-Louis Bossart * Since pm_runtime is already disabled, we don't decrease 1492caf68819SPierre-Louis Bossart * the refcount when the clock_stop_quirk is 1493caf68819SPierre-Louis Bossart * SDW_INTEL_CLK_STOP_NOT_ALLOWED 1494caf68819SPierre-Louis Bossart */ 1495b6109dd6SPierre-Louis Bossart if (!bus->prop.hw_disabled) { 149679ee6631SPierre-Louis Bossart intel_debugfs_exit(sdw); 149783e129afSPierre-Louis Bossart sdw_cdns_enable_interrupt(cdns, false); 1498b6109dd6SPierre-Louis Bossart snd_soc_unregister_component(dev); 1499395713d8SPierre-Louis Bossart } 1500b6109dd6SPierre-Louis Bossart sdw_bus_master_delete(bus); 150171bb8a1bSVinod Koul 150271bb8a1bSVinod Koul return 0; 150371bb8a1bSVinod Koul } 150471bb8a1bSVinod Koul 1505ab2c9132SRander Wang int intel_master_process_wakeen_event(struct platform_device *pdev) 1506ab2c9132SRander Wang { 1507ab2c9132SRander Wang struct device *dev = &pdev->dev; 150871bb8a1bSVinod Koul struct sdw_intel *sdw; 1509ab2c9132SRander Wang struct sdw_bus *bus; 1510ab2c9132SRander Wang void __iomem *shim; 1511ab2c9132SRander Wang u16 wake_sts; 151271bb8a1bSVinod Koul 151371bb8a1bSVinod Koul sdw = platform_get_drvdata(pdev); 1514ab2c9132SRander Wang bus = &sdw->cdns.bus; 151571bb8a1bSVinod Koul 1516ab2c9132SRander Wang if (bus->prop.hw_disabled) { 1517ab2c9132SRander Wang dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", bus->link_id); 1518ab2c9132SRander Wang return 0; 151971bb8a1bSVinod Koul } 1520ab2c9132SRander Wang 1521ab2c9132SRander Wang shim = sdw->link_res->shim; 1522ab2c9132SRander Wang wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); 1523ab2c9132SRander Wang 1524ab2c9132SRander Wang if (!(wake_sts & BIT(sdw->instance))) 1525ab2c9132SRander Wang return 0; 1526ab2c9132SRander Wang 1527ab2c9132SRander Wang /* disable WAKEEN interrupt ASAP to prevent interrupt flood */ 1528ab2c9132SRander Wang intel_shim_wake(sdw, false); 1529ab2c9132SRander Wang 1530ab2c9132SRander Wang /* 1531ab2c9132SRander Wang * resume the Master, which will generate a bus reset and result in 1532ab2c9132SRander Wang * Slaves re-attaching and be re-enumerated. The SoundWire physical 1533ab2c9132SRander Wang * device which generated the wake will trigger an interrupt, which 1534ab2c9132SRander Wang * will in turn cause the corresponding Linux Slave device to be 1535ab2c9132SRander Wang * resumed and the Slave codec driver to check the status. 1536ab2c9132SRander Wang */ 1537ab2c9132SRander Wang pm_request_resume(dev); 153871bb8a1bSVinod Koul 153971bb8a1bSVinod Koul return 0; 154071bb8a1bSVinod Koul } 154171bb8a1bSVinod Koul 15429b3b4b3fSPierre-Louis Bossart /* 15439b3b4b3fSPierre-Louis Bossart * PM calls 15449b3b4b3fSPierre-Louis Bossart */ 15459b3b4b3fSPierre-Louis Bossart 15469b3b4b3fSPierre-Louis Bossart #ifdef CONFIG_PM 15479b3b4b3fSPierre-Louis Bossart 1548f046b233SBard Liao static int __maybe_unused intel_suspend(struct device *dev) 15499b3b4b3fSPierre-Louis Bossart { 15509b3b4b3fSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 15519b3b4b3fSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 15529b3b4b3fSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1553e4be9facSPierre-Louis Bossart u32 clock_stop_quirks; 15549b3b4b3fSPierre-Louis Bossart int ret; 15559b3b4b3fSPierre-Louis Bossart 15569b3b4b3fSPierre-Louis Bossart if (bus->prop.hw_disabled) { 15579b3b4b3fSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", 15589b3b4b3fSPierre-Louis Bossart bus->link_id); 15599b3b4b3fSPierre-Louis Bossart return 0; 15609b3b4b3fSPierre-Louis Bossart } 15619b3b4b3fSPierre-Louis Bossart 1562b61b8b37SPierre-Louis Bossart if (pm_runtime_suspended(dev)) { 1563b61b8b37SPierre-Louis Bossart dev_dbg(dev, "%s: pm_runtime status: suspended\n", __func__); 1564b61b8b37SPierre-Louis Bossart 1565e4be9facSPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1566e4be9facSPierre-Louis Bossart 1567e4be9facSPierre-Louis Bossart if ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || 1568e4be9facSPierre-Louis Bossart !clock_stop_quirks) && 1569e4be9facSPierre-Louis Bossart !pm_runtime_suspended(dev->parent)) { 1570e4be9facSPierre-Louis Bossart 1571e4be9facSPierre-Louis Bossart /* 1572e4be9facSPierre-Louis Bossart * if we've enabled clock stop, and the parent 1573e4be9facSPierre-Louis Bossart * is still active, disable shim wake. The 1574e4be9facSPierre-Louis Bossart * SHIM registers are not accessible if the 1575e4be9facSPierre-Louis Bossart * parent is already pm_runtime suspended so 1576e4be9facSPierre-Louis Bossart * it's too late to change that configuration 1577e4be9facSPierre-Louis Bossart */ 1578e4be9facSPierre-Louis Bossart 1579e4be9facSPierre-Louis Bossart intel_shim_wake(sdw, false); 1580e4be9facSPierre-Louis Bossart } 1581e4be9facSPierre-Louis Bossart 1582b61b8b37SPierre-Louis Bossart return 0; 1583b61b8b37SPierre-Louis Bossart } 1584b61b8b37SPierre-Louis Bossart 15859b3b4b3fSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, false); 15869b3b4b3fSPierre-Louis Bossart if (ret < 0) { 15879b3b4b3fSPierre-Louis Bossart dev_err(dev, "cannot disable interrupts on suspend\n"); 15889b3b4b3fSPierre-Louis Bossart return ret; 15899b3b4b3fSPierre-Louis Bossart } 15909b3b4b3fSPierre-Louis Bossart 15919b3b4b3fSPierre-Louis Bossart ret = intel_link_power_down(sdw); 15929b3b4b3fSPierre-Louis Bossart if (ret) { 15939b3b4b3fSPierre-Louis Bossart dev_err(dev, "Link power down failed: %d", ret); 15949b3b4b3fSPierre-Louis Bossart return ret; 15959b3b4b3fSPierre-Louis Bossart } 15969b3b4b3fSPierre-Louis Bossart 15979b3b4b3fSPierre-Louis Bossart intel_shim_wake(sdw, false); 15989b3b4b3fSPierre-Louis Bossart 15999b3b4b3fSPierre-Louis Bossart return 0; 16009b3b4b3fSPierre-Louis Bossart } 16019b3b4b3fSPierre-Louis Bossart 1602ebf878edSPierre-Louis Bossart static int intel_suspend_runtime(struct device *dev) 1603ebf878edSPierre-Louis Bossart { 1604ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 1605ebf878edSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 1606ebf878edSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1607a320f41eSPierre-Louis Bossart u32 clock_stop_quirks; 1608ebf878edSPierre-Louis Bossart int ret; 1609ebf878edSPierre-Louis Bossart 1610ebf878edSPierre-Louis Bossart if (bus->prop.hw_disabled) { 1611ebf878edSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", 1612ebf878edSPierre-Louis Bossart bus->link_id); 1613ebf878edSPierre-Louis Bossart return 0; 1614ebf878edSPierre-Louis Bossart } 1615ebf878edSPierre-Louis Bossart 1616a320f41eSPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1617a320f41eSPierre-Louis Bossart 1618a320f41eSPierre-Louis Bossart if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { 1619a320f41eSPierre-Louis Bossart 1620ebf878edSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, false); 1621ebf878edSPierre-Louis Bossart if (ret < 0) { 1622ebf878edSPierre-Louis Bossart dev_err(dev, "cannot disable interrupts on suspend\n"); 1623ebf878edSPierre-Louis Bossart return ret; 1624ebf878edSPierre-Louis Bossart } 1625ebf878edSPierre-Louis Bossart 1626ebf878edSPierre-Louis Bossart ret = intel_link_power_down(sdw); 1627ebf878edSPierre-Louis Bossart if (ret) { 1628ebf878edSPierre-Louis Bossart dev_err(dev, "Link power down failed: %d", ret); 1629ebf878edSPierre-Louis Bossart return ret; 1630ebf878edSPierre-Louis Bossart } 1631ebf878edSPierre-Louis Bossart 1632ebf878edSPierre-Louis Bossart intel_shim_wake(sdw, false); 1633ebf878edSPierre-Louis Bossart 163461fb830bSPierre-Louis Bossart } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || 163561fb830bSPierre-Louis Bossart !clock_stop_quirks) { 16366626a616SRander Wang ret = sdw_cdns_clock_stop(cdns, true); 16376626a616SRander Wang if (ret < 0) { 16386626a616SRander Wang dev_err(dev, "cannot enable clock stop on suspend\n"); 16396626a616SRander Wang return ret; 16406626a616SRander Wang } 16416626a616SRander Wang 16426626a616SRander Wang ret = sdw_cdns_enable_interrupt(cdns, false); 16436626a616SRander Wang if (ret < 0) { 16446626a616SRander Wang dev_err(dev, "cannot disable interrupts on suspend\n"); 16456626a616SRander Wang return ret; 16466626a616SRander Wang } 16476626a616SRander Wang 16486626a616SRander Wang ret = intel_link_power_down(sdw); 16496626a616SRander Wang if (ret) { 16506626a616SRander Wang dev_err(dev, "Link power down failed: %d", ret); 16516626a616SRander Wang return ret; 16526626a616SRander Wang } 16536626a616SRander Wang 16546626a616SRander Wang intel_shim_wake(sdw, true); 1655a320f41eSPierre-Louis Bossart } else { 1656a320f41eSPierre-Louis Bossart dev_err(dev, "%s clock_stop_quirks %x unsupported\n", 1657a320f41eSPierre-Louis Bossart __func__, clock_stop_quirks); 1658a320f41eSPierre-Louis Bossart ret = -EINVAL; 1659a320f41eSPierre-Louis Bossart } 1660a320f41eSPierre-Louis Bossart 1661a320f41eSPierre-Louis Bossart return ret; 1662ebf878edSPierre-Louis Bossart } 1663ebf878edSPierre-Louis Bossart 1664f046b233SBard Liao static int __maybe_unused intel_resume(struct device *dev) 16659b3b4b3fSPierre-Louis Bossart { 16669b3b4b3fSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 16679b3b4b3fSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 16689b3b4b3fSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1669a2d9c161SPierre-Louis Bossart int link_flags; 1670857a7c42SPierre-Louis Bossart bool multi_link; 16719b3b4b3fSPierre-Louis Bossart int ret; 16729b3b4b3fSPierre-Louis Bossart 16739b3b4b3fSPierre-Louis Bossart if (bus->prop.hw_disabled) { 16749b3b4b3fSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", 16759b3b4b3fSPierre-Louis Bossart bus->link_id); 16769b3b4b3fSPierre-Louis Bossart return 0; 16779b3b4b3fSPierre-Louis Bossart } 16789b3b4b3fSPierre-Louis Bossart 1679857a7c42SPierre-Louis Bossart link_flags = md_flags >> (bus->link_id * 8); 1680857a7c42SPierre-Louis Bossart multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); 1681857a7c42SPierre-Louis Bossart 1682b61b8b37SPierre-Louis Bossart if (pm_runtime_suspended(dev)) { 1683b61b8b37SPierre-Louis Bossart dev_dbg(dev, "%s: pm_runtime status was suspended, forcing active\n", __func__); 1684b61b8b37SPierre-Louis Bossart 1685b61b8b37SPierre-Louis Bossart /* follow required sequence from runtime_pm.rst */ 1686b61b8b37SPierre-Louis Bossart pm_runtime_disable(dev); 1687b61b8b37SPierre-Louis Bossart pm_runtime_set_active(dev); 1688b61b8b37SPierre-Louis Bossart pm_runtime_mark_last_busy(dev); 1689b61b8b37SPierre-Louis Bossart pm_runtime_enable(dev); 1690a2d9c161SPierre-Louis Bossart 1691a2d9c161SPierre-Louis Bossart link_flags = md_flags >> (bus->link_id * 8); 1692857a7c42SPierre-Louis Bossart 1693a2d9c161SPierre-Louis Bossart if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) 1694a2d9c161SPierre-Louis Bossart pm_runtime_idle(dev); 1695b61b8b37SPierre-Louis Bossart } 1696b61b8b37SPierre-Louis Bossart 16979b3b4b3fSPierre-Louis Bossart ret = intel_init(sdw); 16989b3b4b3fSPierre-Louis Bossart if (ret) { 16999b3b4b3fSPierre-Louis Bossart dev_err(dev, "%s failed: %d", __func__, ret); 17009b3b4b3fSPierre-Louis Bossart return ret; 17019b3b4b3fSPierre-Louis Bossart } 17029b3b4b3fSPierre-Louis Bossart 170399b6a30fSPierre-Louis Bossart /* 170499b6a30fSPierre-Louis Bossart * make sure all Slaves are tagged as UNATTACHED and provide 170599b6a30fSPierre-Louis Bossart * reason for reinitialization 170699b6a30fSPierre-Louis Bossart */ 170799b6a30fSPierre-Louis Bossart sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); 170899b6a30fSPierre-Louis Bossart 17099b3b4b3fSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 17109b3b4b3fSPierre-Louis Bossart if (ret < 0) { 17119b3b4b3fSPierre-Louis Bossart dev_err(dev, "cannot enable interrupts during resume\n"); 17129b3b4b3fSPierre-Louis Bossart return ret; 17139b3b4b3fSPierre-Louis Bossart } 17149b3b4b3fSPierre-Louis Bossart 1715857a7c42SPierre-Louis Bossart /* 1716857a7c42SPierre-Louis Bossart * follow recommended programming flows to avoid timeouts when 1717857a7c42SPierre-Louis Bossart * gsync is enabled 1718857a7c42SPierre-Louis Bossart */ 1719857a7c42SPierre-Louis Bossart if (multi_link) 1720857a7c42SPierre-Louis Bossart intel_shim_sync_arm(sdw); 1721857a7c42SPierre-Louis Bossart 1722857a7c42SPierre-Louis Bossart ret = sdw_cdns_init(&sdw->cdns); 1723857a7c42SPierre-Louis Bossart if (ret < 0) { 1724857a7c42SPierre-Louis Bossart dev_err(dev, "unable to initialize Cadence IP during resume\n"); 1725857a7c42SPierre-Louis Bossart return ret; 1726857a7c42SPierre-Louis Bossart } 1727857a7c42SPierre-Louis Bossart 17289b3b4b3fSPierre-Louis Bossart ret = sdw_cdns_exit_reset(cdns); 17299b3b4b3fSPierre-Louis Bossart if (ret < 0) { 17309b3b4b3fSPierre-Louis Bossart dev_err(dev, "unable to exit bus reset sequence during resume\n"); 17319b3b4b3fSPierre-Louis Bossart return ret; 17329b3b4b3fSPierre-Louis Bossart } 17339b3b4b3fSPierre-Louis Bossart 1734857a7c42SPierre-Louis Bossart if (multi_link) { 1735857a7c42SPierre-Louis Bossart ret = intel_shim_sync_go(sdw); 1736857a7c42SPierre-Louis Bossart if (ret < 0) { 1737857a7c42SPierre-Louis Bossart dev_err(dev, "sync go failed during resume\n"); 1738857a7c42SPierre-Louis Bossart return ret; 1739857a7c42SPierre-Louis Bossart } 1740857a7c42SPierre-Louis Bossart } 1741857a7c42SPierre-Louis Bossart 1742cb1e6d59SPierre-Louis Bossart /* 1743cb1e6d59SPierre-Louis Bossart * after system resume, the pm_runtime suspend() may kick in 1744cb1e6d59SPierre-Louis Bossart * during the enumeration, before any children device force the 1745cb1e6d59SPierre-Louis Bossart * master device to remain active. Using pm_runtime_get() 1746cb1e6d59SPierre-Louis Bossart * routines is not really possible, since it'd prevent the 1747cb1e6d59SPierre-Louis Bossart * master from suspending. 1748cb1e6d59SPierre-Louis Bossart * A reasonable compromise is to update the pm_runtime 1749cb1e6d59SPierre-Louis Bossart * counters and delay the pm_runtime suspend by several 1750cb1e6d59SPierre-Louis Bossart * seconds, by when all enumeration should be complete. 1751cb1e6d59SPierre-Louis Bossart */ 1752cb1e6d59SPierre-Louis Bossart pm_runtime_mark_last_busy(dev); 1753cb1e6d59SPierre-Louis Bossart 17549b3b4b3fSPierre-Louis Bossart return ret; 17559b3b4b3fSPierre-Louis Bossart } 17569b3b4b3fSPierre-Louis Bossart 1757ebf878edSPierre-Louis Bossart static int intel_resume_runtime(struct device *dev) 1758ebf878edSPierre-Louis Bossart { 1759ebf878edSPierre-Louis Bossart struct sdw_cdns *cdns = dev_get_drvdata(dev); 1760ebf878edSPierre-Louis Bossart struct sdw_intel *sdw = cdns_to_intel(cdns); 1761ebf878edSPierre-Louis Bossart struct sdw_bus *bus = &cdns->bus; 1762a320f41eSPierre-Louis Bossart u32 clock_stop_quirks; 176308abad9fSRander Wang bool clock_stop0; 1764857a7c42SPierre-Louis Bossart int link_flags; 1765857a7c42SPierre-Louis Bossart bool multi_link; 176608abad9fSRander Wang int status; 1767ebf878edSPierre-Louis Bossart int ret; 1768ebf878edSPierre-Louis Bossart 1769ebf878edSPierre-Louis Bossart if (bus->prop.hw_disabled) { 1770ebf878edSPierre-Louis Bossart dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", 1771ebf878edSPierre-Louis Bossart bus->link_id); 1772ebf878edSPierre-Louis Bossart return 0; 1773ebf878edSPierre-Louis Bossart } 1774ebf878edSPierre-Louis Bossart 1775857a7c42SPierre-Louis Bossart link_flags = md_flags >> (bus->link_id * 8); 1776857a7c42SPierre-Louis Bossart multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); 1777857a7c42SPierre-Louis Bossart 1778a320f41eSPierre-Louis Bossart clock_stop_quirks = sdw->link_res->clock_stop_quirks; 1779a320f41eSPierre-Louis Bossart 1780a320f41eSPierre-Louis Bossart if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { 1781ebf878edSPierre-Louis Bossart ret = intel_init(sdw); 1782ebf878edSPierre-Louis Bossart if (ret) { 1783ebf878edSPierre-Louis Bossart dev_err(dev, "%s failed: %d", __func__, ret); 1784ebf878edSPierre-Louis Bossart return ret; 1785ebf878edSPierre-Louis Bossart } 1786ebf878edSPierre-Louis Bossart 178799b6a30fSPierre-Louis Bossart /* 178899b6a30fSPierre-Louis Bossart * make sure all Slaves are tagged as UNATTACHED and provide 178999b6a30fSPierre-Louis Bossart * reason for reinitialization 179099b6a30fSPierre-Louis Bossart */ 179199b6a30fSPierre-Louis Bossart sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); 179299b6a30fSPierre-Louis Bossart 1793ebf878edSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 1794ebf878edSPierre-Louis Bossart if (ret < 0) { 1795ebf878edSPierre-Louis Bossart dev_err(dev, "cannot enable interrupts during resume\n"); 1796ebf878edSPierre-Louis Bossart return ret; 1797ebf878edSPierre-Louis Bossart } 1798ebf878edSPierre-Louis Bossart 1799857a7c42SPierre-Louis Bossart /* 1800857a7c42SPierre-Louis Bossart * follow recommended programming flows to avoid 1801857a7c42SPierre-Louis Bossart * timeouts when gsync is enabled 1802857a7c42SPierre-Louis Bossart */ 1803857a7c42SPierre-Louis Bossart if (multi_link) 1804857a7c42SPierre-Louis Bossart intel_shim_sync_arm(sdw); 1805857a7c42SPierre-Louis Bossart 1806857a7c42SPierre-Louis Bossart ret = sdw_cdns_init(&sdw->cdns); 1807857a7c42SPierre-Louis Bossart if (ret < 0) { 1808857a7c42SPierre-Louis Bossart dev_err(dev, "unable to initialize Cadence IP during resume\n"); 1809857a7c42SPierre-Louis Bossart return ret; 1810857a7c42SPierre-Louis Bossart } 1811857a7c42SPierre-Louis Bossart 1812ebf878edSPierre-Louis Bossart ret = sdw_cdns_exit_reset(cdns); 1813ebf878edSPierre-Louis Bossart if (ret < 0) { 1814ebf878edSPierre-Louis Bossart dev_err(dev, "unable to exit bus reset sequence during resume\n"); 1815ebf878edSPierre-Louis Bossart return ret; 1816ebf878edSPierre-Louis Bossart } 1817857a7c42SPierre-Louis Bossart 1818857a7c42SPierre-Louis Bossart if (multi_link) { 1819857a7c42SPierre-Louis Bossart ret = intel_shim_sync_go(sdw); 1820857a7c42SPierre-Louis Bossart if (ret < 0) { 1821857a7c42SPierre-Louis Bossart dev_err(dev, "sync go failed during resume\n"); 1822857a7c42SPierre-Louis Bossart return ret; 1823857a7c42SPierre-Louis Bossart } 1824857a7c42SPierre-Louis Bossart } 18256626a616SRander Wang } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) { 18266626a616SRander Wang ret = intel_init(sdw); 18276626a616SRander Wang if (ret) { 18286626a616SRander Wang dev_err(dev, "%s failed: %d", __func__, ret); 18296626a616SRander Wang return ret; 18306626a616SRander Wang } 18316626a616SRander Wang 18326626a616SRander Wang /* 183308abad9fSRander Wang * An exception condition occurs for the CLK_STOP_BUS_RESET 183408abad9fSRander Wang * case if one or more masters remain active. In this condition, 183508abad9fSRander Wang * all the masters are powered on for they are in the same power 183608abad9fSRander Wang * domain. Master can preserve its context for clock stop0, so 183708abad9fSRander Wang * there is no need to clear slave status and reset bus. 183808abad9fSRander Wang */ 183908abad9fSRander Wang clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns); 184008abad9fSRander Wang 1841857a7c42SPierre-Louis Bossart if (!clock_stop0) { 1842857a7c42SPierre-Louis Bossart 1843857a7c42SPierre-Louis Bossart /* 18446626a616SRander Wang * make sure all Slaves are tagged as UNATTACHED and 18456626a616SRander Wang * provide reason for reinitialization 18466626a616SRander Wang */ 1847857a7c42SPierre-Louis Bossart 184808abad9fSRander Wang status = SDW_UNATTACH_REQUEST_MASTER_RESET; 184908abad9fSRander Wang sdw_clear_slave_status(bus, status); 18506626a616SRander Wang 18516626a616SRander Wang ret = sdw_cdns_enable_interrupt(cdns, true); 18526626a616SRander Wang if (ret < 0) { 18536626a616SRander Wang dev_err(dev, "cannot enable interrupts during resume\n"); 18546626a616SRander Wang return ret; 18556626a616SRander Wang } 18566626a616SRander Wang 1857d78071b4SPierre-Louis Bossart /* 1858d78071b4SPierre-Louis Bossart * follow recommended programming flows to avoid 1859d78071b4SPierre-Louis Bossart * timeouts when gsync is enabled 1860d78071b4SPierre-Louis Bossart */ 1861d78071b4SPierre-Louis Bossart if (multi_link) 1862d78071b4SPierre-Louis Bossart intel_shim_sync_arm(sdw); 1863d78071b4SPierre-Louis Bossart 1864d78071b4SPierre-Louis Bossart /* 1865d78071b4SPierre-Louis Bossart * Re-initialize the IP since it was powered-off 1866d78071b4SPierre-Louis Bossart */ 1867d78071b4SPierre-Louis Bossart sdw_cdns_init(&sdw->cdns); 1868d78071b4SPierre-Louis Bossart 1869d78071b4SPierre-Louis Bossart } else { 1870d78071b4SPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 1871d78071b4SPierre-Louis Bossart if (ret < 0) { 1872d78071b4SPierre-Louis Bossart dev_err(dev, "cannot enable interrupts during resume\n"); 1873d78071b4SPierre-Louis Bossart return ret; 1874d78071b4SPierre-Louis Bossart } 1875d78071b4SPierre-Louis Bossart } 1876d78071b4SPierre-Louis Bossart 187708abad9fSRander Wang ret = sdw_cdns_clock_restart(cdns, !clock_stop0); 18786626a616SRander Wang if (ret < 0) { 18796626a616SRander Wang dev_err(dev, "unable to restart clock during resume\n"); 18806626a616SRander Wang return ret; 18816626a616SRander Wang } 1882d78071b4SPierre-Louis Bossart 1883d78071b4SPierre-Louis Bossart if (!clock_stop0) { 1884d78071b4SPierre-Louis Bossart ret = sdw_cdns_exit_reset(cdns); 1885d78071b4SPierre-Louis Bossart if (ret < 0) { 1886d78071b4SPierre-Louis Bossart dev_err(dev, "unable to exit bus reset sequence during resume\n"); 1887d78071b4SPierre-Louis Bossart return ret; 1888d78071b4SPierre-Louis Bossart } 1889d78071b4SPierre-Louis Bossart 1890d78071b4SPierre-Louis Bossart if (multi_link) { 1891d78071b4SPierre-Louis Bossart ret = intel_shim_sync_go(sdw); 1892d78071b4SPierre-Louis Bossart if (ret < 0) { 1893d78071b4SPierre-Louis Bossart dev_err(sdw->cdns.dev, "sync go failed during resume\n"); 1894d78071b4SPierre-Louis Bossart return ret; 1895d78071b4SPierre-Louis Bossart } 1896d78071b4SPierre-Louis Bossart } 1897d78071b4SPierre-Louis Bossart } 189861fb830bSPierre-Louis Bossart } else if (!clock_stop_quirks) { 1899f748f34eSPierre-Louis Bossart 1900f748f34eSPierre-Louis Bossart clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns); 1901f748f34eSPierre-Louis Bossart if (!clock_stop0) 1902f748f34eSPierre-Louis Bossart dev_err(dev, "%s invalid configuration, clock was not stopped", __func__); 1903f748f34eSPierre-Louis Bossart 190461fb830bSPierre-Louis Bossart ret = intel_init(sdw); 190561fb830bSPierre-Louis Bossart if (ret) { 190661fb830bSPierre-Louis Bossart dev_err(dev, "%s failed: %d", __func__, ret); 190761fb830bSPierre-Louis Bossart return ret; 190861fb830bSPierre-Louis Bossart } 190961fb830bSPierre-Louis Bossart 191061fb830bSPierre-Louis Bossart ret = sdw_cdns_enable_interrupt(cdns, true); 191161fb830bSPierre-Louis Bossart if (ret < 0) { 191261fb830bSPierre-Louis Bossart dev_err(dev, "cannot enable interrupts during resume\n"); 191361fb830bSPierre-Louis Bossart return ret; 191461fb830bSPierre-Louis Bossart } 191561fb830bSPierre-Louis Bossart 191661fb830bSPierre-Louis Bossart ret = sdw_cdns_clock_restart(cdns, false); 191761fb830bSPierre-Louis Bossart if (ret < 0) { 191861fb830bSPierre-Louis Bossart dev_err(dev, "unable to resume master during resume\n"); 191961fb830bSPierre-Louis Bossart return ret; 192061fb830bSPierre-Louis Bossart } 1921a320f41eSPierre-Louis Bossart } else { 1922a320f41eSPierre-Louis Bossart dev_err(dev, "%s clock_stop_quirks %x unsupported\n", 1923a320f41eSPierre-Louis Bossart __func__, clock_stop_quirks); 1924a320f41eSPierre-Louis Bossart ret = -EINVAL; 1925a320f41eSPierre-Louis Bossart } 1926ebf878edSPierre-Louis Bossart 1927ebf878edSPierre-Louis Bossart return ret; 1928ebf878edSPierre-Louis Bossart } 1929ebf878edSPierre-Louis Bossart 19309b3b4b3fSPierre-Louis Bossart #endif 19319b3b4b3fSPierre-Louis Bossart 19329b3b4b3fSPierre-Louis Bossart static const struct dev_pm_ops intel_pm = { 19339b3b4b3fSPierre-Louis Bossart SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) 1934ebf878edSPierre-Louis Bossart SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL) 19359b3b4b3fSPierre-Louis Bossart }; 19369b3b4b3fSPierre-Louis Bossart 193771bb8a1bSVinod Koul static struct platform_driver sdw_intel_drv = { 1938b6109dd6SPierre-Louis Bossart .probe = intel_master_probe, 1939b6109dd6SPierre-Louis Bossart .remove = intel_master_remove, 194071bb8a1bSVinod Koul .driver = { 19416d2c6669SPierre-Louis Bossart .name = "intel-sdw", 19429b3b4b3fSPierre-Louis Bossart .pm = &intel_pm, 19439b3b4b3fSPierre-Louis Bossart } 194471bb8a1bSVinod Koul }; 194571bb8a1bSVinod Koul 194671bb8a1bSVinod Koul module_platform_driver(sdw_intel_drv); 194771bb8a1bSVinod Koul 194871bb8a1bSVinod Koul MODULE_LICENSE("Dual BSD/GPL"); 19496d2c6669SPierre-Louis Bossart MODULE_ALIAS("platform:intel-sdw"); 195071bb8a1bSVinod Koul MODULE_DESCRIPTION("Intel Soundwire Master Driver"); 1951