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