From 236d6a6ace0e9589a99d66cb9f1e50a6c869d52e Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Mon, 8 Jun 2015 18:00:46 +0200
Subject: [PATCH 01/19] Added VT1613 codec select

---
 sound/soc/codecs/Kconfig | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 86513b0..d813556 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -82,6 +82,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_TWL6040 if TWL6040_CORE
 	select SND_SOC_UDA134X
 	select SND_SOC_UDA1380 if I2C
+	select SND_SOC_VT1613 if SND_SOC_AC97_BUS
 	select SND_SOC_WL1273 if MFD_WL1273_CORE
 	select SND_SOC_WM0010 if SPI_MASTER
 	select SND_SOC_WM1250_EV1 if I2C
@@ -386,7 +387,10 @@ config SND_SOC_UDA134X
 
 config SND_SOC_UDA1380
         tristate
-
+        
+config SND_SOC_VT1613
+        tristate
+        
 config SND_SOC_WL1273
 	tristate
 
-- 
1.8.1.2


From c6e8e1fec42464c53b68951bcacbac88fcdaa4cd Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Mon, 8 Jun 2015 18:09:18 +0200
Subject: [PATCH 02/19] Added VT1613 codec object file

---
 sound/soc/codecs/Makefile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 2deeb14..6e03b09 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -72,6 +72,7 @@ snd-soc-twl4030-objs := twl4030.o
 snd-soc-twl6040-objs := twl6040.o
 snd-soc-uda134x-objs := uda134x.o
 snd-soc-uda1380-objs := uda1380.o
+snd-soc-vt1613-objs := vt1613.o
 snd-soc-wl1273-objs := wl1273.o
 snd-soc-wm-adsp-objs := wm_adsp.o
 snd-soc-wm0010-objs := wm0010.o
@@ -203,6 +204,7 @@ obj-$(CONFIG_SND_SOC_TWL4030)	+= snd-soc-twl4030.o
 obj-$(CONFIG_SND_SOC_TWL6040)	+= snd-soc-twl6040.o
 obj-$(CONFIG_SND_SOC_UDA134X)	+= snd-soc-uda134x.o
 obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_VT1613)	+= snd-soc-vt1613.o
 obj-$(CONFIG_SND_SOC_WL1273)	+= snd-soc-wl1273.o
 obj-$(CONFIG_SND_SOC_WM0010)	+= snd-soc-wm0010.o
 obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
-- 
1.8.1.2


From 00249f8bd18b0daabae622666db89cce93804d3d Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Mon, 8 Jun 2015 18:41:53 +0200
Subject: [PATCH 03/19] Added support for sound card with VT1613 AC'97

---
 sound/soc/fsl/Kconfig | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 573dabb..e569a6f 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -223,6 +223,18 @@
 	  Say Y if you want to add support for SoC audio on an i.MX board with
 	  a sgtl5000 codec.
 
+config SND_SOC_IMX_AC97_VT1613
+       tristate "SoC Audio support for i.MX boards with VT1613 AC'97"
+       depends on OF
+       select SND_SOC_AC97_BUS
+       select SND_SOC_VT1613 
+       select SND_SOC_IMX_PCM_DMA
+       select SND_SOC_IMX_AUDMUX
+       select SND_SOC_FSL_SSI
+       help
+         Say Y if you want to add support for SoC audio on an i.MX board with
+         a VT1613 codec in AC97 mode.
+
 config SND_SOC_IMX_SPDIF
 	tristate "SoC Audio support for i.MX boards with S/PDIF"
 	select SND_SOC_IMX_PCM_DMA
-- 
1.8.1.2


From fb49447a2eaa99be5e98a515c0f84af2ffcffd6c Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Mon, 8 Jun 2015 18:57:41 +0200
Subject: [PATCH 04/19] Added UDOO audio driver's object file

---
 sound/soc/fsl/Makefile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 144cd69..c1d14a9 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -56,6 +56,7 @@
 snd-soc-wm1133-ev1-objs := wm1133-ev1.o
 snd-soc-imx-cs42888-objs := imx-cs42888.o
 snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
+snd-soc-imx-ac97-vt1613-objs := imx-ac97-vt1613.o
 snd-soc-imx-wm8962-objs := imx-wm8962.o
 snd-soc-imx-spdif-objs := imx-spdif.o
 snd-soc-imx-hdmi-objs := imx-hdmi.o
@@ -67,6 +68,7 @@
 obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
 obj-$(CONFIG_SND_SOC_IMX_CS42888) += snd-soc-imx-cs42888.o
 obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
+obj-$(CONFIG_SND_SOC_IMX_AC97_VT1613) += snd-soc-imx-ac97-vt1613.o
 obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
 obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
 obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o
-- 
1.8.1.2


From 8a2a72cb699fe5d5809d4dcea26344d1f8be468e Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Tue, 9 Jun 2015 10:38:11 +0200
Subject: [PATCH 05/19] AC97 regmap API from linaro patch

---
 drivers/base/regmap/regmap-ac97.c | 113 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)
 create mode 100644 drivers/base/regmap/regmap-ac97.c

diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c
new file mode 100644
index 0000000..ed05da8
--- /dev/null
+++ b/drivers/base/regmap/regmap-ac97.c
@@ -0,0 +1,113 @@
+/*
+ * Register map access API - AC'97 support
+ *
+ * Copyright 2013 Linaro Ltd.  All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/ac97_codec.h>
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case AC97_RESET:
+	case AC97_POWERDOWN:
+	case AC97_INT_PAGING:
+	case AC97_EXTENDED_ID:
+	case AC97_EXTENDED_STATUS:
+	case AC97_EXTENDED_MID:
+	case AC97_EXTENDED_MSTATUS:
+	case AC97_GPIO_STATUS:
+	case AC97_MISC_AFE:
+	case AC97_VENDOR_ID1:
+	case AC97_VENDOR_ID2:
+	case AC97_CODEC_CLASS_REV:
+	case AC97_PCI_SVID:
+	case AC97_PCI_SID:
+	case AC97_FUNC_SELECT:
+	case AC97_FUNC_INFO:
+	case AC97_SENSE_INFO:
+		return true;
+	default:
+		return false;
+	}
+}
+EXPORT_SYMBOL_GPL(regmap_ac97_default_volatile);
+
+static int regmap_ac97_reg_read(void *context, unsigned int reg,
+	unsigned int *val)
+{
+	struct snd_ac97 *ac97 = context;
+	*val = ac97->bus->ops->read(ac97, reg);
+
+	return 0;
+}
+
+static int regmap_ac97_reg_write(void *context, unsigned int reg,
+	unsigned int val)
+{
+	struct snd_ac97 *ac97 = context;
+
+	ac97->bus->ops->write(ac97, reg, val);
+
+	return 0;
+}
+
+static const struct regmap_bus ac97_regmap_bus = {
+		.reg_write = regmap_ac97_reg_write,
+		.reg_read = regmap_ac97_reg_read,
+};
+
+/**
+ * regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+				const struct regmap_config *config)
+{
+	return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(regmap_init_ac97);
+
+/**
+ * devm_regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap.  The regmap will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+				     const struct regmap_config *config)
+{
+	return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_ac97);
+
+MODULE_LICENSE("GPL v2");
-- 
1.8.1.2


From 3bcfa4b56ad2bc3f3bbdedf6fa930bc4d761116d Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Tue, 9 Jun 2015 10:42:43 +0200
Subject: [PATCH 06/19] Add REGMAP_AC97 - from linaro patch

---
 drivers/base/regmap/Kconfig | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 4251570..be1d6f5 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -3,11 +3,14 @@
 # subsystems should select the appropriate symbols.
 
 config REGMAP
-	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ)
+	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ)
 	select LZO_COMPRESS
 	select LZO_DECOMPRESS
 	select IRQ_DOMAIN if REGMAP_IRQ
 	bool
+	
+config REGMAP_AC97
+	tristate
 
 config REGMAP_I2C
 	tristate
-- 
1.8.1.2


From c0b42bf7e32581d820e6a33dc54a81f241147136 Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Tue, 9 Jun 2015 10:45:53 +0200
Subject: [PATCH 07/19] add regmap-ac97 object file (linaro patch)

---
 drivers/base/regmap/Makefile | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index a7c670b..0a53365 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -1,6 +1,7 @@
 obj-$(CONFIG_REGMAP) += regmap.o regcache.o
 obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
 obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
+obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
 obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
 obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
 obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
-- 
1.8.1.2


From e3376edcf3e3edb2c9ae997aff376bc922f4af55 Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Tue, 9 Jun 2015 11:05:20 +0200
Subject: [PATCH 08/19] AC97 regmap API (from linaro patch)

---
 include/linux/regmap.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 4149f1a..46b092b 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -27,6 +27,7 @@ struct spmi_device;
 struct regmap;
 struct regmap_range_cfg;
 struct regmap_field;
+struct snd_ac97;
 
 /* An enum of all the supported cache types */
 enum regcache_type {
@@ -326,6 +327,8 @@ struct regmap *regmap_init_spmi(struct spmi_device *dev,
 struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
 				    void __iomem *regs,
 				    const struct regmap_config *config);
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+				const struct regmap_config *config);
 
 struct regmap *devm_regmap_init(struct device *dev,
 				const struct regmap_bus *bus,
@@ -340,6 +343,10 @@ struct regmap *devm_regmap_init_spmi(struct spmi_device *dev,
 struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
 					 void __iomem *regs,
 					 const struct regmap_config *config);
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+				     const struct regmap_config *config);
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
 
 /**
  * regmap_init_mmio(): Initialise register map
-- 
1.8.1.2


From 9b1514f6562f43438c8d6478bfb83f9372db7550 Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Tue, 9 Jun 2015 11:25:20 +0200
Subject: [PATCH 09/19] regmap: add reg_read/reg_write

from Boris BREZILLON's patch "regmap: add reg_read/reg_write callbacks to regmap_bus struct"
---
 include/linux/regmap.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 46b092b..fab0407 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -273,6 +273,12 @@ typedef int (*regmap_hw_async_write)(void *context,
 typedef int (*regmap_hw_read)(void *context,
 			      const void *reg_buf, size_t reg_size,
 			      void *val_buf, size_t val_size);
+
+typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg,
+                                   unsigned int *val);
+typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg,
+                                   unsigned int val);
+                                   
 typedef struct regmap_async *(*regmap_hw_async_alloc)(void);
 typedef void (*regmap_hw_free_context)(void *context);
 
@@ -306,7 +312,9 @@ struct regmap_bus {
 	regmap_hw_write write;
 	regmap_hw_gather_write gather_write;
 	regmap_hw_async_write async_write;
+	regmap_hw_reg_write reg_write;
 	regmap_hw_read read;
+	regmap_hw_reg_read reg_read;
 	regmap_hw_free_context free_context;
 	regmap_hw_async_alloc async_alloc;
 	u8 read_flag_mask;
-- 
1.8.1.2


From 08effbde69449e1c1b4676466ba91fa95924dd22 Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Tue, 9 Jun 2015 11:45:25 +0200
Subject: [PATCH 10/19] Add _regmap_bus_reg_read/_regmap_bus_reg_write

from Boris BREZILLON's patch - "regmap: add reg_read/reg_write callbacks to regmap_bus struct"
---
 drivers/base/regmap/regmap.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 2f9a3d8..b672374 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -35,10 +35,14 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
 			       unsigned int mask, unsigned int val,
 			       bool *change);
 
+static int _regmap_bus_reg_read(void *context, unsigned int reg,
+				unsigned int *val);
 static int _regmap_bus_read(void *context, unsigned int reg,
 			    unsigned int *val);
 static int _regmap_bus_formatted_write(void *context, unsigned int reg,
 				       unsigned int val);
+static int _regmap_bus_reg_write(void *context, unsigned int reg,
+				 unsigned int val);
 static int _regmap_bus_raw_write(void *context, unsigned int reg,
 				 unsigned int val);
 
@@ -472,6 +476,12 @@ struct regmap *regmap_init(struct device *dev,
 
 		map->defer_caching = false;
 		goto skip_format_initialization;
+	} else if (!bus->read || !bus->write) {
+		map->reg_read = _regmap_bus_reg_read;
+		map->reg_write = _regmap_bus_reg_write;
+
+		map->defer_caching = false;
+		goto skip_format_initialization;
 	} else {
 		map->reg_read  = _regmap_bus_read;
 	}
@@ -1267,6 +1277,14 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg,
 	return ret;
 }
 
+static int _regmap_bus_reg_write(void *context, unsigned int reg,
+				 unsigned int val)
+{
+	struct regmap *map = context;
+
+	return map->bus->reg_write(map->bus_context, reg, val);
+}
+
 static int _regmap_bus_raw_write(void *context, unsigned int reg,
 				 unsigned int val)
 {
@@ -1708,6 +1726,14 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
 	return ret;
 }
 
+static int _regmap_bus_reg_read(void *context, unsigned int reg,
+				unsigned int *val)
+{
+	struct regmap *map = context;
+	
+	return map->bus->reg_read(map->bus_context, reg, val);
+}
+
 static int _regmap_bus_read(void *context, unsigned int reg,
 			    unsigned int *val)
 {
-- 
1.8.1.2


From 3d039a1d2561951e4f2a94e6e6d61f730c35ae5e Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Tue, 9 Jun 2015 12:29:12 +0200
Subject: [PATCH 11/19] some more supports for AC97 mode

---
 sound/soc/fsl/fsl_ssi.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 00d0e5b..4e2bf1c 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -936,6 +936,9 @@
 		strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP |
 			CCSR_SSI_STCR_TXBIT0;
 		break;
+	case SND_SOC_DAIFMT_AC97:
+		scr |= CCSR_SSI_SCR_I2S_MODE_NORMAL;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -971,6 +974,11 @@
 	case SND_SOC_DAIFMT_CBM_CFM:
 		scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
 		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		strcr &= ~CCSR_SSI_STCR_TXDIR; /* transmit clock is external */
