Unverified Commit efb1a2d3 authored by Mark Brown's avatar Mark Brown
Browse files

ALSA/ASoC/SOF/Intel: improve support for ES8336-based platforms

Merge series from Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>:

This patchset adds a number of improvements for ES8336-based Intel
platforms, which are not well supported at all in Linux. Since
Christmas 2021, we've seen dozens of reports of broken audio [1].

The fundamental problem is that those platforms were built for Windows
but using an I2S codec - instead of the HDaudio traditional
solution. As a result, we are missing all the usual information needed
to configure the audio card (which I2S, what configuration, DMICs or
not, etc). The situation is similar to Baytrail with all possible
permutations enabled.

Some of the information can be discovered by checking the contents of
the 'NHLT' ACPI table. This helps discover at run-time which SSP to
use, and the number of microphones present. This NHLT-based solution
helps remove quirks that were added earlier.

Unfortunately, there are still a number of platform properties that
are not described by ACPI, just as GPIOs used for speakers, jack
detection inversion, etc. For some case, quirks are still provided in
the machine drivers.

Additional work will likely be needed, e.g. to detect which MCLK needs
to be used, refine the UCM settings, add the ES8326 codec driver, but
this is a first-step towards an 'out of the box' experience on Intel
platforms.

This patchset touches the sound/hda/intel-nhlt parts but should IMHO
be merged in the ASoC tree.

I would like to acknowledge the help of Nikolai Kostrigin, Mauro
Carvalho Chehab, Huajun Li, David Yang (@yangxiaohua2009) and other
GitHub testers.

[1] https://github.com/thesofproject/linux/issues?q=is%3Aissue+is%3Aopen+label%3A%22codec+ES8336%22
parents 31ef579d fe0596a0
Loading
Loading
Loading
Loading
+15 −7
Original line number Diff line number Diff line
@@ -18,6 +18,13 @@ enum nhlt_link_type {
	NHLT_LINK_INVALID
};

enum nhlt_device_type {
	NHLT_DEVICE_BT = 0,
	NHLT_DEVICE_DMIC = 1,
	NHLT_DEVICE_I2S = 4,
	NHLT_DEVICE_INVALID
};

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

struct wav_fmt {
@@ -41,13 +48,6 @@ struct wav_fmt_ext {
	u8 sub_fmt[16];
} __packed;

enum nhlt_device_type {
	NHLT_DEVICE_BT = 0,
	NHLT_DEVICE_DMIC = 1,
	NHLT_DEVICE_I2S = 4,
	NHLT_DEVICE_INVALID
};

struct nhlt_specific_cfg {
	u32 size;
	u8 caps[];
@@ -133,6 +133,9 @@ void intel_nhlt_free(struct nhlt_acpi_table *addr);
int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt);

bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type);

int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type);

struct nhlt_specific_cfg *
intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
			     u32 bus_id, u8 link_type, u8 vbps, u8 bps,
@@ -163,6 +166,11 @@ static inline bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt,
	return false;
}

static inline int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type)
{
	return 0;
}

static inline struct nhlt_specific_cfg *
intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
			     u32 bus_id, u8 link_type, u8 vbps, u8 bps,
+25 −2
Original line number Diff line number Diff line
@@ -60,9 +60,11 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg)
 * @acpi_ipc_irq_index: used for BYT-CR detection
 * @platform: string used for HDAudio codec support
 * @codec_mask: used for HDAudio support
 * @dmic_num: number of SoC- or chipset-attached PDM digital microphones
 * @common_hdmi_codec_drv: use commom HDAudio HDMI codec driver
 * @link_mask: links enabled on the board
 * @links: array of link _ADR descriptors, null terminated
 * @link_mask: SoundWire links enabled on the board
 * @links: array of SoundWire link _ADR descriptors, null terminated
 * @i2s_link_mask: I2S/TDM links enabled on the board
 * @num_dai_drivers: number of elements in @dai_drivers
 * @dai_drivers: pointer to dai_drivers, used e.g. in nocodec mode
 */
@@ -74,6 +76,7 @@ struct snd_soc_acpi_mach_params {
	bool common_hdmi_codec_drv;
	u32 link_mask;
	const struct snd_soc_acpi_link_adr *links;
	u32 i2s_link_mask;
	u32 num_dai_drivers;
	struct snd_soc_dai_driver *dai_drivers;
};
@@ -122,6 +125,24 @@ struct snd_soc_acpi_link_adr {
	const struct snd_soc_acpi_adr_device *adr_d;
};

/*
 * when set the topology uses the -ssp<N> suffix, where N is determined based on
 * BIOS or DMI information
 */
#define SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER BIT(0)

/*
 * when more than one SSP is reported in the link mask, use the most significant.
 * This choice was found to be valid on platforms with ES8336 codecs.
 */
#define SND_SOC_ACPI_TPLG_INTEL_SSP_MSB BIT(1)

/*
 * when set the topology uses the -dmic<N>ch suffix, where N is determined based on
 * BIOS or DMI information
 */
#define SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER BIT(2)

/**
 * snd_soc_acpi_mach: ACPI-based machine descriptor. Most of the fields are
 * related to the hardware, except for the firmware and topology file names.
@@ -143,6 +164,7 @@ struct snd_soc_acpi_link_adr {
 * @pdata: intended for platform data or machine specific-ops. This structure
 *  is not constant since this field may be updated at run-time
 * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled
 * @tplg_quirk_mask: quirks to select different topology files dynamically
 */
