xref: /kernel/linux/linux-5.10/sound/hda/intel-nhlt.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci// Copyright (c) 2015-2019 Intel Corporation
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/acpi.h>
58c2ecf20Sopenharmony_ci#include <sound/intel-nhlt.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_cistruct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
88c2ecf20Sopenharmony_ci{
98c2ecf20Sopenharmony_ci	struct nhlt_acpi_table *nhlt;
108c2ecf20Sopenharmony_ci	acpi_status status;
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci	status = acpi_get_table(ACPI_SIG_NHLT, 0,
138c2ecf20Sopenharmony_ci				(struct acpi_table_header **)&nhlt);
148c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
158c2ecf20Sopenharmony_ci		dev_warn(dev, "NHLT table not found\n");
168c2ecf20Sopenharmony_ci		return NULL;
178c2ecf20Sopenharmony_ci	}
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	return nhlt;
208c2ecf20Sopenharmony_ci}
218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_nhlt_init);
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_civoid intel_nhlt_free(struct nhlt_acpi_table *nhlt)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	acpi_put_table((struct acpi_table_header *)nhlt);
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_nhlt_free);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciint intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	struct nhlt_endpoint *epnt;
328c2ecf20Sopenharmony_ci	struct nhlt_dmic_array_config *cfg;
338c2ecf20Sopenharmony_ci	struct nhlt_vendor_dmic_array_config *cfg_vendor;
348c2ecf20Sopenharmony_ci	struct nhlt_fmt *fmt_configs;
358c2ecf20Sopenharmony_ci	unsigned int dmic_geo = 0;
368c2ecf20Sopenharmony_ci	u16 max_ch = 0;
378c2ecf20Sopenharmony_ci	u8 i, j;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if (!nhlt)
408c2ecf20Sopenharmony_ci		return 0;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (nhlt->header.length <= sizeof(struct acpi_table_header)) {
438c2ecf20Sopenharmony_ci		dev_warn(dev, "Invalid DMIC description table\n");
448c2ecf20Sopenharmony_ci		return 0;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	for (j = 0, epnt = nhlt->desc; j < nhlt->endpoint_count; j++,
488c2ecf20Sopenharmony_ci	     epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length)) {
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci		if (epnt->linktype != NHLT_LINK_DMIC)
518c2ecf20Sopenharmony_ci			continue;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci		cfg = (struct nhlt_dmic_array_config  *)(epnt->config.caps);
548c2ecf20Sopenharmony_ci		fmt_configs = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci		/* find max number of channels based on format_configuration */
578c2ecf20Sopenharmony_ci		if (fmt_configs->fmt_count) {
588c2ecf20Sopenharmony_ci			struct nhlt_fmt_cfg *fmt_cfg = fmt_configs->fmt_config;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci			dev_dbg(dev, "found %d format definitions\n",
618c2ecf20Sopenharmony_ci				fmt_configs->fmt_count);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci			for (i = 0; i < fmt_configs->fmt_count; i++) {
648c2ecf20Sopenharmony_ci				struct wav_fmt_ext *fmt_ext;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci				fmt_ext = &fmt_cfg->fmt_ext;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci				if (fmt_ext->fmt.channels > max_ch)
698c2ecf20Sopenharmony_ci					max_ch = fmt_ext->fmt.channels;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci				/* Move to the next nhlt_fmt_cfg */
728c2ecf20Sopenharmony_ci				fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps +
738c2ecf20Sopenharmony_ci								  fmt_cfg->config.size);
748c2ecf20Sopenharmony_ci			}
758c2ecf20Sopenharmony_ci			dev_dbg(dev, "max channels found %d\n", max_ch);
768c2ecf20Sopenharmony_ci		} else {
778c2ecf20Sopenharmony_ci			dev_dbg(dev, "No format information found\n");
788c2ecf20Sopenharmony_ci		}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		if (cfg->device_config.config_type != NHLT_CONFIG_TYPE_MIC_ARRAY) {
818c2ecf20Sopenharmony_ci			dmic_geo = max_ch;
828c2ecf20Sopenharmony_ci		} else {
838c2ecf20Sopenharmony_ci			switch (cfg->array_type) {
848c2ecf20Sopenharmony_ci			case NHLT_MIC_ARRAY_2CH_SMALL:
858c2ecf20Sopenharmony_ci			case NHLT_MIC_ARRAY_2CH_BIG:
868c2ecf20Sopenharmony_ci				dmic_geo = MIC_ARRAY_2CH;
878c2ecf20Sopenharmony_ci				break;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci			case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
908c2ecf20Sopenharmony_ci			case NHLT_MIC_ARRAY_4CH_L_SHAPED:
918c2ecf20Sopenharmony_ci			case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
928c2ecf20Sopenharmony_ci				dmic_geo = MIC_ARRAY_4CH;
938c2ecf20Sopenharmony_ci				break;
948c2ecf20Sopenharmony_ci			case NHLT_MIC_ARRAY_VENDOR_DEFINED:
958c2ecf20Sopenharmony_ci				cfg_vendor = (struct nhlt_vendor_dmic_array_config *)cfg;
968c2ecf20Sopenharmony_ci				dmic_geo = cfg_vendor->nb_mics;
978c2ecf20Sopenharmony_ci				break;
988c2ecf20Sopenharmony_ci			default:
998c2ecf20Sopenharmony_ci				dev_warn(dev, "%s: undefined DMIC array_type 0x%0x\n",
1008c2ecf20Sopenharmony_ci					 __func__, cfg->array_type);
1018c2ecf20Sopenharmony_ci			}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci			if (dmic_geo > 0) {
1048c2ecf20Sopenharmony_ci				dev_dbg(dev, "Array with %d dmics\n", dmic_geo);
1058c2ecf20Sopenharmony_ci			}
1068c2ecf20Sopenharmony_ci			if (max_ch > dmic_geo) {
1078c2ecf20Sopenharmony_ci				dev_dbg(dev, "max channels %d exceed dmic number %d\n",
1088c2ecf20Sopenharmony_ci					max_ch, dmic_geo);
1098c2ecf20Sopenharmony_ci			}
1108c2ecf20Sopenharmony_ci		}
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	dev_dbg(dev, "dmic number %d max_ch %d\n", dmic_geo, max_ch);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return dmic_geo;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo);
118