From 2218519dddb322b2b69745e011e830fa6f7a163b Mon Sep 17 00:00:00 2001 From: yangqiang 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 + */ + +/* + * 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 +#include +#include + +#include + +#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 "); +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 + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, ®); + 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 "); +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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include + + +/* 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