]> Git Repo - linux.git/blob - sound/soc/sof/intel/hda-mlink.c
Merge tag 'amd-drm-next-6.5-2023-06-09' of https://gitlab.freedesktop.org/agd5f/linux...
[linux.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 /**
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");
This page took 0.080833 seconds and 4 git commands to generate.