about summary refs log tree commit diff
path: root/modules/hardware
diff options
context:
space:
mode:
authoraszlig <aszlig@redmoonstudios.org>2016-12-15 23:32:41 +0100
committeraszlig <aszlig@redmoonstudios.org>2016-12-15 23:45:04 +0100
commitd912892ccb1f7ea911db54c06c99c104d1e9278b (patch)
tree77c5a3712e4d80624785b17c216d63490a74d071 /modules/hardware
parent4a27d62243942c4be181c7129fb532a709a15a8e (diff)
hardware/t100ha: Add patch for SST sound
This is essentially a backport of @plbossart's "experimental/codecs"
branch against Linux 4.9.

The original repository can be found at:

https://github.com/plbossart/sound/commits/experimental/codecs

Thanks to him for doing a lot of work on getting sound working for these
CherryTrail devices.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Diffstat (limited to 'modules/hardware')
-rw-r--r--modules/hardware/t100ha/default.nix11
-rw-r--r--modules/hardware/t100ha/sound.patch5632
2 files changed, 5640 insertions, 3 deletions
diff --git a/modules/hardware/t100ha/default.nix b/modules/hardware/t100ha/default.nix
index 00a5855d..69fd10d1 100644
--- a/modules/hardware/t100ha/default.nix
+++ b/modules/hardware/t100ha/default.nix
@@ -8,12 +8,14 @@ in {
   options.vuizvui.hardware.t100ha.enable = lib.mkEnableOption desc;
 
   config = lib.mkIf cfg.enable {
-    hardware.firmware = lib.singleton (pkgs.runCommand "brcm43340-firmware" {
+    hardware.firmware = lib.singleton (pkgs.runCommand "t100ha-firmware" {
       params = ./brcmfmac43340-sdio.txt;
-      fwbase = "lib/firmware/brcm/brcmfmac43340-sdio";
+      fwpkg = pkgs.firmwareLinuxNonfree;
       install = "install -vD -m 0644";
     } ''
-      $install "${pkgs.firmwareLinuxNonfree}/$fwbase.bin" "$out/$fwbase.bin"
+      for fw in brcm/brcmfmac43340-sdio intel/fw_sst_22a8; do
+        $install "$fwpkg/lib/firmware/$fw.bin" "$out/lib/firmware/$fw.bin"
+      done
       $install "$params" "$out/$fwbase.txt"
     '');
 
@@ -27,6 +29,9 @@ in {
       { name = "sdio";
         patch = ./sdio.patch;
       }
+      { name = "sound";
+        patch = ./sound.patch;
+      }
     ];
 
     boot.kernelPackages = let
diff --git a/modules/hardware/t100ha/sound.patch b/modules/hardware/t100ha/sound.patch
new file mode 100644
index 00000000..2fb67f17
--- /dev/null
+++ b/modules/hardware/t100ha/sound.patch
@@ -0,0 +1,5632 @@
+diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
+index bada636d1065..233898ec1fbd 100644
+--- a/arch/x86/Kconfig
++++ b/arch/x86/Kconfig
+@@ -2756,6 +2756,7 @@ config X86_DMA_REMAP
+ config PMC_ATOM
+ 	def_bool y
+         depends on PCI
++	select COMMON_CLK
+ 
+ source "net/Kconfig"
+ 
+diff --git a/arch/x86/include/asm/pmc_atom.h b/arch/x86/include/asm/pmc_atom.h
+index aa8744c77c6d..59f95168fa8d 100644
+--- a/arch/x86/include/asm/pmc_atom.h
++++ b/arch/x86/include/asm/pmc_atom.h
+@@ -152,6 +152,12 @@
+ #define	SLEEP_TYPE_S5		0x1C00
+ #define	SLEEP_ENABLE		0x2000
+ 
++struct pmc_clk {
++	const char *name;
++	unsigned long freq;
++	const char *parent_name;
++};
++
+ extern int pmc_atom_read(int offset, u32 *value);
+ extern int pmc_atom_write(int offset, u32 value);
+ 
+diff --git a/arch/x86/platform/atom/pmc_atom.c b/arch/x86/platform/atom/pmc_atom.c
+index 964ff4fc61f9..e2de3e234959 100644
+--- a/arch/x86/platform/atom/pmc_atom.c
++++ b/arch/x86/platform/atom/pmc_atom.c
+@@ -21,6 +21,7 @@
+ #include <linux/debugfs.h>
+ #include <linux/seq_file.h>
+ #include <linux/io.h>
++#include <linux/platform_device.h>
+ 
+ #include <asm/pmc_atom.h>
+ 
+@@ -37,6 +38,11 @@ struct pmc_reg_map {
+ 	const struct pmc_bit_map *pss;
+ };
+ 
++struct pmc_data {
++	const struct pmc_reg_map *map;
++	const struct pmc_clk *clks;
++};
++
+ struct pmc_dev {
+ 	u32 base_addr;
+ 	void __iomem *regmap;
+@@ -50,6 +56,29 @@ struct pmc_dev {
+ static struct pmc_dev pmc_device;
+ static u32 acpi_base_addr;
+ 
++static const struct pmc_clk byt_clks[] = {
++	{
++		.name = "xtal",
++		.freq = 25000000,
++		.parent_name = NULL,
++	},
++	{
++		.name = "pll",
++		.freq = 19200000,
++		.parent_name = "xtal",
++	},
++	{},
++};
++
++static const struct pmc_clk cht_clks[] = {
++	{
++		.name = "xtal",
++		.freq = 19200000,
++		.parent_name = NULL,
++	},
++	{},
++};
++
+ static const struct pmc_bit_map d3_sts_0_map[] = {
+ 	{"LPSS1_F0_DMA",	BIT_LPSS1_F0_DMA},
+ 	{"LPSS1_F1_PWM1",	BIT_LPSS1_F1_PWM1},
+@@ -169,6 +198,16 @@ static const struct pmc_reg_map cht_reg_map = {
+ 	.pss		= cht_pss_map,
+ };
+ 
++static const struct pmc_data byt_data = {
++	.map = &byt_reg_map,
++	.clks = byt_clks,
++};
++
++static const struct pmc_data cht_data = {
++	.map = &cht_reg_map,
++	.clks = cht_clks,
++};
++
+ static inline u32 pmc_reg_read(struct pmc_dev *pmc, int reg_offset)
+ {
+ 	return readl(pmc->regmap + reg_offset);
+@@ -384,8 +423,11 @@ static int pmc_dbgfs_register(struct pmc_dev *pmc)
+ 
+ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
+ {
++	struct platform_device *clkdev;
+ 	struct pmc_dev *pmc = &pmc_device;
+-	const struct pmc_reg_map *map = (struct pmc_reg_map *)ent->driver_data;
++	const struct pmc_data *data = (struct pmc_data *)ent->driver_data;
++	const struct pmc_reg_map *map = data->map;
++	const struct pmc_clk *clks = data->clks;
+ 	int ret;
+ 
+ 	/* Obtain ACPI base address */
+@@ -414,6 +456,13 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
+ 	if (ret)
+ 		dev_warn(&pdev->dev, "debugfs register failed\n");
+ 
++	/* Register platform clocks - PMC_PLT_CLK [5:0] */
++	clkdev = platform_device_register_data(NULL, "clk-byt-plt", -1,
++					       &clks, sizeof(clks));
++	if (IS_ERR(clkdev))
++		dev_warn(&pdev->dev, "platform clocks register failed: %ld\n",
++			 PTR_ERR(clkdev));
++
+ 	pmc->init = true;
+ 	return ret;
+ }
+@@ -424,8 +473,8 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
+  * used by pci_match_id() call below.
+  */
+ static const struct pci_device_id pmc_pci_ids[] = {
+-	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), (kernel_ulong_t)&byt_reg_map },
+-	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), (kernel_ulong_t)&cht_reg_map },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), (kernel_ulong_t)&byt_data },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), (kernel_ulong_t)&cht_data },
+ 	{ 0, },
+ };
+ 
+diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile
+index 04781389d0fb..cbdc8cc00060 100644
+--- a/drivers/clk/x86/Makefile
++++ b/drivers/clk/x86/Makefile
+@@ -1,2 +1,3 @@
+ clk-x86-lpss-objs		:= clk-lpt.o
+ obj-$(CONFIG_X86_INTEL_LPSS)	+= clk-x86-lpss.o
++obj-$(CONFIG_PMC_ATOM)		+= clk-byt-plt.o
+diff --git a/drivers/clk/x86/clk-byt-plt.c b/drivers/clk/x86/clk-byt-plt.c
+new file mode 100644
+index 000000000000..330cd35d1d27
+--- /dev/null
++++ b/drivers/clk/x86/clk-byt-plt.c
+@@ -0,0 +1,413 @@
++/*
++ * Intel Atom platform clocks driver for Baytrail and CherryTrail SoC.
++ *
++ * Copyright (C) 2016, Intel Corporation
++ * Author: Irina Tirdea <irina.tirdea@intel.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
++ * more details.
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/err.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/clkdev.h>
++
++#include <asm/pmc_atom.h>
++
++#define PLT_CLK_NAME_BASE	"pmc_plt_clk_"
++#define PLT_CLK_DRIVER_NAME	"clk-byt-plt"
++
++#define PMC_CLK_CTL_0		0x60
++#define PMC_CLK_CTL_SIZE	4
++#define PMC_CLK_NUM		6
++#define PMC_MASK_CLK_CTL	GENMASK(1, 0)
++#define PMC_MASK_CLK_FREQ	BIT(2)
++#define PMC_CLK_CTL_GATED_ON_D3	0x0
++#define PMC_CLK_CTL_FORCE_ON	0x1
++#define PMC_CLK_CTL_FORCE_OFF	0x2
++#define PMC_CLK_CTL_RESERVED	0x3
++#define PMC_CLK_FREQ_XTAL	0x0	/* 25 MHz */
++#define PMC_CLK_FREQ_PLL	0x4	/* 19.2 MHz */
++
++struct clk_plt_fixed {
++	struct clk *clk;
++	struct clk_lookup *lookup;
++};
++
++struct clk_plt {
++	struct clk_hw hw;
++	u8 id;
++	u32 offset;
++	struct clk_lookup *lookup;
++	spinlock_t lock;
++};
++
++#define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw)
++
++struct clk_plt_data {
++	struct clk_plt_fixed **parents;
++	u8 nparents;
++	struct clk *clks[PMC_CLK_NUM];
++};
++
++static inline int plt_reg_to_parent(int reg)
++{
++	switch (reg & PMC_MASK_CLK_FREQ) {
++	case PMC_CLK_FREQ_XTAL:
++		return 0;	/* index 0 in parents[] */
++	case PMC_CLK_FREQ_PLL:
++		return 1;	/* index 1 in parents[] */
++	}
++
++	return 0;
++}
++
++static inline int plt_parent_to_reg(int index)
++{
++	switch (index) {
++	case 0:	/* index 0 in parents[] */
++		return PMC_CLK_FREQ_XTAL;
++	case 1:	/* index 0 in parents[] */
++		return PMC_CLK_FREQ_PLL;
++	}
++
++	return PMC_CLK_FREQ_XTAL;
++}
++
++static inline int plt_reg_to_enabled(int reg)
++{
++	switch (reg & PMC_MASK_CLK_CTL) {
++	case PMC_CLK_CTL_GATED_ON_D3:
++	case PMC_CLK_CTL_FORCE_ON:
++		return 1;	/* enabled */
++	case PMC_CLK_CTL_FORCE_OFF:
++	case PMC_CLK_CTL_RESERVED:
++	default:
++		return 0;	/* disabled */
++	}
++}
++
++static int plt_pmc_atom_update(struct clk_plt *clk, u32 mask, u32 val)
++{
++	int ret;
++	u32 orig, tmp;
++	unsigned long flags = 0;
++
++	spin_lock_irqsave(&clk->lock, flags);
++
++	ret = pmc_atom_read(clk->offset, &orig);
++	if (ret)
++		goto out;
++
++	tmp = orig & ~mask;
++	tmp |= val & mask;
++
++	if (tmp == orig)
++		goto out;
++
++	ret = pmc_atom_write(clk->offset, tmp);
++	if (ret)
++		goto out;
++
++out:
++	spin_unlock_irqrestore(&clk->lock, flags);
++
++	return ret;
++}
++
++static int plt_clk_set_parent(struct clk_hw *hw, u8 index)
++{
++	struct clk_plt *clk = to_clk_plt(hw);
++
++	return plt_pmc_atom_update(clk, PMC_MASK_CLK_FREQ,
++				   plt_parent_to_reg(index));
++}
++
++static u8 plt_clk_get_parent(struct clk_hw *hw)
++{
++	struct clk_plt *clk = to_clk_plt(hw);
++	u32 value;
++	int ret;
++
++	ret = pmc_atom_read(clk->offset, &value);
++	if (ret)
++		return ret;
++
++	return plt_reg_to_parent(value);
++}
++
++static int plt_clk_enable(struct clk_hw *hw)
++{
++	struct clk_plt *clk = to_clk_plt(hw);
++
++	return plt_pmc_atom_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON);
++}
++
++static void plt_clk_disable(struct clk_hw *hw)
++{
++	struct clk_plt *clk = to_clk_plt(hw);
++
++	plt_pmc_atom_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF);
++}
++
++static int plt_clk_is_enabled(struct clk_hw *hw)
++{
++	struct clk_plt *clk = to_clk_plt(hw);
++	u32 value;
++	int ret;
++
++	ret = pmc_atom_read(clk->offset, &value);
++	if (ret)
++		return ret;
++
++	return plt_reg_to_enabled(value);
++}
++
++static const struct clk_ops plt_clk_ops = {
++	.enable = plt_clk_enable,
++	.disable = plt_clk_disable,
++	.is_enabled = plt_clk_is_enabled,
++	.get_parent = plt_clk_get_parent,
++	.set_parent = plt_clk_set_parent,
++	.determine_rate = __clk_mux_determine_rate,
++};
++
++static struct clk *plt_clk_register(struct platform_device *pdev, int id,
++				    const char **parent_names, int num_parents)
++{
++	struct clk_plt *pclk;
++	struct clk *clk;
++	struct clk_init_data init;
++	int ret = 0;
++
++	pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
++	if (!pclk)
++		return ERR_PTR(-ENOMEM);
++
++	init.name =  kasprintf(GFP_KERNEL, "%s%d", PLT_CLK_NAME_BASE, id);
++	init.ops = &plt_clk_ops;
++	init.flags = 0;
++	init.parent_names = parent_names;
++	init.num_parents = num_parents;
++
++	pclk->hw.init = &init;
++	pclk->id = id;
++	pclk->offset = PMC_CLK_CTL_0 + id * PMC_CLK_CTL_SIZE;
++	spin_lock_init(&pclk->lock);
++
++	clk = clk_register(&pdev->dev, &pclk->hw);
++	if (IS_ERR(clk)) {
++		ret = PTR_ERR(clk);
++		goto err_free_pclk;
++	}
++
++	pclk->lookup = clkdev_create(clk, init.name, NULL);
++	if (!pclk->lookup) {
++		ret = -ENOMEM;
++		goto err_clk_unregister;
++	}
++
++	kfree(init.name);
++
++	return clk;
++
++err_clk_unregister:
++	clk_unregister(clk);
++err_free_pclk:
++	kfree(init.name);
++	return ERR_PTR(ret);
++}
++
++static void plt_clk_unregister(struct clk *clk)
++{
++	struct clk_plt *pclk;
++	struct clk_hw *hw;
++
++	hw = __clk_get_hw(clk);
++	if (!hw)
++		return;
++
++	pclk = to_clk_plt(hw);
++
++	clkdev_drop(pclk->lookup);
++	clk_unregister(clk);
++}
++
++static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev,
++						 const char *name,
++						 const char *parent_name,
++						 unsigned long fixed_rate)
++{
++	struct clk_plt_fixed *pclk;
++	int ret = 0;
++
++	pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
++	if (!pclk)
++		return ERR_PTR(-ENOMEM);
++
++	pclk->clk = clk_register_fixed_rate(&pdev->dev, name, parent_name,
++					    0, fixed_rate);
++	if (IS_ERR(pclk->clk)) {
++		ret = PTR_ERR(pclk->clk);
++		return ERR_PTR(ret);
++	}
++
++	pclk->lookup = clkdev_create(pclk->clk, name, NULL);
++	if (!pclk->lookup) {
++		ret = -ENOMEM;
++		goto err_clk_unregister;
++	}
++
++	return pclk;
++
++err_clk_unregister:
++	clk_unregister_fixed_rate(pclk->clk);
++	return ERR_PTR(ret);
++}
++
++static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk)
++{
++	clkdev_drop(pclk->lookup);
++	clk_unregister_fixed_rate(pclk->clk);
++}
++
++static const char **plt_clk_register_parents(struct platform_device *pdev,
++					     struct clk_plt_data *data)
++{
++	struct pmc_clk **pclks, *clks;
++	const char **parent_names;
++	int i, err;
++
++	data->nparents = 0;
++	pclks = dev_get_platdata(&pdev->dev);
++	if (!pclks)
++		return NULL;
++
++	clks = *pclks;
++	while (clks[data->nparents].name)
++		data->nparents++;
++
++	data->parents = devm_kzalloc(&pdev->dev,
++				     sizeof(*data->parents) * data->nparents,
++				     GFP_KERNEL);
++	if (!data->parents) {
++		err = -ENOMEM;
++		goto err_out;
++	}
++
++	parent_names = kcalloc(data->nparents, sizeof(*parent_names),
++			       GFP_KERNEL);
++	if (!parent_names) {
++		err = -ENOMEM;
++		goto err_out;
++	}
++
++	for (i = 0; i < data->nparents; i++) {
++		data->parents[i] =
++			plt_clk_register_fixed_rate(pdev, clks[i].name,
++						    clks[i].parent_name,
++						    clks[i].freq);
++		if (IS_ERR(data->parents[i])) {
++			err = PTR_ERR(data->parents[i]);
++			goto err_unreg;
++		}
++		parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL);
++	}
++
++	return parent_names;
++
++err_unreg:
++	for (i--; i >= 0; i--) {
++		plt_clk_unregister_fixed_rate(data->parents[i]);
++		kfree_const(parent_names[i]);
++	}
++	kfree(parent_names);
++err_out:
++	data->nparents = 0;
++	return ERR_PTR(err);
++}
++
++static void plt_clk_unregister_parents(struct clk_plt_data *data)
++{
++	int i;
++
++	for (i = 0; i < data->nparents; i++)
++		plt_clk_unregister_fixed_rate(data->parents[i]);
++}
++
++static int plt_clk_probe(struct platform_device *pdev)
++{
++	struct clk_plt_data *data;
++	int i, err;
++	const char **parent_names;
++
++	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
++	if (!data)
++		return -ENOMEM;
++
++	parent_names = plt_clk_register_parents(pdev, data);
++	if (IS_ERR(parent_names))
++		return PTR_ERR(parent_names);
++
++	for (i = 0; i < PMC_CLK_NUM; i++) {
++		data->clks[i] = plt_clk_register(pdev, i, parent_names,
++						 data->nparents);
++		if (IS_ERR(data->clks[i])) {
++			err = PTR_ERR(data->clks[i]);
++			goto err_unreg_clk_plt;
++		}
++	}
++
++	for (i = 0; i < data->nparents; i++)
++		kfree_const(parent_names[i]);
++	kfree(parent_names);
++
++	dev_set_drvdata(&pdev->dev, data);
++	return 0;
++
++err_unreg_clk_plt:
++	for (i--; i >= 0; i--)
++		plt_clk_unregister(data->clks[i]);
++	plt_clk_unregister_parents(data);
++	for (i = 0; i < data->nparents; i++)
++		kfree_const(parent_names[i]);
++	kfree(parent_names);
++	return err;
++}
++
++static int plt_clk_remove(struct platform_device *pdev)
++{
++	struct clk_plt_data *data;
++	int i;
++
++	data = dev_get_drvdata(&pdev->dev);
++	if (!data)
++		return 0;
++
++	for (i = 0; i < PMC_CLK_NUM; i++)
++		plt_clk_unregister(data->clks[i]);
++	plt_clk_unregister_parents(data);
++	return 0;
++}
++
++static struct platform_driver plt_clk_driver = {
++	.driver = {
++		.name = PLT_CLK_DRIVER_NAME,
++	},
++	.probe = plt_clk_probe,
++	.remove = plt_clk_remove,
++};
++module_platform_driver(plt_clk_driver);
++
++MODULE_DESCRIPTION("Intel Atom platform clocks driver");
++MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
++MODULE_LICENSE("GPL v2");
+diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
+index c67667bb970f..655e2f4d1dd0 100644
+--- a/sound/soc/codecs/Kconfig
++++ b/sound/soc/codecs/Kconfig
+@@ -61,6 +61,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
+@@ -478,6 +479,10 @@ config SND_SOC_CX20442
+ 	tristate
+ 	depends on TTY
+ 
++config SND_SOC_CX2072X
++	tristate "Conexant CX2072X CODEC"
++	depends on I2C
++	
+ config SND_SOC_JZ4740_CODEC
+ 	select REGMAP_MMIO
+ 	tristate
+diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
+index 958cd4912fbc..1dded20a4d84 100644
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -54,6 +54,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
+@@ -279,6 +280,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..aaec3722854c
+--- /dev/null
++++ b/sound/soc/codecs/cx2072x.c
+@@ -0,0 +1,2124 @@
++/*
++ * ALSA SoC CX20721/cx20723 Solana codec driver
++ * Copyright:   (C) 2016 Conexant Systems, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ ************************************************************************/
++#define DEBUG
++/*#define INTEL_MCLK_CONTROL*/
++
++/*#define ENABLE_MIC_POP_WA*/
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/platform_device.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++#include <sound/tlv.h>
++#include <linux/of_gpio.h>
++#include <linux/gpio.h>
++#include <sound/jack.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/firmware.h>
++#include <linux/regmap.h>
++#include <linux/proc_fs.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/acpi.h>
++#ifdef INTEL_MCLK_CONTROL
++#include <linux/vlv2_plat_clock.h>
++#endif
++#include "cx2072x.h"
++
++#define SUPPORT_RKI2S_FORMAT
++#define CX2072X_REV_A2 0x00100002
++#define CXDBG_REG_DUMP
++
++/*FIXME: need to move the EQ/DRC setting to device tree*/
++static unsigned char cx2072x_eq_coeff_array[MAX_EQ_BAND][MAC_EQ_COEFF] = {
++	{0x77, 0x26, 0x13, 0xb3, 0x76, 0x26, 0x0a, 0x3d, 0xd4, 0xe2, 0x04},
++	{0x97, 0x3e, 0xb3, 0x86, 0xc2, 0x3b, 0x4d, 0x79, 0xa7, 0xc5, 0x03},
++	{0x0f, 0x39, 0x76, 0xa3, 0x1b, 0x2b, 0x89, 0x5c, 0xd7, 0xdb, 0x03},
++	{0x21, 0x46, 0xfe, 0xa6, 0xec, 0x24, 0x01, 0x59, 0xf4, 0xd4, 0x03},
++	{0xe9, 0x78, 0x9c, 0xb0, 0x8a, 0x56, 0x64, 0x4f, 0x8d, 0xb0, 0x02},
++	{0x60, 0x6e, 0x57, 0xee, 0xec, 0x18, 0xa8, 0x11, 0xb5, 0xf8, 0x02},
++	{0x5a, 0x14, 0x68, 0xe9, 0x1d, 0x06, 0xb9, 0x5f, 0x68, 0xdc, 0x03},
++};
++
++static unsigned char cx2072x_drc_array[MAX_DRC_REGS] = {
++	0x65, 0x55, 0x3C, 0x01, 0x05, 0x39, 0x76, 0x1A, 0x00
++};
++
++/*#define CXDBG_REG_DUMP*/
++#ifdef CXDBG_REG_DUMP
++
++#define CX2072X_FORMATS (SNDRV_PCM_FMTBIT_S24_LE \
++	| SNDRV_PCM_FMTBIT_S16_LE)
++#define BITS_PER_SLOT 8
++
++#define _REG(_name_, _size_, _access_, _volatile_) { \
++	 #_name_, _name_, _size_ | _access_ | _volatile_}
++
++struct CX2072X_REG_DEF {
++	const char  *name;
++	unsigned int addr;
++	unsigned int attr;
++};
++
++#define WO 0x0100
++#define RO 0x0200
++#define RW 0x0300
++#define VO 0x8000
++#define NV 0x0000
++#define REGISTER_SIZE_MASK 0x000F
++#define REGISTER_ASSCESS_MASK 0x0F00
++#define REGISTER_VOLATILE_MASK 0x8000
++#define UNAVAILABLE 0
++
++
++static const struct CX2072X_REG_DEF cx2072x_regs[] = {
++	_REG(CX2072X_VENDOR_ID,                      4, RO, VO),
++	_REG(CX2072X_REVISION_ID,                    4, RO, VO),
++	_REG(CX2072X_CURRENT_BCLK_FREQUENCY,         4, RO, VO),
++	_REG(CX2072X_AFG_POWER_STATE,                1, RW, NV),
++	_REG(CX2072X_UM_RESPONSE,                    1, RW, NV),
++	_REG(CX2072X_GPIO_DATA,                      1, RW, NV),
++	_REG(CX2072X_GPIO_ENABLE,                    1, RW, NV),
++	_REG(CX2072X_GPIO_DIRECTION,                 1, RW, NV),
++	_REG(CX2072X_GPIO_WAKE,                      1, RW, NV),
++	_REG(CX2072X_GPIO_UM_ENABLE,                 1, RW, NV),
++	_REG(CX2072X_GPIO_STICKY_MASK,               1, RW, NV),
++	_REG(CX2072X_AFG_FUNCTION_RESET,             1, WO, NV),
++	_REG(CX2072X_DAC1_CONVERTER_FORMAT,          2, RW, NV),
++	_REG(CX2072X_DAC1_AMP_GAIN_RIGHT,            1, RW, NV),
++	_REG(CX2072X_DAC1_AMP_GAIN_LEFT,             1, RW, NV),
++	_REG(CX2072X_DAC1_POWER_STATE,               1, RW, NV),
++	_REG(CX2072X_DAC1_CONVERTER_STREAM_CHANNEL,  1, RW, NV),
++	_REG(CX2072X_DAC1_EAPD_ENABLE,               1, RW, NV),
++	_REG(CX2072X_DAC2_CONVERTER_FORMAT,          2, RW, NV),
++	_REG(CX2072X_DAC2_AMP_GAIN_RIGHT,            1, RW, NV),
++	_REG(CX2072X_DAC2_AMP_GAIN_LEFT,             1, RW, NV),
++	_REG(CX2072X_DAC2_POWER_STATE,               1, RW, NV),
++	_REG(CX2072X_DAC2_CONVERTER_STREAM_CHANNEL,  1, RW, NV),
++	_REG(CX2072X_ADC1_CONVERTER_FORMAT,          2, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_0,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_0,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_1,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_1,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_2,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_2,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_3,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_3,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_4,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_4,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_5,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_5,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_6,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_6,           1, RW, NV),
++	_REG(CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 1, RW, NV),
++	_REG(CX2072X_ADC1_POWER_STATE,               1, RW, NV),
++	_REG(CX2072X_ADC1_CONVERTER_STREAM_CHANNEL,  1, RW, NV),
++	_REG(CX2072X_ADC2_CONVERTER_FORMAT,          2, WO, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_RIGHT_0,          1, RW, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_LEFT_0,           1, RW, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_RIGHT_1,          1, RW, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_LEFT_1,           1, RW, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_RIGHT_2,          1, RW, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_LEFT_2,           1, RW, NV),
++	_REG(CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 1, RW, NV),
++	_REG(CX2072X_ADC2_POWER_STATE,               1, RW, NV),
++	_REG(CX2072X_ADC2_CONVERTER_STREAM_CHANNEL,  1, RW, NV),
++	_REG(CX2072X_PORTA_CONNECTION_SELECT_CTRL,   1, RW, NV),
++	_REG(CX2072X_PORTA_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTA_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTA_UNSOLICITED_RESPONSE,     1, RW, NV),
++	_REG(CX2072X_PORTA_PIN_SENSE,                4, RO, VO),
++	_REG(CX2072X_PORTA_EAPD_BTL,                 1, RW, NV),
++	_REG(CX2072X_PORTB_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTB_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTB_UNSOLICITED_RESPONSE,     1, RW, NV),
++	_REG(CX2072X_PORTB_PIN_SENSE,                4, RO, VO),
++	_REG(CX2072X_PORTB_EAPD_BTL,                 1, RW, NV),
++	_REG(CX2072X_PORTB_GAIN_RIGHT,               1, RW, NV),
++	_REG(CX2072X_PORTB_GAIN_LEFT,                1, RW, NV),
++	_REG(CX2072X_PORTC_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTC_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTC_GAIN_RIGHT,               1, RW, NV),
++	_REG(CX2072X_PORTC_GAIN_LEFT,                1, RW, NV),
++	_REG(CX2072X_PORTD_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTD_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTD_UNSOLICITED_RESPONSE,     1, RW, NV),
++	_REG(CX2072X_PORTD_PIN_SENSE,                4, RO, VO),
++	_REG(CX2072X_PORTD_GAIN_RIGHT,               1, RW, NV),
++	_REG(CX2072X_PORTD_GAIN_LEFT,                1, RW, NV),
++	_REG(CX2072X_PORTE_CONNECTION_SELECT_CTRL,   1, RW, NV),
++	_REG(CX2072X_PORTE_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTE_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTE_UNSOLICITED_RESPONSE,     1, RW, NV),
++	_REG(CX2072X_PORTE_PIN_SENSE,                4, RO, VO),
++	_REG(CX2072X_PORTE_EAPD_BTL,                 1, RW, NV),
++	_REG(CX2072X_PORTE_GAIN_RIGHT,               1, RW, NV),
++	_REG(CX2072X_PORTE_GAIN_LEFT,                1, RW, NV),
++	_REG(CX2072X_PORTF_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTF_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTF_UNSOLICITED_RESPONSE,     1, RW, NV),
++	_REG(CX2072X_PORTF_PIN_SENSE,                4, RO, VO),
++	_REG(CX2072X_PORTF_GAIN_RIGHT,               1, RW, NV),
++	_REG(CX2072X_PORTF_GAIN_LEFT,                1, RW, NV),
++	_REG(CX2072X_PORTG_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTG_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTG_CONNECTION_SELECT_CTRL,   1, RW, NV),
++	_REG(CX2072X_PORTG_EAPD_BTL,                 1, RW, NV),
++	_REG(CX2072X_PORTM_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTM_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTM_CONNECTION_SELECT_CTRL,   1, RW, NV),
++	_REG(CX2072X_PORTM_EAPD_BTL,                 1, RW, NV),
++	_REG(CX2072X_MIXER_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_MIXER_GAIN_RIGHT_0,             1, RW, NV),
++	_REG(CX2072X_MIXER_GAIN_LEFT_0,              1, WO, NV),
++	_REG(CX2072X_MIXER_GAIN_RIGHT_1,             1, RW, NV),
++	_REG(CX2072X_MIXER_GAIN_LEFT_1,              1, RW, NV),
++	_REG(CX2072X_EQ_ENABLE_BYPASS,               2, RW, NV),
++	_REG(CX2072X_EQ_B0_COEFF,                    2, WO, VO),
++	_REG(CX2072X_EQ_B1_COEFF,                    2, WO, VO),
++	_REG(CX2072X_EQ_B2_COEFF,                    2, WO, VO),
++	_REG(CX2072X_EQ_A1_COEFF,                    2, WO, VO),
++	_REG(CX2072X_EQ_A2_COEFF,                    2, WO, VO),
++	_REG(CX2072X_EQ_G_COEFF,                     1, WO, VO),
++	_REG(CX2072X_EQ_BAND,                        1, WO, VO),
++	_REG(CX2072X_SPKR_DRC_ENABLE_STEP,           1, RW, NV),
++	_REG(CX2072X_SPKR_DRC_CONTROL,               4, RW, NV),
++	_REG(CX2072X_SPKR_DRC_TEST,                  4, RW, NV),
++	_REG(CX2072X_DIGITAL_BIOS_TEST0,             4, RW, NV),
++	_REG(CX2072X_DIGITAL_BIOS_TEST2,             4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL1,                4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL2,                4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL3,                4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL4,                4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL5,                4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL6,                4, RW, NV),
++	_REG(CX2072X_UM_INTERRUPT_CRTL_E,            4, RW, NV),
++	_REG(CX2072X_CODEC_TEST20,                   2, RW, NV),
++	_REG(CX2072X_CODEC_TEST26,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST4,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST5,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST6,                   2, WO, NV),
++	_REG(CX2072X_ANALOG_TEST7,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST8,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST9,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST10,                  2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST11,                  2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST12,                  2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST13,                  2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST0,                  2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST1,                  2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST11,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST12,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST15,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST16,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST17,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST18,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST19,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST20,                 2, RW, NV),
++};
++#endif
++
++/* codec private data */
++struct cx2072x_priv {
++	struct regmap *regmap;
++	unsigned int mclk;
++	struct device *dev;
++	struct snd_soc_codec *codec;
++	struct snd_soc_dai_driver *dai_drv;
++	int is_biason;
++	struct snd_soc_jack *jack;
++	bool jack_detecting;
++	bool jack_mic;
++	int jack_mode;
++	int jack_flips;
++	unsigned int jack_state;
++	int audsmt_enable;
++	struct mutex lock;
++	unsigned int bclk_ratio;
++
++#ifdef ENABLE_MIC_POP_WA
++	struct delayed_work mic_pop_workq;
++#endif
++
++	bool plbk_dsp_en;
++	bool plbk_dsp_changed;
++	bool plbk_dsp_init;
++	bool pll_changed;
++	bool i2spcm_changed;
++	int sample_size; /*used for non-PCM mode*/
++	int frame_size; /*used for non-PCM mode*/
++	int sample_rate;
++	unsigned int dai_fmt;
++	int tdm_rx_mask;
++	int tdm_tx_mask;
++	int tdm_slot_width;
++	int tdm_slots;
++	u32 rev_id;
++};
++
++/*
++* 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);
++
++#define get_cx2072x_priv(_codec_) \
++	((struct cx2072x_priv *) snd_soc_codec_get_drvdata(_codec_))
++
++/* Lookup table for PRE_DIV*/
++static struct {
++	unsigned int mclk;
++	unsigned int div;
++} MCLK_PRE_DIV[] = {
++	{ 6144000, 1 },
++	{ 12288000, 2 },
++	{ 19200000, 3 },
++	{ 26000000, 4 },
++	{ 28224000, 5 },
++	{ 36864000, 6 },
++	{ 36864000, 7 },/* Don't use div 7*/
++	{ 48000000, 8 },
++	{ 49152000, 8 },
++};
++
++/*
++ * cx2072x register cache.
++ */
++static const struct reg_default cx2072x_reg_defaults[] = {
++	{ 0x0414, 0x00000003 },	/*2072X_AFG_POWER_STATE */
++	{ 0x0420, 0x00000000 },	/*2072X_UM_RESPONSE */
++	{ 0x0454, 0x00000000 },	/*2072X_GPIO_DATA */
++	{ 0x0458, 0x00000000 },	/*2072X_GPIO_ENABLE */
++	{ 0x045c, 0x00000000 },	/*2072X_GPIO_DIRECTION */
++	{ 0x0460, 0x00000000 },	/*2072X_GPIO_WAKE */
++	{ 0x0464, 0x00000000 },	/*2072X_GPIO_UM_ENABLE */
++	{ 0x0468, 0x00000000 },	/*2072X_GPIO_STICKY_MASK */
++	{ 0x43c8, 0x00000031 },	/*2072X_DAC1_CONVERTER_FORMAT */
++	{ 0x41c0, 0x0000004a },	/*2072X_DAC1_AMP_GAIN_RIGHT */
++	{ 0x41e0, 0x0000004a },	/*2072X_DAC1_AMP_GAIN_LEFT */
++	{ 0x4014, 0x00000433 },	/*2072X_DAC1_POWER_STATE */
++	{ 0x4018, 0x00000000 },	/*2072X_DAC1_CONVERTER_STREAM_CHANNEL */
++	{ 0x4030, 0x00000000 },	/*2072X_DAC1_EAPD_ENABLE */
++	{ 0x47c8, 0x00000031 },	/*2072X_DAC2_CONVERTER_FORMAT */
++	{ 0x45c0, 0x0000004a },	/*2072X_DAC2_AMP_GAIN_RIGHT */
++	{ 0x45e0, 0x0000004a },	/*2072X_DAC2_AMP_GAIN_LEFT */
++	{ 0x4414, 0x00000433 },	/*2072X_DAC2_POWER_STATE */
++	{ 0x4418, 0x00000000 },	/*2072X_DAC2_CONVERTER_STREAM_CHANNEL */
++	{ 0x4fc8, 0x00000031 },	/*2072X_ADC1_CONVERTER_FORMAT */
++	{ 0x4d80, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_0 */
++	{ 0x4da0, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_0  */
++	{ 0x4d84, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_1 */
++	{ 0x4da4, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_1 */
++	{ 0x4d88, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_2  */
++	{ 0x4da8, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_2  */
++	{ 0x4d8c, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_3  */
++	{ 0x4dac, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_3  */
++	{ 0x4d90, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_4  */
++	{ 0x4db0, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_4  */
++	{ 0x4d94, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_5  */
++	{ 0x4db4, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_5 */
++	{ 0x4d98, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_6 */
++	{ 0x4db8, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_6 */
++	{ 0x4c04, 0x00000000 },	/*2072X_ADC1_CONNECTION_SELECT_CONTROL */
++	{ 0x4c14, 0x00000433 },	/*2072X_ADC1_POWER_STATE */
++	{ 0x4c18, 0x00000000 },	/*2072X_ADC1_CONVERTER_STREAM_CHANNEL */
++	{ 0x53c8, 0x00000031 },	/*2072X_ADC2_CONVERTER_FORMAT */
++	{ 0x5180, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_RIGHT_0 */
++	{ 0x51a0, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_LEFT_0 */
++	{ 0x5184, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_RIGHT_1 */
++	{ 0x51a4, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_LEFT_1 */
++	{ 0x5188, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_RIGHT_2 */
++	{ 0x51a8, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_LEFT_2 */
++	{ 0x5004, 0x00000000 },	/*2072X_ADC2_CONNECTION_SELECT_CONTROL */
++	{ 0x5014, 0x00000433 },	/*2072X_ADC2_POWER_STATE */
++	{ 0x5018, 0x00000000 },	/*2072X_ADC2_CONVERTER_STREAM_CHANNEL */
++	{ 0x5804, 0x00000000 },	/*2072X_PORTA_CONNECTION_SELECT_CTRL */
++	{ 0x5814, 0x00000433 },	/*2072X_PORTA_POWER_STATE */
++	{ 0x581c, 0x000000c0 },	/*2072X_PORTA_PIN_CTRL */
++	{ 0x5820, 0x00000000 },	/*2072X_PORTA_UNSOLICITED_RESPONSE */
++	{ 0x5824, 0x00000000 },	/*2072X_PORTA_PIN_SENSE */
++	{ 0x5830, 0x00000002 },	/*2072X_PORTA_EAPD_BTL */
++	{ 0x6014, 0x00000433 },	/*2072X_PORTB_POWER_STATE */
++	{ 0x601c, 0x00000000 },	/*2072X_PORTB_PIN_CTRL */
++	{ 0x6020, 0x00000000 },	/*2072X_PORTB_UNSOLICITED_RESPONSE */
++	{ 0x6024, 0x00000000 },	/*2072X_PORTB_PIN_SENSE */
++	{ 0x6030, 0x00000002 },	/*2072X_PORTB_EAPD_BTL */
++	{ 0x6180, 0x00000000 },	/*2072X_PORTB_GAIN_RIGHT */
++	{ 0x61a0, 0x00000000 },	/*2072X_PORTB_GAIN_LEFT */
++	{ 0x6814, 0x00000433 },	/*2072X_PORTC_POWER_STATE */
++	{ 0x681c, 0x00000000 },	/*2072X_PORTC_PIN_CTRL */
++	{ 0x6980, 0x00000000 },	/*2072X_PORTC_GAIN_RIGHT */
++	{ 0x69a0, 0x00000000 },	/*2072X_PORTC_GAIN_LEFT */
++	{ 0x6414, 0x00000433 },	/*2072X_PORTD_POWER_STATE */
++	{ 0x641c, 0x00000020 },	/*2072X_PORTD_PIN_CTRL */
++	{ 0x6420, 0x00000000 },	/*2072X_PORTD_UNSOLICITED_RESPONSE */
++	{ 0x6424, 0x00000000 },	/*2072X_PORTD_PIN_SENSE */
++	{ 0x6580, 0x00000000 },	/*2072X_PORTD_GAIN_RIGHT */
++	{ 0x65a0, 0x00000000 },	/*2072X_PORTD_GAIN_LEFT */
++	{ 0x7404, 0x00000000 },	/*2072X_PORTE_CONNECTION_SELECT_CTRL */
++	{ 0x7414, 0x00000433 },	/*2072X_PORTE_POWER_STATE */
++	{ 0x741c, 0x00000040 },	/*2072X_PORTE_PIN_CTRL */
++	{ 0x7420, 0x00000000 },	/*2072X_PORTE_UNSOLICITED_RESPONSE */
++	{ 0x7424, 0x00000000 },	/*2072X_PORTE_PIN_SENSE */
++	{ 0x7430, 0x00000002 },	/*2072X_PORTE_EAPD_BTL */
++	{ 0x7580, 0x00000000 },	/*2072X_PORTE_GAIN_RIGHT */
++	{ 0x75a0, 0x00000000 },	/*2072X_PORTE_GAIN_LEFT */
++	{ 0x7814, 0x00000433 },	/*2072X_PORTF_POWER_STATE */
++	{ 0x781c, 0x00000000 },	/*2072X_PORTF_PIN_CTRL */
++	{ 0x7820, 0x00000000 },	/*2072X_PORTF_UNSOLICITED_RESPONSE */
++	{ 0x7824, 0x00000000 },	/*2072X_PORTF_PIN_SENSE */
++	{ 0x7980, 0x00000000 },	/*2072X_PORTF_GAIN_RIGHT */
++	{ 0x79a0, 0x00000000 },	/*2072X_PORTF_GAIN_LEFT */
++	{ 0x5c14, 0x00000433 },	/*2072X_PORTG_POWER_STATE */
++	{ 0x5c1c, 0x00000040 },	/*2072X_PORTG_PIN_CTRL */
++	{ 0x5c04, 0x00000000 },	/*2072X_PORTG_CONNECTION_SELECT_CTRL */
++	{ 0x5c30, 0x00000002 },	/*2072X_PORTG_EAPD_BTL */
++	{ 0x8814, 0x00000433 },	/*2072X_PORTM_POWER_STATE */
++	{ 0x881c, 0x00000000 },	/*2072X_PORTM_PIN_CTRL */
++	{ 0x8804, 0x00000000 },	/*2072X_PORTM_CONNECTION_SELECT_CTRL */
++	{ 0x8830, 0x00000002 },	/*2072X_PORTM_EAPD_BTL */
++	{ 0x5414, 0x00000433 },	/*2072X_MIXER_POWER_STATE */
++	{ 0x5580, 0x0000004a },	/*2072X_MIXER_GAIN_RIGHT_0 */
++	{ 0x55a0, 0x0000004a },	/*2072X_MIXER_GAIN_LEFT_0 */
++	{ 0x5584, 0x0000004a },	/*2072X_MIXER_GAIN_RIGHT_1 */
++	{ 0x55a4, 0x0000004a },	/*2072X_MIXER_GAIN_LEFT_1 */
++	{ 0x6d00, 0x0000720c },	/*2072X_EQ_ENABLE_BYPASS */
++	{ 0x6d10, 0x040065a4 },	/*2072X_SPKR_DRC_ENABLE_STEP */
++	{ 0x6d14, 0x007b0024 },	/*2072X_SPKR_DRC_CONTROL */
++	{ 0X6D18, 0x00000000 },	/*2072X_SPKR_DRC_TEST */
++	{ 0x6d80, 0x001f008a },	/*2072X_DIGITAL_BIOS_TEST0 */
++	{ 0x6d84, 0x00990026 },	/*2072X_DIGITAL_BIOS_TEST2 */
++	{ 0x6e00, 0x00010001 },	/*2072X_I2SPCM_CONTROL1 */
++	{ 0x6e04, 0x00000000 },	/*2072X_I2SPCM_CONTROL2 */
++	{ 0x6e08, 0x00000000 },	/*2072X_I2SPCM_CONTROL3 */
++	{ 0x6e0c, 0x00000000 },	/*2072X_I2SPCM_CONTROL4 */
++	{ 0x6e10, 0x00000000 },	/*2072X_I2SPCM_CONTROL5 */
++	{ 0x6e18, 0x00000000 },	/*2072X_I2SPCM_CONTROL6 */
++	{ 0x6e14, 0x00000000 },	/*2072X_UM_INTERRUPT_CRTL_E */
++	{ 0x7310, 0x00000600 },	/*2072X_CODEC_TEST20 */
++	{ 0x7328, 0x00000208 },	/*2072X_CODEC_TEST26 */
++	{ 0x7190, 0x00000000 },	/*2072X_ANALOG_TEST4 */
++	{ 0x7194, 0x00000000 },	/*2072X_ANALOG_TEST5 */
++	{ 0x7198, 0x0000059a },	/*2072X_ANALOG_TEST6 */
++	{ 0x719c, 0x000000a7 },	/*2072X_ANALOG_TEST7 */
++	{ 0x71a0, 0x00000017 },	/*2072X_ANALOG_TEST8 */
++	{ 0x71a4, 0x00000000 },	/*2072X_ANALOG_TEST9 */
++	{ 0x71a8, 0x00000285 },	/*2072X_ANALOG_TEST10 */
++	{ 0x71ac, 0x00000000 },	/*2072X_ANALOG_TEST11 */
++	{ 0x71b0, 0x00000000 },	/*2072X_ANALOG_TEST12 */
++	{ 0x71b4, 0x00000000 },	/*2072X_ANALOG_TEST13 */
++	{ 0x7204, 0x00000242 },	/*2072X_DIGITAL_TEST1 */
++	{ 0x7224, 0x00000000 },	/*2072X_DIGITAL_TEST11 */
++	{ 0x7230, 0x00000084 },	/*2072X_DIGITAL_TEST12 */
++	{ 0x723c, 0x00000077 },	/*2072X_DIGITAL_TEST15 */
++	{ 0x7080, 0x00000021 },	/*2072X_DIGITAL_TEST16 */
++	{ 0x7084, 0x00000018 },	/*2072X_DIGITAL_TEST17 */
++	{ 0x7088, 0x00000024 },	/*2072X_DIGITAL_TEST18 */
++	{ 0x708c, 0x00000001 },	/*2072X_DIGITAL_TEST19 */
++	{ 0x7090, 0x00000002 },	/*2072X_DIGITAL_TEST20 */
++};
++
++
++/*
++ * cx2072x patch.
++ */
++static const struct reg_sequence cx2072x_patch[] = {
++	{ 0x71A4, 0x080 }, /* DC offset Calibration        */
++	{ 0x71a8, 0x287 }, /* Set max spk power to 1.5 W   */
++	{ 0x7328, 0xa8c }, /* Set average spk power to 1.5W*/
++	{ 0x7310, 0xf01 }, /*                             */
++	{ 0x7328, 0xa8f }, /*                              */
++	{ 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*/
++};
++
++
++/* 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_ENABLE_STEP:*/
++	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_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;
++	}
++
++}
++
++#ifdef CXDBG_REG_DUMP
++static const char *cx2072x_get_reg_name(struct device *dev, unsigned int reg)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(cx2072x_regs); i++)
++		if (cx2072x_regs[i].addr == reg)
++			return cx2072x_regs[i].name;
++
++	dev_err(dev, "Unkown reg %08x\n", reg);
++
++	return "Unknown reg";
++}
++#endif
++
++static int cx2072x_reg_write(void *context, unsigned int reg,
++			      unsigned int value)
++{
++	struct i2c_client *client = context;
++	unsigned int i;
++	unsigned int size;
++	uint8_t buf[6];
++	int ret;
++	struct device *dev = &client->dev;
++
++	size = cx2072x_register_size(dev, reg);
++	if (size == 0)
++		return -EINVAL;
++#ifdef CXDBG_REG_DUMP
++	dev_dbg(dev, "I2C write address %40s,%d <= %08x\n",
++		cx2072x_get_reg_name(dev, reg), size, value);
++#endif
++
++	if (reg == CX2072X_UM_INTERRUPT_CRTL_E) {
++		/* workaround to 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) {
++		ret =  0;
++	} else if (ret < 0) {
++		dev_err(dev,
++			"I2C write address failed\n");
++	} else {
++		dev_err(dev,
++			"I2C write failed\n");
++		ret =  -EIO;
++	}
++	return ret;
++}
++
++static int cx2072x_reg_bulk_write(struct snd_soc_codec *codec,
++	unsigned int reg, const void *val, size_t val_count)
++{
++	/*fix me*/
++	struct i2c_client *client = to_i2c_client(codec->dev);
++	uint8_t buf[2 + MAC_EQ_COEFF];
++	int ret;
++	struct device *dev = &client->dev;
++
++#ifdef CXDBG_REG_DUMP
++	dev_dbg(dev, "I2C bulk write address %40s,%zd\n",
++		cx2072x_get_reg_name(dev, reg), val_count);
++#endif
++
++	if (val_count > MAC_EQ_COEFF) {
++		dev_err(dev,
++			"cx2072x_reg_bulk_write failed, writing count = %zd\n",
++			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)
++		return 0;
++	else if (ret < 0) {
++		dev_err(dev,
++			"I2C bulk write address failed\n");
++	} else {
++		dev_err(dev,
++			"I2C bulk write address failed\n");
++		ret = -EIO;
++	}
++	return ret;
++}
++
++
++/* get suggested pre_div valuce from mclk frequency */
++static unsigned int get_div_from_mclk(unsigned int mclk)
++{
++	unsigned int div = 8;
++	int i = 0;
++
++	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_reg_read(void *context, unsigned int reg,
++	unsigned int *value)
++{
++	int ret;
++	unsigned int size;
++	uint8_t send_buf[2];
++	unsigned int recv_buf = 0;
++	struct i2c_client *client = context;
++	struct i2c_msg msgs[2];
++	struct device *dev = &client->dev;
++
++	size = cx2072x_register_size(dev, reg);
++	if (size == 0)
++		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 = (uint8_t *)&recv_buf;
++	msgs[1].flags = I2C_M_RD;
++
++	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++	if (ret < 0) {
++		dev_err(dev,
++			"Failed to register codec: %d\n", ret);
++		return ret;
++	} else if (ret != ARRAY_SIZE(msgs)) {
++		dev_err(dev,
++			"Failed to register codec: %d\n", ret);
++		return -EIO;
++	}
++
++	*value = recv_buf;
++
++#ifdef CXDBG_REG_DUMP
++	dev_dbg(dev,
++		"I2C read address %40s,%d => %08x\n",
++		cx2072x_get_reg_name(dev, reg), size, *value);
++#endif
++	return 0;
++}
++
++static int cx2072x_config_headset_det(struct cx2072x_priv *cx2072x)
++{
++	const int interrupt_gpio_pin = 1;
++
++	dev_dbg(cx2072x->dev,
++		"Configure interrupt pin: %d\n", interrupt_gpio_pin);
++	/*No-sticky input type*/
++	regmap_write(cx2072x->regmap, CX2072X_GPIO_STICKY_MASK, 0x1f);
++	/*Use GPOI0 as intrrupt output 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);
++
++	return 0;
++}
++
++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;
++	unsigned int frac_num = 0;
++	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);
++	pll_input = cx2072x->mclk / 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_num = (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_num & 0xfff);
++		regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST7,
++			(unsigned char)(frac_num >> 12));
++	}
++	int_div--;
++	regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST8,
++		(unsigned char)int_div & 0xffff);
++
++	/* 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;
++
++	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;
++	const int slots_per_channel = cx2072x->tdm_slot_width / BITS_PER_SLOT;
++	const unsigned fmt = cx2072x->dai_fmt;
++
++	REG_I2SPCM_CTRL_REG1 reg1 = { .ulVal = 0 };
++	REG_I2SPCM_CTRL_REG2 reg2 = { .ulVal = 0 };
++	REG_I2SPCM_CTRL_REG3 reg3 = { .ulVal = 0 };
++	REG_I2SPCM_CTRL_REG4 reg4 = { .ulVal = 0 };
++	REG_I2SPCM_CTRL_REG5 reg5 = { .ulVal = 0 };
++	REG_I2SPCM_CTRL_REG6 reg6 = { .ulVal = 0 };
++	REG_DIGITAL_BIOS_TEST2 regDBT2 = { .ulVal = 0 };
++
++	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;
++	}
++
++	/*fix me */
++	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_err(dev, "DAI master mode is not implemented yet.\n");
++		return -EINVAL;
++	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:
++		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;
++	case SND_SOC_DAIFMT_DSP_A:
++		has_one_bit_delay = 1;
++		break;
++	case SND_SOC_DAIFMT_DSP_B:
++		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 ? 1 : 0;
++		is_bclk_inv = is_i2s ? 1 : 0;
++		break;
++	case SND_SOC_DAIFMT_IB_IF:
++		is_frame_inv = is_i2s ? 0 : 1;
++		is_bclk_inv = is_i2s ? 0 : 1;
++		break;
++	case SND_SOC_DAIFMT_IB_NF:
++		is_frame_inv = is_i2s ? 1 : 0;
++		is_bclk_inv = is_i2s ? 0 : 1;
++		break;
++	case SND_SOC_DAIFMT_NB_IF:
++		is_frame_inv = is_i2s ? 0 : 1;
++		is_bclk_inv = is_i2s ? 1 : 0;
++		break;
++	default:
++		dev_err(dev, "Unsupported DAI clock inversion\n");
++		return -EINVAL;
++	}
++
++	cx2072x->dai_fmt = fmt;
++
++	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 ? 0 : 1;
++	reg2.r.tx_dstart_dly = has_one_bit_delay;
++
++	reg3.r.rx_endian_sel = is_big_endian ? 0 : 1;
++	reg3.r.rx_dstart_dly = has_one_bit_delay;
++
++	if (is_i2s) {
++		reg2.r.tx_en_ch1 = 1;
++		reg2.r.tx_en_ch2 = 1;
++		reg2.r.tx_slot_1 = 0;
++		reg2.r.tx_slot_2 = i2s_right_slot;
++		reg3.r.rx_en_ch1 = 1;
++		reg3.r.rx_en_ch2 = 1;
++		reg3.r.rx_slot_1 = 0;
++		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 {
++		reg2.r.tx_en_ch1 = cx2072x->tdm_tx_mask & 0x01 ? 1 : 0;
++		reg2.r.tx_en_ch2 = cx2072x->tdm_tx_mask & 0x02 ? 1 : 0;
++		reg2.r.tx_en_ch3 = cx2072x->tdm_tx_mask & 0x04 ? 1 : 0;
++		reg2.r.tx_en_ch4 = cx2072x->tdm_tx_mask & 0x08 ? 1 : 0;
++		reg2.r.tx_slot_1 = 0;
++		reg2.r.tx_slot_2 = slots_per_channel * 1;
++		reg2.r.tx_slot_3 = slots_per_channel * 2;
++		reg2.r.tx_slot_4 = slots_per_channel * 3;
++
++		reg3.r.rx_en_ch1 = cx2072x->tdm_rx_mask & 0x01 ? 1 : 0;
++		reg3.r.rx_en_ch2 = cx2072x->tdm_rx_mask & 0x02 ? 1 : 0;
++		reg3.r.rx_en_ch3 = cx2072x->tdm_rx_mask & 0x04 ? 1 : 0;
++		reg3.r.rx_en_ch4 = cx2072x->tdm_rx_mask & 0x08 ? 1 : 0;
++		reg3.r.rx_slot_1 = 0;
++		reg3.r.rx_slot_2 = slots_per_channel * 1;
++		reg3.r.rx_slot_3 = slots_per_channel * 2;
++		reg3.r.rx_slot_4 = slots_per_channel * 3;
++	}
++	regDBT2.r.i2s_bclk_invert = is_bclk_inv;
++
++	reg1.r.rx_data_one_line = 1;
++	reg1.r.tx_data_one_line = 1;
++
++#ifdef ENABLE_MIC_POP_WA
++	/*Mute I2S TX*/
++	reg4.ulVal |= 0x2;
++#endif
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL1, reg1.ulVal);
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL2, reg2.ulVal);
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL3, reg3.ulVal);
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL4, reg4.ulVal);
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL5, reg5.ulVal);
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL6, reg6.ulVal);
++	/*enable bclk and EAPD input*/
++	if (cx2072x->rev_id == CX2072X_REV_A2)
++		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
++			0x84, 0xFF);
++	else
++		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
++			regDBT2.ulVal);
++
++	return 0;
++}
++
++
++static void cx2072x_dsp_init(struct snd_soc_codec *codec)
++{
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	int i, j, band;
++	unsigned char *pCoef = &cx2072x_eq_coeff_array[0][0];
++
++	regmap_write(cx2072x->regmap, CX2072X_EQ_ENABLE_BYPASS, 0x6e0f);
++
++	for (i = 0; i < MAX_EQ_BAND; i++) {
++		for (j = 0; j < 2; j++) {
++			cx2072x_reg_bulk_write(codec, CX2072X_EQ_B0_COEFF,
++				pCoef + (MAC_EQ_COEFF*i), MAC_EQ_COEFF);
++			band = i + (j << 3) + (1 << 6);
++			regmap_write(cx2072x->regmap, CX2072X_EQ_BAND, band);
++			mdelay(5);
++		}
++	}
++
++	cx2072x_reg_bulk_write(codec, CX2072X_SPKR_DRC_ENABLE_STEP,
++		cx2072x_drc_array, MAX_DRC_REGS);
++}
++static void cx2072x_update_dsp(struct snd_soc_codec *codec)
++{
++	unsigned int afg_reg;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++	regmap_read(cx2072x->regmap, CX2072X_AFG_POWER_STATE, &afg_reg);
++
++	if (!cx2072x->plbk_dsp_changed) {
++		/*nothing change*/
++		return;
++	}
++
++	if ((afg_reg & 0xf) != 0) {
++		/*skip since device is on D3 mode*/
++		return;
++	}
++
++	if (cx2072x->plbk_dsp_en && !cx2072x->plbk_dsp_init) {
++		cx2072x_dsp_init(codec);
++		cx2072x->plbk_dsp_init = true;
++	}
++
++	if (cx2072x->plbk_dsp_en) {
++		regmap_write(cx2072x->regmap, CX2072X_EQ_ENABLE_BYPASS,
++			0x6203);
++		regmap_write(cx2072x->regmap, CX2072X_SPKR_DRC_ENABLE_STEP,
++			0x65);
++	} else {
++		/*By pass DRC and EQ.*/
++		regmap_write(cx2072x->regmap, CX2072X_EQ_ENABLE_BYPASS,
++			0x620c);
++		regmap_write(cx2072x->regmap, CX2072X_SPKR_DRC_ENABLE_STEP,
++			0xa4);
++	}
++	cx2072x->plbk_dsp_changed = false;
++}
++
++
++
++static int afg_power_ev(struct snd_soc_dapm_widget *w,
++	struct snd_kcontrol *kcontrol, int event)
++{
++	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(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;
++}
++
++#ifdef ENABLE_MIC_POP_WA
++/*
++ * This work will be called at ADC widget power on time.
++ * to reduce initial mic pop noise caused by hardware
++ */
++
++static void cx2072x_anit_mic_pop_work(struct work_struct *work)
++{
++	struct snd_soc_dapm_context *dapm =
++		container_of(work, struct snd_soc_dapm_context,
++		delayed_work.work);
++	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++	dev_dbg(cx2072x->dev, "Unmute I2S TX\n");
++	/*Unmute I2S TX*/
++
++	regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL4,
++		0x2, 0x0);
++}
++#endif
++
++static int adc1_power_ev(struct snd_soc_dapm_widget *w,
++	struct snd_kcontrol *kcontrol, int event)
++{
++#ifdef ENABLE_MIC_POP_WA
++	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++	switch (event) {
++	case SND_SOC_DAPM_POST_PMU:
++		/* Umute I2S TX after 300 ms to get around the mic
++		 * pop noise issue.
++		 */
++		schedule_delayed_work(&codec->dapm.delayed_work,
++			msecs_to_jiffies(300));
++		break;
++
++	case SND_SOC_DAPM_POST_PMD:
++		/*Mute TX I2S*/
++
++		regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL4,
++			0x2, 0x2);
++		break;
++	}
++#endif
++	return 0;
++}
++
++
++static int portg_power_ev(struct snd_soc_dapm_widget *w,
++	struct snd_kcontrol *kcontrol, int event)
++{
++	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
++
++	switch (event) {
++	case SND_SOC_DAPM_POST_PMU:
++		cx2072x_update_dsp(codec);
++		break;
++	default:
++		break;
++	}
++	return 0;
++}
++static int cx2072x_plbk_dsp_info(struct snd_kcontrol *kcontrol,
++	struct snd_ctl_elem_info *uinfo)
++{
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++	uinfo->count = 1;
++	uinfo->value.integer.min = 0;
++	uinfo->value.integer.max = 1;
++	return 0;
++}
++
++static int cx2072x_plbk_dsp_get(struct snd_kcontrol *kcontrol,
++	struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++	ucontrol->value.integer.value[0] = cx2072x->plbk_dsp_en;
++
++	return 0;
++}
++
++static int cx2072x_plbk_dsp_put(struct snd_kcontrol *kcontrol,
++	struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	const bool en_dsp = ucontrol->value.integer.value[0];
++
++	if (ucontrol->value.integer.value[0] > 1)
++		return -EINVAL;
++
++	if (cx2072x->plbk_dsp_en != en_dsp) {
++		cx2072x->plbk_dsp_en = en_dsp;
++		cx2072x->plbk_dsp_changed = true;
++		cx2072x_update_dsp(codec);
++	}
++	return 0;
++}
++
++
++#define CX2072X_PLBK_DSP_SWITCH(xname) {\
++	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
++	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
++	.info = cx2072x_plbk_dsp_info, \
++	.get = cx2072x_plbk_dsp_get, .put = cx2072x_plbk_dsp_put}
++
++static const struct snd_kcontrol_new cx2072x_snd_controls[] = {
++
++	SOC_DOUBLE_R_TLV("PortD Boost", CX2072X_PORTD_GAIN_LEFT,
++	CX2072X_PORTD_GAIN_RIGHT, 0, 3, 0, boost_tlv),
++	SOC_DOUBLE_R_TLV("PortC Boost", CX2072X_PORTC_GAIN_LEFT,
++	CX2072X_PORTC_GAIN_RIGHT, 0, 3, 0, boost_tlv),
++	SOC_DOUBLE_R_TLV("PortB Boost", 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", 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_SWITCH("Playback DSP Switch"),
++};
++
++/**
++* cx2072x_hs_jack_report: Report jack notification to upper layer
++* @codec : pointer variable to codec having information related to codec
++* @jack : Pointer variable to snd_soc_jack having information of codec
++* and pin number$
++* @report : Provides informaton of whether it is headphone or microphone
++*
++*/
++int cx2072x_hs_jack_report(struct snd_soc_codec *codec)
++{
++	unsigned int jack;
++	unsigned int type = 0;
++	int  state = 0;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++
++	regcache_cache_bypass(cx2072x->regmap, true);
++	cx2072x->jack_state = CX_JACK_NONE;
++	regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack);
++	jack = jack >> 24;
++	regmap_read(cx2072x->regmap, CX2072X_DIGITAL_TEST11, &type);
++	regcache_cache_bypass(cx2072x->regmap, false);
++	if (jack == 0x80) {
++		type = type >> 8;
++
++		if (type & 0x8) {
++			state |= SND_JACK_HEADSET;
++			cx2072x->jack_state = CX_JACK_APPLE_HEADSET;
++			if (type & 0x2)
++				state |= SND_JACK_BTN_0;
++		} else if (type & 0x4) {
++			state |= SND_JACK_HEADPHONE;
++			cx2072x->jack_state = CX_JACK_NOKIE_HEADSET;
++		} else {
++			state |= SND_JACK_HEADPHONE;
++			cx2072x->jack_state = CX_JACK_HEADPHONE;
++		}
++
++	}
++
++
++	/* clear interrupt */
++	regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);
++
++	dev_err(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n",
++		type, state);
++	return state;
++}
++EXPORT_SYMBOL_GPL(cx2072x_hs_jack_report);
++
++
++
++
++
++static int cx2072x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
++	unsigned int rx_mask, int slots, int slot_width)
++{
++	struct snd_soc_codec *codec = dai->codec;
++	struct cx2072x_priv *cx2072x = snd_soc_codec_get_drvdata(codec);
++
++	if (slots == 0)
++		goto out;
++
++
++	switch (rx_mask) {
++	case 1 ... 0xf:
++	default:
++		return -EINVAL;
++	}
++
++
++	switch (tx_mask) {
++	case 1 ... 0xf:
++	default:
++		return -EINVAL;
++	}
++
++out:
++	cx2072x->tdm_rx_mask = rx_mask;
++	cx2072x->tdm_tx_mask = tx_mask;
++	cx2072x->tdm_slot_width = slot_width;
++	cx2072x->tdm_slots = slots;
++	return 0;
++}
++
++static int cx2072x_hw_params(struct snd_pcm_substream *substream,
++	struct snd_pcm_hw_params *params,
++	struct snd_soc_dai *dai)
++{
++	struct snd_soc_codec *codec = dai->codec;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(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->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 (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 int cx2072x_digital_mute(struct snd_soc_dai *dai, int mute)
++{
++	return 0;
++}
++
++static int cx2072x_set_dai_bclk_ratio(struct snd_soc_dai *dai,
++				      unsigned int ratio)
++{
++	struct snd_soc_codec *codec = dai->codec;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(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_codec *codec = dai->codec;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	struct device *dev = codec->dev;
++
++	if (freq == 0) {
++		dev_dbg(dev, "MCLK: Switch to internal OSC\n");
++		return 0;
++	}
++	cx2072x->mclk = freq;
++	switch (clk_id) {
++	case CX2072X_MCLK_EXTERNAL_PLL:
++		dev_dbg(dev, "MCLK: Switch to external PLL\n");
++		break;
++	case CX2072X_MCLK_INTERNAL_OSC:
++		dev_err(dev, "Unsupported DAI format\n");
++		break;
++	default:
++		dev_dbg(dev, "the MCLK is not configured\n");
++		break;
++	}
++
++	return 0;
++}
++
++static int cx2072x_set_dai_fmt(struct snd_soc_dai *dai,
++	unsigned int fmt)
++{
++	struct snd_soc_codec *codec = dai->codec;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	struct device *dev = codec->dev;
++
++	/* 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:
++	case SND_SOC_DAIFMT_DSP_A:
++	case SND_SOC_DAIFMT_DSP_B:
++		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 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 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);
++
++#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_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_REG(snd_soc_dapm_switch, "PortA",
++		CX2072X_PORTA_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "PortG",
++		CX2072X_PORTG_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	CX2072X_DAPM_SWITCH("PortG", 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_OUTPUT("PORTA"),
++	SND_SOC_DAPM_OUTPUT("PORTG"),
++	SND_SOC_DAPM_OUTPUT("PORTE"),
++
++	/*Capture*/
++	SND_SOC_DAPM_AIF_OUT("Out AIF", "Capture", 0, SND_SOC_NOPM, 0, 0),
++
++	CX2072X_DAPM_REG_E(snd_soc_dapm_adc, "ADC1", CX2072X_ADC1_POWER_STATE,
++		0, 0xFF, 0x00, 0x03, adc1_power_ev,
++		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
++
++	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_REG(snd_soc_dapm_switch, "PortB",
++		CX2072X_PORTB_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "PortC",
++		CX2072X_PORTC_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "PortD",
++		CX2072X_PORTD_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "PortE",
++		CX2072X_PORTE_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "Widget15",
++		CX2072X_MIXER_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++
++	SND_SOC_DAPM_INPUT("PORTB"),
++	SND_SOC_DAPM_INPUT("PORTC"),
++	SND_SOC_DAPM_INPUT("PORTD"),
++
++	SND_SOC_DAPM_MICBIAS("Headset Bias", CX2072X_ANALOG_TEST11, 1, 0),
++	SND_SOC_DAPM_MICBIAS("PortD Mic Bias", CX2072X_PORTD_PIN_CTRL, 2, 0),
++	SND_SOC_DAPM_MICBIAS("PortB Mic Bias", CX2072X_PORTB_PIN_CTRL, 2, 0),
++
++};
++
++static const struct snd_soc_dapm_route cx2072x_intercon[] = {
++
++	/* Playback */
++	{"In AIF", NULL, "AFG Power"},
++	{"DAC1", NULL, "In AIF"},
++	{"DAC2", NULL, "In AIF"},
++	{"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"},
++	{"Widget15", NULL, "DAC1"},
++	{"Widget15", NULL, "DAC2"},
++	{"PortA", NULL, "PortA Mux"},
++	{"PortG", NULL, "PortG Mux"},
++	{"PortE", NULL, "PortE Mux"},
++	{"PORTA", NULL, "PortA"},
++	{"PORTG", NULL, "PortG"},
++	{"PORTE", NULL, "PortE"},
++
++	/* Capture */
++	{"PORTD", NULL, "Headset Bias"},
++	{"PortD", NULL, "PORTD"},
++	{"PortC", NULL, "PORTC"},
++	{"PortB", NULL, "PORTB"},
++	{"ADC1 Mux", "PortD Switch", "PortD"},
++	{"ADC1 Mux", "PortC Switch", "PortC"},
++	{"ADC1 Mux", "PortB Switch", "PortB"},
++	{"ADC1 Mux", "Widget15 Switch", "Widget15"},
++	{"ADC1", NULL, "ADC1 Mux"},
++	{"Out AIF", NULL, "ADC1"},
++	{"Out AIF", NULL, "AFG 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_codec *codec)
++{
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	int ret;
++
++	cx2072x_sw_reset(cx2072x);
++
++	cx2072x->plbk_dsp_changed = true;
++	cx2072x->plbk_dsp_init = false;
++
++	regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_TEST15,
++		0x00, 0x06);  /* reduce the monitor time.*/
++
++	cx2072x_config_headset_det(cx2072x);
++
++	regmap_update_bits(cx2072x->regmap, CX2072X_PORTC_PIN_CTRL,
++		0x20, 0x20);  /*reduce the monitor time*/
++
++	ret = regmap_register_patch(cx2072x->regmap, cx2072x_patch,
++		ARRAY_SIZE(cx2072x_patch));
++	if (ret)
++		return ret;
++
++	/*enable bclk and EAPD input*/
++	if (cx2072x->rev_id == CX2072X_REV_A2)
++		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
++			0x84, 0xFF);
++
++	return 0;
++}
++static int cx2072x_set_bias_level(struct snd_soc_codec *codec,
++enum snd_soc_bias_level level)
++{
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	const enum snd_soc_bias_level old_level = 
++		 snd_soc_codec_get_bias_level(codec);
++
++	switch (level) {
++	case SND_SOC_BIAS_ON:
++		dev_dbg(cx2072x->dev, "SND_SOC_BIAS_ON\n");
++		/* Enable Headset Mic Bias */
++		if (cx2072x->is_biason == 0)
++			cx2072x->is_biason = 1;
++		break;
++	case SND_SOC_BIAS_PREPARE:
++		dev_dbg(cx2072x->dev, "SND_SOC_BIAS_PREPARE\n");
++		if (SND_SOC_BIAS_STANDBY == old_level) {
++			dev_dbg(cx2072x->dev, "SND_SOC_BIAS_STANDBY = > SND_SOC_BIAS_PREPARE\n");
++#ifdef INTEL_MCLK_CONTROL
++			/*FIXME: added some delay to make sure the MCLK
++			 * is stable before Codec is on. */
++			mdelay(3);
++			dev_dbg(cx2072x->dev, "Turn on MCLK\n");
++#endif
++		}
++		break;
++	case SND_SOC_BIAS_STANDBY:
++		dev_dbg(cx2072x->dev, "SND_SOC_BIAS_STANDBY\n");
++
++		if (SND_SOC_BIAS_OFF == old_level) {
++			dev_dbg(codec->dev, "Initialize codec\n");
++#ifdef INTEL_MCLK_CONTROL
++			dev_dbg(cx2072x->dev, "Turn on MCLK\n");
++			/*FIXME: added some delay to make sure the MCLK is
++			  stable before Codec is on. */
++			mdelay(3);
++#endif
++			cx2072x_init(codec);
++		}
++	/*power device into standby mode*/
++#ifdef INTEL_MCLK_CONTROL
++		dev_dbg(cx2072x->dev, "Turn off MCLK\n");
++#endif
++		break;
++	case SND_SOC_BIAS_OFF:
++		dev_dbg(cx2072x->dev, "SND_SOC_BIAS_OFF\n");
++		/* Gracefully shutdwon codec*/
++
++		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_PORTG_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);
++		regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 3);
++		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0,
++			0x10, 0x10);
++		/*Shutdown codec completely*/
++		cx2072x_sw_reset(cx2072x);
++		break;
++	}
++	return 0;
++}
++
++
++static int cx2072x_probe(struct snd_soc_codec *codec)
++{
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	int ret = 0;
++	unsigned int ven_id;
++
++	cx2072x->codec = codec;
++	codec->control_data = cx2072x->regmap;
++
++	dev_dbg(codec->dev, "codec version: 4.4.18\n");
++	regmap_read(cx2072x->regmap, CX2072X_VENDOR_ID, &ven_id);
++	regmap_read(cx2072x->regmap, CX2072X_REVISION_ID, &cx2072x->rev_id);
++	dev_dbg(codec->dev, "codec version: %08x,%08x\n",
++		ven_id, cx2072x->rev_id);
++
++#ifdef ENABLE_MIC_POP_WA
++	INIT_DELAYED_WORK(&codec->dapm.delayed_work,
++		cx2072x_anit_mic_pop_work);
++#endif
++
++	/*power on device*/
++	cx2072x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
++
++	return ret;
++}
++
++static int cx2072x_remove(struct snd_soc_codec *codec)
++{
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	/*power off device*/
++	cx2072x_set_bias_level(cx2072x->codec, SND_SOC_BIAS_OFF);
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int cx2072x_runtime_suspend(struct device *dev)
++{
++	struct cx2072x_priv *cx2072x = dev_get_drvdata(dev);
++
++	dev_dbg(cx2072x->codec->dev, "%s----%d\n", __func__, __LINE__);
++	cx2072x_set_bias_level(cx2072x->codec, SND_SOC_BIAS_OFF);
++
++	return 0;
++}
++
++static int cx2072x_runtime_resume(struct device *dev)
++{
++	struct cx2072x_priv *cx2072x = dev_get_drvdata(dev);
++
++	dev_dbg(cx2072x->codec->dev, "%s----%d\n", __func__, __LINE__);
++	cx2072x_set_bias_level(cx2072x->codec, SND_SOC_BIAS_STANDBY);
++	return 0;
++}
++#else
++#define cx2072x_suspend NULL
++#define cx2072x_resume NULL
++#endif
++
++
++
++
++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_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 struct snd_soc_codec_driver soc_codec_driver_cx2072x = {
++	.probe = cx2072x_probe,
++	.remove = cx2072x_remove,
++	.set_bias_level = cx2072x_set_bias_level,
++	.component_driver = {
++		.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,
++	.set_tdm_slot = cx2072x_set_tdm_slot,
++	.hw_params = cx2072x_hw_params,
++	.digital_mute = cx2072x_digital_mute,
++	.set_bclk_ratio	= cx2072x_set_dai_bclk_ratio
++};
++
++/*
++ * DAI driver
++ */
++static struct snd_soc_dai_driver soc_codec_cx2072x_dai = {
++	.name = "cx2072x-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,
++};
++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,
++	.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)
++{
++	int ret = -1;
++	struct cx2072x_priv *cx2072x;
++
++	dev_dbg(&i2c->dev, "CX2072X codec driver i2c probe() is called\n");
++
++	cx2072x = (struct cx2072x_priv *)devm_kzalloc(
++		&i2c->dev, sizeof(struct cx2072x_priv), GFP_KERNEL);
++	if (cx2072x == NULL) {
++		dev_err(&i2c->dev, "Out of memory!\n");
++		return -ENOMEM;
++	}
++
++	mutex_init(&cx2072x->lock);
++
++	cx2072x->regmap = devm_regmap_init(&i2c->dev, NULL, i2c,
++		&cx2072x_regmap);
++	if (IS_ERR(&cx2072x->regmap)) {
++		ret = PTR_ERR(cx2072x->regmap);
++		dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
++		return ret;
++	}
++
++	i2c_set_clientdata(i2c, cx2072x);
++
++	cx2072x->dev = &i2c->dev;
++	cx2072x->pll_changed = true;
++	cx2072x->i2spcm_changed = true;
++
++	/* setts the frame size to
++	 * Frame size = number of channel * sample width
++	 */
++	cx2072x->bclk_ratio = 0;
++
++	ret = snd_soc_register_codec(cx2072x->dev,
++		&soc_codec_driver_cx2072x, &soc_codec_cx2072x_dai,
++		1);
++	if (ret < 0)
++		dev_err(cx2072x->dev,
++		"Failed to register codec: %d\n", ret);
++	else
++		dev_dbg(cx2072x->dev,
++		"%s: Register codec.\n", __func__);
++
++	return ret;
++}
++
++static int cx2072x_i2c_remove(struct i2c_client *client)
++{
++	snd_soc_unregister_codec(&client->dev);
++	return 0;
++}
++
++static void cx2072x_i2c_shutdown(struct i2c_client *client)
++{
++	struct cx2072x_priv *cx2072x = i2c_get_clientdata(client);
++	cx2072x_set_bias_level(cx2072x->codec, SND_SOC_BIAS_OFF);
++}
++
++
++const struct dev_pm_ops cx2072x_pm_ops = {
++	SET_RUNTIME_PM_OPS(cx2072x_runtime_suspend, cx2072x_runtime_resume,
++	NULL)
++};
++static const struct i2c_device_id cx2072x_i2c_id[] = {
++	{ "cx20721", 0 },
++	{ "cx20722", 0 },
++	{ "14F10720", 0 },
++	{}
++};
++MODULE_DEVICE_TABLE(i2c, cx2072x_i2c_id);
++
++static const struct of_device_id cx2072x_of_match[] = {
++	{ .compatible = "cnxt,cx20721", },
++	{ .compatible = "cnxt,cx20723", },
++	{ .compatible = "cnxt,cx7601", },
++	{}
++};
++MODULE_DEVICE_TABLE(of, cx2072x_of_match);
++#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,
++	.shutdown = cx2072x_i2c_shutdown,
++	.id_table = cx2072x_i2c_id,
++	.driver = {
++		.name = "cx2072x",
++		.owner = THIS_MODULE,
++		.of_match_table = cx2072x_of_match,
++#ifdef CONFIG_ACPI
++		.acpi_match_table = ACPI_PTR(cx2072x_acpi_match),
++#endif
++		.pm	= &cx2072x_pm_ops,
++	},
++};
++
++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..67b90c184c6b
+--- /dev/null
++++ b/sound/soc/codecs/cx2072x.h
+@@ -0,0 +1,339 @@
++/*
++ * ALSA SoC CX2072x Solana codec driver
++ *
++ * Copyright:   (C) 2016 Conexant Systems, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ ************************************************************************/
++
++#define NUM_OF_DAI 1
++#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 AUDDRV_VERSION(major0, major1, minor, build) \
++		((major0)<<24 | (major1)<<16 | (minor)<<8 | (build))
++
++#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_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 INVALID_GPIO -1
++#define MAX_EQ_BAND 7
++#define MAC_EQ_COEFF 11
++#define MAX_DRC_REGS 9
++#define MIC_EQ_COEFF 10
++/*
++static unsigned char cx2072x_eq_coeff_flat[MAX_EQ_BAND][MAC_EQ_COEFF] = {
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++};
++*/
++/*
++static unsigned char cx2072x_miceq_coeff_array[][MIC_EQ_COEFF] = {
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x42, 0x17, 0x85, 0x2e, 0x42, 0x17, 0x0a, 0x5d, 0xed, 0xc5},
++};
++*/
++
++
++enum cx2072x_jack_types {
++	CX_JACK_NONE = 0x0000,
++	CX_JACK_HEADPHONE = 0x0001,
++	CX_JACK_APPLE_HEADSET = 0x0002,
++	CX_JACK_NOKIE_HEADSET = 0x0003,
++
++};
++
++int cx2072x_hs_jack_report(struct snd_soc_codec *codec);
++
++typedef enum _REG_SAMPLE_SIZE{
++	SAMPLE_SIZE_8_BITS = 0 ,
++	SAMPLE_SIZE_16_BITS = 1 ,
++	SAMPLE_SIZE_24_BITS = 2 ,
++	SAMPLE_SIZE_RESERVED = 3 
++}REG_SAMPLE_SIZE;
++
++typedef union _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;
++}REG_I2SPCM_CTRL_REG1;
++
++
++typedef union _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;
++}REG_I2SPCM_CTRL_REG2;
++
++typedef union _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;
++}REG_I2SPCM_CTRL_REG3;
++
++
++typedef union _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;
++}REG_I2SPCM_CTRL_REG4;
++
++
++typedef union _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;
++}REG_I2SPCM_CTRL_REG5;
++
++typedef union _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;
++}REG_I2SPCM_CTRL_REG6;
++
++typedef union _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_sheild_clk:1;
++		u32  audio_pll_bypass_mode:1;
++		u32  reserved          :4;
++	}r;
++	u32 ulVal;
++}REG_DIGITAL_BIOS_TEST2;
+diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
+index 10c2a564a715..77bde13147bd 100644
+--- a/sound/soc/codecs/rt5645.c
++++ b/sound/soc/codecs/rt5645.c
+@@ -3545,8 +3545,10 @@ MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
+ #ifdef CONFIG_ACPI
+ static const struct acpi_device_id rt5645_acpi_match[] = {
+ 	{ "10EC5645", 0 },
++	{ "10EC5648", 0 },
+ 	{ "10EC5650", 0 },
+ 	{ "10EC5640", 0 },
++	{ "10EC3270", 0 },
+ 	{},
+ };
+ MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
+@@ -3658,8 +3660,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
+ 						       GPIOD_IN);
+ 
+ 	if (IS_ERR(rt5645->gpiod_hp_det)) {
+-		dev_err(&i2c->dev, "failed to initialize gpiod\n");
+-		return PTR_ERR(rt5645->gpiod_hp_det);
++		dev_info(&i2c->dev, "failed to initialize gpiod\n");
+ 	}
+ 
+ 	for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++)
+diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
+index 49caf1393aeb..fdc14e50d3b9 100644
+--- a/sound/soc/codecs/rt5670.c
++++ b/sound/soc/codecs/rt5670.c
+@@ -2813,6 +2813,8 @@ MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
+ #ifdef CONFIG_ACPI
+ static const struct acpi_device_id rt5670_acpi_match[] = {
+ 	{ "10EC5670", 0},
++	{ "10EC5672", 0},
++	{ "10EC5640", 0}, /* quirk */
+ 	{ },
+ };
+ MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
+diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
+index be1a64bfd320..a1b4e14b347a 100644
+--- a/sound/soc/codecs/tlv320aic31xx.c
++++ b/sound/soc/codecs/tlv320aic31xx.c
+@@ -192,46 +192,68 @@ static const struct aic31xx_rate_divs aic31xx_divs[] = {
+ 	{12000000,   8000,	8, 1920,	128,  48,  2,	128,  48,  2},
+ 	{12000000,   8000,	8, 1920,	128,  32,  3,	128,  32,  3},
+ 	{12500000,   8000,	7, 8643,	128,  48,  2,	128,  48,  2},
++	{19200000,   8000,	5, 1200,	128,  48,  2,	128,  48,  2},
++	{19200000,   8000,	5, 1200,	128,  32,  3,	128,  32,  3},
+ 	/* 11.025k rate */
+ 	{12000000,  11025,	7, 5264,	128,  32,  2,	128,  32,  2},
+ 	{12000000,  11025,	8, 4672,	128,  24,  3,	128,  24,  3},
+ 	{12500000,  11025,	7, 2253,	128,  32,  2,	128,  32,  2},
++	{19200000,  11025,	4, 7040,	128,  24,  2,	128,  32,  2},
++	{19200000,  11025,      5, 2920,	128,  32,  3,	128,  24,  3},
+ 	/* 16k rate */
+ 	{12000000,  16000,	8, 1920,	128,  24,  2,	128,  24,  2},
+ 	{12000000,  16000,	8, 1920,	128,  16,  3,	128,  16,  3},
+ 	{12500000,  16000,	7, 8643,	128,  24,  2,	128,  24,  2},
++	{19200000,  16000,	5, 1200,	128,  24,  2,	128,  24,  2},
++	{19200000,  16000,	5, 1200,	128,  16,  3,	128,  16,  3},
+ 	/* 22.05k rate */
+ 	{12000000,  22050,	7, 5264,	128,  16,  2,	128,  16,  2},
+ 	{12000000,  22050,	8, 4672,	128,  12,  3,	128,  12,  3},
+ 	{12500000,  22050,	7, 2253,	128,  16,  2,	128,  16,  2},
++	{19200000,  22050,	4, 7040,	128,  16,  2,	128,  16,  2},
++	{19200000,  22050,      5, 2920,	128,  12,  3,	128,  12,  3},
+ 	/* 32k rate */
+ 	{12000000,  32000,	8, 1920,	128,  12,  2,	128,  12,  2},
+ 	{12000000,  32000,	8, 1920,	128,   8,  3,	128,   8,  3},
+ 	{12500000,  32000,	7, 8643,	128,  12,  2,	128,  12,  2},
++	{19200000,  32000,	1, 0000,	 32,   1,  2,	 32,   1,  2},
++	{19200000,  32000,	5, 1200,	128,   8,  3,	128,   8,  3},
+ 	/* 44.1k rate */
+ 	{12000000,  44100,	7, 5264,	128,   8,  2,	128,   8,  2},
+ 	{12000000,  44100,	8, 4672,	128,   6,  3,	128,   6,  3},
+ 	{12500000,  44100,	7, 2253,	128,   8,  2,	128,   8,  2},
++	{19200000,  44100,	4, 7040,	128,   8,  2,	128,   8,  2},
++	{19200000,  44100,      5, 2920,	128,   6,  3,	128,   6,  3},
+ 	/* 48k rate */
+ 	{12000000,  48000,	8, 1920,	128,   8,  2,	128,   8,  2},
+ 	{12000000,  48000,	7, 6800,	 96,   5,  4,	 96,   5,  4},
+ 	{12500000,  48000,	7, 8643,	128,   8,  2,	128,   8,  2},
++	{19200000,  48000,	5, 1200,	128,   8,  2,	128,   8,  2},
++	{19200000,  48000,	4, 8000,	 96,   5,  4,	 96,   5,  4},
+ 	/* 88.2k rate */
+ 	{12000000,  88200,	7, 5264,	 64,   8,  2,	 64,   8,  2},
+ 	{12000000,  88200,	8, 4672,	 64,   6,  3,	 64,   6,  3},
+ 	{12500000,  88200,	7, 2253,	 64,   8,  2,	 64,   8,  2},
++	{19200000,  88200,	4, 7040,	 64,   8,  2,	 64,   8,  2},
++	{19200000,  88200,      5, 2920,	 64,   6,  3,	 64,   6,  3},
+ 	/* 96k rate */
+ 	{12000000,  96000,	8, 1920,	 64,   8,  2,	 64,   8,  2},
+ 	{12000000,  96000,	7, 6800,	 48,   5,  4,	 48,   5,  4},
+ 	{12500000,  96000,	7, 8643,	 64,   8,  2,	 64,   8,  2},
++	{19200000,  96000,	5, 1200,	 64,   8,  2,	 64,   8,  2},
++	{19200000,  96000,	4, 8000,	 48,   5,  4,	 48,   5,  4},
+ 	/* 176.4k rate */
+ 	{12000000, 176400,	7, 5264,	 32,   8,  2,	 32,   8,  2},
+ 	{12000000, 176400,	8, 4672,	 32,   6,  3,	 32,   6,  3},
+ 	{12500000, 176400,	7, 2253,	 32,   8,  2,	 32,   8,  2},
++	{19200000, 176400,	4, 7040,	 32,   8,  2,	 32,   8,  2},
++	{19200000, 176400,      5, 2920,	 32,   6,  3,	 32,   6,  3},
+ 	/* 192k rate */
+ 	{12000000, 192000,	8, 1920,	 32,   8,  2,	 32,   8,  2},
+ 	{12000000, 192000,	7, 6800,	 24,   5,  4,	 24,   5,  4},
+ 	{12500000, 192000,	7, 8643,	 32,   8,  2,	 32,   8,  2},
++	{19200000, 192000,	5, 1200,	 32,   8,  2,	 32,   8,  2},
++	{19200000, 192000,	4, 8000,	 24,   5,  4,	 24,   5,  4},
+ };
+ 
+ static const char * const ldac_in_text[] = {
+diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
+index fd5d1e091038..76de542c66b7 100644
+--- a/sound/soc/intel/Kconfig
++++ b/sound/soc/intel/Kconfig
+@@ -172,6 +172,32 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH
+           Say Y if you have such a device.
+           If unsure select "N".
+ 
++config SND_SOC_INTEL_BYTCR_AIC3100_MACH
++        tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with TLV320AIC31XX codec"
++	depends on X86 && I2C && ACPI
++	select SND_SOC_TLV320AIC31XX
++	select SND_SST_MFLD_PLATFORM
++	select SND_SST_IPC_ACPI
++	select SND_SOC_INTEL_SST_MATCH if ACPI
++	help
++          This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
++          platforms with TLV320AIC31XX audio codec.
++          Say Y if you have such a device.
++          If unsure select "N".
++
++config SND_SOC_INTEL_CHT_CX2072X_MACH
++        tristate "ASoC Audio driver for Intel Baytrail and Cherrytrail with CX2072X codec"
++        depends on X86_INTEL_LPSS && I2C && ACPI
++        select SND_SOC_CX2072X
++        select SND_SST_MFLD_PLATFORM
++        select SND_SST_IPC_ACPI
++	select SND_SOC_INTEL_SST_MATCH if ACPI
++        help
++          This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
++          platforms with Conexant CX2072X audio codec.
++          Say Y if you have such a device.
++          If unsure select "N".
++
+ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
+         tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"
+         depends on X86_INTEL_LPSS && I2C && ACPI
+diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
+index 0a88537ca58a..4e230282205d 100644
+--- a/sound/soc/intel/atom/sst/sst_acpi.c
++++ b/sound/soc/intel/atom/sst/sst_acpi.c
+@@ -400,6 +400,7 @@ static int sst_acpi_remove(struct platform_device *pdev)
+ static unsigned long cht_machine_id;
+ 
+ #define CHT_SURFACE_MACH 1
++#define BYT_THINKPAD_10  2
+ 
+ static int cht_surface_quirk_cb(const struct dmi_system_id *id)
+ {
+@@ -407,6 +408,23 @@ static int cht_surface_quirk_cb(const struct dmi_system_id *id)
+ 	return 1;
+ }
+ 
++static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id)
++{
++	cht_machine_id = BYT_THINKPAD_10;
++	return 1;
++}
++
++
++static const struct dmi_system_id byt_table[] = {
++	{
++		.callback = byt_thinkpad10_quirk_cb,
++		.matches = {
++			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++			DMI_MATCH(DMI_PRODUCT_NAME, "20C3001VHH"),
++		},
++	},
++	{ }
++};
+ 
+ static const struct dmi_system_id cht_table[] = {
+ 	{
+@@ -424,6 +442,10 @@ static struct sst_acpi_mach cht_surface_mach = {
+ 	"10EC5640", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
+ 								&chv_platform_data };
+ 
++static struct sst_acpi_mach byt_thinkpad_10 = {
++	"10EC5640", "cht-bsw-rt5672", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
++	                                                        &byt_rvp_platform_data };
++
+ static struct sst_acpi_mach *cht_quirk(void *arg)
+ {
+ 	struct sst_acpi_mach *mach = arg;
+@@ -436,8 +458,21 @@ static struct sst_acpi_mach *cht_quirk(void *arg)
+ 		return mach;
+ }
+ 
++static struct sst_acpi_mach *byt_quirk(void *arg)
++{
++	struct sst_acpi_mach *mach = arg;
++
++	dmi_check_system(byt_table);
++
++	if (cht_machine_id == BYT_THINKPAD_10)
++		return &byt_thinkpad_10;
++	else
++		return mach;
++}
++
++
+ static struct sst_acpi_mach sst_acpi_bytcr[] = {
+-	{"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
++	{"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", byt_quirk,
+ 						&byt_rvp_platform_data },
+ 	{"10EC5642", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
+ 						&byt_rvp_platform_data },
+@@ -445,6 +480,16 @@ static struct sst_acpi_mach sst_acpi_bytcr[] = {
+ 						&byt_rvp_platform_data },
+ 	{"10EC5651", "bytcr_rt5651", "intel/fw_sst_0f28.bin", "bytcr_rt5651", NULL,
+ 						&byt_rvp_platform_data },
++	/* some Baytrail platforms rely on RT5645, use CHT machine driver */
++	{"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
++						&byt_rvp_platform_data },
++	{"10EC5648", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
++						&byt_rvp_platform_data },
++	/* use CHT driver to Baytrail Chromebooks */
++	{"193C9890", "cht-bsw-max98090", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
++						&byt_rvp_platform_data },
++	{"14F10720", "cht-cx2072x", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
++						&byt_rvp_platform_data },
+ 	{},
+ };
+ 
+@@ -456,7 +501,9 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
+ 						&chv_platform_data },
+ 	{"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
+ 						&chv_platform_data },
+-	{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
++	{"10EC3270", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
++						&chv_platform_data },
++	{"14F10720", "cht-cx2072x", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
+ 						&chv_platform_data },
+ 	/* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
+ 	{"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", cht_quirk,
+diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
+index 5639f10774e6..68dd93d91de9 100644
+--- a/sound/soc/intel/boards/Makefile
++++ b/sound/soc/intel/boards/Makefile
+@@ -7,6 +7,8 @@ snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o
+ snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
+ snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
+ snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
++snd-soc-sst-bytcr-aic3100-objs := bytcr_aic3100.o
++snd-soc-sst-cht-cx2072x-objs := cht_cx2072x.o
+ 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
+@@ -23,6 +25,8 @@ obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
+ obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o
+ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
+ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o
++obj-$(CONFIG_SND_SOC_INTEL_BYTCR_AIC3100_MACH) += snd-soc-sst-bytcr-aic3100.o
++obj-$(CONFIG_SND_SOC_INTEL_CHT_CX2072X_MACH) += snd-soc-sst-cht-cx2072x.o
+ 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
+diff --git a/sound/soc/intel/boards/bytcr_aic3100.c b/sound/soc/intel/boards/bytcr_aic3100.c
+new file mode 100644
+index 000000000000..f6a45f685933
+--- /dev/null
++++ b/sound/soc/intel/boards/bytcr_aic3100.c
+@@ -0,0 +1,608 @@
++/*
++ *  bytcr_aic3100.c - ASoc Machine driver for Intel Byt CR platform
++ *
++ *  Copyright (C) 2016 Intel Corporation
++ *
++ *  Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
++ *  Based on earlier 3.14 work by Praveen Diwakar and Gurudatta Bhakte
++ *
++ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; version 2 of the License.
++ *
++ *  This program is distributed in the hope that it will be useful, but
++ *  WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ *  General Public License for more details.
++ *
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/acpi.h>
++#include <linux/device.h>
++#include <linux/dmi.h>
++#include <linux/slab.h>
++#include <asm/cpu_device_id.h>
++#include <asm/platform_sst_audio.h>
++#include <linux/clk.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/jack.h>
++#include "../../codecs/tlv320aic31xx.h"
++#include "../atom/sst-atom-controls.h"
++#include "../common/sst-acpi.h"
++#include "../common/sst-dsp.h"
++
++enum {
++	BYT_AIC3100_IN1_MAP,
++};
++
++#define BYT_AIC3100_MAP(quirk)	((quirk) & 0xff)
++#define BYT_AIC3100_SSP2_AIF    BIT(16) /* default true, use SSP2 */
++#define BYT_AIC3100_MCLK_25MHZ	BIT(18) /* default false, use 19.2MHz */
++
++struct byt_aic3100_private {
++	struct clk *mclk;
++};
++
++static unsigned long byt_aic3100_quirk =
++	BYT_AIC3100_IN1_MAP | BYT_AIC3100_SSP2_AIF;
++
++static void log_quirks(struct device *dev)
++{
++	if (BYT_AIC3100_MAP(byt_aic3100_quirk) == BYT_AIC3100_IN1_MAP)
++		dev_info(dev, "quirk IN1_MAP enabled");
++	if (byt_aic3100_quirk & BYT_AIC3100_SSP2_AIF)
++		dev_info(dev, "quirk SSP2_AIF enabled");
++	else
++		dev_info(dev, "quirk SSP0_AIF enabled");
++	if (byt_aic3100_quirk & BYT_AIC3100_MCLK_25MHZ)
++		dev_info(dev, "quirk MCLK_25MHZ enabled");
++	else
++		dev_info(dev, "quirk MCLK_19200kHZ enabled");
++}
++
++#define BYT_CODEC_DAI1	"tlv320aic31xx-hifi"
++
++static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card)
++{
++	struct snd_soc_pcm_runtime *rtd;
++
++	list_for_each_entry(rtd, &card->rtd_list, list) {
++		if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1,
++			     strlen(BYT_CODEC_DAI1)))
++			return rtd->codec_dai;
++	}
++	return NULL;
++}
++
++static int platform_clock_control(struct snd_soc_dapm_widget *w,
++				  struct snd_kcontrol *k, int  event)
++{
++	struct snd_soc_dapm_context *dapm = w->dapm;
++	struct snd_soc_card *card = dapm->card;
++	struct snd_soc_dai *codec_dai;
++	struct byt_aic3100_private *priv = snd_soc_card_get_drvdata(card);
++	int ret;
++
++	codec_dai = byt_get_codec_dai(card);
++	if (!codec_dai) {
++		dev_err(card->dev,
++			"Codec dai not found; Unable to set platform clock\n");
++		return -EIO;
++	}
++
++	if (SND_SOC_DAPM_EVENT_ON(event)) {
++		if (priv->mclk) {
++			ret = clk_prepare_enable(priv->mclk);
++			if (ret < 0) {
++				dev_err(card->dev,
++					"could not configure MCLK state");
++				return ret;
++			}
++		}
++
++		if (byt_aic3100_quirk & BYT_AIC3100_MCLK_25MHZ) {
++			ret = snd_soc_dai_set_sysclk(codec_dai,
++						AIC31XX_PLL_CLKIN_MCLK,
++						25000000,
++						SND_SOC_CLOCK_IN);
++		} else {
++			ret = snd_soc_dai_set_sysclk(codec_dai,
++						AIC31XX_PLL_CLKIN_MCLK,
++						19200000,
++						SND_SOC_CLOCK_IN);
++		}
++	} else {
++		/*
++		 * Set codec clock source to internal clock before
++		 * turning off the platform clock. Codec needs clock
++		 * for Jack detection and button press
++		 */
++		/* FIXME: use RC oscillator? */
++		ret = snd_soc_dai_set_sysclk(codec_dai, AIC31XX_PLL_CLKIN_MCLK,
++					0, SND_SOC_CLOCK_IN);
++		if (!ret) {
++			if (priv->mclk)
++				clk_disable_unprepare(priv->mclk);
++		}
++	}
++
++	if (ret < 0) {
++		dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++static const struct snd_soc_dapm_widget byt_aic3100_widgets[] = {
++	SND_SOC_DAPM_HP("Headphone", NULL),
++	SND_SOC_DAPM_MIC("Headset Mic", NULL),
++	SND_SOC_DAPM_MIC("Internal Mic", NULL),
++	SND_SOC_DAPM_SPK("Speakers", NULL),
++	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
++			    platform_clock_control, SND_SOC_DAPM_PRE_PMU |
++			    SND_SOC_DAPM_POST_PMD),
++
++};
++
++static const struct snd_soc_dapm_route byt_aic3100_audio_map[] = {
++	/* External Speakers: HFL, HFR */
++	{"Speakers", NULL, "SPK"},
++
++	/* Headset Stereophone(Headphone): HSOL, HSOR */
++	{"Headphone", NULL, "HPL"},
++	{"Headphone", NULL, "HPR"},
++
++	{"Headphone", NULL, "Platform Clock"},
++	{"Headset Mic", NULL, "Platform Clock"},
++	{"Internal Mic", NULL, "Platform Clock"},
++	{"Speaker", NULL, "Platform Clock"},
++
++};
++
++static const struct snd_soc_dapm_route byt_aic3100_intmic_in1_map[] = {
++	{"micbias", NULL, "Internal Mic"},
++	/* Headset Mic: Headset Mic with bias */
++	{"MIC1RP", NULL, "Headset Mic"},
++};
++
++static const struct snd_soc_dapm_route byt_aic3100_ssp2_aif_map[] = {
++	{"ssp2 Tx", NULL, "codec_out0"},
++	{"ssp2 Tx", NULL, "codec_out1"},
++	{"codec_in0", NULL, "ssp2 Rx"},
++	{"codec_in1", NULL, "ssp2 Rx"},
++
++	{"Playback", NULL, "ssp2 Tx"},
++	{"ssp2 Rx", NULL, "Capture"},
++};
++
++static const struct snd_soc_dapm_route byt_aic3100_ssp0_aif_map[] = {
++	{"ssp0 Tx", NULL, "modem_out"},
++	{"modem_in", NULL, "ssp0 Rx"},
++
++	{"Playback", NULL, "ssp0 Tx"},
++	{"ssp0 Rx", NULL, "Capture"},
++};
++
++static const struct snd_kcontrol_new byt_aic3100_controls[] = {
++	SOC_DAPM_PIN_SWITCH("Headphone"),
++	SOC_DAPM_PIN_SWITCH("Headset Mic"),
++	SOC_DAPM_PIN_SWITCH("Internal Mic"),
++	SOC_DAPM_PIN_SWITCH("Speaker"),
++};
++
++static int byt_aic3100_aif1_hw_params(struct snd_pcm_substream *substream,
++					struct snd_pcm_hw_params *params)
++{
++	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	struct snd_soc_dai *codec_dai = rtd->codec_dai;
++	int ret;
++
++	if (byt_aic3100_quirk & BYT_AIC3100_MCLK_25MHZ) {
++		ret = snd_soc_dai_set_sysclk(codec_dai, AIC31XX_PLL_CLKIN_MCLK,
++					25000000,
++					SND_SOC_CLOCK_IN);
++	} else {
++		ret = snd_soc_dai_set_sysclk(codec_dai, AIC31XX_PLL_CLKIN_MCLK,
++					19200000,
++					SND_SOC_CLOCK_IN);
++	}
++
++	if (ret < 0) {
++		dev_err(rtd->dev, "can't set codec clock %d\n", ret);
++		return ret;
++	}
++
++	if (byt_aic3100_quirk & BYT_AIC3100_MCLK_25MHZ) {
++		ret = snd_soc_dai_set_pll(codec_dai, 0, AIC31XX_PLL_CLKIN_MCLK,
++					250000000, params_rate(params));
++	} else {
++		ret = snd_soc_dai_set_pll(codec_dai, 0, AIC31XX_PLL_CLKIN_MCLK,
++					19200000, params_rate(params));
++
++	}
++
++	if (ret < 0) {
++		dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++/* uncomment when we have an actual quirk
++static int byt_aic3100_quirk_cb(const struct dmi_system_id *id)
++{
++	byt_aic3100_quirk = (unsigned long)id->driver_data;
++	return 1;
++}
++*/
++
++static const struct dmi_system_id byt_aic3100_quirk_table[] = {
++	{}
++};
++
++static int byt_aic3100_init(struct snd_soc_pcm_runtime *runtime)
++{
++	int ret;
++	struct snd_soc_card *card = runtime->card;
++	const struct snd_soc_dapm_route *custom_map;
++	struct byt_aic3100_private *priv = snd_soc_card_get_drvdata(card);
++	int num_routes;
++
++	card->dapm.idle_bias_off = true;
++
++	ret = snd_soc_add_card_controls(card, byt_aic3100_controls,
++					ARRAY_SIZE(byt_aic3100_controls));
++	if (ret) {
++		dev_err(card->dev, "unable to add card controls\n");
++		return ret;
++	}
++
++	switch (BYT_AIC3100_MAP(byt_aic3100_quirk)) {
++	case BYT_AIC3100_IN1_MAP:
++	default:
++		custom_map = byt_aic3100_intmic_in1_map;
++		num_routes = ARRAY_SIZE(byt_aic3100_intmic_in1_map);
++	}
++
++	ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
++	if (ret)
++		return ret;
++
++	if (byt_aic3100_quirk & BYT_AIC3100_SSP2_AIF) {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					byt_aic3100_ssp2_aif_map,
++					ARRAY_SIZE(byt_aic3100_ssp2_aif_map));
++	} else {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					byt_aic3100_ssp0_aif_map,
++					ARRAY_SIZE(byt_aic3100_ssp0_aif_map));
++	}
++	if (ret)
++		return ret;
++
++	snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
++	snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
++
++	if (priv->mclk) {
++		/*
++		 * The firmware might enable the clock at
++		 * boot (this information may or may not
++		 * be reflected in the enable clock register).
++		 * To change the rate we must disable the clock
++		 * first to cover these cases. Due to common
++		 * clock framework restrictions that do not allow
++		 * to disable a clock that has not been enabled,
++		 * we need to enable the clock first.
++		 */
++		ret = clk_prepare_enable(priv->mclk);
++		if (!ret)
++			clk_disable_unprepare(priv->mclk);
++
++		if (byt_aic3100_quirk & BYT_AIC3100_MCLK_25MHZ)
++			ret = clk_set_rate(priv->mclk, 25000000);
++		else
++			ret = clk_set_rate(priv->mclk, 19200000);
++
++		if (ret)
++			dev_err(card->dev, "unable to set MCLK rate\n");
++	}
++
++	return ret;
++}
++
++static const struct snd_soc_pcm_stream byt_aic3100_dai_params = {
++	.formats = SNDRV_PCM_FMTBIT_S24_LE,
++	.rate_min = 48000,
++	.rate_max = 48000,
++	.channels_min = 2,
++	.channels_max = 2,
++};
++
++static int byt_aic3100_codec_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 */
++	rate->min = rate->max = 48000;
++	channels->min = channels->max = 2;
++
++	if (!(byt_aic3100_quirk & BYT_AIC3100_SSP2_AIF)) {
++
++		/* set SSP0 to 16-bit */
++		params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
++
++		/*
++		 * Default mode for SSP configuration is TDM 4 slot,
++		 * override config with explicit setting to I2S 2ch 16-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_IF   |
++					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, 16);
++		if (ret < 0) {
++			dev_err(rtd->dev,
++				"can't set I2S config, err %d\n", ret);
++			return ret;
++		}
++
++	} else {
++
++		/* 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_IF   |
++					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;
++		}
++	}
++	return 0;
++}
++
++static int byt_aic3100_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_aic3100_aif1_ops = {
++	.startup = byt_aic3100_aif1_startup,
++};
++
++static struct snd_soc_ops byt_aic3100_be_ssp2_ops = {
++	.hw_params = byt_aic3100_aif1_hw_params,
++};
++
++static struct snd_soc_dai_link byt_aic3100_dais[] = {
++	[MERR_DPCM_AUDIO] = {
++		.name = "Baytrail Audio Port",
++		.stream_name = "Baytrail Audio",
++		.cpu_dai_name = "media-cpu-dai",
++		.codec_dai_name = "snd-soc-dummy-dai",
++		.codec_name = "snd-soc-dummy",
++		.platform_name = "sst-mfld-platform",
++		.ignore_suspend = 1,
++		.dynamic = 1,
++		.dpcm_playback = 1,
++		.dpcm_capture = 1,
++		.ops = &byt_aic3100_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",
++		.ignore_suspend = 1,
++		.nonatomic = true,
++		.dynamic = 1,
++		.dpcm_playback = 1,
++		.ops = &byt_aic3100_aif1_ops,
++	},
++	[MERR_DPCM_COMPR] = {
++		.name = "Baytrail Compressed Port",
++		.stream_name = "Baytrail Compress",
++		.cpu_dai_name = "compress-cpu-dai",
++		.codec_dai_name = "snd-soc-dummy-dai",
++		.codec_name = "snd-soc-dummy",
++		.platform_name = "sst-mfld-platform",
++	},
++		/* back ends */
++	{
++		.name = "SSP2-Codec",
++		.id = 1,
++		.cpu_dai_name = "ssp2-port", /* overwritten for ssp0 routing */
++		.platform_name = "sst-mfld-platform",
++		.no_pcm = 1,
++		.codec_dai_name = "tlv320aic31xx-hifi",
++		.codec_name = "i2c-10TI3100:00", /* overwritten with HID */
++		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
++						| SND_SOC_DAIFMT_CBS_CFS,
++		.be_hw_params_fixup = byt_aic3100_codec_fixup,
++		.ignore_suspend = 1,
++		.dpcm_playback = 1,
++		.dpcm_capture = 1,
++		.init = byt_aic3100_init,
++		.ops = &byt_aic3100_be_ssp2_ops,
++	},
++};
++
++/* SoC card */
++static struct snd_soc_card byt_aic3100_card = {
++	.name = "bytcr-aic3100",
++	.owner = THIS_MODULE,
++	.dai_link = byt_aic3100_dais,
++	.num_links = ARRAY_SIZE(byt_aic3100_dais),
++	.dapm_widgets = byt_aic3100_widgets,
++	.num_dapm_widgets = ARRAY_SIZE(byt_aic3100_widgets),
++	.dapm_routes = byt_aic3100_audio_map,
++	.num_dapm_routes = ARRAY_SIZE(byt_aic3100_audio_map),
++	.fully_routed = true,
++};
++
++static char byt_aic3100_codec_name[16];/* i2c-<HID>:00 with HID being 8 chars */
++static char byt_aic3100_cpu_dai_name[10]; /*  = "ssp[0|2]-port" */
++
++static bool is_valleyview(void)
++{
++	static const struct x86_cpu_id cpu_ids[] = {
++		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
++		{}
++	};
++
++	if (!x86_match_cpu(cpu_ids))
++		return false;
++	return true;
++}
++
++
++static int snd_byt_aic3100_mc_probe(struct platform_device *pdev)
++{
++	int ret_val = 0;
++	struct sst_acpi_mach *mach;
++	const char *i2c_name = NULL;
++	int i;
++	int dai_index;
++	struct byt_aic3100_private *priv;
++	bool is_bytcr = false;
++
++	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
++	if (!priv)
++		return -ENOMEM;
++
++	/* register the soc card */
++	byt_aic3100_card.dev = &pdev->dev;
++	mach = byt_aic3100_card.dev->platform_data;
++	snd_soc_card_set_drvdata(&byt_aic3100_card, priv);
++
++	/* fix index of codec dai */
++	dai_index = MERR_DPCM_COMPR + 1;
++	for (i = 0; i < ARRAY_SIZE(byt_aic3100_dais); i++) {
++		if (!strcmp(byt_aic3100_dais[i].codec_name,
++				"i2c-10TI3100:00")) {
++			dai_index = i;
++			break;
++		}
++	}
++
++	/* fixup codec name based on HID */
++	i2c_name = sst_acpi_find_name_from_hid(mach->id);
++	if (i2c_name != NULL) {
++		snprintf(byt_aic3100_codec_name, sizeof(byt_aic3100_codec_name),
++			"%s%s", "i2c-", i2c_name);
++
++		byt_aic3100_dais[dai_index].codec_name = byt_aic3100_codec_name;
++	}
++
++	/*
++	 * swap SSP0 if bytcr is detected
++	 * (will be overridden if DMI quirk is detected)
++	 */
++	if (is_valleyview()) {
++		struct sst_platform_info *p_info = mach->pdata;
++		const struct sst_res_info *res_info = p_info->res_info;
++
++		if (res_info->acpi_ipc_irq_index == 0) {
++			is_bytcr = true;
++			byt_aic3100_quirk &= ~BYT_AIC3100_SSP2_AIF;
++		}
++	}
++
++	/* check quirks before creating card */
++	dmi_check_system(byt_aic3100_quirk_table);
++	log_quirks(&pdev->dev);
++
++	if (!(byt_aic3100_quirk & BYT_AIC3100_SSP2_AIF))  {
++
++		/* fixup cpu dai name name */
++		snprintf(byt_aic3100_cpu_dai_name,
++			sizeof(byt_aic3100_cpu_dai_name),
++			"%s", "ssp0-port");
++
++		byt_aic3100_dais[dai_index].cpu_dai_name =
++			byt_aic3100_cpu_dai_name;
++	}
++
++	if (is_valleyview()) {
++		priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
++		if (IS_ERR(priv->mclk)) {
++			dev_err(&pdev->dev,
++				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
++				PTR_ERR(priv->mclk));
++
++			/*
++			 * Audio output only works with MCLK enabled
++			 */
++			return PTR_ERR(priv->mclk);
++		}
++	}
++
++	ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_aic3100_card);
++
++	if (ret_val) {
++		dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
++			ret_val);
++		return ret_val;
++	}
++	platform_set_drvdata(pdev, &byt_aic3100_card);
++	return ret_val;
++}
++
++static struct platform_driver snd_byt_aic3100_mc_driver = {
++	.driver = {
++		.name = "bytcr_aic3100",
++		.pm = &snd_soc_pm_ops,
++	},
++	.probe = snd_byt_aic3100_mc_probe,
++};
++
++module_platform_driver(snd_byt_aic3100_mc_driver);
++
++MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
++MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:bytcr_aic3100");
+diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
+index bff77a1f27fc..c2ecbdc28459 100644
+--- a/sound/soc/intel/boards/bytcr_rt5640.c
++++ b/sound/soc/intel/boards/bytcr_rt5640.c
+@@ -389,6 +389,16 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
+ 						 BYT_RT5640_SSP0_AIF1),
+ 
+ 	},
++	{
++		.callback = byt_rt5640_quirk_cb,
++		.matches = {
++			DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
++		},
++		.driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP |
++						 BYT_RT5640_MCLK_EN |
++						 BYT_RT5640_SSP0_AIF1),
++
++	},
+ 	{}
+ };
+ 
+@@ -689,6 +699,10 @@ static bool is_valleyview(void)
+ 	return true;
+ }
+ 
++struct acpi_chan_package {   /* ACPICA seems to require 64 bit integers */
++	u64 aif_value;       /* 1: AIF1, 2: AIF2 */
++	u64 mclock_value;    /* usually 25MHz (0x17d7940), ignored */
++};
+ 
+ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
+ {
+@@ -698,6 +712,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
+ 	int i;
+ 	int dai_index;
+ 	struct byt_rt5640_private *priv;
++	bool is_bytcr = false;
+ 
+ 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
+ 	if (!priv)
+@@ -734,8 +749,52 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
+ 		struct sst_platform_info *p_info = mach->pdata;
+ 		const struct sst_res_info *res_info = p_info->res_info;
+ 
+-		/* TODO: use CHAN package info from BIOS to detect AIF1/AIF2 */
+-		if (res_info->acpi_ipc_irq_index == 0) {
++		if (res_info->acpi_ipc_irq_index == 0)
++			is_bytcr = true;
++	}
++
++	if (is_bytcr) {
++		/*
++		 * Baytrail CR platforms may have CHAN package in BIOS, try
++		 * to find relevant routing quirk based as done on Windows
++		 * platforms. We have to read the information directly from the
++		 * BIOS, at this stage the card is not created and the links
++		 * with the codec driver/pdata are non-existent
++		 */
++
++		struct acpi_chan_package chan_package;
++
++		/* format specified: 2 64-bit integers */
++		struct acpi_buffer format = {sizeof("NN"), "NN"};
++		struct acpi_buffer state = {0, NULL};
++		struct sst_acpi_package_context pkg_ctx;
++		bool pkg_found = false;
++
++		state.length = sizeof(chan_package);
++		state.pointer = &chan_package;
++
++		pkg_ctx.name = "CHAN";
++		pkg_ctx.length = 2;
++		pkg_ctx.format = &format;
++		pkg_ctx.state = &state;
++		pkg_ctx.data_valid = false;
++
++		pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx);
++		if (pkg_found) {
++			if (chan_package.aif_value == 1) {
++				dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n");
++				byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF1;
++			} else  if (chan_package.aif_value == 2) {
++				dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n");
++				byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
++			} else {
++				dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n");
++				pkg_found = false;
++			}
++		}
++
++		if (!pkg_found) {
++			/* no BIOS indications, assume SSP0-AIF2 connection */
+ 			byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
+ 		}
+ 	}
+@@ -774,7 +833,21 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
+ 			dev_err(&pdev->dev,
+ 				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
+ 				PTR_ERR(priv->mclk));
+-			return PTR_ERR(priv->mclk);
++
++			if (is_bytcr) {
++				/*
++				 * Audio output on Baytrail CR only works
++				 * with MCLK enabled
++				 */
++				return PTR_ERR(priv->mclk);
++			} else {
++				/*
++				 * Audio output can work with bitclock only
++				 * on Baytrail, with a limited audio quality
++				 * degradation
++				 */
++				byt_rt5640_quirk &= ~BYT_RT5640_MCLK_EN;
++			}
+ 		}
+ 	}
+ 
+diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
+index 35f591eab3c9..1c672768fc35 100644
+--- a/sound/soc/intel/boards/bytcr_rt5651.c
++++ b/sound/soc/intel/boards/bytcr_rt5651.c
+@@ -24,21 +24,134 @@
+ #include <linux/device.h>
+ #include <linux/dmi.h>
+ #include <linux/slab.h>
++#include <asm/cpu_device_id.h>
++#include <asm/platform_sst_audio.h>
++#include <linux/clk.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+ #include <sound/soc.h>
+ #include <sound/jack.h>
+ #include "../../codecs/rt5651.h"
+ #include "../atom/sst-atom-controls.h"
++#include "../common/sst-acpi.h"
++
++enum {
++	BYT_RT5651_DMIC1_MAP,
++	BYT_RT5651_DMIC2_MAP,
++	BYT_RT5651_IN1_MAP,
++};
++
++#define BYT_RT5651_MAP(quirk)	((quirk) & 0xff)
++#define BYT_RT5651_DMIC_EN	BIT(16)
++#define BYT_RT5651_MCLK_EN	BIT(17)
++#define BYT_RT5651_MCLK_25MHZ	BIT(18)
++
++struct byt_rt5651_private {
++	struct clk *mclk;
++};
++
++static unsigned long byt_rt5651_quirk = BYT_RT5651_DMIC1_MAP |
++					BYT_RT5651_DMIC_EN |
++	                                BYT_RT5651_MCLK_EN;
++
++static void log_quirks(struct device *dev)
++{
++	if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_DMIC1_MAP)
++		dev_info(dev, "quirk DMIC1_MAP enabled");
++	if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_DMIC2_MAP)
++		dev_info(dev, "quirk DMIC2_MAP enabled");
++	if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_MAP)
++		dev_info(dev, "quirk IN1_MAP enabled");
++	if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN)
++		dev_info(dev, "quirk DMIC enabled");
++	if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN)
++		dev_info(dev, "quirk MCLK_EN enabled");
++	if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ)
++		dev_info(dev, "quirk MCLK_25MHZ enabled");
++}
++
++#define BYT_CODEC_DAI1	"rt5651-aif1"
++
++static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card)
++{
++	struct snd_soc_pcm_runtime *rtd;
++
++	list_for_each_entry(rtd, &card->rtd_list, list) {
++		if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1,
++			     strlen(BYT_CODEC_DAI1)))
++			return rtd->codec_dai;
++	}
++	return NULL;
++}
++
++static int platform_clock_control(struct snd_soc_dapm_widget *w,
++				  struct snd_kcontrol *k, int  event)
++{
++	struct snd_soc_dapm_context *dapm = w->dapm;
++	struct snd_soc_card *card = dapm->card;
++	struct snd_soc_dai *codec_dai;
++	struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
++	int ret;
++
++	codec_dai = byt_get_codec_dai(card);
++	if (!codec_dai) {
++		dev_err(card->dev,
++			"Codec dai not found; Unable to set platform clock\n");
++		return -EIO;
++	}
++
++	if (SND_SOC_DAPM_EVENT_ON(event)) {
++		if ((byt_rt5651_quirk & BYT_RT5651_MCLK_EN) && priv->mclk) {
++			ret = clk_prepare_enable(priv->mclk);
++			if (ret < 0) {
++				dev_err(card->dev,
++					"could not configure MCLK state");
++				return ret;
++			}
++		}
++		ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_PLL1,
++					     48000 * 512,
++					     SND_SOC_CLOCK_IN);
++	} else {
++		/*
++		 * Set codec clock source to internal clock before
++		 * turning off the platform clock. Codec needs clock
++		 * for Jack detection and button press
++		 */
++		ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_RCCLK,
++					     0,
++					     SND_SOC_CLOCK_IN);
++		if (!ret) {
++			if ((byt_rt5651_quirk & BYT_RT5651_MCLK_EN) && priv->mclk)
++				clk_disable_unprepare(priv->mclk);
++		}
++	}
++
++	if (ret < 0) {
++		dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
+ 
+ static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = {
+ 	SND_SOC_DAPM_HP("Headphone", NULL),
+ 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ 	SND_SOC_DAPM_MIC("Internal Mic", NULL),
+ 	SND_SOC_DAPM_SPK("Speaker", NULL),
++	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
++			    platform_clock_control, SND_SOC_DAPM_PRE_PMU |
++			    SND_SOC_DAPM_POST_PMD),
++
+ };
+ 
+ static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = {
++	{"Headphone", NULL, "Platform Clock"},
++	{"Headset Mic", NULL, "Platform Clock"},
++	{"Internal Mic", NULL, "Platform Clock"},
++	{"Speaker", NULL, "Platform Clock"},
++
+ 	{"AIF1 Playback", NULL, "ssp2 Tx"},
+ 	{"ssp2 Tx", NULL, "codec_out0"},
+ 	{"ssp2 Tx", NULL, "codec_out1"},
+@@ -67,18 +180,6 @@ static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_map[] = {
+ 	{"IN1P", NULL, "Internal Mic"},
+ };
+ 
+-enum {
+-	BYT_RT5651_DMIC1_MAP,
+-	BYT_RT5651_DMIC2_MAP,
+-	BYT_RT5651_IN1_MAP,
+-};
+-
+-#define BYT_RT5651_MAP(quirk)	((quirk) & 0xff)
+-#define BYT_RT5651_DMIC_EN	BIT(16)
+-
+-static unsigned long byt_rt5651_quirk = BYT_RT5651_DMIC1_MAP |
+-					BYT_RT5651_DMIC_EN;
+-
+ static const struct snd_kcontrol_new byt_rt5651_controls[] = {
+ 	SOC_DAPM_PIN_SWITCH("Headphone"),
+ 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
+@@ -103,9 +204,27 @@ static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream,
+ 		return ret;
+ 	}
+ 
+-	ret = snd_soc_dai_set_pll(codec_dai, 0, RT5651_PLL1_S_BCLK1,
+-				  params_rate(params) * 50,
+-				  params_rate(params) * 512);
++	if (!(byt_rt5651_quirk & BYT_RT5651_MCLK_EN)) {
++		/* 2x25 bit slots on SSP2 */
++		ret = snd_soc_dai_set_pll(codec_dai, 0,
++					RT5651_PLL1_S_BCLK1,
++					params_rate(params) * 50,
++					params_rate(params) * 512);
++	}
++	else {
++		if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) {
++			ret = snd_soc_dai_set_pll(codec_dai, 0,
++						RT5651_PLL1_S_MCLK,
++						25000000,
++						params_rate(params) * 512);
++		} else {
++			ret = snd_soc_dai_set_pll(codec_dai, 0,
++						RT5651_PLL1_S_MCLK,
++						19200000,
++						params_rate(params) * 512);
++		}
++	}
++
+ 	if (ret < 0) {
+ 		dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ 		return ret;
+@@ -114,8 +233,22 @@ static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream,
+ 	return 0;
+ }
+ 
++static int byt_rt5651_quirk_cb(const struct dmi_system_id *id)
++{
++	byt_rt5651_quirk = (unsigned long)id->driver_data;
++	return 1;
++}
++
+ static const struct dmi_system_id byt_rt5651_quirk_table[] = {
+-	{}
++	{
++		.callback = byt_rt5651_quirk_cb,
++		.matches = {
++			DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"),
++			DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"),
++		},
++		.driver_data = (unsigned long *)(BYT_RT5651_DMIC1_MAP |
++						 BYT_RT5651_DMIC_EN),
++	},
+ };
+ 
+ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
+@@ -123,6 +256,7 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
+ 	int ret;
+ 	struct snd_soc_card *card = runtime->card;
+ 	const struct snd_soc_dapm_route *custom_map;
++	struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
+ 	int num_routes;
+ 
+ 	card->dapm.idle_bias_off = true;
+@@ -141,6 +275,9 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
+ 		custom_map = byt_rt5651_intmic_dmic1_map;
+ 		num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic1_map);
+ 	}
++	ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
++	if (ret)
++		return ret;
+ 
+ 	ret = snd_soc_add_card_controls(card, byt_rt5651_controls,
+ 					ARRAY_SIZE(byt_rt5651_controls));
+@@ -151,6 +288,30 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
+ 	snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
+ 	snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
+ 
++	if ((byt_rt5651_quirk & BYT_RT5651_MCLK_EN) && priv->mclk) {
++		/*
++		 * The firmware might enable the clock at
++		 * boot (this information may or may not
++		 * be reflected in the enable clock register).
++		 * To change the rate we must disable the clock
++		 * first to cover these cases. Due to common
++		 * clock framework restrictions that do not allow
++		 * to disable a clock that has not been enabled,
++		 * we need to enable the clock first.
++		 */
++		ret = clk_prepare_enable(priv->mclk);
++		if (!ret)
++			clk_disable_unprepare(priv->mclk);
++
++		if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ)
++			ret = clk_set_rate(priv->mclk, 25000000);
++		else
++			ret = clk_set_rate(priv->mclk, 19200000);
++
++		if (ret)
++			dev_err(card->dev, "unable to set MCLK rate\n");
++	}
++
+ 	return ret;
+ }
+ 
+@@ -298,12 +459,73 @@ static struct snd_soc_card byt_rt5651_card = {
+ 	.fully_routed = true,
+ };
+ 
++static char byt_rt5651_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
++
++static bool is_valleyview(void)
++{
++	static const struct x86_cpu_id cpu_ids[] = {
++		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
++		{}
++	};
++
++	if (!x86_match_cpu(cpu_ids))
++		return false;
++	return true;
++}
++
+ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
+ {
+ 	int ret_val = 0;
++	struct sst_acpi_mach *mach;
++	const char *i2c_name = NULL;
++	int i;
++	int dai_index;
++	struct byt_rt5651_private *priv;
++
++	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
++	if (!priv)
++		return -ENOMEM;
+ 
+ 	/* register the soc card */
+ 	byt_rt5651_card.dev = &pdev->dev;
++	mach = byt_rt5651_card.dev->platform_data;
++
++	/* fix index of codec dai */
++	dai_index = MERR_DPCM_COMPR + 1;
++	for (i = 0; i < ARRAY_SIZE(byt_rt5651_dais); i++) {
++		if (!strcmp(byt_rt5651_dais[i].codec_name, "i2c-10EC5651:00")) {
++			dai_index = i;
++			break;
++		}
++	}
++
++	/* fixup codec name based on HID */
++	i2c_name = sst_acpi_find_name_from_hid(mach->id);
++	if (i2c_name != NULL) {
++		snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name),
++			"%s%s", "i2c-", i2c_name);
++
++		byt_rt5651_dais[dai_index].codec_name = byt_rt5651_codec_name;
++	}
++
++	/* check quirks before creating card */
++	dmi_check_system(byt_rt5651_quirk_table);
++	log_quirks(&pdev->dev);
++
++	if ((byt_rt5651_quirk & BYT_RT5651_MCLK_EN) && (is_valleyview())) {
++		priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
++		if (IS_ERR(priv->mclk)) {
++			dev_err(&pdev->dev,
++				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
++				PTR_ERR(priv->mclk));
++			/*
++			 * Audio output can work with bitclock only
++			 * on Baytrail, with a limited audio quality
++			 * degradation
++			 */
++			byt_rt5651_quirk &= ~BYT_RT5651_MCLK_EN;
++		}
++	}
+ 
+ 	ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card);
+ 
+diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+index cdcced9f32b6..a95ae141e3a6 100644
+--- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c
++++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+@@ -23,6 +23,9 @@
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
+ #include <linux/acpi.h>
++#include <asm/cpu_device_id.h>
++#include <asm/platform_sst_audio.h>
++#include <linux/clk.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+ #include <sound/soc.h>
+@@ -37,6 +40,7 @@
+ struct cht_mc_private {
+ 	struct snd_soc_jack jack;
+ 	bool ts3a227e_present;
++	struct clk *mclk;
+ };
+ 
+ static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+@@ -51,11 +55,51 @@ static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+ 	return NULL;
+ }
+ 
++static int platform_clock_control(struct snd_soc_dapm_widget *w,
++					  struct snd_kcontrol *k, int  event)
++{
++	struct snd_soc_dapm_context *dapm = w->dapm;
++	struct snd_soc_card *card = dapm->card;
++	struct snd_soc_dai *codec_dai;
++	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
++	int ret;
++
++	codec_dai = cht_get_codec_dai(card);
++	if (!codec_dai) {
++		dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
++		return -EIO;
++	}
++
++	if (SND_SOC_DAPM_EVENT_ON(event)) {
++		if (ctx->mclk) {
++			ret = clk_prepare_enable(ctx->mclk);
++			if (ret < 0) {
++				dev_err(card->dev,
++					"could not configure MCLK state");
++				return ret;
++			}
++		}
++	} else {
++
++		/* FIXME: if there is no clock can jack detection work ? */
++
++		if (ctx->mclk) {
++			clk_disable_unprepare(ctx->mclk);
++		}
++	}
++
++	return 0;
++}
++
++
+ static const struct snd_soc_dapm_widget cht_dapm_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),
++	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
++			    platform_clock_control, SND_SOC_DAPM_PRE_PMU |
++			    SND_SOC_DAPM_POST_PMD),
+ };
+ 
+ static const struct snd_soc_dapm_route cht_audio_map[] = {
+@@ -72,6 +116,10 @@ static const struct snd_soc_dapm_route cht_audio_map[] = {
+ 	{"codec_in0", NULL, "ssp2 Rx" },
+ 	{"codec_in1", NULL, "ssp2 Rx" },
+ 	{"ssp2 Rx", NULL, "HiFi Capture"},
++	{"Headphone", NULL, "Platform Clock"},
++	{"Headset Mic", NULL, "Platform Clock"},
++	{"Int Mic", NULL, "Platform Clock"},
++	{"Ext Spk", NULL, "Platform Clock"},
+ };
+ 
+ static const struct snd_kcontrol_new cht_mc_controls[] = {
+@@ -153,6 +201,26 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+ 	if (ctx->ts3a227e_present)
+ 		snd_soc_jack_notifier_register(jack, &cht_jack_nb);
+ 
++	if (ctx->mclk) {
++		/*
++		 * The firmware might enable the clock at
++		 * boot (this information may or may not
++		 * be reflected in the enable clock register).
++		 * To change the rate we must disable the clock
++		 * first to cover these cases. Due to common
++		 * clock framework restrictions that do not allow
++		 * to disable a clock that has not been enabled,
++		 * we need to enable the clock first.
++		 */
++		ret = clk_prepare_enable(ctx->mclk);
++		if (!ret)
++			clk_disable_unprepare(ctx->mclk);
++
++		ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
++
++		if (ret)
++			dev_err(runtime->dev, "unable to set MCLK rate\n");
++	}
+ 	return ret;
+ }
+ 
+@@ -287,6 +355,18 @@ static struct snd_soc_card snd_soc_card_cht = {
+ 	.num_controls = ARRAY_SIZE(cht_mc_controls),
+ };
+ 
++static bool is_valleyview(void)
++{
++	static const struct x86_cpu_id cpu_ids[] = {
++		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
++		{}
++	};
++
++	if (!x86_match_cpu(cpu_ids))
++		return false;
++	return true;
++}
++
+ static int snd_cht_mc_probe(struct platform_device *pdev)
+ {
+ 	int ret_val = 0;
+@@ -306,6 +386,17 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
+ 	/* register the soc card */
+ 	snd_soc_card_cht.dev = &pdev->dev;
+ 	snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
++
++	if (is_valleyview()) {
++		drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
++		if (IS_ERR(drv->mclk)) {
++			dev_err(&pdev->dev,
++				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
++				PTR_ERR(drv->mclk));
++			return PTR_ERR(drv->mclk);
++		}
++	}
++
+ 	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+ 	if (ret_val) {
+ 		dev_err(&pdev->dev,
+diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
+index 56056ed7fcfd..e792f9849f98 100644
+--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
++++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
+@@ -23,7 +23,11 @@
+ #include <linux/module.h>
+ #include <linux/acpi.h>
+ #include <linux/platform_device.h>
++#include <linux/dmi.h>
+ #include <linux/slab.h>
++#include <asm/cpu_device_id.h>
++#include <asm/platform_sst_audio.h>
++#include <linux/clk.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+ #include <sound/soc.h>
+@@ -33,7 +37,8 @@
+ #include "../common/sst-acpi.h"
+ 
+ #define CHT_PLAT_CLK_3_HZ	19200000
+-#define CHT_CODEC_DAI	"rt5645-aif1"
++#define CHT_CODEC_DAI1	"rt5645-aif1"
++#define CHT_CODEC_DAI2	"rt5645-aif2"
+ 
+ struct cht_acpi_card {
+ 	char *codec_id;
+@@ -44,15 +49,37 @@ struct cht_acpi_card {
+ struct cht_mc_private {
+ 	struct snd_soc_jack jack;
+ 	struct cht_acpi_card *acpi_card;
++	char codec_name[16];
++	struct clk *mclk;
+ };
+ 
++#define CHT_RT5645_MAP(quirk)	((quirk) & 0xff)
++#define CHT_RT5645_SSP2_AIF2     BIT(16) /* default is using AIF1  */
++#define CHT_RT5645_SSP0_AIF1     BIT(17)
++#define CHT_RT5645_SSP0_AIF2     BIT(18)
++
++static unsigned long cht_rt5645_quirk = 0;
++
++static void log_quirks(struct device *dev)
++{
++	if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2)
++		dev_info(dev, "quirk SSP2_AIF2 enabled");
++	if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1)
++		dev_info(dev, "quirk SSP0_AIF1 enabled");
++	if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)
++		dev_info(dev, "quirk SSP0_AIF2 enabled");
++}
++
+ static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+ {
+ 	struct snd_soc_pcm_runtime *rtd;
+ 
+ 	list_for_each_entry(rtd, &card->rtd_list, list) {
+-		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
+-			     strlen(CHT_CODEC_DAI)))
++		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI1,
++			     strlen(CHT_CODEC_DAI1)))
++			return rtd->codec_dai;
++		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI2,
++			     strlen(CHT_CODEC_DAI2)))
+ 			return rtd->codec_dai;
+ 	}
+ 	return NULL;
+@@ -64,6 +91,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ 	struct snd_soc_dapm_context *dapm = w->dapm;
+ 	struct snd_soc_card *card = dapm->card;
+ 	struct snd_soc_dai *codec_dai;
++	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ 	int ret;
+ 
+ 	codec_dai = cht_get_codec_dai(card);
+@@ -72,19 +100,30 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ 		return -EIO;
+ 	}
+ 
+-	if (!SND_SOC_DAPM_EVENT_OFF(event))
+-		return 0;
++	if (SND_SOC_DAPM_EVENT_ON(event)) {
++		if (ctx->mclk) {
++			ret = clk_prepare_enable(ctx->mclk);
++			if (ret < 0) {
++				dev_err(card->dev,
++					"could not configure MCLK state");
++				return ret;
++			}
++		}
++	} else {
++		/* Set codec sysclk source to its internal clock because codec PLL will
++		 * be off when idle and MCLK will also be off when codec is
++		 * runtime suspended. Codec needs clock for jack detection and button
++		 * press. MCLK is turned off with clock framework or ACPI.
++		 */
++		ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
++					0, SND_SOC_CLOCK_IN);
++		if (ret < 0) {
++			dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
++			return ret;
++		}
+ 
+-	/* Set codec sysclk source to its internal clock because codec PLL will
+-	 * be off when idle and MCLK will also be off by ACPI when codec is
+-	 * runtime suspended. Codec needs clock for jack detection and button
+-	 * press.
+-	 */
+-	ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
+-			0, SND_SOC_CLOCK_IN);
+-	if (ret < 0) {
+-		dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
+-		return ret;
++		if (ctx->mclk)
++			clk_disable_unprepare(ctx->mclk);
+ 	}
+ 
+ 	return 0;
+@@ -96,7 +135,7 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
+ 	SND_SOC_DAPM_MIC("Int Mic", NULL),
+ 	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ 	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+-			platform_clock_control, SND_SOC_DAPM_POST_PMD),
++			platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ };
+ 
+ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
+@@ -108,12 +147,6 @@ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
+ 	{"Headphone", NULL, "HPOR"},
+ 	{"Ext Spk", NULL, "SPOL"},
+ 	{"Ext Spk", NULL, "SPOR"},
+-	{"AIF1 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, "AIF1 Capture"},
+ 	{"Headphone", NULL, "Platform Clock"},
+ 	{"Headset Mic", NULL, "Platform Clock"},
+ 	{"Int Mic", NULL, "Platform Clock"},
+@@ -129,16 +162,42 @@ static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = {
+ 	{"Headphone", NULL, "HPOR"},
+ 	{"Ext Spk", NULL, "SPOL"},
+ 	{"Ext Spk", NULL, "SPOR"},
++	{"Headphone", NULL, "Platform Clock"},
++	{"Headset Mic", NULL, "Platform Clock"},
++	{"Int Mic", NULL, "Platform Clock"},
++	{"Ext Spk", NULL, "Platform Clock"},
++};
++
++static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif1_map[] = {
+ 	{"AIF1 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, "AIF1 Capture"},
+-	{"Headphone", NULL, "Platform Clock"},
+-	{"Headset Mic", NULL, "Platform Clock"},
+-	{"Int Mic", NULL, "Platform Clock"},
+-	{"Ext Spk", NULL, "Platform Clock"},
++};
++
++static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif2_map[] = {
++	{"AIF2 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, "AIF2 Capture"},
++};
++
++static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif1_map[] = {
++	{"AIF1 Playback", NULL, "ssp0 Tx"},
++	{"ssp0 Tx", NULL, "modem_out"},
++	{"modem_in", NULL, "ssp0 Rx" },
++	{"ssp0 Rx", NULL, "AIF1 Capture"},
++};
++
++static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif2_map[] = {
++	{"AIF2 Playback", NULL, "ssp0 Tx"},
++	{"ssp0 Tx", NULL, "modem_out"},
++	{"modem_in", NULL, "ssp0 Rx" },
++	{"ssp0 Rx", NULL, "AIF2 Capture"},
+ };
+ 
+ static const struct snd_kcontrol_new cht_mc_controls[] = {
+@@ -184,11 +243,25 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
+ 	return 0;
+ }
+ 
++/* uncomment when we have a real quirk
++static int cht_rt5645_quirk_cb(const struct dmi_system_id *id)
++{
++	cht_rt5645_quirk = (unsigned long)id->driver_data;
++	return 1;
++}
++*/
++
++static const struct dmi_system_id cht_rt5645_quirk_table[] = {
++	{
++	},
++};
++
+ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+ {
+ 	int ret;
+ 	int jack_type;
+ 	struct snd_soc_codec *codec = runtime->codec;
++	struct snd_soc_card *card = runtime->card;
+ 	struct snd_soc_dai *codec_dai = runtime->codec_dai;
+ 	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
+ 
+@@ -200,6 +273,26 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+ 				RT5645_AD_STEREO_FILTER,
+ 				RT5645_CLK_SEL_I2S1_ASRC);
+ 
++	if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					cht_rt5645_ssp2_aif2_map,
++					ARRAY_SIZE(cht_rt5645_ssp2_aif2_map));
++	} else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					cht_rt5645_ssp0_aif1_map,
++					ARRAY_SIZE(cht_rt5645_ssp0_aif1_map));
++	} else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2) {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					cht_rt5645_ssp0_aif2_map,
++					ARRAY_SIZE(cht_rt5645_ssp0_aif2_map));
++	} else {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					cht_rt5645_ssp2_aif1_map,
++					ARRAY_SIZE(cht_rt5645_ssp2_aif1_map));
++	}
++	if (ret)
++		return ret;
++
+ 	/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+ 	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
+ 	if (ret < 0) {
+@@ -224,12 +317,33 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+ 
+ 	rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack);
+ 
++	if (ctx->mclk) {
++		/*
++		 * The firmware might enable the clock at
++		 * boot (this information may or may not
++		 * be reflected in the enable clock register).
++		 * To change the rate we must disable the clock
++		 * first to cover these cases. Due to common
++		 * clock framework restrictions that do not allow
++		 * to disable a clock that has not been enabled,
++		 * we need to enable the clock first.
++		 */
++		ret = clk_prepare_enable(ctx->mclk);
++		if (!ret)
++			clk_disable_unprepare(ctx->mclk);
++
++		ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
++
++		if (ret)
++			dev_err(runtime->dev, "unable to set MCLK rate\n");
++	}
+ 	return ret;
+ }
+ 
+ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ 			    struct snd_pcm_hw_params *params)
+ {
++	int ret;
+ 	struct snd_interval *rate = hw_param_interval(params,
+ 			SNDRV_PCM_HW_PARAM_RATE);
+ 	struct snd_interval *channels = hw_param_interval(params,
+@@ -239,8 +353,39 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ 	rate->min = rate->max = 48000;
+ 	channels->min = channels->max = 2;
+ 
+-	/* set SSP2 to 24-bit */
+-	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
++	if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) ||
++		(cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) {
++
++		/* set SSP0 to 16-bit */
++		params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
++
++		/*
++		 * Default mode for SSP configuration is TDM 4 slot, override config
++		 * with explicit setting to I2S 2ch 16-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_IF   |
++					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, 16);
++		if (ret < 0) {
++			dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
++			return ret;
++		}
++
++	} else {
++
++		/* set SSP2 to 24-bit */
++		params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
++
++	}
+ 	return 0;
+ }
+ 
+@@ -343,10 +488,31 @@ static struct snd_soc_card snd_soc_card_chtrt5650 = {
+ static struct cht_acpi_card snd_soc_cards[] = {
+ 	{"10EC5640", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
+ 	{"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
++	{"10EC5648", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
++	{"10EC3270", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
+ 	{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
+ };
+ 
+-static char cht_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
++static char cht_rt5645_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
++static char cht_rt5645_codec_aif_name[12]; /*  = "rt5645-aif[1|2]" */
++static char cht_rt5645_cpu_dai_name[10]; /*  = "ssp[0|2]-port" */
++
++static bool is_valleyview(void)
++{
++	static const struct x86_cpu_id cpu_ids[] = {
++		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
++		{}
++	};
++
++	if (!x86_match_cpu(cpu_ids))
++		return false;
++	return true;
++}
++
++struct acpi_chan_package {   /* ACPICA seems to require 64 bit integers */
++	u64 aif_value;       /* 1: AIF1, 2: AIF2 */
++	u64 mclock_value;    /* usually 25MHz (0x17d7940), ignored */
++};
+ 
+ static int snd_cht_mc_probe(struct platform_device *pdev)
+ {
+@@ -354,41 +520,147 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
+ 	int i;
+ 	struct cht_mc_private *drv;
+ 	struct snd_soc_card *card = snd_soc_cards[0].soc_card;
+-	char codec_name[16];
+ 	struct sst_acpi_mach *mach;
+ 	const char *i2c_name = NULL;
+ 	int dai_index = 0;
++	bool found = false;
++	bool is_bytcr = false;
+ 
+ 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
+ 	if (!drv)
+ 		return -ENOMEM;
+ 
++	mach = (&pdev->dev)->platform_data;
++
+ 	for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
+-		if (acpi_dev_found(snd_soc_cards[i].codec_id)) {
++		if (acpi_dev_found(snd_soc_cards[i].codec_id) &&
++			(!strncmp(snd_soc_cards[i].codec_id, mach->id, 8))) {
+ 			dev_dbg(&pdev->dev,
+ 				"found codec %s\n", snd_soc_cards[i].codec_id);
+ 			card = snd_soc_cards[i].soc_card;
+ 			drv->acpi_card = &snd_soc_cards[i];
++			found = true;
+ 			break;
+ 		}
+ 	}
++
++	if (!found) {
++		dev_err(&pdev->dev, "No matching HID found in supported list\n");
++		return -ENODEV;
++	}
++
+ 	card->dev = &pdev->dev;
+-	mach = card->dev->platform_data;
+-	sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
++	sprintf(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
+ 
+ 	/* set correct codec name */
+ 	for (i = 0; i < ARRAY_SIZE(cht_dailink); i++)
+ 		if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) {
+-			card->dai_link[i].codec_name = kstrdup(codec_name, GFP_KERNEL);
++			card->dai_link[i].codec_name = drv->codec_name;
+ 			dai_index = i;
+ 		}
+ 
+ 	/* fixup codec name based on HID */
+ 	i2c_name = sst_acpi_find_name_from_hid(mach->id);
+ 	if (i2c_name != NULL) {
+-		snprintf(cht_rt5640_codec_name, sizeof(cht_rt5640_codec_name),
++		snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name),
+ 			"%s%s", "i2c-", i2c_name);
+-		cht_dailink[dai_index].codec_name = cht_rt5640_codec_name;
++		cht_dailink[dai_index].codec_name = cht_rt5645_codec_name;
++	}
++
++	/*
++	 * swap SSP0 if bytcr is detected
++	 * (will be overridden if DMI quirk is detected)
++	 */
++	if (is_valleyview()) {
++		struct sst_platform_info *p_info = mach->pdata;
++		const struct sst_res_info *res_info = p_info->res_info;
++
++		if (res_info->acpi_ipc_irq_index == 0)
++			is_bytcr = true;
++	}
++
++	if (is_bytcr) {
++		/*
++		 * Baytrail CR platforms may have CHAN package in BIOS, try
++		 * to find relevant routing quirk based as done on Windows
++		 * platforms. We have to read the information directly from the
++		 * BIOS, at this stage the card is not created and the links
++		 * with the codec driver/pdata are non-existent
++		 */
++
++		struct acpi_chan_package chan_package;
++
++		/* format specified: 2 64-bit integers */
++		struct acpi_buffer format = {sizeof("NN"), "NN"};
++		struct acpi_buffer state = {0, NULL};
++		struct sst_acpi_package_context pkg_ctx;
++		bool pkg_found = false;
++
++		state.length = sizeof(chan_package);
++		state.pointer = &chan_package;
++
++		pkg_ctx.name = "CHAN";
++		pkg_ctx.length = 2;
++		pkg_ctx.format = &format;
++		pkg_ctx.state = &state;
++		pkg_ctx.data_valid = false;
++
++		pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx);
++		if (pkg_found) {
++			if (chan_package.aif_value == 1) {
++				dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n");
++				cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF1;
++			} else  if (chan_package.aif_value == 2) {
++				dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n");
++				cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2;
++			} else {
++				dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n");
++				pkg_found = false;
++			}
++		}
++
++		if (!pkg_found) {
++			/* no BIOS indications, assume SSP0-AIF2 connection */
++			cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2;
++		}
++	}
++
++	/* check quirks before creating card */
++	dmi_check_system(cht_rt5645_quirk_table);
++	log_quirks(&pdev->dev);
++
++	if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) ||
++		(cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) {
++
++		/* fixup codec aif name */
++		snprintf(cht_rt5645_codec_aif_name,
++			sizeof(cht_rt5645_codec_aif_name),
++			"%s", "rt5645-aif2");
++
++		cht_dailink[dai_index].codec_dai_name =
++			cht_rt5645_codec_aif_name;
++	}
++
++	if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) ||
++		(cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) {
++
++		/* fixup cpu dai name name */
++		snprintf(cht_rt5645_cpu_dai_name,
++			sizeof(cht_rt5645_cpu_dai_name),
++			"%s", "ssp0-port");
++
++		cht_dailink[dai_index].cpu_dai_name =
++			cht_rt5645_cpu_dai_name;
++	}
++
++	if (is_valleyview()) {
++		drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
++		if (IS_ERR(drv->mclk)) {
++			dev_err(&pdev->dev,
++				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
++				PTR_ERR(drv->mclk));
++			return PTR_ERR(drv->mclk);
++		}
+ 	}
+ 
+ 	snd_soc_card_set_drvdata(card, drv);
+diff --git a/sound/soc/intel/boards/cht_cx2072x.c b/sound/soc/intel/boards/cht_cx2072x.c
+new file mode 100644
+index 000000000000..30a97b201c9a
+--- /dev/null
++++ b/sound/soc/intel/boards/cht_cx2072x.c
+@@ -0,0 +1,453 @@
++/*
++ *  cht_cx207x.c - ASoc DPCM Machine driver for CherryTrail w/ CX2072x
++ *
++ *  Copyright (C) 2016 Intel Corp
++ *  Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
++ *
++ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; version 2 of the License.
++ *
++ *  This program is distributed in the hope that it will be useful, but
++ *  WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ *  General Public License for more details.
++ *
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/acpi.h>
++#include <linux/device.h>
++#include <linux/dmi.h>
++#include <linux/slab.h>
++#include <asm/cpu_device_id.h>
++#include <asm/platform_sst_audio.h>
++#include <linux/clk.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/jack.h>
++#include "../../codecs/cx2072x.h"
++#include "../atom/sst-atom-controls.h"
++#include "../common/sst-acpi.h"
++
++
++#define CHT_PLAT_CLK_3_HZ	19200000
++
++enum {
++	CHT_CX_DMIC_MAP,
++};
++
++#define CHT_CX_MAP(quirk)	((quirk) & 0xff)
++#define CHT_CX_MCLK_EN	BIT(16)
++
++
++struct cht_mc_private {
++	struct clk *mclk;
++};
++
++static unsigned long cht_cx_quirk = CHT_CX_MCLK_EN;
++
++#define BYT_CODEC_DAI1	"cx2072x-hifi"
++
++static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card)
++{
++	struct snd_soc_pcm_runtime *rtd;
++
++	list_for_each_entry(rtd, &card->rtd_list, list) {
++		if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1,
++			     strlen(BYT_CODEC_DAI1)))
++			return rtd->codec_dai;
++	}
++	return NULL;
++}
++
++static int platform_clock_control(struct snd_soc_dapm_widget *w,
++				  struct snd_kcontrol *k, int  event)
++{
++	struct snd_soc_dapm_context *dapm = w->dapm;
++	struct snd_soc_card *card = dapm->card;
++	struct snd_soc_dai *codec_dai;
++	struct cht_mc_private *priv = snd_soc_card_get_drvdata(card);
++	int ret;
++
++	codec_dai = byt_get_codec_dai(card);
++	if (!codec_dai) {
++		dev_err(card->dev,
++			"Codec dai not found; Unable to set platform clock\n");
++		return -EIO;
++	}
++
++	if (SND_SOC_DAPM_EVENT_ON(event)) {
++		if ((cht_cx_quirk & CHT_CX_MCLK_EN) && priv->mclk) {
++			ret = clk_prepare_enable(priv->mclk);
++			if (ret < 0) {
++				dev_err(card->dev,
++					"could not configure MCLK state");
++				return ret;
++			}
++		}
++		ret = snd_soc_dai_set_sysclk(codec_dai, CX2072X_MCLK_EXTERNAL_PLL,
++				     19200000, SND_SOC_CLOCK_IN);
++	} else {
++		/*
++		 * Set codec clock source to internal clock before
++		 * turning off the platform clock. Codec needs clock
++		 * for Jack detection and button press
++		 */
++
++		/* FIXME: what is the internal setting, if any? */
++		ret = snd_soc_dai_set_sysclk(codec_dai, CX2072X_MCLK_EXTERNAL_PLL,
++				     19200000, SND_SOC_CLOCK_IN);
++
++		if (!ret) {
++			if ((cht_cx_quirk & CHT_CX_MCLK_EN) && priv->mclk)
++				clk_disable_unprepare(priv->mclk);
++		}
++	}
++
++	if (ret < 0) {
++		dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++static const struct snd_soc_dapm_widget cht_dapm_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),
++	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
++			platform_clock_control, SND_SOC_DAPM_PRE_PMU |
++			SND_SOC_DAPM_POST_PMD),
++};
++
++static const struct snd_soc_dapm_route cht_audio_map[] = {
++		/* External Speakers: HFL, HFR */
++		{"Headphone", NULL, "PORTA"},
++		{"Ext Spk", NULL, "PORTG"},
++		{"PORTC", NULL, "Int Mic"},
++		/* Headset Mic: Headset Mic with bias */
++		{"PORTD", NULL, "Headset Mic"},
++		/*{"Headset Bias", NULL, "Headset Mic"},*/
++		/*{"PortD Mic Bias", NULL, "Headset Mic"},*/
++
++		/* Headset Stereophone(Headphone): HSOL, HSOR */
++		//{"Headphone", NULL, "HPL"},
++		//{"Headphone", NULL, "HPR"},
++
++		{"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"},
++		{"Playback", NULL, "Platform Clock"},
++		{"Capture", NULL, "Platform Clock"},
++};
++
++static const struct snd_kcontrol_new cht_mc_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 int cht_aif1_hw_params(struct snd_pcm_substream *substream,
++			     struct snd_pcm_hw_params *params)
++{
++	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	struct snd_soc_dai *codec_dai = rtd->codec_dai;
++	int ret;
++	
++	ret = snd_soc_dai_set_sysclk(codec_dai, CX2072X_MCLK_EXTERNAL_PLL,
++				     19200000, SND_SOC_CLOCK_IN);
++	if (ret < 0) {
++		dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
++		return ret;
++	}
++
++	/* FIXME: No need PLL for conexant codec */
++#if 0
++	ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
++				  CHT_PLAT_CLK_3_HZ, rate * 512);
++	if (ret < 0) {
++		dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
++		return ret;
++	}
++#endif
++
++
++	return 0;
++}
++
++static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
++{
++	int ret=0;
++	struct snd_soc_card *card = runtime->card;
++	struct cht_mc_private *priv = snd_soc_card_get_drvdata(runtime->card);
++
++	card->dapm.idle_bias_off = true;
++
++	/* FIXME: is this necessary */
++//      snd_soc_dapm_ignore_suspend(&codec->dapm, "PortD");
++//	snd_soc_dapm_enable_pin(&card->dapm, "Headset Mic");
++//	snd_soc_dapm_enable_pin(&card->dapm, "Headphone");
++//	snd_soc_dapm_enable_pin(&card->dapm, "Ext Spk");
++//	snd_soc_dapm_enable_pin(&card->dapm, "Int Mic");
++
++//	snd_soc_dapm_sync(&card->dapm);
++
++	if ((cht_cx_quirk & CHT_CX_MCLK_EN) && priv->mclk) {
++		/*
++		 * The firmware might enable the clock at
++		 * boot (this information may or may not
++		 * be reflected in the enable clock register).
++		 * To change the rate we must disable the clock
++		 * first to cover these cases. Due to common
++		 * clock framework restrictions that do not allow
++		 * to disable a clock that has not been enabled,
++		 * we need to enable the clock first.
++		 */
++		ret = clk_prepare_enable(priv->mclk);
++		if (!ret)
++			clk_disable_unprepare(priv->mclk);
++
++		ret = clk_set_rate(priv->mclk, 19200000);
++
++		if (ret)
++			dev_err(card->dev, "unable to set MCLK rate\n");
++	}
++
++	return ret;
++}
++
++static const struct snd_soc_pcm_stream cht_dai_params = {
++	.formats = SNDRV_PCM_FMTBIT_S24_LE,
++	.rate_min = 48000,
++	.rate_max = 48000,
++	.channels_min = 2,
++	.channels_max = 2,
++};
++
++static int cht_codec_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_IF   |
++				SND_SOC_DAIFMT_CBS_CFS);
++	if (ret < 0) {
++		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++static const struct snd_soc_pcm_stream cht_cx_dai_params = {
++	.formats = SNDRV_PCM_FMTBIT_S24_LE,
++	.rate_min = 48000,
++	.rate_max = 48000,
++	.channels_min = 2,
++	.channels_max = 2,
++};
++
++static int cht_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 cht_aif1_ops = {
++	.startup = cht_aif1_startup,
++};
++
++static struct snd_soc_ops cht_be_ssp2_ops = {
++	.hw_params = cht_aif1_hw_params,
++};
++
++static struct snd_soc_dai_link cht_dailink[] = {
++	[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 = &cht_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 = &cht_aif1_ops,
++	},
++	[MERR_DPCM_COMPR] = {
++		.name = "Compressed Port",
++		.stream_name = "Compress",
++		.cpu_dai_name = "compress-cpu-dai",
++		.codec_dai_name = "snd-soc-dummy-dai",
++		.codec_name = "snd-soc-dummy",
++		.platform_name = "sst-mfld-platform",
++	},
++	/* CODEC<->CODEC link */
++	/* 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_IF
++		                              | SND_SOC_DAIFMT_CBS_CFS,
++		.init = cht_codec_init,
++		.be_hw_params_fixup = cht_codec_fixup,
++		.nonatomic = true,
++		.dpcm_playback = 1,
++		.dpcm_capture = 1,
++		.ops = &cht_be_ssp2_ops,
++	},
++};
++
++/* SoC card */
++static struct snd_soc_card chtcx2072x_card = {
++	.name = "chtcx2072x",
++	.dai_link = cht_dailink,
++	.num_links = ARRAY_SIZE(cht_dailink),
++	.dapm_widgets = cht_dapm_widgets,
++	.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
++	.dapm_routes = cht_audio_map,
++	.num_dapm_routes = ARRAY_SIZE(cht_audio_map),
++	.controls = cht_mc_controls,
++	.num_controls = ARRAY_SIZE(cht_mc_controls),
++};
++
++static char cht_cx_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
++
++static bool is_valleyview(void)
++{
++	static const struct x86_cpu_id cpu_ids[] = {
++		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
++		{}
++	};
++
++	if (!x86_match_cpu(cpu_ids))
++		return false;
++	return true;
++}
++
++static int snd_cht_mc_probe(struct platform_device *pdev)
++{
++	int ret_val = 0;
++	int i;
++	int dai_index;
++	struct cht_mc_private *drv;
++	struct sst_acpi_mach *mach;
++	const char *i2c_name = NULL;
++	
++	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
++	if (!drv) {
++		pr_err("allocation failed\n");
++		return -ENOMEM;
++	}
++
++	/* register the soc card */
++	chtcx2072x_card.dev = &pdev->dev;
++	mach = chtcx2072x_card.dev->platform_data;
++	snd_soc_card_set_drvdata(&chtcx2072x_card, drv);
++	
++	/* fix index of codec dai */
++	dai_index = MERR_DPCM_COMPR + 1;
++	for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) {
++		if (!strcmp(cht_dailink[i].codec_name, "i2c-14F10720:00")) {
++			dai_index = i;
++			break;
++		}
++	}
++
++	/* fixup codec name based on HID */
++	i2c_name = sst_acpi_find_name_from_hid(mach->id);
++	if (i2c_name != NULL) {
++		snprintf(cht_cx_codec_name, sizeof(cht_cx_codec_name),
++			"%s%s", "i2c-", i2c_name);
++
++		cht_dailink[dai_index].codec_name = cht_cx_codec_name;
++	}
++
++	if ((cht_cx_quirk & CHT_CX_MCLK_EN) && (is_valleyview())) {
++		drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
++		if (IS_ERR(drv->mclk)) {
++			dev_err(&pdev->dev,
++				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
++				PTR_ERR(drv->mclk));
++			
++			return PTR_ERR(drv->mclk);
++		}
++	}
++
++	ret_val = devm_snd_soc_register_card(&pdev->dev, &chtcx2072x_card);
++	
++	if (ret_val) {
++		dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
++			ret_val);
++		return ret_val;
++	}
++	platform_set_drvdata(pdev, &chtcx2072x_card);
++	return ret_val;
++}
++
++
++
++static struct platform_driver snd_cht_mc_driver = {
++	.driver = {
++		.name = "cht-cx2072x",
++		.pm = &snd_soc_pm_ops,
++	},
++	.probe = snd_cht_mc_probe,
++};
++module_platform_driver(snd_cht_mc_driver);
++
++MODULE_DESCRIPTION("ASoC Intel(R) Cherrytrail Machine driver");
++MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:cht-cx2072x");
+diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h
+index 012742299dd5..214e000667ae 100644
+--- a/sound/soc/intel/common/sst-acpi.h
++++ b/sound/soc/intel/common/sst-acpi.h
+@@ -15,14 +15,29 @@
+ #include <linux/stddef.h>
+ #include <linux/acpi.h>
+ 
+-/* translation fron HID to I2C name, needed for DAI codec_name */
++struct sst_acpi_package_context {
++	char *name;           /* package name */
++	int length;           /* number of elements */
++	struct acpi_buffer *format;
++	struct acpi_buffer *state;
++	bool data_valid;
++};
++
+ #if IS_ENABLED(CONFIG_ACPI)
++/* translation fron HID to I2C name, needed for DAI codec_name */
+ const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
++bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
++				    struct sst_acpi_package_context *ctx);
+ #else
+ static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
+ {
+ 	return NULL;
+ }
++static inline bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
++					   struct sst_acpi_package_context *ctx)
++{
++	return false;
++}
+ #endif
+ 
+ /* acpi match */
+diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c
+index 789843307a49..1070f3ad23e5 100644
+--- a/sound/soc/intel/common/sst-match-acpi.c
++++ b/sound/soc/intel/common/sst-match-acpi.c
+@@ -77,5 +77,62 @@ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines)
+ }
+ EXPORT_SYMBOL_GPL(sst_acpi_find_machine);
+ 
++static acpi_status sst_acpi_find_package(acpi_handle handle, u32 level,
++					void *context, void **ret)
++{
++	struct acpi_device *adev;
++	acpi_status status = AE_OK;
++	struct sst_acpi_package_context *pkg_ctx = context;
++
++	pkg_ctx->data_valid = false;
++
++	if (acpi_bus_get_device(handle, &adev))
++		return AE_OK;
++
++	if (adev->status.present && adev->status.functional) {
++		struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
++		union acpi_object  *myobj = NULL;
++
++		status = acpi_evaluate_object_typed(handle, pkg_ctx->name,
++						NULL, &buffer,
++						ACPI_TYPE_PACKAGE);
++		if (ACPI_FAILURE(status))
++			return AE_OK;
++
++		myobj = buffer.pointer;
++		if (!myobj || myobj->package.count != pkg_ctx->length) {
++			kfree(buffer.pointer);
++			return AE_OK;
++		}
++
++		status = acpi_extract_package(myobj,
++					pkg_ctx->format, pkg_ctx->state);
++		if (ACPI_FAILURE(status)) {
++			kfree(buffer.pointer);
++			return AE_OK;
++		}
++
++		kfree(buffer.pointer);
++		pkg_ctx->data_valid = true;
++		return AE_CTRL_TERMINATE;
++	}
++
++	return AE_OK;
++}
++
++bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
++				struct sst_acpi_package_context *ctx)
++{
++	acpi_status status;
++
++	status = acpi_get_devices(hid, sst_acpi_find_package, ctx, NULL);
++
++	if (ACPI_FAILURE(status) || !ctx->data_valid)
++		return false;
++
++	return true;
++}
++EXPORT_SYMBOL_GPL(sst_acpi_find_package_from_hid);
++
+ MODULE_LICENSE("GPL v2");
+ MODULE_DESCRIPTION("Intel Common ACPI Match module");