Skip to content

Commit 8ace173

Browse files
committed
Merge branch 'topic/hda-nhlt' into for-next
Pull HD-audio NHLT fix Signed-off-by: Takashi Iwai <tiwai@suse.de>
2 parents 914728f + 51bebf3 commit 8ace173

File tree

7 files changed

+601
-189
lines changed

7 files changed

+601
-189
lines changed

drivers/acpi/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,9 @@ config ACPI_REDUCED_HARDWARE_ONLY
469469

470470
If you are unsure what to do, do not enable this option.
471471

472+
config ACPI_NHLT
473+
bool
474+
472475
source "drivers/acpi/nfit/Kconfig"
473476
source "drivers/acpi/numa/Kconfig"
474477
source "drivers/acpi/apei/Kconfig"

drivers/acpi/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ obj-$(CONFIG_ACPI_THERMAL_LIB) += thermal_lib.o
9393
obj-$(CONFIG_ACPI_THERMAL) += thermal.o
9494
obj-$(CONFIG_ACPI_PLATFORM_PROFILE) += platform_profile.o
9595
obj-$(CONFIG_ACPI_NFIT) += nfit/
96+
obj-$(CONFIG_ACPI_NHLT) += nhlt.o
9697
obj-$(CONFIG_ACPI_NUMA) += numa/
9798
obj-$(CONFIG_ACPI) += acpi_memhotplug.o
9899
obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o

