1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
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.
6 // Copyright(c) 2022 Intel Corporation. All rights reserved.
10 * Management of HDaudio multi-link (capabilities, power, coupling)
13 #include <sound/hdaudio_ext.h>
14 #include <sound/hda_register.h>
15 #include <sound/hda-mlink.h>
17 #include <linux/bitfield.h>
18 #include <linux/module.h>
20 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
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)
26 * struct hdac_ext2_link - HDAudio extended+alternate link
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
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
46 struct hdac_ext2_link {
47 struct hdac_ext_link hext_link;
49 /* read directly from LCAP register */
59 struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
60 int sublink_ref_count[HDAML_MAX_SUBLINKS];
62 /* internal values computed from LCAP contents */
63 void __iomem *base_ptr;
70 #define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
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
77 /* only one instance supported */
78 #define AZX_REG_INTEL_DMIC_SHIM_OFFSET 0x0
79 #define AZX_REG_INTEL_DMIC_IP_OFFSET 0x100
80 #define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET 0x6000
82 #define AZX_REG_INTEL_SSP_INSTANCE_OFFSET 0x1000
83 #define AZX_REG_INTEL_SSP_SHIM_OFFSET 0x0
84 #define AZX_REG_INTEL_SSP_IP_OFFSET 0x100
85 #define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET 0xC00
87 /* only one instance supported */
88 #define AZX_REG_INTEL_UAOL_SHIM_OFFSET 0x0
89 #define AZX_REG_INTEL_UAOL_IP_OFFSET 0x100
90 #define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET 0xC00
92 /* HDAML section - this part follows sequences in the hardware specification,
93 * including naming conventions and the use of the hdaml_ prefix.
94 * The code is intentionally minimal with limited dependencies on frameworks or
95 * helpers. Locking and scanning lists is handled at a higher level
98 static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
99 void __iomem *ml_addr, int link_idx)
101 struct hdac_ext_link *hlink = &h2link->hext_link;
104 hlink->lcaps = readl(ml_addr + AZX_REG_ML_LCAP);
106 h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);
108 /* handle alternate extensions */
113 * LSDIID is initialized by hardware for HDaudio link,
114 * it needs to be setup by software for alternate links
116 hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);
118 dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",
119 link_idx, hlink->lsdiid);
124 h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps);
125 h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps);
126 h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps);
128 /* read slcount (increment due to zero-based hardware representation */
129 h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
130 dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n",
131 link_idx, h2link->slcount);
133 /* find IP ID and offsets */
134 h2link->leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
136 h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
138 base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
139 h2link->base_ptr = hlink->ml_addr + base_offset;
141 switch (h2link->elid) {
142 case AZX_REG_ML_LEPTR_ID_SDW:
143 h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET;
144 h2link->ip_offset = AZX_REG_SDW_IP_OFFSET;
145 h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET;
146 dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n",
147 link_idx, base_offset);
149 case AZX_REG_ML_LEPTR_ID_INTEL_DMIC:
150 h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET;
151 h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET;
152 h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET;
153 dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n",
154 link_idx, base_offset);
156 case AZX_REG_ML_LEPTR_ID_INTEL_SSP:
157 h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET;
158 h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET;
159 h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET;
160 dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n",
161 link_idx, base_offset);
163 case AZX_REG_ML_LEPTR_ID_INTEL_UAOL:
164 h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET;
165 h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET;
166 h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET;
167 dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n",
168 link_idx, base_offset);
171 dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n",
172 link_idx, h2link->elid);
179 * Hardware recommendations are to wait ~10us before checking any hardware transition
180 * reported by bits changing status.
181 * This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
182 * The worst-case is about 1ms before reporting an issue
184 #define HDAML_POLL_DELAY_MIN_US 10
185 #define HDAML_POLL_DELAY_SLACK_US 5
186 #define HDAML_POLL_DELAY_RETRY 100
188 static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
190 int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
191 int retry = HDAML_POLL_DELAY_RETRY;
194 usleep_range(HDAML_POLL_DELAY_MIN_US,
195 HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
205 usleep_range(HDAML_POLL_DELAY_MIN_US,
206 HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
213 static int hdaml_link_init(u32 __iomem *lctl, int sublink)
216 u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
223 return check_sublink_power(lctl, sublink, true);
226 static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)
232 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
237 return check_sublink_power(lctl, sublink, false);
240 static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)
246 val |= AZX_ML_LCTL_INTEN;
248 val &= ~AZX_ML_LCTL_INTEN;
253 static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
259 return val & AZX_ML_LCTL_INTSTS;
262 static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
264 int timeout = HDAML_POLL_DELAY_RETRY;
268 reg_read = readl(base + offset);
269 if ((reg_read & mask) == target)
273 usleep_range(HDAML_POLL_DELAY_MIN_US,
274 HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
275 } while (timeout != 0);
280 static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
285 val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
286 val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
289 * set SYNCPU but do not wait. The bit is cleared by hardware when
290 * the link becomes active.
292 val |= AZX_REG_ML_LSYNC_SYNCPU;
297 static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
299 return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
302 static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)
307 val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);
312 static void hdaml_link_sync_go(u32 __iomem *lsync)
317 val |= AZX_REG_ML_LSYNC_SYNCGO;
322 static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)
328 return !!(val & cmdsync_mask);
331 static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num)
341 static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
343 u32 val = readl(lctl);
346 val |= AZX_ML_LCTL_OFLEN;
348 val &= ~AZX_ML_LCTL_OFLEN;
353 /* END HDAML section */
355 static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
357 struct hdac_ext2_link *h2link;
358 struct hdac_ext_link *hlink;
361 h2link = kzalloc(sizeof(*h2link), GFP_KERNEL);
365 /* basic initialization */
366 hlink = &h2link->hext_link;
368 hlink->index = index;
370 hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
372 ret = hdaml_lnk_enum(bus->dev, h2link, hlink->ml_addr, index);
378 mutex_init(&h2link->eml_lock);
380 list_add_tail(&hlink->list, &bus->hlink_list);
383 * HDaudio regular links are powered-on by default, the
384 * refcount needs to be initialized.
387 hlink->ref_count = 1;
392 int hda_bus_ml_init(struct hdac_bus *bus)
401 link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
403 dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
405 for (i = 0; i < link_count; i++) {
406 ret = hda_ml_alloc_h2link(bus, i);
408 hda_bus_ml_free(bus);
414 EXPORT_SYMBOL_NS(hda_bus_ml_init, SND_SOC_SOF_HDA_MLINK);
416 void hda_bus_ml_free(struct hdac_bus *bus)
418 struct hdac_ext_link *hlink, *_h;
419 struct hdac_ext2_link *h2link;
424 list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) {
425 list_del(&hlink->list);
426 h2link = hdac_ext_link_to_ext2(hlink);
428 mutex_destroy(&h2link->eml_lock);
432 EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK);
434 static struct hdac_ext2_link *
435 find_ext2_link(struct hdac_bus *bus, bool alt, int elid)
437 struct hdac_ext_link *hlink;
439 list_for_each_entry(hlink, &bus->hlink_list, list) {
440 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
442 if (h2link->alt == alt && h2link->elid == elid)
449 int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
451 struct hdac_ext2_link *h2link;
453 h2link = find_ext2_link(bus, alt, elid);
457 return h2link->slcount;
459 EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK);
461 void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)
463 struct hdac_ext2_link *h2link;
464 struct hdac_ext_link *hlink;
466 h2link = find_ext2_link(bus, alt, elid);
473 hlink = &h2link->hext_link;
475 mutex_lock(&h2link->eml_lock);
477 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
479 mutex_unlock(&h2link->eml_lock);
481 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK);
483 bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
485 struct hdac_ext2_link *h2link;
486 struct hdac_ext_link *hlink;
488 h2link = find_ext2_link(bus, alt, elid);
495 hlink = &h2link->hext_link;
497 return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
499 EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK);
501 int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
503 struct hdac_ext2_link *h2link;
504 struct hdac_ext_link *hlink;
506 h2link = find_ext2_link(bus, alt, elid);
513 hlink = &h2link->hext_link;
515 hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
519 EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
521 int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
523 return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
525 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
527 int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
529 struct hdac_ext2_link *h2link;
530 struct hdac_ext_link *hlink;
532 h2link = find_ext2_link(bus, alt, elid);
539 hlink = &h2link->hext_link;
541 return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
543 EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
545 int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
547 return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
549 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
551 void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
553 struct hdac_ext2_link *h2link;
554 struct hdac_ext_link *hlink;
556 h2link = find_ext2_link(bus, alt, elid);
563 hlink = &h2link->hext_link;
565 hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
567 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
569 void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
571 hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
573 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
575 int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
577 struct hdac_ext2_link *h2link;
578 struct hdac_ext_link *hlink;
580 h2link = find_ext2_link(bus, alt, elid);
587 hlink = &h2link->hext_link;
589 hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
593 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
595 int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
597 return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
599 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
601 bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
603 struct hdac_ext2_link *h2link;
604 struct hdac_ext_link *hlink;
607 h2link = find_ext2_link(bus, alt, elid);
614 hlink = &h2link->hext_link;
616 cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
617 AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
619 return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
622 EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
624 bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
626 return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
628 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
630 static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
633 struct hdac_ext2_link *h2link;
634 struct hdac_ext_link *hlink;
637 h2link = find_ext2_link(bus, alt, elid);
641 if (sublink >= h2link->slcount)
644 hlink = &h2link->hext_link;
647 mutex_lock(&h2link->eml_lock);
650 if (++hlink->ref_count > 1)
653 if (++h2link->sublink_ref_count[sublink] > 1)
657 ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
661 mutex_unlock(&h2link->eml_lock);
666 int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
668 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
670 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK);
672 int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
674 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
676 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
678 static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
681 struct hdac_ext2_link *h2link;
682 struct hdac_ext_link *hlink;
685 h2link = find_ext2_link(bus, alt, elid);
689 if (sublink >= h2link->slcount)
692 hlink = &h2link->hext_link;
695 mutex_lock(&h2link->eml_lock);
698 if (--hlink->ref_count > 0)
701 if (--h2link->sublink_ref_count[sublink] > 0)
704 ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
708 mutex_unlock(&h2link->eml_lock);
713 int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
715 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
717 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK);
719 int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
721 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
723 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
725 int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
727 return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
729 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
731 int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
733 return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
735 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
737 int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
739 struct hdac_ext2_link *h2link;
740 struct hdac_ext_link *hlink;
742 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
746 hlink = &h2link->hext_link;
748 mutex_lock(&h2link->eml_lock);
750 hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
752 mutex_unlock(&h2link->eml_lock);
755 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK);
757 void hda_bus_ml_put_all(struct hdac_bus *bus)
759 struct hdac_ext_link *hlink;
761 list_for_each_entry(hlink, &bus->hlink_list, list) {
762 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
765 snd_hdac_ext_bus_link_put(bus, hlink);
768 EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK);
770 void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
772 struct hdac_ext_link *hlink;
774 /* Reset stream-to-link mapping */
775 list_for_each_entry(hlink, &bus->hlink_list, list)
776 writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
778 EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK);
780 int hda_bus_ml_resume(struct hdac_bus *bus)
782 struct hdac_ext_link *hlink;
785 /* power up links that were active before suspend */
786 list_for_each_entry(hlink, &bus->hlink_list, list) {
787 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
789 if (!h2link->alt && hlink->ref_count) {
790 ret = snd_hdac_ext_bus_link_power_up(hlink);
797 EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK);
799 int hda_bus_ml_suspend(struct hdac_bus *bus)
801 struct hdac_ext_link *hlink;
804 list_for_each_entry(hlink, &bus->hlink_list, list) {
805 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
808 ret = snd_hdac_ext_bus_link_power_down(hlink);
815 EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);
817 struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
819 struct hdac_ext2_link *h2link;
821 h2link = find_ext2_link(bus, alt, elid);
825 return &h2link->eml_lock;
827 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK);
829 struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
831 struct hdac_ext2_link *h2link;
833 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
837 return &h2link->hext_link;
839 EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK);
841 struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
843 struct hdac_ext2_link *h2link;
845 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
849 return &h2link->hext_link;
851 EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK);
853 int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
855 struct hdac_ext2_link *h2link;
856 struct hdac_ext_link *hlink;
858 h2link = find_ext2_link(bus, alt, elid);
865 hlink = &h2link->hext_link;
867 mutex_lock(&h2link->eml_lock);
869 hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
871 mutex_unlock(&h2link->eml_lock);
875 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK);
879 MODULE_LICENSE("Dual BSD/GPL");