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

nickel на altlinux.org nickel на altlinux.org
Чт Мар 2 14:58:20 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

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    | 437 ++++++++++++++++++++++++++++++++
 sound/soc/amd/raven/pci-acp3x.c |   4 +-
 sound/soc/codecs/es8316.c       | 130 ++++++----
 6 files changed, 534 insertions(+), 53 deletions(-)
 create mode 100644 sound/soc/amd/acp3x-es8336.c

diff --git a/config b/config
index 76e54c37e497a..722dcbd3c74f9 100644
--- a/config
+++ b/config
@@ -6823,6 +6823,7 @@ CONFIG_SND_SOC_AMD_ACP=m
 CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH=m
 CONFIG_SND_SOC_AMD_CZ_RT5645_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 49ff5e73e9bad..5d03db45751d1 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -28,6 +28,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 SND_SOC_RT5682_I2C
diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile
index 07150d26f3155..0f0b6ca7bd9cb 100644
--- a/sound/soc/amd/Makefile
+++ b/sound/soc/amd/Makefile
@@ -3,6 +3,7 @@ acp_audio_dma-objs := acp-pcm-dma.o
 snd-soc-acp-da7219mx98357-mach-objs := acp-da7219-max98357a.o
 snd-soc-acp-rt5645-mach-objs := acp-rt5645.o
 snd-soc-acp-rt5682-mach-objs := acp3x-rt5682-max9836.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
@@ -11,3 +12,4 @@ obj-$(CONFIG_SND_SOC_AMD_ACP3x) += raven/
 obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o
 obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
 obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/
+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..f930cc258dfc9
--- /dev/null
+++ b/sound/soc/amd/acp3x-es8336.c
@@ -0,0 +1,437 @@
+// 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_hw_params(struct snd_pcm_substream *substream,
+  struct snd_pcm_hw_params *params)
+  {
+  struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+  struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+  struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+  printk(KERN_INFO"mysound1: entered card set hw params\n");
+
+  //snd_mask_none(fmt);
+  //snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+
+  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(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 = {
+	//	.hw_params = acp3x_es8336_hw_params,
+	.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");
+		}
+	}
+	else {
+		dev_warn(dev, "this system has a ES8336 codec defined in ACPI, "
+		         "but the driver doesn't have this system registered in DMI table\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 platform_device_id board_ids[] = {
+  {
+  .name = "acp3x-essx8336",
+  },
+  { }
+  };
+*/
+//MODULE_DEVICE_TABLE(platform, board_ids);
+
+
+
+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,
+		//.acpi_match_table = ACPI_PTR(acp3x_audio_acpi_match),
+		.pm = &snd_soc_pm_ops,
+	},
+	.probe = acp3x_probe,
+	.remove = acp3x_remove,
+	//.id_table = board_ids,
+};
+
+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)) {
+		printk(KERN_ERR DRV_NAME": probe of hw failed\n");
+		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");
+//MODULE_ALIAS("dmi:bvnHUAWEI:*:*:*:*:*:pnKLVL-WXXW:pvrM1010:rvnHUAWEI:rnKLVL-WXXW-PCB:rvrM1010:*:*:*:skuC100:*")
+//MODULE_ALIAS("platform:acp3x-essx8336");
diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c
index a013a607b3d47..cdb8773b11f13 100644
--- a/sound/soc/amd/raven/pci-acp3x.c
+++ b/sound/soc/amd/raven/pci-acp3x.c
@@ -133,8 +133,8 @@ 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 (pci->revision != 0x00 && pci->revision != 0x01)
 		return -ENODEV;
 
 	if (pci_enable_device(pci)) {
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index 9778c988e49e1..d3afbfbe0758c 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 {
@@ -401,10 +401,8 @@ static int es8316_set_dai_fmt(struct snd_soc_dai *codec_dai,
 	u8 clksw;
 	u8 mask;
 
-	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
-		dev_err(component->dev, "Codec driver only supports slave mode\n");
-		return -EINVAL;
-	}
+	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBP_CFP)
+		serdata1 |= ES8316_SERDATA1_MASTER;
 
 	if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) {
 		dev_err(component->dev, "Codec driver only supports I2S format\n");
@@ -464,11 +462,15 @@ static int es8316_pcm_hw_params(struct snd_pcm_substream *substream,
 	struct snd_soc_component *component = dai->component;
 	struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
 	u8 wordlen = 0;
+	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;
@@ -478,27 +480,52 @@ static int es8316_pcm_hw_params(struct snd_pcm_substream *substream,
 	if (i == NR_SUPPORTED_MCLK_LRCK_RATIOS)
 		return -EINVAL;
 
+	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:
 		wordlen = ES8316_SERDATA2_LEN_16;
+		bclk_divider /= 16;
 		break;
 	case SNDRV_PCM_FORMAT_S20_3LE:
 		wordlen = ES8316_SERDATA2_LEN_20;
+		bclk_divider /= 20;
 		break;
 	case SNDRV_PCM_FORMAT_S24_LE:
 		wordlen = ES8316_SERDATA2_LEN_24;
+		bclk_divider /= 24;
 		break;
 	case SNDRV_PCM_FORMAT_S32_LE:
 		wordlen = ES8316_SERDATA2_LEN_32;
+		bclk_divider /= 32;
 		break;
 	default:
 		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,
 			    ES8316_SERDATA2_LEN_MASK, wordlen);
+	snd_soc_component_update_bits(component, ES8316_SERDATA1, 0x1f, bclk_divider);
+	snd_soc_component_update_bits(component, ES8316_CLKMGR_ADCDIV1, 0x0f, lrck_divider >> 8);
+	snd_soc_component_update_bits(component, ES8316_CLKMGR_ADCDIV2, 0xff, lrck_divider & 0xff);
+	snd_soc_component_update_bits(component, ES8316_CLKMGR_DACDIV1, 0x0f, lrck_divider >> 8);
+	snd_soc_component_update_bits(component, ES8316_CLKMGR_DACDIV2, 0xff, lrck_divider & 0xff);
 	return 0;
 }
 
@@ -510,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,
@@ -707,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);
@@ -728,8 +790,6 @@ static int es8316_probe(struct snd_soc_component *component)
 		return ret;
 	}
 
-	snd_soc_component_init_regmap(component, es8316->regmap);
-
 	/* Reset codec and enable current state machine */
 	snd_soc_component_write(component, ES8316_RESET, 0x3f);
 	usleep_range(5000, 5500);
@@ -756,46 +816,14 @@ static void es8316_remove(struct snd_soc_component *component)
 {
 	struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
 
-	snd_soc_component_exit_regmap(component);
-
 	clk_disable_unprepare(es8316->mclk);
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int es8316_suspend(struct device *dev)
-{
-	struct es8316_priv *es8316 = dev_get_drvdata(dev);
-
-	dev_dbg(dev, "%s: Enter\n", __func__);
-
-	regcache_cache_only(es8316->regmap, true);
-	regcache_mark_dirty(es8316->regmap);
-
-	return 0;
-}
-
-static int es8316_resume(struct device *dev)
-{
-	struct es8316_priv *es8316 = dev_get_drvdata(dev);
-
-	dev_dbg(dev, "%s: Enter\n", __func__);
-
-	regcache_cache_only(es8316->regmap, false);
-	regcache_sync(es8316->regmap);
-
-	es8316_irq(es8316->irq, es8316);
-
-	return 0;
-}
-#endif
-
-static const struct dev_pm_ops es8316_pm = {
-	SET_SYSTEM_SLEEP_PM_OPS(es8316_suspend, es8316_resume)
-};
-
 static const struct snd_soc_component_driver soc_component_dev_es8316 = {
 	.probe			= es8316_probe,
 	.remove			= es8316_remove,
+	.resume			= es8316_resume,
+	.suspend		= es8316_suspend,
 	.set_jack		= es8316_set_jack,
 	.controls		= es8316_snd_controls,
 	.num_controls		= ARRAY_SIZE(es8316_snd_controls),
@@ -805,11 +833,13 @@ static const struct snd_soc_component_driver soc_component_dev_es8316 = {
 	.num_dapm_routes	= ARRAY_SIZE(es8316_dapm_routes),
 	.use_pmdown_time	= 1,
 	.endianness		= 1,
-	.non_legacy_dai_naming	= 1,
 };
 
 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 = {
@@ -820,15 +850,14 @@ static const struct regmap_access_table es8316_volatile_table = {
 static const struct regmap_config es8316_regmap = {
 	.reg_bits = 8,
 	.val_bits = 8,
+	.use_single_read = true,
+	.use_single_write = true,
 	.max_register = 0x53,
 	.volatile_table	= &es8316_volatile_table,
 	.cache_type = REGCACHE_RBTREE,
-	.use_single_read = true,
-	.use_single_write = true,
 };
 
-static int es8316_i2c_probe(struct i2c_client *i2c_client,
-			    const struct i2c_device_id *id)
+static int es8316_i2c_probe(struct i2c_client *i2c_client)
 {
 	struct device *dev = &i2c_client->dev;
 	struct es8316_priv *es8316;
@@ -889,9 +918,8 @@ static struct i2c_driver es8316_i2c_driver = {
 		.name			= "es8316",
 		.acpi_match_table	= ACPI_PTR(es8316_acpi_match),
 		.of_match_table		= of_match_ptr(es8316_of_match),
-		.pm			= &es8316_pm,
 	},
-	.probe		= es8316_i2c_probe,
+	.probe_new	= es8316_i2c_probe,
 	.id_table	= es8316_i2c_id,
 };
 module_i2c_driver(es8316_i2c_driver);
-- 
2.33.6



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