kernel/0018-feat-Porting-eswin-audio-to-6.6-kernel.patch

3334 lines
99 KiB
Diff
Raw Normal View History

2024-12-19 21:34:44 +00:00
From 2218519dddb322b2b69745e011e830fa6f7a163b Mon Sep 17 00:00:00 2001
2024-12-15 18:29:23 +00:00
From: yangqiang <yangqiang1@eswincomputing.com>
Date: Tue, 21 May 2024 09:32:04 +0800
Subject: [PATCH 018/219] feat:Porting eswin audio to 6.6 kernel
Changelogs:
1. Porting eswin audio to 6.6 kernel.
---
arch/riscv/configs/win2030_defconfig | 5 +-
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/codecs/Kconfig | 1 +
sound/soc/codecs/Makefile | 1 +
sound/soc/codecs/eswin/Kconfig | 20 +
sound/soc/codecs/eswin/Makefile | 3 +
sound/soc/codecs/eswin/es8328-i2c.c | 65 ++
sound/soc/codecs/eswin/es8328.c | 1101 ++++++++++++++++++++++++++
sound/soc/codecs/eswin/es8328.h | 299 +++++++
sound/soc/eswin/Kconfig | 11 +
sound/soc/eswin/Makefile | 5 +
sound/soc/eswin/esw-audio-proc.c | 488 ++++++++++++
sound/soc/eswin/esw-audio-proc.h | 23 +
sound/soc/eswin/esw-i2s.c | 973 +++++++++++++++++++++++
sound/soc/eswin/esw-i2s.h | 181 +++++
16 files changed, 3177 insertions(+), 1 deletion(-)
create mode 100755 sound/soc/codecs/eswin/Kconfig
create mode 100755 sound/soc/codecs/eswin/Makefile
create mode 100644 sound/soc/codecs/eswin/es8328-i2c.c
create mode 100644 sound/soc/codecs/eswin/es8328.c
create mode 100644 sound/soc/codecs/eswin/es8328.h
create mode 100644 sound/soc/eswin/Kconfig
create mode 100644 sound/soc/eswin/Makefile
create mode 100644 sound/soc/eswin/esw-audio-proc.c
create mode 100644 sound/soc/eswin/esw-audio-proc.h
create mode 100755 sound/soc/eswin/esw-i2s.c
create mode 100644 sound/soc/eswin/esw-i2s.h
diff --git a/arch/riscv/configs/win2030_defconfig b/arch/riscv/configs/win2030_defconfig
index 9149eabecee2..5d25e67b935d 100644
--- a/arch/riscv/configs/win2030_defconfig
+++ b/arch/riscv/configs/win2030_defconfig
@@ -177,7 +177,10 @@ CONFIG_SND=y
CONFIG_SND_SOC=y
CONFIG_SND_SOC_SOF_TOPLEVEL=y
CONFIG_SND_SOC_SOF_OF=y
-CONFIG_SND_SOC_ES8316=y
+CONFIG_SND_ESWIN_DW_I2S=y
+CONFIG_SND_SOC_HDMI_CODEC=y
+CONFIG_ESWIN_SND_SOC_CODECS=y
+CONFIG_ESWIN_SND_ES8388_CODEC=y
CONFIG_SND_SIMPLE_CARD=y
CONFIG_SND_AUDIO_GRAPH_CARD=y
CONFIG_USB_ULPI_BUS=y
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 439fa631c342..dfd26f13970c 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -114,6 +114,7 @@ source "sound/soc/uniphier/Kconfig"
source "sound/soc/ux500/Kconfig"
source "sound/soc/xilinx/Kconfig"
source "sound/soc/xtensa/Kconfig"
+source "sound/soc/eswin/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 8376fdb217ed..7c3b9197e7b2 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -71,3 +71,4 @@ obj-$(CONFIG_SND_SOC) += uniphier/
obj-$(CONFIG_SND_SOC) += ux500/
obj-$(CONFIG_SND_SOC) += xilinx/
obj-$(CONFIG_SND_SOC) += xtensa/
+obj-$(CONFIG_SND_SOC) += eswin/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index f1e1dbc509f6..319d89477279 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -2404,4 +2404,5 @@ config SND_SOC_LPASS_TX_MACRO
select SND_SOC_LPASS_MACRO_COMMON
tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)"
+source "sound/soc/codecs/eswin/Kconfig"
endmenu
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index a87e56938ce5..ea3f39cf3d70 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -772,3 +772,4 @@ obj-$(CONFIG_SND_SOC_LPASS_TX_MACRO) += snd-soc-lpass-tx-macro.o
# Mux
obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o
+obj-$(CONFIG_ESWIN_SND_SOC_CODECS) += eswin/
diff --git a/sound/soc/codecs/eswin/Kconfig b/sound/soc/codecs/eswin/Kconfig
new file mode 100755
index 000000000000..d3c7039aa8e9
--- /dev/null
+++ b/sound/soc/codecs/eswin/Kconfig
@@ -0,0 +1,20 @@
+menuconfig ESWIN_SND_SOC_CODECS
+ bool "ESWIN CODEC drivers"
+ default n
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the ESWIN Asoc interface. You will also need
+ to select the audio interfaces to support below.
+
+#if ESWIN_SND_SOC_CODECS
+
+config ESWIN_SND_ES8388_CODEC
+ bool "ESWIN Audio es8388 codec"
+ depends on ESWIN_SND_SOC_CODECS
+ default n
+ help
+ ESWIN Audio codec,
+ es8388 codec,
+ this codec is internal
+
+#endif #ESWIN_SND_SOC_CODECS
diff --git a/sound/soc/codecs/eswin/Makefile b/sound/soc/codecs/eswin/Makefile
new file mode 100755
index 000000000000..ee696d62096c
--- /dev/null
+++ b/sound/soc/codecs/eswin/Makefile
@@ -0,0 +1,3 @@
+esw_es8328_codec-objs := es8328-i2c.o es8328.o
+
+obj-$(CONFIG_ESWIN_SND_ES8388_CODEC) += esw_es8328_codec.o
\ No newline at end of file
diff --git a/sound/soc/codecs/eswin/es8328-i2c.c b/sound/soc/codecs/eswin/es8328-i2c.c
new file mode 100644
index 000000000000..d6756c46a4a5
--- /dev/null
+++ b/sound/soc/codecs/eswin/es8328-i2c.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8328-i2c.c -- ES8328 ALSA SoC I2C Audio driver
+ *
+ * Copyright 2014 Sutajio Ko-Usagi PTE LTD
+ *
+ * Author: Sean Cross <xobs@kosagi.com>
+ */
+
+/*
+ * Copyright (C) 2021 ESWIN, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "es8328.h"
+
+static const struct i2c_device_id es8328_id[] = {
+ { "es8328", 0 },
+ { "es8388", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8328_id);
+
+static const struct of_device_id es8328_of_match[] = {
+ { .compatible = "eswin,es8388", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8328_of_match);
+
+static int es8328_i2c_probe(struct i2c_client *i2c)
+{
+ return es8328_probe(&i2c->dev,
+ devm_regmap_init_i2c(i2c, &es8328_regmap_config));
+}
+
+static struct i2c_driver es8328_i2c_driver = {
+ .driver = {
+ .name = "es8328",
+ .of_match_table = es8328_of_match,
+ },
+ .probe = es8328_i2c_probe,
+ .id_table = es8328_id,
+};
+
+module_i2c_driver(es8328_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES8328 audio CODEC I2C driver");
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/eswin/es8328.c b/sound/soc/codecs/eswin/es8328.c
new file mode 100644
index 000000000000..2c7a4031fa44
--- /dev/null
+++ b/sound/soc/codecs/eswin/es8328.c
@@ -0,0 +1,1101 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8328.c -- ES8328 ALSA SoC Audio driver
+ *
+ * Copyright 2014 Sutajio Ko-Usagi PTE LTD
+ *
+ * Author: Sean Cross <xobs@kosagi.com>
+ */
+
+/*
+ * Copyright (C) 2021 ESWIN, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/gpio/consumer.h>
+#include "es8328.h"
+
+#define MIN_CHANNEL_NUM 2
+#define MAX_CHANNEL_NUM 2
+
+static const unsigned int rates_12288[] = {
+ 8000, 12000, 16000, 24000, 32000, 48000, 96000,
+};
+
+static const int ratios_12288[] = {
+ 10, 7, 6, 4, 3, 2, 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_12288 = {
+ .count = ARRAY_SIZE(rates_12288),
+ .list = rates_12288,
+};
+
+static const unsigned int rates_11289[] = {
+ 8018, 11025, 22050, 44100, 88200,
+};
+
+static const int ratios_11289[] = {
+ 9, 7, 4, 2, 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_11289 = {
+ .count = ARRAY_SIZE(rates_11289),
+ .list = rates_11289,
+};
+
+/* regulator supplies for sgtl5000, VDDD is an optional external supply */
+enum sgtl5000_regulator_supplies {
+ DVDD,
+ AVDD,
+ PVDD,
+ HPVDD,
+ ES8328_SUPPLY_NUM
+};
+
+/* vddd is optional supply */
+static const char * const supply_names[ES8328_SUPPLY_NUM] = {
+ "DVDD",
+ "AVDD",
+ "PVDD",
+ "HPVDD",
+};
+
+#define ES8328_RATES (SNDRV_PCM_RATE_192000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_8000_48000)
+#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct es8328_priv {
+ struct regmap *regmap;
+ struct clk *clk;
+ int playback_fs;
+ bool deemph;
+ int mclkdiv2;
+ const struct snd_pcm_hw_constraint_list *sysclk_constraints;
+ const int *mclk_ratios;
+ bool provider;
+ struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
+
+ u32 eswin_plat;
+ struct snd_soc_component *component;
+ struct gpio_desc *front_jack_gpio;
+ struct gpio_desc *back_jack_gpio;
+};
+
+/*
+ * ES8328 Controls
+ */
+
+static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert",
+ "L + R Invert"};
+static SOC_ENUM_SINGLE_DECL(adcpol,
+ ES8328_ADCCONTROL6, 6, adcpol_txt);
+
+static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
+
+static const struct {
+ int rate;
+ unsigned int val;
+} deemph_settings[] = {
+ { 0, ES8328_DACCONTROL6_DEEMPH_OFF },
+ { 32000, ES8328_DACCONTROL6_DEEMPH_32k },
+ { 44100, ES8328_DACCONTROL6_DEEMPH_44_1k },
+ { 48000, ES8328_DACCONTROL6_DEEMPH_48k },
+};
+
+static int es8328_set_deemph(struct snd_soc_component *component)
+{
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ int val, i, best;
+
+ /*
+ * If we're using deemphasis select the nearest available sample
+ * rate.
+ */
+ if (es8328->deemph) {
+ best = 0;
+ for (i = 1; i < ARRAY_SIZE(deemph_settings); i++) {
+ if (abs(deemph_settings[i].rate - es8328->playback_fs) <
+ abs(deemph_settings[best].rate - es8328->playback_fs))
+ best = i;
+ }
+
+ val = deemph_settings[best].val;
+ } else {
+ val = ES8328_DACCONTROL6_DEEMPH_OFF;
+ }
+
+ dev_dbg(component->dev, "Set deemphasis %d\n", val);
+
+ return snd_soc_component_update_bits(component, ES8328_DACCONTROL6,
+ ES8328_DACCONTROL6_DEEMPH_MASK, val);
+}
+
+static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = es8328->deemph;
+ return 0;
+}
+
+static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ unsigned int deemph = ucontrol->value.integer.value[0];
+ int ret;
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ if (es8328->deemph == deemph)
+ return 0;
+
+ ret = es8328_set_deemph(component);
+ if (ret < 0)
+ return ret;
+
+ es8328->deemph = deemph;
+
+ return 1;
+}
+static int dump_flag = 0;
+static u32 g_reg = 1;
+static u32 g_get_cnt = 0;
+static u32 g_put_cnt = 0;
+
+int esw_codec_dump_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = dump_flag;
+ return 0;
+}
+
+int esw_codec_dump_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+int esw_codec_dump_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int dump_onoff = ucontrol->value.integer.value[0];
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ u32 reg, ret, i;
+
+ printk("codec dump onoff:%d\n", dump_onoff);
+ dump_flag = dump_onoff;
+ if (dump_onoff == true) {
+ printk("statr codec dump\n");
+ for (i = 0; i < 53; i++) {
+ ret = regmap_read(component->regmap, i, &reg);
+ if (ret != 0) {
+ printk("reag reg[%d] failed!\n", i);
+ }
+ printk("reg[%d]:0x%x\n", i, reg);
+ }
+ }
+ return 0;
+}
+
+int esw_codec_reg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ u32 val;
+ int ret;
+
+ g_get_cnt++;
+
+ if (g_get_cnt == 1) {
+ ucontrol->value.integer.value[0] = g_reg;
+ } else {
+ ret = regmap_read(component->regmap, g_reg, &val);
+ if (ret != 0) {
+ printk("read reg[%d] failed!\n", g_reg);
+ }
+ printk("codec read reg[%d]:0x%x\n", g_reg, val);
+ ucontrol->value.integer.value[1] = val;
+ g_get_cnt = 0;
+ }
+ return 0;
+}
+
+int esw_codec_reg_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 256;
+ return 0;
+}
+
+int esw_codec_reg_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+
+ g_put_cnt++;
+
+ if (g_put_cnt == 1) {
+ g_reg = ucontrol->value.integer.value[0];
+ } else {
+ printk("codec write reg:%d, val:0x%x\n", g_reg, (u32)ucontrol->value.integer.value[1]);
+ ret = regmap_write(component->regmap, g_reg, (u32)ucontrol->value.integer.value[1]);
+ if (ret != 0) {
+ printk("write reg[%d] failed!\n", g_reg);
+ }
+ g_put_cnt = 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new es8328_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Capture Digital Volume",
+ ES8328_ADCCONTROL8, ES8328_ADCCONTROL9,
+ 0, 0xc0, 1, dac_adc_tlv),
+ SOC_SINGLE("Capture ZC Switch", ES8328_ADCCONTROL7, 5, 1, 0),
+
+ SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+ es8328_get_deemph, es8328_put_deemph),
+
+ SOC_ENUM("Capture Polarity", adcpol),
+
+ SOC_SINGLE_TLV("Left Mixer Left Bypass Volume",
+ ES8328_DACCONTROL17, 3, 7, 1, bypass_tlv),
+ SOC_SINGLE_TLV("Left Mixer Right Bypass Volume",
+ ES8328_DACCONTROL19, 3, 7, 1, bypass_tlv),
+ SOC_SINGLE_TLV("Right Mixer Left Bypass Volume",
+ ES8328_DACCONTROL18, 3, 7, 1, bypass_tlv),
+ SOC_SINGLE_TLV("Right Mixer Right Bypass Volume",
+ ES8328_DACCONTROL20, 3, 7, 1, bypass_tlv),
+
+ SOC_DOUBLE_R_TLV("PCM Volume",
+ ES8328_LDACVOL, ES8328_RDACVOL,
+ 0, ES8328_LDACVOL_MAX, 1, dac_adc_tlv),
+
+ SOC_DOUBLE_R_TLV("Output 1 Playback Volume",
+ ES8328_LOUT1VOL, ES8328_ROUT1VOL,
+ 0, ES8328_OUT1VOL_MAX, 0, play_tlv),
+
+ SOC_DOUBLE_R_TLV("Output 2 Playback Volume",
+ ES8328_LOUT2VOL, ES8328_ROUT2VOL,
+ 0, ES8328_OUT2VOL_MAX, 0, play_tlv),
+
+ SOC_DOUBLE_TLV("Mic PGA Volume", ES8328_ADCCONTROL1,
+ 4, 0, 8, 0, mic_tlv),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "Reg Dump",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = esw_codec_dump_info,
+ .get = esw_codec_dump_get,
+ .put = esw_codec_dump_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Reg Write",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = esw_codec_reg_info,
+ .get = esw_codec_reg_get,
+ .put = esw_codec_reg_put,
+ }
+};
+
+/*
+ * DAPM Controls
+ */
+
+static const char * const es8328_line_texts[] = {
+ "Line 1", "Line 2", "PGA", "Differential"};
+
+static const struct soc_enum es8328_lline_enum =
+ SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 3,
+ ARRAY_SIZE(es8328_line_texts),
+ es8328_line_texts);
+static const struct snd_kcontrol_new es8328_left_line_controls =
+ SOC_DAPM_ENUM("Route", es8328_lline_enum);
+
+static const struct soc_enum es8328_rline_enum =
+ SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 0,
+ ARRAY_SIZE(es8328_line_texts),
+ es8328_line_texts);
+static const struct snd_kcontrol_new es8328_right_line_controls =
+ SOC_DAPM_ENUM("Route", es8328_rline_enum);
+
+/* Left Mixer */
+static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 6, 1, 0),
+ SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 6, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new es8328_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 6, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 6, 1, 0),
+};
+
+static const char * const es8328_pga_sel[] = {
+ "Line 1", "Line 2", "Line 3", "Differential"};
+
+/* Left PGA Mux */
+static const struct soc_enum es8328_lpga_enum =
+ SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 6,
+ ARRAY_SIZE(es8328_pga_sel),
+ es8328_pga_sel);
+static const struct snd_kcontrol_new es8328_left_pga_controls =
+ SOC_DAPM_ENUM("Route", es8328_lpga_enum);
+
+/* Right PGA Mux */
+static const struct soc_enum es8328_rpga_enum =
+ SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 4,
+ ARRAY_SIZE(es8328_pga_sel),
+ es8328_pga_sel);
+static const struct snd_kcontrol_new es8328_right_pga_controls =
+ SOC_DAPM_ENUM("Route", es8328_rpga_enum);
+
+/* Differential Mux */
+static const char * const es8328_diff_sel[] = {"Line 1", "Line 2"};
+static SOC_ENUM_SINGLE_DECL(diffmux,
+ ES8328_ADCCONTROL3, 7, es8328_diff_sel);
+static const struct snd_kcontrol_new es8328_diffmux_controls =
+ SOC_DAPM_ENUM("Route", diffmux);
+
+/* Mono ADC Mux */
+static const char * const es8328_mono_mux[] = {"Stereo", "Mono (Left)",
+ "Mono (Right)", "Digital Mono"};
+static SOC_ENUM_SINGLE_DECL(monomux,
+ ES8328_ADCCONTROL3, 3, es8328_mono_mux);
+static const struct snd_kcontrol_new es8328_monomux_controls =
+ SOC_DAPM_ENUM("Route", monomux);
+
+static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_diffmux_controls),
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_monomux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_monomux_controls),
+
+ SND_SOC_DAPM_MUX("Left PGA Mux", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_AINL_OFF, 1,
+ &es8328_left_pga_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_AINR_OFF, 1,
+ &es8328_right_pga_controls),
+
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_right_line_controls),
+
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_ADCR_OFF, 1),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_ADCL_OFF, 1),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_MIC_BIAS_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_ADC_BIAS_GEN_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC STM", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACSTM_RESET, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC STM", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC DIG", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACDIG_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC DIG", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC DLL", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACDLL_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC DLL", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC Vref", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Vref", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACVREF_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8328_DACPOWER,
+ ES8328_DACPOWER_RDAC_OFF, 1),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER,
+ ES8328_DACPOWER_LDAC_OFF, 1),
+
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &es8328_left_mixer_controls[0],
+ ARRAY_SIZE(es8328_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &es8328_right_mixer_controls[0],
+ ARRAY_SIZE(es8328_right_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right Out 2", ES8328_DACPOWER,
+ ES8328_DACPOWER_ROUT2_ON, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", ES8328_DACPOWER,
+ ES8328_DACPOWER_LOUT2_ON, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", ES8328_DACPOWER,
+ ES8328_DACPOWER_ROUT1_ON, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", ES8328_DACPOWER,
+ ES8328_DACPOWER_LOUT1_ON, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("LINPUT2"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT2"),
+};
+
+static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
+
+ { "Left Line Mux", "Line 1", "LINPUT1" },
+ { "Left Line Mux", "Line 2", "LINPUT2" },
+ { "Left Line Mux", "PGA", "Left PGA Mux" },
+ { "Left Line Mux", "Differential", "Differential Mux" },
+
+ { "Right Line Mux", "Line 1", "RINPUT1" },
+ { "Right Line Mux", "Line 2", "RINPUT2" },
+ { "Right Line Mux", "PGA", "Right PGA Mux" },
+ { "Right Line Mux", "Differential", "Differential Mux" },
+
+ { "Left PGA Mux", "Line 1", "LINPUT1" },
+ { "Left PGA Mux", "Line 2", "LINPUT2" },
+ { "Left PGA Mux", "Differential", "Differential Mux" },
+
+ { "Right PGA Mux", "Line 1", "RINPUT1" },
+ { "Right PGA Mux", "Line 2", "RINPUT2" },
+ { "Right PGA Mux", "Differential", "Differential Mux" },
+
+ { "Differential Mux", "Line 1", "LINPUT1" },
+ { "Differential Mux", "Line 1", "RINPUT1" },
+ { "Differential Mux", "Line 2", "LINPUT2" },
+ { "Differential Mux", "Line 2", "RINPUT2" },
+
+ { "Left ADC Mux", "Stereo", "Left PGA Mux" },
+ { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
+ { "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
+
+ { "Right ADC Mux", "Stereo", "Right PGA Mux" },
+ { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
+ { "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
+
+ { "Left ADC", NULL, "Left ADC Mux" },
+ { "Right ADC", NULL, "Right ADC Mux" },
+
+ { "ADC DIG", NULL, "ADC STM" },
+ { "ADC DIG", NULL, "ADC Vref" },
+ { "ADC DIG", NULL, "ADC DLL" },
+
+ { "Left ADC", NULL, "ADC DIG" },
+ { "Right ADC", NULL, "ADC DIG" },
+
+ { "Mic Bias", NULL, "Mic Bias Gen" },
+
+ { "Left ADC", NULL, "Mic Bias" },
+ { "Right ADC", NULL, "Mic Bias" },
+
+ { "Left Line Mux", "Line 1", "LINPUT1" },
+ { "Left Line Mux", "Line 2", "LINPUT2" },
+ { "Left Line Mux", "PGA", "Left PGA Mux" },
+ { "Left Line Mux", "Differential", "Differential Mux" },
+
+ { "Right Line Mux", "Line 1", "RINPUT1" },
+ { "Right Line Mux", "Line 2", "RINPUT2" },
+ { "Right Line Mux", "PGA", "Right PGA Mux" },
+ { "Right Line Mux", "Differential", "Differential Mux" },
+
+ { "Left Out 1", NULL, "Left DAC" },
+ { "Right Out 1", NULL, "Right DAC" },
+ { "Left Out 2", NULL, "Left DAC" },
+ { "Right Out 2", NULL, "Right DAC" },
+
+ { "Left Mixer", "Playback Switch", "Left DAC" },
+ { "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
+ { "Left Mixer", "Right Playback Switch", "Right DAC" },
+ { "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+ { "Right Mixer", "Left Playback Switch", "Left DAC" },
+ { "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
+ { "Right Mixer", "Playback Switch", "Right DAC" },
+ { "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+ { "DAC DIG", NULL, "DAC STM" },
+ { "DAC DIG", NULL, "DAC Vref" },
+ { "DAC DIG", NULL, "DAC DLL" },
+
+ { "Left DAC", NULL, "DAC DIG" },
+ { "Right DAC", NULL, "DAC DIG" },
+
+ { "Left Out 1", NULL, "Left Mixer" },
+ { "LOUT1", NULL, "Left Out 1" },
+ { "Right Out 1", NULL, "Right Mixer" },
+ { "ROUT1", NULL, "Right Out 1" },
+
+ { "Left Out 2", NULL, "Left Mixer" },
+ { "LOUT2", NULL, "Left Out 2" },
+ { "Right Out 2", NULL, "Right Mixer" },
+ { "ROUT2", NULL, "Right Out 2" },
+};
+
+static int es8328_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ return snd_soc_component_update_bits(dai->component, ES8328_DACCONTROL3,
+ ES8328_DACCONTROL3_DACMUTE,
+ mute ? ES8328_DACCONTROL3_DACMUTE : 0);
+}
+
+static int es8328_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+
+ if (es8328->provider && es8328->sysclk_constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ es8328->sysclk_constraints);
+
+ return 0;
+}
+
+static int es8328_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ int i;
+ int reg;
+ int wl;
+ int ratio;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = ES8328_DACCONTROL2;
+ else
+ reg = ES8328_ADCCONTROL5;
+
+ if (es8328->provider) {
+ if (!es8328->sysclk_constraints) {
+ dev_err(component->dev, "No MCLK configured\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < es8328->sysclk_constraints->count; i++)
+ if (es8328->sysclk_constraints->list[i] ==
+ params_rate(params))
+ break;
+
+ if (i == es8328->sysclk_constraints->count) {
+ dev_err(component->dev,
+ "LRCLK %d unsupported with current clock\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ ratio = es8328->mclk_ratios[i];
+ } else {
+ ratio = 0;
+ es8328->mclkdiv2 = 0;
+ }
+
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MCLKDIV2,
+ es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
+
+ switch (params_width(params)) {
+ case 16:
+ wl = 3;
+ break;
+ case 18:
+ wl = 2;
+ break;
+ case 20:
+ wl = 1;
+ break;
+ case 24:
+ wl = 0;
+ break;
+ case 32:
+ wl = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
+ ES8328_DACCONTROL1_DACWL_MASK,
+ wl << ES8328_DACCONTROL1_DACWL_SHIFT);
+
+ es8328->playback_fs = params_rate(params);
+ es8328_set_deemph(component);
+ } else
+ snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
+ ES8328_ADCCONTROL4_ADCWL_MASK,
+ wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
+
+ return snd_soc_component_update_bits(component, reg, ES8328_RATEMASK, ratio);
+}
+
+static int es8328_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ int mclkdiv2 = 0;
+
+ switch (freq) {
+ case 0:
+ es8328->sysclk_constraints = NULL;
+ es8328->mclk_ratios = NULL;
+ break;
+ case 22579200:
+ mclkdiv2 = 1;
+ fallthrough;
+ case 11289600:
+ es8328->sysclk_constraints = &constraints_11289;
+ es8328->mclk_ratios = ratios_11289;
+ break;
+ case 24576000:
+ mclkdiv2 = 1;
+ fallthrough;
+ case 12288000:
+ es8328->sysclk_constraints = &constraints_12288;
+ es8328->mclk_ratios = ratios_12288;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ es8328->mclkdiv2 = mclkdiv2;
+ return 0;
+}
+
+static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ u8 dac_mode = 0;
+ u8 adc_mode = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Master serial port mode, with BCLK generated automatically */
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC,
+ ES8328_MASTERMODE_MSC);
+ es8328->provider = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Slave serial port mode */
+ snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC, 0);
+ es8328->provider = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
+ return -EINVAL;
+
+ snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
+ ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode);
+ snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
+ ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
+ snd_soc_component_update_bits(component, ES8328_DACCONTROL21,
+ ES8328_DACCONTROL21_SLRCK, ES8328_DACCONTROL21_SLRCK);
+
+ /* Set Capture Digital Volume */
+ snd_soc_component_write(component, ES8328_ADCCONTROL8, 0);
+ snd_soc_component_write(component, ES8328_ADCCONTROL9, 0);
+
+ /* Set PCM Volume */
+ snd_soc_component_write(component, ES8328_LDACVOL, 0);
+ snd_soc_component_write(component, ES8328_RDACVOL, 0);
+
+ /* Set L/R Out Volume */
+ snd_soc_component_write(component, ES8328_LOUT1VOL, 0x1e);
+ snd_soc_component_write(component, ES8328_ROUT1VOL, 0x1e);
+ snd_soc_component_write(component, ES8328_LOUT2VOL, 0x1e);
+ snd_soc_component_write(component, ES8328_ROUT2VOL, 0x1e);
+
+ /* Set MIC PGA Volume */
+ snd_soc_component_write(component, ES8328_ADCCONTROL1, 0x88);
+
+ if (es8328->eswin_plat == 2) {
+ if (gpiod_get_value(es8328->front_jack_gpio) == 1 && gpiod_get_value(es8328->back_jack_gpio) == 0) {
+ /* Select default capture path ---> LIN1 */
+ snd_soc_component_write(component, ES8328_ADCCONTROL2, 0);
+ } else {
+ /* Select default capture path ---> LIN2 */
+ snd_soc_component_write(component, ES8328_ADCCONTROL2, 0x50);
+ }
+ } else {
+ /* Select default capture path ---> phone mic */
+ snd_soc_component_write(component, ES8328_ADCCONTROL2, 0xf0);
+ }
+
+ snd_soc_component_update_bits(component, ES8328_ADCCONTROL3,
+ ES8328_ADCCONTROL3_DS, 0);
+
+ /* Select Playback path */
+ snd_soc_component_update_bits(component, ES8328_DACCONTROL17,
+ ES8328_DACCONTROL17_LD2LO, ES8328_DACCONTROL17_LD2LO);
+ snd_soc_component_update_bits(component, ES8328_DACCONTROL20,
+ ES8328_DACCONTROL20_RD2RO, ES8328_DACCONTROL20_RD2RO);
+
+ return 0;
+}
+
+static int es8328_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VREF, VMID=2x50k, digital enabled */
+ snd_soc_component_write(component, ES8328_CHIPPOWER, 0);
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_50k |
+ ES8328_CONTROL1_ENREF);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_5k |
+ ES8328_CONTROL1_ENREF);
+
+ /* Charge caps */
+ msleep(100);
+ }
+
+ snd_soc_component_write(component, ES8328_CONTROL2,
+ ES8328_CONTROL2_OVERCURRENT_ON |
+ ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
+
+ /* VREF, VMID=2*500k, digital stopped */
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_500k |
+ ES8328_CONTROL1_ENREF);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ 0);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es8328_dai_ops = {
+ .startup = es8328_startup,
+ .hw_params = es8328_hw_params,
+ .mute_stream = es8328_mute,
+ .set_sysclk = es8328_set_sysclk,
+ .set_fmt = es8328_set_dai_fmt,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver es8328_dai[3] = {
+ {
+ .name = "es8328-0-hifi-analog",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ES8328_RATES,
+ .formats = ES8328_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ES8328_RATES,
+ .formats = ES8328_FORMATS,
+ },
+ .ops = &es8328_dai_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "es8328-1-hifi-analog",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ES8328_RATES,
+ .formats = ES8328_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ES8328_RATES,
+ .formats = ES8328_FORMATS,
+ },
+ .ops = &es8328_dai_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "es8328-2-hifi-analog",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ES8328_RATES,
+ .formats = ES8328_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ES8328_RATES,
+ .formats = ES8328_FORMATS,
+ },
+ .ops = &es8328_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static int es8328_suspend(struct snd_soc_component *component)
+{
+ return 0;
+}
+
+static int es8328_resume(struct snd_soc_component *component)
+{
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
+ int ret;
+
+ regcache_mark_dirty(regmap);
+ ret = regcache_sync(regmap);
+ if (ret) {
+ dev_err(component->dev, "unable to sync regcache\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int es8328_component_probe(struct snd_soc_component *component)
+{
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+
+ es8328->component = component;
+
+ return 0;
+}
+
+static void es8328_remove(struct snd_soc_component *component)
+{
+}
+
+const struct regmap_config es8328_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ES8328_REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+EXPORT_SYMBOL_GPL(es8328_regmap_config);
+
+static const struct snd_soc_component_driver es8328_component_driver = {
+ .probe = es8328_component_probe,
+ .remove = es8328_remove,
+ .suspend = es8328_suspend,
+ .resume = es8328_resume,
+ .set_bias_level = es8328_set_bias_level,
+ .controls = es8328_snd_controls,
+ .num_controls = ARRAY_SIZE(es8328_snd_controls),
+ .dapm_widgets = es8328_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets),
+ .dapm_routes = es8328_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static irqreturn_t es8328_jack_irq(int irq, void *data)
+{
+ struct es8328_priv *es8328 = data;
+ struct snd_soc_component *comp = es8328->component;
+ int front_jack_value, back_jack_value;
+
+ if (!es8328->front_jack_gpio || !es8328->back_jack_gpio) {
+ dev_warn(comp->dev, "jack gpio desc is null\n");
+ return IRQ_NONE;
+ }
+
+ front_jack_value = gpiod_get_value(es8328->front_jack_gpio);
+ back_jack_value = gpiod_get_value(es8328->back_jack_gpio);
+
+ dev_dbg(comp->dev, "front jack value:%d, back jack value:%d\n", front_jack_value, back_jack_value);
+
+ if (back_jack_value == 0 && front_jack_value == 1) {
+ /* Select Capture path ---> LIN1 */
+ regmap_write(comp->regmap, ES8328_ADCCONTROL2, 0);
+ } else {
+ /* Select Capture path ---> LIN2 */
+ regmap_write(comp->regmap, ES8328_ADCCONTROL2, 0x50);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int es8328_probe(struct device *dev, struct regmap *regmap)
+{
+ struct es8328_priv *es8328;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ es8328 = devm_kzalloc(dev, sizeof(*es8328), GFP_KERNEL);
+ if (es8328 == NULL)
+ return -ENOMEM;
+
+ es8328->regmap = regmap;
+
+ dev_set_drvdata(dev, es8328);
+
+ ret = device_property_read_u32(dev, "eswin-plat", &es8328->eswin_plat);
+ if (0 != ret) {
+ es8328->eswin_plat = 0;
+ }
+ dev_info(dev, "eswin platform:%d\n", es8328->eswin_plat);
+
+ if (es8328->eswin_plat == 2) {
+ es8328->front_jack_gpio = devm_gpiod_get(dev, "front-jack", GPIOD_IN);
+ ret = IS_ERR(es8328->front_jack_gpio);
+ if(ret) {
+ dev_err(dev, "can not get front jack gpio\n");
+ }
+
+ es8328->back_jack_gpio = devm_gpiod_get(dev, "back-jack", GPIOD_IN);
+ ret = IS_ERR(es8328->back_jack_gpio);
+ if(ret) {
+ dev_err(dev, "can not get back jack gpio\n");
+ }
+
+ ret = devm_request_threaded_irq(dev, gpiod_to_irq(es8328->front_jack_gpio), NULL, es8328_jack_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "front jack", es8328);
+ if (ret) {
+ dev_err(dev, "Failed to request front irq[%d], ret:%d\n", gpiod_to_irq(es8328->back_jack_gpio), ret);
+ }
+
+ ret = devm_request_threaded_irq(dev, gpiod_to_irq(es8328->back_jack_gpio), NULL, es8328_jack_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "back jack", es8328);
+ if (ret) {
+ dev_err(dev, "Failed to request back irq[%d], ret:%d\n", gpiod_to_irq(es8328->back_jack_gpio), ret);
+ }
+ }
+
+ if (of_node_name_prefix(dev->of_node, "es8388-0")) {
+ ret = devm_snd_soc_register_component(dev,
+ &es8328_component_driver, &es8328_dai[0], 1);
+ } else if (of_node_name_prefix(dev->of_node, "es8388-1")) {
+ ret = devm_snd_soc_register_component(dev,
+ &es8328_component_driver, &es8328_dai[1], 1);
+ } else {
+ ret = devm_snd_soc_register_component(dev,
+ &es8328_component_driver, &es8328_dai[2], 1);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(es8328_probe);
+
+MODULE_DESCRIPTION("ASoC ES8328 driver");
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/eswin/es8328.h b/sound/soc/codecs/eswin/es8328.h
new file mode 100644
index 000000000000..70515466a4b2
--- /dev/null
+++ b/sound/soc/codecs/eswin/es8328.h
@@ -0,0 +1,299 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * es8328.h -- ES8328 ALSA SoC Audio driver
+ */
+
+/*
+ * Copyright (C) 2021 ESWIN, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _ES8328_H
+#define _ES8328_H
+
+#include <linux/regmap.h>
+
+struct device;
+
+extern const struct regmap_config es8328_regmap_config;
+int es8328_probe(struct device *dev, struct regmap *regmap);
+
+#define ES8328_DACLVOL 46
+#define ES8328_DACRVOL 47
+#define ES8328_DACCTL 28
+#define ES8328_RATEMASK (0x1f << 0)
+
+#define ES8328_CONTROL1 0x00
+#define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0)
+#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
+#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
+#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
+#define ES8328_CONTROL1_VMIDSEL_MASK (3 << 0)
+#define ES8328_CONTROL1_ENREF (1 << 2)
+#define ES8328_CONTROL1_SEQEN (1 << 3)
+#define ES8328_CONTROL1_SAMEFS (1 << 4)
+#define ES8328_CONTROL1_DACMCLK_ADC (0 << 5)
+#define ES8328_CONTROL1_DACMCLK_DAC (1 << 5)
+#define ES8328_CONTROL1_LRCM (1 << 6)
+#define ES8328_CONTROL1_SCP_RESET (1 << 7)
+
+#define ES8328_CONTROL2 0x01
+#define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0)
+#define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1)
+#define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2)
+#define ES8328_CONTROL2_ANALOG_OFF (1 << 3)
+#define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4)
+#define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5)
+#define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6)
+#define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7)
+
+#define ES8328_CHIPPOWER 0x02
+#define ES8328_CHIPPOWER_DACVREF_OFF 0
+#define ES8328_CHIPPOWER_ADCVREF_OFF 1
+#define ES8328_CHIPPOWER_DACDLL_OFF 2
+#define ES8328_CHIPPOWER_ADCDLL_OFF 3
+#define ES8328_CHIPPOWER_DACSTM_RESET 4
+#define ES8328_CHIPPOWER_ADCSTM_RESET 5
+#define ES8328_CHIPPOWER_DACDIG_OFF 6
+#define ES8328_CHIPPOWER_ADCDIG_OFF 7
+
+#define ES8328_ADCPOWER 0x03
+#define ES8328_ADCPOWER_INT1_LOWPOWER 0
+#define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER 1
+#define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF 2
+#define ES8328_ADCPOWER_MIC_BIAS_OFF 3
+#define ES8328_ADCPOWER_ADCR_OFF 4
+#define ES8328_ADCPOWER_ADCL_OFF 5
+#define ES8328_ADCPOWER_AINR_OFF 6
+#define ES8328_ADCPOWER_AINL_OFF 7
+
+#define ES8328_DACPOWER 0x04
+#define ES8328_DACPOWER_OUT3_ON 0
+#define ES8328_DACPOWER_MONO_ON 1
+#define ES8328_DACPOWER_ROUT2_ON 2
+#define ES8328_DACPOWER_LOUT2_ON 3
+#define ES8328_DACPOWER_ROUT1_ON 4
+#define ES8328_DACPOWER_LOUT1_ON 5
+#define ES8328_DACPOWER_RDAC_OFF 6
+#define ES8328_DACPOWER_LDAC_OFF 7
+
+#define ES8328_CHIPLOPOW1 0x05
+#define ES8328_CHIPLOPOW2 0x06
+#define ES8328_ANAVOLMANAG 0x07
+
+#define ES8328_MASTERMODE 0x08
+#define ES8328_MASTERMODE_BCLKDIV (0 << 0)
+#define ES8328_MASTERMODE_BCLK_INV (1 << 5)
+#define ES8328_MASTERMODE_MCLKDIV2 (1 << 6)
+#define ES8328_MASTERMODE_MSC (1 << 7)
+
+#define ES8328_ADCCONTROL1 0x09
+#define ES8328_ADCCONTROL2 0x0a
+#define ES8328_ADCCONTROL3 0x0b
+#define ES8328_ADCCONTROL3_DS (1 << 7)
+
+#define ES8328_ADCCONTROL4 0x0c
+#define ES8328_ADCCONTROL4_ADCFORMAT_MASK (3 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_I2S (0 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_PCM (3 << 0)
+#define ES8328_ADCCONTROL4_ADCWL_SHIFT 2
+#define ES8328_ADCCONTROL4_ADCWL_MASK (7 << 2)
+#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5)
+
+#define ES8328_ADCCONTROL5 0x0d
+#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
+
+#define ES8328_ADCCONTROL6 0x0e
+
+#define ES8328_ADCCONTROL7 0x0f
+#define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2)
+#define ES8328_ADCCONTROL7_ADC_LER (1 << 3)
+#define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4)
+#define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6)
+
+#define ES8328_ADCCONTROL8 0x10
+#define ES8328_ADCCONTROL9 0x11
+#define ES8328_ADCCONTROL10 0x12
+#define ES8328_ADCCONTROL11 0x13
+#define ES8328_ADCCONTROL12 0x14
+#define ES8328_ADCCONTROL13 0x15
+#define ES8328_ADCCONTROL14 0x16
+
+#define ES8328_DACCONTROL1 0x17
+#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
+#define ES8328_DACCONTROL1_DACWL_SHIFT 3
+#define ES8328_DACCONTROL1_DACWL_MASK (7 << 3)
+#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6)
+#define ES8328_DACCONTROL1_LRSWAP (1 << 7)
+
+#define ES8328_DACCONTROL2 0x18
+#define ES8328_DACCONTROL2_RATEMASK (0x1f << 0)
+#define ES8328_DACCONTROL2_DOUBLESPEED (1 << 5)
+
+#define ES8328_DACCONTROL3 0x19
+#define ES8328_DACCONTROL3_AUTOMUTE (1 << 2)
+#define ES8328_DACCONTROL3_DACMUTE (1 << 2)
+#define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3)
+#define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4)
+#define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5)
+#define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6)
+
+#define ES8328_LDACVOL 0x1a
+#define ES8328_LDACVOL_MASK (0 << 0)
+#define ES8328_LDACVOL_MAX (0xc0)
+
+#define ES8328_RDACVOL 0x1b
+#define ES8328_RDACVOL_MASK (0 << 0)
+#define ES8328_RDACVOL_MAX (0xc0)
+
+#define ES8328_DACCONTROL6 0x1c
+#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
+#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
+#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
+#define ES8328_DACCONTROL6_DEEMPH_MASK (3 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6)
+
+#define ES8328_DACCONTROL7 0x1d
+#define ES8328_DACCONTROL7_VPP_SCALE_3p5 (0 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_4p0 (1 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_3p0 (2 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_2p5 (3 << 0)
+#define ES8328_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */
+#define ES8328_DACCONTROL7_MONO (1 << 5)
+#define ES8328_DACCONTROL7_ZEROR (1 << 6)
+#define ES8328_DACCONTROL7_ZEROL (1 << 7)
+
+/* Shelving filter */
+#define ES8328_DACCONTROL8 0x1e
+#define ES8328_DACCONTROL9 0x1f
+#define ES8328_DACCONTROL10 0x20
+#define ES8328_DACCONTROL11 0x21
+#define ES8328_DACCONTROL12 0x22
+#define ES8328_DACCONTROL13 0x23
+#define ES8328_DACCONTROL14 0x24
+#define ES8328_DACCONTROL15 0x25
+
+#define ES8328_DACCONTROL16 0x26
+#define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3)
+
+#define ES8328_DACCONTROL17 0x27
+#define ES8328_DACCONTROL17_LI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL17_LI2LO (1 << 6)
+#define ES8328_DACCONTROL17_LD2LO (1 << 7)
+
+#define ES8328_DACCONTROL18 0x28
+#define ES8328_DACCONTROL18_RI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL18_RI2LO (1 << 6)
+#define ES8328_DACCONTROL18_RD2LO (1 << 7)
+
+#define ES8328_DACCONTROL19 0x29
+#define ES8328_DACCONTROL19_LI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL19_LI2RO (1 << 6)
+#define ES8328_DACCONTROL19_LD2RO (1 << 7)
+
+#define ES8328_DACCONTROL20 0x2a
+#define ES8328_DACCONTROL20_RI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL20_RI2RO (1 << 6)
+#define ES8328_DACCONTROL20_RD2RO (1 << 7)
+
+#define ES8328_DACCONTROL21 0x2b
+#define ES8328_DACCONTROL21_SLRCK (1 << 7)
+
+#define ES8328_DACCONTROL22 0x2c
+#define ES8328_DACCONTROL22_RI2MOVOL (7 << 3)
+#define ES8328_DACCONTROL22_RI2MO (1 << 6)
+#define ES8328_DACCONTROL22_RD2MO (1 << 7)
+
+#define ES8328_DACCONTROL23 0x2d
+#define ES8328_DACCONTROL23_MOUTINV (1 << 1)
+#define ES8328_DACCONTROL23_HPSWPOL (1 << 2)
+#define ES8328_DACCONTROL23_HPSWEN (1 << 3)
+#define ES8328_DACCONTROL23_VROI_1p5k (0 << 4)
+#define ES8328_DACCONTROL23_VROI_40k (1 << 4)
+#define ES8328_DACCONTROL23_OUT3_VREF (0 << 5)
+#define ES8328_DACCONTROL23_OUT3_ROUT1 (1 << 5)
+#define ES8328_DACCONTROL23_OUT3_MONOOUT (2 << 5)
+#define ES8328_DACCONTROL23_OUT3_RIGHT_MIXER (3 << 5)
+#define ES8328_DACCONTROL23_ROUT2INV (1 << 7)
+
+/* LOUT1 Amplifier */
+#define ES8328_LOUT1VOL 0x2e
+#define ES8328_LOUT1VOL_MASK (0 << 5)
+#define ES8328_LOUT1VOL_MAX (0x24)
+
+/* ROUT1 Amplifier */
+#define ES8328_ROUT1VOL 0x2f
+#define ES8328_ROUT1VOL_MASK (0 << 5)
+#define ES8328_ROUT1VOL_MAX (0x24)
+
+#define ES8328_OUT1VOL_MAX (0x24)
+
+/* LOUT2 Amplifier */
+#define ES8328_LOUT2VOL 0x30
+#define ES8328_LOUT2VOL_MASK (0 << 5)
+#define ES8328_LOUT2VOL_MAX (0x24)
+
+/* ROUT2 Amplifier */
+#define ES8328_ROUT2VOL 0x31
+#define ES8328_ROUT2VOL_MASK (0 << 5)
+#define ES8328_ROUT2VOL_MAX (0x24)
+
+#define ES8328_OUT2VOL_MAX (0x24)
+
+/* Mono Out Amplifier */
+#define ES8328_MONOOUTVOL 0x32
+#define ES8328_MONOOUTVOL_MASK (0 << 5)
+#define ES8328_MONOOUTVOL_MAX (0x24)
+
+#define ES8328_DACCONTROL29 0x33
+#define ES8328_DACCONTROL30 0x34
+
+#define ES8328_SYSCLK 0
+
+#define ES8328_REG_MAX 0x35
+
+#define ES8328_1536FS 1536
+#define ES8328_1024FS 1024
+#define ES8328_768FS 768
+#define ES8328_512FS 512
+#define ES8328_384FS 384
+#define ES8328_256FS 256
+#define ES8328_128FS 128
+
+#endif
diff --git a/sound/soc/eswin/Kconfig b/sound/soc/eswin/Kconfig
new file mode 100644
index 000000000000..7c930a329daa
--- /dev/null
+++ b/sound/soc/eswin/Kconfig
@@ -0,0 +1,11 @@
+menu "SND ESWIN SOC"
+
+config SND_ESWIN_DW_I2S
+ tristate "Eswin Dw I2S Device Driver"
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for I2S driver for
+ Eswin dw I2S device. The device supports up to
+ a maximum of 8 channels each for play and record.
+
+endmenu
diff --git a/sound/soc/eswin/Makefile b/sound/soc/eswin/Makefile
new file mode 100644
index 000000000000..2c133c695dbe
--- /dev/null
+++ b/sound/soc/eswin/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# ESWIN Platform Support
+snd-soc-i2s-objs := esw-i2s.o esw-audio-proc.o
+
+obj-$(CONFIG_SND_ESWIN_DW_I2S) += snd-soc-i2s.o
\ No newline at end of file
diff --git a/sound/soc/eswin/esw-audio-proc.c b/sound/soc/eswin/esw-audio-proc.c
new file mode 100644
index 000000000000..5ffed4c4ab34
--- /dev/null
+++ b/sound/soc/eswin/esw-audio-proc.c
@@ -0,0 +1,488 @@
+/*
+ *
+ * Copyright (C) 2021 ESWIN, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/kernel.h>
+#include <linux/seq_file.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <linux/eventfd.h>
+#include <linux/ioctl.h>
+#include <linux/device.h>
+
+// proc data definition
+typedef enum
+{
+ ADEC_CREATE_CHN,
+ ADEC_SEND_STREAM,
+ ADEC_GET_FRAME,
+ ADEC_PARSE_PACKET,
+ ADEC_DECODE_STREAM,
+ AENC_CREATE_CHN,
+ AENC_SEND_FRAME,
+ AENC_GET_STREAM,
+ AENC_ENCODE_FRAME,
+ AGC_PROCESS,
+ ANS_PROCESS,
+ AEC_PROCESS,
+ DRC_PROCESS,
+ EQ_PROCESS,
+ DCBLOCK_PROCESS,
+ VOLUME_PROCESS,
+ SRC_HOST_PROCESS,
+ SRC_DAI_PROCESS,
+ HOST_PROCESS,
+ DAI_PROCESS,
+ AO_START,
+ AO_PROCESS_FRAME,
+ AO_WRITE_FRAME,
+ AO_STOP,
+ AI_START,
+ AI_READ_FRAME,
+ AI_PROCESS_FRAME,
+ AI_STOP,
+} PERF_MARK;
+
+static struct proc_dir_entry *proc_esaudio;
+static int g_switch = 0;
+
+// Device data definition
+#define MAX_PERF_SIZE 1024
+enum DEVICES_ID{
+ INVALID_DEVICE = -1,
+ AO = 0,
+ AI,
+ AENC,
+ ADEC,
+ NUM_DEVICES,
+};
+static const char *device_names[NUM_DEVICES] = {"ao", "ai", "aenc", "adec"};
+static int audio_proc_major[NUM_DEVICES] = {0};
+static struct class *audio_proc_class = NULL;
+static struct device *audio_proc_device[NUM_DEVICES] = {NULL};
+static int32_t *g_perf_data[NUM_DEVICES] = {NULL};
+
+static void show_aenc_data(struct seq_file *m)
+{
+ seq_printf(m,"----------------------------------------------------AENC PERF STATISTIC BEGIN"
+ "----------------------------------------------------\n");
+ seq_printf(m, "\n");
+ seq_printf(m, "------------------------------------------------------------------------"
+ "----------------------------------------------------------\n");
+ seq_printf(m, "audio encoder performance(us):\n");
+ seq_printf(m, "%-14s%-14s%-14s%-14s\n", "create_chn", "send_frame", "get_stream", "encode_frame");
+ seq_printf(m, "----------------------------------------------------------------------------------"
+ "------------------------------------------------\n");
+ seq_printf(m, "%-14d%-14d%-14d%-14d\n", g_perf_data[AENC][AENC_CREATE_CHN], g_perf_data[AENC][AENC_SEND_FRAME],
+ g_perf_data[AENC][AENC_GET_STREAM], g_perf_data[AENC][AENC_ENCODE_FRAME]);
+ seq_printf(m, "\n");
+ seq_printf(m, "-----------------------------------------------------AENC PERF STATISTIC END"
+ "-----------------------------------------------------\n");
+}
+
+static void show_adec_data(struct seq_file *m)
+{
+ seq_printf(m, "----------------------------------------------------ADEC PERF STATISTIC BEGIN"
+ "----------------------------------------------------\n");
+ seq_printf(m, "\n");
+ seq_printf(m, "--------------------------------------------------------------------------"
+ "--------------------------------------------------------\n");
+ seq_printf(m, "audio decoder performance(us):\n");
+ seq_printf(m, "%-14s%-14s%-14s%-14s%-14s\n", "create_chn", "send_stream", "get_frame",
+ "parse_packet", "decode_stream");
+ seq_printf(m, "------------------------------------------------------------------------------------"
+ "----------------------------------------------\n");
+ seq_printf(m, "%-14d%-14d%-14d%-14d%-14d\n", g_perf_data[ADEC][ADEC_CREATE_CHN],
+ g_perf_data[ADEC][ADEC_SEND_STREAM], g_perf_data[ADEC][ADEC_GET_FRAME],
+ g_perf_data[ADEC][ADEC_PARSE_PACKET], g_perf_data[ADEC][ADEC_DECODE_STREAM]);
+ seq_printf(m, "\n");
+ seq_printf(m,"-----------------------------------------------------ADEC PERF STATISTIC END"
+ "-----------------------------------------------------\n");
+}
+
+static void show_ao_data(struct seq_file *m)
+{
+ seq_printf(m,"----------------------------------------------------AO PERF STATISTIC BEGIN"
+ "----------------------------------------------------\n");
+ seq_printf(m, "\n");
+ seq_printf(m, "---------------------------------------------------------------------------"
+ "-------------------------------------------------------\n");
+ seq_printf(m, "audio output performance(us):\n");
+ seq_printf(m, "%-14s%-24s%-24s\n", "ao_start", "ao_process_frame", "ao_write_frame");
+ seq_printf(m, "----------------------------------------------------------------------------"
+ "------------------------------------------------------\n");
+ seq_printf(m, "%-14d%-24d%-24d\n", g_perf_data[AO][AO_START], g_perf_data[AO][AO_PROCESS_FRAME],
+ g_perf_data[AO][AO_WRITE_FRAME]);
+ seq_printf(m, "\n");
+ seq_printf(m, "-----------------------------------------------------------------------------"
+ "-----------------------------------------------------\n");
+ seq_printf(m, "audio argorithm performance(ns/1ms):\n");
+ seq_printf(m, "%-14s%-14s%-14s%-14s%-14s%-14s%-14s%-14s%-14s\n", "agc", "ans", "eq", "hpf",
+ "volume", "src-host","src-dai", "host", "dai");
+ seq_printf(m, "------------------------------------------------------------------------------"
+ "----------------------------------------------------\n");
+ seq_printf(m, "%-14d%-14d%-14d%-14d%-14d%-14d%-14d%-14d%-14d\n", g_perf_data[AO][AGC_PROCESS],
+ g_perf_data[AO][ANS_PROCESS],g_perf_data[AO][EQ_PROCESS],
+ g_perf_data[AO][DCBLOCK_PROCESS],g_perf_data[AO][VOLUME_PROCESS],
+ g_perf_data[AO][SRC_HOST_PROCESS],g_perf_data[AO][SRC_DAI_PROCESS],
+ g_perf_data[AO][HOST_PROCESS], g_perf_data[AO][DAI_PROCESS]);
+ seq_printf(m, "\n");
+ seq_printf(m,"-----------------------------------------------------AO PERF STATISTIC END"
+ "-----------------------------------------------------\n");
+}
+
+static void show_ai_data(struct seq_file *m)
+{
+ seq_printf(m,"----------------------------------------------------AI PERF STATISTIC BEGIN"
+ "----------------------------------------------------\n");
+ seq_printf(m, "\n");
+ seq_printf(m, "---------------------------------------------------------------------------"
+ "-------------------------------------------------------\n");
+ seq_printf(m, "audio input performance(us):\n");
+ seq_printf(m, "%-14s%-24s%-24s\n", "ai_start", "ai_read_frame", "ai_process_frame");
+ seq_printf(m, "----------------------------------------------------------------------------"
+ "------------------------------------------------------\n");
+ seq_printf(m, "%-14d%-24d%-24d\n", g_perf_data[AI][AI_START], g_perf_data[AI][AI_READ_FRAME],
+ g_perf_data[AI][AI_PROCESS_FRAME]);
+ seq_printf(m, "\n");
+ seq_printf(m, "-----------------------------------------------------------------------------"
+ "-----------------------------------------------------\n");
+ seq_printf(m, "audio argorithm performance(ns/1ms):\n");
+ seq_printf(m, "%-14s%-14s%-14s%-14s%-14s%-14s%-14s%-14s%-14s%-14s\n", "agc", "ans", "drc", "eq",
+ "hpf", "volume","src-host", "src-dai", "host", "dai");
+ seq_printf(m, "------------------------------------------------------------------------------"
+ "----------------------------------------------------\n");
+ seq_printf(m, "%-14d%-14d%-14d%-14d%-14d%-14d%-14d%-14d%-14d%-14d\n", g_perf_data[AI][AGC_PROCESS],
+ g_perf_data[AI][ANS_PROCESS],g_perf_data[AI][DRC_PROCESS], g_perf_data[AI][EQ_PROCESS],
+ g_perf_data[AI][DCBLOCK_PROCESS], g_perf_data[AI][VOLUME_PROCESS],g_perf_data[AI][SRC_HOST_PROCESS],
+ g_perf_data[AI][SRC_DAI_PROCESS], g_perf_data[AI][HOST_PROCESS], g_perf_data[AI][DAI_PROCESS]);
+ seq_printf(m, "\n");
+ seq_printf(m,"-----------------------------------------------------AI PERF STATISTIC END"
+ "-----------------------------------------------------\n");
+}
+
+
+static int audio_info_show(struct seq_file *m, void *p)
+{
+ int i;
+ const char *fileName = m->file->f_path.dentry->d_name.name;
+ enum DEVICES_ID deviceID = INVALID_DEVICE;
+
+ pr_info("audio_info_show:%s\n", m->file->f_path.dentry->d_name.name);
+
+ for (i = 0; i < NUM_DEVICES; ++i) {
+ if (strcmp(fileName, device_names[i]) == 0) {
+ deviceID = i;
+ break;
+ }
+ }
+
+ if (deviceID == INVALID_DEVICE) {
+ pr_err("deviceID is INVALID\n");
+ return -EINVAL;
+ }
+
+ if (g_switch == 0) {
+ seq_printf(m, "The switch is not turned on, pls first turn on the switch.\n");
+ return 0;
+ }
+
+ switch (deviceID) {
+ case AI:
+ show_ai_data(m);
+ break;
+ case AO:
+ show_ao_data(m);
+ break;
+ case AENC:
+ show_aenc_data(m);
+ break;
+ case ADEC:
+ show_adec_data(m);
+ break;
+ default:
+ pr_err("deviceID is INVALID\n");
+ break;
+ }
+ return 0;
+}
+
+static int info_open(struct inode *inode, struct file *flip)
+{
+ return single_open(flip, audio_info_show, NULL);
+}
+
+static int switch_show(struct seq_file *m, void *p)
+{
+ seq_printf(m, "--------------------AUDIO Performance Switch--------------------\n");
+ seq_printf(m, "\n");
+ seq_printf(m, "AUDIO Performance Switch Status Value:%d\n", g_switch);
+ seq_printf(m, "\n");
+ return 0;
+}
+
+static int switch_open(struct inode *inode, struct file *flip)
+{
+ return single_open(flip, switch_show, NULL);
+}
+
+static ssize_t switch_write(struct file *flip, const char __user *buf, size_t size, loff_t *pos)
+{
+ u16 data;
+ u8 value;
+
+ if (size > 2) {
+ return -EINVAL;
+ }
+ if (copy_from_user(&data, buf, size)) {
+ return -EFAULT;
+ }
+ value = data & 0xff;
+
+ value -= '0';
+ if (!(value == 1 || value == 0)) {
+ printk("%s, %d, data=%d is not correct, pls use 1 or 0.\n", __func__, __LINE__, value);
+ return -EINVAL;
+ }
+
+ g_switch = value ? 1 : 0;
+
+ return size;
+}
+
+static struct proc_ops proc_info_fops = {
+ .proc_open = info_open,
+ .proc_read = seq_read,
+ .proc_release = single_release,
+};
+
+static struct proc_ops proc_switch_fops = {
+ .proc_open = switch_open,
+ .proc_read = seq_read,
+ .proc_release = single_release,
+ .proc_write = switch_write,
+};
+
+int audio_create_procfs(void)
+{
+ proc_esaudio = proc_mkdir("es_audio", NULL);
+ if (proc_esaudio == NULL) {
+ pr_err("create es_audio dir err.\n");
+ return -ENOMEM;
+ }
+
+ if (!proc_create("ao", 0644, proc_esaudio, &proc_info_fops)) {
+ pr_err("error create proc ao file.\n");
+ goto err_ao;
+ }
+
+ if (!proc_create("ai", 0644, proc_esaudio, &proc_info_fops)) {
+ pr_err("error create proc ai file.\n");
+ goto err_ai;
+ }
+
+ if (!proc_create("aenc", 0644, proc_esaudio, &proc_info_fops)) {
+ pr_err("error create proc aenc file.\n");
+ goto err_aenc;
+ }
+
+ if (!proc_create("adec", 0644, proc_esaudio, &proc_info_fops)) {
+ pr_err("error create proc adec file.\n");
+ goto err_adec;
+ }
+
+ if (!proc_create("switch", 0644, proc_esaudio, &proc_switch_fops)) {
+ pr_err("error create proc switch file.\n");
+ goto err_switch;
+ }
+
+ return 0;
+
+err_switch:
+ remove_proc_entry("adec", proc_esaudio);
+err_adec:
+ remove_proc_entry("aenc", proc_esaudio);
+err_aenc:
+ remove_proc_entry("ai", proc_esaudio);
+err_ai:
+ remove_proc_entry("ao", proc_esaudio);
+err_ao:
+ remove_proc_entry("es_audio", NULL);
+ return -1;
+}
+
+void audio_remove_procfs(void)
+{
+ remove_proc_entry("switch", proc_esaudio);
+
+ remove_proc_entry("adec", proc_esaudio);
+
+ remove_proc_entry("aenc", proc_esaudio);
+
+ remove_proc_entry("ai", proc_esaudio);
+
+ remove_proc_entry("ao", proc_esaudio);
+
+ remove_proc_entry("es_audio", NULL);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// audio dev implementation
+static int audio_dev_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int i;
+ const char *fileName = file->f_path.dentry->d_name.name;
+ enum DEVICES_ID deviceID = INVALID_DEVICE;
+ unsigned long size = vma->vm_end - vma->vm_start;
+
+ pr_info("audio_dev_mmap:%s\n", file->f_path.dentry->d_name.name);
+
+ pr_info("vma->vm_end:%ld,vma->vm_start:%ld\n",vma->vm_end, vma->vm_start);
+
+ if (size > (MAX_PERF_SIZE * sizeof(int32_t))) {
+ pr_err("audio_dev_mmap: size:%ld > %ld.\n", size, MAX_PERF_SIZE * sizeof(int32_t));
+ return -EINVAL;
+ }
+
+ for (i = 0; i < NUM_DEVICES; ++i) {
+ if (strcmp(fileName, device_names[i]) == 0) {
+ deviceID = i;
+ break;
+ }
+ }
+
+ if (deviceID == INVALID_DEVICE) {
+ pr_err("deviceID is INVALID\n");
+ return -EINVAL;
+ }
+
+ // Remap the shared memory into the process's address space
+ if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(g_perf_data[deviceID]) >> PAGE_SHIFT,
+ size, vma->vm_page_prot)) {
+ pr_err("Failed to remap shared memory.\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static const struct file_operations dev_fops = {
+ .owner = THIS_MODULE,
+ .mmap = audio_dev_mmap,
+};
+
+static bool g_proc_initialized = false;
+
+int audio_proc_module_init(void)
+{
+ int i, ret;
+ struct device *dev;
+
+ if (g_proc_initialized) {
+ return 0;
+ }
+
+ pr_info("audio_proc_module_init enter.\n");
+
+ audio_proc_class = class_create("audio_proc_class");
+ if (IS_ERR(audio_proc_class)) {
+ pr_err("Failed to create audio_proc_class\n");
+ return PTR_ERR(audio_proc_class);
+ }
+
+ for (i = 0; i < NUM_DEVICES; ++i) {
+ g_perf_data[i] = kmalloc(MAX_PERF_SIZE * sizeof(int32_t), GFP_KERNEL);
+ if (!g_perf_data[i]) {
+ pr_err("Failed to allocate shared memory for '%s'\n", device_names[i]);
+ goto cleanup;
+ }
+
+ memset(g_perf_data[i], 0, MAX_PERF_SIZE * sizeof(int32_t));
+
+ ret = register_chrdev(0, device_names[i], &dev_fops);
+ if (ret < 0) {
+ pr_err("Failed to register character device '%s'\n", device_names[i]);
+ goto cleanup;
+ }
+
+ audio_proc_major[i] = ret;
+
+ dev = device_create(audio_proc_class, NULL, MKDEV(audio_proc_major[i], 0), NULL, device_names[i]);
+ if (IS_ERR(dev)) {
+ pr_err("Failed to create device node '%s'\n", device_names[i]);
+ goto cleanup;
+ }
+
+ audio_proc_device[i] = dev;
+ }
+
+ audio_create_procfs();
+
+ g_proc_initialized = true;
+
+ pr_info("es_audio_proc: initialized\n");
+ return 0;
+
+cleanup:
+ for (i = 0; i < NUM_DEVICES; ++i) {
+ if (g_perf_data[i]) {
+ kfree(g_perf_data[i]);
+ g_perf_data[i] = NULL;
+ }
+
+ if (audio_proc_major[i]) {
+ unregister_chrdev(audio_proc_major[i], device_names[i]);
+ }
+
+ if (audio_proc_device[i]) {
+ device_destroy(audio_proc_class, MKDEV(audio_proc_major[i], 0));
+ }
+ }
+
+ class_destroy(audio_proc_class);
+ return ret;
+}
+
+static bool g_proc_uninitialized = false;
+
+void audio_proc_module_exit(void)
+{
+ int i;
+
+ if (g_proc_uninitialized) {
+ return;
+ }
+
+ audio_remove_procfs();
+ for (i = 0; i < NUM_DEVICES; ++i) {
+ device_destroy(audio_proc_class, MKDEV(audio_proc_major[i], 0));
+ unregister_chrdev(audio_proc_major[i], device_names[i]);
+ kfree(g_perf_data[i]);
+ g_perf_data[i] = NULL;
+ }
+
+ class_destroy(audio_proc_class);
+
+ g_proc_uninitialized = true;
+
+ pr_info("es_audio_proc: uninitialized\n");
+}
diff --git a/sound/soc/eswin/esw-audio-proc.h b/sound/soc/eswin/esw-audio-proc.h
new file mode 100644
index 000000000000..1fd3f377671d
--- /dev/null
+++ b/sound/soc/eswin/esw-audio-proc.h
@@ -0,0 +1,23 @@
+/*
+ *
+ * Copyright (C) 2021 ESWIN, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _ES_AUDIO_PROC_H_
+#define _ES_AUDIO_PROC_H_
+
+int audio_proc_module_init(void);
+void audio_proc_module_exit(void);
+
+#endif
diff --git a/sound/soc/eswin/esw-i2s.c b/sound/soc/eswin/esw-i2s.c
new file mode 100755
index 000000000000..8fe165ddb0d6
--- /dev/null
+++ b/sound/soc/eswin/esw-i2s.c
@@ -0,0 +1,973 @@
+/*
+ * ALSA SoC Synopsys I2S Audio Layer
+ *
+ * sound/soc/dwc/designware_i2s.c
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Rajeev Kumar <rajeevkumar.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ */
+
+/*
+ * Copyright (C) 2021 ESWIN, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <sound/pcm.h>
+#include <linux/device.h>
+#include <sound/soc.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <asm/io.h>
+#include <sound/asound.h>
+#include <sound/designware_i2s.h>
+#include <linux/irqreturn.h>
+#include <linux/clk.h>
+#include <sound/soc-dai.h>
+#include <linux/ioport.h>
+#include <linux/err.h>
+#include <linux/pm.h>
+#include <linux/mfd/syscon.h>
+#include <linux/reset.h>
+#include <linux/dma-map-ops.h>
+#include "esw-i2s.h"
+#include "esw-audio-proc.h"
+
+#define VO_MCLK_DIVSOR_MASK 0xff0
+#define VO_MCLK_DIVSOR_OFFSET 4
+
+#define MAX_SAMPLE_RATE_SUPPORT (192000UL)
+#define MAX_SAMPLE_RATE_CLK (MAX_SAMPLE_RATE_SUPPORT * 32 * 2) // 32 bits, 2channels
+
+#define VO_TOP_CSR 0x50280000UL
+#define VO_I2S0_DIV_NUM 0x2000
+#define VO_I2S1_DIV_NUM 0x2004
+#define VO_I2S2_DIV_NUM 0x2008
+#define DIV_NUM_MASK 0x1f
+
+#define ESW_I2S_RATES (SNDRV_PCM_RATE_192000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_8000)
+#define ESW_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
+
+static u32 dmaen_txch[] = {
+ DMAEN_TXCH_0,
+ DMAEN_TXCH_1,
+ DMAEN_TXCH_2,
+ DMAEN_TXCH_3
+};
+
+static u32 dmaen_rxch[] = {
+ DMAEN_RXCH_0,
+ DMAEN_RXCH_1,
+ DMAEN_RXCH_2,
+ DMAEN_RXCH_3
+};
+
+/* Maximum bit resolution of a channel - not uniformly spaced */
+static const u32 fifo_width[COMP_MAX_WORDSIZE] = {
+ 12, 16, 20, 24, 32, 0, 0, 0
+};
+
+/* Width of (DMA) bus */
+static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = {
+ DMA_SLAVE_BUSWIDTH_1_BYTE,
+ DMA_SLAVE_BUSWIDTH_2_BYTES,
+ DMA_SLAVE_BUSWIDTH_4_BYTES,
+ DMA_SLAVE_BUSWIDTH_UNDEFINED
+};
+
+static inline u32 i2s_read_reg(void *io_base, int reg)
+{
+ return readl((char *)io_base + reg);
+}
+
+static inline void i2s_write_reg(void *io_base, int reg, u32 val)
+{
+ writel(val, (char *)io_base + reg);
+}
+
+static inline void i2s_disable_channels(struct i2s_dev *i2s_drvdata, u32 stream)
+{
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ i2s_write_reg(i2s_drvdata->i2s_base, TER(0), 0);
+ } else {
+ i2s_write_reg(i2s_drvdata->i2s_base, RER(0), 0);
+ }
+}
+
+static void i2s_config(struct i2s_dev *i2s_drvdata, int stream)
+{
+ u32 ch_reg;
+ struct i2s_clk_config_data *config = &i2s_drvdata->config;
+ i2s_disable_channels(i2s_drvdata, stream);
+ for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ i2s_write_reg(i2s_drvdata->i2s_base, TCR(ch_reg),
+ i2s_drvdata->xfer_resolution);
+ i2s_write_reg(i2s_drvdata->i2s_base, TFCR(ch_reg),
+ i2s_drvdata->fifo_th - 1);
+ i2s_write_reg(i2s_drvdata->i2s_base, TER(ch_reg), 1);
+ } else {
+ i2s_write_reg(i2s_drvdata->i2s_base, RCR(ch_reg),
+ i2s_drvdata->xfer_resolution);
+ i2s_write_reg(i2s_drvdata->i2s_base, RFCR(ch_reg),
+ i2s_drvdata->fifo_th - 1);
+ i2s_write_reg(i2s_drvdata->i2s_base, RER(ch_reg), 1);
+ }
+ }
+}
+
+static inline void i2s_enable_irqs(struct i2s_dev *i2s_drvdata, u32 stream,
+ int chan_nr)
+{
+ u32 i, irq;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ irq = i2s_read_reg(i2s_drvdata->i2s_base, IMR(i));
+ i2s_write_reg(i2s_drvdata->i2s_base, IMR(i), irq & ~0x30);
+ }
+ } else {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ irq = i2s_read_reg(i2s_drvdata->i2s_base, IMR(i));
+ i2s_write_reg(i2s_drvdata->i2s_base, IMR(i), irq & ~0x03);
+ }
+ }
+}
+
+static inline void i2s_enable_dedicated_dma(struct i2s_dev *i2s_drvdata, u32 stream,
+ int chan_nr)
+{
+ u32 i, dmacr;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ dmacr = i2s_read_reg(i2s_drvdata->i2s_base, DMACR);
+ i2s_write_reg(i2s_drvdata->i2s_base, DMACR, dmacr | dmaen_txch[i]);
+ }
+ } else {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ dmacr = i2s_read_reg(i2s_drvdata->i2s_base, DMACR);
+ i2s_write_reg(i2s_drvdata->i2s_base, DMACR, dmacr | dmaen_rxch[i]);
+ }
+ }
+}
+
+static inline void i2s_disable_dedicated_dma(struct i2s_dev *i2s_drvdata, u32 stream,
+ int chan_nr)
+{
+ u32 i, dmacr;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ dmacr = i2s_read_reg(i2s_drvdata->i2s_base, DMACR);
+ i2s_write_reg(i2s_drvdata->i2s_base, DMACR, dmacr & ~dmaen_txch[i]);
+ }
+ } else {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ dmacr = i2s_read_reg(i2s_drvdata->i2s_base, DMACR);
+ i2s_write_reg(i2s_drvdata->i2s_base, DMACR, dmacr & ~dmaen_rxch[i]);
+ }
+ }
+}
+
+static inline void i2s_enable_combined_dma(struct i2s_dev *i2s_drvdata, u32 stream)
+{
+ u32 dmacr;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dmacr = i2s_read_reg(i2s_drvdata->i2s_base, DMACR);
+ i2s_write_reg(i2s_drvdata->i2s_base, DMACR, dmacr | DMAEN_TXBLOCK);
+
+ } else {
+ dmacr = i2s_read_reg(i2s_drvdata->i2s_base, DMACR);
+ i2s_write_reg(i2s_drvdata->i2s_base, DMACR, dmacr | DMAEN_RXBLOCK);
+ }
+}
+
+static inline void i2s_disable_combined_dma(struct i2s_dev *i2s_drvdata, u32 stream)
+{
+ u32 dmacr;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dmacr = i2s_read_reg(i2s_drvdata->i2s_base, DMACR);
+ i2s_write_reg(i2s_drvdata->i2s_base, DMACR, dmacr & ~DMAEN_TXBLOCK);
+
+ } else {
+ dmacr = i2s_read_reg(i2s_drvdata->i2s_base, DMACR);
+ i2s_write_reg(i2s_drvdata->i2s_base, DMACR, dmacr & ~DMAEN_RXBLOCK);
+ }
+}
+
+static void i2s_start(struct i2s_dev *i2s_drvdata,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_clk_config_data *config = &i2s_drvdata->config;
+ i2s_write_reg(i2s_drvdata->i2s_base, IER, 1);
+ if (i2s_drvdata->use_pio) {
+ i2s_enable_irqs(i2s_drvdata, substream->stream, config->chan_nr);
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ i2s_write_reg(i2s_drvdata->i2s_base, ITER, 1);
+ } else {
+ i2s_write_reg(i2s_drvdata->i2s_base, IRER, 1);
+ }
+ if (!i2s_drvdata->use_pio) {
+ i2s_enable_dedicated_dma(i2s_drvdata, substream->stream, config->chan_nr);
+ }
+ i2s_write_reg(i2s_drvdata->i2s_base, CER, 1);
+}
+
+static inline void i2s_clear_irqs(struct i2s_dev *i2s_drvdata, u32 stream)
+{
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ i2s_read_reg(i2s_drvdata->i2s_base, TOR(0));
+ } else {
+ i2s_read_reg(i2s_drvdata->i2s_base, ROR(0));
+ }
+}
+
+static inline void i2s_disable_irqs(struct i2s_dev *i2s_drvdata, u32 stream,
+ int chan_nr)
+{
+ u32 i, irq;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ irq = i2s_read_reg(i2s_drvdata->i2s_base, IMR(i));
+ i2s_write_reg(i2s_drvdata->i2s_base, IMR(i), irq | 0x30);
+ }
+ } else {
+ for (i = 0; i < (chan_nr / 2); i++) {
+ irq = i2s_read_reg(i2s_drvdata->i2s_base, IMR(i));
+ i2s_write_reg(i2s_drvdata->i2s_base, IMR(i), irq | 0x03);
+ }
+ }
+}
+
+static void i2s_stop(struct i2s_dev *i2s_drvdata,
+ struct snd_pcm_substream *substream)
+{
+ if (i2s_drvdata->use_pio) {
+ i2s_clear_irqs(i2s_drvdata, substream->stream);
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ i2s_write_reg(i2s_drvdata->i2s_base, ITER, 0);
+ } else {
+ i2s_write_reg(i2s_drvdata->i2s_base, IRER, 0);
+ }
+ if (i2s_drvdata->use_pio) {
+ i2s_disable_irqs(i2s_drvdata, substream->stream, 2);
+ } else {
+ i2s_disable_dedicated_dma(i2s_drvdata, substream->stream, 2);
+ }
+ if (!i2s_drvdata->active) {
+ i2s_write_reg(i2s_drvdata->i2s_base, CER, 0);
+ i2s_write_reg(i2s_drvdata->i2s_base, IER, 0);
+ }
+}
+
+#define COMP1_MAX_WORDSIZE 5
+static const u32 i2s_formats[COMP1_MAX_WORDSIZE] = {
+ SNDRV_PCM_FMTBIT_S16_LE,
+ SNDRV_PCM_FMTBIT_S16_LE,
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE
+};
+
+static int i2s_configure_dai(struct i2s_dev *i2s_drvdata,
+ struct snd_soc_dai_driver *i2s_dai,
+ unsigned int rates)
+{
+ u32 idx;
+ u32 fifo_depth;
+ u32 comp1, comp2;
+
+ comp1 = i2s_read_reg(i2s_drvdata->i2s_base, i2s_drvdata->i2s_reg_comp1);
+ comp2 = i2s_read_reg(i2s_drvdata->i2s_base, i2s_drvdata->i2s_reg_comp2);
+ fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
+
+ if (COMP1_TX_ENABLED(comp1)) {
+ dev_dbg(i2s_drvdata->dev, " i2s: play supported\n");
+ idx = COMP1_TX_WORDSIZE_0(comp1);
+ if (WARN_ON(idx >= ARRAY_SIZE(i2s_formats)))
+ return -EINVAL;
+ i2s_dai->playback.formats = i2s_formats[idx];
+ i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
+ i2s_dai->playback.channels_max =
+ (COMP1_TX_CHANNELS(comp1) + 1) << 1;
+ i2s_dai->playback.rates = rates;
+ }
+
+ if (COMP1_RX_ENABLED(comp1)){
+ dev_dbg(i2s_drvdata->dev, "i2s: record supported\n");
+ idx = COMP2_RX_WORDSIZE_0(comp2);
+ if (WARN_ON(idx >= ARRAY_SIZE(i2s_formats)))
+ return -EINVAL;
+ i2s_dai->capture.formats = i2s_formats[idx];
+ i2s_dai->capture.channels_min = MIN_CHANNEL_NUM;
+ i2s_dai->capture.channels_max =
+ (COMP1_RX_CHANNELS(comp1) + 1) << 1;
+ i2s_dai->capture.rates = rates;
+ }
+
+ if (COMP1_MODE_EN(comp1)) {
+ dev_dbg(i2s_drvdata->dev, "eswin: i2s master mode supported\n");
+ i2s_drvdata->capability |= DW_I2S_MASTER;
+ } else {
+ dev_dbg(i2s_drvdata->dev, "eswin: i2s slave mode supported\n");
+ i2s_drvdata->capability |= DW_I2S_SLAVE;
+ }
+ i2s_drvdata->fifo_th = fifo_depth / 2;
+ return 0;
+}
+
+static int i2s_configure_dai_by_dt(struct i2s_dev *dev,
+ struct snd_soc_dai_driver *i2s_dai,
+ struct resource *res)
+{
+ struct snd_soc_component *component;
+ struct dmaengine_pcm *pcm;
+ u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
+ u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
+ u32 fifo_depth;
+ u32 idx;
+ u32 idx2;
+ int ret;
+
+ dev_info(dev->dev, "comp1:0x%x, comp2:0x%x\n", comp1, comp2);
+ fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
+ idx = COMP1_APB_DATA_WIDTH(comp1);
+
+ if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) {
+ dev_err(dev->dev, "idx:%d inval\n", idx);
+ return -EINVAL;
+ }
+ ret = i2s_configure_dai(dev, i2s_dai, SNDRV_PCM_RATE_8000_192000);
+ if (ret < 0) {
+ dev_err(dev->dev, "i2s_configure_dai failed: %d\n", ret);
+ return ret;
+ }
+ component = snd_soc_lookup_component(dev->dev, SND_DMAENGINE_PCM_DRV_NAME);
+ if (!component) {
+ dev_err(dev->dev, "Can not find snd_soc_component\n");
+ return -1;
+ }
+
+ pcm = soc_component_to_pcm(component);
+ if (COMP1_TX_ENABLED(comp1)) {
+ idx2 = COMP1_TX_WORDSIZE_0(comp1);
+ dev->capability |= DWC_I2S_PLAY;
+ /* only configure Combined DMA addr, Our scenario is not Dedicated DMA case */
+ dev->play_dma_data.addr_width = bus_widths[idx];
+ dev->play_dma_data.fifo_size = fifo_depth *
+ (fifo_width[idx2]) >> 3;
+ if (of_node_name_prefix(pcm->chan[SNDRV_PCM_STREAM_PLAYBACK]->device->dev->of_node,
+ "dma-controller-hsp")) {
+ dev->play_dma_data.addr = dma_map_resource(
+ pcm->chan[SNDRV_PCM_STREAM_PLAYBACK]->device->dev,
+ res->start + TXDMA_CH(0),
+ dev->play_dma_data.fifo_size,
+ DMA_BIDIRECTIONAL,
+ DMA_ATTR_SKIP_CPU_SYNC);
+ } else {
+ dev->play_dma_data.addr = res->start + TXDMA_CH(0);
+ }
+ dev->play_dma_data.maxburst = 16;
+ }
+ if (COMP1_RX_ENABLED(comp1)) {
+ idx2 = COMP2_RX_WORDSIZE_0(comp2);
+ dev->capability |= DWC_I2S_RECORD;
+ /* only configure Combined DMA addr, Our scenario is not Dedicated DMA case */
+ dev->capture_dma_data.addr_width = bus_widths[idx];
+ dev->capture_dma_data.fifo_size = fifo_depth *
+ (fifo_width[idx2]) >> 3;
+ if (of_node_name_prefix(pcm->chan[SNDRV_PCM_STREAM_CAPTURE]->device->dev->of_node,
+ "dma-controller-hsp")) {
+ dev->capture_dma_data.addr = dma_map_resource(
+ pcm->chan[SNDRV_PCM_STREAM_CAPTURE]->device->dev,
+ res->start + RXDMA_CH(0),
+ dev->capture_dma_data.fifo_size,
+ DMA_BIDIRECTIONAL,
+ DMA_ATTR_SKIP_CPU_SYNC);
+ } else {
+ dev->capture_dma_data.addr = res->start + RXDMA_CH(0);
+ }
+ dev->capture_dma_data.maxburst = 16;
+ }
+ return 0;
+}
+
+static int i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC;
+ return 0;
+}
+
+
+static int i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct i2s_dev *i2s_drvdata = snd_soc_dai_get_drvdata(dai);
+ struct i2s_clk_config_data *config = &i2s_drvdata->config;
+ struct device_node *node = i2s_drvdata->dev->of_node;
+ struct regmap *vo_mclk_sel_regmap;
+ uint32_t vo_mclk_sel_reg;
+ uint32_t vo_mclk_sel;
+ int ret;
+ uint32_t div_num = 0;
+ uint32_t div_num_reg;
+
+ dev_dbg(i2s_drvdata->dev, "sample rate:%d, chan:%d, width:%d\n",
+ params_rate(params), params_channels(params), params_width(params));
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ config->data_width = 16;
+ i2s_drvdata->ccr = CLOCK_CYCLES_32 << CCR_WSS_POS |
+ NO_CLOCK_GATING;
+ i2s_drvdata->xfer_resolution = RESOLUTION_16_BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ config->data_width = 24;
+ i2s_drvdata->ccr = CLOCK_CYCLES_32 << CCR_WSS_POS |
+ NO_CLOCK_GATING;
+ i2s_drvdata->xfer_resolution = RESOLUTION_24_BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ config->data_width = 32;
+ i2s_drvdata->ccr = CLOCK_CYCLES_32 << CCR_WSS_POS |
+ NO_CLOCK_GATING;
+ i2s_drvdata->xfer_resolution = RESOLUTION_32_BIT;
+ break;
+ default:
+ dev_err(i2s_drvdata->dev, "eswin-i2s: unsupported PCM fmt");
+ return -EINVAL;
+ }
+ config->chan_nr = MAX_CHANNEL_NUM;
+ switch (config->chan_nr) {
+ case TWO_CHANNEL_SUPPORT:
+ break;
+ default:
+ dev_err(i2s_drvdata->dev, "channel not supported\n");
+ return -EINVAL;
+ }
+ i2s_config(i2s_drvdata, substream->stream);
+ i2s_write_reg(i2s_drvdata->i2s_base, CCR, i2s_drvdata->ccr);
+ config->sample_rate = params_rate(params);
+ if (i2s_drvdata->capability & DW_I2S_MASTER) {
+ if (!i2s_drvdata->eswin_plat) {
+ vo_mclk_sel_regmap =
+ syscon_regmap_lookup_by_phandle(node, "vo_mclk_sel,syscrg");
+ if (IS_ERR(vo_mclk_sel_regmap)) {
+ dev_err(i2s_drvdata->dev, "No vo_mclk_sel,syscrg phandle specified\n");
+ return PTR_ERR(vo_mclk_sel_regmap);
+ }
+ ret = of_property_read_u32_index(node, "vo_mclk_sel,syscrg", 1,
+ &vo_mclk_sel_reg);
+ if (ret) {
+ dev_err(i2s_drvdata->dev, "can't get vo_mclk_sel_reg offset (%d)\n", ret);
+ return ret;
+ }
+ regmap_read(vo_mclk_sel_regmap, vo_mclk_sel_reg, &vo_mclk_sel);
+ vo_mclk_sel &= ~VO_MCLK_DIVSOR_MASK;
+
+ switch (config->sample_rate) {
+ case 96000:
+ vo_mclk_sel |= (0x10 << VO_MCLK_DIVSOR_OFFSET);
+ break;
+ case 48000:
+ vo_mclk_sel |= (0x12 << VO_MCLK_DIVSOR_OFFSET);
+ break;
+ case 44100:
+ vo_mclk_sel |= (0x11 << VO_MCLK_DIVSOR_OFFSET);
+ break;
+ default:
+ dev_err(i2s_drvdata->dev, "Can't support sample rate: %d\n",
+ config->sample_rate);
+ return -EINVAL;
+ }
+ regmap_write(vo_mclk_sel_regmap, vo_mclk_sel_reg, vo_mclk_sel);
+ } else {
+ if (MAX_SAMPLE_RATE_SUPPORT % config->sample_rate != 0) {
+ dev_err(i2s_drvdata->dev, "Not support sample rate: %d\n", config->sample_rate);
+ return -EINVAL;
+ }
+
+ div_num = MAX_SAMPLE_RATE_SUPPORT / config->sample_rate - 1;
+
+ if (i2s_drvdata->active) {
+ if (i2s_drvdata->i2s_div_num != div_num) {
+ dev_err(i2s_drvdata->dev, "Not support the playback and capture clocks are different\n");
+ return -EINVAL;
+ }
+ } else {
+ div_num_reg = i2s_read_reg(i2s_drvdata->i2s_div_base, 0) & ~DIV_NUM_MASK;
+ div_num_reg |= div_num;
+
+ dev_dbg(i2s_drvdata->dev, "div num:0x%x\n", div_num);
+ i2s_drvdata->i2s_div_num = div_num;
+ i2s_write_reg(i2s_drvdata->i2s_div_base, 0, div_num_reg);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct i2s_dev *i2s_drvdata = snd_soc_dai_get_drvdata(dai);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ i2s_write_reg(i2s_drvdata->i2s_base, TXFFR, 1);
+ else
+ i2s_write_reg(i2s_drvdata->i2s_base, RXFFR, 1);
+
+ return 0;
+}
+
+static int i2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct i2s_dev *i2s_drvdata = snd_soc_dai_get_drvdata(dai);
+ int ret = 0;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ i2s_drvdata->active++;
+ i2s_start(i2s_drvdata, substream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ i2s_drvdata->playback_active = true;
+ } else {
+ i2s_drvdata->capture_active = true;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ i2s_drvdata->active--;
+ i2s_stop(i2s_drvdata, substream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ i2s_drvdata->playback_active = false;
+ } else {
+ i2s_drvdata->capture_active = false;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ if (dev->capability & DW_I2S_SLAVE)
+ ret = 0;
+ else
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ if (dev->capability & DW_I2S_MASTER)
+ ret = 0;
+ else
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_BC_FP:
+ case SND_SOC_DAIFMT_BP_FC:
+ ret = -EINVAL;
+ break;
+ default:
+ dev_dbg(dev->dev, "dwc : Invalid clock provider format\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int i2s_pcm_dai_probe(struct snd_soc_dai *dai)
+{
+ struct i2s_dev *i2s_drvdata = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &i2s_drvdata->play_dma_data, &i2s_drvdata->capture_dma_data);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops i2s_dai_ops = {
+ .probe = i2s_pcm_dai_probe,
+ .startup = i2s_startup,
+ .hw_params = i2s_hw_params,
+ .prepare = i2s_prepare,
+ .trigger = i2s_trigger,
+ .set_fmt = i2s_set_fmt,
+};
+
+#ifdef CONFIG_PM
+static int i2s_runtime_suspend(struct device *dev)
+{
+ struct i2s_dev *i2s_drvdata = dev_get_drvdata(dev);
+ if (i2s_drvdata->capability & DW_I2S_MASTER)
+ clk_disable(i2s_drvdata->clk);
+
+ return 0;
+}
+
+static int i2s_runtime_resume(struct device *dev)
+{
+ struct i2s_dev *i2s_drvdata = dev_get_drvdata(dev);
+ if (i2s_drvdata->capability & DW_I2S_MASTER)
+ clk_enable(i2s_drvdata->clk);
+
+ return 0;
+}
+
+static int i2s_suspend(struct snd_soc_component *component)
+{
+ struct i2s_dev *i2s_drvdata = snd_soc_component_get_drvdata(component);
+ if (i2s_drvdata->capability & DW_I2S_MASTER) {
+ clk_disable(i2s_drvdata->clk);
+ }
+ return 0;
+}
+
+static int i2s_resume(struct snd_soc_component *component)
+{
+ struct i2s_dev *i2s_drvdata = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dai *dai = NULL;
+ int stream;
+
+ if (i2s_drvdata->capability & DW_I2S_MASTER)
+ clk_enable(i2s_drvdata->clk);
+
+ for_each_component_dais(component, dai) {
+ for_each_pcm_streams(stream)
+ if (snd_soc_dai_stream_active(dai, stream))
+ i2s_config(i2s_drvdata, stream);
+ }
+ return 0;
+}
+
+#else
+#define i2s_suspend NULL
+#define i2s_resume NULL
+#endif
+
+static int i2s_reset(struct platform_device *pdev, struct i2s_dev *i2s)
+{
+ struct reset_control *rst;
+ struct reset_control *prst;
+ struct reset_control *voprst;
+ int ret;
+
+ rst = devm_reset_control_get_optional_exclusive(&pdev->dev, "i2srst");
+ if (IS_ERR(rst)) {
+ return PTR_ERR(rst);
+ }
+
+ prst = devm_reset_control_get_optional_exclusive(&pdev->dev, "i2sprst");
+ if (IS_ERR(prst)) {
+ return PTR_ERR(prst);
+ }
+
+ voprst = devm_reset_control_get_optional_exclusive(&pdev->dev, "voprst");
+ if (IS_ERR(prst)) {
+ return PTR_ERR(prst);
+ }
+
+ ret = reset_control_assert(rst);
+ WARN_ON(0 != ret);
+ ret = reset_control_assert(prst);
+ WARN_ON(0 != ret);
+ ret = reset_control_deassert(rst);
+ WARN_ON(0 != ret);
+ ret = reset_control_deassert(prst);
+ WARN_ON(0 != ret);
+ ret = reset_control_deassert(voprst);
+ WARN_ON(0 != ret);
+
+ return 0;
+}
+
+static int i2s_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_dev *i2s_drvdata = snd_soc_component_get_drvdata(component);
+
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK && i2s_drvdata->playback_active == true) ||
+ (substream->stream == SNDRV_PCM_STREAM_CAPTURE && i2s_drvdata->capture_active == true)) {
+ dev_err(i2s_drvdata->dev, "i2s is busying\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver i2s_component = {
+ .name = "i2s",
+ .open = i2s_open,
+ .suspend = i2s_suspend,
+ .resume = i2s_resume,
+};
+
+static struct snd_soc_dai_driver i2s_dai[4] = {
+ {
+ .name = "i2s0-hdmi",
+ .id = 0,
+ .ops = &i2s_dai_ops,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ESW_I2S_RATES,
+ .formats = ESW_I2S_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ESW_I2S_RATES,
+ .formats = ESW_I2S_FORMATS,
+ },
+ },
+ {
+ .name = "i2s0",
+ .id = 1,
+ .ops = &i2s_dai_ops,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ESW_I2S_RATES,
+ .formats = ESW_I2S_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ESW_I2S_RATES,
+ .formats = ESW_I2S_FORMATS,
+ },
+ },
+ {
+ .name = "i2s1",
+ .id = 0,
+ .ops = &i2s_dai_ops,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ESW_I2S_RATES,
+ .formats = ESW_I2S_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ESW_I2S_RATES,
+ .formats = ESW_I2S_FORMATS,
+ },
+ },
+ {
+ .name = "i2s2",
+ .id = 0,
+ .ops = &i2s_dai_ops,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ESW_I2S_RATES,
+ .formats = ESW_I2S_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = MIN_CHANNEL_NUM,
+ .channels_max = MAX_CHANNEL_NUM,
+ .rates = ESW_I2S_RATES,
+ .formats = ESW_I2S_FORMATS,
+ },
+ },
+};
+
+static int i2s_probe(struct platform_device *pdev)
+{
+ struct i2s_dev *i2s_drvdata;
+ struct resource *res;
+ int ret;
+ const char *clk_id;
+ struct snd_dmaengine_pcm_config *config;
+
+ dev_info(&pdev->dev, "dev name:%s\n", pdev->dev.of_node->name);
+ i2s_drvdata = devm_kzalloc(&pdev->dev, sizeof(*i2s_drvdata), GFP_KERNEL);
+ if (!i2s_drvdata)
+ return -ENOMEM;
+
+ config = devm_kzalloc(&pdev->dev,
+ sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+ config->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = "tx";
+ config->chan_names[SNDRV_PCM_STREAM_CAPTURE] = "rx";
+ config->prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;
+
+ res = platform_get_resource(pdev,IORESOURCE_MEM, 0);
+ i2s_drvdata->i2s_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(i2s_drvdata->i2s_base)) {
+ dev_err(&pdev->dev, "devm_ioremap_resource failed\n");
+ return PTR_ERR(i2s_drvdata->i2s_base);
+ }
+ i2s_drvdata->dev = &pdev->dev;
+
+ clk_id = "mclk";
+ if (of_node_name_prefix(pdev->dev.of_node, "i2s0")) {
+ i2s_drvdata->clk = devm_clk_get(&pdev->dev, clk_id);
+ if (IS_ERR(i2s_drvdata->clk))
+ return PTR_ERR(i2s_drvdata->clk);
+ ret = clk_prepare_enable(i2s_drvdata->clk);
+ if (ret < 0)
+ return ret;
+ ret = clk_set_rate(i2s_drvdata->clk, MAX_SAMPLE_RATE_CLK);
+ if (ret) {
+ dev_err(i2s_drvdata->dev, "Can't set I2S clock rate: %d\n", ret);
+ }
+
+ ret = i2s_reset(pdev, i2s_drvdata);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "i2s_reset failed\n");
+ goto err_probe;
+ }
+ }
+
+ dev_set_drvdata(&pdev->dev, i2s_drvdata);
+
+ if (of_node_name_prefix(pdev->dev.of_node, "i2s0")) {
+ i2s_drvdata->i2s_div_base = devm_ioremap(i2s_drvdata->dev, VO_TOP_CSR + VO_I2S0_DIV_NUM, 4);
+ if (!i2s_drvdata->i2s_div_base) {
+ dev_err(&pdev->dev, "failed to remap i2s0 div config\n");
+ return -ENOMEM;
+ }
+ ret = devm_snd_soc_register_component(&pdev->dev, &i2s_component,
+ &i2s_dai[0], 2);
+ } else if (of_node_name_prefix(pdev->dev.of_node, "i2s1")) {
+ i2s_drvdata->i2s_div_base = devm_ioremap(i2s_drvdata->dev, VO_TOP_CSR + VO_I2S1_DIV_NUM, 4);
+ if (!i2s_drvdata->i2s_div_base) {
+ dev_err(&pdev->dev, "failed to remap i2s1 div config\n");
+ return -ENOMEM;
+ }
+ ret = devm_snd_soc_register_component(&pdev->dev, &i2s_component,
+ &i2s_dai[2], 1);
+ } else {
+ i2s_drvdata->i2s_div_base = devm_ioremap(i2s_drvdata->dev, VO_TOP_CSR + VO_I2S2_DIV_NUM, 4);
+ if (!i2s_drvdata->i2s_div_base) {
+ dev_err(&pdev->dev, "failed to remap i2s2 div config\n");
+ return -ENOMEM;
+ }
+ ret = devm_snd_soc_register_component(&pdev->dev, &i2s_component,
+ &i2s_dai[3], 1);
+ }
+ if (ret != 0) {
+ dev_err(&pdev->dev, "not able to register dai\n");
+ goto err_probe;
+ }
+
+ i2s_drvdata->use_pio = false;
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, config, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register pcm: %d\n", ret);
+ goto err_probe;
+ }
+
+ i2s_drvdata->i2s_reg_comp1 = I2S_COMP_PARAM_1;
+ i2s_drvdata->i2s_reg_comp2 = I2S_COMP_PARAM_2;
+ ret = i2s_configure_dai_by_dt(i2s_drvdata, &i2s_dai[0], res);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "i2s_configure_dai_by_dt failed\n");
+ return ret;
+ }
+
+ ret = device_property_read_u32(&pdev->dev, "eswin-plat", &i2s_drvdata->eswin_plat);
+ if (0 != ret) {
+ dev_warn(&pdev->dev, "Failed to get eswin platform\n");
+ i2s_drvdata->eswin_plat = 0;
+ }
+ dev_info(&pdev->dev, "eswin platform:%d\n", i2s_drvdata->eswin_plat);
+
+ pm_runtime_enable(&pdev->dev);
+
+ audio_proc_module_init();
+
+ return 0;
+err_probe:
+ if (i2s_drvdata->capability & DW_I2S_MASTER)
+ clk_disable_unprepare(i2s_drvdata->clk);
+ return ret;
+}
+
+static int i2s_remove(struct platform_device *pdev)
+{
+ struct i2s_dev *i2s_drvdata = dev_get_drvdata(&pdev->dev);
+ if (i2s_drvdata->capability & DW_I2S_MASTER)
+ clk_disable_unprepare(i2s_drvdata->clk);
+
+ pm_runtime_disable(&pdev->dev);
+
+ audio_proc_module_exit();
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id i2s_of_match[] = {
+ { .compatible = "snps,i2s", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, i2s_of_match);
+#endif
+
+static const struct dev_pm_ops i2s_pm_ops = {
+ SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume, NULL)
+};
+
+static struct platform_driver i2s_driver = {
+ .probe = i2s_probe,
+ .remove = i2s_remove,
+ .driver = {
+ .name = "i2s",
+ .of_match_table = of_match_ptr(i2s_of_match),
+ .pm = &i2s_pm_ops,
+ },
+};
+
+module_platform_driver(i2s_driver);
+
+MODULE_AUTHOR("ESWIN, INC.");
+MODULE_DESCRIPTION("I2S driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/eswin/esw-i2s.h b/sound/soc/eswin/esw-i2s.h
new file mode 100644
index 000000000000..14c4980c8267
--- /dev/null
+++ b/sound/soc/eswin/esw-i2s.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (ST) 2012 Rajeev Kumar (rajeevkumar.linux@gmail.com)
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+/*
+ *
+ * Copyright (C) 2021 ESWIN, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __I2S_H
+#define __I2S_H
+
+#include <linux/device.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/clk.h>
+#include <linux/types.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/designware_i2s.h>
+
+
+/* common register for all channel */
+#define IER 0x000
+#define IRER 0x004
+#define ITER 0x008
+#define CER 0x00C
+#define CCR 0x010
+#define RXFFR 0x014
+#define TXFFR 0x018
+
+/* DMA Control Register Offset */
+#define DMACR 0x200
+/* DMA Control Register fields */
+#define DMAEN_TXBLOCK BIT(17)
+#define DMAEN_RXBLOCK BIT(16)
+#define DMAEN_TXCH_3 BIT(11)
+#define DMAEN_TXCH_2 BIT(10)
+#define DMAEN_TXCH_1 BIT(9)
+#define DMAEN_TXCH_0 BIT(8)
+#define DMAEN_RXCH_3 BIT(3)
+#define DMAEN_RXCH_2 BIT(2)
+#define DMAEN_RXCH_1 BIT(1)
+#define DMAEN_RXCH_0 BIT(0)
+
+/* Interrupt status register fields */
+#define ISR_TXFO BIT(5)
+#define ISR_TXFE BIT(4)
+#define ISR_RXFO BIT(1)
+#define ISR_RXDA BIT(0)
+
+/* I2STxRxRegisters for all channels */
+#define LRBR_LTHR(x) (0x40 * x + 0x020)
+#define RRBR_RTHR(x) (0x40 * x + 0x024)
+#define RER(x) (0x40 * x + 0x028)
+#define TER(x) (0x40 * x + 0x02C)
+#define RCR(x) (0x40 * x + 0x030)
+#define TCR(x) (0x40 * x + 0x034)
+#define ISR(x) (0x40 * x + 0x038)
+#define IMR(x) (0x40 * x + 0x03C)
+#define ROR(x) (0x40 * x + 0x040)
+#define TOR(x) (0x40 * x + 0x044)
+#define RFCR(x) (0x40 * x + 0x048)
+#define TFCR(x) (0x40 * x + 0x04C)
+#define RFF(x) (0x40 * x + 0x050)
+#define TFF(x) (0x40 * x + 0x054)
+
+/* I2SCOMPRegisters */
+#define I2S_COMP_PARAM_2 0x01F0
+#define I2S_COMP_PARAM_1 0x01F4
+#define I2S_COMP_VERSION 0x01F8
+#define I2S_COMP_TYPE 0x01FC
+
+/* I2S DMA registers */
+#define RXDMA_CH(x) (0x4 * x + 0x204)
+#define TXDMA_CH(x) (0x4 * x + 0x214)
+
+/*
+ * Component parameter register fields - define the I2S block's
+ * configuration.
+ */
+#define COMP1_TX_WORDSIZE_3(r) (((r) & GENMASK(27, 25)) >> 25)
+#define COMP1_TX_WORDSIZE_2(r) (((r) & GENMASK(24, 22)) >> 22)
+#define COMP1_TX_WORDSIZE_1(r) (((r) & GENMASK(21, 19)) >> 19)
+#define COMP1_TX_WORDSIZE_0(r) (((r) & GENMASK(18, 16)) >> 16)
+#define COMP1_TX_CHANNELS(r) (((r) & GENMASK(10, 9)) >> 9)
+#define COMP1_RX_CHANNELS(r) (((r) & GENMASK(8, 7)) >> 7)
+#define COMP1_RX_ENABLED(r) (((r) & BIT(6)) >> 6)
+#define COMP1_TX_ENABLED(r) (((r) & BIT(5)) >> 5)
+#define COMP1_MODE_EN(r) (((r) & BIT(4)) >> 4)
+#define COMP1_FIFO_DEPTH_GLOBAL(r) (((r) & GENMASK(3, 2)) >> 2)
+#define COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0)
+#define COMP2_RX_WORDSIZE_3(r) (((r) & GENMASK(12, 10)) >> 10)
+#define COMP2_RX_WORDSIZE_2(r) (((r) & GENMASK(9, 7)) >> 7)
+#define COMP2_RX_WORDSIZE_1(r) (((r) & GENMASK(5, 3)) >> 3)
+#define COMP2_RX_WORDSIZE_0(r) (((r) & GENMASK(2, 0)) >> 0)
+
+/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
+#define COMP_MAX_WORDSIZE (1 << 3)
+#define COMP_MAX_DATA_WIDTH (1 << 2)
+#define MAX_CHANNEL_NUM 2
+#define MIN_CHANNEL_NUM 2
+#define STEREO 0
+#define TDM 1
+
+#define CCR_SCLKG_POS 0
+#define CCR_WSS_POS 3
+
+enum {
+ CLOCK_CYCLES_16,
+ CLOCK_CYCLES_24,
+ CLOCK_CYCLES_32
+};
+
+enum {
+ NO_CLOCK_GATING,
+ GATE_CLOCK_CYCLES_12,
+ GATE_CLOCK_CYCLES_16,
+ GATE_CLOCK_CYCLES_20,
+ GATE_CLOCK_CYCLES_24
+};
+
+enum {
+ IGNORE_WORD_LENGTH,
+ RESOLUTION_12_BIT,
+ RESOLUTION_16_BIT,
+ RESOLUTION_20_BIT,
+ RESOLUTION_24_BIT,
+ RESOLUTION_32_BIT
+};
+
+struct i2s_dev {
+ void __iomem *i2s_base;
+ struct clk *clk;
+ struct device *dev;
+ unsigned int i2s_reg_comp1;
+ unsigned int i2s_reg_comp2;
+ unsigned int capability;
+ u32 fifo_th;
+ bool use_pio;
+ /* data related to DMA transfers b/w i2s and DMAC */
+ struct snd_dmaengine_dai_dma_data play_dma_data;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct i2s_clk_config_data config;
+ struct snd_pcm_substream __rcu *tx_substream;
+ struct snd_pcm_substream __rcu *rx_substream;
+ unsigned int (*tx_fn)(struct i2s_dev *dev,
+ struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+ bool *period_elapsed,int type);
+ unsigned int (*rx_fn)(struct i2s_dev *dev,
+ struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
+ bool *period_elapsed,int type);
+ unsigned int tx_ptr;
+ unsigned int rx_ptr;
+ u32 xfer_resolution;
+ int active;
+ u32 ccr;
+ void __iomem *i2s_div_base;
+ u32 i2s_div_num;
+ bool playback_active;
+ bool capture_active;
+ u32 eswin_plat;
+};
+
+#endif /* __I2S_H */
--
2.47.0