[PATCH 2/2] ASoC: codecs: cmx655: Add CML's CMX655D codec

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Nikola Jelic <nikola.jelic83@xxxxxxxxx>
---
 sound/soc/codecs/Kconfig      |   16 +
 sound/soc/codecs/Makefile     |    6 +
 sound/soc/codecs/cmx655-i2c.c |   63 ++
 sound/soc/codecs/cmx655-spi.c |   63 ++
 sound/soc/codecs/cmx655.c     | 1080 +++++++++++++++++++++++++++++++++
 sound/soc/codecs/cmx655.h     |  146 +++++
 6 files changed, 1374 insertions(+)
 create mode 100644 sound/soc/codecs/cmx655-i2c.c
 create mode 100644 sound/soc/codecs/cmx655-spi.c
 create mode 100644 sound/soc/codecs/cmx655.c
 create mode 100644 sound/soc/codecs/cmx655.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index ee35f3aa5521..d06b4eb8f1e5 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -63,6 +63,7 @@ config SND_SOC_ALL_CODECS
 	imply SND_SOC_BT_SCO
 	imply SND_SOC_BD28623
 	imply SND_SOC_CHV3_CODEC
+	imply SND_SOC_CMX655
 	imply SND_SOC_CQ0093VC
 	imply SND_SOC_CROS_EC_CODEC
 	imply SND_SOC_CS35L32
@@ -747,6 +748,21 @@ config SND_SOC_CPCAP
 	tristate "Motorola CPCAP codec"
 	depends on MFD_CPCAP || COMPILE_TEST
 
+config SND_SOC_CMX655D
+	tristate
+
+config SND_SOC_CMX655D_I2C
+       tristate "CMX655D codec (I2C)"
+       depends on I2C
+       select SND_SOC_CMX655D
+       select REGMAP_I2C
+
+config SND_SOC_CMX655D_SPI
+       tristate "CMX655D codec (SPI)"
+       depends on SPI
+       select SND_SOC_CMX655D
+       select REGMAP_SPI
+
 config SND_SOC_CQ0093VC
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index d7ad795603c1..130d29e2e574 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -58,6 +58,9 @@ snd-soc-aw88399-y := aw88399.o
 snd-soc-bd28623-y := bd28623.o
 snd-soc-bt-sco-y := bt-sco.o
 snd-soc-chv3-codec-y := chv3-codec.o
+snd-soc-cmx655-y := cmx655.o
+snd-soc-cmx655-i2c-y := cmx655-i2c.o
+snd-soc-cmx655-spi-y := cmx655-spi.o
 snd-soc-cpcap-y := cpcap.o
 snd-soc-cq93vc-y := cq93vc.o
 snd-soc-cros-ec-codec-y := cros_ec_codec.o
@@ -475,6 +478,9 @@ obj-$(CONFIG_SND_SOC_AW88399)	+= snd-soc-aw88399.o
 obj-$(CONFIG_SND_SOC_BD28623)	+= snd-soc-bd28623.o
 obj-$(CONFIG_SND_SOC_BT_SCO)	+= snd-soc-bt-sco.o
 obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o
+obj-$(CONFIG_SND_SOC_CMX655D) += snd-soc-cmx655.o
+obj-$(CONFIG_SND_SOC_CMX655D_I2C) += snd-soc-cmx655-i2c.o
+obj-$(CONFIG_SND_SOC_CMX655D_SPI) += snd-soc-cmx655-spi.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CPCAP)	+= snd-soc-cpcap.o
 obj-$(CONFIG_SND_SOC_CROS_EC_CODEC)	+= snd-soc-cros-ec-codec.o
