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> |
9 | #include <linux/debugfs.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/io.h> |
12 | #include <sound/pcm_params.h> |
13 | #include <linux/pm_runtime.h> |
14 | #include <sound/soc.h> |
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" |
19 | #include "bus.h" |
20 | #include "intel.h" |
21 | |
22 | static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target) |
23 | { |
24 | int timeout = 10; |
25 | u32 reg_read; |
26 | |
27 | do { |
28 | reg_read = readl(addr: base + offset); |
29 | if ((reg_read & mask) == target) |
30 | return 0; |
31 | |
32 | timeout--; |
33 | usleep_range(min: 50, max: 100); |
34 | } while (timeout != 0); |
35 | |
36 | return -EAGAIN; |
37 | } |
38 | |
39 | static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask) |
40 | { |
41 | writel(val: value, addr: base + offset); |
42 | return intel_wait_bit(base, offset, mask, target: 0); |
43 | } |
44 | |
45 | static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) |
46 | { |
47 | writel(val: value, addr: base + offset); |
48 | return intel_wait_bit(base, offset, mask, target: mask); |
49 | } |
50 | |
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(base: mem, offset: reg); |
65 | else |
66 | value = intel_readw(base: mem, offset: reg); |
67 | |
68 | return scnprintf(buf: buf + pos, RD_BUF - pos, fmt: "%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; |
74 | void __iomem *s = sdw->link_res->shim; |
75 | void __iomem *a = sdw->link_res->alh; |
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 | |
85 | links = intel_readl(base: s, SDW_SHIM_LCAP) & SDW_SHIM_LCAP_LCOUNT_MASK; |
86 | |
87 | ret = scnprintf(buf, RD_BUF, fmt: "Register Value\n" ); |
88 | ret += scnprintf(buf: buf + ret, RD_BUF - ret, fmt: "\nShim\n" ); |
89 | |
90 | for (i = 0; i < links; i++) { |
91 | reg = SDW_SHIM_LCAP + i * 4; |
92 | ret += intel_sprintf(mem: s, l: true, buf, pos: ret, reg); |
93 | } |
94 | |
95 | for (i = 0; i < links; i++) { |
96 | ret += scnprintf(buf: buf + ret, RD_BUF - ret, fmt: "\nLink%d\n" , i); |
97 | ret += intel_sprintf(mem: s, l: false, buf, pos: ret, SDW_SHIM_CTLSCAP(i)); |
98 | ret += intel_sprintf(mem: s, l: false, buf, pos: ret, SDW_SHIM_CTLS0CM(i)); |
99 | ret += intel_sprintf(mem: s, l: false, buf, pos: ret, SDW_SHIM_CTLS1CM(i)); |
100 | ret += intel_sprintf(mem: s, l: false, buf, pos: ret, SDW_SHIM_CTLS2CM(i)); |
101 | ret += intel_sprintf(mem: s, l: false, buf, pos: ret, SDW_SHIM_CTLS3CM(i)); |
102 | ret += intel_sprintf(mem: s, l: false, buf, pos: ret, SDW_SHIM_PCMSCAP(i)); |
103 | |
104 | ret += scnprintf(buf: buf + ret, RD_BUF - ret, fmt: "\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(mem: s, l: false, buf, pos: ret, |
113 | SDW_SHIM_PCMSYCHM(i, j)); |
114 | ret += intel_sprintf(mem: s, l: false, buf, pos: ret, |
115 | SDW_SHIM_PCMSYCHC(i, j)); |
116 | } |
117 | ret += scnprintf(buf: buf + ret, RD_BUF - ret, fmt: "\n IOCTL, CTMCTL\n" ); |
118 | |
119 | ret += intel_sprintf(mem: s, l: false, buf, pos: ret, SDW_SHIM_IOCTL(i)); |
120 | ret += intel_sprintf(mem: s, l: false, buf, pos: ret, SDW_SHIM_CTMCTL(i)); |
121 | } |
122 | |
123 | ret += scnprintf(buf: buf + ret, RD_BUF - ret, fmt: "\nWake registers\n" ); |
124 | ret += intel_sprintf(mem: s, l: false, buf, pos: ret, SDW_SHIM_WAKEEN); |
125 | ret += intel_sprintf(mem: s, l: false, buf, pos: ret, SDW_SHIM_WAKESTS); |
126 | |
127 | ret += scnprintf(buf: buf + ret, RD_BUF - ret, fmt: "\nALH STRMzCFG\n" ); |
128 | for (i = 0; i < SDW_ALH_NUM_STREAMS; i++) |
129 | ret += intel_sprintf(mem: a, l: true, buf, pos: ret, SDW_ALH_STRMZCFG(i)); |
130 | |
131 | seq_printf(m: s_file, fmt: "%s" , buf); |
132 | kfree(objp: buf); |
133 | |
134 | return 0; |
135 | } |
136 | DEFINE_SHOW_ATTRIBUTE(intel_reg); |
137 | |
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 | |
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(name: "intel-sdw" , parent: root); |
182 | |
183 | debugfs_create_file(name: "intel-registers" , mode: 0400, parent: sdw->debugfs, data: sdw, |
184 | fops: &intel_reg_fops); |
185 | |
186 | debugfs_create_file(name: "intel-m-datamode" , mode: 0200, parent: sdw->debugfs, data: sdw, |
187 | fops: &intel_set_m_datamode_fops); |
188 | |
189 | debugfs_create_file(name: "intel-s-datamode" , mode: 0200, parent: sdw->debugfs, data: sdw, |
190 | fops: &intel_set_s_datamode_fops); |
191 | |
192 | sdw_cdns_debugfs_init(cdns: &sdw->cdns, root: sdw->debugfs); |
193 | } |
194 | |
195 | static void intel_debugfs_exit(struct sdw_intel *sdw) |
196 | { |
197 | debugfs_remove_recursive(dentry: 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 | |
204 | /* |
205 | * shim ops |
206 | */ |
207 | /* this needs to be called with shim_lock */ |
208 | static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw) |
209 | { |
210 | void __iomem *shim = sdw->link_res->shim; |
211 | unsigned int link_id = sdw->instance; |
212 | u16 ioctl; |
213 | |
214 | /* Switch to MIP from Glue logic */ |
215 | ioctl = intel_readw(base: shim, SDW_SHIM_IOCTL(link_id)); |
216 | |
217 | ioctl &= ~(SDW_SHIM_IOCTL_DOE); |
218 | intel_writew(base: shim, SDW_SHIM_IOCTL(link_id), value: ioctl); |
219 | usleep_range(min: 10, max: 15); |
220 | |
221 | ioctl &= ~(SDW_SHIM_IOCTL_DO); |
222 | intel_writew(base: shim, SDW_SHIM_IOCTL(link_id), value: ioctl); |
223 | usleep_range(min: 10, max: 15); |
224 | |
225 | ioctl |= (SDW_SHIM_IOCTL_MIF); |
226 | intel_writew(base: shim, SDW_SHIM_IOCTL(link_id), value: ioctl); |
227 | usleep_range(min: 10, max: 15); |
228 | |
229 | ioctl &= ~(SDW_SHIM_IOCTL_BKE); |
230 | ioctl &= ~(SDW_SHIM_IOCTL_COE); |
231 | intel_writew(base: shim, SDW_SHIM_IOCTL(link_id), value: ioctl); |
232 | usleep_range(min: 10, max: 15); |
233 | |
234 | /* at this point Master IP has full control of the I/Os */ |
235 | } |
236 | |
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(base: shim, SDW_SHIM_IOCTL(link_id)); |
246 | ioctl |= SDW_SHIM_IOCTL_BKE; |
247 | ioctl |= SDW_SHIM_IOCTL_COE; |
248 | intel_writew(base: shim, SDW_SHIM_IOCTL(link_id), value: ioctl); |
249 | usleep_range(min: 10, max: 15); |
250 | |
251 | ioctl &= ~(SDW_SHIM_IOCTL_MIF); |
252 | intel_writew(base: shim, SDW_SHIM_IOCTL(link_id), value: ioctl); |
253 | usleep_range(min: 10, max: 15); |
254 | |
255 | /* at this point Integration Glue has full control of the I/Os */ |
256 | } |
257 | |
258 | /* this needs to be called with shim_lock */ |
259 | static void intel_shim_init(struct sdw_intel *sdw) |
260 | { |
261 | void __iomem *shim = sdw->link_res->shim; |
262 | unsigned int link_id = sdw->instance; |
263 | u16 ioctl = 0, act; |
264 | |
265 | /* Initialize Shim */ |
266 | ioctl |= SDW_SHIM_IOCTL_BKE; |
267 | intel_writew(base: shim, SDW_SHIM_IOCTL(link_id), value: ioctl); |
268 | usleep_range(min: 10, max: 15); |
269 | |
270 | ioctl |= SDW_SHIM_IOCTL_WPDD; |
271 | intel_writew(base: shim, SDW_SHIM_IOCTL(link_id), value: ioctl); |
272 | usleep_range(min: 10, max: 15); |
273 | |
274 | ioctl |= SDW_SHIM_IOCTL_DO; |
275 | intel_writew(base: shim, SDW_SHIM_IOCTL(link_id), value: ioctl); |
276 | usleep_range(min: 10, max: 15); |
277 | |
278 | ioctl |= SDW_SHIM_IOCTL_DOE; |
279 | intel_writew(base: shim, SDW_SHIM_IOCTL(link_id), value: ioctl); |
280 | usleep_range(min: 10, max: 15); |
281 | |
282 | intel_shim_glue_to_master_ip(sdw); |
283 | |
284 | act = intel_readw(base: shim, SDW_SHIM_CTMCTL(link_id)); |
285 | u16p_replace_bits(p: &act, val: 0x1, SDW_SHIM_CTMCTL_DOAIS); |
286 | act |= SDW_SHIM_CTMCTL_DACTQE; |
287 | act |= SDW_SHIM_CTMCTL_DODS; |
288 | intel_writew(base: shim, SDW_SHIM_CTMCTL(link_id), value: act); |
289 | usleep_range(min: 10, max: 15); |
290 | } |
291 | |
292 | static int intel_shim_check_wake(struct sdw_intel *sdw) |
293 | { |
294 | void __iomem *shim; |
295 | u16 wake_sts; |
296 | |
297 | shim = sdw->link_res->shim; |
298 | wake_sts = intel_readw(base: shim, SDW_SHIM_WAKESTS); |
299 | |
300 | return wake_sts & BIT(sdw->instance); |
301 | } |
302 | |
303 | static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) |
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(base: 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(base: shim, SDW_SHIM_WAKEEN, value: wake_en); |
316 | } else { |
317 | /* Disable the wake up interrupt */ |
318 | wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); |
319 | intel_writew(base: shim, SDW_SHIM_WAKEEN, value: wake_en); |
320 | |
321 | /* Clear wake status */ |
322 | wake_sts = intel_readw(base: shim, SDW_SHIM_WAKESTS); |
323 | wake_sts |= (SDW_SHIM_WAKESTS_STATUS << link_id); |
324 | intel_writew(base: shim, SDW_SHIM_WAKESTS, value: wake_sts); |
325 | } |
326 | mutex_unlock(lock: sdw->link_res->shim_lock); |
327 | } |
328 | |
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(base: shim, SDW_SHIM_SYNC); |
335 | return !!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK); |
336 | } |
337 | |
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(base: shim, SDW_SHIM_SYNC); |
377 | u32p_replace_bits(p: &sync_reg, val: syncprd, SDW_SHIM_SYNC_SYNCPRD); |
378 | |
379 | /* Set SyncCPU bit */ |
380 | sync_reg |= SDW_SHIM_SYNC_SYNCCPU; |
381 | intel_writel(base: shim, SDW_SHIM_SYNC, value: sync_reg); |
382 | |
383 | /* Link power up sequence */ |
384 | link_control = intel_readl(base: 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(base: shim, SDW_SHIM_LCTL, value: link_control, mask: 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(base: shim, SDW_SHIM_SYNC, |
400 | SDW_SHIM_SYNC_SYNCCPU, target: 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; |
411 | |
412 | intel_shim_init(sdw); |
413 | |
414 | out: |
415 | mutex_unlock(lock: sdw->link_res->shim_lock); |
416 | |
417 | return ret; |
418 | } |
419 | |
420 | static int intel_link_power_down(struct sdw_intel *sdw) |
421 | { |
422 | u32 link_control, spa_mask, cpa_mask; |
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 | |
430 | if (!(*shim_mask & BIT(link_id))) |
431 | dev_err(sdw->cdns.dev, |
432 | "%s: Unbalanced power-up/down calls\n" , __func__); |
433 | |
434 | sdw->cdns.link_up = false; |
435 | |
436 | intel_shim_master_ip_to_glue(sdw); |
437 | |
438 | *shim_mask &= ~BIT(link_id); |
439 | |
440 | if (!*shim_mask) { |
441 | |
442 | dev_dbg(sdw->cdns.dev, "powering down all links\n" ); |
443 | |
444 | /* Link power down sequence */ |
445 | link_control = intel_readl(base: shim, SDW_SHIM_LCTL); |
446 | |
447 | /* only power-down enabled links */ |
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); |
450 | |
451 | link_control &= spa_mask; |
452 | |
453 | ret = intel_clear_bit(base: shim, SDW_SHIM_LCTL, value: link_control, mask: cpa_mask); |
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 | } |
462 | } |
463 | |
464 | mutex_unlock(lock: sdw->link_res->shim_lock); |
465 | |
466 | return ret; |
467 | } |
468 | |
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(base: shim, SDW_SHIM_SYNC); |
478 | sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance); |
479 | intel_writel(base: shim, SDW_SHIM_SYNC, value: sync_reg); |
480 | |
481 | mutex_unlock(lock: sdw->link_res->shim_lock); |
482 | } |
483 | |
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; |
488 | |
489 | /* Read SYNC register */ |
490 | sync_reg = intel_readl(base: shim, SDW_SHIM_SYNC); |
491 | |
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 | |
499 | intel_writel(base: shim, SDW_SHIM_SYNC, value: sync_reg); |
500 | |
501 | return 0; |
502 | } |
503 | |
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(lock: sdw->link_res->shim_lock); |
513 | |
514 | return ret; |
515 | } |
516 | |
517 | /* |
518 | * PDI routines |
519 | */ |
520 | static void intel_pdi_init(struct sdw_intel *sdw, |
521 | struct sdw_cdns_stream_config *config) |
522 | { |
523 | void __iomem *shim = sdw->link_res->shim; |
524 | unsigned int link_id = sdw->instance; |
525 | int pcm_cap; |
526 | |
527 | /* PCM Stream Capability */ |
528 | pcm_cap = intel_readw(base: shim, SDW_SHIM_PCMSCAP(link_id)); |
529 | |
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); |
533 | |
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); |
536 | } |
537 | |
538 | static int |
539 | intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num) |
540 | { |
541 | void __iomem *shim = sdw->link_res->shim; |
542 | unsigned int link_id = sdw->instance; |
543 | int count; |
544 | |
545 | count = intel_readw(base: shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); |
546 | |
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; |
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, |
563 | struct sdw_cdns_pdi *pdi, |
564 | unsigned int num_pdi, |
565 | unsigned int *num_ch) |
566 | { |
567 | int i, ch_count = 0; |
568 | |
569 | for (i = 0; i < num_pdi; i++) { |
570 | pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi_num: pdi->num); |
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, |
580 | struct sdw_cdns_streams *stream) |
581 | { |
582 | intel_pdi_get_ch_update(sdw, pdi: stream->bd, num_pdi: stream->num_bd, |
583 | num_ch: &stream->num_ch_bd); |
584 | |
585 | intel_pdi_get_ch_update(sdw, pdi: stream->in, num_pdi: stream->num_in, |
586 | num_ch: &stream->num_ch_in); |
587 | |
588 | intel_pdi_get_ch_update(sdw, pdi: stream->out, num_pdi: stream->num_out, |
589 | num_ch: &stream->num_ch_out); |
590 | |
591 | return 0; |
592 | } |
593 | |
594 | static void |
595 | intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) |
596 | { |
597 | void __iomem *shim = sdw->link_res->shim; |
598 | unsigned int link_id = sdw->instance; |
599 | int pdi_conf = 0; |
600 | |
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; |
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 | |
618 | u32p_replace_bits(p: &pdi_conf, val: pdi->intel_alh_id, SDW_SHIM_PCMSYCM_STREAM); |
619 | u32p_replace_bits(p: &pdi_conf, val: pdi->l_ch_num, SDW_SHIM_PCMSYCM_LCHN); |
620 | u32p_replace_bits(p: &pdi_conf, val: pdi->h_ch_num, SDW_SHIM_PCMSYCM_HCHN); |
621 | |
622 | intel_writew(base: shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), value: pdi_conf); |
623 | } |
624 | |
625 | static void |
626 | intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) |
627 | { |
628 | void __iomem *alh = sdw->link_res->alh; |
629 | unsigned int link_id = sdw->instance; |
630 | unsigned int conf; |
631 | |
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; |
636 | |
637 | /* Program Stream config ALH register */ |
638 | conf = intel_readl(base: alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id)); |
639 | |
640 | u32p_replace_bits(p: &conf, SDW_ALH_STRMZCFG_DMAT_VAL, SDW_ALH_STRMZCFG_DMAT); |
641 | u32p_replace_bits(p: &conf, val: pdi->ch_count - 1, SDW_ALH_STRMZCFG_CHN); |
642 | |
643 | intel_writel(base: alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), value: conf); |
644 | } |
645 | |
646 | static int intel_params_stream(struct sdw_intel *sdw, |
647 | struct snd_pcm_substream *substream, |
648 | struct snd_soc_dai *dai, |
649 | struct snd_pcm_hw_params *hw_params, |
650 | int link_id, int alh_stream_id) |
651 | { |
652 | struct sdw_intel_link_res *res = sdw->link_res; |
653 | struct sdw_intel_stream_params_data params_data; |
654 | |
655 | params_data.substream = substream; |
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; |
660 | |
661 | if (res->ops && res->ops->params_stream && res->dev) |
662 | return res->ops->params_stream(res->dev, |
663 | ¶ms_data); |
664 | return -EIO; |
665 | } |
666 | |
667 | /* |
668 | * DAI routines |
669 | */ |
670 | |
671 | static int intel_hw_params(struct snd_pcm_substream *substream, |
672 | struct snd_pcm_hw_params *params, |
673 | struct snd_soc_dai *dai) |
674 | { |
675 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); |
676 | struct sdw_intel *sdw = cdns_to_intel(cdns); |
677 | struct sdw_cdns_dai_runtime *dai_runtime; |
678 | struct sdw_cdns_pdi *pdi; |
679 | struct sdw_stream_config sconfig; |
680 | struct sdw_port_config *pconfig; |
681 | int ch, dir; |
682 | int ret; |
683 | |
684 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
685 | if (!dai_runtime) |
686 | return -EIO; |
687 | |
688 | ch = params_channels(p: params); |
689 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) |
690 | dir = SDW_DATA_DIR_RX; |
691 | else |
692 | dir = SDW_DATA_DIR_TX; |
693 | |
694 | pdi = sdw_cdns_alloc_pdi(cdns, stream: &cdns->pcm, ch, dir, dai_id: dai->id); |
695 | |
696 | if (!pdi) { |
697 | ret = -EINVAL; |
698 | goto error; |
699 | } |
700 | |
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); |
705 | |
706 | /* store pdi and hw_params, may be needed in prepare step */ |
707 | dai_runtime->paused = false; |
708 | dai_runtime->suspended = false; |
709 | dai_runtime->pdi = pdi; |
710 | |
711 | /* Inform DSP about PDI stream number */ |
712 | ret = intel_params_stream(sdw, substream, dai, hw_params: params, |
713 | link_id: sdw->instance, |
714 | alh_stream_id: pdi->intel_alh_id); |
715 | if (ret) |
716 | goto error; |
717 | |
718 | sconfig.direction = dir; |
719 | sconfig.ch_count = ch; |
720 | sconfig.frame_rate = params_rate(p: params); |
721 | sconfig.type = dai_runtime->stream_type; |
722 | |
723 | sconfig.bps = snd_pcm_format_width(format: params_format(p: params)); |
724 | |
725 | /* Port configuration */ |
726 | pconfig = kzalloc(size: sizeof(*pconfig), GFP_KERNEL); |
727 | if (!pconfig) { |
728 | ret = -ENOMEM; |
729 | goto error; |
730 | } |
731 | |
732 | pconfig->num = pdi->num; |
733 | pconfig->ch_mask = (1 << ch) - 1; |
734 | |
735 | ret = sdw_stream_add_master(bus: &cdns->bus, stream_config: &sconfig, |
736 | port_config: pconfig, num_ports: 1, stream: dai_runtime->stream); |
737 | if (ret) |
738 | dev_err(cdns->dev, "add master to stream failed:%d\n" , ret); |
739 | |
740 | kfree(objp: pconfig); |
741 | error: |
742 | return ret; |
743 | } |
744 | |
745 | static int intel_prepare(struct snd_pcm_substream *substream, |
746 | struct snd_soc_dai *dai) |
747 | { |
748 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); |
749 | struct sdw_intel *sdw = cdns_to_intel(cdns); |
750 | struct sdw_cdns_dai_runtime *dai_runtime; |
751 | int ch, dir; |
752 | int ret = 0; |
753 | |
754 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
755 | if (!dai_runtime) { |
756 | dev_err(dai->dev, "failed to get dai runtime in %s\n" , |
757 | __func__); |
758 | return -EIO; |
759 | } |
760 | |
761 | if (dai_runtime->suspended) { |
762 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
763 | struct snd_pcm_hw_params *hw_params; |
764 | |
765 | hw_params = &rtd->dpcm[substream->stream].hw_params; |
766 | |
767 | dai_runtime->suspended = false; |
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 */ |
778 | ch = params_channels(p: hw_params); |
779 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) |
780 | dir = SDW_DATA_DIR_RX; |
781 | else |
782 | dir = SDW_DATA_DIR_TX; |
783 | |
784 | intel_pdi_shim_configure(sdw, pdi: dai_runtime->pdi); |
785 | intel_pdi_alh_configure(sdw, pdi: dai_runtime->pdi); |
786 | sdw_cdns_config_stream(cdns, ch, dir, pdi: dai_runtime->pdi); |
787 | |
788 | /* Inform DSP about PDI stream number */ |
789 | ret = intel_params_stream(sdw, substream, dai, |
790 | hw_params, |
791 | link_id: sdw->instance, |
792 | alh_stream_id: dai_runtime->pdi->intel_alh_id); |
793 | } |
794 | |
795 | return ret; |
796 | } |
797 | |
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); |
802 | struct sdw_cdns_dai_runtime *dai_runtime; |
803 | int ret; |
804 | |
805 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
806 | if (!dai_runtime) |
807 | return -EIO; |
808 | |
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 | */ |
815 | ret = sdw_stream_remove_master(bus: &cdns->bus, stream: dai_runtime->stream); |
816 | if (ret < 0) { |
817 | dev_err(dai->dev, "remove master from stream %s failed: %d\n" , |
818 | dai_runtime->stream->name, ret); |
819 | return ret; |
820 | } |
821 | |
822 | dai_runtime->pdi = NULL; |
823 | |
824 | return 0; |
825 | } |
826 | |
827 | static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, |
828 | void *stream, int direction) |
829 | { |
830 | return cdns_set_sdw_stream(dai, stream, direction); |
831 | } |
832 | |
833 | static void *intel_get_sdw_stream(struct snd_soc_dai *dai, |
834 | int direction) |
835 | { |
836 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); |
837 | struct sdw_cdns_dai_runtime *dai_runtime; |
838 | |
839 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
840 | if (!dai_runtime) |
841 | return ERR_PTR(error: -EINVAL); |
842 | |
843 | return dai_runtime->stream; |
844 | } |
845 | |
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); |
849 | struct sdw_cdns_dai_runtime *dai_runtime; |
850 | int ret = 0; |
851 | |
852 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
853 | if (!dai_runtime) { |
854 | dev_err(dai->dev, "failed to get dai runtime in %s\n" , |
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 | |
869 | dai_runtime->suspended = true; |
870 | |
871 | break; |
872 | |
873 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
874 | dai_runtime->paused = true; |
875 | break; |
876 | case SNDRV_PCM_TRIGGER_STOP: |
877 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
878 | dai_runtime->paused = false; |
879 | break; |
880 | default: |
881 | break; |
882 | } |
883 | |
884 | return ret; |
885 | } |
886 | |
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(dev: component->dev); |
898 | if (ret < 0 && ret != -EACCES) |
899 | return ret; |
900 | |
901 | return 0; |
902 | } |
903 | |
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); |
916 | struct sdw_cdns_dai_runtime *dai_runtime; |
917 | |
918 | dai_runtime = cdns->dai_runtime_array[dai->id]; |
919 | |
920 | if (!dai_runtime) |
921 | continue; |
922 | |
923 | if (dai_runtime->suspended) |
924 | continue; |
925 | |
926 | if (dai_runtime->paused) |
927 | dai_runtime->suspended = true; |
928 | } |
929 | |
930 | return 0; |
931 | } |
932 | |
933 | static const struct snd_soc_dai_ops intel_pcm_dai_ops = { |
934 | .hw_params = intel_hw_params, |
935 | .prepare = intel_prepare, |
936 | .hw_free = intel_hw_free, |
937 | .trigger = intel_trigger, |
938 | .set_stream = intel_pcm_set_sdw_stream, |
939 | .get_stream = intel_get_sdw_stream, |
940 | }; |
941 | |
942 | static const struct snd_soc_component_driver dai_component = { |
943 | .name = "soundwire" , |
944 | .probe = intel_component_probe, |
945 | .suspend = intel_component_dais_suspend, |
946 | .legacy_dai_naming = 1, |
947 | }; |
948 | |
949 | static int intel_create_dai(struct sdw_cdns *cdns, |
950 | struct snd_soc_dai_driver *dais, |
951 | enum intel_pdi_type type, |
952 | u32 num, u32 off, u32 max_ch) |
953 | { |
954 | int i; |
955 | |
956 | if (num == 0) |
957 | return 0; |
958 | |
959 | for (i = off; i < (off + num); i++) { |
960 | dais[i].name = devm_kasprintf(dev: cdns->dev, GFP_KERNEL, |
961 | fmt: "SDW%d Pin%d" , |
962 | cdns->instance, i); |
963 | if (!dais[i].name) |
964 | return -ENOMEM; |
965 | |
966 | if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { |
967 | dais[i].playback.channels_min = 1; |
968 | dais[i].playback.channels_max = max_ch; |
969 | } |
970 | |
971 | if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { |
972 | dais[i].capture.channels_min = 1; |
973 | dais[i].capture.channels_max = max_ch; |
974 | } |
975 | |
976 | dais[i].ops = &intel_pcm_dai_ops; |
977 | } |
978 | |
979 | return 0; |
980 | } |
981 | |
982 | static int intel_register_dai(struct sdw_intel *sdw) |
983 | { |
984 | struct sdw_cdns_dai_runtime **dai_runtime_array; |
985 | struct sdw_cdns_stream_config config; |
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 | |
991 | /* Read the PDI config and initialize cadence PDI */ |
992 | intel_pdi_init(sdw, config: &config); |
993 | ret = sdw_cdns_pdi_init(cdns, config); |
994 | if (ret) |
995 | return ret; |
996 | |
997 | intel_pdi_stream_ch_update(sdw, stream: &sdw->cdns.pcm); |
998 | |
999 | /* DAIs are created based on total number of PDIs supported */ |
1000 | num_dai = cdns->pcm.num_pdi; |
1001 | |
1002 | dai_runtime_array = devm_kcalloc(dev: cdns->dev, n: num_dai, |
1003 | size: 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 | |
1009 | dais = devm_kcalloc(dev: cdns->dev, n: num_dai, size: sizeof(*dais), GFP_KERNEL); |
1010 | if (!dais) |
1011 | return -ENOMEM; |
1012 | |
1013 | /* Create PCM DAIs */ |
1014 | stream = &cdns->pcm; |
1015 | |
1016 | ret = intel_create_dai(cdns, dais, type: INTEL_PDI_IN, num: cdns->pcm.num_in, |
1017 | off, max_ch: stream->num_ch_in); |
1018 | if (ret) |
1019 | return ret; |
1020 | |
1021 | off += cdns->pcm.num_in; |
1022 | ret = intel_create_dai(cdns, dais, type: INTEL_PDI_OUT, num: cdns->pcm.num_out, |
1023 | off, max_ch: stream->num_ch_out); |
1024 | if (ret) |
1025 | return ret; |
1026 | |
1027 | off += cdns->pcm.num_out; |
1028 | ret = intel_create_dai(cdns, dais, type: INTEL_PDI_BD, num: cdns->pcm.num_bd, |
1029 | off, max_ch: stream->num_ch_bd); |
1030 | if (ret) |
1031 | return ret; |
1032 | |
1033 | return devm_snd_soc_register_component(dev: cdns->dev, component_driver: &dai_component, |
1034 | dai_drv: dais, num_dai); |
1035 | } |
1036 | |
1037 | |
1038 | const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops = { |
1039 | .debugfs_init = intel_debugfs_init, |
1040 | .debugfs_exit = intel_debugfs_exit, |
1041 | |
1042 | .register_dai = intel_register_dai, |
1043 | |
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 | |
1050 | .link_power_up = intel_link_power_up, |
1051 | .link_power_down = intel_link_power_down, |
1052 | |
1053 | .shim_check_wake = intel_shim_check_wake, |
1054 | .shim_wake = intel_shim_wake, |
1055 | |
1056 | .pre_bank_switch = intel_pre_bank_switch, |
1057 | .post_bank_switch = intel_post_bank_switch, |
1058 | |
1059 | .sync_arm = intel_shim_sync_arm, |
1060 | .sync_go_unlocked = intel_shim_sync_go_unlocked, |
1061 | .sync_go = intel_shim_sync_go, |
1062 | .sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked, |
1063 | }; |
1064 | EXPORT_SYMBOL_NS(sdw_intel_cnl_hw_ops, SOUNDWIRE_INTEL); |
1065 | |
1066 | |