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; | |
3d912d1a | 263 | u16 ioctl = 0, act; |
4a17c441 | 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 | |
3d912d1a | 284 | act = intel_readw(shim, SDW_SHIM_CTMCTL(link_id)); |
f067c925 | 285 | u16p_replace_bits(&act, 0x1, SDW_SHIM_CTMCTL_DOAIS); |
71bb8a1b VK |
286 | act |= SDW_SHIM_CTMCTL_DACTQE; |
287 | act |= SDW_SHIM_CTMCTL_DODS; | |
288 | intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act); | |
4a17c441 | 289 | usleep_range(10, 15); |
4a17c441 | 290 | } |
71bb8a1b | 291 | |
0f3c54c2 PLB |
292 | static int intel_shim_check_wake(struct sdw_intel *sdw) |
293 | { | |
294 | void __iomem *shim; | |
295 | u16 wake_sts; | |
4a17c441 | 296 | |
0f3c54c2 PLB |
297 | shim = sdw->link_res->shim; |
298 | wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); | |
299 | ||
300 | return wake_sts & BIT(sdw->instance); | |
4a17c441 PLB |
301 | } |
302 | ||
ab2c9132 | 303 | static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) |
4a17c441 PLB |
304 | { |
305 | void __iomem *shim = sdw->link_res->shim; | |
306 | unsigned int link_id = sdw->instance; | |
307 | u16 wake_en, wake_sts; | |
308 | ||
309 | mutex_lock(sdw->link_res->shim_lock); | |
310 | wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); | |
311 | ||
312 | if (wake_enable) { | |
313 | /* Enable the wakeup */ | |
314 | wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id); | |
315 | intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); | |
316 | } else { | |
317 | /* Disable the wake up interrupt */ | |
318 | wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); | |
319 | intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); | |
320 | ||
321 | /* Clear wake status */ | |
322 | wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); | |
3957db3a LY |
323 | wake_sts |= (SDW_SHIM_WAKESTS_STATUS << link_id); |
324 | intel_writew(shim, SDW_SHIM_WAKESTS, wake_sts); | |
4a17c441 PLB |
325 | } |
326 | mutex_unlock(sdw->link_res->shim_lock); | |
327 | } | |
328 | ||
1e76de2e PLB |
329 | static bool intel_check_cmdsync_unlocked(struct sdw_intel *sdw) |
330 | { | |
331 | void __iomem *shim = sdw->link_res->shim; | |
332 | int sync_reg; | |
333 | ||
334 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); | |
335 | return !!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK); | |
336 | } | |
337 | ||
bc872947 PLB |
338 | static int intel_link_power_up(struct sdw_intel *sdw) |
339 | { | |
340 | unsigned int link_id = sdw->instance; | |
341 | void __iomem *shim = sdw->link_res->shim; | |
342 | u32 *shim_mask = sdw->link_res->shim_mask; | |
343 | struct sdw_bus *bus = &sdw->cdns.bus; | |
344 | struct sdw_master_prop *prop = &bus->prop; | |
345 | u32 spa_mask, cpa_mask; | |
346 | u32 link_control; | |
347 | int ret = 0; | |
348 | u32 syncprd; | |
349 | u32 sync_reg; | |
350 | ||
351 | mutex_lock(sdw->link_res->shim_lock); | |
352 | ||
353 | /* | |
354 | * The hardware relies on an internal counter, typically 4kHz, | |
355 | * to generate the SoundWire SSP - which defines a 'safe' | |
356 | * synchronization point between commands and audio transport | |
357 | * and allows for multi link synchronization. The SYNCPRD value | |
358 | * is only dependent on the oscillator clock provided to | |
359 | * the IP, so adjust based on _DSD properties reported in DSDT | |
360 | * tables. The values reported are based on either 24MHz | |
361 | * (CNL/CML) or 38.4 MHz (ICL/TGL+). | |
362 | */ | |
363 | if (prop->mclk_freq % 6000000) | |
364 | syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; | |
365 | else | |
366 | syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; | |
367 | ||
368 | if (!*shim_mask) { | |
369 | dev_dbg(sdw->cdns.dev, "powering up all links\n"); | |
370 | ||
371 | /* we first need to program the SyncPRD/CPU registers */ | |
372 | dev_dbg(sdw->cdns.dev, | |
373 | "first link up, programming SYNCPRD\n"); | |
374 | ||
375 | /* set SyncPRD period */ | |
376 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); | |
377 | u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD); | |
378 | ||
379 | /* Set SyncCPU bit */ | |
380 | sync_reg |= SDW_SHIM_SYNC_SYNCCPU; | |
381 | intel_writel(shim, SDW_SHIM_SYNC, sync_reg); | |
382 | ||
383 | /* Link power up sequence */ | |
384 | link_control = intel_readl(shim, SDW_SHIM_LCTL); | |
385 | ||
386 | /* only power-up enabled links */ | |
387 | spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask); | |
388 | cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); | |
389 | ||
390 | link_control |= spa_mask; | |
391 | ||
392 | ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); | |
393 | if (ret < 0) { | |
394 | dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret); | |
395 | goto out; | |
396 | } | |
397 | ||
398 | /* SyncCPU will change once link is active */ | |
399 | ret = intel_wait_bit(shim, SDW_SHIM_SYNC, | |
400 | SDW_SHIM_SYNC_SYNCCPU, 0); | |
401 | if (ret < 0) { | |
402 | dev_err(sdw->cdns.dev, | |
403 | "Failed to set SHIM_SYNC: %d\n", ret); | |
404 | goto out; | |
405 | } | |
406 | } | |
407 | ||
408 | *shim_mask |= BIT(link_id); | |
409 | ||
410 | sdw->cdns.link_up = true; | |
b81bcdb4 PLB |
411 | |
412 | intel_shim_init(sdw); | |
413 | ||
bc872947 PLB |
414 | out: |
415 | mutex_unlock(sdw->link_res->shim_lock); | |
416 | ||
417 | return ret; | |
418 | } | |
419 | ||
9b3b4b3f | 420 | static int intel_link_power_down(struct sdw_intel *sdw) |
4a17c441 | 421 | { |
5ee74eb2 | 422 | u32 link_control, spa_mask, cpa_mask; |
4a17c441 PLB |
423 | unsigned int link_id = sdw->instance; |
424 | void __iomem *shim = sdw->link_res->shim; | |
425 | u32 *shim_mask = sdw->link_res->shim_mask; | |
426 | int ret = 0; | |
427 | ||
428 | mutex_lock(sdw->link_res->shim_lock); | |
429 | ||
4a17c441 PLB |
430 | if (!(*shim_mask & BIT(link_id))) |
431 | dev_err(sdw->cdns.dev, | |
432 | "%s: Unbalanced power-up/down calls\n", __func__); | |
433 | ||
ea6942da PLB |
434 | sdw->cdns.link_up = false; |
435 | ||
436 | intel_shim_master_ip_to_glue(sdw); | |
437 | ||
4a17c441 PLB |
438 | *shim_mask &= ~BIT(link_id); |
439 | ||
5ee74eb2 PLB |
440 | if (!*shim_mask) { |
441 | ||
63198aaa | 442 | dev_dbg(sdw->cdns.dev, "powering down all links\n"); |
5ee74eb2 PLB |
443 | |
444 | /* Link power down sequence */ | |
445 | link_control = intel_readl(shim, SDW_SHIM_LCTL); | |
446 | ||
447 | /* only power-down enabled links */ | |
3b4979ca VK |
448 | spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, ~sdw->link_res->link_mask); |
449 | cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); | |
5ee74eb2 PLB |
450 | |
451 | link_control &= spa_mask; | |
452 | ||
453 | ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); | |
ea6942da PLB |
454 | if (ret < 0) { |
455 | dev_err(sdw->cdns.dev, "%s: could not power down link\n", __func__); | |
456 | ||
457 | /* | |
458 | * we leave the sdw->cdns.link_up flag as false since we've disabled | |
459 | * the link at this point and cannot handle interrupts any longer. | |
460 | */ | |
461 | } | |
5ee74eb2 PLB |
462 | } |
463 | ||
4a17c441 | 464 | mutex_unlock(sdw->link_res->shim_lock); |
71bb8a1b | 465 | |
ea6942da | 466 | return ret; |
71bb8a1b VK |
467 | } |
468 | ||
02629e45 PLB |
469 | static void intel_shim_sync_arm(struct sdw_intel *sdw) |
470 | { | |
471 | void __iomem *shim = sdw->link_res->shim; | |
472 | u32 sync_reg; | |
473 | ||
474 | mutex_lock(sdw->link_res->shim_lock); | |
475 | ||
476 | /* update SYNC register */ | |
477 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); | |
478 | sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance); | |
479 | intel_writel(shim, SDW_SHIM_SYNC, sync_reg); | |
480 | ||
481 | mutex_unlock(sdw->link_res->shim_lock); | |
482 | } | |
483 | ||
437e3289 PLB |
484 | static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw) |
485 | { | |
486 | void __iomem *shim = sdw->link_res->shim; | |
487 | u32 sync_reg; | |
71bb8a1b | 488 | |
437e3289 | 489 | /* Read SYNC register */ |
71bb8a1b | 490 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); |
71bb8a1b | 491 | |
437e3289 PLB |
492 | /* |
493 | * Set SyncGO bit to synchronously trigger a bank switch for | |
494 | * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all | |
495 | * the Masters. | |
496 | */ | |
497 | sync_reg |= SDW_SHIM_SYNC_SYNCGO; | |
498 | ||
9c49a4dd | 499 | intel_writel(shim, SDW_SHIM_SYNC, sync_reg); |
71bb8a1b | 500 | |
9c49a4dd | 501 | return 0; |
71bb8a1b VK |
502 | } |
503 | ||
857a7c42 PLB |
504 | static int intel_shim_sync_go(struct sdw_intel *sdw) |
505 | { | |
506 | int ret; | |
507 | ||
508 | mutex_lock(sdw->link_res->shim_lock); | |
509 | ||
510 | ret = intel_shim_sync_go_unlocked(sdw); | |
511 | ||
512 | mutex_unlock(sdw->link_res->shim_lock); | |
513 | ||
514 | return ret; | |
515 | } | |
516 | ||
37a2d22b VK |
517 | /* |
518 | * PDI routines | |
519 | */ | |
520 | static void intel_pdi_init(struct sdw_intel *sdw, | |
d542bc9e | 521 | struct sdw_cdns_stream_config *config) |
37a2d22b | 522 | { |
2523486b | 523 | void __iomem *shim = sdw->link_res->shim; |
37a2d22b | 524 | unsigned int link_id = sdw->instance; |
63a6aa96 | 525 | int pcm_cap; |
37a2d22b VK |
526 | |
527 | /* PCM Stream Capability */ | |
528 | pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id)); | |
529 | ||
3b4979ca VK |
530 | config->pcm_bd = FIELD_GET(SDW_SHIM_PCMSCAP_BSS, pcm_cap); |
531 | config->pcm_in = FIELD_GET(SDW_SHIM_PCMSCAP_ISS, pcm_cap); | |
532 | config->pcm_out = FIELD_GET(SDW_SHIM_PCMSCAP_OSS, pcm_cap); | |
37a2d22b | 533 | |
121f4361 PLB |
534 | dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n", |
535 | config->pcm_bd, config->pcm_in, config->pcm_out); | |
37a2d22b VK |
536 | } |
537 | ||
538 | static int | |
63a6aa96 | 539 | intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num) |
37a2d22b | 540 | { |
2523486b | 541 | void __iomem *shim = sdw->link_res->shim; |
37a2d22b VK |
542 | unsigned int link_id = sdw->instance; |
543 | int count; | |
544 | ||
63a6aa96 | 545 | count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); |
18046335 | 546 | |
63a6aa96 PLB |
547 | /* |
548 | * WORKAROUND: on all existing Intel controllers, pdi | |
549 | * number 2 reports channel count as 1 even though it | |
550 | * supports 8 channels. Performing hardcoding for pdi | |
551 | * number 2. | |
552 | */ | |
553 | if (pdi_num == 2) | |
554 | count = 7; | |
37a2d22b VK |
555 | |
556 | /* zero based values for channel count in register */ | |
557 | count++; | |
558 | ||
559 | return count; | |
560 | } | |
561 | ||
562 | static int intel_pdi_get_ch_update(struct sdw_intel *sdw, | |
d542bc9e PLB |
563 | struct sdw_cdns_pdi *pdi, |
564 | unsigned int num_pdi, | |
63a6aa96 | 565 | unsigned int *num_ch) |
37a2d22b VK |
566 | { |
567 | int i, ch_count = 0; | |
568 | ||
569 | for (i = 0; i < num_pdi; i++) { | |
63a6aa96 | 570 | pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num); |
37a2d22b VK |
571 | ch_count += pdi->ch_count; |
572 | pdi++; | |
573 | } | |
574 | ||
575 | *num_ch = ch_count; | |
576 | return 0; | |
577 | } | |
578 | ||
579 | static int intel_pdi_stream_ch_update(struct sdw_intel *sdw, | |
63a6aa96 | 580 | struct sdw_cdns_streams *stream) |
37a2d22b VK |
581 | { |
582 | intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd, | |
63a6aa96 | 583 | &stream->num_ch_bd); |
37a2d22b VK |
584 | |
585 | intel_pdi_get_ch_update(sdw, stream->in, stream->num_in, | |
63a6aa96 | 586 | &stream->num_ch_in); |
37a2d22b VK |
587 | |
588 | intel_pdi_get_ch_update(sdw, stream->out, stream->num_out, | |
63a6aa96 | 589 | &stream->num_ch_out); |
37a2d22b VK |
590 | |
591 | return 0; | |
592 | } | |
593 | ||
37a2d22b VK |
594 | static void |
595 | intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) | |
596 | { | |
2523486b | 597 | void __iomem *shim = sdw->link_res->shim; |
37a2d22b VK |
598 | unsigned int link_id = sdw->instance; |
599 | int pdi_conf = 0; | |
600 | ||
c134f914 PLB |
601 | /* the Bulk and PCM streams are not contiguous */ |
602 | pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; | |
603 | if (pdi->num >= 2) | |
604 | pdi->intel_alh_id += 2; | |
37a2d22b VK |
605 | |
606 | /* | |
607 | * Program stream parameters to stream SHIM register | |
608 | * This is applicable for PCM stream only. | |
609 | */ | |
610 | if (pdi->type != SDW_STREAM_PCM) | |
611 | return; | |
612 | ||
613 | if (pdi->dir == SDW_DATA_DIR_RX) | |
614 | pdi_conf |= SDW_SHIM_PCMSYCM_DIR; | |
615 | else | |
616 | pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR); | |
617 | ||
f067c925 VK |
618 | u32p_replace_bits(&pdi_conf, pdi->intel_alh_id, SDW_SHIM_PCMSYCM_STREAM); |
619 | u32p_replace_bits(&pdi_conf, pdi->l_ch_num, SDW_SHIM_PCMSYCM_LCHN); | |
620 | u32p_replace_bits(&pdi_conf, pdi->h_ch_num, SDW_SHIM_PCMSYCM_HCHN); | |
37a2d22b VK |
621 | |
622 | intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf); | |
623 | } | |
624 | ||
625 | static void | |
626 | intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) | |
627 | { | |
2523486b | 628 | void __iomem *alh = sdw->link_res->alh; |
37a2d22b VK |
629 | unsigned int link_id = sdw->instance; |
630 | unsigned int conf; | |
631 | ||
c134f914 PLB |
632 | /* the Bulk and PCM streams are not contiguous */ |
633 | pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; | |
634 | if (pdi->num >= 2) | |
635 | pdi->intel_alh_id += 2; | |
37a2d22b VK |
636 | |
637 | /* Program Stream config ALH register */ | |
638 | conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id)); | |
639 | ||
f067c925 VK |
640 | u32p_replace_bits(&conf, SDW_ALH_STRMZCFG_DMAT_VAL, SDW_ALH_STRMZCFG_DMAT); |
641 | u32p_replace_bits(&conf, pdi->ch_count - 1, SDW_ALH_STRMZCFG_CHN); | |
37a2d22b VK |
642 | |
643 | intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); | |
644 | } | |
645 | ||
4b206d34 | 646 | static int intel_params_stream(struct sdw_intel *sdw, |
1d905d35 | 647 | struct snd_pcm_substream *substream, |
d542bc9e | 648 | struct snd_soc_dai *dai, |
4b206d34 RW |
649 | struct snd_pcm_hw_params *hw_params, |
650 | int link_id, int alh_stream_id) | |
c46302ec | 651 | { |
2523486b | 652 | struct sdw_intel_link_res *res = sdw->link_res; |
4b206d34 | 653 | struct sdw_intel_stream_params_data params_data; |
05c8afe4 | 654 | |
1d905d35 | 655 | params_data.substream = substream; |
4b206d34 RW |
656 | params_data.dai = dai; |
657 | params_data.hw_params = hw_params; | |
658 | params_data.link_id = link_id; | |
659 | params_data.alh_stream_id = alh_stream_id; | |
c46302ec | 660 | |
4b206d34 RW |
661 | if (res->ops && res->ops->params_stream && res->dev) |
662 | return res->ops->params_stream(res->dev, | |
663 | ¶ms_data); | |
c46302ec VK |
664 | return -EIO; |
665 | } | |
666 | ||
667 | /* | |
668 | * DAI routines | |
669 | */ | |
670 | ||
c46302ec | 671 | static int intel_hw_params(struct snd_pcm_substream *substream, |
d542bc9e PLB |
672 | struct snd_pcm_hw_params *params, |
673 | struct snd_soc_dai *dai) | |
c46302ec VK |
674 | { |
675 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
676 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
e0767e39 | 677 | struct sdw_cdns_dai_runtime *dai_runtime; |
57a34790 | 678 | struct sdw_cdns_pdi *pdi; |
c46302ec VK |
679 | struct sdw_stream_config sconfig; |
680 | struct sdw_port_config *pconfig; | |
57a34790 PLB |
681 | int ch, dir; |
682 | int ret; | |
c46302ec | 683 | |
7dddead7 | 684 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
e0767e39 | 685 | if (!dai_runtime) |
c46302ec VK |
686 | return -EIO; |
687 | ||
688 | ch = params_channels(params); | |
689 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | |
690 | dir = SDW_DATA_DIR_RX; | |
691 | else | |
692 | dir = SDW_DATA_DIR_TX; | |
693 | ||
63a6aa96 | 694 | pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); |
57a34790 PLB |
695 | |
696 | if (!pdi) { | |
697 | ret = -EINVAL; | |
698 | goto error; | |
c46302ec VK |
699 | } |
700 | ||
57a34790 PLB |
701 | /* do run-time configurations for SHIM, ALH and PDI/PORT */ |
702 | intel_pdi_shim_configure(sdw, pdi); | |
703 | intel_pdi_alh_configure(sdw, pdi); | |
704 | sdw_cdns_config_stream(cdns, ch, dir, pdi); | |
c46302ec | 705 | |
a5a0239c | 706 | /* store pdi and hw_params, may be needed in prepare step */ |
e0767e39 PLB |
707 | dai_runtime->paused = false; |
708 | dai_runtime->suspended = false; | |
709 | dai_runtime->pdi = pdi; | |
c46302ec VK |
710 | |
711 | /* Inform DSP about PDI stream number */ | |
1d905d35 | 712 | ret = intel_params_stream(sdw, substream, dai, params, |
4b206d34 | 713 | sdw->instance, |
57a34790 PLB |
714 | pdi->intel_alh_id); |
715 | if (ret) | |
716 | goto error; | |
c46302ec VK |
717 | |
718 | sconfig.direction = dir; | |
719 | sconfig.ch_count = ch; | |
720 | sconfig.frame_rate = params_rate(params); | |
e0767e39 | 721 | sconfig.type = dai_runtime->stream_type; |
c46302ec | 722 | |
63a6aa96 | 723 | sconfig.bps = snd_pcm_format_width(params_format(params)); |
c46302ec VK |
724 | |
725 | /* Port configuration */ | |
235ae89b | 726 | pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL); |
c46302ec VK |
727 | if (!pconfig) { |
728 | ret = -ENOMEM; | |
57a34790 | 729 | goto error; |
c46302ec VK |
730 | } |
731 | ||
57a34790 PLB |
732 | pconfig->num = pdi->num; |
733 | pconfig->ch_mask = (1 << ch) - 1; | |
c46302ec VK |
734 | |
735 | ret = sdw_stream_add_master(&cdns->bus, &sconfig, | |
e0767e39 | 736 | pconfig, 1, dai_runtime->stream); |
57a34790 | 737 | if (ret) |
17ed5bef | 738 | dev_err(cdns->dev, "add master to stream failed:%d\n", ret); |
c46302ec | 739 | |
c46302ec | 740 | kfree(pconfig); |
57a34790 | 741 | error: |
c46302ec VK |
742 | return ret; |
743 | } | |
744 | ||
27b198f4 RW |
745 | static int intel_prepare(struct snd_pcm_substream *substream, |
746 | struct snd_soc_dai *dai) | |
747 | { | |
a5a0239c BL |
748 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); |
749 | struct sdw_intel *sdw = cdns_to_intel(cdns); | |
e0767e39 | 750 | struct sdw_cdns_dai_runtime *dai_runtime; |
a5a0239c | 751 | int ch, dir; |
244eb888 | 752 | int ret = 0; |
27b198f4 | 753 | |
7dddead7 | 754 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
e0767e39 PLB |
755 | if (!dai_runtime) { |
756 | dev_err(dai->dev, "failed to get dai runtime in %s\n", | |
27b198f4 RW |
757 | __func__); |
758 | return -EIO; | |
759 | } | |
760 | ||
e0767e39 | 761 | if (dai_runtime->suspended) { |
0a0d1740 PLB |
762 | struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); |
763 | struct snd_pcm_hw_params *hw_params; | |
764 | ||
765 | hw_params = &rtd->dpcm[substream->stream].hw_params; | |
766 | ||
e0767e39 | 767 | dai_runtime->suspended = false; |
a5a0239c BL |
768 | |
769 | /* | |
770 | * .prepare() is called after system resume, where we | |
771 | * need to reinitialize the SHIM/ALH/Cadence IP. | |
772 | * .prepare() is also called to deal with underflows, | |
773 | * but in those cases we cannot touch ALH/SHIM | |
774 | * registers | |
775 | */ | |
776 | ||
777 | /* configure stream */ | |
0a0d1740 | 778 | ch = params_channels(hw_params); |
a5a0239c BL |
779 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) |
780 | dir = SDW_DATA_DIR_RX; | |
781 | else | |
782 | dir = SDW_DATA_DIR_TX; | |
783 | ||
e0767e39 PLB |
784 | intel_pdi_shim_configure(sdw, dai_runtime->pdi); |
785 | intel_pdi_alh_configure(sdw, dai_runtime->pdi); | |
786 | sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi); | |
a5a0239c BL |
787 | |
788 | /* Inform DSP about PDI stream number */ | |
1d905d35 | 789 | ret = intel_params_stream(sdw, substream, dai, |
0a0d1740 | 790 | hw_params, |
a5a0239c | 791 | sdw->instance, |
e0767e39 | 792 | dai_runtime->pdi->intel_alh_id); |
a5a0239c BL |
793 | } |
794 | ||
a5a0239c | 795 | return ret; |
27b198f4 RW |
796 | } |
797 | ||
c46302ec VK |
798 | static int |
799 | intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) | |
800 | { | |
801 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
e0767e39 | 802 | struct sdw_cdns_dai_runtime *dai_runtime; |
c46302ec VK |
803 | int ret; |
804 | ||
7dddead7 | 805 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
e0767e39 | 806 | if (!dai_runtime) |
c46302ec VK |
807 | return -EIO; |
808 | ||
244eb888 PLB |
809 | /* |
810 | * The sdw stream state will transition to RELEASED when stream-> | |
811 | * master_list is empty. So the stream state will transition to | |
812 | * DEPREPARED for the first cpu-dai and to RELEASED for the last | |
813 | * cpu-dai. | |
814 | */ | |
e0767e39 | 815 | ret = sdw_stream_remove_master(&cdns->bus, dai_runtime->stream); |
eff346f2 | 816 | if (ret < 0) { |
17ed5bef | 817 | dev_err(dai->dev, "remove master from stream %s failed: %d\n", |
e0767e39 | 818 | dai_runtime->stream->name, ret); |
eff346f2 PLB |
819 | return ret; |
820 | } | |
c46302ec | 821 | |
e0767e39 | 822 | dai_runtime->pdi = NULL; |
a5a0239c | 823 | |
eff346f2 | 824 | return 0; |
c46302ec VK |
825 | } |
826 | ||
827 | static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, | |
d542bc9e | 828 | void *stream, int direction) |
c46302ec | 829 | { |
63a6aa96 | 830 | return cdns_set_sdw_stream(dai, stream, direction); |
c46302ec VK |
831 | } |
832 | ||
09553140 PLB |
833 | static void *intel_get_sdw_stream(struct snd_soc_dai *dai, |
834 | int direction) | |
835 | { | |
7dddead7 | 836 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); |
e0767e39 | 837 | struct sdw_cdns_dai_runtime *dai_runtime; |
09553140 | 838 | |
7dddead7 | 839 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
e0767e39 | 840 | if (!dai_runtime) |
06dcb4e4 | 841 | return ERR_PTR(-EINVAL); |
09553140 | 842 | |
e0767e39 | 843 | return dai_runtime->stream; |
09553140 PLB |
844 | } |
845 | ||
8ddeafb9 RS |
846 | static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) |
847 | { | |
848 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
e0767e39 | 849 | struct sdw_cdns_dai_runtime *dai_runtime; |
8ddeafb9 RS |
850 | int ret = 0; |
851 | ||
7dddead7 | 852 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
e0767e39 PLB |
853 | if (!dai_runtime) { |
854 | dev_err(dai->dev, "failed to get dai runtime in %s\n", | |
8ddeafb9 RS |
855 | __func__); |
856 | return -EIO; | |
857 | } | |
858 | ||
859 | switch (cmd) { | |
860 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
861 | ||
862 | /* | |
863 | * The .prepare callback is used to deal with xruns and resume operations. | |
864 | * In the case of xruns, the DMAs and SHIM registers cannot be touched, | |
865 | * but for resume operations the DMAs and SHIM registers need to be initialized. | |
866 | * the .trigger callback is used to track the suspend case only. | |
867 | */ | |
868 | ||
e0767e39 | 869 | dai_runtime->suspended = true; |
8ddeafb9 | 870 | |
8ddeafb9 RS |
871 | break; |
872 | ||
873 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
e0767e39 | 874 | dai_runtime->paused = true; |
8ddeafb9 RS |
875 | break; |
876 | case SNDRV_PCM_TRIGGER_STOP: | |
877 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
e0767e39 | 878 | dai_runtime->paused = false; |
8ddeafb9 RS |
879 | break; |
880 | default: | |
881 | break; | |
882 | } | |
883 | ||
884 | return ret; | |
885 | } | |
886 | ||
3e9c9f90 PLB |
887 | static int intel_component_probe(struct snd_soc_component *component) |
888 | { | |
889 | int ret; | |
890 | ||
891 | /* | |
892 | * make sure the device is pm_runtime_active before initiating | |
893 | * bus transactions during the card registration. | |
894 | * We use pm_runtime_resume() here, without taking a reference | |
895 | * and releasing it immediately. | |
896 | */ | |
897 | ret = pm_runtime_resume(component->dev); | |
898 | if (ret < 0 && ret != -EACCES) | |
899 | return ret; | |
900 | ||
901 | return 0; | |
902 | } | |
903 | ||
8ddeafb9 RS |
904 | static int intel_component_dais_suspend(struct snd_soc_component *component) |
905 | { | |
906 | struct snd_soc_dai *dai; | |
907 | ||
908 | /* | |
909 | * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core | |
910 | * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state. | |
911 | * Since the component suspend is called last, we can trap this corner case | |
912 | * and force the DAIs to release their resources. | |
913 | */ | |
914 | for_each_component_dais(component, dai) { | |
915 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
e0767e39 | 916 | struct sdw_cdns_dai_runtime *dai_runtime; |
8ddeafb9 | 917 | |
7dddead7 | 918 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
8ddeafb9 | 919 | |
e0767e39 | 920 | if (!dai_runtime) |
8ddeafb9 RS |
921 | continue; |
922 | ||
e0767e39 | 923 | if (dai_runtime->suspended) |
8ddeafb9 | 924 | continue; |
c46302ec | 925 | |
01271045 | 926 | if (dai_runtime->paused) |
e0767e39 | 927 | dai_runtime->suspended = true; |
8ddeafb9 RS |
928 | } |
929 | ||
930 | return 0; | |
931 | } | |
932 | ||
b1635596 | 933 | static const struct snd_soc_dai_ops intel_pcm_dai_ops = { |
c46302ec | 934 | .hw_params = intel_hw_params, |
27b198f4 | 935 | .prepare = intel_prepare, |
c46302ec | 936 | .hw_free = intel_hw_free, |
8ddeafb9 | 937 | .trigger = intel_trigger, |
e8444560 PLB |
938 | .set_stream = intel_pcm_set_sdw_stream, |
939 | .get_stream = intel_get_sdw_stream, | |
c46302ec VK |
940 | }; |
941 | ||
942 | static const struct snd_soc_component_driver dai_component = { | |
ca682020 | 943 | .name = "soundwire", |
668c3c23 | 944 | .probe = intel_component_probe, |
ca682020 CK |
945 | .suspend = intel_component_dais_suspend, |
946 | .legacy_dai_naming = 1, | |
c46302ec VK |
947 | }; |
948 | ||
949 | static int intel_create_dai(struct sdw_cdns *cdns, | |
d542bc9e PLB |
950 | struct snd_soc_dai_driver *dais, |
951 | enum intel_pdi_type type, | |
63a6aa96 | 952 | u32 num, u32 off, u32 max_ch) |
c46302ec VK |
953 | { |
954 | int i; | |
955 | ||
956 | if (num == 0) | |
957 | return 0; | |
958 | ||
c46302ec | 959 | for (i = off; i < (off + num); i++) { |
bf6d6e68 PLB |
960 | dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL, |
961 | "SDW%d Pin%d", | |
962 | cdns->instance, i); | |
c46302ec VK |
963 | if (!dais[i].name) |
964 | return -ENOMEM; | |
965 | ||
966 | if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { | |
c46302ec VK |
967 | dais[i].playback.channels_min = 1; |
968 | dais[i].playback.channels_max = max_ch; | |
c46302ec VK |
969 | } |
970 | ||
971 | if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { | |
39194128 SK |
972 | dais[i].capture.channels_min = 1; |
973 | dais[i].capture.channels_max = max_ch; | |
c46302ec VK |
974 | } |
975 | ||
63a6aa96 | 976 | dais[i].ops = &intel_pcm_dai_ops; |
c46302ec VK |
977 | } |
978 | ||
979 | return 0; | |
980 | } | |
981 | ||
982 | static int intel_register_dai(struct sdw_intel *sdw) | |
983 | { | |
7dddead7 | 984 | struct sdw_cdns_dai_runtime **dai_runtime_array; |
30cbae66 | 985 | struct sdw_cdns_stream_config config; |
c46302ec VK |
986 | struct sdw_cdns *cdns = &sdw->cdns; |
987 | struct sdw_cdns_streams *stream; | |
988 | struct snd_soc_dai_driver *dais; | |
989 | int num_dai, ret, off = 0; | |
990 | ||
30cbae66 PLB |
991 | /* Read the PDI config and initialize cadence PDI */ |
992 | intel_pdi_init(sdw, &config); | |
993 | ret = sdw_cdns_pdi_init(cdns, config); | |
994 | if (ret) | |
995 | return ret; | |
996 | ||
59e924fe | 997 | intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm); |
30cbae66 | 998 | |
c46302ec | 999 | /* DAIs are created based on total number of PDIs supported */ |
63a6aa96 | 1000 | num_dai = cdns->pcm.num_pdi; |
c46302ec | 1001 | |
7dddead7 PLB |
1002 | dai_runtime_array = devm_kcalloc(cdns->dev, num_dai, |
1003 | sizeof(struct sdw_cdns_dai_runtime *), | |
1004 | GFP_KERNEL); | |
1005 | if (!dai_runtime_array) | |
1006 | return -ENOMEM; | |
1007 | cdns->dai_runtime_array = dai_runtime_array; | |
1008 | ||
c46302ec VK |
1009 | dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); |
1010 | if (!dais) | |
1011 | return -ENOMEM; | |
1012 | ||
1013 | /* Create PCM DAIs */ | |
1014 | stream = &cdns->pcm; | |
1015 | ||
cf924962 | 1016 | ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in, |
63a6aa96 | 1017 | off, stream->num_ch_in); |
c46302ec VK |
1018 | if (ret) |
1019 | return ret; | |
1020 | ||
1021 | off += cdns->pcm.num_in; | |
1215daee | 1022 | ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out, |
63a6aa96 | 1023 | off, stream->num_ch_out); |
c46302ec VK |
1024 | if (ret) |
1025 | return ret; | |
1026 | ||
1027 | off += cdns->pcm.num_out; | |
1215daee | 1028 | ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd, |
63a6aa96 | 1029 | off, stream->num_ch_bd); |
c46302ec VK |
1030 | if (ret) |
1031 | return ret; | |
1032 | ||
54f391dd PLB |
1033 | return devm_snd_soc_register_component(cdns->dev, &dai_component, |
1034 | dais, num_dai); | |
c46302ec VK |
1035 | } |
1036 | ||
c46302ec | 1037 | |
b3ad31f3 | 1038 | const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops = { |
fb2dc6a0 PLB |
1039 | .debugfs_init = intel_debugfs_init, |
1040 | .debugfs_exit = intel_debugfs_exit, | |
1041 | ||
b6234bcc PLB |
1042 | .register_dai = intel_register_dai, |
1043 | ||
3db0c5a6 PLB |
1044 | .check_clock_stop = intel_check_clock_stop, |
1045 | .start_bus = intel_start_bus, | |
1046 | .start_bus_after_reset = intel_start_bus_after_reset, | |
1047 | .start_bus_after_clock_stop = intel_start_bus_after_clock_stop, | |
1048 | .stop_bus = intel_stop_bus, | |
1049 | ||
49c9ff45 PLB |
1050 | .link_power_up = intel_link_power_up, |
1051 | .link_power_down = intel_link_power_down, | |
1052 | ||
36e3b385 PLB |
1053 | .shim_check_wake = intel_shim_check_wake, |
1054 | .shim_wake = intel_shim_wake, | |
1055 | ||
b3ad31f3 PLB |
1056 | .pre_bank_switch = intel_pre_bank_switch, |
1057 | .post_bank_switch = intel_post_bank_switch, | |
84706e9a PLB |
1058 | |
1059 | .sync_arm = intel_shim_sync_arm, | |
1060 | .sync_go_unlocked = intel_shim_sync_go_unlocked, | |
1061 | .sync_go = intel_shim_sync_go, | |
1e76de2e | 1062 | .sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked, |
b3ad31f3 PLB |
1063 | }; |
1064 | EXPORT_SYMBOL_NS(sdw_intel_cnl_hw_ops, SOUNDWIRE_INTEL); | |
1065 |