drivers/acpi/nhlt.c

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright(c) 2023-2024 Intel Corporation
4+
*
5+
* Authors: Cezary Rojewski <cezary.rojewski@intel.com>
6+
* Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
7+
*/
8+
9+
#define pr_fmt(fmt) "ACPI: NHLT: " fmt
10+
11+
#include <linux/acpi.h>
12+
#include <linux/errno.h>
13+
#include <linux/export.h>
14+
#include <linux/minmax.h>
15+
#include <linux/printk.h>
16+
#include <linux/types.h>
17+
#include <acpi/nhlt.h>
18+
19+
static struct acpi_table_nhlt *acpi_gbl_nhlt;
20+
21+
static struct acpi_table_nhlt empty_nhlt = {
22+
.header = {
23+
.signature = ACPI_SIG_NHLT,
24+
},
25+
};
26+
27+
/**
28+
* acpi_nhlt_get_gbl_table - Retrieve a pointer to the first NHLT table.
29+
*
30+
* If there is no NHLT in the system, acpi_gbl_nhlt will instead point to an
31+
* empty table.
32+
*
33+
* Return: ACPI status code of the operation.
34+
*/
35+
acpi_status acpi_nhlt_get_gbl_table(void)
36+
{
37+
acpi_status status;
38+
39+
status = acpi_get_table(ACPI_SIG_NHLT, 0, (struct acpi_table_header **)(&acpi_gbl_nhlt));
40+
if (!acpi_gbl_nhlt)
41+
acpi_gbl_nhlt = &empty_nhlt;
42+
return status;
43+
}
44+
EXPORT_SYMBOL_GPL(acpi_nhlt_get_gbl_table);
45+
46+
/**
47+
* acpi_nhlt_put_gbl_table - Release the global NHLT table.
48+
*/
49+
void acpi_nhlt_put_gbl_table(void)
50+
{
51+
acpi_put_table((struct acpi_table_header *)acpi_gbl_nhlt);
52+
}
53+
EXPORT_SYMBOL_GPL(acpi_nhlt_put_gbl_table);
54+
55+
/**
56+
* acpi_nhlt_endpoint_match - Verify if an endpoint matches criteria.
57+
* @ep: the endpoint to check.
58+
* @link_type: the hardware link type, e.g.: PDM or SSP.
59+
* @dev_type: the device type.
60+
* @dir: stream direction.
61+
* @bus_id: the ID of virtual bus hosting the endpoint.
62+
*
63+
* Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative
64+
* value to ignore the parameter when matching.
65+
*
66+
* Return: %true if endpoint matches specified criteria or %false otherwise.
67+
*/
68+
bool acpi_nhlt_endpoint_match(const struct acpi_nhlt_endpoint *ep,
69+
int link_type, int dev_type, int dir, int bus_id)
70+
{
71+
return ep &&
72+
(link_type < 0 || ep->link_type == link_type) &&
73+
(dev_type < 0 || ep->device_type == dev_type) &&
74+
(bus_id < 0 || ep->virtual_bus_id == bus_id) &&
75+
(dir < 0 || ep->direction == dir);
76+
}
77+
EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_match);
78+
79+
/**
80+
* acpi_nhlt_tb_find_endpoint - Search a NHLT table for an endpoint.
81+
* @tb: the table to search.
82+
* @link_type: the hardware link type, e.g.: PDM or SSP.
83+
* @dev_type: the device type.
84+
* @dir: stream direction.
85+
* @bus_id: the ID of virtual bus hosting the endpoint.
86+
*
87+
* Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative
88+
* value to ignore the parameter during the search.
89+
*
90+
* Return: A pointer to endpoint matching the criteria, %NULL if not found or
91+
* an ERR_PTR() otherwise.
92+
*/
93+
struct acpi_nhlt_endpoint *
94+
acpi_nhlt_tb_find_endpoint(const struct acpi_table_nhlt *tb,
95+
int link_type, int dev_type, int dir, int bus_id)
96+
{
97+
struct acpi_nhlt_endpoint *ep;
98+
99+
for_each_nhlt_endpoint(tb, ep)
100+
if (acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id))
101+
return ep;
102+
return NULL;
103+
}
104+
EXPORT_SYMBOL_GPL(acpi_nhlt_tb_find_endpoint);
105+
106+
/**
107+
* acpi_nhlt_find_endpoint - Search all NHLT tables for an endpoint.
108+
* @link_type: the hardware link type, e.g.: PDM or SSP.
109+
* @dev_type: the device type.
110+
* @dir: stream direction.
111+
* @bus_id: the ID of virtual bus hosting the endpoint.
112+
*
113+
* Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative
114+
* value to ignore the parameter during the search.
115+
*
116+
* Return: A pointer to endpoint matching the criteria, %NULL if not found or
117+
* an ERR_PTR() otherwise.
118+
*/
119+
struct acpi_nhlt_endpoint *
120+
acpi_nhlt_find_endpoint(int link_type, int dev_type, int dir, int bus_id)
121+
{
122+
/* TODO: Currently limited to table of index 0. */
123+
return acpi_nhlt_tb_find_endpoint(acpi_gbl_nhlt, link_type, dev_type, dir, bus_id);
124+
}
125+
EXPORT_SYMBOL_GPL(acpi_nhlt_find_endpoint);
126+
127+
/**
128+
* acpi_nhlt_endpoint_find_fmtcfg - Search endpoint's formats configuration space
129+
* for a specific format.
130+
* @ep: the endpoint to search.
131+
* @ch: number of channels.
132+
* @rate: samples per second.
133+
* @vbps: valid bits per sample.
134+
* @bps: bits per sample.
135+
*
136+
* Return: A pointer to format matching the criteria, %NULL if not found or
137+
* an ERR_PTR() otherwise.
138+
*/
139+
struct acpi_nhlt_format_config *
140+
acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt_endpoint *ep,
141+
u16 ch, u32 rate, u16 vbps, u16 bps)
142+
{
143+
struct acpi_nhlt_wave_formatext *wav;
144+
struct acpi_nhlt_format_config *fmt;
145+
146+
for_each_nhlt_endpoint_fmtcfg(ep, fmt) {
147+
wav = &fmt->format;
148+
149+
if (wav->valid_bits_per_sample == vbps &&
150+
wav->samples_per_sec == rate &&
151+
wav->bits_per_sample == bps &&
152+
wav->channel_count == ch)
153+
return fmt;
154+
}
155+
156+
return NULL;
157+
}
158+
EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_find_fmtcfg);
159+
160+
/**
161+
* acpi_nhlt_tb_find_fmtcfg - Search a NHLT table for a specific format.
162+
* @tb: the table to search.
163+
* @link_type: the hardware link type, e.g.: PDM or SSP.
164+
* @dev_type: the device type.
165+
* @dir: stream direction.
166+
* @bus_id: the ID of virtual bus hosting the endpoint.
167+
*
168+
* @ch: number of channels.
169+
* @rate: samples per second.
170+
* @vbps: valid bits per sample.
171+
* @bps: bits per sample.
172+
*
173+
* Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative
174+
* value to ignore the parameter during the search.
175+
*
176+
* Return: A pointer to format matching the criteria, %NULL if not found or
177+
* an ERR_PTR() otherwise.
178+
*/
179+
struct acpi_nhlt_format_config *
180+
acpi_nhlt_tb_find_fmtcfg(const struct acpi_table_nhlt *tb,
181+
int link_type, int dev_type, int dir, int bus_id,
182+
u16 ch, u32 rate, u16 vbps, u16 bps)
183+
{
184+
struct acpi_nhlt_format_config *fmt;
185+
struct acpi_nhlt_endpoint *ep;
186+
187+
for_each_nhlt_endpoint(tb, ep) {
188+
if (!acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id))
189+
continue;
190+
191+
fmt = acpi_nhlt_endpoint_find_fmtcfg(ep, ch, rate, vbps, bps);
192+
if (fmt)
193+
return fmt;
194+
}
195+
196+
return NULL;
197+
}
198+
EXPORT_SYMBOL_GPL(acpi_nhlt_tb_find_fmtcfg);
199+
200+
/**
201+
* acpi_nhlt_find_fmtcfg - Search all NHLT tables for a specific format.
202+
* @link_type: the hardware link type, e.g.: PDM or SSP.
203+
* @dev_type: the device type.
204+
* @dir: stream direction.
205+
* @bus_id: the ID of virtual bus hosting the endpoint.
206+
*
207+
* @ch: number of channels.
208+
* @rate: samples per second.
209+
* @vbps: valid bits per sample.
210+
* @bps: bits per sample.
211+
*
212+
* Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative
213+
* value to ignore the parameter during the search.
214+
*
215+
* Return: A pointer to format matching the criteria, %NULL if not found or
216+
* an ERR_PTR() otherwise.
217+
*/
218+
struct acpi_nhlt_format_config *
219+
acpi_nhlt_find_fmtcfg(int link_type, int dev_type, int dir, int bus_id,
220+
u16 ch, u32 rate, u16 vbps, u16 bps)
221+
{
222+
/* TODO: Currently limited to table of index 0. */
223+
return acpi_nhlt_tb_find_fmtcfg(acpi_gbl_nhlt, link_type, dev_type, dir, bus_id,
224+
ch, rate, vbps, bps);
225+
}
226+
EXPORT_SYMBOL_GPL(acpi_nhlt_find_fmtcfg);
227+
228+
static bool acpi_nhlt_config_is_micdevice(struct acpi_nhlt_config *cfg)
229+
{
230+
return cfg->capabilities_size >= sizeof(struct acpi_nhlt_micdevice_config);
231+
}
232+
233+
static bool acpi_nhlt_config_is_vendor_micdevice(struct acpi_nhlt_config *cfg)
234+
{
235+
struct acpi_nhlt_vendor_micdevice_config *devcfg = __acpi_nhlt_config_caps(cfg);
236+
237+
return cfg->capabilities_size >= sizeof(*devcfg) &&
238+
cfg->capabilities_size == struct_size(devcfg, mics, devcfg->mics_count);
239+
}
240+
241+
/**
242+
* acpi_nhlt_endpoint_mic_count - Retrieve number of digital microphones for a PDM endpoint.
243+
* @ep: the endpoint to return microphones count for.
244+
*
245+
* Return: A number of microphones or an error code if an invalid endpoint is provided.
246+
*/
247+
int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt_endpoint *ep)
248+
{
249+
union acpi_nhlt_device_config *devcfg;
250+
struct acpi_nhlt_format_config *fmt;
251+
struct acpi_nhlt_config *cfg;
252+
u16 max_ch = 0;
253+
254+
if (!ep || ep->link_type != ACPI_NHLT_LINKTYPE_PDM)
255+
return -EINVAL;
256+
257+
/* Find max number of channels based on formats configuration. */
258+
for_each_nhlt_endpoint_fmtcfg(ep, fmt)
259+
max_ch = max(fmt->format.channel_count, max_ch);
260+
261+
cfg = __acpi_nhlt_endpoint_config(ep);
262+
devcfg = __acpi_nhlt_config_caps(cfg);
263+
264+
/* If @ep is not a mic array, fallback to channels count. */
265+
if (!acpi_nhlt_config_is_micdevice(cfg) ||
266+
devcfg->gen.config_type != ACPI_NHLT_CONFIGTYPE_MICARRAY)
267+
return max_ch;
268+
269+
switch (devcfg->mic.array_type) {
270+
case ACPI_NHLT_ARRAYTYPE_LINEAR2_SMALL:
271+
case ACPI_NHLT_ARRAYTYPE_LINEAR2_BIG:
272+
return 2;
273+
274+
case ACPI_NHLT_ARRAYTYPE_LINEAR4_GEO1:
275+
case ACPI_NHLT_ARRAYTYPE_PLANAR4_LSHAPED:
276+
case ACPI_NHLT_ARRAYTYPE_LINEAR4_GEO2:
277+
return 4;
278+
279+
case ACPI_NHLT_ARRAYTYPE_VENDOR:
280+
if (!acpi_nhlt_config_is_vendor_micdevice(cfg))
281+
return -EINVAL;
282+
return devcfg->vendor_mic.mics_count;
283+
284+
default:
285+
pr_warn("undefined mic array type: %#x\n", devcfg->mic.array_type);
286+
return max_ch;
287+
}
288+
}
289+
EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_mic_count);

0 commit comments

Comments
 (0)