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_unlocked(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 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); 499 } 500 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt_unlocked, SND_SOC_SOF_HDA_MLINK); 501 502 void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable) 503 { 504 struct hdac_ext2_link *h2link; 505 struct hdac_ext_link *hlink; 506 507 h2link = find_ext2_link(bus, alt, elid); 508 if (!h2link) 509 return; 510 511 if (!h2link->intc) 512 return; 513 514 hlink = &h2link->hext_link; 515 516 mutex_lock(&h2link->eml_lock); 517 518 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); 519 520 mutex_unlock(&h2link->eml_lock); 521 } 522 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK); 523 524 bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) 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 false; 532 533 if (!h2link->intc) 534 return false; 535 536 hlink = &h2link->hext_link; 537 538 return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL); 539 } 540 EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK); 541 542 int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd) 543 { 544 struct hdac_ext2_link *h2link; 545 struct hdac_ext_link *hlink; 546 547 h2link = find_ext2_link(bus, alt, elid); 548 if (!h2link) 549 return 0; 550 551 if (!h2link->lss) 552 return 0; 553 554 hlink = &h2link->hext_link; 555 556 hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd); 557 558 return 0; 559 } 560 EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK); 561 562 int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd) 563 { 564 return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd); 565 } 566 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK); 567 568 int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid) 569 { 570 struct hdac_ext2_link *h2link; 571 struct hdac_ext_link *hlink; 572 573 h2link = find_ext2_link(bus, alt, elid); 574 if (!h2link) 575 return 0; 576 577 if (!h2link->lss) 578 return 0; 579 580 hlink = &h2link->hext_link; 581 582 return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC); 583 } 584 EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK); 585 586 int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) 587 { 588 return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 589 } 590 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK); 591 592 void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) 593 { 594 struct hdac_ext2_link *h2link; 595 struct hdac_ext_link *hlink; 596 597 h2link = find_ext2_link(bus, alt, elid); 598 if (!h2link) 599 return; 600 601 if (!h2link->lss) 602 return; 603 604 hlink = &h2link->hext_link; 605 606 hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink); 607 } 608 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK); 609 610 void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink) 611 { 612 hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); 613 } 614 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK); 615 616 int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) 617 { 618 struct hdac_ext2_link *h2link; 619 struct hdac_ext_link *hlink; 620 621 h2link = find_ext2_link(bus, alt, elid); 622 if (!h2link) 623 return 0; 624 625 if (!h2link->lss) 626 return 0; 627 628 hlink = &h2link->hext_link; 629 630 hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC); 631 632 return 0; 633 } 634 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK); 635 636 int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) 637 { 638 return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 639 } 640 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK); 641 642 bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid) 643 { 644 struct hdac_ext2_link *h2link; 645 struct hdac_ext_link *hlink; 646 u32 cmdsync_mask; 647 648 h2link = find_ext2_link(bus, alt, elid); 649 if (!h2link) 650 return 0; 651 652 if (!h2link->lss) 653 return 0; 654 655 hlink = &h2link->hext_link; 656 657 cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1, 658 AZX_REG_ML_LSYNC_CMDSYNC_SHIFT); 659 660 return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC, 661 cmdsync_mask); 662 } 663 EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK); 664 665 bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus) 666 { 667 return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 668 } 669 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK); 670 671 static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, 672 bool eml_lock) 673 { 674 struct hdac_ext2_link *h2link; 675 struct hdac_ext_link *hlink; 676 int ret = 0; 677 678 h2link = find_ext2_link(bus, alt, elid); 679 if (!h2link) 680 return -ENODEV; 681 682 if (sublink >= h2link->slcount) 683 return -EINVAL; 684 685 hlink = &h2link->hext_link; 686 687 if (eml_lock) 688 mutex_lock(&h2link->eml_lock); 689 690 if (!alt) { 691 if (++hlink->ref_count > 1) 692 goto skip_init; 693 } else { 694 if (++h2link->sublink_ref_count[sublink] > 1) 695 goto skip_init; 696 } 697 698 ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); 699 700 skip_init: 701 if (eml_lock) 702 mutex_unlock(&h2link->eml_lock); 703 704 return ret; 705 } 706 707 int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) 708 { 709 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true); 710 } 711 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK); 712 713 int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) 714 { 715 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false); 716 } 717 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK); 718 719 static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink, 720 bool eml_lock) 721 { 722 struct hdac_ext2_link *h2link; 723 struct hdac_ext_link *hlink; 724 int ret = 0; 725 726 h2link = find_ext2_link(bus, alt, elid); 727 if (!h2link) 728 return -ENODEV; 729 730 if (sublink >= h2link->slcount) 731 return -EINVAL; 732 733 hlink = &h2link->hext_link; 734 735 if (eml_lock) 736 mutex_lock(&h2link->eml_lock); 737 738 if (!alt) { 739 if (--hlink->ref_count > 0) 740 goto skip_shutdown; 741 } else { 742 if (--h2link->sublink_ref_count[sublink] > 0) 743 goto skip_shutdown; 744 } 745 ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); 746 747 skip_shutdown: 748 if (eml_lock) 749 mutex_unlock(&h2link->eml_lock); 750 751 return ret; 752 } 753 754 int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink) 755 { 756 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true); 757 } 758 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK); 759 760 int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) 761 { 762 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false); 763 } 764 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); 765 766 int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink) 767 { 768 return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); 769 } 770 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK); 771 772 int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) 773 { 774 return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); 775 } 776 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); 777 778 int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid) 779 { 780 struct hdac_ext2_link *h2link; 781 struct hdac_ext_link *hlink; 782 783 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 784 if (!h2link) 785 return -ENODEV; 786 787 hlink = &h2link->hext_link; 788 789 *lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink)); 790 791 return 0; 792 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_lsdiid_unlocked, SND_SOC_SOF_HDA_MLINK); 793 794 int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) 795 { 796 struct hdac_ext2_link *h2link; 797 struct hdac_ext_link *hlink; 798 799 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 800 if (!h2link) 801 return -ENODEV; 802 803 hlink = &h2link->hext_link; 804 805 mutex_lock(&h2link->eml_lock); 806 807 hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num); 808 809 mutex_unlock(&h2link->eml_lock); 810 811 return 0; 812 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK); 813 814 /* 815 * the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the 816 * PDI index, i.e. the FIFO used for RX or TX 817 */ 818 int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, 819 int channel_mask, int stream_id, int dir) 820 { 821 struct hdac_ext2_link *h2link; 822 u16 __iomem *pcmsycm; 823 int hchan; 824 int lchan; 825 u16 val; 826 827 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 828 if (!h2link) 829 return -ENODEV; 830 831 pcmsycm = h2link->base_ptr + h2link->shim_offset + 832 h2link->instance_offset * sublink + 833 AZX_REG_SDW_SHIM_PCMSyCM(y); 834 835 if (channel_mask) { 836 hchan = __fls(channel_mask); 837 lchan = __ffs(channel_mask); 838 } else { 839 hchan = 0; 840 lchan = 0; 841 } 842 843 mutex_lock(&h2link->eml_lock); 844 845 hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan, 846 stream_id, dir); 847 848 mutex_unlock(&h2link->eml_lock); 849 850 val = readw(pcmsycm); 851 852 dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n", 853 sublink, channel_mask, stream_id, dir, val); 854 855 return 0; 856 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, SND_SOC_SOF_HDA_MLINK); 857 858 void hda_bus_ml_put_all(struct hdac_bus *bus) 859 { 860 struct hdac_ext_link *hlink; 861 862 list_for_each_entry(hlink, &bus->hlink_list, list) { 863 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); 864 865 if (!h2link->alt) 866 snd_hdac_ext_bus_link_put(bus, hlink); 867 } 868 } 869 EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK); 870 871 void hda_bus_ml_reset_losidv(struct hdac_bus *bus) 872 { 873 struct hdac_ext_link *hlink; 874 875 /* Reset stream-to-link mapping */ 876 list_for_each_entry(hlink, &bus->hlink_list, list) 877 writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); 878 } 879 EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK); 880 881 int hda_bus_ml_resume(struct hdac_bus *bus) 882 { 883 struct hdac_ext_link *hlink; 884 int ret; 885 886 /* power up links that were active before suspend */ 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 && hlink->ref_count) { 891 ret = snd_hdac_ext_bus_link_power_up(hlink); 892 if (ret < 0) 893 return ret; 894 } 895 } 896 return 0; 897 } 898 EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK); 899 900 int hda_bus_ml_suspend(struct hdac_bus *bus) 901 { 902 struct hdac_ext_link *hlink; 903 int ret; 904 905 list_for_each_entry(hlink, &bus->hlink_list, list) { 906 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); 907 908 if (!h2link->alt) { 909 ret = snd_hdac_ext_bus_link_power_down(hlink); 910 if (ret < 0) 911 return ret; 912 } 913 } 914 return 0; 915 } 916 EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK); 917 918 struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) 919 { 920 struct hdac_ext2_link *h2link; 921 922 h2link = find_ext2_link(bus, alt, elid); 923 if (!h2link) 924 return NULL; 925 926 return &h2link->eml_lock; 927 } 928 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK); 929 930 struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) 931 { 932 struct hdac_ext2_link *h2link; 933 934 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP); 935 if (!h2link) 936 return NULL; 937 938 return &h2link->hext_link; 939 } 940 EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK); 941 942 struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) 943 { 944 struct hdac_ext2_link *h2link; 945 946 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC); 947 if (!h2link) 948 return NULL; 949 950 return &h2link->hext_link; 951 } 952 EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK); 953 954 struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus) 955 { 956 struct hdac_ext2_link *h2link; 957 958 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 959 if (!h2link) 960 return NULL; 961 962 return &h2link->hext_link; 963 } 964 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, SND_SOC_SOF_HDA_MLINK); 965 966 int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable) 967 { 968 struct hdac_ext2_link *h2link; 969 struct hdac_ext_link *hlink; 970 971 h2link = find_ext2_link(bus, alt, elid); 972 if (!h2link) 973 return -ENODEV; 974 975 if (!h2link->ofls) 976 return 0; 977 978 hlink = &h2link->hext_link; 979 980 mutex_lock(&h2link->eml_lock); 981 982 hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable); 983 984 mutex_unlock(&h2link->eml_lock); 985 986 return 0; 987 } 988 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK); 989 990 #endif 991 992 MODULE_LICENSE("Dual BSD/GPL"); 993 MODULE_DESCRIPTION("SOF support for HDaudio multi-link"); 994