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
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
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
;
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 */
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
);
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
);
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
);
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
);
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
);
174 dev_err(dev
, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n",
175 link_idx
, h2link
->elid
);
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
;
197 usleep_range(HDAML_POLL_DELAY_MIN_US
,
198 HDAML_POLL_DELAY_MIN_US
+ HDAML_POLL_DELAY_SLACK_US
);
208 usleep_range(HDAML_POLL_DELAY_MIN_US
,
209 HDAML_POLL_DELAY_MIN_US
+ HDAML_POLL_DELAY_SLACK_US
);
216 static int hdaml_link_init(u32 __iomem
*lctl
, int sublink
)
219 u32 mask
= BIT(sublink
) << AZX_ML_LCTL_SPA_SHIFT
;
226 return check_sublink_power(lctl
, sublink
, true);
229 static int hdaml_link_shutdown(u32 __iomem
*lctl
, int sublink
)
235 mask
= BIT(sublink
) << AZX_ML_LCTL_SPA_SHIFT
;
240 return check_sublink_power(lctl
, sublink
, false);
243 static void hdaml_link_enable_interrupt(u32 __iomem
*lctl
, bool enable
)
249 val
|= AZX_ML_LCTL_INTEN
;
251 val
&= ~AZX_ML_LCTL_INTEN
;
256 static bool hdaml_link_check_interrupt(u32 __iomem
*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
;
271 reg_read
= readl(base
+ offset
);
272 if ((reg_read
& mask
) == target
)
276 usleep_range(HDAML_POLL_DELAY_MIN_US
,
277 HDAML_POLL_DELAY_MIN_US
+ HDAML_POLL_DELAY_SLACK_US
);
278 } while (timeout
!= 0);
283 static void hdaml_link_set_syncprd(u32 __iomem
*lsync
, u32 syncprd
)
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
;
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
)
310 val
|= (AZX_REG_ML_LSYNC_CMDSYNC
<< sublink
);
315 static void hdaml_link_sync_go(u32 __iomem
*lsync
)
320 val
|= AZX_REG_ML_LSYNC_SYNCGO
;
325 static bool hdaml_link_check_cmdsync(u32 __iomem
*lsync
, u32 cmdsync_mask
)
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
)
349 static void hdaml_shim_map_stream_ch(u16 __iomem
*pcmsycm
, int lchan
, int hchan
,
350 int stream_id
, int dir
)
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
);
369 val
|= AZX_ML_LCTL_OFLEN
;
371 val
&= ~AZX_ML_LCTL_OFLEN
;
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
;
384 h2link
= kzalloc(sizeof(*h2link
), GFP_KERNEL
);
388 /* basic initialization */
389 hlink
= &h2link
->hext_link
;
391 hlink
->index
= index
;
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
);
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.
410 hlink
->ref_count
= 1;
415 int hda_bus_ml_init(struct hdac_bus
*bus
)
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
);
431 hda_bus_ml_free(bus
);
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
;
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
);
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
)
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
);
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
);
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
);
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
);
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
);
554 hlink
= &h2link
->hext_link
;
556 hdaml_link_set_syncprd(hlink
->ml_addr
+ AZX_REG_ML_LSYNC
, syncprd
);
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
);
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
);
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
);
628 hlink
= &h2link
->hext_link
;
630 hdaml_link_sync_go(hlink
->ml_addr
+ AZX_REG_ML_LSYNC
);
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
;
648 h2link
= find_ext2_link(bus
, alt
, elid
);
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
,
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
,
674 struct hdac_ext2_link
*h2link
;
675 struct hdac_ext_link
*hlink
;
678 h2link
= find_ext2_link(bus
, alt
, elid
);
682 if (sublink
>= h2link
->slcount
)
685 hlink
= &h2link
->hext_link
;
688 mutex_lock(&h2link
->eml_lock
);
691 if (++hlink
->ref_count
> 1)
694 if (++h2link
->sublink_ref_count
[sublink
] > 1)
698 ret
= hdaml_link_init(hlink
->ml_addr
+ AZX_REG_ML_LCTL
, sublink
);
702 mutex_unlock(&h2link
->eml_lock
);
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
,
722 struct hdac_ext2_link
*h2link
;
723 struct hdac_ext_link
*hlink
;
726 h2link
= find_ext2_link(bus
, alt
, elid
);
730 if (sublink
>= h2link
->slcount
)
733 hlink
= &h2link
->hext_link
;
736 mutex_lock(&h2link
->eml_lock
);
739 if (--hlink
->ref_count
> 0)
742 if (--h2link
->sublink_ref_count
[sublink
] > 0)
745 ret
= hdaml_link_shutdown(hlink
->ml_addr
+ AZX_REG_ML_LCTL
, sublink
);
749 mutex_unlock(&h2link
->eml_lock
);
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
);
787 hlink
= &h2link
->hext_link
;
789 *lsdiid
= hdaml_link_get_lsdiid(hlink
->ml_addr
+ AZX_REG_ML_LSDIID_OFFSET(sublink
));
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
);
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
);
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
;
827 h2link
= find_ext2_link(bus
, true, AZX_REG_ML_LEPTR_ID_SDW
);
831 pcmsycm
= h2link
->base_ptr
+ h2link
->shim_offset
+
832 h2link
->instance_offset
* sublink
+
833 AZX_REG_SDW_SHIM_PCMSyCM(y
);
836 hchan
= __fls(channel_mask
);
837 lchan
= __ffs(channel_mask
);
843 mutex_lock(&h2link
->eml_lock
);
845 hdaml_shim_map_stream_ch(pcmsycm
, lchan
, hchan
,
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
);
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
);
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
;
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
);
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
;
905 list_for_each_entry(hlink
, &bus
->hlink_list
, list
) {
906 struct hdac_ext2_link
*h2link
= hdac_ext_link_to_ext2(hlink
);
909 ret
= snd_hdac_ext_bus_link_power_down(hlink
);
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
);
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
);
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
);
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
);
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
);
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
);
988 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload
, "SND_SOC_SOF_HDA_MLINK");
992 MODULE_LICENSE("Dual BSD/GPL");
993 MODULE_DESCRIPTION("SOF support for HDaudio multi-link");