Commit | Line | Data |
---|---|---|
71bb8a1b VK |
1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
2 | // Copyright(c) 2015-17 Intel Corporation. | |
3 | ||
4 | /* | |
5 | * Soundwire Intel Master Driver | |
6 | */ | |
7 | ||
8 | #include <linux/acpi.h> | |
79ee6631 | 9 | #include <linux/debugfs.h> |
71bb8a1b | 10 | #include <linux/delay.h> |
4abbd783 | 11 | #include <linux/module.h> |
71bb8a1b | 12 | #include <linux/interrupt.h> |
df72b719 | 13 | #include <linux/io.h> |
29a269c6 | 14 | #include <linux/auxiliary_bus.h> |
37a2d22b | 15 | #include <sound/pcm_params.h> |
ab2c9132 | 16 | #include <linux/pm_runtime.h> |
37a2d22b | 17 | #include <sound/soc.h> |
71bb8a1b VK |
18 | #include <linux/soundwire/sdw_registers.h> |
19 | #include <linux/soundwire/sdw.h> | |
20 | #include <linux/soundwire/sdw_intel.h> | |
21 | #include "cadence_master.h" | |
79ee6631 | 22 | #include "bus.h" |
71bb8a1b VK |
23 | #include "intel.h" |
24 | ||
1f2dcf3a PLB |
25 | /* IDA min selected to avoid conflicts with HDaudio/iDISP SDI values */ |
26 | #define INTEL_DEV_NUM_IDA_MIN 4 | |
27 | ||
ebf878ed | 28 | #define INTEL_MASTER_SUSPEND_DELAY_MS 3000 |
ff560946 | 29 | #define INTEL_MASTER_RESET_ITERATIONS 10 |
ebf878ed PLB |
30 | |
31 | /* | |
32 | * debug/config flags for the Intel SoundWire Master. | |
33 | * | |
34 | * Since we may have multiple masters active, we can have up to 8 | |
35 | * flags reused in each byte, with master0 using the ls-byte, etc. | |
36 | */ | |
37 | ||
a2d9c161 PLB |
38 | #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME BIT(0) |
39 | #define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP BIT(1) | |
40 | #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE BIT(2) | |
857a7c42 | 41 | #define SDW_INTEL_MASTER_DISABLE_MULTI_LINK BIT(3) |
ebf878ed PLB |
42 | |
43 | static int md_flags; | |
44 | module_param_named(sdw_md_flags, md_flags, int, 0444); | |
45 | MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)"); | |
46 | ||
c46302ec VK |
47 | enum intel_pdi_type { |
48 | INTEL_PDI_IN = 0, | |
49 | INTEL_PDI_OUT = 1, | |
50 | INTEL_PDI_BD = 2, | |
51 | }; | |
52 | ||
71bb8a1b VK |
53 | #define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns) |
54 | ||
55 | /* | |
56 | * Read, write helpers for HW registers | |
57 | */ | |
58 | static inline int intel_readl(void __iomem *base, int offset) | |
59 | { | |
60 | return readl(base + offset); | |
61 | } | |
62 | ||
63 | static inline void intel_writel(void __iomem *base, int offset, int value) | |
64 | { | |
65 | writel(value, base + offset); | |
66 | } | |
67 | ||
68 | static inline u16 intel_readw(void __iomem *base, int offset) | |
69 | { | |
70 | return readw(base + offset); | |
71 | } | |
72 | ||
73 | static inline void intel_writew(void __iomem *base, int offset, u16 value) | |
74 | { | |
75 | writew(value, base + offset); | |
76 | } | |
77 | ||
7d2845d5 | 78 | static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target) |
71bb8a1b VK |
79 | { |
80 | int timeout = 10; | |
81 | u32 reg_read; | |
82 | ||
71bb8a1b VK |
83 | do { |
84 | reg_read = readl(base + offset); | |
7d2845d5 | 85 | if ((reg_read & mask) == target) |
71bb8a1b VK |
86 | return 0; |
87 | ||
88 | timeout--; | |
7d2845d5 | 89 | usleep_range(50, 100); |
71bb8a1b VK |
90 | } while (timeout != 0); |
91 | ||
92 | return -EAGAIN; | |
93 | } | |
94 | ||
7d2845d5 | 95 | static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask) |
71bb8a1b | 96 | { |
71bb8a1b | 97 | writel(value, base + offset); |
7d2845d5 PLB |
98 | return intel_wait_bit(base, offset, mask, 0); |
99 | } | |
71bb8a1b | 100 | |
7d2845d5 PLB |
101 | static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) |
102 | { | |
103 | writel(value, base + offset); | |
104 | return intel_wait_bit(base, offset, mask, mask); | |
71bb8a1b VK |
105 | } |
106 | ||
79ee6631 PLB |
107 | /* |
108 | * debugfs | |
109 | */ | |
110 | #ifdef CONFIG_DEBUG_FS | |
111 | ||
112 | #define RD_BUF (2 * PAGE_SIZE) | |
113 | ||
114 | static ssize_t intel_sprintf(void __iomem *mem, bool l, | |
115 | char *buf, size_t pos, unsigned int reg) | |
116 | { | |
117 | int value; | |
118 | ||
119 | if (l) | |
120 | value = intel_readl(mem, reg); | |
121 | else | |
122 | value = intel_readw(mem, reg); | |
123 | ||
124 | return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value); | |
125 | } | |
126 | ||
127 | static int intel_reg_show(struct seq_file *s_file, void *data) | |
128 | { | |
129 | struct sdw_intel *sdw = s_file->private; | |
2523486b PLB |
130 | void __iomem *s = sdw->link_res->shim; |
131 | void __iomem *a = sdw->link_res->alh; | |
79ee6631 PLB |
132 | char *buf; |
133 | ssize_t ret; | |
134 | int i, j; | |
135 | unsigned int links, reg; | |
136 | ||
137 | buf = kzalloc(RD_BUF, GFP_KERNEL); | |
138 | if (!buf) | |
139 | return -ENOMEM; | |
140 | ||
7f817068 | 141 | links = intel_readl(s, SDW_SHIM_LCAP) & SDW_SHIM_LCAP_LCOUNT_MASK; |
79ee6631 PLB |
142 | |
143 | ret = scnprintf(buf, RD_BUF, "Register Value\n"); | |
144 | ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n"); | |
145 | ||
146 | for (i = 0; i < links; i++) { | |
147 | reg = SDW_SHIM_LCAP + i * 4; | |
148 | ret += intel_sprintf(s, true, buf, ret, reg); | |
149 | } | |
150 | ||
151 | for (i = 0; i < links; i++) { | |
152 | ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i); | |
153 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i)); | |
154 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i)); | |
155 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i)); | |
156 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i)); | |
157 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i)); | |
158 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i)); | |
159 | ||
160 | ret += scnprintf(buf + ret, RD_BUF - ret, "\n PCMSyCH registers\n"); | |
161 | ||
162 | /* | |
163 | * the value 10 is the number of PDIs. We will need a | |
164 | * cleanup to remove hard-coded Intel configurations | |
165 | * from cadence_master.c | |
166 | */ | |
167 | for (j = 0; j < 10; j++) { | |
168 | ret += intel_sprintf(s, false, buf, ret, | |
169 | SDW_SHIM_PCMSYCHM(i, j)); | |
170 | ret += intel_sprintf(s, false, buf, ret, | |
171 | SDW_SHIM_PCMSYCHC(i, j)); | |
172 | } | |
c27ce5c9 | 173 | ret += scnprintf(buf + ret, RD_BUF - ret, "\n IOCTL, CTMCTL\n"); |
79ee6631 | 174 | |
79ee6631 PLB |
175 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i)); |
176 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i)); | |
177 | } | |
178 | ||
179 | ret += scnprintf(buf + ret, RD_BUF - ret, "\nWake registers\n"); | |
180 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN); | |
181 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS); | |
182 | ||
183 | ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH STRMzCFG\n"); | |
184 | for (i = 0; i < SDW_ALH_NUM_STREAMS; i++) | |
185 | ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i)); | |
186 | ||
187 | seq_printf(s_file, "%s", buf); | |
188 | kfree(buf); | |
189 | ||
190 | return 0; | |
191 | } | |
192 | DEFINE_SHOW_ATTRIBUTE(intel_reg); | |
193 | ||
0f9138e7 PLB |
194 | static int intel_set_m_datamode(void *data, u64 value) |
195 | { | |
196 | struct sdw_intel *sdw = data; | |
197 | struct sdw_bus *bus = &sdw->cdns.bus; | |
198 | ||
199 | if (value > SDW_PORT_DATA_MODE_STATIC_1) | |
200 | return -EINVAL; | |
201 | ||
202 | /* Userspace changed the hardware state behind the kernel's back */ | |
203 | add_taint(TAINT_USER, LOCKDEP_STILL_OK); | |
204 | ||
205 | bus->params.m_data_mode = value; | |
206 | ||
207 | return 0; | |
208 | } | |
209 | DEFINE_DEBUGFS_ATTRIBUTE(intel_set_m_datamode_fops, NULL, | |
210 | intel_set_m_datamode, "%llu\n"); | |
211 | ||
212 | static int intel_set_s_datamode(void *data, u64 value) | |
213 | { | |
214 | struct sdw_intel *sdw = data; | |
215 | struct sdw_bus *bus = &sdw->cdns.bus; | |
216 | ||
217 | if (value > SDW_PORT_DATA_MODE_STATIC_1) | |
218 | return -EINVAL; | |
219 | ||
220 | /* Userspace changed the hardware state behind the kernel's back */ | |
221 | add_taint(TAINT_USER, LOCKDEP_STILL_OK); | |
222 | ||
223 | bus->params.s_data_mode = value; | |
224 | ||
225 | return 0; | |
226 | } | |
227 | DEFINE_DEBUGFS_ATTRIBUTE(intel_set_s_datamode_fops, NULL, | |
228 | intel_set_s_datamode, "%llu\n"); | |
229 | ||
79ee6631 PLB |
230 | static void intel_debugfs_init(struct sdw_intel *sdw) |
231 | { | |
232 | struct dentry *root = sdw->cdns.bus.debugfs; | |
233 | ||
234 | if (!root) | |
235 | return; | |
236 | ||
237 | sdw->debugfs = debugfs_create_dir("intel-sdw", root); | |
238 | ||
239 | debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw, | |
240 | &intel_reg_fops); | |
241 | ||
0f9138e7 PLB |
242 | debugfs_create_file("intel-m-datamode", 0200, sdw->debugfs, sdw, |
243 | &intel_set_m_datamode_fops); | |
244 | ||
245 | debugfs_create_file("intel-s-datamode", 0200, sdw->debugfs, sdw, | |
246 | &intel_set_s_datamode_fops); | |
247 | ||
79ee6631 PLB |
248 | sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs); |
249 | } | |
250 | ||
251 | static void intel_debugfs_exit(struct sdw_intel *sdw) | |
252 | { | |
253 | debugfs_remove_recursive(sdw->debugfs); | |
254 | } | |
255 | #else | |
256 | static void intel_debugfs_init(struct sdw_intel *sdw) {} | |
257 | static void intel_debugfs_exit(struct sdw_intel *sdw) {} | |
258 | #endif /* CONFIG_DEBUG_FS */ | |
259 | ||
71bb8a1b VK |
260 | /* |
261 | * shim ops | |
262 | */ | |
263 | ||
264 | static int intel_link_power_up(struct sdw_intel *sdw) | |
265 | { | |
266 | unsigned int link_id = sdw->instance; | |
2523486b | 267 | void __iomem *shim = sdw->link_res->shim; |
4a17c441 PLB |
268 | u32 *shim_mask = sdw->link_res->shim_mask; |
269 | struct sdw_bus *bus = &sdw->cdns.bus; | |
270 | struct sdw_master_prop *prop = &bus->prop; | |
5ee74eb2 PLB |
271 | u32 spa_mask, cpa_mask; |
272 | u32 link_control; | |
4a17c441 PLB |
273 | int ret = 0; |
274 | u32 syncprd; | |
275 | u32 sync_reg; | |
276 | ||
277 | mutex_lock(sdw->link_res->shim_lock); | |
278 | ||
279 | /* | |
280 | * The hardware relies on an internal counter, typically 4kHz, | |
281 | * to generate the SoundWire SSP - which defines a 'safe' | |
282 | * synchronization point between commands and audio transport | |
283 | * and allows for multi link synchronization. The SYNCPRD value | |
284 | * is only dependent on the oscillator clock provided to | |
285 | * the IP, so adjust based on _DSD properties reported in DSDT | |
286 | * tables. The values reported are based on either 24MHz | |
287 | * (CNL/CML) or 38.4 MHz (ICL/TGL+). | |
288 | */ | |
289 | if (prop->mclk_freq % 6000000) | |
290 | syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; | |
291 | else | |
292 | syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; | |
293 | ||
294 | if (!*shim_mask) { | |
63198aaa | 295 | dev_dbg(sdw->cdns.dev, "powering up all links\n"); |
5ee74eb2 | 296 | |
4a17c441 PLB |
297 | /* we first need to program the SyncPRD/CPU registers */ |
298 | dev_dbg(sdw->cdns.dev, | |
63198aaa | 299 | "first link up, programming SYNCPRD\n"); |
4a17c441 PLB |
300 | |
301 | /* set SyncPRD period */ | |
302 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); | |
f067c925 | 303 | u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD); |
4a17c441 PLB |
304 | |
305 | /* Set SyncCPU bit */ | |
306 | sync_reg |= SDW_SHIM_SYNC_SYNCCPU; | |
307 | intel_writel(shim, SDW_SHIM_SYNC, sync_reg); | |
71bb8a1b | 308 | |
5ee74eb2 PLB |
309 | /* Link power up sequence */ |
310 | link_control = intel_readl(shim, SDW_SHIM_LCTL); | |
71bb8a1b | 311 | |
5ee74eb2 | 312 | /* only power-up enabled links */ |
3b4979ca VK |
313 | spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask); |
314 | cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); | |
5ee74eb2 PLB |
315 | |
316 | link_control |= spa_mask; | |
317 | ||
318 | ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); | |
319 | if (ret < 0) { | |
320 | dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret); | |
321 | goto out; | |
322 | } | |
4a17c441 | 323 | |
4a17c441 PLB |
324 | /* SyncCPU will change once link is active */ |
325 | ret = intel_wait_bit(shim, SDW_SHIM_SYNC, | |
326 | SDW_SHIM_SYNC_SYNCCPU, 0); | |
327 | if (ret < 0) { | |
328 | dev_err(sdw->cdns.dev, | |
329 | "Failed to set SHIM_SYNC: %d\n", ret); | |
330 | goto out; | |
331 | } | |
332 | } | |
333 | ||
334 | *shim_mask |= BIT(link_id); | |
71bb8a1b VK |
335 | |
336 | sdw->cdns.link_up = true; | |
4a17c441 PLB |
337 | out: |
338 | mutex_unlock(sdw->link_res->shim_lock); | |
339 | ||
340 | return ret; | |
71bb8a1b VK |
341 | } |
342 | ||
4a17c441 PLB |
343 | /* this needs to be called with shim_lock */ |
344 | static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw) | |
71bb8a1b | 345 | { |
2523486b | 346 | void __iomem *shim = sdw->link_res->shim; |
71bb8a1b | 347 | unsigned int link_id = sdw->instance; |
4a17c441 | 348 | u16 ioctl; |
71bb8a1b | 349 | |
4a17c441 PLB |
350 | /* Switch to MIP from Glue logic */ |
351 | ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); | |
352 | ||
353 | ioctl &= ~(SDW_SHIM_IOCTL_DOE); | |
71bb8a1b | 354 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 355 | usleep_range(10, 15); |
71bb8a1b | 356 | |
4a17c441 | 357 | ioctl &= ~(SDW_SHIM_IOCTL_DO); |
71bb8a1b | 358 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 359 | usleep_range(10, 15); |
71bb8a1b | 360 | |
4a17c441 | 361 | ioctl |= (SDW_SHIM_IOCTL_MIF); |
71bb8a1b | 362 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 363 | usleep_range(10, 15); |
71bb8a1b | 364 | |
4a17c441 PLB |
365 | ioctl &= ~(SDW_SHIM_IOCTL_BKE); |
366 | ioctl &= ~(SDW_SHIM_IOCTL_COE); | |
71bb8a1b | 367 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 368 | usleep_range(10, 15); |
71bb8a1b | 369 | |
4a17c441 PLB |
370 | /* at this point Master IP has full control of the I/Os */ |
371 | } | |
71bb8a1b | 372 | |
4a17c441 PLB |
373 | /* this needs to be called with shim_lock */ |
374 | static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw) | |
375 | { | |
376 | unsigned int link_id = sdw->instance; | |
377 | void __iomem *shim = sdw->link_res->shim; | |
378 | u16 ioctl; | |
379 | ||
380 | /* Glue logic */ | |
381 | ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); | |
382 | ioctl |= SDW_SHIM_IOCTL_BKE; | |
383 | ioctl |= SDW_SHIM_IOCTL_COE; | |
71bb8a1b | 384 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 385 | usleep_range(10, 15); |
71bb8a1b | 386 | |
4a17c441 | 387 | ioctl &= ~(SDW_SHIM_IOCTL_MIF); |
71bb8a1b | 388 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 389 | usleep_range(10, 15); |
71bb8a1b | 390 | |
4a17c441 PLB |
391 | /* at this point Integration Glue has full control of the I/Os */ |
392 | } | |
393 | ||
394 | static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop) | |
395 | { | |
396 | void __iomem *shim = sdw->link_res->shim; | |
397 | unsigned int link_id = sdw->instance; | |
398 | int ret = 0; | |
399 | u16 ioctl = 0, act = 0; | |
400 | ||
401 | mutex_lock(sdw->link_res->shim_lock); | |
402 | ||
403 | /* Initialize Shim */ | |
404 | ioctl |= SDW_SHIM_IOCTL_BKE; | |
71bb8a1b | 405 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 406 | usleep_range(10, 15); |
71bb8a1b | 407 | |
4a17c441 PLB |
408 | ioctl |= SDW_SHIM_IOCTL_WPDD; |
409 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); | |
410 | usleep_range(10, 15); | |
71bb8a1b | 411 | |
4a17c441 | 412 | ioctl |= SDW_SHIM_IOCTL_DO; |
71bb8a1b | 413 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 PLB |
414 | usleep_range(10, 15); |
415 | ||
416 | ioctl |= SDW_SHIM_IOCTL_DOE; | |
71bb8a1b | 417 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 PLB |
418 | usleep_range(10, 15); |
419 | ||
420 | intel_shim_glue_to_master_ip(sdw); | |
71bb8a1b | 421 | |
f067c925 | 422 | u16p_replace_bits(&act, 0x1, SDW_SHIM_CTMCTL_DOAIS); |
71bb8a1b VK |
423 | act |= SDW_SHIM_CTMCTL_DACTQE; |
424 | act |= SDW_SHIM_CTMCTL_DODS; | |
425 | intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act); | |
4a17c441 | 426 | usleep_range(10, 15); |
71bb8a1b | 427 | |
4a17c441 PLB |
428 | mutex_unlock(sdw->link_res->shim_lock); |
429 | ||
430 | return ret; | |
431 | } | |
432 | ||
ab2c9132 | 433 | static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) |
4a17c441 PLB |
434 | { |
435 | void __iomem *shim = sdw->link_res->shim; | |
436 | unsigned int link_id = sdw->instance; | |
437 | u16 wake_en, wake_sts; | |
438 | ||
439 | mutex_lock(sdw->link_res->shim_lock); | |
440 | wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); | |
441 | ||
442 | if (wake_enable) { | |
443 | /* Enable the wakeup */ | |
444 | wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id); | |
445 | intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); | |
446 | } else { | |
447 | /* Disable the wake up interrupt */ | |
448 | wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); | |
449 | intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); | |
450 | ||
451 | /* Clear wake status */ | |
452 | wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); | |
3957db3a LY |
453 | wake_sts |= (SDW_SHIM_WAKESTS_STATUS << link_id); |
454 | intel_writew(shim, SDW_SHIM_WAKESTS, wake_sts); | |
4a17c441 PLB |
455 | } |
456 | mutex_unlock(sdw->link_res->shim_lock); | |
457 | } | |
458 | ||
9b3b4b3f | 459 | static int intel_link_power_down(struct sdw_intel *sdw) |
4a17c441 | 460 | { |
5ee74eb2 | 461 | u32 link_control, spa_mask, cpa_mask; |
4a17c441 PLB |
462 | unsigned int link_id = sdw->instance; |
463 | void __iomem *shim = sdw->link_res->shim; | |
464 | u32 *shim_mask = sdw->link_res->shim_mask; | |
465 | int ret = 0; | |
466 | ||
467 | mutex_lock(sdw->link_res->shim_lock); | |
468 | ||
4a17c441 PLB |
469 | if (!(*shim_mask & BIT(link_id))) |
470 | dev_err(sdw->cdns.dev, | |
471 | "%s: Unbalanced power-up/down calls\n", __func__); | |
472 | ||
ea6942da PLB |
473 | sdw->cdns.link_up = false; |
474 | ||
475 | intel_shim_master_ip_to_glue(sdw); | |
476 | ||
4a17c441 PLB |
477 | *shim_mask &= ~BIT(link_id); |
478 | ||
5ee74eb2 PLB |
479 | if (!*shim_mask) { |
480 | ||
63198aaa | 481 | dev_dbg(sdw->cdns.dev, "powering down all links\n"); |
5ee74eb2 PLB |
482 | |
483 | /* Link power down sequence */ | |
484 | link_control = intel_readl(shim, SDW_SHIM_LCTL); | |
485 | ||
486 | /* only power-down enabled links */ | |
3b4979ca VK |
487 | spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, ~sdw->link_res->link_mask); |
488 | cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); | |
5ee74eb2 PLB |
489 | |
490 | link_control &= spa_mask; | |
491 | ||
492 | ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); | |
ea6942da PLB |
493 | if (ret < 0) { |
494 | dev_err(sdw->cdns.dev, "%s: could not power down link\n", __func__); | |
495 | ||
496 | /* | |
497 | * we leave the sdw->cdns.link_up flag as false since we've disabled | |
498 | * the link at this point and cannot handle interrupts any longer. | |
499 | */ | |
500 | } | |
5ee74eb2 PLB |
501 | } |
502 | ||
4a17c441 | 503 | mutex_unlock(sdw->link_res->shim_lock); |
71bb8a1b | 504 | |
ea6942da | 505 | return ret; |
71bb8a1b VK |
506 | } |
507 | ||
02629e45 PLB |
508 | static void intel_shim_sync_arm(struct sdw_intel *sdw) |
509 | { | |
510 | void __iomem *shim = sdw->link_res->shim; | |
511 | u32 sync_reg; | |
512 | ||
513 | mutex_lock(sdw->link_res->shim_lock); | |
514 | ||
515 | /* update SYNC register */ | |
516 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); | |
517 | sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance); | |
518 | intel_writel(shim, SDW_SHIM_SYNC, sync_reg); | |
519 | ||
520 | mutex_unlock(sdw->link_res->shim_lock); | |
521 | } | |
522 | ||
437e3289 PLB |
523 | static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw) |
524 | { | |
525 | void __iomem *shim = sdw->link_res->shim; | |
526 | u32 sync_reg; | |
527 | int ret; | |
71bb8a1b | 528 | |
437e3289 | 529 | /* Read SYNC register */ |
71bb8a1b | 530 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); |
71bb8a1b | 531 | |
437e3289 PLB |
532 | /* |
533 | * Set SyncGO bit to synchronously trigger a bank switch for | |
534 | * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all | |
535 | * the Masters. | |
536 | */ | |
537 | sync_reg |= SDW_SHIM_SYNC_SYNCGO; | |
538 | ||
71bb8a1b | 539 | ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, |
437e3289 PLB |
540 | SDW_SHIM_SYNC_SYNCGO); |
541 | ||
71bb8a1b | 542 | if (ret < 0) |
437e3289 | 543 | dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret); |
71bb8a1b VK |
544 | |
545 | return ret; | |
546 | } | |
547 | ||
857a7c42 PLB |
548 | static int intel_shim_sync_go(struct sdw_intel *sdw) |
549 | { | |
550 | int ret; | |
551 | ||
552 | mutex_lock(sdw->link_res->shim_lock); | |
553 | ||
554 | ret = intel_shim_sync_go_unlocked(sdw); | |
555 | ||
556 | mutex_unlock(sdw->link_res->shim_lock); | |
557 | ||
558 | return ret; | |
559 | } | |
560 | ||
37a2d22b VK |
561 | /* |
562 | * PDI routines | |
563 | */ | |
564 | static void intel_pdi_init(struct sdw_intel *sdw, | |
d542bc9e | 565 | struct sdw_cdns_stream_config *config) |
37a2d22b | 566 | { |
2523486b | 567 | void __iomem *shim = sdw->link_res->shim; |
37a2d22b | 568 | unsigned int link_id = sdw->instance; |
63a6aa96 | 569 | int pcm_cap; |
37a2d22b VK |
570 | |
571 | /* PCM Stream Capability */ | |
572 | pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id)); | |
573 | ||
3b4979ca VK |
574 | config->pcm_bd = FIELD_GET(SDW_SHIM_PCMSCAP_BSS, pcm_cap); |
575 | config->pcm_in = FIELD_GET(SDW_SHIM_PCMSCAP_ISS, pcm_cap); | |
576 | config->pcm_out = FIELD_GET(SDW_SHIM_PCMSCAP_OSS, pcm_cap); | |
37a2d22b | 577 | |
121f4361 PLB |
578 | dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n", |
579 | config->pcm_bd, config->pcm_in, config->pcm_out); | |
37a2d22b VK |
580 | } |
581 | ||
582 | static int | |
63a6aa96 | 583 | intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num) |
37a2d22b | 584 | { |
2523486b | 585 | void __iomem *shim = sdw->link_res->shim; |
37a2d22b VK |
586 | unsigned int link_id = sdw->instance; |
587 | int count; | |
588 | ||
63a6aa96 | 589 | count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); |
18046335 | 590 | |
63a6aa96 PLB |
591 | /* |
592 | * WORKAROUND: on all existing Intel controllers, pdi | |
593 | * number 2 reports channel count as 1 even though it | |
594 | * supports 8 channels. Performing hardcoding for pdi | |
595 | * number 2. | |
596 | */ | |
597 | if (pdi_num == 2) | |
598 | count = 7; | |
37a2d22b VK |
599 | |
600 | /* zero based values for channel count in register */ | |
601 | count++; | |
602 | ||
603 | return count; | |
604 | } | |
605 | ||
606 | static int intel_pdi_get_ch_update(struct sdw_intel *sdw, | |
d542bc9e PLB |
607 | struct sdw_cdns_pdi *pdi, |
608 | unsigned int num_pdi, | |
63a6aa96 | 609 | unsigned int *num_ch) |
37a2d22b VK |
610 | { |
611 | int i, ch_count = 0; | |
612 | ||
613 | for (i = 0; i < num_pdi; i++) { | |
63a6aa96 | 614 | pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num); |
37a2d22b VK |
615 | ch_count += pdi->ch_count; |
616 | pdi++; | |
617 | } | |
618 | ||
619 | *num_ch = ch_count; | |
620 | return 0; | |
621 | } | |
622 | ||
623 | static int intel_pdi_stream_ch_update(struct sdw_intel *sdw, | |
63a6aa96 | 624 | struct sdw_cdns_streams *stream) |
37a2d22b VK |
625 | { |
626 | intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd, | |
63a6aa96 | 627 | &stream->num_ch_bd); |
37a2d22b VK |
628 | |
629 | intel_pdi_get_ch_update(sdw, stream->in, stream->num_in, | |
63a6aa96 | 630 | &stream->num_ch_in); |
37a2d22b VK |
631 | |
632 | intel_pdi_get_ch_update(sdw, stream->out, stream->num_out, | |
63a6aa96 | 633 | &stream->num_ch_out); |
37a2d22b VK |
634 | |
635 | return 0; | |
636 | } | |
637 | ||
638 | static int intel_pdi_ch_update(struct sdw_intel *sdw) | |
639 | { | |
63a6aa96 | 640 | intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm); |
37a2d22b VK |
641 | |
642 | return 0; | |
643 | } | |
644 | ||
645 | static void | |
646 | intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) | |
647 | { | |
2523486b | 648 | void __iomem *shim = sdw->link_res->shim; |
37a2d22b VK |
649 | unsigned int link_id = sdw->instance; |
650 | int pdi_conf = 0; | |
651 | ||
c134f914 PLB |
652 | /* the Bulk and PCM streams are not contiguous */ |
653 | pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; | |
654 | if (pdi->num >= 2) | |
655 | pdi->intel_alh_id += 2; | |
37a2d22b VK |
656 | |
657 | /* | |
658 | * Program stream parameters to stream SHIM register | |
659 | * This is applicable for PCM stream only. | |
660 | */ | |
661 | if (pdi->type != SDW_STREAM_PCM) | |
662 | return; | |
663 | ||
664 | if (pdi->dir == SDW_DATA_DIR_RX) | |
665 | pdi_conf |= SDW_SHIM_PCMSYCM_DIR; | |
666 | else | |
667 | pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR); | |
668 | ||
f067c925 VK |
669 | u32p_replace_bits(&pdi_conf, pdi->intel_alh_id, SDW_SHIM_PCMSYCM_STREAM); |
670 | u32p_replace_bits(&pdi_conf, pdi->l_ch_num, SDW_SHIM_PCMSYCM_LCHN); | |
671 | u32p_replace_bits(&pdi_conf, pdi->h_ch_num, SDW_SHIM_PCMSYCM_HCHN); | |
37a2d22b VK |
672 | |
673 | intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf); | |
674 | } | |
675 | ||
676 | static void | |
677 | intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) | |
678 | { | |
2523486b | 679 | void __iomem *alh = sdw->link_res->alh; |
37a2d22b VK |
680 | unsigned int link_id = sdw->instance; |
681 | unsigned int conf; | |
682 | ||
c134f914 PLB |
683 | /* the Bulk and PCM streams are not contiguous */ |
684 | pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; | |
685 | if (pdi->num >= 2) | |
686 | pdi->intel_alh_id += 2; | |
37a2d22b VK |
687 | |
688 | /* Program Stream config ALH register */ | |
689 | conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id)); | |
690 | ||
f067c925 VK |
691 | u32p_replace_bits(&conf, SDW_ALH_STRMZCFG_DMAT_VAL, SDW_ALH_STRMZCFG_DMAT); |
692 | u32p_replace_bits(&conf, pdi->ch_count - 1, SDW_ALH_STRMZCFG_CHN); | |
37a2d22b VK |
693 | |
694 | intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); | |
695 | } | |
696 | ||
4b206d34 | 697 | static int intel_params_stream(struct sdw_intel *sdw, |
b86947b5 | 698 | int stream, |
d542bc9e | 699 | struct snd_soc_dai *dai, |
4b206d34 RW |
700 | struct snd_pcm_hw_params *hw_params, |
701 | int link_id, int alh_stream_id) | |
c46302ec | 702 | { |
2523486b | 703 | struct sdw_intel_link_res *res = sdw->link_res; |
4b206d34 | 704 | struct sdw_intel_stream_params_data params_data; |
05c8afe4 | 705 | |
b86947b5 | 706 | params_data.stream = stream; /* direction */ |
4b206d34 RW |
707 | params_data.dai = dai; |
708 | params_data.hw_params = hw_params; | |
709 | params_data.link_id = link_id; | |
710 | params_data.alh_stream_id = alh_stream_id; | |
c46302ec | 711 | |
4b206d34 RW |
712 | if (res->ops && res->ops->params_stream && res->dev) |
713 | return res->ops->params_stream(res->dev, | |
714 | ¶ms_data); | |
c46302ec VK |
715 | return -EIO; |
716 | } | |
717 | ||
eff346f2 | 718 | static int intel_free_stream(struct sdw_intel *sdw, |
b86947b5 | 719 | int stream, |
eff346f2 PLB |
720 | struct snd_soc_dai *dai, |
721 | int link_id) | |
722 | { | |
723 | struct sdw_intel_link_res *res = sdw->link_res; | |
724 | struct sdw_intel_stream_free_data free_data; | |
725 | ||
b86947b5 | 726 | free_data.stream = stream; /* direction */ |
eff346f2 PLB |
727 | free_data.dai = dai; |
728 | free_data.link_id = link_id; | |
729 | ||
730 | if (res->ops && res->ops->free_stream && res->dev) | |
731 | return res->ops->free_stream(res->dev, | |
732 | &free_data); | |
733 | ||
734 | return 0; | |
735 | } | |
736 | ||
30246e2d SN |
737 | /* |
738 | * bank switch routines | |
739 | */ | |
740 | ||
741 | static int intel_pre_bank_switch(struct sdw_bus *bus) | |
742 | { | |
743 | struct sdw_cdns *cdns = bus_to_cdns(bus); | |
744 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
30246e2d SN |
745 | |
746 | /* Write to register only for multi-link */ | |
747 | if (!bus->multi_link) | |
748 | return 0; | |
749 | ||
02629e45 | 750 | intel_shim_sync_arm(sdw); |
30246e2d SN |
751 | |
752 | return 0; | |
753 | } | |
754 | ||
755 | static int intel_post_bank_switch(struct sdw_bus *bus) | |
756 | { | |
757 | struct sdw_cdns *cdns = bus_to_cdns(bus); | |
758 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
2523486b | 759 | void __iomem *shim = sdw->link_res->shim; |
30246e2d SN |
760 | int sync_reg, ret; |
761 | ||
762 | /* Write to register only for multi-link */ | |
763 | if (!bus->multi_link) | |
764 | return 0; | |
765 | ||
4a17c441 PLB |
766 | mutex_lock(sdw->link_res->shim_lock); |
767 | ||
30246e2d SN |
768 | /* Read SYNC register */ |
769 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); | |
770 | ||
771 | /* | |
772 | * post_bank_switch() ops is called from the bus in loop for | |
773 | * all the Masters in the steam with the expectation that | |
774 | * we trigger the bankswitch for the only first Master in the list | |
775 | * and do nothing for the other Masters | |
776 | * | |
777 | * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master. | |
778 | */ | |
4a17c441 PLB |
779 | if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) { |
780 | ret = 0; | |
781 | goto unlock; | |
782 | } | |
30246e2d | 783 | |
437e3289 | 784 | ret = intel_shim_sync_go_unlocked(sdw); |
4a17c441 PLB |
785 | unlock: |
786 | mutex_unlock(sdw->link_res->shim_lock); | |
30246e2d | 787 | |
30246e2d | 788 | if (ret < 0) |
17ed5bef | 789 | dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret); |
30246e2d SN |
790 | |
791 | return ret; | |
792 | } | |
793 | ||
c46302ec VK |
794 | /* |
795 | * DAI routines | |
796 | */ | |
797 | ||
5e7484d0 RW |
798 | static int intel_startup(struct snd_pcm_substream *substream, |
799 | struct snd_soc_dai *dai) | |
800 | { | |
ebf878ed PLB |
801 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); |
802 | int ret; | |
803 | ||
fff1fd9c | 804 | ret = pm_runtime_resume_and_get(cdns->dev); |
ebf878ed PLB |
805 | if (ret < 0 && ret != -EACCES) { |
806 | dev_err_ratelimited(cdns->dev, | |
fff1fd9c | 807 | "pm_runtime_resume_and_get failed in %s, ret %d\n", |
ebf878ed | 808 | __func__, ret); |
ebf878ed PLB |
809 | return ret; |
810 | } | |
ff16d1e5 | 811 | return 0; |
5e7484d0 RW |
812 | } |
813 | ||
c46302ec | 814 | static int intel_hw_params(struct snd_pcm_substream *substream, |
d542bc9e PLB |
815 | struct snd_pcm_hw_params *params, |
816 | struct snd_soc_dai *dai) | |
c46302ec VK |
817 | { |
818 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
819 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
820 | struct sdw_cdns_dma_data *dma; | |
57a34790 | 821 | struct sdw_cdns_pdi *pdi; |
c46302ec VK |
822 | struct sdw_stream_config sconfig; |
823 | struct sdw_port_config *pconfig; | |
57a34790 PLB |
824 | int ch, dir; |
825 | int ret; | |
c46302ec VK |
826 | |
827 | dma = snd_soc_dai_get_dma_data(dai, substream); | |
828 | if (!dma) | |
829 | return -EIO; | |
830 | ||
831 | ch = params_channels(params); | |
832 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | |
833 | dir = SDW_DATA_DIR_RX; | |
834 | else | |
835 | dir = SDW_DATA_DIR_TX; | |
836 | ||
63a6aa96 | 837 | pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); |
57a34790 PLB |
838 | |
839 | if (!pdi) { | |
840 | ret = -EINVAL; | |
841 | goto error; | |
c46302ec VK |
842 | } |
843 | ||
57a34790 PLB |
844 | /* do run-time configurations for SHIM, ALH and PDI/PORT */ |
845 | intel_pdi_shim_configure(sdw, pdi); | |
846 | intel_pdi_alh_configure(sdw, pdi); | |
847 | sdw_cdns_config_stream(cdns, ch, dir, pdi); | |
c46302ec | 848 | |
a5a0239c | 849 | /* store pdi and hw_params, may be needed in prepare step */ |
8ddeafb9 | 850 | dma->paused = false; |
a5a0239c BL |
851 | dma->suspended = false; |
852 | dma->pdi = pdi; | |
853 | dma->hw_params = params; | |
c46302ec VK |
854 | |
855 | /* Inform DSP about PDI stream number */ | |
b86947b5 | 856 | ret = intel_params_stream(sdw, substream->stream, dai, params, |
4b206d34 | 857 | sdw->instance, |
57a34790 PLB |
858 | pdi->intel_alh_id); |
859 | if (ret) | |
860 | goto error; | |
c46302ec VK |
861 | |
862 | sconfig.direction = dir; | |
863 | sconfig.ch_count = ch; | |
864 | sconfig.frame_rate = params_rate(params); | |
865 | sconfig.type = dma->stream_type; | |
866 | ||
63a6aa96 | 867 | sconfig.bps = snd_pcm_format_width(params_format(params)); |
c46302ec VK |
868 | |
869 | /* Port configuration */ | |
235ae89b | 870 | pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL); |
c46302ec VK |
871 | if (!pconfig) { |
872 | ret = -ENOMEM; | |
57a34790 | 873 | goto error; |
c46302ec VK |
874 | } |
875 | ||
57a34790 PLB |
876 | pconfig->num = pdi->num; |
877 | pconfig->ch_mask = (1 << ch) - 1; | |
c46302ec VK |
878 | |
879 | ret = sdw_stream_add_master(&cdns->bus, &sconfig, | |
57a34790 PLB |
880 | pconfig, 1, dma->stream); |
881 | if (ret) | |
17ed5bef | 882 | dev_err(cdns->dev, "add master to stream failed:%d\n", ret); |
c46302ec | 883 | |
c46302ec | 884 | kfree(pconfig); |
57a34790 | 885 | error: |
c46302ec VK |
886 | return ret; |
887 | } | |
888 | ||
27b198f4 RW |
889 | static int intel_prepare(struct snd_pcm_substream *substream, |
890 | struct snd_soc_dai *dai) | |
891 | { | |
a5a0239c BL |
892 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); |
893 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
27b198f4 | 894 | struct sdw_cdns_dma_data *dma; |
a5a0239c | 895 | int ch, dir; |
244eb888 | 896 | int ret = 0; |
27b198f4 RW |
897 | |
898 | dma = snd_soc_dai_get_dma_data(dai, substream); | |
899 | if (!dma) { | |
4e3ea93e | 900 | dev_err(dai->dev, "failed to get dma data in %s\n", |
27b198f4 RW |
901 | __func__); |
902 | return -EIO; | |
903 | } | |
904 | ||
a5a0239c BL |
905 | if (dma->suspended) { |
906 | dma->suspended = false; | |
907 | ||
908 | /* | |
909 | * .prepare() is called after system resume, where we | |
910 | * need to reinitialize the SHIM/ALH/Cadence IP. | |
911 | * .prepare() is also called to deal with underflows, | |
912 | * but in those cases we cannot touch ALH/SHIM | |
913 | * registers | |
914 | */ | |
915 | ||
916 | /* configure stream */ | |
917 | ch = params_channels(dma->hw_params); | |
918 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | |
919 | dir = SDW_DATA_DIR_RX; | |
920 | else | |
921 | dir = SDW_DATA_DIR_TX; | |
922 | ||
923 | intel_pdi_shim_configure(sdw, dma->pdi); | |
924 | intel_pdi_alh_configure(sdw, dma->pdi); | |
925 | sdw_cdns_config_stream(cdns, ch, dir, dma->pdi); | |
926 | ||
927 | /* Inform DSP about PDI stream number */ | |
b86947b5 | 928 | ret = intel_params_stream(sdw, substream->stream, dai, |
a5a0239c BL |
929 | dma->hw_params, |
930 | sdw->instance, | |
931 | dma->pdi->intel_alh_id); | |
a5a0239c BL |
932 | } |
933 | ||
a5a0239c | 934 | return ret; |
27b198f4 RW |
935 | } |
936 | ||
c46302ec VK |
937 | static int |
938 | intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) | |
939 | { | |
940 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
eff346f2 | 941 | struct sdw_intel *sdw = cdns_to_intel(cdns); |
c46302ec VK |
942 | struct sdw_cdns_dma_data *dma; |
943 | int ret; | |
944 | ||
945 | dma = snd_soc_dai_get_dma_data(dai, substream); | |
946 | if (!dma) | |
947 | return -EIO; | |
948 | ||
244eb888 PLB |
949 | /* |
950 | * The sdw stream state will transition to RELEASED when stream-> | |
951 | * master_list is empty. So the stream state will transition to | |
952 | * DEPREPARED for the first cpu-dai and to RELEASED for the last | |
953 | * cpu-dai. | |
954 | */ | |
c46302ec | 955 | ret = sdw_stream_remove_master(&cdns->bus, dma->stream); |
eff346f2 | 956 | if (ret < 0) { |
17ed5bef | 957 | dev_err(dai->dev, "remove master from stream %s failed: %d\n", |
d542bc9e | 958 | dma->stream->name, ret); |
eff346f2 PLB |
959 | return ret; |
960 | } | |
c46302ec | 961 | |
b86947b5 | 962 | ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); |
eff346f2 | 963 | if (ret < 0) { |
4e3ea93e | 964 | dev_err(dai->dev, "intel_free_stream: failed %d\n", ret); |
eff346f2 PLB |
965 | return ret; |
966 | } | |
967 | ||
a5a0239c BL |
968 | dma->hw_params = NULL; |
969 | dma->pdi = NULL; | |
970 | ||
eff346f2 | 971 | return 0; |
c46302ec VK |
972 | } |
973 | ||
183c7687 PLB |
974 | static void intel_shutdown(struct snd_pcm_substream *substream, |
975 | struct snd_soc_dai *dai) | |
976 | { | |
ebf878ed | 977 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); |
183c7687 | 978 | |
ebf878ed PLB |
979 | pm_runtime_mark_last_busy(cdns->dev); |
980 | pm_runtime_put_autosuspend(cdns->dev); | |
183c7687 PLB |
981 | } |
982 | ||
c46302ec | 983 | static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, |
d542bc9e | 984 | void *stream, int direction) |
c46302ec | 985 | { |
63a6aa96 | 986 | return cdns_set_sdw_stream(dai, stream, direction); |
c46302ec VK |
987 | } |
988 | ||
09553140 PLB |
989 | static void *intel_get_sdw_stream(struct snd_soc_dai *dai, |
990 | int direction) | |
991 | { | |
992 | struct sdw_cdns_dma_data *dma; | |
993 | ||
994 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) | |
995 | dma = dai->playback_dma_data; | |
996 | else | |
997 | dma = dai->capture_dma_data; | |
998 | ||
999 | if (!dma) | |
06dcb4e4 | 1000 | return ERR_PTR(-EINVAL); |
09553140 PLB |
1001 | |
1002 | return dma->stream; | |
1003 | } | |
1004 | ||
8ddeafb9 RS |
1005 | static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) |
1006 | { | |
1007 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
1008 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
6d1c1a73 | 1009 | struct sdw_intel_link_res *res = sdw->link_res; |
8ddeafb9 RS |
1010 | struct sdw_cdns_dma_data *dma; |
1011 | int ret = 0; | |
1012 | ||
6d1c1a73 BL |
1013 | /* |
1014 | * The .trigger callback is used to send required IPC to audio | |
1015 | * firmware. The .free_stream callback will still be called | |
1016 | * by intel_free_stream() in the TRIGGER_SUSPEND case. | |
1017 | */ | |
1018 | if (res->ops && res->ops->trigger) | |
1019 | res->ops->trigger(dai, cmd, substream->stream); | |
1020 | ||
8ddeafb9 RS |
1021 | dma = snd_soc_dai_get_dma_data(dai, substream); |
1022 | if (!dma) { | |
1023 | dev_err(dai->dev, "failed to get dma data in %s\n", | |
1024 | __func__); | |
1025 | return -EIO; | |
1026 | } | |
1027 | ||
1028 | switch (cmd) { | |
1029 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
1030 | ||
1031 | /* | |
1032 | * The .prepare callback is used to deal with xruns and resume operations. | |
1033 | * In the case of xruns, the DMAs and SHIM registers cannot be touched, | |
1034 | * but for resume operations the DMAs and SHIM registers need to be initialized. | |
1035 | * the .trigger callback is used to track the suspend case only. | |
1036 | */ | |
1037 | ||
1038 | dma->suspended = true; | |
1039 | ||
1040 | ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); | |
1041 | break; | |
1042 | ||
1043 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
1044 | dma->paused = true; | |
1045 | break; | |
1046 | case SNDRV_PCM_TRIGGER_STOP: | |
1047 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
1048 | dma->paused = false; | |
1049 | break; | |
1050 | default: | |
1051 | break; | |
1052 | } | |
1053 | ||
1054 | return ret; | |
1055 | } | |
1056 | ||
3e9c9f90 PLB |
1057 | static int intel_component_probe(struct snd_soc_component *component) |
1058 | { | |
1059 | int ret; | |
1060 | ||
1061 | /* | |
1062 | * make sure the device is pm_runtime_active before initiating | |
1063 | * bus transactions during the card registration. | |
1064 | * We use pm_runtime_resume() here, without taking a reference | |
1065 | * and releasing it immediately. | |
1066 | */ | |
1067 | ret = pm_runtime_resume(component->dev); | |
1068 | if (ret < 0 && ret != -EACCES) | |
1069 | return ret; | |
1070 | ||
1071 | return 0; | |
1072 | } | |
1073 | ||
8ddeafb9 RS |
1074 | static int intel_component_dais_suspend(struct snd_soc_component *component) |
1075 | { | |
1076 | struct snd_soc_dai *dai; | |
1077 | ||
1078 | /* | |
1079 | * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core | |
1080 | * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state. | |
1081 | * Since the component suspend is called last, we can trap this corner case | |
1082 | * and force the DAIs to release their resources. | |
1083 | */ | |
1084 | for_each_component_dais(component, dai) { | |
1085 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
1086 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
1087 | struct sdw_cdns_dma_data *dma; | |
1088 | int stream; | |
1089 | int ret; | |
1090 | ||
1091 | dma = dai->playback_dma_data; | |
1092 | stream = SNDRV_PCM_STREAM_PLAYBACK; | |
1093 | if (!dma) { | |
1094 | dma = dai->capture_dma_data; | |
1095 | stream = SNDRV_PCM_STREAM_CAPTURE; | |
1096 | } | |
1097 | ||
1098 | if (!dma) | |
1099 | continue; | |
1100 | ||
1101 | if (dma->suspended) | |
1102 | continue; | |
c46302ec | 1103 | |
8ddeafb9 RS |
1104 | if (dma->paused) { |
1105 | dma->suspended = true; | |
1106 | ||
1107 | ret = intel_free_stream(sdw, stream, dai, sdw->instance); | |
1108 | if (ret < 0) | |
1109 | return ret; | |
1110 | } | |
1111 | } | |
1112 | ||
1113 | return 0; | |
1114 | } | |
1115 | ||
b1635596 | 1116 | static const struct snd_soc_dai_ops intel_pcm_dai_ops = { |
5e7484d0 | 1117 | .startup = intel_startup, |
c46302ec | 1118 | .hw_params = intel_hw_params, |
27b198f4 | 1119 | .prepare = intel_prepare, |
c46302ec | 1120 | .hw_free = intel_hw_free, |
8ddeafb9 | 1121 | .trigger = intel_trigger, |
183c7687 | 1122 | .shutdown = intel_shutdown, |
e8444560 PLB |
1123 | .set_stream = intel_pcm_set_sdw_stream, |
1124 | .get_stream = intel_get_sdw_stream, | |
c46302ec VK |
1125 | }; |
1126 | ||
1127 | static const struct snd_soc_component_driver dai_component = { | |
ca682020 | 1128 | .name = "soundwire", |
668c3c23 | 1129 | .probe = intel_component_probe, |
ca682020 CK |
1130 | .suspend = intel_component_dais_suspend, |
1131 | .legacy_dai_naming = 1, | |
c46302ec VK |
1132 | }; |
1133 | ||
1134 | static int intel_create_dai(struct sdw_cdns *cdns, | |
d542bc9e PLB |
1135 | struct snd_soc_dai_driver *dais, |
1136 | enum intel_pdi_type type, | |
63a6aa96 | 1137 | u32 num, u32 off, u32 max_ch) |
c46302ec VK |
1138 | { |
1139 | int i; | |
1140 | ||
1141 | if (num == 0) | |
1142 | return 0; | |
1143 | ||
1144 | /* TODO: Read supported rates/formats from hardware */ | |
1145 | for (i = off; i < (off + num); i++) { | |
bf6d6e68 PLB |
1146 | dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL, |
1147 | "SDW%d Pin%d", | |
1148 | cdns->instance, i); | |
c46302ec VK |
1149 | if (!dais[i].name) |
1150 | return -ENOMEM; | |
1151 | ||
1152 | if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { | |
c46302ec VK |
1153 | dais[i].playback.channels_min = 1; |
1154 | dais[i].playback.channels_max = max_ch; | |
1155 | dais[i].playback.rates = SNDRV_PCM_RATE_48000; | |
1156 | dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE; | |
1157 | } | |
1158 | ||
1159 | if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { | |
39194128 SK |
1160 | dais[i].capture.channels_min = 1; |
1161 | dais[i].capture.channels_max = max_ch; | |
c46302ec VK |
1162 | dais[i].capture.rates = SNDRV_PCM_RATE_48000; |
1163 | dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; | |
1164 | } | |
1165 | ||
63a6aa96 | 1166 | dais[i].ops = &intel_pcm_dai_ops; |
c46302ec VK |
1167 | } |
1168 | ||
1169 | return 0; | |
1170 | } | |
1171 | ||
1172 | static int intel_register_dai(struct sdw_intel *sdw) | |
1173 | { | |
1174 | struct sdw_cdns *cdns = &sdw->cdns; | |
1175 | struct sdw_cdns_streams *stream; | |
1176 | struct snd_soc_dai_driver *dais; | |
1177 | int num_dai, ret, off = 0; | |
1178 | ||
1179 | /* DAIs are created based on total number of PDIs supported */ | |
63a6aa96 | 1180 | num_dai = cdns->pcm.num_pdi; |
c46302ec VK |
1181 | |
1182 | dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); | |
1183 | if (!dais) | |
1184 | return -ENOMEM; | |
1185 | ||
1186 | /* Create PCM DAIs */ | |
1187 | stream = &cdns->pcm; | |
1188 | ||
cf924962 | 1189 | ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in, |
63a6aa96 | 1190 | off, stream->num_ch_in); |
c46302ec VK |
1191 | if (ret) |
1192 | return ret; | |
1193 | ||
1194 | off += cdns->pcm.num_in; | |
1215daee | 1195 | ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out, |
63a6aa96 | 1196 | off, stream->num_ch_out); |
c46302ec VK |
1197 | if (ret) |
1198 | return ret; | |
1199 | ||
1200 | off += cdns->pcm.num_out; | |
1215daee | 1201 | ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd, |
63a6aa96 | 1202 | off, stream->num_ch_bd); |
c46302ec VK |
1203 | if (ret) |
1204 | return ret; | |
1205 | ||
54f391dd PLB |
1206 | return devm_snd_soc_register_component(cdns->dev, &dai_component, |
1207 | dais, num_dai); | |
c46302ec VK |
1208 | } |
1209 | ||
085f4ace PLB |
1210 | static int sdw_master_read_intel_prop(struct sdw_bus *bus) |
1211 | { | |
1212 | struct sdw_master_prop *prop = &bus->prop; | |
1213 | struct fwnode_handle *link; | |
1214 | char name[32]; | |
395713d8 | 1215 | u32 quirk_mask; |
085f4ace PLB |
1216 | |
1217 | /* Find master handle */ | |
1218 | snprintf(name, sizeof(name), | |
1219 | "mipi-sdw-link-%d-subproperties", bus->link_id); | |
1220 | ||
1221 | link = device_get_named_child_node(bus->dev, name); | |
1222 | if (!link) { | |
1223 | dev_err(bus->dev, "Master node %s not found\n", name); | |
1224 | return -EIO; | |
1225 | } | |
1226 | ||
1227 | fwnode_property_read_u32(link, | |
1228 | "intel-sdw-ip-clock", | |
1229 | &prop->mclk_freq); | |
395713d8 | 1230 | |
a19efb52 BL |
1231 | /* the values reported by BIOS are the 2x clock, not the bus clock */ |
1232 | prop->mclk_freq /= 2; | |
1233 | ||
395713d8 PLB |
1234 | fwnode_property_read_u32(link, |
1235 | "intel-quirk-mask", | |
1236 | &quirk_mask); | |
1237 | ||
1238 | if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) | |
1239 | prop->hw_disabled = true; | |
1240 | ||
bb877beb BL |
1241 | prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH | |
1242 | SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY; | |
1243 | ||
085f4ace PLB |
1244 | return 0; |
1245 | } | |
1246 | ||
71bb8a1b VK |
1247 | static int intel_prop_read(struct sdw_bus *bus) |
1248 | { | |
1249 | /* Initialize with default handler to read all DisCo properties */ | |
1250 | sdw_master_read_prop(bus); | |
1251 | ||
085f4ace PLB |
1252 | /* read Intel-specific properties */ |
1253 | sdw_master_read_intel_prop(bus); | |
1254 | ||
71bb8a1b VK |
1255 | return 0; |
1256 | } | |
1257 | ||
c91605f4 SN |
1258 | static struct sdw_master_ops sdw_intel_ops = { |
1259 | .read_prop = sdw_master_read_prop, | |
f6594cdf | 1260 | .override_adr = sdw_dmi_override_adr, |
c91605f4 SN |
1261 | .xfer_msg = cdns_xfer_msg, |
1262 | .xfer_msg_defer = cdns_xfer_msg_defer, | |
1263 | .reset_page_addr = cdns_reset_page_addr, | |
07abeff1 | 1264 | .set_bus_conf = cdns_bus_conf, |
30246e2d SN |
1265 | .pre_bank_switch = intel_pre_bank_switch, |
1266 | .post_bank_switch = intel_post_bank_switch, | |
c91605f4 SN |
1267 | }; |
1268 | ||
dfbe642d PLB |
1269 | static int intel_init(struct sdw_intel *sdw) |
1270 | { | |
4a17c441 PLB |
1271 | bool clock_stop; |
1272 | ||
dfbe642d PLB |
1273 | /* Initialize shim and controller */ |
1274 | intel_link_power_up(sdw); | |
4a17c441 PLB |
1275 | |
1276 | clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns); | |
1277 | ||
1278 | intel_shim_init(sdw, clock_stop); | |
1279 | ||
857a7c42 | 1280 | return 0; |
dfbe642d PLB |
1281 | } |
1282 | ||
71bb8a1b | 1283 | /* |
29a269c6 | 1284 | * probe and init (aux_dev_id argument is required by function prototype but not used) |
71bb8a1b | 1285 | */ |
29a269c6 PLB |
1286 | static int intel_link_probe(struct auxiliary_device *auxdev, |
1287 | const struct auxiliary_device_id *aux_dev_id) | |
1288 | ||
71bb8a1b | 1289 | { |
29a269c6 PLB |
1290 | struct device *dev = &auxdev->dev; |
1291 | struct sdw_intel_link_dev *ldev = auxiliary_dev_to_sdw_intel_link_dev(auxdev); | |
71bb8a1b | 1292 | struct sdw_intel *sdw; |
83e129af | 1293 | struct sdw_cdns *cdns; |
b6109dd6 | 1294 | struct sdw_bus *bus; |
71bb8a1b VK |
1295 | int ret; |
1296 | ||
b6109dd6 | 1297 | sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL); |
71bb8a1b VK |
1298 | if (!sdw) |
1299 | return -ENOMEM; | |
1300 | ||
83e129af PLB |
1301 | cdns = &sdw->cdns; |
1302 | bus = &cdns->bus; | |
b6109dd6 | 1303 | |
29a269c6 PLB |
1304 | sdw->instance = auxdev->id; |
1305 | sdw->link_res = &ldev->link_res; | |
83e129af PLB |
1306 | cdns->dev = dev; |
1307 | cdns->registers = sdw->link_res->registers; | |
1308 | cdns->instance = sdw->instance; | |
1309 | cdns->msg_count = 0; | |
1310 | ||
29a269c6 | 1311 | bus->link_id = auxdev->id; |
1f2dcf3a | 1312 | bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN; |
71bb8a1b | 1313 | |
83e129af | 1314 | sdw_cdns_probe(cdns); |
71bb8a1b VK |
1315 | |
1316 | /* Set property read ops */ | |
c91605f4 | 1317 | sdw_intel_ops.read_prop = intel_prop_read; |
b6109dd6 | 1318 | bus->ops = &sdw_intel_ops; |
71bb8a1b | 1319 | |
b6109dd6 | 1320 | /* set driver data, accessed by snd_soc_dai_get_drvdata() */ |
3edac08e | 1321 | auxiliary_set_drvdata(auxdev, cdns); |
71bb8a1b | 1322 | |
9026118f BL |
1323 | /* use generic bandwidth allocation algorithm */ |
1324 | sdw->cdns.bus.compute_params = sdw_compute_params; | |
1325 | ||
6d9f2dad PLB |
1326 | /* avoid resuming from pm_runtime suspend if it's not required */ |
1327 | dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND); | |
1328 | ||
b6109dd6 | 1329 | ret = sdw_bus_master_add(bus, dev, dev->fwnode); |
71bb8a1b | 1330 | if (ret) { |
b6109dd6 | 1331 | dev_err(dev, "sdw_bus_master_add fail: %d\n", ret); |
9e3d47fb | 1332 | return ret; |
71bb8a1b VK |
1333 | } |
1334 | ||
6d2c6669 | 1335 | if (bus->prop.hw_disabled) |
b6109dd6 PLB |
1336 | dev_info(dev, |
1337 | "SoundWire master %d is disabled, will be ignored\n", | |
1338 | bus->link_id); | |
0ef2986e PLB |
1339 | /* |
1340 | * Ignore BIOS err_threshold, it's a really bad idea when dealing | |
1341 | * with multiple hardware synchronized links | |
1342 | */ | |
1343 | bus->prop.err_threshold = 0; | |
6d2c6669 | 1344 | |
6d2c6669 | 1345 | return 0; |
6d2c6669 PLB |
1346 | } |
1347 | ||
29a269c6 | 1348 | int intel_link_startup(struct auxiliary_device *auxdev) |
6d2c6669 PLB |
1349 | { |
1350 | struct sdw_cdns_stream_config config; | |
29a269c6 | 1351 | struct device *dev = &auxdev->dev; |
3edac08e | 1352 | struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev); |
6d2c6669 PLB |
1353 | struct sdw_intel *sdw = cdns_to_intel(cdns); |
1354 | struct sdw_bus *bus = &cdns->bus; | |
ebf878ed | 1355 | int link_flags; |
857a7c42 | 1356 | bool multi_link; |
caf68819 | 1357 | u32 clock_stop_quirks; |
6d2c6669 PLB |
1358 | int ret; |
1359 | ||
1360 | if (bus->prop.hw_disabled) { | |
1361 | dev_info(dev, | |
1362 | "SoundWire master %d is disabled, ignoring\n", | |
1363 | sdw->instance); | |
395713d8 PLB |
1364 | return 0; |
1365 | } | |
1366 | ||
857a7c42 PLB |
1367 | link_flags = md_flags >> (bus->link_id * 8); |
1368 | multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); | |
1369 | if (!multi_link) { | |
1370 | dev_dbg(dev, "Multi-link is disabled\n"); | |
1371 | bus->multi_link = false; | |
1372 | } else { | |
94eed661 PLB |
1373 | /* |
1374 | * hardware-based synchronization is required regardless | |
1375 | * of the number of segments used by a stream: SSP-based | |
1376 | * synchronization is gated by gsync when the multi-master | |
1377 | * mode is set. | |
1378 | */ | |
857a7c42 | 1379 | bus->multi_link = true; |
94eed661 | 1380 | bus->hw_sync_min_links = 1; |
857a7c42 PLB |
1381 | } |
1382 | ||
1383 | /* Initialize shim, controller */ | |
dfbe642d | 1384 | ret = intel_init(sdw); |
71bb8a1b VK |
1385 | if (ret) |
1386 | goto err_init; | |
1387 | ||
37a2d22b VK |
1388 | /* Read the PDI config and initialize cadence PDI */ |
1389 | intel_pdi_init(sdw, &config); | |
83e129af | 1390 | ret = sdw_cdns_pdi_init(cdns, config); |
71bb8a1b VK |
1391 | if (ret) |
1392 | goto err_init; | |
1393 | ||
37a2d22b VK |
1394 | intel_pdi_ch_update(sdw); |
1395 | ||
aa425707 PLB |
1396 | /* Register DAIs */ |
1397 | ret = intel_register_dai(sdw); | |
1398 | if (ret) { | |
1399 | dev_err(dev, "DAI registration failed: %d\n", ret); | |
1400 | goto err_init; | |
1401 | } | |
1402 | ||
1403 | intel_debugfs_init(sdw); | |
1404 | ||
83e129af | 1405 | ret = sdw_cdns_enable_interrupt(cdns, true); |
71bb8a1b | 1406 | if (ret < 0) { |
b6109dd6 | 1407 | dev_err(dev, "cannot enable interrupts\n"); |
71bb8a1b VK |
1408 | goto err_init; |
1409 | } | |
1410 | ||
857a7c42 PLB |
1411 | /* |
1412 | * follow recommended programming flows to avoid timeouts when | |
1413 | * gsync is enabled | |
1414 | */ | |
1415 | if (multi_link) | |
1416 | intel_shim_sync_arm(sdw); | |
1417 | ||
1418 | ret = sdw_cdns_init(cdns); | |
1419 | if (ret < 0) { | |
1420 | dev_err(dev, "unable to initialize Cadence IP\n"); | |
1421 | goto err_interrupt; | |
1422 | } | |
1423 | ||
83e129af | 1424 | ret = sdw_cdns_exit_reset(cdns); |
49ea07d3 | 1425 | if (ret < 0) { |
b6109dd6 | 1426 | dev_err(dev, "unable to exit bus reset sequence\n"); |
9e3d47fb | 1427 | goto err_interrupt; |
49ea07d3 PLB |
1428 | } |
1429 | ||
857a7c42 PLB |
1430 | if (multi_link) { |
1431 | ret = intel_shim_sync_go(sdw); | |
1432 | if (ret < 0) { | |
1433 | dev_err(dev, "sync go failed: %d\n", ret); | |
1434 | goto err_interrupt; | |
1435 | } | |
1436 | } | |
ff560946 PLB |
1437 | sdw_cdns_check_self_clearing_bits(cdns, __func__, |
1438 | true, INTEL_MASTER_RESET_ITERATIONS); | |
857a7c42 | 1439 | |
ebf878ed | 1440 | /* Enable runtime PM */ |
ebf878ed PLB |
1441 | if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) { |
1442 | pm_runtime_set_autosuspend_delay(dev, | |
1443 | INTEL_MASTER_SUSPEND_DELAY_MS); | |
1444 | pm_runtime_use_autosuspend(dev); | |
1445 | pm_runtime_mark_last_busy(dev); | |
1446 | ||
1447 | pm_runtime_set_active(dev); | |
1448 | pm_runtime_enable(dev); | |
1449 | } | |
1450 | ||
caf68819 PLB |
1451 | clock_stop_quirks = sdw->link_res->clock_stop_quirks; |
1452 | if (clock_stop_quirks & SDW_INTEL_CLK_STOP_NOT_ALLOWED) { | |
1453 | /* | |
1454 | * To keep the clock running we need to prevent | |
1455 | * pm_runtime suspend from happening by increasing the | |
1456 | * reference count. | |
1457 | * This quirk is specified by the parent PCI device in | |
1458 | * case of specific latency requirements. It will have | |
1459 | * no effect if pm_runtime is disabled by the user via | |
1460 | * a module parameter for testing purposes. | |
1461 | */ | |
1462 | pm_runtime_get_noresume(dev); | |
1463 | } | |
1464 | ||
a2d9c161 PLB |
1465 | /* |
1466 | * The runtime PM status of Slave devices is "Unsupported" | |
1467 | * until they report as ATTACHED. If they don't, e.g. because | |
1468 | * there are no Slave devices populated or if the power-on is | |
1469 | * delayed or dependent on a power switch, the Master will | |
1470 | * remain active and prevent its parent from suspending. | |
1471 | * | |
1472 | * Conditionally force the pm_runtime core to re-evaluate the | |
1473 | * Master status in the absence of any Slave activity. A quirk | |
1474 | * is provided to e.g. deal with Slaves that may be powered on | |
1475 | * with a delay. A more complete solution would require the | |
1476 | * definition of Master properties. | |
1477 | */ | |
1478 | if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) | |
1479 | pm_runtime_idle(dev); | |
1480 | ||
e4401abb | 1481 | sdw->startup_done = true; |
71bb8a1b VK |
1482 | return 0; |
1483 | ||
9e3d47fb | 1484 | err_interrupt: |
83e129af | 1485 | sdw_cdns_enable_interrupt(cdns, false); |
71bb8a1b | 1486 | err_init: |
71bb8a1b VK |
1487 | return ret; |
1488 | } | |
1489 | ||
29a269c6 | 1490 | static void intel_link_remove(struct auxiliary_device *auxdev) |
71bb8a1b | 1491 | { |
3edac08e | 1492 | struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev); |
83e129af PLB |
1493 | struct sdw_intel *sdw = cdns_to_intel(cdns); |
1494 | struct sdw_bus *bus = &cdns->bus; | |
b6109dd6 | 1495 | |
caf68819 PLB |
1496 | /* |
1497 | * Since pm_runtime is already disabled, we don't decrease | |
1498 | * the refcount when the clock_stop_quirk is | |
1499 | * SDW_INTEL_CLK_STOP_NOT_ALLOWED | |
1500 | */ | |
b6109dd6 | 1501 | if (!bus->prop.hw_disabled) { |
395713d8 | 1502 | intel_debugfs_exit(sdw); |
83e129af | 1503 | sdw_cdns_enable_interrupt(cdns, false); |
395713d8 | 1504 | } |
b6109dd6 | 1505 | sdw_bus_master_delete(bus); |
71bb8a1b VK |
1506 | } |
1507 | ||
29a269c6 | 1508 | int intel_link_process_wakeen_event(struct auxiliary_device *auxdev) |
ab2c9132 | 1509 | { |
29a269c6 | 1510 | struct device *dev = &auxdev->dev; |
71bb8a1b | 1511 | struct sdw_intel *sdw; |
ab2c9132 RW |
1512 | struct sdw_bus *bus; |
1513 | void __iomem *shim; | |
1514 | u16 wake_sts; | |
71bb8a1b | 1515 | |
3edac08e | 1516 | sdw = auxiliary_get_drvdata(auxdev); |
ab2c9132 | 1517 | bus = &sdw->cdns.bus; |
71bb8a1b | 1518 | |
e4401abb PLB |
1519 | if (bus->prop.hw_disabled || !sdw->startup_done) { |
1520 | dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", | |
1521 | bus->link_id); | |
ab2c9132 | 1522 | return 0; |
395713d8 | 1523 | } |
ab2c9132 RW |
1524 | |
1525 | shim = sdw->link_res->shim; | |
1526 | wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); | |
1527 | ||
1528 | if (!(wake_sts & BIT(sdw->instance))) | |
1529 | return 0; | |
1530 | ||
1531 | /* disable WAKEEN interrupt ASAP to prevent interrupt flood */ | |
1532 | intel_shim_wake(sdw, false); | |
1533 | ||
1534 | /* | |
1535 | * resume the Master, which will generate a bus reset and result in | |
1536 | * Slaves re-attaching and be re-enumerated. The SoundWire physical | |
1537 | * device which generated the wake will trigger an interrupt, which | |
1538 | * will in turn cause the corresponding Linux Slave device to be | |
1539 | * resumed and the Slave codec driver to check the status. | |
1540 | */ | |
1541 | pm_request_resume(dev); | |
71bb8a1b VK |
1542 | |
1543 | return 0; | |
1544 | } | |
1545 | ||
9b3b4b3f PLB |
1546 | /* |
1547 | * PM calls | |
1548 | */ | |
1549 | ||
029bfd1c PLB |
1550 | static int intel_resume_child_device(struct device *dev, void *data) |
1551 | { | |
1552 | int ret; | |
1553 | struct sdw_slave *slave = dev_to_sdw_dev(dev); | |
1554 | ||
1555 | if (!slave->probed) { | |
63198aaa | 1556 | dev_dbg(dev, "skipping device, no probed driver\n"); |
029bfd1c PLB |
1557 | return 0; |
1558 | } | |
1559 | if (!slave->dev_num_sticky) { | |
63198aaa | 1560 | dev_dbg(dev, "skipping device, never detected on bus\n"); |
029bfd1c PLB |
1561 | return 0; |
1562 | } | |
1563 | ||
1564 | ret = pm_request_resume(dev); | |
1565 | if (ret < 0) | |
1566 | dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret); | |
1567 | ||
1568 | return ret; | |
1569 | } | |
1570 | ||
1571 | static int __maybe_unused intel_pm_prepare(struct device *dev) | |
1572 | { | |
1573 | struct sdw_cdns *cdns = dev_get_drvdata(dev); | |
1574 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
1575 | struct sdw_bus *bus = &cdns->bus; | |
1576 | u32 clock_stop_quirks; | |
9283b6f9 | 1577 | int ret; |
029bfd1c PLB |
1578 | |
1579 | if (bus->prop.hw_disabled || !sdw->startup_done) { | |
1580 | dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", | |
1581 | bus->link_id); | |
1582 | return 0; | |
1583 | } | |
1584 | ||
1585 | clock_stop_quirks = sdw->link_res->clock_stop_quirks; | |
1586 | ||
1587 | if (pm_runtime_suspended(dev) && | |
1588 | pm_runtime_suspended(dev->parent) && | |
1589 | ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) || | |
1590 | !clock_stop_quirks)) { | |
1591 | /* | |
1592 | * if we've enabled clock stop, and the parent is suspended, the SHIM registers | |
1593 | * are not accessible and the shim wake cannot be disabled. | |
1594 | * The only solution is to resume the entire bus to full power | |
1595 | */ | |
1596 | ||
1597 | /* | |
1598 | * If any operation in this block fails, we keep going since we don't want | |
1599 | * to prevent system suspend from happening and errors should be recoverable | |
1600 | * on resume. | |
1601 | */ | |
1602 | ||
1603 | /* | |
1604 | * first resume the device for this link. This will also by construction | |
1605 | * resume the PCI parent device. | |
1606 | */ | |
1607 | ret = pm_request_resume(dev); | |
1608 | if (ret < 0) { | |
1609 | dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret); | |
1610 | return 0; | |
1611 | } | |
1612 | ||
1613 | /* | |
1614 | * Continue resuming the entire bus (parent + child devices) to exit | |
1615 | * the clock stop mode. If there are no devices connected on this link | |
1616 | * this is a no-op. | |
1617 | * The resume to full power could have been implemented with a .prepare | |
1618 | * step in SoundWire codec drivers. This would however require a lot | |
1619 | * of code to handle an Intel-specific corner case. It is simpler in | |
1620 | * practice to add a loop at the link level. | |
1621 | */ | |
1622 | ret = device_for_each_child(bus->dev, NULL, intel_resume_child_device); | |
1623 | ||
1624 | if (ret < 0) | |
1625 | dev_err(dev, "%s: intel_resume_child_device failed: %d\n", __func__, ret); | |
1626 | } | |
1627 | ||
1628 | return 0; | |
1629 | } | |
1630 | ||
f046b233 | 1631 | static int __maybe_unused intel_suspend(struct device *dev) |
9b3b4b3f PLB |
1632 | { |
1633 | struct sdw_cdns *cdns = dev_get_drvdata(dev); | |
1634 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
1635 | struct sdw_bus *bus = &cdns->bus; | |
e4be9fac | 1636 | u32 clock_stop_quirks; |
9b3b4b3f PLB |
1637 | int ret; |
1638 | ||
e4401abb PLB |
1639 | if (bus->prop.hw_disabled || !sdw->startup_done) { |
1640 | dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", | |
9b3b4b3f PLB |
1641 | bus->link_id); |
1642 | return 0; | |
1643 | } | |
1644 | ||
b61b8b37 | 1645 | if (pm_runtime_suspended(dev)) { |
63198aaa | 1646 | dev_dbg(dev, "pm_runtime status: suspended\n"); |
b61b8b37 | 1647 | |
e4be9fac PLB |
1648 | clock_stop_quirks = sdw->link_res->clock_stop_quirks; |
1649 | ||
029bfd1c PLB |
1650 | if ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) || |
1651 | !clock_stop_quirks) { | |
1652 | ||
1653 | if (pm_runtime_suspended(dev->parent)) { | |
1654 | /* | |
1655 | * paranoia check: this should not happen with the .prepare | |
1656 | * resume to full power | |
1657 | */ | |
1658 | dev_err(dev, "%s: invalid config: parent is suspended\n", __func__); | |
1659 | } else { | |
1660 | intel_shim_wake(sdw, false); | |
1661 | } | |
e4be9fac PLB |
1662 | } |
1663 | ||
b61b8b37 PLB |
1664 | return 0; |
1665 | } | |
1666 | ||
9b3b4b3f PLB |
1667 | ret = sdw_cdns_enable_interrupt(cdns, false); |
1668 | if (ret < 0) { | |
1669 | dev_err(dev, "cannot disable interrupts on suspend\n"); | |
1670 | return ret; | |
1671 | } | |
1672 | ||
1673 | ret = intel_link_power_down(sdw); | |
1674 | if (ret) { | |
4e3ea93e | 1675 | dev_err(dev, "Link power down failed: %d\n", ret); |
9b3b4b3f PLB |
1676 | return ret; |
1677 | } | |
1678 | ||
1679 | intel_shim_wake(sdw, false); | |
1680 | ||
1681 | return 0; | |
1682 | } | |
1683 | ||
17e0da0b | 1684 | static int __maybe_unused intel_suspend_runtime(struct device *dev) |
ebf878ed PLB |
1685 | { |
1686 | struct sdw_cdns *cdns = dev_get_drvdata(dev); | |
1687 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
1688 | struct sdw_bus *bus = &cdns->bus; | |
a320f41e | 1689 | u32 clock_stop_quirks; |
ebf878ed PLB |
1690 | int ret; |
1691 | ||
e4401abb PLB |
1692 | if (bus->prop.hw_disabled || !sdw->startup_done) { |
1693 | dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", | |
ebf878ed PLB |
1694 | bus->link_id); |
1695 | return 0; | |
1696 | } | |
1697 | ||
a320f41e | 1698 | clock_stop_quirks = sdw->link_res->clock_stop_quirks; |
ebf878ed | 1699 | |
a320f41e | 1700 | if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { |
ebf878ed | 1701 | |
a320f41e PLB |
1702 | ret = sdw_cdns_enable_interrupt(cdns, false); |
1703 | if (ret < 0) { | |
1704 | dev_err(dev, "cannot disable interrupts on suspend\n"); | |
1705 | return ret; | |
1706 | } | |
ebf878ed | 1707 | |
a320f41e PLB |
1708 | ret = intel_link_power_down(sdw); |
1709 | if (ret) { | |
4e3ea93e | 1710 | dev_err(dev, "Link power down failed: %d\n", ret); |
a320f41e PLB |
1711 | return ret; |
1712 | } | |
1713 | ||
1714 | intel_shim_wake(sdw, false); | |
1715 | ||
61fb830b PLB |
1716 | } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || |
1717 | !clock_stop_quirks) { | |
ee3db942 BL |
1718 | bool wake_enable = true; |
1719 | ||
6626a616 RW |
1720 | ret = sdw_cdns_clock_stop(cdns, true); |
1721 | if (ret < 0) { | |
1722 | dev_err(dev, "cannot enable clock stop on suspend\n"); | |
ee3db942 | 1723 | wake_enable = false; |
6626a616 RW |
1724 | } |
1725 | ||
1726 | ret = sdw_cdns_enable_interrupt(cdns, false); | |
1727 | if (ret < 0) { | |
1728 | dev_err(dev, "cannot disable interrupts on suspend\n"); | |
1729 | return ret; | |
1730 | } | |
1731 | ||
1732 | ret = intel_link_power_down(sdw); | |
1733 | if (ret) { | |
4e3ea93e | 1734 | dev_err(dev, "Link power down failed: %d\n", ret); |
6626a616 RW |
1735 | return ret; |
1736 | } | |
1737 | ||
ee3db942 | 1738 | intel_shim_wake(sdw, wake_enable); |
a320f41e PLB |
1739 | } else { |
1740 | dev_err(dev, "%s clock_stop_quirks %x unsupported\n", | |
1741 | __func__, clock_stop_quirks); | |
1742 | ret = -EINVAL; | |
1743 | } | |
1744 | ||
1745 | return ret; | |
ebf878ed PLB |
1746 | } |
1747 | ||
f046b233 | 1748 | static int __maybe_unused intel_resume(struct device *dev) |
9b3b4b3f PLB |
1749 | { |
1750 | struct sdw_cdns *cdns = dev_get_drvdata(dev); | |
1751 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
1752 | struct sdw_bus *bus = &cdns->bus; | |
a2d9c161 | 1753 | int link_flags; |
857a7c42 | 1754 | bool multi_link; |
9b3b4b3f PLB |
1755 | int ret; |
1756 | ||
e4401abb PLB |
1757 | if (bus->prop.hw_disabled || !sdw->startup_done) { |
1758 | dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", | |
9b3b4b3f PLB |
1759 | bus->link_id); |
1760 | return 0; | |
1761 | } | |
1762 | ||
857a7c42 PLB |
1763 | link_flags = md_flags >> (bus->link_id * 8); |
1764 | multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); | |
1765 | ||
b61b8b37 | 1766 | if (pm_runtime_suspended(dev)) { |
63198aaa | 1767 | dev_dbg(dev, "pm_runtime status was suspended, forcing active\n"); |
b61b8b37 PLB |
1768 | |
1769 | /* follow required sequence from runtime_pm.rst */ | |
1770 | pm_runtime_disable(dev); | |
1771 | pm_runtime_set_active(dev); | |
1772 | pm_runtime_mark_last_busy(dev); | |
1773 | pm_runtime_enable(dev); | |
a2d9c161 PLB |
1774 | |
1775 | link_flags = md_flags >> (bus->link_id * 8); | |
857a7c42 | 1776 | |
a2d9c161 PLB |
1777 | if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) |
1778 | pm_runtime_idle(dev); | |
b61b8b37 PLB |
1779 | } |
1780 | ||
9b3b4b3f PLB |
1781 | ret = intel_init(sdw); |
1782 | if (ret) { | |
4e3ea93e | 1783 | dev_err(dev, "%s failed: %d\n", __func__, ret); |
9b3b4b3f PLB |
1784 | return ret; |
1785 | } | |
1786 | ||
99b6a30f PLB |
1787 | /* |
1788 | * make sure all Slaves are tagged as UNATTACHED and provide | |
1789 | * reason for reinitialization | |
1790 | */ | |
1791 | sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); | |
1792 | ||
9b3b4b3f PLB |
1793 | ret = sdw_cdns_enable_interrupt(cdns, true); |
1794 | if (ret < 0) { | |
1795 | dev_err(dev, "cannot enable interrupts during resume\n"); | |
1796 | return ret; | |
1797 | } | |
1798 | ||
857a7c42 PLB |
1799 | /* |
1800 | * follow recommended programming flows to avoid timeouts when | |
1801 | * gsync is enabled | |
1802 | */ | |
1803 | if (multi_link) | |
1804 | intel_shim_sync_arm(sdw); | |
1805 | ||
1806 | ret = sdw_cdns_init(&sdw->cdns); | |
1807 | if (ret < 0) { | |
1808 | dev_err(dev, "unable to initialize Cadence IP during resume\n"); | |
1809 | return ret; | |
1810 | } | |
1811 | ||
9b3b4b3f PLB |
1812 | ret = sdw_cdns_exit_reset(cdns); |
1813 | if (ret < 0) { | |
1814 | dev_err(dev, "unable to exit bus reset sequence during resume\n"); | |
1815 | return ret; | |
1816 | } | |
1817 | ||
857a7c42 PLB |
1818 | if (multi_link) { |
1819 | ret = intel_shim_sync_go(sdw); | |
1820 | if (ret < 0) { | |
1821 | dev_err(dev, "sync go failed during resume\n"); | |
1822 | return ret; | |
1823 | } | |
1824 | } | |
ff560946 PLB |
1825 | sdw_cdns_check_self_clearing_bits(cdns, __func__, |
1826 | true, INTEL_MASTER_RESET_ITERATIONS); | |
857a7c42 | 1827 | |
cb1e6d59 PLB |
1828 | /* |
1829 | * after system resume, the pm_runtime suspend() may kick in | |
1830 | * during the enumeration, before any children device force the | |
1831 | * master device to remain active. Using pm_runtime_get() | |
1832 | * routines is not really possible, since it'd prevent the | |
1833 | * master from suspending. | |
1834 | * A reasonable compromise is to update the pm_runtime | |
1835 | * counters and delay the pm_runtime suspend by several | |
1836 | * seconds, by when all enumeration should be complete. | |
1837 | */ | |
1838 | pm_runtime_mark_last_busy(dev); | |
1839 | ||
9b3b4b3f PLB |
1840 | return ret; |
1841 | } | |
1842 | ||
17e0da0b | 1843 | static int __maybe_unused intel_resume_runtime(struct device *dev) |
ebf878ed PLB |
1844 | { |
1845 | struct sdw_cdns *cdns = dev_get_drvdata(dev); | |
1846 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
1847 | struct sdw_bus *bus = &cdns->bus; | |
a320f41e | 1848 | u32 clock_stop_quirks; |
08abad9f | 1849 | bool clock_stop0; |
857a7c42 PLB |
1850 | int link_flags; |
1851 | bool multi_link; | |
08abad9f | 1852 | int status; |
ebf878ed PLB |
1853 | int ret; |
1854 | ||
e4401abb PLB |
1855 | if (bus->prop.hw_disabled || !sdw->startup_done) { |
1856 | dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", | |
ebf878ed PLB |
1857 | bus->link_id); |
1858 | return 0; | |
1859 | } | |
1860 | ||
e286472c PLB |
1861 | /* unconditionally disable WAKEEN interrupt */ |
1862 | intel_shim_wake(sdw, false); | |
1863 | ||
857a7c42 PLB |
1864 | link_flags = md_flags >> (bus->link_id * 8); |
1865 | multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); | |
1866 | ||
a320f41e | 1867 | clock_stop_quirks = sdw->link_res->clock_stop_quirks; |
ebf878ed | 1868 | |
a320f41e PLB |
1869 | if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { |
1870 | ret = intel_init(sdw); | |
1871 | if (ret) { | |
4e3ea93e | 1872 | dev_err(dev, "%s failed: %d\n", __func__, ret); |
a320f41e PLB |
1873 | return ret; |
1874 | } | |
99b6a30f | 1875 | |
a320f41e PLB |
1876 | /* |
1877 | * make sure all Slaves are tagged as UNATTACHED and provide | |
1878 | * reason for reinitialization | |
1879 | */ | |
1880 | sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); | |
ebf878ed | 1881 | |
a320f41e PLB |
1882 | ret = sdw_cdns_enable_interrupt(cdns, true); |
1883 | if (ret < 0) { | |
1884 | dev_err(dev, "cannot enable interrupts during resume\n"); | |
1885 | return ret; | |
1886 | } | |
1887 | ||
857a7c42 PLB |
1888 | /* |
1889 | * follow recommended programming flows to avoid | |
1890 | * timeouts when gsync is enabled | |
1891 | */ | |
1892 | if (multi_link) | |
1893 | intel_shim_sync_arm(sdw); | |
1894 | ||
1895 | ret = sdw_cdns_init(&sdw->cdns); | |
1896 | if (ret < 0) { | |
1897 | dev_err(dev, "unable to initialize Cadence IP during resume\n"); | |
1898 | return ret; | |
1899 | } | |
1900 | ||
a320f41e PLB |
1901 | ret = sdw_cdns_exit_reset(cdns); |
1902 | if (ret < 0) { | |
1903 | dev_err(dev, "unable to exit bus reset sequence during resume\n"); | |
1904 | return ret; | |
1905 | } | |
857a7c42 PLB |
1906 | |
1907 | if (multi_link) { | |
1908 | ret = intel_shim_sync_go(sdw); | |
1909 | if (ret < 0) { | |
1910 | dev_err(dev, "sync go failed during resume\n"); | |
1911 | return ret; | |
1912 | } | |
1913 | } | |
ff560946 PLB |
1914 | sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime TEARDOWN", |
1915 | true, INTEL_MASTER_RESET_ITERATIONS); | |
1916 | ||
6626a616 RW |
1917 | } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) { |
1918 | ret = intel_init(sdw); | |
1919 | if (ret) { | |
4e3ea93e | 1920 | dev_err(dev, "%s failed: %d\n", __func__, ret); |
6626a616 RW |
1921 | return ret; |
1922 | } | |
1923 | ||
08abad9f RW |
1924 | /* |
1925 | * An exception condition occurs for the CLK_STOP_BUS_RESET | |
1926 | * case if one or more masters remain active. In this condition, | |
1927 | * all the masters are powered on for they are in the same power | |
1928 | * domain. Master can preserve its context for clock stop0, so | |
1929 | * there is no need to clear slave status and reset bus. | |
1930 | */ | |
1931 | clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns); | |
1932 | ||
08abad9f | 1933 | if (!clock_stop0) { |
857a7c42 | 1934 | |
857a7c42 PLB |
1935 | /* |
1936 | * make sure all Slaves are tagged as UNATTACHED and | |
1937 | * provide reason for reinitialization | |
1938 | */ | |
1939 | ||
08abad9f RW |
1940 | status = SDW_UNATTACH_REQUEST_MASTER_RESET; |
1941 | sdw_clear_slave_status(bus, status); | |
08abad9f | 1942 | |
d78071b4 PLB |
1943 | ret = sdw_cdns_enable_interrupt(cdns, true); |
1944 | if (ret < 0) { | |
1945 | dev_err(dev, "cannot enable interrupts during resume\n"); | |
1946 | return ret; | |
1947 | } | |
1948 | ||
1949 | /* | |
1950 | * follow recommended programming flows to avoid | |
1951 | * timeouts when gsync is enabled | |
1952 | */ | |
1953 | if (multi_link) | |
1954 | intel_shim_sync_arm(sdw); | |
6626a616 | 1955 | |
d78071b4 PLB |
1956 | /* |
1957 | * Re-initialize the IP since it was powered-off | |
1958 | */ | |
1959 | sdw_cdns_init(&sdw->cdns); | |
1960 | ||
1961 | } else { | |
1962 | ret = sdw_cdns_enable_interrupt(cdns, true); | |
1963 | if (ret < 0) { | |
1964 | dev_err(dev, "cannot enable interrupts during resume\n"); | |
1965 | return ret; | |
1966 | } | |
6626a616 RW |
1967 | } |
1968 | ||
08abad9f | 1969 | ret = sdw_cdns_clock_restart(cdns, !clock_stop0); |
6626a616 RW |
1970 | if (ret < 0) { |
1971 | dev_err(dev, "unable to restart clock during resume\n"); | |
1972 | return ret; | |
1973 | } | |
d78071b4 PLB |
1974 | |
1975 | if (!clock_stop0) { | |
1976 | ret = sdw_cdns_exit_reset(cdns); | |
1977 | if (ret < 0) { | |
1978 | dev_err(dev, "unable to exit bus reset sequence during resume\n"); | |
1979 | return ret; | |
1980 | } | |
1981 | ||
1982 | if (multi_link) { | |
1983 | ret = intel_shim_sync_go(sdw); | |
1984 | if (ret < 0) { | |
1985 | dev_err(sdw->cdns.dev, "sync go failed during resume\n"); | |
1986 | return ret; | |
1987 | } | |
1988 | } | |
1989 | } | |
ff560946 PLB |
1990 | sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime BUS_RESET", |
1991 | true, INTEL_MASTER_RESET_ITERATIONS); | |
1992 | ||
61fb830b | 1993 | } else if (!clock_stop_quirks) { |
f748f34e PLB |
1994 | |
1995 | clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns); | |
1996 | if (!clock_stop0) | |
1997 | dev_err(dev, "%s invalid configuration, clock was not stopped", __func__); | |
1998 | ||
61fb830b PLB |
1999 | ret = intel_init(sdw); |
2000 | if (ret) { | |
4e3ea93e | 2001 | dev_err(dev, "%s failed: %d\n", __func__, ret); |
61fb830b PLB |
2002 | return ret; |
2003 | } | |
2004 | ||
2005 | ret = sdw_cdns_enable_interrupt(cdns, true); | |
2006 | if (ret < 0) { | |
2007 | dev_err(dev, "cannot enable interrupts during resume\n"); | |
2008 | return ret; | |
2009 | } | |
2010 | ||
2011 | ret = sdw_cdns_clock_restart(cdns, false); | |
2012 | if (ret < 0) { | |
2013 | dev_err(dev, "unable to resume master during resume\n"); | |
2014 | return ret; | |
2015 | } | |
ff560946 PLB |
2016 | |
2017 | sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime no_quirks", | |
2018 | true, INTEL_MASTER_RESET_ITERATIONS); | |
a320f41e PLB |
2019 | } else { |
2020 | dev_err(dev, "%s clock_stop_quirks %x unsupported\n", | |
2021 | __func__, clock_stop_quirks); | |
2022 | ret = -EINVAL; | |
ebf878ed PLB |
2023 | } |
2024 | ||
2025 | return ret; | |
2026 | } | |
2027 | ||
9b3b4b3f | 2028 | static const struct dev_pm_ops intel_pm = { |
029bfd1c | 2029 | .prepare = intel_pm_prepare, |
9b3b4b3f | 2030 | SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) |
ebf878ed | 2031 | SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL) |
9b3b4b3f PLB |
2032 | }; |
2033 | ||
29a269c6 PLB |
2034 | static const struct auxiliary_device_id intel_link_id_table[] = { |
2035 | { .name = "soundwire_intel.link" }, | |
2036 | {}, | |
2037 | }; | |
2038 | MODULE_DEVICE_TABLE(auxiliary, intel_link_id_table); | |
2039 | ||
2040 | static struct auxiliary_driver sdw_intel_drv = { | |
2041 | .probe = intel_link_probe, | |
2042 | .remove = intel_link_remove, | |
71bb8a1b | 2043 | .driver = { |
29a269c6 | 2044 | /* auxiliary_driver_register() sets .name to be the modname */ |
9b3b4b3f | 2045 | .pm = &intel_pm, |
29a269c6 PLB |
2046 | }, |
2047 | .id_table = intel_link_id_table | |
71bb8a1b | 2048 | }; |
29a269c6 | 2049 | module_auxiliary_driver(sdw_intel_drv); |
71bb8a1b VK |
2050 | |
2051 | MODULE_LICENSE("Dual BSD/GPL"); | |
29a269c6 | 2052 | MODULE_DESCRIPTION("Intel Soundwire Link Driver"); |