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); 290