[d-kernel] [PATCH 1/4] ASoC: AMD: add ACP machine driver for ES8336

nickel на altlinux.org nickel на altlinux.org
Ср Апр 5 19:59:40 MSK 2023


From: Vasiliy Kovalev <kovalev at altlinux.org>

Backport from 6.1.3
Based on commit 6976fc0fea0dcc5a367580cd7edf94574601837e from
fork of Marian Postevca aka @codepayne

Link: https://github.com/thesofproject/linux/issues/3249#issuecomment-1378025039
Link: https://github.com/codepayne/linux-sound-huawei/issues/5

fix conflict use pci-acp driver for ES8336 codec (kovalev)

For a family of machines whose pci revision ID is 0x1 (renoir),
working with the es8336 codec is only available through the
snd-pci-acp3x driver (raven). Now we will use a specific driver
if there is an acpi device detected as "ESSX8336" in the machine.

Signed-off-by: Vasiliy Kovalev <kovalev at altlinux.org>
Signed-off-by: Nikolai Kostrigin <nickel at altlinux.org>
---
 config                          |   1 +
 sound/soc/amd/Kconfig           |  13 ++
 sound/soc/amd/Makefile          |   2 +
 sound/soc/amd/acp3x-es8336.c    | 395 ++++++++++++++++++++++++++++++++
 sound/soc/amd/raven/pci-acp3x.c |   7 +-
 sound/soc/codecs/es8316.c       |  85 +++++--
 6 files changed, 476 insertions(+), 27 deletions(-)
 create mode 100644 sound/soc/amd/acp3x-es8336.c

diff --git a/config b/config
index 7a9be2c8203ff..61e6930c3b532 100644
--- a/config
+++ b/config
@@ -6833,6 +6833,7 @@ CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH=m
 CONFIG_SND_SOC_AMD_CZ_RT5645_MACH=m
 CONFIG_SND_SOC_AMD_ST_ES8336_MACH=m
 CONFIG_SND_SOC_AMD_ACP3x=m
+CONFIG_SND_SOC_AMD_ACP3x_ES8336_MACH=m
 CONFIG_SND_SOC_AMD_RV_RT5682_MACH=m
 CONFIG_SND_SOC_AMD_RENOIR=m
 CONFIG_SND_SOC_AMD_RENOIR_MACH=m
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index 150786279257d..605e332cb6f30 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -41,6 +41,19 @@ config SND_SOC_AMD_ACP3x
 	help
 	 This option enables ACP v3.x I2S support on AMD platform
 
+config SND_SOC_AMD_ACP3x_ES8336_MACH
+	tristate "AMD ACP3x support for ES8336"
+	select SND_SOC_ACPI if ACPI
+	select SND_SOC_ES8316
+	depends on ACPI
+	depends on I2C
+	depends on SND_SOC_AMD_ACP3x
+	help
+	 This option enables machine driver for ACP3x platform
+	 using es8336 codec.
+	 Say m if you have such a device.
+	 If unsure select "N".
+
 config SND_SOC_AMD_RV_RT5682_MACH
 	tristate "AMD RV support for RT5682"
 	select CLK_FIXED_FCH
diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile
index 82e1cf864a409..959b3439b03dc 100644
--- a/sound/soc/amd/Makefile
+++ b/sound/soc/amd/Makefile
@@ -5,6 +5,7 @@ snd-soc-acp-rt5645-mach-objs := acp-rt5645.o
 snd-soc-acp-es8336-mach-objs := acp-es8336.o
 snd-soc-acp-rt5682-mach-objs := acp3x-rt5682-max9836.o
 snd-acp-config-objs := acp-config.o
+snd-soc-acp3x-es8336-mach-objs := acp3x-es8336.o
 
 obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o
 obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o
@@ -19,3 +20,4 @@ obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/
 obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o
 obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += rpl/
 obj-$(CONFIG_SND_SOC_AMD_PS) += ps/
