Skip to content

Commit

Permalink
Merge branch 'topic/hda-dmic' into for-next
Browse files Browse the repository at this point in the history
Pull HD-audio DMIC probe patchset from Pierre-Louis Bossart

Signed-off-by: Takashi Iwai <[email protected]>
  • Loading branch information
tiwai committed Jul 31, 2019
2 parents 83554cb + 8c57588 commit c1c0c03
Show file tree
Hide file tree
Showing 12 changed files with 212 additions and 108 deletions.
51 changes: 41 additions & 10 deletions sound/soc/intel/skylake/skl-nhlt.h → include/sound/intel-nhlt.h
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* skl-nhlt.h - Intel HDA Platform NHLT header
* intel-nhlt.h - Intel HDA Platform NHLT header
*
* Copyright (C) 2015 Intel Corp
* Author: Sanjiv Kumar <[email protected]>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Copyright (c) 2015-2019 Intel Corporation
*/
#ifndef __SKL_NHLT_H__
#define __SKL_NHLT_H__

#ifndef __INTEL_NHLT_H__
#define __INTEL_NHLT_H__

#include <linux/acpi.h>

#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT)

struct wav_fmt {
u16 fmt_tag;
u16 channels;
Expand Down Expand Up @@ -97,16 +96,22 @@ struct nhlt_resource_desc {
#define MIC_ARRAY_2CH 2
#define MIC_ARRAY_4CH 4

struct nhlt_tdm_config {
struct nhlt_device_specific_config {
u8 virtual_slot;
u8 config_type;
} __packed;

struct nhlt_dmic_array_config {
struct nhlt_tdm_config tdm_config;
struct nhlt_device_specific_config device_config;
u8 array_type;
} __packed;

struct nhlt_vendor_dmic_array_config {
struct nhlt_dmic_array_config dmic_config;
u8 nb_mics;
/* TODO add vendor mic config */
} __packed;

enum {
NHLT_MIC_ARRAY_2CH_SMALL = 0xa,
NHLT_MIC_ARRAY_2CH_BIG = 0xb,
Expand All @@ -116,4 +121,30 @@ enum {
NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf,
};

struct nhlt_acpi_table *intel_nhlt_init(struct device *dev);

void intel_nhlt_free(struct nhlt_acpi_table *addr);

int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt);

#else

struct nhlt_acpi_table;

static inline struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
{
return NULL;
}

static inline void intel_nhlt_free(struct nhlt_acpi_table *addr)
{
}

static inline int intel_nhlt_get_dmic_geo(struct device *dev,
struct nhlt_acpi_table *nhlt)
{
return 0;
}
#endif

#endif
5 changes: 5 additions & 0 deletions sound/hda/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ config SND_HDA_PREALLOC_SIZE

Note that the pre-allocation size can be changed dynamically
via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.

config SND_INTEL_NHLT
tristate
# this config should be selected only for Intel ACPI platforms.
# A fallback is provided so that the code compiles in all cases.
3 changes: 3 additions & 0 deletions sound/hda/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o

#extended hda
obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/

snd-intel-nhlt-objs := intel-nhlt.o
obj-$(CONFIG_SND_INTEL_NHLT) += snd-intel-nhlt.o
107 changes: 107 additions & 0 deletions sound/hda/intel-nhlt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2015-2019 Intel Corporation

#include <linux/acpi.h>
#include <sound/intel-nhlt.h>

#define NHLT_ACPI_HEADER_SIG "NHLT"

/* Unique identification for getting NHLT blobs */
static guid_t osc_guid =
GUID_INIT(0xA69F886E, 0x6CEB, 0x4594,
0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53);

struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
{
acpi_handle handle;
union acpi_object *obj;
struct nhlt_resource_desc *nhlt_ptr;
struct nhlt_acpi_table *nhlt_table = NULL;

handle = ACPI_HANDLE(dev);
if (!handle) {
dev_err(dev, "Didn't find ACPI_HANDLE\n");
return NULL;
}

obj = acpi_evaluate_dsm(handle, &osc_guid, 1, 1, NULL);

if (!obj)
return NULL;

if (obj->type != ACPI_TYPE_BUFFER) {
dev_dbg(dev, "No NHLT table found\n");
ACPI_FREE(obj);
return NULL;
}

nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
if (nhlt_ptr->length)
nhlt_table = (struct nhlt_acpi_table *)
memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
MEMREMAP_WB);
ACPI_FREE(obj);
if (nhlt_table &&
(strncmp(nhlt_table->header.signature,
NHLT_ACPI_HEADER_SIG,
strlen(NHLT_ACPI_HEADER_SIG)) != 0)) {
memunmap(nhlt_table);
dev_err(dev, "NHLT ACPI header signature incorrect\n");
return NULL;
}
return nhlt_table;
}
EXPORT_SYMBOL_GPL(intel_nhlt_init);

void intel_nhlt_free(struct nhlt_acpi_table *nhlt)
{
memunmap((void *)nhlt);
}
EXPORT_SYMBOL_GPL(intel_nhlt_free);

int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
{
struct nhlt_endpoint *epnt;
struct nhlt_dmic_array_config *cfg;
struct nhlt_vendor_dmic_array_config *cfg_vendor;
unsigned int dmic_geo = 0;
u8 j;

if (!nhlt)
return 0;

epnt = (struct nhlt_endpoint *)nhlt->desc;

for (j = 0; j < nhlt->endpoint_count; j++) {
if (epnt->linktype == NHLT_LINK_DMIC) {
cfg = (struct nhlt_dmic_array_config *)
(epnt->config.caps);
switch (cfg->array_type) {
case NHLT_MIC_ARRAY_2CH_SMALL:
case NHLT_MIC_ARRAY_2CH_BIG:
dmic_geo = MIC_ARRAY_2CH;
break;

case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
case NHLT_MIC_ARRAY_4CH_L_SHAPED:
case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
dmic_geo = MIC_ARRAY_4CH;
break;
case NHLT_MIC_ARRAY_VENDOR_DEFINED:
cfg_vendor = (struct nhlt_vendor_dmic_array_config *)cfg;
dmic_geo = cfg_vendor->nb_mics;
break;
default:
dev_warn(dev, "undefined DMIC array_type 0x%0x\n",
cfg->array_type);
}
}
epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
}

return dmic_geo;
}
EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel NHLT driver");
10 changes: 10 additions & 0 deletions sound/pci/hda/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ config SND_HDA_INTEL
tristate "HD Audio PCI"
depends on SND_PCI
select SND_HDA
select SND_INTEL_NHLT if ACPI
help
Say Y here to include support for Intel "High Definition
Audio" (Azalia) and its compatible devices.
Expand All @@ -22,6 +23,15 @@ config SND_HDA_INTEL
To compile this driver as a module, choose M here: the module
will be called snd-hda-intel.

config SND_HDA_INTEL_DETECT_DMIC
bool "DMIC detection and probe abort"
depends on SND_HDA_INTEL
help
Say Y to detect digital microphones on SKL+ devices. DMICs
cannot be handled by the HDaudio legacy driver and are
currently only supported by the SOF driver.
If unsure say N.

config SND_HDA_TEGRA
tristate "NVIDIA Tegra HD Audio"
depends on ARCH_TEGRA
Expand Down
34 changes: 34 additions & 0 deletions sound/pci/hda/hda_intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include <sound/initval.h>
#include <sound/hdaudio.h>
#include <sound/hda_i915.h>
#include <sound/intel-nhlt.h>
#include <linux/vgaarb.h>
#include <linux/vga_switcheroo.h>
#include <linux/firmware.h>
Expand Down Expand Up @@ -124,6 +125,7 @@ static char *patch[SNDRV_CARDS];
static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
CONFIG_SND_HDA_INPUT_BEEP_MODE};
#endif
static bool dmic_detect = IS_ENABLED(CONFIG_SND_HDA_INTEL_DETECT_DMIC);

module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
Expand Down Expand Up @@ -158,6 +160,8 @@ module_param_array(beep_mode, bool, NULL, 0444);
MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
"(0=off, 1=on) (default=1).");
#endif
module_param(dmic_detect, bool, 0444);
MODULE_PARM_DESC(dmic_detect, "DMIC detect on SKL+ platforms");

#ifdef CONFIG_PM
static int param_set_xint(const char *val, const struct kernel_param *kp);
Expand Down Expand Up @@ -2025,6 +2029,25 @@ static const struct hda_controller_ops pci_hda_ops = {
.position_check = azx_position_check,
};

static int azx_check_dmic(struct pci_dev *pci, struct azx *chip)
{
struct nhlt_acpi_table *nhlt;
int ret = 0;

if (chip->driver_type == AZX_DRIVER_SKL &&
pci->class != 0x040300) {
nhlt = intel_nhlt_init(&pci->dev);
if (nhlt) {
if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) {
ret = -ENODEV;
dev_info(&pci->dev, "Digital mics found on Skylake+ platform, aborting probe\n");
}
intel_nhlt_free(nhlt);
}
}
return ret;
}

static int azx_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
Expand Down Expand Up @@ -2055,6 +2078,17 @@ static int azx_probe(struct pci_dev *pci,
card->private_data = chip;
hda = container_of(chip, struct hda_intel, chip);

/*
* stop probe if digital microphones detected on Skylake+ platform
* with the DSP enabled. This is an opt-in behavior defined at build
* time or at run-time with a module parameter
*/
if (dmic_detect) {
err = azx_check_dmic(pci, chip);
if (err < 0)
goto out_free;
}

pci_set_drvdata(pci, card);

err = register_vga_switcheroo(chip);
Expand Down
1 change: 1 addition & 0 deletions sound/soc/intel/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ config SND_SOC_INTEL_SKYLAKE_COMMON
select SND_SOC_INTEL_SST
select SND_SOC_HDAC_HDA if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC
select SND_SOC_ACPI_INTEL_MATCH
select SND_INTEL_NHLT
help
If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/
GeminiLake or CannonLake platform with the DSP enabled in the BIOS
Expand Down
Loading

0 comments on commit c1c0c03

Please sign in to comment.