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 */ 48 struct 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 110 static 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 202 static 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 227 static 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 240 static 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 254 static 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 267 static 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 276 static 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 294 static 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 311 static int hdaml_link_wait_syncpu(u32 __iomem *lsync) 312 { 313 return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0); 314 } 315 316 static 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 326 static 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 336 static 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 345 static u16 hdaml_link_get_lsdiid(u16 __iomem *lsdiid) 346 { 347 return readw(lsdiid); 348 } 349 350 static 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 360 static 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 375 static 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 389 static 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_obj(*h2link); 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 426 int 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 /* Enumeration is a one time operation, skip if already done */ 436 if (!list_empty(&bus->hlink_list)) 437 return 0; 438 439 link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; 440 441 dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count); 442 443 for (i = 0; i < link_count; i++) { 444 ret = hda_ml_alloc_h2link(bus, i); 445 if (ret < 0) { 446 hda_bus_ml_free(bus); 447 return ret; 448 } 449 } 450 return 0; 451 } 452 EXPORT_SYMBOL_NS(hda_bus_ml_init, "SND_SOC_SOF_HDA_MLINK"); 453 454 void hda_bus_ml_free(struct hdac_bus *bus) 455 { 456 struct hdac_ext_link *hlink, *_h; 457 struct hdac_ext2_link *h2link; 458 459 if (!bus->mlcap) 460 return; 461 462 list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) { 463 list_del(&hlink->list); 464 h2link = hdac_ext_link_to_ext2(hlink); 465 466 mutex_destroy(&h2link->eml_lock); 467 kfree(h2link); 468 } 469 } 470 EXPORT_SYMBOL_NS(hda_bus_ml_free, "SND_SOC_SOF_HDA_MLINK"); 471 472 static struct hdac_ext2_link * 473 find_ext2_link(struct hdac_bus *bus, bool alt, int elid) 474 { 475 struct hdac_ext_link *hlink; 476 477 list_for_each_entry(hlink, &bus->hlink_list, list) { 478 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); 479 480 if (h2link->alt == alt && h2link->elid == elid) 481 return h2link; 482 } 483 484 return NULL; 485 } 486 487 int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) 488 { 489 struct hdac_ext2_link *h2link; 490 491 h2link = find_ext2_link(bus, alt, elid); 492 if (!h2link) 493 return 0; 494 495 return h2link->slcount; 496 } 497 EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, "SND_SOC_SOF_HDA_MLINK"); 498 499 void hdac_bus_eml_enable_interrupt_unlocked(struct hdac_bus *bus, bool alt, int elid, bool enable) 500 { 501 struct hdac_ext2_link *h2link; 502 struct hdac_ext_link *hlink; 503 504 h2link = find_ext2_link(bus, alt, elid); 505 if (!h2link) 506 return; 507 508 if (!h2link->intc) 509 return; 510 511 hlink = &h2link->hext_link; 512 513 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); 514 } 515 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt_unlocked, "SND_SOC_SOF_HDA_MLINK"); 516 517 void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable) 518 { 519 struct hdac_ext2_link *h2link; 520 struct hdac_ext_link *hlink; 521 522 h2link = find_ext2_link(bus, alt, elid); 523 if (!h2link) 524 return; 525 526 if (!h2link->intc) 527 return; 528 529 hlink = &h2link->hext_link; 530 531 scoped_guard(mutex, &h2link->eml_lock) 532 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); 533 } 534 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, "SND_SOC_SOF_HDA_MLINK"); 535 536 bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) 537 { 538 struct hdac_ext2_link *h2link; 539 struct hdac_ext_link *hlink; 540 541 h2link = find_ext2_link(bus, alt, elid); 542 if (!h2link) 543 return false; 544 545 if (!h2link->intc) 546 return false; 547 548 hlink = &h2link->hext_link; 549 550 return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL); 551 } 552 EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, "SND_SOC_SOF_HDA_MLINK"); 553 554 int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd) 555 { 556 struct hdac_ext2_link *h2link; 557 struct hdac_ext_link *hlink; 558 559 h2link = find_ext2_link(bus, alt, elid); 560 if (!h2link) 561 return 0; 562 563 if (!h2link->lss) 564 return 0; 565 566 hlink = &h2link->hext_link; 567 568 hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd); 569 570 return 0; 571 } 572 EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK"); 573 574 int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd) 575 { 576 return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd); 577 } 578 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK"); 579 580 int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid) 581 { 582 struct hdac_ext2_link *h2link; 583 struct hdac_ext_link *hlink; 584 585 h2link = find_ext2_link(bus, alt, elid); 586 if (!h2link) 587 return 0; 588 589 if (!h2link->lss) 590 return 0; 591 592 hlink = &h2link->hext_link; 593 594 return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC); 595 } 596 EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK"); 597 598 int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) 599 { 600 return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 601 } 602 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK"); 603 604 void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) 605 { 606 struct hdac_ext2_link *h2link; 607 struct hdac_ext_link *hlink; 608 609 h2link = find_ext2_link(bus, alt, elid); 610 if (!h2link) 611 return; 612 613 if (!h2link->lss) 614 return; 615 616 hlink = &h2link->hext_link; 617 618 hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink); 619 } 620 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK"); 621 622 void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink) 623 { 624 hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); 625 } 626 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK"); 627 628 int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) 629 { 630 struct hdac_ext2_link *h2link; 631 struct hdac_ext_link *hlink; 632 633 h2link = find_ext2_link(bus, alt, elid); 634 if (!h2link) 635 return 0; 636 637 if (!h2link->lss) 638 return 0; 639 640 hlink = &h2link->hext_link; 641 642 hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC); 643 644 return 0; 645 } 646 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK"); 647 648 int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) 649 { 650 return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 651 } 652 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK"); 653 654 bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid) 655 { 656 struct hdac_ext2_link *h2link; 657 struct hdac_ext_link *hlink; 658 u32 cmdsync_mask; 659 660 h2link = find_ext2_link(bus, alt, elid); 661 if (!h2link) 662 return 0; 663 664 if (!h2link->lss) 665 return 0; 666 667 hlink = &h2link->hext_link; 668 669 cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1, 670 AZX_REG_ML_LSYNC_CMDSYNC_SHIFT); 671 672 return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC, 673 cmdsync_mask); 674 } 675 EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK"); 676 677 bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus) 678 { 679 return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 680 } 681 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK"); 682 683 static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, 684 bool eml_lock) 685 { 686 struct hdac_ext2_link *h2link; 687 struct hdac_ext_link *hlink; 688 int ret = 0; 689 690 h2link = find_ext2_link(bus, alt, elid); 691 if (!h2link) 692 return -ENODEV; 693 694 if (sublink >= h2link->slcount) 695 return -EINVAL; 696 697 hlink = &h2link->hext_link; 698 699 if (eml_lock) 700 mutex_lock(&h2link->eml_lock); 701 702 if (!alt) { 703 if (++hlink->ref_count > 1) 704 goto skip_init; 705 } else { 706 if (++h2link->sublink_ref_count[sublink] > 1) 707 goto skip_init; 708 } 709 710 ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); 711 if ((h2link->mic_privacy_mask & BIT(sublink)) && !ret) { 712 u16 __iomem *pvccs = h2link->base_ptr + 713 h2link->shim_vs_offset + 714 sublink * h2link->instance_offset + 715 AZX_REG_INTEL_VS_SHIM_PVCCS; 716 u16 val = readw(pvccs); 717 718 writew(val | AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs); 719 720 if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS) 721 dev_dbg(bus->dev, 722 "sublink %d (%d:%d): Mic privacy is enabled\n", 723 sublink, alt, elid); 724 } 725 726 skip_init: 727 if (eml_lock) 728 mutex_unlock(&h2link->eml_lock); 729 730 return ret; 731 } 732 733 int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) 734 { 735 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true); 736 } 737 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, "SND_SOC_SOF_HDA_MLINK"); 738 739 int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) 740 { 741 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false); 742 } 743 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK"); 744 745 static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink, 746 bool eml_lock) 747 { 748 struct hdac_ext2_link *h2link; 749 struct hdac_ext_link *hlink; 750 int ret = 0; 751 752 h2link = find_ext2_link(bus, alt, elid); 753 if (!h2link) 754 return -ENODEV; 755 756 if (sublink >= h2link->slcount) 757 return -EINVAL; 758 759 hlink = &h2link->hext_link; 760 761 if (eml_lock) 762 mutex_lock(&h2link->eml_lock); 763 764 if (!alt) { 765 if (--hlink->ref_count > 0) 766 goto skip_shutdown; 767 } else { 768 if (--h2link->sublink_ref_count[sublink] > 0) 769 goto skip_shutdown; 770 } 771 772 if (h2link->mic_privacy_mask & BIT(sublink)) { 773 u16 __iomem *pvccs = h2link->base_ptr + 774 h2link->shim_vs_offset + 775 sublink * h2link->instance_offset + 776 AZX_REG_INTEL_VS_SHIM_PVCCS; 777 778 writew(readw(pvccs) & ~AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs); 779 } 780 781 ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); 782 783 skip_shutdown: 784 if (eml_lock) 785 mutex_unlock(&h2link->eml_lock); 786 787 return ret; 788 } 789 790 int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink) 791 { 792 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true); 793 } 794 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, "SND_SOC_SOF_HDA_MLINK"); 795 796 int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) 797 { 798 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false); 799 } 800 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK"); 801 802 int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink) 803 { 804 return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); 805 } 806 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK"); 807 808 int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) 809 { 810 return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); 811 } 812 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK"); 813 814 int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid) 815 { 816 struct hdac_ext2_link *h2link; 817 struct hdac_ext_link *hlink; 818 819 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 820 if (!h2link) 821 return -ENODEV; 822 823 hlink = &h2link->hext_link; 824 825 *lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink)); 826 827 return 0; 828 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_lsdiid_unlocked, "SND_SOC_SOF_HDA_MLINK"); 829 830 int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) 831 { 832 struct hdac_ext2_link *h2link; 833 struct hdac_ext_link *hlink; 834 835 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 836 if (!h2link) 837 return -ENODEV; 838 839 hlink = &h2link->hext_link; 840 841 scoped_guard(mutex, &h2link->eml_lock) 842 hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num); 843 844 return 0; 845 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, "SND_SOC_SOF_HDA_MLINK"); 846 847 /* 848 * the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the 849 * PDI index, i.e. the FIFO used for RX or TX 850 */ 851 int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, 852 int channel_mask, int stream_id, int dir) 853 { 854 struct hdac_ext2_link *h2link; 855 u16 __iomem *pcmsycm; 856 int hchan; 857 int lchan; 858 u16 val; 859 860 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 861 if (!h2link) 862 return -ENODEV; 863 864 pcmsycm = h2link->base_ptr + h2link->shim_offset + 865 h2link->instance_offset * sublink + 866 AZX_REG_SDW_SHIM_PCMSyCM(y); 867 868 if (channel_mask) { 869 hchan = __fls(channel_mask); 870 lchan = __ffs(channel_mask); 871 } else { 872 hchan = 0; 873 lchan = 0; 874 } 875 876 scoped_guard(mutex, &h2link->eml_lock) 877 hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan, stream_id, dir); 878 879 val = readw(pcmsycm); 880 881 dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n", 882 sublink, channel_mask, stream_id, dir, val); 883 884 return 0; 885 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK"); 886 887 void hda_bus_ml_reset_losidv(struct hdac_bus *bus) 888 { 889 struct hdac_ext_link *hlink; 890 891 /* Reset stream-to-link mapping */ 892 list_for_each_entry(hlink, &bus->hlink_list, list) 893 writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); 894 } 895 EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK"); 896 897 enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) 898 { 899 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); 900 901 if (!h2link->alt) 902 return HDA_BUS_ML_LINK_HDA; 903 904 switch (h2link->elid) { 905 case AZX_REG_ML_LEPTR_ID_SDW: 906 return HDA_BUS_ML_LINK_SDW; 907 case AZX_REG_ML_LEPTR_ID_INTEL_UAOL: 908 return HDA_BUS_ML_LINK_UAOL; 909 default: 910 return HDA_BUS_ML_LINK_OTHER; 911 } 912 } 913 EXPORT_SYMBOL_NS(hda_bus_ml_link_get_type, "SND_SOC_SOF_HDA_MLINK"); 914 915 int hda_bus_ml_resume(struct hdac_bus *bus) 916 { 917 struct hdac_ext_link *hlink; 918 int ret; 919 920 /* power up links that were active before suspend */ 921 list_for_each_entry(hlink, &bus->hlink_list, list) { 922 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); 923 924 if (!h2link->alt && hlink->ref_count) { 925 ret = snd_hdac_ext_bus_link_power_up(hlink); 926 if (ret < 0) 927 return ret; 928 } 929 } 930 return 0; 931 } 932 EXPORT_SYMBOL_NS(hda_bus_ml_resume, "SND_SOC_SOF_HDA_MLINK"); 933 934 int hda_bus_ml_suspend(struct hdac_bus *bus) 935 { 936 struct hdac_ext_link *hlink; 937 int ret; 938 939 list_for_each_entry(hlink, &bus->hlink_list, list) { 940 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); 941 942 if (!h2link->alt) { 943 ret = snd_hdac_ext_bus_link_power_down(hlink); 944 if (ret < 0) 945 return ret; 946 } 947 } 948 return 0; 949 } 950 EXPORT_SYMBOL_NS(hda_bus_ml_suspend, "SND_SOC_SOF_HDA_MLINK"); 951 952 struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) 953 { 954 struct hdac_ext2_link *h2link; 955 956 h2link = find_ext2_link(bus, alt, elid); 957 if (!h2link) 958 return NULL; 959 960 return &h2link->eml_lock; 961 } 962 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, "SND_SOC_SOF_HDA_MLINK"); 963 964 struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) 965 { 966 struct hdac_ext2_link *h2link; 967 968 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP); 969 if (!h2link) 970 return NULL; 971 972 return &h2link->hext_link; 973 } 974 EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, "SND_SOC_SOF_HDA_MLINK"); 975 976 struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) 977 { 978 struct hdac_ext2_link *h2link; 979 980 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC); 981 if (!h2link) 982 return NULL; 983 984 return &h2link->hext_link; 985 } 986 EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, "SND_SOC_SOF_HDA_MLINK"); 987 988 struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus) 989 { 990 struct hdac_ext2_link *h2link; 991 992 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 993 if (!h2link) 994 return NULL; 995 996 return &h2link->hext_link; 997 } 998 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, "SND_SOC_SOF_HDA_MLINK"); 999 1000 void hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable) 1001 { 1002 struct hdac_ext2_link *h2link; 1003 struct hdac_ext_link *hlink; 1004 1005 h2link = find_ext2_link(bus, alt, elid); 1006 if (!h2link || !h2link->ofls) 1007 return; 1008 1009 hlink = &h2link->hext_link; 1010 1011 scoped_guard(mutex, &h2link->eml_lock) 1012 hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable); 1013 } 1014 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, "SND_SOC_SOF_HDA_MLINK"); 1015 1016 void hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid, 1017 unsigned long mask) 1018 { 1019 struct hdac_ext2_link *h2link; 1020 1021 if (!mask) 1022 return; 1023 1024 h2link = find_ext2_link(bus, alt, elid); 1025 if (!h2link) 1026 return; 1027 1028 if (__fls(mask) > h2link->slcount) { 1029 dev_warn(bus->dev, 1030 "%s: invalid sublink mask for %d:%d, slcount %d: %#lx\n", 1031 __func__, alt, elid, h2link->slcount, mask); 1032 return; 1033 } 1034 1035 dev_dbg(bus->dev, "sublink mask for %d:%d, slcount %d: %#lx\n", alt, 1036 elid, h2link->slcount, mask); 1037 1038 h2link->mic_privacy_mask = mask; 1039 } 1040 EXPORT_SYMBOL_NS(hdac_bus_eml_set_mic_privacy_mask, "SND_SOC_SOF_HDA_MLINK"); 1041 1042 bool hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid) 1043 { 1044 struct hdac_ext2_link *h2link; 1045 bool changed = false; 1046 u16 __iomem *pvccs; 1047 int i; 1048 1049 h2link = find_ext2_link(bus, alt, elid); 1050 if (!h2link) 1051 return false; 1052 1053 /* The change in privacy state needs to be acked for each link */ 1054 for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) { 1055 u16 val; 1056 1057 if (h2link->sublink_ref_count[i] == 0) 1058 continue; 1059 1060 pvccs = h2link->base_ptr + 1061 h2link->shim_vs_offset + 1062 i * h2link->instance_offset + 1063 AZX_REG_INTEL_VS_SHIM_PVCCS; 1064 1065 val = readw(pvccs); 1066 if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG) { 1067 writew(val, pvccs); 1068 changed = true; 1069 } 1070 } 1071 1072 return changed; 1073 } 1074 EXPORT_SYMBOL_NS(hdac_bus_eml_is_mic_privacy_changed, "SND_SOC_SOF_HDA_MLINK"); 1075 1076 bool hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid) 1077 { 1078 struct hdac_ext2_link *h2link; 1079 u16 __iomem *pvccs; 1080 bool state; 1081 int i; 1082 1083 h2link = find_ext2_link(bus, alt, elid); 1084 if (!h2link) 1085 return false; 1086 1087 for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) { 1088 if (h2link->sublink_ref_count[i] == 0) 1089 continue; 1090 1091 /* Return the privacy state from the first active link */ 1092 pvccs = h2link->base_ptr + 1093 h2link->shim_vs_offset + 1094 i * h2link->instance_offset + 1095 AZX_REG_INTEL_VS_SHIM_PVCCS; 1096 1097 state = readw(pvccs) & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS; 1098 dev_dbg(bus->dev, "alt: %d, elid: %d: Mic privacy is %s\n", alt, 1099 elid, str_enabled_disabled(state)); 1100 1101 return state; 1102 } 1103 1104 return false; 1105 } 1106 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mic_privacy_state, "SND_SOC_SOF_HDA_MLINK"); 1107 1108 #endif 1109 1110 MODULE_LICENSE("Dual BSD/GPL"); 1111 MODULE_DESCRIPTION("SOF support for HDaudio multi-link"); 1112