diff options
Diffstat (limited to 'modules/hardware/t100ha/sound.patch')
-rw-r--r-- | modules/hardware/t100ha/sound.patch | 5620 |
1 files changed, 0 insertions, 5620 deletions
diff --git a/modules/hardware/t100ha/sound.patch b/modules/hardware/t100ha/sound.patch deleted file mode 100644 index 15a2a883..00000000 --- a/modules/hardware/t100ha/sound.patch +++ /dev/null @@ -1,5620 +0,0 @@ -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 16c94c45ce50..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; -@@ -45,15 +50,36 @@ 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; -@@ -65,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); -@@ -73,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; -@@ -97,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[] = { -@@ -109,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"}, -@@ -130,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[] = { -@@ -185,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); - -@@ -201,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) { -@@ -225,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, -@@ -240,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; - } - -@@ -344,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) - { -@@ -358,22 +523,33 @@ static int snd_cht_mc_probe(struct platform_device *pdev) - 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(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id); - - /* set correct codec name */ -@@ -386,9 +562,105 @@ static int snd_cht_mc_probe(struct platform_device *pdev) - /* 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"); |