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_obj(*h2link);
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 scoped_guard(mutex, &h2link->eml_lock)
528 hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
529 }
530 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, "SND_SOC_SOF_HDA_MLINK");
531
hdac_bus_eml_check_interrupt(struct hdac_bus * bus,bool alt,int elid)532 bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
533 {
534 struct hdac_ext2_link *h2link;
535 struct hdac_ext_link *hlink;
536
537 h2link = find_ext2_link(bus, alt, elid);
538 if (!h2link)
539 return false;
540
541 if (!h2link->intc)
542 return false;
543
544 hlink = &h2link->hext_link;
545
546 return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
547 }
548 EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, "SND_SOC_SOF_HDA_MLINK");
549
hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus * bus,bool alt,int elid,u32 syncprd)550 int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
551 {
552 struct hdac_ext2_link *h2link;
553 struct hdac_ext_link *hlink;
554
555 h2link = find_ext2_link(bus, alt, elid);
556 if (!h2link)
557 return 0;
558
559 if (!h2link->lss)
560 return 0;
561
562 hlink = &h2link->hext_link;
563
564 hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
565
566 return 0;
567 }
568 EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK");
569
hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus * bus,u32 syncprd)570 int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
571 {
572 return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
573 }
574 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, "SND_SOC_SOF_HDA_MLINK");
575
hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus * bus,bool alt,int elid)576 int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
577 {
578 struct hdac_ext2_link *h2link;
579 struct hdac_ext_link *hlink;
580
581 h2link = find_ext2_link(bus, alt, elid);
582 if (!h2link)
583 return 0;
584
585 if (!h2link->lss)
586 return 0;
587
588 hlink = &h2link->hext_link;
589
590 return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
591 }
592 EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK");
593
hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus * bus)594 int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
595 {
596 return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
597 }
598 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, "SND_SOC_SOF_HDA_MLINK");
599
hdac_bus_eml_sync_arm_unlocked(struct hdac_bus * bus,bool alt,int elid,int sublink)600 void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
601 {
602 struct hdac_ext2_link *h2link;
603 struct hdac_ext_link *hlink;
604
605 h2link = find_ext2_link(bus, alt, elid);
606 if (!h2link)
607 return;
608
609 if (!h2link->lss)
610 return;
611
612 hlink = &h2link->hext_link;
613
614 hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
615 }
616 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK");
617
hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus * bus,int sublink)618 void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
619 {
620 hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
621 }
622 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, "SND_SOC_SOF_HDA_MLINK");
623
hdac_bus_eml_sync_go_unlocked(struct hdac_bus * bus,bool alt,int elid)624 int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
625 {
626 struct hdac_ext2_link *h2link;
627 struct hdac_ext_link *hlink;
628
629 h2link = find_ext2_link(bus, alt, elid);
630 if (!h2link)
631 return 0;
632
633 if (!h2link->lss)
634 return 0;
635
636 hlink = &h2link->hext_link;
637
638 hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
639
640 return 0;
641 }
642 EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK");
643
hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus * bus)644 int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
645 {
646 return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
647 }
648 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, "SND_SOC_SOF_HDA_MLINK");
649
hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus * bus,bool alt,int elid)650 bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
651 {
652 struct hdac_ext2_link *h2link;
653 struct hdac_ext_link *hlink;
654 u32 cmdsync_mask;
655
656 h2link = find_ext2_link(bus, alt, elid);
657 if (!h2link)
658 return 0;
659
660 if (!h2link->lss)
661 return 0;
662
663 hlink = &h2link->hext_link;
664
665 cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
666 AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
667
668 return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
669 cmdsync_mask);
670 }
671 EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK");
672
hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus * bus)673 bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
674 {
675 return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
676 }
677 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, "SND_SOC_SOF_HDA_MLINK");
678
hdac_bus_eml_power_up_base(struct hdac_bus * bus,bool alt,int elid,int sublink,bool eml_lock)679 static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
680 bool eml_lock)
681 {
682 struct hdac_ext2_link *h2link;
683 struct hdac_ext_link *hlink;
684 int ret = 0;
685
686 h2link = find_ext2_link(bus, alt, elid);
687 if (!h2link)
688 return -ENODEV;
689
690 if (sublink >= h2link->slcount)
691 return -EINVAL;
692
693 hlink = &h2link->hext_link;
694
695 if (eml_lock)
696 mutex_lock(&h2link->eml_lock);
697
698 if (!alt) {
699 if (++hlink->ref_count > 1)
700 goto skip_init;
701 } else {
702 if (++h2link->sublink_ref_count[sublink] > 1)
703 goto skip_init;
704 }
705
706 ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
707 if ((h2link->mic_privacy_mask & BIT(sublink)) && !ret) {
708 u16 __iomem *pvccs = h2link->base_ptr +
709 h2link->shim_vs_offset +
710 sublink * h2link->instance_offset +
711 AZX_REG_INTEL_VS_SHIM_PVCCS;
712 u16 val = readw(pvccs);
713
714 writew(val | AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
715
716 if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS)
717 dev_dbg(bus->dev,
718 "sublink %d (%d:%d): Mic privacy is enabled\n",
719 sublink, alt, elid);
720 }
721
722 skip_init:
723 if (eml_lock)
724 mutex_unlock(&h2link->eml_lock);
725
726 return ret;
727 }
728
hdac_bus_eml_power_up(struct hdac_bus * bus,bool alt,int elid,int sublink)729 int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
730 {
731 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
732 }
733 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, "SND_SOC_SOF_HDA_MLINK");
734
hdac_bus_eml_power_up_unlocked(struct hdac_bus * bus,bool alt,int elid,int sublink)735 int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
736 {
737 return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
738 }
739 EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK");
740
hdac_bus_eml_power_down_base(struct hdac_bus * bus,bool alt,int elid,int sublink,bool eml_lock)741 static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
742 bool eml_lock)
743 {
744 struct hdac_ext2_link *h2link;
745 struct hdac_ext_link *hlink;
746 int ret = 0;
747
748 h2link = find_ext2_link(bus, alt, elid);
749 if (!h2link)
750 return -ENODEV;
751
752 if (sublink >= h2link->slcount)
753 return -EINVAL;
754
755 hlink = &h2link->hext_link;
756
757 if (eml_lock)
758 mutex_lock(&h2link->eml_lock);
759
760 if (!alt) {
761 if (--hlink->ref_count > 0)
762 goto skip_shutdown;
763 } else {
764 if (--h2link->sublink_ref_count[sublink] > 0)
765 goto skip_shutdown;
766 }
767
768 if (h2link->mic_privacy_mask & BIT(sublink)) {
769 u16 __iomem *pvccs = h2link->base_ptr +
770 h2link->shim_vs_offset +
771 sublink * h2link->instance_offset +
772 AZX_REG_INTEL_VS_SHIM_PVCCS;
773
774 writew(readw(pvccs) & ~AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
775 }
776
777 ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
778
779 skip_shutdown:
780 if (eml_lock)
781 mutex_unlock(&h2link->eml_lock);
782
783 return ret;
784 }
785
hdac_bus_eml_power_down(struct hdac_bus * bus,bool alt,int elid,int sublink)786 int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
787 {
788 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
789 }
790 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, "SND_SOC_SOF_HDA_MLINK");
791
hdac_bus_eml_power_down_unlocked(struct hdac_bus * bus,bool alt,int elid,int sublink)792 int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
793 {
794 return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
795 }
796 EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK");
797
hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus * bus,int sublink)798 int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
799 {
800 return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
801 }
802 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, "SND_SOC_SOF_HDA_MLINK");
803
hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus * bus,int sublink)804 int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
805 {
806 return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
807 }
808 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, "SND_SOC_SOF_HDA_MLINK");
809
hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus * bus,int sublink,u16 * lsdiid)810 int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid)
811 {
812 struct hdac_ext2_link *h2link;
813 struct hdac_ext_link *hlink;
814
815 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
816 if (!h2link)
817 return -ENODEV;
818
819 hlink = &h2link->hext_link;
820
821 *lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink));
822
823 return 0;
824 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_lsdiid_unlocked, "SND_SOC_SOF_HDA_MLINK");
825
hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus * bus,int sublink,int dev_num)826 int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
827 {
828 struct hdac_ext2_link *h2link;
829 struct hdac_ext_link *hlink;
830
831 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
832 if (!h2link)
833 return -ENODEV;
834
835 hlink = &h2link->hext_link;
836
837 scoped_guard(mutex, &h2link->eml_lock)
838 hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
839
840 return 0;
841 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, "SND_SOC_SOF_HDA_MLINK");
842
843 /*
844 * the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the
845 * PDI index, i.e. the FIFO used for RX or TX
846 */
hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus * bus,int sublink,int y,int channel_mask,int stream_id,int dir)847 int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
848 int channel_mask, int stream_id, int dir)
849 {
850 struct hdac_ext2_link *h2link;
851 u16 __iomem *pcmsycm;
852 int hchan;
853 int lchan;
854 u16 val;
855
856 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
857 if (!h2link)
858 return -ENODEV;
859
860 pcmsycm = h2link->base_ptr + h2link->shim_offset +
861 h2link->instance_offset * sublink +
862 AZX_REG_SDW_SHIM_PCMSyCM(y);
863
864 if (channel_mask) {
865 hchan = __fls(channel_mask);
866 lchan = __ffs(channel_mask);
867 } else {
868 hchan = 0;
869 lchan = 0;
870 }
871
872 scoped_guard(mutex, &h2link->eml_lock)
873 hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan, stream_id, dir);
874
875 val = readw(pcmsycm);
876
877 dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n",
878 sublink, channel_mask, stream_id, dir, val);
879
880 return 0;
881 } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK");
882
hda_bus_ml_put_all(struct hdac_bus * bus)883 void hda_bus_ml_put_all(struct hdac_bus *bus)
884 {
885 struct hdac_ext_link *hlink;
886
887 list_for_each_entry(hlink, &bus->hlink_list, list) {
888 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
889
890 if (!h2link->alt)
891 snd_hdac_ext_bus_link_put(bus, hlink);
892 }
893 }
894 EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK");
895
hda_bus_ml_reset_losidv(struct hdac_bus * bus)896 void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
897 {
898 struct hdac_ext_link *hlink;
899
900 /* Reset stream-to-link mapping */
901 list_for_each_entry(hlink, &bus->hlink_list, list)
902 writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
903 }
904 EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK");
905
hda_bus_ml_resume(struct hdac_bus * bus)906 int hda_bus_ml_resume(struct hdac_bus *bus)
907 {
908 struct hdac_ext_link *hlink;
909 int ret;
910
911 /* power up links that were active before suspend */
912 list_for_each_entry(hlink, &bus->hlink_list, list) {
913 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
914
915 if (!h2link->alt && hlink->ref_count) {
916 ret = snd_hdac_ext_bus_link_power_up(hlink);
917 if (ret < 0)
918 return ret;
919 }
920 }
921 return 0;
922 }
923 EXPORT_SYMBOL_NS(hda_bus_ml_resume, "SND_SOC_SOF_HDA_MLINK");
924
hda_bus_ml_suspend(struct hdac_bus * bus)925 int hda_bus_ml_suspend(struct hdac_bus *bus)
926 {
927 struct hdac_ext_link *hlink;
928 int ret;
929
930 list_for_each_entry(hlink, &bus->hlink_list, list) {
931 struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
932
933 if (!h2link->alt) {
934 ret = snd_hdac_ext_bus_link_power_down(hlink);
935 if (ret < 0)
936 return ret;
937 }
938 }
939 return 0;
940 }
941 EXPORT_SYMBOL_NS(hda_bus_ml_suspend, "SND_SOC_SOF_HDA_MLINK");
942
hdac_bus_eml_get_mutex(struct hdac_bus * bus,bool alt,int elid)943 struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
944 {
945 struct hdac_ext2_link *h2link;
946
947 h2link = find_ext2_link(bus, alt, elid);
948 if (!h2link)
949 return NULL;
950
951 return &h2link->eml_lock;
952 }
953 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, "SND_SOC_SOF_HDA_MLINK");
954
hdac_bus_eml_ssp_get_hlink(struct hdac_bus * bus)955 struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
956 {
957 struct hdac_ext2_link *h2link;
958
959 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
960 if (!h2link)
961 return NULL;
962
963 return &h2link->hext_link;
964 }
965 EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, "SND_SOC_SOF_HDA_MLINK");
966
hdac_bus_eml_dmic_get_hlink(struct hdac_bus * bus)967 struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
968 {
969 struct hdac_ext2_link *h2link;
970
971 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
972 if (!h2link)
973 return NULL;
974
975 return &h2link->hext_link;
976 }
977 EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, "SND_SOC_SOF_HDA_MLINK");
978
hdac_bus_eml_sdw_get_hlink(struct hdac_bus * bus)979 struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus)
980 {
981 struct hdac_ext2_link *h2link;
982
983 h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
984 if (!h2link)
985 return NULL;
986
987 return &h2link->hext_link;
988 }
989 EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, "SND_SOC_SOF_HDA_MLINK");
990
hdac_bus_eml_enable_offload(struct hdac_bus * bus,bool alt,int elid,bool enable)991 int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
992 {
993 struct hdac_ext2_link *h2link;
994 struct hdac_ext_link *hlink;
995
996 h2link = find_ext2_link(bus, alt, elid);
997 if (!h2link)
998 return -ENODEV;
999
1000 if (!h2link->ofls)
1001 return 0;
1002
1003 hlink = &h2link->hext_link;
1004
1005 scoped_guard(mutex, &h2link->eml_lock)
1006 hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
1007
1008 return 0;
1009 }
1010 EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, "SND_SOC_SOF_HDA_MLINK");
1011
hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus * bus,bool alt,int elid,unsigned long mask)1012 void hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid,
1013 unsigned long mask)
1014 {
1015 struct hdac_ext2_link *h2link;
1016
1017 if (!mask)
1018 return;
1019
1020 h2link = find_ext2_link(bus, alt, elid);
1021 if (!h2link)
1022 return;
1023
1024 if (__fls(mask) > h2link->slcount) {
1025 dev_warn(bus->dev,
1026 "%s: invalid sublink mask for %d:%d, slcount %d: %#lx\n",
1027 __func__, alt, elid, h2link->slcount, mask);
1028 return;
1029 }
1030
1031 dev_dbg(bus->dev, "sublink mask for %d:%d, slcount %d: %#lx\n", alt,
1032 elid, h2link->slcount, mask);
1033
1034 h2link->mic_privacy_mask = mask;
1035 }
1036 EXPORT_SYMBOL_NS(hdac_bus_eml_set_mic_privacy_mask, "SND_SOC_SOF_HDA_MLINK");
1037
hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus * bus,bool alt,int elid)1038 bool hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid)
1039 {
1040 struct hdac_ext2_link *h2link;
1041 bool changed = false;
1042 u16 __iomem *pvccs;
1043 int i;
1044
1045 h2link = find_ext2_link(bus, alt, elid);
1046 if (!h2link)
1047 return false;
1048
1049 /* The change in privacy state needs to be acked for each link */
1050 for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
1051 u16 val;
1052
1053 if (h2link->sublink_ref_count[i] == 0)
1054 continue;
1055
1056 pvccs = h2link->base_ptr +
1057 h2link->shim_vs_offset +
1058 i * h2link->instance_offset +
1059 AZX_REG_INTEL_VS_SHIM_PVCCS;
1060
1061 val = readw(pvccs);
1062 if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG) {
1063 writew(val, pvccs);
1064 changed = true;
1065 }
1066 }
1067
1068 return changed;
1069 }
1070 EXPORT_SYMBOL_NS(hdac_bus_eml_is_mic_privacy_changed, "SND_SOC_SOF_HDA_MLINK");
1071
hdac_bus_eml_get_mic_privacy_state(struct hdac_bus * bus,bool alt,int elid)1072 bool hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid)
1073 {
1074 struct hdac_ext2_link *h2link;
1075 u16 __iomem *pvccs;
1076 bool state;
1077 int i;
1078
1079 h2link = find_ext2_link(bus, alt, elid);
1080 if (!h2link)
1081 return false;
1082
1083 for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
1084 if (h2link->sublink_ref_count[i] == 0)
1085 continue;
1086
1087 /* Return the privacy state from the first active link */
1088 pvccs = h2link->base_ptr +
1089 h2link->shim_vs_offset +
1090 i * h2link->instance_offset +
1091 AZX_REG_INTEL_VS_SHIM_PVCCS;
1092
1093 state = readw(pvccs) & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS;
1094 dev_dbg(bus->dev, "alt: %d, elid: %d: Mic privacy is %s\n", alt,
1095 elid, str_enabled_disabled(state));
1096
1097 return state;
1098 }
1099
1100 return false;
1101 }
1102 EXPORT_SYMBOL_NS(hdac_bus_eml_get_mic_privacy_state, "SND_SOC_SOF_HDA_MLINK");
1103
1104 #endif
1105
1106 MODULE_LICENSE("Dual BSD/GPL");
1107 MODULE_DESCRIPTION("SOF support for HDaudio multi-link");
1108