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> |
df72b719 | 11 | #include <linux/io.h> |
37a2d22b | 12 | #include <sound/pcm_params.h> |
ab2c9132 | 13 | #include <linux/pm_runtime.h> |
37a2d22b | 14 | #include <sound/soc.h> |
71bb8a1b VK |
15 | #include <linux/soundwire/sdw_registers.h> |
16 | #include <linux/soundwire/sdw.h> | |
17 | #include <linux/soundwire/sdw_intel.h> | |
18 | #include "cadence_master.h" | |
79ee6631 | 19 | #include "bus.h" |
71bb8a1b VK |
20 | #include "intel.h" |
21 | ||
7d2845d5 | 22 | static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target) |
71bb8a1b VK |
23 | { |
24 | int timeout = 10; | |
25 | u32 reg_read; | |
26 | ||
71bb8a1b VK |
27 | do { |
28 | reg_read = readl(base + offset); | |
7d2845d5 | 29 | if ((reg_read & mask) == target) |
71bb8a1b VK |
30 | return 0; |
31 | ||
32 | timeout--; | |
7d2845d5 | 33 | usleep_range(50, 100); |
71bb8a1b VK |
34 | } while (timeout != 0); |
35 | ||
36 | return -EAGAIN; | |
37 | } | |
38 | ||
7d2845d5 | 39 | static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask) |
71bb8a1b | 40 | { |
71bb8a1b | 41 | writel(value, base + offset); |
7d2845d5 PLB |
42 | return intel_wait_bit(base, offset, mask, 0); |
43 | } | |
71bb8a1b | 44 | |
7d2845d5 PLB |
45 | static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) |
46 | { | |
47 | writel(value, base + offset); | |
48 | return intel_wait_bit(base, offset, mask, mask); | |
71bb8a1b VK |
49 | } |
50 | ||
79ee6631 PLB |
51 | /* |
52 | * debugfs | |
53 | */ | |
54 | #ifdef CONFIG_DEBUG_FS | |
55 | ||
56 | #define RD_BUF (2 * PAGE_SIZE) | |
57 | ||
58 | static ssize_t intel_sprintf(void __iomem *mem, bool l, | |
59 | char *buf, size_t pos, unsigned int reg) | |
60 | { | |
61 | int value; | |
62 | ||
63 | if (l) | |
64 | value = intel_readl(mem, reg); | |
65 | else | |
66 | value = intel_readw(mem, reg); | |
67 | ||
68 | return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value); | |
69 | } | |
70 | ||
71 | static int intel_reg_show(struct seq_file *s_file, void *data) | |
72 | { | |
73 | struct sdw_intel *sdw = s_file->private; | |
2523486b PLB |
74 | void __iomem *s = sdw->link_res->shim; |
75 | void __iomem *a = sdw->link_res->alh; | |
79ee6631 PLB |
76 | char *buf; |
77 | ssize_t ret; | |
78 | int i, j; | |
79 | unsigned int links, reg; | |
80 | ||
81 | buf = kzalloc(RD_BUF, GFP_KERNEL); | |
82 | if (!buf) | |
83 | return -ENOMEM; | |
84 | ||
7f817068 | 85 | links = intel_readl(s, SDW_SHIM_LCAP) & SDW_SHIM_LCAP_LCOUNT_MASK; |
79ee6631 PLB |
86 | |
87 | ret = scnprintf(buf, RD_BUF, "Register Value\n"); | |
88 | ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n"); | |
89 | ||
90 | for (i = 0; i < links; i++) { | |
91 | reg = SDW_SHIM_LCAP + i * 4; | |
92 | ret += intel_sprintf(s, true, buf, ret, reg); | |
93 | } | |
94 | ||
95 | for (i = 0; i < links; i++) { | |
96 | ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i); | |
97 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i)); | |
98 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i)); | |
99 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i)); | |
100 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i)); | |
101 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i)); | |
102 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i)); | |
103 | ||
104 | ret += scnprintf(buf + ret, RD_BUF - ret, "\n PCMSyCH registers\n"); | |
105 | ||
106 | /* | |
107 | * the value 10 is the number of PDIs. We will need a | |
108 | * cleanup to remove hard-coded Intel configurations | |
109 | * from cadence_master.c | |
110 | */ | |
111 | for (j = 0; j < 10; j++) { | |
112 | ret += intel_sprintf(s, false, buf, ret, | |
113 | SDW_SHIM_PCMSYCHM(i, j)); | |
114 | ret += intel_sprintf(s, false, buf, ret, | |
115 | SDW_SHIM_PCMSYCHC(i, j)); | |
116 | } | |
c27ce5c9 | 117 | ret += scnprintf(buf + ret, RD_BUF - ret, "\n IOCTL, CTMCTL\n"); |
79ee6631 | 118 | |
79ee6631 PLB |
119 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i)); |
120 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i)); | |
121 | } | |
122 | ||
123 | ret += scnprintf(buf + ret, RD_BUF - ret, "\nWake registers\n"); | |
124 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN); | |
125 | ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS); | |
126 | ||
127 | ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH STRMzCFG\n"); | |
128 | for (i = 0; i < SDW_ALH_NUM_STREAMS; i++) | |
129 | ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i)); | |
130 | ||
131 | seq_printf(s_file, "%s", buf); | |
132 | kfree(buf); | |
133 | ||
134 | return 0; | |
135 | } | |
136 | DEFINE_SHOW_ATTRIBUTE(intel_reg); | |
137 | ||
0f9138e7 PLB |
138 | static int intel_set_m_datamode(void *data, u64 value) |
139 | { | |
140 | struct sdw_intel *sdw = data; | |
141 | struct sdw_bus *bus = &sdw->cdns.bus; | |
142 | ||
143 | if (value > SDW_PORT_DATA_MODE_STATIC_1) | |
144 | return -EINVAL; | |
145 | ||
146 | /* Userspace changed the hardware state behind the kernel's back */ | |
147 | add_taint(TAINT_USER, LOCKDEP_STILL_OK); | |
148 | ||
149 | bus->params.m_data_mode = value; | |
150 | ||
151 | return 0; | |
152 | } | |
153 | DEFINE_DEBUGFS_ATTRIBUTE(intel_set_m_datamode_fops, NULL, | |
154 | intel_set_m_datamode, "%llu\n"); | |
155 | ||
156 | static int intel_set_s_datamode(void *data, u64 value) | |
157 | { | |
158 | struct sdw_intel *sdw = data; | |
159 | struct sdw_bus *bus = &sdw->cdns.bus; | |
160 | ||
161 | if (value > SDW_PORT_DATA_MODE_STATIC_1) | |
162 | return -EINVAL; | |
163 | ||
164 | /* Userspace changed the hardware state behind the kernel's back */ | |
165 | add_taint(TAINT_USER, LOCKDEP_STILL_OK); | |
166 | ||
167 | bus->params.s_data_mode = value; | |
168 | ||
169 | return 0; | |
170 | } | |
171 | DEFINE_DEBUGFS_ATTRIBUTE(intel_set_s_datamode_fops, NULL, | |
172 | intel_set_s_datamode, "%llu\n"); | |
173 | ||
79ee6631 PLB |
174 | static void intel_debugfs_init(struct sdw_intel *sdw) |
175 | { | |
176 | struct dentry *root = sdw->cdns.bus.debugfs; | |
177 | ||
178 | if (!root) | |
179 | return; | |
180 | ||
181 | sdw->debugfs = debugfs_create_dir("intel-sdw", root); | |
182 | ||
183 | debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw, | |
184 | &intel_reg_fops); | |
185 | ||
0f9138e7 PLB |
186 | debugfs_create_file("intel-m-datamode", 0200, sdw->debugfs, sdw, |
187 | &intel_set_m_datamode_fops); | |
188 | ||
189 | debugfs_create_file("intel-s-datamode", 0200, sdw->debugfs, sdw, | |
190 | &intel_set_s_datamode_fops); | |
191 | ||
79ee6631 PLB |
192 | sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs); |
193 | } | |
194 | ||
195 | static void intel_debugfs_exit(struct sdw_intel *sdw) | |
196 | { | |
197 | debugfs_remove_recursive(sdw->debugfs); | |
198 | } | |
199 | #else | |
200 | static void intel_debugfs_init(struct sdw_intel *sdw) {} | |
201 | static void intel_debugfs_exit(struct sdw_intel *sdw) {} | |
202 | #endif /* CONFIG_DEBUG_FS */ | |
203 | ||
71bb8a1b VK |
204 | /* |
205 | * shim ops | |
206 | */ | |
4a17c441 PLB |
207 | /* this needs to be called with shim_lock */ |
208 | static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw) | |
71bb8a1b | 209 | { |
2523486b | 210 | void __iomem *shim = sdw->link_res->shim; |
71bb8a1b | 211 | unsigned int link_id = sdw->instance; |
4a17c441 | 212 | u16 ioctl; |
71bb8a1b | 213 | |
4a17c441 PLB |
214 | /* Switch to MIP from Glue logic */ |
215 | ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); | |
216 | ||
217 | ioctl &= ~(SDW_SHIM_IOCTL_DOE); | |
71bb8a1b | 218 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 219 | usleep_range(10, 15); |
71bb8a1b | 220 | |
4a17c441 | 221 | ioctl &= ~(SDW_SHIM_IOCTL_DO); |
71bb8a1b | 222 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 223 | usleep_range(10, 15); |
71bb8a1b | 224 | |
4a17c441 | 225 | ioctl |= (SDW_SHIM_IOCTL_MIF); |
71bb8a1b | 226 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 227 | usleep_range(10, 15); |
71bb8a1b | 228 | |
4a17c441 PLB |
229 | ioctl &= ~(SDW_SHIM_IOCTL_BKE); |
230 | ioctl &= ~(SDW_SHIM_IOCTL_COE); | |
71bb8a1b | 231 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 232 | usleep_range(10, 15); |
71bb8a1b | 233 | |
4a17c441 PLB |
234 | /* at this point Master IP has full control of the I/Os */ |
235 | } | |
71bb8a1b | 236 | |
4a17c441 PLB |
237 | /* this needs to be called with shim_lock */ |
238 | static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw) | |
239 | { | |
240 | unsigned int link_id = sdw->instance; | |
241 | void __iomem *shim = sdw->link_res->shim; | |
242 | u16 ioctl; | |
243 | ||
244 | /* Glue logic */ | |
245 | ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); | |
246 | ioctl |= SDW_SHIM_IOCTL_BKE; | |
247 | ioctl |= SDW_SHIM_IOCTL_COE; | |
71bb8a1b | 248 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 249 | usleep_range(10, 15); |
71bb8a1b | 250 | |
4a17c441 | 251 | ioctl &= ~(SDW_SHIM_IOCTL_MIF); |
71bb8a1b | 252 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 253 | usleep_range(10, 15); |
71bb8a1b | 254 | |
4a17c441 PLB |
255 | /* at this point Integration Glue has full control of the I/Os */ |
256 | } | |
257 | ||
b81bcdb4 PLB |
258 | /* this needs to be called with shim_lock */ |
259 | static void intel_shim_init(struct sdw_intel *sdw) | |
4a17c441 PLB |
260 | { |
261 | void __iomem *shim = sdw->link_res->shim; | |
262 | unsigned int link_id = sdw->instance; | |
4a17c441 PLB |
263 | u16 ioctl = 0, act = 0; |
264 | ||
4a17c441 PLB |
265 | /* Initialize Shim */ |
266 | ioctl |= SDW_SHIM_IOCTL_BKE; | |
71bb8a1b | 267 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 | 268 | usleep_range(10, 15); |
71bb8a1b | 269 | |
4a17c441 PLB |
270 | ioctl |= SDW_SHIM_IOCTL_WPDD; |
271 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); | |
272 | usleep_range(10, 15); | |
71bb8a1b | 273 | |
4a17c441 | 274 | ioctl |= SDW_SHIM_IOCTL_DO; |
71bb8a1b | 275 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 PLB |
276 | usleep_range(10, 15); |
277 | ||
278 | ioctl |= SDW_SHIM_IOCTL_DOE; | |
71bb8a1b | 279 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); |
4a17c441 PLB |
280 | usleep_range(10, 15); |
281 | ||
282 | intel_shim_glue_to_master_ip(sdw); | |
71bb8a1b | 283 | |
f067c925 | 284 | u16p_replace_bits(&act, 0x1, SDW_SHIM_CTMCTL_DOAIS); |
71bb8a1b VK |
285 | act |= SDW_SHIM_CTMCTL_DACTQE; |
286 | act |= SDW_SHIM_CTMCTL_DODS; | |
287 | intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act); | |
4a17c441 | 288 | usleep_range(10, 15); |
4a17c441 | 289 | } |
71bb8a1b | 290 | |
0f3c54c2 PLB |
291 | static int intel_shim_check_wake(struct sdw_intel *sdw) |
292 | { | |
293 | void __iomem *shim; | |
294 | u16 wake_sts; | |
4a17c441 | 295 | |
0f3c54c2 PLB |
296 | shim = sdw->link_res->shim; |
297 | wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); | |
298 | ||
299 | return wake_sts & BIT(sdw->instance); | |
4a17c441 PLB |
300 | } |
301 | ||
ab2c9132 | 302 | static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) |
4a17c441 PLB |
303 | { |
304 | void __iomem *shim = sdw->link_res->shim; | |
305 | unsigned int link_id = sdw->instance; | |
306 | u16 wake_en, wake_sts; | |
307 | ||
308 | mutex_lock(sdw->link_res->shim_lock); | |
309 | wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); | |
310 | ||
311 | if (wake_enable) { | |
312 | /* Enable the wakeup */ | |
313 | wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id); | |
314 | intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); | |
315 | } else { | |
316 | /* Disable the wake up interrupt */ | |
317 | wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); | |
318 | intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); | |
319 | ||
320 | /* Clear wake status */ | |
321 | wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); | |
3957db3a LY |
322 | wake_sts |= (SDW_SHIM_WAKESTS_STATUS << link_id); |
323 | intel_writew(shim, SDW_SHIM_WAKESTS, wake_sts); | |
4a17c441 PLB |
324 | } |
325 | mutex_unlock(sdw->link_res->shim_lock); | |
326 | } | |
327 | ||
1e76de2e PLB |
328 | static bool intel_check_cmdsync_unlocked(struct sdw_intel *sdw) |
329 | { | |
330 | void __iomem *shim = sdw->link_res->shim; | |
331 | int sync_reg; | |
332 | ||
333 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); | |
334 | return !!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK); | |
335 | } | |
336 | ||
bc872947 PLB |
337 | static int intel_link_power_up(struct sdw_intel *sdw) |
338 | { | |
339 | unsigned int link_id = sdw->instance; | |
340 | void __iomem *shim = sdw->link_res->shim; | |
341 | u32 *shim_mask = sdw->link_res->shim_mask; | |
342 | struct sdw_bus *bus = &sdw->cdns.bus; | |
343 | struct sdw_master_prop *prop = &bus->prop; | |
344 | u32 spa_mask, cpa_mask; | |
345 | u32 link_control; | |
346 | int ret = 0; | |
347 | u32 syncprd; | |
348 | u32 sync_reg; | |
349 | ||
350 | mutex_lock(sdw->link_res->shim_lock); | |
351 | ||
352 | /* | |
353 | * The hardware relies on an internal counter, typically 4kHz, | |
354 | * to generate the SoundWire SSP - which defines a 'safe' | |
355 | * synchronization point between commands and audio transport | |
356 | * and allows for multi link synchronization. The SYNCPRD value | |
357 | * is only dependent on the oscillator clock provided to | |
358 | * the IP, so adjust based on _DSD properties reported in DSDT | |
359 | * tables. The values reported are based on either 24MHz | |
360 | * (CNL/CML) or 38.4 MHz (ICL/TGL+). | |
361 | */ | |
362 | if (prop->mclk_freq % 6000000) | |
363 | syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; | |
364 | else | |
365 | syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; | |
366 | ||
367 | if (!*shim_mask) { | |
368 | dev_dbg(sdw->cdns.dev, "powering up all links\n"); | |
369 | ||
370 | /* we first need to program the SyncPRD/CPU registers */ | |
371 | dev_dbg(sdw->cdns.dev, | |
372 | "first link up, programming SYNCPRD\n"); | |
373 | ||
374 | /* set SyncPRD period */ | |
375 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); | |
376 | u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD); | |
377 | ||
378 | /* Set SyncCPU bit */ | |
379 | sync_reg |= SDW_SHIM_SYNC_SYNCCPU; | |
380 | intel_writel(shim, SDW_SHIM_SYNC, sync_reg); | |
381 | ||
382 | /* Link power up sequence */ | |
383 | link_control = intel_readl(shim, SDW_SHIM_LCTL); | |
384 | ||
385 | /* only power-up enabled links */ | |
386 | spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask); | |
387 | cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); | |
388 | ||
389 | link_control |= spa_mask; | |
390 | ||
391 | ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); | |
392 | if (ret < 0) { | |
393 | dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret); | |
394 | goto out; | |
395 | } | |
396 | ||
397 | /* SyncCPU will change once link is active */ | |
398 | ret = intel_wait_bit(shim, SDW_SHIM_SYNC, | |
399 | SDW_SHIM_SYNC_SYNCCPU, 0); | |
400 | if (ret < 0) { | |
401 | dev_err(sdw->cdns.dev, | |
402 | "Failed to set SHIM_SYNC: %d\n", ret); | |
403 | goto out; | |
404 | } | |
405 | } | |
406 | ||
407 | *shim_mask |= BIT(link_id); | |
408 | ||
409 | sdw->cdns.link_up = true; | |
b81bcdb4 PLB |
410 | |
411 | intel_shim_init(sdw); | |
412 | ||
bc872947 PLB |
413 | out: |
414 | mutex_unlock(sdw->link_res->shim_lock); | |
415 | ||
416 | return ret; | |
417 | } | |
418 | ||
9b3b4b3f | 419 | static int intel_link_power_down(struct sdw_intel *sdw) |
4a17c441 | 420 | { |
5ee74eb2 | 421 | u32 link_control, spa_mask, cpa_mask; |
4a17c441 PLB |
422 | unsigned int link_id = sdw->instance; |
423 | void __iomem *shim = sdw->link_res->shim; | |
424 | u32 *shim_mask = sdw->link_res->shim_mask; | |
425 | int ret = 0; | |
426 | ||
427 | mutex_lock(sdw->link_res->shim_lock); | |
428 | ||
4a17c441 PLB |
429 | if (!(*shim_mask & BIT(link_id))) |
430 | dev_err(sdw->cdns.dev, | |
431 | "%s: Unbalanced power-up/down calls\n", __func__); | |
432 | ||
ea6942da PLB |
433 | sdw->cdns.link_up = false; |
434 | ||
435 | intel_shim_master_ip_to_glue(sdw); | |
436 | ||
4a17c441 PLB |
437 | *shim_mask &= ~BIT(link_id); |
438 | ||
5ee74eb2 PLB |
439 | if (!*shim_mask) { |
440 | ||
63198aaa | 441 | dev_dbg(sdw->cdns.dev, "powering down all links\n"); |
5ee74eb2 PLB |
442 | |
443 | /* Link power down sequence */ | |
444 | link_control = intel_readl(shim, SDW_SHIM_LCTL); | |
445 | ||
446 | /* only power-down enabled links */ | |
3b4979ca VK |
447 | spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, ~sdw->link_res->link_mask); |
448 | cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); | |
5ee74eb2 PLB |
449 | |
450 | link_control &= spa_mask; | |
451 | ||
452 | ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); | |
ea6942da PLB |
453 | if (ret < 0) { |
454 | dev_err(sdw->cdns.dev, "%s: could not power down link\n", __func__); | |
455 | ||
456 | /* | |
457 | * we leave the sdw->cdns.link_up flag as false since we've disabled | |
458 | * the link at this point and cannot handle interrupts any longer. | |
459 | */ | |
460 | } | |
5ee74eb2 PLB |
461 | } |
462 | ||
4a17c441 | 463 | mutex_unlock(sdw->link_res->shim_lock); |
71bb8a1b | 464 | |
ea6942da | 465 | return ret; |
71bb8a1b VK |
466 | } |
467 | ||
02629e45 PLB |
468 | static void intel_shim_sync_arm(struct sdw_intel *sdw) |
469 | { | |
470 | void __iomem *shim = sdw->link_res->shim; | |
471 | u32 sync_reg; | |
472 | ||
473 | mutex_lock(sdw->link_res->shim_lock); | |
474 | ||
475 | /* update SYNC register */ | |
476 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); | |
477 | sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance); | |
478 | intel_writel(shim, SDW_SHIM_SYNC, sync_reg); | |
479 | ||
480 | mutex_unlock(sdw->link_res->shim_lock); | |
481 | } | |
482 | ||
437e3289 PLB |
483 | static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw) |
484 | { | |
485 | void __iomem *shim = sdw->link_res->shim; | |
486 | u32 sync_reg; | |
71bb8a1b | 487 | |
437e3289 | 488 | /* Read SYNC register */ |
71bb8a1b | 489 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); |
71bb8a1b | 490 | |
437e3289 PLB |
491 | /* |
492 | * Set SyncGO bit to synchronously trigger a bank switch for | |
493 | * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all | |
494 | * the Masters. | |
495 | */ | |
496 | sync_reg |= SDW_SHIM_SYNC_SYNCGO; | |
497 | ||
9c49a4dd | 498 | intel_writel(shim, SDW_SHIM_SYNC, sync_reg); |
71bb8a1b | 499 | |
9c49a4dd | 500 | return 0; |
71bb8a1b VK |
501 | } |
502 | ||
857a7c42 PLB |
503 | static int intel_shim_sync_go(struct sdw_intel *sdw) |
504 | { | |
505 | int ret; | |
506 | ||
507 | mutex_lock(sdw->link_res->shim_lock); | |
508 | ||
509 | ret = intel_shim_sync_go_unlocked(sdw); | |
510 | ||
511 | mutex_unlock(sdw->link_res->shim_lock); | |
512 | ||
513 | return ret; | |
514 | } | |
515 | ||
37a2d22b VK |
516 | /* |
517 | * PDI routines | |
518 | */ | |
519 | static void intel_pdi_init(struct sdw_intel *sdw, | |
d542bc9e | 520 | struct sdw_cdns_stream_config *config) |
37a2d22b | 521 | { |
2523486b | 522 | void __iomem *shim = sdw->link_res->shim; |
37a2d22b | 523 | unsigned int link_id = sdw->instance; |
63a6aa96 | 524 | int pcm_cap; |
37a2d22b VK |
525 | |
526 | /* PCM Stream Capability */ | |
527 | pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id)); | |
528 | ||
3b4979ca VK |
529 | config->pcm_bd = FIELD_GET(SDW_SHIM_PCMSCAP_BSS, pcm_cap); |
530 | config->pcm_in = FIELD_GET(SDW_SHIM_PCMSCAP_ISS, pcm_cap); | |
531 | config->pcm_out = FIELD_GET(SDW_SHIM_PCMSCAP_OSS, pcm_cap); | |
37a2d22b | 532 | |
121f4361 PLB |
533 | dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n", |
534 | config->pcm_bd, config->pcm_in, config->pcm_out); | |
37a2d22b VK |
535 | } |
536 | ||
537 | static int | |
63a6aa96 | 538 | intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num) |
37a2d22b | 539 | { |
2523486b | 540 | void __iomem *shim = sdw->link_res->shim; |
37a2d22b VK |
541 | unsigned int link_id = sdw->instance; |
542 | int count; | |
543 | ||
63a6aa96 | 544 | count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); |
18046335 | 545 | |
63a6aa96 PLB |
546 | /* |
547 | * WORKAROUND: on all existing Intel controllers, pdi | |
548 | * number 2 reports channel count as 1 even though it | |
549 | * supports 8 channels. Performing hardcoding for pdi | |
550 | * number 2. | |
551 | */ | |
552 | if (pdi_num == 2) | |
553 | count = 7; | |
37a2d22b VK |
554 | |
555 | /* zero based values for channel count in register */ | |
556 | count++; | |
557 | ||
558 | return count; | |
559 | } | |
560 | ||
561 | static int intel_pdi_get_ch_update(struct sdw_intel *sdw, | |
d542bc9e PLB |
562 | struct sdw_cdns_pdi *pdi, |
563 | unsigned int num_pdi, | |
63a6aa96 | 564 | unsigned int *num_ch) |
37a2d22b VK |
565 | { |
566 | int i, ch_count = 0; | |
567 | ||
568 | for (i = 0; i < num_pdi; i++) { | |
63a6aa96 | 569 | pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num); |
37a2d22b VK |
570 | ch_count += pdi->ch_count; |
571 | pdi++; | |
572 | } | |
573 | ||
574 | *num_ch = ch_count; | |
575 | return 0; | |
576 | } | |
577 | ||
578 | static int intel_pdi_stream_ch_update(struct sdw_intel *sdw, | |
63a6aa96 | 579 | struct sdw_cdns_streams *stream) |
37a2d22b VK |
580 | { |
581 | intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd, | |
63a6aa96 | 582 | &stream->num_ch_bd); |
37a2d22b VK |
583 | |
584 | intel_pdi_get_ch_update(sdw, stream->in, stream->num_in, | |
63a6aa96 | 585 | &stream->num_ch_in); |
37a2d22b VK |
586 | |
587 | intel_pdi_get_ch_update(sdw, stream->out, stream->num_out, | |
63a6aa96 | 588 | &stream->num_ch_out); |
37a2d22b VK |
589 | |
590 | return 0; | |
591 | } | |
592 | ||
37a2d22b VK |
593 | static void |
594 | intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) | |
595 | { | |
2523486b | 596 | void __iomem *shim = sdw->link_res->shim; |
37a2d22b VK |
597 | unsigned int link_id = sdw->instance; |
598 | int pdi_conf = 0; | |
599 | ||
c134f914 PLB |
600 | /* the Bulk and PCM streams are not contiguous */ |
601 | pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; | |
602 | if (pdi->num >= 2) | |
603 | pdi->intel_alh_id += 2; | |
37a2d22b VK |
604 | |
605 | /* | |
606 | * Program stream parameters to stream SHIM register | |
607 | * This is applicable for PCM stream only. | |
608 | */ | |
609 | if (pdi->type != SDW_STREAM_PCM) | |
610 | return; | |
611 | ||
612 | if (pdi->dir == SDW_DATA_DIR_RX) | |
613 | pdi_conf |= SDW_SHIM_PCMSYCM_DIR; | |
614 | else | |
615 | pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR); | |
616 | ||
f067c925 VK |
617 | u32p_replace_bits(&pdi_conf, pdi->intel_alh_id, SDW_SHIM_PCMSYCM_STREAM); |
618 | u32p_replace_bits(&pdi_conf, pdi->l_ch_num, SDW_SHIM_PCMSYCM_LCHN); | |
619 | u32p_replace_bits(&pdi_conf, pdi->h_ch_num, SDW_SHIM_PCMSYCM_HCHN); | |
37a2d22b VK |
620 | |
621 | intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf); | |
622 | } | |
623 | ||
624 | static void | |
625 | intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) | |
626 | { | |
2523486b | 627 | void __iomem *alh = sdw->link_res->alh; |
37a2d22b VK |
628 | unsigned int link_id = sdw->instance; |
629 | unsigned int conf; | |
630 | ||
c134f914 PLB |
631 | /* the Bulk and PCM streams are not contiguous */ |
632 | pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; | |
633 | if (pdi->num >= 2) | |
634 | pdi->intel_alh_id += 2; | |
37a2d22b VK |
635 | |
636 | /* Program Stream config ALH register */ | |
637 | conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id)); | |
638 | ||
f067c925 VK |
639 | u32p_replace_bits(&conf, SDW_ALH_STRMZCFG_DMAT_VAL, SDW_ALH_STRMZCFG_DMAT); |
640 | u32p_replace_bits(&conf, pdi->ch_count - 1, SDW_ALH_STRMZCFG_CHN); | |
37a2d22b VK |
641 | |
642 | intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); | |
643 | } | |
644 | ||
4b206d34 | 645 | static int intel_params_stream(struct sdw_intel *sdw, |
b86947b5 | 646 | int stream, |
d542bc9e | 647 | struct snd_soc_dai *dai, |
4b206d34 RW |
648 | struct snd_pcm_hw_params *hw_params, |
649 | int link_id, int alh_stream_id) | |
c46302ec | 650 | { |
2523486b | 651 | struct sdw_intel_link_res *res = sdw->link_res; |
4b206d34 | 652 | struct sdw_intel_stream_params_data params_data; |
05c8afe4 | 653 | |
b86947b5 | 654 | params_data.stream = stream; /* direction */ |
4b206d34 RW |
655 | params_data.dai = dai; |
656 | params_data.hw_params = hw_params; | |
657 | params_data.link_id = link_id; | |
658 | params_data.alh_stream_id = alh_stream_id; | |
c46302ec | 659 | |
4b206d34 RW |
660 | if (res->ops && res->ops->params_stream && res->dev) |
661 | return res->ops->params_stream(res->dev, | |
662 | ¶ms_data); | |
c46302ec VK |
663 | return -EIO; |
664 | } | |
665 | ||
eff346f2 | 666 | static int intel_free_stream(struct sdw_intel *sdw, |
b86947b5 | 667 | int stream, |
eff346f2 PLB |
668 | struct snd_soc_dai *dai, |
669 | int link_id) | |
670 | { | |
671 | struct sdw_intel_link_res *res = sdw->link_res; | |
672 | struct sdw_intel_stream_free_data free_data; | |
673 | ||
b86947b5 | 674 | free_data.stream = stream; /* direction */ |
eff346f2 PLB |
675 | free_data.dai = dai; |
676 | free_data.link_id = link_id; | |
677 | ||
678 | if (res->ops && res->ops->free_stream && res->dev) | |
679 | return res->ops->free_stream(res->dev, | |
680 | &free_data); | |
681 | ||
682 | return 0; | |
683 | } | |
684 | ||
c46302ec VK |
685 | /* |
686 | * DAI routines | |
687 | */ | |
688 | ||
c46302ec | 689 | static int intel_hw_params(struct snd_pcm_substream *substream, |
d542bc9e PLB |
690 | struct snd_pcm_hw_params *params, |
691 | struct snd_soc_dai *dai) | |
c46302ec VK |
692 | { |
693 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
694 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
e0767e39 | 695 | struct sdw_cdns_dai_runtime *dai_runtime; |
57a34790 | 696 | struct sdw_cdns_pdi *pdi; |
c46302ec VK |
697 | struct sdw_stream_config sconfig; |
698 | struct sdw_port_config *pconfig; | |
57a34790 PLB |
699 | int ch, dir; |
700 | int ret; | |
c46302ec | 701 | |
7dddead7 | 702 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
e0767e39 | 703 | if (!dai_runtime) |
c46302ec VK |
704 | return -EIO; |
705 | ||
706 | ch = params_channels(params); | |
707 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | |
708 | dir = SDW_DATA_DIR_RX; | |
709 | else | |
710 | dir = SDW_DATA_DIR_TX; | |
711 | ||
63a6aa96 | 712 | pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); |
57a34790 PLB |
713 | |
714 | if (!pdi) { | |
715 | ret = -EINVAL; | |
716 | goto error; | |
c46302ec VK |
717 | } |
718 | ||
57a34790 PLB |
719 | /* do run-time configurations for SHIM, ALH and PDI/PORT */ |
720 | intel_pdi_shim_configure(sdw, pdi); | |
721 | intel_pdi_alh_configure(sdw, pdi); | |
722 | sdw_cdns_config_stream(cdns, ch, dir, pdi); | |
c46302ec | 723 | |
a5a0239c | 724 | /* store pdi and hw_params, may be needed in prepare step */ |
e0767e39 PLB |
725 | dai_runtime->paused = false; |
726 | dai_runtime->suspended = false; | |
727 | dai_runtime->pdi = pdi; | |
c46302ec VK |
728 | |
729 | /* Inform DSP about PDI stream number */ | |
b86947b5 | 730 | ret = intel_params_stream(sdw, substream->stream, dai, params, |
4b206d34 | 731 | sdw->instance, |
57a34790 PLB |
732 | pdi->intel_alh_id); |
733 | if (ret) | |
734 | goto error; | |
c46302ec VK |
735 | |
736 | sconfig.direction = dir; | |
737 | sconfig.ch_count = ch; | |
738 | sconfig.frame_rate = params_rate(params); | |
e0767e39 | 739 | sconfig.type = dai_runtime->stream_type; |
c46302ec | 740 | |
63a6aa96 | 741 | sconfig.bps = snd_pcm_format_width(params_format(params)); |
c46302ec VK |
742 | |
743 | /* Port configuration */ | |
235ae89b | 744 | pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL); |
c46302ec VK |
745 | if (!pconfig) { |
746 | ret = -ENOMEM; | |
57a34790 | 747 | goto error; |
c46302ec VK |
748 | } |
749 | ||
57a34790 PLB |
750 | pconfig->num = pdi->num; |
751 | pconfig->ch_mask = (1 << ch) - 1; | |
c46302ec VK |
752 | |
753 | ret = sdw_stream_add_master(&cdns->bus, &sconfig, | |
e0767e39 | 754 | pconfig, 1, dai_runtime->stream); |
57a34790 | 755 | if (ret) |
17ed5bef | 756 | dev_err(cdns->dev, "add master to stream failed:%d\n", ret); |
c46302ec | 757 | |
c46302ec | 758 | kfree(pconfig); |
57a34790 | 759 | error: |
c46302ec VK |
760 | return ret; |
761 | } | |
762 | ||
27b198f4 RW |
763 | static int intel_prepare(struct snd_pcm_substream *substream, |
764 | struct snd_soc_dai *dai) | |
765 | { | |
a5a0239c BL |
766 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); |
767 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
e0767e39 | 768 | struct sdw_cdns_dai_runtime *dai_runtime; |
a5a0239c | 769 | int ch, dir; |
244eb888 | 770 | int ret = 0; |
27b198f4 | 771 | |
7dddead7 | 772 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
e0767e39 PLB |
773 | if (!dai_runtime) { |
774 | dev_err(dai->dev, "failed to get dai runtime in %s\n", | |
27b198f4 RW |
775 | __func__); |
776 | return -EIO; | |
777 | } | |
778 | ||
e0767e39 | 779 | if (dai_runtime->suspended) { |
0a0d1740 PLB |
780 | struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); |
781 | struct snd_pcm_hw_params *hw_params; | |
782 | ||
783 | hw_params = &rtd->dpcm[substream->stream].hw_params; | |
784 | ||
e0767e39 | 785 | dai_runtime->suspended = false; |
a5a0239c BL |
786 | |
787 | /* | |
788 | * .prepare() is called after system resume, where we | |
789 | * need to reinitialize the SHIM/ALH/Cadence IP. | |
790 | * .prepare() is also called to deal with underflows, | |
791 | * but in those cases we cannot touch ALH/SHIM | |
792 | * registers | |
793 | */ | |
794 | ||
795 | /* configure stream */ | |
0a0d1740 | 796 | ch = params_channels(hw_params); |
a5a0239c BL |
797 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) |
798 | dir = SDW_DATA_DIR_RX; | |
799 | else | |
800 | dir = SDW_DATA_DIR_TX; | |
801 | ||
e0767e39 PLB |
802 | intel_pdi_shim_configure(sdw, dai_runtime->pdi); |
803 | intel_pdi_alh_configure(sdw, dai_runtime->pdi); | |
804 | sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi); | |
a5a0239c BL |
805 | |
806 | /* Inform DSP about PDI stream number */ | |
b86947b5 | 807 | ret = intel_params_stream(sdw, substream->stream, dai, |
0a0d1740 | 808 | hw_params, |
a5a0239c | 809 | sdw->instance, |
e0767e39 | 810 | dai_runtime->pdi->intel_alh_id); |
a5a0239c BL |
811 | } |
812 | ||
a5a0239c | 813 | return ret; |
27b198f4 RW |
814 | } |
815 | ||
c46302ec VK |
816 | static int |
817 | intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) | |
818 | { | |
819 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
eff346f2 | 820 | struct sdw_intel *sdw = cdns_to_intel(cdns); |
e0767e39 | 821 | struct sdw_cdns_dai_runtime *dai_runtime; |
c46302ec VK |
822 | int ret; |
823 | ||
7dddead7 | 824 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
e0767e39 | 825 | if (!dai_runtime) |
c46302ec VK |
826 | return -EIO; |
827 | ||
244eb888 PLB |
828 | /* |
829 | * The sdw stream state will transition to RELEASED when stream-> | |
830 | * master_list is empty. So the stream state will transition to | |
831 | * DEPREPARED for the first cpu-dai and to RELEASED for the last | |
832 | * cpu-dai. | |
833 | */ | |
e0767e39 | 834 | ret = sdw_stream_remove_master(&cdns->bus, dai_runtime->stream); |
eff346f2 | 835 | if (ret < 0) { |
17ed5bef | 836 | dev_err(dai->dev, "remove master from stream %s failed: %d\n", |
e0767e39 | 837 | dai_runtime->stream->name, ret); |
eff346f2 PLB |
838 | return ret; |
839 | } | |
c46302ec | 840 | |
b86947b5 | 841 | ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); |
eff346f2 | 842 | if (ret < 0) { |
4e3ea93e | 843 | dev_err(dai->dev, "intel_free_stream: failed %d\n", ret); |
eff346f2 PLB |
844 | return ret; |
845 | } | |
846 | ||
e0767e39 | 847 | dai_runtime->pdi = NULL; |
a5a0239c | 848 | |
eff346f2 | 849 | return 0; |
c46302ec VK |
850 | } |
851 | ||
852 | static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, | |
d542bc9e | 853 | void *stream, int direction) |
c46302ec | 854 | { |
63a6aa96 | 855 | return cdns_set_sdw_stream(dai, stream, direction); |
c46302ec VK |
856 | } |
857 | ||
09553140 PLB |
858 | static void *intel_get_sdw_stream(struct snd_soc_dai *dai, |
859 | int direction) | |
860 | { | |
7dddead7 | 861 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); |
e0767e39 | 862 | struct sdw_cdns_dai_runtime *dai_runtime; |
09553140 | 863 | |
7dddead7 | 864 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
e0767e39 | 865 | if (!dai_runtime) |
06dcb4e4 | 866 | return ERR_PTR(-EINVAL); |
09553140 | 867 | |
e0767e39 | 868 | return dai_runtime->stream; |
09553140 PLB |
869 | } |
870 | ||
8ddeafb9 RS |
871 | static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) |
872 | { | |
873 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
874 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
6d1c1a73 | 875 | struct sdw_intel_link_res *res = sdw->link_res; |
e0767e39 | 876 | struct sdw_cdns_dai_runtime *dai_runtime; |
8ddeafb9 RS |
877 | int ret = 0; |
878 | ||
6d1c1a73 BL |
879 | /* |
880 | * The .trigger callback is used to send required IPC to audio | |
881 | * firmware. The .free_stream callback will still be called | |
882 | * by intel_free_stream() in the TRIGGER_SUSPEND case. | |
883 | */ | |
884 | if (res->ops && res->ops->trigger) | |
885 | res->ops->trigger(dai, cmd, substream->stream); | |
886 | ||
7dddead7 | 887 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
e0767e39 PLB |
888 | if (!dai_runtime) { |
889 | dev_err(dai->dev, "failed to get dai runtime in %s\n", | |
8ddeafb9 RS |
890 | __func__); |
891 | return -EIO; | |
892 | } | |
893 | ||
894 | switch (cmd) { | |
895 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
896 | ||
897 | /* | |
898 | * The .prepare callback is used to deal with xruns and resume operations. | |
899 | * In the case of xruns, the DMAs and SHIM registers cannot be touched, | |
900 | * but for resume operations the DMAs and SHIM registers need to be initialized. | |
901 | * the .trigger callback is used to track the suspend case only. | |
902 | */ | |
903 | ||
e0767e39 | 904 | dai_runtime->suspended = true; |
8ddeafb9 RS |
905 | |
906 | ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); | |
907 | break; | |
908 | ||
909 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
e0767e39 | 910 | dai_runtime->paused = true; |
8ddeafb9 RS |
911 | break; |
912 | case SNDRV_PCM_TRIGGER_STOP: | |
913 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
e0767e39 | 914 | dai_runtime->paused = false; |
8ddeafb9 RS |
915 | break; |
916 | default: | |
917 | break; | |
918 | } | |
919 | ||
920 | return ret; | |
921 | } | |
922 | ||
3e9c9f90 PLB |
923 | static int intel_component_probe(struct snd_soc_component *component) |
924 | { | |
925 | int ret; | |
926 | ||
927 | /* | |
928 | * make sure the device is pm_runtime_active before initiating | |
929 | * bus transactions during the card registration. | |
930 | * We use pm_runtime_resume() here, without taking a reference | |
931 | * and releasing it immediately. | |
932 | */ | |
933 | ret = pm_runtime_resume(component->dev); | |
934 | if (ret < 0 && ret != -EACCES) | |
935 | return ret; | |
936 | ||
937 | return 0; | |
938 | } | |
939 | ||
8ddeafb9 RS |
940 | static int intel_component_dais_suspend(struct snd_soc_component *component) |
941 | { | |
942 | struct snd_soc_dai *dai; | |
943 | ||
944 | /* | |
945 | * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core | |
946 | * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state. | |
947 | * Since the component suspend is called last, we can trap this corner case | |
948 | * and force the DAIs to release their resources. | |
949 | */ | |
950 | for_each_component_dais(component, dai) { | |
951 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
952 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
e0767e39 | 953 | struct sdw_cdns_dai_runtime *dai_runtime; |
8ddeafb9 RS |
954 | int ret; |
955 | ||
7dddead7 | 956 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
8ddeafb9 | 957 | |
e0767e39 | 958 | if (!dai_runtime) |
8ddeafb9 RS |
959 | continue; |
960 | ||
e0767e39 | 961 | if (dai_runtime->suspended) |
8ddeafb9 | 962 | continue; |
c46302ec | 963 | |
e0767e39 PLB |
964 | if (dai_runtime->paused) { |
965 | dai_runtime->suspended = true; | |
8ddeafb9 | 966 | |
7dddead7 | 967 | ret = intel_free_stream(sdw, dai_runtime->direction, dai, sdw->instance); |
8ddeafb9 RS |
968 | if (ret < 0) |
969 | return ret; | |
970 | } | |
971 | } | |
972 | ||
973 | return 0; | |
974 | } | |
975 | ||
b1635596 | 976 | static const struct snd_soc_dai_ops intel_pcm_dai_ops = { |
c46302ec | 977 | .hw_params = intel_hw_params, |
27b198f4 | 978 | .prepare = intel_prepare, |
c46302ec | 979 | .hw_free = intel_hw_free, |
8ddeafb9 | 980 | .trigger = intel_trigger, |
e8444560 PLB |
981 | .set_stream = intel_pcm_set_sdw_stream, |
982 | .get_stream = intel_get_sdw_stream, | |
c46302ec VK |
983 | }; |
984 | ||
985 | static const struct snd_soc_component_driver dai_component = { | |
ca682020 | 986 | .name = "soundwire", |
668c3c23 | 987 | .probe = intel_component_probe, |
ca682020 CK |
988 | .suspend = intel_component_dais_suspend, |
989 | .legacy_dai_naming = 1, | |
c46302ec VK |
990 | }; |
991 | ||
992 | static int intel_create_dai(struct sdw_cdns *cdns, | |
d542bc9e PLB |
993 | struct snd_soc_dai_driver *dais, |
994 | enum intel_pdi_type type, | |
63a6aa96 | 995 | u32 num, u32 off, u32 max_ch) |
c46302ec VK |
996 | { |
997 | int i; | |
998 | ||
999 | if (num == 0) | |
1000 | return 0; | |
1001 | ||
c46302ec | 1002 | for (i = off; i < (off + num); i++) { |
bf6d6e68 PLB |
1003 | dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL, |
1004 | "SDW%d Pin%d", | |
1005 | cdns->instance, i); | |
c46302ec VK |
1006 | if (!dais[i].name) |
1007 | return -ENOMEM; | |
1008 | ||
1009 | if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { | |
c46302ec VK |
1010 | dais[i].playback.channels_min = 1; |
1011 | dais[i].playback.channels_max = max_ch; | |
c46302ec VK |
1012 | } |
1013 | ||
1014 | if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { | |
39194128 SK |
1015 | dais[i].capture.channels_min = 1; |
1016 | dais[i].capture.channels_max = max_ch; | |
c46302ec VK |
1017 | } |
1018 | ||
63a6aa96 | 1019 | dais[i].ops = &intel_pcm_dai_ops; |
c46302ec VK |
1020 | } |
1021 | ||
1022 | return 0; | |
1023 | } | |
1024 | ||
1025 | static int intel_register_dai(struct sdw_intel *sdw) | |
1026 | { | |
7dddead7 | 1027 | struct sdw_cdns_dai_runtime **dai_runtime_array; |
30cbae66 | 1028 | struct sdw_cdns_stream_config config; |
c46302ec VK |
1029 | struct sdw_cdns *cdns = &sdw->cdns; |
1030 | struct sdw_cdns_streams *stream; | |
1031 | struct snd_soc_dai_driver *dais; | |
1032 | int num_dai, ret, off = 0; | |
1033 | ||
30cbae66 PLB |
1034 | /* Read the PDI config and initialize cadence PDI */ |
1035 | intel_pdi_init(sdw, &config); | |
1036 | ret = sdw_cdns_pdi_init(cdns, config); | |
1037 | if (ret) | |
1038 | return ret; | |
1039 | ||
59e924fe | 1040 | intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm); |
30cbae66 | 1041 | |
c46302ec | 1042 | /* DAIs are created based on total number of PDIs supported */ |
63a6aa96 | 1043 | num_dai = cdns->pcm.num_pdi; |
c46302ec | 1044 | |
7dddead7 PLB |
1045 | dai_runtime_array = devm_kcalloc(cdns->dev, num_dai, |
1046 | sizeof(struct sdw_cdns_dai_runtime *), | |
1047 | GFP_KERNEL); | |
1048 | if (!dai_runtime_array) | |
1049 | return -ENOMEM; | |
1050 | cdns->dai_runtime_array = dai_runtime_array; | |
1051 | ||
c46302ec VK |
1052 | dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); |
1053 | if (!dais) | |
1054 | return -ENOMEM; | |
1055 | ||
1056 | /* Create PCM DAIs */ | |
1057 | stream = &cdns->pcm; | |
1058 | ||
cf924962 | 1059 | ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in, |
63a6aa96 | 1060 | off, stream->num_ch_in); |
c46302ec VK |
1061 | if (ret) |
1062 | return ret; | |
1063 | ||
1064 | off += cdns->pcm.num_in; | |
1215daee | 1065 | ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out, |
63a6aa96 | 1066 | off, stream->num_ch_out); |
c46302ec VK |
1067 | if (ret) |
1068 | return ret; | |
1069 | ||
1070 | off += cdns->pcm.num_out; | |
1215daee | 1071 | ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd, |
63a6aa96 | 1072 | off, stream->num_ch_bd); |
c46302ec VK |
1073 | if (ret) |
1074 | return ret; | |
1075 | ||
54f391dd PLB |
1076 | return devm_snd_soc_register_component(cdns->dev, &dai_component, |
1077 | dais, num_dai); | |
c46302ec VK |
1078 | } |
1079 | ||
c46302ec | 1080 | |
b3ad31f3 | 1081 | const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops = { |
fb2dc6a0 PLB |
1082 | .debugfs_init = intel_debugfs_init, |
1083 | .debugfs_exit = intel_debugfs_exit, | |
1084 | ||
b6234bcc PLB |
1085 | .register_dai = intel_register_dai, |
1086 | ||
3db0c5a6 PLB |
1087 | .check_clock_stop = intel_check_clock_stop, |
1088 | .start_bus = intel_start_bus, | |
1089 | .start_bus_after_reset = intel_start_bus_after_reset, | |
1090 | .start_bus_after_clock_stop = intel_start_bus_after_clock_stop, | |
1091 | .stop_bus = intel_stop_bus, | |
1092 | ||
49c9ff45 PLB |
1093 | .link_power_up = intel_link_power_up, |
1094 | .link_power_down = intel_link_power_down, | |
1095 | ||
36e3b385 PLB |
1096 | .shim_check_wake = intel_shim_check_wake, |
1097 | .shim_wake = intel_shim_wake, | |
1098 | ||
b3ad31f3 PLB |
1099 | .pre_bank_switch = intel_pre_bank_switch, |
1100 | .post_bank_switch = intel_post_bank_switch, | |
84706e9a PLB |
1101 | |
1102 | .sync_arm = intel_shim_sync_arm, | |
1103 | .sync_go_unlocked = intel_shim_sync_go_unlocked, | |
1104 | .sync_go = intel_shim_sync_go, | |
1e76de2e | 1105 | .sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked, |
b3ad31f3 PLB |
1106 | }; |
1107 | EXPORT_SYMBOL_NS(sdw_intel_cnl_hw_ops, SOUNDWIRE_INTEL); | |
1108 |