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_unlocked(struct hdac_bus *bus, bool alt, int elid, bool enable) in hdac_bus_eml_enable_interrupt_unlocked() argument
489 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_enable_interrupt_unlocked()
493 if (!h2link->intc) in hdac_bus_eml_enable_interrupt_unlocked()
496 hlink = &h2link->hext_link; in hdac_bus_eml_enable_interrupt_unlocked()
498 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); in hdac_bus_eml_enable_interrupt_unlocked()
502 void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable) in hdac_bus_eml_enable_interrupt() argument
507 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_enable_interrupt()
511 if (!h2link->intc) in hdac_bus_eml_enable_interrupt()
514 hlink = &h2link->hext_link; in hdac_bus_eml_enable_interrupt()
516 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_enable_interrupt()
518 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); in hdac_bus_eml_enable_interrupt()
520 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_enable_interrupt()
524 bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) in hdac_bus_eml_check_interrupt() argument
529 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_check_interrupt()
533 if (!h2link->intc) in hdac_bus_eml_check_interrupt()
536 hlink = &h2link->hext_link; in hdac_bus_eml_check_interrupt()
538 return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL); in hdac_bus_eml_check_interrupt()
542 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
547 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_set_syncprd_unlocked()
551 if (!h2link->lss) in hdac_bus_eml_set_syncprd_unlocked()
554 hlink = &h2link->hext_link; in hdac_bus_eml_set_syncprd_unlocked()
556 hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd); in hdac_bus_eml_set_syncprd_unlocked()
568 int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid) in hdac_bus_eml_wait_syncpu_unlocked() argument
573 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_wait_syncpu_unlocked()
577 if (!h2link->lss) in hdac_bus_eml_wait_syncpu_unlocked()
580 hlink = &h2link->hext_link; in hdac_bus_eml_wait_syncpu_unlocked()
582 return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC); in hdac_bus_eml_wait_syncpu_unlocked()
592 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
597 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_sync_arm_unlocked()
601 if (!h2link->lss) in hdac_bus_eml_sync_arm_unlocked()
604 hlink = &h2link->hext_link; in hdac_bus_eml_sync_arm_unlocked()
606 hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink); in hdac_bus_eml_sync_arm_unlocked()
616 int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) in hdac_bus_eml_sync_go_unlocked() argument
621 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_sync_go_unlocked()
625 if (!h2link->lss) in hdac_bus_eml_sync_go_unlocked()
628 hlink = &h2link->hext_link; in hdac_bus_eml_sync_go_unlocked()
630 hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC); in hdac_bus_eml_sync_go_unlocked()
642 bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid) in hdac_bus_eml_check_cmdsync_unlocked() argument
648 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_check_cmdsync_unlocked()
652 if (!h2link->lss) in hdac_bus_eml_check_cmdsync_unlocked()
655 hlink = &h2link->hext_link; in hdac_bus_eml_check_cmdsync_unlocked()
657 cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1, in hdac_bus_eml_check_cmdsync_unlocked()
660 return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC, in hdac_bus_eml_check_cmdsync_unlocked()
671 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
678 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_power_up_base()
680 return -ENODEV; in hdac_bus_eml_power_up_base()
682 if (sublink >= h2link->slcount) in hdac_bus_eml_power_up_base()
683 return -EINVAL; in hdac_bus_eml_power_up_base()
685 hlink = &h2link->hext_link; in hdac_bus_eml_power_up_base()
688 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_power_up_base()
690 if (!alt) { in hdac_bus_eml_power_up_base()
691 if (++hlink->ref_count > 1) in hdac_bus_eml_power_up_base()
694 if (++h2link->sublink_ref_count[sublink] > 1) in hdac_bus_eml_power_up_base()
698 ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); in hdac_bus_eml_power_up_base()
702 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_power_up_base()
707 int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) in hdac_bus_eml_power_up() argument
709 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true); in hdac_bus_eml_power_up()
713 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
715 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false); in hdac_bus_eml_power_up_unlocked()
719 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
726 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_power_down_base()
728 return -ENODEV; in hdac_bus_eml_power_down_base()
730 if (sublink >= h2link->slcount) in hdac_bus_eml_power_down_base()
731 return -EINVAL; in hdac_bus_eml_power_down_base()
733 hlink = &h2link->hext_link; in hdac_bus_eml_power_down_base()
736 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_power_down_base()
738 if (!alt) { in hdac_bus_eml_power_down_base()
739 if (--hlink->ref_count > 0) in hdac_bus_eml_power_down_base()
742 if (--h2link->sublink_ref_count[sublink] > 0) in hdac_bus_eml_power_down_base()
745 ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); in hdac_bus_eml_power_down_base()
749 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_power_down_base()
754 int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink) in hdac_bus_eml_power_down() argument
756 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true); in hdac_bus_eml_power_down()
760 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
762 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false); in hdac_bus_eml_power_down_unlocked()
785 return -ENODEV; in hdac_bus_eml_sdw_get_lsdiid_unlocked()
787 hlink = &h2link->hext_link; in hdac_bus_eml_sdw_get_lsdiid_unlocked()
789 *lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink)); in hdac_bus_eml_sdw_get_lsdiid_unlocked()
801 return -ENODEV; in hdac_bus_eml_sdw_set_lsdiid()
803 hlink = &h2link->hext_link; in hdac_bus_eml_sdw_set_lsdiid()
805 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_sdw_set_lsdiid()
807 hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num); in hdac_bus_eml_sdw_set_lsdiid()
809 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_sdw_set_lsdiid()
829 return -ENODEV; in hdac_bus_eml_sdw_map_stream_ch()
831 pcmsycm = h2link->base_ptr + h2link->shim_offset + in hdac_bus_eml_sdw_map_stream_ch()
832 h2link->instance_offset * sublink + in hdac_bus_eml_sdw_map_stream_ch()
843 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_sdw_map_stream_ch()
848 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_sdw_map_stream_ch()
852 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()
862 list_for_each_entry(hlink, &bus->hlink_list, list) { in hda_bus_ml_put_all()
865 if (!h2link->alt) in hda_bus_ml_put_all()
875 /* Reset stream-to-link mapping */ in hda_bus_ml_reset_losidv()
876 list_for_each_entry(hlink, &bus->hlink_list, list) in hda_bus_ml_reset_losidv()
877 writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); in hda_bus_ml_reset_losidv()
887 list_for_each_entry(hlink, &bus->hlink_list, list) { in hda_bus_ml_resume()
890 if (!h2link->alt && hlink->ref_count) { in hda_bus_ml_resume()
905 list_for_each_entry(hlink, &bus->hlink_list, list) { in hda_bus_ml_suspend()
908 if (!h2link->alt) { in hda_bus_ml_suspend()
918 struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) in hdac_bus_eml_get_mutex() argument
922 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_get_mutex()
926 return &h2link->eml_lock; in hdac_bus_eml_get_mutex()
938 return &h2link->hext_link; in hdac_bus_eml_ssp_get_hlink()
950 return &h2link->hext_link; in hdac_bus_eml_dmic_get_hlink()
962 return &h2link->hext_link; in hdac_bus_eml_sdw_get_hlink()
966 int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable) in hdac_bus_eml_enable_offload() argument
971 h2link = find_ext2_link(bus, alt, elid); in hdac_bus_eml_enable_offload()
973 return -ENODEV; in hdac_bus_eml_enable_offload()
975 if (!h2link->ofls) in hdac_bus_eml_enable_offload()
978 hlink = &h2link->hext_link; in hdac_bus_eml_enable_offload()
980 mutex_lock(&h2link->eml_lock); in hdac_bus_eml_enable_offload()
982 hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable); in hdac_bus_eml_enable_offload()
984 mutex_unlock(&h2link->eml_lock); in hdac_bus_eml_enable_offload()
993 MODULE_DESCRIPTION("SOF support for HDaudio multi-link");