All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] ASoC: CX2072X codec support (revised)
@ 2019-04-23 14:13 Takashi Iwai
  2019-04-23 14:13 ` [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC Takashi Iwai
  2019-04-23 14:13 ` [PATCH 2/2] ASoC: Intel: Add machine driver for CX2072X on BYT/CHT platforms Takashi Iwai
  0 siblings, 2 replies; 20+ messages in thread
From: Takashi Iwai @ 2019-04-23 14:13 UTC (permalink / raw)
  To: alsa-devel; +Cc: Mark Brown, Pierre-Louis Bossart

Hi,

it's been long time ago the first version of CX2072X codec patch was
submitted, and I hoped that it'd be resubmitted, but never happened,
as the original author seems to have left, unfortunately.

Since I have such a laptop (ASUS E200HA), I took some time to cook and
brush up the patches.  The patches are for 5.2, should be applicable
to ASoC tree for-next branch.

Will post the corresponding UCM profile patch once when this gets
accepted.


thanks,

Takashi

===

Simon Ho (1):
  ASoC: Add support for Conexant CX2072X CODEC

Takashi Iwai (1):
  ASoC: Intel: Add machine driver for CX2072X on BYT/CHT platforms

 sound/soc/codecs/Kconfig                          |    7 +
 sound/soc/codecs/Makefile                         |    2 +
 sound/soc/codecs/cx2072x.c                        | 2197 +++++++++++++++++++++
 sound/soc/codecs/cx2072x.h                        |  317 +++
 sound/soc/intel/boards/Kconfig                    |   11 +
 sound/soc/intel/boards/Makefile                   |    2 +
 sound/soc/intel/boards/bytcht_cx2072x.c           |  285 +++
 sound/soc/intel/common/soc-acpi-intel-byt-match.c |    8 +
 sound/soc/intel/common/soc-acpi-intel-cht-match.c |    8 +
 9 files changed, 2837 insertions(+)
 create mode 100644 sound/soc/codecs/cx2072x.c
 create mode 100644 sound/soc/codecs/cx2072x.h
 create mode 100644 sound/soc/intel/boards/bytcht_cx2072x.c

