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