diff --git a/sound/soc/codecs/cmx655-i2c.c b/sound/soc/codecs/cmx655-i2c.c
new file mode 100644
index 000000000000..9ea6008c375a
--- /dev/null
+++ b/sound/soc/codecs/cmx655-i2c.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+
+#include "cmx655.h"
+
+static int cmx655_i2c_probe(struct i2c_client *client)
+{
+	int ret;
+
+	ret =
+	    cmx655_common_register_component(&client->dev,
+					     devm_regmap_init_i2c(client,
+								  &cmx655_regmap),
+					     client->irq);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"%s: Register component failed %d\n", __func__, ret);
+	}
+
+	return ret;
+};
+
+static void cmx655_i2c_remove(struct i2c_client *client)
+{
+	struct cmx655_data *cmx655_data = i2c_get_clientdata(client);
+
+	cmx655_common_unregister_component(&client->dev);
+	gpiod_set_value_cansleep(cmx655_data->reset_gpio, 1);
+};
+
+static const struct i2c_device_id cmx655_device_id[] = {
+	{ "cmx655", 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, cmx655_device_id);
+
+static const struct of_device_id cmx655_of_match[] = {
+	{.compatible = "cml,cmx655d" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, cmx655_of_match);
+
+static struct i2c_driver cmx655_i2c_driver = {
+	.probe = cmx655_i2c_probe,
+	.remove = cmx655_i2c_remove,
+	.driver = {
+		   .name = "cmx655",
+		   .of_match_table = cmx655_of_match,
+		    },
+	.id_table = cmx655_device_id
+};
+
+module_i2c_driver(cmx655_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CMX655 driver, I2C adapter");
+MODULE_AUTHOR("CML");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cmx655-spi.c b/sound/soc/codecs/cmx655-spi.c
new file mode 100644
index 000000000000..b868bf50b615
--- /dev/null
+++ b/sound/soc/codecs/cmx655-spi.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/of.h>
+
+#include "cmx655.h"
+
+static int cmx655_spi_probe(struct spi_device *spi)
+{
+	int ret;
+
+	ret =
+	    cmx655_common_register_component(&spi->dev,
+					     devm_regmap_init_spi(spi,
+								  &cmx655_regmap),
+					     spi->irq);
+	if (ret < 0) {
+		dev_err(&spi->dev,
+			"%s: Register component failed %d\n", __func__, ret);
+	}
+
+	return ret;
+};
+
+static void cmx655_spi_remove(struct spi_device *spi)
+{
+	struct cmx655_data *cmx655_data = spi_get_drvdata(spi);
+
+	cmx655_common_unregister_component(&spi->dev);
+	gpiod_set_value_cansleep(cmx655_data->reset_gpio, 1);
+};
+
+static const struct spi_device_id cmx655_device_id[] = {
+	{ "cmx655" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(spi, cmx655_device_id);
+
+static const struct of_device_id cmx655_of_match[] = {
+	{.compatible = "cml,cmx655d" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, cmx655_of_match);
+
+static struct spi_driver cmx655_spi_driver = {
+	.probe = cmx655_spi_probe,
+	.remove = cmx655_spi_remove,
+	.driver = {
+		   .name = "cmx655",
+		   .of_match_table = cmx655_of_match,
+		    },
+	.id_table = cmx655_device_id
+};
+
+module_spi_driver(cmx655_spi_driver);
+
+MODULE_DESCRIPTION("ASoC CMX655 driver, SPI adapter");
+MODULE_AUTHOR("CML");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cmx655.c b/sound/soc/codecs/cmx655.c
new file mode 100644
index 000000000000..d0f0b91d7c68
--- /dev/null
+++ b/sound/soc/codecs/cmx655.c
@@ -0,0 +1,1080 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/version.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "cmx655.h"
+
+static const struct reg_default cmx655_reg_defaults[] = {
+	{ CMX655_ISR, 0x00 },
+	{ CMX655_ISM, 0x00 },
+	{ CMX655_ISE, 0x00 },
+	{ CMX655_CLKCTRL, 0x00 },
+	{ CMX655_RDIVHI, 0x00 },
+	{ CMX655_RDIVLO, 0x00 },
+	{ CMX655_NDIVHI, 0x00 },
+	{ CMX655_NDIVLO, 0x00 },
+	{ CMX655_PLLCTRL, 0x00 },
+	{ CMX655_SAICTRL, 0x00 },
+	{ CMX655_SAIMUX, 0x00 },
+
+	{ CMX655_RVF, 0x00 },
+	{ CMX655_LDCTRL, 0x00 },
+	{ CMX655_RDCTRL, 0x00 },
+	{ CMX655_LEVEL, 0x00 },
+
+	{ CMX655_NGCTRL, 0x00 },
+	{ CMX655_NGTIME, 0x00 },
+	{ CMX655_NGLSTAT, 0x00 },
+	{ CMX655_NGRSTAT, 0x00 },
+
+	{ CMX655_PVF, 0x00 },
+	{ CMX655_PREAMP, 0x00 },
+	{ CMX655_VOLUME, 0x00 },
+	{ CMX655_ALCCTRL, 0x00 },
+	{ CMX655_ALCTIME, 0x00 },
+	{ CMX655_ALCGAIN, 0x00 },
+	{ CMX655_ALCSTAT, 0x00 },
+	{ CMX655_DST, 0x00 },
+	{ CMX655_CPR, 0x00 },
+
+	{ CMX655_SYSCTRL, 0x00 },
+	{ CMX655_COMMAND, 0x00 },
+	{ 0x34, 0x29 },
+	{ 0x35, 0x40 },
+	{ 0x36, 0x80 },
+	{ 0x37, 0x80 },
+
+};
+
+static const struct regmap_range cmx655_valid_reg[] = {
+	{ 0x00, 0x0a },
+	{ 0x0c, 0x0f },
+	{ 0x1c, 0x1f },
+	{ 0x28, 0x30 },
+	{ 0x32, 0x33 }
+};
+
+static const struct regmap_range cmx655_read_only_reg[] = {
+	{ 0x00, 0x00 },
+	{ 0x1e, 0x1f },
+	{ 0x2e, 0x2e }
+};
+
+static const struct regmap_range cmx655_write_only_reg[] = {
+	{ 0x33, 0x33 }
+};
+
+static const struct regmap_access_table cmx655_readable_reg = {
+	.yes_ranges = cmx655_valid_reg,
+	.n_yes_ranges = ARRAY_SIZE(cmx655_valid_reg),
+	.no_ranges = cmx655_write_only_reg,
+	.n_no_ranges = ARRAY_SIZE(cmx655_write_only_reg)
+};
+
+static const struct regmap_access_table cmx655_writeable_reg = {
+	.yes_ranges = cmx655_valid_reg,
+	.n_yes_ranges = ARRAY_SIZE(cmx655_valid_reg),
+	.no_ranges = cmx655_read_only_reg,
+	.n_no_ranges = ARRAY_SIZE(cmx655_read_only_reg)
+};
+
+static bool cmx655_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CMX655_COMMAND:
+	case CMX655_SYSCTRL:
+	case CMX655_ISR:
+	case CMX655_ISE:
+		return true;
+	default:
+		return false;
+	}
+};
+
+const struct regmap_config cmx655_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = CMX655_COMMAND,
+
+	.volatile_reg = cmx655_volatile_reg,
+	.wr_table = &cmx655_writeable_reg,
+	.rd_table = &cmx655_readable_reg,
+
+	.reg_defaults = cmx655_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(cmx655_reg_defaults),
+	.cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_GPL(cmx655_regmap);
+
+static int cmx655_start_sys_clk(struct snd_soc_component *component)
+{
+	int ret;
+	int i;
+	unsigned int val;
+
+	val = snd_soc_component_read(component, CMX655_ISR);
+	ret = snd_soc_component_write(component, CMX655_COMMAND,
+				      CMX655_CMD_CLOCK_START);
+	if (ret < 0) {
+		dev_err(component->dev,
+			"Failed to write start clock command %d\n", ret);
+		return ret;
+	}
+	for (i = 0; i < 100; i++) {
+		val = snd_soc_component_read(component, CMX655_ISR);
+		if (val & CMX655_ISR_CLKRDY)
+			break;
+	}
+	if (i == 100)
+		ret = -EIO;
+	return ret;
+}
+
+static int cmx655_stop_sys_clk(struct snd_soc_component *component)
+{
+	return snd_soc_component_write(component, CMX655_COMMAND,
+				       CMX655_CMD_CLOCK_STOP);
+}
+
+/**
+ * cmx655_get_sys_clk_config(): Get the clock configuration.
+ * @clk_id: Clock source setting as defined in cmx655.h
+ * @primary_mode: Non-zero if the CMX655 is the DAI primary
+ * @sr_setting: Setting for sample rate 0 to 3
+ * @clk_src: pointer for storing clock source (PLLREF, PLLSEL and
+ *           CLKSEL bits)
+ * @rdiv: pointer for storing PLL's RDIV value (13 bits)
+ * @ndiv: pointer for storing PLL's NDIV value (13 bits)
+ * @pll_ctrl: pointer for storing PLLCTRL register value (8 bits)
+ *
+ * Get the clock setup for the system clock based on clock Id, DAI primary mode
+ * and sample rate.
+ *
+ * Return: 0 for success, negative value otherwise
+ */
+static int cmx655_get_sys_clk_config(int clk_id,
+				     int primary_mode,
+				     int sr_setting,
+				     int *clk_src,
+				     int *rdiv, int *ndiv, int *pll_ctrl)
+{
+	if (clk_id == CMX655_SYSCLK_AUTO) {
+		if (primary_mode != 0)
+			clk_id = CMX655_SYSCLK_RCLK;
+		else
+			clk_id = CMX655_SYSCLK_LRCLK;
+	}
+	*rdiv = 0;
+	*ndiv = 0;
+	*pll_ctrl = 0;
+	switch (clk_id) {
+	case (CMX655_SYSCLK_RCLK):
+		*clk_src = CMX655_CLKCTRL_CLRSRC_RCLK;
+		break;
+	case (CMX655_SYSCLK_LPO):
+		*clk_src = CMX655_CLKCTRL_CLRSRC_LPO;
+		break;
+	case (CMX655_SYSCLK_LRCLK):
+		*clk_src = CMX655_CLKCTRL_CLRSRC_LRCLK;
+		*rdiv = 1;
+		switch (sr_setting) {
+		case (CMX655_CLKCTRL_SR_8K):
+			*ndiv = 3072;
+			*pll_ctrl = (0 << CMX655_PLLCTRL_LFILT_SHIFT) ||
+			    (3 << CMX655_PLLCTRL_CPI_SHIFT);
+			break;
+		case (CMX655_CLKCTRL_SR_16K):
+			*ndiv = 1536;
+			*pll_ctrl = (0 << CMX655_PLLCTRL_LFILT_SHIFT) ||
+			    (3 << CMX655_PLLCTRL_CPI_SHIFT);
+			break;
+		case (CMX655_CLKCTRL_SR_32K):
+			*ndiv = 768;
+			*pll_ctrl = (12 << CMX655_PLLCTRL_LFILT_SHIFT) ||
+			    (3 << CMX655_PLLCTRL_CPI_SHIFT);
+			break;
+		case (CMX655_CLKCTRL_SR_48K):
+			*ndiv = 512;
+			*pll_ctrl = (12 << CMX655_PLLCTRL_LFILT_SHIFT) ||
+			    (3 << CMX655_PLLCTRL_CPI_SHIFT);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+};
+
+/**
+ * cmx655_setup_rate(): Setup the clock and sample rate.
+ * @component: sound component to set up
+ * @hw_params: hardware parameters holding the sample rate
+ *
+ * Setup the clock and sample rate. The clock needs to be setup at the same
+ * time as the sample rate in case we are using the serial port as the clock
+ * source.
+ * If the clock source the serial port then the PLL settings are dependent on
+ * the sample rate.
+ *
+ * Return: 0 for success, negative for error.
+ */
+static int cmx655_setup_rate(struct snd_soc_component *component,
+			     struct snd_pcm_hw_params *hw_params)
+{
+	int ret;
+	struct cmx655_data *cmx655_data =
+	    snd_soc_component_get_drvdata(component);
+	struct cmx655_dai_data *cmx655_dai_data = &cmx655_data->dai_data;
+	int srate = params_rate(hw_params);
+	int primary_mode;
+	int srate_setting;
+	int clk_src;
+	int rdiv;
+	int ndiv;
+	int pll_ctrl;
+	int sys_ctrl;
+	int vol;
+
+	primary_mode = snd_soc_component_read(component, CMX655_SAICTRL);
+	primary_mode = primary_mode & CMX655_SAI_MSTR;
+
+	switch (srate) {
+	case 8000:
+		srate_setting = CMX655_CLKCTRL_SR_8K;
+		break;
+	case 16000:
+		srate_setting = CMX655_CLKCTRL_SR_16K;
+		break;
+	case 32000:
+		srate_setting = CMX655_CLKCTRL_SR_32K;
+		break;
+	case 48000:
+		srate_setting = CMX655_CLKCTRL_SR_48K;
+		break;
+	default:
+		dev_err(component->dev, "Unsupported rate %d\n", srate);
+		return -EINVAL;
+	}
+
+	ret = cmx655_get_sys_clk_config(cmx655_dai_data->sys_clk,
+					primary_mode, srate_setting,
+					&clk_src, &rdiv, &ndiv, &pll_ctrl);
+	if (ret < 0) {
+		dev_err(component->dev,
+			"Failed to get system clock settings %i\n", ret);
+	}
+	if (clk_src == CMX655_CLKCTRL_CLRSRC_LRCLK) {
+		dev_dbg(component->dev,
+			"Using LRCLK as clk source. Using LPO for setup then switch over to LRCLK later");
+		cmx655_dai_data->clk_src = clk_src;
+		cmx655_dai_data->best_clk_running = false;
+		clk_src = CMX655_CLKCTRL_CLRSRC_LPO;
+	} else {
+		cmx655_dai_data->best_clk_running = true;
+	}
+	if (snd_soc_component_test_bits(component, CMX655_CLKCTRL,
+					CMX655_CLKCTRL_CLRSRC_MASK |
+					CMX655_CLKCTRL_SR_MASK,
+					clk_src | srate_setting) == 0) {
+		dev_dbg(component->dev, "Rate Setup correct skipping setup\n");
+		return 0;
+	}
+	/* Turn all inputs and outputs off before disabling clock */
+	sys_ctrl = snd_soc_component_read(component, CMX655_SYSCTRL);
+	snd_soc_component_update_bits(component, CMX655_SYSCTRL,
+				      CMX655_SYSCTRL_MICR |
+				      CMX655_SYSCTRL_MICL |
+				      CMX655_SYSCTRL_PAMP |
+				      CMX655_SYSCTRL_LOUT, 0);
+
+	cmx655_stop_sys_clk(component);
+	/* Set new sample rate and clock source */
+	snd_soc_component_update_bits(component, CMX655_CLKCTRL,
+				      CMX655_CLKCTRL_CLRSRC_MASK |
+				      CMX655_CLKCTRL_SR_MASK,
+				      clk_src | srate_setting);
+	/* Set new RDIV */
+	snd_soc_component_update_bits(component, CMX655_RDIVHI,
+				      0x1F, rdiv >> 8);
+	snd_soc_component_update_bits(component, CMX655_RDIVLO,
+				      0xFF, rdiv & 0xFF);
+	/* Set new NDIV */
+	snd_soc_component_update_bits(component, CMX655_NDIVHI,
+				      0x1F, ndiv >> 8);
+	snd_soc_component_update_bits(component, CMX655_NDIVLO,
+				      0xFF, ndiv & 0xFF);
+	/* Set new PLLCTRL */
+	snd_soc_component_update_bits(component, CMX655_PLLCTRL,
+				      0xFF, pll_ctrl & 0xFF);
+	/* Now we can re-start the clock */
+	ret = cmx655_start_sys_clk(component);
+	if (ret < 0) {
+		dev_err(component->dev,
+			"System clock failed to start %i\n", ret);
+		return ret;
+	}
+	/* Turn anything on that we turned off */
+	if ((sys_ctrl & (CMX655_SYSCTRL_MICR | CMX655_SYSCTRL_MICL)) > 0) {
+		snd_soc_component_update_bits(component, CMX655_SYSCTRL,
+					      CMX655_SYSCTRL_MICR |
+					      CMX655_SYSCTRL_MICL, sys_ctrl);
+		/* Wait for filters to settle */
+		if (snd_soc_component_test_bits
+		    (component, CMX655_RVF, CMX655_VF_DCBLOCK,
+		     CMX655_VF_DCBLOCK) == 0) {
+			/* DC blocking filter off, Shorter wait */
+			usleep_range(3500, 4000);
+		} else {
+			/* This allows time for Mics and DC blocking filter to settle */
+			msleep(320);
+		}
+	}
+	if ((sys_ctrl & (CMX655_SYSCTRL_PAMP | CMX655_SYSCTRL_LOUT)) > 0) {
+		/* Store volume */
+		vol = snd_soc_component_read(component, CMX655_VOLUME);
+		/* Lower volume with smooth on */
+		snd_soc_component_write(component, CMX655_VOLUME, 0x80);
+		snd_soc_component_update_bits(component, CMX655_SYSCTRL,
+					      CMX655_SYSCTRL_PAMP |
+					      CMX655_SYSCTRL_LOUT, sys_ctrl);
+		/* Restore volume */
+		snd_soc_component_write(component, CMX655_VOLUME, vol);
+	}
+
+	return 0;
+};
+
+static int cmx655_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_component *component = dai->component;
+	unsigned int reg_val = 0;
+	/* Set primary bit */
+	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		reg_val = reg_val | CMX655_SAI_MSTR;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		dev_err(component->dev,
+			"Unsupported digital audio interface primary mode\n");
+		return -EINVAL;
+	}
+	/* Set data format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		reg_val = reg_val | CMX655_SAI_DLY | CMX655_SAI_POL;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		break;
+	default:
+		dev_err(component->dev,
+			"Unsupported digital audio interface data format\n");
+		return -EINVAL;
+	}
+	/* Change invert bits if required */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		reg_val = reg_val ^ CMX655_SAI_POL;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		reg_val = reg_val | CMX655_SAI_BINV;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		reg_val = (reg_val | CMX655_SAI_BINV) ^ CMX655_SAI_POL;
+		break;
+	default:
+		dev_err(component->dev,
+			"Unknown digital audio interface polarity\n");
+		return -EINVAL;
+	}
+
+	/* Write value to codec */
+	snd_soc_component_write(component, CMX655_SAICTRL, reg_val);
+	return 0;
+}
+
+static int cmx655_set_dai_sysclk(struct snd_soc_dai *dai,
+				 int clk_id, unsigned int freq, int dir)
+{
+	struct cmx655_data *cmx655_data =
+	    snd_soc_component_get_drvdata(dai->component);
+	struct cmx655_dai_data *cmx655_dai_data = &cmx655_data->dai_data;
+
+	switch (clk_id) {
+	case CMX655_SYSCLK_MIN ... CMX655_SYSCLK_MAX:
+		break;
+	default:
+		return -EINVAL;
+	}
+	cmx655_dai_data->sys_clk = clk_id;
+
+	return 0;
+}
+
+static const unsigned int cmx655_rate_vals[] = {
+	8000, 16000, 32000, 48000
+};
+
+static const struct snd_pcm_hw_constraint_list cmx655_rate_constraint = {
+	.count = ARRAY_SIZE(cmx655_rate_vals),
+	.list = cmx655_rate_vals,
+};
+
+static int cmx655_dai_startup(struct snd_pcm_substream *stream,
+			      struct snd_soc_dai *dai)
+{
+	int ret = 0;
+
+	ret = snd_pcm_hw_constraint_list(stream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_RATE,
+					 &cmx655_rate_constraint);
+	return ret;
+}
+
+static int cmx655_dai_prepare(struct snd_pcm_substream *stream,
+			      struct snd_soc_dai *dai)
+{
+	int ret = 0;
+	struct snd_soc_component *component = dai->component;
+	struct cmx655_data *cmx655_data =
+	    snd_soc_component_get_drvdata(component);
+	struct cmx655_dai_data *cmx655_dai_data = &cmx655_data->dai_data;
+
+	if (!cmx655_dai_data->best_clk_running) {
+		/* Stop the clock change over to the correct one an start it again */
+		ret = cmx655_stop_sys_clk(component);
+		if (ret < 0) {
+			dev_err(component->dev, "Failed to stop clock %d\n",
+				ret);
+			goto get_out;
+		}
+		ret =
+		    snd_soc_component_update_bits(component, CMX655_CLKCTRL,
+						  CMX655_CLKCTRL_CLRSRC_MASK,
+						  cmx655_dai_data->clk_src);
+		if (ret < 0) {
+			dev_err(component->dev,
+				"Failed to set new clock setup %d\n", ret);
+			goto get_out;
+		}
+		ret = cmx655_start_sys_clk(component);
+		if (ret < 0) {
+			dev_warn(component->dev, "Failed to restart clock\n");
+			ret = 0;
+			/*
+			 * This will happen if the CPU driver does not start the LRCLK
+			 * until the last point.
+			 * For now we will assume the clock will start
+			 */
+		}
+		cmx655_dai_data->best_clk_running = true;
+	}
+get_out:
+	return ret;
+};
+
+static int cmx655_hw_params(struct snd_pcm_substream *stream,
+			    struct snd_pcm_hw_params *hw_params,
+			    struct snd_soc_dai *dai)
+{
+	int ret;
+	struct snd_soc_component *component = dai->component;
+	struct cmx655_data *cmx655_data =
+	    snd_soc_component_get_drvdata(component);
+	struct cmx655_dai_data *cmx655_dai_data = &cmx655_data->dai_data;
+	unsigned int enabled_streams = cmx655_dai_data->enabled_streams;
+
+	/* Setup clock and sample rate */
+	ret = cmx655_setup_rate(component, hw_params);
+	if (ret < 0) {
+		dev_err(component->dev, "Failed to set rates %d\n",
+			ret);
+		return ret;
+	}
+
+	/* Set mono bit based on channel count */
+	if (params_channels(hw_params) == 1) {
+		dev_dbg(component->dev, "Switching into mono mode\n");
+		snd_soc_component_update_bits(component, CMX655_SAICTRL,
+					      CMX655_SAI_MONO, CMX655_SAI_MONO);
+	} else {
+		snd_soc_component_update_bits(component, CMX655_SAICTRL,
+					      CMX655_SAI_MONO, 0);
+	}
+
+	if (cmx655_data->irq)
+		cmx655_data->oc_cnt = 0;
+	if (enabled_streams == 0) {
+		dev_dbg(component->dev,
+			"First stream to enable, enabling SAI\n");
+		/*
+		 * If first stream to be enabled
+		 * Enable SAI (serial audio interface) port
+		 * We need it running before the platform starts.
+		 * to avoid I2S sync errors
+		 */
+		snd_soc_component_update_bits(component, CMX655_SYSCTRL,
+					      CMX655_SYSCTRL_SAI,
+					      CMX655_SYSCTRL_SAI);
+	} else {
+		dev_dbg(component->dev,
+			"Not first stream to enable, skipping SAI enable\n");
+	}
+
+	/* Inc enabled streams by 1 */
+	cmx655_dai_data->enabled_streams = enabled_streams + 1;
+
+	return ret;
+}
+
+static void cmx655_dai_shutdown(struct snd_pcm_substream *stream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct cmx655_data *cmx655_data =
+	    snd_soc_component_get_drvdata(component);
+	struct cmx655_dai_data *cmx655_dai_data = &cmx655_data->dai_data;
+	unsigned int enabled_streams = cmx655_dai_data->enabled_streams;
+
+	if (enabled_streams == 0) {
+		dev_dbg(component->dev,
+			"Shutdown called when SAI not running\n");
+		return;
+	}
+	enabled_streams = enabled_streams - 1;
+	cmx655_dai_data->enabled_streams = enabled_streams;
+	if (enabled_streams == 0) {
+		dev_dbg(component->dev,
+			"Last stream to disable, disabling SAI\n");
+		snd_soc_component_update_bits(component, CMX655_SYSCTRL,
+					      CMX655_SYSCTRL_SAI, 0);
+		cmx655_dai_data->best_clk_running = false;
+	} else {
+		dev_dbg(component->dev,
+			"Not last stream to disable, skipping SAI disable\n");
+	}
+}
+
+static const struct snd_soc_dai_ops cmx655_dai_ops = {
+	.set_sysclk = cmx655_set_dai_sysclk,
+	.set_fmt = cmx655_set_dai_fmt,
+
+	.startup = cmx655_dai_startup,
+	.prepare = cmx655_dai_prepare,
+	.hw_params = cmx655_hw_params,
+	.shutdown = cmx655_dai_shutdown,
+};
+
+static struct snd_soc_dai_driver cmx655_dai_driver = {
+	.name = "cmx655",
+	.playback = {
+		     .stream_name = "CMX655 Playback",
+		     .channels_min = 1,
+		     .channels_max = 2,
+		     .rates = CMX655_RATES,
+		     .formats = CMX655_FMTS,
+		      },
+	.capture = {
+		    .stream_name = "CMX655 Record",
+		    .channels_min = 1,
+		    .channels_max = 2,
+		    .rates = CMX655_RATES,
+		    .formats = CMX655_FMTS,
+		     },
+	.ops = &cmx655_dai_ops,
+	.symmetric_rate = 1
+};
+
+static irqreturn_t cmx655_irq_thread(int irq, void *data)
+{
+	struct snd_soc_component *component = data;
+	struct cmx655_data *cmx655_data =
+	    snd_soc_component_get_drvdata(component);
+	unsigned int status;
+
+	status = snd_soc_component_read(component, CMX655_ISR);
+	if (status == 0)
+		return IRQ_NONE;
+	/* Thermal protection event */
+	if (status & CMX655_ISR_THERM) {
+		dev_err(component->dev,
+			"CMX655 class-D over temperature detected\n");
+	}
+	/* Over current  event */
+	if (status & CMX655_ISR_AMPOC) {
+		dev_warn(component->dev,
+			 "CMX655 class-D over current detected\n");
+		if (cmx655_data->oc_cnt < cmx655_data->oc_cnt_max) {
+			/* Re enable class-D */
+			snd_soc_component_update_bits(component,
+						      CMX655_SYSCTRL,
+						      CMX655_SYSCTRL_PAMP,
+						      CMX655_SYSCTRL_PAMP);
+			if (cmx655_data->oc_cnt_max <= 10000)
+				cmx655_data->oc_cnt = cmx655_data->oc_cnt + 1;
+		} else {
+			/* Re enable count reached, do not try again */
+			dev_err(component->dev,
+				"Class-D over current restart attempts exceeded\n");
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+static int cmx655_component_probe(struct snd_soc_component *component)
+{
+	int ret;
+	struct cmx655_data *cmx655_data =
+	    snd_soc_component_get_drvdata(component);
+
+	ret = cmx655_start_sys_clk(component);
+	if (ret < 0) {
+		dev_err(component->dev, "Failed to start system clock %d\n",
+			ret);
+		goto codec_err;
+	}
+	cmx655_data->oc_cnt = 0;
+	if (cmx655_data->irq) {
+		ret =
+		    request_threaded_irq(cmx655_data->irq, NULL,
+					 cmx655_irq_thread, IRQF_ONESHOT,
+					 "cmx655", component);
+		if (ret < 0) {
+			dev_err(component->dev,
+				"Failed to setup interrupt %d\n", ret);
+			goto interrupt_err;
+		}
+		snd_soc_component_write(component, CMX655_ISM,
+					(CMX655_ISM_AMPOC | CMX655_ISM_THERM));
+	}
+
+	return 0;
+
+interrupt_err:
+codec_err:
+	return ret;
+}
+
+static void cmx655_component_remove(struct snd_soc_component *component)
+{
+	struct cmx655_data *cmx655_data =
+	    snd_soc_component_get_drvdata(component);
+	if (cmx655_data->irq) {
+		snd_soc_component_write(component, CMX655_ISM, 0);
+		free_irq(cmx655_data->irq, component);
+	}
+}
+
+static const DECLARE_TLV_DB_SCALE(cmx655_level, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cmx655_ng_thresh, -6300, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cmx655_vol, -9100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(cmx655_pre_amp, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(cmx655_dst_gain, -6200, 200, 0);
+static const DECLARE_TLV_DB_SCALE(cmx655_alc_gain, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cmx655_alc_thresh, -3100, 100, 0);
+
+static const char *const cmx655_ngratio_text[] = {
+	"1:2",
+	"1:3",
+	"1:4"
+};
+
+static SOC_ENUM_SINGLE_DECL(cmx655_ngratio_enum, CMX655_NGCTRL, 5,
+			    cmx655_ngratio_text);
+static const char *const cmx655_ngattack_text[] = {
+	"1.5ms",
+	"3ms",
+	"4.5ms",
+	"6ms",
+	"12ms",
+	"24ms",
+	"48ms",
+	"96ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(cmx655_ngattack_enum, CMX655_NGTIME, 4,
+			    cmx655_ngattack_text);
+static const char *const cmx655_ngrelease_text[] = {
+	"0.06s",
+	"0.12s",
+	"0.24s",
+	"0.48s",
+	"0.96s",
+	"1.92s",
+	"3.84s",
+	"7.68s"
+};
+
+static SOC_ENUM_SINGLE_DECL(cmx655_ngrelease_enum, CMX655_NGTIME, 0,
+			    cmx655_ngrelease_text);
+static const char *const cmx655_hpf_text[] = {
+	"Disabled",
+	"SRate/320",
+	"SRate/53.3",
+	"SRate/26.7"
+};
+
+static SOC_ENUM_SINGLE_DECL(cmx655_hpf_capture_enum, CMX655_RVF, 0,
+			    cmx655_hpf_text);
+static SOC_ENUM_SINGLE_DECL(cmx655_hpf_playback_enum, CMX655_PVF, 0,
+			    cmx655_hpf_text);
+static const char *const cmx655_companding_text[] = {
+	"u-Law",
+	"a-Law"
+};
+
+static SOC_ENUM_SINGLE_DECL(cmx655_companding_enum, CMX655_SAIMUX, 5,
+			    cmx655_companding_text);
+static const char *const cmx655_alc_ratio_text[] = {
+	"1.5:1",
+	"2:1",
+	"4:1",
+	"Inf:1"
+};
+
+static SOC_ENUM_SINGLE_DECL(cmx655_alc_ratio_enum, CMX655_ALCCTRL, 5,
+			    cmx655_alc_ratio_text);
+/* Note: The attack and release times are the same as the Noise gate's. */
+static SOC_ENUM_SINGLE_DECL(cmx655_alc_attack_enum, CMX655_ALCTIME, 4,
+			    cmx655_ngattack_text);
+static SOC_ENUM_SINGLE_DECL(cmx655_alc_release_enum, CMX655_ALCTIME, 0,
+			    cmx655_ngrelease_text);
+
+static const struct snd_kcontrol_new cmx655_snd_controls[] = {
+	/* Capture */
+	SOC_DOUBLE_TLV("Master Capture Volume", CMX655_LEVEL, 4, 0, 15, 0,
+		       cmx655_level),
+
+	SOC_SINGLE("Digital Capture Block Switch", CMX655_RVF,
+		   CMX655_VF_DCBLOCK_SHIFT,
+		   1, 0),
+
+	SOC_SINGLE("LPF Capture Switch", CMX655_RVF, 3, 1, 0),
+	SOC_ENUM("HPF Capture Switch", cmx655_hpf_capture_enum),
+	/* Noise gate */
+	SOC_SINGLE("Noise Gate Capture Switch", CMX655_NGCTRL, 7, 1, 0),
+	SOC_SINGLE_TLV("Noise Gate Threshold Capture Volume", CMX655_NGCTRL, 0, 31, 0,
+		       cmx655_ng_thresh),
+	SOC_ENUM("Noise Gate Ratio Capture Switch", cmx655_ngratio_enum),
+	SOC_ENUM("Noise Gate Attack Capture Switch", cmx655_ngattack_enum),
+	SOC_ENUM("Noise Gate Release Capture Switch", cmx655_ngrelease_enum),
+
+	/* Playback */
+	SOC_SINGLE_TLV("Master Playback Volume", CMX655_VOLUME, 0, 91, 0,
+		       cmx655_vol),
+	SOC_SINGLE_TLV("Preamp Playback Volume", CMX655_PREAMP, 0, 3, 0,
+		       cmx655_pre_amp),
+	SOC_SINGLE("Smooth Playback Switch", CMX655_VOLUME, 7, 0x01, 0),
+	SOC_SINGLE("Dogital Capture Block Playback Switch", CMX655_PVF,
+		   CMX655_VF_DCBLOCK_SHIFT,
+		   1, 0),
+	SOC_SINGLE("LPF Playback Switch", CMX655_PVF, 3, 1, 0),
+	SOC_ENUM("HPF Playback Switch", cmx655_hpf_playback_enum),
+	SOC_SINGLE("Soft Mute Playback Switch", CMX655_CPR, 0, 1, 0),
+	SOC_SINGLE("ALC Playback Switch", CMX655_ALCCTRL, 7, 1, 0),
+	SOC_ENUM("ALC Ratio Playback Switch", cmx655_alc_ratio_enum),
+	SOC_SINGLE_TLV("ALC Threshold Playback Volume", CMX655_ALCCTRL,
+		       0, 31, 0,
+		       cmx655_alc_thresh),
+	SOC_SINGLE_TLV("ALC Gain Playback Volume", CMX655_ALCGAIN, 0, 12, 0,
+		       cmx655_alc_gain),
+	SOC_ENUM("ALC Attack Playback Switch", cmx655_alc_attack_enum),
+	SOC_ENUM("ALC Release Playback Switch", cmx655_alc_release_enum),
+
+	/* Digital Sidetone */
+	SOC_SINGLE_TLV("Sidetone Playback Volume", CMX655_DST, 0, 31, 0,
+		       cmx655_dst_gain),
+	/* Companding */
+	SOC_SINGLE("Companding Switch", CMX655_SAIMUX, 4, 1, 0),
+	SOC_ENUM("Companding Type Enum", cmx655_companding_enum),
+};
+
+static const char *const cmx655_mic_mux_text[] = {
+	"Normal",
+	"Swapped",
+	"Left only",
+	"Right only"
+};
+
+static SOC_ENUM_SINGLE_DECL(cmx655_mic_mux_enum, CMX655_SAIMUX, 0,
+			    cmx655_mic_mux_text);
+
+static const char *const cmx655_amp_mux_text[] = {
+	"Left",
+	"Right",
+	"Mean"
+};
+
+static SOC_ENUM_SINGLE_DECL(cmx655_amp_mux_enum, CMX655_SAIMUX, 2,
+			    cmx655_amp_mux_text);
+
+static const char *const cmx655_digital_sidetone_text[] = {
+	"Left",
+	"Right",
+	"Mean"
+};
+
+static SOC_ENUM_SINGLE_DECL(cmx655_sidetone_enum, CMX655_DST, 5,
+			    cmx655_digital_sidetone_text);
+static const struct snd_kcontrol_new cmx655_mic_mux =
+SOC_DAPM_ENUM("SAI Capture Route",
+	      cmx655_mic_mux_enum);
+
+static const struct snd_kcontrol_new cmx655_amp_mux =
+SOC_DAPM_ENUM("Play_SAI Playback Route",
+	      cmx655_amp_mux_enum);
+
+static const struct snd_kcontrol_new cmx655_spkr_en[] = {
+	SOC_DAPM_SINGLE_VIRT("Playback Switch", 1)
+};
+
+static const struct snd_kcontrol_new cmx655_lout_en[] = {
+	SOC_DAPM_SINGLE_VIRT("Playback Switch", 1)
+};
+
+static const struct snd_kcontrol_new cmx655_sidetone_mux =
+SOC_DAPM_ENUM("DST Route", cmx655_sidetone_enum);
+
+static const struct snd_kcontrol_new cmx655_dst_en[] = {
+	SOC_DAPM_SINGLE_VIRT("Playback Switch", 1)
+};
+
+static int cmx655_mic_dapm_event(struct snd_soc_dapm_widget *widget,
+				 struct snd_kcontrol *control, int event)
+{
+	struct snd_soc_component *component =
+	    snd_soc_dapm_to_component(widget->dapm);
+	int reg_val;
+
+	switch (event) {
+	case (SND_SOC_DAPM_POST_PMU):
+		/*
+		 *After turn on give MIC filters time
+		 * Time can be shorter if the DC blocking filter is not enabled
+		 */
+		reg_val = snd_soc_component_read(component, CMX655_RVF);
+		if ((reg_val && CMX655_VF_DCBLOCK) > 0) {
+			/* This allows time for Mics and DC blocking filter to settle */
+			msleep(320);
+		} else {
+			usleep_range(3500, 4000);
+		}
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget cmx655_dapm_widgets[] = {
+	/* Input path widgets */
+	SND_SOC_DAPM_INPUT("MICL"),
+	SND_SOC_DAPM_INPUT("MICR"),
+
+	/* Custom widgets for Mics to get them to turn on before switches */
+	{.id = snd_soc_dapm_mic,
+	 .name = "Left Mic",
+	 .kcontrol_news = NULL,
+	 .num_kcontrols = 0,
+	 .reg = CMX655_SYSCTRL,
+	 .shift = 1,
+	 .mask = 1,
+	 .on_val = 1,
+	 .off_val = 0,
+	 .event = cmx655_mic_dapm_event,
+	 .event_flags = SND_SOC_DAPM_POST_PMU },
+	{.id = snd_soc_dapm_mic,
+	 .name = "Right Mic",
+	 .kcontrol_news = NULL,
+	 .num_kcontrols = 0,
+	 .reg = CMX655_SYSCTRL,
+	 .shift = 0,
+	 .mask = 1,
+	 .on_val = 1,
+	 .off_val = 0,
+	 .event = cmx655_mic_dapm_event,
+	 .event_flags = SND_SOC_DAPM_POST_PMU },
+
+	SND_SOC_DAPM_MUX("SAI Left Capture Mux", SND_SOC_NOPM, 0, 0,
+			 &cmx655_mic_mux),
+	SND_SOC_DAPM_MUX("SAI Right Capture Mux", SND_SOC_NOPM, 0, 0,
+			 &cmx655_mic_mux),
+	/*
+	 * Note: SAI enable is controlled by DAI's HWparams and shutdown. Using
+	 * DAPM control resulted in I2S sync errors on the platform driver
+	 */
+	SND_SOC_DAPM_AIF_OUT("SAI Left Out", "CMX655 Record", 0,
+			     SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SAI Right Out", "CMX655 Record", 1,
+			     SND_SOC_NOPM, 0, 0),
+
+	/* Output path widgets */
+	SND_SOC_DAPM_AIF_IN("SAI Left In", "CMX655 Playback", 0,
+			    SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SAI Right In", "CMX655 Playback", 1,
+			    SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_MUX("SAI Playback Route", SND_SOC_NOPM, 0, 0,
+			 &cmx655_amp_mux),
+
+	SND_SOC_DAPM_DAC("Power Amp", "CMX655 Playback", CMX655_SYSCTRL, 3, 0),
+	SND_SOC_DAPM_DAC("Line Out", "CMX655 Playback", CMX655_SYSCTRL, 4, 0),
+
+	SND_SOC_DAPM_SWITCH("SPKR Switch", SND_SOC_NOPM, 0, 0, cmx655_spkr_en),
+	SND_SOC_DAPM_SWITCH("LOUT Switch", SND_SOC_NOPM, 0, 0, cmx655_lout_en),
+
+	SND_SOC_DAPM_OUTPUT("SPKR"),
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+
+	/* Digital side tone widgets */
+	SND_SOC_DAPM_SWITCH("DST Switch", SND_SOC_NOPM, 0, 0, cmx655_dst_en),
+	SND_SOC_DAPM_MUX("DST Mux", CMX655_DST, 7, 0, &cmx655_sidetone_mux),
+
+};
+
+static const struct snd_soc_dapm_route cmx655_dapm_routes[] = {
+	/* Main output path */
+	{ "SPKR", NULL, "SPKR Switch" },
+	{ "LOUT", NULL, "LOUT Switch" },
+	{ "SPKR Switch", "Playback Switch", "Power Amp" },
+	{ "LOUT Switch", "Playback Switch", "Line Out" },
+	{ "Power Amp", NULL, "SAI Playback Route" },
+	{ "Line Out", NULL, "SAI Playback Route" },
+	{ "SAI Playback Route", "Left", "SAI Left In" },
+	{ "SAI Playback Route", "Right", "SAI Right In" },
+	{ "SAI Playback Route", "Mean", "SAI Left In" },
+	{ "SAI Playback Route", "Mean", "SAI Right In" },
+	/* Main input path */
+	{ "SAI Right Out", NULL, "SAI Right Capture Mux" },
+	{ "SAI Left Out", NULL, "SAI Left Capture Mux" },
+	{ "SAI Left Capture Mux", "Normal", "Left Mic" },
+	{ "SAI Right Capture Mux", "Normal", "Right Mic" },
+	{ "SAI Left Capture Mux", "Swapped", "Right Mic" },
+	{ "SAI Right Capture Mux", "Swapped", "Left Mic" },
+	{ "SAI Left Capture Mux", "Left only", "Left Mic" },
+	{ "SAI Right Capture Mux", "Left only", "Left Mic" },
+	{ "SAI Left Capture Mux", "Right only", "Right Mic" },
+	{ "SAI Right Capture Mux", "Right only", "Right Mic" },
+	{ "Right Mic", NULL, "MICR" },
+	{ "Left Mic", NULL, "MICL" },
+	/* Digital side tone */
+	{ "DST Mux", "Left", "Left Mic" },
+	{ "DST Mux", "Right", "Right Mic" },
+	{ "DST Mux", "Mean", "Left Mic" },
+	{ "DST Mux", "Mean", "Right Mic" },
+
+	{ "DST Switch", "Playback Switch", "DST Mux" },
+
+	{ "Power Amp", NULL, "DST Switch" },
+	{ "Line Out", NULL, "DST Switch" },
+
+};
+
+static const struct snd_soc_component_driver cmx655_component_driver = {
+	.controls = cmx655_snd_controls,
+	.num_controls = ARRAY_SIZE(cmx655_snd_controls),
+	.dapm_widgets = cmx655_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(cmx655_dapm_widgets),
+	.dapm_routes = cmx655_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(cmx655_dapm_routes),
+
+	.probe = cmx655_component_probe,
+	.remove = cmx655_component_remove
+};
+
+static int cmx655_parse_data_from_of(const struct device_node *device_node,
+				     struct cmx655_data *cmx655_data)
+{
+	int ret;
+	unsigned int val;
+
+	ret =
+	    of_property_read_u32(device_node,
+				 "cmx655,classd-oc-reset-attempts", &val);
+	if (ret >= 0)
+		cmx655_data->oc_cnt_max = val;
+	else
+		cmx655_data->oc_cnt_max = 5;
+
+	return 0;
+}
+
+/**
+ * cmx655_common_register_component() - registers cmx655 codec.
+ * @dev: bus-specific device descriptor
+ * @regmap: bus-specific register mapping
+ * @irq: interrupt id for the device
+ *
+ * Return: 0 for success, negative number otherwise
+ */
+int cmx655_common_register_component(struct device *dev, struct regmap *regmap,
+				     int irq)
+{
+	int ret;
+	struct cmx655_data *cmx655_data;
+	struct cmx655_dai_data *cmx655_dai_data = dev_get_platdata(dev);
+
+	cmx655_data = devm_kzalloc(dev, sizeof(*cmx655_data), GFP_KERNEL);
+	if (!cmx655_data)
+		return -ENOMEM;
+	cmx655_data->regmap = regmap;
+	if (IS_ERR(cmx655_data->regmap))
+		return PTR_ERR(cmx655_data->regmap);
+	cmx655_data->irq = irq;
+	if (cmx655_dai_data) {
+		memcpy(&cmx655_data->dai_data, cmx655_dai_data,
+		       sizeof(*cmx655_dai_data));
+	}
+	if (dev->of_node) {
+		ret = cmx655_parse_data_from_of(dev->of_node, cmx655_data);
+		if (ret < 0) {
+			dev_err(dev,
+				"Failed to extract data from device tree%d\n",
+				ret);
+			return ret;
+		}
+	}
+	cmx655_data->reset_gpio = devm_gpiod_get_optional(dev,
+							  "reset",
+							  GPIOD_OUT_LOW);
+	if (cmx655_data->reset_gpio) {
+		gpiod_set_value_cansleep(cmx655_data->reset_gpio, 1);
+		usleep_range(10, 1000);
+		gpiod_set_value_cansleep(cmx655_data->reset_gpio, 0);
+	} else {
+		dev_dbg(dev, "No reset GPIO, using reset command\n");
+		regmap_write(cmx655_data->regmap, CMX655_COMMAND,
+			     CMX655_CMD_SOFT_RESET);
+	}
+	dev_set_drvdata(dev, cmx655_data);
+
+	return devm_snd_soc_register_component(dev,
+					       &cmx655_component_driver,
+					       &cmx655_dai_driver, 1);
+}
+EXPORT_SYMBOL_GPL(cmx655_common_register_component);
+
+void cmx655_common_unregister_component(struct device *dev)
+{
+	snd_soc_unregister_component(dev);
+}
+EXPORT_SYMBOL_GPL(cmx655_common_unregister_component);
+
+MODULE_DESCRIPTION("ASoC CMX655 driver");
+MODULE_AUTHOR("CML");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cmx655.h b/sound/soc/codecs/cmx655.h
new file mode 100644
index 000000000000..e25182c39282
--- /dev/null
+++ b/sound/soc/codecs/cmx655.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef CMX655_H
+#define CMX655_H
+
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+
+#define CMX655_ISR      (0x00)
+#define     CMX655_ISR_MICR        BIT(0)
+#define     CMX655_ISR_MICL        BIT(1)
+#define     CMX655_ISR_AMPOC       BIT(2)
+#define     CMX655_ISR_AMPCLIP     BIT(3)
+#define     CMX655_ISR_CLKRDY      BIT(4)
+#define     CMX655_ISR_THERM       BIT(5)
+#define     CMX655_ISR_VOL         BIT(6)
+#define     CMX655_ISR_CAL         BIT(7)
+
+#define CMX655_ISM      (0x01)
+#define     CMX655_ISM_MICR        BIT(0)
+#define     CMX655_ISM_MICL        BIT(1)
+#define     CMX655_ISM_AMPOC       BIT(2)
+#define     CMX655_ISM_AMPCLIP     BIT(3)
+#define     CMX655_ISM_CLKRDY      BIT(4)
+#define     CMX655_ISM_THERM       BIT(5)
+#define     CMX655_ISM_VOL         BIT(6)
+#define     CMX655_ISM_CAL         BIT(7)
+#define CMX655_ISE      (0x02)
+#define CMX655_CLKCTRL  (0x03)
+#define     CMX655_CLKCTRL_PREDIV_SHIFT    (0)
+#define     CMX655_CLKCTRL_PREDIV_VALUE    (0x3)
+#define     CMX655_CLKCTRL_PREDIV_MASK     (CMX655_CLKCTRL_PREDIV_VALUE << \
+					CMX655_CLKCTRL_PREDIV_SHIFT)
+#define     CMX655_CLKCTRL_CLRSRC_SHIFT    (2)
+#define     CMX655_CLKCTRL_CLRSRC_VALUE    (0x7)
+#define     CMX655_CLKCTRL_CLRSRC_MASK     (CMX655_CLKCTRL_CLRSRC_VALUE << \
+					CMX655_CLKCTRL_CLRSRC_SHIFT)
+#define     CMX655_CLKCTRL_CLRSRC_RCLK     (0x0 << CMX655_CLKCTRL_CLRSRC_SHIFT)
+#define     CMX655_CLKCTRL_CLRSRC_LPO      (0x1 << CMX655_CLKCTRL_CLRSRC_SHIFT)
+#define     CMX655_CLKCTRL_CLRSRC_LRCLK    (0x7 << CMX655_CLKCTRL_CLRSRC_SHIFT)
+#define     CMX655_CLKCTRL_SR_SHIFT        (5)
+#define     CMX655_CLKCTRL_SR_VALUE        (0x3)
+#define     CMX655_CLKCTRL_SR_MASK         (CMX655_CLKCTRL_SR_VALUE << \
+					CMX655_CLKCTRL_SR_SHIFT)
+#define     CMX655_CLKCTRL_SR_8K            (0x0 << CMX655_CLKCTRL_SR_SHIFT)
+#define     CMX655_CLKCTRL_SR_16K           (0x1 << CMX655_CLKCTRL_SR_SHIFT)
+#define     CMX655_CLKCTRL_SR_32K           (0x2 << CMX655_CLKCTRL_SR_SHIFT)
+#define     CMX655_CLKCTRL_SR_48K           (0x3 << CMX655_CLKCTRL_SR_SHIFT)
+
+#define CMX655_RDIVHI   (0x04)
+#define CMX655_RDIVLO   (0x05)
+#define CMX655_NDIVHI   (0x06)
+#define CMX655_NDIVLO   (0x07)
+#define CMX655_PLLCTRL  (0x08)
+#define     CMX655_PLLCTRL_CPI_SHIFT       (0)
+#define     CMX655_PLLCTRL_LFILT_SHIFT     (4)
+#define CMX655_SAICTRL  (0x09)
+#define     CMX655_SAI_PCM         BIT(0)
+#define     CMX655_SAI_BINV        BIT(2)
+#define     CMX655_SAI_POL         BIT(3)
+#define     CMX655_SAI_DLY         BIT(4)
+#define     CMX655_SAI_MONO        BIT(5)
+#define     CMX655_SAI_WL          BIT(6)
+#define     CMX655_SAI_MSTR        BIT(7)
+
+#define CMX655_SAIMUX   (0x0a)
+#define CMX655_RVF      (0x0c)
+#define     CMX655_VF_DCBLOCK_SHIFT     (2)
+#define     CMX655_VF_DCBLOCK           (0x1 << CMX655_VF_DCBLOCK_SHIFT)
+#define CMX655_LDCTRL   (0x0d)
+#define CMX655_RDCTRL   (0x0e)
+#define CMX655_LEVEL    (0x0f)
+#define CMX655_NGCTRL   (0x1c)
+#define CMX655_NGTIME   (0x1d)
+#define CMX655_NGLSTAT  (0x1e)
+#define CMX655_NGRSTAT  (0x1f)
+#define CMX655_PVF      (0x28)
+#define CMX655_PREAMP   (0x29)
+#define CMX655_VOLUME   (0x2a)
+#define CMX655_ALCCTRL  (0x2b)
+#define CMX655_ALCTIME  (0x2c)
+#define CMX655_ALCGAIN  (0x2d)
+#define CMX655_ALCSTAT  (0x2e)
+#define CMX655_DST      (0x2f)
+#define CMX655_CPR      (0x30)
+#define CMX655_SYSCTRL  (0x32)
+#define     CMX655_SYSCTRL_MICR    BIT(0)
+#define     CMX655_SYSCTRL_MICL    BIT(1)
+#define     CMX655_SYSCTRL_PAMP    BIT(3)
+#define     CMX655_SYSCTRL_LOUT    BIT(4)
+#define     CMX655_SYSCTRL_SAI     BIT(5)
+
+#define CMX655_COMMAND  (0x33)
+#define     CMX655_CMD_CLOCK_STOP  (0x00)
+#define     CMX655_CMD_CLOCK_START (0x01)
+#define     CMX655_CMD_SOFT_RESET  (0xff)
+
+/*  GPIO connection for reset and irq */
+#define CMX655_RESETN   (24)
+#define CMX655_IRQN     (25)
+#define CMX655_CS       (8)
+
+#define CMX655_RATES (SNDRV_PCM_RATE_8000 | \
+		SNDRV_PCM_RATE_16000 | \
+		SNDRV_PCM_RATE_32000 | \
+		SNDRV_PCM_RATE_48000)
+
+#define CMX655_FMTS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
+
+/*
+ * clock id's when calling set sysclk
+ * Auto = Use RCLK when in DAI primary mode. Use LRCLK in secondary mode.
+ * DO NOT use CMX655_SYSCLK_LRCLK when in DAI primary mode
+ */
+#define CMX655_SYSCLK_AUTO  (0)
+#define CMX655_SYSCLK_RCLK  (1)
+#define CMX655_SYSCLK_LRCLK (2)
+#define CMX655_SYSCLK_LPO   (3)
+#define CMX655_SYSCLK_MIN   (CMX655_SYSCLK_AUTO)
+#define CMX655_SYSCLK_MAX   (CMX655_SYSCLK_LPO)
+
+struct cmx655_dai_data {
+	int sys_clk;
+	unsigned int enabled_streams;
+	bool best_clk_running;
+	int clk_src;
+};
+
+struct cmx655_data {
+	struct regmap *regmap;
+	struct cmx655_dai_data dai_data;
+	struct gpio_desc *reset_gpio;
+	int irq;
+	/* Number of times the class-D overcurrent has been reset */
+	unsigned int oc_cnt;
+	/* Max times the class-D overcurrent should be reset */
+	unsigned int oc_cnt_max;
+};
+
+extern const struct regmap_config cmx655_regmap;
+
+int cmx655_common_register_component(struct device *dev, struct regmap *regmap, int irq);
+void cmx655_common_unregister_component(struct device *dev);
+
+#endif
-- 
2.47.2





[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux