[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