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