brintos

brintos / linux-shallow public Read only

0
0
Text · 24.2 KiB · 9a3559c Raw
976 lines · c
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 or4// redistributing this file, you may do so under either license.5//6// Copyright(c) 2022 Intel Corporation7//8 9/*10 * Management of HDaudio multi-link (capabilities, power, coupling)11 */12 13#include <sound/hdaudio_ext.h>14#include <sound/hda_register.h>15#include <sound/hda-mlink.h>16 17#include <linux/bitfield.h>18#include <linux/module.h>19 20#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)21 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)24 25/**26 * struct hdac_ext2_link - HDAudio extended+alternate link27 *28 * @hext_link:		hdac_ext_link29 * @alt:		flag set for alternate extended links30 * @intc:		boolean for interrupt capable31 * @ofls:		boolean for offload support32 * @lss:		boolean for link synchronization capabilities33 * @slcount:		sublink count34 * @elid:		extended link ID (AZX_REG_ML_LEPTR_ID_ defines)35 * @elver:		extended link version36 * @leptr:		extended link pointer37 * @eml_lock:		mutual exclusion to access shared registers e.g. CPA/SPA bits38 * in LCTL register39 * @sublink_ref_count:	array of refcounts, required to power-manage sublinks independently40 * @base_ptr:		pointer to shim/ip/shim_vs space41 * @instance_offset:	offset between each of @slcount instances managed by link42 * @shim_offset:	offset to SHIM register base43 * @ip_offset:		offset to IP register base44 * @shim_vs_offset:	offset to vendor-specific (VS) SHIM base45 */46struct hdac_ext2_link {47	struct hdac_ext_link hext_link;48 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;58 59	struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */60	int sublink_ref_count[HDAML_MAX_SUBLINKS];61 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;68};69 70#define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)71 72#define AZX_REG_SDW_INSTANCE_OFFSET			0x800073#define AZX_REG_SDW_SHIM_OFFSET				0x074#define AZX_REG_SDW_IP_OFFSET				0x10075#define AZX_REG_SDW_VS_SHIM_OFFSET			0x600076#define AZX_REG_SDW_SHIM_PCMSyCM(y)			(0x16 + 0x4 * (y))77 78/* only one instance supported */79#define AZX_REG_INTEL_DMIC_SHIM_OFFSET			0x080#define AZX_REG_INTEL_DMIC_IP_OFFSET			0x10081#define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET		0x600082 83#define AZX_REG_INTEL_SSP_INSTANCE_OFFSET		0x100084#define AZX_REG_INTEL_SSP_SHIM_OFFSET			0x085#define AZX_REG_INTEL_SSP_IP_OFFSET			0x10086#define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET		0xC0087 88/* only one instance supported */89#define AZX_REG_INTEL_UAOL_SHIM_OFFSET			0x090#define AZX_REG_INTEL_UAOL_IP_OFFSET			0x10091#define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET		0xC0092 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 or96 * helpers. Locking and scanning lists is handled at a higher level97 */98 99static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,100			  void __iomem *remap_addr, void __iomem *ml_addr, int link_idx)101{102	struct hdac_ext_link *hlink = &h2link->hext_link;103	u32 base_offset;104 105	hlink->lcaps  = readl(ml_addr + AZX_REG_ML_LCAP);106 107	h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);108 109	/* handle alternate extensions */110	if (!h2link->alt) {111		h2link->slcount = 1;112 113		/*114		 * LSDIID is initialized by hardware for HDaudio link,115		 * it needs to be setup by software for alternate links116		 */117		hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);118 119		dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",120			link_idx, hlink->lsdiid);121 122		return 0;123	}124 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);128 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);133 134	/* find IP ID and offsets */135	h2link->leptr = readl(ml_addr + AZX_REG_ML_LEPTR);136 137	h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);138 139	base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);140	h2link->base_ptr = remap_addr + base_offset;141 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;177	}178	return 0;179}180 181/*182 * Hardware recommendations are to wait ~10us before checking any hardware transition183 * 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 issue186 */187#define HDAML_POLL_DELAY_MIN_US 10188#define HDAML_POLL_DELAY_SLACK_US 5189#define HDAML_POLL_DELAY_RETRY  100190 191static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)192{193	int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;194	int retry = HDAML_POLL_DELAY_RETRY;195	u32 val;196 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;207		}208		usleep_range(HDAML_POLL_DELAY_MIN_US,209			     HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);210 211	} while (--retry);212 213	return -EIO;214}215 216static int hdaml_link_init(u32 __iomem *lctl, int sublink)217{218	u32 val;219	u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;220 221	val = readl(lctl);222	val |= mask;223 224	writel(val, lctl);225 226	return check_sublink_power(lctl, sublink, true);227}228 229static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)230{231	u32 val;232	u32 mask;233 234	val = readl(lctl);235	mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;236	val &= ~mask;237 238	writel(val, lctl);239 240	return check_sublink_power(lctl, sublink, false);241}242 243static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)244{245	u32 val;246 247	val = readl(lctl);248	if (enable)249		val |= AZX_ML_LCTL_INTEN;250	else251		val &= ~AZX_ML_LCTL_INTEN;252 253	writel(val, lctl);254}255 256static bool hdaml_link_check_interrupt(u32 __iomem *lctl)257{258	u32 val;259 260	val = readl(lctl);261 262	return val & AZX_ML_LCTL_INTSTS;263}264 265static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)266{267	int timeout = HDAML_POLL_DELAY_RETRY;268	u32 reg_read;269 270	do {271		reg_read = readl(base + offset);272		if ((reg_read & mask) == target)273			return 0;274 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);279 280	return -EAGAIN;281}282 283static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)284{285	u32 val;286 287	val = readl(lsync);288	val &= ~AZX_REG_ML_LSYNC_SYNCPRD;289	val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);290 291	/*292	 * set SYNCPU but do not wait. The bit is cleared by hardware when293	 * the link becomes active.294	 */295	val |= AZX_REG_ML_LSYNC_SYNCPU;296 297	writel(val, lsync);298}299 300static int hdaml_link_wait_syncpu(u32 __iomem *lsync)301{302	return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);303}304 305static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)306{307	u32 val;308 309	val = readl(lsync);310	val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);311 312	writel(val, lsync);313}314 315static void hdaml_link_sync_go(u32 __iomem *lsync)316{317	u32 val;318 319	val = readl(lsync);320	val |= AZX_REG_ML_LSYNC_SYNCGO;321 322	writel(val, lsync);323}324 325static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)326{327	u32 val;328 329	val = readl(lsync);330 331	return !!(val & cmdsync_mask);332}333 334static u16 hdaml_link_get_lsdiid(u16 __iomem *lsdiid)335{336	return readw(lsdiid);337}338 339static void hdaml_link_set_lsdiid(u16 __iomem *lsdiid, int dev_num)340{341	u16 val;342 343	val = readw(lsdiid);344	val |= BIT(dev_num);345 346	writew(val, lsdiid);347}348 349static void hdaml_shim_map_stream_ch(u16 __iomem *pcmsycm, int lchan, int hchan,350				     int stream_id, int dir)351{352	u16 val;353 354	val = readw(pcmsycm);355 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));360 361	writew(val, pcmsycm);362}363 364static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)365{366	u32 val = readl(lctl);367 368	if (enable)369		val |=  AZX_ML_LCTL_OFLEN;370	else371		val &=  ~AZX_ML_LCTL_OFLEN;372 373	writel(val, lctl);374}375 376/* END HDAML section */377 378static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)379{380	struct hdac_ext2_link *h2link;381	struct hdac_ext_link *hlink;382	int ret;383 384	h2link  = kzalloc(sizeof(*h2link), GFP_KERNEL);385	if (!h2link)386		return -ENOMEM;387 388	/* basic initialization */389	hlink = &h2link->hext_link;390 391	hlink->index = index;392	hlink->bus = bus;393	hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);394 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;399	}400 401	mutex_init(&h2link->eml_lock);402 403	list_add_tail(&hlink->list, &bus->hlink_list);404 405	/*406	 * HDaudio regular links are powered-on by default, the407	 * refcount needs to be initialized.408	 */409	if (!h2link->alt)410		hlink->ref_count = 1;411 412	return 0;413}414 415int hda_bus_ml_init(struct hdac_bus *bus)416{417	u32 link_count;418	int ret;419	int i;420 421	if (!bus->mlcap)422		return 0;423 424	link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;425 426	dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);427 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;433		}434	}435	return 0;436}437EXPORT_SYMBOL_NS(hda_bus_ml_init, SND_SOC_SOF_HDA_MLINK);438 439void hda_bus_ml_free(struct hdac_bus *bus)440{441	struct hdac_ext_link *hlink, *_h;442	struct hdac_ext2_link *h2link;443 444	if (!bus->mlcap)445		return;446 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);450 451		mutex_destroy(&h2link->eml_lock);452		kfree(h2link);453	}454}455EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK);456 457static struct hdac_ext2_link *458find_ext2_link(struct hdac_bus *bus, bool alt, int elid)459{460	struct hdac_ext_link *hlink;461 462	list_for_each_entry(hlink, &bus->hlink_list, list) {463		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);464 465		if (h2link->alt == alt && h2link->elid == elid)466			return h2link;467	}468 469	return NULL;470}471 472int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)473{474	struct hdac_ext2_link *h2link;475 476	h2link = find_ext2_link(bus, alt, elid);477	if (!h2link)478		return 0;479 480	return h2link->slcount;481}482EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK);483 484void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)485{486	struct hdac_ext2_link *h2link;487	struct hdac_ext_link *hlink;488 489	h2link = find_ext2_link(bus, alt, elid);490	if (!h2link)491		return;492 493	if (!h2link->intc)494		return;495 496	hlink = &h2link->hext_link;497 498	mutex_lock(&h2link->eml_lock);499 500	hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);501 502	mutex_unlock(&h2link->eml_lock);503}504EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK);505 506bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)507{508	struct hdac_ext2_link *h2link;509	struct hdac_ext_link *hlink;510 511	h2link = find_ext2_link(bus, alt, elid);512	if (!h2link)513		return false;514 515	if (!h2link->intc)516		return false;517 518	hlink = &h2link->hext_link;519 520	return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);521}522EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK);523 524int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)525{526	struct hdac_ext2_link *h2link;527	struct hdac_ext_link *hlink;528 529	h2link = find_ext2_link(bus, alt, elid);530	if (!h2link)531		return 0;532 533	if (!h2link->lss)534		return 0;535 536	hlink = &h2link->hext_link;537 538	hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);539 540	return 0;541}542EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);543 544int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)545{546	return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);547}548EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);549 550int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)551{552	struct hdac_ext2_link *h2link;553	struct hdac_ext_link *hlink;554 555	h2link = find_ext2_link(bus, alt, elid);556	if (!h2link)557		return 0;558 559	if (!h2link->lss)560		return 0;561 562	hlink = &h2link->hext_link;563 564	return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);565}566EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);567 568int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)569{570	return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);571}572EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);573 574void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)575{576	struct hdac_ext2_link *h2link;577	struct hdac_ext_link *hlink;578 579	h2link = find_ext2_link(bus, alt, elid);580	if (!h2link)581		return;582 583	if (!h2link->lss)584		return;585 586	hlink = &h2link->hext_link;587 588	hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);589}590EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);591 592void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)593{594	hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);595}596EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);597 598int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)599{600	struct hdac_ext2_link *h2link;601	struct hdac_ext_link *hlink;602 603	h2link = find_ext2_link(bus, alt, elid);604	if (!h2link)605		return 0;606 607	if (!h2link->lss)608		return 0;609 610	hlink = &h2link->hext_link;611 612	hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);613 614	return 0;615}616EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);617 618int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)619{620	return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);621}622EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);623 624bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)625{626	struct hdac_ext2_link *h2link;627	struct hdac_ext_link *hlink;628	u32 cmdsync_mask;629 630	h2link = find_ext2_link(bus, alt, elid);631	if (!h2link)632		return 0;633 634	if (!h2link->lss)635		return 0;636 637	hlink = &h2link->hext_link;638 639	cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,640			       AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);641 642	return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,643					cmdsync_mask);644}645EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);646 647bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)648{649	return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);650}651EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);652 653static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,654				      bool eml_lock)655{656	struct hdac_ext2_link *h2link;657	struct hdac_ext_link *hlink;658	int ret = 0;659 660	h2link = find_ext2_link(bus, alt, elid);661	if (!h2link)662		return -ENODEV;663 664	if (sublink >= h2link->slcount)665		return -EINVAL;666 667	hlink = &h2link->hext_link;668 669	if (eml_lock)670		mutex_lock(&h2link->eml_lock);671 672	if (!alt) {673		if (++hlink->ref_count > 1)674			goto skip_init;675	} else {676		if (++h2link->sublink_ref_count[sublink] > 1)677			goto skip_init;678	}679 680	ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);681 682skip_init:683	if (eml_lock)684		mutex_unlock(&h2link->eml_lock);685 686	return ret;687}688 689int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)690{691	return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);692}693EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK);694 695int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)696{697	return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);698}699EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);700 701static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,702					bool eml_lock)703{704	struct hdac_ext2_link *h2link;705	struct hdac_ext_link *hlink;706	int ret = 0;707 708	h2link = find_ext2_link(bus, alt, elid);709	if (!h2link)710		return -ENODEV;711 712	if (sublink >= h2link->slcount)713		return -EINVAL;714 715	hlink = &h2link->hext_link;716 717	if (eml_lock)718		mutex_lock(&h2link->eml_lock);719 720	if (!alt) {721		if (--hlink->ref_count > 0)722			goto skip_shutdown;723	} else {724		if (--h2link->sublink_ref_count[sublink] > 0)725			goto skip_shutdown;726	}727	ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);728 729skip_shutdown:730	if (eml_lock)731		mutex_unlock(&h2link->eml_lock);732 733	return ret;734}735 736int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)737{738	return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);739}740EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK);741 742int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)743{744	return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);745}746EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);747 748int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)749{750	return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);751}752EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);753 754int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)755{756	return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);757}758EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);759 760int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid)761{762	struct hdac_ext2_link *h2link;763	struct hdac_ext_link *hlink;764 765	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);766	if (!h2link)767		return -ENODEV;768 769	hlink = &h2link->hext_link;770 771	*lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink));772 773	return 0;774} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_lsdiid_unlocked, SND_SOC_SOF_HDA_MLINK);775 776int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)777{778	struct hdac_ext2_link *h2link;779	struct hdac_ext_link *hlink;780 781	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);782	if (!h2link)783		return -ENODEV;784 785	hlink = &h2link->hext_link;786 787	mutex_lock(&h2link->eml_lock);788 789	hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);790 791	mutex_unlock(&h2link->eml_lock);792 793	return 0;794} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK);795 796/*797 * the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the798 * PDI index, i.e. the FIFO used for RX or TX799 */800int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,801				   int channel_mask, int stream_id, int dir)802{803	struct hdac_ext2_link *h2link;804	u16 __iomem *pcmsycm;805	int hchan;806	int lchan;807	u16 val;808 809	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);810	if (!h2link)811		return -ENODEV;812 813	pcmsycm = h2link->base_ptr + h2link->shim_offset +814		h2link->instance_offset * sublink +815		AZX_REG_SDW_SHIM_PCMSyCM(y);816 817	if (channel_mask) {818		hchan = __fls(channel_mask);819		lchan = __ffs(channel_mask);820	} else {821		hchan = 0;822		lchan = 0;823	}824 825	mutex_lock(&h2link->eml_lock);826 827	hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan,828				 stream_id, dir);829 830	mutex_unlock(&h2link->eml_lock);831 832	val = readw(pcmsycm);833 834	dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n",835		sublink, channel_mask, stream_id, dir, val);836 837	return 0;838} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, SND_SOC_SOF_HDA_MLINK);839 840void hda_bus_ml_put_all(struct hdac_bus *bus)841{842	struct hdac_ext_link *hlink;843 844	list_for_each_entry(hlink, &bus->hlink_list, list) {845		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);846 847		if (!h2link->alt)848			snd_hdac_ext_bus_link_put(bus, hlink);849	}850}851EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK);852 853void hda_bus_ml_reset_losidv(struct hdac_bus *bus)854{855	struct hdac_ext_link *hlink;856 857	/* Reset stream-to-link mapping */858	list_for_each_entry(hlink, &bus->hlink_list, list)859		writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);860}861EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK);862 863int hda_bus_ml_resume(struct hdac_bus *bus)864{865	struct hdac_ext_link *hlink;866	int ret;867 868	/* power up links that were active before suspend */869	list_for_each_entry(hlink, &bus->hlink_list, list) {870		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);871 872		if (!h2link->alt && hlink->ref_count) {873			ret = snd_hdac_ext_bus_link_power_up(hlink);874			if (ret < 0)875				return ret;876		}877	}878	return 0;879}880EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK);881 882int hda_bus_ml_suspend(struct hdac_bus *bus)883{884	struct hdac_ext_link *hlink;885	int ret;886 887	list_for_each_entry(hlink, &bus->hlink_list, list) {888		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);889 890		if (!h2link->alt) {891			ret = snd_hdac_ext_bus_link_power_down(hlink);892			if (ret < 0)893				return ret;894		}895	}896	return 0;897}898EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);899 900struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)901{902	struct hdac_ext2_link *h2link;903 904	h2link = find_ext2_link(bus, alt, elid);905	if (!h2link)906		return NULL;907 908	return &h2link->eml_lock;909}910EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK);911 912struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)913{914	struct hdac_ext2_link *h2link;915 916	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);917	if (!h2link)918		return NULL;919 920	return &h2link->hext_link;921}922EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK);923 924struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)925{926	struct hdac_ext2_link *h2link;927 928	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);929	if (!h2link)930		return NULL;931 932	return &h2link->hext_link;933}934EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK);935 936struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus)937{938	struct hdac_ext2_link *h2link;939 940	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);941	if (!h2link)942		return NULL;943 944	return &h2link->hext_link;945}946EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, SND_SOC_SOF_HDA_MLINK);947 948int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)949{950	struct hdac_ext2_link *h2link;951	struct hdac_ext_link *hlink;952 953	h2link = find_ext2_link(bus, alt, elid);954	if (!h2link)955		return -ENODEV;956 957	if (!h2link->ofls)958		return 0;959 960	hlink = &h2link->hext_link;961 962	mutex_lock(&h2link->eml_lock);963 964	hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);965 966	mutex_unlock(&h2link->eml_lock);967 968	return 0;969}970EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK);971 972#endif973 974MODULE_LICENSE("Dual BSD/GPL");975MODULE_DESCRIPTION("SOF support for HDaudio multi-link");976