FILE
hda-mlink.c
File preview with syntax highlighting.
linux-6.19.14/sound/soc/sof/intel/hda-mlink.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.