Merge tag 'clk-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / sound / soc / sof / intel / hda-mlink.c
blobfe627bcb0531a064ead936fda55d84330f891324
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 //
9 /*
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)
25 /**
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
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
46 struct hdac_ext2_link {
47 struct hdac_ext_link hext_link;
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;
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;
64 u32 instance_offset;
65 u32 shim_offset;
66 u32 ip_offset;
67 u32 shim_vs_offset;
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
76 #define AZX_REG_SDW_SHIM_PCMSyCM(y) (0x16 + 0x4 * (y))
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
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
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
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
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)
102 struct hdac_ext_link *hlink = &h2link->hext_link;
103 u32 base_offset;
105 hlink->lcaps = readl(ml_addr + AZX_REG_ML_LCAP);
107 h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);
109 /* handle alternate extensions */
110 if (!h2link->alt) {
111 h2link->slcount = 1;
114 * LSDIID is initialized by hardware for HDaudio link,
115 * it needs to be setup by software for alternate links
117 hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);
119 dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",
120 link_idx, hlink->lsdiid);
122 return 0;
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);
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);
134 /* find IP ID and offsets */
135 h2link->leptr = readl(ml_addr + AZX_REG_ML_LEPTR);
137 h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
139 base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
140 h2link->base_ptr = remap_addr + base_offset;
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;
178 return 0;
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
187 #define HDAML_POLL_DELAY_MIN_US 10
188 #define HDAML_POLL_DELAY_SLACK_US 5
189 #define HDAML_POLL_DELAY_RETRY 100
191 static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
193 int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
194 int retry = HDAML_POLL_DELAY_RETRY;
195 u32 val;
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;
208 usleep_range(HDAML_POLL_DELAY_MIN_US,
209 HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
211 } while (--retry);
213 return -EIO;
216 static int hdaml_link_init(u32 __iomem *lctl, int sublink)
218 u32 val;
219 u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
221 val = readl(lctl);
222 val |= mask;
224 writel(val, lctl);
226 return check_sublink_power(lctl, sublink, true);
229 static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)
231 u32 val;
232 u32 mask;
234 val = readl(lctl);
235 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
236 val &= ~mask;
238 writel(val, lctl);
240 return check_sublink_power(lctl, sublink, false);
243 static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)
245 u32 val;
247 val = readl(lctl);
248 if (enable)
249 val |= AZX_ML_LCTL_INTEN;
250 else
251 val &= ~AZX_ML_LCTL_INTEN;
253 writel(val, lctl);
256 static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
258 u32 val;
260 val = readl(lctl);
262 return val & AZX_ML_LCTL_INTSTS;
265 static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
267 int timeout = HDAML_POLL_DELAY_RETRY;
268 u32 reg_read;
270 do {
271 reg_read = readl(base + offset);
272 if ((reg_read & mask) == target)
273 return 0;
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);
280 return -EAGAIN;
283 static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
285 u32 val;
287 val = readl(lsync);
288 val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
289 val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
292 * set SYNCPU but do not wait. The bit is cleared by hardware when
293 * the link becomes active.
295 val |= AZX_REG_ML_LSYNC_SYNCPU;
297 writel(val, lsync);
300 static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
302 return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
305 static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)
307 u32 val;
309 val = readl(lsync);
310 val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);
312 writel(val, lsync);
315 static void hdaml_link_sync_go(u32 __iomem *lsync)
317 u32 val;
319 val = readl(lsync);
320 val |= AZX_REG_ML_LSYNC_SYNCGO;
322 writel(val, lsync);
325 static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)
327 u32 val;
329 val = readl(lsync);
331 return !!(val & cmdsync_mask);
334 static u16 hdaml_link_get_lsdiid(u16 __iomem *lsdiid)
336 return readw(lsdiid);
339 static void hdaml_link_set_lsdiid(u16 __iomem *lsdiid, int dev_num)
341 u16 val;
343 val = readw(lsdiid);
344 val |= BIT(dev_num);
346 writew(val, lsdiid);
349 static void hdaml_shim_map_stream_ch(u16 __iomem *pcmsycm, int lchan, int hchan,
350 int stream_id, int dir)
352 u16 val;
354 val = readw(pcmsycm);
356 u16p_replace_bits(&val, lchan, GENMASK(3, 0));
357 u16p_replace_bits(&val, hchan, GENMASK(7, 4));
358 u16p_replace_bits(&val, stream_id, GENMASK(13, 8));
359 u16p_replace_bits(&val, dir, BIT(15));
361 writew(val, pcmsycm);
364 static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
366 u32 val = readl(lctl);
368 if (enable)
369 val |= AZX_ML_LCTL_OFLEN;
370 else
371 val &= ~AZX_ML_LCTL_OFLEN;
373 writel(val, lctl);
376 /* END HDAML section */
378 static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
380 struct hdac_ext2_link *h2link;
381 struct hdac_ext_link *hlink;
382 int ret;
384 h2link = kzalloc(sizeof(*h2link), GFP_KERNEL);
385 if (!h2link)
386 return -ENOMEM;
388 /* basic initialization */
389 hlink = &h2link->hext_link;
391 hlink->index = index;
392 hlink->bus = bus;
393 hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
395 ret = hdaml_lnk_enum(bus->dev, h2link, bus->remap_addr, hlink->ml_addr, index);
396 if (ret < 0) {
397 kfree(h2link);
398 return ret;
401 mutex_init(&h2link->eml_lock);
403 list_add_tail(&hlink->list, &bus->hlink_list);
406 * HDaudio regular links are powered-on by default, the
407 * refcount needs to be initialized.
409 if (!h2link->alt)
410 hlink->ref_count = 1;
412 return 0;
415 int hda_bus_ml_init(struct hdac_bus *bus)
417 u32 link_count;
418 int ret;
419 int i;
421 if (!bus->mlcap)
422 return 0;
424 link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
426 dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
428 for (i = 0; i < link_count; i++) {
429 ret = hda_ml_alloc_h2link(bus, i);
430 if (ret < 0) {
431 hda_bus_ml_free(bus);
432 return ret;
435 return 0;
437 EXPORT_SYMBOL_NS(hda_bus_ml_init, "SND_SOC_SOF_HDA_MLINK");
439 void hda_bus_ml_free(struct hdac_bus *bus)
441 struct hdac_ext_link *hlink, *_h;
442 struct hdac_ext2_link *h2link;
444 if (!bus->mlcap)
445 return;
447 list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) {
448 list_del(&hlink->list);
449 h2link = hdac_ext_link_to_ext2(hlink);
451 mutex_destroy(&h2link->eml_lock);
452 kfree(h2link);
455 EXPORT_SYMBOL_NS(hda_bus_ml_free, "SND_SOC_SOF_HDA_MLINK");
457 static struct hdac_ext2_link *
458 find_ext2_link(struct hdac_bus *bus, bool alt, int elid)
460 struct hdac_ext_link *hlink;
462 list_for_each_entry(hlink, &bus->hlink_list, list) {
463 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
465 if (h2link->alt == alt && h2link->elid == elid)
466 return h2link;
469 return NULL;
472 int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
474 struct hdac_ext2_link *h2link;
476 h2link = find_ext2_link(bus, alt, elid);
477 if (!h2link)
478 return 0;
480 return h2link->slcount;
482 EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, "SND_SOC_SOF_HDA_MLINK");
484 void hdac_bus_eml_enable_interrupt_unlocked(struct hdac_bus *bus, bool alt, int elid, bool enable)
486 struct hdac_ext2_link *h2link;
487 struct hdac_ext_link *hlink;
489 h2link = find_ext2_link(bus, alt, elid);
490 if (!h2link)
491 return;
493 if (!h2link->intc)
494 return;
496 hlink = &h2link->hext_link;
498 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
500 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt_unlocked, "SND_SOC_SOF_HDA_MLINK");
502 void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)
504 struct hdac_ext2_link *h2link;
505 struct hdac_ext_link *hlink;
507 h2link = find_ext2_link(bus, alt, elid);
508 if (!h2link)
509 return;
511 if (!h2link->intc)
512 return;
514 hlink = &h2link->hext_link;
516 mutex_lock(&h2link->eml_lock);
518 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
520 mutex_unlock(&h2link->eml_lock);
522 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, "SND_SOC_SOF_HDA_MLINK");
524 bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
526 struct hdac_ext2_link *h2link;
527 struct hdac_ext_link *hlink;
529 h2link = find_ext2_link(bus, alt, elid);
530 if (!h2link)
531 return false;
533 if (!h2link->intc)
534 return false;
536 hlink = &h2link->hext_link;
538 return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
540 EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, "SND_SOC_SOF_HDA_MLINK");
542 int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
544 struct hdac_ext2_link *h2link;
545 struct hdac_ext_link *hlink;
547 h2link = find_ext2_link(bus, alt, elid);
548 if (!h2link)
549 return 0;
551 if (!h2link->lss)
552 return 0;
554 hlink = &h2link->hext_link;
556 hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
558 return 0;
560 EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK");
562 int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
564 return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
566 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK");
568 int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
570 struct hdac_ext2_link *h2link;
571 struct hdac_ext_link *hlink;
573 h2link = find_ext2_link(bus, alt, elid);
574 if (!h2link)
575 return 0;
577 if (!h2link->lss)
578 return 0;
580 hlink = &h2link->hext_link;
582 return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
584 EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK");
586 int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
588 return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
590 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK");
592 void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
594 struct hdac_ext2_link *h2link;
595 struct hdac_ext_link *hlink;
597 h2link = find_ext2_link(bus, alt, elid);
598 if (!h2link)
599 return;
601 if (!h2link->lss)
602 return;
604 hlink = &h2link->hext_link;
606 hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
608 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK");
610 void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
612 hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
614 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK");
616 int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
618 struct hdac_ext2_link *h2link;
619 struct hdac_ext_link *hlink;
621 h2link = find_ext2_link(bus, alt, elid);
622 if (!h2link)
623 return 0;
625 if (!h2link->lss)
626 return 0;
628 hlink = &h2link->hext_link;
630 hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
632 return 0;
634 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK");
636 int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
638 return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
640 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK");
642 bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
644 struct hdac_ext2_link *h2link;
645 struct hdac_ext_link *hlink;
646 u32 cmdsync_mask;
648 h2link = find_ext2_link(bus, alt, elid);
649 if (!h2link)
650 return 0;
652 if (!h2link->lss)
653 return 0;
655 hlink = &h2link->hext_link;
657 cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
658 AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
660 return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
661 cmdsync_mask);
663 EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK");
665 bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
667 return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
669 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK");
671 static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
672 bool eml_lock)
674 struct hdac_ext2_link *h2link;
675 struct hdac_ext_link *hlink;
676 int ret = 0;
678 h2link = find_ext2_link(bus, alt, elid);
679 if (!h2link)
680 return -ENODEV;
682 if (sublink >= h2link->slcount)
683 return -EINVAL;
685 hlink = &h2link->hext_link;
687 if (eml_lock)
688 mutex_lock(&h2link->eml_lock);
690 if (!alt) {
691 if (++hlink->ref_count > 1)
692 goto skip_init;
693 } else {
694 if (++h2link->sublink_ref_count[sublink] > 1)
695 goto skip_init;
698 ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
700 skip_init:
701 if (eml_lock)
702 mutex_unlock(&h2link->eml_lock);
704 return ret;
707 int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
709 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
711 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, "SND_SOC_SOF_HDA_MLINK");
713 int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
715 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
717 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK");
719 static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
720 bool eml_lock)
722 struct hdac_ext2_link *h2link;
723 struct hdac_ext_link *hlink;
724 int ret = 0;
726 h2link = find_ext2_link(bus, alt, elid);
727 if (!h2link)
728 return -ENODEV;
730 if (sublink >= h2link->slcount)
731 return -EINVAL;
733 hlink = &h2link->hext_link;
735 if (eml_lock)
736 mutex_lock(&h2link->eml_lock);
738 if (!alt) {
739 if (--hlink->ref_count > 0)
740 goto skip_shutdown;
741 } else {
742 if (--h2link->sublink_ref_count[sublink] > 0)
743 goto skip_shutdown;
745 ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
747 skip_shutdown:
748 if (eml_lock)
749 mutex_unlock(&h2link->eml_lock);
751 return ret;
754 int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
756 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
758 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, "SND_SOC_SOF_HDA_MLINK");
760 int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
762 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
764 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK");
766 int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
768 return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
770 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK");
772 int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
774 return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
776 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK");
778 int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid)
780 struct hdac_ext2_link *h2link;
781 struct hdac_ext_link *hlink;
783 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
784 if (!h2link)
785 return -ENODEV;
787 hlink = &h2link->hext_link;
789 *lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink));
791 return 0;
792 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_lsdiid_unlocked, "SND_SOC_SOF_HDA_MLINK");
794 int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
796 struct hdac_ext2_link *h2link;
797 struct hdac_ext_link *hlink;
799 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
800 if (!h2link)
801 return -ENODEV;
803 hlink = &h2link->hext_link;
805 mutex_lock(&h2link->eml_lock);
807 hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
809 mutex_unlock(&h2link->eml_lock);
811 return 0;
812 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, "SND_SOC_SOF_HDA_MLINK");
815 * the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the
816 * PDI index, i.e. the FIFO used for RX or TX
818 int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
819 int channel_mask, int stream_id, int dir)
821 struct hdac_ext2_link *h2link;
822 u16 __iomem *pcmsycm;
823 int hchan;
824 int lchan;
825 u16 val;
827 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
828 if (!h2link)
829 return -ENODEV;
831 pcmsycm = h2link->base_ptr + h2link->shim_offset +
832 h2link->instance_offset * sublink +
833 AZX_REG_SDW_SHIM_PCMSyCM(y);
835 if (channel_mask) {
836 hchan = __fls(channel_mask);
837 lchan = __ffs(channel_mask);
838 } else {
839 hchan = 0;
840 lchan = 0;
843 mutex_lock(&h2link->eml_lock);
845 hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan,
846 stream_id, dir);
848 mutex_unlock(&h2link->eml_lock);
850 val = readw(pcmsycm);
852 dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n",
853 sublink, channel_mask, stream_id, dir, val);
855 return 0;
856 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK");
858 void hda_bus_ml_put_all(struct hdac_bus *bus)
860 struct hdac_ext_link *hlink;
862 list_for_each_entry(hlink, &bus->hlink_list, list) {
863 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
865 if (!h2link->alt)
866 snd_hdac_ext_bus_link_put(bus, hlink);
869 EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK");
871 void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
873 struct hdac_ext_link *hlink;
875 /* Reset stream-to-link mapping */
876 list_for_each_entry(hlink, &bus->hlink_list, list)
877 writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
879 EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK");
881 int hda_bus_ml_resume(struct hdac_bus *bus)
883 struct hdac_ext_link *hlink;
884 int ret;
886 /* power up links that were active before suspend */
887 list_for_each_entry(hlink, &bus->hlink_list, list) {
888 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
890 if (!h2link->alt && hlink->ref_count) {
891 ret = snd_hdac_ext_bus_link_power_up(hlink);
892 if (ret < 0)
893 return ret;
896 return 0;
898 EXPORT_SYMBOL_NS(hda_bus_ml_resume, "SND_SOC_SOF_HDA_MLINK");
900 int hda_bus_ml_suspend(struct hdac_bus *bus)
902 struct hdac_ext_link *hlink;
903 int ret;
905 list_for_each_entry(hlink, &bus->hlink_list, list) {
906 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
908 if (!h2link->alt) {
909 ret = snd_hdac_ext_bus_link_power_down(hlink);
910 if (ret < 0)
911 return ret;
914 return 0;
916 EXPORT_SYMBOL_NS(hda_bus_ml_suspend, "SND_SOC_SOF_HDA_MLINK");
918 struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
920 struct hdac_ext2_link *h2link;
922 h2link = find_ext2_link(bus, alt, elid);
923 if (!h2link)
924 return NULL;
926 return &h2link->eml_lock;
928 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, "SND_SOC_SOF_HDA_MLINK");
930 struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
932 struct hdac_ext2_link *h2link;
934 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
935 if (!h2link)
936 return NULL;
938 return &h2link->hext_link;
940 EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, "SND_SOC_SOF_HDA_MLINK");
942 struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
944 struct hdac_ext2_link *h2link;
946 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
947 if (!h2link)
948 return NULL;
950 return &h2link->hext_link;
952 EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, "SND_SOC_SOF_HDA_MLINK");
954 struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus)
956 struct hdac_ext2_link *h2link;
958 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
959 if (!h2link)
960 return NULL;
962 return &h2link->hext_link;
964 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, "SND_SOC_SOF_HDA_MLINK");
966 int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
968 struct hdac_ext2_link *h2link;
969 struct hdac_ext_link *hlink;
971 h2link = find_ext2_link(bus, alt, elid);
972 if (!h2link)
973 return -ENODEV;
975 if (!h2link->ofls)
976 return 0;
978 hlink = &h2link->hext_link;
980 mutex_lock(&h2link->eml_lock);
982 hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
984 mutex_unlock(&h2link->eml_lock);
986 return 0;
988 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, "SND_SOC_SOF_HDA_MLINK");
990 #endif
992 MODULE_LICENSE("Dual BSD/GPL");
993 MODULE_DESCRIPTION("SOF support for HDaudio multi-link");