File Index
Files and directories
Sign in
FILE

hda-mlink.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/sof/intel/hda-mlink.c 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 or
   4// redistributing this file, you may do so under either license.
   5//
   6// Copyright(c) 2022 Intel Corporation
   7//
   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#include <linux/string_choices.h>
  20
  21#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
  22
  23/* worst-case number of sublinks is used for sublink refcount array allocation only */
  24#define HDAML_MAX_SUBLINKS (AZX_ML_LCTL_CPA_SHIFT - AZX_ML_LCTL_SPA_SHIFT)
  25
  26/**
  27 * struct hdac_ext2_link - HDAudio extended+alternate link
  28 *
  29 * @hext_link:		hdac_ext_link
  30 * @alt:		flag set for alternate extended links
  31 * @intc:		boolean for interrupt capable
  32 * @ofls:		boolean for offload support
  33 * @lss:		boolean for link synchronization capabilities
  34 * @slcount:		sublink count
  35 * @elid:		extended link ID (AZX_REG_ML_LEPTR_ID_ defines)
  36 * @elver:		extended link version
  37 * @leptr:		extended link pointer
  38 * @eml_lock:		mutual exclusion to access shared registers e.g. CPA/SPA bits
  39 * in LCTL register
  40 * @sublink_ref_count:	array of refcounts, required to power-manage sublinks independently
  41 * @base_ptr:		pointer to shim/ip/shim_vs space
  42 * @instance_offset:	offset between each of @slcount instances managed by link
  43 * @shim_offset:	offset to SHIM register base
  44 * @ip_offset:		offset to IP register base
  45 * @shim_vs_offset:	offset to vendor-specific (VS) SHIM base
  46 * @mic_privacy_mask:	bitmask of sublinks where mic privacy is applied
  47 */
  48struct hdac_ext2_link {
  49	struct hdac_ext_link hext_link;
  50
  51	/* read directly from LCAP register */
  52	bool alt;
  53	bool intc;
  54	bool ofls;
  55	bool lss;
  56	int slcount;
  57	int elid;
  58	int elver;
  59	u32 leptr;
  60
  61	struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
  62	int sublink_ref_count[HDAML_MAX_SUBLINKS];
  63
  64	/* internal values computed from LCAP contents */
  65	void __iomem *base_ptr;
  66	u32 instance_offset;
  67	u32 shim_offset;
  68	u32 ip_offset;
  69	u32 shim_vs_offset;
  70
  71	unsigned long mic_privacy_mask;
  72};
  73
  74#define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
  75
  76#define AZX_REG_SDW_INSTANCE_OFFSET			0x8000
  77#define AZX_REG_SDW_SHIM_OFFSET				0x0
  78#define AZX_REG_SDW_IP_OFFSET				0x100
  79#define AZX_REG_SDW_VS_SHIM_OFFSET			0x6000
  80#define AZX_REG_SDW_SHIM_PCMSyCM(y)			(0x16 + 0x4 * (y))
  81
  82/* only one instance supported */
  83#define AZX_REG_INTEL_DMIC_SHIM_OFFSET			0x0
  84#define AZX_REG_INTEL_DMIC_IP_OFFSET			0x100
  85#define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET		0x6000
  86
  87#define AZX_REG_INTEL_SSP_INSTANCE_OFFSET		0x1000
  88#define AZX_REG_INTEL_SSP_SHIM_OFFSET			0x0
  89#define AZX_REG_INTEL_SSP_IP_OFFSET			0x100
  90#define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET		0xC00
  91
  92/* only one instance supported */
  93#define AZX_REG_INTEL_UAOL_SHIM_OFFSET			0x0
  94#define AZX_REG_INTEL_UAOL_IP_OFFSET			0x100
  95#define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET		0xC00
  96
  97/* Microphone privacy */
  98#define AZX_REG_INTEL_VS_SHIM_PVCCS			0x10
  99#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE		BIT(0)
 100#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG		BIT(8)
 101#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS		BIT(9)
 102#define AZX_REG_INTEL_VS_SHIM_PVCCS_FMDIS		BIT(10)
 103
 104/* HDAML section - this part follows sequences in the hardware specification,
 105 * including naming conventions and the use of the hdaml_ prefix.
 106 * The code is intentionally minimal with limited dependencies on frameworks or
 107 * helpers. Locking and scanning lists is handled at a higher level
 108 */
 109
 110static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
 111			  void __iomem *remap_addr, void __iomem *ml_addr, int link_idx)
 112{
 113	struct hdac_ext_link *hlink = &h2link->hext_link;
 114	u32 base_offset;
 115
 116	hlink->lcaps  = readl(ml_addr + AZX_REG_ML_LCAP);
 117
 118	h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);
 119
 120	/* handle alternate extensions */
 121	if (!h2link->alt) {
 122		h2link->slcount = 1;
 123
 124		/*
 125		 * LSDIID is initialized by hardware for HDaudio link,
 126		 * it needs to be setup by software for alternate links
 127		 */
 128		hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);
 129
 130		dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",
 131			link_idx, hlink->lsdiid);
 132
 133		return 0;
 134	}
 135
 136	h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps);
 137	h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps);
 138	h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps);
 139
 140	/* read slcount (increment due to zero-based hardware representation */
 141	h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
 142	dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n",
 143		link_idx, h2link->slcount);
 144
 145	/* find IP ID and offsets */
 146	h2link->leptr = readl(ml_addr + AZX_REG_ML_LEPTR);
 147
 148	h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
 149
 150	base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
 151	h2link->base_ptr = remap_addr + base_offset;
 152
 153	switch (h2link->elid) {
 154	case AZX_REG_ML_LEPTR_ID_SDW:
 155		h2link->instance_offset = AZX_REG_SDW_INSTANCE_OFFSET;
 156		h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET;
 157		h2link->ip_offset = AZX_REG_SDW_IP_OFFSET;
 158		h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET;
 159		dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n",
 160			link_idx, base_offset);
 161		break;
 162	case AZX_REG_ML_LEPTR_ID_INTEL_DMIC:
 163		h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET;
 164		h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET;
 165		h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET;
 166		dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n",
 167			link_idx, base_offset);
 168		break;
 169	case AZX_REG_ML_LEPTR_ID_INTEL_SSP:
 170		h2link->instance_offset = AZX_REG_INTEL_SSP_INSTANCE_OFFSET;
 171		h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET;
 172		h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET;
 173		h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET;
 174		dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n",
 175			link_idx, base_offset);
 176		break;
 177	case AZX_REG_ML_LEPTR_ID_INTEL_UAOL:
 178		h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET;
 179		h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET;
 180		h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET;
 181		dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n",
 182			link_idx, base_offset);
 183		break;
 184	default:
 185		dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n",
 186			link_idx, h2link->elid);
 187		return -EINVAL;
 188	}
 189	return 0;
 190}
 191
 192/*
 193 * Hardware recommendations are to wait ~10us before checking any hardware transition
 194 * reported by bits changing status.
 195 * This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
 196 * The worst-case is about 1ms before reporting an issue
 197 */
 198#define HDAML_POLL_DELAY_MIN_US 10
 199#define HDAML_POLL_DELAY_SLACK_US 5
 200#define HDAML_POLL_DELAY_RETRY  100
 201
 202static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
 203{
 204	int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
 205	int retry = HDAML_POLL_DELAY_RETRY;
 206	u32 val;
 207
 208	usleep_range(HDAML_POLL_DELAY_MIN_US,
 209		     HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
 210	do {
 211		val = readl(lctl);
 212		if (enabled) {
 213			if (val & mask)
 214				return 0;
 215		} else {
 216			if (!(val & mask))
 217				return 0;
 218		}
 219		usleep_range(HDAML_POLL_DELAY_MIN_US,
 220			     HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
 221
 222	} while (--retry);
 223
 224	return -EIO;
 225}
 226
 227static int hdaml_link_init(u32 __iomem *lctl, int sublink)
 228{
 229	u32 val;
 230	u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
 231
 232	val = readl(lctl);
 233	val |= mask;
 234
 235	writel(val, lctl);
 236
 237	return check_sublink_power(lctl, sublink, true);
 238}
 239
 240static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)
 241{
 242	u32 val;
 243	u32 mask;
 244
 245	val = readl(lctl);
 246	mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
 247	val &= ~mask;
 248
 249	writel(val, lctl);
 250
 251	return check_sublink_power(lctl, sublink, false);
 252}
 253
 254static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)
 255{
 256	u32 val;
 257
 258	val = readl(lctl);
 259	if (enable)
 260		val |= AZX_ML_LCTL_INTEN;
 261	else
 262		val &= ~AZX_ML_LCTL_INTEN;
 263
 264	writel(val, lctl);
 265}
 266
 267static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
 268{
 269	u32 val;
 270
 271	val = readl(lctl);
 272
 273	return val & AZX_ML_LCTL_INTSTS;
 274}
 275
 276static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
 277{
 278	int timeout = HDAML_POLL_DELAY_RETRY;
 279	u32 reg_read;
 280
 281	do {
 282		reg_read = readl(base + offset);
 283		if ((reg_read & mask) == target)
 284			return 0;
 285
 286		timeout--;
 287		usleep_range(HDAML_POLL_DELAY_MIN_US,
 288			     HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
 289	} while (timeout != 0);
 290
 291	return -EAGAIN;
 292}
 293
 294static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
 295{
 296	u32 val;
 297
 298	val = readl(lsync);
 299	val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
 300	val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
 301
 302	/*
 303	 * set SYNCPU but do not wait. The bit is cleared by hardware when
 304	 * the link becomes active.
 305	 */
 306	val |= AZX_REG_ML_LSYNC_SYNCPU;
 307
 308	writel(val, lsync);
 309}
 310
 311static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
 312{
 313	return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
 314}
 315
 316static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)
 317{
 318	u32 val;
 319
 320	val = readl(lsync);
 321	val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);
 322
 323	writel(val, lsync);
 324}
 325
 326static void hdaml_link_sync_go(u32 __iomem *lsync)
 327{
 328	u32 val;
 329
 330	val = readl(lsync);
 331	val |= AZX_REG_ML_LSYNC_SYNCGO;
 332
 333	writel(val, lsync);
 334}
 335
 336static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)
 337{
 338	u32 val;
 339
 340	val = readl(lsync);
 341
 342	return !!(val & cmdsync_mask);
 343}
 344
 345static u16 hdaml_link_get_lsdiid(u16 __iomem *lsdiid)
 346{
 347	return readw(lsdiid);
 348}
 349
 350static void hdaml_link_set_lsdiid(u16 __iomem *lsdiid, int dev_num)
 351{
 352	u16 val;
 353
 354	val = readw(lsdiid);
 355	val |= BIT(dev_num);
 356
 357	writew(val, lsdiid);
 358}
 359
 360static void hdaml_shim_map_stream_ch(u16 __iomem *pcmsycm, int lchan, int hchan,
 361				     int stream_id, int dir)
 362{
 363	u16 val;
 364
 365	val = readw(pcmsycm);
 366
 367	u16p_replace_bits(&val, lchan, GENMASK(3, 0));
 368	u16p_replace_bits(&val, hchan, GENMASK(7, 4));
 369	u16p_replace_bits(&val, stream_id, GENMASK(13, 8));
 370	u16p_replace_bits(&val, dir, BIT(15));
 371
 372	writew(val, pcmsycm);
 373}
 374
 375static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
 376{
 377	u32 val = readl(lctl);
 378
 379	if (enable)
 380		val |=  AZX_ML_LCTL_OFLEN;
 381	else
 382		val &=  ~AZX_ML_LCTL_OFLEN;
 383
 384	writel(val, lctl);
 385}
 386
 387/* END HDAML section */
 388
 389static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
 390{
 391	struct hdac_ext2_link *h2link;
 392	struct hdac_ext_link *hlink;
 393	int ret;
 394
 395	h2link  = kzalloc(sizeof(*h2link), GFP_KERNEL);
 396	if (!h2link)
 397		return -ENOMEM;
 398
 399	/* basic initialization */
 400	hlink = &h2link->hext_link;
 401
 402	hlink->index = index;
 403	hlink->bus = bus;
 404	hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
 405
 406	ret = hdaml_lnk_enum(bus->dev, h2link, bus->remap_addr, hlink->ml_addr, index);
 407	if (ret < 0) {
 408		kfree(h2link);
 409		return ret;
 410	}
 411
 412	mutex_init(&h2link->eml_lock);
 413
 414	list_add_tail(&hlink->list, &bus->hlink_list);
 415
 416	/*
 417	 * HDaudio regular links are powered-on by default, the
 418	 * refcount needs to be initialized.
 419	 */
 420	if (!h2link->alt)
 421		hlink->ref_count = 1;
 422
 423	return 0;
 424}
 425
 426int hda_bus_ml_init(struct hdac_bus *bus)
 427{
 428	u32 link_count;
 429	int ret;
 430	int i;
 431
 432	if (!bus->mlcap)
 433		return 0;
 434
 435	link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
 436
 437	dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
 438
 439	for (i = 0; i < link_count; i++) {
 440		ret = hda_ml_alloc_h2link(bus, i);
 441		if (ret < 0) {
 442			hda_bus_ml_free(bus);
 443			return ret;
 444		}
 445	}
 446	return 0;
 447}
 448EXPORT_SYMBOL_NS(hda_bus_ml_init, "SND_SOC_SOF_HDA_MLINK");
 449
 450void hda_bus_ml_free(struct hdac_bus *bus)
 451{
 452	struct hdac_ext_link *hlink, *_h;
 453	struct hdac_ext2_link *h2link;
 454
 455	if (!bus->mlcap)
 456		return;
 457
 458	list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) {
 459		list_del(&hlink->list);
 460		h2link = hdac_ext_link_to_ext2(hlink);
 461
 462		mutex_destroy(&h2link->eml_lock);
 463		kfree(h2link);
 464	}
 465}
 466EXPORT_SYMBOL_NS(hda_bus_ml_free, "SND_SOC_SOF_HDA_MLINK");
 467
 468static struct hdac_ext2_link *
 469find_ext2_link(struct hdac_bus *bus, bool alt, int elid)
 470{
 471	struct hdac_ext_link *hlink;
 472
 473	list_for_each_entry(hlink, &bus->hlink_list, list) {
 474		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
 475
 476		if (h2link->alt == alt && h2link->elid == elid)
 477			return h2link;
 478	}
 479
 480	return NULL;
 481}
 482
 483int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
 484{
 485	struct hdac_ext2_link *h2link;
 486
 487	h2link = find_ext2_link(bus, alt, elid);
 488	if (!h2link)
 489		return 0;
 490
 491	return h2link->slcount;
 492}
 493EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, "SND_SOC_SOF_HDA_MLINK");
 494
 495void hdac_bus_eml_enable_interrupt_unlocked(struct hdac_bus *bus, bool alt, int elid, bool enable)
 496{
 497	struct hdac_ext2_link *h2link;
 498	struct hdac_ext_link *hlink;
 499
 500	h2link = find_ext2_link(bus, alt, elid);
 501	if (!h2link)
 502		return;
 503
 504	if (!h2link->intc)
 505		return;
 506
 507	hlink = &h2link->hext_link;
 508
 509	hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
 510}
 511EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt_unlocked, "SND_SOC_SOF_HDA_MLINK");
 512
 513void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)
 514{
 515	struct hdac_ext2_link *h2link;
 516	struct hdac_ext_link *hlink;
 517
 518	h2link = find_ext2_link(bus, alt, elid);
 519	if (!h2link)
 520		return;
 521
 522	if (!h2link->intc)
 523		return;
 524
 525	hlink = &h2link->hext_link;
 526
 527	mutex_lock(&h2link->eml_lock);
 528
 529	hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
 530
 531	mutex_unlock(&h2link->eml_lock);
 532}
 533EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, "SND_SOC_SOF_HDA_MLINK");
 534
 535bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
 536{
 537	struct hdac_ext2_link *h2link;
 538	struct hdac_ext_link *hlink;
 539
 540	h2link = find_ext2_link(bus, alt, elid);
 541	if (!h2link)
 542		return false;
 543
 544	if (!h2link->intc)
 545		return false;
 546
 547	hlink = &h2link->hext_link;
 548
 549	return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
 550}
 551EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, "SND_SOC_SOF_HDA_MLINK");
 552
 553int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
 554{
 555	struct hdac_ext2_link *h2link;
 556	struct hdac_ext_link *hlink;
 557
 558	h2link = find_ext2_link(bus, alt, elid);
 559	if (!h2link)
 560		return 0;
 561
 562	if (!h2link->lss)
 563		return 0;
 564
 565	hlink = &h2link->hext_link;
 566
 567	hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
 568
 569	return 0;
 570}
 571EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK");
 572
 573int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
 574{
 575	return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
 576}
 577EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK");
 578
 579int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
 580{
 581	struct hdac_ext2_link *h2link;
 582	struct hdac_ext_link *hlink;
 583
 584	h2link = find_ext2_link(bus, alt, elid);
 585	if (!h2link)
 586		return 0;
 587
 588	if (!h2link->lss)
 589		return 0;
 590
 591	hlink = &h2link->hext_link;
 592
 593	return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
 594}
 595EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK");
 596
 597int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
 598{
 599	return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
 600}
 601EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK");
 602
 603void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
 604{
 605	struct hdac_ext2_link *h2link;
 606	struct hdac_ext_link *hlink;
 607
 608	h2link = find_ext2_link(bus, alt, elid);
 609	if (!h2link)
 610		return;
 611
 612	if (!h2link->lss)
 613		return;
 614
 615	hlink = &h2link->hext_link;
 616
 617	hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
 618}
 619EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK");
 620
 621void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
 622{
 623	hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
 624}
 625EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK");
 626
 627int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
 628{
 629	struct hdac_ext2_link *h2link;
 630	struct hdac_ext_link *hlink;
 631
 632	h2link = find_ext2_link(bus, alt, elid);
 633	if (!h2link)
 634		return 0;
 635
 636	if (!h2link->lss)
 637		return 0;
 638
 639	hlink = &h2link->hext_link;
 640
 641	hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
 642
 643	return 0;
 644}
 645EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK");
 646
 647int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
 648{
 649	return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
 650}
 651EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK");
 652
 653bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
 654{
 655	struct hdac_ext2_link *h2link;
 656	struct hdac_ext_link *hlink;
 657	u32 cmdsync_mask;
 658
 659	h2link = find_ext2_link(bus, alt, elid);
 660	if (!h2link)
 661		return 0;
 662
 663	if (!h2link->lss)
 664		return 0;
 665
 666	hlink = &h2link->hext_link;
 667
 668	cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
 669			       AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
 670
 671	return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
 672					cmdsync_mask);
 673}
 674EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK");
 675
 676bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
 677{
 678	return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
 679}
 680EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK");
 681
 682static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
 683				      bool eml_lock)
 684{
 685	struct hdac_ext2_link *h2link;
 686	struct hdac_ext_link *hlink;
 687	int ret = 0;
 688
 689	h2link = find_ext2_link(bus, alt, elid);
 690	if (!h2link)
 691		return -ENODEV;
 692
 693	if (sublink >= h2link->slcount)
 694		return -EINVAL;
 695
 696	hlink = &h2link->hext_link;
 697
 698	if (eml_lock)
 699		mutex_lock(&h2link->eml_lock);
 700
 701	if (!alt) {
 702		if (++hlink->ref_count > 1)
 703			goto skip_init;
 704	} else {
 705		if (++h2link->sublink_ref_count[sublink] > 1)
 706			goto skip_init;
 707	}
 708
 709	ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
 710	if ((h2link->mic_privacy_mask & BIT(sublink)) && !ret) {
 711		u16 __iomem *pvccs = h2link->base_ptr +
 712				     h2link->shim_vs_offset +
 713				     sublink * h2link->instance_offset +
 714				     AZX_REG_INTEL_VS_SHIM_PVCCS;
 715		u16 val = readw(pvccs);
 716
 717		writew(val | AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
 718
 719		if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS)
 720			dev_dbg(bus->dev,
 721				"sublink %d (%d:%d): Mic privacy is enabled\n",
 722				sublink, alt, elid);
 723	}
 724
 725skip_init:
 726	if (eml_lock)
 727		mutex_unlock(&h2link->eml_lock);
 728
 729	return ret;
 730}
 731
 732int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
 733{
 734	return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
 735}
 736EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, "SND_SOC_SOF_HDA_MLINK");
 737
 738int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
 739{
 740	return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
 741}
 742EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK");
 743
 744static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
 745					bool eml_lock)
 746{
 747	struct hdac_ext2_link *h2link;
 748	struct hdac_ext_link *hlink;
 749	int ret = 0;
 750
 751	h2link = find_ext2_link(bus, alt, elid);
 752	if (!h2link)
 753		return -ENODEV;
 754
 755	if (sublink >= h2link->slcount)
 756		return -EINVAL;
 757
 758	hlink = &h2link->hext_link;
 759
 760	if (eml_lock)
 761		mutex_lock(&h2link->eml_lock);
 762
 763	if (!alt) {
 764		if (--hlink->ref_count > 0)
 765			goto skip_shutdown;
 766	} else {
 767		if (--h2link->sublink_ref_count[sublink] > 0)
 768			goto skip_shutdown;
 769	}
 770
 771	if (h2link->mic_privacy_mask & BIT(sublink)) {
 772		u16 __iomem *pvccs = h2link->base_ptr +
 773				     h2link->shim_vs_offset +
 774				     sublink * h2link->instance_offset +
 775				     AZX_REG_INTEL_VS_SHIM_PVCCS;
 776
 777		writew(readw(pvccs) & ~AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
 778	}
 779
 780	ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
 781
 782skip_shutdown:
 783	if (eml_lock)
 784		mutex_unlock(&h2link->eml_lock);
 785
 786	return ret;
 787}
 788
 789int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
 790{
 791	return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
 792}
 793EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, "SND_SOC_SOF_HDA_MLINK");
 794
 795int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
 796{
 797	return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
 798}
 799EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK");
 800
 801int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
 802{
 803	return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
 804}
 805EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK");
 806
 807int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
 808{
 809	return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
 810}
 811EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK");
 812
 813int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid)
 814{
 815	struct hdac_ext2_link *h2link;
 816	struct hdac_ext_link *hlink;
 817
 818	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
 819	if (!h2link)
 820		return -ENODEV;
 821
 822	hlink = &h2link->hext_link;
 823
 824	*lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink));
 825
 826	return 0;
 827} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_lsdiid_unlocked, "SND_SOC_SOF_HDA_MLINK");
 828
 829int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
 830{
 831	struct hdac_ext2_link *h2link;
 832	struct hdac_ext_link *hlink;
 833
 834	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
 835	if (!h2link)
 836		return -ENODEV;
 837
 838	hlink = &h2link->hext_link;
 839
 840	mutex_lock(&h2link->eml_lock);
 841
 842	hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
 843
 844	mutex_unlock(&h2link->eml_lock);
 845
 846	return 0;
 847} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, "SND_SOC_SOF_HDA_MLINK");
 848
 849/*
 850 * the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the
 851 * PDI index, i.e. the FIFO used for RX or TX
 852 */
 853int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
 854				   int channel_mask, int stream_id, int dir)
 855{
 856	struct hdac_ext2_link *h2link;
 857	u16 __iomem *pcmsycm;
 858	int hchan;
 859	int lchan;
 860	u16 val;
 861
 862	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
 863	if (!h2link)
 864		return -ENODEV;
 865
 866	pcmsycm = h2link->base_ptr + h2link->shim_offset +
 867		h2link->instance_offset * sublink +
 868		AZX_REG_SDW_SHIM_PCMSyCM(y);
 869
 870	if (channel_mask) {
 871		hchan = __fls(channel_mask);
 872		lchan = __ffs(channel_mask);
 873	} else {
 874		hchan = 0;
 875		lchan = 0;
 876	}
 877
 878	mutex_lock(&h2link->eml_lock);
 879
 880	hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan,
 881				 stream_id, dir);
 882
 883	mutex_unlock(&h2link->eml_lock);
 884
 885	val = readw(pcmsycm);
 886
 887	dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n",
 888		sublink, channel_mask, stream_id, dir, val);
 889
 890	return 0;
 891} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK");
 892
 893void hda_bus_ml_put_all(struct hdac_bus *bus)
 894{
 895	struct hdac_ext_link *hlink;
 896
 897	list_for_each_entry(hlink, &bus->hlink_list, list) {
 898		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
 899
 900		if (!h2link->alt)
 901			snd_hdac_ext_bus_link_put(bus, hlink);
 902	}
 903}
 904EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK");
 905
 906void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
 907{
 908	struct hdac_ext_link *hlink;
 909
 910	/* Reset stream-to-link mapping */
 911	list_for_each_entry(hlink, &bus->hlink_list, list)
 912		writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
 913}
 914EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK");
 915
 916int hda_bus_ml_resume(struct hdac_bus *bus)
 917{
 918	struct hdac_ext_link *hlink;
 919	int ret;
 920
 921	/* power up links that were active before suspend */
 922	list_for_each_entry(hlink, &bus->hlink_list, list) {
 923		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
 924
 925		if (!h2link->alt && hlink->ref_count) {
 926			ret = snd_hdac_ext_bus_link_power_up(hlink);
 927			if (ret < 0)
 928				return ret;
 929		}
 930	}
 931	return 0;
 932}
 933EXPORT_SYMBOL_NS(hda_bus_ml_resume, "SND_SOC_SOF_HDA_MLINK");
 934
 935int hda_bus_ml_suspend(struct hdac_bus *bus)
 936{
 937	struct hdac_ext_link *hlink;
 938	int ret;
 939
 940	list_for_each_entry(hlink, &bus->hlink_list, list) {
 941		struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
 942
 943		if (!h2link->alt) {
 944			ret = snd_hdac_ext_bus_link_power_down(hlink);
 945			if (ret < 0)
 946				return ret;
 947		}
 948	}
 949	return 0;
 950}
 951EXPORT_SYMBOL_NS(hda_bus_ml_suspend, "SND_SOC_SOF_HDA_MLINK");
 952
 953struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
 954{
 955	struct hdac_ext2_link *h2link;
 956
 957	h2link = find_ext2_link(bus, alt, elid);
 958	if (!h2link)
 959		return NULL;
 960
 961	return &h2link->eml_lock;
 962}
 963EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, "SND_SOC_SOF_HDA_MLINK");
 964
 965struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
 966{
 967	struct hdac_ext2_link *h2link;
 968
 969	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
 970	if (!h2link)
 971		return NULL;
 972
 973	return &h2link->hext_link;
 974}
 975EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, "SND_SOC_SOF_HDA_MLINK");
 976
 977struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
 978{
 979	struct hdac_ext2_link *h2link;
 980
 981	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
 982	if (!h2link)
 983		return NULL;
 984
 985	return &h2link->hext_link;
 986}
 987EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, "SND_SOC_SOF_HDA_MLINK");
 988
 989struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus)
 990{
 991	struct hdac_ext2_link *h2link;
 992
 993	h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
 994	if (!h2link)
 995		return NULL;
 996
 997	return &h2link->hext_link;
 998}
 999EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, "SND_SOC_SOF_HDA_MLINK");
