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 #include <linux/string_choices.h>
20
21 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
22
23 /* worst-case number of sublinks is used for sublink refcount array allocation only */
24 #define HDAML_MAX_SUBLINKS (AZX_ML_LCTL_CPA_SHIFT - AZX_ML_LCTL_SPA_SHIFT)
25
26 /**
27 * struct hdac_ext2_link - HDAudio extended+alternate link
28 *
29 * @hext_link: hdac_ext_link
30 * @alt: flag set for alternate extended links
31 * @intc: boolean for interrupt capable
32 * @ofls: boolean for offload support
33 * @lss: boolean for link synchronization capabilities
34 * @slcount: sublink count
35 * @elid: extended link ID (AZX_REG_ML_LEPTR_ID_ defines)
36 * @elver: extended link version
37 * @leptr: extended link pointer
38 * @eml_lock: mutual exclusion to access shared registers e.g. CPA/SPA bits
39 * in LCTL register
40 * @sublink_ref_count: array of refcounts, required to power-manage sublinks independently
41 * @base_ptr: pointer to shim/ip/shim_vs space
42 * @instance_offset: offset between each of @slcount instances managed by link
43 * @shim_offset: offset to SHIM register base
44 * @ip_offset: offset to IP register base
45 * @shim_vs_offset: offset to vendor-specific (VS) SHIM base
46 * @mic_privacy_mask: bitmask of sublinks where mic privacy is applied
47 */
48 struct hdac_ext2_link {
49 struct hdac_ext_link hext_link;
50
51 /* read directly from LCAP register */
52 bool alt;
53 bool intc;
54 bool ofls;
55 bool lss;
56 int slcount;
57 int elid;
58 int elver;
59 u32 leptr;
60
61 struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
62 int sublink_ref_count[HDAML_MAX_SUBLINKS];
63
64 /* internal values computed from LCAP contents */
65 void __iomem *base_ptr;
66 u32 instance_offset;
67 u32 shim_offset;
68 u32 ip_offset;
69 u32 shim_vs_offset;
70
71 unsigned long mic_privacy_mask;
72 };
73
74 #define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
75
76 #define AZX_REG_SDW_INSTANCE_OFFSET 0x8000
77 #define AZX_REG_SDW_SHIM_OFFSET 0x0
78 #define AZX_REG_SDW_IP_OFFSET 0x100
79 #define AZX_REG_SDW_VS_SHIM_OFFSET 0x6000
80 #define AZX_REG_SDW_SHIM_PCMSyCM(y) (0x16 + 0x4 * (y))
81
82 /* only one instance supported */
83 #define AZX_REG_INTEL_DMIC_SHIM_OFFSET 0x0
84 #define AZX_REG_INTEL_DMIC_IP_OFFSET 0x100
85 #define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET 0x6000
86
87 #define AZX_REG_INTEL_SSP_INSTANCE_OFFSET 0x1000
88 #define AZX_REG_INTEL_SSP_SHIM_OFFSET 0x0
89 #define AZX_REG_INTEL_SSP_IP_OFFSET 0x100
90 #define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET 0xC00
91
92 /* only one instance supported */
93 #define AZX_REG_INTEL_UAOL_SHIM_OFFSET 0x0
94 #define AZX_REG_INTEL_UAOL_IP_OFFSET 0x100
95 #define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET 0xC00
96
97 /* Microphone privacy */
98 #define AZX_REG_INTEL_VS_SHIM_PVCCS 0x10
99 #define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE BIT(0)
100 #define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG BIT(8)
101 #define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS BIT(9)
102 #define AZX_REG_INTEL_VS_SHIM_PVCCS_FMDIS BIT(10)
103
104 /* HDAML section - this part follows sequences in the hardware specification,
105 * including naming conventions and the use of the hdaml_ prefix.
106 * The code is intentionally minimal with limited dependencies on frameworks or
107 * helpers. Locking and scanning lists is handled at a higher level
108 */
109
hdaml_lnk_enum(struct device * dev,struct hdac_ext2_link * h2link,void __iomem * remap_addr,void __iomem * ml_addr,int link_idx)110 static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
111 void __iomem *remap_addr, void __iomem *ml_addr, int link_idx)
112 {
113 struct hdac_ext_link *hlink = &h2link->hext_link;
114 u32 base_offset;
115
116 hlink->lcaps = readl(ml_addr + AZX_REG_ML_LCAP);
117
118 h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);
119
120 /* handle alternate extensions */
121 if (!h2link->alt) {
122 h2link->slcount = 1;
123
124 /*
125 * LSDIID is initialized by hardware for HDaudio link,
126 * it needs to be setup by software for alternate links
127 */
128 hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);
129
130 dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",
131 link_idx, hlink->lsdiid);
132
133 return 0;
134 }
135
136 h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps);
137 h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps);
138 h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps);
139
140 /* read slcount (increment due to zero-based hardware representation */
141 h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
142 dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n",
143 link_idx, h2link->slcount);
144
145 /* find IP ID and offsets */
146 h2link->leptr = readl(ml_addr + AZX_REG_ML_LEPTR);
147
148 h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
149
150 base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
151 h2link->base_ptr = remap_addr + base_offset;
152
153 switch (h2link->elid) {
154 case AZX_REG_ML_LEPTR_ID_SDW:
155 h2link->instance_offset = AZX_REG_SDW_INSTANCE_OFFSET;
156 h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET;
157 h2link->ip_offset = AZX_REG_SDW_IP_OFFSET;
158 h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET;
159 dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n",
160 link_idx, base_offset);
161 break;
162 case AZX_REG_ML_LEPTR_ID_INTEL_DMIC:
163 h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET;
164 h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET;
165 h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET;
166 dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n",
167 link_idx, base_offset);
168 break;
169 case AZX_REG_ML_LEPTR_ID_INTEL_SSP:
170 h2link->instance_offset = AZX_REG_INTEL_SSP_INSTANCE_OFFSET;
171 h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET;
172 h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET;
173 h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET;
174 dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n",
175 link_idx, base_offset);
176 break;
177 case AZX_REG_ML_LEPTR_ID_INTEL_UAOL:
178 h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET;
179 h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET;
180 h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET;
181 dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n",
182 link_idx, base_offset);
183 break;
184 default:
185 dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n",
186 link_idx, h2link->elid);
187 return -EINVAL;
188 }
189 return 0;
190 }
191
192 /*
193 * Hardware recommendations are to wait ~10us before checking any hardware transition
194 * reported by bits changing status.
195 * This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
196 * The worst-case is about 1ms before reporting an issue
197 */
198 #define HDAML_POLL_DELAY_MIN_US 10
199 #define HDAML_POLL_DELAY_SLACK_US 5
200 #define HDAML_POLL_DELAY_RETRY 100
201
check_sublink_power(u32 __iomem * lctl,int sublink,bool enabled)202 static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
203 {
204 int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
205 int retry = HDAML_POLL_DELAY_RETRY;
206 u32 val;
207
208 usleep_range(HDAML_POLL_DELAY_MIN_US,
209 HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
210 do {
211 val = readl(lctl);
212 if (enabled) {
213 if (val & mask)
214 return 0;
215 } else {
216 if (!(val & mask))
217 return 0;
218 }
219 usleep_range(HDAML_POLL_DELAY_MIN_US,
220 HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
221
222 } while (--retry);
223
224 return -EIO;
225 }
226
hdaml_link_init(u32 __iomem * lctl,int sublink)227 static int hdaml_link_init(u32 __iomem *lctl, int sublink)
228 {
229 u32 val;
230 u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
231
232 val = readl(lctl);
233 val |= mask;
234
235 writel(val, lctl);
236
237 return check_sublink_power(lctl, sublink, true);
238 }
239
hdaml_link_shutdown(u32 __iomem * lctl,int sublink)240 static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)
241 {
242 u32 val;
243 u32 mask;
244
245 val = readl(lctl);
246 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
247 val &= ~mask;
248
249 writel(val, lctl);
250
251 return check_sublink_power(lctl, sublink, false);
252 }
253
hdaml_link_enable_interrupt(u32 __iomem * lctl,bool enable)254 static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)
255 {
256 u32 val;
257
258 val = readl(lctl);
259 if (enable)
260 val |= AZX_ML_LCTL_INTEN;
261 else
262 val &= ~AZX_ML_LCTL_INTEN;
263
264 writel(val, lctl);
265 }
266
hdaml_link_check_interrupt(u32 __iomem * lctl)267 static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
268 {
269 u32 val;
270
271 val = readl(lctl);
272
273 return val & AZX_ML_LCTL_INTSTS;
274 }
275
hdaml_wait_bit(void __iomem * base,int offset,u32 mask,u32 target)276 static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
277 {
278 int timeout = HDAML_POLL_DELAY_RETRY;
279 u32 reg_read;
280
281 do {
282 reg_read = readl(base + offset);
283 if ((reg_read & mask) == target)
284 return 0;
285
286 timeout--;
287 usleep_range(HDAML_POLL_DELAY_MIN_US,
288 HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
289 } while (timeout != 0);
290
291 return -EAGAIN;
292 }
293
hdaml_link_set_syncprd(u32 __iomem * lsync,u32 syncprd)294 static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
295 {
296 u32 val;
297
298 val = readl(lsync);
299 val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
300 val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
301
302 /*
303 * set SYNCPU but do not wait. The bit is cleared by hardware when
304 * the link becomes active.
305 */
306 val |= AZX_REG_ML_LSYNC_SYNCPU;
307
308 writel(val, lsync);
309 }
310
hdaml_link_wait_syncpu(u32 __iomem * lsync)311 static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
312 {
313 return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
314 }
315
hdaml_link_sync_arm(u32 __iomem * lsync,int sublink)316 static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)
317 {
318 u32 val;
319
320 val = readl(lsync);
321 val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);
322
323 writel(val, lsync);
324 }
325
hdaml_link_sync_go(u32 __iomem * lsync)326 static void hdaml_link_sync_go(u32 __iomem *lsync)
327 {
328 u32 val;
329
330 val = readl(lsync);
331 val |= AZX_REG_ML_LSYNC_SYNCGO;
332
333 writel(val, lsync);
334 }
335
hdaml_link_check_cmdsync(u32 __iomem * lsync,u32 cmdsync_mask)336 static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)
337 {
338 u32 val;
339
340 val = readl(lsync);
341
342 return !!(val & cmdsync_mask);
343 }
344
hdaml_link_get_lsdiid(u16 __iomem * lsdiid)345 static u16 hdaml_link_get_lsdiid(u16 __iomem *lsdiid)
346 {
347 return readw(lsdiid);
348 }
349
hdaml_link_set_lsdiid(u16 __iomem * lsdiid,int dev_num)350 static void hdaml_link_set_lsdiid(u16 __iomem *lsdiid, int dev_num)
351 {
352 u16 val;
353
354 val = readw(lsdiid);
355 val |= BIT(dev_num);
356
357 writew(val, lsdiid);
358 }
359
hdaml_shim_map_stream_ch(u16 __iomem * pcmsycm,int lchan,int hchan,int stream_id,int dir)360 static void hdaml_shim_map_stream_ch(u16 __iomem *pcmsycm, int lchan, int hchan,
361 int stream_id, int dir)
362 {
363 u16 val;
364
365 val = readw(pcmsycm);
366
367 u16p_replace_bits(&val, lchan, GENMASK(3, 0));
368 u16p_replace_bits(&val, hchan, GENMASK(7, 4));
369 u16p_replace_bits(&val, stream_id, GENMASK(13, 8));
370 u16p_replace_bits(&val, dir, BIT(15));
371
372 writew(val, pcmsycm);
373 }
374
hdaml_lctl_offload_enable(u32 __iomem * lctl,bool enable)375 static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
376 {
377 u32 val = readl(lctl);
378
379 if (enable)
380 val |= AZX_ML_LCTL_OFLEN;
381 else
382 val &= ~AZX_ML_LCTL_OFLEN;
383
384 writel(val, lctl);
385 }
386
387 /* END HDAML section */
388
hda_ml_alloc_h2link(struct hdac_bus * bus,int index)389 static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
390 {
391 struct hdac_ext2_link *h2link;
392 struct hdac_ext_link *hlink;
393 int ret;
394
395 h2link = kzalloc(sizeof(*h2link), GFP_KERNEL);
396 if (!h2link)
397 return -ENOMEM;
398
399 /* basic initialization */
400 hlink = &h2link->hext_link;
401
402 hlink->index = index;
403 hlink->bus = bus;
404 hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
405
406 ret = hdaml_lnk_enum(bus->dev, h2link, bus->remap_addr, hlink->ml_addr, index);
407 if (ret < 0) {
408 kfree(h2link);
409 return ret;
410 }
411
412 mutex_init(&h2link->eml_lock);
413
414 list_add_tail(&hlink->list, &bus->hlink_list);
415
416 /*
417 * HDaudio regular links are powered-on by default, the
418 * refcount needs to be initialized.
419 */
420 if (!h2link->alt)
421 hlink->ref_count = 1;
422
423 return 0;
424 }
425
hda_bus_ml_init(struct hdac_bus * bus)426 int hda_bus_ml_init(struct hdac_bus *bus)
427 {
428 u32 link_count;
429 int ret;
430 int i;
431
432 if (!bus->mlcap)
433 return 0;
434
435 link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
436
437 dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
438
439 for (i = 0; i < link_count; i++) {
440 ret = hda_ml_alloc_h2link(bus, i);
441 if (ret < 0) {
442 hda_bus_ml_free(bus);
443 return ret;
444 }
445 }
446 return 0;
447 }
448 EXPORT_SYMBOL_NS(hda_bus_ml_init, "SND_SOC_SOF_HDA_MLINK");
449
hda_bus_ml_free(struct hdac_bus * bus)450 void hda_bus_ml_free(struct hdac_bus *bus)
451 {
452 struct hdac_ext_link *hlink, *_h;
453 struct hdac_ext2_link *h2link;
454
455 if (!bus->mlcap)
456 return;
457
458 list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) {
459 list_del(&hlink->list);
460 h2link = hdac_ext_link_to_ext2(hlink);
461
462 mutex_destroy(&h2link->eml_lock);
463 kfree(h2link);
464 }
465 }
466 EXPORT_SYMBOL_NS(hda_bus_ml_free, "SND_SOC_SOF_HDA_MLINK");
467
468 static struct hdac_ext2_link *
find_ext2_link(struct hdac_bus * bus,bool alt,int elid)469 find_ext2_link(struct hdac_bus *bus, bool alt, int elid)
470 {
471 struct hdac_ext_link *hlink;
472
473 list_for_each_entry(hlink, &bus->hlink_list, list) {
474 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
475
476 if (h2link->alt == alt && h2link->elid == elid)
477 return h2link;
478 }
479
480 return NULL;
481 }
482
hdac_bus_eml_get_count(struct hdac_bus * bus,bool alt,int elid)483 int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
484 {
485 struct hdac_ext2_link *h2link;
486
487 h2link = find_ext2_link(bus, alt, elid);
488 if (!h2link)
489 return 0;
490
491 return h2link->slcount;
492 }
493 EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, "SND_SOC_SOF_HDA_MLINK");
494
hdac_bus_eml_enable_interrupt_unlocked(struct hdac_bus * bus,bool alt,int elid,bool enable)495 void hdac_bus_eml_enable_interrupt_unlocked(struct hdac_bus *bus, bool alt, int elid, bool enable)
496 {
497 struct hdac_ext2_link *h2link;
498 struct hdac_ext_link *hlink;
499
500 h2link = find_ext2_link(bus, alt, elid);
501 if (!h2link)
502 return;
503
504 if (!h2link->intc)
505 return;
506
507 hlink = &h2link->hext_link;
508
509 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
510 }
511 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt_unlocked, "SND_SOC_SOF_HDA_MLINK");
512
hdac_bus_eml_enable_interrupt(struct hdac_bus * bus,bool alt,int elid,bool enable)513 void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)
514 {
515 struct hdac_ext2_link *h2link;
516 struct hdac_ext_link *hlink;
517
518 h2link = find_ext2_link(bus, alt, elid);
519 if (!h2link)
520 return;
521
522 if (!h2link->intc)
523 return;
524
525 hlink = &h2link->hext_link;
526
527 mutex_lock(&h2link->eml_lock);
528
529 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
530
531 mutex_unlock(&h2link->eml_lock);
532 }
533 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, "SND_SOC_SOF_HDA_MLINK");
534
hdac_bus_eml_check_interrupt(struct hdac_bus * bus,bool alt,int elid)535 bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
536 {
537 struct hdac_ext2_link *h2link;
538 struct hdac_ext_link *hlink;
539
540 h2link = find_ext2_link(bus, alt, elid);
541 if (!h2link)
542 return false;
543
544 if (!h2link->intc)
545 return false;
546
547 hlink = &h2link->hext_link;
548
549 return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
550 }
551 EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, "SND_SOC_SOF_HDA_MLINK");
552
hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus * bus,bool alt,int elid,u32 syncprd)553 int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
554 {
555 struct hdac_ext2_link *h2link;
556 struct hdac_ext_link *hlink;
557
558 h2link = find_ext2_link(bus, alt, elid);
559 if (!h2link)
560 return 0;
561
562 if (!h2link->lss)
563 return 0;
564
565 hlink = &h2link->hext_link;
566
567 hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
568
569 return 0;
570 }
571 EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK");
572
hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus * bus,u32 syncprd)573 int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
574 {
575 return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
576 }
577 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK");
578
hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus * bus,bool alt,int elid)579 int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
580 {
581 struct hdac_ext2_link *h2link;
582 struct hdac_ext_link *hlink;
583
584 h2link = find_ext2_link(bus, alt, elid);
585 if (!h2link)
586 return 0;
587
588 if (!h2link->lss)
589 return 0;
590
591 hlink = &h2link->hext_link;
592
593 return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
594 }
595 EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK");
596
hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus * bus)597 int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
598 {
599 return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
600 }
601 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK");
602
hdac_bus_eml_sync_arm_unlocked(struct hdac_bus * bus,bool alt,int elid,int sublink)603 void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
604 {
605 struct hdac_ext2_link *h2link;
606 struct hdac_ext_link *hlink;
607
608 h2link = find_ext2_link(bus, alt, elid);
609 if (!h2link)
610 return;
611
612 if (!h2link->lss)
613 return;
614
615 hlink = &h2link->hext_link;
616
617 hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
618 }
619 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK");
620
hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus * bus,int sublink)621 void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
622 {
623 hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
624 }
625 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK");
626
hdac_bus_eml_sync_go_unlocked(struct hdac_bus * bus,bool alt,int elid)627 int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
628 {
629 struct hdac_ext2_link *h2link;
630 struct hdac_ext_link *hlink;
631
632 h2link = find_ext2_link(bus, alt, elid);
633 if (!h2link)
634 return 0;
635
636 if (!h2link->lss)
637 return 0;
638
639 hlink = &h2link->hext_link;
640
641 hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
642
643 return 0;
644 }
645 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK");
646
hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus * bus)647 int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
648 {
649 return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
650 }
651 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK");
652
hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus * bus,bool alt,int elid)653 bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
654 {
655 struct hdac_ext2_link *h2link;
656 struct hdac_ext_link *hlink;
657 u32 cmdsync_mask;
658
659 h2link = find_ext2_link(bus, alt, elid);
660 if (!h2link)
661 return 0;
662
663 if (!h2link->lss)
664 return 0;
665
666 hlink = &h2link->hext_link;
667
668 cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
669 AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
670
671 return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
672 cmdsync_mask);
673 }
674 EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK");
675
hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus * bus)676 bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
677 {
678 return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
679 }
680 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK");
681
hdac_bus_eml_power_up_base(struct hdac_bus * bus,bool alt,int elid,int sublink,bool eml_lock)682 static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
683 bool eml_lock)
684 {
685 struct hdac_ext2_link *h2link;
686 struct hdac_ext_link *hlink;
687 int ret = 0;
688
689 h2link = find_ext2_link(bus, alt, elid);
690 if (!h2link)
691 return -ENODEV;
692
693 if (sublink >= h2link->slcount)
694 return -EINVAL;
695
696 hlink = &h2link->hext_link;
697
698 if (eml_lock)
699 mutex_lock(&h2link->eml_lock);
700
701 if (!alt) {
702 if (++hlink->ref_count > 1)
703 goto skip_init;
704 } else {
705 if (++h2link->sublink_ref_count[sublink] > 1)
706 goto skip_init;
707 }
708
709 ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
710 if ((h2link->mic_privacy_mask & BIT(sublink)) && !ret) {
711 u16 __iomem *pvccs = h2link->base_ptr +
712 h2link->shim_vs_offset +
713 sublink * h2link->instance_offset +
714 AZX_REG_INTEL_VS_SHIM_PVCCS;
715 u16 val = readw(pvccs);
716
717 writew(val | AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
718
719 if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS)
720 dev_dbg(bus->dev,
721 "sublink %d (%d:%d): Mic privacy is enabled\n",
722 sublink, alt, elid);
723 }
724
725 skip_init:
726 if (eml_lock)
727 mutex_unlock(&h2link->eml_lock);
728
729 return ret;
730 }
731
hdac_bus_eml_power_up(struct hdac_bus * bus,bool alt,int elid,int sublink)732 int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
733 {
734 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
735 }
736 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, "SND_SOC_SOF_HDA_MLINK");
737
hdac_bus_eml_power_up_unlocked(struct hdac_bus * bus,bool alt,int elid,int sublink)738 int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
739 {
740 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
741 }
742 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK");
743
hdac_bus_eml_power_down_base(struct hdac_bus * bus,bool alt,int elid,int sublink,bool eml_lock)744 static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
745 bool eml_lock)
746 {
747 struct hdac_ext2_link *h2link;
748 struct hdac_ext_link *hlink;
749 int ret = 0;
750
751 h2link = find_ext2_link(bus, alt, elid);
752 if (!h2link)
753 return -ENODEV;
754
755 if (sublink >= h2link->slcount)
756 return -EINVAL;
757
758 hlink = &h2link->hext_link;
759
760 if (eml_lock)
761 mutex_lock(&h2link->eml_lock);
762
763 if (!alt) {
764 if (--hlink->ref_count > 0)
765 goto skip_shutdown;
766 } else {
767 if (--h2link->sublink_ref_count[sublink] > 0)
768 goto skip_shutdown;
769 }
770
771 if (h2link->mic_privacy_mask & BIT(sublink)) {
772 u16 __iomem *pvccs = h2link->base_ptr +
773 h2link->shim_vs_offset +
774 sublink * h2link->instance_offset +
775 AZX_REG_INTEL_VS_SHIM_PVCCS;
776
777 writew(readw(pvccs) & ~AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
778 }
779
780 ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
781
782 skip_shutdown:
783 if (eml_lock)
784 mutex_unlock(&h2link->eml_lock);
785
786 return ret;
787 }
788
hdac_bus_eml_power_down(struct hdac_bus * bus,bool alt,int elid,int sublink)789 int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
790 {
791 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
792 }
793 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, "SND_SOC_SOF_HDA_MLINK");
794
hdac_bus_eml_power_down_unlocked(struct hdac_bus * bus,bool alt,int elid,int sublink)795 int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
796 {
797 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
798 }
799 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK");
800
hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus * bus,int sublink)801 int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
802 {
803 return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
804 }
805 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK");
806
hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus * bus,int sublink)807 int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
808 {
809 return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
810 }
811 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK");
812
hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus * bus,int sublink,u16 * lsdiid)813 int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid)
814 {
815 struct hdac_ext2_link *h2link;
816 struct hdac_ext_link *hlink;
817
818 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
819 if (!h2link)
820 return -ENODEV;
821
822 hlink = &h2link->hext_link;
823
824 *lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink));
825
826 return 0;
827 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_lsdiid_unlocked, "SND_SOC_SOF_HDA_MLINK");
828
hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus * bus,int sublink,int dev_num)829 int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
830 {
831 struct hdac_ext2_link *h2link;
832 struct hdac_ext_link *hlink;
833
834 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
835 if (!h2link)
836 return -ENODEV;
837
838 hlink = &h2link->hext_link;
839
840 mutex_lock(&h2link->eml_lock);
841
842 hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
843
844 mutex_unlock(&h2link->eml_lock);
845
846 return 0;
847 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, "SND_SOC_SOF_HDA_MLINK");
848
849 /*
850 * the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the
851 * PDI index, i.e. the FIFO used for RX or TX
852 */
hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus * bus,int sublink,int y,int channel_mask,int stream_id,int dir)853 int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
854 int channel_mask, int stream_id, int dir)
855 {
856 struct hdac_ext2_link *h2link;
857 u16 __iomem *pcmsycm;
858 int hchan;
859 int lchan;
860 u16 val;
861
862 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
863 if (!h2link)
864 return -ENODEV;
865
866 pcmsycm = h2link->base_ptr + h2link->shim_offset +
867 h2link->instance_offset * sublink +
868 AZX_REG_SDW_SHIM_PCMSyCM(y);
869
870 if (channel_mask) {
871 hchan = __fls(channel_mask);
872 lchan = __ffs(channel_mask);
873 } else {
874 hchan = 0;
875 lchan = 0;
876 }
877
878 mutex_lock(&h2link->eml_lock);
879
880 hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan,
881 stream_id, dir);
882
883 mutex_unlock(&h2link->eml_lock);
884
885 val = readw(pcmsycm);
886
887 dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n",
888 sublink, channel_mask, stream_id, dir, val);
889
890 return 0;
891 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK");
892
hda_bus_ml_put_all(struct hdac_bus * bus)893 void hda_bus_ml_put_all(struct hdac_bus *bus)
894 {
895 struct hdac_ext_link *hlink;
896
897 list_for_each_entry(hlink, &bus->hlink_list, list) {
898 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
899
900 if (!h2link->alt)
901 snd_hdac_ext_bus_link_put(bus, hlink);
902 }
903 }
904 EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK");
905
hda_bus_ml_reset_losidv(struct hdac_bus * bus)906 void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
907 {
908 struct hdac_ext_link *hlink;
909
910 /* Reset stream-to-link mapping */
911 list_for_each_entry(hlink, &bus->hlink_list, list)
912 writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
913 }
914 EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK");
915
hda_bus_ml_resume(struct hdac_bus * bus)916 int hda_bus_ml_resume(struct hdac_bus *bus)
917 {
918 struct hdac_ext_link *hlink;
919 int ret;
920
921 /* power up links that were active before suspend */
922 list_for_each_entry(hlink, &bus->hlink_list, list) {
923 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
924
925 if (!h2link->alt && hlink->ref_count) {
926 ret = snd_hdac_ext_bus_link_power_up(hlink);
927 if (ret < 0)
928 return ret;
929 }
930 }
931 return 0;
932 }
933 EXPORT_SYMBOL_NS(hda_bus_ml_resume, "SND_SOC_SOF_HDA_MLINK");
934
hda_bus_ml_suspend(struct hdac_bus * bus)935 int hda_bus_ml_suspend(struct hdac_bus *bus)
936 {
937 struct hdac_ext_link *hlink;
938 int ret;
939
940 list_for_each_entry(hlink, &bus->hlink_list, list) {
941 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
942
943 if (!h2link->alt) {
944 ret = snd_hdac_ext_bus_link_power_down(hlink);
945 if (ret < 0)
946 return ret;
947 }
948 }
949 return 0;
950 }
951 EXPORT_SYMBOL_NS(hda_bus_ml_suspend, "SND_SOC_SOF_HDA_MLINK");
952
hdac_bus_eml_get_mutex(struct hdac_bus * bus,bool alt,int elid)953 struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
954 {
955 struct hdac_ext2_link *h2link;
956
957 h2link = find_ext2_link(bus, alt, elid);
958 if (!h2link)
959 return NULL;
960
961 return &h2link->eml_lock;
962 }
963 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, "SND_SOC_SOF_HDA_MLINK");
964
hdac_bus_eml_ssp_get_hlink(struct hdac_bus * bus)965 struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
966 {
967 struct hdac_ext2_link *h2link;
968
969 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
970 if (!h2link)
971 return NULL;
972
973 return &h2link->hext_link;
974 }
975 EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, "SND_SOC_SOF_HDA_MLINK");
976
hdac_bus_eml_dmic_get_hlink(struct hdac_bus * bus)977 struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
978 {
979 struct hdac_ext2_link *h2link;
980
981 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
982 if (!h2link)
983 return NULL;
984
985 return &h2link->hext_link;
986 }
987 EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, "SND_SOC_SOF_HDA_MLINK");
988
hdac_bus_eml_sdw_get_hlink(struct hdac_bus * bus)989 struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus)
990 {
991 struct hdac_ext2_link *h2link;
992
993 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
994 if (!h2link)
995 return NULL;
996
997 return &h2link->hext_link;
998 }
999 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, "SND_SOC_SOF_HDA_MLINK");
1000
hdac_bus_eml_enable_offload(struct hdac_bus * bus,bool alt,int elid,bool enable)1001 int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
1002 {
1003 struct hdac_ext2_link *h2link;
1004 struct hdac_ext_link *hlink;
1005
1006 h2link = find_ext2_link(bus, alt, elid);
1007 if (!h2link)
1008 return -ENODEV;
1009
1010 if (!h2link->ofls)
1011 return 0;
1012
1013 hlink = &h2link->hext_link;
1014
1015 mutex_lock(&h2link->eml_lock);
1016
1017 hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
1018
1019 mutex_unlock(&h2link->eml_lock);
1020
1021 return 0;
1022 }
1023 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, "SND_SOC_SOF_HDA_MLINK");
1024
hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus * bus,bool alt,int elid,unsigned long mask)1025 void hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid,
1026 unsigned long mask)
1027 {
1028 struct hdac_ext2_link *h2link;
1029
1030 if (!mask)
1031 return;
1032
1033 h2link = find_ext2_link(bus, alt, elid);
1034 if (!h2link)
1035 return;
1036
1037 if (__fls(mask) > h2link->slcount) {
1038 dev_warn(bus->dev,
1039 "%s: invalid sublink mask for %d:%d, slcount %d: %#lx\n",
1040 __func__, alt, elid, h2link->slcount, mask);
1041 return;
1042 }
1043
1044 dev_dbg(bus->dev, "sublink mask for %d:%d, slcount %d: %#lx\n", alt,
1045 elid, h2link->slcount, mask);
1046
1047 h2link->mic_privacy_mask = mask;
1048 }
1049 EXPORT_SYMBOL_NS(hdac_bus_eml_set_mic_privacy_mask, "SND_SOC_SOF_HDA_MLINK");
1050
hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus * bus,bool alt,int elid)1051 bool hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid)
1052 {
1053 struct hdac_ext2_link *h2link;
1054 bool changed = false;
1055 u16 __iomem *pvccs;
1056 int i;
1057
1058 h2link = find_ext2_link(bus, alt, elid);
1059 if (!h2link)
1060 return false;
1061
1062 /* The change in privacy state needs to be acked for each link */
1063 for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
1064 u16 val;
1065
1066 if (h2link->sublink_ref_count[i] == 0)
1067 continue;
1068
1069 pvccs = h2link->base_ptr +
1070 h2link->shim_vs_offset +
1071 i * h2link->instance_offset +
1072 AZX_REG_INTEL_VS_SHIM_PVCCS;
1073
1074 val = readw(pvccs);
1075 if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG) {
1076 writew(val, pvccs);
1077 changed = true;
1078 }
1079 }
1080
1081 return changed;
1082 }
1083 EXPORT_SYMBOL_NS(hdac_bus_eml_is_mic_privacy_changed, "SND_SOC_SOF_HDA_MLINK");
1084
hdac_bus_eml_get_mic_privacy_state(struct hdac_bus * bus,bool alt,int elid)1085 bool hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid)
1086 {
1087 struct hdac_ext2_link *h2link;
1088 u16 __iomem *pvccs;
1089 bool state;
1090 int i;
1091
1092 h2link = find_ext2_link(bus, alt, elid);
1093 if (!h2link)
1094 return false;
1095
1096 for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
1097 if (h2link->sublink_ref_count[i] == 0)
1098 continue;
1099
1100 /* Return the privacy state from the first active link */
1101 pvccs = h2link->base_ptr +
1102 h2link->shim_vs_offset +
1103 i * h2link->instance_offset +
1104 AZX_REG_INTEL_VS_SHIM_PVCCS;
1105
1106 state = readw(pvccs) & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS;
1107 dev_dbg(bus->dev, "alt: %d, elid: %d: Mic privacy is %s\n", alt,
1108 elid, str_enabled_disabled(state));
1109
1110 return state;
1111 }
1112
1113 return false;
1114 }
1115 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mic_privacy_state, "SND_SOC_SOF_HDA_MLINK");
1116
1117 #endif
1118
1119 MODULE_LICENSE("Dual BSD/GPL");
1120 MODULE_DESCRIPTION("SOF support for HDaudio multi-link");
1121