1876 lines
56 KiB
Diff
1876 lines
56 KiB
Diff
|
From 08d8881e87cf80b9fe04361dde8cc7a592884499 Mon Sep 17 00:00:00 2001
|
||
|
From: afaulkner420 <afaulkner420@gmail.com>
|
||
|
Date: Fri, 25 Mar 2022 20:33:02 +0000
|
||
|
Subject: [PATCH 144/153] allwinner: h6: Support ac200 audio codec
|
||
|
|
||
|
---
|
||
|
drivers/mfd/Makefile | 2 +-
|
||
|
drivers/mfd/{ac200.c => sunxi-ac200.c} | 16 +-
|
||
|
include/linux/mfd/ac200.h | 2 +
|
||
|
sound/soc/codecs/Kconfig | 8 +
|
||
|
sound/soc/codecs/Makefile | 2 +
|
||
|
sound/soc/codecs/acx00.c | 1371 ++++++++++++++++++++++++
|
||
|
sound/soc/codecs/acx00.h | 356 ++++++
|
||
|
7 files changed, 1755 insertions(+), 2 deletions(-)
|
||
|
rename drivers/mfd/{ac200.c => sunxi-ac200.c} (93%)
|
||
|
create mode 100644 sound/soc/codecs/acx00.c
|
||
|
create mode 100644 sound/soc/codecs/acx00.h
|
||
|
|
||
|
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
|
||
|
index b2e69fbce..0e4a09539 100644
|
||
|
--- a/drivers/mfd/Makefile
|
||
|
+++ b/drivers/mfd/Makefile
|
||
|
@@ -141,7 +141,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
|
||
|
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
|
||
|
|
||
|
obj-$(CONFIG_MFD_AC100) += ac100.o
|
||
|
-obj-$(CONFIG_MFD_AC200) += ac200.o
|
||
|
+obj-$(CONFIG_MFD_AC200) += sunxi-ac200.o
|
||
|
obj-$(CONFIG_MFD_AXP20X) += axp20x.o
|
||
|
obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o
|
||
|
obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o
|
||
|
diff --git a/drivers/mfd/ac200.c b/drivers/mfd/sunxi-ac200.c
|
||
|
similarity index 93%
|
||
|
rename from drivers/mfd/ac200.c
|
||
|
rename to drivers/mfd/sunxi-ac200.c
|
||
|
index 570573790..368a54587 100644
|
||
|
--- a/drivers/mfd/ac200.c
|
||
|
+++ b/drivers/mfd/sunxi-ac200.c
|
||
|
@@ -41,6 +41,7 @@ static const struct regmap_range_cfg ac200_range_cfg[] = {
|
||
|
};
|
||
|
|
||
|
static const struct regmap_config ac200_regmap_config = {
|
||
|
+ .name = "ac200",
|
||
|
.reg_bits = 8,
|
||
|
.val_bits = 16,
|
||
|
.ranges = ac200_range_cfg,
|
||
|
@@ -75,6 +76,10 @@ static const struct mfd_cell ac200_cells[] = {
|
||
|
.resources = ephy_resource,
|
||
|
.of_compatible = "x-powers,ac200-ephy",
|
||
|
},
|
||
|
+ {
|
||
|
+ .name = "acx00-codec",
|
||
|
+ .of_compatible = "x-powers,ac200-codec",
|
||
|
+ },
|
||
|
};
|
||
|
|
||
|
static int ac200_i2c_probe(struct i2c_client *i2c,
|
||
|
@@ -97,8 +102,17 @@ static int ac200_i2c_probe(struct i2c_client *i2c,
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
- /* do a reset to put chip in a known state */
|
||
|
+ ac200->clk = devm_clk_get(dev, NULL);
|
||
|
+ if (IS_ERR(ac200->clk)) {
|
||
|
+ dev_err(dev, "Can't obtain the clock!\n");
|
||
|
+ return PTR_ERR(ac200->clk);
|
||
|
+ }
|
||
|
|
||
|
+ ret = clk_prepare_enable(ac200->clk);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ /* do a reset to put chip in a known state */
|
||
|
ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
diff --git a/include/linux/mfd/ac200.h b/include/linux/mfd/ac200.h
|
||
|
index 0c677094a..c8c140226 100644
|
||
|
--- a/include/linux/mfd/ac200.h
|
||
|
+++ b/include/linux/mfd/ac200.h
|
||
|
@@ -9,6 +9,7 @@
|
||
|
#define __LINUX_MFD_AC200_H
|
||
|
|
||
|
#include <linux/regmap.h>
|
||
|
+#include <linux/clk.h>
|
||
|
|
||
|
/* interface registers (can be accessed from any page) */
|
||
|
#define AC200_TWI_CHANGE_TO_RSB 0x3E
|
||
|
@@ -201,6 +202,7 @@
|
||
|
#define AC200_IC_CHARA1 0xA1F2
|
||
|
|
||
|
struct ac200_dev {
|
||
|
+ struct clk *clk;
|
||
|
struct regmap *regmap;
|
||
|
struct regmap_irq_chip_data *regmap_irqc;
|
||
|
};
|
||
|
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
|
||
|
index 944d0ea1a..359c515ef 100644
|
||
|
--- a/sound/soc/codecs/Kconfig
|
||
|
+++ b/sound/soc/codecs/Kconfig
|
||
|
@@ -2170,4 +2170,12 @@ config SND_SOC_LPASS_TX_MACRO
|
||
|
select SND_SOC_LPASS_MACRO_COMMON
|
||
|
tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)"
|
||
|
|
||
|
+config SND_SOC_ACX00
|
||
|
+ tristate "ACX00 Codec"
|
||
|
+ select MFD_ACX00
|
||
|
+ default n
|
||
|
+ help
|
||
|
+ ACX00 now used as SUN50IW6 internal Codec, Connect Through I2S0.
|
||
|
+ Say Y or M if you want to add support internal audio codec.
|
||
|
+
|
||
|
endmenu
|
||
|
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
|
||
|
index e2d463a31..ccb2a0fe4 100644
|
||
|
--- a/sound/soc/codecs/Makefile
|
||
|
+++ b/sound/soc/codecs/Makefile
|
||
|
@@ -347,6 +347,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
|
||
|
snd-soc-wsa881x-objs := wsa881x.o
|
||
|
snd-soc-wsa883x-objs := wsa883x.o
|
||
|
snd-soc-zl38060-objs := zl38060.o
|
||
|
+snd-soc-acx00-objs := acx00.o
|
||
|
# Amp
|
||
|
snd-soc-max9877-objs := max9877.o
|
||
|
snd-soc-max98504-objs := max98504.o
|
||
|
@@ -709,6 +710,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
|
||
|
obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o
|
||
|
obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o
|
||
|
obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o
|
||
|
+obj-$(CONFIG_SND_SOC_ACX00) += snd-soc-acx00.o
|
||
|
|
||
|
# Amp
|
||
|
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
|
||
|
diff --git a/sound/soc/codecs/acx00.c b/sound/soc/codecs/acx00.c
|
||
|
new file mode 100644
|
||
|
index 000000000..ab7467e4e
|
||
|
--- /dev/null
|
||
|
+++ b/sound/soc/codecs/acx00.c
|
||
|
@@ -0,0 +1,1371 @@
|
||
|
+/*
|
||
|
+ * acx00.c -- ACX00 ALSA Soc Audio Codec driver
|
||
|
+ *
|
||
|
+ * (C) Copyright 2010-2016 Allwinnertech Technology., Ltd.
|
||
|
+ *
|
||
|
+ * Author: Wolfgang Huang <huangjinhui@allwinner.com>
|
||
|
+ *
|
||
|
+ * This program is free software; you can redistribute it and/or modify
|
||
|
+ * it under the terms of the GNU General Public License version 2 as
|
||
|
+ * published by the Free Software Foundation.
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#include <linux/module.h>
|
||
|
+#include <linux/moduleparam.h>
|
||
|
+#include <linux/kernel.h>
|
||
|
+#include <linux/init.h>
|
||
|
+#include <linux/firmware.h>
|
||
|
+#include <linux/delay.h>
|
||
|
+#include <linux/pm.h>
|
||
|
+#include <linux/i2c.h>
|
||
|
+#include <linux/clk.h>
|
||
|
+#include <linux/of_gpio.h>
|
||
|
+#include <linux/of_device.h>
|
||
|
+#include <linux/of_platform.h>
|
||
|
+#include <linux/debugfs.h>
|
||
|
+#include <linux/slab.h>
|
||
|
+#include <linux/mfd/ac200.h>
|
||
|
+#include <sound/core.h>
|
||
|
+#include <sound/pcm.h>
|
||
|
+#include <sound/pcm_params.h>
|
||
|
+#include <sound/soc.h>
|
||
|
+#include <sound/initval.h>
|
||
|
+#include <sound/tlv.h>
|
||
|
+#include <linux/workqueue.h>
|
||
|
+
|
||
|
+#include "acx00.h"
|
||
|
+
|
||
|
+
|
||
|
+#define ACX00_DEF_VOL 0x9F9F
|
||
|
+#undef ACX00_DAPM_LINEOUT
|
||
|
+
|
||
|
+struct acx00_priv {
|
||
|
+ struct ac200_dev *acx00; /* parent mfd device struct */
|
||
|
+ struct snd_soc_component *component;
|
||
|
+ struct clk *clk;
|
||
|
+ unsigned int sample_rate;
|
||
|
+ unsigned int fmt;
|
||
|
+ unsigned int enable;
|
||
|
+ unsigned int spk_gpio;
|
||
|
+ unsigned int switch_gpio;
|
||
|
+ bool spk_gpio_used;
|
||
|
+ struct mutex mutex;
|
||
|
+ struct delayed_work spk_work;
|
||
|
+ struct delayed_work resume_work;
|
||
|
+};
|
||
|
+
|
||
|
+struct sample_rate {
|
||
|
+ unsigned int samplerate;
|
||
|
+ unsigned int rate_bit;
|
||
|
+};
|
||
|
+
|
||
|
+static const struct sample_rate sample_rate_conv[] = {
|
||
|
+ {44100, 7},
|
||
|
+ {48000, 8},
|
||
|
+ {8000, 0},
|
||
|
+ {32000, 6},
|
||
|
+ {22050, 4},
|
||
|
+ {24000, 5},
|
||
|
+ {16000, 3},
|
||
|
+ {11025, 1},
|
||
|
+ {12000, 2},
|
||
|
+ {192000, 10},
|
||
|
+ {96000, 9},
|
||
|
+};
|
||
|
+
|
||
|
+void __iomem *io_stat_addr;
|
||
|
+
|
||
|
+static const DECLARE_TLV_DB_SCALE(i2s_mixer_adc_tlv, -600, 600, 1);
|
||
|
+static const DECLARE_TLV_DB_SCALE(i2s_mixer_dac_tlv, -600, 600, 1);
|
||
|
+static const DECLARE_TLV_DB_SCALE(dac_mixer_adc_tlv, -600, 600, 1);
|
||
|
+static const DECLARE_TLV_DB_SCALE(dac_mixer_dac_tlv, -600, 600, 1);
|
||
|
+static const DECLARE_TLV_DB_SCALE(line_out_tlv, -450, 150, 0);
|
||
|
+static const DECLARE_TLV_DB_SCALE(mic_out_tlv, -450, 150, 0);
|
||
|
+static const DECLARE_TLV_DB_SCALE(phoneout_tlv, -450, 150, 0);
|
||
|
+static const DECLARE_TLV_DB_SCALE(adc_input_tlv, -450, 150, 0);
|
||
|
+static const DECLARE_TLV_DB_SCALE(lineout_tlv, -4800, 150, 1);
|
||
|
+static const unsigned int mic_boost_tlv[] = {
|
||
|
+ TLV_DB_RANGE_HEAD(2),
|
||
|
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
|
||
|
+ 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct snd_kcontrol_new acx00_codec_controls[] = {
|
||
|
+ SOC_DOUBLE_TLV("I2S Mixer ADC Volume", AC_I2S_MIXER_GAIN,
|
||
|
+ I2S_MIXERL_GAIN_ADC, I2S_MIXERR_GAIN_ADC,
|
||
|
+ 0x1, 0, i2s_mixer_adc_tlv),
|
||
|
+ SOC_DOUBLE_TLV("I2S Mixer DAC Volume", AC_I2S_MIXER_GAIN,
|
||
|
+ I2S_MIXERL_GAIN_DAC, I2S_MIXERR_GAIN_DAC,
|
||
|
+ 0x1, 0, i2s_mixer_dac_tlv),
|
||
|
+ SOC_DOUBLE_TLV("DAC Mixer ADC Volume", AC_DAC_MIXER_GAIN,
|
||
|
+ DAC_MIXERL_GAIN_ADC, DAC_MIXERR_GAIN_ADC,
|
||
|
+ 0x1, 0, dac_mixer_adc_tlv),
|
||
|
+ SOC_DOUBLE_TLV("DAC Mxier DAC Volume", AC_DAC_MIXER_GAIN,
|
||
|
+ DAC_MIXERL_GAIN_DAC, DAC_MIXERR_GAIN_DAC,
|
||
|
+ 0x1, 0, dac_mixer_dac_tlv),
|
||
|
+ SOC_SINGLE_TLV("Line Out Mixer Volume", AC_OUT_MIXER_CTL,
|
||
|
+ OUT_MIXER_LINE_VOL, 0x7, 0, line_out_tlv),
|
||
|
+ SOC_DOUBLE_TLV("MIC Out Mixer Volume", AC_OUT_MIXER_CTL,
|
||
|
+ OUT_MIXER_MIC1_VOL, OUT_MIXER_MIC2_VOL,
|
||
|
+ 0x7, 0, mic_out_tlv),
|
||
|
+ SOC_SINGLE_TLV("ADC Input Volume", AC_ADC_MIC_CTL,
|
||
|
+ ADC_GAIN, 0x07, 0, adc_input_tlv),
|
||
|
+ SOC_SINGLE_TLV("Master Volume", AC_LINEOUT_CTL,
|
||
|
+ LINEOUT_VOL, 0x1f, 0, lineout_tlv),
|
||
|
+ SOC_SINGLE_TLV("MIC1 Boost Volume", AC_ADC_MIC_CTL,
|
||
|
+ MIC1_BOOST, 0x07, 0, mic_boost_tlv),
|
||
|
+ SOC_SINGLE_TLV("MIC2 Boost Volume", AC_ADC_MIC_CTL,
|
||
|
+ MIC2_BOOST, 0x07, 0, mic_boost_tlv),
|
||
|
+};
|
||
|
+
|
||
|
+/* Enable I2S & DAC clk, then enable the DAC digital part */
|
||
|
+static int acx00_playback_event(struct snd_soc_dapm_widget *w,
|
||
|
+ struct snd_kcontrol *k, int event)
|
||
|
+{
|
||
|
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||
|
+
|
||
|
+ switch (event) {
|
||
|
+ case SND_SOC_DAPM_POST_PMU:
|
||
|
+ snd_soc_component_update_bits(component, AC_SYS_CLK_CTL,
|
||
|
+ (0x1<<SYS_CLK_DAC), (0x1<<SYS_CLK_DAC));
|
||
|
+ snd_soc_component_update_bits(component, AC_SYS_MOD_RST,
|
||
|
+ (0x1<<MOD_RST_DAC), (0x1<<MOD_RST_DAC));
|
||
|
+ snd_soc_component_update_bits(component, AC_DAC_CTL,
|
||
|
+ (0x1<<DAC_CTL_DAC_EN), (0x1<<DAC_CTL_DAC_EN));
|
||
|
+ break;
|
||
|
+ case SND_SOC_DAPM_POST_PMD:
|
||
|
+ snd_soc_component_update_bits(component, AC_SYS_CLK_CTL,
|
||
|
+ (0x1<<SYS_CLK_DAC), (0x0<<SYS_CLK_DAC));
|
||
|
+ snd_soc_component_update_bits(component, AC_SYS_MOD_RST,
|
||
|
+ (0x1<<MOD_RST_DAC), (0x0<<MOD_RST_DAC));
|
||
|
+ snd_soc_component_update_bits(component, AC_DAC_CTL,
|
||
|
+ (0x1<<DAC_CTL_DAC_EN), (0x0<<DAC_CTL_DAC_EN));
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/* Enable I2S & ADC clk, then enable the ADC digital part */
|
||
|
+static int acx00_capture_event(struct snd_soc_dapm_widget *w,
|
||
|
+ struct snd_kcontrol *k, int event)
|
||
|
+{
|
||
|
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||
|
+
|
||
|
+ switch (event) {
|
||
|
+ case SND_SOC_DAPM_POST_PMU:
|
||
|
+ snd_soc_component_update_bits(component, AC_SYS_CLK_CTL,
|
||
|
+ (0x1<<SYS_CLK_ADC), (0x1<<SYS_CLK_ADC));
|
||
|
+ snd_soc_component_update_bits(component, AC_SYS_MOD_RST,
|
||
|
+ (0x1<<MOD_RST_ADC), (0x1<<MOD_RST_ADC));
|
||
|
+ snd_soc_component_update_bits(component, AC_ADC_CTL,
|
||
|
+ (0x1<<ADC_EN), (0x1<<ADC_EN));
|
||
|
+ break;
|
||
|
+ case SND_SOC_DAPM_POST_PMD:
|
||
|
+ snd_soc_component_update_bits(component, AC_SYS_CLK_CTL,
|
||
|
+ (0x1<<SYS_CLK_ADC), (0x0<<SYS_CLK_ADC));
|
||
|
+ snd_soc_component_update_bits(component, AC_SYS_MOD_RST,
|
||
|
+ (0x1<<MOD_RST_ADC), (0x0<<MOD_RST_ADC));
|
||
|
+ snd_soc_component_update_bits(component, AC_ADC_CTL,
|
||
|
+ (0x1<<ADC_EN), (0x0<<ADC_EN));
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * we used for three scene:
|
||
|
+ * 1. No external Spker & DAPM LINEOUT used, we just enable the LINEOUT in the
|
||
|
+ * ALSA codec probe(acx00_codec_probe) and resume, and we shutdown the LINEOUT
|
||
|
+ * in device shutdown or suspend.
|
||
|
+ * 2. No external Spker, but DAPM LINEOUT used, we just using the LINEOUT
|
||
|
+ * enable or disable throught the DAPM control.
|
||
|
+ * 3. External Spker & DAPM LINEOUT used, we just using the LINEOUT and
|
||
|
+ * External Spker control GPIO enable or disable through DAPM control.
|
||
|
+ */
|
||
|
+static unsigned int spk_delay = 100;
|
||
|
+module_param(spk_delay, int, 0644);
|
||
|
+MODULE_PARM_DESC(spk_delay, "ACX00-Codec spk mute delay time");
|
||
|
+
|
||
|
+static void acx00_spk_enable(struct work_struct *work)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = container_of(work,
|
||
|
+ struct acx00_priv, spk_work.work);
|
||
|
+ gpio_set_value(priv->spk_gpio, 1);
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_lineout_event(struct snd_soc_dapm_widget *w,
|
||
|
+ struct snd_kcontrol *k, int event)
|
||
|
+{
|
||
|
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||
|
+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component);
|
||
|
+
|
||
|
+ switch (event) {
|
||
|
+ case SND_SOC_DAPM_POST_PMU:
|
||
|
+ if (!priv->enable) {
|
||
|
+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL,
|
||
|
+ (1<<LINEL_SRC_EN), (1<<LINEL_SRC_EN));
|
||
|
+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL,
|
||
|
+ (1<<LINER_SRC_EN), (1<<LINER_SRC_EN));
|
||
|
+ msleep(100);
|
||
|
+ priv->enable = 1;
|
||
|
+ }
|
||
|
+#ifdef ACX00_DAPM_LINEOUT
|
||
|
+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL,
|
||
|
+ (1<<LINEOUT_EN), (1<<LINEOUT_EN));
|
||
|
+ mdelay(50);
|
||
|
+#endif
|
||
|
+ if (priv->spk_gpio_used) {
|
||
|
+ if (spk_delay == 0) {
|
||
|
+ gpio_set_value(priv->spk_gpio, 1);
|
||
|
+ /*
|
||
|
+ * time delay to wait spk pa work fine,
|
||
|
+ * general setting 50ms
|
||
|
+ */
|
||
|
+ mdelay(50);
|
||
|
+ } else
|
||
|
+ schedule_delayed_work(&priv->spk_work,
|
||
|
+ msecs_to_jiffies(spk_delay));
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ case SND_SOC_DAPM_PRE_PMD:
|
||
|
+ mdelay(50);
|
||
|
+ if (priv->spk_gpio_used) {
|
||
|
+ gpio_set_value(priv->spk_gpio, 0);
|
||
|
+ msleep(50);
|
||
|
+ }
|
||
|
+#ifdef ACX00_DAPM_LINEOUT
|
||
|
+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL,
|
||
|
+ (1<<LINEOUT_EN), (0<<LINEOUT_EN));
|
||
|
+#endif
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/* AC_I2S_MIXER_SRC : 0x2114 */
|
||
|
+static const struct snd_kcontrol_new i2sl_mixer_src[] = {
|
||
|
+ SOC_DAPM_SINGLE("I2SDACL Switch", AC_I2S_MIXER_SRC,
|
||
|
+ I2S_MIXERL_SRC_DAC, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("ADCL Switch", AC_I2S_MIXER_SRC,
|
||
|
+ I2S_MIXERL_SRC_ADC, 1, 0),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct snd_kcontrol_new i2sr_mixer_src[] = {
|
||
|
+ SOC_DAPM_SINGLE("I2SDACR Switch", AC_I2S_MIXER_SRC,
|
||
|
+ I2S_MIXERR_SRC_DAC, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("ADCR Switch", AC_I2S_MIXER_SRC,
|
||
|
+ I2S_MIXERR_SRC_ADC, 1, 0),
|
||
|
+};
|
||
|
+
|
||
|
+/* AC_DAC_MIXER_SRC : 0x2202 */
|
||
|
+static const struct snd_kcontrol_new dacl_mixer_src[] = {
|
||
|
+ SOC_DAPM_SINGLE("I2SDACL Switch", AC_DAC_MIXER_SRC,
|
||
|
+ DAC_MIXERL_SRC_DAC, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("ADCL Switch", AC_DAC_MIXER_SRC,
|
||
|
+ DAC_MIXERL_SRC_ADC, 1, 0),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct snd_kcontrol_new dacr_mixer_src[] = {
|
||
|
+ SOC_DAPM_SINGLE("I2SDACR Switch", AC_DAC_MIXER_SRC,
|
||
|
+ DAC_MIXERR_SRC_DAC, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("ADCR Switch", AC_DAC_MIXER_SRC,
|
||
|
+ DAC_MIXERR_SRC_ADC, 1, 0),
|
||
|
+};
|
||
|
+
|
||
|
+/* AC_OUT_MIXER_SRC : 0x2222 */
|
||
|
+static const struct snd_kcontrol_new left_output_mixer[] = {
|
||
|
+ SOC_DAPM_SINGLE("MIC1 Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERL_SRC_MIC1, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("MIC2 Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERL_SRC_MIC2, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("PhonePN Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERL_SRC_PHPN, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("PhoneN Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERL_SRC_PHN, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("LINEINL Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERL_SRC_LINEL, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("DACL Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERL_SRC_DACL, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("DACR Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERL_SRC_DACR, 1, 0),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct snd_kcontrol_new right_output_mixer[] = {
|
||
|
+ SOC_DAPM_SINGLE("MIC1 Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERR_SRC_MIC1, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("MIC2 Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERR_SRC_MIC2, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("PhonePN Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERR_SRC_PHPN, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("PhoneP Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERR_SRC_PHP, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("LINEINR Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERR_SRC_LINER, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("DACR Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERR_SRC_DACR, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("DACL Switch", AC_OUT_MIXER_SRC,
|
||
|
+ OUT_MIXERR_SRC_DACL, 1, 0),
|
||
|
+};
|
||
|
+
|
||
|
+/* AC_LINEOUT_CTL : 0x2224 */
|
||
|
+const char * const left_lineout_text[] = {
|
||
|
+ "Left OMixer", "LR OMixer",
|
||
|
+};
|
||
|
+
|
||
|
+static const struct soc_enum left_lineout_enum =
|
||
|
+ SOC_ENUM_SINGLE(AC_LINEOUT_CTL, LINEL_SRC,
|
||
|
+ ARRAY_SIZE(left_lineout_text), left_lineout_text);
|
||
|
+
|
||
|
+static const struct snd_kcontrol_new left_lineout_mux =
|
||
|
+ SOC_DAPM_ENUM("Left LINEOUT Mux", left_lineout_enum);
|
||
|
+
|
||
|
+const char * const right_lineout_text[] = {
|
||
|
+ "Right OMixer", "LR OMixer",
|
||
|
+};
|
||
|
+
|
||
|
+static const struct soc_enum right_lineout_enum =
|
||
|
+ SOC_ENUM_SINGLE(AC_LINEOUT_CTL, LINER_SRC,
|
||
|
+ ARRAY_SIZE(right_lineout_text), right_lineout_text);
|
||
|
+
|
||
|
+static const struct snd_kcontrol_new right_lineout_mux =
|
||
|
+ SOC_DAPM_ENUM("Right LINEOUT Mux", right_lineout_enum);
|
||
|
+
|
||
|
+/* AC_ADC_MIXER_SRC : 0x2322 */
|
||
|
+static const struct snd_kcontrol_new left_input_mixer[] = {
|
||
|
+ SOC_DAPM_SINGLE("MIC1 Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERL_MIC1, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("MIC2 Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERL_MIC2, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("PhonePN Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERL_PHPN, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("PhoneN Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERL_PHN, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("LINEINL Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERL_LINEL, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("OMixerL Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERL_MIXL, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("OMixerR Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERL_MIXR, 1, 0),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct snd_kcontrol_new right_input_mixer[] = {
|
||
|
+ SOC_DAPM_SINGLE("MIC1 Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERR_MIC1, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("MIC2 Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERR_MIC2, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("PhonePN Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERR_PHPN, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("PhoneP Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERR_PHP, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("LINEINR Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERR_LINER, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("OMixerR Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERR_MIXR, 1, 0),
|
||
|
+ SOC_DAPM_SINGLE("OMixerL Switch", AC_ADC_MIXER_SRC,
|
||
|
+ ADC_MIXERR_MIXL, 1, 0),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct snd_soc_dapm_widget acx00_codec_dapm_widgets[] = {
|
||
|
+ SND_SOC_DAPM_AIF_IN_E("DACL", "Playback", 0, AC_DAC_CTL,
|
||
|
+ OUT_MIXER_DACL_EN, 0,
|
||
|
+ acx00_playback_event,
|
||
|
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
|
||
|
+ SND_SOC_DAPM_AIF_IN_E("DACR", "Playback", 0,
|
||
|
+ AC_DAC_CTL, OUT_MIXER_DACR_EN, 0,
|
||
|
+ acx00_playback_event,
|
||
|
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
|
||
|
+
|
||
|
+ SND_SOC_DAPM_AIF_OUT_E("ADCL", "Capture", 0,
|
||
|
+ AC_ADC_MIC_CTL, ADCL_EN, 0,
|
||
|
+ acx00_capture_event,
|
||
|
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
|
||
|
+ SND_SOC_DAPM_AIF_OUT_E("ADCR", "Capture", 0,
|
||
|
+ AC_ADC_MIC_CTL, ADCR_EN, 0,
|
||
|
+ acx00_capture_event,
|
||
|
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
|
||
|
+
|
||
|
+ SND_SOC_DAPM_MIXER("Left Output Mixer", AC_OUT_MIXER_CTL,
|
||
|
+ OUT_MIXER_LMIX_EN, 0,
|
||
|
+ left_output_mixer, ARRAY_SIZE(left_output_mixer)),
|
||
|
+
|
||
|
+ SND_SOC_DAPM_MIXER("Right Output Mixer", AC_OUT_MIXER_CTL,
|
||
|
+ OUT_MIXER_RMIX_EN, 0, right_output_mixer,
|
||
|
+ ARRAY_SIZE(right_output_mixer)),
|
||
|
+
|
||
|
+ SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0,
|
||
|
+ left_input_mixer, ARRAY_SIZE(left_input_mixer)),
|
||
|
+ SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0,
|
||
|
+ right_input_mixer, ARRAY_SIZE(right_input_mixer)),
|
||
|
+
|
||
|
+ SND_SOC_DAPM_MIXER("Left DAC Mixer", AC_OUT_MIXER_CTL,
|
||
|
+ OUT_MIXER_DACL_EN, 0, dacl_mixer_src,
|
||
|
+ ARRAY_SIZE(dacl_mixer_src)),
|
||
|
+ SND_SOC_DAPM_MIXER("Right DAC Mixer", AC_OUT_MIXER_CTL,
|
||
|
+ OUT_MIXER_DACR_EN, 0, dacr_mixer_src,
|
||
|
+ ARRAY_SIZE(dacr_mixer_src)),
|
||
|
+
|
||
|
+ SND_SOC_DAPM_MIXER("Left I2S Mixer", SND_SOC_NOPM,
|
||
|
+ 0, 0, i2sl_mixer_src, ARRAY_SIZE(i2sl_mixer_src)),
|
||
|
+ SND_SOC_DAPM_MIXER("Right I2S Mixer", SND_SOC_NOPM,
|
||
|
+ 0, 0, i2sr_mixer_src, ARRAY_SIZE(i2sr_mixer_src)),
|
||
|
+
|
||
|
+ SND_SOC_DAPM_MUX("Left LINEOUT Mux", SND_SOC_NOPM,
|
||
|
+ 0, 0, &left_lineout_mux),
|
||
|
+ SND_SOC_DAPM_MUX("Right LINEOUT Mux", SND_SOC_NOPM,
|
||
|
+ 0, 0, &right_lineout_mux),
|
||
|
+
|
||
|
+ SND_SOC_DAPM_PGA("MIC1 PGA", AC_ADC_MIC_CTL,
|
||
|
+ MIC1_GAIN_EN, 0, NULL, 0),
|
||
|
+ SND_SOC_DAPM_PGA("MIC2 PGA", AC_ADC_MIC_CTL,
|
||
|
+ MIC2_GAIN_EN, 0, NULL, 0),
|
||
|
+
|
||
|
+ SND_SOC_DAPM_MICBIAS("MIC Bias", AC_MICBIAS_CTL,
|
||
|
+ MMBIAS_EN, 0),
|
||
|
+
|
||
|
+ /* PHONEIN & PHONEOUT not enable in pin assign */
|
||
|
+ SND_SOC_DAPM_INPUT("PHONEINP"),
|
||
|
+ SND_SOC_DAPM_INPUT("PHONEINN"),
|
||
|
+ SND_SOC_DAPM_INPUT("PHONEINPN"),
|
||
|
+
|
||
|
+ /* endpoint define */
|
||
|
+ SND_SOC_DAPM_LINE("LINEIN", NULL),
|
||
|
+ SND_SOC_DAPM_LINE("LINEOUT", acx00_lineout_event),
|
||
|
+ SND_SOC_DAPM_MIC("MIC1", NULL),
|
||
|
+ SND_SOC_DAPM_MIC("MIC2", NULL),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct snd_soc_dapm_route acx00_codec_dapm_routes[] = {
|
||
|
+ {"Left Output Mixer", "MIC1 Switch", "MIC1 PGA"},
|
||
|
+ {"Left Output Mixer", "MIC2 Switch", "MIC2 PGA"},
|
||
|
+ {"Left Output Mixer", "PhonePN Switch", "PHONEINPN"},
|
||
|
+ {"Left Output Mixer", "PhoneN Switch", "PHONEINN"},
|
||
|
+ {"Left Output Mixer", "LINEINL Switch", "LINEIN"},
|
||
|
+ {"Left Output Mixer", "DACR Switch", "Right DAC Mixer"},
|
||
|
+ {"Left Output Mixer", "DACL Switch", "Left DAC Mixer"},
|
||
|
+
|
||
|
+ {"Right Output Mixer", "MIC1 Switch", "MIC1 PGA"},
|
||
|
+ {"Right Output Mixer", "MIC2 Switch", "MIC2 PGA"},
|
||
|
+ {"Right Output Mixer", "PhonePN Switch", "PHONEINPN"},
|
||
|
+ {"Right Output Mixer", "PhoneP Switch", "PHONEINP"},
|
||
|
+ {"Right Output Mixer", "LINEINR Switch", "LINEIN"},
|
||
|
+ {"Right Output Mixer", "DACR Switch", "Right DAC Mixer"},
|
||
|
+ {"Right Output Mixer", "DACL Switch", "Left DAC Mixer"},
|
||
|
+
|
||
|
+ {"Left LINEOUT Mux", NULL, "Left Output Mixer"},
|
||
|
+ {"Left LINEOUT Mux", "LR OMixer", "Right Output Mixer"},
|
||
|
+ {"Right LINEOUT Mux", NULL, "Right Output Mixer"},
|
||
|
+ {"Right LINEOUT Mux", "LR OMixer", "Left Output Mixer"},
|
||
|
+
|
||
|
+ {"Left Input Mixer", "MIC1 Switch", "MIC1 PGA"},
|
||
|
+ {"Left Input Mixer", "MIC2 Switch", "MIC2 PGA"},
|
||
|
+ {"Left Input Mixer", "PhonePN Switch", "PHONEINPN"},
|
||
|
+ {"Left Input Mixer", "PhoneN Switch", "PHONEINN"},
|
||
|
+ {"Left Input Mixer", "LINEINL Switch", "LINEIN"},
|
||
|
+ {"Left Input Mixer", "OMixerL Switch", "Left Output Mixer"},
|
||
|
+ {"Left Input Mixer", "OMixerR Switch", "Right Output Mixer"},
|
||
|
+
|
||
|
+ {"Right Input Mixer", "MIC1 Switch", "MIC1 PGA"},
|
||
|
+ {"Right Input Mixer", "MIC2 Switch", "MIC2 PGA"},
|
||
|
+ {"Right Input Mixer", "PhonePN Switch", "PHONEINPN"},
|
||
|
+ {"Right Input Mixer", "PhoneP Switch", "PHONEINP"},
|
||
|
+ {"Right Input Mixer", "LINEINR Switch", "LINEIN"},
|
||
|
+ {"Right Input Mixer", "OMixerR Switch", "Right Output Mixer"},
|
||
|
+ {"Right Input Mixer", "OMixerL Switch", "Left Output Mixer"},
|
||
|
+
|
||
|
+ {"Left I2S Mixer", "I2SDACL Switch", "DACL"},
|
||
|
+ {"Left I2S Mixer", "ADCL Switch", "Left Input Mixer"},
|
||
|
+
|
||
|
+ {"Right I2S Mixer", "I2SDACR Switch", "DACR"},
|
||
|
+ {"Right I2S Mixer", "ADCR Switch", "Right Input Mixer"},
|
||
|
+
|
||
|
+ {"Left DAC Mixer", "I2SDACL Switch", "DACL"},
|
||
|
+ {"Left DAC Mixer", "ADCL Switch", "Left Input Mixer"},
|
||
|
+
|
||
|
+ {"Right DAC Mixer", "I2SDACR Switch", "DACR"},
|
||
|
+ {"Right DAC Mixer", "ADCR Switch", "Right Input Mixer"},
|
||
|
+
|
||
|
+ {"ADCL", NULL, "Left I2S Mixer"},
|
||
|
+ {"ADCR", NULL, "Right I2S Mixer"},
|
||
|
+
|
||
|
+ {"LINEOUT", NULL, "Left LINEOUT Mux"},
|
||
|
+ {"LINEOUT", NULL, "Right LINEOUT Mux"},
|
||
|
+
|
||
|
+ {"MIC Bias", NULL, "MIC1"},
|
||
|
+ {"MIC Bias", NULL, "MIC2"},
|
||
|
+ {"MIC1 PGA", NULL, "MIC Bias"},
|
||
|
+ {"MIC2 PGA", NULL, "MIC Bias"},
|
||
|
+};
|
||
|
+
|
||
|
+static void acx00_codec_txctrl_enable(struct snd_soc_component *component,
|
||
|
+ int enable)
|
||
|
+{
|
||
|
+ pr_debug("Enter %s, enable %d\n", __func__, enable);
|
||
|
+ if (enable) {
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CTL,
|
||
|
+ (1<<I2S_RX_EN), (1<<I2S_RX_EN));
|
||
|
+ } else {
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CTL,
|
||
|
+ (1<<I2S_RX_EN), (0<<I2S_RX_EN));
|
||
|
+ }
|
||
|
+ pr_debug("End %s, enable %d\n", __func__, enable);
|
||
|
+}
|
||
|
+
|
||
|
+static void acx00_codec_rxctrl_enable(struct snd_soc_component *component,
|
||
|
+ int enable)
|
||
|
+{
|
||
|
+ pr_debug("Enter %s, enable %d\n", __func__, enable);
|
||
|
+ if (enable) {
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CTL,
|
||
|
+ (1<<I2S_TX_EN), (1<<I2S_TX_EN));
|
||
|
+ } else {
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CTL,
|
||
|
+ (1<<I2S_TX_EN), (0<<I2S_TX_EN));
|
||
|
+ }
|
||
|
+ pr_debug("End %s, enable %d\n", __func__, enable);
|
||
|
+}
|
||
|
+
|
||
|
+int acx00_reg_read(struct ac200_dev *acx00, unsigned short reg)
|
||
|
+{
|
||
|
+ unsigned int val;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ ret = regmap_read(acx00->regmap, reg, &val);
|
||
|
+
|
||
|
+ if (ret < 0)
|
||
|
+ return ret;
|
||
|
+ else
|
||
|
+ return val;
|
||
|
+}
|
||
|
+
|
||
|
+int acx00_reg_write(struct ac200_dev *acx00, unsigned short reg, unsigned short val)
|
||
|
+{
|
||
|
+ return regmap_write(acx00->regmap, reg, val);
|
||
|
+}
|
||
|
+
|
||
|
+static void acx00_codec_init(struct snd_soc_component *component)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component);
|
||
|
+
|
||
|
+ acx00_reg_write(priv->acx00, 0x50, 0x82b1);
|
||
|
+ acx00_reg_write(priv->acx00, 0xc, 0xce01);
|
||
|
+
|
||
|
+ /* acx00_codec sysctl init */
|
||
|
+ acx00_reg_write(priv->acx00, 0x0010, 0x03);
|
||
|
+ acx00_reg_write(priv->acx00, 0x0012, 0x01);
|
||
|
+
|
||
|
+ /* The bit3 need to setup to 1 for bias current. */
|
||
|
+ snd_soc_component_update_bits(component, AC_MICBIAS_CTL,
|
||
|
+ (0x1 << ADDA_BIAS_CUR), (0x1 << ADDA_BIAS_CUR));
|
||
|
+
|
||
|
+ /* enable the output & global enable bit */
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CTL,
|
||
|
+ (1<<I2S_SDO0_EN), (1<<I2S_SDO0_EN));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CTL, (1<<I2S_GEN), (1<<I2S_GEN));
|
||
|
+
|
||
|
+ /* Default setting slot width as 32 bit for I2S */
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (7<<I2S_FMT_SLOT_WIDTH), (7<<I2S_FMT_SLOT_WIDTH));
|
||
|
+
|
||
|
+ /* default setting 0xA0A0 for ADC & DAC Volume */
|
||
|
+ snd_soc_component_write(component, AC_I2S_DAC_VOL, ACX00_DEF_VOL);
|
||
|
+ snd_soc_component_write(component, AC_I2S_ADC_VOL, ACX00_DEF_VOL);
|
||
|
+
|
||
|
+ /* Enable HPF for high pass filter */
|
||
|
+ snd_soc_component_update_bits(component, AC_DAC_CTL,
|
||
|
+ (1<<DAC_CTL_HPF_EN), (1<<DAC_CTL_HPF_EN));
|
||
|
+
|
||
|
+ /* LINEOUT ANTI POP & Click noise */
|
||
|
+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL,
|
||
|
+ (0x7<<LINE_ANTI_TIME), (0x3<<LINE_ANTI_TIME));
|
||
|
+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL,
|
||
|
+ (0x3<<LINE_SLOPE_SEL), (0x3<<LINE_SLOPE_SEL));
|
||
|
+
|
||
|
+ /* enable & setting adc convert delay time */
|
||
|
+ snd_soc_component_update_bits(component, AC_ADC_CTL, (0x3<<ADC_DELAY_TIME),
|
||
|
+ (0x3<<ADC_DELAY_TIME));
|
||
|
+ snd_soc_component_update_bits(component, AC_ADC_CTL, (1<<ADC_DELAY_EN),
|
||
|
+ (1<<ADC_DELAY_EN));
|
||
|
+
|
||
|
+ if (priv->spk_gpio_used) {
|
||
|
+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL,
|
||
|
+ (1<<LINEL_SRC_EN), (1<<LINEL_SRC_EN));
|
||
|
+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL,
|
||
|
+ (1<<LINER_SRC_EN), (1<<LINER_SRC_EN));
|
||
|
+ priv->enable = 1;
|
||
|
+ }
|
||
|
+#ifndef ACX00_DAPM_LINEOUT
|
||
|
+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL, (1<<LINEOUT_EN),
|
||
|
+ (1<<LINEOUT_EN));
|
||
|
+#endif
|
||
|
+
|
||
|
+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL, 0x1f, 0x0f);
|
||
|
+ snd_soc_component_write(component, AC_DAC_MIXER_SRC, 0x2200);
|
||
|
+ snd_soc_component_write(component, AC_OUT_MIXER_SRC, 0x0202);
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_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;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ switch (params_format(params)) {
|
||
|
+ case SNDRV_PCM_FORMAT_S16_LE:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (7<<I2S_FMT_SAMPLE), (3<<I2S_FMT_SAMPLE));
|
||
|
+ break;
|
||
|
+ case SNDRV_PCM_FORMAT_S24_LE:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (7<<I2S_FMT_SAMPLE), (5<<I2S_FMT_SAMPLE));
|
||
|
+ break;
|
||
|
+ case SNDRV_PCM_FORMAT_S32_LE:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (7<<I2S_FMT_SAMPLE), (7<<I2S_FMT_SAMPLE));
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ dev_err(component->dev, "unrecognized format support\n");
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ for (i = 0; i < ARRAY_SIZE(sample_rate_conv); i++) {
|
||
|
+ if (sample_rate_conv[i].samplerate == params_rate(params)) {
|
||
|
+ snd_soc_component_update_bits(component, AC_SYS_SR_CTL,
|
||
|
+ (SYS_SR_MASK<<SYS_SR_BIT),
|
||
|
+ (sample_rate_conv[i].rate_bit<<SYS_SR_BIT));
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_dai_set_sysclk(struct snd_soc_dai *codec_dai,
|
||
|
+ int clk_id, unsigned int freq, int dir)
|
||
|
+{
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_dai_set_fmt(struct snd_soc_dai *codec_dai,
|
||
|
+ unsigned int fmt)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = snd_soc_dai_get_drvdata(codec_dai);
|
||
|
+ struct snd_soc_component *component = priv->component;
|
||
|
+
|
||
|
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||
|
+ /* codec clk & FRM master */
|
||
|
+ case SND_SOC_DAIFMT_CBM_CFM:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CLK,
|
||
|
+ (0x1<<I2S_BCLK_OUT), (0x1<<I2S_BCLK_OUT));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CLK,
|
||
|
+ (0x1<<I2S_LRCK_OUT), (0x1<<I2S_LRCK_OUT));
|
||
|
+ break;
|
||
|
+ /* codec clk & FRM slave */
|
||
|
+ case SND_SOC_DAIFMT_CBS_CFS:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CLK,
|
||
|
+ (0x1<<I2S_BCLK_OUT), 0x0<<I2S_BCLK_OUT);
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CLK,
|
||
|
+ (0x1<<I2S_LRCK_OUT), 0x0<<I2S_LRCK_OUT);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||
|
+ case SND_SOC_DAIFMT_I2S:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CLK,
|
||
|
+ (0x3FF<<I2S_LRCK_PERIOD),
|
||
|
+ (0x1F<<I2S_LRCK_PERIOD));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x3<<I2S_FMT_MODE), (0x1<<I2S_FMT_MODE));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_TX_OFFSET),
|
||
|
+ (0x1<<I2S_FMT_TX_OFFSET));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_RX_OFFSET),
|
||
|
+ (0x1<<I2S_FMT_RX_OFFSET));
|
||
|
+ break;
|
||
|
+ case SND_SOC_DAIFMT_RIGHT_J:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CLK,
|
||
|
+ (0x3FF<<I2S_LRCK_PERIOD),
|
||
|
+ (0x1F<<I2S_LRCK_PERIOD));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x3<<I2S_FMT_MODE), (0x2<<I2S_FMT_MODE));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_TX_OFFSET),
|
||
|
+ (0x0<<I2S_FMT_TX_OFFSET));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_TX_OFFSET),
|
||
|
+ (0x0<<I2S_FMT_RX_OFFSET));
|
||
|
+ break;
|
||
|
+ case SND_SOC_DAIFMT_LEFT_J:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CLK,
|
||
|
+ (0x3FF<<I2S_LRCK_PERIOD),
|
||
|
+ (0x1F<<I2S_LRCK_PERIOD));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x3<<I2S_FMT_MODE), (0x1<<I2S_FMT_MODE));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_TX_OFFSET),
|
||
|
+ (0x0<<I2S_FMT_TX_OFFSET));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_RX_OFFSET),
|
||
|
+ (0x0<<I2S_FMT_RX_OFFSET));
|
||
|
+ break;
|
||
|
+ case SND_SOC_DAIFMT_DSP_A:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CLK,
|
||
|
+ (0x3FF<<I2S_LRCK_PERIOD),
|
||
|
+ (0x3F<<I2S_LRCK_PERIOD));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x3<<I2S_FMT_MODE), (0x0<<I2S_FMT_MODE));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_TX_OFFSET),
|
||
|
+ (0x1<<I2S_FMT_TX_OFFSET));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_RX_OFFSET),
|
||
|
+ (0x1<<I2S_FMT_RX_OFFSET));
|
||
|
+ break;
|
||
|
+ case SND_SOC_DAIFMT_DSP_B:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CLK,
|
||
|
+ (0x3FF<<I2S_LRCK_PERIOD),
|
||
|
+ (0x3F<<I2S_LRCK_PERIOD));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x3<<I2S_FMT_MODE), (0x0<<I2S_FMT_MODE));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_TX_OFFSET),
|
||
|
+ (0x0<<I2S_FMT_TX_OFFSET));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_RX_OFFSET),
|
||
|
+ (0x0<<I2S_FMT_RX_OFFSET));
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ dev_err(component->dev, "format setting failed\n");
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||
|
+ case SND_SOC_DAIFMT_NB_NF:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT1,
|
||
|
+ (0x1<<I2S_FMT_BCLK_POLAR),
|
||
|
+ (0x0<<I2S_FMT_BCLK_POLAR));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT1,
|
||
|
+ (0x1<<I2S_FMT_LRCK_POLAR),
|
||
|
+ (0x0<<I2S_FMT_LRCK_POLAR));
|
||
|
+ break;
|
||
|
+ case SND_SOC_DAIFMT_NB_IF:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT1,
|
||
|
+ (0x1<<I2S_FMT_BCLK_POLAR),
|
||
|
+ (0x0<<I2S_FMT_BCLK_POLAR));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT1,
|
||
|
+ (0x1<<I2S_FMT_LRCK_POLAR),
|
||
|
+ (0x1<<I2S_FMT_LRCK_POLAR));
|
||
|
+ break;
|
||
|
+ case SND_SOC_DAIFMT_IB_NF:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT1,
|
||
|
+ (0x1<<I2S_FMT_BCLK_POLAR),
|
||
|
+ (0x1<<I2S_FMT_BCLK_POLAR));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT1,
|
||
|
+ (0x1<<I2S_FMT_LRCK_POLAR),
|
||
|
+ (0x0<<I2S_FMT_LRCK_POLAR));
|
||
|
+ break;
|
||
|
+ case SND_SOC_DAIFMT_IB_IF:
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT1,
|
||
|
+ (0x1<<I2S_FMT_BCLK_POLAR),
|
||
|
+ (0x1<<I2S_FMT_BCLK_POLAR));
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT1,
|
||
|
+ (0x1<<I2S_FMT_LRCK_POLAR),
|
||
|
+ (0x1<<I2S_FMT_LRCK_POLAR));
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ dev_err(component->dev, "invert clk setting failed\n");
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_dai_set_clkdiv(struct snd_soc_dai *codec_dai,
|
||
|
+ int clk_id, int clk_div)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = snd_soc_dai_get_drvdata(codec_dai);
|
||
|
+ struct snd_soc_component *component = priv->component;
|
||
|
+ unsigned int bclk_div;
|
||
|
+ /*
|
||
|
+ * when PCM mode, setting as 64fs, when I2S mode as 32fs,
|
||
|
+ * then two channel, then just as 64fs
|
||
|
+ */
|
||
|
+ unsigned int div_ratio = clk_div / 64;
|
||
|
+
|
||
|
+ switch (div_ratio) {
|
||
|
+ case 1:
|
||
|
+ bclk_div = I2S_BCLK_DIV_1;
|
||
|
+ break;
|
||
|
+ case 2:
|
||
|
+ bclk_div = I2S_BCLK_DIV_2;
|
||
|
+ break;
|
||
|
+ case 4:
|
||
|
+ bclk_div = I2S_BCLK_DIV_3;
|
||
|
+ break;
|
||
|
+ case 6:
|
||
|
+ bclk_div = I2S_BCLK_DIV_4;
|
||
|
+ break;
|
||
|
+ case 8:
|
||
|
+ bclk_div = I2S_BCLK_DIV_5;
|
||
|
+ break;
|
||
|
+ case 12:
|
||
|
+ bclk_div = I2S_BCLK_DIV_6;
|
||
|
+ break;
|
||
|
+ case 16:
|
||
|
+ bclk_div = I2S_BCLK_DIV_7;
|
||
|
+ break;
|
||
|
+ case 24:
|
||
|
+ bclk_div = I2S_BCLK_DIV_8;
|
||
|
+ break;
|
||
|
+ case 32:
|
||
|
+ bclk_div = I2S_BCLK_DIV_9;
|
||
|
+ break;
|
||
|
+ case 48:
|
||
|
+ bclk_div = I2S_BCLK_DIV_10;
|
||
|
+ break;
|
||
|
+ case 64:
|
||
|
+ bclk_div = I2S_BCLK_DIV_11;
|
||
|
+ break;
|
||
|
+ case 96:
|
||
|
+ bclk_div = I2S_BCLK_DIV_12;
|
||
|
+ break;
|
||
|
+ case 128:
|
||
|
+ bclk_div = I2S_BCLK_DIV_13;
|
||
|
+ break;
|
||
|
+ case 176:
|
||
|
+ bclk_div = I2S_BCLK_DIV_14;
|
||
|
+ break;
|
||
|
+ case 192:
|
||
|
+ bclk_div = I2S_BCLK_DIV_15;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ dev_err(component->dev, "setting blck div failed\n");
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_CLK,
|
||
|
+ (I2S_BCLK_DIV_MASK<<I2S_BLCK_DIV),
|
||
|
+ (bclk_div<<I2S_BLCK_DIV));
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_startup(struct snd_pcm_substream *substream,
|
||
|
+ struct snd_soc_dai *codec_dai)
|
||
|
+{
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static bool acx00_loop_en;
|
||
|
+module_param(acx00_loop_en, bool, 0644);
|
||
|
+MODULE_PARM_DESC(acx00_loop_en, "ACX00-Codec audio loopback debug(Y=enable, N=disable)");
|
||
|
+
|
||
|
+static int acx00_codec_trigger(struct snd_pcm_substream *substream,
|
||
|
+ int cmd, struct snd_soc_dai *codec_dai)
|
||
|
+{
|
||
|
+ struct snd_soc_component *component = codec_dai->component;
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_prepare(struct snd_pcm_substream *substream,
|
||
|
+ struct snd_soc_dai *codec_dai)
|
||
|
+{
|
||
|
+ struct snd_soc_component *component = codec_dai->component;
|
||
|
+
|
||
|
+ snd_soc_component_update_bits(component, AC_SYS_CLK_CTL,
|
||
|
+ (0x1<<SYS_CLK_I2S), (0x1<<SYS_CLK_I2S));
|
||
|
+ snd_soc_component_update_bits(component, AC_SYS_MOD_RST,
|
||
|
+ (0x1<<MOD_RST_I2S), (0x1<<MOD_RST_I2S));
|
||
|
+
|
||
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||
|
+ if (acx00_loop_en)
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_LOOP),
|
||
|
+ (0x1<<I2S_FMT_LOOP));
|
||
|
+ else
|
||
|
+ snd_soc_component_update_bits(component, AC_I2S_FMT0,
|
||
|
+ (0x1<<I2S_FMT_LOOP),
|
||
|
+ (0x0<<I2S_FMT_LOOP));
|
||
|
+ acx00_codec_txctrl_enable(component, 1);
|
||
|
+ } else
|
||
|
+ acx00_codec_rxctrl_enable(component, 1);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_digital_mute(struct snd_soc_dai *codec_dai,
|
||
|
+ int mute)
|
||
|
+{
|
||
|
+ struct snd_soc_component *component = codec_dai->component;
|
||
|
+
|
||
|
+ if (mute)
|
||
|
+ snd_soc_component_write(component, AC_I2S_DAC_VOL, 0);
|
||
|
+ else
|
||
|
+ snd_soc_component_write(component, AC_I2S_DAC_VOL, ACX00_DEF_VOL);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void acx00_codec_shutdown(struct snd_pcm_substream *substream,
|
||
|
+ struct snd_soc_dai *dai)
|
||
|
+{
|
||
|
+ struct snd_soc_component *component = dai->component;
|
||
|
+
|
||
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||
|
+ acx00_codec_txctrl_enable(component, 0);
|
||
|
+ else
|
||
|
+ acx00_codec_rxctrl_enable(component, 0);
|
||
|
+}
|
||
|
+
|
||
|
+static const struct snd_soc_dai_ops acx00_codec_dai_ops = {
|
||
|
+ .hw_params = acx00_codec_hw_params,
|
||
|
+ .shutdown = acx00_codec_shutdown,
|
||
|
+// .digital_mute = acx00_codec_digital_mute,
|
||
|
+ .set_sysclk = acx00_codec_dai_set_sysclk,
|
||
|
+ .set_fmt = acx00_codec_dai_set_fmt,
|
||
|
+ .set_clkdiv = acx00_codec_dai_set_clkdiv,
|
||
|
+ .startup = acx00_codec_startup,
|
||
|
+ .trigger = acx00_codec_trigger,
|
||
|
+ .prepare = acx00_codec_prepare,
|
||
|
+};
|
||
|
+
|
||
|
+static struct snd_soc_dai_driver acx00_codec_dai[] = {
|
||
|
+ {
|
||
|
+ .name = "acx00-dai",
|
||
|
+ .playback = {
|
||
|
+ .stream_name = "Playback",
|
||
|
+ .channels_min = 1,
|
||
|
+ .channels_max = 2,
|
||
|
+ .rates = SNDRV_PCM_RATE_8000_192000
|
||
|
+ | SNDRV_PCM_RATE_KNOT,
|
||
|
+ .formats = SNDRV_PCM_FMTBIT_S16_LE
|
||
|
+ | SNDRV_PCM_FMTBIT_S24_LE
|
||
|
+ | SNDRV_PCM_FMTBIT_S32_LE,
|
||
|
+ },
|
||
|
+
|
||
|
+ .capture = {
|
||
|
+ .stream_name = "Capture",
|
||
|
+ .channels_min = 1,
|
||
|
+ .channels_max = 2,
|
||
|
+ .rates = SNDRV_PCM_RATE_8000_192000
|
||
|
+ | SNDRV_PCM_RATE_KNOT,
|
||
|
+ .formats = SNDRV_PCM_FMTBIT_S16_LE
|
||
|
+ | SNDRV_PCM_FMTBIT_S24_LE
|
||
|
+ | SNDRV_PCM_FMTBIT_S32_LE,
|
||
|
+ },
|
||
|
+
|
||
|
+ .ops = &acx00_codec_dai_ops,
|
||
|
+ },
|
||
|
+};
|
||
|
+
|
||
|
+static void acx00_codec_resume_work(struct work_struct *work)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = container_of(work,
|
||
|
+ struct acx00_priv, resume_work.work);
|
||
|
+
|
||
|
+ acx00_codec_init(priv->component);
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_probe(struct snd_soc_component *component)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component);
|
||
|
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
|
||
|
+ int ret = 0;
|
||
|
+
|
||
|
+ mutex_init(&priv->mutex);
|
||
|
+
|
||
|
+ priv->component = component;
|
||
|
+#if 0
|
||
|
+ /* Add virtual switch */
|
||
|
+ ret = snd_soc_add_component_controls(component, acx00_codec_controls,
|
||
|
+ ARRAY_SIZE(acx00_codec_controls));
|
||
|
+ if (ret) {
|
||
|
+ pr_err("[audio-codec] Failed to register audio mode control, will continue without it.\n");
|
||
|
+ }
|
||
|
+ snd_soc_dapm_new_controls(dapm, acx00_codec_dapm_widgets, ARRAY_SIZE(acx00_codec_dapm_widgets));
|
||
|
+ snd_soc_dapm_add_routes(dapm, acx00_codec_dapm_routes, ARRAY_SIZE(acx00_codec_dapm_routes));
|
||
|
+#endif
|
||
|
+ /* using late_initcall to wait 120ms acx00-core to make chip reset */
|
||
|
+ acx00_codec_init(component);
|
||
|
+ INIT_DELAYED_WORK(&priv->spk_work, acx00_spk_enable);
|
||
|
+ INIT_DELAYED_WORK(&priv->resume_work, acx00_codec_resume_work);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void acx00_codec_remove(struct snd_soc_component *component)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component);
|
||
|
+
|
||
|
+ cancel_delayed_work_sync(&priv->spk_work);
|
||
|
+ cancel_delayed_work_sync(&priv->resume_work);
|
||
|
+}
|
||
|
+
|
||
|
+static unsigned int acx00_codec_read(struct snd_soc_component *component,
|
||
|
+ unsigned int reg)
|
||
|
+{
|
||
|
+ unsigned int data;
|
||
|
+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component);
|
||
|
+
|
||
|
+ /* Device I/O API */
|
||
|
+ data = acx00_reg_read(priv->acx00, reg);
|
||
|
+ return data;
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_write(struct snd_soc_component *component,
|
||
|
+ unsigned int reg, unsigned int value)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component);
|
||
|
+
|
||
|
+ return acx00_reg_write(priv->acx00, reg, value);
|
||
|
+}
|
||
|
+
|
||
|
+static int sunxi_gpio_iodisable(u32 gpio)
|
||
|
+{
|
||
|
+ char pin_name[8];
|
||
|
+ u32 config, ret;
|
||
|
+#if 0
|
||
|
+ sunxi_gpio_to_name(gpio, pin_name);
|
||
|
+ config = 7 << 16;
|
||
|
+ ret = pin_config_set(SUNXI_PINCTRL, pin_name, config);
|
||
|
+#endif
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_suspend(struct snd_soc_component *component)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component);
|
||
|
+
|
||
|
+ pr_debug("Enter %s\n", __func__);
|
||
|
+
|
||
|
+ clk_disable_unprepare(priv->clk);
|
||
|
+
|
||
|
+ /* PA_CTRL first setting low state, then make it iodisabled */
|
||
|
+ if (priv->spk_gpio_used) {
|
||
|
+ sunxi_gpio_iodisable(priv->spk_gpio);
|
||
|
+ msleep(30);
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * when codec suspend, then the register reset, if auto reset produce
|
||
|
+ * Pop & Click noise, then we should cut down the LINEOUT in this town.
|
||
|
+ */
|
||
|
+ if (priv->enable) {
|
||
|
+ snd_soc_component_update_bits(component, AC_LINEOUT_CTL,
|
||
|
+ (1<<LINEOUT_EN), (0<<LINEOUT_EN));
|
||
|
+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL,
|
||
|
+ (1<<LINEL_SRC_EN), (0<<LINEL_SRC_EN));
|
||
|
+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL,
|
||
|
+ (1<<LINER_SRC_EN), (0<<LINER_SRC_EN));
|
||
|
+ priv->enable = 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ pr_debug("Exit %s\n", __func__);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_resume(struct snd_soc_component *component)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = snd_soc_component_get_drvdata(component);
|
||
|
+
|
||
|
+ pr_debug("Enter %s\n", __func__);
|
||
|
+
|
||
|
+ if (clk_prepare_enable(priv->clk)) {
|
||
|
+ dev_err(component->dev, "codec resume clk failed\n");
|
||
|
+ return -EBUSY;
|
||
|
+ }
|
||
|
+
|
||
|
+ schedule_delayed_work(&priv->resume_work, msecs_to_jiffies(300));
|
||
|
+
|
||
|
+ if (priv->spk_gpio_used) {
|
||
|
+ gpio_direction_output(priv->spk_gpio, 1);
|
||
|
+ gpio_set_value(priv->spk_gpio, 0);
|
||
|
+ }
|
||
|
+
|
||
|
+ pr_debug("Exit %s\n", __func__);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+static int acx00_codec_set_bias_level(struct snd_soc_component *component,
|
||
|
+ enum snd_soc_bias_level level)
|
||
|
+{
|
||
|
+ component->dapm.bias_level = level;
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+struct label {
|
||
|
+ const char *name;
|
||
|
+ int value;
|
||
|
+};
|
||
|
+
|
||
|
+#define LABEL(constant) { #constant, constant }
|
||
|
+#define LABEL_END { NULL, -1 }
|
||
|
+
|
||
|
+static struct label reg_labels[] = {
|
||
|
+ LABEL(AC_SYS_CLK_CTL),
|
||
|
+ LABEL(AC_SYS_MOD_RST),
|
||
|
+ LABEL(AC_SYS_SR_CTL),
|
||
|
+ LABEL(AC_I2S_CTL),
|
||
|
+ LABEL(AC_I2S_CLK),
|
||
|
+ LABEL(AC_I2S_FMT0),
|
||
|
+ LABEL(AC_I2S_FMT1),
|
||
|
+ LABEL(AC_I2S_MIXER_SRC),
|
||
|
+ LABEL(AC_I2S_MIXER_GAIN),
|
||
|
+ LABEL(AC_I2S_DAC_VOL),
|
||
|
+ LABEL(AC_I2S_ADC_VOL),
|
||
|
+ LABEL(AC_DAC_CTL),
|
||
|
+ LABEL(AC_DAC_MIXER_SRC),
|
||
|
+ LABEL(AC_DAC_MIXER_GAIN),
|
||
|
+ LABEL(AC_OUT_MIXER_CTL),
|
||
|
+ LABEL(AC_OUT_MIXER_SRC),
|
||
|
+ LABEL(AC_LINEOUT_CTL),
|
||
|
+ LABEL(AC_ADC_CTL),
|
||
|
+ LABEL(AC_MICBIAS_CTL),
|
||
|
+ LABEL(AC_ADC_MIC_CTL),
|
||
|
+ LABEL(AC_ADC_MIXER_SRC),
|
||
|
+ LABEL(AC_BIAS_CTL),
|
||
|
+ LABEL(AC_ANALOG_PROF_CTL),
|
||
|
+ LABEL_END,
|
||
|
+};
|
||
|
+
|
||
|
+static ssize_t show_audio_reg(struct device *dev,
|
||
|
+ struct device_attribute *attr, char *buf)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = dev_get_drvdata(dev);
|
||
|
+ int count = 0, i = 0;
|
||
|
+ unsigned int reg_val;
|
||
|
+
|
||
|
+ count += sprintf(buf, "dump audio reg:\n");
|
||
|
+
|
||
|
+ while (reg_labels[i].name != NULL) {
|
||
|
+ reg_val = acx00_reg_read(priv->acx00, reg_labels[i].value);
|
||
|
+ count += sprintf(buf + count, "%s 0x%x: 0x%x\n",
|
||
|
+ reg_labels[i].name, (reg_labels[i].value), reg_val);
|
||
|
+ i++;
|
||
|
+ }
|
||
|
+
|
||
|
+ return count;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * param 1: 0 read;1 write
|
||
|
+ * param 2: 1 digital reg; 2 analog reg
|
||
|
+ * param 3: reg value;
|
||
|
+ * param 4: write value;
|
||
|
+ * read:
|
||
|
+ * echo 0,1,0x00> audio_reg
|
||
|
+ * echo 0,2,0x00> audio_reg
|
||
|
+ * write:
|
||
|
+ * echo 1,1,0x00,0xa > audio_reg
|
||
|
+ * echo 1,2,0x00,0xff > audio_reg
|
||
|
+*/
|
||
|
+static ssize_t store_audio_reg(struct device *dev,
|
||
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
||
|
+{
|
||
|
+ int ret;
|
||
|
+ int rw_flag;
|
||
|
+ unsigned int input_reg_val = 0;
|
||
|
+ int input_reg_group = 0;
|
||
|
+ unsigned int input_reg_offset = 0;
|
||
|
+ struct acx00_priv *priv = dev_get_drvdata(dev);
|
||
|
+
|
||
|
+ ret = sscanf(buf, "%d,%d,0x%x,0x%x", &rw_flag, &input_reg_group,
|
||
|
+ &input_reg_offset, &input_reg_val);
|
||
|
+ dev_info(dev, "ret:%d, reg_group:%d, reg_offset:%d, reg_val:0x%x\n",
|
||
|
+ ret, input_reg_group, input_reg_offset, input_reg_val);
|
||
|
+
|
||
|
+ if (input_reg_group != 1) {
|
||
|
+ pr_err("not exist reg group\n");
|
||
|
+ ret = count;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ if (!(rw_flag == 1 || rw_flag == 0)) {
|
||
|
+ pr_err("not rw_flag\n");
|
||
|
+ ret = count;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (rw_flag) {
|
||
|
+ acx00_reg_write(priv->acx00, input_reg_offset, input_reg_val);
|
||
|
+ } else {
|
||
|
+ input_reg_val = acx00_reg_read(priv->acx00, input_reg_offset);
|
||
|
+ dev_info(dev, "\n\n Reg[0x%x] : 0x%04x\n\n",
|
||
|
+ input_reg_offset, input_reg_val);
|
||
|
+ }
|
||
|
+ ret = count;
|
||
|
+
|
||
|
+out:
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static DEVICE_ATTR(audio_reg, 0644, show_audio_reg, store_audio_reg);
|
||
|
+
|
||
|
+static struct attribute *audio_debug_attrs[] = {
|
||
|
+ &dev_attr_audio_reg.attr,
|
||
|
+ NULL,
|
||
|
+};
|
||
|
+
|
||
|
+static struct attribute_group audio_debug_attr_group = {
|
||
|
+ .name = "audio_reg_debug",
|
||
|
+ .attrs = audio_debug_attrs,
|
||
|
+};
|
||
|
+
|
||
|
+static struct snd_soc_component_driver soc_codec_driver_acx00 = {
|
||
|
+ .probe = acx00_codec_probe,
|
||
|
+ .remove = acx00_codec_remove,
|
||
|
+ .suspend = acx00_codec_suspend,
|
||
|
+ .resume = acx00_codec_resume,
|
||
|
+ .read = acx00_codec_read,
|
||
|
+ .write = acx00_codec_write,
|
||
|
+// .ignore_pmdown_time = 1,
|
||
|
+ .set_bias_level = acx00_codec_set_bias_level,
|
||
|
+ .controls = acx00_codec_controls,
|
||
|
+ .num_controls = ARRAY_SIZE(acx00_codec_controls),
|
||
|
+ .dapm_widgets = acx00_codec_dapm_widgets,
|
||
|
+ .num_dapm_widgets = ARRAY_SIZE(acx00_codec_dapm_widgets),
|
||
|
+ .dapm_routes = acx00_codec_dapm_routes,
|
||
|
+ .num_dapm_routes = ARRAY_SIZE(acx00_codec_dapm_routes),
|
||
|
+};
|
||
|
+
|
||
|
+/* through acx00 is part of mfd devices, after the mfd */
|
||
|
+static int acx00_codec_dev_probe(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv;
|
||
|
+ int ret;
|
||
|
+ struct device_node *np = of_find_compatible_node(NULL, NULL, "allwinner,ac200_codec");
|
||
|
+
|
||
|
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct acx00_priv), GFP_KERNEL);
|
||
|
+ if (!priv) {
|
||
|
+ dev_err(&pdev->dev, "acx00 codec priv mem alloc failed\n");
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ platform_set_drvdata(pdev, priv);
|
||
|
+ priv->acx00 = dev_get_drvdata(pdev->dev.parent);
|
||
|
+
|
||
|
+ if (np) {
|
||
|
+ ret = of_get_named_gpio(np, "gpio-spk", 0);
|
||
|
+ if (ret >= 0) {
|
||
|
+ priv->spk_gpio_used = 1;
|
||
|
+ priv->spk_gpio = ret;
|
||
|
+ if (!gpio_is_valid(priv->spk_gpio)) {
|
||
|
+ dev_err(&pdev->dev, "gpio-spk is valid\n");
|
||
|
+ ret = -EINVAL;
|
||
|
+ goto err_devm_kfree;
|
||
|
+ } else {
|
||
|
+ ret = devm_gpio_request(&pdev->dev,
|
||
|
+ priv->spk_gpio, "SPK");
|
||
|
+ if (ret) {
|
||
|
+ dev_err(&pdev->dev,
|
||
|
+ "failed request gpio-spk\n");
|
||
|
+ ret = -EBUSY;
|
||
|
+ goto err_devm_kfree;
|
||
|
+ } else {
|
||
|
+ gpio_direction_output(priv->spk_gpio,
|
||
|
+ 1);
|
||
|
+ gpio_set_value(priv->spk_gpio, 0);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ priv->spk_gpio_used = 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = of_get_named_gpio(np, "gpio-switch", 0);
|
||
|
+ if (ret >= 0) {
|
||
|
+ priv->switch_gpio = ret;
|
||
|
+ if (!gpio_is_valid(priv->switch_gpio)) {
|
||
|
+ dev_err(&pdev->dev, "gpio-switch is valid\n");
|
||
|
+ ret = -EINVAL;
|
||
|
+ goto err_devm_kfree;
|
||
|
+ } else {
|
||
|
+ ret = devm_gpio_request(&pdev->dev, priv->switch_gpio, "SWITCH");
|
||
|
+ if (ret) {
|
||
|
+ dev_err(&pdev->dev,
|
||
|
+ "failed request gpio-switch\n");
|
||
|
+ ret = -EBUSY;
|
||
|
+ goto err_devm_kfree;
|
||
|
+ } else {
|
||
|
+ gpio_direction_output(priv->switch_gpio, 1);
|
||
|
+ gpio_set_value(priv->switch_gpio, 1);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = snd_soc_register_component(&pdev->dev, &soc_codec_driver_acx00,
|
||
|
+ acx00_codec_dai, ARRAY_SIZE(acx00_codec_dai));
|
||
|
+
|
||
|
+ if (ret < 0)
|
||
|
+ dev_err(&pdev->dev, "Failed register acx00: %d\n", ret);
|
||
|
+
|
||
|
+ ret = sysfs_create_group(&pdev->dev.kobj, &audio_debug_attr_group);
|
||
|
+ if (ret)
|
||
|
+ dev_warn(&pdev->dev, "failed to create attr group\n");
|
||
|
+
|
||
|
+ return 0;
|
||
|
+
|
||
|
+err_devm_kfree:
|
||
|
+ devm_kfree(&pdev->dev, priv);
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+/* Mark this space to clear the LINEOUT & gpio */
|
||
|
+static void acx00_codec_dev_shutdown(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = platform_get_drvdata(pdev);
|
||
|
+
|
||
|
+ if (priv->spk_gpio_used)
|
||
|
+ gpio_set_value(priv->spk_gpio, 0);
|
||
|
+}
|
||
|
+
|
||
|
+static int acx00_codec_dev_remove(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ struct acx00_priv *priv = platform_get_drvdata(pdev);
|
||
|
+
|
||
|
+#ifndef ACX00_DAPM_LINEOUT
|
||
|
+ /*
|
||
|
+ snd_soc_component_update_bits(priv->component, AC_LINEOUT_CTL,
|
||
|
+ (1<<LINEOUT_EN), (0<<LINEOUT_EN));
|
||
|
+ */
|
||
|
+#endif
|
||
|
+ snd_soc_unregister_component(&pdev->dev);
|
||
|
+ clk_disable_unprepare(priv->clk);
|
||
|
+ devm_kfree(&pdev->dev, priv);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct of_device_id acx00_codec_match[] = {
|
||
|
+ { .compatible = "x-powers,ac200-codec" },
|
||
|
+ { }
|
||
|
+};
|
||
|
+MODULE_DEVICE_TABLE(of, acx00_codec_match);
|
||
|
+
|
||
|
+static struct platform_driver acx00_codec_driver = {
|
||
|
+ .driver = {
|
||
|
+ .name = "acx00-codec",
|
||
|
+ .of_match_table = acx00_codec_match,
|
||
|
+ },
|
||
|
+ .probe = acx00_codec_dev_probe,
|
||
|
+ .remove = acx00_codec_dev_remove,
|
||
|
+ .shutdown = acx00_codec_dev_shutdown,
|
||
|
+};
|
||
|
+
|
||
|
+static int __init acx00_codec_driver_init(void)
|
||
|
+{
|
||
|
+ return platform_driver_register(&acx00_codec_driver);
|
||
|
+}
|
||
|
+
|
||
|
+static void __exit acx00_codec_driver_exit(void)
|
||
|
+{
|
||
|
+ platform_driver_unregister(&acx00_codec_driver);
|
||
|
+}
|
||
|
+late_initcall(acx00_codec_driver_init);
|
||
|
+module_exit(acx00_codec_driver_exit);
|
||
|
+
|
||
|
+MODULE_LICENSE("GPL");
|
||
|
+MODULE_DESCRIPTION("SUNXI ASoC ACX00 Codec Driver");
|
||
|
+MODULE_AUTHOR("wolfgang huang");
|
||
|
+MODULE_ALIAS("platform:acx00-codec");
|
||
|
diff --git a/sound/soc/codecs/acx00.h b/sound/soc/codecs/acx00.h
|
||
|
new file mode 100644
|
||
|
index 000000000..5137cf365
|
||
|
--- /dev/null
|
||
|
+++ b/sound/soc/codecs/acx00.h
|
||
|
@@ -0,0 +1,356 @@
|
||
|
+/*
|
||
|
+ * sound\soc\codecs\acx00.h
|
||
|
+ * (C) Copyright 2012-2016
|
||
|
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
||
|
+ * Wolfgang Huang <huangjinhui@allwinnertech.com>
|
||
|
+ *
|
||
|
+ * some simple description for this code
|
||
|
+ *
|
||
|
+ * 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.
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#ifndef __ACX00_H_
|
||
|
+#define __ACX00_H_
|
||
|
+
|
||
|
+/* ACX00 register offset list */
|
||
|
+#define AC_SYS_CLK_CTL 0x2000
|
||
|
+#define AC_SYS_MOD_RST 0x2002
|
||
|
+#define AC_SYS_SR_CTL 0x2004
|
||
|
+/* Left blank */
|
||
|
+#define AC_I2S_CTL 0x2100
|
||
|
+#define AC_I2S_CLK 0x2102
|
||
|
+#define AC_I2S_FMT0 0x2104
|
||
|
+/* Left blank */
|
||
|
+#define AC_I2S_FMT1 0x2108
|
||
|
+/* Left blank */
|
||
|
+#define AC_I2S_MIXER_SRC 0x2114
|
||
|
+#define AC_I2S_MIXER_GAIN 0x2116
|
||
|
+#define AC_I2S_DAC_VOL 0x2118
|
||
|
+#define AC_I2S_ADC_VOL 0x211A
|
||
|
+/* Left blank */
|
||
|
+#define AC_DAC_CTL 0x2200
|
||
|
+#define AC_DAC_MIXER_SRC 0x2202
|
||
|
+#define AC_DAC_MIXER_GAIN 0x2204
|
||
|
+/* Left blank */
|
||
|
+#define AC_OUT_MIXER_CTL 0x2220
|
||
|
+#define AC_OUT_MIXER_SRC 0x2222
|
||
|
+#define AC_LINEOUT_CTL 0x2224
|
||
|
+/* Left blank */
|
||
|
+#define AC_ADC_CTL 0x2300
|
||
|
+/* Left blank */
|
||
|
+#define AC_MICBIAS_CTL 0x2310
|
||
|
+/* Left blank */
|
||
|
+#define AC_ADC_MIC_CTL 0x2320
|
||
|
+#define AC_ADC_MIXER_SRC 0x2322
|
||
|
+/* Left blank */
|
||
|
+#define AC_BIAS_CTL 0x232A
|
||
|
+#define AC_ANALOG_PROF_CTL 0x232C
|
||
|
+/* Left blank */
|
||
|
+#define AC_ADC_DAPL_CTRL 0x2500
|
||
|
+#define AC_ADC_DAPR_CTRL 0x2502
|
||
|
+#define AC_ADC_DAPLSTA 0x2504
|
||
|
+#define AC_ADC_DAPRSTA 0x2506
|
||
|
+#define AC_ADC_DAP_LTL 0x2508
|
||
|
+#define AC_ADC_DAP_RTL 0x250A
|
||
|
+#define AC_ADC_DAP_LHAC 0x250C
|
||
|
+#define AC_ADC_DAP_LLAC 0x250E
|
||
|
+#define AC_ADC_DAP_RHAC 0x2510
|
||
|
+#define AC_ADC_DAP_RLAC 0x2512
|
||
|
+#define AC_ADC_DAP_LDT 0x2514
|
||
|
+#define AC_ADC_DAP_LAT 0x2516
|
||
|
+#define AC_ADC_DAP_RDT 0x2518
|
||
|
+#define AC_ADC_DAP_RAT 0x251A
|
||
|
+#define AC_ADC_DAP_NTH 0x251C
|
||
|
+#define AC_ADC_DAP_LHNAC 0x251E
|
||
|
+#define AC_ADC_DAP_LLNAC 0x2520
|
||
|
+#define AC_ADC_DAP_RHNAC 0x2522
|
||
|
+#define AC_ADC_DAP_RLNAC 0x2524
|
||
|
+#define AC_ADC_DAP_HHPFC 0x2526
|
||
|
+#define AC_ADC_DAP_LHPFC 0x2528
|
||
|
+#define AC_ADC_DAP_OPT 0x252A
|
||
|
+/* Left blank */
|
||
|
+#define AC_AGC_SEL 0x2480
|
||
|
+/* Left blank */
|
||
|
+#define AC_ADC_DAPL_CTRL 0x2500
|
||
|
+#define AC_ADC_DAPR_CTRL 0x2502
|
||
|
+#define AC_ADC_DAPLSTA 0x2504
|
||
|
+#define AC_ADC_DAPRSTA 0x2506
|
||
|
+#define AC_ADC_DAP_LTL 0x2508
|
||
|
+#define AC_ADC_DAP_RTL 0x250A
|
||
|
+#define AC_ADC_DAP_LHAC 0x250C
|
||
|
+#define AC_ADC_DAP_LLAC 0x250E
|
||
|
+#define AC_ADC_DAP_RHAC 0x2510
|
||
|
+#define AC_ADC_DAP_RLAC 0x2512
|
||
|
+#define AC_ADC_DAP_LDT 0x2514
|
||
|
+#define AC_ADC_DAP_LAT 0x2516
|
||
|
+#define AC_ADC_DAP_RDT 0x2518
|
||
|
+#define AC_ADC_DAP_RAT 0x251A
|
||
|
+#define AC_ADC_DAP_NTH 0x251C
|
||
|
+#define AC_ADC_DAP_LHNAC 0x251E
|
||
|
+#define AC_ADC_DAP_LLNAC 0x2520
|
||
|
+#define AC_ADC_DAP_RHNAC 0x2522
|
||
|
+#define AC_ADC_DAP_RLNAC 0x2524
|
||
|
+#define AC_ADC_DAP_HHPFC 0x2526
|
||
|
+#define AC_ADC_DAP_LHPFC 0x2528
|
||
|
+#define AC_ADC_DAP_OPT 0x252A
|
||
|
+/* Left blank */
|
||
|
+#define AC_DRC_SEL 0x2f80
|
||
|
+/* Left blank */
|
||
|
+#define AC_DRC_CHAN_CTRL 0x3000
|
||
|
+#define AC_DRC_HHPFC 0x3002
|
||
|
+#define AC_DRC_LHPFC 0x3004
|
||
|
+#define AC_DRC_CTRL 0x3006
|
||
|
+#define AC_DRC_LPFHAT 0x3008
|
||
|
+#define AC_DRC_LPFLAT 0x300A
|
||
|
+#define AC_DRC_RPFHAT 0x300C
|
||
|
+#define AC_DRC_RPFLAT 0x300E
|
||
|
+#define AC_DRC_LPFHRT 0x3010
|
||
|
+#define AC_DRC_LPFLRT 0x3012
|
||
|
+#define AC_DRC_RPFHRT 0x3014
|
||
|
+#define AC_DRC_RPFLRT 0x3016
|
||
|
+#define AC_DRC_LRMSHAT 0x3018
|
||
|
+#define AC_DRC_LRMSLAT 0x301A
|
||
|
+#define AC_DRC_RRMSHAT 0x301C
|
||
|
+#define AC_DRC_RRMSLAT 0x301E
|
||
|
+#define AC_DRC_HCT 0x3020
|
||
|
+#define AC_DRC_LCT 0x3022
|
||
|
+#define AC_DRC_HKC 0x3024
|
||
|
+#define AC_DRC_LKC 0x3026
|
||
|
+#define AC_DRC_HOPC 0x3028
|
||
|
+#define AC_DRC_LOPC 0x302A
|
||
|
+#define AC_DRC_HLT 0x302C
|
||
|
+#define AC_DRC_LLT 0x302E
|
||
|
+#define AC_DRC_HKI 0x3030
|
||
|
+#define AC_DRC_LKI 0x3032
|
||
|
+#define AC_DRC_HOPL 0x3034
|
||
|
+#define AC_DRC_LOPL 0x3036
|
||
|
+#define AC_DRC_HET 0x3038
|
||
|
+#define AC_DRC_LET 0x303A
|
||
|
+#define AC_DRC_HKE 0x303C
|
||
|
+#define AC_DRC_LKE 0x303E
|
||
|
+#define AC_DRC_HOPE 0x3040
|
||
|
+#define AC_DRC_LOPE 0x3042
|
||
|
+#define AC_DRC_HKN 0x3044
|
||
|
+#define AC_DRC_LKN 0x3046
|
||
|
+#define AC_DRC_SFHAT 0x3048
|
||
|
+#define AC_DRC_SFLAT 0x304A
|
||
|
+#define AC_DRC_SFHRT 0x304C
|
||
|
+#define AC_DRC_SFLRT 0x304E
|
||
|
+#define AC_DRC_MXGHS 0x3050
|
||
|
+#define AC_DRC_MXGLS 0x3052
|
||
|
+#define AC_DRC_MNGHS 0x3054
|
||
|
+#define AC_DRC_MNGLS 0x3056
|
||
|
+#define AC_DRC_EPSHC 0x3058
|
||
|
+#define AC_DRC_EPSLC 0x305A
|
||
|
+#define AC_DRC_OPT 0x305C
|
||
|
+#define AC_DRC_HPFHGAIN 0x305E
|
||
|
+#define AC_DRC_HPFLGAIN 0x3060
|
||
|
+#define AC_DRC_BISTCR 0x3100
|
||
|
+#define AC_DRC_BISTST 0x3102
|
||
|
+
|
||
|
+/* AC_SYS_CLK_CTL : 0x2000 */
|
||
|
+#define SYS_CLK_I2S 15
|
||
|
+#define SYS_CLK_AGC 7
|
||
|
+#define SYS_CLK_DRC 6
|
||
|
+#define SYS_CLK_ADC 3
|
||
|
+#define SYS_CLK_DAC 2
|
||
|
+
|
||
|
+/* AC_SYS_MOD_RST : 0x2002 */
|
||
|
+#define MOD_RST_I2S 15
|
||
|
+#define MOD_RST_AGC 7
|
||
|
+#define MOD_RST_DRC 6
|
||
|
+#define MOD_RST_ADC 3
|
||
|
+#define MOD_RST_DAC 2
|
||
|
+
|
||
|
+/* AC_SYS_SR_CTL : 0x2004 */
|
||
|
+#define SYS_SR_BIT 0
|
||
|
+#define SYS_SR_MASK 0xF
|
||
|
+#define SYS_SR_BIT_0 0 /* 8000 */
|
||
|
+#define SYS_SR_BIT_1 1 /* 11025 */
|
||
|
+#define SYS_SR_BIT_2 2 /* 12000 */
|
||
|
+#define SYS_SR_BIT_3 3 /* 16000 */
|
||
|
+#define SYS_SR_BIT_4 4 /* 22050 */
|
||
|
+#define SYS_SR_BIT_5 5 /* 24000 */
|
||
|
+#define SYS_SR_BIT_6 6 /* 32000 */
|
||
|
+#define SYS_SR_BIT_7 7 /* 44100 */
|
||
|
+#define SYS_SR_BIT_8 8 /* 48000 */
|
||
|
+#define SYS_SR_BIT_9 9 /* 96000 */
|
||
|
+#define SYS_SR_BIT_10 10 /* 192000 */
|
||
|
+
|
||
|
+/* AC_I2S_CTL : 0x2100 */
|
||
|
+#define I2S_SDO0_EN 3
|
||
|
+#define I2S_TX_EN 2
|
||
|
+#define I2S_RX_EN 1
|
||
|
+#define I2S_GEN 0
|
||
|
+
|
||
|
+/* AC_I2S_CLK : 0x2102 */
|
||
|
+#define I2S_BCLK_OUT 15
|
||
|
+#define I2S_LRCK_OUT 14
|
||
|
+#define I2S_BLCK_DIV 10
|
||
|
+#define I2S_LRCK_PERIOD 0
|
||
|
+/* BCLK DIV Define */
|
||
|
+#define I2S_BCLK_DIV_MASK 0xF
|
||
|
+#define I2S_BCLK_DIV_1 1
|
||
|
+#define I2S_BCLK_DIV_2 2
|
||
|
+#define I2S_BCLK_DIV_3 3
|
||
|
+#define I2S_BCLK_DIV_4 4
|
||
|
+#define I2S_BCLK_DIV_5 5
|
||
|
+#define I2S_BCLK_DIV_6 6
|
||
|
+#define I2S_BCLK_DIV_7 7
|
||
|
+#define I2S_BCLK_DIV_8 8
|
||
|
+#define I2S_BCLK_DIV_9 9
|
||
|
+#define I2S_BCLK_DIV_10 10
|
||
|
+#define I2S_BCLK_DIV_11 11
|
||
|
+#define I2S_BCLK_DIV_12 12
|
||
|
+#define I2S_BCLK_DIV_13 13
|
||
|
+#define I2S_BCLK_DIV_14 14
|
||
|
+#define I2S_BCLK_DIV_15 15
|
||
|
+#define I2S_LRCK_PERIOD_MASK 0x3FF
|
||
|
+
|
||
|
+/* AC_I2S_FMT0 : 0x2104 */
|
||
|
+#define I2S_FMT_MODE 14
|
||
|
+#define I2S_FMT_TX_OFFSET 10
|
||
|
+#define I2S_FMT_RX_OFFSET 8
|
||
|
+#define I2S_FMT_SAMPLE 4
|
||
|
+#define I2S_FMT_SLOT_WIDTH 1
|
||
|
+#define I2S_FMT_LOOP 0
|
||
|
+
|
||
|
+/* AC_I2S_FMT1 : 0x2108 */
|
||
|
+#define I2S_FMT_BCLK_POLAR 15
|
||
|
+#define I2S_FMT_LRCK_POLAR 14
|
||
|
+#define I2S_FMT_EDGE_TRANSFER 13
|
||
|
+#define I2S_FMT_RX_MLS 11
|
||
|
+#define I2S_FMT_TX_MLS 10
|
||
|
+#define I2S_FMT_EXTEND 9
|
||
|
+#define I2S_FMT_LRCK_WIDTH 4 /* PCM long/short Frame */
|
||
|
+#define I2S_MFT_RX_PDM 2
|
||
|
+#define I2S_FMT_TX_PDM 0
|
||
|
+
|
||
|
+/* AC_I2S_MIXER_SRC : 0x2114 */
|
||
|
+#define I2S_MIXERL_SRC_DAC 13
|
||
|
+#define I2S_MIXERL_SRC_ADC 12
|
||
|
+#define I2S_MIXERR_SRC_DAC 9
|
||
|
+#define I2S_MIXERR_SRC_ADC 8
|
||
|
+
|
||
|
+/* AC_I2S_MIXER_GAIN : 0x2116 */
|
||
|
+#define I2S_MIXERL_GAIN_DAC 13
|
||
|
+#define I2S_MIXERL_GAIN_ADC 12
|
||
|
+#define I2S_MIXERR_GAIN_DAC 9
|
||
|
+#define I2S_MIXERR_GAIN_ADC 8
|
||
|
+
|
||
|
+
|
||
|
+/* AC_I2S_DAC_VOL : 0x2118 */
|
||
|
+#define I2S_DACL_VOL 8
|
||
|
+#define I2S_DACR_VOL 0
|
||
|
+
|
||
|
+/* AC_I2S_ADC_VOL : 0x211A */
|
||
|
+#define I2S_ADCL_VOL 8
|
||
|
+#define I2S_ADCR_VOL 0
|
||
|
+
|
||
|
+/* AC_DAC_CTL : 0x2200 */
|
||
|
+#define DAC_CTL_DAC_EN 15
|
||
|
+#define DAC_CTL_HPF_EN 14
|
||
|
+#define DAC_CTL_FIR 13
|
||
|
+#define DAC_CTL_MODQU 8
|
||
|
+
|
||
|
+/* AC_DAC_MIXER_SRC : 0x2202 */
|
||
|
+#define DAC_MIXERL_SRC_DAC 13
|
||
|
+#define DAC_MIXERL_SRC_ADC 12
|
||
|
+#define DAC_MIXERR_SRC_DAC 9
|
||
|
+#define DAC_MIXERR_SRC_ADC 8
|
||
|
+
|
||
|
+/* AC_DAC_MIXER_GAIN : 0x2204 */
|
||
|
+#define DAC_MIXERL_GAIN_DAC 13
|
||
|
+#define DAC_MIXERL_GAIN_ADC 12
|
||
|
+#define DAC_MIXERR_GAIN_DAC 9
|
||
|
+#define DAC_MIXERR_GAIN_ADC 8
|
||
|
+
|
||
|
+/* AC_OUT_MIXER_CTL : 0x2220 */
|
||
|
+#define OUT_MIXER_DACR_EN 15
|
||
|
+#define OUT_MIXER_DACL_EN 14
|
||
|
+#define OUT_MIXER_RMIX_EN 13
|
||
|
+#define OUT_MIXER_LMIX_EN 12
|
||
|
+#define OUT_MIXER_LINE_VOL 8
|
||
|
+#define OUT_MIXER_MIC1_VOL 4
|
||
|
+#define OUT_MIXER_MIC2_VOL 0
|
||
|
+
|
||
|
+/* AC_OUT_MIXER_SRC : 0x2222 */
|
||
|
+#define OUT_MIXERR_SRC_MIC1 14
|
||
|
+#define OUT_MIXERR_SRC_MIC2 13
|
||
|
+#define OUT_MIXERR_SRC_PHPN 12
|
||
|
+#define OUT_MIXERR_SRC_PHP 11
|
||
|
+#define OUT_MIXERR_SRC_LINER 10
|
||
|
+#define OUT_MIXERR_SRC_DACR 9
|
||
|
+#define OUT_MIXERR_SRC_DACL 8
|
||
|
+#define OUT_MIXERL_SRC_MIC1 6
|
||
|
+#define OUT_MIXERL_SRC_MIC2 5
|
||
|
+#define OUT_MIXERL_SRC_PHPN 4
|
||
|
+#define OUT_MIXERL_SRC_PHN 3
|
||
|
+#define OUT_MIXERL_SRC_LINEL 2
|
||
|
+#define OUT_MIXERL_SRC_DACL 1
|
||
|
+#define OUT_MIXERL_SRC_DACR 0
|
||
|
+
|
||
|
+/* AC_LINEOUT_CTL : 0x2224 */
|
||
|
+#define LINEOUT_EN 15
|
||
|
+#define LINEL_SRC_EN 14
|
||
|
+#define LINER_SRC_EN 13
|
||
|
+#define LINEL_SRC 12
|
||
|
+#define LINER_SRC 11
|
||
|
+/* ramp just skip */
|
||
|
+#define LINE_SLOPE_SEL 8
|
||
|
+#define LINE_ANTI_TIME 5
|
||
|
+#define LINEOUT_VOL 0
|
||
|
+
|
||
|
+/* AC_ADC_CTL : 0x2300 */
|
||
|
+#define ADC_EN 15
|
||
|
+#define ADC_ENDM 14
|
||
|
+#define ADC_FIR 13
|
||
|
+#define ADC_DELAY_TIME 2
|
||
|
+#define ADC_DELAY_EN 1
|
||
|
+
|
||
|
+/* AC_MICBIAS_CTL : 0x2310 */
|
||
|
+#define MMBIAS_EN 15
|
||
|
+#define MMBIAS_CHOPPER 14
|
||
|
+#define MMBIAS_CHOP_CLK 12
|
||
|
+#define MMBIAS_SEL 8
|
||
|
+#define ADDA_BIAS_CUR 3
|
||
|
+
|
||
|
+/* AC_ADC_MIC_CTL : 0x2320 */
|
||
|
+#define ADCR_EN 15
|
||
|
+#define ADCL_EN 14
|
||
|
+#define ADC_GAIN 8
|
||
|
+#define MIC1_GAIN_EN 7
|
||
|
+#define MIC1_BOOST 4
|
||
|
+#define MIC2_GAIN_EN 3
|
||
|
+#define MIC2_BOOST 0
|
||
|
+
|
||
|
+/* AC_ADC_MIXER_SRC : 0x2322 */
|
||
|
+#define ADC_MIXERR_MIC1 14
|
||
|
+#define ADC_MIXERR_MIC2 13
|
||
|
+#define ADC_MIXERR_PHPN 12
|
||
|
+#define ADC_MIXERR_PHP 11
|
||
|
+#define ADC_MIXERR_LINER 10
|
||
|
+#define ADC_MIXERR_MIXR 9
|
||
|
+#define ADC_MIXERR_MIXL 8
|
||
|
+#define ADC_MIXERL_MIC1 6
|
||
|
+#define ADC_MIXERL_MIC2 5
|
||
|
+#define ADC_MIXERL_PHPN 4
|
||
|
+#define ADC_MIXERL_PHN 3
|
||
|
+#define ADC_MIXERL_LINEL 2
|
||
|
+#define ADC_MIXERL_MIXL 1
|
||
|
+#define ADC_MIXERL_MIXR 0
|
||
|
+
|
||
|
+/* AC_BIAS_CTL : 0x232A */
|
||
|
+
|
||
|
+/* AC_ANALOG_PROF_CTL : 0x232C */
|
||
|
+/* used for current performance measure */
|
||
|
+
|
||
|
+/* AC_DLDO_OSC_CTL : 0x2340 */
|
||
|
+/* AC_ALDO_CTL : 0x2342 */
|
||
|
+/* used for digital & analog LDO test... etc */
|
||
|
+
|
||
|
+#endif
|
||
|
--
|
||
|
2.35.3
|
||
|
|