1000
1001int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
1002{
1003	struct hdac_ext2_link *h2link;
1004	struct hdac_ext_link *hlink;
1005
1006	h2link = find_ext2_link(bus, alt, elid);
1007	if (!h2link)
1008		return -ENODEV;
1009
1010	if (!h2link->ofls)
1011		return 0;
1012
1013	hlink = &h2link->hext_link;
1014
1015	mutex_lock(&h2link->eml_lock);
1016
1017	hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
1018
1019	mutex_unlock(&h2link->eml_lock);
1020
1021	return 0;
1022}
1023EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, "SND_SOC_SOF_HDA_MLINK");
1024
1025void hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid,
1026				       unsigned long mask)
1027{
1028	struct hdac_ext2_link *h2link;
1029
1030	if (!mask)
1031		return;
1032
1033	h2link = find_ext2_link(bus, alt, elid);
1034	if (!h2link)
1035		return;
1036
1037	if (__fls(mask) > h2link->slcount) {
1038		dev_warn(bus->dev,
1039			 "%s: invalid sublink mask for %d:%d, slcount %d: %#lx\n",
1040			 __func__, alt, elid, h2link->slcount, mask);
1041		return;
1042	}
1043
1044	dev_dbg(bus->dev, "sublink mask for %d:%d, slcount %d: %#lx\n", alt,
1045		elid, h2link->slcount, mask);
1046
1047	h2link->mic_privacy_mask = mask;
1048}
1049EXPORT_SYMBOL_NS(hdac_bus_eml_set_mic_privacy_mask, "SND_SOC_SOF_HDA_MLINK");
1050
1051bool hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid)
1052{
1053	struct hdac_ext2_link *h2link;
1054	bool changed = false;
1055	u16 __iomem *pvccs;
1056	int i;
1057
1058	h2link = find_ext2_link(bus, alt, elid);
1059	if (!h2link)
1060		return false;
1061
1062	/* The change in privacy state needs to be acked for each link */
1063	for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
1064		u16 val;
1065
1066		if (h2link->sublink_ref_count[i] == 0)
1067			continue;
1068
1069		pvccs = h2link->base_ptr +
1070			h2link->shim_vs_offset +
1071			i * h2link->instance_offset +
1072			AZX_REG_INTEL_VS_SHIM_PVCCS;
1073
1074		val = readw(pvccs);
1075		if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG) {
1076			writew(val, pvccs);
1077			changed = true;
1078		}
1079	}
1080
1081	return changed;
1082}
1083EXPORT_SYMBOL_NS(hdac_bus_eml_is_mic_privacy_changed, "SND_SOC_SOF_HDA_MLINK");
1084
1085bool hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid)
1086{
1087	struct hdac_ext2_link *h2link;
1088	u16 __iomem *pvccs;
1089	bool state;
1090	int i;
1091
1092	h2link = find_ext2_link(bus, alt, elid);
1093	if (!h2link)
1094		return false;
1095
1096	for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
1097		if (h2link->sublink_ref_count[i] == 0)
1098			continue;
1099
1100		/* Return the privacy state from the first active link */
1101		pvccs = h2link->base_ptr +
1102			h2link->shim_vs_offset +
1103			i * h2link->instance_offset +
1104			AZX_REG_INTEL_VS_SHIM_PVCCS;
1105
1106		state = readw(pvccs) & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS;
1107		dev_dbg(bus->dev, "alt: %d, elid: %d: Mic privacy is %s\n", alt,
1108			elid, str_enabled_disabled(state));
1109
1110		return state;
1111	}
1112
1113	return false;
1114}
1115EXPORT_SYMBOL_NS(hdac_bus_eml_get_mic_privacy_state, "SND_SOC_SOF_HDA_MLINK");
1116
1117#endif
1118
1119MODULE_LICENSE("Dual BSD/GPL");
1120MODULE_DESCRIPTION("SOF support for HDaudio multi-link");
~/ Read-only file preview.