-- 
2.16.4

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-04-23 14:13 [PATCH 0/2] ASoC: CX2072X codec support (revised) Takashi Iwai
@ 2019-04-23 14:13 ` Takashi Iwai
  2019-04-27 17:59   ` Mark Brown
  2019-04-23 14:13 ` [PATCH 2/2] ASoC: Intel: Add machine driver for CX2072X on BYT/CHT platforms Takashi Iwai
  1 sibling, 1 reply; 20+ messages in thread
From: Takashi Iwai @ 2019-04-23 14:13 UTC (permalink / raw)
  To: alsa-devel; +Cc: Mark Brown, Pierre-Louis Bossart

From: Simon Ho <simon.ho@conexant.com>

Initial commit of the Conexant CX2072X CODEC driver. Some features are
not present.

The CX2072X is a ultra low power stereo audio codec supports I2S/TDM
host interface with EQ, DRC features in playback mode.

Featues of CX2072X codec:

 * Two 24 bits DACs and DACs.
 * Stereo Headphone AMP.
 * 2.8W per channel class-D output.
 * Integrated seven bands per channel EQ and DRC.
 * Fully integrated headset support with detect/switch.
 * Stereo digital microphone for array applications.
 * S/PDIF output.
 * Bi-directional GPIOs.
 * Support analog and digital PC Beeep.
 * One headset button support.
 * Supports a wide variety of host interfaces.
   -wide variety of I2S and similar bit-stream formats
    with word lengths of up to 24 bits.
   -TDM stream supports up to 4 channels.
 * AEC loopback support.

Further fixes by tiwai:
 * Rebase to 5.1+
 * Missing declarations of jack detection helpers
 * Missing DAPM entry definitions
 * Missing power hooks
 * Workaround for the jack detection during cache-only
 * Fix uninitialized variable warning
 * Fix doubly clk disablement at cx2072x_set_bias_level()
 * Plumbing jack detection code for Intel ASoC
 * Code cleanups and minor refactoring

The OF code was dropped due to the lack of testability.
It should be easy to re-add once if someone can test it.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=115531
Signed-off-by: Simon Ho <simon.ho@conexant.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/soc/codecs/Kconfig   |    7 +
 sound/soc/codecs/Makefile  |    2 +
 sound/soc/codecs/cx2072x.c | 2197 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/cx2072x.h |  317 +++++++
 4 files changed, 2523 insertions(+)
 create mode 100644 sound/soc/codecs/cx2072x.c
 create mode 100644 sound/soc/codecs/cx2072x.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 8f577258080b..472bde124ebe 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -72,6 +72,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_CS47L24 if MFD_CS47L24
 	select SND_SOC_CS53L30 if I2C
 	select SND_SOC_CX20442 if TTY
+	select SND_SOC_CX2072X if I2C
 	select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_DA7213 if I2C
 	select SND_SOC_DA7218 if I2C
@@ -584,6 +585,12 @@ config SND_SOC_CX20442
 	tristate
 	depends on TTY
 
+config SND_SOC_CX2072X
+	tristate "Conexant CX2072X CODEC"
+	depends on I2C
+	help
+	  Enable support for Conexant CX20721 and CX20723 codec chips.
+
 config SND_SOC_JZ4740_CODEC
 	depends on MIPS || COMPILE_TEST
 	select REGMAP_MMIO
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index aa7720a7a0aa..454b8087a5bd 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -67,6 +67,7 @@ snd-soc-cs4349-objs := cs4349.o
 snd-soc-cs47l24-objs := cs47l24.o
 snd-soc-cs53l30-objs := cs53l30.o
 snd-soc-cx20442-objs := cx20442.o
+snd-soc-cx2072x-objs := cx2072x.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-da7213-objs := da7213.o
 snd-soc-da7218-objs := da7218.o
@@ -341,6 +342,7 @@ obj-$(CONFIG_SND_SOC_CS4349)	+= snd-soc-cs4349.o
 obj-$(CONFIG_SND_SOC_CS47L24)	+= snd-soc-cs47l24.o
 obj-$(CONFIG_SND_SOC_CS53L30)	+= snd-soc-cs53l30.o
 obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
+obj-$(CONFIG_SND_SOC_CX2072X)	+= snd-soc-cx2072x.o
 obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_DA7213)	+= snd-soc-da7213.o
 obj-$(CONFIG_SND_SOC_DA7218)	+= snd-soc-da7218.o
diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
new file mode 100644
index 000000000000..f77d587efe2e
--- /dev/null
+++ b/sound/soc/codecs/cx2072x.c
@@ -0,0 +1,2197 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC CX20721/CX20723 codec driver
+//
+// Copyright:	(C) 2017 Conexant Systems, Inc.
+// Author:	Simon Ho, <Simon.ho@conexant.com>
+//
+// TODO: add support for TDM mode.
+//
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include "cx2072x.h"
+
+#define PLL_OUT_HZ_48	(1024 * 3 * 48000)
+#define BITS_PER_SLOT	8
+
+#define CX2072X_PLBK_EQ_BAND_NUM	7
+#define CX2072X_PLBK_EQ_COEF_LEN	11
+#define CX2072X_PLBK_DRC_PARM_LEN	9
+#define CX2072X_CLASSD_AMP_LEN		6
+
+/* codec private data */
+struct cx2072x_priv {
+	struct regmap *regmap;
+	struct clk *mclk;
+	unsigned int mclk_rate;
+	struct device *dev;
+	struct snd_soc_component *codec;
+	struct snd_soc_jack *jack;
+	unsigned int jack_state;
+	unsigned int bclk_ratio;
+	bool plbk_eq_en;
+	bool plbk_eq_en_changed;
+	bool plbk_eq_changed;
+	u8 plbk_eq[2][CX2072X_PLBK_EQ_BAND_NUM][CX2072X_PLBK_EQ_COEF_LEN];
+	int plbk_eq_channel;
+	bool plbk_drc_en;
+	bool plbk_drc_en_changed;
+	bool plbk_drc_changed;
+	bool pll_changed;
+	bool i2spcm_changed;
+	int sample_size;
+	int frame_size;
+	int sample_rate;
+	unsigned int dai_fmt;
+	u32 rev_id;
+	bool en_aec_ref;
+	u8 plbk_drc[CX2072X_PLBK_DRC_PARM_LEN];
+	u8 classd_amp[CX2072X_CLASSD_AMP_LEN];
+	struct mutex eq_coeff_lock; /* EQ DSP lock */
+};
+
+/*
+ * DAC/ADC Volume
+ *
+ * max : 74 : 0 dB
+ *	 ( in 1 dB  step )
+ * min : 0 : -74 dB
+ */
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -7400, 100, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -7400, 100, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 1200, 0);
+
+struct cx2072x_eq_ctrl {
+	u8 ch;
+	u8 band;
+};
+
+static const DECLARE_TLV_DB_RANGE(hpf_tlv,
+	0, 0, TLV_DB_SCALE_ITEM(120, 0, 0),
+	1, 63, TLV_DB_SCALE_ITEM(30, 30, 0)
+);
+
+/* Lookup table for PRE_DIV */
+static const struct {
+	unsigned int mclk;
+	unsigned int div;
+} mclk_pre_div[] = {
+	{ 6144000, 1 },
+	{ 12288000, 2 },
+	{ 19200000, 3 },
+	{ 26000000, 4 },
+	{ 28224000, 5 },
+	{ 36864000, 6 },
+	{ 36864000, 7 },
+	{ 48000000, 8 },
+	{ 49152000, 8 },
+};
+
+/*
+ * cx2072x register cache.
+ */
+static const struct reg_default cx2072x_reg_defaults[] = {
+	{ CX2072X_AFG_POWER_STATE, 0x00000003 },
+	{ CX2072X_UM_RESPONSE, 0x00000000 },
+	{ CX2072X_GPIO_DATA, 0x00000000 },
+	{ CX2072X_GPIO_ENABLE, 0x00000000 },
+	{ CX2072X_GPIO_DIRECTION, 0x00000000 },
+	{ CX2072X_GPIO_WAKE, 0x00000000 },
+	{ CX2072X_GPIO_UM_ENABLE, 0x00000000 },
+	{ CX2072X_GPIO_STICKY_MASK, 0x00000000 },
+	{ CX2072X_DAC1_CONVERTER_FORMAT, 0x00000031 },
+	{ CX2072X_DAC1_AMP_GAIN_RIGHT, 0x0000004a },
+	{ CX2072X_DAC1_AMP_GAIN_LEFT, 0x0000004a },
+	{ CX2072X_DAC1_POWER_STATE, 0x00000433 },
+	{ CX2072X_DAC1_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+	{ CX2072X_DAC1_EAPD_ENABLE, 0x00000000 },
+	{ CX2072X_DAC2_CONVERTER_FORMAT, 0x00000031 },
+	{ CX2072X_DAC2_AMP_GAIN_RIGHT, 0x0000004a },
+	{ CX2072X_DAC2_AMP_GAIN_LEFT, 0x0000004a },
+	{ CX2072X_DAC2_POWER_STATE, 0x00000433 },
+	{ CX2072X_DAC2_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+	{ CX2072X_ADC1_CONVERTER_FORMAT, 0x00000031 },
+	{ CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_LEFT_0, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_LEFT_1, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_LEFT_2, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_RIGHT_3, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_LEFT_3, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_RIGHT_4, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_LEFT_4, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_RIGHT_5, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_LEFT_5, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_RIGHT_6, 0x0000004a },
+	{ CX2072X_ADC1_AMP_GAIN_LEFT_6, 0x0000004a },
+	{ CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0x00000000 },
+	{ CX2072X_ADC1_POWER_STATE, 0x00000433 },
+	{ CX2072X_ADC1_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+	{ CX2072X_ADC2_CONVERTER_FORMAT, 0x00000031 },
+	{ CX2072X_ADC2_AMP_GAIN_RIGHT_0, 0x0000004a },
+	{ CX2072X_ADC2_AMP_GAIN_LEFT_0, 0x0000004a },
+	{ CX2072X_ADC2_AMP_GAIN_RIGHT_1, 0x0000004a },
+	{ CX2072X_ADC2_AMP_GAIN_LEFT_1, 0x0000004a },
+	{ CX2072X_ADC2_AMP_GAIN_RIGHT_2, 0x0000004a },
+	{ CX2072X_ADC2_AMP_GAIN_LEFT_2, 0x0000004a },
+	{ CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0x00000000 },
+	{ CX2072X_ADC2_POWER_STATE, 0x00000433 },
+	{ CX2072X_ADC2_CONVERTER_STREAM_CHANNEL, 0x00000000 },
+	{ CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0x00000000 },
+	{ CX2072X_PORTA_POWER_STATE, 0x00000433 },
+	{ CX2072X_PORTA_PIN_CTRL, 0x000000c0 },
+	{ CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x00000000 },
+	{ CX2072X_PORTA_PIN_SENSE, 0x00000000 },
+	{ CX2072X_PORTA_EAPD_BTL, 0x00000002 },
+	{ CX2072X_PORTB_POWER_STATE, 0x00000433 },
+	{ CX2072X_PORTB_PIN_CTRL, 0x00000000 },
+	{ CX2072X_PORTB_UNSOLICITED_RESPONSE, 0x00000000 },
+	{ CX2072X_PORTB_PIN_SENSE, 0x00000000 },
+	{ CX2072X_PORTB_EAPD_BTL, 0x00000002 },
+	{ CX2072X_PORTB_GAIN_RIGHT, 0x00000000 },
+	{ CX2072X_PORTB_GAIN_LEFT, 0x00000000 },
+	{ CX2072X_PORTC_POWER_STATE, 0x00000433 },
+	{ CX2072X_PORTC_PIN_CTRL, 0x00000000 },
+	{ CX2072X_PORTC_GAIN_RIGHT, 0x00000000 },
+	{ CX2072X_PORTC_GAIN_LEFT, 0x00000000 },
+	{ CX2072X_PORTD_POWER_STATE, 0x00000433 },
+	{ CX2072X_PORTD_PIN_CTRL, 0x00000020 },
+	{ CX2072X_PORTD_UNSOLICITED_RESPONSE, 0x00000000 },
+	{ CX2072X_PORTD_PIN_SENSE, 0x00000000 },
+	{ CX2072X_PORTD_GAIN_RIGHT, 0x00000000 },
+	{ CX2072X_PORTD_GAIN_LEFT, 0x00000000 },
+	{ CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0x00000000 },
+	{ CX2072X_PORTE_POWER_STATE, 0x00000433 },
+	{ CX2072X_PORTE_PIN_CTRL, 0x00000040 },
+	{ CX2072X_PORTE_UNSOLICITED_RESPONSE, 0x00000000 },
+	{ CX2072X_PORTE_PIN_SENSE, 0x00000000 },
+	{ CX2072X_PORTE_EAPD_BTL, 0x00000002 },
+	{ CX2072X_PORTE_GAIN_RIGHT, 0x00000000 },
+	{ CX2072X_PORTE_GAIN_LEFT, 0x00000000 },
+	{ CX2072X_PORTF_POWER_STATE, 0x00000433 },
+	{ CX2072X_PORTF_PIN_CTRL, 0x00000000 },
+	{ CX2072X_PORTF_UNSOLICITED_RESPONSE, 0x00000000 },
+	{ CX2072X_PORTF_PIN_SENSE, 0x00000000 },
+	{ CX2072X_PORTF_GAIN_RIGHT, 0x00000000 },
+	{ CX2072X_PORTF_GAIN_LEFT, 0x00000000 },
+	{ CX2072X_PORTG_POWER_STATE, 0x00000433 },
+	{ CX2072X_PORTG_PIN_CTRL, 0x00000040 },
+	{ CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0x00000000 },
+	{ CX2072X_PORTG_EAPD_BTL, 0x00000002 },
+	{ CX2072X_PORTM_POWER_STATE, 0x00000433 },
+	{ CX2072X_PORTM_PIN_CTRL, 0x00000000 },
+	{ CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0x00000000 },
+	{ CX2072X_PORTM_EAPD_BTL, 0x00000002 },
+	{ CX2072X_MIXER_POWER_STATE, 0x00000433 },
+	{ CX2072X_MIXER_GAIN_RIGHT_0, 0x0000004a },
+	{ CX2072X_MIXER_GAIN_LEFT_0, 0x0000004a },
+	{ CX2072X_MIXER_GAIN_RIGHT_1, 0x0000004a },
+	{ CX2072X_MIXER_GAIN_LEFT_1, 0x0000004a },
+	{ CX2072X_SPKR_DRC_ENABLE_STEP, 0x040065a4 },
+	{ CX2072X_SPKR_DRC_CONTROL, 0x007b0024 },
+	{ CX2072X_SPKR_DRC_TEST, 0x00000000 },
+	{ CX2072X_DIGITAL_BIOS_TEST0, 0x001f008a },
+	{ CX2072X_DIGITAL_BIOS_TEST2, 0x00990026 },
+	{ CX2072X_I2SPCM_CONTROL1, 0x00010001 },
+	{ CX2072X_I2SPCM_CONTROL2, 0x00000000 },
+	{ CX2072X_I2SPCM_CONTROL3, 0x00000000 },
+	{ CX2072X_I2SPCM_CONTROL4, 0x00000000 },
+	{ CX2072X_I2SPCM_CONTROL5, 0x00000000 },
+	{ CX2072X_I2SPCM_CONTROL6, 0x00000000 },
+	{ CX2072X_UM_INTERRUPT_CRTL_E, 0x00000000 },
+	{ CX2072X_CODEC_TEST2, 0x00000000 },
+	{ CX2072X_CODEC_TEST9, 0x00000004 },
+	{ CX2072X_CODEC_TEST20, 0x00000600 },
+	{ CX2072X_CODEC_TEST26, 0x00000208 },
+	{ CX2072X_ANALOG_TEST4, 0x00000000 },
+	{ CX2072X_ANALOG_TEST5, 0x00000000 },
+	{ CX2072X_ANALOG_TEST6, 0x0000059a },
+	{ CX2072X_ANALOG_TEST7, 0x000000a7 },
+	{ CX2072X_ANALOG_TEST8, 0x00000017 },
+	{ CX2072X_ANALOG_TEST9, 0x00000000 },
+	{ CX2072X_ANALOG_TEST10, 0x00000285 },
+	{ CX2072X_ANALOG_TEST11, 0x00000000 },
+	{ CX2072X_ANALOG_TEST12, 0x00000000 },
+	{ CX2072X_ANALOG_TEST13, 0x00000000 },
+	{ CX2072X_DIGITAL_TEST1, 0x00000242 },
+	{ CX2072X_DIGITAL_TEST11, 0x00000000 },
+	{ CX2072X_DIGITAL_TEST12, 0x00000084 },
+	{ CX2072X_DIGITAL_TEST15, 0x00000077 },
+	{ CX2072X_DIGITAL_TEST16, 0x00000021 },
+	{ CX2072X_DIGITAL_TEST17, 0x00000018 },
+	{ CX2072X_DIGITAL_TEST18, 0x00000024 },
+	{ CX2072X_DIGITAL_TEST19, 0x00000001 },
+	{ CX2072X_DIGITAL_TEST20, 0x00000002 },
+};
+
+/*
+ * register patch.
+ */
+static const struct reg_sequence cx2072x_patch[] = {
+	{ 0x71A4, 0x080 }, /* DC offset Calibration		*/
+	{ 0x7328, 0x65f }, /* disable the PA			*/
+	{ 0x71a8, 0x289 }, /* Set the spkeaer output gain	*/
+	{ 0x7310, 0xf05 },
+	{ 0x7290, 0x380 },
+	{ 0x7328, 0xb90 },
+	{ 0x7124, 0x001 }, /* Enable 30 Hz High pass filter	*/
+	{ 0x718c, 0x300 }, /* Disable PCBEEP pad		*/
+	{ 0x731c, 0x100 }, /* Disable SnM mode			*/
+	{ 0x641c, 0x020 }, /* Enable PortD input		*/
+	{ 0x0458, 0x040 }, /* Enable GPIO7 pin for button	*/
+	{ 0x0464, 0x040 }, /* Enable UM for GPIO7		*/
+	{ 0x0420, 0x080 }, /* Enable button response		*/
+	{ 0x7230, 0x0c4 }, /* Enable headset button		*/
+	{ 0x7200, 0x415 }, /* Power down class-D during idle	*/
+	{ 0x6e04, 0x00f }, /* Enable I2S TX			*/
+	{ 0x6e08, 0x00f }, /* Enable I2S RX			*/
+};
+
+/* return register size */
+static unsigned int cx2072x_register_size(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CX2072X_VENDOR_ID:
+	case CX2072X_REVISION_ID:
+	case CX2072X_PORTA_PIN_SENSE:
+	case CX2072X_PORTB_PIN_SENSE:
+	case CX2072X_PORTD_PIN_SENSE:
+	case CX2072X_PORTE_PIN_SENSE:
+	case CX2072X_PORTF_PIN_SENSE:
+	case CX2072X_I2SPCM_CONTROL1:
+	case CX2072X_I2SPCM_CONTROL2:
+	case CX2072X_I2SPCM_CONTROL3:
+	case CX2072X_I2SPCM_CONTROL4:
+	case CX2072X_I2SPCM_CONTROL5:
+	case CX2072X_I2SPCM_CONTROL6:
+	case CX2072X_UM_INTERRUPT_CRTL_E:
+	case CX2072X_EQ_G_COEFF:
+	case CX2072X_SPKR_DRC_CONTROL:
+	case CX2072X_SPKR_DRC_TEST:
+	case CX2072X_DIGITAL_BIOS_TEST0:
+	case CX2072X_DIGITAL_BIOS_TEST2:
+		return 4;
+	case CX2072X_EQ_ENABLE_BYPASS:
+	case CX2072X_EQ_B0_COEFF:
+	case CX2072X_EQ_B1_COEFF:
+	case CX2072X_EQ_B2_COEFF:
+	case CX2072X_EQ_A1_COEFF:
+	case CX2072X_EQ_A2_COEFF:
+	case CX2072X_DAC1_CONVERTER_FORMAT:
+	case CX2072X_DAC2_CONVERTER_FORMAT:
+	case CX2072X_ADC1_CONVERTER_FORMAT:
+	case CX2072X_ADC2_CONVERTER_FORMAT:
+	case CX2072X_CODEC_TEST2:
+	case CX2072X_CODEC_TEST9:
+	case CX2072X_CODEC_TEST20:
+	case CX2072X_CODEC_TEST26:
+	case CX2072X_ANALOG_TEST3:
+	case CX2072X_ANALOG_TEST4:
+	case CX2072X_ANALOG_TEST5:
+	case CX2072X_ANALOG_TEST6:
+	case CX2072X_ANALOG_TEST7:
+	case CX2072X_ANALOG_TEST8:
+	case CX2072X_ANALOG_TEST9:
+	case CX2072X_ANALOG_TEST10:
+	case CX2072X_ANALOG_TEST11:
+	case CX2072X_ANALOG_TEST12:
+	case CX2072X_ANALOG_TEST13:
+	case CX2072X_DIGITAL_TEST0:
+	case CX2072X_DIGITAL_TEST1:
+	case CX2072X_DIGITAL_TEST11:
+	case CX2072X_DIGITAL_TEST12:
+	case CX2072X_DIGITAL_TEST15:
+	case CX2072X_DIGITAL_TEST16:
+	case CX2072X_DIGITAL_TEST17:
+	case CX2072X_DIGITAL_TEST18:
+	case CX2072X_DIGITAL_TEST19:
+	case CX2072X_DIGITAL_TEST20:
+		return 2;
+	default:
+		return 1;
+	}
+}
+
+static int cx2072x_reg_write(void *context, unsigned int reg,
+			     unsigned int value)
+{
+	struct i2c_client *client = context;
+	struct device *dev = &client->dev;
+	unsigned int i;
+	unsigned int size;
+	u8 buf[6];
+	int ret;
+
+	size = cx2072x_register_size(dev, reg);
+	if (!size)
+		return -EINVAL;
+
+	if (reg == CX2072X_UM_INTERRUPT_CRTL_E) {
+		/* Update the MSB byte only */
+		reg += 3;
+		size = 1;
+		value >>= 24;
+	}
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	for (i = 2; i < (size + 2); ++i) {
+		buf[i] = value;
+		value >>= 8;
+	}
+
+	ret = i2c_master_send(client, buf, size + 2);
+	if (ret != size + 2) {
+		dev_err(dev, "I2C write failed, ret = %d\n", ret);
+		return ret < 0 ? ret : -EIO;
+	}
+	return 0;
+}
+
+static int cx2072x_reg_bulk_write(struct snd_soc_component *codec,
+				  unsigned int reg,
+				  const void *val, size_t val_count)
+{
+	struct i2c_client *client = to_i2c_client(codec->dev);
+	struct device *dev = &client->dev;
+	u8 buf[2 + CX2072X_MAX_EQ_COEFF];
+	int ret;
+
+	if (val_count > CX2072X_MAX_EQ_COEFF) {
+		dev_err(dev,
+			"cx2072x_reg_bulk_write failed, writing count = %d\n",
+			(int)val_count);
+		return -EINVAL;
+	}
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	memcpy(&buf[2], val, val_count);
+
+	ret = i2c_master_send(client, buf, val_count + 2);
+	if (ret != val_count + 2) {
+		dev_err(dev, "I2C bulk write failed, ret = %d\n", ret);
+		return ret < 0 ? ret : -EIO;
+	}
+	return 0;
+}
+
+static int cx2072x_reg_read(void *context, unsigned int reg,
+			    unsigned int *value)
+{
+	struct i2c_client *client = context;
+	struct device *dev = &client->dev;
+	unsigned int recv_buf = 0;
+	struct i2c_msg msgs[2];
+	unsigned int size;
+	u8 send_buf[2];
+	int ret;
+
+	size = cx2072x_register_size(dev, reg);
+	if (!size)
+		return -EINVAL;
+
+	send_buf[0] = reg >> 8;
+	send_buf[1] = reg & 0xff;
+
+	msgs[0].addr = client->addr;
+	msgs[0].len = sizeof(send_buf);
+	msgs[0].buf = send_buf;
+	msgs[0].flags = 0;
+
+	msgs[1].addr = client->addr;
+	msgs[1].len = size;
+	msgs[1].buf = (u8 *)&recv_buf;
+	msgs[1].flags = I2C_M_RD;
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs)) {
+		dev_err(dev, "Failed to read register, ret = %d\n", ret);
+		return ret < 0 ? ret : -EIO;
+	}
+
+	*value = recv_buf;
+	return 0;
+}
+
+/* get suggested pre_div valuce from mclk frequency */
+static unsigned int get_div_from_mclk(unsigned int mclk)
+{
+	unsigned int div = 8;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mclk_pre_div); i++) {
+		if (mclk <= mclk_pre_div[i].mclk) {
+			div = mclk_pre_div[i].div;
+			break;
+		}
+	}
+	return div;
+}
+
+static int cx2072x_config_pll(struct cx2072x_priv *cx2072x)
+{
+	struct device *dev = cx2072x->dev;
+	unsigned int pre_div;
+	unsigned int pre_div_val;
+	unsigned int pll_input;
+	unsigned int pll_output;
+	unsigned int int_div;
+	unsigned int frac_div;
+	u64 frac_num;
+	unsigned int frac;
+	unsigned int sample_rate = cx2072x->sample_rate;
+	int pt_sample_per_sync = 2;
+	int pt_clock_per_sample = 96;
+
+	switch (sample_rate) {
+	case 48000:
+	case 32000:
+	case 24000:
+	case 16000:
+		break;
+
+	case 96000:
+		pt_sample_per_sync = 1;
+		pt_clock_per_sample = 48;
+		break;
+
+	case 192000:
+		pt_sample_per_sync = 0;
+		pt_clock_per_sample = 24;
+		break;
+
+	default:
+		dev_err(dev, "Unsupported sample rate %d\n", sample_rate);
+		return -EINVAL;
+	}
+
+	/* Configure PLL settings */
+	pre_div = get_div_from_mclk(cx2072x->mclk_rate);
+	pll_input = cx2072x->mclk_rate / pre_div;
+	pll_output = sample_rate * 3072;
+	int_div = pll_output / pll_input;
+	frac_div = pll_output - (int_div * pll_input);
+
+	if (frac_div) {
+		frac_div *= 1000;
+		frac_div /= pll_input;
+		frac_num = ((4000 + frac_div) * ((1 << 20) - 4));
+		do_div(frac_num, 7);
+		frac = ((u32)frac_num + 499) / 1000;
+	}
+	pre_div_val = (pre_div - 1) * 2;
+
+	regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST4,
+		     0X40 | (pre_div_val << 8));
+	if (frac_div == 0) {
+		/* Int mode */
+		regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST7, 0x100);
+	} else {
+		/* frac mode */
+		regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST6,
+			     frac & 0xfff);
+		regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST7,
+			     (u8)(frac >> 12));
+	}
+
+	int_div--;
+	regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST8, int_div);
+
+	/* configure PLL tracking */
+	if (frac_div == 0) {
+		/* disable PLL tracking */
+		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST16, 0x00);
+	} else {
+		/* configure and enable PLL tracking */
+		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST16,
+			     (pt_sample_per_sync << 4) & 0xf0);
+		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST17,
+			     pt_clock_per_sample);
+		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST18,
+			     pt_clock_per_sample * 3 / 2);
+		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST19, 0x01);
+		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST20, 0x02);
+		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_TEST16,
+				   0X01, 0X01);
+	}
+
+	return 0;
+}
+
+static int cx2072x_config_i2spcm(struct cx2072x_priv *cx2072x)
+{
+	struct device *dev = cx2072x->dev;
+	unsigned int bclk_rate = 0;
+	int is_i2s = 0;
+	int has_one_bit_delay = 0;
+	int is_right_j = 0;
+	int is_frame_inv = 0;
+	int is_bclk_inv = 0;
+	int pulse_len = 1;
+	int frame_len = cx2072x->frame_size;
+	int sample_size = cx2072x->sample_size;
+	int i2s_right_slot;
+	int i2s_right_pause_interval = 0;
+	int i2s_right_pause_pos;
+	int is_big_endian = 1;
+	u64 div;
+	unsigned int mod;
+	union cx2072x_reg_i2spcm_ctrl_reg1 reg1;
+	union cx2072x_reg_i2spcm_ctrl_reg2 reg2;
+	union cx2072x_reg_i2spcm_ctrl_reg3 reg3;
+	union cx2072x_reg_i2spcm_ctrl_reg4 reg4;
+	union cx2072x_reg_i2spcm_ctrl_reg5 reg5;
+	union cx2072x_reg_i2spcm_ctrl_reg6 reg6;
+	union cx2072x_reg_digital_bios_test2 regdbt2;
+	const unsigned int fmt = cx2072x->dai_fmt;
+
+	if (frame_len <= 0) {
+		dev_err(dev, "Incorrect frame len %d\n", frame_len);
+		return -EINVAL;
+	}
+
+	if (sample_size <= 0) {
+		dev_err(dev, "Incorrect sample size %d\n", sample_size);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "config_i2spcm set_dai_fmt- %08x\n", fmt);
+
+	regdbt2.ulval = 0xac;
+
+	/* set master/slave */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		reg2.r.tx_master = 1;
+		reg3.r.rx_master = 1;
+		dev_dbg(dev, "Sets Master mode\n");
+		break;
+
+	case SND_SOC_DAIFMT_CBS_CFS:
+		reg2.r.tx_master = 0;
+		reg3.r.rx_master = 0;
+		dev_dbg(dev, "Sets Slave mode\n");
+		break;
+
+	default:
+		dev_err(dev, "Unsupported DAI master mode\n");
+		return -EINVAL;
+	}
+
+	/* set format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		is_i2s = 1;
+		has_one_bit_delay = 1;
+		pulse_len = frame_len / 2;
+		break;
+
+	case SND_SOC_DAIFMT_RIGHT_J:
+		is_i2s = 1;
+		is_right_j = 1;
+		pulse_len = frame_len / 2;
+		break;
+
+	case SND_SOC_DAIFMT_LEFT_J:
+		is_i2s = 1;
+		pulse_len = frame_len / 2;
+		break;
+
+	default:
+		dev_err(dev, "Unsupported DAI format\n");
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		is_frame_inv = is_i2s;
+		is_bclk_inv = is_i2s;
+		break;
+
+	case SND_SOC_DAIFMT_IB_IF:
+		is_frame_inv = !is_i2s;
+		is_bclk_inv = !is_i2s;
+		break;
+
+	case SND_SOC_DAIFMT_IB_NF:
+		is_frame_inv = is_i2s;
+		is_bclk_inv = !is_i2s;
+		break;
+
+	case SND_SOC_DAIFMT_NB_IF:
+		is_frame_inv = !is_i2s;
+		is_bclk_inv = is_i2s;
+		break;
+
+	default:
+		dev_err(dev, "Unsupported DAI clock inversion\n");
+		return -EINVAL;
+	}
+
+	reg1.r.rx_data_one_line = 1;
+	reg1.r.tx_data_one_line = 1;
+
+	if (is_i2s) {
+		i2s_right_slot = (frame_len / 2) / BITS_PER_SLOT;
+		i2s_right_pause_interval = (frame_len / 2) % BITS_PER_SLOT;
+		i2s_right_pause_pos = i2s_right_slot * BITS_PER_SLOT;
+	}
+
+	reg1.r.rx_ws_pol = is_frame_inv;
+	reg1.r.rx_ws_wid = pulse_len - 1;
+
+	reg1.r.rx_frm_len = frame_len / BITS_PER_SLOT - 1;
+	reg1.r.rx_sa_size = (sample_size / BITS_PER_SLOT) - 1;
+
+	reg1.r.tx_ws_pol = reg1.r.rx_ws_pol;
+	reg1.r.tx_ws_wid = pulse_len - 1;
+	reg1.r.tx_frm_len = reg1.r.rx_frm_len;
+	reg1.r.tx_sa_size = reg1.r.rx_sa_size;
+
+	reg2.r.tx_endian_sel = !is_big_endian;
+	reg2.r.tx_dstart_dly = has_one_bit_delay;
+	if (cx2072x->en_aec_ref)
+		reg2.r.tx_dstart_dly = 0;
+
+	reg3.r.rx_endian_sel = !is_big_endian;
+	reg3.r.rx_dstart_dly = has_one_bit_delay;
+
+	reg4.ulval = 0;
+
+	if (is_i2s) {
+		reg2.r.tx_slot_1 = 0;
+		reg2.r.tx_slot_2 = i2s_right_slot;
+		reg3.r.rx_slot_1 = 0;
+		if (cx2072x->en_aec_ref)
+			reg3.r.rx_slot_2 = 0;
+		else
+			reg3.r.rx_slot_2 = i2s_right_slot;
+		reg6.r.rx_pause_start_pos = i2s_right_pause_pos;
+		reg6.r.rx_pause_cycles = i2s_right_pause_interval;
+		reg6.r.tx_pause_start_pos = i2s_right_pause_pos;
+		reg6.r.tx_pause_cycles = i2s_right_pause_interval;
+	} else {
+		dev_err(dev, "TDM mode is not implemented yet\n");
+		return -EINVAL;
+	}
+	regdbt2.r.i2s_bclk_invert = is_bclk_inv;
+
+	reg1.r.rx_data_one_line = 1;
+	reg1.r.tx_data_one_line = 1;
+
+	/* Configures the BCLK output */
+	bclk_rate = cx2072x->sample_rate * frame_len;
+	reg5.r.i2s_pcm_clk_div_chan_en = 0;
+
+	/* Disables bclk output before setting new value */
+	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL5, 0);
+
+	if (reg2.r.tx_master) {
+		/* Configures BCLK rate */
+		div = PLL_OUT_HZ_48;
+		mod = do_div(div, bclk_rate);
+		if (mod) {
+			dev_err(dev, "Unsupported BCLK %dHz\n", bclk_rate);
+			return -EINVAL;
+		}
+		dev_dbg(dev, "enables BCLK %dHz output\n", bclk_rate);
+		reg5.r.i2s_pcm_clk_div = (u32)div - 1;
+		reg5.r.i2s_pcm_clk_div_chan_en = 1;
+	}
+
+	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL1, reg1.ulval);
+	regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL2, 0xffffffc0,
+			   reg2.ulval);
+	regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL3, 0xffffffc0,
+			   reg3.ulval);
+	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL4, reg4.ulval);
+	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL6, reg6.ulval);
+	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL5, reg5.ulval);
+
+	regmap_write(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
+		     regdbt2.ulval);
+
+	return 0;
+}
+
+static void cx2072x_update_eq_coeff(struct snd_soc_component *codec)
+{
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	int band, ch, value;
+
+	if (!cx2072x->plbk_eq_changed)
+		return;
+	if (!cx2072x->plbk_eq_en)
+		return;
+
+	/* set EQ to bypass mode before configuring the EQ settings */
+	regmap_write(cx2072x->regmap, CX2072X_EQ_ENABLE_BYPASS, 0x620f);
+
+	for (ch = 0; ch < 2; ch++) {
+		for (band = 0; band < CX2072X_PLBK_EQ_BAND_NUM; band++) {
+			cx2072x_reg_bulk_write(codec, CX2072X_EQ_B0_COEFF,
+					       &cx2072x->plbk_eq[ch][band][0],
+					       CX2072X_MAX_EQ_COEFF);
+			value = band + (ch << 3) + (1 << 6);
+			regmap_write(cx2072x->regmap, CX2072X_EQ_BAND, value);
+			mdelay(5);
+		}
+	}
+
+	cx2072x->plbk_eq_changed = false;
+	cx2072x->plbk_eq_en_changed = true;
+}
+
+static void cx2072x_update_eq_en(struct snd_soc_component *codec)
+{
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+	if (cx2072x->plbk_eq_en_changed) {
+		if (cx2072x->plbk_eq_en)
+			regmap_write(cx2072x->regmap, CX2072X_EQ_ENABLE_BYPASS,
+				     0x6203);
+		else
+			regmap_write(cx2072x->regmap, CX2072X_EQ_ENABLE_BYPASS,
+				     0x620c);
+
+		cx2072x->plbk_eq_en_changed = false;
+	}
+}
+
+static void cx2072x_update_drc(struct snd_soc_component *codec)
+{
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+	if (cx2072x->plbk_drc_changed && cx2072x->plbk_drc_en) {
+		cx2072x_reg_bulk_write(codec, CX2072X_SPKR_DRC_ENABLE_STEP,
+				       cx2072x->plbk_drc,
+				       CX2072X_MAX_DRC_REGS);
+		cx2072x->plbk_drc_changed = false;
+		cx2072x->plbk_drc_en_changed = true;
+	}
+}
+
+static void cx2072x_update_drc_en(struct snd_soc_component *codec)
+{
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	u8 drc_status = cx2072x->plbk_drc[0];
+
+	if (!cx2072x->plbk_drc_en_changed)
+		return;
+
+	if (cx2072x->plbk_drc_en) {
+		drc_status |= 0x1;
+		regmap_write(cx2072x->regmap, CX2072X_SPKR_DRC_ENABLE_STEP,
+			     drc_status);
+		cx2072x->plbk_drc[0] = drc_status;
+	} else {
+		drc_status &= 0xfe;
+		regmap_write(cx2072x->regmap, CX2072X_SPKR_DRC_ENABLE_STEP,
+			     drc_status);
+		cx2072x->plbk_drc[0] = drc_status;
+	}
+
+	cx2072x->plbk_drc_en_changed = false;
+}
+
+static void cx2072x_update_dsp(struct snd_soc_component *codec)
+{
+	unsigned int afg_reg;
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+	regmap_read(cx2072x->regmap, CX2072X_AFG_POWER_STATE, &afg_reg);
+
+	/* skip since device is on D3 mode */
+	if ((afg_reg & 0xf) != 0)
+		return;
+
+	regmap_read(cx2072x->regmap, CX2072X_PORTG_POWER_STATE, &afg_reg);
+
+	/* skip since device is on D3 mode */
+	if ((afg_reg & 0xf) != 0) {
+		dev_dbg(codec->dev, "failed to update dsp dueo portg is off\n");
+		return;
+	}
+
+	cx2072x_update_eq_coeff(codec);
+	cx2072x_update_eq_en(codec);
+	cx2072x_update_drc(codec);
+	cx2072x_update_drc_en(codec);
+}
+
+static int afg_power_ev(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0,
+				   0x00, 0x10);
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0,
+				   0x10, 0x10);
+		break;
+	}
+
+	return 0;
+}
+
+static int portg_power_ev(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		dev_dbg(codec->dev, "portg_power_event\n");
+		cx2072x_update_dsp(codec);
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+#define cx2072x_plbk_eq_en_info		snd_ctl_boolean_mono_info
+
+static int cx2072x_plbk_eq_en_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = cx2072x->plbk_eq_en;
+	return 0;
+}
+
+static int cx2072x_plbk_eq_en_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	const bool enable = ucontrol->value.integer.value[0];
+	int changed = 0;
+
+	if (ucontrol->value.integer.value[0] > 1)
+		return -EINVAL;
+
+	if (cx2072x->plbk_eq_en != enable) {
+		cx2072x->plbk_eq_en = enable;
+		cx2072x->plbk_eq_en_changed = true;
+		cx2072x_update_dsp(codec);
+		changed = 1;
+	}
+
+	return changed;
+}
+
+#define cx2072x_plbk_drc_en_info	snd_ctl_boolean_mono_info
+
+static int cx2072x_plbk_drc_en_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = cx2072x->plbk_drc_en;
+	return 0;
+}
+
+static int cx2072x_plbk_drc_en_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	const bool enable = ucontrol->value.integer.value[0];
+	int changed = 0;
+
+	if (ucontrol->value.integer.value[0] > 1)
+		return -EINVAL;
+
+	if (cx2072x->plbk_drc_en != enable) {
+		cx2072x->plbk_drc_en = enable;
+		cx2072x->plbk_drc_en_changed = true;
+		cx2072x_update_dsp(codec);
+		changed = 1;
+	}
+
+	return changed;
+}
+
+static int cx2072x_plbk_eq_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = CX2072X_PLBK_EQ_COEF_LEN;
+	return 0;
+}
+
+static int cx2072x_plbk_eq_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	struct cx2072x_eq_ctrl *eq =
+		(struct cx2072x_eq_ctrl *)kcontrol->private_value;
+	u8 *param = ucontrol->value.bytes.data;
+	u8 *cache = cx2072x->plbk_eq[eq->ch][eq->band];
+
+	memcpy(param, cache, CX2072X_PLBK_EQ_COEF_LEN);
+	return 0;
+}
+
+static int cx2072x_plbk_eq_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	struct cx2072x_eq_ctrl *eq =
+		(struct cx2072x_eq_ctrl *)kcontrol->private_value;
+	u8 *param = ucontrol->value.bytes.data;
+	u8 *cache = cx2072x->plbk_eq[eq->ch][eq->band];
+	int changed = 0;
+
+	mutex_lock(&cx2072x->eq_coeff_lock);
+
+	/* do nothing if the value is the same */
+	if (memcmp(cache, param, CX2072X_PLBK_EQ_COEF_LEN))
+		goto unlock;
+
+	memcpy(cache, param, CX2072X_PLBK_EQ_COEF_LEN);
+
+	cx2072x->plbk_eq_changed = true;
+	cx2072x->plbk_eq_channel = eq->ch;
+	cx2072x_update_dsp(codec);
+	changed = 1;
+ unlock:
+	mutex_unlock(&cx2072x->eq_coeff_lock);
+	return changed;
+}
+
+static int cx2072x_classd_level_info(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = CX2072X_CLASSD_AMP_LEN;
+	return 0;
+}
+
+static int cx2072x_classd_level_get(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	u8 *param = ucontrol->value.bytes.data;
+	u8 *cache = cx2072x->classd_amp;
+
+	memcpy(param, cache, CX2072X_CLASSD_AMP_LEN);
+	return 0;
+}
+
+static int cx2072x_classd_level_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	u8 *param = ucontrol->value.bytes.data;
+	u8 *cache = cx2072x->classd_amp;
+
+	memcpy(cache, param, CX2072X_CLASSD_AMP_LEN);
+
+	/* Config Power Averaging */
+	cx2072x_reg_bulk_write(codec, CX2072X_ANALOG_TEST10,
+			       &cx2072x->classd_amp[0], 2);
+	cx2072x_reg_bulk_write(codec, CX2072X_CODEC_TEST20,
+			       &cx2072x->classd_amp[2], 2);
+	cx2072x_reg_bulk_write(codec, CX2072X_CODEC_TEST26,
+			       &cx2072x->classd_amp[4], 2);
+	return 0;
+}
+
+static int cx2072x_plbk_drc_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = CX2072X_PLBK_DRC_PARM_LEN;
+	return 0;
+}
+
+static int cx2072x_plbk_drc_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	u8 *param = ucontrol->value.bytes.data;
+	u8 *cache = cx2072x->plbk_drc;
+
+	memcpy(param, cache, CX2072X_PLBK_DRC_PARM_LEN);
+	return 0;
+}
+
+static int cx2072x_plbk_drc_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	u8 *param = ucontrol->value.bytes.data;
+	u8 *cache = cx2072x->plbk_drc;
+
+	memcpy(cache, param, CX2072X_PLBK_DRC_PARM_LEN);
+
+	cx2072x->plbk_drc_changed = true;
+	cx2072x_update_dsp(codec);
+	return 0;
+}
+
+#define CX2072X_PLBK_DRC_COEF(xname)			\
+	{						\
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	\
+		.name = xname,				\
+		.info = cx2072x_plbk_drc_info,		\
+		.get = cx2072x_plbk_drc_get,		\
+		.put = cx2072x_plbk_drc_put		\
+	}
+
+#define CX2072X_PLBK_EQ_COEF(xname, xch, xband)				\
+	{								\
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,			\
+		.name = xname,						\
+		.info = cx2072x_plbk_eq_info,				\
+		.get = cx2072x_plbk_eq_get,				\
+		.put = cx2072x_plbk_eq_put,				\
+		.private_value =					\
+			(unsigned long)&(struct cx2072x_eq_ctrl)	\
+			{.ch = xch, .band = xband }			\
+	}
+
+#define CX2072X_PLBK_DSP_EQ_SWITCH(xname)		\
+	{						\
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	\
+		.name = (xname),			\
+		.info = cx2072x_plbk_eq_en_info,	\
+		.get = cx2072x_plbk_eq_en_get,		\
+		.put = cx2072x_plbk_eq_en_put		\
+	}
+
+#define CX2072X_PLBK_DSP_DRC_SWITCH(xname)		\
+	{						\
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	\
+		.name = (xname),			\
+		.info = cx2072x_plbk_drc_en_info,	\
+		.get = cx2072x_plbk_drc_en_get,		\
+		.put = cx2072x_plbk_drc_en_put		\
+	}
+
+#define CX2072X_CLASSD_LEVEL(xname)			\
+	{						\
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	\
+		.name = (xname),			\
+		.info = cx2072x_classd_level_info,	\
+		.get = cx2072x_classd_level_get,	\
+		.put = cx2072x_classd_level_put		\
+	}
+
+static const struct snd_kcontrol_new cx2072x_snd_controls[] = {
+	SOC_DOUBLE_R_TLV("PortD Boost Volume", CX2072X_PORTD_GAIN_LEFT,
+			 CX2072X_PORTD_GAIN_RIGHT, 0, 3, 0, boost_tlv),
+	SOC_DOUBLE_R_TLV("PortC Boost Volume", CX2072X_PORTC_GAIN_LEFT,
+			 CX2072X_PORTC_GAIN_RIGHT, 0, 3, 0, boost_tlv),
+	SOC_DOUBLE_R_TLV("PortB Boost Volume", CX2072X_PORTB_GAIN_LEFT,
+			 CX2072X_PORTB_GAIN_RIGHT, 0, 3, 0, boost_tlv),
+	SOC_DOUBLE_R_TLV("PortD ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_1,
+			 CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0, 0x4a, 0, adc_tlv),
+	SOC_DOUBLE_R_TLV("PortC ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_2,
+			 CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0, 0x4a, 0, adc_tlv),
+	SOC_DOUBLE_R_TLV("PortB ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_0,
+			 CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0, 0x4a, 0, adc_tlv),
+	SOC_DOUBLE_R_TLV("DAC1 Volume", CX2072X_DAC1_AMP_GAIN_LEFT,
+			 CX2072X_DAC1_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv),
+	SOC_DOUBLE_R("DAC1 Mute Switch", CX2072X_DAC1_AMP_GAIN_LEFT,
+		     CX2072X_DAC1_AMP_GAIN_RIGHT, 7,  1, 0),
+	SOC_DOUBLE_R_TLV("DAC2 Volume", CX2072X_DAC2_AMP_GAIN_LEFT,
+			 CX2072X_DAC2_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv),
+	CX2072X_PLBK_DSP_EQ_SWITCH("EQ Switch"),
+	CX2072X_PLBK_DSP_DRC_SWITCH("DRC Switch"),
+
+	CX2072X_PLBK_EQ_COEF("DACL EQ 0", 0, 0),
+	CX2072X_PLBK_EQ_COEF("DACL EQ 1", 0, 1),
+	CX2072X_PLBK_EQ_COEF("DACL EQ 2", 0, 2),
+	CX2072X_PLBK_EQ_COEF("DACL EQ 3", 0, 3),
+	CX2072X_PLBK_EQ_COEF("DACL EQ 4", 0, 4),
+	CX2072X_PLBK_EQ_COEF("DACL EQ 5", 0, 5),
+	CX2072X_PLBK_EQ_COEF("DACL EQ 6", 0, 6),
+	CX2072X_PLBK_EQ_COEF("DACR EQ 0", 1, 0),
+	CX2072X_PLBK_EQ_COEF("DACR EQ 1", 1, 1),
+	CX2072X_PLBK_EQ_COEF("DACR EQ 2", 1, 2),
+	CX2072X_PLBK_EQ_COEF("DACR EQ 3", 1, 3),
+	CX2072X_PLBK_EQ_COEF("DACR EQ 4", 1, 4),
+	CX2072X_PLBK_EQ_COEF("DACR EQ 5", 1, 5),
+	CX2072X_PLBK_EQ_COEF("DACR EQ 6", 1, 6),
+	CX2072X_PLBK_DRC_COEF("DRC"),
+	SOC_SINGLE_TLV("HPF Freq", CX2072X_CODEC_TEST9, 0, 0x3f, 0, hpf_tlv),
+	SOC_DOUBLE("HPF Switch", CX2072X_CODEC_TEST9, 8, 9, 1, 1),
+	CX2072X_CLASSD_LEVEL("Class-D Output Level"),
+	SOC_SINGLE("PortA HP Amp Switch", CX2072X_PORTA_PIN_CTRL, 7, 1, 0),
+};
+
+/**
+ * snd_soc_cx2072x_enable_jack_detect - Enable CX2072X jack detection
+ * @codec: codec object
+ */
+int snd_soc_cx2072x_enable_jack_detect(struct snd_soc_component *codec)
+{
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
+
+	/* No-sticky input type */
+	regmap_write(cx2072x->regmap, CX2072X_GPIO_STICKY_MASK, 0x1f);
+
+	/* Use GPOI0 as interrupt pin */
+	regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);
+
+	/* Enables unsolitited message on PortA */
+	regmap_write(cx2072x->regmap, CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x80);
+
+	/* support both nokia and apple headset set. Monitor time = 275 ms */
+	regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST15, 0x73);
+
+	/* Disable TIP detection */
+	regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST12, 0x300);
+
+	/* Switch MusicD3Live pin to GPIO */
+	regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST1, 0);
+
+	snd_soc_dapm_mutex_lock(dapm);
+
+	snd_soc_dapm_force_enable_pin_unlocked(dapm, "PORTD");
+	snd_soc_dapm_force_enable_pin_unlocked(dapm, "Headset Bias");
+	snd_soc_dapm_force_enable_pin_unlocked(dapm, "PortD Mic Bias");
+
+	snd_soc_dapm_mutex_unlock(dapm);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_cx2072x_enable_jack_detect);
+
+/**
+ * snd_soc_cx2072x_get_jack_state: Return current jack state.
+ * @codec: codec object
+ */
+int snd_soc_cx2072x_get_jack_state(struct snd_soc_component *codec)
+{
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	unsigned int jack;
+	unsigned int type = 0;
+	int state = 0;
+	bool need_cache_bypass =
+		snd_soc_component_get_bias_level(codec) == SND_SOC_BIAS_OFF;
+
+	if (need_cache_bypass)
+		regcache_cache_only(cx2072x->regmap, false);
+
+	cx2072x->jack_state = CX2072X_JACK_NONE;
+	regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack);
+	jack = jack >> 24;
+	regmap_read(cx2072x->regmap, CX2072X_DIGITAL_TEST11, &type);
+
+	if (need_cache_bypass)
+		regcache_cache_only(cx2072x->regmap, true);
+
+	if (jack == 0x80) {
+		type = type >> 8;
+
+		if (type & 0x8) {
+			state |= SND_JACK_HEADSET;
+			cx2072x->jack_state = CX2072X_JACK_APPLE_HEADSET;
+			if (type & 0x2)
+				state |= SND_JACK_BTN_0;
+		} else if (type & 0x4) {
+			state |= SND_JACK_HEADPHONE;
+			cx2072x->jack_state = CX2072X_JACK_NOKIE_HEADSET;
+		} else {
+			state |= SND_JACK_HEADPHONE;
+			cx2072x->jack_state = CX2072X_JACK_HEADPHONE;
+		}
+	}
+
+	/* clear interrupt */
+	regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);
+
+	dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n",
+		type, state);
+	return state;
+}
+EXPORT_SYMBOL_GPL(snd_soc_cx2072x_get_jack_state);
+
+static int cx2072x_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *codec = dai->component;
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	struct device *dev = codec->dev;
+	const unsigned int sample_rate = params_rate(params);
+	int sample_size, frame_size;
+
+	/* Data sizes if not using TDM */
+	sample_size = params_width(params);
+
+	if (sample_size < 0)
+		return sample_size;
+
+	frame_size = snd_soc_params_to_frame_size(params);
+	if (frame_size < 0)
+		return frame_size;
+
+	if (cx2072x->mclk_rate == 0) {
+		dev_err(dev, "Master clock rate is not configued\n");
+		return -EINVAL;
+	}
+
+	if (cx2072x->bclk_ratio)
+		frame_size = cx2072x->bclk_ratio;
+
+	switch (sample_rate) {
+	case 48000:
+	case 32000:
+	case 24000:
+	case 16000:
+	case 96000:
+	case 192000:
+		break;
+
+	default:
+		dev_err(dev, "Unsupported sample rate %d\n", sample_rate);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "Sample size %d bits, frame = %d bits, rate = %d Hz\n",
+		sample_size, frame_size, sample_rate);
+
+	cx2072x->frame_size = frame_size;
+	cx2072x->sample_size = sample_size;
+	cx2072x->sample_rate = sample_rate;
+
+	if (dai->id == CX2072X_DAI_DSP) {
+		cx2072x->en_aec_ref = true;
+		dev_dbg(cx2072x->dev, "enables aec reference\n");
+		regmap_write(cx2072x->regmap,
+			     CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 3);
+	}
+
+	if (cx2072x->pll_changed) {
+		cx2072x_config_pll(cx2072x);
+		cx2072x->pll_changed = false;
+	}
+
+	if (cx2072x->i2spcm_changed) {
+		cx2072x_config_i2spcm(cx2072x);
+		cx2072x->i2spcm_changed = false;
+	}
+
+	return 0;
+}
+
+static void cx2072x_shutdown(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *codec = dai->component;
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+	/* shutdown codec */
+	regcache_cache_only(cx2072x->regmap, false);
+	regmap_write(cx2072x->regmap, CX2072X_PORTA_POWER_STATE, 3);
+	regmap_write(cx2072x->regmap, CX2072X_PORTB_POWER_STATE, 3);
+	regmap_write(cx2072x->regmap, CX2072X_PORTC_POWER_STATE, 3);
+	regmap_write(cx2072x->regmap, CX2072X_PORTD_POWER_STATE, 3);
+	regmap_write(cx2072x->regmap, CX2072X_PORTE_POWER_STATE, 3);
+	regmap_write(cx2072x->regmap, CX2072X_PORTG_POWER_STATE, 3);
+	regmap_write(cx2072x->regmap, CX2072X_MIXER_POWER_STATE, 3);
+	regmap_write(cx2072x->regmap, CX2072X_ADC1_POWER_STATE, 3);
+	regmap_write(cx2072x->regmap, CX2072X_ADC2_POWER_STATE, 3);
+	regmap_write(cx2072x->regmap, CX2072X_DAC1_POWER_STATE, 3);
+	regmap_write(cx2072x->regmap, CX2072X_DAC2_POWER_STATE, 3);
+
+	snd_soc_component_force_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int cx2072x_set_dai_bclk_ratio(struct snd_soc_dai *dai,
+				      unsigned int ratio)
+{
+	struct snd_soc_component *codec = dai->component;
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+	cx2072x->bclk_ratio = ratio;
+	return 0;
+}
+
+static int cx2072x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+				  unsigned int freq, int dir)
+{
+	struct snd_soc_component *codec = dai->component;
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+	if (cx2072x->mclk && clk_set_rate(cx2072x->mclk, freq)) {
+		dev_err(codec->dev, "set clk rate failed\n");
+		return -EINVAL;
+	}
+
+	cx2072x->mclk_rate = freq;
+	return 0;
+}
+
+static int cx2072x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_component *codec = dai->component;
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	struct device *dev = codec->dev;
+
+	dev_dbg(dev, "set_dai_fmt- %08x\n", fmt);
+	/* set master/slave */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+
+	default:
+		dev_err(dev, "Unsupported DAI master mode\n");
+		return -EINVAL;
+	}
+
+	/* set format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_RIGHT_J:
+	case SND_SOC_DAIFMT_LEFT_J:
+		break;
+
+	default:
+		dev_err(dev, "Unsupported DAI format\n");
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+	case SND_SOC_DAIFMT_IB_IF:
+	case SND_SOC_DAIFMT_IB_NF:
+	case SND_SOC_DAIFMT_NB_IF:
+		break;
+
+	default:
+		dev_err(dev, "Unsupported DAI clock inversion\n");
+		return -EINVAL;
+	}
+
+	cx2072x->dai_fmt = fmt;
+	return 0;
+}
+
+static const struct snd_kcontrol_new portaouten_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_PORTA_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new porteouten_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_PORTE_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new portgouten_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_PORTG_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new portmouten_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_PORTM_PIN_CTRL, 6, 1, 0);
+
+static const struct snd_kcontrol_new portbinen_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_PORTB_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new portcinen_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_PORTC_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new portdinen_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_PORTD_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new porteinen_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_PORTE_PIN_CTRL, 5, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc1l_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 0, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc1r_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 1, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc2l_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 2, 1, 0);
+
+static const struct snd_kcontrol_new i2sadc2r_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 3, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac1l_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 0, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac1r_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 1, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac2l_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 2, 1, 0);
+
+static const struct snd_kcontrol_new i2sdac2r_ctl =
+	SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 3, 1, 0);
+
+static const char * const dac_enum_text[] = {
+	"DAC1 Switch", "DAC2 Switch",
+};
+
+static const struct soc_enum porta_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new porta_mux =
+SOC_DAPM_ENUM("PortA Mux", porta_dac_enum);
+
+static const struct soc_enum portg_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new portg_mux =
+SOC_DAPM_ENUM("PortG Mux", portg_dac_enum);
+
+static const struct soc_enum porte_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new porte_mux =
+SOC_DAPM_ENUM("PortE Mux", porte_dac_enum);
+
+static const struct soc_enum portm_dac_enum =
+SOC_ENUM_SINGLE(CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
+
+static const struct snd_kcontrol_new portm_mux =
+SOC_DAPM_ENUM("PortM Mux", portm_dac_enum);
+
+static const char * const adc1in_sel_text[] = {
+	"PortB Switch", "PortD Switch", "PortC Switch", "Widget15 Switch",
+	"PortE Switch", "PortF Switch", "PortH Switch"
+};
+
+static const struct soc_enum adc1in_sel_enum =
+SOC_ENUM_SINGLE(CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0, 7, adc1in_sel_text);
+
+static const struct snd_kcontrol_new adc1_mux =
+SOC_DAPM_ENUM("ADC1 Mux", adc1in_sel_enum);
+
+static const char * const adc2in_sel_text[] = {
+	"PortC Switch", "Widget15 Switch", "PortH Switch"
+};
+
+static const struct soc_enum adc2in_sel_enum =
+SOC_ENUM_SINGLE(CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0, 3, adc2in_sel_text);
+
+static const struct snd_kcontrol_new adc2_mux =
+SOC_DAPM_ENUM("ADC2 Mux", adc2in_sel_enum);
+
+static const struct snd_kcontrol_new wid15_mix[] = {
+	SOC_DAPM_SINGLE("DAC1L Switch", CX2072X_MIXER_GAIN_LEFT_0, 7, 1, 1),
+	SOC_DAPM_SINGLE("DAC1R Switch", CX2072X_MIXER_GAIN_RIGHT_0, 7, 1, 1),
+	SOC_DAPM_SINGLE("DAC2L Switch", CX2072X_MIXER_GAIN_LEFT_1, 7, 1, 1),
+	SOC_DAPM_SINGLE("DAC2R Switch", CX2072X_MIXER_GAIN_RIGHT_1, 7, 1, 1),
+};
+
+#define CX2072X_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, wmask,  won_val, \
+	woff_val, wevent, wflags) \
+	{.id = snd_soc_dapm_supply, .name = wname, .kcontrol_news = NULL, \
+	.num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
+	.on_val = won_val, .off_val = woff_val, \
+	.subseq = wsubseq, .event = wevent, .event_flags = wflags}
+
+#define CX2072X_DAPM_SWITCH(wname,  wreg, wshift, wmask,  won_val, woff_val, \
+	wevent, wflags) \
+	{.id = snd_soc_dapm_switch, .name = wname, .kcontrol_news = NULL, \
+	.num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
+	.on_val = won_val, .off_val = woff_val, \
+	.event = wevent, .event_flags = wflags}
+
+#define CX2072X_DAPM_SWITCH(wname,  wreg, wshift, wmask,  won_val, woff_val, \
+	wevent, wflags) \
+	{.id = snd_soc_dapm_switch, .name = wname, .kcontrol_news = NULL, \
+	.num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
+	.on_val = won_val, .off_val = woff_val, \
+	.event = wevent, .event_flags = wflags}
+
+#define CX2072X_DAPM_REG_E(wid, wname, wreg, wshift, wmask, won_val, woff_val, \
+				wevent, wflags) \
+	{.id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
+	.reg = wreg, .shift = wshift, .mask = wmask, \
+	.on_val = won_val, .off_val = woff_val, \
+	.event = wevent, .event_flags = wflags}
+
+static const struct snd_soc_dapm_widget cx2072x_dapm_widgets[] = {
+	/*Playback*/
+	SND_SOC_DAPM_AIF_IN("In AIF", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_SWITCH("I2S DAC1L", SND_SOC_NOPM, 0, 0, &i2sdac1l_ctl),
+	SND_SOC_DAPM_SWITCH("I2S DAC1R", SND_SOC_NOPM, 0, 0, &i2sdac1r_ctl),
+	SND_SOC_DAPM_SWITCH("I2S DAC2L", SND_SOC_NOPM, 0, 0, &i2sdac2l_ctl),
+	SND_SOC_DAPM_SWITCH("I2S DAC2R", SND_SOC_NOPM, 0, 0, &i2sdac2r_ctl),
+
+	SND_SOC_DAPM_REG(snd_soc_dapm_dac, "DAC1", CX2072X_DAC1_POWER_STATE,
+			 0, 0xFFF, 0x00, 0x03),
+
+	SND_SOC_DAPM_REG(snd_soc_dapm_dac, "DAC2", CX2072X_DAC2_POWER_STATE,
+			 0, 0xFFF, 0x00, 0x03),
+
+	SND_SOC_DAPM_MUX("PortA Mux", SND_SOC_NOPM, 0, 0, &porta_mux),
+	SND_SOC_DAPM_MUX("PortG Mux", SND_SOC_NOPM, 0, 0, &portg_mux),
+	SND_SOC_DAPM_MUX("PortE Mux", SND_SOC_NOPM, 0, 0, &porte_mux),
+	SND_SOC_DAPM_MUX("PortM Mux", SND_SOC_NOPM, 0, 0, &portm_mux),
+
+	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortA Power",
+			 CX2072X_PORTA_POWER_STATE, 0, 0XFFF, 0X00, 0X03),
+
+	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortM Power",
+			 CX2072X_PORTM_POWER_STATE, 0, 0XFFF, 0X00, 0X03),
+
+	CX2072X_DAPM_SUPPLY_S("PortG Power", 1, CX2072X_PORTG_POWER_STATE, 0,
+			      0xFF, 0x00, 0x03, portg_power_ev,
+			      SND_SOC_DAPM_POST_PMU),
+
+	CX2072X_DAPM_SUPPLY_S("AFG Power", 0, CX2072X_AFG_POWER_STATE,
+			      0, 0xFFF, 0x00, 0x03, afg_power_ev,
+			      SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_SWITCH("PortA Out En", SND_SOC_NOPM, 0, 0,
+			    &portaouten_ctl),
+	SND_SOC_DAPM_SWITCH("PortE Out En", SND_SOC_NOPM, 0, 0,
+			    &porteouten_ctl),
+	SND_SOC_DAPM_SWITCH("PortG Out En", SND_SOC_NOPM, 0, 0,
+			    &portgouten_ctl),
+	SND_SOC_DAPM_SWITCH("PortM Out En", SND_SOC_NOPM, 0, 0,
+			    &portmouten_ctl),
+
+	SND_SOC_DAPM_OUTPUT("PORTA"),
+	SND_SOC_DAPM_OUTPUT("PORTG"),
+	SND_SOC_DAPM_OUTPUT("PORTE"),
+	SND_SOC_DAPM_OUTPUT("PORTM"),
+	SND_SOC_DAPM_OUTPUT("AEC REF"),
+
+	/*Capture*/
+	SND_SOC_DAPM_AIF_OUT("Out AIF", "Capture", 0, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_SWITCH("I2S ADC1L", SND_SOC_NOPM, 0, 0, &i2sadc1l_ctl),
+	SND_SOC_DAPM_SWITCH("I2S ADC1R", SND_SOC_NOPM, 0, 0, &i2sadc1r_ctl),
+	SND_SOC_DAPM_SWITCH("I2S ADC2L", SND_SOC_NOPM, 0, 0, &i2sadc2l_ctl),
+	SND_SOC_DAPM_SWITCH("I2S ADC2R", SND_SOC_NOPM, 0, 0, &i2sadc2r_ctl),
+
+	SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC1", CX2072X_ADC1_POWER_STATE,
+			 0, 0xFF, 0x00, 0x03),
+	SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC2", CX2072X_ADC2_POWER_STATE,
+			 0, 0xFF, 0x00, 0x03),
+
+	SND_SOC_DAPM_MUX("ADC1 Mux", SND_SOC_NOPM, 0, 0, &adc1_mux),
+	SND_SOC_DAPM_MUX("ADC2 Mux", SND_SOC_NOPM, 0, 0, &adc2_mux),
+
+	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortB Power",
+			 CX2072X_PORTB_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
+	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortC Power",
+			 CX2072X_PORTC_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
+	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortD Power",
+			 CX2072X_PORTD_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
+	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortE Power",
+			 CX2072X_PORTE_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
+	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Widget15 Power",
+			 CX2072X_MIXER_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
+
+	SND_SOC_DAPM_MIXER("Widget15 Mixer", SND_SOC_NOPM, 0, 0,
+			   wid15_mix, ARRAY_SIZE(wid15_mix)),
+	SND_SOC_DAPM_SWITCH("PortB In En", SND_SOC_NOPM, 0, 0, &portbinen_ctl),
+	SND_SOC_DAPM_SWITCH("PortC In En", SND_SOC_NOPM, 0, 0, &portcinen_ctl),
+	SND_SOC_DAPM_SWITCH("PortD In En", SND_SOC_NOPM, 0, 0, &portdinen_ctl),
+	SND_SOC_DAPM_SWITCH("PortE In En", SND_SOC_NOPM, 0, 0, &porteinen_ctl),
+
+	SND_SOC_DAPM_MICBIAS("Headset Bias", CX2072X_ANALOG_TEST11, 1, 0),
+	SND_SOC_DAPM_MICBIAS("PortB Mic Bias", CX2072X_PORTB_PIN_CTRL, 2, 0),
+	SND_SOC_DAPM_MICBIAS("PortD Mic Bias", CX2072X_PORTD_PIN_CTRL, 2, 0),
+	SND_SOC_DAPM_MICBIAS("PortE Mic Bias", CX2072X_PORTE_PIN_CTRL, 2, 0),
+	SND_SOC_DAPM_INPUT("PORTB"),
+	SND_SOC_DAPM_INPUT("PORTC"),
+	SND_SOC_DAPM_INPUT("PORTD"),
+	SND_SOC_DAPM_INPUT("PORTEIN"),
+
+};
+
+static const struct snd_soc_dapm_route cx2072x_intercon[] = {
+	/* Playback */
+	{"In AIF", NULL, "AFG Power"},
+	{"I2S DAC1L", "Switch", "In AIF"},
+	{"I2S DAC1R", "Switch", "In AIF"},
+	{"I2S DAC2L", "Switch", "In AIF"},
+	{"I2S DAC2R", "Switch", "In AIF"},
+	{"DAC1", NULL, "I2S DAC1L"},
+	{"DAC1", NULL, "I2S DAC1R"},
+	{"DAC2", NULL, "I2S DAC2L"},
+	{"DAC2", NULL, "I2S DAC2R"},
+	{"PortA Mux", "DAC1 Switch", "DAC1"},
+	{"PortA Mux", "DAC2 Switch", "DAC2"},
+	{"PortG Mux", "DAC1 Switch", "DAC1"},
+	{"PortG Mux", "DAC2 Switch", "DAC2"},
+	{"PortE Mux", "DAC1 Switch", "DAC1"},
+	{"PortE Mux", "DAC2 Switch", "DAC2"},
+	{"PortM Mux", "DAC1 Switch", "DAC1"},
+	{"PortM Mux", "DAC2 Switch", "DAC2"},
+	{"Widget15 Mixer", "DAC1L Switch", "DAC1"},
+	{"Widget15 Mixer", "DAC1R Switch", "DAC2"},
+	{"Widget15 Mixer", "DAC2L Switch", "DAC1"},
+	{"Widget15 Mixer", "DAC2R Switch", "DAC2"},
+	{"Widget15 Mixer", NULL, "Widget15 Power"},
+	{"PortA Out En", "Switch", "PortA Mux"},
+	{"PortG Out En", "Switch", "PortG Mux"},
+	{"PortE Out En", "Switch", "PortE Mux"},
+	{"PortM Out En", "Switch", "PortM Mux"},
+	{"PortA Mux", NULL, "PortA Power"},
+	{"PortG Mux", NULL, "PortG Power"},
+	{"PortE Mux", NULL, "PortE Power"},
+	{"PortM Mux", NULL, "PortM Power"},
+	{"PortA Out En", NULL, "PortA Power"},
+	{"PortG Out En", NULL, "PortG Power"},
+	{"PortE Out En", NULL, "PortE Power"},
+	{"PortM Out En", NULL, "PortM Power"},
+	{"PORTA", NULL, "PortA Out En"},
+	{"PORTG", NULL, "PortG Out En"},
+	{"PORTE", NULL, "PortE Out En"},
+	{"PORTM", NULL, "PortM Out En"},
+
+	/* Capture */
+	{"PORTD", NULL, "Headset Bias"},
+	{"PortB In En", "Switch", "PORTB"},
+	{"PortC In En", "Switch", "PORTC"},
+	{"PortD In En", "Switch", "PORTD"},
+	{"PortE In En", "Switch", "PORTEIN"},
+	{"ADC1 Mux", "PortB Switch", "PortB In En"},
+	{"ADC1 Mux", "PortC Switch", "PortC In En"},
+	{"ADC1 Mux", "PortD Switch", "PortD In En"},
+	{"ADC1 Mux", "PortE Switch", "PortE In En"},
+	{"ADC1 Mux", "Widget15 Switch", "Widget15 Mixer"},
+	{"ADC2 Mux", "PortC Switch", "PortC In En"},
+	{"ADC2 Mux", "Widget15 Switch", "Widget15 Mixer"},
+	{"ADC1", NULL, "ADC1 Mux"},
+	{"ADC2", NULL, "ADC2 Mux"},
+	{"I2S ADC1L", "Switch", "ADC1"},
+	{"I2S ADC1R", "Switch", "ADC1"},
+	{"I2S ADC2L", "Switch", "ADC2"},
+	{"I2S ADC2R", "Switch", "ADC2"},
+	{"Out AIF", NULL, "I2S ADC1L"},
+	{"Out AIF", NULL, "I2S ADC1R"},
+	{"Out AIF", NULL, "I2S ADC2L"},
+	{"Out AIF", NULL, "I2S ADC2R"},
+	{"Out AIF", NULL, "AFG Power"},
+	{"AEC REF", NULL, "Out AIF"},
+	{"PortB In En", NULL, "PortB Power"},
+	{"PortC In En", NULL, "PortC Power"},
+	{"PortD In En", NULL, "PortD Power"},
+	{"PortE In En", NULL, "PortE Power"},
+};
+
+static void cx2072x_sw_reset(struct cx2072x_priv *cx2072x)
+{
+	regmap_write(cx2072x->regmap, CX2072X_AFG_FUNCTION_RESET, 0x01);
+	regmap_write(cx2072x->regmap, CX2072X_AFG_FUNCTION_RESET, 0x01);
+}
+
+static int cx2072x_init(struct snd_soc_component *codec)
+{
+	int ch, band;
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+
+	regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 0);
+
+	/* configre PortC as input device */
+	regmap_update_bits(cx2072x->regmap, CX2072X_PORTC_PIN_CTRL,
+			   0x20, 0x20);
+
+	cx2072x->plbk_eq_changed = true;
+	cx2072x->plbk_drc_changed = true;
+
+	/* use flat eq by default */
+	for (ch = 0 ; ch < 2 ; ch++) {
+		for (band = 0; band < CX2072X_PLBK_EQ_BAND_NUM; band++) {
+			cx2072x->plbk_eq[ch][band][1] = 64;
+			cx2072x->plbk_eq[ch][band][10] = 3;
+		}
+	}
+
+	regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
+			   0x84, 0xFF);
+
+	return 0;
+}
+
+static int cx2072x_set_bias_level(struct snd_soc_component *codec,
+				  enum snd_soc_bias_level level)
+{
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	const enum snd_soc_bias_level old_level =
+		 snd_soc_component_get_bias_level(codec);
+	int ret;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (old_level == SND_SOC_BIAS_OFF) {
+			if (cx2072x->mclk) {
+				dev_dbg(cx2072x->dev,
+					"Turn on MCLK with rate %d\n",
+					cx2072x->mclk_rate);
+				ret = clk_prepare_enable(cx2072x->mclk);
+				if (ret)
+					return ret;
+			}
+			regcache_cache_only(cx2072x->regmap, false);
+			regmap_write(cx2072x->regmap,
+				     CX2072X_AFG_POWER_STATE, 0);
+			regcache_sync(cx2072x->regmap);
+		}
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		if (old_level != SND_SOC_BIAS_OFF) {
+			/* Shutdown codec completely */
+			cx2072x_sw_reset(cx2072x);
+			regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 3);
+			regcache_mark_dirty(cx2072x->regmap);
+			regcache_cache_only(cx2072x->regmap, true);
+			cx2072x->plbk_eq_changed = true;
+			cx2072x->plbk_drc_changed = true;
+			if (cx2072x->mclk) {
+				/* delayed mclk shutdown for 200ms */
+				mdelay(200);
+				clk_disable_unprepare(cx2072x->mclk);
+			}
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int cx2072x_probe(struct snd_soc_component *codec)
+{
+	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
+	int ret;
+	unsigned int ven_id;
+
+	cx2072x->codec = codec;
+
+	regmap_read(cx2072x->regmap, CX2072X_VENDOR_ID, &ven_id);
+	regmap_read(cx2072x->regmap, CX2072X_REVISION_ID, &cx2072x->rev_id);
+
+	dev_info(codec->dev, "codec version: %08x,%08x\n",
+		 ven_id, cx2072x->rev_id);
+
+	/*
+	 * Check if MCLK is specified, if not the clock is control by machine
+	 * driver
+	 */
+	cx2072x->mclk = devm_clk_get(codec->dev, "mclk");
+	if (IS_ERR(cx2072x->mclk)) {
+		ret = PTR_ERR(cx2072x->mclk);
+		if (ret == -ENOENT) {
+			if (IS_ERR(cx2072x->mclk)) {
+				dev_warn(codec->dev, "Assuming static MCLK\n");
+				cx2072x->mclk = NULL;
+				ret = 0;
+			}
+		} else {
+			dev_err(codec->dev, "Failed to get MCLK: %d\n", ret);
+			return ret;
+		}
+	}
+
+	dev_dbg(codec->dev, "Initialize codec\n");
+
+	/* enable clock for codec access */
+	if (cx2072x->mclk)
+		ret = clk_prepare_enable(cx2072x->mclk);
+
+	cx2072x_init(codec);
+
+	ret = regmap_register_patch(cx2072x->regmap, cx2072x_patch,
+				    ARRAY_SIZE(cx2072x_patch));
+	if (ret)
+		return ret;
+	regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 3);
+	regcache_cache_only(cx2072x->regmap, true);
+
+	/* disable clock */
+	if (cx2072x->mclk)
+		clk_disable_unprepare(cx2072x->mclk);
+
+	return ret;
+}
+
+static void cx2072x_remove(struct snd_soc_component *codec)
+{
+	/* power off device */
+	snd_soc_component_force_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static bool cx2072x_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CX2072X_VENDOR_ID:
+	case CX2072X_REVISION_ID:
+	case CX2072X_CURRENT_BCLK_FREQUENCY:
+	case CX2072X_AFG_POWER_STATE:
+	case CX2072X_UM_RESPONSE:
+	case CX2072X_GPIO_DATA:
+	case CX2072X_GPIO_ENABLE:
+	case CX2072X_GPIO_DIRECTION:
+	case CX2072X_GPIO_WAKE:
+	case CX2072X_GPIO_UM_ENABLE:
+	case CX2072X_GPIO_STICKY_MASK:
+	case CX2072X_DAC1_CONVERTER_FORMAT:
+	case CX2072X_DAC1_AMP_GAIN_RIGHT:
+	case CX2072X_DAC1_AMP_GAIN_LEFT:
+	case CX2072X_DAC1_POWER_STATE:
+	case CX2072X_DAC1_CONVERTER_STREAM_CHANNEL:
+	case CX2072X_DAC1_EAPD_ENABLE:
+	case CX2072X_DAC2_CONVERTER_FORMAT:
+	case CX2072X_DAC2_AMP_GAIN_RIGHT:
+	case CX2072X_DAC2_AMP_GAIN_LEFT:
+	case CX2072X_DAC2_POWER_STATE:
+	case CX2072X_DAC2_CONVERTER_STREAM_CHANNEL:
+	case CX2072X_ADC1_CONVERTER_FORMAT:
+	case CX2072X_ADC1_AMP_GAIN_RIGHT_0:
+	case CX2072X_ADC1_AMP_GAIN_LEFT_0:
+	case CX2072X_ADC1_AMP_GAIN_RIGHT_1:
+	case CX2072X_ADC1_AMP_GAIN_LEFT_1:
+	case CX2072X_ADC1_AMP_GAIN_RIGHT_2:
+	case CX2072X_ADC1_AMP_GAIN_LEFT_2:
+	case CX2072X_ADC1_AMP_GAIN_RIGHT_3:
+	case CX2072X_ADC1_AMP_GAIN_LEFT_3:
+	case CX2072X_ADC1_AMP_GAIN_RIGHT_4:
+	case CX2072X_ADC1_AMP_GAIN_LEFT_4:
+	case CX2072X_ADC1_AMP_GAIN_RIGHT_5:
+	case CX2072X_ADC1_AMP_GAIN_LEFT_5:
+	case CX2072X_ADC1_AMP_GAIN_RIGHT_6:
+	case CX2072X_ADC1_AMP_GAIN_LEFT_6:
+	case CX2072X_ADC1_CONNECTION_SELECT_CONTROL:
+	case CX2072X_ADC1_POWER_STATE:
+	case CX2072X_ADC1_CONVERTER_STREAM_CHANNEL:
+	case CX2072X_ADC2_CONVERTER_FORMAT:
+	case CX2072X_ADC2_AMP_GAIN_RIGHT_0:
+	case CX2072X_ADC2_AMP_GAIN_LEFT_0:
+	case CX2072X_ADC2_AMP_GAIN_RIGHT_1:
+	case CX2072X_ADC2_AMP_GAIN_LEFT_1:
+	case CX2072X_ADC2_AMP_GAIN_RIGHT_2:
+	case CX2072X_ADC2_AMP_GAIN_LEFT_2:
+	case CX2072X_ADC2_CONNECTION_SELECT_CONTROL:
+	case CX2072X_ADC2_POWER_STATE:
+	case CX2072X_ADC2_CONVERTER_STREAM_CHANNEL:
+	case CX2072X_PORTA_CONNECTION_SELECT_CTRL:
+	case CX2072X_PORTA_POWER_STATE:
+	case CX2072X_PORTA_PIN_CTRL:
+	case CX2072X_PORTA_UNSOLICITED_RESPONSE:
+	case CX2072X_PORTA_PIN_SENSE:
+	case CX2072X_PORTA_EAPD_BTL:
+	case CX2072X_PORTB_POWER_STATE:
+	case CX2072X_PORTB_PIN_CTRL:
+	case CX2072X_PORTB_UNSOLICITED_RESPONSE:
+	case CX2072X_PORTB_PIN_SENSE:
+	case CX2072X_PORTB_EAPD_BTL:
+	case CX2072X_PORTB_GAIN_RIGHT:
+	case CX2072X_PORTB_GAIN_LEFT:
+	case CX2072X_PORTC_POWER_STATE:
+	case CX2072X_PORTC_PIN_CTRL:
+	case CX2072X_PORTC_GAIN_RIGHT:
+	case CX2072X_PORTC_GAIN_LEFT:
+	case CX2072X_PORTD_POWER_STATE:
+	case CX2072X_PORTD_PIN_CTRL:
+	case CX2072X_PORTD_UNSOLICITED_RESPONSE:
+	case CX2072X_PORTD_PIN_SENSE:
+	case CX2072X_PORTD_GAIN_RIGHT:
+	case CX2072X_PORTD_GAIN_LEFT:
+	case CX2072X_PORTE_CONNECTION_SELECT_CTRL:
+	case CX2072X_PORTE_POWER_STATE:
+	case CX2072X_PORTE_PIN_CTRL:
+	case CX2072X_PORTE_UNSOLICITED_RESPONSE:
+	case CX2072X_PORTE_PIN_SENSE:
+	case CX2072X_PORTE_EAPD_BTL:
+	case CX2072X_PORTE_GAIN_RIGHT:
+	case CX2072X_PORTE_GAIN_LEFT:
+	case CX2072X_PORTF_POWER_STATE:
+	case CX2072X_PORTF_PIN_CTRL:
+	case CX2072X_PORTF_UNSOLICITED_RESPONSE:
+	case CX2072X_PORTF_PIN_SENSE:
+	case CX2072X_PORTF_GAIN_RIGHT:
+	case CX2072X_PORTF_GAIN_LEFT:
+	case CX2072X_PORTG_POWER_STATE:
+	case CX2072X_PORTG_PIN_CTRL:
+	case CX2072X_PORTG_CONNECTION_SELECT_CTRL:
+	case CX2072X_PORTG_EAPD_BTL:
+	case CX2072X_PORTM_POWER_STATE:
+	case CX2072X_PORTM_PIN_CTRL:
+	case CX2072X_PORTM_CONNECTION_SELECT_CTRL:
+	case CX2072X_PORTM_EAPD_BTL:
+	case CX2072X_MIXER_POWER_STATE:
+	case CX2072X_MIXER_GAIN_RIGHT_0:
+	case CX2072X_MIXER_GAIN_LEFT_0:
+	case CX2072X_MIXER_GAIN_RIGHT_1:
+	case CX2072X_MIXER_GAIN_LEFT_1:
+	case CX2072X_EQ_ENABLE_BYPASS:
+	case CX2072X_EQ_B0_COEFF:
+	case CX2072X_EQ_B1_COEFF:
+	case CX2072X_EQ_B2_COEFF:
+	case CX2072X_EQ_A1_COEFF:
+	case CX2072X_EQ_A2_COEFF:
+	case CX2072X_EQ_G_COEFF:
+	case CX2072X_SPKR_DRC_ENABLE_STEP:
+	case CX2072X_SPKR_DRC_CONTROL:
+	case CX2072X_SPKR_DRC_TEST:
+	case CX2072X_DIGITAL_BIOS_TEST0:
+	case CX2072X_DIGITAL_BIOS_TEST2:
+	case CX2072X_I2SPCM_CONTROL1:
+	case CX2072X_I2SPCM_CONTROL2:
+	case CX2072X_I2SPCM_CONTROL3:
+	case CX2072X_I2SPCM_CONTROL4:
+	case CX2072X_I2SPCM_CONTROL5:
+	case CX2072X_I2SPCM_CONTROL6:
+	case CX2072X_UM_INTERRUPT_CRTL_E:
+	case CX2072X_CODEC_TEST2:
+	case CX2072X_CODEC_TEST9:
+	case CX2072X_CODEC_TEST20:
+	case CX2072X_CODEC_TEST26:
+	case CX2072X_ANALOG_TEST4:
+	case CX2072X_ANALOG_TEST5:
+	case CX2072X_ANALOG_TEST6:
+	case CX2072X_ANALOG_TEST7:
+	case CX2072X_ANALOG_TEST8:
+	case CX2072X_ANALOG_TEST9:
+	case CX2072X_ANALOG_TEST10:
+	case CX2072X_ANALOG_TEST11:
+	case CX2072X_ANALOG_TEST12:
+	case CX2072X_ANALOG_TEST13:
+	case CX2072X_DIGITAL_TEST0:
+	case CX2072X_DIGITAL_TEST1:
+	case CX2072X_DIGITAL_TEST11:
+	case CX2072X_DIGITAL_TEST12:
+	case CX2072X_DIGITAL_TEST15:
+	case CX2072X_DIGITAL_TEST16:
+	case CX2072X_DIGITAL_TEST17:
+	case CX2072X_DIGITAL_TEST18:
+	case CX2072X_DIGITAL_TEST19:
+	case CX2072X_DIGITAL_TEST20:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool cx2072x_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CX2072X_VENDOR_ID:
+	case CX2072X_REVISION_ID:
+	case CX2072X_UM_INTERRUPT_CRTL_E:
+	case CX2072X_DIGITAL_TEST11:
+	case CX2072X_PORTA_PIN_SENSE:
+	case CX2072X_PORTB_PIN_SENSE:
+	case CX2072X_PORTD_PIN_SENSE:
+	case CX2072X_PORTE_PIN_SENSE:
+	case CX2072X_PORTF_PIN_SENSE:
+	case CX2072X_EQ_G_COEFF:
+	case CX2072X_EQ_BAND:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct snd_soc_component_driver soc_codec_driver_cx2072x = {
+	.probe = cx2072x_probe,
+	.remove = cx2072x_remove,
+	.set_bias_level = cx2072x_set_bias_level,
+	.controls = cx2072x_snd_controls,
+	.num_controls = ARRAY_SIZE(cx2072x_snd_controls),
+	.dapm_widgets = cx2072x_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(cx2072x_dapm_widgets),
+	.dapm_routes = cx2072x_intercon,
+	.num_dapm_routes = ARRAY_SIZE(cx2072x_intercon),
+};
+
+/*
+ * DAI ops
+ */
+static struct snd_soc_dai_ops cx2072x_dai_ops = {
+	.set_sysclk = cx2072x_set_dai_sysclk,
+	.set_fmt = cx2072x_set_dai_fmt,
+	.hw_params = cx2072x_hw_params,
+	.shutdown = cx2072x_shutdown,
+	.set_bclk_ratio = cx2072x_set_dai_bclk_ratio,
+};
+
+static int cx2072x_dsp_dai_probe(struct snd_soc_dai *dai)
+{
+	struct cx2072x_priv *cx2072x =
+		snd_soc_component_get_drvdata(dai->component);
+
+	cx2072x->en_aec_ref = true;
+	return 0;
+}
+
+#define CX2072X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = {
+	{ /* playback and capture */
+		.name = "cx2072x-hifi",
+		.id	= CX2072X_DAI_HIFI,
+		.playback = {
+			.stream_name = "Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = CX2072X_RATES_DSP,
+			.formats = CX2072X_FORMATS,
+		},
+		.capture = {
+			.stream_name = "Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = CX2072X_RATES_DSP,
+			.formats = CX2072X_FORMATS,
+		},
+		.ops = &cx2072x_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{ /* plabayck only, return echo reference to Conexant DSP chip */
+		.name = "cx2072x-dsp",
+		.id	= CX2072X_DAI_DSP,
+		.probe = cx2072x_dsp_dai_probe,
+		.playback = {
+			.stream_name = "Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = CX2072X_RATES_DSP,
+			.formats = CX2072X_FORMATS,
+		},
+		.ops = &cx2072x_dai_ops,
+	},
+	{ /* plabayck only, return echo reference through I2S TX */
+		.name = "cx2072x-aec",
+		.id	= 3,
+		.capture = {
+			.stream_name = "Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = CX2072X_RATES_DSP,
+			.formats = CX2072X_FORMATS,
+		},
+	},
+};
+EXPORT_SYMBOL_GPL(soc_codec_cx2072x_dai);
+
+static const struct regmap_config cx2072x_regmap = {
+	.reg_bits = 16,
+	.val_bits = 32,
+	.max_register = CX2072X_REG_MAX,
+	.reg_defaults = cx2072x_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(cx2072x_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+	.readable_reg = cx2072x_readable_register,
+	.volatile_reg = cx2072x_volatile_register,
+	/* Needs custom READ/WRITE functions for various register lengths */
+	.reg_read = cx2072x_reg_read,
+	.reg_write = cx2072x_reg_write,
+};
+
+static int cx2072x_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct cx2072x_priv *cx2072x;
+
+	cx2072x = devm_kzalloc(&i2c->dev, sizeof(struct cx2072x_priv),
+			       GFP_KERNEL);
+	if (!cx2072x)
+		return -ENOMEM;
+
+	cx2072x->regmap = devm_regmap_init(&i2c->dev, NULL, i2c,
+					   &cx2072x_regmap);
+	if (IS_ERR(cx2072x->regmap))
+		return PTR_ERR(cx2072x->regmap);
+
+	mutex_init(&cx2072x->eq_coeff_lock);
+
+	i2c_set_clientdata(i2c, cx2072x);
+
+	cx2072x->dev = &i2c->dev;
+	cx2072x->pll_changed = true;
+	cx2072x->i2spcm_changed = true;
+	cx2072x->bclk_ratio = 0;
+
+	return snd_soc_register_component(cx2072x->dev,
+					  &soc_codec_driver_cx2072x,
+					  soc_codec_cx2072x_dai,
+					  ARRAY_SIZE(soc_codec_cx2072x_dai));
+}
+
+static int cx2072x_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_component(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id cx2072x_i2c_id[] = {
+	{ "cx20721", 0 },
+	{ "cx20723", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, cx2072x_i2c_id);
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id cx2072x_acpi_match[] = {
+	{ "14F10720", 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, cx2072x_acpi_match);
+#endif
+
+static struct i2c_driver cx2072x_i2c_driver = {
+	.probe = cx2072x_i2c_probe,
+	.remove = cx2072x_i2c_remove,
+	.id_table = cx2072x_i2c_id,
+	.driver = {
+		.name = "cx2072x",
+		.acpi_match_table = ACPI_PTR(cx2072x_acpi_match),
+	},
+};
+
+module_i2c_driver(cx2072x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC cx2072x Codec Driver");
+MODULE_AUTHOR("Simon Ho <simon.ho@conexant.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cx2072x.h b/sound/soc/codecs/cx2072x.h
new file mode 100644
index 000000000000..3f9ba1681383
--- /dev/null
+++ b/sound/soc/codecs/cx2072x.h
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC CX20721/CX20723 codec driver
+//
+// Copyright:	(C) 2017 Conexant Systems, Inc.
+// Author:	Simon Ho, <Simon.ho@conexant.com>
+//
+
+#ifndef __CX2072X_H__
+#define __CX2072X_H__
+
+#define CX2072X_MCLK_PLL		1
+#define CX2072X_MCLK_EXTERNAL_PLL	1
+#define CX2072X_MCLK_INTERNAL_OSC	2
+
+/*#define CX2072X_RATES		SNDRV_PCM_RATE_8000_192000*/
+#define CX2072X_RATES_DSP	SNDRV_PCM_RATE_48000
+
+#define CX2072X_REG_MAX					0x8a3c
+
+#define CX2072X_VENDOR_ID				0x0200
+#define CX2072X_REVISION_ID				0x0208
+#define CX2072X_CURRENT_BCLK_FREQUENCY			0x00dc
+#define CX2072X_AFG_POWER_STATE				0x0414
+#define CX2072X_UM_RESPONSE				0x0420
+#define CX2072X_GPIO_DATA				0x0454
+#define CX2072X_GPIO_ENABLE				0x0458
+#define CX2072X_GPIO_DIRECTION				0x045c
+#define CX2072X_GPIO_WAKE				0x0460
+#define CX2072X_GPIO_UM_ENABLE				0x0464
+#define CX2072X_GPIO_STICKY_MASK			0x0468
+#define CX2072X_AFG_FUNCTION_RESET			0x07fc
+#define CX2072X_DAC1_CONVERTER_FORMAT			0x43c8
+#define CX2072X_DAC1_AMP_GAIN_RIGHT			0x41c0
+#define CX2072X_DAC1_AMP_GAIN_LEFT			0x41e0
+#define CX2072X_DAC1_POWER_STATE			0x4014
+#define CX2072X_DAC1_CONVERTER_STREAM_CHANNEL		0x4018
+#define CX2072X_DAC1_EAPD_ENABLE			0x4030
+#define CX2072X_DAC2_CONVERTER_FORMAT			0x47c8
+#define CX2072X_DAC2_AMP_GAIN_RIGHT			0x45c0
+#define CX2072X_DAC2_AMP_GAIN_LEFT			0x45e0
+#define CX2072X_DAC2_POWER_STATE			0x4414
+#define CX2072X_DAC2_CONVERTER_STREAM_CHANNEL		0x4418
+#define CX2072X_ADC1_CONVERTER_FORMAT			0x4fc8
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_0			0x4d80
+#define CX2072X_ADC1_AMP_GAIN_LEFT_0			0x4da0
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_1			0x4d84
+#define CX2072X_ADC1_AMP_GAIN_LEFT_1			0x4da4
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_2			0x4d88
+#define CX2072X_ADC1_AMP_GAIN_LEFT_2			0x4da8
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_3			0x4d8c
+#define CX2072X_ADC1_AMP_GAIN_LEFT_3			0x4dac
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_4			0x4d90
+#define CX2072X_ADC1_AMP_GAIN_LEFT_4			0x4db0
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_5			0x4d94
+#define CX2072X_ADC1_AMP_GAIN_LEFT_5			0x4db4
+#define CX2072X_ADC1_AMP_GAIN_RIGHT_6			0x4d98
+#define CX2072X_ADC1_AMP_GAIN_LEFT_6			0x4db8
+#define CX2072X_ADC1_CONNECTION_SELECT_CONTROL		0x4c04
+#define CX2072X_ADC1_POWER_STATE			0x4c14
+#define CX2072X_ADC1_CONVERTER_STREAM_CHANNEL		0x4c18
+#define CX2072X_ADC2_CONVERTER_FORMAT			0x53c8
+#define CX2072X_ADC2_AMP_GAIN_RIGHT_0			0x5180
+#define CX2072X_ADC2_AMP_GAIN_LEFT_0			0x51a0
+#define CX2072X_ADC2_AMP_GAIN_RIGHT_1			0x5184
+#define CX2072X_ADC2_AMP_GAIN_LEFT_1			0x51a4
+#define CX2072X_ADC2_AMP_GAIN_RIGHT_2			0x5188
+#define CX2072X_ADC2_AMP_GAIN_LEFT_2			0x51a8
+#define CX2072X_ADC2_CONNECTION_SELECT_CONTROL		0x5004
+#define CX2072X_ADC2_POWER_STATE			0x5014
+#define CX2072X_ADC2_CONVERTER_STREAM_CHANNEL		0x5018
+#define CX2072X_PORTA_CONNECTION_SELECT_CTRL		0x5804
+#define CX2072X_PORTA_POWER_STATE			0x5814
+#define CX2072X_PORTA_PIN_CTRL				0x581c
+#define CX2072X_PORTA_UNSOLICITED_RESPONSE		0x5820
+#define CX2072X_PORTA_PIN_SENSE				0x5824
+#define CX2072X_PORTA_EAPD_BTL				0x5830
+#define CX2072X_PORTB_POWER_STATE			0x6014
+#define CX2072X_PORTB_PIN_CTRL				0x601c
+#define CX2072X_PORTB_UNSOLICITED_RESPONSE		0x6020
+#define CX2072X_PORTB_PIN_SENSE				0x6024
+#define CX2072X_PORTB_EAPD_BTL				0x6030
+#define CX2072X_PORTB_GAIN_RIGHT			0x6180
+#define CX2072X_PORTB_GAIN_LEFT				0x61a0
+#define CX2072X_PORTC_POWER_STATE			0x6814
+#define CX2072X_PORTC_PIN_CTRL				0x681c
+#define CX2072X_PORTC_GAIN_RIGHT			0x6980
+#define CX2072X_PORTC_GAIN_LEFT				0x69a0
+#define CX2072X_PORTD_POWER_STATE			0x6414
+#define CX2072X_PORTD_PIN_CTRL				0x641c
+#define CX2072X_PORTD_UNSOLICITED_RESPONSE		0x6420
+#define CX2072X_PORTD_PIN_SENSE				0x6424
+#define CX2072X_PORTD_GAIN_RIGHT			0x6580
+#define CX2072X_PORTD_GAIN_LEFT				0x65a0
+#define CX2072X_PORTE_CONNECTION_SELECT_CTRL		0x7404
+#define CX2072X_PORTE_POWER_STATE			0x7414
+#define CX2072X_PORTE_PIN_CTRL				0x741c
+#define CX2072X_PORTE_UNSOLICITED_RESPONSE		0x7420
+#define CX2072X_PORTE_PIN_SENSE				0x7424
+#define CX2072X_PORTE_EAPD_BTL				0x7430
+#define CX2072X_PORTE_GAIN_RIGHT			0x7580
+#define CX2072X_PORTE_GAIN_LEFT				0x75a0
+#define CX2072X_PORTF_POWER_STATE			0x7814
+#define CX2072X_PORTF_PIN_CTRL				0x781c
+#define CX2072X_PORTF_UNSOLICITED_RESPONSE		0x7820
+#define CX2072X_PORTF_PIN_SENSE				0x7824
+#define CX2072X_PORTF_GAIN_RIGHT			0x7980
+#define CX2072X_PORTF_GAIN_LEFT				0x79a0
+#define CX2072X_PORTG_POWER_STATE			0x5c14
+#define CX2072X_PORTG_PIN_CTRL				0x5c1c
+#define CX2072X_PORTG_CONNECTION_SELECT_CTRL		0x5c04
+#define CX2072X_PORTG_EAPD_BTL				0x5c30
+#define CX2072X_PORTM_POWER_STATE			0x8814
+#define CX2072X_PORTM_PIN_CTRL				0x881c
+#define CX2072X_PORTM_CONNECTION_SELECT_CTRL		0x8804
+#define CX2072X_PORTM_EAPD_BTL				0x8830
+#define CX2072X_MIXER_POWER_STATE			0x5414
+#define CX2072X_MIXER_GAIN_RIGHT_0			0x5580
+#define CX2072X_MIXER_GAIN_LEFT_0			0x55a0
+#define CX2072X_MIXER_GAIN_RIGHT_1			0x5584
+#define CX2072X_MIXER_GAIN_LEFT_1			0x55a4
+#define CX2072X_EQ_ENABLE_BYPASS			0x6d00
+#define CX2072X_EQ_B0_COEFF				0x6d02
+#define CX2072X_EQ_B1_COEFF				0x6d04
+#define CX2072X_EQ_B2_COEFF				0x6d06
+#define CX2072X_EQ_A1_COEFF				0x6d08
+#define CX2072X_EQ_A2_COEFF				0x6d0a
+#define CX2072X_EQ_G_COEFF				0x6d0c
+#define CX2072X_EQ_BAND					0x6d0d
+#define CX2072X_SPKR_DRC_ENABLE_STEP			0x6d10
+#define CX2072X_SPKR_DRC_CONTROL			0x6d14
+#define CX2072X_SPKR_DRC_TEST				0x6d18
+#define CX2072X_DIGITAL_BIOS_TEST0			0x6d80
+#define CX2072X_DIGITAL_BIOS_TEST2			0x6d84
+#define CX2072X_I2SPCM_CONTROL1				0x6e00
+#define CX2072X_I2SPCM_CONTROL2				0x6e04
+#define CX2072X_I2SPCM_CONTROL3				0x6e08
+#define CX2072X_I2SPCM_CONTROL4				0x6e0c
+#define CX2072X_I2SPCM_CONTROL5				0x6e10
+#define CX2072X_I2SPCM_CONTROL6				0x6e18
+#define CX2072X_UM_INTERRUPT_CRTL_E			0x6e14
+#define CX2072X_CODEC_TEST2				0x7108
+#define CX2072X_CODEC_TEST9				0x7124
+#define CX2072X_CODEC_TEST20				0x7310
+#define CX2072X_CODEC_TEST26				0x7328
+#define CX2072X_ANALOG_TEST3				0x718c
+#define CX2072X_ANALOG_TEST4				0x7190
+#define CX2072X_ANALOG_TEST5				0x7194
+#define CX2072X_ANALOG_TEST6				0x7198
+#define CX2072X_ANALOG_TEST7				0x719c
+#define CX2072X_ANALOG_TEST8				0x71a0
+#define CX2072X_ANALOG_TEST9				0x71a4
+#define CX2072X_ANALOG_TEST10				0x71a8
+#define CX2072X_ANALOG_TEST11				0x71ac
+#define CX2072X_ANALOG_TEST12				0x71b0
+#define CX2072X_ANALOG_TEST13				0x71b4
+#define CX2072X_DIGITAL_TEST0				0x7200
+#define CX2072X_DIGITAL_TEST1				0x7204
+#define CX2072X_DIGITAL_TEST11				0x722c
+#define CX2072X_DIGITAL_TEST12				0x7230
+#define CX2072X_DIGITAL_TEST15				0x723c
+#define CX2072X_DIGITAL_TEST16				0x7080
+#define CX2072X_DIGITAL_TEST17				0x7084
+#define CX2072X_DIGITAL_TEST18				0x7088
+#define CX2072X_DIGITAL_TEST19				0x708c
+#define CX2072X_DIGITAL_TEST20				0x7090
+
+#define CX2072X_MAX_EQ_BAND	7
+#define CX2072X_MAX_EQ_COEFF	11
+#define CX2072X_MAX_DRC_REGS	9
+#define CX2072X_MIC_EQ_COEFF	10
+
+/* DAI interfae type */
+#define CX2072X_DAI_HIFI	1
+#define CX2072X_DAI_DSP		2
+#define CX2072X_DAI_DSP_PWM	3 /* 4 ch, including mic and AEC */
+
+enum cx2072x_jack_types {
+	CX2072X_JACK_NONE = 0x0000,
+	CX2072X_JACK_HEADPHONE = 0x0001,
+	CX2072X_JACK_APPLE_HEADSET = 0x0002,
+	CX2072X_JACK_NOKIE_HEADSET = 0x0003,
+};
+
+int snd_soc_cx2072x_enable_jack_detect(struct snd_soc_component *codec);
+int snd_soc_cx2072x_get_jack_state(struct snd_soc_component *codec);
+
+enum cx2072x_reg_sample_size {
+	CX2072X_SAMPLE_SIZE_8_BITS = 0,
+	CX2072X_SAMPLE_SIZE_16_BITS = 1,
+	CX2072X_SAMPLE_SIZE_24_BITS = 2,
+	CX2072X_SAMPLE_SIZE_RESERVED = 3,
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg1 {
+	struct {
+		u32 rx_data_one_line:1;
+		u32 rx_ws_pol:1;
+		u32 rx_ws_wid:7;
+		u32 rx_frm_len:5;
+		u32 rx_sa_size:2;
+		u32 tx_data_one_line:1;
+		u32 tx_ws_pol:1;
+		u32 tx_ws_wid:7;
+		u32 tx_frm_len:5;
+		u32 tx_sa_size:2;
+	} r;
+	u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg2 {
+	struct {
+		u32 tx_en_ch1:1;
+		u32 tx_en_ch2:1;
+		u32 tx_en_ch3:1;
+		u32 tx_en_ch4:1;
+		u32 tx_en_ch5:1;
+		u32 tx_en_ch6:1;
+		u32 tx_slot_1:5;
+		u32 tx_slot_2:5;
+		u32 tx_slot_3:5;
+		u32 tx_slot_4:5;
+		u32 res:1;
+		u32 tx_data_neg_bclk:1;
+		u32 tx_master:1;
+		u32 tx_tri_n:1;
+		u32 tx_endian_sel:1;
+		u32 tx_dstart_dly:1;
+	} r;
+	u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg3 {
+	struct {
+		u32 rx_en_ch1:1;
+		u32 rx_en_ch2:1;
+		u32 rx_en_ch3:1;
+		u32 rx_en_ch4:1;
+		u32 rx_en_ch5:1;
+		u32 rx_en_ch6:1;
+		u32 rx_slot_1:5;
+		u32 rx_slot_2:5;
+		u32 rx_slot_3:5;
+		u32 rx_slot_4:5;
+		u32 res:1;
+		u32 rx_data_neg_bclk:1;
+		u32 rx_master:1;
+		u32 rx_tri_n:1;
+		u32 rx_endian_sel:1;
+		u32 rx_dstart_dly:1;
+	} r;
+	u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg4 {
+	struct {
+		u32 rx_mute:1;
+		u32 tx_mute:1;
+		u32 reserved:1;
+		u32 dac_34_independent:1;
+		u32 dac_bclk_lrck_share:1;
+		u32 bclk_lrck_share_en:1;
+		u32 reserved2:2;
+		u32 rx_last_dac_ch_en:1;
+		u32 rx_last_dac_ch:3;
+		u32 tx_last_adc_ch_en:1;
+		u32 tx_last_adc_ch:3;
+		u32 rx_slot_5:5;
+		u32 rx_slot_6:5;
+		u32 reserved3:6;
+	} r;
+	u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg5 {
+	struct {
+		u32 tx_slot_5:5;
+		u32 reserved:3;
+		u32 tx_slot_6:5;
+		u32 reserved2:3;
+		u32 reserved3:8;
+		u32 i2s_pcm_clk_div:7;
+		u32 i2s_pcm_clk_div_chan_en:1;
+	} r;
+	u32 ulval;
+};
+
+union cx2072x_reg_i2spcm_ctrl_reg6 {
+	struct {
+		u32 reserved:5;
+		u32 rx_pause_cycles:3;
+		u32 rx_pause_start_pos:8;
+		u32 reserved2:5;
+		u32 tx_pause_cycles:3;
+		u32 tx_pause_start_pos:8;
+	} r;
+	u32 ulval;
+};
+
+union cx2072x_reg_digital_bios_test2 {
+	struct {
+		u32 pull_down_eapd:2;
+		u32 input_en_eapd_pad:1;
+		u32 push_pull_mode:1;
+		u32 eapd_pad_output_driver:2;
+		u32 pll_source:1;
+		u32 i2s_bclk_en:1;
+		u32 i2s_bclk_invert:1;
+		u32 pll_ref_clock:1;
+		u32 class_d_shield_clk:1;
+		u32 audio_pll_bypass_mode:1;
+		u32 reserved:4;
+	} r;
+	u32 ulval;
+};
+
+#endif /* __CX2072X_H__ */
-- 
2.16.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 2/2] ASoC: Intel: Add machine driver for CX2072X on BYT/CHT platforms
  2019-04-23 14:13 [PATCH 0/2] ASoC: CX2072X codec support (revised) Takashi Iwai
  2019-04-23 14:13 ` [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC Takashi Iwai
@ 2019-04-23 14:13 ` Takashi Iwai
  2019-04-23 19:20   ` Pierre-Louis Bossart
  1 sibling, 1 reply; 20+ messages in thread
From: Takashi Iwai @ 2019-04-23 14:13 UTC (permalink / raw)
  To: alsa-devel; +Cc: Mark Brown, Pierre-Louis Bossart

This is an implementation of a machine driver needed for Conexant
CX2072X codec on Intel Baytrail and Cherrytrail platforms.  The
current patch is based on the initial work by Pierre-Louis Bossart and
the other Intel machine drivers.

The jack detection support (driven via the standard GPIO) was added on
top of the original work.

No SOF support yet, so the corresponding entries are commented out.

Tested with ASUS E200HA laptop.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=115531
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/soc/intel/boards/Kconfig                    |  11 +
 sound/soc/intel/boards/Makefile                   |   2 +
 sound/soc/intel/boards/bytcht_cx2072x.c           | 285 ++++++++++++++++++++++
 sound/soc/intel/common/soc-acpi-intel-byt-match.c |   8 +
 sound/soc/intel/common/soc-acpi-intel-cht-match.c |   8 +
 5 files changed, 314 insertions(+)
 create mode 100644 sound/soc/intel/boards/bytcht_cx2072x.c

diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index 12d6b73e9531..e8494ef96447 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -136,6 +136,17 @@ config SND_SOC_INTEL_CHT_BSW_NAU8824_MACH
 	  Say Y or m if you have such a device. This is a recommended option.
 	  If unsure select "N".
 
+config SND_SOC_INTEL_BYT_CHT_CX2072X_MACH
+	tristate "Baytrail & Cherrytrail with CX2072X codec"
+	depends on X86_INTEL_LPSS && I2C && ACPI
+	select SND_SOC_ACPI
+	select SND_SOC_CX2072X
+	help
+	  This adds support for ASoC machine driver for Intel(R) Baytrail &
+	  Cherrytrail platforms with Conexant CX2072X audio codec.
+	  Say Y or m if you have such a device. This is a recommended option.
+	  If unsure select "N".
+
 config SND_SOC_INTEL_BYT_CHT_DA7213_MACH
 	tristate "Baytrail & Cherrytrail with DA7212/7213 codec"
 	depends on X86_INTEL_LPSS && I2C && ACPI
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index bf072ea299b7..098ad7998b43 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -13,6 +13,7 @@ snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
 snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
 snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
 snd-soc-sst-cht-bsw-nau8824-objs := cht_bsw_nau8824.o
+snd-soc-sst-byt-cht-cx2072x-objs := bytcht_cx2072x.o
 snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o
 snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o
 snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o
@@ -40,6 +41,7 @@ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH) += snd-soc-sst-cht-bsw-nau8824.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_CX2072X_MACH) += snd-soc-sst-byt-cht-cx2072x.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH) += snd-soc-sst-byt-cht-es8316.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o
diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c
new file mode 100644
index 000000000000..697ff679576d
--- /dev/null
+++ b/sound/soc/intel/boards/bytcht_cx2072x.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+//  bytcht_cx207x.c - ASoC DPCM Machine driver for Baytrail / CherryTrail
+//                    platforms with CX2072X codec
+//
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/platform_sst_audio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../codecs/cx2072x.h"
+#include "../atom/sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget byt_cht_cx2072x_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Int Mic", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_cht_cx2072x_audio_map[] = {
+	/* External Speakers: HFL, HFR */
+	{"Headphone", NULL, "PORTA"},
+	{"Ext Spk", NULL, "PORTG"},
+	{"PORTC", NULL, "Int Mic"},
+	{"PORTD", NULL, "Headset Mic"},
+
+	{"Playback", NULL, "ssp2 Tx"},
+	{"ssp2 Tx", NULL, "codec_out0"},
+	{"ssp2 Tx", NULL, "codec_out1"},
+	{"codec_in0", NULL, "ssp2 Rx"},
+	{"codec_in1", NULL, "ssp2 Rx"},
+	{"ssp2 Rx", NULL, "Capture"},
+	{"ssp0 Tx", NULL, "modem_out"},
+	{"modem_in", NULL, "ssp0 Rx"},
+};
+
+static const struct snd_kcontrol_new byt_cht_cx2072x_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Headphone"),
+	SOC_DAPM_PIN_SWITCH("Headset Mic"),
+	SOC_DAPM_PIN_SWITCH("Int Mic"),
+	SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static struct snd_soc_jack byt_cht_cx2072x_headset;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin byt_cht_cx2072x_headset_pins[] = {
+	{
+		.pin = "Headset Mic",
+		.mask = SND_JACK_MICROPHONE,
+	},
+	{
+		.pin = "Headphone",
+		.mask = SND_JACK_HEADPHONE,
+	},
+};
+
+static const struct acpi_gpio_params byt_cht_cx2072x_headset_gpios;
+static const struct acpi_gpio_mapping byt_cht_cx2072x_acpi_gpios[] = {
+	{ "headset-gpios", &byt_cht_cx2072x_headset_gpios, 1 },
+	{},
+};
+
+static int byt_cht_cx2072x_jack_status_check(void *data)
+{
+	return snd_soc_cx2072x_get_jack_state(data);
+}
+
+static struct snd_soc_jack_gpio byt_cht_cx2072x_gpio = {
+	.name = "headset",
+	.report = SND_JACK_HEADSET | SND_JACK_BTN_0,
+	.debounce_time = 150,
+	.wake = true,
+	.jack_status_check = byt_cht_cx2072x_jack_status_check,
+};
+
+static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_card *card = rtd->card;
+	struct snd_soc_component *codec = rtd->codec_dai->component;
+	int ret;
+
+	if (devm_acpi_dev_add_driver_gpios(codec->dev,
+					   byt_cht_cx2072x_acpi_gpios))
+		dev_warn(rtd->dev, "Unable to add GPIO mapping table\n");
+
+	card->dapm.idle_bias_off = true;
+
+	/* set the default PLL rate, the clock is handled by the codec driver */
+	ret = snd_soc_dai_set_sysclk(rtd->codec_dai, CX2072X_MCLK_EXTERNAL_PLL,
+				     19200000, SND_SOC_CLOCK_IN);
+	if (ret) {
+		dev_err(rtd->dev, "Could not set sysclk\n");
+		return ret;
+	}
+
+	ret = snd_soc_card_jack_new(card, "Headset",
+				    SND_JACK_HEADSET | SND_JACK_BTN_0,
+				    &byt_cht_cx2072x_headset,
+				    byt_cht_cx2072x_headset_pins,
+				    ARRAY_SIZE(byt_cht_cx2072x_headset_pins));
+	if (ret)
+		return ret;
+
+	byt_cht_cx2072x_gpio.gpiod_dev = codec->dev;
+	byt_cht_cx2072x_gpio.data = codec;
+	ret = snd_soc_jack_add_gpios(&byt_cht_cx2072x_headset, 1,
+				     &byt_cht_cx2072x_gpio);
+	if (ret) {
+		dev_err(rtd->dev, "Adding jack GPIO failed\n");
+		return ret;
+	}
+
+	snd_soc_cx2072x_enable_jack_detect(codec);
+
+	return ret;
+}
+
+static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd,
+				 struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	int ret;
+
+	/* The DSP will covert the FE rate to 48k, stereo, 24bits */
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = 2;
+
+	/* set SSP2 to 24-bit */
+	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+	/*
+	 * Default mode for SSP configuration is TDM 4 slot, override config
+	 * with explicit setting to I2S 2ch 24-bit. The word length is set with
+	 * dai_set_tdm_slot() since there is no other API exposed
+	 */
+	ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+				SND_SOC_DAIFMT_I2S     |
+				SND_SOC_DAIFMT_NB_NF   |
+				SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+		return ret;
+	}
+
+	snd_soc_dai_set_bclk_ratio(rtd->codec_dai, 50);
+	return 0;
+}
+
+static int byt_cht_cx2072x_aif1_startup(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_hw_constraint_single(substream->runtime,
+					    SNDRV_PCM_HW_PARAM_RATE, 48000);
+}
+
+static struct snd_soc_ops byt_cht_cx2072x_aif1_ops = {
+	.startup = byt_cht_cx2072x_aif1_startup,
+};
+
+static struct snd_soc_dai_link byt_cht_cx2072x_dais[] = {
+	[MERR_DPCM_AUDIO] = {
+		.name = "Audio Port",
+		.stream_name = "Audio",
+		.cpu_dai_name = "media-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+		.nonatomic = true,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.ops = &byt_cht_cx2072x_aif1_ops,
+	},
+	[MERR_DPCM_DEEP_BUFFER] = {
+		.name = "Deep-Buffer Audio Port",
+		.stream_name = "Deep-Buffer Audio",
+		.cpu_dai_name = "deepbuffer-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+		.nonatomic = true,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.ops = &byt_cht_cx2072x_aif1_ops,
+	},
+	/* back ends */
+	{
+		.name = "SSP2-Codec",
+		.id = 1,
+		.cpu_dai_name = "ssp2-port",
+		.platform_name = "sst-mfld-platform",
+		.no_pcm = 1,
+		.codec_dai_name = "cx2072x-hifi",
+		.codec_name = "i2c-14F10720:00",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+					      | SND_SOC_DAIFMT_CBS_CFS,
+		.init = byt_cht_cx2072x_init,
+		.be_hw_params_fixup = byt_cht_cx2072x_fixup,
+		.nonatomic = true,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+};
+
+/* SoC card */
+static struct snd_soc_card byt_cht_cx2072x_card = {
+	.name = "bytcht-cx2072x",
+	.owner = THIS_MODULE,
+	.dai_link = byt_cht_cx2072x_dais,
+	.num_links = ARRAY_SIZE(byt_cht_cx2072x_dais),
+	.dapm_widgets = byt_cht_cx2072x_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(byt_cht_cx2072x_widgets),
+	.dapm_routes = byt_cht_cx2072x_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(byt_cht_cx2072x_audio_map),
+	.controls = byt_cht_cx2072x_controls,
+	.num_controls = ARRAY_SIZE(byt_cht_cx2072x_controls),
+};
+
+static char codec_name[SND_ACPI_I2C_ID_LEN];
+
+static int snd_byt_cht_cx2072x_probe(struct platform_device *pdev)
+{
+	struct snd_soc_acpi_mach *mach;
+	struct acpi_device *adev;
+	int dai_index = 0;
+	int i, ret;
+
+	byt_cht_cx2072x_card.dev = &pdev->dev;
+	mach = dev_get_platdata(&pdev->dev);
+
+	/* fix index of codec dai */
+	for (i = 0; i < ARRAY_SIZE(byt_cht_cx2072x_dais); i++) {
+		if (!strcmp(byt_cht_cx2072x_dais[i].codec_name,
+			    "i2c-14F10720:00")) {
+			dai_index = i;
+			break;
+		}
+	}
+
+	/* fixup codec name based on HID */
+	adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
+	if (adev) {
+		snprintf(codec_name, sizeof(codec_name), "i2c-%s",
+			 acpi_dev_name(adev));
+		put_device(&adev->dev);
+		byt_cht_cx2072x_dais[dai_index].codec_name = codec_name;
+	}
+
+	/* override plaform name, if required */
+	ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_cx2072x_card,
+						    mach->mach_params.platform);
+	if (ret)
+		return ret;
+
+	return devm_snd_soc_register_card(&pdev->dev, &byt_cht_cx2072x_card);
+}
+
+static struct platform_driver snd_byt_cht_cx2072x_driver = {
+	.driver = {
+		.name = "bytcht_cx2072x",
+	},
+	.probe = snd_byt_cht_cx2072x_probe,
+};
+module_platform_driver(snd_byt_cht_cx2072x_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_cx2072x");
diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c
index fe812a909db4..5387a82c2cf5 100644
--- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c
@@ -211,6 +211,14 @@ struct snd_soc_acpi_mach  snd_soc_acpi_intel_baytrail_machines[] = {
 		.sof_fw_filename = "sof-byt.ri",
 		.sof_tplg_filename = "sof-byt-max98090.tplg",
 	},
+	{
+		.id = "14F10720",
+		.drv_name = "bytcht_cx2072x",
+		.fw_filename = "intel/fw_sst_0f28.bin",
+		.board = "bytcht_cx2072x",
+		/* .sof_fw_filename = "sof-byt.ri", */
+		/* .sof_tplg_filename = "sof-byt-cx2072x.tplg", */
+	},
 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
 	/*
 	 * This is always last in the table so that it is selected only when
diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
index deafd87cc764..69ecbb88c171 100644
--- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
@@ -169,6 +169,14 @@ struct snd_soc_acpi_mach  snd_soc_acpi_intel_cherrytrail_machines[] = {
 		.sof_fw_filename = "sof-cht.ri",
 		.sof_tplg_filename = "sof-cht-rt5651.tplg",
 	},
+	{
+		.id = "14F10720",
+		.drv_name = "bytcht_cx2072x",
+		.fw_filename = "intel/fw_sst_22a8.bin",
+		.board = "bytcht_cx2072x",
+		/* .sof_fw_filename = "sof-cht.ri", */
+		/* .sof_tplg_filename = "sof-cht-cx2072x.tplg", */
+	},
 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
 	/*
 	 * This is always last in the table so that it is selected only when
-- 
2.16.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/2] ASoC: Intel: Add machine driver for CX2072X on BYT/CHT platforms
  2019-04-23 14:13 ` [PATCH 2/2] ASoC: Intel: Add machine driver for CX2072X on BYT/CHT platforms Takashi Iwai
@ 2019-04-23 19:20   ` Pierre-Louis Bossart
  2019-04-23 19:39     ` Takashi Iwai
  0 siblings, 1 reply; 20+ messages in thread
From: Pierre-Louis Bossart @ 2019-04-23 19:20 UTC (permalink / raw)
  To: Takashi Iwai, alsa-devel; +Cc: Mark Brown

On 4/23/19 9:13 AM, Takashi Iwai wrote:
> This is an implementation of a machine driver needed for Conexant
> CX2072X codec on Intel Baytrail and Cherrytrail platforms.  The
> current patch is based on the initial work by Pierre-Louis Bossart and
> the other Intel machine drivers.
> 
> The jack detection support (driven via the standard GPIO) was added on
> top of the original work.
> 
> No SOF support yet, so the corresponding entries are commented out.

SOF support is trivial to add, can we help you here?


> +config SND_SOC_INTEL_BYT_CHT_CX2072X_MACH
> +	tristate "Baytrail & Cherrytrail with CX2072X codec"
> +	depends on X86_INTEL_LPSS && I2C && ACPI

Didn't Mark recently split this in two, with X86_INTEL_LPSS || COMPILE_TEST?

> +static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd,
> +				 struct snd_pcm_hw_params *params)
> +{
> +	struct snd_interval *rate =
> +		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
> +	struct snd_interval *channels =
> +		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
> +	int ret;
> +
> +	/* The DSP will covert the FE rate to 48k, stereo, 24bits */
> +	rate->min = rate->max = 48000;
> +	channels->min = channels->max = 2;
> +
> +	/* set SSP2 to 24-bit */
> +	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
> +
> +	/*
> +	 * Default mode for SSP configuration is TDM 4 slot, override config
> +	 * with explicit setting to I2S 2ch 24-bit. The word length is set with
> +	 * dai_set_tdm_slot() since there is no other API exposed
> +	 */
> +	ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
> +				SND_SOC_DAIFMT_I2S     |
> +				SND_SOC_DAIFMT_NB_NF   |
> +				SND_SOC_DAIFMT_CBS_CFS);
> +	if (ret < 0) {
> +		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
> +	if (ret < 0) {
> +		dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
> +		return ret;
> +	}
> +
> +	snd_soc_dai_set_bclk_ratio(rtd->codec_dai, 50);

that part would be problematic for SOF. IIRC we put all clock-related 
stuff in the init, and ignore the fixups to use topology-based 
information instead. If this call to _set_bclk_ratio can be moved to the 
init it's more future-proof. Is there a reason to do this here in the fixup?


> +static struct snd_soc_dai_link byt_cht_cx2072x_dais[] = {
> +	[MERR_DPCM_AUDIO] = {
> +		.name = "Audio Port",
> +		.stream_name = "Audio",
> +		.cpu_dai_name = "media-cpu-dai",
> +		.codec_dai_name = "snd-soc-dummy-dai",
> +		.codec_name = "snd-soc-dummy",
> +		.platform_name = "sst-mfld-platform",
> +		.nonatomic = true,
> +		.dynamic = 1,
> +		.dpcm_playback = 1,
> +		.dpcm_capture = 1,
> +		.ops = &byt_cht_cx2072x_aif1_ops,
> +	},
> +	[MERR_DPCM_DEEP_BUFFER] = {
> +		.name = "Deep-Buffer Audio Port",
> +		.stream_name = "Deep-Buffer Audio",
> +		.cpu_dai_name = "deepbuffer-cpu-dai",
> +		.codec_dai_name = "snd-soc-dummy-dai",
> +		.codec_name = "snd-soc-dummy",
> +		.platform_name = "sst-mfld-platform",
> +		.nonatomic = true,
> +		.dynamic = 1,
> +		.dpcm_playback = 1,
> +		.ops = &byt_cht_cx2072x_aif1_ops,
> +	},
> +	/* back ends */
> +	{
> +		.name = "SSP2-Codec",
> +		.id = 1,
> +		.cpu_dai_name = "ssp2-port",
> +		.platform_name = "sst-mfld-platform",
> +		.no_pcm = 1,
> +		.codec_dai_name = "cx2072x-hifi",
> +		.codec_name = "i2c-14F10720:00",
> +		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
> +					      | SND_SOC_DAIFMT_CBS_CFS,
> +		.init = byt_cht_cx2072x_init,
> +		.be_hw_params_fixup = byt_cht_cx2072x_fixup,
> +		.nonatomic = true,

I can't recall if this .atomic is needed or not for back-ends.

> +	{
> +		.id = "14F10720",
> +		.drv_name = "bytcht_cx2072x",
> +		.fw_filename = "intel/fw_sst_0f28.bin",
> +		.board = "bytcht_cx2072x",
> +		/* .sof_fw_filename = "sof-byt.ri", */
> +		/* .sof_tplg_filename = "sof-byt-cx2072x.tplg", */
> +	},
>   #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
>   	/*
>   	 * This is always last in the table so that it is selected only when
> diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
> index deafd87cc764..69ecbb88c171 100644
> --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c
> +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
> @@ -169,6 +169,14 @@ struct snd_soc_acpi_mach  snd_soc_acpi_intel_cherrytrail_machines[] = {
>   		.sof_fw_filename = "sof-cht.ri",
>   		.sof_tplg_filename = "sof-cht-rt5651.tplg",
>   	},
> +	{
> +		.id = "14F10720",
> +		.drv_name = "bytcht_cx2072x",
> +		.fw_filename = "intel/fw_sst_22a8.bin",
> +		.board = "bytcht_cx2072x",
> +		/* .sof_fw_filename = "sof-cht.ri", */
> +		/* .sof_tplg_filename = "sof-cht-cx2072x.tplg", */
> +	},

I'd uncomment those two ACPI machine parts. There is not risk unless SOF 
is actually enabled.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/2] ASoC: Intel: Add machine driver for CX2072X on BYT/CHT platforms
  2019-04-23 19:20   ` Pierre-Louis Bossart
@ 2019-04-23 19:39     ` Takashi Iwai
  2019-04-24 13:08       ` Takashi Iwai
  0 siblings, 1 reply; 20+ messages in thread
From: Takashi Iwai @ 2019-04-23 19:39 UTC (permalink / raw)
  To: Pierre-Louis Bossart; +Cc: alsa-devel, Mark Brown

On Tue, 23 Apr 2019 21:20:24 +0200,
Pierre-Louis Bossart wrote:
> 
> On 4/23/19 9:13 AM, Takashi Iwai wrote:
> > This is an implementation of a machine driver needed for Conexant
> > CX2072X codec on Intel Baytrail and Cherrytrail platforms.  The
> > current patch is based on the initial work by Pierre-Louis Bossart and
> > the other Intel machine drivers.
> >
> > The jack detection support (driven via the standard GPIO) was added on
> > top of the original work.
> >
> > No SOF support yet, so the corresponding entries are commented out.
> 
> SOF support is trivial to add, can we help you here?

I just had no time to take a deeper look at that direction, so it's a
thing I'd like to play at my next spare time.

> > +config SND_SOC_INTEL_BYT_CHT_CX2072X_MACH
> > +	tristate "Baytrail & Cherrytrail with CX2072X codec"
> > +	depends on X86_INTEL_LPSS && I2C && ACPI
> 
> Didn't Mark recently split this in two, with X86_INTEL_LPSS || COMPILE_TEST?

The entry I just copied, CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH,
still has this style, so no, it doesn't seem applied.


> > +static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd,
> > +				 struct snd_pcm_hw_params *params)
> > +{
> > +	struct snd_interval *rate =
> > +		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
> > +	struct snd_interval *channels =
> > +		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
> > +	int ret;
> > +
> > +	/* The DSP will covert the FE rate to 48k, stereo, 24bits */
> > +	rate->min = rate->max = 48000;
> > +	channels->min = channels->max = 2;
> > +
> > +	/* set SSP2 to 24-bit */
> > +	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
> > +
> > +	/*
> > +	 * Default mode for SSP configuration is TDM 4 slot, override config
> > +	 * with explicit setting to I2S 2ch 24-bit. The word length is set with
> > +	 * dai_set_tdm_slot() since there is no other API exposed
> > +	 */
> > +	ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
> > +				SND_SOC_DAIFMT_I2S     |
> > +				SND_SOC_DAIFMT_NB_NF   |
> > +				SND_SOC_DAIFMT_CBS_CFS);
> > +	if (ret < 0) {
> > +		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
> > +	if (ret < 0) {
> > +		dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	snd_soc_dai_set_bclk_ratio(rtd->codec_dai, 50);
> 
> that part would be problematic for SOF. IIRC we put all clock-related
> stuff in the init, and ignore the fixups to use topology-based
> information instead. If this call to _set_bclk_ratio can be moved to
> the init it's more future-proof. Is there a reason to do this here in
> the fixup?

No particular reason from my side.  I just took over your code ;)

That is, it'd be appreciated if you can give a fixup patch that can be
applied on top.


> > +static struct snd_soc_dai_link byt_cht_cx2072x_dais[] = {
> > +	[MERR_DPCM_AUDIO] = {
> > +		.name = "Audio Port",
> > +		.stream_name = "Audio",
> > +		.cpu_dai_name = "media-cpu-dai",
> > +		.codec_dai_name = "snd-soc-dummy-dai",
> > +		.codec_name = "snd-soc-dummy",
> > +		.platform_name = "sst-mfld-platform",
> > +		.nonatomic = true,
> > +		.dynamic = 1,
> > +		.dpcm_playback = 1,
> > +		.dpcm_capture = 1,
> > +		.ops = &byt_cht_cx2072x_aif1_ops,
> > +	},
> > +	[MERR_DPCM_DEEP_BUFFER] = {
> > +		.name = "Deep-Buffer Audio Port",
> > +		.stream_name = "Deep-Buffer Audio",
> > +		.cpu_dai_name = "deepbuffer-cpu-dai",
> > +		.codec_dai_name = "snd-soc-dummy-dai",
> > +		.codec_name = "snd-soc-dummy",
> > +		.platform_name = "sst-mfld-platform",
> > +		.nonatomic = true,
> > +		.dynamic = 1,
> > +		.dpcm_playback = 1,
> > +		.ops = &byt_cht_cx2072x_aif1_ops,
> > +	},
> > +	/* back ends */
> > +	{
> > +		.name = "SSP2-Codec",
> > +		.id = 1,
> > +		.cpu_dai_name = "ssp2-port",
> > +		.platform_name = "sst-mfld-platform",
> > +		.no_pcm = 1,
> > +		.codec_dai_name = "cx2072x-hifi",
> > +		.codec_name = "i2c-14F10720:00",
> > +		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
> > +					      | SND_SOC_DAIFMT_CBS_CFS,
> > +		.init = byt_cht_cx2072x_init,
> > +		.be_hw_params_fixup = byt_cht_cx2072x_fixup,
> > +		.nonatomic = true,
> 
> I can't recall if this .atomic is needed or not for back-ends.

If FE requires it, BE must set it, too, in general.


> > +	{
> > +		.id = "14F10720",
> > +		.drv_name = "bytcht_cx2072x",
> > +		.fw_filename = "intel/fw_sst_0f28.bin",
> > +		.board = "bytcht_cx2072x",
> > +		/* .sof_fw_filename = "sof-byt.ri", */
> > +		/* .sof_tplg_filename = "sof-byt-cx2072x.tplg", */
> > +	},
> >   #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
> >   	/*
> >   	 * This is always last in the table so that it is selected only when
> > diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
> > index deafd87cc764..69ecbb88c171 100644
> > --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c
> > +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
> > @@ -169,6 +169,14 @@ struct snd_soc_acpi_mach  snd_soc_acpi_intel_cherrytrail_machines[] = {
> >   		.sof_fw_filename = "sof-cht.ri",
> >   		.sof_tplg_filename = "sof-cht-rt5651.tplg",
> >   	},
> > +	{
> > +		.id = "14F10720",
> > +		.drv_name = "bytcht_cx2072x",
> > +		.fw_filename = "intel/fw_sst_22a8.bin",
> > +		.board = "bytcht_cx2072x",
> > +		/* .sof_fw_filename = "sof-cht.ri", */
> > +		/* .sof_tplg_filename = "sof-cht-cx2072x.tplg", */
> > +	},
> 
> I'd uncomment those two ACPI machine parts. There is not risk unless
> SOF is actually enabled.

OK, will do that.


Thanks for a quick review!

Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/2] ASoC: Intel: Add machine driver for CX2072X on BYT/CHT platforms
  2019-04-23 19:39     ` Takashi Iwai
@ 2019-04-24 13:08       ` Takashi Iwai
  0 siblings, 0 replies; 20+ messages in thread
From: Takashi Iwai @ 2019-04-24 13:08 UTC (permalink / raw)
  To: Pierre-Louis Bossart; +Cc: alsa-devel, Mark Brown

On Tue, 23 Apr 2019 21:39:50 +0200,
Takashi Iwai wrote:
> 
> > > +static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd,
> > > +				 struct snd_pcm_hw_params *params)
> > > +{
> > > +	struct snd_interval *rate =
> > > +		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
> > > +	struct snd_interval *channels =
> > > +		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
> > > +	int ret;
> > > +
> > > +	/* The DSP will covert the FE rate to 48k, stereo, 24bits */
> > > +	rate->min = rate->max = 48000;
> > > +	channels->min = channels->max = 2;
> > > +
> > > +	/* set SSP2 to 24-bit */
> > > +	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
> > > +
> > > +	/*
> > > +	 * Default mode for SSP configuration is TDM 4 slot, override config
> > > +	 * with explicit setting to I2S 2ch 24-bit. The word length is set with
> > > +	 * dai_set_tdm_slot() since there is no other API exposed
> > > +	 */
> > > +	ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
> > > +				SND_SOC_DAIFMT_I2S     |
> > > +				SND_SOC_DAIFMT_NB_NF   |
> > > +				SND_SOC_DAIFMT_CBS_CFS);
> > > +	if (ret < 0) {
> > > +		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
> > > +	if (ret < 0) {
> > > +		dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	snd_soc_dai_set_bclk_ratio(rtd->codec_dai, 50);
> > 
> > that part would be problematic for SOF. IIRC we put all clock-related
> > stuff in the init, and ignore the fixups to use topology-based
> > information instead. If this call to _set_bclk_ratio can be moved to
> > the init it's more future-proof. Is there a reason to do this here in
> > the fixup?
> 
> No particular reason from my side.  I just took over your code ;)
> 
> That is, it'd be appreciated if you can give a fixup patch that can be
> applied on top.

OK, I tried just moving snd_soc_dai_set_bclk_ratio() into the init
callback, and it seems working fine.  The fix will be included in v2
patch.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-04-23 14:13 ` [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC Takashi Iwai
@ 2019-04-27 17:59   ` Mark Brown
  2019-05-02  7:04     ` Takashi Iwai
  0 siblings, 1 reply; 20+ messages in thread
From: Mark Brown @ 2019-04-27 17:59 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Pierre-Louis Bossart


[-- Attachment #1.1: Type: text/plain, Size: 5640 bytes --]

On Tue, Apr 23, 2019 at 04:13:35PM +0200, Takashi Iwai wrote:

> +/*
> + * register patch.
> + */
> +static const struct reg_sequence cx2072x_patch[] = {
> +	{ 0x71A4, 0x080 }, /* DC offset Calibration		*/
> +	{ 0x7328, 0x65f }, /* disable the PA			*/
> +	{ 0x71a8, 0x289 }, /* Set the spkeaer output gain	*/
> +	{ 0x7310, 0xf05 },
> +	{ 0x7290, 0x380 },
> +	{ 0x7328, 0xb90 },
> +	{ 0x7124, 0x001 }, /* Enable 30 Hz High pass filter	*/
> +	{ 0x718c, 0x300 }, /* Disable PCBEEP pad		*/
> +	{ 0x731c, 0x100 }, /* Disable SnM mode			*/
> +	{ 0x641c, 0x020 }, /* Enable PortD input		*/
> +	{ 0x0458, 0x040 }, /* Enable GPIO7 pin for button	*/
> +	{ 0x0464, 0x040 }, /* Enable UM for GPIO7		*/
> +	{ 0x0420, 0x080 }, /* Enable button response		*/
> +	{ 0x7230, 0x0c4 }, /* Enable headset button		*/
> +	{ 0x7200, 0x415 }, /* Power down class-D during idle	*/
> +	{ 0x6e04, 0x00f }, /* Enable I2S TX			*/
> +	{ 0x6e08, 0x00f }, /* Enable I2S RX			*/
> +};

This looks *very* much like board configuration rather than a patch -
there's no kind of test bit and the comments talk specifically about
things like gain settings and pad configuration which look very board
specific.  Register patches are supposed to be for things like early
revisions of the chip which have different register defaults or magic
sequences that vendors tell you to run on startup, usually to tune test
registers.

> +/* return register size */
> +static unsigned int cx2072x_register_size(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {

This is *not* going to play well with regmap...

> +static int cx2072x_reg_bulk_write(struct snd_soc_component *codec,
> +				  unsigned int reg,
> +				  const void *val, size_t val_count)
> +{
> +	struct i2c_client *client = to_i2c_client(codec->dev);
> +	struct device *dev = &client->dev;
> +	u8 buf[2 + CX2072X_MAX_EQ_COEFF];
> +	int ret;
> +
> +	if (val_count > CX2072X_MAX_EQ_COEFF) {
> +		dev_err(dev,
> +			"cx2072x_reg_bulk_write failed, writing count = %d\n",
> +			(int)val_count);
> +		return -EINVAL;
> +	}

So this only works for the EQ registers?  If so there should be
validation to confirm that it only gets used for them.  If not there
should be handling for register size variation issues.

> +#define cx2072x_plbk_eq_en_info		snd_ctl_boolean_mono_info

Why not just use the function directly rather than hiding it?

> +	/* do nothing if the value is the same */
> +	if (memcmp(cache, param, CX2072X_PLBK_EQ_COEF_LEN))
> +		goto unlock;

Is this test not inverted - shouldn't we be skipping if memcmp returns
0?

> +	SOC_DOUBLE_R_TLV("DAC1 Volume", CX2072X_DAC1_AMP_GAIN_LEFT,
> +			 CX2072X_DAC1_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv),
> +	SOC_DOUBLE_R("DAC1 Mute Switch", CX2072X_DAC1_AMP_GAIN_LEFT,
> +		     CX2072X_DAC1_AMP_GAIN_RIGHT, 7,  1, 0),

Should just be DAC1 Switch so it gets joined up with DAC1 Volume in UIs
and the "Mute" is redundant anyway.

> +int snd_soc_cx2072x_enable_jack_detect(struct snd_soc_component *codec)
> +{
> +	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
> +	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
> +
> +	/* No-sticky input type */
> +	regmap_write(cx2072x->regmap, CX2072X_GPIO_STICKY_MASK, 0x1f);
> +
> +	/* Use GPOI0 as interrupt pin */
> +	regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);

This isn't board specific is it?

> +int snd_soc_cx2072x_get_jack_state(struct snd_soc_component *codec)
> +{
> +	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
> +	unsigned int jack;
> +	unsigned int type = 0;
> +	int state = 0;
> +	bool need_cache_bypass =
> +		snd_soc_component_get_bias_level(codec) == SND_SOC_BIAS_OFF;
> +
> +	if (need_cache_bypass)
> +		regcache_cache_only(cx2072x->regmap, false);

This looks funky and racy - what's going on here?  If the register map
is live and usable why is it in cache only mode?

> +	dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n",
> +		type, state);
> +	return state;
> +}
> +EXPORT_SYMBOL_GPL(snd_soc_cx2072x_get_jack_state);

Why is this symbol exported?

> +static void cx2072x_shutdown(struct snd_pcm_substream *substream,
> +			     struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_component *codec = dai->component;
> +	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
> +
> +	/* shutdown codec */
> +	regcache_cache_only(cx2072x->regmap, false);
> +	regmap_write(cx2072x->regmap, CX2072X_PORTA_POWER_STATE, 3);
> +	regmap_write(cx2072x->regmap, CX2072X_PORTB_POWER_STATE, 3);
> +	regmap_write(cx2072x->regmap, CX2072X_PORTC_POWER_STATE, 3);
> +	regmap_write(cx2072x->regmap, CX2072X_PORTD_POWER_STATE, 3);
> +	regmap_write(cx2072x->regmap, CX2072X_PORTE_POWER_STATE, 3);
> +	regmap_write(cx2072x->regmap, CX2072X_PORTG_POWER_STATE, 3);
> +	regmap_write(cx2072x->regmap, CX2072X_MIXER_POWER_STATE, 3);
> +	regmap_write(cx2072x->regmap, CX2072X_ADC1_POWER_STATE, 3);
> +	regmap_write(cx2072x->regmap, CX2072X_ADC2_POWER_STATE, 3);
> +	regmap_write(cx2072x->regmap, CX2072X_DAC1_POWER_STATE, 3);
> +	regmap_write(cx2072x->regmap, CX2072X_DAC2_POWER_STATE, 3);

Why isn't DAPM powering things off for us?

> +	/* use flat eq by default */
> +	for (ch = 0 ; ch < 2 ; ch++) {
> +		for (band = 0; band < CX2072X_PLBK_EQ_BAND_NUM; band++) {
> +			cx2072x->plbk_eq[ch][band][1] = 64;
> +			cx2072x->plbk_eq[ch][band][10] = 3;
> +		}
> +	}

Why not use the register defaults?

> +static bool cx2072x_readable_register(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case CX2072X_VENDOR_ID:

It's weird that this is a long way away from the other regmap functions.

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-04-27 17:59   ` Mark Brown
@ 2019-05-02  7:04     ` Takashi Iwai
  2019-05-02  7:52       ` Takashi Iwai
       [not found]       ` <20190503064729.GF14916@sirena.org.uk>
  0 siblings, 2 replies; 20+ messages in thread
From: Takashi Iwai @ 2019-05-02  7:04 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Pierre-Louis Bossart

On Sat, 27 Apr 2019 19:59:38 +0200,
Mark Brown wrote:
> 
> On Tue, Apr 23, 2019 at 04:13:35PM +0200, Takashi Iwai wrote:
> 
> > +/*
> > + * register patch.
> > + */
> > +static const struct reg_sequence cx2072x_patch[] = {
> > +	{ 0x71A4, 0x080 }, /* DC offset Calibration		*/
> > +	{ 0x7328, 0x65f }, /* disable the PA			*/
> > +	{ 0x71a8, 0x289 }, /* Set the spkeaer output gain	*/
> > +	{ 0x7310, 0xf05 },
> > +	{ 0x7290, 0x380 },
> > +	{ 0x7328, 0xb90 },
> > +	{ 0x7124, 0x001 }, /* Enable 30 Hz High pass filter	*/
> > +	{ 0x718c, 0x300 }, /* Disable PCBEEP pad		*/
> > +	{ 0x731c, 0x100 }, /* Disable SnM mode			*/
> > +	{ 0x641c, 0x020 }, /* Enable PortD input		*/
> > +	{ 0x0458, 0x040 }, /* Enable GPIO7 pin for button	*/
> > +	{ 0x0464, 0x040 }, /* Enable UM for GPIO7		*/
> > +	{ 0x0420, 0x080 }, /* Enable button response		*/
> > +	{ 0x7230, 0x0c4 }, /* Enable headset button		*/
> > +	{ 0x7200, 0x415 }, /* Power down class-D during idle	*/
> > +	{ 0x6e04, 0x00f }, /* Enable I2S TX			*/
> > +	{ 0x6e08, 0x00f }, /* Enable I2S RX			*/
> > +};
> 
> This looks *very* much like board configuration rather than a patch -
> there's no kind of test bit and the comments talk specifically about
> things like gain settings and pad configuration which look very board
> specific.  Register patches are supposed to be for things like early
> revisions of the chip which have different register defaults or magic
> sequences that vendors tell you to run on startup, usually to tune test
> registers.

OK, will replace with the straight regmap_multi_reg_write().


> > +/* return register size */
> > +static unsigned int cx2072x_register_size(struct device *dev, unsigned int reg)
> > +{
> > +	switch (reg) {
> 
> This is *not* going to play well with regmap...

The code seems trying to unify the register access with the same
width, while the register size differs depending on the register
index.  So the read/write ops adjust it by checking this register size
*internally*.  IOW, it's hiding the internal register size difference
from the outside so that regmap can work.

(Actually the zero check in the caller side is bogus -- this function
never returns an error.)

I'm going to rewrite the whole these tables.  Maybe better to have a
single register attribute table.

> > +static int cx2072x_reg_bulk_write(struct snd_soc_component *codec,
> > +				  unsigned int reg,
> > +				  const void *val, size_t val_count)
> > +{
> > +	struct i2c_client *client = to_i2c_client(codec->dev);
> > +	struct device *dev = &client->dev;
> > +	u8 buf[2 + CX2072X_MAX_EQ_COEFF];
> > +	int ret;
> > +
> > +	if (val_count > CX2072X_MAX_EQ_COEFF) {
> > +		dev_err(dev,
> > +			"cx2072x_reg_bulk_write failed, writing count = %d\n",
> > +			(int)val_count);
> > +		return -EINVAL;
> > +	}
> 
> So this only works for the EQ registers?

I guess this was meant only as a sanity check.  It's an internal
function that is called from a few helper functions, and the check
above is to assure not overflowing the temporary buffer on the stack.
It can be simply WARN_ON() like
	if (WARN_ON(val_count + 2 > sizeof(buf)))
		return -EINVAL;

> If so there should be
> validation to confirm that it only gets used for them.  If not there
> should be handling for register size variation issues.
> 
> > +#define cx2072x_plbk_eq_en_info		snd_ctl_boolean_mono_info
> 
> Why not just use the function directly rather than hiding it?

Just a standard idiom.  Can be replaced if preferred.


> > +	/* do nothing if the value is the same */
> > +	if (memcmp(cache, param, CX2072X_PLBK_EQ_COEF_LEN))
> > +		goto unlock;
> 
> Is this test not inverted - shouldn't we be skipping if memcmp returns
> 0?

Oh yeah, indeed, it looks like a real bug.  This shows that I have
never played with the EQ stuff :)

(IOW, I'm fine to drop the untested EQ stuff, but someone else might
 play with it...)


> > +	SOC_DOUBLE_R_TLV("DAC1 Volume", CX2072X_DAC1_AMP_GAIN_LEFT,
> > +			 CX2072X_DAC1_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv),
> > +	SOC_DOUBLE_R("DAC1 Mute Switch", CX2072X_DAC1_AMP_GAIN_LEFT,
> > +		     CX2072X_DAC1_AMP_GAIN_RIGHT, 7,  1, 0),
> 
> Should just be DAC1 Switch so it gets joined up with DAC1 Volume in UIs
> and the "Mute" is redundant anyway.

OK.

> > +int snd_soc_cx2072x_enable_jack_detect(struct snd_soc_component *codec)
> > +{
> > +	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
> > +	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
> > +
> > +	/* No-sticky input type */
> > +	regmap_write(cx2072x->regmap, CX2072X_GPIO_STICKY_MASK, 0x1f);
> > +
> > +	/* Use GPOI0 as interrupt pin */
> > +	regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);
> 
> This isn't board specific is it?

I have no idea.  It's been so from the original code, and there
doesn't seem any other hardware implementations.


> > +int snd_soc_cx2072x_get_jack_state(struct snd_soc_component *codec)
> > +{
> > +	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
> > +	unsigned int jack;
> > +	unsigned int type = 0;
> > +	int state = 0;
> > +	bool need_cache_bypass =
> > +		snd_soc_component_get_bias_level(codec) == SND_SOC_BIAS_OFF;
> > +
> > +	if (need_cache_bypass)
> > +		regcache_cache_only(cx2072x->regmap, false);
> 
> This looks funky and racy - what's going on here?  If the register map
> is live and usable why is it in cache only mode?

Not to read the register while the chip is turned off, I suppose.
The jack detection in ASoC is anyway a bit funky, especially when
involved with PM...


> > +	dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n",
> > +		type, state);
> > +	return state;
> > +}
> > +EXPORT_SYMBOL_GPL(snd_soc_cx2072x_get_jack_state);
> 
> Why is this symbol exported?

It's called from the machine driver.
snd_soc_jack_add_gpios() is called in the machine driver side, and it
needs the jack_status_check callback that calls this function.


> > +static void cx2072x_shutdown(struct snd_pcm_substream *substream,
> > +			     struct snd_soc_dai *dai)
> > +{
> > +	struct snd_soc_component *codec = dai->component;
> > +	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
> > +
> > +	/* shutdown codec */
> > +	regcache_cache_only(cx2072x->regmap, false);
> > +	regmap_write(cx2072x->regmap, CX2072X_PORTA_POWER_STATE, 3);
> > +	regmap_write(cx2072x->regmap, CX2072X_PORTB_POWER_STATE, 3);
> > +	regmap_write(cx2072x->regmap, CX2072X_PORTC_POWER_STATE, 3);
> > +	regmap_write(cx2072x->regmap, CX2072X_PORTD_POWER_STATE, 3);
> > +	regmap_write(cx2072x->regmap, CX2072X_PORTE_POWER_STATE, 3);
> > +	regmap_write(cx2072x->regmap, CX2072X_PORTG_POWER_STATE, 3);
> > +	regmap_write(cx2072x->regmap, CX2072X_MIXER_POWER_STATE, 3);
> > +	regmap_write(cx2072x->regmap, CX2072X_ADC1_POWER_STATE, 3);
> > +	regmap_write(cx2072x->regmap, CX2072X_ADC2_POWER_STATE, 3);
> > +	regmap_write(cx2072x->regmap, CX2072X_DAC1_POWER_STATE, 3);
> > +	regmap_write(cx2072x->regmap, CX2072X_DAC2_POWER_STATE, 3);
> 
> Why isn't DAPM powering things off for us?

Honestly speaking, no idea.  It seems to have been added some revision
of the original code.  Let's try to rip off.

> > +	/* use flat eq by default */
> > +	for (ch = 0 ; ch < 2 ; ch++) {
> > +		for (band = 0; band < CX2072X_PLBK_EQ_BAND_NUM; band++) {
> > +			cx2072x->plbk_eq[ch][band][1] = 64;
> > +			cx2072x->plbk_eq[ch][band][10] = 3;
> > +		}
> > +	}
> 
> Why not use the register defaults?

Because it'll become too messy for put flatten array values?
The initialization using loop makes more sense in such a case, IMO.


> > +static bool cx2072x_readable_register(struct device *dev, unsigned int reg)
> > +{
> > +	switch (reg) {
> > +	case CX2072X_VENDOR_ID:
> 
> It's weird that this is a long way away from the other regmap functions.

Agreed, will move to a former place.


Thanks!

Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-05-02  7:04     ` Takashi Iwai
@ 2019-05-02  7:52       ` Takashi Iwai
  2019-05-03  6:52         ` Mark Brown
       [not found]       ` <20190503064729.GF14916@sirena.org.uk>
  1 sibling, 1 reply; 20+ messages in thread
From: Takashi Iwai @ 2019-05-02  7:52 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Pierre-Louis Bossart

On Thu, 02 May 2019 09:04:06 +0200,
Takashi Iwai wrote:
> 
> > > +int snd_soc_cx2072x_get_jack_state(struct snd_soc_component *codec)
> > > +{
> > > +	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
> > > +	unsigned int jack;
> > > +	unsigned int type = 0;
> > > +	int state = 0;
> > > +	bool need_cache_bypass =
> > > +		snd_soc_component_get_bias_level(codec) == SND_SOC_BIAS_OFF;
> > > +
> > > +	if (need_cache_bypass)
> > > +		regcache_cache_only(cx2072x->regmap, false);
> > 
> > This looks funky and racy - what's going on here?  If the register map
> > is live and usable why is it in cache only mode?
> 
> Not to read the register while the chip is turned off, I suppose.

Actually other way round: the codec driver tries to avoid the whole
register access while the chip is in BIAS_OFF state.  OTOH, the jack
state check is still required even in that state, so it flips the
cache-only flag temporarily at reading the jack detect bit.

I guess we may remove the cache-only behavior, although this is a
nice-to-have thing.


Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-05-02  7:52       ` Takashi Iwai
@ 2019-05-03  6:52         ` Mark Brown
  0 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2019-05-03  6:52 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Pierre-Louis Bossart


[-- Attachment #1.1: Type: text/plain, Size: 955 bytes --]

On Thu, May 02, 2019 at 09:52:40AM +0200, Takashi Iwai wrote:
> Takashi Iwai wrote:

> > Not to read the register while the chip is turned off, I suppose.

> Actually other way round: the codec driver tries to avoid the whole
> register access while the chip is in BIAS_OFF state.  OTOH, the jack
> state check is still required even in that state, so it flips the
> cache-only flag temporarily at reading the jack detect bit.

> I guess we may remove the cache-only behavior, although this is a
> nice-to-have thing.

No, it should be removed - it's buggy and racy.  Devices should only be
going into cache only mode if they can't be physically accessed,
otherwise you end up with incoherent things like this that create bugs.
If the driver is actually able to access the register map, actively
doing so and therefore needs to try flipping cache only mode on and off
without coordinating with the rest of the device it isn't really in a
cache only mode.

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
       [not found]       ` <20190503064729.GF14916@sirena.org.uk>
@ 2019-05-03  7:18         ` Takashi Iwai
       [not found]           ` <20190506042625.GK14916@sirena.org.uk>
  2019-05-03  8:05         ` Takashi Iwai
  1 sibling, 1 reply; 20+ messages in thread
From: Takashi Iwai @ 2019-05-03  7:18 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Pierre-Louis Bossart

On Fri, 03 May 2019 08:47:29 +0200,
Mark Brown wrote:
> 
> On Thu, May 02, 2019 at 09:04:06AM +0200, Takashi Iwai wrote:
> > Mark Brown wrote:
> > > On Tue, Apr 23, 2019 at 04:13:35PM +0200, Takashi Iwai wrote:
> 
> > > This looks *very* much like board configuration rather than a patch -
> > > there's no kind of test bit and the comments talk specifically about
> > > things like gain settings and pad configuration which look very board
> > > specific.  Register patches are supposed to be for things like early
> > > revisions of the chip which have different register defaults or magic
> > > sequences that vendors tell you to run on startup, usually to tune test
> > > registers.
> 
> > OK, will replace with the straight regmap_multi_reg_write().
> 
> That's probably not addressing the issue, a lot of that stuff just
> doesn't seem like it should be in some fixed configuration table at all.

So what's your alternative suggestion?

> > > > +#define cx2072x_plbk_eq_en_info		snd_ctl_boolean_mono_info
> 
> > > Why not just use the function directly rather than hiding it?
> 
> > Just a standard idiom.  Can be replaced if preferred.
> 
> Please.
> 
> > > > +int snd_soc_cx2072x_enable_jack_detect(struct snd_soc_component *codec)
> > > > +{
> > > > +	struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec);
> > > > +	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
> > > > +
> > > > +	/* No-sticky input type */
> > > > +	regmap_write(cx2072x->regmap, CX2072X_GPIO_STICKY_MASK, 0x1f);
> > > > +
> > > > +	/* Use GPOI0 as interrupt pin */
> > > > +	regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);
> 
> > > This isn't board specific is it?
> 
> > I have no idea.  It's been so from the original code, and there
> > doesn't seem any other hardware implementations.
> 
> Oh, joy.  What's the story here?  Do you have a datasheet for the part?

Not at all.  I just refreshed the already submitted patches since I
have a laptop with the codec.  I tried to contact Conexant, but in
vain, so I decided to submit the renewed one.

> > The jack detection in ASoC is anyway a bit funky, especially when
> > involved with PM...
> 
> What do you mean here?  I'm not aware of any issues and the systems I've
> worked with seemed robust...

There are tons of different ways of implementation for jack controls,
with different API usages.  IOW, no consistency.

> > > > +	dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n",
> > > > +		type, state);
> > > > +	return state;
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(snd_soc_cx2072x_get_jack_state);
> 
> > > Why is this symbol exported?
> 
> > It's called from the machine driver.
> > snd_soc_jack_add_gpios() is called in the machine driver side, and it
> > needs the jack_status_check callback that calls this function.
> 
> That code shouldn't be in the machine driver, the CODEC driver should
> request any interrupts it needs itself.

The similar things are done on many other Intel SST board drivers.
The current patch just follows the pattern.

> > > > +	/* use flat eq by default */
> > > > +	for (ch = 0 ; ch < 2 ; ch++) {
> > > > +		for (band = 0; band < CX2072X_PLBK_EQ_BAND_NUM; band++) {
> > > > +			cx2072x->plbk_eq[ch][band][1] = 64;
> > > > +			cx2072x->plbk_eq[ch][band][10] = 3;
> > > > +		}
> > > > +	}
> 
> > > Why not use the register defaults?
> 
> > Because it'll become too messy for put flatten array values?
> > The initialization using loop makes more sense in such a case, IMO.
> 
> No, that's not the question.  The question is why there is any
> initialization at all?

Again, no idea.  These are likely no default values of the hardware
registers, and we need to set up some.  I *guess* ditto for the
initial register table in the above, too.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
       [not found]       ` <20190503064729.GF14916@sirena.org.uk>
  2019-05-03  7:18         ` Takashi Iwai
@ 2019-05-03  8:05         ` Takashi Iwai
  1 sibling, 0 replies; 20+ messages in thread
From: Takashi Iwai @ 2019-05-03  8:05 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Pierre-Louis Bossart

On Fri, 03 May 2019 08:47:29 +0200,
Mark Brown wrote:
> 
> > > > +#define cx2072x_plbk_eq_en_info		snd_ctl_boolean_mono_info
> 
> > > Why not just use the function directly rather than hiding it?
> 
> > Just a standard idiom.  Can be replaced if preferred.
> 
> Please.

BTW, a good reason for the style above is that it makes code more
undrestandable.  For defining a ctl element, typically we define three
callback functions, info, get and put, in that order:

static int foo_info()
{
	....
}

static int foo_get()
{
	....
}

static int foo_put()
{
	...
};

and often the actual snd_kcontrol_new table containing these callbacks
appears much later, where you'd need to scroll down a few screens to
read that point.

In the above, especially defining the info callback at the beginning
is important.  By reading foo_init() at first, readers can know which
type (int, boolean, enum, etc) and the number of elements to be used
in get/put callbacks beforehand.  It's a commonly seen mistake, for
example, that a wrong type (e.g. integer) is passed to info callback
while enum type is used in the get/put.

The #define above keeps this foo_info() definition while optimizing
with the standard helper.  If we drop this and set
snd_ctl_boolean_mono_info directly in the snd_kcontrol_new entry,
you'll have to go and back the screen just to take a look at the info
callback.

That's why I called it a standard idiom.  It's not strictly necessary,
but often help reading / reviewing the code.  Though, it's just
another bikeshed theme, so I'm not sticking with this style.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
       [not found]           ` <20190506042625.GK14916@sirena.org.uk>
@ 2019-05-06  9:55             ` Takashi Iwai
  2019-05-06 14:05               ` Mark Brown
  0 siblings, 1 reply; 20+ messages in thread
From: Takashi Iwai @ 2019-05-06  9:55 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Pierre-Louis Bossart

On Mon, 06 May 2019 06:26:25 +0200,
Mark Brown wrote:
> 
> On Fri, May 03, 2019 at 09:18:26AM +0200, Takashi Iwai wrote:
> > On Fri, 03 May 2019 08:47:29 +0200,
> > Mark Brown wrote:
> > > On Thu, May 02, 2019 at 09:04:06AM +0200, Takashi Iwai wrote:
> > > > Mark Brown wrote:
> 
> > > > > This looks *very* much like board configuration rather than a patch -
> > > > > there's no kind of test bit and the comments talk specifically about
> 
> > > > OK, will replace with the straight regmap_multi_reg_write().
> 
> > > That's probably not addressing the issue, a lot of that stuff just
> > > doesn't seem like it should be in some fixed configuration table at all.
> 
> > So what's your alternative suggestion?
> 
> Make it either platform data or runtime controls depending on the
> specific thing being configured.  Though...
> 
> > > Oh, joy.  What's the story here?  Do you have a datasheet for the part?
> 
> > Not at all.  I just refreshed the already submitted patches since I
> > have a laptop with the codec.  I tried to contact Conexant, but in
> > vain, so I decided to submit the renewed one.
> 
> ...this is not going to help.  At the very least it needs to be
> abundantly clear to anyone reading the code tht any magic register write
> sequences like this are being done the way they are due to a badly
> documented part from an unsupportive vendor rather than because it's a
> good idea in case someone decides to use the driver as an example.  Some
> comments or something.  For safety the bits that look board specific
> like the pinmuxing should ideally be behind DMI checks, though if nobody
> else ever bought the CODEC that's probably not so important.

Fair enough.  I'm going to put more comments on that part.

Since this is the only code for this codec on the net and all existing
platforms (Intel devices, RPi extensions and else) seem working with
it -- i.e. all these depend on the same init sequence more or less, so
splitting to platform data won't help much in practice.

> > > > The jack detection in ASoC is anyway a bit funky, especially when
> > > > involved with PM...
> 
> > > What do you mean here?  I'm not aware of any issues and the systems I've
> > > worked with seemed robust...
> 
> > There are tons of different ways of implementation for jack controls,
> > with different API usages.  IOW, no consistency.
> 
> The board registration interfaces do vary as the hardware isn't very
> standard so there's a bunch of things you can do for configuration
> (though there is a generic interface for the 90% case, the main
> limitation these days on using it is Intel's lack of any sensible
> firmware interface).  I'm not aware of any issues with power management
> though?

Yeah, I understand the various use cases and coverage for a wide range
of hardware implementations, but the lack of the documentation
is... not a thing we can be proud of.

Regarding the PM, my statement wasn't straight, sorry.  The fact is
that ASoC manages so many different levels of (a sort of) "PM".  We
have the Linux device-object system and runtime PMs, and we have BIAS
level control.  The machine needed the jack handling via ACPI GPIO
IRQ, that is outside the codec, which looks rather like a rare case, 
and  I had hard time to look for the right choice of the API usage.

> > > > > > +EXPORT_SYMBOL_GPL(snd_soc_cx2072x_get_jack_state);
> 
> > > > > Why is this symbol exported?
> 
> > > > It's called from the machine driver.
> > > > snd_soc_jack_add_gpios() is called in the machine driver side, and it
> > > > needs the jack_status_check callback that calls this function.
> 
> > > That code shouldn't be in the machine driver, the CODEC driver should
> > > request any interrupts it needs itself.
> 
> > The similar things are done on many other Intel SST board drivers.
> > The current patch just follows the pattern.
> 
> The only example I can find is the max98095 which it just plain buggy in
> this regard, that example shouldn't be followed - it looks like it's
> just plain an oversight in review.  Frankly the export was done by me
> and looks like I was just rushing to fix up a build breakage.

This is rather a question how generic the codec driver should be
written.  I changed the code in v5 patchset to embed the jack_gpio
stuff inside the codec driver side rather than the machine driver, so
the whole exported functions can be reduced now.  But, of course, it
slightly gives more implicit assumption about the hardware
implementations, too.  Though, the existing code seems to have already
fixed gpio / pin setups, so the other setups wouldn't have worked, in
anyway.

> > > No, that's not the question.  The question is why there is any
> > > initialization at all?
> 
> > Again, no idea.  These are likely no default values of the hardware
> > registers, and we need to set up some.  I *guess* ditto for the
> > initial register table in the above, too.
> 
> Or it's just a taste setting someone had for their particular system.
> With the earlier register default table an awful lot of it is fairly
> obvious board specific configuration, doing things like pinmuxing which
> will vary with the board.
> 
> It's unlikely there are *no* default values - you'd kind of have to try
> to have a specific register range like this with genuinely floating
> values.  Given that the code for configuring the EQ was broken to IIRC
> never take effect I'd not be 100% surprisd if someone couldn't figure
> out why their settings weren't taking effect and just bodged something
> directly in the driver.

Actually I'm fine to drop the whole EQ stuff that brings lots of black
magic.  Certainly it'll benefit us for code simplification.  Let's
see.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-05-06  9:55             ` Takashi Iwai
@ 2019-05-06 14:05               ` Mark Brown
  2019-05-06 15:26                 ` Takashi Iwai
  0 siblings, 1 reply; 20+ messages in thread
From: Mark Brown @ 2019-05-06 14:05 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Pierre-Louis Bossart


[-- Attachment #1.1: Type: text/plain, Size: 4141 bytes --]

On Mon, May 06, 2019 at 11:55:37AM +0200, Takashi Iwai wrote:
> Mark Brown wrote:
> > On Fri, May 03, 2019 at 09:18:26AM +0200, Takashi Iwai wrote:

> > comments or something.  For safety the bits that look board specific
> > like the pinmuxing should ideally be behind DMI checks, though if nobody
> > else ever bought the CODEC that's probably not so important.

> Since this is the only code for this codec on the net and all existing
> platforms (Intel devices, RPi extensions and else) seem working with
> it -- i.e. all these depend on the same init sequence more or less, so
> splitting to platform data won't help much in practice.

Yeah, like I say that's a bit more nice to have given that it looks like
there's basically one system using the thing.

> > The board registration interfaces do vary as the hardware isn't very
> > standard so there's a bunch of things you can do for configuration
> > (though there is a generic interface for the 90% case, the main
> > limitation these days on using it is Intel's lack of any sensible
> > firmware interface).  I'm not aware of any issues with power management
> > though?

> Yeah, I understand the various use cases and coverage for a wide range
> of hardware implementations, but the lack of the documentation
> is... not a thing we can be proud of.

The bits that vary are driver specific interfaces so any sort of general
documentation is pretty hard.  The core stuff all at least has kerneldoc
which is about average here.

> Regarding the PM, my statement wasn't straight, sorry.  The fact is
> that ASoC manages so many different levels of (a sort of) "PM".  We
> have the Linux device-object system and runtime PMs, and we have BIAS
> level control.  The machine needed the jack handling via ACPI GPIO
> IRQ, that is outside the codec, which looks rather like a rare case, 
> and  I had hard time to look for the right choice of the API usage.

The reason you're not finding much handling for interrupt GPIOs is that
if something is an interrupt it should be going through the kernel
interrupt interfaces, even if the pin that is providing the interrupt
has GPIO support the code should just ignore that and let the frameworks
deal with things.  The only exception that comes up is that there's a
few devices that manually emulate level triggered interrupts with GPIOs
and edge triggered interrupts for cases where controllers only provide
level triggers, unfortunately nobody had the time/enthusiasm to push
that emulation into the interrupt core but fortunately few hardware
designers are implementing edge triggered interrupt controllers these
days.

> This is rather a question how generic the codec driver should be
> written.  I changed the code in v5 patchset to embed the jack_gpio
> stuff inside the codec driver side rather than the machine driver, so
> the whole exported functions can be reduced now.  But, of course, it
> slightly gives more implicit assumption about the hardware
> implementations, too.  Though, the existing code seems to have already
> fixed gpio / pin setups, so the other setups wouldn't have worked, in
> anyway.

Like I say if the device is using the fact that the pin is a GPIO
there's quite likely something wrong - that shouldn't be something that
the user of an interrupt should need to see.

> > It's unlikely there are *no* default values - you'd kind of have to try
> > to have a specific register range like this with genuinely floating
> > values.  Given that the code for configuring the EQ was broken to IIRC
> > never take effect I'd not be 100% surprisd if someone couldn't figure
> > out why their settings weren't taking effect and just bodged something
> > directly in the driver.

> Actually I'm fine to drop the whole EQ stuff that brings lots of black
> magic.  Certainly it'll benefit us for code simplification.  Let's
> see.

Probably worth checking to make sure the default EQ setup isn't too
awful (though I guess the EQ is just turned off by default so it'll just
be an uncorrected speaker/headphone which should be fine if not ideal).

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-05-06 14:05               ` Mark Brown
@ 2019-05-06 15:26                 ` Takashi Iwai
  2019-05-08  8:10                   ` Mark Brown
  0 siblings, 1 reply; 20+ messages in thread
From: Takashi Iwai @ 2019-05-06 15:26 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Pierre-Louis Bossart

On Mon, 06 May 2019 16:05:06 +0200,
Mark Brown wrote:
> 
> > This is rather a question how generic the codec driver should be
> > written.  I changed the code in v5 patchset to embed the jack_gpio
> > stuff inside the codec driver side rather than the machine driver, so
> > the whole exported functions can be reduced now.  But, of course, it
> > slightly gives more implicit assumption about the hardware
> > implementations, too.  Though, the existing code seems to have already
> > fixed gpio / pin setups, so the other setups wouldn't have worked, in
> > anyway.
> 
> Like I say if the device is using the fact that the pin is a GPIO
> there's quite likely something wrong - that shouldn't be something that
> the user of an interrupt should need to see.

Yeah, unfortunately we have no reference, so the only chance would be
to test with another board that has a different setup.

> > > It's unlikely there are *no* default values - you'd kind of have to try
> > > to have a specific register range like this with genuinely floating
> > > values.  Given that the code for configuring the EQ was broken to IIRC
> > > never take effect I'd not be 100% surprisd if someone couldn't figure
> > > out why their settings weren't taking effect and just bodged something
> > > directly in the driver.
> 
> > Actually I'm fine to drop the whole EQ stuff that brings lots of black
> > magic.  Certainly it'll benefit us for code simplification.  Let's
> > see.
> 
> Probably worth checking to make sure the default EQ setup isn't too
> awful (though I guess the EQ is just turned off by default so it'll just
> be an uncorrected speaker/headphone which should be fine if not ideal).

A good point.  I checked the status now, and found that actually the
EQ and others won't appear in the normal playback / capture via HiFi
route, but only through DSP.  Since the normal usages with UCM profile
goes only via HiFi, EQ/DRC don't play any role.  So it's OK to remove
them, at least, for normal PC usages, as well as RPi HiFi extensions,
as it seems.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-05-06 15:26                 ` Takashi Iwai
@ 2019-05-08  8:10                   ` Mark Brown
  2019-05-08  8:16                     ` Takashi Iwai
  0 siblings, 1 reply; 20+ messages in thread
From: Mark Brown @ 2019-05-08  8:10 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Pierre-Louis Bossart


[-- Attachment #1.1: Type: text/plain, Size: 717 bytes --]

On Mon, May 06, 2019 at 05:26:51PM +0200, Takashi Iwai wrote:
> Mark Brown wrote:

> > Like I say if the device is using the fact that the pin is a GPIO
> > there's quite likely something wrong - that shouldn't be something that
> > the user of an interrupt should need to see.

> Yeah, unfortunately we have no reference, so the only chance would be
> to test with another board that has a different setup.

The GPIO/IRQ equivalence thing should just be a purely Linux internal
thing - looking at the driver it appears that there's no need to look at
the GPIO status, Linux can tell if something is plugged in purely by
reading the chip registers so the jack status function could directly be
the interrupt handler.

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-05-08  8:10                   ` Mark Brown
@ 2019-05-08  8:16                     ` Takashi Iwai
  2019-05-08  8:59                       ` Mark Brown
  0 siblings, 1 reply; 20+ messages in thread
From: Takashi Iwai @ 2019-05-08  8:16 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Pierre-Louis Bossart

On Wed, 08 May 2019 10:10:06 +0200,
Mark Brown wrote:
> 
> On Mon, May 06, 2019 at 05:26:51PM +0200, Takashi Iwai wrote:
> > Mark Brown wrote:
> 
> > > Like I say if the device is using the fact that the pin is a GPIO
> > > there's quite likely something wrong - that shouldn't be something that
> > > the user of an interrupt should need to see.
> 
> > Yeah, unfortunately we have no reference, so the only chance would be
> > to test with another board that has a different setup.
> 
> The GPIO/IRQ equivalence thing should just be a purely Linux internal
> thing - looking at the driver it appears that there's no need to look at
> the GPIO status, Linux can tell if something is plugged in purely by
> reading the chip registers so the jack status function could directly be
> the interrupt handler.

But it can't see the button status or detect the headset type without
reading the codec's magic registers...  So some help from the codec
side is required, and the irq handler isn't in the codec side because
there is no i2c irq assigned but only ACPI gpio, as far as I checked.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-05-08  8:16                     ` Takashi Iwai
@ 2019-05-08  8:59                       ` Mark Brown
  2019-05-08  9:16                         ` Takashi Iwai
  0 siblings, 1 reply; 20+ messages in thread
From: Mark Brown @ 2019-05-08  8:59 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Pierre-Louis Bossart


[-- Attachment #1.1: Type: text/plain, Size: 938 bytes --]

On Wed, May 08, 2019 at 10:16:06AM +0200, Takashi Iwai wrote:
> Mark Brown wrote:

> > The GPIO/IRQ equivalence thing should just be a purely Linux internal
> > thing - looking at the driver it appears that there's no need to look at
> > the GPIO status, Linux can tell if something is plugged in purely by
> > reading the chip registers so the jack status function could directly be
> > the interrupt handler.

> But it can't see the button status or detect the headset type without
> reading the codec's magic registers...  So some help from the codec
> side is required, and the irq handler isn't in the codec side because

The whole thing should be in the CODEC side.

> there is no i2c irq assigned but only ACPI gpio, as far as I checked.

You can map through an ACPI GPIO to an interrupt without worrying about
the fact that it's mapped as a GPIO in ACPI IIRC - if you can't there's
lots of other drivers are going to have issues.

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-05-08  8:59                       ` Mark Brown
@ 2019-05-08  9:16                         ` Takashi Iwai
  2019-05-08 10:10                           ` Mark Brown
  0 siblings, 1 reply; 20+ messages in thread
From: Takashi Iwai @ 2019-05-08  9:16 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Pierre-Louis Bossart

On Wed, 08 May 2019 10:59:53 +0200,
Mark Brown wrote:
> 
> On Wed, May 08, 2019 at 10:16:06AM +0200, Takashi Iwai wrote:
> > Mark Brown wrote:
> 
> > > The GPIO/IRQ equivalence thing should just be a purely Linux internal
> > > thing - looking at the driver it appears that there's no need to look at
> > > the GPIO status, Linux can tell if something is plugged in purely by
> > > reading the chip registers so the jack status function could directly be
> > > the interrupt handler.
> 
> > But it can't see the button status or detect the headset type without
> > reading the codec's magic registers...  So some help from the codec
> > side is required, and the irq handler isn't in the codec side because
> 
> The whole thing should be in the CODEC side.
> 
> > there is no i2c irq assigned but only ACPI gpio, as far as I checked.
> 
> You can map through an ACPI GPIO to an interrupt without worrying about
> the fact that it's mapped as a GPIO in ACPI IIRC - if you can't there's
> lots of other drivers are going to have issues.

But what gives a benefit?  It needs more plumbing between codec and
machine driver.  The i2c probe doesn't give the irq, so you'd need and
extra stuff to enable the irq in a different route if you'd need to
implement the handler in the codec driver.

Actually, my latest patchset already eliminates the exported stuff by
moving to set_jack callback like some other drivers do.  If you have
an idea for further simplification / fixes, let me know.

I haven't submitted because of the merge window.  The patch is found
at topic/soc-cx2072x-5.2 branch, the commit is
  https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git/commit/?h=topic/soc-cx2072x-5.2&id=ca7f4eee5ecbefcf347f5a4984c0a17629360186


thanks,

Takashi

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC
  2019-05-08  9:16                         ` Takashi Iwai
@ 2019-05-08 10:10                           ` Mark Brown
  0 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2019-05-08 10:10 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Pierre-Louis Bossart


[-- Attachment #1.1: Type: text/plain, Size: 1848 bytes --]

On Wed, May 08, 2019 at 11:16:18AM +0200, Takashi Iwai wrote:
> Mark Brown wrote:

> > > there is no i2c irq assigned but only ACPI gpio, as far as I checked.

> > You can map through an ACPI GPIO to an interrupt without worrying about
> > the fact that it's mapped as a GPIO in ACPI IIRC - if you can't there's
> > lots of other drivers are going to have issues.

> But what gives a benefit?  It needs more plumbing between codec and
> machine driver.  The i2c probe doesn't give the irq, so you'd need and
> extra stuff to enable the irq in a different route if you'd need to
> implement the handler in the codec driver.

Handling the interrupt entirely within the CODEC driver *reduces* the
coupling between the CODEC and machine drivers, making it more possible
to use the standard set_jack() interface and generic machine drivers
with the CODEC drivers.

Actually looking a little I think you may need some ACPI specific
parsing code in the probe function to look up the GPIO but that should
just be like all the other DMI quirking stuff, hidden in the CODEC
driver.  It's really sad how bad a job Intel have done of making
firmware interfaces for their audio hardware.  I had thought people had
managed to hide all that stuff but I'm not seeing the code right now.

> Actually, my latest patchset already eliminates the exported stuff by
> moving to set_jack callback like some other drivers do.  If you have
> an idea for further simplification / fixes, let me know.

> I haven't submitted because of the merge window.  The patch is found
> at topic/soc-cx2072x-5.2 branch, the commit is
>   https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git/commit/?h=topic/soc-cx2072x-5.2&id=ca7f4eee5ecbefcf347f5a4984c0a17629360186

I'm on a train with intermittent network connectivity right now (and
getting near to my destination too)...

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2019-05-08 10:10 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-23 14:13 [PATCH 0/2] ASoC: CX2072X codec support (revised) Takashi Iwai
2019-04-23 14:13 ` [PATCH 1/2] ASoC: Add support for Conexant CX2072X CODEC Takashi Iwai
2019-04-27 17:59   ` Mark Brown
2019-05-02  7:04     ` Takashi Iwai
2019-05-02  7:52       ` Takashi Iwai
2019-05-03  6:52         ` Mark Brown
     [not found]       ` <20190503064729.GF14916@sirena.org.uk>
2019-05-03  7:18         ` Takashi Iwai
     [not found]           ` <20190506042625.GK14916@sirena.org.uk>
2019-05-06  9:55             ` Takashi Iwai
2019-05-06 14:05               ` Mark Brown
2019-05-06 15:26                 ` Takashi Iwai
2019-05-08  8:10                   ` Mark Brown
2019-05-08  8:16                     ` Takashi Iwai
2019-05-08  8:59                       ` Mark Brown
2019-05-08  9:16                         ` Takashi Iwai
2019-05-08 10:10                           ` Mark Brown
2019-05-03  8:05         ` Takashi Iwai
2019-04-23 14:13 ` [PATCH 2/2] ASoC: Intel: Add machine driver for CX2072X on BYT/CHT platforms Takashi Iwai
2019-04-23 19:20   ` Pierre-Louis Bossart
2019-04-23 19:39     ` Takashi Iwai
2019-04-24 13:08       ` Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.