/* Descriptor for SST ASoC machine driver */
struct snd_soc_acpi_mach {
@@ -158,6 +180,7 @@ struct snd_soc_acpi_mach {
	void *pdata;
	struct snd_soc_acpi_mach_params mach_params;
	const char *sof_tplg_filename;
	const u32 tplg_quirk_mask;
};

#define SND_SOC_ACPI_MAX_CODECS 3
+27 −9
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <sound/core.h>
#include <sound/intel-dsp-config.h>
#include <sound/intel-nhlt.h>
#include <sound/soc-acpi.h>

static int dsp_driver;

@@ -31,7 +32,12 @@ struct config_entry {
	u16 device;
	u8 acpi_hid[ACPI_ID_LEN];
	const struct dmi_system_id *dmi_table;
	u8 codec_hid[ACPI_ID_LEN];
	const struct snd_soc_acpi_codecs *codec_hid;
};

static const struct snd_soc_acpi_codecs __maybe_unused essx_83x6 = {
	.num_codecs = 3,
	.codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
};

/*
@@ -77,7 +83,7 @@ static const struct config_entry config_table[] = {
	{
		.flags = FLAG_SOF,
		.device = 0x5a98,
		.codec_hid = "ESSX8336",
		.codec_hid =  &essx_83x6,
	},
#endif
#if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL)
@@ -163,7 +169,7 @@ static const struct config_entry config_table[] = {
	{
		.flags = FLAG_SOF,
		.device = 0x3198,
		.codec_hid = "ESSX8336",
		.codec_hid =  &essx_83x6,
	},
#endif

@@ -193,6 +199,11 @@ static const struct config_entry config_table[] = {
			{}
		}
	},
	{
		.flags = FLAG_SOF,
		.device = 0x09dc8,
		.codec_hid =  &essx_83x6,
	},
	{
		.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
		.device = 0x9dc8,
@@ -251,7 +262,7 @@ static const struct config_entry config_table[] = {
	{
		.flags = FLAG_SOF,
		.device = 0x02c8,
		.codec_hid = "ESSX8336",
		.codec_hid =  &essx_83x6,
	},
	{
		.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
@@ -280,7 +291,7 @@ static const struct config_entry config_table[] = {
	{
		.flags = FLAG_SOF,
		.device = 0x06c8,
		.codec_hid = "ESSX8336",
		.codec_hid =  &essx_83x6,
	},
	{
		.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
@@ -327,7 +338,7 @@ static const struct config_entry config_table[] = {
	{
		.flags = FLAG_SOF,
		.device = 0x4dc8,
		.codec_hid = "ESSX8336",
		.codec_hid =  &essx_83x6,
	},
	{
		.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
@@ -353,7 +364,7 @@ static const struct config_entry config_table[] = {
	{
		.flags = FLAG_SOF,
		.device = 0xa0c8,
		.codec_hid = "ESSX8336",
		.codec_hid =  &essx_83x6,
	},
	{
		.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
@@ -414,8 +425,15 @@ static const struct config_entry *snd_intel_dsp_find_config
			continue;
		if (table->dmi_table && !dmi_check_system(table->dmi_table))
			continue;
		if (table->codec_hid[0] && !acpi_dev_present(table->codec_hid, NULL, -1))
		if (table->codec_hid) {
			int i;

			for (i = 0; i < table->codec_hid->num_codecs; i++)
				if (acpi_dev_present(table->codec_hid->codecs[i], NULL, -1))
					break;
			if (i == table->codec_hid->num_codecs)
				continue;
		}
		return table;
	}
	return NULL;
+22 −0
Original line number Diff line number Diff line
@@ -130,6 +130,28 @@ bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type)
}
EXPORT_SYMBOL(intel_nhlt_has_endpoint_type);

int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type)
{
	struct nhlt_endpoint *epnt;
	int ssp_mask = 0;
	int i;

	if (!nhlt || (device_type != NHLT_DEVICE_BT && device_type != NHLT_DEVICE_I2S))
		return 0;

	epnt = (struct nhlt_endpoint *)nhlt->desc;
	for (i = 0; i < nhlt->endpoint_count; i++) {
		if (epnt->linktype == NHLT_LINK_SSP && epnt->device_type == device_type) {
			/* for SSP the virtual bus id is the SSP port */
			ssp_mask |= BIT(epnt->virtual_bus_id);
		}
		epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
	}

	return ssp_mask;
}
EXPORT_SYMBOL(intel_nhlt_ssp_endpoint_mask);

static struct nhlt_specific_cfg *
nhlt_get_specific_cfg(struct device *dev, struct nhlt_fmt *fmt, u8 num_ch,
		      u32 rate, u8 vbps, u8 bps)
+2 −1
Original line number Diff line number Diff line
@@ -530,12 +530,13 @@ config SND_SOC_INTEL_SOF_PCM512x_MACH
	  If unsure select "N".

config SND_SOC_INTEL_SOF_ES8336_MACH
	tristate "SOF with ES8336 codec in I2S mode"
	tristate "SOF with ES8336 or ES8326 codec in I2S mode"
	depends on I2C && ACPI
	depends on MFD_INTEL_LPSS || COMPILE_TEST
	depends on GPIOLIB || COMPILE_TEST
	depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC
	select SND_SOC_ES8316
	select SND_SOC_ES8326
	select SND_SOC_DMIC
	select SND_SOC_INTEL_HDA_DSP_COMMON
	help
Loading