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 /* 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 void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num) 335 { 336 u32 val; 337 338 val = readl(lsdiid); 339 val |= BIT(dev_num); 340 341 writel(val, lsdiid); 342 } 343 344 static void hdaml_shim_map_stream_ch(u16 __iomem *pcmsycm, int lchan, int hchan, 345 int stream_id, int dir) 346 { 347 u16 val; 348 349 val = readw(pcmsycm); 350 351 u16p_replace_bits(&val, lchan, GENMASK(3, 0)); 352 u16p_replace_bits(&val, hchan, GENMASK(7, 4)); 353 u16p_replace_bits(&val, stream_id, GENMASK(13, 8)); 354 u16p_replace_bits(&val, dir, BIT(15)); 355 356 writew(val, pcmsycm); 357 } 358 359 static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable) 360 { 361 u32 val = readl(lctl); 362 363 if (enable) 364 val |= AZX_ML_LCTL_OFLEN; 365 else 366 val &= ~AZX_ML_LCTL_OFLEN; 367 368 writel(val, lctl); 369 } 370 371 /* END HDAML section */ 372 373 static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) 374 { 375 struct hdac_ext2_link *h2link; 376 struct hdac_ext_link *hlink; 377 int ret; 378 379 h2link = kzalloc(sizeof(*h2link), GFP_KERNEL); 380 if (!h2link) 381 return -ENOMEM; 382 383 /* basic initialization */ 384 hlink = &h2link->hext_link; 385 386 hlink->index = index; 387 hlink->bus = bus; 388 hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index); 389 390 ret = hdaml_lnk_enum(bus->dev, h2link, bus->remap_addr, hlink->ml_addr, index); 391 if (ret < 0) { 392 kfree(h2link); 393 return ret; 394 } 395 396 mutex_init(&h2link->eml_lock); 397 398 list_add_tail(&hlink->list, &bus->hlink_list); 399 400 /* 401 * HDaudio regular links are powered-on by default, the 402 * refcount needs to be initialized. 403 */ 404 if (!h2link->alt) 405 hlink->ref_count = 1; 406 407 return 0; 408 } 409 410 int hda_bus_ml_init(struct hdac_bus *bus) 411 { 412 u32 link_count; 413 int ret; 414 int i; 415 416 if (!bus->mlcap) 417 return 0; 418 419 link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; 420 421 dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count); 422 423 for (i = 0; i < link_count; i++) { 424 ret = hda_ml_alloc_h2link(bus, i); 425 if (ret < 0) { 426 hda_bus_ml_free(bus); 427 return ret; 428 } 429 } 430 return 0; 431 } 432 EXPORT_SYMBOL_NS(hda_bus_ml_init, SND_SOC_SOF_HDA_MLINK); 433 434 void hda_bus_ml_free(struct hdac_bus *bus) 435 { 436 struct hdac_ext_link *hlink, *_h; 437 struct hdac_ext2_link *h2link; 438 439 if (!bus->mlcap) 440 return; 441 442 list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) { 443 list_del(&hlink->list); 444 h2link = hdac_ext_link_to_ext2(hlink); 445 446 mutex_destroy(&h2link->eml_lock); 447 kfree(h2link); 448 } 449 } 450 EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK); 451 452 static struct hdac_ext2_link * 453 find_ext2_link(struct hdac_bus *bus, bool alt, int elid) 454 { 455 struct hdac_ext_link *hlink; 456 457 list_for_each_entry(hlink, &bus->hlink_list, list) { 458 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); 459 460 if (h2link->alt == alt && h2link->elid == elid) 461 return h2link; 462 } 463 464 return NULL; 465 } 466 467 int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) 468 { 469 struct hdac_ext2_link *h2link; 470 471 h2link = find_ext2_link(bus, alt, elid); 472 if (!h2link) 473 return 0; 474 475 return h2link->slcount; 476 } 477 EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK); 478 479 void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable) 480 { 481 struct hdac_ext2_link *h2link; 482 struct hdac_ext_link *hlink; 483 484 h2link = find_ext2_link(bus, alt, elid); 485 if (!h2link) 486 return; 487 488 if (!h2link->intc) 489 return; 490 491 hlink = &h2link->hext_link; 492 493 mutex_lock(&h2link->eml_lock); 494 495 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); 496 497 mutex_unlock(&h2link->eml_lock); 498 } 499 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK); 500 501 bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) 502 { 503 struct hdac_ext2_link *h2link; 504 struct hdac_ext_link *hlink; 505 506 h2link = find_ext2_link(bus, alt, elid); 507 if (!h2link) 508 return false; 509 510 if (!h2link->intc) 511 return false; 512 513 hlink = &h2link->hext_link; 514 515 return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL); 516 } 517 EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK); 518 519 int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd) 520 { 521 struct hdac_ext2_link *h2link; 522 struct hdac_ext_link *hlink; 523 524 h2link = find_ext2_link(bus, alt, elid); 525 if (!h2link) 526 return 0; 527 528 if (!h2link->lss) 529 return 0; 530 531 hlink = &h2link->hext_link; 532 533 hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd); 534 535 return 0; 536 } 537 EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK); 538 539 int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd) 540 { 541 return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd); 542 } 543 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK); 544 545 int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid) 546 { 547 struct hdac_ext2_link *h2link; 548 struct hdac_ext_link *hlink; 549 550 h2link = find_ext2_link(bus, alt, elid); 551 if (!h2link) 552 return 0; 553 554 if (!h2link->lss) 555 return 0; 556 557 hlink = &h2link->hext_link; 558 559 return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC); 560 } 561 EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK); 562 563 int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) 564 { 565 return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 566 } 567 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK); 568 569 void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) 570 { 571 struct hdac_ext2_link *h2link; 572 struct hdac_ext_link *hlink; 573 574 h2link = find_ext2_link(bus, alt, elid); 575 if (!h2link) 576 return; 577 578 if (!h2link->lss) 579 return; 580 581 hlink = &h2link->hext_link; 582 583 hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink); 584 } 585 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK); 586 587 void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink) 588 { 589 hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); 590 } 591 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK); 592 593 int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) 594 { 595 struct hdac_ext2_link *h2link; 596 struct hdac_ext_link *hlink; 597 598 h2link = find_ext2_link(bus, alt, elid); 599 if (!h2link) 600 return 0; 601 602 if (!h2link->lss) 603 return 0; 604 605 hlink = &h2link->hext_link; 606 607 hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC); 608 609 return 0; 610 } 611 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK); 612 613 int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) 614 { 615 return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 616 } 617 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK); 618 619 bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid) 620 { 621 struct hdac_ext2_link *h2link; 622 struct hdac_ext_link *hlink; 623 u32 cmdsync_mask; 624 625 h2link = find_ext2_link(bus, alt, elid); 626 if (!h2link) 627 return 0; 628 629 if (!h2link->lss) 630 return 0; 631 632 hlink = &h2link->hext_link; 633 634 cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1, 635 AZX_REG_ML_LSYNC_CMDSYNC_SHIFT); 636 637 return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC, 638 cmdsync_mask); 639 } 640 EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK); 641 642 bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus) 643 { 644 return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 645 } 646 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK); 647 648 static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, 649 bool eml_lock) 650 { 651 struct hdac_ext2_link *h2link; 652 struct hdac_ext_link *hlink; 653 int ret = 0; 654 655 h2link = find_ext2_link(bus, alt, elid); 656 if (!h2link) 657 return -ENODEV; 658 659 if (sublink >= h2link->slcount) 660 return -EINVAL; 661 662 hlink = &h2link->hext_link; 663 664 if (eml_lock) 665 mutex_lock(&h2link->eml_lock); 666 667 if (!alt) { 668 if (++hlink->ref_count > 1) 669 goto skip_init; 670 } else { 671 if (++h2link->sublink_ref_count[sublink] > 1) 672 goto skip_init; 673 } 674 675 ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); 676 677 skip_init: 678 if (eml_lock) 679 mutex_unlock(&h2link->eml_lock); 680 681 return ret; 682 } 683 684 int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) 685 { 686 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true); 687 } 688 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK); 689 690 int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) 691 { 692 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false); 693 } 694 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK); 695 696 static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink, 697 bool eml_lock) 698 { 699 struct hdac_ext2_link *h2link; 700 struct hdac_ext_link *hlink; 701 int ret = 0; 702 703 h2link = find_ext2_link(bus, alt, elid); 704 if (!h2link) 705 return -ENODEV; 706 707 if (sublink >= h2link->slcount) 708 return -EINVAL; 709 710 hlink = &h2link->hext_link; 711 712 if (eml_lock) 713 mutex_lock(&h2link->eml_lock); 714 715 if (!alt) { 716 if (--hlink->ref_count > 0) 717 goto skip_shutdown; 718 } else { 719 if (--h2link->sublink_ref_count[sublink] > 0) 720 goto skip_shutdown; 721 } 722 ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); 723 724 skip_shutdown: 725 if (eml_lock) 726 mutex_unlock(&h2link->eml_lock); 727 728 return ret; 729 } 730 731 int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink) 732 { 733 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true); 734 } 735 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK); 736 737 int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) 738 { 739 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false); 740 } 741 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); 742 743 int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink) 744 { 745 return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); 746 } 747 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK); 748 749 int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) 750 { 751 return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); 752 } 753 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); 754 755 int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) 756 { 757 struct hdac_ext2_link *h2link; 758 struct hdac_ext_link *hlink; 759 760 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 761 if (!h2link) 762 return -ENODEV; 763 764 hlink = &h2link->hext_link; 765 766 mutex_lock(&h2link->eml_lock); 767 768 hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num); 769 770 mutex_unlock(&h2link->eml_lock); 771 772 return 0; 773 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK); 774 775 /* 776 * the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the 777 * PDI index, i.e. the FIFO used for RX or TX 778 */ 779 int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, 780 int channel_mask, int stream_id, int dir) 781 { 782 struct hdac_ext2_link *h2link; 783 u16 __iomem *pcmsycm; 784 u16 val; 785 786 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 787 if (!h2link) 788 return -ENODEV; 789 790 pcmsycm = h2link->base_ptr + h2link->shim_offset + 791 h2link->instance_offset * sublink + 792 AZX_REG_SDW_SHIM_PCMSyCM(y); 793 794 mutex_lock(&h2link->eml_lock); 795 796 hdaml_shim_map_stream_ch(pcmsycm, 0, hweight32(channel_mask), 797 stream_id, dir); 798 799 mutex_unlock(&h2link->eml_lock); 800 801 val = readw(pcmsycm); 802 803 dev_dbg(bus->dev, "channel_mask %#x stream_id %d dir %d pcmscm %#x\n", 804 channel_mask, stream_id, dir, val); 805 806 return 0; 807 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, SND_SOC_SOF_HDA_MLINK); 808 809 void hda_bus_ml_put_all(struct hdac_bus *bus) 810 { 811 struct hdac_ext_link *hlink; 812 813 list_for_each_entry(hlink, &bus->hlink_list, list) { 814 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); 815 816 if (!h2link->alt) 817 snd_hdac_ext_bus_link_put(bus, hlink); 818 } 819 } 820 EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK); 821 822 void hda_bus_ml_reset_losidv(struct hdac_bus *bus) 823 { 824 struct hdac_ext_link *hlink; 825 826 /* Reset stream-to-link mapping */ 827 list_for_each_entry(hlink, &bus->hlink_list, list) 828 writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); 829 } 830 EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK); 831 832 int hda_bus_ml_resume(struct hdac_bus *bus) 833 { 834 struct hdac_ext_link *hlink; 835 int ret; 836 837 /* power up links that were active before suspend */ 838 list_for_each_entry(hlink, &bus->hlink_list, list) { 839 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); 840 841 if (!h2link->alt && hlink->ref_count) { 842 ret = snd_hdac_ext_bus_link_power_up(hlink); 843 if (ret < 0) 844 return ret; 845 } 846 } 847 return 0; 848 } 849 EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK); 850 851 int hda_bus_ml_suspend(struct hdac_bus *bus) 852 { 853 struct hdac_ext_link *hlink; 854 int ret; 855 856 list_for_each_entry(hlink, &bus->hlink_list, list) { 857 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); 858 859 if (!h2link->alt) { 860 ret = snd_hdac_ext_bus_link_power_down(hlink); 861 if (ret < 0) 862 return ret; 863 } 864 } 865 return 0; 866 } 867 EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK); 868 869 struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) 870 { 871 struct hdac_ext2_link *h2link; 872 873 h2link = find_ext2_link(bus, alt, elid); 874 if (!h2link) 875 return NULL; 876 877 return &h2link->eml_lock; 878 } 879 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK); 880 881 struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) 882 { 883 struct hdac_ext2_link *h2link; 884 885 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP); 886 if (!h2link) 887 return NULL; 888 889 return &h2link->hext_link; 890 } 891 EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK); 892 893 struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) 894 { 895 struct hdac_ext2_link *h2link; 896 897 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC); 898 if (!h2link) 899 return NULL; 900 901 return &h2link->hext_link; 902 } 903 EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK); 904 905 struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus) 906 { 907 struct hdac_ext2_link *h2link; 908 909 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); 910 if (!h2link) 911 return NULL; 912 913 return &h2link->hext_link; 914 } 915 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, SND_SOC_SOF_HDA_MLINK); 916 917 int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable) 918 { 919 struct hdac_ext2_link *h2link; 920 struct hdac_ext_link *hlink; 921 922 h2link = find_ext2_link(bus, alt, elid); 923 if (!h2link) 924 return -ENODEV; 925 926 if (!h2link->ofls) 927 return 0; 928 929 hlink = &h2link->hext_link; 930 931 mutex_lock(&h2link->eml_lock); 932 933 hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable); 934 935 mutex_unlock(&h2link->eml_lock); 936 937 return 0; 938 } 939 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK); 940 941 #endif 942 943 MODULE_LICENSE("Dual BSD/GPL"); 944