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)
23 * struct hdac_ext2_link - HDAudio extended+alternate link
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
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
42 struct hdac_ext2_link {
43 struct hdac_ext_link hext_link;
45 /* read directly from LCAP register */
55 struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
57 /* internal values computed from LCAP contents */
58 void __iomem *base_ptr;
65 #define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
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
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
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
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
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
93 static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
94 void __iomem *ml_addr, int link_idx)
96 struct hdac_ext_link *hlink = &h2link->hext_link;
99 hlink->lcaps = readl(ml_addr + AZX_REG_ML_LCAP);
101 h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);
103 /* handle alternate extensions */
108 * LSDIID is initialized by hardware for HDaudio link,
109 * it needs to be setup by software for alternate links
111 hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);
113 dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",
114 link_idx, hlink->lsdiid);
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);
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);
128 /* find IP ID and offsets */
129 h2link->leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
131 h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
133 base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
134 h2link->base_ptr = hlink->ml_addr + base_offset;
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);
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);
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);
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);
166 dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n",
167 link_idx, h2link->elid);
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
179 #define HDAML_POLL_DELAY_MIN_US 10
180 #define HDAML_POLL_DELAY_SLACK_US 5
181 #define HDAML_POLL_DELAY_RETRY 100
183 static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
185 int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
186 int retry = HDAML_POLL_DELAY_RETRY;
189 usleep_range(HDAML_POLL_DELAY_MIN_US,
190 HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
200 usleep_range(HDAML_POLL_DELAY_MIN_US,
201 HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
208 static int hdaml_link_init(u32 __iomem *lctl, int sublink)
211 u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
218 return check_sublink_power(lctl, sublink, true);
221 static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)
227 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
232 return check_sublink_power(lctl, sublink, false);
235 static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)
241 val |= AZX_ML_LCTL_INTEN;
243 val &= ~AZX_ML_LCTL_INTEN;
248 static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
254 return val & AZX_ML_LCTL_INTSTS;
257 static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
259 int timeout = HDAML_POLL_DELAY_RETRY;
263 reg_read = readl(base + offset);
264 if ((reg_read & mask) == target)
268 usleep_range(HDAML_POLL_DELAY_MIN_US,
269 HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
270 } while (timeout != 0);
275 static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
280 val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
281 val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
284 * set SYNCPU but do not wait. The bit is cleared by hardware when
285 * the link becomes active.
287 val |= AZX_REG_ML_LSYNC_SYNCPU;
292 static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
294 return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
297 static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)
302 val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);
307 static void hdaml_link_sync_go(u32 __iomem *lsync)
312 val |= AZX_REG_ML_LSYNC_SYNCGO;
317 static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)
323 return !!(val & cmdsync_mask);
326 static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num)
336 static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
338 u32 val = readl(lctl);
341 val |= AZX_ML_LCTL_OFLEN;
343 val &= ~AZX_ML_LCTL_OFLEN;
348 /* END HDAML section */
350 static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
352 struct hdac_ext2_link *h2link;
353 struct hdac_ext_link *hlink;
356 h2link = kzalloc(sizeof(*h2link), GFP_KERNEL);
360 /* basic initialization */
361 hlink = &h2link->hext_link;
363 hlink->index = index;
365 hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
367 ret = hdaml_lnk_enum(bus->dev, h2link, hlink->ml_addr, index);
373 mutex_init(&h2link->eml_lock);
375 list_add_tail(&hlink->list, &bus->hlink_list);
378 * HDaudio regular links are powered-on by default, the
379 * refcount needs to be initialized.
382 hlink->ref_count = 1;
387 int hda_bus_ml_init(struct hdac_bus *bus)
396 link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
398 dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
400 for (i = 0; i < link_count; i++) {
401 ret = hda_ml_alloc_h2link(bus, i);
403 hda_bus_ml_free(bus);
409 EXPORT_SYMBOL_NS(hda_bus_ml_init, SND_SOC_SOF_HDA_MLINK);
411 void hda_bus_ml_free(struct hdac_bus *bus)
413 struct hdac_ext_link *hlink, *_h;
414 struct hdac_ext2_link *h2link;
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);
423 mutex_destroy(&h2link->eml_lock);
427 EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK);
429 static struct hdac_ext2_link *
430 find_ext2_link(struct hdac_bus *bus, bool alt, int elid)
432 struct hdac_ext_link *hlink;
434 list_for_each_entry(hlink, &bus->hlink_list, list) {
435 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
437 if (h2link->alt == alt && h2link->elid == elid)
444 int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
446 struct hdac_ext2_link *h2link;
448 h2link = find_ext2_link(bus, alt, elid);
452 return h2link->slcount;
454 EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK);
456 void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)
458 struct hdac_ext2_link *h2link;
459 struct hdac_ext_link *hlink;
461 h2link = find_ext2_link(bus, alt, elid);
468 hlink = &h2link->hext_link;
470 mutex_lock(&h2link->eml_lock);
472 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
474 mutex_unlock(&h2link->eml_lock);
476 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK);
478 bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
480 struct hdac_ext2_link *h2link;
481 struct hdac_ext_link *hlink;
483 h2link = find_ext2_link(bus, alt, elid);
490 hlink = &h2link->hext_link;
492 return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
494 EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK);
496 int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
498 struct hdac_ext2_link *h2link;
499 struct hdac_ext_link *hlink;
501 h2link = find_ext2_link(bus, alt, elid);
508 hlink = &h2link->hext_link;
510 hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
514 EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
516 int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
518 return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
520 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
522 int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
524 struct hdac_ext2_link *h2link;
525 struct hdac_ext_link *hlink;
527 h2link = find_ext2_link(bus, alt, elid);
534 hlink = &h2link->hext_link;
536 return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
538 EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
540 int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
542 return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
544 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
546 void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
548 struct hdac_ext2_link *h2link;
549 struct hdac_ext_link *hlink;
551 h2link = find_ext2_link(bus, alt, elid);
558 hlink = &h2link->hext_link;
560 hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
562 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
564 void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
566 hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
568 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
570 int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
572 struct hdac_ext2_link *h2link;
573 struct hdac_ext_link *hlink;
575 h2link = find_ext2_link(bus, alt, elid);
582 hlink = &h2link->hext_link;
584 hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
588 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
590 int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
592 return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
594 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
596 bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
598 struct hdac_ext2_link *h2link;
599 struct hdac_ext_link *hlink;
602 h2link = find_ext2_link(bus, alt, elid);
609 hlink = &h2link->hext_link;
611 cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
612 AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
614 return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
617 EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
619 bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
621 return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
623 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
625 static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
628 struct hdac_ext2_link *h2link;
629 struct hdac_ext_link *hlink;
632 h2link = find_ext2_link(bus, alt, elid);
636 if (sublink >= h2link->slcount)
639 hlink = &h2link->hext_link;
642 mutex_lock(&h2link->eml_lock);
644 if (++hlink->ref_count > 1)
647 ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
651 mutex_unlock(&h2link->eml_lock);
656 int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
658 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
660 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK);
662 int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
664 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
666 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
668 static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
671 struct hdac_ext2_link *h2link;
672 struct hdac_ext_link *hlink;
675 h2link = find_ext2_link(bus, alt, elid);
679 if (sublink >= h2link->slcount)
682 hlink = &h2link->hext_link;
685 mutex_lock(&h2link->eml_lock);
687 if (--hlink->ref_count > 0)
690 ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
694 mutex_unlock(&h2link->eml_lock);
699 int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
701 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
703 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK);
705 int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
707 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
709 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
711 int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
713 return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
715 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
717 int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
719 return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
721 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
723 int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
725 struct hdac_ext2_link *h2link;
726 struct hdac_ext_link *hlink;
728 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
732 hlink = &h2link->hext_link;
734 mutex_lock(&h2link->eml_lock);
736 hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
738 mutex_unlock(&h2link->eml_lock);
741 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK);
743 void hda_bus_ml_put_all(struct hdac_bus *bus)
745 struct hdac_ext_link *hlink;
747 list_for_each_entry(hlink, &bus->hlink_list, list) {
748 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
751 snd_hdac_ext_bus_link_put(bus, hlink);
754 EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK);
756 void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
758 struct hdac_ext_link *hlink;
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);
764 EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK);
766 int hda_bus_ml_resume(struct hdac_bus *bus)
768 struct hdac_ext_link *hlink;
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);
775 if (!h2link->alt && hlink->ref_count) {
776 ret = snd_hdac_ext_bus_link_power_up(hlink);
783 EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK);
785 int hda_bus_ml_suspend(struct hdac_bus *bus)
787 struct hdac_ext_link *hlink;
790 list_for_each_entry(hlink, &bus->hlink_list, list) {
791 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
794 ret = snd_hdac_ext_bus_link_power_down(hlink);
801 EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);
803 struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
805 struct hdac_ext2_link *h2link;
807 h2link = find_ext2_link(bus, alt, elid);
811 return &h2link->eml_lock;
813 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK);
815 struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
817 struct hdac_ext2_link *h2link;
819 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
823 return &h2link->hext_link;
825 EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK);
827 struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
829 struct hdac_ext2_link *h2link;
831 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
835 return &h2link->hext_link;
837 EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK);
839 int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
841 struct hdac_ext2_link *h2link;
842 struct hdac_ext_link *hlink;
844 h2link = find_ext2_link(bus, alt, elid);
851 hlink = &h2link->hext_link;
853 mutex_lock(&h2link->eml_lock);
855 hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
857 mutex_unlock(&h2link->eml_lock);
861 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK);
865 MODULE_LICENSE("Dual BSD/GPL");