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