xref: /openbmc/linux/sound/soc/sof/intel/hda-mlink.c (revision a5961bed)
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2 //
3 // This file is provided under a dual BSD/GPLv2 license.  When using or
4 // redistributing this file, you may do so under either license.
5 //
6 // Copyright(c) 2022 Intel Corporation. All rights reserved.
7 //
8 
9 /*
10  * Management of HDaudio multi-link (capabilities, power, coupling)
11  */
12 
13 #include <sound/hdaudio_ext.h>
14 #include <sound/hda_register.h>
15 #include <sound/hda-mlink.h>
16 
17 #include <linux/bitfield.h>
18 #include <linux/module.h>
19 
20 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
21 
22 /**
23  * struct hdac_ext2_link - HDAudio extended+alternate link
24  *
25  * @hext_link:		hdac_ext_link
26  * @alt:		flag set for alternate extended links
27  * @intc:		boolean for interrupt capable
28  * @ofls:		boolean for offload support
29  * @lss:		boolean for link synchronization capabilities
30  * @slcount:		sublink count
31  * @elid:		extended link ID (AZX_REG_ML_LEPTR_ID_ defines)
32  * @elver:		extended link version
33  * @leptr:		extended link pointer
34  * @eml_lock:		mutual exclusion to access shared registers e.g. CPA/SPA bits
35  * in LCTL register
36  * @base_ptr:		pointer to shim/ip/shim_vs space
37  * @instance_offset:	offset between each of @slcount instances managed by link
38  * @shim_offset:	offset to SHIM register base
39  * @ip_offset:		offset to IP register base
40  * @shim_vs_offset:	offset to vendor-specific (VS) SHIM base
41  */
42 struct hdac_ext2_link {
43 	struct hdac_ext_link hext_link;
44 
45 	/* read directly from LCAP register */
46 	bool alt;
47 	bool intc;
48 	bool ofls;
49 	bool lss;
50 	int slcount;
51 	int elid;
52 	int elver;
53 	u32 leptr;
54 
55 	struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
56 
57 	/* internal values computed from LCAP contents */
58 	void __iomem *base_ptr;
59 	u32 instance_offset;
60 	u32 shim_offset;
61 	u32 ip_offset;
62 	u32 shim_vs_offset;
63 };
64 
65 #define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
66 
67 #define AZX_REG_SDW_INSTANCE_OFFSET			0x8000
68 #define AZX_REG_SDW_SHIM_OFFSET				0x0
69 #define AZX_REG_SDW_IP_OFFSET				0x100
70 #define AZX_REG_SDW_VS_SHIM_OFFSET			0x6000
71 
72 /* only one instance supported */
73 #define AZX_REG_INTEL_DMIC_SHIM_OFFSET			0x0
74 #define AZX_REG_INTEL_DMIC_IP_OFFSET			0x100
75 #define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET		0x6000
76 
77 #define AZX_REG_INTEL_SSP_INSTANCE_OFFSET		0x1000
78 #define AZX_REG_INTEL_SSP_SHIM_OFFSET			0x0
79 #define AZX_REG_INTEL_SSP_IP_OFFSET			0x100
80 #define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET		0xC00
81 
82 /* only one instance supported */
83 #define AZX_REG_INTEL_UAOL_SHIM_OFFSET			0x0
84 #define AZX_REG_INTEL_UAOL_IP_OFFSET			0x100
85 #define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET		0xC00
86 
87 /* HDAML section - this part follows sequences in the hardware specification,
88  * including naming conventions and the use of the hdaml_ prefix.
89  * The code is intentionally minimal with limited dependencies on frameworks or
90  * helpers. Locking and scanning lists is handled at a higher level
91  */
92 
93 static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
94 			  void __iomem *ml_addr, int link_idx)
95 {
96 	struct hdac_ext_link *hlink = &h2link->hext_link;
97 	u32 base_offset;
98 
99 	hlink->lcaps  = readl(ml_addr + AZX_REG_ML_LCAP);
100 
101 	h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);
102 
103 	/* handle alternate extensions */
104 	if (!h2link->alt) {
105 		h2link->slcount = 1;
106 
107 		/*
108 		 * LSDIID is initialized by hardware for HDaudio link,
109 		 * it needs to be setup by software for alternate links
110 		 */
111 		hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);
112 
113 		dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",
114 			link_idx, hlink->lsdiid);
115 
116 		return 0;
117 	}
118 
119 	h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps);
120 	h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps);
121 	h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps);
122 
123 	/* read slcount (increment due to zero-based hardware representation */
124 	h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
125 	dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n",
126 		link_idx, h2link->slcount);
127 
128 	/* find IP ID and offsets */
129 	h2link->leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
130 
131 	h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
132 
133 	base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
134 	h2link->base_ptr = hlink->ml_addr + base_offset;
135 
136 	switch (h2link->elid) {
137 	case AZX_REG_ML_LEPTR_ID_SDW:
138 		h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET;
139 		h2link->ip_offset = AZX_REG_SDW_IP_OFFSET;
140 		h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET;
141 		dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n",
142 			link_idx, base_offset);
143 		break;
144 	case AZX_REG_ML_LEPTR_ID_INTEL_DMIC:
145 		h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET;
146 		h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET;
147 		h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET;
148 		dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n",
149 			link_idx, base_offset);
150 		break;
151 	case AZX_REG_ML_LEPTR_ID_INTEL_SSP:
152 		h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET;
153 		h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET;
154 		h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET;
155 		dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n",
156 			link_idx, base_offset);
157 		break;
158 	case AZX_REG_ML_LEPTR_ID_INTEL_UAOL:
159 		h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET;
160 		h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET;
161 		h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET;
162 		dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n",
163 			link_idx, base_offset);
164 		break;
165 	default:
166 		dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n",
167 			link_idx, h2link->elid);
168 		return -EINVAL;
169 	}
170 	return 0;
171 }
172 
173 /*
174  * Hardware recommendations are to wait ~10us before checking any hardware transition
175  * reported by bits changing status.
176  * This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
177  * The worst-case is about 1ms before reporting an issue
178  */
179 #define HDAML_POLL_DELAY_MIN_US 10
180 #define HDAML_POLL_DELAY_SLACK_US 5
181 #define HDAML_POLL_DELAY_RETRY  100
182 
183 static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
184 {
185 	int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
186 	int retry = HDAML_POLL_DELAY_RETRY;
187 	u32 val;
188 
189 	usleep_range(HDAML_POLL_DELAY_MIN_US,
190 		     HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
191 	do {
192 		val = readl(lctl);
193 		if (enabled) {
194 			if (val & mask)
195 				return 0;
196 		} else {
197 			if (!(val & mask))
198 				return 0;
199 		}
200 		usleep_range(HDAML_POLL_DELAY_MIN_US,
201 			     HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
202 
203 	} while (--retry);
204 
205 	return -EIO;
206 }
207 
208 static int hdaml_link_init(u32 __iomem *lctl, int sublink)
209 {
210 	u32 val;
211 	u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
212 
213 	val = readl(lctl);
214 	val |= mask;
215 
216 	writel(val, lctl);
217 
218 	return check_sublink_power(lctl, sublink, true);
219 }
220 
221 static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)
222 {
223 	u32 val;
224 	u32 mask;
225 
226 	val = readl(lctl);
227 	mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
228 	val &= ~mask;
229 
230 	writel(val, lctl);
231 
232 	return check_sublink_power(lctl, sublink, false);
233 }
234 
235 static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)
236 {
237 	u32 val;
238 
239 	val = readl(lctl);
240 	if (enable)
241 		val |= AZX_ML_LCTL_INTEN;
242 	else
243 		val &= ~AZX_ML_LCTL_INTEN;
244 
245 	writel(val, lctl);
246 }
247 
248 static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
249 {
250 	u32 val;
251 
252 	val = readl(lctl);
253 
254 	return val & AZX_ML_LCTL_INTSTS;
255 }
256 
257 static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
258 {
259 	int timeout = HDAML_POLL_DELAY_RETRY;
260 	u32 reg_read;
261 
262 	do {
263 		reg_read = readl(base + offset);
264 		if ((reg_read & mask) == target)
265 			return 0;
266 
267 		timeout--;
268 		usleep_range(HDAML_POLL_DELAY_MIN_US,
269 			     HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
270 	} while (timeout != 0);
271 
272 	return -EAGAIN;
273 }
274 
275 static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
276 {
277 	u32 val;
278 
279 	val = readl(lsync);
280 	val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
281 	val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
282 
283 	/*
284 	 * set SYNCPU but do not wait. The bit is cleared by hardware when
285 	 * the link becomes active.
286 	 */
287 	val |= AZX_REG_ML_LSYNC_SYNCPU;
288 
289 	writel(val, lsync);
290 }
291 
292 static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
293 {
294 	return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
295 }
296 
297 static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)
298 {
299 	u32 val;
300 
301 	val = readl(lsync);
302 	val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);
303 
304 	writel(val, lsync);
305 }
306 
307 static void hdaml_link_sync_go(u32 __iomem *lsync)
308 {
309 	u32 val;
310 
311 	val = readl(lsync);
312 	val |= AZX_REG_ML_LSYNC_SYNCGO;
313 
314 	writel(val, lsync);
315 }
316 
317 static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)
318 {
319 	u32 val;
320 
321 	val = readl(lsync);
322 
323 	return !!(val & cmdsync_mask);
324 }
325 
326 static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num)
327 {
328 	u32 val;
329 
330 	val = readl(lsdiid);
331 	val |= BIT(dev_num);
332 
333 	writel(val, lsdiid);
334 }
335 
336 static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
337 {
338 	u32 val = readl(lctl);
339 
340 	if (enable)
341 		val |=  AZX_ML_LCTL_OFLEN;
342 	else
343 		val &=  ~AZX_ML_LCTL_OFLEN;
344 
345 	writel(val, lctl);
346 }
347 
348 /* END HDAML section */
349 
350 static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
351 {
352 	struct hdac_ext2_link *h2link;
353 	struct hdac_ext_link *hlink;
354 	int ret;
355 
356 	h2link  = kzalloc(sizeof(*h2link), GFP_KERNEL);
357 	if (!h2link)
358 		return -ENOMEM;
359 
360 	/* basic initialization */
361 	hlink = &h2link->hext_link;
362 
363 	hlink->index = index;
364 	hlink->bus = bus;
365 	hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
366 
367 	ret = hdaml_lnk_enum(bus->dev, h2link, hlink->ml_addr, index);
368 	if (ret < 0) {
369 		kfree(h2link);
370 		return ret;
371 	}
372 
373 	mutex_init(&h2link->eml_lock);
374 
375 	list_add_tail(&hlink->list, &bus->hlink_list);
376 
377 	/*
378 	 * HDaudio regular links are powered-on by default, the
379 	 * refcount needs to be initialized.
380 	 */
381 	if (!h2link->alt)
382 		hlink->ref_count = 1;
383 
384 	return 0;
385 }
386 
387 int hda_bus_ml_init(struct hdac_bus *bus)
388 {
389 	u32 link_count;
390 	int ret;
391 	int i;
392 
393 	if (!bus->mlcap)
394 		return 0;
395 
396 	link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
397 
398 	dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
399 
400 	for (i = 0; i < link_count; i++) {
401 		ret = hda_ml_alloc_h2link(bus, i);
402 		if (ret < 0) {
403 			hda_bus_ml_free(bus);
404 			return ret;
405 		}
406 	}
407 	return 0;
408 }
409 EXPORT_SYMBOL_NS(hda_bus_ml_init, SND_SOC_SOF_HDA_MLINK);
410 
411 void hda_bus_ml_free(struct hdac_bus *bus)
412 {
413 	struct hdac_ext_link *hlink, *_h;
414 	struct hdac_ext2_link *h2link;
415 
416 	if (!bus->mlcap)
417 		return;
418 
419 	list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) {
420 		list_del(&hlink->list);
421 		h2link = hdac_ext_link_to_ext2(hlink);
422 
423 		mutex_destroy(&h2link->eml_lock);
424 		kfree(h2link);
425 	}
426 }
427 EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK);
428 
429 static struct hdac_ext2_link *
430 find_ext2_link(struct hdac_bus *bus, bool alt, int elid)
431 {
432 	struct hdac_ext_link *hlink;
433 
434 	list_for_each_entry(hlink, &bus->hlink_list, list) {
435 		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
436 
437 		if (h2link->alt == alt && h2link->elid == elid)
438 			return h2link;
439 	}
440 
441 	return NULL;
442 }
443 
444 int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
445 {
446 	struct hdac_ext2_link *h2link;
447 
448 	h2link = find_ext2_link(bus, alt, elid);
449 	if (!h2link)
450 		return 0;
451 
452 	return h2link->slcount;
453 }
454 EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK);
455 
456 void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)
457 {
458 	struct hdac_ext2_link *h2link;
459 	struct hdac_ext_link *hlink;
460 
461 	h2link = find_ext2_link(bus, alt, elid);
462 	if (!h2link)
463 		return;
464 
465 	if (!h2link->intc)
466 		return;
467 
468 	hlink = &h2link->hext_link;
469 
470 	mutex_lock(&h2link->eml_lock);
471 
472 	hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
473 
474 	mutex_unlock(&h2link->eml_lock);
475 }
476 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK);
477 
478 bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
479 {
480 	struct hdac_ext2_link *h2link;
481 	struct hdac_ext_link *hlink;
482 
483 	h2link = find_ext2_link(bus, alt, elid);
484 	if (!h2link)
485 		return false;
486 
487 	if (!h2link->intc)
488 		return false;
489 
490 	hlink = &h2link->hext_link;
491 
492 	return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
493 }
494 EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK);
495 
496 int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
497 {
498 	struct hdac_ext2_link *h2link;
499 	struct hdac_ext_link *hlink;
500 
501 	h2link = find_ext2_link(bus, alt, elid);
502 	if (!h2link)
503 		return 0;
504 
505 	if (!h2link->lss)
506 		return 0;
507 
508 	hlink = &h2link->hext_link;
509 
510 	hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
511 
512 	return 0;
513 }
514 EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
515 
516 int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
517 {
518 	return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
519 }
520 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
521 
522 int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
523 {
524 	struct hdac_ext2_link *h2link;
525 	struct hdac_ext_link *hlink;
526 
527 	h2link = find_ext2_link(bus, alt, elid);
528 	if (!h2link)
529 		return 0;
530 
531 	if (!h2link->lss)
532 		return 0;
533 
534 	hlink = &h2link->hext_link;
535 
536 	return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
537 }
538 EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
539 
540 int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
541 {
542 	return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
543 }
544 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
545 
546 void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
547 {
548 	struct hdac_ext2_link *h2link;
549 	struct hdac_ext_link *hlink;
550 
551 	h2link = find_ext2_link(bus, alt, elid);
552 	if (!h2link)
553 		return;
554 
555 	if (!h2link->lss)
556 		return;
557 
558 	hlink = &h2link->hext_link;
559 
560 	hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
561 }
562 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
563 
564 void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
565 {
566 	hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
567 }
568 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
569 
570 int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
571 {
572 	struct hdac_ext2_link *h2link;
573 	struct hdac_ext_link *hlink;
574 
575 	h2link = find_ext2_link(bus, alt, elid);
576 	if (!h2link)
577 		return 0;
578 
579 	if (!h2link->lss)
580 		return 0;
581 
582 	hlink = &h2link->hext_link;
583 
584 	hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
585 
586 	return 0;
587 }
588 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
589 
590 int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
591 {
592 	return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
593 }
594 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
595 
596 bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
597 {
598 	struct hdac_ext2_link *h2link;
599 	struct hdac_ext_link *hlink;
600 	u32 cmdsync_mask;
601 
602 	h2link = find_ext2_link(bus, alt, elid);
603 	if (!h2link)
604 		return 0;
605 
606 	if (!h2link->lss)
607 		return 0;
608 
609 	hlink = &h2link->hext_link;
610 
611 	cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
612 			       AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
613 
614 	return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
615 					cmdsync_mask);
616 }
617 EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
618 
619 bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
620 {
621 	return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
622 }
623 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
624 
625 static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
626 				      bool eml_lock)
627 {
628 	struct hdac_ext2_link *h2link;
629 	struct hdac_ext_link *hlink;
630 	int ret = 0;
631 
632 	h2link = find_ext2_link(bus, alt, elid);
633 	if (!h2link)
634 		return -ENODEV;
635 
636 	if (sublink >= h2link->slcount)
637 		return -EINVAL;
638 
639 	hlink = &h2link->hext_link;
640 
641 	if (eml_lock)
642 		mutex_lock(&h2link->eml_lock);
643 
644 	if (++hlink->ref_count > 1)
645 		goto skip_init;
646 
647 	ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
648 
649 skip_init:
650 	if (eml_lock)
651 		mutex_unlock(&h2link->eml_lock);
652 
653 	return ret;
654 }
655 
656 int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
657 {
658 	return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
659 }
660 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK);
661 
662 int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
663 {
664 	return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
665 }
666 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
667 
668 static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
669 					bool eml_lock)
670 {
671 	struct hdac_ext2_link *h2link;
672 	struct hdac_ext_link *hlink;
673 	int ret = 0;
674 
675 	h2link = find_ext2_link(bus, alt, elid);
676 	if (!h2link)
677 		return -ENODEV;
678 
679 	if (sublink >= h2link->slcount)
680 		return -EINVAL;
681 
682 	hlink = &h2link->hext_link;
683 
684 	if (eml_lock)
685 		mutex_lock(&h2link->eml_lock);
686 
687 	if (--hlink->ref_count > 0)
688 		goto skip_shutdown;
689 
690 	ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
691 
692 skip_shutdown:
693 	if (eml_lock)
694 		mutex_unlock(&h2link->eml_lock);
695 
696 	return ret;
697 }
698 
699 int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
700 {
701 	return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
702 }
703 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK);
704 
705 int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
706 {
707 	return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
708 }
709 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
710 
711 int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
712 {
713 	return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
714 }
715 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
716 
717 int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
718 {
719 	return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
720 }
721 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
722 
723 int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
724 {
725 	struct hdac_ext2_link *h2link;
726 	struct hdac_ext_link *hlink;
727 
728 	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
729 	if (!h2link)
730 		return -ENODEV;
731 
732 	hlink = &h2link->hext_link;
733 
734 	mutex_lock(&h2link->eml_lock);
735 
736 	hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
737 
738 	mutex_unlock(&h2link->eml_lock);
739 
740 	return 0;
741 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK);
742 
743 void hda_bus_ml_put_all(struct hdac_bus *bus)
744 {
745 	struct hdac_ext_link *hlink;
746 
747 	list_for_each_entry(hlink, &bus->hlink_list, list) {
748 		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
749 
750 		if (!h2link->alt)
751 			snd_hdac_ext_bus_link_put(bus, hlink);
752 	}
753 }
754 EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK);
755 
756 void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
757 {
758 	struct hdac_ext_link *hlink;
759 
760 	/* Reset stream-to-link mapping */
761 	list_for_each_entry(hlink, &bus->hlink_list, list)
762 		writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
763 }
764 EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK);
765 
766 int hda_bus_ml_resume(struct hdac_bus *bus)
767 {
768 	struct hdac_ext_link *hlink;
769 	int ret;
770 
771 	/* power up links that were active before suspend */
772 	list_for_each_entry(hlink, &bus->hlink_list, list) {
773 		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
774 
775 		if (!h2link->alt && hlink->ref_count) {
776 			ret = snd_hdac_ext_bus_link_power_up(hlink);
777 			if (ret < 0)
778 				return ret;
779 		}
780 	}
781 	return 0;
782 }
783 EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK);
784 
785 int hda_bus_ml_suspend(struct hdac_bus *bus)
786 {
787 	struct hdac_ext_link *hlink;
788 	int ret;
789 
790 	list_for_each_entry(hlink, &bus->hlink_list, list) {
791 		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
792 
793 		if (!h2link->alt) {
794 			ret = snd_hdac_ext_bus_link_power_down(hlink);
795 			if (ret < 0)
796 				return ret;
797 		}
798 	}
799 	return 0;
800 }
801 EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);
802 
803 struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
804 {
805 	struct hdac_ext2_link *h2link;
806 
807 	h2link = find_ext2_link(bus, alt, elid);
808 	if (!h2link)
809 		return NULL;
810 
811 	return &h2link->eml_lock;
812 }
813 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK);
814 
815 struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
816 {
817 	struct hdac_ext2_link *h2link;
818 
819 	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
820 	if (!h2link)
821 		return NULL;
822 
823 	return &h2link->hext_link;
824 }
825 EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK);
826 
827 struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
828 {
829 	struct hdac_ext2_link *h2link;
830 
831 	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
832 	if (!h2link)
833 		return NULL;
834 
835 	return &h2link->hext_link;
836 }
837 EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK);
838 
839 int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
840 {
841 	struct hdac_ext2_link *h2link;
842 	struct hdac_ext_link *hlink;
843 
844 	h2link = find_ext2_link(bus, alt, elid);
845 	if (!h2link)
846 		return -ENODEV;
847 
848 	if (!h2link->ofls)
849 		return 0;
850 
851 	hlink = &h2link->hext_link;
852 
853 	mutex_lock(&h2link->eml_lock);
854 
855 	hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
856 
857 	mutex_unlock(&h2link->eml_lock);
858 
859 	return 0;
860 }
861 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK);
862 
863 #endif
864 
865 MODULE_LICENSE("Dual BSD/GPL");
866