1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright(c) 2023-2024 Intel Corporation
5 * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
6 * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
9 #define pr_fmt(fmt) "ACPI: NHLT: " fmt
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>
19 static struct acpi_table_nhlt
*acpi_gbl_nhlt
;
21 static struct acpi_table_nhlt empty_nhlt
= {
23 .signature
= ACPI_SIG_NHLT
,
28 * acpi_nhlt_get_gbl_table - Retrieve a pointer to the first NHLT table.
30 * If there is no NHLT in the system, acpi_gbl_nhlt will instead point to an
33 * Return: ACPI status code of the operation.
35 acpi_status
acpi_nhlt_get_gbl_table(void)
39 status
= acpi_get_table(ACPI_SIG_NHLT
, 0, (struct acpi_table_header
**)(&acpi_gbl_nhlt
));
41 acpi_gbl_nhlt
= &empty_nhlt
;
44 EXPORT_SYMBOL_GPL(acpi_nhlt_get_gbl_table
);
47 * acpi_nhlt_put_gbl_table - Release the global NHLT table.
49 void acpi_nhlt_put_gbl_table(void)
51 acpi_put_table((struct acpi_table_header
*)acpi_gbl_nhlt
);
53 EXPORT_SYMBOL_GPL(acpi_nhlt_put_gbl_table
);
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.
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.
66 * Return: %true if endpoint matches specified criteria or %false otherwise.
68 bool acpi_nhlt_endpoint_match(const struct acpi_nhlt_endpoint
*ep
,
69 int link_type
, int dev_type
, int dir
, int bus_id
)
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
);
77 EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_match
);
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.
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.
90 * Return: A pointer to endpoint matching the criteria, %NULL if not found or
91 * an ERR_PTR() otherwise.
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
)
97 struct acpi_nhlt_endpoint
*ep
;
99 for_each_nhlt_endpoint(tb
, ep
)
100 if (acpi_nhlt_endpoint_match(ep
, link_type
, dev_type
, dir
, bus_id
))
104 EXPORT_SYMBOL_GPL(acpi_nhlt_tb_find_endpoint
);
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.
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.
116 * Return: A pointer to endpoint matching the criteria, %NULL if not found or
117 * an ERR_PTR() otherwise.
119 struct acpi_nhlt_endpoint
*
120 acpi_nhlt_find_endpoint(int link_type
, int dev_type
, int dir
, int bus_id
)
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
);
125 EXPORT_SYMBOL_GPL(acpi_nhlt_find_endpoint
);
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.
136 * Return: A pointer to format matching the criteria, %NULL if not found or
137 * an ERR_PTR() otherwise.
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
)
143 struct acpi_nhlt_wave_formatext
*wav
;
144 struct acpi_nhlt_format_config
*fmt
;
146 for_each_nhlt_endpoint_fmtcfg(ep
, fmt
) {
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
)
158 EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_find_fmtcfg
);
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.
168 * @ch: number of channels.
169 * @rate: samples per second.
170 * @vbps: valid bits per sample.
171 * @bps: bits per sample.
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.
176 * Return: A pointer to format matching the criteria, %NULL if not found or
177 * an ERR_PTR() otherwise.
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
)
184 struct acpi_nhlt_format_config
*fmt
;
185 struct acpi_nhlt_endpoint
*ep
;
187 for_each_nhlt_endpoint(tb
, ep
) {
188 if (!acpi_nhlt_endpoint_match(ep
, link_type
, dev_type
, dir
, bus_id
))
191 fmt
= acpi_nhlt_endpoint_find_fmtcfg(ep
, ch
, rate
, vbps
, bps
);
198 EXPORT_SYMBOL_GPL(acpi_nhlt_tb_find_fmtcfg
);
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.
207 * @ch: number of channels.
208 * @rate: samples per second.
209 * @vbps: valid bits per sample.
210 * @bps: bits per sample.
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.
215 * Return: A pointer to format matching the criteria, %NULL if not found or
216 * an ERR_PTR() otherwise.
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
)
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
);
226 EXPORT_SYMBOL_GPL(acpi_nhlt_find_fmtcfg
);
228 static bool acpi_nhlt_config_is_micdevice(struct acpi_nhlt_config
*cfg
)
230 return cfg
->capabilities_size
>= sizeof(struct acpi_nhlt_micdevice_config
);
233 static bool acpi_nhlt_config_is_vendor_micdevice(struct acpi_nhlt_config
*cfg
)
235 struct acpi_nhlt_vendor_micdevice_config
*devcfg
= __acpi_nhlt_config_caps(cfg
);
237 return cfg
->capabilities_size
>= sizeof(*devcfg
) &&
238 cfg
->capabilities_size
== struct_size(devcfg
, mics
, devcfg
->mics_count
);
242 * acpi_nhlt_endpoint_mic_count - Retrieve number of digital microphones for a PDM endpoint.
243 * @ep: the endpoint to return microphones count for.
245 * Return: A number of microphones or an error code if an invalid endpoint is provided.
247 int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt_endpoint
*ep
)
249 union acpi_nhlt_device_config
*devcfg
;
250 struct acpi_nhlt_format_config
*fmt
;
251 struct acpi_nhlt_config
*cfg
;
254 if (!ep
|| ep
->link_type
!= ACPI_NHLT_LINKTYPE_PDM
)
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
);
261 cfg
= __acpi_nhlt_endpoint_config(ep
);
262 devcfg
= __acpi_nhlt_config_caps(cfg
);
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
)
269 switch (devcfg
->mic
.array_type
) {
270 case ACPI_NHLT_ARRAYTYPE_LINEAR2_SMALL
:
271 case ACPI_NHLT_ARRAYTYPE_LINEAR2_BIG
:
274 case ACPI_NHLT_ARRAYTYPE_LINEAR4_GEO1
:
275 case ACPI_NHLT_ARRAYTYPE_PLANAR4_LSHAPED
:
276 case ACPI_NHLT_ARRAYTYPE_LINEAR4_GEO2
:
279 case ACPI_NHLT_ARRAYTYPE_VENDOR
:
280 if (!acpi_nhlt_config_is_vendor_micdevice(cfg
))
282 return devcfg
->vendor_mic
.mics_count
;
285 pr_warn("undefined mic array type: %#x\n", devcfg
->mic
.array_type
);
289 EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_mic_count
);