+obj-$(CONFIG_SND_SOC_AMD_ACP3x_ES8336_MACH) += snd-soc-acp3x-es8336-mach.o
diff --git a/sound/soc/amd/acp3x-es8336.c b/sound/soc/amd/acp3x-es8336.c
new file mode 100644
index 0000000000000..fd23afa50eb52
--- /dev/null
+++ b/sound/soc/amd/acp3x-es8336.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Machine driver for AMD ACP Audio engine using DA7219 & MAX98357 codec.
+//
+//Copyright 2016 Advanced Micro Devices, Inc.
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+
+#include "raven/acp3x.h"
+
+#define DRV_NAME        "amd-acp3x-essx8336"
+#define SND_CARD_NAME   DRV_NAME
+#define DUAL_CHANNEL	2
+
+struct acp3x_es8336_private {
+	/* struct acp3x_platform_info machine must always be
+	 * the first entry in the structure,
+	 * the acp3x-i2s driver casts the card private data to
+	 * struct acp3x_platform_info
+	 */
+	struct acp3x_platform_info machine;
+	struct device *codec_dev;
+	struct gpio_desc *gpio_speakers;
+	bool speaker_en;
+};
+
+static const unsigned int channels[] = {
+	DUAL_CHANNEL,
+};
+
+static const unsigned int rates[] = {
+	48000
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+	.count = ARRAY_SIZE(rates),
+	.list  = rates,
+	.mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+	.count = ARRAY_SIZE(channels),
+	.list = channels,
+	.mask = 0,
+};
+#define ES8336_MCLK_FREQ (48000 * 1000)
+
+static int acp3x_es8336_codec_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime;
+	struct snd_soc_pcm_runtime *rtd;
+	struct acp3x_platform_info *machine;
+	struct acp3x_es8336_private *priv;
+	struct snd_soc_dai *codec_dai;
+	int ret;
+
+	runtime = substream->runtime;
+	rtd = asoc_substream_to_rtd(substream);
+	priv = snd_soc_card_get_drvdata(rtd->card);
+	machine = &priv->machine;
+	codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, ES8336_MCLK_FREQ, SND_SOC_CLOCK_OUT);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+		return ret;
+	}
+
+	ret =  snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+	                           | SND_SOC_DAIFMT_CBP_CFP);
+	if (ret < 0) {
+		dev_err(rtd->dev, "failed to set DAI fmt: %d\n", ret);
+		return ret;
+	}
+	/* Report to userspace ALSA that we don't support suspending and resuming pcm streams,
+	 * this means that during suspends and resumes of the PC, pulseaudio will not try to resume
+	 * the substream, but will drop the connection and establish a new one.
+	 * This is needed because sometimes after resume pulseaudio is unable to resume the stream
+	 * and no sound can be heard. As a workaround for this issue, pulseaudio needs to be restarted.
+	 */
+	runtime->hw.info &= ~(SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME);
+
+	runtime->hw.channels_max = DUAL_CHANNEL;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+	                           &constraints_channels);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+	                           &constraints_rates);
+	runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+	machine->play_i2s_instance = I2S_SP_INSTANCE;
+
+	return 0;
+}
+
+static int acp3x_es8336_speaker_power_event(struct snd_soc_dapm_widget *w,
+                                            struct snd_kcontrol *kcontrol, int event);
+
+static struct snd_soc_jack es8336_jack;
+
+static struct snd_soc_jack_pin es8336_jack_pins[] = {
+	{
+		.pin	= "Headphone",
+		.mask	= SND_JACK_HEADPHONE,
+	},
+	{
+		.pin	= "Headset Mic",
+		.mask	= SND_JACK_MICROPHONE,
+	 },
+};
+
+static const struct snd_soc_dapm_widget acp3x_es8336_widgets[] = {
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Internal Mic", NULL),
+
+	SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+	                    acp3x_es8336_speaker_power_event,
+	                    SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_route acp3x_es8336_audio_map[] = {
+	{"Headphone", NULL, "HPOL"},
+	{"Headphone", NULL, "HPOR"},
+
+	/*
+	 * There is no separate speaker output instead the speakers are muxed to
+	 * the HP outputs. The mux is controlled Speaker and/or headphone switch.
+	 */
+	{"Speaker", NULL, "HPOL"},
+	{"Speaker", NULL, "HPOR"},
+	{"MIC1", NULL, "Headset Mic"},
+	{"Speaker", NULL, "Speaker Power"},
+};
+
+
+static const struct snd_kcontrol_new acp3x_es8336_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Speaker"),
+	SOC_DAPM_PIN_SWITCH("Headphone"),
+	SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct acpi_gpio_params enable_gpio0 = { 0, 0, true };
+
+static const struct acpi_gpio_mapping acpi_speakers_enable_gpio0[] = {
+	{ "speakers-enable-gpios", &enable_gpio0, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
+	{ }
+};
+
+static int acp3x_es8336_speaker_power_event(struct snd_soc_dapm_widget *w,
+                                            struct snd_kcontrol *kcontrol, int event)
+{
+	struct acp3x_es8336_private *priv = snd_soc_card_get_drvdata(w->dapm->card);
+
+	if (priv->speaker_en == !SND_SOC_DAPM_EVENT_ON(event))
+		return 0;
+
+	priv->speaker_en = !SND_SOC_DAPM_EVENT_ON(event);
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		msleep(70);
+
+	gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_en);
+
+	return 0;
+}
+
+static int acp3x_es8336_init(struct snd_soc_pcm_runtime *runtime)
+{
+	struct snd_soc_component *codec = asoc_rtd_to_codec(runtime, 0)->component;
+	struct snd_soc_card *card = runtime->card;
+	struct acp3x_es8336_private *priv = snd_soc_card_get_drvdata(card);
+	int ret;
+
+	ret = snd_soc_card_jack_new_pins(card, "Headset",
+	                                 SND_JACK_HEADSET | SND_JACK_BTN_0,
+	                                 &es8336_jack, es8336_jack_pins,
+	                                 ARRAY_SIZE(es8336_jack_pins));
+	if (ret) {
+		dev_err(card->dev, "jack creation failed %d\n", ret);
+		return ret;
+	}
+
+	snd_jack_set_key(es8336_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+
+	snd_soc_component_set_jack(codec, &es8336_jack, NULL);
+
+	ret = devm_acpi_dev_add_driver_gpios(codec->dev, acpi_speakers_enable_gpio0);
+	if (ret)
+		dev_warn(codec->dev, "failed to add speaker gpio\n");
+
+	priv->codec_dev = codec->dev;
+	priv->gpio_speakers = gpiod_get_optional(codec->dev, "speakers-enable", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->gpio_speakers)) {
+		dev_err(codec->dev, "could not get speakers-enable GPIO\n");
+		return PTR_ERR(priv->gpio_speakers);
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops acp3x_es8336_ops = {
+	.startup = acp3x_es8336_codec_startup,
+};
+
+
+SND_SOC_DAILINK_DEF(acp3x_i2s,
+                    DAILINK_COMP_ARRAY(COMP_CPU("acp3x_i2s_playcap.0")));
+SND_SOC_DAILINK_DEF(codec,
+                    DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
+SND_SOC_DAILINK_DEF(platform,
+                    DAILINK_COMP_ARRAY(COMP_PLATFORM("acp3x_rv_i2s_dma.0")));
+
+static struct snd_soc_dai_link acp3x_dai_es8336[] = {
+	{
+		.name = "amd-acp3x-es8336-dai",
+		.stream_name = "ES8336 HiFi Play",
+		.stop_dma_first = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.init = acp3x_es8336_init,
+		.ops = &acp3x_es8336_ops,
+		SND_SOC_DAILINK_REG(acp3x_i2s, codec, platform),
+	},
+};
+
+static struct snd_soc_card acp3x_es8336 = {
+	.name = SND_CARD_NAME,
+	.owner = THIS_MODULE,
+	.dai_link = acp3x_dai_es8336,
+	.num_links = ARRAY_SIZE(acp3x_dai_es8336),
+	.dapm_widgets = acp3x_es8336_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(acp3x_es8336_widgets),
+	.dapm_routes = acp3x_es8336_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(acp3x_es8336_audio_map),
+	.controls = acp3x_es8336_controls,
+	.num_controls = ARRAY_SIZE(acp3x_es8336_controls),
+};
+
+static const struct dmi_system_id acp3x_es8336_dmi_table[] = {
+	{
+		.matches = {
+			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+		},
+		.driver_data = &acp3x_es8336,
+	},
+	{
+		.matches = {
+			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+		},
+		.driver_data = &acp3x_es8336,
+	},
+	{
+		.matches = {
+			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+		},
+		.driver_data = &acp3x_es8336,
+	},
+		{
+		.matches = {
+			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"),
+		},
+		.driver_data = &acp3x_es8336,
+	},
+	{}
+};
+
+static int acp3x_probe(struct platform_device *pdev)
+{
+	int ret = -ENODEV;
+	struct device *dev = &pdev->dev;
+	const struct dmi_system_id *dmi_id;
+
+	dmi_id = dmi_first_match(acp3x_es8336_dmi_table);
+	if (dmi_id && dmi_id->driver_data) {
+		struct acp3x_es8336_private *priv;
+		struct snd_soc_card *card;
+
+		dev_info(dev, "matched DMI table with this system, trying to register sound card\n");
+
+		priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+		if (!priv) {
+			dev_err(dev, "can't alloc priv structure\n");
+			return -ENOMEM;
+		}
+
+		card = (struct snd_soc_card *)dmi_id->driver_data;
+
+		card->dev = &pdev->dev;
+		platform_set_drvdata(pdev, card);
+		snd_soc_card_set_drvdata(card, priv);
+		ret = devm_snd_soc_register_card(&pdev->dev, card);
+		if (ret) {
+			dev_err(dev, "failed to register sound card, ret = %d\n", ret);
+			return dev_err_probe(&pdev->dev, ret,
+			                     "devm_snd_soc_register_card(%s) failed\n",
+			                     card->name);
+		} else {
+			dev_info(dev, "successfully registered the sound card\n");
+		}
+	}
+	return ret;
+}
+
+static int acp3x_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct acp3x_es8336_private *priv = snd_soc_card_get_drvdata(card);
+	struct device *dev = &pdev->dev;
+
+	gpiod_put(priv->gpio_speakers);
+
+	dev_info(dev, "removing sound card\n");
+	return 0;
+}
+
+static const struct acpi_device_id acp3x_audio_acpi_match[] = {
+	{"ESSX8336", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, acp3x_audio_acpi_match);
+
+static struct platform_driver acp3x_audio = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+	},
+	.probe = acp3x_probe,
+	.remove = acp3x_remove,
+};
+
+static struct platform_device *acp3x_es8336_snd_device;
+
+static int __init acp3x_es8336_module_init(void)
+{
+	int ret = -ENODEV;
+
+	msleep(5000);
+	ret = platform_driver_register(&acp3x_audio);
+	if (ret < 0) {
+		printk(KERN_ERR DRV_NAME": can't register platform driver\n");
+		return ret;
+	}
+
+	acp3x_es8336_snd_device = platform_device_register_simple(DRV_NAME, 0, NULL, 0);
+	if (IS_ERR(acp3x_es8336_snd_device)) {
+		printk(KERN_ERR DRV_NAME": couldn't register platform device\n");
+		platform_driver_unregister(&acp3x_audio);
+		return PTR_ERR(acp3x_es8336_snd_device);
+	}
+
+	if (!platform_get_drvdata(acp3x_es8336_snd_device)) {
+		platform_device_unregister(acp3x_es8336_snd_device);
+		platform_driver_unregister(&acp3x_audio);
+		return -ENODEV;
+	}
+	printk(KERN_INFO DRV_NAME": platform device registered successfully\n");
+
+	return ret;
+}
+module_init(acp3x_es8336_module_init);
+
+static void __exit acp3x_es8336_module_exit(void)
+{
+	printk(KERN_INFO DRV_NAME": module unloading\n");
+	platform_device_unregister(acp3x_es8336_snd_device);
+	platform_driver_unregister(&acp3x_audio);
+}
+module_exit(acp3x_es8336_module_exit);
+
+MODULE_AUTHOR("posteuca at mutex.one");
+MODULE_DESCRIPTION("ACP3x rev 1 ES8336 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c
index a013a607b3d47..312a16c0e624f 100644
--- a/sound/soc/amd/raven/pci-acp3x.c
+++ b/sound/soc/amd/raven/pci-acp3x.c
@@ -11,6 +11,7 @@
 #include <linux/interrupt.h>
 #include <linux/pm_runtime.h>
 #include <linux/delay.h>
+#include <sound/soc-acpi.h>
 
 #include "acp3x.h"
 
@@ -133,8 +134,10 @@ static int snd_acp3x_probe(struct pci_dev *pci,
 	int ret, i;
 	u32 addr, val;
 
-	/* Raven device detection */
-	if (pci->revision != 0x00)
+	/* Raven and lucienne device detection */
+	if (acpi_dev_get_first_match_dev("ESSX8336", NULL, -1))
+		dev_info(&pci->dev, "use pci-acp for ES8336 codec\n");
+	else if (pci->revision != 0x00)
 		return -ENODEV;
 
 	if (pci_enable_device(pci)) {
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index 056c3082fe02c..20a47573409a9 100644
--- a/sound/soc/codecs/es8316.c
+++ b/sound/soc/codecs/es8316.c
@@ -27,9 +27,9 @@
  * MCLK/LRCK ratios, but we also add ratio 400, which is commonly used on
  * Intel Cherry Trail platforms (19.2MHz MCLK, 48kHz LRCK).
  */
-#define NR_SUPPORTED_MCLK_LRCK_RATIOS 6
+#define NR_SUPPORTED_MCLK_LRCK_RATIOS 7
 static const unsigned int supported_mclk_lrck_ratios[] = {
-	256, 384, 400, 512, 768, 1024
+	256, 384, 400, 512, 768, 1000, 1024
 };
 
 struct es8316_priv {
@@ -465,10 +465,12 @@ static int es8316_pcm_hw_params(struct snd_pcm_substream *substream,
 	u8 bclk_divider;
 	u16 lrck_divider;
 	int i;
+	int mclk_div = 1;
+	unsigned int ratio;
 
 	/* Validate supported sample rates that are autodetected from MCLK */
 	for (i = 0; i < NR_SUPPORTED_MCLK_LRCK_RATIOS; i++) {
-		const unsigned int ratio = supported_mclk_lrck_ratios[i];
+		ratio = supported_mclk_lrck_ratios[i];
 
 		if (es8316->sysclk % ratio != 0)
 			continue;
@@ -477,7 +479,13 @@ static int es8316_pcm_hw_params(struct snd_pcm_substream *substream,
 	}
 	if (i == NR_SUPPORTED_MCLK_LRCK_RATIOS)
 		return -EINVAL;
-	lrck_divider = es8316->sysclk / params_rate(params);
+
+	if (ratio == 1000) {
+		snd_soc_component_update_bits(component, 0x01, 0x80, 0x80);
+		mclk_div = 2;
+	}
+
+	lrck_divider = es8316->sysclk / params_rate(params) / mclk_div;
 	bclk_divider = lrck_divider / 4;
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
@@ -500,6 +508,15 @@ static int es8316_pcm_hw_params(struct snd_pcm_substream *substream,
 		return -EINVAL;
 	}
 
+	static u8 only_one = 0;
+	if (!only_one) {
+		only_one = 1;
+		if (mclk_div == 2)
+			dev_info(component->dev, "Activating MCLK div by 2\n");
+
+		dev_info(component->dev, "Using lrck div = %d, bclk div = %d, wordlen = %d\n", lrck_divider, bclk_divider, wordlen);
+	}
+
 	snd_soc_component_update_bits(component, ES8316_SERDATA_DAC,
 			    ES8316_SERDATA2_LEN_MASK, wordlen);
 	snd_soc_component_update_bits(component, ES8316_SERDATA_ADC,
@@ -520,7 +537,7 @@ static int es8316_mute(struct snd_soc_dai *dai, int mute, int direction)
 }
 
 #define ES8316_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
-			SNDRV_PCM_FMTBIT_S24_LE)
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
 static const struct snd_soc_dai_ops es8316_ops = {
 	.startup = es8316_pcm_startup,
@@ -717,6 +734,41 @@ static int es8316_set_jack(struct snd_soc_component *component,
 	return 0;
 }
 
+#ifdef CONFIG_PM
+
+static int es8316_suspend(struct snd_soc_component *component)
+{
+	struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+	regcache_cache_only(es8316->regmap, true);
+
+	return 0;
+}
+
+static int es8316_resume(struct snd_soc_component *component)
+{
+	struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+	regcache_cache_only(es8316->regmap, false);
+	/* Reset codec and enable current state machine */
+	snd_soc_component_write(component, ES8316_RESET, 0x3f);
+	usleep_range(5000, 5500);
+	snd_soc_component_write(component, ES8316_RESET, ES8316_RESET_CSM_ON);
+	msleep(30);
+
+	snd_soc_component_write(component, ES8316_SYS_VMIDSEL, 0xff);
+
+	snd_soc_component_write(component, ES8316_CLKMGR_ADCOSR, 0x32);
+
+	regcache_mark_dirty(es8316->regmap);
+	regcache_sync(es8316->regmap);
+
+	return 0;
+}
+#else
+#define es8316_suspend NULL
+#define es8316_resume NULL
+#endif
+
 static int es8316_probe(struct snd_soc_component *component)
 {
 	struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
@@ -767,26 +819,6 @@ static void es8316_remove(struct snd_soc_component *component)
 	clk_disable_unprepare(es8316->mclk);
 }
 
-static int es8316_resume(struct snd_soc_component *component)
-{
-	struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
-
-	regcache_cache_only(es8316->regmap, false);
-	regcache_sync(es8316->regmap);
-
-	return 0;
-}
-
-static int es8316_suspend(struct snd_soc_component *component)
-{
-	struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
-
-	regcache_cache_only(es8316->regmap, true);
-	regcache_mark_dirty(es8316->regmap);
-
-	return 0;
-}
-
 static const struct snd_soc_component_driver soc_component_dev_es8316 = {
 	.probe			= es8316_probe,
 	.remove			= es8316_remove,
@@ -805,6 +837,9 @@ static const struct snd_soc_component_driver soc_component_dev_es8316 = {
 
 static const struct regmap_range es8316_volatile_ranges[] = {
 	regmap_reg_range(ES8316_GPIO_FLAG, ES8316_GPIO_FLAG),
+	regmap_reg_range(ES8316_RESET, ES8316_RESET),
+	regmap_reg_range(ES8316_SYS_VMIDSEL,ES8316_SYS_VMIDSEL),
+	regmap_reg_range(ES8316_CLKMGR_ADCOSR,ES8316_CLKMGR_ADCOSR),
 };
 
 static const struct regmap_access_table es8316_volatile_table = {
-- 
2.33.5



Подробная информация о списке рассылки devel-kernel