Lines Matching +full:- +full:alt
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
10 * Management of HDaudio multi-link (capabilities, power, coupling)
15 #include <sound/hda-mlink.h>
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
29 * @alt: flag set for alternate extended links
39 * @sublink_ref_count: array of refcounts, required to power-manage sublinks independently
44 * @shim_vs_offset: offset to vendor-specific (VS) SHIM base
50 bool alt; member
93 /* HDAML section - this part follows sequences in the hardware specification,
102 struct hdac_ext_link *hlink = &h2link->hext_link; in hdaml_lnk_enum()
105 hlink->lcaps = readl(ml_addr + AZX_REG_ML_LCAP); in hdaml_lnk_enum()
107 h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps); in hdaml_lnk_enum()
110 if (!h2link->alt) { in hdaml_lnk_enum()
111 h2link->slcount = 1; in hdaml_lnk_enum()
117 hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID); in hdaml_lnk_enum()
119 dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n", in hdaml_lnk_enum()
120 link_idx, hlink->lsdiid); in hdaml_lnk_enum()
125 h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps); in hdaml_lnk_enum()
126 h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps); in hdaml_lnk_enum()
127 h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps); in hdaml_lnk_enum()
129 /* read slcount (increment due to zero-based hardware representation */ in hdaml_lnk_enum()
130 h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1; in hdaml_lnk_enum()
131 dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n", in hdaml_lnk_enum()
132 link_idx, h2link->slcount); in hdaml_lnk_enum()
135 h2link->leptr = readl(ml_addr + AZX_REG_ML_LEPTR); in hdaml_lnk_enum()
137 h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr); in hdaml_lnk_enum()
139 base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr); in hdaml_lnk_enum()
140 h2link->base_ptr = remap_addr + base_offset; in hdaml_lnk_enum()
142 switch (h2link->elid) { in hdaml_lnk_enum()
144 h2link->instance_offset = AZX_REG_SDW_INSTANCE_OFFSET; in hdaml_lnk_enum()
145 h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET; in hdaml_lnk_enum()
146 h2link->ip_offset = AZX_REG_SDW_IP_OFFSET; in hdaml_lnk_enum()
147 h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET; in hdaml_lnk_enum()
148 dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n", in hdaml_lnk_enum()
152 h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET; in hdaml_lnk_enum()
153 h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET; in hdaml_lnk_enum()
154 h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET; in hdaml_lnk_enum()
155 dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n", in hdaml_lnk_enum()
159 h2link->instance_offset = AZX_REG_INTEL_SSP_INSTANCE_OFFSET; in hdaml_lnk_enum()
160 h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET; in hdaml_lnk_enum()
161 h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET; in hdaml_lnk_enum()
162 h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET; in hdaml_lnk_enum()
163 dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n", in hdaml_lnk_enum()
167 h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET; in hdaml_lnk_enum()
168 h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET; in hdaml_lnk_enum()
169 h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET; in hdaml_lnk_enum()
170 dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n", in hdaml_lnk_enum()
174 dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n", in hdaml_lnk_enum()
175 link_idx, h2link->elid); in hdaml_lnk_enum()
176 return -EINVAL; in hdaml_lnk_enum()
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
211 } while (--retry); in check_sublink_power()
213 return -EIO; in check_sublink_power()
275 timeout--; in hdaml_wait_bit()
280 return -EAGAIN; in hdaml_wait_bit()
386 return -ENOMEM; in hda_ml_alloc_h2link()
389 hlink = &h2link->hext_link; in hda_ml_alloc_h2link()
391 hlink->index = index; in hda_ml_alloc_h2link()
392 hlink->bus = bus; in hda_ml_alloc_h2link()
393 hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index); in hda_ml_alloc_h2link()
395 ret = hdaml_lnk_enum(bus->dev, h2link, bus->remap_addr, hlink->ml_addr, index); in hda_ml_alloc_h2link()
401 mutex_init(&h2link->eml_lock); in hda_ml_alloc_h2link()
403 list_add_tail(&hlink->list, &bus->hlink_list); in hda_ml_alloc_h2link()
406 * HDaudio regular links are powered-on by default, the in hda_ml_alloc_h2link()
409 if (!h2link->alt) in hda_ml_alloc_h2link()
410 hlink->ref_count = 1; in hda_ml_alloc_h2link()
421 if (!bus->mlcap) in hda_bus_ml_init()
424 link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; in hda_bus_ml_init()
426 dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count); in hda_bus_ml_init()
444 if (!bus->mlcap) in hda_bus_ml_free()
447 list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) { in hda_bus_ml_free()
448 list_del(&hlink->list); in hda_bus_ml_free()
451 mutex_destroy(&h2link->eml_lock); in hda_bus_ml_free()
458 find_ext2_link(struct hdac_bus *bus, bool alt, int elid) in find_ext2_link() argument
462 list_for_each_entry(hlink, &bus->hlink_list, list) { in find_ext2_link()
465 if (h2link->alt == alt && h2link->elid == elid) in find_ext2_link()
472 int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) in hdac_bus_eml_get_count() argument
476 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_get_count()
480 return h2link->slcount; in hdac_bus_eml_get_count()
484 void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable) in hdac_bus_eml_enable_interrupt() argument
489 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_enable_interrupt()
493 if (!h2link->intc) in hdac_bus_eml_enable_interrupt()
496 hlink = &h2link->hext_link; in hdac_bus_eml_enable_interrupt()
498 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_enable_interrupt()
500 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); in hdac_bus_eml_enable_interrupt()
502 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_enable_interrupt()
506 bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) in hdac_bus_eml_check_interrupt() argument
511 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_check_interrupt()
515 if (!h2link->intc) in hdac_bus_eml_check_interrupt()
518 hlink = &h2link->hext_link; in hdac_bus_eml_check_interrupt()
520 return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL); in hdac_bus_eml_check_interrupt()
524 int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd) in hdac_bus_eml_set_syncprd_unlocked() argument
529 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_set_syncprd_unlocked()
533 if (!h2link->lss) in hdac_bus_eml_set_syncprd_unlocked()
536 hlink = &h2link->hext_link; in hdac_bus_eml_set_syncprd_unlocked()
538 hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd); in hdac_bus_eml_set_syncprd_unlocked()
550 int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid) in hdac_bus_eml_wait_syncpu_unlocked() argument
555 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_wait_syncpu_unlocked()
559 if (!h2link->lss) in hdac_bus_eml_wait_syncpu_unlocked()
562 hlink = &h2link->hext_link; in hdac_bus_eml_wait_syncpu_unlocked()
564 return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC); in hdac_bus_eml_wait_syncpu_unlocked()
574 void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) in hdac_bus_eml_sync_arm_unlocked() argument
579 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_sync_arm_unlocked()
583 if (!h2link->lss) in hdac_bus_eml_sync_arm_unlocked()
586 hlink = &h2link->hext_link; in hdac_bus_eml_sync_arm_unlocked()
588 hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink); in hdac_bus_eml_sync_arm_unlocked()
598 int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) in hdac_bus_eml_sync_go_unlocked() argument
603 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_sync_go_unlocked()
607 if (!h2link->lss) in hdac_bus_eml_sync_go_unlocked()
610 hlink = &h2link->hext_link; in hdac_bus_eml_sync_go_unlocked()
612 hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC); in hdac_bus_eml_sync_go_unlocked()
624 bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid) in hdac_bus_eml_check_cmdsync_unlocked() argument
630 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_check_cmdsync_unlocked()
634 if (!h2link->lss) in hdac_bus_eml_check_cmdsync_unlocked()
637 hlink = &h2link->hext_link; in hdac_bus_eml_check_cmdsync_unlocked()
639 cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1, in hdac_bus_eml_check_cmdsync_unlocked()
642 return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC, in hdac_bus_eml_check_cmdsync_unlocked()
653 static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, in hdac_bus_eml_power_up_base() argument
660 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_power_up_base()
662 return -ENODEV; in hdac_bus_eml_power_up_base()
664 if (sublink >= h2link->slcount) in hdac_bus_eml_power_up_base()
665 return -EINVAL; in hdac_bus_eml_power_up_base()
667 hlink = &h2link->hext_link; in hdac_bus_eml_power_up_base()
670 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_power_up_base()
672 if (!alt) { in hdac_bus_eml_power_up_base()
673 if (++hlink->ref_count > 1) in hdac_bus_eml_power_up_base()
676 if (++h2link->sublink_ref_count[sublink] > 1) in hdac_bus_eml_power_up_base()
680 ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); in hdac_bus_eml_power_up_base()
684 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_power_up_base()
689 int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) in hdac_bus_eml_power_up() argument
691 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true); in hdac_bus_eml_power_up()
695 int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) in hdac_bus_eml_power_up_unlocked() argument
697 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false); in hdac_bus_eml_power_up_unlocked()
701 static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink, in hdac_bus_eml_power_down_base() argument
708 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_power_down_base()
710 return -ENODEV; in hdac_bus_eml_power_down_base()
712 if (sublink >= h2link->slcount) in hdac_bus_eml_power_down_base()
713 return -EINVAL; in hdac_bus_eml_power_down_base()
715 hlink = &h2link->hext_link; in hdac_bus_eml_power_down_base()
718 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_power_down_base()
720 if (!alt) { in hdac_bus_eml_power_down_base()
721 if (--hlink->ref_count > 0) in hdac_bus_eml_power_down_base()
724 if (--h2link->sublink_ref_count[sublink] > 0) in hdac_bus_eml_power_down_base()
727 ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); in hdac_bus_eml_power_down_base()
731 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_power_down_base()
736 int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink) in hdac_bus_eml_power_down() argument
738 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true); in hdac_bus_eml_power_down()
742 int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) in hdac_bus_eml_power_down_unlocked() argument
744 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false); in hdac_bus_eml_power_down_unlocked()
767 return -ENODEV; in hdac_bus_eml_sdw_get_lsdiid_unlocked()
769 hlink = &h2link->hext_link; in hdac_bus_eml_sdw_get_lsdiid_unlocked()
771 *lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink)); in hdac_bus_eml_sdw_get_lsdiid_unlocked()
783 return -ENODEV; in hdac_bus_eml_sdw_set_lsdiid()
785 hlink = &h2link->hext_link; in hdac_bus_eml_sdw_set_lsdiid()
787 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_sdw_set_lsdiid()
789 hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num); in hdac_bus_eml_sdw_set_lsdiid()
791 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_sdw_set_lsdiid()
811 return -ENODEV; in hdac_bus_eml_sdw_map_stream_ch()
813 pcmsycm = h2link->base_ptr + h2link->shim_offset + in hdac_bus_eml_sdw_map_stream_ch()
814 h2link->instance_offset * sublink + in hdac_bus_eml_sdw_map_stream_ch()
825 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_sdw_map_stream_ch()
830 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_sdw_map_stream_ch()
834 dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n", in hdac_bus_eml_sdw_map_stream_ch()
844 list_for_each_entry(hlink, &bus->hlink_list, list) { in hda_bus_ml_put_all()
847 if (!h2link->alt) in hda_bus_ml_put_all()
857 /* Reset stream-to-link mapping */ in hda_bus_ml_reset_losidv()
858 list_for_each_entry(hlink, &bus->hlink_list, list) in hda_bus_ml_reset_losidv()
859 writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); in hda_bus_ml_reset_losidv()
869 list_for_each_entry(hlink, &bus->hlink_list, list) { in hda_bus_ml_resume()
872 if (!h2link->alt && hlink->ref_count) { in hda_bus_ml_resume()
887 list_for_each_entry(hlink, &bus->hlink_list, list) { in hda_bus_ml_suspend()
890 if (!h2link->alt) { in hda_bus_ml_suspend()
900 struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) in hdac_bus_eml_get_mutex() argument
904 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_get_mutex()
908 return &h2link->eml_lock; in hdac_bus_eml_get_mutex()
920 return &h2link->hext_link; in hdac_bus_eml_ssp_get_hlink()
932 return &h2link->hext_link; in hdac_bus_eml_dmic_get_hlink()
944 return &h2link->hext_link; in hdac_bus_eml_sdw_get_hlink()
948 int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable) in hdac_bus_eml_enable_offload() argument
953 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_enable_offload()
955 return -ENODEV; in hdac_bus_eml_enable_offload()
957 if (!h2link->ofls) in hdac_bus_eml_enable_offload()
960 hlink = &h2link->hext_link; in hdac_bus_eml_enable_offload()
962 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_enable_offload()
964 hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable); in hdac_bus_eml_enable_offload()
966 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_enable_offload()
975 MODULE_DESCRIPTION("SOF support for HDaudio multi-link");