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