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