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