+		strcr |= CCSR_SSI_STCR_TFDIR; /* frame sync generated internally */
+		scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -988,6 +996,9 @@
 	write_ssi(srcr, &ssi->srcr);
 	write_ssi(scr, &ssi->scr);
 
+	if (fmt & SND_SOC_DAIFMT_AC97)
+		fsl_ssi_setup_ac97(ssi_private);
+
 	return 0;
 }
 
@@ -1232,6 +1243,7 @@
 };
 
 static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
+	.probe = fsl_ssi_dai_probe,
 	.ac97_control = 1,
 	.playback = {
 		.stream_name = "AC97 Playback",
@@ -1497,6 +1509,10 @@
 		}
 	}
 
+	if (ac97) {
+		fsl_ssi_clk_ctrl(ssi_private, true);
+	}
+
 	/*
 	 * Enable interrupts only for MCP8610 and MX51. The other MXs have
 	 * different writeable interrupt status registers.

-- 
1.8.1.2


From 455331b64c045f49713c6f3dcd4d85e3482bf868 Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Tue, 9 Jun 2015 14:55:53 +0200
Subject: [PATCH 12/19] VT1613 AC97 ASoC machine driver

---
 sound/soc/fsl/imx-ac97-vt1613.c | 202 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 202 insertions(+)
 create mode 100644 sound/soc/fsl/imx-ac97-vt1613.c

diff --git a/sound/soc/fsl/imx-ac97-vt1613.c b/sound/soc/fsl/imx-ac97-vt1613.c
new file mode 100644
index 0000000..3f2c212
--- /dev/null
+++ b/sound/soc/fsl/imx-ac97-vt1613.c
@@ -0,0 +1,208 @@
+/*
+ * imx-ac97-vt1613.c -- SoC audio for i.MX Seco UDOO board with
+ *                                      VT1613 AC'97 codec 
+ * Copyright:	Seco s.r.l.
+
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/vt1613.h"
+#include "imx-audmux.h"
+#include "fsl_ssi.h"
+
+#define DRV_NAME "imx-ac97-vt1613"
+
+static int imx_vt1613_audio_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 *cpu_dai = rtd->cpu_dai;	
+	int ret;
+
+	 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_AC97
+	 				| SND_SOC_DAIFMT_NB_NF
+	 				| SND_SOC_DAIFMT_CBM_CFS);
+	 if (ret < 0) {
+	 	dev_err(cpu_dai->dev,
+	 		"Failed to set cpu dai format: %d\n", ret);
+	 	return ret;
+	}
+
+	return 0;
+}
+static struct snd_soc_ops imx_vt1613_audio_ops = {
+	.hw_params = imx_vt1613_audio_params,
+};
+
+
+static struct snd_soc_dai_link imx_vt1613_dai = {
+	.name		= "vt1613-AC97",
+	.stream_name	= "AC97-analog",
+	.codec_dai_name	= "vt1613-hifi-analog",	
+};
+
+static struct snd_soc_card imx_vt1613_card = { 
+	.name		= "imx-vt1613-audio",
+	.owner  	= THIS_MODULE,
+	.dai_link	= &imx_vt1613_dai,
+	.num_links	= 1,
+};
+
+static int imx_audmux_ac97_config(struct platform_device *pdev, int intPort, int extPort)
+{
+	int ret;
+        unsigned int ptcr, pdcr;
+
+        intPort = intPort - 1;
+        extPort = extPort - 1;
+
+	ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+                        IMX_AUDMUX_V2_PTCR_TCLKDIR | IMX_AUDMUX_V2_PTCR_TCSEL(extPort); 
+	pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(extPort);
+
+        ret = imx_audmux_v2_configure_port(intPort, ptcr, pdcr);
+	if (ret) {
+		dev_err(&pdev->dev, "Audmux internal port setup failed\n");
+		return ret;
+	}
+
+	ptcr = IMX_AUDMUX_V2_PTCR_SYN | 
+                        IMX_AUDMUX_V2_PTCR_TFSDIR | IMX_AUDMUX_V2_PTCR_TFSEL(intPort); 
+                        
+	pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(intPort);
+
+        ret = imx_audmux_v2_configure_port(extPort, ptcr, pdcr);
+	if (ret) {
+		dev_err(&pdev->dev, "Audmux external port setup failed\n");
+		return ret;
+	}
+
+        return 0;
+}
+
+static int imx_vt1613_probe(struct platform_device *pdev)
+{
+	struct device_node *ssi_np, *codec_np, *np = pdev->dev.of_node;
+
+	struct platform_device *codec_pdev;
+	struct platform_device *ssi_pdev;
+	int int_port, ext_port;
+	int ret;
+
+	if (!of_machine_is_compatible("udoo,imx6q-udoo") && !of_machine_is_compatible("udoo,imx6dl-udoo")) 
+		return -ENODEV;
+
+	if (vt1613_modules_dep_ok) {
+		dev_dbg(&pdev->dev, "module dependency (codec module) ok\n");
+	} else {
+		dev_err(&pdev->dev, "module dependency (codec module) not ok\n");	
+	}
+
+	ret = of_property_read_u32(np, "mux-int-port", &int_port);
+	if (ret) {
+		dev_err(&pdev->dev, "mux-int-port property missing or invalid\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
+	if (ret) {
+		dev_err(&pdev->dev, "mux-ext-port property missing or invalid\n");
+		return ret;
+	}
+
+	ret = imx_audmux_ac97_config(pdev, int_port, ext_port); 
+	if (ret) {
+		dev_err(&pdev->dev, "Audmux port setup failed\n");
+		return ret;
+	}
+
+	ssi_np = of_parse_phandle(np, "ssi-controller", 0);	
+	if (!ssi_np) {
+		dev_err(&pdev->dev, "ssi-controller phandle missing or invalid\n");
+		return -EINVAL;
+	}
+	ssi_pdev = of_find_device_by_node(ssi_np);
+	if (!ssi_pdev) {
+		dev_err(&pdev->dev, "Failed to find SSI platform device\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	codec_np = of_parse_phandle(np, "audio-codec", 0); 
+	if (!codec_np) {
+		dev_err(&pdev->dev, "audio-codec phandle missing or invalid\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+	codec_pdev = of_find_device_by_node(codec_np); 
+	if (!codec_pdev) {
+		dev_err(&pdev->dev, "Failed to find codec device\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	imx_vt1613_dai.codec_name = dev_name(&codec_pdev->dev);
+	imx_vt1613_dai.cpu_of_node = ssi_np; 
+	imx_vt1613_dai.cpu_dai_name = dev_name(&ssi_pdev->dev);
+	imx_vt1613_dai.platform_of_node = ssi_np;
+	imx_vt1613_dai.ops = &imx_vt1613_audio_ops; 
+	imx_vt1613_dai.dai_fmt = SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFS;
+
+	imx_vt1613_card.dev = &pdev->dev;
+	
+	platform_set_drvdata(pdev, &imx_vt1613_card);
+
+	ret = snd_soc_register_card(&imx_vt1613_card);
+	if (ret)
+		dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
+        
+fail:
+	if (ssi_np)
+		of_node_put(ssi_np);
+	if (codec_np)
+		of_node_put(codec_np);
+
+	return ret;
+
+}
+
+static int imx_vt1613_remove(struct platform_device *pdev)
+{
+	int ret;
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	ret = snd_soc_unregister_card(card);
+
+	return ret;
+}
+
+static const struct of_device_id imx_vt1613_audio_match[] = {
+	{ .compatible = "udoo,imx-vt1613-audio", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, imx_vt1613_audio_match);
+
+static struct platform_driver imx_vt1613_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = imx_vt1613_audio_match,
+	},
+	.probe = imx_vt1613_probe,
+	.remove = imx_vt1613_remove,
+};
+module_platform_driver(imx_vt1613_driver);
+
+MODULE_AUTHOR("Seco <info@seco.it>");
+MODULE_DESCRIPTION(DRV_NAME ":Freescale i.MX VT1613 AC97 ASoC machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-vt1613");
-- 
1.8.1.2


From c750211687be74e17acb8f089767a4d8099e3b6c Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Wed, 10 Jun 2015 10:06:17 +0200
Subject: [PATCH 13/19] VT1613 AC'97 codec driver

---
 sound/soc/codecs/vt1613.c | 494 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 494 insertions(+)
 create mode 100644 sound/soc/codecs/vt1613.c

diff --git a/sound/soc/codecs/vt1613.c b/sound/soc/codecs/vt1613.c
new file mode 100644
index 0000000..9eccad7
--- /dev/null
+++ b/sound/soc/codecs/vt1613.c
@@ -0,0 +1,497 @@
+/*
+* vt1613.c  --  ALSA SoC VT1613 AC'97 codec support
+*
+* Copyright 2010-2015 Seco s.r.l.
+*
+* 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.
+*
+* 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.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA
+*
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/soc.h> 
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/ac97_codec.h>
+
+#include "vt1613.h"
+#define DRV_NAME "vt1613-codec"
+
+/* TODO: S/PDIF implementation. As this driver was developed for UDOO board's needs,
+* we skipped S/PDIF features developing*/ 
+
+bool vt1613_modules_dep_ok = true;
+EXPORT_SYMBOL_GPL(vt1613_modules_dep_ok); 
+
+static const struct reg_default vt1613_reg_defaults[] = {
+  { 0x00, 0x0140 },  /* Reset */ 		
+  { 0x02, 0x8000 },  /* Stereo Output Volume */
+  { 0x04, 0x8000 },  /* HP Stereo Output Volume */
+  { 0x0A, 0x0000 },  /* PC Beep Volume */
+  { 0x0C, 0x8008 },  /* Phone Volume */
+  { 0x0E, 0x8008 },  /* MIC Volume */
+  { 0x10, 0x8808 },  /* Line In Volume */
+  { 0x12, 0x8808 },  /* CD Volume */
+  { 0x16, 0x8808 },  /* AUX Volume */
+  { 0x18, 0x8808 },  /* PCM out Volume */
+  { 0x1A, 0x0000 },  /* Record Select */
+  { 0x1C, 0x8000 },  /* Record Gain */
+  { 0x20, 0x0000 },  /* General Purpose */
+  { 0x24, 0x0001 },  /* Audio Interrupt & Paging (AC'97 2.3) */
+  { 0x26, 0x0000 },  /* Powerdown control / status */	
+  { 0x28, 0x097C },  /* Extended Audio ID */
+  { 0x2A, 0x3830 },  /* Extended Audio Status and Control */
+  { 0x2C, 0xBB80 },  /* PCM Front DAC Rate */          
+  { 0x32, 0xBB80 },  /* PCM LR ADC Rate */         
+  { 0x3A, 0x2000 },  /* S/PDIF Control */
+  { 0x5A, 0x0000 },  /* Vendor Reserved Register */ 
+  { 0x5C, 0x00A9 },  /* Vendor Reserved Register */
+  { 0x62, 0xFFFF },  /*  PCI SVID  Page ID = 01h */                            
+  { 0x64, 0xFFFF },  /*  PCI SID   Page ID = 01h */                            
+  { 0x66, 0x0000 },  /* S/PDIF RX Status    Page ID = 00h */
+  { 0x68, 0x0000 },  /* S/PDIF RX Status    Page ID = 00h */
+  { 0x6A, 0x0000 },  /* S/PDIF RX Control   Page ID = 00h */
+  { 0x6C, 0x376A },  /* DAC Slot Mapping  Page ID = 01h */                     
+  { 0x6E, 0x0000 },  /* ADC Slot Mapping  Page ID = 01h */                     	  
+  { 0x70, 0x0000 },  /* ADC / SPDIF RX Left Peak */
+  { 0x74, 0x0000 },  /* PLL Setting /Debugging */
+  { 0x76, 0x1182 },  /* Miscellaneous */
+  { 0x78, 0x0070 },  /* GPIO Control */
+  { 0x7A, 0x0070 },  /* GPIO Status */
+  { 0x7C, 0x5649 },  /* Vendor ID1 */
+  { 0x7E, 0x4120 },  /* Vendor ID2 */	
+};
+
+static bool vt1613_readable_reg(struct device *dev, unsigned int reg)
+{
+  switch (reg) {
+  case AC97_RESET ... AC97_HEADPHONE:
+  case AC97_PC_BEEP ... AC97_CD:
+  case AC97_AUX ... AC97_GENERAL_PURPOSE:
+  case AC97_INT_PAGING ... AC97_PCM_LR_ADC_RATE:
+  case AC97_SPDIF:
+	case AC97_AD_TEST:
+	case AC97_VT1613_STEREO_MIC:
+  case AC97_PCI_SVID ... AC97_SENSE_INFO:
+	case AC97_VT1613_DAC_SLOT_MAP:
+	case AC97_VT1613_ADC_SLOT_MAP:
+  case AC97_AD_CODEC_CFG:
+  case AC97_AD_SERIAL_CFG:
+	case AC97_AD_MISC:
+	case AC97_VT1613_GPIO_CTRL:
+  case AC97_VT1613_GPIO_STATUS:
+  case AC97_VENDOR_ID1:
+  case AC97_VENDOR_ID2:
+    return true;
+  default:
+    return false;
+  }
+}
+  
+static bool vt1613_writeable_reg(struct device *dev, unsigned int reg)
+{
+  switch (reg) {
+  case AC97_RESET:
+  case AC97_EXTENDED_ID:
+  case AC97_PCM_SURR_DAC_RATE:
+  case AC97_PCM_LFE_DAC_RATE:
+  case AC97_FUNC_SELECT:
+  case AC97_FUNC_INFO:
+  case AC97_VT1613_ADC_SLOT_MAP:
+  case AC97_VENDOR_ID1:
+  case AC97_VENDOR_ID2:
+    return false;
+  default:
+    return vt1613_readable_reg(dev, reg);
+  }
+}
+  
+static const struct regmap_config vt1613_regmap_config = {
+	.name = "vt1613_regmap",
+  .reg_bits = 16,
+  .reg_stride = 2,
+  .val_bits = 16,
+  .max_register = 0x7E,
+  .cache_type = REGCACHE_RBTREE,
+ 
+  .volatile_reg = regmap_ac97_default_volatile,
+  .readable_reg = vt1613_readable_reg,
+  .writeable_reg = vt1613_writeable_reg,
+ 
+  .reg_defaults = vt1613_reg_defaults,
+  .num_reg_defaults = ARRAY_SIZE(vt1613_reg_defaults),
+};
+
+static const char *vt1613_record_mux[] = {"Mic", "CD", "--", "AUX",
+			"Line", "Stereo Mix", "Mono Mix", "Phone"};
+static SOC_ENUM_DOUBLE_DECL(vt1613_record_enum,
+			AC97_REC_SEL, 8, 0, vt1613_record_mux);
+
+static const char *vt1613_mic_mux[] = {"Mic1", "Mic2"};
+static SOC_ENUM_SINGLE_DECL(vt1613_mic_enum, 
+			AC97_GENERAL_PURPOSE, 8, vt1613_mic_mux); 
+
+static const char *vt1613_boost[] = {"0dB", "20dB"};
+static SOC_ENUM_SINGLE_DECL(vt1613_boost_enum, 
+			AC97_MIC, 6, vt1613_boost); 
+
+static const char *vt1613_mic_sel[] = {"MonoMic", "StereoMic"};
+static SOC_ENUM_SINGLE_DECL(vt1613_mic_sel_enum, 
+			AC97_VT1613_STEREO_MIC, 2, vt1613_mic_sel); 
+
+static const DECLARE_TLV_DB_LINEAR(master_tlv, -4650, 0);
+static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250); 
+static const DECLARE_TLV_DB_LINEAR(beep_tlv, -4500, 0); 
+static const DECLARE_TLV_DB_LINEAR(mix_tlv, -3450, 1200);
+
+static const struct snd_kcontrol_new vt1613_snd_ac97_controls[] = {
+	SOC_DOUBLE_TLV("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1, master_tlv),
+	SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1), 
+
+	SOC_DOUBLE_TLV("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1, master_tlv),
+	SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
+	
+	SOC_DOUBLE_TLV("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1, mix_tlv),
+	SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1), 
+
+	SOC_DOUBLE_TLV("Record Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0, record_tlv),
+	SOC_SINGLE("Record Capture Switch", AC97_REC_GAIN, 15, 1, 1),
+
+	SOC_SINGLE_TLV("Beep Volume", AC97_PC_BEEP, 1, 15, 1, beep_tlv),
+	SOC_SINGLE("Beep Switch", AC97_PC_BEEP, 15, 1, 1), 
+	SOC_SINGLE_TLV("Phone Volume", AC97_PHONE, 0, 31, 0, mix_tlv),
+	SOC_SINGLE("Phone Switch", AC97_PHONE, 15, 1, 1),
+
+	/* Mono Mic and Stereo Mic's right channel controls */
+	SOC_SINGLE_TLV("Mic/StereoMic_R Volume", AC97_MIC, 0, 31, 0, mix_tlv),
+	SOC_SINGLE("Mic/StereoMic_R Switch", AC97_MIC, 15, 1, 1), 
+
+	/* Stereo Mic's left channel controls */
+	SOC_SINGLE("StereoMic_L Switch", AC97_MIC, 7, 1, 1), 
+	SOC_SINGLE_TLV("StereoMic_L Volume", AC97_MIC, 8, 31, 0, mix_tlv), 
+
+	SOC_DOUBLE_TLV("Line Volume", AC97_LINE, 8, 0, 31, 0, mix_tlv),
+	SOC_SINGLE("Line Switch", AC97_LINE, 15, 1, 1), 
+	SOC_DOUBLE_TLV("CD Volume", AC97_CD, 8, 0, 31, 0, mix_tlv),
+	SOC_SINGLE("CD Switch", AC97_CD, 15, 1, 1), 
+	SOC_DOUBLE_TLV("AUX Volume", AC97_AUX, 8, 0, 31, 0, mix_tlv), 
+	SOC_SINGLE("AUX Switch", AC97_AUX, 15, 1, 1), 	
+	
+	SOC_SINGLE("Analog Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0),
+
+	SOC_ENUM("Mic Boost", vt1613_boost_enum),
+	SOC_ENUM("Mic1/2 Mux", vt1613_mic_enum),
+	SOC_ENUM("Mic Select", vt1613_mic_sel_enum),
+	SOC_ENUM("Record Mux", vt1613_record_enum),
+};
+
+static const unsigned int vt1613_rates[] = {
+	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static const struct snd_pcm_hw_constraint_list vt1613_rate_constraints = {
+	.count	= ARRAY_SIZE(vt1613_rates),
+	.list	= vt1613_rates,
+};
+
+static int vt1613_startup(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{	
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
+		snd_pcm_hw_constraint_list(substream->runtime, 0,
+			SNDRV_PCM_HW_PARAM_RATE, &vt1613_rate_constraints);
+	} else {
+		snd_pcm_hw_constraint_list(substream->runtime, 0,
+			SNDRV_PCM_HW_PARAM_RATE, &vt1613_rate_constraints);
+	}
+	
+	return 0;
+}
+
+static int ac97_analog_prepare(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned short reg; 
+
+	/* enable variable rate audio (VRA) and disable S/PDIF output */
+	snd_soc_write(codec, AC97_EXTENDED_STATUS, 
+		(snd_soc_read(codec, AC97_EXTENDED_STATUS) | 0x1) & ~0x4);	
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
+		reg = AC97_PCM_FRONT_DAC_RATE;		
+	} else {
+		reg = AC97_PCM_LR_ADC_RATE;
+	}
+
+	return snd_soc_write(codec, reg, runtime->rate);
+}
+
+static int ac97_digital_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	snd_soc_write(codec, AC97_SPDIF, 0x2002);
+
+	 /* enable VRA and S/PDIF output */
+	snd_soc_write(codec, AC97_EXTENDED_STATUS, snd_soc_read(codec, AC97_EXTENDED_STATUS) | 0x5);
+
+	return snd_soc_write(codec, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+}
+
+static int vt1613_set_bias_level(struct snd_soc_codec *codec,
+				   enum snd_soc_bias_level level)
+{
+	switch (level) {
+	case SND_SOC_BIAS_ON: /* full On */
+	case SND_SOC_BIAS_PREPARE: /* partial On */
+	case SND_SOC_BIAS_STANDBY: /* Off, with power */
+		snd_soc_write(codec, AC97_POWERDOWN, 0x0000);
+		break;
+	case SND_SOC_BIAS_OFF: /* Off, without power */
+		/* disable everything including AC link */
+		snd_soc_write(codec, AC97_POWERDOWN, 0xffff);
+		break;
+	}
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static int vt1613_reset(struct snd_soc_codec *codec, int try_warm)
+{
+	
+	if (try_warm && soc_ac97_ops->warm_reset) {
+		soc_ac97_ops->warm_reset(codec->ac97);
+		if (snd_soc_read(codec, AC97_RESET) == 0x0140)
+			return 1;
+	}
+	soc_ac97_ops->reset(codec->ac97);
+	if (soc_ac97_ops->warm_reset)
+		soc_ac97_ops->warm_reset(codec->ac97);
+	if (snd_soc_read(codec, AC97_RESET) == 0x0140) 
+		return 0;
+
+	return -EIO;
+}
+
+static struct snd_soc_dai_ops vt1613_dai_ops_analog = {
+	.startup = vt1613_startup,
+	.prepare = ac97_analog_prepare,
+};
+
+static struct snd_soc_dai_ops vt1613_dai_ops_digital = {
+	.prepare = ac97_digital_prepare,
+};
+
+struct snd_soc_dai_driver vt1613_dai[] = {
+{
+	.name = "vt1613-hifi-analog",
+	.ac97_control = 1,
+
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_KNOT,
+		.formats = SND_SOC_STD_AC97_FMTS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_KNOT,
+		.formats = SND_SOC_STD_AC97_FMTS,
+	},
+
+	.ops = &vt1613_dai_ops_analog,
+},
+{
+	.name = "vt1613-hifi-IEC958",
+	.ac97_control = 1,
+
+	.playback = {
+		.stream_name = "vt1613 IEC958",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_32000 | 
+			SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE,
+	},
+
+	.ops = &vt1613_dai_ops_digital,
+}
+};
+
+static int vt1613_codec_suspend(struct snd_soc_codec *codec)
+{
+	vt1613_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int vt1613_codec_resume(struct snd_soc_codec *codec)
+{
+	u16 id, reset;
+
+	reset = 0;
+	/* give the codec an AC97 warm reset to start the link */
+reset:
+	if (reset > 5) {
+		printk(KERN_ERR "vt1613 failed to resume");
+		return -EIO;
+	}
+	codec->ac97->bus->ops->warm_reset(codec->ac97);
+	id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
+	if (id != 0x4123) {
+		vt1613_reset(codec, 0);
+		reset++;
+		goto reset;
+	}
+	vt1613_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+
+static int vt1613_codec_probe(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+	struct regmap *regmap;
+
+	ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
+	if (ret){
+		dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
+		return ret;
+	}	
+
+	soc_ac97_dev_register(codec);
+	if (ret){
+		dev_err(codec->dev, "Failed to register AC97 codec to bus: %d\n", ret);
+		goto free_ac97;
+	}
+	codec->ac97_registered = 1; 
+
+	regmap = regmap_init_ac97(codec->ac97, &vt1613_regmap_config);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(codec->dev, "Failed to init register map: %d\n", ret);
+		goto free_ac97;
+	}
+	
+	codec->control_data = regmap;
+	ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto free_regmap;
+	}
+	snd_soc_codec_set_drvdata(codec, codec->ac97);
+
+	/* do a cold reset for the controller and then try
+	 * a warm reset followed by an optional cold reset for codec */
+	
+	vt1613_reset(codec, 0);
+	ret = vt1613_reset(codec, 1);
+	if (ret < 0) {
+		printk(KERN_ERR "Failed to reset VT1613: AC97 link error\n");
+		goto free_regmap;
+	}
+
+	/* Read out vendor IDs */
+	printk(KERN_INFO "VT1613 SoC Audio Codec [ID = %04x - %04x]\n", 
+		snd_soc_read(codec, AC97_VENDOR_ID1), snd_soc_read(codec, AC97_VENDOR_ID2));
+
+	vt1613_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ 	/* unmute captures and playbacks volume */
+	snd_soc_write(codec, AC97_MASTER, 0x0000);
+ 	snd_soc_write(codec, AC97_PCM, 0x0000);
+ 	snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
+
+	/* At 3.3V analog supply, for the bits 3:2 should be set 10b for the lowest power instead of default 00b */
+	snd_soc_write(codec, AC97_AD_TEST, snd_soc_read(codec, AC97_AD_TEST) | 0x0008);
+
+	/* To maximize recording quality by removing white noise */
+	snd_soc_write(codec, AC97_AD_TEST, snd_soc_read(codec, AC97_AD_TEST) | 0x0400);
+
+	snd_soc_add_codec_controls(codec, vt1613_snd_ac97_controls,
+			     ARRAY_SIZE(vt1613_snd_ac97_controls));
+	
+	return 0;
+
+free_regmap:
+  regmap_exit(regmap);
+free_ac97:
+  snd_soc_free_ac97_codec(codec);
+
+ 	return ret;
+}
+
+static int vt1613_codec_remove(struct snd_soc_codec *codec)
+{
+	regmap_exit(codec->control_data);
+	snd_soc_free_ac97_codec(codec);
+	return 0;
+}
+
+struct snd_soc_codec_driver vt1613_codec = {
+	.probe = vt1613_codec_probe,
+	.remove = vt1613_codec_remove,
+	.suspend = vt1613_codec_suspend,
+	.resume = vt1613_codec_resume,	
+	.set_bias_level = vt1613_set_bias_level,
+};
+
+static int vt1613_probe(struct platform_device *pdev)
+{	
+	return snd_soc_register_codec(&pdev->dev,
+		&vt1613_codec, vt1613_dai, ARRAY_SIZE(vt1613_dai));
+}
+
+static int vt1613_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_codec(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id vt1613_of_match[] = {
+	{ .compatible = "via,vt1613", }, 
+	{ }
+};
+MODULE_DEVICE_TABLE(of, vt1613_of_match);
+
+static struct platform_driver vt1613_codec_driver = {
+	.driver = {
+			.name = DRV_NAME,
+			.owner = THIS_MODULE,
+			.of_match_table = vt1613_of_match,
+	},
+
+	.probe = vt1613_probe,
+	.remove = vt1613_remove,
+};
+
+module_platform_driver(vt1613_codec_driver);
+
+MODULE_DESCRIPTION("ASoC VT1613 codec driver");
+MODULE_AUTHOR("Seco s.r.l.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
-- 
1.8.1.2


From 4bb3141618cdc0b8e9e05267f5402e29923bec07 Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Wed, 10 Jun 2015 11:59:53 +0200
Subject: [PATCH 16/19] VT1613 AC'97 header file

---
 sound/soc/codecs/vt1613.h | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 sound/soc/codecs/vt1613.h

diff --git a/sound/soc/codecs/vt1613.h b/sound/soc/codecs/vt1613.h
new file mode 100644
index 0000000..482e757
--- /dev/null
+++ b/sound/soc/codecs/vt1613.h
@@ -0,0 +1,29 @@
+/*
+ * vt1613.h - VT1613 audio codec interface
+ *
+ * Copyright 2010-2015 Seco s.r.l.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#ifndef _VT1613_H
+#define _VT1613_H
+
+#define AC97_VT1613_DAC_SLOT_MAP  0x6C
+#define AC97_VT1613_ADC_SLOT_MAP  0x6E 
+
+#define AC97_VT1613_GPIO_CTRL     0x78
+#define AC97_VT1613_GPIO_STATUS   0x7A
+
+#define AC97_VT1613_STEREO_MIC    0x5C
+
+/* 	VT1613 DAI ID's */
+#define VT1613_DAI_AC97_ANALOG          0
+#define VT1613_DAI_AC97_DIGITAL         1
+
+extern bool vt1613_modules_dep_ok;
+
+#endif
-- 
1.8.1.2


From 2a0ebcf045bc22d48856e0a2279307a258b4b5b0 Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Wed, 10 Jun 2015 12:37:30 +0200
Subject: [PATCH 17/19] Add select REGMAP_AC97 for VT1613 codec

---
 sound/soc/codecs/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index d813556..a5996d5 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -389,6 +389,7 @@ config SND_SOC_UDA1380
         tristate
         
 config SND_SOC_VT1613
+	select REGMAP_AC97
         tristate
         
 config SND_SOC_WL1273
-- 
1.8.1.2


From ca2d5c1f9944150b88d44ee7de1d10d3fb63ce5f Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Wed, 10 Jun 2015 12:57:48 +0200
Subject: [PATCH 18/19] Make soc_ac97_dev_register function non static

---
 sound/soc/soc-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index c1d9d85..348754b 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -523,7 +523,7 @@ static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
 static void soc_ac97_device_release(struct device *dev){}
 
 /* register ac97 codec to bus */
-static int soc_ac97_dev_register(struct snd_soc_codec *codec)
+int soc_ac97_dev_register(struct snd_soc_codec *codec)
 {
 	int err;
 
@@ -541,6 +541,7 @@
 	}
 	return 0;
 }
+EXPORT_SYMBOL_GPL(soc_ac97_dev_register);
 #endif
 
 static void codec2codec_close_delayed_work(struct work_struct *work)
@@ -882,6 +882,8 @@
 			dai_link->cpu_dai_name);
 		return -EPROBE_DEFER;
 	}
+	
+	dev_err(card->dev, "ASoC: CPU DAI %s registered\n", dai_link->cpu_dai_name);
 
 	/* Find CODEC from registered CODECs */
 	list_for_each_entry(codec, &codec_list, list) {
@@ -920,6 +922,8 @@
 			dai_link->codec_name);
 		return -EPROBE_DEFER;
 	}
+	
+	dev_err(card->dev, "ASoC: CODEC %s registered\n", dai_link->codec_name);
 
 	/* if there's no platform we match on the empty platform */
 	platform_name = dai_link->platform_name;
-- 
1.8.1.2


From 5f679d20733e1998b1e705ceb97abaf37e893f6c Mon Sep 17 00:00:00 2001
From: waynatali <waykovalenko@gmail.com>
Date: Wed, 10 Jun 2015 13:07:16 +0200
Subject: [PATCH 19/19] Add soc_ac97_dev_register function

---
 include/sound/soc.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/sound/soc.h b/include/sound/soc.h
index 59a4785..ddc359d 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -412,6 +412,7 @@ struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
 		const char *dai_link, int stream);
 struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
 		const char *dai_link);
+int soc_ac97_dev_register(struct snd_soc_codec *codec);
 
 /* Utility functions to get clock rates from various things */
 int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
-- 
1.8.1.2

