1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 // Copyright(c) 2023 Intel Corporation. All rights reserved. 3 4 /* 5 * Soundwire Intel ops for LunarLake 6 */ 7 8 #include <linux/acpi.h> 9 #include <linux/device.h> 10 #include <linux/soundwire/sdw_registers.h> 11 #include <linux/soundwire/sdw.h> 12 #include <linux/soundwire/sdw_intel.h> 13 #include <sound/hda-mlink.h> 14 #include "cadence_master.h" 15 #include "bus.h" 16 #include "intel.h" 17 18 /* 19 * shim vendor-specific (vs) ops 20 */ 21 22 static void intel_shim_vs_init(struct sdw_intel *sdw) 23 { 24 void __iomem *shim_vs = sdw->link_res->shim_vs; 25 u16 act = 0; 26 27 u16p_replace_bits(&act, 0x1, SDW_SHIM2_INTEL_VS_ACTMCTL_DOAIS); 28 act |= SDW_SHIM2_INTEL_VS_ACTMCTL_DACTQE; 29 act |= SDW_SHIM2_INTEL_VS_ACTMCTL_DODS; 30 intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_ACTMCTL, act); 31 usleep_range(10, 15); 32 } 33 34 static int intel_link_power_up(struct sdw_intel *sdw) 35 { 36 struct sdw_bus *bus = &sdw->cdns.bus; 37 struct sdw_master_prop *prop = &bus->prop; 38 u32 *shim_mask = sdw->link_res->shim_mask; 39 unsigned int link_id = sdw->instance; 40 u32 syncprd; 41 int ret; 42 43 mutex_lock(sdw->link_res->shim_lock); 44 45 if (!*shim_mask) { 46 /* we first need to program the SyncPRD/CPU registers */ 47 dev_dbg(sdw->cdns.dev, "first link up, programming SYNCPRD\n"); 48 49 if (prop->mclk_freq % 6000000) 50 syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; 51 else 52 syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; 53 54 ret = hdac_bus_eml_sdw_set_syncprd_unlocked(sdw->link_res->hbus, syncprd); 55 if (ret < 0) { 56 dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_set_syncprd failed: %d\n", 57 __func__, ret); 58 goto out; 59 } 60 } 61 62 ret = hdac_bus_eml_sdw_power_up_unlocked(sdw->link_res->hbus, link_id); 63 if (ret < 0) { 64 dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_up failed: %d\n", 65 __func__, ret); 66 goto out; 67 } 68 69 if (!*shim_mask) { 70 /* SYNCPU will change once link is active */ 71 ret = hdac_bus_eml_sdw_wait_syncpu_unlocked(sdw->link_res->hbus); 72 if (ret < 0) { 73 dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_wait_syncpu failed: %d\n", 74 __func__, ret); 75 goto out; 76 } 77 } 78 79 *shim_mask |= BIT(link_id); 80 81 sdw->cdns.link_up = true; 82 83 intel_shim_vs_init(sdw); 84 85 out: 86 mutex_unlock(sdw->link_res->shim_lock); 87 88 return ret; 89 } 90 91 static int intel_link_power_down(struct sdw_intel *sdw) 92 { 93 u32 *shim_mask = sdw->link_res->shim_mask; 94 unsigned int link_id = sdw->instance; 95 int ret; 96 97 mutex_lock(sdw->link_res->shim_lock); 98 99 sdw->cdns.link_up = false; 100 101 *shim_mask &= ~BIT(link_id); 102 103 ret = hdac_bus_eml_sdw_power_down_unlocked(sdw->link_res->hbus, link_id); 104 if (ret < 0) { 105 dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_down failed: %d\n", 106 __func__, ret); 107 108 /* 109 * we leave the sdw->cdns.link_up flag as false since we've disabled 110 * the link at this point and cannot handle interrupts any longer. 111 */ 112 } 113 114 mutex_unlock(sdw->link_res->shim_lock); 115 116 return ret; 117 } 118 119 const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops = { 120 .debugfs_init = intel_ace2x_debugfs_init, 121 .debugfs_exit = intel_ace2x_debugfs_exit, 122 123 .link_power_up = intel_link_power_up, 124 .link_power_down = intel_link_power_down, 125 }; 126 EXPORT_SYMBOL_NS(sdw_intel_lnl_hw_ops, SOUNDWIRE_INTEL); 127 128 MODULE_IMPORT_NS(SND_SOC_SOF_HDA_MLINK); 129